diff --git a/audio-kernel/Android.mk b/audio-kernel/Android.mk new file mode 100644 index 000000000..d5be7c8da --- /dev/null +++ b/audio-kernel/Android.mk @@ -0,0 +1,49 @@ +# Android makefile for audio kernel modules +MY_LOCAL_PATH := $(call my-dir) + +UAPI_OUT := $(PRODUCT_OUT)/obj/vendor/qcom/opensource/audio-kernel/include + +ifeq ($(call is-board-platform-in-list,msm8953 sdm845 sdm670 qcs605 msmnile $(MSMSTEPPE) $(TRINKET)),true) +$(shell mkdir -p $(UAPI_OUT)/linux;) +$(shell mkdir -p $(UAPI_OUT)/sound;) +$(shell rm -rf $(PRODUCT_OUT)/obj/vendor/qcom/opensource/audio-kernel/ipc/Module.symvers) +$(shell rm -rf $(PRODUCT_OUT)/obj/vendor/qcom/opensource/audio-kernel/dsp/Module.symvers) +$(shell rm -rf $(PRODUCT_OUT)/obj/vendor/qcom/opensource/audio-kernel/dsp/codecs/Module.symvers) +$(shell rm -rf $(PRODUCT_OUT)/obj/vendor/qcom/opensource/audio-kernel/soc/Module.symvers) +$(shell rm -rf $(PRODUCT_OUT)/obj/vendor/qcom/opensource/audio-kernel/asoc/Module.symvers) +$(shell rm -rf $(PRODUCT_OUT)/obj/vendor/qcom/opensource/audio-kernel/asoc/codecs/Module.symvers) +$(shell rm -rf $(PRODUCT_OUT)/obj/vendor/qcom/opensource/audio-kernel/asoc/codecs/wcd934x/Module.symvers) + +include $(MY_LOCAL_PATH)/include/uapi/Android.mk +include $(MY_LOCAL_PATH)/ipc/Android.mk +include $(MY_LOCAL_PATH)/dsp/Android.mk +include $(MY_LOCAL_PATH)/dsp/codecs/Android.mk +include $(MY_LOCAL_PATH)/soc/Android.mk +include $(MY_LOCAL_PATH)/asoc/Android.mk +include $(MY_LOCAL_PATH)/asoc/codecs/Android.mk +include $(MY_LOCAL_PATH)/asoc/codecs/wcd934x/Android.mk +endif + +ifeq ($(call is-board-platform-in-list,sdm670 msmnile),true) +$(shell rm -rf $(PRODUCT_OUT)/obj/vendor/qcom/opensource/audio-kernel/asoc/codecs/aqt1000/Module.symvers) +include $(MY_LOCAL_PATH)/asoc/codecs/aqt1000/Android.mk +endif + +ifeq ($(call is-board-platform-in-list,$(MSMSTEPPE) $(TRINKET)),true) +$(shell rm -rf $(PRODUCT_OUT)/obj/vendor/qcom/opensource/audio-kernel/asoc/codecs/bolero/Module.symvers) +include $(MY_LOCAL_PATH)/asoc/codecs/bolero/Android.mk +$(shell rm -rf $(PRODUCT_OUT)/obj/vendor/qcom/opensource/audio-kernel/asoc/codecs/wcd937x/Module.symvers) +include $(MY_LOCAL_PATH)/asoc/codecs/wcd937x/Android.mk +endif + +ifeq ($(call is-board-platform-in-list,msm8953 sdm670 qcs605),true) +$(shell rm -rf $(PRODUCT_OUT)/obj/vendor/qcom/opensource/audio-kernel/asoc/codecs/sdm660_cdc/Module.symvers) +$(shell rm -rf $(PRODUCT_OUT)/obj/vendor/qcom/opensource/audio-kernel/asoc/codecs/msm_sdw/Module.symvers) +include $(MY_LOCAL_PATH)/asoc/codecs/sdm660_cdc/Android.mk +include $(MY_LOCAL_PATH)/asoc/codecs/msm_sdw/Android.mk +endif + +ifeq ($(call is-board-platform-in-list,msmnile),true) +$(shell rm -rf $(PRODUCT_OUT)/obj/vendor/qcom/opensource/audio-kernel/asoc/codecs/wcd9360/Module.symvers) +include $(MY_LOCAL_PATH)/asoc/codecs/wcd9360/Android.mk +endif diff --git a/audio-kernel/Makefile b/audio-kernel/Makefile new file mode 100644 index 000000000..159b68cd0 --- /dev/null +++ b/audio-kernel/Makefile @@ -0,0 +1,57 @@ +# auto-detect subdirs +ifeq ($(CONFIG_ARCH_SDM845), y) +include $(srctree)/techpack/audio/config/sdm845auto.conf +export +endif +ifeq ($(CONFIG_ARCH_SDM670), y) +include $(srctree)/techpack/audio/config/sdm670auto_static.conf +export +endif +ifeq ($(CONFIG_ARCH_SDXPOORWILLS), y) +include $(srctree)/techpack/audio/config/sdxpoorwillsauto.conf +export +endif +ifeq ($(CONFIG_ARCH_SM8150), y) +include $(srctree)/techpack/audio/config/sm8150auto.conf +export +endif +ifeq ($(CONFIG_ARCH_SDMSHRIKE), y) +include $(srctree)/techpack/audio/config/sm8150auto.conf +export +endif + +# Use USERINCLUDE when you must reference the UAPI directories only. +USERINCLUDE += \ + -I$(srctree)/techpack/audio/include/uapi \ + +# Use LINUXINCLUDE when you must reference the include/ directory. +# Needed to be compatible with the O= option +LINUXINCLUDE += \ + -I$(srctree)/techpack/audio/include/uapi \ + -I$(srctree)/techpack/audio/include + +ifeq ($(CONFIG_ARCH_SDM845), y) +LINUXINCLUDE += \ + -include $(srctree)/techpack/audio/config/sdm845autoconf.h +endif +ifeq ($(CONFIG_ARCH_SDM670), y) +LINUXINCLUDE += \ + -include $(srctree)/techpack/audio/config/sdm670autoconf.h +endif +ifeq ($(CONFIG_ARCH_SDXPOORWILLS), y) +LINUXINCLUDE += \ + -include $(srctree)/techpack/audio/config/sdxpoorwillsautoconf.h +endif +ifeq ($(CONFIG_ARCH_SM8150), y) +LINUXINCLUDE += \ + -include $(srctree)/techpack/audio/config/sm8150autoconf.h +endif +ifeq ($(CONFIG_ARCH_SDMSHRIKE), y) +LINUXINCLUDE += \ + -include $(srctree)/techpack/audio/config/sm8150autoconf.h +endif + +obj-y += soc/ +obj-y += dsp/ +obj-y += ipc/ +obj-y += asoc/ diff --git a/audio-kernel/Makefile.am b/audio-kernel/Makefile.am new file mode 100644 index 000000000..4a9f96ed5 --- /dev/null +++ b/audio-kernel/Makefile.am @@ -0,0 +1,76 @@ +AUDIO_ROOT=$(PWD) +UAPI_OUT=$(PWD) +HEADER_INSTALL_DIR=$(KERNEL_SRC)/scripts +KERNEL_BINARY_DIR=$(KERNEL_SRC)/../kernel-build-artifacts + +KBUILD_OPTIONS := AUDIO_ROOT=$(PWD) +KBUILD_OPTIONS += MODNAME=audio +KBUILD_OPTIONS += UAPI_OUT=$(PWD) + +AUDIO_KERNEL_HEADERS_PATH1 = $(shell ls ./include/uapi/linux/*.h) +AUDIO_KERNEL_HEADERS_PATH2 = $(shell ls ./include/uapi/linux/mfd/wcd9xxx/*.h) +AUDIO_KERNEL_HEADERS_PATH3 = $(shell ls ./include/uapi/sound/*.h) + +ifeq ($(TARGET_SUPPORT), $(filter $(TARGET_SUPPORT), sdm670 qcs605)) +KBUILD_OPTIONS += CONFIG_ARCH_SDM670=y +endif +ifeq ($(TARGET_SUPPORT),sdm845) +KBUILD_OPTIONS += CONFIG_ARCH_SDM845=y +endif +ifeq ($(TARGET_SUPPORT),apq8053) +KBUILD_OPTIONS += CONFIG_ARCH_SDM450=y +endif +ifeq ($(TARGET_SUPPORT),qcs40x) +KBUILD_OPTIONS += CONFIG_ARCH_QCS405=y +endif +ifeq ($(TARGET_SUPPORT), sdmsteppe)) +KBUILD_OPTIONS += CONFIG_ARCH_SM6150=y +endif + +obj-m := ipc/ +obj-m += dsp/ +obj-m += dsp/codecs/ +obj-m += soc/ +obj-m += asoc/ +obj-m += asoc/codecs/ +ifeq ($(TARGET_SUPPORT), $(filter $(TARGET_SUPPORT), sdm670 qcs605 sdmsteppe)) +obj-m += asoc/codecs/wcd934x/ +endif +ifeq ($(TARGET_SUPPORT), $(filter $(TARGET_SUPPORT), apq8053 sdm670 qcs605)) +obj-m += asoc/codecs/sdm660_cdc/ +endif +ifeq ($(TARGET_SUPPORT), $(filter $(TARGET_SUPPORT), sdm670 qcs605)) +obj-m += asoc/codecs/msm_sdw/ +endif +ifeq ($(TARGET_SUPPORT), $(filter $(TARGET_SUPPORT), qcs40x)) +obj-m += asoc/codecs/bolero/ +obj-m += asoc/codecs/csra66x0/ +obj-m += asoc/codecs/ep92/ +endif +ifeq ($(TARGET_SUPPORT), sdmsteppe)) +obj-m += asoc/codecs/bolero/ +obj-m += asoc/codecs/wcd937x/ +endif + +all: + $(shell rm -fr $(shell pwd)/soc/core.h) + $(shell ln -s $(KERNEL_SRC)/drivers/pinctrl/core.h $(shell pwd)/soc/core.h) + $(shell rm -fr $(shell pwd)/include/soc/internal.h) + $(shell ln -s $(KERNEL_SRC)/drivers/base/regmap/internal.h $(shell pwd)/include/soc/internal.h) + $(shell rm -fr $(shell pwd)/soc/pinctrl-utils.h) + $(shell ln -s $(KERNEL_SRC)/drivers/pinctrl/pinctrl-utils.h $(shell pwd)/soc/pinctrl-utils.h) + $(shell mkdir $(shell pwd)/linux) + $(shell mkdir $(shell pwd)/sound) + $(shell mkdir $(shell pwd)/linux/mfd) + $(shell mkdir $(shell pwd)/linux/mfd/wcd9xxx) + $(shell cd $(KERNEL_BINARY_DIR) && $(KERNEL_SRC)/scripts/headers_install.sh $(UAPI_OUT)/linux/ $(AUDIO_ROOT)/include/uapi/linux/ $(notdir $(AUDIO_KERNEL_HEADERS_PATH1))) + $(shell cd $(KERNEL_BINARY_DIR) && $(KERNEL_SRC)/scripts/headers_install.sh $(UAPI_OUT)/linux/mfd/wcd9xxx/ $(AUDIO_ROOT)/include/uapi/linux/mfd/wcd9xxx/ $(notdir $(AUDIO_KERNEL_HEADERS_PATH2))) + $(shell cd $(KERNEL_BINARY_DIR) && $(KERNEL_SRC)/scripts/headers_install.sh $(UAPI_OUT)/sound/ $(AUDIO_ROOT)/include/uapi/sound/ $(notdir $(AUDIO_KERNEL_HEADERS_PATH3))) + $(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install + +clean: + rm -f *.o *.ko *.mod.c *.mod.o *~ .*.cmd Module.symvers + rm -rf .tmp_versions diff --git a/audio-kernel/NOTICE b/audio-kernel/NOTICE new file mode 100644 index 000000000..9ad222017 --- /dev/null +++ b/audio-kernel/NOTICE @@ -0,0 +1,24 @@ +Copyright (c) 2009-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 +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. +________________________________________ + +Copyright (C) 2008 Google, Inc. +Copyright (C) 2008 HTC Corporation +Copyright (c) 2010-2017, The Linux Foundation. All rights reserved. + +This software is licensed under the terms of the GNU General Public +License version 2, as published by the Free Software Foundation, and +may be copied, distributed, and modified under those terms. + +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. diff --git a/audio-kernel/asoc/Android.mk b/audio-kernel/asoc/Android.mk new file mode 100644 index 000000000..5b906f6f7 --- /dev/null +++ b/audio-kernel/asoc/Android.mk @@ -0,0 +1,88 @@ +# Android makefile for audio kernel modules + +# Assume no targets will be supported + +# Check if this driver needs be built for current target +ifeq ($(call is-board-platform,sdm845),true) +TARGET := sdm845 +AUDIO_SELECT := CONFIG_SND_SOC_SDM845=m +endif + +ifeq ($(call is-board-platform-in-list,msm8953 sdm670 qcs605),true) +TARGET := sdm670 +AUDIO_SELECT := CONFIG_SND_SOC_SDM670=m +endif + +ifeq ($(call is-board-platform,msmnile),true) +TARGET := msmnile +AUDIO_SELECT := CONFIG_SND_SOC_SM8150=m +endif + +ifeq ($(call is-board-platform,$(MSMSTEPPE)),true) +TARGET := talos +AUDIO_SELECT := CONFIG_SND_SOC_SM6150=m +endif + +ifeq ($(call is-board-platform,$(TRINKET)),true) +TARGET := trinket +AUDIO_SELECT := CONFIG_SND_SOC_SM6150=m +endif + +AUDIO_CHIPSET := audio +# Build/Package only in case of supported target +ifeq ($(call is-board-platform-in-list,msm8953 sdm845 sdm670 qcs605 msmnile $(MSMSTEPPE) $(TRINKET)),true) + +LOCAL_PATH := $(call my-dir) + +# This makefile is only for DLKM +ifneq ($(findstring vendor,$(LOCAL_PATH)),) + +ifneq ($(findstring opensource,$(LOCAL_PATH)),) + AUDIO_BLD_DIR := $(shell pwd)/vendor/qcom/opensource/audio-kernel +endif # opensource + +DLKM_DIR := $(TOP)/device/qcom/common/dlkm + +# Build audio.ko as $(AUDIO_CHIPSET)_audio.ko +########################################################### +# This is set once per LOCAL_PATH, not per (kernel) module +KBUILD_OPTIONS := AUDIO_ROOT=$(AUDIO_BLD_DIR) + +# We are actually building audio.ko here, as per the +# requirement we are specifying _audio.ko as LOCAL_MODULE. +# This means we need to rename the module to _audio.ko +# after audio.ko is built. +KBUILD_OPTIONS += MODNAME=platform_dlkm +KBUILD_OPTIONS += BOARD_PLATFORM=$(TARGET_BOARD_PLATFORM) +KBUILD_OPTIONS += $(AUDIO_SELECT) + +########################################################### +include $(CLEAR_VARS) +LOCAL_MODULE := $(AUDIO_CHIPSET)_platform.ko +LOCAL_MODULE_KBUILD_NAME := platform_dlkm.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_DEBUG_ENABLE := true +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk +########################################################### +ifeq ($(call is-board-platform-in-list,msm8953 sdm670 qcs605 $(TRINKET)),true) +include $(CLEAR_VARS) +LOCAL_MODULE := $(AUDIO_CHIPSET)_cpe_lsm.ko +LOCAL_MODULE_KBUILD_NAME := cpe_lsm_dlkm.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_DEBUG_ENABLE := true +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk +endif +########################################################### +include $(CLEAR_VARS) +LOCAL_MODULE := $(AUDIO_CHIPSET)_machine_$(TARGET).ko +LOCAL_MODULE_KBUILD_NAME := machine_dlkm.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_DEBUG_ENABLE := true +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk +########################################################### + +endif # DLKM check +endif # supported target check diff --git a/audio-kernel/asoc/Kbuild b/audio-kernel/asoc/Kbuild new file mode 100644 index 000000000..17e329eb7 --- /dev/null +++ b/audio-kernel/asoc/Kbuild @@ -0,0 +1,237 @@ +# We can build either as part of a standalone Kernel build or as +# an external module. Determine which mechanism is being used +ifeq ($(MODNAME),) + KERNEL_BUILD := 1 +else + KERNEL_BUILD := 0 +endif + +ifeq ($(KERNEL_BUILD), 1) + # These are configurable via Kconfig for kernel-based builds + # Need to explicitly configure for Android-based builds + AUDIO_BLD_DIR := $(shell pwd)/kernel/msm-4.14 + AUDIO_ROOT := $(AUDIO_BLD_DIR)/techpack/audio +endif + +ifeq ($(KERNEL_BUILD), 0) + ifeq ($(CONFIG_ARCH_SDM845), y) + include $(AUDIO_ROOT)/config/sdm845auto.conf + export + INCS += -include $(AUDIO_ROOT)/config/sdm845autoconf.h + endif + ifeq ($(CONFIG_ARCH_SDM670), y) + include $(AUDIO_ROOT)/config/sdm670auto.conf + export + INCS += -include $(AUDIO_ROOT)/config/sdm670autoconf.h + endif + ifeq ($(CONFIG_ARCH_SDM450), y) + include $(AUDIO_ROOT)/config/sdm670auto.conf + export + INCS += -include $(AUDIO_ROOT)/config/sdm670autoconf.h + endif + ifeq ($(CONFIG_ARCH_SM8150), y) + include $(AUDIO_ROOT)/config/sm8150auto.conf + export + INCS += -include $(AUDIO_ROOT)/config/sm8150autoconf.h + endif + ifeq ($(CONFIG_ARCH_SM6150), y) + include $(AUDIO_ROOT)/config/sm6150auto.conf + export + INCS += -include $(AUDIO_ROOT)/config/sm6150autoconf.h + endif + ifeq ($(CONFIG_ARCH_TRINKET), y) + include $(AUDIO_ROOT)/config/trinketauto.conf + export + INCS += -include $(AUDIO_ROOT)/config/trinketautoconf.h + endif + ifeq ($(CONFIG_ARCH_SDMSHRIKE), y) + include $(AUDIO_ROOT)/config/sm8150auto.conf + export + INCS += -include $(AUDIO_ROOT)/config/sm8150autoconf.h + endif + ifeq ($(CONFIG_ARCH_QCS405), y) + include $(AUDIO_ROOT)/config/qcs405auto.conf + export + INCS += -include $(AUDIO_ROOT)/config/qcs405autoconf.h + endif +endif + +# As per target team, build is done as follows: +# Defconfig : build with default flags +# Slub : defconfig + CONFIG_SLUB_DEBUG := y + +# CONFIG_SLUB_DEBUG_ON := y + CONFIG_PAGE_POISONING := y +# Perf : Using appropriate msmXXXX-perf_defconfig +# +# Shipment builds (user variants) should not have any debug feature +# enabled. This is identified using 'TARGET_BUILD_VARIANT'. Slub builds +# are identified using the CONFIG_SLUB_DEBUG_ON configuration. Since +# there is no other way to identify defconfig builds, QTI internal +# representation of perf builds (identified using the string 'perf'), +# is used to identify if the build is a slub or defconfig one. This +# way no critical debug feature will be enabled for perf and shipment +# builds. Other OEMs are also protected using the TARGET_BUILD_VARIANT +# config. + +############ UAPI ############ +UAPI_DIR := uapi +UAPI_INC := -I$(AUDIO_ROOT)/include/$(UAPI_DIR) + +############ COMMON ############ +COMMON_DIR := include +COMMON_INC := -I$(AUDIO_ROOT)/$(COMMON_DIR) + +############ ASoC Drivers ############ + + +# for SDM6xx sound card driver +ifdef CONFIG_SND_SOC_SDM670 + MACHINE_OBJS += sdm660-common.o +endif + +# for SDM6xx sound card driver +ifdef CONFIG_SND_SOC_INT_CODEC + MACHINE_OBJS += sdm660-internal.o +endif + +# for SDM6xx sound card driver +ifdef CONFIG_SND_SOC_EXT_CODEC + MACHINE_OBJS += sdm660-external.o + MACHINE_OBJS += sdm660-ext-dai-links.o +endif + +# for SDM845 sound card driver +ifdef CONFIG_SND_SOC_MACHINE_SDM845 + MACHINE_OBJS += sdm845.o +endif + +# for SM8150 sound card driver +ifdef CONFIG_SND_SOC_SM8150 + MACHINE_OBJS += sa8155.o + MACHINE_OBJS += sm8150.o + MACHINE_OBJS += machine_815x_init.o +endif + +# for SM6150 sound card driver +ifdef CONFIG_SND_SOC_SM6150 + MACHINE_OBJS += sm6150.o +endif + +# for qcs405 sound card driver +ifdef CONFIG_SND_SOC_QCS405 + MACHINE_OBJS += qcs405.o +endif + +ifdef CONFIG_SND_SOC_CPE + CPE_LSM_OBJS += msm-cpe-lsm.o +endif + +ifdef CONFIG_SND_SOC_QDSP6V2 + PLATFORM_OBJS += msm-audio-effects-q6-v2.o + PLATFORM_OBJS += msm-compress-q6-v2.o + PLATFORM_OBJS += msm-dai-fe.o + PLATFORM_OBJS += msm-dai-q6-hdmi-v2.o + PLATFORM_OBJS += msm-dai-q6-v2.o + PLATFORM_OBJS += msm-dai-slim.o + PLATFORM_OBJS += msm-dai-stub-v2.o + PLATFORM_OBJS += msm-lsm-client.o + PLATFORM_OBJS += msm-pcm-afe-v2.o + PLATFORM_OBJS += msm-pcm-dtmf-v2.o + PLATFORM_OBJS += msm-pcm-hostless.o + PLATFORM_OBJS += msm-pcm-host-voice-v2.o + PLATFORM_OBJS += msm-pcm-loopback-v2.o + PLATFORM_OBJS += msm-pcm-q6-noirq.o + PLATFORM_OBJS += msm-pcm-q6-v2.o + PLATFORM_OBJS += msm-pcm-routing-v2.o + PLATFORM_OBJS += msm-pcm-voice-v2.o + PLATFORM_OBJS += msm-pcm-voip-v2.o + PLATFORM_OBJS += msm-transcode-loopback-q6-v2.o + PLATFORM_OBJS += platform_init.o +endif +ifdef CONFIG_DOLBY_DS2 + PLATFORM_OBJS += msm-ds2-dap-config.o +endif +ifdef CONFIG_DOLBY_LICENSE + PLATFORM_OBJS += msm-ds2-dap-config.o +endif +ifdef CONFIG_SND_HWDEP_ROUTING + PLATFORM_OBJS += msm-pcm-routing-devdep.o +endif +ifdef CONFIG_QTI_PP + PLATFORM_OBJS += msm-qti-pp-config.o +endif + +LINUX_INC += -Iinclude/linux + +INCS += $(COMMON_INC) \ + $(UAPI_INC) + +EXTRA_CFLAGS += $(INCS) + +CDEFINES += -DANI_LITTLE_BYTE_ENDIAN \ + -DANI_LITTLE_BIT_ENDIAN \ + -DDOT11F_LITTLE_ENDIAN_HOST \ + -DANI_COMPILER_TYPE_GCC \ + -DANI_OS_TYPE_ANDROID=6 \ + -DPTT_SOCK_SVC_ENABLE \ + -Wall\ + -Werror\ + -D__linux__ + +KBUILD_CPPFLAGS += $(CDEFINES) + +# Currently, for versions of gcc which support it, the kernel Makefile +# is disabling the maybe-uninitialized warning. Re-enable it for the +# AUDIO driver. Note that we must use EXTRA_CFLAGS here so that it +# will override the kernel settings. +ifeq ($(call cc-option-yn, -Wmaybe-uninitialized),y) +EXTRA_CFLAGS += -Wmaybe-uninitialized +endif +#EXTRA_CFLAGS += -Wmissing-prototypes + +ifeq ($(call cc-option-yn, -Wheader-guard),y) +EXTRA_CFLAGS += -Wheader-guard +endif + +ifeq ($(KERNEL_BUILD), 0) +KBUILD_EXTRA_SYMBOLS +=$(OUT)/obj/vendor/qcom/opensource/audio-kernel/dsp/Module.symvers +KBUILD_EXTRA_SYMBOLS +=$(OUT)/obj/vendor/qcom/opensource/audio-kernel/asoc/codecs/Module.symvers +KBUILD_EXTRA_SYMBOLS +=$(OUT)/obj/vendor/qcom/opensource/audio-kernel/asoc/codecs/wcd934x/Module.symvers +KBUILD_EXTRA_SYMBOLS +=$(OUT)/obj/vendor/qcom/opensource/audio-kernel/asoc/codecs/wcd937x/Module.symvers +KBUILD_EXTRA_SYMBOLS +=$(OUT)/obj/vendor/qcom/opensource/audio-kernel/asoc/codecs/sdm660_cdc/Module.symvers +KBUILD_EXTRA_SYMBOLS +=$(OUT)/obj/vendor/qcom/opensource/audio-kernel/asoc/codecs/msm_sdw/Module.symvers +endif +ifeq ($(KERNEL_BUILD), 1) + obj-y += codecs/ +endif + +ifeq ($(CONFIG_SND_SOC_GCOV), y) +GCOV_PROFILE := y +endif + +# Module information used by KBuild framework +obj-$(CONFIG_SND_SOC_QDSP6V2) += platform_dlkm.o +platform_dlkm-y := $(PLATFORM_OBJS) + +obj-$(CONFIG_SND_SOC_MACHINE_SDM845) += machine_dlkm.o +machine_dlkm-y := $(MACHINE_OBJS) + +obj-$(CONFIG_SND_SOC_SM8150) += machine_dlkm.o +machine_dlkm-y := $(MACHINE_OBJS) + +obj-$(CONFIG_SND_SOC_SM6150) += machine_dlkm.o +machine_dlkm-y := $(MACHINE_OBJS) + +obj-$(CONFIG_SND_SOC_QCS405) += machine_dlkm.o +machine_dlkm-y := $(MACHINE_OBJS) + +obj-$(CONFIG_SND_SOC_EXT_CODEC) += machine_dlkm.o +machine_dlkm-y := $(MACHINE_OBJS) + +obj-$(CONFIG_SND_SOC_INT_CODEC) += machine_dlkm.o +machine_dlkm-y := $(MACHINE_OBJS) + +obj-$(CONFIG_SND_SOC_CPE) += cpe_lsm_dlkm.o +cpe_lsm_dlkm-y := $(CPE_LSM_OBJS) + +# inject some build related information +DEFINES += -DBUILD_TIMESTAMP=\"$(shell date -u +'%Y-%m-%dT%H:%M:%SZ')\" diff --git a/audio-kernel/asoc/codecs/Android.mk b/audio-kernel/asoc/codecs/Android.mk new file mode 100644 index 000000000..1bd6c1e8c --- /dev/null +++ b/audio-kernel/asoc/codecs/Android.mk @@ -0,0 +1,133 @@ +# Android makefile for audio kernel modules + +# Assume no targets will be supported + +# Check if this driver needs be built for current target +ifeq ($(call is-board-platform,sdm845),true) +AUDIO_SELECT := CONFIG_SND_SOC_SDM845=m +endif + +ifeq ($(call is-board-platform-in-list,msm8953 sdm670 qcs605),true) +AUDIO_SELECT := CONFIG_SND_SOC_SDM670=m +endif + +ifeq ($(call is-board-platform,msmnile),true) +AUDIO_SELECT := CONFIG_SND_SOC_SM8150=m +endif + +ifeq ($(call is-board-platform,$(MSMSTEPPE)),true) +AUDIO_SELECT := CONFIG_SND_SOC_SM6150=m +endif + +ifeq ($(call is-board-platform,$(TRINKET)),true) +AUDIO_SELECT := CONFIG_SND_SOC_SM6150=m +endif + +AUDIO_CHIPSET := audio +# Build/Package only in case of supported target +ifeq ($(call is-board-platform-in-list,msm8953 sdm845 sdm670 qcs605 msmnile $(MSMSTEPPE) $(TRINKET)),true) + +LOCAL_PATH := $(call my-dir) + +# This makefile is only for DLKM +ifneq ($(findstring vendor,$(LOCAL_PATH)),) + +ifneq ($(findstring opensource,$(LOCAL_PATH)),) + AUDIO_BLD_DIR := $(shell pwd)/vendor/qcom/opensource/audio-kernel +endif # opensource + +DLKM_DIR := $(TOP)/device/qcom/common/dlkm + +# Build audio.ko as $(AUDIO_CHIPSET)_audio.ko +########################################################### +# This is set once per LOCAL_PATH, not per (kernel) module +KBUILD_OPTIONS := AUDIO_ROOT=$(AUDIO_BLD_DIR) + +# We are actually building audio.ko here, as per the +# requirement we are specifying _audio.ko as LOCAL_MODULE. +# This means we need to rename the module to _audio.ko +# after audio.ko is built. +KBUILD_OPTIONS += MODNAME=wcd_core_dlkm +KBUILD_OPTIONS += BOARD_PLATFORM=$(TARGET_BOARD_PLATFORM) +KBUILD_OPTIONS += $(AUDIO_SELECT) + +########################################################### +include $(CLEAR_VARS) +LOCAL_MODULE := $(AUDIO_CHIPSET)_wcd_core.ko +LOCAL_MODULE_KBUILD_NAME := wcd_core_dlkm.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_DEBUG_ENABLE := true +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk +########################################################### +include $(CLEAR_VARS) +LOCAL_MODULE := $(AUDIO_CHIPSET)_wcd9xxx.ko +LOCAL_MODULE_KBUILD_NAME := wcd9xxx_dlkm.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_DEBUG_ENABLE := true +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk +########################################################### +ifeq ($(call is-board-platform-in-list,sdm670 qcs605 $(TRINKET)),true) +include $(CLEAR_VARS) +LOCAL_MODULE := $(AUDIO_CHIPSET)_wcd_cpe.ko +LOCAL_MODULE_KBUILD_NAME := wcd_cpe_dlkm.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_DEBUG_ENABLE := true +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk +endif +########################################################### +include $(CLEAR_VARS) +LOCAL_MODULE := $(AUDIO_CHIPSET)_wcd_spi.ko +LOCAL_MODULE_KBUILD_NAME := wcd_spi_dlkm.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_DEBUG_ENABLE := true +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk +########################################################### +ifeq ($(call is-board-platform-in-list,msm8953 sdm670 qcs605 $(TRINKET)),true) +include $(CLEAR_VARS) +LOCAL_MODULE := $(AUDIO_CHIPSET)_wcd9335.ko +LOCAL_MODULE_KBUILD_NAME := wcd9335_dlkm.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_DEBUG_ENABLE := true +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk +endif +########################################################### +include $(CLEAR_VARS) +LOCAL_MODULE := $(AUDIO_CHIPSET)_wsa881x.ko +LOCAL_MODULE_KBUILD_NAME := wsa881x_dlkm.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_DEBUG_ENABLE := true +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk +########################################################### +include $(CLEAR_VARS) +LOCAL_MODULE := $(AUDIO_CHIPSET)_stub.ko +LOCAL_MODULE_KBUILD_NAME := stub_dlkm.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_DEBUG_ENABLE := true +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk +########################################################### +include $(CLEAR_VARS) +LOCAL_MODULE := $(AUDIO_CHIPSET)_mbhc.ko +LOCAL_MODULE_KBUILD_NAME := mbhc_dlkm.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_DEBUG_ENABLE := true +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk +########################################################### +include $(CLEAR_VARS) +LOCAL_MODULE := $(AUDIO_CHIPSET)_hdmi.ko +LOCAL_MODULE_KBUILD_NAME := hdmi_dlkm.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_DEBUG_ENABLE := true +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk +########################################################### + +endif # DLKM check +endif # supported target check diff --git a/audio-kernel/asoc/codecs/Kbuild b/audio-kernel/asoc/codecs/Kbuild new file mode 100644 index 000000000..399f9b6fe --- /dev/null +++ b/audio-kernel/asoc/codecs/Kbuild @@ -0,0 +1,235 @@ +# We can build either as part of a standalone Kernel build or as +# an external module. Determine which mechanism is being used +ifeq ($(MODNAME),) + KERNEL_BUILD := 1 +else + KERNEL_BUILD := 0 +endif + +ifeq ($(KERNEL_BUILD), 1) + # These are configurable via Kconfig for kernel-based builds + # Need to explicitly configure for Android-based builds + AUDIO_BLD_DIR := $(shell pwd)/kernel/msm-4.14 + AUDIO_ROOT := $(AUDIO_BLD_DIR)/techpack/audio +endif + +ifeq ($(KERNEL_BUILD), 0) + ifeq ($(CONFIG_ARCH_SDM845), y) + include $(AUDIO_ROOT)/config/sdm845auto.conf + export + INCS += -include $(AUDIO_ROOT)/config/sdm845autoconf.h + endif + ifeq ($(CONFIG_ARCH_SDM670), y) + include $(AUDIO_ROOT)/config/sdm670auto.conf + export + INCS += -include $(AUDIO_ROOT)/config/sdm670autoconf.h + endif + ifeq ($(CONFIG_ARCH_SDM450), y) + include $(AUDIO_ROOT)/config/sdm670auto.conf + export + INCS += -include $(AUDIO_ROOT)/config/sdm670autoconf.h + endif + ifeq ($(CONFIG_ARCH_SM8150), y) + include $(AUDIO_ROOT)/config/sm8150auto.conf + export + INCS += -include $(AUDIO_ROOT)/config/sm8150autoconf.h + endif + ifeq ($(CONFIG_ARCH_SM6150), y) + include $(AUDIO_ROOT)/config/sm6150auto.conf + export + INCS += -include $(AUDIO_ROOT)/config/sm6150autoconf.h + endif + ifeq ($(CONFIG_ARCH_TRINKET), y) + include $(AUDIO_ROOT)/config/trinketauto.conf + export + INCS += -include $(AUDIO_ROOT)/config/trinketautoconf.h + endif + ifeq ($(CONFIG_ARCH_SDMSHRIKE), y) + include $(AUDIO_ROOT)/config/sm8150auto.conf + export + INCS += -include $(AUDIO_ROOT)/config/sm8150autoconf.h + endif + ifeq ($(CONFIG_ARCH_QCS405), y) + include $(AUDIO_ROOT)/config/qcs405auto.conf + export + INCS += -include $(AUDIO_ROOT)/config/qcs405autoconf.h + endif +endif + +# As per target team, build is done as follows: +# Defconfig : build with default flags +# Slub : defconfig + CONFIG_SLUB_DEBUG := y + +# CONFIG_SLUB_DEBUG_ON := y + CONFIG_PAGE_POISONING := y +# Perf : Using appropriate msmXXXX-perf_defconfig +# +# Shipment builds (user variants) should not have any debug feature +# enabled. This is identified using 'TARGET_BUILD_VARIANT'. Slub builds +# are identified using the CONFIG_SLUB_DEBUG_ON configuration. Since +# there is no other way to identify defconfig builds, QTI internal +# representation of perf builds (identified using the string 'perf'), +# is used to identify if the build is a slub or defconfig one. This +# way no critical debug feature will be enabled for perf and shipment +# builds. Other OEMs are also protected using the TARGET_BUILD_VARIANT +# config. + +############ UAPI ############ +UAPI_DIR := uapi +UAPI_INC := -I$(AUDIO_ROOT)/include/$(UAPI_DIR) + +############ COMMON ############ +COMMON_DIR := include +COMMON_INC := -I$(AUDIO_ROOT)/$(COMMON_DIR) + +############ ASoC Codecs ############ +ifdef CONFIG_WCD9XXX_CODEC_CORE + CORE_OBJS += wcd9xxx-rst.o + CORE_OBJS += wcd9xxx-core-init.o + CORE_OBJS += wcd9xxx-core.o + CORE_OBJS += wcd9xxx-irq.o + CORE_OBJS += wcd9xxx-slimslave.o + CORE_OBJS += wcd9xxx-utils.o + CORE_OBJS += wcd9335-regmap.o + CORE_OBJS += wcd9335-tables.o + CORE_OBJS += msm-cdc-pinctrl.o + CORE_OBJS += msm-cdc-supply.o + CORE_OBJS += wcd934x/wcd934x-regmap.o + CORE_OBJS += wcd934x/wcd934x-tables.o + CORE_OBJS += wcd9360/wcd9360-regmap.o +endif + +ifdef CONFIG_SND_SOC_WCD9XXX_V2 + WCD9XXX_OBJS += wcd-clsh.o + WCD9XXX_OBJS += wcd9xxx-common-v2.o + WCD9XXX_OBJS += wcd9xxx-resmgr-v2.o + WCD9XXX_OBJS += wcdcal-hwdep.o + WCD9XXX_OBJS += wcd9xxx-soc-init.o + WCD9XXX_OBJS += wcd-dsp-utils.o + WCD9XXX_OBJS += wcd-dsp-mgr.o + WCD9XXX_OBJS += audio-ext-clk-up.o +endif + +ifdef CONFIG_SND_SOC_WCD9335 + WCD9335_OBJS += wcd9335.o +endif + +ifdef CONFIG_SND_SOC_WSA881X + WSA881X_OBJS += wsa881x.o + WSA881X_OBJS += wsa881x-tables.o + WSA881X_OBJS += wsa881x-regmap.o + WSA881X_OBJS += wsa881x-temp-sensor.o +endif + +ifdef CONFIG_SND_SOC_MSM_STUB + STUB_OBJS += msm_stub.o +endif +ifdef CONFIG_SND_SOC_WCD_SPI + SPI_OBJS += wcd-spi.o +endif + +ifdef CONFIG_SND_SOC_WCD_CPE + WCD_CPE_OBJS += wcd_cpe_core.o + WCD_CPE_OBJS += wcd_cpe_services.o +endif + +ifdef CONFIG_SND_SOC_WCD_MBHC + MBHC_OBJS += wcd-mbhc-v2.o +endif + +ifdef CONFIG_SND_SOC_WCD_MBHC_ADC + MBHC_OBJS += wcd-mbhc-adc.o +endif + +ifdef CONFIG_SND_SOC_WCD_MBHC_LEGACY + MBHC_OBJS += wcd-mbhc-legacy.o +endif + +ifdef CONFIG_SND_SOC_MSM_HDMI_CODEC_RX + HDMICODEC_OBJS += msm_hdmi_codec_rx.o +endif + +ifdef CONFIG_SND_SOC_WCD_IRQ + CORE_OBJS += wcd-irq.o +endif + +LINUX_INC += -Iinclude/linux + +INCS += $(COMMON_INC) \ + $(UAPI_INC) + + +EXTRA_CFLAGS += $(INCS) + + +CDEFINES += -DANI_LITTLE_BYTE_ENDIAN \ + -DANI_LITTLE_BIT_ENDIAN \ + -DDOT11F_LITTLE_ENDIAN_HOST \ + -DANI_COMPILER_TYPE_GCC \ + -DANI_OS_TYPE_ANDROID=6 \ + -DPTT_SOCK_SVC_ENABLE \ + -Wall\ + -Werror\ + -D__linux__ + +KBUILD_CPPFLAGS += $(CDEFINES) + +# Currently, for versions of gcc which support it, the kernel Makefile +# is disabling the maybe-uninitialized warning. Re-enable it for the +# AUDIO driver. Note that we must use EXTRA_CFLAGS here so that it +# will override the kernel settings. +ifeq ($(call cc-option-yn, -Wmaybe-uninitialized),y) +EXTRA_CFLAGS += -Wmaybe-uninitialized +endif +#EXTRA_CFLAGS += -Wmissing-prototypes + +ifeq ($(call cc-option-yn, -Wheader-guard),y) +EXTRA_CFLAGS += -Wheader-guard +endif + + +ifeq ($(KERNEL_BUILD), 0) +KBUILD_EXTRA_SYMBOLS +=$(OUT)/obj/vendor/qcom/opensource/audio-kernel/dsp/Module.symvers +KBUILD_EXTRA_SYMBOLS +=$(OUT)/obj/vendor/qcom/opensource/audio-kernel/soc/Module.symvers +endif + +ifeq ($(KERNEL_BUILD), 1) + obj-y += wcd934x/ + obj-y += sdm660_cdc/ + obj-y += msm_sdw/ + obj-y += wcd9360/ + obj-y += wcd937x/ +endif + +ifeq ($(CONFIG_SND_SOC_GCOV), y) +GCOV_PROFILE := y +endif + +# Module information used by KBuild framework +obj-$(CONFIG_WCD9XXX_CODEC_CORE) += wcd_core_dlkm.o +wcd_core_dlkm-y := $(CORE_OBJS) + +obj-$(CONFIG_SND_SOC_WCD9XXX_V2) += wcd9xxx_dlkm.o +wcd9xxx_dlkm-y := $(WCD9XXX_OBJS) + +obj-$(CONFIG_SND_SOC_WCD9335) += wcd9335_dlkm.o +wcd9335_dlkm-y := $(WCD9335_OBJS) + +obj-$(CONFIG_SND_SOC_WSA881X) += wsa881x_dlkm.o +wsa881x_dlkm-y := $(WSA881X_OBJS) + +obj-$(CONFIG_SND_SOC_MSM_STUB) += stub_dlkm.o +stub_dlkm-y := $(STUB_OBJS) + +obj-$(CONFIG_SND_SOC_WCD_CPE) += wcd_cpe_dlkm.o +wcd_cpe_dlkm-y := $(WCD_CPE_OBJS) + +obj-$(CONFIG_SND_SOC_WCD_SPI) += wcd_spi_dlkm.o +wcd_spi_dlkm-y := $(SPI_OBJS) + +obj-$(CONFIG_SND_SOC_WCD_MBHC) += mbhc_dlkm.o +mbhc_dlkm-y := $(MBHC_OBJS) + +obj-$(CONFIG_SND_SOC_MSM_HDMI_CODEC_RX) += hdmi_dlkm.o +hdmi_dlkm-y := $(HDMICODEC_OBJS) + +# inject some build related information +DEFINES += -DBUILD_TIMESTAMP=\"$(shell date -u +'%Y-%m-%dT%H:%M:%SZ')\" diff --git a/audio-kernel/asoc/codecs/aqt1000/Android.mk b/audio-kernel/asoc/codecs/aqt1000/Android.mk new file mode 100644 index 000000000..ac26f6d52 --- /dev/null +++ b/audio-kernel/asoc/codecs/aqt1000/Android.mk @@ -0,0 +1,41 @@ +# Android makefile for audio kernel modules + +AUDIO_CHIPSET := audio +# Build/Package only in case of supported target +ifeq ($(call is-board-platform-in-list,sdm670),true) + +LOCAL_PATH := $(call my-dir) + +# This makefile is only for DLKM +ifneq ($(findstring vendor,$(LOCAL_PATH)),) + +ifneq ($(findstring opensource,$(LOCAL_PATH)),) + AUDIO_BLD_DIR := $(shell pwd)/vendor/qcom/opensource/audio-kernel +endif # opensource + +DLKM_DIR := $(TOP)/device/qcom/common/dlkm + +# Build audio.ko as $(AUDIO_CHIPSET)_audio.ko +########################################################### +# This is set once per LOCAL_PATH, not per (kernel) module +KBUILD_OPTIONS := AUDIO_ROOT=$(AUDIO_BLD_DIR) + +# We are actually building audio.ko here, as per the +# requirement we are specifying _audio.ko as LOCAL_MODULE. +# This means we need to rename the module to _audio.ko +# after audio.ko is built. +KBUILD_OPTIONS += MODNAME=aqt1000_cdc_dlkm +KBUILD_OPTIONS += BOARD_PLATFORM=$(TARGET_BOARD_PLATFORM) +KBUILD_OPTIONS += $(AUDIO_SELECT) + +########################################################### +include $(CLEAR_VARS) +LOCAL_MODULE := $(AUDIO_CHIPSET)_aqt1000_cdc.ko +LOCAL_MODULE_KBUILD_NAME := aqt1000_cdc_dlkm.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_DEBUG_ENABLE := true +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk +########################################################### +endif # DLKM check +endif # supported target check diff --git a/audio-kernel/asoc/codecs/aqt1000/Kbuild b/audio-kernel/asoc/codecs/aqt1000/Kbuild new file mode 100644 index 000000000..5b05798b2 --- /dev/null +++ b/audio-kernel/asoc/codecs/aqt1000/Kbuild @@ -0,0 +1,124 @@ +# We can build either as part of a standalone Kernel build or as +# an external module. Determine which mechanism is being used +ifeq ($(MODNAME),) + KERNEL_BUILD := 1 +else + KERNEL_BUILD := 0 +endif + +ifeq ($(KERNEL_BUILD), 1) + # These are configurable via Kconfig for kernel-based builds + # Need to explicitly configure for Android-based builds + AUDIO_BLD_DIR := $(shell pwd)/kernel/msm-4.14 + AUDIO_ROOT := $(AUDIO_BLD_DIR)/techpack/audio +endif + +ifeq ($(KERNEL_BUILD), 0) + # These are configurable via Kconfig for kernel-based builds + # Need to explicitly configure for Android-based builds + + ifeq ($(CONFIG_ARCH_SDM670), y) + include $(AUDIO_ROOT)/config/sdm670auto.conf + export + INCS += -include $(AUDIO_ROOT)/config/sdm670autoconf.h + endif + + ifeq ($(CONFIG_ARCH_SM8150), y) + include $(AUDIO_ROOT)/config/sm8150auto.conf + export + INCS += -include $(AUDIO_ROOT)/config/sm8150autoconf.h + endif + + ifeq ($(CONFIG_ARCH_SDMSHRIKE), y) + include $(AUDIO_ROOT)/config/sm8150auto.conf + export + INCS += -include $(AUDIO_ROOT)/config/sm8150autoconf.h + endif +endif + +# As per target team, build is done as follows: +# Defconfig : build with default flags +# Slub : defconfig + CONFIG_SLUB_DEBUG := y + +# CONFIG_SLUB_DEBUG_ON := y + CONFIG_PAGE_POISONING := y +# Perf : Using appropriate msmXXXX-perf_defconfig +# +# Shipment builds (user variants) should not have any debug feature +# enabled. This is identified using 'TARGET_BUILD_VARIANT'. Slub builds +# are identified using the CONFIG_SLUB_DEBUG_ON configuration. Since +# there is no other way to identify defconfig builds, QTI internal +# representation of perf builds (identified using the string 'perf'), +# is used to identify if the build is a slub or defconfig one. This +# way no critical debug feature will be enabled for perf and shipment +# builds. Other OEMs are also protected using the TARGET_BUILD_VARIANT +# config. + +############ UAPI ############ +UAPI_DIR := uapi +UAPI_INC := -I$(AUDIO_ROOT)/include/$(UAPI_DIR) + +############ COMMON ############ +COMMON_DIR := include +COMMON_INC := -I$(AUDIO_ROOT)/$(COMMON_DIR) + +# for AQT1000 Codec +ifeq ($(CONFIG_SND_SOC_AQT1000), m) + AQT1000_CDC_OBJS += aqt1000-regmap.o + AQT1000_CDC_OBJS += aqt1000-utils.o + AQT1000_CDC_OBJS += aqt1000-core.o + AQT1000_CDC_OBJS += aqt1000-irq.o + AQT1000_CDC_OBJS += aqt1000-clsh.o + AQT1000_CDC_OBJS += aqt1000.o + AQT1000_CDC_OBJS += aqt1000-mbhc.o +endif + +LINUX_INC += -Iinclude/linux + +INCS += $(COMMON_INC) \ + $(UAPI_INC) + +EXTRA_CFLAGS += $(INCS) + +CDEFINES += -DANI_LITTLE_BYTE_ENDIAN \ + -DANI_LITTLE_BIT_ENDIAN \ + -DDOT11F_LITTLE_ENDIAN_HOST \ + -DANI_COMPILER_TYPE_GCC \ + -DANI_OS_TYPE_ANDROID=6 \ + -DPTT_SOCK_SVC_ENABLE \ + -Wall\ + -Werror\ + -D__linux__ + +KBUILD_CPPFLAGS += $(CDEFINES) + +# Currently, for versions of gcc which support it, the kernel Makefile +# is disabling the maybe-uninitialized warning. Re-enable it for the +# AUDIO driver. Note that we must use EXTRA_CFLAGS here so that it +# will override the kernel settings. +ifeq ($(call cc-option-yn, -Wmaybe-uninitialized),y) +EXTRA_CFLAGS += -Wmaybe-uninitialized +endif +#EXTRA_CFLAGS += -Wmissing-prototypes + +ifeq ($(call cc-option-yn, -Wheader-guard),y) +EXTRA_CFLAGS += -Wheader-guard +endif + +ifeq ($(KERNEL_BUILD), 0) +KBUILD_EXTRA_SYMBOLS +=$(OUT)/obj/vendor/qcom/opensource/audio-kernel/ipc/Module.symvers +KBUILD_EXTRA_SYMBOLS +=$(OUT)/obj/vendor/qcom/opensource/audio-kernel/dsp/Module.symvers +KBUILD_EXTRA_SYMBOLS +=$(OUT)/obj/vendor/qcom/opensource/audio-kernel/asoc/Module.symvers +KBUILD_EXTRA_SYMBOLS +=$(OUT)/obj/vendor/qcom/opensource/audio-kernel/asoc/codecs/Module.symvers +KBUILD_EXTRA_SYMBOLS +=$(OUT)/obj/vendor/qcom/opensource/audio-kernel/soc/Module.symvers +KBUILD_EXTRA_SYMBOLS +=$(OUT)/obj/vendor/qcom/opensource/audio-kernel/asoc/codecs/aqt1000/Module.symvers +endif + +ifeq ($(CONFIG_SND_SOC_GCOV), y) +GCOV_PROFILE := y +endif + +# Module information used by KBuild framework +obj-$(CONFIG_SND_SOC_AQT1000) += aqt1000_cdc_dlkm.o +aqt1000_cdc_dlkm-y := $(AQT1000_CDC_OBJS) + +# inject some build related information +DEFINES += -DBUILD_TIMESTAMP=\"$(shell date -u +'%Y-%m-%dT%H:%M:%SZ')\" diff --git a/audio-kernel/asoc/codecs/aqt1000/aqt1000-api.h b/audio-kernel/asoc/codecs/aqt1000/aqt1000-api.h new file mode 100644 index 000000000..ef2e5267f --- /dev/null +++ b/audio-kernel/asoc/codecs/aqt1000/aqt1000-api.h @@ -0,0 +1,28 @@ +/* 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 AQT1000_API_H +#define AQT1000_API_H + +#include +#include +#include +#include + +extern int aqt_mbhc_micb_adjust_voltage(struct snd_soc_codec *codec, + int volt, int micb_num); +extern int aqt_cdc_mclk_enable(struct snd_soc_codec *codec, bool enable); +extern int aqt_get_micb_vout_ctl_val(u32 micb_mv); +extern int aqt_micbias_control(struct snd_soc_codec *codec, int micb_num, + int req, bool is_dapm); + +#endif /* AQT1000_API_H */ diff --git a/audio-kernel/asoc/codecs/aqt1000/aqt1000-clsh.c b/audio-kernel/asoc/codecs/aqt1000/aqt1000-clsh.c new file mode 100644 index 000000000..640795dc7 --- /dev/null +++ b/audio-kernel/asoc/codecs/aqt1000/aqt1000-clsh.c @@ -0,0 +1,805 @@ +/* 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 + * 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 +#include +#include +#include +#include +#include "aqt1000-registers.h" +#include "aqt1000-clsh.h" + +#define AQT_USLEEP_RANGE 50 +#define MAX_IMPED_PARAMS 6 + +enum aqt_vref_dac_sel { + VREF_N1P9V = 0, + VREF_N1P86V, + VREF_N181V, + VREF_N1P74V, + VREF_N1P7V, + VREF_N0P9V, + VREF_N1P576V, + VREF_N1P827V, +}; + +enum aqt_vref_ctl { + CONTROLLER = 0, + I2C, +}; + +enum aqt_hd2_res_div_ctl { + DISCONNECT = 0, + P5_0P35, + P75_0P68, + P82_0P77, + P9_0P87, +}; + +enum aqt_curr_bias_err_amp { + I_0P25UA = 0, + I_0P5UA, + I_0P75UA, + I_1UA, + I_1P25UA, + I_1P5UA, + I_1P75UA, + I_2UA, +}; + +static const struct aqt_reg_mask_val imped_table_aqt[][MAX_IMPED_PARAMS] = { + { + {AQT1000_CDC_RX1_RX_VOL_CTL, 0xff, 0xf2}, + {AQT1000_CDC_RX1_RX_VOL_MIX_CTL, 0xff, 0xf2}, + {AQT1000_CDC_RX1_RX_PATH_SEC1, 0x01, 0x00}, + {AQT1000_CDC_RX2_RX_VOL_CTL, 0xff, 0xf2}, + {AQT1000_CDC_RX2_RX_VOL_MIX_CTL, 0xff, 0xf2}, + {AQT1000_CDC_RX2_RX_PATH_SEC1, 0x01, 0x00}, + }, + { + {AQT1000_CDC_RX1_RX_VOL_CTL, 0xff, 0xf4}, + {AQT1000_CDC_RX1_RX_VOL_MIX_CTL, 0xff, 0xf4}, + {AQT1000_CDC_RX1_RX_PATH_SEC1, 0x01, 0x00}, + {AQT1000_CDC_RX2_RX_VOL_CTL, 0xff, 0xf4}, + {AQT1000_CDC_RX2_RX_VOL_MIX_CTL, 0xff, 0xf4}, + {AQT1000_CDC_RX2_RX_PATH_SEC1, 0x01, 0x00}, + }, + { + {AQT1000_CDC_RX1_RX_VOL_CTL, 0xff, 0xf7}, + {AQT1000_CDC_RX1_RX_VOL_MIX_CTL, 0xff, 0xf7}, + {AQT1000_CDC_RX1_RX_PATH_SEC1, 0x01, 0x01}, + {AQT1000_CDC_RX2_RX_VOL_CTL, 0xff, 0xf7}, + {AQT1000_CDC_RX2_RX_VOL_MIX_CTL, 0xff, 0xf7}, + {AQT1000_CDC_RX2_RX_PATH_SEC1, 0x01, 0x01}, + }, + { + {AQT1000_CDC_RX1_RX_VOL_CTL, 0xff, 0xf9}, + {AQT1000_CDC_RX1_RX_VOL_MIX_CTL, 0xff, 0xf9}, + {AQT1000_CDC_RX1_RX_PATH_SEC1, 0x01, 0x00}, + {AQT1000_CDC_RX2_RX_VOL_CTL, 0xff, 0xf9}, + {AQT1000_CDC_RX2_RX_VOL_MIX_CTL, 0xff, 0xf9}, + {AQT1000_CDC_RX2_RX_PATH_SEC1, 0x01, 0x00}, + }, + { + {AQT1000_CDC_RX1_RX_VOL_CTL, 0xff, 0xfa}, + {AQT1000_CDC_RX1_RX_VOL_MIX_CTL, 0xff, 0xfa}, + {AQT1000_CDC_RX1_RX_PATH_SEC1, 0x01, 0x00}, + {AQT1000_CDC_RX2_RX_VOL_CTL, 0xff, 0xfa}, + {AQT1000_CDC_RX2_RX_VOL_MIX_CTL, 0xff, 0xfa}, + {AQT1000_CDC_RX2_RX_PATH_SEC1, 0x01, 0x00}, + }, + { + {AQT1000_CDC_RX1_RX_VOL_CTL, 0xff, 0xfb}, + {AQT1000_CDC_RX1_RX_VOL_MIX_CTL, 0xff, 0xfb}, + {AQT1000_CDC_RX1_RX_PATH_SEC1, 0x01, 0x00}, + {AQT1000_CDC_RX2_RX_VOL_CTL, 0xff, 0xfb}, + {AQT1000_CDC_RX2_RX_VOL_MIX_CTL, 0xff, 0xfb}, + {AQT1000_CDC_RX2_RX_PATH_SEC1, 0x01, 0x00}, + }, + { + {AQT1000_CDC_RX1_RX_VOL_CTL, 0xff, 0xfc}, + {AQT1000_CDC_RX1_RX_VOL_MIX_CTL, 0xff, 0xfc}, + {AQT1000_CDC_RX1_RX_PATH_SEC1, 0x01, 0x00}, + {AQT1000_CDC_RX2_RX_VOL_CTL, 0xff, 0xfc}, + {AQT1000_CDC_RX2_RX_VOL_MIX_CTL, 0xff, 0xfc}, + {AQT1000_CDC_RX2_RX_PATH_SEC1, 0x01, 0x00}, + }, + { + {AQT1000_CDC_RX1_RX_VOL_CTL, 0xff, 0xfd}, + {AQT1000_CDC_RX1_RX_VOL_MIX_CTL, 0xff, 0xfd}, + {AQT1000_CDC_RX1_RX_PATH_SEC1, 0x01, 0x00}, + {AQT1000_CDC_RX2_RX_VOL_CTL, 0xff, 0xfd}, + {AQT1000_CDC_RX2_RX_VOL_MIX_CTL, 0xff, 0xfd}, + {AQT1000_CDC_RX2_RX_PATH_SEC1, 0x01, 0x00}, + }, + { + {AQT1000_CDC_RX1_RX_VOL_CTL, 0xff, 0xfd}, + {AQT1000_CDC_RX1_RX_VOL_MIX_CTL, 0xff, 0xfd}, + {AQT1000_CDC_RX1_RX_PATH_SEC1, 0x01, 0x01}, + {AQT1000_CDC_RX2_RX_VOL_CTL, 0xff, 0xfd}, + {AQT1000_CDC_RX2_RX_VOL_MIX_CTL, 0xff, 0xfd}, + {AQT1000_CDC_RX2_RX_PATH_SEC1, 0x01, 0x01}, + }, +}; + +static const struct aqt_imped_val imped_index[] = { + {4, 0}, + {5, 1}, + {6, 2}, + {7, 3}, + {8, 4}, + {9, 5}, + {10, 6}, + {11, 7}, + {12, 8}, + {13, 9}, +}; + +static void (*clsh_state_fp[NUM_CLSH_STATES])(struct snd_soc_codec *, + struct aqt_clsh_cdc_data *, + u8 req_state, bool en, int mode); + +static int get_impedance_index(int imped) +{ + int i = 0; + + if (imped < imped_index[i].imped_val) { + pr_debug("%s, detected impedance is less than 4 Ohm\n", + __func__); + i = 0; + goto ret; + } + if (imped >= imped_index[ARRAY_SIZE(imped_index) - 1].imped_val) { + pr_debug("%s, detected impedance is greater than 12 Ohm\n", + __func__); + i = ARRAY_SIZE(imped_index) - 1; + goto ret; + } + for (i = 0; i < ARRAY_SIZE(imped_index) - 1; i++) { + if (imped >= imped_index[i].imped_val && + imped < imped_index[i + 1].imped_val) + break; + } +ret: + pr_debug("%s: selected impedance index = %d\n", + __func__, imped_index[i].index); + return imped_index[i].index; +} + +/* + * Function: aqt_clsh_imped_config + * Params: codec, imped, reset + * Description: + * This function updates HPHL and HPHR gain settings + * according to the impedance value. + */ +void aqt_clsh_imped_config(struct snd_soc_codec *codec, int imped, bool reset) +{ + int i; + int index = 0; + int table_size; + + static const struct aqt_reg_mask_val + (*imped_table_ptr)[MAX_IMPED_PARAMS]; + + table_size = ARRAY_SIZE(imped_table_aqt); + imped_table_ptr = imped_table_aqt; + + /* reset = 1, which means request is to reset the register values */ + if (reset) { + for (i = 0; i < MAX_IMPED_PARAMS; i++) + snd_soc_update_bits(codec, + imped_table_ptr[index][i].reg, + imped_table_ptr[index][i].mask, 0); + return; + } + index = get_impedance_index(imped); + if (index >= (ARRAY_SIZE(imped_index) - 1)) { + pr_debug("%s, impedance not in range = %d\n", __func__, imped); + return; + } + if (index >= table_size) { + pr_debug("%s, impedance index not in range = %d\n", __func__, + index); + return; + } + for (i = 0; i < MAX_IMPED_PARAMS; i++) + snd_soc_update_bits(codec, + imped_table_ptr[index][i].reg, + imped_table_ptr[index][i].mask, + imped_table_ptr[index][i].val); +} +EXPORT_SYMBOL(aqt_clsh_imped_config); + +static const char *mode_to_str(int mode) +{ + switch (mode) { + case CLS_H_NORMAL: + return "CLS_H_NORMAL"; + case CLS_H_HIFI: + return "CLS_H_HIFI"; + case CLS_H_LOHIFI: + return "CLS_H_LOHIFI"; + case CLS_H_LP: + return "CLS_H_LP"; + case CLS_H_ULP: + return "CLS_H_ULP"; + case CLS_AB: + return "CLS_AB"; + case CLS_AB_HIFI: + return "CLS_AB_HIFI"; + default: + return "CLS_H_INVALID"; + }; +} + +static const char *const state_to_str[] = { + [AQT_CLSH_STATE_IDLE] = "STATE_IDLE", + [AQT_CLSH_STATE_HPHL] = "STATE_HPH_L", + [AQT_CLSH_STATE_HPHR] = "STATE_HPH_R", + [AQT_CLSH_STATE_HPH_ST] = "STATE_HPH_ST", +}; + +static inline void +aqt_enable_clsh_block(struct snd_soc_codec *codec, + struct aqt_clsh_cdc_data *clsh_d, bool enable) +{ + if ((enable && ++clsh_d->clsh_users == 1) || + (!enable && --clsh_d->clsh_users == 0)) + snd_soc_update_bits(codec, AQT1000_CDC_CLSH_CRC, 0x01, + (u8) enable); + if (clsh_d->clsh_users < 0) + clsh_d->clsh_users = 0; + dev_dbg(codec->dev, "%s: clsh_users %d, enable %d", __func__, + clsh_d->clsh_users, enable); +} + +static inline bool aqt_clsh_enable_status(struct snd_soc_codec *codec) +{ + return snd_soc_read(codec, AQT1000_CDC_CLSH_CRC) & 0x01; +} + +static inline int aqt_clsh_get_int_mode(struct aqt_clsh_cdc_data *clsh_d, + int clsh_state) +{ + int mode; + + if ((clsh_state != AQT_CLSH_STATE_HPHL) && + (clsh_state != AQT_CLSH_STATE_HPHR)) + mode = CLS_NONE; + else + mode = clsh_d->interpolator_modes[ffs(clsh_state)]; + + return mode; +} + +static inline void aqt_clsh_set_int_mode(struct aqt_clsh_cdc_data *clsh_d, + int clsh_state, int mode) +{ + if ((clsh_state != AQT_CLSH_STATE_HPHL) && + (clsh_state != AQT_CLSH_STATE_HPHR)) + return; + + clsh_d->interpolator_modes[ffs(clsh_state)] = mode; +} + +static inline void aqt_clsh_set_buck_mode(struct snd_soc_codec *codec, + int mode) +{ + if (mode == CLS_H_HIFI || mode == CLS_H_LOHIFI || + mode == CLS_AB_HIFI || mode == CLS_AB) + snd_soc_update_bits(codec, AQT1000_ANA_RX_SUPPLIES, + 0x08, 0x08); /* set to HIFI */ + else + snd_soc_update_bits(codec, AQT1000_ANA_RX_SUPPLIES, + 0x08, 0x00); /* set to default */ +} + +static inline void aqt_clsh_set_flyback_mode(struct snd_soc_codec *codec, + int mode) +{ + if (mode == CLS_H_HIFI || mode == CLS_H_LOHIFI || + mode == CLS_AB_HIFI || mode == CLS_AB) + snd_soc_update_bits(codec, AQT1000_ANA_RX_SUPPLIES, + 0x04, 0x04); /* set to HIFI */ + else + snd_soc_update_bits(codec, AQT1000_ANA_RX_SUPPLIES, + 0x04, 0x00); /* set to Default */ +} + +static inline void aqt_clsh_gm3_boost_disable(struct snd_soc_codec *codec, + int mode) +{ + if (mode == CLS_H_HIFI || mode == CLS_H_LOHIFI || + mode == CLS_AB_HIFI || mode == CLS_AB) { + snd_soc_update_bits(codec, AQT1000_HPH_CNP_WG_CTL, + 0x80, 0x0); /* disable GM3 Boost */ + snd_soc_update_bits(codec, AQT1000_FLYBACK_VNEG_CTRL_4, + 0xF0, 0x80); + } else { + snd_soc_update_bits(codec, AQT1000_HPH_CNP_WG_CTL, + 0x80, 0x80); /* set to Default */ + snd_soc_update_bits(codec, AQT1000_FLYBACK_VNEG_CTRL_4, + 0xF0, 0x70); + } +} + +static inline void aqt_clsh_flyback_dac_ctl(struct snd_soc_codec *codec, + int vref) +{ + snd_soc_update_bits(codec, AQT1000_FLYBACK_VNEGDAC_CTRL_2, + 0xE0, (vref << 5)); +} + +static inline void aqt_clsh_mode_vref_ctl(struct snd_soc_codec *codec, + int vref_ctl) +{ + if (vref_ctl == I2C) { + snd_soc_update_bits(codec, AQT1000_CLASSH_MODE_3, 0x02, 0x02); + snd_soc_update_bits(codec, AQT1000_CLASSH_MODE_2, 0xFF, 0x1C); + } else { + snd_soc_update_bits(codec, AQT1000_CLASSH_MODE_2, 0xFF, 0x3A); + snd_soc_update_bits(codec, AQT1000_CLASSH_MODE_3, 0x02, 0x00); + } +} + +static inline void aqt_clsh_buck_current_bias_ctl(struct snd_soc_codec *codec, + bool enable) +{ + if (enable) { + snd_soc_update_bits(codec, AQT1000_BUCK_5V_IBIAS_CTL_4, + 0x70, (I_2UA << 4)); + snd_soc_update_bits(codec, AQT1000_BUCK_5V_IBIAS_CTL_4, + 0x07, I_0P25UA); + snd_soc_update_bits(codec, AQT1000_BUCK_5V_CTRL_CCL_2, + 0x3F, 0x3F); + } else { + snd_soc_update_bits(codec, AQT1000_BUCK_5V_IBIAS_CTL_4, + 0x70, (I_1UA << 4)); + snd_soc_update_bits(codec, AQT1000_BUCK_5V_IBIAS_CTL_4, + 0x07, I_1UA); + snd_soc_update_bits(codec, AQT1000_BUCK_5V_CTRL_CCL_2, + 0x3F, 0x20); + } +} + +static inline void aqt_clsh_rdac_hd2_ctl(struct snd_soc_codec *codec, + u8 hd2_div_ctl, u8 state) +{ + u16 reg = 0; + + if (state == AQT_CLSH_STATE_HPHL) + reg = AQT1000_HPH_NEW_INT_RDAC_HD2_CTL_L; + else if (state == AQT_CLSH_STATE_HPHR) + reg = AQT1000_HPH_NEW_INT_RDAC_HD2_CTL_R; + else + dev_err(codec->dev, "%s: Invalid state: %d\n", + __func__, state); + if (!reg) + snd_soc_update_bits(codec, reg, 0x0F, hd2_div_ctl); +} + +static inline void aqt_clsh_force_iq_ctl(struct snd_soc_codec *codec, + int mode) +{ + if (mode == CLS_H_LOHIFI || mode == CLS_AB) { + snd_soc_update_bits(codec, AQT1000_HPH_NEW_INT_PA_MISC2, + 0x20, 0x20); + snd_soc_update_bits(codec, AQT1000_RX_BIAS_HPH_LOWPOWER, + 0xF0, 0xC0); + snd_soc_update_bits(codec, AQT1000_HPH_PA_CTL1, + 0x0E, 0x02); + } else { + + snd_soc_update_bits(codec, AQT1000_HPH_NEW_INT_PA_MISC2, + 0x20, 0x0); + snd_soc_update_bits(codec, AQT1000_RX_BIAS_HPH_LOWPOWER, + 0xF0, 0x80); + snd_soc_update_bits(codec, AQT1000_HPH_PA_CTL1, + 0x0E, 0x06); + } +} + +static void aqt_clsh_buck_ctrl(struct snd_soc_codec *codec, + struct aqt_clsh_cdc_data *clsh_d, + int mode, + bool enable) +{ + /* enable/disable buck */ + if ((enable && (++clsh_d->buck_users == 1)) || + (!enable && (--clsh_d->buck_users == 0))) + snd_soc_update_bits(codec, AQT1000_ANA_RX_SUPPLIES, + (1 << 7), (enable << 7)); + dev_dbg(codec->dev, "%s: buck_users %d, enable %d, mode: %s", + __func__, clsh_d->buck_users, enable, mode_to_str(mode)); + /* + * 500us sleep is required after buck enable/disable + * as per HW requirement + */ + usleep_range(500, 500 + AQT_USLEEP_RANGE); +} + +static void aqt_clsh_flyback_ctrl(struct snd_soc_codec *codec, + struct aqt_clsh_cdc_data *clsh_d, + int mode, + bool enable) +{ + /* enable/disable flyback */ + if ((enable && (++clsh_d->flyback_users == 1)) || + (!enable && (--clsh_d->flyback_users == 0))) { + snd_soc_update_bits(codec, AQT1000_ANA_RX_SUPPLIES, + (1 << 6), (enable << 6)); + /* 100usec delay is needed as per HW requirement */ + usleep_range(100, 110); + } + dev_dbg(codec->dev, "%s: flyback_users %d, enable %d, mode: %s", + __func__, clsh_d->flyback_users, enable, mode_to_str(mode)); + /* + * 500us sleep is required after flyback enable/disable + * as per HW requirement + */ + usleep_range(500, 500 + AQT_USLEEP_RANGE); +} + +static void aqt_clsh_set_hph_mode(struct snd_soc_codec *codec, + int mode) +{ + u8 val = 0; + u8 gain = 0; + u8 res_val = VREF_FILT_R_0OHM; + u8 ipeak = DELTA_I_50MA; + + switch (mode) { + case CLS_H_NORMAL: + res_val = VREF_FILT_R_50KOHM; + val = 0x00; + gain = DAC_GAIN_0DB; + ipeak = DELTA_I_50MA; + break; + case CLS_AB: + val = 0x00; + gain = DAC_GAIN_0DB; + ipeak = DELTA_I_50MA; + break; + case CLS_AB_HIFI: + val = 0x08; + break; + case CLS_H_HIFI: + val = 0x08; + gain = DAC_GAIN_M0P2DB; + ipeak = DELTA_I_50MA; + break; + case CLS_H_LOHIFI: + val = 0x00; + break; + case CLS_H_ULP: + val = 0x0C; + break; + case CLS_H_LP: + val = 0x04; + ipeak = DELTA_I_30MA; + break; + default: + return; + }; + + if (mode == CLS_H_LOHIFI || mode == CLS_AB) + val = 0x04; + + snd_soc_update_bits(codec, AQT1000_ANA_HPH, 0x0C, val); +} + +static void aqt_clsh_set_buck_regulator_mode(struct snd_soc_codec *codec, + int mode) +{ + snd_soc_update_bits(codec, AQT1000_ANA_RX_SUPPLIES, + 0x02, 0x00); +} + +static void aqt_clsh_state_hph_st(struct snd_soc_codec *codec, + struct aqt_clsh_cdc_data *clsh_d, + u8 req_state, bool is_enable, int mode) +{ + dev_dbg(codec->dev, "%s: mode: %s, %s\n", __func__, mode_to_str(mode), + is_enable ? "enable" : "disable"); + + if (mode == CLS_AB || mode == CLS_AB_HIFI) + return; + + if (is_enable) { + if (req_state == AQT_CLSH_STATE_HPHL) + snd_soc_update_bits(codec, + AQT1000_CDC_RX1_RX_PATH_CFG0, + 0x40, 0x40); + if (req_state == AQT_CLSH_STATE_HPHR) + snd_soc_update_bits(codec, + AQT1000_CDC_RX2_RX_PATH_CFG0, + 0x40, 0x40); + } else { + if (req_state == AQT_CLSH_STATE_HPHL) + snd_soc_update_bits(codec, + AQT1000_CDC_RX1_RX_PATH_CFG0, + 0x40, 0x00); + if (req_state == AQT_CLSH_STATE_HPHR) + snd_soc_update_bits(codec, + AQT1000_CDC_RX2_RX_PATH_CFG0, + 0x40, 0x00); + } +} + +static void aqt_clsh_state_hph_r(struct snd_soc_codec *codec, + struct aqt_clsh_cdc_data *clsh_d, + u8 req_state, bool is_enable, int mode) +{ + dev_dbg(codec->dev, "%s: mode: %s, %s\n", __func__, mode_to_str(mode), + is_enable ? "enable" : "disable"); + + if (mode == CLS_H_NORMAL) { + dev_err(codec->dev, "%s: Normal mode not applicable for hph_r\n", + __func__); + return; + } + + if (is_enable) { + if (mode != CLS_AB && mode != CLS_AB_HIFI) { + aqt_enable_clsh_block(codec, clsh_d, true); + /* + * These K1 values depend on the Headphone Impedance + * For now it is assumed to be 16 ohm + */ + snd_soc_update_bits(codec, AQT1000_CDC_CLSH_K1_MSB, + 0x0F, 0x00); + snd_soc_update_bits(codec, AQT1000_CDC_CLSH_K1_LSB, + 0xFF, 0xC0); + snd_soc_update_bits(codec, + AQT1000_CDC_RX2_RX_PATH_CFG0, + 0x40, 0x40); + } + aqt_clsh_set_buck_regulator_mode(codec, mode); + aqt_clsh_set_flyback_mode(codec, mode); + aqt_clsh_gm3_boost_disable(codec, mode); + aqt_clsh_flyback_dac_ctl(codec, VREF_N0P9V); + aqt_clsh_mode_vref_ctl(codec, I2C); + aqt_clsh_force_iq_ctl(codec, mode); + aqt_clsh_rdac_hd2_ctl(codec, P82_0P77, req_state); + aqt_clsh_flyback_ctrl(codec, clsh_d, mode, true); + aqt_clsh_flyback_dac_ctl(codec, VREF_N1P827V); + aqt_clsh_set_buck_mode(codec, mode); + aqt_clsh_buck_ctrl(codec, clsh_d, mode, true); + aqt_clsh_mode_vref_ctl(codec, CONTROLLER); + aqt_clsh_buck_current_bias_ctl(codec, true); + aqt_clsh_set_hph_mode(codec, mode); + } else { + aqt_clsh_set_hph_mode(codec, CLS_H_NORMAL); + aqt_clsh_buck_current_bias_ctl(codec, false); + + if (mode != CLS_AB && mode != CLS_AB_HIFI) { + snd_soc_update_bits(codec, + AQT1000_CDC_RX2_RX_PATH_CFG0, + 0x40, 0x00); + aqt_enable_clsh_block(codec, clsh_d, false); + } + /* buck and flyback set to default mode and disable */ + aqt_clsh_buck_ctrl(codec, clsh_d, CLS_H_NORMAL, false); + aqt_clsh_flyback_ctrl(codec, clsh_d, CLS_H_NORMAL, false); + aqt_clsh_rdac_hd2_ctl(codec, P5_0P35, req_state); + aqt_clsh_force_iq_ctl(codec, CLS_H_NORMAL); + aqt_clsh_gm3_boost_disable(codec, CLS_H_NORMAL); + aqt_clsh_set_flyback_mode(codec, CLS_H_NORMAL); + aqt_clsh_set_buck_mode(codec, CLS_H_NORMAL); + aqt_clsh_set_buck_regulator_mode(codec, CLS_H_NORMAL); + } +} + +static void aqt_clsh_state_hph_l(struct snd_soc_codec *codec, + struct aqt_clsh_cdc_data *clsh_d, + u8 req_state, bool is_enable, int mode) +{ + dev_dbg(codec->dev, "%s: mode: %s, %s\n", __func__, mode_to_str(mode), + is_enable ? "enable" : "disable"); + + if (mode == CLS_H_NORMAL) { + dev_err(codec->dev, "%s: Normal mode not applicable for hph_l\n", + __func__); + return; + } + + if (is_enable) { + if (mode != CLS_AB && mode != CLS_AB_HIFI) { + aqt_enable_clsh_block(codec, clsh_d, true); + /* + * These K1 values depend on the Headphone Impedance + * For now it is assumed to be 16 ohm + */ + snd_soc_update_bits(codec, AQT1000_CDC_CLSH_K1_MSB, + 0x0F, 0x00); + snd_soc_update_bits(codec, AQT1000_CDC_CLSH_K1_LSB, + 0xFF, 0xC0); + snd_soc_update_bits(codec, + AQT1000_CDC_RX1_RX_PATH_CFG0, + 0x40, 0x40); + } + aqt_clsh_set_buck_regulator_mode(codec, mode); + aqt_clsh_set_flyback_mode(codec, mode); + aqt_clsh_gm3_boost_disable(codec, mode); + aqt_clsh_flyback_dac_ctl(codec, VREF_N0P9V); + aqt_clsh_mode_vref_ctl(codec, I2C); + aqt_clsh_force_iq_ctl(codec, mode); + aqt_clsh_rdac_hd2_ctl(codec, P82_0P77, req_state); + aqt_clsh_flyback_ctrl(codec, clsh_d, mode, true); + aqt_clsh_flyback_dac_ctl(codec, VREF_N1P827V); + aqt_clsh_set_buck_mode(codec, mode); + aqt_clsh_buck_ctrl(codec, clsh_d, mode, true); + aqt_clsh_mode_vref_ctl(codec, CONTROLLER); + aqt_clsh_buck_current_bias_ctl(codec, true); + aqt_clsh_set_hph_mode(codec, mode); + } else { + aqt_clsh_set_hph_mode(codec, CLS_H_NORMAL); + aqt_clsh_buck_current_bias_ctl(codec, false); + + if (mode != CLS_AB && mode != CLS_AB_HIFI) { + snd_soc_update_bits(codec, + AQT1000_CDC_RX1_RX_PATH_CFG0, + 0x40, 0x00); + aqt_enable_clsh_block(codec, clsh_d, false); + } + /* set buck and flyback to Default Mode */ + aqt_clsh_buck_ctrl(codec, clsh_d, CLS_H_NORMAL, false); + aqt_clsh_flyback_ctrl(codec, clsh_d, CLS_H_NORMAL, false); + aqt_clsh_rdac_hd2_ctl(codec, P5_0P35, req_state); + aqt_clsh_force_iq_ctl(codec, CLS_H_NORMAL); + aqt_clsh_gm3_boost_disable(codec, CLS_H_NORMAL); + aqt_clsh_set_flyback_mode(codec, CLS_H_NORMAL); + aqt_clsh_set_buck_mode(codec, CLS_H_NORMAL); + aqt_clsh_set_buck_regulator_mode(codec, CLS_H_NORMAL); + } +} + +static void aqt_clsh_state_err(struct snd_soc_codec *codec, + struct aqt_clsh_cdc_data *clsh_d, + u8 req_state, bool is_enable, int mode) +{ + dev_err(codec->dev, + "%s Wrong request for class H state machine requested to %s %s", + __func__, is_enable ? "enable" : "disable", + state_to_str[req_state]); +} + +/* + * Function: aqt_clsh_is_state_valid + * Params: state + * Description: + * Provides information on valid states of Class H configuration + */ +static bool aqt_clsh_is_state_valid(u8 state) +{ + switch (state) { + case AQT_CLSH_STATE_IDLE: + case AQT_CLSH_STATE_HPHL: + case AQT_CLSH_STATE_HPHR: + case AQT_CLSH_STATE_HPH_ST: + return true; + default: + return false; + }; +} + +/* + * Function: aqt_clsh_fsm + * Params: codec, cdc_clsh_d, req_state, req_type, clsh_event + * Description: + * This function handles PRE DAC and POST DAC conditions of different devices + * and updates class H configuration of different combination of devices + * based on validity of their states. cdc_clsh_d will contain current + * class h state information + */ +void aqt_clsh_fsm(struct snd_soc_codec *codec, + struct aqt_clsh_cdc_data *cdc_clsh_d, + u8 clsh_event, u8 req_state, + int int_mode) +{ + u8 old_state, new_state; + + switch (clsh_event) { + case AQT_CLSH_EVENT_PRE_DAC: + old_state = cdc_clsh_d->state; + new_state = old_state | req_state; + + if (!aqt_clsh_is_state_valid(new_state)) { + dev_err(codec->dev, + "%s: Class-H not a valid new state: %s\n", + __func__, state_to_str[new_state]); + return; + } + if (new_state == old_state) { + dev_err(codec->dev, + "%s: Class-H already in requested state: %s\n", + __func__, state_to_str[new_state]); + return; + } + cdc_clsh_d->state = new_state; + aqt_clsh_set_int_mode(cdc_clsh_d, req_state, int_mode); + (*clsh_state_fp[new_state]) (codec, cdc_clsh_d, req_state, + CLSH_REQ_ENABLE, int_mode); + dev_dbg(codec->dev, + "%s: ClassH state transition from %s to %s\n", + __func__, state_to_str[old_state], + state_to_str[cdc_clsh_d->state]); + break; + case AQT_CLSH_EVENT_POST_PA: + old_state = cdc_clsh_d->state; + new_state = old_state & (~req_state); + if (new_state < NUM_CLSH_STATES) { + if (!aqt_clsh_is_state_valid(old_state)) { + dev_err(codec->dev, + "%s:Invalid old state:%s\n", + __func__, state_to_str[old_state]); + return; + } + if (new_state == old_state) { + dev_err(codec->dev, + "%s: Class-H already in requested state: %s\n", + __func__,state_to_str[new_state]); + return; + } + (*clsh_state_fp[old_state]) (codec, cdc_clsh_d, + req_state, CLSH_REQ_DISABLE, + int_mode); + cdc_clsh_d->state = new_state; + aqt_clsh_set_int_mode(cdc_clsh_d, req_state, CLS_NONE); + dev_dbg(codec->dev, "%s: ClassH state transition from %s to %s\n", + __func__, state_to_str[old_state], + state_to_str[cdc_clsh_d->state]); + } + break; + }; +} +EXPORT_SYMBOL(aqt_clsh_fsm); + +/* + * Function: aqt_clsh_get_clsh_state + * Params: clsh + * Description: + * This function returns the state of the class H controller + */ +int aqt_clsh_get_clsh_state(struct aqt_clsh_cdc_data *clsh) +{ + return clsh->state; +} +EXPORT_SYMBOL(aqt_clsh_get_clsh_state); + +/* + * Function: aqt_clsh_init + * Params: clsh + * Description: + * This function initializes the class H controller + */ +void aqt_clsh_init(struct aqt_clsh_cdc_data *clsh) +{ + int i; + + clsh->state = AQT_CLSH_STATE_IDLE; + + for (i = 0; i < NUM_CLSH_STATES; i++) + clsh_state_fp[i] = aqt_clsh_state_err; + + clsh_state_fp[AQT_CLSH_STATE_HPHL] = aqt_clsh_state_hph_l; + clsh_state_fp[AQT_CLSH_STATE_HPHR] = aqt_clsh_state_hph_r; + clsh_state_fp[AQT_CLSH_STATE_HPH_ST] = aqt_clsh_state_hph_st; + /* Set interpolator modes to NONE */ + aqt_clsh_set_int_mode(clsh, AQT_CLSH_STATE_HPHL, CLS_NONE); + aqt_clsh_set_int_mode(clsh, AQT_CLSH_STATE_HPHR, CLS_NONE); + clsh->flyback_users = 0; + clsh->buck_users = 0; + clsh->clsh_users = 0; +} +EXPORT_SYMBOL(aqt_clsh_init); diff --git a/audio-kernel/asoc/codecs/aqt1000/aqt1000-clsh.h b/audio-kernel/asoc/codecs/aqt1000/aqt1000-clsh.h new file mode 100644 index 000000000..4a829b0c6 --- /dev/null +++ b/audio-kernel/asoc/codecs/aqt1000/aqt1000-clsh.h @@ -0,0 +1,115 @@ +/* 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 + * 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 _AQT1000_CLSH_H +#define _AQT1000_CLSH_H + +#include +#include +#include +#include + +#define CLSH_REQ_ENABLE true +#define CLSH_REQ_DISABLE false + +#define AQT_CLSH_EVENT_PRE_DAC 0x01 +#define AQT_CLSH_EVENT_POST_PA 0x02 +/* + * Basic states for Class H state machine. + * represented as a bit mask within a u8 data type + * bit 0: HPH Left mode + * bit 1: HPH Right mode + */ +#define AQT_CLSH_STATE_IDLE 0x00 +#define AQT_CLSH_STATE_HPHL (0x01 << 0) +#define AQT_CLSH_STATE_HPHR (0x01 << 1) + +/* + * Though number of CLSH states are 2, max state shoulbe be 3 + * because state array index starts from 1. + */ +#define AQT_CLSH_STATE_MAX 3 +#define NUM_CLSH_STATES (0x01 << AQT_CLSH_STATE_MAX) + + +/* Derived State: Bits 1 and 2 should be set for Headphone stereo */ +#define AQT_CLSH_STATE_HPH_ST (AQT_CLSH_STATE_HPHL | \ + AQT_CLSH_STATE_HPHR) + +enum { + CLS_H_NORMAL = 0, /* Class-H Default */ + CLS_H_HIFI, /* Class-H HiFi */ + CLS_H_LP, /* Class-H Low Power */ + CLS_AB, /* Class-AB Low HIFI*/ + CLS_H_LOHIFI, /* LoHIFI */ + CLS_H_ULP, /* Ultra Low power */ + CLS_AB_HIFI, /* Class-AB */ + CLS_NONE, /* None of the above modes */ +}; + +enum { + DAC_GAIN_0DB = 0, + DAC_GAIN_0P2DB, + DAC_GAIN_0P4DB, + DAC_GAIN_0P6DB, + DAC_GAIN_0P8DB, + DAC_GAIN_M0P2DB, + DAC_GAIN_M0P4DB, + DAC_GAIN_M0P6DB, +}; + +enum { + VREF_FILT_R_0OHM = 0, + VREF_FILT_R_25KOHM, + VREF_FILT_R_50KOHM, + VREF_FILT_R_100KOHM, +}; + +enum { + DELTA_I_0MA, + DELTA_I_10MA, + DELTA_I_20MA, + DELTA_I_30MA, + DELTA_I_40MA, + DELTA_I_50MA, +}; + +struct aqt_imped_val { + u32 imped_val; + u8 index; +}; + +struct aqt_clsh_cdc_data { + u8 state; + int flyback_users; + int buck_users; + int clsh_users; + int interpolator_modes[AQT_CLSH_STATE_MAX]; +}; + +struct aqt_reg_mask_val { + u16 reg; + u8 mask; + u8 val; +}; + +extern void aqt_clsh_fsm(struct snd_soc_codec *codec, + struct aqt_clsh_cdc_data *cdc_clsh_d, + u8 clsh_event, u8 req_state, + int int_mode); + +extern void aqt_clsh_init(struct aqt_clsh_cdc_data *clsh); +extern int aqt_clsh_get_clsh_state(struct aqt_clsh_cdc_data *clsh); +extern void aqt_clsh_imped_config(struct snd_soc_codec *codec, int imped, + bool reset); + +#endif /* _AQT1000_CLSH_H */ diff --git a/audio-kernel/asoc/codecs/aqt1000/aqt1000-core.c b/audio-kernel/asoc/codecs/aqt1000/aqt1000-core.c new file mode 100644 index 000000000..e6313c13e --- /dev/null +++ b/audio-kernel/asoc/codecs/aqt1000/aqt1000-core.c @@ -0,0 +1,646 @@ +/* Copyright (c) 2011-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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../msm-cdc-pinctrl.h" +#include "../msm-cdc-supply.h" +#include "aqt1000-registers.h" +#include "aqt1000-internal.h" +#include "aqt1000.h" +#include "aqt1000-utils.h" +#include "aqt1000-irq.h" + +static int aqt1000_bringup(struct aqt1000 *aqt) +{ + struct aqt1000_pdata *pdata; + u8 clk_div = 0, mclk = 1; + + if (!aqt->regmap) { + dev_err(aqt->dev, "%s: aqt regmap is NULL\n", __func__); + return -EINVAL; + } + + /* Bringup register write sequence */ + regmap_update_bits(aqt->regmap, AQT1000_BUCK_5V_CTRL_CCL_1, 0xF0, 0xF0); + regmap_update_bits(aqt->regmap, AQT1000_BIAS_CCOMP_FINE_ADJ, + 0xF0, 0x90); + regmap_update_bits(aqt->regmap, AQT1000_ANA_BIAS, 0x80, 0x80); + regmap_update_bits(aqt->regmap, AQT1000_ANA_BIAS, 0x40, 0x40); + + /* Added 1msec sleep as per HW requirement */ + usleep_range(1000, 1010); + + regmap_update_bits(aqt->regmap, AQT1000_ANA_BIAS, 0x40, 0x00); + + clk_div = 0x04; /* Assumption is CLK DIV 2 */ + pdata = dev_get_platdata(aqt->dev); + if (pdata) { + if (pdata->mclk_rate == AQT1000_CLK_12P288MHZ) + mclk = 0; + clk_div = (((pdata->ext_clk_rate / pdata->mclk_rate) >> 1) + << 2); + } + regmap_update_bits(aqt->regmap, AQT1000_CHIP_CFG0_CLK_CFG_MCLK, + 0x03, mclk); + + regmap_update_bits(aqt->regmap, AQT1000_CLK_SYS_MCLK1_PRG, + 0x0C, clk_div); + + /* Source clock enable */ + regmap_update_bits(aqt->regmap, AQT1000_CLK_SYS_MCLK1_PRG, 0x02, 0x02); + + /* Ungate the source clock */ + regmap_update_bits(aqt->regmap, AQT1000_CLK_SYS_MCLK1_PRG, 0x10, 0x10); + + /* Set the I2S_HS_CLK reference to CLK DIV 2 */ + regmap_update_bits(aqt->regmap, AQT1000_CLK_SYS_MCLK2_I2S_HS_CLK_PRG, + 0x60, 0x20); + + /* Set the PLL preset to CLK9P6M_IN_12P288M_OUT */ + regmap_update_bits(aqt->regmap, AQT1000_CLK_SYS_PLL_PRESET, 0x0F, 0x02); + + /* Enable clock PLL */ + regmap_update_bits(aqt->regmap, AQT1000_CLK_SYS_PLL_ENABLES, + 0x01, 0x01); + + /* Add 100usec delay as per HW requirement */ + usleep_range(100, 110); + + /* Set AQT to I2S Master */ + regmap_update_bits(aqt->regmap, AQT1000_I2S_I2S_0_CTL, 0x02, 0x02); + + /* Enable I2S HS clock */ + regmap_update_bits(aqt->regmap, AQT1000_CLK_SYS_MCLK2_I2S_HS_CLK_PRG, + 0x01, 0x01); + + regmap_update_bits(aqt->regmap, AQT1000_CHIP_CFG0_CLK_CFG_MCLK, + 0x04, 0x00); + + /* Add 100usec delay as per HW requirement */ + usleep_range(100, 110); + regmap_update_bits(aqt->regmap, AQT1000_CDC_CLK_RST_CTRL_MCLK_CONTROL, + 0x01, 0x01); + regmap_update_bits(aqt->regmap, AQT1000_CDC_CLK_RST_CTRL_FS_CNT_CONTROL, + 0x01, 0x01); + regmap_update_bits(aqt->regmap, AQT1000_CHIP_CFG0_CLK_CTL_CDC_DIG, + 0x01, 0x01); + + /* Codec digital reset */ + regmap_update_bits(aqt->regmap, AQT1000_CHIP_CFG0_RST_CTL, 0x01, 0x01); + /* Add 100usec delay as per HW requirement */ + usleep_range(100, 110); + + return 0; +} + +static int aqt1000_device_init(struct aqt1000 *aqt) +{ + int ret = 0; + + mutex_init(&aqt->io_lock); + mutex_init(&aqt->xfer_lock); + mutex_init(&aqt->cdc_bg_clk_lock); + mutex_init(&aqt->master_bias_lock); + + ret = aqt1000_bringup(aqt); + if (ret) { + ret = -EPROBE_DEFER; + goto done; + } + + ret = aqt_irq_init(aqt); + if (ret) + goto done; + + return ret; +done: + mutex_destroy(&aqt->io_lock); + mutex_destroy(&aqt->xfer_lock); + mutex_destroy(&aqt->cdc_bg_clk_lock); + mutex_destroy(&aqt->master_bias_lock); + return ret; +} + +static int aqt1000_i2c_write(struct aqt1000 *aqt1000, unsigned short reg, + void *val, int bytes) +{ + struct i2c_msg *msg; + int ret = 0; + u8 reg_addr = 0; + u8 data[bytes + 1]; + struct aqt1000_i2c *aqt1000_i2c; + u8 *value = (u8 *)val; + + aqt1000_i2c = &aqt1000->i2c_dev; + if (aqt1000_i2c == NULL || aqt1000_i2c->client == NULL) { + pr_err("%s: Failed to get device info\n", __func__); + return -ENODEV; + } + reg_addr = (u8)reg; + msg = &aqt1000_i2c->xfer_msg[0]; + msg->addr = aqt1000_i2c->client->addr; + msg->len = bytes + 1; + msg->flags = 0; + data[0] = reg; + data[1] = *value; + msg->buf = data; + ret = i2c_transfer(aqt1000_i2c->client->adapter, + aqt1000_i2c->xfer_msg, 1); + /* Try again if the write fails */ + if (ret != 1) { + ret = i2c_transfer(aqt1000_i2c->client->adapter, + aqt1000_i2c->xfer_msg, 1); + if (ret != 1) { + dev_err(aqt1000->dev, + "%s: I2C write failed, reg: 0x%x ret: %d\n", + __func__, reg, ret); + return ret; + } + } + dev_dbg(aqt1000->dev, "%s: write success register = %x val = %x\n", + __func__, reg, data[1]); + return 0; +} + +static int aqt1000_i2c_read(struct aqt1000 *aqt1000, unsigned short reg, + void *dst, int bytes) +{ + struct i2c_msg *msg; + int ret = 0; + u8 reg_addr = 0; + struct aqt1000_i2c *aqt1000_i2c; + u8 i = 0; + unsigned char *dest = (unsigned char *)dst; + + aqt1000_i2c = &aqt1000->i2c_dev; + if (aqt1000_i2c == NULL || aqt1000_i2c->client == NULL) { + pr_err("%s: Failed to get device info\n", __func__); + return -ENODEV; + } + for (i = 0; i < bytes; i++) { + reg_addr = (u8)reg++; + msg = &aqt1000_i2c->xfer_msg[0]; + msg->addr = aqt1000_i2c->client->addr; + msg->len = 1; + msg->flags = 0; + msg->buf = ®_addr; + + msg = &aqt1000_i2c->xfer_msg[1]; + msg->addr = aqt1000_i2c->client->addr; + msg->len = 1; + msg->flags = I2C_M_RD; + msg->buf = dest++; + ret = i2c_transfer(aqt1000_i2c->client->adapter, + aqt1000_i2c->xfer_msg, 2); + + /* Try again if read fails first time */ + if (ret != 2) { + ret = i2c_transfer(aqt1000_i2c->client->adapter, + aqt1000_i2c->xfer_msg, 2); + if (ret != 2) { + dev_err(aqt1000->dev, + "%s: I2C read failed, reg: 0x%x\n", + __func__, reg); + return ret; + } + } + } + return 0; +} + +static int aqt1000_reset(struct device *dev) +{ + struct aqt1000 *aqt1000; + int rc = 0; + + if (!dev) + return -ENODEV; + + aqt1000 = dev_get_drvdata(dev); + if (!aqt1000) + return -EINVAL; + + if (!aqt1000->aqt_rst_np) { + dev_err(dev, "%s: reset gpio device node not specified\n", + __func__); + return -EINVAL; + } + + if (!msm_cdc_pinctrl_get_state(aqt1000->aqt_rst_np)) { + rc = msm_cdc_pinctrl_select_sleep_state(aqt1000->aqt_rst_np); + if (rc) { + dev_err(dev, "%s: aqt sleep state request fail!\n", + __func__); + return rc; + } + + /* 20ms sleep required after pulling the reset gpio to LOW */ + msleep(20); + + rc = msm_cdc_pinctrl_select_active_state(aqt1000->aqt_rst_np); + if (rc) { + dev_err(dev, + "%s: aqt active state request fail, ret: %d\n", + __func__, rc); + return rc; + } + /* 20ms sleep required after pulling the reset gpio to HIGH */ + msleep(20); + } + + return rc; +} + +static int aqt1000_read_of_property_u32(struct device *dev, const char *name, + u32 *val) +{ + int rc = 0; + + rc = of_property_read_u32(dev->of_node, name, val); + if (rc) + dev_err(dev, "%s: Looking up %s property in node %s failed", + __func__, name, dev->of_node->full_name); + + return rc; +} + +static void aqt1000_dt_parse_micbias_info(struct device *dev, + struct aqt1000_micbias_setting *mb) +{ + u32 prop_val; + int rc; + + if (of_find_property(dev->of_node, "qcom,cdc-micbias-ldoh-v", NULL)) { + rc = aqt1000_read_of_property_u32(dev, + "qcom,cdc-micbias-ldoh-v", + &prop_val); + if (!rc) + mb->ldoh_v = (u8)prop_val; + } + + /* MB1 */ + if (of_find_property(dev->of_node, "qcom,cdc-micbias-cfilt1-mv", + NULL)) { + rc = aqt1000_read_of_property_u32(dev, + "qcom,cdc-micbias-cfilt1-mv", + &prop_val); + if (!rc) + mb->cfilt1_mv = prop_val; + + rc = aqt1000_read_of_property_u32(dev, + "qcom,cdc-micbias1-cfilt-sel", + &prop_val); + if (!rc) + mb->bias1_cfilt_sel = (u8)prop_val; + + } else if (of_find_property(dev->of_node, "qcom,cdc-micbias1-mv", + NULL)) { + rc = aqt1000_read_of_property_u32(dev, + "qcom,cdc-micbias1-mv", + &prop_val); + if (!rc) + mb->micb1_mv = prop_val; + } else { + dev_info(dev, "%s: Micbias1 DT property not found\n", + __func__); + } + + /* Print micbias info */ + dev_dbg(dev, "%s: ldoh_v %u cfilt1_mv %u micb1_mv %u \n", __func__, + (u32)mb->ldoh_v, (u32)mb->cfilt1_mv, (u32)mb->micb1_mv); +} + +static struct aqt1000_pdata *aqt1000_populate_dt_data(struct device *dev) +{ + struct aqt1000_pdata *pdata; + u32 prop_val; + + if (!dev || !dev->of_node) + return NULL; + + pdata = devm_kzalloc(dev, sizeof(struct aqt1000_pdata), + GFP_KERNEL); + if (!pdata) + return NULL; + + /* Parse power supplies */ + msm_cdc_get_power_supplies(dev, &pdata->regulator, + &pdata->num_supplies); + if (!pdata->regulator || (pdata->num_supplies <= 0)) { + dev_err(dev, "%s: no power supplies defined for codec\n", + __func__); + goto err_power_sup; + } + + /* Parse micbias info */ + aqt1000_dt_parse_micbias_info(dev, &pdata->micbias); + + pdata->aqt_rst_np = of_parse_phandle(dev->of_node, + "qcom,aqt-rst-gpio-node", 0); + if (!pdata->aqt_rst_np) { + dev_err(dev, "%s: Looking up %s property in node %s failed\n", + __func__, "qcom,aqt-rst-gpio-node", + dev->of_node->full_name); + goto err_parse_dt_prop; + } + + if (!(aqt1000_read_of_property_u32(dev, "qcom,cdc-ext-clk-rate", + &prop_val))) + pdata->ext_clk_rate = prop_val; + if (pdata->ext_clk_rate != AQT1000_CLK_24P576MHZ && + pdata->ext_clk_rate != AQT1000_CLK_19P2MHZ && + pdata->ext_clk_rate != AQT1000_CLK_12P288MHZ) { + /* Use the default ext_clk_rate if the DT value is wrong */ + pdata->ext_clk_rate = AQT1000_CLK_9P6MHZ; + } + + prop_val = 0; + if (!(aqt1000_read_of_property_u32(dev, "qcom,cdc-mclk-clk-rate", + &prop_val))) + pdata->mclk_rate = prop_val; + + if (pdata->mclk_rate != AQT1000_CLK_9P6MHZ && + pdata->mclk_rate != AQT1000_CLK_12P288MHZ) { + dev_err(dev, "%s: Invalid mclk_rate = %u\n", __func__, + pdata->mclk_rate); + goto err_parse_dt_prop; + } + if (pdata->ext_clk_rate % pdata->mclk_rate) { + dev_err(dev, + "%s: Invalid clock group, ext_clk = %d mclk = %d\n", + __func__, pdata->ext_clk_rate, pdata->mclk_rate); + goto err_parse_dt_prop; + } + + pdata->irq_gpio = of_get_named_gpio(dev->of_node, + "qcom,gpio-connect", 0); + if (!gpio_is_valid(pdata->irq_gpio)) { + dev_err(dev, "%s: TLMM connect gpio not found\n", __func__); + goto err_parse_dt_prop; + } + + return pdata; + +err_parse_dt_prop: + devm_kfree(dev, pdata->regulator); + pdata->regulator = NULL; + pdata->num_supplies = 0; +err_power_sup: + devm_kfree(dev, pdata); + return NULL; +} + +static int aqt1000_bringdown(struct device *dev) +{ + /* No sequence for teardown */ + + return 0; +} + +static void aqt1000_device_exit(struct aqt1000 *aqt) +{ + aqt_irq_exit(aqt); + aqt1000_bringdown(aqt->dev); + mutex_destroy(&aqt->io_lock); + mutex_destroy(&aqt->xfer_lock); + mutex_destroy(&aqt->cdc_bg_clk_lock); + mutex_destroy(&aqt->master_bias_lock); +} + +static int aqt1000_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct aqt1000 *aqt1000 = NULL; + struct aqt1000_pdata *pdata = NULL; + int ret = 0; + + pdata = aqt1000_populate_dt_data(&client->dev); + if (!pdata) { + dev_err(&client->dev, + "%s: Fail to obtain pdata from device tree\n", + __func__); + ret = -EINVAL; + goto fail; + } + client->dev.platform_data = pdata; + + aqt1000 = devm_kzalloc(&client->dev, sizeof(struct aqt1000), + GFP_KERNEL); + if (!aqt1000) { + ret = -ENOMEM; + goto fail; + } + + aqt1000->regmap = aqt1000_regmap_init(&client->dev, + &aqt1000_regmap_config); + if (IS_ERR(aqt1000->regmap)) { + ret = PTR_ERR(aqt1000->regmap); + dev_err(&client->dev, + "%s: Failed to init register map: %d\n", + __func__, ret); + goto fail; + } + aqt1000->aqt_rst_np = pdata->aqt_rst_np; + if (!aqt1000->aqt_rst_np) { + dev_err(&client->dev, "%s: pinctrl not used for rst_n\n", + __func__); + ret = -EINVAL; + goto fail; + } + + if (i2c_check_functionality(client->adapter, + I2C_FUNC_I2C) == 0) { + dev_dbg(&client->dev, "%s: can't talk I2C?\n", __func__); + ret = -EIO; + goto fail; + } + dev_set_drvdata(&client->dev, aqt1000); + aqt1000->dev = &client->dev; + aqt1000->dev_up = true; + aqt1000->mclk_rate = pdata->mclk_rate; + aqt1000->irq = client->irq; + + aqt1000->num_of_supplies = pdata->num_supplies; + ret = msm_cdc_init_supplies(aqt1000->dev, &aqt1000->supplies, + pdata->regulator, + pdata->num_supplies); + if (!aqt1000->supplies) { + dev_err(aqt1000->dev, "%s: Cannot init aqt supplies\n", + __func__); + goto err_codec; + } + ret = msm_cdc_enable_static_supplies(aqt1000->dev, + aqt1000->supplies, + pdata->regulator, + pdata->num_supplies); + if (ret) { + dev_err(aqt1000->dev, "%s: aqt static supply enable failed!\n", + __func__); + goto err_codec; + } + /* 5 usec sleep is needed as per HW requirement */ + usleep_range(5, 10); + + ret = aqt1000_reset(aqt1000->dev); + if (ret) { + dev_err(aqt1000->dev, "%s: Codec reset failed\n", __func__); + goto err_supplies; + } + + aqt1000->i2c_dev.client = client; + aqt1000->read_dev = aqt1000_i2c_read; + aqt1000->write_dev = aqt1000_i2c_write; + + ret = aqt1000_device_init(aqt1000); + if (ret) { + pr_err("%s: error, initializing device failed (%d)\n", + __func__, ret); + goto err_supplies; + } + + pm_runtime_set_active(aqt1000->dev); + pm_runtime_enable(aqt1000->dev); + + ret = aqt_register_codec(&client->dev); + if (ret) { + dev_err(aqt1000->dev, "%s: Codec registration failed\n", + __func__); + goto err_cdc_register; + } + + return ret; + +err_cdc_register: + pm_runtime_disable(aqt1000->dev); + aqt1000_device_exit(aqt1000); +err_supplies: + msm_cdc_release_supplies(aqt1000->dev, aqt1000->supplies, + pdata->regulator, + pdata->num_supplies); + pdata->regulator = NULL; + pdata->num_supplies = 0; +err_codec: + devm_kfree(&client->dev, aqt1000); + dev_set_drvdata(&client->dev, NULL); +fail: + return ret; +} + +static int aqt1000_i2c_remove(struct i2c_client *client) +{ + struct aqt1000 *aqt; + struct aqt1000_pdata *pdata = client->dev.platform_data; + + aqt = dev_get_drvdata(&client->dev); + + pm_runtime_disable(aqt->dev); + msm_cdc_release_supplies(aqt->dev, aqt->supplies, + pdata->regulator, + pdata->num_supplies); + aqt1000_device_exit(aqt); + dev_set_drvdata(&client->dev, NULL); + return 0; +} + +#ifdef CONFIG_PM +static int aqt1000_runtime_resume(struct device *dev) +{ + dev_dbg(dev, "%s system resume\n", __func__); + + return 0; +} + +static int aqt1000_runtime_suspend(struct device *dev) +{ + dev_dbg(dev, "%s system suspend\n", __func__); + + return 0; +} +#endif + +#ifdef CONFIG_PM_SLEEP +static int aqt1000_i2c_resume(struct device *dev) +{ + pr_debug("%s system resume\n", __func__); + return 0; +} + +static int aqt1000_i2c_suspend(struct device *dev) +{ + pr_debug("%s system suspend\n", __func__); + return 0; +} +#endif + +static struct i2c_device_id aqt1000_id_table[] = { + {"aqt1000-i2c", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, aqt1000_id_table); + +static const struct dev_pm_ops aqt1000_i2c_pm_ops = { + SET_RUNTIME_PM_OPS(aqt1000_runtime_suspend, + aqt1000_runtime_resume, NULL) + SET_SYSTEM_SLEEP_PM_OPS(aqt1000_i2c_suspend, + aqt1000_i2c_resume) +}; + +static const struct of_device_id aqt_match_table[] = { + {.compatible = "qcom,aqt1000-i2c-codec"}, + {} +}; +MODULE_DEVICE_TABLE(of, aqt_match_table); + +static struct i2c_driver aqt1000_i2c_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "aqt1000-i2c-codec", +#ifdef CONFIG_PM_SLEEP + .pm = &aqt1000_i2c_pm_ops, +#endif + .of_match_table = aqt_match_table, + }, + .id_table = aqt1000_id_table, + .probe = aqt1000_i2c_probe, + .remove = aqt1000_i2c_remove, +}; + +static int __init aqt1000_init(void) +{ + return i2c_add_driver(&aqt1000_i2c_driver); +} +module_init(aqt1000_init); + +static void __exit aqt1000_exit(void) +{ + i2c_del_driver(&aqt1000_i2c_driver); +} +module_exit(aqt1000_exit); + +MODULE_DESCRIPTION("AQT1000 Codec driver"); +MODULE_LICENSE("GPL v2"); diff --git a/audio-kernel/asoc/codecs/aqt1000/aqt1000-internal.h b/audio-kernel/asoc/codecs/aqt1000/aqt1000-internal.h new file mode 100644 index 000000000..19b4b57b1 --- /dev/null +++ b/audio-kernel/asoc/codecs/aqt1000/aqt1000-internal.h @@ -0,0 +1,170 @@ +/* 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 _AQT1000_INTERNAL_H +#define _AQT1000_INTERNAL_H + +#include +#include +#include + +#define AQT1000_RATES_MASK (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\ + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |\ + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000 |\ + SNDRV_PCM_RATE_384000) +/* Fractional Rates */ +#define AQT1000_FRAC_RATES_MASK (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_88200 |\ + SNDRV_PCM_RATE_176400) + +#define AQT1000_FORMATS_S16_S24_LE (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE) + +#define AQT1000_FORMATS_S16_S24_S32_LE (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) + +#define AQT1000_FORMATS_S16_LE (SNDRV_PCM_FMTBIT_S16_LE) + +/* Macros for packing register writes into a U32 */ +#define AQT1000_PACKED_REG_SIZE sizeof(u32) +#define AQT1000_CODEC_UNPACK_ENTRY(packed, reg, mask, val) \ + do { \ + ((reg) = ((packed >> 16) & (0xffff))); \ + ((mask) = ((packed >> 8) & (0xff))); \ + ((val) = ((packed) & (0xff))); \ + } while (0) + +#define STRING(name) #name +#define AQT_DAPM_ENUM(name, reg, offset, text) \ +static SOC_ENUM_SINGLE_DECL(name##_enum, reg, offset, text); \ +static const struct snd_kcontrol_new name##_mux = \ + SOC_DAPM_ENUM(STRING(name), name##_enum) + +#define AQT_DAPM_ENUM_EXT(name, reg, offset, text, getname, putname) \ +static SOC_ENUM_SINGLE_DECL(name##_enum, reg, offset, text); \ +static const struct snd_kcontrol_new name##_mux = \ + SOC_DAPM_ENUM_EXT(STRING(name), name##_enum, getname, putname) + +#define AQT_DAPM_MUX(name, shift, kctl) \ + SND_SOC_DAPM_MUX(name, SND_SOC_NOPM, shift, 0, &kctl##_mux) + + +#define AQT1000_INTERP_MUX_NUM_INPUTS 3 +#define AQT1000_RX_PATH_CTL_OFFSET 20 + +#define BYTE_BIT_MASK(nr) (1 << ((nr) % BITS_PER_BYTE)) + +#define AQT1000_REG_BITS 8 +#define AQT1000_MAX_VALID_ADC_MUX 3 + +#define AQT1000_AMIC_PWR_LEVEL_LP 0 +#define AQT1000_AMIC_PWR_LEVEL_DEFAULT 1 +#define AQT1000_AMIC_PWR_LEVEL_HP 2 +#define AQT1000_AMIC_PWR_LVL_MASK 0x60 +#define AQT1000_AMIC_PWR_LVL_SHIFT 0x5 + +#define AQT1000_DEC_PWR_LVL_MASK 0x06 +#define AQT1000_DEC_PWR_LVL_DF 0x00 +#define AQT1000_DEC_PWR_LVL_LP 0x02 +#define AQT1000_DEC_PWR_LVL_HP 0x04 +#define AQT1000_STRING_LEN 100 + +#define AQT1000_CDC_SIDETONE_IIR_COEFF_MAX 5 + +#define AQT1000_MAX_MICBIAS 1 +#define DAPM_MICBIAS1_STANDALONE "MIC BIAS1 Standalone" + +#define TX_HPF_CUT_OFF_FREQ_MASK 0x60 +#define CF_MIN_3DB_4HZ 0x0 +#define CF_MIN_3DB_75HZ 0x1 +#define CF_MIN_3DB_150HZ 0x2 + +enum { + AUDIO_NOMINAL, + HPH_PA_DELAY, + CLSH_Z_CONFIG, + ANC_MIC_AMIC1, + ANC_MIC_AMIC2, + ANC_MIC_AMIC3, +}; + +enum { + INTn_1_INP_SEL_ZERO = 0, + INTn_1_INP_SEL_DEC0, + INTn_1_INP_SEL_DEC1, + INTn_1_INP_SEL_IIR0, + INTn_1_INP_SEL_IIR1, + INTn_1_INP_SEL_RX0, + INTn_1_INP_SEL_RX1, +}; + +enum { + INTn_2_INP_SEL_ZERO = 0, + INTn_2_INP_SEL_RX0, + INTn_2_INP_SEL_RX1, + INTn_2_INP_SEL_PROXIMITY, +}; + +/* Codec supports 2 IIR filters */ +enum { + IIR0 = 0, + IIR1, + IIR_MAX, +}; + +enum { + ASRC_IN_HPHL, + ASRC_IN_HPHR, + ASRC_INVALID, +}; + +enum { + CONV_88P2K_TO_384K, + CONV_96K_TO_352P8K, + CONV_352P8K_TO_384K, + CONV_384K_TO_352P8K, + CONV_384K_TO_384K, + CONV_96K_TO_384K, +}; + +enum aqt_notify_event { + AQT_EVENT_INVALID, + /* events for micbias ON and OFF */ + AQT_EVENT_PRE_MICBIAS_1_OFF, + AQT_EVENT_POST_MICBIAS_1_OFF, + AQT_EVENT_PRE_MICBIAS_1_ON, + AQT_EVENT_POST_MICBIAS_1_ON, + AQT_EVENT_PRE_DAPM_MICBIAS_1_OFF, + AQT_EVENT_POST_DAPM_MICBIAS_1_OFF, + AQT_EVENT_PRE_DAPM_MICBIAS_1_ON, + AQT_EVENT_POST_DAPM_MICBIAS_1_ON, + /* events for PA ON and OFF */ + AQT_EVENT_PRE_HPHL_PA_ON, + AQT_EVENT_POST_HPHL_PA_OFF, + AQT_EVENT_PRE_HPHR_PA_ON, + AQT_EVENT_POST_HPHR_PA_OFF, + AQT_EVENT_PRE_HPHL_PA_OFF, + AQT_EVENT_PRE_HPHR_PA_OFF, + AQT_EVENT_OCP_OFF, + AQT_EVENT_OCP_ON, + AQT_EVENT_LAST, +}; + +struct interp_sample_rate { + int sample_rate; + int rate_val; +}; + +extern struct regmap_config aqt1000_regmap_config; +extern int aqt_register_codec(struct device *dev); + +#endif /* _AQT1000_INTERNAL_H */ diff --git a/audio-kernel/asoc/codecs/aqt1000/aqt1000-irq.c b/audio-kernel/asoc/codecs/aqt1000/aqt1000-irq.c new file mode 100644 index 000000000..321b490be --- /dev/null +++ b/audio-kernel/asoc/codecs/aqt1000/aqt1000-irq.c @@ -0,0 +1,289 @@ +/* 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pdata.h" +#include "aqt1000.h" + +#include "aqt1000-registers.h" +#include "aqt1000-irq.h" + +static const struct regmap_irq aqt1000_irqs[AQT1000_NUM_IRQS] = { + REGMAP_IRQ_REG(AQT1000_IRQ_MBHC_BUTTON_RELEASE_DET, 0, 0x01), + REGMAP_IRQ_REG(AQT1000_IRQ_MBHC_BUTTON_PRESS_DET, 0, 0x02), + REGMAP_IRQ_REG(AQT1000_IRQ_MBHC_ELECT_INS_REM_DET, 0, 0x04), + REGMAP_IRQ_REG(AQT1000_IRQ_MBHC_ELECT_INS_REM_LEG_DET, 0, 0x08), + REGMAP_IRQ_REG(AQT1000_IRQ_MBHC_SW_DET, 0, 0x10), + REGMAP_IRQ_REG(AQT1000_IRQ_HPH_PA_OCPL_FAULT, 0, 0x20), + REGMAP_IRQ_REG(AQT1000_IRQ_HPH_PA_OCPR_FAULT, 0, 0x40), + REGMAP_IRQ_REG(AQT1000_IRQ_HPH_PA_CNPL_COMPLETE, 0, 0x80), + REGMAP_IRQ_REG(AQT1000_IRQ_HPH_PA_CNPR_COMPLETE, 1, 0x01), + REGMAP_IRQ_REG(AQT1000_CDC_HPHL_SURGE, 1, 0x02), + REGMAP_IRQ_REG(AQT1000_CDC_HPHR_SURGE, 1, 0x04), +}; + +static const struct regmap_irq_chip aqt_regmap_irq_chip = { + .name = "AQT1000", + .irqs = aqt1000_irqs, + .num_irqs = ARRAY_SIZE(aqt1000_irqs), + .num_regs = 2, + .status_base = AQT1000_INTR_CTRL_INT_STATUS_2, + .mask_base = AQT1000_INTR_CTRL_INT_MASK_2, + .unmask_base = AQT1000_INTR_CTRL_INT_CLEAR_2, + .ack_base = AQT1000_INTR_CTRL_INT_STATUS_2, + .runtime_pm = true, +}; + +static int aqt_map_irq(struct aqt1000 *aqt, int irq) +{ + return regmap_irq_get_virq(aqt->irq_chip, irq); +} + +/** + * aqt_request_irq: Request a thread handler for the given IRQ + * @aqt: pointer to aqt1000 structure + * @irq: irq number + * @name: name for the IRQ thread + * @handler: irq handler + * @data: data pointer + * + * Returns 0 on success or error on failure + */ +int aqt_request_irq(struct aqt1000 *aqt, int irq, const char *name, + irq_handler_t handler, void *data) +{ + irq = aqt_map_irq(aqt, irq); + if (irq < 0) + return irq; + + return request_threaded_irq(irq, NULL, handler, + IRQF_ONESHOT | IRQF_TRIGGER_RISING, + name, data); +} +EXPORT_SYMBOL(aqt_request_irq); + +/** + * aqt_free_irq: Free the IRQ resources allocated during request_irq + * @aqt: pointer to aqt1000 structure + * @irq: irq number + * @data: data pointer + */ +void aqt_free_irq(struct aqt1000 *aqt, int irq, void *data) +{ + irq = aqt_map_irq(aqt, irq); + if (irq < 0) + return; + + free_irq(irq, data); +} +EXPORT_SYMBOL(aqt_free_irq); + +/** + * aqt_enable_irq: Enable the given IRQ + * @aqt: pointer to aqt1000 structure + * @irq: irq number + */ +void aqt_enable_irq(struct aqt1000 *aqt, int irq) +{ + if (aqt) + enable_irq(aqt_map_irq(aqt, irq)); +} +EXPORT_SYMBOL(aqt_enable_irq); + +/** + * aqt_disable_irq: Disable the given IRQ + * @aqt: pointer to aqt1000 structure + * @irq: irq number + */ +void aqt_disable_irq(struct aqt1000 *aqt, int irq) +{ + if (aqt) + disable_irq(aqt_map_irq(aqt, irq)); +} +EXPORT_SYMBOL(aqt_disable_irq); + +static irqreturn_t aqt_irq_thread(int irq, void *data) +{ + int ret = 0; + u8 sts[2]; + struct aqt1000 *aqt = data; + int num_irq_regs = aqt->num_irq_regs; + struct aqt1000_pdata *pdata; + + pdata = dev_get_platdata(aqt->dev); + + memset(sts, 0, sizeof(sts)); + ret = regmap_bulk_read(aqt->regmap, AQT1000_INTR_CTRL_INT_STATUS_2, + sts, num_irq_regs); + if (ret < 0) { + dev_err(aqt->dev, "%s: Failed to read intr status: %d\n", + __func__, ret); + } else if (ret == 0) { + while (gpio_get_value_cansleep(pdata->irq_gpio)) + handle_nested_irq(irq_find_mapping(aqt->virq, 0)); + } + + return IRQ_HANDLED; +} + +static void aqt_irq_disable(struct irq_data *data) +{ +} + +static void aqt_irq_enable(struct irq_data *data) +{ +} + +static struct irq_chip aqt_irq_chip = { + .name = "AQT", + .irq_disable = aqt_irq_disable, + .irq_enable = aqt_irq_enable, +}; + +static struct lock_class_key aqt_irq_lock_class; + +static int aqt_irq_map(struct irq_domain *irqd, unsigned int virq, + irq_hw_number_t hw) +{ + struct aqt1000 *data = irqd->host_data; + + irq_set_chip_data(virq, data); + irq_set_chip_and_handler(virq, &aqt_irq_chip, handle_simple_irq); + irq_set_lockdep_class(virq, &aqt_irq_lock_class); + irq_set_nested_thread(virq, 1); + irq_set_noprobe(virq); + + return 0; +} + +static const struct irq_domain_ops aqt_domain_ops = { + .map = aqt_irq_map, + .xlate = irq_domain_xlate_twocell, +}; + +/** + * aqt_irq_init: Initializes IRQ module + * @aqt: pointer to aqt1000 structure + * + * Returns 0 on success or error on failure + */ +int aqt_irq_init(struct aqt1000 *aqt) +{ + int i, ret; + unsigned int flags = IRQF_ONESHOT; + struct irq_data *irq_data; + struct aqt1000_pdata *pdata; + + if (!aqt) { + pr_err("%s: Null pointer handle\n", __func__); + return -EINVAL; + } + + pdata = dev_get_platdata(aqt->dev); + if (!pdata) { + dev_err(aqt->dev, "%s: Invalid platform data\n", __func__); + return -EINVAL; + } + + /* Select default if not defined in DT */ + flags = IRQF_TRIGGER_HIGH | IRQF_ONESHOT; + if (pdata->irq_flags) + flags = pdata->irq_flags; + + if (pdata->irq_gpio) { + aqt->irq = gpio_to_irq(pdata->irq_gpio); + ret = devm_gpio_request_one(aqt->dev, pdata->irq_gpio, + GPIOF_IN, "AQT IRQ"); + if (ret) { + dev_err(aqt->dev, "%s: Failed to request gpio %d\n", + __func__, ret); + pdata->irq_gpio = 0; + return ret; + } + } + + irq_data = irq_get_irq_data(aqt->irq); + if (!irq_data) { + dev_err(aqt->dev, "%s: Invalid IRQ: %d\n", + __func__, aqt->irq); + return -EINVAL; + } + + aqt->num_irq_regs = aqt_regmap_irq_chip.num_regs; + for (i = 0; i < aqt->num_irq_regs; i++) { + regmap_write(aqt->regmap, + (AQT1000_INTR_CTRL_INT_TYPE_2 + i), 0); + } + + aqt->virq = irq_domain_add_linear(NULL, 1, &aqt_domain_ops, aqt); + if (!aqt->virq) { + dev_err(aqt->dev, "%s: Failed to add IRQ domain\n", __func__); + ret = -EINVAL; + goto err; + } + ret = regmap_add_irq_chip(aqt->regmap, + irq_create_mapping(aqt->virq, 0), + IRQF_ONESHOT, 0, &aqt_regmap_irq_chip, + &aqt->irq_chip); + if (ret) { + dev_err(aqt->dev, "%s: Failed to add IRQs: %d\n", + __func__, ret); + goto err; + } + + ret = request_threaded_irq(aqt->irq, NULL, aqt_irq_thread, flags, + "aqt", aqt); + if (ret) { + dev_err(aqt->dev, "%s: failed to register irq: %d\n", + __func__, ret); + goto err_irq; + } + + return 0; + +err_irq: + regmap_del_irq_chip(irq_create_mapping(aqt->virq, 1), aqt->irq_chip); +err: + return ret; +} +EXPORT_SYMBOL(aqt_irq_init); + +/** + * aqt_irq_exit: Uninitialize regmap IRQ and free IRQ resources + * @aqt: pointer to aqt1000 structure + * + * Returns 0 on success or error on failure + */ +int aqt_irq_exit(struct aqt1000 *aqt) +{ + if (!aqt) { + pr_err("%s: Null pointer handle\n", __func__); + return -EINVAL; + } + regmap_del_irq_chip(irq_create_mapping(aqt->virq, 1), aqt->irq_chip); + free_irq(aqt->irq, aqt); + + return 0; +} +EXPORT_SYMBOL(aqt_irq_exit); diff --git a/audio-kernel/asoc/codecs/aqt1000/aqt1000-irq.h b/audio-kernel/asoc/codecs/aqt1000/aqt1000-irq.h new file mode 100644 index 000000000..60be49c21 --- /dev/null +++ b/audio-kernel/asoc/codecs/aqt1000/aqt1000-irq.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 __AQT1000_IRQ_H_ +#define __AQT1000_IRQ_H_ + +#include +#include +#include + +enum { + /* INTR_CTRL_INT_MASK_2 */ + AQT1000_IRQ_MBHC_BUTTON_RELEASE_DET = 0, + AQT1000_IRQ_MBHC_BUTTON_PRESS_DET, + AQT1000_IRQ_MBHC_ELECT_INS_REM_DET, + AQT1000_IRQ_MBHC_ELECT_INS_REM_LEG_DET, + AQT1000_IRQ_MBHC_SW_DET, + AQT1000_IRQ_HPH_PA_OCPL_FAULT, + AQT1000_IRQ_HPH_PA_OCPR_FAULT, + AQT1000_IRQ_HPH_PA_CNPL_COMPLETE, + + /* INTR_CTRL_INT_MASK_3 */ + AQT1000_IRQ_HPH_PA_CNPR_COMPLETE, + AQT1000_CDC_HPHL_SURGE, + AQT1000_CDC_HPHR_SURGE, + AQT1000_NUM_IRQS, +}; + +int aqt_request_irq(struct aqt1000 *aqt, int irq, const char *name, + irq_handler_t handler, void *data); +void aqt_free_irq(struct aqt1000 *aqt, int irq, void *data); +int aqt_irq_init(struct aqt1000 *aqt); +int aqt_irq_exit(struct aqt1000 *aqt); +void aqt_enable_irq(struct aqt1000 *aqt, int irq); +void aqt_disable_irq(struct aqt1000 *aqt, int irq); + +#endif /* __AQT1000_IRQ_H_ */ diff --git a/audio-kernel/asoc/codecs/aqt1000/aqt1000-mbhc.c b/audio-kernel/asoc/codecs/aqt1000/aqt1000-mbhc.c new file mode 100644 index 000000000..24f14a46e --- /dev/null +++ b/audio-kernel/asoc/codecs/aqt1000/aqt1000-mbhc.c @@ -0,0 +1,1058 @@ +/* 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 + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "aqt1000.h" +#include "aqt1000-api.h" +#include "aqt1000-mbhc.h" +#include "aqt1000-registers.h" +#include "aqt1000-irq.h" +#include "pdata.h" +#include "../wcdcal-hwdep.h" +#include "../wcd-mbhc-v2-api.h" + +#define AQT_ZDET_SUPPORTED true +/* Z value defined in milliohm */ +#define AQT_ZDET_VAL_32 32000 +#define AQT_ZDET_VAL_400 400000 +#define AQT_ZDET_VAL_1200 1200000 +#define AQT_ZDET_VAL_100K 100000000 +/* Z floating defined in ohms */ +#define AQT_ZDET_FLOATING_IMPEDANCE 0x0FFFFFFE + +#define AQT_ZDET_NUM_MEASUREMENTS 900 +#define AQT_MBHC_GET_C1(c) ((c & 0xC000) >> 14) +#define AQT_MBHC_GET_X1(x) (x & 0x3FFF) +/* Z value compared in milliOhm */ +#define AQT_MBHC_IS_SECOND_RAMP_REQUIRED(z) ((z > 400000) || (z < 32000)) +#define AQT_MBHC_ZDET_CONST (86 * 16384) +#define AQT_MBHC_MOISTURE_RREF R_24_KOHM + +static struct wcd_mbhc_register + wcd_mbhc_registers[WCD_MBHC_REG_FUNC_MAX] = { + WCD_MBHC_REGISTER("WCD_MBHC_L_DET_EN", + AQT1000_ANA_MBHC_MECH, 0x80, 7, 0), + WCD_MBHC_REGISTER("WCD_MBHC_GND_DET_EN", + AQT1000_ANA_MBHC_MECH, 0x40, 6, 0), + WCD_MBHC_REGISTER("WCD_MBHC_MECH_DETECTION_TYPE", + AQT1000_ANA_MBHC_MECH, 0x20, 5, 0), + WCD_MBHC_REGISTER("WCD_MBHC_MIC_CLAMP_CTL", + AQT1000_MBHC_NEW_PLUG_DETECT_CTL, 0x30, 4, 0), + WCD_MBHC_REGISTER("WCD_MBHC_ELECT_DETECTION_TYPE", + AQT1000_ANA_MBHC_ELECT, 0x08, 3, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HS_L_DET_PULL_UP_CTRL", + AQT1000_MBHC_NEW_INT_MECH_DET_CURRENT, 0x1F, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HS_L_DET_PULL_UP_COMP_CTRL", + AQT1000_ANA_MBHC_MECH, 0x04, 2, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPHL_PLUG_TYPE", + AQT1000_ANA_MBHC_MECH, 0x10, 4, 0), + WCD_MBHC_REGISTER("WCD_MBHC_GND_PLUG_TYPE", + AQT1000_ANA_MBHC_MECH, 0x08, 3, 0), + WCD_MBHC_REGISTER("WCD_MBHC_SW_HPH_LP_100K_TO_GND", + AQT1000_ANA_MBHC_MECH, 0x01, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_ELECT_SCHMT_ISRC", + AQT1000_ANA_MBHC_ELECT, 0x06, 1, 0), + WCD_MBHC_REGISTER("WCD_MBHC_FSM_EN", + AQT1000_ANA_MBHC_ELECT, 0x80, 7, 0), + WCD_MBHC_REGISTER("WCD_MBHC_INSREM_DBNC", + AQT1000_MBHC_NEW_PLUG_DETECT_CTL, 0x0F, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_BTN_DBNC", + AQT1000_MBHC_NEW_CTL_1, 0x03, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HS_VREF", + AQT1000_MBHC_NEW_CTL_2, 0x03, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HS_COMP_RESULT", + AQT1000_ANA_MBHC_RESULT_3, 0x08, 3, 0), + WCD_MBHC_REGISTER("WCD_MBHC_MIC_SCHMT_RESULT", + AQT1000_ANA_MBHC_RESULT_3, 0x20, 5, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPHL_SCHMT_RESULT", + AQT1000_ANA_MBHC_RESULT_3, 0x80, 7, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPHR_SCHMT_RESULT", + AQT1000_ANA_MBHC_RESULT_3, 0x40, 6, 0), + WCD_MBHC_REGISTER("WCD_MBHC_OCP_FSM_EN", + AQT1000_HPH_OCP_CTL, 0x10, 4, 0), + WCD_MBHC_REGISTER("WCD_MBHC_BTN_RESULT", + AQT1000_ANA_MBHC_RESULT_3, 0x07, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_BTN_ISRC_CTL", + AQT1000_ANA_MBHC_ELECT, 0x70, 4, 0), + WCD_MBHC_REGISTER("WCD_MBHC_ELECT_RESULT", + AQT1000_ANA_MBHC_RESULT_3, 0xFF, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_MICB_CTRL", + AQT1000_ANA_MICB1, 0xC0, 6, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPH_CNP_WG_TIME", + AQT1000_HPH_CNP_WG_TIME, 0xFF, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPHR_PA_EN", + AQT1000_ANA_HPH, 0x40, 6, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPHL_PA_EN", + AQT1000_ANA_HPH, 0x80, 7, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPH_PA_EN", + AQT1000_ANA_HPH, 0xC0, 6, 0), + WCD_MBHC_REGISTER("WCD_MBHC_SWCH_LEVEL_REMOVE", + AQT1000_ANA_MBHC_RESULT_3, 0x10, 4, 0), + WCD_MBHC_REGISTER("WCD_MBHC_PULLDOWN_CTRL", + 0, 0, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_ANC_DET_EN", + AQT1000_MBHC_CTL_BCS, 0x02, 1, 0), + WCD_MBHC_REGISTER("WCD_MBHC_FSM_STATUS", + AQT1000_MBHC_NEW_FSM_STATUS, 0x01, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_MUX_CTL", + AQT1000_MBHC_NEW_CTL_2, 0x70, 4, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPHL_OCP_DET_EN", + AQT1000_HPH_L_TEST, 0x01, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPHR_OCP_DET_EN", + AQT1000_HPH_R_TEST, 0x01, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPHL_OCP_STATUS", + AQT1000_INTR_CTRL_INT_STATUS_2, 0x20, 5, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPHR_OCP_STATUS", + AQT1000_INTR_CTRL_INT_STATUS_2, 0x40, 6, 0), + WCD_MBHC_REGISTER("WCD_MBHC_ADC_EN", + AQT1000_MBHC_NEW_CTL_1, 0x08, 3, 0), + WCD_MBHC_REGISTER("WCD_MBHC_ADC_COMPLETE", AQT1000_MBHC_NEW_FSM_STATUS, + 0x40, 6, 0), + WCD_MBHC_REGISTER("WCD_MBHC_ADC_TIMEOUT", AQT1000_MBHC_NEW_FSM_STATUS, + 0x80, 7, 0), + WCD_MBHC_REGISTER("WCD_MBHC_ADC_RESULT", AQT1000_MBHC_NEW_ADC_RESULT, + 0xFF, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_MICB2_VOUT", AQT1000_ANA_MICB1, 0x3F, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_ADC_MODE", + AQT1000_MBHC_NEW_CTL_1, 0x10, 4, 0), + WCD_MBHC_REGISTER("WCD_MBHC_DETECTION_DONE", + AQT1000_MBHC_NEW_CTL_1, 0x04, 2, 0), + WCD_MBHC_REGISTER("WCD_MBHC_ELECT_ISRC_EN", + AQT1000_ANA_MBHC_ZDET, 0x02, 1, 0), +}; + +static const struct wcd_mbhc_intr intr_ids = { + .mbhc_sw_intr = AQT1000_IRQ_MBHC_SW_DET, + .mbhc_btn_press_intr = AQT1000_IRQ_MBHC_BUTTON_PRESS_DET, + .mbhc_btn_release_intr = AQT1000_IRQ_MBHC_BUTTON_RELEASE_DET, + .mbhc_hs_ins_intr = AQT1000_IRQ_MBHC_ELECT_INS_REM_LEG_DET, + .mbhc_hs_rem_intr = AQT1000_IRQ_MBHC_ELECT_INS_REM_DET, + .hph_left_ocp = AQT1000_IRQ_HPH_PA_OCPL_FAULT, + .hph_right_ocp = AQT1000_IRQ_HPH_PA_OCPR_FAULT, +}; + +struct aqt_mbhc_zdet_param { + u16 ldo_ctl; + u16 noff; + u16 nshift; + u16 btn5; + u16 btn6; + u16 btn7; +}; + +static int aqt_mbhc_request_irq(struct snd_soc_codec *codec, + int irq, irq_handler_t handler, + const char *name, void *data) +{ + struct aqt1000 *aqt = dev_get_drvdata(codec->dev); + + return aqt_request_irq(aqt, irq, name, handler, data); +} + +static void aqt_mbhc_irq_control(struct snd_soc_codec *codec, + int irq, bool enable) +{ + struct aqt1000 *aqt = dev_get_drvdata(codec->dev); + + if (enable) + aqt_enable_irq(aqt, irq); + else + aqt_disable_irq(aqt, irq); +} + +static int aqt_mbhc_free_irq(struct snd_soc_codec *codec, + int irq, void *data) +{ + struct aqt1000 *aqt = dev_get_drvdata(codec->dev); + + aqt_free_irq(aqt, irq, data); + + return 0; +} + +static void aqt_mbhc_clk_setup(struct snd_soc_codec *codec, + bool enable) +{ + if (enable) + snd_soc_update_bits(codec, AQT1000_MBHC_NEW_CTL_1, + 0x80, 0x80); + else + snd_soc_update_bits(codec, AQT1000_MBHC_NEW_CTL_1, + 0x80, 0x00); +} + +static int aqt_mbhc_btn_to_num(struct snd_soc_codec *codec) +{ + return snd_soc_read(codec, AQT1000_ANA_MBHC_RESULT_3) & 0x7; +} + +static void aqt_mbhc_mbhc_bias_control(struct snd_soc_codec *codec, + bool enable) +{ + if (enable) + snd_soc_update_bits(codec, AQT1000_ANA_MBHC_ELECT, + 0x01, 0x01); + else + snd_soc_update_bits(codec, AQT1000_ANA_MBHC_ELECT, + 0x01, 0x00); +} + +static void aqt_mbhc_program_btn_thr(struct snd_soc_codec *codec, + s16 *btn_low, s16 *btn_high, + int num_btn, bool is_micbias) +{ + int i; + int vth; + + if (num_btn > WCD_MBHC_DEF_BUTTONS) { + dev_err(codec->dev, "%s: invalid number of buttons: %d\n", + __func__, num_btn); + return; + } + + for (i = 0; i < num_btn; i++) { + vth = ((btn_high[i] * 2) / 25) & 0x3F; + snd_soc_update_bits(codec, AQT1000_ANA_MBHC_BTN0 + i, + 0xFC, vth << 2); + dev_dbg(codec->dev, "%s: btn_high[%d]: %d, vth: %d\n", + __func__, i, btn_high[i], vth); + } +} + +static bool aqt_mbhc_lock_sleep(struct wcd_mbhc *mbhc, bool lock) +{ + struct snd_soc_codec *codec = mbhc->codec; + struct aqt1000 *aqt = dev_get_drvdata(codec->dev); + bool ret = 0; + + dev_dbg(aqt->dev, "%s: lock: %d\n", __func__, lock); + + return ret; +} + +static int aqt_mbhc_register_notifier(struct wcd_mbhc *mbhc, + struct notifier_block *nblock, + bool enable) +{ + struct aqt1000_mbhc *aqt_mbhc; + + aqt_mbhc = container_of(mbhc, struct aqt1000_mbhc, wcd_mbhc); + + if (enable) + return blocking_notifier_chain_register(&aqt_mbhc->notifier, + nblock); + else + return blocking_notifier_chain_unregister( + &aqt_mbhc->notifier, nblock); +} + +static bool aqt_mbhc_micb_en_status(struct wcd_mbhc *mbhc, int micb_num) +{ + u8 val; + + if (micb_num == MIC_BIAS_1) { + val = ((snd_soc_read(mbhc->codec, AQT1000_ANA_MICB1) & 0xC0) + >> 6); + if (val == 0x01) + return true; + } + return false; +} + +static bool aqt_mbhc_hph_pa_on_status(struct snd_soc_codec *codec) +{ + return (snd_soc_read(codec, AQT1000_ANA_HPH) & 0xC0) ? true : false; +} + +static void aqt_mbhc_hph_l_pull_up_control(struct snd_soc_codec *codec, + int pull_up_cur) +{ + /* Default pull up current to 2uA */ + if (pull_up_cur > HS_PULLUP_I_OFF || pull_up_cur < HS_PULLUP_I_3P0_UA || + pull_up_cur == HS_PULLUP_I_DEFAULT) + pull_up_cur = HS_PULLUP_I_2P0_UA; + + dev_dbg(codec->dev, "%s: HS pull up current:%d\n", + __func__, pull_up_cur); + + snd_soc_update_bits(codec, AQT1000_MBHC_NEW_INT_MECH_DET_CURRENT, + 0x1F, pull_up_cur); +} + +static int aqt_mbhc_request_micbias(struct snd_soc_codec *codec, + int micb_num, int req) +{ + int ret = 0; + + /* + * If micbias is requested, make sure that there + * is vote to enable mclk + */ + if (req == MICB_ENABLE) + aqt_cdc_mclk_enable(codec, true); + + ret = aqt_micbias_control(codec, micb_num, req, false); + + /* + * Release vote for mclk while requesting for + * micbias disable + */ + if (req == MICB_DISABLE) + aqt_cdc_mclk_enable(codec, false); + + return ret; +} + +static void aqt_mbhc_micb_ramp_control(struct snd_soc_codec *codec, + bool enable) +{ + if (enable) { + snd_soc_update_bits(codec, AQT1000_ANA_MICB1_RAMP, + 0x1C, 0x0C); + snd_soc_update_bits(codec, AQT1000_ANA_MICB1_RAMP, + 0x80, 0x80); + } else { + snd_soc_update_bits(codec, AQT1000_ANA_MICB1_RAMP, + 0x80, 0x00); + snd_soc_update_bits(codec, AQT1000_ANA_MICB1_RAMP, + 0x1C, 0x00); + } +} + +static struct firmware_cal *aqt_get_hwdep_fw_cal(struct wcd_mbhc *mbhc, + enum wcd_cal_type type) +{ + struct aqt1000_mbhc *aqt_mbhc; + struct firmware_cal *hwdep_cal; + struct snd_soc_codec *codec = mbhc->codec; + + aqt_mbhc = container_of(mbhc, struct aqt1000_mbhc, wcd_mbhc); + + if (!codec) { + pr_err("%s: NULL codec pointer\n", __func__); + return NULL; + } + hwdep_cal = wcdcal_get_fw_cal(aqt_mbhc->fw_data, type); + if (!hwdep_cal) + dev_err(codec->dev, "%s: cal not sent by %d\n", + __func__, type); + + return hwdep_cal; +} + +static int aqt_mbhc_micb_ctrl_threshold_mic(struct snd_soc_codec *codec, + int micb_num, bool req_en) +{ + struct aqt1000_pdata *pdata = dev_get_platdata(codec->dev); + int rc, micb_mv; + + if (micb_num != MIC_BIAS_1) + return -EINVAL; + + /* + * If device tree micbias level is already above the minimum + * voltage needed to detect threshold microphone, then do + * not change the micbias, just return. + */ + if (pdata->micbias.micb1_mv >= WCD_MBHC_THR_HS_MICB_MV) + return 0; + + micb_mv = req_en ? WCD_MBHC_THR_HS_MICB_MV : pdata->micbias.micb1_mv; + + rc = aqt_mbhc_micb_adjust_voltage(codec, micb_mv, MIC_BIAS_1); + + return rc; +} + +static inline void aqt_mbhc_get_result_params(struct aqt1000 *aqt, + s16 *d1_a, u16 noff, + int32_t *zdet) +{ + int i; + int val, val1; + s16 c1; + s32 x1, d1; + int32_t denom; + int minCode_param[] = { + 3277, 1639, 820, 410, 205, 103, 52, 26 + }; + + regmap_update_bits(aqt->regmap, AQT1000_ANA_MBHC_ZDET, 0x20, 0x20); + for (i = 0; i < AQT_ZDET_NUM_MEASUREMENTS; i++) { + regmap_read(aqt->regmap, AQT1000_ANA_MBHC_RESULT_2, &val); + if (val & 0x80) + break; + } + val = val << 0x8; + regmap_read(aqt->regmap, AQT1000_ANA_MBHC_RESULT_1, &val1); + val |= val1; + regmap_update_bits(aqt->regmap, AQT1000_ANA_MBHC_ZDET, 0x20, 0x00); + x1 = AQT_MBHC_GET_X1(val); + c1 = AQT_MBHC_GET_C1(val); + /* If ramp is not complete, give additional 5ms */ + if ((c1 < 2) && x1) + usleep_range(5000, 5050); + + if (!c1 || !x1) { + dev_dbg(aqt->dev, + "%s: Impedance detect ramp error, c1=%d, x1=0x%x\n", + __func__, c1, x1); + goto ramp_down; + } + d1 = d1_a[c1]; + denom = (x1 * d1) - (1 << (14 - noff)); + if (denom > 0) + *zdet = (AQT_MBHC_ZDET_CONST * 1000) / denom; + else if (x1 < minCode_param[noff]) + *zdet = AQT_ZDET_FLOATING_IMPEDANCE; + + dev_dbg(aqt->dev, "%s: d1=%d, c1=%d, x1=0x%x, z_val=%d(milliOhm)\n", + __func__, d1, c1, x1, *zdet); +ramp_down: + i = 0; + while (x1) { + regmap_bulk_read(aqt->regmap, + AQT1000_ANA_MBHC_RESULT_1, (u8 *)&val, 2); + x1 = AQT_MBHC_GET_X1(val); + i++; + if (i == AQT_ZDET_NUM_MEASUREMENTS) + break; + } +} + +static void aqt_mbhc_zdet_ramp(struct snd_soc_codec *codec, + struct aqt_mbhc_zdet_param *zdet_param, + int32_t *zl, int32_t *zr, s16 *d1_a) +{ + struct aqt1000 *aqt = dev_get_drvdata(codec->dev); + int32_t zdet = 0; + + snd_soc_update_bits(codec, AQT1000_MBHC_NEW_ZDET_ANA_CTL, 0x70, + zdet_param->ldo_ctl << 4); + snd_soc_update_bits(codec, AQT1000_ANA_MBHC_BTN5, 0xFC, + zdet_param->btn5); + snd_soc_update_bits(codec, AQT1000_ANA_MBHC_BTN6, 0xFC, + zdet_param->btn6); + snd_soc_update_bits(codec, AQT1000_ANA_MBHC_BTN7, 0xFC, + zdet_param->btn7); + snd_soc_update_bits(codec, AQT1000_MBHC_NEW_ZDET_ANA_CTL, 0x0F, + zdet_param->noff); + snd_soc_update_bits(codec, AQT1000_MBHC_NEW_ZDET_RAMP_CTL, 0x0F, + zdet_param->nshift); + + if (!zl) + goto z_right; + /* Start impedance measurement for HPH_L */ + regmap_update_bits(aqt->regmap, + AQT1000_ANA_MBHC_ZDET, 0x80, 0x80); + dev_dbg(aqt->dev, "%s: ramp for HPH_L, noff = %d\n", + __func__, zdet_param->noff); + aqt_mbhc_get_result_params(aqt, d1_a, zdet_param->noff, &zdet); + regmap_update_bits(aqt->regmap, + AQT1000_ANA_MBHC_ZDET, 0x80, 0x00); + + *zl = zdet; + +z_right: + if (!zr) + return; + /* Start impedance measurement for HPH_R */ + regmap_update_bits(aqt->regmap, + AQT1000_ANA_MBHC_ZDET, 0x40, 0x40); + dev_dbg(aqt->dev, "%s: ramp for HPH_R, noff = %d\n", + __func__, zdet_param->noff); + aqt_mbhc_get_result_params(aqt, d1_a, zdet_param->noff, &zdet); + regmap_update_bits(aqt->regmap, + AQT1000_ANA_MBHC_ZDET, 0x40, 0x00); + + *zr = zdet; +} + +static inline void aqt_wcd_mbhc_qfuse_cal(struct snd_soc_codec *codec, + int32_t *z_val, int flag_l_r) +{ + s16 q1; + int q1_cal; + + if (*z_val < (AQT_ZDET_VAL_400/1000)) + q1 = snd_soc_read(codec, + AQT1000_CHIP_CFG0_EFUSE_VAL_OUT1 + (2 * flag_l_r)); + else + q1 = snd_soc_read(codec, + AQT1000_CHIP_CFG0_EFUSE_VAL_OUT2 + (2 * flag_l_r)); + if (q1 & 0x80) + q1_cal = (10000 - ((q1 & 0x7F) * 25)); + else + q1_cal = (10000 + (q1 * 25)); + if (q1_cal > 0) + *z_val = ((*z_val) * 10000) / q1_cal; +} + +static void aqt_wcd_mbhc_calc_impedance(struct wcd_mbhc *mbhc, uint32_t *zl, + uint32_t *zr) +{ + struct snd_soc_codec *codec = mbhc->codec; + struct aqt1000 *aqt = dev_get_drvdata(codec->dev); + s16 reg0, reg1, reg2, reg3, reg4; + int32_t z1L, z1R, z1Ls; + int zMono, z_diff1, z_diff2; + bool is_fsm_disable = false; + struct aqt_mbhc_zdet_param zdet_param[] = { + {4, 0, 4, 0x08, 0x14, 0x18}, /* < 32ohm */ + {2, 0, 3, 0x18, 0x7C, 0x90}, /* 32ohm < Z < 400ohm */ + {1, 4, 5, 0x18, 0x7C, 0x90}, /* 400ohm < Z < 1200ohm */ + {1, 6, 7, 0x18, 0x7C, 0x90}, /* >1200ohm */ + }; + struct aqt_mbhc_zdet_param *zdet_param_ptr = NULL; + s16 d1_a[][4] = { + {0, 30, 90, 30}, + {0, 30, 30, 5}, + {0, 30, 30, 5}, + {0, 30, 30, 5}, + }; + s16 *d1 = NULL; + + WCD_MBHC_RSC_ASSERT_LOCKED(mbhc); + + reg0 = snd_soc_read(codec, AQT1000_ANA_MBHC_BTN5); + reg1 = snd_soc_read(codec, AQT1000_ANA_MBHC_BTN6); + reg2 = snd_soc_read(codec, AQT1000_ANA_MBHC_BTN7); + reg3 = snd_soc_read(codec, AQT1000_MBHC_CTL_CLK); + reg4 = snd_soc_read(codec, AQT1000_MBHC_NEW_ZDET_ANA_CTL); + + if (snd_soc_read(codec, AQT1000_ANA_MBHC_ELECT) & 0x80) { + is_fsm_disable = true; + regmap_update_bits(aqt->regmap, + AQT1000_ANA_MBHC_ELECT, 0x80, 0x00); + } + + /* For NO-jack, disable L_DET_EN before Z-det measurements */ + if (mbhc->hphl_swh) + regmap_update_bits(aqt->regmap, + AQT1000_ANA_MBHC_MECH, 0x80, 0x00); + + /* Turn off 100k pull down on HPHL */ + regmap_update_bits(aqt->regmap, + AQT1000_ANA_MBHC_MECH, 0x01, 0x00); + + /* First get impedance on Left */ + d1 = d1_a[1]; + zdet_param_ptr = &zdet_param[1]; + aqt_mbhc_zdet_ramp(codec, zdet_param_ptr, &z1L, NULL, d1); + + if (!AQT_MBHC_IS_SECOND_RAMP_REQUIRED(z1L)) + goto left_ch_impedance; + + /* Second ramp for left ch */ + if (z1L < AQT_ZDET_VAL_32) { + zdet_param_ptr = &zdet_param[0]; + d1 = d1_a[0]; + } else if ((z1L > AQT_ZDET_VAL_400) && (z1L <= AQT_ZDET_VAL_1200)) { + zdet_param_ptr = &zdet_param[2]; + d1 = d1_a[2]; + } else if (z1L > AQT_ZDET_VAL_1200) { + zdet_param_ptr = &zdet_param[3]; + d1 = d1_a[3]; + } + aqt_mbhc_zdet_ramp(codec, zdet_param_ptr, &z1L, NULL, d1); + +left_ch_impedance: + if ((z1L == AQT_ZDET_FLOATING_IMPEDANCE) || + (z1L > AQT_ZDET_VAL_100K)) { + *zl = AQT_ZDET_FLOATING_IMPEDANCE; + zdet_param_ptr = &zdet_param[1]; + d1 = d1_a[1]; + } else { + *zl = z1L/1000; + aqt_wcd_mbhc_qfuse_cal(codec, zl, 0); + } + dev_dbg(codec->dev, "%s: impedance on HPH_L = %d(ohms)\n", + __func__, *zl); + + /* Start of right impedance ramp and calculation */ + aqt_mbhc_zdet_ramp(codec, zdet_param_ptr, NULL, &z1R, d1); + if (AQT_MBHC_IS_SECOND_RAMP_REQUIRED(z1R)) { + if (((z1R > AQT_ZDET_VAL_1200) && + (zdet_param_ptr->noff == 0x6)) || + ((*zl) != AQT_ZDET_FLOATING_IMPEDANCE)) + goto right_ch_impedance; + /* Second ramp for right ch */ + if (z1R < AQT_ZDET_VAL_32) { + zdet_param_ptr = &zdet_param[0]; + d1 = d1_a[0]; + } else if ((z1R > AQT_ZDET_VAL_400) && + (z1R <= AQT_ZDET_VAL_1200)) { + zdet_param_ptr = &zdet_param[2]; + d1 = d1_a[2]; + } else if (z1R > AQT_ZDET_VAL_1200) { + zdet_param_ptr = &zdet_param[3]; + d1 = d1_a[3]; + } + aqt_mbhc_zdet_ramp(codec, zdet_param_ptr, NULL, &z1R, d1); + } +right_ch_impedance: + if ((z1R == AQT_ZDET_FLOATING_IMPEDANCE) || + (z1R > AQT_ZDET_VAL_100K)) { + *zr = AQT_ZDET_FLOATING_IMPEDANCE; + } else { + *zr = z1R/1000; + aqt_wcd_mbhc_qfuse_cal(codec, zr, 1); + } + dev_dbg(codec->dev, "%s: impedance on HPH_R = %d(ohms)\n", + __func__, *zr); + + /* Mono/stereo detection */ + if ((*zl == AQT_ZDET_FLOATING_IMPEDANCE) && + (*zr == AQT_ZDET_FLOATING_IMPEDANCE)) { + dev_dbg(codec->dev, + "%s: plug type is invalid or extension cable\n", + __func__); + goto zdet_complete; + } + if ((*zl == AQT_ZDET_FLOATING_IMPEDANCE) || + (*zr == AQT_ZDET_FLOATING_IMPEDANCE) || + ((*zl < WCD_MONO_HS_MIN_THR) && (*zr > WCD_MONO_HS_MIN_THR)) || + ((*zl > WCD_MONO_HS_MIN_THR) && (*zr < WCD_MONO_HS_MIN_THR))) { + dev_dbg(codec->dev, + "%s: Mono plug type with one ch floating or shorted to GND\n", + __func__); + mbhc->hph_type = WCD_MBHC_HPH_MONO; + goto zdet_complete; + } + snd_soc_update_bits(codec, AQT1000_HPH_R_ATEST, 0x02, 0x02); + snd_soc_update_bits(codec, AQT1000_HPH_PA_CTL2, 0x40, 0x01); + if (*zl < (AQT_ZDET_VAL_32/1000)) + aqt_mbhc_zdet_ramp(codec, &zdet_param[0], &z1Ls, NULL, d1); + else + aqt_mbhc_zdet_ramp(codec, &zdet_param[1], &z1Ls, NULL, d1); + snd_soc_update_bits(codec, AQT1000_HPH_PA_CTL2, 0x40, 0x00); + snd_soc_update_bits(codec, AQT1000_HPH_R_ATEST, 0x02, 0x00); + z1Ls /= 1000; + aqt_wcd_mbhc_qfuse_cal(codec, &z1Ls, 0); + /* Parallel of left Z and 9 ohm pull down resistor */ + zMono = ((*zl) * 9) / ((*zl) + 9); + z_diff1 = (z1Ls > zMono) ? (z1Ls - zMono) : (zMono - z1Ls); + z_diff2 = ((*zl) > z1Ls) ? ((*zl) - z1Ls) : (z1Ls - (*zl)); + if ((z_diff1 * (*zl + z1Ls)) > (z_diff2 * (z1Ls + zMono))) { + dev_dbg(codec->dev, "%s: stereo plug type detected\n", + __func__); + mbhc->hph_type = WCD_MBHC_HPH_STEREO; + } else { + dev_dbg(codec->dev, "%s: MONO plug type detected\n", + __func__); + mbhc->hph_type = WCD_MBHC_HPH_MONO; + } + +zdet_complete: + snd_soc_write(codec, AQT1000_ANA_MBHC_BTN5, reg0); + snd_soc_write(codec, AQT1000_ANA_MBHC_BTN6, reg1); + snd_soc_write(codec, AQT1000_ANA_MBHC_BTN7, reg2); + /* Turn on 100k pull down on HPHL */ + regmap_update_bits(aqt->regmap, + AQT1000_ANA_MBHC_MECH, 0x01, 0x01); + + /* For NO-jack, re-enable L_DET_EN after Z-det measurements */ + if (mbhc->hphl_swh) + regmap_update_bits(aqt->regmap, + AQT1000_ANA_MBHC_MECH, 0x80, 0x80); + + snd_soc_write(codec, AQT1000_MBHC_NEW_ZDET_ANA_CTL, reg4); + snd_soc_write(codec, AQT1000_MBHC_CTL_CLK, reg3); + if (is_fsm_disable) + regmap_update_bits(aqt->regmap, + AQT1000_ANA_MBHC_ELECT, 0x80, 0x80); +} + +static void aqt_mbhc_gnd_det_ctrl(struct snd_soc_codec *codec, bool enable) +{ + if (enable) { + snd_soc_update_bits(codec, AQT1000_ANA_MBHC_MECH, + 0x02, 0x02); + snd_soc_update_bits(codec, AQT1000_ANA_MBHC_MECH, + 0x40, 0x40); + } else { + snd_soc_update_bits(codec, AQT1000_ANA_MBHC_MECH, + 0x40, 0x00); + snd_soc_update_bits(codec, AQT1000_ANA_MBHC_MECH, + 0x02, 0x00); + } +} + +static void aqt_mbhc_hph_pull_down_ctrl(struct snd_soc_codec *codec, + bool enable) +{ + if (enable) { + snd_soc_update_bits(codec, AQT1000_HPH_PA_CTL2, + 0x40, 0x40); + snd_soc_update_bits(codec, AQT1000_HPH_PA_CTL2, + 0x10, 0x10); + } else { + snd_soc_update_bits(codec, AQT1000_HPH_PA_CTL2, + 0x40, 0x00); + snd_soc_update_bits(codec, AQT1000_HPH_PA_CTL2, + 0x10, 0x00); + } +} + +static void aqt_mbhc_moisture_config(struct wcd_mbhc *mbhc) +{ + struct snd_soc_codec *codec = mbhc->codec; + + if ((mbhc->moist_rref == R_OFF) || + (mbhc->mbhc_cfg->enable_usbc_analog)) { + snd_soc_update_bits(codec, AQT1000_MBHC_NEW_CTL_2, + 0x0C, R_OFF << 2); + return; + } + + /* Do not enable moisture detection if jack type is NC */ + if (!mbhc->hphl_swh) { + dev_dbg(codec->dev, "%s: disable moisture detection for NC\n", + __func__); + snd_soc_update_bits(codec, AQT1000_MBHC_NEW_CTL_2, + 0x0C, R_OFF << 2); + return; + } + + snd_soc_update_bits(codec, AQT1000_MBHC_NEW_CTL_2, + 0x0C, mbhc->moist_rref << 2); +} + +static void aqt_update_anc_state(struct snd_soc_codec *codec, bool enable, + int anc_num) +{ + if (enable) + snd_soc_update_bits(codec, AQT1000_CDC_RX1_RX_PATH_CFG0 + + (20 * anc_num), 0x10, 0x10); + else + snd_soc_update_bits(codec, AQT1000_CDC_RX1_RX_PATH_CFG0 + + (20 * anc_num), 0x10, 0x00); +} + +static bool aqt_is_anc_on(struct wcd_mbhc *mbhc) +{ + bool anc_on = false; + u16 ancl, ancr; + + ancl = + (snd_soc_read(mbhc->codec, AQT1000_CDC_RX1_RX_PATH_CFG0)) & 0x10; + ancr = + (snd_soc_read(mbhc->codec, AQT1000_CDC_RX2_RX_PATH_CFG0)) & 0x10; + + anc_on = !!(ancl | ancr); + + return anc_on; +} + +static const struct wcd_mbhc_cb mbhc_cb = { + .request_irq = aqt_mbhc_request_irq, + .irq_control = aqt_mbhc_irq_control, + .free_irq = aqt_mbhc_free_irq, + .clk_setup = aqt_mbhc_clk_setup, + .map_btn_code_to_num = aqt_mbhc_btn_to_num, + .mbhc_bias = aqt_mbhc_mbhc_bias_control, + .set_btn_thr = aqt_mbhc_program_btn_thr, + .lock_sleep = aqt_mbhc_lock_sleep, + .register_notifier = aqt_mbhc_register_notifier, + .micbias_enable_status = aqt_mbhc_micb_en_status, + .hph_pa_on_status = aqt_mbhc_hph_pa_on_status, + .hph_pull_up_control_v2 = aqt_mbhc_hph_l_pull_up_control, + .mbhc_micbias_control = aqt_mbhc_request_micbias, + .mbhc_micb_ramp_control = aqt_mbhc_micb_ramp_control, + .get_hwdep_fw_cal = aqt_get_hwdep_fw_cal, + .mbhc_micb_ctrl_thr_mic = aqt_mbhc_micb_ctrl_threshold_mic, + .compute_impedance = aqt_wcd_mbhc_calc_impedance, + .mbhc_gnd_det_ctrl = aqt_mbhc_gnd_det_ctrl, + .hph_pull_down_ctrl = aqt_mbhc_hph_pull_down_ctrl, + .mbhc_moisture_config = aqt_mbhc_moisture_config, + .update_anc_state = aqt_update_anc_state, + .is_anc_on = aqt_is_anc_on, +}; + +static int aqt_get_hph_type(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct aqt1000 *aqt = snd_soc_codec_get_drvdata(codec); + struct aqt1000_mbhc *aqt_mbhc = aqt->mbhc; + struct wcd_mbhc *mbhc; + + if (!aqt_mbhc) { + dev_err(codec->dev, "%s: mbhc not initialized!\n", __func__); + return -EINVAL; + } + + mbhc = &aqt_mbhc->wcd_mbhc; + + ucontrol->value.integer.value[0] = (u32) mbhc->hph_type; + dev_dbg(codec->dev, "%s: hph_type = %u\n", __func__, mbhc->hph_type); + + return 0; +} + +static int aqt_hph_impedance_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + uint32_t zl, zr; + bool hphr; + struct soc_multi_mixer_control *mc; + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct aqt1000 *aqt = snd_soc_codec_get_drvdata(codec); + struct aqt1000_mbhc *aqt_mbhc = aqt->mbhc; + + if (!aqt_mbhc) { + dev_err(codec->dev, "%s: mbhc not initialized!\n", __func__); + return -EINVAL; + } + + mc = (struct soc_multi_mixer_control *)(kcontrol->private_value); + hphr = mc->shift; + wcd_mbhc_get_impedance(&aqt_mbhc->wcd_mbhc, &zl, &zr); + dev_dbg(codec->dev, "%s: zl=%u(ohms), zr=%u(ohms)\n", __func__, zl, zr); + ucontrol->value.integer.value[0] = hphr ? zr : zl; + + return 0; +} + +static const struct snd_kcontrol_new hph_type_detect_controls[] = { + SOC_SINGLE_EXT("HPH Type", 0, 0, UINT_MAX, 0, + aqt_get_hph_type, NULL), +}; + +static const struct snd_kcontrol_new impedance_detect_controls[] = { + SOC_SINGLE_EXT("HPHL Impedance", 0, 0, UINT_MAX, 0, + aqt_hph_impedance_get, NULL), + SOC_SINGLE_EXT("HPHR Impedance", 0, 1, UINT_MAX, 0, + aqt_hph_impedance_get, NULL), +}; + +/* + * aqt_mbhc_get_impedance: get impedance of headphone left and right channels + * @aqt_mbhc: handle to struct aqt_mbhc * + * @zl: handle to left-ch impedance + * @zr: handle to right-ch impedance + * return 0 for success or error code in case of failure + */ +int aqt_mbhc_get_impedance(struct aqt1000_mbhc *aqt_mbhc, + uint32_t *zl, uint32_t *zr) +{ + if (!aqt_mbhc) { + pr_err("%s: mbhc not initialized!\n", __func__); + return -EINVAL; + } + if (!zl || !zr) { + pr_err("%s: zl or zr null!\n", __func__); + return -EINVAL; + } + + return wcd_mbhc_get_impedance(&aqt_mbhc->wcd_mbhc, zl, zr); +} +EXPORT_SYMBOL(aqt_mbhc_get_impedance); + +/* + * aqt_mbhc_hs_detect: starts mbhc insertion/removal functionality + * @codec: handle to snd_soc_codec * + * @mbhc_cfg: handle to mbhc configuration structure + * return 0 if mbhc_start is success or error code in case of failure + */ +int aqt_mbhc_hs_detect(struct snd_soc_codec *codec, + struct wcd_mbhc_config *mbhc_cfg) +{ + struct aqt1000 *aqt; + struct aqt1000_mbhc *aqt_mbhc; + + if (!codec) { + pr_err("%s: codec is NULL\n", __func__); + return -EINVAL; + } + + aqt = snd_soc_codec_get_drvdata(codec); + if (!aqt) { + pr_err("%s: aqt is NULL\n", __func__); + return -EINVAL; + } + + aqt_mbhc = aqt->mbhc; + if (!aqt_mbhc) { + dev_err(codec->dev, "%s: mbhc not initialized!\n", __func__); + return -EINVAL; + } + + return wcd_mbhc_start(&aqt_mbhc->wcd_mbhc, mbhc_cfg); +} +EXPORT_SYMBOL(aqt_mbhc_hs_detect); + +/* + * aqt_mbhc_hs_detect_exit: stop mbhc insertion/removal functionality + * @codec: handle to snd_soc_codec * + */ +void aqt_mbhc_hs_detect_exit(struct snd_soc_codec *codec) +{ + struct aqt1000 *aqt; + struct aqt1000_mbhc *aqt_mbhc; + + if (!codec) { + pr_err("%s: codec is NULL\n", __func__); + return; + } + + aqt = snd_soc_codec_get_drvdata(codec); + if (!aqt) { + pr_err("%s: aqt is NULL\n", __func__); + return; + } + + aqt_mbhc = aqt->mbhc; + if (!aqt_mbhc) { + dev_err(codec->dev, "%s: mbhc not initialized!\n", __func__); + return; + } + wcd_mbhc_stop(&aqt_mbhc->wcd_mbhc); +} +EXPORT_SYMBOL(aqt_mbhc_hs_detect_exit); + +/* + * aqt_mbhc_post_ssr_init: initialize mbhc for aqt post subsystem restart + * @mbhc: poniter to aqt_mbhc structure + * @codec: handle to snd_soc_codec * + * + * return 0 if mbhc_init is success or error code in case of failure + */ +int aqt_mbhc_post_ssr_init(struct aqt1000_mbhc *mbhc, + struct snd_soc_codec *codec) +{ + int ret; + struct wcd_mbhc *wcd_mbhc; + + if (!mbhc || !codec) + return -EINVAL; + + wcd_mbhc = &mbhc->wcd_mbhc; + if (wcd_mbhc == NULL) { + pr_err("%s: wcd_mbhc is NULL\n", __func__); + return -EINVAL; + } + + wcd_mbhc_deinit(wcd_mbhc); + ret = wcd_mbhc_init(wcd_mbhc, codec, &mbhc_cb, &intr_ids, + wcd_mbhc_registers, AQT_ZDET_SUPPORTED); + if (ret) { + dev_err(codec->dev, "%s: mbhc initialization failed\n", + __func__); + goto done; + } + +done: + return ret; +} +EXPORT_SYMBOL(aqt_mbhc_post_ssr_init); + +/* + * aqt_mbhc_init: initialize mbhc for aqt + * @mbhc: poniter to aqt_mbhc struct pointer to store the configs + * @codec: handle to snd_soc_codec * + * @fw_data: handle to firmware data + * + * return 0 if mbhc_init is success or error code in case of failure + */ +int aqt_mbhc_init(struct aqt1000_mbhc **mbhc, struct snd_soc_codec *codec, + struct fw_info *fw_data) +{ + struct aqt1000_mbhc *aqt_mbhc; + struct wcd_mbhc *wcd_mbhc; + int ret; + + if (!codec) { + pr_err("%s: codec is NULL\n", __func__); + return -EINVAL; + } + + aqt_mbhc = devm_kzalloc(codec->dev, sizeof(struct aqt1000_mbhc), + GFP_KERNEL); + if (!aqt_mbhc) + return -ENOMEM; + + aqt_mbhc->aqt = dev_get_drvdata(codec->dev); + aqt_mbhc->fw_data = fw_data; + BLOCKING_INIT_NOTIFIER_HEAD(&aqt_mbhc->notifier); + wcd_mbhc = &aqt_mbhc->wcd_mbhc; + if (wcd_mbhc == NULL) { + pr_err("%s: wcd_mbhc is NULL\n", __func__); + ret = -EINVAL; + goto err; + } + + + /* Setting default mbhc detection logic to ADC */ + wcd_mbhc->mbhc_detection_logic = WCD_DETECTION_ADC; + + ret = wcd_mbhc_init(wcd_mbhc, codec, &mbhc_cb, + &intr_ids, wcd_mbhc_registers, + AQT_ZDET_SUPPORTED); + if (ret) { + dev_err(codec->dev, "%s: mbhc initialization failed\n", + __func__); + goto err; + } + + (*mbhc) = aqt_mbhc; + snd_soc_add_codec_controls(codec, impedance_detect_controls, + ARRAY_SIZE(impedance_detect_controls)); + snd_soc_add_codec_controls(codec, hph_type_detect_controls, + ARRAY_SIZE(hph_type_detect_controls)); + + return 0; +err: + devm_kfree(codec->dev, aqt_mbhc); + return ret; +} +EXPORT_SYMBOL(aqt_mbhc_init); + +/* + * aqt_mbhc_deinit: deinitialize mbhc for aqt + * @codec: handle to snd_soc_codec * + */ +void aqt_mbhc_deinit(struct snd_soc_codec *codec) +{ + struct aqt1000 *aqt; + struct aqt1000_mbhc *aqt_mbhc; + + if (!codec) { + pr_err("%s: codec is NULL\n", __func__); + return; + } + + aqt = snd_soc_codec_get_drvdata(codec); + if (!aqt) { + pr_err("%s: aqt is NULL\n", __func__); + return; + } + + aqt_mbhc = aqt->mbhc; + if (aqt_mbhc) { + wcd_mbhc_deinit(&aqt_mbhc->wcd_mbhc); + devm_kfree(codec->dev, aqt_mbhc); + } +} +EXPORT_SYMBOL(aqt_mbhc_deinit); diff --git a/audio-kernel/asoc/codecs/aqt1000/aqt1000-mbhc.h b/audio-kernel/asoc/codecs/aqt1000/aqt1000-mbhc.h new file mode 100644 index 000000000..bdf0f5a69 --- /dev/null +++ b/audio-kernel/asoc/codecs/aqt1000/aqt1000-mbhc.h @@ -0,0 +1,71 @@ +/* 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 + * 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 __AQT1000_MBHC_H__ +#define __AQT1000_MBHC_H__ +#include "../wcd-mbhc-v2.h" + +struct aqt1000_mbhc { + struct wcd_mbhc wcd_mbhc; + struct blocking_notifier_head notifier; + struct aqt1000 *aqt; + struct fw_info *fw_data; + bool mbhc_started; +}; + +#if IS_ENABLED(CONFIG_SND_SOC_AQT1000) +extern int aqt_mbhc_init(struct aqt1000_mbhc **mbhc, + struct snd_soc_codec *codec, + struct fw_info *fw_data); +extern void aqt_mbhc_hs_detect_exit(struct snd_soc_codec *codec); +extern int aqt_mbhc_hs_detect(struct snd_soc_codec *codec, + struct wcd_mbhc_config *mbhc_cfg); +extern void aqt_mbhc_deinit(struct snd_soc_codec *codec); +extern int aqt_mbhc_post_ssr_init(struct aqt1000_mbhc *mbhc, + struct snd_soc_codec *codec); +extern int aqt_mbhc_get_impedance(struct aqt1000_mbhc *aqt_mbhc, + uint32_t *zl, uint32_t *zr); +#else +static inline int aqt_mbhc_init(struct aqt1000_mbhc **mbhc, + struct snd_soc_codec *codec, + struct fw_info *fw_data) +{ + return 0; +} +static inline void aqt_mbhc_hs_detect_exit(struct snd_soc_codec *codec) +{ +} +static inline int aqt_mbhc_hs_detect(struct snd_soc_codec *codec, + struct wcd_mbhc_config *mbhc_cfg) +{ + return 0; +} +static inline void aqt_mbhc_deinit(struct snd_soc_codec *codec) +{ +} +static inline int aqt_mbhc_post_ssr_init(struct aqt1000_mbhc *mbhc, + struct snd_soc_codec *codec) +{ + return 0; +} + +static inline int aqt_mbhc_get_impedance(struct aqt1000_mbhc *aqt_mbhc, + uint32_t *zl, uint32_t *zr) +{ + if (zl) + *zl = 0; + if (zr) + *zr = 0; + return -EINVAL; +} +#endif + +#endif /* __AQT1000_MBHC_H__ */ diff --git a/audio-kernel/asoc/codecs/aqt1000/aqt1000-reg-defaults.h b/audio-kernel/asoc/codecs/aqt1000/aqt1000-reg-defaults.h new file mode 100644 index 000000000..5a5da25fa --- /dev/null +++ b/audio-kernel/asoc/codecs/aqt1000/aqt1000-reg-defaults.h @@ -0,0 +1,1616 @@ +/* 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 _AQT1000_REG_DEFAULTS_H +#define _AQT1000_REG_DEFAULTS_H + +#include +#include "aqt1000-registers.h" + +#define AQT1000_REG(reg) ((reg) & 0xFF) +#define AQT1000_PAGE_SIZE 256 + +enum { + AQT1000_PAGE_0 = 0, + AQT1000_PAGE_1, + AQT1000_PAGE_2, + AQT1000_PAGE_5 = 5, + AQT1000_PAGE_6, + AQT1000_PAGE_7, + AQT1000_PAGE_10 = 0xA, + AQT1000_PAGE_11, + AQT1000_PAGE_12, + AQT1000_PAGE_13, + AQT1000_PAGE_15 = 0xF, + AQT1000_PAGE_128, + AQT1000_PAGE_MAX, +}; + +enum { + AQT1000_WO = 0, + AQT1000_RO, + AQT1000_RW, +}; + +static const struct reg_default aqt1000_defaults[] = { + {AQT1000_CHIP_CFG1_PWR_MEM_SD, 0x07}, + {AQT1000_CHIP_CFG1_PWR_SYS_MEM_SD_RAM, 0x00}, + {AQT1000_CHIP_CFG1_PWR_SYS_MEM_SD_ROM, 0x00}, + {AQT1000_CHIP_CFG1_PWR_SYS_MEM_FORCE_DS_RAM, 0x00}, + {AQT1000_CHIP_CFG1_PWR_SYS_MEM_FORCE_DS_ROM, 0x00}, + {AQT1000_CHIP_CFG1_CLK_CFG_FLL, 0x20}, + {AQT1000_CHIP_CFG1_CLK_CFG_SPI_M, 0x01}, + {AQT1000_CHIP_CFG1_CLK_CFG_I2C_M, 0x01}, + {AQT1000_CHIP_CFG1_CLK_CFG_UART, 0x01}, + {AQT1000_CHIP_CFG1_RST_USB_SS, 0x0E}, + {AQT1000_CHIP_CFG1_RST_BLSP, 0x0F}, + {AQT1000_CHIP_CFG1_RST_BUS_MTRX, 0x00}, + {AQT1000_CHIP_CFG1_RST_MISC, 0x00}, + {AQT1000_CHIP_CFG1_ANA_WAIT_STATE_CTL, 0xCC}, + {AQT1000_PAGE1_PAGE_REGISTER, 0x00}, + {AQT1000_FLL_USER_CTL_0, 0x71}, + {AQT1000_FLL_USER_CTL_1, 0x34}, + {AQT1000_FLL_USER_CTL_2, 0x0B}, + {AQT1000_FLL_USER_CTL_3, 0x02}, + {AQT1000_FLL_USER_CTL_4, 0x04}, + {AQT1000_FLL_USER_CTL_5, 0x02}, + {AQT1000_FLL_USER_CTL_6, 0x6E}, + {AQT1000_FLL_USER_CTL_7, 0x00}, + {AQT1000_FLL_USER_CTL_8, 0x94}, + {AQT1000_FLL_USER_CTL_9, 0x70}, + {AQT1000_FLL_L_VAL_CTL_0, 0x34}, + {AQT1000_FLL_L_VAL_CTL_1, 0x00}, + {AQT1000_FLL_DSM_FRAC_CTL_0, 0x00}, + {AQT1000_FLL_DSM_FRAC_CTL_1, 0xFF}, + {AQT1000_FLL_CONFIG_CTL_0, 0x6B}, + {AQT1000_FLL_CONFIG_CTL_1, 0x05}, + {AQT1000_FLL_CONFIG_CTL_2, 0x08}, + {AQT1000_FLL_CONFIG_CTL_3, 0x00}, + {AQT1000_FLL_CONFIG_CTL_4, 0x10}, + {AQT1000_FLL_TEST_CTL_0, 0x80}, + {AQT1000_FLL_TEST_CTL_1, 0x00}, + {AQT1000_FLL_TEST_CTL_2, 0x00}, + {AQT1000_FLL_TEST_CTL_3, 0x00}, + {AQT1000_FLL_TEST_CTL_4, 0x00}, + {AQT1000_FLL_TEST_CTL_5, 0x00}, + {AQT1000_FLL_TEST_CTL_6, 0x04}, + {AQT1000_FLL_TEST_CTL_7, 0x33}, + {AQT1000_FLL_FREQ_CTL_0, 0x00}, + {AQT1000_FLL_FREQ_CTL_1, 0x00}, + {AQT1000_FLL_FREQ_CTL_2, 0x00}, + {AQT1000_FLL_FREQ_CTL_3, 0x00}, + {AQT1000_FLL_SSC_CTL_0, 0x00}, + {AQT1000_FLL_SSC_CTL_1, 0x00}, + {AQT1000_FLL_SSC_CTL_2, 0x00}, + {AQT1000_FLL_SSC_CTL_3, 0x00}, + {AQT1000_FLL_FLL_MODE, 0xA0}, + {AQT1000_FLL_STATUS_0, 0x00}, + {AQT1000_FLL_STATUS_1, 0x00}, + {AQT1000_FLL_STATUS_2, 0x00}, + {AQT1000_FLL_STATUS_3, 0x00}, + {AQT1000_PAGE2_PAGE_REGISTER, 0x00}, + {AQT1000_I2S_I2S_0_TX_CFG, 0x00}, + {AQT1000_I2S_I2S_0_RX_CFG, 0x00}, + {AQT1000_I2S_I2S_0_CTL, 0x0C}, + {AQT1000_I2S_I2S_CLKSRC_CTL, 0x01}, + {AQT1000_I2S_I2S_HS_CLK_CTL, 0x00}, + {AQT1000_I2S_I2S_0_RST, 0x00}, + {AQT1000_I2S_SHADOW_I2S_0_CTL, 0x00}, + {AQT1000_I2S_SHADOW_I2S_0_RX_CFG, 0x09}, + {AQT1000_PAGE5_PAGE_REGISTER, 0x00}, + {AQT1000_INTR_CTRL_MCU_INT_POLARITY, 0x00}, + {AQT1000_INTR_CTRL_INT_MASK_0, 0xFE}, + {AQT1000_INTR_CTRL_INT_MASK_1, 0xFF}, + {AQT1000_INTR_CTRL_INT_MASK_2, 0xFF}, + {AQT1000_INTR_CTRL_INT_MASK_3, 0xEF}, + {AQT1000_INTR_CTRL_INT_MASK_4, 0x3B}, + {AQT1000_INTR_CTRL_INT_MASK_5, 0xFF}, + {AQT1000_INTR_CTRL_INT_MASK_6, 0x3F}, + {AQT1000_INTR_CTRL_INT_STATUS_0, 0x00}, + {AQT1000_INTR_CTRL_INT_STATUS_1, 0x00}, + {AQT1000_INTR_CTRL_INT_STATUS_2, 0x00}, + {AQT1000_INTR_CTRL_INT_STATUS_3, 0x00}, + {AQT1000_INTR_CTRL_INT_STATUS_4, 0x00}, + {AQT1000_INTR_CTRL_INT_STATUS_5, 0x00}, + {AQT1000_INTR_CTRL_INT_STATUS_6, 0x00}, + {AQT1000_INTR_CTRL_INT_CLEAR_0, 0x00}, + {AQT1000_INTR_CTRL_INT_CLEAR_1, 0x00}, + {AQT1000_INTR_CTRL_INT_CLEAR_2, 0x00}, + {AQT1000_INTR_CTRL_INT_CLEAR_3, 0x00}, + {AQT1000_INTR_CTRL_INT_CLEAR_4, 0x00}, + {AQT1000_INTR_CTRL_INT_CLEAR_5, 0x00}, + {AQT1000_INTR_CTRL_INT_CLEAR_6, 0x00}, + {AQT1000_INTR_CTRL_INT_TYPE_0, 0xEF}, + {AQT1000_INTR_CTRL_INT_TYPE_1, 0x03}, + {AQT1000_INTR_CTRL_INT_TYPE_2, 0x00}, + {AQT1000_INTR_CTRL_INT_TYPE_3, 0x20}, + {AQT1000_INTR_CTRL_INT_TYPE_4, 0x44}, + {AQT1000_INTR_CTRL_INT_TYPE_5, 0x00}, + {AQT1000_INTR_CTRL_INT_TYPE_6, 0x00}, + {AQT1000_INTR_CTRL_INT_TEST_EN_0, 0x00}, + {AQT1000_INTR_CTRL_INT_TEST_EN_1, 0x00}, + {AQT1000_INTR_CTRL_INT_TEST_EN_2, 0x00}, + {AQT1000_INTR_CTRL_INT_TEST_EN_3, 0x00}, + {AQT1000_INTR_CTRL_INT_TEST_EN_4, 0x00}, + {AQT1000_INTR_CTRL_INT_TEST_EN_5, 0x00}, + {AQT1000_INTR_CTRL_INT_TEST_EN_6, 0x00}, + {AQT1000_INTR_CTRL_INT_TEST_VAL_0, 0x00}, + {AQT1000_INTR_CTRL_INT_TEST_VAL_1, 0x00}, + {AQT1000_INTR_CTRL_INT_TEST_VAL_2, 0x00}, + {AQT1000_INTR_CTRL_INT_TEST_VAL_3, 0x00}, + {AQT1000_INTR_CTRL_INT_TEST_VAL_4, 0x00}, + {AQT1000_INTR_CTRL_INT_TEST_VAL_5, 0x00}, + {AQT1000_INTR_CTRL_INT_TEST_VAL_6, 0x00}, + {AQT1000_INTR_CTRL_INT_DEST_0, 0x02}, + {AQT1000_INTR_CTRL_INT_DEST_1, 0x00}, + {AQT1000_INTR_CTRL_INT_DEST_2, 0x00}, + {AQT1000_INTR_CTRL_INT_DEST_3, 0x00}, + {AQT1000_INTR_CTRL_INT_DEST_4, 0x00}, + {AQT1000_INTR_CTRL_INT_DEST_5, 0x00}, + {AQT1000_INTR_CTRL_INT_DEST_6, 0x00}, + {AQT1000_INTR_CTRL_INT_DEST_7, 0x00}, + {AQT1000_INTR_CTRL_INT_DEST_8, 0x00}, + {AQT1000_INTR_CTRL_INT_DEST_9, 0x00}, + {AQT1000_INTR_CTRL_INT_DEST_10, 0x00}, + {AQT1000_INTR_CTRL_INT_DEST_11, 0x00}, + {AQT1000_INTR_CTRL_INT_DEST_12, 0x00}, + {AQT1000_INTR_CTRL_INT_DEST_13, 0x00}, + {AQT1000_INTR_CTRL_CLR_COMMIT, 0x00}, + {AQT1000_ANA_PAGE_REGISTER, 0x00}, + {AQT1000_ANA_BIAS, 0x00}, + {AQT1000_ANA_RX_SUPPLIES, 0x00}, + {AQT1000_ANA_HPH, 0x0C}, + {AQT1000_ANA_AMIC1, 0x20}, + {AQT1000_ANA_AMIC2, 0x00}, + {AQT1000_ANA_AMIC3, 0x20}, + {AQT1000_ANA_AMIC3_HPF, 0x00}, + {AQT1000_ANA_MBHC_MECH, 0x39}, + {AQT1000_ANA_MBHC_ELECT, 0x08}, + {AQT1000_ANA_MBHC_ZDET, 0x00}, + {AQT1000_ANA_MBHC_RESULT_1, 0x00}, + {AQT1000_ANA_MBHC_RESULT_2, 0x00}, + {AQT1000_ANA_MBHC_RESULT_3, 0x00}, + {AQT1000_ANA_MBHC_BTN0, 0x00}, + {AQT1000_ANA_MBHC_BTN1, 0x10}, + {AQT1000_ANA_MBHC_BTN2, 0x20}, + {AQT1000_ANA_MBHC_BTN3, 0x30}, + {AQT1000_ANA_MBHC_BTN4, 0x40}, + {AQT1000_ANA_MBHC_BTN5, 0x50}, + {AQT1000_ANA_MBHC_BTN6, 0x60}, + {AQT1000_ANA_MBHC_BTN7, 0x70}, + {AQT1000_ANA_MICB1, 0x10}, + {AQT1000_ANA_MICB1_RAMP, 0x00}, + {AQT1000_BIAS_CTL, 0x28}, + {AQT1000_BIAS_CCOMP_FINE_ADJ, 0x75}, + {AQT1000_LED_LED_MODE_SEL_R, 0x00}, + {AQT1000_LED_LED_MISC_R, 0x00}, + {AQT1000_LED_LED_MODE_SEL_G, 0x00}, + {AQT1000_LED_LED_MISC_G, 0x00}, + {AQT1000_LED_LED_MODE_SEL_B, 0x00}, + {AQT1000_LED_LED_MISC_B, 0x00}, + {AQT1000_LDOH_MODE, 0x1D}, + {AQT1000_LDOH_BIAS, 0x00}, + {AQT1000_LDOH_STB_LOADS, 0x00}, + {AQT1000_LDOH_MISC1, 0x00}, + {AQT1000_LDOL_VDDCX_ADJUST, 0x01}, + {AQT1000_LDOL_DISABLE_LDOL, 0x00}, + {AQT1000_BUCK_5V_EN_CTL, 0x03}, + {AQT1000_BUCK_5V_VOUT_SEL, 0x03}, + {AQT1000_BUCK_5V_CTRL_VCL_1, 0x03}, + {AQT1000_BUCK_5V_CTRL_VCL_2, 0x21}, + {AQT1000_BUCK_5V_CTRL_CCL_2, 0x20}, + {AQT1000_BUCK_5V_CTRL_CCL_1, 0xA1}, + {AQT1000_BUCK_5V_CTRL_CCL_3, 0x02}, + {AQT1000_BUCK_5V_CTRL_CCL_4, 0x05}, + {AQT1000_BUCK_5V_CTRL_CCL_5, 0x00}, + {AQT1000_BUCK_5V_IBIAS_CTL_1, 0x37}, + {AQT1000_BUCK_5V_IBIAS_CTL_2, 0x00}, + {AQT1000_BUCK_5V_IBIAS_CTL_3, 0x33}, + {AQT1000_BUCK_5V_IBIAS_CTL_4, 0x33}, + {AQT1000_BUCK_5V_IBIAS_CTL_5, 0x00}, + {AQT1000_BUCK_5V_ATEST_DTEST_CTL, 0x00}, + {AQT1000_PON_BG_CTRL, 0x80}, + {AQT1000_PON_TEST_CTRL, 0x00}, + {AQT1000_MBHC_CTL_CLK, 0x30}, + {AQT1000_MBHC_CTL_ANA, 0x00}, + {AQT1000_MBHC_CTL_SPARE_1, 0x00}, + {AQT1000_MBHC_CTL_SPARE_2, 0x00}, + {AQT1000_MBHC_CTL_BCS, 0x00}, + {AQT1000_MBHC_MOISTURE_DET_FSM_STATUS, 0x00}, + {AQT1000_MBHC_TEST_CTL, 0x00}, + {AQT1000_MICB1_TEST_CTL_1, 0x1A}, + {AQT1000_MICB1_TEST_CTL_2, 0x18}, + {AQT1000_MICB1_TEST_CTL_3, 0xA4}, + {AQT1000_MICB1_MISC_MICB1_INM_RES_BIAS, 0x00}, + {AQT1000_MICB1_MISC_MICB_MISC1, 0x00}, + {AQT1000_MICB1_MISC_MICB_MISC2, 0x00}, + {AQT1000_TX_COM_ADC_VCM, 0x39}, + {AQT1000_TX_COM_BIAS_ATEST, 0xC0}, + {AQT1000_TX_COM_ADC_INT1_IB, 0x6F}, + {AQT1000_TX_COM_ADC_INT2_IB, 0x4F}, + {AQT1000_TX_COM_TXFE_DIV_CTL, 0x2E}, + {AQT1000_TX_COM_TXFE_DIV_START, 0x00}, + {AQT1000_TX_COM_TXFE_DIV_STOP_9P6M, 0xC7}, + {AQT1000_TX_COM_TXFE_DIV_STOP_12P288M, 0xFF}, + {AQT1000_TX_1_2_TEST_EN, 0xCC}, + {AQT1000_TX_1_2_ADC_IB, 0x09}, + {AQT1000_TX_1_2_ATEST_REFCTL, 0x0A}, + {AQT1000_TX_1_2_TEST_CTL, 0x38}, + {AQT1000_TX_1_2_TEST_BLK_EN, 0xFF}, + {AQT1000_TX_1_2_TXFE_CLKDIV, 0x00}, + {AQT1000_TX_1_2_SAR1_ERR, 0x00}, + {AQT1000_TX_1_2_SAR2_ERR, 0x00}, + {AQT1000_TX_3_TEST_EN, 0xC0}, + {AQT1000_TX_3_ADC_IB, 0x09}, + {AQT1000_TX_3_ATEST_REFCTL, 0x0A}, + {AQT1000_TX_3_TEST_CTL, 0x38}, + {AQT1000_TX_3_TEST_BLK_EN, 0xFE}, + {AQT1000_TX_3_TXFE_CLKDIV, 0x00}, + {AQT1000_TX_3_SAR1_ERR, 0x00}, + {AQT1000_TX_3_SAR2_ERR, 0x00}, + {AQT1000_TX_ATEST1_2_SEL, 0x60}, + {AQT1000_CLASSH_MODE_1, 0x40}, + {AQT1000_CLASSH_MODE_2, 0x3A}, + {AQT1000_CLASSH_MODE_3, 0x00}, + {AQT1000_CLASSH_CTRL_VCL_1, 0x70}, + {AQT1000_CLASSH_CTRL_VCL_2, 0x82}, + {AQT1000_CLASSH_CTRL_CCL_1, 0x31}, + {AQT1000_CLASSH_CTRL_CCL_2, 0x80}, + {AQT1000_CLASSH_CTRL_CCL_3, 0x80}, + {AQT1000_CLASSH_CTRL_CCL_4, 0x51}, + {AQT1000_CLASSH_CTRL_CCL_5, 0x00}, + {AQT1000_CLASSH_BUCK_TMUX_A_D, 0x00}, + {AQT1000_CLASSH_BUCK_SW_DRV_CNTL, 0x77}, + {AQT1000_CLASSH_SPARE, 0x00}, + {AQT1000_FLYBACK_EN, 0x4E}, + {AQT1000_FLYBACK_VNEG_CTRL_1, 0x0B}, + {AQT1000_FLYBACK_VNEG_CTRL_2, 0x45}, + {AQT1000_FLYBACK_VNEG_CTRL_3, 0x74}, + {AQT1000_FLYBACK_VNEG_CTRL_4, 0x7F}, + {AQT1000_FLYBACK_VNEG_CTRL_5, 0x83}, + {AQT1000_FLYBACK_VNEG_CTRL_6, 0x98}, + {AQT1000_FLYBACK_VNEG_CTRL_7, 0xA9}, + {AQT1000_FLYBACK_VNEG_CTRL_8, 0x68}, + {AQT1000_FLYBACK_VNEG_CTRL_9, 0x64}, + {AQT1000_FLYBACK_VNEGDAC_CTRL_1, 0xED}, + {AQT1000_FLYBACK_VNEGDAC_CTRL_2, 0xF0}, + {AQT1000_FLYBACK_VNEGDAC_CTRL_3, 0xA6}, + {AQT1000_FLYBACK_CTRL_1, 0x65}, + {AQT1000_FLYBACK_TEST_CTL, 0x00}, + {AQT1000_RX_AUX_SW_CTL, 0x00}, + {AQT1000_RX_PA_AUX_IN_CONN, 0x00}, + {AQT1000_RX_TIMER_DIV, 0x32}, + {AQT1000_RX_OCP_CTL, 0x1F}, + {AQT1000_RX_OCP_COUNT, 0x77}, + {AQT1000_RX_BIAS_ATEST, 0x00}, + {AQT1000_RX_BIAS_MISC1, 0xAA}, + {AQT1000_RX_BIAS_HPH_LDO, 0xA9}, + {AQT1000_RX_BIAS_HPH_PA, 0xAA}, + {AQT1000_RX_BIAS_HPH_RDACBUFF_CNP2, 0x8A}, + {AQT1000_RX_BIAS_HPH_RDAC_LDO, 0x88}, + {AQT1000_RX_BIAS_HPH_CNP1, 0x82}, + {AQT1000_RX_BIAS_HPH_LOWPOWER, 0x82}, + {AQT1000_RX_BIAS_MISC2, 0x80}, + {AQT1000_RX_BIAS_MISC3, 0x88}, + {AQT1000_RX_BIAS_MISC4, 0x88}, + {AQT1000_RX_BIAS_MISC5, 0xA8}, + {AQT1000_RX_BIAS_BUCK_RST, 0x08}, + {AQT1000_RX_BIAS_BUCK_VREF_ERRAMP, 0x44}, + {AQT1000_RX_BIAS_FLYB_ERRAMP, 0x40}, + {AQT1000_RX_BIAS_FLYB_BUFF, 0xAA}, + {AQT1000_RX_BIAS_FLYB_MID_RST, 0x14}, + {AQT1000_HPH_L_STATUS, 0x04}, + {AQT1000_HPH_R_STATUS, 0x04}, + {AQT1000_HPH_CNP_EN, 0x80}, + {AQT1000_HPH_CNP_WG_CTL, 0x9A}, + {AQT1000_HPH_CNP_WG_TIME, 0x14}, + {AQT1000_HPH_OCP_CTL, 0x28}, + {AQT1000_HPH_AUTO_CHOP, 0x16}, + {AQT1000_HPH_CHOP_CTL, 0x83}, + {AQT1000_HPH_PA_CTL1, 0x46}, + {AQT1000_HPH_PA_CTL2, 0x50}, + {AQT1000_HPH_L_EN, 0x80}, + {AQT1000_HPH_L_TEST, 0xE0}, + {AQT1000_HPH_L_ATEST, 0x50}, + {AQT1000_HPH_R_EN, 0x80}, + {AQT1000_HPH_R_TEST, 0xE0}, + {AQT1000_HPH_R_ATEST, 0x54}, + {AQT1000_HPH_RDAC_CLK_CTL1, 0x99}, + {AQT1000_HPH_RDAC_CLK_CTL2, 0x9B}, + {AQT1000_HPH_RDAC_LDO_CTL, 0x33}, + {AQT1000_HPH_RDAC_CHOP_CLK_LP_CTL, 0x00}, + {AQT1000_HPH_REFBUFF_UHQA_CTL, 0xA8}, + {AQT1000_HPH_REFBUFF_LP_CTL, 0x0E}, + {AQT1000_HPH_L_DAC_CTL, 0x00}, + {AQT1000_HPH_R_DAC_CTL, 0x00}, + {AQT1000_HPHLR_SURGE_COMP_SEL, 0x55}, + {AQT1000_HPHLR_SURGE_EN, 0x1D}, + {AQT1000_HPHLR_SURGE_MISC1, 0xA0}, + {AQT1000_HPHLR_SURGE_STATUS, 0x00}, + {AQT1000_ANA_NEW_PAGE_REGISTER, 0x00}, + {AQT1000_HPH_NEW_ANA_HPH2, 0x00}, + {AQT1000_HPH_NEW_ANA_HPH3, 0x00}, + {AQT1000_CLK_SYS_MCLK1_PRG, 0x09}, + {AQT1000_CLK_SYS_MCLK2_I2S_HS_CLK_PRG, 0x20}, + {AQT1000_CLK_SYS_XO_CAP_XTP, 0x3F}, + {AQT1000_CLK_SYS_XO_CAP_XTM, 0x3F}, + {AQT1000_CLK_SYS_PLL_ENABLES, 0x00}, + {AQT1000_CLK_SYS_PLL_PRESET, 0x00}, + {AQT1000_CLK_SYS_PLL_STATUS, 0x00}, + {AQT1000_MBHC_NEW_ELECT_REM_CLAMP_CTL, 0x00}, + {AQT1000_MBHC_NEW_CTL_1, 0x02}, + {AQT1000_MBHC_NEW_CTL_2, 0x05}, + {AQT1000_MBHC_NEW_PLUG_DETECT_CTL, 0x29}, + {AQT1000_MBHC_NEW_ZDET_ANA_CTL, 0x0F}, + {AQT1000_MBHC_NEW_ZDET_RAMP_CTL, 0x00}, + {AQT1000_MBHC_NEW_FSM_STATUS, 0x00}, + {AQT1000_MBHC_NEW_ADC_RESULT, 0x00}, + {AQT1000_HPH_NEW_INT_RDAC_GAIN_CTL, 0x40}, + {AQT1000_HPH_NEW_INT_RDAC_HD2_CTL_L, 0x81}, + {AQT1000_HPH_NEW_INT_RDAC_VREF_CTL, 0x10}, + {AQT1000_HPH_NEW_INT_RDAC_OVERRIDE_CTL, 0x00}, + {AQT1000_HPH_NEW_INT_RDAC_HD2_CTL_R, 0x81}, + {AQT1000_HPH_NEW_INT_PA_MISC1, 0x22}, + {AQT1000_HPH_NEW_INT_PA_MISC2, 0x00}, + {AQT1000_HPH_NEW_INT_PA_RDAC_MISC, 0x00}, + {AQT1000_HPH_NEW_INT_HPH_TIMER1, 0xFE}, + {AQT1000_HPH_NEW_INT_HPH_TIMER2, 0x02}, + {AQT1000_HPH_NEW_INT_HPH_TIMER3, 0x4E}, + {AQT1000_HPH_NEW_INT_HPH_TIMER4, 0x54}, + {AQT1000_HPH_NEW_INT_PA_RDAC_MISC2, 0x80}, + {AQT1000_HPH_NEW_INT_PA_RDAC_MISC3, 0x00}, + {AQT1000_RX_NEW_INT_HPH_RDAC_BIAS_LOHIFI, 0x62}, + {AQT1000_RX_NEW_INT_HPH_RDAC_BIAS_ULP, 0x01}, + {AQT1000_RX_NEW_INT_HPH_RDAC_LDO_LP, 0x11}, + {AQT1000_CLK_SYS_INT_CLK_TEST1, 0x00}, + {AQT1000_CLK_SYS_INT_XO_TEST1, 0x98}, + {AQT1000_CLK_SYS_INT_XO_TEST2, 0x00}, + {AQT1000_CLK_SYS_INT_POST_DIV_REG0, 0x00}, + {AQT1000_CLK_SYS_INT_POST_DIV_REG1, 0x00}, + {AQT1000_CLK_SYS_INT_REF_DIV_REG0, 0x00}, + {AQT1000_CLK_SYS_INT_REF_DIV_REG1, 0x00}, + {AQT1000_CLK_SYS_INT_FILTER_REG0, 0x00}, + {AQT1000_CLK_SYS_INT_FILTER_REG1, 0x00}, + {AQT1000_CLK_SYS_INT_PLL_L_VAL, 0x00}, + {AQT1000_CLK_SYS_INT_PLL_M_VAL, 0x00}, + {AQT1000_CLK_SYS_INT_PLL_N_VAL, 0x00}, + {AQT1000_CLK_SYS_INT_TEST_REG0, 0x00}, + {AQT1000_CLK_SYS_INT_PFD_CP_DSM_PROG, 0x00}, + {AQT1000_CLK_SYS_INT_VCO_PROG, 0x00}, + {AQT1000_CLK_SYS_INT_TEST_REG1, 0x00}, + {AQT1000_CLK_SYS_INT_LDO_LOCK_CFG, 0x00}, + {AQT1000_CLK_SYS_INT_DIG_LOCK_DET_CFG, 0x00}, + {AQT1000_MBHC_NEW_INT_MOISTURE_DET_DC_CTRL, 0x57}, + {AQT1000_MBHC_NEW_INT_MOISTURE_DET_POLLING_CTRL, 0x01}, + {AQT1000_MBHC_NEW_INT_MECH_DET_CURRENT, 0x00}, + {AQT1000_MBHC_NEW_INT_SPARE_2, 0x00}, + {AQT1000_PAGE10_PAGE_REGISTER, 0x00}, + {AQT1000_CDC_ANC0_CLK_RESET_CTL, 0x00}, + {AQT1000_CDC_ANC0_MODE_1_CTL, 0x00}, + {AQT1000_CDC_ANC0_MODE_2_CTL, 0x00}, + {AQT1000_CDC_ANC0_FF_SHIFT, 0x00}, + {AQT1000_CDC_ANC0_FB_SHIFT, 0x00}, + {AQT1000_CDC_ANC0_LPF_FF_A_CTL, 0x00}, + {AQT1000_CDC_ANC0_LPF_FF_B_CTL, 0x00}, + {AQT1000_CDC_ANC0_LPF_FB_CTL, 0x00}, + {AQT1000_CDC_ANC0_SMLPF_CTL, 0x00}, + {AQT1000_CDC_ANC0_DCFLT_SHIFT_CTL, 0x00}, + {AQT1000_CDC_ANC0_IIR_ADAPT_CTL, 0x00}, + {AQT1000_CDC_ANC0_IIR_COEFF_1_CTL, 0x00}, + {AQT1000_CDC_ANC0_IIR_COEFF_2_CTL, 0x00}, + {AQT1000_CDC_ANC0_FF_A_GAIN_CTL, 0x00}, + {AQT1000_CDC_ANC0_FF_B_GAIN_CTL, 0x00}, + {AQT1000_CDC_ANC0_FB_GAIN_CTL, 0x00}, + {AQT1000_CDC_ANC0_RC_COMMON_CTL, 0x00}, + {AQT1000_CDC_ANC0_FIFO_COMMON_CTL, 0x88}, + {AQT1000_CDC_ANC0_RC0_STATUS_FMIN_CNTR, 0x00}, + {AQT1000_CDC_ANC0_RC1_STATUS_FMIN_CNTR, 0x00}, + {AQT1000_CDC_ANC0_RC0_STATUS_FMAX_CNTR, 0x00}, + {AQT1000_CDC_ANC0_RC1_STATUS_FMAX_CNTR, 0x00}, + {AQT1000_CDC_ANC0_STATUS_FIFO, 0x00}, + {AQT1000_CDC_ANC1_CLK_RESET_CTL, 0x00}, + {AQT1000_CDC_ANC1_MODE_1_CTL, 0x00}, + {AQT1000_CDC_ANC1_MODE_2_CTL, 0x00}, + {AQT1000_CDC_ANC1_FF_SHIFT, 0x00}, + {AQT1000_CDC_ANC1_FB_SHIFT, 0x00}, + {AQT1000_CDC_ANC1_LPF_FF_A_CTL, 0x00}, + {AQT1000_CDC_ANC1_LPF_FF_B_CTL, 0x00}, + {AQT1000_CDC_ANC1_LPF_FB_CTL, 0x00}, + {AQT1000_CDC_ANC1_SMLPF_CTL, 0x00}, + {AQT1000_CDC_ANC1_DCFLT_SHIFT_CTL, 0x00}, + {AQT1000_CDC_ANC1_IIR_ADAPT_CTL, 0x00}, + {AQT1000_CDC_ANC1_IIR_COEFF_1_CTL, 0x00}, + {AQT1000_CDC_ANC1_IIR_COEFF_2_CTL, 0x00}, + {AQT1000_CDC_ANC1_FF_A_GAIN_CTL, 0x00}, + {AQT1000_CDC_ANC1_FF_B_GAIN_CTL, 0x00}, + {AQT1000_CDC_ANC1_FB_GAIN_CTL, 0x00}, + {AQT1000_CDC_ANC1_RC_COMMON_CTL, 0x00}, + {AQT1000_CDC_ANC1_FIFO_COMMON_CTL, 0x88}, + {AQT1000_CDC_ANC1_RC0_STATUS_FMIN_CNTR, 0x00}, + {AQT1000_CDC_ANC1_RC1_STATUS_FMIN_CNTR, 0x00}, + {AQT1000_CDC_ANC1_RC0_STATUS_FMAX_CNTR, 0x00}, + {AQT1000_CDC_ANC1_RC1_STATUS_FMAX_CNTR, 0x00}, + {AQT1000_CDC_ANC1_STATUS_FIFO, 0x00}, + {AQT1000_CDC_TX0_TX_PATH_CTL, 0x04}, + {AQT1000_CDC_TX0_TX_PATH_CFG0, 0x10}, + {AQT1000_CDC_TX0_TX_PATH_CFG1, 0x03}, + {AQT1000_CDC_TX0_TX_VOL_CTL, 0x00}, + {AQT1000_CDC_TX0_TX_PATH_SEC0, 0x00}, + {AQT1000_CDC_TX0_TX_PATH_SEC1, 0x00}, + {AQT1000_CDC_TX0_TX_PATH_SEC2, 0x01}, + {AQT1000_CDC_TX0_TX_PATH_SEC3, 0x3C}, + {AQT1000_CDC_TX0_TX_PATH_SEC4, 0x20}, + {AQT1000_CDC_TX0_TX_PATH_SEC5, 0x00}, + {AQT1000_CDC_TX0_TX_PATH_SEC6, 0x00}, + {AQT1000_CDC_TX1_TX_PATH_CTL, 0x04}, + {AQT1000_CDC_TX1_TX_PATH_CFG0, 0x10}, + {AQT1000_CDC_TX1_TX_PATH_CFG1, 0x03}, + {AQT1000_CDC_TX1_TX_VOL_CTL, 0x00}, + {AQT1000_CDC_TX1_TX_PATH_SEC0, 0x00}, + {AQT1000_CDC_TX1_TX_PATH_SEC1, 0x00}, + {AQT1000_CDC_TX1_TX_PATH_SEC2, 0x01}, + {AQT1000_CDC_TX1_TX_PATH_SEC3, 0x3C}, + {AQT1000_CDC_TX1_TX_PATH_SEC4, 0x20}, + {AQT1000_CDC_TX1_TX_PATH_SEC5, 0x00}, + {AQT1000_CDC_TX1_TX_PATH_SEC6, 0x00}, + {AQT1000_CDC_TX2_TX_PATH_CTL, 0x04}, + {AQT1000_CDC_TX2_TX_PATH_CFG0, 0x10}, + {AQT1000_CDC_TX2_TX_PATH_CFG1, 0x03}, + {AQT1000_CDC_TX2_TX_VOL_CTL, 0x00}, + {AQT1000_CDC_TX2_TX_PATH_SEC0, 0x00}, + {AQT1000_CDC_TX2_TX_PATH_SEC1, 0x00}, + {AQT1000_CDC_TX2_TX_PATH_SEC2, 0x01}, + {AQT1000_CDC_TX2_TX_PATH_SEC3, 0x3C}, + {AQT1000_CDC_TX2_TX_PATH_SEC4, 0x20}, + {AQT1000_CDC_TX2_TX_PATH_SEC5, 0x00}, + {AQT1000_CDC_TX2_TX_PATH_SEC6, 0x00}, + {AQT1000_CDC_TX2_TX_PATH_SEC7, 0x25}, + {AQT1000_PAGE11_PAGE_REGISTER, 0x00}, + {AQT1000_CDC_COMPANDER1_CTL0, 0x60}, + {AQT1000_CDC_COMPANDER1_CTL1, 0xDB}, + {AQT1000_CDC_COMPANDER1_CTL2, 0xFF}, + {AQT1000_CDC_COMPANDER1_CTL3, 0x35}, + {AQT1000_CDC_COMPANDER1_CTL4, 0xFF}, + {AQT1000_CDC_COMPANDER1_CTL5, 0x00}, + {AQT1000_CDC_COMPANDER1_CTL6, 0x01}, + {AQT1000_CDC_COMPANDER1_CTL7, 0x26}, + {AQT1000_CDC_COMPANDER2_CTL0, 0x60}, + {AQT1000_CDC_COMPANDER2_CTL1, 0xDB}, + {AQT1000_CDC_COMPANDER2_CTL2, 0xFF}, + {AQT1000_CDC_COMPANDER2_CTL3, 0x35}, + {AQT1000_CDC_COMPANDER2_CTL4, 0xFF}, + {AQT1000_CDC_COMPANDER2_CTL5, 0x00}, + {AQT1000_CDC_COMPANDER2_CTL6, 0x01}, + {AQT1000_CDC_COMPANDER2_CTL7, 0x26}, + {AQT1000_CDC_RX1_RX_PATH_CTL, 0x04}, + {AQT1000_CDC_RX1_RX_PATH_CFG0, 0x00}, + {AQT1000_CDC_RX1_RX_PATH_CFG1, 0x64}, + {AQT1000_CDC_RX1_RX_PATH_CFG2, 0x8F}, + {AQT1000_CDC_RX1_RX_VOL_CTL, 0x00}, + {AQT1000_CDC_RX1_RX_PATH_MIX_CTL, 0x04}, + {AQT1000_CDC_RX1_RX_PATH_MIX_CFG, 0x7E}, + {AQT1000_CDC_RX1_RX_VOL_MIX_CTL, 0x00}, + {AQT1000_CDC_RX1_RX_PATH_SEC0, 0xFC}, + {AQT1000_CDC_RX1_RX_PATH_SEC1, 0x08}, + {AQT1000_CDC_RX1_RX_PATH_SEC2, 0x00}, + {AQT1000_CDC_RX1_RX_PATH_SEC3, 0x00}, + {AQT1000_CDC_RX1_RX_PATH_SEC4, 0x00}, + {AQT1000_CDC_RX1_RX_PATH_SEC5, 0x00}, + {AQT1000_CDC_RX1_RX_PATH_SEC6, 0x00}, + {AQT1000_CDC_RX1_RX_PATH_SEC7, 0x00}, + {AQT1000_CDC_RX1_RX_PATH_MIX_SEC0, 0x08}, + {AQT1000_CDC_RX1_RX_PATH_MIX_SEC1, 0x00}, + {AQT1000_CDC_RX1_RX_PATH_DSMDEM_CTL, 0x00}, + {AQT1000_CDC_RX2_RX_PATH_CTL, 0x04}, + {AQT1000_CDC_RX2_RX_PATH_CFG0, 0x00}, + {AQT1000_CDC_RX2_RX_PATH_CFG1, 0x64}, + {AQT1000_CDC_RX2_RX_PATH_CFG2, 0x8F}, + {AQT1000_CDC_RX2_RX_VOL_CTL, 0x00}, + {AQT1000_CDC_RX2_RX_PATH_MIX_CTL, 0x04}, + {AQT1000_CDC_RX2_RX_PATH_MIX_CFG, 0x7E}, + {AQT1000_CDC_RX2_RX_VOL_MIX_CTL, 0x00}, + {AQT1000_CDC_RX2_RX_PATH_SEC0, 0xFC}, + {AQT1000_CDC_RX2_RX_PATH_SEC1, 0x08}, + {AQT1000_CDC_RX2_RX_PATH_SEC2, 0x00}, + {AQT1000_CDC_RX2_RX_PATH_SEC3, 0x00}, + {AQT1000_CDC_RX2_RX_PATH_SEC4, 0x00}, + {AQT1000_CDC_RX2_RX_PATH_SEC5, 0x00}, + {AQT1000_CDC_RX2_RX_PATH_SEC6, 0x00}, + {AQT1000_CDC_RX2_RX_PATH_SEC7, 0x00}, + {AQT1000_CDC_RX2_RX_PATH_MIX_SEC0, 0x08}, + {AQT1000_CDC_RX2_RX_PATH_MIX_SEC1, 0x00}, + {AQT1000_CDC_RX2_RX_PATH_DSMDEM_CTL, 0x00}, + {AQT1000_CDC_EQ_IIR0_PATH_CTL, 0x00}, + {AQT1000_CDC_EQ_IIR0_PATH_CFG0, 0x1F}, + {AQT1000_CDC_EQ_IIR0_PATH_CFG1, 0x00}, + {AQT1000_CDC_EQ_IIR0_PATH_CFG2, 0x00}, + {AQT1000_CDC_EQ_IIR0_PATH_CFG3, 0x00}, + {AQT1000_CDC_EQ_IIR0_COEF_CFG0, 0x00}, + {AQT1000_CDC_EQ_IIR0_COEF_CFG1, 0x00}, + {AQT1000_CDC_EQ_IIR1_PATH_CTL, 0x00}, + {AQT1000_CDC_EQ_IIR1_PATH_CFG0, 0x1F}, + {AQT1000_CDC_EQ_IIR1_PATH_CFG1, 0x00}, + {AQT1000_CDC_EQ_IIR1_PATH_CFG2, 0x00}, + {AQT1000_CDC_EQ_IIR1_PATH_CFG3, 0x00}, + {AQT1000_CDC_EQ_IIR1_COEF_CFG0, 0x00}, + {AQT1000_CDC_EQ_IIR1_COEF_CFG1, 0x00}, + {AQT1000_PAGE12_PAGE_REGISTER, 0x00}, + {AQT1000_CDC_CLSH_CRC, 0x00}, + {AQT1000_CDC_CLSH_DLY_CTRL, 0x03}, + {AQT1000_CDC_CLSH_DECAY_CTRL, 0x02}, + {AQT1000_CDC_CLSH_HPH_V_PA, 0x1C}, + {AQT1000_CDC_CLSH_EAR_V_PA, 0x39}, + {AQT1000_CDC_CLSH_HPH_V_HD, 0x0C}, + {AQT1000_CDC_CLSH_EAR_V_HD, 0x0C}, + {AQT1000_CDC_CLSH_K1_MSB, 0x01}, + {AQT1000_CDC_CLSH_K1_LSB, 0x00}, + {AQT1000_CDC_CLSH_K2_MSB, 0x00}, + {AQT1000_CDC_CLSH_K2_LSB, 0x80}, + {AQT1000_CDC_CLSH_IDLE_CTRL, 0x00}, + {AQT1000_CDC_CLSH_IDLE_HPH, 0x00}, + {AQT1000_CDC_CLSH_IDLE_EAR, 0x00}, + {AQT1000_CDC_CLSH_TEST0, 0x07}, + {AQT1000_CDC_CLSH_TEST1, 0x00}, + {AQT1000_CDC_CLSH_OVR_VREF, 0x00}, + {AQT1000_MIXING_ASRC0_CLK_RST_CTL, 0x00}, + {AQT1000_MIXING_ASRC0_CTL0, 0x00}, + {AQT1000_MIXING_ASRC0_CTL1, 0x00}, + {AQT1000_MIXING_ASRC0_FIFO_CTL, 0xA8}, + {AQT1000_MIXING_ASRC0_STATUS_FMIN_CNTR_LSB, 0x00}, + {AQT1000_MIXING_ASRC0_STATUS_FMIN_CNTR_MSB, 0x00}, + {AQT1000_MIXING_ASRC0_STATUS_FMAX_CNTR_LSB, 0x00}, + {AQT1000_MIXING_ASRC0_STATUS_FMAX_CNTR_MSB, 0x00}, + {AQT1000_MIXING_ASRC0_STATUS_FIFO, 0x00}, + {AQT1000_MIXING_ASRC1_CLK_RST_CTL, 0x00}, + {AQT1000_MIXING_ASRC1_CTL0, 0x00}, + {AQT1000_MIXING_ASRC1_CTL1, 0x00}, + {AQT1000_MIXING_ASRC1_FIFO_CTL, 0xA8}, + {AQT1000_MIXING_ASRC1_STATUS_FMIN_CNTR_LSB, 0x00}, + {AQT1000_MIXING_ASRC1_STATUS_FMIN_CNTR_MSB, 0x00}, + {AQT1000_MIXING_ASRC1_STATUS_FMAX_CNTR_LSB, 0x00}, + {AQT1000_MIXING_ASRC1_STATUS_FMAX_CNTR_MSB, 0x00}, + {AQT1000_MIXING_ASRC1_STATUS_FIFO, 0x00}, + {AQT1000_CDC_SIDETONE_SRC0_ST_SRC_PATH_CTL, 0x04}, + {AQT1000_CDC_SIDETONE_SRC0_ST_SRC_PATH_CFG1, 0x00}, + {AQT1000_SIDETONE_ASRC0_CLK_RST_CTL, 0x00}, + {AQT1000_SIDETONE_ASRC0_CTL0, 0x00}, + {AQT1000_SIDETONE_ASRC0_CTL1, 0x00}, + {AQT1000_SIDETONE_ASRC0_FIFO_CTL, 0xA8}, + {AQT1000_SIDETONE_ASRC0_STATUS_FMIN_CNTR_LSB, 0x00}, + {AQT1000_SIDETONE_ASRC0_STATUS_FMIN_CNTR_MSB, 0x00}, + {AQT1000_SIDETONE_ASRC0_STATUS_FMAX_CNTR_LSB, 0x00}, + {AQT1000_SIDETONE_ASRC0_STATUS_FMAX_CNTR_MSB, 0x00}, + {AQT1000_SIDETONE_ASRC0_STATUS_FIFO, 0x00}, + {AQT1000_EC_REF_HQ0_EC_REF_HQ_PATH_CTL, 0x00}, + {AQT1000_EC_REF_HQ0_EC_REF_HQ_CFG0, 0x01}, + {AQT1000_EC_REF_HQ1_EC_REF_HQ_PATH_CTL, 0x00}, + {AQT1000_EC_REF_HQ1_EC_REF_HQ_CFG0, 0x01}, + {AQT1000_EC_ASRC0_CLK_RST_CTL, 0x00}, + {AQT1000_EC_ASRC0_CTL0, 0x00}, + {AQT1000_EC_ASRC0_CTL1, 0x00}, + {AQT1000_EC_ASRC0_FIFO_CTL, 0xA8}, + {AQT1000_EC_ASRC0_STATUS_FMIN_CNTR_LSB, 0x00}, + {AQT1000_EC_ASRC0_STATUS_FMIN_CNTR_MSB, 0x00}, + {AQT1000_EC_ASRC0_STATUS_FMAX_CNTR_LSB, 0x00}, + {AQT1000_EC_ASRC0_STATUS_FMAX_CNTR_MSB, 0x00}, + {AQT1000_EC_ASRC0_STATUS_FIFO, 0x00}, + {AQT1000_EC_ASRC1_CLK_RST_CTL, 0x00}, + {AQT1000_EC_ASRC1_CTL0, 0x00}, + {AQT1000_EC_ASRC1_CTL1, 0x00}, + {AQT1000_EC_ASRC1_FIFO_CTL, 0xA8}, + {AQT1000_EC_ASRC1_STATUS_FMIN_CNTR_LSB, 0x00}, + {AQT1000_EC_ASRC1_STATUS_FMIN_CNTR_MSB, 0x00}, + {AQT1000_EC_ASRC1_STATUS_FMAX_CNTR_LSB, 0x00}, + {AQT1000_EC_ASRC1_STATUS_FMAX_CNTR_MSB, 0x00}, + {AQT1000_EC_ASRC1_STATUS_FIFO, 0x00}, + {AQT1000_PAGE13_PAGE_REGISTER, 0x00}, + {AQT1000_CDC_RX_INP_MUX_RX_INT1_CFG0, 0x00}, + {AQT1000_CDC_RX_INP_MUX_RX_INT1_CFG1, 0x00}, + {AQT1000_CDC_RX_INP_MUX_RX_INT2_CFG0, 0x00}, + {AQT1000_CDC_RX_INP_MUX_RX_INT2_CFG1, 0x00}, + {AQT1000_CDC_RX_INP_MUX_EQ_IIR_CFG0, 0x00}, + {AQT1000_CDC_RX_INP_MUX_DSD_CFG0, 0x00}, + {AQT1000_CDC_RX_INP_MUX_RX_MIX_CFG0, 0x00}, + {AQT1000_CDC_RX_INP_MUX_SIDETONE_SRC_CFG0, 0x00}, + {AQT1000_CDC_RX_INP_MUX_ANC_CFG0, 0x00}, + {AQT1000_CDC_RX_INP_MUX_SPLINE_ASRC_CFG0, 0x00}, + {AQT1000_CDC_RX_INP_MUX_EC_REF_HQ_CFG0, 0x00}, + {AQT1000_CDC_TX_INP_MUX_ADC_MUX0_CFG0, 0x00}, + {AQT1000_CDC_TX_INP_MUX_ADC_MUX0_CFG1, 0x00}, + {AQT1000_CDC_TX_INP_MUX_ADC_MUX1_CFG0, 0x00}, + {AQT1000_CDC_TX_INP_MUX_ADC_MUX1_CFG1, 0x00}, + {AQT1000_CDC_TX_INP_MUX_ADC_MUX2_CFG0, 0x00}, + {AQT1000_CDC_TX_INP_MUX_ADC_MUX2_CFG1, 0x00}, + {AQT1000_CDC_TX_INP_MUX_ADC_MUX10_CFG0, 0x00}, + {AQT1000_CDC_TX_INP_MUX_ADC_MUX10_CFG1, 0x00}, + {AQT1000_CDC_TX_INP_MUX_ADC_MUX11_CFG0, 0x00}, + {AQT1000_CDC_TX_INP_MUX_ADC_MUX11_CFG1, 0x00}, + {AQT1000_CDC_TX_INP_MUX_ADC_MUX12_CFG0, 0x00}, + {AQT1000_CDC_TX_INP_MUX_ADC_MUX12_CFG1, 0x00}, + {AQT1000_CDC_TX_INP_MUX_ADC_MUX13_CFG0, 0x00}, + {AQT1000_CDC_TX_INP_MUX_ADC_MUX13_CFG1, 0x00}, + {AQT1000_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG0, 0x00}, + {AQT1000_CDC_IF_ROUTER_TX_MUX_CFG0, 0x00}, + {AQT1000_CDC_CLK_RST_CTRL_MCLK_CONTROL, 0x00}, + {AQT1000_CDC_CLK_RST_CTRL_FS_CNT_CONTROL, 0x00}, + {AQT1000_CDC_CLK_RST_CTRL_DSD_CONTROL, 0x00}, + {AQT1000_CDC_CLK_RST_CTRL_ASRC_SHARE_CONTROL, 0x0F}, + {AQT1000_CDC_CLK_RST_CTRL_GFM_CONTROL, 0x00}, + {AQT1000_CDC_CLK_RST_CTRL_I2S_CONTROL, 0x00}, + {AQT1000_CDC_SIDETONE_IIR0_IIR_PATH_CTL, 0x00}, + {AQT1000_CDC_SIDETONE_IIR0_IIR_GAIN_B1_CTL, 0x00}, + {AQT1000_CDC_SIDETONE_IIR0_IIR_GAIN_B2_CTL, 0x00}, + {AQT1000_CDC_SIDETONE_IIR0_IIR_GAIN_B3_CTL, 0x00}, + {AQT1000_CDC_SIDETONE_IIR0_IIR_GAIN_B4_CTL, 0x00}, + {AQT1000_CDC_SIDETONE_IIR0_IIR_GAIN_B5_CTL, 0x00}, + {AQT1000_CDC_SIDETONE_IIR0_IIR_GAIN_B6_CTL, 0x00}, + {AQT1000_CDC_SIDETONE_IIR0_IIR_GAIN_B7_CTL, 0x00}, + {AQT1000_CDC_SIDETONE_IIR0_IIR_GAIN_B8_CTL, 0x00}, + {AQT1000_CDC_SIDETONE_IIR0_IIR_CTL, 0x40}, + {AQT1000_CDC_SIDETONE_IIR0_IIR_GAIN_TIMER_CTL, 0x00}, + {AQT1000_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL, 0x00}, + {AQT1000_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL, 0x00}, + {AQT1000_CDC_TOP_TOP_CFG0, 0x00}, + {AQT1000_CDC_TOP_HPHL_COMP_WR_LSB, 0x00}, + {AQT1000_CDC_TOP_HPHL_COMP_WR_MSB, 0x00}, + {AQT1000_CDC_TOP_HPHL_COMP_LUT, 0x00}, + {AQT1000_CDC_TOP_HPHL_COMP_RD_LSB, 0x00}, + {AQT1000_CDC_TOP_HPHL_COMP_RD_MSB, 0x00}, + {AQT1000_CDC_TOP_HPHR_COMP_WR_LSB, 0x00}, + {AQT1000_CDC_TOP_HPHR_COMP_WR_MSB, 0x00}, + {AQT1000_CDC_TOP_HPHR_COMP_LUT, 0x00}, + {AQT1000_CDC_TOP_HPHR_COMP_RD_LSB, 0x00}, + {AQT1000_CDC_TOP_HPHR_COMP_RD_MSB, 0x00}, + {AQT1000_CDC_DSD0_PATH_CTL, 0x00}, + {AQT1000_CDC_DSD0_CFG0, 0x00}, + {AQT1000_CDC_DSD0_CFG1, 0x00}, + {AQT1000_CDC_DSD0_CFG2, 0x42}, + {AQT1000_CDC_DSD0_CFG3, 0x00}, + {AQT1000_CDC_DSD0_CFG4, 0x02}, + {AQT1000_CDC_DSD0_CFG5, 0x00}, + {AQT1000_CDC_DSD1_PATH_CTL, 0x00}, + {AQT1000_CDC_DSD1_CFG0, 0x00}, + {AQT1000_CDC_DSD1_CFG1, 0x00}, + {AQT1000_CDC_DSD1_CFG2, 0x42}, + {AQT1000_CDC_DSD1_CFG3, 0x00}, + {AQT1000_CDC_DSD1_CFG4, 0x02}, + {AQT1000_CDC_DSD1_CFG5, 0x00}, + {AQT1000_CDC_RX_IDLE_DET_PATH_CTL, 0x00}, + {AQT1000_CDC_RX_IDLE_DET_CFG0, 0x07}, + {AQT1000_CDC_RX_IDLE_DET_CFG1, 0x3C}, + {AQT1000_CDC_RX_IDLE_DET_CFG2, 0x00}, + {AQT1000_CDC_RX_IDLE_DET_CFG3, 0x00}, + {AQT1000_CDC_DOP_DET_CTL, 0x00}, + {AQT1000_CDC_DOP_DET_CFG0, 0xFA}, + {AQT1000_CDC_DOP_DET_CFG1, 0x05}, + {AQT1000_CDC_DOP_DET_CFG2, 0x00}, + {AQT1000_CDC_DOP_DET_CFG3, 0x33}, + {AQT1000_CDC_DOP_DET_CFG4, 0x20}, + {AQT1000_CDC_DOP_DET_STATUS0, 0x00}, + {AQT1000_PAGE15_PAGE_REGISTER, 0x00}, + {AQT1000_CDC_DEBUG_DSD0_DEBUG_CFG0, 0x1B}, + {AQT1000_CDC_DEBUG_DSD0_DEBUG_CFG1, 0x24}, + {AQT1000_CDC_DEBUG_DSD0_DEBUG_CFG2, 0x00}, + {AQT1000_CDC_DEBUG_DSD0_DEBUG_CFG3, 0x08}, + {AQT1000_CDC_DEBUG_DSD1_DEBUG_CFG0, 0x1B}, + {AQT1000_CDC_DEBUG_DSD1_DEBUG_CFG1, 0x24}, + {AQT1000_CDC_DEBUG_DSD1_DEBUG_CFG2, 0x00}, + {AQT1000_CDC_DEBUG_DSD1_DEBUG_CFG3, 0x08}, + {AQT1000_CDC_DEBUG_RC_RE_ASRC_DEBUG_CFG0, 0x00}, + {AQT1000_CDC_DEBUG_ANC0_RC0_FIFO_CTL, 0x4C}, + {AQT1000_CDC_DEBUG_ANC0_RC1_FIFO_CTL, 0x4C}, + {AQT1000_CDC_DEBUG_ANC1_RC0_FIFO_CTL, 0x4C}, + {AQT1000_CDC_DEBUG_ANC1_RC1_FIFO_CTL, 0x4C}, + {AQT1000_CDC_DEBUG_ANC_RC_RST_DBG_CNTR, 0x00}, + {AQT1000_PAGE128_PAGE_REGISTER, 0x00}, + {AQT1000_TLMM_SPI_CLK_PINCFG, 0x00}, + {AQT1000_TLMM_SPI_MOSI_PINCFG, 0x00}, + {AQT1000_TLMM_SPI_MISO_PINCFG, 0x00}, + {AQT1000_TLMM_SPI_CS_N_PINCFG, 0x00}, + {AQT1000_TLMM_GPIO1_PINCFG, 0x00}, + {AQT1000_TLMM_GPIO2_PINCFG, 0x00}, + {AQT1000_TLMM_GPIO3_PINCFG, 0x00}, + {AQT1000_TLMM_GPIO4_PINCFG, 0x00}, + {AQT1000_TLMM_GPIO5_PINCFG, 0x00}, + {AQT1000_TLMM_GPIO6_PINCFG, 0x00}, + {AQT1000_TLMM_GPIO7_PINCFG, 0x00}, + {AQT1000_TLMM_GPIO8_PINCFG, 0x00}, + {AQT1000_TLMM_GPIO9_PINCFG, 0x00}, + {AQT1000_TLMM_GPIO10_PINCFG, 0x00}, + {AQT1000_PAD_CTRL_PAD_PDN_CTRL_0, 0x00}, + {AQT1000_PAD_CTRL_PAD_PDN_CTRL_1, 0x00}, + {AQT1000_PAD_CTRL_PAD_PU_CTRL_0, 0x00}, + {AQT1000_PAD_CTRL_PAD_PU_CTRL_1, 0x00}, + {AQT1000_PAD_CTRL_GPIO_CTL_0_OE, 0x00}, + {AQT1000_PAD_CTRL_GPIO_CTL_1_OE, 0x00}, + {AQT1000_PAD_CTRL_GPIO_CTL_0_DATA, 0x00}, + {AQT1000_PAD_CTRL_GPIO_CTL_1_DATA, 0x00}, + {AQT1000_PAD_CTRL_PAD_DRVCTL, 0x00}, + {AQT1000_PAD_CTRL_PIN_STATUS, 0x00}, + {AQT1000_PAD_CTRL_MEM_CTRL, 0x00}, + {AQT1000_PAD_CTRL_PAD_INP_DISABLE_0, 0x00}, + {AQT1000_PAD_CTRL_PAD_INP_DISABLE_1, 0x00}, + {AQT1000_PAD_CTRL_PIN_CTL_OE_0, 0x00}, + {AQT1000_PAD_CTRL_PIN_CTL_OE_1, 0x00}, + {AQT1000_PAD_CTRL_PIN_CTL_DATA_0, 0x00}, + {AQT1000_PAD_CTRL_PIN_CTL_DATA_1, 0x00}, + {AQT1000_PAD_CTRL_USB_PHY_CLK_DIV, 0x0F}, + {AQT1000_PAD_CTRL_DEBUG_BUS_CDC, 0x00}, + {AQT1000_PAD_CTRL_DEBUG_BUS_SEL, 0x00}, + {AQT1000_PAD_CTRL_DEBUG_EN_1, 0x00}, + {AQT1000_PAD_CTRL_DEBUG_EN_2, 0x00}, + {AQT1000_PAD_CTRL_DEBUG_EN_3, 0x00}, + {AQT1000_PAD_CTRL_DEBUG_EN_4, 0x00}, + {AQT1000_PAD_CTRL_DEBUG_EN_5, 0x00}, + {AQT1000_PAD_CTRL_DEBUG_MUX_BIT_0, 0x00}, + {AQT1000_PAD_CTRL_DEBUG_MUX_BIT_1, 0x01}, + {AQT1000_PAD_CTRL_DEBUG_MUX_BIT_2, 0x02}, + {AQT1000_PAD_CTRL_DEBUG_MUX_BIT_3, 0x03}, + {AQT1000_PAD_CTRL_DEBUG_MUX_BIT_4, 0x04}, + {AQT1000_PAD_CTRL_DEBUG_MUX_BIT_5, 0x05}, + {AQT1000_PAD_CTRL_DEBUG_MUX_BIT_6, 0x06}, + {AQT1000_PAD_CTRL_DEBUG_MUX_BIT_7, 0x07}, + {AQT1000_PAD_CTRL_DEBUG_MUX_BIT_8, 0x08}, + {AQT1000_PAD_CTRL_DEBUG_MUX_BIT_9, 0x09}, + {AQT1000_PAD_CTRL_DEBUG_MUX_BIT_10, 0x0A}, + {AQT1000_PAD_CTRL_DEBUG_MUX_BIT_11, 0x0B}, + {AQT1000_PAD_CTRL_DEBUG_MUX_BIT_12, 0x0C}, + {AQT1000_PAD_CTRL_DEBUG_MUX_BIT_13, 0x0D}, + {AQT1000_PAD_CTRL_DEBUG_READ_0, 0x0D}, + {AQT1000_PAD_CTRL_DEBUG_READ_1, 0x0D}, + {AQT1000_PAD_CTRL_DEBUG_READ_2, 0x0D}, + {AQT1000_PAD_CTRL_DEBUG_READ_3, 0x0D}, + {AQT1000_PAD_CTRL_FPGA_CTL, 0x00}, +}; + +const u8 aqt1000_page0_reg_access[AQT1000_PAGE_SIZE] = { + [AQT1000_REG(AQT1000_PAGE0_PAGE_REGISTER)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CHIP_CFG0_CHIP_ID_BYTE0)] = AQT1000_RO, + [AQT1000_REG(AQT1000_CHIP_CFG0_CHIP_ID_BYTE1)] = AQT1000_RO, + [AQT1000_REG(AQT1000_CHIP_CFG0_CHIP_ID_BYTE2)] = AQT1000_RO, + [AQT1000_REG(AQT1000_CHIP_CFG0_CHIP_ID_BYTE3)] = AQT1000_RO, + [AQT1000_REG(AQT1000_CHIP_CFG0_EFUSE_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CHIP_CFG0_EFUSE_TEST0)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CHIP_CFG0_EFUSE_TEST1)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CHIP_CFG0_EFUSE_VAL_OUT0)] = AQT1000_RO, + [AQT1000_REG(AQT1000_CHIP_CFG0_EFUSE_VAL_OUT1)] = AQT1000_RO, + [AQT1000_REG(AQT1000_CHIP_CFG0_EFUSE_VAL_OUT2)] = AQT1000_RO, + [AQT1000_REG(AQT1000_CHIP_CFG0_EFUSE_VAL_OUT3)] = AQT1000_RO, + [AQT1000_REG(AQT1000_CHIP_CFG0_EFUSE_VAL_OUT4)] = AQT1000_RO, + [AQT1000_REG(AQT1000_CHIP_CFG0_EFUSE_VAL_OUT5)] = AQT1000_RO, + [AQT1000_REG(AQT1000_CHIP_CFG0_EFUSE_VAL_OUT6)] = AQT1000_RO, + [AQT1000_REG(AQT1000_CHIP_CFG0_EFUSE_VAL_OUT7)] = AQT1000_RO, + [AQT1000_REG(AQT1000_CHIP_CFG0_EFUSE_VAL_OUT8)] = AQT1000_RO, + [AQT1000_REG(AQT1000_CHIP_CFG0_EFUSE_VAL_OUT9)] = AQT1000_RO, + [AQT1000_REG(AQT1000_CHIP_CFG0_EFUSE_VAL_OUT10)] = AQT1000_RO, + [AQT1000_REG(AQT1000_CHIP_CFG0_EFUSE_VAL_OUT11)] = AQT1000_RO, + [AQT1000_REG(AQT1000_CHIP_CFG0_EFUSE_VAL_OUT12)] = AQT1000_RO, + [AQT1000_REG(AQT1000_CHIP_CFG0_EFUSE_VAL_OUT13)] = AQT1000_RO, + [AQT1000_REG(AQT1000_CHIP_CFG0_EFUSE_VAL_OUT14)] = AQT1000_RO, + [AQT1000_REG(AQT1000_CHIP_CFG0_EFUSE_VAL_OUT15)] = AQT1000_RO, + [AQT1000_REG(AQT1000_CHIP_CFG0_EFUSE_STATUS)] = AQT1000_RO, + [AQT1000_REG(AQT1000_CHIP_CFG0_I2C_SLAVE_ID_NONNEGO)] = AQT1000_RO, + [AQT1000_REG(AQT1000_CHIP_CFG0_I2C_SLAVE_ID_1)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CHIP_CFG0_I2C_SLAVE_ID_2)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CHIP_CFG0_I2C_SLAVE_ID_3)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CHIP_CFG0_I2C_ACTIVE)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CHIP_CFG0_CLK_CFG_MCLK)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CHIP_CFG0_CLK_CFG_MCLK2)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CHIP_CFG0_CLK_CTL_CDC_DIG)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CHIP_CFG0_RST_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CHIP_CFG0_EFUSE2_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CHIP_CFG0_EFUSE2_TEST0)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CHIP_CFG0_EFUSE2_TEST1)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CHIP_CFG0_EFUSE2_VAL_OUT0)] = AQT1000_RO, + [AQT1000_REG(AQT1000_CHIP_CFG0_EFUSE2_VAL_OUT1)] = AQT1000_RO, + [AQT1000_REG(AQT1000_CHIP_CFG0_EFUSE2_VAL_OUT2)] = AQT1000_RO, + [AQT1000_REG(AQT1000_CHIP_CFG0_EFUSE2_VAL_OUT3)] = AQT1000_RO, + [AQT1000_REG(AQT1000_CHIP_CFG0_EFUSE2_VAL_OUT4)] = AQT1000_RO, + [AQT1000_REG(AQT1000_CHIP_CFG0_EFUSE2_VAL_OUT5)] = AQT1000_RO, + [AQT1000_REG(AQT1000_CHIP_CFG0_EFUSE2_VAL_OUT6)] = AQT1000_RO, + [AQT1000_REG(AQT1000_CHIP_CFG0_EFUSE2_VAL_OUT7)] = AQT1000_RO, + [AQT1000_REG(AQT1000_CHIP_CFG0_EFUSE2_VAL_OUT8)] = AQT1000_RO, + [AQT1000_REG(AQT1000_CHIP_CFG0_EFUSE2_VAL_OUT9)] = AQT1000_RO, + [AQT1000_REG(AQT1000_CHIP_CFG0_EFUSE2_VAL_OUT10)] = AQT1000_RO, + [AQT1000_REG(AQT1000_CHIP_CFG0_EFUSE2_VAL_OUT11)] = AQT1000_RO, + [AQT1000_REG(AQT1000_CHIP_CFG0_EFUSE2_VAL_OUT12)] = AQT1000_RO, + [AQT1000_REG(AQT1000_CHIP_CFG0_EFUSE2_VAL_OUT13)] = AQT1000_RO, + [AQT1000_REG(AQT1000_CHIP_CFG0_EFUSE2_VAL_OUT14)] = AQT1000_RO, + [AQT1000_REG(AQT1000_CHIP_CFG0_EFUSE2_VAL_OUT15)] = AQT1000_RO, + [AQT1000_REG(AQT1000_CHIP_CFG0_EFUSE2_STATUS)] = AQT1000_RO, + [AQT1000_REG(AQT1000_CHIP_CFG1_PWR_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CHIP_CFG1_BUS_MTRX_CFG)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CHIP_CFG1_DMA_BUS_VOTE)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CHIP_CFG1_USB_BUS_VOTE)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CHIP_CFG1_BLSP_BUS_VOTE)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CHIP_CFG1_PWR_MEM_SD)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CHIP_CFG1_PWR_SYS_MEM_SD_RAM)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CHIP_CFG1_PWR_SYS_MEM_SD_ROM)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CHIP_CFG1_PWR_SYS_MEM_FORCE_DS_RAM)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CHIP_CFG1_PWR_SYS_MEM_FORCE_DS_ROM)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CHIP_CFG1_CLK_CFG_FLL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CHIP_CFG1_CLK_CFG_SPI_M)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CHIP_CFG1_CLK_CFG_I2C_M)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CHIP_CFG1_CLK_CFG_UART)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CHIP_CFG1_RST_USB_SS)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CHIP_CFG1_RST_BLSP)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CHIP_CFG1_RST_BUS_MTRX)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CHIP_CFG1_RST_MISC)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CHIP_CFG1_ANA_WAIT_STATE_CTL)] = AQT1000_RW, +}; + +const u8 aqt1000_page1_reg_access[AQT1000_PAGE_SIZE] = { + [AQT1000_REG(AQT1000_PAGE1_PAGE_REGISTER)] = AQT1000_RW, + [AQT1000_REG(AQT1000_FLL_USER_CTL_0)] = AQT1000_RW, + [AQT1000_REG(AQT1000_FLL_USER_CTL_1)] = AQT1000_RW, + [AQT1000_REG(AQT1000_FLL_USER_CTL_2)] = AQT1000_RW, + [AQT1000_REG(AQT1000_FLL_USER_CTL_3)] = AQT1000_RW, + [AQT1000_REG(AQT1000_FLL_USER_CTL_4)] = AQT1000_RW, + [AQT1000_REG(AQT1000_FLL_USER_CTL_5)] = AQT1000_RW, + [AQT1000_REG(AQT1000_FLL_USER_CTL_6)] = AQT1000_RW, + [AQT1000_REG(AQT1000_FLL_USER_CTL_7)] = AQT1000_RW, + [AQT1000_REG(AQT1000_FLL_USER_CTL_8)] = AQT1000_RW, + [AQT1000_REG(AQT1000_FLL_USER_CTL_9)] = AQT1000_RW, + [AQT1000_REG(AQT1000_FLL_L_VAL_CTL_0)] = AQT1000_RW, + [AQT1000_REG(AQT1000_FLL_L_VAL_CTL_1)] = AQT1000_RW, + [AQT1000_REG(AQT1000_FLL_DSM_FRAC_CTL_0)] = AQT1000_RW, + [AQT1000_REG(AQT1000_FLL_DSM_FRAC_CTL_1)] = AQT1000_RW, + [AQT1000_REG(AQT1000_FLL_CONFIG_CTL_0)] = AQT1000_RW, + [AQT1000_REG(AQT1000_FLL_CONFIG_CTL_1)] = AQT1000_RW, + [AQT1000_REG(AQT1000_FLL_CONFIG_CTL_2)] = AQT1000_RW, + [AQT1000_REG(AQT1000_FLL_CONFIG_CTL_3)] = AQT1000_RW, + [AQT1000_REG(AQT1000_FLL_CONFIG_CTL_4)] = AQT1000_RW, + [AQT1000_REG(AQT1000_FLL_TEST_CTL_0)] = AQT1000_RW, + [AQT1000_REG(AQT1000_FLL_TEST_CTL_1)] = AQT1000_RW, + [AQT1000_REG(AQT1000_FLL_TEST_CTL_2)] = AQT1000_RW, + [AQT1000_REG(AQT1000_FLL_TEST_CTL_3)] = AQT1000_RW, + [AQT1000_REG(AQT1000_FLL_TEST_CTL_4)] = AQT1000_RW, + [AQT1000_REG(AQT1000_FLL_TEST_CTL_5)] = AQT1000_RW, + [AQT1000_REG(AQT1000_FLL_TEST_CTL_6)] = AQT1000_RW, + [AQT1000_REG(AQT1000_FLL_TEST_CTL_7)] = AQT1000_RW, + [AQT1000_REG(AQT1000_FLL_FREQ_CTL_0)] = AQT1000_RW, + [AQT1000_REG(AQT1000_FLL_FREQ_CTL_1)] = AQT1000_RW, + [AQT1000_REG(AQT1000_FLL_FREQ_CTL_2)] = AQT1000_RW, + [AQT1000_REG(AQT1000_FLL_FREQ_CTL_3)] = AQT1000_RW, + [AQT1000_REG(AQT1000_FLL_SSC_CTL_0)] = AQT1000_RW, + [AQT1000_REG(AQT1000_FLL_SSC_CTL_1)] = AQT1000_RW, + [AQT1000_REG(AQT1000_FLL_SSC_CTL_2)] = AQT1000_RW, + [AQT1000_REG(AQT1000_FLL_SSC_CTL_3)] = AQT1000_RW, + [AQT1000_REG(AQT1000_FLL_FLL_MODE)] = AQT1000_RW, + [AQT1000_REG(AQT1000_FLL_STATUS_0)] = AQT1000_RO, + [AQT1000_REG(AQT1000_FLL_STATUS_1)] = AQT1000_RO, + [AQT1000_REG(AQT1000_FLL_STATUS_2)] = AQT1000_RO, + [AQT1000_REG(AQT1000_FLL_STATUS_3)] = AQT1000_RO, +}; + +const u8 aqt1000_page2_reg_access[AQT1000_PAGE_SIZE] = { + [AQT1000_REG(AQT1000_PAGE2_PAGE_REGISTER)] = AQT1000_RW, + [AQT1000_REG(AQT1000_I2S_I2S_0_TX_CFG)] = AQT1000_RW, + [AQT1000_REG(AQT1000_I2S_I2S_0_RX_CFG)] = AQT1000_RW, + [AQT1000_REG(AQT1000_I2S_I2S_0_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_I2S_I2S_CLKSRC_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_I2S_I2S_HS_CLK_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_I2S_I2S_0_RST)] = AQT1000_RW, + [AQT1000_REG(AQT1000_I2S_SHADOW_I2S_0_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_I2S_SHADOW_I2S_0_RX_CFG)] = AQT1000_RW, +}; + +const u8 aqt1000_page5_reg_access[AQT1000_PAGE_SIZE] = { + [AQT1000_REG(AQT1000_PAGE5_PAGE_REGISTER)] = AQT1000_RW, + [AQT1000_REG(AQT1000_INTR_CTRL_MCU_INT_POLARITY)] = AQT1000_RW, + [AQT1000_REG(AQT1000_INTR_CTRL_INT_MASK_0)] = AQT1000_RW, + [AQT1000_REG(AQT1000_INTR_CTRL_INT_MASK_1)] = AQT1000_RW, + [AQT1000_REG(AQT1000_INTR_CTRL_INT_MASK_2)] = AQT1000_RW, + [AQT1000_REG(AQT1000_INTR_CTRL_INT_MASK_3)] = AQT1000_RW, + [AQT1000_REG(AQT1000_INTR_CTRL_INT_MASK_4)] = AQT1000_RW, + [AQT1000_REG(AQT1000_INTR_CTRL_INT_MASK_5)] = AQT1000_RW, + [AQT1000_REG(AQT1000_INTR_CTRL_INT_MASK_6)] = AQT1000_RW, + [AQT1000_REG(AQT1000_INTR_CTRL_INT_STATUS_0)] = AQT1000_RO, + [AQT1000_REG(AQT1000_INTR_CTRL_INT_STATUS_1)] = AQT1000_RO, + [AQT1000_REG(AQT1000_INTR_CTRL_INT_STATUS_2)] = AQT1000_RO, + [AQT1000_REG(AQT1000_INTR_CTRL_INT_STATUS_3)] = AQT1000_RO, + [AQT1000_REG(AQT1000_INTR_CTRL_INT_STATUS_4)] = AQT1000_RO, + [AQT1000_REG(AQT1000_INTR_CTRL_INT_STATUS_5)] = AQT1000_RO, + [AQT1000_REG(AQT1000_INTR_CTRL_INT_STATUS_6)] = AQT1000_RO, + [AQT1000_REG(AQT1000_INTR_CTRL_INT_CLEAR_0)] = AQT1000_WO, + [AQT1000_REG(AQT1000_INTR_CTRL_INT_CLEAR_1)] = AQT1000_WO, + [AQT1000_REG(AQT1000_INTR_CTRL_INT_CLEAR_2)] = AQT1000_WO, + [AQT1000_REG(AQT1000_INTR_CTRL_INT_CLEAR_3)] = AQT1000_WO, + [AQT1000_REG(AQT1000_INTR_CTRL_INT_CLEAR_4)] = AQT1000_WO, + [AQT1000_REG(AQT1000_INTR_CTRL_INT_CLEAR_5)] = AQT1000_WO, + [AQT1000_REG(AQT1000_INTR_CTRL_INT_CLEAR_6)] = AQT1000_WO, + [AQT1000_REG(AQT1000_INTR_CTRL_INT_TYPE_0)] = AQT1000_RW, + [AQT1000_REG(AQT1000_INTR_CTRL_INT_TYPE_1)] = AQT1000_RW, + [AQT1000_REG(AQT1000_INTR_CTRL_INT_TYPE_2)] = AQT1000_RW, + [AQT1000_REG(AQT1000_INTR_CTRL_INT_TYPE_3)] = AQT1000_RW, + [AQT1000_REG(AQT1000_INTR_CTRL_INT_TYPE_4)] = AQT1000_RW, + [AQT1000_REG(AQT1000_INTR_CTRL_INT_TYPE_5)] = AQT1000_RW, + [AQT1000_REG(AQT1000_INTR_CTRL_INT_TYPE_6)] = AQT1000_RW, + [AQT1000_REG(AQT1000_INTR_CTRL_INT_TEST_EN_0)] = AQT1000_RW, + [AQT1000_REG(AQT1000_INTR_CTRL_INT_TEST_EN_1)] = AQT1000_RW, + [AQT1000_REG(AQT1000_INTR_CTRL_INT_TEST_EN_2)] = AQT1000_RW, + [AQT1000_REG(AQT1000_INTR_CTRL_INT_TEST_EN_3)] = AQT1000_RW, + [AQT1000_REG(AQT1000_INTR_CTRL_INT_TEST_EN_4)] = AQT1000_RW, + [AQT1000_REG(AQT1000_INTR_CTRL_INT_TEST_EN_5)] = AQT1000_RW, + [AQT1000_REG(AQT1000_INTR_CTRL_INT_TEST_EN_6)] = AQT1000_RW, + [AQT1000_REG(AQT1000_INTR_CTRL_INT_TEST_VAL_0)] = AQT1000_RW, + [AQT1000_REG(AQT1000_INTR_CTRL_INT_TEST_VAL_1)] = AQT1000_RW, + [AQT1000_REG(AQT1000_INTR_CTRL_INT_TEST_VAL_2)] = AQT1000_RW, + [AQT1000_REG(AQT1000_INTR_CTRL_INT_TEST_VAL_3)] = AQT1000_RW, + [AQT1000_REG(AQT1000_INTR_CTRL_INT_TEST_VAL_4)] = AQT1000_RW, + [AQT1000_REG(AQT1000_INTR_CTRL_INT_TEST_VAL_5)] = AQT1000_RW, + [AQT1000_REG(AQT1000_INTR_CTRL_INT_TEST_VAL_6)] = AQT1000_RW, + [AQT1000_REG(AQT1000_INTR_CTRL_INT_DEST_0)] = AQT1000_RW, + [AQT1000_REG(AQT1000_INTR_CTRL_INT_DEST_1)] = AQT1000_RW, + [AQT1000_REG(AQT1000_INTR_CTRL_INT_DEST_2)] = AQT1000_RW, + [AQT1000_REG(AQT1000_INTR_CTRL_INT_DEST_3)] = AQT1000_RW, + [AQT1000_REG(AQT1000_INTR_CTRL_INT_DEST_4)] = AQT1000_RW, + [AQT1000_REG(AQT1000_INTR_CTRL_INT_DEST_5)] = AQT1000_RW, + [AQT1000_REG(AQT1000_INTR_CTRL_INT_DEST_6)] = AQT1000_RW, + [AQT1000_REG(AQT1000_INTR_CTRL_INT_DEST_7)] = AQT1000_RW, + [AQT1000_REG(AQT1000_INTR_CTRL_INT_DEST_8)] = AQT1000_RW, + [AQT1000_REG(AQT1000_INTR_CTRL_INT_DEST_9)] = AQT1000_RW, + [AQT1000_REG(AQT1000_INTR_CTRL_INT_DEST_10)] = AQT1000_RW, + [AQT1000_REG(AQT1000_INTR_CTRL_INT_DEST_11)] = AQT1000_RW, + [AQT1000_REG(AQT1000_INTR_CTRL_INT_DEST_12)] = AQT1000_RW, + [AQT1000_REG(AQT1000_INTR_CTRL_INT_DEST_13)] = AQT1000_RW, + [AQT1000_REG(AQT1000_INTR_CTRL_CLR_COMMIT)] = AQT1000_WO, +}; + +const u8 aqt1000_page6_reg_access[AQT1000_PAGE_SIZE] = { + [AQT1000_REG(AQT1000_ANA_PAGE_REGISTER)] = AQT1000_RW, + [AQT1000_REG(AQT1000_ANA_BIAS)] = AQT1000_RW, + [AQT1000_REG(AQT1000_ANA_RX_SUPPLIES)] = AQT1000_RW, + [AQT1000_REG(AQT1000_ANA_HPH)] = AQT1000_RW, + [AQT1000_REG(AQT1000_ANA_AMIC1)] = AQT1000_RW, + [AQT1000_REG(AQT1000_ANA_AMIC2)] = AQT1000_RW, + [AQT1000_REG(AQT1000_ANA_AMIC3)] = AQT1000_RW, + [AQT1000_REG(AQT1000_ANA_AMIC3_HPF)] = AQT1000_RW, + [AQT1000_REG(AQT1000_ANA_MBHC_MECH)] = AQT1000_RW, + [AQT1000_REG(AQT1000_ANA_MBHC_ELECT)] = AQT1000_RW, + [AQT1000_REG(AQT1000_ANA_MBHC_ZDET)] = AQT1000_RW, + [AQT1000_REG(AQT1000_ANA_MBHC_RESULT_1)] = AQT1000_RO, + [AQT1000_REG(AQT1000_ANA_MBHC_RESULT_2)] = AQT1000_RO, + [AQT1000_REG(AQT1000_ANA_MBHC_RESULT_3)] = AQT1000_RO, + [AQT1000_REG(AQT1000_ANA_MBHC_BTN0)] = AQT1000_RW, + [AQT1000_REG(AQT1000_ANA_MBHC_BTN1)] = AQT1000_RW, + [AQT1000_REG(AQT1000_ANA_MBHC_BTN2)] = AQT1000_RW, + [AQT1000_REG(AQT1000_ANA_MBHC_BTN3)] = AQT1000_RW, + [AQT1000_REG(AQT1000_ANA_MBHC_BTN4)] = AQT1000_RW, + [AQT1000_REG(AQT1000_ANA_MBHC_BTN5)] = AQT1000_RW, + [AQT1000_REG(AQT1000_ANA_MBHC_BTN6)] = AQT1000_RW, + [AQT1000_REG(AQT1000_ANA_MBHC_BTN7)] = AQT1000_RW, + [AQT1000_REG(AQT1000_ANA_MICB1)] = AQT1000_RW, + [AQT1000_REG(AQT1000_ANA_MICB1_RAMP)] = AQT1000_RW, + [AQT1000_REG(AQT1000_BIAS_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_BIAS_CCOMP_FINE_ADJ)] = AQT1000_RW, + [AQT1000_REG(AQT1000_LED_LED_MODE_SEL_R)] = AQT1000_RW, + [AQT1000_REG(AQT1000_LED_LED_MISC_R)] = AQT1000_RW, + [AQT1000_REG(AQT1000_LED_LED_MODE_SEL_G)] = AQT1000_RW, + [AQT1000_REG(AQT1000_LED_LED_MISC_G)] = AQT1000_RW, + [AQT1000_REG(AQT1000_LED_LED_MODE_SEL_B)] = AQT1000_RW, + [AQT1000_REG(AQT1000_LED_LED_MISC_B)] = AQT1000_RW, + [AQT1000_REG(AQT1000_LDOH_MODE)] = AQT1000_RW, + [AQT1000_REG(AQT1000_LDOH_BIAS)] = AQT1000_RW, + [AQT1000_REG(AQT1000_LDOH_STB_LOADS)] = AQT1000_RW, + [AQT1000_REG(AQT1000_LDOH_MISC1)] = AQT1000_RW, + [AQT1000_REG(AQT1000_LDOL_VDDCX_ADJUST)] = AQT1000_RW, + [AQT1000_REG(AQT1000_LDOL_DISABLE_LDOL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_BUCK_5V_EN_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_BUCK_5V_VOUT_SEL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_BUCK_5V_CTRL_VCL_1)] = AQT1000_RW, + [AQT1000_REG(AQT1000_BUCK_5V_CTRL_VCL_2)] = AQT1000_RW, + [AQT1000_REG(AQT1000_BUCK_5V_CTRL_CCL_2)] = AQT1000_RW, + [AQT1000_REG(AQT1000_BUCK_5V_CTRL_CCL_1)] = AQT1000_RW, + [AQT1000_REG(AQT1000_BUCK_5V_CTRL_CCL_3)] = AQT1000_RW, + [AQT1000_REG(AQT1000_BUCK_5V_CTRL_CCL_4)] = AQT1000_RW, + [AQT1000_REG(AQT1000_BUCK_5V_CTRL_CCL_5)] = AQT1000_RW, + [AQT1000_REG(AQT1000_BUCK_5V_IBIAS_CTL_1)] = AQT1000_RW, + [AQT1000_REG(AQT1000_BUCK_5V_IBIAS_CTL_2)] = AQT1000_RW, + [AQT1000_REG(AQT1000_BUCK_5V_IBIAS_CTL_3)] = AQT1000_RW, + [AQT1000_REG(AQT1000_BUCK_5V_IBIAS_CTL_4)] = AQT1000_RW, + [AQT1000_REG(AQT1000_BUCK_5V_IBIAS_CTL_5)] = AQT1000_RW, + [AQT1000_REG(AQT1000_BUCK_5V_ATEST_DTEST_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_PON_BG_CTRL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_PON_TEST_CTRL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_MBHC_CTL_CLK)] = AQT1000_RW, + [AQT1000_REG(AQT1000_MBHC_CTL_ANA)] = AQT1000_RW, + [AQT1000_REG(AQT1000_MBHC_CTL_SPARE_1)] = AQT1000_RW, + [AQT1000_REG(AQT1000_MBHC_CTL_SPARE_2)] = AQT1000_RW, + [AQT1000_REG(AQT1000_MBHC_CTL_BCS)] = AQT1000_RW, + [AQT1000_REG(AQT1000_MBHC_MOISTURE_DET_FSM_STATUS)] = AQT1000_RO, + [AQT1000_REG(AQT1000_MBHC_TEST_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_MICB1_TEST_CTL_1)] = AQT1000_RW, + [AQT1000_REG(AQT1000_MICB1_TEST_CTL_2)] = AQT1000_RW, + [AQT1000_REG(AQT1000_MICB1_TEST_CTL_3)] = AQT1000_RW, + [AQT1000_REG(AQT1000_MICB1_MISC_MICB1_INM_RES_BIAS)] = AQT1000_RW, + [AQT1000_REG(AQT1000_MICB1_MISC_MICB_MISC1)] = AQT1000_RW, + [AQT1000_REG(AQT1000_MICB1_MISC_MICB_MISC2)] = AQT1000_RW, + [AQT1000_REG(AQT1000_TX_COM_ADC_VCM)] = AQT1000_RW, + [AQT1000_REG(AQT1000_TX_COM_BIAS_ATEST)] = AQT1000_RW, + [AQT1000_REG(AQT1000_TX_COM_ADC_INT1_IB)] = AQT1000_RW, + [AQT1000_REG(AQT1000_TX_COM_ADC_INT2_IB)] = AQT1000_RW, + [AQT1000_REG(AQT1000_TX_COM_TXFE_DIV_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_TX_COM_TXFE_DIV_START)] = AQT1000_RW, + [AQT1000_REG(AQT1000_TX_COM_TXFE_DIV_STOP_9P6M)] = AQT1000_RW, + [AQT1000_REG(AQT1000_TX_COM_TXFE_DIV_STOP_12P288M)] = AQT1000_RW, + [AQT1000_REG(AQT1000_TX_1_2_TEST_EN)] = AQT1000_RW, + [AQT1000_REG(AQT1000_TX_1_2_ADC_IB)] = AQT1000_RW, + [AQT1000_REG(AQT1000_TX_1_2_ATEST_REFCTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_TX_1_2_TEST_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_TX_1_2_TEST_BLK_EN)] = AQT1000_RW, + [AQT1000_REG(AQT1000_TX_1_2_TXFE_CLKDIV)] = AQT1000_RW, + [AQT1000_REG(AQT1000_TX_1_2_SAR1_ERR)] = AQT1000_RO, + [AQT1000_REG(AQT1000_TX_1_2_SAR2_ERR)] = AQT1000_RO, + [AQT1000_REG(AQT1000_TX_3_TEST_EN)] = AQT1000_RW, + [AQT1000_REG(AQT1000_TX_3_ADC_IB)] = AQT1000_RW, + [AQT1000_REG(AQT1000_TX_3_ATEST_REFCTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_TX_3_TEST_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_TX_3_TEST_BLK_EN)] = AQT1000_RW, + [AQT1000_REG(AQT1000_TX_3_TXFE_CLKDIV)] = AQT1000_RW, + [AQT1000_REG(AQT1000_TX_3_SAR1_ERR)] = AQT1000_RO, + [AQT1000_REG(AQT1000_TX_3_SAR2_ERR)] = AQT1000_RO, + [AQT1000_REG(AQT1000_TX_ATEST1_2_SEL)] = AQT1000_RO, + [AQT1000_REG(AQT1000_CLASSH_MODE_1)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CLASSH_MODE_2)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CLASSH_MODE_3)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CLASSH_CTRL_VCL_1)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CLASSH_CTRL_VCL_2)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CLASSH_CTRL_CCL_1)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CLASSH_CTRL_CCL_2)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CLASSH_CTRL_CCL_3)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CLASSH_CTRL_CCL_4)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CLASSH_CTRL_CCL_5)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CLASSH_BUCK_TMUX_A_D)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CLASSH_BUCK_SW_DRV_CNTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CLASSH_SPARE)] = AQT1000_RW, + [AQT1000_REG(AQT1000_FLYBACK_EN)] = AQT1000_RW, + [AQT1000_REG(AQT1000_FLYBACK_VNEG_CTRL_1)] = AQT1000_RW, + [AQT1000_REG(AQT1000_FLYBACK_VNEG_CTRL_2)] = AQT1000_RW, + [AQT1000_REG(AQT1000_FLYBACK_VNEG_CTRL_3)] = AQT1000_RW, + [AQT1000_REG(AQT1000_FLYBACK_VNEG_CTRL_4)] = AQT1000_RW, + [AQT1000_REG(AQT1000_FLYBACK_VNEG_CTRL_5)] = AQT1000_RW, + [AQT1000_REG(AQT1000_FLYBACK_VNEG_CTRL_6)] = AQT1000_RW, + [AQT1000_REG(AQT1000_FLYBACK_VNEG_CTRL_7)] = AQT1000_RW, + [AQT1000_REG(AQT1000_FLYBACK_VNEG_CTRL_8)] = AQT1000_RW, + [AQT1000_REG(AQT1000_FLYBACK_VNEG_CTRL_9)] = AQT1000_RW, + [AQT1000_REG(AQT1000_FLYBACK_VNEGDAC_CTRL_1)] = AQT1000_RW, + [AQT1000_REG(AQT1000_FLYBACK_VNEGDAC_CTRL_2)] = AQT1000_RW, + [AQT1000_REG(AQT1000_FLYBACK_VNEGDAC_CTRL_3)] = AQT1000_RW, + [AQT1000_REG(AQT1000_FLYBACK_CTRL_1)] = AQT1000_RW, + [AQT1000_REG(AQT1000_FLYBACK_TEST_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_RX_AUX_SW_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_RX_PA_AUX_IN_CONN)] = AQT1000_RW, + [AQT1000_REG(AQT1000_RX_TIMER_DIV)] = AQT1000_RW, + [AQT1000_REG(AQT1000_RX_OCP_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_RX_OCP_COUNT)] = AQT1000_RW, + [AQT1000_REG(AQT1000_RX_BIAS_ATEST)] = AQT1000_RW, + [AQT1000_REG(AQT1000_RX_BIAS_MISC1)] = AQT1000_RW, + [AQT1000_REG(AQT1000_RX_BIAS_HPH_LDO)] = AQT1000_RW, + [AQT1000_REG(AQT1000_RX_BIAS_HPH_PA)] = AQT1000_RW, + [AQT1000_REG(AQT1000_RX_BIAS_HPH_RDACBUFF_CNP2)] = AQT1000_RW, + [AQT1000_REG(AQT1000_RX_BIAS_HPH_RDAC_LDO)] = AQT1000_RW, + [AQT1000_REG(AQT1000_RX_BIAS_HPH_CNP1)] = AQT1000_RW, + [AQT1000_REG(AQT1000_RX_BIAS_HPH_LOWPOWER)] = AQT1000_RW, + [AQT1000_REG(AQT1000_RX_BIAS_MISC2)] = AQT1000_RW, + [AQT1000_REG(AQT1000_RX_BIAS_MISC3)] = AQT1000_RW, + [AQT1000_REG(AQT1000_RX_BIAS_MISC4)] = AQT1000_RW, + [AQT1000_REG(AQT1000_RX_BIAS_MISC5)] = AQT1000_RW, + [AQT1000_REG(AQT1000_RX_BIAS_BUCK_RST)] = AQT1000_RW, + [AQT1000_REG(AQT1000_RX_BIAS_BUCK_VREF_ERRAMP)] = AQT1000_RW, + [AQT1000_REG(AQT1000_RX_BIAS_FLYB_ERRAMP)] = AQT1000_RW, + [AQT1000_REG(AQT1000_RX_BIAS_FLYB_BUFF)] = AQT1000_RW, + [AQT1000_REG(AQT1000_RX_BIAS_FLYB_MID_RST)] = AQT1000_RW, + [AQT1000_REG(AQT1000_HPH_L_STATUS)] = AQT1000_RO, + [AQT1000_REG(AQT1000_HPH_R_STATUS)] = AQT1000_RO, + [AQT1000_REG(AQT1000_HPH_CNP_EN)] = AQT1000_RW, + [AQT1000_REG(AQT1000_HPH_CNP_WG_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_HPH_CNP_WG_TIME)] = AQT1000_RW, + [AQT1000_REG(AQT1000_HPH_OCP_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_HPH_AUTO_CHOP)] = AQT1000_RW, + [AQT1000_REG(AQT1000_HPH_CHOP_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_HPH_PA_CTL1)] = AQT1000_RW, + [AQT1000_REG(AQT1000_HPH_PA_CTL2)] = AQT1000_RW, + [AQT1000_REG(AQT1000_HPH_L_EN)] = AQT1000_RW, + [AQT1000_REG(AQT1000_HPH_L_TEST)] = AQT1000_RW, + [AQT1000_REG(AQT1000_HPH_L_ATEST)] = AQT1000_RW, + [AQT1000_REG(AQT1000_HPH_R_EN)] = AQT1000_RW, + [AQT1000_REG(AQT1000_HPH_R_TEST)] = AQT1000_RW, + [AQT1000_REG(AQT1000_HPH_R_ATEST)] = AQT1000_RW, + [AQT1000_REG(AQT1000_HPH_RDAC_CLK_CTL1)] = AQT1000_RW, + [AQT1000_REG(AQT1000_HPH_RDAC_CLK_CTL2)] = AQT1000_RW, + [AQT1000_REG(AQT1000_HPH_RDAC_LDO_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_HPH_RDAC_CHOP_CLK_LP_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_HPH_REFBUFF_UHQA_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_HPH_REFBUFF_LP_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_HPH_L_DAC_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_HPH_R_DAC_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_HPHLR_SURGE_COMP_SEL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_HPHLR_SURGE_EN)] = AQT1000_RW, + [AQT1000_REG(AQT1000_HPHLR_SURGE_MISC1)] = AQT1000_RW, + [AQT1000_REG(AQT1000_HPHLR_SURGE_STATUS)] = AQT1000_RO, +}; + +const u8 aqt1000_page7_reg_access[AQT1000_PAGE_SIZE] = { + [AQT1000_REG(AQT1000_ANA_NEW_PAGE_REGISTER)] = AQT1000_RW, + [AQT1000_REG(AQT1000_HPH_NEW_ANA_HPH2)] = AQT1000_RW, + [AQT1000_REG(AQT1000_HPH_NEW_ANA_HPH3)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CLK_SYS_MCLK1_PRG)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CLK_SYS_MCLK2_I2S_HS_CLK_PRG)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CLK_SYS_XO_CAP_XTP)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CLK_SYS_XO_CAP_XTM)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CLK_SYS_PLL_ENABLES)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CLK_SYS_PLL_PRESET)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CLK_SYS_PLL_STATUS)] = AQT1000_RO, + [AQT1000_REG(AQT1000_MBHC_NEW_ELECT_REM_CLAMP_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_MBHC_NEW_CTL_1)] = AQT1000_RW, + [AQT1000_REG(AQT1000_MBHC_NEW_CTL_2)] = AQT1000_RW, + [AQT1000_REG(AQT1000_MBHC_NEW_PLUG_DETECT_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_MBHC_NEW_ZDET_ANA_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_MBHC_NEW_ZDET_RAMP_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_MBHC_NEW_FSM_STATUS)] = AQT1000_RO, + [AQT1000_REG(AQT1000_MBHC_NEW_ADC_RESULT)] = AQT1000_RO, + [AQT1000_REG(AQT1000_HPH_NEW_INT_RDAC_GAIN_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_HPH_NEW_INT_RDAC_HD2_CTL_L)] = AQT1000_RW, + [AQT1000_REG(AQT1000_HPH_NEW_INT_RDAC_VREF_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_HPH_NEW_INT_RDAC_OVERRIDE_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_HPH_NEW_INT_RDAC_HD2_CTL_R)] = AQT1000_RW, + [AQT1000_REG(AQT1000_HPH_NEW_INT_PA_MISC1)] = AQT1000_RW, + [AQT1000_REG(AQT1000_HPH_NEW_INT_PA_MISC2)] = AQT1000_RW, + [AQT1000_REG(AQT1000_HPH_NEW_INT_PA_RDAC_MISC)] = AQT1000_RW, + [AQT1000_REG(AQT1000_HPH_NEW_INT_HPH_TIMER1)] = AQT1000_RW, + [AQT1000_REG(AQT1000_HPH_NEW_INT_HPH_TIMER2)] = AQT1000_RW, + [AQT1000_REG(AQT1000_HPH_NEW_INT_HPH_TIMER3)] = AQT1000_RW, + [AQT1000_REG(AQT1000_HPH_NEW_INT_HPH_TIMER4)] = AQT1000_RW, + [AQT1000_REG(AQT1000_HPH_NEW_INT_PA_RDAC_MISC2)] = AQT1000_RW, + [AQT1000_REG(AQT1000_HPH_NEW_INT_PA_RDAC_MISC3)] = AQT1000_RW, + [AQT1000_REG(AQT1000_RX_NEW_INT_HPH_RDAC_BIAS_LOHIFI)] = AQT1000_RW, + [AQT1000_REG(AQT1000_RX_NEW_INT_HPH_RDAC_BIAS_ULP)] = AQT1000_RW, + [AQT1000_REG(AQT1000_RX_NEW_INT_HPH_RDAC_LDO_LP)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CLK_SYS_INT_CLK_TEST1)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CLK_SYS_INT_XO_TEST1)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CLK_SYS_INT_XO_TEST2)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CLK_SYS_INT_POST_DIV_REG0)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CLK_SYS_INT_POST_DIV_REG1)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CLK_SYS_INT_REF_DIV_REG0)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CLK_SYS_INT_REF_DIV_REG1)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CLK_SYS_INT_FILTER_REG0)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CLK_SYS_INT_FILTER_REG1)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CLK_SYS_INT_PLL_L_VAL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CLK_SYS_INT_PLL_M_VAL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CLK_SYS_INT_PLL_N_VAL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CLK_SYS_INT_TEST_REG0)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CLK_SYS_INT_PFD_CP_DSM_PROG)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CLK_SYS_INT_VCO_PROG)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CLK_SYS_INT_TEST_REG1)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CLK_SYS_INT_LDO_LOCK_CFG)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CLK_SYS_INT_DIG_LOCK_DET_CFG)] = AQT1000_RW, + [AQT1000_REG(AQT1000_MBHC_NEW_INT_MOISTURE_DET_DC_CTRL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_MBHC_NEW_INT_MOISTURE_DET_POLLING_CTRL)] = + AQT1000_RW, + [AQT1000_REG(AQT1000_MBHC_NEW_INT_MECH_DET_CURRENT)] = AQT1000_RW, + [AQT1000_REG(AQT1000_MBHC_NEW_INT_SPARE_2)] = AQT1000_RW, +}; + +const u8 aqt1000_page10_reg_access[AQT1000_PAGE_SIZE] = { + [AQT1000_REG(AQT1000_PAGE10_PAGE_REGISTER)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_ANC0_CLK_RESET_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_ANC0_MODE_1_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_ANC0_MODE_2_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_ANC0_FF_SHIFT)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_ANC0_FB_SHIFT)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_ANC0_LPF_FF_A_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_ANC0_LPF_FF_B_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_ANC0_LPF_FB_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_ANC0_SMLPF_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_ANC0_DCFLT_SHIFT_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_ANC0_IIR_ADAPT_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_ANC0_IIR_COEFF_1_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_ANC0_IIR_COEFF_2_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_ANC0_FF_A_GAIN_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_ANC0_FF_B_GAIN_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_ANC0_FB_GAIN_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_ANC0_RC_COMMON_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_ANC0_FIFO_COMMON_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_ANC0_RC0_STATUS_FMIN_CNTR)] = AQT1000_RO, + [AQT1000_REG(AQT1000_CDC_ANC0_RC1_STATUS_FMIN_CNTR)] = AQT1000_RO, + [AQT1000_REG(AQT1000_CDC_ANC0_RC0_STATUS_FMAX_CNTR)] = AQT1000_RO, + [AQT1000_REG(AQT1000_CDC_ANC0_RC1_STATUS_FMAX_CNTR)] = AQT1000_RO, + [AQT1000_REG(AQT1000_CDC_ANC0_STATUS_FIFO)] = AQT1000_RO, + [AQT1000_REG(AQT1000_CDC_ANC1_CLK_RESET_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_ANC1_MODE_1_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_ANC1_MODE_2_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_ANC1_FF_SHIFT)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_ANC1_FB_SHIFT)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_ANC1_LPF_FF_A_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_ANC1_LPF_FF_B_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_ANC1_LPF_FB_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_ANC1_SMLPF_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_ANC1_DCFLT_SHIFT_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_ANC1_IIR_ADAPT_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_ANC1_IIR_COEFF_1_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_ANC1_IIR_COEFF_2_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_ANC1_FF_A_GAIN_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_ANC1_FF_B_GAIN_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_ANC1_FB_GAIN_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_ANC1_RC_COMMON_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_ANC1_FIFO_COMMON_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_ANC1_RC0_STATUS_FMIN_CNTR)] = AQT1000_RO, + [AQT1000_REG(AQT1000_CDC_ANC1_RC1_STATUS_FMIN_CNTR)] = AQT1000_RO, + [AQT1000_REG(AQT1000_CDC_ANC1_RC0_STATUS_FMAX_CNTR)] = AQT1000_RO, + [AQT1000_REG(AQT1000_CDC_ANC1_RC1_STATUS_FMAX_CNTR)] = AQT1000_RO, + [AQT1000_REG(AQT1000_CDC_ANC1_STATUS_FIFO)] = AQT1000_RO, + [AQT1000_REG(AQT1000_CDC_TX0_TX_PATH_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_TX0_TX_PATH_CFG0)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_TX0_TX_PATH_CFG1)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_TX0_TX_VOL_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_TX0_TX_PATH_SEC0)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_TX0_TX_PATH_SEC1)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_TX0_TX_PATH_SEC2)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_TX0_TX_PATH_SEC3)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_TX0_TX_PATH_SEC4)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_TX0_TX_PATH_SEC5)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_TX0_TX_PATH_SEC6)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_TX1_TX_PATH_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_TX1_TX_PATH_CFG0)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_TX1_TX_PATH_CFG1)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_TX1_TX_VOL_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_TX1_TX_PATH_SEC0)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_TX1_TX_PATH_SEC1)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_TX1_TX_PATH_SEC2)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_TX1_TX_PATH_SEC3)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_TX1_TX_PATH_SEC4)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_TX1_TX_PATH_SEC5)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_TX1_TX_PATH_SEC6)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_TX2_TX_PATH_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_TX2_TX_PATH_CFG0)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_TX2_TX_PATH_CFG1)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_TX2_TX_VOL_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_TX2_TX_PATH_SEC0)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_TX2_TX_PATH_SEC1)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_TX2_TX_PATH_SEC2)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_TX2_TX_PATH_SEC3)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_TX2_TX_PATH_SEC4)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_TX2_TX_PATH_SEC5)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_TX2_TX_PATH_SEC6)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_TX2_TX_PATH_SEC7)] = AQT1000_RW, +}; + +const u8 aqt1000_page11_reg_access[AQT1000_PAGE_SIZE] = { + [AQT1000_REG(AQT1000_PAGE11_PAGE_REGISTER)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_COMPANDER1_CTL0)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_COMPANDER1_CTL1)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_COMPANDER1_CTL2)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_COMPANDER1_CTL3)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_COMPANDER1_CTL4)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_COMPANDER1_CTL5)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_COMPANDER1_CTL6)] = AQT1000_RO, + [AQT1000_REG(AQT1000_CDC_COMPANDER1_CTL7)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_COMPANDER2_CTL0)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_COMPANDER2_CTL1)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_COMPANDER2_CTL2)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_COMPANDER2_CTL3)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_COMPANDER2_CTL4)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_COMPANDER2_CTL5)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_COMPANDER2_CTL6)] = AQT1000_RO, + [AQT1000_REG(AQT1000_CDC_COMPANDER2_CTL7)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_RX1_RX_PATH_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_RX1_RX_PATH_CFG0)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_RX1_RX_PATH_CFG1)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_RX1_RX_PATH_CFG2)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_RX1_RX_VOL_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_RX1_RX_PATH_MIX_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_RX1_RX_PATH_MIX_CFG)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_RX1_RX_VOL_MIX_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_RX1_RX_PATH_SEC0)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_RX1_RX_PATH_SEC1)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_RX1_RX_PATH_SEC2)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_RX1_RX_PATH_SEC3)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_RX1_RX_PATH_SEC4)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_RX1_RX_PATH_SEC5)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_RX1_RX_PATH_SEC6)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_RX1_RX_PATH_SEC7)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_RX1_RX_PATH_MIX_SEC0)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_RX1_RX_PATH_MIX_SEC1)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_RX1_RX_PATH_DSMDEM_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_RX2_RX_PATH_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_RX2_RX_PATH_CFG0)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_RX2_RX_PATH_CFG1)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_RX2_RX_PATH_CFG2)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_RX2_RX_VOL_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_RX2_RX_PATH_MIX_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_RX2_RX_PATH_MIX_CFG)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_RX2_RX_VOL_MIX_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_RX2_RX_PATH_SEC0)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_RX2_RX_PATH_SEC1)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_RX2_RX_PATH_SEC2)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_RX2_RX_PATH_SEC3)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_RX2_RX_PATH_SEC4)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_RX2_RX_PATH_SEC5)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_RX2_RX_PATH_SEC6)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_RX2_RX_PATH_SEC7)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_RX2_RX_PATH_MIX_SEC0)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_RX2_RX_PATH_MIX_SEC1)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_RX2_RX_PATH_DSMDEM_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_EQ_IIR0_PATH_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_EQ_IIR0_PATH_CFG0)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_EQ_IIR0_PATH_CFG1)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_EQ_IIR0_PATH_CFG2)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_EQ_IIR0_PATH_CFG3)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_EQ_IIR0_COEF_CFG0)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_EQ_IIR0_COEF_CFG1)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_EQ_IIR1_PATH_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_EQ_IIR1_PATH_CFG0)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_EQ_IIR1_PATH_CFG1)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_EQ_IIR1_PATH_CFG2)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_EQ_IIR1_PATH_CFG3)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_EQ_IIR1_COEF_CFG0)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_EQ_IIR1_COEF_CFG1)] = AQT1000_RW, +}; + +const u8 aqt1000_page12_reg_access[AQT1000_PAGE_SIZE] = { + [AQT1000_REG(AQT1000_PAGE12_PAGE_REGISTER)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_CLSH_CRC)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_CLSH_DLY_CTRL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_CLSH_DECAY_CTRL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_CLSH_HPH_V_PA)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_CLSH_EAR_V_PA)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_CLSH_HPH_V_HD)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_CLSH_EAR_V_HD)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_CLSH_K1_MSB)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_CLSH_K1_LSB)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_CLSH_K2_MSB)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_CLSH_K2_LSB)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_CLSH_IDLE_CTRL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_CLSH_IDLE_HPH)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_CLSH_IDLE_EAR)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_CLSH_TEST0)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_CLSH_TEST1)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_CLSH_OVR_VREF)] = AQT1000_RW, + [AQT1000_REG(AQT1000_MIXING_ASRC0_CLK_RST_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_MIXING_ASRC0_CTL0)] = AQT1000_RW, + [AQT1000_REG(AQT1000_MIXING_ASRC0_CTL1)] = AQT1000_RW, + [AQT1000_REG(AQT1000_MIXING_ASRC0_FIFO_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_MIXING_ASRC0_STATUS_FMIN_CNTR_LSB)] = AQT1000_RO, + [AQT1000_REG(AQT1000_MIXING_ASRC0_STATUS_FMIN_CNTR_MSB)] = AQT1000_RO, + [AQT1000_REG(AQT1000_MIXING_ASRC0_STATUS_FMAX_CNTR_LSB)] = AQT1000_RO, + [AQT1000_REG(AQT1000_MIXING_ASRC0_STATUS_FMAX_CNTR_MSB)] = AQT1000_RO, + [AQT1000_REG(AQT1000_MIXING_ASRC0_STATUS_FIFO)] = AQT1000_RO, + [AQT1000_REG(AQT1000_MIXING_ASRC1_CLK_RST_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_MIXING_ASRC1_CTL0)] = AQT1000_RW, + [AQT1000_REG(AQT1000_MIXING_ASRC1_CTL1)] = AQT1000_RW, + [AQT1000_REG(AQT1000_MIXING_ASRC1_FIFO_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_MIXING_ASRC1_STATUS_FMIN_CNTR_LSB)] = AQT1000_RO, + [AQT1000_REG(AQT1000_MIXING_ASRC1_STATUS_FMIN_CNTR_MSB)] = AQT1000_RO, + [AQT1000_REG(AQT1000_MIXING_ASRC1_STATUS_FMAX_CNTR_LSB)] = AQT1000_RO, + [AQT1000_REG(AQT1000_MIXING_ASRC1_STATUS_FMAX_CNTR_MSB)] = AQT1000_RO, + [AQT1000_REG(AQT1000_MIXING_ASRC1_STATUS_FIFO)] = AQT1000_RO, + [AQT1000_REG(AQT1000_CDC_SIDETONE_SRC0_ST_SRC_PATH_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_SIDETONE_SRC0_ST_SRC_PATH_CFG1)] = AQT1000_RW, + [AQT1000_REG(AQT1000_SIDETONE_ASRC0_CLK_RST_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_SIDETONE_ASRC0_CTL0)] = AQT1000_RW, + [AQT1000_REG(AQT1000_SIDETONE_ASRC0_CTL1)] = AQT1000_RW, + [AQT1000_REG(AQT1000_SIDETONE_ASRC0_FIFO_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_SIDETONE_ASRC0_STATUS_FMIN_CNTR_LSB)] = AQT1000_RO, + [AQT1000_REG(AQT1000_SIDETONE_ASRC0_STATUS_FMIN_CNTR_MSB)] = AQT1000_RO, + [AQT1000_REG(AQT1000_SIDETONE_ASRC0_STATUS_FMAX_CNTR_LSB)] = AQT1000_RO, + [AQT1000_REG(AQT1000_SIDETONE_ASRC0_STATUS_FMAX_CNTR_MSB)] = AQT1000_RO, + [AQT1000_REG(AQT1000_SIDETONE_ASRC0_STATUS_FIFO)] = AQT1000_RO, + [AQT1000_REG(AQT1000_EC_REF_HQ0_EC_REF_HQ_PATH_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_EC_REF_HQ0_EC_REF_HQ_CFG0)] = AQT1000_RW, + [AQT1000_REG(AQT1000_EC_REF_HQ1_EC_REF_HQ_PATH_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_EC_REF_HQ1_EC_REF_HQ_CFG0)] = AQT1000_RW, + [AQT1000_REG(AQT1000_EC_ASRC0_CLK_RST_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_EC_ASRC0_CTL0)] = AQT1000_RW, + [AQT1000_REG(AQT1000_EC_ASRC0_CTL1)] = AQT1000_RW, + [AQT1000_REG(AQT1000_EC_ASRC0_FIFO_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_EC_ASRC0_STATUS_FMIN_CNTR_LSB)] = AQT1000_RO, + [AQT1000_REG(AQT1000_EC_ASRC0_STATUS_FMIN_CNTR_MSB)] = AQT1000_RO, + [AQT1000_REG(AQT1000_EC_ASRC0_STATUS_FMAX_CNTR_LSB)] = AQT1000_RO, + [AQT1000_REG(AQT1000_EC_ASRC0_STATUS_FMAX_CNTR_MSB)] = AQT1000_RO, + [AQT1000_REG(AQT1000_EC_ASRC0_STATUS_FIFO)] = AQT1000_RO, + [AQT1000_REG(AQT1000_EC_ASRC1_CLK_RST_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_EC_ASRC1_CTL0)] = AQT1000_RW, + [AQT1000_REG(AQT1000_EC_ASRC1_CTL1)] = AQT1000_RW, + [AQT1000_REG(AQT1000_EC_ASRC1_FIFO_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_EC_ASRC1_STATUS_FMIN_CNTR_LSB)] = AQT1000_RO, + [AQT1000_REG(AQT1000_EC_ASRC1_STATUS_FMIN_CNTR_MSB)] = AQT1000_RO, + [AQT1000_REG(AQT1000_EC_ASRC1_STATUS_FMAX_CNTR_LSB)] = AQT1000_RO, + [AQT1000_REG(AQT1000_EC_ASRC1_STATUS_FMAX_CNTR_MSB)] = AQT1000_RO, + [AQT1000_REG(AQT1000_EC_ASRC1_STATUS_FIFO)] = AQT1000_RO, +}; + +const u8 aqt1000_page13_reg_access[AQT1000_PAGE_SIZE] = { + [AQT1000_REG(AQT1000_PAGE13_PAGE_REGISTER)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_RX_INP_MUX_RX_INT1_CFG0)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_RX_INP_MUX_RX_INT1_CFG1)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_RX_INP_MUX_RX_INT2_CFG0)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_RX_INP_MUX_RX_INT2_CFG1)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_RX_INP_MUX_EQ_IIR_CFG0)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_RX_INP_MUX_DSD_CFG0)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_RX_INP_MUX_RX_MIX_CFG0)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_RX_INP_MUX_SIDETONE_SRC_CFG0)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_RX_INP_MUX_ANC_CFG0)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_RX_INP_MUX_SPLINE_ASRC_CFG0)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_RX_INP_MUX_EC_REF_HQ_CFG0)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_TX_INP_MUX_ADC_MUX0_CFG0)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_TX_INP_MUX_ADC_MUX0_CFG1)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_TX_INP_MUX_ADC_MUX1_CFG0)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_TX_INP_MUX_ADC_MUX1_CFG1)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_TX_INP_MUX_ADC_MUX2_CFG0)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_TX_INP_MUX_ADC_MUX2_CFG1)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_TX_INP_MUX_ADC_MUX10_CFG0)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_TX_INP_MUX_ADC_MUX10_CFG1)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_TX_INP_MUX_ADC_MUX11_CFG0)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_TX_INP_MUX_ADC_MUX11_CFG1)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_TX_INP_MUX_ADC_MUX12_CFG0)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_TX_INP_MUX_ADC_MUX12_CFG1)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_TX_INP_MUX_ADC_MUX13_CFG0)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_TX_INP_MUX_ADC_MUX13_CFG1)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG0)] = + AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_IF_ROUTER_TX_MUX_CFG0)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_CLK_RST_CTRL_MCLK_CONTROL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_CLK_RST_CTRL_FS_CNT_CONTROL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_CLK_RST_CTRL_DSD_CONTROL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_CLK_RST_CTRL_ASRC_SHARE_CONTROL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_CLK_RST_CTRL_GFM_CONTROL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_CLK_RST_CTRL_I2S_CONTROL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_SIDETONE_IIR0_IIR_PATH_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_SIDETONE_IIR0_IIR_GAIN_B1_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_SIDETONE_IIR0_IIR_GAIN_B2_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_SIDETONE_IIR0_IIR_GAIN_B3_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_SIDETONE_IIR0_IIR_GAIN_B4_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_SIDETONE_IIR0_IIR_GAIN_B5_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_SIDETONE_IIR0_IIR_GAIN_B6_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_SIDETONE_IIR0_IIR_GAIN_B7_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_SIDETONE_IIR0_IIR_GAIN_B8_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_SIDETONE_IIR0_IIR_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_SIDETONE_IIR0_IIR_GAIN_TIMER_CTL)] = + AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_TOP_TOP_CFG0)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_TOP_HPHL_COMP_WR_LSB)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_TOP_HPHL_COMP_WR_MSB)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_TOP_HPHL_COMP_LUT)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_TOP_HPHL_COMP_RD_LSB)] = AQT1000_RO, + [AQT1000_REG(AQT1000_CDC_TOP_HPHL_COMP_RD_MSB)] = AQT1000_RO, + [AQT1000_REG(AQT1000_CDC_TOP_HPHR_COMP_WR_LSB)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_TOP_HPHR_COMP_WR_MSB)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_TOP_HPHR_COMP_LUT)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_TOP_HPHR_COMP_RD_LSB)] = AQT1000_RO, + [AQT1000_REG(AQT1000_CDC_TOP_HPHR_COMP_RD_MSB)] = AQT1000_RO, + [AQT1000_REG(AQT1000_CDC_DSD0_PATH_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_DSD0_CFG0)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_DSD0_CFG1)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_DSD0_CFG2)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_DSD0_CFG3)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_DSD0_CFG4)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_DSD0_CFG5)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_DSD1_PATH_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_DSD1_CFG0)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_DSD1_CFG1)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_DSD1_CFG2)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_DSD1_CFG3)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_DSD1_CFG4)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_DSD1_CFG5)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_RX_IDLE_DET_PATH_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_RX_IDLE_DET_CFG0)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_RX_IDLE_DET_CFG1)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_RX_IDLE_DET_CFG2)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_RX_IDLE_DET_CFG3)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_DOP_DET_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_DOP_DET_CFG0)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_DOP_DET_CFG1)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_DOP_DET_CFG2)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_DOP_DET_CFG3)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_DOP_DET_CFG4)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_DOP_DET_STATUS0)] = AQT1000_RO, +}; + +const u8 aqt1000_page15_reg_access[AQT1000_PAGE_SIZE] = { + [AQT1000_REG(AQT1000_PAGE15_PAGE_REGISTER)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_DEBUG_DSD0_DEBUG_CFG0)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_DEBUG_DSD0_DEBUG_CFG1)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_DEBUG_DSD0_DEBUG_CFG2)] = AQT1000_RO, + [AQT1000_REG(AQT1000_CDC_DEBUG_DSD0_DEBUG_CFG3)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_DEBUG_DSD1_DEBUG_CFG0)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_DEBUG_DSD1_DEBUG_CFG1)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_DEBUG_DSD1_DEBUG_CFG2)] = AQT1000_RO, + [AQT1000_REG(AQT1000_CDC_DEBUG_DSD1_DEBUG_CFG3)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_DEBUG_RC_RE_ASRC_DEBUG_CFG0)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_DEBUG_ANC0_RC0_FIFO_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_DEBUG_ANC0_RC1_FIFO_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_DEBUG_ANC1_RC0_FIFO_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_DEBUG_ANC1_RC1_FIFO_CTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_CDC_DEBUG_ANC_RC_RST_DBG_CNTR)] = AQT1000_RW, +}; + +const u8 aqt1000_page128_reg_access[AQT1000_PAGE_SIZE] = { + [AQT1000_REG(AQT1000_PAGE128_PAGE_REGISTER)] = AQT1000_RW, + [AQT1000_REG(AQT1000_TLMM_SPI_CLK_PINCFG)] = AQT1000_RW, + [AQT1000_REG(AQT1000_TLMM_SPI_MOSI_PINCFG)] = AQT1000_RW, + [AQT1000_REG(AQT1000_TLMM_SPI_MISO_PINCFG)] = AQT1000_RW, + [AQT1000_REG(AQT1000_TLMM_SPI_CS_N_PINCFG)] = AQT1000_RW, + [AQT1000_REG(AQT1000_TLMM_GPIO1_PINCFG)] = AQT1000_RW, + [AQT1000_REG(AQT1000_TLMM_GPIO2_PINCFG)] = AQT1000_RW, + [AQT1000_REG(AQT1000_TLMM_GPIO3_PINCFG)] = AQT1000_RW, + [AQT1000_REG(AQT1000_TLMM_GPIO4_PINCFG)] = AQT1000_RW, + [AQT1000_REG(AQT1000_TLMM_GPIO5_PINCFG)] = AQT1000_RW, + [AQT1000_REG(AQT1000_TLMM_GPIO6_PINCFG)] = AQT1000_RW, + [AQT1000_REG(AQT1000_TLMM_GPIO7_PINCFG)] = AQT1000_RW, + [AQT1000_REG(AQT1000_TLMM_GPIO8_PINCFG)] = AQT1000_RW, + [AQT1000_REG(AQT1000_TLMM_GPIO9_PINCFG)] = AQT1000_RW, + [AQT1000_REG(AQT1000_TLMM_GPIO10_PINCFG)] = AQT1000_RW, + [AQT1000_REG(AQT1000_PAD_CTRL_PAD_PDN_CTRL_0)] = AQT1000_RW, + [AQT1000_REG(AQT1000_PAD_CTRL_PAD_PDN_CTRL_1)] = AQT1000_RW, + [AQT1000_REG(AQT1000_PAD_CTRL_PAD_PU_CTRL_0)] = AQT1000_RW, + [AQT1000_REG(AQT1000_PAD_CTRL_PAD_PU_CTRL_1)] = AQT1000_RW, + [AQT1000_REG(AQT1000_PAD_CTRL_GPIO_CTL_0_OE)] = AQT1000_RW, + [AQT1000_REG(AQT1000_PAD_CTRL_GPIO_CTL_1_OE)] = AQT1000_RW, + [AQT1000_REG(AQT1000_PAD_CTRL_GPIO_CTL_0_DATA)] = AQT1000_RW, + [AQT1000_REG(AQT1000_PAD_CTRL_GPIO_CTL_1_DATA)] = AQT1000_RW, + [AQT1000_REG(AQT1000_PAD_CTRL_PAD_DRVCTL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_PAD_CTRL_PIN_STATUS)] = AQT1000_RO, + [AQT1000_REG(AQT1000_PAD_CTRL_MEM_CTRL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_PAD_CTRL_PAD_INP_DISABLE_0)] = AQT1000_RW, + [AQT1000_REG(AQT1000_PAD_CTRL_PAD_INP_DISABLE_1)] = AQT1000_RW, + [AQT1000_REG(AQT1000_PAD_CTRL_PIN_CTL_OE_0)] = AQT1000_RW, + [AQT1000_REG(AQT1000_PAD_CTRL_PIN_CTL_OE_1)] = AQT1000_RW, + [AQT1000_REG(AQT1000_PAD_CTRL_PIN_CTL_DATA_0)] = AQT1000_RW, + [AQT1000_REG(AQT1000_PAD_CTRL_PIN_CTL_DATA_1)] = AQT1000_RW, + [AQT1000_REG(AQT1000_PAD_CTRL_USB_PHY_CLK_DIV)] = AQT1000_RW, + [AQT1000_REG(AQT1000_PAD_CTRL_DEBUG_BUS_CDC)] = AQT1000_RW, + [AQT1000_REG(AQT1000_PAD_CTRL_DEBUG_BUS_SEL)] = AQT1000_RW, + [AQT1000_REG(AQT1000_PAD_CTRL_DEBUG_EN_1)] = AQT1000_RW, + [AQT1000_REG(AQT1000_PAD_CTRL_DEBUG_EN_2)] = AQT1000_RW, + [AQT1000_REG(AQT1000_PAD_CTRL_DEBUG_EN_3)] = AQT1000_RW, + [AQT1000_REG(AQT1000_PAD_CTRL_DEBUG_EN_4)] = AQT1000_RW, + [AQT1000_REG(AQT1000_PAD_CTRL_DEBUG_EN_5)] = AQT1000_RW, + [AQT1000_REG(AQT1000_PAD_CTRL_DEBUG_MUX_BIT_0)] = AQT1000_RW, + [AQT1000_REG(AQT1000_PAD_CTRL_DEBUG_MUX_BIT_1)] = AQT1000_RW, + [AQT1000_REG(AQT1000_PAD_CTRL_DEBUG_MUX_BIT_2)] = AQT1000_RW, + [AQT1000_REG(AQT1000_PAD_CTRL_DEBUG_MUX_BIT_3)] = AQT1000_RW, + [AQT1000_REG(AQT1000_PAD_CTRL_DEBUG_MUX_BIT_4)] = AQT1000_RW, + [AQT1000_REG(AQT1000_PAD_CTRL_DEBUG_MUX_BIT_5)] = AQT1000_RW, + [AQT1000_REG(AQT1000_PAD_CTRL_DEBUG_MUX_BIT_6)] = AQT1000_RW, + [AQT1000_REG(AQT1000_PAD_CTRL_DEBUG_MUX_BIT_7)] = AQT1000_RW, + [AQT1000_REG(AQT1000_PAD_CTRL_DEBUG_MUX_BIT_8)] = AQT1000_RW, + [AQT1000_REG(AQT1000_PAD_CTRL_DEBUG_MUX_BIT_9)] = AQT1000_RW, + [AQT1000_REG(AQT1000_PAD_CTRL_DEBUG_MUX_BIT_10)] = AQT1000_RW, + [AQT1000_REG(AQT1000_PAD_CTRL_DEBUG_MUX_BIT_11)] = AQT1000_RW, + [AQT1000_REG(AQT1000_PAD_CTRL_DEBUG_MUX_BIT_12)] = AQT1000_RW, + [AQT1000_REG(AQT1000_PAD_CTRL_DEBUG_MUX_BIT_13)] = AQT1000_RW, + [AQT1000_REG(AQT1000_PAD_CTRL_DEBUG_READ_0)] = AQT1000_RO, + [AQT1000_REG(AQT1000_PAD_CTRL_DEBUG_READ_1)] = AQT1000_RO, + [AQT1000_REG(AQT1000_PAD_CTRL_DEBUG_READ_2)] = AQT1000_RO, + [AQT1000_REG(AQT1000_PAD_CTRL_DEBUG_READ_3)] = AQT1000_RO, + [AQT1000_REG(AQT1000_PAD_CTRL_FPGA_CTL)] = AQT1000_RW, +}; + +const u8 * const aqt1000_reg[AQT1000_PAGE_MAX] = { + [AQT1000_PAGE_0] = aqt1000_page0_reg_access, + [AQT1000_PAGE_1] = aqt1000_page1_reg_access, + [AQT1000_PAGE_2] = aqt1000_page2_reg_access, + [AQT1000_PAGE_5] = aqt1000_page5_reg_access, + [AQT1000_PAGE_6] = aqt1000_page6_reg_access, + [AQT1000_PAGE_7] = aqt1000_page7_reg_access, + [AQT1000_PAGE_10] = aqt1000_page10_reg_access, + [AQT1000_PAGE_11] = aqt1000_page11_reg_access, + [AQT1000_PAGE_12] = aqt1000_page12_reg_access, + [AQT1000_PAGE_13] = aqt1000_page13_reg_access, + [AQT1000_PAGE_15] = aqt1000_page15_reg_access, + [AQT1000_PAGE_128] = aqt1000_page128_reg_access, +}; + +#endif /* _AQT1000_REG_DEFAULTS_H */ diff --git a/audio-kernel/asoc/codecs/aqt1000/aqt1000-registers.h b/audio-kernel/asoc/codecs/aqt1000/aqt1000-registers.h new file mode 100644 index 000000000..903346665 --- /dev/null +++ b/audio-kernel/asoc/codecs/aqt1000/aqt1000-registers.h @@ -0,0 +1,881 @@ +/* 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 _AQT1000_REGISTERS_H +#define _AQT1000_REGISTERS_H + +#define AQT1000_PAGE0_BASE (0x00000000) +#define AQT1000_PAGE0_PAGE_REGISTER (0x00000000) +#define AQT1000_CHIP_CFG0_BASE (0x00000001) +#define AQT1000_CHIP_CFG0_CHIP_ID_BYTE0 (0x00000001) +#define AQT1000_CHIP_CFG0_CHIP_ID_BYTE1 (0x00000002) +#define AQT1000_CHIP_CFG0_CHIP_ID_BYTE2 (0x00000003) +#define AQT1000_CHIP_CFG0_CHIP_ID_BYTE3 (0x00000004) +#define AQT1000_CHIP_CFG0_EFUSE_CTL (0x00000005) +#define AQT1000_CHIP_CFG0_EFUSE_TEST0 (0x00000006) +#define AQT1000_CHIP_CFG0_EFUSE_TEST1 (0x00000007) +#define AQT1000_CHIP_CFG0_EFUSE_VAL_OUT0 (0x00000009) +#define AQT1000_CHIP_CFG0_EFUSE_VAL_OUT1 (0x0000000A) +#define AQT1000_CHIP_CFG0_EFUSE_VAL_OUT2 (0x0000000B) +#define AQT1000_CHIP_CFG0_EFUSE_VAL_OUT3 (0x0000000C) +#define AQT1000_CHIP_CFG0_EFUSE_VAL_OUT4 (0x0000000D) +#define AQT1000_CHIP_CFG0_EFUSE_VAL_OUT5 (0x0000000E) +#define AQT1000_CHIP_CFG0_EFUSE_VAL_OUT6 (0x0000000F) +#define AQT1000_CHIP_CFG0_EFUSE_VAL_OUT7 (0x00000010) +#define AQT1000_CHIP_CFG0_EFUSE_VAL_OUT8 (0x00000011) +#define AQT1000_CHIP_CFG0_EFUSE_VAL_OUT9 (0x00000012) +#define AQT1000_CHIP_CFG0_EFUSE_VAL_OUT10 (0x00000013) +#define AQT1000_CHIP_CFG0_EFUSE_VAL_OUT11 (0x00000014) +#define AQT1000_CHIP_CFG0_EFUSE_VAL_OUT12 (0x00000015) +#define AQT1000_CHIP_CFG0_EFUSE_VAL_OUT13 (0x00000016) +#define AQT1000_CHIP_CFG0_EFUSE_VAL_OUT14 (0x00000017) +#define AQT1000_CHIP_CFG0_EFUSE_VAL_OUT15 (0x00000018) +#define AQT1000_CHIP_CFG0_EFUSE_STATUS (0x00000019) +#define AQT1000_CHIP_CFG0_I2C_SLAVE_ID_NONNEGO (0x0000001A) +#define AQT1000_CHIP_CFG0_I2C_SLAVE_ID_1 (0x0000001B) +#define AQT1000_CHIP_CFG0_I2C_SLAVE_ID_2 (0x0000001C) +#define AQT1000_CHIP_CFG0_I2C_SLAVE_ID_3 (0x0000001D) +#define AQT1000_CHIP_CFG0_I2C_ACTIVE (0x00000020) +#define AQT1000_CHIP_CFG0_CLK_CFG_MCLK (0x00000021) +#define AQT1000_CHIP_CFG0_CLK_CFG_MCLK2 (0x00000022) +#define AQT1000_CHIP_CFG0_CLK_CTL_CDC_DIG (0x00000023) +#define AQT1000_CHIP_CFG0_RST_CTL (0x00000032) +#define AQT1000_CHIP_CFG0_EFUSE2_CTL (0x0000003D) +#define AQT1000_CHIP_CFG0_EFUSE2_TEST0 (0x0000003E) +#define AQT1000_CHIP_CFG0_EFUSE2_TEST1 (0x0000003F) +#define AQT1000_CHIP_CFG0_EFUSE2_VAL_OUT0 (0x00000040) +#define AQT1000_CHIP_CFG0_EFUSE2_VAL_OUT1 (0x00000041) +#define AQT1000_CHIP_CFG0_EFUSE2_VAL_OUT2 (0x00000042) +#define AQT1000_CHIP_CFG0_EFUSE2_VAL_OUT3 (0x00000043) +#define AQT1000_CHIP_CFG0_EFUSE2_VAL_OUT4 (0x00000044) +#define AQT1000_CHIP_CFG0_EFUSE2_VAL_OUT5 (0x00000045) +#define AQT1000_CHIP_CFG0_EFUSE2_VAL_OUT6 (0x00000046) +#define AQT1000_CHIP_CFG0_EFUSE2_VAL_OUT7 (0x00000047) +#define AQT1000_CHIP_CFG0_EFUSE2_VAL_OUT8 (0x00000048) +#define AQT1000_CHIP_CFG0_EFUSE2_VAL_OUT9 (0x00000049) +#define AQT1000_CHIP_CFG0_EFUSE2_VAL_OUT10 (0x0000004A) +#define AQT1000_CHIP_CFG0_EFUSE2_VAL_OUT11 (0x0000004B) +#define AQT1000_CHIP_CFG0_EFUSE2_VAL_OUT12 (0x0000004C) +#define AQT1000_CHIP_CFG0_EFUSE2_VAL_OUT13 (0x0000004D) +#define AQT1000_CHIP_CFG0_EFUSE2_VAL_OUT14 (0x0000004E) +#define AQT1000_CHIP_CFG0_EFUSE2_VAL_OUT15 (0x0000004F) +#define AQT1000_CHIP_CFG0_EFUSE2_STATUS (0x00000050) +#define AQT1000_CHIP_CFG1_BASE (0x00000051) +#define AQT1000_CHIP_CFG1_PWR_CTL (0x00000051) +#define AQT1000_CHIP_CFG1_BUS_MTRX_CFG (0x00000052) +#define AQT1000_CHIP_CFG1_DMA_BUS_VOTE (0x00000053) +#define AQT1000_CHIP_CFG1_USB_BUS_VOTE (0x00000054) +#define AQT1000_CHIP_CFG1_BLSP_BUS_VOTE (0x00000055) +#define AQT1000_CHIP_CFG1_PWR_MEM_SD (0x00000059) +#define AQT1000_CHIP_CFG1_PWR_SYS_MEM_SD_RAM (0x0000005C) +#define AQT1000_CHIP_CFG1_PWR_SYS_MEM_SD_ROM (0x0000005D) +#define AQT1000_CHIP_CFG1_PWR_SYS_MEM_FORCE_DS_RAM (0x0000005E) +#define AQT1000_CHIP_CFG1_PWR_SYS_MEM_FORCE_DS_ROM (0x0000005F) +#define AQT1000_CHIP_CFG1_CLK_CFG_FLL (0x00000061) +#define AQT1000_CHIP_CFG1_CLK_CFG_SPI_M (0x00000062) +#define AQT1000_CHIP_CFG1_CLK_CFG_I2C_M (0x00000063) +#define AQT1000_CHIP_CFG1_CLK_CFG_UART (0x00000064) +#define AQT1000_CHIP_CFG1_RST_USB_SS (0x00000071) +#define AQT1000_CHIP_CFG1_RST_BLSP (0x00000072) +#define AQT1000_CHIP_CFG1_RST_BUS_MTRX (0x00000073) +#define AQT1000_CHIP_CFG1_RST_MISC (0x00000074) +#define AQT1000_CHIP_CFG1_ANA_WAIT_STATE_CTL (0x00000081) +#define AQT1000_PAGE1_BASE (0x00000100) +#define AQT1000_PAGE1_PAGE_REGISTER (0x00000100) +#define AQT1000_FLL_BASE (0x00000101) +#define AQT1000_FLL_USER_CTL_0 (0x00000101) +#define AQT1000_FLL_USER_CTL_1 (0x00000102) +#define AQT1000_FLL_USER_CTL_2 (0x00000103) +#define AQT1000_FLL_USER_CTL_3 (0x00000104) +#define AQT1000_FLL_USER_CTL_4 (0x00000105) +#define AQT1000_FLL_USER_CTL_5 (0x00000106) +#define AQT1000_FLL_USER_CTL_6 (0x00000107) +#define AQT1000_FLL_USER_CTL_7 (0x00000108) +#define AQT1000_FLL_USER_CTL_8 (0x00000109) +#define AQT1000_FLL_USER_CTL_9 (0x0000010A) +#define AQT1000_FLL_L_VAL_CTL_0 (0x0000010B) +#define AQT1000_FLL_L_VAL_CTL_1 (0x0000010C) +#define AQT1000_FLL_DSM_FRAC_CTL_0 (0x0000010D) +#define AQT1000_FLL_DSM_FRAC_CTL_1 (0x0000010E) +#define AQT1000_FLL_CONFIG_CTL_0 (0x0000010F) +#define AQT1000_FLL_CONFIG_CTL_1 (0x00000110) +#define AQT1000_FLL_CONFIG_CTL_2 (0x00000111) +#define AQT1000_FLL_CONFIG_CTL_3 (0x00000112) +#define AQT1000_FLL_CONFIG_CTL_4 (0x00000113) +#define AQT1000_FLL_TEST_CTL_0 (0x00000114) +#define AQT1000_FLL_TEST_CTL_1 (0x00000115) +#define AQT1000_FLL_TEST_CTL_2 (0x00000116) +#define AQT1000_FLL_TEST_CTL_3 (0x00000117) +#define AQT1000_FLL_TEST_CTL_4 (0x00000118) +#define AQT1000_FLL_TEST_CTL_5 (0x00000119) +#define AQT1000_FLL_TEST_CTL_6 (0x0000011A) +#define AQT1000_FLL_TEST_CTL_7 (0x0000011B) +#define AQT1000_FLL_FREQ_CTL_0 (0x0000011C) +#define AQT1000_FLL_FREQ_CTL_1 (0x0000011D) +#define AQT1000_FLL_FREQ_CTL_2 (0x0000011E) +#define AQT1000_FLL_FREQ_CTL_3 (0x0000011F) +#define AQT1000_FLL_SSC_CTL_0 (0x00000120) +#define AQT1000_FLL_SSC_CTL_1 (0x00000121) +#define AQT1000_FLL_SSC_CTL_2 (0x00000122) +#define AQT1000_FLL_SSC_CTL_3 (0x00000123) +#define AQT1000_FLL_FLL_MODE (0x00000124) +#define AQT1000_FLL_STATUS_0 (0x00000125) +#define AQT1000_FLL_STATUS_1 (0x00000126) +#define AQT1000_FLL_STATUS_2 (0x00000127) +#define AQT1000_FLL_STATUS_3 (0x00000128) +#define AQT1000_PAGE2_BASE (0x00000200) +#define AQT1000_PAGE2_PAGE_REGISTER (0x00000200) +#define AQT1000_I2S_BASE (0x00000201) +#define AQT1000_I2S_I2S_0_TX_CFG (0x00000201) +#define AQT1000_I2S_I2S_0_RX_CFG (0x00000202) +#define AQT1000_I2S_I2S_0_CTL (0x00000203) +#define AQT1000_I2S_I2S_CLKSRC_CTL (0x00000204) +#define AQT1000_I2S_I2S_HS_CLK_CTL (0x00000205) +#define AQT1000_I2S_I2S_0_RST (0x00000206) +#define AQT1000_I2S_SHADOW_I2S_0_CTL (0x00000207) +#define AQT1000_I2S_SHADOW_I2S_0_RX_CFG (0x00000208) +#define AQT1000_PAGE5_BASE (0x00000500) +#define AQT1000_PAGE5_PAGE_REGISTER (0x00000500) +#define AQT1000_INTR_CTRL_INTR_CTRL_BASE (0x00000501) +#define AQT1000_INTR_CTRL_MCU_INT_POLARITY (0x00000501) +#define AQT1000_INTR_CTRL_INT_MASK_0 (0x00000502) +#define AQT1000_INTR_CTRL_INT_MASK_1 (0x00000503) +#define AQT1000_INTR_CTRL_INT_MASK_2 (0x00000504) +#define AQT1000_INTR_CTRL_INT_MASK_3 (0x00000505) +#define AQT1000_INTR_CTRL_INT_MASK_4 (0x00000506) +#define AQT1000_INTR_CTRL_INT_MASK_5 (0x00000507) +#define AQT1000_INTR_CTRL_INT_MASK_6 (0x00000508) +#define AQT1000_INTR_CTRL_INT_STATUS_0 (0x00000509) +#define AQT1000_INTR_CTRL_INT_STATUS_1 (0x0000050A) +#define AQT1000_INTR_CTRL_INT_STATUS_2 (0x0000050B) +#define AQT1000_INTR_CTRL_INT_STATUS_3 (0x0000050C) +#define AQT1000_INTR_CTRL_INT_STATUS_4 (0x0000050D) +#define AQT1000_INTR_CTRL_INT_STATUS_5 (0x0000050E) +#define AQT1000_INTR_CTRL_INT_STATUS_6 (0x0000050F) +#define AQT1000_INTR_CTRL_INT_CLEAR_0 (0x00000510) +#define AQT1000_INTR_CTRL_INT_CLEAR_1 (0x00000511) +#define AQT1000_INTR_CTRL_INT_CLEAR_2 (0x00000512) +#define AQT1000_INTR_CTRL_INT_CLEAR_3 (0x00000513) +#define AQT1000_INTR_CTRL_INT_CLEAR_4 (0x00000514) +#define AQT1000_INTR_CTRL_INT_CLEAR_5 (0x00000515) +#define AQT1000_INTR_CTRL_INT_CLEAR_6 (0x00000516) +#define AQT1000_INTR_CTRL_INT_TYPE_0 (0x00000517) +#define AQT1000_INTR_CTRL_INT_TYPE_1 (0x00000518) +#define AQT1000_INTR_CTRL_INT_TYPE_2 (0x00000519) +#define AQT1000_INTR_CTRL_INT_TYPE_3 (0x0000051A) +#define AQT1000_INTR_CTRL_INT_TYPE_4 (0x0000051B) +#define AQT1000_INTR_CTRL_INT_TYPE_5 (0x0000051C) +#define AQT1000_INTR_CTRL_INT_TYPE_6 (0x0000051D) +#define AQT1000_INTR_CTRL_INT_TEST_EN_0 (0x0000051E) +#define AQT1000_INTR_CTRL_INT_TEST_EN_1 (0x0000051F) +#define AQT1000_INTR_CTRL_INT_TEST_EN_2 (0x00000520) +#define AQT1000_INTR_CTRL_INT_TEST_EN_3 (0x00000521) +#define AQT1000_INTR_CTRL_INT_TEST_EN_4 (0x00000522) +#define AQT1000_INTR_CTRL_INT_TEST_EN_5 (0x00000523) +#define AQT1000_INTR_CTRL_INT_TEST_EN_6 (0x00000524) +#define AQT1000_INTR_CTRL_INT_TEST_VAL_0 (0x00000525) +#define AQT1000_INTR_CTRL_INT_TEST_VAL_1 (0x00000526) +#define AQT1000_INTR_CTRL_INT_TEST_VAL_2 (0x00000527) +#define AQT1000_INTR_CTRL_INT_TEST_VAL_3 (0x00000528) +#define AQT1000_INTR_CTRL_INT_TEST_VAL_4 (0x00000529) +#define AQT1000_INTR_CTRL_INT_TEST_VAL_5 (0x0000052A) +#define AQT1000_INTR_CTRL_INT_TEST_VAL_6 (0x0000052B) +#define AQT1000_INTR_CTRL_INT_DEST_0 (0x0000052C) +#define AQT1000_INTR_CTRL_INT_DEST_1 (0x0000052D) +#define AQT1000_INTR_CTRL_INT_DEST_2 (0x0000052E) +#define AQT1000_INTR_CTRL_INT_DEST_3 (0x0000052F) +#define AQT1000_INTR_CTRL_INT_DEST_4 (0x00000530) +#define AQT1000_INTR_CTRL_INT_DEST_5 (0x00000531) +#define AQT1000_INTR_CTRL_INT_DEST_6 (0x00000532) +#define AQT1000_INTR_CTRL_INT_DEST_7 (0x00000533) +#define AQT1000_INTR_CTRL_INT_DEST_8 (0x00000534) +#define AQT1000_INTR_CTRL_INT_DEST_9 (0x00000535) +#define AQT1000_INTR_CTRL_INT_DEST_10 (0x00000536) +#define AQT1000_INTR_CTRL_INT_DEST_11 (0x00000537) +#define AQT1000_INTR_CTRL_INT_DEST_12 (0x00000538) +#define AQT1000_INTR_CTRL_INT_DEST_13 (0x00000539) +#define AQT1000_INTR_CTRL_CLR_COMMIT (0x000005E1) +#define AQT1000_ANA_BASE (0x00000600) +#define AQT1000_ANA_PAGE_REGISTER (0x00000600) +#define AQT1000_ANA_BIAS (0x00000601) +#define AQT1000_ANA_RX_SUPPLIES (0x00000608) +#define AQT1000_ANA_HPH (0x00000609) +#define AQT1000_ANA_AMIC1 (0x0000060E) +#define AQT1000_ANA_AMIC2 (0x0000060F) +#define AQT1000_ANA_AMIC3 (0x00000610) +#define AQT1000_ANA_AMIC3_HPF (0x00000611) +#define AQT1000_ANA_MBHC_MECH (0x00000614) +#define AQT1000_ANA_MBHC_ELECT (0x00000615) +#define AQT1000_ANA_MBHC_ZDET (0x00000616) +#define AQT1000_ANA_MBHC_RESULT_1 (0x00000617) +#define AQT1000_ANA_MBHC_RESULT_2 (0x00000618) +#define AQT1000_ANA_MBHC_RESULT_3 (0x00000619) +#define AQT1000_ANA_MBHC_BTN0 (0x0000061A) +#define AQT1000_ANA_MBHC_BTN1 (0x0000061B) +#define AQT1000_ANA_MBHC_BTN2 (0x0000061C) +#define AQT1000_ANA_MBHC_BTN3 (0x0000061D) +#define AQT1000_ANA_MBHC_BTN4 (0x0000061E) +#define AQT1000_ANA_MBHC_BTN5 (0x0000061F) +#define AQT1000_ANA_MBHC_BTN6 (0x00000620) +#define AQT1000_ANA_MBHC_BTN7 (0x00000621) +#define AQT1000_ANA_MICB1 (0x00000622) +#define AQT1000_ANA_MICB1_RAMP (0x00000624) +#define AQT1000_BIAS_BASE (0x00000628) +#define AQT1000_BIAS_CTL (0x00000628) +#define AQT1000_BIAS_CCOMP_FINE_ADJ (0x00000629) +#define AQT1000_LED_BASE (0x0000062E) +#define AQT1000_LED_LED_MODE_SEL_R (0x0000062E) +#define AQT1000_LED_LED_MISC_R (0x0000062F) +#define AQT1000_LED_LED_MODE_SEL_G (0x00000630) +#define AQT1000_LED_LED_MISC_G (0x00000631) +#define AQT1000_LED_LED_MODE_SEL_B (0x00000632) +#define AQT1000_LED_LED_MISC_B (0x00000633) +#define AQT1000_LDOH_BASE (0x0000063A) +#define AQT1000_LDOH_MODE (0x0000063A) +#define AQT1000_LDOH_BIAS (0x0000063B) +#define AQT1000_LDOH_STB_LOADS (0x0000063C) +#define AQT1000_LDOH_MISC1 (0x0000063D) +#define AQT1000_LDOL_BASE (0x00000640) +#define AQT1000_LDOL_VDDCX_ADJUST (0x00000640) +#define AQT1000_LDOL_DISABLE_LDOL (0x00000641) +#define AQT1000_BUCK_5V_BASE (0x00000644) +#define AQT1000_BUCK_5V_EN_CTL (0x00000644) +#define AQT1000_BUCK_5V_VOUT_SEL (0x00000645) +#define AQT1000_BUCK_5V_CTRL_VCL_1 (0x00000646) +#define AQT1000_BUCK_5V_CTRL_VCL_2 (0x00000647) +#define AQT1000_BUCK_5V_CTRL_CCL_2 (0x00000648) +#define AQT1000_BUCK_5V_CTRL_CCL_1 (0x00000649) +#define AQT1000_BUCK_5V_CTRL_CCL_3 (0x0000064A) +#define AQT1000_BUCK_5V_CTRL_CCL_4 (0x0000064B) +#define AQT1000_BUCK_5V_CTRL_CCL_5 (0x0000064C) +#define AQT1000_BUCK_5V_IBIAS_CTL_1 (0x0000064D) +#define AQT1000_BUCK_5V_IBIAS_CTL_2 (0x0000064E) +#define AQT1000_BUCK_5V_IBIAS_CTL_3 (0x0000064F) +#define AQT1000_BUCK_5V_IBIAS_CTL_4 (0x00000650) +#define AQT1000_BUCK_5V_IBIAS_CTL_5 (0x00000651) +#define AQT1000_BUCK_5V_ATEST_DTEST_CTL (0x00000652) +#define AQT1000_PON_BASE (0x00000653) +#define AQT1000_PON_BG_CTRL (0x00000653) +#define AQT1000_PON_TEST_CTRL (0x00000654) +#define AQT1000_MBHC_BASE (0x00000656) +#define AQT1000_MBHC_CTL_CLK (0x00000656) +#define AQT1000_MBHC_CTL_ANA (0x00000657) +#define AQT1000_MBHC_CTL_SPARE_1 (0x00000658) +#define AQT1000_MBHC_CTL_SPARE_2 (0x00000659) +#define AQT1000_MBHC_CTL_BCS (0x0000065A) +#define AQT1000_MBHC_MOISTURE_DET_FSM_STATUS (0x0000065B) +#define AQT1000_MBHC_TEST_CTL (0x0000065C) +#define AQT1000_MICB1_BASE (0x0000066B) +#define AQT1000_MICB1_TEST_CTL_1 (0x0000066B) +#define AQT1000_MICB1_TEST_CTL_2 (0x0000066C) +#define AQT1000_MICB1_TEST_CTL_3 (0x0000066D) +#define AQT1000_MICB1_MISC_BASE (0x0000066E) +#define AQT1000_MICB1_MISC_MICB1_INM_RES_BIAS (0x0000066E) +#define AQT1000_MICB1_MISC_MICB_MISC1 (0x0000066F) +#define AQT1000_MICB1_MISC_MICB_MISC2 (0x00000670) +#define AQT1000_TX_COM_BASE (0x00000677) +#define AQT1000_TX_COM_ADC_VCM (0x00000677) +#define AQT1000_TX_COM_BIAS_ATEST (0x00000678) +#define AQT1000_TX_COM_ADC_INT1_IB (0x00000679) +#define AQT1000_TX_COM_ADC_INT2_IB (0x0000067A) +#define AQT1000_TX_COM_TXFE_DIV_CTL (0x0000067B) +#define AQT1000_TX_COM_TXFE_DIV_START (0x0000067C) +#define AQT1000_TX_COM_TXFE_DIV_STOP_9P6M (0x0000067D) +#define AQT1000_TX_COM_TXFE_DIV_STOP_12P288M (0x0000067E) +#define AQT1000_TX_1_2_BASE (0x0000067F) +#define AQT1000_TX_1_2_TEST_EN (0x0000067F) +#define AQT1000_TX_1_2_ADC_IB (0x00000680) +#define AQT1000_TX_1_2_ATEST_REFCTL (0x00000681) +#define AQT1000_TX_1_2_TEST_CTL (0x00000682) +#define AQT1000_TX_1_2_TEST_BLK_EN (0x00000683) +#define AQT1000_TX_1_2_TXFE_CLKDIV (0x00000684) +#define AQT1000_TX_1_2_SAR1_ERR (0x00000685) +#define AQT1000_TX_1_2_SAR2_ERR (0x00000686) +#define AQT1000_TX_3_BASE (0x00000687) +#define AQT1000_TX_3_TEST_EN (0x00000687) +#define AQT1000_TX_3_ADC_IB (0x00000688) +#define AQT1000_TX_3_ATEST_REFCTL (0x00000689) +#define AQT1000_TX_3_TEST_CTL (0x0000068A) +#define AQT1000_TX_3_TEST_BLK_EN (0x0000068B) +#define AQT1000_TX_3_TXFE_CLKDIV (0x0000068C) +#define AQT1000_TX_3_SAR1_ERR (0x0000068D) +#define AQT1000_TX_3_SAR2_ERR (0x0000068E) +#define AQT1000_TX_BASE (0x0000068F) +#define AQT1000_TX_ATEST1_2_SEL (0x0000068F) +#define AQT1000_CLASSH_BASE (0x00000697) +#define AQT1000_CLASSH_MODE_1 (0x00000697) +#define AQT1000_CLASSH_MODE_2 (0x00000698) +#define AQT1000_CLASSH_MODE_3 (0x00000699) +#define AQT1000_CLASSH_CTRL_VCL_1 (0x0000069A) +#define AQT1000_CLASSH_CTRL_VCL_2 (0x0000069B) +#define AQT1000_CLASSH_CTRL_CCL_1 (0x0000069C) +#define AQT1000_CLASSH_CTRL_CCL_2 (0x0000069D) +#define AQT1000_CLASSH_CTRL_CCL_3 (0x0000069E) +#define AQT1000_CLASSH_CTRL_CCL_4 (0x0000069F) +#define AQT1000_CLASSH_CTRL_CCL_5 (0x000006A0) +#define AQT1000_CLASSH_BUCK_TMUX_A_D (0x000006A1) +#define AQT1000_CLASSH_BUCK_SW_DRV_CNTL (0x000006A2) +#define AQT1000_CLASSH_SPARE (0x000006A3) +#define AQT1000_FLYBACK_BASE (0x000006A4) +#define AQT1000_FLYBACK_EN (0x000006A4) +#define AQT1000_FLYBACK_VNEG_CTRL_1 (0x000006A5) +#define AQT1000_FLYBACK_VNEG_CTRL_2 (0x000006A6) +#define AQT1000_FLYBACK_VNEG_CTRL_3 (0x000006A7) +#define AQT1000_FLYBACK_VNEG_CTRL_4 (0x000006A8) +#define AQT1000_FLYBACK_VNEG_CTRL_5 (0x000006A9) +#define AQT1000_FLYBACK_VNEG_CTRL_6 (0x000006AA) +#define AQT1000_FLYBACK_VNEG_CTRL_7 (0x000006AB) +#define AQT1000_FLYBACK_VNEG_CTRL_8 (0x000006AC) +#define AQT1000_FLYBACK_VNEG_CTRL_9 (0x000006AD) +#define AQT1000_FLYBACK_VNEGDAC_CTRL_1 (0x000006AE) +#define AQT1000_FLYBACK_VNEGDAC_CTRL_2 (0x000006AF) +#define AQT1000_FLYBACK_VNEGDAC_CTRL_3 (0x000006B0) +#define AQT1000_FLYBACK_CTRL_1 (0x000006B1) +#define AQT1000_FLYBACK_TEST_CTL (0x000006B2) +#define AQT1000_RX_BASE (0x000006B3) +#define AQT1000_RX_AUX_SW_CTL (0x000006B3) +#define AQT1000_RX_PA_AUX_IN_CONN (0x000006B4) +#define AQT1000_RX_TIMER_DIV (0x000006B5) +#define AQT1000_RX_OCP_CTL (0x000006B6) +#define AQT1000_RX_OCP_COUNT (0x000006B7) +#define AQT1000_RX_BIAS_ATEST (0x000006B8) +#define AQT1000_RX_BIAS_MISC1 (0x000006B9) +#define AQT1000_RX_BIAS_HPH_LDO (0x000006BA) +#define AQT1000_RX_BIAS_HPH_PA (0x000006BB) +#define AQT1000_RX_BIAS_HPH_RDACBUFF_CNP2 (0x000006BC) +#define AQT1000_RX_BIAS_HPH_RDAC_LDO (0x000006BD) +#define AQT1000_RX_BIAS_HPH_CNP1 (0x000006BE) +#define AQT1000_RX_BIAS_HPH_LOWPOWER (0x000006BF) +#define AQT1000_RX_BIAS_MISC2 (0x000006C0) +#define AQT1000_RX_BIAS_MISC3 (0x000006C1) +#define AQT1000_RX_BIAS_MISC4 (0x000006C2) +#define AQT1000_RX_BIAS_MISC5 (0x000006C3) +#define AQT1000_RX_BIAS_BUCK_RST (0x000006C4) +#define AQT1000_RX_BIAS_BUCK_VREF_ERRAMP (0x000006C5) +#define AQT1000_RX_BIAS_FLYB_ERRAMP (0x000006C6) +#define AQT1000_RX_BIAS_FLYB_BUFF (0x000006C7) +#define AQT1000_RX_BIAS_FLYB_MID_RST (0x000006C8) +#define AQT1000_HPH_BASE (0x000006C9) +#define AQT1000_HPH_L_STATUS (0x000006C9) +#define AQT1000_HPH_R_STATUS (0x000006CA) +#define AQT1000_HPH_CNP_EN (0x000006CB) +#define AQT1000_HPH_CNP_WG_CTL (0x000006CC) +#define AQT1000_HPH_CNP_WG_TIME (0x000006CD) +#define AQT1000_HPH_OCP_CTL (0x000006CE) +#define AQT1000_HPH_AUTO_CHOP (0x000006CF) +#define AQT1000_HPH_CHOP_CTL (0x000006D0) +#define AQT1000_HPH_PA_CTL1 (0x000006D1) +#define AQT1000_HPH_PA_CTL2 (0x000006D2) +#define AQT1000_HPH_L_EN (0x000006D3) +#define AQT1000_HPH_L_TEST (0x000006D4) +#define AQT1000_HPH_L_ATEST (0x000006D5) +#define AQT1000_HPH_R_EN (0x000006D6) +#define AQT1000_HPH_R_TEST (0x000006D7) +#define AQT1000_HPH_R_ATEST (0x000006D8) +#define AQT1000_HPH_RDAC_CLK_CTL1 (0x000006D9) +#define AQT1000_HPH_RDAC_CLK_CTL2 (0x000006DA) +#define AQT1000_HPH_RDAC_LDO_CTL (0x000006DB) +#define AQT1000_HPH_RDAC_CHOP_CLK_LP_CTL (0x000006DC) +#define AQT1000_HPH_REFBUFF_UHQA_CTL (0x000006DD) +#define AQT1000_HPH_REFBUFF_LP_CTL (0x000006DE) +#define AQT1000_HPH_L_DAC_CTL (0x000006DF) +#define AQT1000_HPH_R_DAC_CTL (0x000006E0) +#define AQT1000_HPHLR_BASE (0x000006E1) +#define AQT1000_HPHLR_SURGE_COMP_SEL (0x000006E1) +#define AQT1000_HPHLR_SURGE_EN (0x000006E2) +#define AQT1000_HPHLR_SURGE_MISC1 (0x000006E3) +#define AQT1000_HPHLR_SURGE_STATUS (0x000006E4) +#define AQT1000_ANA_NEW_BASE (0x00000700) +#define AQT1000_ANA_NEW_PAGE_REGISTER (0x00000700) +#define AQT1000_HPH_NEW_BASE (0x00000701) +#define AQT1000_HPH_NEW_ANA_HPH2 (0x00000701) +#define AQT1000_HPH_NEW_ANA_HPH3 (0x00000702) +#define AQT1000_CLK_SYS_BASE (0x0000070E) +#define AQT1000_CLK_SYS_MCLK1_PRG (0x0000070E) +#define AQT1000_CLK_SYS_MCLK2_I2S_HS_CLK_PRG (0x0000070F) +#define AQT1000_CLK_SYS_XO_CAP_XTP (0x00000710) +#define AQT1000_CLK_SYS_XO_CAP_XTM (0x00000711) +#define AQT1000_CLK_SYS_PLL_ENABLES (0x00000712) +#define AQT1000_CLK_SYS_PLL_PRESET (0x00000713) +#define AQT1000_CLK_SYS_PLL_STATUS (0x00000714) +#define AQT1000_MBHC_NEW_BASE (0x0000071F) +#define AQT1000_MBHC_NEW_ELECT_REM_CLAMP_CTL (0x0000071F) +#define AQT1000_MBHC_NEW_CTL_1 (0x00000720) +#define AQT1000_MBHC_NEW_CTL_2 (0x00000721) +#define AQT1000_MBHC_NEW_PLUG_DETECT_CTL (0x00000722) +#define AQT1000_MBHC_NEW_ZDET_ANA_CTL (0x00000723) +#define AQT1000_MBHC_NEW_ZDET_RAMP_CTL (0x00000724) +#define AQT1000_MBHC_NEW_FSM_STATUS (0x00000725) +#define AQT1000_MBHC_NEW_ADC_RESULT (0x00000726) +#define AQT1000_HPH_NEW_INT_BASE (0x00000732) +#define AQT1000_HPH_NEW_INT_RDAC_GAIN_CTL (0x00000732) +#define AQT1000_HPH_NEW_INT_RDAC_HD2_CTL_L (0x00000733) +#define AQT1000_HPH_NEW_INT_RDAC_VREF_CTL (0x00000734) +#define AQT1000_HPH_NEW_INT_RDAC_OVERRIDE_CTL (0x00000735) +#define AQT1000_HPH_NEW_INT_RDAC_HD2_CTL_R (0x00000736) +#define AQT1000_HPH_NEW_INT_PA_MISC1 (0x00000737) +#define AQT1000_HPH_NEW_INT_PA_MISC2 (0x00000738) +#define AQT1000_HPH_NEW_INT_PA_RDAC_MISC (0x00000739) +#define AQT1000_HPH_NEW_INT_HPH_TIMER1 (0x0000073A) +#define AQT1000_HPH_NEW_INT_HPH_TIMER2 (0x0000073B) +#define AQT1000_HPH_NEW_INT_HPH_TIMER3 (0x0000073C) +#define AQT1000_HPH_NEW_INT_HPH_TIMER4 (0x0000073D) +#define AQT1000_HPH_NEW_INT_PA_RDAC_MISC2 (0x0000073E) +#define AQT1000_HPH_NEW_INT_PA_RDAC_MISC3 (0x0000073F) +#define AQT1000_RX_NEW_INT_BASE (0x00000745) +#define AQT1000_RX_NEW_INT_HPH_RDAC_BIAS_LOHIFI (0x00000745) +#define AQT1000_RX_NEW_INT_HPH_RDAC_BIAS_ULP (0x00000746) +#define AQT1000_RX_NEW_INT_HPH_RDAC_LDO_LP (0x00000747) +#define AQT1000_CLK_SYS_INT_BASE (0x0000076C) +#define AQT1000_CLK_SYS_INT_CLK_TEST1 (0x0000076C) +#define AQT1000_CLK_SYS_INT_XO_TEST1 (0x0000076D) +#define AQT1000_CLK_SYS_INT_XO_TEST2 (0x0000076E) +#define AQT1000_CLK_SYS_INT_POST_DIV_REG0 (0x0000076F) +#define AQT1000_CLK_SYS_INT_POST_DIV_REG1 (0x00000770) +#define AQT1000_CLK_SYS_INT_REF_DIV_REG0 (0x00000771) +#define AQT1000_CLK_SYS_INT_REF_DIV_REG1 (0x00000772) +#define AQT1000_CLK_SYS_INT_FILTER_REG0 (0x00000773) +#define AQT1000_CLK_SYS_INT_FILTER_REG1 (0x00000774) +#define AQT1000_CLK_SYS_INT_PLL_L_VAL (0x00000775) +#define AQT1000_CLK_SYS_INT_PLL_M_VAL (0x00000776) +#define AQT1000_CLK_SYS_INT_PLL_N_VAL (0x00000777) +#define AQT1000_CLK_SYS_INT_TEST_REG0 (0x00000778) +#define AQT1000_CLK_SYS_INT_PFD_CP_DSM_PROG (0x00000779) +#define AQT1000_CLK_SYS_INT_VCO_PROG (0x0000077A) +#define AQT1000_CLK_SYS_INT_TEST_REG1 (0x0000077B) +#define AQT1000_CLK_SYS_INT_LDO_LOCK_CFG (0x0000077C) +#define AQT1000_CLK_SYS_INT_DIG_LOCK_DET_CFG (0x0000077D) +#define AQT1000_MBHC_NEW_INT_BASE (0x000007AF) +#define AQT1000_MBHC_NEW_INT_MOISTURE_DET_DC_CTRL (0x000007AF) +#define AQT1000_MBHC_NEW_INT_MOISTURE_DET_POLLING_CTRL (0x000007B0) +#define AQT1000_MBHC_NEW_INT_MECH_DET_CURRENT (0x000007B1) +#define AQT1000_MBHC_NEW_INT_SPARE_2 (0x000007B2) +#define AQT1000_PAGE10_BASE (0x00000A00) +#define AQT1000_PAGE10_PAGE_REGISTER (0x00000A00) +#define AQT1000_CDC_ANC0_BASE (0x00000A01) +#define AQT1000_CDC_ANC0_CLK_RESET_CTL (0x00000A01) +#define AQT1000_CDC_ANC0_MODE_1_CTL (0x00000A02) +#define AQT1000_CDC_ANC0_MODE_2_CTL (0x00000A03) +#define AQT1000_CDC_ANC0_FF_SHIFT (0x00000A04) +#define AQT1000_CDC_ANC0_FB_SHIFT (0x00000A05) +#define AQT1000_CDC_ANC0_LPF_FF_A_CTL (0x00000A06) +#define AQT1000_CDC_ANC0_LPF_FF_B_CTL (0x00000A07) +#define AQT1000_CDC_ANC0_LPF_FB_CTL (0x00000A08) +#define AQT1000_CDC_ANC0_SMLPF_CTL (0x00000A09) +#define AQT1000_CDC_ANC0_DCFLT_SHIFT_CTL (0x00000A0A) +#define AQT1000_CDC_ANC0_IIR_ADAPT_CTL (0x00000A0B) +#define AQT1000_CDC_ANC0_IIR_COEFF_1_CTL (0x00000A0C) +#define AQT1000_CDC_ANC0_IIR_COEFF_2_CTL (0x00000A0D) +#define AQT1000_CDC_ANC0_FF_A_GAIN_CTL (0x00000A0E) +#define AQT1000_CDC_ANC0_FF_B_GAIN_CTL (0x00000A0F) +#define AQT1000_CDC_ANC0_FB_GAIN_CTL (0x00000A10) +#define AQT1000_CDC_ANC0_RC_COMMON_CTL (0x00000A11) +#define AQT1000_CDC_ANC0_FIFO_COMMON_CTL (0x00000A13) +#define AQT1000_CDC_ANC0_RC0_STATUS_FMIN_CNTR (0x00000A14) +#define AQT1000_CDC_ANC0_RC1_STATUS_FMIN_CNTR (0x00000A15) +#define AQT1000_CDC_ANC0_RC0_STATUS_FMAX_CNTR (0x00000A16) +#define AQT1000_CDC_ANC0_RC1_STATUS_FMAX_CNTR (0x00000A17) +#define AQT1000_CDC_ANC0_STATUS_FIFO (0x00000A18) +#define AQT1000_CDC_ANC1_BASE (0x00000A19) +#define AQT1000_CDC_ANC1_CLK_RESET_CTL (0x00000A19) +#define AQT1000_CDC_ANC1_MODE_1_CTL (0x00000A1A) +#define AQT1000_CDC_ANC1_MODE_2_CTL (0x00000A1B) +#define AQT1000_CDC_ANC1_FF_SHIFT (0x00000A1C) +#define AQT1000_CDC_ANC1_FB_SHIFT (0x00000A1D) +#define AQT1000_CDC_ANC1_LPF_FF_A_CTL (0x00000A1E) +#define AQT1000_CDC_ANC1_LPF_FF_B_CTL (0x00000A1F) +#define AQT1000_CDC_ANC1_LPF_FB_CTL (0x00000A20) +#define AQT1000_CDC_ANC1_SMLPF_CTL (0x00000A21) +#define AQT1000_CDC_ANC1_DCFLT_SHIFT_CTL (0x00000A22) +#define AQT1000_CDC_ANC1_IIR_ADAPT_CTL (0x00000A23) +#define AQT1000_CDC_ANC1_IIR_COEFF_1_CTL (0x00000A24) +#define AQT1000_CDC_ANC1_IIR_COEFF_2_CTL (0x00000A25) +#define AQT1000_CDC_ANC1_FF_A_GAIN_CTL (0x00000A26) +#define AQT1000_CDC_ANC1_FF_B_GAIN_CTL (0x00000A27) +#define AQT1000_CDC_ANC1_FB_GAIN_CTL (0x00000A28) +#define AQT1000_CDC_ANC1_RC_COMMON_CTL (0x00000A29) +#define AQT1000_CDC_ANC1_FIFO_COMMON_CTL (0x00000A2B) +#define AQT1000_CDC_ANC1_RC0_STATUS_FMIN_CNTR (0x00000A2C) +#define AQT1000_CDC_ANC1_RC1_STATUS_FMIN_CNTR (0x00000A2D) +#define AQT1000_CDC_ANC1_RC0_STATUS_FMAX_CNTR (0x00000A2E) +#define AQT1000_CDC_ANC1_RC1_STATUS_FMAX_CNTR (0x00000A2F) +#define AQT1000_CDC_ANC1_STATUS_FIFO (0x00000A30) +#define AQT1000_CDC_TX0_BASE (0x00000A31) +#define AQT1000_CDC_TX0_TX_PATH_CTL (0x00000A31) +#define AQT1000_CDC_TX0_TX_PATH_CFG0 (0x00000A32) +#define AQT1000_CDC_TX0_TX_PATH_CFG1 (0x00000A33) +#define AQT1000_CDC_TX0_TX_VOL_CTL (0x00000A34) +#define AQT1000_CDC_TX0_TX_PATH_SEC0 (0x00000A37) +#define AQT1000_CDC_TX0_TX_PATH_SEC1 (0x00000A38) +#define AQT1000_CDC_TX0_TX_PATH_SEC2 (0x00000A39) +#define AQT1000_CDC_TX0_TX_PATH_SEC3 (0x00000A3A) +#define AQT1000_CDC_TX0_TX_PATH_SEC4 (0x00000A3B) +#define AQT1000_CDC_TX0_TX_PATH_SEC5 (0x00000A3C) +#define AQT1000_CDC_TX0_TX_PATH_SEC6 (0x00000A3D) +#define AQT1000_CDC_TX1_BASE (0x00000A41) +#define AQT1000_CDC_TX1_TX_PATH_CTL (0x00000A41) +#define AQT1000_CDC_TX1_TX_PATH_CFG0 (0x00000A42) +#define AQT1000_CDC_TX1_TX_PATH_CFG1 (0x00000A43) +#define AQT1000_CDC_TX1_TX_VOL_CTL (0x00000A44) +#define AQT1000_CDC_TX1_TX_PATH_SEC0 (0x00000A47) +#define AQT1000_CDC_TX1_TX_PATH_SEC1 (0x00000A48) +#define AQT1000_CDC_TX1_TX_PATH_SEC2 (0x00000A49) +#define AQT1000_CDC_TX1_TX_PATH_SEC3 (0x00000A4A) +#define AQT1000_CDC_TX1_TX_PATH_SEC4 (0x00000A4B) +#define AQT1000_CDC_TX1_TX_PATH_SEC5 (0x00000A4C) +#define AQT1000_CDC_TX1_TX_PATH_SEC6 (0x00000A4D) +#define AQT1000_CDC_TX2_BASE (0x00000A51) +#define AQT1000_CDC_TX2_TX_PATH_CTL (0x00000A51) +#define AQT1000_CDC_TX2_TX_PATH_CFG0 (0x00000A52) +#define AQT1000_CDC_TX2_TX_PATH_CFG1 (0x00000A53) +#define AQT1000_CDC_TX2_TX_VOL_CTL (0x00000A54) +#define AQT1000_CDC_TX2_TX_PATH_SEC0 (0x00000A57) +#define AQT1000_CDC_TX2_TX_PATH_SEC1 (0x00000A58) +#define AQT1000_CDC_TX2_TX_PATH_SEC2 (0x00000A59) +#define AQT1000_CDC_TX2_TX_PATH_SEC3 (0x00000A5A) +#define AQT1000_CDC_TX2_TX_PATH_SEC4 (0x00000A5B) +#define AQT1000_CDC_TX2_TX_PATH_SEC5 (0x00000A5C) +#define AQT1000_CDC_TX2_TX_PATH_SEC6 (0x00000A5D) +#define AQT1000_CDC_TX2_TX_PATH_SEC7 (0x00000A5E) +#define AQT1000_PAGE11_BASE (0x00000B00) +#define AQT1000_PAGE11_PAGE_REGISTER (0x00000B00) +#define AQT1000_CDC_COMPANDER1_BASE (0x00000B01) +#define AQT1000_CDC_COMPANDER1_CTL0 (0x00000B01) +#define AQT1000_CDC_COMPANDER1_CTL1 (0x00000B02) +#define AQT1000_CDC_COMPANDER1_CTL2 (0x00000B03) +#define AQT1000_CDC_COMPANDER1_CTL3 (0x00000B04) +#define AQT1000_CDC_COMPANDER1_CTL4 (0x00000B05) +#define AQT1000_CDC_COMPANDER1_CTL5 (0x00000B06) +#define AQT1000_CDC_COMPANDER1_CTL6 (0x00000B07) +#define AQT1000_CDC_COMPANDER1_CTL7 (0x00000B08) +#define AQT1000_CDC_COMPANDER2_BASE (0x00000B09) +#define AQT1000_CDC_COMPANDER2_CTL0 (0x00000B09) +#define AQT1000_CDC_COMPANDER2_CTL1 (0x00000B0A) +#define AQT1000_CDC_COMPANDER2_CTL2 (0x00000B0B) +#define AQT1000_CDC_COMPANDER2_CTL3 (0x00000B0C) +#define AQT1000_CDC_COMPANDER2_CTL4 (0x00000B0D) +#define AQT1000_CDC_COMPANDER2_CTL5 (0x00000B0E) +#define AQT1000_CDC_COMPANDER2_CTL6 (0x00000B0F) +#define AQT1000_CDC_COMPANDER2_CTL7 (0x00000B10) +#define AQT1000_CDC_RX1_BASE (0x00000B55) +#define AQT1000_CDC_RX1_RX_PATH_CTL (0x00000B55) +#define AQT1000_CDC_RX1_RX_PATH_CFG0 (0x00000B56) +#define AQT1000_CDC_RX1_RX_PATH_CFG1 (0x00000B57) +#define AQT1000_CDC_RX1_RX_PATH_CFG2 (0x00000B58) +#define AQT1000_CDC_RX1_RX_VOL_CTL (0x00000B59) +#define AQT1000_CDC_RX1_RX_PATH_MIX_CTL (0x00000B5A) +#define AQT1000_CDC_RX1_RX_PATH_MIX_CFG (0x00000B5B) +#define AQT1000_CDC_RX1_RX_VOL_MIX_CTL (0x00000B5C) +#define AQT1000_CDC_RX1_RX_PATH_SEC0 (0x00000B5D) +#define AQT1000_CDC_RX1_RX_PATH_SEC1 (0x00000B5E) +#define AQT1000_CDC_RX1_RX_PATH_SEC2 (0x00000B5F) +#define AQT1000_CDC_RX1_RX_PATH_SEC3 (0x00000B60) +#define AQT1000_CDC_RX1_RX_PATH_SEC4 (0x00000B61) +#define AQT1000_CDC_RX1_RX_PATH_SEC5 (0x00000B62) +#define AQT1000_CDC_RX1_RX_PATH_SEC6 (0x00000B63) +#define AQT1000_CDC_RX1_RX_PATH_SEC7 (0x00000B64) +#define AQT1000_CDC_RX1_RX_PATH_MIX_SEC0 (0x00000B65) +#define AQT1000_CDC_RX1_RX_PATH_MIX_SEC1 (0x00000B66) +#define AQT1000_CDC_RX1_RX_PATH_DSMDEM_CTL (0x00000B67) +#define AQT1000_CDC_RX2_BASE (0x00000B69) +#define AQT1000_CDC_RX2_RX_PATH_CTL (0x00000B69) +#define AQT1000_CDC_RX2_RX_PATH_CFG0 (0x00000B6A) +#define AQT1000_CDC_RX2_RX_PATH_CFG1 (0x00000B6B) +#define AQT1000_CDC_RX2_RX_PATH_CFG2 (0x00000B6C) +#define AQT1000_CDC_RX2_RX_VOL_CTL (0x00000B6D) +#define AQT1000_CDC_RX2_RX_PATH_MIX_CTL (0x00000B6E) +#define AQT1000_CDC_RX2_RX_PATH_MIX_CFG (0x00000B6F) +#define AQT1000_CDC_RX2_RX_VOL_MIX_CTL (0x00000B70) +#define AQT1000_CDC_RX2_RX_PATH_SEC0 (0x00000B71) +#define AQT1000_CDC_RX2_RX_PATH_SEC1 (0x00000B72) +#define AQT1000_CDC_RX2_RX_PATH_SEC2 (0x00000B73) +#define AQT1000_CDC_RX2_RX_PATH_SEC3 (0x00000B74) +#define AQT1000_CDC_RX2_RX_PATH_SEC4 (0x00000B75) +#define AQT1000_CDC_RX2_RX_PATH_SEC5 (0x00000B76) +#define AQT1000_CDC_RX2_RX_PATH_SEC6 (0x00000B77) +#define AQT1000_CDC_RX2_RX_PATH_SEC7 (0x00000B78) +#define AQT1000_CDC_RX2_RX_PATH_MIX_SEC0 (0x00000B79) +#define AQT1000_CDC_RX2_RX_PATH_MIX_SEC1 (0x00000B7A) +#define AQT1000_CDC_RX2_RX_PATH_DSMDEM_CTL (0x00000B7B) +#define AQT1000_CDC_EQ_IIR0_BASE (0x00000BD1) +#define AQT1000_CDC_EQ_IIR0_PATH_CTL (0x00000BD1) +#define AQT1000_CDC_EQ_IIR0_PATH_CFG0 (0x00000BD2) +#define AQT1000_CDC_EQ_IIR0_PATH_CFG1 (0x00000BD3) +#define AQT1000_CDC_EQ_IIR0_PATH_CFG2 (0x00000BD4) +#define AQT1000_CDC_EQ_IIR0_PATH_CFG3 (0x00000BD5) +#define AQT1000_CDC_EQ_IIR0_COEF_CFG0 (0x00000BD6) +#define AQT1000_CDC_EQ_IIR0_COEF_CFG1 (0x00000BD7) +#define AQT1000_CDC_EQ_IIR1_BASE (0x00000BE1) +#define AQT1000_CDC_EQ_IIR1_PATH_CTL (0x00000BE1) +#define AQT1000_CDC_EQ_IIR1_PATH_CFG0 (0x00000BE2) +#define AQT1000_CDC_EQ_IIR1_PATH_CFG1 (0x00000BE3) +#define AQT1000_CDC_EQ_IIR1_PATH_CFG2 (0x00000BE4) +#define AQT1000_CDC_EQ_IIR1_PATH_CFG3 (0x00000BE5) +#define AQT1000_CDC_EQ_IIR1_COEF_CFG0 (0x00000BE6) +#define AQT1000_CDC_EQ_IIR1_COEF_CFG1 (0x00000BE7) +#define AQT1000_PAGE12_BASE (0x00000C00) +#define AQT1000_PAGE12_PAGE_REGISTER (0x00000C00) +#define AQT1000_CDC_CLSH_CDC_CLSH_BASE (0x00000C01) +#define AQT1000_CDC_CLSH_CRC (0x00000C01) +#define AQT1000_CDC_CLSH_DLY_CTRL (0x00000C02) +#define AQT1000_CDC_CLSH_DECAY_CTRL (0x00000C03) +#define AQT1000_CDC_CLSH_HPH_V_PA (0x00000C04) +#define AQT1000_CDC_CLSH_EAR_V_PA (0x00000C05) +#define AQT1000_CDC_CLSH_HPH_V_HD (0x00000C06) +#define AQT1000_CDC_CLSH_EAR_V_HD (0x00000C07) +#define AQT1000_CDC_CLSH_K1_MSB (0x00000C08) +#define AQT1000_CDC_CLSH_K1_LSB (0x00000C09) +#define AQT1000_CDC_CLSH_K2_MSB (0x00000C0A) +#define AQT1000_CDC_CLSH_K2_LSB (0x00000C0B) +#define AQT1000_CDC_CLSH_IDLE_CTRL (0x00000C0C) +#define AQT1000_CDC_CLSH_IDLE_HPH (0x00000C0D) +#define AQT1000_CDC_CLSH_IDLE_EAR (0x00000C0E) +#define AQT1000_CDC_CLSH_TEST0 (0x00000C0F) +#define AQT1000_CDC_CLSH_TEST1 (0x00000C10) +#define AQT1000_CDC_CLSH_OVR_VREF (0x00000C11) +#define AQT1000_MIXING_ASRC0_BASE (0x00000C55) +#define AQT1000_MIXING_ASRC0_CLK_RST_CTL (0x00000C55) +#define AQT1000_MIXING_ASRC0_CTL0 (0x00000C56) +#define AQT1000_MIXING_ASRC0_CTL1 (0x00000C57) +#define AQT1000_MIXING_ASRC0_FIFO_CTL (0x00000C58) +#define AQT1000_MIXING_ASRC0_STATUS_FMIN_CNTR_LSB (0x00000C59) +#define AQT1000_MIXING_ASRC0_STATUS_FMIN_CNTR_MSB (0x00000C5A) +#define AQT1000_MIXING_ASRC0_STATUS_FMAX_CNTR_LSB (0x00000C5B) +#define AQT1000_MIXING_ASRC0_STATUS_FMAX_CNTR_MSB (0x00000C5C) +#define AQT1000_MIXING_ASRC0_STATUS_FIFO (0x00000C5D) +#define AQT1000_MIXING_ASRC1_BASE (0x00000C61) +#define AQT1000_MIXING_ASRC1_CLK_RST_CTL (0x00000C61) +#define AQT1000_MIXING_ASRC1_CTL0 (0x00000C62) +#define AQT1000_MIXING_ASRC1_CTL1 (0x00000C63) +#define AQT1000_MIXING_ASRC1_FIFO_CTL (0x00000C64) +#define AQT1000_MIXING_ASRC1_STATUS_FMIN_CNTR_LSB (0x00000C65) +#define AQT1000_MIXING_ASRC1_STATUS_FMIN_CNTR_MSB (0x00000C66) +#define AQT1000_MIXING_ASRC1_STATUS_FMAX_CNTR_LSB (0x00000C67) +#define AQT1000_MIXING_ASRC1_STATUS_FMAX_CNTR_MSB (0x00000C68) +#define AQT1000_MIXING_ASRC1_STATUS_FIFO (0x00000C69) +#define AQT1000_CDC_SIDETONE_SRC0_BASE (0x00000CB5) +#define AQT1000_CDC_SIDETONE_SRC0_ST_SRC_PATH_CTL (0x00000CB5) +#define AQT1000_CDC_SIDETONE_SRC0_ST_SRC_PATH_CFG1 (0x00000CB6) +#define AQT1000_SIDETONE_ASRC0_BASE (0x00000CBD) +#define AQT1000_SIDETONE_ASRC0_CLK_RST_CTL (0x00000CBD) +#define AQT1000_SIDETONE_ASRC0_CTL0 (0x00000CBE) +#define AQT1000_SIDETONE_ASRC0_CTL1 (0x00000CBF) +#define AQT1000_SIDETONE_ASRC0_FIFO_CTL (0x00000CC0) +#define AQT1000_SIDETONE_ASRC0_STATUS_FMIN_CNTR_LSB (0x00000CC1) +#define AQT1000_SIDETONE_ASRC0_STATUS_FMIN_CNTR_MSB (0x00000CC2) +#define AQT1000_SIDETONE_ASRC0_STATUS_FMAX_CNTR_LSB (0x00000CC3) +#define AQT1000_SIDETONE_ASRC0_STATUS_FMAX_CNTR_MSB (0x00000CC4) +#define AQT1000_SIDETONE_ASRC0_STATUS_FIFO (0x00000CC5) +#define AQT1000_EC_REF_HQ0_BASE (0x00000CD5) +#define AQT1000_EC_REF_HQ0_EC_REF_HQ_PATH_CTL (0x00000CD5) +#define AQT1000_EC_REF_HQ0_EC_REF_HQ_CFG0 (0x00000CD6) +#define AQT1000_EC_REF_HQ1_BASE (0x00000CDD) +#define AQT1000_EC_REF_HQ1_EC_REF_HQ_PATH_CTL (0x00000CDD) +#define AQT1000_EC_REF_HQ1_EC_REF_HQ_CFG0 (0x00000CDE) +#define AQT1000_EC_ASRC0_BASE (0x00000CE5) +#define AQT1000_EC_ASRC0_CLK_RST_CTL (0x00000CE5) +#define AQT1000_EC_ASRC0_CTL0 (0x00000CE6) +#define AQT1000_EC_ASRC0_CTL1 (0x00000CE7) +#define AQT1000_EC_ASRC0_FIFO_CTL (0x00000CE8) +#define AQT1000_EC_ASRC0_STATUS_FMIN_CNTR_LSB (0x00000CE9) +#define AQT1000_EC_ASRC0_STATUS_FMIN_CNTR_MSB (0x00000CEA) +#define AQT1000_EC_ASRC0_STATUS_FMAX_CNTR_LSB (0x00000CEB) +#define AQT1000_EC_ASRC0_STATUS_FMAX_CNTR_MSB (0x00000CEC) +#define AQT1000_EC_ASRC0_STATUS_FIFO (0x00000CED) +#define AQT1000_EC_ASRC1_BASE (0x00000CF1) +#define AQT1000_EC_ASRC1_CLK_RST_CTL (0x00000CF1) +#define AQT1000_EC_ASRC1_CTL0 (0x00000CF2) +#define AQT1000_EC_ASRC1_CTL1 (0x00000CF3) +#define AQT1000_EC_ASRC1_FIFO_CTL (0x00000CF4) +#define AQT1000_EC_ASRC1_STATUS_FMIN_CNTR_LSB (0x00000CF5) +#define AQT1000_EC_ASRC1_STATUS_FMIN_CNTR_MSB (0x00000CF6) +#define AQT1000_EC_ASRC1_STATUS_FMAX_CNTR_LSB (0x00000CF7) +#define AQT1000_EC_ASRC1_STATUS_FMAX_CNTR_MSB (0x00000CF8) +#define AQT1000_EC_ASRC1_STATUS_FIFO (0x00000CF9) +#define AQT1000_PAGE13_BASE (0x00000D00) +#define AQT1000_PAGE13_PAGE_REGISTER (0x00000D00) +#define AQT1000_CDC_RX_INP_MUX_CDC_RX_INP_MUX_BASE (0x00000D01) +#define AQT1000_CDC_RX_INP_MUX_RX_INT1_CFG0 (0x00000D03) +#define AQT1000_CDC_RX_INP_MUX_RX_INT1_CFG1 (0x00000D04) +#define AQT1000_CDC_RX_INP_MUX_RX_INT2_CFG0 (0x00000D05) +#define AQT1000_CDC_RX_INP_MUX_RX_INT2_CFG1 (0x00000D06) +#define AQT1000_CDC_RX_INP_MUX_EQ_IIR_CFG0 (0x00000D11) +#define AQT1000_CDC_RX_INP_MUX_DSD_CFG0 (0x00000D12) +#define AQT1000_CDC_RX_INP_MUX_RX_MIX_CFG0 (0x00000D13) +#define AQT1000_CDC_RX_INP_MUX_SIDETONE_SRC_CFG0 (0x00000D18) +#define AQT1000_CDC_RX_INP_MUX_ANC_CFG0 (0x00000D1A) +#define AQT1000_CDC_RX_INP_MUX_SPLINE_ASRC_CFG0 (0x00000D1B) +#define AQT1000_CDC_RX_INP_MUX_EC_REF_HQ_CFG0 (0x00000D1C) +#define AQT1000_CDC_TX_INP_MUX_CDC_TX_INP_MUX_BASE (0x00000D1D) +#define AQT1000_CDC_TX_INP_MUX_ADC_MUX0_CFG0 (0x00000D1D) +#define AQT1000_CDC_TX_INP_MUX_ADC_MUX0_CFG1 (0x00000D1E) +#define AQT1000_CDC_TX_INP_MUX_ADC_MUX1_CFG0 (0x00000D1F) +#define AQT1000_CDC_TX_INP_MUX_ADC_MUX1_CFG1 (0x00000D20) +#define AQT1000_CDC_TX_INP_MUX_ADC_MUX2_CFG0 (0x00000D21) +#define AQT1000_CDC_TX_INP_MUX_ADC_MUX2_CFG1 (0x00000D22) +#define AQT1000_CDC_TX_INP_MUX_ADC_MUX10_CFG0 (0x00000D29) +#define AQT1000_CDC_TX_INP_MUX_ADC_MUX10_CFG1 (0x00000D2A) +#define AQT1000_CDC_TX_INP_MUX_ADC_MUX11_CFG0 (0x00000D2B) +#define AQT1000_CDC_TX_INP_MUX_ADC_MUX11_CFG1 (0x00000D2C) +#define AQT1000_CDC_TX_INP_MUX_ADC_MUX12_CFG0 (0x00000D2D) +#define AQT1000_CDC_TX_INP_MUX_ADC_MUX12_CFG1 (0x00000D2E) +#define AQT1000_CDC_TX_INP_MUX_ADC_MUX13_CFG0 (0x00000D2F) +#define AQT1000_CDC_TX_INP_MUX_ADC_MUX13_CFG1 (0x00000D30) +#define AQT1000_CDC_SIDETONE_IIR_INP_MUX_CDC_SIDETONE_IIR_INP_MUX_BASE (0xD31) +#define AQT1000_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG0 (0x00000D31) +#define AQT1000_CDC_IF_ROUTER_CDC_IF_ROUTER_BASE (0x00000D3D) +#define AQT1000_CDC_IF_ROUTER_TX_MUX_CFG0 (0x00000D3D) +#define AQT1000_CDC_CLK_RST_CTRL_CDC_CLK_RST_CTRL_BASE (0x00000D41) +#define AQT1000_CDC_CLK_RST_CTRL_MCLK_CONTROL (0x00000D41) +#define AQT1000_CDC_CLK_RST_CTRL_FS_CNT_CONTROL (0x00000D42) +#define AQT1000_CDC_CLK_RST_CTRL_DSD_CONTROL (0x00000D44) +#define AQT1000_CDC_CLK_RST_CTRL_ASRC_SHARE_CONTROL (0x00000D45) +#define AQT1000_CDC_CLK_RST_CTRL_GFM_CONTROL (0x00000D46) +#define AQT1000_CDC_CLK_RST_CTRL_I2S_CONTROL (0x00000D47) +#define AQT1000_CDC_SIDETONE_IIR0_BASE (0x00000D55) +#define AQT1000_CDC_SIDETONE_IIR0_IIR_PATH_CTL (0x00000D55) +#define AQT1000_CDC_SIDETONE_IIR0_IIR_GAIN_B1_CTL (0x00000D56) +#define AQT1000_CDC_SIDETONE_IIR0_IIR_GAIN_B2_CTL (0x00000D57) +#define AQT1000_CDC_SIDETONE_IIR0_IIR_GAIN_B3_CTL (0x00000D58) +#define AQT1000_CDC_SIDETONE_IIR0_IIR_GAIN_B4_CTL (0x00000D59) +#define AQT1000_CDC_SIDETONE_IIR0_IIR_GAIN_B5_CTL (0x00000D5A) +#define AQT1000_CDC_SIDETONE_IIR0_IIR_GAIN_B6_CTL (0x00000D5B) +#define AQT1000_CDC_SIDETONE_IIR0_IIR_GAIN_B7_CTL (0x00000D5C) +#define AQT1000_CDC_SIDETONE_IIR0_IIR_GAIN_B8_CTL (0x00000D5D) +#define AQT1000_CDC_SIDETONE_IIR0_IIR_CTL (0x00000D5E) +#define AQT1000_CDC_SIDETONE_IIR0_IIR_GAIN_TIMER_CTL (0x00000D5F) +#define AQT1000_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL (0x00000D60) +#define AQT1000_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL (0x00000D61) +#define AQT1000_CDC_TOP_CDC_TOP_BASE (0x00000D81) +#define AQT1000_CDC_TOP_TOP_CFG0 (0x00000D81) +#define AQT1000_CDC_TOP_HPHL_COMP_WR_LSB (0x00000D89) +#define AQT1000_CDC_TOP_HPHL_COMP_WR_MSB (0x00000D8A) +#define AQT1000_CDC_TOP_HPHL_COMP_LUT (0x00000D8B) +#define AQT1000_CDC_TOP_HPHL_COMP_RD_LSB (0x00000D8C) +#define AQT1000_CDC_TOP_HPHL_COMP_RD_MSB (0x00000D8D) +#define AQT1000_CDC_TOP_HPHR_COMP_WR_LSB (0x00000D8E) +#define AQT1000_CDC_TOP_HPHR_COMP_WR_MSB (0x00000D8F) +#define AQT1000_CDC_TOP_HPHR_COMP_LUT (0x00000D90) +#define AQT1000_CDC_TOP_HPHR_COMP_RD_LSB (0x00000D91) +#define AQT1000_CDC_TOP_HPHR_COMP_RD_MSB (0x00000D92) +#define AQT1000_CDC_DSD0_BASE (0x00000DB1) +#define AQT1000_CDC_DSD0_PATH_CTL (0x00000DB1) +#define AQT1000_CDC_DSD0_CFG0 (0x00000DB2) +#define AQT1000_CDC_DSD0_CFG1 (0x00000DB3) +#define AQT1000_CDC_DSD0_CFG2 (0x00000DB4) +#define AQT1000_CDC_DSD0_CFG3 (0x00000DB5) +#define AQT1000_CDC_DSD0_CFG4 (0x00000DB6) +#define AQT1000_CDC_DSD0_CFG5 (0x00000DB7) +#define AQT1000_CDC_DSD1_BASE (0x00000DC1) +#define AQT1000_CDC_DSD1_PATH_CTL (0x00000DC1) +#define AQT1000_CDC_DSD1_CFG0 (0x00000DC2) +#define AQT1000_CDC_DSD1_CFG1 (0x00000DC3) +#define AQT1000_CDC_DSD1_CFG2 (0x00000DC4) +#define AQT1000_CDC_DSD1_CFG3 (0x00000DC5) +#define AQT1000_CDC_DSD1_CFG4 (0x00000DC6) +#define AQT1000_CDC_DSD1_CFG5 (0x00000DC7) +#define AQT1000_CDC_RX_IDLE_DET_CDC_RX_IDLE_DET_BASE (0x00000DD1) +#define AQT1000_CDC_RX_IDLE_DET_PATH_CTL (0x00000DD1) +#define AQT1000_CDC_RX_IDLE_DET_CFG0 (0x00000DD2) +#define AQT1000_CDC_RX_IDLE_DET_CFG1 (0x00000DD3) +#define AQT1000_CDC_RX_IDLE_DET_CFG2 (0x00000DD4) +#define AQT1000_CDC_RX_IDLE_DET_CFG3 (0x00000DD5) +#define AQT1000_CDC_DOP_DET_CDC_DOP_DET_BASE (0x00000DD9) +#define AQT1000_CDC_DOP_DET_CTL (0x00000DD9) +#define AQT1000_CDC_DOP_DET_CFG0 (0x00000DDA) +#define AQT1000_CDC_DOP_DET_CFG1 (0x00000DDB) +#define AQT1000_CDC_DOP_DET_CFG2 (0x00000DDC) +#define AQT1000_CDC_DOP_DET_CFG3 (0x00000DDD) +#define AQT1000_CDC_DOP_DET_CFG4 (0x00000DDE) +#define AQT1000_CDC_DOP_DET_STATUS0 (0x00000DE1) +#define AQT1000_PAGE15_BASE (0x00000F00) +#define AQT1000_PAGE15_PAGE_REGISTER (0x00000F00) +#define AQT1000_CDC_DEBUG_CDC_DEBUG_BASE (0x00000FA1) +#define AQT1000_CDC_DEBUG_DSD0_DEBUG_CFG0 (0x00000FA1) +#define AQT1000_CDC_DEBUG_DSD0_DEBUG_CFG1 (0x00000FA2) +#define AQT1000_CDC_DEBUG_DSD0_DEBUG_CFG2 (0x00000FA3) +#define AQT1000_CDC_DEBUG_DSD0_DEBUG_CFG3 (0x00000FA4) +#define AQT1000_CDC_DEBUG_DSD1_DEBUG_CFG0 (0x00000FA5) +#define AQT1000_CDC_DEBUG_DSD1_DEBUG_CFG1 (0x00000FA6) +#define AQT1000_CDC_DEBUG_DSD1_DEBUG_CFG2 (0x00000FA7) +#define AQT1000_CDC_DEBUG_DSD1_DEBUG_CFG3 (0x00000FA8) +#define AQT1000_CDC_DEBUG_RC_RE_ASRC_DEBUG_CFG0 (0x00000FAB) +#define AQT1000_CDC_DEBUG_ANC0_RC0_FIFO_CTL (0x00000FAC) +#define AQT1000_CDC_DEBUG_ANC0_RC1_FIFO_CTL (0x00000FAD) +#define AQT1000_CDC_DEBUG_ANC1_RC0_FIFO_CTL (0x00000FAE) +#define AQT1000_CDC_DEBUG_ANC1_RC1_FIFO_CTL (0x00000FAF) +#define AQT1000_CDC_DEBUG_ANC_RC_RST_DBG_CNTR (0x00000FB0) +#define AQT1000_PAGE128_BASE (0x00008000) +#define AQT1000_PAGE128_PAGE_REGISTER (0x00008000) +#define AQT1000_TLMM_TLMM_BASE (0x00008001) +#define AQT1000_TLMM_SPI_CLK_PINCFG (0x00008001) +#define AQT1000_TLMM_SPI_MOSI_PINCFG (0x00008002) +#define AQT1000_TLMM_SPI_MISO_PINCFG (0x00008003) +#define AQT1000_TLMM_SPI_CS_N_PINCFG (0x00008004) +#define AQT1000_TLMM_GPIO1_PINCFG (0x00008005) +#define AQT1000_TLMM_GPIO2_PINCFG (0x00008006) +#define AQT1000_TLMM_GPIO3_PINCFG (0x00008007) +#define AQT1000_TLMM_GPIO4_PINCFG (0x00008008) +#define AQT1000_TLMM_GPIO5_PINCFG (0x00008009) +#define AQT1000_TLMM_GPIO6_PINCFG (0x0000800A) +#define AQT1000_TLMM_GPIO7_PINCFG (0x0000800B) +#define AQT1000_TLMM_GPIO8_PINCFG (0x0000800C) +#define AQT1000_TLMM_GPIO9_PINCFG (0x0000800D) +#define AQT1000_TLMM_GPIO10_PINCFG (0x0000800E) +#define AQT1000_PAD_CTRL_PAD_CTRL_BASE (0x00008031) +#define AQT1000_PAD_CTRL_PAD_PDN_CTRL_0 (0x00008031) +#define AQT1000_PAD_CTRL_PAD_PDN_CTRL_1 (0x00008032) +#define AQT1000_PAD_CTRL_PAD_PU_CTRL_0 (0x00008033) +#define AQT1000_PAD_CTRL_PAD_PU_CTRL_1 (0x00008034) +#define AQT1000_PAD_CTRL_GPIO_CTL_0_OE (0x00008036) +#define AQT1000_PAD_CTRL_GPIO_CTL_1_OE (0x00008037) +#define AQT1000_PAD_CTRL_GPIO_CTL_0_DATA (0x00008038) +#define AQT1000_PAD_CTRL_GPIO_CTL_1_DATA (0x00008039) +#define AQT1000_PAD_CTRL_PAD_DRVCTL (0x0000803A) +#define AQT1000_PAD_CTRL_PIN_STATUS (0x0000803B) +#define AQT1000_PAD_CTRL_MEM_CTRL (0x0000803C) +#define AQT1000_PAD_CTRL_PAD_INP_DISABLE_0 (0x0000803E) +#define AQT1000_PAD_CTRL_PAD_INP_DISABLE_1 (0x0000803F) +#define AQT1000_PAD_CTRL_PIN_CTL_OE_0 (0x00008040) +#define AQT1000_PAD_CTRL_PIN_CTL_OE_1 (0x00008041) +#define AQT1000_PAD_CTRL_PIN_CTL_DATA_0 (0x00008042) +#define AQT1000_PAD_CTRL_PIN_CTL_DATA_1 (0x00008043) +#define AQT1000_PAD_CTRL_USB_PHY_CLK_DIV (0x00008044) +#define AQT1000_PAD_CTRL_DEBUG_BUS_CDC (0x00008045) +#define AQT1000_PAD_CTRL_DEBUG_BUS_SEL (0x00008046) +#define AQT1000_PAD_CTRL_DEBUG_EN_1 (0x00008047) +#define AQT1000_PAD_CTRL_DEBUG_EN_2 (0x00008048) +#define AQT1000_PAD_CTRL_DEBUG_EN_3 (0x00008049) +#define AQT1000_PAD_CTRL_DEBUG_EN_4 (0x0000804A) +#define AQT1000_PAD_CTRL_DEBUG_EN_5 (0x0000804B) +#define AQT1000_PAD_CTRL_DEBUG_MUX_BIT_0 (0x0000804C) +#define AQT1000_PAD_CTRL_DEBUG_MUX_BIT_1 (0x0000804D) +#define AQT1000_PAD_CTRL_DEBUG_MUX_BIT_2 (0x0000804E) +#define AQT1000_PAD_CTRL_DEBUG_MUX_BIT_3 (0x0000804F) +#define AQT1000_PAD_CTRL_DEBUG_MUX_BIT_4 (0x00008050) +#define AQT1000_PAD_CTRL_DEBUG_MUX_BIT_5 (0x00008051) +#define AQT1000_PAD_CTRL_DEBUG_MUX_BIT_6 (0x00008052) +#define AQT1000_PAD_CTRL_DEBUG_MUX_BIT_7 (0x00008053) +#define AQT1000_PAD_CTRL_DEBUG_MUX_BIT_8 (0x00008054) +#define AQT1000_PAD_CTRL_DEBUG_MUX_BIT_9 (0x00008055) +#define AQT1000_PAD_CTRL_DEBUG_MUX_BIT_10 (0x00008056) +#define AQT1000_PAD_CTRL_DEBUG_MUX_BIT_11 (0x00008057) +#define AQT1000_PAD_CTRL_DEBUG_MUX_BIT_12 (0x00008058) +#define AQT1000_PAD_CTRL_DEBUG_MUX_BIT_13 (0x00008059) +#define AQT1000_PAD_CTRL_DEBUG_READ_0 (0x0000805A) +#define AQT1000_PAD_CTRL_DEBUG_READ_1 (0x0000805B) +#define AQT1000_PAD_CTRL_DEBUG_READ_2 (0x0000805C) +#define AQT1000_PAD_CTRL_DEBUG_READ_3 (0x0000805D) +#define AQT1000_PAD_CTRL_FPGA_CTL (0x00008061) +#define AQT1000_MAX_REGISTER (0x000080FF) + +#endif /*_AQT_REGISTERS_H*/ diff --git a/audio-kernel/asoc/codecs/aqt1000/aqt1000-regmap.c b/audio-kernel/asoc/codecs/aqt1000/aqt1000-regmap.c new file mode 100644 index 000000000..e38c32748 --- /dev/null +++ b/audio-kernel/asoc/codecs/aqt1000/aqt1000-regmap.c @@ -0,0 +1,110 @@ +/* Copyright (c) 2016-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 +#include +#include "aqt1000-registers.h" +#include "aqt1000-reg-defaults.h" +#include "aqt1000-internal.h" + +static bool aqt1000_is_readable_register(struct device *dev, unsigned int reg) +{ + u8 pg_num, reg_offset; + const u8 *reg_tbl = NULL; + + /* + * Get the page number from MSB of codec register. If its 0x80, assign + * the corresponding page index PAGE_0x80. + */ + pg_num = reg >> 0x8; + if (pg_num == 0x80) + pg_num = AQT1000_PAGE_128; + else if (pg_num > 15) + return false; + + reg_tbl = aqt1000_reg[pg_num]; + reg_offset = reg & 0xFF; + + if (reg_tbl && reg_tbl[reg_offset]) + return true; + else + return false; +} + +static bool aqt1000_is_volatile_register(struct device *dev, unsigned int reg) +{ + u8 pg_num, reg_offset; + const u8 *reg_tbl = NULL; + + pg_num = reg >> 0x8; + if (pg_num == 0x80) + pg_num = AQT1000_PAGE_128; + else if (pg_num > 15) + return false; + + reg_tbl = aqt1000_reg[pg_num]; + reg_offset = reg & 0xFF; + + if (reg_tbl && reg_tbl[reg_offset] == AQT1000_RO) + return true; + + /* IIR Coeff registers are not cacheable */ + if ((reg >= AQT1000_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL) && + (reg <= AQT1000_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL)) + return true; + + if ((reg >= AQT1000_CDC_ANC0_IIR_COEFF_1_CTL) && + (reg <= AQT1000_CDC_ANC0_FB_GAIN_CTL)) + return true; + + if ((reg >= AQT1000_CDC_ANC1_IIR_COEFF_1_CTL) && + (reg <= AQT1000_CDC_ANC1_FB_GAIN_CTL)) + return true; + + /* + * Need to mark volatile for registers that are writable but + * only few bits are read-only + */ + switch (reg) { + case AQT1000_BUCK_5V_CTRL_CCL_1: + case AQT1000_BIAS_CCOMP_FINE_ADJ: + case AQT1000_ANA_BIAS: + case AQT1000_BUCK_5V_IBIAS_CTL_4: + case AQT1000_BUCK_5V_CTRL_CCL_2: + case AQT1000_CHIP_CFG0_RST_CTL: + case AQT1000_CHIP_CFG0_CLK_CTL_CDC_DIG: + case AQT1000_CHIP_CFG0_CLK_CFG_MCLK: + case AQT1000_CHIP_CFG0_EFUSE_CTL: + case AQT1000_CDC_CLK_RST_CTRL_FS_CNT_CONTROL: + case AQT1000_CDC_CLK_RST_CTRL_MCLK_CONTROL: + case AQT1000_ANA_RX_SUPPLIES: + case AQT1000_ANA_MBHC_MECH: + case AQT1000_ANA_MBHC_ELECT: + case AQT1000_ANA_MBHC_ZDET: + case AQT1000_ANA_MICB1: + case AQT1000_BUCK_5V_EN_CTL: + return true; + } + + return false; +} + +struct regmap_config aqt1000_regmap_config = { + .reg_bits = 16, + .val_bits = 8, + .cache_type = REGCACHE_RBTREE, + .reg_defaults = aqt1000_defaults, + .num_reg_defaults = ARRAY_SIZE(aqt1000_defaults), + .max_register = AQT1000_MAX_REGISTER, + .volatile_reg = aqt1000_is_volatile_register, + .readable_reg = aqt1000_is_readable_register, +}; diff --git a/audio-kernel/asoc/codecs/aqt1000/aqt1000-routing.h b/audio-kernel/asoc/codecs/aqt1000/aqt1000-routing.h new file mode 100644 index 000000000..7ae3276ff --- /dev/null +++ b/audio-kernel/asoc/codecs/aqt1000/aqt1000-routing.h @@ -0,0 +1,174 @@ +/* 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 AQT1000_ROUTING_H +#define AQT1000_ROUTING_H + +#include + +const struct snd_soc_dapm_route aqt_audio_map[] = { + + /* CDC Tx interface */ + + {"AQT AIF1 CAP", NULL, "AQT AIF1 CAP Mixer"}, + {"AQT AIF1 CAP Mixer", "TX0", "AQT TX0_MUX"}, + {"AQT AIF1 CAP Mixer", "TX1", "AQT TX1_MUX"}, + + {"AQT TX0_MUX", "DEC_L", "AQT ADC0 MUX"}, + {"AQT TX0_MUX", "DEC_R", "AQT ADC1 MUX"}, + {"AQT TX0_MUX", "DEC_V", "AQT ADC2 MUX"}, + + {"AQT TX1_MUX", "DEC_L", "AQT ADC0 MUX"}, + {"AQT TX1_MUX", "DEC_R", "AQT ADC1 MUX"}, + {"AQT TX1_MUX", "DEC_V", "AQT ADC2 MUX"}, + + {"AQT ADC0 MUX", "AMIC", "AQT AMIC0_MUX"}, + {"AQT ADC0 MUX", "ANC_FB0", "AQT ANC_FB_TUNE0"}, + {"AQT ADC0 MUX", "ANC_FB1", "AQT ANC_FB_TUNE1"}, + + {"AQT ADC1 MUX", "AMIC", "AQT AMIC1_MUX"}, + {"AQT ADC1 MUX", "ANC_FB0", "AQT ANC_FB_TUNE0"}, + {"AQT ADC1 MUX", "ANC_FB1", "AQT ANC_FB_TUNE1"}, + + {"AQT ADC2 MUX", "AMIC", "AQT AMIC2_MUX"}, + {"AQT ADC2 MUX", "ANC_FB0", "AQT ANC_FB_TUNE0"}, + {"AQT ADC2 MUX", "ANC_FB1", "AQT ANC_FB_TUNE1"}, + + {"AQT AMIC0_MUX", "ADC_L", "AQT ADC_L"}, + {"AQT AMIC0_MUX", "ADC_R", "AQT ADC_R"}, + {"AQT AMIC0_MUX", "ADC_V", "AQT ADC_V"}, + + {"AQT AMIC1_MUX", "ADC_L", "AQT ADC_L"}, + {"AQT AMIC1_MUX", "ADC_R", "AQT ADC_R"}, + {"AQT AMIC1_MUX", "ADC_V", "AQT ADC_V"}, + + {"AQT AMIC2_MUX", "ADC_L", "AQT ADC_L"}, + {"AQT AMIC2_MUX", "ADC_R", "AQT ADC_R"}, + {"AQT AMIC2_MUX", "ADC_V", "AQT ADC_V"}, + + {"AQT ADC_L", NULL, "AQT AMIC1"}, + {"AQT ADC_R", NULL, "AQT AMIC2"}, + {"AQT ADC_V", NULL, "AQT AMIC3"}, + + {"AQT AMIC10_MUX", "ADC_L", "AQT ADC_L"}, + {"AQT AMIC10_MUX", "ADC_R", "AQT ADC_R"}, + {"AQT AMIC10_MUX", "ADC_V", "AQT ADC_V"}, + + {"AQT AMIC11_MUX", "ADC_L", "AQT ADC_L"}, + {"AQT AMIC11_MUX", "ADC_R", "AQT ADC_R"}, + {"AQT AMIC11_MUX", "ADC_V", "AQT ADC_V"}, + + {"AQT AMIC12_MUX", "ADC_L", "AQT ADC_L"}, + {"AQT AMIC12_MUX", "ADC_R", "AQT ADC_R"}, + {"AQT AMIC12_MUX", "ADC_V", "AQT ADC_V"}, + + {"AQT AMIC13_MUX", "ADC_L", "AQT ADC_L"}, + {"AQT AMIC13_MUX", "ADC_R", "AQT ADC_R"}, + {"AQT AMIC13_MUX", "ADC_V", "AQT ADC_V"}, + + {"AQT ANC OUT HPHL Enable", "Switch", "AQT AMIC10_MUX"}, + {"AQT ANC OUT HPHL Enable", "Switch", "AQT AMIC11_MUX"}, + {"AQT ANC OUT HPHR Enable", "Switch", "AQT AMIC12_MUX"}, + {"AQT ANC OUT HPHR Enable", "Switch", "AQT AMIC13_MUX"}, + + {"AQT RX INT1 MIX2", NULL, "AQT ANC OUT HPHL Enable"}, + {"AQT RX INT2 MIX2", NULL, "AQT ANC OUT HPHR Enable"}, + + {"AQT ANC0 FB MUX", "ANC_IN_HPHL", "AQT RX INT1 MIX2"}, + {"AQT ANC1 FB MUX", "ANC_IN_HPHR", "AQT RX INT2 MIX2"}, + + {"AQT I2S_L RX", NULL, "AQT AIF1 PB"}, + {"AQT I2S_R RX", NULL, "AQT AIF1 PB"}, + + {"AQT RX INT1_1 MUX", "I2S0_L", "AQT I2S_L RX"}, + {"AQT RX INT1_1 MUX", "I2S0_R", "AQT I2S_R RX"}, + {"AQT RX INT1_1 MUX", "DEC_L", "AQT ADC0 MUX"}, + {"AQT RX INT1_1 MUX", "DEC_R", "AQT ADC1 MUX"}, + {"AQT RX INT1_1 MUX", "DEC_V", "AQT ADC2 MUX"}, + + {"AQT RX INT2_1 MUX", "I2S0_L", "AQT I2S_L RX"}, + {"AQT RX INT2_1 MUX", "I2S0_R", "AQT I2S_R RX"}, + {"AQT RX INT2_1 MUX", "DEC_L", "AQT ADC0 MUX"}, + {"AQT RX INT2_1 MUX", "DEC_R", "AQT ADC1 MUX"}, + {"AQT RX INT2_1 MUX", "DEC_V", "AQT ADC2 MUX"}, + + {"AQT RX INT1_2 MUX", "I2S0_L", "AQT I2S_L RX"}, + {"AQT RX INT1_2 MUX", "I2S0_R", "AQT I2S_R RX"}, + {"AQT RX INT1_2 MUX", "DEC_L", "AQT ADC0 MUX"}, + {"AQT RX INT1_2 MUX", "DEC_R", "AQT ADC1 MUX"}, + {"AQT RX INT1_2 MUX", "DEC_V", "AQT ADC2 MUX"}, + {"AQT RX INT1_2 MUX", "IIR0", "AQT IIR0"}, + + {"AQT RX INT2_2 MUX", "I2S0_L", "AQT I2S_L RX"}, + {"AQT RX INT2_2 MUX", "I2S0_R", "AQT I2S_R RX"}, + {"AQT RX INT2_2 MUX", "DEC_L", "AQT ADC0 MUX"}, + {"AQT RX INT2_2 MUX", "DEC_R", "AQT ADC1 MUX"}, + {"AQT RX INT2_2 MUX", "DEC_V", "AQT ADC2 MUX"}, + {"AQT RX INT2_2 MUX", "IIR0", "AQT IIR0"}, + + {"AQT RX INT1_1 INTERP", NULL, "AQT RX INT1_1 MUX"}, + {"AQT RX INT1 MIX1", NULL, "AQT RX INT1_1 INTERP"}, + {"AQT RX INT1 MIX2", NULL, "AQT RX INT1 MIX1"}, + + {"AQT RX INT1_2 INTERP", NULL, "AQT RX INT1_2 MUX"}, + {"AQT RX INT1 MIX1", NULL, "AQT RX INT1_2 INTERP"}, + + {"AQT ASRC0 MUX", "ASRC_IN_HPHL", "AQT RX INT1_2 INTERP"}, + {"AQT RX INT1 MIX1", "HPHL Switch", "AQT ASRC0 MUX"}, + + {"AQT RX INT2_1 INTERP", NULL, "AQT RX INT2_1 MUX"}, + {"AQT RX INT2 MIX1", NULL, "AQT RX INT2_1 INTERP"}, + {"AQT RX INT2 MIX2", NULL, "AQT RX INT2 MIX1"}, + + {"AQT RX INT2_2 INTERP", NULL, "AQT RX INT2_2 MUX"}, + {"AQT RX INT2 MIX1", NULL, "AQT RX INT2_2 INTERP"}, + + {"AQT ASRC1 MUX", "ASRC_IN_HPHR", "AQT RX INT2_2 INTERP"}, + {"AQT RX INT2 MIX1", "HPHR Switch", "AQT ASRC1 MUX"}, + + {"AQT RX INT1 DEM MUX", "CLSH_DSM_OUT", "AQT RX INT1 MIX2"}, + {"AQT RX INT1 DAC", NULL, "AQT RX INT1 DEM MUX"}, + {"AQT RX INT1 DAC", NULL, "AQT RX_BIAS"}, + {"AQT RX_BIAS", NULL, "AQT MCLK"}, + {"AQT MIC BIAS1", NULL, "AQT MCLK"}, + {"AQT HPHL PA", NULL, "AQT RX INT1 DAC"}, + {"AQT HPHL", NULL, "AQT HPHL PA"}, + + {"AQT RX INT2 DEM MUX", "CLSH_DSM_OUT", "AQT RX INT2 MIX2"}, + {"AQT RX INT2 DAC", NULL, "AQT RX INT2 DEM MUX"}, + {"AQT RX INT2 DAC", NULL, "AQT RX_BIAS"}, + {"AQT HPHR PA", NULL, "AQT RX INT2 DAC"}, + {"AQT HPHR", NULL, "AQT HPHR PA"}, + + {"AQT ANC HPHL PA", NULL, "AQT RX INT1 DAC"}, + {"AQT ANC HPHL", NULL, "AQT ANC HPHL PA"}, + + {"AQT ANC HPHR PA", NULL, "AQT RX INT2 DAC"}, + {"AQT ANC HPHR", NULL, "AQT ANC HPHR PA"}, + + {"AQT IIR0", NULL, "AQT ADC2 MUX"}, + {"AQT SRC0", NULL, "AQT IIR0"}, + {"AQT RX ST MUX", "SRC0", "AQT SRC0"}, + + {"AQT RX INT1 MIX2", NULL, "AQT RX ST MUX"}, + {"AQT RX INT2 MIX2", NULL, "AQT RX ST MUX"}, + + /* Native clk main path routing */ + {"AQT RX INT1_1 NATIVE MUX", "ON", "AQT RX INT1_1 MUX"}, + {"AQT RX INT1_1 INTERP", NULL, "AQT RX INT1_1 NATIVE MUX"}, + {"AQT RX INT1_1 NATIVE MUX", NULL, "AQT RX INT1 NATIVE SUPPLY"}, + + {"AQT RX INT2_1 NATIVE MUX", "ON", "AQT RX INT2_1 MUX"}, + {"AQT RX INT2_1 INTERP", NULL, "AQT RX INT2_1 NATIVE MUX"}, + {"AQT RX INT2_1 NATIVE MUX", NULL, "AQT RX INT2 NATIVE SUPPLY"}, +}; + +#endif diff --git a/audio-kernel/asoc/codecs/aqt1000/aqt1000-utils.c b/audio-kernel/asoc/codecs/aqt1000/aqt1000-utils.c new file mode 100644 index 000000000..f1c16de8a --- /dev/null +++ b/audio-kernel/asoc/codecs/aqt1000/aqt1000-utils.c @@ -0,0 +1,199 @@ +/* Copyright (c) 2016-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 +#include +#include +#include +#include +#include +#include "aqt1000.h" +#include "aqt1000-utils.h" + +#define REG_BYTES 2 +#define VAL_BYTES 1 +/* + * Page Register Address that APP Proc uses to + * access codec registers is identified as 0x00 + */ +#define PAGE_REG_ADDR 0x00 + +static int aqt_page_write(struct aqt1000 *aqt, unsigned short *reg) +{ + int ret = 0; + unsigned short c_reg, reg_addr; + u8 pg_num, prev_pg_num; + + c_reg = *reg; + pg_num = c_reg >> 8; + reg_addr = c_reg & 0xff; + if (aqt->prev_pg_valid) { + prev_pg_num = aqt->prev_pg; + if (prev_pg_num != pg_num) { + ret = aqt->write_dev( + aqt, PAGE_REG_ADDR, + (void *) &pg_num, 1); + if (ret < 0) + dev_err(aqt->dev, + "%s: page write error, pg_num: 0x%x\n", + __func__, pg_num); + else { + aqt->prev_pg = pg_num; + dev_dbg(aqt->dev, "%s: Page 0x%x Write to 0x00\n", + __func__, pg_num); + } + } + } else { + ret = aqt->write_dev( + aqt, PAGE_REG_ADDR, (void *) &pg_num, 1); + if (ret < 0) + dev_err(aqt->dev, + "%s: page write error, pg_num: 0x%x\n", + __func__, pg_num); + else { + aqt->prev_pg = pg_num; + aqt->prev_pg_valid = true; + dev_dbg(aqt->dev, "%s: Page 0x%x Write to 0x00\n", + __func__, pg_num); + } + } + *reg = reg_addr; + return ret; +} + +static int regmap_bus_read(void *context, const void *reg, size_t reg_size, + void *val, size_t val_size) +{ + struct device *dev = context; + struct aqt1000 *aqt = dev_get_drvdata(dev); + unsigned short c_reg, rreg; + int ret, i; + + if (!aqt) { + dev_err(dev, "%s: aqt is NULL\n", __func__); + return -EINVAL; + } + if (!reg || !val) { + dev_err(dev, "%s: reg or val is NULL\n", __func__); + return -EINVAL; + } + + if (reg_size != REG_BYTES) { + dev_err(dev, "%s: register size %zd bytes, not supported\n", + __func__, reg_size); + return -EINVAL; + } + + mutex_lock(&aqt->io_lock); + c_reg = *(u16 *)reg; + rreg = c_reg; + + ret = aqt_page_write(aqt, &c_reg); + if (ret) + goto err; + ret = aqt->read_dev(aqt, c_reg, val, val_size); + if (ret < 0) + dev_err(dev, "%s: Codec read failed (%d), reg: 0x%x, size:%zd\n", + __func__, ret, rreg, val_size); + else { + for (i = 0; i < val_size; i++) + dev_dbg(dev, "%s: Read 0x%02x from 0x%x\n", + __func__, ((u8 *)val)[i], rreg + i); + } +err: + mutex_unlock(&aqt->io_lock); + return ret; +} + +static int regmap_bus_gather_write(void *context, + const void *reg, size_t reg_size, + const void *val, size_t val_size) +{ + struct device *dev = context; + struct aqt1000 *aqt = dev_get_drvdata(dev); + unsigned short c_reg, rreg; + int ret, i; + + if (!aqt) { + dev_err(dev, "%s: aqt is NULL\n", __func__); + return -EINVAL; + } + if (!reg || !val) { + dev_err(dev, "%s: reg or val is NULL\n", __func__); + return -EINVAL; + } + if (reg_size != REG_BYTES) { + dev_err(dev, "%s: register size %zd bytes, not supported\n", + __func__, reg_size); + return -EINVAL; + } + mutex_lock(&aqt->io_lock); + c_reg = *(u16 *)reg; + rreg = c_reg; + + ret = aqt_page_write(aqt, &c_reg); + if (ret) + goto err; + + for (i = 0; i < val_size; i++) + dev_dbg(dev, "Write %02x to 0x%x\n", ((u8 *)val)[i], + rreg + i); + + ret = aqt->write_dev(aqt, c_reg, (void *) val, val_size); + if (ret < 0) + dev_err(dev, "%s: Codec write failed (%d), reg:0x%x, size:%zd\n", + __func__, ret, rreg, val_size); + +err: + mutex_unlock(&aqt->io_lock); + return ret; +} + +static int regmap_bus_write(void *context, const void *data, size_t count) +{ + struct device *dev = context; + struct aqt1000 *aqt = dev_get_drvdata(dev); + + if (!aqt) + return -EINVAL; + + WARN_ON(count < REG_BYTES); + + return regmap_bus_gather_write(context, data, REG_BYTES, + data + REG_BYTES, + count - REG_BYTES); +} + +static struct regmap_bus regmap_bus_config = { + .write = regmap_bus_write, + .gather_write = regmap_bus_gather_write, + .read = regmap_bus_read, + .reg_format_endian_default = REGMAP_ENDIAN_NATIVE, + .val_format_endian_default = REGMAP_ENDIAN_NATIVE, +}; + +/* + * aqt1000_regmap_init: + * Initialize aqt1000 register map + * + * @dev: pointer to wcd device + * @config: pointer to register map config + * + * Returns pointer to regmap structure for success + * or NULL in case of failure. + */ +struct regmap *aqt1000_regmap_init(struct device *dev, + const struct regmap_config *config) +{ + return devm_regmap_init(dev, ®map_bus_config, dev, config); +} +EXPORT_SYMBOL(aqt1000_regmap_init); diff --git a/audio-kernel/asoc/codecs/aqt1000/aqt1000-utils.h b/audio-kernel/asoc/codecs/aqt1000/aqt1000-utils.h new file mode 100644 index 000000000..9f68cf12b --- /dev/null +++ b/audio-kernel/asoc/codecs/aqt1000/aqt1000-utils.h @@ -0,0 +1,22 @@ +/* Copyright (c) 2016-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 __WCD9XXX_UTILS_H__ +#define __WCD9XXX_UTILS_H__ + +#include +#include +#include + +struct regmap *aqt1000_regmap_init(struct device *dev, + const struct regmap_config *config); +#endif diff --git a/audio-kernel/asoc/codecs/aqt1000/aqt1000.c b/audio-kernel/asoc/codecs/aqt1000/aqt1000.c new file mode 100644 index 000000000..8463d8223 --- /dev/null +++ b/audio-kernel/asoc/codecs/aqt1000/aqt1000.c @@ -0,0 +1,3470 @@ +/* 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "aqt1000-registers.h" +#include "aqt1000.h" +#include "aqt1000-api.h" +#include "aqt1000-mbhc.h" +#include "aqt1000-routing.h" +#include "../wcdcal-hwdep.h" +#include "aqt1000-internal.h" + +#define AQT1000_TX_UNMUTE_DELAY_MS 40 +#define TX_HPF_CUT_OFF_FREQ_MASK 0x60 +#define CF_MIN_3DB_4HZ 0x0 +#define CF_MIN_3DB_75HZ 0x1 +#define CF_MIN_3DB_150HZ 0x2 + +#define AQT_VERSION_ENTRY_SIZE 17 +#define AQT_VOUT_CTL_TO_MICB(x) (1000 + x *50) + +static struct interp_sample_rate sr_val_tbl[] = { + {8000, 0x0}, {16000, 0x1}, {32000, 0x3}, {48000, 0x4}, {96000, 0x5}, + {192000, 0x6}, {384000, 0x7}, {44100, 0x9}, {88200, 0xA}, + {176400, 0xB}, {352800, 0xC}, +}; + +static int tx_unmute_delay = AQT1000_TX_UNMUTE_DELAY_MS; +module_param(tx_unmute_delay, int, 0664); +MODULE_PARM_DESC(tx_unmute_delay, "delay to unmute the tx path"); + +static void aqt_codec_set_tx_hold(struct snd_soc_codec *, u16, bool); + +/* Cutoff frequency for high pass filter */ +static const char * const cf_text[] = { + "CF_NEG_3DB_4HZ", "CF_NEG_3DB_75HZ", "CF_NEG_3DB_150HZ" +}; + +static const char * const rx_cf_text[] = { + "CF_NEG_3DB_4HZ", "CF_NEG_3DB_75HZ", "CF_NEG_3DB_150HZ", + "CF_NEG_3DB_0P48HZ" +}; + +struct aqt1000_anc_header { + u32 reserved[3]; + u32 num_anc_slots; +}; + +static SOC_ENUM_SINGLE_DECL(cf_dec0_enum, AQT1000_CDC_TX0_TX_PATH_CFG0, 5, + cf_text); +static SOC_ENUM_SINGLE_DECL(cf_dec1_enum, AQT1000_CDC_TX1_TX_PATH_CFG0, 5, + cf_text); +static SOC_ENUM_SINGLE_DECL(cf_dec2_enum, AQT1000_CDC_TX2_TX_PATH_CFG0, 5, + cf_text); +static SOC_ENUM_SINGLE_DECL(cf_int1_1_enum, AQT1000_CDC_RX1_RX_PATH_CFG2, 0, + rx_cf_text); +static SOC_ENUM_SINGLE_DECL(cf_int1_2_enum, AQT1000_CDC_RX1_RX_PATH_MIX_CFG, 2, + rx_cf_text); +static SOC_ENUM_SINGLE_DECL(cf_int2_1_enum, AQT1000_CDC_RX2_RX_PATH_CFG2, 0, + rx_cf_text); +static SOC_ENUM_SINGLE_DECL(cf_int2_2_enum, AQT1000_CDC_RX2_RX_PATH_MIX_CFG, 2, + rx_cf_text); + +static const DECLARE_TLV_DB_SCALE(hph_gain, -3000, 150, 0); +static const DECLARE_TLV_DB_SCALE(analog_gain, 0, 150, 0); +static const DECLARE_TLV_DB_SCALE(digital_gain, 0, 1, 0); + +static int aqt_get_anc_slot(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct aqt1000 *aqt = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = aqt->anc_slot; + return 0; +} + +static int aqt_put_anc_slot(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct aqt1000 *aqt = snd_soc_codec_get_drvdata(codec); + + aqt->anc_slot = ucontrol->value.integer.value[0]; + return 0; +} + +static int aqt_get_anc_func(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct aqt1000 *aqt = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = (aqt->anc_func == true ? 1 : 0); + return 0; +} + +static int aqt_put_anc_func(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct aqt1000 *aqt = snd_soc_codec_get_drvdata(codec); + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + + mutex_lock(&aqt->codec_mutex); + aqt->anc_func = (!ucontrol->value.integer.value[0] ? false : true); + dev_dbg(codec->dev, "%s: anc_func %x", __func__, aqt->anc_func); + + if (aqt->anc_func == true) { + snd_soc_dapm_enable_pin(dapm, "ANC HPHL PA"); + snd_soc_dapm_enable_pin(dapm, "ANC HPHR PA"); + snd_soc_dapm_enable_pin(dapm, "ANC HPHL"); + snd_soc_dapm_enable_pin(dapm, "ANC HPHR"); + snd_soc_dapm_disable_pin(dapm, "HPHL PA"); + snd_soc_dapm_disable_pin(dapm, "HPHR PA"); + snd_soc_dapm_disable_pin(dapm, "HPHL"); + snd_soc_dapm_disable_pin(dapm, "HPHR"); + } else { + snd_soc_dapm_disable_pin(dapm, "ANC HPHL PA"); + snd_soc_dapm_disable_pin(dapm, "ANC HPHR PA"); + snd_soc_dapm_disable_pin(dapm, "ANC HPHL"); + snd_soc_dapm_disable_pin(dapm, "ANC HPHR"); + snd_soc_dapm_enable_pin(dapm, "HPHL"); + snd_soc_dapm_enable_pin(dapm, "HPHR"); + snd_soc_dapm_enable_pin(dapm, "HPHL PA"); + snd_soc_dapm_enable_pin(dapm, "HPHR PA"); + } + mutex_unlock(&aqt->codec_mutex); + + snd_soc_dapm_sync(dapm); + return 0; +} + +static const char *const aqt_anc_func_text[] = {"OFF", "ON"}; +static const struct soc_enum aqt_anc_func_enum = + SOC_ENUM_SINGLE_EXT(2, aqt_anc_func_text); + +static int aqt_rx_hph_mode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct aqt1000 *aqt = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = aqt->hph_mode; + return 0; +} + +static int aqt_rx_hph_mode_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct aqt1000 *aqt = snd_soc_codec_get_drvdata(codec); + u32 mode_val; + + mode_val = ucontrol->value.enumerated.item[0]; + + dev_dbg(codec->dev, "%s: mode: %d\n", __func__, mode_val); + + if (mode_val == 0) { + dev_warn(codec->dev, "%s:Invalid HPH Mode, default to Cls-H LOHiFi\n", + __func__); + mode_val = CLS_H_LOHIFI; + } + aqt->hph_mode = mode_val; + return 0; +} + +static const char * const rx_hph_mode_mux_text[] = { + "CLS_H_INVALID", "CLS_H_HIFI", "CLS_H_LP", "CLS_AB", "CLS_H_LOHIFI", + "CLS_H_ULP", "CLS_AB_HIFI", +}; + +static const struct soc_enum rx_hph_mode_mux_enum = + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(rx_hph_mode_mux_text), + rx_hph_mode_mux_text); + +static int aqt_iir_enable_audio_mixer_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + int band_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + + ucontrol->value.integer.value[0] = (snd_soc_read(codec, + AQT1000_CDC_SIDETONE_IIR0_IIR_CTL) & + (1 << band_idx)) != 0; + + dev_dbg(codec->dev, "%s: IIR0 band #%d enable %d\n", __func__, + band_idx, (uint32_t)ucontrol->value.integer.value[0]); + + return 0; +} + +static int aqt_iir_enable_audio_mixer_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + int band_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + bool iir_band_en_status; + int value = ucontrol->value.integer.value[0]; + + /* Mask first 5 bits, 6-8 are reserved */ + snd_soc_update_bits(codec, AQT1000_CDC_SIDETONE_IIR0_IIR_CTL, + (1 << band_idx), (value << band_idx)); + + iir_band_en_status = ((snd_soc_read(codec, + AQT1000_CDC_SIDETONE_IIR0_IIR_CTL) & + (1 << band_idx)) != 0); + dev_dbg(codec->dev, "%s: IIR0 band #%d enable %d\n", __func__, + band_idx, iir_band_en_status); + + return 0; +} + +static uint32_t aqt_get_iir_band_coeff(struct snd_soc_codec *codec, + int band_idx, int coeff_idx) +{ + uint32_t value = 0; + + /* Address does not automatically update if reading */ + snd_soc_write(codec, + AQT1000_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL, + ((band_idx * BAND_MAX + coeff_idx) + * sizeof(uint32_t)) & 0x7F); + + value |= snd_soc_read(codec, AQT1000_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL); + + snd_soc_write(codec, AQT1000_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL, + ((band_idx * BAND_MAX + coeff_idx) + * sizeof(uint32_t) + 1) & 0x7F); + + value |= (snd_soc_read(codec, + AQT1000_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL) << 8); + + snd_soc_write(codec, + AQT1000_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL, + ((band_idx * BAND_MAX + coeff_idx) + * sizeof(uint32_t) + 2) & 0x7F); + + value |= (snd_soc_read(codec, + AQT1000_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL) << 16); + + snd_soc_write(codec, + AQT1000_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL, + ((band_idx * BAND_MAX + coeff_idx) + * sizeof(uint32_t) + 3) & 0x7F); + + /* Mask bits top 2 bits since they are reserved */ + value |= ((snd_soc_read(codec, + AQT1000_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL) + & 0x3F) << 24); + + return value; +} + +static int aqt_iir_band_audio_mixer_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + int band_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + + ucontrol->value.integer.value[0] = + aqt_get_iir_band_coeff(codec, band_idx, 0); + ucontrol->value.integer.value[1] = + aqt_get_iir_band_coeff(codec, band_idx, 1); + ucontrol->value.integer.value[2] = + aqt_get_iir_band_coeff(codec, band_idx, 2); + ucontrol->value.integer.value[3] = + aqt_get_iir_band_coeff(codec, band_idx, 3); + ucontrol->value.integer.value[4] = + aqt_get_iir_band_coeff(codec, band_idx, 4); + + dev_dbg(codec->dev, "%s: IIR band #%d b0 = 0x%x\n" + "%s: IIR band #%d b1 = 0x%x\n" + "%s: IIR band #%d b2 = 0x%x\n" + "%s: IIR band #%d a1 = 0x%x\n" + "%s: IIR band #%d a2 = 0x%x\n", + __func__, band_idx, + (uint32_t)ucontrol->value.integer.value[0], + __func__, band_idx, + (uint32_t)ucontrol->value.integer.value[1], + __func__, band_idx, + (uint32_t)ucontrol->value.integer.value[2], + __func__, band_idx, + (uint32_t)ucontrol->value.integer.value[3], + __func__, band_idx, + (uint32_t)ucontrol->value.integer.value[4]); + + return 0; +} + +static void aqt_set_iir_band_coeff(struct snd_soc_codec *codec, + int band_idx, uint32_t value) +{ + snd_soc_write(codec, + (AQT1000_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL), + (value & 0xFF)); + + snd_soc_write(codec, + (AQT1000_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL), + (value >> 8) & 0xFF); + + snd_soc_write(codec, + (AQT1000_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL), + (value >> 16) & 0xFF); + + /* Mask top 2 bits, 7-8 are reserved */ + snd_soc_write(codec, + (AQT1000_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL), + (value >> 24) & 0x3F); +} + +static int aqt_iir_band_audio_mixer_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + int band_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + int coeff_idx; + + /* + * Mask top bit it is reserved + * Updates addr automatically for each B2 write + */ + snd_soc_write(codec, + (AQT1000_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL), + (band_idx * BAND_MAX * sizeof(uint32_t)) & 0x7F); + + for (coeff_idx = 0; coeff_idx < AQT1000_CDC_SIDETONE_IIR_COEFF_MAX; + coeff_idx++) { + aqt_set_iir_band_coeff(codec, band_idx, + ucontrol->value.integer.value[coeff_idx]); + } + + dev_dbg(codec->dev, "%s: IIR band #%d b0 = 0x%x\n" + "%s: IIR band #%d b1 = 0x%x\n" + "%s: IIR band #%d b2 = 0x%x\n" + "%s: IIR band #%d a1 = 0x%x\n" + "%s: IIR band #%d a2 = 0x%x\n", + __func__, band_idx, + aqt_get_iir_band_coeff(codec, band_idx, 0), + __func__, band_idx, + aqt_get_iir_band_coeff(codec, band_idx, 1), + __func__, band_idx, + aqt_get_iir_band_coeff(codec, band_idx, 2), + __func__, band_idx, + aqt_get_iir_band_coeff(codec, band_idx, 3), + __func__, band_idx, + aqt_get_iir_band_coeff(codec, band_idx, 4)); + + return 0; +} + +static int aqt_compander_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + int comp = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + struct aqt1000 *aqt = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = aqt->comp_enabled[comp]; + return 0; +} + +static int aqt_compander_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct aqt1000 *aqt = snd_soc_codec_get_drvdata(codec); + int comp = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + int value = ucontrol->value.integer.value[0]; + + dev_dbg(codec->dev, "%s: Compander %d enable current %d, new %d\n", + __func__, comp + 1, aqt->comp_enabled[comp], value); + aqt->comp_enabled[comp] = value; + + /* Any specific register configuration for compander */ + switch (comp) { + case COMPANDER_1: + /* Set Gain Source Select based on compander enable/disable */ + snd_soc_update_bits(codec, AQT1000_HPH_L_EN, 0x20, + (value ? 0x00:0x20)); + break; + case COMPANDER_2: + snd_soc_update_bits(codec, AQT1000_HPH_R_EN, 0x20, + (value ? 0x00:0x20)); + break; + default: + /* + * if compander is not enabled for any interpolator, + * it does not cause any audio failure, so do not + * return error in this case, but just print a log + */ + dev_warn(codec->dev, "%s: unknown compander: %d\n", + __func__, comp); + }; + return 0; +} + +static int aqt_hph_asrc_mode_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct aqt1000 *aqt = snd_soc_codec_get_drvdata(codec); + int index = -EINVAL; + + if (!strcmp(kcontrol->id.name, "AQT ASRC0 Output Mode")) + index = ASRC0; + if (!strcmp(kcontrol->id.name, "AQT ASRC1 Output Mode")) + index = ASRC1; + + if (aqt && (index >= 0) && (index < ASRC_MAX)) + aqt->asrc_output_mode[index] = + ucontrol->value.integer.value[0]; + + return 0; +} + +static int aqt_hph_asrc_mode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct aqt1000 *aqt = snd_soc_codec_get_drvdata(codec); + int val = 0; + int index = -EINVAL; + + if (!strcmp(kcontrol->id.name, "AQT ASRC0 Output Mode")) + index = ASRC0; + if (!strcmp(kcontrol->id.name, "AQT ASRC1 Output Mode")) + index = ASRC1; + + if (aqt && (index >= 0) && (index < ASRC_MAX)) + val = aqt->asrc_output_mode[index]; + + ucontrol->value.integer.value[0] = val; + + return 0; +} + +static const char * const asrc_mode_text[] = { + "INT", "FRAC" +}; +static SOC_ENUM_SINGLE_EXT_DECL(asrc_mode_enum, asrc_mode_text); + +static int aqt_hph_idle_detect_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct aqt1000 *aqt = snd_soc_codec_get_drvdata(codec); + int val = 0; + + if (aqt) + val = aqt->idle_det_cfg.hph_idle_detect_en; + + ucontrol->value.integer.value[0] = val; + + return 0; +} + +static int aqt_hph_idle_detect_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct aqt1000 *aqt = snd_soc_codec_get_drvdata(codec); + + if (aqt) + aqt->idle_det_cfg.hph_idle_detect_en = + ucontrol->value.integer.value[0]; + + return 0; +} + +static const char * const hph_idle_detect_text[] = { + "OFF", "ON" +}; + +static SOC_ENUM_SINGLE_EXT_DECL(hph_idle_detect_enum, hph_idle_detect_text); + +static int aqt_amic_pwr_lvl_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + u16 amic_reg = 0; + + if (!strcmp(kcontrol->id.name, "AQT AMIC_1_2 PWR MODE")) + amic_reg = AQT1000_ANA_AMIC1; + if (!strcmp(kcontrol->id.name, "AQT AMIC_3 PWR MODE")) + amic_reg = AQT1000_ANA_AMIC3; + + if (amic_reg) + ucontrol->value.integer.value[0] = + (snd_soc_read(codec, amic_reg) & + AQT1000_AMIC_PWR_LVL_MASK) >> + AQT1000_AMIC_PWR_LVL_SHIFT; + return 0; +} + +static int aqt_amic_pwr_lvl_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + u32 mode_val; + u16 amic_reg = 0; + + mode_val = ucontrol->value.enumerated.item[0]; + + dev_dbg(codec->dev, "%s: mode: %d\n", __func__, mode_val); + + if (!strcmp(kcontrol->id.name, "AQT AMIC_1_2 PWR MODE")) + amic_reg = AQT1000_ANA_AMIC1; + if (!strcmp(kcontrol->id.name, "AQT AMIC_3 PWR MODE")) + amic_reg = AQT1000_ANA_AMIC3; + + if (amic_reg) + snd_soc_update_bits(codec, amic_reg, AQT1000_AMIC_PWR_LVL_MASK, + mode_val << AQT1000_AMIC_PWR_LVL_SHIFT); + return 0; +} + +static const char * const amic_pwr_lvl_text[] = { + "LOW_PWR", "DEFAULT", "HIGH_PERF", "HYBRID" +}; + +static SOC_ENUM_SINGLE_EXT_DECL(amic_pwr_lvl_enum, amic_pwr_lvl_text); + +static const struct snd_kcontrol_new aqt_snd_controls[] = { + SOC_SINGLE_TLV("AQT HPHL Volume", AQT1000_HPH_L_EN, 0, 24, 1, hph_gain), + SOC_SINGLE_TLV("AQT HPHR Volume", AQT1000_HPH_R_EN, 0, 24, 1, hph_gain), + SOC_SINGLE_TLV("AQT ADC1 Volume", AQT1000_ANA_AMIC1, 0, 20, 0, + analog_gain), + SOC_SINGLE_TLV("AQT ADC2 Volume", AQT1000_ANA_AMIC2, 0, 20, 0, + analog_gain), + SOC_SINGLE_TLV("AQT ADC3 Volume", AQT1000_ANA_AMIC3, 0, 20, 0, + analog_gain), + + SOC_SINGLE_SX_TLV("AQT RX1 Digital Volume", AQT1000_CDC_RX1_RX_VOL_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("AQT RX2 Digital Volume", AQT1000_CDC_RX2_RX_VOL_CTL, + 0, -84, 40, digital_gain), + + SOC_SINGLE_SX_TLV("AQT DEC0 Volume", AQT1000_CDC_TX0_TX_VOL_CTL, 0, + -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("AQT DEC1 Volume", AQT1000_CDC_TX1_TX_VOL_CTL, 0, + -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("AQT DEC2 Volume", AQT1000_CDC_TX2_TX_VOL_CTL, 0, + -84, 40, digital_gain), + + SOC_SINGLE_SX_TLV("AQT IIR0 INP0 Volume", + AQT1000_CDC_SIDETONE_IIR0_IIR_GAIN_B1_CTL, 0, -84, 40, + digital_gain), + SOC_SINGLE_SX_TLV("AQT IIR0 INP1 Volume", + AQT1000_CDC_SIDETONE_IIR0_IIR_GAIN_B2_CTL, 0, -84, 40, + digital_gain), + SOC_SINGLE_SX_TLV("AQT IIR0 INP2 Volume", + AQT1000_CDC_SIDETONE_IIR0_IIR_GAIN_B3_CTL, 0, -84, 40, + digital_gain), + SOC_SINGLE_SX_TLV("AQT IIR0 INP3 Volume", + AQT1000_CDC_SIDETONE_IIR0_IIR_GAIN_B4_CTL, 0, -84, 40, + digital_gain), + SOC_SINGLE_EXT("AQT ANC Slot", SND_SOC_NOPM, 0, 100, 0, + aqt_get_anc_slot, aqt_put_anc_slot), + SOC_ENUM_EXT("AQT ANC Function", aqt_anc_func_enum, aqt_get_anc_func, + aqt_put_anc_func), + + SOC_ENUM("AQT TX0 HPF cut off", cf_dec0_enum), + SOC_ENUM("AQT TX1 HPF cut off", cf_dec1_enum), + SOC_ENUM("AQT TX2 HPF cut off", cf_dec2_enum), + + SOC_ENUM("AQT RX INT1_1 HPF cut off", cf_int1_1_enum), + SOC_ENUM("AQT RX INT1_2 HPF cut off", cf_int1_2_enum), + SOC_ENUM("AQT RX INT2_1 HPF cut off", cf_int2_1_enum), + SOC_ENUM("AQT RX INT2_2 HPF cut off", cf_int2_2_enum), + + SOC_ENUM_EXT("AQT RX HPH Mode", rx_hph_mode_mux_enum, + aqt_rx_hph_mode_get, aqt_rx_hph_mode_put), + + SOC_SINGLE_EXT("AQT IIR0 Enable Band1", IIR0, BAND1, 1, 0, + aqt_iir_enable_audio_mixer_get, + aqt_iir_enable_audio_mixer_put), + SOC_SINGLE_EXT("AQT IIR0 Enable Band2", IIR0, BAND2, 1, 0, + aqt_iir_enable_audio_mixer_get, + aqt_iir_enable_audio_mixer_put), + SOC_SINGLE_EXT("AQT IIR0 Enable Band3", IIR0, BAND3, 1, 0, + aqt_iir_enable_audio_mixer_get, + aqt_iir_enable_audio_mixer_put), + SOC_SINGLE_EXT("AQT IIR0 Enable Band4", IIR0, BAND4, 1, 0, + aqt_iir_enable_audio_mixer_get, + aqt_iir_enable_audio_mixer_put), + SOC_SINGLE_EXT("AQT IIR0 Enable Band5", IIR0, BAND5, 1, 0, + aqt_iir_enable_audio_mixer_get, + aqt_iir_enable_audio_mixer_put), + + SOC_SINGLE_MULTI_EXT("AQT IIR0 Band1", IIR0, BAND1, 255, 0, 5, + aqt_iir_band_audio_mixer_get, aqt_iir_band_audio_mixer_put), + SOC_SINGLE_MULTI_EXT("AQT IIR0 Band2", IIR0, BAND2, 255, 0, 5, + aqt_iir_band_audio_mixer_get, aqt_iir_band_audio_mixer_put), + SOC_SINGLE_MULTI_EXT("AQT IIR0 Band3", IIR0, BAND3, 255, 0, 5, + aqt_iir_band_audio_mixer_get, aqt_iir_band_audio_mixer_put), + SOC_SINGLE_MULTI_EXT("AQT IIR0 Band4", IIR0, BAND4, 255, 0, 5, + aqt_iir_band_audio_mixer_get, aqt_iir_band_audio_mixer_put), + SOC_SINGLE_MULTI_EXT("AQT IIR0 Band5", IIR0, BAND5, 255, 0, 5, + aqt_iir_band_audio_mixer_get, aqt_iir_band_audio_mixer_put), + + SOC_SINGLE_EXT("AQT COMP1 Switch", SND_SOC_NOPM, COMPANDER_1, 1, 0, + aqt_compander_get, aqt_compander_put), + SOC_SINGLE_EXT("AQT COMP2 Switch", SND_SOC_NOPM, COMPANDER_2, 1, 0, + aqt_compander_get, aqt_compander_put), + + SOC_ENUM_EXT("AQT ASRC0 Output Mode", asrc_mode_enum, + aqt_hph_asrc_mode_get, aqt_hph_asrc_mode_put), + SOC_ENUM_EXT("AQT ASRC1 Output Mode", asrc_mode_enum, + aqt_hph_asrc_mode_get, aqt_hph_asrc_mode_put), + + SOC_ENUM_EXT("AQT HPH Idle Detect", hph_idle_detect_enum, + aqt_hph_idle_detect_get, aqt_hph_idle_detect_put), + + SOC_ENUM_EXT("AQT AMIC_1_2 PWR MODE", amic_pwr_lvl_enum, + aqt_amic_pwr_lvl_get, aqt_amic_pwr_lvl_put), + SOC_ENUM_EXT("AQT AMIC_3 PWR MODE", amic_pwr_lvl_enum, + aqt_amic_pwr_lvl_get, aqt_amic_pwr_lvl_put), +}; + +static int aqt_codec_enable_rx_bias(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct aqt1000 *aqt = snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + aqt->rx_bias_count++; + if (aqt->rx_bias_count == 1) { + snd_soc_update_bits(codec, AQT1000_ANA_RX_SUPPLIES, + 0x01, 0x01); + } + break; + case SND_SOC_DAPM_POST_PMD: + aqt->rx_bias_count--; + if (!aqt->rx_bias_count) + snd_soc_update_bits(codec, AQT1000_ANA_RX_SUPPLIES, + 0x01, 0x00); + break; + }; + dev_dbg(codec->dev, "%s: Current RX BIAS user count: %d\n", __func__, + aqt->rx_bias_count); + + return 0; +} + +/* + * aqt_mbhc_micb_adjust_voltage: adjust specific micbias voltage + * @codec: handle to snd_soc_codec * + * @req_volt: micbias voltage to be set + * @micb_num: micbias to be set, e.g. micbias1 or micbias2 + * + * return 0 if adjustment is success or error code in case of failure + */ +int aqt_mbhc_micb_adjust_voltage(struct snd_soc_codec *codec, + int req_volt, int micb_num) +{ + struct aqt1000 *aqt; + int cur_vout_ctl, req_vout_ctl; + int micb_reg, micb_val, micb_en; + int ret = 0; + + if (!codec) { + pr_err("%s: Invalid codec pointer\n", __func__); + return -EINVAL; + } + + if (micb_num != MIC_BIAS_1) + return -EINVAL; + else + micb_reg = AQT1000_ANA_MICB1; + + aqt = snd_soc_codec_get_drvdata(codec); + mutex_lock(&aqt->micb_lock); + + /* + * If requested micbias voltage is same as current micbias + * voltage, then just return. Otherwise, adjust voltage as + * per requested value. If micbias is already enabled, then + * to avoid slow micbias ramp-up or down enable pull-up + * momentarily, change the micbias value and then re-enable + * micbias. + */ + micb_val = snd_soc_read(codec, micb_reg); + micb_en = (micb_val & 0xC0) >> 6; + cur_vout_ctl = micb_val & 0x3F; + + req_vout_ctl = aqt_get_micb_vout_ctl_val(req_volt); + if (req_vout_ctl < 0) { + ret = -EINVAL; + goto exit; + } + if (cur_vout_ctl == req_vout_ctl) { + ret = 0; + goto exit; + } + + dev_dbg(codec->dev, "%s: micb_num: %d, cur_mv: %d, req_mv: %d, micb_en: %d\n", + __func__, micb_num, AQT_VOUT_CTL_TO_MICB(cur_vout_ctl), + req_volt, micb_en); + + if (micb_en == 0x1) + snd_soc_update_bits(codec, micb_reg, 0xC0, 0x80); + + snd_soc_update_bits(codec, micb_reg, 0x3F, req_vout_ctl); + + if (micb_en == 0x1) { + snd_soc_update_bits(codec, micb_reg, 0xC0, 0x40); + /* + * Add 2ms delay as per HW requirement after enabling + * micbias + */ + usleep_range(2000, 2100); + } +exit: + mutex_unlock(&aqt->micb_lock); + + return ret; +} +EXPORT_SYMBOL(aqt_mbhc_micb_adjust_voltage); + +/* + * aqt_micbias_control: enable/disable micbias + * @codec: handle to snd_soc_codec * + * @micb_num: micbias to be enabled/disabled, e.g. micbias1 or micbias2 + * @req: control requested, enable/disable or pullup enable/disable + * @is_dapm: triggered by dapm or not + * + * return 0 if control is success or error code in case of failure + */ +int aqt_micbias_control(struct snd_soc_codec *codec, + int micb_num, int req, bool is_dapm) +{ + struct aqt1000 *aqt = snd_soc_codec_get_drvdata(codec); + u16 micb_reg; + int pre_off_event = 0, post_off_event = 0; + int post_on_event = 0, post_dapm_off = 0; + int post_dapm_on = 0; + int ret = 0; + + switch (micb_num) { + case MIC_BIAS_1: + micb_reg = AQT1000_ANA_MICB1; + pre_off_event = AQT_EVENT_PRE_MICBIAS_1_OFF; + post_off_event = AQT_EVENT_POST_MICBIAS_1_OFF; + post_on_event = AQT_EVENT_POST_MICBIAS_1_ON; + post_dapm_on = AQT_EVENT_POST_DAPM_MICBIAS_1_ON; + post_dapm_off = AQT_EVENT_POST_DAPM_MICBIAS_1_OFF; + break; + default: + dev_err(codec->dev, "%s: Invalid micbias number: %d\n", + __func__, micb_num); + return -EINVAL; + } + mutex_lock(&aqt->micb_lock); + + switch (req) { + case MICB_PULLUP_ENABLE: + aqt->pullup_ref++; + if ((aqt->pullup_ref == 1) && + (aqt->micb_ref == 0)) + snd_soc_update_bits(codec, micb_reg, 0xC0, 0x80); + break; + case MICB_PULLUP_DISABLE: + if (aqt->pullup_ref > 0) + aqt->pullup_ref--; + if ((aqt->pullup_ref == 0) && + (aqt->micb_ref == 0)) + snd_soc_update_bits(codec, micb_reg, 0xC0, 0x00); + break; + case MICB_ENABLE: + aqt->micb_ref++; + if (aqt->micb_ref == 1) { + snd_soc_update_bits(codec, micb_reg, 0xC0, 0x40); + if (post_on_event && aqt->mbhc) + blocking_notifier_call_chain( + &aqt->mbhc->notifier, + post_on_event, + &aqt->mbhc->wcd_mbhc); + } + if (is_dapm && post_dapm_on && aqt->mbhc) + blocking_notifier_call_chain(&aqt->mbhc->notifier, + post_dapm_on, &aqt->mbhc->wcd_mbhc); + break; + case MICB_DISABLE: + if (aqt->micb_ref > 0) + aqt->micb_ref--; + if ((aqt->micb_ref == 0) && + (aqt->pullup_ref > 0)) + snd_soc_update_bits(codec, micb_reg, 0xC0, 0x80); + else if ((aqt->micb_ref == 0) && + (aqt->pullup_ref == 0)) { + if (pre_off_event && aqt->mbhc) + blocking_notifier_call_chain( + &aqt->mbhc->notifier, + pre_off_event, + &aqt->mbhc->wcd_mbhc); + snd_soc_update_bits(codec, micb_reg, 0xC0, 0x00); + if (post_off_event && aqt->mbhc) + blocking_notifier_call_chain( + &aqt->mbhc->notifier, + post_off_event, + &aqt->mbhc->wcd_mbhc); + } + if (is_dapm && post_dapm_off && aqt->mbhc) + blocking_notifier_call_chain(&aqt->mbhc->notifier, + post_dapm_off, &aqt->mbhc->wcd_mbhc); + break; + default: + dev_err(codec->dev, "%s: Invalid micbias request: %d\n", + __func__, req); + ret = -EINVAL; + break; + }; + + if (!ret) + dev_dbg(codec->dev, + "%s: micb_num:%d, micb_ref: %d, pullup_ref: %d\n", + __func__, micb_num, aqt->micb_ref, aqt->pullup_ref); + + mutex_unlock(&aqt->micb_lock); + + return ret; +} +EXPORT_SYMBOL(aqt_micbias_control); + +static int __aqt_codec_enable_micbias(struct snd_soc_dapm_widget *w, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + int micb_num; + + dev_dbg(codec->dev, "%s: wname: %s, event: %d\n", + __func__, w->name, event); + + if (strnstr(w->name, "AQT MIC BIAS1", sizeof("AQT MIC BIAS1"))) + micb_num = MIC_BIAS_1; + else + return -EINVAL; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* + * MIC BIAS can also be requested by MBHC, + * so use ref count to handle micbias pullup + * and enable requests + */ + aqt_micbias_control(codec, micb_num, MICB_ENABLE, true); + break; + case SND_SOC_DAPM_POST_PMU: + /* wait for cnp time */ + usleep_range(1000, 1100); + break; + case SND_SOC_DAPM_POST_PMD: + aqt_micbias_control(codec, micb_num, MICB_DISABLE, true); + break; + }; + + return 0; +} + +static int aqt_codec_enable_micbias(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + return __aqt_codec_enable_micbias(w, event); +} + +static int aqt_codec_enable_i2s_block(struct snd_soc_codec *codec) +{ + struct aqt1000 *aqt = snd_soc_codec_get_drvdata(codec); + + mutex_lock(&aqt->i2s_lock); + if (++aqt->i2s_users == 1) + snd_soc_update_bits(codec, AQT1000_I2S_I2S_0_CTL, 0x01, 0x01); + mutex_unlock(&aqt->i2s_lock); + + return 0; +} + +static int aqt_codec_disable_i2s_block(struct snd_soc_codec *codec) +{ + struct aqt1000 *aqt = snd_soc_codec_get_drvdata(codec); + + mutex_lock(&aqt->i2s_lock); + if (--aqt->i2s_users == 0) + snd_soc_update_bits(codec, AQT1000_I2S_I2S_0_CTL, 0x01, 0x00); + + if (aqt->i2s_users < 0) + dev_warn(codec->dev, "%s: i2s_users count (%d) < 0\n", + __func__, aqt->i2s_users); + mutex_unlock(&aqt->i2s_lock); + + return 0; +} + +static int aqt_codec_enable_i2s_tx(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + aqt_codec_enable_i2s_block(codec); + break; + case SND_SOC_DAPM_POST_PMD: + aqt_codec_disable_i2s_block(codec); + break; + } + dev_dbg(codec->dev, "%s: event: %d\n", __func__, event); + + return 0; +} + +static int aqt_codec_enable_i2s_rx(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + aqt_codec_enable_i2s_block(codec); + break; + case SND_SOC_DAPM_POST_PMD: + aqt_codec_disable_i2s_block(codec); + break; + } + dev_dbg(codec->dev, "%s: event: %d\n", __func__, event); + + return 0; +} + +static const char * const tx_mux_text[] = { + "ZERO", "DEC_L", "DEC_R", "DEC_V", +}; +AQT_DAPM_ENUM(tx0, AQT1000_CDC_IF_ROUTER_TX_MUX_CFG0, 0, tx_mux_text); +AQT_DAPM_ENUM(tx1, AQT1000_CDC_IF_ROUTER_TX_MUX_CFG0, 2, tx_mux_text); + +static const char * const tx_adc_mux_text[] = { + "AMIC", "ANC_FB0", "ANC_FB1", +}; +AQT_DAPM_ENUM(tx_adc0, AQT1000_CDC_TX_INP_MUX_ADC_MUX0_CFG1, 0, + tx_adc_mux_text); +AQT_DAPM_ENUM(tx_adc1, AQT1000_CDC_TX_INP_MUX_ADC_MUX1_CFG1, 0, + tx_adc_mux_text); +AQT_DAPM_ENUM(tx_adc2, AQT1000_CDC_TX_INP_MUX_ADC_MUX2_CFG1, 0, + tx_adc_mux_text); + +static int aqt_find_amic_input(struct snd_soc_codec *codec, int adc_mux_n) +{ + u8 mask; + u16 adc_mux_in_reg = 0, amic_mux_sel_reg = 0; + bool is_amic; + + if (adc_mux_n > 2) + return 0; + + if (adc_mux_n < 3) { + adc_mux_in_reg = AQT1000_CDC_TX_INP_MUX_ADC_MUX0_CFG1 + + adc_mux_n; + mask = 0x03; + amic_mux_sel_reg = AQT1000_CDC_TX_INP_MUX_ADC_MUX0_CFG0 + + 2 * adc_mux_n; + } + is_amic = (((snd_soc_read(codec, adc_mux_in_reg) & mask)) == 0); + if (!is_amic) + return 0; + + return snd_soc_read(codec, amic_mux_sel_reg) & 0x07; +} + +static u16 aqt_codec_get_amic_pwlvl_reg(struct snd_soc_codec *codec, int amic) +{ + u16 pwr_level_reg = 0; + + switch (amic) { + case 1: + case 2: + pwr_level_reg = AQT1000_ANA_AMIC1; + break; + case 3: + pwr_level_reg = AQT1000_ANA_AMIC3; + break; + default: + dev_dbg(codec->dev, "%s: invalid amic: %d\n", + __func__, amic); + break; + } + + return pwr_level_reg; +} + +static void aqt_tx_hpf_corner_freq_callback(struct work_struct *work) +{ + struct delayed_work *hpf_delayed_work; + struct hpf_work *hpf_work; + struct aqt1000 *aqt; + struct snd_soc_codec *codec; + u16 dec_cfg_reg, amic_reg, go_bit_reg; + u8 hpf_cut_off_freq; + int amic_n; + + hpf_delayed_work = to_delayed_work(work); + hpf_work = container_of(hpf_delayed_work, struct hpf_work, dwork); + aqt = hpf_work->aqt; + codec = aqt->codec; + hpf_cut_off_freq = hpf_work->hpf_cut_off_freq; + + dec_cfg_reg = AQT1000_CDC_TX0_TX_PATH_CFG0 + 16 * hpf_work->decimator; + go_bit_reg = dec_cfg_reg + 7; + + dev_dbg(codec->dev, "%s: decimator %u hpf_cut_of_freq 0x%x\n", + __func__, hpf_work->decimator, hpf_cut_off_freq); + + amic_n = aqt_find_amic_input(codec, hpf_work->decimator); + if (amic_n) { + amic_reg = AQT1000_ANA_AMIC1 + amic_n - 1; + aqt_codec_set_tx_hold(codec, amic_reg, false); + } + snd_soc_update_bits(codec, dec_cfg_reg, TX_HPF_CUT_OFF_FREQ_MASK, + hpf_cut_off_freq << 5); + snd_soc_update_bits(codec, go_bit_reg, 0x02, 0x02); + /* Minimum 1 clk cycle delay is required as per HW spec */ + usleep_range(1000, 1010); + snd_soc_update_bits(codec, go_bit_reg, 0x02, 0x00); +} + +static void aqt_tx_mute_update_callback(struct work_struct *work) +{ + struct tx_mute_work *tx_mute_dwork; + struct aqt1000 *aqt; + struct delayed_work *delayed_work; + struct snd_soc_codec *codec; + u16 tx_vol_ctl_reg, hpf_gate_reg; + + delayed_work = to_delayed_work(work); + tx_mute_dwork = container_of(delayed_work, struct tx_mute_work, dwork); + aqt = tx_mute_dwork->aqt; + codec = aqt->codec; + + tx_vol_ctl_reg = AQT1000_CDC_TX0_TX_PATH_CTL + + 16 * tx_mute_dwork->decimator; + hpf_gate_reg = AQT1000_CDC_TX0_TX_PATH_SEC2 + + 16 * tx_mute_dwork->decimator; + snd_soc_update_bits(codec, tx_vol_ctl_reg, 0x10, 0x00); +} + +static int aqt_codec_enable_dec(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct aqt1000 *aqt = snd_soc_codec_get_drvdata(codec); + char *widget_name = NULL; + char *dec = NULL; + unsigned int decimator = 0; + u8 amic_n = 0; + u16 tx_vol_ctl_reg, pwr_level_reg = 0, dec_cfg_reg, hpf_gate_reg; + u16 tx_gain_ctl_reg; + int ret = 0; + u8 hpf_cut_off_freq; + + dev_dbg(codec->dev, "%s: event: %d\n", __func__, event); + widget_name = kstrndup(w->name, 15, GFP_KERNEL); + if (!widget_name) + return -ENOMEM; + + dec = strpbrk(widget_name, "012"); + if (!dec) { + dev_err(codec->dev, "%s: decimator index not found\n", + __func__); + ret = -EINVAL; + goto out; + } + + ret = kstrtouint(dec, 10, &decimator); + if (ret < 0) { + dev_err(codec->dev, "%s: Invalid decimator = %s\n", + __func__, widget_name); + ret = -EINVAL; + goto out; + } + dev_dbg(codec->dev, "%s(): widget = %s decimator = %u\n", __func__, + w->name, decimator); + + tx_vol_ctl_reg = AQT1000_CDC_TX0_TX_PATH_CTL + 16 * decimator; + hpf_gate_reg = AQT1000_CDC_TX0_TX_PATH_SEC2 + 16 * decimator; + dec_cfg_reg = AQT1000_CDC_TX0_TX_PATH_CFG0 + 16 * decimator; + tx_gain_ctl_reg = AQT1000_CDC_TX0_TX_VOL_CTL + 16 * decimator; + + amic_n = aqt_find_amic_input(codec, decimator); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (amic_n) + pwr_level_reg = aqt_codec_get_amic_pwlvl_reg(codec, + amic_n); + if (pwr_level_reg) { + switch ((snd_soc_read(codec, pwr_level_reg) & + AQT1000_AMIC_PWR_LVL_MASK) >> + AQT1000_AMIC_PWR_LVL_SHIFT) { + case AQT1000_AMIC_PWR_LEVEL_LP: + snd_soc_update_bits(codec, dec_cfg_reg, + AQT1000_DEC_PWR_LVL_MASK, + AQT1000_DEC_PWR_LVL_LP); + break; + + case AQT1000_AMIC_PWR_LEVEL_HP: + snd_soc_update_bits(codec, dec_cfg_reg, + AQT1000_DEC_PWR_LVL_MASK, + AQT1000_DEC_PWR_LVL_HP); + break; + case AQT1000_AMIC_PWR_LEVEL_DEFAULT: + default: + snd_soc_update_bits(codec, dec_cfg_reg, + AQT1000_DEC_PWR_LVL_MASK, + AQT1000_DEC_PWR_LVL_DF); + break; + } + } + /* Enable TX PGA Mute */ + snd_soc_update_bits(codec, tx_vol_ctl_reg, 0x10, 0x10); + break; + case SND_SOC_DAPM_POST_PMU: + hpf_cut_off_freq = (snd_soc_read(codec, dec_cfg_reg) & + TX_HPF_CUT_OFF_FREQ_MASK) >> 5; + + aqt->tx_hpf_work[decimator].hpf_cut_off_freq = + hpf_cut_off_freq; + if (hpf_cut_off_freq != CF_MIN_3DB_150HZ) { + snd_soc_update_bits(codec, dec_cfg_reg, + TX_HPF_CUT_OFF_FREQ_MASK, + CF_MIN_3DB_150HZ << 5); + snd_soc_update_bits(codec, hpf_gate_reg, 0x02, 0x02); + /* + * Minimum 1 clk cycle delay is required as per + * HW spec. + */ + usleep_range(1000, 1010); + snd_soc_update_bits(codec, hpf_gate_reg, 0x02, 0x00); + } + /* schedule work queue to Remove Mute */ + schedule_delayed_work(&aqt->tx_mute_dwork[decimator].dwork, + msecs_to_jiffies(tx_unmute_delay)); + if (aqt->tx_hpf_work[decimator].hpf_cut_off_freq != + CF_MIN_3DB_150HZ) + schedule_delayed_work( + &aqt->tx_hpf_work[decimator].dwork, + msecs_to_jiffies(300)); + /* apply gain after decimator is enabled */ + snd_soc_write(codec, tx_gain_ctl_reg, + snd_soc_read(codec, tx_gain_ctl_reg)); + break; + case SND_SOC_DAPM_PRE_PMD: + hpf_cut_off_freq = + aqt->tx_hpf_work[decimator].hpf_cut_off_freq; + snd_soc_update_bits(codec, tx_vol_ctl_reg, 0x10, 0x10); + if (cancel_delayed_work_sync( + &aqt->tx_hpf_work[decimator].dwork)) { + if (hpf_cut_off_freq != CF_MIN_3DB_150HZ) { + snd_soc_update_bits(codec, dec_cfg_reg, + TX_HPF_CUT_OFF_FREQ_MASK, + hpf_cut_off_freq << 5); + snd_soc_update_bits(codec, hpf_gate_reg, + 0x02, 0x02); + /* + * Minimum 1 clk cycle delay is required as per + * HW spec. + */ + usleep_range(1000, 1010); + snd_soc_update_bits(codec, hpf_gate_reg, + 0x02, 0x00); + } + } + cancel_delayed_work_sync( + &aqt->tx_mute_dwork[decimator].dwork); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_update_bits(codec, tx_vol_ctl_reg, 0x10, 0x00); + snd_soc_update_bits(codec, dec_cfg_reg, + AQT1000_DEC_PWR_LVL_MASK, + AQT1000_DEC_PWR_LVL_DF); + break; + } + +out: + kfree(widget_name); + return ret; +} + +static const char * const tx_amic_text[] = { + "ZERO", "ADC_L", "ADC_R", "ADC_V", +}; +AQT_DAPM_ENUM(tx_amic0, AQT1000_CDC_TX_INP_MUX_ADC_MUX0_CFG0, 0, tx_amic_text); +AQT_DAPM_ENUM(tx_amic1, AQT1000_CDC_TX_INP_MUX_ADC_MUX1_CFG0, 0, tx_amic_text); +AQT_DAPM_ENUM(tx_amic2, AQT1000_CDC_TX_INP_MUX_ADC_MUX2_CFG0, 0, tx_amic_text); + +AQT_DAPM_ENUM(tx_amic10, AQT1000_CDC_TX_INP_MUX_ADC_MUX10_CFG0, 0, + tx_amic_text); +AQT_DAPM_ENUM(tx_amic11, AQT1000_CDC_TX_INP_MUX_ADC_MUX11_CFG0, 0, + tx_amic_text); +AQT_DAPM_ENUM(tx_amic12, AQT1000_CDC_TX_INP_MUX_ADC_MUX12_CFG0, 0, + tx_amic_text); +AQT_DAPM_ENUM(tx_amic13, AQT1000_CDC_TX_INP_MUX_ADC_MUX13_CFG0, 0, + tx_amic_text); + +static int aqt_codec_enable_adc(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + aqt_codec_set_tx_hold(codec, w->reg, true); + break; + default: + break; + } + + return 0; +} + +static const struct snd_kcontrol_new anc_hphl_pa_switch = + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct snd_kcontrol_new anc_hphr_pa_switch = + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0); + +static int aqt_config_compander(struct snd_soc_codec *codec, int interp_n, + int event) +{ + struct aqt1000 *aqt = snd_soc_codec_get_drvdata(codec); + int comp; + u16 comp_ctl0_reg, rx_path_cfg0_reg; + + comp = interp_n; + dev_dbg(codec->dev, "%s: event %d compander %d, enabled %d\n", + __func__, event, comp, aqt->comp_enabled[comp]); + + if (!aqt->comp_enabled[comp]) + return 0; + + comp_ctl0_reg = AQT1000_CDC_COMPANDER1_CTL0 + (comp * 8); + rx_path_cfg0_reg = AQT1000_CDC_RX1_RX_PATH_CFG0 + (comp * 20); + + if (SND_SOC_DAPM_EVENT_ON(event)) { + /* Enable Compander Clock */ + snd_soc_update_bits(codec, comp_ctl0_reg, 0x01, 0x01); + snd_soc_update_bits(codec, comp_ctl0_reg, 0x02, 0x02); + snd_soc_update_bits(codec, comp_ctl0_reg, 0x02, 0x00); + snd_soc_update_bits(codec, rx_path_cfg0_reg, 0x02, 0x02); + } + + if (SND_SOC_DAPM_EVENT_OFF(event)) { + snd_soc_update_bits(codec, rx_path_cfg0_reg, 0x02, 0x00); + snd_soc_update_bits(codec, comp_ctl0_reg, 0x04, 0x04); + snd_soc_update_bits(codec, comp_ctl0_reg, 0x02, 0x02); + snd_soc_update_bits(codec, comp_ctl0_reg, 0x02, 0x00); + snd_soc_update_bits(codec, comp_ctl0_reg, 0x01, 0x00); + snd_soc_update_bits(codec, comp_ctl0_reg, 0x04, 0x00); + } + + return 0; +} + +static void aqt_codec_idle_detect_control(struct snd_soc_codec *codec, + int interp, int event) +{ + int reg = 0, mask, val; + struct aqt1000 *aqt = snd_soc_codec_get_drvdata(codec); + + if (!aqt->idle_det_cfg.hph_idle_detect_en) + return; + + if (interp == INTERP_HPHL) { + reg = AQT1000_CDC_RX_IDLE_DET_PATH_CTL; + mask = 0x01; + val = 0x01; + } + if (interp == INTERP_HPHR) { + reg = AQT1000_CDC_RX_IDLE_DET_PATH_CTL; + mask = 0x02; + val = 0x02; + } + + if (reg && SND_SOC_DAPM_EVENT_ON(event)) + snd_soc_update_bits(codec, reg, mask, val); + + if (reg && SND_SOC_DAPM_EVENT_OFF(event)) { + snd_soc_update_bits(codec, reg, mask, 0x00); + aqt->idle_det_cfg.hph_idle_thr = 0; + snd_soc_write(codec, AQT1000_CDC_RX_IDLE_DET_CFG3, 0x0); + } +} + +static void aqt_codec_hphdelay_lutbypass(struct snd_soc_codec *codec, + u16 interp_idx, int event) +{ + struct aqt1000 *aqt = snd_soc_codec_get_drvdata(codec); + u8 hph_dly_mask; + u16 hph_lut_bypass_reg = 0; + u16 hph_comp_ctrl7 = 0; + + + switch (interp_idx) { + case INTERP_HPHL: + hph_dly_mask = 1; + hph_lut_bypass_reg = AQT1000_CDC_TOP_HPHL_COMP_LUT; + hph_comp_ctrl7 = AQT1000_CDC_COMPANDER1_CTL7; + break; + case INTERP_HPHR: + hph_dly_mask = 2; + hph_lut_bypass_reg = AQT1000_CDC_TOP_HPHR_COMP_LUT; + hph_comp_ctrl7 = AQT1000_CDC_COMPANDER2_CTL7; + break; + default: + break; + } + + if (hph_lut_bypass_reg && SND_SOC_DAPM_EVENT_ON(event)) { + snd_soc_update_bits(codec, AQT1000_CDC_CLSH_TEST0, + hph_dly_mask, 0x0); + snd_soc_update_bits(codec, hph_lut_bypass_reg, 0x80, 0x80); + if (aqt->hph_mode == CLS_H_ULP) + snd_soc_update_bits(codec, hph_comp_ctrl7, 0x20, 0x20); + } + + if (hph_lut_bypass_reg && SND_SOC_DAPM_EVENT_OFF(event)) { + snd_soc_update_bits(codec, AQT1000_CDC_CLSH_TEST0, + hph_dly_mask, hph_dly_mask); + snd_soc_update_bits(codec, hph_lut_bypass_reg, 0x80, 0x00); + snd_soc_update_bits(codec, hph_comp_ctrl7, 0x20, 0x0); + } +} + +static int aqt_codec_enable_interp_clk(struct snd_soc_codec *codec, + int event, int interp_idx) +{ + struct aqt1000 *aqt; + u16 main_reg, dsm_reg; + + if (!codec) { + pr_err("%s: codec is NULL\n", __func__); + return -EINVAL; + } + + aqt = snd_soc_codec_get_drvdata(codec); + main_reg = AQT1000_CDC_RX1_RX_PATH_CTL + (interp_idx * 20); + dsm_reg = AQT1000_CDC_RX1_RX_PATH_DSMDEM_CTL + (interp_idx * 20); + + if (SND_SOC_DAPM_EVENT_ON(event)) { + if (aqt->main_clk_users[interp_idx] == 0) { + /* Main path PGA mute enable */ + snd_soc_update_bits(codec, main_reg, 0x10, 0x10); + /* Clk enable */ + snd_soc_update_bits(codec, dsm_reg, 0x01, 0x01); + snd_soc_update_bits(codec, main_reg, 0x20, 0x20); + aqt_codec_idle_detect_control(codec, interp_idx, + event); + aqt_codec_hphdelay_lutbypass(codec, interp_idx, + event); + aqt_config_compander(codec, interp_idx, event); + } + aqt->main_clk_users[interp_idx]++; + } + + if (SND_SOC_DAPM_EVENT_OFF(event)) { + aqt->main_clk_users[interp_idx]--; + if (aqt->main_clk_users[interp_idx] <= 0) { + aqt->main_clk_users[interp_idx] = 0; + aqt_config_compander(codec, interp_idx, event); + aqt_codec_hphdelay_lutbypass(codec, interp_idx, + event); + aqt_codec_idle_detect_control(codec, interp_idx, + event); + /* Clk Disable */ + snd_soc_update_bits(codec, main_reg, 0x20, 0x00); + snd_soc_update_bits(codec, dsm_reg, 0x01, 0x00); + /* Reset enable and disable */ + snd_soc_update_bits(codec, main_reg, 0x40, 0x40); + snd_soc_update_bits(codec, main_reg, 0x40, 0x00); + /* Reset rate to 48K*/ + snd_soc_update_bits(codec, main_reg, 0x0F, 0x04); + } + } + + dev_dbg(codec->dev, "%s event %d main_clk_users %d\n", + __func__, event, aqt->main_clk_users[interp_idx]); + + return aqt->main_clk_users[interp_idx]; +} + +static int aqt_anc_out_switch_cb(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + + aqt_codec_enable_interp_clk(codec, event, w->shift); + + return 0; +} + +static const char * const anc0_fb_mux_text[] = { + "ZERO", "ANC_IN_HPHL", +}; + +static const char * const anc1_fb_mux_text[] = { + "ZERO", "ANC_IN_HPHR", +}; + +AQT_DAPM_ENUM(anc0_fb, AQT1000_CDC_RX_INP_MUX_ANC_CFG0, 0, anc0_fb_mux_text); +AQT_DAPM_ENUM(anc1_fb, AQT1000_CDC_RX_INP_MUX_ANC_CFG0, 3, anc1_fb_mux_text); + +static const char *const rx_int1_1_mux_text[] = { + "ZERO", "MAIN_DMA_L", "I2S0_L", "I2S0_R", "DEC_L", "DEC_R", "DEC_V", + "SHADOW_I2S0_L", "MAIN_DMA_R" +}; + +static const char *const rx_int1_2_mux_text[] = { + "ZERO", "MIX_DMA_L", "I2S0_L", "I2S0_R", "DEC_L", "DEC_R", "DEC_V", + "IIR0", "MIX_DMA_R" +}; + +static const char *const rx_int2_1_mux_text[] = { + "ZERO", "MAIN_DMA_R", "I2S0_L", "I2S0_R", "DEC_L", "DEC_R", "DEC_V", + "SHADOW_I2S0_R", "MAIN_DMA_L" +}; + +static const char *const rx_int2_2_mux_text[] = { + "ZERO", "MIX_DMA_R", "I2S0_L", "I2S0_R", "DEC_L", "DEC_R", "DEC_V", + "IIR0", "MIX_DMA_L" +}; + +AQT_DAPM_ENUM(rx_int1_1, AQT1000_CDC_RX_INP_MUX_RX_INT1_CFG0, 0, + rx_int1_1_mux_text); +AQT_DAPM_ENUM(rx_int1_2, AQT1000_CDC_RX_INP_MUX_RX_INT1_CFG1, 0, + rx_int1_2_mux_text); +AQT_DAPM_ENUM(rx_int2_1, AQT1000_CDC_RX_INP_MUX_RX_INT2_CFG0, 0, + rx_int2_1_mux_text); +AQT_DAPM_ENUM(rx_int2_2, AQT1000_CDC_RX_INP_MUX_RX_INT2_CFG1, 0, + rx_int2_2_mux_text); + +static int aqt_codec_set_idle_detect_thr(struct snd_soc_codec *codec, + int interp, int path_type) +{ + int port_id[4] = { 0, 0, 0, 0 }; + int *port_ptr, num_ports; + int bit_width = 0; + int mux_reg = 0, mux_reg_val = 0; + struct aqt1000 *aqt = snd_soc_codec_get_drvdata(codec); + int idle_thr; + + if ((interp != INTERP_HPHL) && (interp != INTERP_HPHR)) + return 0; + + if (!aqt->idle_det_cfg.hph_idle_detect_en) + return 0; + + port_ptr = &port_id[0]; + num_ports = 0; + + if (path_type == INTERP_MIX_PATH) { + if (interp == INTERP_HPHL) + mux_reg = AQT1000_CDC_RX_INP_MUX_RX_INT1_CFG1; + else + mux_reg = AQT1000_CDC_RX_INP_MUX_RX_INT2_CFG1; + } + + if (path_type == INTERP_MAIN_PATH) { + if (interp == INTERP_HPHL) + mux_reg = AQT1000_CDC_RX_INP_MUX_RX_INT1_CFG0; + else + mux_reg = AQT1000_CDC_RX_INP_MUX_RX_INT2_CFG0; + } + mux_reg_val = snd_soc_read(codec, mux_reg); + + /* Read bit width from I2S reg if mux is set to I2S0_L or I2S0_R */ + if (mux_reg_val == 0x02 || mux_reg_val == 0x03) + bit_width = ((snd_soc_read(codec, AQT1000_I2S_I2S_0_CTL) & + 0x40) >> 6); + + switch (bit_width) { + case 1: /* 16 bit */ + idle_thr = 0xff; /* F16 */ + break; + case 0: /* 32 bit */ + default: + idle_thr = 0x03; /* F22 */ + break; + } + + dev_dbg(codec->dev, "%s: (new) idle_thr: %d, (cur) idle_thr: %d\n", + __func__, idle_thr, aqt->idle_det_cfg.hph_idle_thr); + + if ((aqt->idle_det_cfg.hph_idle_thr == 0) || + (idle_thr < aqt->idle_det_cfg.hph_idle_thr)) { + snd_soc_write(codec, AQT1000_CDC_RX_IDLE_DET_CFG3, idle_thr); + aqt->idle_det_cfg.hph_idle_thr = idle_thr; + } + + return 0; +} + +static int aqt_codec_enable_main_path(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + u16 gain_reg = 0; + int val = 0; + + dev_dbg(codec->dev, "%s %d %s\n", __func__, event, w->name); + + if (w->shift >= AQT1000_NUM_INTERPOLATORS) { + dev_err(codec->dev, "%s: Invalid Interpolator value %d for name %s\n", + __func__, w->shift, w->name); + return -EINVAL; + }; + + gain_reg = AQT1000_CDC_RX1_RX_VOL_CTL + (w->shift * + AQT1000_RX_PATH_CTL_OFFSET); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + aqt_codec_enable_interp_clk(codec, event, w->shift); + break; + case SND_SOC_DAPM_POST_PMU: + aqt_codec_set_idle_detect_thr(codec, w->shift, + INTERP_MAIN_PATH); + /* apply gain after int clk is enabled */ + val = snd_soc_read(codec, gain_reg); + snd_soc_write(codec, gain_reg, val); + break; + case SND_SOC_DAPM_POST_PMD: + aqt_codec_enable_interp_clk(codec, event, w->shift); + break; + }; + + return 0; +} + +static int aqt_codec_enable_mix_path(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + u16 gain_reg = 0; + u16 mix_reg = 0; + + if (w->shift >= AQT1000_NUM_INTERPOLATORS) { + dev_err(codec->dev, "%s: Invalid Interpolator value %d for name %s\n", + __func__, w->shift, w->name); + return -EINVAL; + }; + gain_reg = AQT1000_CDC_RX1_RX_VOL_MIX_CTL + + (w->shift * AQT1000_RX_PATH_CTL_OFFSET); + mix_reg = AQT1000_CDC_RX1_RX_PATH_MIX_CTL + + (w->shift * AQT1000_RX_PATH_CTL_OFFSET); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + aqt_codec_enable_interp_clk(codec, event, w->shift); + /* Clk enable */ + snd_soc_update_bits(codec, mix_reg, 0x20, 0x20); + break; + case SND_SOC_DAPM_POST_PMU: + aqt_codec_set_idle_detect_thr(codec, w->shift, + INTERP_MIX_PATH); + break; + case SND_SOC_DAPM_POST_PMD: + /* Clk Disable */ + snd_soc_update_bits(codec, mix_reg, 0x20, 0x00); + aqt_codec_enable_interp_clk(codec, event, w->shift); + /* Reset enable and disable */ + snd_soc_update_bits(codec, mix_reg, 0x40, 0x40); + snd_soc_update_bits(codec, mix_reg, 0x40, 0x00); + + break; + }; + dev_dbg(codec->dev, "%s event %d name %s\n", __func__, event, w->name); + + return 0; +} + +static const char * const rx_int1_1_interp_mux_text[] = { + "ZERO", "RX INT1_1 MUX", +}; + +static const char * const rx_int2_1_interp_mux_text[] = { + "ZERO", "RX INT2_1 MUX", +}; + +static const char * const rx_int1_2_interp_mux_text[] = { + "ZERO", "RX INT1_2 MUX", +}; + +static const char * const rx_int2_2_interp_mux_text[] = { + "ZERO", "RX INT2_2 MUX", +}; + +AQT_DAPM_ENUM(rx_int1_1_interp, SND_SOC_NOPM, 0, rx_int1_1_interp_mux_text); +AQT_DAPM_ENUM(rx_int2_1_interp, SND_SOC_NOPM, 0, rx_int2_1_interp_mux_text); + +AQT_DAPM_ENUM(rx_int1_2_interp, SND_SOC_NOPM, 0, rx_int1_2_interp_mux_text); +AQT_DAPM_ENUM(rx_int2_2_interp, SND_SOC_NOPM, 0, rx_int2_2_interp_mux_text); + +static const char * const asrc0_mux_text[] = { + "ZERO", "ASRC_IN_HPHL", +}; + +static const char * const asrc1_mux_text[] = { + "ZERO", "ASRC_IN_HPHR", +}; + +AQT_DAPM_ENUM(asrc0, AQT1000_CDC_RX_INP_MUX_SPLINE_ASRC_CFG0, 0, + asrc0_mux_text); +AQT_DAPM_ENUM(asrc1, AQT1000_CDC_RX_INP_MUX_SPLINE_ASRC_CFG0, 2, + asrc1_mux_text); + +static int aqt_get_asrc_mode(struct aqt1000 *aqt, int asrc, + u8 main_sr, u8 mix_sr) +{ + u8 asrc_output_mode; + int asrc_mode = CONV_88P2K_TO_384K; + + if ((asrc < 0) || (asrc >= ASRC_MAX)) + return 0; + + asrc_output_mode = aqt->asrc_output_mode[asrc]; + + if (asrc_output_mode) { + /* + * If Mix sample rate is < 96KHz, use 96K to 352.8K + * conversion, or else use 384K to 352.8K conversion + */ + if (mix_sr < 5) + asrc_mode = CONV_96K_TO_352P8K; + else + asrc_mode = CONV_384K_TO_352P8K; + } else { + /* Integer main and Fractional mix path */ + if (main_sr < 8 && mix_sr > 9) { + asrc_mode = CONV_352P8K_TO_384K; + } else if (main_sr > 8 && mix_sr < 8) { + /* Fractional main and Integer mix path */ + if (mix_sr < 5) + asrc_mode = CONV_96K_TO_352P8K; + else + asrc_mode = CONV_384K_TO_352P8K; + } else if (main_sr < 8 && mix_sr < 8) { + /* Integer main and Integer mix path */ + asrc_mode = CONV_96K_TO_384K; + } + } + + return asrc_mode; +} + +static int aqt_codec_enable_asrc_resampler(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct aqt1000 *aqt = snd_soc_codec_get_drvdata(codec); + int asrc = 0, ret = 0; + u8 cfg; + u16 cfg_reg = 0; + u16 ctl_reg = 0; + u16 clk_reg = 0; + u16 asrc_ctl = 0; + u16 mix_ctl_reg = 0; + u16 paired_reg = 0; + u8 main_sr, mix_sr, asrc_mode = 0; + + cfg = snd_soc_read(codec, AQT1000_CDC_RX_INP_MUX_SPLINE_ASRC_CFG0); + if (!(cfg & 0xFF)) { + dev_err(codec->dev, "%s: ASRC%u input not selected\n", + __func__, w->shift); + return -EINVAL; + } + + switch (w->shift) { + case ASRC0: + if ((cfg & 0x03) == 0x01) { + cfg_reg = AQT1000_CDC_RX1_RX_PATH_CFG0; + ctl_reg = AQT1000_CDC_RX1_RX_PATH_CTL; + clk_reg = AQT1000_MIXING_ASRC0_CLK_RST_CTL; + paired_reg = AQT1000_MIXING_ASRC1_CLK_RST_CTL; + asrc_ctl = AQT1000_MIXING_ASRC0_CTL1; + } + break; + case ASRC1: + if ((cfg & 0x0C) == 0x4) { + cfg_reg = AQT1000_CDC_RX2_RX_PATH_CFG0; + ctl_reg = AQT1000_CDC_RX2_RX_PATH_CTL; + clk_reg = AQT1000_MIXING_ASRC1_CLK_RST_CTL; + paired_reg = AQT1000_MIXING_ASRC0_CLK_RST_CTL; + asrc_ctl = AQT1000_MIXING_ASRC1_CTL1; + } + break; + default: + dev_err(codec->dev, "%s: Invalid asrc:%u\n", __func__, + w->shift); + ret = -EINVAL; + break; + }; + + if ((cfg_reg == 0) || (ctl_reg == 0) || (clk_reg == 0) || + (asrc_ctl == 0) || ret) + goto done; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if ((snd_soc_read(codec, clk_reg) & 0x02) || + (snd_soc_read(codec, paired_reg) & 0x02)) { + snd_soc_update_bits(codec, clk_reg, 0x02, 0x00); + snd_soc_update_bits(codec, paired_reg, 0x02, 0x00); + } + snd_soc_update_bits(codec, cfg_reg, 0x80, 0x80); + snd_soc_update_bits(codec, clk_reg, 0x01, 0x01); + main_sr = snd_soc_read(codec, ctl_reg) & 0x0F; + mix_ctl_reg = ctl_reg + 5; + mix_sr = snd_soc_read(codec, mix_ctl_reg) & 0x0F; + asrc_mode = aqt_get_asrc_mode(aqt, asrc, + main_sr, mix_sr); + dev_dbg(codec->dev, "%s: main_sr:%d mix_sr:%d asrc_mode %d\n", + __func__, main_sr, mix_sr, asrc_mode); + snd_soc_update_bits(codec, asrc_ctl, 0x07, asrc_mode); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_update_bits(codec, asrc_ctl, 0x07, 0x00); + snd_soc_update_bits(codec, cfg_reg, 0x80, 0x00); + snd_soc_update_bits(codec, clk_reg, 0x03, 0x02); + break; + }; + +done: + return ret; +} + +static int aqt_codec_enable_anc(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct aqt1000 *aqt = snd_soc_codec_get_drvdata(codec); + const char *filename; + const struct firmware *fw; + int i; + int ret = 0; + int num_anc_slots; + struct aqt1000_anc_header *anc_head; + struct firmware_cal *hwdep_cal = NULL; + u32 anc_writes_size = 0; + u32 anc_cal_size = 0; + int anc_size_remaining; + u32 *anc_ptr; + u16 reg; + u8 mask, val; + size_t cal_size; + const void *data; + + if (!aqt->anc_func) + return 0; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + hwdep_cal = wcdcal_get_fw_cal(aqt->fw_data, WCD9XXX_ANC_CAL); + if (hwdep_cal) { + data = hwdep_cal->data; + cal_size = hwdep_cal->size; + dev_dbg(codec->dev, "%s: using hwdep calibration, cal_size %zd", + __func__, cal_size); + } else { + filename = "AQT1000/AQT1000_anc.bin"; + ret = request_firmware(&fw, filename, codec->dev); + if (ret < 0) { + dev_err(codec->dev, "%s: Failed to acquire ANC data: %d\n", + __func__, ret); + return ret; + } + if (!fw) { + dev_err(codec->dev, "%s: Failed to get anc fw\n", + __func__); + return -ENODEV; + } + data = fw->data; + cal_size = fw->size; + dev_dbg(codec->dev, "%s: using request_firmware calibration\n", + __func__); + } + if (cal_size < sizeof(struct aqt1000_anc_header)) { + dev_err(codec->dev, "%s: Invalid cal_size %zd\n", + __func__, cal_size); + ret = -EINVAL; + goto err; + } + /* First number is the number of register writes */ + anc_head = (struct aqt1000_anc_header *)(data); + anc_ptr = (u32 *)(data + sizeof(struct aqt1000_anc_header)); + anc_size_remaining = cal_size - + sizeof(struct aqt1000_anc_header); + num_anc_slots = anc_head->num_anc_slots; + + if (aqt->anc_slot >= num_anc_slots) { + dev_err(codec->dev, "%s: Invalid ANC slot selected\n", + __func__); + ret = -EINVAL; + goto err; + } + for (i = 0; i < num_anc_slots; i++) { + if (anc_size_remaining < AQT1000_PACKED_REG_SIZE) { + dev_err(codec->dev, "%s: Invalid register format\n", + __func__); + ret = -EINVAL; + goto err; + } + anc_writes_size = (u32)(*anc_ptr); + anc_size_remaining -= sizeof(u32); + anc_ptr += 1; + + if ((anc_writes_size * AQT1000_PACKED_REG_SIZE) > + anc_size_remaining) { + dev_err(codec->dev, "%s: Invalid register format\n", + __func__); + ret = -EINVAL; + goto err; + } + + if (aqt->anc_slot == i) + break; + + anc_size_remaining -= (anc_writes_size * + AQT1000_PACKED_REG_SIZE); + anc_ptr += anc_writes_size; + } + if (i == num_anc_slots) { + dev_err(codec->dev, "%s: Selected ANC slot not present\n", + __func__); + ret = -EINVAL; + goto err; + } + + i = 0; + anc_cal_size = anc_writes_size; + /* Rate converter clk enable and set bypass mode */ + if (!strcmp(w->name, "AQT RX INT1 DAC")) { + snd_soc_update_bits(codec, + AQT1000_CDC_ANC0_RC_COMMON_CTL, + 0x05, 0x05); + snd_soc_update_bits(codec, + AQT1000_CDC_ANC0_FIFO_COMMON_CTL, + 0x66, 0x66); + anc_writes_size = anc_cal_size / 2; + snd_soc_update_bits(codec, + AQT1000_CDC_ANC0_CLK_RESET_CTL, 0x39, 0x39); + } else if (!strcmp(w->name, "AQT RX INT2 DAC")) { + snd_soc_update_bits(codec, + AQT1000_CDC_ANC1_RC_COMMON_CTL, + 0x05, 0x05); + snd_soc_update_bits(codec, + AQT1000_CDC_ANC1_FIFO_COMMON_CTL, + 0x66, 0x66); + i = anc_cal_size / 2; + snd_soc_update_bits(codec, + AQT1000_CDC_ANC1_CLK_RESET_CTL, 0x39, 0x39); + } + + for (; i < anc_writes_size; i++) { + AQT1000_CODEC_UNPACK_ENTRY(anc_ptr[i], reg, mask, val); + snd_soc_write(codec, reg, (val & mask)); + } + if (!strcmp(w->name, "AQT RX INT1 DAC")) + snd_soc_update_bits(codec, + AQT1000_CDC_ANC0_CLK_RESET_CTL, 0x08, 0x08); + else if (!strcmp(w->name, "AQT RX INT2 DAC")) + snd_soc_update_bits(codec, + AQT1000_CDC_ANC1_CLK_RESET_CTL, 0x08, 0x08); + + if (!hwdep_cal) + release_firmware(fw); + break; + + case SND_SOC_DAPM_POST_PMU: + /* Remove ANC Rx from reset */ + snd_soc_update_bits(codec, + AQT1000_CDC_ANC0_CLK_RESET_CTL, + 0x08, 0x00); + snd_soc_update_bits(codec, + AQT1000_CDC_ANC1_CLK_RESET_CTL, + 0x08, 0x00); + break; + + case SND_SOC_DAPM_POST_PMD: + snd_soc_update_bits(codec, AQT1000_CDC_ANC0_RC_COMMON_CTL, + 0x05, 0x00); + if (!strcmp(w->name, "AQT ANC HPHL PA")) { + snd_soc_update_bits(codec, AQT1000_CDC_ANC0_MODE_1_CTL, + 0x30, 0x00); + /* 50 msec sleep is needed to avoid click and pop as + * per HW requirement + */ + msleep(50); + snd_soc_update_bits(codec, AQT1000_CDC_ANC0_MODE_1_CTL, + 0x01, 0x00); + snd_soc_update_bits(codec, + AQT1000_CDC_ANC0_CLK_RESET_CTL, + 0x38, 0x38); + snd_soc_update_bits(codec, + AQT1000_CDC_ANC0_CLK_RESET_CTL, + 0x07, 0x00); + snd_soc_update_bits(codec, + AQT1000_CDC_ANC0_CLK_RESET_CTL, + 0x38, 0x00); + } else if (!strcmp(w->name, "AQT ANC HPHR PA")) { + snd_soc_update_bits(codec, AQT1000_CDC_ANC1_MODE_1_CTL, + 0x30, 0x00); + /* 50 msec sleep is needed to avoid click and pop as + * per HW requirement + */ + msleep(50); + snd_soc_update_bits(codec, AQT1000_CDC_ANC1_MODE_1_CTL, + 0x01, 0x00); + snd_soc_update_bits(codec, + AQT1000_CDC_ANC1_CLK_RESET_CTL, + 0x38, 0x38); + snd_soc_update_bits(codec, + AQT1000_CDC_ANC1_CLK_RESET_CTL, + 0x07, 0x00); + snd_soc_update_bits(codec, + AQT1000_CDC_ANC1_CLK_RESET_CTL, + 0x38, 0x00); + } + break; + } + + return 0; +err: + if (!hwdep_cal) + release_firmware(fw); + return ret; +} + +static void aqt_codec_override(struct snd_soc_codec *codec, int mode, + int event) +{ + if (mode == CLS_AB || mode == CLS_AB_HIFI) { + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + case SND_SOC_DAPM_POST_PMU: + snd_soc_update_bits(codec, + AQT1000_ANA_RX_SUPPLIES, 0x02, 0x02); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_update_bits(codec, + AQT1000_ANA_RX_SUPPLIES, 0x02, 0x00); + break; + } + } +} + +static void aqt_codec_set_tx_hold(struct snd_soc_codec *codec, + u16 amic_reg, bool set) +{ + u8 mask = 0x20; + u8 val; + + if (amic_reg == AQT1000_ANA_AMIC1 || + amic_reg == AQT1000_ANA_AMIC3) + mask = 0x40; + + val = set ? mask : 0x00; + + switch (amic_reg) { + case AQT1000_ANA_AMIC1: + case AQT1000_ANA_AMIC2: + snd_soc_update_bits(codec, AQT1000_ANA_AMIC2, mask, val); + break; + case AQT1000_ANA_AMIC3: + snd_soc_update_bits(codec, AQT1000_ANA_AMIC3_HPF, mask, val); + break; + default: + dev_dbg(codec->dev, "%s: invalid amic: %d\n", + __func__, amic_reg); + break; + } +} + +static void aqt_codec_clear_anc_tx_hold(struct aqt1000 *aqt) +{ + if (test_and_clear_bit(ANC_MIC_AMIC1, &aqt->status_mask)) + aqt_codec_set_tx_hold(aqt->codec, AQT1000_ANA_AMIC1, false); + if (test_and_clear_bit(ANC_MIC_AMIC2, &aqt->status_mask)) + aqt_codec_set_tx_hold(aqt->codec, AQT1000_ANA_AMIC2, false); + if (test_and_clear_bit(ANC_MIC_AMIC3, &aqt->status_mask)) + aqt_codec_set_tx_hold(aqt->codec, AQT1000_ANA_AMIC3, false); +} + +static const char * const rx_int_dem_inp_mux_text[] = { + "NORMAL_DSM_OUT", "CLSH_DSM_OUT", +}; + +static int aqt_int_dem_inp_mux_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int val; + unsigned short look_ahead_dly_reg = AQT1000_CDC_RX1_RX_PATH_CFG0; + + val = ucontrol->value.enumerated.item[0]; + if (val >= e->items) + return -EINVAL; + + dev_dbg(codec->dev, "%s: wname: %s, val: 0x%x\n", __func__, + widget->name, val); + + if (e->reg == AQT1000_CDC_RX1_RX_PATH_SEC0) + look_ahead_dly_reg = AQT1000_CDC_RX1_RX_PATH_CFG0; + else if (e->reg == AQT1000_CDC_RX2_RX_PATH_SEC0) + look_ahead_dly_reg = AQT1000_CDC_RX2_RX_PATH_CFG0; + + /* Set Look Ahead Delay */ + snd_soc_update_bits(codec, look_ahead_dly_reg, + 0x08, (val ? 0x08 : 0x00)); + /* Set DEM INP Select */ + return snd_soc_dapm_put_enum_double(kcontrol, ucontrol); +} + +AQT_DAPM_ENUM_EXT(rx_int1_dem, AQT1000_CDC_RX1_RX_PATH_SEC0, 0, + rx_int_dem_inp_mux_text, snd_soc_dapm_get_enum_double, + aqt_int_dem_inp_mux_put); +AQT_DAPM_ENUM_EXT(rx_int2_dem, AQT1000_CDC_RX2_RX_PATH_SEC0, 0, + rx_int_dem_inp_mux_text, snd_soc_dapm_get_enum_double, + aqt_int_dem_inp_mux_put); + +static int aqt_codec_hphl_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct aqt1000 *aqt = snd_soc_codec_get_drvdata(codec); + int hph_mode = aqt->hph_mode; + u8 dem_inp; + int ret = 0; + uint32_t impedl = 0; + uint32_t impedr = 0; + + dev_dbg(codec->dev, "%s wname: %s event: %d hph_mode: %d\n", __func__, + w->name, event, hph_mode); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (aqt->anc_func) { + ret = aqt_codec_enable_anc(w, kcontrol, event); + /* 40 msec delay is needed to avoid click and pop */ + msleep(40); + } + /* Read DEM INP Select */ + dem_inp = snd_soc_read(codec, AQT1000_CDC_RX1_RX_PATH_SEC0) & + 0x03; + if (((hph_mode == CLS_H_HIFI) || (hph_mode == CLS_H_LOHIFI) || + (hph_mode == CLS_H_LP)) && (dem_inp != 0x01)) { + dev_err(codec->dev, "%s: DEM Input not set correctly, hph_mode: %d\n", + __func__, hph_mode); + return -EINVAL; + } + /* Disable AutoChop timer during power up */ + snd_soc_update_bits(codec, AQT1000_HPH_NEW_INT_HPH_TIMER1, + 0x02, 0x00); + + aqt_clsh_fsm(codec, &aqt->clsh_d, + AQT_CLSH_EVENT_PRE_DAC, + AQT_CLSH_STATE_HPHL, + hph_mode); + + if (aqt->anc_func) + snd_soc_update_bits(codec, + AQT1000_CDC_RX1_RX_PATH_CFG0, + 0x10, 0x10); + + ret = aqt_mbhc_get_impedance(aqt->mbhc, + &impedl, &impedr); + if (!ret) { + aqt_clsh_imped_config(codec, impedl, false); + set_bit(CLSH_Z_CONFIG, &aqt->status_mask); + } else { + dev_dbg(codec->dev, "%s: Failed to get mbhc impedance %d\n", + __func__, ret); + ret = 0; + } + break; + case SND_SOC_DAPM_POST_PMD: + /* 1000us required as per HW requirement */ + usleep_range(1000, 1100); + aqt_clsh_fsm(codec, &aqt->clsh_d, + AQT_CLSH_EVENT_POST_PA, + AQT_CLSH_STATE_HPHL, + hph_mode); + if (test_bit(CLSH_Z_CONFIG, &aqt->status_mask)) { + aqt_clsh_imped_config(codec, impedl, true); + clear_bit(CLSH_Z_CONFIG, &aqt->status_mask); + } + break; + default: + break; + }; + + return ret; +} + +static int aqt_codec_hphr_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct aqt1000 *aqt = snd_soc_codec_get_drvdata(codec); + int hph_mode = aqt->hph_mode; + u8 dem_inp; + int ret = 0; + + dev_dbg(codec->dev, "%s wname: %s event: %d hph_mode: %d\n", __func__, + w->name, event, hph_mode); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (aqt->anc_func) { + ret = aqt_codec_enable_anc(w, kcontrol, event); + /* 40 msec delay is needed to avoid click and pop */ + msleep(40); + } + /* Read DEM INP Select */ + dem_inp = snd_soc_read(codec, AQT1000_CDC_RX2_RX_PATH_SEC0) & + 0x03; + if (((hph_mode == CLS_H_HIFI) || (hph_mode == CLS_H_LOHIFI) || + (hph_mode == CLS_H_LP)) && (dem_inp != 0x01)) { + dev_err(codec->dev, "%s: DEM Input not set correctly, hph_mode: %d\n", + __func__, hph_mode); + return -EINVAL; + } + /* Disable AutoChop timer during power up */ + snd_soc_update_bits(codec, AQT1000_HPH_NEW_INT_HPH_TIMER1, + 0x02, 0x00); + aqt_clsh_fsm(codec, &aqt->clsh_d, + AQT_CLSH_EVENT_PRE_DAC, + AQT_CLSH_STATE_HPHR, + hph_mode); + if (aqt->anc_func) + snd_soc_update_bits(codec, + AQT1000_CDC_RX2_RX_PATH_CFG0, + 0x10, 0x10); + break; + case SND_SOC_DAPM_POST_PMD: + /* 1000us required as per HW requirement */ + usleep_range(1000, 1100); + aqt_clsh_fsm(codec, &aqt->clsh_d, + AQT_CLSH_EVENT_POST_PA, + AQT_CLSH_STATE_HPHR, + hph_mode); + break; + default: + break; + }; + + return 0; +} + +static int aqt_codec_enable_hphr_pa(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct aqt1000 *aqt = snd_soc_codec_get_drvdata(codec); + int ret = 0; + + dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if ((!(strcmp(w->name, "AQT ANC HPHR PA"))) && + (test_bit(HPH_PA_DELAY, &aqt->status_mask))) + snd_soc_update_bits(codec, AQT1000_ANA_HPH, 0xC0, 0xC0); + + set_bit(HPH_PA_DELAY, &aqt->status_mask); + break; + case SND_SOC_DAPM_POST_PMU: + if ((!(strcmp(w->name, "AQT ANC HPHR PA")))) { + if ((snd_soc_read(codec, AQT1000_ANA_HPH) & 0xC0) + != 0xC0) + /* + * If PA_EN is not set (potentially in ANC case) + * then do nothing for POST_PMU and let left + * channel handle everything. + */ + break; + } + /* + * 7ms sleep is required after PA is enabled as per + * HW requirement. If compander is disabled, then + * 20ms delay is needed. + */ + if (test_bit(HPH_PA_DELAY, &aqt->status_mask)) { + if (!aqt->comp_enabled[COMPANDER_2]) + usleep_range(20000, 20100); + else + usleep_range(7000, 7100); + clear_bit(HPH_PA_DELAY, &aqt->status_mask); + } + if (aqt->anc_func) { + /* Clear Tx FE HOLD if both PAs are enabled */ + if ((snd_soc_read(aqt->codec, AQT1000_ANA_HPH) & + 0xC0) == 0xC0) + aqt_codec_clear_anc_tx_hold(aqt); + } + + snd_soc_update_bits(codec, AQT1000_HPH_R_TEST, 0x01, 0x01); + + /* Remove mute */ + snd_soc_update_bits(codec, AQT1000_CDC_RX2_RX_PATH_CTL, + 0x10, 0x00); + /* Enable GM3 boost */ + snd_soc_update_bits(codec, AQT1000_HPH_CNP_WG_CTL, + 0x80, 0x80); + /* Enable AutoChop timer at the end of power up */ + snd_soc_update_bits(codec, AQT1000_HPH_NEW_INT_HPH_TIMER1, + 0x02, 0x02); + /* Remove mix path mute if it is enabled */ + if ((snd_soc_read(codec, AQT1000_CDC_RX2_RX_PATH_MIX_CTL)) & + 0x10) + snd_soc_update_bits(codec, + AQT1000_CDC_RX2_RX_PATH_MIX_CTL, + 0x10, 0x00); + if (!(strcmp(w->name, "AQT ANC HPHR PA"))) { + dev_dbg(codec->dev, + "%s:Do everything needed for left channel\n", + __func__); + /* Do everything needed for left channel */ + snd_soc_update_bits(codec, AQT1000_HPH_L_TEST, + 0x01, 0x01); + + /* Remove mute */ + snd_soc_update_bits(codec, AQT1000_CDC_RX1_RX_PATH_CTL, + 0x10, 0x00); + + /* Remove mix path mute if it is enabled */ + if ((snd_soc_read(codec, + AQT1000_CDC_RX1_RX_PATH_MIX_CTL)) & + 0x10) + snd_soc_update_bits(codec, + AQT1000_CDC_RX1_RX_PATH_MIX_CTL, + 0x10, 0x00); + + /* Remove ANC Rx from reset */ + ret = aqt_codec_enable_anc(w, kcontrol, event); + } + aqt_codec_override(codec, aqt->hph_mode, event); + break; + case SND_SOC_DAPM_PRE_PMD: + blocking_notifier_call_chain(&aqt->mbhc->notifier, + AQT_EVENT_PRE_HPHR_PA_OFF, + &aqt->mbhc->wcd_mbhc); + snd_soc_update_bits(codec, AQT1000_HPH_R_TEST, 0x01, 0x00); + snd_soc_update_bits(codec, AQT1000_CDC_RX2_RX_PATH_CTL, + 0x10, 0x10); + snd_soc_update_bits(codec, AQT1000_CDC_RX2_RX_PATH_MIX_CTL, + 0x10, 0x10); + if (!(strcmp(w->name, "AQT ANC HPHR PA"))) + snd_soc_update_bits(codec, AQT1000_ANA_HPH, 0x40, 0x00); + break; + case SND_SOC_DAPM_POST_PMD: + /* + * 5ms sleep is required after PA disable. If compander is + * disabled, then 20ms delay is needed after PA disable. + */ + if (!aqt->comp_enabled[COMPANDER_2]) + usleep_range(20000, 20100); + else + usleep_range(5000, 5100); + aqt_codec_override(codec, aqt->hph_mode, event); + blocking_notifier_call_chain(&aqt->mbhc->notifier, + AQT_EVENT_POST_HPHR_PA_OFF, + &aqt->mbhc->wcd_mbhc); + if (!(strcmp(w->name, "AQT ANC HPHR PA"))) { + ret = aqt_codec_enable_anc(w, kcontrol, event); + snd_soc_update_bits(codec, + AQT1000_CDC_RX2_RX_PATH_CFG0, + 0x10, 0x00); + } + break; + }; + + return ret; +} + +static int aqt_codec_enable_hphl_pa(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct aqt1000 *aqt = snd_soc_codec_get_drvdata(codec); + int ret = 0; + + dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if ((!(strcmp(w->name, "AQT ANC HPHL PA"))) && + (test_bit(HPH_PA_DELAY, &aqt->status_mask))) + snd_soc_update_bits(codec, AQT1000_ANA_HPH, + 0xC0, 0xC0); + set_bit(HPH_PA_DELAY, &aqt->status_mask); + break; + case SND_SOC_DAPM_POST_PMU: + if (!(strcmp(w->name, "AQT ANC HPHL PA"))) { + if ((snd_soc_read(codec, AQT1000_ANA_HPH) & 0xC0) + != 0xC0) + /* + * If PA_EN is not set (potentially in ANC + * case) then do nothing for POST_PMU and + * let right channel handle everything. + */ + break; + } + /* + * 7ms sleep is required after PA is enabled as per + * HW requirement. If compander is disabled, then + * 20ms delay is needed. + */ + if (test_bit(HPH_PA_DELAY, &aqt->status_mask)) { + if (!aqt->comp_enabled[COMPANDER_1]) + usleep_range(20000, 20100); + else + usleep_range(7000, 7100); + clear_bit(HPH_PA_DELAY, &aqt->status_mask); + } + if (aqt->anc_func) { + /* Clear Tx FE HOLD if both PAs are enabled */ + if ((snd_soc_read(aqt->codec, AQT1000_ANA_HPH) & + 0xC0) == 0xC0) + aqt_codec_clear_anc_tx_hold(aqt); + } + + snd_soc_update_bits(codec, AQT1000_HPH_L_TEST, 0x01, 0x01); + /* Remove Mute on primary path */ + snd_soc_update_bits(codec, AQT1000_CDC_RX1_RX_PATH_CTL, + 0x10, 0x00); + /* Enable GM3 boost */ + snd_soc_update_bits(codec, AQT1000_HPH_CNP_WG_CTL, + 0x80, 0x80); + /* Enable AutoChop timer at the end of power up */ + snd_soc_update_bits(codec, AQT1000_HPH_NEW_INT_HPH_TIMER1, + 0x02, 0x02); + /* Remove mix path mute if it is enabled */ + if ((snd_soc_read(codec, AQT1000_CDC_RX1_RX_PATH_MIX_CTL)) & + 0x10) + snd_soc_update_bits(codec, + AQT1000_CDC_RX1_RX_PATH_MIX_CTL, + 0x10, 0x00); + if (!(strcmp(w->name, "AQT ANC HPHL PA"))) { + dev_dbg(codec->dev, + "%s:Do everything needed for right channel\n", + __func__); + + /* Do everything needed for right channel */ + snd_soc_update_bits(codec, AQT1000_HPH_R_TEST, + 0x01, 0x01); + + /* Remove mute */ + snd_soc_update_bits(codec, AQT1000_CDC_RX2_RX_PATH_CTL, + 0x10, 0x00); + + /* Remove mix path mute if it is enabled */ + if ((snd_soc_read(codec, + AQT1000_CDC_RX2_RX_PATH_MIX_CTL)) & + 0x10) + snd_soc_update_bits(codec, + AQT1000_CDC_RX2_RX_PATH_MIX_CTL, + 0x10, 0x00); + /* Remove ANC Rx from reset */ + ret = aqt_codec_enable_anc(w, kcontrol, event); + } + aqt_codec_override(codec, aqt->hph_mode, event); + break; + case SND_SOC_DAPM_PRE_PMD: + blocking_notifier_call_chain(&aqt->mbhc->notifier, + AQT_EVENT_PRE_HPHL_PA_OFF, + &aqt->mbhc->wcd_mbhc); + snd_soc_update_bits(codec, AQT1000_HPH_L_TEST, 0x01, 0x00); + snd_soc_update_bits(codec, AQT1000_CDC_RX1_RX_PATH_CTL, + 0x10, 0x10); + snd_soc_update_bits(codec, AQT1000_CDC_RX1_RX_PATH_MIX_CTL, + 0x10, 0x10); + if (!(strcmp(w->name, "AQT ANC HPHL PA"))) + snd_soc_update_bits(codec, AQT1000_ANA_HPH, + 0x80, 0x00); + break; + case SND_SOC_DAPM_POST_PMD: + /* + * 5ms sleep is required after PA disable. If compander is + * disabled, then 20ms delay is needed after PA disable. + */ + if (!aqt->comp_enabled[COMPANDER_1]) + usleep_range(20000, 20100); + else + usleep_range(5000, 5100); + aqt_codec_override(codec, aqt->hph_mode, event); + blocking_notifier_call_chain(&aqt->mbhc->notifier, + AQT_EVENT_POST_HPHL_PA_OFF, + &aqt->mbhc->wcd_mbhc); + if (!(strcmp(w->name, "AQT ANC HPHL PA"))) { + ret = aqt_codec_enable_anc(w, kcontrol, event); + snd_soc_update_bits(codec, + AQT1000_CDC_RX1_RX_PATH_CFG0, 0x10, 0x00); + } + break; + }; + + return ret; +} + +static int aqt_codec_set_iir_gain(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + + dev_dbg(codec->dev, "%s: event = %d\n", __func__, event); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: /* fall through */ + case SND_SOC_DAPM_PRE_PMD: + if (strnstr(w->name, "AQT IIR0", sizeof("AQT IIR0"))) { + snd_soc_write(codec, + AQT1000_CDC_SIDETONE_IIR0_IIR_GAIN_B1_CTL, + snd_soc_read(codec, + AQT1000_CDC_SIDETONE_IIR0_IIR_GAIN_B1_CTL)); + snd_soc_write(codec, + AQT1000_CDC_SIDETONE_IIR0_IIR_GAIN_B2_CTL, + snd_soc_read(codec, + AQT1000_CDC_SIDETONE_IIR0_IIR_GAIN_B2_CTL)); + snd_soc_write(codec, + AQT1000_CDC_SIDETONE_IIR0_IIR_GAIN_B3_CTL, + snd_soc_read(codec, + AQT1000_CDC_SIDETONE_IIR0_IIR_GAIN_B3_CTL)); + snd_soc_write(codec, + AQT1000_CDC_SIDETONE_IIR0_IIR_GAIN_B4_CTL, + snd_soc_read(codec, + AQT1000_CDC_SIDETONE_IIR0_IIR_GAIN_B4_CTL)); + } + break; + } + return 0; +} + +static int aqt_enable_native_supply(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct aqt1000 *aqt = snd_soc_codec_get_drvdata(codec); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (++aqt->native_clk_users == 1) { + snd_soc_update_bits(codec, AQT1000_CLK_SYS_PLL_ENABLES, + 0x01, 0x01); + /* 100usec is needed as per HW requirement */ + usleep_range(100, 120); + snd_soc_update_bits(codec, + AQT1000_CDC_CLK_RST_CTRL_MCLK_CONTROL, + 0x02, 0x02); + snd_soc_update_bits(codec, + AQT1000_CDC_CLK_RST_CTRL_FS_CNT_CONTROL, + 0x10, 0x10); + } + break; + case SND_SOC_DAPM_PRE_PMD: + if (aqt->native_clk_users && + (--aqt->native_clk_users == 0)) { + snd_soc_update_bits(codec, + AQT1000_CDC_CLK_RST_CTRL_FS_CNT_CONTROL, + 0x10, 0x00); + snd_soc_update_bits(codec, + AQT1000_CDC_CLK_RST_CTRL_MCLK_CONTROL, + 0x02, 0x00); + snd_soc_update_bits(codec, AQT1000_CLK_SYS_PLL_ENABLES, + 0x01, 0x00); + } + break; + } + + dev_dbg(codec->dev, "%s: native_clk_users: %d, event: %d\n", + __func__, aqt->native_clk_users, event); + + return 0; +} + +static const char * const native_mux_text[] = { + "OFF", "ON", +}; + +AQT_DAPM_ENUM(int1_1_native, SND_SOC_NOPM, 0, native_mux_text); +AQT_DAPM_ENUM(int2_1_native, SND_SOC_NOPM, 0, native_mux_text); + +static int aqt_mclk_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + int ret = 0; + + dev_dbg(codec->dev, "%s: event = %d\n", __func__, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + ret = aqt_cdc_mclk_enable(codec, true); + break; + case SND_SOC_DAPM_POST_PMD: + ret = aqt_cdc_mclk_enable(codec, false); + break; + } + + return ret; +} + +static int aif_cap_mixer_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + return 0; +} + +static int aif_cap_mixer_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + return 0; +} + +static const struct snd_kcontrol_new aif1_cap_mixer[] = { + SOC_SINGLE_EXT("TX0", SND_SOC_NOPM, AQT_TX0, 1, 0, + aif_cap_mixer_get, aif_cap_mixer_put), + SOC_SINGLE_EXT("TX1", SND_SOC_NOPM, AQT_TX1, 1, 0, + aif_cap_mixer_get, aif_cap_mixer_put), +}; + +static const char * const rx_inp_st_mux_text[] = { + "ZERO", "SRC0", +}; +AQT_DAPM_ENUM(rx_inp_st, AQT1000_CDC_RX_INP_MUX_SIDETONE_SRC_CFG0, 4, + rx_inp_st_mux_text); + +static const struct snd_soc_dapm_widget aqt_dapm_widgets[] = { + + SND_SOC_DAPM_SUPPLY("AQT MCLK", SND_SOC_NOPM, 0, 0, aqt_mclk_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_AIF_OUT_E("AQT AIF1 CAP", "AQT AIF1 Capture", 0, + SND_SOC_NOPM, AIF1_CAP, 0, aqt_codec_enable_i2s_tx, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MIXER("AQT AIF1 CAP Mixer", SND_SOC_NOPM, AIF1_CAP, 0, + aif1_cap_mixer, ARRAY_SIZE(aif1_cap_mixer)), + + AQT_DAPM_MUX("AQT TX0_MUX", 0, tx0), + AQT_DAPM_MUX("AQT TX1_MUX", 0, tx1), + + SND_SOC_DAPM_MUX_E("AQT ADC0 MUX", AQT1000_CDC_TX0_TX_PATH_CTL, 5, 0, + &tx_adc0_mux, aqt_codec_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX_E("AQT ADC1 MUX", AQT1000_CDC_TX1_TX_PATH_CTL, 5, 0, + &tx_adc1_mux, aqt_codec_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX_E("AQT ADC2 MUX", AQT1000_CDC_TX2_TX_PATH_CTL, 5, 0, + &tx_adc2_mux, aqt_codec_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + AQT_DAPM_MUX("AQT AMIC0_MUX", 0, tx_amic0), + AQT_DAPM_MUX("AQT AMIC1_MUX", 0, tx_amic1), + AQT_DAPM_MUX("AQT AMIC2_MUX", 0, tx_amic2), + + SND_SOC_DAPM_ADC_E("AQT ADC_L", NULL, AQT1000_ANA_AMIC1, 7, 0, + aqt_codec_enable_adc, SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_ADC_E("AQT ADC_R", NULL, AQT1000_ANA_AMIC2, 7, 0, + aqt_codec_enable_adc, SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_ADC_E("AQT ADC_V", NULL, AQT1000_ANA_AMIC3, 7, 0, + aqt_codec_enable_adc, SND_SOC_DAPM_PRE_PMU), + + AQT_DAPM_MUX("AQT AMIC10_MUX", 0, tx_amic10), + AQT_DAPM_MUX("AQT AMIC11_MUX", 0, tx_amic11), + AQT_DAPM_MUX("AQT AMIC12_MUX", 0, tx_amic12), + AQT_DAPM_MUX("AQT AMIC13_MUX", 0, tx_amic13), + + SND_SOC_DAPM_SWITCH_E("AQT ANC OUT HPHL Enable", SND_SOC_NOPM, + INTERP_HPHL, 0, &anc_hphl_pa_switch, aqt_anc_out_switch_cb, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_SWITCH_E("AQT ANC OUT HPHR Enable", SND_SOC_NOPM, + INTERP_HPHR, 0, &anc_hphr_pa_switch, aqt_anc_out_switch_cb, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), + + SND_SOC_DAPM_MIXER("AQT RX INT1 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("AQT RX INT2 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0), + + AQT_DAPM_MUX("AQT ANC0 FB MUX", 0, anc0_fb), + AQT_DAPM_MUX("AQT ANC1 FB MUX", 0, anc1_fb), + + SND_SOC_DAPM_INPUT("AQT AMIC1"), + SND_SOC_DAPM_INPUT("AQT AMIC2"), + SND_SOC_DAPM_INPUT("AQT AMIC3"), + + SND_SOC_DAPM_MIXER("AQT I2S_L RX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("AQT I2S_R RX", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_AIF_IN_E("AQT AIF1 PB", "AQT AIF1 Playback", 0, + SND_SOC_NOPM, AIF1_PB, 0, aqt_codec_enable_i2s_rx, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX_E("AQT RX INT1_1 MUX", SND_SOC_NOPM, INTERP_HPHL, 0, + &rx_int1_1_mux, aqt_codec_enable_main_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("AQT RX INT2_1 MUX", SND_SOC_NOPM, INTERP_HPHR, 0, + &rx_int2_1_mux, aqt_codec_enable_main_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX_E("AQT RX INT1_2 MUX", SND_SOC_NOPM, INTERP_HPHL, 0, + &rx_int1_2_mux, aqt_codec_enable_mix_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("AQT RX INT2_2 MUX", SND_SOC_NOPM, INTERP_HPHR, 0, + &rx_int2_2_mux, aqt_codec_enable_mix_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + + AQT_DAPM_MUX("AQT RX INT1_1 INTERP", 0, rx_int1_1_interp), + AQT_DAPM_MUX("AQT RX INT1_2 INTERP", 0, rx_int1_2_interp), + AQT_DAPM_MUX("AQT RX INT2_1 INTERP", 0, rx_int2_1_interp), + AQT_DAPM_MUX("AQT RX INT2_2 INTERP", 0, rx_int2_2_interp), + + SND_SOC_DAPM_MIXER("AQT RX INT1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("AQT RX INT2 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_MUX_E("AQT ASRC0 MUX", SND_SOC_NOPM, ASRC0, 0, + &asrc0_mux, aqt_codec_enable_asrc_resampler, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("AQT ASRC1 MUX", SND_SOC_NOPM, ASRC1, 0, + &asrc1_mux, aqt_codec_enable_asrc_resampler, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + AQT_DAPM_MUX("AQT RX INT1 DEM MUX", 0, rx_int1_dem), + AQT_DAPM_MUX("AQT RX INT2 DEM MUX", 0, rx_int2_dem), + + SND_SOC_DAPM_DAC_E("AQT RX INT1 DAC", NULL, AQT1000_ANA_HPH, + 5, 0, aqt_codec_hphl_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_DAC_E("AQT RX INT2 DAC", NULL, AQT1000_ANA_HPH, + 4, 0, aqt_codec_hphr_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_PGA_E("AQT HPHL PA", AQT1000_ANA_HPH, 7, 0, NULL, 0, + aqt_codec_enable_hphl_pa, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("AQT HPHR PA", AQT1000_ANA_HPH, 6, 0, NULL, 0, + aqt_codec_enable_hphr_pa, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("AQT ANC HPHL PA", SND_SOC_NOPM, 0, 0, NULL, 0, + aqt_codec_enable_hphl_pa, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("AQT ANC HPHR PA", SND_SOC_NOPM, 0, 0, NULL, 0, + aqt_codec_enable_hphr_pa, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_OUTPUT("AQT HPHL"), + SND_SOC_DAPM_OUTPUT("AQT HPHR"), + SND_SOC_DAPM_OUTPUT("AQT ANC HPHL"), + SND_SOC_DAPM_OUTPUT("AQT ANC HPHR"), + + SND_SOC_DAPM_MIXER_E("AQT IIR0", AQT1000_CDC_SIDETONE_IIR0_IIR_PATH_CTL, + 4, 0, NULL, 0, aqt_codec_set_iir_gain, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + + SND_SOC_DAPM_MIXER("AQT SRC0", + AQT1000_CDC_SIDETONE_SRC0_ST_SRC_PATH_CTL, + 4, 0, NULL, 0), + + SND_SOC_DAPM_MICBIAS_E("AQT MIC BIAS1", SND_SOC_NOPM, 0, 0, + aqt_codec_enable_micbias, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_SUPPLY("AQT RX_BIAS", SND_SOC_NOPM, 0, 0, + aqt_codec_enable_rx_bias, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_SUPPLY("AQT RX INT1 NATIVE SUPPLY", SND_SOC_NOPM, + INTERP_HPHL, 0, aqt_enable_native_supply, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_SUPPLY("AQT RX INT2 NATIVE SUPPLY", SND_SOC_NOPM, + INTERP_HPHR, 0, aqt_enable_native_supply, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), + + AQT_DAPM_MUX("AQT RX INT1_1 NATIVE MUX", 0, int1_1_native), + AQT_DAPM_MUX("AQT RX INT2_1 NATIVE MUX", 0, int2_1_native), + + SND_SOC_DAPM_MUX("AQT RX ST MUX", + AQT1000_CDC_RX_INP_MUX_SIDETONE_SRC_CFG0, 2, 0, + &rx_inp_st_mux), +}; + +static int aqt_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + pr_debug("%s(): substream = %s stream = %d\n", __func__, + substream->name, substream->stream); + + return 0; +} + +static void aqt_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + pr_debug("%s(): substream = %s stream = %d\n", __func__, + substream->name, substream->stream); +} + +static int aqt_set_decimator_rate(struct snd_soc_dai *dai, + u32 sample_rate) +{ + struct snd_soc_codec *codec = dai->codec; + u8 tx_fs_rate = 0; + u8 tx_mux_sel = 0, tx0_mux_sel = 0, tx1_mux_sel = 0; + u16 tx_path_ctl_reg = 0; + + switch (sample_rate) { + case 8000: + tx_fs_rate = 0; + break; + case 16000: + tx_fs_rate = 1; + break; + case 32000: + tx_fs_rate = 3; + break; + case 48000: + tx_fs_rate = 4; + break; + case 96000: + tx_fs_rate = 5; + break; + case 192000: + tx_fs_rate = 6; + break; + default: + dev_err(codec->dev, "%s: Invalid TX sample rate: %d\n", + __func__, sample_rate); + return -EINVAL; + + }; + + /* Find which decimator path is enabled */ + tx_mux_sel = snd_soc_read(codec, AQT1000_CDC_IF_ROUTER_TX_MUX_CFG0); + tx0_mux_sel = (tx_mux_sel & 0x03); + tx1_mux_sel = (tx_mux_sel & 0xC0); + + if (tx0_mux_sel) { + tx_path_ctl_reg = AQT1000_CDC_TX0_TX_PATH_CTL + + ((tx0_mux_sel - 1) * 16); + snd_soc_update_bits(codec, tx_path_ctl_reg, 0x0F, tx_fs_rate); + } + + if (tx1_mux_sel) { + tx_path_ctl_reg = AQT1000_CDC_TX0_TX_PATH_CTL + + ((tx1_mux_sel - 1) * 16); + snd_soc_update_bits(codec, tx_path_ctl_reg, 0x0F, tx_fs_rate); + } + + return 0; +} + +static int aqt_set_interpolator_rate(struct snd_soc_dai *dai, + u32 sample_rate) +{ + struct snd_soc_codec *codec = dai->codec; + int rate_val = 0; + int i; + + for (i = 0; i < ARRAY_SIZE(sr_val_tbl); i++) { + if (sample_rate == sr_val_tbl[i].sample_rate) { + rate_val = sr_val_tbl[i].rate_val; + break; + } + } + if ((i == ARRAY_SIZE(sr_val_tbl)) || (rate_val < 0)) { + dev_err(codec->dev, "%s: Unsupported sample rate: %d\n", + __func__, sample_rate); + return -EINVAL; + } + + /* TODO - Set the rate only to enabled path */ + /* Set Primary interpolator rate */ + snd_soc_update_bits(codec, AQT1000_CDC_RX1_RX_PATH_CTL, + 0x0F, (u8)rate_val); + snd_soc_update_bits(codec, AQT1000_CDC_RX2_RX_PATH_CTL, + 0x0F, (u8)rate_val); + + /* Set mixing path interpolator rate */ + snd_soc_update_bits(codec, AQT1000_CDC_RX1_RX_PATH_MIX_CTL, + 0x0F, (u8)rate_val); + snd_soc_update_bits(codec, AQT1000_CDC_RX2_RX_PATH_MIX_CTL, + 0x0F, (u8)rate_val); + + return 0; +} + +static int aqt_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + pr_debug("%s(): substream = %s stream = %d\n", __func__, + substream->name, substream->stream); + return 0; +} + +static int aqt_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct aqt1000 *aqt = snd_soc_codec_get_drvdata(dai->codec); + int ret = 0; + + dev_dbg(aqt->dev, "%s: dai_name = %s DAI-ID %x rate %d num_ch %d\n", + __func__, dai->name, dai->id, params_rate(params), + params_channels(params)); + + switch (substream->stream) { + case SNDRV_PCM_STREAM_PLAYBACK: + ret = aqt_set_interpolator_rate(dai, params_rate(params)); + if (ret) { + dev_err(aqt->dev, "%s: cannot set sample rate: %u\n", + __func__, params_rate(params)); + return ret; + } + switch (params_width(params)) { + case 16: + aqt->dai[dai->id].bit_width = 16; + break; + case 24: + aqt->dai[dai->id].bit_width = 24; + break; + case 32: + aqt->dai[dai->id].bit_width = 32; + break; + default: + return -EINVAL; + } + aqt->dai[dai->id].rate = params_rate(params); + break; + case SNDRV_PCM_STREAM_CAPTURE: + ret = aqt_set_decimator_rate(dai, params_rate(params)); + if (ret) { + dev_err(aqt->dev, + "%s: cannot set TX Decimator rate: %d\n", + __func__, ret); + return ret; + } + switch (params_width(params)) { + case 16: + aqt->dai[dai->id].bit_width = 16; + break; + case 24: + aqt->dai[dai->id].bit_width = 24; + break; + default: + dev_err(aqt->dev, "%s: Invalid format 0x%x\n", + __func__, params_width(params)); + return -EINVAL; + }; + aqt->dai[dai->id].rate = params_rate(params); + break; + default: + dev_err(aqt->dev, "%s: Invalid stream type %d\n", __func__, + substream->stream); + return -EINVAL; + }; + + return 0; +} + +static struct snd_soc_dai_ops aqt_dai_ops = { + .startup = aqt_startup, + .shutdown = aqt_shutdown, + .hw_params = aqt_hw_params, + .prepare = aqt_prepare, +}; + +struct snd_soc_dai_driver aqt_dai[] = { + { + .name = "aqt_rx1", + .id = AIF1_PB, + .playback = { + .stream_name = "AQT AIF1 Playback", + .rates = AQT1000_RATES_MASK | AQT1000_FRAC_RATES_MASK, + .formats = AQT1000_FORMATS_S16_S24_S32_LE, + .rate_min = 8000, + .rate_max = 384000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &aqt_dai_ops, + }, + { + .name = "aqt_tx1", + .id = AIF1_CAP, + .capture = { + .stream_name = "AQT AIF1 Capture", + .rates = AQT1000_RATES_MASK, + .formats = AQT1000_FORMATS_S16_S24_LE, + .rate_min = 8000, + .rate_max = 192000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &aqt_dai_ops, + }, +}; + +static int aqt_enable_mclk(struct aqt1000 *aqt) +{ + struct snd_soc_codec *codec = aqt->codec; + + /* Enable mclk requires master bias to be enabled first */ + if (aqt->master_bias_users <= 0) { + dev_err(aqt->dev, + "%s: Cannot turn on MCLK, BG is not enabled\n", + __func__); + return -EINVAL; + } + + if (++aqt->mclk_users == 1) { + /* Set clock div 2 */ + snd_soc_update_bits(codec, + AQT1000_CLK_SYS_MCLK1_PRG, 0x0C, 0x04); + snd_soc_update_bits(codec, + AQT1000_CLK_SYS_MCLK1_PRG, 0x10, 0x10); + snd_soc_update_bits(codec, + AQT1000_CDC_CLK_RST_CTRL_FS_CNT_CONTROL, + 0x01, 0x01); + snd_soc_update_bits(codec, + AQT1000_CDC_CLK_RST_CTRL_MCLK_CONTROL, + 0x01, 0x01); + /* + * 10us sleep is required after clock is enabled + * as per HW requirement + */ + usleep_range(10, 15); + } + + dev_dbg(aqt->dev, "%s: mclk_users: %d\n", __func__, aqt->mclk_users); + + return 0; +} + +static int aqt_disable_mclk(struct aqt1000 *aqt) +{ + struct snd_soc_codec *codec = aqt->codec; + + if (aqt->mclk_users <= 0) { + dev_err(aqt->dev, "%s: No mclk users, cannot disable mclk\n", + __func__); + return -EINVAL; + } + + if (--aqt->mclk_users == 0) { + snd_soc_update_bits(codec, + AQT1000_CDC_CLK_RST_CTRL_MCLK_CONTROL, + 0x01, 0x00); + snd_soc_update_bits(codec, + AQT1000_CDC_CLK_RST_CTRL_FS_CNT_CONTROL, + 0x01, 0x00); + snd_soc_update_bits(codec, + AQT1000_CLK_SYS_MCLK1_PRG, 0x10, 0x00); + } + + dev_dbg(codec->dev, "%s: mclk_users: %d\n", __func__, aqt->mclk_users); + + return 0; +} + +static int aqt_enable_master_bias(struct aqt1000 *aqt) +{ + struct snd_soc_codec *codec = aqt->codec; + + mutex_lock(&aqt->master_bias_lock); + + aqt->master_bias_users++; + if (aqt->master_bias_users == 1) { + snd_soc_update_bits(codec, AQT1000_ANA_BIAS, 0x80, 0x80); + snd_soc_update_bits(codec, AQT1000_ANA_BIAS, 0x40, 0x40); + /* + * 1ms delay is required after pre-charge is enabled + * as per HW requirement + */ + usleep_range(1000, 1100); + snd_soc_update_bits(codec, AQT1000_ANA_BIAS, 0x40, 0x00); + } + + mutex_unlock(&aqt->master_bias_lock); + + return 0; +} + +static int aqt_disable_master_bias(struct aqt1000 *aqt) +{ + struct snd_soc_codec *codec = aqt->codec; + + mutex_lock(&aqt->master_bias_lock); + if (aqt->master_bias_users <= 0) { + mutex_unlock(&aqt->master_bias_lock); + return -EINVAL; + } + + aqt->master_bias_users--; + if (aqt->master_bias_users == 0) + snd_soc_update_bits(codec, AQT1000_ANA_BIAS, 0x80, 0x00); + mutex_unlock(&aqt->master_bias_lock); + + return 0; +} + +static int aqt_cdc_req_mclk_enable(struct aqt1000 *aqt, + bool enable) +{ + int ret = 0; + + if (enable) { + ret = clk_prepare_enable(aqt->ext_clk); + if (ret) { + dev_err(aqt->dev, "%s: ext clk enable failed\n", + __func__); + goto done; + } + /* Get BG */ + aqt_enable_master_bias(aqt); + /* Get MCLK */ + aqt_enable_mclk(aqt); + } else { + /* put MCLK */ + aqt_disable_mclk(aqt); + /* put BG */ + if (aqt_disable_master_bias(aqt)) + dev_err(aqt->dev, "%s: master bias disable failed\n", + __func__); + clk_disable_unprepare(aqt->ext_clk); + } + +done: + return ret; +} + +static int __aqt_cdc_mclk_enable_locked(struct aqt1000 *aqt, + bool enable) +{ + int ret = 0; + + dev_dbg(aqt->dev, "%s: mclk_enable = %u\n", __func__, enable); + + if (enable) + ret = aqt_cdc_req_mclk_enable(aqt, true); + else + aqt_cdc_req_mclk_enable(aqt, false); + + return ret; +} + +static int __aqt_cdc_mclk_enable(struct aqt1000 *aqt, + bool enable) +{ + int ret; + + mutex_lock(&aqt->cdc_bg_clk_lock); + ret = __aqt_cdc_mclk_enable_locked(aqt, enable); + mutex_unlock(&aqt->cdc_bg_clk_lock); + + return ret; +} + +/** + * aqt_cdc_mclk_enable - Enable/disable codec mclk + * + * @codec: codec instance + * @enable: Indicates clk enable or disable + * + * Returns 0 on Success and error on failure + */ +int aqt_cdc_mclk_enable(struct snd_soc_codec *codec, bool enable) +{ + struct aqt1000 *aqt = snd_soc_codec_get_drvdata(codec); + + return __aqt_cdc_mclk_enable(aqt, enable); +} +EXPORT_SYMBOL(aqt_cdc_mclk_enable); + +/* + * aqt_get_micb_vout_ctl_val: converts micbias from volts to register value + * @micb_mv: micbias in mv + * + * return register value converted + */ +int aqt_get_micb_vout_ctl_val(u32 micb_mv) +{ + /* min micbias voltage is 1V and maximum is 2.85V */ + if (micb_mv < 1000 || micb_mv > 2850) { + pr_err("%s: unsupported micbias voltage\n", __func__); + return -EINVAL; + } + + return (micb_mv - 1000) / 50; +} +EXPORT_SYMBOL(aqt_get_micb_vout_ctl_val); + +static int aqt_set_micbias(struct aqt1000 *aqt, + struct aqt1000_pdata *pdata) +{ + struct snd_soc_codec *codec = aqt->codec; + int vout_ctl_1; + + if (!pdata) { + dev_err(codec->dev, "%s: NULL pdata\n", __func__); + return -ENODEV; + } + + /* set micbias voltage */ + vout_ctl_1 = aqt_get_micb_vout_ctl_val(pdata->micbias.micb1_mv); + if (vout_ctl_1 < 0) + return -EINVAL; + + snd_soc_update_bits(codec, AQT1000_ANA_MICB1, 0x3F, vout_ctl_1); + + return 0; +} + +static ssize_t aqt_codec_version_read(struct snd_info_entry *entry, + void *file_private_data, + struct file *file, + char __user *buf, size_t count, + loff_t pos) +{ + char buffer[AQT_VERSION_ENTRY_SIZE]; + int len = 0; + + len = snprintf(buffer, sizeof(buffer), "AQT1000_1_0\n"); + + return simple_read_from_buffer(buf, count, &pos, buffer, len); +} + +static struct snd_info_entry_ops aqt_codec_info_ops = { + .read = aqt_codec_version_read, +}; + +/* + * aqt_codec_info_create_codec_entry - creates aqt1000 module + * @codec_root: The parent directory + * @codec: Codec instance + * + * Creates aqt1000 module and version entry under the given + * parent directory. + * + * Return: 0 on success or negative error code on failure. + */ +int aqt_codec_info_create_codec_entry(struct snd_info_entry *codec_root, + struct snd_soc_codec *codec) +{ + struct snd_info_entry *version_entry; + struct aqt1000 *aqt; + struct snd_soc_card *card; + + if (!codec_root || !codec) + return -EINVAL; + + aqt = snd_soc_codec_get_drvdata(codec); + if (!aqt) { + dev_dbg(codec->dev, "%s: aqt is NULL\n", __func__); + return -EINVAL; + } + card = codec->component.card; + aqt->entry = snd_info_create_subdir(codec_root->module, + "aqt1000", codec_root); + if (!aqt->entry) { + dev_dbg(codec->dev, "%s: failed to create aqt1000 entry\n", + __func__); + return -ENOMEM; + } + + version_entry = snd_info_create_card_entry(card->snd_card, + "version", + aqt->entry); + if (!version_entry) { + dev_dbg(codec->dev, "%s: failed to create aqt1000 version entry\n", + __func__); + return -ENOMEM; + } + + version_entry->private_data = aqt; + version_entry->size = AQT_VERSION_ENTRY_SIZE; + version_entry->content = SNDRV_INFO_CONTENT_DATA; + version_entry->c.ops = &aqt_codec_info_ops; + + if (snd_info_register(version_entry) < 0) { + snd_info_free_entry(version_entry); + return -ENOMEM; + } + aqt->version_entry = version_entry; + + return 0; +} +EXPORT_SYMBOL(aqt_codec_info_create_codec_entry); + +static const struct aqt_reg_mask_val aqt_codec_reg_init[] = { + {AQT1000_CHIP_CFG0_EFUSE_CTL, 0x01, 0x01}, +}; + +static const struct aqt_reg_mask_val aqt_codec_reg_update[] = { + {AQT1000_LDOH_MODE, 0x1F, 0x0B}, + {AQT1000_MICB1_TEST_CTL_2, 0x07, 0x01}, + {AQT1000_MICB1_MISC_MICB1_INM_RES_BIAS, 0x03, 0x02}, + {AQT1000_MICB1_MISC_MICB1_INM_RES_BIAS, 0x0C, 0x08}, + {AQT1000_MICB1_MISC_MICB1_INM_RES_BIAS, 0x30, 0x20}, + {AQT1000_CDC_TX0_TX_PATH_CFG1, 0x01, 0x00}, + {AQT1000_CDC_TX1_TX_PATH_CFG1, 0x01, 0x00}, + {AQT1000_CDC_TX2_TX_PATH_CFG1, 0x01, 0x00}, +}; + +static void aqt_codec_init_reg(struct aqt1000 *priv) +{ + struct snd_soc_codec *codec = priv->codec; + u32 i; + + for (i = 0; i < ARRAY_SIZE(aqt_codec_reg_init); i++) + snd_soc_update_bits(codec, + aqt_codec_reg_init[i].reg, + aqt_codec_reg_init[i].mask, + aqt_codec_reg_init[i].val); +} + +static void aqt_codec_update_reg(struct aqt1000 *priv) +{ + struct snd_soc_codec *codec = priv->codec; + u32 i; + + for (i = 0; i < ARRAY_SIZE(aqt_codec_reg_update); i++) + snd_soc_update_bits(codec, + aqt_codec_reg_update[i].reg, + aqt_codec_reg_update[i].mask, + aqt_codec_reg_update[i].val); + +} + +static int aqt_soc_codec_probe(struct snd_soc_codec *codec) +{ + struct aqt1000 *aqt; + struct aqt1000_pdata *pdata; + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + int i, ret = 0; + + dev_dbg(codec->dev, "%s()\n", __func__); + aqt = snd_soc_codec_get_drvdata(codec); + + mutex_init(&aqt->codec_mutex); + mutex_init(&aqt->i2s_lock); + /* Class-H Init */ + aqt_clsh_init(&aqt->clsh_d); + /* Default HPH Mode to Class-H Low HiFi */ + aqt->hph_mode = CLS_H_LOHIFI; + + aqt->fw_data = devm_kzalloc(codec->dev, sizeof(*(aqt->fw_data)), + GFP_KERNEL); + if (!aqt->fw_data) + goto err; + + set_bit(WCD9XXX_ANC_CAL, aqt->fw_data->cal_bit); + set_bit(WCD9XXX_MBHC_CAL, aqt->fw_data->cal_bit); + + /* Register for Clock */ + aqt->ext_clk = clk_get(aqt->dev, "aqt_clk"); + if (IS_ERR(aqt->ext_clk)) { + dev_err(aqt->dev, "%s: clk get %s failed\n", + __func__, "aqt_ext_clk"); + goto err_clk; + } + + ret = wcd_cal_create_hwdep(aqt->fw_data, + AQT1000_CODEC_HWDEP_NODE, codec); + if (ret < 0) { + dev_err(codec->dev, "%s hwdep failed %d\n", __func__, ret); + goto err_hwdep; + } + + /* Initialize MBHC module */ + ret = aqt_mbhc_init(&aqt->mbhc, codec, aqt->fw_data); + if (ret) { + pr_err("%s: mbhc initialization failed\n", __func__); + goto err_hwdep; + } + aqt->codec = codec; + for (i = 0; i < COMPANDER_MAX; i++) + aqt->comp_enabled[i] = 0; + + aqt_cdc_mclk_enable(codec, true); + aqt_codec_init_reg(aqt); + aqt_cdc_mclk_enable(codec, false); + + /* Add 100usec delay as per HW requirement */ + usleep_range(100, 110); + + aqt_codec_update_reg(aqt); + + pdata = dev_get_platdata(codec->dev); + + /* If 1.8v is supplied externally, then disable internal 1.8v supply */ + for (i = 0; i < pdata->num_supplies; i++) { + if (!strcmp(pdata->regulator->name, "aqt_vdd1p8")) { + snd_soc_update_bits(codec, AQT1000_BUCK_5V_EN_CTL, + 0x03, 0x00); + dev_dbg(codec->dev, "%s: Disabled internal supply\n", + __func__); + break; + } + } + + aqt_set_micbias(aqt, pdata); + + snd_soc_dapm_add_routes(dapm, aqt_audio_map, + ARRAY_SIZE(aqt_audio_map)); + + for (i = 0; i < NUM_CODEC_DAIS; i++) { + INIT_LIST_HEAD(&aqt->dai[i].ch_list); + init_waitqueue_head(&aqt->dai[i].dai_wait); + } + + for (i = 0; i < AQT1000_NUM_DECIMATORS; i++) { + aqt->tx_hpf_work[i].aqt = aqt; + aqt->tx_hpf_work[i].decimator = i; + INIT_DELAYED_WORK(&aqt->tx_hpf_work[i].dwork, + aqt_tx_hpf_corner_freq_callback); + + aqt->tx_mute_dwork[i].aqt = aqt; + aqt->tx_mute_dwork[i].decimator = i; + INIT_DELAYED_WORK(&aqt->tx_mute_dwork[i].dwork, + aqt_tx_mute_update_callback); + } + + mutex_lock(&aqt->codec_mutex); + snd_soc_dapm_disable_pin(dapm, "AQT ANC HPHL PA"); + snd_soc_dapm_disable_pin(dapm, "AQT ANC HPHR PA"); + snd_soc_dapm_disable_pin(dapm, "AQT ANC HPHL"); + snd_soc_dapm_disable_pin(dapm, "AQT ANC HPHR"); + mutex_unlock(&aqt->codec_mutex); + + snd_soc_dapm_ignore_suspend(dapm, "AQT AIF1 Playback"); + snd_soc_dapm_ignore_suspend(dapm, "AQT AIF1 Capture"); + + snd_soc_dapm_sync(dapm); + + return ret; + +err_hwdep: + clk_put(aqt->ext_clk); +err_clk: + devm_kfree(codec->dev, aqt->fw_data); + aqt->fw_data = NULL; +err: + mutex_destroy(&aqt->i2s_lock); + mutex_destroy(&aqt->codec_mutex); + return ret; +} + +static int aqt_soc_codec_remove(struct snd_soc_codec *codec) +{ + struct aqt1000 *aqt = snd_soc_codec_get_drvdata(codec); + + /* Deinitialize MBHC module */ + aqt_mbhc_deinit(codec); + aqt->mbhc = NULL; + mutex_destroy(&aqt->i2s_lock); + mutex_destroy(&aqt->codec_mutex); + clk_put(aqt->ext_clk); + + return 0; +} + +static struct regmap *aqt_get_regmap(struct device *dev) +{ + struct aqt1000 *control = dev_get_drvdata(dev); + + return control->regmap; +} + +struct snd_soc_codec_driver snd_cdc_dev_aqt = { + .probe = aqt_soc_codec_probe, + .remove = aqt_soc_codec_remove, + .get_regmap = aqt_get_regmap, + .component_driver = { + .controls = aqt_snd_controls, + .num_controls = ARRAY_SIZE(aqt_snd_controls), + .dapm_widgets = aqt_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(aqt_dapm_widgets), + .dapm_routes = aqt_audio_map, + .num_dapm_routes = ARRAY_SIZE(aqt_audio_map), + }, +}; + +/* + * aqt_register_codec: Register the device to ASoC + * @dev: device + * + * return 0 success or error code in case of failure + */ +int aqt_register_codec(struct device *dev) +{ + return snd_soc_register_codec(dev, &snd_cdc_dev_aqt, aqt_dai, + ARRAY_SIZE(aqt_dai)); +} +EXPORT_SYMBOL(aqt_register_codec); diff --git a/audio-kernel/asoc/codecs/aqt1000/aqt1000.h b/audio-kernel/asoc/codecs/aqt1000/aqt1000.h new file mode 100644 index 000000000..7b870db7a --- /dev/null +++ b/audio-kernel/asoc/codecs/aqt1000/aqt1000.h @@ -0,0 +1,229 @@ +/* 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 + * 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 AQT1000_H +#define AQT1000_H + +#include +#include +#include +#include +#include "pdata.h" +#include "aqt1000-clsh.h" + +#define AQT1000_MAX_MICBIAS 1 +#define AQT1000_NUM_INTERPOLATORS 2 +#define AQT1000_NUM_DECIMATORS 3 +#define AQT1000_VOUT_CTL_TO_MICB(v) (1000 + v * 50) +#define AQT1000_RX_PATH_CTL_OFFSET 20 + +#define AQT1000_CLK_24P576MHZ 24576000 +#define AQT1000_CLK_19P2MHZ 19200000 +#define AQT1000_CLK_12P288MHZ 12288000 +#define AQT1000_CLK_9P6MHZ 9600000 + +#define AQT1000_ST_IIR_COEFF_MAX 5 + +enum { + AQT1000_RX0 = 0, + AQT1000_RX1, + AQT1000_RX_MAX, +}; + +enum { + AQT_NONE, + AQT_MCLK, + AQT_RCO, +}; + +enum { + AQT_TX0 = 0, + AQT_TX1, +}; + +enum { + ASRC0, + ASRC1, + ASRC_MAX, +}; + +/* Each IIR has 5 Filter Stages */ +enum { + BAND1 = 0, + BAND2, + BAND3, + BAND4, + BAND5, + BAND_MAX, +}; + +enum { + AQT1000_TX0 = 0, + AQT1000_TX1, + AQT1000_TX2, + AQT1000_TX_MAX, +}; + +enum { + INTERP_HPHL, + INTERP_HPHR, + INTERP_MAX, +}; + +enum { + INTERP_MAIN_PATH, + INTERP_MIX_PATH, +}; + +enum { + COMPANDER_1, /* HPH_L */ + COMPANDER_2, /* HPH_R */ + COMPANDER_MAX, +}; + +enum { + AIF1_PB = 0, + AIF1_CAP, + NUM_CODEC_DAIS, +}; + +struct aqt_codec_dai_data { + u32 rate; + u32 *ch_num; + u32 ch_act; + u32 ch_tot; +}; + +struct aqt_idle_detect_config { + u8 hph_idle_thr; + u8 hph_idle_detect_en; +}; + +struct aqt1000_i2c { + struct i2c_client *client; + struct i2c_msg xfer_msg[2]; + struct mutex xfer_lock; + int mod_id; +}; + +struct aqt1000_cdc_dai_data { + u32 rate; /* sample rate */ + u32 bit_width; /* sit width 16,24,32 */ + struct list_head ch_list; + wait_queue_head_t dai_wait; +}; + +struct tx_mute_work { + struct aqt1000 *aqt; + u8 decimator; + struct delayed_work dwork; +}; + +struct hpf_work { + struct aqt1000 *aqt; + u8 decimator; + u8 hpf_cut_off_freq; + struct delayed_work dwork; +}; + +struct aqt1000 { + struct device *dev; + struct mutex io_lock; + struct mutex xfer_lock; + struct mutex reset_lock; + + struct device_node *aqt_rst_np; + + int (*read_dev)(struct aqt1000 *aqt, unsigned short reg, + void *dest, int bytes); + int (*write_dev)(struct aqt1000 *aqt, unsigned short reg, + void *src, int bytes); + + u32 num_of_supplies; + struct regulator_bulk_data *supplies; + + u32 mclk_rate; + struct regmap *regmap; + struct snd_soc_codec *codec; + bool dev_up; + bool prev_pg_valid; + u8 prev_pg; + + struct aqt1000_i2c i2c_dev; + + /* Codec params */ + + /* ANC related */ + u32 anc_slot; + bool anc_func; + + /* compander */ + int comp_enabled[COMPANDER_MAX]; + + /* class h specific data */ + struct aqt_clsh_cdc_data clsh_d; + + /* Interpolator Mode Select for HPH_L and HPH_R */ + u32 hph_mode; + + unsigned long status_mask; + + struct aqt1000_cdc_dai_data dai[NUM_CODEC_DAIS]; + + struct mutex micb_lock; + + struct clk *ext_clk; + + /* mbhc module */ + struct aqt1000_mbhc *mbhc; + + struct mutex codec_mutex; + + /* cal info for codec */ + struct fw_info *fw_data; + + int native_clk_users; + /* ASRC users count */ + int asrc_users[ASRC_MAX]; + int asrc_output_mode[ASRC_MAX]; + /* Main path clock users count */ + int main_clk_users[AQT1000_NUM_INTERPOLATORS]; + + struct aqt_idle_detect_config idle_det_cfg; + u32 rx_bias_count; + + s32 micb_ref; + s32 pullup_ref; + int master_bias_users; + int mclk_users; + int i2s_users; + + struct hpf_work tx_hpf_work[AQT1000_NUM_DECIMATORS]; + struct tx_mute_work tx_mute_dwork[AQT1000_NUM_DECIMATORS]; + + struct mutex master_bias_lock; + struct mutex cdc_bg_clk_lock; + struct mutex i2s_lock; + + /* Interrupt */ + struct regmap_irq_chip_data *irq_chip; + int num_irq_regs; + struct irq_domain *virq; + int irq; + int irq_base; + + /* Entry for version info */ + struct snd_info_entry *entry; + struct snd_info_entry *version_entry; +}; + +#endif /* AQT1000_H */ diff --git a/audio-kernel/asoc/codecs/aqt1000/pdata.h b/audio-kernel/asoc/codecs/aqt1000/pdata.h new file mode 100644 index 000000000..2c29848d7 --- /dev/null +++ b/audio-kernel/asoc/codecs/aqt1000/pdata.h @@ -0,0 +1,40 @@ +/* + * 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 + * 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 _AQT1000_PDATA_H_ +#define _AQT1000_PDATA_H_ + +#include +#include +#include "../msm-cdc-supply.h" + +struct aqt1000_micbias_setting { + u8 ldoh_v; + u32 cfilt1_mv; + u32 micb1_mv; + u8 bias1_cfilt_sel; +}; + +struct aqt1000_pdata { + unsigned int irq_gpio; + unsigned int irq_flags; + struct cdc_regulator *regulator; + int num_supplies; + struct aqt1000_micbias_setting micbias; + struct device_node *aqt_rst_np; + u32 mclk_rate; + u32 ext_clk_rate; + u32 ext_1p8v_supply; +}; + +#endif /* _AQT1000_PDATA_H_ */ diff --git a/audio-kernel/asoc/codecs/audio-ext-clk-up.c b/audio-kernel/asoc/codecs/audio-ext-clk-up.c new file mode 100644 index 000000000..9889d01b5 --- /dev/null +++ b/audio-kernel/asoc/codecs/audio-ext-clk-up.c @@ -0,0 +1,583 @@ +/* Copyright (c) 2015-2019, 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 +#include +#include +#include +#include +#include +#include +#include "../../../drivers/clk/qcom/common.h" +#include +#include +#include +#include +#include "audio-ext-clk-up.h" + +enum { + AUDIO_EXT_CLK_PMI, + AUDIO_EXT_CLK_LNBB2, + AUDIO_EXT_CLK_LPASS, + AUDIO_EXT_CLK_LPASS2, + AUDIO_EXT_CLK_LPASS3, + AUDIO_EXT_CLK_LPASS4, + AUDIO_EXT_CLK_LPASS5, + AUDIO_EXT_CLK_LPASS6, + AUDIO_EXT_CLK_LPASS7, + AUDIO_EXT_CLK_LPASS_CORE_HW_VOTE, + AUDIO_EXT_CLK_LPASS_MAX, + AUDIO_EXT_CLK_MAX = AUDIO_EXT_CLK_LPASS_MAX, +}; + +struct pinctrl_info { + struct pinctrl *pinctrl; + struct pinctrl_state *sleep; + struct pinctrl_state *active; + char __iomem *base; +}; + +struct audio_ext_clk { + struct pinctrl_info pnctrl_info; + struct clk_fixed_factor fact; +}; + +struct audio_ext_clk_priv { + struct device *dev; + int clk_src; + struct afe_clk_set clk_cfg; + struct audio_ext_clk audio_clk; + const char *clk_name; + uint32_t lpass_core_hwvote_client_handle; +}; + +static inline struct audio_ext_clk_priv *to_audio_clk(struct clk_hw *hw) +{ + return container_of(hw, struct audio_ext_clk_priv, audio_clk.fact.hw); +} + +static int audio_ext_clk_prepare(struct clk_hw *hw) +{ + struct audio_ext_clk_priv *clk_priv = to_audio_clk(hw); + struct pinctrl_info *pnctrl_info = &clk_priv->audio_clk.pnctrl_info; + int ret; + + if ((clk_priv->clk_src >= AUDIO_EXT_CLK_LPASS) && + (clk_priv->clk_src < AUDIO_EXT_CLK_LPASS_MAX)) { + clk_priv->clk_cfg.enable = 1; + ret = afe_set_lpass_clk_cfg(IDX_RSVD_3, &clk_priv->clk_cfg); + if (ret < 0) { + pr_err_ratelimited("%s afe_set_digital_codec_core_clock failed\n", + __func__); + return ret; + } + } + + if (pnctrl_info->pinctrl) { + ret = pinctrl_select_state(pnctrl_info->pinctrl, + pnctrl_info->active); + if (ret) { + pr_err("%s: active state select failed with %d\n", + __func__, ret); + return -EIO; + } + } + + if (pnctrl_info->base) + iowrite32(1, pnctrl_info->base); + return 0; +} + +static void audio_ext_clk_unprepare(struct clk_hw *hw) +{ + struct audio_ext_clk_priv *clk_priv = to_audio_clk(hw); + struct pinctrl_info *pnctrl_info = &clk_priv->audio_clk.pnctrl_info; + int ret; + + if (pnctrl_info->pinctrl) { + ret = pinctrl_select_state(pnctrl_info->pinctrl, + pnctrl_info->sleep); + if (ret) { + pr_err("%s: active state select failed with %d\n", + __func__, ret); + return; + } + } + + if ((clk_priv->clk_src >= AUDIO_EXT_CLK_LPASS) && + (clk_priv->clk_src < AUDIO_EXT_CLK_LPASS_MAX)) { + clk_priv->clk_cfg.enable = 0; + ret = afe_set_lpass_clk_cfg(IDX_RSVD_3, &clk_priv->clk_cfg); + if (ret < 0) + pr_err_ratelimited("%s: afe_set_lpass_clk_cfg failed, ret = %d\n", + __func__, ret); + } + + if (pnctrl_info->base) + iowrite32(0, pnctrl_info->base); +} + +static u8 audio_ext_clk_get_parent(struct clk_hw *hw) +{ + struct audio_ext_clk_priv *clk_priv = to_audio_clk(hw); + int num_parents = clk_hw_get_num_parents(hw); + const char * const *parent_names = hw->init->parent_names; + u8 i = 0, ret = hw->init->num_parents + 1; + + if ((clk_priv->clk_src == AUDIO_EXT_CLK_PMI) && clk_priv->clk_name) { + for (i = 0; i < num_parents; i++) { + if (!strcmp(parent_names[i], clk_priv->clk_name)) + ret = i; + } + pr_debug("%s: parent index = %u\n", __func__, ret); + return ret; + } else + return 0; +} + +static int lpass_hw_vote_prepare(struct clk_hw *hw) +{ + struct audio_ext_clk_priv *clk_priv = to_audio_clk(hw); + int ret = 0; + int32_t avs_state = 0; + uint32_t *client_handle = &clk_priv->lpass_core_hwvote_client_handle; + + if (clk_priv->clk_src == AUDIO_EXT_CLK_LPASS_CORE_HW_VOTE) { + ret = afe_vote_lpass_core_hw(AFE_LPASS_CORE_HW_MACRO_BLOCK, + "LPASS_HW_MACRO", + client_handle); + if (ret < 0) { + pr_err("%s lpass core hw vote failed %d\n", + __func__, ret); + /* + * DSP returns -EBUSY when AVS services are not up + * Check for AVS state and then retry voting + * for core hw clock. + */ + if (ret == -EBUSY) { + q6core_is_avs_up(&avs_state); + if (avs_state) + ret = afe_vote_lpass_core_hw( + AFE_LPASS_CORE_HW_MACRO_BLOCK, + "LPASS_HW_MACRO", + client_handle); + } + return ret; + } + } + + return 0; +} + +static void lpass_hw_vote_unprepare(struct clk_hw *hw) +{ + struct audio_ext_clk_priv *clk_priv = to_audio_clk(hw); + int ret = 0; + + if (clk_priv->clk_src == AUDIO_EXT_CLK_LPASS_CORE_HW_VOTE) { + ret = afe_unvote_lpass_core_hw( + AFE_LPASS_CORE_HW_MACRO_BLOCK, + clk_priv->lpass_core_hwvote_client_handle); + if (ret < 0) + pr_err("%s lpass core hw vote failed %d\n", + __func__, ret); + } +} + +static const struct clk_ops audio_ext_clk_ops = { + .prepare = audio_ext_clk_prepare, + .unprepare = audio_ext_clk_unprepare, + .get_parent = audio_ext_clk_get_parent, +}; + +static const struct clk_ops lpass_hw_vote_ops = { + .prepare = lpass_hw_vote_prepare, + .unprepare = lpass_hw_vote_unprepare, +}; + +static const char * const audio_ext_pmi_div_clk[] = { + "qpnp_clkdiv_1", + "pms405_div_clk1", + "pm6150_div_clk1", + "pm6125_div_clk1", +}; + +static int audio_ext_clk_dummy_prepare(struct clk_hw *hw) +{ + return 0; +} + +static void audio_ext_clk_dummy_unprepare(struct clk_hw *hw) +{ + +} + +static const struct clk_ops audio_ext_clk_dummy_ops = { + .prepare = audio_ext_clk_dummy_prepare, + .unprepare = audio_ext_clk_dummy_unprepare, +}; + +static struct audio_ext_clk audio_clk_array[] = { + { + .pnctrl_info = {NULL}, + .fact = { + .mult = 1, + .div = 1, + .hw.init = &(struct clk_init_data){ + .name = "audio_ext_pmi_clk", + .parent_names = audio_ext_pmi_div_clk, + .num_parents = + ARRAY_SIZE(audio_ext_pmi_div_clk), + .ops = &audio_ext_clk_ops, + }, + }, + }, + { + .pnctrl_info = {NULL}, + .fact = { + .mult = 1, + .div = 1, + .hw.init = &(struct clk_init_data){ + .name = "audio_ext_pmi_lnbb_clk", + .parent_names = (const char *[]) + { "ln_bb_clk2" }, + .num_parents = 1, + .ops = &audio_ext_clk_dummy_ops, + }, + }, + }, + { + .pnctrl_info = {NULL}, + .fact = { + .mult = 1, + .div = 1, + .hw.init = &(struct clk_init_data){ + .name = "audio_lpass_mclk", + .ops = &audio_ext_clk_ops, + }, + }, + }, + { + .pnctrl_info = {NULL}, + .fact = { + .mult = 1, + .div = 1, + .hw.init = &(struct clk_init_data){ + .name = "audio_lpass_mclk2", + .ops = &audio_ext_clk_ops, + }, + }, + }, + { + .pnctrl_info = {NULL}, + .fact = { + .mult = 1, + .div = 1, + .hw.init = &(struct clk_init_data){ + .name = "audio_lpass_mclk3", + .ops = &audio_ext_clk_ops, + }, + }, + }, + { + .pnctrl_info = {NULL}, + .fact = { + .mult = 1, + .div = 1, + .hw.init = &(struct clk_init_data){ + .name = "audio_lpass_mclk4", + .ops = &audio_ext_clk_ops, + }, + }, + }, + { + .pnctrl_info = {NULL}, + .fact = { + .mult = 1, + .div = 1, + .hw.init = &(struct clk_init_data){ + .name = "audio_lpass_mclk5", + .ops = &audio_ext_clk_ops, + }, + }, + }, + { + .pnctrl_info = {NULL}, + .fact = { + .mult = 1, + .div = 1, + .hw.init = &(struct clk_init_data){ + .name = "audio_lpass_mclk6", + .ops = &audio_ext_clk_ops, + }, + }, + }, + { + .pnctrl_info = {NULL}, + .fact = { + .mult = 1, + .div = 1, + .hw.init = &(struct clk_init_data){ + .name = "audio_lpass_mclk7", + .ops = &audio_ext_clk_ops, + }, + }, + }, + { + .pnctrl_info = {NULL}, + .fact = { + .hw.init = &(struct clk_init_data){ + .name = "lpass_hw_vote_clk", + .ops = &lpass_hw_vote_ops, + }, + }, + }, +}; + +static int audio_get_pinctrl(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct audio_ext_clk_priv *clk_priv = platform_get_drvdata(pdev); + struct pinctrl_info *pnctrl_info; + struct pinctrl *pinctrl; + int ret; + u32 reg; + + pnctrl_info = &clk_priv->audio_clk.pnctrl_info; + if (pnctrl_info->pinctrl) { + dev_err(dev, "%s: already requested before\n", + __func__); + return -EINVAL; + } + + pinctrl = devm_pinctrl_get(dev); + if (IS_ERR_OR_NULL(pinctrl)) { + dev_err(dev, "%s: Unable to get pinctrl handle\n", + __func__); + return -EINVAL; + } + pnctrl_info->pinctrl = pinctrl; + /* get all state handles from Device Tree */ + pnctrl_info->sleep = pinctrl_lookup_state(pinctrl, "sleep"); + if (IS_ERR(pnctrl_info->sleep)) { + dev_err(dev, "%s: could not get sleep pinstate\n", + __func__); + goto err; + } + pnctrl_info->active = pinctrl_lookup_state(pinctrl, "active"); + if (IS_ERR(pnctrl_info->active)) { + dev_err(dev, "%s: could not get active pinstate\n", + __func__); + goto err; + } + /* Reset the TLMM pins to a default state */ + ret = pinctrl_select_state(pnctrl_info->pinctrl, + pnctrl_info->sleep); + if (ret) { + dev_err(dev, "%s: Disable TLMM pins failed with %d\n", + __func__, ret); + goto err; + } + + ret = of_property_read_u32(dev->of_node, "qcom,mclk-clk-reg", ®); + if (ret < 0) { + dev_dbg(dev, "%s: miss mclk reg\n", __func__); + } else { + pnctrl_info->base = ioremap(reg, sizeof(u32)); + if (pnctrl_info->base == NULL) { + dev_err(dev, "%s ioremap failed\n", __func__); + goto err; + } + } + + return 0; + +err: + devm_pinctrl_put(pnctrl_info->pinctrl); + return -EINVAL; +} + +static int audio_put_pinctrl(struct platform_device *pdev) +{ + struct audio_ext_clk_priv *clk_priv = platform_get_drvdata(pdev); + struct pinctrl_info *pnctrl_info = NULL; + + pnctrl_info = &clk_priv->audio_clk.pnctrl_info; + if (pnctrl_info && pnctrl_info->pinctrl) { + devm_pinctrl_put(pnctrl_info->pinctrl); + pnctrl_info->pinctrl = NULL; + } + + return 0; +} + +static int audio_get_clk_data(struct platform_device *pdev) +{ + int ret; + struct clk *audio_clk; + struct clk_hw *clkhw; + struct clk_onecell_data *clk_data; + struct audio_ext_clk_priv *clk_priv = platform_get_drvdata(pdev); + + clk_data = devm_kzalloc(&pdev->dev, sizeof(*clk_data), GFP_KERNEL); + if (!clk_data) + return -ENOMEM; + + clk_data->clk_num = 1; + clk_data->clks = devm_kzalloc(&pdev->dev, + sizeof(struct clk *), + GFP_KERNEL); + if (!clk_data->clks) + return -ENOMEM; + + clkhw = &clk_priv->audio_clk.fact.hw; + audio_clk = devm_clk_register(&pdev->dev, clkhw); + if (IS_ERR(audio_clk)) { + dev_err(&pdev->dev, + "%s: clock register failed for clk_src = %d\\n", + __func__, clk_priv->clk_src); + ret = PTR_ERR(audio_clk); + return ret; + } + clk_data->clks[0] = audio_clk; + + ret = of_clk_add_provider(pdev->dev.of_node, + of_clk_src_onecell_get, clk_data); + if (ret) + dev_err(&pdev->dev, "%s: clock add failed for clk_src = %d\n", + __func__, clk_priv->clk_src); + + return ret; +} + +static int audio_ref_clk_probe(struct platform_device *pdev) +{ + int ret; + struct audio_ext_clk_priv *clk_priv; + u32 clk_freq = 0, clk_id = 0, clk_src = 0, use_pinctrl = 0; + + clk_priv = devm_kzalloc(&pdev->dev, sizeof(*clk_priv), GFP_KERNEL); + if (!clk_priv) + return -ENOMEM; + + ret = of_property_read_u32(pdev->dev.of_node, + "qcom,codec-ext-clk-src", + &clk_src); + if (ret) { + dev_err(&pdev->dev, "%s: could not get clk source, ret = %d\n", + __func__, ret); + return ret; + } + + if (clk_src >= AUDIO_EXT_CLK_MAX) { + dev_err(&pdev->dev, "%s: Invalid clk source = %d\n", + __func__, clk_src); + return -EINVAL; + } + clk_priv->clk_name = NULL; + clk_priv->clk_src = clk_src; + memcpy(&clk_priv->audio_clk, &audio_clk_array[clk_src], + sizeof(struct audio_ext_clk)); + + /* Init lpass clk default values */ + clk_priv->clk_cfg.clk_set_minor_version = + Q6AFE_LPASS_CLK_CONFIG_API_VERSION; + clk_priv->clk_cfg.clk_id = Q6AFE_LPASS_CLK_ID_SPEAKER_I2S_OSR; + clk_priv->clk_cfg.clk_freq_in_hz = Q6AFE_LPASS_OSR_CLK_9_P600_MHZ; + clk_priv->clk_cfg.clk_attri = Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO; + + ret = of_property_read_u32(pdev->dev.of_node, + "qcom,codec-lpass-ext-clk-freq", + &clk_freq); + if (!ret) + clk_priv->clk_cfg.clk_freq_in_hz = clk_freq; + + ret = of_property_read_u32(pdev->dev.of_node, + "qcom,codec-lpass-clk-id", + &clk_id); + if (!ret) + clk_priv->clk_cfg.clk_id = clk_id; + + dev_dbg(&pdev->dev, "%s: ext-clk freq: %d, lpass clk_id: %d, clk_src: %d\n", + __func__, clk_priv->clk_cfg.clk_freq_in_hz, + clk_priv->clk_cfg.clk_id, clk_priv->clk_src); + platform_set_drvdata(pdev, clk_priv); + + ret = of_property_read_string(pdev->dev.of_node, "pmic-clock-names", + &clk_priv->clk_name); + if (ret) + dev_dbg(&pdev->dev, "%s: could not find pmic clock names\n", + __func__); + /* + * property qcom,use-pinctrl to be defined in DTSI to val 1 + * for clock nodes using pinctrl + */ + of_property_read_u32(pdev->dev.of_node, "qcom,use-pinctrl", + &use_pinctrl); + dev_dbg(&pdev->dev, "%s: use-pinctrl : %d\n", + __func__, use_pinctrl); + + if (use_pinctrl) { + ret = audio_get_pinctrl(pdev); + if (ret) { + dev_err(&pdev->dev, "%s: Parsing PMI pinctrl failed\n", + __func__); + return ret; + } + } + + ret = audio_get_clk_data(pdev); + if (ret) { + dev_err(&pdev->dev, "%s: clk_init is failed\n", + __func__); + audio_put_pinctrl(pdev); + return ret; + } + + return 0; +} + +static int audio_ref_clk_remove(struct platform_device *pdev) +{ + audio_put_pinctrl(pdev); + + return 0; +} + +static const struct of_device_id audio_ref_clk_match[] = { + {.compatible = "qcom,audio-ref-clk"}, + {} +}; +MODULE_DEVICE_TABLE(of, audio_ref_clk_match); + +static struct platform_driver audio_ref_clk_driver = { + .driver = { + .name = "audio-ref-clk", + .owner = THIS_MODULE, + .of_match_table = audio_ref_clk_match, + }, + .probe = audio_ref_clk_probe, + .remove = audio_ref_clk_remove, +}; + +int audio_ref_clk_platform_init(void) +{ + return platform_driver_register(&audio_ref_clk_driver); +} + +void audio_ref_clk_platform_exit(void) +{ + platform_driver_unregister(&audio_ref_clk_driver); +} + +MODULE_DESCRIPTION("Audio Ref Up Clock module platform driver"); +MODULE_LICENSE("GPL v2"); diff --git a/audio-kernel/asoc/codecs/audio-ext-clk-up.h b/audio-kernel/asoc/codecs/audio-ext-clk-up.h new file mode 100644 index 000000000..8a0232e11 --- /dev/null +++ b/audio-kernel/asoc/codecs/audio-ext-clk-up.h @@ -0,0 +1,20 @@ +/* + * Copyright (c) 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 + * 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_EXT_CLK_UP_H_ +#define __AUDIO_EXT_CLK_UP_H_ + +int audio_ref_clk_platform_init(void); +void audio_ref_clk_platform_exit(void); + +#endif diff --git a/audio-kernel/asoc/codecs/audio-ext-clk.c b/audio-kernel/asoc/codecs/audio-ext-clk.c new file mode 100644 index 000000000..72f16f584 --- /dev/null +++ b/audio-kernel/asoc/codecs/audio-ext-clk.c @@ -0,0 +1,348 @@ +/* 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 + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "audio-ext-clk-up.h" + +struct pinctrl_info { + struct pinctrl *pinctrl; + struct pinctrl_state *sleep; + struct pinctrl_state *active; +}; + +struct audio_ext_ap_clk { + bool enabled; + int gpio; + struct clk c; +}; + +struct audio_ext_pmi_clk { + int gpio; + struct clk c; +}; + +struct audio_ext_ap_clk2 { + bool enabled; + struct pinctrl_info pnctrl_info; + struct clk c; +}; + +static struct afe_clk_set clk2_config = { + Q6AFE_LPASS_CLK_CONFIG_API_VERSION, + Q6AFE_LPASS_CLK_ID_SPEAKER_I2S_OSR, + Q6AFE_LPASS_IBIT_CLK_11_P2896_MHZ, + Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO, + Q6AFE_LPASS_CLK_ROOT_DEFAULT, + 0, +}; + +static inline struct audio_ext_ap_clk *to_audio_ap_clk(struct clk *clk) +{ + return container_of(clk, struct audio_ext_ap_clk, c); +} + +static int audio_ext_clk_prepare(struct clk *clk) +{ + struct audio_ext_ap_clk *audio_clk = to_audio_ap_clk(clk); + + pr_debug("%s: gpio: %d\n", __func__, audio_clk->gpio); + if (gpio_is_valid(audio_clk->gpio)) + return gpio_direction_output(audio_clk->gpio, 1); + return 0; +} + +static void audio_ext_clk_unprepare(struct clk *clk) +{ + struct audio_ext_ap_clk *audio_clk = to_audio_ap_clk(clk); + + pr_debug("%s: gpio: %d\n", __func__, audio_clk->gpio); + if (gpio_is_valid(audio_clk->gpio)) + gpio_direction_output(audio_clk->gpio, 0); +} + +static inline struct audio_ext_ap_clk2 *to_audio_ap_clk2(struct clk *clk) +{ + return container_of(clk, struct audio_ext_ap_clk2, c); +} + +static int audio_ext_clk2_prepare(struct clk *clk) +{ + struct audio_ext_ap_clk2 *audio_clk2 = to_audio_ap_clk2(clk); + struct pinctrl_info *pnctrl_info = &audio_clk2->pnctrl_info; + int ret; + + + if (!pnctrl_info->pinctrl || !pnctrl_info->active) + return 0; + + ret = pinctrl_select_state(pnctrl_info->pinctrl, + pnctrl_info->active); + if (ret) { + pr_err("%s: active state select failed with %d\n", + __func__, ret); + return -EIO; + } + + clk2_config.enable = 1; + ret = afe_set_lpass_clk_cfg(IDX_RSVD_3, &clk2_config); + if (ret < 0) { + pr_err("%s: failed to set clock, ret = %d\n", __func__, ret); + return -EINVAL; + } + + return 0; +} + +static void audio_ext_clk2_unprepare(struct clk *clk) +{ + struct audio_ext_ap_clk2 *audio_clk2 = to_audio_ap_clk2(clk); + struct pinctrl_info *pnctrl_info = &audio_clk2->pnctrl_info; + int ret; + + if (!pnctrl_info->pinctrl || !pnctrl_info->sleep) + return; + + ret = pinctrl_select_state(pnctrl_info->pinctrl, + pnctrl_info->sleep); + if (ret) + pr_err("%s: sleep state select failed with %d\n", + __func__, ret); + + clk2_config.enable = 0; + ret = afe_set_lpass_clk_cfg(IDX_RSVD_3, &clk2_config); + if (ret < 0) + pr_err("%s: failed to reset clock, ret = %d\n", __func__, ret); +} + +static const struct clk_ops audio_ext_ap_clk_ops = { + .prepare = audio_ext_clk_prepare, + .unprepare = audio_ext_clk_unprepare, +}; + +static const struct clk_ops audio_ext_ap_clk2_ops = { + .prepare = audio_ext_clk2_prepare, + .unprepare = audio_ext_clk2_unprepare, +}; + +static struct audio_ext_pmi_clk audio_pmi_clk = { + .gpio = -EINVAL, + .c = { + .dbg_name = "audio_ext_pmi_clk", + .ops = &clk_ops_dummy, + CLK_INIT(audio_pmi_clk.c), + }, +}; + +static struct audio_ext_pmi_clk audio_pmi_lnbb_clk = { + .gpio = -EINVAL, + .c = { + .dbg_name = "audio_ext_pmi_lnbb_clk", + .ops = &clk_ops_dummy, + CLK_INIT(audio_pmi_lnbb_clk.c), + }, +}; + +static struct audio_ext_ap_clk audio_ap_clk = { + .gpio = -EINVAL, + .c = { + .dbg_name = "audio_ext_ap_clk", + .ops = &audio_ext_ap_clk_ops, + CLK_INIT(audio_ap_clk.c), + }, +}; + +static struct audio_ext_ap_clk2 audio_ap_clk2 = { + .c = { + .dbg_name = "audio_ext_ap_clk2", + .ops = &audio_ext_ap_clk2_ops, + CLK_INIT(audio_ap_clk2.c), + }, +}; + +static struct clk_lookup audio_ref_clock[] = { + CLK_LIST(audio_ap_clk), + CLK_LIST(audio_pmi_clk), + CLK_LIST(audio_pmi_lnbb_clk), + CLK_LIST(audio_ap_clk2), +}; + +static int audio_get_pinctrl(struct platform_device *pdev) +{ + struct pinctrl_info *pnctrl_info; + struct pinctrl *pinctrl; + int ret; + + pnctrl_info = &audio_ap_clk2.pnctrl_info; + + if (pnctrl_info->pinctrl) { + dev_dbg(&pdev->dev, "%s: already requested before\n", + __func__); + return -EINVAL; + } + + pinctrl = devm_pinctrl_get(&pdev->dev); + if (IS_ERR_OR_NULL(pinctrl)) { + dev_dbg(&pdev->dev, "%s: Unable to get pinctrl handle\n", + __func__); + return -EINVAL; + } + pnctrl_info->pinctrl = pinctrl; + /* get all state handles from Device Tree */ + pnctrl_info->sleep = pinctrl_lookup_state(pinctrl, "sleep"); + if (IS_ERR(pnctrl_info->sleep)) { + dev_err(&pdev->dev, "%s: could not get sleep pinstate\n", + __func__); + goto err; + } + pnctrl_info->active = pinctrl_lookup_state(pinctrl, "active"); + if (IS_ERR(pnctrl_info->active)) { + dev_err(&pdev->dev, "%s: could not get active pinstate\n", + __func__); + goto err; + } + /* Reset the TLMM pins to a default state */ + ret = pinctrl_select_state(pnctrl_info->pinctrl, + pnctrl_info->sleep); + if (ret) { + dev_err(&pdev->dev, "%s: Disable TLMM pins failed with %d\n", + __func__, ret); + goto err; + } + return 0; + +err: + devm_pinctrl_put(pnctrl_info->pinctrl); + return -EINVAL; +} + +static int audio_ref_clk_probe(struct platform_device *pdev) +{ + int clk_gpio; + int ret; + struct clk *audio_clk; + + clk_gpio = of_get_named_gpio(pdev->dev.of_node, + "qcom,audio-ref-clk-gpio", 0); + if (clk_gpio > 0) { + ret = gpio_request(clk_gpio, "EXT_CLK"); + if (ret) { + dev_err(&pdev->dev, + "Request ext clk gpio failed %d, err:%d\n", + clk_gpio, ret); + goto err; + } + if (of_property_read_bool(pdev->dev.of_node, + "qcom,node_has_rpm_clock")) { + audio_clk = clk_get(&pdev->dev, NULL); + if (IS_ERR(audio_clk)) { + dev_err(&pdev->dev, "Failed to get RPM div clk\n"); + ret = PTR_ERR(audio_clk); + goto err_gpio; + } + audio_pmi_clk.c.parent = audio_clk; + audio_pmi_clk.gpio = clk_gpio; + } else + audio_ap_clk.gpio = clk_gpio; + + } else { + if (of_property_read_bool(pdev->dev.of_node, + "qcom,node_has_rpm_clock")) { + audio_clk = clk_get(&pdev->dev, NULL); + if (IS_ERR(audio_clk)) { + dev_err(&pdev->dev, "Failed to get lnbbclk2\n"); + ret = PTR_ERR(audio_clk); + goto err; + } + audio_pmi_lnbb_clk.c.parent = audio_clk; + audio_pmi_lnbb_clk.gpio = -EINVAL; + } + } + + ret = audio_get_pinctrl(pdev); + if (ret) + dev_dbg(&pdev->dev, "%s: Parsing pinctrl failed\n", + __func__); + + ret = of_msm_clock_register(pdev->dev.of_node, audio_ref_clock, + ARRAY_SIZE(audio_ref_clock)); + if (ret) { + dev_err(&pdev->dev, "%s: audio ref clock register failed\n", + __func__); + goto err_gpio; + } + + return 0; + +err_gpio: + gpio_free(clk_gpio); + +err: + return ret; +} + +static int audio_ref_clk_remove(struct platform_device *pdev) +{ + struct pinctrl_info *pnctrl_info = &audio_ap_clk2.pnctrl_info; + + if (audio_pmi_clk.gpio > 0) + gpio_free(audio_pmi_clk.gpio); + else if (audio_ap_clk.gpio > 0) + gpio_free(audio_ap_clk.gpio); + + if (pnctrl_info->pinctrl) { + devm_pinctrl_put(pnctrl_info->pinctrl); + pnctrl_info->pinctrl = NULL; + } + + return 0; +} + +static const struct of_device_id audio_ref_clk_match[] = { + {.compatible = "qcom,audio-ref-clk"}, + {} +}; +MODULE_DEVICE_TABLE(of, audio_ref_clk_match); + +static struct platform_driver audio_ref_clk_driver = { + .driver = { + .name = "audio-ref-clk", + .owner = THIS_MODULE, + .of_match_table = audio_ref_clk_match, + }, + .probe = audio_ref_clk_probe, + .remove = audio_ref_clk_remove, +}; + +int audio_ref_clk_platform_init(void) +{ + return platform_driver_register(&audio_ref_clk_driver); +} + +void audio_ref_clk_platform_exit(void) +{ + platform_driver_unregister(&audio_ref_clk_driver); +} + +MODULE_DESCRIPTION("Audio Ref Clock module platform driver"); +MODULE_LICENSE("GPL v2"); diff --git a/audio-kernel/asoc/codecs/bolero/Android.mk b/audio-kernel/asoc/codecs/bolero/Android.mk new file mode 100644 index 000000000..d8343c0ef --- /dev/null +++ b/audio-kernel/asoc/codecs/bolero/Android.mk @@ -0,0 +1,82 @@ +# Android makefile for audio kernel modules + +# Assume no targets will be supported + +# Check if this driver needs be built for current target +ifeq ($(call is-board-platform,$(MSMSTEPPE) $(TRINKET)),true) +AUDIO_SELECT := CONFIG_SND_SOC_SM6150=m +endif + +AUDIO_CHIPSET := audio +# Build/Package only in case of supported target +ifeq ($(call is-board-platform-in-list,$(MSMSTEPPE) $(TRINKET)),true) + +LOCAL_PATH := $(call my-dir) + +# This makefile is only for DLKM +ifneq ($(findstring vendor,$(LOCAL_PATH)),) + +ifneq ($(findstring opensource,$(LOCAL_PATH)),) + AUDIO_BLD_DIR := $(shell pwd)/vendor/qcom/opensource/audio-kernel +endif # opensource + +DLKM_DIR := $(TOP)/device/qcom/common/dlkm + +# Build audio.ko as $(AUDIO_CHIPSET)_audio.ko +########################################################### +# This is set once per LOCAL_PATH, not per (kernel) module +KBUILD_OPTIONS := AUDIO_ROOT=$(AUDIO_BLD_DIR) + +# We are actually building audio.ko here, as per the +# requirement we are specifying _audio.ko as LOCAL_MODULE. +# This means we need to rename the module to _audio.ko +# after audio.ko is built. +KBUILD_OPTIONS += MODNAME=bolero_cdc_dlkm +KBUILD_OPTIONS += BOARD_PLATFORM=$(TARGET_BOARD_PLATFORM) +KBUILD_OPTIONS += $(AUDIO_SELECT) + +########################################################### +include $(CLEAR_VARS) +LOCAL_MODULE := $(AUDIO_CHIPSET)_bolero_cdc.ko +LOCAL_MODULE_KBUILD_NAME := bolero_cdc_dlkm.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_DEBUG_ENABLE := true +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk +########################################################### +include $(CLEAR_VARS) +LOCAL_MODULE := $(AUDIO_CHIPSET)_wsa_macro.ko +LOCAL_MODULE_KBUILD_NAME := wsa_macro_dlkm.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_DEBUG_ENABLE := true +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk +########################################################### +include $(CLEAR_VARS) +LOCAL_MODULE := $(AUDIO_CHIPSET)_va_macro.ko +LOCAL_MODULE_KBUILD_NAME := va_macro_dlkm.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_DEBUG_ENABLE := true +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk +########################################################### +include $(CLEAR_VARS) +LOCAL_MODULE := $(AUDIO_CHIPSET)_tx_macro.ko +LOCAL_MODULE_KBUILD_NAME := tx_macro_dlkm.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_DEBUG_ENABLE := true +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk +########################################################### +include $(CLEAR_VARS) +LOCAL_MODULE := $(AUDIO_CHIPSET)_rx_macro.ko +LOCAL_MODULE_KBUILD_NAME := rx_macro_dlkm.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_DEBUG_ENABLE := true +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk +########################################################### +########################################################### + +endif # DLKM check +endif # supported target check diff --git a/audio-kernel/asoc/codecs/bolero/Kbuild b/audio-kernel/asoc/codecs/bolero/Kbuild new file mode 100644 index 000000000..167664162 --- /dev/null +++ b/audio-kernel/asoc/codecs/bolero/Kbuild @@ -0,0 +1,148 @@ +# We can build either as part of a standalone Kernel build or as +# an external module. Determine which mechanism is being used +ifeq ($(MODNAME),) + KERNEL_BUILD := 1 +else + KERNEL_BUILD := 0 +endif + + + +ifeq ($(KERNEL_BUILD), 1) + # These are configurable via Kconfig for kernel-based builds + # Need to explicitly configure for Android-based builds + AUDIO_BLD_DIR := $(shell pwd)/kernel/msm-4.14 + AUDIO_ROOT := $(AUDIO_BLD_DIR)/techpack/audio +endif + +ifeq ($(KERNEL_BUILD), 0) + ifeq ($(CONFIG_ARCH_SM6150), y) + include $(AUDIO_ROOT)/config/sm6150auto.conf + export + INCS += -include $(AUDIO_ROOT)/config/sm6150autoconf.h + endif + ifeq ($(CONFIG_ARCH_TRINKET), y) + include $(AUDIO_ROOT)/config/trinketauto.conf + export + INCS += -include $(AUDIO_ROOT)/config/trinketautoconf.h + endif + ifeq ($(CONFIG_ARCH_QCS405), y) + include $(AUDIO_ROOT)/config/qcs405auto.conf + export + INCS += -include $(AUDIO_ROOT)/config/qcs405autoconf.h + endif +endif + +# As per target team, build is done as follows: +# Defconfig : build with default flags +# Slub : defconfig + CONFIG_SLUB_DEBUG := y + +# CONFIG_SLUB_DEBUG_ON := y + CONFIG_PAGE_POISONING := y +# Perf : Using appropriate msmXXXX-perf_defconfig +# +# Shipment builds (user variants) should not have any debug feature +# enabled. This is identified using 'TARGET_BUILD_VARIANT'. Slub builds +# are identified using the CONFIG_SLUB_DEBUG_ON configuration. Since +# there is no other way to identify defconfig builds, QTI internal +# representation of perf builds (identified using the string 'perf'), +# is used to identify if the build is a slub or defconfig one. This +# way no critical debug feature will be enabled for perf and shipment +# builds. Other OEMs are also protected using the TARGET_BUILD_VARIANT +# config. + +############ UAPI ############ +UAPI_DIR := uapi +UAPI_INC := -I$(AUDIO_ROOT)/include/$(UAPI_DIR) + +############ COMMON ############ +COMMON_DIR := include +COMMON_INC := -I$(AUDIO_ROOT)/$(COMMON_DIR) + +############ BOLERO ############ + +# for BOLERO Codec +ifdef CONFIG_SND_SOC_BOLERO + BOLERO_OBJS += bolero-cdc.o + BOLERO_OBJS += bolero-cdc-utils.o + BOLERO_OBJS += bolero-cdc-regmap.o + BOLERO_OBJS += bolero-cdc-tables.o +endif + +ifdef CONFIG_WSA_MACRO + WSA_OBJS += wsa-macro.o +endif + +ifdef CONFIG_VA_MACRO + VA_OBJS += va-macro.o +endif + +ifdef CONFIG_TX_MACRO + TX_OBJS += tx-macro.o +endif + +ifdef CONFIG_RX_MACRO + RX_OBJS += rx-macro.o +endif + +LINUX_INC += -Iinclude/linux + +INCS += $(COMMON_INC) \ + $(UAPI_INC) + +EXTRA_CFLAGS += $(INCS) + + +CDEFINES += -DANI_LITTLE_BYTE_ENDIAN \ + -DANI_LITTLE_BIT_ENDIAN \ + -DDOT11F_LITTLE_ENDIAN_HOST \ + -DANI_COMPILER_TYPE_GCC \ + -DANI_OS_TYPE_ANDROID=6 \ + -DPTT_SOCK_SVC_ENABLE \ + -Wall\ + -Werror\ + -D__linux__ + +KBUILD_CPPFLAGS += $(CDEFINES) + +# Currently, for versions of gcc which support it, the kernel Makefile +# is disabling the maybe-uninitialized warning. Re-enable it for the +# AUDIO driver. Note that we must use EXTRA_CFLAGS here so that it +# will override the kernel settings. +ifeq ($(call cc-option-yn, -Wmaybe-uninitialized),y) +EXTRA_CFLAGS += -Wmaybe-uninitialized +endif +#EXTRA_CFLAGS += -Wmissing-prototypes + +ifeq ($(call cc-option-yn, -Wheader-guard),y) +EXTRA_CFLAGS += -Wheader-guard +endif + +ifeq ($(KERNEL_BUILD), 0) +KBUILD_EXTRA_SYMBOLS +=$(OUT)/obj/vendor/qcom/opensource/audio-kernel/ipc/Module.symvers +KBUILD_EXTRA_SYMBOLS +=$(OUT)/obj/vendor/qcom/opensource/audio-kernel/dsp/Module.symvers +KBUILD_EXTRA_SYMBOLS +=$(OUT)/obj/vendor/qcom/opensource/audio-kernel/asoc/Module.symvers +KBUILD_EXTRA_SYMBOLS +=$(OUT)/obj/vendor/qcom/opensource/audio-kernel/asoc/codecs/Module.symvers +KBUILD_EXTRA_SYMBOLS +=$(OUT)/obj/vendor/qcom/opensource/audio-kernel/soc/Module.symvers +endif + +ifeq ($(CONFIG_SND_SOC_GCOV), y) +GCOV_PROFILE := y +endif + +# Module information used by KBuild framework +obj-$(CONFIG_SND_SOC_BOLERO) += bolero_cdc_dlkm.o +bolero_cdc_dlkm-y := $(BOLERO_OBJS) + +obj-$(CONFIG_WSA_MACRO) += wsa_macro_dlkm.o +wsa_macro_dlkm-y := $(WSA_OBJS) + +obj-$(CONFIG_VA_MACRO) += va_macro_dlkm.o +va_macro_dlkm-y := $(VA_OBJS) + +obj-$(CONFIG_TX_MACRO) += tx_macro_dlkm.o +tx_macro_dlkm-y := $(TX_OBJS) + +obj-$(CONFIG_RX_MACRO) += rx_macro_dlkm.o +rx_macro_dlkm-y := $(RX_OBJS) + +# inject some build related information +DEFINES += -DBUILD_TIMESTAMP=\"$(shell date -u +'%Y-%m-%dT%H:%M:%SZ')\" diff --git a/audio-kernel/asoc/codecs/bolero/bolero-cdc-registers.h b/audio-kernel/asoc/codecs/bolero/bolero-cdc-registers.h new file mode 100644 index 000000000..d413ff961 --- /dev/null +++ b/audio-kernel/asoc/codecs/bolero/bolero-cdc-registers.h @@ -0,0 +1,840 @@ +/* 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 _BOLERO_CDC_REGISTERS_H +#define _BOLERO_CDC_REGISTERS_H + +#define TX_START_OFFSET 0x0000 + +#define BOLERO_CDC_TX_CLK_RST_CTRL_MCLK_CONTROL (TX_START_OFFSET + 0x0000) +#define BOLERO_CDC_TX_CLK_RST_CTRL_FS_CNT_CONTROL (TX_START_OFFSET + 0x0004) +#define BOLERO_CDC_TX_CLK_RST_CTRL_SWR_CONTROL (TX_START_OFFSET + 0x0008) +#define BOLERO_CDC_TX_TOP_CSR_TOP_CFG0 (TX_START_OFFSET + 0x0080) +#define BOLERO_CDC_TX_TOP_CSR_ANC_CFG (TX_START_OFFSET + 0x0084) +#define BOLERO_CDC_TX_TOP_CSR_SWR_CTRL (TX_START_OFFSET + 0x0088) +#define BOLERO_CDC_TX_TOP_CSR_FREQ_MCLK (TX_START_OFFSET + 0x0090) +#define BOLERO_CDC_TX_TOP_CSR_DEBUG_BUS (TX_START_OFFSET + 0x0094) +#define BOLERO_CDC_TX_TOP_CSR_DEBUG_EN (TX_START_OFFSET + 0x0098) +#define BOLERO_CDC_TX_TOP_CSR_TX_I2S_CTL (TX_START_OFFSET + 0x00A4) +#define BOLERO_CDC_TX_TOP_CSR_I2S_CLK (TX_START_OFFSET + 0x00A8) +#define BOLERO_CDC_TX_TOP_CSR_I2S_RESET (TX_START_OFFSET + 0x00AC) +#define BOLERO_CDC_TX_TOP_CSR_SWR_DMIC0_CTL (TX_START_OFFSET + 0x00C0) +#define BOLERO_CDC_TX_TOP_CSR_SWR_DMIC1_CTL (TX_START_OFFSET + 0x00C4) +#define BOLERO_CDC_TX_TOP_CSR_SWR_DMIC2_CTL (TX_START_OFFSET + 0x00C8) +#define BOLERO_CDC_TX_TOP_CSR_SWR_DMIC3_CTL (TX_START_OFFSET + 0x00CC) +#define BOLERO_CDC_TX_TOP_CSR_SWR_AMIC0_CTL (TX_START_OFFSET + 0x00D0) +#define BOLERO_CDC_TX_TOP_CSR_SWR_AMIC1_CTL (TX_START_OFFSET + 0x00D4) +#define BOLERO_CDC_TX_INP_MUX_ADC_MUX0_CFG0 (TX_START_OFFSET + 0x0100) +#define BOLERO_CDC_TX_INP_MUX_ADC_MUX0_CFG1 (TX_START_OFFSET + 0x0104) +#define BOLERO_CDC_TX_INP_MUX_ADC_MUX1_CFG0 (TX_START_OFFSET + 0x0108) +#define BOLERO_CDC_TX_INP_MUX_ADC_MUX1_CFG1 (TX_START_OFFSET + 0x010C) +#define BOLERO_CDC_TX_INP_MUX_ADC_MUX2_CFG0 (TX_START_OFFSET + 0x0110) +#define BOLERO_CDC_TX_INP_MUX_ADC_MUX2_CFG1 (TX_START_OFFSET + 0x0114) +#define BOLERO_CDC_TX_INP_MUX_ADC_MUX3_CFG0 (TX_START_OFFSET + 0x0118) +#define BOLERO_CDC_TX_INP_MUX_ADC_MUX3_CFG1 (TX_START_OFFSET + 0x011C) +#define BOLERO_CDC_TX_INP_MUX_ADC_MUX4_CFG0 (TX_START_OFFSET + 0x0120) +#define BOLERO_CDC_TX_INP_MUX_ADC_MUX4_CFG1 (TX_START_OFFSET + 0x0124) +#define BOLERO_CDC_TX_INP_MUX_ADC_MUX5_CFG0 (TX_START_OFFSET + 0x0128) +#define BOLERO_CDC_TX_INP_MUX_ADC_MUX5_CFG1 (TX_START_OFFSET + 0x012C) +#define BOLERO_CDC_TX_INP_MUX_ADC_MUX6_CFG0 (TX_START_OFFSET + 0x0130) +#define BOLERO_CDC_TX_INP_MUX_ADC_MUX6_CFG1 (TX_START_OFFSET + 0x0134) +#define BOLERO_CDC_TX_INP_MUX_ADC_MUX7_CFG0 (TX_START_OFFSET + 0x0138) +#define BOLERO_CDC_TX_INP_MUX_ADC_MUX7_CFG1 (TX_START_OFFSET + 0x013C) +#define BOLERO_CDC_TX_ANC0_CLK_RESET_CTL (TX_START_OFFSET + 0x0200) +#define BOLERO_CDC_TX_ANC0_MODE_1_CTL (TX_START_OFFSET + 0x0204) +#define BOLERO_CDC_TX_ANC0_MODE_2_CTL (TX_START_OFFSET + 0x0208) +#define BOLERO_CDC_TX_ANC0_FF_SHIFT (TX_START_OFFSET + 0x020C) +#define BOLERO_CDC_TX_ANC0_FB_SHIFT (TX_START_OFFSET + 0x0210) +#define BOLERO_CDC_TX_ANC0_LPF_FF_A_CTL (TX_START_OFFSET + 0x0214) +#define BOLERO_CDC_TX_ANC0_LPF_FF_B_CTL (TX_START_OFFSET + 0x0218) +#define BOLERO_CDC_TX_ANC0_LPF_FB_CTL (TX_START_OFFSET + 0x021C) +#define BOLERO_CDC_TX_ANC0_SMLPF_CTL (TX_START_OFFSET + 0x0220) +#define BOLERO_CDC_TX_ANC0_DCFLT_SHIFT_CTL (TX_START_OFFSET + 0x0224) +#define BOLERO_CDC_TX_ANC0_IIR_ADAPT_CTL (TX_START_OFFSET + 0x0228) +#define BOLERO_CDC_TX_ANC0_IIR_COEFF_1_CTL (TX_START_OFFSET + 0x022C) +#define BOLERO_CDC_TX_ANC0_IIR_COEFF_2_CTL (TX_START_OFFSET + 0x0230) +#define BOLERO_CDC_TX_ANC0_FF_A_GAIN_CTL (TX_START_OFFSET + 0x0234) +#define BOLERO_CDC_TX_ANC0_FF_B_GAIN_CTL (TX_START_OFFSET + 0x0238) +#define BOLERO_CDC_TX_ANC0_FB_GAIN_CTL (TX_START_OFFSET + 0x023C) +#define BOLERO_CDC_TX0_TX_PATH_CTL (TX_START_OFFSET + 0x0400) +#define BOLERO_CDC_TX0_TX_PATH_CFG0 (TX_START_OFFSET + 0x0404) +#define BOLERO_CDC_TX0_TX_PATH_CFG1 (TX_START_OFFSET + 0x0408) +#define BOLERO_CDC_TX0_TX_VOL_CTL (TX_START_OFFSET + 0x040C) +#define BOLERO_CDC_TX0_TX_PATH_SEC0 (TX_START_OFFSET + 0x0410) +#define BOLERO_CDC_TX0_TX_PATH_SEC1 (TX_START_OFFSET + 0x0414) +#define BOLERO_CDC_TX0_TX_PATH_SEC2 (TX_START_OFFSET + 0x0418) +#define BOLERO_CDC_TX0_TX_PATH_SEC3 (TX_START_OFFSET + 0x041C) +#define BOLERO_CDC_TX0_TX_PATH_SEC4 (TX_START_OFFSET + 0x0420) +#define BOLERO_CDC_TX0_TX_PATH_SEC5 (TX_START_OFFSET + 0x0424) +#define BOLERO_CDC_TX0_TX_PATH_SEC6 (TX_START_OFFSET + 0x0428) +#define BOLERO_CDC_TX0_TX_PATH_SEC7 (TX_START_OFFSET + 0x042C) +#define BOLERO_CDC_TX1_TX_PATH_CTL (TX_START_OFFSET + 0x0480) +#define BOLERO_CDC_TX1_TX_PATH_CFG0 (TX_START_OFFSET + 0x0484) +#define BOLERO_CDC_TX1_TX_PATH_CFG1 (TX_START_OFFSET + 0x0488) +#define BOLERO_CDC_TX1_TX_VOL_CTL (TX_START_OFFSET + 0x048C) +#define BOLERO_CDC_TX1_TX_PATH_SEC0 (TX_START_OFFSET + 0x0490) +#define BOLERO_CDC_TX1_TX_PATH_SEC1 (TX_START_OFFSET + 0x0494) +#define BOLERO_CDC_TX1_TX_PATH_SEC2 (TX_START_OFFSET + 0x0498) +#define BOLERO_CDC_TX1_TX_PATH_SEC3 (TX_START_OFFSET + 0x049C) +#define BOLERO_CDC_TX1_TX_PATH_SEC4 (TX_START_OFFSET + 0x04A0) +#define BOLERO_CDC_TX1_TX_PATH_SEC5 (TX_START_OFFSET + 0x04A4) +#define BOLERO_CDC_TX1_TX_PATH_SEC6 (TX_START_OFFSET + 0x04A8) +#define BOLERO_CDC_TX2_TX_PATH_CTL (TX_START_OFFSET + 0x0500) +#define BOLERO_CDC_TX2_TX_PATH_CFG0 (TX_START_OFFSET + 0x0504) +#define BOLERO_CDC_TX2_TX_PATH_CFG1 (TX_START_OFFSET + 0x0508) +#define BOLERO_CDC_TX2_TX_VOL_CTL (TX_START_OFFSET + 0x050C) +#define BOLERO_CDC_TX2_TX_PATH_SEC0 (TX_START_OFFSET + 0x0510) +#define BOLERO_CDC_TX2_TX_PATH_SEC1 (TX_START_OFFSET + 0x0514) +#define BOLERO_CDC_TX2_TX_PATH_SEC2 (TX_START_OFFSET + 0x0518) +#define BOLERO_CDC_TX2_TX_PATH_SEC3 (TX_START_OFFSET + 0x051C) +#define BOLERO_CDC_TX2_TX_PATH_SEC4 (TX_START_OFFSET + 0x0520) +#define BOLERO_CDC_TX2_TX_PATH_SEC5 (TX_START_OFFSET + 0x0524) +#define BOLERO_CDC_TX2_TX_PATH_SEC6 (TX_START_OFFSET + 0x0528) +#define BOLERO_CDC_TX3_TX_PATH_CTL (TX_START_OFFSET + 0x0580) +#define BOLERO_CDC_TX3_TX_PATH_CFG0 (TX_START_OFFSET + 0x0584) +#define BOLERO_CDC_TX3_TX_PATH_CFG1 (TX_START_OFFSET + 0x0588) +#define BOLERO_CDC_TX3_TX_VOL_CTL (TX_START_OFFSET + 0x058C) +#define BOLERO_CDC_TX3_TX_PATH_SEC0 (TX_START_OFFSET + 0x0590) +#define BOLERO_CDC_TX3_TX_PATH_SEC1 (TX_START_OFFSET + 0x0594) +#define BOLERO_CDC_TX3_TX_PATH_SEC2 (TX_START_OFFSET + 0x0598) +#define BOLERO_CDC_TX3_TX_PATH_SEC3 (TX_START_OFFSET + 0x059C) +#define BOLERO_CDC_TX3_TX_PATH_SEC4 (TX_START_OFFSET + 0x05A0) +#define BOLERO_CDC_TX3_TX_PATH_SEC5 (TX_START_OFFSET + 0x05A4) +#define BOLERO_CDC_TX3_TX_PATH_SEC6 (TX_START_OFFSET + 0x05A8) +#define BOLERO_CDC_TX4_TX_PATH_CTL (TX_START_OFFSET + 0x0600) +#define BOLERO_CDC_TX4_TX_PATH_CFG0 (TX_START_OFFSET + 0x0604) +#define BOLERO_CDC_TX4_TX_PATH_CFG1 (TX_START_OFFSET + 0x0608) +#define BOLERO_CDC_TX4_TX_VOL_CTL (TX_START_OFFSET + 0x060C) +#define BOLERO_CDC_TX4_TX_PATH_SEC0 (TX_START_OFFSET + 0x0610) +#define BOLERO_CDC_TX4_TX_PATH_SEC1 (TX_START_OFFSET + 0x0614) +#define BOLERO_CDC_TX4_TX_PATH_SEC2 (TX_START_OFFSET + 0x0618) +#define BOLERO_CDC_TX4_TX_PATH_SEC3 (TX_START_OFFSET + 0x061C) +#define BOLERO_CDC_TX4_TX_PATH_SEC4 (TX_START_OFFSET + 0x0620) +#define BOLERO_CDC_TX4_TX_PATH_SEC5 (TX_START_OFFSET + 0x0624) +#define BOLERO_CDC_TX4_TX_PATH_SEC6 (TX_START_OFFSET + 0x0628) +#define BOLERO_CDC_TX5_TX_PATH_CTL (TX_START_OFFSET + 0x0680) +#define BOLERO_CDC_TX5_TX_PATH_CFG0 (TX_START_OFFSET + 0x0684) +#define BOLERO_CDC_TX5_TX_PATH_CFG1 (TX_START_OFFSET + 0x0688) +#define BOLERO_CDC_TX5_TX_VOL_CTL (TX_START_OFFSET + 0x068C) +#define BOLERO_CDC_TX5_TX_PATH_SEC0 (TX_START_OFFSET + 0x0690) +#define BOLERO_CDC_TX5_TX_PATH_SEC1 (TX_START_OFFSET + 0x0694) +#define BOLERO_CDC_TX5_TX_PATH_SEC2 (TX_START_OFFSET + 0x0698) +#define BOLERO_CDC_TX5_TX_PATH_SEC3 (TX_START_OFFSET + 0x069C) +#define BOLERO_CDC_TX5_TX_PATH_SEC4 (TX_START_OFFSET + 0x06A0) +#define BOLERO_CDC_TX5_TX_PATH_SEC5 (TX_START_OFFSET + 0x06A4) +#define BOLERO_CDC_TX5_TX_PATH_SEC6 (TX_START_OFFSET + 0x06A8) +#define BOLERO_CDC_TX6_TX_PATH_CTL (TX_START_OFFSET + 0x0700) +#define BOLERO_CDC_TX6_TX_PATH_CFG0 (TX_START_OFFSET + 0x0704) +#define BOLERO_CDC_TX6_TX_PATH_CFG1 (TX_START_OFFSET + 0x0708) +#define BOLERO_CDC_TX6_TX_VOL_CTL (TX_START_OFFSET + 0x070C) +#define BOLERO_CDC_TX6_TX_PATH_SEC0 (TX_START_OFFSET + 0x0710) +#define BOLERO_CDC_TX6_TX_PATH_SEC1 (TX_START_OFFSET + 0x0714) +#define BOLERO_CDC_TX6_TX_PATH_SEC2 (TX_START_OFFSET + 0x0718) +#define BOLERO_CDC_TX6_TX_PATH_SEC3 (TX_START_OFFSET + 0x071C) +#define BOLERO_CDC_TX6_TX_PATH_SEC4 (TX_START_OFFSET + 0x0720) +#define BOLERO_CDC_TX6_TX_PATH_SEC5 (TX_START_OFFSET + 0x0724) +#define BOLERO_CDC_TX6_TX_PATH_SEC6 (TX_START_OFFSET + 0x0728) +#define BOLERO_CDC_TX7_TX_PATH_CTL (TX_START_OFFSET + 0x0780) +#define BOLERO_CDC_TX7_TX_PATH_CFG0 (TX_START_OFFSET + 0x0784) +#define BOLERO_CDC_TX7_TX_PATH_CFG1 (TX_START_OFFSET + 0x0788) +#define BOLERO_CDC_TX7_TX_VOL_CTL (TX_START_OFFSET + 0x078C) +#define BOLERO_CDC_TX7_TX_PATH_SEC0 (TX_START_OFFSET + 0x0790) +#define BOLERO_CDC_TX7_TX_PATH_SEC1 (TX_START_OFFSET + 0x0794) +#define BOLERO_CDC_TX7_TX_PATH_SEC2 (TX_START_OFFSET + 0x0798) +#define BOLERO_CDC_TX7_TX_PATH_SEC3 (TX_START_OFFSET + 0x079C) +#define BOLERO_CDC_TX7_TX_PATH_SEC4 (TX_START_OFFSET + 0x07A0) +#define BOLERO_CDC_TX7_TX_PATH_SEC5 (TX_START_OFFSET + 0x07A4) +#define BOLERO_CDC_TX7_TX_PATH_SEC6 (TX_START_OFFSET + 0x07A8) +#define TX_MAX_OFFSET (TX_START_OFFSET + 0x07A8) + +#define BOLERO_CDC_TX_MACRO_MAX 0x1EB /* 7A8/4 = 1EA + 1 */ + +#define RX_START_OFFSET 0x1000 +#define BOLERO_CDC_RX_TOP_TOP_CFG0 (RX_START_OFFSET + 0x0000) +#define BOLERO_CDC_RX_TOP_SWR_CTRL (RX_START_OFFSET + 0x0008) +#define BOLERO_CDC_RX_TOP_DEBUG (RX_START_OFFSET + 0x000C) +#define BOLERO_CDC_RX_TOP_DEBUG_BUS (RX_START_OFFSET + 0x0010) +#define BOLERO_CDC_RX_TOP_DEBUG_EN0 (RX_START_OFFSET + 0x0014) +#define BOLERO_CDC_RX_TOP_DEBUG_EN1 (RX_START_OFFSET + 0x0018) +#define BOLERO_CDC_RX_TOP_DEBUG_EN2 (RX_START_OFFSET + 0x001C) +#define BOLERO_CDC_RX_TOP_HPHL_COMP_WR_LSB (RX_START_OFFSET + 0x0020) +#define BOLERO_CDC_RX_TOP_HPHL_COMP_WR_MSB (RX_START_OFFSET + 0x0024) +#define BOLERO_CDC_RX_TOP_HPHL_COMP_LUT (RX_START_OFFSET + 0x0028) +#define BOLERO_CDC_RX_TOP_HPHL_COMP_RD_LSB (RX_START_OFFSET + 0x002C) +#define BOLERO_CDC_RX_TOP_HPHL_COMP_RD_MSB (RX_START_OFFSET + 0x0030) +#define BOLERO_CDC_RX_TOP_HPHR_COMP_WR_LSB (RX_START_OFFSET + 0x0034) +#define BOLERO_CDC_RX_TOP_HPHR_COMP_WR_MSB (RX_START_OFFSET + 0x0038) +#define BOLERO_CDC_RX_TOP_HPHR_COMP_LUT (RX_START_OFFSET + 0x003C) +#define BOLERO_CDC_RX_TOP_HPHR_COMP_RD_LSB (RX_START_OFFSET + 0x0040) +#define BOLERO_CDC_RX_TOP_HPHR_COMP_RD_MSB (RX_START_OFFSET + 0x0044) +#define BOLERO_CDC_RX_TOP_DSD0_DEBUG_CFG0 (RX_START_OFFSET + 0x0070) +#define BOLERO_CDC_RX_TOP_DSD0_DEBUG_CFG1 (RX_START_OFFSET + 0x0074) +#define BOLERO_CDC_RX_TOP_DSD0_DEBUG_CFG2 (RX_START_OFFSET + 0x0078) +#define BOLERO_CDC_RX_TOP_DSD0_DEBUG_CFG3 (RX_START_OFFSET + 0x007C) +#define BOLERO_CDC_RX_TOP_DSD1_DEBUG_CFG0 (RX_START_OFFSET + 0x0080) +#define BOLERO_CDC_RX_TOP_DSD1_DEBUG_CFG1 (RX_START_OFFSET + 0x0084) +#define BOLERO_CDC_RX_TOP_DSD1_DEBUG_CFG2 (RX_START_OFFSET + 0x0088) +#define BOLERO_CDC_RX_TOP_DSD1_DEBUG_CFG3 (RX_START_OFFSET + 0x008C) +#define BOLERO_CDC_RX_TOP_RX_I2S_CTL (RX_START_OFFSET + 0x0090) +#define BOLERO_CDC_RX_TOP_TX_I2S2_CTL (RX_START_OFFSET + 0x0094) +#define BOLERO_CDC_RX_TOP_I2S_CLK (RX_START_OFFSET + 0x0098) +#define BOLERO_CDC_RX_TOP_I2S_RESET (RX_START_OFFSET + 0x009C) +#define BOLERO_CDC_RX_TOP_I2S_MUX (RX_START_OFFSET + 0x00A0) +#define BOLERO_CDC_RX_CLK_RST_CTRL_MCLK_CONTROL (RX_START_OFFSET + 0x0100) +#define BOLERO_CDC_RX_CLK_RST_CTRL_FS_CNT_CONTROL \ + (RX_START_OFFSET + 0x0104) +#define BOLERO_CDC_RX_CLK_RST_CTRL_SWR_CONTROL (RX_START_OFFSET + 0x0108) +#define BOLERO_CDC_RX_CLK_RST_CTRL_DSD_CONTROL (RX_START_OFFSET + 0x010C) +#define BOLERO_CDC_RX_CLK_RST_CTRL_ASRC_SHARE_CONTROL \ + (RX_START_OFFSET + 0x0110) +#define BOLERO_CDC_RX_SOFTCLIP_CRC (RX_START_OFFSET + 0x0140) +#define BOLERO_CDC_RX_SOFTCLIP_SOFTCLIP_CTRL (RX_START_OFFSET + 0x0144) +#define BOLERO_CDC_RX_INP_MUX_RX_INT0_CFG0 (RX_START_OFFSET + 0x0180) +#define BOLERO_CDC_RX_INP_MUX_RX_INT0_CFG1 (RX_START_OFFSET + 0x0184) +#define BOLERO_CDC_RX_INP_MUX_RX_INT1_CFG0 (RX_START_OFFSET + 0x0188) +#define BOLERO_CDC_RX_INP_MUX_RX_INT1_CFG1 (RX_START_OFFSET + 0x018C) +#define BOLERO_CDC_RX_INP_MUX_RX_INT2_CFG0 (RX_START_OFFSET + 0x0190) +#define BOLERO_CDC_RX_INP_MUX_RX_INT2_CFG1 (RX_START_OFFSET + 0x0194) +#define BOLERO_CDC_RX_INP_MUX_RX_MIX_CFG4 (RX_START_OFFSET + 0x0198) +#define BOLERO_CDC_RX_INP_MUX_RX_MIX_CFG5 (RX_START_OFFSET + 0x019C) +#define BOLERO_CDC_RX_INP_MUX_SIDETONE_SRC_CFG0 (RX_START_OFFSET + 0x01A0) +#define BOLERO_CDC_RX_CLSH_CRC (RX_START_OFFSET + 0x0200) +#define BOLERO_CDC_RX_CLSH_DLY_CTRL (RX_START_OFFSET + 0x0204) +#define BOLERO_CDC_RX_CLSH_DECAY_CTRL (RX_START_OFFSET + 0x0208) +#define BOLERO_CDC_RX_CLSH_HPH_V_PA (RX_START_OFFSET + 0x020C) +#define BOLERO_CDC_RX_CLSH_EAR_V_PA (RX_START_OFFSET + 0x0210) +#define BOLERO_CDC_RX_CLSH_HPH_V_HD (RX_START_OFFSET + 0x0214) +#define BOLERO_CDC_RX_CLSH_EAR_V_HD (RX_START_OFFSET + 0x0218) +#define BOLERO_CDC_RX_CLSH_K1_MSB (RX_START_OFFSET + 0x021C) +#define BOLERO_CDC_RX_CLSH_K1_LSB (RX_START_OFFSET + 0x0220) +#define BOLERO_CDC_RX_CLSH_K2_MSB (RX_START_OFFSET + 0x0224) +#define BOLERO_CDC_RX_CLSH_K2_LSB (RX_START_OFFSET + 0x0228) +#define BOLERO_CDC_RX_CLSH_IDLE_CTRL (RX_START_OFFSET + 0x022C) +#define BOLERO_CDC_RX_CLSH_IDLE_HPH (RX_START_OFFSET + 0x0230) +#define BOLERO_CDC_RX_CLSH_IDLE_EAR (RX_START_OFFSET + 0x0234) +#define BOLERO_CDC_RX_CLSH_TEST0 (RX_START_OFFSET + 0x0238) +#define BOLERO_CDC_RX_CLSH_TEST1 (RX_START_OFFSET + 0x023C) +#define BOLERO_CDC_RX_CLSH_OVR_VREF (RX_START_OFFSET + 0x0240) +#define BOLERO_CDC_RX_CLSH_CLSG_CTL (RX_START_OFFSET + 0x0244) +#define BOLERO_CDC_RX_CLSH_CLSG_CFG1 (RX_START_OFFSET + 0x0248) +#define BOLERO_CDC_RX_CLSH_CLSG_CFG2 (RX_START_OFFSET + 0x024C) +#define BOLERO_CDC_RX_BCL_VBAT_PATH_CTL (RX_START_OFFSET + 0x0280) +#define BOLERO_CDC_RX_BCL_VBAT_CFG (RX_START_OFFSET + 0x0284) +#define BOLERO_CDC_RX_BCL_VBAT_ADC_CAL1 (RX_START_OFFSET + 0x0288) +#define BOLERO_CDC_RX_BCL_VBAT_ADC_CAL2 (RX_START_OFFSET + 0x028C) +#define BOLERO_CDC_RX_BCL_VBAT_ADC_CAL3 (RX_START_OFFSET + 0x0290) +#define BOLERO_CDC_RX_BCL_VBAT_PK_EST1 (RX_START_OFFSET + 0x0294) +#define BOLERO_CDC_RX_BCL_VBAT_PK_EST2 (RX_START_OFFSET + 0x0298) +#define BOLERO_CDC_RX_BCL_VBAT_PK_EST3 (RX_START_OFFSET + 0x029C) +#define BOLERO_CDC_RX_BCL_VBAT_RF_PROC1 (RX_START_OFFSET + 0x02A0) +#define BOLERO_CDC_RX_BCL_VBAT_RF_PROC2 (RX_START_OFFSET + 0x02A4) +#define BOLERO_CDC_RX_BCL_VBAT_TAC1 (RX_START_OFFSET + 0x02A8) +#define BOLERO_CDC_RX_BCL_VBAT_TAC2 (RX_START_OFFSET + 0x02AC) +#define BOLERO_CDC_RX_BCL_VBAT_TAC3 (RX_START_OFFSET + 0x02B0) +#define BOLERO_CDC_RX_BCL_VBAT_TAC4 (RX_START_OFFSET + 0x02B4) +#define BOLERO_CDC_RX_BCL_VBAT_GAIN_UPD1 (RX_START_OFFSET + 0x02B8) +#define BOLERO_CDC_RX_BCL_VBAT_GAIN_UPD2 (RX_START_OFFSET + 0x02BC) +#define BOLERO_CDC_RX_BCL_VBAT_GAIN_UPD3 (RX_START_OFFSET + 0x02C0) +#define BOLERO_CDC_RX_BCL_VBAT_GAIN_UPD4 (RX_START_OFFSET + 0x02C4) +#define BOLERO_CDC_RX_BCL_VBAT_GAIN_UPD5 (RX_START_OFFSET + 0x02C8) +#define BOLERO_CDC_RX_BCL_VBAT_DEBUG1 (RX_START_OFFSET + 0x02CC) +#define BOLERO_CDC_RX_BCL_VBAT_GAIN_UPD_MON (RX_START_OFFSET + 0x02D0) +#define BOLERO_CDC_RX_BCL_VBAT_GAIN_MON_VAL (RX_START_OFFSET + 0x02D4) +#define BOLERO_CDC_RX_BCL_VBAT_BAN (RX_START_OFFSET + 0x02D8) +#define BOLERO_CDC_RX_BCL_VBAT_BCL_GAIN_UPD1 (RX_START_OFFSET + 0x02DC) +#define BOLERO_CDC_RX_BCL_VBAT_BCL_GAIN_UPD2 (RX_START_OFFSET + 0x02E0) +#define BOLERO_CDC_RX_BCL_VBAT_BCL_GAIN_UPD3 (RX_START_OFFSET + 0x02E4) +#define BOLERO_CDC_RX_BCL_VBAT_BCL_GAIN_UPD4 (RX_START_OFFSET + 0x02E8) +#define BOLERO_CDC_RX_BCL_VBAT_BCL_GAIN_UPD5 (RX_START_OFFSET + 0x02EC) +#define BOLERO_CDC_RX_BCL_VBAT_BCL_GAIN_UPD6 (RX_START_OFFSET + 0x02F0) +#define BOLERO_CDC_RX_BCL_VBAT_BCL_GAIN_UPD7 (RX_START_OFFSET + 0x02F4) +#define BOLERO_CDC_RX_BCL_VBAT_BCL_GAIN_UPD8 (RX_START_OFFSET + 0x02F8) +#define BOLERO_CDC_RX_BCL_VBAT_BCL_GAIN_UPD9 (RX_START_OFFSET + 0x02FC) +#define BOLERO_CDC_RX_BCL_VBAT_ATTN1 (RX_START_OFFSET + 0x0300) +#define BOLERO_CDC_RX_BCL_VBAT_ATTN2 (RX_START_OFFSET + 0x0304) +#define BOLERO_CDC_RX_BCL_VBAT_ATTN3 (RX_START_OFFSET + 0x0308) +#define BOLERO_CDC_RX_BCL_VBAT_DECODE_CTL1 (RX_START_OFFSET + 0x030C) +#define BOLERO_CDC_RX_BCL_VBAT_DECODE_CTL2 (RX_START_OFFSET + 0x0310) +#define BOLERO_CDC_RX_BCL_VBAT_DECODE_CFG1 (RX_START_OFFSET + 0x0314) +#define BOLERO_CDC_RX_BCL_VBAT_DECODE_CFG2 (RX_START_OFFSET + 0x0318) +#define BOLERO_CDC_RX_BCL_VBAT_DECODE_CFG3 (RX_START_OFFSET + 0x031C) +#define BOLERO_CDC_RX_BCL_VBAT_DECODE_CFG4 (RX_START_OFFSET + 0x0320) +#define BOLERO_CDC_RX_BCL_VBAT_DECODE_ST (RX_START_OFFSET + 0x0324) +#define BOLERO_CDC_RX_INTR_CTRL_CFG (RX_START_OFFSET + 0x0340) +#define BOLERO_CDC_RX_INTR_CTRL_CLR_COMMIT (RX_START_OFFSET + 0x0344) +#define BOLERO_CDC_RX_INTR_CTRL_PIN1_MASK0 (RX_START_OFFSET + 0x0360) +#define BOLERO_CDC_RX_INTR_CTRL_PIN1_STATUS0 (RX_START_OFFSET + 0x0368) +#define BOLERO_CDC_RX_INTR_CTRL_PIN1_CLEAR0 (RX_START_OFFSET + 0x0370) +#define BOLERO_CDC_RX_INTR_CTRL_PIN2_MASK0 (RX_START_OFFSET + 0x0380) +#define BOLERO_CDC_RX_INTR_CTRL_PIN2_STATUS0 (RX_START_OFFSET + 0x0388) +#define BOLERO_CDC_RX_INTR_CTRL_PIN2_CLEAR0 (RX_START_OFFSET + 0x0390) +#define BOLERO_CDC_RX_INTR_CTRL_LEVEL0 (RX_START_OFFSET + 0x03C0) +#define BOLERO_CDC_RX_INTR_CTRL_BYPASS0 (RX_START_OFFSET + 0x03C8) +#define BOLERO_CDC_RX_INTR_CTRL_SET0 (RX_START_OFFSET + 0x03D0) +#define BOLERO_CDC_RX_RX0_RX_PATH_CTL (RX_START_OFFSET + 0x0400) +#define BOLERO_CDC_RX_RX0_RX_PATH_CFG0 (RX_START_OFFSET + 0x0404) +#define BOLERO_CDC_RX_RX0_RX_PATH_CFG1 (RX_START_OFFSET + 0x0408) +#define BOLERO_CDC_RX_RX0_RX_PATH_CFG2 (RX_START_OFFSET + 0x040C) +#define BOLERO_CDC_RX_RX0_RX_PATH_CFG3 (RX_START_OFFSET + 0x0410) +#define BOLERO_CDC_RX_RX0_RX_VOL_CTL (RX_START_OFFSET + 0x0414) +#define BOLERO_CDC_RX_RX0_RX_PATH_MIX_CTL (RX_START_OFFSET + 0x0418) +#define BOLERO_CDC_RX_RX0_RX_PATH_MIX_CFG (RX_START_OFFSET + 0x041C) +#define BOLERO_CDC_RX_RX0_RX_VOL_MIX_CTL (RX_START_OFFSET + 0x0420) +#define BOLERO_CDC_RX_RX0_RX_PATH_SEC1 (RX_START_OFFSET + 0x0424) +#define BOLERO_CDC_RX_RX0_RX_PATH_SEC2 (RX_START_OFFSET + 0x0428) +#define BOLERO_CDC_RX_RX0_RX_PATH_SEC3 (RX_START_OFFSET + 0x042C) +#define BOLERO_CDC_RX_RX0_RX_PATH_SEC4 (RX_START_OFFSET + 0x0430) +#define BOLERO_CDC_RX_RX0_RX_PATH_SEC7 (RX_START_OFFSET + 0x0434) +#define BOLERO_CDC_RX_RX0_RX_PATH_MIX_SEC0 (RX_START_OFFSET + 0x0438) +#define BOLERO_CDC_RX_RX0_RX_PATH_MIX_SEC1 (RX_START_OFFSET + 0x043C) +#define BOLERO_CDC_RX_RX0_RX_PATH_DSM_CTL (RX_START_OFFSET + 0x0440) +#define BOLERO_CDC_RX_RX0_RX_PATH_DSM_DATA1 (RX_START_OFFSET + 0x0444) +#define BOLERO_CDC_RX_RX0_RX_PATH_DSM_DATA2 (RX_START_OFFSET + 0x0448) +#define BOLERO_CDC_RX_RX0_RX_PATH_DSM_DATA3 (RX_START_OFFSET + 0x044C) +#define BOLERO_CDC_RX_RX0_RX_PATH_DSM_DATA4 (RX_START_OFFSET + 0x0450) +#define BOLERO_CDC_RX_RX0_RX_PATH_DSM_DATA5 (RX_START_OFFSET + 0x0454) +#define BOLERO_CDC_RX_RX0_RX_PATH_DSM_DATA6 (RX_START_OFFSET + 0x0458) +#define BOLERO_CDC_RX_RX1_RX_PATH_CTL (RX_START_OFFSET + 0x0480) +#define BOLERO_CDC_RX_RX1_RX_PATH_CFG0 (RX_START_OFFSET + 0x0484) +#define BOLERO_CDC_RX_RX1_RX_PATH_CFG1 (RX_START_OFFSET + 0x0488) +#define BOLERO_CDC_RX_RX1_RX_PATH_CFG2 (RX_START_OFFSET + 0x048C) +#define BOLERO_CDC_RX_RX1_RX_PATH_CFG3 (RX_START_OFFSET + 0x0490) +#define BOLERO_CDC_RX_RX1_RX_VOL_CTL (RX_START_OFFSET + 0x0494) +#define BOLERO_CDC_RX_RX1_RX_PATH_MIX_CTL (RX_START_OFFSET + 0x0498) +#define BOLERO_CDC_RX_RX1_RX_PATH_MIX_CFG (RX_START_OFFSET + 0x049C) +#define BOLERO_CDC_RX_RX1_RX_VOL_MIX_CTL (RX_START_OFFSET + 0x04A0) +#define BOLERO_CDC_RX_RX1_RX_PATH_SEC1 (RX_START_OFFSET + 0x04A4) +#define BOLERO_CDC_RX_RX1_RX_PATH_SEC2 (RX_START_OFFSET + 0x04A8) +#define BOLERO_CDC_RX_RX1_RX_PATH_SEC3 (RX_START_OFFSET + 0x04AC) +#define BOLERO_CDC_RX_RX1_RX_PATH_SEC4 (RX_START_OFFSET + 0x04B0) +#define BOLERO_CDC_RX_RX1_RX_PATH_SEC7 (RX_START_OFFSET + 0x04B4) +#define BOLERO_CDC_RX_RX1_RX_PATH_MIX_SEC0 (RX_START_OFFSET + 0x04B8) +#define BOLERO_CDC_RX_RX1_RX_PATH_MIX_SEC1 (RX_START_OFFSET + 0x04BC) +#define BOLERO_CDC_RX_RX1_RX_PATH_DSM_CTL (RX_START_OFFSET + 0x04C0) +#define BOLERO_CDC_RX_RX1_RX_PATH_DSM_DATA1 (RX_START_OFFSET + 0x04C4) +#define BOLERO_CDC_RX_RX1_RX_PATH_DSM_DATA2 (RX_START_OFFSET + 0x04C8) +#define BOLERO_CDC_RX_RX1_RX_PATH_DSM_DATA3 (RX_START_OFFSET + 0x04CC) +#define BOLERO_CDC_RX_RX1_RX_PATH_DSM_DATA4 (RX_START_OFFSET + 0x04D0) +#define BOLERO_CDC_RX_RX1_RX_PATH_DSM_DATA5 (RX_START_OFFSET + 0x04D4) +#define BOLERO_CDC_RX_RX1_RX_PATH_DSM_DATA6 (RX_START_OFFSET + 0x04D8) +#define BOLERO_CDC_RX_RX2_RX_PATH_CTL (RX_START_OFFSET + 0x0500) +#define BOLERO_CDC_RX_RX2_RX_PATH_CFG0 (RX_START_OFFSET + 0x0504) +#define BOLERO_CDC_RX_RX2_RX_PATH_CFG1 (RX_START_OFFSET + 0x0508) +#define BOLERO_CDC_RX_RX2_RX_PATH_CFG2 (RX_START_OFFSET + 0x050C) +#define BOLERO_CDC_RX_RX2_RX_PATH_CFG3 (RX_START_OFFSET + 0x0510) +#define BOLERO_CDC_RX_RX2_RX_VOL_CTL (RX_START_OFFSET + 0x0514) +#define BOLERO_CDC_RX_RX2_RX_PATH_MIX_CTL (RX_START_OFFSET + 0x0518) +#define BOLERO_CDC_RX_RX2_RX_PATH_MIX_CFG (RX_START_OFFSET + 0x051C) +#define BOLERO_CDC_RX_RX2_RX_VOL_MIX_CTL (RX_START_OFFSET + 0x0520) +#define BOLERO_CDC_RX_RX2_RX_PATH_SEC0 (RX_START_OFFSET + 0x0524) +#define BOLERO_CDC_RX_RX2_RX_PATH_SEC1 (RX_START_OFFSET + 0x0528) +#define BOLERO_CDC_RX_RX2_RX_PATH_SEC2 (RX_START_OFFSET + 0x052C) +#define BOLERO_CDC_RX_RX2_RX_PATH_SEC3 (RX_START_OFFSET + 0x0530) +#define BOLERO_CDC_RX_RX2_RX_PATH_SEC4 (RX_START_OFFSET + 0x0534) +#define BOLERO_CDC_RX_RX2_RX_PATH_SEC5 (RX_START_OFFSET + 0x0538) +#define BOLERO_CDC_RX_RX2_RX_PATH_SEC6 (RX_START_OFFSET + 0x053C) +#define BOLERO_CDC_RX_RX2_RX_PATH_SEC7 (RX_START_OFFSET + 0x0540) +#define BOLERO_CDC_RX_RX2_RX_PATH_MIX_SEC0 (RX_START_OFFSET + 0x0544) +#define BOLERO_CDC_RX_RX2_RX_PATH_MIX_SEC1 (RX_START_OFFSET + 0x0548) +#define BOLERO_CDC_RX_RX2_RX_PATH_DSM_CTL (RX_START_OFFSET + 0x054C) +#define BOLERO_CDC_RX_IDLE_DETECT_PATH_CTL (RX_START_OFFSET + 0x0780) +#define BOLERO_CDC_RX_IDLE_DETECT_CFG0 (RX_START_OFFSET + 0x0784) +#define BOLERO_CDC_RX_IDLE_DETECT_CFG1 (RX_START_OFFSET + 0x0788) +#define BOLERO_CDC_RX_IDLE_DETECT_CFG2 (RX_START_OFFSET + 0x078C) +#define BOLERO_CDC_RX_IDLE_DETECT_CFG3 (RX_START_OFFSET + 0x0790) +#define BOLERO_CDC_RX_COMPANDER0_CTL0 (RX_START_OFFSET + 0x0800) +#define BOLERO_CDC_RX_COMPANDER0_CTL1 (RX_START_OFFSET + 0x0804) +#define BOLERO_CDC_RX_COMPANDER0_CTL2 (RX_START_OFFSET + 0x0808) +#define BOLERO_CDC_RX_COMPANDER0_CTL3 (RX_START_OFFSET + 0x080C) +#define BOLERO_CDC_RX_COMPANDER0_CTL4 (RX_START_OFFSET + 0x0810) +#define BOLERO_CDC_RX_COMPANDER0_CTL5 (RX_START_OFFSET + 0x0814) +#define BOLERO_CDC_RX_COMPANDER0_CTL6 (RX_START_OFFSET + 0x0818) +#define BOLERO_CDC_RX_COMPANDER0_CTL7 (RX_START_OFFSET + 0x081C) +#define BOLERO_CDC_RX_COMPANDER1_CTL0 (RX_START_OFFSET + 0x0840) +#define BOLERO_CDC_RX_COMPANDER1_CTL1 (RX_START_OFFSET + 0x0844) +#define BOLERO_CDC_RX_COMPANDER1_CTL2 (RX_START_OFFSET + 0x0848) +#define BOLERO_CDC_RX_COMPANDER1_CTL3 (RX_START_OFFSET + 0x084C) +#define BOLERO_CDC_RX_COMPANDER1_CTL4 (RX_START_OFFSET + 0x0850) +#define BOLERO_CDC_RX_COMPANDER1_CTL5 (RX_START_OFFSET + 0x0854) +#define BOLERO_CDC_RX_COMPANDER1_CTL6 (RX_START_OFFSET + 0x0858) +#define BOLERO_CDC_RX_COMPANDER1_CTL7 (RX_START_OFFSET + 0x085C) +#define BOLERO_CDC_RX_SIDETONE_IIR0_IIR_PATH_CTL \ + (RX_START_OFFSET + 0x0A00) +#define BOLERO_CDC_RX_SIDETONE_IIR0_IIR_GAIN_B1_CTL \ + (RX_START_OFFSET + 0x0A04) +#define BOLERO_CDC_RX_SIDETONE_IIR0_IIR_GAIN_B2_CTL \ + (RX_START_OFFSET + 0x0A08) +#define BOLERO_CDC_RX_SIDETONE_IIR0_IIR_GAIN_B3_CTL \ + (RX_START_OFFSET + 0x0A0C) +#define BOLERO_CDC_RX_SIDETONE_IIR0_IIR_GAIN_B4_CTL \ + (RX_START_OFFSET + 0x0A10) +#define BOLERO_CDC_RX_SIDETONE_IIR0_IIR_GAIN_B5_CTL \ + (RX_START_OFFSET + 0x0A14) +#define BOLERO_CDC_RX_SIDETONE_IIR0_IIR_GAIN_B6_CTL \ + (RX_START_OFFSET + 0x0A18) +#define BOLERO_CDC_RX_SIDETONE_IIR0_IIR_GAIN_B7_CTL \ + (RX_START_OFFSET + 0x0A1C) +#define BOLERO_CDC_RX_SIDETONE_IIR0_IIR_GAIN_B8_CTL \ + (RX_START_OFFSET + 0x0A20) +#define BOLERO_CDC_RX_SIDETONE_IIR0_IIR_CTL (RX_START_OFFSET + 0x0A24) +#define BOLERO_CDC_RX_SIDETONE_IIR0_IIR_GAIN_TIMER_CTL \ + (RX_START_OFFSET + 0x0A28) +#define BOLERO_CDC_RX_SIDETONE_IIR0_IIR_COEF_B1_CTL \ + (RX_START_OFFSET + 0x0A2C) +#define BOLERO_CDC_RX_SIDETONE_IIR0_IIR_COEF_B2_CTL \ + (RX_START_OFFSET + 0x0A30) +#define BOLERO_CDC_RX_SIDETONE_IIR1_IIR_PATH_CTL \ + (RX_START_OFFSET + 0x0A80) +#define BOLERO_CDC_RX_SIDETONE_IIR1_IIR_GAIN_B1_CTL \ + (RX_START_OFFSET + 0x0A84) +#define BOLERO_CDC_RX_SIDETONE_IIR1_IIR_GAIN_B2_CTL \ + (RX_START_OFFSET + 0x0A88) +#define BOLERO_CDC_RX_SIDETONE_IIR1_IIR_GAIN_B3_CTL \ + (RX_START_OFFSET + 0x0A8C) +#define BOLERO_CDC_RX_SIDETONE_IIR1_IIR_GAIN_B4_CTL \ + (RX_START_OFFSET + 0x0A90) +#define BOLERO_CDC_RX_SIDETONE_IIR1_IIR_GAIN_B5_CTL \ + (RX_START_OFFSET + 0x0A94) +#define BOLERO_CDC_RX_SIDETONE_IIR1_IIR_GAIN_B6_CTL \ + (RX_START_OFFSET + 0x0A98) +#define BOLERO_CDC_RX_SIDETONE_IIR1_IIR_GAIN_B7_CTL \ + (RX_START_OFFSET + 0x0A9C) +#define BOLERO_CDC_RX_SIDETONE_IIR1_IIR_GAIN_B8_CTL \ + (RX_START_OFFSET + 0x0AA0) +#define BOLERO_CDC_RX_SIDETONE_IIR1_IIR_CTL (RX_START_OFFSET + 0x0AA4) +#define BOLERO_CDC_RX_SIDETONE_IIR1_IIR_GAIN_TIMER_CTL \ + (RX_START_OFFSET + 0x0AA8) +#define BOLERO_CDC_RX_SIDETONE_IIR1_IIR_COEF_B1_CTL \ + (RX_START_OFFSET + 0x0AAC) +#define BOLERO_CDC_RX_SIDETONE_IIR1_IIR_COEF_B2_CTL \ + (RX_START_OFFSET + 0x0AB0) +#define BOLERO_CDC_RX_IIR_INP_MUX_IIR0_MIX_CFG0 (RX_START_OFFSET + 0x0B00) +#define BOLERO_CDC_RX_IIR_INP_MUX_IIR0_MIX_CFG1 (RX_START_OFFSET + 0x0B04) +#define BOLERO_CDC_RX_IIR_INP_MUX_IIR0_MIX_CFG2 (RX_START_OFFSET + 0x0B08) +#define BOLERO_CDC_RX_IIR_INP_MUX_IIR0_MIX_CFG3 (RX_START_OFFSET + 0x0B0C) +#define BOLERO_CDC_RX_IIR_INP_MUX_IIR1_MIX_CFG0 (RX_START_OFFSET + 0x0B10) +#define BOLERO_CDC_RX_IIR_INP_MUX_IIR1_MIX_CFG1 (RX_START_OFFSET + 0x0B14) +#define BOLERO_CDC_RX_IIR_INP_MUX_IIR1_MIX_CFG2 (RX_START_OFFSET + 0x0B18) +#define BOLERO_CDC_RX_IIR_INP_MUX_IIR1_MIX_CFG3 (RX_START_OFFSET + 0x0B1C) +#define BOLERO_CDC_RX_SIDETONE_SRC0_ST_SRC_PATH_CTL \ + (RX_START_OFFSET + 0x0B40) +#define BOLERO_CDC_RX_SIDETONE_SRC0_ST_SRC_PATH_CFG1 \ + (RX_START_OFFSET + 0x0B44) +#define BOLERO_CDC_RX_SIDETONE_SRC1_ST_SRC_PATH_CTL \ + (RX_START_OFFSET + 0x0B50) +#define BOLERO_CDC_RX_SIDETONE_SRC1_ST_SRC_PATH_CFG1 \ + (RX_START_OFFSET + 0x0B54) +#define BOLERO_CDC_RX_EC_REF_HQ0_EC_REF_HQ_PATH_CTL \ + (RX_START_OFFSET + 0x0C00) +#define BOLERO_CDC_RX_EC_REF_HQ0_EC_REF_HQ_CFG0 (RX_START_OFFSET + 0x0C04) +#define BOLERO_CDC_RX_EC_REF_HQ1_EC_REF_HQ_PATH_CTL \ + (RX_START_OFFSET + 0x0C40) +#define BOLERO_CDC_RX_EC_REF_HQ1_EC_REF_HQ_CFG0 (RX_START_OFFSET + 0x0C44) +#define BOLERO_CDC_RX_EC_REF_HQ2_EC_REF_HQ_PATH_CTL \ + (RX_START_OFFSET + 0x0C80) +#define BOLERO_CDC_RX_EC_REF_HQ2_EC_REF_HQ_CFG0 (RX_START_OFFSET + 0x0C84) +#define BOLERO_CDC_RX_EC_ASRC0_CLK_RST_CTL (RX_START_OFFSET + 0x0D00) +#define BOLERO_CDC_RX_EC_ASRC0_CTL0 (RX_START_OFFSET + 0x0D04) +#define BOLERO_CDC_RX_EC_ASRC0_CTL1 (RX_START_OFFSET + 0x0D08) +#define BOLERO_CDC_RX_EC_ASRC0_FIFO_CTL (RX_START_OFFSET + 0x0D0C) +#define BOLERO_CDC_RX_EC_ASRC0_STATUS_FMIN_CNTR_LSB \ + (RX_START_OFFSET + 0x0D10) +#define BOLERO_CDC_RX_EC_ASRC0_STATUS_FMIN_CNTR_MSB \ + (RX_START_OFFSET + 0x0D14) +#define BOLERO_CDC_RX_EC_ASRC0_STATUS_FMAX_CNTR_LSB \ + (RX_START_OFFSET + 0x0D18) +#define BOLERO_CDC_RX_EC_ASRC0_STATUS_FMAX_CNTR_MSB \ + (RX_START_OFFSET + 0x0D1C) +#define BOLERO_CDC_RX_EC_ASRC0_STATUS_FIFO (RX_START_OFFSET + 0x0D20) +#define BOLERO_CDC_RX_EC_ASRC1_CLK_RST_CTL (RX_START_OFFSET + 0x0D40) +#define BOLERO_CDC_RX_EC_ASRC1_CTL0 (RX_START_OFFSET + 0x0D44) +#define BOLERO_CDC_RX_EC_ASRC1_CTL1 (RX_START_OFFSET + 0x0D48) +#define BOLERO_CDC_RX_EC_ASRC1_FIFO_CTL (RX_START_OFFSET + 0x0D4C) +#define BOLERO_CDC_RX_EC_ASRC1_STATUS_FMIN_CNTR_LSB \ + (RX_START_OFFSET + 0x0D50) +#define BOLERO_CDC_RX_EC_ASRC1_STATUS_FMIN_CNTR_MSB \ + (RX_START_OFFSET + 0x0D54) +#define BOLERO_CDC_RX_EC_ASRC1_STATUS_FMAX_CNTR_LSB \ + (RX_START_OFFSET + 0x0D58) +#define BOLERO_CDC_RX_EC_ASRC1_STATUS_FMAX_CNTR_MSB \ + (RX_START_OFFSET + 0x0D5C) +#define BOLERO_CDC_RX_EC_ASRC1_STATUS_FIFO (RX_START_OFFSET + 0x0D60) +#define BOLERO_CDC_RX_EC_ASRC2_CLK_RST_CTL (RX_START_OFFSET + 0x0D80) +#define BOLERO_CDC_RX_EC_ASRC2_CTL0 (RX_START_OFFSET + 0x0D84) +#define BOLERO_CDC_RX_EC_ASRC2_CTL1 (RX_START_OFFSET + 0x0D88) +#define BOLERO_CDC_RX_EC_ASRC2_FIFO_CTL (RX_START_OFFSET + 0x0D8C) +#define BOLERO_CDC_RX_EC_ASRC2_STATUS_FMIN_CNTR_LSB \ + (RX_START_OFFSET + 0x0D90) +#define BOLERO_CDC_RX_EC_ASRC2_STATUS_FMIN_CNTR_MSB \ + (RX_START_OFFSET + 0x0D94) +#define BOLERO_CDC_RX_EC_ASRC2_STATUS_FMAX_CNTR_LSB \ + (RX_START_OFFSET + 0x0D98) +#define BOLERO_CDC_RX_EC_ASRC2_STATUS_FMAX_CNTR_MSB \ + (RX_START_OFFSET + 0x0D9C) +#define BOLERO_CDC_RX_EC_ASRC2_STATUS_FIFO (RX_START_OFFSET + 0x0DA0) +#define BOLERO_CDC_RX_DSD0_PATH_CTL (RX_START_OFFSET + 0x0F00) +#define BOLERO_CDC_RX_DSD0_CFG0 (RX_START_OFFSET + 0x0F04) +#define BOLERO_CDC_RX_DSD0_CFG1 (RX_START_OFFSET + 0x0F08) +#define BOLERO_CDC_RX_DSD0_CFG2 (RX_START_OFFSET + 0x0F0C) +#define BOLERO_CDC_RX_DSD1_PATH_CTL (RX_START_OFFSET + 0x0F80) +#define BOLERO_CDC_RX_DSD1_CFG0 (RX_START_OFFSET + 0x0F84) +#define BOLERO_CDC_RX_DSD1_CFG1 (RX_START_OFFSET + 0x0F88) +#define BOLERO_CDC_RX_DSD1_CFG2 (RX_START_OFFSET + 0x0F8C) +#define RX_MAX_OFFSET (RX_START_OFFSET + 0x0F8C) + +#define BOLERO_CDC_RX_MACRO_MAX 0x3E4 /* F8C/4 = 3E3 + 1 */ + +/* WSA - macro#2 */ +#define WSA_START_OFFSET 0x2000 +#define BOLERO_CDC_WSA_CLK_RST_CTRL_MCLK_CONTROL \ + (WSA_START_OFFSET + 0x0000) +#define BOLERO_CDC_WSA_CLK_RST_CTRL_FS_CNT_CONTROL \ + (WSA_START_OFFSET + 0x0004) +#define BOLERO_CDC_WSA_CLK_RST_CTRL_SWR_CONTROL (WSA_START_OFFSET + 0x0008) +#define BOLERO_CDC_WSA_TOP_TOP_CFG0 (WSA_START_OFFSET + 0x0080) +#define BOLERO_CDC_WSA_TOP_TOP_CFG1 (WSA_START_OFFSET + 0x0084) +#define BOLERO_CDC_WSA_TOP_FREQ_MCLK (WSA_START_OFFSET + 0x0088) +#define BOLERO_CDC_WSA_TOP_DEBUG_BUS_SEL (WSA_START_OFFSET + 0x008C) +#define BOLERO_CDC_WSA_TOP_DEBUG_EN0 (WSA_START_OFFSET + 0x0090) +#define BOLERO_CDC_WSA_TOP_DEBUG_EN1 (WSA_START_OFFSET + 0x0094) +#define BOLERO_CDC_WSA_TOP_DEBUG_DSM_LB (WSA_START_OFFSET + 0x0098) +#define BOLERO_CDC_WSA_TOP_RX_I2S_CTL (WSA_START_OFFSET + 0x009C) +#define BOLERO_CDC_WSA_TOP_TX_I2S_CTL (WSA_START_OFFSET + 0x00A0) +#define BOLERO_CDC_WSA_TOP_I2S_CLK (WSA_START_OFFSET + 0x00A4) +#define BOLERO_CDC_WSA_TOP_I2S_RESET (WSA_START_OFFSET + 0x00A8) +#define BOLERO_CDC_WSA_RX_INP_MUX_RX_INT0_CFG0 (WSA_START_OFFSET + 0x0100) +#define BOLERO_CDC_WSA_RX_INP_MUX_RX_INT0_CFG1 (WSA_START_OFFSET + 0x0104) +#define BOLERO_CDC_WSA_RX_INP_MUX_RX_INT1_CFG0 (WSA_START_OFFSET + 0x0108) +#define BOLERO_CDC_WSA_RX_INP_MUX_RX_INT1_CFG1 (WSA_START_OFFSET + 0x010C) +#define BOLERO_CDC_WSA_RX_INP_MUX_RX_MIX_CFG0 (WSA_START_OFFSET + 0x0110) +#define BOLERO_CDC_WSA_RX_INP_MUX_RX_EC_CFG0 (WSA_START_OFFSET + 0x0114) +#define BOLERO_CDC_WSA_RX_INP_MUX_SOFTCLIP_CFG0 (WSA_START_OFFSET + 0x0118) +/* VBAT registers */ +#define BOLERO_CDC_WSA_VBAT_BCL_VBAT_PATH_CTL (WSA_START_OFFSET + 0x0180) +#define BOLERO_CDC_WSA_VBAT_BCL_VBAT_CFG (WSA_START_OFFSET + 0x0184) +#define BOLERO_CDC_WSA_VBAT_BCL_VBAT_ADC_CAL1 (WSA_START_OFFSET + 0x0188) +#define BOLERO_CDC_WSA_VBAT_BCL_VBAT_ADC_CAL2 (WSA_START_OFFSET + 0x018C) +#define BOLERO_CDC_WSA_VBAT_BCL_VBAT_ADC_CAL3 (WSA_START_OFFSET + 0x0190) +#define BOLERO_CDC_WSA_VBAT_BCL_VBAT_PK_EST1 (WSA_START_OFFSET + 0x0194) +#define BOLERO_CDC_WSA_VBAT_BCL_VBAT_PK_EST2 (WSA_START_OFFSET + 0x0198) +#define BOLERO_CDC_WSA_VBAT_BCL_VBAT_PK_EST3 (WSA_START_OFFSET + 0x019C) +#define BOLERO_CDC_WSA_VBAT_BCL_VBAT_RF_PROC1 (WSA_START_OFFSET + 0x01A0) +#define BOLERO_CDC_WSA_VBAT_BCL_VBAT_RF_PROC2 (WSA_START_OFFSET + 0x01A4) +#define BOLERO_CDC_WSA_VBAT_BCL_VBAT_TAC1 (WSA_START_OFFSET + 0x01A8) +#define BOLERO_CDC_WSA_VBAT_BCL_VBAT_TAC2 (WSA_START_OFFSET + 0x01AC) +#define BOLERO_CDC_WSA_VBAT_BCL_VBAT_TAC3 (WSA_START_OFFSET + 0x01B0) +#define BOLERO_CDC_WSA_VBAT_BCL_VBAT_TAC4 (WSA_START_OFFSET + 0x01B4) +#define BOLERO_CDC_WSA_VBAT_BCL_VBAT_GAIN_UPD1 (WSA_START_OFFSET + 0x01B8) +#define BOLERO_CDC_WSA_VBAT_BCL_VBAT_GAIN_UPD2 (WSA_START_OFFSET + 0x01BC) +#define BOLERO_CDC_WSA_VBAT_BCL_VBAT_GAIN_UPD3 (WSA_START_OFFSET + 0x01C0) +#define BOLERO_CDC_WSA_VBAT_BCL_VBAT_GAIN_UPD4 (WSA_START_OFFSET + 0x01C4) +#define BOLERO_CDC_WSA_VBAT_BCL_VBAT_GAIN_UPD5 (WSA_START_OFFSET + 0x01C8) +#define BOLERO_CDC_WSA_VBAT_BCL_VBAT_DEBUG1 (WSA_START_OFFSET + 0x01CC) +#define BOLERO_CDC_WSA_VBAT_BCL_VBAT_GAIN_UPD_MON \ + (WSA_START_OFFSET + 0x01D0) +#define BOLERO_CDC_WSA_VBAT_BCL_VBAT_GAIN_MON_VAL \ + (WSA_START_OFFSET + 0x01D4) +#define BOLERO_CDC_WSA_VBAT_BCL_VBAT_BAN (WSA_START_OFFSET + 0x01D8) +#define BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_GAIN_UPD1 \ + (WSA_START_OFFSET + 0x01DC) +#define BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_GAIN_UPD2 \ + (WSA_START_OFFSET + 0x01E0) +#define BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_GAIN_UPD3 \ + (WSA_START_OFFSET + 0x01E4) +#define BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_GAIN_UPD4 \ + (WSA_START_OFFSET + 0x01E8) +#define BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_GAIN_UPD5 \ + (WSA_START_OFFSET + 0x01EC) +#define BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_GAIN_UPD6 \ + (WSA_START_OFFSET + 0x01F0) +#define BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_GAIN_UPD7 \ + (WSA_START_OFFSET + 0x01F4) +#define BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_GAIN_UPD8 \ + (WSA_START_OFFSET + 0x01F8) +#define BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_GAIN_UPD9 \ + (WSA_START_OFFSET + 0x01FC) +#define BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_ATTN1 (WSA_START_OFFSET + 0x0200) +#define BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_ATTN2 (WSA_START_OFFSET + 0x0204) +#define BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_ATTN3 (WSA_START_OFFSET + 0x0208) +#define BOLERO_CDC_WSA_VBAT_BCL_VBAT_DECODE_CTL1 \ + (WSA_START_OFFSET + 0x020C) +#define BOLERO_CDC_WSA_VBAT_BCL_VBAT_DECODE_CTL2 \ + (WSA_START_OFFSET + 0x0210) +#define BOLERO_CDC_WSA_VBAT_BCL_VBAT_DECODE_CFG1 \ + (WSA_START_OFFSET + 0x0214) +#define BOLERO_CDC_WSA_VBAT_BCL_VBAT_DECODE_CFG2 \ + (WSA_START_OFFSET + 0x0218) +#define BOLERO_CDC_WSA_VBAT_BCL_VBAT_DECODE_CFG3 \ + (WSA_START_OFFSET + 0x021C) +#define BOLERO_CDC_WSA_VBAT_BCL_VBAT_DECODE_CFG4 \ + (WSA_START_OFFSET + 0x0220) +#define BOLERO_CDC_WSA_VBAT_BCL_VBAT_DECODE_ST (WSA_START_OFFSET + 0x0224) +#define BOLERO_CDC_WSA_TX0_SPKR_PROT_PATH_CTL (WSA_START_OFFSET + 0x0244) +#define BOLERO_CDC_WSA_TX0_SPKR_PROT_PATH_CFG0 (WSA_START_OFFSET + 0x0248) +#define BOLERO_CDC_WSA_TX1_SPKR_PROT_PATH_CTL (WSA_START_OFFSET + 0x0264) +#define BOLERO_CDC_WSA_TX1_SPKR_PROT_PATH_CFG0 (WSA_START_OFFSET + 0x0268) +#define BOLERO_CDC_WSA_TX2_SPKR_PROT_PATH_CTL (WSA_START_OFFSET + 0x0284) +#define BOLERO_CDC_WSA_TX2_SPKR_PROT_PATH_CFG0 (WSA_START_OFFSET + 0x0288) +#define BOLERO_CDC_WSA_TX3_SPKR_PROT_PATH_CTL (WSA_START_OFFSET + 0x02A4) +#define BOLERO_CDC_WSA_TX3_SPKR_PROT_PATH_CFG0 (WSA_START_OFFSET + 0x02A8) +#define BOLERO_CDC_WSA_INTR_CTRL_CFG (WSA_START_OFFSET + 0x0340) +#define BOLERO_CDC_WSA_INTR_CTRL_CLR_COMMIT (WSA_START_OFFSET + 0x0344) +#define BOLERO_CDC_WSA_INTR_CTRL_PIN1_MASK0 (WSA_START_OFFSET + 0x0360) +#define BOLERO_CDC_WSA_INTR_CTRL_PIN1_STATUS0 (WSA_START_OFFSET + 0x0368) +#define BOLERO_CDC_WSA_INTR_CTRL_PIN1_CLEAR0 (WSA_START_OFFSET + 0x0370) +#define BOLERO_CDC_WSA_INTR_CTRL_PIN2_MASK0 (WSA_START_OFFSET + 0x0380) +#define BOLERO_CDC_WSA_INTR_CTRL_PIN2_STATUS0 (WSA_START_OFFSET + 0x0388) +#define BOLERO_CDC_WSA_INTR_CTRL_PIN2_CLEAR0 (WSA_START_OFFSET + 0x0390) +#define BOLERO_CDC_WSA_INTR_CTRL_LEVEL0 (WSA_START_OFFSET + 0x03C0) +#define BOLERO_CDC_WSA_INTR_CTRL_BYPASS0 (WSA_START_OFFSET + 0x03C8) +#define BOLERO_CDC_WSA_INTR_CTRL_SET0 (WSA_START_OFFSET + 0x03D0) +#define BOLERO_CDC_WSA_RX0_RX_PATH_CTL (WSA_START_OFFSET + 0x0400) +#define BOLERO_CDC_WSA_RX0_RX_PATH_CFG0 (WSA_START_OFFSET + 0x0404) +#define BOLERO_CDC_WSA_RX0_RX_PATH_CFG1 (WSA_START_OFFSET + 0x0408) +#define BOLERO_CDC_WSA_RX0_RX_PATH_CFG2 (WSA_START_OFFSET + 0x040C) +#define BOLERO_CDC_WSA_RX0_RX_PATH_CFG3 (WSA_START_OFFSET + 0x0410) +#define BOLERO_CDC_WSA_RX0_RX_VOL_CTL (WSA_START_OFFSET + 0x0414) +#define BOLERO_CDC_WSA_RX0_RX_PATH_MIX_CTL (WSA_START_OFFSET + 0x0418) +#define BOLERO_CDC_WSA_RX0_RX_PATH_MIX_CFG (WSA_START_OFFSET + 0x041C) +#define BOLERO_CDC_WSA_RX0_RX_VOL_MIX_CTL (WSA_START_OFFSET + 0x0420) +#define BOLERO_CDC_WSA_RX0_RX_PATH_SEC0 (WSA_START_OFFSET + 0x0424) +#define BOLERO_CDC_WSA_RX0_RX_PATH_SEC1 (WSA_START_OFFSET + 0x0428) +#define BOLERO_CDC_WSA_RX0_RX_PATH_SEC2 (WSA_START_OFFSET + 0x042C) +#define BOLERO_CDC_WSA_RX0_RX_PATH_SEC3 (WSA_START_OFFSET + 0x0430) +#define BOLERO_CDC_WSA_RX0_RX_PATH_SEC5 (WSA_START_OFFSET + 0x0438) +#define BOLERO_CDC_WSA_RX0_RX_PATH_SEC6 (WSA_START_OFFSET + 0x043C) +#define BOLERO_CDC_WSA_RX0_RX_PATH_SEC7 (WSA_START_OFFSET + 0x0440) +#define BOLERO_CDC_WSA_RX0_RX_PATH_MIX_SEC0 (WSA_START_OFFSET + 0x0444) +#define BOLERO_CDC_WSA_RX0_RX_PATH_MIX_SEC1 (WSA_START_OFFSET + 0x0448) +#define BOLERO_CDC_WSA_RX0_RX_PATH_DSMDEM_CTL (WSA_START_OFFSET + 0x044C) +#define BOLERO_CDC_WSA_RX1_RX_PATH_CTL (WSA_START_OFFSET + 0x0480) +#define BOLERO_CDC_WSA_RX1_RX_PATH_CFG0 (WSA_START_OFFSET + 0x0484) +#define BOLERO_CDC_WSA_RX1_RX_PATH_CFG1 (WSA_START_OFFSET + 0x0488) +#define BOLERO_CDC_WSA_RX1_RX_PATH_CFG2 (WSA_START_OFFSET + 0x048C) +#define BOLERO_CDC_WSA_RX1_RX_PATH_CFG3 (WSA_START_OFFSET + 0x0490) +#define BOLERO_CDC_WSA_RX1_RX_VOL_CTL (WSA_START_OFFSET + 0x0494) +#define BOLERO_CDC_WSA_RX1_RX_PATH_MIX_CTL (WSA_START_OFFSET + 0x0498) +#define BOLERO_CDC_WSA_RX1_RX_PATH_MIX_CFG (WSA_START_OFFSET + 0x049C) +#define BOLERO_CDC_WSA_RX1_RX_VOL_MIX_CTL (WSA_START_OFFSET + 0x04A0) +#define BOLERO_CDC_WSA_RX1_RX_PATH_SEC0 (WSA_START_OFFSET + 0x04A4) +#define BOLERO_CDC_WSA_RX1_RX_PATH_SEC1 (WSA_START_OFFSET + 0x04A8) +#define BOLERO_CDC_WSA_RX1_RX_PATH_SEC2 (WSA_START_OFFSET + 0x04AC) +#define BOLERO_CDC_WSA_RX1_RX_PATH_SEC3 (WSA_START_OFFSET + 0x04B0) +#define BOLERO_CDC_WSA_RX1_RX_PATH_SEC5 (WSA_START_OFFSET + 0x04B8) +#define BOLERO_CDC_WSA_RX1_RX_PATH_SEC6 (WSA_START_OFFSET + 0x04BC) +#define BOLERO_CDC_WSA_RX1_RX_PATH_SEC7 (WSA_START_OFFSET + 0x04C0) +#define BOLERO_CDC_WSA_RX1_RX_PATH_MIX_SEC0 (WSA_START_OFFSET + 0x04C4) +#define BOLERO_CDC_WSA_RX1_RX_PATH_MIX_SEC1 (WSA_START_OFFSET + 0x04C8) +#define BOLERO_CDC_WSA_RX1_RX_PATH_DSMDEM_CTL (WSA_START_OFFSET + 0x04CC) +#define BOLERO_CDC_WSA_BOOST0_BOOST_PATH_CTL (WSA_START_OFFSET + 0x0500) +#define BOLERO_CDC_WSA_BOOST0_BOOST_CTL (WSA_START_OFFSET + 0x0504) +#define BOLERO_CDC_WSA_BOOST0_BOOST_CFG1 (WSA_START_OFFSET + 0x0508) +#define BOLERO_CDC_WSA_BOOST0_BOOST_CFG2 (WSA_START_OFFSET + 0x050C) +#define BOLERO_CDC_WSA_BOOST1_BOOST_PATH_CTL (WSA_START_OFFSET + 0x0540) +#define BOLERO_CDC_WSA_BOOST1_BOOST_CTL (WSA_START_OFFSET + 0x0544) +#define BOLERO_CDC_WSA_BOOST1_BOOST_CFG1 (WSA_START_OFFSET + 0x0548) +#define BOLERO_CDC_WSA_BOOST1_BOOST_CFG2 (WSA_START_OFFSET + 0x054C) +#define BOLERO_CDC_WSA_COMPANDER0_CTL0 (WSA_START_OFFSET + 0x0580) +#define BOLERO_CDC_WSA_COMPANDER0_CTL1 (WSA_START_OFFSET + 0x0584) +#define BOLERO_CDC_WSA_COMPANDER0_CTL2 (WSA_START_OFFSET + 0x0588) +#define BOLERO_CDC_WSA_COMPANDER0_CTL3 (WSA_START_OFFSET + 0x058C) +#define BOLERO_CDC_WSA_COMPANDER0_CTL4 (WSA_START_OFFSET + 0x0590) +#define BOLERO_CDC_WSA_COMPANDER0_CTL5 (WSA_START_OFFSET + 0x0594) +#define BOLERO_CDC_WSA_COMPANDER0_CTL6 (WSA_START_OFFSET + 0x0598) +#define BOLERO_CDC_WSA_COMPANDER0_CTL7 (WSA_START_OFFSET + 0x059C) +#define BOLERO_CDC_WSA_COMPANDER1_CTL0 (WSA_START_OFFSET + 0x05C0) +#define BOLERO_CDC_WSA_COMPANDER1_CTL1 (WSA_START_OFFSET + 0x05C4) +#define BOLERO_CDC_WSA_COMPANDER1_CTL2 (WSA_START_OFFSET + 0x05C8) +#define BOLERO_CDC_WSA_COMPANDER1_CTL3 (WSA_START_OFFSET + 0x05CC) +#define BOLERO_CDC_WSA_COMPANDER1_CTL4 (WSA_START_OFFSET + 0x05D0) +#define BOLERO_CDC_WSA_COMPANDER1_CTL5 (WSA_START_OFFSET + 0x05D4) +#define BOLERO_CDC_WSA_COMPANDER1_CTL6 (WSA_START_OFFSET + 0x05D8) +#define BOLERO_CDC_WSA_COMPANDER1_CTL7 (WSA_START_OFFSET + 0x05DC) +#define BOLERO_CDC_WSA_SOFTCLIP0_CRC (WSA_START_OFFSET + 0x0600) +#define BOLERO_CDC_WSA_SOFTCLIP0_SOFTCLIP_CTRL (WSA_START_OFFSET + 0x0604) +#define BOLERO_CDC_WSA_SOFTCLIP1_CRC (WSA_START_OFFSET + 0x0640) +#define BOLERO_CDC_WSA_SOFTCLIP1_SOFTCLIP_CTRL (WSA_START_OFFSET + 0x0644) +#define BOLERO_CDC_WSA_EC_HQ0_EC_REF_HQ_PATH_CTL \ + (WSA_START_OFFSET + 0x0680) +#define BOLERO_CDC_WSA_EC_HQ0_EC_REF_HQ_CFG0 (WSA_START_OFFSET + 0x0684) +#define BOLERO_CDC_WSA_EC_HQ1_EC_REF_HQ_PATH_CTL \ + (WSA_START_OFFSET + 0x06C0) +#define BOLERO_CDC_WSA_EC_HQ1_EC_REF_HQ_CFG0 (WSA_START_OFFSET + 0x06C4) +#define BOLERO_CDC_WSA_SPLINE_ASRC0_CLK_RST_CTL (WSA_START_OFFSET + 0x0700) +#define BOLERO_CDC_WSA_SPLINE_ASRC0_CTL0 (WSA_START_OFFSET + 0x0704) +#define BOLERO_CDC_WSA_SPLINE_ASRC0_CTL1 (WSA_START_OFFSET + 0x0708) +#define BOLERO_CDC_WSA_SPLINE_ASRC0_FIFO_CTL (WSA_START_OFFSET + 0x070C) +#define BOLERO_CDC_WSA_SPLINE_ASRC0_STATUS_FMIN_CNTR_LSB \ + (WSA_START_OFFSET + 0x0710) +#define BOLERO_CDC_WSA_SPLINE_ASRC0_STATUS_FMIN_CNTR_MSB \ + (WSA_START_OFFSET + 0x0714) +#define BOLERO_CDC_WSA_SPLINE_ASRC0_STATUS_FMAX_CNTR_LSB \ + (WSA_START_OFFSET + 0x0718) +#define BOLERO_CDC_WSA_SPLINE_ASRC0_STATUS_FMAX_CNTR_MSB \ + (WSA_START_OFFSET + 0x071C) +#define BOLERO_CDC_WSA_SPLINE_ASRC0_STATUS_FIFO (WSA_START_OFFSET + 0x0720) +#define BOLERO_CDC_WSA_SPLINE_ASRC1_CLK_RST_CTL (WSA_START_OFFSET + 0x0740) +#define BOLERO_CDC_WSA_SPLINE_ASRC1_CTL0 (WSA_START_OFFSET + 0x0744) +#define BOLERO_CDC_WSA_SPLINE_ASRC1_CTL1 (WSA_START_OFFSET + 0x0748) +#define BOLERO_CDC_WSA_SPLINE_ASRC1_FIFO_CTL (WSA_START_OFFSET + 0x074C) +#define BOLERO_CDC_WSA_SPLINE_ASRC1_STATUS_FMIN_CNTR_LSB \ + (WSA_START_OFFSET + 0x0750) +#define BOLERO_CDC_WSA_SPLINE_ASRC1_STATUS_FMIN_CNTR_MSB \ + (WSA_START_OFFSET + 0x0754) +#define BOLERO_CDC_WSA_SPLINE_ASRC1_STATUS_FMAX_CNTR_LSB \ + (WSA_START_OFFSET + 0x0758) +#define BOLERO_CDC_WSA_SPLINE_ASRC1_STATUS_FMAX_CNTR_MSB \ + (WSA_START_OFFSET + 0x075C) +#define BOLERO_CDC_WSA_SPLINE_ASRC1_STATUS_FIFO (WSA_START_OFFSET + 0x0760) +#define WSA_MAX_OFFSET (WSA_START_OFFSET + 0x0760) + +#define BOLERO_CDC_WSA_MACRO_MAX 0x1D9 /* 0x760/4 = 0x1D8 + 1 registers */ + +/* VA macro registers */ +#define VA_START_OFFSET 0x3000 +#define BOLERO_CDC_VA_CLK_RST_CTRL_MCLK_CONTROL (VA_START_OFFSET + 0x0000) +#define BOLERO_CDC_VA_CLK_RST_CTRL_FS_CNT_CONTROL \ + (VA_START_OFFSET + 0x0004) +#define BOLERO_CDC_VA_TOP_CSR_TOP_CFG0 (VA_START_OFFSET + 0x0080) +#define BOLERO_CDC_VA_TOP_CSR_DMIC0_CTL (VA_START_OFFSET + 0x0084) +#define BOLERO_CDC_VA_TOP_CSR_DMIC1_CTL (VA_START_OFFSET + 0x0088) +#define BOLERO_CDC_VA_TOP_CSR_DMIC2_CTL (VA_START_OFFSET + 0x008C) +#define BOLERO_CDC_VA_TOP_CSR_DMIC3_CTL (VA_START_OFFSET + 0x0090) +#define BOLERO_CDC_VA_TOP_CSR_DMIC_CFG (VA_START_OFFSET + 0x0094) +#define BOLERO_CDC_VA_TOP_CSR_DEBUG_BUS (VA_START_OFFSET + 0x009C) +#define BOLERO_CDC_VA_TOP_CSR_DEBUG_EN (VA_START_OFFSET + 0x00A0) +#define BOLERO_CDC_VA_TOP_CSR_TX_I2S_CTL (VA_START_OFFSET + 0x00A4) +#define BOLERO_CDC_VA_TOP_CSR_I2S_CLK (VA_START_OFFSET + 0x00A8) +#define BOLERO_CDC_VA_TOP_CSR_I2S_RESET (VA_START_OFFSET + 0x00AC) +#define BOLERO_CDC_VA_TOP_CSR_CORE_ID_0 (VA_START_OFFSET + 0x00C0) +#define BOLERO_CDC_VA_TOP_CSR_CORE_ID_1 (VA_START_OFFSET + 0x00C4) +#define BOLERO_CDC_VA_TOP_CSR_CORE_ID_2 (VA_START_OFFSET + 0x00C8) +#define BOLERO_CDC_VA_TOP_CSR_CORE_ID_3 (VA_START_OFFSET + 0x00CC) +#define VA_TOP_MAX_OFFSET (VA_START_OFFSET + 0x00CC) + +#define BOLERO_CDC_VA_MACRO_TOP_MAX 0x34 /* 0x0CC/4 = 0x33 + 1 = 0x34 */ + +#define BOLERO_CDC_VA_INP_MUX_ADC_MUX0_CFG0 (VA_START_OFFSET + 0x0100) +#define BOLERO_CDC_VA_INP_MUX_ADC_MUX0_CFG1 (VA_START_OFFSET + 0x0104) +#define BOLERO_CDC_VA_INP_MUX_ADC_MUX1_CFG0 (VA_START_OFFSET + 0x0108) +#define BOLERO_CDC_VA_INP_MUX_ADC_MUX1_CFG1 (VA_START_OFFSET + 0x010C) +#define BOLERO_CDC_VA_INP_MUX_ADC_MUX2_CFG0 (VA_START_OFFSET + 0x0110) +#define BOLERO_CDC_VA_INP_MUX_ADC_MUX2_CFG1 (VA_START_OFFSET + 0x0114) +#define BOLERO_CDC_VA_INP_MUX_ADC_MUX3_CFG0 (VA_START_OFFSET + 0x0118) +#define BOLERO_CDC_VA_INP_MUX_ADC_MUX3_CFG1 (VA_START_OFFSET + 0x011C) +#define BOLERO_CDC_VA_INP_MUX_ADC_MUX4_CFG0 (VA_START_OFFSET + 0x0120) +#define BOLERO_CDC_VA_INP_MUX_ADC_MUX4_CFG1 (VA_START_OFFSET + 0x0124) +#define BOLERO_CDC_VA_INP_MUX_ADC_MUX5_CFG0 (VA_START_OFFSET + 0x0128) +#define BOLERO_CDC_VA_INP_MUX_ADC_MUX5_CFG1 (VA_START_OFFSET + 0x012C) +#define BOLERO_CDC_VA_INP_MUX_ADC_MUX6_CFG0 (VA_START_OFFSET + 0x0130) +#define BOLERO_CDC_VA_INP_MUX_ADC_MUX6_CFG1 (VA_START_OFFSET + 0x0134) +#define BOLERO_CDC_VA_INP_MUX_ADC_MUX7_CFG0 (VA_START_OFFSET + 0x0138) +#define BOLERO_CDC_VA_INP_MUX_ADC_MUX7_CFG1 (VA_START_OFFSET + 0x013C) + +#define BOLERO_CDC_VA_TX0_TX_PATH_CTL (VA_START_OFFSET + 0x0400) +#define BOLERO_CDC_VA_TX0_TX_PATH_CFG0 (VA_START_OFFSET + 0x0404) +#define BOLERO_CDC_VA_TX0_TX_PATH_CFG1 (VA_START_OFFSET + 0x0408) +#define BOLERO_CDC_VA_TX0_TX_VOL_CTL (VA_START_OFFSET + 0x040C) +#define BOLERO_CDC_VA_TX0_TX_PATH_SEC0 (VA_START_OFFSET + 0x0410) +#define BOLERO_CDC_VA_TX0_TX_PATH_SEC1 (VA_START_OFFSET + 0x0414) +#define BOLERO_CDC_VA_TX0_TX_PATH_SEC2 (VA_START_OFFSET + 0x0418) +#define BOLERO_CDC_VA_TX0_TX_PATH_SEC3 (VA_START_OFFSET + 0x041C) +#define BOLERO_CDC_VA_TX0_TX_PATH_SEC4 (VA_START_OFFSET + 0x0420) +#define BOLERO_CDC_VA_TX0_TX_PATH_SEC5 (VA_START_OFFSET + 0x0424) +#define BOLERO_CDC_VA_TX0_TX_PATH_SEC6 (VA_START_OFFSET + 0x0428) +#define BOLERO_CDC_VA_TX0_TX_PATH_SEC7 (VA_START_OFFSET + 0x042C) +#define BOLERO_CDC_VA_TX1_TX_PATH_CTL (VA_START_OFFSET + 0x0480) +#define BOLERO_CDC_VA_TX1_TX_PATH_CFG0 (VA_START_OFFSET + 0x0484) +#define BOLERO_CDC_VA_TX1_TX_PATH_CFG1 (VA_START_OFFSET + 0x0488) +#define BOLERO_CDC_VA_TX1_TX_VOL_CTL (VA_START_OFFSET + 0x048C) +#define BOLERO_CDC_VA_TX1_TX_PATH_SEC0 (VA_START_OFFSET + 0x0490) +#define BOLERO_CDC_VA_TX1_TX_PATH_SEC1 (VA_START_OFFSET + 0x0494) +#define BOLERO_CDC_VA_TX1_TX_PATH_SEC2 (VA_START_OFFSET + 0x0498) +#define BOLERO_CDC_VA_TX1_TX_PATH_SEC3 (VA_START_OFFSET + 0x049C) +#define BOLERO_CDC_VA_TX1_TX_PATH_SEC4 (VA_START_OFFSET + 0x04A0) +#define BOLERO_CDC_VA_TX1_TX_PATH_SEC5 (VA_START_OFFSET + 0x04A4) +#define BOLERO_CDC_VA_TX1_TX_PATH_SEC6 (VA_START_OFFSET + 0x04A8) +#define BOLERO_CDC_VA_TX2_TX_PATH_CTL (VA_START_OFFSET + 0x0500) +#define BOLERO_CDC_VA_TX2_TX_PATH_CFG0 (VA_START_OFFSET + 0x0504) +#define BOLERO_CDC_VA_TX2_TX_PATH_CFG1 (VA_START_OFFSET + 0x0508) +#define BOLERO_CDC_VA_TX2_TX_VOL_CTL (VA_START_OFFSET + 0x050C) +#define BOLERO_CDC_VA_TX2_TX_PATH_SEC0 (VA_START_OFFSET + 0x0510) +#define BOLERO_CDC_VA_TX2_TX_PATH_SEC1 (VA_START_OFFSET + 0x0514) +#define BOLERO_CDC_VA_TX2_TX_PATH_SEC2 (VA_START_OFFSET + 0x0518) +#define BOLERO_CDC_VA_TX2_TX_PATH_SEC3 (VA_START_OFFSET + 0x051C) +#define BOLERO_CDC_VA_TX2_TX_PATH_SEC4 (VA_START_OFFSET + 0x0520) +#define BOLERO_CDC_VA_TX2_TX_PATH_SEC5 (VA_START_OFFSET + 0x0524) +#define BOLERO_CDC_VA_TX2_TX_PATH_SEC6 (VA_START_OFFSET + 0x0528) +#define BOLERO_CDC_VA_TX3_TX_PATH_CTL (VA_START_OFFSET + 0x0580) +#define BOLERO_CDC_VA_TX3_TX_PATH_CFG0 (VA_START_OFFSET + 0x0584) +#define BOLERO_CDC_VA_TX3_TX_PATH_CFG1 (VA_START_OFFSET + 0x0588) +#define BOLERO_CDC_VA_TX3_TX_VOL_CTL (VA_START_OFFSET + 0x058C) +#define BOLERO_CDC_VA_TX3_TX_PATH_SEC0 (VA_START_OFFSET + 0x0590) +#define BOLERO_CDC_VA_TX3_TX_PATH_SEC1 (VA_START_OFFSET + 0x0594) +#define BOLERO_CDC_VA_TX3_TX_PATH_SEC2 (VA_START_OFFSET + 0x0598) +#define BOLERO_CDC_VA_TX3_TX_PATH_SEC3 (VA_START_OFFSET + 0x059C) +#define BOLERO_CDC_VA_TX3_TX_PATH_SEC4 (VA_START_OFFSET + 0x05A0) +#define BOLERO_CDC_VA_TX3_TX_PATH_SEC5 (VA_START_OFFSET + 0x05A4) +#define BOLERO_CDC_VA_TX3_TX_PATH_SEC6 (VA_START_OFFSET + 0x05A8) +#define BOLERO_CDC_VA_TX4_TX_PATH_CTL (VA_START_OFFSET + 0x0600) +#define BOLERO_CDC_VA_TX4_TX_PATH_CFG0 (VA_START_OFFSET + 0x0604) +#define BOLERO_CDC_VA_TX4_TX_PATH_CFG1 (VA_START_OFFSET + 0x0608) +#define BOLERO_CDC_VA_TX4_TX_VOL_CTL (VA_START_OFFSET + 0x060C) +#define BOLERO_CDC_VA_TX4_TX_PATH_SEC0 (VA_START_OFFSET + 0x0610) +#define BOLERO_CDC_VA_TX4_TX_PATH_SEC1 (VA_START_OFFSET + 0x0614) +#define BOLERO_CDC_VA_TX4_TX_PATH_SEC2 (VA_START_OFFSET + 0x0618) +#define BOLERO_CDC_VA_TX4_TX_PATH_SEC3 (VA_START_OFFSET + 0x061C) +#define BOLERO_CDC_VA_TX4_TX_PATH_SEC4 (VA_START_OFFSET + 0x0620) +#define BOLERO_CDC_VA_TX4_TX_PATH_SEC5 (VA_START_OFFSET + 0x0624) +#define BOLERO_CDC_VA_TX4_TX_PATH_SEC6 (VA_START_OFFSET + 0x0628) +#define BOLERO_CDC_VA_TX5_TX_PATH_CTL (VA_START_OFFSET + 0x0680) +#define BOLERO_CDC_VA_TX5_TX_PATH_CFG0 (VA_START_OFFSET + 0x0684) +#define BOLERO_CDC_VA_TX5_TX_PATH_CFG1 (VA_START_OFFSET + 0x0688) +#define BOLERO_CDC_VA_TX5_TX_VOL_CTL (VA_START_OFFSET + 0x068C) +#define BOLERO_CDC_VA_TX5_TX_PATH_SEC0 (VA_START_OFFSET + 0x0690) +#define BOLERO_CDC_VA_TX5_TX_PATH_SEC1 (VA_START_OFFSET + 0x0694) +#define BOLERO_CDC_VA_TX5_TX_PATH_SEC2 (VA_START_OFFSET + 0x0698) +#define BOLERO_CDC_VA_TX5_TX_PATH_SEC3 (VA_START_OFFSET + 0x069C) +#define BOLERO_CDC_VA_TX5_TX_PATH_SEC4 (VA_START_OFFSET + 0x06A0) +#define BOLERO_CDC_VA_TX5_TX_PATH_SEC5 (VA_START_OFFSET + 0x06A4) +#define BOLERO_CDC_VA_TX5_TX_PATH_SEC6 (VA_START_OFFSET + 0x06A8) +#define BOLERO_CDC_VA_TX6_TX_PATH_CTL (VA_START_OFFSET + 0x0700) +#define BOLERO_CDC_VA_TX6_TX_PATH_CFG0 (VA_START_OFFSET + 0x0704) +#define BOLERO_CDC_VA_TX6_TX_PATH_CFG1 (VA_START_OFFSET + 0x0708) +#define BOLERO_CDC_VA_TX6_TX_VOL_CTL (VA_START_OFFSET + 0x070C) +#define BOLERO_CDC_VA_TX6_TX_PATH_SEC0 (VA_START_OFFSET + 0x0710) +#define BOLERO_CDC_VA_TX6_TX_PATH_SEC1 (VA_START_OFFSET + 0x0714) +#define BOLERO_CDC_VA_TX6_TX_PATH_SEC2 (VA_START_OFFSET + 0x0718) +#define BOLERO_CDC_VA_TX6_TX_PATH_SEC3 (VA_START_OFFSET + 0x071C) +#define BOLERO_CDC_VA_TX6_TX_PATH_SEC4 (VA_START_OFFSET + 0x0720) +#define BOLERO_CDC_VA_TX6_TX_PATH_SEC5 (VA_START_OFFSET + 0x0724) +#define BOLERO_CDC_VA_TX6_TX_PATH_SEC6 (VA_START_OFFSET + 0x0728) +#define BOLERO_CDC_VA_TX7_TX_PATH_CTL (VA_START_OFFSET + 0x0780) +#define BOLERO_CDC_VA_TX7_TX_PATH_CFG0 (VA_START_OFFSET + 0x0784) +#define BOLERO_CDC_VA_TX7_TX_PATH_CFG1 (VA_START_OFFSET + 0x0788) +#define BOLERO_CDC_VA_TX7_TX_VOL_CTL (VA_START_OFFSET + 0x078C) +#define BOLERO_CDC_VA_TX7_TX_PATH_SEC0 (VA_START_OFFSET + 0x0790) +#define BOLERO_CDC_VA_TX7_TX_PATH_SEC1 (VA_START_OFFSET + 0x0794) +#define BOLERO_CDC_VA_TX7_TX_PATH_SEC2 (VA_START_OFFSET + 0x0798) +#define BOLERO_CDC_VA_TX7_TX_PATH_SEC3 (VA_START_OFFSET + 0x079C) +#define BOLERO_CDC_VA_TX7_TX_PATH_SEC4 (VA_START_OFFSET + 0x07A0) +#define BOLERO_CDC_VA_TX7_TX_PATH_SEC5 (VA_START_OFFSET + 0x07A4) +#define BOLERO_CDC_VA_TX7_TX_PATH_SEC6 (VA_START_OFFSET + 0x07A8) +#define VA_MAX_OFFSET (VA_START_OFFSET + 0x07A8) + +#define BOLERO_CDC_VA_MACRO_MAX 0x1EB /* 7A8/4 = 1EA + 1 = 1EB */ + +#define BOLERO_CDC_MAX_REGISTER VA_MAX_OFFSET + +#define BOLERO_REG(reg) (((reg) & 0x0FFF)/4) + +#endif diff --git a/audio-kernel/asoc/codecs/bolero/bolero-cdc-regmap.c b/audio-kernel/asoc/codecs/bolero/bolero-cdc-regmap.c new file mode 100644 index 000000000..ecc13243d --- /dev/null +++ b/audio-kernel/asoc/codecs/bolero/bolero-cdc-regmap.c @@ -0,0 +1,862 @@ +/* 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 +#include "bolero-cdc.h" +#include "internal.h" + +static const struct reg_default bolero_defaults[] = { + /* TX Macro */ + { BOLERO_CDC_TX_CLK_RST_CTRL_MCLK_CONTROL, 0x00 }, + { BOLERO_CDC_TX_CLK_RST_CTRL_FS_CNT_CONTROL, 0x00 }, + { BOLERO_CDC_TX_CLK_RST_CTRL_SWR_CONTROL, 0x00}, + { BOLERO_CDC_TX_TOP_CSR_TOP_CFG0, 0x00}, + { BOLERO_CDC_TX_TOP_CSR_ANC_CFG, 0x00}, + { BOLERO_CDC_TX_TOP_CSR_SWR_CTRL, 0x00}, + { BOLERO_CDC_TX_TOP_CSR_FREQ_MCLK, 0x00}, + { BOLERO_CDC_TX_TOP_CSR_DEBUG_BUS, 0x00}, + { BOLERO_CDC_TX_TOP_CSR_DEBUG_EN, 0x00}, + { BOLERO_CDC_TX_TOP_CSR_TX_I2S_CTL, 0x0C}, + { BOLERO_CDC_TX_TOP_CSR_I2S_CLK, 0x00}, + { BOLERO_CDC_TX_TOP_CSR_I2S_RESET, 0x00}, + { BOLERO_CDC_TX_TOP_CSR_SWR_DMIC0_CTL, 0x00}, + { BOLERO_CDC_TX_TOP_CSR_SWR_DMIC1_CTL, 0x00}, + { BOLERO_CDC_TX_TOP_CSR_SWR_DMIC2_CTL, 0x00}, + { BOLERO_CDC_TX_TOP_CSR_SWR_DMIC3_CTL, 0x00}, + { BOLERO_CDC_TX_TOP_CSR_SWR_AMIC0_CTL, 0x00}, + { BOLERO_CDC_TX_TOP_CSR_SWR_AMIC1_CTL, 0x00}, + { BOLERO_CDC_TX_INP_MUX_ADC_MUX0_CFG0, 0x00}, + { BOLERO_CDC_TX_INP_MUX_ADC_MUX0_CFG1, 0x00}, + { BOLERO_CDC_TX_INP_MUX_ADC_MUX1_CFG0, 0x00}, + { BOLERO_CDC_TX_INP_MUX_ADC_MUX1_CFG1, 0x00}, + { BOLERO_CDC_TX_INP_MUX_ADC_MUX2_CFG0, 0x00}, + { BOLERO_CDC_TX_INP_MUX_ADC_MUX2_CFG1, 0x00}, + { BOLERO_CDC_TX_INP_MUX_ADC_MUX3_CFG0, 0x00}, + { BOLERO_CDC_TX_INP_MUX_ADC_MUX3_CFG1, 0x00}, + { BOLERO_CDC_TX_INP_MUX_ADC_MUX4_CFG0, 0x00}, + { BOLERO_CDC_TX_INP_MUX_ADC_MUX4_CFG1, 0x00}, + { BOLERO_CDC_TX_INP_MUX_ADC_MUX5_CFG0, 0x00}, + { BOLERO_CDC_TX_INP_MUX_ADC_MUX5_CFG1, 0x00}, + { BOLERO_CDC_TX_INP_MUX_ADC_MUX6_CFG0, 0x00}, + { BOLERO_CDC_TX_INP_MUX_ADC_MUX6_CFG1, 0x00}, + { BOLERO_CDC_TX_INP_MUX_ADC_MUX7_CFG0, 0x00}, + { BOLERO_CDC_TX_INP_MUX_ADC_MUX7_CFG1, 0x00}, + { BOLERO_CDC_TX_ANC0_CLK_RESET_CTL, 0x00}, + { BOLERO_CDC_TX_ANC0_MODE_1_CTL, 0x00}, + { BOLERO_CDC_TX_ANC0_MODE_2_CTL, 0x00}, + { BOLERO_CDC_TX_ANC0_FF_SHIFT, 0x00}, + { BOLERO_CDC_TX_ANC0_FB_SHIFT, 0x00}, + { BOLERO_CDC_TX_ANC0_LPF_FF_A_CTL, 0x00}, + { BOLERO_CDC_TX_ANC0_LPF_FF_B_CTL, 0x00}, + { BOLERO_CDC_TX_ANC0_LPF_FB_CTL, 0x00}, + { BOLERO_CDC_TX_ANC0_SMLPF_CTL, 0x00}, + { BOLERO_CDC_TX_ANC0_DCFLT_SHIFT_CTL, 0x00}, + { BOLERO_CDC_TX_ANC0_IIR_ADAPT_CTL, 0x00}, + { BOLERO_CDC_TX_ANC0_IIR_COEFF_1_CTL, 0x00}, + { BOLERO_CDC_TX_ANC0_IIR_COEFF_2_CTL, 0x00}, + { BOLERO_CDC_TX_ANC0_FF_A_GAIN_CTL, 0x00}, + { BOLERO_CDC_TX_ANC0_FF_B_GAIN_CTL, 0x00}, + { BOLERO_CDC_TX_ANC0_FB_GAIN_CTL, 0x00}, + { BOLERO_CDC_TX0_TX_PATH_CTL, 0x04}, + { BOLERO_CDC_TX0_TX_PATH_CFG0, 0x10}, + { BOLERO_CDC_TX0_TX_PATH_CFG1, 0x0B}, + { BOLERO_CDC_TX0_TX_VOL_CTL, 0x00}, + { BOLERO_CDC_TX0_TX_PATH_SEC0, 0x00}, + { BOLERO_CDC_TX0_TX_PATH_SEC1, 0x00}, + { BOLERO_CDC_TX0_TX_PATH_SEC2, 0x01}, + { BOLERO_CDC_TX0_TX_PATH_SEC3, 0x3C}, + { BOLERO_CDC_TX0_TX_PATH_SEC4, 0x20}, + { BOLERO_CDC_TX0_TX_PATH_SEC5, 0x00}, + { BOLERO_CDC_TX0_TX_PATH_SEC6, 0x00}, + { BOLERO_CDC_TX0_TX_PATH_SEC7, 0x25}, + { BOLERO_CDC_TX1_TX_PATH_CTL, 0x04}, + { BOLERO_CDC_TX1_TX_PATH_CFG0, 0x10}, + { BOLERO_CDC_TX1_TX_PATH_CFG1, 0x0B}, + { BOLERO_CDC_TX1_TX_VOL_CTL, 0x00}, + { BOLERO_CDC_TX1_TX_PATH_SEC0, 0x00}, + { BOLERO_CDC_TX1_TX_PATH_SEC1, 0x00}, + { BOLERO_CDC_TX1_TX_PATH_SEC2, 0x01}, + { BOLERO_CDC_TX1_TX_PATH_SEC3, 0x3C}, + { BOLERO_CDC_TX1_TX_PATH_SEC4, 0x20}, + { BOLERO_CDC_TX1_TX_PATH_SEC5, 0x00}, + { BOLERO_CDC_TX1_TX_PATH_SEC6, 0x00}, + { BOLERO_CDC_TX2_TX_PATH_CTL, 0x04}, + { BOLERO_CDC_TX2_TX_PATH_CFG0, 0x10}, + { BOLERO_CDC_TX2_TX_PATH_CFG1, 0x0B}, + { BOLERO_CDC_TX2_TX_VOL_CTL, 0x00}, + { BOLERO_CDC_TX2_TX_PATH_SEC0, 0x00}, + { BOLERO_CDC_TX2_TX_PATH_SEC1, 0x00}, + { BOLERO_CDC_TX2_TX_PATH_SEC2, 0x01}, + { BOLERO_CDC_TX2_TX_PATH_SEC3, 0x3C}, + { BOLERO_CDC_TX2_TX_PATH_SEC4, 0x20}, + { BOLERO_CDC_TX2_TX_PATH_SEC5, 0x00}, + { BOLERO_CDC_TX2_TX_PATH_SEC6, 0x00}, + { BOLERO_CDC_TX3_TX_PATH_CTL, 0x04}, + { BOLERO_CDC_TX3_TX_PATH_CFG0, 0x10}, + { BOLERO_CDC_TX3_TX_PATH_CFG1, 0x0B}, + { BOLERO_CDC_TX3_TX_VOL_CTL, 0x00}, + { BOLERO_CDC_TX3_TX_PATH_SEC0, 0x00}, + { BOLERO_CDC_TX3_TX_PATH_SEC1, 0x00}, + { BOLERO_CDC_TX3_TX_PATH_SEC2, 0x01}, + { BOLERO_CDC_TX3_TX_PATH_SEC3, 0x3C}, + { BOLERO_CDC_TX3_TX_PATH_SEC4, 0x20}, + { BOLERO_CDC_TX3_TX_PATH_SEC5, 0x00}, + { BOLERO_CDC_TX3_TX_PATH_SEC6, 0x00}, + { BOLERO_CDC_TX4_TX_PATH_CTL, 0x04}, + { BOLERO_CDC_TX4_TX_PATH_CFG0, 0x10}, + { BOLERO_CDC_TX4_TX_PATH_CFG1, 0x0B}, + { BOLERO_CDC_TX4_TX_VOL_CTL, 0x00}, + { BOLERO_CDC_TX4_TX_PATH_SEC0, 0x00}, + { BOLERO_CDC_TX4_TX_PATH_SEC1, 0x00}, + { BOLERO_CDC_TX4_TX_PATH_SEC2, 0x01}, + { BOLERO_CDC_TX4_TX_PATH_SEC3, 0x3C}, + { BOLERO_CDC_TX4_TX_PATH_SEC4, 0x20}, + { BOLERO_CDC_TX4_TX_PATH_SEC5, 0x00}, + { BOLERO_CDC_TX4_TX_PATH_SEC6, 0x00}, + { BOLERO_CDC_TX5_TX_PATH_CTL, 0x04}, + { BOLERO_CDC_TX5_TX_PATH_CFG0, 0x10}, + { BOLERO_CDC_TX5_TX_PATH_CFG1, 0x0B}, + { BOLERO_CDC_TX5_TX_VOL_CTL, 0x00}, + { BOLERO_CDC_TX5_TX_PATH_SEC0, 0x00}, + { BOLERO_CDC_TX5_TX_PATH_SEC1, 0x00}, + { BOLERO_CDC_TX5_TX_PATH_SEC2, 0x01}, + { BOLERO_CDC_TX5_TX_PATH_SEC3, 0x3C}, + { BOLERO_CDC_TX5_TX_PATH_SEC4, 0x20}, + { BOLERO_CDC_TX5_TX_PATH_SEC5, 0x00}, + { BOLERO_CDC_TX5_TX_PATH_SEC6, 0x00}, + { BOLERO_CDC_TX6_TX_PATH_CTL, 0x04}, + { BOLERO_CDC_TX6_TX_PATH_CFG0, 0x10}, + { BOLERO_CDC_TX6_TX_PATH_CFG1, 0x0B}, + { BOLERO_CDC_TX6_TX_VOL_CTL, 0x00}, + { BOLERO_CDC_TX6_TX_PATH_SEC0, 0x00}, + { BOLERO_CDC_TX6_TX_PATH_SEC1, 0x00}, + { BOLERO_CDC_TX6_TX_PATH_SEC2, 0x01}, + { BOLERO_CDC_TX6_TX_PATH_SEC3, 0x3C}, + { BOLERO_CDC_TX6_TX_PATH_SEC4, 0x20}, + { BOLERO_CDC_TX6_TX_PATH_SEC5, 0x00}, + { BOLERO_CDC_TX6_TX_PATH_SEC6, 0x00}, + { BOLERO_CDC_TX7_TX_PATH_CTL, 0x04}, + { BOLERO_CDC_TX7_TX_PATH_CFG0, 0x10}, + { BOLERO_CDC_TX7_TX_PATH_CFG1, 0x0B}, + { BOLERO_CDC_TX7_TX_VOL_CTL, 0x00}, + { BOLERO_CDC_TX7_TX_PATH_SEC0, 0x00}, + { BOLERO_CDC_TX7_TX_PATH_SEC1, 0x00}, + { BOLERO_CDC_TX7_TX_PATH_SEC2, 0x01}, + { BOLERO_CDC_TX7_TX_PATH_SEC3, 0x3C}, + { BOLERO_CDC_TX7_TX_PATH_SEC4, 0x20}, + { BOLERO_CDC_TX7_TX_PATH_SEC5, 0x00}, + { BOLERO_CDC_TX7_TX_PATH_SEC6, 0x00}, + + /* RX Macro */ + { BOLERO_CDC_RX_TOP_TOP_CFG0, 0x00}, + { BOLERO_CDC_RX_TOP_SWR_CTRL, 0x00}, + { BOLERO_CDC_RX_TOP_DEBUG, 0x00}, + { BOLERO_CDC_RX_TOP_DEBUG_BUS, 0x00}, + { BOLERO_CDC_RX_TOP_DEBUG_EN0, 0x00}, + { BOLERO_CDC_RX_TOP_DEBUG_EN1, 0x00}, + { BOLERO_CDC_RX_TOP_DEBUG_EN2, 0x00}, + { BOLERO_CDC_RX_TOP_HPHL_COMP_WR_LSB, 0x00}, + { BOLERO_CDC_RX_TOP_HPHL_COMP_WR_MSB, 0x00}, + { BOLERO_CDC_RX_TOP_HPHL_COMP_LUT, 0x00}, + { BOLERO_CDC_RX_TOP_HPHL_COMP_RD_LSB, 0x00}, + { BOLERO_CDC_RX_TOP_HPHL_COMP_RD_MSB, 0x00}, + { BOLERO_CDC_RX_TOP_HPHR_COMP_WR_LSB, 0x00}, + { BOLERO_CDC_RX_TOP_HPHR_COMP_WR_MSB, 0x00}, + { BOLERO_CDC_RX_TOP_HPHR_COMP_LUT, 0x00}, + { BOLERO_CDC_RX_TOP_HPHR_COMP_RD_LSB, 0x00}, + { BOLERO_CDC_RX_TOP_HPHR_COMP_RD_MSB, 0x00}, + { BOLERO_CDC_RX_TOP_DSD0_DEBUG_CFG0, 0x11}, + { BOLERO_CDC_RX_TOP_DSD0_DEBUG_CFG1, 0x20}, + { BOLERO_CDC_RX_TOP_DSD0_DEBUG_CFG2, 0x00}, + { BOLERO_CDC_RX_TOP_DSD0_DEBUG_CFG3, 0x00}, + { BOLERO_CDC_RX_TOP_DSD1_DEBUG_CFG0, 0x11}, + { BOLERO_CDC_RX_TOP_DSD1_DEBUG_CFG1, 0x20}, + { BOLERO_CDC_RX_TOP_DSD1_DEBUG_CFG2, 0x00}, + { BOLERO_CDC_RX_TOP_DSD1_DEBUG_CFG3, 0x00}, + { BOLERO_CDC_RX_TOP_RX_I2S_CTL, 0x0C}, + { BOLERO_CDC_RX_TOP_TX_I2S2_CTL, 0x0C}, + { BOLERO_CDC_RX_TOP_I2S_CLK, 0x0C}, + { BOLERO_CDC_RX_TOP_I2S_RESET, 0x00}, + { BOLERO_CDC_RX_TOP_I2S_MUX, 0x00}, + { BOLERO_CDC_RX_CLK_RST_CTRL_MCLK_CONTROL, 0x00}, + { BOLERO_CDC_RX_CLK_RST_CTRL_FS_CNT_CONTROL, 0x00}, + { BOLERO_CDC_RX_CLK_RST_CTRL_SWR_CONTROL, 0x00}, + { BOLERO_CDC_RX_CLK_RST_CTRL_DSD_CONTROL, 0x00}, + { BOLERO_CDC_RX_CLK_RST_CTRL_ASRC_SHARE_CONTROL, 0x08}, + { BOLERO_CDC_RX_SOFTCLIP_CRC, 0x00}, + { BOLERO_CDC_RX_SOFTCLIP_SOFTCLIP_CTRL, 0x38}, + { BOLERO_CDC_RX_INP_MUX_RX_INT0_CFG0, 0x00}, + { BOLERO_CDC_RX_INP_MUX_RX_INT0_CFG1, 0x00}, + { BOLERO_CDC_RX_INP_MUX_RX_INT1_CFG0, 0x00}, + { BOLERO_CDC_RX_INP_MUX_RX_INT1_CFG1, 0x00}, + { BOLERO_CDC_RX_INP_MUX_RX_INT2_CFG0, 0x00}, + { BOLERO_CDC_RX_INP_MUX_RX_INT2_CFG1, 0x00}, + { BOLERO_CDC_RX_INP_MUX_RX_MIX_CFG4, 0x00}, + { BOLERO_CDC_RX_INP_MUX_RX_MIX_CFG5, 0x00}, + { BOLERO_CDC_RX_INP_MUX_SIDETONE_SRC_CFG0, 0x00}, + { BOLERO_CDC_RX_CLSH_CRC, 0x00}, + { BOLERO_CDC_RX_CLSH_DLY_CTRL, 0x03}, + { BOLERO_CDC_RX_CLSH_DECAY_CTRL, 0x02}, + { BOLERO_CDC_RX_CLSH_HPH_V_PA, 0x1C}, + { BOLERO_CDC_RX_CLSH_EAR_V_PA, 0x39}, + { BOLERO_CDC_RX_CLSH_HPH_V_HD, 0x0C}, + { BOLERO_CDC_RX_CLSH_EAR_V_HD, 0x0C}, + { BOLERO_CDC_RX_CLSH_K1_MSB, 0x01}, + { BOLERO_CDC_RX_CLSH_K1_LSB, 0x00}, + { BOLERO_CDC_RX_CLSH_K2_MSB, 0x00}, + { BOLERO_CDC_RX_CLSH_K2_LSB, 0x80}, + { BOLERO_CDC_RX_CLSH_IDLE_CTRL, 0x00}, + { BOLERO_CDC_RX_CLSH_IDLE_HPH, 0x00}, + { BOLERO_CDC_RX_CLSH_IDLE_EAR, 0x00}, + { BOLERO_CDC_RX_CLSH_TEST0, 0x07}, + { BOLERO_CDC_RX_CLSH_TEST1, 0x00}, + { BOLERO_CDC_RX_CLSH_OVR_VREF, 0x00}, + { BOLERO_CDC_RX_CLSH_CLSG_CTL, 0x02}, + { BOLERO_CDC_RX_CLSH_CLSG_CFG1, 0x9A}, + { BOLERO_CDC_RX_CLSH_CLSG_CFG2, 0x10}, + { BOLERO_CDC_RX_BCL_VBAT_PATH_CTL, 0x00}, + { BOLERO_CDC_RX_BCL_VBAT_CFG, 0x10}, + { BOLERO_CDC_RX_BCL_VBAT_ADC_CAL1, 0x00}, + { BOLERO_CDC_RX_BCL_VBAT_ADC_CAL2, 0x00}, + { BOLERO_CDC_RX_BCL_VBAT_ADC_CAL3, 0x04}, + { BOLERO_CDC_RX_BCL_VBAT_PK_EST1, 0xE0}, + { BOLERO_CDC_RX_BCL_VBAT_PK_EST2, 0x01}, + { BOLERO_CDC_RX_BCL_VBAT_PK_EST3, 0x40}, + { BOLERO_CDC_RX_BCL_VBAT_RF_PROC1, 0x2A}, + { BOLERO_CDC_RX_BCL_VBAT_RF_PROC1, 0x00}, + { BOLERO_CDC_RX_BCL_VBAT_TAC1, 0x00}, + { BOLERO_CDC_RX_BCL_VBAT_TAC2, 0x18}, + { BOLERO_CDC_RX_BCL_VBAT_TAC3, 0x18}, + { BOLERO_CDC_RX_BCL_VBAT_TAC4, 0x03}, + { BOLERO_CDC_RX_BCL_VBAT_GAIN_UPD1, 0x01}, + { BOLERO_CDC_RX_BCL_VBAT_GAIN_UPD2, 0x00}, + { BOLERO_CDC_RX_BCL_VBAT_GAIN_UPD3, 0x00}, + { BOLERO_CDC_RX_BCL_VBAT_GAIN_UPD4, 0x64}, + { BOLERO_CDC_RX_BCL_VBAT_GAIN_UPD5, 0x01}, + { BOLERO_CDC_RX_BCL_VBAT_DEBUG1, 0x00}, + { BOLERO_CDC_RX_BCL_VBAT_GAIN_UPD_MON, 0x00}, + { BOLERO_CDC_RX_BCL_VBAT_GAIN_MON_VAL, 0x00}, + { BOLERO_CDC_RX_BCL_VBAT_BAN, 0x0C}, + { BOLERO_CDC_RX_BCL_VBAT_BCL_GAIN_UPD1, 0x00}, + { BOLERO_CDC_RX_BCL_VBAT_BCL_GAIN_UPD2, 0x77}, + { BOLERO_CDC_RX_BCL_VBAT_BCL_GAIN_UPD3, 0x01}, + { BOLERO_CDC_RX_BCL_VBAT_BCL_GAIN_UPD4, 0x00}, + { BOLERO_CDC_RX_BCL_VBAT_BCL_GAIN_UPD5, 0x4B}, + { BOLERO_CDC_RX_BCL_VBAT_BCL_GAIN_UPD6, 0x00}, + { BOLERO_CDC_RX_BCL_VBAT_BCL_GAIN_UPD7, 0x01}, + { BOLERO_CDC_RX_BCL_VBAT_BCL_GAIN_UPD8, 0x00}, + { BOLERO_CDC_RX_BCL_VBAT_BCL_GAIN_UPD9, 0x00}, + { BOLERO_CDC_RX_BCL_VBAT_ATTN1, 0x04}, + { BOLERO_CDC_RX_BCL_VBAT_ATTN2, 0x08}, + { BOLERO_CDC_RX_BCL_VBAT_ATTN3, 0x0C}, + { BOLERO_CDC_RX_BCL_VBAT_DECODE_CTL1, 0xE0}, + { BOLERO_CDC_RX_BCL_VBAT_DECODE_CTL2, 0x00}, + { BOLERO_CDC_RX_BCL_VBAT_DECODE_CFG1, 0x00}, + { BOLERO_CDC_RX_BCL_VBAT_DECODE_CFG2, 0x00}, + { BOLERO_CDC_RX_BCL_VBAT_DECODE_CFG3, 0x00}, + { BOLERO_CDC_RX_BCL_VBAT_DECODE_CFG4, 0x00}, + { BOLERO_CDC_RX_BCL_VBAT_DECODE_ST, 0x00}, + { BOLERO_CDC_RX_INTR_CTRL_CFG, 0x00}, + { BOLERO_CDC_RX_INTR_CTRL_CLR_COMMIT, 0x00}, + { BOLERO_CDC_RX_INTR_CTRL_PIN1_MASK0, 0xFF}, + { BOLERO_CDC_RX_INTR_CTRL_PIN1_STATUS0, 0x00}, + { BOLERO_CDC_RX_INTR_CTRL_PIN1_CLEAR0, 0x00}, + { BOLERO_CDC_RX_INTR_CTRL_PIN2_MASK0, 0xFF}, + { BOLERO_CDC_RX_INTR_CTRL_PIN2_STATUS0, 0x00}, + { BOLERO_CDC_RX_INTR_CTRL_PIN2_CLEAR0, 0x00}, + { BOLERO_CDC_RX_INTR_CTRL_LEVEL0, 0x00}, + { BOLERO_CDC_RX_INTR_CTRL_BYPASS0, 0x00}, + { BOLERO_CDC_RX_INTR_CTRL_SET0, 0x00}, + { BOLERO_CDC_RX_RX0_RX_PATH_CTL, 0x04}, + { BOLERO_CDC_RX_RX0_RX_PATH_CFG0, 0x00}, + { BOLERO_CDC_RX_RX0_RX_PATH_CFG1, 0x64}, + { BOLERO_CDC_RX_RX0_RX_PATH_CFG2, 0x8F}, + { BOLERO_CDC_RX_RX0_RX_PATH_CFG3, 0x00}, + { BOLERO_CDC_RX_RX0_RX_VOL_CTL, 0x00}, + { BOLERO_CDC_RX_RX0_RX_PATH_MIX_CTL, 0x04}, + { BOLERO_CDC_RX_RX0_RX_PATH_MIX_CFG, 0x7E}, + { BOLERO_CDC_RX_RX0_RX_VOL_MIX_CTL, 0x00}, + { BOLERO_CDC_RX_RX0_RX_PATH_SEC1, 0x08}, + { BOLERO_CDC_RX_RX0_RX_PATH_SEC2, 0x00}, + { BOLERO_CDC_RX_RX0_RX_PATH_SEC3, 0x00}, + { BOLERO_CDC_RX_RX0_RX_PATH_SEC4, 0x00}, + { BOLERO_CDC_RX_RX0_RX_PATH_SEC7, 0x00}, + { BOLERO_CDC_RX_RX0_RX_PATH_MIX_SEC0, 0x08}, + { BOLERO_CDC_RX_RX0_RX_PATH_MIX_SEC1, 0x00}, + { BOLERO_CDC_RX_RX0_RX_PATH_DSM_CTL, 0x08}, + { BOLERO_CDC_RX_RX0_RX_PATH_DSM_DATA1, 0x00}, + { BOLERO_CDC_RX_RX0_RX_PATH_DSM_DATA2, 0x00}, + { BOLERO_CDC_RX_RX0_RX_PATH_DSM_DATA3, 0x00}, + { BOLERO_CDC_RX_RX0_RX_PATH_DSM_DATA4, 0x55}, + { BOLERO_CDC_RX_RX0_RX_PATH_DSM_DATA5, 0x55}, + { BOLERO_CDC_RX_RX0_RX_PATH_DSM_DATA6, 0x55}, + { BOLERO_CDC_RX_RX1_RX_PATH_CTL, 0x04}, + { BOLERO_CDC_RX_RX1_RX_PATH_CFG0, 0x00}, + { BOLERO_CDC_RX_RX1_RX_PATH_CFG1, 0x64}, + { BOLERO_CDC_RX_RX1_RX_PATH_CFG2, 0x8F}, + { BOLERO_CDC_RX_RX1_RX_PATH_CFG3, 0x00}, + { BOLERO_CDC_RX_RX1_RX_VOL_CTL, 0x00}, + { BOLERO_CDC_RX_RX1_RX_PATH_MIX_CTL, 0x04}, + { BOLERO_CDC_RX_RX1_RX_PATH_MIX_CFG, 0x7E}, + { BOLERO_CDC_RX_RX1_RX_VOL_MIX_CTL, 0x00}, + { BOLERO_CDC_RX_RX1_RX_PATH_SEC1, 0x08}, + { BOLERO_CDC_RX_RX1_RX_PATH_SEC2, 0x00}, + { BOLERO_CDC_RX_RX1_RX_PATH_SEC3, 0x00}, + { BOLERO_CDC_RX_RX1_RX_PATH_SEC4, 0x00}, + { BOLERO_CDC_RX_RX1_RX_PATH_SEC7, 0x00}, + { BOLERO_CDC_RX_RX1_RX_PATH_MIX_SEC0, 0x08}, + { BOLERO_CDC_RX_RX1_RX_PATH_MIX_SEC1, 0x00}, + { BOLERO_CDC_RX_RX1_RX_PATH_DSM_CTL, 0x08}, + { BOLERO_CDC_RX_RX1_RX_PATH_DSM_DATA1, 0x00}, + { BOLERO_CDC_RX_RX1_RX_PATH_DSM_DATA2, 0x00}, + { BOLERO_CDC_RX_RX1_RX_PATH_DSM_DATA3, 0x00}, + { BOLERO_CDC_RX_RX1_RX_PATH_DSM_DATA4, 0x55}, + { BOLERO_CDC_RX_RX1_RX_PATH_DSM_DATA5, 0x55}, + { BOLERO_CDC_RX_RX1_RX_PATH_DSM_DATA6, 0x55}, + { BOLERO_CDC_RX_RX2_RX_PATH_CTL, 0x04}, + { BOLERO_CDC_RX_RX2_RX_PATH_CFG0, 0x00}, + { BOLERO_CDC_RX_RX2_RX_PATH_CFG1, 0x64}, + { BOLERO_CDC_RX_RX2_RX_PATH_CFG2, 0x8F}, + { BOLERO_CDC_RX_RX2_RX_PATH_CFG3, 0x00}, + { BOLERO_CDC_RX_RX2_RX_VOL_CTL, 0x00}, + { BOLERO_CDC_RX_RX2_RX_PATH_MIX_CTL, 0x04}, + { BOLERO_CDC_RX_RX2_RX_PATH_MIX_CFG, 0x7E}, + { BOLERO_CDC_RX_RX2_RX_VOL_MIX_CTL, 0x00}, + { BOLERO_CDC_RX_RX2_RX_PATH_SEC0, 0x04}, + { BOLERO_CDC_RX_RX2_RX_PATH_SEC1, 0x08}, + { BOLERO_CDC_RX_RX2_RX_PATH_SEC2, 0x00}, + { BOLERO_CDC_RX_RX2_RX_PATH_SEC3, 0x00}, + { BOLERO_CDC_RX_RX2_RX_PATH_SEC4, 0x00}, + { BOLERO_CDC_RX_RX2_RX_PATH_SEC5, 0x00}, + { BOLERO_CDC_RX_RX2_RX_PATH_SEC6, 0x00}, + { BOLERO_CDC_RX_RX2_RX_PATH_SEC7, 0x00}, + { BOLERO_CDC_RX_RX2_RX_PATH_MIX_SEC0, 0x08}, + { BOLERO_CDC_RX_RX2_RX_PATH_MIX_SEC1, 0x00}, + { BOLERO_CDC_RX_RX2_RX_PATH_DSM_CTL, 0x00}, + { BOLERO_CDC_RX_IDLE_DETECT_PATH_CTL, 0x00}, + { BOLERO_CDC_RX_IDLE_DETECT_CFG0, 0x07}, + { BOLERO_CDC_RX_IDLE_DETECT_CFG1, 0x3C}, + { BOLERO_CDC_RX_IDLE_DETECT_CFG2, 0x00}, + { BOLERO_CDC_RX_IDLE_DETECT_CFG3, 0x00}, + { BOLERO_CDC_RX_COMPANDER0_CTL0, 0x60}, + { BOLERO_CDC_RX_COMPANDER0_CTL1, 0xDB}, + { BOLERO_CDC_RX_COMPANDER0_CTL2, 0xFF}, + { BOLERO_CDC_RX_COMPANDER0_CTL3, 0x35}, + { BOLERO_CDC_RX_COMPANDER0_CTL4, 0xFF}, + { BOLERO_CDC_RX_COMPANDER0_CTL5, 0x00}, + { BOLERO_CDC_RX_COMPANDER0_CTL6, 0x01}, + { BOLERO_CDC_RX_COMPANDER0_CTL7, 0x28}, + { BOLERO_CDC_RX_COMPANDER1_CTL0, 0x60}, + { BOLERO_CDC_RX_COMPANDER1_CTL1, 0xDB}, + { BOLERO_CDC_RX_COMPANDER1_CTL2, 0xFF}, + { BOLERO_CDC_RX_COMPANDER1_CTL3, 0x35}, + { BOLERO_CDC_RX_COMPANDER1_CTL4, 0xFF}, + { BOLERO_CDC_RX_COMPANDER1_CTL5, 0x00}, + { BOLERO_CDC_RX_COMPANDER1_CTL6, 0x01}, + { BOLERO_CDC_RX_COMPANDER1_CTL7, 0x28}, + { BOLERO_CDC_RX_SIDETONE_IIR0_IIR_PATH_CTL, 0x00}, + { BOLERO_CDC_RX_SIDETONE_IIR0_IIR_GAIN_B1_CTL, 0x00}, + { BOLERO_CDC_RX_SIDETONE_IIR0_IIR_GAIN_B2_CTL, 0x00}, + { BOLERO_CDC_RX_SIDETONE_IIR0_IIR_GAIN_B3_CTL, 0x00}, + { BOLERO_CDC_RX_SIDETONE_IIR0_IIR_GAIN_B4_CTL, 0x00}, + { BOLERO_CDC_RX_SIDETONE_IIR0_IIR_GAIN_B5_CTL, 0x00}, + { BOLERO_CDC_RX_SIDETONE_IIR0_IIR_GAIN_B6_CTL, 0x00}, + { BOLERO_CDC_RX_SIDETONE_IIR0_IIR_GAIN_B7_CTL, 0x00}, + { BOLERO_CDC_RX_SIDETONE_IIR0_IIR_GAIN_B8_CTL, 0x00}, + { BOLERO_CDC_RX_SIDETONE_IIR0_IIR_CTL, 0x40}, + { BOLERO_CDC_RX_SIDETONE_IIR0_IIR_GAIN_TIMER_CTL, 0x00}, + { BOLERO_CDC_RX_SIDETONE_IIR0_IIR_COEF_B1_CTL, 0x00}, + { BOLERO_CDC_RX_SIDETONE_IIR0_IIR_COEF_B2_CTL, 0x00}, + { BOLERO_CDC_RX_SIDETONE_IIR1_IIR_PATH_CTL, 0x00}, + { BOLERO_CDC_RX_SIDETONE_IIR1_IIR_GAIN_B1_CTL, 0x00}, + { BOLERO_CDC_RX_SIDETONE_IIR1_IIR_GAIN_B2_CTL, 0x00}, + { BOLERO_CDC_RX_SIDETONE_IIR1_IIR_GAIN_B3_CTL, 0x00}, + { BOLERO_CDC_RX_SIDETONE_IIR1_IIR_GAIN_B4_CTL, 0x00}, + { BOLERO_CDC_RX_SIDETONE_IIR1_IIR_GAIN_B5_CTL, 0x00}, + { BOLERO_CDC_RX_SIDETONE_IIR1_IIR_GAIN_B6_CTL, 0x00}, + { BOLERO_CDC_RX_SIDETONE_IIR1_IIR_GAIN_B7_CTL, 0x00}, + { BOLERO_CDC_RX_SIDETONE_IIR1_IIR_GAIN_B8_CTL, 0x00}, + { BOLERO_CDC_RX_SIDETONE_IIR1_IIR_CTL, 0x40}, + { BOLERO_CDC_RX_SIDETONE_IIR1_IIR_GAIN_TIMER_CTL, 0x00}, + { BOLERO_CDC_RX_SIDETONE_IIR1_IIR_COEF_B1_CTL, 0x00}, + { BOLERO_CDC_RX_SIDETONE_IIR1_IIR_COEF_B2_CTL, 0x00}, + { BOLERO_CDC_RX_IIR_INP_MUX_IIR0_MIX_CFG0, 0x00}, + { BOLERO_CDC_RX_IIR_INP_MUX_IIR0_MIX_CFG1, 0x00}, + { BOLERO_CDC_RX_IIR_INP_MUX_IIR0_MIX_CFG2, 0x00}, + { BOLERO_CDC_RX_IIR_INP_MUX_IIR0_MIX_CFG3, 0x00}, + { BOLERO_CDC_RX_IIR_INP_MUX_IIR1_MIX_CFG0, 0x00}, + { BOLERO_CDC_RX_IIR_INP_MUX_IIR1_MIX_CFG1, 0x00}, + { BOLERO_CDC_RX_IIR_INP_MUX_IIR1_MIX_CFG2, 0x00}, + { BOLERO_CDC_RX_IIR_INP_MUX_IIR1_MIX_CFG3, 0x00}, + { BOLERO_CDC_RX_SIDETONE_SRC0_ST_SRC_PATH_CTL, 0x04}, + { BOLERO_CDC_RX_SIDETONE_SRC0_ST_SRC_PATH_CFG1, 0x00}, + { BOLERO_CDC_RX_SIDETONE_SRC1_ST_SRC_PATH_CTL, 0x04}, + { BOLERO_CDC_RX_SIDETONE_SRC1_ST_SRC_PATH_CFG1, 0x00}, + { BOLERO_CDC_RX_EC_REF_HQ0_EC_REF_HQ_PATH_CTL, 0x00}, + { BOLERO_CDC_RX_EC_REF_HQ0_EC_REF_HQ_CFG0, 0x01}, + { BOLERO_CDC_RX_EC_REF_HQ1_EC_REF_HQ_PATH_CTL, 0x00}, + { BOLERO_CDC_RX_EC_REF_HQ1_EC_REF_HQ_CFG0, 0x01}, + { BOLERO_CDC_RX_EC_REF_HQ2_EC_REF_HQ_PATH_CTL, 0x00}, + { BOLERO_CDC_RX_EC_REF_HQ2_EC_REF_HQ_CFG0, 0x01}, + { BOLERO_CDC_RX_EC_ASRC0_CLK_RST_CTL, 0x00}, + { BOLERO_CDC_RX_EC_ASRC0_CTL0, 0x00}, + { BOLERO_CDC_RX_EC_ASRC0_CTL1, 0x00}, + { BOLERO_CDC_RX_EC_ASRC0_FIFO_CTL, 0xA8}, + { BOLERO_CDC_RX_EC_ASRC0_STATUS_FMIN_CNTR_LSB, 0x00}, + { BOLERO_CDC_RX_EC_ASRC0_STATUS_FMIN_CNTR_MSB, 0x00}, + { BOLERO_CDC_RX_EC_ASRC0_STATUS_FMAX_CNTR_LSB, 0x00}, + { BOLERO_CDC_RX_EC_ASRC0_STATUS_FMAX_CNTR_MSB, 0x00}, + { BOLERO_CDC_RX_EC_ASRC0_STATUS_FIFO, 0x00}, + { BOLERO_CDC_RX_EC_ASRC1_CLK_RST_CTL, 0x00}, + { BOLERO_CDC_RX_EC_ASRC1_CTL0, 0x00}, + { BOLERO_CDC_RX_EC_ASRC1_CTL1, 0x00}, + { BOLERO_CDC_RX_EC_ASRC1_FIFO_CTL, 0xA8}, + { BOLERO_CDC_RX_EC_ASRC1_STATUS_FMIN_CNTR_LSB, 0x00}, + { BOLERO_CDC_RX_EC_ASRC1_STATUS_FMIN_CNTR_MSB, 0x00}, + { BOLERO_CDC_RX_EC_ASRC1_STATUS_FMAX_CNTR_LSB, 0x00}, + { BOLERO_CDC_RX_EC_ASRC1_STATUS_FMAX_CNTR_MSB, 0x00}, + { BOLERO_CDC_RX_EC_ASRC1_STATUS_FIFO, 0x00}, + { BOLERO_CDC_RX_EC_ASRC2_CLK_RST_CTL, 0x00}, + { BOLERO_CDC_RX_EC_ASRC2_CTL0, 0x00}, + { BOLERO_CDC_RX_EC_ASRC2_CTL1, 0x00}, + { BOLERO_CDC_RX_EC_ASRC2_FIFO_CTL, 0xA8}, + { BOLERO_CDC_RX_EC_ASRC2_STATUS_FMIN_CNTR_LSB, 0x00}, + { BOLERO_CDC_RX_EC_ASRC2_STATUS_FMIN_CNTR_MSB, 0x00}, + { BOLERO_CDC_RX_EC_ASRC2_STATUS_FMAX_CNTR_LSB, 0x00}, + { BOLERO_CDC_RX_EC_ASRC2_STATUS_FMAX_CNTR_MSB, 0x00}, + { BOLERO_CDC_RX_EC_ASRC2_STATUS_FIFO, 0x00}, + { BOLERO_CDC_RX_DSD0_PATH_CTL, 0x00}, + { BOLERO_CDC_RX_DSD0_CFG0, 0x00}, + { BOLERO_CDC_RX_DSD0_CFG1, 0x62}, + { BOLERO_CDC_RX_DSD0_CFG2, 0x96}, + { BOLERO_CDC_RX_DSD1_PATH_CTL, 0x00}, + { BOLERO_CDC_RX_DSD1_CFG0, 0x00}, + { BOLERO_CDC_RX_DSD1_CFG1, 0x62}, + { BOLERO_CDC_RX_DSD1_CFG2, 0x96}, + + /* WSA Macro */ + { BOLERO_CDC_WSA_CLK_RST_CTRL_MCLK_CONTROL, 0x00}, + { BOLERO_CDC_WSA_CLK_RST_CTRL_FS_CNT_CONTROL, 0x00}, + { BOLERO_CDC_WSA_CLK_RST_CTRL_SWR_CONTROL, 0x00}, + { BOLERO_CDC_WSA_TOP_TOP_CFG0, 0x00}, + { BOLERO_CDC_WSA_TOP_TOP_CFG1, 0x00}, + { BOLERO_CDC_WSA_TOP_FREQ_MCLK, 0x00}, + { BOLERO_CDC_WSA_TOP_DEBUG_BUS_SEL, 0x00}, + { BOLERO_CDC_WSA_TOP_DEBUG_EN0, 0x00}, + { BOLERO_CDC_WSA_TOP_DEBUG_EN1, 0x00}, + { BOLERO_CDC_WSA_TOP_DEBUG_DSM_LB, 0x88}, + { BOLERO_CDC_WSA_TOP_RX_I2S_CTL, 0x0C}, + { BOLERO_CDC_WSA_TOP_TX_I2S_CTL, 0x0C}, + { BOLERO_CDC_WSA_TOP_I2S_CLK, 0x02}, + { BOLERO_CDC_WSA_TOP_I2S_RESET, 0x00}, + { BOLERO_CDC_WSA_RX_INP_MUX_RX_INT0_CFG0, 0x00}, + { BOLERO_CDC_WSA_RX_INP_MUX_RX_INT0_CFG1, 0x00}, + { BOLERO_CDC_WSA_RX_INP_MUX_RX_INT1_CFG0, 0x00}, + { BOLERO_CDC_WSA_RX_INP_MUX_RX_INT1_CFG1, 0x00}, + { BOLERO_CDC_WSA_RX_INP_MUX_RX_MIX_CFG0, 0x00}, + { BOLERO_CDC_WSA_RX_INP_MUX_RX_EC_CFG0, 0x00}, + { BOLERO_CDC_WSA_RX_INP_MUX_SOFTCLIP_CFG0, 0x00}, + { BOLERO_CDC_WSA_VBAT_BCL_VBAT_PATH_CTL, 0x00}, + { BOLERO_CDC_WSA_VBAT_BCL_VBAT_CFG, 0x10}, + { BOLERO_CDC_WSA_VBAT_BCL_VBAT_ADC_CAL1, 0x00}, + { BOLERO_CDC_WSA_VBAT_BCL_VBAT_ADC_CAL2, 0x00}, + { BOLERO_CDC_WSA_VBAT_BCL_VBAT_ADC_CAL3, 0x04}, + { BOLERO_CDC_WSA_VBAT_BCL_VBAT_PK_EST1, 0xE0}, + { BOLERO_CDC_WSA_VBAT_BCL_VBAT_PK_EST2, 0x01}, + { BOLERO_CDC_WSA_VBAT_BCL_VBAT_PK_EST3, 0x40}, + { BOLERO_CDC_WSA_VBAT_BCL_VBAT_RF_PROC1, 0x2A}, + { BOLERO_CDC_WSA_VBAT_BCL_VBAT_RF_PROC2, 0x00}, + { BOLERO_CDC_WSA_VBAT_BCL_VBAT_TAC1, 0x00}, + { BOLERO_CDC_WSA_VBAT_BCL_VBAT_TAC2, 0x18}, + { BOLERO_CDC_WSA_VBAT_BCL_VBAT_TAC3, 0x18}, + { BOLERO_CDC_WSA_VBAT_BCL_VBAT_TAC4, 0x03}, + { BOLERO_CDC_WSA_VBAT_BCL_VBAT_GAIN_UPD1, 0x01}, + { BOLERO_CDC_WSA_VBAT_BCL_VBAT_GAIN_UPD2, 0x00}, + { BOLERO_CDC_WSA_VBAT_BCL_VBAT_GAIN_UPD3, 0x00}, + { BOLERO_CDC_WSA_VBAT_BCL_VBAT_GAIN_UPD4, 0x64}, + { BOLERO_CDC_WSA_VBAT_BCL_VBAT_GAIN_UPD5, 0x01}, + { BOLERO_CDC_WSA_VBAT_BCL_VBAT_DEBUG1, 0x00}, + { BOLERO_CDC_WSA_VBAT_BCL_VBAT_GAIN_UPD_MON, 0x00}, + { BOLERO_CDC_WSA_VBAT_BCL_VBAT_GAIN_MON_VAL, 0x00}, + { BOLERO_CDC_WSA_VBAT_BCL_VBAT_BAN, 0x0C}, + { BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_GAIN_UPD1, 0x00}, + { BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_GAIN_UPD2, 0x77}, + { BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_GAIN_UPD3, 0x01}, + { BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_GAIN_UPD4, 0x00}, + { BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_GAIN_UPD5, 0x4B}, + { BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_GAIN_UPD6, 0x00}, + { BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_GAIN_UPD7, 0x01}, + { BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_GAIN_UPD8, 0x00}, + { BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_GAIN_UPD9, 0x00}, + { BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_ATTN1, 0x04}, + { BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_ATTN2, 0x08}, + { BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_ATTN3, 0x0C}, + { BOLERO_CDC_WSA_VBAT_BCL_VBAT_DECODE_CTL1, 0xE0}, + { BOLERO_CDC_WSA_VBAT_BCL_VBAT_DECODE_CTL2, 0x00}, + { BOLERO_CDC_WSA_VBAT_BCL_VBAT_DECODE_CFG1, 0x00}, + { BOLERO_CDC_WSA_VBAT_BCL_VBAT_DECODE_CFG2, 0x00}, + { BOLERO_CDC_WSA_VBAT_BCL_VBAT_DECODE_CFG3, 0x00}, + { BOLERO_CDC_WSA_VBAT_BCL_VBAT_DECODE_CFG4, 0x00}, + { BOLERO_CDC_WSA_VBAT_BCL_VBAT_DECODE_ST, 0x00}, + { BOLERO_CDC_WSA_TX0_SPKR_PROT_PATH_CTL, 0x02}, + { BOLERO_CDC_WSA_TX0_SPKR_PROT_PATH_CFG0, 0x00}, + { BOLERO_CDC_WSA_TX1_SPKR_PROT_PATH_CTL, 0x02}, + { BOLERO_CDC_WSA_TX1_SPKR_PROT_PATH_CFG0, 0x00}, + { BOLERO_CDC_WSA_TX2_SPKR_PROT_PATH_CTL, 0x02}, + { BOLERO_CDC_WSA_TX2_SPKR_PROT_PATH_CFG0, 0x00}, + { BOLERO_CDC_WSA_TX3_SPKR_PROT_PATH_CTL, 0x02}, + { BOLERO_CDC_WSA_TX3_SPKR_PROT_PATH_CFG0, 0x00}, + { BOLERO_CDC_WSA_INTR_CTRL_CFG, 0x00}, + { BOLERO_CDC_WSA_INTR_CTRL_CLR_COMMIT, 0x00}, + { BOLERO_CDC_WSA_INTR_CTRL_PIN1_MASK0, 0xFF}, + { BOLERO_CDC_WSA_INTR_CTRL_PIN1_STATUS0, 0x00}, + { BOLERO_CDC_WSA_INTR_CTRL_PIN1_CLEAR0, 0x00}, + { BOLERO_CDC_WSA_INTR_CTRL_PIN2_MASK0, 0xFF}, + { BOLERO_CDC_WSA_INTR_CTRL_PIN2_STATUS0, 0x00}, + { BOLERO_CDC_WSA_INTR_CTRL_PIN2_CLEAR0, 0x00}, + { BOLERO_CDC_WSA_INTR_CTRL_LEVEL0, 0x00}, + { BOLERO_CDC_WSA_INTR_CTRL_BYPASS0, 0x00}, + { BOLERO_CDC_WSA_INTR_CTRL_SET0, 0x00}, + { BOLERO_CDC_WSA_RX0_RX_PATH_CTL, 0x04}, + { BOLERO_CDC_WSA_RX0_RX_PATH_CFG0, 0x00}, + { BOLERO_CDC_WSA_RX0_RX_PATH_CFG1, 0x64}, + { BOLERO_CDC_WSA_RX0_RX_PATH_CFG2, 0x8F}, + { BOLERO_CDC_WSA_RX0_RX_PATH_CFG3, 0x00}, + { BOLERO_CDC_WSA_RX0_RX_VOL_CTL, 0x00}, + { BOLERO_CDC_WSA_RX0_RX_PATH_MIX_CTL, 0x04}, + { BOLERO_CDC_WSA_RX0_RX_PATH_MIX_CFG, 0x7E}, + { BOLERO_CDC_WSA_RX0_RX_VOL_MIX_CTL, 0x00}, + { BOLERO_CDC_WSA_RX0_RX_PATH_SEC0, 0x04}, + { BOLERO_CDC_WSA_RX0_RX_PATH_SEC1, 0x08}, + { BOLERO_CDC_WSA_RX0_RX_PATH_SEC2, 0x00}, + { BOLERO_CDC_WSA_RX0_RX_PATH_SEC3, 0x00}, + { BOLERO_CDC_WSA_RX0_RX_PATH_SEC5, 0x00}, + { BOLERO_CDC_WSA_RX0_RX_PATH_SEC6, 0x00}, + { BOLERO_CDC_WSA_RX0_RX_PATH_SEC7, 0x00}, + { BOLERO_CDC_WSA_RX0_RX_PATH_MIX_SEC0, 0x08}, + { BOLERO_CDC_WSA_RX0_RX_PATH_MIX_SEC1, 0x00}, + { BOLERO_CDC_WSA_RX0_RX_PATH_DSMDEM_CTL, 0x00}, + { BOLERO_CDC_WSA_RX1_RX_PATH_CFG0, 0x00}, + { BOLERO_CDC_WSA_RX1_RX_PATH_CFG1, 0x64}, + { BOLERO_CDC_WSA_RX1_RX_PATH_CFG2, 0x8F}, + { BOLERO_CDC_WSA_RX1_RX_PATH_CFG3, 0x00}, + { BOLERO_CDC_WSA_RX1_RX_VOL_CTL, 0x00}, + { BOLERO_CDC_WSA_RX1_RX_PATH_MIX_CTL, 0x04}, + { BOLERO_CDC_WSA_RX1_RX_PATH_MIX_CFG, 0x7E}, + { BOLERO_CDC_WSA_RX1_RX_VOL_MIX_CTL, 0x00}, + { BOLERO_CDC_WSA_RX1_RX_PATH_SEC0, 0x04}, + { BOLERO_CDC_WSA_RX1_RX_PATH_SEC1, 0x08}, + { BOLERO_CDC_WSA_RX1_RX_PATH_SEC2, 0x00}, + { BOLERO_CDC_WSA_RX1_RX_PATH_SEC3, 0x00}, + { BOLERO_CDC_WSA_RX1_RX_PATH_SEC5, 0x00}, + { BOLERO_CDC_WSA_RX1_RX_PATH_SEC6, 0x00}, + { BOLERO_CDC_WSA_RX1_RX_PATH_SEC7, 0x00}, + { BOLERO_CDC_WSA_RX1_RX_PATH_MIX_SEC0, 0x08}, + { BOLERO_CDC_WSA_RX1_RX_PATH_MIX_SEC1, 0x00}, + { BOLERO_CDC_WSA_RX1_RX_PATH_DSMDEM_CTL, 0x00}, + { BOLERO_CDC_WSA_BOOST0_BOOST_PATH_CTL, 0x00}, + { BOLERO_CDC_WSA_BOOST0_BOOST_CTL, 0xD0}, + { BOLERO_CDC_WSA_BOOST0_BOOST_CFG1, 0x89}, + { BOLERO_CDC_WSA_BOOST0_BOOST_CFG2, 0x04}, + { BOLERO_CDC_WSA_BOOST1_BOOST_PATH_CTL, 0x00}, + { BOLERO_CDC_WSA_BOOST1_BOOST_CTL, 0xD0}, + { BOLERO_CDC_WSA_BOOST1_BOOST_CFG1, 0x89}, + { BOLERO_CDC_WSA_BOOST1_BOOST_CFG2, 0x04}, + { BOLERO_CDC_WSA_COMPANDER0_CTL0, 0x60}, + { BOLERO_CDC_WSA_COMPANDER0_CTL1, 0xDB}, + { BOLERO_CDC_WSA_COMPANDER0_CTL2, 0xFF}, + { BOLERO_CDC_WSA_COMPANDER0_CTL3, 0x35}, + { BOLERO_CDC_WSA_COMPANDER0_CTL4, 0xFF}, + { BOLERO_CDC_WSA_COMPANDER0_CTL5, 0x00}, + { BOLERO_CDC_WSA_COMPANDER0_CTL6, 0x01}, + { BOLERO_CDC_WSA_COMPANDER0_CTL7, 0x28}, + { BOLERO_CDC_WSA_COMPANDER1_CTL0, 0x60}, + { BOLERO_CDC_WSA_COMPANDER1_CTL1, 0xDB}, + { BOLERO_CDC_WSA_COMPANDER1_CTL2, 0xFF}, + { BOLERO_CDC_WSA_COMPANDER1_CTL3, 0x35}, + { BOLERO_CDC_WSA_COMPANDER1_CTL4, 0xFF}, + { BOLERO_CDC_WSA_COMPANDER1_CTL5, 0x00}, + { BOLERO_CDC_WSA_COMPANDER1_CTL6, 0x01}, + { BOLERO_CDC_WSA_COMPANDER1_CTL7, 0x28}, + { BOLERO_CDC_WSA_SOFTCLIP0_CRC, 0x00}, + { BOLERO_CDC_WSA_SOFTCLIP0_SOFTCLIP_CTRL, 0x38}, + { BOLERO_CDC_WSA_SOFTCLIP1_CRC, 0x00}, + { BOLERO_CDC_WSA_SOFTCLIP1_SOFTCLIP_CTRL, 0x38}, + { BOLERO_CDC_WSA_EC_HQ0_EC_REF_HQ_PATH_CTL, 0x00}, + { BOLERO_CDC_WSA_EC_HQ0_EC_REF_HQ_CFG0, 0x01}, + { BOLERO_CDC_WSA_EC_HQ1_EC_REF_HQ_PATH_CTL, 0x00}, + { BOLERO_CDC_WSA_EC_HQ1_EC_REF_HQ_CFG0, 0x01}, + { BOLERO_CDC_WSA_SPLINE_ASRC0_CLK_RST_CTL, 0x00}, + { BOLERO_CDC_WSA_SPLINE_ASRC0_CTL0, 0x00}, + { BOLERO_CDC_WSA_SPLINE_ASRC0_CTL1, 0x00}, + { BOLERO_CDC_WSA_SPLINE_ASRC0_FIFO_CTL, 0xA8}, + { BOLERO_CDC_WSA_SPLINE_ASRC0_STATUS_FMIN_CNTR_LSB, 0x00}, + { BOLERO_CDC_WSA_SPLINE_ASRC0_STATUS_FMIN_CNTR_MSB, 0x00}, + { BOLERO_CDC_WSA_SPLINE_ASRC0_STATUS_FMAX_CNTR_LSB, 0x00}, + { BOLERO_CDC_WSA_SPLINE_ASRC0_STATUS_FMAX_CNTR_MSB, 0x00}, + { BOLERO_CDC_WSA_SPLINE_ASRC0_STATUS_FIFO, 0x00}, + { BOLERO_CDC_WSA_SPLINE_ASRC1_CLK_RST_CTL, 0x00}, + { BOLERO_CDC_WSA_SPLINE_ASRC1_CTL0, 0x00}, + { BOLERO_CDC_WSA_SPLINE_ASRC1_CTL1, 0x00}, + { BOLERO_CDC_WSA_SPLINE_ASRC1_FIFO_CTL, 0xA8}, + { BOLERO_CDC_WSA_SPLINE_ASRC1_STATUS_FMIN_CNTR_LSB, 0x00}, + { BOLERO_CDC_WSA_SPLINE_ASRC1_STATUS_FMIN_CNTR_MSB, 0x00}, + { BOLERO_CDC_WSA_SPLINE_ASRC1_STATUS_FMAX_CNTR_LSB, 0x00}, + { BOLERO_CDC_WSA_SPLINE_ASRC1_STATUS_FMAX_CNTR_MSB, 0x00}, + { BOLERO_CDC_WSA_SPLINE_ASRC1_STATUS_FIFO, 0x00}, + + /* VA macro */ + { BOLERO_CDC_VA_CLK_RST_CTRL_MCLK_CONTROL, 0x00}, + { BOLERO_CDC_VA_CLK_RST_CTRL_FS_CNT_CONTROL, 0x00}, + { BOLERO_CDC_VA_TOP_CSR_TOP_CFG0, 0x00}, + { BOLERO_CDC_VA_TOP_CSR_DMIC0_CTL, 0x00}, + { BOLERO_CDC_VA_TOP_CSR_DMIC1_CTL, 0x00}, + { BOLERO_CDC_VA_TOP_CSR_DMIC2_CTL, 0x00}, + { BOLERO_CDC_VA_TOP_CSR_DMIC3_CTL, 0x00}, + { BOLERO_CDC_VA_TOP_CSR_DMIC_CFG, 0x80}, + { BOLERO_CDC_VA_TOP_CSR_DEBUG_BUS, 0x00}, + { BOLERO_CDC_VA_TOP_CSR_DEBUG_EN, 0x00}, + { BOLERO_CDC_VA_TOP_CSR_TX_I2S_CTL, 0x0C}, + { BOLERO_CDC_VA_TOP_CSR_I2S_CLK, 0x00}, + { BOLERO_CDC_VA_TOP_CSR_I2S_RESET, 0x00}, + { BOLERO_CDC_VA_TOP_CSR_CORE_ID_0, 0x00}, + { BOLERO_CDC_VA_TOP_CSR_CORE_ID_1, 0x00}, + { BOLERO_CDC_VA_TOP_CSR_CORE_ID_2, 0x00}, + { BOLERO_CDC_VA_TOP_CSR_CORE_ID_3, 0x00}, + + /* VA core */ + { BOLERO_CDC_VA_INP_MUX_ADC_MUX0_CFG0, 0x00}, + { BOLERO_CDC_VA_INP_MUX_ADC_MUX0_CFG1, 0x00}, + { BOLERO_CDC_VA_INP_MUX_ADC_MUX1_CFG0, 0x00}, + { BOLERO_CDC_VA_INP_MUX_ADC_MUX1_CFG1, 0x00}, + { BOLERO_CDC_VA_INP_MUX_ADC_MUX2_CFG0, 0x00}, + { BOLERO_CDC_VA_INP_MUX_ADC_MUX2_CFG1, 0x00}, + { BOLERO_CDC_VA_INP_MUX_ADC_MUX3_CFG0, 0x00}, + { BOLERO_CDC_VA_INP_MUX_ADC_MUX3_CFG1, 0x00}, + { BOLERO_CDC_VA_INP_MUX_ADC_MUX4_CFG0, 0x00}, + { BOLERO_CDC_VA_INP_MUX_ADC_MUX4_CFG1, 0x00}, + { BOLERO_CDC_VA_INP_MUX_ADC_MUX5_CFG0, 0x00}, + { BOLERO_CDC_VA_INP_MUX_ADC_MUX5_CFG1, 0x00}, + { BOLERO_CDC_VA_INP_MUX_ADC_MUX6_CFG0, 0x00}, + { BOLERO_CDC_VA_INP_MUX_ADC_MUX6_CFG1, 0x00}, + { BOLERO_CDC_VA_INP_MUX_ADC_MUX7_CFG0, 0x00}, + { BOLERO_CDC_VA_INP_MUX_ADC_MUX7_CFG1, 0x00}, + { BOLERO_CDC_VA_TX0_TX_PATH_CTL, 0x04}, + { BOLERO_CDC_VA_TX0_TX_PATH_CFG0, 0x10}, + { BOLERO_CDC_VA_TX0_TX_PATH_CFG1, 0x0B}, + { BOLERO_CDC_VA_TX0_TX_VOL_CTL, 0x00}, + { BOLERO_CDC_VA_TX0_TX_PATH_SEC0, 0x00}, + { BOLERO_CDC_VA_TX0_TX_PATH_SEC1, 0x00}, + { BOLERO_CDC_VA_TX0_TX_PATH_SEC2, 0x01}, + { BOLERO_CDC_VA_TX0_TX_PATH_SEC3, 0x3C}, + { BOLERO_CDC_VA_TX0_TX_PATH_SEC4, 0x20}, + { BOLERO_CDC_VA_TX0_TX_PATH_SEC5, 0x00}, + { BOLERO_CDC_VA_TX0_TX_PATH_SEC6, 0x00}, + { BOLERO_CDC_VA_TX0_TX_PATH_SEC7, 0x25}, + { BOLERO_CDC_VA_TX1_TX_PATH_CTL, 0x04}, + { BOLERO_CDC_VA_TX1_TX_PATH_CFG0, 0x10}, + { BOLERO_CDC_VA_TX1_TX_PATH_CFG1, 0x0B}, + { BOLERO_CDC_VA_TX1_TX_VOL_CTL, 0x00}, + { BOLERO_CDC_VA_TX1_TX_PATH_SEC0, 0x00}, + { BOLERO_CDC_VA_TX1_TX_PATH_SEC1, 0x00}, + { BOLERO_CDC_VA_TX1_TX_PATH_SEC2, 0x01}, + { BOLERO_CDC_VA_TX1_TX_PATH_SEC3, 0x3C}, + { BOLERO_CDC_VA_TX1_TX_PATH_SEC4, 0x20}, + { BOLERO_CDC_VA_TX1_TX_PATH_SEC5, 0x00}, + { BOLERO_CDC_VA_TX1_TX_PATH_SEC6, 0x00}, + { BOLERO_CDC_VA_TX2_TX_PATH_CTL, 0x04}, + { BOLERO_CDC_VA_TX2_TX_PATH_CFG0, 0x10}, + { BOLERO_CDC_VA_TX2_TX_PATH_CFG1, 0x0B}, + { BOLERO_CDC_VA_TX2_TX_VOL_CTL, 0x00}, + { BOLERO_CDC_VA_TX2_TX_PATH_SEC0, 0x00}, + { BOLERO_CDC_VA_TX2_TX_PATH_SEC1, 0x00}, + { BOLERO_CDC_VA_TX2_TX_PATH_SEC2, 0x01}, + { BOLERO_CDC_VA_TX2_TX_PATH_SEC3, 0x3C}, + { BOLERO_CDC_VA_TX2_TX_PATH_SEC4, 0x20}, + { BOLERO_CDC_VA_TX2_TX_PATH_SEC5, 0x00}, + { BOLERO_CDC_VA_TX2_TX_PATH_SEC6, 0x00}, + { BOLERO_CDC_VA_TX3_TX_PATH_CTL, 0x04}, + { BOLERO_CDC_VA_TX3_TX_PATH_CFG0, 0x10}, + { BOLERO_CDC_VA_TX3_TX_PATH_CFG1, 0x0B}, + { BOLERO_CDC_VA_TX3_TX_VOL_CTL, 0x00}, + { BOLERO_CDC_VA_TX3_TX_PATH_SEC0, 0x00}, + { BOLERO_CDC_VA_TX3_TX_PATH_SEC1, 0x00}, + { BOLERO_CDC_VA_TX3_TX_PATH_SEC2, 0x01}, + { BOLERO_CDC_VA_TX3_TX_PATH_SEC3, 0x3C}, + { BOLERO_CDC_VA_TX3_TX_PATH_SEC4, 0x20}, + { BOLERO_CDC_VA_TX3_TX_PATH_SEC5, 0x00}, + { BOLERO_CDC_VA_TX3_TX_PATH_SEC6, 0x00}, + { BOLERO_CDC_VA_TX4_TX_PATH_CTL, 0x04}, + { BOLERO_CDC_VA_TX4_TX_PATH_CFG0, 0x10}, + { BOLERO_CDC_VA_TX4_TX_PATH_CFG1, 0x0B}, + { BOLERO_CDC_VA_TX4_TX_VOL_CTL, 0x00}, + { BOLERO_CDC_VA_TX4_TX_PATH_SEC0, 0x00}, + { BOLERO_CDC_VA_TX4_TX_PATH_SEC1, 0x00}, + { BOLERO_CDC_VA_TX4_TX_PATH_SEC2, 0x01}, + { BOLERO_CDC_VA_TX4_TX_PATH_SEC3, 0x3C}, + { BOLERO_CDC_VA_TX4_TX_PATH_SEC4, 0x20}, + { BOLERO_CDC_VA_TX4_TX_PATH_SEC5, 0x00}, + { BOLERO_CDC_VA_TX4_TX_PATH_SEC6, 0x00}, + { BOLERO_CDC_VA_TX5_TX_PATH_CTL, 0x04}, + { BOLERO_CDC_VA_TX5_TX_PATH_CFG0, 0x10}, + { BOLERO_CDC_VA_TX5_TX_PATH_CFG1, 0x0B}, + { BOLERO_CDC_VA_TX5_TX_VOL_CTL, 0x00}, + { BOLERO_CDC_VA_TX5_TX_PATH_SEC0, 0x00}, + { BOLERO_CDC_VA_TX5_TX_PATH_SEC1, 0x00}, + { BOLERO_CDC_VA_TX5_TX_PATH_SEC2, 0x01}, + { BOLERO_CDC_VA_TX5_TX_PATH_SEC3, 0x3C}, + { BOLERO_CDC_VA_TX5_TX_PATH_SEC4, 0x20}, + { BOLERO_CDC_VA_TX5_TX_PATH_SEC5, 0x00}, + { BOLERO_CDC_VA_TX5_TX_PATH_SEC6, 0x00}, + { BOLERO_CDC_VA_TX6_TX_PATH_CTL, 0x04}, + { BOLERO_CDC_VA_TX6_TX_PATH_CFG0, 0x10}, + { BOLERO_CDC_VA_TX6_TX_PATH_CFG1, 0x0B}, + { BOLERO_CDC_VA_TX6_TX_VOL_CTL, 0x00}, + { BOLERO_CDC_VA_TX6_TX_PATH_SEC0, 0x00}, + { BOLERO_CDC_VA_TX6_TX_PATH_SEC1, 0x00}, + { BOLERO_CDC_VA_TX6_TX_PATH_SEC2, 0x01}, + { BOLERO_CDC_VA_TX6_TX_PATH_SEC3, 0x3C}, + { BOLERO_CDC_VA_TX6_TX_PATH_SEC4, 0x20}, + { BOLERO_CDC_VA_TX6_TX_PATH_SEC5, 0x00}, + { BOLERO_CDC_VA_TX6_TX_PATH_SEC6, 0x00}, + { BOLERO_CDC_VA_TX7_TX_PATH_CTL, 0x04}, + { BOLERO_CDC_VA_TX7_TX_PATH_CFG0, 0x10}, + { BOLERO_CDC_VA_TX7_TX_PATH_CFG1, 0x0B}, + { BOLERO_CDC_VA_TX7_TX_VOL_CTL, 0x00}, + { BOLERO_CDC_VA_TX7_TX_PATH_SEC0, 0x00}, + { BOLERO_CDC_VA_TX7_TX_PATH_SEC1, 0x00}, + { BOLERO_CDC_VA_TX7_TX_PATH_SEC2, 0x01}, + { BOLERO_CDC_VA_TX7_TX_PATH_SEC3, 0x3C}, + { BOLERO_CDC_VA_TX7_TX_PATH_SEC4, 0x20}, + { BOLERO_CDC_VA_TX7_TX_PATH_SEC5, 0x00}, + { BOLERO_CDC_VA_TX7_TX_PATH_SEC6, 0x00}, +}; + +static bool bolero_is_readable_register(struct device *dev, + unsigned int reg) +{ + struct bolero_priv *priv = dev_get_drvdata(dev); + u16 reg_offset; + int macro_id; + u8 *reg_tbl = NULL; + + if (!priv) + return false; + + macro_id = bolero_get_macro_id(priv->va_without_decimation, + reg); + if (macro_id < 0 || !priv->macros_supported[macro_id]) + return false; + + reg_tbl = bolero_reg_access[macro_id]; + reg_offset = (reg - macro_id_base_offset[macro_id])/4; + + if (reg_tbl) + return (reg_tbl[reg_offset] & RD_REG); + + return false; +} + +static bool bolero_is_writeable_register(struct device *dev, + unsigned int reg) +{ + struct bolero_priv *priv = dev_get_drvdata(dev); + u16 reg_offset; + int macro_id; + const u8 *reg_tbl = NULL; + + if (!priv) + return false; + + macro_id = bolero_get_macro_id(priv->va_without_decimation, + reg); + if (macro_id < 0 || !priv->macros_supported[macro_id]) + return false; + + reg_tbl = bolero_reg_access[macro_id]; + reg_offset = (reg - macro_id_base_offset[macro_id])/4; + + if (reg_tbl) + return (reg_tbl[reg_offset] & WR_REG); + + return false; +} + +static bool bolero_is_volatile_register(struct device *dev, + unsigned int reg) +{ + /* Update volatile list for rx/tx macros */ + switch (reg) { + case BOLERO_CDC_VA_TOP_CSR_CORE_ID_0: + case BOLERO_CDC_VA_TOP_CSR_CORE_ID_1: + case BOLERO_CDC_VA_TOP_CSR_CORE_ID_2: + case BOLERO_CDC_VA_TOP_CSR_CORE_ID_3: + case BOLERO_CDC_WSA_VBAT_BCL_VBAT_GAIN_MON_VAL: + case BOLERO_CDC_WSA_VBAT_BCL_VBAT_DECODE_ST: + case BOLERO_CDC_WSA_INTR_CTRL_PIN1_STATUS0: + case BOLERO_CDC_WSA_INTR_CTRL_PIN2_STATUS0: + case BOLERO_CDC_WSA_COMPANDER0_CTL6: + case BOLERO_CDC_WSA_COMPANDER1_CTL6: + case BOLERO_CDC_WSA_SPLINE_ASRC0_STATUS_FMIN_CNTR_LSB: + case BOLERO_CDC_WSA_SPLINE_ASRC0_STATUS_FMIN_CNTR_MSB: + case BOLERO_CDC_WSA_SPLINE_ASRC0_STATUS_FMAX_CNTR_LSB: + case BOLERO_CDC_WSA_SPLINE_ASRC0_STATUS_FMAX_CNTR_MSB: + case BOLERO_CDC_WSA_SPLINE_ASRC0_STATUS_FIFO: + case BOLERO_CDC_WSA_SPLINE_ASRC1_STATUS_FMIN_CNTR_LSB: + case BOLERO_CDC_WSA_SPLINE_ASRC1_STATUS_FMIN_CNTR_MSB: + case BOLERO_CDC_WSA_SPLINE_ASRC1_STATUS_FMAX_CNTR_LSB: + case BOLERO_CDC_WSA_SPLINE_ASRC1_STATUS_FMAX_CNTR_MSB: + case BOLERO_CDC_WSA_SPLINE_ASRC1_STATUS_FIFO: + case BOLERO_CDC_RX_TOP_HPHL_COMP_RD_LSB: + case BOLERO_CDC_RX_TOP_HPHL_COMP_RD_MSB: + case BOLERO_CDC_RX_TOP_HPHR_COMP_RD_LSB: + case BOLERO_CDC_RX_TOP_HPHR_COMP_RD_MSB: + case BOLERO_CDC_RX_TOP_DSD0_DEBUG_CFG2: + case BOLERO_CDC_RX_TOP_DSD1_DEBUG_CFG2: + case BOLERO_CDC_RX_BCL_VBAT_GAIN_MON_VAL: + case BOLERO_CDC_RX_BCL_VBAT_DECODE_ST: + case BOLERO_CDC_RX_INTR_CTRL_PIN1_STATUS0: + case BOLERO_CDC_RX_INTR_CTRL_PIN2_STATUS0: + case BOLERO_CDC_RX_COMPANDER0_CTL6: + case BOLERO_CDC_RX_COMPANDER1_CTL6: + case BOLERO_CDC_RX_EC_ASRC0_STATUS_FMIN_CNTR_LSB: + case BOLERO_CDC_RX_EC_ASRC0_STATUS_FMIN_CNTR_MSB: + case BOLERO_CDC_RX_EC_ASRC0_STATUS_FMAX_CNTR_LSB: + case BOLERO_CDC_RX_EC_ASRC0_STATUS_FMAX_CNTR_MSB: + case BOLERO_CDC_RX_EC_ASRC0_STATUS_FIFO: + case BOLERO_CDC_RX_EC_ASRC1_STATUS_FMIN_CNTR_LSB: + case BOLERO_CDC_RX_EC_ASRC1_STATUS_FMIN_CNTR_MSB: + case BOLERO_CDC_RX_EC_ASRC1_STATUS_FMAX_CNTR_LSB: + case BOLERO_CDC_RX_EC_ASRC1_STATUS_FMAX_CNTR_MSB: + case BOLERO_CDC_RX_EC_ASRC1_STATUS_FIFO: + case BOLERO_CDC_RX_EC_ASRC2_STATUS_FMIN_CNTR_LSB: + case BOLERO_CDC_RX_EC_ASRC2_STATUS_FMIN_CNTR_MSB: + case BOLERO_CDC_RX_EC_ASRC2_STATUS_FMAX_CNTR_LSB: + case BOLERO_CDC_RX_EC_ASRC2_STATUS_FMAX_CNTR_MSB: + case BOLERO_CDC_RX_EC_ASRC2_STATUS_FIFO: + return true; + } + return false; +} + +const struct regmap_config bolero_regmap_config = { + .reg_bits = 16, + .val_bits = 8, + .reg_stride = 4, + .cache_type = REGCACHE_RBTREE, + .reg_defaults = bolero_defaults, + .num_reg_defaults = ARRAY_SIZE(bolero_defaults), + .max_register = BOLERO_CDC_MAX_REGISTER, + .writeable_reg = bolero_is_writeable_register, + .volatile_reg = bolero_is_volatile_register, + .readable_reg = bolero_is_readable_register, +}; diff --git a/audio-kernel/asoc/codecs/bolero/bolero-cdc-tables.c b/audio-kernel/asoc/codecs/bolero/bolero-cdc-tables.c new file mode 100644 index 000000000..2407ac70e --- /dev/null +++ b/audio-kernel/asoc/codecs/bolero/bolero-cdc-tables.c @@ -0,0 +1,768 @@ +/* + * 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 +#include "bolero-cdc.h" +#include "internal.h" + +u8 bolero_tx_reg_access[BOLERO_CDC_TX_MACRO_MAX] = { + [BOLERO_REG(BOLERO_CDC_TX_CLK_RST_CTRL_MCLK_CONTROL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX_CLK_RST_CTRL_FS_CNT_CONTROL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX_CLK_RST_CTRL_SWR_CONTROL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX_TOP_CSR_TOP_CFG0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX_TOP_CSR_ANC_CFG)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX_TOP_CSR_SWR_CTRL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX_TOP_CSR_FREQ_MCLK)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX_TOP_CSR_DEBUG_BUS)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX_TOP_CSR_DEBUG_EN)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX_TOP_CSR_TX_I2S_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX_TOP_CSR_I2S_CLK)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX_TOP_CSR_I2S_RESET)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX_TOP_CSR_SWR_DMIC0_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX_TOP_CSR_SWR_DMIC1_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX_TOP_CSR_SWR_DMIC2_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX_TOP_CSR_SWR_DMIC3_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX_TOP_CSR_SWR_AMIC0_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX_TOP_CSR_SWR_AMIC1_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX_ANC0_CLK_RESET_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX_ANC0_MODE_1_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX_ANC0_MODE_2_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX_ANC0_FF_SHIFT)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX_ANC0_FB_SHIFT)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX_ANC0_LPF_FF_A_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX_ANC0_LPF_FF_B_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX_ANC0_LPF_FB_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX_ANC0_SMLPF_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX_ANC0_DCFLT_SHIFT_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX_ANC0_IIR_ADAPT_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX_ANC0_IIR_COEFF_1_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX_ANC0_IIR_COEFF_2_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX_ANC0_FF_A_GAIN_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX_ANC0_FF_B_GAIN_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX_ANC0_FB_GAIN_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX_INP_MUX_ADC_MUX0_CFG0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX_INP_MUX_ADC_MUX0_CFG1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX_INP_MUX_ADC_MUX1_CFG0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX_INP_MUX_ADC_MUX1_CFG1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX_INP_MUX_ADC_MUX2_CFG0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX_INP_MUX_ADC_MUX2_CFG1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX_INP_MUX_ADC_MUX3_CFG0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX_INP_MUX_ADC_MUX3_CFG1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX_INP_MUX_ADC_MUX4_CFG0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX_INP_MUX_ADC_MUX4_CFG1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX_INP_MUX_ADC_MUX5_CFG0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX_INP_MUX_ADC_MUX5_CFG1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX_INP_MUX_ADC_MUX6_CFG0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX_INP_MUX_ADC_MUX6_CFG1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX_INP_MUX_ADC_MUX7_CFG0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX_INP_MUX_ADC_MUX7_CFG1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX0_TX_PATH_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX0_TX_PATH_CFG0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX0_TX_PATH_CFG1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX0_TX_VOL_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX0_TX_PATH_SEC0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX0_TX_PATH_SEC1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX0_TX_PATH_SEC2)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX0_TX_PATH_SEC3)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX0_TX_PATH_SEC4)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX0_TX_PATH_SEC5)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX0_TX_PATH_SEC6)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX0_TX_PATH_SEC7)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX1_TX_PATH_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX1_TX_PATH_CFG0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX1_TX_PATH_CFG1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX1_TX_VOL_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX1_TX_PATH_SEC0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX1_TX_PATH_SEC1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX1_TX_PATH_SEC2)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX1_TX_PATH_SEC3)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX1_TX_PATH_SEC4)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX1_TX_PATH_SEC5)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX1_TX_PATH_SEC6)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX2_TX_PATH_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX2_TX_PATH_CFG0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX2_TX_PATH_CFG1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX2_TX_VOL_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX2_TX_PATH_SEC0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX2_TX_PATH_SEC1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX2_TX_PATH_SEC2)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX2_TX_PATH_SEC3)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX2_TX_PATH_SEC4)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX2_TX_PATH_SEC5)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX2_TX_PATH_SEC6)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX3_TX_PATH_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX3_TX_PATH_CFG0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX3_TX_PATH_CFG1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX3_TX_VOL_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX3_TX_PATH_SEC0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX3_TX_PATH_SEC1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX3_TX_PATH_SEC2)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX3_TX_PATH_SEC3)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX3_TX_PATH_SEC4)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX3_TX_PATH_SEC5)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX3_TX_PATH_SEC6)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX4_TX_PATH_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX4_TX_PATH_CFG0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX4_TX_PATH_CFG1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX4_TX_VOL_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX4_TX_PATH_SEC0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX4_TX_PATH_SEC1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX4_TX_PATH_SEC2)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX4_TX_PATH_SEC3)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX4_TX_PATH_SEC4)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX4_TX_PATH_SEC5)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX4_TX_PATH_SEC6)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX5_TX_PATH_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX5_TX_PATH_CFG0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX5_TX_PATH_CFG1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX5_TX_VOL_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX5_TX_PATH_SEC0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX5_TX_PATH_SEC1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX5_TX_PATH_SEC2)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX5_TX_PATH_SEC3)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX5_TX_PATH_SEC4)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX5_TX_PATH_SEC5)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX5_TX_PATH_SEC6)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX6_TX_PATH_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX6_TX_PATH_CFG0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX6_TX_PATH_CFG1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX6_TX_VOL_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX6_TX_PATH_SEC0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX6_TX_PATH_SEC1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX6_TX_PATH_SEC2)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX6_TX_PATH_SEC3)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX6_TX_PATH_SEC4)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX6_TX_PATH_SEC5)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX6_TX_PATH_SEC6)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX7_TX_PATH_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX7_TX_PATH_CFG0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX7_TX_PATH_CFG1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX7_TX_VOL_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX7_TX_PATH_SEC0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX7_TX_PATH_SEC1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX7_TX_PATH_SEC2)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX7_TX_PATH_SEC3)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX7_TX_PATH_SEC4)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX7_TX_PATH_SEC5)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_TX7_TX_PATH_SEC6)] = RD_WR_REG, +}; + +u8 bolero_rx_reg_access[BOLERO_CDC_RX_MACRO_MAX] = { + [BOLERO_REG(BOLERO_CDC_RX_TOP_TOP_CFG0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_TOP_SWR_CTRL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_TOP_DEBUG)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_TOP_DEBUG_BUS)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_TOP_DEBUG_EN0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_TOP_DEBUG_EN1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_TOP_DEBUG_EN2)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_TOP_HPHL_COMP_WR_LSB)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_TOP_HPHL_COMP_WR_MSB)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_TOP_HPHL_COMP_LUT)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_TOP_HPHL_COMP_RD_LSB)] = RD_REG, + [BOLERO_REG(BOLERO_CDC_RX_TOP_HPHL_COMP_RD_MSB)] = RD_REG, + [BOLERO_REG(BOLERO_CDC_RX_TOP_HPHR_COMP_WR_LSB)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_TOP_HPHR_COMP_WR_MSB)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_TOP_HPHR_COMP_LUT)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_TOP_HPHR_COMP_RD_LSB)] = RD_REG, + [BOLERO_REG(BOLERO_CDC_RX_TOP_HPHR_COMP_RD_MSB)] = RD_REG, + [BOLERO_REG(BOLERO_CDC_RX_TOP_DSD0_DEBUG_CFG0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_TOP_DSD0_DEBUG_CFG1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_TOP_DSD0_DEBUG_CFG2)] = RD_REG, + [BOLERO_REG(BOLERO_CDC_RX_TOP_DSD0_DEBUG_CFG3)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_TOP_DSD1_DEBUG_CFG0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_TOP_DSD1_DEBUG_CFG1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_TOP_DSD1_DEBUG_CFG2)] = RD_REG, + [BOLERO_REG(BOLERO_CDC_RX_TOP_DSD1_DEBUG_CFG3)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_TOP_RX_I2S_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_TOP_TX_I2S2_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_TOP_I2S_CLK)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_TOP_I2S_RESET)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_TOP_I2S_MUX)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_CLK_RST_CTRL_MCLK_CONTROL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_CLK_RST_CTRL_FS_CNT_CONTROL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_CLK_RST_CTRL_SWR_CONTROL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_CLK_RST_CTRL_DSD_CONTROL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_CLK_RST_CTRL_ASRC_SHARE_CONTROL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_SOFTCLIP_CRC)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_SOFTCLIP_SOFTCLIP_CTRL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_INP_MUX_RX_INT0_CFG0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_INP_MUX_RX_INT0_CFG1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_INP_MUX_RX_INT1_CFG0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_INP_MUX_RX_INT1_CFG1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_INP_MUX_RX_INT2_CFG0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_INP_MUX_RX_INT2_CFG1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_INP_MUX_RX_MIX_CFG4)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_INP_MUX_RX_MIX_CFG5)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_INP_MUX_SIDETONE_SRC_CFG0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_CLSH_CRC)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_CLSH_DLY_CTRL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_CLSH_DECAY_CTRL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_CLSH_HPH_V_PA)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_CLSH_EAR_V_PA)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_CLSH_HPH_V_HD)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_CLSH_EAR_V_HD)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_CLSH_K1_MSB)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_CLSH_K1_LSB)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_CLSH_K2_MSB)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_CLSH_K2_LSB)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_CLSH_IDLE_CTRL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_CLSH_IDLE_HPH)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_CLSH_IDLE_EAR)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_CLSH_TEST0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_CLSH_TEST1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_CLSH_OVR_VREF)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_CLSH_CLSG_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_CLSH_CLSG_CFG1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_CLSH_CLSG_CFG2)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_BCL_VBAT_PATH_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_BCL_VBAT_CFG)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_BCL_VBAT_ADC_CAL1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_BCL_VBAT_ADC_CAL2)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_BCL_VBAT_ADC_CAL3)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_BCL_VBAT_PK_EST1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_BCL_VBAT_PK_EST2)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_BCL_VBAT_PK_EST3)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_BCL_VBAT_RF_PROC1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_BCL_VBAT_RF_PROC2)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_BCL_VBAT_TAC1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_BCL_VBAT_TAC2)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_BCL_VBAT_TAC3)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_BCL_VBAT_TAC4)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_BCL_VBAT_GAIN_UPD1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_BCL_VBAT_GAIN_UPD2)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_BCL_VBAT_GAIN_UPD3)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_BCL_VBAT_GAIN_UPD4)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_BCL_VBAT_GAIN_UPD5)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_BCL_VBAT_DEBUG1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_BCL_VBAT_GAIN_UPD_MON)] = WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_BCL_VBAT_GAIN_MON_VAL)] = RD_REG, + [BOLERO_REG(BOLERO_CDC_RX_BCL_VBAT_BAN)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_BCL_VBAT_BCL_GAIN_UPD1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_BCL_VBAT_BCL_GAIN_UPD2)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_BCL_VBAT_BCL_GAIN_UPD3)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_BCL_VBAT_BCL_GAIN_UPD4)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_BCL_VBAT_BCL_GAIN_UPD5)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_BCL_VBAT_BCL_GAIN_UPD6)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_BCL_VBAT_BCL_GAIN_UPD7)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_BCL_VBAT_BCL_GAIN_UPD8)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_BCL_VBAT_BCL_GAIN_UPD9)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_BCL_VBAT_ATTN1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_BCL_VBAT_ATTN2)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_BCL_VBAT_ATTN3)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_BCL_VBAT_DECODE_CTL1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_BCL_VBAT_DECODE_CTL2)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_BCL_VBAT_DECODE_CFG1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_BCL_VBAT_DECODE_CFG2)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_BCL_VBAT_DECODE_CFG3)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_BCL_VBAT_DECODE_CFG4)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_BCL_VBAT_DECODE_ST)] = RD_REG, + [BOLERO_REG(BOLERO_CDC_RX_INTR_CTRL_CFG)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_INTR_CTRL_CLR_COMMIT)] = WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_INTR_CTRL_PIN1_MASK0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_INTR_CTRL_PIN1_STATUS0)] = RD_REG, + [BOLERO_REG(BOLERO_CDC_RX_INTR_CTRL_PIN1_CLEAR0)] = WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_INTR_CTRL_PIN2_MASK0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_INTR_CTRL_PIN2_STATUS0)] = RD_REG, + [BOLERO_REG(BOLERO_CDC_RX_INTR_CTRL_PIN2_CLEAR0)] = WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_INTR_CTRL_LEVEL0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_INTR_CTRL_BYPASS0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_INTR_CTRL_SET0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_RX0_RX_PATH_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_RX0_RX_PATH_CFG0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_RX0_RX_PATH_CFG1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_RX0_RX_PATH_CFG2)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_RX0_RX_PATH_CFG3)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_RX0_RX_VOL_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_RX0_RX_PATH_MIX_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_RX0_RX_PATH_MIX_CFG)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_RX0_RX_VOL_MIX_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_RX0_RX_PATH_SEC1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_RX0_RX_PATH_SEC2)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_RX0_RX_PATH_SEC3)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_RX0_RX_PATH_SEC4)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_RX0_RX_PATH_SEC7)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_RX0_RX_PATH_MIX_SEC0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_RX0_RX_PATH_MIX_SEC1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_RX0_RX_PATH_DSM_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_RX0_RX_PATH_DSM_DATA1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_RX0_RX_PATH_DSM_DATA2)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_RX0_RX_PATH_DSM_DATA3)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_RX0_RX_PATH_DSM_DATA4)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_RX0_RX_PATH_DSM_DATA5)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_RX0_RX_PATH_DSM_DATA6)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_RX1_RX_PATH_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_RX1_RX_PATH_CFG0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_RX1_RX_PATH_CFG1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_RX1_RX_PATH_CFG2)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_RX1_RX_PATH_CFG3)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_RX1_RX_VOL_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_RX1_RX_PATH_MIX_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_RX1_RX_PATH_MIX_CFG)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_RX1_RX_VOL_MIX_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_RX1_RX_PATH_SEC1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_RX1_RX_PATH_SEC2)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_RX1_RX_PATH_SEC3)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_RX1_RX_PATH_SEC4)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_RX1_RX_PATH_SEC7)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_RX1_RX_PATH_MIX_SEC0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_RX1_RX_PATH_MIX_SEC1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_RX1_RX_PATH_DSM_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_RX1_RX_PATH_DSM_DATA1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_RX1_RX_PATH_DSM_DATA2)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_RX1_RX_PATH_DSM_DATA3)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_RX1_RX_PATH_DSM_DATA4)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_RX1_RX_PATH_DSM_DATA5)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_RX1_RX_PATH_DSM_DATA6)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_RX2_RX_PATH_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_RX2_RX_PATH_CFG0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_RX2_RX_PATH_CFG1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_RX2_RX_PATH_CFG2)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_RX2_RX_PATH_CFG3)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_RX2_RX_VOL_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_RX2_RX_PATH_MIX_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_RX2_RX_PATH_MIX_CFG)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_RX2_RX_VOL_MIX_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_RX2_RX_PATH_SEC0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_RX2_RX_PATH_SEC1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_RX2_RX_PATH_SEC2)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_RX2_RX_PATH_SEC3)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_RX2_RX_PATH_SEC4)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_RX2_RX_PATH_SEC5)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_RX2_RX_PATH_SEC6)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_RX2_RX_PATH_SEC7)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_RX2_RX_PATH_MIX_SEC0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_RX2_RX_PATH_MIX_SEC1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_RX2_RX_PATH_DSM_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_IDLE_DETECT_PATH_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_IDLE_DETECT_CFG0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_IDLE_DETECT_CFG1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_IDLE_DETECT_CFG2)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_IDLE_DETECT_CFG3)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_COMPANDER0_CTL0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_COMPANDER0_CTL1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_COMPANDER0_CTL2)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_COMPANDER0_CTL3)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_COMPANDER0_CTL4)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_COMPANDER0_CTL5)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_COMPANDER0_CTL6)] = RD_REG, + [BOLERO_REG(BOLERO_CDC_RX_COMPANDER0_CTL7)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_COMPANDER1_CTL0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_COMPANDER1_CTL1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_COMPANDER1_CTL2)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_COMPANDER1_CTL3)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_COMPANDER1_CTL4)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_COMPANDER1_CTL5)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_COMPANDER1_CTL6)] = RD_REG, + [BOLERO_REG(BOLERO_CDC_RX_COMPANDER1_CTL7)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_SIDETONE_IIR0_IIR_PATH_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_SIDETONE_IIR0_IIR_GAIN_B1_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_SIDETONE_IIR0_IIR_GAIN_B2_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_SIDETONE_IIR0_IIR_GAIN_B3_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_SIDETONE_IIR0_IIR_GAIN_B4_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_SIDETONE_IIR0_IIR_GAIN_B5_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_SIDETONE_IIR0_IIR_GAIN_B6_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_SIDETONE_IIR0_IIR_GAIN_B7_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_SIDETONE_IIR0_IIR_GAIN_B8_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_SIDETONE_IIR0_IIR_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_SIDETONE_IIR0_IIR_GAIN_TIMER_CTL)] = + RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_SIDETONE_IIR0_IIR_COEF_B1_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_SIDETONE_IIR0_IIR_COEF_B2_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_SIDETONE_IIR1_IIR_PATH_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_SIDETONE_IIR1_IIR_GAIN_B1_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_SIDETONE_IIR1_IIR_GAIN_B2_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_SIDETONE_IIR1_IIR_GAIN_B3_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_SIDETONE_IIR1_IIR_GAIN_B4_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_SIDETONE_IIR1_IIR_GAIN_B5_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_SIDETONE_IIR1_IIR_GAIN_B6_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_SIDETONE_IIR1_IIR_GAIN_B7_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_SIDETONE_IIR1_IIR_GAIN_B8_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_SIDETONE_IIR1_IIR_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_SIDETONE_IIR1_IIR_GAIN_TIMER_CTL)] = + RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_SIDETONE_IIR1_IIR_COEF_B1_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_SIDETONE_IIR1_IIR_COEF_B2_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_IIR_INP_MUX_IIR0_MIX_CFG0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_IIR_INP_MUX_IIR0_MIX_CFG1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_IIR_INP_MUX_IIR0_MIX_CFG2)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_IIR_INP_MUX_IIR0_MIX_CFG3)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_IIR_INP_MUX_IIR1_MIX_CFG0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_IIR_INP_MUX_IIR1_MIX_CFG1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_IIR_INP_MUX_IIR1_MIX_CFG2)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_IIR_INP_MUX_IIR1_MIX_CFG3)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_SIDETONE_SRC0_ST_SRC_PATH_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_SIDETONE_SRC0_ST_SRC_PATH_CFG1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_SIDETONE_SRC1_ST_SRC_PATH_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_SIDETONE_SRC1_ST_SRC_PATH_CFG1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_EC_REF_HQ0_EC_REF_HQ_PATH_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_EC_REF_HQ0_EC_REF_HQ_CFG0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_EC_REF_HQ1_EC_REF_HQ_PATH_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_EC_REF_HQ1_EC_REF_HQ_CFG0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_EC_REF_HQ2_EC_REF_HQ_PATH_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_EC_REF_HQ2_EC_REF_HQ_CFG0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_EC_ASRC0_CLK_RST_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_EC_ASRC0_CTL0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_EC_ASRC0_CTL1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_EC_ASRC0_FIFO_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_EC_ASRC0_STATUS_FMIN_CNTR_LSB)] = RD_REG, + [BOLERO_REG(BOLERO_CDC_RX_EC_ASRC0_STATUS_FMIN_CNTR_MSB)] = RD_REG, + [BOLERO_REG(BOLERO_CDC_RX_EC_ASRC0_STATUS_FMAX_CNTR_LSB)] = RD_REG, + [BOLERO_REG(BOLERO_CDC_RX_EC_ASRC0_STATUS_FMAX_CNTR_MSB)] = RD_REG, + [BOLERO_REG(BOLERO_CDC_RX_EC_ASRC0_STATUS_FIFO)] = RD_REG, + [BOLERO_REG(BOLERO_CDC_RX_EC_ASRC1_CLK_RST_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_EC_ASRC1_CTL0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_EC_ASRC1_CTL1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_EC_ASRC1_FIFO_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_EC_ASRC1_STATUS_FMIN_CNTR_LSB)] = RD_REG, + [BOLERO_REG(BOLERO_CDC_RX_EC_ASRC1_STATUS_FMIN_CNTR_MSB)] = RD_REG, + [BOLERO_REG(BOLERO_CDC_RX_EC_ASRC1_STATUS_FMAX_CNTR_LSB)] = RD_REG, + [BOLERO_REG(BOLERO_CDC_RX_EC_ASRC1_STATUS_FMAX_CNTR_MSB)] = RD_REG, + [BOLERO_REG(BOLERO_CDC_RX_EC_ASRC1_STATUS_FIFO)] = RD_REG, + [BOLERO_REG(BOLERO_CDC_RX_EC_ASRC2_CLK_RST_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_EC_ASRC2_CTL0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_EC_ASRC2_CTL1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_EC_ASRC2_FIFO_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_EC_ASRC2_STATUS_FMIN_CNTR_LSB)] = RD_REG, + [BOLERO_REG(BOLERO_CDC_RX_EC_ASRC2_STATUS_FMIN_CNTR_MSB)] = RD_REG, + [BOLERO_REG(BOLERO_CDC_RX_EC_ASRC2_STATUS_FMAX_CNTR_LSB)] = RD_REG, + [BOLERO_REG(BOLERO_CDC_RX_EC_ASRC2_STATUS_FMAX_CNTR_MSB)] = RD_REG, + [BOLERO_REG(BOLERO_CDC_RX_EC_ASRC2_STATUS_FIFO)] = RD_REG, + [BOLERO_REG(BOLERO_CDC_RX_DSD0_PATH_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_DSD0_CFG0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_DSD0_CFG1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_DSD0_CFG2)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_DSD1_PATH_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_DSD1_CFG0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_DSD1_CFG1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_RX_DSD1_CFG2)] = RD_WR_REG, +}; + +u8 bolero_va_reg_access[BOLERO_CDC_VA_MACRO_MAX] = { + [BOLERO_REG(BOLERO_CDC_VA_CLK_RST_CTRL_MCLK_CONTROL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_CLK_RST_CTRL_FS_CNT_CONTROL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_TOP_CSR_TOP_CFG0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_TOP_CSR_DMIC0_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_TOP_CSR_DMIC1_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_TOP_CSR_DMIC2_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_TOP_CSR_DMIC3_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_TOP_CSR_DMIC_CFG)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_TOP_CSR_DEBUG_BUS)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_TOP_CSR_DEBUG_EN)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_TOP_CSR_TX_I2S_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_TOP_CSR_I2S_CLK)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_TOP_CSR_I2S_RESET)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_TOP_CSR_CORE_ID_0)] = RD_REG, + [BOLERO_REG(BOLERO_CDC_VA_TOP_CSR_CORE_ID_1)] = RD_REG, + [BOLERO_REG(BOLERO_CDC_VA_TOP_CSR_CORE_ID_2)] = RD_REG, + [BOLERO_REG(BOLERO_CDC_VA_TOP_CSR_CORE_ID_3)] = RD_REG, + [BOLERO_REG(BOLERO_CDC_VA_INP_MUX_ADC_MUX0_CFG0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_INP_MUX_ADC_MUX0_CFG1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_INP_MUX_ADC_MUX1_CFG0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_INP_MUX_ADC_MUX1_CFG1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_INP_MUX_ADC_MUX2_CFG0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_INP_MUX_ADC_MUX2_CFG1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_INP_MUX_ADC_MUX3_CFG0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_INP_MUX_ADC_MUX3_CFG1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_INP_MUX_ADC_MUX4_CFG0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_INP_MUX_ADC_MUX4_CFG1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_INP_MUX_ADC_MUX5_CFG0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_INP_MUX_ADC_MUX5_CFG1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_INP_MUX_ADC_MUX6_CFG0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_INP_MUX_ADC_MUX6_CFG1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_INP_MUX_ADC_MUX7_CFG0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_INP_MUX_ADC_MUX7_CFG1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_TX0_TX_PATH_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_TX0_TX_PATH_CFG0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_TX0_TX_PATH_CFG1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_TX0_TX_VOL_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_TX0_TX_PATH_SEC0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_TX0_TX_PATH_SEC1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_TX0_TX_PATH_SEC2)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_TX0_TX_PATH_SEC3)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_TX0_TX_PATH_SEC4)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_TX0_TX_PATH_SEC5)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_TX0_TX_PATH_SEC6)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_TX0_TX_PATH_SEC7)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_TX1_TX_PATH_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_TX1_TX_PATH_CFG0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_TX1_TX_PATH_CFG1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_TX1_TX_VOL_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_TX1_TX_PATH_SEC0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_TX1_TX_PATH_SEC1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_TX1_TX_PATH_SEC2)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_TX1_TX_PATH_SEC3)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_TX1_TX_PATH_SEC4)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_TX1_TX_PATH_SEC5)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_TX1_TX_PATH_SEC6)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_TX2_TX_PATH_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_TX2_TX_PATH_CFG0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_TX2_TX_PATH_CFG1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_TX2_TX_VOL_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_TX2_TX_PATH_SEC0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_TX2_TX_PATH_SEC1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_TX2_TX_PATH_SEC2)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_TX2_TX_PATH_SEC3)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_TX2_TX_PATH_SEC4)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_TX2_TX_PATH_SEC5)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_TX2_TX_PATH_SEC6)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_TX3_TX_PATH_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_TX3_TX_PATH_CFG0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_TX3_TX_PATH_CFG1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_TX3_TX_VOL_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_TX3_TX_PATH_SEC0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_TX3_TX_PATH_SEC1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_TX3_TX_PATH_SEC2)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_TX3_TX_PATH_SEC3)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_TX3_TX_PATH_SEC4)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_TX3_TX_PATH_SEC5)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_TX3_TX_PATH_SEC6)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_TX4_TX_PATH_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_TX4_TX_PATH_CFG0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_TX4_TX_PATH_CFG1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_TX4_TX_VOL_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_TX4_TX_PATH_SEC0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_TX4_TX_PATH_SEC1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_TX4_TX_PATH_SEC2)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_TX4_TX_PATH_SEC3)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_TX4_TX_PATH_SEC4)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_TX4_TX_PATH_SEC5)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_TX4_TX_PATH_SEC6)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_TX5_TX_PATH_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_TX5_TX_PATH_CFG0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_TX5_TX_PATH_CFG1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_TX5_TX_VOL_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_TX5_TX_PATH_SEC0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_TX5_TX_PATH_SEC1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_TX5_TX_PATH_SEC2)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_TX5_TX_PATH_SEC3)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_TX5_TX_PATH_SEC4)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_TX5_TX_PATH_SEC5)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_TX5_TX_PATH_SEC6)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_TX6_TX_PATH_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_TX6_TX_PATH_CFG0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_TX6_TX_PATH_CFG1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_TX6_TX_VOL_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_TX6_TX_PATH_SEC0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_TX6_TX_PATH_SEC1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_TX6_TX_PATH_SEC2)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_TX6_TX_PATH_SEC3)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_TX6_TX_PATH_SEC4)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_TX6_TX_PATH_SEC5)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_TX6_TX_PATH_SEC6)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_TX7_TX_PATH_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_TX7_TX_PATH_CFG0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_TX7_TX_PATH_CFG1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_TX7_TX_VOL_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_TX7_TX_PATH_SEC0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_TX7_TX_PATH_SEC1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_TX7_TX_PATH_SEC2)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_TX7_TX_PATH_SEC3)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_TX7_TX_PATH_SEC4)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_TX7_TX_PATH_SEC5)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_TX7_TX_PATH_SEC6)] = RD_WR_REG, +}; + +u8 bolero_va_top_reg_access[BOLERO_CDC_VA_MACRO_TOP_MAX] = { + [BOLERO_REG(BOLERO_CDC_VA_CLK_RST_CTRL_MCLK_CONTROL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_CLK_RST_CTRL_FS_CNT_CONTROL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_TOP_CSR_TOP_CFG0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_TOP_CSR_DMIC0_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_TOP_CSR_DMIC1_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_TOP_CSR_DMIC_CFG)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_TOP_CSR_DEBUG_BUS)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_TOP_CSR_DEBUG_EN)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_VA_TOP_CSR_CORE_ID_0)] = RD_REG, + [BOLERO_REG(BOLERO_CDC_VA_TOP_CSR_CORE_ID_1)] = RD_REG, + [BOLERO_REG(BOLERO_CDC_VA_TOP_CSR_CORE_ID_2)] = RD_REG, + [BOLERO_REG(BOLERO_CDC_VA_TOP_CSR_CORE_ID_3)] = RD_REG, +}; + +u8 bolero_wsa_reg_access[BOLERO_CDC_WSA_MACRO_MAX] = { + [BOLERO_REG(BOLERO_CDC_WSA_CLK_RST_CTRL_MCLK_CONTROL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_CLK_RST_CTRL_FS_CNT_CONTROL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_CLK_RST_CTRL_SWR_CONTROL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_TOP_TOP_CFG0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_TOP_TOP_CFG1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_TOP_FREQ_MCLK)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_TOP_DEBUG_BUS_SEL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_TOP_DEBUG_EN0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_TOP_DEBUG_EN1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_TOP_DEBUG_DSM_LB)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_TOP_RX_I2S_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_TOP_TX_I2S_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_TOP_I2S_CLK)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_TOP_I2S_RESET)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_RX_INP_MUX_RX_INT0_CFG0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_RX_INP_MUX_RX_INT0_CFG1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_RX_INP_MUX_RX_INT1_CFG0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_RX_INP_MUX_RX_INT1_CFG1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_RX_INP_MUX_RX_MIX_CFG0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_RX_INP_MUX_RX_EC_CFG0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_RX_INP_MUX_SOFTCLIP_CFG0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_VBAT_BCL_VBAT_PATH_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_VBAT_BCL_VBAT_CFG)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_VBAT_BCL_VBAT_ADC_CAL1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_VBAT_BCL_VBAT_ADC_CAL2)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_VBAT_BCL_VBAT_ADC_CAL3)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_VBAT_BCL_VBAT_PK_EST1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_VBAT_BCL_VBAT_PK_EST2)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_VBAT_BCL_VBAT_PK_EST3)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_VBAT_BCL_VBAT_RF_PROC1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_VBAT_BCL_VBAT_RF_PROC2)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_VBAT_BCL_VBAT_TAC1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_VBAT_BCL_VBAT_TAC2)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_VBAT_BCL_VBAT_TAC3)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_VBAT_BCL_VBAT_TAC4)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_VBAT_BCL_VBAT_GAIN_UPD1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_VBAT_BCL_VBAT_GAIN_UPD2)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_VBAT_BCL_VBAT_GAIN_UPD3)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_VBAT_BCL_VBAT_GAIN_UPD4)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_VBAT_BCL_VBAT_GAIN_UPD5)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_VBAT_BCL_VBAT_DEBUG1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_VBAT_BCL_VBAT_GAIN_UPD_MON)] = WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_VBAT_BCL_VBAT_GAIN_MON_VAL)] = RD_REG, + [BOLERO_REG(BOLERO_CDC_WSA_VBAT_BCL_VBAT_BAN)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_GAIN_UPD1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_GAIN_UPD2)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_GAIN_UPD3)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_GAIN_UPD4)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_GAIN_UPD5)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_GAIN_UPD6)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_GAIN_UPD7)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_GAIN_UPD8)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_GAIN_UPD9)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_ATTN1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_ATTN2)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_ATTN3)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_VBAT_BCL_VBAT_DECODE_CTL1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_VBAT_BCL_VBAT_DECODE_CTL2)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_VBAT_BCL_VBAT_DECODE_CFG1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_VBAT_BCL_VBAT_DECODE_CFG2)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_VBAT_BCL_VBAT_DECODE_CFG3)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_VBAT_BCL_VBAT_DECODE_CFG4)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_VBAT_BCL_VBAT_DECODE_ST)] = RD_REG, + [BOLERO_REG(BOLERO_CDC_WSA_TX0_SPKR_PROT_PATH_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_TX0_SPKR_PROT_PATH_CFG0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_TX1_SPKR_PROT_PATH_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_TX1_SPKR_PROT_PATH_CFG0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_TX2_SPKR_PROT_PATH_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_TX2_SPKR_PROT_PATH_CFG0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_TX3_SPKR_PROT_PATH_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_TX3_SPKR_PROT_PATH_CFG0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_INTR_CTRL_CFG)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_INTR_CTRL_CLR_COMMIT)] = WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_INTR_CTRL_PIN1_MASK0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_INTR_CTRL_PIN1_STATUS0)] = RD_REG, + [BOLERO_REG(BOLERO_CDC_WSA_INTR_CTRL_PIN1_CLEAR0)] = WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_INTR_CTRL_PIN2_MASK0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_INTR_CTRL_PIN2_STATUS0)] = RD_REG, + [BOLERO_REG(BOLERO_CDC_WSA_INTR_CTRL_PIN2_CLEAR0)] = WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_INTR_CTRL_LEVEL0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_INTR_CTRL_BYPASS0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_INTR_CTRL_SET0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_RX0_RX_PATH_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_RX0_RX_PATH_CFG0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_RX0_RX_PATH_CFG1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_RX0_RX_PATH_CFG2)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_RX0_RX_PATH_CFG3)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_RX0_RX_VOL_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_RX0_RX_PATH_MIX_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_RX0_RX_PATH_MIX_CFG)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_RX0_RX_VOL_MIX_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_RX0_RX_PATH_SEC0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_RX0_RX_PATH_SEC1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_RX0_RX_PATH_SEC2)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_RX0_RX_PATH_SEC3)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_RX0_RX_PATH_SEC5)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_RX0_RX_PATH_SEC6)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_RX0_RX_PATH_SEC7)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_RX0_RX_PATH_MIX_SEC0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_RX0_RX_PATH_MIX_SEC1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_RX0_RX_PATH_DSMDEM_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_RX1_RX_PATH_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_RX1_RX_PATH_CFG0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_RX1_RX_PATH_CFG1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_RX1_RX_PATH_CFG2)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_RX1_RX_PATH_CFG3)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_RX1_RX_VOL_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_RX1_RX_PATH_MIX_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_RX1_RX_PATH_MIX_CFG)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_RX1_RX_VOL_MIX_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_RX1_RX_PATH_SEC0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_RX1_RX_PATH_SEC1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_RX1_RX_PATH_SEC2)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_RX1_RX_PATH_SEC3)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_RX1_RX_PATH_SEC5)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_RX1_RX_PATH_SEC6)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_RX1_RX_PATH_SEC7)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_RX1_RX_PATH_MIX_SEC0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_RX1_RX_PATH_MIX_SEC1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_RX1_RX_PATH_DSMDEM_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_BOOST0_BOOST_PATH_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_BOOST0_BOOST_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_BOOST0_BOOST_CFG1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_BOOST0_BOOST_CFG2)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_BOOST1_BOOST_PATH_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_BOOST1_BOOST_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_BOOST1_BOOST_CFG1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_BOOST1_BOOST_CFG2)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_COMPANDER0_CTL0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_COMPANDER0_CTL1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_COMPANDER0_CTL2)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_COMPANDER0_CTL3)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_COMPANDER0_CTL4)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_COMPANDER0_CTL5)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_COMPANDER0_CTL6)] = RD_REG, + [BOLERO_REG(BOLERO_CDC_WSA_COMPANDER0_CTL7)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_COMPANDER1_CTL0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_COMPANDER1_CTL1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_COMPANDER1_CTL2)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_COMPANDER1_CTL3)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_COMPANDER1_CTL4)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_COMPANDER1_CTL5)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_COMPANDER1_CTL6)] = RD_REG, + [BOLERO_REG(BOLERO_CDC_WSA_COMPANDER1_CTL7)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_SOFTCLIP0_CRC)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_SOFTCLIP0_SOFTCLIP_CTRL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_SOFTCLIP1_CRC)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_SOFTCLIP1_SOFTCLIP_CTRL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_EC_HQ0_EC_REF_HQ_PATH_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_EC_HQ0_EC_REF_HQ_CFG0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_EC_HQ1_EC_REF_HQ_PATH_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_EC_HQ1_EC_REF_HQ_CFG0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_SPLINE_ASRC0_CLK_RST_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_SPLINE_ASRC0_CTL0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_SPLINE_ASRC0_CTL1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_SPLINE_ASRC0_FIFO_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_SPLINE_ASRC0_STATUS_FMIN_CNTR_LSB)] = RD_REG, + [BOLERO_REG(BOLERO_CDC_WSA_SPLINE_ASRC0_STATUS_FMIN_CNTR_MSB)] = RD_REG, + [BOLERO_REG(BOLERO_CDC_WSA_SPLINE_ASRC0_STATUS_FMAX_CNTR_LSB)] = RD_REG, + [BOLERO_REG(BOLERO_CDC_WSA_SPLINE_ASRC0_STATUS_FMAX_CNTR_MSB)] = RD_REG, + [BOLERO_REG(BOLERO_CDC_WSA_SPLINE_ASRC0_STATUS_FIFO)] = RD_REG, + [BOLERO_REG(BOLERO_CDC_WSA_SPLINE_ASRC1_CLK_RST_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_SPLINE_ASRC1_CTL0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_SPLINE_ASRC1_CTL1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_SPLINE_ASRC1_FIFO_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_SPLINE_ASRC1_STATUS_FMIN_CNTR_LSB)] = RD_REG, + [BOLERO_REG(BOLERO_CDC_WSA_SPLINE_ASRC1_STATUS_FMIN_CNTR_MSB)] = RD_REG, + [BOLERO_REG(BOLERO_CDC_WSA_SPLINE_ASRC1_STATUS_FMAX_CNTR_LSB)] = RD_REG, + [BOLERO_REG(BOLERO_CDC_WSA_SPLINE_ASRC1_STATUS_FMAX_CNTR_MSB)] = RD_REG, + [BOLERO_REG(BOLERO_CDC_WSA_SPLINE_ASRC1_STATUS_FIFO)] = RD_REG, +}; + +u8 *bolero_reg_access[MAX_MACRO] = { + [TX_MACRO] = bolero_tx_reg_access, + [RX_MACRO] = bolero_rx_reg_access, + [WSA_MACRO] = bolero_wsa_reg_access, + [VA_MACRO] = bolero_va_reg_access, +}; diff --git a/audio-kernel/asoc/codecs/bolero/bolero-cdc-utils.c b/audio-kernel/asoc/codecs/bolero/bolero-cdc-utils.c new file mode 100644 index 000000000..b7f41420d --- /dev/null +++ b/audio-kernel/asoc/codecs/bolero/bolero-cdc-utils.c @@ -0,0 +1,186 @@ +/* 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 +#include +#include "bolero-cdc.h" +#include "internal.h" + +#define REG_BYTES 2 +#define VAL_BYTES 1 + +const u16 macro_id_base_offset[MAX_MACRO] = { + TX_START_OFFSET, + RX_START_OFFSET, + WSA_START_OFFSET, + VA_START_OFFSET, +}; + +int bolero_get_macro_id(bool va_no_dec_flag, u16 reg) +{ + if (reg >= TX_START_OFFSET + && reg <= TX_MAX_OFFSET) + return TX_MACRO; + if (reg >= RX_START_OFFSET + && reg <= RX_MAX_OFFSET) + return RX_MACRO; + if (reg >= WSA_START_OFFSET + && reg <= WSA_MAX_OFFSET) + return WSA_MACRO; + if (!va_no_dec_flag && + (reg >= VA_START_OFFSET && + reg <= VA_MAX_OFFSET)) + return VA_MACRO; + if (va_no_dec_flag && + (reg >= VA_START_OFFSET && + reg <= VA_TOP_MAX_OFFSET)) + return VA_MACRO; + + return -EINVAL; +} + +static int regmap_bus_read(void *context, const void *reg, size_t reg_size, + void *val, size_t val_size) +{ + struct device *dev = context; + struct bolero_priv *priv = dev_get_drvdata(dev); + u16 *reg_p; + u16 __reg; + int macro_id, i; + u8 temp = 0; + int ret = -EINVAL; + + if (!priv) { + dev_err(dev, "%s: priv is NULL\n", __func__); + return ret; + } + if (!reg || !val) { + dev_err(dev, "%s: reg or val is NULL\n", __func__); + return ret; + } + if (reg_size != REG_BYTES) { + dev_err(dev, "%s: register size %zd bytes, not supported\n", + __func__, reg_size); + return ret; + } + + reg_p = (u16 *)reg; + macro_id = bolero_get_macro_id(priv->va_without_decimation, + reg_p[0]); + if (macro_id < 0 || !priv->macros_supported[macro_id]) { + dev_err_ratelimited(dev, + "%s: Unsupported macro %d or reg 0x%x is invalid\n", + __func__, macro_id, reg_p[0]); + return ret; + } + mutex_lock(&priv->io_lock); + for (i = 0; i < val_size; i++) { + __reg = (reg_p[0] + i * 4) - macro_id_base_offset[macro_id]; + ret = priv->read_dev(priv, macro_id, __reg, &temp); + if (ret < 0) { + dev_err_ratelimited(dev, + "%s: Codec read failed (%d), reg: 0x%x, size:%zd\n", + __func__, ret, reg_p[0] + i * 4, val_size); + break; + } + ((u8 *)val)[i] = temp; + dev_dbg(dev, "%s: Read 0x%02x from reg 0x%x\n", + __func__, temp, reg_p[0] + i * 4); + } + mutex_unlock(&priv->io_lock); + + return ret; +} + +static int regmap_bus_gather_write(void *context, + const void *reg, size_t reg_size, + const void *val, size_t val_size) +{ + struct device *dev = context; + struct bolero_priv *priv = dev_get_drvdata(dev); + u16 *reg_p; + u16 __reg; + int macro_id, i; + int ret = -EINVAL; + + if (!priv) { + dev_err(dev, "%s: priv is NULL\n", __func__); + return ret; + } + if (!reg || !val) { + dev_err(dev, "%s: reg or val is NULL\n", __func__); + return ret; + } + if (reg_size != REG_BYTES) { + dev_err(dev, "%s: register size %zd bytes, not supported\n", + __func__, reg_size); + return ret; + } + + reg_p = (u16 *)reg; + macro_id = bolero_get_macro_id(priv->va_without_decimation, + reg_p[0]); + if (macro_id < 0 || !priv->macros_supported[macro_id]) { + dev_err_ratelimited(dev, + "%s: Unsupported macro-id %d or reg 0x%x is invalid\n", + __func__, macro_id, reg_p[0]); + return ret; + } + mutex_lock(&priv->io_lock); + for (i = 0; i < val_size; i++) { + __reg = (reg_p[0] + i * 4) - macro_id_base_offset[macro_id]; + ret = priv->write_dev(priv, macro_id, __reg, ((u8 *)val)[i]); + if (ret < 0) { + dev_err_ratelimited(dev, + "%s: Codec write failed (%d), reg:0x%x, size:%zd\n", + __func__, ret, reg_p[0] + i * 4, val_size); + break; + } + dev_dbg(dev, "Write %02x to reg 0x%x\n", ((u8 *)val)[i], + reg_p[0] + i * 4); + } + mutex_unlock(&priv->io_lock); + return ret; +} + +static int regmap_bus_write(void *context, const void *data, size_t count) +{ + struct device *dev = context; + struct bolero_priv *priv = dev_get_drvdata(dev); + + if (!priv) + return -EINVAL; + + if (count < REG_BYTES) { + dev_err(dev, "%s: count %zd bytes < %d, not supported\n", + __func__, count, REG_BYTES); + return -EINVAL; + } + + return regmap_bus_gather_write(context, data, REG_BYTES, + data + REG_BYTES, + count - REG_BYTES); +} + +static struct regmap_bus regmap_bus_config = { + .write = regmap_bus_write, + .gather_write = regmap_bus_gather_write, + .read = regmap_bus_read, + .reg_format_endian_default = REGMAP_ENDIAN_NATIVE, + .val_format_endian_default = REGMAP_ENDIAN_NATIVE, +}; + +struct regmap *bolero_regmap_init(struct device *dev, + const struct regmap_config *config) +{ + return devm_regmap_init(dev, ®map_bus_config, dev, config); +} diff --git a/audio-kernel/asoc/codecs/bolero/bolero-cdc.c b/audio-kernel/asoc/codecs/bolero/bolero-cdc.c new file mode 100644 index 000000000..b4950c01c --- /dev/null +++ b/audio-kernel/asoc/codecs/bolero/bolero-cdc.c @@ -0,0 +1,966 @@ +/* Copyright (c) 2018-2019, 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include "bolero-cdc.h" +#include "internal.h" + +#define BOLERO_VERSION_1_0 0x0001 +#define BOLERO_VERSION_1_1 0x0002 +#define BOLERO_VERSION_1_2 0x0003 +#define BOLERO_VERSION_ENTRY_SIZE 32 +#define BOLERO_CDC_STRING_LEN 80 + +static struct snd_soc_codec_driver bolero; + +/* MCLK_MUX table for all macros */ +static u16 bolero_mclk_mux_tbl[MAX_MACRO][MCLK_MUX_MAX] = { + {TX_MACRO, VA_MACRO}, + {TX_MACRO, RX_MACRO}, + {TX_MACRO, WSA_MACRO}, + {TX_MACRO, VA_MACRO}, +}; + +static void bolero_ahb_write_device(char __iomem *io_base, + u16 reg, u8 value) +{ + u32 temp = (u32)(value) & 0x000000FF; + + iowrite32(temp, io_base + reg); +} + +static void bolero_ahb_read_device(char __iomem *io_base, + u16 reg, u8 *value) +{ + u32 temp; + + temp = ioread32(io_base + reg); + *value = (u8)temp; +} + +static int __bolero_reg_read(struct bolero_priv *priv, + u16 macro_id, u16 reg, u8 *val) +{ + int ret = -EINVAL; + u16 current_mclk_mux_macro; + + mutex_lock(&priv->clk_lock); + if (!priv->dev_up) { + dev_dbg_ratelimited(priv->dev, + "%s: SSR in progress, exit\n", __func__); + goto err; + } + current_mclk_mux_macro = + priv->current_mclk_mux_macro[macro_id]; + if (!priv->macro_params[current_mclk_mux_macro].mclk_fn) { + dev_dbg_ratelimited(priv->dev, + "%s: mclk_fn not init for macro-id:%d, current_mclk_mux_macro:%d\n", + __func__, macro_id, current_mclk_mux_macro); + goto err; + } + ret = priv->macro_params[current_mclk_mux_macro].mclk_fn( + priv->macro_params[current_mclk_mux_macro].dev, true); + if (ret) { + dev_dbg_ratelimited(priv->dev, + "%s: clock enable failed for macro-id:%d, current_mclk_mux_macro:%d\n", + __func__, macro_id, current_mclk_mux_macro); + goto err; + } + bolero_ahb_read_device( + priv->macro_params[macro_id].io_base, reg, val); + priv->macro_params[current_mclk_mux_macro].mclk_fn( + priv->macro_params[current_mclk_mux_macro].dev, false); +err: + mutex_unlock(&priv->clk_lock); + return ret; +} + +static int __bolero_reg_write(struct bolero_priv *priv, + u16 macro_id, u16 reg, u8 val) +{ + int ret = -EINVAL; + u16 current_mclk_mux_macro; + + mutex_lock(&priv->clk_lock); + if (!priv->dev_up) { + dev_dbg_ratelimited(priv->dev, + "%s: SSR in progress, exit\n", __func__); + goto err; + } + current_mclk_mux_macro = + priv->current_mclk_mux_macro[macro_id]; + if (!priv->macro_params[current_mclk_mux_macro].mclk_fn) { + dev_dbg_ratelimited(priv->dev, + "%s: mclk_fn not init for macro-id:%d, current_mclk_mux_macro:%d\n", + __func__, macro_id, current_mclk_mux_macro); + goto err; + } + ret = priv->macro_params[current_mclk_mux_macro].mclk_fn( + priv->macro_params[current_mclk_mux_macro].dev, true); + if (ret) { + dev_dbg_ratelimited(priv->dev, + "%s: clock enable failed for macro-id:%d, current_mclk_mux_macro:%d\n", + __func__, macro_id, current_mclk_mux_macro); + goto err; + } + bolero_ahb_write_device( + priv->macro_params[macro_id].io_base, reg, val); + priv->macro_params[current_mclk_mux_macro].mclk_fn( + priv->macro_params[current_mclk_mux_macro].dev, false); +err: + mutex_unlock(&priv->clk_lock); + return ret; +} + +static int bolero_cdc_update_wcd_event(void *handle, u16 event, u32 data) +{ + struct bolero_priv *priv = (struct bolero_priv *)handle; + + if (!priv) { + pr_err("%s:Invalid bolero priv handle\n", __func__); + return -EINVAL; + } + + switch (event) { + case WCD_BOLERO_EVT_RX_MUTE: + if (priv->macro_params[RX_MACRO].event_handler) + priv->macro_params[RX_MACRO].event_handler(priv->codec, + BOLERO_MACRO_EVT_RX_MUTE, data); + break; + case WCD_BOLERO_EVT_IMPED_TRUE: + if (priv->macro_params[RX_MACRO].event_handler) + priv->macro_params[RX_MACRO].event_handler(priv->codec, + BOLERO_MACRO_EVT_IMPED_TRUE, data); + break; + case WCD_BOLERO_EVT_IMPED_FALSE: + if (priv->macro_params[RX_MACRO].event_handler) + priv->macro_params[RX_MACRO].event_handler(priv->codec, + BOLERO_MACRO_EVT_IMPED_FALSE, data); + break; + default: + dev_err(priv->dev, "%s: Invalid event %d trigger from wcd\n", + __func__, event); + return -EINVAL; + } + return 0; +} + +static int bolero_cdc_register_notifier(void *handle, + struct notifier_block *nblock, + bool enable) +{ + struct bolero_priv *priv = (struct bolero_priv *)handle; + + if (!priv) { + pr_err("%s: bolero priv is null\n", __func__); + return -EINVAL; + } + if (enable) + return blocking_notifier_chain_register(&priv->notifier, + nblock); + + return blocking_notifier_chain_unregister(&priv->notifier, + nblock); +} + +static void bolero_cdc_notifier_call(struct bolero_priv *priv, + u32 data) +{ + dev_dbg(priv->dev, "%s: notifier call, data:%d\n", __func__, data); + blocking_notifier_call_chain(&priv->notifier, + data, (void *)priv->wcd_dev); +} + +static bool bolero_is_valid_macro_dev(struct device *dev) +{ + if (of_device_is_compatible(dev->parent->of_node, "qcom,bolero-codec")) + return true; + + return false; +} + +static bool bolero_is_valid_codec_dev(struct device *dev) +{ + if (of_device_is_compatible(dev->of_node, "qcom,bolero-codec")) + return true; + + return false; +} + +/** + * bolero_clear_amic_tx_hold - clears AMIC register on analog codec + * + * @dev: bolero device ptr. + * + */ +void bolero_clear_amic_tx_hold(struct device *dev, u16 adc_n) +{ + struct bolero_priv *priv; + u16 event; + u16 amic = 0; + + if (!dev) { + pr_err("%s: dev is null\n", __func__); + return; + } + + if (!bolero_is_valid_codec_dev(dev)) { + pr_err("%s: invalid codec\n", __func__); + return; + } + priv = dev_get_drvdata(dev); + if (!priv) { + dev_err(dev, "%s: priv is null\n", __func__); + return; + } + event = BOLERO_WCD_EVT_TX_CH_HOLD_CLEAR; + if (adc_n == BOLERO_ADC0) + amic = 0x1; + else if (adc_n == BOLERO_ADC2) + amic = 0x2; + else if (adc_n == BOLERO_ADC3) + amic = 0x3; + else + return; + + bolero_cdc_notifier_call(priv, (amic << 0x10 | event)); +} +EXPORT_SYMBOL(bolero_clear_amic_tx_hold); + +/** + * bolero_get_device_ptr - Get child or macro device ptr + * + * @dev: bolero device ptr. + * @macro_id: ID of macro calling this API. + * + * Returns dev ptr on success or NULL on error. + */ +struct device *bolero_get_device_ptr(struct device *dev, u16 macro_id) +{ + struct bolero_priv *priv; + + if (!dev) { + pr_err("%s: dev is null\n", __func__); + return NULL; + } + + if (!bolero_is_valid_codec_dev(dev)) { + pr_err("%s: invalid codec\n", __func__); + return NULL; + } + priv = dev_get_drvdata(dev); + if (!priv || (macro_id >= MAX_MACRO)) { + dev_err(dev, "%s: priv is null or invalid macro\n", __func__); + return NULL; + } + + return priv->macro_params[macro_id].dev; +} +EXPORT_SYMBOL(bolero_get_device_ptr); + +static int bolero_copy_dais_from_macro(struct bolero_priv *priv) +{ + struct snd_soc_dai_driver *dai_ptr; + u16 macro_idx; + + /* memcpy into bolero_dais all macro dais */ + if (!priv->bolero_dais) + priv->bolero_dais = devm_kzalloc(priv->dev, + priv->num_dais * + sizeof( + struct snd_soc_dai_driver), + GFP_KERNEL); + if (!priv->bolero_dais) + return -ENOMEM; + + dai_ptr = priv->bolero_dais; + + for (macro_idx = START_MACRO; macro_idx < MAX_MACRO; macro_idx++) { + if (priv->macro_params[macro_idx].dai_ptr) { + memcpy(dai_ptr, + priv->macro_params[macro_idx].dai_ptr, + priv->macro_params[macro_idx].num_dais * + sizeof(struct snd_soc_dai_driver)); + dai_ptr += priv->macro_params[macro_idx].num_dais; + } + } + return 0; +} + +/** + * bolero_register_macro - Registers macro to bolero + * + * @dev: macro device ptr. + * @macro_id: ID of macro calling this API. + * @ops: macro params to register. + * + * Returns 0 on success or -EINVAL on error. + */ +int bolero_register_macro(struct device *dev, u16 macro_id, + struct macro_ops *ops) +{ + struct bolero_priv *priv; + int ret = -EINVAL; + + if (!dev || !ops) { + pr_err("%s: dev or ops is null\n", __func__); + return -EINVAL; + } + if (!bolero_is_valid_macro_dev(dev)) { + dev_err(dev, "%s: child device for macro:%d not added yet\n", + __func__, macro_id); + return -EINVAL; + } + priv = dev_get_drvdata(dev->parent); + if (!priv || (macro_id >= MAX_MACRO)) { + dev_err(dev, "%s: priv is null or invalid macro\n", __func__); + return -EINVAL; + } + + priv->macro_params[macro_id].init = ops->init; + priv->macro_params[macro_id].exit = ops->exit; + priv->macro_params[macro_id].io_base = ops->io_base; + priv->macro_params[macro_id].num_dais = ops->num_dais; + priv->macro_params[macro_id].dai_ptr = ops->dai_ptr; + priv->macro_params[macro_id].mclk_fn = ops->mclk_fn; + priv->macro_params[macro_id].event_handler = ops->event_handler; + priv->macro_params[macro_id].dev = dev; + priv->current_mclk_mux_macro[macro_id] = + bolero_mclk_mux_tbl[macro_id][MCLK_MUX0]; + if (macro_id == TX_MACRO) + priv->macro_params[macro_id].reg_wake_irq = ops->reg_wake_irq; + + priv->num_dais += ops->num_dais; + priv->num_macros_registered++; + priv->macros_supported[macro_id] = true; + + if (priv->num_macros_registered == priv->num_macros) { + ret = bolero_copy_dais_from_macro(priv); + if (ret < 0) { + dev_err(dev, "%s: copy_dais failed\n", __func__); + return ret; + } + if (priv->macros_supported[TX_MACRO] == false) { + bolero_mclk_mux_tbl[WSA_MACRO][MCLK_MUX0] = WSA_MACRO; + priv->current_mclk_mux_macro[WSA_MACRO] = WSA_MACRO; + bolero_mclk_mux_tbl[VA_MACRO][MCLK_MUX0] = VA_MACRO; + priv->current_mclk_mux_macro[VA_MACRO] = VA_MACRO; + } + ret = snd_soc_register_codec(dev->parent, &bolero, + priv->bolero_dais, priv->num_dais); + if (ret < 0) { + dev_err(dev, "%s: register codec failed\n", __func__); + return ret; + } + } + return 0; +} +EXPORT_SYMBOL(bolero_register_macro); + +/** + * bolero_unregister_macro - De-Register macro from bolero + * + * @dev: macro device ptr. + * @macro_id: ID of macro calling this API. + * + */ +void bolero_unregister_macro(struct device *dev, u16 macro_id) +{ + struct bolero_priv *priv; + + if (!dev) { + pr_err("%s: dev is null\n", __func__); + return; + } + if (!bolero_is_valid_macro_dev(dev)) { + dev_err(dev, "%s: macro:%d not in valid registered macro-list\n", + __func__, macro_id); + return; + } + priv = dev_get_drvdata(dev->parent); + if (!priv || (macro_id >= MAX_MACRO)) { + dev_err(dev, "%s: priv is null or invalid macro\n", __func__); + return; + } + + priv->macro_params[macro_id].init = NULL; + priv->macro_params[macro_id].num_dais = 0; + priv->macro_params[macro_id].dai_ptr = NULL; + priv->macro_params[macro_id].mclk_fn = NULL; + priv->macro_params[macro_id].event_handler = NULL; + priv->macro_params[macro_id].dev = NULL; + if (macro_id == TX_MACRO) + priv->macro_params[macro_id].reg_wake_irq = NULL; + + priv->num_dais -= priv->macro_params[macro_id].num_dais; + priv->num_macros_registered--; + + /* UNREGISTER CODEC HERE */ + if (priv->num_macros - 1 == priv->num_macros_registered) + snd_soc_unregister_codec(dev->parent); +} +EXPORT_SYMBOL(bolero_unregister_macro); + +/** + * bolero_request_clock - request for clock enable/disable + * + * @dev: macro device ptr. + * @macro_id: ID of macro calling this API. + * @mclk_mux_id: MCLK_MUX ID. + * @enable: enable or disable clock flag + * + * Returns 0 on success or -EINVAL on error. + */ +int bolero_request_clock(struct device *dev, u16 macro_id, + enum mclk_mux mclk_mux_id, + bool enable) +{ + struct bolero_priv *priv; + u16 mclk_mux0_macro, mclk_mux1_macro; + int ret = 0, ret1 = 0; + + if (!dev) { + pr_err("%s: dev is null\n", __func__); + return -EINVAL; + } + if (!bolero_is_valid_macro_dev(dev)) { + dev_err(dev, "%s: macro:%d not in valid registered macro-list\n", + __func__, macro_id); + return -EINVAL; + } + priv = dev_get_drvdata(dev->parent); + if (!priv || (macro_id >= MAX_MACRO)) { + dev_err(dev, "%s: priv is null or invalid macro\n", __func__); + return -EINVAL; + } + + mutex_lock(&priv->clk_lock); + + mclk_mux0_macro = bolero_mclk_mux_tbl[macro_id][MCLK_MUX0]; + switch (mclk_mux_id) { + case MCLK_MUX0: + ret = priv->macro_params[mclk_mux0_macro].mclk_fn( + priv->macro_params[mclk_mux0_macro].dev, enable); + if (ret < 0) { + dev_err_ratelimited(dev, + "%s: MCLK_MUX0 %s failed for macro:%d, mclk_mux0_macro:%d\n", + __func__, + enable ? "enable" : "disable", + macro_id, mclk_mux0_macro); + goto err; + } + break; + case MCLK_MUX1: + mclk_mux1_macro = bolero_mclk_mux_tbl[macro_id][MCLK_MUX1]; + ret = priv->macro_params[mclk_mux0_macro].mclk_fn( + priv->macro_params[mclk_mux0_macro].dev, + true); + if (ret < 0) { + dev_err_ratelimited(dev, + "%s: MCLK_MUX0 en failed for macro:%d mclk_mux0_macro:%d\n", + __func__, macro_id, mclk_mux0_macro); + /* + * for disable case, need to proceed still for mclk_mux1 + * counter to decrement + */ + if (enable) + goto err; + } + /* + * need different return value as ret variable + * is used to track mclk_mux0 enable success or fail + */ + ret1 = priv->macro_params[mclk_mux1_macro].mclk_fn( + priv->macro_params[mclk_mux1_macro].dev, enable); + if (ret1 < 0) + dev_err_ratelimited(dev, + "%s: MCLK_MUX1 %s failed for macro:%d, mclk_mux1_macro:%d\n", + __func__, + enable ? "enable" : "disable", + macro_id, mclk_mux1_macro); + /* disable mclk_mux0 only if ret is success(0) */ + if (!ret) + priv->macro_params[mclk_mux0_macro].mclk_fn( + priv->macro_params[mclk_mux0_macro].dev, + false); + if (enable && ret1) + goto err; + break; + case MCLK_MUX_MAX: + default: + dev_err(dev, "%s: invalid mclk_mux_id: %d\n", + __func__, mclk_mux_id); + ret = -EINVAL; + goto err; + } + if (enable) + priv->current_mclk_mux_macro[macro_id] = + bolero_mclk_mux_tbl[macro_id][mclk_mux_id]; + else + priv->current_mclk_mux_macro[macro_id] = + bolero_mclk_mux_tbl[macro_id][MCLK_MUX0]; +err: + mutex_unlock(&priv->clk_lock); + return ret; +} +EXPORT_SYMBOL(bolero_request_clock); + +static ssize_t bolero_version_read(struct snd_info_entry *entry, + void *file_private_data, + struct file *file, + char __user *buf, size_t count, + loff_t pos) +{ + struct bolero_priv *priv; + char buffer[BOLERO_VERSION_ENTRY_SIZE]; + int len = 0; + + priv = (struct bolero_priv *) entry->private_data; + if (!priv) { + pr_err("%s: bolero priv is null\n", __func__); + return -EINVAL; + } + + switch (priv->version) { + case BOLERO_VERSION_1_0: + len = snprintf(buffer, sizeof(buffer), "BOLERO_1_0\n"); + break; + case BOLERO_VERSION_1_1: + len = snprintf(buffer, sizeof(buffer), "BOLERO_1_1\n"); + break; + case BOLERO_VERSION_1_2: + len = snprintf(buffer, sizeof(buffer), "BOLERO_1_2\n"); + break; + default: + len = snprintf(buffer, sizeof(buffer), "VER_UNDEFINED\n"); + } + + return simple_read_from_buffer(buf, count, &pos, buffer, len); +} + +static int bolero_ssr_enable(struct device *dev, void *data) +{ + struct bolero_priv *priv = data; + int macro_idx; + + if (priv->initial_boot) { + priv->initial_boot = false; + return 0; + } + + if (priv->macro_params[VA_MACRO].event_handler) + priv->macro_params[VA_MACRO].event_handler(priv->codec, + BOLERO_MACRO_EVT_WAIT_VA_CLK_RESET, 0x0); + + regcache_cache_only(priv->regmap, false); + /* call ssr event for supported macros */ + for (macro_idx = START_MACRO; macro_idx < MAX_MACRO; macro_idx++) { + if (!priv->macro_params[macro_idx].event_handler) + continue; + priv->macro_params[macro_idx].event_handler(priv->codec, + BOLERO_MACRO_EVT_SSR_UP, 0x0); + } + mutex_lock(&priv->clk_lock); + priv->dev_up = true; + mutex_unlock(&priv->clk_lock); + bolero_cdc_notifier_call(priv, BOLERO_WCD_EVT_SSR_UP); + return 0; +} + +static void bolero_ssr_disable(struct device *dev, void *data) +{ + struct bolero_priv *priv = data; + int macro_idx; + + bolero_cdc_notifier_call(priv, BOLERO_WCD_EVT_PA_OFF_PRE_SSR); + regcache_cache_only(priv->regmap, true); + + mutex_lock(&priv->clk_lock); + priv->dev_up = false; + mutex_unlock(&priv->clk_lock); + /* call ssr event for supported macros */ + for (macro_idx = START_MACRO; macro_idx < MAX_MACRO; macro_idx++) { + if (!priv->macro_params[macro_idx].event_handler) + continue; + priv->macro_params[macro_idx].event_handler(priv->codec, + BOLERO_MACRO_EVT_SSR_DOWN, 0x0); + } + bolero_cdc_notifier_call(priv, BOLERO_WCD_EVT_SSR_DOWN); +} + +static struct snd_info_entry_ops bolero_info_ops = { + .read = bolero_version_read, +}; + +static const struct snd_event_ops bolero_ssr_ops = { + .enable = bolero_ssr_enable, + .disable = bolero_ssr_disable, +}; + +/* + * bolero_info_create_codec_entry - creates bolero module + * @codec_root: The parent directory + * @codec: Codec instance + * + * Creates bolero module and version entry under the given + * parent directory. + * + * Return: 0 on success or negative error code on failure. + */ +int bolero_info_create_codec_entry(struct snd_info_entry *codec_root, + struct snd_soc_codec *codec) +{ + struct snd_info_entry *version_entry; + struct bolero_priv *priv; + struct snd_soc_card *card; + + if (!codec_root || !codec) + return -EINVAL; + + priv = snd_soc_codec_get_drvdata(codec); + if (priv->entry) { + dev_dbg(priv->dev, + "%s:bolero module already created\n", __func__); + return 0; + } + card = codec->component.card; + priv->entry = snd_info_create_subdir(codec_root->module, + "bolero", codec_root); + if (!priv->entry) { + dev_dbg(codec->dev, "%s: failed to create bolero entry\n", + __func__); + return -ENOMEM; + } + version_entry = snd_info_create_card_entry(card->snd_card, + "version", + priv->entry); + if (!version_entry) { + dev_err(codec->dev, "%s: failed to create bolero version entry\n", + __func__); + return -ENOMEM; + } + + version_entry->private_data = priv; + version_entry->size = BOLERO_VERSION_ENTRY_SIZE; + version_entry->content = SNDRV_INFO_CONTENT_DATA; + version_entry->c.ops = &bolero_info_ops; + + if (snd_info_register(version_entry) < 0) { + snd_info_free_entry(version_entry); + return -ENOMEM; + } + priv->version_entry = version_entry; + + return 0; +} +EXPORT_SYMBOL(bolero_info_create_codec_entry); + +/** + * bolero_register_wake_irq - Register wake irq of Tx macro + * + * @codec: codec ptr. + * @ipc_wakeup: bool to identify ipc_wakeup to be used or HW interrupt line. + * + * Return: 0 on success or negative error code on failure. + */ +int bolero_register_wake_irq(struct snd_soc_codec *codec, u32 ipc_wakeup) +{ + struct bolero_priv *priv = NULL; + + if (!codec) + return -EINVAL; + + priv = snd_soc_codec_get_drvdata(codec); + if (!priv) + return -EINVAL; + + if (!bolero_is_valid_codec_dev(priv->dev)) { + dev_err(codec->dev, "%s: invalid codec\n", __func__); + return -EINVAL; + } + + if (priv->macro_params[TX_MACRO].reg_wake_irq) + priv->macro_params[TX_MACRO].reg_wake_irq(codec, ipc_wakeup); + + return 0; +} +EXPORT_SYMBOL(bolero_register_wake_irq); + +static int bolero_soc_codec_probe(struct snd_soc_codec *codec) +{ + struct bolero_priv *priv = dev_get_drvdata(codec->dev); + int macro_idx, ret = 0; + + /* call init for supported macros */ + for (macro_idx = START_MACRO; macro_idx < MAX_MACRO; macro_idx++) { + if (priv->macro_params[macro_idx].init) { + ret = priv->macro_params[macro_idx].init(codec); + if (ret < 0) { + dev_err(codec->dev, + "%s: init for macro %d failed\n", + __func__, macro_idx); + goto err; + } + } + } + priv->codec = codec; + /* + * In order for the ADIE RTC to differentiate between targets + * version info is used. + * Assign 1.0 for target with only one macro + * Assign 1.1 for target with two macros + * Assign 1.2 for target with more than two macros + */ + if (priv->num_macros_registered == 1) + priv->version = BOLERO_VERSION_1_0; + else if (priv->num_macros_registered == 2) + priv->version = BOLERO_VERSION_1_1; + else if (priv->num_macros_registered > 2) + priv->version = BOLERO_VERSION_1_2; + + ret = snd_event_client_register(priv->dev, &bolero_ssr_ops, priv); + if (!ret) { + snd_event_notify(priv->dev, SND_EVENT_UP); + } else { + dev_err(codec->dev, + "%s: Registration with SND event FWK failed ret = %d\n", + __func__, ret); + goto err; + } + + dev_dbg(codec->dev, "%s: bolero soc codec probe success\n", __func__); +err: + return ret; +} + +static int bolero_soc_codec_remove(struct snd_soc_codec *codec) +{ + struct bolero_priv *priv = dev_get_drvdata(codec->dev); + int macro_idx; + + snd_event_client_deregister(priv->dev); + /* call exit for supported macros */ + for (macro_idx = START_MACRO; macro_idx < MAX_MACRO; macro_idx++) + if (priv->macro_params[macro_idx].exit) + priv->macro_params[macro_idx].exit(codec); + + return 0; +} + +static struct regmap *bolero_get_regmap(struct device *dev) +{ + struct bolero_priv *priv = dev_get_drvdata(dev); + + return priv->regmap; +} + +static struct snd_soc_codec_driver bolero = { + .probe = bolero_soc_codec_probe, + .remove = bolero_soc_codec_remove, + .get_regmap = bolero_get_regmap, +}; + +static void bolero_add_child_devices(struct work_struct *work) +{ + struct bolero_priv *priv; + bool wcd937x_node = false; + struct platform_device *pdev; + struct device_node *node; + int ret = 0, count = 0; + struct wcd_ctrl_platform_data *platdata = NULL; + char plat_dev_name[BOLERO_CDC_STRING_LEN] = ""; + + priv = container_of(work, struct bolero_priv, + bolero_add_child_devices_work); + if (!priv) { + pr_err("%s: Memory for bolero priv does not exist\n", + __func__); + return; + } + if (!priv->dev || !priv->dev->of_node) { + dev_err(priv->dev, "%s: DT node for bolero does not exist\n", + __func__); + return; + } + + platdata = &priv->plat_data; + priv->child_count = 0; + + for_each_available_child_of_node(priv->dev->of_node, node) { + wcd937x_node = false; + if (strnstr(node->name, "wcd937x", strlen("wcd937x")) != NULL) + wcd937x_node = true; + + strlcpy(plat_dev_name, node->name, + (BOLERO_CDC_STRING_LEN - 1)); + + pdev = platform_device_alloc(plat_dev_name, -1); + if (!pdev) { + dev_err(priv->dev, "%s: pdev memory alloc failed\n", + __func__); + ret = -ENOMEM; + goto err; + } + pdev->dev.parent = priv->dev; + pdev->dev.of_node = node; + + if (wcd937x_node) { + priv->dev->platform_data = platdata; + priv->wcd_dev = &pdev->dev; + } + + ret = platform_device_add(pdev); + if (ret) { + dev_err(&pdev->dev, + "%s: Cannot add platform device\n", + __func__); + platform_device_put(pdev); + goto fail_pdev_add; + } + + if (priv->child_count < BOLERO_CDC_CHILD_DEVICES_MAX) + priv->pdev_child_devices[priv->child_count++] = pdev; + else + goto err; + } + return; +fail_pdev_add: + for (count = 0; count < priv->child_count; count++) + platform_device_put(priv->pdev_child_devices[count]); +err: + return; +} + +static int bolero_probe(struct platform_device *pdev) +{ + struct bolero_priv *priv; + u32 num_macros = 0; + int ret; + u32 slew_reg1 = 0, slew_reg2 = 0; + char __iomem *slew_io_base1 = NULL, *slew_io_base2 = NULL; + + priv = devm_kzalloc(&pdev->dev, sizeof(struct bolero_priv), + GFP_KERNEL); + if (!priv) + return -ENOMEM; + ret = of_property_read_u32(pdev->dev.of_node, "qcom,num-macros", + &num_macros); + if (ret) { + dev_err(&pdev->dev, + "%s:num-macros property not found\n", + __func__); + return ret; + } + priv->num_macros = num_macros; + if (priv->num_macros > MAX_MACRO) { + dev_err(&pdev->dev, + "%s:num_macros(%d) > MAX_MACRO(%d) than supported\n", + __func__, priv->num_macros, MAX_MACRO); + return -EINVAL; + } + priv->va_without_decimation = of_property_read_bool(pdev->dev.of_node, + "qcom,va-without-decimation"); + if (priv->va_without_decimation) + bolero_reg_access[VA_MACRO] = bolero_va_top_reg_access; + + priv->dev = &pdev->dev; + priv->dev_up = true; + priv->initial_boot = true; + priv->regmap = bolero_regmap_init(priv->dev, + &bolero_regmap_config); + if (IS_ERR_OR_NULL((void *)(priv->regmap))) { + dev_err(&pdev->dev, "%s:regmap init failed\n", __func__); + return -EINVAL; + } + priv->read_dev = __bolero_reg_read; + priv->write_dev = __bolero_reg_write; + + priv->plat_data.handle = (void *) priv; + priv->plat_data.update_wcd_event = bolero_cdc_update_wcd_event; + priv->plat_data.register_notifier = bolero_cdc_register_notifier; + + dev_set_drvdata(&pdev->dev, priv); + mutex_init(&priv->io_lock); + mutex_init(&priv->clk_lock); + + ret = of_property_read_u32(pdev->dev.of_node, "slew_rate_reg1", + &slew_reg1); + ret |= of_property_read_u32(pdev->dev.of_node, "slew_rate_reg2", + &slew_reg2); + + if (!ret) { + slew_io_base1 = devm_ioremap(&pdev->dev, slew_reg1, 0x4); + if (!slew_io_base1) { + dev_err(&pdev->dev, "%s: ioremap failed for slew reg 1\n", + __func__); + return -ENOMEM; + } + + slew_io_base2 = devm_ioremap(&pdev->dev, slew_reg2, 0x4); + if (!slew_io_base2) { + dev_err(&pdev->dev, "%s: ioremap failed for slew reg 2\n", + __func__); + return -ENOMEM; + } + + /* update slew rate for tx/rx swr interface */ + iowrite32(0x3333, slew_io_base1); + iowrite32(0xF, slew_io_base2); + } + INIT_WORK(&priv->bolero_add_child_devices_work, + bolero_add_child_devices); + schedule_work(&priv->bolero_add_child_devices_work); + + return 0; +} + +static int bolero_remove(struct platform_device *pdev) +{ + struct bolero_priv *priv = dev_get_drvdata(&pdev->dev); + + if (!priv) + return -EINVAL; + + of_platform_depopulate(&pdev->dev); + mutex_destroy(&priv->io_lock); + mutex_destroy(&priv->clk_lock); + return 0; +} + +static const struct of_device_id bolero_dt_match[] = { + {.compatible = "qcom,bolero-codec"}, + {} +}; +MODULE_DEVICE_TABLE(of, bolero_dt_match); + +static struct platform_driver bolero_drv = { + .driver = { + .name = "bolero-codec", + .owner = THIS_MODULE, + .of_match_table = bolero_dt_match, + }, + .probe = bolero_probe, + .remove = bolero_remove, +}; + +module_platform_driver(bolero_drv); + +MODULE_DESCRIPTION("Bolero driver"); +MODULE_LICENSE("GPL v2"); diff --git a/audio-kernel/asoc/codecs/bolero/bolero-cdc.h b/audio-kernel/asoc/codecs/bolero/bolero-cdc.h new file mode 100644 index 000000000..36d17f5ab --- /dev/null +++ b/audio-kernel/asoc/codecs/bolero/bolero-cdc.h @@ -0,0 +1,120 @@ +/* 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 BOLERO_CDC_H +#define BOLERO_CDC_H + +#include +#include + +enum { + START_MACRO, + TX_MACRO = START_MACRO, + RX_MACRO, + WSA_MACRO, + VA_MACRO, + MAX_MACRO +}; + +enum mclk_mux { + MCLK_MUX0, + MCLK_MUX1, + MCLK_MUX_MAX +}; + +enum { + BOLERO_ADC0 = 1, + BOLERO_ADC1, + BOLERO_ADC2, + BOLERO_ADC3, + BOLERO_ADC_MAX +}; + +enum { + BOLERO_MACRO_EVT_RX_MUTE = 1, /* for RX mute/unmute */ + BOLERO_MACRO_EVT_IMPED_TRUE, /* for imped true */ + BOLERO_MACRO_EVT_IMPED_FALSE, /* for imped false */ + BOLERO_MACRO_EVT_SSR_DOWN, + BOLERO_MACRO_EVT_SSR_UP, + BOLERO_MACRO_EVT_WAIT_VA_CLK_RESET, + BOLERO_MACRO_EVT_REG_WAKE_IRQ +}; + +struct macro_ops { + int (*init)(struct snd_soc_codec *codec); + int (*exit)(struct snd_soc_codec *codec); + u16 num_dais; + struct device *dev; + struct snd_soc_dai_driver *dai_ptr; + int (*mclk_fn)(struct device *dev, bool enable); + int (*event_handler)(struct snd_soc_codec *codec, u16 event, + u32 data); + int (*reg_wake_irq)(struct snd_soc_codec *codec, u32 data); + char __iomem *io_base; +}; + +#if IS_ENABLED(CONFIG_SND_SOC_BOLERO) +int bolero_register_macro(struct device *dev, u16 macro_id, + struct macro_ops *ops); +void bolero_unregister_macro(struct device *dev, u16 macro_id); +struct device *bolero_get_device_ptr(struct device *dev, u16 macro_id); +int bolero_request_clock(struct device *dev, u16 macro_id, + enum mclk_mux mclk_mux_id, + bool enable); +int bolero_info_create_codec_entry( + struct snd_info_entry *codec_root, + struct snd_soc_codec *codec); +int bolero_register_wake_irq(struct snd_soc_codec *codec, u32 data); +void bolero_clear_amic_tx_hold(struct device *dev, u16 adc_n); +#else +static inline int bolero_register_macro(struct device *dev, + u16 macro_id, + struct macro_ops *ops) +{ + return 0; +} + +static inline void bolero_unregister_macro(struct device *dev, u16 macro_id) +{ +} + +static inline struct device *bolero_get_device_ptr(struct device *dev, + u16 macro_id) +{ + return NULL; +} + +static inline int bolero_request_clock(struct device *dev, u16 macro_id, + enum mclk_mux mclk_mux_id, + bool enable) +{ + return 0; +} + +static int bolero_info_create_codec_entry( + struct snd_info_entry *codec_root, + struct snd_soc_codec *codec) +{ + return 0; +} + +static inline void bolero_clear_amic_tx_hold(struct device *dev, u16 adc_n) +{ +} + +static inline int bolero_register_wake_irq(struct snd_soc_codec *codec, + u32 data) +{ + return 0; +} +#endif /* CONFIG_SND_SOC_BOLERO */ +#endif /* BOLERO_CDC_H */ diff --git a/audio-kernel/asoc/codecs/bolero/internal.h b/audio-kernel/asoc/codecs/bolero/internal.h new file mode 100644 index 000000000..89d3cadb7 --- /dev/null +++ b/audio-kernel/asoc/codecs/bolero/internal.h @@ -0,0 +1,94 @@ +/* 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 _BOLERO_INTERNAL_H +#define _BOLERO_INTERNAL_H + +#include "bolero-cdc-registers.h" + +#define BOLERO_CDC_CHILD_DEVICES_MAX 5 + +/* from bolero to WCD events */ +enum { + BOLERO_WCD_EVT_TX_CH_HOLD_CLEAR = 1, + BOLERO_WCD_EVT_PA_OFF_PRE_SSR, + BOLERO_WCD_EVT_SSR_DOWN, + BOLERO_WCD_EVT_SSR_UP, +}; + +enum { + REG_NO_ACCESS, + RD_REG, + WR_REG, + RD_WR_REG +}; + +/* from WCD to bolero events */ +enum { + WCD_BOLERO_EVT_RX_MUTE = 1, /* for RX mute/unmute */ + WCD_BOLERO_EVT_IMPED_TRUE, /* for imped true */ + WCD_BOLERO_EVT_IMPED_FALSE, /* for imped false */ +}; + +struct wcd_ctrl_platform_data { + void *handle; + int (*update_wcd_event)(void *handle, u16 event, u32 data); + int (*register_notifier)(void *handle, + struct notifier_block *nblock, + bool enable); +}; + +struct bolero_priv { + struct device *dev; + struct snd_soc_codec *codec; + struct regmap *regmap; + struct mutex io_lock; + struct mutex clk_lock; + bool va_without_decimation; + bool macros_supported[MAX_MACRO]; + bool dev_up; + bool initial_boot; + struct macro_ops macro_params[MAX_MACRO]; + struct snd_soc_dai_driver *bolero_dais; + u16 num_dais; + u16 num_macros_registered; + u16 num_macros; + u16 current_mclk_mux_macro[MAX_MACRO]; + struct work_struct bolero_add_child_devices_work; + u32 version; + + /* Entry for version info */ + struct snd_info_entry *entry; + struct snd_info_entry *version_entry; + + int (*read_dev)(struct bolero_priv *priv, + u16 macro_id, u16 reg, u8 *val); + int (*write_dev)(struct bolero_priv *priv, + u16 macro_id, u16 reg, u8 val); + struct platform_device *pdev_child_devices + [BOLERO_CDC_CHILD_DEVICES_MAX]; + u16 child_count; + struct wcd_ctrl_platform_data plat_data; + struct device *wcd_dev; + struct blocking_notifier_head notifier; +}; + +struct regmap *bolero_regmap_init(struct device *dev, + const struct regmap_config *config); +int bolero_get_macro_id(bool va_no_dec_flag, u16 reg); + +extern const struct regmap_config bolero_regmap_config; +extern u8 *bolero_reg_access[MAX_MACRO]; +extern u8 bolero_va_top_reg_access[BOLERO_CDC_VA_MACRO_TOP_MAX]; +extern const u16 macro_id_base_offset[MAX_MACRO]; + +#endif diff --git a/audio-kernel/asoc/codecs/bolero/rx-macro.c b/audio-kernel/asoc/codecs/bolero/rx-macro.c new file mode 100644 index 000000000..bc146fb87 --- /dev/null +++ b/audio-kernel/asoc/codecs/bolero/rx-macro.c @@ -0,0 +1,3612 @@ +/* Copyright (c) 2018-2019, 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "bolero-cdc.h" +#include "bolero-cdc-registers.h" +#include "../msm-cdc-pinctrl.h" + +#define RX_MACRO_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\ + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |\ + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000 |\ + SNDRV_PCM_RATE_384000) +/* Fractional Rates */ +#define RX_MACRO_FRAC_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_88200 |\ + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800) + +#define RX_MACRO_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ + SNDRV_PCM_FMTBIT_S24_LE |\ + SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE) + +#define RX_MACRO_ECHO_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\ + SNDRV_PCM_RATE_48000) +#define RX_MACRO_ECHO_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ + SNDRV_PCM_FMTBIT_S24_LE |\ + SNDRV_PCM_FMTBIT_S24_3LE) + +#define SAMPLING_RATE_44P1KHZ 44100 +#define SAMPLING_RATE_88P2KHZ 88200 +#define SAMPLING_RATE_176P4KHZ 176400 +#define SAMPLING_RATE_352P8KHZ 352800 + +#define RX_MACRO_MAX_OFFSET 0x1000 + +#define RX_MACRO_MAX_DMA_CH_PER_PORT 2 +#define RX_SWR_STRING_LEN 80 +#define RX_MACRO_CHILD_DEVICES_MAX 3 + +#define RX_MACRO_INTERP_MUX_NUM_INPUTS 3 +#define RX_MACRO_SIDETONE_IIR_COEFF_MAX 5 + +#define STRING(name) #name +#define RX_MACRO_DAPM_ENUM(name, reg, offset, text) \ +static SOC_ENUM_SINGLE_DECL(name##_enum, reg, offset, text); \ +static const struct snd_kcontrol_new name##_mux = \ + SOC_DAPM_ENUM(STRING(name), name##_enum) + +#define RX_MACRO_DAPM_ENUM_EXT(name, reg, offset, text, getname, putname) \ +static SOC_ENUM_SINGLE_DECL(name##_enum, reg, offset, text); \ +static const struct snd_kcontrol_new name##_mux = \ + SOC_DAPM_ENUM_EXT(STRING(name), name##_enum, getname, putname) + +#define RX_MACRO_DAPM_MUX(name, shift, kctl) \ + SND_SOC_DAPM_MUX(name, SND_SOC_NOPM, shift, 0, &kctl##_mux) + +#define RX_MACRO_RX_PATH_OFFSET 0x80 +#define RX_MACRO_COMP_OFFSET 0x40 + +#define MAX_IMPED_PARAMS 6 + +#define RX_MACRO_EC_MIX_TX0_MASK 0xf0 +#define RX_MACRO_EC_MIX_TX1_MASK 0x0f +#define RX_MACRO_EC_MIX_TX2_MASK 0x0f + +struct wcd_imped_val { + u32 imped_val; + u8 index; +}; + +static const struct wcd_imped_val imped_index[] = { + {4, 0}, + {5, 1}, + {6, 2}, + {7, 3}, + {8, 4}, + {9, 5}, + {10, 6}, + {11, 7}, + {12, 8}, + {13, 9}, +}; + +struct rx_macro_reg_mask_val { + u16 reg; + u8 mask; + u8 val; +}; + +static const struct rx_macro_reg_mask_val imped_table[][MAX_IMPED_PARAMS] = { + { + {BOLERO_CDC_RX_RX0_RX_VOL_CTL, 0xff, 0xf2}, + {BOLERO_CDC_RX_RX0_RX_VOL_MIX_CTL, 0xff, 0xf2}, + {BOLERO_CDC_RX_RX0_RX_PATH_SEC1, 0x01, 0x00}, + {BOLERO_CDC_RX_RX1_RX_VOL_CTL, 0xff, 0xf2}, + {BOLERO_CDC_RX_RX1_RX_VOL_MIX_CTL, 0xff, 0xf2}, + {BOLERO_CDC_RX_RX1_RX_PATH_SEC1, 0x01, 0x00}, + }, + { + {BOLERO_CDC_RX_RX0_RX_VOL_CTL, 0xff, 0xf4}, + {BOLERO_CDC_RX_RX0_RX_VOL_MIX_CTL, 0xff, 0xf4}, + {BOLERO_CDC_RX_RX0_RX_PATH_SEC1, 0x01, 0x00}, + {BOLERO_CDC_RX_RX1_RX_VOL_CTL, 0xff, 0xf4}, + {BOLERO_CDC_RX_RX1_RX_VOL_MIX_CTL, 0xff, 0xf4}, + {BOLERO_CDC_RX_RX1_RX_PATH_SEC1, 0x01, 0x00}, + }, + { + {BOLERO_CDC_RX_RX0_RX_VOL_CTL, 0xff, 0xf7}, + {BOLERO_CDC_RX_RX0_RX_VOL_MIX_CTL, 0xff, 0xf7}, + {BOLERO_CDC_RX_RX0_RX_PATH_SEC1, 0x01, 0x01}, + {BOLERO_CDC_RX_RX1_RX_VOL_CTL, 0xff, 0xf7}, + {BOLERO_CDC_RX_RX1_RX_VOL_MIX_CTL, 0xff, 0xf7}, + {BOLERO_CDC_RX_RX1_RX_PATH_SEC1, 0x01, 0x01}, + }, + { + {BOLERO_CDC_RX_RX0_RX_VOL_CTL, 0xff, 0xf9}, + {BOLERO_CDC_RX_RX0_RX_VOL_MIX_CTL, 0xff, 0xf9}, + {BOLERO_CDC_RX_RX0_RX_PATH_SEC1, 0x01, 0x00}, + {BOLERO_CDC_RX_RX1_RX_VOL_CTL, 0xff, 0xf9}, + {BOLERO_CDC_RX_RX1_RX_VOL_MIX_CTL, 0xff, 0xf9}, + {BOLERO_CDC_RX_RX1_RX_PATH_SEC1, 0x01, 0x00}, + }, + { + {BOLERO_CDC_RX_RX0_RX_VOL_CTL, 0xff, 0xfa}, + {BOLERO_CDC_RX_RX0_RX_VOL_MIX_CTL, 0xff, 0xfa}, + {BOLERO_CDC_RX_RX0_RX_PATH_SEC1, 0x01, 0x00}, + {BOLERO_CDC_RX_RX1_RX_VOL_CTL, 0xff, 0xfa}, + {BOLERO_CDC_RX_RX1_RX_VOL_MIX_CTL, 0xff, 0xfa}, + {BOLERO_CDC_RX_RX1_RX_PATH_SEC1, 0x01, 0x00}, + }, + { + {BOLERO_CDC_RX_RX0_RX_VOL_CTL, 0xff, 0xfb}, + {BOLERO_CDC_RX_RX0_RX_VOL_MIX_CTL, 0xff, 0xfb}, + {BOLERO_CDC_RX_RX0_RX_PATH_SEC1, 0x01, 0x00}, + {BOLERO_CDC_RX_RX1_RX_VOL_CTL, 0xff, 0xfb}, + {BOLERO_CDC_RX_RX1_RX_VOL_MIX_CTL, 0xff, 0xfb}, + {BOLERO_CDC_RX_RX1_RX_PATH_SEC1, 0x01, 0x00}, + }, + { + {BOLERO_CDC_RX_RX0_RX_VOL_CTL, 0xff, 0xfc}, + {BOLERO_CDC_RX_RX0_RX_VOL_MIX_CTL, 0xff, 0xfc}, + {BOLERO_CDC_RX_RX0_RX_PATH_SEC1, 0x01, 0x00}, + {BOLERO_CDC_RX_RX1_RX_VOL_CTL, 0xff, 0xfc}, + {BOLERO_CDC_RX_RX1_RX_VOL_MIX_CTL, 0xff, 0xfc}, + {BOLERO_CDC_RX_RX1_RX_PATH_SEC1, 0x01, 0x00}, + }, + { + {BOLERO_CDC_RX_RX0_RX_VOL_CTL, 0xff, 0xfd}, + {BOLERO_CDC_RX_RX0_RX_VOL_MIX_CTL, 0xff, 0xfd}, + {BOLERO_CDC_RX_RX0_RX_PATH_SEC1, 0x01, 0x00}, + {BOLERO_CDC_RX_RX1_RX_VOL_CTL, 0xff, 0xfd}, + {BOLERO_CDC_RX_RX1_RX_VOL_MIX_CTL, 0xff, 0xfd}, + {BOLERO_CDC_RX_RX1_RX_PATH_SEC1, 0x01, 0x00}, + }, + { + {BOLERO_CDC_RX_RX0_RX_VOL_CTL, 0xff, 0xfd}, + {BOLERO_CDC_RX_RX0_RX_VOL_MIX_CTL, 0xff, 0xfd}, + {BOLERO_CDC_RX_RX0_RX_PATH_SEC1, 0x01, 0x01}, + {BOLERO_CDC_RX_RX1_RX_VOL_CTL, 0xff, 0xfd}, + {BOLERO_CDC_RX_RX1_RX_VOL_MIX_CTL, 0xff, 0xfd}, + {BOLERO_CDC_RX_RX1_RX_PATH_SEC1, 0x01, 0x01}, + }, +}; + +enum { + INTERP_HPHL, + INTERP_HPHR, + INTERP_AUX, + INTERP_MAX +}; + +enum { + RX_MACRO_RX0, + RX_MACRO_RX1, + RX_MACRO_RX2, + RX_MACRO_RX3, + RX_MACRO_RX4, + RX_MACRO_RX5, + RX_MACRO_PORTS_MAX +}; + +enum { + RX_MACRO_COMP1, /* HPH_L */ + RX_MACRO_COMP2, /* HPH_R */ + RX_MACRO_COMP_MAX +}; + +enum { + RX_MACRO_EC0_MUX = 0, + RX_MACRO_EC1_MUX, + RX_MACRO_EC2_MUX, + RX_MACRO_EC_MUX_MAX, +}; + +enum { + INTn_1_INP_SEL_ZERO = 0, + INTn_1_INP_SEL_DEC0, + INTn_1_INP_SEL_DEC1, + INTn_1_INP_SEL_IIR0, + INTn_1_INP_SEL_IIR1, + INTn_1_INP_SEL_RX0, + INTn_1_INP_SEL_RX1, + INTn_1_INP_SEL_RX2, + INTn_1_INP_SEL_RX3, + INTn_1_INP_SEL_RX4, + INTn_1_INP_SEL_RX5, +}; + +enum { + INTn_2_INP_SEL_ZERO = 0, + INTn_2_INP_SEL_RX0, + INTn_2_INP_SEL_RX1, + INTn_2_INP_SEL_RX2, + INTn_2_INP_SEL_RX3, + INTn_2_INP_SEL_RX4, + INTn_2_INP_SEL_RX5, +}; + +enum { + INTERP_MAIN_PATH, + INTERP_MIX_PATH, +}; + +/* Codec supports 2 IIR filters */ +enum { + IIR0 = 0, + IIR1, + IIR_MAX, +}; + +/* Each IIR has 5 Filter Stages */ +enum { + BAND1 = 0, + BAND2, + BAND3, + BAND4, + BAND5, + BAND_MAX, +}; + +struct rx_macro_idle_detect_config { + u8 hph_idle_thr; + u8 hph_idle_detect_en; +}; + +struct interp_sample_rate { + int sample_rate; + int rate_val; +}; + +static struct interp_sample_rate sr_val_tbl[] = { + {8000, 0x0}, {16000, 0x1}, {32000, 0x3}, {48000, 0x4}, {96000, 0x5}, + {192000, 0x6}, {384000, 0x7}, {44100, 0x9}, {88200, 0xA}, + {176400, 0xB}, {352800, 0xC}, +}; + +struct rx_macro_bcl_pmic_params { + u8 id; + u8 sid; + u8 ppid; +}; + +static int rx_macro_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai); +static int rx_macro_get_channel_map(struct snd_soc_dai *dai, + unsigned int *tx_num, unsigned int *tx_slot, + unsigned int *rx_num, unsigned int *rx_slot); +static int rx_macro_int_dem_inp_mux_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +static int rx_macro_mux_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +static int rx_macro_mux_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +static int rx_macro_enable_interp_clk(struct snd_soc_codec *codec, + int event, int interp_idx); + +/* Hold instance to soundwire platform device */ +struct rx_swr_ctrl_data { + struct platform_device *rx_swr_pdev; +}; + +struct rx_swr_ctrl_platform_data { + void *handle; /* holds codec private data */ + int (*read)(void *handle, int reg); + int (*write)(void *handle, int reg, int val); + int (*bulk_write)(void *handle, u32 *reg, u32 *val, size_t len); + int (*clk)(void *handle, bool enable); + int (*handle_irq)(void *handle, + irqreturn_t (*swrm_irq_handler)(int irq, + void *data), + void *swrm_handle, + int action); +}; + +enum { + RX_MACRO_AIF_INVALID = 0, + RX_MACRO_AIF1_PB, + RX_MACRO_AIF2_PB, + RX_MACRO_AIF3_PB, + RX_MACRO_AIF4_PB, + RX_MACRO_AIF_ECHO, + RX_MACRO_MAX_DAIS, +}; + +enum { + RX_MACRO_AIF1_CAP = 0, + RX_MACRO_AIF2_CAP, + RX_MACRO_AIF3_CAP, + RX_MACRO_MAX_AIF_CAP_DAIS +}; +/* + * @dev: rx macro device pointer + * @comp_enabled: compander enable mixer value set + * @prim_int_users: Users of interpolator + * @rx_mclk_users: RX MCLK users count + * @vi_feed_value: VI sense mask + * @swr_clk_lock: to lock swr master clock operations + * @swr_ctrl_data: SoundWire data structure + * @swr_plat_data: Soundwire platform data + * @rx_macro_add_child_devices_work: work for adding child devices + * @rx_swr_gpio_p: used by pinctrl API + * @rx_core_clk: MCLK for rx macro + * @rx_npl_clk: NPL clock for RX soundwire + * @codec: codec handle + */ +struct rx_macro_priv { + struct device *dev; + int comp_enabled[RX_MACRO_COMP_MAX]; + /* Main path clock users count */ + int main_clk_users[INTERP_MAX]; + int rx_port_value[RX_MACRO_PORTS_MAX]; + u16 prim_int_users[INTERP_MAX]; + int rx_mclk_users; + int swr_clk_users; + bool dapm_mclk_enable; + bool reset_swr; + int clsh_users; + int rx_mclk_cnt; + bool is_native_on; + bool is_ear_mode_on; + bool dev_up; + bool hph_pwr_mode; + bool hph_hd2_mode; + u16 mclk_mux; + struct mutex mclk_lock; + struct mutex swr_clk_lock; + struct rx_swr_ctrl_data *swr_ctrl_data; + struct rx_swr_ctrl_platform_data swr_plat_data; + struct work_struct rx_macro_add_child_devices_work; + struct device_node *rx_swr_gpio_p; + struct clk *rx_core_clk; + struct clk *rx_npl_clk; + struct snd_soc_codec *codec; + unsigned long active_ch_mask[RX_MACRO_MAX_DAIS]; + unsigned long active_ch_cnt[RX_MACRO_MAX_DAIS]; + u16 bit_width[RX_MACRO_MAX_DAIS]; + char __iomem *rx_io_base; + char __iomem *rx_mclk_mode_muxsel; + struct rx_macro_idle_detect_config idle_det_cfg; + u8 sidetone_coeff_array[IIR_MAX][BAND_MAX] + [RX_MACRO_SIDETONE_IIR_COEFF_MAX * 4]; + + struct platform_device *pdev_child_devices + [RX_MACRO_CHILD_DEVICES_MAX]; + int child_count; + int is_softclip_on; + int softclip_clk_users; + struct rx_macro_bcl_pmic_params bcl_pmic_params; +}; + +static struct snd_soc_dai_driver rx_macro_dai[]; +static const DECLARE_TLV_DB_SCALE(digital_gain, 0, 1, 0); + +static const char * const rx_int_mix_mux_text[] = { + "ZERO", "RX0", "RX1", "RX2", "RX3", "RX4", "RX5" +}; + +static const char * const rx_prim_mix_text[] = { + "ZERO", "DEC0", "DEC1", "IIR0", "IIR1", "RX0", "RX1", "RX2", + "RX3", "RX4", "RX5" +}; + +static const char * const rx_sidetone_mix_text[] = { + "ZERO", "SRC0", "SRC1", "SRC_SUM" +}; + +static const char * const iir_inp_mux_text[] = { + "ZERO", "DEC0", "DEC1", "DEC2", "DEC3", + "RX0", "RX1", "RX2", "RX3", "RX4", "RX5" +}; + +static const char * const rx_int_dem_inp_mux_text[] = { + "NORMAL_DSM_OUT", "CLSH_DSM_OUT", +}; + +static const char * const rx_int0_1_interp_mux_text[] = { + "ZERO", "RX INT0_1 MIX1", +}; + +static const char * const rx_int1_1_interp_mux_text[] = { + "ZERO", "RX INT1_1 MIX1", +}; + +static const char * const rx_int2_1_interp_mux_text[] = { + "ZERO", "RX INT2_1 MIX1", +}; + +static const char * const rx_int0_2_interp_mux_text[] = { + "ZERO", "RX INT0_2 MUX", +}; + +static const char * const rx_int1_2_interp_mux_text[] = { + "ZERO", "RX INT1_2 MUX", +}; + +static const char * const rx_int2_2_interp_mux_text[] = { + "ZERO", "RX INT2_2 MUX", +}; + +static const char *const rx_macro_mux_text[] = { + "ZERO", "AIF1_PB", "AIF2_PB", "AIF3_PB", "AIF4_PB" +}; + +static const char *const rx_macro_ear_mode_text[] = {"OFF", "ON"}; +static const struct soc_enum rx_macro_ear_mode_enum = + SOC_ENUM_SINGLE_EXT(2, rx_macro_ear_mode_text); + +static const char *const rx_macro_hph_hd2_mode_text[] = {"OFF", "ON"}; +static const struct soc_enum rx_macro_hph_hd2_mode_enum = + SOC_ENUM_SINGLE_EXT(2, rx_macro_hph_hd2_mode_text); + +static const char *const rx_macro_hph_pwr_mode_text[] = {"ULP", "LOHIFI"}; +static const struct soc_enum rx_macro_hph_pwr_mode_enum = + SOC_ENUM_SINGLE_EXT(2, rx_macro_hph_pwr_mode_text); + +static const char * const rx_macro_vbat_bcl_gsm_mode_text[] = {"OFF", "ON"}; +static const struct soc_enum rx_macro_vbat_bcl_gsm_mode_enum = + SOC_ENUM_SINGLE_EXT(2, rx_macro_vbat_bcl_gsm_mode_text); + +static const struct snd_kcontrol_new rx_int2_1_vbat_mix_switch[] = { + SOC_DAPM_SINGLE("RX AUX VBAT Enable", SND_SOC_NOPM, 0, 1, 0) +}; + +static const char * const hph_idle_detect_text[] = {"OFF", "ON"}; + +static SOC_ENUM_SINGLE_EXT_DECL(hph_idle_detect_enum, hph_idle_detect_text); + +RX_MACRO_DAPM_ENUM(rx_int0_2, BOLERO_CDC_RX_INP_MUX_RX_INT0_CFG1, 0, + rx_int_mix_mux_text); +RX_MACRO_DAPM_ENUM(rx_int1_2, BOLERO_CDC_RX_INP_MUX_RX_INT1_CFG1, 0, + rx_int_mix_mux_text); +RX_MACRO_DAPM_ENUM(rx_int2_2, BOLERO_CDC_RX_INP_MUX_RX_INT2_CFG1, 0, + rx_int_mix_mux_text); + + +RX_MACRO_DAPM_ENUM(rx_int0_1_mix_inp0, BOLERO_CDC_RX_INP_MUX_RX_INT0_CFG0, 0, + rx_prim_mix_text); +RX_MACRO_DAPM_ENUM(rx_int0_1_mix_inp1, BOLERO_CDC_RX_INP_MUX_RX_INT0_CFG0, 4, + rx_prim_mix_text); +RX_MACRO_DAPM_ENUM(rx_int0_1_mix_inp2, BOLERO_CDC_RX_INP_MUX_RX_INT0_CFG1, 4, + rx_prim_mix_text); +RX_MACRO_DAPM_ENUM(rx_int1_1_mix_inp0, BOLERO_CDC_RX_INP_MUX_RX_INT1_CFG0, 0, + rx_prim_mix_text); +RX_MACRO_DAPM_ENUM(rx_int1_1_mix_inp1, BOLERO_CDC_RX_INP_MUX_RX_INT1_CFG0, 4, + rx_prim_mix_text); +RX_MACRO_DAPM_ENUM(rx_int1_1_mix_inp2, BOLERO_CDC_RX_INP_MUX_RX_INT1_CFG1, 4, + rx_prim_mix_text); +RX_MACRO_DAPM_ENUM(rx_int2_1_mix_inp0, BOLERO_CDC_RX_INP_MUX_RX_INT2_CFG0, 0, + rx_prim_mix_text); +RX_MACRO_DAPM_ENUM(rx_int2_1_mix_inp1, BOLERO_CDC_RX_INP_MUX_RX_INT2_CFG0, 4, + rx_prim_mix_text); +RX_MACRO_DAPM_ENUM(rx_int2_1_mix_inp2, BOLERO_CDC_RX_INP_MUX_RX_INT2_CFG1, 4, + rx_prim_mix_text); + +RX_MACRO_DAPM_ENUM(rx_int0_mix2_inp, BOLERO_CDC_RX_INP_MUX_SIDETONE_SRC_CFG0, 2, + rx_sidetone_mix_text); +RX_MACRO_DAPM_ENUM(rx_int1_mix2_inp, BOLERO_CDC_RX_INP_MUX_SIDETONE_SRC_CFG0, 4, + rx_sidetone_mix_text); +RX_MACRO_DAPM_ENUM(rx_int2_mix2_inp, BOLERO_CDC_RX_INP_MUX_SIDETONE_SRC_CFG0, 6, + rx_sidetone_mix_text); + +RX_MACRO_DAPM_ENUM(iir0_inp0, BOLERO_CDC_RX_IIR_INP_MUX_IIR0_MIX_CFG0, 0, + iir_inp_mux_text); +RX_MACRO_DAPM_ENUM(iir0_inp1, BOLERO_CDC_RX_IIR_INP_MUX_IIR0_MIX_CFG1, 0, + iir_inp_mux_text); +RX_MACRO_DAPM_ENUM(iir0_inp2, BOLERO_CDC_RX_IIR_INP_MUX_IIR0_MIX_CFG2, 0, + iir_inp_mux_text); +RX_MACRO_DAPM_ENUM(iir0_inp3, BOLERO_CDC_RX_IIR_INP_MUX_IIR0_MIX_CFG3, 0, + iir_inp_mux_text); +RX_MACRO_DAPM_ENUM(iir1_inp0, BOLERO_CDC_RX_IIR_INP_MUX_IIR1_MIX_CFG0, 0, + iir_inp_mux_text); +RX_MACRO_DAPM_ENUM(iir1_inp1, BOLERO_CDC_RX_IIR_INP_MUX_IIR1_MIX_CFG1, 0, + iir_inp_mux_text); +RX_MACRO_DAPM_ENUM(iir1_inp2, BOLERO_CDC_RX_IIR_INP_MUX_IIR1_MIX_CFG2, 0, + iir_inp_mux_text); +RX_MACRO_DAPM_ENUM(iir1_inp3, BOLERO_CDC_RX_IIR_INP_MUX_IIR1_MIX_CFG3, 0, + iir_inp_mux_text); + +RX_MACRO_DAPM_ENUM(rx_int0_1_interp, SND_SOC_NOPM, 0, + rx_int0_1_interp_mux_text); +RX_MACRO_DAPM_ENUM(rx_int1_1_interp, SND_SOC_NOPM, 0, + rx_int1_1_interp_mux_text); +RX_MACRO_DAPM_ENUM(rx_int2_1_interp, SND_SOC_NOPM, 0, + rx_int2_1_interp_mux_text); + +RX_MACRO_DAPM_ENUM(rx_int0_2_interp, SND_SOC_NOPM, 0, + rx_int0_2_interp_mux_text); +RX_MACRO_DAPM_ENUM(rx_int1_2_interp, SND_SOC_NOPM, 0, + rx_int1_2_interp_mux_text); +RX_MACRO_DAPM_ENUM(rx_int2_2_interp, SND_SOC_NOPM, 0, + rx_int2_2_interp_mux_text); + +RX_MACRO_DAPM_ENUM_EXT(rx_int0_dem_inp, BOLERO_CDC_RX_RX0_RX_PATH_CFG1, 0, + rx_int_dem_inp_mux_text, snd_soc_dapm_get_enum_double, + rx_macro_int_dem_inp_mux_put); +RX_MACRO_DAPM_ENUM_EXT(rx_int1_dem_inp, BOLERO_CDC_RX_RX1_RX_PATH_CFG1, 0, + rx_int_dem_inp_mux_text, snd_soc_dapm_get_enum_double, + rx_macro_int_dem_inp_mux_put); + +RX_MACRO_DAPM_ENUM_EXT(rx_macro_rx0, SND_SOC_NOPM, 0, rx_macro_mux_text, + rx_macro_mux_get, rx_macro_mux_put); +RX_MACRO_DAPM_ENUM_EXT(rx_macro_rx1, SND_SOC_NOPM, 0, rx_macro_mux_text, + rx_macro_mux_get, rx_macro_mux_put); +RX_MACRO_DAPM_ENUM_EXT(rx_macro_rx2, SND_SOC_NOPM, 0, rx_macro_mux_text, + rx_macro_mux_get, rx_macro_mux_put); +RX_MACRO_DAPM_ENUM_EXT(rx_macro_rx3, SND_SOC_NOPM, 0, rx_macro_mux_text, + rx_macro_mux_get, rx_macro_mux_put); +RX_MACRO_DAPM_ENUM_EXT(rx_macro_rx4, SND_SOC_NOPM, 0, rx_macro_mux_text, + rx_macro_mux_get, rx_macro_mux_put); +RX_MACRO_DAPM_ENUM_EXT(rx_macro_rx5, SND_SOC_NOPM, 0, rx_macro_mux_text, + rx_macro_mux_get, rx_macro_mux_put); + +static const char * const rx_echo_mux_text[] = { + "ZERO", "RX_MIX0", "RX_MIX1", "RX_MIX2" +}; + +static const struct soc_enum rx_mix_tx2_mux_enum = + SOC_ENUM_SINGLE(BOLERO_CDC_RX_INP_MUX_RX_MIX_CFG5, 0, 4, + rx_echo_mux_text); + +static const struct snd_kcontrol_new rx_mix_tx2_mux = + SOC_DAPM_ENUM("RX MIX TX2_MUX Mux", rx_mix_tx2_mux_enum); + +static const struct soc_enum rx_mix_tx1_mux_enum = + SOC_ENUM_SINGLE(BOLERO_CDC_RX_INP_MUX_RX_MIX_CFG4, 0, 4, + rx_echo_mux_text); + +static const struct snd_kcontrol_new rx_mix_tx1_mux = + SOC_DAPM_ENUM("RX MIX TX1_MUX Mux", rx_mix_tx1_mux_enum); + +static const struct soc_enum rx_mix_tx0_mux_enum = + SOC_ENUM_SINGLE(BOLERO_CDC_RX_INP_MUX_RX_MIX_CFG4, 4, 4, + rx_echo_mux_text); + +static const struct snd_kcontrol_new rx_mix_tx0_mux = + SOC_DAPM_ENUM("RX MIX TX0_MUX Mux", rx_mix_tx0_mux_enum); + +static struct snd_soc_dai_ops rx_macro_dai_ops = { + .hw_params = rx_macro_hw_params, + .get_channel_map = rx_macro_get_channel_map, +}; + +static struct snd_soc_dai_driver rx_macro_dai[] = { + { + .name = "rx_macro_rx1", + .id = RX_MACRO_AIF1_PB, + .playback = { + .stream_name = "RX_MACRO_AIF1 Playback", + .rates = RX_MACRO_RATES | RX_MACRO_FRAC_RATES, + .formats = RX_MACRO_FORMATS, + .rate_max = 384000, + .rate_min = 8000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &rx_macro_dai_ops, + }, + { + .name = "rx_macro_rx2", + .id = RX_MACRO_AIF2_PB, + .playback = { + .stream_name = "RX_MACRO_AIF2 Playback", + .rates = RX_MACRO_RATES | RX_MACRO_FRAC_RATES, + .formats = RX_MACRO_FORMATS, + .rate_max = 384000, + .rate_min = 8000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &rx_macro_dai_ops, + }, + { + .name = "rx_macro_rx3", + .id = RX_MACRO_AIF3_PB, + .playback = { + .stream_name = "RX_MACRO_AIF3 Playback", + .rates = RX_MACRO_RATES | RX_MACRO_FRAC_RATES, + .formats = RX_MACRO_FORMATS, + .rate_max = 384000, + .rate_min = 8000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &rx_macro_dai_ops, + }, + { + .name = "rx_macro_rx4", + .id = RX_MACRO_AIF4_PB, + .playback = { + .stream_name = "RX_MACRO_AIF4 Playback", + .rates = RX_MACRO_RATES | RX_MACRO_FRAC_RATES, + .formats = RX_MACRO_FORMATS, + .rate_max = 384000, + .rate_min = 8000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &rx_macro_dai_ops, + }, + { + .name = "rx_macro_echo", + .id = RX_MACRO_AIF_ECHO, + .capture = { + .stream_name = "RX_AIF_ECHO Capture", + .rates = RX_MACRO_ECHO_RATES, + .formats = RX_MACRO_ECHO_FORMATS, + .rate_max = 48000, + .rate_min = 8000, + .channels_min = 1, + .channels_max = 3, + }, + .ops = &rx_macro_dai_ops, + }, +}; + +static int get_impedance_index(int imped) +{ + int i = 0; + + if (imped < imped_index[i].imped_val) { + pr_debug("%s, detected impedance is less than %d Ohm\n", + __func__, imped_index[i].imped_val); + i = 0; + goto ret; + } + if (imped >= imped_index[ARRAY_SIZE(imped_index) - 1].imped_val) { + pr_debug("%s, detected impedance is greater than %d Ohm\n", + __func__, + imped_index[ARRAY_SIZE(imped_index) - 1].imped_val); + i = ARRAY_SIZE(imped_index) - 1; + goto ret; + } + for (i = 0; i < ARRAY_SIZE(imped_index) - 1; i++) { + if (imped >= imped_index[i].imped_val && + imped < imped_index[i + 1].imped_val) + break; + } +ret: + pr_debug("%s: selected impedance index = %d\n", + __func__, imped_index[i].index); + return imped_index[i].index; +} + +/* + * rx_macro_wcd_clsh_imped_config - + * This function updates HPHL and HPHR gain settings + * according to the impedance value. + * + * @codec: codec pointer handle + * @imped: impedance value of HPHL/R + * @reset: bool variable to reset registers when teardown + */ +static void rx_macro_wcd_clsh_imped_config(struct snd_soc_codec *codec, + int imped, bool reset) +{ + int i; + int index = 0; + int table_size; + + static const struct rx_macro_reg_mask_val + (*imped_table_ptr)[MAX_IMPED_PARAMS]; + + table_size = ARRAY_SIZE(imped_table); + imped_table_ptr = imped_table; + /* reset = 1, which means request is to reset the register values */ + if (reset) { + for (i = 0; i < MAX_IMPED_PARAMS; i++) + snd_soc_update_bits(codec, + imped_table_ptr[index][i].reg, + imped_table_ptr[index][i].mask, 0); + return; + } + index = get_impedance_index(imped); + if (index >= (ARRAY_SIZE(imped_index) - 1)) { + pr_debug("%s, impedance not in range = %d\n", __func__, imped); + return; + } + if (index >= table_size) { + pr_debug("%s, impedance index not in range = %d\n", __func__, + index); + return; + } + for (i = 0; i < MAX_IMPED_PARAMS; i++) + snd_soc_update_bits(codec, + imped_table_ptr[index][i].reg, + imped_table_ptr[index][i].mask, + imped_table_ptr[index][i].val); +} + +static bool rx_macro_get_data(struct snd_soc_codec *codec, + struct device **rx_dev, + struct rx_macro_priv **rx_priv, + const char *func_name) +{ + *rx_dev = bolero_get_device_ptr(codec->dev, RX_MACRO); + + if (!(*rx_dev)) { + dev_err(codec->dev, + "%s: null device for macro!\n", func_name); + return false; + } + + *rx_priv = dev_get_drvdata((*rx_dev)); + if (!(*rx_priv)) { + dev_err(codec->dev, + "%s: priv is null for macro!\n", func_name); + return false; + } + + if (!(*rx_priv)->codec) { + dev_err(codec->dev, + "%s: rx_priv codec is not initialized!\n", func_name); + return false; + } + + return true; +} + +static int rx_macro_int_dem_inp_mux_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int val = 0; + unsigned short look_ahead_dly_reg = + BOLERO_CDC_RX_RX0_RX_PATH_CFG0; + + val = ucontrol->value.enumerated.item[0]; + if (val >= e->items) + return -EINVAL; + + dev_dbg(codec->dev, "%s: wname: %s, val: 0x%x\n", __func__, + widget->name, val); + + if (e->reg == BOLERO_CDC_RX_RX0_RX_PATH_CFG1) + look_ahead_dly_reg = BOLERO_CDC_RX_RX0_RX_PATH_CFG0; + else if (e->reg == BOLERO_CDC_RX_RX1_RX_PATH_CFG1) + look_ahead_dly_reg = BOLERO_CDC_RX_RX1_RX_PATH_CFG0; + + /* Set Look Ahead Delay */ + snd_soc_update_bits(codec, look_ahead_dly_reg, + 0x08, (val ? 0x08 : 0x00)); + /* Set DEM INP Select */ + return snd_soc_dapm_put_enum_double(kcontrol, ucontrol); +} + +static int rx_macro_set_prim_interpolator_rate(struct snd_soc_dai *dai, + u8 rate_reg_val, + u32 sample_rate) +{ + u8 int_1_mix1_inp = 0; + u32 j = 0, port = 0; + u16 int_mux_cfg0 = 0, int_mux_cfg1 = 0; + u16 int_fs_reg = 0; + u8 int_mux_cfg0_val = 0, int_mux_cfg1_val = 0; + u8 inp0_sel = 0, inp1_sel = 0, inp2_sel = 0; + struct snd_soc_codec *codec = dai->codec; + struct device *rx_dev = NULL; + struct rx_macro_priv *rx_priv = NULL; + + if (!rx_macro_get_data(codec, &rx_dev, &rx_priv, __func__)) + return -EINVAL; + + for_each_set_bit(port, &rx_priv->active_ch_mask[dai->id], + RX_MACRO_PORTS_MAX) { + int_1_mix1_inp = port; + if ((int_1_mix1_inp < RX_MACRO_RX0) || + (int_1_mix1_inp > RX_MACRO_PORTS_MAX)) { + pr_err("%s: Invalid RX port, Dai ID is %d\n", + __func__, dai->id); + return -EINVAL; + } + + int_mux_cfg0 = BOLERO_CDC_RX_INP_MUX_RX_INT0_CFG0; + + /* + * Loop through all interpolator MUX inputs and find out + * to which interpolator input, the rx port + * is connected + */ + for (j = 0; j < INTERP_MAX; j++) { + int_mux_cfg1 = int_mux_cfg0 + 4; + + int_mux_cfg0_val = snd_soc_read(codec, int_mux_cfg0); + int_mux_cfg1_val = snd_soc_read(codec, int_mux_cfg1); + inp0_sel = int_mux_cfg0_val & 0x07; + inp1_sel = (int_mux_cfg0_val >> 4) & 0x038; + inp2_sel = (int_mux_cfg1_val >> 4) & 0x038; + if ((inp0_sel == int_1_mix1_inp) || + (inp1_sel == int_1_mix1_inp) || + (inp2_sel == int_1_mix1_inp)) { + int_fs_reg = BOLERO_CDC_RX_RX0_RX_PATH_CTL + + 0x80 * j; + pr_debug("%s: AIF_PB DAI(%d) connected to INT%u_1\n", + __func__, dai->id, j); + pr_debug("%s: set INT%u_1 sample rate to %u\n", + __func__, j, sample_rate); + /* sample_rate is in Hz */ + snd_soc_update_bits(codec, int_fs_reg, + 0x0F, rate_reg_val); + } + int_mux_cfg0 += 8; + } + } + + return 0; +} + +static int rx_macro_set_mix_interpolator_rate(struct snd_soc_dai *dai, + u8 rate_reg_val, + u32 sample_rate) +{ + u8 int_2_inp = 0; + u32 j = 0, port = 0; + u16 int_mux_cfg1 = 0, int_fs_reg = 0; + u8 int_mux_cfg1_val = 0; + struct snd_soc_codec *codec = dai->codec; + struct device *rx_dev = NULL; + struct rx_macro_priv *rx_priv = NULL; + + if (!rx_macro_get_data(codec, &rx_dev, &rx_priv, __func__)) + return -EINVAL; + + for_each_set_bit(port, &rx_priv->active_ch_mask[dai->id], + RX_MACRO_PORTS_MAX) { + int_2_inp = port; + if ((int_2_inp < RX_MACRO_RX0) || + (int_2_inp > RX_MACRO_PORTS_MAX)) { + pr_err("%s: Invalid RX port, Dai ID is %d\n", + __func__, dai->id); + return -EINVAL; + } + + int_mux_cfg1 = BOLERO_CDC_RX_INP_MUX_RX_INT0_CFG1; + for (j = 0; j < INTERP_MAX; j++) { + int_mux_cfg1_val = snd_soc_read(codec, int_mux_cfg1) & + 0x07; + if (int_mux_cfg1_val == int_2_inp) { + int_fs_reg = BOLERO_CDC_RX_RX0_RX_PATH_MIX_CTL + + 0x80 * j; + pr_debug("%s: AIF_PB DAI(%d) connected to INT%u_2\n", + __func__, dai->id, j); + pr_debug("%s: set INT%u_2 sample rate to %u\n", + __func__, j, sample_rate); + snd_soc_update_bits(codec, int_fs_reg, + 0x0F, rate_reg_val); + } + int_mux_cfg1 += 8; + } + } + return 0; +} + +static bool rx_macro_is_fractional_sample_rate(u32 sample_rate) +{ + switch (sample_rate) { + case SAMPLING_RATE_44P1KHZ: + case SAMPLING_RATE_88P2KHZ: + case SAMPLING_RATE_176P4KHZ: + case SAMPLING_RATE_352P8KHZ: + return true; + default: + return false; + } + return false; +} + +static int rx_macro_set_interpolator_rate(struct snd_soc_dai *dai, + u32 sample_rate) +{ + struct snd_soc_codec *codec = dai->codec; + int rate_val = 0; + int i = 0, ret = 0; + struct device *rx_dev = NULL; + struct rx_macro_priv *rx_priv = NULL; + + if (!rx_macro_get_data(codec, &rx_dev, &rx_priv, __func__)) + return -EINVAL; + + + for (i = 0; i < ARRAY_SIZE(sr_val_tbl); i++) { + if (sample_rate == sr_val_tbl[i].sample_rate) { + rate_val = sr_val_tbl[i].rate_val; + if (rx_macro_is_fractional_sample_rate(sample_rate)) + rx_priv->is_native_on = true; + else + rx_priv->is_native_on = false; + break; + } + } + if ((i == ARRAY_SIZE(sr_val_tbl)) || (rate_val < 0)) { + dev_err(codec->dev, "%s: Unsupported sample rate: %d\n", + __func__, sample_rate); + return -EINVAL; + } + + ret = rx_macro_set_prim_interpolator_rate(dai, (u8)rate_val, sample_rate); + if (ret) + return ret; + ret = rx_macro_set_mix_interpolator_rate(dai, (u8)rate_val, sample_rate); + if (ret) + return ret; + + return ret; +} + +static int rx_macro_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + int ret = 0; + struct device *rx_dev = NULL; + struct rx_macro_priv *rx_priv = NULL; + + if (!rx_macro_get_data(codec, &rx_dev, &rx_priv, __func__)) + return -EINVAL; + + dev_dbg(codec->dev, + "%s: dai_name = %s DAI-ID %x rate %d num_ch %d\n", __func__, + dai->name, dai->id, params_rate(params), + params_channels(params)); + + switch (substream->stream) { + case SNDRV_PCM_STREAM_PLAYBACK: + ret = rx_macro_set_interpolator_rate(dai, params_rate(params)); + if (ret) { + pr_err("%s: cannot set sample rate: %u\n", + __func__, params_rate(params)); + return ret; + } + rx_priv->bit_width[dai->id] = params_width(params); + break; + case SNDRV_PCM_STREAM_CAPTURE: + default: + break; + } + return 0; +} + +static int rx_macro_get_channel_map(struct snd_soc_dai *dai, + unsigned int *tx_num, unsigned int *tx_slot, + unsigned int *rx_num, unsigned int *rx_slot) +{ + struct snd_soc_codec *codec = dai->codec; + struct device *rx_dev = NULL; + struct rx_macro_priv *rx_priv = NULL; + unsigned int temp = 0, ch_mask = 0; + u16 val = 0, mask = 0, cnt = 0, i = 0; + + if (!rx_macro_get_data(codec, &rx_dev, &rx_priv, __func__)) + return -EINVAL; + + switch (dai->id) { + case RX_MACRO_AIF1_PB: + case RX_MACRO_AIF2_PB: + case RX_MACRO_AIF3_PB: + case RX_MACRO_AIF4_PB: + for_each_set_bit(temp, &rx_priv->active_ch_mask[dai->id], + RX_MACRO_PORTS_MAX) { + ch_mask |= (1 << temp); + if (++i == RX_MACRO_MAX_DMA_CH_PER_PORT) + break; + } + *rx_slot = ch_mask; + *rx_num = rx_priv->active_ch_cnt[dai->id]; + break; + case RX_MACRO_AIF_ECHO: + val = snd_soc_read(codec, + BOLERO_CDC_RX_INP_MUX_RX_MIX_CFG4); + if (val & RX_MACRO_EC_MIX_TX0_MASK) { + mask |= 0x1; + cnt++; + } + if (val & RX_MACRO_EC_MIX_TX1_MASK) { + mask |= 0x2; + cnt++; + } + val = snd_soc_read(codec, + BOLERO_CDC_RX_INP_MUX_RX_MIX_CFG5); + if (val & RX_MACRO_EC_MIX_TX2_MASK) { + mask |= 0x4; + cnt++; + } + *tx_slot = mask; + *tx_num = cnt; + break; + default: + dev_err(rx_dev, "%s: Invalid AIF\n", __func__); + break; + } + return 0; +} + +static int rx_macro_mclk_enable(struct rx_macro_priv *rx_priv, + bool mclk_enable, bool dapm) +{ + struct regmap *regmap = dev_get_regmap(rx_priv->dev->parent, NULL); + int ret = 0, mclk_mux = MCLK_MUX0; + + if (regmap == NULL) { + dev_err(rx_priv->dev, "%s: regmap is NULL\n", __func__); + return -EINVAL; + } + + dev_dbg(rx_priv->dev, "%s: mclk_enable = %u, dapm = %d clk_users= %d\n", + __func__, mclk_enable, dapm, rx_priv->rx_mclk_users); + + mutex_lock(&rx_priv->mclk_lock); + if (mclk_enable) { + if (rx_priv->rx_mclk_users == 0) { + if (rx_priv->is_native_on) + mclk_mux = MCLK_MUX1; + ret = bolero_request_clock(rx_priv->dev, + RX_MACRO, mclk_mux, true); + if (ret < 0) { + dev_err_ratelimited(rx_priv->dev, + "%s: rx request clock enable failed\n", + __func__); + goto exit; + } + rx_priv->mclk_mux = mclk_mux; + regcache_mark_dirty(regmap); + regcache_sync_region(regmap, + RX_START_OFFSET, + RX_MAX_OFFSET); + regmap_update_bits(regmap, + BOLERO_CDC_RX_CLK_RST_CTRL_MCLK_CONTROL, + 0x01, 0x01); + regmap_update_bits(regmap, + BOLERO_CDC_RX_CLK_RST_CTRL_MCLK_CONTROL, + 0x02, 0x02); + regmap_update_bits(regmap, + BOLERO_CDC_RX_CLK_RST_CTRL_FS_CNT_CONTROL, + 0x01, 0x01); + } + rx_priv->rx_mclk_users++; + } else { + if (rx_priv->rx_mclk_users <= 0) { + dev_err(rx_priv->dev, "%s: clock already disabled\n", + __func__); + rx_priv->rx_mclk_users = 0; + goto exit; + } + rx_priv->rx_mclk_users--; + if (rx_priv->rx_mclk_users == 0) { + regmap_update_bits(regmap, + BOLERO_CDC_RX_CLK_RST_CTRL_FS_CNT_CONTROL, + 0x01, 0x00); + regmap_update_bits(regmap, + BOLERO_CDC_RX_CLK_RST_CTRL_MCLK_CONTROL, + 0x01, 0x00); + mclk_mux = rx_priv->mclk_mux; + bolero_request_clock(rx_priv->dev, + RX_MACRO, mclk_mux, false); + rx_priv->mclk_mux = MCLK_MUX0; + } + } +exit: + mutex_unlock(&rx_priv->mclk_lock); + return ret; +} + +static int rx_macro_mclk_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + int ret = 0; + struct device *rx_dev = NULL; + struct rx_macro_priv *rx_priv = NULL; + int mclk_freq = MCLK_FREQ; + + if (!rx_macro_get_data(codec, &rx_dev, &rx_priv, __func__)) + return -EINVAL; + + dev_dbg(rx_dev, "%s: event = %d\n", __func__, event); + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* if swr_clk_users > 0, call device down */ + if (rx_priv->swr_clk_users > 0) { + if ((rx_priv->mclk_mux == MCLK_MUX0 && + rx_priv->is_native_on) || + (rx_priv->mclk_mux == MCLK_MUX1 && + !rx_priv->is_native_on)) { + swrm_wcd_notify( + rx_priv->swr_ctrl_data[0].rx_swr_pdev, + SWR_DEVICE_DOWN, NULL); + } + } + if (rx_priv->is_native_on) + mclk_freq = MCLK_FREQ_NATIVE; + swrm_wcd_notify( + rx_priv->swr_ctrl_data[0].rx_swr_pdev, + SWR_CLK_FREQ, &mclk_freq); + ret = rx_macro_mclk_enable(rx_priv, 1, true); + if (ret) + rx_priv->dapm_mclk_enable = false; + else + rx_priv->dapm_mclk_enable = true; + break; + case SND_SOC_DAPM_POST_PMD: + if (rx_priv->dapm_mclk_enable) + ret = rx_macro_mclk_enable(rx_priv, 0, true); + break; + default: + dev_err(rx_priv->dev, + "%s: invalid DAPM event %d\n", __func__, event); + ret = -EINVAL; + } + return ret; +} + +static int rx_macro_mclk_ctrl(struct device *dev, bool enable) +{ + struct rx_macro_priv *rx_priv = dev_get_drvdata(dev); + int ret = 0; + + if (enable) { + ret = clk_prepare_enable(rx_priv->rx_core_clk); + if (ret < 0) { + dev_err(dev, "%s:rx mclk enable failed\n", __func__); + return ret; + } + ret = clk_prepare_enable(rx_priv->rx_npl_clk); + if (ret < 0) { + clk_disable_unprepare(rx_priv->rx_core_clk); + dev_err(dev, "%s:rx npl_clk enable failed\n", + __func__); + return ret; + } + if (rx_priv->rx_mclk_cnt++ == 0) { + if (rx_priv->dev_up) + iowrite32(0x1, rx_priv->rx_mclk_mode_muxsel); + } + } else { + if (rx_priv->rx_mclk_cnt <= 0) { + dev_dbg(dev, "%s:rx mclk already disabled\n", __func__); + rx_priv->rx_mclk_cnt = 0; + return 0; + } + if (--rx_priv->rx_mclk_cnt == 0) { + if (rx_priv->dev_up) + iowrite32(0x0, rx_priv->rx_mclk_mode_muxsel); + } + clk_disable_unprepare(rx_priv->rx_npl_clk); + clk_disable_unprepare(rx_priv->rx_core_clk); + } + + return 0; +} + +static int rx_macro_event_handler(struct snd_soc_codec *codec, u16 event, + u32 data) +{ + u16 reg = 0, reg_mix = 0, rx_idx = 0, mute = 0x0, val = 0; + struct device *rx_dev = NULL; + struct rx_macro_priv *rx_priv = NULL; + + if (!rx_macro_get_data(codec, &rx_dev, &rx_priv, __func__)) + return -EINVAL; + + switch (event) { + case BOLERO_MACRO_EVT_RX_MUTE: + rx_idx = data >> 0x10; + mute = data & 0xffff; + val = mute ? 0x10 : 0x00; + reg = BOLERO_CDC_RX_RX0_RX_PATH_CTL + (rx_idx * + RX_MACRO_RX_PATH_OFFSET); + reg_mix = BOLERO_CDC_RX_RX0_RX_PATH_MIX_CTL + (rx_idx * + RX_MACRO_RX_PATH_OFFSET); + snd_soc_update_bits(codec, reg, 0x10, val); + snd_soc_update_bits(codec, reg_mix, 0x10, val); + break; + case BOLERO_MACRO_EVT_IMPED_TRUE: + rx_macro_wcd_clsh_imped_config(codec, data, true); + break; + case BOLERO_MACRO_EVT_IMPED_FALSE: + rx_macro_wcd_clsh_imped_config(codec, data, false); + break; + case BOLERO_MACRO_EVT_SSR_DOWN: + rx_priv->dev_up = false; + swrm_wcd_notify( + rx_priv->swr_ctrl_data[0].rx_swr_pdev, + SWR_DEVICE_DOWN, NULL); + swrm_wcd_notify( + rx_priv->swr_ctrl_data[0].rx_swr_pdev, + SWR_DEVICE_SSR_DOWN, NULL); + break; + case BOLERO_MACRO_EVT_SSR_UP: + rx_priv->dev_up = true; + /* reset swr after ssr/pdr */ + rx_priv->reset_swr = true; + /* enable&disable MCLK_MUX1 to reset GFMUX reg */ + bolero_request_clock(rx_priv->dev, + RX_MACRO, MCLK_MUX1, true); + bolero_request_clock(rx_priv->dev, + RX_MACRO, MCLK_MUX1, false); + swrm_wcd_notify( + rx_priv->swr_ctrl_data[0].rx_swr_pdev, + SWR_DEVICE_SSR_UP, NULL); + break; + } + return 0; +} + +static int rx_macro_find_playback_dai_id_for_port(int port_id, + struct rx_macro_priv *rx_priv) +{ + int i = 0; + + for (i = RX_MACRO_AIF1_PB; i < RX_MACRO_MAX_DAIS; i++) { + if (test_bit(port_id, &rx_priv->active_ch_mask[i])) + return i; + } + + return -EINVAL; +} + +static int rx_macro_set_idle_detect_thr(struct snd_soc_codec *codec, + struct rx_macro_priv *rx_priv, + int interp, int path_type) +{ + int port_id[4] = { 0, 0, 0, 0 }; + int *port_ptr = NULL; + int num_ports = 0; + int bit_width = 0, i = 0; + int mux_reg = 0, mux_reg_val = 0; + int dai_id = 0, idle_thr = 0; + + if ((interp != INTERP_HPHL) && (interp != INTERP_HPHR)) + return 0; + + if (!rx_priv->idle_det_cfg.hph_idle_detect_en) + return 0; + + port_ptr = &port_id[0]; + num_ports = 0; + + /* + * Read interpolator MUX input registers and find + * which cdc_dma port is connected and store the port + * numbers in port_id array. + */ + if (path_type == INTERP_MIX_PATH) { + mux_reg = BOLERO_CDC_RX_INP_MUX_RX_INT0_CFG1 + + 2 * interp; + mux_reg_val = snd_soc_read(codec, mux_reg) & 0x0f; + + if ((mux_reg_val >= INTn_2_INP_SEL_RX0) && + (mux_reg_val <= INTn_2_INP_SEL_RX5)) { + *port_ptr++ = mux_reg_val - 1; + num_ports++; + } + } + + if (path_type == INTERP_MAIN_PATH) { + mux_reg = BOLERO_CDC_RX_INP_MUX_RX_INT1_CFG0 + + 2 * (interp - 1); + mux_reg_val = snd_soc_read(codec, mux_reg) & 0x0f; + i = RX_MACRO_INTERP_MUX_NUM_INPUTS; + + while (i) { + if ((mux_reg_val >= INTn_1_INP_SEL_RX0) && + (mux_reg_val <= INTn_1_INP_SEL_RX5)) { + *port_ptr++ = mux_reg_val - + INTn_1_INP_SEL_RX0; + num_ports++; + } + mux_reg_val = (snd_soc_read(codec, mux_reg) & + 0xf0) >> 4; + mux_reg += 1; + i--; + } + } + + dev_dbg(codec->dev, "%s: num_ports: %d, ports[%d %d %d %d]\n", + __func__, num_ports, port_id[0], port_id[1], + port_id[2], port_id[3]); + + i = 0; + while (num_ports) { + dai_id = rx_macro_find_playback_dai_id_for_port(port_id[i++], + rx_priv); + + if ((dai_id >= 0) && (dai_id < RX_MACRO_MAX_DAIS)) { + dev_dbg(codec->dev, "%s: dai_id: %d bit_width: %d\n", + __func__, dai_id, + rx_priv->bit_width[dai_id]); + + if (rx_priv->bit_width[dai_id] > bit_width) + bit_width = rx_priv->bit_width[dai_id]; + } + num_ports--; + } + + switch (bit_width) { + case 16: + idle_thr = 0xff; /* F16 */ + break; + case 24: + case 32: + idle_thr = 0x03; /* F22 */ + break; + default: + idle_thr = 0x00; + break; + } + + dev_dbg(codec->dev, "%s: (new) idle_thr: %d, (cur) idle_thr: %d\n", + __func__, idle_thr, rx_priv->idle_det_cfg.hph_idle_thr); + + if ((rx_priv->idle_det_cfg.hph_idle_thr == 0) || + (idle_thr < rx_priv->idle_det_cfg.hph_idle_thr)) { + snd_soc_write(codec, BOLERO_CDC_RX_IDLE_DETECT_CFG3, idle_thr); + rx_priv->idle_det_cfg.hph_idle_thr = idle_thr; + } + + return 0; +} + +static int rx_macro_enable_mix_path(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + u16 gain_reg = 0, mix_reg = 0; + struct device *rx_dev = NULL; + struct rx_macro_priv *rx_priv = NULL; + + if (!rx_macro_get_data(codec, &rx_dev, &rx_priv, __func__)) + return -EINVAL; + + if (w->shift >= INTERP_MAX) { + dev_err(codec->dev, "%s: Invalid Interpolator value %d for name %s\n", + __func__, w->shift, w->name); + return -EINVAL; + } + + gain_reg = BOLERO_CDC_RX_RX0_RX_VOL_MIX_CTL + + (w->shift * RX_MACRO_RX_PATH_OFFSET); + mix_reg = BOLERO_CDC_RX_RX0_RX_PATH_MIX_CTL + + (w->shift * RX_MACRO_RX_PATH_OFFSET); + + dev_dbg(codec->dev, "%s %d %s\n", __func__, event, w->name); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + rx_macro_set_idle_detect_thr(codec, rx_priv, w->shift, + INTERP_MIX_PATH); + rx_macro_enable_interp_clk(codec, event, w->shift); + /* Clk enable */ + snd_soc_update_bits(codec, mix_reg, 0x20, 0x20); + break; + case SND_SOC_DAPM_POST_PMU: + snd_soc_write(codec, gain_reg, + snd_soc_read(codec, gain_reg)); + break; + case SND_SOC_DAPM_POST_PMD: + /* Clk Disable */ + snd_soc_update_bits(codec, mix_reg, 0x20, 0x00); + rx_macro_enable_interp_clk(codec, event, w->shift); + /* Reset enable and disable */ + snd_soc_update_bits(codec, mix_reg, 0x40, 0x40); + snd_soc_update_bits(codec, mix_reg, 0x40, 0x00); + break; + } + + return 0; +} + +static int rx_macro_enable_main_path(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + u16 gain_reg = 0; + u16 reg = 0; + struct device *rx_dev = NULL; + struct rx_macro_priv *rx_priv = NULL; + + if (!rx_macro_get_data(codec, &rx_dev, &rx_priv, __func__)) + return -EINVAL; + + dev_dbg(codec->dev, "%s %d %s\n", __func__, event, w->name); + + if (w->shift >= INTERP_MAX) { + dev_err(codec->dev, "%s: Invalid Interpolator value %d for name %s\n", + __func__, w->shift, w->name); + return -EINVAL; + } + + reg = BOLERO_CDC_RX_RX0_RX_PATH_CTL + (w->shift * + RX_MACRO_RX_PATH_OFFSET); + gain_reg = BOLERO_CDC_RX_RX0_RX_VOL_CTL + (w->shift * + RX_MACRO_RX_PATH_OFFSET); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + rx_macro_set_idle_detect_thr(codec, rx_priv, w->shift, + INTERP_MAIN_PATH); + rx_macro_enable_interp_clk(codec, event, w->shift); + break; + case SND_SOC_DAPM_POST_PMU: + snd_soc_write(codec, gain_reg, + snd_soc_read(codec, gain_reg)); + break; + case SND_SOC_DAPM_POST_PMD: + rx_macro_enable_interp_clk(codec, event, w->shift); + break; + } + + return 0; +} + +static int rx_macro_config_compander(struct snd_soc_codec *codec, + struct rx_macro_priv *rx_priv, + int interp_n, int event) +{ + int comp = 0; + u16 comp_ctl0_reg = 0, rx_path_cfg0_reg = 0; + + /* AUX does not have compander */ + if (interp_n == INTERP_AUX) + return 0; + + comp = interp_n; + dev_dbg(codec->dev, "%s: event %d compander %d, enabled %d\n", + __func__, event, comp + 1, rx_priv->comp_enabled[comp]); + + if (!rx_priv->comp_enabled[comp]) + return 0; + + comp_ctl0_reg = BOLERO_CDC_RX_COMPANDER0_CTL0 + + (comp * RX_MACRO_COMP_OFFSET); + rx_path_cfg0_reg = BOLERO_CDC_RX_RX0_RX_PATH_CFG0 + + (comp * RX_MACRO_RX_PATH_OFFSET); + + if (SND_SOC_DAPM_EVENT_ON(event)) { + /* Enable Compander Clock */ + snd_soc_update_bits(codec, comp_ctl0_reg, 0x01, 0x01); + snd_soc_update_bits(codec, comp_ctl0_reg, 0x02, 0x02); + snd_soc_update_bits(codec, comp_ctl0_reg, 0x02, 0x00); + snd_soc_update_bits(codec, rx_path_cfg0_reg, 0x02, 0x02); + } + + if (SND_SOC_DAPM_EVENT_OFF(event)) { + snd_soc_update_bits(codec, comp_ctl0_reg, 0x04, 0x04); + snd_soc_update_bits(codec, rx_path_cfg0_reg, 0x02, 0x00); + snd_soc_update_bits(codec, comp_ctl0_reg, 0x01, 0x00); + snd_soc_update_bits(codec, comp_ctl0_reg, 0x04, 0x00); + } + + return 0; +} + +static void rx_macro_enable_softclip_clk(struct snd_soc_codec *codec, + struct rx_macro_priv *rx_priv, + bool enable) +{ + if (enable) { + if (rx_priv->softclip_clk_users == 0) + snd_soc_update_bits(codec, + BOLERO_CDC_RX_SOFTCLIP_CRC, + 0x01, 0x01); + rx_priv->softclip_clk_users++; + } else { + rx_priv->softclip_clk_users--; + if (rx_priv->softclip_clk_users == 0) + snd_soc_update_bits(codec, + BOLERO_CDC_RX_SOFTCLIP_CRC, + 0x01, 0x00); + } +} + +static int rx_macro_config_softclip(struct snd_soc_codec *codec, + struct rx_macro_priv *rx_priv, + int event) +{ + dev_dbg(codec->dev, "%s: event %d, enabled %d\n", + __func__, event, rx_priv->is_softclip_on); + + if (!rx_priv->is_softclip_on) + return 0; + + if (SND_SOC_DAPM_EVENT_ON(event)) { + /* Enable Softclip clock */ + rx_macro_enable_softclip_clk(codec, rx_priv, true); + /* Enable Softclip control */ + snd_soc_update_bits(codec, + BOLERO_CDC_RX_SOFTCLIP_SOFTCLIP_CTRL, 0x01, 0x01); + } + + if (SND_SOC_DAPM_EVENT_OFF(event)) { + snd_soc_update_bits(codec, + BOLERO_CDC_RX_SOFTCLIP_SOFTCLIP_CTRL, 0x01, 0x00); + rx_macro_enable_softclip_clk(codec, rx_priv, false); + } + + return 0; +} + +static inline void +rx_macro_enable_clsh_block(struct rx_macro_priv *rx_priv, bool enable) +{ + if ((enable && ++rx_priv->clsh_users == 1) || + (!enable && --rx_priv->clsh_users == 0)) + snd_soc_update_bits(rx_priv->codec, + BOLERO_CDC_RX_CLSH_CRC, 0x01, + (u8) enable); + if (rx_priv->clsh_users < 0) + rx_priv->clsh_users = 0; + dev_dbg(rx_priv->dev, "%s: clsh_users %d, enable %d", __func__, + rx_priv->clsh_users, enable); +} + +static int rx_macro_config_classh(struct snd_soc_codec *codec, + struct rx_macro_priv *rx_priv, + int interp_n, int event) +{ + if (SND_SOC_DAPM_EVENT_OFF(event)) { + rx_macro_enable_clsh_block(rx_priv, false); + return 0; + } + + if (!SND_SOC_DAPM_EVENT_ON(event)) + return 0; + + rx_macro_enable_clsh_block(rx_priv, true); + if (interp_n == INTERP_HPHL || + interp_n == INTERP_HPHR) { + /* + * These K1 values depend on the Headphone Impedance + * For now it is assumed to be 16 ohm + */ + snd_soc_update_bits(codec, BOLERO_CDC_RX_CLSH_K1_LSB, + 0xFF, 0xC0); + snd_soc_update_bits(codec, BOLERO_CDC_RX_CLSH_K1_MSB, + 0x0F, 0x00); + } + switch (interp_n) { + case INTERP_HPHL: + if (rx_priv->is_ear_mode_on) + snd_soc_update_bits(codec, + BOLERO_CDC_RX_CLSH_HPH_V_PA, + 0x3F, 0x39); + else + snd_soc_update_bits(codec, + BOLERO_CDC_RX_CLSH_HPH_V_PA, + 0x3F, 0x1C); + snd_soc_update_bits(codec, BOLERO_CDC_RX_CLSH_DECAY_CTRL, + 0x07, 0x00); + snd_soc_update_bits(codec, BOLERO_CDC_RX_RX0_RX_PATH_CFG0, + 0x40, 0x40); + break; + case INTERP_HPHR: + snd_soc_update_bits(codec, BOLERO_CDC_RX_CLSH_HPH_V_PA, + 0x3F, 0x1C); + snd_soc_update_bits(codec, BOLERO_CDC_RX_CLSH_DECAY_CTRL, + 0x07, 0x00); + snd_soc_update_bits(codec, BOLERO_CDC_RX_RX1_RX_PATH_CFG0, + 0x40, 0x40); + break; + case INTERP_AUX: + snd_soc_update_bits(codec, BOLERO_CDC_RX_RX2_RX_PATH_CFG0, + 0x10, 0x10); + break; + } + + return 0; +} + +static void rx_macro_hd2_control(struct snd_soc_codec *codec, + u16 interp_idx, int event) +{ + u16 hd2_scale_reg = 0; + u16 hd2_enable_reg = 0; + + switch (interp_idx) { + case INTERP_HPHL: + hd2_scale_reg = BOLERO_CDC_RX_RX0_RX_PATH_SEC3; + hd2_enable_reg = BOLERO_CDC_RX_RX0_RX_PATH_CFG0; + break; + case INTERP_HPHR: + hd2_scale_reg = BOLERO_CDC_RX_RX1_RX_PATH_SEC3; + hd2_enable_reg = BOLERO_CDC_RX_RX1_RX_PATH_CFG0; + break; + } + + if (hd2_enable_reg && SND_SOC_DAPM_EVENT_ON(event)) { + snd_soc_update_bits(codec, hd2_scale_reg, 0x3C, 0x14); + snd_soc_update_bits(codec, hd2_enable_reg, 0x04, 0x04); + } + + if (hd2_enable_reg && SND_SOC_DAPM_EVENT_OFF(event)) { + snd_soc_update_bits(codec, hd2_enable_reg, 0x04, 0x00); + snd_soc_update_bits(codec, hd2_scale_reg, 0x3C, 0x00); + } +} + +static int rx_macro_hph_idle_detect_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct rx_macro_priv *rx_priv = NULL; + struct device *rx_dev = NULL; + + if (!rx_macro_get_data(codec, &rx_dev, &rx_priv, __func__)) + return -EINVAL; + + ucontrol->value.integer.value[0] = + rx_priv->idle_det_cfg.hph_idle_detect_en; + + return 0; +} + +static int rx_macro_hph_idle_detect_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct rx_macro_priv *rx_priv = NULL; + struct device *rx_dev = NULL; + + if (!rx_macro_get_data(codec, &rx_dev, &rx_priv, __func__)) + return -EINVAL; + + rx_priv->idle_det_cfg.hph_idle_detect_en = + ucontrol->value.integer.value[0]; + + return 0; +} + +static int rx_macro_get_compander(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + int comp = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + struct device *rx_dev = NULL; + struct rx_macro_priv *rx_priv = NULL; + + if (!rx_macro_get_data(codec, &rx_dev, &rx_priv, __func__)) + return -EINVAL; + + ucontrol->value.integer.value[0] = rx_priv->comp_enabled[comp]; + return 0; +} + +static int rx_macro_set_compander(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + int comp = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + int value = ucontrol->value.integer.value[0]; + struct device *rx_dev = NULL; + struct rx_macro_priv *rx_priv = NULL; + + if (!rx_macro_get_data(codec, &rx_dev, &rx_priv, __func__)) + return -EINVAL; + + dev_dbg(codec->dev, "%s: Compander %d enable current %d, new %d\n", + __func__, comp + 1, rx_priv->comp_enabled[comp], value); + rx_priv->comp_enabled[comp] = value; + + return 0; +} + +static int rx_macro_mux_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm); + struct device *rx_dev = NULL; + struct rx_macro_priv *rx_priv = NULL; + + if (!rx_macro_get_data(codec, &rx_dev, &rx_priv, __func__)) + return -EINVAL; + + ucontrol->value.integer.value[0] = + rx_priv->rx_port_value[widget->shift]; + return 0; +} + +static int rx_macro_mux_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + struct snd_soc_dapm_update *update = NULL; + u32 rx_port_value = ucontrol->value.integer.value[0]; + u32 aif_rst = 0; + struct device *rx_dev = NULL; + struct rx_macro_priv *rx_priv = NULL; + + if (!rx_macro_get_data(codec, &rx_dev, &rx_priv, __func__)) + return -EINVAL; + + aif_rst = rx_priv->rx_port_value[widget->shift]; + if (!rx_port_value) { + if (aif_rst == 0) { + dev_err(rx_dev, "%s:AIF reset already\n", __func__); + return 0; + } + } + rx_priv->rx_port_value[widget->shift] = rx_port_value; + + switch (rx_port_value) { + case 0: + clear_bit(widget->shift, + &rx_priv->active_ch_mask[aif_rst]); + rx_priv->active_ch_cnt[aif_rst]--; + break; + case 1: + case 2: + case 3: + case 4: + set_bit(widget->shift, + &rx_priv->active_ch_mask[rx_port_value]); + rx_priv->active_ch_cnt[rx_port_value]++; + break; + default: + dev_err(codec->dev, + "%s:Invalid AIF_ID for RX_MACRO MUX\n", __func__); + goto err; + } + + snd_soc_dapm_mux_update_power(widget->dapm, kcontrol, + rx_port_value, e, update); + return 0; +err: + return -EINVAL; +} + +static int rx_macro_get_ear_mode(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct device *rx_dev = NULL; + struct rx_macro_priv *rx_priv = NULL; + + if (!rx_macro_get_data(codec, &rx_dev, &rx_priv, __func__)) + return -EINVAL; + + ucontrol->value.integer.value[0] = rx_priv->is_ear_mode_on; + return 0; +} + +static int rx_macro_put_ear_mode(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct device *rx_dev = NULL; + struct rx_macro_priv *rx_priv = NULL; + + if (!rx_macro_get_data(codec, &rx_dev, &rx_priv, __func__)) + return -EINVAL; + + rx_priv->is_ear_mode_on = + (!ucontrol->value.integer.value[0] ? false : true); + return 0; +} + +static int rx_macro_get_hph_hd2_mode(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct device *rx_dev = NULL; + struct rx_macro_priv *rx_priv = NULL; + + if (!rx_macro_get_data(codec, &rx_dev, &rx_priv, __func__)) + return -EINVAL; + + ucontrol->value.integer.value[0] = rx_priv->hph_hd2_mode; + return 0; +} + +static int rx_macro_put_hph_hd2_mode(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct device *rx_dev = NULL; + struct rx_macro_priv *rx_priv = NULL; + + if (!rx_macro_get_data(codec, &rx_dev, &rx_priv, __func__)) + return -EINVAL; + + rx_priv->hph_hd2_mode = ucontrol->value.integer.value[0]; + return 0; +} + +static int rx_macro_get_hph_pwr_mode(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct device *rx_dev = NULL; + struct rx_macro_priv *rx_priv = NULL; + + if (!rx_macro_get_data(codec, &rx_dev, &rx_priv, __func__)) + return -EINVAL; + + ucontrol->value.integer.value[0] = rx_priv->hph_pwr_mode; + return 0; +} + +static int rx_macro_put_hph_pwr_mode(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct device *rx_dev = NULL; + struct rx_macro_priv *rx_priv = NULL; + + if (!rx_macro_get_data(codec, &rx_dev, &rx_priv, __func__)) + return -EINVAL; + + rx_priv->hph_pwr_mode = ucontrol->value.integer.value[0]; + return 0; +} + +static int rx_macro_vbat_bcl_gsm_mode_func_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + + ucontrol->value.integer.value[0] = + ((snd_soc_read(codec, BOLERO_CDC_RX_BCL_VBAT_CFG) & 0x04) ? + 1 : 0); + + dev_dbg(codec->dev, "%s: value: %lu\n", __func__, + ucontrol->value.integer.value[0]); + + return 0; +} + +static int rx_macro_vbat_bcl_gsm_mode_func_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + + dev_dbg(codec->dev, "%s: value: %lu\n", __func__, + ucontrol->value.integer.value[0]); + + /* Set Vbat register configuration for GSM mode bit based on value */ + if (ucontrol->value.integer.value[0]) + snd_soc_update_bits(codec, BOLERO_CDC_RX_BCL_VBAT_CFG, + 0x04, 0x04); + else + snd_soc_update_bits(codec, BOLERO_CDC_RX_BCL_VBAT_CFG, + 0x04, 0x00); + + return 0; +} + +static int rx_macro_soft_clip_enable_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct device *rx_dev = NULL; + struct rx_macro_priv *rx_priv = NULL; + + if (!rx_macro_get_data(codec, &rx_dev, &rx_priv, __func__)) + return -EINVAL; + + ucontrol->value.integer.value[0] = rx_priv->is_softclip_on; + + dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n", + __func__, ucontrol->value.integer.value[0]); + + return 0; +} + +static int rx_macro_soft_clip_enable_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct device *rx_dev = NULL; + struct rx_macro_priv *rx_priv = NULL; + + if (!rx_macro_get_data(codec, &rx_dev, &rx_priv, __func__)) + return -EINVAL; + + rx_priv->is_softclip_on = ucontrol->value.integer.value[0]; + + dev_dbg(codec->dev, "%s: soft clip enable = %d\n", __func__, + rx_priv->is_softclip_on); + + return 0; +} + +static int rx_macro_enable_vbat(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct device *rx_dev = NULL; + struct rx_macro_priv *rx_priv = NULL; + + dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event); + if (!rx_macro_get_data(codec, &rx_dev, &rx_priv, __func__)) + return -EINVAL; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* Enable clock for VBAT block */ + snd_soc_update_bits(codec, + BOLERO_CDC_RX_BCL_VBAT_PATH_CTL, 0x10, 0x10); + /* Enable VBAT block */ + snd_soc_update_bits(codec, + BOLERO_CDC_RX_BCL_VBAT_CFG, 0x01, 0x01); + /* Update interpolator with 384K path */ + snd_soc_update_bits(codec, BOLERO_CDC_RX_RX2_RX_PATH_CFG1, + 0x80, 0x80); + /* Update DSM FS rate */ + snd_soc_update_bits(codec, BOLERO_CDC_RX_RX2_RX_PATH_SEC7, + 0x02, 0x02); + /* Use attenuation mode */ + snd_soc_update_bits(codec, BOLERO_CDC_RX_BCL_VBAT_CFG, + 0x02, 0x00); + /* BCL block needs softclip clock to be enabled */ + rx_macro_enable_softclip_clk(codec, rx_priv, true); + /* Enable VBAT at channel level */ + snd_soc_update_bits(codec, BOLERO_CDC_RX_RX2_RX_PATH_CFG1, + 0x02, 0x02); + /* Set the ATTK1 gain */ + snd_soc_update_bits(codec, + BOLERO_CDC_RX_BCL_VBAT_BCL_GAIN_UPD1, + 0xFF, 0xFF); + snd_soc_update_bits(codec, + BOLERO_CDC_RX_BCL_VBAT_BCL_GAIN_UPD2, + 0xFF, 0x03); + snd_soc_update_bits(codec, + BOLERO_CDC_RX_BCL_VBAT_BCL_GAIN_UPD3, + 0xFF, 0x00); + /* Set the ATTK2 gain */ + snd_soc_update_bits(codec, + BOLERO_CDC_RX_BCL_VBAT_BCL_GAIN_UPD4, + 0xFF, 0xFF); + snd_soc_update_bits(codec, + BOLERO_CDC_RX_BCL_VBAT_BCL_GAIN_UPD5, + 0xFF, 0x03); + snd_soc_update_bits(codec, + BOLERO_CDC_RX_BCL_VBAT_BCL_GAIN_UPD6, + 0xFF, 0x00); + /* Set the ATTK3 gain */ + snd_soc_update_bits(codec, + BOLERO_CDC_RX_BCL_VBAT_BCL_GAIN_UPD7, + 0xFF, 0xFF); + snd_soc_update_bits(codec, + BOLERO_CDC_RX_BCL_VBAT_BCL_GAIN_UPD8, + 0xFF, 0x03); + snd_soc_update_bits(codec, + BOLERO_CDC_RX_BCL_VBAT_BCL_GAIN_UPD9, + 0xFF, 0x00); + break; + + case SND_SOC_DAPM_POST_PMD: + snd_soc_update_bits(codec, BOLERO_CDC_RX_RX2_RX_PATH_CFG1, + 0x80, 0x00); + snd_soc_update_bits(codec, BOLERO_CDC_RX_RX2_RX_PATH_SEC7, + 0x02, 0x00); + snd_soc_update_bits(codec, BOLERO_CDC_RX_BCL_VBAT_CFG, + 0x02, 0x02); + snd_soc_update_bits(codec, BOLERO_CDC_RX_RX2_RX_PATH_CFG1, + 0x02, 0x00); + snd_soc_update_bits(codec, + BOLERO_CDC_RX_BCL_VBAT_BCL_GAIN_UPD1, + 0xFF, 0x00); + snd_soc_update_bits(codec, + BOLERO_CDC_RX_BCL_VBAT_BCL_GAIN_UPD2, + 0xFF, 0x00); + snd_soc_update_bits(codec, + BOLERO_CDC_RX_BCL_VBAT_BCL_GAIN_UPD3, + 0xFF, 0x00); + snd_soc_update_bits(codec, + BOLERO_CDC_RX_BCL_VBAT_BCL_GAIN_UPD4, + 0xFF, 0x00); + snd_soc_update_bits(codec, + BOLERO_CDC_RX_BCL_VBAT_BCL_GAIN_UPD5, + 0xFF, 0x00); + snd_soc_update_bits(codec, + BOLERO_CDC_RX_BCL_VBAT_BCL_GAIN_UPD6, + 0xFF, 0x00); + snd_soc_update_bits(codec, + BOLERO_CDC_RX_BCL_VBAT_BCL_GAIN_UPD7, + 0xFF, 0x00); + snd_soc_update_bits(codec, + BOLERO_CDC_RX_BCL_VBAT_BCL_GAIN_UPD8, + 0xFF, 0x00); + snd_soc_update_bits(codec, + BOLERO_CDC_RX_BCL_VBAT_BCL_GAIN_UPD9, + 0xFF, 0x00); + rx_macro_enable_softclip_clk(codec, rx_priv, false); + snd_soc_update_bits(codec, + BOLERO_CDC_RX_BCL_VBAT_CFG, 0x01, 0x00); + snd_soc_update_bits(codec, + BOLERO_CDC_RX_BCL_VBAT_PATH_CTL, 0x10, 0x00); + break; + default: + dev_err(rx_dev, "%s: Invalid event %d\n", __func__, event); + break; + } + return 0; +} + +static void rx_macro_idle_detect_control(struct snd_soc_codec *codec, + struct rx_macro_priv *rx_priv, + int interp, int event) +{ + int reg = 0, mask = 0, val = 0; + + if (!rx_priv->idle_det_cfg.hph_idle_detect_en) + return; + + if (interp == INTERP_HPHL) { + reg = BOLERO_CDC_RX_IDLE_DETECT_PATH_CTL; + mask = 0x01; + val = 0x01; + } + if (interp == INTERP_HPHR) { + reg = BOLERO_CDC_RX_IDLE_DETECT_PATH_CTL; + mask = 0x02; + val = 0x02; + } + + if (reg && SND_SOC_DAPM_EVENT_ON(event)) + snd_soc_update_bits(codec, reg, mask, val); + + if (reg && SND_SOC_DAPM_EVENT_OFF(event)) { + snd_soc_update_bits(codec, reg, mask, 0x00); + rx_priv->idle_det_cfg.hph_idle_thr = 0; + snd_soc_write(codec, BOLERO_CDC_RX_IDLE_DETECT_CFG3, 0x0); + } +} + +static void rx_macro_hphdelay_lutbypass(struct snd_soc_codec *codec, + struct rx_macro_priv *rx_priv, + u16 interp_idx, int event) +{ + u16 hph_lut_bypass_reg = 0; + u16 hph_comp_ctrl7 = 0; + + switch (interp_idx) { + case INTERP_HPHL: + hph_lut_bypass_reg = BOLERO_CDC_RX_TOP_HPHL_COMP_LUT; + hph_comp_ctrl7 = BOLERO_CDC_RX_COMPANDER0_CTL7; + break; + case INTERP_HPHR: + hph_lut_bypass_reg = BOLERO_CDC_RX_TOP_HPHR_COMP_LUT; + hph_comp_ctrl7 = BOLERO_CDC_RX_COMPANDER1_CTL7; + break; + default: + break; + } + + if (hph_lut_bypass_reg && SND_SOC_DAPM_EVENT_ON(event)) { + if (interp_idx == INTERP_HPHL) { + if (rx_priv->is_ear_mode_on) + snd_soc_update_bits(codec, + BOLERO_CDC_RX_RX0_RX_PATH_CFG1, + 0x02, 0x02); + else + snd_soc_update_bits(codec, + hph_lut_bypass_reg, + 0x80, 0x80); + } else { + snd_soc_update_bits(codec, + hph_lut_bypass_reg, + 0x80, 0x80); + } + if (rx_priv->hph_pwr_mode) + snd_soc_update_bits(codec, hph_comp_ctrl7, 0x20, 0x00); + } + + if (hph_lut_bypass_reg && SND_SOC_DAPM_EVENT_OFF(event)) { + snd_soc_update_bits(codec, BOLERO_CDC_RX_RX0_RX_PATH_CFG1, + 0x02, 0x00); + snd_soc_update_bits(codec, hph_lut_bypass_reg, 0x80, 0x00); + snd_soc_update_bits(codec, hph_comp_ctrl7, 0x20, 0x20); + } +} + +static int rx_macro_enable_interp_clk(struct snd_soc_codec *codec, + int event, int interp_idx) +{ + u16 main_reg = 0, dsm_reg = 0, rx_cfg2_reg = 0; + struct device *rx_dev = NULL; + struct rx_macro_priv *rx_priv = NULL; + + if (!codec) { + pr_err("%s: codec is NULL\n", __func__); + return -EINVAL; + } + + if (!rx_macro_get_data(codec, &rx_dev, &rx_priv, __func__)) + return -EINVAL; + + main_reg = BOLERO_CDC_RX_RX0_RX_PATH_CTL + + (interp_idx * RX_MACRO_RX_PATH_OFFSET); + dsm_reg = BOLERO_CDC_RX_RX0_RX_PATH_DSM_CTL + + (interp_idx * RX_MACRO_RX_PATH_OFFSET); + rx_cfg2_reg = BOLERO_CDC_RX_RX0_RX_PATH_CFG2 + + (interp_idx * RX_MACRO_RX_PATH_OFFSET); + + if (SND_SOC_DAPM_EVENT_ON(event)) { + if (rx_priv->main_clk_users[interp_idx] == 0) { + snd_soc_update_bits(codec, dsm_reg, 0x01, 0x01); + /* Main path PGA mute enable */ + snd_soc_update_bits(codec, main_reg, 0x10, 0x10); + /* Clk enable */ + snd_soc_update_bits(codec, main_reg, 0x20, 0x20); + snd_soc_update_bits(codec, rx_cfg2_reg, 0x03, 0x03); + rx_macro_idle_detect_control(codec, rx_priv, + interp_idx, event); + if (rx_priv->hph_hd2_mode) + rx_macro_hd2_control(codec, interp_idx, event); + rx_macro_hphdelay_lutbypass(codec, rx_priv, interp_idx, + event); + rx_macro_config_compander(codec, rx_priv, + interp_idx, event); + if (interp_idx == INTERP_AUX) + rx_macro_config_softclip(codec, rx_priv, + event); + rx_macro_config_classh(codec, rx_priv, + interp_idx, event); + } + rx_priv->main_clk_users[interp_idx]++; + } + + if (SND_SOC_DAPM_EVENT_OFF(event)) { + rx_priv->main_clk_users[interp_idx]--; + if (rx_priv->main_clk_users[interp_idx] <= 0) { + rx_priv->main_clk_users[interp_idx] = 0; + /* Clk Disable */ + snd_soc_update_bits(codec, dsm_reg, 0x01, 0x00); + snd_soc_update_bits(codec, main_reg, 0x20, 0x00); + /* Reset enable and disable */ + snd_soc_update_bits(codec, main_reg, 0x40, 0x40); + snd_soc_update_bits(codec, main_reg, 0x40, 0x00); + /* Reset rate to 48K*/ + snd_soc_update_bits(codec, main_reg, 0x0F, 0x04); + snd_soc_update_bits(codec, rx_cfg2_reg, 0x03, 0x00); + rx_macro_config_classh(codec, rx_priv, + interp_idx, event); + rx_macro_config_compander(codec, rx_priv, + interp_idx, event); + if (interp_idx == INTERP_AUX) + rx_macro_config_softclip(codec, rx_priv, + event); + rx_macro_hphdelay_lutbypass(codec, rx_priv, interp_idx, + event); + if (rx_priv->hph_hd2_mode) + rx_macro_hd2_control(codec, interp_idx, event); + rx_macro_idle_detect_control(codec, rx_priv, + interp_idx, event); + } + } + + dev_dbg(codec->dev, "%s event %d main_clk_users %d\n", + __func__, event, rx_priv->main_clk_users[interp_idx]); + + return rx_priv->main_clk_users[interp_idx]; +} + +static int rx_macro_enable_rx_path_clk(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + u16 sidetone_reg = 0; + + dev_dbg(codec->dev, "%s %d %d\n", __func__, event, w->shift); + sidetone_reg = BOLERO_CDC_RX_RX0_RX_PATH_CFG1 + + RX_MACRO_RX_PATH_OFFSET * (w->shift); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + rx_macro_enable_interp_clk(codec, event, w->shift); + snd_soc_update_bits(codec, sidetone_reg, 0x10, 0x10); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_update_bits(codec, sidetone_reg, 0x10, 0x00); + rx_macro_enable_interp_clk(codec, event, w->shift); + break; + default: + break; + }; + return 0; +} + +static void rx_macro_restore_iir_coeff(struct rx_macro_priv *rx_priv, int iir_idx, + int band_idx) +{ + u16 reg_add = 0, coeff_idx = 0, idx = 0; + struct regmap *regmap = dev_get_regmap(rx_priv->dev->parent, NULL); + + if (regmap == NULL) { + dev_err(rx_priv->dev, "%s: regmap is NULL\n", __func__); + return; + } + + regmap_write(regmap, + (BOLERO_CDC_RX_SIDETONE_IIR0_IIR_COEF_B1_CTL + 0x80 * iir_idx), + (band_idx * BAND_MAX * sizeof(uint32_t)) & 0x7F); + + reg_add = BOLERO_CDC_RX_SIDETONE_IIR0_IIR_COEF_B2_CTL + 0x80 * iir_idx; + + /* 5 coefficients per band and 4 writes per coefficient */ + for (coeff_idx = 0; coeff_idx < RX_MACRO_SIDETONE_IIR_COEFF_MAX; + coeff_idx++) { + /* Four 8 bit values(one 32 bit) per coefficient */ + regmap_write(regmap, reg_add, + rx_priv->sidetone_coeff_array[iir_idx][band_idx][idx++]); + regmap_write(regmap, reg_add, + rx_priv->sidetone_coeff_array[iir_idx][band_idx][idx++]); + regmap_write(regmap, reg_add, + rx_priv->sidetone_coeff_array[iir_idx][band_idx][idx++]); + regmap_write(regmap, reg_add, + rx_priv->sidetone_coeff_array[iir_idx][band_idx][idx++]); + } +} + +static int rx_macro_iir_enable_audio_mixer_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + int iir_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->reg; + int band_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + /* IIR filter band registers are at integer multiples of 0x80 */ + u16 iir_reg = BOLERO_CDC_RX_SIDETONE_IIR0_IIR_CTL + 0x80 * iir_idx; + + ucontrol->value.integer.value[0] = (snd_soc_read(codec, iir_reg) & + (1 << band_idx)) != 0; + + dev_dbg(codec->dev, "%s: IIR #%d band #%d enable %d\n", __func__, + iir_idx, band_idx, + (uint32_t)ucontrol->value.integer.value[0]); + return 0; +} + +static int rx_macro_iir_enable_audio_mixer_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + int iir_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->reg; + int band_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + bool iir_band_en_status = 0; + int value = ucontrol->value.integer.value[0]; + u16 iir_reg = BOLERO_CDC_RX_SIDETONE_IIR0_IIR_CTL + 0x80 * iir_idx; + struct device *rx_dev = NULL; + struct rx_macro_priv *rx_priv = NULL; + + if (!rx_macro_get_data(codec, &rx_dev, &rx_priv, __func__)) + return -EINVAL; + + rx_macro_restore_iir_coeff(rx_priv, iir_idx, band_idx); + + /* Mask first 5 bits, 6-8 are reserved */ + snd_soc_update_bits(codec, iir_reg, (1 << band_idx), + (value << band_idx)); + + iir_band_en_status = ((snd_soc_read(codec, iir_reg) & + (1 << band_idx)) != 0); + dev_dbg(codec->dev, "%s: IIR #%d band #%d enable %d\n", __func__, + iir_idx, band_idx, iir_band_en_status); + return 0; +} + +static uint32_t get_iir_band_coeff(struct snd_soc_codec *codec, + int iir_idx, int band_idx, + int coeff_idx) +{ + uint32_t value = 0; + + /* Address does not automatically update if reading */ + snd_soc_write(codec, + (BOLERO_CDC_RX_SIDETONE_IIR0_IIR_COEF_B1_CTL + 0x80 * iir_idx), + ((band_idx * BAND_MAX + coeff_idx) + * sizeof(uint32_t)) & 0x7F); + + value |= snd_soc_read(codec, + (BOLERO_CDC_RX_SIDETONE_IIR0_IIR_COEF_B2_CTL + 0x80 * iir_idx)); + + snd_soc_write(codec, + (BOLERO_CDC_RX_SIDETONE_IIR0_IIR_COEF_B1_CTL + 0x80 * iir_idx), + ((band_idx * BAND_MAX + coeff_idx) + * sizeof(uint32_t) + 1) & 0x7F); + + value |= (snd_soc_read(codec, + (BOLERO_CDC_RX_SIDETONE_IIR0_IIR_COEF_B2_CTL + + 0x80 * iir_idx)) << 8); + + snd_soc_write(codec, + (BOLERO_CDC_RX_SIDETONE_IIR0_IIR_COEF_B1_CTL + 0x80 * iir_idx), + ((band_idx * BAND_MAX + coeff_idx) + * sizeof(uint32_t) + 2) & 0x7F); + + value |= (snd_soc_read(codec, + (BOLERO_CDC_RX_SIDETONE_IIR0_IIR_COEF_B2_CTL + + 0x80 * iir_idx)) << 16); + + snd_soc_write(codec, + (BOLERO_CDC_RX_SIDETONE_IIR0_IIR_COEF_B1_CTL + 0x80 * iir_idx), + ((band_idx * BAND_MAX + coeff_idx) + * sizeof(uint32_t) + 3) & 0x7F); + + /* Mask bits top 2 bits since they are reserved */ + value |= ((snd_soc_read(codec, + (BOLERO_CDC_RX_SIDETONE_IIR0_IIR_COEF_B2_CTL + + 16 * iir_idx)) & 0x3F) << 24); + + return value; +} + +static int rx_macro_iir_band_audio_mixer_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + int iir_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->reg; + int band_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + + ucontrol->value.integer.value[0] = + get_iir_band_coeff(codec, iir_idx, band_idx, 0); + ucontrol->value.integer.value[1] = + get_iir_band_coeff(codec, iir_idx, band_idx, 1); + ucontrol->value.integer.value[2] = + get_iir_band_coeff(codec, iir_idx, band_idx, 2); + ucontrol->value.integer.value[3] = + get_iir_band_coeff(codec, iir_idx, band_idx, 3); + ucontrol->value.integer.value[4] = + get_iir_band_coeff(codec, iir_idx, band_idx, 4); + + dev_dbg(codec->dev, "%s: IIR #%d band #%d b0 = 0x%x\n" + "%s: IIR #%d band #%d b1 = 0x%x\n" + "%s: IIR #%d band #%d b2 = 0x%x\n" + "%s: IIR #%d band #%d a1 = 0x%x\n" + "%s: IIR #%d band #%d a2 = 0x%x\n", + __func__, iir_idx, band_idx, + (uint32_t)ucontrol->value.integer.value[0], + __func__, iir_idx, band_idx, + (uint32_t)ucontrol->value.integer.value[1], + __func__, iir_idx, band_idx, + (uint32_t)ucontrol->value.integer.value[2], + __func__, iir_idx, band_idx, + (uint32_t)ucontrol->value.integer.value[3], + __func__, iir_idx, band_idx, + (uint32_t)ucontrol->value.integer.value[4]); + return 0; +} + +static void set_iir_band_coeff(struct snd_soc_codec *codec, + int iir_idx, int band_idx, + uint32_t value) +{ + snd_soc_write(codec, + (BOLERO_CDC_RX_SIDETONE_IIR0_IIR_COEF_B2_CTL + 0x80 * iir_idx), + (value & 0xFF)); + + snd_soc_write(codec, + (BOLERO_CDC_RX_SIDETONE_IIR0_IIR_COEF_B2_CTL + 0x80 * iir_idx), + (value >> 8) & 0xFF); + + snd_soc_write(codec, + (BOLERO_CDC_RX_SIDETONE_IIR0_IIR_COEF_B2_CTL + 0x80 * iir_idx), + (value >> 16) & 0xFF); + + /* Mask top 2 bits, 7-8 are reserved */ + snd_soc_write(codec, + (BOLERO_CDC_RX_SIDETONE_IIR0_IIR_COEF_B2_CTL + 0x80 * iir_idx), + (value >> 24) & 0x3F); +} + +static int rx_macro_iir_band_audio_mixer_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + int iir_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->reg; + int band_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + int coeff_idx, idx = 0; + struct device *rx_dev = NULL; + struct rx_macro_priv *rx_priv = NULL; + + if (!rx_macro_get_data(codec, &rx_dev, &rx_priv, __func__)) + return -EINVAL; + + /* + * Mask top bit it is reserved + * Updates addr automatically for each B2 write + */ + snd_soc_write(codec, + (BOLERO_CDC_RX_SIDETONE_IIR0_IIR_COEF_B1_CTL + 16 * iir_idx), + (band_idx * BAND_MAX * sizeof(uint32_t)) & 0x7F); + + /* Store the coefficients in sidetone coeff array */ + for (coeff_idx = 0; coeff_idx < RX_MACRO_SIDETONE_IIR_COEFF_MAX; + coeff_idx++) { + uint32_t value = ucontrol->value.integer.value[coeff_idx]; + + set_iir_band_coeff(codec, iir_idx, band_idx, value); + + /* Four 8 bit values(one 32 bit) per coefficient */ + rx_priv->sidetone_coeff_array[iir_idx][band_idx][idx++] = + (value & 0xFF); + rx_priv->sidetone_coeff_array[iir_idx][band_idx][idx++] = + (value >> 8) & 0xFF; + rx_priv->sidetone_coeff_array[iir_idx][band_idx][idx++] = + (value >> 16) & 0xFF; + rx_priv->sidetone_coeff_array[iir_idx][band_idx][idx++] = + (value >> 24) & 0xFF; + } + + pr_debug("%s: IIR #%d band #%d b0 = 0x%x\n" + "%s: IIR #%d band #%d b1 = 0x%x\n" + "%s: IIR #%d band #%d b2 = 0x%x\n" + "%s: IIR #%d band #%d a1 = 0x%x\n" + "%s: IIR #%d band #%d a2 = 0x%x\n", + __func__, iir_idx, band_idx, + get_iir_band_coeff(codec, iir_idx, band_idx, 0), + __func__, iir_idx, band_idx, + get_iir_band_coeff(codec, iir_idx, band_idx, 1), + __func__, iir_idx, band_idx, + get_iir_band_coeff(codec, iir_idx, band_idx, 2), + __func__, iir_idx, band_idx, + get_iir_band_coeff(codec, iir_idx, band_idx, 3), + __func__, iir_idx, band_idx, + get_iir_band_coeff(codec, iir_idx, band_idx, 4)); + return 0; +} + +static int rx_macro_set_iir_gain(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + + dev_dbg(codec->dev, "%s: event = %d\n", __func__, event); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: /* fall through */ + case SND_SOC_DAPM_PRE_PMD: + if (strnstr(w->name, "IIR0", sizeof("IIR0"))) { + snd_soc_write(codec, + BOLERO_CDC_RX_SIDETONE_IIR0_IIR_GAIN_B1_CTL, + snd_soc_read(codec, + BOLERO_CDC_RX_SIDETONE_IIR0_IIR_GAIN_B1_CTL)); + snd_soc_write(codec, + BOLERO_CDC_RX_SIDETONE_IIR0_IIR_GAIN_B2_CTL, + snd_soc_read(codec, + BOLERO_CDC_RX_SIDETONE_IIR0_IIR_GAIN_B2_CTL)); + snd_soc_write(codec, + BOLERO_CDC_RX_SIDETONE_IIR0_IIR_GAIN_B3_CTL, + snd_soc_read(codec, + BOLERO_CDC_RX_SIDETONE_IIR0_IIR_GAIN_B3_CTL)); + snd_soc_write(codec, + BOLERO_CDC_RX_SIDETONE_IIR0_IIR_GAIN_B4_CTL, + snd_soc_read(codec, + BOLERO_CDC_RX_SIDETONE_IIR0_IIR_GAIN_B4_CTL)); + } else { + snd_soc_write(codec, + BOLERO_CDC_RX_SIDETONE_IIR1_IIR_GAIN_B1_CTL, + snd_soc_read(codec, + BOLERO_CDC_RX_SIDETONE_IIR1_IIR_GAIN_B1_CTL)); + snd_soc_write(codec, + BOLERO_CDC_RX_SIDETONE_IIR1_IIR_GAIN_B2_CTL, + snd_soc_read(codec, + BOLERO_CDC_RX_SIDETONE_IIR1_IIR_GAIN_B2_CTL)); + snd_soc_write(codec, + BOLERO_CDC_RX_SIDETONE_IIR1_IIR_GAIN_B3_CTL, + snd_soc_read(codec, + BOLERO_CDC_RX_SIDETONE_IIR1_IIR_GAIN_B3_CTL)); + snd_soc_write(codec, + BOLERO_CDC_RX_SIDETONE_IIR1_IIR_GAIN_B4_CTL, + snd_soc_read(codec, + BOLERO_CDC_RX_SIDETONE_IIR1_IIR_GAIN_B4_CTL)); + } + break; + } + return 0; +} + +static const struct snd_kcontrol_new rx_macro_snd_controls[] = { + SOC_SINGLE_SX_TLV("RX_RX0 Digital Volume", + BOLERO_CDC_RX_RX0_RX_VOL_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("RX_RX1 Digital Volume", + BOLERO_CDC_RX_RX1_RX_VOL_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("RX_RX2 Digital Volume", + BOLERO_CDC_RX_RX2_RX_VOL_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("RX_RX0 Mix Digital Volume", + BOLERO_CDC_RX_RX0_RX_VOL_MIX_CTL, 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("RX_RX1 Mix Digital Volume", + BOLERO_CDC_RX_RX1_RX_VOL_MIX_CTL, 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("RX_RX2 Mix Digital Volume", + BOLERO_CDC_RX_RX2_RX_VOL_MIX_CTL, 0, -84, 40, digital_gain), + + SOC_SINGLE_EXT("RX_COMP1 Switch", SND_SOC_NOPM, RX_MACRO_COMP1, 1, 0, + rx_macro_get_compander, rx_macro_set_compander), + SOC_SINGLE_EXT("RX_COMP2 Switch", SND_SOC_NOPM, RX_MACRO_COMP2, 1, 0, + rx_macro_get_compander, rx_macro_set_compander), + + SOC_ENUM_EXT("HPH Idle Detect", hph_idle_detect_enum, + rx_macro_hph_idle_detect_get, rx_macro_hph_idle_detect_put), + + SOC_ENUM_EXT("RX_EAR Mode", rx_macro_ear_mode_enum, + rx_macro_get_ear_mode, rx_macro_put_ear_mode), + + SOC_ENUM_EXT("RX_HPH HD2 Mode", rx_macro_hph_hd2_mode_enum, + rx_macro_get_hph_hd2_mode, rx_macro_put_hph_hd2_mode), + + SOC_ENUM_EXT("RX_HPH_PWR_MODE", rx_macro_hph_pwr_mode_enum, + rx_macro_get_hph_pwr_mode, rx_macro_put_hph_pwr_mode), + + SOC_ENUM_EXT("RX_GSM mode Enable", rx_macro_vbat_bcl_gsm_mode_enum, + rx_macro_vbat_bcl_gsm_mode_func_get, + rx_macro_vbat_bcl_gsm_mode_func_put), + SOC_SINGLE_EXT("RX_Softclip Enable", SND_SOC_NOPM, 0, 1, 0, + rx_macro_soft_clip_enable_get, + rx_macro_soft_clip_enable_put), + + SOC_SINGLE_SX_TLV("IIR0 INP0 Volume", + BOLERO_CDC_RX_SIDETONE_IIR0_IIR_GAIN_B1_CTL, 0, -84, 40, + digital_gain), + SOC_SINGLE_SX_TLV("IIR0 INP1 Volume", + BOLERO_CDC_RX_SIDETONE_IIR0_IIR_GAIN_B2_CTL, 0, -84, 40, + digital_gain), + SOC_SINGLE_SX_TLV("IIR0 INP2 Volume", + BOLERO_CDC_RX_SIDETONE_IIR0_IIR_GAIN_B3_CTL, 0, -84, 40, + digital_gain), + SOC_SINGLE_SX_TLV("IIR0 INP3 Volume", + BOLERO_CDC_RX_SIDETONE_IIR0_IIR_GAIN_B4_CTL, 0, -84, 40, + digital_gain), + SOC_SINGLE_SX_TLV("IIR1 INP0 Volume", + BOLERO_CDC_RX_SIDETONE_IIR1_IIR_GAIN_B1_CTL, 0, -84, 40, + digital_gain), + SOC_SINGLE_SX_TLV("IIR1 INP1 Volume", + BOLERO_CDC_RX_SIDETONE_IIR1_IIR_GAIN_B2_CTL, 0, -84, 40, + digital_gain), + SOC_SINGLE_SX_TLV("IIR1 INP2 Volume", + BOLERO_CDC_RX_SIDETONE_IIR1_IIR_GAIN_B3_CTL, 0, -84, 40, + digital_gain), + SOC_SINGLE_SX_TLV("IIR1 INP3 Volume", + BOLERO_CDC_RX_SIDETONE_IIR1_IIR_GAIN_B4_CTL, 0, -84, 40, + digital_gain), + + SOC_SINGLE_EXT("IIR0 Enable Band1", IIR0, BAND1, 1, 0, + rx_macro_iir_enable_audio_mixer_get, + rx_macro_iir_enable_audio_mixer_put), + SOC_SINGLE_EXT("IIR0 Enable Band2", IIR0, BAND2, 1, 0, + rx_macro_iir_enable_audio_mixer_get, + rx_macro_iir_enable_audio_mixer_put), + SOC_SINGLE_EXT("IIR0 Enable Band3", IIR0, BAND3, 1, 0, + rx_macro_iir_enable_audio_mixer_get, + rx_macro_iir_enable_audio_mixer_put), + SOC_SINGLE_EXT("IIR0 Enable Band4", IIR0, BAND4, 1, 0, + rx_macro_iir_enable_audio_mixer_get, + rx_macro_iir_enable_audio_mixer_put), + SOC_SINGLE_EXT("IIR0 Enable Band5", IIR0, BAND5, 1, 0, + rx_macro_iir_enable_audio_mixer_get, + rx_macro_iir_enable_audio_mixer_put), + SOC_SINGLE_EXT("IIR1 Enable Band1", IIR1, BAND1, 1, 0, + rx_macro_iir_enable_audio_mixer_get, + rx_macro_iir_enable_audio_mixer_put), + SOC_SINGLE_EXT("IIR1 Enable Band2", IIR1, BAND2, 1, 0, + rx_macro_iir_enable_audio_mixer_get, + rx_macro_iir_enable_audio_mixer_put), + SOC_SINGLE_EXT("IIR1 Enable Band3", IIR1, BAND3, 1, 0, + rx_macro_iir_enable_audio_mixer_get, + rx_macro_iir_enable_audio_mixer_put), + SOC_SINGLE_EXT("IIR1 Enable Band4", IIR1, BAND4, 1, 0, + rx_macro_iir_enable_audio_mixer_get, + rx_macro_iir_enable_audio_mixer_put), + SOC_SINGLE_EXT("IIR1 Enable Band5", IIR1, BAND5, 1, 0, + rx_macro_iir_enable_audio_mixer_get, + rx_macro_iir_enable_audio_mixer_put), + + SOC_SINGLE_MULTI_EXT("IIR0 Band1", IIR0, BAND1, 255, 0, 5, + rx_macro_iir_band_audio_mixer_get, + rx_macro_iir_band_audio_mixer_put), + SOC_SINGLE_MULTI_EXT("IIR0 Band2", IIR0, BAND2, 255, 0, 5, + rx_macro_iir_band_audio_mixer_get, + rx_macro_iir_band_audio_mixer_put), + SOC_SINGLE_MULTI_EXT("IIR0 Band3", IIR0, BAND3, 255, 0, 5, + rx_macro_iir_band_audio_mixer_get, + rx_macro_iir_band_audio_mixer_put), + SOC_SINGLE_MULTI_EXT("IIR0 Band4", IIR0, BAND4, 255, 0, 5, + rx_macro_iir_band_audio_mixer_get, + rx_macro_iir_band_audio_mixer_put), + SOC_SINGLE_MULTI_EXT("IIR0 Band5", IIR0, BAND5, 255, 0, 5, + rx_macro_iir_band_audio_mixer_get, + rx_macro_iir_band_audio_mixer_put), + SOC_SINGLE_MULTI_EXT("IIR1 Band1", IIR1, BAND1, 255, 0, 5, + rx_macro_iir_band_audio_mixer_get, + rx_macro_iir_band_audio_mixer_put), + SOC_SINGLE_MULTI_EXT("IIR1 Band2", IIR1, BAND2, 255, 0, 5, + rx_macro_iir_band_audio_mixer_get, + rx_macro_iir_band_audio_mixer_put), + SOC_SINGLE_MULTI_EXT("IIR1 Band3", IIR1, BAND3, 255, 0, 5, + rx_macro_iir_band_audio_mixer_get, + rx_macro_iir_band_audio_mixer_put), + SOC_SINGLE_MULTI_EXT("IIR1 Band4", IIR1, BAND4, 255, 0, 5, + rx_macro_iir_band_audio_mixer_get, + rx_macro_iir_band_audio_mixer_put), + SOC_SINGLE_MULTI_EXT("IIR1 Band5", IIR1, BAND5, 255, 0, 5, + rx_macro_iir_band_audio_mixer_get, + rx_macro_iir_band_audio_mixer_put), +}; + +static int rx_macro_enable_echo(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct device *rx_dev = NULL; + struct rx_macro_priv *rx_priv = NULL; + u16 val = 0, ec_hq_reg = 0; + int ec_tx = 0; + + if (!rx_macro_get_data(codec, &rx_dev, &rx_priv, __func__)) + return -EINVAL; + + dev_dbg(rx_dev, "%s %d %s\n", __func__, event, w->name); + + val = snd_soc_read(codec, BOLERO_CDC_RX_INP_MUX_RX_MIX_CFG4); + if (!(strcmp(w->name, "RX MIX TX0 MUX"))) + ec_tx = ((val & 0xf0) >> 0x4) - 1; + else if (!(strcmp(w->name, "RX MIX TX1 MUX"))) + ec_tx = (val & 0x0f) - 1; + + val = snd_soc_read(codec, BOLERO_CDC_RX_INP_MUX_RX_MIX_CFG5); + if (!(strcmp(w->name, "RX MIX TX2 MUX"))) + ec_tx = (val & 0x0f) - 1; + + if (ec_tx < 0 || (ec_tx >= RX_MACRO_EC_MUX_MAX)) { + dev_err(rx_dev, "%s: EC mix control not set correctly\n", + __func__); + return -EINVAL; + } + ec_hq_reg = BOLERO_CDC_RX_EC_REF_HQ0_EC_REF_HQ_PATH_CTL + + 0x40 * ec_tx; + snd_soc_update_bits(codec, ec_hq_reg, 0x01, 0x01); + ec_hq_reg = BOLERO_CDC_RX_EC_REF_HQ0_EC_REF_HQ_CFG0 + + 0x40 * ec_tx; + /* default set to 48k */ + snd_soc_update_bits(codec, ec_hq_reg, 0x1E, 0x08); + + return 0; +} + +static const struct snd_soc_dapm_widget rx_macro_dapm_widgets[] = { + SND_SOC_DAPM_AIF_IN("RX AIF1 PB", "RX_MACRO_AIF1 Playback", 0, + SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_AIF_IN("RX AIF2 PB", "RX_MACRO_AIF2 Playback", 0, + SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_AIF_IN("RX AIF3 PB", "RX_MACRO_AIF3 Playback", 0, + SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_AIF_IN("RX AIF4 PB", "RX_MACRO_AIF4 Playback", 0, + SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_AIF_OUT("RX AIF_ECHO", "RX_AIF_ECHO Capture", 0, + SND_SOC_NOPM, 0, 0), + + RX_MACRO_DAPM_MUX("RX_MACRO RX0 MUX", RX_MACRO_RX0, rx_macro_rx0), + RX_MACRO_DAPM_MUX("RX_MACRO RX1 MUX", RX_MACRO_RX1, rx_macro_rx1), + RX_MACRO_DAPM_MUX("RX_MACRO RX2 MUX", RX_MACRO_RX2, rx_macro_rx2), + RX_MACRO_DAPM_MUX("RX_MACRO RX3 MUX", RX_MACRO_RX3, rx_macro_rx3), + RX_MACRO_DAPM_MUX("RX_MACRO RX4 MUX", RX_MACRO_RX4, rx_macro_rx4), + RX_MACRO_DAPM_MUX("RX_MACRO RX5 MUX", RX_MACRO_RX5, rx_macro_rx5), + + SND_SOC_DAPM_MIXER("RX_RX0", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX_RX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX_RX2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX_RX3", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX_RX4", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX_RX5", SND_SOC_NOPM, 0, 0, NULL, 0), + + RX_MACRO_DAPM_MUX("IIR0 INP0 MUX", 0, iir0_inp0), + RX_MACRO_DAPM_MUX("IIR0 INP1 MUX", 0, iir0_inp1), + RX_MACRO_DAPM_MUX("IIR0 INP2 MUX", 0, iir0_inp2), + RX_MACRO_DAPM_MUX("IIR0 INP3 MUX", 0, iir0_inp3), + RX_MACRO_DAPM_MUX("IIR1 INP0 MUX", 0, iir1_inp0), + RX_MACRO_DAPM_MUX("IIR1 INP1 MUX", 0, iir1_inp1), + RX_MACRO_DAPM_MUX("IIR1 INP2 MUX", 0, iir1_inp2), + RX_MACRO_DAPM_MUX("IIR1 INP3 MUX", 0, iir1_inp3), + + SND_SOC_DAPM_MUX_E("RX MIX TX0 MUX", SND_SOC_NOPM, + RX_MACRO_EC0_MUX, 0, + &rx_mix_tx0_mux, rx_macro_enable_echo, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX MIX TX1 MUX", SND_SOC_NOPM, + RX_MACRO_EC1_MUX, 0, + &rx_mix_tx1_mux, rx_macro_enable_echo, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX MIX TX2 MUX", SND_SOC_NOPM, + RX_MACRO_EC2_MUX, 0, + &rx_mix_tx2_mux, rx_macro_enable_echo, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MIXER_E("IIR0", BOLERO_CDC_RX_SIDETONE_IIR0_IIR_PATH_CTL, + 4, 0, NULL, 0, rx_macro_set_iir_gain, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_MIXER_E("IIR1", BOLERO_CDC_RX_SIDETONE_IIR1_IIR_PATH_CTL, + 4, 0, NULL, 0, rx_macro_set_iir_gain, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_MIXER("SRC0", BOLERO_CDC_RX_SIDETONE_SRC0_ST_SRC_PATH_CTL, + 4, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SRC1", BOLERO_CDC_RX_SIDETONE_SRC1_ST_SRC_PATH_CTL, + 4, 0, NULL, 0), + + RX_MACRO_DAPM_MUX("RX INT0 DEM MUX", 0, rx_int0_dem_inp), + RX_MACRO_DAPM_MUX("RX INT1 DEM MUX", 0, rx_int1_dem_inp), + + SND_SOC_DAPM_MUX_E("RX INT0_2 MUX", SND_SOC_NOPM, INTERP_HPHL, 0, + &rx_int0_2_mux, rx_macro_enable_mix_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT1_2 MUX", SND_SOC_NOPM, INTERP_HPHR, 0, + &rx_int1_2_mux, rx_macro_enable_mix_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT2_2 MUX", SND_SOC_NOPM, INTERP_AUX, 0, + &rx_int2_2_mux, rx_macro_enable_mix_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + + RX_MACRO_DAPM_MUX("RX INT0_1 MIX1 INP0", 0, rx_int0_1_mix_inp0), + RX_MACRO_DAPM_MUX("RX INT0_1 MIX1 INP1", 0, rx_int0_1_mix_inp1), + RX_MACRO_DAPM_MUX("RX INT0_1 MIX1 INP2", 0, rx_int0_1_mix_inp2), + RX_MACRO_DAPM_MUX("RX INT1_1 MIX1 INP0", 0, rx_int1_1_mix_inp0), + RX_MACRO_DAPM_MUX("RX INT1_1 MIX1 INP1", 0, rx_int1_1_mix_inp1), + RX_MACRO_DAPM_MUX("RX INT1_1 MIX1 INP2", 0, rx_int1_1_mix_inp2), + RX_MACRO_DAPM_MUX("RX INT2_1 MIX1 INP0", 0, rx_int2_1_mix_inp0), + RX_MACRO_DAPM_MUX("RX INT2_1 MIX1 INP1", 0, rx_int2_1_mix_inp1), + RX_MACRO_DAPM_MUX("RX INT2_1 MIX1 INP2", 0, rx_int2_1_mix_inp2), + + SND_SOC_DAPM_MUX_E("RX INT0_1 INTERP", SND_SOC_NOPM, INTERP_HPHL, 0, + &rx_int0_1_interp_mux, rx_macro_enable_main_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT1_1 INTERP", SND_SOC_NOPM, INTERP_HPHR, 0, + &rx_int1_1_interp_mux, rx_macro_enable_main_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT2_1 INTERP", SND_SOC_NOPM, INTERP_AUX, 0, + &rx_int2_1_interp_mux, rx_macro_enable_main_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + + RX_MACRO_DAPM_MUX("RX INT0_2 INTERP", 0, rx_int0_2_interp), + RX_MACRO_DAPM_MUX("RX INT1_2 INTERP", 0, rx_int1_2_interp), + RX_MACRO_DAPM_MUX("RX INT2_2 INTERP", 0, rx_int2_2_interp), + + SND_SOC_DAPM_MIXER("RX INT0_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT0 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT1_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT1 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT2_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT2 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_MUX_E("RX INT0 MIX2 INP", SND_SOC_NOPM, INTERP_HPHL, + 0, &rx_int0_mix2_inp_mux, rx_macro_enable_rx_path_clk, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT1 MIX2 INP", SND_SOC_NOPM, INTERP_HPHR, + 0, &rx_int1_mix2_inp_mux, rx_macro_enable_rx_path_clk, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT2 MIX2 INP", SND_SOC_NOPM, INTERP_AUX, + 0, &rx_int2_mix2_inp_mux, rx_macro_enable_rx_path_clk, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MIXER_E("RX INT2_1 VBAT", SND_SOC_NOPM, + 0, 0, rx_int2_1_vbat_mix_switch, + ARRAY_SIZE(rx_int2_1_vbat_mix_switch), + rx_macro_enable_vbat, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MIXER("RX INT0 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT1 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT2 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_OUTPUT("HPHL_OUT"), + SND_SOC_DAPM_OUTPUT("HPHR_OUT"), + SND_SOC_DAPM_OUTPUT("AUX_OUT"), + + SND_SOC_DAPM_INPUT("RX_TX DEC0_INP"), + SND_SOC_DAPM_INPUT("RX_TX DEC1_INP"), + SND_SOC_DAPM_INPUT("RX_TX DEC2_INP"), + SND_SOC_DAPM_INPUT("RX_TX DEC3_INP"), + + SND_SOC_DAPM_SUPPLY_S("RX_MCLK", 0, SND_SOC_NOPM, 0, 0, + rx_macro_mclk_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +}; + +static const struct snd_soc_dapm_route rx_audio_map[] = { + {"RX AIF1 PB", NULL, "RX_MCLK"}, + {"RX AIF2 PB", NULL, "RX_MCLK"}, + {"RX AIF3 PB", NULL, "RX_MCLK"}, + {"RX AIF4 PB", NULL, "RX_MCLK"}, + + {"RX_MACRO RX0 MUX", "AIF1_PB", "RX AIF1 PB"}, + {"RX_MACRO RX1 MUX", "AIF1_PB", "RX AIF1 PB"}, + {"RX_MACRO RX2 MUX", "AIF1_PB", "RX AIF1 PB"}, + {"RX_MACRO RX3 MUX", "AIF1_PB", "RX AIF1 PB"}, + {"RX_MACRO RX4 MUX", "AIF1_PB", "RX AIF1 PB"}, + {"RX_MACRO RX5 MUX", "AIF1_PB", "RX AIF1 PB"}, + + {"RX_MACRO RX0 MUX", "AIF2_PB", "RX AIF2 PB"}, + {"RX_MACRO RX1 MUX", "AIF2_PB", "RX AIF2 PB"}, + {"RX_MACRO RX2 MUX", "AIF2_PB", "RX AIF2 PB"}, + {"RX_MACRO RX3 MUX", "AIF2_PB", "RX AIF2 PB"}, + {"RX_MACRO RX4 MUX", "AIF2_PB", "RX AIF2 PB"}, + {"RX_MACRO RX5 MUX", "AIF2_PB", "RX AIF2 PB"}, + + {"RX_MACRO RX0 MUX", "AIF3_PB", "RX AIF3 PB"}, + {"RX_MACRO RX1 MUX", "AIF3_PB", "RX AIF3 PB"}, + {"RX_MACRO RX2 MUX", "AIF3_PB", "RX AIF3 PB"}, + {"RX_MACRO RX3 MUX", "AIF3_PB", "RX AIF3 PB"}, + {"RX_MACRO RX4 MUX", "AIF3_PB", "RX AIF3 PB"}, + {"RX_MACRO RX5 MUX", "AIF3_PB", "RX AIF3 PB"}, + + {"RX_MACRO RX0 MUX", "AIF4_PB", "RX AIF4 PB"}, + {"RX_MACRO RX1 MUX", "AIF4_PB", "RX AIF4 PB"}, + {"RX_MACRO RX2 MUX", "AIF4_PB", "RX AIF4 PB"}, + {"RX_MACRO RX3 MUX", "AIF4_PB", "RX AIF4 PB"}, + {"RX_MACRO RX4 MUX", "AIF4_PB", "RX AIF4 PB"}, + {"RX_MACRO RX5 MUX", "AIF4_PB", "RX AIF4 PB"}, + + {"RX_RX0", NULL, "RX_MACRO RX0 MUX"}, + {"RX_RX1", NULL, "RX_MACRO RX1 MUX"}, + {"RX_RX2", NULL, "RX_MACRO RX2 MUX"}, + {"RX_RX3", NULL, "RX_MACRO RX3 MUX"}, + {"RX_RX4", NULL, "RX_MACRO RX4 MUX"}, + {"RX_RX5", NULL, "RX_MACRO RX5 MUX"}, + + {"RX INT0_1 MIX1 INP0", "RX0", "RX_RX0"}, + {"RX INT0_1 MIX1 INP0", "RX1", "RX_RX1"}, + {"RX INT0_1 MIX1 INP0", "RX2", "RX_RX2"}, + {"RX INT0_1 MIX1 INP0", "RX3", "RX_RX3"}, + {"RX INT0_1 MIX1 INP0", "RX4", "RX_RX4"}, + {"RX INT0_1 MIX1 INP0", "RX5", "RX_RX5"}, + {"RX INT0_1 MIX1 INP0", "IIR0", "IIR0"}, + {"RX INT0_1 MIX1 INP0", "IIR1", "IIR1"}, + {"RX INT0_1 MIX1 INP1", "RX0", "RX_RX0"}, + {"RX INT0_1 MIX1 INP1", "RX1", "RX_RX1"}, + {"RX INT0_1 MIX1 INP1", "RX2", "RX_RX2"}, + {"RX INT0_1 MIX1 INP1", "RX3", "RX_RX3"}, + {"RX INT0_1 MIX1 INP1", "RX4", "RX_RX4"}, + {"RX INT0_1 MIX1 INP1", "RX5", "RX_RX5"}, + {"RX INT0_1 MIX1 INP1", "IIR0", "IIR0"}, + {"RX INT0_1 MIX1 INP1", "IIR1", "IIR1"}, + {"RX INT0_1 MIX1 INP2", "RX0", "RX_RX0"}, + {"RX INT0_1 MIX1 INP2", "RX1", "RX_RX1"}, + {"RX INT0_1 MIX1 INP2", "RX2", "RX_RX2"}, + {"RX INT0_1 MIX1 INP2", "RX3", "RX_RX3"}, + {"RX INT0_1 MIX1 INP2", "RX4", "RX_RX4"}, + {"RX INT0_1 MIX1 INP2", "RX5", "RX_RX5"}, + {"RX INT0_1 MIX1 INP2", "IIR0", "IIR0"}, + {"RX INT0_1 MIX1 INP2", "IIR1", "IIR1"}, + + {"RX INT1_1 MIX1 INP0", "RX0", "RX_RX0"}, + {"RX INT1_1 MIX1 INP0", "RX1", "RX_RX1"}, + {"RX INT1_1 MIX1 INP0", "RX2", "RX_RX2"}, + {"RX INT1_1 MIX1 INP0", "RX3", "RX_RX3"}, + {"RX INT1_1 MIX1 INP0", "RX4", "RX_RX4"}, + {"RX INT1_1 MIX1 INP0", "RX5", "RX_RX5"}, + {"RX INT1_1 MIX1 INP0", "IIR0", "IIR0"}, + {"RX INT1_1 MIX1 INP0", "IIR1", "IIR1"}, + {"RX INT1_1 MIX1 INP1", "RX0", "RX_RX0"}, + {"RX INT1_1 MIX1 INP1", "RX1", "RX_RX1"}, + {"RX INT1_1 MIX1 INP1", "RX2", "RX_RX2"}, + {"RX INT1_1 MIX1 INP1", "RX3", "RX_RX3"}, + {"RX INT1_1 MIX1 INP1", "RX4", "RX_RX4"}, + {"RX INT1_1 MIX1 INP1", "RX5", "RX_RX5"}, + {"RX INT1_1 MIX1 INP1", "IIR0", "IIR0"}, + {"RX INT1_1 MIX1 INP1", "IIR1", "IIR1"}, + {"RX INT1_1 MIX1 INP2", "RX0", "RX_RX0"}, + {"RX INT1_1 MIX1 INP2", "RX1", "RX_RX1"}, + {"RX INT1_1 MIX1 INP2", "RX2", "RX_RX2"}, + {"RX INT1_1 MIX1 INP2", "RX3", "RX_RX3"}, + {"RX INT1_1 MIX1 INP2", "RX4", "RX_RX4"}, + {"RX INT1_1 MIX1 INP2", "RX5", "RX_RX5"}, + {"RX INT1_1 MIX1 INP2", "IIR0", "IIR0"}, + {"RX INT1_1 MIX1 INP2", "IIR1", "IIR1"}, + + {"RX INT2_1 MIX1 INP0", "RX0", "RX_RX0"}, + {"RX INT2_1 MIX1 INP0", "RX1", "RX_RX1"}, + {"RX INT2_1 MIX1 INP0", "RX2", "RX_RX2"}, + {"RX INT2_1 MIX1 INP0", "RX3", "RX_RX3"}, + {"RX INT2_1 MIX1 INP0", "RX4", "RX_RX4"}, + {"RX INT2_1 MIX1 INP0", "RX5", "RX_RX5"}, + {"RX INT2_1 MIX1 INP0", "IIR0", "IIR0"}, + {"RX INT2_1 MIX1 INP0", "IIR1", "IIR1"}, + {"RX INT2_1 MIX1 INP1", "RX0", "RX_RX0"}, + {"RX INT2_1 MIX1 INP1", "RX1", "RX_RX1"}, + {"RX INT2_1 MIX1 INP1", "RX2", "RX_RX2"}, + {"RX INT2_1 MIX1 INP1", "RX3", "RX_RX3"}, + {"RX INT2_1 MIX1 INP1", "RX4", "RX_RX4"}, + {"RX INT2_1 MIX1 INP1", "RX5", "RX_RX5"}, + {"RX INT2_1 MIX1 INP1", "IIR0", "IIR0"}, + {"RX INT2_1 MIX1 INP1", "IIR1", "IIR1"}, + {"RX INT2_1 MIX1 INP2", "RX0", "RX_RX0"}, + {"RX INT2_1 MIX1 INP2", "RX1", "RX_RX1"}, + {"RX INT2_1 MIX1 INP2", "RX2", "RX_RX2"}, + {"RX INT2_1 MIX1 INP2", "RX3", "RX_RX3"}, + {"RX INT2_1 MIX1 INP2", "RX4", "RX_RX4"}, + {"RX INT2_1 MIX1 INP2", "RX5", "RX_RX5"}, + {"RX INT2_1 MIX1 INP2", "IIR0", "IIR0"}, + {"RX INT2_1 MIX1 INP2", "IIR1", "IIR1"}, + + {"RX INT0_1 MIX1", NULL, "RX INT0_1 MIX1 INP0"}, + {"RX INT0_1 MIX1", NULL, "RX INT0_1 MIX1 INP1"}, + {"RX INT0_1 MIX1", NULL, "RX INT0_1 MIX1 INP2"}, + {"RX INT1_1 MIX1", NULL, "RX INT1_1 MIX1 INP0"}, + {"RX INT1_1 MIX1", NULL, "RX INT1_1 MIX1 INP1"}, + {"RX INT1_1 MIX1", NULL, "RX INT1_1 MIX1 INP2"}, + {"RX INT2_1 MIX1", NULL, "RX INT2_1 MIX1 INP0"}, + {"RX INT2_1 MIX1", NULL, "RX INT2_1 MIX1 INP1"}, + {"RX INT2_1 MIX1", NULL, "RX INT2_1 MIX1 INP2"}, + + {"RX MIX TX0 MUX", "RX_MIX0", "RX INT0 SEC MIX"}, + {"RX MIX TX0 MUX", "RX_MIX1", "RX INT1 SEC MIX"}, + {"RX MIX TX0 MUX", "RX_MIX2", "RX INT2 SEC MIX"}, + {"RX MIX TX1 MUX", "RX_MIX0", "RX INT0 SEC MIX"}, + {"RX MIX TX1 MUX", "RX_MIX1", "RX INT1 SEC MIX"}, + {"RX MIX TX1 MUX", "RX_MIX2", "RX INT2 SEC MIX"}, + {"RX MIX TX2 MUX", "RX_MIX0", "RX INT0 SEC MIX"}, + {"RX MIX TX2 MUX", "RX_MIX1", "RX INT1 SEC MIX"}, + {"RX MIX TX2 MUX", "RX_MIX2", "RX INT2 SEC MIX"}, + {"RX AIF_ECHO", NULL, "RX MIX TX0 MUX"}, + {"RX AIF_ECHO", NULL, "RX MIX TX1 MUX"}, + {"RX AIF_ECHO", NULL, "RX MIX TX2 MUX"}, + {"RX AIF_ECHO", NULL, "RX_MCLK"}, + + /* Mixing path INT0 */ + {"RX INT0_2 MUX", "RX0", "RX_RX0"}, + {"RX INT0_2 MUX", "RX1", "RX_RX1"}, + {"RX INT0_2 MUX", "RX2", "RX_RX2"}, + {"RX INT0_2 MUX", "RX3", "RX_RX3"}, + {"RX INT0_2 MUX", "RX4", "RX_RX4"}, + {"RX INT0_2 MUX", "RX5", "RX_RX5"}, + {"RX INT0_2 INTERP", NULL, "RX INT0_2 MUX"}, + {"RX INT0 SEC MIX", NULL, "RX INT0_2 INTERP"}, + + /* Mixing path INT1 */ + {"RX INT1_2 MUX", "RX0", "RX_RX0"}, + {"RX INT1_2 MUX", "RX1", "RX_RX1"}, + {"RX INT1_2 MUX", "RX2", "RX_RX2"}, + {"RX INT1_2 MUX", "RX3", "RX_RX3"}, + {"RX INT1_2 MUX", "RX4", "RX_RX4"}, + {"RX INT1_2 MUX", "RX5", "RX_RX5"}, + {"RX INT1_2 INTERP", NULL, "RX INT1_2 MUX"}, + {"RX INT1 SEC MIX", NULL, "RX INT1_2 INTERP"}, + + /* Mixing path INT2 */ + {"RX INT2_2 MUX", "RX0", "RX_RX0"}, + {"RX INT2_2 MUX", "RX1", "RX_RX1"}, + {"RX INT2_2 MUX", "RX2", "RX_RX2"}, + {"RX INT2_2 MUX", "RX3", "RX_RX3"}, + {"RX INT2_2 MUX", "RX4", "RX_RX4"}, + {"RX INT2_2 MUX", "RX5", "RX_RX5"}, + {"RX INT2_2 INTERP", NULL, "RX INT2_2 MUX"}, + {"RX INT2 SEC MIX", NULL, "RX INT2_2 INTERP"}, + + {"RX INT0_1 INTERP", NULL, "RX INT0_1 MIX1"}, + {"RX INT0 SEC MIX", NULL, "RX INT0_1 INTERP"}, + {"RX INT0 MIX2", NULL, "RX INT0 SEC MIX"}, + {"RX INT0 MIX2", NULL, "RX INT0 MIX2 INP"}, + {"RX INT0 DEM MUX", "CLSH_DSM_OUT", "RX INT0 MIX2"}, + {"HPHL_OUT", NULL, "RX INT0 DEM MUX"}, + {"HPHL_OUT", NULL, "RX_MCLK"}, + + {"RX INT1_1 INTERP", NULL, "RX INT1_1 MIX1"}, + {"RX INT1 SEC MIX", NULL, "RX INT1_1 INTERP"}, + {"RX INT1 MIX2", NULL, "RX INT1 SEC MIX"}, + {"RX INT1 MIX2", NULL, "RX INT1 MIX2 INP"}, + {"RX INT1 DEM MUX", "CLSH_DSM_OUT", "RX INT1 MIX2"}, + {"HPHR_OUT", NULL, "RX INT1 DEM MUX"}, + {"HPHR_OUT", NULL, "RX_MCLK"}, + + {"RX INT2_1 INTERP", NULL, "RX INT2_1 MIX1"}, + + {"RX INT2_1 VBAT", "RX AUX VBAT Enable", "RX INT2_1 INTERP"}, + {"RX INT2 SEC MIX", NULL, "RX INT2_1 VBAT"}, + + {"RX INT2 SEC MIX", NULL, "RX INT2_1 INTERP"}, + {"RX INT2 MIX2", NULL, "RX INT2 SEC MIX"}, + {"RX INT2 MIX2", NULL, "RX INT2 MIX2 INP"}, + {"AUX_OUT", NULL, "RX INT2 MIX2"}, + {"AUX_OUT", NULL, "RX_MCLK"}, + + {"IIR0", NULL, "RX_MCLK"}, + {"IIR0", NULL, "IIR0 INP0 MUX"}, + {"IIR0 INP0 MUX", "DEC0", "RX_TX DEC0_INP"}, + {"IIR0 INP0 MUX", "DEC1", "RX_TX DEC1_INP"}, + {"IIR0 INP0 MUX", "DEC2", "RX_TX DEC2_INP"}, + {"IIR0 INP0 MUX", "DEC3", "RX_TX DEC3_INP"}, + {"IIR0 INP0 MUX", "RX0", "RX_RX0"}, + {"IIR0 INP0 MUX", "RX1", "RX_RX1"}, + {"IIR0 INP0 MUX", "RX2", "RX_RX2"}, + {"IIR0 INP0 MUX", "RX3", "RX_RX3"}, + {"IIR0 INP0 MUX", "RX4", "RX_RX4"}, + {"IIR0 INP0 MUX", "RX5", "RX_RX5"}, + {"IIR0", NULL, "IIR0 INP1 MUX"}, + {"IIR0 INP1 MUX", "DEC0", "RX_TX DEC0_INP"}, + {"IIR0 INP1 MUX", "DEC1", "RX_TX DEC1_INP"}, + {"IIR0 INP1 MUX", "DEC2", "RX_TX DEC2_INP"}, + {"IIR0 INP1 MUX", "DEC3", "RX_TX DEC3_INP"}, + {"IIR0 INP1 MUX", "RX0", "RX_RX0"}, + {"IIR0 INP1 MUX", "RX1", "RX_RX1"}, + {"IIR0 INP1 MUX", "RX2", "RX_RX2"}, + {"IIR0 INP1 MUX", "RX3", "RX_RX3"}, + {"IIR0 INP1 MUX", "RX4", "RX_RX4"}, + {"IIR0 INP1 MUX", "RX5", "RX_RX5"}, + {"IIR0", NULL, "IIR0 INP2 MUX"}, + {"IIR0 INP2 MUX", "DEC0", "RX_TX DEC0_INP"}, + {"IIR0 INP2 MUX", "DEC1", "RX_TX DEC1_INP"}, + {"IIR0 INP2 MUX", "DEC2", "RX_TX DEC2_INP"}, + {"IIR0 INP2 MUX", "DEC3", "RX_TX DEC3_INP"}, + {"IIR0 INP2 MUX", "RX0", "RX_RX0"}, + {"IIR0 INP2 MUX", "RX1", "RX_RX1"}, + {"IIR0 INP2 MUX", "RX2", "RX_RX2"}, + {"IIR0 INP2 MUX", "RX3", "RX_RX3"}, + {"IIR0 INP2 MUX", "RX4", "RX_RX4"}, + {"IIR0 INP2 MUX", "RX5", "RX_RX5"}, + {"IIR0", NULL, "IIR0 INP3 MUX"}, + {"IIR0 INP3 MUX", "DEC0", "RX_TX DEC0_INP"}, + {"IIR0 INP3 MUX", "DEC1", "RX_TX DEC1_INP"}, + {"IIR0 INP3 MUX", "DEC2", "RX_TX DEC2_INP"}, + {"IIR0 INP3 MUX", "DEC3", "RX_TX DEC3_INP"}, + {"IIR0 INP3 MUX", "RX0", "RX_RX0"}, + {"IIR0 INP3 MUX", "RX1", "RX_RX1"}, + {"IIR0 INP3 MUX", "RX2", "RX_RX2"}, + {"IIR0 INP3 MUX", "RX3", "RX_RX3"}, + {"IIR0 INP3 MUX", "RX4", "RX_RX4"}, + {"IIR0 INP3 MUX", "RX5", "RX_RX5"}, + + {"IIR1", NULL, "RX_MCLK"}, + {"IIR1", NULL, "IIR1 INP0 MUX"}, + {"IIR1 INP0 MUX", "DEC0", "RX_TX DEC0_INP"}, + {"IIR1 INP0 MUX", "DEC1", "RX_TX DEC1_INP"}, + {"IIR1 INP0 MUX", "DEC2", "RX_TX DEC2_INP"}, + {"IIR1 INP0 MUX", "DEC3", "RX_TX DEC3_INP"}, + {"IIR1 INP0 MUX", "RX0", "RX_RX0"}, + {"IIR1 INP0 MUX", "RX1", "RX_RX1"}, + {"IIR1 INP0 MUX", "RX2", "RX_RX2"}, + {"IIR1 INP0 MUX", "RX3", "RX_RX3"}, + {"IIR1 INP0 MUX", "RX4", "RX_RX4"}, + {"IIR1 INP0 MUX", "RX5", "RX_RX5"}, + {"IIR1", NULL, "IIR1 INP1 MUX"}, + {"IIR1 INP1 MUX", "DEC0", "RX_TX DEC0_INP"}, + {"IIR1 INP1 MUX", "DEC1", "RX_TX DEC1_INP"}, + {"IIR1 INP1 MUX", "DEC2", "RX_TX DEC2_INP"}, + {"IIR1 INP1 MUX", "DEC3", "RX_TX DEC3_INP"}, + {"IIR1 INP1 MUX", "RX0", "RX_RX0"}, + {"IIR1 INP1 MUX", "RX1", "RX_RX1"}, + {"IIR1 INP1 MUX", "RX2", "RX_RX2"}, + {"IIR1 INP1 MUX", "RX3", "RX_RX3"}, + {"IIR1 INP1 MUX", "RX4", "RX_RX4"}, + {"IIR1 INP1 MUX", "RX5", "RX_RX5"}, + {"IIR1", NULL, "IIR1 INP2 MUX"}, + {"IIR1 INP2 MUX", "DEC0", "RX_TX DEC0_INP"}, + {"IIR1 INP2 MUX", "DEC1", "RX_TX DEC1_INP"}, + {"IIR1 INP2 MUX", "DEC2", "RX_TX DEC2_INP"}, + {"IIR1 INP2 MUX", "DEC3", "RX_TX DEC3_INP"}, + {"IIR1 INP2 MUX", "RX0", "RX_RX0"}, + {"IIR1 INP2 MUX", "RX1", "RX_RX1"}, + {"IIR1 INP2 MUX", "RX2", "RX_RX2"}, + {"IIR1 INP2 MUX", "RX3", "RX_RX3"}, + {"IIR1 INP2 MUX", "RX4", "RX_RX4"}, + {"IIR1 INP2 MUX", "RX5", "RX_RX5"}, + {"IIR1", NULL, "IIR1 INP3 MUX"}, + {"IIR1 INP3 MUX", "DEC0", "RX_TX DEC0_INP"}, + {"IIR1 INP3 MUX", "DEC1", "RX_TX DEC1_INP"}, + {"IIR1 INP3 MUX", "DEC2", "RX_TX DEC2_INP"}, + {"IIR1 INP3 MUX", "DEC3", "RX_TX DEC3_INP"}, + {"IIR1 INP3 MUX", "RX0", "RX_RX0"}, + {"IIR1 INP3 MUX", "RX1", "RX_RX1"}, + {"IIR1 INP3 MUX", "RX2", "RX_RX2"}, + {"IIR1 INP3 MUX", "RX3", "RX_RX3"}, + {"IIR1 INP3 MUX", "RX4", "RX_RX4"}, + {"IIR1 INP3 MUX", "RX5", "RX_RX5"}, + + {"SRC0", NULL, "IIR0"}, + {"SRC1", NULL, "IIR1"}, + {"RX INT0 MIX2 INP", "SRC0", "SRC0"}, + {"RX INT0 MIX2 INP", "SRC1", "SRC1"}, + {"RX INT1 MIX2 INP", "SRC0", "SRC0"}, + {"RX INT1 MIX2 INP", "SRC1", "SRC1"}, + {"RX INT2 MIX2 INP", "SRC0", "SRC0"}, + {"RX INT2 MIX2 INP", "SRC1", "SRC1"}, +}; + +static int rx_swrm_clock(void *handle, bool enable) +{ + struct rx_macro_priv *rx_priv = (struct rx_macro_priv *) handle; + struct regmap *regmap = dev_get_regmap(rx_priv->dev->parent, NULL); + int ret = 0; + + if (regmap == NULL) { + dev_err(rx_priv->dev, "%s: regmap is NULL\n", __func__); + return -EINVAL; + } + + mutex_lock(&rx_priv->swr_clk_lock); + + dev_dbg(rx_priv->dev, "%s: swrm clock %s\n", + __func__, (enable ? "enable" : "disable")); + if (enable) { + if (rx_priv->swr_clk_users == 0) { + ret = rx_macro_mclk_enable(rx_priv, 1, true); + if (ret < 0) { + dev_err_ratelimited(rx_priv->dev, + "%s: rx request clock enable failed\n", + __func__); + goto exit; + } + if (rx_priv->reset_swr) + regmap_update_bits(regmap, + BOLERO_CDC_RX_CLK_RST_CTRL_SWR_CONTROL, + 0x02, 0x02); + regmap_update_bits(regmap, + BOLERO_CDC_RX_CLK_RST_CTRL_SWR_CONTROL, + 0x01, 0x01); + if (rx_priv->reset_swr) + regmap_update_bits(regmap, + BOLERO_CDC_RX_CLK_RST_CTRL_SWR_CONTROL, + 0x02, 0x00); + rx_priv->reset_swr = false; + msm_cdc_pinctrl_select_active_state( + rx_priv->rx_swr_gpio_p); + } + rx_priv->swr_clk_users++; + } else { + if (rx_priv->swr_clk_users <= 0) { + dev_err(rx_priv->dev, + "%s: rx swrm clock users already reset\n", + __func__); + rx_priv->swr_clk_users = 0; + goto exit; + } + rx_priv->swr_clk_users--; + if (rx_priv->swr_clk_users == 0) { + regmap_update_bits(regmap, + BOLERO_CDC_RX_CLK_RST_CTRL_SWR_CONTROL, + 0x01, 0x00); + msm_cdc_pinctrl_select_sleep_state( + rx_priv->rx_swr_gpio_p); + rx_macro_mclk_enable(rx_priv, 0, true); + } + } + dev_dbg(rx_priv->dev, "%s: swrm clock users %d\n", + __func__, rx_priv->swr_clk_users); +exit: + mutex_unlock(&rx_priv->swr_clk_lock); + return ret; +} + +static void rx_macro_init_bcl_pmic_reg(struct snd_soc_codec *codec) +{ + struct device *rx_dev = NULL; + struct rx_macro_priv *rx_priv = NULL; + + if (!codec) { + pr_err("%s: NULL codec pointer!\n", __func__); + return; + } + + if (!rx_macro_get_data(codec, &rx_dev, &rx_priv, __func__)) + return; + + switch (rx_priv->bcl_pmic_params.id) { + case 0: + /* Enable ID0 to listen to respective PMIC group interrupts */ + snd_soc_update_bits(codec, + BOLERO_CDC_RX_BCL_VBAT_DECODE_CTL1, 0x02, 0x02); + /* Update MC_SID0 */ + snd_soc_update_bits(codec, + BOLERO_CDC_RX_BCL_VBAT_DECODE_CFG1, 0x0F, + rx_priv->bcl_pmic_params.sid); + /* Update MC_PPID0 */ + snd_soc_update_bits(codec, + BOLERO_CDC_RX_BCL_VBAT_DECODE_CFG2, 0xFF, + rx_priv->bcl_pmic_params.ppid); + break; + case 1: + /* Enable ID1 to listen to respective PMIC group interrupts */ + snd_soc_update_bits(codec, + BOLERO_CDC_RX_BCL_VBAT_DECODE_CTL1, 0x01, 0x01); + /* Update MC_SID1 */ + snd_soc_update_bits(codec, + BOLERO_CDC_RX_BCL_VBAT_DECODE_CFG3, 0x0F, + rx_priv->bcl_pmic_params.sid); + /* Update MC_PPID1 */ + snd_soc_update_bits(codec, + BOLERO_CDC_RX_BCL_VBAT_DECODE_CFG1, 0xFF, + rx_priv->bcl_pmic_params.ppid); + break; + default: + dev_err(rx_dev, "%s: PMIC ID is invalid %d\n", + __func__, rx_priv->bcl_pmic_params.id); + break; + } +} + +static int rx_macro_init(struct snd_soc_codec *codec) +{ + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + int ret = 0; + struct device *rx_dev = NULL; + struct rx_macro_priv *rx_priv = NULL; + + rx_dev = bolero_get_device_ptr(codec->dev, RX_MACRO); + if (!rx_dev) { + dev_err(codec->dev, + "%s: null device for macro!\n", __func__); + return -EINVAL; + } + rx_priv = dev_get_drvdata(rx_dev); + if (!rx_priv) { + dev_err(codec->dev, + "%s: priv is null for macro!\n", __func__); + return -EINVAL; + } + + ret = snd_soc_dapm_new_controls(dapm, rx_macro_dapm_widgets, + ARRAY_SIZE(rx_macro_dapm_widgets)); + if (ret < 0) { + dev_err(rx_dev, "%s: failed to add controls\n", __func__); + return ret; + } + ret = snd_soc_dapm_add_routes(dapm, rx_audio_map, + ARRAY_SIZE(rx_audio_map)); + if (ret < 0) { + dev_err(rx_dev, "%s: failed to add routes\n", __func__); + return ret; + } + ret = snd_soc_dapm_new_widgets(dapm->card); + if (ret < 0) { + dev_err(rx_dev, "%s: failed to add widgets\n", __func__); + return ret; + } + ret = snd_soc_add_codec_controls(codec, rx_macro_snd_controls, + ARRAY_SIZE(rx_macro_snd_controls)); + if (ret < 0) { + dev_err(rx_dev, "%s: failed to add snd_ctls\n", __func__); + return ret; + } + rx_priv->dev_up = true; + snd_soc_dapm_ignore_suspend(dapm, "RX_MACRO_AIF1 Playback"); + snd_soc_dapm_ignore_suspend(dapm, "RX_MACRO_AIF2 Playback"); + snd_soc_dapm_ignore_suspend(dapm, "RX_MACRO_AIF3 Playback"); + snd_soc_dapm_ignore_suspend(dapm, "RX_MACRO_AIF4 Playback"); + snd_soc_dapm_ignore_suspend(dapm, "HPHL_OUT"); + snd_soc_dapm_ignore_suspend(dapm, "HPHR_OUT"); + snd_soc_dapm_ignore_suspend(dapm, "AUX_OUT"); + snd_soc_dapm_ignore_suspend(dapm, "RX_TX DEC0_INP"); + snd_soc_dapm_ignore_suspend(dapm, "RX_TX DEC1_INP"); + snd_soc_dapm_ignore_suspend(dapm, "RX_TX DEC2_INP"); + snd_soc_dapm_ignore_suspend(dapm, "RX_TX DEC3_INP"); + snd_soc_dapm_sync(dapm); + + snd_soc_update_bits(codec, BOLERO_CDC_RX_RX0_RX_PATH_DSM_CTL, 0x01, 0x01); + snd_soc_update_bits(codec, BOLERO_CDC_RX_RX1_RX_PATH_DSM_CTL, 0x01, 0x01); + snd_soc_update_bits(codec, BOLERO_CDC_RX_RX2_RX_PATH_DSM_CTL, 0x01, 0x01); + snd_soc_update_bits(codec, BOLERO_CDC_RX_RX0_RX_PATH_SEC7, 0x07, 0x02); + snd_soc_update_bits(codec, BOLERO_CDC_RX_RX1_RX_PATH_SEC7, 0x07, 0x02); + snd_soc_update_bits(codec, BOLERO_CDC_RX_RX2_RX_PATH_SEC7, 0x07, 0x02); + snd_soc_update_bits(codec, BOLERO_CDC_RX_RX0_RX_PATH_CFG3, 0x03, 0x02); + snd_soc_update_bits(codec, BOLERO_CDC_RX_RX1_RX_PATH_CFG3, 0x03, 0x02); + snd_soc_update_bits(codec, BOLERO_CDC_RX_RX2_RX_PATH_CFG3, 0x03, 0x02); + + rx_priv->codec = codec; + rx_macro_init_bcl_pmic_reg(codec); + + return 0; +} + +static int rx_macro_deinit(struct snd_soc_codec *codec) +{ + struct device *rx_dev = NULL; + struct rx_macro_priv *rx_priv = NULL; + + if (!rx_macro_get_data(codec, &rx_dev, &rx_priv, __func__)) + return -EINVAL; + + rx_priv->codec = NULL; + + return 0; +} + +static void rx_macro_add_child_devices(struct work_struct *work) +{ + struct rx_macro_priv *rx_priv = NULL; + struct platform_device *pdev = NULL; + struct device_node *node = NULL; + struct rx_swr_ctrl_data *swr_ctrl_data = NULL, *temp = NULL; + int ret = 0; + u16 count = 0, ctrl_num = 0; + struct rx_swr_ctrl_platform_data *platdata = NULL; + char plat_dev_name[RX_SWR_STRING_LEN] = ""; + bool rx_swr_master_node = false; + + rx_priv = container_of(work, struct rx_macro_priv, + rx_macro_add_child_devices_work); + if (!rx_priv) { + pr_err("%s: Memory for rx_priv does not exist\n", + __func__); + return; + } + + if (!rx_priv->dev) { + pr_err("%s: RX device does not exist\n", __func__); + return; + } + + if(!rx_priv->dev->of_node) { + dev_err(rx_priv->dev, + "%s: DT node for RX dev does not exist\n", __func__); + return; + } + + platdata = &rx_priv->swr_plat_data; + rx_priv->child_count = 0; + + for_each_available_child_of_node(rx_priv->dev->of_node, node) { + rx_swr_master_node = false; + if (strnstr(node->name, "rx_swr_master", + strlen("rx_swr_master")) != NULL) + rx_swr_master_node = true; + + if(rx_swr_master_node) + strlcpy(plat_dev_name, "rx_swr_ctrl", + (RX_SWR_STRING_LEN - 1)); + else + strlcpy(plat_dev_name, node->name, + (RX_SWR_STRING_LEN - 1)); + + pdev = platform_device_alloc(plat_dev_name, -1); + if (!pdev) { + dev_err(rx_priv->dev, "%s: pdev memory alloc failed\n", + __func__); + ret = -ENOMEM; + goto err; + } + pdev->dev.parent = rx_priv->dev; + pdev->dev.of_node = node; + + if (rx_swr_master_node) { + ret = platform_device_add_data(pdev, platdata, + sizeof(*platdata)); + if (ret) { + dev_err(&pdev->dev, + "%s: cannot add plat data ctrl:%d\n", + __func__, ctrl_num); + goto fail_pdev_add; + } + } + + ret = platform_device_add(pdev); + if (ret) { + dev_err(&pdev->dev, + "%s: Cannot add platform device\n", + __func__); + goto fail_pdev_add; + } + + if (rx_swr_master_node) { + temp = krealloc(swr_ctrl_data, + (ctrl_num + 1) * sizeof( + struct rx_swr_ctrl_data), + GFP_KERNEL); + if (!temp) { + ret = -ENOMEM; + goto fail_pdev_add; + } + swr_ctrl_data = temp; + swr_ctrl_data[ctrl_num].rx_swr_pdev = pdev; + ctrl_num++; + dev_dbg(&pdev->dev, + "%s: Added soundwire ctrl device(s)\n", + __func__); + rx_priv->swr_ctrl_data = swr_ctrl_data; + } + if (rx_priv->child_count < RX_MACRO_CHILD_DEVICES_MAX) + rx_priv->pdev_child_devices[ + rx_priv->child_count++] = pdev; + else + goto err; + } + return; +fail_pdev_add: + for (count = 0; count < rx_priv->child_count; count++) + platform_device_put(rx_priv->pdev_child_devices[count]); +err: + return; +} + +static void rx_macro_init_ops(struct macro_ops *ops, char __iomem *rx_io_base) +{ + memset(ops, 0, sizeof(struct macro_ops)); + ops->init = rx_macro_init; + ops->exit = rx_macro_deinit; + ops->io_base = rx_io_base; + ops->dai_ptr = rx_macro_dai; + ops->num_dais = ARRAY_SIZE(rx_macro_dai); + ops->mclk_fn = rx_macro_mclk_ctrl; + ops->event_handler = rx_macro_event_handler; +} + +static int rx_macro_probe(struct platform_device *pdev) +{ + struct macro_ops ops = {0}; + struct rx_macro_priv *rx_priv = NULL; + u32 rx_base_addr = 0, muxsel = 0; + char __iomem *rx_io_base = NULL, *muxsel_io = NULL; + int ret = 0; + struct clk *rx_core_clk = NULL, *rx_npl_clk = NULL; + u8 bcl_pmic_params[3]; + + rx_priv = devm_kzalloc(&pdev->dev, sizeof(struct rx_macro_priv), + GFP_KERNEL); + if (!rx_priv) + return -ENOMEM; + + rx_priv->dev = &pdev->dev; + ret = of_property_read_u32(pdev->dev.of_node, "reg", + &rx_base_addr); + if (ret) { + dev_err(&pdev->dev, "%s: could not find %s entry in dt\n", + __func__, "reg"); + return ret; + } + ret = of_property_read_u32(pdev->dev.of_node, "qcom,rx_mclk_mode_muxsel", + &muxsel); + if (ret) { + dev_err(&pdev->dev, "%s: could not find %s entry in dt\n", + __func__, "reg"); + return ret; + } + rx_priv->rx_swr_gpio_p = of_parse_phandle(pdev->dev.of_node, + "qcom,rx-swr-gpios", 0); + if (!rx_priv->rx_swr_gpio_p) { + dev_err(&pdev->dev, "%s: swr_gpios handle not provided!\n", + __func__); + return -EINVAL; + } + rx_io_base = devm_ioremap(&pdev->dev, rx_base_addr, + RX_MACRO_MAX_OFFSET); + if (!rx_io_base) { + dev_err(&pdev->dev, "%s: ioremap failed\n", __func__); + return -ENOMEM; + } + rx_priv->rx_io_base = rx_io_base; + muxsel_io = devm_ioremap(&pdev->dev, muxsel, 0x4); + if (!muxsel_io) { + dev_err(&pdev->dev, "%s: ioremap failed for muxsel\n", + __func__); + return -ENOMEM; + } + rx_priv->rx_mclk_mode_muxsel = muxsel_io; + rx_priv->reset_swr = true; + INIT_WORK(&rx_priv->rx_macro_add_child_devices_work, + rx_macro_add_child_devices); + rx_priv->swr_plat_data.handle = (void *) rx_priv; + rx_priv->swr_plat_data.read = NULL; + rx_priv->swr_plat_data.write = NULL; + rx_priv->swr_plat_data.bulk_write = NULL; + rx_priv->swr_plat_data.clk = rx_swrm_clock; + rx_priv->swr_plat_data.handle_irq = NULL; + + /* Register MCLK for rx macro */ + rx_core_clk = devm_clk_get(&pdev->dev, "rx_core_clk"); + if (IS_ERR(rx_core_clk)) { + ret = PTR_ERR(rx_core_clk); + dev_err(&pdev->dev, "%s: clk get %s failed %d\n", + __func__, "rx_core_clk", ret); + return ret; + } + rx_priv->rx_core_clk = rx_core_clk; + /* Register npl clk for soundwire */ + rx_npl_clk = devm_clk_get(&pdev->dev, "rx_npl_clk"); + if (IS_ERR(rx_npl_clk)) { + ret = PTR_ERR(rx_npl_clk); + dev_err(&pdev->dev, "%s: clk get %s failed %d\n", + __func__, "rx_npl_clk", ret); + return ret; + } + rx_priv->rx_npl_clk = rx_npl_clk; + + ret = of_property_read_u8_array(pdev->dev.of_node, + "qcom,rx-bcl-pmic-params", bcl_pmic_params, + sizeof(bcl_pmic_params)); + if (ret) { + dev_dbg(&pdev->dev, "%s: could not find %s entry in dt\n", + __func__, "qcom,rx-bcl-pmic-params"); + } else { + rx_priv->bcl_pmic_params.id = bcl_pmic_params[0]; + rx_priv->bcl_pmic_params.sid = bcl_pmic_params[1]; + rx_priv->bcl_pmic_params.ppid = bcl_pmic_params[2]; + } + + dev_set_drvdata(&pdev->dev, rx_priv); + mutex_init(&rx_priv->mclk_lock); + mutex_init(&rx_priv->swr_clk_lock); + rx_macro_init_ops(&ops, rx_io_base); + + ret = bolero_register_macro(&pdev->dev, RX_MACRO, &ops); + if (ret) { + dev_err(&pdev->dev, + "%s: register macro failed\n", __func__); + goto err_reg_macro; + } + schedule_work(&rx_priv->rx_macro_add_child_devices_work); + + return 0; + +err_reg_macro: + mutex_destroy(&rx_priv->mclk_lock); + mutex_destroy(&rx_priv->swr_clk_lock); + return ret; +} + +static int rx_macro_remove(struct platform_device *pdev) +{ + struct rx_macro_priv *rx_priv = NULL; + u16 count = 0; + + rx_priv = dev_get_drvdata(&pdev->dev); + + if (!rx_priv) + return -EINVAL; + + for (count = 0; count < rx_priv->child_count && + count < RX_MACRO_CHILD_DEVICES_MAX; count++) + platform_device_unregister(rx_priv->pdev_child_devices[count]); + + bolero_unregister_macro(&pdev->dev, RX_MACRO); + mutex_destroy(&rx_priv->mclk_lock); + mutex_destroy(&rx_priv->swr_clk_lock); + kfree(rx_priv->swr_ctrl_data); + return 0; +} + +static const struct of_device_id rx_macro_dt_match[] = { + {.compatible = "qcom,rx-macro"}, + {} +}; + +static struct platform_driver rx_macro_driver = { + .driver = { + .name = "rx_macro", + .owner = THIS_MODULE, + .of_match_table = rx_macro_dt_match, + }, + .probe = rx_macro_probe, + .remove = rx_macro_remove, +}; + +module_platform_driver(rx_macro_driver); + +MODULE_DESCRIPTION("RX macro driver"); +MODULE_LICENSE("GPL v2"); diff --git a/audio-kernel/asoc/codecs/bolero/tx-macro.c b/audio-kernel/asoc/codecs/bolero/tx-macro.c new file mode 100644 index 000000000..83bea66be --- /dev/null +++ b/audio-kernel/asoc/codecs/bolero/tx-macro.c @@ -0,0 +1,1861 @@ +/* Copyright (c) 2018-2019, 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "bolero-cdc.h" +#include "bolero-cdc-registers.h" +#include "../msm-cdc-pinctrl.h" + +#define TX_MACRO_MAX_OFFSET 0x1000 + +#define NUM_DECIMATORS 8 + +#define TX_MACRO_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\ + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |\ + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000) +#define TX_MACRO_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ + SNDRV_PCM_FMTBIT_S24_LE |\ + SNDRV_PCM_FMTBIT_S24_3LE) + +#define TX_HPF_CUT_OFF_FREQ_MASK 0x60 +#define CF_MIN_3DB_4HZ 0x0 +#define CF_MIN_3DB_75HZ 0x1 +#define CF_MIN_3DB_150HZ 0x2 + +#define TX_MACRO_DMIC_SAMPLE_RATE_UNDEFINED 0 +#define TX_MACRO_MCLK_FREQ 9600000 +#define TX_MACRO_TX_PATH_OFFSET 0x80 +#define TX_MACRO_SWR_MIC_MUX_SEL_MASK 0xF +#define TX_MACRO_ADC_MUX_CFG_OFFSET 0x2 + +#define TX_MACRO_TX_UNMUTE_DELAY_MS 40 + +static int tx_unmute_delay = TX_MACRO_TX_UNMUTE_DELAY_MS; +module_param(tx_unmute_delay, int, 0664); +MODULE_PARM_DESC(tx_unmute_delay, "delay to unmute the tx path"); + +static const DECLARE_TLV_DB_SCALE(digital_gain, 0, 1, 0); + +static int tx_macro_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai); +static int tx_macro_get_channel_map(struct snd_soc_dai *dai, + unsigned int *tx_num, unsigned int *tx_slot, + unsigned int *rx_num, unsigned int *rx_slot); + +#define TX_MACRO_SWR_STRING_LEN 80 +#define TX_MACRO_CHILD_DEVICES_MAX 3 + +/* Hold instance to soundwire platform device */ +struct tx_macro_swr_ctrl_data { + struct platform_device *tx_swr_pdev; +}; + +struct tx_macro_swr_ctrl_platform_data { + void *handle; /* holds codec private data */ + int (*read)(void *handle, int reg); + int (*write)(void *handle, int reg, int val); + int (*bulk_write)(void *handle, u32 *reg, u32 *val, size_t len); + int (*clk)(void *handle, bool enable); + int (*handle_irq)(void *handle, + irqreturn_t (*swrm_irq_handler)(int irq, + void *data), + void *swrm_handle, + int action); +}; + +enum { + TX_MACRO_AIF_INVALID = 0, + TX_MACRO_AIF1_CAP, + TX_MACRO_AIF2_CAP, + TX_MACRO_MAX_DAIS +}; + +enum { + TX_MACRO_DEC0, + TX_MACRO_DEC1, + TX_MACRO_DEC2, + TX_MACRO_DEC3, + TX_MACRO_DEC4, + TX_MACRO_DEC5, + TX_MACRO_DEC6, + TX_MACRO_DEC7, + TX_MACRO_DEC_MAX, +}; + +enum { + TX_MACRO_CLK_DIV_2, + TX_MACRO_CLK_DIV_3, + TX_MACRO_CLK_DIV_4, + TX_MACRO_CLK_DIV_6, + TX_MACRO_CLK_DIV_8, + TX_MACRO_CLK_DIV_16, +}; + +enum { + MSM_DMIC, + SWR_MIC, + ANC_FB_TUNE1 +}; + +struct tx_mute_work { + struct tx_macro_priv *tx_priv; + u32 decimator; + struct delayed_work dwork; +}; + +struct hpf_work { + struct tx_macro_priv *tx_priv; + u8 decimator; + u8 hpf_cut_off_freq; + struct delayed_work dwork; +}; + +struct tx_macro_priv { + struct device *dev; + bool dec_active[NUM_DECIMATORS]; + int tx_mclk_users; + int swr_clk_users; + bool dapm_mclk_enable; + bool reset_swr; + struct clk *tx_core_clk; + struct clk *tx_npl_clk; + struct mutex mclk_lock; + struct mutex swr_clk_lock; + struct snd_soc_codec *codec; + struct device_node *tx_swr_gpio_p; + struct tx_macro_swr_ctrl_data *swr_ctrl_data; + struct tx_macro_swr_ctrl_platform_data swr_plat_data; + struct work_struct tx_macro_add_child_devices_work; + struct hpf_work tx_hpf_work[NUM_DECIMATORS]; + struct tx_mute_work tx_mute_dwork[NUM_DECIMATORS]; + s32 dmic_0_1_clk_cnt; + s32 dmic_2_3_clk_cnt; + s32 dmic_4_5_clk_cnt; + s32 dmic_6_7_clk_cnt; + u16 dmic_clk_div; + unsigned long active_ch_mask[TX_MACRO_MAX_DAIS]; + unsigned long active_ch_cnt[TX_MACRO_MAX_DAIS]; + char __iomem *tx_io_base; + struct platform_device *pdev_child_devices + [TX_MACRO_CHILD_DEVICES_MAX]; + int child_count; +}; + +static bool tx_macro_get_data(struct snd_soc_codec *codec, + struct device **tx_dev, + struct tx_macro_priv **tx_priv, + const char *func_name) +{ + *tx_dev = bolero_get_device_ptr(codec->dev, TX_MACRO); + if (!(*tx_dev)) { + dev_err(codec->dev, + "%s: null device for macro!\n", func_name); + return false; + } + + *tx_priv = dev_get_drvdata((*tx_dev)); + if (!(*tx_priv)) { + dev_err(codec->dev, + "%s: priv is null for macro!\n", func_name); + return false; + } + + if (!(*tx_priv)->codec) { + dev_err(codec->dev, + "%s: tx_priv->codec not initialized!\n", func_name); + return false; + } + + return true; +} + +static int tx_macro_mclk_enable(struct tx_macro_priv *tx_priv, + bool mclk_enable) +{ + struct regmap *regmap = dev_get_regmap(tx_priv->dev->parent, NULL); + int ret = 0; + + if (regmap == NULL) { + dev_err(tx_priv->dev, "%s: regmap is NULL\n", __func__); + return -EINVAL; + } + + dev_dbg(tx_priv->dev, "%s: mclk_enable = %u,clk_users= %d\n", + __func__, mclk_enable, tx_priv->tx_mclk_users); + + mutex_lock(&tx_priv->mclk_lock); + if (mclk_enable) { + if (tx_priv->tx_mclk_users == 0) { + ret = bolero_request_clock(tx_priv->dev, + TX_MACRO, MCLK_MUX0, true); + if (ret < 0) { + dev_err_ratelimited(tx_priv->dev, + "%s: request clock enable failed\n", + __func__); + goto exit; + } + regcache_mark_dirty(regmap); + regcache_sync_region(regmap, + TX_START_OFFSET, + TX_MAX_OFFSET); + /* 9.6MHz MCLK, set value 0x00 if other frequency */ + regmap_update_bits(regmap, + BOLERO_CDC_TX_TOP_CSR_FREQ_MCLK, 0x01, 0x01); + regmap_update_bits(regmap, + BOLERO_CDC_TX_CLK_RST_CTRL_MCLK_CONTROL, + 0x01, 0x01); + regmap_update_bits(regmap, + BOLERO_CDC_TX_CLK_RST_CTRL_FS_CNT_CONTROL, + 0x01, 0x01); + } + tx_priv->tx_mclk_users++; + } else { + if (tx_priv->tx_mclk_users <= 0) { + dev_err(tx_priv->dev, "%s: clock already disabled\n", + __func__); + tx_priv->tx_mclk_users = 0; + goto exit; + } + tx_priv->tx_mclk_users--; + if (tx_priv->tx_mclk_users == 0) { + regmap_update_bits(regmap, + BOLERO_CDC_TX_CLK_RST_CTRL_FS_CNT_CONTROL, + 0x01, 0x00); + regmap_update_bits(regmap, + BOLERO_CDC_TX_CLK_RST_CTRL_MCLK_CONTROL, + 0x01, 0x00); + bolero_request_clock(tx_priv->dev, + TX_MACRO, MCLK_MUX0, false); + } + } +exit: + mutex_unlock(&tx_priv->mclk_lock); + return ret; +} + +static int tx_macro_mclk_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + int ret = 0; + struct device *tx_dev = NULL; + struct tx_macro_priv *tx_priv = NULL; + + if (!tx_macro_get_data(codec, &tx_dev, &tx_priv, __func__)) + return -EINVAL; + + dev_dbg(tx_dev, "%s: event = %d\n", __func__, event); + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + ret = tx_macro_mclk_enable(tx_priv, 1); + if (ret) + tx_priv->dapm_mclk_enable = false; + else + tx_priv->dapm_mclk_enable = true; + break; + case SND_SOC_DAPM_POST_PMD: + if (tx_priv->dapm_mclk_enable) + ret = tx_macro_mclk_enable(tx_priv, 0); + break; + default: + dev_err(tx_priv->dev, + "%s: invalid DAPM event %d\n", __func__, event); + ret = -EINVAL; + } + return ret; +} + +static int tx_macro_mclk_ctrl(struct device *dev, bool enable) +{ + struct tx_macro_priv *tx_priv = dev_get_drvdata(dev); + int ret = 0; + + if (enable) { + ret = clk_prepare_enable(tx_priv->tx_core_clk); + if (ret < 0) { + dev_err(dev, "%s:tx mclk enable failed\n", __func__); + goto exit; + } + ret = clk_prepare_enable(tx_priv->tx_npl_clk); + if (ret < 0) { + dev_err(dev, "%s:tx npl_clk enable failed\n", + __func__); + clk_disable_unprepare(tx_priv->tx_core_clk); + goto exit; + } + } else { + clk_disable_unprepare(tx_priv->tx_npl_clk); + clk_disable_unprepare(tx_priv->tx_core_clk); + } + +exit: + return ret; +} + +static int tx_macro_event_handler(struct snd_soc_codec *codec, u16 event, + u32 data) +{ + struct device *tx_dev = NULL; + struct tx_macro_priv *tx_priv = NULL; + + if (!tx_macro_get_data(codec, &tx_dev, &tx_priv, __func__)) + return -EINVAL; + + switch (event) { + case BOLERO_MACRO_EVT_SSR_DOWN: + swrm_wcd_notify( + tx_priv->swr_ctrl_data[0].tx_swr_pdev, + SWR_DEVICE_DOWN, NULL); + swrm_wcd_notify( + tx_priv->swr_ctrl_data[0].tx_swr_pdev, + SWR_DEVICE_SSR_DOWN, NULL); + break; + case BOLERO_MACRO_EVT_SSR_UP: + /* reset swr after ssr/pdr */ + tx_priv->reset_swr = true; + swrm_wcd_notify( + tx_priv->swr_ctrl_data[0].tx_swr_pdev, + SWR_DEVICE_SSR_UP, NULL); + break; + } + return 0; +} + +static int tx_macro_reg_wake_irq(struct snd_soc_codec *codec, + u32 data) +{ + struct device *tx_dev = NULL; + struct tx_macro_priv *tx_priv = NULL; + u32 ipc_wakeup = data; + int ret = 0; + + if (!tx_macro_get_data(codec, &tx_dev, &tx_priv, __func__)) + return -EINVAL; + + ret = swrm_wcd_notify( + tx_priv->swr_ctrl_data[0].tx_swr_pdev, + SWR_REGISTER_WAKE_IRQ, &ipc_wakeup); + + return ret; +} + +static void tx_macro_tx_hpf_corner_freq_callback(struct work_struct *work) +{ + struct delayed_work *hpf_delayed_work = NULL; + struct hpf_work *hpf_work = NULL; + struct tx_macro_priv *tx_priv = NULL; + struct snd_soc_codec *codec = NULL; + u16 dec_cfg_reg = 0, hpf_gate_reg = 0; + u8 hpf_cut_off_freq = 0; + u16 adc_mux_reg = 0, adc_n = 0, adc_reg = 0; + + hpf_delayed_work = to_delayed_work(work); + hpf_work = container_of(hpf_delayed_work, struct hpf_work, dwork); + tx_priv = hpf_work->tx_priv; + codec = tx_priv->codec; + hpf_cut_off_freq = hpf_work->hpf_cut_off_freq; + + dec_cfg_reg = BOLERO_CDC_TX0_TX_PATH_CFG0 + + TX_MACRO_TX_PATH_OFFSET * hpf_work->decimator; + hpf_gate_reg = BOLERO_CDC_TX0_TX_PATH_SEC2 + + TX_MACRO_TX_PATH_OFFSET * hpf_work->decimator; + + dev_dbg(codec->dev, "%s: decimator %u hpf_cut_of_freq 0x%x\n", + __func__, hpf_work->decimator, hpf_cut_off_freq); + + adc_mux_reg = BOLERO_CDC_TX_INP_MUX_ADC_MUX0_CFG1 + + TX_MACRO_ADC_MUX_CFG_OFFSET * hpf_work->decimator; + if (snd_soc_read(codec, adc_mux_reg) & SWR_MIC) { + adc_reg = BOLERO_CDC_TX_INP_MUX_ADC_MUX0_CFG0 + + TX_MACRO_ADC_MUX_CFG_OFFSET * hpf_work->decimator; + adc_n = snd_soc_read(codec, adc_reg) & + TX_MACRO_SWR_MIC_MUX_SEL_MASK; + if (adc_n >= BOLERO_ADC_MAX) + goto tx_hpf_set; + /* analog mic clear TX hold */ + bolero_clear_amic_tx_hold(codec->dev, adc_n); + } +tx_hpf_set: + snd_soc_update_bits(codec, dec_cfg_reg, TX_HPF_CUT_OFF_FREQ_MASK, + hpf_cut_off_freq << 5); + snd_soc_update_bits(codec, hpf_gate_reg, 0x03, 0x02); + /* Minimum 1 clk cycle delay is required as per HW spec */ + usleep_range(1000, 1010); + snd_soc_update_bits(codec, hpf_gate_reg, 0x03, 0x01); +} + +static void tx_macro_mute_update_callback(struct work_struct *work) +{ + struct tx_mute_work *tx_mute_dwork = NULL; + struct snd_soc_codec *codec = NULL; + struct tx_macro_priv *tx_priv = NULL; + struct delayed_work *delayed_work = NULL; + u16 tx_vol_ctl_reg = 0; + u8 decimator = 0; + + delayed_work = to_delayed_work(work); + tx_mute_dwork = container_of(delayed_work, struct tx_mute_work, dwork); + tx_priv = tx_mute_dwork->tx_priv; + codec = tx_priv->codec; + decimator = tx_mute_dwork->decimator; + + tx_vol_ctl_reg = + BOLERO_CDC_TX0_TX_PATH_CTL + + TX_MACRO_TX_PATH_OFFSET * decimator; + snd_soc_update_bits(codec, tx_vol_ctl_reg, 0x10, 0x00); + dev_dbg(tx_priv->dev, "%s: decimator %u unmute\n", + __func__, decimator); +} + +static int tx_macro_put_dec_enum(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int val = 0; + u16 mic_sel_reg = 0; + + val = ucontrol->value.enumerated.item[0]; + if (val > e->items - 1) + return -EINVAL; + + dev_dbg(codec->dev, "%s: wname: %s, val: 0x%x\n", __func__, + widget->name, val); + + switch (e->reg) { + case BOLERO_CDC_TX_INP_MUX_ADC_MUX0_CFG0: + mic_sel_reg = BOLERO_CDC_TX0_TX_PATH_CFG0; + break; + case BOLERO_CDC_TX_INP_MUX_ADC_MUX1_CFG0: + mic_sel_reg = BOLERO_CDC_TX1_TX_PATH_CFG0; + break; + case BOLERO_CDC_TX_INP_MUX_ADC_MUX2_CFG0: + mic_sel_reg = BOLERO_CDC_TX2_TX_PATH_CFG0; + break; + case BOLERO_CDC_TX_INP_MUX_ADC_MUX3_CFG0: + mic_sel_reg = BOLERO_CDC_TX3_TX_PATH_CFG0; + break; + case BOLERO_CDC_TX_INP_MUX_ADC_MUX4_CFG0: + mic_sel_reg = BOLERO_CDC_TX4_TX_PATH_CFG0; + break; + case BOLERO_CDC_TX_INP_MUX_ADC_MUX5_CFG0: + mic_sel_reg = BOLERO_CDC_TX5_TX_PATH_CFG0; + break; + case BOLERO_CDC_TX_INP_MUX_ADC_MUX6_CFG0: + mic_sel_reg = BOLERO_CDC_TX6_TX_PATH_CFG0; + break; + case BOLERO_CDC_TX_INP_MUX_ADC_MUX7_CFG0: + mic_sel_reg = BOLERO_CDC_TX7_TX_PATH_CFG0; + break; + default: + dev_err(codec->dev, "%s: e->reg: 0x%x not expected\n", + __func__, e->reg); + return -EINVAL; + } + if (strnstr(widget->name, "SMIC", strlen(widget->name))) { + if (val != 0) { + if (val < 5) + snd_soc_update_bits(codec, mic_sel_reg, + 1 << 7, 0x0 << 7); + else + snd_soc_update_bits(codec, mic_sel_reg, + 1 << 7, 0x1 << 7); + } + } else { + /* DMIC selected */ + if (val != 0) + snd_soc_update_bits(codec, mic_sel_reg, 1 << 7, 1 << 7); + } + + return snd_soc_dapm_put_enum_double(kcontrol, ucontrol); +} + +static int tx_macro_tx_mixer_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm); + struct soc_multi_mixer_control *mixer = + ((struct soc_multi_mixer_control *)kcontrol->private_value); + u32 dai_id = widget->shift; + u32 dec_id = mixer->shift; + struct device *tx_dev = NULL; + struct tx_macro_priv *tx_priv = NULL; + + if (!tx_macro_get_data(codec, &tx_dev, &tx_priv, __func__)) + return -EINVAL; + + if (test_bit(dec_id, &tx_priv->active_ch_mask[dai_id])) + ucontrol->value.integer.value[0] = 1; + else + ucontrol->value.integer.value[0] = 0; + return 0; +} + +static int tx_macro_tx_mixer_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm); + struct snd_soc_dapm_update *update = NULL; + struct soc_multi_mixer_control *mixer = + ((struct soc_multi_mixer_control *)kcontrol->private_value); + u32 dai_id = widget->shift; + u32 dec_id = mixer->shift; + u32 enable = ucontrol->value.integer.value[0]; + struct device *tx_dev = NULL; + struct tx_macro_priv *tx_priv = NULL; + + if (!tx_macro_get_data(codec, &tx_dev, &tx_priv, __func__)) + return -EINVAL; + + if (enable) { + set_bit(dec_id, &tx_priv->active_ch_mask[dai_id]); + tx_priv->active_ch_cnt[dai_id]++; + } else { + tx_priv->active_ch_cnt[dai_id]--; + clear_bit(dec_id, &tx_priv->active_ch_mask[dai_id]); + } + snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, enable, update); + + return 0; +} + +static int tx_macro_enable_dmic(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + u8 dmic_clk_en = 0x01; + u16 dmic_clk_reg = 0; + s32 *dmic_clk_cnt = NULL; + unsigned int dmic = 0; + int ret = 0; + char *wname = NULL; + struct device *tx_dev = NULL; + struct tx_macro_priv *tx_priv = NULL; + + if (!tx_macro_get_data(codec, &tx_dev, &tx_priv, __func__)) + return -EINVAL; + + wname = strpbrk(w->name, "01234567"); + if (!wname) { + dev_err(codec->dev, "%s: widget not found\n", __func__); + return -EINVAL; + } + + ret = kstrtouint(wname, 10, &dmic); + if (ret < 0) { + dev_err(codec->dev, "%s: Invalid DMIC line on the codec\n", + __func__); + return -EINVAL; + } + + switch (dmic) { + case 0: + case 1: + dmic_clk_cnt = &(tx_priv->dmic_0_1_clk_cnt); + dmic_clk_reg = BOLERO_CDC_VA_TOP_CSR_DMIC0_CTL; + break; + case 2: + case 3: + dmic_clk_cnt = &(tx_priv->dmic_2_3_clk_cnt); + dmic_clk_reg = BOLERO_CDC_VA_TOP_CSR_DMIC1_CTL; + break; + case 4: + case 5: + dmic_clk_cnt = &(tx_priv->dmic_4_5_clk_cnt); + dmic_clk_reg = BOLERO_CDC_VA_TOP_CSR_DMIC2_CTL; + break; + case 6: + case 7: + dmic_clk_cnt = &(tx_priv->dmic_6_7_clk_cnt); + dmic_clk_reg = BOLERO_CDC_VA_TOP_CSR_DMIC3_CTL; + break; + default: + dev_err(codec->dev, "%s: Invalid DMIC Selection\n", + __func__); + return -EINVAL; + } + dev_dbg(codec->dev, "%s: event %d DMIC%d dmic_clk_cnt %d\n", + __func__, event, dmic, *dmic_clk_cnt); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + (*dmic_clk_cnt)++; + if (*dmic_clk_cnt == 1) { + snd_soc_update_bits(codec, BOLERO_CDC_VA_TOP_CSR_DMIC_CFG, + 0x80, 0x00); + + snd_soc_update_bits(codec, dmic_clk_reg, + 0x0E, tx_priv->dmic_clk_div << 0x1); + snd_soc_update_bits(codec, dmic_clk_reg, + dmic_clk_en, dmic_clk_en); + } + break; + case SND_SOC_DAPM_POST_PMD: + (*dmic_clk_cnt)--; + if (*dmic_clk_cnt == 0) + snd_soc_update_bits(codec, dmic_clk_reg, + dmic_clk_en, 0); + break; + } + + return 0; +} + +static int tx_macro_enable_dec(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + unsigned int decimator = 0; + u16 tx_vol_ctl_reg = 0; + u16 dec_cfg_reg = 0; + u16 hpf_gate_reg = 0; + u16 tx_gain_ctl_reg = 0; + u8 hpf_cut_off_freq = 0; + struct device *tx_dev = NULL; + struct tx_macro_priv *tx_priv = NULL; + + if (!tx_macro_get_data(codec, &tx_dev, &tx_priv, __func__)) + return -EINVAL; + + decimator = w->shift; + + dev_dbg(codec->dev, "%s(): widget = %s decimator = %u\n", __func__, + w->name, decimator); + + tx_vol_ctl_reg = BOLERO_CDC_TX0_TX_PATH_CTL + + TX_MACRO_TX_PATH_OFFSET * decimator; + hpf_gate_reg = BOLERO_CDC_TX0_TX_PATH_SEC2 + + TX_MACRO_TX_PATH_OFFSET * decimator; + dec_cfg_reg = BOLERO_CDC_TX0_TX_PATH_CFG0 + + TX_MACRO_TX_PATH_OFFSET * decimator; + tx_gain_ctl_reg = BOLERO_CDC_TX0_TX_VOL_CTL + + TX_MACRO_TX_PATH_OFFSET * decimator; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* Enable TX PGA Mute */ + snd_soc_update_bits(codec, tx_vol_ctl_reg, 0x10, 0x10); + break; + case SND_SOC_DAPM_POST_PMU: + snd_soc_update_bits(codec, tx_vol_ctl_reg, 0x20, 0x20); + snd_soc_update_bits(codec, hpf_gate_reg, 0x01, 0x00); + + hpf_cut_off_freq = (snd_soc_read(codec, dec_cfg_reg) & + TX_HPF_CUT_OFF_FREQ_MASK) >> 5; + tx_priv->tx_hpf_work[decimator].hpf_cut_off_freq = + hpf_cut_off_freq; + + if (hpf_cut_off_freq != CF_MIN_3DB_150HZ) + snd_soc_update_bits(codec, dec_cfg_reg, + TX_HPF_CUT_OFF_FREQ_MASK, + CF_MIN_3DB_150HZ << 5); + /* schedule work queue to Remove Mute */ + schedule_delayed_work(&tx_priv->tx_mute_dwork[decimator].dwork, + msecs_to_jiffies(tx_unmute_delay)); + if (tx_priv->tx_hpf_work[decimator].hpf_cut_off_freq != + CF_MIN_3DB_150HZ) { + schedule_delayed_work( + &tx_priv->tx_hpf_work[decimator].dwork, + msecs_to_jiffies(300)); + snd_soc_update_bits(codec, hpf_gate_reg, 0x02, 0x02); + /* + * Minimum 1 clk cycle delay is required as per HW spec + */ + usleep_range(1000, 1010); + snd_soc_update_bits(codec, hpf_gate_reg, 0x02, 0x00); + } + /* apply gain after decimator is enabled */ + snd_soc_write(codec, tx_gain_ctl_reg, + snd_soc_read(codec, tx_gain_ctl_reg)); + break; + case SND_SOC_DAPM_PRE_PMD: + hpf_cut_off_freq = + tx_priv->tx_hpf_work[decimator].hpf_cut_off_freq; + snd_soc_update_bits(codec, tx_vol_ctl_reg, 0x10, 0x10); + if (cancel_delayed_work_sync( + &tx_priv->tx_hpf_work[decimator].dwork)) { + if (hpf_cut_off_freq != CF_MIN_3DB_150HZ) { + snd_soc_update_bits(codec, dec_cfg_reg, + TX_HPF_CUT_OFF_FREQ_MASK, + hpf_cut_off_freq << 5); + snd_soc_update_bits(codec, hpf_gate_reg, + 0x02, 0x02); + /* + * Minimum 1 clk cycle delay is required + * as per HW spec + */ + usleep_range(1000, 1010); + snd_soc_update_bits(codec, hpf_gate_reg, + 0x02, 0x00); + } + } + cancel_delayed_work_sync( + &tx_priv->tx_mute_dwork[decimator].dwork); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_update_bits(codec, tx_vol_ctl_reg, 0x20, 0x00); + snd_soc_update_bits(codec, tx_vol_ctl_reg, 0x10, 0x00); + break; + } + return 0; +} + +static int tx_macro_enable_micbias(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + return 0; +} + +static int tx_macro_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + int tx_fs_rate = -EINVAL; + struct snd_soc_codec *codec = dai->codec; + u32 decimator = 0; + u32 sample_rate = 0; + u16 tx_fs_reg = 0; + struct device *tx_dev = NULL; + struct tx_macro_priv *tx_priv = NULL; + + if (!tx_macro_get_data(codec, &tx_dev, &tx_priv, __func__)) + return -EINVAL; + + pr_debug("%s: dai_name = %s DAI-ID %x rate %d num_ch %d\n", __func__, + dai->name, dai->id, params_rate(params), + params_channels(params)); + + sample_rate = params_rate(params); + switch (sample_rate) { + case 8000: + tx_fs_rate = 0; + break; + case 16000: + tx_fs_rate = 1; + break; + case 32000: + tx_fs_rate = 3; + break; + case 48000: + tx_fs_rate = 4; + break; + case 96000: + tx_fs_rate = 5; + break; + case 192000: + tx_fs_rate = 6; + break; + case 384000: + tx_fs_rate = 7; + break; + default: + dev_err(codec->dev, "%s: Invalid TX sample rate: %d\n", + __func__, params_rate(params)); + return -EINVAL; + } + for_each_set_bit(decimator, &tx_priv->active_ch_mask[dai->id], + TX_MACRO_DEC_MAX) { + if (decimator >= 0) { + tx_fs_reg = BOLERO_CDC_TX0_TX_PATH_CTL + + TX_MACRO_TX_PATH_OFFSET * decimator; + dev_dbg(codec->dev, "%s: set DEC%u rate to %u\n", + __func__, decimator, sample_rate); + snd_soc_update_bits(codec, tx_fs_reg, 0x0F, + tx_fs_rate); + } else { + dev_err(codec->dev, + "%s: ERROR: Invalid decimator: %d\n", + __func__, decimator); + return -EINVAL; + } + } + return 0; +} + +static int tx_macro_get_channel_map(struct snd_soc_dai *dai, + unsigned int *tx_num, unsigned int *tx_slot, + unsigned int *rx_num, unsigned int *rx_slot) +{ + struct snd_soc_codec *codec = dai->codec; + struct device *tx_dev = NULL; + struct tx_macro_priv *tx_priv = NULL; + + if (!tx_macro_get_data(codec, &tx_dev, &tx_priv, __func__)) + return -EINVAL; + + switch (dai->id) { + case TX_MACRO_AIF1_CAP: + case TX_MACRO_AIF2_CAP: + *tx_slot = tx_priv->active_ch_mask[dai->id]; + *tx_num = tx_priv->active_ch_cnt[dai->id]; + break; + default: + dev_err(tx_dev, "%s: Invalid AIF\n", __func__); + break; + } + return 0; +} + +static struct snd_soc_dai_ops tx_macro_dai_ops = { + .hw_params = tx_macro_hw_params, + .get_channel_map = tx_macro_get_channel_map, +}; + +static struct snd_soc_dai_driver tx_macro_dai[] = { + { + .name = "tx_macro_tx1", + .id = TX_MACRO_AIF1_CAP, + .capture = { + .stream_name = "TX_AIF1 Capture", + .rates = TX_MACRO_RATES, + .formats = TX_MACRO_FORMATS, + .rate_max = 192000, + .rate_min = 8000, + .channels_min = 1, + .channels_max = 8, + }, + .ops = &tx_macro_dai_ops, + }, + { + .name = "tx_macro_tx2", + .id = TX_MACRO_AIF2_CAP, + .capture = { + .stream_name = "TX_AIF2 Capture", + .rates = TX_MACRO_RATES, + .formats = TX_MACRO_FORMATS, + .rate_max = 192000, + .rate_min = 8000, + .channels_min = 1, + .channels_max = 8, + }, + .ops = &tx_macro_dai_ops, + }, +}; + +#define STRING(name) #name +#define TX_MACRO_DAPM_ENUM(name, reg, offset, text) \ +static SOC_ENUM_SINGLE_DECL(name##_enum, reg, offset, text); \ +static const struct snd_kcontrol_new name##_mux = \ + SOC_DAPM_ENUM(STRING(name), name##_enum) + +#define TX_MACRO_DAPM_ENUM_EXT(name, reg, offset, text, getname, putname) \ +static SOC_ENUM_SINGLE_DECL(name##_enum, reg, offset, text); \ +static const struct snd_kcontrol_new name##_mux = \ + SOC_DAPM_ENUM_EXT(STRING(name), name##_enum, getname, putname) + +#define TX_MACRO_DAPM_MUX(name, shift, kctl) \ + SND_SOC_DAPM_MUX(name, SND_SOC_NOPM, shift, 0, &kctl##_mux) + +static const char * const adc_mux_text[] = { + "MSM_DMIC", "SWR_MIC", "ANC_FB_TUNE1" +}; + +TX_MACRO_DAPM_ENUM(tx_dec0, BOLERO_CDC_TX_INP_MUX_ADC_MUX0_CFG1, + 0, adc_mux_text); +TX_MACRO_DAPM_ENUM(tx_dec1, BOLERO_CDC_TX_INP_MUX_ADC_MUX1_CFG1, + 0, adc_mux_text); +TX_MACRO_DAPM_ENUM(tx_dec2, BOLERO_CDC_TX_INP_MUX_ADC_MUX2_CFG1, + 0, adc_mux_text); +TX_MACRO_DAPM_ENUM(tx_dec3, BOLERO_CDC_TX_INP_MUX_ADC_MUX3_CFG1, + 0, adc_mux_text); +TX_MACRO_DAPM_ENUM(tx_dec4, BOLERO_CDC_TX_INP_MUX_ADC_MUX4_CFG1, + 0, adc_mux_text); +TX_MACRO_DAPM_ENUM(tx_dec5, BOLERO_CDC_TX_INP_MUX_ADC_MUX5_CFG1, + 0, adc_mux_text); +TX_MACRO_DAPM_ENUM(tx_dec6, BOLERO_CDC_TX_INP_MUX_ADC_MUX6_CFG1, + 0, adc_mux_text); +TX_MACRO_DAPM_ENUM(tx_dec7, BOLERO_CDC_TX_INP_MUX_ADC_MUX7_CFG1, + 0, adc_mux_text); + + +static const char * const dmic_mux_text[] = { + "ZERO", "DMIC0", "DMIC1", "DMIC2", "DMIC3", + "DMIC4", "DMIC5", "DMIC6", "DMIC7" +}; + +TX_MACRO_DAPM_ENUM_EXT(tx_dmic0, BOLERO_CDC_TX_INP_MUX_ADC_MUX0_CFG0, + 4, dmic_mux_text, snd_soc_dapm_get_enum_double, + tx_macro_put_dec_enum); + +TX_MACRO_DAPM_ENUM_EXT(tx_dmic1, BOLERO_CDC_TX_INP_MUX_ADC_MUX1_CFG0, + 4, dmic_mux_text, snd_soc_dapm_get_enum_double, + tx_macro_put_dec_enum); + +TX_MACRO_DAPM_ENUM_EXT(tx_dmic2, BOLERO_CDC_TX_INP_MUX_ADC_MUX2_CFG0, + 4, dmic_mux_text, snd_soc_dapm_get_enum_double, + tx_macro_put_dec_enum); + +TX_MACRO_DAPM_ENUM_EXT(tx_dmic3, BOLERO_CDC_TX_INP_MUX_ADC_MUX3_CFG0, + 4, dmic_mux_text, snd_soc_dapm_get_enum_double, + tx_macro_put_dec_enum); + +TX_MACRO_DAPM_ENUM_EXT(tx_dmic4, BOLERO_CDC_TX_INP_MUX_ADC_MUX4_CFG0, + 4, dmic_mux_text, snd_soc_dapm_get_enum_double, + tx_macro_put_dec_enum); + +TX_MACRO_DAPM_ENUM_EXT(tx_dmic5, BOLERO_CDC_TX_INP_MUX_ADC_MUX5_CFG0, + 4, dmic_mux_text, snd_soc_dapm_get_enum_double, + tx_macro_put_dec_enum); + +TX_MACRO_DAPM_ENUM_EXT(tx_dmic6, BOLERO_CDC_TX_INP_MUX_ADC_MUX6_CFG0, + 4, dmic_mux_text, snd_soc_dapm_get_enum_double, + tx_macro_put_dec_enum); + +TX_MACRO_DAPM_ENUM_EXT(tx_dmic7, BOLERO_CDC_TX_INP_MUX_ADC_MUX7_CFG0, + 4, dmic_mux_text, snd_soc_dapm_get_enum_double, + tx_macro_put_dec_enum); + +static const char * const smic_mux_text[] = { + "ZERO", "ADC0", "ADC1", "ADC2", "ADC3", + "SWR_DMIC0", "SWR_DMIC1", "SWR_DMIC2", "SWR_DMIC3", + "SWR_DMIC4", "SWR_DMIC5", "SWR_DMIC6", "SWR_DMIC7" +}; + +TX_MACRO_DAPM_ENUM_EXT(tx_smic0, BOLERO_CDC_TX_INP_MUX_ADC_MUX0_CFG0, + 0, smic_mux_text, snd_soc_dapm_get_enum_double, + tx_macro_put_dec_enum); + +TX_MACRO_DAPM_ENUM_EXT(tx_smic1, BOLERO_CDC_TX_INP_MUX_ADC_MUX1_CFG0, + 0, smic_mux_text, snd_soc_dapm_get_enum_double, + tx_macro_put_dec_enum); + +TX_MACRO_DAPM_ENUM_EXT(tx_smic2, BOLERO_CDC_TX_INP_MUX_ADC_MUX2_CFG0, + 0, smic_mux_text, snd_soc_dapm_get_enum_double, + tx_macro_put_dec_enum); + +TX_MACRO_DAPM_ENUM_EXT(tx_smic3, BOLERO_CDC_TX_INP_MUX_ADC_MUX3_CFG0, + 0, smic_mux_text, snd_soc_dapm_get_enum_double, + tx_macro_put_dec_enum); + +TX_MACRO_DAPM_ENUM_EXT(tx_smic4, BOLERO_CDC_TX_INP_MUX_ADC_MUX4_CFG0, + 0, smic_mux_text, snd_soc_dapm_get_enum_double, + tx_macro_put_dec_enum); + +TX_MACRO_DAPM_ENUM_EXT(tx_smic5, BOLERO_CDC_TX_INP_MUX_ADC_MUX5_CFG0, + 0, smic_mux_text, snd_soc_dapm_get_enum_double, + tx_macro_put_dec_enum); + +TX_MACRO_DAPM_ENUM_EXT(tx_smic6, BOLERO_CDC_TX_INP_MUX_ADC_MUX6_CFG0, + 0, smic_mux_text, snd_soc_dapm_get_enum_double, + tx_macro_put_dec_enum); + +TX_MACRO_DAPM_ENUM_EXT(tx_smic7, BOLERO_CDC_TX_INP_MUX_ADC_MUX7_CFG0, + 0, smic_mux_text, snd_soc_dapm_get_enum_double, + tx_macro_put_dec_enum); + +static const struct snd_kcontrol_new tx_aif1_cap_mixer[] = { + SOC_SINGLE_EXT("DEC0", SND_SOC_NOPM, TX_MACRO_DEC0, 1, 0, + tx_macro_tx_mixer_get, tx_macro_tx_mixer_put), + SOC_SINGLE_EXT("DEC1", SND_SOC_NOPM, TX_MACRO_DEC1, 1, 0, + tx_macro_tx_mixer_get, tx_macro_tx_mixer_put), + SOC_SINGLE_EXT("DEC2", SND_SOC_NOPM, TX_MACRO_DEC2, 1, 0, + tx_macro_tx_mixer_get, tx_macro_tx_mixer_put), + SOC_SINGLE_EXT("DEC3", SND_SOC_NOPM, TX_MACRO_DEC3, 1, 0, + tx_macro_tx_mixer_get, tx_macro_tx_mixer_put), + SOC_SINGLE_EXT("DEC4", SND_SOC_NOPM, TX_MACRO_DEC4, 1, 0, + tx_macro_tx_mixer_get, tx_macro_tx_mixer_put), + SOC_SINGLE_EXT("DEC5", SND_SOC_NOPM, TX_MACRO_DEC5, 1, 0, + tx_macro_tx_mixer_get, tx_macro_tx_mixer_put), + SOC_SINGLE_EXT("DEC6", SND_SOC_NOPM, TX_MACRO_DEC6, 1, 0, + tx_macro_tx_mixer_get, tx_macro_tx_mixer_put), + SOC_SINGLE_EXT("DEC7", SND_SOC_NOPM, TX_MACRO_DEC7, 1, 0, + tx_macro_tx_mixer_get, tx_macro_tx_mixer_put), +}; + +static const struct snd_kcontrol_new tx_aif2_cap_mixer[] = { + SOC_SINGLE_EXT("DEC0", SND_SOC_NOPM, TX_MACRO_DEC0, 1, 0, + tx_macro_tx_mixer_get, tx_macro_tx_mixer_put), + SOC_SINGLE_EXT("DEC1", SND_SOC_NOPM, TX_MACRO_DEC1, 1, 0, + tx_macro_tx_mixer_get, tx_macro_tx_mixer_put), + SOC_SINGLE_EXT("DEC2", SND_SOC_NOPM, TX_MACRO_DEC2, 1, 0, + tx_macro_tx_mixer_get, tx_macro_tx_mixer_put), + SOC_SINGLE_EXT("DEC3", SND_SOC_NOPM, TX_MACRO_DEC3, 1, 0, + tx_macro_tx_mixer_get, tx_macro_tx_mixer_put), + SOC_SINGLE_EXT("DEC4", SND_SOC_NOPM, TX_MACRO_DEC4, 1, 0, + tx_macro_tx_mixer_get, tx_macro_tx_mixer_put), + SOC_SINGLE_EXT("DEC5", SND_SOC_NOPM, TX_MACRO_DEC5, 1, 0, + tx_macro_tx_mixer_get, tx_macro_tx_mixer_put), + SOC_SINGLE_EXT("DEC6", SND_SOC_NOPM, TX_MACRO_DEC6, 1, 0, + tx_macro_tx_mixer_get, tx_macro_tx_mixer_put), + SOC_SINGLE_EXT("DEC7", SND_SOC_NOPM, TX_MACRO_DEC7, 1, 0, + tx_macro_tx_mixer_get, tx_macro_tx_mixer_put), +}; + +static const struct snd_soc_dapm_widget tx_macro_dapm_widgets[] = { + SND_SOC_DAPM_AIF_OUT("TX_AIF1 CAP", "TX_AIF1 Capture", 0, + SND_SOC_NOPM, TX_MACRO_AIF1_CAP, 0), + + SND_SOC_DAPM_AIF_OUT("TX_AIF2 CAP", "TX_AIF2 Capture", 0, + SND_SOC_NOPM, TX_MACRO_AIF2_CAP, 0), + + SND_SOC_DAPM_MIXER("TX_AIF1_CAP Mixer", SND_SOC_NOPM, TX_MACRO_AIF1_CAP, 0, + tx_aif1_cap_mixer, ARRAY_SIZE(tx_aif1_cap_mixer)), + + SND_SOC_DAPM_MIXER("TX_AIF2_CAP Mixer", SND_SOC_NOPM, TX_MACRO_AIF2_CAP, 0, + tx_aif2_cap_mixer, ARRAY_SIZE(tx_aif2_cap_mixer)), + + + TX_MACRO_DAPM_MUX("TX DMIC MUX0", 0, tx_dmic0), + TX_MACRO_DAPM_MUX("TX DMIC MUX1", 0, tx_dmic1), + TX_MACRO_DAPM_MUX("TX DMIC MUX2", 0, tx_dmic2), + TX_MACRO_DAPM_MUX("TX DMIC MUX3", 0, tx_dmic3), + TX_MACRO_DAPM_MUX("TX DMIC MUX4", 0, tx_dmic4), + TX_MACRO_DAPM_MUX("TX DMIC MUX5", 0, tx_dmic5), + TX_MACRO_DAPM_MUX("TX DMIC MUX6", 0, tx_dmic6), + TX_MACRO_DAPM_MUX("TX DMIC MUX7", 0, tx_dmic7), + + TX_MACRO_DAPM_MUX("TX SMIC MUX0", 0, tx_smic0), + TX_MACRO_DAPM_MUX("TX SMIC MUX1", 0, tx_smic1), + TX_MACRO_DAPM_MUX("TX SMIC MUX2", 0, tx_smic2), + TX_MACRO_DAPM_MUX("TX SMIC MUX3", 0, tx_smic3), + TX_MACRO_DAPM_MUX("TX SMIC MUX4", 0, tx_smic4), + TX_MACRO_DAPM_MUX("TX SMIC MUX5", 0, tx_smic5), + TX_MACRO_DAPM_MUX("TX SMIC MUX6", 0, tx_smic6), + TX_MACRO_DAPM_MUX("TX SMIC MUX7", 0, tx_smic7), + + SND_SOC_DAPM_MICBIAS_E("TX MIC BIAS1", SND_SOC_NOPM, 0, 0, + tx_macro_enable_micbias, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("TX DMIC0", NULL, SND_SOC_NOPM, 0, 0, + tx_macro_enable_dmic, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_ADC_E("TX DMIC1", NULL, SND_SOC_NOPM, 0, 0, + tx_macro_enable_dmic, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_ADC_E("TX DMIC2", NULL, SND_SOC_NOPM, 0, 0, + tx_macro_enable_dmic, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_ADC_E("TX DMIC3", NULL, SND_SOC_NOPM, 0, 0, + tx_macro_enable_dmic, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_ADC_E("TX DMIC4", NULL, SND_SOC_NOPM, 0, 0, + tx_macro_enable_dmic, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_ADC_E("TX DMIC5", NULL, SND_SOC_NOPM, 0, 0, + tx_macro_enable_dmic, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_ADC_E("TX DMIC6", NULL, SND_SOC_NOPM, 0, 0, + tx_macro_enable_dmic, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_ADC_E("TX DMIC7", NULL, SND_SOC_NOPM, 0, 0, + tx_macro_enable_dmic, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_INPUT("TX SWR_ADC0"), + SND_SOC_DAPM_INPUT("TX SWR_ADC1"), + SND_SOC_DAPM_INPUT("TX SWR_ADC2"), + SND_SOC_DAPM_INPUT("TX SWR_ADC3"), + SND_SOC_DAPM_INPUT("TX SWR_DMIC0"), + SND_SOC_DAPM_INPUT("TX SWR_DMIC1"), + SND_SOC_DAPM_INPUT("TX SWR_DMIC2"), + SND_SOC_DAPM_INPUT("TX SWR_DMIC3"), + SND_SOC_DAPM_INPUT("TX SWR_DMIC4"), + SND_SOC_DAPM_INPUT("TX SWR_DMIC5"), + SND_SOC_DAPM_INPUT("TX SWR_DMIC6"), + SND_SOC_DAPM_INPUT("TX SWR_DMIC7"), + + SND_SOC_DAPM_MUX_E("TX DEC0 MUX", SND_SOC_NOPM, + TX_MACRO_DEC0, 0, + &tx_dec0_mux, tx_macro_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX_E("TX DEC1 MUX", SND_SOC_NOPM, + TX_MACRO_DEC1, 0, + &tx_dec1_mux, tx_macro_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX_E("TX DEC2 MUX", SND_SOC_NOPM, + TX_MACRO_DEC2, 0, + &tx_dec2_mux, tx_macro_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX_E("TX DEC3 MUX", SND_SOC_NOPM, + TX_MACRO_DEC3, 0, + &tx_dec3_mux, tx_macro_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX_E("TX DEC4 MUX", SND_SOC_NOPM, + TX_MACRO_DEC4, 0, + &tx_dec4_mux, tx_macro_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX_E("TX DEC5 MUX", SND_SOC_NOPM, + TX_MACRO_DEC5, 0, + &tx_dec5_mux, tx_macro_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX_E("TX DEC6 MUX", SND_SOC_NOPM, + TX_MACRO_DEC6, 0, + &tx_dec6_mux, tx_macro_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX_E("TX DEC7 MUX", SND_SOC_NOPM, + TX_MACRO_DEC7, 0, + &tx_dec7_mux, tx_macro_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_SUPPLY_S("TX_MCLK", 0, SND_SOC_NOPM, 0, 0, + tx_macro_mclk_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +}; + +static const struct snd_soc_dapm_route tx_audio_map[] = { + {"TX_AIF1 CAP", NULL, "TX_MCLK"}, + {"TX_AIF2 CAP", NULL, "TX_MCLK"}, + + {"TX_AIF1 CAP", NULL, "TX_AIF1_CAP Mixer"}, + {"TX_AIF2 CAP", NULL, "TX_AIF2_CAP Mixer"}, + + {"TX_AIF1_CAP Mixer", "DEC0", "TX DEC0 MUX"}, + {"TX_AIF1_CAP Mixer", "DEC1", "TX DEC1 MUX"}, + {"TX_AIF1_CAP Mixer", "DEC2", "TX DEC2 MUX"}, + {"TX_AIF1_CAP Mixer", "DEC3", "TX DEC3 MUX"}, + {"TX_AIF1_CAP Mixer", "DEC4", "TX DEC4 MUX"}, + {"TX_AIF1_CAP Mixer", "DEC5", "TX DEC5 MUX"}, + {"TX_AIF1_CAP Mixer", "DEC6", "TX DEC6 MUX"}, + {"TX_AIF1_CAP Mixer", "DEC7", "TX DEC7 MUX"}, + + {"TX_AIF2_CAP Mixer", "DEC0", "TX DEC0 MUX"}, + {"TX_AIF2_CAP Mixer", "DEC1", "TX DEC1 MUX"}, + {"TX_AIF2_CAP Mixer", "DEC2", "TX DEC2 MUX"}, + {"TX_AIF2_CAP Mixer", "DEC3", "TX DEC3 MUX"}, + {"TX_AIF2_CAP Mixer", "DEC4", "TX DEC4 MUX"}, + {"TX_AIF2_CAP Mixer", "DEC5", "TX DEC5 MUX"}, + {"TX_AIF2_CAP Mixer", "DEC6", "TX DEC6 MUX"}, + {"TX_AIF2_CAP Mixer", "DEC7", "TX DEC7 MUX"}, + + {"TX DEC0 MUX", NULL, "TX_MCLK"}, + {"TX DEC1 MUX", NULL, "TX_MCLK"}, + {"TX DEC2 MUX", NULL, "TX_MCLK"}, + {"TX DEC3 MUX", NULL, "TX_MCLK"}, + {"TX DEC4 MUX", NULL, "TX_MCLK"}, + {"TX DEC5 MUX", NULL, "TX_MCLK"}, + {"TX DEC6 MUX", NULL, "TX_MCLK"}, + {"TX DEC7 MUX", NULL, "TX_MCLK"}, + + {"TX DEC0 MUX", "MSM_DMIC", "TX DMIC MUX0"}, + {"TX DMIC MUX0", "DMIC0", "TX DMIC0"}, + {"TX DMIC MUX0", "DMIC1", "TX DMIC1"}, + {"TX DMIC MUX0", "DMIC2", "TX DMIC2"}, + {"TX DMIC MUX0", "DMIC3", "TX DMIC3"}, + {"TX DMIC MUX0", "DMIC4", "TX DMIC4"}, + {"TX DMIC MUX0", "DMIC5", "TX DMIC5"}, + {"TX DMIC MUX0", "DMIC6", "TX DMIC6"}, + {"TX DMIC MUX0", "DMIC7", "TX DMIC7"}, + + {"TX DEC0 MUX", "SWR_MIC", "TX SMIC MUX0"}, + {"TX SMIC MUX0", "ADC0", "TX SWR_ADC0"}, + {"TX SMIC MUX0", "ADC1", "TX SWR_ADC1"}, + {"TX SMIC MUX0", "ADC2", "TX SWR_ADC2"}, + {"TX SMIC MUX0", "ADC3", "TX SWR_ADC3"}, + {"TX SMIC MUX0", "SWR_DMIC0", "TX SWR_DMIC0"}, + {"TX SMIC MUX0", "SWR_DMIC1", "TX SWR_DMIC1"}, + {"TX SMIC MUX0", "SWR_DMIC2", "TX SWR_DMIC2"}, + {"TX SMIC MUX0", "SWR_DMIC3", "TX SWR_DMIC3"}, + {"TX SMIC MUX0", "SWR_DMIC4", "TX SWR_DMIC4"}, + {"TX SMIC MUX0", "SWR_DMIC5", "TX SWR_DMIC5"}, + {"TX SMIC MUX0", "SWR_DMIC6", "TX SWR_DMIC6"}, + {"TX SMIC MUX0", "SWR_DMIC7", "TX SWR_DMIC7"}, + + {"TX DEC1 MUX", "MSM_DMIC", "TX DMIC MUX1"}, + {"TX DMIC MUX1", "DMIC0", "TX DMIC0"}, + {"TX DMIC MUX1", "DMIC1", "TX DMIC1"}, + {"TX DMIC MUX1", "DMIC2", "TX DMIC2"}, + {"TX DMIC MUX1", "DMIC3", "TX DMIC3"}, + {"TX DMIC MUX1", "DMIC4", "TX DMIC4"}, + {"TX DMIC MUX1", "DMIC5", "TX DMIC5"}, + {"TX DMIC MUX1", "DMIC6", "TX DMIC6"}, + {"TX DMIC MUX1", "DMIC7", "TX DMIC7"}, + + {"TX DEC1 MUX", "SWR_MIC", "TX SMIC MUX1"}, + {"TX SMIC MUX1", "ADC0", "TX SWR_ADC0"}, + {"TX SMIC MUX1", "ADC1", "TX SWR_ADC1"}, + {"TX SMIC MUX1", "ADC2", "TX SWR_ADC2"}, + {"TX SMIC MUX1", "ADC3", "TX SWR_ADC3"}, + {"TX SMIC MUX1", "SWR_DMIC0", "TX SWR_DMIC0"}, + {"TX SMIC MUX1", "SWR_DMIC1", "TX SWR_DMIC1"}, + {"TX SMIC MUX1", "SWR_DMIC2", "TX SWR_DMIC2"}, + {"TX SMIC MUX1", "SWR_DMIC3", "TX SWR_DMIC3"}, + {"TX SMIC MUX1", "SWR_DMIC4", "TX SWR_DMIC4"}, + {"TX SMIC MUX1", "SWR_DMIC5", "TX SWR_DMIC5"}, + {"TX SMIC MUX1", "SWR_DMIC6", "TX SWR_DMIC6"}, + {"TX SMIC MUX1", "SWR_DMIC7", "TX SWR_DMIC7"}, + + {"TX DEC2 MUX", "MSM_DMIC", "TX DMIC MUX2"}, + {"TX DMIC MUX2", "DMIC0", "TX DMIC0"}, + {"TX DMIC MUX2", "DMIC1", "TX DMIC1"}, + {"TX DMIC MUX2", "DMIC2", "TX DMIC2"}, + {"TX DMIC MUX2", "DMIC3", "TX DMIC3"}, + {"TX DMIC MUX2", "DMIC4", "TX DMIC4"}, + {"TX DMIC MUX2", "DMIC5", "TX DMIC5"}, + {"TX DMIC MUX2", "DMIC6", "TX DMIC6"}, + {"TX DMIC MUX2", "DMIC7", "TX DMIC7"}, + + {"TX DEC2 MUX", "SWR_MIC", "TX SMIC MUX2"}, + {"TX SMIC MUX2", "ADC0", "TX SWR_ADC0"}, + {"TX SMIC MUX2", "ADC1", "TX SWR_ADC1"}, + {"TX SMIC MUX2", "ADC2", "TX SWR_ADC2"}, + {"TX SMIC MUX2", "ADC3", "TX SWR_ADC3"}, + {"TX SMIC MUX2", "SWR_DMIC0", "TX SWR_DMIC0"}, + {"TX SMIC MUX2", "SWR_DMIC1", "TX SWR_DMIC1"}, + {"TX SMIC MUX2", "SWR_DMIC2", "TX SWR_DMIC2"}, + {"TX SMIC MUX2", "SWR_DMIC3", "TX SWR_DMIC3"}, + {"TX SMIC MUX2", "SWR_DMIC4", "TX SWR_DMIC4"}, + {"TX SMIC MUX2", "SWR_DMIC5", "TX SWR_DMIC5"}, + {"TX SMIC MUX2", "SWR_DMIC6", "TX SWR_DMIC6"}, + {"TX SMIC MUX2", "SWR_DMIC7", "TX SWR_DMIC7"}, + + {"TX DEC3 MUX", "MSM_DMIC", "TX DMIC MUX3"}, + {"TX DMIC MUX3", "DMIC0", "TX DMIC0"}, + {"TX DMIC MUX3", "DMIC1", "TX DMIC1"}, + {"TX DMIC MUX3", "DMIC2", "TX DMIC2"}, + {"TX DMIC MUX3", "DMIC3", "TX DMIC3"}, + {"TX DMIC MUX3", "DMIC4", "TX DMIC4"}, + {"TX DMIC MUX3", "DMIC5", "TX DMIC5"}, + {"TX DMIC MUX3", "DMIC6", "TX DMIC6"}, + {"TX DMIC MUX3", "DMIC7", "TX DMIC7"}, + + {"TX DEC3 MUX", "SWR_MIC", "TX SMIC MUX3"}, + {"TX SMIC MUX3", "ADC0", "TX SWR_ADC0"}, + {"TX SMIC MUX3", "ADC1", "TX SWR_ADC1"}, + {"TX SMIC MUX3", "ADC2", "TX SWR_ADC2"}, + {"TX SMIC MUX3", "ADC3", "TX SWR_ADC3"}, + {"TX SMIC MUX3", "SWR_DMIC0", "TX SWR_DMIC0"}, + {"TX SMIC MUX3", "SWR_DMIC1", "TX SWR_DMIC1"}, + {"TX SMIC MUX3", "SWR_DMIC2", "TX SWR_DMIC2"}, + {"TX SMIC MUX3", "SWR_DMIC3", "TX SWR_DMIC3"}, + {"TX SMIC MUX3", "SWR_DMIC4", "TX SWR_DMIC4"}, + {"TX SMIC MUX3", "SWR_DMIC5", "TX SWR_DMIC5"}, + {"TX SMIC MUX3", "SWR_DMIC6", "TX SWR_DMIC6"}, + {"TX SMIC MUX3", "SWR_DMIC7", "TX SWR_DMIC7"}, + + {"TX DEC4 MUX", "MSM_DMIC", "TX DMIC MUX4"}, + {"TX DMIC MUX4", "DMIC0", "TX DMIC0"}, + {"TX DMIC MUX4", "DMIC1", "TX DMIC1"}, + {"TX DMIC MUX4", "DMIC2", "TX DMIC2"}, + {"TX DMIC MUX4", "DMIC3", "TX DMIC3"}, + {"TX DMIC MUX4", "DMIC4", "TX DMIC4"}, + {"TX DMIC MUX4", "DMIC5", "TX DMIC5"}, + {"TX DMIC MUX4", "DMIC6", "TX DMIC6"}, + {"TX DMIC MUX4", "DMIC7", "TX DMIC7"}, + + {"TX DEC4 MUX", "SWR_MIC", "TX SMIC MUX4"}, + {"TX SMIC MUX4", "ADC0", "TX SWR_ADC0"}, + {"TX SMIC MUX4", "ADC1", "TX SWR_ADC1"}, + {"TX SMIC MUX4", "ADC2", "TX SWR_ADC2"}, + {"TX SMIC MUX4", "ADC3", "TX SWR_ADC3"}, + {"TX SMIC MUX4", "SWR_DMIC0", "TX SWR_DMIC0"}, + {"TX SMIC MUX4", "SWR_DMIC1", "TX SWR_DMIC1"}, + {"TX SMIC MUX4", "SWR_DMIC2", "TX SWR_DMIC2"}, + {"TX SMIC MUX4", "SWR_DMIC3", "TX SWR_DMIC3"}, + {"TX SMIC MUX4", "SWR_DMIC4", "TX SWR_DMIC4"}, + {"TX SMIC MUX4", "SWR_DMIC5", "TX SWR_DMIC5"}, + {"TX SMIC MUX4", "SWR_DMIC6", "TX SWR_DMIC6"}, + {"TX SMIC MUX4", "SWR_DMIC7", "TX SWR_DMIC7"}, + + {"TX DEC5 MUX", "MSM_DMIC", "TX DMIC MUX5"}, + {"TX DMIC MUX5", "DMIC0", "TX DMIC0"}, + {"TX DMIC MUX5", "DMIC1", "TX DMIC1"}, + {"TX DMIC MUX5", "DMIC2", "TX DMIC2"}, + {"TX DMIC MUX5", "DMIC3", "TX DMIC3"}, + {"TX DMIC MUX5", "DMIC4", "TX DMIC4"}, + {"TX DMIC MUX5", "DMIC5", "TX DMIC5"}, + {"TX DMIC MUX5", "DMIC6", "TX DMIC6"}, + {"TX DMIC MUX5", "DMIC7", "TX DMIC7"}, + + {"TX DEC5 MUX", "SWR_MIC", "TX SMIC MUX5"}, + {"TX SMIC MUX5", "ADC0", "TX SWR_ADC0"}, + {"TX SMIC MUX5", "ADC1", "TX SWR_ADC1"}, + {"TX SMIC MUX5", "ADC2", "TX SWR_ADC2"}, + {"TX SMIC MUX5", "ADC3", "TX SWR_ADC3"}, + {"TX SMIC MUX5", "SWR_DMIC0", "TX SWR_DMIC0"}, + {"TX SMIC MUX5", "SWR_DMIC1", "TX SWR_DMIC1"}, + {"TX SMIC MUX5", "SWR_DMIC2", "TX SWR_DMIC2"}, + {"TX SMIC MUX5", "SWR_DMIC3", "TX SWR_DMIC3"}, + {"TX SMIC MUX5", "SWR_DMIC4", "TX SWR_DMIC4"}, + {"TX SMIC MUX5", "SWR_DMIC5", "TX SWR_DMIC5"}, + {"TX SMIC MUX5", "SWR_DMIC6", "TX SWR_DMIC6"}, + {"TX SMIC MUX5", "SWR_DMIC7", "TX SWR_DMIC7"}, + + {"TX DEC6 MUX", "MSM_DMIC", "TX DMIC MUX6"}, + {"TX DMIC MUX6", "DMIC0", "TX DMIC0"}, + {"TX DMIC MUX6", "DMIC1", "TX DMIC1"}, + {"TX DMIC MUX6", "DMIC2", "TX DMIC2"}, + {"TX DMIC MUX6", "DMIC3", "TX DMIC3"}, + {"TX DMIC MUX6", "DMIC4", "TX DMIC4"}, + {"TX DMIC MUX6", "DMIC5", "TX DMIC5"}, + {"TX DMIC MUX6", "DMIC6", "TX DMIC6"}, + {"TX DMIC MUX6", "DMIC7", "TX DMIC7"}, + + {"TX DEC6 MUX", "SWR_MIC", "TX SMIC MUX6"}, + {"TX SMIC MUX6", "ADC0", "TX SWR_ADC0"}, + {"TX SMIC MUX6", "ADC1", "TX SWR_ADC1"}, + {"TX SMIC MUX6", "ADC2", "TX SWR_ADC2"}, + {"TX SMIC MUX6", "ADC3", "TX SWR_ADC3"}, + {"TX SMIC MUX6", "SWR_DMIC0", "TX SWR_DMIC0"}, + {"TX SMIC MUX6", "SWR_DMIC1", "TX SWR_DMIC1"}, + {"TX SMIC MUX6", "SWR_DMIC2", "TX SWR_DMIC2"}, + {"TX SMIC MUX6", "SWR_DMIC3", "TX SWR_DMIC3"}, + {"TX SMIC MUX6", "SWR_DMIC4", "TX SWR_DMIC4"}, + {"TX SMIC MUX6", "SWR_DMIC5", "TX SWR_DMIC5"}, + {"TX SMIC MUX6", "SWR_DMIC6", "TX SWR_DMIC6"}, + {"TX SMIC MUX6", "SWR_DMIC7", "TX SWR_DMIC7"}, + + {"TX DEC7 MUX", "MSM_DMIC", "TX DMIC MUX7"}, + {"TX DMIC MUX7", "DMIC0", "TX DMIC0"}, + {"TX DMIC MUX7", "DMIC1", "TX DMIC1"}, + {"TX DMIC MUX7", "DMIC2", "TX DMIC2"}, + {"TX DMIC MUX7", "DMIC3", "TX DMIC3"}, + {"TX DMIC MUX7", "DMIC4", "TX DMIC4"}, + {"TX DMIC MUX7", "DMIC5", "TX DMIC5"}, + {"TX DMIC MUX7", "DMIC6", "TX DMIC6"}, + {"TX DMIC MUX7", "DMIC7", "TX DMIC7"}, + + {"TX DEC7 MUX", "SWR_MIC", "TX SMIC MUX7"}, + {"TX SMIC MUX7", "ADC0", "TX SWR_ADC0"}, + {"TX SMIC MUX7", "ADC1", "TX SWR_ADC1"}, + {"TX SMIC MUX7", "ADC2", "TX SWR_ADC2"}, + {"TX SMIC MUX7", "ADC3", "TX SWR_ADC3"}, + {"TX SMIC MUX7", "SWR_DMIC0", "TX SWR_DMIC0"}, + {"TX SMIC MUX7", "SWR_DMIC1", "TX SWR_DMIC1"}, + {"TX SMIC MUX7", "SWR_DMIC2", "TX SWR_DMIC2"}, + {"TX SMIC MUX7", "SWR_DMIC3", "TX SWR_DMIC3"}, + {"TX SMIC MUX7", "SWR_DMIC4", "TX SWR_DMIC4"}, + {"TX SMIC MUX7", "SWR_DMIC5", "TX SWR_DMIC5"}, + {"TX SMIC MUX7", "SWR_DMIC6", "TX SWR_DMIC6"}, + {"TX SMIC MUX7", "SWR_DMIC7", "TX SWR_DMIC7"}, +}; + +static const struct snd_kcontrol_new tx_macro_snd_controls[] = { + SOC_SINGLE_SX_TLV("TX_DEC0 Volume", + BOLERO_CDC_TX0_TX_VOL_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("TX_DEC1 Volume", + BOLERO_CDC_TX1_TX_VOL_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("TX_DEC2 Volume", + BOLERO_CDC_TX2_TX_VOL_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("TX_DEC3 Volume", + BOLERO_CDC_TX3_TX_VOL_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("TX_DEC4 Volume", + BOLERO_CDC_TX4_TX_VOL_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("TX_DEC5 Volume", + BOLERO_CDC_TX5_TX_VOL_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("TX_DEC6 Volume", + BOLERO_CDC_TX6_TX_VOL_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("TX_DEC7 Volume", + BOLERO_CDC_TX7_TX_VOL_CTL, + 0, -84, 40, digital_gain), +}; + +static int tx_macro_swrm_clock(void *handle, bool enable) +{ + struct tx_macro_priv *tx_priv = (struct tx_macro_priv *) handle; + struct regmap *regmap = dev_get_regmap(tx_priv->dev->parent, NULL); + int ret = 0; + + if (regmap == NULL) { + dev_err(tx_priv->dev, "%s: regmap is NULL\n", __func__); + return -EINVAL; + } + + mutex_lock(&tx_priv->swr_clk_lock); + + dev_dbg(tx_priv->dev, "%s: swrm clock %s\n", + __func__, (enable ? "enable" : "disable")); + if (enable) { + if (tx_priv->swr_clk_users == 0) { + ret = tx_macro_mclk_enable(tx_priv, 1); + if (ret < 0) { + dev_err_ratelimited(tx_priv->dev, + "%s: request clock enable failed\n", + __func__); + goto exit; + } + if (tx_priv->reset_swr) + regmap_update_bits(regmap, + BOLERO_CDC_TX_CLK_RST_CTRL_SWR_CONTROL, + 0x02, 0x02); + regmap_update_bits(regmap, + BOLERO_CDC_TX_CLK_RST_CTRL_SWR_CONTROL, + 0x01, 0x01); + if (tx_priv->reset_swr) + regmap_update_bits(regmap, + BOLERO_CDC_TX_CLK_RST_CTRL_SWR_CONTROL, + 0x02, 0x00); + tx_priv->reset_swr = false; + regmap_update_bits(regmap, + BOLERO_CDC_TX_CLK_RST_CTRL_SWR_CONTROL, + 0x1C, 0x0C); + msm_cdc_pinctrl_select_active_state( + tx_priv->tx_swr_gpio_p); + } + tx_priv->swr_clk_users++; + } else { + if (tx_priv->swr_clk_users <= 0) { + dev_err(tx_priv->dev, + "tx swrm clock users already 0\n"); + tx_priv->swr_clk_users = 0; + goto exit; + } + tx_priv->swr_clk_users--; + if (tx_priv->swr_clk_users == 0) { + regmap_update_bits(regmap, + BOLERO_CDC_TX_CLK_RST_CTRL_SWR_CONTROL, + 0x01, 0x00); + msm_cdc_pinctrl_select_sleep_state( + tx_priv->tx_swr_gpio_p); + tx_macro_mclk_enable(tx_priv, 0); + } + } + dev_dbg(tx_priv->dev, "%s: swrm clock users %d\n", + __func__, tx_priv->swr_clk_users); +exit: + mutex_unlock(&tx_priv->swr_clk_lock); + return ret; +} + +static int tx_macro_validate_dmic_sample_rate(u32 dmic_sample_rate, + struct tx_macro_priv *tx_priv) +{ + u32 div_factor = TX_MACRO_CLK_DIV_2; + u32 mclk_rate = TX_MACRO_MCLK_FREQ; + + if (dmic_sample_rate == TX_MACRO_DMIC_SAMPLE_RATE_UNDEFINED || + mclk_rate % dmic_sample_rate != 0) + goto undefined_rate; + + div_factor = mclk_rate / dmic_sample_rate; + + switch (div_factor) { + case 2: + tx_priv->dmic_clk_div = TX_MACRO_CLK_DIV_2; + break; + case 3: + tx_priv->dmic_clk_div = TX_MACRO_CLK_DIV_3; + break; + case 4: + tx_priv->dmic_clk_div = TX_MACRO_CLK_DIV_4; + break; + case 6: + tx_priv->dmic_clk_div = TX_MACRO_CLK_DIV_6; + break; + case 8: + tx_priv->dmic_clk_div = TX_MACRO_CLK_DIV_8; + break; + case 16: + tx_priv->dmic_clk_div = TX_MACRO_CLK_DIV_16; + break; + default: + /* Any other DIV factor is invalid */ + goto undefined_rate; + } + + /* Valid dmic DIV factors */ + dev_dbg(tx_priv->dev, "%s: DMIC_DIV = %u, mclk_rate = %u\n", + __func__, div_factor, mclk_rate); + + return dmic_sample_rate; + +undefined_rate: + dev_dbg(tx_priv->dev, "%s: Invalid rate %d, for mclk %d\n", + __func__, dmic_sample_rate, mclk_rate); + dmic_sample_rate = TX_MACRO_DMIC_SAMPLE_RATE_UNDEFINED; + + return dmic_sample_rate; +} + +static int tx_macro_init(struct snd_soc_codec *codec) +{ + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + int ret = 0, i = 0; + struct device *tx_dev = NULL; + struct tx_macro_priv *tx_priv = NULL; + + tx_dev = bolero_get_device_ptr(codec->dev, TX_MACRO); + if (!tx_dev) { + dev_err(codec->dev, + "%s: null device for macro!\n", __func__); + return -EINVAL; + } + tx_priv = dev_get_drvdata(tx_dev); + if (!tx_priv) { + dev_err(codec->dev, + "%s: priv is null for macro!\n", __func__); + return -EINVAL; + } + ret = snd_soc_dapm_new_controls(dapm, tx_macro_dapm_widgets, + ARRAY_SIZE(tx_macro_dapm_widgets)); + if (ret < 0) { + dev_err(tx_dev, "%s: Failed to add controls\n", __func__); + return ret; + } + + ret = snd_soc_dapm_add_routes(dapm, tx_audio_map, + ARRAY_SIZE(tx_audio_map)); + if (ret < 0) { + dev_err(tx_dev, "%s: Failed to add routes\n", __func__); + return ret; + } + + ret = snd_soc_dapm_new_widgets(dapm->card); + if (ret < 0) { + dev_err(tx_dev, "%s: Failed to add widgets\n", __func__); + return ret; + } + + ret = snd_soc_add_codec_controls(codec, tx_macro_snd_controls, + ARRAY_SIZE(tx_macro_snd_controls)); + if (ret < 0) { + dev_err(tx_dev, "%s: Failed to add snd_ctls\n", __func__); + return ret; + } + + snd_soc_dapm_ignore_suspend(dapm, "TX_AIF1 Capture"); + snd_soc_dapm_ignore_suspend(dapm, "TX_AIF2 Capture"); + snd_soc_dapm_ignore_suspend(dapm, "TX SWR_ADC0"); + snd_soc_dapm_ignore_suspend(dapm, "TX SWR_ADC1"); + snd_soc_dapm_ignore_suspend(dapm, "TX SWR_ADC2"); + snd_soc_dapm_ignore_suspend(dapm, "TX SWR_ADC3"); + snd_soc_dapm_ignore_suspend(dapm, "TX SWR_DMIC0"); + snd_soc_dapm_ignore_suspend(dapm, "TX SWR_DMIC1"); + snd_soc_dapm_ignore_suspend(dapm, "TX SWR_DMIC2"); + snd_soc_dapm_ignore_suspend(dapm, "TX SWR_DMIC3"); + snd_soc_dapm_ignore_suspend(dapm, "TX SWR_DMIC4"); + snd_soc_dapm_ignore_suspend(dapm, "TX SWR_DMIC5"); + snd_soc_dapm_ignore_suspend(dapm, "TX SWR_DMIC6"); + snd_soc_dapm_ignore_suspend(dapm, "TX SWR_DMIC7"); + snd_soc_dapm_sync(dapm); + + for (i = 0; i < NUM_DECIMATORS; i++) { + tx_priv->tx_hpf_work[i].tx_priv = tx_priv; + tx_priv->tx_hpf_work[i].decimator = i; + INIT_DELAYED_WORK(&tx_priv->tx_hpf_work[i].dwork, + tx_macro_tx_hpf_corner_freq_callback); + } + + for (i = 0; i < NUM_DECIMATORS; i++) { + tx_priv->tx_mute_dwork[i].tx_priv = tx_priv; + tx_priv->tx_mute_dwork[i].decimator = i; + INIT_DELAYED_WORK(&tx_priv->tx_mute_dwork[i].dwork, + tx_macro_mute_update_callback); + } + tx_priv->codec = codec; + + return 0; +} + +static int tx_macro_deinit(struct snd_soc_codec *codec) +{ + struct device *tx_dev = NULL; + struct tx_macro_priv *tx_priv = NULL; + + if (!tx_macro_get_data(codec, &tx_dev, &tx_priv, __func__)) + return -EINVAL; + + tx_priv->codec = NULL; + return 0; +} + +static void tx_macro_add_child_devices(struct work_struct *work) +{ + struct tx_macro_priv *tx_priv = NULL; + struct platform_device *pdev = NULL; + struct device_node *node = NULL; + struct tx_macro_swr_ctrl_data *swr_ctrl_data = NULL, *temp = NULL; + int ret = 0; + u16 count = 0, ctrl_num = 0; + struct tx_macro_swr_ctrl_platform_data *platdata = NULL; + char plat_dev_name[TX_MACRO_SWR_STRING_LEN] = ""; + bool tx_swr_master_node = false; + + tx_priv = container_of(work, struct tx_macro_priv, + tx_macro_add_child_devices_work); + if (!tx_priv) { + pr_err("%s: Memory for tx_priv does not exist\n", + __func__); + return; + } + + if (!tx_priv->dev) { + pr_err("%s: tx dev does not exist\n", __func__); + return; + } + + if (!tx_priv->dev->of_node) { + dev_err(tx_priv->dev, + "%s: DT node for tx_priv does not exist\n", __func__); + return; + } + + platdata = &tx_priv->swr_plat_data; + tx_priv->child_count = 0; + + for_each_available_child_of_node(tx_priv->dev->of_node, node) { + tx_swr_master_node = false; + if (strnstr(node->name, "tx_swr_master", + strlen("tx_swr_master")) != NULL) + tx_swr_master_node = true; + + if (tx_swr_master_node) + strlcpy(plat_dev_name, "tx_swr_ctrl", + (TX_MACRO_SWR_STRING_LEN - 1)); + else + strlcpy(plat_dev_name, node->name, + (TX_MACRO_SWR_STRING_LEN - 1)); + + pdev = platform_device_alloc(plat_dev_name, -1); + if (!pdev) { + dev_err(tx_priv->dev, "%s: pdev memory alloc failed\n", + __func__); + ret = -ENOMEM; + goto err; + } + pdev->dev.parent = tx_priv->dev; + pdev->dev.of_node = node; + + if (tx_swr_master_node) { + ret = platform_device_add_data(pdev, platdata, + sizeof(*platdata)); + if (ret) { + dev_err(&pdev->dev, + "%s: cannot add plat data ctrl:%d\n", + __func__, ctrl_num); + goto fail_pdev_add; + } + } + + ret = platform_device_add(pdev); + if (ret) { + dev_err(&pdev->dev, + "%s: Cannot add platform device\n", + __func__); + goto fail_pdev_add; + } + + if (tx_swr_master_node) { + temp = krealloc(swr_ctrl_data, + (ctrl_num + 1) * sizeof( + struct tx_macro_swr_ctrl_data), + GFP_KERNEL); + if (!temp) { + ret = -ENOMEM; + goto fail_pdev_add; + } + swr_ctrl_data = temp; + swr_ctrl_data[ctrl_num].tx_swr_pdev = pdev; + ctrl_num++; + dev_dbg(&pdev->dev, + "%s: Added soundwire ctrl device(s)\n", + __func__); + tx_priv->swr_ctrl_data = swr_ctrl_data; + } + if (tx_priv->child_count < TX_MACRO_CHILD_DEVICES_MAX) + tx_priv->pdev_child_devices[ + tx_priv->child_count++] = pdev; + else + goto err; + } + return; +fail_pdev_add: + for (count = 0; count < tx_priv->child_count; count++) + platform_device_put(tx_priv->pdev_child_devices[count]); +err: + return; +} + +static void tx_macro_init_ops(struct macro_ops *ops, + char __iomem *tx_io_base) +{ + memset(ops, 0, sizeof(struct macro_ops)); + ops->init = tx_macro_init; + ops->exit = tx_macro_deinit; + ops->io_base = tx_io_base; + ops->dai_ptr = tx_macro_dai; + ops->num_dais = ARRAY_SIZE(tx_macro_dai); + ops->mclk_fn = tx_macro_mclk_ctrl; + ops->event_handler = tx_macro_event_handler; + ops->reg_wake_irq = tx_macro_reg_wake_irq; +} + +static int tx_macro_probe(struct platform_device *pdev) +{ + struct macro_ops ops = {0}; + struct tx_macro_priv *tx_priv = NULL; + u32 tx_base_addr = 0, sample_rate = 0; + char __iomem *tx_io_base = NULL; + struct clk *tx_core_clk = NULL, *tx_npl_clk = NULL; + int ret = 0; + const char *dmic_sample_rate = "qcom,tx-dmic-sample-rate"; + + tx_priv = devm_kzalloc(&pdev->dev, sizeof(struct tx_macro_priv), + GFP_KERNEL); + if (!tx_priv) + return -ENOMEM; + platform_set_drvdata(pdev, tx_priv); + + tx_priv->dev = &pdev->dev; + ret = of_property_read_u32(pdev->dev.of_node, "reg", + &tx_base_addr); + if (ret) { + dev_err(&pdev->dev, "%s: could not find %s entry in dt\n", + __func__, "reg"); + return ret; + } + dev_set_drvdata(&pdev->dev, tx_priv); + tx_priv->tx_swr_gpio_p = of_parse_phandle(pdev->dev.of_node, + "qcom,tx-swr-gpios", 0); + if (!tx_priv->tx_swr_gpio_p) { + dev_err(&pdev->dev, "%s: swr_gpios handle not provided!\n", + __func__); + return -EINVAL; + } + tx_io_base = devm_ioremap(&pdev->dev, + tx_base_addr, TX_MACRO_MAX_OFFSET); + if (!tx_io_base) { + dev_err(&pdev->dev, "%s: ioremap failed\n", __func__); + return -ENOMEM; + } + tx_priv->tx_io_base = tx_io_base; + ret = of_property_read_u32(pdev->dev.of_node, dmic_sample_rate, + &sample_rate); + if (ret) { + dev_err(&pdev->dev, + "%s: could not find sample_rate entry in dt\n", + __func__); + tx_priv->dmic_clk_div = TX_MACRO_CLK_DIV_2; + } else { + if (tx_macro_validate_dmic_sample_rate( + sample_rate, tx_priv) == TX_MACRO_DMIC_SAMPLE_RATE_UNDEFINED) + return -EINVAL; + } + tx_priv->reset_swr = true; + INIT_WORK(&tx_priv->tx_macro_add_child_devices_work, + tx_macro_add_child_devices); + tx_priv->swr_plat_data.handle = (void *) tx_priv; + tx_priv->swr_plat_data.read = NULL; + tx_priv->swr_plat_data.write = NULL; + tx_priv->swr_plat_data.bulk_write = NULL; + tx_priv->swr_plat_data.clk = tx_macro_swrm_clock; + tx_priv->swr_plat_data.handle_irq = NULL; + /* Register MCLK for tx macro */ + tx_core_clk = devm_clk_get(&pdev->dev, "tx_core_clk"); + if (IS_ERR(tx_core_clk)) { + ret = PTR_ERR(tx_core_clk); + dev_err(&pdev->dev, "%s: clk get %s failed %d\n", + __func__, "tx_core_clk", ret); + return ret; + } + tx_priv->tx_core_clk = tx_core_clk; + /* Register npl clk for soundwire */ + tx_npl_clk = devm_clk_get(&pdev->dev, "tx_npl_clk"); + if (IS_ERR(tx_npl_clk)) { + ret = PTR_ERR(tx_npl_clk); + dev_err(&pdev->dev, "%s: clk get %s failed %d\n", + __func__, "tx_npl_clk", ret); + return ret; + } + tx_priv->tx_npl_clk = tx_npl_clk; + + mutex_init(&tx_priv->mclk_lock); + mutex_init(&tx_priv->swr_clk_lock); + tx_macro_init_ops(&ops, tx_io_base); + ret = bolero_register_macro(&pdev->dev, TX_MACRO, &ops); + if (ret) { + dev_err(&pdev->dev, + "%s: register macro failed\n", __func__); + goto err_reg_macro; + } + schedule_work(&tx_priv->tx_macro_add_child_devices_work); + return 0; +err_reg_macro: + mutex_destroy(&tx_priv->mclk_lock); + mutex_destroy(&tx_priv->swr_clk_lock); + return ret; +} + +static int tx_macro_remove(struct platform_device *pdev) +{ + struct tx_macro_priv *tx_priv = NULL; + u16 count = 0; + + tx_priv = platform_get_drvdata(pdev); + + if (!tx_priv) + return -EINVAL; + + kfree(tx_priv->swr_ctrl_data); + for (count = 0; count < tx_priv->child_count && + count < TX_MACRO_CHILD_DEVICES_MAX; count++) + platform_device_unregister(tx_priv->pdev_child_devices[count]); + + mutex_destroy(&tx_priv->mclk_lock); + mutex_destroy(&tx_priv->swr_clk_lock); + bolero_unregister_macro(&pdev->dev, TX_MACRO); + return 0; +} + + +static const struct of_device_id tx_macro_dt_match[] = { + {.compatible = "qcom,tx-macro"}, + {} +}; + +static struct platform_driver tx_macro_driver = { + .driver = { + .name = "tx_macro", + .owner = THIS_MODULE, + .of_match_table = tx_macro_dt_match, + }, + .probe = tx_macro_probe, + .remove = tx_macro_remove, +}; + +module_platform_driver(tx_macro_driver); + +MODULE_DESCRIPTION("TX macro driver"); +MODULE_LICENSE("GPL v2"); diff --git a/audio-kernel/asoc/codecs/bolero/va-macro.c b/audio-kernel/asoc/codecs/bolero/va-macro.c new file mode 100644 index 000000000..9e709e838 --- /dev/null +++ b/audio-kernel/asoc/codecs/bolero/va-macro.c @@ -0,0 +1,1669 @@ +/* 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "bolero-cdc.h" +#include "bolero-cdc-registers.h" + +#define VA_MACRO_MAX_OFFSET 0x1000 + +#define VA_MACRO_NUM_DECIMATORS 8 + +#define VA_MACRO_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\ + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |\ + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000) +#define VA_MACRO_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ + SNDRV_PCM_FMTBIT_S24_LE |\ + SNDRV_PCM_FMTBIT_S24_3LE) + +#define TX_HPF_CUT_OFF_FREQ_MASK 0x60 +#define CF_MIN_3DB_4HZ 0x0 +#define CF_MIN_3DB_75HZ 0x1 +#define CF_MIN_3DB_150HZ 0x2 + +#define VA_MACRO_DMIC_SAMPLE_RATE_UNDEFINED 0 +#define VA_MACRO_MCLK_FREQ 9600000 +#define VA_MACRO_TX_PATH_OFFSET 0x80 +#define VA_MACRO_TX_DMIC_CLK_DIV_MASK 0x0E +#define VA_MACRO_TX_DMIC_CLK_DIV_SHFT 0x01 + +#define BOLERO_CDC_VA_TX_UNMUTE_DELAY_MS 40 +#define MAX_RETRY_ATTEMPTS 500 + +static const DECLARE_TLV_DB_SCALE(digital_gain, 0, 1, 0); +static int va_tx_unmute_delay = BOLERO_CDC_VA_TX_UNMUTE_DELAY_MS; +module_param(va_tx_unmute_delay, int, 0664); +MODULE_PARM_DESC(va_tx_unmute_delay, "delay to unmute the tx path"); + +enum { + VA_MACRO_AIF_INVALID = 0, + VA_MACRO_AIF1_CAP, + VA_MACRO_AIF2_CAP, + VA_MACRO_MAX_DAIS, +}; + +enum { + VA_MACRO_DEC0, + VA_MACRO_DEC1, + VA_MACRO_DEC2, + VA_MACRO_DEC3, + VA_MACRO_DEC4, + VA_MACRO_DEC5, + VA_MACRO_DEC6, + VA_MACRO_DEC7, + VA_MACRO_DEC_MAX, +}; + +enum { + VA_MACRO_CLK_DIV_2, + VA_MACRO_CLK_DIV_3, + VA_MACRO_CLK_DIV_4, + VA_MACRO_CLK_DIV_6, + VA_MACRO_CLK_DIV_8, + VA_MACRO_CLK_DIV_16, +}; + +struct va_mute_work { + struct va_macro_priv *va_priv; + u32 decimator; + struct delayed_work dwork; +}; + +struct hpf_work { + struct va_macro_priv *va_priv; + u8 decimator; + u8 hpf_cut_off_freq; + struct delayed_work dwork; +}; + +struct va_macro_priv { + struct device *dev; + bool dec_active[VA_MACRO_NUM_DECIMATORS]; + bool va_without_decimation; + struct clk *va_core_clk; + struct mutex mclk_lock; + struct snd_soc_codec *codec; + struct hpf_work va_hpf_work[VA_MACRO_NUM_DECIMATORS]; + struct va_mute_work va_mute_dwork[VA_MACRO_NUM_DECIMATORS]; + unsigned long active_ch_mask[VA_MACRO_MAX_DAIS]; + unsigned long active_ch_cnt[VA_MACRO_MAX_DAIS]; + s32 dmic_0_1_clk_cnt; + s32 dmic_2_3_clk_cnt; + s32 dmic_4_5_clk_cnt; + s32 dmic_6_7_clk_cnt; + u16 dmic_clk_div; + u16 va_mclk_users; + char __iomem *va_io_base; + struct regulator *micb_supply; + u32 micb_voltage; + u32 micb_current; + int micb_users; +}; + +static bool va_macro_get_data(struct snd_soc_codec *codec, + struct device **va_dev, + struct va_macro_priv **va_priv, + const char *func_name) +{ + *va_dev = bolero_get_device_ptr(codec->dev, VA_MACRO); + if (!(*va_dev)) { + dev_err(codec->dev, + "%s: null device for macro!\n", func_name); + return false; + } + *va_priv = dev_get_drvdata((*va_dev)); + if (!(*va_priv) || !(*va_priv)->codec) { + dev_err(codec->dev, + "%s: priv is null for macro!\n", func_name); + return false; + } + return true; +} + +static int va_macro_mclk_enable(struct va_macro_priv *va_priv, + bool mclk_enable, bool dapm) +{ + struct regmap *regmap = dev_get_regmap(va_priv->dev->parent, NULL); + int ret = 0; + + if (regmap == NULL) { + dev_err(va_priv->dev, "%s: regmap is NULL\n", __func__); + return -EINVAL; + } + + dev_dbg(va_priv->dev, "%s: mclk_enable = %u, dapm = %d clk_users= %d\n", + __func__, mclk_enable, dapm, va_priv->va_mclk_users); + + mutex_lock(&va_priv->mclk_lock); + if (mclk_enable) { + if (va_priv->va_mclk_users == 0) { + ret = bolero_request_clock(va_priv->dev, + VA_MACRO, MCLK_MUX0, true); + if (ret < 0) { + dev_err(va_priv->dev, + "%s: va request clock en failed\n", + __func__); + goto exit; + } + regcache_mark_dirty(regmap); + regcache_sync_region(regmap, + VA_START_OFFSET, + VA_MAX_OFFSET); + regmap_update_bits(regmap, + BOLERO_CDC_VA_CLK_RST_CTRL_MCLK_CONTROL, + 0x01, 0x01); + regmap_update_bits(regmap, + BOLERO_CDC_VA_CLK_RST_CTRL_FS_CNT_CONTROL, + 0x01, 0x01); + regmap_update_bits(regmap, + BOLERO_CDC_VA_TOP_CSR_TOP_CFG0, + 0x02, 0x02); + } + va_priv->va_mclk_users++; + } else { + if (va_priv->va_mclk_users <= 0) { + dev_err(va_priv->dev, "%s: clock already disabled\n", + __func__); + va_priv->va_mclk_users = 0; + goto exit; + } + va_priv->va_mclk_users--; + if (va_priv->va_mclk_users == 0) { + regmap_update_bits(regmap, + BOLERO_CDC_VA_TOP_CSR_TOP_CFG0, + 0x02, 0x00); + regmap_update_bits(regmap, + BOLERO_CDC_VA_CLK_RST_CTRL_FS_CNT_CONTROL, + 0x01, 0x00); + regmap_update_bits(regmap, + BOLERO_CDC_VA_CLK_RST_CTRL_MCLK_CONTROL, + 0x01, 0x00); + bolero_request_clock(va_priv->dev, + VA_MACRO, MCLK_MUX0, false); + } + } +exit: + mutex_unlock(&va_priv->mclk_lock); + return ret; +} + +static int va_macro_event_handler(struct snd_soc_codec *codec, u16 event, + u32 data) +{ + struct device *va_dev = NULL; + struct va_macro_priv *va_priv = NULL; + int retry_cnt = MAX_RETRY_ATTEMPTS; + + if (!va_macro_get_data(codec, &va_dev, &va_priv, __func__)) + return -EINVAL; + + switch (event) { + case BOLERO_MACRO_EVT_WAIT_VA_CLK_RESET: + while ((va_priv->va_mclk_users != 0) && (retry_cnt != 0)) { + dev_dbg_ratelimited(va_dev, "%s:retry_cnt: %d\n", + __func__, retry_cnt); + /* + * Userspace takes 10 seconds to close + * the session when pcm_start fails due to concurrency + * with PDR/SSR. Loop and check every 20ms till 10 + * seconds for va_mclk user count to get reset to 0 + * which ensures userspace teardown is done and SSR + * powerup seq can proceed. + */ + msleep(20); + retry_cnt--; + } + if (retry_cnt == 0) + dev_err(va_dev, + "%s: va_mclk_users is non-zero still, audio SSR fail!!\n", + __func__); + break; + default: + break; + } + return 0; +} + +static int va_macro_mclk_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + int ret = 0; + struct device *va_dev = NULL; + struct va_macro_priv *va_priv = NULL; + + if (!va_macro_get_data(codec, &va_dev, &va_priv, __func__)) + return -EINVAL; + + dev_dbg(va_dev, "%s: event = %d\n", __func__, event); + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + ret = va_macro_mclk_enable(va_priv, 1, true); + break; + case SND_SOC_DAPM_POST_PMD: + va_macro_mclk_enable(va_priv, 0, true); + break; + default: + dev_err(va_priv->dev, + "%s: invalid DAPM event %d\n", __func__, event); + ret = -EINVAL; + } + return ret; +} + +static int va_macro_mclk_ctrl(struct device *dev, bool enable) +{ + struct va_macro_priv *va_priv = dev_get_drvdata(dev); + int ret = 0; + + if (enable) { + ret = clk_prepare_enable(va_priv->va_core_clk); + if (ret < 0) { + dev_err(dev, "%s:va mclk enable failed\n", __func__); + goto exit; + } + } else { + clk_disable_unprepare(va_priv->va_core_clk); + } + +exit: + return ret; +} + +static void va_macro_tx_hpf_corner_freq_callback(struct work_struct *work) +{ + struct delayed_work *hpf_delayed_work; + struct hpf_work *hpf_work; + struct va_macro_priv *va_priv; + struct snd_soc_codec *codec; + u16 dec_cfg_reg, hpf_gate_reg; + u8 hpf_cut_off_freq; + + hpf_delayed_work = to_delayed_work(work); + hpf_work = container_of(hpf_delayed_work, struct hpf_work, dwork); + va_priv = hpf_work->va_priv; + codec = va_priv->codec; + hpf_cut_off_freq = hpf_work->hpf_cut_off_freq; + + dec_cfg_reg = BOLERO_CDC_VA_TX0_TX_PATH_CFG0 + + VA_MACRO_TX_PATH_OFFSET * hpf_work->decimator; + hpf_gate_reg = BOLERO_CDC_VA_TX0_TX_PATH_SEC2 + + VA_MACRO_TX_PATH_OFFSET * hpf_work->decimator; + + dev_dbg(va_priv->dev, "%s: decimator %u hpf_cut_of_freq 0x%x\n", + __func__, hpf_work->decimator, hpf_cut_off_freq); + + snd_soc_update_bits(codec, dec_cfg_reg, TX_HPF_CUT_OFF_FREQ_MASK, + hpf_cut_off_freq << 5); + snd_soc_update_bits(codec, hpf_gate_reg, 0x03, 0x02); + /* Minimum 1 clk cycle delay is required as per HW spec */ + usleep_range(1000, 1010); + snd_soc_update_bits(codec, hpf_gate_reg, 0x03, 0x01); +} + +static void va_macro_mute_update_callback(struct work_struct *work) +{ + struct va_mute_work *va_mute_dwork; + struct snd_soc_codec *codec = NULL; + struct va_macro_priv *va_priv; + struct delayed_work *delayed_work; + u16 tx_vol_ctl_reg, decimator; + + delayed_work = to_delayed_work(work); + va_mute_dwork = container_of(delayed_work, struct va_mute_work, dwork); + va_priv = va_mute_dwork->va_priv; + codec = va_priv->codec; + decimator = va_mute_dwork->decimator; + + tx_vol_ctl_reg = + BOLERO_CDC_VA_TX0_TX_PATH_CTL + + VA_MACRO_TX_PATH_OFFSET * decimator; + snd_soc_update_bits(codec, tx_vol_ctl_reg, 0x10, 0x00); + dev_dbg(va_priv->dev, "%s: decimator %u unmute\n", + __func__, decimator); +} + +static int va_macro_put_dec_enum(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int val; + u16 mic_sel_reg; + + val = ucontrol->value.enumerated.item[0]; + if (val > e->items - 1) + return -EINVAL; + + dev_dbg(codec->dev, "%s: wname: %s, val: 0x%x\n", __func__, + widget->name, val); + + switch (e->reg) { + case BOLERO_CDC_VA_INP_MUX_ADC_MUX0_CFG0: + mic_sel_reg = BOLERO_CDC_VA_TX0_TX_PATH_CFG0; + break; + case BOLERO_CDC_VA_INP_MUX_ADC_MUX1_CFG0: + mic_sel_reg = BOLERO_CDC_VA_TX1_TX_PATH_CFG0; + break; + case BOLERO_CDC_VA_INP_MUX_ADC_MUX2_CFG0: + mic_sel_reg = BOLERO_CDC_VA_TX2_TX_PATH_CFG0; + break; + case BOLERO_CDC_VA_INP_MUX_ADC_MUX3_CFG0: + mic_sel_reg = BOLERO_CDC_VA_TX3_TX_PATH_CFG0; + break; + case BOLERO_CDC_VA_INP_MUX_ADC_MUX4_CFG0: + mic_sel_reg = BOLERO_CDC_VA_TX4_TX_PATH_CFG0; + break; + case BOLERO_CDC_VA_INP_MUX_ADC_MUX5_CFG0: + mic_sel_reg = BOLERO_CDC_VA_TX5_TX_PATH_CFG0; + break; + case BOLERO_CDC_VA_INP_MUX_ADC_MUX6_CFG0: + mic_sel_reg = BOLERO_CDC_VA_TX6_TX_PATH_CFG0; + break; + case BOLERO_CDC_VA_INP_MUX_ADC_MUX7_CFG0: + mic_sel_reg = BOLERO_CDC_VA_TX7_TX_PATH_CFG0; + break; + default: + dev_err(codec->dev, "%s: e->reg: 0x%x not expected\n", + __func__, e->reg); + return -EINVAL; + } + /* DMIC selected */ + if (val != 0) + snd_soc_update_bits(codec, mic_sel_reg, 1 << 7, 1 << 7); + + return snd_soc_dapm_put_enum_double(kcontrol, ucontrol); +} + +static int va_macro_tx_mixer_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm); + struct soc_multi_mixer_control *mixer = + ((struct soc_multi_mixer_control *)kcontrol->private_value); + u32 dai_id = widget->shift; + u32 dec_id = mixer->shift; + struct device *va_dev = NULL; + struct va_macro_priv *va_priv = NULL; + + if (!va_macro_get_data(codec, &va_dev, &va_priv, __func__)) + return -EINVAL; + + if (test_bit(dec_id, &va_priv->active_ch_mask[dai_id])) + ucontrol->value.integer.value[0] = 1; + else + ucontrol->value.integer.value[0] = 0; + return 0; +} + +static int va_macro_tx_mixer_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm); + struct snd_soc_dapm_update *update = NULL; + struct soc_multi_mixer_control *mixer = + ((struct soc_multi_mixer_control *)kcontrol->private_value); + u32 dai_id = widget->shift; + u32 dec_id = mixer->shift; + u32 enable = ucontrol->value.integer.value[0]; + struct device *va_dev = NULL; + struct va_macro_priv *va_priv = NULL; + + if (!va_macro_get_data(codec, &va_dev, &va_priv, __func__)) + return -EINVAL; + + if (enable) { + set_bit(dec_id, &va_priv->active_ch_mask[dai_id]); + va_priv->active_ch_cnt[dai_id]++; + } else { + clear_bit(dec_id, &va_priv->active_ch_mask[dai_id]); + va_priv->active_ch_cnt[dai_id]--; + } + + snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, enable, update); + + return 0; +} + +static int va_macro_enable_dmic(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + u8 dmic_clk_en = 0x01; + u16 dmic_clk_reg; + s32 *dmic_clk_cnt; + unsigned int dmic; + int ret; + char *wname; + struct device *va_dev = NULL; + struct va_macro_priv *va_priv = NULL; + + if (!va_macro_get_data(codec, &va_dev, &va_priv, __func__)) + return -EINVAL; + + wname = strpbrk(w->name, "01234567"); + if (!wname) { + dev_err(va_dev, "%s: widget not found\n", __func__); + return -EINVAL; + } + + ret = kstrtouint(wname, 10, &dmic); + if (ret < 0) { + dev_err(va_dev, "%s: Invalid DMIC line on the codec\n", + __func__); + return -EINVAL; + } + + switch (dmic) { + case 0: + case 1: + dmic_clk_cnt = &(va_priv->dmic_0_1_clk_cnt); + dmic_clk_reg = BOLERO_CDC_VA_TOP_CSR_DMIC0_CTL; + break; + case 2: + case 3: + dmic_clk_cnt = &(va_priv->dmic_2_3_clk_cnt); + dmic_clk_reg = BOLERO_CDC_VA_TOP_CSR_DMIC1_CTL; + break; + case 4: + case 5: + dmic_clk_cnt = &(va_priv->dmic_4_5_clk_cnt); + dmic_clk_reg = BOLERO_CDC_VA_TOP_CSR_DMIC2_CTL; + break; + case 6: + case 7: + dmic_clk_cnt = &(va_priv->dmic_6_7_clk_cnt); + dmic_clk_reg = BOLERO_CDC_VA_TOP_CSR_DMIC3_CTL; + break; + default: + dev_err(va_dev, "%s: Invalid DMIC Selection\n", + __func__); + return -EINVAL; + } + dev_dbg(va_dev, "%s: event %d DMIC%d dmic_clk_cnt %d\n", + __func__, event, dmic, *dmic_clk_cnt); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + (*dmic_clk_cnt)++; + if (*dmic_clk_cnt == 1) { + snd_soc_update_bits(codec, + BOLERO_CDC_VA_TOP_CSR_DMIC_CFG, + 0x80, 0x00); + snd_soc_update_bits(codec, dmic_clk_reg, + VA_MACRO_TX_DMIC_CLK_DIV_MASK, + va_priv->dmic_clk_div << + VA_MACRO_TX_DMIC_CLK_DIV_SHFT); + snd_soc_update_bits(codec, dmic_clk_reg, + dmic_clk_en, dmic_clk_en); + } + break; + case SND_SOC_DAPM_POST_PMD: + (*dmic_clk_cnt)--; + if (*dmic_clk_cnt == 0) { + snd_soc_update_bits(codec, dmic_clk_reg, + dmic_clk_en, 0); + } + break; + } + + return 0; +} + +static int va_macro_enable_dec(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + unsigned int decimator; + u16 tx_vol_ctl_reg, dec_cfg_reg, hpf_gate_reg; + u16 tx_gain_ctl_reg; + u8 hpf_cut_off_freq; + struct device *va_dev = NULL; + struct va_macro_priv *va_priv = NULL; + + if (!va_macro_get_data(codec, &va_dev, &va_priv, __func__)) + return -EINVAL; + + decimator = w->shift; + + dev_dbg(va_dev, "%s(): widget = %s decimator = %u\n", __func__, + w->name, decimator); + + tx_vol_ctl_reg = BOLERO_CDC_VA_TX0_TX_PATH_CTL + + VA_MACRO_TX_PATH_OFFSET * decimator; + hpf_gate_reg = BOLERO_CDC_VA_TX0_TX_PATH_SEC2 + + VA_MACRO_TX_PATH_OFFSET * decimator; + dec_cfg_reg = BOLERO_CDC_VA_TX0_TX_PATH_CFG0 + + VA_MACRO_TX_PATH_OFFSET * decimator; + tx_gain_ctl_reg = BOLERO_CDC_VA_TX0_TX_VOL_CTL + + VA_MACRO_TX_PATH_OFFSET * decimator; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* Enable TX PGA Mute */ + snd_soc_update_bits(codec, tx_vol_ctl_reg, 0x10, 0x10); + break; + case SND_SOC_DAPM_POST_PMU: + /* Enable TX CLK */ + snd_soc_update_bits(codec, tx_vol_ctl_reg, 0x20, 0x20); + snd_soc_update_bits(codec, hpf_gate_reg, 0x01, 0x00); + + hpf_cut_off_freq = (snd_soc_read(codec, dec_cfg_reg) & + TX_HPF_CUT_OFF_FREQ_MASK) >> 5; + va_priv->va_hpf_work[decimator].hpf_cut_off_freq = + hpf_cut_off_freq; + + if (hpf_cut_off_freq != CF_MIN_3DB_150HZ) { + snd_soc_update_bits(codec, dec_cfg_reg, + TX_HPF_CUT_OFF_FREQ_MASK, + CF_MIN_3DB_150HZ << 5); + snd_soc_update_bits(codec, hpf_gate_reg, 0x02, 0x02); + /* + * Minimum 1 clk cycle delay is required as per HW spec + */ + usleep_range(1000, 1010); + snd_soc_update_bits(codec, hpf_gate_reg, 0x02, 0x00); + } + /* schedule work queue to Remove Mute */ + schedule_delayed_work(&va_priv->va_mute_dwork[decimator].dwork, + msecs_to_jiffies(va_tx_unmute_delay)); + if (va_priv->va_hpf_work[decimator].hpf_cut_off_freq != + CF_MIN_3DB_150HZ) + schedule_delayed_work( + &va_priv->va_hpf_work[decimator].dwork, + msecs_to_jiffies(300)); + /* apply gain after decimator is enabled */ + snd_soc_write(codec, tx_gain_ctl_reg, + snd_soc_read(codec, tx_gain_ctl_reg)); + break; + case SND_SOC_DAPM_PRE_PMD: + hpf_cut_off_freq = + va_priv->va_hpf_work[decimator].hpf_cut_off_freq; + snd_soc_update_bits(codec, tx_vol_ctl_reg, 0x10, 0x10); + if (cancel_delayed_work_sync( + &va_priv->va_hpf_work[decimator].dwork)) { + if (hpf_cut_off_freq != CF_MIN_3DB_150HZ) { + snd_soc_update_bits(codec, dec_cfg_reg, + TX_HPF_CUT_OFF_FREQ_MASK, + hpf_cut_off_freq << 5); + snd_soc_update_bits(codec, hpf_gate_reg, + 0x02, 0x02); + /* + * Minimum 1 clk cycle delay is required + * as per HW spec + */ + usleep_range(1000, 1010); + snd_soc_update_bits(codec, hpf_gate_reg, + 0x02, 0x00); + } + } + cancel_delayed_work_sync( + &va_priv->va_mute_dwork[decimator].dwork); + break; + case SND_SOC_DAPM_POST_PMD: + /* Disable TX CLK */ + snd_soc_update_bits(codec, tx_vol_ctl_reg, 0x20, 0x00); + snd_soc_update_bits(codec, tx_vol_ctl_reg, 0x10, 0x00); + break; + } + return 0; +} + +static int va_macro_enable_micbias(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct device *va_dev = NULL; + struct va_macro_priv *va_priv = NULL; + int ret = 0; + + if (!va_macro_get_data(codec, &va_dev, &va_priv, __func__)) + return -EINVAL; + + if (!va_priv->micb_supply) { + dev_err(va_dev, + "%s:regulator not provided in dtsi\n", __func__); + return -EINVAL; + } + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (va_priv->micb_users++ > 0) + return 0; + ret = regulator_set_voltage(va_priv->micb_supply, + va_priv->micb_voltage, + va_priv->micb_voltage); + if (ret) { + dev_err(va_dev, "%s: Setting voltage failed, err = %d\n", + __func__, ret); + return ret; + } + ret = regulator_set_load(va_priv->micb_supply, + va_priv->micb_current); + if (ret) { + dev_err(va_dev, "%s: Setting current failed, err = %d\n", + __func__, ret); + return ret; + } + ret = regulator_enable(va_priv->micb_supply); + if (ret) { + dev_err(va_dev, "%s: regulator enable failed, err = %d\n", + __func__, ret); + return ret; + } + break; + case SND_SOC_DAPM_POST_PMD: + if (--va_priv->micb_users > 0) + return 0; + if (va_priv->micb_users < 0) { + va_priv->micb_users = 0; + dev_dbg(va_dev, "%s: regulator already disabled\n", + __func__); + return 0; + } + ret = regulator_disable(va_priv->micb_supply); + if (ret) { + dev_err(va_dev, "%s: regulator disable failed, err = %d\n", + __func__, ret); + return ret; + } + regulator_set_voltage(va_priv->micb_supply, 0, + va_priv->micb_voltage); + regulator_set_load(va_priv->micb_supply, 0); + break; + } + return 0; +} + +static int va_macro_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + int tx_fs_rate = -EINVAL; + struct snd_soc_codec *codec = dai->codec; + u32 decimator, sample_rate; + u16 tx_fs_reg = 0; + struct device *va_dev = NULL; + struct va_macro_priv *va_priv = NULL; + + if (!va_macro_get_data(codec, &va_dev, &va_priv, __func__)) + return -EINVAL; + + dev_dbg(va_dev, + "%s: dai_name = %s DAI-ID %x rate %d num_ch %d\n", __func__, + dai->name, dai->id, params_rate(params), + params_channels(params)); + + sample_rate = params_rate(params); + switch (sample_rate) { + case 8000: + tx_fs_rate = 0; + break; + case 16000: + tx_fs_rate = 1; + break; + case 32000: + tx_fs_rate = 3; + break; + case 48000: + tx_fs_rate = 4; + break; + case 96000: + tx_fs_rate = 5; + break; + case 192000: + tx_fs_rate = 6; + break; + case 384000: + tx_fs_rate = 7; + break; + default: + dev_err(va_dev, "%s: Invalid TX sample rate: %d\n", + __func__, params_rate(params)); + return -EINVAL; + } + for_each_set_bit(decimator, &va_priv->active_ch_mask[dai->id], + VA_MACRO_DEC_MAX) { + if (decimator >= 0) { + tx_fs_reg = BOLERO_CDC_VA_TX0_TX_PATH_CTL + + VA_MACRO_TX_PATH_OFFSET * decimator; + dev_dbg(va_dev, "%s: set DEC%u rate to %u\n", + __func__, decimator, sample_rate); + snd_soc_update_bits(codec, tx_fs_reg, 0x0F, + tx_fs_rate); + } else { + dev_err(va_dev, + "%s: ERROR: Invalid decimator: %d\n", + __func__, decimator); + return -EINVAL; + } + } + return 0; +} + +static int va_macro_get_channel_map(struct snd_soc_dai *dai, + unsigned int *tx_num, unsigned int *tx_slot, + unsigned int *rx_num, unsigned int *rx_slot) +{ + struct snd_soc_codec *codec = dai->codec; + struct device *va_dev = NULL; + struct va_macro_priv *va_priv = NULL; + + if (!va_macro_get_data(codec, &va_dev, &va_priv, __func__)) + return -EINVAL; + + switch (dai->id) { + case VA_MACRO_AIF1_CAP: + case VA_MACRO_AIF2_CAP: + *tx_slot = va_priv->active_ch_mask[dai->id]; + *tx_num = va_priv->active_ch_cnt[dai->id]; + break; + default: + dev_err(va_dev, "%s: Invalid AIF\n", __func__); + break; + } + return 0; +} + +static struct snd_soc_dai_ops va_macro_dai_ops = { + .hw_params = va_macro_hw_params, + .get_channel_map = va_macro_get_channel_map, +}; + +static struct snd_soc_dai_driver va_macro_dai[] = { + { + .name = "va_macro_tx1", + .id = VA_MACRO_AIF1_CAP, + .capture = { + .stream_name = "VA_AIF1 Capture", + .rates = VA_MACRO_RATES, + .formats = VA_MACRO_FORMATS, + .rate_max = 192000, + .rate_min = 8000, + .channels_min = 1, + .channels_max = 8, + }, + .ops = &va_macro_dai_ops, + }, + { + .name = "va_macro_tx2", + .id = VA_MACRO_AIF2_CAP, + .capture = { + .stream_name = "VA_AIF2 Capture", + .rates = VA_MACRO_RATES, + .formats = VA_MACRO_FORMATS, + .rate_max = 192000, + .rate_min = 8000, + .channels_min = 1, + .channels_max = 8, + }, + .ops = &va_macro_dai_ops, + }, +}; + +#define STRING(name) #name +#define VA_MACRO_DAPM_ENUM(name, reg, offset, text) \ +static SOC_ENUM_SINGLE_DECL(name##_enum, reg, offset, text); \ +static const struct snd_kcontrol_new name##_mux = \ + SOC_DAPM_ENUM(STRING(name), name##_enum) + +#define VA_MACRO_DAPM_ENUM_EXT(name, reg, offset, text, getname, putname) \ +static SOC_ENUM_SINGLE_DECL(name##_enum, reg, offset, text); \ +static const struct snd_kcontrol_new name##_mux = \ + SOC_DAPM_ENUM_EXT(STRING(name), name##_enum, getname, putname) + +#define VA_MACRO_DAPM_MUX(name, shift, kctl) \ + SND_SOC_DAPM_MUX(name, SND_SOC_NOPM, shift, 0, &kctl##_mux) + +static const char * const adc_mux_text[] = { + "MSM_DMIC", "SWR_MIC" +}; + +VA_MACRO_DAPM_ENUM(va_dec0, BOLERO_CDC_VA_INP_MUX_ADC_MUX0_CFG1, + 0, adc_mux_text); +VA_MACRO_DAPM_ENUM(va_dec1, BOLERO_CDC_VA_INP_MUX_ADC_MUX1_CFG1, + 0, adc_mux_text); +VA_MACRO_DAPM_ENUM(va_dec2, BOLERO_CDC_VA_INP_MUX_ADC_MUX2_CFG1, + 0, adc_mux_text); +VA_MACRO_DAPM_ENUM(va_dec3, BOLERO_CDC_VA_INP_MUX_ADC_MUX3_CFG1, + 0, adc_mux_text); +VA_MACRO_DAPM_ENUM(va_dec4, BOLERO_CDC_VA_INP_MUX_ADC_MUX4_CFG1, + 0, adc_mux_text); +VA_MACRO_DAPM_ENUM(va_dec5, BOLERO_CDC_VA_INP_MUX_ADC_MUX5_CFG1, + 0, adc_mux_text); +VA_MACRO_DAPM_ENUM(va_dec6, BOLERO_CDC_VA_INP_MUX_ADC_MUX6_CFG1, + 0, adc_mux_text); +VA_MACRO_DAPM_ENUM(va_dec7, BOLERO_CDC_VA_INP_MUX_ADC_MUX7_CFG1, + 0, adc_mux_text); + +static const char * const dmic_mux_text[] = { + "ZERO", "DMIC0", "DMIC1", "DMIC2", "DMIC3", + "DMIC4", "DMIC5", "DMIC6", "DMIC7" +}; + +VA_MACRO_DAPM_ENUM_EXT(va_dmic0, BOLERO_CDC_VA_INP_MUX_ADC_MUX0_CFG0, + 4, dmic_mux_text, snd_soc_dapm_get_enum_double, + va_macro_put_dec_enum); + +VA_MACRO_DAPM_ENUM_EXT(va_dmic1, BOLERO_CDC_VA_INP_MUX_ADC_MUX1_CFG0, + 4, dmic_mux_text, snd_soc_dapm_get_enum_double, + va_macro_put_dec_enum); + +VA_MACRO_DAPM_ENUM_EXT(va_dmic2, BOLERO_CDC_VA_INP_MUX_ADC_MUX2_CFG0, + 4, dmic_mux_text, snd_soc_dapm_get_enum_double, + va_macro_put_dec_enum); + +VA_MACRO_DAPM_ENUM_EXT(va_dmic3, BOLERO_CDC_VA_INP_MUX_ADC_MUX3_CFG0, + 4, dmic_mux_text, snd_soc_dapm_get_enum_double, + va_macro_put_dec_enum); + +VA_MACRO_DAPM_ENUM_EXT(va_dmic4, BOLERO_CDC_VA_INP_MUX_ADC_MUX4_CFG0, + 4, dmic_mux_text, snd_soc_dapm_get_enum_double, + va_macro_put_dec_enum); + +VA_MACRO_DAPM_ENUM_EXT(va_dmic5, BOLERO_CDC_VA_INP_MUX_ADC_MUX5_CFG0, + 4, dmic_mux_text, snd_soc_dapm_get_enum_double, + va_macro_put_dec_enum); + +VA_MACRO_DAPM_ENUM_EXT(va_dmic6, BOLERO_CDC_VA_INP_MUX_ADC_MUX6_CFG0, + 4, dmic_mux_text, snd_soc_dapm_get_enum_double, + va_macro_put_dec_enum); + +VA_MACRO_DAPM_ENUM_EXT(va_dmic7, BOLERO_CDC_VA_INP_MUX_ADC_MUX7_CFG0, + 4, dmic_mux_text, snd_soc_dapm_get_enum_double, + va_macro_put_dec_enum); + +static const char * const smic_mux_text[] = { + "ZERO", "ADC0", "ADC1", "ADC2", "ADC3", + "SWR_DMIC0", "SWR_DMIC1", "SWR_DMIC2", "SWR_DMIC3", + "SWR_DMIC4", "SWR_DMIC5", "SWR_DMIC6", "SWR_DMIC7" +}; + +VA_MACRO_DAPM_ENUM_EXT(va_smic0, BOLERO_CDC_VA_INP_MUX_ADC_MUX0_CFG0, + 0, smic_mux_text, snd_soc_dapm_get_enum_double, + va_macro_put_dec_enum); + +VA_MACRO_DAPM_ENUM_EXT(va_smic1, BOLERO_CDC_VA_INP_MUX_ADC_MUX1_CFG0, + 0, smic_mux_text, snd_soc_dapm_get_enum_double, + va_macro_put_dec_enum); + +VA_MACRO_DAPM_ENUM_EXT(va_smic2, BOLERO_CDC_VA_INP_MUX_ADC_MUX2_CFG0, + 0, smic_mux_text, snd_soc_dapm_get_enum_double, + va_macro_put_dec_enum); + +VA_MACRO_DAPM_ENUM_EXT(va_smic3, BOLERO_CDC_VA_INP_MUX_ADC_MUX3_CFG0, + 0, smic_mux_text, snd_soc_dapm_get_enum_double, + va_macro_put_dec_enum); + +VA_MACRO_DAPM_ENUM_EXT(va_smic4, BOLERO_CDC_VA_INP_MUX_ADC_MUX4_CFG0, + 0, smic_mux_text, snd_soc_dapm_get_enum_double, + va_macro_put_dec_enum); + +VA_MACRO_DAPM_ENUM_EXT(va_smic5, BOLERO_CDC_VA_INP_MUX_ADC_MUX5_CFG0, + 0, smic_mux_text, snd_soc_dapm_get_enum_double, + va_macro_put_dec_enum); + +VA_MACRO_DAPM_ENUM_EXT(va_smic6, BOLERO_CDC_VA_INP_MUX_ADC_MUX6_CFG0, + 0, smic_mux_text, snd_soc_dapm_get_enum_double, + va_macro_put_dec_enum); + +VA_MACRO_DAPM_ENUM_EXT(va_smic7, BOLERO_CDC_VA_INP_MUX_ADC_MUX7_CFG0, + 0, smic_mux_text, snd_soc_dapm_get_enum_double, + va_macro_put_dec_enum); + +static const struct snd_kcontrol_new va_aif1_cap_mixer[] = { + SOC_SINGLE_EXT("DEC0", SND_SOC_NOPM, VA_MACRO_DEC0, 1, 0, + va_macro_tx_mixer_get, va_macro_tx_mixer_put), + SOC_SINGLE_EXT("DEC1", SND_SOC_NOPM, VA_MACRO_DEC1, 1, 0, + va_macro_tx_mixer_get, va_macro_tx_mixer_put), + SOC_SINGLE_EXT("DEC2", SND_SOC_NOPM, VA_MACRO_DEC2, 1, 0, + va_macro_tx_mixer_get, va_macro_tx_mixer_put), + SOC_SINGLE_EXT("DEC3", SND_SOC_NOPM, VA_MACRO_DEC3, 1, 0, + va_macro_tx_mixer_get, va_macro_tx_mixer_put), + SOC_SINGLE_EXT("DEC4", SND_SOC_NOPM, VA_MACRO_DEC4, 1, 0, + va_macro_tx_mixer_get, va_macro_tx_mixer_put), + SOC_SINGLE_EXT("DEC5", SND_SOC_NOPM, VA_MACRO_DEC5, 1, 0, + va_macro_tx_mixer_get, va_macro_tx_mixer_put), + SOC_SINGLE_EXT("DEC6", SND_SOC_NOPM, VA_MACRO_DEC6, 1, 0, + va_macro_tx_mixer_get, va_macro_tx_mixer_put), + SOC_SINGLE_EXT("DEC7", SND_SOC_NOPM, VA_MACRO_DEC7, 1, 0, + va_macro_tx_mixer_get, va_macro_tx_mixer_put), +}; + +static const struct snd_kcontrol_new va_aif2_cap_mixer[] = { + SOC_SINGLE_EXT("DEC0", SND_SOC_NOPM, VA_MACRO_DEC0, 1, 0, + va_macro_tx_mixer_get, va_macro_tx_mixer_put), + SOC_SINGLE_EXT("DEC1", SND_SOC_NOPM, VA_MACRO_DEC1, 1, 0, + va_macro_tx_mixer_get, va_macro_tx_mixer_put), + SOC_SINGLE_EXT("DEC2", SND_SOC_NOPM, VA_MACRO_DEC2, 1, 0, + va_macro_tx_mixer_get, va_macro_tx_mixer_put), + SOC_SINGLE_EXT("DEC3", SND_SOC_NOPM, VA_MACRO_DEC3, 1, 0, + va_macro_tx_mixer_get, va_macro_tx_mixer_put), + SOC_SINGLE_EXT("DEC4", SND_SOC_NOPM, VA_MACRO_DEC4, 1, 0, + va_macro_tx_mixer_get, va_macro_tx_mixer_put), + SOC_SINGLE_EXT("DEC5", SND_SOC_NOPM, VA_MACRO_DEC5, 1, 0, + va_macro_tx_mixer_get, va_macro_tx_mixer_put), + SOC_SINGLE_EXT("DEC6", SND_SOC_NOPM, VA_MACRO_DEC6, 1, 0, + va_macro_tx_mixer_get, va_macro_tx_mixer_put), + SOC_SINGLE_EXT("DEC7", SND_SOC_NOPM, VA_MACRO_DEC7, 1, 0, + va_macro_tx_mixer_get, va_macro_tx_mixer_put), +}; + +static const struct snd_soc_dapm_widget va_macro_dapm_widgets[] = { + SND_SOC_DAPM_AIF_OUT("VA_AIF1 CAP", "VA_AIF1 Capture", 0, + SND_SOC_NOPM, VA_MACRO_AIF1_CAP, 0), + + SND_SOC_DAPM_AIF_OUT("VA_AIF2 CAP", "VA_AIF2 Capture", 0, + SND_SOC_NOPM, VA_MACRO_AIF2_CAP, 0), + + SND_SOC_DAPM_MIXER("VA_AIF1_CAP Mixer", SND_SOC_NOPM, + VA_MACRO_AIF1_CAP, 0, + va_aif1_cap_mixer, ARRAY_SIZE(va_aif1_cap_mixer)), + + SND_SOC_DAPM_MIXER("VA_AIF2_CAP Mixer", SND_SOC_NOPM, + VA_MACRO_AIF2_CAP, 0, + va_aif2_cap_mixer, ARRAY_SIZE(va_aif2_cap_mixer)), + + + VA_MACRO_DAPM_MUX("VA DMIC MUX0", 0, va_dmic0), + VA_MACRO_DAPM_MUX("VA DMIC MUX1", 0, va_dmic1), + VA_MACRO_DAPM_MUX("VA DMIC MUX2", 0, va_dmic2), + VA_MACRO_DAPM_MUX("VA DMIC MUX3", 0, va_dmic3), + VA_MACRO_DAPM_MUX("VA DMIC MUX4", 0, va_dmic4), + VA_MACRO_DAPM_MUX("VA DMIC MUX5", 0, va_dmic5), + VA_MACRO_DAPM_MUX("VA DMIC MUX6", 0, va_dmic6), + VA_MACRO_DAPM_MUX("VA DMIC MUX7", 0, va_dmic7), + + VA_MACRO_DAPM_MUX("VA SMIC MUX0", 0, va_smic0), + VA_MACRO_DAPM_MUX("VA SMIC MUX1", 0, va_smic1), + VA_MACRO_DAPM_MUX("VA SMIC MUX2", 0, va_smic2), + VA_MACRO_DAPM_MUX("VA SMIC MUX3", 0, va_smic3), + VA_MACRO_DAPM_MUX("VA SMIC MUX4", 0, va_smic4), + VA_MACRO_DAPM_MUX("VA SMIC MUX5", 0, va_smic5), + VA_MACRO_DAPM_MUX("VA SMIC MUX6", 0, va_smic6), + VA_MACRO_DAPM_MUX("VA SMIC MUX7", 0, va_smic7), + + SND_SOC_DAPM_MICBIAS_E("VA MIC BIAS1", SND_SOC_NOPM, 0, 0, + va_macro_enable_micbias, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_ADC_E("VA DMIC0", NULL, SND_SOC_NOPM, 0, 0, + va_macro_enable_dmic, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_ADC_E("VA DMIC1", NULL, SND_SOC_NOPM, 0, 0, + va_macro_enable_dmic, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_ADC_E("VA DMIC2", NULL, SND_SOC_NOPM, 0, 0, + va_macro_enable_dmic, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_ADC_E("VA DMIC3", NULL, SND_SOC_NOPM, 0, 0, + va_macro_enable_dmic, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_ADC_E("VA DMIC4", NULL, SND_SOC_NOPM, 0, 0, + va_macro_enable_dmic, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_ADC_E("VA DMIC5", NULL, SND_SOC_NOPM, 0, 0, + va_macro_enable_dmic, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_ADC_E("VA DMIC6", NULL, SND_SOC_NOPM, 0, 0, + va_macro_enable_dmic, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_ADC_E("VA DMIC7", NULL, SND_SOC_NOPM, 0, 0, + va_macro_enable_dmic, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_INPUT("VA SWR_ADC0"), + SND_SOC_DAPM_INPUT("VA SWR_ADC1"), + SND_SOC_DAPM_INPUT("VA SWR_ADC2"), + SND_SOC_DAPM_INPUT("VA SWR_ADC3"), + SND_SOC_DAPM_INPUT("VA SWR_MIC0"), + SND_SOC_DAPM_INPUT("VA SWR_MIC1"), + SND_SOC_DAPM_INPUT("VA SWR_MIC2"), + SND_SOC_DAPM_INPUT("VA SWR_MIC3"), + SND_SOC_DAPM_INPUT("VA SWR_MIC4"), + SND_SOC_DAPM_INPUT("VA SWR_MIC5"), + SND_SOC_DAPM_INPUT("VA SWR_MIC6"), + SND_SOC_DAPM_INPUT("VA SWR_MIC7"), + + SND_SOC_DAPM_MUX_E("VA DEC0 MUX", SND_SOC_NOPM, VA_MACRO_DEC0, 0, + &va_dec0_mux, va_macro_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX_E("VA DEC1 MUX", SND_SOC_NOPM, VA_MACRO_DEC1, 0, + &va_dec1_mux, va_macro_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX_E("VA DEC2 MUX", SND_SOC_NOPM, VA_MACRO_DEC2, 0, + &va_dec2_mux, va_macro_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX_E("VA DEC3 MUX", SND_SOC_NOPM, VA_MACRO_DEC3, 0, + &va_dec3_mux, va_macro_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX_E("VA DEC4 MUX", SND_SOC_NOPM, VA_MACRO_DEC4, 0, + &va_dec4_mux, va_macro_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX_E("VA DEC5 MUX", SND_SOC_NOPM, VA_MACRO_DEC5, 0, + &va_dec5_mux, va_macro_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX_E("VA DEC6 MUX", SND_SOC_NOPM, VA_MACRO_DEC6, 0, + &va_dec6_mux, va_macro_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX_E("VA DEC7 MUX", SND_SOC_NOPM, VA_MACRO_DEC7, 0, + &va_dec7_mux, va_macro_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_SUPPLY_S("VA_MCLK", -1, SND_SOC_NOPM, 0, 0, + va_macro_mclk_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +}; + +static const struct snd_soc_dapm_widget va_macro_wod_dapm_widgets[] = { + SND_SOC_DAPM_SUPPLY_S("VA_MCLK", -1, SND_SOC_NOPM, 0, 0, + va_macro_mclk_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +}; + +static const struct snd_soc_dapm_route va_audio_map[] = { + {"VA_AIF1 CAP", NULL, "VA_MCLK"}, + {"VA_AIF2 CAP", NULL, "VA_MCLK"}, + + {"VA_AIF1 CAP", NULL, "VA_AIF1_CAP Mixer"}, + {"VA_AIF2 CAP", NULL, "VA_AIF2_CAP Mixer"}, + + {"VA_AIF1_CAP Mixer", "DEC0", "VA DEC0 MUX"}, + {"VA_AIF1_CAP Mixer", "DEC1", "VA DEC1 MUX"}, + {"VA_AIF1_CAP Mixer", "DEC2", "VA DEC2 MUX"}, + {"VA_AIF1_CAP Mixer", "DEC3", "VA DEC3 MUX"}, + {"VA_AIF1_CAP Mixer", "DEC4", "VA DEC4 MUX"}, + {"VA_AIF1_CAP Mixer", "DEC5", "VA DEC5 MUX"}, + {"VA_AIF1_CAP Mixer", "DEC6", "VA DEC6 MUX"}, + {"VA_AIF1_CAP Mixer", "DEC7", "VA DEC7 MUX"}, + + {"VA_AIF2_CAP Mixer", "DEC0", "VA DEC0 MUX"}, + {"VA_AIF2_CAP Mixer", "DEC1", "VA DEC1 MUX"}, + {"VA_AIF2_CAP Mixer", "DEC2", "VA DEC2 MUX"}, + {"VA_AIF2_CAP Mixer", "DEC3", "VA DEC3 MUX"}, + {"VA_AIF2_CAP Mixer", "DEC4", "VA DEC4 MUX"}, + {"VA_AIF2_CAP Mixer", "DEC5", "VA DEC5 MUX"}, + {"VA_AIF2_CAP Mixer", "DEC6", "VA DEC6 MUX"}, + {"VA_AIF2_CAP Mixer", "DEC7", "VA DEC7 MUX"}, + + {"VA DEC0 MUX", "MSM_DMIC", "VA DMIC MUX0"}, + {"VA DMIC MUX0", "DMIC0", "VA DMIC0"}, + {"VA DMIC MUX0", "DMIC1", "VA DMIC1"}, + {"VA DMIC MUX0", "DMIC2", "VA DMIC2"}, + {"VA DMIC MUX0", "DMIC3", "VA DMIC3"}, + {"VA DMIC MUX0", "DMIC4", "VA DMIC4"}, + {"VA DMIC MUX0", "DMIC5", "VA DMIC5"}, + {"VA DMIC MUX0", "DMIC6", "VA DMIC6"}, + {"VA DMIC MUX0", "DMIC7", "VA DMIC7"}, + + {"VA DEC0 MUX", "SWR_MIC", "VA SMIC MUX0"}, + {"VA SMIC MUX0", "ADC0", "VA SWR_ADC0"}, + {"VA SMIC MUX0", "ADC1", "VA SWR_ADC1"}, + {"VA SMIC MUX0", "ADC2", "VA SWR_ADC2"}, + {"VA SMIC MUX0", "ADC3", "VA SWR_ADC3"}, + {"VA SMIC MUX0", "SWR_DMIC0", "VA SWR_MIC0"}, + {"VA SMIC MUX0", "SWR_DMIC1", "VA SWR_MIC1"}, + {"VA SMIC MUX0", "SWR_DMIC2", "VA SWR_MIC2"}, + {"VA SMIC MUX0", "SWR_DMIC3", "VA SWR_MIC3"}, + {"VA SMIC MUX0", "SWR_DMIC4", "VA SWR_MIC4"}, + {"VA SMIC MUX0", "SWR_DMIC5", "VA SWR_MIC5"}, + {"VA SMIC MUX0", "SWR_DMIC6", "VA SWR_MIC6"}, + {"VA SMIC MUX0", "SWR_DMIC7", "VA SWR_MIC7"}, + + {"VA DEC1 MUX", "MSM_DMIC", "VA DMIC MUX1"}, + {"VA DMIC MUX1", "DMIC0", "VA DMIC0"}, + {"VA DMIC MUX1", "DMIC1", "VA DMIC1"}, + {"VA DMIC MUX1", "DMIC2", "VA DMIC2"}, + {"VA DMIC MUX1", "DMIC3", "VA DMIC3"}, + {"VA DMIC MUX1", "DMIC4", "VA DMIC4"}, + {"VA DMIC MUX1", "DMIC5", "VA DMIC5"}, + {"VA DMIC MUX1", "DMIC6", "VA DMIC6"}, + {"VA DMIC MUX1", "DMIC7", "VA DMIC7"}, + + {"VA DEC1 MUX", "SWR_MIC", "VA SMIC MUX1"}, + {"VA SMIC MUX1", "ADC0", "VA SWR_ADC0"}, + {"VA SMIC MUX1", "ADC1", "VA SWR_ADC1"}, + {"VA SMIC MUX1", "ADC2", "VA SWR_ADC2"}, + {"VA SMIC MUX1", "ADC3", "VA SWR_ADC3"}, + {"VA SMIC MUX1", "SWR_DMIC0", "VA SWR_MIC0"}, + {"VA SMIC MUX1", "SWR_DMIC1", "VA SWR_MIC1"}, + {"VA SMIC MUX1", "SWR_DMIC2", "VA SWR_MIC2"}, + {"VA SMIC MUX1", "SWR_DMIC3", "VA SWR_MIC3"}, + {"VA SMIC MUX1", "SWR_DMIC4", "VA SWR_MIC4"}, + {"VA SMIC MUX1", "SWR_DMIC5", "VA SWR_MIC5"}, + {"VA SMIC MUX1", "SWR_DMIC6", "VA SWR_MIC6"}, + {"VA SMIC MUX1", "SWR_DMIC7", "VA SWR_MIC7"}, + + {"VA DEC2 MUX", "MSM_DMIC", "VA DMIC MUX2"}, + {"VA DMIC MUX2", "DMIC0", "VA DMIC0"}, + {"VA DMIC MUX2", "DMIC1", "VA DMIC1"}, + {"VA DMIC MUX2", "DMIC2", "VA DMIC2"}, + {"VA DMIC MUX2", "DMIC3", "VA DMIC3"}, + {"VA DMIC MUX2", "DMIC4", "VA DMIC4"}, + {"VA DMIC MUX2", "DMIC5", "VA DMIC5"}, + {"VA DMIC MUX2", "DMIC6", "VA DMIC6"}, + {"VA DMIC MUX2", "DMIC7", "VA DMIC7"}, + + {"VA DEC2 MUX", "SWR_MIC", "VA SMIC MUX2"}, + {"VA SMIC MUX2", "ADC0", "VA SWR_ADC0"}, + {"VA SMIC MUX2", "ADC1", "VA SWR_ADC1"}, + {"VA SMIC MUX2", "ADC2", "VA SWR_ADC2"}, + {"VA SMIC MUX2", "ADC3", "VA SWR_ADC3"}, + {"VA SMIC MUX2", "SWR_DMIC0", "VA SWR_MIC0"}, + {"VA SMIC MUX2", "SWR_DMIC1", "VA SWR_MIC1"}, + {"VA SMIC MUX2", "SWR_DMIC2", "VA SWR_MIC2"}, + {"VA SMIC MUX2", "SWR_DMIC3", "VA SWR_MIC3"}, + {"VA SMIC MUX2", "SWR_DMIC4", "VA SWR_MIC4"}, + {"VA SMIC MUX2", "SWR_DMIC5", "VA SWR_MIC5"}, + {"VA SMIC MUX2", "SWR_DMIC6", "VA SWR_MIC6"}, + {"VA SMIC MUX2", "SWR_DMIC7", "VA SWR_MIC7"}, + + {"VA DEC3 MUX", "MSM_DMIC", "VA DMIC MUX3"}, + {"VA DMIC MUX3", "DMIC0", "VA DMIC0"}, + {"VA DMIC MUX3", "DMIC1", "VA DMIC1"}, + {"VA DMIC MUX3", "DMIC2", "VA DMIC2"}, + {"VA DMIC MUX3", "DMIC3", "VA DMIC3"}, + {"VA DMIC MUX3", "DMIC4", "VA DMIC4"}, + {"VA DMIC MUX3", "DMIC5", "VA DMIC5"}, + {"VA DMIC MUX3", "DMIC6", "VA DMIC6"}, + {"VA DMIC MUX3", "DMIC7", "VA DMIC7"}, + + {"VA DEC3 MUX", "SWR_MIC", "VA SMIC MUX3"}, + {"VA SMIC MUX3", "ADC0", "VA SWR_ADC0"}, + {"VA SMIC MUX3", "ADC1", "VA SWR_ADC1"}, + {"VA SMIC MUX3", "ADC2", "VA SWR_ADC2"}, + {"VA SMIC MUX3", "ADC3", "VA SWR_ADC3"}, + {"VA SMIC MUX3", "SWR_DMIC0", "VA SWR_MIC0"}, + {"VA SMIC MUX3", "SWR_DMIC1", "VA SWR_MIC1"}, + {"VA SMIC MUX3", "SWR_DMIC2", "VA SWR_MIC2"}, + {"VA SMIC MUX3", "SWR_DMIC3", "VA SWR_MIC3"}, + {"VA SMIC MUX3", "SWR_DMIC4", "VA SWR_MIC4"}, + {"VA SMIC MUX3", "SWR_DMIC5", "VA SWR_MIC5"}, + {"VA SMIC MUX3", "SWR_DMIC6", "VA SWR_MIC6"}, + {"VA SMIC MUX3", "SWR_DMIC7", "VA SWR_MIC7"}, + + {"VA DEC4 MUX", "MSM_DMIC", "VA DMIC MUX4"}, + {"VA DMIC MUX4", "DMIC0", "VA DMIC0"}, + {"VA DMIC MUX4", "DMIC1", "VA DMIC1"}, + {"VA DMIC MUX4", "DMIC2", "VA DMIC2"}, + {"VA DMIC MUX4", "DMIC3", "VA DMIC3"}, + {"VA DMIC MUX4", "DMIC4", "VA DMIC4"}, + {"VA DMIC MUX4", "DMIC5", "VA DMIC5"}, + {"VA DMIC MUX4", "DMIC6", "VA DMIC6"}, + {"VA DMIC MUX4", "DMIC7", "VA DMIC7"}, + + {"VA DEC4 MUX", "SWR_MIC", "VA SMIC MUX4"}, + {"VA SMIC MUX4", "ADC0", "VA SWR_ADC0"}, + {"VA SMIC MUX4", "ADC1", "VA SWR_ADC1"}, + {"VA SMIC MUX4", "ADC2", "VA SWR_ADC2"}, + {"VA SMIC MUX4", "ADC3", "VA SWR_ADC3"}, + {"VA SMIC MUX4", "SWR_DMIC0", "VA SWR_MIC0"}, + {"VA SMIC MUX4", "SWR_DMIC1", "VA SWR_MIC1"}, + {"VA SMIC MUX4", "SWR_DMIC2", "VA SWR_MIC2"}, + {"VA SMIC MUX4", "SWR_DMIC3", "VA SWR_MIC3"}, + {"VA SMIC MUX4", "SWR_DMIC4", "VA SWR_MIC4"}, + {"VA SMIC MUX4", "SWR_DMIC5", "VA SWR_MIC5"}, + {"VA SMIC MUX4", "SWR_DMIC6", "VA SWR_MIC6"}, + {"VA SMIC MUX4", "SWR_DMIC7", "VA SWR_MIC7"}, + + {"VA DEC5 MUX", "MSM_DMIC", "VA DMIC MUX5"}, + {"VA DMIC MUX5", "DMIC0", "VA DMIC0"}, + {"VA DMIC MUX5", "DMIC1", "VA DMIC1"}, + {"VA DMIC MUX5", "DMIC2", "VA DMIC2"}, + {"VA DMIC MUX5", "DMIC3", "VA DMIC3"}, + {"VA DMIC MUX5", "DMIC4", "VA DMIC4"}, + {"VA DMIC MUX5", "DMIC5", "VA DMIC5"}, + {"VA DMIC MUX5", "DMIC6", "VA DMIC6"}, + {"VA DMIC MUX5", "DMIC7", "VA DMIC7"}, + + {"VA DEC5 MUX", "SWR_MIC", "VA SMIC MUX5"}, + {"VA SMIC MUX5", "ADC0", "VA SWR_ADC0"}, + {"VA SMIC MUX5", "ADC1", "VA SWR_ADC1"}, + {"VA SMIC MUX5", "ADC2", "VA SWR_ADC2"}, + {"VA SMIC MUX5", "ADC3", "VA SWR_ADC3"}, + {"VA SMIC MUX5", "SWR_DMIC0", "VA SWR_MIC0"}, + {"VA SMIC MUX5", "SWR_DMIC1", "VA SWR_MIC1"}, + {"VA SMIC MUX5", "SWR_DMIC2", "VA SWR_MIC2"}, + {"VA SMIC MUX5", "SWR_DMIC3", "VA SWR_MIC3"}, + {"VA SMIC MUX5", "SWR_DMIC4", "VA SWR_MIC4"}, + {"VA SMIC MUX5", "SWR_DMIC5", "VA SWR_MIC5"}, + {"VA SMIC MUX5", "SWR_DMIC6", "VA SWR_MIC6"}, + {"VA SMIC MUX5", "SWR_DMIC7", "VA SWR_MIC7"}, + + {"VA DEC6 MUX", "MSM_DMIC", "VA DMIC MUX6"}, + {"VA DMIC MUX6", "DMIC0", "VA DMIC0"}, + {"VA DMIC MUX6", "DMIC1", "VA DMIC1"}, + {"VA DMIC MUX6", "DMIC2", "VA DMIC2"}, + {"VA DMIC MUX6", "DMIC3", "VA DMIC3"}, + {"VA DMIC MUX6", "DMIC4", "VA DMIC4"}, + {"VA DMIC MUX6", "DMIC5", "VA DMIC5"}, + {"VA DMIC MUX6", "DMIC6", "VA DMIC6"}, + {"VA DMIC MUX6", "DMIC7", "VA DMIC7"}, + + {"VA DEC6 MUX", "SWR_MIC", "VA SMIC MUX6"}, + {"VA SMIC MUX6", "ADC0", "VA SWR_ADC0"}, + {"VA SMIC MUX6", "ADC1", "VA SWR_ADC1"}, + {"VA SMIC MUX6", "ADC2", "VA SWR_ADC2"}, + {"VA SMIC MUX6", "ADC3", "VA SWR_ADC3"}, + {"VA SMIC MUX6", "SWR_DMIC0", "VA SWR_MIC0"}, + {"VA SMIC MUX6", "SWR_DMIC1", "VA SWR_MIC1"}, + {"VA SMIC MUX6", "SWR_DMIC2", "VA SWR_MIC2"}, + {"VA SMIC MUX6", "SWR_DMIC3", "VA SWR_MIC3"}, + {"VA SMIC MUX6", "SWR_DMIC4", "VA SWR_MIC4"}, + {"VA SMIC MUX6", "SWR_DMIC5", "VA SWR_MIC5"}, + {"VA SMIC MUX6", "SWR_DMIC6", "VA SWR_MIC6"}, + {"VA SMIC MUX6", "SWR_DMIC7", "VA SWR_MIC7"}, + + {"VA DEC7 MUX", "MSM_DMIC", "VA DMIC MUX7"}, + {"VA DMIC MUX7", "DMIC0", "VA DMIC0"}, + {"VA DMIC MUX7", "DMIC1", "VA DMIC1"}, + {"VA DMIC MUX7", "DMIC2", "VA DMIC2"}, + {"VA DMIC MUX7", "DMIC3", "VA DMIC3"}, + {"VA DMIC MUX7", "DMIC4", "VA DMIC4"}, + {"VA DMIC MUX7", "DMIC5", "VA DMIC5"}, + {"VA DMIC MUX7", "DMIC6", "VA DMIC6"}, + {"VA DMIC MUX7", "DMIC7", "VA DMIC7"}, + + {"VA DEC7 MUX", "SWR_MIC", "VA SMIC MUX7"}, + {"VA SMIC MUX7", "ADC0", "VA SWR_ADC0"}, + {"VA SMIC MUX7", "ADC1", "VA SWR_ADC1"}, + {"VA SMIC MUX7", "ADC2", "VA SWR_ADC2"}, + {"VA SMIC MUX7", "ADC3", "VA SWR_ADC3"}, + {"VA SMIC MUX7", "SWR_DMIC0", "VA SWR_MIC0"}, + {"VA SMIC MUX7", "SWR_DMIC1", "VA SWR_MIC1"}, + {"VA SMIC MUX7", "SWR_DMIC2", "VA SWR_MIC2"}, + {"VA SMIC MUX7", "SWR_DMIC3", "VA SWR_MIC3"}, + {"VA SMIC MUX7", "SWR_DMIC4", "VA SWR_MIC4"}, + {"VA SMIC MUX7", "SWR_DMIC5", "VA SWR_MIC5"}, + {"VA SMIC MUX7", "SWR_DMIC6", "VA SWR_MIC6"}, + {"VA SMIC MUX7", "SWR_DMIC7", "VA SWR_MIC7"}, +}; + +static const struct snd_kcontrol_new va_macro_snd_controls[] = { + SOC_SINGLE_SX_TLV("VA_DEC0 Volume", + BOLERO_CDC_VA_TX0_TX_VOL_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("VA_DEC1 Volume", + BOLERO_CDC_VA_TX1_TX_VOL_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("VA_DEC2 Volume", + BOLERO_CDC_VA_TX2_TX_VOL_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("VA_DEC3 Volume", + BOLERO_CDC_VA_TX3_TX_VOL_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("VA_DEC4 Volume", + BOLERO_CDC_VA_TX4_TX_VOL_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("VA_DEC5 Volume", + BOLERO_CDC_VA_TX5_TX_VOL_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("VA_DEC6 Volume", + BOLERO_CDC_VA_TX6_TX_VOL_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("VA_DEC7 Volume", + BOLERO_CDC_VA_TX7_TX_VOL_CTL, + 0, -84, 40, digital_gain), +}; + +static int va_macro_validate_dmic_sample_rate(u32 dmic_sample_rate, + struct va_macro_priv *va_priv) +{ + u32 div_factor; + u32 mclk_rate = VA_MACRO_MCLK_FREQ; + + if (dmic_sample_rate == VA_MACRO_DMIC_SAMPLE_RATE_UNDEFINED || + mclk_rate % dmic_sample_rate != 0) + goto undefined_rate; + + div_factor = mclk_rate / dmic_sample_rate; + + switch (div_factor) { + case 2: + va_priv->dmic_clk_div = VA_MACRO_CLK_DIV_2; + break; + case 3: + va_priv->dmic_clk_div = VA_MACRO_CLK_DIV_3; + break; + case 4: + va_priv->dmic_clk_div = VA_MACRO_CLK_DIV_4; + break; + case 6: + va_priv->dmic_clk_div = VA_MACRO_CLK_DIV_6; + break; + case 8: + va_priv->dmic_clk_div = VA_MACRO_CLK_DIV_8; + break; + case 16: + va_priv->dmic_clk_div = VA_MACRO_CLK_DIV_16; + break; + default: + /* Any other DIV factor is invalid */ + goto undefined_rate; + } + + /* Valid dmic DIV factors */ + dev_dbg(va_priv->dev, "%s: DMIC_DIV = %u, mclk_rate = %u\n", + __func__, div_factor, mclk_rate); + + return dmic_sample_rate; + +undefined_rate: + dev_dbg(va_priv->dev, "%s: Invalid rate %d, for mclk %d\n", + __func__, dmic_sample_rate, mclk_rate); + dmic_sample_rate = VA_MACRO_DMIC_SAMPLE_RATE_UNDEFINED; + + return dmic_sample_rate; +} + +static int va_macro_init(struct snd_soc_codec *codec) +{ + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + int ret, i; + struct device *va_dev = NULL; + struct va_macro_priv *va_priv = NULL; + + va_dev = bolero_get_device_ptr(codec->dev, VA_MACRO); + if (!va_dev) { + dev_err(codec->dev, + "%s: null device for macro!\n", __func__); + return -EINVAL; + } + va_priv = dev_get_drvdata(va_dev); + if (!va_priv) { + dev_err(codec->dev, + "%s: priv is null for macro!\n", __func__); + return -EINVAL; + } + + if (va_priv->va_without_decimation) { + ret = snd_soc_dapm_new_controls(dapm, va_macro_wod_dapm_widgets, + ARRAY_SIZE(va_macro_wod_dapm_widgets)); + if (ret < 0) { + dev_err(va_dev, + "%s: Failed to add without dec controls\n", + __func__); + return ret; + } + va_priv->codec = codec; + return 0; + } + + ret = snd_soc_dapm_new_controls(dapm, va_macro_dapm_widgets, + ARRAY_SIZE(va_macro_dapm_widgets)); + if (ret < 0) { + dev_err(va_dev, "%s: Failed to add controls\n", __func__); + return ret; + } + + ret = snd_soc_dapm_add_routes(dapm, va_audio_map, + ARRAY_SIZE(va_audio_map)); + if (ret < 0) { + dev_err(va_dev, "%s: Failed to add routes\n", __func__); + return ret; + } + + ret = snd_soc_dapm_new_widgets(dapm->card); + if (ret < 0) { + dev_err(va_dev, "%s: Failed to add widgets\n", __func__); + return ret; + } + ret = snd_soc_add_codec_controls(codec, va_macro_snd_controls, + ARRAY_SIZE(va_macro_snd_controls)); + if (ret < 0) { + dev_err(va_dev, "%s: Failed to add snd_ctls\n", __func__); + return ret; + } + + snd_soc_dapm_ignore_suspend(dapm, "VA_AIF1 Capture"); + snd_soc_dapm_ignore_suspend(dapm, "VA_AIF2 Capture"); + snd_soc_dapm_ignore_suspend(dapm, "VA SWR_ADC0"); + snd_soc_dapm_ignore_suspend(dapm, "VA SWR_ADC1"); + snd_soc_dapm_ignore_suspend(dapm, "VA SWR_ADC2"); + snd_soc_dapm_ignore_suspend(dapm, "VA SWR_ADC3"); + snd_soc_dapm_ignore_suspend(dapm, "VA SWR_MIC0"); + snd_soc_dapm_ignore_suspend(dapm, "VA SWR_MIC1"); + snd_soc_dapm_ignore_suspend(dapm, "VA SWR_MIC2"); + snd_soc_dapm_ignore_suspend(dapm, "VA SWR_MIC3"); + snd_soc_dapm_ignore_suspend(dapm, "VA SWR_MIC4"); + snd_soc_dapm_ignore_suspend(dapm, "VA SWR_MIC5"); + snd_soc_dapm_ignore_suspend(dapm, "VA SWR_MIC6"); + snd_soc_dapm_ignore_suspend(dapm, "VA SWR_MIC7"); + snd_soc_dapm_sync(dapm); + + for (i = 0; i < VA_MACRO_NUM_DECIMATORS; i++) { + va_priv->va_hpf_work[i].va_priv = va_priv; + va_priv->va_hpf_work[i].decimator = i; + INIT_DELAYED_WORK(&va_priv->va_hpf_work[i].dwork, + va_macro_tx_hpf_corner_freq_callback); + } + + for (i = 0; i < VA_MACRO_NUM_DECIMATORS; i++) { + va_priv->va_mute_dwork[i].va_priv = va_priv; + va_priv->va_mute_dwork[i].decimator = i; + INIT_DELAYED_WORK(&va_priv->va_mute_dwork[i].dwork, + va_macro_mute_update_callback); + } + va_priv->codec = codec; + + return 0; +} + +static int va_macro_deinit(struct snd_soc_codec *codec) +{ + struct device *va_dev = NULL; + struct va_macro_priv *va_priv = NULL; + + if (!va_macro_get_data(codec, &va_dev, &va_priv, __func__)) + return -EINVAL; + + va_priv->codec = NULL; + return 0; +} + +static void va_macro_init_ops(struct macro_ops *ops, + char __iomem *va_io_base, + bool va_without_decimation) +{ + memset(ops, 0, sizeof(struct macro_ops)); + if (!va_without_decimation) { + ops->dai_ptr = va_macro_dai; + ops->num_dais = ARRAY_SIZE(va_macro_dai); + } else { + ops->dai_ptr = NULL; + ops->num_dais = 0; + } + ops->init = va_macro_init; + ops->exit = va_macro_deinit; + ops->io_base = va_io_base; + ops->mclk_fn = va_macro_mclk_ctrl; + ops->event_handler = va_macro_event_handler; +} + +static int va_macro_probe(struct platform_device *pdev) +{ + struct macro_ops ops; + struct va_macro_priv *va_priv; + u32 va_base_addr, sample_rate = 0; + char __iomem *va_io_base; + struct clk *va_core_clk; + bool va_without_decimation = false; + const char *micb_supply_str = "va-vdd-micb-supply"; + const char *micb_supply_str1 = "va-vdd-micb"; + const char *micb_voltage_str = "qcom,va-vdd-micb-voltage"; + const char *micb_current_str = "qcom,va-vdd-micb-current"; + int ret = 0; + const char *dmic_sample_rate = "qcom,va-dmic-sample-rate"; + + va_priv = devm_kzalloc(&pdev->dev, sizeof(struct va_macro_priv), + GFP_KERNEL); + if (!va_priv) + return -ENOMEM; + + va_priv->dev = &pdev->dev; + ret = of_property_read_u32(pdev->dev.of_node, "reg", + &va_base_addr); + if (ret) { + dev_err(&pdev->dev, "%s: could not find %s entry in dt\n", + __func__, "reg"); + return ret; + } + va_without_decimation = of_property_read_bool(pdev->dev.parent->of_node, + "qcom,va-without-decimation"); + + va_priv->va_without_decimation = va_without_decimation; + ret = of_property_read_u32(pdev->dev.of_node, dmic_sample_rate, + &sample_rate); + if (ret) { + dev_err(&pdev->dev, "%s: could not find %d entry in dt\n", + __func__, sample_rate); + va_priv->dmic_clk_div = VA_MACRO_CLK_DIV_2; + } else { + if (va_macro_validate_dmic_sample_rate( + sample_rate, va_priv) == VA_MACRO_DMIC_SAMPLE_RATE_UNDEFINED) + return -EINVAL; + } + + va_io_base = devm_ioremap(&pdev->dev, va_base_addr, + VA_MAX_OFFSET); + if (!va_io_base) { + dev_err(&pdev->dev, "%s: ioremap failed\n", __func__); + return -EINVAL; + } + va_priv->va_io_base = va_io_base; + /* Register MCLK for va macro */ + va_core_clk = devm_clk_get(&pdev->dev, "va_core_clk"); + if (IS_ERR(va_core_clk)) { + ret = PTR_ERR(va_core_clk); + dev_err(&pdev->dev, "%s: clk get %s failed\n", + __func__, "va_core_clk"); + return ret; + } + va_priv->va_core_clk = va_core_clk; + + if (of_parse_phandle(pdev->dev.of_node, micb_supply_str, 0)) { + va_priv->micb_supply = devm_regulator_get(&pdev->dev, + micb_supply_str1); + if (IS_ERR(va_priv->micb_supply)) { + ret = PTR_ERR(va_priv->micb_supply); + dev_err(&pdev->dev, + "%s:Failed to get micbias supply for VA Mic %d\n", + __func__, ret); + return ret; + } + ret = of_property_read_u32(pdev->dev.of_node, + micb_voltage_str, + &va_priv->micb_voltage); + if (ret) { + dev_err(&pdev->dev, + "%s:Looking up %s property in node %s failed\n", + __func__, micb_voltage_str, + pdev->dev.of_node->full_name); + return ret; + } + ret = of_property_read_u32(pdev->dev.of_node, + micb_current_str, + &va_priv->micb_current); + if (ret) { + dev_err(&pdev->dev, + "%s:Looking up %s property in node %s failed\n", + __func__, micb_current_str, + pdev->dev.of_node->full_name); + return ret; + } + } + + mutex_init(&va_priv->mclk_lock); + dev_set_drvdata(&pdev->dev, va_priv); + va_macro_init_ops(&ops, va_io_base, va_without_decimation); + ret = bolero_register_macro(&pdev->dev, VA_MACRO, &ops); + if (ret < 0) { + dev_err(&pdev->dev, "%s: register macro failed\n", __func__); + goto reg_macro_fail; + } + return ret; + +reg_macro_fail: + mutex_destroy(&va_priv->mclk_lock); + return ret; +} + +static int va_macro_remove(struct platform_device *pdev) +{ + struct va_macro_priv *va_priv; + + va_priv = dev_get_drvdata(&pdev->dev); + + if (!va_priv) + return -EINVAL; + + bolero_unregister_macro(&pdev->dev, VA_MACRO); + mutex_destroy(&va_priv->mclk_lock); + return 0; +} + + +static const struct of_device_id va_macro_dt_match[] = { + {.compatible = "qcom,va-macro"}, + {} +}; + +static struct platform_driver va_macro_driver = { + .driver = { + .name = "va_macro", + .owner = THIS_MODULE, + .of_match_table = va_macro_dt_match, + }, + .probe = va_macro_probe, + .remove = va_macro_remove, +}; + +module_platform_driver(va_macro_driver); + +MODULE_DESCRIPTION("VA macro driver"); +MODULE_LICENSE("GPL v2"); diff --git a/audio-kernel/asoc/codecs/bolero/wsa-macro.c b/audio-kernel/asoc/codecs/bolero/wsa-macro.c new file mode 100644 index 000000000..5e600b985 --- /dev/null +++ b/audio-kernel/asoc/codecs/bolero/wsa-macro.c @@ -0,0 +1,2842 @@ +/* Copyright (c) 2018-2019, 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include "bolero-cdc.h" +#include "bolero-cdc-registers.h" +#include "wsa-macro.h" +#include "../msm-cdc-pinctrl.h" + +#define WSA_MACRO_MAX_OFFSET 0x1000 + +#define WSA_MACRO_RX_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\ + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |\ + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000) +#define WSA_MACRO_RX_MIX_RATES (SNDRV_PCM_RATE_48000 |\ + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000) +#define WSA_MACRO_RX_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ + SNDRV_PCM_FMTBIT_S24_LE |\ + SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE) + +#define WSA_MACRO_ECHO_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\ + SNDRV_PCM_RATE_48000) +#define WSA_MACRO_ECHO_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ + SNDRV_PCM_FMTBIT_S24_LE |\ + SNDRV_PCM_FMTBIT_S24_3LE) + +#define NUM_INTERPOLATORS 2 + +#define WSA_MACRO_MUX_INP_SHFT 0x3 +#define WSA_MACRO_MUX_INP_MASK1 0x38 +#define WSA_MACRO_MUX_INP_MASK2 0x38 +#define WSA_MACRO_MUX_CFG_OFFSET 0x8 +#define WSA_MACRO_MUX_CFG1_OFFSET 0x4 +#define WSA_MACRO_RX_COMP_OFFSET 0x40 +#define WSA_MACRO_RX_SOFTCLIP_OFFSET 0x40 +#define WSA_MACRO_RX_PATH_OFFSET 0x80 +#define WSA_MACRO_RX_PATH_CFG3_OFFSET 0x10 +#define WSA_MACRO_RX_PATH_DSMDEM_OFFSET 0x4C +#define WSA_MACRO_FS_RATE_MASK 0x0F + +enum { + WSA_MACRO_RX0 = 0, + WSA_MACRO_RX1, + WSA_MACRO_RX_MIX, + WSA_MACRO_RX_MIX0 = WSA_MACRO_RX_MIX, + WSA_MACRO_RX_MIX1, + WSA_MACRO_RX_MAX, +}; + +enum { + WSA_MACRO_TX0 = 0, + WSA_MACRO_TX1, + WSA_MACRO_TX_MAX, +}; + +enum { + WSA_MACRO_EC0_MUX = 0, + WSA_MACRO_EC1_MUX, + WSA_MACRO_EC_MUX_MAX, +}; + +enum { + WSA_MACRO_COMP1, /* SPK_L */ + WSA_MACRO_COMP2, /* SPK_R */ + WSA_MACRO_COMP_MAX +}; + +enum { + WSA_MACRO_SOFTCLIP0, /* RX0 */ + WSA_MACRO_SOFTCLIP1, /* RX1 */ + WSA_MACRO_SOFTCLIP_MAX +}; + +struct interp_sample_rate { + int sample_rate; + int rate_val; +}; + +/* + * Structure used to update codec + * register defaults after reset + */ +struct wsa_macro_reg_mask_val { + u16 reg; + u8 mask; + u8 val; +}; + +static struct interp_sample_rate int_prim_sample_rate_val[] = { + {8000, 0x0}, /* 8K */ + {16000, 0x1}, /* 16K */ + {24000, -EINVAL},/* 24K */ + {32000, 0x3}, /* 32K */ + {48000, 0x4}, /* 48K */ + {96000, 0x5}, /* 96K */ + {192000, 0x6}, /* 192K */ + {384000, 0x7}, /* 384K */ + {44100, 0x8}, /* 44.1K */ +}; + +static struct interp_sample_rate int_mix_sample_rate_val[] = { + {48000, 0x4}, /* 48K */ + {96000, 0x5}, /* 96K */ + {192000, 0x6}, /* 192K */ +}; + +#define WSA_MACRO_SWR_STRING_LEN 80 + +static int wsa_macro_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai); +static int wsa_macro_get_channel_map(struct snd_soc_dai *dai, + unsigned int *tx_num, unsigned int *tx_slot, + unsigned int *rx_num, unsigned int *rx_slot); +/* Hold instance to soundwire platform device */ +struct wsa_macro_swr_ctrl_data { + struct platform_device *wsa_swr_pdev; +}; + +struct wsa_macro_swr_ctrl_platform_data { + void *handle; /* holds codec private data */ + int (*read)(void *handle, int reg); + int (*write)(void *handle, int reg, int val); + int (*bulk_write)(void *handle, u32 *reg, u32 *val, size_t len); + int (*clk)(void *handle, bool enable); + int (*handle_irq)(void *handle, + irqreturn_t (*swrm_irq_handler)(int irq, + void *data), + void *swrm_handle, + int action); +}; + +struct wsa_macro_bcl_pmic_params { + u8 id; + u8 sid; + u8 ppid; +}; + +enum { + WSA_MACRO_AIF_INVALID = 0, + WSA_MACRO_AIF1_PB, + WSA_MACRO_AIF_MIX1_PB, + WSA_MACRO_AIF_VI, + WSA_MACRO_AIF_ECHO, + WSA_MACRO_MAX_DAIS, +}; + +#define WSA_MACRO_CHILD_DEVICES_MAX 3 + +/* + * @dev: wsa macro device pointer + * @comp_enabled: compander enable mixer value set + * @ec_hq: echo HQ enable mixer value set + * @prim_int_users: Users of interpolator + * @wsa_mclk_users: WSA MCLK users count + * @swr_clk_users: SWR clk users count + * @vi_feed_value: VI sense mask + * @mclk_lock: to lock mclk operations + * @swr_clk_lock: to lock swr master clock operations + * @swr_ctrl_data: SoundWire data structure + * @swr_plat_data: Soundwire platform data + * @wsa_macro_add_child_devices_work: work for adding child devices + * @wsa_swr_gpio_p: used by pinctrl API + * @wsa_core_clk: MCLK for wsa macro + * @wsa_npl_clk: NPL clock for WSA soundwire + * @codec: codec handle + * @rx_0_count: RX0 interpolation users + * @rx_1_count: RX1 interpolation users + * @active_ch_mask: channel mask for all AIF DAIs + * @active_ch_cnt: channel count of all AIF DAIs + * @rx_port_value: mixer ctl value of WSA RX MUXes + * @wsa_io_base: Base address of WSA macro addr space + */ +struct wsa_macro_priv { + struct device *dev; + int comp_enabled[WSA_MACRO_COMP_MAX]; + int ec_hq[WSA_MACRO_RX1 + 1]; + u16 prim_int_users[WSA_MACRO_RX1 + 1]; + u16 wsa_mclk_users; + u16 swr_clk_users; + bool dapm_mclk_enable; + bool reset_swr; + unsigned int vi_feed_value; + struct mutex mclk_lock; + struct mutex swr_clk_lock; + struct wsa_macro_swr_ctrl_data *swr_ctrl_data; + struct wsa_macro_swr_ctrl_platform_data swr_plat_data; + struct work_struct wsa_macro_add_child_devices_work; + struct device_node *wsa_swr_gpio_p; + struct clk *wsa_core_clk; + struct clk *wsa_npl_clk; + struct snd_soc_codec *codec; + int rx_0_count; + int rx_1_count; + unsigned long active_ch_mask[WSA_MACRO_MAX_DAIS]; + unsigned long active_ch_cnt[WSA_MACRO_MAX_DAIS]; + int rx_port_value[WSA_MACRO_RX_MAX]; + char __iomem *wsa_io_base; + struct platform_device *pdev_child_devices + [WSA_MACRO_CHILD_DEVICES_MAX]; + int child_count; + int ear_spkr_gain; + int spkr_gain_offset; + int spkr_mode; + int is_softclip_on[WSA_MACRO_SOFTCLIP_MAX]; + int softclip_clk_users[WSA_MACRO_SOFTCLIP_MAX]; + struct wsa_macro_bcl_pmic_params bcl_pmic_params; +}; + +static int wsa_macro_config_ear_spkr_gain(struct snd_soc_codec *codec, + struct wsa_macro_priv *wsa_priv, + int event, int gain_reg); +static struct snd_soc_dai_driver wsa_macro_dai[]; +static const DECLARE_TLV_DB_SCALE(digital_gain, 0, 1, 0); + +static const char *const rx_text[] = { + "ZERO", "RX0", "RX1", "RX_MIX0", "RX_MIX1", "DEC0", "DEC1" +}; + +static const char *const rx_mix_text[] = { + "ZERO", "RX0", "RX1", "RX_MIX0", "RX_MIX1" +}; + +static const char *const rx_mix_ec_text[] = { + "ZERO", "RX_MIX_TX0", "RX_MIX_TX1" +}; + +static const char *const rx_mux_text[] = { + "ZERO", "AIF1_PB", "AIF_MIX1_PB" +}; + +static const char *const rx_sidetone_mix_text[] = { + "ZERO", "SRC0" +}; + +static const char * const wsa_macro_ear_spkr_pa_gain_text[] = { + "G_DEFAULT", "G_0_DB", "G_1_DB", "G_2_DB", "G_3_DB", + "G_4_DB", "G_5_DB", "G_6_DB" +}; + +static const char * const wsa_macro_speaker_boost_stage_text[] = { + "NO_MAX_STATE", "MAX_STATE_1", "MAX_STATE_2" +}; + +static const char * const wsa_macro_vbat_bcl_gsm_mode_text[] = { + "OFF", "ON" +}; + +static const struct snd_kcontrol_new wsa_int0_vbat_mix_switch[] = { + SOC_DAPM_SINGLE("WSA RX0 VBAT Enable", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new wsa_int1_vbat_mix_switch[] = { + SOC_DAPM_SINGLE("WSA RX1 VBAT Enable", SND_SOC_NOPM, 0, 1, 0) +}; + +static SOC_ENUM_SINGLE_EXT_DECL(wsa_macro_ear_spkr_pa_gain_enum, + wsa_macro_ear_spkr_pa_gain_text); +static SOC_ENUM_SINGLE_EXT_DECL(wsa_macro_spkr_boost_stage_enum, + wsa_macro_speaker_boost_stage_text); +static SOC_ENUM_SINGLE_EXT_DECL(wsa_macro_vbat_bcl_gsm_mode_enum, + wsa_macro_vbat_bcl_gsm_mode_text); + +/* RX INT0 */ +static const struct soc_enum rx0_prim_inp0_chain_enum = + SOC_ENUM_SINGLE(BOLERO_CDC_WSA_RX_INP_MUX_RX_INT0_CFG0, + 0, 7, rx_text); + +static const struct soc_enum rx0_prim_inp1_chain_enum = + SOC_ENUM_SINGLE(BOLERO_CDC_WSA_RX_INP_MUX_RX_INT0_CFG0, + 3, 7, rx_text); + +static const struct soc_enum rx0_prim_inp2_chain_enum = + SOC_ENUM_SINGLE(BOLERO_CDC_WSA_RX_INP_MUX_RX_INT0_CFG1, + 3, 7, rx_text); + +static const struct soc_enum rx0_mix_chain_enum = + SOC_ENUM_SINGLE(BOLERO_CDC_WSA_RX_INP_MUX_RX_INT0_CFG1, + 0, 5, rx_mix_text); + +static const struct soc_enum rx0_sidetone_mix_enum = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, 2, rx_sidetone_mix_text); + +static const struct snd_kcontrol_new rx0_prim_inp0_mux = + SOC_DAPM_ENUM("WSA_RX0 INP0 Mux", rx0_prim_inp0_chain_enum); + +static const struct snd_kcontrol_new rx0_prim_inp1_mux = + SOC_DAPM_ENUM("WSA_RX0 INP1 Mux", rx0_prim_inp1_chain_enum); + +static const struct snd_kcontrol_new rx0_prim_inp2_mux = + SOC_DAPM_ENUM("WSA_RX0 INP2 Mux", rx0_prim_inp2_chain_enum); + +static const struct snd_kcontrol_new rx0_mix_mux = + SOC_DAPM_ENUM("WSA_RX0 MIX Mux", rx0_mix_chain_enum); + +static const struct snd_kcontrol_new rx0_sidetone_mix_mux = + SOC_DAPM_ENUM("WSA_RX0 SIDETONE MIX Mux", rx0_sidetone_mix_enum); + +/* RX INT1 */ +static const struct soc_enum rx1_prim_inp0_chain_enum = + SOC_ENUM_SINGLE(BOLERO_CDC_WSA_RX_INP_MUX_RX_INT1_CFG0, + 0, 7, rx_text); + +static const struct soc_enum rx1_prim_inp1_chain_enum = + SOC_ENUM_SINGLE(BOLERO_CDC_WSA_RX_INP_MUX_RX_INT1_CFG0, + 3, 7, rx_text); + +static const struct soc_enum rx1_prim_inp2_chain_enum = + SOC_ENUM_SINGLE(BOLERO_CDC_WSA_RX_INP_MUX_RX_INT1_CFG1, + 3, 7, rx_text); + +static const struct soc_enum rx1_mix_chain_enum = + SOC_ENUM_SINGLE(BOLERO_CDC_WSA_RX_INP_MUX_RX_INT1_CFG1, + 0, 5, rx_mix_text); + +static const struct snd_kcontrol_new rx1_prim_inp0_mux = + SOC_DAPM_ENUM("WSA_RX1 INP0 Mux", rx1_prim_inp0_chain_enum); + +static const struct snd_kcontrol_new rx1_prim_inp1_mux = + SOC_DAPM_ENUM("WSA_RX1 INP1 Mux", rx1_prim_inp1_chain_enum); + +static const struct snd_kcontrol_new rx1_prim_inp2_mux = + SOC_DAPM_ENUM("WSA_RX1 INP2 Mux", rx1_prim_inp2_chain_enum); + +static const struct snd_kcontrol_new rx1_mix_mux = + SOC_DAPM_ENUM("WSA_RX1 MIX Mux", rx1_mix_chain_enum); + +static const struct soc_enum rx_mix_ec0_enum = + SOC_ENUM_SINGLE(BOLERO_CDC_WSA_RX_INP_MUX_RX_MIX_CFG0, + 0, 3, rx_mix_ec_text); + +static const struct soc_enum rx_mix_ec1_enum = + SOC_ENUM_SINGLE(BOLERO_CDC_WSA_RX_INP_MUX_RX_MIX_CFG0, + 3, 3, rx_mix_ec_text); + +static const struct snd_kcontrol_new rx_mix_ec0_mux = + SOC_DAPM_ENUM("WSA RX_MIX EC0_Mux", rx_mix_ec0_enum); + +static const struct snd_kcontrol_new rx_mix_ec1_mux = + SOC_DAPM_ENUM("WSA RX_MIX EC1_Mux", rx_mix_ec1_enum); + +static struct snd_soc_dai_ops wsa_macro_dai_ops = { + .hw_params = wsa_macro_hw_params, + .get_channel_map = wsa_macro_get_channel_map, +}; + +static struct snd_soc_dai_driver wsa_macro_dai[] = { + { + .name = "wsa_macro_rx1", + .id = WSA_MACRO_AIF1_PB, + .playback = { + .stream_name = "WSA_AIF1 Playback", + .rates = WSA_MACRO_RX_RATES, + .formats = WSA_MACRO_RX_FORMATS, + .rate_max = 384000, + .rate_min = 8000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &wsa_macro_dai_ops, + }, + { + .name = "wsa_macro_rx_mix", + .id = WSA_MACRO_AIF_MIX1_PB, + .playback = { + .stream_name = "WSA_AIF_MIX1 Playback", + .rates = WSA_MACRO_RX_MIX_RATES, + .formats = WSA_MACRO_RX_FORMATS, + .rate_max = 192000, + .rate_min = 48000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &wsa_macro_dai_ops, + }, + { + .name = "wsa_macro_vifeedback", + .id = WSA_MACRO_AIF_VI, + .capture = { + .stream_name = "WSA_AIF_VI Capture", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_48000, + .formats = WSA_MACRO_RX_FORMATS, + .rate_max = 48000, + .rate_min = 8000, + .channels_min = 1, + .channels_max = 4, + }, + .ops = &wsa_macro_dai_ops, + }, + { + .name = "wsa_macro_echo", + .id = WSA_MACRO_AIF_ECHO, + .capture = { + .stream_name = "WSA_AIF_ECHO Capture", + .rates = WSA_MACRO_ECHO_RATES, + .formats = WSA_MACRO_ECHO_FORMATS, + .rate_max = 48000, + .rate_min = 8000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &wsa_macro_dai_ops, + }, +}; + +static const struct wsa_macro_reg_mask_val wsa_macro_spkr_default[] = { + {BOLERO_CDC_WSA_COMPANDER0_CTL3, 0x80, 0x80}, + {BOLERO_CDC_WSA_COMPANDER1_CTL3, 0x80, 0x80}, + {BOLERO_CDC_WSA_COMPANDER0_CTL7, 0x01, 0x01}, + {BOLERO_CDC_WSA_COMPANDER1_CTL7, 0x01, 0x01}, + {BOLERO_CDC_WSA_BOOST0_BOOST_CTL, 0x7C, 0x58}, + {BOLERO_CDC_WSA_BOOST1_BOOST_CTL, 0x7C, 0x58}, +}; + +static const struct wsa_macro_reg_mask_val wsa_macro_spkr_mode1[] = { + {BOLERO_CDC_WSA_COMPANDER0_CTL3, 0x80, 0x00}, + {BOLERO_CDC_WSA_COMPANDER1_CTL3, 0x80, 0x00}, + {BOLERO_CDC_WSA_COMPANDER0_CTL7, 0x01, 0x00}, + {BOLERO_CDC_WSA_COMPANDER1_CTL7, 0x01, 0x00}, + {BOLERO_CDC_WSA_BOOST0_BOOST_CTL, 0x7C, 0x44}, + {BOLERO_CDC_WSA_BOOST1_BOOST_CTL, 0x7C, 0x44}, +}; + +static bool wsa_macro_get_data(struct snd_soc_codec *codec, + struct device **wsa_dev, + struct wsa_macro_priv **wsa_priv, + const char *func_name) +{ + *wsa_dev = bolero_get_device_ptr(codec->dev, WSA_MACRO); + if (!(*wsa_dev)) { + dev_err(codec->dev, + "%s: null device for macro!\n", func_name); + return false; + } + *wsa_priv = dev_get_drvdata((*wsa_dev)); + if (!(*wsa_priv) || !(*wsa_priv)->codec) { + dev_err(codec->dev, + "%s: priv is null for macro!\n", func_name); + return false; + } + return true; +} + +/** + * wsa_macro_set_spkr_gain_offset - offset the speaker path + * gain with the given offset value. + * + * @codec: codec instance + * @offset: Indicates speaker path gain offset value. + * + * Returns 0 on success or -EINVAL on error. + */ +int wsa_macro_set_spkr_gain_offset(struct snd_soc_codec *codec, int offset) +{ + struct device *wsa_dev = NULL; + struct wsa_macro_priv *wsa_priv = NULL; + + if (!codec) { + pr_err("%s: NULL codec pointer!\n", __func__); + return -EINVAL; + } + + if (!wsa_macro_get_data(codec, &wsa_dev, &wsa_priv, __func__)) + return -EINVAL; + + wsa_priv->spkr_gain_offset = offset; + return 0; +} +EXPORT_SYMBOL(wsa_macro_set_spkr_gain_offset); + +/** + * wsa_macro_set_spkr_mode - Configures speaker compander and smartboost + * settings based on speaker mode. + * + * @codec: codec instance + * @mode: Indicates speaker configuration mode. + * + * Returns 0 on success or -EINVAL on error. + */ +int wsa_macro_set_spkr_mode(struct snd_soc_codec *codec, int mode) +{ + int i; + const struct wsa_macro_reg_mask_val *regs; + int size; + struct device *wsa_dev = NULL; + struct wsa_macro_priv *wsa_priv = NULL; + + if (!codec) { + pr_err("%s: NULL codec pointer!\n", __func__); + return -EINVAL; + } + + if (!wsa_macro_get_data(codec, &wsa_dev, &wsa_priv, __func__)) + return -EINVAL; + + switch (mode) { + case WSA_MACRO_SPKR_MODE_1: + regs = wsa_macro_spkr_mode1; + size = ARRAY_SIZE(wsa_macro_spkr_mode1); + break; + default: + regs = wsa_macro_spkr_default; + size = ARRAY_SIZE(wsa_macro_spkr_default); + break; + } + + wsa_priv->spkr_mode = mode; + for (i = 0; i < size; i++) + snd_soc_update_bits(codec, regs[i].reg, + regs[i].mask, regs[i].val); + return 0; +} +EXPORT_SYMBOL(wsa_macro_set_spkr_mode); + +static int wsa_macro_set_prim_interpolator_rate(struct snd_soc_dai *dai, + u8 int_prim_fs_rate_reg_val, + u32 sample_rate) +{ + u8 int_1_mix1_inp; + u32 j, port; + u16 int_mux_cfg0, int_mux_cfg1; + u16 int_fs_reg; + u8 int_mux_cfg0_val, int_mux_cfg1_val; + u8 inp0_sel, inp1_sel, inp2_sel; + struct snd_soc_codec *codec = dai->codec; + struct device *wsa_dev = NULL; + struct wsa_macro_priv *wsa_priv = NULL; + + if (!wsa_macro_get_data(codec, &wsa_dev, &wsa_priv, __func__)) + return -EINVAL; + + for_each_set_bit(port, &wsa_priv->active_ch_mask[dai->id], + WSA_MACRO_RX_MAX) { + int_1_mix1_inp = port; + if ((int_1_mix1_inp < WSA_MACRO_RX0) || + (int_1_mix1_inp > WSA_MACRO_RX_MIX1)) { + dev_err(wsa_dev, + "%s: Invalid RX port, Dai ID is %d\n", + __func__, dai->id); + return -EINVAL; + } + + int_mux_cfg0 = BOLERO_CDC_WSA_RX_INP_MUX_RX_INT0_CFG0; + + /* + * Loop through all interpolator MUX inputs and find out + * to which interpolator input, the cdc_dma rx port + * is connected + */ + for (j = 0; j < NUM_INTERPOLATORS; j++) { + int_mux_cfg1 = int_mux_cfg0 + WSA_MACRO_MUX_CFG1_OFFSET; + + int_mux_cfg0_val = snd_soc_read(codec, int_mux_cfg0); + int_mux_cfg1_val = snd_soc_read(codec, int_mux_cfg1); + inp0_sel = int_mux_cfg0_val & WSA_MACRO_MUX_INP_MASK1; + inp1_sel = (int_mux_cfg0_val >> + WSA_MACRO_MUX_INP_SHFT) & + WSA_MACRO_MUX_INP_MASK2; + inp2_sel = (int_mux_cfg1_val >> + WSA_MACRO_MUX_INP_SHFT) & + WSA_MACRO_MUX_INP_MASK2; + if ((inp0_sel == int_1_mix1_inp) || + (inp1_sel == int_1_mix1_inp) || + (inp2_sel == int_1_mix1_inp)) { + int_fs_reg = BOLERO_CDC_WSA_RX0_RX_PATH_CTL + + WSA_MACRO_RX_PATH_OFFSET * j; + dev_dbg(wsa_dev, + "%s: AIF_PB DAI(%d) connected to INT%u_1\n", + __func__, dai->id, j); + dev_dbg(wsa_dev, + "%s: set INT%u_1 sample rate to %u\n", + __func__, j, sample_rate); + /* sample_rate is in Hz */ + snd_soc_update_bits(codec, int_fs_reg, + WSA_MACRO_FS_RATE_MASK, + int_prim_fs_rate_reg_val); + } + int_mux_cfg0 += WSA_MACRO_MUX_CFG_OFFSET; + } + } + + return 0; +} + +static int wsa_macro_set_mix_interpolator_rate(struct snd_soc_dai *dai, + u8 int_mix_fs_rate_reg_val, + u32 sample_rate) +{ + u8 int_2_inp; + u32 j, port; + u16 int_mux_cfg1, int_fs_reg; + u8 int_mux_cfg1_val; + struct snd_soc_codec *codec = dai->codec; + struct device *wsa_dev = NULL; + struct wsa_macro_priv *wsa_priv = NULL; + + if (!wsa_macro_get_data(codec, &wsa_dev, &wsa_priv, __func__)) + return -EINVAL; + + + for_each_set_bit(port, &wsa_priv->active_ch_mask[dai->id], + WSA_MACRO_RX_MAX) { + int_2_inp = port; + if ((int_2_inp < WSA_MACRO_RX0) || + (int_2_inp > WSA_MACRO_RX_MIX1)) { + dev_err(wsa_dev, + "%s: Invalid RX port, Dai ID is %d\n", + __func__, dai->id); + return -EINVAL; + } + + int_mux_cfg1 = BOLERO_CDC_WSA_RX_INP_MUX_RX_INT0_CFG1; + for (j = 0; j < NUM_INTERPOLATORS; j++) { + int_mux_cfg1_val = snd_soc_read(codec, int_mux_cfg1) & + WSA_MACRO_MUX_INP_MASK1; + if (int_mux_cfg1_val == int_2_inp) { + int_fs_reg = + BOLERO_CDC_WSA_RX0_RX_PATH_MIX_CTL + + WSA_MACRO_RX_PATH_OFFSET * j; + + dev_dbg(wsa_dev, + "%s: AIF_PB DAI(%d) connected to INT%u_2\n", + __func__, dai->id, j); + dev_dbg(wsa_dev, + "%s: set INT%u_2 sample rate to %u\n", + __func__, j, sample_rate); + snd_soc_update_bits(codec, int_fs_reg, + WSA_MACRO_FS_RATE_MASK, + int_mix_fs_rate_reg_val); + } + int_mux_cfg1 += WSA_MACRO_MUX_CFG_OFFSET; + } + } + return 0; +} + +static int wsa_macro_set_interpolator_rate(struct snd_soc_dai *dai, + u32 sample_rate) +{ + int rate_val = 0; + int i, ret; + + /* set mixing path rate */ + for (i = 0; i < ARRAY_SIZE(int_mix_sample_rate_val); i++) { + if (sample_rate == + int_mix_sample_rate_val[i].sample_rate) { + rate_val = + int_mix_sample_rate_val[i].rate_val; + break; + } + } + if ((i == ARRAY_SIZE(int_mix_sample_rate_val)) || + (rate_val < 0)) + goto prim_rate; + ret = wsa_macro_set_mix_interpolator_rate(dai, + (u8) rate_val, sample_rate); +prim_rate: + /* set primary path sample rate */ + for (i = 0; i < ARRAY_SIZE(int_prim_sample_rate_val); i++) { + if (sample_rate == + int_prim_sample_rate_val[i].sample_rate) { + rate_val = + int_prim_sample_rate_val[i].rate_val; + break; + } + } + if ((i == ARRAY_SIZE(int_prim_sample_rate_val)) || + (rate_val < 0)) + return -EINVAL; + ret = wsa_macro_set_prim_interpolator_rate(dai, + (u8) rate_val, sample_rate); + return ret; +} + +static int wsa_macro_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + int ret; + + dev_dbg(codec->dev, + "%s: dai_name = %s DAI-ID %x rate %d num_ch %d\n", __func__, + dai->name, dai->id, params_rate(params), + params_channels(params)); + + switch (substream->stream) { + case SNDRV_PCM_STREAM_PLAYBACK: + ret = wsa_macro_set_interpolator_rate(dai, params_rate(params)); + if (ret) { + dev_err(codec->dev, + "%s: cannot set sample rate: %u\n", + __func__, params_rate(params)); + return ret; + } + break; + case SNDRV_PCM_STREAM_CAPTURE: + default: + break; + } + return 0; +} + +static int wsa_macro_get_channel_map(struct snd_soc_dai *dai, + unsigned int *tx_num, unsigned int *tx_slot, + unsigned int *rx_num, unsigned int *rx_slot) +{ + struct snd_soc_codec *codec = dai->codec; + struct device *wsa_dev = NULL; + struct wsa_macro_priv *wsa_priv = NULL; + + if (!wsa_macro_get_data(codec, &wsa_dev, &wsa_priv, __func__)) + return -EINVAL; + + wsa_priv = dev_get_drvdata(wsa_dev); + if (!wsa_priv) + return -EINVAL; + + switch (dai->id) { + case WSA_MACRO_AIF_VI: + case WSA_MACRO_AIF_ECHO: + *tx_slot = wsa_priv->active_ch_mask[dai->id]; + *tx_num = wsa_priv->active_ch_cnt[dai->id]; + break; + case WSA_MACRO_AIF1_PB: + case WSA_MACRO_AIF_MIX1_PB: + *rx_slot = wsa_priv->active_ch_mask[dai->id]; + *rx_num = wsa_priv->active_ch_cnt[dai->id]; + break; + default: + dev_err(wsa_dev, "%s: Invalid AIF\n", __func__); + break; + } + return 0; +} + +static int wsa_macro_mclk_enable(struct wsa_macro_priv *wsa_priv, + bool mclk_enable, bool dapm) +{ + struct regmap *regmap = dev_get_regmap(wsa_priv->dev->parent, NULL); + int ret = 0; + + if (regmap == NULL) { + dev_err(wsa_priv->dev, "%s: regmap is NULL\n", __func__); + return -EINVAL; + } + + dev_dbg(wsa_priv->dev, "%s: mclk_enable = %u, dapm = %d clk_users= %d\n", + __func__, mclk_enable, dapm, wsa_priv->wsa_mclk_users); + + mutex_lock(&wsa_priv->mclk_lock); + if (mclk_enable) { + if (wsa_priv->wsa_mclk_users == 0) { + ret = bolero_request_clock(wsa_priv->dev, + WSA_MACRO, MCLK_MUX0, true); + if (ret < 0) { + dev_err_ratelimited(wsa_priv->dev, + "%s: wsa request clock enable failed\n", + __func__); + goto exit; + } + regcache_mark_dirty(regmap); + regcache_sync_region(regmap, + WSA_START_OFFSET, + WSA_MAX_OFFSET); + /* 9.6MHz MCLK, set value 0x00 if other frequency */ + regmap_update_bits(regmap, + BOLERO_CDC_WSA_TOP_FREQ_MCLK, 0x01, 0x01); + regmap_update_bits(regmap, + BOLERO_CDC_WSA_CLK_RST_CTRL_MCLK_CONTROL, + 0x01, 0x01); + regmap_update_bits(regmap, + BOLERO_CDC_WSA_CLK_RST_CTRL_FS_CNT_CONTROL, + 0x01, 0x01); + } + wsa_priv->wsa_mclk_users++; + } else { + if (wsa_priv->wsa_mclk_users <= 0) { + dev_err(wsa_priv->dev, "%s: clock already disabled\n", + __func__); + wsa_priv->wsa_mclk_users = 0; + goto exit; + } + wsa_priv->wsa_mclk_users--; + if (wsa_priv->wsa_mclk_users == 0) { + regmap_update_bits(regmap, + BOLERO_CDC_WSA_CLK_RST_CTRL_FS_CNT_CONTROL, + 0x01, 0x00); + regmap_update_bits(regmap, + BOLERO_CDC_WSA_CLK_RST_CTRL_MCLK_CONTROL, + 0x01, 0x00); + bolero_request_clock(wsa_priv->dev, + WSA_MACRO, MCLK_MUX0, false); + } + } +exit: + mutex_unlock(&wsa_priv->mclk_lock); + return ret; +} + +static int wsa_macro_mclk_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + int ret = 0; + struct device *wsa_dev = NULL; + struct wsa_macro_priv *wsa_priv = NULL; + + if (!wsa_macro_get_data(codec, &wsa_dev, &wsa_priv, __func__)) + return -EINVAL; + + dev_dbg(wsa_dev, "%s: event = %d\n", __func__, event); + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + ret = wsa_macro_mclk_enable(wsa_priv, 1, true); + if (ret) + wsa_priv->dapm_mclk_enable = false; + else + wsa_priv->dapm_mclk_enable = true; + break; + case SND_SOC_DAPM_POST_PMD: + if (wsa_priv->dapm_mclk_enable) + wsa_macro_mclk_enable(wsa_priv, 0, true); + break; + default: + dev_err(wsa_priv->dev, + "%s: invalid DAPM event %d\n", __func__, event); + ret = -EINVAL; + } + return ret; +} + +static int wsa_macro_mclk_ctrl(struct device *dev, bool enable) +{ + struct wsa_macro_priv *wsa_priv = dev_get_drvdata(dev); + int ret = 0; + + if (!wsa_priv) + return -EINVAL; + + if (enable) { + ret = clk_prepare_enable(wsa_priv->wsa_core_clk); + if (ret < 0) { + dev_err(dev, "%s:wsa mclk enable failed\n", __func__); + goto exit; + } + ret = clk_prepare_enable(wsa_priv->wsa_npl_clk); + if (ret < 0) { + dev_err(dev, "%s:wsa npl_clk enable failed\n", + __func__); + clk_disable_unprepare(wsa_priv->wsa_core_clk); + goto exit; + } + } else { + clk_disable_unprepare(wsa_priv->wsa_npl_clk); + clk_disable_unprepare(wsa_priv->wsa_core_clk); + } +exit: + return ret; +} + +static int wsa_macro_event_handler(struct snd_soc_codec *codec, u16 event, + u32 data) +{ + struct device *wsa_dev = NULL; + struct wsa_macro_priv *wsa_priv = NULL; + + if (!wsa_macro_get_data(codec, &wsa_dev, &wsa_priv, __func__)) + return -EINVAL; + + switch (event) { + case BOLERO_MACRO_EVT_SSR_DOWN: + swrm_wcd_notify( + wsa_priv->swr_ctrl_data[0].wsa_swr_pdev, + SWR_DEVICE_DOWN, NULL); + swrm_wcd_notify( + wsa_priv->swr_ctrl_data[0].wsa_swr_pdev, + SWR_DEVICE_SSR_DOWN, NULL); + break; + case BOLERO_MACRO_EVT_SSR_UP: + /* reset swr after ssr/pdr */ + wsa_priv->reset_swr = true; + swrm_wcd_notify( + wsa_priv->swr_ctrl_data[0].wsa_swr_pdev, + SWR_DEVICE_SSR_UP, NULL); + break; + } + return 0; +} + +static int wsa_macro_enable_vi_feedback(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct device *wsa_dev = NULL; + struct wsa_macro_priv *wsa_priv = NULL; + + if (!wsa_macro_get_data(codec, &wsa_dev, &wsa_priv, __func__)) + return -EINVAL; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + if (test_bit(WSA_MACRO_TX0, + &wsa_priv->active_ch_mask[WSA_MACRO_AIF_VI])) { + dev_dbg(wsa_dev, "%s: spkr1 enabled\n", __func__); + /* Enable V&I sensing */ + snd_soc_update_bits(codec, + BOLERO_CDC_WSA_TX0_SPKR_PROT_PATH_CTL, + 0x20, 0x20); + snd_soc_update_bits(codec, + BOLERO_CDC_WSA_TX1_SPKR_PROT_PATH_CTL, + 0x20, 0x20); + snd_soc_update_bits(codec, + BOLERO_CDC_WSA_TX0_SPKR_PROT_PATH_CTL, + 0x0F, 0x00); + snd_soc_update_bits(codec, + BOLERO_CDC_WSA_TX1_SPKR_PROT_PATH_CTL, + 0x0F, 0x00); + snd_soc_update_bits(codec, + BOLERO_CDC_WSA_TX0_SPKR_PROT_PATH_CTL, + 0x10, 0x10); + snd_soc_update_bits(codec, + BOLERO_CDC_WSA_TX1_SPKR_PROT_PATH_CTL, + 0x10, 0x10); + snd_soc_update_bits(codec, + BOLERO_CDC_WSA_TX0_SPKR_PROT_PATH_CTL, + 0x20, 0x00); + snd_soc_update_bits(codec, + BOLERO_CDC_WSA_TX1_SPKR_PROT_PATH_CTL, + 0x20, 0x00); + } + if (test_bit(WSA_MACRO_TX1, + &wsa_priv->active_ch_mask[WSA_MACRO_AIF_VI])) { + dev_dbg(wsa_dev, "%s: spkr2 enabled\n", __func__); + /* Enable V&I sensing */ + snd_soc_update_bits(codec, + BOLERO_CDC_WSA_TX2_SPKR_PROT_PATH_CTL, + 0x20, 0x20); + snd_soc_update_bits(codec, + BOLERO_CDC_WSA_TX3_SPKR_PROT_PATH_CTL, + 0x20, 0x20); + snd_soc_update_bits(codec, + BOLERO_CDC_WSA_TX2_SPKR_PROT_PATH_CTL, + 0x0F, 0x00); + snd_soc_update_bits(codec, + BOLERO_CDC_WSA_TX3_SPKR_PROT_PATH_CTL, + 0x0F, 0x00); + snd_soc_update_bits(codec, + BOLERO_CDC_WSA_TX2_SPKR_PROT_PATH_CTL, + 0x10, 0x10); + snd_soc_update_bits(codec, + BOLERO_CDC_WSA_TX3_SPKR_PROT_PATH_CTL, + 0x10, 0x10); + snd_soc_update_bits(codec, + BOLERO_CDC_WSA_TX2_SPKR_PROT_PATH_CTL, + 0x20, 0x00); + snd_soc_update_bits(codec, + BOLERO_CDC_WSA_TX3_SPKR_PROT_PATH_CTL, + 0x20, 0x00); + } + break; + case SND_SOC_DAPM_POST_PMD: + if (test_bit(WSA_MACRO_TX0, + &wsa_priv->active_ch_mask[WSA_MACRO_AIF_VI])) { + /* Disable V&I sensing */ + snd_soc_update_bits(codec, + BOLERO_CDC_WSA_TX0_SPKR_PROT_PATH_CTL, + 0x20, 0x20); + snd_soc_update_bits(codec, + BOLERO_CDC_WSA_TX1_SPKR_PROT_PATH_CTL, + 0x20, 0x20); + dev_dbg(wsa_dev, "%s: spkr1 disabled\n", __func__); + snd_soc_update_bits(codec, + BOLERO_CDC_WSA_TX0_SPKR_PROT_PATH_CTL, + 0x10, 0x00); + snd_soc_update_bits(codec, + BOLERO_CDC_WSA_TX1_SPKR_PROT_PATH_CTL, + 0x10, 0x00); + } + if (test_bit(WSA_MACRO_TX1, + &wsa_priv->active_ch_mask[WSA_MACRO_AIF_VI])) { + /* Disable V&I sensing */ + dev_dbg(wsa_dev, "%s: spkr2 disabled\n", __func__); + snd_soc_update_bits(codec, + BOLERO_CDC_WSA_TX2_SPKR_PROT_PATH_CTL, + 0x20, 0x20); + snd_soc_update_bits(codec, + BOLERO_CDC_WSA_TX3_SPKR_PROT_PATH_CTL, + 0x20, 0x20); + snd_soc_update_bits(codec, + BOLERO_CDC_WSA_TX2_SPKR_PROT_PATH_CTL, + 0x10, 0x00); + snd_soc_update_bits(codec, + BOLERO_CDC_WSA_TX3_SPKR_PROT_PATH_CTL, + 0x10, 0x00); + } + break; + } + + return 0; +} + +static int wsa_macro_enable_mix_path(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + u16 gain_reg; + int offset_val = 0; + int val = 0; + + dev_dbg(codec->dev, "%s %d %s\n", __func__, event, w->name); + + switch (w->reg) { + case BOLERO_CDC_WSA_RX0_RX_PATH_MIX_CTL: + gain_reg = BOLERO_CDC_WSA_RX0_RX_VOL_MIX_CTL; + break; + case BOLERO_CDC_WSA_RX1_RX_PATH_MIX_CTL: + gain_reg = BOLERO_CDC_WSA_RX1_RX_VOL_MIX_CTL; + break; + default: + dev_err(codec->dev, "%s: No gain register avail for %s\n", + __func__, w->name); + return 0; + } + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + val = snd_soc_read(codec, gain_reg); + val += offset_val; + snd_soc_write(codec, gain_reg, val); + break; + case SND_SOC_DAPM_POST_PMD: + break; + } + + return 0; +} + +static void wsa_macro_hd2_control(struct snd_soc_codec *codec, + u16 reg, int event) +{ + u16 hd2_scale_reg; + u16 hd2_enable_reg = 0; + + if (reg == BOLERO_CDC_WSA_RX0_RX_PATH_CTL) { + hd2_scale_reg = BOLERO_CDC_WSA_RX0_RX_PATH_SEC3; + hd2_enable_reg = BOLERO_CDC_WSA_RX0_RX_PATH_CFG0; + } + if (reg == BOLERO_CDC_WSA_RX1_RX_PATH_CTL) { + hd2_scale_reg = BOLERO_CDC_WSA_RX1_RX_PATH_SEC3; + hd2_enable_reg = BOLERO_CDC_WSA_RX1_RX_PATH_CFG0; + } + + if (hd2_enable_reg && SND_SOC_DAPM_EVENT_ON(event)) { + snd_soc_update_bits(codec, hd2_scale_reg, 0x3C, 0x10); + snd_soc_update_bits(codec, hd2_scale_reg, 0x03, 0x01); + snd_soc_update_bits(codec, hd2_enable_reg, 0x04, 0x04); + } + + if (hd2_enable_reg && SND_SOC_DAPM_EVENT_OFF(event)) { + snd_soc_update_bits(codec, hd2_enable_reg, 0x04, 0x00); + snd_soc_update_bits(codec, hd2_scale_reg, 0x03, 0x00); + snd_soc_update_bits(codec, hd2_scale_reg, 0x3C, 0x00); + } +} + +static int wsa_macro_enable_swr(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + int ch_cnt; + struct device *wsa_dev = NULL; + struct wsa_macro_priv *wsa_priv = NULL; + + if (!wsa_macro_get_data(codec, &wsa_dev, &wsa_priv, __func__)) + return -EINVAL; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (!(strnstr(w->name, "RX0", sizeof("WSA_RX0"))) && + !wsa_priv->rx_0_count) + wsa_priv->rx_0_count++; + if (!(strnstr(w->name, "RX1", sizeof("WSA_RX1"))) && + !wsa_priv->rx_1_count) + wsa_priv->rx_1_count++; + ch_cnt = wsa_priv->rx_0_count + wsa_priv->rx_1_count; + + swrm_wcd_notify( + wsa_priv->swr_ctrl_data[0].wsa_swr_pdev, + SWR_DEVICE_UP, NULL); + swrm_wcd_notify( + wsa_priv->swr_ctrl_data[0].wsa_swr_pdev, + SWR_SET_NUM_RX_CH, &ch_cnt); + break; + case SND_SOC_DAPM_POST_PMD: + if (!(strnstr(w->name, "RX0", sizeof("WSA_RX0"))) && + wsa_priv->rx_0_count) + wsa_priv->rx_0_count--; + if (!(strnstr(w->name, "RX1", sizeof("WSA_RX1"))) && + wsa_priv->rx_1_count) + wsa_priv->rx_1_count--; + ch_cnt = wsa_priv->rx_0_count + wsa_priv->rx_1_count; + + swrm_wcd_notify( + wsa_priv->swr_ctrl_data[0].wsa_swr_pdev, + SWR_SET_NUM_RX_CH, &ch_cnt); + break; + } + dev_dbg(wsa_priv->dev, "%s: current swr ch cnt: %d\n", + __func__, wsa_priv->rx_0_count + wsa_priv->rx_1_count); + + return 0; +} + +static int wsa_macro_config_compander(struct snd_soc_codec *codec, + int comp, int event) +{ + u16 comp_ctl0_reg, rx_path_cfg0_reg; + struct device *wsa_dev = NULL; + struct wsa_macro_priv *wsa_priv = NULL; + + if (!wsa_macro_get_data(codec, &wsa_dev, &wsa_priv, __func__)) + return -EINVAL; + + dev_dbg(codec->dev, "%s: event %d compander %d, enabled %d\n", + __func__, event, comp + 1, wsa_priv->comp_enabled[comp]); + + if (!wsa_priv->comp_enabled[comp]) + return 0; + + comp_ctl0_reg = BOLERO_CDC_WSA_COMPANDER0_CTL0 + + (comp * WSA_MACRO_RX_COMP_OFFSET); + rx_path_cfg0_reg = BOLERO_CDC_WSA_RX0_RX_PATH_CFG0 + + (comp * WSA_MACRO_RX_PATH_OFFSET); + + if (SND_SOC_DAPM_EVENT_ON(event)) { + /* Enable Compander Clock */ + snd_soc_update_bits(codec, comp_ctl0_reg, 0x01, 0x01); + snd_soc_update_bits(codec, comp_ctl0_reg, 0x02, 0x02); + snd_soc_update_bits(codec, comp_ctl0_reg, 0x02, 0x00); + snd_soc_update_bits(codec, rx_path_cfg0_reg, 0x02, 0x02); + } + + if (SND_SOC_DAPM_EVENT_OFF(event)) { + snd_soc_update_bits(codec, comp_ctl0_reg, 0x04, 0x04); + snd_soc_update_bits(codec, rx_path_cfg0_reg, 0x02, 0x00); + snd_soc_update_bits(codec, comp_ctl0_reg, 0x02, 0x02); + snd_soc_update_bits(codec, comp_ctl0_reg, 0x02, 0x00); + snd_soc_update_bits(codec, comp_ctl0_reg, 0x01, 0x00); + snd_soc_update_bits(codec, comp_ctl0_reg, 0x04, 0x00); + } + + return 0; +} + +static void wsa_macro_enable_softclip_clk(struct snd_soc_codec *codec, + struct wsa_macro_priv *wsa_priv, + int path, + bool enable) +{ + u16 softclip_clk_reg = BOLERO_CDC_WSA_SOFTCLIP0_CRC + + (path * WSA_MACRO_RX_SOFTCLIP_OFFSET); + u8 softclip_mux_mask = (1 << path); + u8 softclip_mux_value = (1 << path); + + dev_dbg(codec->dev, "%s: path %d, enable %d\n", + __func__, path, enable); + if (enable) { + if (wsa_priv->softclip_clk_users[path] == 0) { + snd_soc_update_bits(codec, + softclip_clk_reg, 0x01, 0x01); + snd_soc_update_bits(codec, + BOLERO_CDC_WSA_RX_INP_MUX_SOFTCLIP_CFG0, + softclip_mux_mask, softclip_mux_value); + } + wsa_priv->softclip_clk_users[path]++; + } else { + wsa_priv->softclip_clk_users[path]--; + if (wsa_priv->softclip_clk_users[path] == 0) { + snd_soc_update_bits(codec, + softclip_clk_reg, 0x01, 0x00); + snd_soc_update_bits(codec, + BOLERO_CDC_WSA_RX_INP_MUX_SOFTCLIP_CFG0, + softclip_mux_mask, 0x00); + } + } +} + +static int wsa_macro_config_softclip(struct snd_soc_codec *codec, + int path, int event) +{ + u16 softclip_ctrl_reg = 0; + struct device *wsa_dev = NULL; + struct wsa_macro_priv *wsa_priv = NULL; + int softclip_path = 0; + + if (!wsa_macro_get_data(codec, &wsa_dev, &wsa_priv, __func__)) + return -EINVAL; + + if (path == WSA_MACRO_COMP1) + softclip_path = WSA_MACRO_SOFTCLIP0; + else if (path == WSA_MACRO_COMP2) + softclip_path = WSA_MACRO_SOFTCLIP1; + + dev_dbg(codec->dev, "%s: event %d path %d, enabled %d\n", + __func__, event, softclip_path, + wsa_priv->is_softclip_on[softclip_path]); + + if (!wsa_priv->is_softclip_on[softclip_path]) + return 0; + + softclip_ctrl_reg = BOLERO_CDC_WSA_SOFTCLIP0_SOFTCLIP_CTRL + + (softclip_path * WSA_MACRO_RX_SOFTCLIP_OFFSET); + + if (SND_SOC_DAPM_EVENT_ON(event)) { + /* Enable Softclip clock and mux */ + wsa_macro_enable_softclip_clk(codec, wsa_priv, softclip_path, + true); + /* Enable Softclip control */ + snd_soc_update_bits(codec, softclip_ctrl_reg, 0x01, 0x01); + } + + if (SND_SOC_DAPM_EVENT_OFF(event)) { + snd_soc_update_bits(codec, softclip_ctrl_reg, 0x01, 0x00); + wsa_macro_enable_softclip_clk(codec, wsa_priv, softclip_path, + false); + } + + return 0; +} + +static int wsa_macro_interp_get_primary_reg(u16 reg, u16 *ind) +{ + u16 prim_int_reg = 0; + + switch (reg) { + case BOLERO_CDC_WSA_RX0_RX_PATH_CTL: + case BOLERO_CDC_WSA_RX0_RX_PATH_MIX_CTL: + prim_int_reg = BOLERO_CDC_WSA_RX0_RX_PATH_CTL; + *ind = 0; + break; + case BOLERO_CDC_WSA_RX1_RX_PATH_CTL: + case BOLERO_CDC_WSA_RX1_RX_PATH_MIX_CTL: + prim_int_reg = BOLERO_CDC_WSA_RX1_RX_PATH_CTL; + *ind = 1; + break; + } + + return prim_int_reg; +} + +static int wsa_macro_enable_prim_interpolator( + struct snd_soc_codec *codec, + u16 reg, int event) +{ + u16 prim_int_reg; + u16 ind = 0; + struct device *wsa_dev = NULL; + struct wsa_macro_priv *wsa_priv = NULL; + + if (!wsa_macro_get_data(codec, &wsa_dev, &wsa_priv, __func__)) + return -EINVAL; + + prim_int_reg = wsa_macro_interp_get_primary_reg(reg, &ind); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + wsa_priv->prim_int_users[ind]++; + if (wsa_priv->prim_int_users[ind] == 1) { + snd_soc_update_bits(codec, + prim_int_reg + WSA_MACRO_RX_PATH_CFG3_OFFSET, + 0x03, 0x03); + snd_soc_update_bits(codec, prim_int_reg, + 0x10, 0x10); + wsa_macro_hd2_control(codec, prim_int_reg, event); + snd_soc_update_bits(codec, + prim_int_reg + WSA_MACRO_RX_PATH_DSMDEM_OFFSET, + 0x1, 0x1); + snd_soc_update_bits(codec, prim_int_reg, + 1 << 0x5, 1 << 0x5); + } + if ((reg != prim_int_reg) && + ((snd_soc_read(codec, prim_int_reg)) & 0x10)) + snd_soc_update_bits(codec, reg, 0x10, 0x10); + break; + case SND_SOC_DAPM_POST_PMD: + wsa_priv->prim_int_users[ind]--; + if (wsa_priv->prim_int_users[ind] == 0) { + snd_soc_update_bits(codec, prim_int_reg, + 1 << 0x5, 0 << 0x5); + snd_soc_update_bits(codec, prim_int_reg, + 0x40, 0x40); + snd_soc_update_bits(codec, prim_int_reg, + 0x40, 0x00); + wsa_macro_hd2_control(codec, prim_int_reg, event); + } + break; + } + + dev_dbg(codec->dev, "%s: primary interpolator: INT%d, users: %d\n", + __func__, ind, wsa_priv->prim_int_users[ind]); + return 0; +} + +static int wsa_macro_enable_interpolator(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + u16 gain_reg; + u16 reg; + int val; + int offset_val = 0; + struct device *wsa_dev = NULL; + struct wsa_macro_priv *wsa_priv = NULL; + + if (!wsa_macro_get_data(codec, &wsa_dev, &wsa_priv, __func__)) + return -EINVAL; + + dev_dbg(codec->dev, "%s %d %s\n", __func__, event, w->name); + + if (!(strcmp(w->name, "WSA_RX INT0 INTERP"))) { + reg = BOLERO_CDC_WSA_RX0_RX_PATH_CTL; + gain_reg = BOLERO_CDC_WSA_RX0_RX_VOL_CTL; + } else if (!(strcmp(w->name, "WSA_RX INT1 INTERP"))) { + reg = BOLERO_CDC_WSA_RX1_RX_PATH_CTL; + gain_reg = BOLERO_CDC_WSA_RX1_RX_VOL_CTL; + } else { + dev_err(codec->dev, "%s: Interpolator reg not found\n", + __func__); + return -EINVAL; + } + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* Reset if needed */ + wsa_macro_enable_prim_interpolator(codec, reg, event); + break; + case SND_SOC_DAPM_POST_PMU: + wsa_macro_config_compander(codec, w->shift, event); + wsa_macro_config_softclip(codec, w->shift, event); + /* apply gain after int clk is enabled */ + if ((wsa_priv->spkr_gain_offset == + WSA_MACRO_GAIN_OFFSET_M1P5_DB) && + (wsa_priv->comp_enabled[WSA_MACRO_COMP1] || + wsa_priv->comp_enabled[WSA_MACRO_COMP2]) && + (gain_reg == BOLERO_CDC_WSA_RX0_RX_VOL_CTL || + gain_reg == BOLERO_CDC_WSA_RX1_RX_VOL_CTL)) { + snd_soc_update_bits(codec, BOLERO_CDC_WSA_RX0_RX_PATH_SEC1, + 0x01, 0x01); + snd_soc_update_bits(codec, + BOLERO_CDC_WSA_RX0_RX_PATH_MIX_SEC0, + 0x01, 0x01); + snd_soc_update_bits(codec, BOLERO_CDC_WSA_RX1_RX_PATH_SEC1, + 0x01, 0x01); + snd_soc_update_bits(codec, + BOLERO_CDC_WSA_RX1_RX_PATH_MIX_SEC0, + 0x01, 0x01); + offset_val = -2; + } + val = snd_soc_read(codec, gain_reg); + val += offset_val; + snd_soc_write(codec, gain_reg, val); + wsa_macro_config_ear_spkr_gain(codec, wsa_priv, + event, gain_reg); + break; + case SND_SOC_DAPM_POST_PMD: + wsa_macro_config_compander(codec, w->shift, event); + wsa_macro_config_softclip(codec, w->shift, event); + wsa_macro_enable_prim_interpolator(codec, reg, event); + if ((wsa_priv->spkr_gain_offset == + WSA_MACRO_GAIN_OFFSET_M1P5_DB) && + (wsa_priv->comp_enabled[WSA_MACRO_COMP1] || + wsa_priv->comp_enabled[WSA_MACRO_COMP2]) && + (gain_reg == BOLERO_CDC_WSA_RX0_RX_VOL_CTL || + gain_reg == BOLERO_CDC_WSA_RX1_RX_VOL_CTL)) { + snd_soc_update_bits(codec, BOLERO_CDC_WSA_RX0_RX_PATH_SEC1, + 0x01, 0x00); + snd_soc_update_bits(codec, + BOLERO_CDC_WSA_RX0_RX_PATH_MIX_SEC0, + 0x01, 0x00); + snd_soc_update_bits(codec, BOLERO_CDC_WSA_RX1_RX_PATH_SEC1, + 0x01, 0x00); + snd_soc_update_bits(codec, + BOLERO_CDC_WSA_RX1_RX_PATH_MIX_SEC0, + 0x01, 0x00); + offset_val = 2; + val = snd_soc_read(codec, gain_reg); + val += offset_val; + snd_soc_write(codec, gain_reg, val); + } + wsa_macro_config_ear_spkr_gain(codec, wsa_priv, + event, gain_reg); + break; + } + + return 0; +} + +static int wsa_macro_config_ear_spkr_gain(struct snd_soc_codec *codec, + struct wsa_macro_priv *wsa_priv, + int event, int gain_reg) +{ + int comp_gain_offset, val; + + switch (wsa_priv->spkr_mode) { + /* Compander gain in WSA_MACRO_SPKR_MODE1 case is 12 dB */ + case WSA_MACRO_SPKR_MODE_1: + comp_gain_offset = -12; + break; + /* Default case compander gain is 15 dB */ + default: + comp_gain_offset = -15; + break; + } + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + /* Apply ear spkr gain only if compander is enabled */ + if (wsa_priv->comp_enabled[WSA_MACRO_COMP1] && + (gain_reg == BOLERO_CDC_WSA_RX0_RX_VOL_CTL) && + (wsa_priv->ear_spkr_gain != 0)) { + /* For example, val is -8(-12+5-1) for 4dB of gain */ + val = comp_gain_offset + wsa_priv->ear_spkr_gain - 1; + snd_soc_write(codec, gain_reg, val); + + dev_dbg(wsa_priv->dev, "%s: RX0 Volume %d dB\n", + __func__, val); + } + break; + case SND_SOC_DAPM_POST_PMD: + /* + * Reset RX0 volume to 0 dB if compander is enabled and + * ear_spkr_gain is non-zero. + */ + if (wsa_priv->comp_enabled[WSA_MACRO_COMP1] && + (gain_reg == BOLERO_CDC_WSA_RX0_RX_VOL_CTL) && + (wsa_priv->ear_spkr_gain != 0)) { + snd_soc_write(codec, gain_reg, 0x0); + + dev_dbg(wsa_priv->dev, "%s: Reset RX0 Volume to 0 dB\n", + __func__); + } + break; + } + + return 0; +} + +static int wsa_macro_spk_boost_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + u16 boost_path_ctl, boost_path_cfg1; + u16 reg, reg_mix; + + dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event); + + if (!strcmp(w->name, "WSA_RX INT0 CHAIN")) { + boost_path_ctl = BOLERO_CDC_WSA_BOOST0_BOOST_PATH_CTL; + boost_path_cfg1 = BOLERO_CDC_WSA_RX0_RX_PATH_CFG1; + reg = BOLERO_CDC_WSA_RX0_RX_PATH_CTL; + reg_mix = BOLERO_CDC_WSA_RX0_RX_PATH_MIX_CTL; + } else if (!strcmp(w->name, "WSA_RX INT1 CHAIN")) { + boost_path_ctl = BOLERO_CDC_WSA_BOOST1_BOOST_PATH_CTL; + boost_path_cfg1 = BOLERO_CDC_WSA_RX1_RX_PATH_CFG1; + reg = BOLERO_CDC_WSA_RX1_RX_PATH_CTL; + reg_mix = BOLERO_CDC_WSA_RX1_RX_PATH_MIX_CTL; + } else { + dev_err(codec->dev, "%s: unknown widget: %s\n", + __func__, w->name); + return -EINVAL; + } + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_update_bits(codec, boost_path_cfg1, 0x01, 0x01); + snd_soc_update_bits(codec, boost_path_ctl, 0x10, 0x10); + if ((snd_soc_read(codec, reg_mix)) & 0x10) + snd_soc_update_bits(codec, reg_mix, 0x10, 0x00); + break; + case SND_SOC_DAPM_POST_PMU: + snd_soc_update_bits(codec, reg, 0x10, 0x00); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_update_bits(codec, boost_path_ctl, 0x10, 0x00); + snd_soc_update_bits(codec, boost_path_cfg1, 0x01, 0x00); + break; + } + + return 0; +} + + +static int wsa_macro_enable_vbat(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct device *wsa_dev = NULL; + struct wsa_macro_priv *wsa_priv = NULL; + u16 vbat_path_cfg = 0; + int softclip_path = 0; + + if (!wsa_macro_get_data(codec, &wsa_dev, &wsa_priv, __func__)) + return -EINVAL; + + dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event); + if (!strcmp(w->name, "WSA_RX INT0 VBAT")) { + vbat_path_cfg = BOLERO_CDC_WSA_RX0_RX_PATH_CFG1; + softclip_path = WSA_MACRO_SOFTCLIP0; + } else if (!strcmp(w->name, "WSA_RX INT1 VBAT")) { + vbat_path_cfg = BOLERO_CDC_WSA_RX1_RX_PATH_CFG1; + softclip_path = WSA_MACRO_SOFTCLIP1; + } + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* Enable clock for VBAT block */ + snd_soc_update_bits(codec, + BOLERO_CDC_WSA_VBAT_BCL_VBAT_PATH_CTL, 0x10, 0x10); + /* Enable VBAT block */ + snd_soc_update_bits(codec, + BOLERO_CDC_WSA_VBAT_BCL_VBAT_CFG, 0x01, 0x01); + /* Update interpolator with 384K path */ + snd_soc_update_bits(codec, vbat_path_cfg, 0x80, 0x80); + /* Use attenuation mode */ + snd_soc_update_bits(codec, BOLERO_CDC_WSA_VBAT_BCL_VBAT_CFG, + 0x02, 0x00); + /* + * BCL block needs softclip clock and mux config to be enabled + */ + wsa_macro_enable_softclip_clk(codec, wsa_priv, softclip_path, + true); + /* Enable VBAT at channel level */ + snd_soc_update_bits(codec, vbat_path_cfg, 0x02, 0x02); + /* Set the ATTK1 gain */ + snd_soc_update_bits(codec, + BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_GAIN_UPD1, + 0xFF, 0xFF); + snd_soc_update_bits(codec, + BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_GAIN_UPD2, + 0xFF, 0x03); + snd_soc_update_bits(codec, + BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_GAIN_UPD3, + 0xFF, 0x00); + /* Set the ATTK2 gain */ + snd_soc_update_bits(codec, + BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_GAIN_UPD4, + 0xFF, 0xFF); + snd_soc_update_bits(codec, + BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_GAIN_UPD5, + 0xFF, 0x03); + snd_soc_update_bits(codec, + BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_GAIN_UPD6, + 0xFF, 0x00); + /* Set the ATTK3 gain */ + snd_soc_update_bits(codec, + BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_GAIN_UPD7, + 0xFF, 0xFF); + snd_soc_update_bits(codec, + BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_GAIN_UPD8, + 0xFF, 0x03); + snd_soc_update_bits(codec, + BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_GAIN_UPD9, + 0xFF, 0x00); + break; + + case SND_SOC_DAPM_POST_PMD: + snd_soc_update_bits(codec, vbat_path_cfg, 0x80, 0x00); + snd_soc_update_bits(codec, BOLERO_CDC_WSA_VBAT_BCL_VBAT_CFG, + 0x02, 0x02); + snd_soc_update_bits(codec, vbat_path_cfg, 0x02, 0x00); + snd_soc_update_bits(codec, + BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_GAIN_UPD1, + 0xFF, 0x00); + snd_soc_update_bits(codec, + BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_GAIN_UPD2, + 0xFF, 0x00); + snd_soc_update_bits(codec, + BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_GAIN_UPD3, + 0xFF, 0x00); + snd_soc_update_bits(codec, + BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_GAIN_UPD4, + 0xFF, 0x00); + snd_soc_update_bits(codec, + BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_GAIN_UPD5, + 0xFF, 0x00); + snd_soc_update_bits(codec, + BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_GAIN_UPD6, + 0xFF, 0x00); + snd_soc_update_bits(codec, + BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_GAIN_UPD7, + 0xFF, 0x00); + snd_soc_update_bits(codec, + BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_GAIN_UPD8, + 0xFF, 0x00); + snd_soc_update_bits(codec, + BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_GAIN_UPD9, + 0xFF, 0x00); + wsa_macro_enable_softclip_clk(codec, wsa_priv, softclip_path, + false); + snd_soc_update_bits(codec, + BOLERO_CDC_WSA_VBAT_BCL_VBAT_CFG, 0x01, 0x00); + snd_soc_update_bits(codec, + BOLERO_CDC_WSA_VBAT_BCL_VBAT_PATH_CTL, 0x10, 0x00); + break; + default: + dev_err(wsa_dev, "%s: Invalid event %d\n", __func__, event); + break; + } + return 0; +} + +static int wsa_macro_enable_echo(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct device *wsa_dev = NULL; + struct wsa_macro_priv *wsa_priv = NULL; + u16 val, ec_tx = 0, ec_hq_reg; + + if (!wsa_macro_get_data(codec, &wsa_dev, &wsa_priv, __func__)) + return -EINVAL; + + dev_dbg(wsa_dev, "%s %d %s\n", __func__, event, w->name); + + val = snd_soc_read(codec, BOLERO_CDC_WSA_RX_INP_MUX_RX_MIX_CFG0); + if (!(strcmp(w->name, "WSA RX_MIX EC0_MUX"))) + ec_tx = (val & 0x07) - 1; + else + ec_tx = ((val & 0x38) >> 0x3) - 1; + + if (ec_tx < 0 || ec_tx >= (WSA_MACRO_RX1 + 1)) { + dev_err(wsa_dev, "%s: EC mix control not set correctly\n", + __func__); + return -EINVAL; + } + if (wsa_priv->ec_hq[ec_tx]) { + snd_soc_update_bits(codec, + BOLERO_CDC_WSA_RX_INP_MUX_RX_MIX_CFG0, + 0x1 << ec_tx, 0x1 << ec_tx); + ec_hq_reg = BOLERO_CDC_WSA_EC_HQ0_EC_REF_HQ_PATH_CTL + + 0x20 * ec_tx; + snd_soc_update_bits(codec, ec_hq_reg, 0x01, 0x01); + ec_hq_reg = BOLERO_CDC_WSA_EC_HQ0_EC_REF_HQ_CFG0 + + 0x20 * ec_tx; + /* default set to 48k */ + snd_soc_update_bits(codec, ec_hq_reg, 0x1E, 0x08); + } + + return 0; +} + +static int wsa_macro_get_ec_hq(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + int ec_tx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + struct device *wsa_dev = NULL; + struct wsa_macro_priv *wsa_priv = NULL; + + if (!wsa_macro_get_data(codec, &wsa_dev, &wsa_priv, __func__)) + return -EINVAL; + + ucontrol->value.integer.value[0] = wsa_priv->ec_hq[ec_tx]; + return 0; +} + +static int wsa_macro_set_ec_hq(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + int ec_tx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + int value = ucontrol->value.integer.value[0]; + struct device *wsa_dev = NULL; + struct wsa_macro_priv *wsa_priv = NULL; + + if (!wsa_macro_get_data(codec, &wsa_dev, &wsa_priv, __func__)) + return -EINVAL; + + dev_dbg(wsa_dev, "%s: enable current %d, new %d\n", + __func__, wsa_priv->ec_hq[ec_tx], value); + wsa_priv->ec_hq[ec_tx] = value; + + return 0; +} + +static int wsa_macro_get_compander(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + int comp = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + struct device *wsa_dev = NULL; + struct wsa_macro_priv *wsa_priv = NULL; + + if (!wsa_macro_get_data(codec, &wsa_dev, &wsa_priv, __func__)) + return -EINVAL; + + ucontrol->value.integer.value[0] = wsa_priv->comp_enabled[comp]; + return 0; +} + +static int wsa_macro_set_compander(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + int comp = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + int value = ucontrol->value.integer.value[0]; + struct device *wsa_dev = NULL; + struct wsa_macro_priv *wsa_priv = NULL; + + if (!wsa_macro_get_data(codec, &wsa_dev, &wsa_priv, __func__)) + return -EINVAL; + + dev_dbg(codec->dev, "%s: Compander %d enable current %d, new %d\n", + __func__, comp + 1, wsa_priv->comp_enabled[comp], value); + wsa_priv->comp_enabled[comp] = value; + + return 0; +} + +static int wsa_macro_ear_spkr_pa_gain_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct device *wsa_dev = NULL; + struct wsa_macro_priv *wsa_priv = NULL; + + if (!wsa_macro_get_data(codec, &wsa_dev, &wsa_priv, __func__)) + return -EINVAL; + + ucontrol->value.integer.value[0] = wsa_priv->ear_spkr_gain; + + dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n", + __func__, ucontrol->value.integer.value[0]); + + return 0; +} + +static int wsa_macro_ear_spkr_pa_gain_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct device *wsa_dev = NULL; + struct wsa_macro_priv *wsa_priv = NULL; + + if (!wsa_macro_get_data(codec, &wsa_dev, &wsa_priv, __func__)) + return -EINVAL; + + wsa_priv->ear_spkr_gain = ucontrol->value.integer.value[0]; + + dev_dbg(codec->dev, "%s: gain = %d\n", __func__, + wsa_priv->ear_spkr_gain); + + return 0; +} + +static int wsa_macro_spkr_left_boost_stage_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u8 bst_state_max = 0; + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + + bst_state_max = snd_soc_read(codec, BOLERO_CDC_WSA_BOOST0_BOOST_CTL); + bst_state_max = (bst_state_max & 0x0c) >> 2; + ucontrol->value.integer.value[0] = bst_state_max; + dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n", + __func__, ucontrol->value.integer.value[0]); + + return 0; +} + +static int wsa_macro_spkr_left_boost_stage_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u8 bst_state_max; + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + + dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n", + __func__, ucontrol->value.integer.value[0]); + bst_state_max = ucontrol->value.integer.value[0] << 2; + snd_soc_update_bits(codec, BOLERO_CDC_WSA_BOOST0_BOOST_CTL, + 0x0c, bst_state_max); + + return 0; +} + +static int wsa_macro_spkr_right_boost_stage_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u8 bst_state_max = 0; + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + + bst_state_max = snd_soc_read(codec, BOLERO_CDC_WSA_BOOST1_BOOST_CTL); + bst_state_max = (bst_state_max & 0x0c) >> 2; + ucontrol->value.integer.value[0] = bst_state_max; + dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n", + __func__, ucontrol->value.integer.value[0]); + + return 0; +} + +static int wsa_macro_spkr_right_boost_stage_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u8 bst_state_max; + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + + dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n", + __func__, ucontrol->value.integer.value[0]); + bst_state_max = ucontrol->value.integer.value[0] << 2; + snd_soc_update_bits(codec, BOLERO_CDC_WSA_BOOST1_BOOST_CTL, + 0x0c, bst_state_max); + + return 0; +} + +static int wsa_macro_rx_mux_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm); + struct device *wsa_dev = NULL; + struct wsa_macro_priv *wsa_priv = NULL; + + if (!wsa_macro_get_data(codec, &wsa_dev, &wsa_priv, __func__)) + return -EINVAL; + + ucontrol->value.integer.value[0] = + wsa_priv->rx_port_value[widget->shift]; + return 0; +} + +static int wsa_macro_rx_mux_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + struct snd_soc_dapm_update *update = NULL; + u32 rx_port_value = ucontrol->value.integer.value[0]; + u32 bit_input = 0; + u32 aif_rst; + struct device *wsa_dev = NULL; + struct wsa_macro_priv *wsa_priv = NULL; + + if (!wsa_macro_get_data(codec, &wsa_dev, &wsa_priv, __func__)) + return -EINVAL; + + aif_rst = wsa_priv->rx_port_value[widget->shift]; + if (!rx_port_value) { + if (aif_rst == 0) { + dev_err(wsa_dev, "%s: AIF reset already\n", __func__); + return 0; + } + } + wsa_priv->rx_port_value[widget->shift] = rx_port_value; + + bit_input = widget->shift; + if (widget->shift >= WSA_MACRO_RX_MIX) + bit_input %= WSA_MACRO_RX_MIX; + + switch (rx_port_value) { + case 0: + clear_bit(bit_input, + &wsa_priv->active_ch_mask[aif_rst]); + wsa_priv->active_ch_cnt[aif_rst]--; + break; + case 1: + case 2: + set_bit(bit_input, + &wsa_priv->active_ch_mask[rx_port_value]); + wsa_priv->active_ch_cnt[rx_port_value]++; + break; + default: + dev_err(wsa_dev, + "%s: Invalid AIF_ID for WSA RX MUX\n", __func__); + return -EINVAL; + } + + snd_soc_dapm_mux_update_power(widget->dapm, kcontrol, + rx_port_value, e, update); + return 0; +} + +static int wsa_macro_vbat_bcl_gsm_mode_func_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + + ucontrol->value.integer.value[0] = + ((snd_soc_read(codec, BOLERO_CDC_WSA_VBAT_BCL_VBAT_CFG) & 0x04) ? + 1 : 0); + + dev_dbg(codec->dev, "%s: value: %lu\n", __func__, + ucontrol->value.integer.value[0]); + + return 0; +} + +static int wsa_macro_vbat_bcl_gsm_mode_func_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + + dev_dbg(codec->dev, "%s: value: %lu\n", __func__, + ucontrol->value.integer.value[0]); + + /* Set Vbat register configuration for GSM mode bit based on value */ + if (ucontrol->value.integer.value[0]) + snd_soc_update_bits(codec, BOLERO_CDC_WSA_VBAT_BCL_VBAT_CFG, + 0x04, 0x04); + else + snd_soc_update_bits(codec, BOLERO_CDC_WSA_VBAT_BCL_VBAT_CFG, + 0x04, 0x00); + + return 0; +} + +static int wsa_macro_soft_clip_enable_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct device *wsa_dev = NULL; + struct wsa_macro_priv *wsa_priv = NULL; + int path = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + + if (!wsa_macro_get_data(codec, &wsa_dev, &wsa_priv, __func__)) + return -EINVAL; + + ucontrol->value.integer.value[0] = wsa_priv->is_softclip_on[path]; + + dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n", + __func__, ucontrol->value.integer.value[0]); + + return 0; +} + +static int wsa_macro_soft_clip_enable_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct device *wsa_dev = NULL; + struct wsa_macro_priv *wsa_priv = NULL; + int path = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + + if (!wsa_macro_get_data(codec, &wsa_dev, &wsa_priv, __func__)) + return -EINVAL; + + wsa_priv->is_softclip_on[path] = ucontrol->value.integer.value[0]; + + dev_dbg(codec->dev, "%s: soft clip enable for %d: %d\n", __func__, + path, wsa_priv->is_softclip_on[path]); + + return 0; +} + +static const struct snd_kcontrol_new wsa_macro_snd_controls[] = { + SOC_ENUM_EXT("EAR SPKR PA Gain", wsa_macro_ear_spkr_pa_gain_enum, + wsa_macro_ear_spkr_pa_gain_get, + wsa_macro_ear_spkr_pa_gain_put), + SOC_ENUM_EXT("SPKR Left Boost Max State", + wsa_macro_spkr_boost_stage_enum, + wsa_macro_spkr_left_boost_stage_get, + wsa_macro_spkr_left_boost_stage_put), + SOC_ENUM_EXT("SPKR Right Boost Max State", + wsa_macro_spkr_boost_stage_enum, + wsa_macro_spkr_right_boost_stage_get, + wsa_macro_spkr_right_boost_stage_put), + SOC_ENUM_EXT("GSM mode Enable", wsa_macro_vbat_bcl_gsm_mode_enum, + wsa_macro_vbat_bcl_gsm_mode_func_get, + wsa_macro_vbat_bcl_gsm_mode_func_put), + SOC_SINGLE_EXT("WSA_Softclip0 Enable", SND_SOC_NOPM, + WSA_MACRO_SOFTCLIP0, 1, 0, + wsa_macro_soft_clip_enable_get, + wsa_macro_soft_clip_enable_put), + SOC_SINGLE_EXT("WSA_Softclip1 Enable", SND_SOC_NOPM, + WSA_MACRO_SOFTCLIP1, 1, 0, + wsa_macro_soft_clip_enable_get, + wsa_macro_soft_clip_enable_put), + SOC_SINGLE_SX_TLV("WSA_RX0 Digital Volume", + BOLERO_CDC_WSA_RX0_RX_VOL_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("WSA_RX1 Digital Volume", + BOLERO_CDC_WSA_RX1_RX_VOL_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_EXT("WSA_COMP1 Switch", SND_SOC_NOPM, WSA_MACRO_COMP1, 1, 0, + wsa_macro_get_compander, wsa_macro_set_compander), + SOC_SINGLE_EXT("WSA_COMP2 Switch", SND_SOC_NOPM, WSA_MACRO_COMP2, 1, 0, + wsa_macro_get_compander, wsa_macro_set_compander), + SOC_SINGLE_EXT("WSA_RX0 EC_HQ Switch", SND_SOC_NOPM, WSA_MACRO_RX0, + 1, 0, wsa_macro_get_ec_hq, wsa_macro_set_ec_hq), + SOC_SINGLE_EXT("WSA_RX1 EC_HQ Switch", SND_SOC_NOPM, WSA_MACRO_RX1, + 1, 0, wsa_macro_get_ec_hq, wsa_macro_set_ec_hq), +}; + +static const struct soc_enum rx_mux_enum = + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(rx_mux_text), rx_mux_text); + +static const struct snd_kcontrol_new rx_mux[WSA_MACRO_RX_MAX] = { + SOC_DAPM_ENUM_EXT("WSA RX0 Mux", rx_mux_enum, + wsa_macro_rx_mux_get, wsa_macro_rx_mux_put), + SOC_DAPM_ENUM_EXT("WSA RX1 Mux", rx_mux_enum, + wsa_macro_rx_mux_get, wsa_macro_rx_mux_put), + SOC_DAPM_ENUM_EXT("WSA RX_MIX0 Mux", rx_mux_enum, + wsa_macro_rx_mux_get, wsa_macro_rx_mux_put), + SOC_DAPM_ENUM_EXT("WSA RX_MIX1 Mux", rx_mux_enum, + wsa_macro_rx_mux_get, wsa_macro_rx_mux_put), +}; + +static int wsa_macro_vi_feed_mixer_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm); + struct soc_multi_mixer_control *mixer = + ((struct soc_multi_mixer_control *)kcontrol->private_value); + u32 dai_id = widget->shift; + u32 spk_tx_id = mixer->shift; + struct device *wsa_dev = NULL; + struct wsa_macro_priv *wsa_priv = NULL; + + if (!wsa_macro_get_data(codec, &wsa_dev, &wsa_priv, __func__)) + return -EINVAL; + + if (test_bit(spk_tx_id, &wsa_priv->active_ch_mask[dai_id])) + ucontrol->value.integer.value[0] = 1; + else + ucontrol->value.integer.value[0] = 0; + + return 0; +} + +static int wsa_macro_vi_feed_mixer_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm); + struct soc_multi_mixer_control *mixer = + ((struct soc_multi_mixer_control *)kcontrol->private_value); + u32 spk_tx_id = mixer->shift; + u32 enable = ucontrol->value.integer.value[0]; + struct device *wsa_dev = NULL; + struct wsa_macro_priv *wsa_priv = NULL; + + if (!wsa_macro_get_data(codec, &wsa_dev, &wsa_priv, __func__)) + return -EINVAL; + + wsa_priv->vi_feed_value = ucontrol->value.integer.value[0]; + + if (enable) { + if (spk_tx_id == WSA_MACRO_TX0 && + !test_bit(WSA_MACRO_TX0, + &wsa_priv->active_ch_mask[WSA_MACRO_AIF_VI])) { + set_bit(WSA_MACRO_TX0, + &wsa_priv->active_ch_mask[WSA_MACRO_AIF_VI]); + wsa_priv->active_ch_cnt[WSA_MACRO_AIF_VI]++; + } + if (spk_tx_id == WSA_MACRO_TX1 && + !test_bit(WSA_MACRO_TX1, + &wsa_priv->active_ch_mask[WSA_MACRO_AIF_VI])) { + set_bit(WSA_MACRO_TX1, + &wsa_priv->active_ch_mask[WSA_MACRO_AIF_VI]); + wsa_priv->active_ch_cnt[WSA_MACRO_AIF_VI]++; + } + } else { + if (spk_tx_id == WSA_MACRO_TX0 && + test_bit(WSA_MACRO_TX0, + &wsa_priv->active_ch_mask[WSA_MACRO_AIF_VI])) { + clear_bit(WSA_MACRO_TX0, + &wsa_priv->active_ch_mask[WSA_MACRO_AIF_VI]); + wsa_priv->active_ch_cnt[WSA_MACRO_AIF_VI]--; + } + if (spk_tx_id == WSA_MACRO_TX1 && + test_bit(WSA_MACRO_TX1, + &wsa_priv->active_ch_mask[WSA_MACRO_AIF_VI])) { + clear_bit(WSA_MACRO_TX1, + &wsa_priv->active_ch_mask[WSA_MACRO_AIF_VI]); + wsa_priv->active_ch_cnt[WSA_MACRO_AIF_VI]--; + } + } + snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, enable, NULL); + + return 0; +} + +static const struct snd_kcontrol_new aif_vi_mixer[] = { + SOC_SINGLE_EXT("WSA_SPKR_VI_1", SND_SOC_NOPM, WSA_MACRO_TX0, 1, 0, + wsa_macro_vi_feed_mixer_get, + wsa_macro_vi_feed_mixer_put), + SOC_SINGLE_EXT("WSA_SPKR_VI_2", SND_SOC_NOPM, WSA_MACRO_TX1, 1, 0, + wsa_macro_vi_feed_mixer_get, + wsa_macro_vi_feed_mixer_put), +}; + +static const struct snd_soc_dapm_widget wsa_macro_dapm_widgets[] = { + SND_SOC_DAPM_AIF_IN("WSA AIF1 PB", "WSA_AIF1 Playback", 0, + SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_AIF_IN("WSA AIF_MIX1 PB", "WSA_AIF_MIX1 Playback", 0, + SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_AIF_OUT_E("WSA AIF_VI", "WSA_AIF_VI Capture", 0, + SND_SOC_NOPM, WSA_MACRO_AIF_VI, 0, + wsa_macro_enable_vi_feedback, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_AIF_OUT("WSA AIF_ECHO", "WSA_AIF_ECHO Capture", 0, + SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_MIXER("WSA_AIF_VI Mixer", SND_SOC_NOPM, WSA_MACRO_AIF_VI, + 0, aif_vi_mixer, ARRAY_SIZE(aif_vi_mixer)), + SND_SOC_DAPM_MUX_E("WSA RX_MIX EC0_MUX", SND_SOC_NOPM, + WSA_MACRO_EC0_MUX, 0, + &rx_mix_ec0_mux, wsa_macro_enable_echo, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("WSA RX_MIX EC1_MUX", SND_SOC_NOPM, + WSA_MACRO_EC1_MUX, 0, + &rx_mix_ec1_mux, wsa_macro_enable_echo, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX("WSA RX0 MUX", SND_SOC_NOPM, WSA_MACRO_RX0, 0, + &rx_mux[WSA_MACRO_RX0]), + SND_SOC_DAPM_MUX("WSA RX1 MUX", SND_SOC_NOPM, WSA_MACRO_RX1, 0, + &rx_mux[WSA_MACRO_RX1]), + SND_SOC_DAPM_MUX("WSA RX_MIX0 MUX", SND_SOC_NOPM, WSA_MACRO_RX_MIX0, 0, + &rx_mux[WSA_MACRO_RX_MIX0]), + SND_SOC_DAPM_MUX("WSA RX_MIX1 MUX", SND_SOC_NOPM, WSA_MACRO_RX_MIX1, 0, + &rx_mux[WSA_MACRO_RX_MIX1]), + + SND_SOC_DAPM_MIXER("WSA RX0", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("WSA RX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("WSA RX_MIX0", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("WSA RX_MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_MUX_E("WSA_RX0 INP0", SND_SOC_NOPM, 0, 0, + &rx0_prim_inp0_mux, wsa_macro_enable_swr, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("WSA_RX0 INP1", SND_SOC_NOPM, 0, 0, + &rx0_prim_inp1_mux, wsa_macro_enable_swr, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("WSA_RX0 INP2", SND_SOC_NOPM, 0, 0, + &rx0_prim_inp2_mux, wsa_macro_enable_swr, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("WSA_RX0 MIX INP", SND_SOC_NOPM, 0, 0, + &rx0_mix_mux, wsa_macro_enable_mix_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("WSA_RX1 INP0", SND_SOC_NOPM, 0, 0, + &rx1_prim_inp0_mux, wsa_macro_enable_swr, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("WSA_RX1 INP1", SND_SOC_NOPM, 0, 0, + &rx1_prim_inp1_mux, wsa_macro_enable_swr, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("WSA_RX1 INP2", SND_SOC_NOPM, 0, 0, + &rx1_prim_inp2_mux, wsa_macro_enable_swr, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("WSA_RX1 MIX INP", SND_SOC_NOPM, 0, 0, + &rx1_mix_mux, wsa_macro_enable_mix_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER("WSA_RX INT0 MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("WSA_RX INT1 MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("WSA_RX INT0 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("WSA_RX INT1 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_MUX_E("WSA_RX0 INT0 SIDETONE MIX", + BOLERO_CDC_WSA_RX0_RX_PATH_CFG1, 4, 0, + &rx0_sidetone_mix_mux, wsa_macro_enable_swr, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_INPUT("WSA SRC0_INP"), + + SND_SOC_DAPM_INPUT("WSA_TX DEC0_INP"), + SND_SOC_DAPM_INPUT("WSA_TX DEC1_INP"), + + SND_SOC_DAPM_MIXER_E("WSA_RX INT0 INTERP", SND_SOC_NOPM, + WSA_MACRO_COMP1, 0, NULL, 0, wsa_macro_enable_interpolator, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("WSA_RX INT1 INTERP", SND_SOC_NOPM, + WSA_MACRO_COMP2, 0, NULL, 0, wsa_macro_enable_interpolator, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MIXER_E("WSA_RX INT0 CHAIN", SND_SOC_NOPM, 0, 0, + NULL, 0, wsa_macro_spk_boost_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("WSA_RX INT1 CHAIN", SND_SOC_NOPM, 0, 0, + NULL, 0, wsa_macro_spk_boost_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MIXER_E("WSA_RX INT0 VBAT", SND_SOC_NOPM, + 0, 0, wsa_int0_vbat_mix_switch, + ARRAY_SIZE(wsa_int0_vbat_mix_switch), + wsa_macro_enable_vbat, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("WSA_RX INT1 VBAT", SND_SOC_NOPM, + 0, 0, wsa_int1_vbat_mix_switch, + ARRAY_SIZE(wsa_int1_vbat_mix_switch), + wsa_macro_enable_vbat, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_INPUT("VIINPUT_WSA"), + + SND_SOC_DAPM_OUTPUT("WSA_SPK1 OUT"), + SND_SOC_DAPM_OUTPUT("WSA_SPK2 OUT"), + + SND_SOC_DAPM_SUPPLY_S("WSA_MCLK", 0, SND_SOC_NOPM, 0, 0, + wsa_macro_mclk_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +}; + +static const struct snd_soc_dapm_route wsa_audio_map[] = { + /* VI Feedback */ + {"WSA_AIF_VI Mixer", "WSA_SPKR_VI_1", "VIINPUT_WSA"}, + {"WSA_AIF_VI Mixer", "WSA_SPKR_VI_2", "VIINPUT_WSA"}, + {"WSA AIF_VI", NULL, "WSA_AIF_VI Mixer"}, + {"WSA AIF_VI", NULL, "WSA_MCLK"}, + + {"WSA RX_MIX EC0_MUX", "RX_MIX_TX0", "WSA_RX INT0 SEC MIX"}, + {"WSA RX_MIX EC1_MUX", "RX_MIX_TX0", "WSA_RX INT0 SEC MIX"}, + {"WSA RX_MIX EC0_MUX", "RX_MIX_TX1", "WSA_RX INT1 SEC MIX"}, + {"WSA RX_MIX EC1_MUX", "RX_MIX_TX1", "WSA_RX INT1 SEC MIX"}, + {"WSA AIF_ECHO", NULL, "WSA RX_MIX EC0_MUX"}, + {"WSA AIF_ECHO", NULL, "WSA RX_MIX EC1_MUX"}, + {"WSA AIF_ECHO", NULL, "WSA_MCLK"}, + + {"WSA AIF1 PB", NULL, "WSA_MCLK"}, + {"WSA AIF_MIX1 PB", NULL, "WSA_MCLK"}, + + {"WSA RX0 MUX", "AIF1_PB", "WSA AIF1 PB"}, + {"WSA RX1 MUX", "AIF1_PB", "WSA AIF1 PB"}, + {"WSA RX_MIX0 MUX", "AIF1_PB", "WSA AIF1 PB"}, + {"WSA RX_MIX1 MUX", "AIF1_PB", "WSA AIF1 PB"}, + + {"WSA RX0 MUX", "AIF_MIX1_PB", "WSA AIF_MIX1 PB"}, + {"WSA RX1 MUX", "AIF_MIX1_PB", "WSA AIF_MIX1 PB"}, + {"WSA RX_MIX0 MUX", "AIF_MIX1_PB", "WSA AIF_MIX1 PB"}, + {"WSA RX_MIX1 MUX", "AIF_MIX1_PB", "WSA AIF_MIX1 PB"}, + + {"WSA RX0", NULL, "WSA RX0 MUX"}, + {"WSA RX1", NULL, "WSA RX1 MUX"}, + {"WSA RX_MIX0", NULL, "WSA RX_MIX0 MUX"}, + {"WSA RX_MIX1", NULL, "WSA RX_MIX1 MUX"}, + + {"WSA_RX0 INP0", "RX0", "WSA RX0"}, + {"WSA_RX0 INP0", "RX1", "WSA RX1"}, + {"WSA_RX0 INP0", "RX_MIX0", "WSA RX_MIX0"}, + {"WSA_RX0 INP0", "RX_MIX1", "WSA RX_MIX1"}, + {"WSA_RX0 INP0", "DEC0", "WSA_TX DEC0_INP"}, + {"WSA_RX0 INP0", "DEC1", "WSA_TX DEC1_INP"}, + {"WSA_RX INT0 MIX", NULL, "WSA_RX0 INP0"}, + + {"WSA_RX0 INP1", "RX0", "WSA RX0"}, + {"WSA_RX0 INP1", "RX1", "WSA RX1"}, + {"WSA_RX0 INP1", "RX_MIX0", "WSA RX_MIX0"}, + {"WSA_RX0 INP1", "RX_MIX1", "WSA RX_MIX1"}, + {"WSA_RX0 INP1", "DEC0", "WSA_TX DEC0_INP"}, + {"WSA_RX0 INP1", "DEC1", "WSA_TX DEC1_INP"}, + {"WSA_RX INT0 MIX", NULL, "WSA_RX0 INP1"}, + + {"WSA_RX0 INP2", "RX0", "WSA RX0"}, + {"WSA_RX0 INP2", "RX1", "WSA RX1"}, + {"WSA_RX0 INP2", "RX_MIX0", "WSA RX_MIX0"}, + {"WSA_RX0 INP2", "RX_MIX1", "WSA RX_MIX1"}, + {"WSA_RX0 INP2", "DEC0", "WSA_TX DEC0_INP"}, + {"WSA_RX0 INP2", "DEC1", "WSA_TX DEC1_INP"}, + {"WSA_RX INT0 MIX", NULL, "WSA_RX0 INP2"}, + + {"WSA_RX0 MIX INP", "RX0", "WSA RX0"}, + {"WSA_RX0 MIX INP", "RX1", "WSA RX1"}, + {"WSA_RX0 MIX INP", "RX_MIX0", "WSA RX_MIX0"}, + {"WSA_RX0 MIX INP", "RX_MIX1", "WSA RX_MIX1"}, + {"WSA_RX INT0 SEC MIX", NULL, "WSA_RX0 MIX INP"}, + + {"WSA_RX INT0 SEC MIX", NULL, "WSA_RX INT0 MIX"}, + {"WSA_RX INT0 INTERP", NULL, "WSA_RX INT0 SEC MIX"}, + {"WSA_RX0 INT0 SIDETONE MIX", "SRC0", "WSA SRC0_INP"}, + {"WSA_RX INT0 INTERP", NULL, "WSA_RX0 INT0 SIDETONE MIX"}, + {"WSA_RX INT0 CHAIN", NULL, "WSA_RX INT0 INTERP"}, + + {"WSA_RX INT0 VBAT", "WSA RX0 VBAT Enable", "WSA_RX INT0 INTERP"}, + {"WSA_RX INT0 CHAIN", NULL, "WSA_RX INT0 VBAT"}, + + {"WSA_SPK1 OUT", NULL, "WSA_RX INT0 CHAIN"}, + {"WSA_SPK1 OUT", NULL, "WSA_MCLK"}, + + {"WSA_RX1 INP0", "RX0", "WSA RX0"}, + {"WSA_RX1 INP0", "RX1", "WSA RX1"}, + {"WSA_RX1 INP0", "RX_MIX0", "WSA RX_MIX0"}, + {"WSA_RX1 INP0", "RX_MIX1", "WSA RX_MIX1"}, + {"WSA_RX1 INP0", "DEC0", "WSA_TX DEC0_INP"}, + {"WSA_RX1 INP0", "DEC1", "WSA_TX DEC1_INP"}, + {"WSA_RX INT1 MIX", NULL, "WSA_RX1 INP0"}, + + {"WSA_RX1 INP1", "RX0", "WSA RX0"}, + {"WSA_RX1 INP1", "RX1", "WSA RX1"}, + {"WSA_RX1 INP1", "RX_MIX0", "WSA RX_MIX0"}, + {"WSA_RX1 INP1", "RX_MIX1", "WSA RX_MIX1"}, + {"WSA_RX1 INP1", "DEC0", "WSA_TX DEC0_INP"}, + {"WSA_RX1 INP1", "DEC1", "WSA_TX DEC1_INP"}, + {"WSA_RX INT1 MIX", NULL, "WSA_RX1 INP1"}, + + {"WSA_RX1 INP2", "RX0", "WSA RX0"}, + {"WSA_RX1 INP2", "RX1", "WSA RX1"}, + {"WSA_RX1 INP2", "RX_MIX0", "WSA RX_MIX0"}, + {"WSA_RX1 INP2", "RX_MIX1", "WSA RX_MIX1"}, + {"WSA_RX1 INP2", "DEC0", "WSA_TX DEC0_INP"}, + {"WSA_RX1 INP2", "DEC1", "WSA_TX DEC1_INP"}, + {"WSA_RX INT1 MIX", NULL, "WSA_RX1 INP2"}, + + {"WSA_RX1 MIX INP", "RX0", "WSA RX0"}, + {"WSA_RX1 MIX INP", "RX1", "WSA RX1"}, + {"WSA_RX1 MIX INP", "RX_MIX0", "WSA RX_MIX0"}, + {"WSA_RX1 MIX INP", "RX_MIX1", "WSA RX_MIX1"}, + {"WSA_RX INT1 SEC MIX", NULL, "WSA_RX1 MIX INP"}, + + {"WSA_RX INT1 SEC MIX", NULL, "WSA_RX INT1 MIX"}, + {"WSA_RX INT1 INTERP", NULL, "WSA_RX INT1 SEC MIX"}, + + {"WSA_RX INT1 VBAT", "WSA RX1 VBAT Enable", "WSA_RX INT1 INTERP"}, + {"WSA_RX INT1 CHAIN", NULL, "WSA_RX INT1 VBAT"}, + + {"WSA_RX INT1 CHAIN", NULL, "WSA_RX INT1 INTERP"}, + {"WSA_SPK2 OUT", NULL, "WSA_RX INT1 CHAIN"}, + {"WSA_SPK2 OUT", NULL, "WSA_MCLK"}, +}; + +static const struct wsa_macro_reg_mask_val wsa_macro_reg_init[] = { + {BOLERO_CDC_WSA_BOOST0_BOOST_CFG1, 0x3F, 0x12}, + {BOLERO_CDC_WSA_BOOST0_BOOST_CFG2, 0x1C, 0x08}, + {BOLERO_CDC_WSA_COMPANDER0_CTL7, 0x1E, 0x18}, + {BOLERO_CDC_WSA_BOOST1_BOOST_CFG1, 0x3F, 0x12}, + {BOLERO_CDC_WSA_BOOST1_BOOST_CFG2, 0x1C, 0x08}, + {BOLERO_CDC_WSA_COMPANDER1_CTL7, 0x1E, 0x18}, + {BOLERO_CDC_WSA_BOOST0_BOOST_CTL, 0x70, 0x58}, + {BOLERO_CDC_WSA_BOOST1_BOOST_CTL, 0x70, 0x58}, + {BOLERO_CDC_WSA_RX0_RX_PATH_CFG1, 0x08, 0x08}, + {BOLERO_CDC_WSA_RX1_RX_PATH_CFG1, 0x08, 0x08}, + {BOLERO_CDC_WSA_TOP_TOP_CFG1, 0x02, 0x02}, + {BOLERO_CDC_WSA_TOP_TOP_CFG1, 0x01, 0x01}, + {BOLERO_CDC_WSA_TX0_SPKR_PROT_PATH_CFG0, 0x01, 0x01}, + {BOLERO_CDC_WSA_TX1_SPKR_PROT_PATH_CFG0, 0x01, 0x01}, + {BOLERO_CDC_WSA_TX2_SPKR_PROT_PATH_CFG0, 0x01, 0x01}, + {BOLERO_CDC_WSA_TX3_SPKR_PROT_PATH_CFG0, 0x01, 0x01}, + {BOLERO_CDC_WSA_COMPANDER0_CTL3, 0x80, 0x80}, + {BOLERO_CDC_WSA_COMPANDER1_CTL3, 0x80, 0x80}, + {BOLERO_CDC_WSA_COMPANDER0_CTL7, 0x01, 0x01}, + {BOLERO_CDC_WSA_COMPANDER1_CTL7, 0x01, 0x01}, + {BOLERO_CDC_WSA_RX0_RX_PATH_CFG0, 0x01, 0x01}, + {BOLERO_CDC_WSA_RX1_RX_PATH_CFG0, 0x01, 0x01}, + {BOLERO_CDC_WSA_RX0_RX_PATH_MIX_CFG, 0x01, 0x01}, + {BOLERO_CDC_WSA_RX1_RX_PATH_MIX_CFG, 0x01, 0x01}, +}; + +static void wsa_macro_init_bcl_pmic_reg(struct snd_soc_codec *codec) +{ + struct device *wsa_dev = NULL; + struct wsa_macro_priv *wsa_priv = NULL; + + if (!codec) { + pr_err("%s: NULL codec pointer!\n", __func__); + return; + } + + if (!wsa_macro_get_data(codec, &wsa_dev, &wsa_priv, __func__)) + return; + + switch (wsa_priv->bcl_pmic_params.id) { + case 0: + /* Enable ID0 to listen to respective PMIC group interrupts */ + snd_soc_update_bits(codec, + BOLERO_CDC_WSA_VBAT_BCL_VBAT_DECODE_CTL1, 0x02, 0x02); + /* Update MC_SID0 */ + snd_soc_update_bits(codec, + BOLERO_CDC_WSA_VBAT_BCL_VBAT_DECODE_CFG1, 0x0F, + wsa_priv->bcl_pmic_params.sid); + /* Update MC_PPID0 */ + snd_soc_update_bits(codec, + BOLERO_CDC_WSA_VBAT_BCL_VBAT_DECODE_CFG2, 0xFF, + wsa_priv->bcl_pmic_params.ppid); + break; + case 1: + /* Enable ID1 to listen to respective PMIC group interrupts */ + snd_soc_update_bits(codec, + BOLERO_CDC_WSA_VBAT_BCL_VBAT_DECODE_CTL1, 0x01, 0x01); + /* Update MC_SID1 */ + snd_soc_update_bits(codec, + BOLERO_CDC_WSA_VBAT_BCL_VBAT_DECODE_CFG3, 0x0F, + wsa_priv->bcl_pmic_params.sid); + /* Update MC_PPID1 */ + snd_soc_update_bits(codec, + BOLERO_CDC_WSA_VBAT_BCL_VBAT_DECODE_CFG4, 0xFF, + wsa_priv->bcl_pmic_params.ppid); + break; + default: + dev_err(wsa_dev, "%s: PMIC ID is invalid %d\n", + __func__, wsa_priv->bcl_pmic_params.id); + break; + } +} + +static void wsa_macro_init_reg(struct snd_soc_codec *codec) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(wsa_macro_reg_init); i++) + snd_soc_update_bits(codec, + wsa_macro_reg_init[i].reg, + wsa_macro_reg_init[i].mask, + wsa_macro_reg_init[i].val); + + wsa_macro_init_bcl_pmic_reg(codec); +} + +static int wsa_swrm_clock(void *handle, bool enable) +{ + struct wsa_macro_priv *wsa_priv = (struct wsa_macro_priv *) handle; + struct regmap *regmap = dev_get_regmap(wsa_priv->dev->parent, NULL); + int ret = 0; + + if (regmap == NULL) { + dev_err(wsa_priv->dev, "%s: regmap is NULL\n", __func__); + return -EINVAL; + } + + mutex_lock(&wsa_priv->swr_clk_lock); + + dev_dbg(wsa_priv->dev, "%s: swrm clock %s\n", + __func__, (enable ? "enable" : "disable")); + if (enable) { + if (wsa_priv->swr_clk_users == 0) { + ret = wsa_macro_mclk_enable(wsa_priv, 1, true); + if (ret < 0) { + dev_err_ratelimited(wsa_priv->dev, + "%s: wsa request clock enable failed\n", + __func__); + goto exit; + } + if (wsa_priv->reset_swr) + regmap_update_bits(regmap, + BOLERO_CDC_WSA_CLK_RST_CTRL_SWR_CONTROL, + 0x02, 0x02); + regmap_update_bits(regmap, + BOLERO_CDC_WSA_CLK_RST_CTRL_SWR_CONTROL, + 0x01, 0x01); + if (wsa_priv->reset_swr) + regmap_update_bits(regmap, + BOLERO_CDC_WSA_CLK_RST_CTRL_SWR_CONTROL, + 0x02, 0x00); + wsa_priv->reset_swr = false; + regmap_update_bits(regmap, + BOLERO_CDC_WSA_CLK_RST_CTRL_SWR_CONTROL, + 0x1C, 0x0C); + msm_cdc_pinctrl_select_active_state( + wsa_priv->wsa_swr_gpio_p); + } + wsa_priv->swr_clk_users++; + } else { + if (wsa_priv->swr_clk_users <= 0) { + dev_err(wsa_priv->dev, "%s: clock already disabled\n", + __func__); + wsa_priv->swr_clk_users = 0; + goto exit; + } + wsa_priv->swr_clk_users--; + if (wsa_priv->swr_clk_users == 0) { + regmap_update_bits(regmap, + BOLERO_CDC_WSA_CLK_RST_CTRL_SWR_CONTROL, + 0x01, 0x00); + msm_cdc_pinctrl_select_sleep_state( + wsa_priv->wsa_swr_gpio_p); + wsa_macro_mclk_enable(wsa_priv, 0, true); + } + } + dev_dbg(wsa_priv->dev, "%s: swrm clock users %d\n", + __func__, wsa_priv->swr_clk_users); +exit: + mutex_unlock(&wsa_priv->swr_clk_lock); + return ret; +} + +static int wsa_macro_init(struct snd_soc_codec *codec) +{ + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + int ret; + struct device *wsa_dev = NULL; + struct wsa_macro_priv *wsa_priv = NULL; + + wsa_dev = bolero_get_device_ptr(codec->dev, WSA_MACRO); + if (!wsa_dev) { + dev_err(codec->dev, + "%s: null device for macro!\n", __func__); + return -EINVAL; + } + wsa_priv = dev_get_drvdata(wsa_dev); + if (!wsa_priv) { + dev_err(codec->dev, + "%s: priv is null for macro!\n", __func__); + return -EINVAL; + } + + ret = snd_soc_dapm_new_controls(dapm, wsa_macro_dapm_widgets, + ARRAY_SIZE(wsa_macro_dapm_widgets)); + if (ret < 0) { + dev_err(wsa_dev, "%s: Failed to add controls\n", __func__); + return ret; + } + + ret = snd_soc_dapm_add_routes(dapm, wsa_audio_map, + ARRAY_SIZE(wsa_audio_map)); + if (ret < 0) { + dev_err(wsa_dev, "%s: Failed to add routes\n", __func__); + return ret; + } + + ret = snd_soc_dapm_new_widgets(dapm->card); + if (ret < 0) { + dev_err(wsa_dev, "%s: Failed to add widgets\n", __func__); + return ret; + } + + ret = snd_soc_add_codec_controls(codec, wsa_macro_snd_controls, + ARRAY_SIZE(wsa_macro_snd_controls)); + if (ret < 0) { + dev_err(wsa_dev, "%s: Failed to add snd_ctls\n", __func__); + return ret; + } + snd_soc_dapm_ignore_suspend(dapm, "WSA_AIF1 Playback"); + snd_soc_dapm_ignore_suspend(dapm, "WSA_AIF_MIX1 Playback"); + snd_soc_dapm_ignore_suspend(dapm, "WSA_AIF_VI Capture"); + snd_soc_dapm_ignore_suspend(dapm, "WSA_AIF_ECHO Capture"); + snd_soc_dapm_ignore_suspend(dapm, "WSA_SPK1 OUT"); + snd_soc_dapm_ignore_suspend(dapm, "WSA_SPK2 OUT"); + snd_soc_dapm_ignore_suspend(dapm, "VIINPUT_WSA"); + snd_soc_dapm_ignore_suspend(dapm, "WSA SRC0_INP"); + snd_soc_dapm_ignore_suspend(dapm, "WSA_TX DEC0_INP"); + snd_soc_dapm_ignore_suspend(dapm, "WSA_TX DEC1_INP"); + snd_soc_dapm_sync(dapm); + + wsa_priv->codec = codec; + wsa_priv->spkr_gain_offset = WSA_MACRO_GAIN_OFFSET_0_DB; + wsa_macro_init_reg(codec); + + return 0; +} + +static int wsa_macro_deinit(struct snd_soc_codec *codec) +{ + struct device *wsa_dev = NULL; + struct wsa_macro_priv *wsa_priv = NULL; + + if (!wsa_macro_get_data(codec, &wsa_dev, &wsa_priv, __func__)) + return -EINVAL; + + wsa_priv->codec = NULL; + + return 0; +} + +static void wsa_macro_add_child_devices(struct work_struct *work) +{ + struct wsa_macro_priv *wsa_priv; + struct platform_device *pdev; + struct device_node *node; + struct wsa_macro_swr_ctrl_data *swr_ctrl_data = NULL, *temp; + int ret; + u16 count = 0, ctrl_num = 0; + struct wsa_macro_swr_ctrl_platform_data *platdata; + char plat_dev_name[WSA_MACRO_SWR_STRING_LEN]; + + wsa_priv = container_of(work, struct wsa_macro_priv, + wsa_macro_add_child_devices_work); + if (!wsa_priv) { + pr_err("%s: Memory for wsa_priv does not exist\n", + __func__); + return; + } + if (!wsa_priv->dev || !wsa_priv->dev->of_node) { + dev_err(wsa_priv->dev, + "%s: DT node for wsa_priv does not exist\n", __func__); + return; + } + + platdata = &wsa_priv->swr_plat_data; + wsa_priv->child_count = 0; + + for_each_available_child_of_node(wsa_priv->dev->of_node, node) { + if (strnstr(node->name, "wsa_swr_master", + strlen("wsa_swr_master")) != NULL) + strlcpy(plat_dev_name, "wsa_swr_ctrl", + (WSA_MACRO_SWR_STRING_LEN - 1)); + else if (strnstr(node->name, "msm_cdc_pinctrl", + strlen("msm_cdc_pinctrl")) != NULL) + strlcpy(plat_dev_name, node->name, + (WSA_MACRO_SWR_STRING_LEN - 1)); + else + continue; + + pdev = platform_device_alloc(plat_dev_name, -1); + if (!pdev) { + dev_err(wsa_priv->dev, "%s: pdev memory alloc failed\n", + __func__); + ret = -ENOMEM; + goto err; + } + pdev->dev.parent = wsa_priv->dev; + pdev->dev.of_node = node; + + if (strnstr(node->name, "wsa_swr_master", + strlen("wsa_swr_master")) != NULL) { + ret = platform_device_add_data(pdev, platdata, + sizeof(*platdata)); + if (ret) { + dev_err(&pdev->dev, + "%s: cannot add plat data ctrl:%d\n", + __func__, ctrl_num); + goto fail_pdev_add; + } + } + + ret = platform_device_add(pdev); + if (ret) { + dev_err(&pdev->dev, + "%s: Cannot add platform device\n", + __func__); + goto fail_pdev_add; + } + + if (!strcmp(node->name, "wsa_swr_master")) { + temp = krealloc(swr_ctrl_data, + (ctrl_num + 1) * sizeof( + struct wsa_macro_swr_ctrl_data), + GFP_KERNEL); + if (!temp) { + dev_err(&pdev->dev, "out of memory\n"); + ret = -ENOMEM; + goto err; + } + swr_ctrl_data = temp; + swr_ctrl_data[ctrl_num].wsa_swr_pdev = pdev; + ctrl_num++; + dev_dbg(&pdev->dev, + "%s: Added soundwire ctrl device(s)\n", + __func__); + wsa_priv->swr_ctrl_data = swr_ctrl_data; + } + if (wsa_priv->child_count < WSA_MACRO_CHILD_DEVICES_MAX) + wsa_priv->pdev_child_devices[ + wsa_priv->child_count++] = pdev; + else + goto err; + } + + return; +fail_pdev_add: + for (count = 0; count < wsa_priv->child_count; count++) + platform_device_put(wsa_priv->pdev_child_devices[count]); +err: + return; +} + +static void wsa_macro_init_ops(struct macro_ops *ops, + char __iomem *wsa_io_base) +{ + memset(ops, 0, sizeof(struct macro_ops)); + ops->init = wsa_macro_init; + ops->exit = wsa_macro_deinit; + ops->io_base = wsa_io_base; + ops->dai_ptr = wsa_macro_dai; + ops->num_dais = ARRAY_SIZE(wsa_macro_dai); + ops->mclk_fn = wsa_macro_mclk_ctrl; + ops->event_handler = wsa_macro_event_handler; +} + +static int wsa_macro_probe(struct platform_device *pdev) +{ + struct macro_ops ops; + struct wsa_macro_priv *wsa_priv; + u32 wsa_base_addr; + char __iomem *wsa_io_base; + int ret = 0; + struct clk *wsa_core_clk, *wsa_npl_clk; + u8 bcl_pmic_params[3]; + + wsa_priv = devm_kzalloc(&pdev->dev, sizeof(struct wsa_macro_priv), + GFP_KERNEL); + if (!wsa_priv) + return -ENOMEM; + + wsa_priv->dev = &pdev->dev; + ret = of_property_read_u32(pdev->dev.of_node, "reg", + &wsa_base_addr); + if (ret) { + dev_err(&pdev->dev, "%s: could not find %s entry in dt\n", + __func__, "reg"); + return ret; + } + wsa_priv->wsa_swr_gpio_p = of_parse_phandle(pdev->dev.of_node, + "qcom,wsa-swr-gpios", 0); + if (!wsa_priv->wsa_swr_gpio_p) { + dev_err(&pdev->dev, "%s: swr_gpios handle not provided!\n", + __func__); + return -EINVAL; + } + wsa_io_base = devm_ioremap(&pdev->dev, + wsa_base_addr, WSA_MACRO_MAX_OFFSET); + if (!wsa_io_base) { + dev_err(&pdev->dev, "%s: ioremap failed\n", __func__); + return -EINVAL; + } + wsa_priv->wsa_io_base = wsa_io_base; + wsa_priv->reset_swr = true; + INIT_WORK(&wsa_priv->wsa_macro_add_child_devices_work, + wsa_macro_add_child_devices); + wsa_priv->swr_plat_data.handle = (void *) wsa_priv; + wsa_priv->swr_plat_data.read = NULL; + wsa_priv->swr_plat_data.write = NULL; + wsa_priv->swr_plat_data.bulk_write = NULL; + wsa_priv->swr_plat_data.clk = wsa_swrm_clock; + wsa_priv->swr_plat_data.handle_irq = NULL; + + /* Register MCLK for wsa macro */ + wsa_core_clk = devm_clk_get(&pdev->dev, "wsa_core_clk"); + if (IS_ERR(wsa_core_clk)) { + ret = PTR_ERR(wsa_core_clk); + dev_err(&pdev->dev, "%s: clk get %s failed\n", + __func__, "wsa_core_clk"); + return ret; + } + wsa_priv->wsa_core_clk = wsa_core_clk; + /* Register npl clk for soundwire */ + wsa_npl_clk = devm_clk_get(&pdev->dev, "wsa_npl_clk"); + if (IS_ERR(wsa_npl_clk)) { + ret = PTR_ERR(wsa_npl_clk); + dev_err(&pdev->dev, "%s: clk get %s failed\n", + __func__, "wsa_npl_clk"); + return ret; + } + wsa_priv->wsa_npl_clk = wsa_npl_clk; + + ret = of_property_read_u8_array(pdev->dev.of_node, + "qcom,wsa-bcl-pmic-params", bcl_pmic_params, + sizeof(bcl_pmic_params)); + if (ret) { + dev_dbg(&pdev->dev, "%s: could not find %s entry in dt\n", + __func__, "qcom,wsa-bcl-pmic-params"); + } else { + wsa_priv->bcl_pmic_params.id = bcl_pmic_params[0]; + wsa_priv->bcl_pmic_params.sid = bcl_pmic_params[1]; + wsa_priv->bcl_pmic_params.ppid = bcl_pmic_params[2]; + } + + dev_set_drvdata(&pdev->dev, wsa_priv); + mutex_init(&wsa_priv->mclk_lock); + mutex_init(&wsa_priv->swr_clk_lock); + wsa_macro_init_ops(&ops, wsa_io_base); + ret = bolero_register_macro(&pdev->dev, WSA_MACRO, &ops); + if (ret < 0) { + dev_err(&pdev->dev, "%s: register macro failed\n", __func__); + goto reg_macro_fail; + } + schedule_work(&wsa_priv->wsa_macro_add_child_devices_work); + return ret; +reg_macro_fail: + mutex_destroy(&wsa_priv->mclk_lock); + mutex_destroy(&wsa_priv->swr_clk_lock); + return ret; +} + +static int wsa_macro_remove(struct platform_device *pdev) +{ + struct wsa_macro_priv *wsa_priv; + u16 count = 0; + + wsa_priv = dev_get_drvdata(&pdev->dev); + + if (!wsa_priv) + return -EINVAL; + + for (count = 0; count < wsa_priv->child_count && + count < WSA_MACRO_CHILD_DEVICES_MAX; count++) + platform_device_unregister(wsa_priv->pdev_child_devices[count]); + + bolero_unregister_macro(&pdev->dev, WSA_MACRO); + mutex_destroy(&wsa_priv->mclk_lock); + mutex_destroy(&wsa_priv->swr_clk_lock); + return 0; +} + +static const struct of_device_id wsa_macro_dt_match[] = { + {.compatible = "qcom,wsa-macro"}, + {} +}; + +static struct platform_driver wsa_macro_driver = { + .driver = { + .name = "wsa_macro", + .owner = THIS_MODULE, + .of_match_table = wsa_macro_dt_match, + }, + .probe = wsa_macro_probe, + .remove = wsa_macro_remove, +}; + +module_platform_driver(wsa_macro_driver); + +MODULE_DESCRIPTION("WSA macro driver"); +MODULE_LICENSE("GPL v2"); diff --git a/audio-kernel/asoc/codecs/bolero/wsa-macro.h b/audio-kernel/asoc/codecs/bolero/wsa-macro.h new file mode 100644 index 000000000..ec1aa9a28 --- /dev/null +++ b/audio-kernel/asoc/codecs/bolero/wsa-macro.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 WSA_MACRO_H +#define WSA_MACRO_H + +/* + * Selects compander and smart boost settings + * for a given speaker mode + */ +enum { + WSA_MACRO_SPKR_MODE_DEFAULT, + WSA_MACRO_SPKR_MODE_1, /* COMP Gain = 12dB, Smartboost Max = 5.5V */ +}; + +/* Rx path gain offsets */ +enum { + WSA_MACRO_GAIN_OFFSET_M1P5_DB, + WSA_MACRO_GAIN_OFFSET_0_DB, +}; + + +#if IS_ENABLED(CONFIG_WSA_MACRO) +extern int wsa_macro_set_spkr_mode(struct snd_soc_codec *codec, int mode); +extern int wsa_macro_set_spkr_gain_offset(struct snd_soc_codec *codec, + int offset); +#else /* CONFIG_WSA_MACRO */ +static inline int wsa_macro_set_spkr_mode(struct snd_soc_codec *codec, int mode) +{ + return 0; +} +static inline int wsa_macro_set_spkr_gain_offset(struct snd_soc_codec *codec, + int offset); +{ + return 0; +} +#endif /* CONFIG_WSA_MACRO */ +#endif diff --git a/audio-kernel/asoc/codecs/core.h b/audio-kernel/asoc/codecs/core.h new file mode 100644 index 000000000..7d9fcd2db --- /dev/null +++ b/audio-kernel/asoc/codecs/core.h @@ -0,0 +1,444 @@ +/* Copyright (c) 2011-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 __MFD_TABLA_CORE_H__ +#define __MFD_TABLA_CORE_H__ + +#include +#include +#include +#include +#include + +#define WCD9XXX_MAX_IRQ_REGS 4 +#define WCD9XXX_MAX_NUM_IRQS (WCD9XXX_MAX_IRQ_REGS * 8) +#define WCD9XXX_SLIM_NUM_PORT_REG 3 +#define TABLA_VERSION_1_0 0 +#define TABLA_VERSION_1_1 1 +#define TABLA_VERSION_2_0 2 +#define TABLA_IS_1_X(ver) \ + (((ver == TABLA_VERSION_1_0) || (ver == TABLA_VERSION_1_1)) ? 1 : 0) +#define TABLA_IS_2_0(ver) ((ver == TABLA_VERSION_2_0) ? 1 : 0) + +#define WCD9XXX_SUPPLY_BUCK_NAME "cdc-vdd-buck" + +#define SITAR_VERSION_1P0 0 +#define SITAR_VERSION_1P1 1 +#define SITAR_IS_1P0(ver) \ + ((ver == SITAR_VERSION_1P0) ? 1 : 0) +#define SITAR_IS_1P1(ver) \ + ((ver == SITAR_VERSION_1P1) ? 1 : 0) + +#define TAIKO_VERSION_1_0 1 +#define TAIKO_IS_1_0(ver) \ + ((ver == TAIKO_VERSION_1_0) ? 1 : 0) + +#define TAPAN_VERSION_1_0 0 +#define TAPAN_IS_1_0(ver) \ + ((ver == TAPAN_VERSION_1_0) ? 1 : 0) + +#define TOMTOM_VERSION_1_0 1 +#define TOMTOM_IS_1_0(ver) \ + ((ver == TOMTOM_VERSION_1_0) ? 1 : 0) + +#define TASHA_VERSION_1_0 0 +#define TASHA_VERSION_1_1 1 +#define TASHA_VERSION_2_0 2 + +#define TASHA_IS_1_0(wcd) \ + ((wcd->type == WCD9335 || wcd->type == WCD9326) ? \ + ((wcd->version == TASHA_VERSION_1_0) ? 1 : 0) : 0) + +#define TASHA_IS_1_1(wcd) \ + ((wcd->type == WCD9335 || wcd->type == WCD9326) ? \ + ((wcd->version == TASHA_VERSION_1_1) ? 1 : 0) : 0) + +#define TASHA_IS_2_0(wcd) \ + ((wcd->type == WCD9335 || wcd->type == WCD9326) ? \ + ((wcd->version == TASHA_VERSION_2_0) ? 1 : 0) : 0) + +/* + * As fine version info cannot be retrieved before tavil probe. + * Define three coarse versions for possible future use before tavil probe. + */ +#define TAVIL_VERSION_1_0 0 +#define TAVIL_VERSION_1_1 1 +#define TAVIL_VERSION_WCD9340_1_0 2 +#define TAVIL_VERSION_WCD9341_1_0 3 +#define TAVIL_VERSION_WCD9340_1_1 4 +#define TAVIL_VERSION_WCD9341_1_1 5 + +#define TAVIL_IS_1_0(wcd) \ + ((wcd->type == WCD934X) ? \ + ((wcd->version == TAVIL_VERSION_1_0 || \ + wcd->version == TAVIL_VERSION_WCD9340_1_0 || \ + wcd->version == TAVIL_VERSION_WCD9341_1_0) ? 1 : 0) : 0) +#define TAVIL_IS_1_1(wcd) \ + ((wcd->type == WCD934X) ? \ + ((wcd->version == TAVIL_VERSION_1_1 || \ + wcd->version == TAVIL_VERSION_WCD9340_1_1 || \ + wcd->version == TAVIL_VERSION_WCD9341_1_1) ? 1 : 0) : 0) +#define TAVIL_IS_WCD9340_1_0(wcd) \ + ((wcd->type == WCD934X) ? \ + ((wcd->version == TAVIL_VERSION_WCD9340_1_0) ? 1 : 0) : 0) +#define TAVIL_IS_WCD9341_1_0(wcd) \ + ((wcd->type == WCD934X) ? \ + ((wcd->version == TAVIL_VERSION_WCD9341_1_0) ? 1 : 0) : 0) +#define TAVIL_IS_WCD9340_1_1(wcd) \ + ((wcd->type == WCD934X) ? \ + ((wcd->version == TAVIL_VERSION_WCD9340_1_1) ? 1 : 0) : 0) +#define TAVIL_IS_WCD9341_1_1(wcd) \ + ((wcd->type == WCD934X) ? \ + ((wcd->version == TAVIL_VERSION_WCD9341_1_1) ? 1 : 0) : 0) + +#define IS_CODEC_TYPE(wcd, wcdtype) \ + ((wcd->type == wcdtype) ? true : false) +#define IS_CODEC_VERSION(wcd, wcdversion) \ + ((wcd->version == wcdversion) ? true : false) + +#define PAHU_VERSION_1_0 0 + +enum { + CDC_V_1_0, + CDC_V_1_1, + CDC_V_2_0, +}; + +enum codec_variant { + WCD9XXX, + WCD9330, + WCD9335, + WCD9326, + WCD934X, + WCD9360, +}; + +enum wcd9xxx_slim_slave_addr_type { + WCD9XXX_SLIM_SLAVE_ADDR_TYPE_0, + WCD9XXX_SLIM_SLAVE_ADDR_TYPE_1, +}; + +enum wcd9xxx_pm_state { + WCD9XXX_PM_SLEEPABLE, + WCD9XXX_PM_AWAKE, + WCD9XXX_PM_ASLEEP, +}; + +enum { + WCD9XXX_INTR_STATUS_BASE = 0, + WCD9XXX_INTR_CLEAR_BASE, + WCD9XXX_INTR_MASK_BASE, + WCD9XXX_INTR_LEVEL_BASE, + WCD9XXX_INTR_CLR_COMMIT, + WCD9XXX_INTR_REG_MAX, +}; + +enum wcd9xxx_intf_status { + WCD9XXX_INTERFACE_TYPE_PROBING, + WCD9XXX_INTERFACE_TYPE_SLIMBUS, + WCD9XXX_INTERFACE_TYPE_I2C, +}; + +enum { + /* INTR_REG 0 */ + WCD9XXX_IRQ_SLIMBUS = 0, + WCD9XXX_IRQ_MBHC_REMOVAL, + WCD9XXX_IRQ_MBHC_SHORT_TERM, + WCD9XXX_IRQ_MBHC_PRESS, + WCD9XXX_IRQ_MBHC_RELEASE, + WCD9XXX_IRQ_MBHC_POTENTIAL, + WCD9XXX_IRQ_MBHC_INSERTION, + WCD9XXX_IRQ_BG_PRECHARGE, + /* INTR_REG 1 */ + WCD9XXX_IRQ_PA1_STARTUP, + WCD9XXX_IRQ_PA2_STARTUP, + WCD9XXX_IRQ_PA3_STARTUP, + WCD9XXX_IRQ_PA4_STARTUP, + WCD9306_IRQ_HPH_PA_OCPR_FAULT = WCD9XXX_IRQ_PA4_STARTUP, + WCD9XXX_IRQ_PA5_STARTUP, + WCD9XXX_IRQ_MICBIAS1_PRECHARGE, + WCD9306_IRQ_HPH_PA_OCPL_FAULT = WCD9XXX_IRQ_MICBIAS1_PRECHARGE, + WCD9XXX_IRQ_MICBIAS2_PRECHARGE, + WCD9XXX_IRQ_MICBIAS3_PRECHARGE, + /* INTR_REG 2 */ + WCD9XXX_IRQ_HPH_PA_OCPL_FAULT, + WCD9XXX_IRQ_HPH_PA_OCPR_FAULT, + WCD9XXX_IRQ_EAR_PA_OCPL_FAULT, + WCD9XXX_IRQ_HPH_L_PA_STARTUP, + WCD9XXX_IRQ_HPH_R_PA_STARTUP, + WCD9320_IRQ_EAR_PA_STARTUP, + WCD9306_IRQ_MBHC_JACK_SWITCH = WCD9320_IRQ_EAR_PA_STARTUP, + WCD9310_NUM_IRQS, + WCD9XXX_IRQ_RESERVED_0 = WCD9310_NUM_IRQS, + WCD9XXX_IRQ_RESERVED_1, + WCD9330_IRQ_SVASS_ERR_EXCEPTION = WCD9310_NUM_IRQS, + WCD9330_IRQ_MBHC_JACK_SWITCH, + /* INTR_REG 3 */ + WCD9XXX_IRQ_MAD_AUDIO, + WCD9XXX_IRQ_MAD_ULTRASOUND, + WCD9XXX_IRQ_MAD_BEACON, + WCD9XXX_IRQ_SPEAKER_CLIPPING, + WCD9320_IRQ_MBHC_JACK_SWITCH, + WCD9306_NUM_IRQS, + WCD9XXX_IRQ_VBAT_MONITOR_ATTACK = WCD9306_NUM_IRQS, + WCD9XXX_IRQ_VBAT_MONITOR_RELEASE, + WCD9XXX_NUM_IRQS, + /* WCD9330 INTR1_REG 3*/ + WCD9330_IRQ_SVASS_ENGINE = WCD9XXX_IRQ_MAD_AUDIO, + WCD9330_IRQ_MAD_AUDIO, + WCD9330_IRQ_MAD_ULTRASOUND, + WCD9330_IRQ_MAD_BEACON, + WCD9330_IRQ_SPEAKER1_CLIPPING, + WCD9330_IRQ_SPEAKER2_CLIPPING, + WCD9330_IRQ_VBAT_MONITOR_ATTACK, + WCD9330_IRQ_VBAT_MONITOR_RELEASE, + WCD9330_NUM_IRQS, + WCD9XXX_IRQ_RESERVED_2 = WCD9330_NUM_IRQS, +}; + +enum { + TABLA_NUM_IRQS = WCD9310_NUM_IRQS, + SITAR_NUM_IRQS = WCD9310_NUM_IRQS, + TAIKO_NUM_IRQS = WCD9XXX_NUM_IRQS, + TAPAN_NUM_IRQS = WCD9306_NUM_IRQS, + TOMTOM_NUM_IRQS = WCD9330_NUM_IRQS, +}; + +struct intr_data { + int intr_num; + bool clear_first; +}; + +struct wcd9xxx_core_resource { + struct mutex irq_lock; + struct mutex nested_irq_lock; + + enum wcd9xxx_pm_state pm_state; + struct mutex pm_lock; + /* pm_wq notifies change of pm_state */ + wait_queue_head_t pm_wq; + struct pm_qos_request pm_qos_req; + int wlock_holders; + + + /* holds the table of interrupts per codec */ + const struct intr_data *intr_table; + int intr_table_size; + unsigned int irq_base; + unsigned int irq; + u8 irq_masks_cur[WCD9XXX_MAX_IRQ_REGS]; + u8 irq_masks_cache[WCD9XXX_MAX_IRQ_REGS]; + bool irq_level_high[WCD9XXX_MAX_NUM_IRQS]; + int num_irqs; + int num_irq_regs; + u16 intr_reg[WCD9XXX_INTR_REG_MAX]; + struct regmap *wcd_core_regmap; + + /* Pointer to parent container data structure */ + void *parent; + + struct device *dev; + struct irq_domain *domain; +}; + +/* + * data structure for Slimbus and I2S channel. + * Some of fields are only used in smilbus mode + */ +struct wcd9xxx_ch { + u32 sph; /* share channel handle - slimbus only */ + u32 ch_num; /* + * vitrual channel number, such as 128 -144. + * apply for slimbus only + */ + u16 ch_h; /* chanel handle - slimbus only */ + u16 port; /* + * tabla port for RX and TX + * such as 0-9 for TX and 10 -16 for RX + * apply for both i2s and slimbus + */ + u16 shift; /* + * shift bit for RX and TX + * apply for both i2s and slimbus + */ + struct list_head list; /* + * channel link list + * apply for both i2s and slimbus + */ +}; + +struct wcd9xxx_codec_dai_data { + u32 rate; /* sample rate */ + u32 bit_width; /* sit width 16,24,32 */ + struct list_head wcd9xxx_ch_list; /* channel list */ + u16 grph; /* slimbus group handle */ + unsigned long ch_mask; + wait_queue_head_t dai_wait; + bool bus_down_in_recovery; +}; + +#define WCD9XXX_CH(xport, xshift) \ + {.port = xport, .shift = xshift} + +enum wcd9xxx_chipid_major { + TABLA_MAJOR = cpu_to_le16(0x100), + SITAR_MAJOR = cpu_to_le16(0x101), + TAIKO_MAJOR = cpu_to_le16(0x102), + TAPAN_MAJOR = cpu_to_le16(0x103), + TOMTOM_MAJOR = cpu_to_le16(0x105), + TASHA_MAJOR = cpu_to_le16(0x0), + TASHA2P0_MAJOR = cpu_to_le16(0x107), + TAVIL_MAJOR = cpu_to_le16(0x108), + PAHU_MAJOR = cpu_to_le16(0x109), +}; + +enum codec_power_states { + WCD_REGION_POWER_COLLAPSE_REMOVE, + WCD_REGION_POWER_COLLAPSE_BEGIN, + WCD_REGION_POWER_DOWN, +}; + +enum wcd_power_regions { + WCD9XXX_DIG_CORE_REGION_1, + WCD9XXX_MAX_PWR_REGIONS, +}; + +struct wcd9xxx_codec_type { + u16 id_major; + u16 id_minor; + struct mfd_cell *dev; + int size; + int num_irqs; + int version; /* -1 to retrieve version from chip version register */ + enum wcd9xxx_slim_slave_addr_type slim_slave_type; + u16 i2c_chip_status; + const struct intr_data *intr_tbl; + int intr_tbl_size; + u16 intr_reg[WCD9XXX_INTR_REG_MAX]; +}; + +struct wcd9xxx_power_region { + enum codec_power_states power_state; + u16 pwr_collapse_reg_min; + u16 pwr_collapse_reg_max; +}; + +struct wcd9xxx { + struct device *dev; + struct slim_device *slim; + struct slim_device *slim_slave; + struct mutex io_lock; + struct mutex xfer_lock; + struct mutex reset_lock; + u8 version; + + int reset_gpio; + struct device_node *wcd_rst_np; + + int (*read_dev)(struct wcd9xxx *wcd9xxx, unsigned short reg, + int bytes, void *dest, bool interface_reg); + int (*write_dev)(struct wcd9xxx *wcd9xxx, unsigned short reg, + int bytes, void *src, bool interface_reg); + int (*multi_reg_write)(struct wcd9xxx *wcd9xxx, const void *data, + size_t count); + int (*dev_down)(struct wcd9xxx *wcd9xxx); + int (*post_reset)(struct wcd9xxx *wcd9xxx); + + void *ssr_priv; + bool dev_up; + + u32 num_of_supplies; + struct regulator_bulk_data *supplies; + + struct wcd9xxx_core_resource core_res; + + u16 id_minor; + u16 id_major; + + /* Slimbus or I2S port */ + u32 num_rx_port; + u32 num_tx_port; + struct wcd9xxx_ch *rx_chs; + struct wcd9xxx_ch *tx_chs; + u32 mclk_rate; + enum codec_variant type; + struct regmap *regmap; + + struct wcd9xxx_codec_type *codec_type; + bool prev_pg_valid; + u8 prev_pg; + u8 avoid_cdc_rstlow; + struct wcd9xxx_power_region *wcd9xxx_pwr[WCD9XXX_MAX_PWR_REGIONS]; +}; + +struct wcd9xxx_reg_val { + unsigned short reg; /* register address */ + u8 *buf; /* buffer to be written to reg. addr */ + int bytes; /* number of bytes to be written */ +}; + +int wcd9xxx_interface_reg_read(struct wcd9xxx *wcd9xxx, unsigned short reg); +int wcd9xxx_interface_reg_write(struct wcd9xxx *wcd9xxx, unsigned short reg, + u8 val); +int wcd9xxx_get_logical_addresses(u8 *pgd_la, u8 *inf_la); +int wcd9xxx_slim_write_repeat(struct wcd9xxx *wcd9xxx, unsigned short reg, + int bytes, void *src); +int wcd9xxx_slim_reserve_bw(struct wcd9xxx *wcd9xxx, + u32 bw_ops, bool commit); +int wcd9xxx_set_power_state(struct wcd9xxx *wcd9xxx, enum codec_power_states, + enum wcd_power_regions); +int wcd9xxx_get_current_power_state(struct wcd9xxx *wcd9xxx, + enum wcd_power_regions); + +int wcd9xxx_page_write(struct wcd9xxx *wcd9xxx, unsigned short *reg); + +int wcd9xxx_slim_bulk_write(struct wcd9xxx *wcd9xxx, + struct wcd9xxx_reg_val *bulk_reg, + unsigned int size, bool interface); + +extern int wcd9xxx_core_res_init( + struct wcd9xxx_core_resource *wcd9xxx_core_res, + int num_irqs, int num_irq_regs, struct regmap *wcd_regmap); + +extern void wcd9xxx_core_res_deinit( + struct wcd9xxx_core_resource *wcd9xxx_core_res); + +extern int wcd9xxx_core_res_suspend( + struct wcd9xxx_core_resource *wcd9xxx_core_res, + pm_message_t pmesg); + +extern int wcd9xxx_core_res_resume( + struct wcd9xxx_core_resource *wcd9xxx_core_res); + +extern int wcd9xxx_core_irq_init( + struct wcd9xxx_core_resource *wcd9xxx_core_res); + +extern int wcd9xxx_assign_irq(struct wcd9xxx_core_resource *wcd9xxx_core_res, + unsigned int irq, + unsigned int irq_base); + +extern enum wcd9xxx_intf_status wcd9xxx_get_intf_type(void); +extern void wcd9xxx_set_intf_type(enum wcd9xxx_intf_status); + +extern enum wcd9xxx_pm_state wcd9xxx_pm_cmpxchg( + struct wcd9xxx_core_resource *wcd9xxx_core_res, + enum wcd9xxx_pm_state o, + enum wcd9xxx_pm_state n); +static inline int __init wcd9xxx_irq_of_init(struct device_node *node, + struct device_node *parent) +{ + return 0; +} + +int wcd9xxx_init(void); +void wcd9xxx_exit(void); +#endif diff --git a/audio-kernel/asoc/codecs/cpe_cmi.h b/audio-kernel/asoc/codecs/cpe_cmi.h new file mode 100644 index 000000000..c145a8a64 --- /dev/null +++ b/audio-kernel/asoc/codecs/cpe_cmi.h @@ -0,0 +1,492 @@ +/* + * Copyright (c) 2014-2016, 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 __CPE_CMI_H__ +#define __CPE_CMI_H__ + +#include + +#define CPE_AFE_PORT_1_TX 1 +#define CPE_AFE_PORT_3_TX 3 +#define CPE_AFE_PORT_ID_2_OUT 0x02 +#define CMI_INBAND_MESSAGE_SIZE 127 + +/* + * Multiple mad types can be supported at once. + * these values can be OR'ed to form the set of + * supported mad types + */ +#define MAD_TYPE_AUDIO (1 << 0) +#define MAD_TYPE_BEACON (1 << 1) +#define MAD_TYPE_ULTRASND (1 << 2) + +/* Core service command opcodes */ +#define CPE_CORE_SVC_CMD_SHARED_MEM_ALLOC (0x3001) +#define CPE_CORE_SVC_CMDRSP_SHARED_MEM_ALLOC (0x3002) +#define CPE_CORE_SVC_CMD_SHARED_MEM_DEALLOC (0x3003) +#define CPE_CORE_SVC_CMD_DRAM_ACCESS_REQ (0x3004) +#define CPE_CORE_SVC_EVENT_SYSTEM_BOOT (0x3005) +/* core service command opcodes for WCD9335 */ +#define CPE_CORE_SVC_CMD_CFG_CLK_PLAN (0x3006) +#define CPE_CORE_SVC_CMD_CLK_FREQ_REQUEST (0x3007) + +#define CPE_BOOT_SUCCESS 0x00 +#define CPE_BOOT_FAILED 0x01 + +#define CPE_CORE_VERSION_SYSTEM_BOOT_EVENT 0x01 + +/* LSM Service command opcodes */ +#define CPE_LSM_SESSION_CMD_OPEN_TX (0x2000) +#define CPE_LSM_SESSION_CMD_SET_PARAMS (0x2001) +#define CPE_LSM_SESSION_CMD_REGISTER_SOUND_MODEL (0x2002) +#define CPE_LSM_SESSION_CMD_DEREGISTER_SOUND_MODEL (0x2003) +#define CPE_LSM_SESSION_CMD_START (0x2004) +#define CPE_LSM_SESSION_CMD_STOP (0x2005) +#define CPE_LSM_SESSION_EVENT_DETECTION_STATUS_V2 (0x2006) +#define CPE_LSM_SESSION_CMD_CLOSE_TX (0x2007) +#define CPE_LSM_SESSION_CMD_SHARED_MEM_ALLOC (0x2008) +#define CPE_LSM_SESSION_CMDRSP_SHARED_MEM_ALLOC (0x2009) +#define CPE_LSM_SESSION_CMD_SHARED_MEM_DEALLOC (0x200A) +#define CPE_LSM_SESSION_CMD_TX_BUFF_OUTPUT_CONFIG (0x200f) +#define CPE_LSM_SESSION_CMD_OPEN_TX_V2 (0x200D) +#define CPE_LSM_SESSION_CMD_SET_PARAMS_V2 (0x200E) + +/* LSM Service module and param IDs */ +#define CPE_LSM_MODULE_ID_VOICE_WAKEUP (0x00012C00) +#define CPE_LSM_MODULE_ID_VOICE_WAKEUP_V2 (0x00012C0D) +#define CPE_LSM_MODULE_FRAMEWORK (0x00012C0E) + +#define CPE_LSM_PARAM_ID_ENDPOINT_DETECT_THRESHOLD (0x00012C01) +#define CPE_LSM_PARAM_ID_OPERATION_MODE (0x00012C02) +#define CPE_LSM_PARAM_ID_GAIN (0x00012C03) +#define CPE_LSM_PARAM_ID_CONNECT_TO_PORT (0x00012C04) +#define CPE_LSM_PARAM_ID_MIN_CONFIDENCE_LEVELS (0x00012C07) + +/* LSM LAB command opcodes */ +#define CPE_LSM_SESSION_CMD_EOB 0x0000200B +#define CPE_LSM_MODULE_ID_LAB 0x00012C08 +/* used for enable/disable lab*/ +#define CPE_LSM_PARAM_ID_LAB_ENABLE 0x00012C09 +/* used for T in LAB config DSP internal buffer*/ +#define CPE_LSM_PARAM_ID_LAB_CONFIG 0x00012C0A +#define CPE_LSM_PARAM_ID_REGISTER_SOUND_MODEL (0x00012C14) +#define CPE_LSM_PARAM_ID_DEREGISTER_SOUND_MODEL (0x00012C15) +#define CPE_LSM_PARAM_ID_MEDIA_FMT (0x00012C1E) + +/* AFE Service command opcodes */ +#define CPE_AFE_PORT_CMD_START (0x1001) +#define CPE_AFE_PORT_CMD_STOP (0x1002) +#define CPE_AFE_PORT_CMD_SUSPEND (0x1003) +#define CPE_AFE_PORT_CMD_RESUME (0x1004) +#define CPE_AFE_PORT_CMD_SHARED_MEM_ALLOC (0x1005) +#define CPE_AFE_PORT_CMDRSP_SHARED_MEM_ALLOC (0x1006) +#define CPE_AFE_PORT_CMD_SHARED_MEM_DEALLOC (0x1007) +#define CPE_AFE_PORT_CMD_GENERIC_CONFIG (0x1008) +#define CPE_AFE_SVC_CMD_LAB_MODE (0x1009) + +/* AFE Service module and param IDs */ +#define CPE_AFE_CMD_SET_PARAM (0x1000) +#define CPE_AFE_MODULE_ID_SW_MAD (0x0001022D) +#define CPE_AFE_PARAM_ID_SW_MAD_CFG (0x0001022E) +#define CPE_AFE_PARAM_ID_SVM_MODEL (0x0001022F) + +#define CPE_AFE_MODULE_HW_MAD (0x00010230) +#define CPE_AFE_PARAM_ID_HW_MAD_CTL (0x00010232) +#define CPE_AFE_PARAM_ID_HW_MAD_CFG (0x00010231) + +#define CPE_AFE_MODULE_AUDIO_DEV_INTERFACE (0x0001020C) +#define CPE_AFE_PARAM_ID_GENERIC_PORT_CONFIG (0x00010253) + +#define CPE_CMI_BASIC_RSP_OPCODE (0x0001) +#define CPE_HDR_MAX_PLD_SIZE (0x7F) + +#define CMI_OBM_FLAG_IN_BAND 0 +#define CMI_OBM_FLAG_OUT_BAND 1 + +#define CMI_SHMEM_ALLOC_FAILED 0xff + +/* + * Future Service ID's can be added one line + * before the CMI_CPE_SERVICE_ID_MAX + */ +enum { + CMI_CPE_SERVICE_ID_MIN = 0, + CMI_CPE_CORE_SERVICE_ID, + CMI_CPE_AFE_SERVICE_ID, + CMI_CPE_LSM_SERVICE_ID, + CMI_CPE_SERVICE_ID_MAX, +}; + +#define CPE_LSM_SESSION_ID_MAX 2 + +#define IS_VALID_SESSION_ID(s_id) \ + (s_id <= CPE_LSM_SESSION_ID_MAX) + +#define IS_VALID_SERVICE_ID(s_id) \ + (s_id > CMI_CPE_SERVICE_ID_MIN && \ + s_id < CMI_CPE_SERVICE_ID_MAX) + +#define IS_VALID_PLD_SIZE(p_size) \ + (p_size <= CPE_HDR_MAX_PLD_SIZE) + +#define CMI_HDR_SET_OPCODE(hdr, cmd) (hdr->opcode = cmd) + + +#define CMI_HDR_SET(hdr_info, mask, shift, value) \ + (hdr_info = (((hdr_info) & ~(mask)) | \ + ((value << shift) & mask))) + +#define SVC_ID_SHIFT 4 +#define SVC_ID_MASK (0x07 << SVC_ID_SHIFT) + +#define SESSION_ID_SHIFT 0 +#define SESSION_ID_MASK (0x0F << SESSION_ID_SHIFT) + +#define PAYLD_SIZE_SHIFT 0 +#define PAYLD_SIZE_MASK (0x7F << PAYLD_SIZE_SHIFT) + +#define OBM_FLAG_SHIFT 7 +#define OBM_FLAG_MASK (1 << OBM_FLAG_SHIFT) + +#define VERSION_SHIFT 7 +#define VERSION_MASK (1 << VERSION_SHIFT) + +#define CMI_HDR_SET_SERVICE(hdr, s_id) \ + CMI_HDR_SET(hdr->hdr_info, SVC_ID_MASK,\ + SVC_ID_SHIFT, s_id) +#define CMI_HDR_GET_SERVICE(hdr) \ + ((hdr->hdr_info >> SVC_ID_SHIFT) & \ + (SVC_ID_MASK >> SVC_ID_SHIFT)) + + +#define CMI_HDR_SET_SESSION(hdr, s_id) \ + CMI_HDR_SET(hdr->hdr_info, SESSION_ID_MASK,\ + SESSION_ID_SHIFT, s_id) + +#define CMI_HDR_GET_SESSION_ID(hdr) \ + ((hdr->hdr_info >> SESSION_ID_SHIFT) & \ + (SESSION_ID_MASK >> SESSION_ID_SHIFT)) + +#define CMI_GET_HEADER(msg) ((struct cmi_hdr *)(msg)) +#define CMI_GET_PAYLOAD(msg) ((void *)(CMI_GET_HEADER(msg) + 1)) +#define CMI_GET_OPCODE(msg) (CMI_GET_HEADER(msg)->opcode) + +#define CMI_HDR_SET_VERSION(hdr, ver) \ + CMI_HDR_SET(hdr->hdr_info, VERSION_MASK, \ + VERSION_SHIFT, ver) + +#define CMI_HDR_SET_PAYLOAD_SIZE(hdr, p_size) \ + CMI_HDR_SET(hdr->pld_info, PAYLD_SIZE_MASK, \ + PAYLD_SIZE_SHIFT, p_size) + +#define CMI_HDR_GET_PAYLOAD_SIZE(hdr) \ + ((hdr->pld_info >> PAYLD_SIZE_SHIFT) & \ + (PAYLD_SIZE_MASK >> PAYLD_SIZE_SHIFT)) + +#define CMI_HDR_SET_OBM(hdr, obm_flag) \ + CMI_HDR_SET(hdr->pld_info, OBM_FLAG_MASK, \ + OBM_FLAG_SHIFT, obm_flag) + +#define CMI_HDR_GET_OBM_FLAG(hdr) \ + ((hdr->pld_info >> OBM_FLAG_SHIFT) & \ + (OBM_FLAG_MASK >> OBM_FLAG_SHIFT)) + +struct cmi_hdr { + /* + * bits 0:3 is session id + * bits 4:6 is service id + * bit 7 is the version flag + */ + u8 hdr_info; + + /* + * bits 0:6 is payload size in case of in-band message + * bits 0:6 is size (OBM message size) + * bit 7 is the OBM flag + */ + u8 pld_info; + + /* 16 bit command opcode */ + u16 opcode; +} __packed; + +union cpe_addr { + u64 msw_lsw; + void *kvaddr; +} __packed; + +struct cmi_obm { + u32 version; + u32 size; + union cpe_addr data_ptr; + u32 mem_handle; +} __packed; + +struct cmi_obm_msg { + struct cmi_hdr hdr; + struct cmi_obm pld; +} __packed; + +struct cmi_core_svc_event_system_boot { + u8 status; + u8 version; + u16 sfr_buff_size; + u32 sfr_buff_address; +} __packed; + +struct cmi_core_svc_cmd_shared_mem_alloc { + u32 size; +} __packed; + +struct cmi_core_svc_cmdrsp_shared_mem_alloc { + u32 addr; +} __packed; + +struct cmi_core_svc_cmd_clk_freq_request { + u32 clk_freq; +} __packed; + +struct cmi_msg_transport { + u32 size; + u32 addr; +} __packed; + +struct cmi_basic_rsp_result { + u8 status; +} __packed; + +struct cpe_lsm_cmd_open_tx { + struct cmi_hdr hdr; + u16 app_id; + u16 reserved; + u32 sampling_rate; +} __packed; + +struct cpe_lsm_cmd_open_tx_v2 { + struct cmi_hdr hdr; + u32 topology_id; +} __packed; + +struct cpe_cmd_shmem_alloc { + struct cmi_hdr hdr; + u32 size; +} __packed; + +struct cpe_cmdrsp_shmem_alloc { + struct cmi_hdr hdr; + u32 addr; +} __packed; + +struct cpe_cmd_shmem_dealloc { + struct cmi_hdr hdr; + u32 addr; +} __packed; + +struct cpe_lsm_event_detect_v2 { + struct cmi_hdr hdr; + u8 detection_status; + u8 size; + u8 payload[0]; +} __packed; + +struct cpe_lsm_psize_res { + u16 param_size; + u16 reserved; +} __packed; + +union cpe_lsm_param_size { + u32 param_size; + struct cpe_lsm_psize_res sr; +} __packed; + +struct cpe_param_data { + u32 module_id; + u32 param_id; + union cpe_lsm_param_size p_size; +} __packed; + +struct cpe_lsm_param_epd_thres { + struct cmi_hdr hdr; + struct cpe_param_data param; + u32 minor_version; + u32 epd_begin; + u32 epd_end; +} __packed; + +struct cpe_lsm_param_gain { + struct cmi_hdr hdr; + struct cpe_param_data param; + u32 minor_version; + u16 gain; + u16 reserved; +} __packed; + +struct cpe_afe_hw_mad_ctrl { + struct cpe_param_data param; + u32 minor_version; + u16 mad_type; + u16 mad_enable; +} __packed; + +struct cpe_afe_port_cfg { + struct cpe_param_data param; + u32 minor_version; + u16 bit_width; + u16 num_channels; + u32 sample_rate; +} __packed; + +struct cpe_afe_cmd_port_cfg { + struct cmi_hdr hdr; + u8 bit_width; + u8 num_channels; + u16 buffer_size; + u32 sample_rate; +} __packed; + +struct cpe_afe_params { + struct cmi_hdr hdr; + struct cpe_afe_hw_mad_ctrl hw_mad_ctrl; + struct cpe_afe_port_cfg port_cfg; +} __packed; + +struct cpe_afe_svc_cmd_mode { + struct cmi_hdr hdr; + u8 mode; +} __packed; + +struct cpe_lsm_param_opmode { + struct cmi_hdr hdr; + struct cpe_param_data param; + u32 minor_version; + u16 mode; + u16 reserved; +} __packed; + +struct cpe_lsm_param_connectport { + struct cmi_hdr hdr; + struct cpe_param_data param; + u32 minor_version; + u16 afe_port_id; + u16 reserved; +} __packed; + +/* + * This cannot be sent to CPE as is, + * need to append the conf_levels dynamically + */ +struct cpe_lsm_conf_level { + struct cmi_hdr hdr; + struct cpe_param_data param; + u8 num_active_models; +} __packed; + +struct cpe_lsm_output_format_cfg { + struct cmi_hdr hdr; + u8 format; + u8 packing; + u8 data_path_events; +} __packed; + +struct cpe_lsm_lab_enable { + struct cpe_param_data param; + u16 enable; + u16 reserved; +} __packed; + +struct cpe_lsm_control_lab { + struct cmi_hdr hdr; + struct cpe_lsm_lab_enable lab_enable; +} __packed; + +struct cpe_lsm_lab_config { + struct cpe_param_data param; + u32 minor_ver; + u32 latency; +} __packed; + +struct cpe_lsm_lab_latency_config { + struct cmi_hdr hdr; + struct cpe_lsm_lab_config latency_cfg; +} __packed; + +struct cpe_lsm_media_fmt_param { + struct cmi_hdr hdr; + struct cpe_param_data param; + u32 minor_version; + u32 sample_rate; + u16 num_channels; + u16 bit_width; +} __packed; + + +#define CPE_PARAM_LSM_LAB_LATENCY_SIZE (\ + sizeof(struct cpe_lsm_lab_latency_config) - \ + sizeof(struct cmi_hdr)) +#define PARAM_SIZE_LSM_LATENCY_SIZE (\ + sizeof(struct cpe_lsm_lab_config) - \ + sizeof(struct cpe_param_data)) +#define CPE_PARAM_SIZE_LSM_LAB_CONTROL (\ + sizeof(struct cpe_lsm_control_lab) - \ + sizeof(struct cmi_hdr)) +#define PARAM_SIZE_LSM_CONTROL_SIZE (sizeof(struct cpe_lsm_lab_enable) - \ + sizeof(struct cpe_param_data)) +#define PARAM_SIZE_AFE_HW_MAD_CTRL (sizeof(struct cpe_afe_hw_mad_ctrl) - \ + sizeof(struct cpe_param_data)) +#define PARAM_SIZE_AFE_PORT_CFG (sizeof(struct cpe_afe_port_cfg) - \ + sizeof(struct cpe_param_data)) +#define CPE_AFE_PARAM_PAYLOAD_SIZE (sizeof(struct cpe_afe_params) - \ + sizeof(struct cmi_hdr)) + +#define OPEN_CMD_PAYLOAD_SIZE (sizeof(struct cpe_lsm_cmd_open_tx) - \ + sizeof(struct cmi_hdr)) +#define OPEN_V2_CMD_PAYLOAD_SIZE (sizeof(struct cpe_lsm_cmd_open_tx_v2) - \ + sizeof(struct cmi_hdr)) +#define SHMEM_ALLOC_CMD_PLD_SIZE (sizeof(struct cpe_cmd_shmem_alloc) - \ + sizeof(struct cmi_hdr)) + +#define SHMEM_DEALLOC_CMD_PLD_SIZE (sizeof(struct cpe_cmd_shmem_dealloc) - \ + sizeof(struct cmi_hdr)) +#define OUT_FMT_CFG_CMD_PAYLOAD_SIZE ( \ + sizeof(struct cpe_lsm_output_format_cfg) - \ + sizeof(struct cmi_hdr)) + +#define CPE_AFE_CMD_PORT_CFG_PAYLOAD_SIZE \ + (sizeof(struct cpe_afe_cmd_port_cfg) - \ + sizeof(struct cmi_hdr)) + +#define CPE_AFE_CMD_MODE_PAYLOAD_SIZE \ + (sizeof(struct cpe_afe_svc_cmd_mode) - \ + sizeof(struct cmi_hdr)) +#define CPE_CMD_EPD_THRES_PLD_SIZE (sizeof(struct cpe_lsm_param_epd_thres) - \ + sizeof(struct cmi_hdr)) +#define CPE_EPD_THRES_PARAM_SIZE ((CPE_CMD_EPD_THRES_PLD_SIZE) - \ + sizeof(struct cpe_param_data)) +#define CPE_CMD_OPMODE_PLD_SIZE (sizeof(struct cpe_lsm_param_opmode) - \ + sizeof(struct cmi_hdr)) +#define CPE_OPMODE_PARAM_SIZE ((CPE_CMD_OPMODE_PLD_SIZE) -\ + sizeof(struct cpe_param_data)) +#define CPE_CMD_CONNECTPORT_PLD_SIZE \ + (sizeof(struct cpe_lsm_param_connectport) - \ + sizeof(struct cmi_hdr)) +#define CPE_CONNECTPORT_PARAM_SIZE ((CPE_CMD_CONNECTPORT_PLD_SIZE) - \ + sizeof(struct cpe_param_data)) +#define CPE_CMD_GAIN_PLD_SIZE (sizeof(struct cpe_lsm_param_gain) - \ + sizeof(struct cmi_hdr)) +#define CPE_GAIN_PARAM_SIZE ((CPE_CMD_GAIN_PLD_SIZE) - \ + sizeof(struct cpe_param_data)) +#define CPE_MEDIA_FMT_PLD_SIZE (sizeof(struct cpe_lsm_media_fmt_param) - \ + sizeof(struct cmi_hdr)) +#define CPE_MEDIA_FMT_PARAM_SIZE ((CPE_MEDIA_FMT_PLD_SIZE) - \ + sizeof(struct cpe_param_data)) +#endif /* __CPE_CMI_H__ */ diff --git a/audio-kernel/asoc/codecs/cpe_core.h b/audio-kernel/asoc/codecs/cpe_core.h new file mode 100644 index 000000000..99504c5c9 --- /dev/null +++ b/audio-kernel/asoc/codecs/cpe_core.h @@ -0,0 +1,194 @@ +/* + * Copyright (c) 2013-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 + * 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 __CPE_CORE_H__ +#define __CPE_CORE_H__ + +#include +#include +#include +#include + +enum { + CMD_INIT_STATE = 0, + CMD_SENT, + CMD_RESP_RCVD, +}; + +enum wcd_cpe_event { + WCD_CPE_PRE_ENABLE = 1, + WCD_CPE_POST_ENABLE, + WCD_CPE_PRE_DISABLE, + WCD_CPE_POST_DISABLE, +}; + +struct wcd_cpe_afe_port_cfg { + u8 port_id; + u16 bit_width; + u16 num_channels; + u32 sample_rate; +}; + +struct lsm_out_fmt_cfg { + u8 format; + u8 pack_mode; + u8 data_path_events; + u8 transfer_mode; +}; + +struct lsm_hw_params { + u32 sample_rate; + u16 num_chs; + u16 bit_width; +}; + +struct cpe_lsm_session { + /* sound model related */ + void *snd_model_data; + u8 *conf_levels; + void *cmi_reg_handle; + + /* Clients private data */ + void *priv_d; + + void (*event_cb)(void *priv_data, + u8 detect_status, + u8 size, u8 *payload); + + struct completion cmd_comp; + struct wcd_cpe_afe_port_cfg afe_port_cfg; + struct wcd_cpe_afe_port_cfg afe_out_port_cfg; + struct mutex lsm_lock; + + u32 snd_model_size; + u32 lsm_mem_handle; + u16 cmd_err_code; + u8 id; + u8 num_confidence_levels; + u16 afe_out_port_id; + struct task_struct *lsm_lab_thread; + bool started; + + u32 lab_enable; + struct lsm_out_fmt_cfg out_fmt_cfg; + + bool is_topology_used; +}; + +struct wcd_cpe_afe_ops { + int (*afe_set_params)(void *core_handle, + struct wcd_cpe_afe_port_cfg *cfg, + bool afe_mad_ctl); + + int (*afe_port_start)(void *core_handle, + struct wcd_cpe_afe_port_cfg *cfg); + + int (*afe_port_stop)(void *core_handle, + struct wcd_cpe_afe_port_cfg *cfg); + + int (*afe_port_suspend)(void *core_handle, + struct wcd_cpe_afe_port_cfg *cfg); + + int (*afe_port_resume)(void *core_handle, + struct wcd_cpe_afe_port_cfg *cfg); + + int (*afe_port_cmd_cfg)(void *core_handle, + struct wcd_cpe_afe_port_cfg *cfg); +}; + +struct wcd_cpe_lsm_ops { + + struct cpe_lsm_session *(*lsm_alloc_session) + (void *core_handle, void *lsm_priv_d, + void (*event_cb)(void *priv_data, + u8 detect_status, + u8 size, u8 *payload)); + + int (*lsm_dealloc_session) + (void *core_handle, struct cpe_lsm_session *); + + int (*lsm_open_tx)(void *core_handle, + struct cpe_lsm_session *, u16, u16); + + int (*lsm_close_tx)(void *core_handle, + struct cpe_lsm_session *); + + int (*lsm_shmem_alloc)(void *core_handle, + struct cpe_lsm_session *, u32 size); + + int (*lsm_shmem_dealloc)(void *core_handle, + struct cpe_lsm_session *); + + int (*lsm_register_snd_model)(void *core_handle, + struct cpe_lsm_session *, + enum lsm_detection_mode, bool); + + int (*lsm_deregister_snd_model)(void *core_handle, + struct cpe_lsm_session *); + + int (*lsm_get_afe_out_port_id)(void *core_handle, + struct cpe_lsm_session *session); + + int (*lsm_start)(void *core_handle, + struct cpe_lsm_session *); + + int (*lsm_stop)(void *core_handle, + struct cpe_lsm_session *); + + int (*lsm_lab_control)(void *core_handle, + struct cpe_lsm_session *session, + bool enable); + + int (*lab_ch_setup)(void *core_handle, + struct cpe_lsm_session *session, + enum wcd_cpe_event event); + + int (*lsm_set_data)(void *core_handle, + struct cpe_lsm_session *session, + enum lsm_detection_mode detect_mode, + bool detect_failure); + int (*lsm_set_fmt_cfg)(void *core_handle, + struct cpe_lsm_session *session); + int (*lsm_set_one_param)(void *core_handle, + struct cpe_lsm_session *session, + struct lsm_params_info *p_info, + void *data, uint32_t param_type); + void (*lsm_get_snd_model_offset) + (void *core_handle, struct cpe_lsm_session *, + size_t *offset); + int (*lsm_set_media_fmt_params)(void *core_handle, + struct cpe_lsm_session *session, + struct lsm_hw_params *param); + int (*lsm_set_port)(void *core_handle, + struct cpe_lsm_session *session, void *data); +}; + +#if IS_ENABLED(CONFIG_SND_SOC_WCD_CPE) +int wcd_cpe_get_lsm_ops(struct wcd_cpe_lsm_ops *lsm_ops); +int wcd_cpe_get_afe_ops(struct wcd_cpe_afe_ops *afe_ops); +void *wcd_cpe_get_core_handle(struct snd_soc_codec *codec); +#else /* CONFIG_SND_SOC_WCD_CPE */ +static inline int wcd_cpe_get_lsm_ops(struct wcd_cpe_lsm_ops *lsm_ops) +{ + return 0; +} +static inline int wcd_cpe_get_afe_ops(struct wcd_cpe_afe_ops *afe_ops) +{ + return 0; +} +static inline void *wcd_cpe_get_core_handle(struct snd_soc_codec *codec) +{ + return NULL; +} +#endif /* CONFIG_SND_SOC_WCD_CPE */ +#endif diff --git a/audio-kernel/asoc/codecs/cpe_err.h b/audio-kernel/asoc/codecs/cpe_err.h new file mode 100644 index 000000000..b70dc490f --- /dev/null +++ b/audio-kernel/asoc/codecs/cpe_err.h @@ -0,0 +1,166 @@ +/* + * 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 + * 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 __CPE_ERR__ +#define __CPE_ERR__ + +#include + +/* ERROR CODES */ +/* Success. The operation completed with no errors. */ +#define CPE_EOK 0x00000000 +/* General failure. */ +#define CPE_EFAILED 0x00000001 +/* Bad operation parameter. */ +#define CPE_EBADPARAM 0x00000002 +/* Unsupported routine or operation. */ +#define CPE_EUNSUPPORTED 0x00000003 +/* Unsupported version. */ +#define CPE_EVERSION 0x00000004 +/* Unexpected problem encountered. */ +#define CPE_EUNEXPECTED 0x00000005 +/* Unhandled problem occurred. */ +#define CPE_EPANIC 0x00000006 +/* Unable to allocate resource. */ +#define CPE_ENORESOURCE 0x00000007 +/* Invalid handle. */ +#define CPE_EHANDLE 0x00000008 +/* Operation is already processed. */ +#define CPE_EALREADY 0x00000009 +/* Operation is not ready to be processed. */ +#define CPE_ENOTREADY 0x0000000A +/* Operation is pending completion. */ +#define CPE_EPENDING 0x0000000B +/* Operation could not be accepted or processed. */ +#define CPE_EBUSY 0x0000000C +/* Operation aborted due to an error. */ +#define CPE_EABORTED 0x0000000D +/* Operation preempted by a higher priority. */ +#define CPE_EPREEMPTED 0x0000000E +/* Operation requests intervention to complete. */ +#define CPE_ECONTINUE 0x0000000F +/* Operation requests immediate intervention to complete. */ +#define CPE_EIMMEDIATE 0x00000010 +/* Operation is not implemented. */ +#define CPE_ENOTIMPL 0x00000011 +/* Operation needs more data or resources. */ +#define CPE_ENEEDMORE 0x00000012 +/* Operation does not have memory. */ +#define CPE_ENOMEMORY 0x00000014 +/* Item does not exist. */ +#define CPE_ENOTEXIST 0x00000015 +/* Operation is finished. */ +#define CPE_ETERMINATED 0x00000016 +/* Max count for adsp error code sent to HLOS*/ +#define CPE_ERR_MAX (CPE_ETERMINATED + 1) + + +/* ERROR STRING */ +/* Success. The operation completed with no errors. */ +#define CPE_EOK_STR "CPE_EOK" +/* General failure. */ +#define CPE_EFAILED_STR "CPE_EFAILED" +/* Bad operation parameter. */ +#define CPE_EBADPARAM_STR "CPE_EBADPARAM" +/* Unsupported routine or operation. */ +#define CPE_EUNSUPPORTED_STR "CPE_EUNSUPPORTED" +/* Unsupported version. */ +#define CPE_EVERSION_STR "CPE_EVERSION" +/* Unexpected problem encountered. */ +#define CPE_EUNEXPECTED_STR "CPE_EUNEXPECTED" +/* Unhandled problem occurred. */ +#define CPE_EPANIC_STR "CPE_EPANIC" +/* Unable to allocate resource. */ +#define CPE_ENORESOURCE_STR "CPE_ENORESOURCE" +/* Invalid handle. */ +#define CPE_EHANDLE_STR "CPE_EHANDLE" +/* Operation is already processed. */ +#define CPE_EALREADY_STR "CPE_EALREADY" +/* Operation is not ready to be processed. */ +#define CPE_ENOTREADY_STR "CPE_ENOTREADY" +/* Operation is pending completion. */ +#define CPE_EPENDING_STR "CPE_EPENDING" +/* Operation could not be accepted or processed. */ +#define CPE_EBUSY_STR "CPE_EBUSY" +/* Operation aborted due to an error. */ +#define CPE_EABORTED_STR "CPE_EABORTED" +/* Operation preempted by a higher priority. */ +#define CPE_EPREEMPTED_STR "CPE_EPREEMPTED" +/* Operation requests intervention to complete. */ +#define CPE_ECONTINUE_STR "CPE_ECONTINUE" +/* Operation requests immediate intervention to complete. */ +#define CPE_EIMMEDIATE_STR "CPE_EIMMEDIATE" +/* Operation is not implemented. */ +#define CPE_ENOTIMPL_STR "CPE_ENOTIMPL" +/* Operation needs more data or resources. */ +#define CPE_ENEEDMORE_STR "CPE_ENEEDMORE" +/* Operation does not have memory. */ +#define CPE_ENOMEMORY_STR "CPE_ENOMEMORY" +/* Item does not exist. */ +#define CPE_ENOTEXIST_STR "CPE_ENOTEXIST" +/* Operation is finished. */ +#define CPE_ETERMINATED_STR "CPE_ETERMINATED" +/* Unexpected error code. */ +#define CPE_ERR_MAX_STR "CPE_ERR_MAX" + + +struct cpe_err_code { + int lnx_err_code; + char *cpe_err_str; +}; + + +static struct cpe_err_code cpe_err_code_info[CPE_ERR_MAX+1] = { + { 0, CPE_EOK_STR}, + { -ENOTRECOVERABLE, CPE_EFAILED_STR}, + { -EINVAL, CPE_EBADPARAM_STR}, + { -EOPNOTSUPP, CPE_EUNSUPPORTED_STR}, + { -ENOPROTOOPT, CPE_EVERSION_STR}, + { -ENOTRECOVERABLE, CPE_EUNEXPECTED_STR}, + { -ENOTRECOVERABLE, CPE_EPANIC_STR}, + { -ENOSPC, CPE_ENORESOURCE_STR}, + { -EBADR, CPE_EHANDLE_STR}, + { -EALREADY, CPE_EALREADY_STR}, + { -EPERM, CPE_ENOTREADY_STR}, + { -EINPROGRESS, CPE_EPENDING_STR}, + { -EBUSY, CPE_EBUSY_STR}, + { -ECANCELED, CPE_EABORTED_STR}, + { -EAGAIN, CPE_EPREEMPTED_STR}, + { -EAGAIN, CPE_ECONTINUE_STR}, + { -EAGAIN, CPE_EIMMEDIATE_STR}, + { -EAGAIN, CPE_ENOTIMPL_STR}, + { -ENODATA, CPE_ENEEDMORE_STR}, + { -EADV, CPE_ERR_MAX_STR}, + { -ENOMEM, CPE_ENOMEMORY_STR}, + { -ENODEV, CPE_ENOTEXIST_STR}, + { -EADV, CPE_ETERMINATED_STR}, + { -EADV, CPE_ERR_MAX_STR}, +}; + +static inline int cpe_err_get_lnx_err_code(u32 cpe_error) +{ + if (cpe_error > CPE_ERR_MAX) + return cpe_err_code_info[CPE_ERR_MAX].lnx_err_code; + else + return cpe_err_code_info[cpe_error].lnx_err_code; +} + +static inline char *cpe_err_get_err_str(u32 cpe_error) +{ + if (cpe_error > CPE_ERR_MAX) + return cpe_err_code_info[CPE_ERR_MAX].cpe_err_str; + else + return cpe_err_code_info[cpe_error].cpe_err_str; +} + +#endif diff --git a/audio-kernel/asoc/codecs/csra66x0/Kbuild b/audio-kernel/asoc/codecs/csra66x0/Kbuild new file mode 100644 index 000000000..4b1a3ba1a --- /dev/null +++ b/audio-kernel/asoc/codecs/csra66x0/Kbuild @@ -0,0 +1,109 @@ +# We can build either as part of a standalone Kernel build or as +# an external module. Determine which mechanism is being used +ifeq ($(MODNAME),) + KERNEL_BUILD := 1 +else + KERNEL_BUILD := 0 +endif + + +ifeq ($(KERNEL_BUILD), 1) + # These are configurable via Kconfig for kernel-based builds + # Need to explicitly configure for Android-based builds + AUDIO_BLD_DIR := $(shell pwd)/kernel/msm-4.14 + AUDIO_ROOT := $(AUDIO_BLD_DIR)/techpack/audio +endif + +ifeq ($(KERNEL_BUILD), 0) + ifeq ($(CONFIG_ARCH_QCS405), y) + include $(AUDIO_ROOT)/config/qcs405auto.conf + export + INCS += -include $(AUDIO_ROOT)/config/qcs405autoconf.h + endif +endif + +# As per target team, build is done as follows: +# Defconfig : build with default flags +# Slub : defconfig + CONFIG_SLUB_DEBUG := y + +# CONFIG_SLUB_DEBUG_ON := y + CONFIG_PAGE_POISONING := y +# Perf : Using appropriate msmXXXX-perf_defconfig +# +# Shipment builds (user variants) should not have any debug feature +# enabled. This is identified using 'TARGET_BUILD_VARIANT'. Slub builds +# are identified using the CONFIG_SLUB_DEBUG_ON configuration. Since +# there is no other way to identify defconfig builds, QTI internal +# representation of perf builds (identified using the string 'perf'), +# is used to identify if the build is a slub or defconfig one. This +# way no critical debug feature will be enabled for perf and shipment +# builds. Other OEMs are also protected using the TARGET_BUILD_VARIANT +# config. + +############ UAPI ############ +UAPI_DIR := uapi +UAPI_INC := -I$(AUDIO_ROOT)/include/$(UAPI_DIR) + +############ COMMON ############ +COMMON_DIR := include +COMMON_INC := -I$(AUDIO_ROOT)/$(COMMON_DIR) + +############ CSRA66X0 ############ + +# for CSRA66X0 Codec +ifdef CONFIG_SND_SOC_CSRA66X0 + CSRA66X0_OBJS += csra66x0.o +endif + +LINUX_INC += -Iinclude/linux + +INCS += $(COMMON_INC) \ + $(UAPI_INC) + +#EXTRA_CFLAGS += $(INCS) +ccflags-y += $(INCS) + + +CDEFINES += -DANI_LITTLE_BYTE_ENDIAN \ + -DANI_LITTLE_BIT_ENDIAN \ + -DDOT11F_LITTLE_ENDIAN_HOST \ + -DANI_COMPILER_TYPE_GCC \ + -DANI_OS_TYPE_ANDROID=6 \ + -DPTT_SOCK_SVC_ENABLE \ + -Wall\ + -Werror\ + -D__linux__ + +KBUILD_CPPFLAGS += $(CDEFINES) + +# Currently, for versions of gcc which support it, the kernel Makefile +# is disabling the maybe-uninitialized warning. Re-enable it for the +# AUDIO driver. Note that we must use EXTRA_CFLAGS here so that it +# will override the kernel settings. +ifeq ($(call cc-option-yn, -Wmaybe-uninitialized),y) +#EXTRA_CFLAGS += -Wmaybe-uninitialized +ccflags-y += -Wmaybe-uninitialized +endif +#EXTRA_CFLAGS += -Wmissing-prototypes + +ifeq ($(call cc-option-yn, -Wheader-guard),y) +#EXTRA_CFLAGS += -Wheader-guard +ccflags-y += -Wheader-guard +endif + +ifeq ($(KERNEL_BUILD), 0) +KBUILD_EXTRA_SYMBOLS +=$(OUT)/obj/vendor/qcom/opensource/audio-kernel/ipc/Module.symvers +KBUILD_EXTRA_SYMBOLS +=$(OUT)/obj/vendor/qcom/opensource/audio-kernel/dsp/Module.symvers +KBUILD_EXTRA_SYMBOLS +=$(OUT)/obj/vendor/qcom/opensource/audio-kernel/asoc/Module.symvers +KBUILD_EXTRA_SYMBOLS +=$(OUT)/obj/vendor/qcom/opensource/audio-kernel/asoc/codecs/Module.symvers +KBUILD_EXTRA_SYMBOLS +=$(OUT)/obj/vendor/qcom/opensource/audio-kernel/soc/Module.symvers +endif + +ifeq ($(CONFIG_SND_SOC_GCOV), y) +GCOV_PROFILE := y +endif + +# Module information used by KBuild framework +obj-$(CONFIG_SND_SOC_CSRA66X0) += csra66x0_dlkm.o +csra66x0_dlkm-y := $(CSRA66X0_OBJS) + +# inject some build related information +DEFINES += -DBUILD_TIMESTAMP=\"$(shell date -u +'%Y-%m-%dT%H:%M:%SZ')\" diff --git a/audio-kernel/asoc/codecs/csra66x0/csra66x0.c b/audio-kernel/asoc/codecs/csra66x0/csra66x0.c new file mode 100644 index 000000000..2189f9bfd --- /dev/null +++ b/audio-kernel/asoc/codecs/csra66x0/csra66x0.c @@ -0,0 +1,1040 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "csra66x0.h" + +/* CSRA66X0 register default values */ +static struct reg_default csra66x0_reg_defaults[] = { + {CSRA66X0_AUDIO_IF_RX_CONFIG1, 0x00}, + {CSRA66X0_AUDIO_IF_RX_CONFIG2, 0x0B}, + {CSRA66X0_AUDIO_IF_RX_CONFIG3, 0x0F}, + {CSRA66X0_AUDIO_IF_TX_EN, 0x00}, + {CSRA66X0_AUDIO_IF_TX_CONFIG1, 0x6B}, + {CSRA66X0_AUDIO_IF_TX_CONFIG2, 0x02}, + {CSRA66X0_I2C_DEVICE_ADDRESS, 0x0D}, + {CSRA66X0_CHIP_ID_FA, 0x39}, + {CSRA66X0_ROM_VER_FA, 0x08}, + {CSRA66X0_CHIP_REV_0_FA, 0x05}, + {CSRA66X0_CHIP_REV_1_FA, 0x03}, + {CSRA66X0_CH1_MIX_SEL, 0x01}, + {CSRA66X0_CH2_MIX_SEL, 0x10}, + {CSRA66X0_CH1_SAMPLE1_SCALE_0, 0x00}, + {CSRA66X0_CH1_SAMPLE1_SCALE_1, 0x20}, + {CSRA66X0_CH1_SAMPLE3_SCALE_0, 0x00}, + {CSRA66X0_CH1_SAMPLE3_SCALE_1, 0x20}, + {CSRA66X0_CH1_SAMPLE5_SCALE_0, 0x00}, + {CSRA66X0_CH1_SAMPLE5_SCALE_1, 0x20}, + {CSRA66X0_CH1_SAMPLE7_SCALE_0, 0x00}, + {CSRA66X0_CH1_SAMPLE7_SCALE_1, 0x20}, + {CSRA66X0_CH1_SAMPLE2_SCALE_0, 0x00}, + {CSRA66X0_CH1_SAMPLE2_SCALE_1, 0x20}, + {CSRA66X0_CH1_SAMPLE4_SCALE_0, 0x00}, + {CSRA66X0_CH1_SAMPLE4_SCALE_1, 0x20}, + {CSRA66X0_CH1_SAMPLE6_SCALE_0, 0x00}, + {CSRA66X0_CH1_SAMPLE6_SCALE_1, 0x20}, + {CSRA66X0_CH1_SAMPLE8_SCALE_0, 0x00}, + {CSRA66X0_CH1_SAMPLE8_SCALE_1, 0x20}, + {CSRA66X0_CH2_SAMPLE1_SCALE_0, 0x00}, + {CSRA66X0_CH2_SAMPLE1_SCALE_1, 0x20}, + {CSRA66X0_CH2_SAMPLE3_SCALE_0, 0x00}, + {CSRA66X0_CH2_SAMPLE3_SCALE_1, 0x20}, + {CSRA66X0_CH2_SAMPLE5_SCALE_0, 0x00}, + {CSRA66X0_CH2_SAMPLE5_SCALE_1, 0x20}, + {CSRA66X0_CH2_SAMPLE7_SCALE_0, 0x00}, + {CSRA66X0_CH2_SAMPLE7_SCALE_1, 0x20}, + {CSRA66X0_CH2_SAMPLE2_SCALE_0, 0x00}, + {CSRA66X0_CH2_SAMPLE2_SCALE_1, 0x20}, + {CSRA66X0_CH2_SAMPLE4_SCALE_0, 0x00}, + {CSRA66X0_CH2_SAMPLE4_SCALE_1, 0x20}, + {CSRA66X0_CH2_SAMPLE6_SCALE_0, 0x00}, + {CSRA66X0_CH2_SAMPLE6_SCALE_1, 0x20}, + {CSRA66X0_CH2_SAMPLE8_SCALE_0, 0x00}, + {CSRA66X0_CH2_SAMPLE8_SCALE_1, 0x20}, + {CSRA66X0_VOLUME_CONFIG_FA, 0x26}, + {CSRA66X0_STARTUP_DELAY_FA, 0x00}, + {CSRA66X0_CH1_VOLUME_0_FA, 0x19}, + {CSRA66X0_CH1_VOLUME_1_FA, 0x01}, + {CSRA66X0_CH2_VOLUME_0_FA, 0x19}, + {CSRA66X0_CH2_VOLUME_1_FA, 0x01}, + {CSRA66X0_QUAD_ENC_COUNT_0_FA, 0x00}, + {CSRA66X0_QUAD_ENC_COUNT_1_FA, 0x00}, + {CSRA66X0_SOFT_CLIP_CONFIG, 0x00}, + {CSRA66X0_CH1_HARD_CLIP_THRESH, 0x00}, + {CSRA66X0_CH2_HARD_CLIP_THRESH, 0x00}, + {CSRA66X0_SOFT_CLIP_THRESH, 0x00}, + {CSRA66X0_DS_ENABLE_THRESH_0, 0x05}, + {CSRA66X0_DS_ENABLE_THRESH_1, 0x00}, + {CSRA66X0_DS_TARGET_COUNT_0, 0x00}, + {CSRA66X0_DS_TARGET_COUNT_1, 0xFF}, + {CSRA66X0_DS_TARGET_COUNT_2, 0xFF}, + {CSRA66X0_DS_DISABLE_THRESH_0, 0x0F}, + {CSRA66X0_DS_DISABLE_THRESH_1, 0x00}, + {CSRA66X0_DCA_CTRL, 0x07}, + {CSRA66X0_CH1_DCA_THRESH, 0x40}, + {CSRA66X0_CH2_DCA_THRESH, 0x40}, + {CSRA66X0_DCA_ATTACK_RATE, 0x00}, + {CSRA66X0_DCA_RELEASE_RATE, 0x00}, + {CSRA66X0_CH1_OUTPUT_INVERT_EN, 0x00}, + {CSRA66X0_CH2_OUTPUT_INVERT_EN, 0x00}, + {CSRA66X0_CH1_176P4K_DELAY, 0x00}, + {CSRA66X0_CH2_176P4K_DELAY, 0x00}, + {CSRA66X0_CH1_192K_DELAY, 0x00}, + {CSRA66X0_CH2_192K_DELAY, 0x00}, + {CSRA66X0_DEEMP_CONFIG_FA, 0x00}, + {CSRA66X0_CH1_TREBLE_GAIN_CTRL_FA, 0x00}, + {CSRA66X0_CH2_TREBLE_GAIN_CTRL_FA, 0x00}, + {CSRA66X0_CH1_TREBLE_FC_CTRL_FA, 0x00}, + {CSRA66X0_CH2_TREBLE_FC_CTRL_FA, 0x00}, + {CSRA66X0_CH1_BASS_GAIN_CTRL_FA, 0x00}, + {CSRA66X0_CH2_BASS_GAIN_CTRL_FA, 0x00}, + {CSRA66X0_CH1_BASS_FC_CTRL_FA, 0x00}, + {CSRA66X0_CH2_BASS_FC_CTRL_FA, 0x00}, + {CSRA66X0_FILTER_SEL_8K, 0x00}, + {CSRA66X0_FILTER_SEL_11P025K, 0x00}, + {CSRA66X0_FILTER_SEL_16K, 0x00}, + {CSRA66X0_FILTER_SEL_22P05K, 0x00}, + {CSRA66X0_FILTER_SEL_32K, 0x00}, + {CSRA66X0_FILTER_SEL_44P1K_48K, 0x00}, + {CSRA66X0_FILTER_SEL_88P2K_96K, 0x00}, + {CSRA66X0_FILTER_SEL_176P4K_192K, 0x00}, + /* RESERVED */ + {CSRA66X0_USER_DSP_CTRL, 0x00}, + {CSRA66X0_TEST_TONE_CTRL, 0x00}, + {CSRA66X0_TEST_TONE_FREQ_0, 0x00}, + {CSRA66X0_TEST_TONE_FREQ_1, 0x00}, + {CSRA66X0_TEST_TONE_FREQ_2, 0x00}, + {CSRA66X0_AUDIO_RATE_CTRL_FA, 0x08}, + {CSRA66X0_MODULATION_INDEX_CTRL, 0x3F}, + {CSRA66X0_MODULATION_INDEX_COUNT, 0x10}, + {CSRA66X0_MIN_MODULATION_PULSE_WIDTH, 0x7A}, + {CSRA66X0_DEAD_TIME_CTRL, 0x00}, + {CSRA66X0_DEAD_TIME_THRESHOLD_0, 0xE7}, + {CSRA66X0_DEAD_TIME_THRESHOLD_1, 0x26}, + {CSRA66X0_DEAD_TIME_THRESHOLD_2, 0x40}, + {CSRA66X0_CH1_LOW_SIDE_DLY, 0x00}, + {CSRA66X0_CH2_LOW_SIDE_DLY, 0x00}, + {CSRA66X0_SPECTRUM_CTRL, 0x00}, + /* RESERVED */ + {CSRA66X0_SPECTRUM_SPREAD_CTRL, 0x0C}, + /* RESERVED */ + {CSRA66X0_EXT_PA_PROTECT_POLARITY, 0x03}, + {CSRA66X0_TEMP0_BACKOFF_COMP_VALUE, 0x98}, + {CSRA66X0_TEMP0_SHUTDOWN_COMP_VALUE, 0xA3}, + {CSRA66X0_TEMP1_BACKOFF_COMP_VALUE, 0x98}, + {CSRA66X0_TEMP1_SHUTDOWN_COMP_VALUE, 0xA3}, + {CSRA66X0_TEMP_PROT_BACKOFF, 0x00}, + {CSRA66X0_TEMP_READ0_FA, 0x00}, + {CSRA66X0_TEMP_READ1_FA, 0x00}, + {CSRA66X0_CHIP_STATE_CTRL_FA, 0x02}, + /* RESERVED */ + {CSRA66X0_PWM_OUTPUT_CONFIG, 0x00}, + {CSRA66X0_MISC_CONTROL_STATUS_0, 0x08}, + {CSRA66X0_MISC_CONTROL_STATUS_1_FA, 0x40}, + {CSRA66X0_PIO0_SELECT, 0x00}, + {CSRA66X0_PIO1_SELECT, 0x00}, + {CSRA66X0_PIO2_SELECT, 0x00}, + {CSRA66X0_PIO3_SELECT, 0x00}, + {CSRA66X0_PIO4_SELECT, 0x00}, + {CSRA66X0_PIO5_SELECT, 0x00}, + {CSRA66X0_PIO6_SELECT, 0x00}, + {CSRA66X0_PIO7_SELECT, 0x00}, + {CSRA66X0_PIO8_SELECT, 0x00}, + {CSRA66X0_PIO_DIRN0, 0xFF}, + {CSRA66X0_PIO_DIRN1, 0x01}, + {CSRA66X0_PIO_PULL_EN0, 0xFF}, + {CSRA66X0_PIO_PULL_EN1, 0x01}, + {CSRA66X0_PIO_PULL_DIR0, 0x00}, + {CSRA66X0_PIO_PULL_DIR1, 0x00}, + {CSRA66X0_PIO_DRIVE_OUT0_FA, 0x00}, + {CSRA66X0_PIO_DRIVE_OUT1_FA, 0x00}, + {CSRA66X0_PIO_STATUS_IN0_FA, 0x00}, + {CSRA66X0_PIO_STATUS_IN1_FA, 0x00}, + /* RESERVED */ + {CSRA66X0_IRQ_OUTPUT_ENABLE, 0x00}, + {CSRA66X0_IRQ_OUTPUT_POLARITY, 0x01}, + {CSRA66X0_IRQ_OUTPUT_STATUS_FA, 0x00}, + {CSRA66X0_CLIP_DCA_STATUS_FA, 0x00}, + {CSRA66X0_CHIP_STATE_STATUS_FA, 0x02}, + {CSRA66X0_FAULT_STATUS_FA, 0x00}, + {CSRA66X0_OTP_STATUS_FA, 0x00}, + {CSRA66X0_AUDIO_IF_STATUS_FA, 0x00}, + /* RESERVED */ + {CSRA66X0_DSP_SATURATION_STATUS_FA, 0x00}, + {CSRA66X0_AUDIO_RATE_STATUS_FA, 0x00}, + /* RESERVED */ + {CSRA66X0_DISABLE_PWM_OUTPUT, 0x00}, + /* RESERVED */ + {CSRA66X0_OTP_VER_FA, 0x03}, + {CSRA66X0_RAM_VER_FA, 0x02}, + /* RESERVED */ + {CSRA66X0_AUDIO_SATURATION_FLAGS_FA, 0x00}, + {CSRA66X0_DCOFFSET_CHAN_1_01_FA, 0x00}, + {CSRA66X0_DCOFFSET_CHAN_1_02_FA, 0x00}, + {CSRA66X0_DCOFFSET_CHAN_1_03_FA, 0x00}, + {CSRA66X0_DCOFFSET_CHAN_2_01_FA, 0x00}, + {CSRA66X0_DCOFFSET_CHAN_2_02_FA, 0x00}, + {CSRA66X0_DCOFFSET_CHAN_2_03_FA, 0x00}, + {CSRA66X0_FORCED_PA_SWITCHING_CTRL, 0x90}, + {CSRA66X0_PA_FORCE_PULSE_WIDTH, 0x07}, + {CSRA66X0_PA_HIGH_MODULATION_CTRL_CH1, 0x00}, + /* RESERVED */ + {CSRA66X0_HIGH_MODULATION_THRESHOLD_LOW, 0xD4}, + {CSRA66X0_HIGH_MODULATION_THRESHOLD_HIGH, 0x78}, + /* RESERVED */ + {CSRA66X0_PA_FREEZE_CTRL, 0x00}, + {CSRA66X0_DCA_FREEZE_CTRL, 0x3C}, + /* RESERVED */ +}; + +static bool csra66x0_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CSRA66X0_CHIP_ID_FA: + case CSRA66X0_ROM_VER_FA: + case CSRA66X0_CHIP_REV_0_FA: + case CSRA66X0_CHIP_REV_1_FA: + case CSRA66X0_TEMP_READ0_FA: + case CSRA66X0_TEMP_READ1_FA: + case CSRA66X0_MISC_CONTROL_STATUS_1_FA: + case CSRA66X0_IRQ_OUTPUT_STATUS_FA: + case CSRA66X0_CLIP_DCA_STATUS_FA: + case CSRA66X0_CHIP_STATE_STATUS_FA: + case CSRA66X0_FAULT_STATUS_FA: + case CSRA66X0_OTP_STATUS_FA: + case CSRA66X0_AUDIO_IF_STATUS_FA: + case CSRA66X0_DSP_SATURATION_STATUS_FA: + case CSRA66X0_AUDIO_RATE_STATUS_FA: + return true; + default: + return false; + } +} + +static bool csra66x0_writeable_registers(struct device *dev, unsigned int reg) +{ + if ((reg >= CSRA66X0_AUDIO_IF_RX_CONFIG1) + && (reg <= CSRA66X0_MAX_REGISTER_ADDR)) + return true; + + return false; +} + +static bool csra66x0_readable_registers(struct device *dev, unsigned int reg) +{ + if ((reg >= CSRA66X0_AUDIO_IF_RX_CONFIG1) + && (reg <= CSRA66X0_MAX_REGISTER_ADDR)) + return true; + + return false; +} + +/* codec private data */ +struct csra66x0_priv { + struct regmap *regmap; + struct snd_soc_codec *codec; + int spk_volume_ch1; + int spk_volume_ch2; + int irq; + int vreg_gpio; + u32 irq_active_low; + u32 in_cluster; + u32 is_master; +#if IS_ENABLED(CONFIG_DEBUG_FS) + struct dentry *debugfs_dir; + struct dentry *debugfs_file_wo; + struct dentry *debugfs_file_ro; +#endif /* CONFIG_DEBUG_FS */ +}; + +struct csra66x0_cluster_device { + struct csra66x0_priv *csra66x0_ptr; + const char *csra66x0_prefix; +}; + +struct csra66x0_cluster_device csra_clust_dev_tbl[] = { + {NULL, "CSRA_12"}, + {NULL, "CSRA_34"}, + {NULL, "CSRA_56"}, + {NULL, "CSRA_78"}, + {NULL, "CSRA_9A"}, + {NULL, "CSRA_BC"} +}; + +#if IS_ENABLED(CONFIG_DEBUG_FS) +static int debugfs_codec_open_op(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static int debugfs_get_parameters(char *buf, u32 *param1, int num_of_par) +{ + char *token; + int base, cnt; + + token = strsep(&buf, " "); + for (cnt = 0; cnt < num_of_par; cnt++) { + if (token) { + if ((token[1] == 'x') || (token[1] == 'X')) + base = 16; + else + base = 10; + + if (kstrtou32(token, base, ¶m1[cnt]) != 0) + return -EINVAL; + + token = strsep(&buf, " "); + } else { + return -EINVAL; + } + } + return 0; +} + +static ssize_t debugfs_codec_write_op(struct file *filp, + const char __user *ubuf, size_t cnt, loff_t *ppos) +{ + struct csra66x0_priv *csra66x0 = + (struct csra66x0_priv *) filp->private_data; + struct snd_soc_codec *codec = csra66x0->codec; + char lbuf[32]; + int rc; + u32 param[2]; + + if (!filp || !ppos || !ubuf) + return -EINVAL; + if (cnt > sizeof(lbuf) - 1) + return -EINVAL; + rc = copy_from_user(lbuf, ubuf, cnt); + if (rc) + return -EFAULT; + lbuf[cnt] = '\0'; + rc = debugfs_get_parameters(lbuf, param, 2); + if ((param[0] < CSRA66X0_AUDIO_IF_RX_CONFIG1) + || (param[0] > CSRA66X0_MAX_REGISTER_ADDR)) { + dev_err(codec->dev, "%s: register address 0x%04X out of range\n", + __func__, param[0]); + return -EINVAL; + } + if ((param[1] < 0) || (param[1] > 255)) { + dev_err(codec->dev, "%s: register data 0x%02X out of range\n", + __func__, param[1]); + return -EINVAL; + } + if (rc == 0) + { + rc = cnt; + dev_info(codec->dev, "%s: reg[0x%04X]=0x%02X\n", + __func__, param[0], param[1]); + snd_soc_write(codec, param[0], param[1]); + } else { + dev_err(codec->dev, "%s: write to register addr=0x%04X failed\n", + __func__, param[0]); + } + return rc; +} + +static ssize_t debugfs_csra66x0_reg_show(struct snd_soc_codec *codec, + char __user *ubuf, size_t count, loff_t *ppos) +{ + int i, reg_val, len; + ssize_t total = 0; + char tmp_buf[20]; + + if (!ubuf || !ppos || !codec || *ppos < 0) + return -EINVAL; + + for (i = ((int) *ppos + CSRA66X0_BASE); + i <= CSRA66X0_MAX_REGISTER_ADDR; i++) { + reg_val = snd_soc_read(codec, i); + len = snprintf(tmp_buf, 20, "0x%04X: 0x%02X\n", i, (reg_val & 0xFF)); + if ((total + len) >= count - 1) + break; + if (copy_to_user((ubuf + total), tmp_buf, len)) { + dev_err(codec->dev, "%s: fail to copy reg dump\n", __func__); + total = -EFAULT; + goto copy_err; + } + *ppos += len; + total += len; + } + +copy_err: + return total; +} + +static ssize_t debugfs_codec_read_op(struct file *filp, + char __user *ubuf, size_t cnt, loff_t *ppos) +{ + struct csra66x0_priv *csra66x0 = + (struct csra66x0_priv *) filp->private_data; + struct snd_soc_codec *codec = csra66x0->codec; + ssize_t ret_cnt; + + if (!filp || !ppos || !ubuf || *ppos < 0) + return -EINVAL; + ret_cnt = debugfs_csra66x0_reg_show(codec, ubuf, cnt, ppos); + return ret_cnt; +} + +static const struct file_operations debugfs_codec_ops = { + .open = debugfs_codec_open_op, + .write = debugfs_codec_write_op, + .read = debugfs_codec_read_op, +}; +#endif /* CONFIG_DEBUG_FS */ + +/* + * CSRA66X0 Controls + */ +static const DECLARE_TLV_DB_SCALE(csra66x0_volume_tlv, -9000, 25, 0); +static const DECLARE_TLV_DB_RANGE(csra66x0_bass_treble_tlv, + 0, 0, TLV_DB_SCALE_ITEM(0, 0, 0), + 1, 15, TLV_DB_SCALE_ITEM(-1500, 100, 0), + 16, 30, TLV_DB_SCALE_ITEM(100, 100, 0) +); + +static int csra66x0_get_volume(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + unsigned int reg_l = mc->reg; + unsigned int reg_r = mc->rreg; + unsigned int val_l, val_r; + + val_l = (snd_soc_read(codec, reg_l) & 0xff) | + ((snd_soc_read(codec, + CSRA66X0_CH1_VOLUME_1_FA) & (0x01)) << 8); + val_r = (snd_soc_read(codec, reg_r) & 0xff) | + ((snd_soc_read(codec, + CSRA66X0_CH2_VOLUME_1_FA) & (0x01)) << 8); + ucontrol->value.integer.value[0] = val_l; + ucontrol->value.integer.value[1] = val_r; + return 0; +} + +static int csra66x0_set_volume(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct csra66x0_priv *csra66x0 = snd_soc_codec_get_drvdata(codec); + unsigned int reg_l = mc->reg; + unsigned int reg_r = mc->rreg; + unsigned int val_l[2]; + unsigned int val_r[2]; + + csra66x0->spk_volume_ch1 = (ucontrol->value.integer.value[0]); + csra66x0->spk_volume_ch2 = (ucontrol->value.integer.value[1]); + val_l[0] = csra66x0->spk_volume_ch1 & SPK_VOLUME_LSB_MSK; + val_l[1] = (csra66x0->spk_volume_ch1 & SPK_VOLUME_MSB_MSK) ? 1 : 0; + val_r[0] = csra66x0->spk_volume_ch2 & SPK_VOLUME_LSB_MSK; + val_r[1] = (csra66x0->spk_volume_ch2 & SPK_VOLUME_MSB_MSK) ? 1 : 0; + snd_soc_write(codec, reg_l, val_l[0]); + snd_soc_write(codec, reg_r, val_r[0]); + snd_soc_write(codec, CSRA66X0_CH1_VOLUME_1_FA, val_l[1]); + snd_soc_write(codec, CSRA66X0_CH2_VOLUME_1_FA, val_r[1]); + return 0; +} + +/* enumerated controls */ +static const char * const csra66x0_mute_output_text[] = {"PLAY", "MUTE"}; +static const char * const csra66x0_output_invert_text[] = { + "UNCHANGED", "INVERTED"}; +static const char * const csra66x0_deemp_config_text[] = { + "DISABLED", "ENABLED"}; + +SOC_ENUM_SINGLE_DECL(csra66x0_mute_output_enum, + CSRA66X0_MISC_CONTROL_STATUS_1_FA, 2, + csra66x0_mute_output_text); +SOC_ENUM_SINGLE_DECL(csra66x0_ch1_output_invert_enum, + CSRA66X0_CH1_OUTPUT_INVERT_EN, 0, + csra66x0_output_invert_text); +SOC_ENUM_SINGLE_DECL(csra66x0_ch2_output_invert_enum, + CSRA66X0_CH2_OUTPUT_INVERT_EN, 0, + csra66x0_output_invert_text); +SOC_ENUM_DOUBLE_DECL(csra66x0_deemp_config_enum, + CSRA66X0_DEEMP_CONFIG_FA, 0, 1, + csra66x0_deemp_config_text); + +static const struct snd_kcontrol_new csra66x0_snd_controls[] = { + /* volume */ + SOC_DOUBLE_R_EXT_TLV("PA VOLUME", CSRA66X0_CH1_VOLUME_0_FA, + CSRA66X0_CH2_VOLUME_0_FA, 0, 0x1C9, 0, + csra66x0_get_volume, csra66x0_set_volume, + csra66x0_volume_tlv), + + /* bass treble */ + SOC_DOUBLE_R_TLV("PA BASS GAIN", CSRA66X0_CH1_BASS_GAIN_CTRL_FA, + CSRA66X0_CH2_BASS_GAIN_CTRL_FA, 0, 0x1E, 0, + csra66x0_bass_treble_tlv), + SOC_DOUBLE_R_TLV("PA TREBLE GAIN", CSRA66X0_CH1_TREBLE_GAIN_CTRL_FA, + CSRA66X0_CH2_TREBLE_GAIN_CTRL_FA, 0, 0x1E, 0, + csra66x0_bass_treble_tlv), + SOC_DOUBLE_R("PA BASS_XOVER FREQ", CSRA66X0_CH1_BASS_FC_CTRL_FA, + CSRA66X0_CH2_BASS_FC_CTRL_FA, 0, 2, 0), + SOC_DOUBLE_R("PA TREBLE_XOVER FREQ", CSRA66X0_CH1_TREBLE_FC_CTRL_FA, + CSRA66X0_CH2_TREBLE_FC_CTRL_FA, 0, 2, 0), + + /* switch */ + SOC_ENUM("PA MUTE_OUTPUT SWITCH", csra66x0_mute_output_enum), + SOC_ENUM("PA DE-EMPHASIS SWITCH", csra66x0_deemp_config_enum), +}; + +static const struct snd_kcontrol_new csra_mix_switch[] = { + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_soc_dapm_widget csra66x0_dapm_widgets[] = { + SND_SOC_DAPM_INPUT("IN"), + SND_SOC_DAPM_MIXER("MIXER", SND_SOC_NOPM, 0, 0, + csra_mix_switch, ARRAY_SIZE(csra_mix_switch)), + SND_SOC_DAPM_DAC("DAC", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_PGA("PGA", CSRA66X0_CHIP_STATE_CTRL_FA, 0, 0, NULL, 0), + SND_SOC_DAPM_OUTPUT("SPKR"), + + SND_SOC_DAPM_SUPPLY("POWER", CSRA66X0_CHIP_STATE_CTRL_FA, + 1, 1, NULL, 0), +}; + +static const struct snd_soc_dapm_route csra66x0_dapm_routes[] = { + {"MIXER", "Switch", "IN"}, + {"DAC", NULL, "MIXER"}, + {"PGA", NULL, "DAC"}, + {"SPKR", NULL, "PGA"}, + {"SPKR", NULL, "POWER"}, +}; + +static int csra66x0_init(struct csra66x0_priv *csra66x0) +{ + struct snd_soc_codec *codec = csra66x0->codec; + + dev_dbg(codec->dev, "%s: initialize %s\n", + __func__, codec->component.name); + /* config */ + snd_soc_write(codec, CSRA66X0_CHIP_STATE_CTRL_FA, CONFIG_STATE); + /* settle time in HW is min. 500ms before proceeding */ + msleep(500); + + /* setup */ + snd_soc_write(codec, CSRA66X0_MISC_CONTROL_STATUS_0, 0x09); + snd_soc_write(codec, CSRA66X0_TEMP_PROT_BACKOFF, 0x0C); + snd_soc_write(codec, CSRA66X0_EXT_PA_PROTECT_POLARITY, 0x03); + snd_soc_write(codec, CSRA66X0_PWM_OUTPUT_CONFIG, 0xC8); + csra66x0->spk_volume_ch1 = SPK_VOLUME_M20DB; + csra66x0->spk_volume_ch2 = SPK_VOLUME_M20DB; + snd_soc_write(codec, CSRA66X0_CH1_VOLUME_0_FA, SPK_VOLUME_M20DB_LSB); + snd_soc_write(codec, CSRA66X0_CH2_VOLUME_0_FA, SPK_VOLUME_M20DB_LSB); + snd_soc_write(codec, CSRA66X0_CH1_VOLUME_1_FA, SPK_VOLUME_M20DB_MSB); + snd_soc_write(codec, CSRA66X0_CH2_VOLUME_1_FA, SPK_VOLUME_M20DB_MSB); + + snd_soc_write(codec, CSRA66X0_DEAD_TIME_CTRL, 0x0); + snd_soc_write(codec, CSRA66X0_DEAD_TIME_THRESHOLD_0, 0xE7); + snd_soc_write(codec, CSRA66X0_DEAD_TIME_THRESHOLD_1, 0x26); + snd_soc_write(codec, CSRA66X0_DEAD_TIME_THRESHOLD_2, 0x40); + + snd_soc_write(codec, CSRA66X0_MIN_MODULATION_PULSE_WIDTH, 0x7A); + snd_soc_write(codec, CSRA66X0_CH1_HARD_CLIP_THRESH, 0x00); + snd_soc_write(codec, CSRA66X0_CH2_HARD_CLIP_THRESH, 0x00); + + snd_soc_write(codec, CSRA66X0_CH1_DCA_THRESH, 0x40); + snd_soc_write(codec, CSRA66X0_CH2_DCA_THRESH, 0x40); + snd_soc_write(codec, CSRA66X0_DCA_ATTACK_RATE, 0x00); + snd_soc_write(codec, CSRA66X0_DCA_RELEASE_RATE, 0x00); + + if (csra66x0->irq) { + snd_soc_write(codec, CSRA66X0_PIO0_SELECT, 0x1); + if (csra66x0->irq_active_low) + snd_soc_write(codec, CSRA66X0_IRQ_OUTPUT_POLARITY, 0x0); + else + snd_soc_write(codec, CSRA66X0_IRQ_OUTPUT_POLARITY, 0x1); + + snd_soc_write(codec, CSRA66X0_IRQ_OUTPUT_ENABLE, 0x01); + } else { + snd_soc_write(codec, CSRA66X0_IRQ_OUTPUT_ENABLE, 0x00); + } + /* settle time in HW is min. 500ms before slave initializing */ + msleep(500); + return 0; +} + +static int csra66x0_reset(struct csra66x0_priv *csra66x0) +{ + struct snd_soc_codec *codec = csra66x0->codec; + u16 val; + + val = snd_soc_read(codec, CSRA66X0_FAULT_STATUS_FA); + if (val & FAULT_STATUS_INTERNAL) + dev_dbg(codec->dev, "%s: FAULT_STATUS_INTERNAL 0x%X\n", + __func__, val); + if (val & FAULT_STATUS_OTP_INTEGRITY) + dev_dbg(codec->dev, "%s: FAULT_STATUS_OTP_INTEGRITY 0x%X\n", + __func__, val); + if (val & FAULT_STATUS_PADS2) + dev_dbg(codec->dev, "%s: FAULT_STATUS_PADS2 0x%X\n", + __func__, val); + if (val & FAULT_STATUS_SMPS) + dev_dbg(codec->dev, "%s: FAULT_STATUS_SMPS 0x%X\n", + __func__, val); + if (val & FAULT_STATUS_TEMP) + dev_dbg(codec->dev, "%s: FAULT_STATUS_TEMP 0x%X\n", + __func__, val); + if (val & FAULT_STATUS_PROTECT) + dev_dbg(codec->dev, "%s: FAULT_STATUS_PROTECT 0x%X\n", + __func__, val); + + dev_dbg(codec->dev, "%s: reset %s\n", + __func__, codec->component.name); + /* clear fault state and re-init */ + snd_soc_write(codec, CSRA66X0_FAULT_STATUS_FA, 0x00); + snd_soc_write(codec, CSRA66X0_IRQ_OUTPUT_STATUS_FA, 0x00); + /* apply reset to CSRA66X0 */ + val = snd_soc_read(codec, CSRA66X0_MISC_CONTROL_STATUS_1_FA); + snd_soc_write(codec, CSRA66X0_MISC_CONTROL_STATUS_1_FA, val | 0x08); + /* wait 500ms after reset to recover CSRA66X0 */ + msleep(500); + return 0; +} + +static int csra66x0_msconfig(struct csra66x0_priv *csra66x0) +{ + struct snd_soc_codec *codec = csra66x0->codec; + + dev_dbg(codec->dev, "%s: configure %s\n", + __func__, codec->component.name); + /* config */ + snd_soc_write(codec, CSRA66X0_CHIP_STATE_CTRL_FA, + CONFIG_STATE); + /* settle time in HW is min. 500ms before proceeding */ + msleep(500); + snd_soc_write(codec, CSRA66X0_PIO7_SELECT, 0x04); + snd_soc_write(codec, CSRA66X0_PIO8_SELECT, 0x04); + if (csra66x0->is_master) { + /* Master specific config */ + snd_soc_write(codec, CSRA66X0_PIO_PULL_EN0, 0xFF); + snd_soc_write(codec, CSRA66X0_PIO_PULL_DIR0, 0x80); + snd_soc_write(codec, CSRA66X0_PIO_PULL_EN1, 0x01); + snd_soc_write(codec, CSRA66X0_PIO_PULL_DIR1, 0x01); + } else { + /* Slave specific config */ + snd_soc_write(codec, CSRA66X0_PIO_PULL_EN0, 0x7F); + snd_soc_write(codec, CSRA66X0_PIO_PULL_EN1, 0x00); + } + snd_soc_write(codec, CSRA66X0_DCA_CTRL, 0x05); + return 0; +} + +static int csra66x0_soc_probe(struct snd_soc_codec *codec) +{ + struct csra66x0_priv *csra66x0 = snd_soc_codec_get_drvdata(codec); + struct snd_soc_dapm_context *dapm; + char name[50]; + unsigned int i, max_num_cluster_devices; + + csra66x0->codec = codec; + if (csra66x0->in_cluster) { + dapm = snd_soc_codec_get_dapm(codec); + dev_dbg(codec->dev, "%s: assign prefix %s to codec device %s\n", + __func__, codec->component.name_prefix, + codec->component.name); + + /* add device to cluster table */ + max_num_cluster_devices = sizeof(csra_clust_dev_tbl)/ + sizeof(csra_clust_dev_tbl[0]); + for (i = 0; i < max_num_cluster_devices; i++) { + if (!strncmp(codec->component.name_prefix, + csra_clust_dev_tbl[i].csra66x0_prefix, + strlen( + csra_clust_dev_tbl[i].csra66x0_prefix))) { + csra_clust_dev_tbl[i].csra66x0_ptr = csra66x0; + break; + } + if (i == max_num_cluster_devices-1) + dev_warn(codec->dev, + "%s: Unknown prefix %s of cluster device %s\n", + __func__, codec->component.name_prefix, + codec->component.name); + } + + /* master slave config */ + csra66x0_msconfig(csra66x0); + if (dapm->component) { + strlcpy(name, dapm->component->name_prefix, + sizeof(name)); + strlcat(name, " IN", sizeof(name)); + snd_soc_dapm_ignore_suspend(dapm, name); + strlcpy(name, dapm->component->name_prefix, + sizeof(name)); + strlcat(name, " SPKR", sizeof(name)); + snd_soc_dapm_ignore_suspend(dapm, name); + } + } + + /* common initialization */ + csra66x0_init(csra66x0); + return 0; +} + +static int csra66x0_soc_remove(struct snd_soc_codec *codec) +{ + snd_soc_write(codec, CSRA66X0_CHIP_STATE_CTRL_FA, STDBY_STATE); + return 0; +} + +static int csra66x0_soc_suspend(struct snd_soc_codec *codec) +{ + u16 state_reg = snd_soc_read(codec, CSRA66X0_CHIP_STATE_CTRL_FA) & 0xFC; + + snd_soc_write(codec, CSRA66X0_CHIP_STATE_CTRL_FA, state_reg | + STDBY_STATE); + return 0; +} + +static int csra66x0_soc_resume(struct snd_soc_codec *codec) +{ + u16 state_reg = snd_soc_read(codec, CSRA66X0_CHIP_STATE_CTRL_FA) & 0xFC; + + snd_soc_write(codec, CSRA66X0_CHIP_STATE_CTRL_FA, state_reg | + RUN_STATE); + return 0; +} + +static struct regmap *csra66x0_get_regmap(struct device *dev) +{ + struct csra66x0_priv *csra66x0_ctrl = dev_get_drvdata(dev); + + if (!csra66x0_ctrl) + return NULL; + return csra66x0_ctrl->regmap; +} + +static struct snd_soc_codec_driver soc_codec_drv_csra66x0 = { + .probe = csra66x0_soc_probe, + .remove = csra66x0_soc_remove, + .suspend = csra66x0_soc_suspend, + .resume = csra66x0_soc_resume, + .get_regmap = csra66x0_get_regmap, + .component_driver = { + .controls = csra66x0_snd_controls, + .num_controls = ARRAY_SIZE(csra66x0_snd_controls), + .dapm_widgets = csra66x0_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(csra66x0_dapm_widgets), + .dapm_routes = csra66x0_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(csra66x0_dapm_routes), + }, +}; + +static struct regmap_config csra66x0_regmap_config = { + .reg_bits = 16, + .val_bits = 8, + .cache_type = REGCACHE_RBTREE, + .reg_defaults = csra66x0_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(csra66x0_reg_defaults), + .max_register = CSRA66X0_MAX_REGISTER_ADDR, + .volatile_reg = csra66x0_volatile_register, + .writeable_reg = csra66x0_writeable_registers, + .readable_reg = csra66x0_readable_registers, +}; + +static irqreturn_t csra66x0_irq(int irq, void *data) +{ + struct csra66x0_priv *csra66x0 = (struct csra66x0_priv *) data; + struct snd_soc_codec *codec = csra66x0->codec; + u16 val; + unsigned int i, max_num_cluster_devices; + + /* Treat interrupt before codec is initialized as spurious */ + if (codec == NULL) + return IRQ_NONE; + + dev_dbg(codec->dev, "%s: csra66x0_interrupt triggered by %s\n", + __func__, codec->component.name); + + /* fault indication */ + val = snd_soc_read(codec, CSRA66X0_IRQ_OUTPUT_STATUS_FA) & 0x1; + if (!val) + return IRQ_HANDLED; + + if (csra66x0->in_cluster) { + /* reset all slave codecs */ + max_num_cluster_devices = + sizeof(csra_clust_dev_tbl) / + sizeof(csra_clust_dev_tbl[0]); + for (i = 0; i < max_num_cluster_devices; i++) { + if (i >= codec->component.card->num_aux_devs) + break; + if (csra_clust_dev_tbl[i].csra66x0_ptr == NULL) + continue; + if (csra_clust_dev_tbl[i].csra66x0_ptr->is_master) + continue; + csra66x0_reset(csra_clust_dev_tbl[i].csra66x0_ptr); + } + /* reset all master codecs */ + for (i = 0; i < max_num_cluster_devices; i++) { + if (i >= codec->component.card->num_aux_devs) + break; + if (csra_clust_dev_tbl[i].csra66x0_ptr == NULL) + continue; + if (csra_clust_dev_tbl[i].csra66x0_ptr->is_master) + csra66x0_reset( + csra_clust_dev_tbl[i].csra66x0_ptr); + } + /* recover all codecs */ + for (i = 0; i < max_num_cluster_devices; i++) { + if (i >= codec->component.card->num_aux_devs) + break; + if (csra_clust_dev_tbl[i].csra66x0_ptr == NULL) + continue; + csra66x0_msconfig(csra_clust_dev_tbl[i].csra66x0_ptr); + csra66x0_init(csra_clust_dev_tbl[i].csra66x0_ptr); + } + } else { + csra66x0_reset(csra66x0); + csra66x0_init(csra66x0); + } + return IRQ_HANDLED; +}; + +static const struct of_device_id csra66x0_of_match[] = { + { .compatible = "qcom,csra66x0", }, + { } +}; +MODULE_DEVICE_TABLE(of, csra66x0_of_match); + +#if IS_ENABLED(CONFIG_I2C) +static int csra66x0_i2c_probe(struct i2c_client *client_i2c, + const struct i2c_device_id *id) +{ + struct csra66x0_priv *csra66x0; + int ret, irq_trigger; + char debugfs_dir_name[32]; + + csra66x0 = devm_kzalloc(&client_i2c->dev, sizeof(struct csra66x0_priv), + GFP_KERNEL); + if (csra66x0 == NULL) + return -ENOMEM; + + csra66x0->regmap = devm_regmap_init_i2c(client_i2c, + &csra66x0_regmap_config); + if (IS_ERR(csra66x0->regmap)) { + ret = PTR_ERR(csra66x0->regmap); + dev_err(&client_i2c->dev, + "%s %d: Failed to allocate register map for I2C device: %d\n", + __func__, __LINE__, ret); + return ret; + } + + i2c_set_clientdata(client_i2c, csra66x0); + + /* get data from device tree */ + if (client_i2c->dev.of_node) { + /* cluster of multiple devices */ + ret = of_property_read_u32( + client_i2c->dev.of_node, "qcom,csra-cluster", + &csra66x0->in_cluster); + if (ret) { + dev_info(&client_i2c->dev, + "%s: qcom,csra-cluster property not defined in DT\n", __func__); + csra66x0->in_cluster = 0; + } + /* master or slave device */ + ret = of_property_read_u32( + client_i2c->dev.of_node, "qcom,csra-cluster-master", + &csra66x0->is_master); + if (ret) { + dev_info(&client_i2c->dev, + "%s: qcom,csra-cluster-master property not defined in DT, slave assumed\n", + __func__); + csra66x0->is_master = 0; + } + + /* gpio setup for vreg */ + csra66x0->vreg_gpio = of_get_named_gpio(client_i2c->dev.of_node, + "qcom,csra-vreg-en-gpio", 0); + if (!gpio_is_valid(csra66x0->vreg_gpio)) { + dev_err(&client_i2c->dev, "%s: %s property is not found %d\n", + __func__, "qcom,csra-vreg-en-gpio", + csra66x0->vreg_gpio); + return -ENODEV; + } + dev_dbg(&client_i2c->dev, "%s: vreg_en gpio %d\n", __func__, + csra66x0->vreg_gpio); + ret = gpio_request(csra66x0->vreg_gpio, dev_name(&client_i2c->dev)); + if (ret) { + if (ret == -EBUSY) { + /* GPIO was already requested */ + dev_dbg(&client_i2c->dev, + "%s: gpio %d is already set\n", + __func__, csra66x0->vreg_gpio); + } else { + dev_err(&client_i2c->dev, "%s: Failed to request gpio %d, err: %d\n", + __func__, csra66x0->vreg_gpio, ret); + } + } else { + gpio_direction_output(csra66x0->vreg_gpio, 1); + gpio_set_value(csra66x0->vreg_gpio, 0); + } + + /* register interrupt handle */ + if (client_i2c->irq) { + csra66x0->irq = client_i2c->irq; + /* interrupt polarity */ + ret = of_property_read_u32( + client_i2c->dev.of_node, "irq-active-low", + &csra66x0->irq_active_low); + if (ret) { + dev_info(&client_i2c->dev, + "%s: irq-active-low property not defined in DT\n", __func__); + csra66x0->irq_active_low = 0; + } + if (csra66x0->irq_active_low) + irq_trigger = IRQF_TRIGGER_LOW; + else + irq_trigger = IRQF_TRIGGER_HIGH; + + ret = devm_request_threaded_irq(&client_i2c->dev, + csra66x0->irq, NULL, csra66x0_irq, + irq_trigger | IRQF_ONESHOT, + "csra66x0_irq", csra66x0); + if (ret) { + dev_err(&client_i2c->dev, + "%s: Failed to request IRQ %d: %d\n", + __func__, csra66x0->irq, ret); + csra66x0->irq = 0; + } + } + } + +#if IS_ENABLED(CONFIG_DEBUG_FS) + /* debugfs interface */ + snprintf(debugfs_dir_name, sizeof(debugfs_dir_name), "%s-%s", + client_i2c->name, dev_name(&client_i2c->dev)); + csra66x0->debugfs_dir = debugfs_create_dir(debugfs_dir_name, NULL); + if (!csra66x0->debugfs_dir) { + dev_dbg(&client_i2c->dev, + "%s: Failed to create /sys/kernel/debug/%s for debugfs\n", + __func__, debugfs_dir_name); + return -ENOMEM; + } + csra66x0->debugfs_file_wo = debugfs_create_file( + "write_reg_val", S_IFREG | S_IRUGO, csra66x0->debugfs_dir, + (void *) csra66x0, + &debugfs_codec_ops); + if (!csra66x0->debugfs_file_wo) { + dev_dbg(&client_i2c->dev, + "%s: Failed to create /sys/kernel/debug/%s/write_reg_val\n", + __func__, debugfs_dir_name); + return -ENOMEM; + } + csra66x0->debugfs_file_ro = debugfs_create_file( + "show_reg_dump", S_IFREG | S_IRUGO, csra66x0->debugfs_dir, + (void *) csra66x0, + &debugfs_codec_ops); + if (!csra66x0->debugfs_file_ro) { + dev_dbg(&client_i2c->dev, + "%s: Failed to create /sys/kernel/debug/%s/show_reg_dump\n", + __func__, debugfs_dir_name); + return -ENOMEM; + } +#endif /* CONFIG_DEBUG_FS */ + + /* register codec */ + ret = snd_soc_register_codec(&client_i2c->dev, + &soc_codec_drv_csra66x0, NULL, 0); + if (ret != 0) { + dev_err(&client_i2c->dev, "%s %d: Failed to register codec: %d\n", + __func__, __LINE__, ret); + if (gpio_is_valid(csra66x0->vreg_gpio)) { + gpio_set_value(csra66x0->vreg_gpio, 0); + gpio_free(csra66x0->vreg_gpio); + } + return ret; + } + return 0; +} + +static int csra66x0_i2c_remove(struct i2c_client *i2c_client) +{ + struct csra66x0_priv *csra66x0 = i2c_get_clientdata(i2c_client); + + if (csra66x0) { + if (gpio_is_valid(csra66x0->vreg_gpio)) { + gpio_set_value(csra66x0->vreg_gpio, 0); + gpio_free(csra66x0->vreg_gpio); + } +#if IS_ENABLED(CONFIG_DEBUG_FS) + debugfs_remove_recursive(csra66x0->debugfs_dir); +#endif + } + snd_soc_unregister_codec(&i2c_client->dev); + return 0; +} + +static const struct i2c_device_id csra66x0_i2c_id[] = { + { "csra66x0", 0}, + { } +}; +MODULE_DEVICE_TABLE(i2c, csra66x0_i2c_id); + +static struct i2c_driver csra66x0_i2c_driver = { + .probe = csra66x0_i2c_probe, + .remove = csra66x0_i2c_remove, + .id_table = csra66x0_i2c_id, + .driver = { + .name = "csra66x0", + .owner = THIS_MODULE, + .of_match_table = csra66x0_of_match + }, +}; +#endif + +static int __init csra66x0_codec_init(void) +{ + int ret = 0; +#if IS_ENABLED(CONFIG_I2C) + ret = i2c_add_driver(&csra66x0_i2c_driver); + if (ret != 0) + pr_err("%s: Failed to register CSRA66X0 I2C driver, ret = %d\n", + __func__, ret); +#endif + return ret; +} +module_init(csra66x0_codec_init); + +static void __exit csra66x0_codec_exit(void) +{ +#if IS_ENABLED(CONFIG_I2C) + i2c_del_driver(&csra66x0_i2c_driver); +#endif +} +module_exit(csra66x0_codec_exit); + +MODULE_DESCRIPTION("CSRA66X0 Codec driver"); +MODULE_LICENSE("GPL v2"); diff --git a/audio-kernel/asoc/codecs/csra66x0/csra66x0.h b/audio-kernel/asoc/codecs/csra66x0/csra66x0.h new file mode 100644 index 000000000..43d83d847 --- /dev/null +++ b/audio-kernel/asoc/codecs/csra66x0/csra66x0.h @@ -0,0 +1,230 @@ +/* + * 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 _CSRA66X0_H +#define _CSRA66X0_H + +/* CSRA66X0 register addresses */ +#define CSRA66X0_BASE 0x7000 + +#define CSRA66X0_AUDIO_IF_RX_CONFIG1 (CSRA66X0_BASE+0x0000) +#define CSRA66X0_AUDIO_IF_RX_CONFIG2 (CSRA66X0_BASE+0x0001) +#define CSRA66X0_AUDIO_IF_RX_CONFIG3 (CSRA66X0_BASE+0x0002) +#define CSRA66X0_AUDIO_IF_TX_EN (CSRA66X0_BASE+0x0003) +#define CSRA66X0_AUDIO_IF_TX_CONFIG1 (CSRA66X0_BASE+0x0004) +#define CSRA66X0_AUDIO_IF_TX_CONFIG2 (CSRA66X0_BASE+0x0005) +#define CSRA66X0_I2C_DEVICE_ADDRESS (CSRA66X0_BASE+0x0006) +#define CSRA66X0_CHIP_ID_FA (CSRA66X0_BASE+0x0007) +#define CSRA66X0_ROM_VER_FA (CSRA66X0_BASE+0x0008) +#define CSRA66X0_CHIP_REV_0_FA (CSRA66X0_BASE+0x0009) +#define CSRA66X0_CHIP_REV_1_FA (CSRA66X0_BASE+0x000A) +#define CSRA66X0_CH1_MIX_SEL (CSRA66X0_BASE+0x000B) +#define CSRA66X0_CH2_MIX_SEL (CSRA66X0_BASE+0x000C) +#define CSRA66X0_CH1_SAMPLE1_SCALE_0 (CSRA66X0_BASE+0x000D) +#define CSRA66X0_CH1_SAMPLE1_SCALE_1 (CSRA66X0_BASE+0x000E) +#define CSRA66X0_CH1_SAMPLE3_SCALE_0 (CSRA66X0_BASE+0x000F) +#define CSRA66X0_CH1_SAMPLE3_SCALE_1 (CSRA66X0_BASE+0x0010) +#define CSRA66X0_CH1_SAMPLE5_SCALE_0 (CSRA66X0_BASE+0x0011) +#define CSRA66X0_CH1_SAMPLE5_SCALE_1 (CSRA66X0_BASE+0x0012) +#define CSRA66X0_CH1_SAMPLE7_SCALE_0 (CSRA66X0_BASE+0x0013) +#define CSRA66X0_CH1_SAMPLE7_SCALE_1 (CSRA66X0_BASE+0x0014) +#define CSRA66X0_CH1_SAMPLE2_SCALE_0 (CSRA66X0_BASE+0x0015) +#define CSRA66X0_CH1_SAMPLE2_SCALE_1 (CSRA66X0_BASE+0x0016) +#define CSRA66X0_CH1_SAMPLE4_SCALE_0 (CSRA66X0_BASE+0x0017) +#define CSRA66X0_CH1_SAMPLE4_SCALE_1 (CSRA66X0_BASE+0x0018) +#define CSRA66X0_CH1_SAMPLE6_SCALE_0 (CSRA66X0_BASE+0x0019) +#define CSRA66X0_CH1_SAMPLE6_SCALE_1 (CSRA66X0_BASE+0x001A) +#define CSRA66X0_CH1_SAMPLE8_SCALE_0 (CSRA66X0_BASE+0x001B) +#define CSRA66X0_CH1_SAMPLE8_SCALE_1 (CSRA66X0_BASE+0x001C) +#define CSRA66X0_CH2_SAMPLE1_SCALE_0 (CSRA66X0_BASE+0x001D) +#define CSRA66X0_CH2_SAMPLE1_SCALE_1 (CSRA66X0_BASE+0x001E) +#define CSRA66X0_CH2_SAMPLE3_SCALE_0 (CSRA66X0_BASE+0x001F) +#define CSRA66X0_CH2_SAMPLE3_SCALE_1 (CSRA66X0_BASE+0x0020) +#define CSRA66X0_CH2_SAMPLE5_SCALE_0 (CSRA66X0_BASE+0x0021) +#define CSRA66X0_CH2_SAMPLE5_SCALE_1 (CSRA66X0_BASE+0x0022) +#define CSRA66X0_CH2_SAMPLE7_SCALE_0 (CSRA66X0_BASE+0x0023) +#define CSRA66X0_CH2_SAMPLE7_SCALE_1 (CSRA66X0_BASE+0x0024) +#define CSRA66X0_CH2_SAMPLE2_SCALE_0 (CSRA66X0_BASE+0x0025) +#define CSRA66X0_CH2_SAMPLE2_SCALE_1 (CSRA66X0_BASE+0x0026) +#define CSRA66X0_CH2_SAMPLE4_SCALE_0 (CSRA66X0_BASE+0x0027) +#define CSRA66X0_CH2_SAMPLE4_SCALE_1 (CSRA66X0_BASE+0x0028) +#define CSRA66X0_CH2_SAMPLE6_SCALE_0 (CSRA66X0_BASE+0x0029) +#define CSRA66X0_CH2_SAMPLE6_SCALE_1 (CSRA66X0_BASE+0x002A) +#define CSRA66X0_CH2_SAMPLE8_SCALE_0 (CSRA66X0_BASE+0x002B) +#define CSRA66X0_CH2_SAMPLE8_SCALE_1 (CSRA66X0_BASE+0x002C) +#define CSRA66X0_VOLUME_CONFIG_FA (CSRA66X0_BASE+0x002D) +#define CSRA66X0_STARTUP_DELAY_FA (CSRA66X0_BASE+0x002E) +#define CSRA66X0_CH1_VOLUME_0_FA (CSRA66X0_BASE+0x002F) +#define CSRA66X0_CH1_VOLUME_1_FA (CSRA66X0_BASE+0x0030) +#define CSRA66X0_CH2_VOLUME_0_FA (CSRA66X0_BASE+0x0031) +#define CSRA66X0_CH2_VOLUME_1_FA (CSRA66X0_BASE+0x0032) +#define CSRA66X0_QUAD_ENC_COUNT_0_FA (CSRA66X0_BASE+0x0033) +#define CSRA66X0_QUAD_ENC_COUNT_1_FA (CSRA66X0_BASE+0x0034) +#define CSRA66X0_SOFT_CLIP_CONFIG (CSRA66X0_BASE+0x0035) +#define CSRA66X0_CH1_HARD_CLIP_THRESH (CSRA66X0_BASE+0x0036) +#define CSRA66X0_CH2_HARD_CLIP_THRESH (CSRA66X0_BASE+0x0037) +#define CSRA66X0_SOFT_CLIP_THRESH (CSRA66X0_BASE+0x0038) +#define CSRA66X0_DS_ENABLE_THRESH_0 (CSRA66X0_BASE+0x0039) +#define CSRA66X0_DS_ENABLE_THRESH_1 (CSRA66X0_BASE+0x003A) +#define CSRA66X0_DS_TARGET_COUNT_0 (CSRA66X0_BASE+0x003B) +#define CSRA66X0_DS_TARGET_COUNT_1 (CSRA66X0_BASE+0x003C) +#define CSRA66X0_DS_TARGET_COUNT_2 (CSRA66X0_BASE+0x003D) +#define CSRA66X0_DS_DISABLE_THRESH_0 (CSRA66X0_BASE+0x003E) +#define CSRA66X0_DS_DISABLE_THRESH_1 (CSRA66X0_BASE+0x003F) +#define CSRA66X0_DCA_CTRL (CSRA66X0_BASE+0x0040) +#define CSRA66X0_CH1_DCA_THRESH (CSRA66X0_BASE+0x0041) +#define CSRA66X0_CH2_DCA_THRESH (CSRA66X0_BASE+0x0042) +#define CSRA66X0_DCA_ATTACK_RATE (CSRA66X0_BASE+0x0043) +#define CSRA66X0_DCA_RELEASE_RATE (CSRA66X0_BASE+0x0044) +#define CSRA66X0_CH1_OUTPUT_INVERT_EN (CSRA66X0_BASE+0x0045) +#define CSRA66X0_CH2_OUTPUT_INVERT_EN (CSRA66X0_BASE+0x0046) +#define CSRA66X0_CH1_176P4K_DELAY (CSRA66X0_BASE+0x0047) +#define CSRA66X0_CH2_176P4K_DELAY (CSRA66X0_BASE+0x0048) +#define CSRA66X0_CH1_192K_DELAY (CSRA66X0_BASE+0x0049) +#define CSRA66X0_CH2_192K_DELAY (CSRA66X0_BASE+0x004A) +#define CSRA66X0_DEEMP_CONFIG_FA (CSRA66X0_BASE+0x004B) +#define CSRA66X0_CH1_TREBLE_GAIN_CTRL_FA (CSRA66X0_BASE+0x004C) +#define CSRA66X0_CH2_TREBLE_GAIN_CTRL_FA (CSRA66X0_BASE+0x004D) +#define CSRA66X0_CH1_TREBLE_FC_CTRL_FA (CSRA66X0_BASE+0x004E) +#define CSRA66X0_CH2_TREBLE_FC_CTRL_FA (CSRA66X0_BASE+0x004F) +#define CSRA66X0_CH1_BASS_GAIN_CTRL_FA (CSRA66X0_BASE+0x0050) +#define CSRA66X0_CH2_BASS_GAIN_CTRL_FA (CSRA66X0_BASE+0x0051) +#define CSRA66X0_CH1_BASS_FC_CTRL_FA (CSRA66X0_BASE+0x0052) +#define CSRA66X0_CH2_BASS_FC_CTRL_FA (CSRA66X0_BASE+0x0053) +#define CSRA66X0_FILTER_SEL_8K (CSRA66X0_BASE+0x0054) +#define CSRA66X0_FILTER_SEL_11P025K (CSRA66X0_BASE+0x0055) +#define CSRA66X0_FILTER_SEL_16K (CSRA66X0_BASE+0x0056) +#define CSRA66X0_FILTER_SEL_22P05K (CSRA66X0_BASE+0x0057) +#define CSRA66X0_FILTER_SEL_32K (CSRA66X0_BASE+0x0058) +#define CSRA66X0_FILTER_SEL_44P1K_48K (CSRA66X0_BASE+0x0059) +#define CSRA66X0_FILTER_SEL_88P2K_96K (CSRA66X0_BASE+0x005A) +#define CSRA66X0_FILTER_SEL_176P4K_192K (CSRA66X0_BASE+0x005B) +/* RESERVED (CSRA66X0_BASE+0x005C) */ +#define CSRA66X0_USER_DSP_CTRL (CSRA66X0_BASE+0x005D) +#define CSRA66X0_TEST_TONE_CTRL (CSRA66X0_BASE+0x005E) +#define CSRA66X0_TEST_TONE_FREQ_0 (CSRA66X0_BASE+0x005F) +#define CSRA66X0_TEST_TONE_FREQ_1 (CSRA66X0_BASE+0x0060) +#define CSRA66X0_TEST_TONE_FREQ_2 (CSRA66X0_BASE+0x0061) +#define CSRA66X0_AUDIO_RATE_CTRL_FA (CSRA66X0_BASE+0x0062) +#define CSRA66X0_MODULATION_INDEX_CTRL (CSRA66X0_BASE+0x0063) +#define CSRA66X0_MODULATION_INDEX_COUNT (CSRA66X0_BASE+0x0064) +#define CSRA66X0_MIN_MODULATION_PULSE_WIDTH (CSRA66X0_BASE+0x0065) +#define CSRA66X0_DEAD_TIME_CTRL (CSRA66X0_BASE+0x0066) +#define CSRA66X0_DEAD_TIME_THRESHOLD_0 (CSRA66X0_BASE+0x0067) +#define CSRA66X0_DEAD_TIME_THRESHOLD_1 (CSRA66X0_BASE+0x0068) +#define CSRA66X0_DEAD_TIME_THRESHOLD_2 (CSRA66X0_BASE+0x0069) +#define CSRA66X0_CH1_LOW_SIDE_DLY (CSRA66X0_BASE+0x006A) +#define CSRA66X0_CH2_LOW_SIDE_DLY (CSRA66X0_BASE+0x006B) +#define CSRA66X0_SPECTRUM_CTRL (CSRA66X0_BASE+0x006C) +/* RESERVED (CSRA66X0_BASE+0x006D) */ +#define CSRA66X0_SPECTRUM_SPREAD_CTRL (CSRA66X0_BASE+0x006E) +/* RESERVED (CSRA66X0_BASE+0x006F) */ +/* ... */ +/* RESERVED (CSRA66X0_BASE+0x007C) */ +#define CSRA66X0_EXT_PA_PROTECT_POLARITY (CSRA66X0_BASE+0x007D) +#define CSRA66X0_TEMP0_BACKOFF_COMP_VALUE (CSRA66X0_BASE+0x007E) +#define CSRA66X0_TEMP0_SHUTDOWN_COMP_VALUE (CSRA66X0_BASE+0x007F) +#define CSRA66X0_TEMP1_BACKOFF_COMP_VALUE (CSRA66X0_BASE+0x0080) +#define CSRA66X0_TEMP1_SHUTDOWN_COMP_VALUE (CSRA66X0_BASE+0x0081) +#define CSRA66X0_TEMP_PROT_BACKOFF (CSRA66X0_BASE+0x0082) +#define CSRA66X0_TEMP_READ0_FA (CSRA66X0_BASE+0x0083) +#define CSRA66X0_TEMP_READ1_FA (CSRA66X0_BASE+0x0084) +#define CSRA66X0_CHIP_STATE_CTRL_FA (CSRA66X0_BASE+0x0085) +/* RESERVED (CSRA66X0_BASE+0x0086) */ +#define CSRA66X0_PWM_OUTPUT_CONFIG (CSRA66X0_BASE+0x0087) +#define CSRA66X0_MISC_CONTROL_STATUS_0 (CSRA66X0_BASE+0x0088) +#define CSRA66X0_MISC_CONTROL_STATUS_1_FA (CSRA66X0_BASE+0x0089) +#define CSRA66X0_PIO0_SELECT (CSRA66X0_BASE+0x008A) +#define CSRA66X0_PIO1_SELECT (CSRA66X0_BASE+0x008B) +#define CSRA66X0_PIO2_SELECT (CSRA66X0_BASE+0x008C) +#define CSRA66X0_PIO3_SELECT (CSRA66X0_BASE+0x008D) +#define CSRA66X0_PIO4_SELECT (CSRA66X0_BASE+0x008E) +#define CSRA66X0_PIO5_SELECT (CSRA66X0_BASE+0x008F) +#define CSRA66X0_PIO6_SELECT (CSRA66X0_BASE+0x0090) +#define CSRA66X0_PIO7_SELECT (CSRA66X0_BASE+0x0091) +#define CSRA66X0_PIO8_SELECT (CSRA66X0_BASE+0x0092) +#define CSRA66X0_PIO_DIRN0 (CSRA66X0_BASE+0x0093) +#define CSRA66X0_PIO_DIRN1 (CSRA66X0_BASE+0x0094) +#define CSRA66X0_PIO_PULL_EN0 (CSRA66X0_BASE+0x0095) +#define CSRA66X0_PIO_PULL_EN1 (CSRA66X0_BASE+0x0096) +#define CSRA66X0_PIO_PULL_DIR0 (CSRA66X0_BASE+0x0097) +#define CSRA66X0_PIO_PULL_DIR1 (CSRA66X0_BASE+0x0098) +#define CSRA66X0_PIO_DRIVE_OUT0_FA (CSRA66X0_BASE+0x0099) +#define CSRA66X0_PIO_DRIVE_OUT1_FA (CSRA66X0_BASE+0x009A) +#define CSRA66X0_PIO_STATUS_IN0_FA (CSRA66X0_BASE+0x009B) +#define CSRA66X0_PIO_STATUS_IN1_FA (CSRA66X0_BASE+0x009C) +/* RESERVED (CSRA66X0_BASE+0x009D) */ +#define CSRA66X0_IRQ_OUTPUT_ENABLE (CSRA66X0_BASE+0x009E) +#define CSRA66X0_IRQ_OUTPUT_POLARITY (CSRA66X0_BASE+0x009F) +#define CSRA66X0_IRQ_OUTPUT_STATUS_FA (CSRA66X0_BASE+0x00A0) +#define CSRA66X0_CLIP_DCA_STATUS_FA (CSRA66X0_BASE+0x00A1) +#define CSRA66X0_CHIP_STATE_STATUS_FA (CSRA66X0_BASE+0x00A2) +#define CSRA66X0_FAULT_STATUS_FA (CSRA66X0_BASE+0x00A3) +#define CSRA66X0_OTP_STATUS_FA (CSRA66X0_BASE+0x00A4) +#define CSRA66X0_AUDIO_IF_STATUS_FA (CSRA66X0_BASE+0x00A5) +/* RESERVED (CSRA66X0_BASE+0x00A6) */ +#define CSRA66X0_DSP_SATURATION_STATUS_FA (CSRA66X0_BASE+0x00A7) +#define CSRA66X0_AUDIO_RATE_STATUS_FA (CSRA66X0_BASE+0x00A8) +/* RESERVED (CSRA66X0_BASE+0x00A9) */ +/* ... */ +/* RESERVED (CSRA66X0_BASE+0x00AB) */ +#define CSRA66X0_DISABLE_PWM_OUTPUT (CSRA66X0_BASE+0x00AC) +/* RESERVED (CSRA66X0_BASE+0x00AD) */ +/* ... */ +/* RESERVED (CSRA66X0_BASE+0x00B0) */ +#define CSRA66X0_OTP_VER_FA (CSRA66X0_BASE+0x00B1) +#define CSRA66X0_RAM_VER_FA (CSRA66X0_BASE+0x00B2) +/* RESERVED (CSRA66X0_BASE+0x00B3) */ +#define CSRA66X0_AUDIO_SATURATION_FLAGS_FA (CSRA66X0_BASE+0x00B4) +#define CSRA66X0_DCOFFSET_CHAN_1_01_FA (CSRA66X0_BASE+0x00B5) +#define CSRA66X0_DCOFFSET_CHAN_1_02_FA (CSRA66X0_BASE+0x00B6) +#define CSRA66X0_DCOFFSET_CHAN_1_03_FA (CSRA66X0_BASE+0x00B7) +#define CSRA66X0_DCOFFSET_CHAN_2_01_FA (CSRA66X0_BASE+0x00B8) +#define CSRA66X0_DCOFFSET_CHAN_2_02_FA (CSRA66X0_BASE+0x00B9) +#define CSRA66X0_DCOFFSET_CHAN_2_03_FA (CSRA66X0_BASE+0x00BA) +#define CSRA66X0_FORCED_PA_SWITCHING_CTRL (CSRA66X0_BASE+0x00BB) +#define CSRA66X0_PA_FORCE_PULSE_WIDTH (CSRA66X0_BASE+0x00BC) +#define CSRA66X0_PA_HIGH_MODULATION_CTRL_CH1 (CSRA66X0_BASE+0x00BD) +/* RESERVED (CSRA66X0_BASE+0x00BE) */ +/* RESERVED (CSRA66X0_BASE+0x00BF) */ +#define CSRA66X0_HIGH_MODULATION_THRESHOLD_LOW (CSRA66X0_BASE+0x00C0) +#define CSRA66X0_HIGH_MODULATION_THRESHOLD_HIGH (CSRA66X0_BASE+0x00C1) +/* RESERVED (CSRA66X0_BASE+0x00C2) */ +/* RESERVED (CSRA66X0_BASE+0x00C3) */ +#define CSRA66X0_PA_FREEZE_CTRL (CSRA66X0_BASE+0x00C4) +#define CSRA66X0_DCA_FREEZE_CTRL (CSRA66X0_BASE+0x00C5) +/* RESERVED (CSRA66X0_BASE+0x00C6) */ +/* ... */ +/* RESERVED (CSRA66X0_BASE+0x00FF) */ +#define CSRA66X0_MAX_REGISTER_ADDR CSRA66X0_DCA_FREEZE_CTRL + +#define EXPECTED_CSRA66X0_CHIP_ID 0x39 + +#define SPK_VOLUME_M20DB 0x119 +#define SPK_VOLUME_M20DB_LSB (SPK_VOLUME_M20DB & 0x0FF) +#define SPK_VOLUME_M20DB_MSB ((SPK_VOLUME_M20DB & 0x100)>>8) +#define SPK_VOLUME_LSB_MSK 0x00FF +#define SPK_VOLUME_MSB_MSK 0x0100 + +#define CONFIG_STATE 0x0 +#define RUN_STATE 0x1 +#define STDBY_STATE 0x2 + +#define FAULT_STATUS_INTERNAL 0x01 +#define FAULT_STATUS_OTP_INTEGRITY 0x02 +#define FAULT_STATUS_PADS2 0x04 +#define FAULT_STATUS_SMPS 0x08 +#define FAULT_STATUS_TEMP 0x10 +#define FAULT_STATUS_PROTECT 0x20 + +#endif /* _CSRA66X0_H */ diff --git a/audio-kernel/asoc/codecs/ep92/Android.mk b/audio-kernel/asoc/codecs/ep92/Android.mk new file mode 100644 index 000000000..c2ac0599f --- /dev/null +++ b/audio-kernel/asoc/codecs/ep92/Android.mk @@ -0,0 +1,50 @@ +# Android makefile for audio kernel modules + +# Assume no targets will be supported + +# Check if this driver needs be built for current target +ifeq ($(call is-board-platform,qcs405),true) +AUDIO_SELECT := CONFIG_SND_SOC_QCS405=m +endif + +AUDIO_CHIPSET := audio +# Build/Package only in case of supported target +ifeq ($(call is-board-platform-in-list,qcs405),true) + +LOCAL_PATH := $(call my-dir) + +# This makefile is only for DLKM +ifneq ($(findstring vendor,$(LOCAL_PATH)),) + +ifneq ($(findstring opensource,$(LOCAL_PATH)),) + AUDIO_BLD_DIR := $(ANDROID_BUILD_TOP)/vendor/qcom/opensource/audio-kernel +endif # opensource + +DLKM_DIR := $(TOP)/device/qcom/common/dlkm + +# Build audio.ko as $(AUDIO_CHIPSET)_audio.ko +########################################################### +# This is set once per LOCAL_PATH, not per (kernel) module +KBUILD_OPTIONS := AUDIO_ROOT=$(AUDIO_BLD_DIR) + +# We are actually building audio.ko here, as per the +# requirement we are specifying _audio.ko as LOCAL_MODULE. +# This means we need to rename the module to _audio.ko +# after audio.ko is built. +KBUILD_OPTIONS += MODNAME=ep92_dlkm +KBUILD_OPTIONS += BOARD_PLATFORM=$(TARGET_BOARD_PLATFORM) +KBUILD_OPTIONS += $(AUDIO_SELECT) + +########################################################### +include $(CLEAR_VARS) +LOCAL_MODULE := $(AUDIO_CHIPSET)_ep92.ko +LOCAL_MODULE_KBUILD_NAME := ep92_dlkm.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_DEBUG_ENABLE := true +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk +########################################################### +########################################################### + +endif # DLKM check +endif # supported target check diff --git a/audio-kernel/asoc/codecs/ep92/Kbuild b/audio-kernel/asoc/codecs/ep92/Kbuild new file mode 100644 index 000000000..af1fd0836 --- /dev/null +++ b/audio-kernel/asoc/codecs/ep92/Kbuild @@ -0,0 +1,110 @@ +# We can build either as part of a standalone Kernel build or as +# an external module. Determine which mechanism is being used +ifeq ($(MODNAME),) + KERNEL_BUILD := 1 +else + KERNEL_BUILD := 0 +endif + + + +ifeq ($(KERNEL_BUILD), 1) + # These are configurable via Kconfig for kernel-based builds + # Need to explicitly configure for Android-based builds + AUDIO_BLD_DIR := $(ANDROID_BUILD_TOP)/kernel/msm-4.14 + AUDIO_ROOT := $(AUDIO_BLD_DIR)/techpack/audio +endif + +ifeq ($(KERNEL_BUILD), 0) + ifeq ($(CONFIG_ARCH_QCS405), y) + include $(AUDIO_ROOT)/config/qcs405auto.conf + export + INCS += -include $(AUDIO_ROOT)/config/qcs405autoconf.h + endif +endif + +# As per target team, build is done as follows: +# Defconfig : build with default flags +# Slub : defconfig + CONFIG_SLUB_DEBUG := y + +# CONFIG_SLUB_DEBUG_ON := y + CONFIG_PAGE_POISONING := y +# Perf : Using appropriate msmXXXX-perf_defconfig +# +# Shipment builds (user variants) should not have any debug feature +# enabled. This is identified using 'TARGET_BUILD_VARIANT'. Slub builds +# are identified using the CONFIG_SLUB_DEBUG_ON configuration. Since +# there is no other way to identify defconfig builds, QTI internal +# representation of perf builds (identified using the string 'perf'), +# is used to identify if the build is a slub or defconfig one. This +# way no critical debug feature will be enabled for perf and shipment +# builds. Other OEMs are also protected using the TARGET_BUILD_VARIANT +# config. + +############ UAPI ############ +UAPI_DIR := uapi +UAPI_INC := -I$(AUDIO_ROOT)/include/$(UAPI_DIR) + +############ COMMON ############ +COMMON_DIR := include +COMMON_INC := -I$(AUDIO_ROOT)/$(COMMON_DIR) + +############ EP92 ############ + +# for EP92 Codec +ifdef CONFIG_SND_SOC_EP92 + EP92_OBJS += ep92.o +endif + +LINUX_INC += -Iinclude/linux + +INCS += $(COMMON_INC) \ + $(UAPI_INC) + +#EXTRA_CFLAGS += $(INCS) +ccflags-y += $(INCS) + + +CDEFINES += -DANI_LITTLE_BYTE_ENDIAN \ + -DANI_LITTLE_BIT_ENDIAN \ + -DDOT11F_LITTLE_ENDIAN_HOST \ + -DANI_COMPILER_TYPE_GCC \ + -DANI_OS_TYPE_ANDROID=6 \ + -DPTT_SOCK_SVC_ENABLE \ + -Wall\ + -Werror\ + -D__linux__ + +KBUILD_CPPFLAGS += $(CDEFINES) + +# Currently, for versions of gcc which support it, the kernel Makefile +# is disabling the maybe-uninitialized warning. Re-enable it for the +# AUDIO driver. Note that we must use EXTRA_CFLAGS here so that it +# will override the kernel settings. +ifeq ($(call cc-option-yn, -Wmaybe-uninitialized),y) +#EXTRA_CFLAGS += -Wmaybe-uninitialized +ccflags-y += -Wmaybe-uninitialized +endif +#EXTRA_CFLAGS += -Wmissing-prototypes + +ifeq ($(call cc-option-yn, -Wheader-guard),y) +#EXTRA_CFLAGS += -Wheader-guard +ccflags-y += -Wheader-guard +endif + +ifeq ($(KERNEL_BUILD), 0) +KBUILD_EXTRA_SYMBOLS +=$(OUT)/obj/vendor/qcom/opensource/audio-kernel/ipc/Module.symvers +KBUILD_EXTRA_SYMBOLS +=$(OUT)/obj/vendor/qcom/opensource/audio-kernel/dsp/Module.symvers +KBUILD_EXTRA_SYMBOLS +=$(OUT)/obj/vendor/qcom/opensource/audio-kernel/asoc/Module.symvers +KBUILD_EXTRA_SYMBOLS +=$(OUT)/obj/vendor/qcom/opensource/audio-kernel/asoc/codecs/Module.symvers +KBUILD_EXTRA_SYMBOLS +=$(OUT)/obj/vendor/qcom/opensource/audio-kernel/soc/Module.symvers +endif + +ifeq ($(CONFIG_SND_SOC_GCOV), y) +GCOV_PROFILE := y +endif + +# Module information used by KBuild framework +obj-$(CONFIG_SND_SOC_EP92) += ep92_dlkm.o +ep92_dlkm-y := $(EP92_OBJS) + +# inject some build related information +DEFINES += -DBUILD_TIMESTAMP=\"$(shell date -u +'%Y-%m-%dT%H:%M:%SZ')\" diff --git a/audio-kernel/asoc/codecs/ep92/ep92.c b/audio-kernel/asoc/codecs/ep92/ep92.c new file mode 100644 index 000000000..52f5e6d0b --- /dev/null +++ b/audio-kernel/asoc/codecs/ep92/ep92.c @@ -0,0 +1,1618 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ep92.h" + +#define EP92_POLL_INTERVAL_OFF_MSEC 200 +#define EP92_POLL_INTERVAL_ON_MSEC 20 +#define EP92_SYSFS_ENTRY_MAX_LEN 64 +#define EP92_HYST_CNT 5 + +#define EP92_RATES (SNDRV_PCM_RATE_32000 |\ + SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |\ + SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 |\ + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000) + +#define EP92_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE) + +static const unsigned int ep92_samp_freq_table[8] = { + 32000, 44100, 48000, 88200, 96000, 176400, 192000, 768000 +}; + +static bool ep92_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case EP92_BI_GENERAL_INFO_0: + case EP92_BI_GENERAL_INFO_1: + case EP92_BI_GENERAL_INFO_2: + case EP92_BI_GENERAL_INFO_3: + case EP92_BI_GENERAL_INFO_4: + case EP92_BI_GENERAL_INFO_5: + case EP92_BI_GENERAL_INFO_6: + case EP92_GENERAL_CONTROL_0: + case EP92_GENERAL_CONTROL_1: + case EP92_GENERAL_CONTROL_2: + case EP92_GENERAL_CONTROL_3: + case EP92_GENERAL_CONTROL_4: + case EP92_AUDIO_INFO_SYSTEM_STATUS_0: + case EP92_AUDIO_INFO_SYSTEM_STATUS_1: + case EP92_AUDIO_INFO_AUDIO_STATUS: + case EP92_AUDIO_INFO_CHANNEL_STATUS_0: + case EP92_AUDIO_INFO_CHANNEL_STATUS_1: + case EP92_AUDIO_INFO_CHANNEL_STATUS_2: + case EP92_AUDIO_INFO_CHANNEL_STATUS_3: + case EP92_AUDIO_INFO_CHANNEL_STATUS_4: + case EP92_AUDIO_INFO_ADO_INFO_FRAME_0: + case EP92_AUDIO_INFO_ADO_INFO_FRAME_1: + case EP92_AUDIO_INFO_ADO_INFO_FRAME_2: + case EP92_AUDIO_INFO_ADO_INFO_FRAME_3: + case EP92_AUDIO_INFO_ADO_INFO_FRAME_4: + case EP92_AUDIO_INFO_ADO_INFO_FRAME_5: + return true; + default: + return false; + } +} + +static bool ep92_writeable_registers(struct device *dev, unsigned int reg) +{ + if (reg >= EP92_ISP_MODE_ENTER_ISP && reg <= EP92_GENERAL_CONTROL_4) + return true; + + return false; +} + +static bool ep92_readable_registers(struct device *dev, unsigned int reg) +{ + if (reg >= EP92_BI_VENDOR_ID_0 && reg <= EP92_MAX_REGISTER_ADDR) + return true; + + return false; +} + +/* codec private data */ +struct ep92_pdata { + struct regmap *regmap; + struct snd_soc_codec *codec; + struct timer_list timer; + struct work_struct read_status_worker; + int irq; + + int hyst_tx_plug; + int hyst_link_on0; + int hyst_link_on1; + int hyst_link_on2; + int filt_tx_plug; + int filt_link_on0; + int filt_link_on1; + int filt_link_on2; + struct { + u8 tx_info; + u8 video_latency; + } gi; /* General Info block */ + + struct { + u8 ctl; + u8 rx_sel; + u8 ctl2; + u8 cec_volume; + u8 link; + } gc; /* General Control block */ + + struct { + u8 system_status_0; + u8 system_status_1; + u8 audio_status; + u8 cs[5]; + u8 cc; + u8 ca; + } ai; /* Audio Info block */ + + u8 old_mode; +#if IS_ENABLED(CONFIG_DEBUG_FS) + struct dentry *debugfs_dir; + struct dentry *debugfs_file_wo; + struct dentry *debugfs_file_ro; +#endif /* CONFIG_DEBUG_FS */ +}; + +#if IS_ENABLED(CONFIG_DEBUG_FS) +static int debugfs_codec_open_op(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static int debugfs_get_parameters(char *buf, u32 *param1, int num_of_par) +{ + char *token; + int base, cnt; + + token = strsep(&buf, " "); + for (cnt = 0; cnt < num_of_par; cnt++) { + if (token) { + if ((token[1] == 'x') || (token[1] == 'X')) + base = 16; + else + base = 10; + + if (kstrtou32(token, base, ¶m1[cnt]) != 0) + return -EINVAL; + + token = strsep(&buf, " "); + } else { + return -EINVAL; + } + } + return 0; +} + +static ssize_t debugfs_codec_write_op(struct file *filp, + const char __user *ubuf, size_t cnt, loff_t *ppos) +{ + struct ep92_pdata *ep92 = (struct ep92_pdata *) filp->private_data; + struct snd_soc_codec *codec = ep92->codec; + char lbuf[32]; + int rc; + u32 param[2]; + + if (!filp || !ppos || !ubuf) + return -EINVAL; + if (cnt > sizeof(lbuf) - 1) + return -EINVAL; + rc = copy_from_user(lbuf, ubuf, cnt); + if (rc) + return -EFAULT; + lbuf[cnt] = '\0'; + rc = debugfs_get_parameters(lbuf, param, 2); + if ((param[0] < EP92_ISP_MODE_ENTER_ISP) + || (param[0] > EP92_GENERAL_CONTROL_4)) { + dev_err(codec->dev, "%s: reg address 0x%02X out of range\n", + __func__, param[0]); + return -EINVAL; + } + if ((param[1] < 0) || (param[1] > 255)) { + dev_err(codec->dev, "%s: reg data 0x%02X out of range\n", + __func__, param[1]); + return -EINVAL; + } + if (rc == 0) { + rc = cnt; + dev_info(codec->dev, "%s: reg[0x%02X]=0x%02X\n", + __func__, param[0], param[1]); + snd_soc_write(codec, param[0], param[1]); + } else { + dev_err(codec->dev, "%s: write to register addr=0x%02X failed\n", + __func__, param[0]); + } + return rc; +} + +static ssize_t debugfs_ep92_reg_show(struct snd_soc_codec *codec, + char __user *ubuf, size_t count, loff_t *ppos) +{ + int i, reg_val, len; + ssize_t total = 0; + char tmp_buf[20]; + + if (!ubuf || !ppos || !codec || *ppos < 0) + return -EINVAL; + + for (i = (int) *ppos / 11; i <= EP92_MAX_REGISTER_ADDR; i++) { + reg_val = snd_soc_read(codec, i); + len = snprintf(tmp_buf, 20, "0x%02X: 0x%02X\n", i, + (reg_val & 0xFF)); + if ((total + len) > count) + break; + if (copy_to_user((ubuf + total), tmp_buf, len)) { + dev_err(codec->dev, "%s: fail to copy reg dump\n", + __func__); + total = -EFAULT; + goto copy_err; + } + *ppos += len; + total += len; + } + +copy_err: + return total; +} + +static ssize_t debugfs_codec_read_op(struct file *filp, + char __user *ubuf, size_t cnt, loff_t *ppos) +{ + struct ep92_pdata *ep92 = (struct ep92_pdata *) filp->private_data; + struct snd_soc_codec *codec = ep92->codec; + ssize_t ret_cnt; + + if (!filp || !ppos || !ubuf || *ppos < 0) + return -EINVAL; + ret_cnt = debugfs_ep92_reg_show(codec, ubuf, cnt, ppos); + return ret_cnt; +} + +static const struct file_operations debugfs_codec_ops = { + .open = debugfs_codec_open_op, + .write = debugfs_codec_write_op, + .read = debugfs_codec_read_op, +}; +#endif /* CONFIG_DEBUG_FS */ + +static int ep92_send_uevent(struct ep92_pdata *ep92, char *event) +{ + char *env[] = { event, NULL }; + + if (!event || !ep92) + return -EINVAL; + + return kobject_uevent_env(&ep92->codec->dev->kobj, KOBJ_CHANGE, env); +} + +static int ep92_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + return 0; +} + +static void ep92_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ +} + +static int ep92_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) +{ + return 0; +} + +static struct snd_soc_dai_ops ep92_dai_ops = { + .startup = ep92_startup, + .shutdown = ep92_shutdown, + .hw_params = ep92_hw_params, +}; + +static struct snd_soc_dai_driver ep92_dai[] = { + { + .name = "ep92-hdmi", + .id = 1, + .capture = { + .stream_name = "HDMI Capture", + .rate_max = 192000, + .rate_min = 32000, + .channels_min = 1, + .channels_max = 8, + .rates = EP92_RATES, + .formats = EP92_FORMATS, + }, + .ops = &ep92_dai_ops, /* callbacks */ + }, + { + .name = "ep92-arc", + .id = 2, + .capture = { + .stream_name = "ARC Capture", + .rate_max = 192000, + .rate_min = 32000, + .channels_min = 1, + .channels_max = 2, + .rates = EP92_RATES, + .formats = EP92_FORMATS, + }, + .ops = &ep92_dai_ops, /* callbacks */ + }, +}; + +static void ep92_read_general_control(struct snd_soc_codec *codec, + struct ep92_pdata *ep92) +{ + u8 old, change; + int val; + + old = ep92->gi.tx_info; + ep92->gi.tx_info = snd_soc_read(codec, EP92_BI_GENERAL_INFO_0); + if (ep92->gi.tx_info == 0xff) { + pr_debug("ep92 EP92_BI_GENERAL_INFO_0 read 0xff\n"); + ep92->gi.tx_info = old; + } + /* implement hysteresis to prevent events on glitches */ + if (ep92->gi.tx_info & EP92_GI_TX_HOT_PLUG_MASK) { + if (ep92->hyst_tx_plug < EP92_HYST_CNT) { + ep92->hyst_tx_plug++; + if ((ep92->hyst_tx_plug == EP92_HYST_CNT) && + (ep92->filt_tx_plug == 0)) { + ep92->filt_tx_plug = 1; + pr_debug("ep92 out_plug changed to 1\n"); + ep92_send_uevent(ep92, + "EP92EVT_OUT_PLUG=CONNECTED"); + } + } + } else { + if (ep92->hyst_tx_plug > 0) { + ep92->hyst_tx_plug--; + if ((ep92->hyst_tx_plug == 0) && + (ep92->filt_tx_plug == 1)) { + ep92->filt_tx_plug = 0; + pr_debug("ep92 out_plug changed to 0\n"); + ep92_send_uevent(ep92, + "EP92EVT_OUT_PLUG=DISCONNECTED"); + } + } + } + + old = ep92->gi.video_latency; + ep92->gi.video_latency = snd_soc_read(codec, EP92_BI_GENERAL_INFO_4); + if (ep92->gi.video_latency == 0xff) { + pr_debug("ep92 EP92_BI_GENERAL_INFO_4 read 0xff\n"); + ep92->gi.video_latency = old; + } + change = ep92->gi.video_latency ^ old; + if (change & EP92_GI_VIDEO_LATENCY_MASK) { + val = ep92->gi.video_latency; + if (val > 0) + val = (val - 1) * 2; + pr_debug("ep92 video latency changed to %d\n", val); + ep92_send_uevent(ep92, "EP92EVT_VIDEO_LATENCY=CHANGED"); + } + + old = ep92->gc.ctl; + ep92->gc.ctl = snd_soc_read(codec, EP92_GENERAL_CONTROL_0); + if (ep92->gc.ctl == 0xff) { + pr_debug("ep92 EP92_GENERAL_CONTROL_0 read 0xff\n"); + ep92->gc.ctl = old; + } + change = ep92->gc.ctl ^ old; + if (change & EP92_GC_POWER_MASK) { + val = (ep92->gc.ctl >> EP92_GC_POWER_SHIFT) & + EP92_2CHOICE_MASK; + pr_debug("ep92 power changed to %d\n", val); + if (val) + ep92_send_uevent(ep92, "EP92EVT_POWER=ON"); + else + ep92_send_uevent(ep92, "EP92EVT_POWER=OFF"); + } + if (change & EP92_GC_AUDIO_PATH_MASK) { + val = (ep92->gc.ctl >> EP92_GC_AUDIO_PATH_SHIFT) & + EP92_2CHOICE_MASK; + pr_debug("ep92 audio_path changed to %d\n", val); + if (val) + ep92_send_uevent(ep92, "EP92EVT_AUDIO_PATH=TV"); + else + ep92_send_uevent(ep92, "EP92EVT_AUDIO_PATH=SPEAKER"); + } + if (change & EP92_GC_CEC_MUTE_MASK) { + val = (ep92->gc.ctl >> EP92_GC_CEC_MUTE_SHIFT) & + EP92_2CHOICE_MASK; + pr_debug("ep92 cec_mute changed to %d\n", val); + if (val) + ep92_send_uevent(ep92, "EP92EVT_CEC_MUTE=NORMAL"); + else + ep92_send_uevent(ep92, "EP92EVT_CEC_MUTE=MUTED"); + } + if (change & EP92_GC_ARC_EN_MASK) { + val = ep92->gc.ctl & EP92_2CHOICE_MASK; + pr_debug("ep92 arc_en changed to %d\n", val); + if (val) + ep92_send_uevent(ep92, "EP92EVT_ARC_EN=ON"); + else + ep92_send_uevent(ep92, "EP92EVT_ARC_EN=OFF"); + } + + old = ep92->gc.rx_sel; + ep92->gc.rx_sel = snd_soc_read(codec, EP92_GENERAL_CONTROL_1); + if (ep92->gc.rx_sel == 0xff) { + pr_debug("ep92 EP92_GENERAL_CONTROL_1 read 0xff\n"); + ep92->gc.rx_sel = old; + } + change = ep92->gc.rx_sel ^ old; + if (change & EP92_GC_RX_SEL_MASK) { + val = ep92->gc.rx_sel & EP92_GC_RX_SEL_MASK; + pr_debug("ep92 rx_sel changed to %d\n", val); + ep92_send_uevent(ep92, "EP92EVT_SRC_SEL=CHANGED"); + } + + old = ep92->gc.cec_volume; + ep92->gc.cec_volume = snd_soc_read(codec, EP92_GENERAL_CONTROL_3); + if (ep92->gc.cec_volume == 0xff) { + pr_debug("ep92 EP92_GENERAL_CONTROL_3 read 0xff\n"); + ep92->gc.cec_volume = old; + } + change = ep92->gc.cec_volume ^ old; + if (change & EP92_GC_CEC_VOLUME_MASK) { + val = ep92->gc.cec_volume & EP92_GC_CEC_VOLUME_MASK; + pr_debug("ep92 cec_volume changed to %d\n", val); + ep92_send_uevent(ep92, "EP92EVT_CEC_VOLUME=CHANGED"); + } + + old = ep92->gc.link; + ep92->gc.link = snd_soc_read(codec, EP92_GENERAL_CONTROL_4); + if (ep92->gc.link == 0xff) { + pr_debug("ep92 EP92_GENERAL_CONTROL_4 read 0xff\n"); + ep92->gc.link = old; + } + + /* implement hysteresis to prevent events on glitches */ + if (ep92->gc.link & EP92_GC_LINK_ON0_MASK) { + if (ep92->hyst_link_on0 < EP92_HYST_CNT) { + ep92->hyst_link_on0++; + if ((ep92->hyst_link_on0 == EP92_HYST_CNT) && + (ep92->filt_link_on0 == 0)) { + ep92->filt_link_on0 = 1; + pr_debug("ep92 link_on0 changed to 1\n"); + ep92_send_uevent(ep92, + "EP92EVT_LINK_ON0=CONNECTED"); + } + } + } else { + if (ep92->hyst_link_on0 > 0) { + ep92->hyst_link_on0--; + if ((ep92->hyst_link_on0 == 0) && + (ep92->filt_link_on0 == 1)) { + ep92->filt_link_on0 = 0; + pr_debug("ep92 link_on0 changed to 0\n"); + ep92_send_uevent(ep92, + "EP92EVT_LINK_ON0=DISCONNECTED"); + } + } + } + + /* implement hysteresis to prevent events on glitches */ + if (ep92->gc.link & EP92_GC_LINK_ON1_MASK) { + if (ep92->hyst_link_on1 < EP92_HYST_CNT) { + ep92->hyst_link_on1++; + if ((ep92->hyst_link_on1 == EP92_HYST_CNT) && + (ep92->filt_link_on1 == 0)) { + ep92->filt_link_on1 = 1; + pr_debug("ep92 link_on1 changed to 1\n"); + ep92_send_uevent(ep92, + "EP92EVT_LINK_ON1=CONNECTED"); + } + } + } else { + if (ep92->hyst_link_on1 > 0) { + ep92->hyst_link_on1--; + if ((ep92->hyst_link_on1 == 0) && + (ep92->filt_link_on1 == 1)) { + ep92->filt_link_on1 = 0; + pr_debug("ep92 link_on1 changed to 0\n"); + ep92_send_uevent(ep92, + "EP92EVT_LINK_ON1=DISCONNECTED"); + } + } + } + + /* implement hysteresis to prevent events on glitches */ + if (ep92->gc.link & EP92_GC_LINK_ON2_MASK) { + if (ep92->hyst_link_on2 < EP92_HYST_CNT) { + ep92->hyst_link_on2++; + if ((ep92->hyst_link_on2 == EP92_HYST_CNT) && + (ep92->filt_link_on2 == 0)) { + ep92->filt_link_on2 = 1; + pr_debug("ep92 link_on2 changed to 1\n"); + ep92_send_uevent(ep92, + "EP92EVT_LINK_ON2=CONNECTED"); + } + } + } else { + if (ep92->hyst_link_on2 > 0) { + ep92->hyst_link_on2--; + if ((ep92->hyst_link_on2 == 0) && + (ep92->filt_link_on2 == 1)) { + ep92->filt_link_on2 = 0; + pr_debug("ep92 link_on2 changed to 0\n"); + ep92_send_uevent(ep92, + "EP92EVT_LINK_ON2=DISCONNECTED"); + } + } + } +} + +static void ep92_read_audio_info(struct snd_soc_codec *codec, + struct ep92_pdata *ep92) +{ + u8 old, change; + u8 new_mode; + bool send_uevent = false; + + old = ep92->ai.system_status_0; + ep92->ai.system_status_0 = snd_soc_read(codec, + EP92_AUDIO_INFO_SYSTEM_STATUS_0); + if (ep92->ai.system_status_0 == 0xff) { + pr_debug("ep92 EP92_AUDIO_INFO_SYSTEM_STATUS_0 read 0xff\n"); + ep92->ai.system_status_0 = old; + } + change = ep92->ai.system_status_0 ^ old; + if (change & EP92_AI_MCLK_ON_MASK) { + pr_debug("ep92 status changed to %d\n", + (ep92->ai.system_status_0 >> EP92_AI_MCLK_ON_SHIFT) & + EP92_2CHOICE_MASK); + send_uevent = true; + } + if (change & EP92_AI_AVMUTE_MASK) { + pr_debug("ep92 avmute changed to %d\n", + (ep92->ai.system_status_0 >> EP92_AI_AVMUTE_SHIFT) & + EP92_2CHOICE_MASK); + send_uevent = true; + } + if (change & EP92_AI_LAYOUT_MASK) { + pr_debug("ep92 layout changed to %d\n", + (ep92->ai.system_status_0) & EP92_2CHOICE_MASK); + send_uevent = true; + } + + old = ep92->ai.audio_status; + ep92->ai.audio_status = snd_soc_read(codec, + EP92_AUDIO_INFO_AUDIO_STATUS); + if (ep92->ai.audio_status == 0xff) { + pr_debug("ep92 EP92_AUDIO_INFO_AUDIO_STATUS read 0xff\n"); + ep92->ai.audio_status = old; + } + change = ep92->ai.audio_status ^ old; + if (change & EP92_AI_RATE_MASK) { + pr_debug("ep92 rate changed to %d\n", + ep92_samp_freq_table[(ep92->ai.audio_status) & + EP92_AI_RATE_MASK]); + send_uevent = true; + } + + new_mode = ep92->old_mode; + if (ep92->ai.audio_status & EP92_AI_STD_ADO_MASK) { + old = ep92->ai.cs[0]; + ep92->ai.cs[0] = snd_soc_read(codec, + EP92_AUDIO_INFO_CHANNEL_STATUS_0); + if (ep92->ai.cs[0] == 0xff) { + pr_debug("ep92 EP92_AUDIO_INFO_CHANNEL_STATUS_0 read 0xff\n"); + ep92->ai.cs[0] = old; + } + if (ep92->ai.cs[0] & EP92_AI_NPCM_MASK) + new_mode = 1; /* Compr */ + else + new_mode = 0; /* LPCM */ + } else if (ep92->ai.audio_status & EP92_AI_HBR_ADO_MASK) + new_mode = 1; /* Compr */ + + if (ep92->old_mode != new_mode) { + pr_debug("ep92 mode changed to %d\n", new_mode); + send_uevent = true; + } + ep92->old_mode = new_mode; + + old = ep92->ai.cc; + ep92->ai.cc = snd_soc_read(codec, EP92_AUDIO_INFO_ADO_INFO_FRAME_1); + if (ep92->ai.cc == 0xff) { + pr_debug("ep92 EP92_AUDIO_INFO_ADO_INFO_FRAME_1 read 0xff\n"); + ep92->ai.cc = old; + } + change = ep92->ai.cc ^ old; + if (change & EP92_AI_CH_COUNT_MASK) { + pr_debug("ep92 ch_count changed to %d (%d)\n", + ep92->ai.cc & EP92_AI_CH_COUNT_MASK, + (ep92->ai.cc & EP92_AI_CH_COUNT_MASK) == 0 ? 0 : + (ep92->ai.cc & EP92_AI_CH_COUNT_MASK) + 1); + send_uevent = true; + } + + old = ep92->ai.ca; + ep92->ai.ca = snd_soc_read(codec, EP92_AUDIO_INFO_ADO_INFO_FRAME_4); + if (ep92->ai.ca == 0xff) { + pr_debug("ep92 EP92_AUDIO_INFO_ADO_INFO_FRAME_4 read 0xff\n"); + ep92->ai.ca = old; + } + change = ep92->ai.ca ^ old; + if (change & EP92_AI_CH_ALLOC_MASK) { + pr_debug("ep92 ch_alloc changed to 0x%02x\n", + (ep92->ai.ca) & EP92_AI_CH_ALLOC_MASK); + send_uevent = true; + } + + if (send_uevent) + ep92_send_uevent(ep92, "EP92EVT_AUDIO=MEDIA_CONFIG_CHANGE"); +} + +static void ep92_init(struct snd_soc_codec *codec, struct ep92_pdata *ep92) +{ + /* update the format information in mixer controls */ + ep92_read_general_control(codec, ep92); + ep92_read_audio_info(codec, ep92); +} + +static int ep92_probe(struct snd_soc_codec *codec) +{ + struct ep92_pdata *ep92 = snd_soc_codec_get_drvdata(codec); + + ep92->codec = codec; + ep92_init(codec, ep92); + + return 0; +} + +static int ep92_remove(struct snd_soc_codec *codec) +{ + return 0; +} + +static struct regmap *ep92_get_regmap(struct device *dev) +{ + struct ep92_pdata *ep92_ctrl = dev_get_drvdata(dev); + + if (!ep92_ctrl) + return NULL; + + return ep92_ctrl->regmap; +} + +static struct snd_soc_codec_driver soc_codec_drv_ep92 = { + .probe = ep92_probe, + .remove = ep92_remove, + .get_regmap = ep92_get_regmap, +}; + +static struct regmap_config ep92_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .cache_type = REGCACHE_RBTREE, + .reg_defaults = ep92_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(ep92_reg_defaults), + .max_register = EP92_MAX_REGISTER_ADDR, + .volatile_reg = ep92_volatile_register, + .writeable_reg = ep92_writeable_registers, + .readable_reg = ep92_readable_registers, +}; + +void ep92_read_status(struct work_struct *work) +{ + struct ep92_pdata *ep92 = container_of(work, struct ep92_pdata, + read_status_worker); + struct snd_soc_codec *codec = ep92->codec; + u8 val; + + /* No polling before codec is initialized */ + if (codec == NULL) + return; + + /* check ADO_CHF that is set when audio format has changed */ + val = snd_soc_read(codec, EP92_BI_GENERAL_INFO_1); + if (val == 0xff) { + /* workaround for Nak'ed first read */ + val = snd_soc_read(codec, EP92_BI_GENERAL_INFO_1); + if (val == 0xff) + return; /* assume device not present */ + } + + if (val & EP92_GI_ADO_CHF_MASK) + pr_debug("ep92 audio mode change trigger.\n"); + + if (val & EP92_GI_CEC_ECF_MASK) + pr_debug("ep92 CEC change trigger.\n"); + + /* check for general control changes */ + ep92_read_general_control(codec, ep92); + + /* update the format information in mixer controls */ + ep92_read_audio_info(codec, ep92); +} + +static irqreturn_t ep92_irq(int irq, void *data) +{ + struct ep92_pdata *ep92 = data; + struct snd_soc_codec *codec = ep92->codec; + + /* Treat interrupt before codec is initialized as spurious */ + if (codec == NULL) + return IRQ_NONE; + + dev_dbg(codec->dev, "ep92_interrupt\n"); + + schedule_work(&ep92->read_status_worker); + + return IRQ_HANDLED; +}; + +void ep92_poll_status(unsigned long data) +{ + struct ep92_pdata *ep92 = (struct ep92_pdata *)data; + u32 poll_msec; + + if ((ep92->gc.ctl & EP92_GC_POWER_MASK) == 0) + poll_msec = EP92_POLL_INTERVAL_OFF_MSEC; + else + poll_msec = EP92_POLL_INTERVAL_ON_MSEC; + + mod_timer(&ep92->timer, jiffies + msecs_to_jiffies(poll_msec)); + + schedule_work(&ep92->read_status_worker); +} + +static const struct of_device_id ep92_of_match[] = { + { .compatible = "explore,ep92a6", }, + { } +}; +MODULE_DEVICE_TABLE(of, ep92_of_match); + +static ssize_t ep92_sysfs_rda_audio_state(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret; + int val; + struct ep92_pdata *ep92 = dev_get_drvdata(dev); + + if (!ep92) { + pr_err("%s: invalid input\n", __func__); + return -EINVAL; + } + + val = (ep92->ai.system_status_0 & EP92_AI_MCLK_ON_MASK) >> + EP92_AI_MCLK_ON_SHIFT; + + ret = snprintf(buf, EP92_SYSFS_ENTRY_MAX_LEN, "%d\n", val); + pr_debug("%s: '%d'\n", __func__, val); + + return ret; +} + +static ssize_t ep92_sysfs_rda_audio_format(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret; + int val; + struct ep92_pdata *ep92 = dev_get_drvdata(dev); + + if (!ep92) { + pr_err("%s: invalid input\n", __func__); + return -EINVAL; + } + + val = ep92->old_mode; + + ret = snprintf(buf, EP92_SYSFS_ENTRY_MAX_LEN, "%d\n", val); + pr_debug("%s: '%d'\n", __func__, val); + + return ret; +} + +static ssize_t ep92_sysfs_rda_audio_rate(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret; + int val; + struct ep92_pdata *ep92 = dev_get_drvdata(dev); + + if (!ep92) { + pr_err("%s: invalid input\n", __func__); + return -EINVAL; + } + + val = ep92_samp_freq_table[(ep92->ai.audio_status) & + EP92_AI_RATE_MASK]; + ret = snprintf(buf, EP92_SYSFS_ENTRY_MAX_LEN, "%d\n", val); + pr_debug("%s: '%d'\n", __func__, val); + + return ret; +} + +static ssize_t ep92_sysfs_rda_audio_layout(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret; + int val; + struct ep92_pdata *ep92 = dev_get_drvdata(dev); + + if (!ep92) { + pr_err("%s: invalid input\n", __func__); + return -EINVAL; + } + + val = (ep92->ai.system_status_0 & EP92_AI_LAYOUT_MASK) >> + EP92_AI_LAYOUT_SHIFT; + + ret = snprintf(buf, EP92_SYSFS_ENTRY_MAX_LEN, "%d\n", val); + pr_debug("%s: '%d'\n", __func__, val); + + return ret; +} + +static ssize_t ep92_sysfs_rda_audio_ch_count(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret; + int val; + struct ep92_pdata *ep92 = dev_get_drvdata(dev); + + if (!ep92) { + pr_err("%s: invalid input\n", __func__); + return -EINVAL; + } + + val = ep92->ai.cc & EP92_AI_CH_COUNT_MASK; + /* mapping is ch_count = reg_val + 1, with exception: 0 = unknown */ + if (val > 0) + val += 1; + + ret = snprintf(buf, EP92_SYSFS_ENTRY_MAX_LEN, "%d\n", val); + pr_debug("%s: '%d'\n", __func__, val); + + return ret; +} + +static ssize_t ep92_sysfs_rda_audio_ch_alloc(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret; + int val; + struct ep92_pdata *ep92 = dev_get_drvdata(dev); + + if (!ep92) { + pr_err("%s: invalid input\n", __func__); + return -EINVAL; + } + + val = ep92->ai.ca & EP92_AI_CH_ALLOC_MASK; + + ret = snprintf(buf, EP92_SYSFS_ENTRY_MAX_LEN, "%d\n", val); + pr_debug("%s: '%d'\n", __func__, val); + + return ret; +} + +static ssize_t ep92_sysfs_rda_avmute(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret; + int val; + struct ep92_pdata *ep92 = dev_get_drvdata(dev); + + if (!ep92) { + pr_err("%s: invalid input\n", __func__); + return -EINVAL; + } + + + val = (ep92->ai.system_status_0 >> EP92_AI_AVMUTE_SHIFT) & + EP92_2CHOICE_MASK; + + ret = snprintf(buf, EP92_SYSFS_ENTRY_MAX_LEN, "%d\n", val); + pr_debug("%s: '%d'\n", __func__, val); + + return ret; +} + +static ssize_t ep92_sysfs_rda_link_on0(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret; + int val; + struct ep92_pdata *ep92 = dev_get_drvdata(dev); + + if (!ep92) { + pr_err("%s: invalid input\n", __func__); + return -EINVAL; + } + + val = ep92->filt_link_on0; + + ret = snprintf(buf, EP92_SYSFS_ENTRY_MAX_LEN, "%d\n", val); + pr_debug("%s: '%d'\n", __func__, val); + + return ret; +} + +static ssize_t ep92_sysfs_rda_link_on1(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret; + int val; + struct ep92_pdata *ep92 = dev_get_drvdata(dev); + + if (!ep92) { + pr_err("%s: invalid input\n", __func__); + return -EINVAL; + } + + val = ep92->filt_link_on1; + + ret = snprintf(buf, EP92_SYSFS_ENTRY_MAX_LEN, "%d\n", val); + pr_debug("%s: '%d'\n", __func__, val); + + return ret; +} + +static ssize_t ep92_sysfs_rda_link_on2(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret; + int val; + struct ep92_pdata *ep92 = dev_get_drvdata(dev); + + if (!ep92) { + pr_err("%s: invalid input\n", __func__); + return -EINVAL; + } + + val = ep92->filt_link_on2; + + ret = snprintf(buf, EP92_SYSFS_ENTRY_MAX_LEN, "%d\n", val); + pr_debug("%s: '%d'\n", __func__, val); + + return ret; +} + +static ssize_t ep92_sysfs_rda_out_plug(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret; + int val; + struct ep92_pdata *ep92 = dev_get_drvdata(dev); + + if (!ep92) { + pr_err("%s: invalid input\n", __func__); + return -EINVAL; + } + + val = ep92->filt_tx_plug; + + ret = snprintf(buf, EP92_SYSFS_ENTRY_MAX_LEN, "%d\n", val); + pr_debug("%s: '%d'\n", __func__, val); + + return ret; +} + +static ssize_t ep92_sysfs_rda_video_latency(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret; + int val; + struct ep92_pdata *ep92 = dev_get_drvdata(dev); + + if (!ep92) { + pr_err("%s: invalid input\n", __func__); + return -EINVAL; + } + + val = ep92->gi.video_latency & EP92_GI_VIDEO_LATENCY_MASK; + if (val > 0) + val = (val - 1) * 2; + + ret = snprintf(buf, EP92_SYSFS_ENTRY_MAX_LEN, "%d\n", val); + pr_debug("%s: '%d'\n", __func__, val); + + return ret; +} + +static ssize_t ep92_sysfs_rda_arc_disable(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret; + int val; + struct ep92_pdata *ep92 = dev_get_drvdata(dev); + + if (!ep92) { + pr_err("%s: invalid input\n", __func__); + return -EINVAL; + } + + val = (ep92->gc.ctl2 >> EP92_GC_ARC_DIS_SHIFT) & + EP92_2CHOICE_MASK; + + ret = snprintf(buf, EP92_SYSFS_ENTRY_MAX_LEN, "%d\n", val); + pr_debug("%s: '%d'\n", __func__, val); + + return ret; +} + +static ssize_t ep92_sysfs_wta_arc_disable(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int reg, val, rc; + struct ep92_pdata *ep92 = dev_get_drvdata(dev); + + if (!ep92) { + pr_err("%s: invalid input\n", __func__); + return -EINVAL; + } + + rc = kstrtoint(buf, 10, &val); + if (rc) { + pr_err("%s: kstrtoint failed. rc=%d\n", __func__, rc); + goto end; + } + if ((val < 0) || (val > 1)) { + pr_err("%s: value out of range.\n", __func__); + rc = -EINVAL; + goto end; + } + + reg = snd_soc_read(ep92->codec, EP92_GENERAL_CONTROL_2); + reg &= ~EP92_GC_ARC_DIS_MASK; + reg |= ((val << EP92_GC_ARC_DIS_SHIFT) & EP92_GC_ARC_DIS_MASK); + snd_soc_write(ep92->codec, EP92_GENERAL_CONTROL_2, reg); + ep92->gc.ctl2 &= ~EP92_GC_ARC_DIS_MASK; + ep92->gc.ctl2 |= (val << EP92_GC_ARC_DIS_SHIFT) & EP92_GC_ARC_DIS_MASK; + + rc = strnlen(buf, EP92_SYSFS_ENTRY_MAX_LEN); +end: + return rc; +} + +static ssize_t ep92_sysfs_rda_power(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret; + int val; + struct ep92_pdata *ep92 = dev_get_drvdata(dev); + + if (!ep92) { + pr_err("%s: invalid input\n", __func__); + return -EINVAL; + } + + val = (ep92->gc.ctl >> EP92_GC_POWER_SHIFT) & EP92_2CHOICE_MASK; + + ret = snprintf(buf, EP92_SYSFS_ENTRY_MAX_LEN, "%d\n", val); + pr_debug("%s: '%d'\n", __func__, val); + + return ret; +} + +static ssize_t ep92_sysfs_wta_power(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int reg, val, rc; + struct ep92_pdata *ep92 = dev_get_drvdata(dev); + + if (!ep92) { + pr_err("%s: invalid input\n", __func__); + return -EINVAL; + } + + rc = kstrtoint(buf, 10, &val); + if (rc) { + pr_err("%s: kstrtoint failed. rc=%d\n", __func__, rc); + goto end; + } + if ((val < 0) || (val > 1)) { + pr_err("%s: value out of range.\n", __func__); + rc = -EINVAL; + goto end; + } + + reg = snd_soc_read(ep92->codec, EP92_GENERAL_CONTROL_0); + reg &= ~EP92_GC_POWER_MASK; + reg |= (val << EP92_GC_POWER_SHIFT) & EP92_GC_POWER_MASK; + snd_soc_write(ep92->codec, EP92_GENERAL_CONTROL_0, reg); + ep92->gc.ctl &= ~EP92_GC_POWER_MASK; + ep92->gc.ctl |= (val << EP92_GC_POWER_SHIFT) & EP92_GC_POWER_MASK; + + rc = strnlen(buf, EP92_SYSFS_ENTRY_MAX_LEN); +end: + return rc; +} + +static ssize_t ep92_sysfs_rda_audio_path(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret; + int val; + struct ep92_pdata *ep92 = dev_get_drvdata(dev); + + if (!ep92) { + pr_err("%s: invalid input\n", __func__); + return -EINVAL; + } + + val = (ep92->gc.ctl >> EP92_GC_AUDIO_PATH_SHIFT) & EP92_2CHOICE_MASK; + + ret = snprintf(buf, EP92_SYSFS_ENTRY_MAX_LEN, "%d\n", val); + pr_debug("%s: '%d'\n", __func__, val); + + return ret; +} + +static ssize_t ep92_sysfs_wta_audio_path(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int reg, val, rc; + struct ep92_pdata *ep92 = dev_get_drvdata(dev); + + if (!ep92) { + pr_err("%s: invalid input\n", __func__); + return -EINVAL; + } + + rc = kstrtoint(buf, 10, &val); + if (rc) { + pr_err("%s: kstrtoint failed. rc=%d\n", __func__, rc); + goto end; + } + if ((val < 0) || (val > 1)) { + pr_err("%s: value out of range.\n", __func__); + rc = -EINVAL; + goto end; + } + + reg = snd_soc_read(ep92->codec, EP92_GENERAL_CONTROL_0); + reg &= ~EP92_GC_AUDIO_PATH_MASK; + reg |= (val << EP92_GC_AUDIO_PATH_SHIFT) & EP92_GC_AUDIO_PATH_MASK; + snd_soc_write(ep92->codec, EP92_GENERAL_CONTROL_0, reg); + ep92->gc.ctl &= ~EP92_GC_AUDIO_PATH_MASK; + ep92->gc.ctl |= (val << EP92_GC_AUDIO_PATH_SHIFT) & + EP92_GC_AUDIO_PATH_MASK; + + rc = strnlen(buf, EP92_SYSFS_ENTRY_MAX_LEN); +end: + return rc; +} + +static ssize_t ep92_sysfs_rda_src_sel(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret; + int val; + struct ep92_pdata *ep92 = dev_get_drvdata(dev); + + if (!ep92) { + pr_err("%s: invalid input\n", __func__); + return -EINVAL; + } + + val = ep92->gc.rx_sel & EP92_GC_RX_SEL_MASK; + + ret = snprintf(buf, EP92_SYSFS_ENTRY_MAX_LEN, "%d\n", val); + pr_debug("%s: '%d'\n", __func__, val); + + return ret; +} + +static ssize_t ep92_sysfs_wta_src_sel(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int reg, val, rc; + struct ep92_pdata *ep92 = dev_get_drvdata(dev); + + if (!ep92) { + pr_err("%s: invalid input\n", __func__); + return -EINVAL; + } + + rc = kstrtoint(buf, 10, &val); + if (rc) { + pr_err("%s: kstrtoint failed. rc=%d\n", __func__, rc); + goto end; + } + if ((val < 0) || (val > 7)) { + pr_err("%s: value out of range.\n", __func__); + rc = -EINVAL; + goto end; + } + + reg = snd_soc_read(ep92->codec, EP92_GENERAL_CONTROL_1); + reg &= ~EP92_GC_RX_SEL_MASK; + reg |= (val << EP92_GC_RX_SEL_SHIFT) & EP92_GC_RX_SEL_MASK; + snd_soc_write(ep92->codec, EP92_GENERAL_CONTROL_1, reg); + ep92->gc.rx_sel &= ~EP92_GC_RX_SEL_MASK; + ep92->gc.rx_sel |= (val << EP92_GC_RX_SEL_SHIFT) & EP92_GC_RX_SEL_MASK; + + rc = strnlen(buf, EP92_SYSFS_ENTRY_MAX_LEN); +end: + return rc; +} + +static ssize_t ep92_sysfs_rda_arc_enable(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret; + int val; + struct ep92_pdata *ep92 = dev_get_drvdata(dev); + + if (!ep92) { + pr_err("%s: invalid input\n", __func__); + return -EINVAL; + } + + val = (ep92->gc.ctl >> EP92_GC_ARC_EN_SHIFT) & EP92_2CHOICE_MASK; + + ret = snprintf(buf, EP92_SYSFS_ENTRY_MAX_LEN, "%d\n", val); + pr_debug("%s: '%d'\n", __func__, val); + + return ret; +} + +static ssize_t ep92_sysfs_wta_arc_enable(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int reg, val, rc; + struct ep92_pdata *ep92 = dev_get_drvdata(dev); + + if (!ep92) { + pr_err("%s: invalid input\n", __func__); + return -EINVAL; + } + + rc = kstrtoint(buf, 10, &val); + if (rc) { + pr_err("%s: kstrtoint failed. rc=%d\n", __func__, rc); + goto end; + } + if ((val < 0) || (val > 1)) { + pr_err("%s: value out of range.\n", __func__); + rc = -EINVAL; + goto end; + } + + reg = snd_soc_read(ep92->codec, EP92_GENERAL_CONTROL_0); + reg &= ~EP92_GC_AUDIO_PATH_MASK; + reg |= (val << EP92_GC_AUDIO_PATH_SHIFT) & EP92_GC_AUDIO_PATH_MASK; + snd_soc_write(ep92->codec, EP92_GENERAL_CONTROL_0, reg); + ep92->gc.ctl &= ~EP92_GC_AUDIO_PATH_MASK; + ep92->gc.ctl |= (val << EP92_GC_AUDIO_PATH_SHIFT) & + EP92_GC_AUDIO_PATH_MASK; + + rc = strnlen(buf, EP92_SYSFS_ENTRY_MAX_LEN); +end: + return rc; +} + +static ssize_t ep92_sysfs_rda_cec_mute(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret; + int val; + struct ep92_pdata *ep92 = dev_get_drvdata(dev); + + if (!ep92) { + pr_err("%s: invalid input\n", __func__); + return -EINVAL; + } + + val = (ep92->gc.ctl >> EP92_GC_CEC_MUTE_SHIFT) & EP92_2CHOICE_MASK; + + ret = snprintf(buf, EP92_SYSFS_ENTRY_MAX_LEN, "%d\n", val); + pr_debug("%s: '%d'\n", __func__, val); + + return ret; +} + +static ssize_t ep92_sysfs_wta_cec_mute(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int reg, val, rc; + struct ep92_pdata *ep92 = dev_get_drvdata(dev); + + if (!ep92) { + pr_err("%s: invalid input\n", __func__); + return -EINVAL; + } + + rc = kstrtoint(buf, 10, &val); + if (rc) { + pr_err("%s: kstrtoint failed. rc=%d\n", __func__, rc); + goto end; + } + if ((val < 0) || (val > 1)) { + pr_err("%s: value out of range.\n", __func__); + rc = -EINVAL; + goto end; + } + + reg = snd_soc_read(ep92->codec, EP92_GENERAL_CONTROL_0); + reg &= ~EP92_GC_CEC_MUTE_MASK; + reg |= (val << EP92_GC_CEC_MUTE_SHIFT) & EP92_GC_CEC_MUTE_MASK; + snd_soc_write(ep92->codec, EP92_GENERAL_CONTROL_0, reg); + ep92->gc.ctl &= ~EP92_GC_CEC_MUTE_MASK; + ep92->gc.ctl |= (val << EP92_GC_CEC_MUTE_SHIFT) & + EP92_GC_CEC_MUTE_MASK; + + rc = strnlen(buf, EP92_SYSFS_ENTRY_MAX_LEN); +end: + return rc; +} + +static ssize_t ep92_sysfs_rda_cec_volume(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret; + int val; + struct ep92_pdata *ep92 = dev_get_drvdata(dev); + + if (!ep92) { + pr_err("%s: invalid input\n", __func__); + return -EINVAL; + } + + val = (ep92->gc.cec_volume >> EP92_GC_CEC_VOLUME_SHIFT) & + EP92_GC_CEC_VOLUME_MASK; + + ret = snprintf(buf, EP92_SYSFS_ENTRY_MAX_LEN, "%d\n", val); + pr_debug("%s: '%d'\n", __func__, val); + + return ret; +} + +static ssize_t ep92_sysfs_wta_cec_volume(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int reg, val, rc; + struct ep92_pdata *ep92 = dev_get_drvdata(dev); + + if (!ep92) { + pr_err("%s: invalid input\n", __func__); + return -EINVAL; + } + + rc = kstrtoint(buf, 10, &val); + if (rc) { + pr_err("%s: kstrtoint failed. rc=%d\n", __func__, rc); + goto end; + } + if ((val < 0) || (val > EP92_GC_CEC_VOLUME_MAX)) { + pr_err("%s: value out of range.\n", __func__); + rc = -EINVAL; + goto end; + } + + reg = val & EP92_GC_CEC_VOLUME_MASK; + snd_soc_write(ep92->codec, EP92_GENERAL_CONTROL_3, reg); + ep92->gc.cec_volume = val & EP92_GC_CEC_VOLUME_MASK; + + rc = strnlen(buf, EP92_SYSFS_ENTRY_MAX_LEN); +end: + return rc; +} + +static DEVICE_ATTR(audio_state, 0444, ep92_sysfs_rda_audio_state, NULL); +static DEVICE_ATTR(audio_format, 0444, ep92_sysfs_rda_audio_format, NULL); +static DEVICE_ATTR(audio_rate, 0444, ep92_sysfs_rda_audio_rate, NULL); +static DEVICE_ATTR(audio_layout, 0444, ep92_sysfs_rda_audio_layout, NULL); +static DEVICE_ATTR(audio_ch_count, 0444, ep92_sysfs_rda_audio_ch_count, NULL); +static DEVICE_ATTR(audio_ch_alloc, 0444, ep92_sysfs_rda_audio_ch_alloc, NULL); +static DEVICE_ATTR(audio_avmute, 0444, ep92_sysfs_rda_avmute, NULL); +static DEVICE_ATTR(link_on0, 0444, ep92_sysfs_rda_link_on0, NULL); +static DEVICE_ATTR(link_on1, 0444, ep92_sysfs_rda_link_on1, NULL); +static DEVICE_ATTR(link_on2, 0444, ep92_sysfs_rda_link_on2, NULL); +static DEVICE_ATTR(out_plug, 0444, ep92_sysfs_rda_out_plug, NULL); +static DEVICE_ATTR(video_latency, 0444, ep92_sysfs_rda_video_latency, NULL); +static DEVICE_ATTR(arc_disable, 0644, ep92_sysfs_rda_arc_disable, + ep92_sysfs_wta_arc_disable); +static DEVICE_ATTR(power_on, 0644, ep92_sysfs_rda_power, ep92_sysfs_wta_power); +static DEVICE_ATTR(audio_path, 0644, ep92_sysfs_rda_audio_path, + ep92_sysfs_wta_audio_path); +static DEVICE_ATTR(src_sel, 0644, ep92_sysfs_rda_src_sel, + ep92_sysfs_wta_src_sel); +static DEVICE_ATTR(arc_enable, 0644, ep92_sysfs_rda_arc_enable, + ep92_sysfs_wta_arc_enable); +static DEVICE_ATTR(cec_mute, 0644, ep92_sysfs_rda_cec_mute, + ep92_sysfs_wta_cec_mute); +static DEVICE_ATTR(cec_volume, 0644, ep92_sysfs_rda_cec_volume, + ep92_sysfs_wta_cec_volume); + +static struct attribute *ep92_fs_attrs[] = { + &dev_attr_audio_state.attr, + &dev_attr_audio_format.attr, + &dev_attr_audio_rate.attr, + &dev_attr_audio_layout.attr, + &dev_attr_audio_ch_count.attr, + &dev_attr_audio_ch_alloc.attr, + &dev_attr_audio_avmute.attr, + &dev_attr_link_on0.attr, + &dev_attr_link_on1.attr, + &dev_attr_link_on2.attr, + &dev_attr_out_plug.attr, + &dev_attr_video_latency.attr, + &dev_attr_arc_disable.attr, + &dev_attr_power_on.attr, + &dev_attr_audio_path.attr, + &dev_attr_src_sel.attr, + &dev_attr_arc_enable.attr, + &dev_attr_cec_mute.attr, + &dev_attr_cec_volume.attr, + NULL, +}; + +static struct attribute_group ep92_fs_attrs_group = { + .attrs = ep92_fs_attrs, +}; + +static int ep92_sysfs_create(struct i2c_client *client, + struct ep92_pdata *ep92) +{ + int rc; + + rc = sysfs_create_group(&client->dev.kobj, &ep92_fs_attrs_group); + + return rc; +} + +static void ep92_sysfs_remove(struct i2c_client *client, + struct ep92_pdata *ep92) +{ + sysfs_remove_group(&client->dev.kobj, &ep92_fs_attrs_group); +} + +static int ep92_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct ep92_pdata *ep92; + int ret; +#if IS_ENABLED(CONFIG_DEBUG_FS) + char debugfs_dir_name[32]; +#endif + + ep92 = devm_kzalloc(&client->dev, sizeof(struct ep92_pdata), + GFP_KERNEL); + if (ep92 == NULL) + return -ENOMEM; + + ep92->regmap = devm_regmap_init_i2c(client, &ep92_regmap_config); + if (IS_ERR(ep92->regmap)) { + ret = PTR_ERR(ep92->regmap); + dev_err(&client->dev, + "%s: Failed to allocate regmap for I2C device: %d\n", + __func__, ret); + return ret; + } + + i2c_set_clientdata(client, ep92); + + /* register interrupt handler */ + INIT_WORK(&ep92->read_status_worker, ep92_read_status); + ep92->irq = client->irq; + if (ep92->irq) { + ret = devm_request_threaded_irq(&client->dev, ep92->irq, + NULL, ep92_irq, IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + "ep92_irq", ep92); + if (ret) { + dev_err(&client->dev, + "%s: Failed to request IRQ %d: %d\n", + __func__, ep92->irq, ret); + ep92->irq = 0; + } + } + /* poll status if IRQ is not configured */ + if (ep92->irq == 0) { + setup_timer(&ep92->timer, ep92_poll_status, + (unsigned long)ep92); + mod_timer(&ep92->timer, jiffies + + msecs_to_jiffies(EP92_POLL_INTERVAL_OFF_MSEC)); + } + +#if IS_ENABLED(CONFIG_DEBUG_FS) + /* debugfs interface */ + snprintf(debugfs_dir_name, sizeof(debugfs_dir_name), "%s-%s", + client->name, dev_name(&client->dev)); + ep92->debugfs_dir = debugfs_create_dir(debugfs_dir_name, NULL); + if (!ep92->debugfs_dir) { + dev_dbg(&client->dev, + "%s: Failed to create /sys/kernel/debug/%s for debugfs\n", + __func__, debugfs_dir_name); + return -ENOMEM; + } + ep92->debugfs_file_wo = debugfs_create_file( + "write_reg_val", S_IFREG | 0444, ep92->debugfs_dir, + (void *) ep92, + &debugfs_codec_ops); + if (!ep92->debugfs_file_wo) { + dev_dbg(&client->dev, + "%s: Failed to create /sys/kernel/debug/%s/write_reg_val\n", + __func__, debugfs_dir_name); + return -ENOMEM; + } + ep92->debugfs_file_ro = debugfs_create_file( + "show_reg_dump", S_IFREG | 0444, ep92->debugfs_dir, + (void *) ep92, + &debugfs_codec_ops); + if (!ep92->debugfs_file_ro) { + dev_dbg(&client->dev, + "%s: Failed to create /sys/kernel/debug/%s/show_reg_dump\n", + __func__, debugfs_dir_name); + return -ENOMEM; + } +#endif /* CONFIG_DEBUG_FS */ + + /* register codec */ + ret = snd_soc_register_codec(&client->dev, &soc_codec_drv_ep92, + ep92_dai, ARRAY_SIZE(ep92_dai)); + if (ret) { + dev_err(&client->dev, + "%s %d: Failed to register CODEC: %d\n", + __func__, __LINE__, ret); + goto err_reg; + } + + ret = ep92_sysfs_create(client, ep92); + if (ret) { + pr_err("%s: sysfs creation failed ret=%d\n", __func__, ret); + goto err_sysfs; + } + + return 0; + +err_sysfs: + snd_soc_unregister_codec(&client->dev); +err_reg: + if (ep92->irq == 0) + del_timer(&ep92->timer); + + return ret; +} + +static int ep92_i2c_remove(struct i2c_client *client) +{ + struct ep92_pdata *ep92; + + ep92 = i2c_get_clientdata(client); + if (ep92) { + if (ep92->irq == 0) + del_timer(&ep92->timer); + +#if IS_ENABLED(CONFIG_DEBUG_FS) + debugfs_remove_recursive(ep92->debugfs_dir); +#endif + } + snd_soc_unregister_codec(&client->dev); + + ep92_sysfs_remove(client, ep92); + + return 0; +} + +static const struct i2c_device_id ep92_i2c_id[] = { + { "ep92-dev", 0}, + { } +}; +MODULE_DEVICE_TABLE(i2c, ep92_i2c_id); + +static struct i2c_driver ep92_i2c_driver = { + .probe = ep92_i2c_probe, + .remove = ep92_i2c_remove, + .id_table = ep92_i2c_id, + .driver = { + .name = "ep92", + .owner = THIS_MODULE, + .of_match_table = ep92_of_match + }, +}; + +static int __init ep92_codec_init(void) +{ + int ret = 0; + + ret = i2c_add_driver(&ep92_i2c_driver); + if (ret) + pr_err("Failed to register EP92 I2C driver: %d\n", ret); + + return ret; +} +module_init(ep92_codec_init); + +static void __exit ep92_codec_exit(void) +{ + i2c_del_driver(&ep92_i2c_driver); +} +module_exit(ep92_codec_exit); + +MODULE_DESCRIPTION("EP92 HDMI repeater/switch driver"); +MODULE_LICENSE("GPL v2"); diff --git a/audio-kernel/asoc/codecs/ep92/ep92.h b/audio-kernel/asoc/codecs/ep92/ep92.h new file mode 100644 index 000000000..ad067ea1d --- /dev/null +++ b/audio-kernel/asoc/codecs/ep92/ep92.h @@ -0,0 +1,216 @@ +/* + * 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 __EP92_H__ +#define __EP92_H__ + +/* EP92 register addresses */ +/* BI = Basic Info */ +#define EP92_BI_VENDOR_ID_0 0x00 +#define EP92_BI_VENDOR_ID_1 0x01 +#define EP92_BI_DEVICE_ID_0 0x02 +#define EP92_BI_DEVICE_ID_1 0x03 +#define EP92_BI_VERSION_NUM 0x04 +#define EP92_BI_VERSION_YEAR 0x05 +#define EP92_BI_VERSION_MONTH 0x06 +#define EP92_BI_VERSION_DATE 0x07 +#define EP92_BI_GENERAL_INFO_0 0x08 +#define EP92_BI_GENERAL_INFO_1 0x09 +#define EP92_BI_GENERAL_INFO_2 0x0A +#define EP92_BI_GENERAL_INFO_3 0x0B +#define EP92_BI_GENERAL_INFO_4 0x0C +#define EP92_BI_GENERAL_INFO_5 0x0D +#define EP92_BI_GENERAL_INFO_6 0x0E + +#define EP92_ISP_MODE_ENTER_ISP 0x0F + +#define EP92_GENERAL_CONTROL_0 0x10 +#define EP92_GENERAL_CONTROL_1 0x11 +#define EP92_GENERAL_CONTROL_2 0x12 +#define EP92_GENERAL_CONTROL_3 0x13 +#define EP92_GENERAL_CONTROL_4 0x14 + +#define EP92_CEC_EVENT_CODE 0x15 +#define EP92_CEC_EVENT_PARAM_1 0x16 +#define EP92_CEC_EVENT_PARAM_2 0x17 +#define EP92_CEC_EVENT_PARAM_3 0x18 +#define EP92_CEC_EVENT_PARAM_4 0x19 +/* RESERVED 0x1A */ +/* ... ... */ +/* RESERVED 0x1F */ +#define EP92_AUDIO_INFO_SYSTEM_STATUS_0 0x20 +#define EP92_AUDIO_INFO_SYSTEM_STATUS_1 0x21 +#define EP92_AUDIO_INFO_AUDIO_STATUS 0x22 +#define EP92_AUDIO_INFO_CHANNEL_STATUS_0 0x23 +#define EP92_AUDIO_INFO_CHANNEL_STATUS_1 0x24 +#define EP92_AUDIO_INFO_CHANNEL_STATUS_2 0x25 +#define EP92_AUDIO_INFO_CHANNEL_STATUS_3 0x26 +#define EP92_AUDIO_INFO_CHANNEL_STATUS_4 0x27 +#define EP92_AUDIO_INFO_ADO_INFO_FRAME_0 0x28 +#define EP92_AUDIO_INFO_ADO_INFO_FRAME_1 0x29 +#define EP92_AUDIO_INFO_ADO_INFO_FRAME_2 0x2A +#define EP92_AUDIO_INFO_ADO_INFO_FRAME_3 0x2B +#define EP92_AUDIO_INFO_ADO_INFO_FRAME_4 0x2C +#define EP92_AUDIO_INFO_ADO_INFO_FRAME_5 0x2D + +#define EP92_OTHER_PACKETS_HDMI_VS_0 0x2E +#define EP92_OTHER_PACKETS_HDMI_VS_1 0x2F +#define EP92_OTHER_PACKETS_ACP_PACKET 0x30 +#define EP92_OTHER_PACKETS_AVI_INFO_FRAME_0 0x31 +#define EP92_OTHER_PACKETS_AVI_INFO_FRAME_1 0x32 +#define EP92_OTHER_PACKETS_AVI_INFO_FRAME_2 0x33 +#define EP92_OTHER_PACKETS_AVI_INFO_FRAME_3 0x34 +#define EP92_OTHER_PACKETS_AVI_INFO_FRAME_4 0x35 +#define EP92_OTHER_PACKETS_GC_PACKET_0 0x36 +#define EP92_OTHER_PACKETS_GC_PACKET_1 0x37 +#define EP92_OTHER_PACKETS_GC_PACKET_2 0x38 + +#define EP92_MAX_REGISTER_ADDR EP92_OTHER_PACKETS_GC_PACKET_2 + + +/* EP92 register default values */ +static struct reg_default ep92_reg_defaults[] = { + {EP92_BI_VENDOR_ID_0, 0x17}, + {EP92_BI_VENDOR_ID_1, 0x7A}, + {EP92_BI_DEVICE_ID_0, 0x94}, + {EP92_BI_DEVICE_ID_1, 0xA3}, + {EP92_BI_VERSION_NUM, 0x10}, + {EP92_BI_VERSION_YEAR, 0x09}, + {EP92_BI_VERSION_MONTH, 0x07}, + {EP92_BI_VERSION_DATE, 0x06}, + {EP92_BI_GENERAL_INFO_0, 0x00}, + {EP92_BI_GENERAL_INFO_1, 0x00}, + {EP92_BI_GENERAL_INFO_2, 0x00}, + {EP92_BI_GENERAL_INFO_3, 0x00}, + {EP92_BI_GENERAL_INFO_4, 0x00}, + {EP92_BI_GENERAL_INFO_5, 0x00}, + {EP92_BI_GENERAL_INFO_6, 0x00}, + {EP92_ISP_MODE_ENTER_ISP, 0x00}, + {EP92_GENERAL_CONTROL_0, 0x20}, + {EP92_GENERAL_CONTROL_1, 0x00}, + {EP92_GENERAL_CONTROL_2, 0x00}, + {EP92_GENERAL_CONTROL_3, 0x10}, + {EP92_GENERAL_CONTROL_4, 0x00}, + {EP92_CEC_EVENT_CODE, 0x00}, + {EP92_CEC_EVENT_PARAM_1, 0x00}, + {EP92_CEC_EVENT_PARAM_2, 0x00}, + {EP92_CEC_EVENT_PARAM_3, 0x00}, + {EP92_CEC_EVENT_PARAM_4, 0x00}, + {EP92_AUDIO_INFO_SYSTEM_STATUS_0, 0x00}, + {EP92_AUDIO_INFO_SYSTEM_STATUS_1, 0x00}, + {EP92_AUDIO_INFO_AUDIO_STATUS, 0x00}, + {EP92_AUDIO_INFO_CHANNEL_STATUS_0, 0x00}, + {EP92_AUDIO_INFO_CHANNEL_STATUS_1, 0x00}, + {EP92_AUDIO_INFO_CHANNEL_STATUS_2, 0x00}, + {EP92_AUDIO_INFO_CHANNEL_STATUS_3, 0x00}, + {EP92_AUDIO_INFO_CHANNEL_STATUS_4, 0x00}, + {EP92_AUDIO_INFO_ADO_INFO_FRAME_0, 0x00}, + {EP92_AUDIO_INFO_ADO_INFO_FRAME_1, 0x00}, + {EP92_AUDIO_INFO_ADO_INFO_FRAME_2, 0x00}, + {EP92_AUDIO_INFO_ADO_INFO_FRAME_3, 0x00}, + {EP92_AUDIO_INFO_ADO_INFO_FRAME_4, 0x00}, + {EP92_AUDIO_INFO_ADO_INFO_FRAME_5, 0x00}, + {EP92_OTHER_PACKETS_HDMI_VS_0, 0x00}, + {EP92_OTHER_PACKETS_HDMI_VS_1, 0x00}, + {EP92_OTHER_PACKETS_ACP_PACKET, 0x00}, + {EP92_OTHER_PACKETS_AVI_INFO_FRAME_0, 0x00}, + {EP92_OTHER_PACKETS_AVI_INFO_FRAME_1, 0x00}, + {EP92_OTHER_PACKETS_AVI_INFO_FRAME_2, 0x00}, + {EP92_OTHER_PACKETS_AVI_INFO_FRAME_3, 0x00}, + {EP92_OTHER_PACKETS_AVI_INFO_FRAME_4, 0x00}, + {EP92_OTHER_PACKETS_GC_PACKET_0, 0x00}, + {EP92_OTHER_PACKETS_GC_PACKET_1, 0x00}, + {EP92_OTHER_PACKETS_GC_PACKET_2, 0x00}, +}; + + +/* shift/masks for register bits + * GI = General Info + * GC = General Control + * AI = Audio Info + */ +#define EP92_GI_ADO_CHF_MASK 0x01 +#define EP92_GI_CEC_ECF_MASK 0x02 +#define EP92_GI_TX_HOT_PLUG_SHIFT 7 +#define EP92_GI_TX_HOT_PLUG_MASK 0x80 +#define EP92_GI_VIDEO_LATENCY_SHIFT 0 +#define EP92_GI_VIDEO_LATENCY_MASK 0xff + +#define EP92_GC_POWER_SHIFT 7 +#define EP92_GC_POWER_MASK 0x80 +#define EP92_GC_AUDIO_PATH_SHIFT 5 +#define EP92_GC_AUDIO_PATH_MASK 0x20 +#define EP92_GC_CEC_MUTE_SHIFT 1 +#define EP92_GC_CEC_MUTE_MASK 0x02 +#define EP92_GC_ARC_EN_SHIFT 0 +#define EP92_GC_ARC_EN_MASK 0x01 +#define EP92_GC_ARC_DIS_SHIFT 6 +#define EP92_GC_ARC_DIS_MASK 0x40 +#define EP92_GC_RX_SEL_SHIFT 0 +#define EP92_GC_RX_SEL_MASK 0x07 +#define EP92_GC_CEC_VOLUME_SHIFT 0 +#define EP92_GC_CEC_VOLUME_MASK 0xff +#define EP92_GC_LINK_ON0_SHIFT 0 +#define EP92_GC_LINK_ON0_MASK 0x01 +#define EP92_GC_LINK_ON1_SHIFT 1 +#define EP92_GC_LINK_ON1_MASK 0x02 +#define EP92_GC_LINK_ON2_SHIFT 2 +#define EP92_GC_LINK_ON2_MASK 0x04 + +#define EP92_AI_MCLK_ON_SHIFT 6 +#define EP92_AI_MCLK_ON_MASK 0x40 +#define EP92_AI_AVMUTE_SHIFT 5 +#define EP92_AI_AVMUTE_MASK 0x20 +#define EP92_AI_LAYOUT_SHIFT 0 +#define EP92_AI_LAYOUT_MASK 0x01 +#define EP92_AI_HBR_ADO_SHIFT 5 +#define EP92_AI_HBR_ADO_MASK 0x20 +#define EP92_AI_STD_ADO_SHIFT 3 +#define EP92_AI_STD_ADO_MASK 0x08 +#define EP92_AI_RATE_MASK 0x07 +#define EP92_AI_NPCM_MASK 0x02 +#define EP92_AI_CH_COUNT_MASK 0x07 +#define EP92_AI_CH_ALLOC_MASK 0xff + +#define EP92_2CHOICE_MASK 1 +#define EP92_GC_CEC_VOLUME_MIN 0 +#define EP92_GC_CEC_VOLUME_MAX 100 +#define EP92_AI_RATE_MIN 0 +#define EP92_AI_RATE_MAX 768000 +#define EP92_AI_CH_COUNT_MIN 0 +#define EP92_AI_CH_COUNT_MAX 8 +#define EP92_AI_CH_ALLOC_MIN 0 +#define EP92_AI_CH_ALLOC_MAX 0xff + +#define EP92_STATUS_NO_SIGNAL 0 +#define EP92_STATUS_AUDIO_ACTIVE 1 + +/* kcontrol storage indices */ +enum { + EP92_KCTL_POWER = 0, + EP92_KCTL_AUDIO_PATH, + EP92_KCTL_CEC_MUTE, + EP92_KCTL_ARC_EN, + EP92_KCTL_RX_SEL, + EP92_KCTL_CEC_VOLUME, + EP92_KCTL_STATE, + EP92_KCTL_AVMUTE, + EP92_KCTL_LAYOUT, + EP92_KCTL_MODE, + EP92_KCTL_RATE, + EP92_KCTL_CH_COUNT, + EP92_KCTL_CH_ALLOC, + EP92_KCTL_MAX +}; + +#endif /* __EP92_H__ */ diff --git a/audio-kernel/asoc/codecs/msm-cdc-pinctrl.c b/audio-kernel/asoc/codecs/msm-cdc-pinctrl.c new file mode 100644 index 000000000..79e322f1a --- /dev/null +++ b/audio-kernel/asoc/codecs/msm-cdc-pinctrl.c @@ -0,0 +1,259 @@ +/* Copyright (c) 2016-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 + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include "msm-cdc-pinctrl.h" + +struct msm_cdc_pinctrl_info { + struct pinctrl *pinctrl; + struct pinctrl_state *pinctrl_active; + struct pinctrl_state *pinctrl_sleep; + int gpio; + bool state; +}; + +static struct msm_cdc_pinctrl_info *msm_cdc_pinctrl_get_gpiodata( + struct device_node *np) +{ + struct platform_device *pdev; + struct msm_cdc_pinctrl_info *gpio_data; + + if (!np) { + pr_err("%s: device node is null\n", __func__); + return NULL; + } + + pdev = of_find_device_by_node(np); + if (!pdev) { + pr_err("%s: platform device not found!\n", __func__); + return NULL; + } + + gpio_data = dev_get_drvdata(&pdev->dev); + if (!gpio_data) + dev_err(&pdev->dev, "%s: cannot find cdc gpio info\n", + __func__); + + return gpio_data; +} + +/* + * msm_cdc_get_gpio_state: select pinctrl sleep state + * @np: pointer to struct device_node + * + * Returns error code for failure and GPIO value on success + */ +int msm_cdc_get_gpio_state(struct device_node *np) +{ + struct msm_cdc_pinctrl_info *gpio_data; + int value = -EINVAL; + + gpio_data = msm_cdc_pinctrl_get_gpiodata(np); + if (!gpio_data) + return value; + + if (gpio_is_valid(gpio_data->gpio)) + value = gpio_get_value_cansleep(gpio_data->gpio); + + return value; +} +EXPORT_SYMBOL(msm_cdc_get_gpio_state); + +/* + * msm_cdc_pinctrl_select_sleep_state: select pinctrl sleep state + * @np: pointer to struct device_node + * + * Returns error code for failure + */ +int msm_cdc_pinctrl_select_sleep_state(struct device_node *np) +{ + struct msm_cdc_pinctrl_info *gpio_data; + + gpio_data = msm_cdc_pinctrl_get_gpiodata(np); + if (!gpio_data) + return -EINVAL; + + if (!gpio_data->pinctrl_sleep) { + pr_err("%s: pinctrl sleep state is null\n", __func__); + return -EINVAL; + } + gpio_data->state = false; + + return pinctrl_select_state(gpio_data->pinctrl, + gpio_data->pinctrl_sleep); +} +EXPORT_SYMBOL(msm_cdc_pinctrl_select_sleep_state); + +/* + * msm_cdc_pinctrl_select_active_state: select pinctrl active state + * @np: pointer to struct device_node + * + * Returns error code for failure + */ +int msm_cdc_pinctrl_select_active_state(struct device_node *np) +{ + struct msm_cdc_pinctrl_info *gpio_data; + + gpio_data = msm_cdc_pinctrl_get_gpiodata(np); + if (!gpio_data) + return -EINVAL; + + if (!gpio_data->pinctrl_active) { + pr_err("%s: pinctrl active state is null\n", __func__); + return -EINVAL; + } + gpio_data->state = true; + + return pinctrl_select_state(gpio_data->pinctrl, + gpio_data->pinctrl_active); +} +EXPORT_SYMBOL(msm_cdc_pinctrl_select_active_state); + +/* + * msm_cdc_pinctrl_get_state: get curren pinctrl state + * @np: pointer to struct device_node + * + * Returns 0 for sleep state, 1 for active state + */ +bool msm_cdc_pinctrl_get_state(struct device_node *np) +{ + struct msm_cdc_pinctrl_info *gpio_data; + + gpio_data = msm_cdc_pinctrl_get_gpiodata(np); + if (!gpio_data) + return -EINVAL; + + return gpio_data->state; +} +EXPORT_SYMBOL(msm_cdc_pinctrl_get_state); + +static int msm_cdc_pinctrl_probe(struct platform_device *pdev) +{ + int ret = 0; + struct msm_cdc_pinctrl_info *gpio_data; + + gpio_data = devm_kzalloc(&pdev->dev, + sizeof(struct msm_cdc_pinctrl_info), + GFP_KERNEL); + if (!gpio_data) + return -ENOMEM; + + gpio_data->pinctrl = devm_pinctrl_get(&pdev->dev); + if (IS_ERR_OR_NULL(gpio_data->pinctrl)) { + dev_err(&pdev->dev, "%s: Cannot get cdc gpio pinctrl:%ld\n", + __func__, PTR_ERR(gpio_data->pinctrl)); + ret = PTR_ERR(gpio_data->pinctrl); + goto err_pctrl_get; + } + + gpio_data->pinctrl_active = pinctrl_lookup_state( + gpio_data->pinctrl, "aud_active"); + if (IS_ERR_OR_NULL(gpio_data->pinctrl_active)) { + dev_err(&pdev->dev, "%s: Cannot get aud_active pinctrl state:%ld\n", + __func__, PTR_ERR(gpio_data->pinctrl_active)); + ret = PTR_ERR(gpio_data->pinctrl_active); + goto err_lookup_state; + } + + gpio_data->pinctrl_sleep = pinctrl_lookup_state( + gpio_data->pinctrl, "aud_sleep"); + if (IS_ERR_OR_NULL(gpio_data->pinctrl_sleep)) { + dev_err(&pdev->dev, "%s: Cannot get aud_sleep pinctrl state:%ld\n", + __func__, PTR_ERR(gpio_data->pinctrl_sleep)); + ret = PTR_ERR(gpio_data->pinctrl_sleep); + goto err_lookup_state; + } + /* skip setting to sleep state for LPI_TLMM GPIOs */ + if (!of_property_read_bool(pdev->dev.of_node, "qcom,lpi-gpios")) { + /* Set pinctrl state to aud_sleep by default */ + ret = pinctrl_select_state(gpio_data->pinctrl, + gpio_data->pinctrl_sleep); + if (ret) + dev_err(&pdev->dev, "%s: set cdc gpio sleep state fail: %d\n", + __func__, ret); + } + + gpio_data->gpio = of_get_named_gpio(pdev->dev.of_node, + "qcom,cdc-rst-n-gpio", 0); + if (gpio_is_valid(gpio_data->gpio)) { + ret = gpio_request(gpio_data->gpio, "MSM_CDC_RESET"); + if (ret) { + dev_err(&pdev->dev, "%s: Failed to request gpio %d\n", + __func__, gpio_data->gpio); + goto err_lookup_state; + } + } + + dev_set_drvdata(&pdev->dev, gpio_data); + return 0; + +err_lookup_state: + devm_pinctrl_put(gpio_data->pinctrl); +err_pctrl_get: + devm_kfree(&pdev->dev, gpio_data); + return ret; +} + +static int msm_cdc_pinctrl_remove(struct platform_device *pdev) +{ + struct msm_cdc_pinctrl_info *gpio_data; + + gpio_data = dev_get_drvdata(&pdev->dev); + + /* to free the requested gpio before exiting */ + if (gpio_data) { + if (gpio_is_valid(gpio_data->gpio)) + gpio_free(gpio_data->gpio); + + if (gpio_data->pinctrl) + devm_pinctrl_put(gpio_data->pinctrl); + } + + devm_kfree(&pdev->dev, gpio_data); + + return 0; +} + +static const struct of_device_id msm_cdc_pinctrl_match[] = { + {.compatible = "qcom,msm-cdc-pinctrl"}, + {} +}; + +static struct platform_driver msm_cdc_pinctrl_driver = { + .driver = { + .name = "msm-cdc-pinctrl", + .owner = THIS_MODULE, + .of_match_table = msm_cdc_pinctrl_match, + }, + .probe = msm_cdc_pinctrl_probe, + .remove = msm_cdc_pinctrl_remove, +}; + +int msm_cdc_pinctrl_drv_init(void) +{ + return platform_driver_register(&msm_cdc_pinctrl_driver); +} + +void msm_cdc_pinctrl_drv_exit(void) +{ + platform_driver_unregister(&msm_cdc_pinctrl_driver); +} +MODULE_DESCRIPTION("MSM CODEC pin control platform driver"); +MODULE_LICENSE("GPL v2"); diff --git a/audio-kernel/asoc/codecs/msm-cdc-pinctrl.h b/audio-kernel/asoc/codecs/msm-cdc-pinctrl.h new file mode 100644 index 000000000..4286fffa3 --- /dev/null +++ b/audio-kernel/asoc/codecs/msm-cdc-pinctrl.h @@ -0,0 +1,53 @@ +/* Copyright (c) 2016-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 __MFD_CDC_PINCTRL_H_ +#define __MFD_CDC_PINCTRL_H_ + +#include +#include + +#if IS_ENABLED(CONFIG_MSM_CDC_PINCTRL) +extern int msm_cdc_pinctrl_select_sleep_state(struct device_node *np); +extern int msm_cdc_pinctrl_select_active_state(struct device_node *np); +extern bool msm_cdc_pinctrl_get_state(struct device_node *np); +extern int msm_cdc_get_gpio_state(struct device_node *np); +int msm_cdc_pinctrl_drv_init(void); +void msm_cdc_pinctrl_drv_exit(void); + +#else +int msm_cdc_pinctrl_select_sleep_state(struct device_node *np) +{ + return 0; +} +int msm_cdc_pinctrl_select_active_state(struct device_node *np) +{ + return 0; +} +int msm_cdc_get_gpio_state(struct device_node *np) +{ + return 0; +} +int msm_cdc_pinctrl_drv_init(void) +{ + return 0; +} +void msm_cdc_pinctrl_drv_exit(void) +{ +} +bool msm_cdc_pinctrl_get_state(struct device_node *np) +{ + return true; +} +#endif + +#endif diff --git a/audio-kernel/asoc/codecs/msm-cdc-supply.c b/audio-kernel/asoc/codecs/msm-cdc-supply.c new file mode 100644 index 000000000..1b3c5f938 --- /dev/null +++ b/audio-kernel/asoc/codecs/msm-cdc-supply.c @@ -0,0 +1,548 @@ +/* + * Copyright (c) 2016-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 +#include +#include +#include +#include +#include "msm-cdc-supply.h" +#include + +#define CODEC_DT_MAX_PROP_SIZE 40 + +static int msm_cdc_dt_parse_vreg_info(struct device *dev, + struct cdc_regulator *cdc_vreg, + const char *name, bool is_ond) +{ + char prop_name[CODEC_DT_MAX_PROP_SIZE]; + struct device_node *regulator_node = NULL; + const __be32 *prop; + int len, rc; + u32 prop_val; + + /* Parse supply name */ + snprintf(prop_name, CODEC_DT_MAX_PROP_SIZE, "%s-supply", name); + + regulator_node = of_parse_phandle(dev->of_node, prop_name, 0); + if (!regulator_node) { + dev_err(dev, "%s: Looking up %s property in node %s failed", + __func__, prop_name, dev->of_node->full_name); + rc = -EINVAL; + goto done; + } + cdc_vreg->name = name; + cdc_vreg->ondemand = is_ond; + + /* Parse supply - voltage */ + snprintf(prop_name, CODEC_DT_MAX_PROP_SIZE, "qcom,%s-voltage", name); + prop = of_get_property(dev->of_node, prop_name, &len); + if (!prop || (len != (2 * sizeof(__be32)))) { + dev_err(dev, "%s: %s %s property\n", __func__, + prop ? "invalid format" : "no", prop_name); + rc = -EINVAL; + goto done; + } else { + cdc_vreg->min_uV = be32_to_cpup(&prop[0]); + cdc_vreg->max_uV = be32_to_cpup(&prop[1]); + } + + /* Parse supply - current */ + snprintf(prop_name, CODEC_DT_MAX_PROP_SIZE, "qcom,%s-current", name); + rc = of_property_read_u32(dev->of_node, prop_name, &prop_val); + if (rc) { + dev_err(dev, "%s: Looking up %s property in node %s failed", + __func__, prop_name, dev->of_node->full_name); + goto done; + } + cdc_vreg->optimum_uA = prop_val; + + dev_info(dev, "%s: %s: vol=[%d %d]uV, curr=[%d]uA, ond %d\n", + __func__, cdc_vreg->name, cdc_vreg->min_uV, cdc_vreg->max_uV, + cdc_vreg->optimum_uA, cdc_vreg->ondemand); + +done: + return rc; +} + +static int msm_cdc_parse_supplies(struct device *dev, + struct cdc_regulator *cdc_reg, + const char *sup_list, int sup_cnt, + bool is_ond) +{ + int idx, rc = 0; + const char *name = NULL; + + for (idx = 0; idx < sup_cnt; idx++) { + rc = of_property_read_string_index(dev->of_node, sup_list, idx, + &name); + if (rc) { + dev_err(dev, "%s: read string %s[%d] error (%d)\n", + __func__, sup_list, idx, rc); + goto done; + } + + dev_dbg(dev, "%s: Found cdc supply %s as part of %s\n", + __func__, name, sup_list); + + rc = msm_cdc_dt_parse_vreg_info(dev, &cdc_reg[idx], name, + is_ond); + if (rc) { + dev_err(dev, "%s: parse %s vreg info failed (%d)\n", + __func__, name, rc); + goto done; + } + } + +done: + return rc; +} + +static int msm_cdc_check_supply_param(struct device *dev, + struct cdc_regulator *cdc_vreg, + int num_supplies) +{ + if (!dev) { + pr_err("%s: device is NULL\n", __func__); + return -ENODEV; + } + + if (!cdc_vreg || (num_supplies <= 0)) { + dev_err(dev, "%s: supply check failed: vreg: %pK, num_supplies: %d\n", + __func__, cdc_vreg, num_supplies); + return -EINVAL; + } + + return 0; +} + +/* + * msm_cdc_disable_ondemand_supply: + * Disable codec ondemand supply + * + * @dev: pointer to codec device + * @supplies: pointer to regulator bulk data + * @cdc_vreg: pointer to platform regulator data + * @num_supplies: number of supplies + * @supply_name: Ondemand supply name to be enabled + * + * Return error code if supply disable is failed + */ +int msm_cdc_disable_ondemand_supply(struct device *dev, + struct regulator_bulk_data *supplies, + struct cdc_regulator *cdc_vreg, + int num_supplies, + char *supply_name) +{ + int rc, i; + + if ((!supply_name) || (!supplies)) { + pr_err("%s: either dev or supplies or cdc_vreg is NULL\n", + __func__); + return -EINVAL; + } + /* input parameter validation */ + rc = msm_cdc_check_supply_param(dev, cdc_vreg, num_supplies); + if (rc) + return rc; + + for (i = 0; i < num_supplies; i++) { + if (cdc_vreg[i].ondemand && + !strcmp(cdc_vreg[i].name, supply_name)) { + rc = regulator_disable(supplies[i].consumer); + if (rc) + dev_err(dev, "%s: failed to disable supply %s, err:%d\n", + __func__, supplies[i].supply, rc); + break; + } + } + if (i == num_supplies) { + dev_err(dev, "%s: not able to find supply %s\n", + __func__, supply_name); + rc = -EINVAL; + } + + return rc; +} +EXPORT_SYMBOL(msm_cdc_disable_ondemand_supply); + +/* + * msm_cdc_enable_ondemand_supply: + * Enable codec ondemand supply + * + * @dev: pointer to codec device + * @supplies: pointer to regulator bulk data + * @cdc_vreg: pointer to platform regulator data + * @num_supplies: number of supplies + * @supply_name: Ondemand supply name to be enabled + * + * Return error code if supply enable is failed + */ +int msm_cdc_enable_ondemand_supply(struct device *dev, + struct regulator_bulk_data *supplies, + struct cdc_regulator *cdc_vreg, + int num_supplies, + char *supply_name) +{ + int rc, i; + + if ((!supply_name) || (!supplies)) { + pr_err("%s: either dev or supplies or cdc_vreg is NULL\n", + __func__); + return -EINVAL; + } + /* input parameter validation */ + rc = msm_cdc_check_supply_param(dev, cdc_vreg, num_supplies); + if (rc) + return rc; + + for (i = 0; i < num_supplies; i++) { + if (cdc_vreg[i].ondemand && + !strcmp(cdc_vreg[i].name, supply_name)) { + rc = regulator_enable(supplies[i].consumer); + if (rc) + dev_err(dev, "%s: failed to enable supply %s, rc: %d\n", + __func__, supplies[i].supply, rc); + break; + } + } + if (i == num_supplies) { + dev_err(dev, "%s: not able to find supply %s\n", + __func__, supply_name); + rc = -EINVAL; + } + + return rc; +} +EXPORT_SYMBOL(msm_cdc_enable_ondemand_supply); + +/* + * msm_cdc_disable_static_supplies: + * Disable codec static supplies + * + * @dev: pointer to codec device + * @supplies: pointer to regulator bulk data + * @cdc_vreg: pointer to platform regulator data + * @num_supplies: number of supplies + * + * Return error code if supply disable is failed + */ +int msm_cdc_disable_static_supplies(struct device *dev, + struct regulator_bulk_data *supplies, + struct cdc_regulator *cdc_vreg, + int num_supplies) +{ + int rc, i; + + if ((!dev) || (!supplies) || (!cdc_vreg)) { + pr_err("%s: either dev or supplies or cdc_vreg is NULL\n", + __func__); + return -EINVAL; + } + /* input parameter validation */ + rc = msm_cdc_check_supply_param(dev, cdc_vreg, num_supplies); + if (rc) + return rc; + + for (i = 0; i < num_supplies; i++) { + if (cdc_vreg[i].ondemand) + continue; + + rc = regulator_disable(supplies[i].consumer); + if (rc) + dev_err(dev, "%s: failed to disable supply %s, err:%d\n", + __func__, supplies[i].supply, rc); + else + dev_dbg(dev, "%s: disabled regulator %s\n", + __func__, supplies[i].supply); + } + + return rc; +} +EXPORT_SYMBOL(msm_cdc_disable_static_supplies); + +/* + * msm_cdc_release_supplies: + * Release codec power supplies + * + * @dev: pointer to codec device + * @supplies: pointer to regulator bulk data + * @cdc_vreg: pointer to platform regulator data + * @num_supplies: number of supplies + * + * Return error code if supply disable is failed + */ +int msm_cdc_release_supplies(struct device *dev, + struct regulator_bulk_data *supplies, + struct cdc_regulator *cdc_vreg, + int num_supplies) +{ + int rc = 0; + int i; + + if ((!dev) || (!supplies) || (!cdc_vreg)) { + pr_err("%s: either dev or supplies or cdc_vreg is NULL\n", + __func__); + return -EINVAL; + } + /* input parameter validation */ + rc = msm_cdc_check_supply_param(dev, cdc_vreg, num_supplies); + if (rc) + return rc; + + msm_cdc_disable_static_supplies(dev, supplies, cdc_vreg, + num_supplies); + for (i = 0; i < num_supplies; i++) { + if (regulator_count_voltages(supplies[i].consumer) < 0) + continue; + + regulator_set_voltage(supplies[i].consumer, 0, + cdc_vreg[i].max_uV); + regulator_set_load(supplies[i].consumer, 0); + } + + return rc; +} +EXPORT_SYMBOL(msm_cdc_release_supplies); + +/* + * msm_cdc_enable_static_supplies: + * Enable codec static supplies + * + * @dev: pointer to codec device + * @supplies: pointer to regulator bulk data + * @cdc_vreg: pointer to platform regulator data + * @num_supplies: number of supplies + * + * Return error code if supply enable is failed + */ +int msm_cdc_enable_static_supplies(struct device *dev, + struct regulator_bulk_data *supplies, + struct cdc_regulator *cdc_vreg, + int num_supplies) +{ + int rc, i; + + if ((!dev) || (!supplies) || (!cdc_vreg)) { + pr_err("%s: either dev or supplies or cdc_vreg is NULL\n", + __func__); + return -EINVAL; + } + /* input parameter validation */ + rc = msm_cdc_check_supply_param(dev, cdc_vreg, num_supplies); + if (rc) + return rc; + + for (i = 0; i < num_supplies; i++) { + if (cdc_vreg[i].ondemand) + continue; + + rc = regulator_enable(supplies[i].consumer); + if (rc) { + dev_err(dev, "%s: failed to enable supply %s, rc: %d\n", + __func__, supplies[i].supply, rc); + break; + } + } + + while (rc && i--) + if (!cdc_vreg[i].ondemand) + regulator_disable(supplies[i].consumer); + + return rc; +} +EXPORT_SYMBOL(msm_cdc_enable_static_supplies); + +/* + * msm_cdc_init_supplies: + * Initialize codec static supplies with regulator get + * + * @dev: pointer to codec device + * @supplies: pointer to regulator bulk data + * @cdc_vreg: pointer to platform regulator data + * @num_supplies: number of supplies + * + * Return error code if supply init is failed + */ +int msm_cdc_init_supplies(struct device *dev, + struct regulator_bulk_data **supplies, + struct cdc_regulator *cdc_vreg, + int num_supplies) +{ + struct regulator_bulk_data *vsup; + int rc; + int i; + + if (!dev || !cdc_vreg) { + pr_err("%s: device pointer or dce_vreg is NULL\n", + __func__); + return -EINVAL; + } + /* input parameter validation */ + rc = msm_cdc_check_supply_param(dev, cdc_vreg, num_supplies); + if (rc) + return rc; + + vsup = devm_kcalloc(dev, num_supplies, + sizeof(struct regulator_bulk_data), + GFP_KERNEL); + if (!vsup) + return -ENOMEM; + + for (i = 0; i < num_supplies; i++) { + if (!cdc_vreg[i].name) { + dev_err(dev, "%s: supply name not defined\n", + __func__); + rc = -EINVAL; + goto err_supply; + } + vsup[i].supply = cdc_vreg[i].name; + } + + rc = devm_regulator_bulk_get(dev, num_supplies, vsup); + if (rc) { + dev_err(dev, "%s: failed to get supplies (%d)\n", + __func__, rc); + goto err_supply; + } + + /* Set voltage and current on regulators */ + for (i = 0; i < num_supplies; i++) { + if (regulator_count_voltages(vsup[i].consumer) < 0) + continue; + + rc = regulator_set_voltage(vsup[i].consumer, + cdc_vreg[i].min_uV, + cdc_vreg[i].max_uV); + if (rc) { + dev_err(dev, "%s: set regulator voltage failed for %s, err:%d\n", + __func__, vsup[i].supply, rc); + goto err_supply; + } + rc = regulator_set_load(vsup[i].consumer, + cdc_vreg[i].optimum_uA); + if (rc < 0) { + dev_err(dev, "%s: set regulator optimum mode failed for %s, err:%d\n", + __func__, vsup[i].supply, rc); + goto err_supply; + } + } + + *supplies = vsup; + + return 0; + +err_supply: + return rc; +} +EXPORT_SYMBOL(msm_cdc_init_supplies); + +/* + * msm_cdc_get_power_supplies: + * Get codec power supplies from device tree. + * Allocate memory to hold regulator data for + * all power supplies. + * + * @dev: pointer to codec device + * @cdc_vreg: pointer to codec regulator + * @total_num_supplies: total number of supplies read from DT + * + * Return error code if supply disable is failed + */ +int msm_cdc_get_power_supplies(struct device *dev, + struct cdc_regulator **cdc_vreg, + int *total_num_supplies) +{ + const char *static_prop_name = "qcom,cdc-static-supplies"; + const char *ond_prop_name = "qcom,cdc-on-demand-supplies"; + const char *cp_prop_name = "qcom,cdc-cp-supplies"; + int static_sup_cnt = 0; + int ond_sup_cnt = 0; + int cp_sup_cnt = 0; + int num_supplies = 0; + struct cdc_regulator *cdc_reg; + int rc; + + if (!dev) { + pr_err("%s: device pointer is NULL\n", __func__); + return -EINVAL; + } + static_sup_cnt = of_property_count_strings(dev->of_node, + static_prop_name); + if (static_sup_cnt < 0) { + dev_err(dev, "%s: Failed to get static supplies(%d)\n", + __func__, static_sup_cnt); + rc = static_sup_cnt; + goto err_supply_cnt; + } + ond_sup_cnt = of_property_count_strings(dev->of_node, ond_prop_name); + if (ond_sup_cnt < 0) + ond_sup_cnt = 0; + + cp_sup_cnt = of_property_count_strings(dev->of_node, + cp_prop_name); + if (cp_sup_cnt < 0) + cp_sup_cnt = 0; + + num_supplies = static_sup_cnt + ond_sup_cnt + cp_sup_cnt; + if (num_supplies <= 0) { + dev_err(dev, "%s: supply count is 0 or negative\n", __func__); + rc = -EINVAL; + goto err_supply_cnt; + } + + cdc_reg = devm_kcalloc(dev, num_supplies, + sizeof(struct cdc_regulator), + GFP_KERNEL); + if (!cdc_reg) { + rc = -ENOMEM; + goto err_mem_alloc; + } + + rc = msm_cdc_parse_supplies(dev, cdc_reg, static_prop_name, + static_sup_cnt, false); + if (rc) { + dev_err(dev, "%s: failed to parse static supplies(%d)\n", + __func__, rc); + goto err_sup; + } + + rc = msm_cdc_parse_supplies(dev, &cdc_reg[static_sup_cnt], + ond_prop_name, ond_sup_cnt, + true); + if (rc) { + dev_err(dev, "%s: failed to parse demand supplies(%d)\n", + __func__, rc); + goto err_sup; + } + + rc = msm_cdc_parse_supplies(dev, + &cdc_reg[static_sup_cnt + ond_sup_cnt], + cp_prop_name, cp_sup_cnt, true); + if (rc) { + dev_err(dev, "%s: failed to parse cp supplies(%d)\n", + __func__, rc); + goto err_sup; + } + + *cdc_vreg = cdc_reg; + *total_num_supplies = num_supplies; + + return 0; + +err_sup: +err_supply_cnt: +err_mem_alloc: + return rc; +} +EXPORT_SYMBOL(msm_cdc_get_power_supplies); diff --git a/audio-kernel/asoc/codecs/msm-cdc-supply.h b/audio-kernel/asoc/codecs/msm-cdc-supply.h new file mode 100644 index 000000000..b6516fa94 --- /dev/null +++ b/audio-kernel/asoc/codecs/msm-cdc-supply.h @@ -0,0 +1,56 @@ +/* Copyright (c) 2016, 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 __CODEC_POWER_SUPPLY_H__ +#define __CODEC_POWER_SUPPLY_H__ + +#include +#include +#include + +struct cdc_regulator { + const char *name; + int min_uV; + int max_uV; + int optimum_uA; + bool ondemand; + struct regulator *regulator; +}; + +extern int msm_cdc_get_power_supplies(struct device *dev, + struct cdc_regulator **cdc_vreg, + int *total_num_supplies); +extern int msm_cdc_disable_ondemand_supply(struct device *dev, + struct regulator_bulk_data *supplies, + struct cdc_regulator *cdc_vreg, + int num_supplies, char *supply_name); +extern int msm_cdc_enable_ondemand_supply(struct device *dev, + struct regulator_bulk_data *supplies, + struct cdc_regulator *cdc_vreg, + int num_supplies, char *supply_name); +extern int msm_cdc_disable_static_supplies(struct device *dev, + struct regulator_bulk_data *supplies, + struct cdc_regulator *cdc_vreg, + int num_supplies); +extern int msm_cdc_release_supplies(struct device *dev, + struct regulator_bulk_data *supplies, + struct cdc_regulator *cdc_vreg, + int num_supplies); +extern int msm_cdc_enable_static_supplies(struct device *dev, + struct regulator_bulk_data *supplies, + struct cdc_regulator *cdc_vreg, + int num_supplies); +extern int msm_cdc_init_supplies(struct device *dev, + struct regulator_bulk_data **supplies, + struct cdc_regulator *cdc_vreg, + int num_supplies); +#endif diff --git a/audio-kernel/asoc/codecs/msm_hdmi_codec_rx.c b/audio-kernel/asoc/codecs/msm_hdmi_codec_rx.c new file mode 100644 index 000000000..07e1cef37 --- /dev/null +++ b/audio-kernel/asoc/codecs/msm_hdmi_codec_rx.c @@ -0,0 +1,723 @@ +/* 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 + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#define MSM_EXT_DISP_PCM_RATES SNDRV_PCM_RATE_48000 +#define AUD_EXT_DISP_ACK_DISCONNECT (AUDIO_ACK_CONNECT ^ AUDIO_ACK_CONNECT) +#define AUD_EXT_DISP_ACK_CONNECT (AUDIO_ACK_CONNECT) +#define AUD_EXT_DISP_ACK_ENABLE (AUDIO_ACK_SET_ENABLE | AUDIO_ACK_ENABLE) + +#define SOC_EXT_DISP_AUDIO_TYPE(index) \ + static SOC_ENUM_SINGLE_DECL(ext_disp_audio_type##index, SND_SOC_NOPM, \ + index, ext_disp_audio_type_text) +#define SOC_EXT_DISP_AUDIO_ACK_STATE(index) \ + static SOC_ENUM_SINGLE_DECL(ext_disp_audio_ack_state##index, \ + SND_SOC_NOPM, index, ext_disp_audio_ack_text) + +#define SWITCH_DP_CODEC(codec_info, codec_data, dai_id) \ + codec_info.type = EXT_DISPLAY_TYPE_DP; \ + codec_info.ctrl_id = codec_data->ctl[dai_id]; \ + codec_info.stream_id = codec_data->stream[dai_id]; \ + msm_ext_disp_select_audio_codec(codec_data->ext_disp_core_pdev, \ + &codec_info) + +enum { + DP_STREAM0 = 0, + DP_STREAM1, + DP_STREAM_MAX, +}; + +enum { + DP_DAI1 = 0, + DP_DAI2, + HDMI_DAI, + DP_DAI_MAX, +}; + +static const char *const ext_disp_audio_type_text[] = {"None", "HDMI", "DP"}; +static const char *const ext_disp_audio_ack_text[] = {"Disconnect", "Connect", + "Ack_Enable"}; + +SOC_EXT_DISP_AUDIO_TYPE(0); +SOC_EXT_DISP_AUDIO_ACK_STATE(0); +SOC_EXT_DISP_AUDIO_TYPE(1); +SOC_EXT_DISP_AUDIO_ACK_STATE(1); + +struct msm_ext_disp_audio_codec_rx_data { + struct platform_device *ext_disp_core_pdev; + struct msm_ext_disp_audio_codec_ops ext_disp_ops; + int cable_status; + struct mutex dp_ops_lock; + int stream[DP_DAI_MAX]; + int ctl[DP_DAI_MAX]; +}; + +static int msm_ext_disp_edid_ctl_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct msm_ext_disp_audio_codec_rx_data *codec_data; + struct msm_ext_disp_audio_edid_blk edid_blk; + int rc = 0; + struct msm_ext_disp_codec_id codec_info; + int dai_id = kcontrol->private_value; + + codec_data = snd_soc_codec_get_drvdata(codec); + + if (!codec_data) { + dev_err(codec->dev, "%s: codec_data is NULL\n", __func__); + return -EINVAL; + } + + if (!codec_data->ext_disp_ops.get_audio_edid_blk) { + dev_dbg(codec->dev, "%s: get_audio_edid_blk() is NULL\n", + __func__); + uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; + uinfo->count = 0; + return 0; + } + + dev_dbg(codec->dev, "%s: DP ctl id %d Stream id %d\n", __func__, + codec_data->ctl[dai_id], codec_data->stream[dai_id]); + + mutex_lock(&codec_data->dp_ops_lock); + SWITCH_DP_CODEC(codec_info, codec_data, dai_id); + rc = codec_data->ext_disp_ops.get_audio_edid_blk( + codec_data->ext_disp_core_pdev, &edid_blk); + mutex_unlock(&codec_data->dp_ops_lock); + if (rc >= 0) { + uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; + uinfo->count = edid_blk.audio_data_blk_size + + edid_blk.spk_alloc_data_blk_size; + } + + dev_dbg(codec->dev, "%s: count: %d\n", __func__, uinfo->count); + + return rc; +} + +static int msm_ext_disp_edid_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct msm_ext_disp_audio_codec_rx_data *codec_data; + struct msm_ext_disp_audio_edid_blk edid_blk; + struct msm_ext_disp_codec_id codec_info; + int rc = 0; + int dai_id = kcontrol->private_value; + + codec_data = snd_soc_codec_get_drvdata(codec); + if (!codec_data || !codec_data->ext_disp_ops.get_audio_edid_blk) { + dev_err(codec->dev, "%s: codec_data or get_audio_edid_blk() is NULL\n", + __func__); + return -EINVAL; + } + + dev_dbg(codec->dev, "%s: DP ctl id %d Stream id %d\n", __func__, + codec_data->ctl[dai_id], codec_data->stream[dai_id]); + + mutex_lock(&codec_data->dp_ops_lock); + SWITCH_DP_CODEC(codec_info, codec_data, dai_id); + rc = codec_data->ext_disp_ops.get_audio_edid_blk( + codec_data->ext_disp_core_pdev, &edid_blk); + mutex_unlock(&codec_data->dp_ops_lock); + if (rc >= 0) { + if (sizeof(ucontrol->value.bytes.data) < + (edid_blk.audio_data_blk_size + + edid_blk.spk_alloc_data_blk_size)) { + dev_err(codec->dev, + "%s: Not enough memory to copy EDID data\n", + __func__); + return -ENOMEM; + } + + memcpy(ucontrol->value.bytes.data, + edid_blk.audio_data_blk, + edid_blk.audio_data_blk_size); + memcpy((ucontrol->value.bytes.data + + edid_blk.audio_data_blk_size), + edid_blk.spk_alloc_data_blk, + edid_blk.spk_alloc_data_blk_size); + + dev_dbg(codec->dev, "%s: data_blk_size:%d, spk_alloc_data_blk_size:%d\n", + __func__, edid_blk.audio_data_blk_size, + edid_blk.spk_alloc_data_blk_size); + } + + return rc; +} + +static int msm_ext_disp_audio_type_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct msm_ext_disp_audio_codec_rx_data *codec_data; + enum msm_ext_disp_cable_state cable_state; + enum msm_ext_disp_type disp_type; + struct msm_ext_disp_codec_id codec_info; + int rc = 0; + int dai_id = ((struct soc_enum *) kcontrol->private_value)->shift_l; + + codec_data = snd_soc_codec_get_drvdata(codec); + if (!codec_data || + !codec_data->ext_disp_ops.get_audio_edid_blk || + !codec_data->ext_disp_ops.get_intf_id) { + dev_err(codec->dev, "%s: codec_data, get_audio_edid_blk() or get_intf_id is NULL\n", + __func__); + return -EINVAL; + } + + dev_dbg(codec->dev, "%s: DP ctl id %d Stream id %d\n", __func__, + codec_data->ctl[dai_id], codec_data->stream[dai_id]); + + mutex_lock(&codec_data->dp_ops_lock); + SWITCH_DP_CODEC(codec_info, codec_data, dai_id); + cable_state = codec_data->ext_disp_ops.cable_status( + codec_data->ext_disp_core_pdev, 1); + if (cable_state < 0) { + dev_err(codec->dev, "%s: Error retrieving cable state from ext_disp, err:%d\n", + __func__, cable_state); + rc = cable_state; + goto cable_err; + } + + codec_data->cable_status = cable_state; + if (cable_state == EXT_DISPLAY_CABLE_DISCONNECT) { + dev_err(codec->dev, "%s: Display cable disconnected\n", + __func__); + ucontrol->value.integer.value[0] = 0; + rc = 0; + goto cable_err; + } + + disp_type = codec_data->ext_disp_ops.get_intf_id( + codec_data->ext_disp_core_pdev); + mutex_unlock(&codec_data->dp_ops_lock); + if (disp_type >= 0) { + switch (disp_type) { + case EXT_DISPLAY_TYPE_DP: + ucontrol->value.integer.value[0] = 2; + rc = 0; + break; + case EXT_DISPLAY_TYPE_HDMI: + ucontrol->value.integer.value[0] = 1; + rc = 0; + break; + default: + rc = -EINVAL; + dev_err(codec->dev, "%s: Invalid disp_type:%d\n", + __func__, disp_type); + goto done; + } + dev_dbg(codec->dev, "%s: Display type: %d\n", + __func__, disp_type); + } else { + dev_err(codec->dev, "%s: Error retrieving disp_type from ext_disp, err:%d\n", + __func__, disp_type); + rc = disp_type; + } + return rc; + +cable_err: + mutex_unlock(&codec_data->dp_ops_lock); +done: + return rc; +} + +static int msm_ext_disp_audio_ack_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct msm_ext_disp_audio_codec_rx_data *codec_data; + u32 ack_state = 0; + struct msm_ext_disp_codec_id codec_info; + int rc = 0; + int dai_id = ((struct soc_enum *) kcontrol->private_value)->shift_l; + + codec_data = snd_soc_codec_get_drvdata(codec); + if (!codec_data || + !codec_data->ext_disp_ops.acknowledge) { + dev_err(codec->dev, + "%s: codec_data or ops acknowledge() is NULL\n", + __func__); + rc = -EINVAL; + goto done; + } + + dev_dbg(codec->dev, "%s: DP ctl id %d Stream id %d\n", __func__, + codec_data->ctl[dai_id], codec_data->stream[dai_id]); + + switch (ucontrol->value.enumerated.item[0]) { + case 0: + ack_state = AUD_EXT_DISP_ACK_DISCONNECT; + break; + case 1: + ack_state = AUD_EXT_DISP_ACK_CONNECT; + break; + case 2: + ack_state = AUD_EXT_DISP_ACK_ENABLE; + break; + default: + rc = -EINVAL; + dev_err(codec->dev, + "%s: invalid value %d for mixer ctl\n", + __func__, ucontrol->value.enumerated.item[0]); + goto done; + } + dev_dbg(codec->dev, "%s: control %d, ack set value 0x%x\n", + __func__, ucontrol->value.enumerated.item[0], ack_state); + + mutex_lock(&codec_data->dp_ops_lock); + SWITCH_DP_CODEC(codec_info, codec_data, dai_id); + rc = codec_data->ext_disp_ops.acknowledge( + codec_data->ext_disp_core_pdev, ack_state); + mutex_unlock(&codec_data->dp_ops_lock); + if (rc < 0) { + dev_err(codec->dev, "%s: error from acknowledge(), err:%d\n", + __func__, rc); + } + +done: + return rc; +} + +static int msm_ext_disp_audio_device_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct msm_ext_disp_audio_codec_rx_data *codec_data; + int rc = 0; + int dai_id = ((struct soc_enum *) kcontrol->private_value)->shift_l; + + codec_data = snd_soc_codec_get_drvdata(codec); + if (!codec_data) { + dev_err(codec->dev, + "%s: codec_data or ops acknowledge() is NULL\n", + __func__); + rc = -EINVAL; + goto done; + } + + mutex_lock(&codec_data->dp_ops_lock); + codec_data->ctl[dai_id] = ucontrol->value.enumerated.item[0]; + codec_data->stream[dai_id] = ucontrol->value.enumerated.item[1]; + mutex_unlock(&codec_data->dp_ops_lock); + +done: + return rc; +} + +static const struct snd_kcontrol_new msm_ext_disp_codec_rx_controls[] = { + { + .access = SNDRV_CTL_ELEM_ACCESS_READ | + SNDRV_CTL_ELEM_ACCESS_VOLATILE, + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "HDMI EDID", + .info = msm_ext_disp_edid_ctl_info, + .get = msm_ext_disp_edid_get, + .private_value = HDMI_DAI, + }, + { + .access = SNDRV_CTL_ELEM_ACCESS_READ | + SNDRV_CTL_ELEM_ACCESS_VOLATILE, + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "Display Port EDID", + .info = msm_ext_disp_edid_ctl_info, + .get = msm_ext_disp_edid_get, + .private_value = DP_DAI1, + }, + { + .access = SNDRV_CTL_ELEM_ACCESS_READ | + SNDRV_CTL_ELEM_ACCESS_VOLATILE, + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "Display Port1 EDID", + .info = msm_ext_disp_edid_ctl_info, + .get = msm_ext_disp_edid_get, + .private_value = DP_DAI2, + }, + SOC_ENUM_EXT("External Display Type", + ext_disp_audio_type0, + msm_ext_disp_audio_type_get, NULL), + SOC_ENUM_EXT("External Display1 Type", + ext_disp_audio_type1, + msm_ext_disp_audio_type_get, NULL), + SOC_ENUM_EXT("External Display Audio Ack", + ext_disp_audio_ack_state0, + NULL, msm_ext_disp_audio_ack_set), + SOC_ENUM_EXT("External Display1 Audio Ack", + ext_disp_audio_ack_state1, + NULL, msm_ext_disp_audio_ack_set), + + SOC_SINGLE_EXT("External Display Audio Device", + SND_SOC_NOPM, DP_DAI1, DP_STREAM_MAX, 0, + NULL, msm_ext_disp_audio_device_set), + SOC_SINGLE_EXT("External Display1 Audio Device", + SND_SOC_NOPM, DP_DAI2, DP_STREAM_MAX, 0, + NULL, msm_ext_disp_audio_device_set), +}; + +static int msm_ext_disp_audio_codec_rx_dai_startup( + struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + int ret = 0; + struct msm_ext_disp_codec_id codec_info; + struct msm_ext_disp_audio_codec_rx_data *codec_data = + dev_get_drvdata(dai->codec->dev); + + if (!codec_data || !codec_data->ext_disp_ops.cable_status) { + dev_err(dai->dev, "%s() codec_data or cable_status is null\n", + __func__); + return -EINVAL; + } + + dev_dbg(dai->codec->dev, "%s: DP ctl id %d Stream id %d\n", __func__, + codec_data->ctl[dai->id], codec_data->stream[dai->id]); + + mutex_lock(&codec_data->dp_ops_lock); + SWITCH_DP_CODEC(codec_info, codec_data, dai->id); + codec_data->cable_status = + codec_data->ext_disp_ops.cable_status( + codec_data->ext_disp_core_pdev, 1); + mutex_unlock(&codec_data->dp_ops_lock); + if (codec_data->cable_status < 0) { + dev_err(dai->dev, + "%s() ext disp core is not ready (ret val = %d)\n", + __func__, codec_data->cable_status); + ret = codec_data->cable_status; + } else if (!codec_data->cable_status) { + dev_err(dai->dev, + "%s() ext disp cable is not connected (ret val = %d)\n", + __func__, codec_data->cable_status); + ret = -ENODEV; + } + + return ret; +} + +static int msm_ext_disp_audio_codec_rx_dai_hw_params( + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + u32 channel_allocation = 0; + u32 level_shift = 0; /* 0dB */ + bool down_mix = 0; + u32 num_channels = params_channels(params); + struct msm_ext_disp_codec_id codec_info; + int rc = 0; + struct msm_ext_disp_audio_setup_params audio_setup_params = {0}; + + struct msm_ext_disp_audio_codec_rx_data *codec_data = + dev_get_drvdata(dai->codec->dev); + + if (!codec_data || !codec_data->ext_disp_ops.audio_info_setup) { + dev_err(dai->dev, "%s: codec_data or audio_info_setup is null\n", + __func__); + return -EINVAL; + } + + dev_dbg(dai->codec->dev, "%s: DP ctl id %d Stream id %d\n", __func__, + codec_data->ctl[dai->id], codec_data->stream[dai->id]); + + if (codec_data->cable_status < 0) { + dev_err_ratelimited(dai->dev, + "%s() ext disp core is not ready (ret val = %d)\n", + __func__, codec_data->cable_status); + return codec_data->cable_status; + } else if (!codec_data->cable_status) { + dev_err_ratelimited(dai->dev, + "%s() ext disp cable is not connected (ret val = %d)\n", + __func__, codec_data->cable_status); + return -ENODEV; + } + + /*refer to HDMI spec CEA-861-E: Table 28 Audio InfoFrame Data Byte 4*/ + switch (num_channels) { + case 2: + channel_allocation = 0; + break; + case 3: + channel_allocation = 0x02;/*default to FL/FR/FC*/ + audio_setup_params.sample_present = 0x3; + break; + case 4: + channel_allocation = 0x06;/*default to FL/FR/FC/RC*/ + audio_setup_params.sample_present = 0x7; + break; + case 5: + channel_allocation = 0x0A;/*default to FL/FR/FC/RR/RL*/ + audio_setup_params.sample_present = 0x7; + break; + case 6: + channel_allocation = 0x0B; + audio_setup_params.sample_present = 0x7; + break; + case 7: + channel_allocation = 0x12;/*default to FL/FR/FC/RL/RR/RRC/RLC*/ + audio_setup_params.sample_present = 0xf; + break; + case 8: + channel_allocation = 0x13; + audio_setup_params.sample_present = 0xf; + break; + default: + dev_err(dai->dev, "invalid Channels = %u\n", num_channels); + return -EINVAL; + } + + dev_dbg(dai->dev, + "%s() num_ch %u samplerate %u channel_allocation = %u\n", + __func__, num_channels, params_rate(params), + channel_allocation); + + audio_setup_params.sample_rate_hz = params_rate(params); + audio_setup_params.num_of_channels = num_channels; + audio_setup_params.channel_allocation = channel_allocation; + audio_setup_params.level_shift = level_shift; + audio_setup_params.down_mix = down_mix; + + mutex_lock(&codec_data->dp_ops_lock); + SWITCH_DP_CODEC(codec_info, codec_data, dai->id); + rc = codec_data->ext_disp_ops.audio_info_setup( + codec_data->ext_disp_core_pdev, &audio_setup_params); + mutex_unlock(&codec_data->dp_ops_lock); + if (rc < 0) { + dev_err_ratelimited(dai->dev, + "%s() ext disp core is not ready, rc: %d\n", + __func__, rc); + } + + return rc; +} + +static void msm_ext_disp_audio_codec_rx_dai_shutdown( + struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + int rc = 0; + struct msm_ext_disp_codec_id codec_info; + + struct msm_ext_disp_audio_codec_rx_data *codec_data = + dev_get_drvdata(dai->codec->dev); + + if (!codec_data || !codec_data->ext_disp_ops.teardown_done || + !codec_data->ext_disp_ops.cable_status) { + dev_err(dai->dev, "%s: codec data or teardown_done or cable_status is null\n", + __func__); + return; + } + + dev_dbg(dai->codec->dev, "%s: DP ctl id %d Stream id %d\n", __func__, + codec_data->ctl[dai->id], codec_data->stream[dai->id]); + + mutex_lock(&codec_data->dp_ops_lock); + SWITCH_DP_CODEC(codec_info, codec_data, dai->id); + rc = codec_data->ext_disp_ops.cable_status( + codec_data->ext_disp_core_pdev, 0); + if (rc < 0) { + dev_err(dai->dev, + "%s: ext disp core had problems releasing audio flag\n", + __func__); + } + + codec_data->ext_disp_ops.teardown_done( + codec_data->ext_disp_core_pdev); + mutex_unlock(&codec_data->dp_ops_lock); +} + +static int msm_ext_disp_audio_codec_rx_probe(struct snd_soc_codec *codec) +{ + struct msm_ext_disp_audio_codec_rx_data *codec_data; + struct device_node *of_node_parent = NULL; + + codec_data = kzalloc(sizeof(struct msm_ext_disp_audio_codec_rx_data), + GFP_KERNEL); + + if (!codec_data) { + dev_err(codec->dev, "%s(): fail to allocate dai data\n", + __func__); + return -ENOMEM; + } + + of_node_parent = of_get_parent(codec->dev->of_node); + if (!of_node_parent) { + dev_err(codec->dev, "%s(): Parent device tree node not found\n", + __func__); + kfree(codec_data); + return -ENODEV; + } + + codec_data->ext_disp_core_pdev = of_find_device_by_node(of_node_parent); + if (!codec_data->ext_disp_core_pdev) { + dev_err(codec->dev, "%s(): can't get parent pdev\n", __func__); + kfree(codec_data); + return -ENODEV; + } + + if (msm_ext_disp_register_audio_codec(codec_data->ext_disp_core_pdev, + &codec_data->ext_disp_ops)) { + dev_err(codec->dev, "%s(): can't register with ext disp core", + __func__); + kfree(codec_data); + return -ENODEV; + } + + mutex_init(&codec_data->dp_ops_lock); + dev_set_drvdata(codec->dev, codec_data); + + dev_dbg(codec->dev, "%s(): registered %s with ext disp core\n", + __func__, codec->component.name); + + return 0; +} + +static int msm_ext_disp_audio_codec_rx_remove(struct snd_soc_codec *codec) +{ + struct msm_ext_disp_audio_codec_rx_data *codec_data; + + codec_data = dev_get_drvdata(codec->dev); + mutex_destroy(&codec_data->dp_ops_lock); + kfree(codec_data); + + return 0; +} + +static struct snd_soc_dai_ops msm_ext_disp_audio_codec_rx_dai_ops = { + .startup = msm_ext_disp_audio_codec_rx_dai_startup, + .hw_params = msm_ext_disp_audio_codec_rx_dai_hw_params, + .shutdown = msm_ext_disp_audio_codec_rx_dai_shutdown +}; + +static struct snd_soc_dai_driver msm_ext_disp_audio_codec_rx_dais[] = { + { + .name = "msm_hdmi_audio_codec_rx_dai", + .id = HDMI_DAI, + .playback = { + .stream_name = "HDMI Playback", + .channels_min = 1, + .channels_max = 8, + .rate_min = 48000, + .rate_max = 48000, + .rates = MSM_EXT_DISP_PCM_RATES, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .ops = &msm_ext_disp_audio_codec_rx_dai_ops, + }, + { + .name = "msm_dp_audio_codec_rx_dai", + .id = DP_DAI1, + .playback = { + .stream_name = "Display Port Playback", + .channels_min = 1, + .channels_max = 8, + .rate_min = 48000, + .rate_max = 192000, + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE, + }, + .ops = &msm_ext_disp_audio_codec_rx_dai_ops, + }, + { + .name = "msm_dp_audio_codec_rx1_dai", + .id = DP_DAI2, + .playback = { + .stream_name = "Display Port1 Playback", + .channels_min = 1, + .channels_max = 8, + .rate_min = 48000, + .rate_max = 192000, + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE, + }, + .ops = &msm_ext_disp_audio_codec_rx_dai_ops, + }, +}; + +static struct snd_soc_codec_driver msm_ext_disp_audio_codec_rx_soc_driver = { + .probe = msm_ext_disp_audio_codec_rx_probe, + .remove = msm_ext_disp_audio_codec_rx_remove, + .component_driver = { + .controls = msm_ext_disp_codec_rx_controls, + .num_controls = ARRAY_SIZE(msm_ext_disp_codec_rx_controls), + }, +}; + +static int msm_ext_disp_audio_codec_rx_plat_probe( + struct platform_device *pdev) +{ + dev_dbg(&pdev->dev, "%s(): dev name %s\n", __func__, + dev_name(&pdev->dev)); + + return snd_soc_register_codec(&pdev->dev, + &msm_ext_disp_audio_codec_rx_soc_driver, + msm_ext_disp_audio_codec_rx_dais, + ARRAY_SIZE(msm_ext_disp_audio_codec_rx_dais)); +} + +static int msm_ext_disp_audio_codec_rx_plat_remove( + struct platform_device *pdev) +{ + snd_soc_unregister_codec(&pdev->dev); + return 0; +} +static const struct of_device_id msm_ext_disp_audio_codec_rx_dt_match[] = { + { .compatible = "qcom,msm-ext-disp-audio-codec-rx", }, + {} +}; +MODULE_DEVICE_TABLE(of, msm_ext_disp_audio_codec_rx_dt_match); + +static struct platform_driver msm_ext_disp_audio_codec_rx_driver = { + .driver = { + .name = "msm-ext-disp-audio-codec-rx", + .owner = THIS_MODULE, + .of_match_table = msm_ext_disp_audio_codec_rx_dt_match, + }, + .probe = msm_ext_disp_audio_codec_rx_plat_probe, + .remove = msm_ext_disp_audio_codec_rx_plat_remove, +}; + +static int __init msm_ext_disp_audio_codec_rx_init(void) +{ + int rc = 0; + + rc = platform_driver_register(&msm_ext_disp_audio_codec_rx_driver); + if (rc) { + pr_err("%s: failed to register ext disp codec driver err:%d\n", + __func__, rc); + } + + return rc; +} +module_init(msm_ext_disp_audio_codec_rx_init); + +static void __exit msm_ext_disp_audio_codec_rx_exit(void) +{ + platform_driver_unregister(&msm_ext_disp_audio_codec_rx_driver); +} +module_exit(msm_ext_disp_audio_codec_rx_exit); + +MODULE_DESCRIPTION("MSM External Display Audio CODEC Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/audio-kernel/asoc/codecs/msm_sdw/Android.mk b/audio-kernel/asoc/codecs/msm_sdw/Android.mk new file mode 100644 index 000000000..7ec659200 --- /dev/null +++ b/audio-kernel/asoc/codecs/msm_sdw/Android.mk @@ -0,0 +1,46 @@ +# Android makefile for audio kernel modules + +# Assume no targets will be supported + +AUDIO_CHIPSET := audio +# Build/Package only in case of supported target +ifeq ($(call is-board-platform-in-list,msm8953 sdm670 qcs605),true) +AUDIO_SELECT := CONFIG_SND_SOC_SDM670=m + +LOCAL_PATH := $(call my-dir) + +# This makefile is only for DLKM +ifneq ($(findstring vendor,$(LOCAL_PATH)),) + +ifneq ($(findstring opensource,$(LOCAL_PATH)),) + AUDIO_BLD_DIR := $(shell pwd)/vendor/qcom/opensource/audio-kernel +endif # opensource + +DLKM_DIR := $(TOP)/device/qcom/common/dlkm + +# Build audio.ko as $(AUDIO_CHIPSET)_audio.ko +########################################################### +# This is set once per LOCAL_PATH, not per (kernel) module +KBUILD_OPTIONS := AUDIO_ROOT=$(AUDIO_BLD_DIR) + +# We are actually building audio.ko here, as per the +# requirement we are specifying _audio.ko as LOCAL_MODULE. +# This means we need to rename the module to _audio.ko +# after audio.ko is built. +KBUILD_OPTIONS += MODNAME=msm_sdw_dlkm +KBUILD_OPTIONS += BOARD_PLATFORM=$(TARGET_BOARD_PLATFORM) +KBUILD_OPTIONS += $(AUDIO_SELECT) + +########################################################### +include $(CLEAR_VARS) +LOCAL_MODULE := $(AUDIO_CHIPSET)_msm_sdw.ko +LOCAL_MODULE_KBUILD_NAME := msm_sdw_dlkm.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_DEBUG_ENABLE := true +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk +########################################################### +########################################################### + +endif # DLKM check +endif # supported target check diff --git a/audio-kernel/asoc/codecs/msm_sdw/Kbuild b/audio-kernel/asoc/codecs/msm_sdw/Kbuild new file mode 100644 index 000000000..73db130cb --- /dev/null +++ b/audio-kernel/asoc/codecs/msm_sdw/Kbuild @@ -0,0 +1,119 @@ +# We can build either as part of a standalone Kernel build or as +# an external module. Determine which mechanism is being used +ifeq ($(MODNAME),) + KERNEL_BUILD := 1 +else + KERNEL_BUILD := 0 +endif + + +ifeq ($(KERNEL_BUILD), 1) + # These are configurable via Kconfig for kernel-based builds + # Need to explicitly configure for Android-based builds + AUDIO_BLD_DIR := $(shell pwd)/kernel/msm-4.9 + AUDIO_ROOT := $(AUDIO_BLD_DIR)/techpack/audio +endif + +ifeq ($(KERNEL_BUILD), 0) + ifeq ($(CONFIG_ARCH_SDM845), y) + include $(AUDIO_ROOT)/config/sdm845auto.conf + export + INCS += -include $(AUDIO_ROOT)/config/sdm845autoconf.h + endif + ifeq ($(CONFIG_ARCH_SDM670), y) + include $(AUDIO_ROOT)/config/sdm670auto.conf + export + INCS += -include $(AUDIO_ROOT)/config/sdm670autoconf.h + endif + ifeq ($(CONFIG_ARCH_SDM450), y) + include $(AUDIO_ROOT)/config/sdm670auto.conf + export + INCS += -include $(AUDIO_ROOT)/config/sdm670autoconf.h + endif +endif + +# As per target team, build is done as follows: +# Defconfig : build with default flags +# Slub : defconfig + CONFIG_SLUB_DEBUG := y + +# CONFIG_SLUB_DEBUG_ON := y + CONFIG_PAGE_POISONING := y +# Perf : Using appropriate msmXXXX-perf_defconfig +# +# Shipment builds (user variants) should not have any debug feature +# enabled. This is identified using 'TARGET_BUILD_VARIANT'. Slub builds +# are identified using the CONFIG_SLUB_DEBUG_ON configuration. Since +# there is no other way to identify defconfig builds, QTI internal +# representation of perf builds (identified using the string 'perf'), +# is used to identify if the build is a slub or defconfig one. This +# way no critical debug feature will be enabled for perf and shipment +# builds. Other OEMs are also protected using the TARGET_BUILD_VARIANT +# config. + +############ UAPI ############ +UAPI_DIR := uapi +UAPI_INC := -I$(AUDIO_ROOT)/include/$(UAPI_DIR) + +############ COMMON ############ +COMMON_DIR := include +COMMON_INC := -I$(AUDIO_ROOT)/$(COMMON_DIR) + +############ MSM Soundwire ############ + +# for MSM Soundwire Codec +ifdef CONFIG_SND_SOC_MSM_SDW + MSM_SDW_OBJS += msm_sdw_cdc.o + MSM_SDW_OBJS += msm_sdw_regmap.o + MSM_SDW_OBJS += msm-sdw-tables.o + MSM_SDW_OBJS += msm_sdw_cdc_utils.o +endif + +LINUX_INC += -Iinclude/linux + +INCS += $(COMMON_INC) \ + $(UAPI_INC) + +EXTRA_CFLAGS += $(INCS) + + +CDEFINES += -DANI_LITTLE_BYTE_ENDIAN \ + -DANI_LITTLE_BIT_ENDIAN \ + -DDOT11F_LITTLE_ENDIAN_HOST \ + -DANI_COMPILER_TYPE_GCC \ + -DANI_OS_TYPE_ANDROID=6 \ + -DPTT_SOCK_SVC_ENABLE \ + -Wall\ + -Werror\ + -D__linux__ + +KBUILD_CPPFLAGS += $(CDEFINES) + +# Currently, for versions of gcc which support it, the kernel Makefile +# is disabling the maybe-uninitialized warning. Re-enable it for the +# AUDIO driver. Note that we must use EXTRA_CFLAGS here so that it +# will override the kernel settings. +ifeq ($(call cc-option-yn, -Wmaybe-uninitialized),y) +EXTRA_CFLAGS += -Wmaybe-uninitialized +endif +#EXTRA_CFLAGS += -Wmissing-prototypes + +ifeq ($(call cc-option-yn, -Wheader-guard),y) +EXTRA_CFLAGS += -Wheader-guard +endif + +ifeq ($(KERNEL_BUILD), 0) +KBUILD_EXTRA_SYMBOLS +=$(OUT)/obj/vendor/qcom/opensource/audio-kernel/ipc/Module.symvers +KBUILD_EXTRA_SYMBOLS +=$(OUT)/obj/vendor/qcom/opensource/audio-kernel/dsp/Module.symvers +KBUILD_EXTRA_SYMBOLS +=$(OUT)/obj/vendor/qcom/opensource/audio-kernel/asoc/Module.symvers +KBUILD_EXTRA_SYMBOLS +=$(OUT)/obj/vendor/qcom/opensource/audio-kernel/asoc/codecs/Module.symvers +KBUILD_EXTRA_SYMBOLS +=$(OUT)/obj/vendor/qcom/opensource/audio-kernel/soc/Module.symvers +endif + +ifeq ($(CONFIG_SND_SOC_GCOV), y) +GCOV_PROFILE := y +endif + +# Module information used by KBuild framework +obj-$(CONFIG_SND_SOC_MSM_SDW) += msm_sdw_dlkm.o +msm_sdw_dlkm-y := $(MSM_SDW_OBJS) + +# inject some build related information +DEFINES += -DBUILD_TIMESTAMP=\"$(shell date -u +'%Y-%m-%dT%H:%M:%SZ')\" diff --git a/audio-kernel/asoc/codecs/msm_sdw/msm-sdw-tables.c b/audio-kernel/asoc/codecs/msm_sdw/msm-sdw-tables.c new file mode 100644 index 000000000..1b51805bb --- /dev/null +++ b/audio-kernel/asoc/codecs/msm_sdw/msm-sdw-tables.c @@ -0,0 +1,319 @@ +/* Copyright (c) 2016-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 + * 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 +#include "msm_sdw.h" + +const u8 msm_sdw_page_map[MSM_SDW_MAX_REGISTER] = { + [MSM_SDW_TX9_SPKR_PROT_PATH_CTL] = 0xa, + [MSM_SDW_TX9_SPKR_PROT_PATH_CFG0] = 0xa, + [MSM_SDW_TX10_SPKR_PROT_PATH_CTL] = 0xa, + [MSM_SDW_TX10_SPKR_PROT_PATH_CFG0] = 0xa, + [MSM_SDW_TX11_SPKR_PROT_PATH_CTL] = 0xa, + [MSM_SDW_TX11_SPKR_PROT_PATH_CFG0] = 0xa, + [MSM_SDW_TX12_SPKR_PROT_PATH_CTL] = 0xa, + [MSM_SDW_TX12_SPKR_PROT_PATH_CFG0] = 0xa, + [MSM_SDW_COMPANDER7_CTL0] = 0xb, + [MSM_SDW_COMPANDER7_CTL1] = 0xb, + [MSM_SDW_COMPANDER7_CTL2] = 0xb, + [MSM_SDW_COMPANDER7_CTL3] = 0xb, + [MSM_SDW_COMPANDER7_CTL4] = 0xb, + [MSM_SDW_COMPANDER7_CTL5] = 0xb, + [MSM_SDW_COMPANDER7_CTL6] = 0xb, + [MSM_SDW_COMPANDER7_CTL7] = 0xb, + [MSM_SDW_COMPANDER8_CTL0] = 0xb, + [MSM_SDW_COMPANDER8_CTL1] = 0xb, + [MSM_SDW_COMPANDER8_CTL2] = 0xb, + [MSM_SDW_COMPANDER8_CTL3] = 0xb, + [MSM_SDW_COMPANDER8_CTL4] = 0xb, + [MSM_SDW_COMPANDER8_CTL5] = 0xb, + [MSM_SDW_COMPANDER8_CTL6] = 0xb, + [MSM_SDW_COMPANDER8_CTL7] = 0xb, + [MSM_SDW_RX7_RX_PATH_CTL] = 0xb, + [MSM_SDW_RX7_RX_PATH_CFG0] = 0xb, + [MSM_SDW_RX7_RX_PATH_CFG1] = 0xb, + [MSM_SDW_RX7_RX_PATH_CFG2] = 0xb, + [MSM_SDW_RX7_RX_VOL_CTL] = 0xb, + [MSM_SDW_RX7_RX_PATH_MIX_CTL] = 0xb, + [MSM_SDW_RX7_RX_PATH_MIX_CFG] = 0xb, + [MSM_SDW_RX7_RX_VOL_MIX_CTL] = 0xb, + [MSM_SDW_RX7_RX_PATH_SEC0] = 0xb, + [MSM_SDW_RX7_RX_PATH_SEC1] = 0xb, + [MSM_SDW_RX7_RX_PATH_SEC2] = 0xb, + [MSM_SDW_RX7_RX_PATH_SEC3] = 0xb, + [MSM_SDW_RX7_RX_PATH_SEC5] = 0xb, + [MSM_SDW_RX7_RX_PATH_SEC6] = 0xb, + [MSM_SDW_RX7_RX_PATH_SEC7] = 0xb, + [MSM_SDW_RX7_RX_PATH_MIX_SEC0] = 0xb, + [MSM_SDW_RX7_RX_PATH_MIX_SEC1] = 0xb, + [MSM_SDW_RX8_RX_PATH_CTL] = 0xb, + [MSM_SDW_RX8_RX_PATH_CFG0] = 0xb, + [MSM_SDW_RX8_RX_PATH_CFG1] = 0xb, + [MSM_SDW_RX8_RX_PATH_CFG2] = 0xb, + [MSM_SDW_RX8_RX_VOL_CTL] = 0xb, + [MSM_SDW_RX8_RX_PATH_MIX_CTL] = 0xb, + [MSM_SDW_RX8_RX_PATH_MIX_CFG] = 0xb, + [MSM_SDW_RX8_RX_VOL_MIX_CTL] = 0xb, + [MSM_SDW_RX8_RX_PATH_SEC0] = 0xb, + [MSM_SDW_RX8_RX_PATH_SEC1] = 0xb, + [MSM_SDW_RX8_RX_PATH_SEC2] = 0xb, + [MSM_SDW_RX8_RX_PATH_SEC3] = 0xb, + [MSM_SDW_RX8_RX_PATH_SEC5] = 0xb, + [MSM_SDW_RX8_RX_PATH_SEC6] = 0xb, + [MSM_SDW_RX8_RX_PATH_SEC7] = 0xb, + [MSM_SDW_RX8_RX_PATH_MIX_SEC0] = 0xb, + [MSM_SDW_RX8_RX_PATH_MIX_SEC1] = 0xb, + [MSM_SDW_BOOST0_BOOST_PATH_CTL] = 0xc, + [MSM_SDW_BOOST0_BOOST_CTL] = 0xc, + [MSM_SDW_BOOST0_BOOST_CFG1] = 0xc, + [MSM_SDW_BOOST0_BOOST_CFG2] = 0xc, + [MSM_SDW_BOOST1_BOOST_PATH_CTL] = 0xc, + [MSM_SDW_BOOST1_BOOST_CTL] = 0xc, + [MSM_SDW_BOOST1_BOOST_CFG1] = 0xc, + [MSM_SDW_BOOST1_BOOST_CFG2] = 0xc, + [MSM_SDW_AHB_BRIDGE_WR_DATA_0] = 0xc, + [MSM_SDW_AHB_BRIDGE_WR_DATA_1] = 0xc, + [MSM_SDW_AHB_BRIDGE_WR_DATA_2] = 0xc, + [MSM_SDW_AHB_BRIDGE_WR_DATA_3] = 0xc, + [MSM_SDW_AHB_BRIDGE_WR_ADDR_0] = 0xc, + [MSM_SDW_AHB_BRIDGE_WR_ADDR_1] = 0xc, + [MSM_SDW_AHB_BRIDGE_WR_ADDR_2] = 0xc, + [MSM_SDW_AHB_BRIDGE_WR_ADDR_3] = 0xc, + [MSM_SDW_AHB_BRIDGE_RD_ADDR_0] = 0xc, + [MSM_SDW_AHB_BRIDGE_RD_ADDR_1] = 0xc, + [MSM_SDW_AHB_BRIDGE_RD_ADDR_2] = 0xc, + [MSM_SDW_AHB_BRIDGE_RD_ADDR_3] = 0xc, + [MSM_SDW_AHB_BRIDGE_RD_DATA_0] = 0xc, + [MSM_SDW_AHB_BRIDGE_RD_DATA_1] = 0xc, + [MSM_SDW_AHB_BRIDGE_RD_DATA_2] = 0xc, + [MSM_SDW_AHB_BRIDGE_RD_DATA_3] = 0xc, + [MSM_SDW_AHB_BRIDGE_ACCESS_CFG] = 0xc, + [MSM_SDW_AHB_BRIDGE_ACCESS_STATUS] = 0xc, + [MSM_SDW_CLK_RST_CTRL_MCLK_CONTROL] = 0xd, + [MSM_SDW_CLK_RST_CTRL_FS_CNT_CONTROL] = 0xd, + [MSM_SDW_CLK_RST_CTRL_SWR_CONTROL] = 0xd, + [MSM_SDW_TOP_TOP_CFG0] = 0xd, + [MSM_SDW_TOP_TOP_CFG1] = 0xd, + [MSM_SDW_TOP_RX_I2S_CTL] = 0xd, + [MSM_SDW_TOP_TX_I2S_CTL] = 0xd, + [MSM_SDW_TOP_I2S_CLK] = 0xd, + [MSM_SDW_TOP_RX7_PATH_INPUT0_MUX] = 0xd, + [MSM_SDW_TOP_RX7_PATH_INPUT1_MUX] = 0xd, + [MSM_SDW_TOP_RX8_PATH_INPUT0_MUX] = 0xd, + [MSM_SDW_TOP_RX8_PATH_INPUT1_MUX] = 0xd, + [MSM_SDW_TOP_FREQ_MCLK] = 0xd, + [MSM_SDW_TOP_DEBUG_BUS_SEL] = 0xd, + [MSM_SDW_TOP_DEBUG_EN] = 0xd, + [MSM_SDW_TOP_I2S_RESET] = 0xd, + [MSM_SDW_TOP_BLOCKS_RESET] = 0xd, +}; + +const u8 msm_sdw_reg_readable[MSM_SDW_MAX_REGISTER] = { + [MSM_SDW_PAGE_REGISTER] = 1, + [MSM_SDW_TX9_SPKR_PROT_PATH_CTL] = 1, + [MSM_SDW_TX9_SPKR_PROT_PATH_CFG0] = 1, + [MSM_SDW_TX10_SPKR_PROT_PATH_CTL] = 1, + [MSM_SDW_TX10_SPKR_PROT_PATH_CFG0] = 1, + [MSM_SDW_TX11_SPKR_PROT_PATH_CTL] = 1, + [MSM_SDW_TX11_SPKR_PROT_PATH_CFG0] = 1, + [MSM_SDW_TX12_SPKR_PROT_PATH_CTL] = 1, + [MSM_SDW_TX12_SPKR_PROT_PATH_CFG0] = 1, + [MSM_SDW_COMPANDER7_CTL0] = 1, + [MSM_SDW_COMPANDER7_CTL1] = 1, + [MSM_SDW_COMPANDER7_CTL2] = 1, + [MSM_SDW_COMPANDER7_CTL3] = 1, + [MSM_SDW_COMPANDER7_CTL4] = 1, + [MSM_SDW_COMPANDER7_CTL5] = 1, + [MSM_SDW_COMPANDER7_CTL6] = 1, + [MSM_SDW_COMPANDER7_CTL7] = 1, + [MSM_SDW_COMPANDER8_CTL0] = 1, + [MSM_SDW_COMPANDER8_CTL1] = 1, + [MSM_SDW_COMPANDER8_CTL2] = 1, + [MSM_SDW_COMPANDER8_CTL3] = 1, + [MSM_SDW_COMPANDER8_CTL4] = 1, + [MSM_SDW_COMPANDER8_CTL5] = 1, + [MSM_SDW_COMPANDER8_CTL6] = 1, + [MSM_SDW_COMPANDER8_CTL7] = 1, + [MSM_SDW_RX7_RX_PATH_CTL] = 1, + [MSM_SDW_RX7_RX_PATH_CFG0] = 1, + [MSM_SDW_RX7_RX_PATH_CFG1] = 1, + [MSM_SDW_RX7_RX_PATH_CFG2] = 1, + [MSM_SDW_RX7_RX_VOL_CTL] = 1, + [MSM_SDW_RX7_RX_PATH_MIX_CTL] = 1, + [MSM_SDW_RX7_RX_PATH_MIX_CFG] = 1, + [MSM_SDW_RX7_RX_VOL_MIX_CTL] = 1, + [MSM_SDW_RX7_RX_PATH_SEC0] = 1, + [MSM_SDW_RX7_RX_PATH_SEC1] = 1, + [MSM_SDW_RX7_RX_PATH_SEC2] = 1, + [MSM_SDW_RX7_RX_PATH_SEC3] = 1, + [MSM_SDW_RX7_RX_PATH_SEC5] = 1, + [MSM_SDW_RX7_RX_PATH_SEC6] = 1, + [MSM_SDW_RX7_RX_PATH_SEC7] = 1, + [MSM_SDW_RX7_RX_PATH_MIX_SEC0] = 1, + [MSM_SDW_RX7_RX_PATH_MIX_SEC1] = 1, + [MSM_SDW_RX8_RX_PATH_CTL] = 1, + [MSM_SDW_RX8_RX_PATH_CFG0] = 1, + [MSM_SDW_RX8_RX_PATH_CFG1] = 1, + [MSM_SDW_RX8_RX_PATH_CFG2] = 1, + [MSM_SDW_RX8_RX_VOL_CTL] = 1, + [MSM_SDW_RX8_RX_PATH_MIX_CTL] = 1, + [MSM_SDW_RX8_RX_PATH_MIX_CFG] = 1, + [MSM_SDW_RX8_RX_VOL_MIX_CTL] = 1, + [MSM_SDW_RX8_RX_PATH_SEC0] = 1, + [MSM_SDW_RX8_RX_PATH_SEC1] = 1, + [MSM_SDW_RX8_RX_PATH_SEC2] = 1, + [MSM_SDW_RX8_RX_PATH_SEC3] = 1, + [MSM_SDW_RX8_RX_PATH_SEC5] = 1, + [MSM_SDW_RX8_RX_PATH_SEC6] = 1, + [MSM_SDW_RX8_RX_PATH_SEC7] = 1, + [MSM_SDW_RX8_RX_PATH_MIX_SEC0] = 1, + [MSM_SDW_RX8_RX_PATH_MIX_SEC1] = 1, + [MSM_SDW_BOOST0_BOOST_PATH_CTL] = 1, + [MSM_SDW_BOOST0_BOOST_CTL] = 1, + [MSM_SDW_BOOST0_BOOST_CFG1] = 1, + [MSM_SDW_BOOST0_BOOST_CFG2] = 1, + [MSM_SDW_BOOST1_BOOST_PATH_CTL] = 1, + [MSM_SDW_BOOST1_BOOST_CTL] = 1, + [MSM_SDW_BOOST1_BOOST_CFG1] = 1, + [MSM_SDW_BOOST1_BOOST_CFG2] = 1, + [MSM_SDW_AHB_BRIDGE_WR_DATA_0] = 1, + [MSM_SDW_AHB_BRIDGE_WR_DATA_1] = 1, + [MSM_SDW_AHB_BRIDGE_WR_DATA_2] = 1, + [MSM_SDW_AHB_BRIDGE_WR_DATA_3] = 1, + [MSM_SDW_AHB_BRIDGE_WR_ADDR_0] = 1, + [MSM_SDW_AHB_BRIDGE_WR_ADDR_1] = 1, + [MSM_SDW_AHB_BRIDGE_WR_ADDR_2] = 1, + [MSM_SDW_AHB_BRIDGE_WR_ADDR_3] = 1, + [MSM_SDW_AHB_BRIDGE_RD_ADDR_0] = 1, + [MSM_SDW_AHB_BRIDGE_RD_ADDR_1] = 1, + [MSM_SDW_AHB_BRIDGE_RD_ADDR_2] = 1, + [MSM_SDW_AHB_BRIDGE_RD_ADDR_3] = 1, + [MSM_SDW_AHB_BRIDGE_RD_DATA_0] = 1, + [MSM_SDW_AHB_BRIDGE_RD_DATA_1] = 1, + [MSM_SDW_AHB_BRIDGE_RD_DATA_2] = 1, + [MSM_SDW_AHB_BRIDGE_RD_DATA_3] = 1, + [MSM_SDW_AHB_BRIDGE_ACCESS_CFG] = 1, + [MSM_SDW_AHB_BRIDGE_ACCESS_STATUS] = 1, + [MSM_SDW_CLK_RST_CTRL_MCLK_CONTROL] = 1, + [MSM_SDW_CLK_RST_CTRL_FS_CNT_CONTROL] = 1, + [MSM_SDW_CLK_RST_CTRL_SWR_CONTROL] = 1, + [MSM_SDW_TOP_TOP_CFG0] = 1, + [MSM_SDW_TOP_TOP_CFG1] = 1, + [MSM_SDW_TOP_RX_I2S_CTL] = 1, + [MSM_SDW_TOP_TX_I2S_CTL] = 1, + [MSM_SDW_TOP_RX7_PATH_INPUT0_MUX] = 1, + [MSM_SDW_TOP_RX7_PATH_INPUT1_MUX] = 1, + [MSM_SDW_TOP_RX8_PATH_INPUT0_MUX] = 1, + [MSM_SDW_TOP_RX8_PATH_INPUT1_MUX] = 1, + [MSM_SDW_TOP_FREQ_MCLK] = 1, + [MSM_SDW_TOP_DEBUG_BUS_SEL] = 1, + [MSM_SDW_TOP_DEBUG_EN] = 1, + [MSM_SDW_TOP_I2S_RESET] = 1, + [MSM_SDW_TOP_BLOCKS_RESET] = 1, +}; + +const u8 msm_sdw_reg_writeable[MSM_SDW_MAX_REGISTER] = { + [MSM_SDW_PAGE_REGISTER] = 1, + [MSM_SDW_TX9_SPKR_PROT_PATH_CTL] = 1, + [MSM_SDW_TX9_SPKR_PROT_PATH_CFG0] = 1, + [MSM_SDW_TX10_SPKR_PROT_PATH_CTL] = 1, + [MSM_SDW_TX10_SPKR_PROT_PATH_CFG0] = 1, + [MSM_SDW_TX11_SPKR_PROT_PATH_CTL] = 1, + [MSM_SDW_TX11_SPKR_PROT_PATH_CFG0] = 1, + [MSM_SDW_TX12_SPKR_PROT_PATH_CTL] = 1, + [MSM_SDW_TX12_SPKR_PROT_PATH_CFG0] = 1, + [MSM_SDW_COMPANDER7_CTL0] = 1, + [MSM_SDW_COMPANDER7_CTL1] = 1, + [MSM_SDW_COMPANDER7_CTL2] = 1, + [MSM_SDW_COMPANDER7_CTL3] = 1, + [MSM_SDW_COMPANDER7_CTL4] = 1, + [MSM_SDW_COMPANDER7_CTL5] = 1, + [MSM_SDW_COMPANDER7_CTL7] = 1, + [MSM_SDW_COMPANDER8_CTL0] = 1, + [MSM_SDW_COMPANDER8_CTL1] = 1, + [MSM_SDW_COMPANDER8_CTL2] = 1, + [MSM_SDW_COMPANDER8_CTL3] = 1, + [MSM_SDW_COMPANDER8_CTL4] = 1, + [MSM_SDW_COMPANDER8_CTL5] = 1, + [MSM_SDW_COMPANDER8_CTL7] = 1, + [MSM_SDW_RX7_RX_PATH_CTL] = 1, + [MSM_SDW_RX7_RX_PATH_CFG0] = 1, + [MSM_SDW_RX7_RX_PATH_CFG1] = 1, + [MSM_SDW_RX7_RX_PATH_CFG2] = 1, + [MSM_SDW_RX7_RX_VOL_CTL] = 1, + [MSM_SDW_RX7_RX_PATH_MIX_CTL] = 1, + [MSM_SDW_RX7_RX_PATH_MIX_CFG] = 1, + [MSM_SDW_RX7_RX_VOL_MIX_CTL] = 1, + [MSM_SDW_RX7_RX_PATH_SEC0] = 1, + [MSM_SDW_RX7_RX_PATH_SEC1] = 1, + [MSM_SDW_RX7_RX_PATH_SEC2] = 1, + [MSM_SDW_RX7_RX_PATH_SEC3] = 1, + [MSM_SDW_RX7_RX_PATH_SEC5] = 1, + [MSM_SDW_RX7_RX_PATH_SEC6] = 1, + [MSM_SDW_RX7_RX_PATH_SEC7] = 1, + [MSM_SDW_RX7_RX_PATH_MIX_SEC0] = 1, + [MSM_SDW_RX7_RX_PATH_MIX_SEC1] = 1, + [MSM_SDW_RX8_RX_PATH_CTL] = 1, + [MSM_SDW_RX8_RX_PATH_CFG0] = 1, + [MSM_SDW_RX8_RX_PATH_CFG1] = 1, + [MSM_SDW_RX8_RX_PATH_CFG2] = 1, + [MSM_SDW_RX8_RX_VOL_CTL] = 1, + [MSM_SDW_RX8_RX_PATH_MIX_CTL] = 1, + [MSM_SDW_RX8_RX_PATH_MIX_CFG] = 1, + [MSM_SDW_RX8_RX_VOL_MIX_CTL] = 1, + [MSM_SDW_RX8_RX_PATH_SEC0] = 1, + [MSM_SDW_RX8_RX_PATH_SEC1] = 1, + [MSM_SDW_RX8_RX_PATH_SEC2] = 1, + [MSM_SDW_RX8_RX_PATH_SEC3] = 1, + [MSM_SDW_RX8_RX_PATH_SEC5] = 1, + [MSM_SDW_RX8_RX_PATH_SEC6] = 1, + [MSM_SDW_RX8_RX_PATH_SEC7] = 1, + [MSM_SDW_RX8_RX_PATH_MIX_SEC0] = 1, + [MSM_SDW_RX8_RX_PATH_MIX_SEC1] = 1, + [MSM_SDW_BOOST0_BOOST_PATH_CTL] = 1, + [MSM_SDW_BOOST0_BOOST_CTL] = 1, + [MSM_SDW_BOOST0_BOOST_CFG1] = 1, + [MSM_SDW_BOOST0_BOOST_CFG2] = 1, + [MSM_SDW_BOOST1_BOOST_PATH_CTL] = 1, + [MSM_SDW_BOOST1_BOOST_CTL] = 1, + [MSM_SDW_BOOST1_BOOST_CFG1] = 1, + [MSM_SDW_BOOST1_BOOST_CFG2] = 1, + [MSM_SDW_AHB_BRIDGE_WR_DATA_0] = 1, + [MSM_SDW_AHB_BRIDGE_WR_DATA_1] = 1, + [MSM_SDW_AHB_BRIDGE_WR_DATA_2] = 1, + [MSM_SDW_AHB_BRIDGE_WR_DATA_3] = 1, + [MSM_SDW_AHB_BRIDGE_WR_ADDR_0] = 1, + [MSM_SDW_AHB_BRIDGE_WR_ADDR_1] = 1, + [MSM_SDW_AHB_BRIDGE_WR_ADDR_2] = 1, + [MSM_SDW_AHB_BRIDGE_WR_ADDR_3] = 1, + [MSM_SDW_AHB_BRIDGE_RD_ADDR_0] = 1, + [MSM_SDW_AHB_BRIDGE_RD_ADDR_1] = 1, + [MSM_SDW_AHB_BRIDGE_RD_ADDR_2] = 1, + [MSM_SDW_AHB_BRIDGE_RD_ADDR_3] = 1, + [MSM_SDW_AHB_BRIDGE_ACCESS_CFG] = 1, + [MSM_SDW_CLK_RST_CTRL_MCLK_CONTROL] = 1, + [MSM_SDW_CLK_RST_CTRL_FS_CNT_CONTROL] = 1, + [MSM_SDW_CLK_RST_CTRL_SWR_CONTROL] = 1, + [MSM_SDW_TOP_TOP_CFG0] = 1, + [MSM_SDW_TOP_TOP_CFG1] = 1, + [MSM_SDW_TOP_RX_I2S_CTL] = 1, + [MSM_SDW_TOP_TX_I2S_CTL] = 1, + [MSM_SDW_TOP_RX7_PATH_INPUT0_MUX] = 1, + [MSM_SDW_TOP_RX7_PATH_INPUT1_MUX] = 1, + [MSM_SDW_TOP_RX8_PATH_INPUT0_MUX] = 1, + [MSM_SDW_TOP_RX8_PATH_INPUT1_MUX] = 1, + [MSM_SDW_TOP_FREQ_MCLK] = 1, + [MSM_SDW_TOP_DEBUG_BUS_SEL] = 1, + [MSM_SDW_TOP_DEBUG_EN] = 1, + [MSM_SDW_TOP_I2S_RESET] = 1, + [MSM_SDW_TOP_BLOCKS_RESET] = 1, +}; diff --git a/audio-kernel/asoc/codecs/msm_sdw/msm_sdw.h b/audio-kernel/asoc/codecs/msm_sdw/msm_sdw.h new file mode 100644 index 000000000..3c7a07dca --- /dev/null +++ b/audio-kernel/asoc/codecs/msm_sdw/msm_sdw.h @@ -0,0 +1,203 @@ +/* Copyright (c) 2016-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 MSM_SDW_H +#define MSM_SDW_H + +#include +#include +#include "msm_sdw_registers.h" + +#define MSM_SDW_MAX_REGISTER 0x400 +#define MSM_SDW_CHILD_DEVICES_MAX 1 + +extern const struct regmap_config msm_sdw_regmap_config; +extern const u8 msm_sdw_page_map[MSM_SDW_MAX_REGISTER]; +extern const u8 msm_sdw_reg_readable[MSM_SDW_MAX_REGISTER]; +extern const u8 msm_sdw_reg_writeable[MSM_SDW_MAX_REGISTER]; + +enum { + MSM_SDW_RX4 = 0, + MSM_SDW_RX5, + MSM_SDW_RX_MAX, +}; + +enum { + MSM_SDW_TX0 = 0, + MSM_SDW_TX1, + MSM_SDW_TX_MAX, +}; + +enum { + COMP1, /* SPK_L */ + COMP2, /* SPK_R */ + COMP_MAX +}; + +/* + * Structure used to update codec + * register defaults after reset + */ +struct msm_sdw_reg_mask_val { + u16 reg; + u8 mask; + u8 val; +}; + +/* + * Selects compander and smart boost settings + * for a given speaker mode + */ +enum { + SPKR_MODE_DEFAULT, + SPKR_MODE_1, /* COMP Gain = 12dB, Smartboost Max = 5.5V */ +}; + +/* Rx path gain offsets */ +enum { + RX_GAIN_OFFSET_M1P5_DB, + RX_GAIN_OFFSET_0_DB, +}; + +struct msm_sdw_reg_val { + unsigned short reg; /* register address */ + u8 *buf; /* buffer to be written to reg. addr */ + int bytes; /* number of bytes to be written */ +}; + +/* Hold instance to soundwire platform device */ +struct msm_sdw_ctrl_data { + struct platform_device *sdw_pdev; +}; + +struct wcd_sdw_ctrl_platform_data { + void *handle; /* holds codec private data */ + int (*read)(void *handle, int reg); + int (*write)(void *handle, int reg, int val); + int (*bulk_write)(void *handle, u32 *reg, u32 *val, size_t len); + int (*clk)(void *handle, bool enable); + int (*handle_irq)(void *handle, + irqreturn_t (*swrm_irq_handler)(int irq, + void *data), + void *swrm_handle, + int action); +}; + +struct msm_sdw_priv { + struct device *dev; + struct mutex io_lock; + + int (*read_dev)(struct msm_sdw_priv *msm_sdw, unsigned short reg, + int bytes, void *dest); + int (*write_dev)(struct msm_sdw_priv *msm_sdw, unsigned short reg, + int bytes, void *src); + int (*multi_reg_write)(struct msm_sdw_priv *msm_sdw, const void *data, + size_t count); + struct snd_soc_codec *codec; + struct device_node *sdw_gpio_p; /* used by pinctrl API */ + /* SoundWire data structure */ + struct msm_sdw_ctrl_data *sdw_ctrl_data; + int nr; + + /* compander */ + int comp_enabled[COMP_MAX]; + int ear_spkr_gain; + + /* to track the status */ + unsigned long status_mask; + + struct work_struct msm_sdw_add_child_devices_work; + struct wcd_sdw_ctrl_platform_data sdw_plat_data; + + unsigned int vi_feed_value; + + struct mutex sdw_read_lock; + struct mutex sdw_write_lock; + struct mutex sdw_clk_lock; + int sdw_clk_users; + int sdw_mclk_users; + + int sdw_irq; + int int_mclk1_rsc_ref; + bool int_mclk1_enabled; + bool sdw_npl_clk_enabled; + struct mutex cdc_int_mclk1_mutex; + struct mutex sdw_npl_clk_mutex; + struct delayed_work disable_int_mclk1_work; + struct afe_clk_set sdw_cdc_core_clk; + struct afe_clk_set sdw_npl_clk; + struct notifier_block service_nb; + int (*sdw_cdc_gpio_fn)(bool enable, struct snd_soc_codec *codec); + bool dev_up; + + int spkr_gain_offset; + int spkr_mode; + struct mutex codec_mutex; + int rx_4_count; + int rx_5_count; + u32 mclk_rate; + struct regmap *regmap; + + bool prev_pg_valid; + u8 prev_pg; + u32 sdw_base_addr; + char __iomem *sdw_base; + u32 version; + + /* Entry for version info */ + struct snd_info_entry *entry; + struct snd_info_entry *version_entry; + struct platform_device *pdev_child_devices + [MSM_SDW_CHILD_DEVICES_MAX]; + int child_count; +}; + +#if IS_ENABLED(CONFIG_SND_SOC_MSM_SDW) +extern int msm_sdw_set_spkr_mode(struct snd_soc_codec *codec, int mode); +extern int msm_sdw_set_spkr_gain_offset(struct snd_soc_codec *codec, + int offset); +extern void msm_sdw_gpio_cb( + int (*sdw_cdc_gpio_fn)(bool enable, struct snd_soc_codec *codec), + struct snd_soc_codec *codec); +extern struct regmap *msm_sdw_regmap_init(struct device *dev, + const struct regmap_config *config); +extern int msm_sdw_codec_info_create_codec_entry( + struct snd_info_entry *codec_root, + struct snd_soc_codec *codec); +#else /* CONFIG_SND_SOC_MSM_SDW */ +static inline int msm_sdw_set_spkr_mode(struct snd_soc_codec *codec, int mode) +{ + return 0; +} +static inline int msm_sdw_set_spkr_gain_offset(struct snd_soc_codec *codec, + int offset); +{ + return 0; +} +static inline void msm_sdw_gpio_cb( + int (*sdw_cdc_gpio_fn)(bool enable, struct snd_soc_codec *codec), + struct snd_soc_codec *codec); +{ + +} +static inline struct regmap *msm_sdw_regmap_init(struct device *dev, + const struct regmap_config *config); +{ + return NULL; +} +static inline int msm_sdw_codec_info_create_codec_entry( + struct snd_info_entry *codec_root, + struct snd_soc_codec *codec) +{ + return 0; +} +#endif /* CONFIG_SND_SOC_MSM_SDW */ +#endif diff --git a/audio-kernel/asoc/codecs/msm_sdw/msm_sdw_cdc.c b/audio-kernel/asoc/codecs/msm_sdw/msm_sdw_cdc.c new file mode 100644 index 000000000..69e340736 --- /dev/null +++ b/audio-kernel/asoc/codecs/msm_sdw/msm_sdw_cdc.c @@ -0,0 +1,2097 @@ +/* Copyright (c) 2016-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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "msm_sdw.h" +#include "msm_sdw_registers.h" +#include "../msm-cdc-pinctrl.h" + +#define MSM_SDW_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\ + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000) +#define MSM_SDW_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ + SNDRV_PCM_FMTBIT_S24_LE |\ + SNDRV_PCM_FMTBIT_S24_3LE) + +#define MSM_SDW_STRING_LEN 80 + +#define INT_MCLK1_FREQ 9600000 +#define SDW_NPL_FREQ 153600000 + +#define MSM_SDW_VERSION_1_0 0x0001 +#define MSM_SDW_VERSION_ENTRY_SIZE 32 + +/* + * 200 Milliseconds sufficient for DSP bring up in the modem + * after Sub System Restart + */ +#define ADSP_STATE_READY_TIMEOUT_MS 200 + +static const DECLARE_TLV_DB_SCALE(digital_gain, 0, 1, 0); +static struct snd_soc_dai_driver msm_sdw_dai[]; +static bool skip_irq = true; + +static int msm_sdw_config_ear_spkr_gain(struct snd_soc_codec *codec, + int event, int gain_reg); +static int msm_sdw_config_compander(struct snd_soc_codec *, int, int); +static int msm_sdw_mclk_enable(struct msm_sdw_priv *msm_sdw, + int mclk_enable, bool dapm); +static int msm_int_enable_sdw_cdc_clk(struct msm_sdw_priv *msm_sdw, + int enable, bool dapm); + +enum { + VI_SENSE_1, + VI_SENSE_2, +}; + +enum { + AIF1_SDW_PB = 0, + AIF1_SDW_VIFEED, + NUM_CODEC_DAIS, +}; + +static const struct msm_sdw_reg_mask_val msm_sdw_spkr_default[] = { + {MSM_SDW_COMPANDER7_CTL3, 0x80, 0x80}, + {MSM_SDW_COMPANDER8_CTL3, 0x80, 0x80}, + {MSM_SDW_COMPANDER7_CTL7, 0x01, 0x01}, + {MSM_SDW_COMPANDER8_CTL7, 0x01, 0x01}, + {MSM_SDW_BOOST0_BOOST_CTL, 0x7C, 0x58}, + {MSM_SDW_BOOST1_BOOST_CTL, 0x7C, 0x58}, +}; + +static const struct msm_sdw_reg_mask_val msm_sdw_spkr_mode1[] = { + {MSM_SDW_COMPANDER7_CTL3, 0x80, 0x00}, + {MSM_SDW_COMPANDER8_CTL3, 0x80, 0x00}, + {MSM_SDW_COMPANDER7_CTL7, 0x01, 0x00}, + {MSM_SDW_COMPANDER8_CTL7, 0x01, 0x00}, + {MSM_SDW_BOOST0_BOOST_CTL, 0x7C, 0x44}, + {MSM_SDW_BOOST1_BOOST_CTL, 0x7C, 0x44}, +}; + +/** + * msm_sdw_set_spkr_gain_offset - offset the speaker path + * gain with the given offset value. + * + * @codec: codec instance + * @offset: Indicates speaker path gain offset value. + * + * Returns 0 on success or -EINVAL on error. + */ +int msm_sdw_set_spkr_gain_offset(struct snd_soc_codec *codec, int offset) +{ + struct msm_sdw_priv *priv; + + if (!codec) { + pr_err("%s: NULL codec pointer!\n", __func__); + return -EINVAL; + } + + priv = snd_soc_codec_get_drvdata(codec); + if (!priv) + return -EINVAL; + + priv->spkr_gain_offset = offset; + return 0; +} +EXPORT_SYMBOL(msm_sdw_set_spkr_gain_offset); + +/** + * msm_sdw_set_spkr_mode - Configures speaker compander and smartboost + * settings based on speaker mode. + * + * @codec: codec instance + * @mode: Indicates speaker configuration mode. + * + * Returns 0 on success or -EINVAL on error. + */ +int msm_sdw_set_spkr_mode(struct snd_soc_codec *codec, int mode) +{ + struct msm_sdw_priv *priv; + int i; + const struct msm_sdw_reg_mask_val *regs; + int size; + + if (!codec) { + pr_err("%s: NULL codec pointer!\n", __func__); + return -EINVAL; + } + + priv = snd_soc_codec_get_drvdata(codec); + if (!priv) + return -EINVAL; + + switch (mode) { + case SPKR_MODE_1: + regs = msm_sdw_spkr_mode1; + size = ARRAY_SIZE(msm_sdw_spkr_mode1); + break; + default: + regs = msm_sdw_spkr_default; + size = ARRAY_SIZE(msm_sdw_spkr_default); + break; + } + + priv->spkr_mode = mode; + for (i = 0; i < size; i++) + snd_soc_update_bits(codec, regs[i].reg, + regs[i].mask, regs[i].val); + return 0; +} +EXPORT_SYMBOL(msm_sdw_set_spkr_mode); + +static int msm_enable_sdw_npl_clk(struct msm_sdw_priv *msm_sdw, int enable) +{ + int ret = 0; + + dev_dbg(msm_sdw->dev, "%s: enable %d\n", __func__, enable); + + mutex_lock(&msm_sdw->sdw_npl_clk_mutex); + if (enable) { + if (msm_sdw->sdw_npl_clk_enabled == false) { + msm_sdw->sdw_npl_clk.enable = 1; + ret = afe_set_lpass_clock_v2( + AFE_PORT_ID_INT4_MI2S_RX, + &msm_sdw->sdw_npl_clk); + if (ret < 0) { + dev_err(msm_sdw->dev, + "%s: failed to enable SDW NPL CLK\n", + __func__); + mutex_unlock(&msm_sdw->sdw_npl_clk_mutex); + return ret; + } + dev_dbg(msm_sdw->dev, "enabled sdw npl clk\n"); + msm_sdw->sdw_npl_clk_enabled = true; + } + } else { + if (msm_sdw->sdw_npl_clk_enabled == true) { + msm_sdw->sdw_npl_clk.enable = 0; + ret = afe_set_lpass_clock_v2( + AFE_PORT_ID_INT4_MI2S_RX, + &msm_sdw->sdw_npl_clk); + if (ret < 0) + dev_err(msm_sdw->dev, + "%s: failed to disable SDW NPL CLK\n", + __func__); + msm_sdw->sdw_npl_clk_enabled = false; + } + } + mutex_unlock(&msm_sdw->sdw_npl_clk_mutex); + return ret; +} + +static int msm_int_enable_sdw_cdc_clk(struct msm_sdw_priv *msm_sdw, + int enable, bool dapm) +{ + int ret = 0; + + mutex_lock(&msm_sdw->cdc_int_mclk1_mutex); + dev_dbg(msm_sdw->dev, "%s: enable %d mclk1 ref counter %d\n", + __func__, enable, msm_sdw->int_mclk1_rsc_ref); + if (enable) { + if (msm_sdw->int_mclk1_rsc_ref == 0) { + cancel_delayed_work_sync( + &msm_sdw->disable_int_mclk1_work); + if (msm_sdw->int_mclk1_enabled == false) { + msm_sdw->sdw_cdc_core_clk.enable = 1; + ret = afe_set_lpass_clock_v2( + AFE_PORT_ID_INT4_MI2S_RX, + &msm_sdw->sdw_cdc_core_clk); + if (ret < 0) { + dev_err(msm_sdw->dev, + "%s: failed to enable SDW MCLK\n", + __func__); + goto rtn; + } + dev_dbg(msm_sdw->dev, + "enabled sdw codec core mclk\n"); + msm_sdw->int_mclk1_enabled = true; + } + } + msm_sdw->int_mclk1_rsc_ref++; + } else { + cancel_delayed_work_sync(&msm_sdw->disable_int_mclk1_work); + if (msm_sdw->int_mclk1_rsc_ref > 0) { + msm_sdw->int_mclk1_rsc_ref--; + dev_dbg(msm_sdw->dev, + "%s: decrementing mclk_res_ref %d\n", + __func__, msm_sdw->int_mclk1_rsc_ref); + } + if (msm_sdw->int_mclk1_enabled == true && + msm_sdw->int_mclk1_rsc_ref == 0) { + msm_sdw->sdw_cdc_core_clk.enable = 0; + ret = afe_set_lpass_clock_v2( + AFE_PORT_ID_INT4_MI2S_RX, + &msm_sdw->sdw_cdc_core_clk); + if (ret < 0) + dev_err(msm_sdw->dev, + "%s: failed to disable SDW MCLK\n", + __func__); + msm_sdw->int_mclk1_enabled = false; + } + } +rtn: + mutex_unlock(&msm_sdw->cdc_int_mclk1_mutex); + return ret; +} +EXPORT_SYMBOL(msm_int_enable_sdw_cdc_clk); + +static void msm_disable_int_mclk1(struct work_struct *work) +{ + struct msm_sdw_priv *msm_sdw = NULL; + struct delayed_work *dwork; + int ret = 0; + + dwork = to_delayed_work(work); + msm_sdw = container_of(dwork, struct msm_sdw_priv, + disable_int_mclk1_work); + mutex_lock(&msm_sdw->cdc_int_mclk1_mutex); + dev_dbg(msm_sdw->dev, "%s: mclk1_enabled %d mclk1_rsc_ref %d\n", + __func__, msm_sdw->int_mclk1_enabled, + msm_sdw->int_mclk1_rsc_ref); + if (msm_sdw->int_mclk1_enabled == true + && msm_sdw->int_mclk1_rsc_ref == 0) { + dev_dbg(msm_sdw->dev, "Disable the mclk1\n"); + msm_sdw->sdw_cdc_core_clk.enable = 0; + ret = afe_set_lpass_clock_v2( + AFE_PORT_ID_INT4_MI2S_RX, + &msm_sdw->sdw_cdc_core_clk); + if (ret < 0) + dev_err(msm_sdw->dev, + "%s failed to disable the MCLK1\n", + __func__); + msm_sdw->int_mclk1_enabled = false; + } + mutex_unlock(&msm_sdw->cdc_int_mclk1_mutex); +} + +static int msm_int_mclk1_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct msm_sdw_priv *msm_sdw = snd_soc_codec_get_drvdata(codec); + int ret = 0; + + dev_dbg(msm_sdw->dev, "%s: event = %d\n", __func__, event); + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* enable the codec mclk config */ + msm_int_enable_sdw_cdc_clk(msm_sdw, 1, true); + msm_sdw_mclk_enable(msm_sdw, 1, true); + break; + case SND_SOC_DAPM_POST_PMD: + /* disable the codec mclk config */ + msm_sdw_mclk_enable(msm_sdw, 0, true); + msm_int_enable_sdw_cdc_clk(msm_sdw, 0, true); + break; + default: + dev_err(msm_sdw->dev, + "%s: invalid DAPM event %d\n", __func__, event); + ret = -EINVAL; + } + return ret; +} + +static int msm_sdw_ahb_write_device(struct msm_sdw_priv *msm_sdw, + u16 reg, u8 *value) +{ + u32 temp = (u32)(*value) & 0x000000FF; + + if (!msm_sdw->dev_up) { + dev_err_ratelimited(msm_sdw->dev, "%s: q6 not ready\n", + __func__); + return 0; + } + + iowrite32(temp, msm_sdw->sdw_base + reg); + return 0; +} + +static int msm_sdw_ahb_read_device(struct msm_sdw_priv *msm_sdw, + u16 reg, u8 *value) +{ + u32 temp; + + if (!msm_sdw->dev_up) { + dev_err_ratelimited(msm_sdw->dev, "%s: q6 not ready\n", + __func__); + return 0; + } + + temp = ioread32(msm_sdw->sdw_base + reg); + *value = (u8)temp; + return 0; +} + +static int __msm_sdw_reg_read(struct msm_sdw_priv *msm_sdw, unsigned short reg, + int bytes, void *dest) +{ + int ret = -EINVAL, i; + u8 temp = 0; + + dev_dbg(msm_sdw->dev, "%s reg = %x\n", __func__, reg); + mutex_lock(&msm_sdw->cdc_int_mclk1_mutex); + if (msm_sdw->int_mclk1_enabled == false) { + msm_sdw->sdw_cdc_core_clk.enable = 1; + ret = afe_set_lpass_clock_v2( + AFE_PORT_ID_INT4_MI2S_RX, + &msm_sdw->sdw_cdc_core_clk); + if (ret < 0) { + dev_err(msm_sdw->dev, + "%s:failed to enable the INT_MCLK1\n", + __func__); + goto unlock_exit; + } + dev_dbg(msm_sdw->dev, "%s:enabled sdw codec core clk\n", + __func__); + for (i = 0; i < bytes; i++) { + ret = msm_sdw_ahb_read_device( + msm_sdw, reg + (4 * i), &temp); + ((u8 *)dest)[i] = temp; + } + msm_sdw->int_mclk1_enabled = true; + schedule_delayed_work(&msm_sdw->disable_int_mclk1_work, 50); + goto unlock_exit; + } + for (i = 0; i < bytes; i++) { + ret = msm_sdw_ahb_read_device( + msm_sdw, reg + (4 * i), &temp); + ((u8 *)dest)[i] = temp; + } +unlock_exit: + mutex_unlock(&msm_sdw->cdc_int_mclk1_mutex); + if (ret < 0) { + dev_err_ratelimited(msm_sdw->dev, + "%s: codec read failed for reg 0x%x\n", + __func__, reg); + return ret; + } + dev_dbg(msm_sdw->dev, "Read 0x%02x from 0x%x\n", temp, reg); + + return 0; +} + +static int __msm_sdw_reg_write(struct msm_sdw_priv *msm_sdw, unsigned short reg, + int bytes, void *src) +{ + int ret = -EINVAL, i; + + mutex_lock(&msm_sdw->cdc_int_mclk1_mutex); + if (msm_sdw->int_mclk1_enabled == false) { + msm_sdw->sdw_cdc_core_clk.enable = 1; + ret = afe_set_lpass_clock_v2(AFE_PORT_ID_INT4_MI2S_RX, + &msm_sdw->sdw_cdc_core_clk); + if (ret < 0) { + dev_err(msm_sdw->dev, + "%s: failed to enable the INT_MCLK1\n", + __func__); + ret = 0; + goto unlock_exit; + } + dev_dbg(msm_sdw->dev, "%s: enabled INT_MCLK1\n", __func__); + for (i = 0; i < bytes; i++) + ret = msm_sdw_ahb_write_device(msm_sdw, reg + (4 * i), + &((u8 *)src)[i]); + msm_sdw->int_mclk1_enabled = true; + schedule_delayed_work(&msm_sdw->disable_int_mclk1_work, 50); + goto unlock_exit; + } + for (i = 0; i < bytes; i++) + ret = msm_sdw_ahb_write_device(msm_sdw, reg + (4 * i), + &((u8 *)src)[i]); +unlock_exit: + mutex_unlock(&msm_sdw->cdc_int_mclk1_mutex); + dev_dbg(msm_sdw->dev, "Write 0x%x val 0x%02x\n", + reg, (u32)(*(u32 *)src)); + + return ret; +} + +static int msm_sdw_codec_enable_vi_feedback(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = NULL; + struct msm_sdw_priv *msm_sdw_p = NULL; + int ret = 0; + + if (!w) { + pr_err("%s invalid params\n", __func__); + return -EINVAL; + } + codec = snd_soc_dapm_to_codec(w->dapm); + msm_sdw_p = snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, "%s: num_dai %d stream name %s\n", + __func__, codec->component.num_dai, w->sname); + + dev_dbg(codec->dev, "%s(): w->name %s event %d w->shift %d\n", + __func__, w->name, event, w->shift); + if (w->shift != AIF1_SDW_VIFEED) { + dev_err(codec->dev, + "%s:Error in enabling the vi feedback path\n", + __func__); + ret = -EINVAL; + goto out_vi; + } + switch (event) { + case SND_SOC_DAPM_POST_PMU: + if (test_bit(VI_SENSE_1, &msm_sdw_p->status_mask)) { + dev_dbg(codec->dev, "%s: spkr1 enabled\n", __func__); + /* Enable V&I sensing */ + snd_soc_update_bits(codec, + MSM_SDW_TX9_SPKR_PROT_PATH_CTL, 0x20, 0x20); + snd_soc_update_bits(codec, + MSM_SDW_TX10_SPKR_PROT_PATH_CTL, 0x20, + 0x20); + snd_soc_update_bits(codec, + MSM_SDW_TX9_SPKR_PROT_PATH_CTL, 0x0F, 0x04); + snd_soc_update_bits(codec, + MSM_SDW_TX10_SPKR_PROT_PATH_CTL, 0x0F, 0x04); + snd_soc_update_bits(codec, + MSM_SDW_TX9_SPKR_PROT_PATH_CTL, 0x10, 0x10); + snd_soc_update_bits(codec, + MSM_SDW_TX10_SPKR_PROT_PATH_CTL, 0x10, + 0x10); + snd_soc_update_bits(codec, + MSM_SDW_TX9_SPKR_PROT_PATH_CTL, 0x20, 0x00); + snd_soc_update_bits(codec, + MSM_SDW_TX10_SPKR_PROT_PATH_CTL, 0x20, + 0x00); + } + if (test_bit(VI_SENSE_2, &msm_sdw_p->status_mask)) { + dev_dbg(codec->dev, "%s: spkr2 enabled\n", __func__); + /* Enable V&I sensing */ + snd_soc_update_bits(codec, + MSM_SDW_TX11_SPKR_PROT_PATH_CTL, 0x20, + 0x20); + snd_soc_update_bits(codec, + MSM_SDW_TX12_SPKR_PROT_PATH_CTL, 0x20, + 0x20); + snd_soc_update_bits(codec, + MSM_SDW_TX11_SPKR_PROT_PATH_CTL, 0x0F, + 0x04); + snd_soc_update_bits(codec, + MSM_SDW_TX12_SPKR_PROT_PATH_CTL, 0x0F, + 0x04); + snd_soc_update_bits(codec, + MSM_SDW_TX11_SPKR_PROT_PATH_CTL, 0x10, + 0x10); + snd_soc_update_bits(codec, + MSM_SDW_TX12_SPKR_PROT_PATH_CTL, 0x10, + 0x10); + snd_soc_update_bits(codec, + MSM_SDW_TX11_SPKR_PROT_PATH_CTL, 0x20, + 0x00); + snd_soc_update_bits(codec, + MSM_SDW_TX12_SPKR_PROT_PATH_CTL, 0x20, + 0x00); + } + break; + case SND_SOC_DAPM_POST_PMD: + if (test_bit(VI_SENSE_1, &msm_sdw_p->status_mask)) { + /* Disable V&I sensing */ + dev_dbg(codec->dev, "%s: spkr1 disabled\n", __func__); + snd_soc_update_bits(codec, + MSM_SDW_TX9_SPKR_PROT_PATH_CTL, 0x20, 0x20); + snd_soc_update_bits(codec, + MSM_SDW_TX10_SPKR_PROT_PATH_CTL, 0x20, + 0x20); + snd_soc_update_bits(codec, + MSM_SDW_TX9_SPKR_PROT_PATH_CTL, 0x10, 0x00); + snd_soc_update_bits(codec, + MSM_SDW_TX10_SPKR_PROT_PATH_CTL, 0x10, + 0x00); + } + if (test_bit(VI_SENSE_2, &msm_sdw_p->status_mask)) { + /* Disable V&I sensing */ + dev_dbg(codec->dev, "%s: spkr2 disabled\n", __func__); + snd_soc_update_bits(codec, + MSM_SDW_TX11_SPKR_PROT_PATH_CTL, 0x20, + 0x20); + snd_soc_update_bits(codec, + MSM_SDW_TX12_SPKR_PROT_PATH_CTL, 0x20, + 0x20); + snd_soc_update_bits(codec, + MSM_SDW_TX11_SPKR_PROT_PATH_CTL, 0x10, + 0x00); + snd_soc_update_bits(codec, + MSM_SDW_TX12_SPKR_PROT_PATH_CTL, 0x10, + 0x00); + } + break; + } +out_vi: + return ret; +} + +static int msm_sdwm_handle_irq(void *handle, + irqreturn_t (*swrm_irq_handler)(int irq, + void *data), + void *swrm_handle, + int action) +{ + struct msm_sdw_priv *msm_sdw; + int ret = 0; + + if (!handle) { + pr_err("%s: null handle received\n", __func__); + return -EINVAL; + } + msm_sdw = (struct msm_sdw_priv *) handle; + + if (skip_irq) + return ret; + + if (action) { + ret = request_threaded_irq(msm_sdw->sdw_irq, NULL, + swrm_irq_handler, + IRQF_TRIGGER_HIGH | IRQF_ONESHOT, + "swr_master_irq", swrm_handle); + if (ret) + dev_err(msm_sdw->dev, "%s: Failed to request irq %d\n", + __func__, ret); + } else + free_irq(msm_sdw->sdw_irq, swrm_handle); + + return ret; +} + +static void msm_sdw_codec_hd2_control(struct snd_soc_codec *codec, + u16 reg, int event) +{ + u16 hd2_scale_reg; + u16 hd2_enable_reg = 0; + + if (reg == MSM_SDW_RX7_RX_PATH_CTL) { + hd2_scale_reg = MSM_SDW_RX7_RX_PATH_SEC3; + hd2_enable_reg = MSM_SDW_RX7_RX_PATH_CFG0; + } + if (reg == MSM_SDW_RX8_RX_PATH_CTL) { + hd2_scale_reg = MSM_SDW_RX8_RX_PATH_SEC3; + hd2_enable_reg = MSM_SDW_RX8_RX_PATH_CFG0; + } + + if (hd2_enable_reg && SND_SOC_DAPM_EVENT_ON(event)) { + snd_soc_update_bits(codec, hd2_scale_reg, 0x3C, 0x10); + snd_soc_update_bits(codec, hd2_scale_reg, 0x03, 0x01); + snd_soc_update_bits(codec, hd2_enable_reg, 0x04, 0x04); + } + + if (hd2_enable_reg && SND_SOC_DAPM_EVENT_OFF(event)) { + snd_soc_update_bits(codec, hd2_enable_reg, 0x04, 0x00); + snd_soc_update_bits(codec, hd2_scale_reg, 0x03, 0x00); + snd_soc_update_bits(codec, hd2_scale_reg, 0x3C, 0x00); + } +} + +static int msm_sdw_enable_swr(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct msm_sdw_priv *msm_sdw; + int i, ch_cnt; + + msm_sdw = snd_soc_codec_get_drvdata(codec); + + if (!msm_sdw->nr) + return 0; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (!(strnstr(w->name, "RX4", sizeof("RX4 MIX"))) && + !msm_sdw->rx_4_count) + msm_sdw->rx_4_count++; + if (!(strnstr(w->name, "RX5", sizeof("RX5 MIX"))) && + !msm_sdw->rx_5_count) + msm_sdw->rx_5_count++; + ch_cnt = msm_sdw->rx_4_count + msm_sdw->rx_5_count; + + for (i = 0; i < msm_sdw->nr; i++) { + swrm_wcd_notify(msm_sdw->sdw_ctrl_data[i].sdw_pdev, + SWR_DEVICE_UP, NULL); + swrm_wcd_notify(msm_sdw->sdw_ctrl_data[i].sdw_pdev, + SWR_SET_NUM_RX_CH, &ch_cnt); + } + break; + case SND_SOC_DAPM_POST_PMD: + if (!(strnstr(w->name, "RX4", sizeof("RX4 MIX"))) && + msm_sdw->rx_4_count) + msm_sdw->rx_4_count--; + if (!(strnstr(w->name, "RX5", sizeof("RX5 MIX"))) && + msm_sdw->rx_5_count) + msm_sdw->rx_5_count--; + ch_cnt = msm_sdw->rx_4_count + msm_sdw->rx_5_count; + + for (i = 0; i < msm_sdw->nr; i++) + swrm_wcd_notify(msm_sdw->sdw_ctrl_data[i].sdw_pdev, + SWR_SET_NUM_RX_CH, &ch_cnt); + break; + } + dev_dbg(msm_sdw->dev, "%s: current swr ch cnt: %d\n", + __func__, msm_sdw->rx_4_count + msm_sdw->rx_5_count); + + return 0; +} + +static int msm_sdw_codec_enable_interpolator(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct msm_sdw_priv *msm_sdw = snd_soc_codec_get_drvdata(codec); + u16 gain_reg; + u16 reg; + int val; + int offset_val = 0; + + dev_dbg(codec->dev, "%s %d %s\n", __func__, event, w->name); + + if (!(strcmp(w->name, "RX INT4 INTERP"))) { + reg = MSM_SDW_RX7_RX_PATH_CTL; + gain_reg = MSM_SDW_RX7_RX_VOL_CTL; + } else if (!(strcmp(w->name, "RX INT5 INTERP"))) { + reg = MSM_SDW_RX8_RX_PATH_CTL; + gain_reg = MSM_SDW_RX8_RX_VOL_CTL; + } else { + dev_err(codec->dev, "%s: Interpolator reg not found\n", + __func__); + return -EINVAL; + } + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_update_bits(codec, reg, 0x10, 0x10); + msm_sdw_codec_hd2_control(codec, reg, event); + snd_soc_update_bits(codec, reg, 1 << 0x5, 1 << 0x5); + break; + case SND_SOC_DAPM_POST_PMU: + msm_sdw_config_compander(codec, w->shift, event); + /* apply gain after int clk is enabled */ + if ((msm_sdw->spkr_gain_offset == RX_GAIN_OFFSET_M1P5_DB) && + (msm_sdw->comp_enabled[COMP1] || + msm_sdw->comp_enabled[COMP2]) && + (gain_reg == MSM_SDW_RX7_RX_VOL_CTL || + gain_reg == MSM_SDW_RX8_RX_VOL_CTL)) { + snd_soc_update_bits(codec, MSM_SDW_RX7_RX_PATH_SEC1, + 0x01, 0x01); + snd_soc_update_bits(codec, + MSM_SDW_RX7_RX_PATH_MIX_SEC0, + 0x01, 0x01); + snd_soc_update_bits(codec, MSM_SDW_RX8_RX_PATH_SEC1, + 0x01, 0x01); + snd_soc_update_bits(codec, + MSM_SDW_RX8_RX_PATH_MIX_SEC0, + 0x01, 0x01); + offset_val = -2; + } + val = snd_soc_read(codec, gain_reg); + val += offset_val; + snd_soc_write(codec, gain_reg, val); + msm_sdw_config_ear_spkr_gain(codec, event, gain_reg); + snd_soc_update_bits(codec, reg, 0x10, 0x00); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_update_bits(codec, reg, 1 << 0x5, 0 << 0x5); + snd_soc_update_bits(codec, reg, 0x40, 0x40); + snd_soc_update_bits(codec, reg, 0x40, 0x00); + msm_sdw_codec_hd2_control(codec, reg, event); + msm_sdw_config_compander(codec, w->shift, event); + if ((msm_sdw->spkr_gain_offset == RX_GAIN_OFFSET_M1P5_DB) && + (msm_sdw->comp_enabled[COMP1] || + msm_sdw->comp_enabled[COMP2]) && + (gain_reg == MSM_SDW_RX7_RX_VOL_CTL || + gain_reg == MSM_SDW_RX8_RX_VOL_CTL)) { + snd_soc_update_bits(codec, MSM_SDW_RX7_RX_PATH_SEC1, + 0x01, 0x00); + snd_soc_update_bits(codec, + MSM_SDW_RX7_RX_PATH_MIX_SEC0, + 0x01, 0x00); + snd_soc_update_bits(codec, MSM_SDW_RX8_RX_PATH_SEC1, + 0x01, 0x00); + snd_soc_update_bits(codec, + MSM_SDW_RX8_RX_PATH_MIX_SEC0, + 0x01, 0x00); + offset_val = 2; + val = snd_soc_read(codec, gain_reg); + val += offset_val; + snd_soc_write(codec, gain_reg, val); + } + msm_sdw_config_ear_spkr_gain(codec, event, gain_reg); + break; + }; + + return 0; +} + +static int msm_sdw_config_ear_spkr_gain(struct snd_soc_codec *codec, + int event, int gain_reg) +{ + int comp_gain_offset, val; + struct msm_sdw_priv *msm_sdw = snd_soc_codec_get_drvdata(codec); + + switch (msm_sdw->spkr_mode) { + /* Compander gain in SPKR_MODE1 case is 12 dB */ + case SPKR_MODE_1: + comp_gain_offset = -12; + break; + /* Default case compander gain is 15 dB */ + default: + comp_gain_offset = -15; + break; + } + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + /* Apply ear spkr gain only if compander is enabled */ + if (msm_sdw->comp_enabled[COMP1] && + (gain_reg == MSM_SDW_RX7_RX_VOL_CTL) && + (msm_sdw->ear_spkr_gain != 0)) { + /* For example, val is -8(-12+5-1) for 4dB of gain */ + val = comp_gain_offset + msm_sdw->ear_spkr_gain - 1; + snd_soc_write(codec, gain_reg, val); + + dev_dbg(codec->dev, "%s: RX4 Volume %d dB\n", + __func__, val); + } + break; + case SND_SOC_DAPM_POST_PMD: + /* + * Reset RX4 volume to 0 dB if compander is enabled and + * ear_spkr_gain is non-zero. + */ + if (msm_sdw->comp_enabled[COMP1] && + (gain_reg == MSM_SDW_RX7_RX_VOL_CTL) && + (msm_sdw->ear_spkr_gain != 0)) { + snd_soc_write(codec, gain_reg, 0x0); + + dev_dbg(codec->dev, "%s: Reset RX4 Volume to 0 dB\n", + __func__); + } + break; + } + + return 0; +} + +static int msm_sdw_codec_spk_boost_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + u16 boost_path_ctl, boost_path_cfg1; + u16 reg; + + dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event); + + if (!strcmp(w->name, "RX INT4 CHAIN")) { + boost_path_ctl = MSM_SDW_BOOST0_BOOST_PATH_CTL; + boost_path_cfg1 = MSM_SDW_RX7_RX_PATH_CFG1; + reg = MSM_SDW_RX7_RX_PATH_CTL; + } else if (!strcmp(w->name, "RX INT5 CHAIN")) { + boost_path_ctl = MSM_SDW_BOOST1_BOOST_PATH_CTL; + boost_path_cfg1 = MSM_SDW_RX8_RX_PATH_CFG1; + reg = MSM_SDW_RX8_RX_PATH_CTL; + } else { + dev_err(codec->dev, "%s: boost reg not found\n", + __func__); + return -EINVAL; + } + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_update_bits(codec, boost_path_ctl, 0x10, 0x10); + snd_soc_update_bits(codec, boost_path_cfg1, 0x01, 0x01); + snd_soc_update_bits(codec, reg, 0x10, 0x00); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_update_bits(codec, boost_path_cfg1, 0x01, 0x00); + snd_soc_update_bits(codec, boost_path_ctl, 0x10, 0x00); + break; + }; + + return 0; +} + +static int msm_sdw_config_compander(struct snd_soc_codec *codec, int comp, + int event) +{ + struct msm_sdw_priv *msm_sdw = snd_soc_codec_get_drvdata(codec); + u16 comp_ctl0_reg, rx_path_cfg0_reg; + + if (comp < COMP1 || comp >= COMP_MAX) + return 0; + + dev_dbg(codec->dev, "%s: event %d compander %d, enabled %d\n", + __func__, event, comp + 1, msm_sdw->comp_enabled[comp]); + + if (!msm_sdw->comp_enabled[comp]) + return 0; + + comp_ctl0_reg = MSM_SDW_COMPANDER7_CTL0 + (comp * 0x20); + rx_path_cfg0_reg = MSM_SDW_RX7_RX_PATH_CFG0 + (comp * 0x1E0); + + if (SND_SOC_DAPM_EVENT_ON(event)) { + /* Enable Compander Clock */ + snd_soc_update_bits(codec, comp_ctl0_reg, 0x01, 0x01); + snd_soc_update_bits(codec, comp_ctl0_reg, 0x02, 0x02); + snd_soc_update_bits(codec, comp_ctl0_reg, 0x02, 0x00); + snd_soc_update_bits(codec, rx_path_cfg0_reg, 0x02, 0x02); + } + + if (SND_SOC_DAPM_EVENT_OFF(event)) { + snd_soc_update_bits(codec, comp_ctl0_reg, 0x04, 0x04); + snd_soc_update_bits(codec, rx_path_cfg0_reg, 0x02, 0x00); + snd_soc_update_bits(codec, comp_ctl0_reg, 0x02, 0x02); + snd_soc_update_bits(codec, comp_ctl0_reg, 0x02, 0x00); + snd_soc_update_bits(codec, comp_ctl0_reg, 0x01, 0x00); + snd_soc_update_bits(codec, comp_ctl0_reg, 0x04, 0x00); + } + + return 0; +} + +static int msm_sdw_get_compander(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + int comp = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + struct msm_sdw_priv *msm_sdw = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = msm_sdw->comp_enabled[comp]; + return 0; +} + +static int msm_sdw_set_compander(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct msm_sdw_priv *msm_sdw = snd_soc_codec_get_drvdata(codec); + int comp = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + int value = ucontrol->value.integer.value[0]; + + dev_dbg(codec->dev, "%s: Compander %d enable current %d, new %d\n", + __func__, comp + 1, msm_sdw->comp_enabled[comp], value); + msm_sdw->comp_enabled[comp] = value; + + return 0; +} + +static int msm_sdw_ear_spkr_pa_gain_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct msm_sdw_priv *msm_sdw = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = msm_sdw->ear_spkr_gain; + + dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n", + __func__, ucontrol->value.integer.value[0]); + + return 0; +} + +static int msm_sdw_ear_spkr_pa_gain_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct msm_sdw_priv *msm_sdw = snd_soc_codec_get_drvdata(codec); + + msm_sdw->ear_spkr_gain = ucontrol->value.integer.value[0]; + + dev_dbg(codec->dev, "%s: gain = %d\n", __func__, + msm_sdw->ear_spkr_gain); + + return 0; +} + +static int msm_sdw_spkr_left_boost_stage_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u8 bst_state_max = 0; + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + + bst_state_max = snd_soc_read(codec, MSM_SDW_BOOST0_BOOST_CTL); + bst_state_max = (bst_state_max & 0x0c) >> 2; + ucontrol->value.integer.value[0] = bst_state_max; + dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n", + __func__, ucontrol->value.integer.value[0]); + + return 0; +} + +static int msm_sdw_spkr_left_boost_stage_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u8 bst_state_max; + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + + dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n", + __func__, ucontrol->value.integer.value[0]); + bst_state_max = ucontrol->value.integer.value[0] << 2; + snd_soc_update_bits(codec, MSM_SDW_BOOST0_BOOST_CTL, + 0x0c, bst_state_max); + + return 0; +} + +static int msm_sdw_spkr_right_boost_stage_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u8 bst_state_max = 0; + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + + bst_state_max = snd_soc_read(codec, MSM_SDW_BOOST1_BOOST_CTL); + bst_state_max = (bst_state_max & 0x0c) >> 2; + ucontrol->value.integer.value[0] = bst_state_max; + dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n", + __func__, ucontrol->value.integer.value[0]); + + return 0; +} + +static int msm_sdw_spkr_right_boost_stage_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u8 bst_state_max; + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + + dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n", + __func__, ucontrol->value.integer.value[0]); + bst_state_max = ucontrol->value.integer.value[0] << 2; + snd_soc_update_bits(codec, MSM_SDW_BOOST1_BOOST_CTL, + 0x0c, bst_state_max); + + return 0; +} + +static int msm_sdw_vi_feed_mixer_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm); + struct msm_sdw_priv *msm_sdw_p = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = msm_sdw_p->vi_feed_value; + + return 0; +} + +static int msm_sdw_vi_feed_mixer_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm); + struct msm_sdw_priv *msm_sdw_p = snd_soc_codec_get_drvdata(codec); + struct soc_multi_mixer_control *mixer = + ((struct soc_multi_mixer_control *)kcontrol->private_value); + u32 dai_id = widget->shift; + u32 port_id = mixer->shift; + u32 enable = ucontrol->value.integer.value[0]; + + dev_dbg(codec->dev, "%s: enable: %d, port_id:%d, dai_id: %d\n", + __func__, enable, port_id, dai_id); + + msm_sdw_p->vi_feed_value = ucontrol->value.integer.value[0]; + + mutex_lock(&msm_sdw_p->codec_mutex); + if (enable) { + if (port_id == MSM_SDW_TX0 && !test_bit(VI_SENSE_1, + &msm_sdw_p->status_mask)) + set_bit(VI_SENSE_1, &msm_sdw_p->status_mask); + if (port_id == MSM_SDW_TX1 && !test_bit(VI_SENSE_2, + &msm_sdw_p->status_mask)) + set_bit(VI_SENSE_2, &msm_sdw_p->status_mask); + } else { + if (port_id == MSM_SDW_TX0 && test_bit(VI_SENSE_1, + &msm_sdw_p->status_mask)) + clear_bit(VI_SENSE_1, &msm_sdw_p->status_mask); + if (port_id == MSM_SDW_TX1 && test_bit(VI_SENSE_2, + &msm_sdw_p->status_mask)) + clear_bit(VI_SENSE_2, &msm_sdw_p->status_mask); + } + mutex_unlock(&msm_sdw_p->codec_mutex); + snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, enable, NULL); + + return 0; +} + +static int msm_sdw_mclk_enable(struct msm_sdw_priv *msm_sdw, + int mclk_enable, bool dapm) +{ + dev_dbg(msm_sdw->dev, "%s: mclk_enable = %u, dapm = %d clk_users= %d\n", + __func__, mclk_enable, dapm, msm_sdw->sdw_mclk_users); + if (mclk_enable) { + msm_sdw->sdw_mclk_users++; + if (msm_sdw->sdw_mclk_users == 1) { + regmap_update_bits(msm_sdw->regmap, + MSM_SDW_CLK_RST_CTRL_FS_CNT_CONTROL, + 0x01, 0x01); + regmap_update_bits(msm_sdw->regmap, + MSM_SDW_CLK_RST_CTRL_MCLK_CONTROL, + 0x01, 0x01); + /* 9.6MHz MCLK, set value 0x00 if other frequency */ + regmap_update_bits(msm_sdw->regmap, + MSM_SDW_TOP_FREQ_MCLK, 0x01, 0x01); + } + } else { + msm_sdw->sdw_mclk_users--; + if (msm_sdw->sdw_mclk_users == 0) { + regmap_update_bits(msm_sdw->regmap, + MSM_SDW_CLK_RST_CTRL_FS_CNT_CONTROL, + 0x01, 0x00); + regmap_update_bits(msm_sdw->regmap, + MSM_SDW_CLK_RST_CTRL_MCLK_CONTROL, + 0x01, 0x00); + } + } + return 0; +} +EXPORT_SYMBOL(msm_sdw_mclk_enable); + +static int msm_sdw_swrm_read(void *handle, int reg) +{ + struct msm_sdw_priv *msm_sdw; + unsigned short sdw_rd_addr_base; + unsigned short sdw_rd_data_base; + int val, ret; + + if (!handle) { + pr_err("%s: NULL handle\n", __func__); + return -EINVAL; + } + msm_sdw = (struct msm_sdw_priv *)handle; + + dev_dbg(msm_sdw->dev, "%s: Reading soundwire register, 0x%x\n", + __func__, reg); + sdw_rd_addr_base = MSM_SDW_AHB_BRIDGE_RD_ADDR_0; + sdw_rd_data_base = MSM_SDW_AHB_BRIDGE_RD_DATA_0; + /* + * Add sleep as SWR slave access read takes time. + * Allow for RD_DONE to complete for previous register if any. + */ + usleep_range(100, 105); + + /* read_lock */ + mutex_lock(&msm_sdw->sdw_read_lock); + ret = regmap_bulk_write(msm_sdw->regmap, sdw_rd_addr_base, + (u8 *)®, 4); + if (ret < 0) { + dev_err(msm_sdw->dev, "%s: RD Addr Failure\n", __func__); + goto err; + } + /* Add sleep for SWR register read value to get updated. */ + usleep_range(100, 105); + /* Check for RD value */ + ret = regmap_bulk_read(msm_sdw->regmap, sdw_rd_data_base, + (u8 *)&val, 4); + if (ret < 0) { + dev_err(msm_sdw->dev, "%s: RD Data Failure\n", __func__); + goto err; + } + ret = val; +err: + /* read_unlock */ + mutex_unlock(&msm_sdw->sdw_read_lock); + return ret; +} + +static int msm_sdw_bulk_write(struct msm_sdw_priv *msm_sdw, + struct msm_sdw_reg_val *bulk_reg, + size_t len) +{ + int i, ret = 0; + unsigned short sdw_wr_addr_base; + unsigned short sdw_wr_data_base; + + sdw_wr_addr_base = MSM_SDW_AHB_BRIDGE_WR_ADDR_0; + sdw_wr_data_base = MSM_SDW_AHB_BRIDGE_WR_DATA_0; + + for (i = 0; i < len; i += 2) { + /* + * Add sleep as SWR slave write takes time. + * Allow for any previous pending write to complete. + */ + usleep_range(100, 105); + /* First Write the Data to register */ + ret = regmap_bulk_write(msm_sdw->regmap, + sdw_wr_data_base, bulk_reg[i].buf, 4); + if (ret < 0) { + dev_err(msm_sdw->dev, "%s: WR Data Failure\n", + __func__); + break; + } + /* Next Write Address */ + ret = regmap_bulk_write(msm_sdw->regmap, + sdw_wr_addr_base, bulk_reg[i+1].buf, 4); + if (ret < 0) { + dev_err(msm_sdw->dev, + "%s: WR Addr Failure: 0x%x\n", + __func__, (u32)(bulk_reg[i+1].buf[0])); + break; + } + } + return ret; +} + +static int msm_sdw_swrm_bulk_write(void *handle, u32 *reg, u32 *val, size_t len) +{ + struct msm_sdw_priv *msm_sdw; + struct msm_sdw_reg_val *bulk_reg; + unsigned short sdw_wr_addr_base; + unsigned short sdw_wr_data_base; + int i, j, ret; + + if (!handle) { + pr_err("%s: NULL handle\n", __func__); + return -EINVAL; + } + + msm_sdw = (struct msm_sdw_priv *)handle; + if (len <= 0) { + dev_err(msm_sdw->dev, + "%s: Invalid size: %zu\n", __func__, len); + return -EINVAL; + } + + sdw_wr_addr_base = MSM_SDW_AHB_BRIDGE_WR_ADDR_0; + sdw_wr_data_base = MSM_SDW_AHB_BRIDGE_WR_DATA_0; + + bulk_reg = kzalloc((2 * len * sizeof(struct msm_sdw_reg_val)), + GFP_KERNEL); + if (!bulk_reg) + return -ENOMEM; + + for (i = 0, j = 0; i < (len * 2); i += 2, j++) { + bulk_reg[i].reg = sdw_wr_data_base; + bulk_reg[i].buf = (u8 *)(&val[j]); + bulk_reg[i].bytes = 4; + bulk_reg[i+1].reg = sdw_wr_addr_base; + bulk_reg[i+1].buf = (u8 *)(®[j]); + bulk_reg[i+1].bytes = 4; + } + mutex_lock(&msm_sdw->sdw_write_lock); + + ret = msm_sdw_bulk_write(msm_sdw, bulk_reg, (len * 2)); + if (ret) + dev_err(msm_sdw->dev, "%s: swrm bulk write failed, ret: %d\n", + __func__, ret); + + mutex_unlock(&msm_sdw->sdw_write_lock); + kfree(bulk_reg); + + return ret; +} + +static int msm_sdw_swrm_write(void *handle, int reg, int val) +{ + struct msm_sdw_priv *msm_sdw; + unsigned short sdw_wr_addr_base; + unsigned short sdw_wr_data_base; + struct msm_sdw_reg_val bulk_reg[2]; + int ret; + + if (!handle) { + pr_err("%s: NULL handle\n", __func__); + return -EINVAL; + } + msm_sdw = (struct msm_sdw_priv *)handle; + + sdw_wr_addr_base = MSM_SDW_AHB_BRIDGE_WR_ADDR_0; + sdw_wr_data_base = MSM_SDW_AHB_BRIDGE_WR_DATA_0; + + /* First Write the Data to register */ + bulk_reg[0].reg = sdw_wr_data_base; + bulk_reg[0].buf = (u8 *)(&val); + bulk_reg[0].bytes = 4; + bulk_reg[1].reg = sdw_wr_addr_base; + bulk_reg[1].buf = (u8 *)(®); + bulk_reg[1].bytes = 4; + + mutex_lock(&msm_sdw->sdw_write_lock); + + ret = msm_sdw_bulk_write(msm_sdw, bulk_reg, 2); + if (ret < 0) + dev_err(msm_sdw->dev, "%s: WR Data Failure\n", __func__); + + mutex_unlock(&msm_sdw->sdw_write_lock); + return ret; +} + +static int msm_sdw_swrm_clock(void *handle, bool enable) +{ + struct msm_sdw_priv *msm_sdw; + + if (!handle) { + pr_err("%s: NULL handle\n", __func__); + return -EINVAL; + } + msm_sdw = (struct msm_sdw_priv *)handle; + + mutex_lock(&msm_sdw->sdw_clk_lock); + + dev_dbg(msm_sdw->dev, "%s: swrm clock %s\n", + __func__, (enable ? "enable" : "disable")); + if (enable) { + msm_sdw->sdw_clk_users++; + if (msm_sdw->sdw_clk_users == 1) { + msm_int_enable_sdw_cdc_clk(msm_sdw, 1, true); + msm_sdw_mclk_enable(msm_sdw, 1, true); + regmap_update_bits(msm_sdw->regmap, + MSM_SDW_CLK_RST_CTRL_SWR_CONTROL, 0x01, 0x01); + msm_enable_sdw_npl_clk(msm_sdw, true); + msm_cdc_pinctrl_select_active_state( + msm_sdw->sdw_gpio_p); + } + } else { + msm_sdw->sdw_clk_users--; + if (msm_sdw->sdw_clk_users == 0) { + regmap_update_bits(msm_sdw->regmap, + MSM_SDW_CLK_RST_CTRL_SWR_CONTROL, + 0x01, 0x00); + msm_sdw_mclk_enable(msm_sdw, 0, true); + msm_int_enable_sdw_cdc_clk(msm_sdw, 0, true); + msm_enable_sdw_npl_clk(msm_sdw, false); + msm_cdc_pinctrl_select_sleep_state(msm_sdw->sdw_gpio_p); + } + } + dev_dbg(msm_sdw->dev, "%s: swrm clock users %d\n", + __func__, msm_sdw->sdw_clk_users); + mutex_unlock(&msm_sdw->sdw_clk_lock); + return 0; +} + +static int msm_sdw_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + dev_dbg(dai->codec->dev, "%s(): substream = %s stream = %d\n", + __func__, + substream->name, substream->stream); + return 0; +} + +static int msm_sdw_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + u8 clk_fs_rate, fs_rate; + + dev_dbg(dai->codec->dev, + "%s: dai_name = %s DAI-ID %x rate %d num_ch %d format %d\n", + __func__, dai->name, dai->id, params_rate(params), + params_channels(params), params_format(params)); + + switch (params_rate(params)) { + case 8000: + clk_fs_rate = 0x00; + fs_rate = 0x00; + break; + case 16000: + clk_fs_rate = 0x01; + fs_rate = 0x01; + break; + case 32000: + clk_fs_rate = 0x02; + fs_rate = 0x03; + break; + case 48000: + clk_fs_rate = 0x03; + fs_rate = 0x04; + break; + case 96000: + clk_fs_rate = 0x04; + fs_rate = 0x05; + break; + case 192000: + clk_fs_rate = 0x05; + fs_rate = 0x06; + break; + default: + dev_err(dai->codec->dev, + "%s: Invalid sampling rate %d\n", __func__, + params_rate(params)); + return -EINVAL; + } + + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + snd_soc_update_bits(dai->codec, + MSM_SDW_TOP_TX_I2S_CTL, 0x1C, + (clk_fs_rate << 2)); + } else { + snd_soc_update_bits(dai->codec, + MSM_SDW_TOP_RX_I2S_CTL, 0x1C, + (clk_fs_rate << 2)); + snd_soc_update_bits(dai->codec, + MSM_SDW_RX7_RX_PATH_CTL, 0x0F, + fs_rate); + snd_soc_update_bits(dai->codec, + MSM_SDW_RX8_RX_PATH_CTL, 0x0F, + fs_rate); + } + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + snd_soc_update_bits(dai->codec, + MSM_SDW_TOP_TX_I2S_CTL, 0x20, 0x20); + else + snd_soc_update_bits(dai->codec, + MSM_SDW_TOP_RX_I2S_CTL, 0x20, 0x20); + break; + case SNDRV_PCM_FORMAT_S24_LE: + case SNDRV_PCM_FORMAT_S24_3LE: + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + snd_soc_update_bits(dai->codec, + MSM_SDW_TOP_TX_I2S_CTL, 0x20, 0x00); + else + snd_soc_update_bits(dai->codec, + MSM_SDW_TOP_RX_I2S_CTL, 0x20, 0x00); + break; + default: + dev_err(dai->codec->dev, "%s: wrong format selected\n", + __func__); + return -EINVAL; + } + + return 0; +} + +static void msm_sdw_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + dev_dbg(dai->codec->dev, + "%s(): substream = %s stream = %d\n", __func__, + substream->name, substream->stream); +} + +static ssize_t msm_sdw_codec_version_read(struct snd_info_entry *entry, + void *file_private_data, + struct file *file, + char __user *buf, size_t count, + loff_t pos) +{ + struct msm_sdw_priv *msm_sdw; + char buffer[MSM_SDW_VERSION_ENTRY_SIZE]; + int len = 0; + + msm_sdw = (struct msm_sdw_priv *) entry->private_data; + if (!msm_sdw) { + pr_err("%s: msm_sdw priv is null\n", __func__); + return -EINVAL; + } + + switch (msm_sdw->version) { + case MSM_SDW_VERSION_1_0: + len = snprintf(buffer, sizeof(buffer), "SDW-CDC_1_0\n"); + break; + default: + len = snprintf(buffer, sizeof(buffer), "VER_UNDEFINED\n"); + } + + return simple_read_from_buffer(buf, count, &pos, buffer, len); +} + +static struct snd_info_entry_ops msm_sdw_codec_info_ops = { + .read = msm_sdw_codec_version_read, +}; + +/* + * msm_sdw_codec_info_create_codec_entry - creates msm_sdw module + * @codec_root: The parent directory + * @codec: Codec instance + * + * Creates msm_sdw module and version entry under the given + * parent directory. + * + * Return: 0 on success or negative error code on failure. + */ +int msm_sdw_codec_info_create_codec_entry(struct snd_info_entry *codec_root, + struct snd_soc_codec *codec) +{ + struct snd_info_entry *version_entry; + struct msm_sdw_priv *msm_sdw; + struct snd_soc_card *card; + char name[80]; + + if (!codec_root || !codec) + return -EINVAL; + + msm_sdw = snd_soc_codec_get_drvdata(codec); + card = codec->component.card; + + snprintf(name, sizeof(name), "%x.%s", (u32)msm_sdw->sdw_base_addr, + "msm-sdw-codec"); + msm_sdw->entry = snd_info_create_subdir(codec_root->module, + (const char *)name, + codec_root); + if (!msm_sdw->entry) { + dev_err(codec->dev, "%s: failed to create msm_sdw entry\n", + __func__); + return -ENOMEM; + } + + version_entry = snd_info_create_card_entry(card->snd_card, + "version", + msm_sdw->entry); + if (!version_entry) { + dev_err(codec->dev, "%s: failed to create msm_sdw version entry\n", + __func__); + return -ENOMEM; + } + + version_entry->private_data = msm_sdw; + version_entry->size = MSM_SDW_VERSION_ENTRY_SIZE; + version_entry->content = SNDRV_INFO_CONTENT_DATA; + version_entry->c.ops = &msm_sdw_codec_info_ops; + + if (snd_info_register(version_entry) < 0) { + snd_info_free_entry(version_entry); + return -ENOMEM; + } + msm_sdw->version_entry = version_entry; + + return 0; +} +EXPORT_SYMBOL(msm_sdw_codec_info_create_codec_entry); + +static struct snd_soc_dai_ops msm_sdw_dai_ops = { + .startup = msm_sdw_startup, + .shutdown = msm_sdw_shutdown, + .hw_params = msm_sdw_hw_params, +}; + +static struct snd_soc_dai_driver msm_sdw_dai[] = { + { + .name = "msm_sdw_i2s_rx1", + .id = AIF1_SDW_PB, + .playback = { + .stream_name = "AIF1_SDW Playback", + .rates = MSM_SDW_RATES, + .formats = MSM_SDW_FORMATS, + .rate_max = 192000, + .rate_min = 8000, + .channels_min = 1, + .channels_max = 4, + }, + .ops = &msm_sdw_dai_ops, + }, + { + .name = "msm_sdw_vifeedback", + .id = AIF1_SDW_VIFEED, + .capture = { + .stream_name = "VIfeed_SDW", + .rates = MSM_SDW_RATES, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rate_max = 48000, + .rate_min = 8000, + .channels_min = 2, + .channels_max = 4, + }, + .ops = &msm_sdw_dai_ops, + }, +}; + +static const char * const rx_mix1_text[] = { + "ZERO", "RX4", "RX5" +}; + +static const char * const msm_sdw_ear_spkr_pa_gain_text[] = { + "G_DEFAULT", "G_0_DB", "G_1_DB", "G_2_DB", "G_3_DB", + "G_4_DB", "G_5_DB", "G_6_DB" +}; + +static const char * const msm_sdw_speaker_boost_stage_text[] = { + "NO_MAX_STATE", "MAX_STATE_1", "MAX_STATE_2" +}; + +static SOC_ENUM_SINGLE_EXT_DECL(msm_sdw_ear_spkr_pa_gain_enum, + msm_sdw_ear_spkr_pa_gain_text); +static SOC_ENUM_SINGLE_EXT_DECL(msm_sdw_spkr_boost_stage_enum, + msm_sdw_speaker_boost_stage_text); + +/* RX4 MIX1 */ +static const struct soc_enum rx4_mix1_inp1_chain_enum = + SOC_ENUM_SINGLE(MSM_SDW_TOP_RX7_PATH_INPUT0_MUX, + 0, 3, rx_mix1_text); + +static const struct soc_enum rx4_mix1_inp2_chain_enum = + SOC_ENUM_SINGLE(MSM_SDW_TOP_RX7_PATH_INPUT1_MUX, + 0, 3, rx_mix1_text); + +/* RX5 MIX1 */ +static const struct soc_enum rx5_mix1_inp1_chain_enum = + SOC_ENUM_SINGLE(MSM_SDW_TOP_RX8_PATH_INPUT0_MUX, + 0, 3, rx_mix1_text); + +static const struct soc_enum rx5_mix1_inp2_chain_enum = + SOC_ENUM_SINGLE(MSM_SDW_TOP_RX8_PATH_INPUT1_MUX, + 0, 3, rx_mix1_text); + +static const struct snd_kcontrol_new rx4_mix1_inp1_mux = + SOC_DAPM_ENUM("RX4 MIX1 INP1 Mux", rx4_mix1_inp1_chain_enum); + +static const struct snd_kcontrol_new rx4_mix1_inp2_mux = + SOC_DAPM_ENUM("RX4 MIX1 INP2 Mux", rx4_mix1_inp2_chain_enum); + +static const struct snd_kcontrol_new rx5_mix1_inp1_mux = + SOC_DAPM_ENUM("RX5 MIX1 INP1 Mux", rx5_mix1_inp1_chain_enum); + +static const struct snd_kcontrol_new rx5_mix1_inp2_mux = + SOC_DAPM_ENUM("RX5 MIX1 INP2 Mux", rx5_mix1_inp2_chain_enum); + +static const struct snd_kcontrol_new aif1_vi_mixer[] = { + SOC_SINGLE_EXT("SPKR_VI_1", SND_SOC_NOPM, MSM_SDW_TX0, 1, 0, + msm_sdw_vi_feed_mixer_get, msm_sdw_vi_feed_mixer_put), + SOC_SINGLE_EXT("SPKR_VI_2", SND_SOC_NOPM, MSM_SDW_TX1, 1, 0, + msm_sdw_vi_feed_mixer_get, msm_sdw_vi_feed_mixer_put), +}; + +static const struct snd_soc_dapm_widget msm_sdw_dapm_widgets[] = { + SND_SOC_DAPM_AIF_IN("I2S RX4", "AIF1_SDW Playback", 0, + SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_AIF_IN("I2S RX5", "AIF1_SDW Playback", 0, + SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_AIF_OUT_E("AIF1_SDW VI", "VIfeed_SDW", 0, SND_SOC_NOPM, + AIF1_SDW_VIFEED, 0, msm_sdw_codec_enable_vi_feedback, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER("AIF1_VI_SDW Mixer", SND_SOC_NOPM, AIF1_SDW_VIFEED, + 0, aif1_vi_mixer, ARRAY_SIZE(aif1_vi_mixer)), + + SND_SOC_DAPM_MUX_E("RX4 MIX1 INP1", SND_SOC_NOPM, 0, 0, + &rx4_mix1_inp1_mux, msm_sdw_enable_swr, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX4 MIX1 INP2", SND_SOC_NOPM, 0, 0, + &rx4_mix1_inp2_mux, msm_sdw_enable_swr, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX5 MIX1 INP1", SND_SOC_NOPM, 0, 0, + &rx5_mix1_inp1_mux, msm_sdw_enable_swr, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX5 MIX1 INP2", SND_SOC_NOPM, 0, 0, + &rx5_mix1_inp2_mux, msm_sdw_enable_swr, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER("RX4 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX5 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_MIXER_E("RX INT4 INTERP", SND_SOC_NOPM, + COMP1, 0, NULL, 0, msm_sdw_codec_enable_interpolator, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("RX INT5 INTERP", SND_SOC_NOPM, + COMP2, 0, NULL, 0, msm_sdw_codec_enable_interpolator, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MIXER_E("RX INT4 CHAIN", SND_SOC_NOPM, 0, 0, + NULL, 0, msm_sdw_codec_spk_boost_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("RX INT5 CHAIN", SND_SOC_NOPM, 0, 0, + NULL, 0, msm_sdw_codec_spk_boost_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_INPUT("VIINPUT_SDW"), + + SND_SOC_DAPM_OUTPUT("SPK1 OUT"), + SND_SOC_DAPM_OUTPUT("SPK2 OUT"), + + SND_SOC_DAPM_SUPPLY_S("SDW_CONN", -1, MSM_SDW_TOP_I2S_CLK, + 0, 0, NULL, 0), + + SND_SOC_DAPM_SUPPLY_S("INT_MCLK1", -2, SND_SOC_NOPM, 0, 0, + msm_int_mclk1_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("SDW_RX_I2S_CLK", + MSM_SDW_TOP_RX_I2S_CTL, 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("SDW_TX_I2S_CLK", + MSM_SDW_TOP_TX_I2S_CTL, 0, 0, NULL, 0), +}; + +static const struct snd_kcontrol_new msm_sdw_snd_controls[] = { + SOC_ENUM_EXT("EAR SPKR PA Gain", msm_sdw_ear_spkr_pa_gain_enum, + msm_sdw_ear_spkr_pa_gain_get, + msm_sdw_ear_spkr_pa_gain_put), + SOC_ENUM_EXT("SPKR Left Boost Max State", + msm_sdw_spkr_boost_stage_enum, + msm_sdw_spkr_left_boost_stage_get, + msm_sdw_spkr_left_boost_stage_put), + SOC_ENUM_EXT("SPKR Right Boost Max State", + msm_sdw_spkr_boost_stage_enum, + msm_sdw_spkr_right_boost_stage_get, + msm_sdw_spkr_right_boost_stage_put), + SOC_SINGLE_SX_TLV("RX4 Digital Volume", MSM_SDW_RX7_RX_VOL_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("RX5 Digital Volume", MSM_SDW_RX8_RX_VOL_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_EXT("COMP1 Switch", SND_SOC_NOPM, COMP1, 1, 0, + msm_sdw_get_compander, msm_sdw_set_compander), + SOC_SINGLE_EXT("COMP2 Switch", SND_SOC_NOPM, COMP2, 1, 0, + msm_sdw_get_compander, msm_sdw_set_compander), +}; + +static const struct snd_soc_dapm_route audio_map[] = { + + {"AIF1_SDW VI", NULL, "SDW_TX_I2S_CLK"}, + {"SDW_TX_I2S_CLK", NULL, "INT_MCLK1"}, + {"SDW_TX_I2S_CLK", NULL, "SDW_CONN"}, + + /* VI Feedback */ + {"AIF1_VI_SDW Mixer", "SPKR_VI_1", "VIINPUT_SDW"}, + {"AIF1_VI_SDW Mixer", "SPKR_VI_2", "VIINPUT_SDW"}, + {"AIF1_SDW VI", NULL, "AIF1_VI_SDW Mixer"}, + + {"SDW_RX_I2S_CLK", NULL, "INT_MCLK1"}, + {"SDW_RX_I2S_CLK", NULL, "SDW_CONN"}, + {"I2S RX4", NULL, "SDW_RX_I2S_CLK"}, + {"I2S RX5", NULL, "SDW_RX_I2S_CLK"}, + + {"RX4 MIX1 INP1", "RX4", "I2S RX4"}, + {"RX4 MIX1 INP1", "RX5", "I2S RX5"}, + {"RX4 MIX1 INP2", "RX4", "I2S RX4"}, + {"RX4 MIX1 INP2", "RX5", "I2S RX5"}, + {"RX5 MIX1 INP1", "RX4", "I2S RX4"}, + {"RX5 MIX1 INP1", "RX5", "I2S RX5"}, + {"RX5 MIX1 INP2", "RX4", "I2S RX4"}, + {"RX5 MIX1 INP2", "RX5", "I2S RX5"}, + + {"RX4 MIX1", NULL, "RX4 MIX1 INP1"}, + {"RX4 MIX1", NULL, "RX4 MIX1 INP2"}, + {"RX5 MIX1", NULL, "RX5 MIX1 INP1"}, + {"RX5 MIX1", NULL, "RX5 MIX1 INP2"}, + + {"RX INT4 INTERP", NULL, "RX4 MIX1"}, + {"RX INT4 CHAIN", NULL, "RX INT4 INTERP"}, + {"SPK1 OUT", NULL, "RX INT4 CHAIN"}, + + {"RX INT5 INTERP", NULL, "RX5 MIX1"}, + {"RX INT5 CHAIN", NULL, "RX INT5 INTERP"}, + {"SPK2 OUT", NULL, "RX INT5 CHAIN"}, +}; + +static const struct msm_sdw_reg_mask_val msm_sdw_reg_init[] = { + {MSM_SDW_BOOST0_BOOST_CFG1, 0x3F, 0x12}, + {MSM_SDW_BOOST0_BOOST_CFG2, 0x1C, 0x08}, + {MSM_SDW_COMPANDER7_CTL7, 0x1E, 0x18}, + {MSM_SDW_BOOST1_BOOST_CFG1, 0x3F, 0x12}, + {MSM_SDW_BOOST1_BOOST_CFG2, 0x1C, 0x08}, + {MSM_SDW_COMPANDER8_CTL7, 0x1E, 0x18}, + {MSM_SDW_BOOST0_BOOST_CTL, 0x7C, 0x58}, + {MSM_SDW_BOOST1_BOOST_CTL, 0x7C, 0x58}, + {MSM_SDW_RX7_RX_PATH_CFG1, 0x08, 0x08}, + {MSM_SDW_RX8_RX_PATH_CFG1, 0x08, 0x08}, + {MSM_SDW_TOP_TOP_CFG1, 0x02, 0x02}, + {MSM_SDW_TOP_TOP_CFG1, 0x01, 0x01}, + {MSM_SDW_TX9_SPKR_PROT_PATH_CFG0, 0x01, 0x01}, + {MSM_SDW_TX10_SPKR_PROT_PATH_CFG0, 0x01, 0x01}, + {MSM_SDW_TX11_SPKR_PROT_PATH_CFG0, 0x01, 0x01}, + {MSM_SDW_TX12_SPKR_PROT_PATH_CFG0, 0x01, 0x01}, + {MSM_SDW_COMPANDER7_CTL3, 0x80, 0x80}, + {MSM_SDW_COMPANDER8_CTL3, 0x80, 0x80}, + {MSM_SDW_COMPANDER7_CTL7, 0x01, 0x01}, + {MSM_SDW_COMPANDER8_CTL7, 0x01, 0x01}, + {MSM_SDW_RX7_RX_PATH_CFG0, 0x01, 0x01}, + {MSM_SDW_RX8_RX_PATH_CFG0, 0x01, 0x01}, + {MSM_SDW_RX7_RX_PATH_MIX_CFG, 0x01, 0x01}, + {MSM_SDW_RX8_RX_PATH_MIX_CFG, 0x01, 0x01}, +}; + +static void msm_sdw_init_reg(struct snd_soc_codec *codec) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(msm_sdw_reg_init); i++) + snd_soc_update_bits(codec, + msm_sdw_reg_init[i].reg, + msm_sdw_reg_init[i].mask, + msm_sdw_reg_init[i].val); +} + +static int msm_sdw_notifier_service_cb(struct notifier_block *nb, + unsigned long opcode, void *ptr) +{ + int i; + struct msm_sdw_priv *msm_sdw = container_of(nb, + struct msm_sdw_priv, + service_nb); + bool adsp_ready = false; + unsigned long timeout; + static bool initial_boot = true; + + pr_debug("%s: Service opcode 0x%lx\n", __func__, opcode); + + mutex_lock(&msm_sdw->codec_mutex); + switch (opcode) { + case AUDIO_NOTIFIER_SERVICE_DOWN: + if (initial_boot) { + initial_boot = false; + break; + } + msm_sdw->int_mclk1_enabled = false; + msm_sdw->dev_up = false; + for (i = 0; i < msm_sdw->nr; i++) + swrm_wcd_notify(msm_sdw->sdw_ctrl_data[i].sdw_pdev, + SWR_DEVICE_DOWN, NULL); + break; + case AUDIO_NOTIFIER_SERVICE_UP: + if (initial_boot) + initial_boot = false; + if (!q6core_is_adsp_ready()) { + dev_dbg(msm_sdw->dev, "ADSP isn't ready\n"); + timeout = jiffies + + msecs_to_jiffies(ADSP_STATE_READY_TIMEOUT_MS); + while (!time_after(jiffies, timeout)) { + if (!q6core_is_adsp_ready()) { + dev_dbg(msm_sdw->dev, + "ADSP isn't ready\n"); + } else { + dev_dbg(msm_sdw->dev, + "ADSP is ready\n"); + adsp_ready = true; + goto powerup; + } + } + } else { + adsp_ready = true; + dev_dbg(msm_sdw->dev, "%s: DSP is ready\n", __func__); + } +powerup: + if (adsp_ready) { + msm_sdw->dev_up = true; + msm_sdw_init_reg(msm_sdw->codec); + regcache_mark_dirty(msm_sdw->regmap); + regcache_sync(msm_sdw->regmap); + msm_sdw_set_spkr_mode(msm_sdw->codec, + msm_sdw->spkr_mode); + } + break; + default: + break; + } + mutex_unlock(&msm_sdw->codec_mutex); + return NOTIFY_OK; +} + +static int msm_sdw_codec_probe(struct snd_soc_codec *codec) +{ + struct msm_sdw_priv *msm_sdw; + int i, ret; + + msm_sdw = snd_soc_codec_get_drvdata(codec); + if (!msm_sdw) { + pr_err("%s:SDW priv data null\n", __func__); + return -EINVAL; + } + msm_sdw->codec = codec; + for (i = 0; i < COMP_MAX; i++) + msm_sdw->comp_enabled[i] = 0; + + msm_sdw->spkr_gain_offset = RX_GAIN_OFFSET_0_DB; + msm_sdw_init_reg(codec); + msm_sdw->version = MSM_SDW_VERSION_1_0; + + msm_sdw->service_nb.notifier_call = msm_sdw_notifier_service_cb; + ret = audio_notifier_register("msm_sdw", + AUDIO_NOTIFIER_ADSP_DOMAIN, + &msm_sdw->service_nb); + if (ret < 0) + dev_err(msm_sdw->dev, + "%s: Audio notifier register failed ret = %d\n", + __func__, ret); + return 0; +} + +static int msm_sdw_codec_remove(struct snd_soc_codec *codec) +{ + return 0; +} + +static struct regmap *msm_sdw_get_regmap(struct device *dev) +{ + struct msm_sdw_priv *msm_sdw = dev_get_drvdata(dev); + + return msm_sdw->regmap; +} + +static struct snd_soc_codec_driver soc_codec_dev_msm_sdw = { + .probe = msm_sdw_codec_probe, + .remove = msm_sdw_codec_remove, + .get_regmap = msm_sdw_get_regmap, + .component_driver = { + .controls = msm_sdw_snd_controls, + .num_controls = ARRAY_SIZE(msm_sdw_snd_controls), + .dapm_widgets = msm_sdw_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(msm_sdw_dapm_widgets), + .dapm_routes = audio_map, + .num_dapm_routes = ARRAY_SIZE(audio_map), + }, +}; + +static void msm_sdw_add_child_devices(struct work_struct *work) +{ + struct msm_sdw_priv *msm_sdw; + struct platform_device *pdev; + struct device_node *node; + struct msm_sdw_ctrl_data *sdw_ctrl_data = NULL, *temp; + int ret, ctrl_num = 0; + struct wcd_sdw_ctrl_platform_data *platdata; + char plat_dev_name[MSM_SDW_STRING_LEN]; + + msm_sdw = container_of(work, struct msm_sdw_priv, + msm_sdw_add_child_devices_work); + if (!msm_sdw) { + pr_err("%s: Memory for msm_sdw does not exist\n", + __func__); + return; + } + if (!msm_sdw->dev->of_node) { + dev_err(msm_sdw->dev, + "%s: DT node for msm_sdw does not exist\n", __func__); + return; + } + + platdata = &msm_sdw->sdw_plat_data; + + for_each_available_child_of_node(msm_sdw->dev->of_node, node) { + if (!strcmp(node->name, "swr_master")) + strlcpy(plat_dev_name, "msm_sdw_swr_ctrl", + (MSM_SDW_STRING_LEN - 1)); + else if (strnstr(node->name, "msm_cdc_pinctrl", + strlen("msm_cdc_pinctrl")) != NULL) + strlcpy(plat_dev_name, node->name, + (MSM_SDW_STRING_LEN - 1)); + else + continue; + + pdev = platform_device_alloc(plat_dev_name, -1); + if (!pdev) { + dev_err(msm_sdw->dev, "%s: pdev memory alloc failed\n", + __func__); + ret = -ENOMEM; + goto err; + } + pdev->dev.parent = msm_sdw->dev; + pdev->dev.of_node = node; + + if (!strcmp(node->name, "swr_master")) { + ret = platform_device_add_data(pdev, platdata, + sizeof(*platdata)); + if (ret) { + dev_err(&pdev->dev, + "%s: cannot add plat data ctrl:%d\n", + __func__, ctrl_num); + goto fail_pdev_add; + } + } + + ret = platform_device_add(pdev); + if (ret) { + dev_err(&pdev->dev, + "%s: Cannot add platform device\n", + __func__); + goto fail_pdev_add; + } + + if (!strcmp(node->name, "swr_master")) { + temp = krealloc(sdw_ctrl_data, + (ctrl_num + 1) * sizeof( + struct msm_sdw_ctrl_data), + GFP_KERNEL); + if (!temp) { + dev_err(&pdev->dev, "out of memory\n"); + ret = -ENOMEM; + goto err; + } + sdw_ctrl_data = temp; + sdw_ctrl_data[ctrl_num].sdw_pdev = pdev; + ctrl_num++; + dev_dbg(&pdev->dev, + "%s: Added soundwire ctrl device(s)\n", + __func__); + msm_sdw->nr = ctrl_num; + msm_sdw->sdw_ctrl_data = sdw_ctrl_data; + } + msm_sdw->pdev_child_devices[msm_sdw->child_count++] = pdev; + } + + return; +fail_pdev_add: + platform_device_put(pdev); +err: + return; +} + +static int msm_sdw_probe(struct platform_device *pdev) +{ + int ret = 0; + struct msm_sdw_priv *msm_sdw; + int adsp_state; + + adsp_state = apr_get_subsys_state(); + if (adsp_state != APR_SUBSYS_LOADED) { + dev_err(&pdev->dev, "Adsp is not loaded yet %d\n", + adsp_state); + return -EPROBE_DEFER; + } + + msm_sdw = devm_kzalloc(&pdev->dev, sizeof(struct msm_sdw_priv), + GFP_KERNEL); + if (!msm_sdw) + return -ENOMEM; + dev_set_drvdata(&pdev->dev, msm_sdw); + msm_sdw->dev_up = true; + + msm_sdw->dev = &pdev->dev; + INIT_WORK(&msm_sdw->msm_sdw_add_child_devices_work, + msm_sdw_add_child_devices); + msm_sdw->sdw_plat_data.handle = (void *) msm_sdw; + msm_sdw->sdw_plat_data.read = msm_sdw_swrm_read; + msm_sdw->sdw_plat_data.write = msm_sdw_swrm_write; + msm_sdw->sdw_plat_data.bulk_write = msm_sdw_swrm_bulk_write; + msm_sdw->sdw_plat_data.clk = msm_sdw_swrm_clock; + msm_sdw->sdw_plat_data.handle_irq = msm_sdwm_handle_irq; + ret = of_property_read_u32(pdev->dev.of_node, "reg", + &msm_sdw->sdw_base_addr); + if (ret) { + dev_err(&pdev->dev, "%s: could not find %s entry in dt\n", + __func__, "reg"); + goto err_sdw_cdc; + } + + msm_sdw->sdw_gpio_p = of_parse_phandle(pdev->dev.of_node, + "qcom,cdc-sdw-gpios", 0); + msm_sdw->sdw_base = ioremap(msm_sdw->sdw_base_addr, + MSM_SDW_MAX_REGISTER); + msm_sdw->read_dev = __msm_sdw_reg_read; + msm_sdw->write_dev = __msm_sdw_reg_write; + + msm_sdw->regmap = msm_sdw_regmap_init(msm_sdw->dev, + &msm_sdw_regmap_config); + msm_sdw->sdw_irq = platform_get_irq_byname(pdev, "swr_master_irq"); + if (msm_sdw->sdw_irq < 0) { + dev_err(msm_sdw->dev, "%s() error getting irq handle: %d\n", + __func__, msm_sdw->sdw_irq); + ret = -ENODEV; + goto err_sdw_cdc; + } + ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_msm_sdw, + msm_sdw_dai, ARRAY_SIZE(msm_sdw_dai)); + if (ret) { + dev_err(&pdev->dev, "%s: Codec registration failed, ret = %d\n", + __func__, ret); + goto err_sdw_cdc; + } + /* initialize the int_mclk1 */ + msm_sdw->sdw_cdc_core_clk.clk_set_minor_version = + AFE_API_VERSION_I2S_CONFIG; + msm_sdw->sdw_cdc_core_clk.clk_id = + Q6AFE_LPASS_CLK_ID_INT_MCLK_1; + msm_sdw->sdw_cdc_core_clk.clk_freq_in_hz = + INT_MCLK1_FREQ; + msm_sdw->sdw_cdc_core_clk.clk_attri = + Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO; + msm_sdw->sdw_cdc_core_clk.clk_root = + Q6AFE_LPASS_CLK_ROOT_DEFAULT; + msm_sdw->sdw_cdc_core_clk.enable = 0; + + /* initialize the sdw_npl_clk */ + msm_sdw->sdw_npl_clk.clk_set_minor_version = + AFE_API_VERSION_I2S_CONFIG; + msm_sdw->sdw_npl_clk.clk_id = + AFE_CLOCK_SET_CLOCK_ID_SWR_NPL_CLK; + msm_sdw->sdw_npl_clk.clk_freq_in_hz = SDW_NPL_FREQ; + msm_sdw->sdw_npl_clk.clk_attri = + Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO; + msm_sdw->sdw_npl_clk.clk_root = + Q6AFE_LPASS_CLK_ROOT_DEFAULT; + msm_sdw->sdw_npl_clk.enable = 0; + + INIT_DELAYED_WORK(&msm_sdw->disable_int_mclk1_work, + msm_disable_int_mclk1); + mutex_init(&msm_sdw->cdc_int_mclk1_mutex); + mutex_init(&msm_sdw->sdw_npl_clk_mutex); + mutex_init(&msm_sdw->io_lock); + mutex_init(&msm_sdw->sdw_read_lock); + mutex_init(&msm_sdw->sdw_write_lock); + mutex_init(&msm_sdw->sdw_clk_lock); + mutex_init(&msm_sdw->codec_mutex); + schedule_work(&msm_sdw->msm_sdw_add_child_devices_work); + + dev_dbg(&pdev->dev, "%s: msm_sdw driver probe done\n", __func__); + return ret; + +err_sdw_cdc: + devm_kfree(&pdev->dev, msm_sdw); + return ret; +} + +static int msm_sdw_remove(struct platform_device *pdev) +{ + struct msm_sdw_priv *msm_sdw; + int count; + + msm_sdw = dev_get_drvdata(&pdev->dev); + + for (count = 0; count < msm_sdw->child_count && + count < MSM_SDW_CHILD_DEVICES_MAX; count++) + platform_device_unregister(msm_sdw->pdev_child_devices[count]); + + mutex_destroy(&msm_sdw->io_lock); + mutex_destroy(&msm_sdw->sdw_read_lock); + mutex_destroy(&msm_sdw->sdw_write_lock); + mutex_destroy(&msm_sdw->sdw_clk_lock); + mutex_destroy(&msm_sdw->codec_mutex); + mutex_destroy(&msm_sdw->cdc_int_mclk1_mutex); + + devm_kfree(&pdev->dev, msm_sdw); + snd_soc_unregister_codec(&pdev->dev); + return 0; +} + +static const struct of_device_id msm_sdw_codec_dt_match[] = { + { .compatible = "qcom,msm-sdw-codec", }, + {} +}; + +static struct platform_driver msm_sdw_codec_driver = { + .probe = msm_sdw_probe, + .remove = msm_sdw_remove, + .driver = { + .name = "msm_sdw_codec", + .owner = THIS_MODULE, + .of_match_table = msm_sdw_codec_dt_match, + }, +}; +module_platform_driver(msm_sdw_codec_driver); + +MODULE_DESCRIPTION("MSM Soundwire Codec driver"); +MODULE_LICENSE("GPL v2"); diff --git a/audio-kernel/asoc/codecs/msm_sdw/msm_sdw_cdc_utils.c b/audio-kernel/asoc/codecs/msm_sdw/msm_sdw_cdc_utils.c new file mode 100644 index 000000000..9a5c85bf0 --- /dev/null +++ b/audio-kernel/asoc/codecs/msm_sdw/msm_sdw_cdc_utils.c @@ -0,0 +1,211 @@ +/* Copyright (c) 2016-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 + * 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 +#include +#include "msm_sdw.h" + +#define REG_BYTES 2 +#define VAL_BYTES 1 +/* + * Page Register Address that APP Proc uses to + * access WCD9335 Codec registers is identified + * as 0x00 + */ +#define PAGE_REG_ADDR 0x00 + +/* + * msm_sdw_page_write: + * Retrieve page number from register and + * write that page number to the page address. + * Called under io_lock acquisition. + * + * @msm_sdw: pointer to msm_sdw + * @reg: Register address from which page number is retrieved + * + * Returns 0 for success and negative error code for failure. + */ +int msm_sdw_page_write(struct msm_sdw_priv *msm_sdw, unsigned short reg) +{ + int ret = 0; + u8 pg_num, prev_pg_num; + + pg_num = msm_sdw_page_map[reg]; + if (msm_sdw->prev_pg_valid) { + prev_pg_num = msm_sdw->prev_pg; + if (prev_pg_num != pg_num) { + ret = msm_sdw->write_dev(msm_sdw, PAGE_REG_ADDR, 1, + (void *) &pg_num); + if (ret < 0) { + dev_err(msm_sdw->dev, + "page write error, pg_num: 0x%x\n", + pg_num); + } else { + msm_sdw->prev_pg = pg_num; + dev_dbg(msm_sdw->dev, + "%s: Page 0x%x Write to 0x00\n", + __func__, pg_num); + } + } + } else { + ret = msm_sdw->write_dev(msm_sdw, PAGE_REG_ADDR, 1, + (void *) &pg_num); + if (ret < 0) { + dev_err(msm_sdw->dev, + "page write error, pg_num: 0x%x\n", pg_num); + } else { + msm_sdw->prev_pg = pg_num; + msm_sdw->prev_pg_valid = true; + dev_dbg(msm_sdw->dev, "%s: Page 0x%x Write to 0x00\n", + __func__, pg_num); + } + } + return ret; +} +EXPORT_SYMBOL(msm_sdw_page_write); + +static int regmap_bus_read(void *context, const void *reg, size_t reg_size, + void *val, size_t val_size) +{ + struct device *dev = context; + struct msm_sdw_priv *msm_sdw = dev_get_drvdata(dev); + unsigned short c_reg; + int ret, i; + + if (!msm_sdw) { + dev_err(dev, "%s: msm_sdw is NULL\n", __func__); + return -EINVAL; + } + if (!reg || !val) { + dev_err(dev, "%s: reg or val is NULL\n", __func__); + return -EINVAL; + } + if (reg_size != REG_BYTES) { + dev_err(dev, "%s: register size %zd bytes, not supported\n", + __func__, reg_size); + return -EINVAL; + } + if (!msm_sdw->dev_up) { + dev_dbg_ratelimited(dev, "%s: No read allowed. dev_up = %d\n", + __func__, msm_sdw->dev_up); + return 0; + } + + mutex_lock(&msm_sdw->io_lock); + c_reg = *(u16 *)reg; + ret = msm_sdw_page_write(msm_sdw, c_reg); + if (ret) + goto err; + ret = msm_sdw->read_dev(msm_sdw, c_reg, val_size, val); + if (ret < 0) + dev_err(dev, "%s: Codec read failed (%d), reg: 0x%x, size:%zd\n", + __func__, ret, c_reg, val_size); + else { + for (i = 0; i < val_size; i++) + dev_dbg(dev, "%s: Read 0x%02x from 0x%x\n", + __func__, ((u8 *)val)[i], c_reg + i); + } +err: + mutex_unlock(&msm_sdw->io_lock); + + return ret; +} + +static int regmap_bus_gather_write(void *context, + const void *reg, size_t reg_size, + const void *val, size_t val_size) +{ + struct device *dev = context; + struct msm_sdw_priv *msm_sdw = dev_get_drvdata(dev); + unsigned short c_reg; + int ret, i; + + if (!msm_sdw) { + dev_err(dev, "%s: msm_sdw is NULL\n", __func__); + return -EINVAL; + } + if (!reg || !val) { + dev_err(dev, "%s: reg or val is NULL\n", __func__); + return -EINVAL; + } + if (reg_size != REG_BYTES) { + dev_err(dev, "%s: register size %zd bytes, not supported\n", + __func__, reg_size); + return -EINVAL; + } + if (!msm_sdw->dev_up) { + dev_dbg_ratelimited(dev, "%s: No write allowed. dev_up = %d\n", + __func__, msm_sdw->dev_up); + return 0; + } + + mutex_lock(&msm_sdw->io_lock); + c_reg = *(u16 *)reg; + ret = msm_sdw_page_write(msm_sdw, c_reg); + if (ret) + goto err; + + for (i = 0; i < val_size; i++) + dev_dbg(dev, "Write %02x to 0x%x\n", ((u8 *)val)[i], + c_reg + i*4); + + ret = msm_sdw->write_dev(msm_sdw, c_reg, val_size, (void *) val); + if (ret < 0) + dev_err(dev, + "%s: Codec write failed (%d), reg:0x%x, size:%zd\n", + __func__, ret, c_reg, val_size); + +err: + mutex_unlock(&msm_sdw->io_lock); + return ret; +} + +static int regmap_bus_write(void *context, const void *data, size_t count) +{ + struct device *dev = context; + struct msm_sdw_priv *msm_sdw = dev_get_drvdata(dev); + + if (!msm_sdw) + return -EINVAL; + + WARN_ON(count < REG_BYTES); + + return regmap_bus_gather_write(context, data, REG_BYTES, + data + REG_BYTES, + count - REG_BYTES); + +} + +static struct regmap_bus regmap_bus_config = { + .write = regmap_bus_write, + .gather_write = regmap_bus_gather_write, + .read = regmap_bus_read, + .reg_format_endian_default = REGMAP_ENDIAN_NATIVE, + .val_format_endian_default = REGMAP_ENDIAN_NATIVE, +}; + +/* + * msm_sdw_regmap_init: + * Initialize msm_sdw register map + * + * @dev: pointer to wcd device + * @config: pointer to register map config + * + * Returns pointer to regmap structure for success + * or NULL in case of failure. + */ +struct regmap *msm_sdw_regmap_init(struct device *dev, + const struct regmap_config *config) +{ + return devm_regmap_init(dev, ®map_bus_config, dev, config); +} +EXPORT_SYMBOL(msm_sdw_regmap_init); diff --git a/audio-kernel/asoc/codecs/msm_sdw/msm_sdw_registers.h b/audio-kernel/asoc/codecs/msm_sdw/msm_sdw_registers.h new file mode 100644 index 000000000..1b7b0b01a --- /dev/null +++ b/audio-kernel/asoc/codecs/msm_sdw/msm_sdw_registers.h @@ -0,0 +1,126 @@ +/* Copyright (c) 2016-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 + * 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 MSM_SDW_REGISTERS_H +#define MSM_SDW_REGISTERS_H + +#define MSM_SDW_PAGE_REGISTER 0x0000 + +/* Page-A Registers */ +#define MSM_SDW_TX9_SPKR_PROT_PATH_CTL 0x0308 +#define MSM_SDW_TX9_SPKR_PROT_PATH_CFG0 0x030c +#define MSM_SDW_TX10_SPKR_PROT_PATH_CTL 0x0318 +#define MSM_SDW_TX10_SPKR_PROT_PATH_CFG0 0x031c +#define MSM_SDW_TX11_SPKR_PROT_PATH_CTL 0x0328 +#define MSM_SDW_TX11_SPKR_PROT_PATH_CFG0 0x032c +#define MSM_SDW_TX12_SPKR_PROT_PATH_CTL 0x0338 +#define MSM_SDW_TX12_SPKR_PROT_PATH_CFG0 0x033c + +/* Page-B Registers */ +#define MSM_SDW_COMPANDER7_CTL0 0x0024 +#define MSM_SDW_COMPANDER7_CTL1 0x0028 +#define MSM_SDW_COMPANDER7_CTL2 0x002c +#define MSM_SDW_COMPANDER7_CTL3 0x0030 +#define MSM_SDW_COMPANDER7_CTL4 0x0034 +#define MSM_SDW_COMPANDER7_CTL5 0x0038 +#define MSM_SDW_COMPANDER7_CTL6 0x003c +#define MSM_SDW_COMPANDER7_CTL7 0x0040 +#define MSM_SDW_COMPANDER8_CTL0 0x0044 +#define MSM_SDW_COMPANDER8_CTL1 0x0048 +#define MSM_SDW_COMPANDER8_CTL2 0x004c +#define MSM_SDW_COMPANDER8_CTL3 0x0050 +#define MSM_SDW_COMPANDER8_CTL4 0x0054 +#define MSM_SDW_COMPANDER8_CTL5 0x0058 +#define MSM_SDW_COMPANDER8_CTL6 0x005c +#define MSM_SDW_COMPANDER8_CTL7 0x0060 +#define MSM_SDW_RX7_RX_PATH_CTL 0x01a4 +#define MSM_SDW_RX7_RX_PATH_CFG0 0x01a8 +#define MSM_SDW_RX7_RX_PATH_CFG1 0x01ac +#define MSM_SDW_RX7_RX_PATH_CFG2 0x01b0 +#define MSM_SDW_RX7_RX_VOL_CTL 0x01b4 +#define MSM_SDW_RX7_RX_PATH_MIX_CTL 0x01b8 +#define MSM_SDW_RX7_RX_PATH_MIX_CFG 0x01bc +#define MSM_SDW_RX7_RX_VOL_MIX_CTL 0x01c0 +#define MSM_SDW_RX7_RX_PATH_SEC0 0x01c4 +#define MSM_SDW_RX7_RX_PATH_SEC1 0x01c8 +#define MSM_SDW_RX7_RX_PATH_SEC2 0x01cc +#define MSM_SDW_RX7_RX_PATH_SEC3 0x01d0 +#define MSM_SDW_RX7_RX_PATH_SEC5 0x01d8 +#define MSM_SDW_RX7_RX_PATH_SEC6 0x01dc +#define MSM_SDW_RX7_RX_PATH_SEC7 0x01e0 +#define MSM_SDW_RX7_RX_PATH_MIX_SEC0 0x01e4 +#define MSM_SDW_RX7_RX_PATH_MIX_SEC1 0x01e8 +#define MSM_SDW_RX8_RX_PATH_CTL 0x0384 +#define MSM_SDW_RX8_RX_PATH_CFG0 0x0388 +#define MSM_SDW_RX8_RX_PATH_CFG1 0x038c +#define MSM_SDW_RX8_RX_PATH_CFG2 0x0390 +#define MSM_SDW_RX8_RX_VOL_CTL 0x0394 +#define MSM_SDW_RX8_RX_PATH_MIX_CTL 0x0398 +#define MSM_SDW_RX8_RX_PATH_MIX_CFG 0x039c +#define MSM_SDW_RX8_RX_VOL_MIX_CTL 0x03a0 +#define MSM_SDW_RX8_RX_PATH_SEC0 0x03a4 +#define MSM_SDW_RX8_RX_PATH_SEC1 0x03a8 +#define MSM_SDW_RX8_RX_PATH_SEC2 0x03ac +#define MSM_SDW_RX8_RX_PATH_SEC3 0x03b0 +#define MSM_SDW_RX8_RX_PATH_SEC5 0x03b8 +#define MSM_SDW_RX8_RX_PATH_SEC6 0x03bc +#define MSM_SDW_RX8_RX_PATH_SEC7 0x03c0 +#define MSM_SDW_RX8_RX_PATH_MIX_SEC0 0x03c4 +#define MSM_SDW_RX8_RX_PATH_MIX_SEC1 0x03c8 + +/* Page-C Registers */ +#define MSM_SDW_BOOST0_BOOST_PATH_CTL 0x0064 +#define MSM_SDW_BOOST0_BOOST_CTL 0x0068 +#define MSM_SDW_BOOST0_BOOST_CFG1 0x006c +#define MSM_SDW_BOOST0_BOOST_CFG2 0x0070 +#define MSM_SDW_BOOST1_BOOST_PATH_CTL 0x0084 +#define MSM_SDW_BOOST1_BOOST_CTL 0x0088 +#define MSM_SDW_BOOST1_BOOST_CFG1 0x008c +#define MSM_SDW_BOOST1_BOOST_CFG2 0x0090 +#define MSM_SDW_AHB_BRIDGE_WR_DATA_0 0x00a4 +#define MSM_SDW_AHB_BRIDGE_WR_DATA_1 0x00a8 +#define MSM_SDW_AHB_BRIDGE_WR_DATA_2 0x00ac +#define MSM_SDW_AHB_BRIDGE_WR_DATA_3 0x00b0 +#define MSM_SDW_AHB_BRIDGE_WR_ADDR_0 0x00b4 +#define MSM_SDW_AHB_BRIDGE_WR_ADDR_1 0x00b8 +#define MSM_SDW_AHB_BRIDGE_WR_ADDR_2 0x00bc +#define MSM_SDW_AHB_BRIDGE_WR_ADDR_3 0x00c0 +#define MSM_SDW_AHB_BRIDGE_RD_ADDR_0 0x00c4 +#define MSM_SDW_AHB_BRIDGE_RD_ADDR_1 0x00c8 +#define MSM_SDW_AHB_BRIDGE_RD_ADDR_2 0x00cc +#define MSM_SDW_AHB_BRIDGE_RD_ADDR_3 0x00d0 +#define MSM_SDW_AHB_BRIDGE_RD_DATA_0 0x00d4 +#define MSM_SDW_AHB_BRIDGE_RD_DATA_1 0x00d8 +#define MSM_SDW_AHB_BRIDGE_RD_DATA_2 0x00dc +#define MSM_SDW_AHB_BRIDGE_RD_DATA_3 0x00e0 +#define MSM_SDW_AHB_BRIDGE_ACCESS_CFG 0x00e4 +#define MSM_SDW_AHB_BRIDGE_ACCESS_STATUS 0x00e8 + +/* Page-D Registers */ +#define MSM_SDW_CLK_RST_CTRL_MCLK_CONTROL 0x0104 +#define MSM_SDW_CLK_RST_CTRL_FS_CNT_CONTROL 0x0108 +#define MSM_SDW_CLK_RST_CTRL_SWR_CONTROL 0x010c +#define MSM_SDW_TOP_TOP_CFG0 0x0204 +#define MSM_SDW_TOP_TOP_CFG1 0x0208 +#define MSM_SDW_TOP_RX_I2S_CTL 0x020c +#define MSM_SDW_TOP_TX_I2S_CTL 0x0210 +#define MSM_SDW_TOP_I2S_CLK 0x0214 +#define MSM_SDW_TOP_RX7_PATH_INPUT0_MUX 0x0218 +#define MSM_SDW_TOP_RX7_PATH_INPUT1_MUX 0x021c +#define MSM_SDW_TOP_RX8_PATH_INPUT0_MUX 0x0220 +#define MSM_SDW_TOP_RX8_PATH_INPUT1_MUX 0x0224 +#define MSM_SDW_TOP_FREQ_MCLK 0x0228 +#define MSM_SDW_TOP_DEBUG_BUS_SEL 0x022c +#define MSM_SDW_TOP_DEBUG_EN 0x0230 +#define MSM_SDW_TOP_I2S_RESET 0x0234 +#define MSM_SDW_TOP_BLOCKS_RESET 0x0238 + +#endif diff --git a/audio-kernel/asoc/codecs/msm_sdw/msm_sdw_regmap.c b/audio-kernel/asoc/codecs/msm_sdw/msm_sdw_regmap.c new file mode 100644 index 000000000..7613e65d2 --- /dev/null +++ b/audio-kernel/asoc/codecs/msm_sdw/msm_sdw_regmap.c @@ -0,0 +1,161 @@ +/* Copyright (c) 2016-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 +#include "msm_sdw.h" + +static const struct reg_default msm_sdw_defaults[] = { + /* Page #10 registers */ + { MSM_SDW_PAGE_REGISTER, 0x00 }, + { MSM_SDW_TX9_SPKR_PROT_PATH_CTL, 0x02 }, + { MSM_SDW_TX9_SPKR_PROT_PATH_CFG0, 0x00 }, + { MSM_SDW_TX10_SPKR_PROT_PATH_CTL, 0x02 }, + { MSM_SDW_TX10_SPKR_PROT_PATH_CFG0, 0x00 }, + { MSM_SDW_TX11_SPKR_PROT_PATH_CTL, 0x02 }, + { MSM_SDW_TX11_SPKR_PROT_PATH_CFG0, 0x00 }, + { MSM_SDW_TX12_SPKR_PROT_PATH_CTL, 0x02 }, + { MSM_SDW_TX12_SPKR_PROT_PATH_CFG0, 0x00 }, + /* Page #11 registers */ + { MSM_SDW_COMPANDER7_CTL0, 0x60 }, + { MSM_SDW_COMPANDER7_CTL1, 0xdb }, + { MSM_SDW_COMPANDER7_CTL2, 0xff }, + { MSM_SDW_COMPANDER7_CTL3, 0x35 }, + { MSM_SDW_COMPANDER7_CTL4, 0xff }, + { MSM_SDW_COMPANDER7_CTL5, 0x00 }, + { MSM_SDW_COMPANDER7_CTL6, 0x01 }, + { MSM_SDW_COMPANDER8_CTL0, 0x60 }, + { MSM_SDW_COMPANDER8_CTL1, 0xdb }, + { MSM_SDW_COMPANDER8_CTL2, 0xff }, + { MSM_SDW_COMPANDER8_CTL3, 0x35 }, + { MSM_SDW_COMPANDER8_CTL4, 0xff }, + { MSM_SDW_COMPANDER8_CTL5, 0x00 }, + { MSM_SDW_COMPANDER8_CTL6, 0x01 }, + { MSM_SDW_RX7_RX_PATH_CTL, 0x04 }, + { MSM_SDW_RX7_RX_PATH_CFG0, 0x00 }, + { MSM_SDW_RX7_RX_PATH_CFG2, 0x8f }, + { MSM_SDW_RX7_RX_VOL_CTL, 0x00 }, + { MSM_SDW_RX7_RX_PATH_MIX_CTL, 0x04 }, + { MSM_SDW_RX7_RX_VOL_MIX_CTL, 0x00 }, + { MSM_SDW_RX7_RX_PATH_SEC2, 0x00 }, + { MSM_SDW_RX7_RX_PATH_SEC3, 0x00 }, + { MSM_SDW_RX7_RX_PATH_SEC5, 0x00 }, + { MSM_SDW_RX7_RX_PATH_SEC6, 0x00 }, + { MSM_SDW_RX7_RX_PATH_SEC7, 0x00 }, + { MSM_SDW_RX7_RX_PATH_MIX_SEC1, 0x00 }, + { MSM_SDW_RX8_RX_PATH_CTL, 0x04 }, + { MSM_SDW_RX8_RX_PATH_CFG0, 0x00 }, + { MSM_SDW_RX8_RX_PATH_CFG2, 0x8f }, + { MSM_SDW_RX8_RX_VOL_CTL, 0x00 }, + { MSM_SDW_RX8_RX_PATH_MIX_CTL, 0x04 }, + { MSM_SDW_RX8_RX_VOL_MIX_CTL, 0x00 }, + { MSM_SDW_RX8_RX_PATH_SEC2, 0x00 }, + { MSM_SDW_RX8_RX_PATH_SEC3, 0x00 }, + { MSM_SDW_RX8_RX_PATH_SEC5, 0x00 }, + { MSM_SDW_RX8_RX_PATH_SEC6, 0x00 }, + { MSM_SDW_RX8_RX_PATH_SEC7, 0x00 }, + { MSM_SDW_RX8_RX_PATH_MIX_SEC1, 0x00 }, + /* Page #12 registers */ + { MSM_SDW_BOOST0_BOOST_PATH_CTL, 0x00 }, + { MSM_SDW_BOOST0_BOOST_CTL, 0xb2 }, + { MSM_SDW_BOOST0_BOOST_CFG1, 0x00 }, + { MSM_SDW_BOOST0_BOOST_CFG2, 0x00 }, + { MSM_SDW_BOOST1_BOOST_PATH_CTL, 0x00 }, + { MSM_SDW_BOOST1_BOOST_CTL, 0xb2 }, + { MSM_SDW_BOOST1_BOOST_CFG1, 0x00 }, + { MSM_SDW_BOOST1_BOOST_CFG2, 0x00 }, + { MSM_SDW_AHB_BRIDGE_WR_DATA_0, 0x00 }, + { MSM_SDW_AHB_BRIDGE_WR_DATA_1, 0x00 }, + { MSM_SDW_AHB_BRIDGE_WR_DATA_2, 0x00 }, + { MSM_SDW_AHB_BRIDGE_WR_DATA_3, 0x00 }, + { MSM_SDW_AHB_BRIDGE_WR_ADDR_0, 0x00 }, + { MSM_SDW_AHB_BRIDGE_WR_ADDR_1, 0x00 }, + { MSM_SDW_AHB_BRIDGE_WR_ADDR_2, 0x00 }, + { MSM_SDW_AHB_BRIDGE_WR_ADDR_3, 0x00 }, + { MSM_SDW_AHB_BRIDGE_RD_ADDR_0, 0x00 }, + { MSM_SDW_AHB_BRIDGE_RD_ADDR_1, 0x00 }, + { MSM_SDW_AHB_BRIDGE_RD_ADDR_2, 0x00 }, + { MSM_SDW_AHB_BRIDGE_RD_ADDR_3, 0x00 }, + { MSM_SDW_AHB_BRIDGE_RD_DATA_0, 0x00 }, + { MSM_SDW_AHB_BRIDGE_RD_DATA_1, 0x00 }, + { MSM_SDW_AHB_BRIDGE_RD_DATA_2, 0x00 }, + { MSM_SDW_AHB_BRIDGE_RD_DATA_3, 0x00 }, + { MSM_SDW_AHB_BRIDGE_ACCESS_CFG, 0x0f }, + { MSM_SDW_AHB_BRIDGE_ACCESS_STATUS, 0x03 }, + /* Page #13 registers */ + { MSM_SDW_CLK_RST_CTRL_MCLK_CONTROL, 0x00 }, + { MSM_SDW_CLK_RST_CTRL_FS_CNT_CONTROL, 0x00 }, + { MSM_SDW_CLK_RST_CTRL_SWR_CONTROL, 0x00 }, + { MSM_SDW_TOP_TOP_CFG0, 0x00 }, + { MSM_SDW_TOP_TOP_CFG1, 0x00 }, + { MSM_SDW_TOP_RX_I2S_CTL, 0x0C }, + { MSM_SDW_TOP_TX_I2S_CTL, 0x00 }, + { MSM_SDW_TOP_I2S_CLK, 0x00 }, + { MSM_SDW_TOP_RX7_PATH_INPUT0_MUX, 0x00 }, + { MSM_SDW_TOP_RX7_PATH_INPUT1_MUX, 0x00 }, + { MSM_SDW_TOP_RX8_PATH_INPUT0_MUX, 0x00 }, + { MSM_SDW_TOP_RX8_PATH_INPUT1_MUX, 0x00 }, + { MSM_SDW_TOP_FREQ_MCLK, 0x00 }, + { MSM_SDW_TOP_DEBUG_BUS_SEL, 0x00 }, + { MSM_SDW_TOP_DEBUG_EN, 0x00 }, + { MSM_SDW_TOP_I2S_RESET, 0x00 }, + { MSM_SDW_TOP_BLOCKS_RESET, 0x00 }, +}; + +static bool msm_sdw_is_readable_register(struct device *dev, unsigned int reg) +{ + return msm_sdw_reg_readable[reg]; +} + +static bool msm_sdw_is_writeable_register(struct device *dev, unsigned int reg) +{ + return msm_sdw_reg_writeable[reg]; +} + +static bool msm_sdw_is_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case MSM_SDW_AHB_BRIDGE_WR_DATA_0: + case MSM_SDW_AHB_BRIDGE_WR_DATA_1: + case MSM_SDW_AHB_BRIDGE_WR_DATA_2: + case MSM_SDW_AHB_BRIDGE_WR_DATA_3: + case MSM_SDW_AHB_BRIDGE_WR_ADDR_0: + case MSM_SDW_AHB_BRIDGE_WR_ADDR_1: + case MSM_SDW_AHB_BRIDGE_WR_ADDR_2: + case MSM_SDW_AHB_BRIDGE_WR_ADDR_3: + case MSM_SDW_AHB_BRIDGE_RD_DATA_0: + case MSM_SDW_AHB_BRIDGE_RD_DATA_1: + case MSM_SDW_AHB_BRIDGE_RD_DATA_2: + case MSM_SDW_AHB_BRIDGE_RD_DATA_3: + case MSM_SDW_AHB_BRIDGE_RD_ADDR_0: + case MSM_SDW_AHB_BRIDGE_RD_ADDR_1: + case MSM_SDW_AHB_BRIDGE_RD_ADDR_2: + case MSM_SDW_AHB_BRIDGE_RD_ADDR_3: + case MSM_SDW_CLK_RST_CTRL_MCLK_CONTROL: + case MSM_SDW_CLK_RST_CTRL_FS_CNT_CONTROL: + return true; + default: + return false; + } +} + +const struct regmap_config msm_sdw_regmap_config = { + .reg_bits = 16, + .val_bits = 8, + .reg_stride = 4, + .cache_type = REGCACHE_RBTREE, + .reg_defaults = msm_sdw_defaults, + .num_reg_defaults = ARRAY_SIZE(msm_sdw_defaults), + .max_register = MSM_SDW_MAX_REGISTER, + .writeable_reg = msm_sdw_is_writeable_register, + .volatile_reg = msm_sdw_is_volatile_register, + .readable_reg = msm_sdw_is_readable_register, +}; diff --git a/audio-kernel/asoc/codecs/msm_stub.c b/audio-kernel/asoc/codecs/msm_stub.c new file mode 100644 index 000000000..68e55ae1d --- /dev/null +++ b/audio-kernel/asoc/codecs/msm_stub.c @@ -0,0 +1,88 @@ +/* Copyright (c) 2011-2014, 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 + * 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 +#include +#include +#include +#include +#include +#include + +/* A dummy driver useful only to advertise hardware parameters */ +static struct snd_soc_dai_driver msm_stub_dais[] = { + { + .name = "msm-stub-rx", + .playback = { /* Support maximum range */ + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 8, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + }, + { + .name = "msm-stub-tx", + .capture = { /* Support maximum range */ + .stream_name = "Record", + .channels_min = 1, + .channels_max = 8, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + }, + }, +}; + +static struct snd_soc_codec_driver soc_msm_stub = {}; + +static int msm_stub_dev_probe(struct platform_device *pdev) +{ + dev_dbg(&pdev->dev, "dev name %s\n", dev_name(&pdev->dev)); + + return snd_soc_register_codec(&pdev->dev, + &soc_msm_stub, msm_stub_dais, ARRAY_SIZE(msm_stub_dais)); +} + +static int msm_stub_dev_remove(struct platform_device *pdev) +{ + snd_soc_unregister_codec(&pdev->dev); + return 0; +} +static const struct of_device_id msm_stub_codec_dt_match[] = { + { .compatible = "qcom,msm-stub-codec", }, + {} +}; + +static struct platform_driver msm_stub_driver = { + .driver = { + .name = "msm-stub-codec", + .owner = THIS_MODULE, + .of_match_table = msm_stub_codec_dt_match, + }, + .probe = msm_stub_dev_probe, + .remove = msm_stub_dev_remove, +}; + +static int __init msm_stub_init(void) +{ + return platform_driver_register(&msm_stub_driver); +} +module_init(msm_stub_init); + +static void __exit msm_stub_exit(void) +{ + platform_driver_unregister(&msm_stub_driver); +} +module_exit(msm_stub_exit); + +MODULE_DESCRIPTION("Generic MSM CODEC driver"); +MODULE_LICENSE("GPL v2"); diff --git a/audio-kernel/asoc/codecs/pdata.h b/audio-kernel/asoc/codecs/pdata.h new file mode 100644 index 000000000..d38ae8491 --- /dev/null +++ b/audio-kernel/asoc/codecs/pdata.h @@ -0,0 +1,201 @@ +/* Copyright (c) 2011-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 __MFD_WCD9XXX_PDATA_H__ + +#define __MFD_WCD9XXX_PDATA_H__ + +#include +#include "msm-cdc-supply.h" + +#define MICBIAS_EXT_BYP_CAP 0x00 +#define MICBIAS_NO_EXT_BYP_CAP 0x01 + +#define SITAR_LDOH_1P95_V 0x0 +#define SITAR_LDOH_2P35_V 0x1 +#define SITAR_LDOH_2P75_V 0x2 +#define SITAR_LDOH_2P85_V 0x3 + +#define SITAR_CFILT1_SEL 0x0 +#define SITAR_CFILT2_SEL 0x1 +#define SITAR_CFILT3_SEL 0x2 + +#define WCD9XXX_LDOH_1P95_V 0x0 +#define WCD9XXX_LDOH_2P35_V 0x1 +#define WCD9XXX_LDOH_2P75_V 0x2 +#define WCD9XXX_LDOH_2P85_V 0x3 +#define WCD9XXX_LDOH_3P0_V 0x3 + +#define TABLA_LDOH_1P95_V 0x0 +#define TABLA_LDOH_2P35_V 0x1 +#define TABLA_LDOH_2P75_V 0x2 +#define TABLA_LDOH_2P85_V 0x3 + +#define TABLA_CFILT1_SEL 0x0 +#define TABLA_CFILT2_SEL 0x1 +#define TABLA_CFILT3_SEL 0x2 + +#define MAX_AMIC_CHANNEL 7 + +#define TABLA_OCP_300_MA 0x0 +#define TABLA_OCP_350_MA 0x2 +#define TABLA_OCP_365_MA 0x3 +#define TABLA_OCP_150_MA 0x4 +#define TABLA_OCP_190_MA 0x6 +#define TABLA_OCP_220_MA 0x7 + +#define TABLA_DCYCLE_255 0x0 +#define TABLA_DCYCLE_511 0x1 +#define TABLA_DCYCLE_767 0x2 +#define TABLA_DCYCLE_1023 0x3 +#define TABLA_DCYCLE_1279 0x4 +#define TABLA_DCYCLE_1535 0x5 +#define TABLA_DCYCLE_1791 0x6 +#define TABLA_DCYCLE_2047 0x7 +#define TABLA_DCYCLE_2303 0x8 +#define TABLA_DCYCLE_2559 0x9 +#define TABLA_DCYCLE_2815 0xA +#define TABLA_DCYCLE_3071 0xB +#define TABLA_DCYCLE_3327 0xC +#define TABLA_DCYCLE_3583 0xD +#define TABLA_DCYCLE_3839 0xE +#define TABLA_DCYCLE_4095 0xF + +#define WCD9XXX_MCLK_CLK_12P288MHZ 12288000 +#define WCD9XXX_MCLK_CLK_9P6HZ 9600000 + +/* Only valid for 9.6 MHz mclk */ +#define WCD9XXX_DMIC_SAMPLE_RATE_600KHZ 600000 +#define WCD9XXX_DMIC_SAMPLE_RATE_2P4MHZ 2400000 +#define WCD9XXX_DMIC_SAMPLE_RATE_3P2MHZ 3200000 +#define WCD9XXX_DMIC_SAMPLE_RATE_4P8MHZ 4800000 + +/* Only valid for 12.288 MHz mclk */ +#define WCD9XXX_DMIC_SAMPLE_RATE_768KHZ 768000 +#define WCD9XXX_DMIC_SAMPLE_RATE_2P048MHZ 2048000 +#define WCD9XXX_DMIC_SAMPLE_RATE_3P072MHZ 3072000 +#define WCD9XXX_DMIC_SAMPLE_RATE_4P096MHZ 4096000 +#define WCD9XXX_DMIC_SAMPLE_RATE_6P144MHZ 6144000 + +#define WCD9XXX_DMIC_SAMPLE_RATE_UNDEFINED 0 + +#define WCD9XXX_DMIC_CLK_DRIVE_UNDEFINED 0 + +struct wcd9xxx_amic { + /*legacy mode, txfe_enable and txfe_buff take 7 input + * each bit represent the channel / TXFE number + * and numbered as below + * bit 0 = channel 1 / TXFE1_ENABLE / TXFE1_BUFF + * bit 1 = channel 2 / TXFE2_ENABLE / TXFE2_BUFF + * ... + * bit 7 = channel 7 / TXFE7_ENABLE / TXFE7_BUFF + */ + u8 legacy_mode:MAX_AMIC_CHANNEL; + u8 txfe_enable:MAX_AMIC_CHANNEL; + u8 txfe_buff:MAX_AMIC_CHANNEL; + u8 use_pdata:MAX_AMIC_CHANNEL; +}; + +/* Each micbias can be assigned to one of three cfilters + * Vbatt_min >= .15V + ldoh_v + * ldoh_v >= .15v + cfiltx_mv + * If ldoh_v = 1.95 160 mv < cfiltx_mv < 1800 mv + * If ldoh_v = 2.35 200 mv < cfiltx_mv < 2200 mv + * If ldoh_v = 2.75 240 mv < cfiltx_mv < 2600 mv + * If ldoh_v = 2.85 250 mv < cfiltx_mv < 2700 mv + */ + +struct wcd9xxx_micbias_setting { + u8 ldoh_v; + u32 cfilt1_mv; /* in mv */ + u32 cfilt2_mv; /* in mv */ + u32 cfilt3_mv; /* in mv */ + u32 micb1_mv; + u32 micb2_mv; + u32 micb3_mv; + u32 micb4_mv; + /* Different WCD9xxx series codecs may not + * have 4 mic biases. If a codec has fewer + * mic biases, some of these properties will + * not be used. + */ + u8 bias1_cfilt_sel; + u8 bias2_cfilt_sel; + u8 bias3_cfilt_sel; + u8 bias4_cfilt_sel; + u8 bias1_cap_mode; + u8 bias2_cap_mode; + u8 bias3_cap_mode; + u8 bias4_cap_mode; + bool bias2_is_headset_only; +}; + +struct wcd9xxx_ocp_setting { + unsigned int use_pdata:1; /* 0 - use sys default as recommended */ + unsigned int num_attempts:4; /* up to 15 attempts */ + unsigned int run_time:4; /* in duty cycle */ + unsigned int wait_time:4; /* in duty cycle */ + unsigned int hph_ocp_limit:3; /* Headphone OCP current limit */ +}; + +#define WCD9XXX_MAX_REGULATOR 9 +/* + * format : TABLA__CUR_MAX + * + * from Tabla objective spec + */ + +#define WCD9XXX_CDC_VDDA_CP_CUR_MAX 500000 +#define WCD9XXX_CDC_VDDA_RX_CUR_MAX 20000 +#define WCD9XXX_CDC_VDDA_TX_CUR_MAX 20000 +#define WCD9XXX_VDDIO_CDC_CUR_MAX 5000 + +#define WCD9XXX_VDDD_CDC_D_CUR_MAX 5000 +#define WCD9XXX_VDDD_CDC_A_CUR_MAX 5000 + +#define WCD9XXX_VDD_SPKDRV_NAME "cdc-vdd-spkdrv" +#define WCD9XXX_VDD_SPKDRV2_NAME "cdc-vdd-spkdrv-2" + +struct wcd9xxx_regulator { + const char *name; + int min_uV; + int max_uV; + int optimum_uA; + bool ondemand; + struct regulator *regulator; +}; + +struct wcd9xxx_pdata { + int irq; + int irq_base; + int num_irqs; + int reset_gpio; + bool has_buck_vsel_gpio; + bool has_micb_supply_en_gpio; + struct device_node *buck_vsel_ctl_np; + struct device_node *micb_en_ctl; + struct device_node *wcd_rst_np; + struct wcd9xxx_amic amic_settings; + struct slim_device slimbus_slave_device; + struct wcd9xxx_micbias_setting micbias; + struct wcd9xxx_ocp_setting ocp; + struct cdc_regulator *regulator; + int num_supplies; + u32 mclk_rate; + u32 dmic_sample_rate; + u32 mad_dmic_sample_rate; + u32 ecpp_dmic_sample_rate; + u32 dmic_clk_drv; + u16 use_pinctrl; +}; + +#endif diff --git a/audio-kernel/asoc/codecs/sdm660_cdc/Android.mk b/audio-kernel/asoc/codecs/sdm660_cdc/Android.mk new file mode 100644 index 000000000..41f3cc7bc --- /dev/null +++ b/audio-kernel/asoc/codecs/sdm660_cdc/Android.mk @@ -0,0 +1,53 @@ +# Android makefile for audio kernel modules + +# Assume no targets will be supported + +AUDIO_CHIPSET := audio +# Build/Package only in case of supported target +ifeq ($(call is-board-platform-in-list,msm8953 sdm670 qcs605),true) + +LOCAL_PATH := $(call my-dir) + +# This makefile is only for DLKM +ifneq ($(findstring vendor,$(LOCAL_PATH)),) + +ifneq ($(findstring opensource,$(LOCAL_PATH)),) + AUDIO_BLD_DIR := $(shell pwd)/vendor/qcom/opensource/audio-kernel +endif # opensource + +DLKM_DIR := $(TOP)/device/qcom/common/dlkm + +# Build audio.ko as $(AUDIO_CHIPSET)_audio.ko +########################################################### +# This is set once per LOCAL_PATH, not per (kernel) module +KBUILD_OPTIONS := AUDIO_ROOT=$(AUDIO_BLD_DIR) + +# We are actually building audio.ko here, as per the +# requirement we are specifying _audio.ko as LOCAL_MODULE. +# This means we need to rename the module to _audio.ko +# after audio.ko is built. +KBUILD_OPTIONS += MODNAME=analog_cdc_dlkm +KBUILD_OPTIONS += BOARD_PLATFORM=$(TARGET_BOARD_PLATFORM) +KBUILD_OPTIONS += $(AUDIO_SELECT) + +########################################################### +include $(CLEAR_VARS) +LOCAL_MODULE := $(AUDIO_CHIPSET)_analog_cdc.ko +LOCAL_MODULE_KBUILD_NAME := analog_cdc_dlkm.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_DEBUG_ENABLE := true +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk +########################################################### +include $(CLEAR_VARS) +LOCAL_MODULE := $(AUDIO_CHIPSET)_digital_cdc.ko +LOCAL_MODULE_KBUILD_NAME := digital_cdc_dlkm.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_DEBUG_ENABLE := true +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk +########################################################### +########################################################### + +endif # DLKM check +endif # supported target check diff --git a/audio-kernel/asoc/codecs/sdm660_cdc/Kbuild b/audio-kernel/asoc/codecs/sdm660_cdc/Kbuild new file mode 100644 index 000000000..de76fab2c --- /dev/null +++ b/audio-kernel/asoc/codecs/sdm660_cdc/Kbuild @@ -0,0 +1,125 @@ +# We can build either as part of a standalone Kernel build or as +# an external module. Determine which mechanism is being used +ifeq ($(MODNAME),) + KERNEL_BUILD := 1 +else + KERNEL_BUILD := 0 +endif + + +ifeq ($(KERNEL_BUILD), 1) + # These are configurable via Kconfig for kernel-based builds + # Need to explicitly configure for Android-based builds + AUDIO_BLD_DIR := $(shell pwd)/kernel/msm-4.9 + AUDIO_ROOT := $(AUDIO_BLD_DIR)/techpack/audio +endif + +ifeq ($(KERNEL_BUILD), 0) + ifeq ($(CONFIG_ARCH_SDM845), y) + include $(AUDIO_ROOT)/config/sdm845auto.conf + export + INCS += -include $(AUDIO_ROOT)/config/sdm845autoconf.h + endif + ifeq ($(CONFIG_ARCH_SDM670), y) + include $(AUDIO_ROOT)/config/sdm670auto.conf + export + INCS += -include $(AUDIO_ROOT)/config/sdm670autoconf.h + endif + ifeq ($(CONFIG_ARCH_SDM450), y) + include $(AUDIO_ROOT)/config/sdm670auto.conf + export + INCS += -include $(AUDIO_ROOT)/config/sdm670autoconf.h + endif +endif + +# As per target team, build is done as follows: +# Defconfig : build with default flags +# Slub : defconfig + CONFIG_SLUB_DEBUG := y + +# CONFIG_SLUB_DEBUG_ON := y + CONFIG_PAGE_POISONING := y +# Perf : Using appropriate msmXXXX-perf_defconfig +# +# Shipment builds (user variants) should not have any debug feature +# enabled. This is identified using 'TARGET_BUILD_VARIANT'. Slub builds +# are identified using the CONFIG_SLUB_DEBUG_ON configuration. Since +# there is no other way to identify defconfig builds, QTI internal +# representation of perf builds (identified using the string 'perf'), +# is used to identify if the build is a slub or defconfig one. This +# way no critical debug feature will be enabled for perf and shipment +# builds. Other OEMs are also protected using the TARGET_BUILD_VARIANT +# config. + +############ UAPI ############ +UAPI_DIR := uapi +UAPI_INC := -I$(AUDIO_ROOT)/include/$(UAPI_DIR) + +############ COMMON ############ +COMMON_DIR := include +COMMON_INC := -I$(AUDIO_ROOT)/$(COMMON_DIR) + +############ SDM660_CDC ############ + +# for SDM660_CDC Codec +ifdef CONFIG_SND_SOC_ANALOG_CDC + ANALOG_CDC_OBJS += msm-analog-cdc.o + ANALOG_CDC_OBJS += sdm660-cdc-irq.o +endif + +ifdef CONFIG_SND_SOC_DIGITAL_CDC + DIGITAL_CDC_OBJS += msm-digital-cdc.o + DIGITAL_CDC_OBJS += msm-digital-cdc-regmap.o +endif +LINUX_INC += -Iinclude/linux + +INCS += $(COMMON_INC) \ + $(UAPI_INC) + +EXTRA_CFLAGS += $(INCS) + + +CDEFINES += -DANI_LITTLE_BYTE_ENDIAN \ + -DANI_LITTLE_BIT_ENDIAN \ + -DDOT11F_LITTLE_ENDIAN_HOST \ + -DANI_COMPILER_TYPE_GCC \ + -DANI_OS_TYPE_ANDROID=6 \ + -DPTT_SOCK_SVC_ENABLE \ + -Wall\ + -Werror\ + -D__linux__ + +KBUILD_CPPFLAGS += $(CDEFINES) + +# Currently, for versions of gcc which support it, the kernel Makefile +# is disabling the maybe-uninitialized warning. Re-enable it for the +# AUDIO driver. Note that we must use EXTRA_CFLAGS here so that it +# will override the kernel settings. +ifeq ($(call cc-option-yn, -Wmaybe-uninitialized),y) +EXTRA_CFLAGS += -Wmaybe-uninitialized +endif +#EXTRA_CFLAGS += -Wmissing-prototypes + +ifeq ($(call cc-option-yn, -Wheader-guard),y) +EXTRA_CFLAGS += -Wheader-guard +endif + + +ifeq ($(KERNEL_BUILD), 0) +KBUILD_EXTRA_SYMBOLS +=$(OUT)/obj/vendor/qcom/opensource/audio-kernel/ipc/Module.symvers +KBUILD_EXTRA_SYMBOLS +=$(OUT)/obj/vendor/qcom/opensource/audio-kernel/dsp/Module.symvers +KBUILD_EXTRA_SYMBOLS +=$(OUT)/obj/vendor/qcom/opensource/audio-kernel/asoc/Module.symvers +KBUILD_EXTRA_SYMBOLS +=$(OUT)/obj/vendor/qcom/opensource/audio-kernel/asoc/codecs/Module.symvers +KBUILD_EXTRA_SYMBOLS +=$(OUT)/obj/vendor/qcom/opensource/audio-kernel/soc/Module.symvers +endif + +ifeq ($(CONFIG_SND_SOC_GCOV), y) +GCOV_PROFILE := y +endif + +# Module information used by KBuild framework +obj-$(CONFIG_SND_SOC_ANALOG_CDC) += analog_cdc_dlkm.o +analog_cdc_dlkm-y := $(ANALOG_CDC_OBJS) + +obj-$(CONFIG_SND_SOC_DIGITAL_CDC) += digital_cdc_dlkm.o +digital_cdc_dlkm-y := $(DIGITAL_CDC_OBJS) + +# inject some build related information +DEFINES += -DBUILD_TIMESTAMP=\"$(shell date -u +'%Y-%m-%dT%H:%M:%SZ')\" diff --git a/audio-kernel/asoc/codecs/sdm660_cdc/msm-analog-cdc-regmap.h b/audio-kernel/asoc/codecs/sdm660_cdc/msm-analog-cdc-regmap.h new file mode 100644 index 000000000..55846a1a2 --- /dev/null +++ b/audio-kernel/asoc/codecs/sdm660_cdc/msm-analog-cdc-regmap.h @@ -0,0 +1,186 @@ +/* + * 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 + * 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 ANALOG_CDC_REGMAP_H +#define ANALOG_CDC_REGMAP_H + +#include +#include "sdm660-cdc-registers.h" + +/* + * Default register reset values that are common across different versions + * are defined here. If a register reset value is changed based on version + * then remove it from this structure and add it in version specific + * structures. + */ + +struct reg_default + msm89xx_pmic_cdc_defaults[MSM89XX_PMIC_CDC_CACHE_SIZE] = { + {MSM89XX_PMIC_DIGITAL_REVISION1, 0x00}, + {MSM89XX_PMIC_DIGITAL_REVISION2, 0x00}, + {MSM89XX_PMIC_DIGITAL_PERPH_TYPE, 0x23}, + {MSM89XX_PMIC_DIGITAL_PERPH_SUBTYPE, 0x01}, + {MSM89XX_PMIC_DIGITAL_INT_RT_STS, 0x00}, + {MSM89XX_PMIC_DIGITAL_INT_SET_TYPE, 0xFF}, + {MSM89XX_PMIC_DIGITAL_INT_POLARITY_HIGH, 0xFF}, + {MSM89XX_PMIC_DIGITAL_INT_POLARITY_LOW, 0x00}, + {MSM89XX_PMIC_DIGITAL_INT_LATCHED_CLR, 0x00}, + {MSM89XX_PMIC_DIGITAL_INT_EN_SET, 0x00}, + {MSM89XX_PMIC_DIGITAL_INT_EN_CLR, 0x00}, + {MSM89XX_PMIC_DIGITAL_INT_LATCHED_STS, 0x00}, + {MSM89XX_PMIC_DIGITAL_INT_PENDING_STS, 0x00}, + {MSM89XX_PMIC_DIGITAL_INT_MID_SEL, 0x00}, + {MSM89XX_PMIC_DIGITAL_INT_PRIORITY, 0x00}, + {MSM89XX_PMIC_DIGITAL_GPIO_MODE, 0x00}, + {MSM89XX_PMIC_DIGITAL_PIN_CTL_OE, 0x01}, + {MSM89XX_PMIC_DIGITAL_PIN_CTL_DATA, 0x00}, + {MSM89XX_PMIC_DIGITAL_PIN_STATUS, 0x00}, + {MSM89XX_PMIC_DIGITAL_HDRIVE_CTL, 0x00}, + {MSM89XX_PMIC_DIGITAL_CDC_RST_CTL, 0x00}, + {MSM89XX_PMIC_DIGITAL_CDC_TOP_CLK_CTL, 0x00}, + {MSM89XX_PMIC_DIGITAL_CDC_ANA_CLK_CTL, 0x00}, + {MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL, 0x00}, + {MSM89XX_PMIC_DIGITAL_CDC_CONN_TX1_CTL, 0x02}, + {MSM89XX_PMIC_DIGITAL_CDC_CONN_TX2_CTL, 0x02}, + {MSM89XX_PMIC_DIGITAL_CDC_CONN_HPHR_DAC_CTL, 0x00}, + {MSM89XX_PMIC_DIGITAL_CDC_CONN_RX1_CTL, 0x00}, + {MSM89XX_PMIC_DIGITAL_CDC_CONN_RX2_CTL, 0x00}, + {MSM89XX_PMIC_DIGITAL_CDC_CONN_RX3_CTL, 0x00}, + {MSM89XX_PMIC_DIGITAL_CDC_CONN_RX_LB_CTL, 0x00}, + {MSM89XX_PMIC_DIGITAL_CDC_RX_CTL1, 0x7C}, + {MSM89XX_PMIC_DIGITAL_CDC_RX_CTL2, 0x7C}, + {MSM89XX_PMIC_DIGITAL_CDC_RX_CTL3, 0x7C}, + {MSM89XX_PMIC_DIGITAL_DEM_BYPASS_DATA0, 0x00}, + {MSM89XX_PMIC_DIGITAL_DEM_BYPASS_DATA1, 0x00}, + {MSM89XX_PMIC_DIGITAL_DEM_BYPASS_DATA2, 0x00}, + {MSM89XX_PMIC_DIGITAL_DEM_BYPASS_DATA3, 0x00}, + {MSM89XX_PMIC_DIGITAL_DIG_DEBUG_CTL, 0x00}, + {MSM89XX_PMIC_DIGITAL_DIG_DEBUG_EN, 0x00}, + {MSM89XX_PMIC_DIGITAL_SPARE_0, 0x00}, + {MSM89XX_PMIC_DIGITAL_SPARE_1, 0x00}, + {MSM89XX_PMIC_DIGITAL_SPARE_2, 0x00}, + {MSM89XX_PMIC_DIGITAL_SEC_ACCESS, 0x00}, + {MSM89XX_PMIC_DIGITAL_PERPH_RESET_CTL1, 0x00}, + {MSM89XX_PMIC_DIGITAL_PERPH_RESET_CTL2, 0x02}, + {MSM89XX_PMIC_DIGITAL_PERPH_RESET_CTL3, 0x05}, + {MSM89XX_PMIC_DIGITAL_PERPH_RESET_CTL4, 0x00}, + {MSM89XX_PMIC_DIGITAL_INT_TEST1, 0x00}, + {MSM89XX_PMIC_DIGITAL_INT_TEST_VAL, 0x00}, + {MSM89XX_PMIC_DIGITAL_TRIM_NUM, 0x00}, + {MSM89XX_PMIC_DIGITAL_TRIM_CTRL, 0x00}, + {MSM89XX_PMIC_ANALOG_REVISION1, 0x00}, + {MSM89XX_PMIC_ANALOG_REVISION2, 0x00}, + {MSM89XX_PMIC_ANALOG_REVISION3, 0x00}, + {MSM89XX_PMIC_ANALOG_REVISION4, 0x00}, + {MSM89XX_PMIC_ANALOG_PERPH_TYPE, 0x23}, + {MSM89XX_PMIC_ANALOG_PERPH_SUBTYPE, 0x09}, + {MSM89XX_PMIC_ANALOG_INT_RT_STS, 0x00}, + {MSM89XX_PMIC_ANALOG_INT_SET_TYPE, 0x3F}, + {MSM89XX_PMIC_ANALOG_INT_POLARITY_HIGH, 0x3F}, + {MSM89XX_PMIC_ANALOG_INT_POLARITY_LOW, 0x00}, + {MSM89XX_PMIC_ANALOG_INT_LATCHED_CLR, 0x00}, + {MSM89XX_PMIC_ANALOG_INT_EN_SET, 0x00}, + {MSM89XX_PMIC_ANALOG_INT_EN_CLR, 0x00}, + {MSM89XX_PMIC_ANALOG_INT_LATCHED_STS, 0x00}, + {MSM89XX_PMIC_ANALOG_INT_PENDING_STS, 0x00}, + {MSM89XX_PMIC_ANALOG_INT_MID_SEL, 0x00}, + {MSM89XX_PMIC_ANALOG_INT_PRIORITY, 0x00}, + {MSM89XX_PMIC_ANALOG_MICB_1_EN, 0x00}, + {MSM89XX_PMIC_ANALOG_MICB_1_VAL, 0x20}, + {MSM89XX_PMIC_ANALOG_MICB_1_CTL, 0x00}, + {MSM89XX_PMIC_ANALOG_MICB_1_INT_RBIAS, 0x49}, + {MSM89XX_PMIC_ANALOG_MICB_2_EN, 0x20}, + {MSM89XX_PMIC_ANALOG_TX_1_2_ATEST_CTL_2, 0x00}, + {MSM89XX_PMIC_ANALOG_MASTER_BIAS_CTL, 0x00}, + {MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_1, 0x35}, + {MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_2, 0x08}, + {MSM89XX_PMIC_ANALOG_MBHC_FSM_CTL, 0x00}, + {MSM89XX_PMIC_ANALOG_MBHC_DBNC_TIMER, 0x98}, + {MSM89XX_PMIC_ANALOG_MBHC_BTN0_ZDETL_CTL, 0x00}, + {MSM89XX_PMIC_ANALOG_MBHC_BTN1_ZDETM_CTL, 0x20}, + {MSM89XX_PMIC_ANALOG_MBHC_BTN2_ZDETH_CTL, 0x40}, + {MSM89XX_PMIC_ANALOG_MBHC_BTN3_CTL, 0x61}, + {MSM89XX_PMIC_ANALOG_MBHC_BTN4_CTL, 0x80}, + {MSM89XX_PMIC_ANALOG_MBHC_BTN_RESULT, 0x00}, + {MSM89XX_PMIC_ANALOG_MBHC_ZDET_ELECT_RESULT, 0x00}, + {MSM89XX_PMIC_ANALOG_TX_1_EN, 0x03}, + {MSM89XX_PMIC_ANALOG_TX_2_EN, 0x03}, + {MSM89XX_PMIC_ANALOG_TX_1_2_TEST_CTL_1, 0xBF}, + {MSM89XX_PMIC_ANALOG_TX_1_2_TEST_CTL_2, 0x8C}, + {MSM89XX_PMIC_ANALOG_TX_1_2_ATEST_CTL, 0x00}, + {MSM89XX_PMIC_ANALOG_TX_1_2_OPAMP_BIAS, 0x6B}, + {MSM89XX_PMIC_ANALOG_TX_1_2_TXFE_CLKDIV, 0x51}, + {MSM89XX_PMIC_ANALOG_TX_3_EN, 0x02}, + {MSM89XX_PMIC_ANALOG_NCP_EN, 0x26}, + {MSM89XX_PMIC_ANALOG_NCP_CLK, 0x23}, + {MSM89XX_PMIC_ANALOG_NCP_DEGLITCH, 0x5B}, + {MSM89XX_PMIC_ANALOG_NCP_FBCTRL, 0x08}, + {MSM89XX_PMIC_ANALOG_NCP_BIAS, 0x29}, + {MSM89XX_PMIC_ANALOG_NCP_VCTRL, 0x24}, + {MSM89XX_PMIC_ANALOG_NCP_TEST, 0x00}, + {MSM89XX_PMIC_ANALOG_NCP_CLIM_ADDR, 0xD5}, + {MSM89XX_PMIC_ANALOG_RX_CLOCK_DIVIDER, 0xE8}, + {MSM89XX_PMIC_ANALOG_RX_COM_OCP_CTL, 0xCF}, + {MSM89XX_PMIC_ANALOG_RX_COM_OCP_COUNT, 0x6E}, + {MSM89XX_PMIC_ANALOG_RX_COM_BIAS_DAC, 0x18}, + {MSM89XX_PMIC_ANALOG_RX_HPH_BIAS_PA, 0x5A}, + {MSM89XX_PMIC_ANALOG_RX_HPH_BIAS_LDO_OCP, 0x69}, + {MSM89XX_PMIC_ANALOG_RX_HPH_BIAS_CNP, 0x29}, + {MSM89XX_PMIC_ANALOG_RX_HPH_CNP_EN, 0x80}, + {MSM89XX_PMIC_ANALOG_RX_HPH_CNP_WG_CTL, 0xDA}, + {MSM89XX_PMIC_ANALOG_RX_HPH_CNP_WG_TIME, 0x16}, + {MSM89XX_PMIC_ANALOG_RX_HPH_L_TEST, 0x00}, + {MSM89XX_PMIC_ANALOG_RX_HPH_L_PA_DAC_CTL, 0x20}, + {MSM89XX_PMIC_ANALOG_RX_HPH_R_TEST, 0x00}, + {MSM89XX_PMIC_ANALOG_RX_HPH_R_PA_DAC_CTL, 0x20}, + {MSM89XX_PMIC_ANALOG_RX_EAR_CTL, 0x12}, + {MSM89XX_PMIC_ANALOG_RX_ATEST, 0x00}, + {MSM89XX_PMIC_ANALOG_RX_HPH_STATUS, 0x0C}, + {MSM89XX_PMIC_ANALOG_RX_EAR_STATUS, 0x00}, + {MSM89XX_PMIC_ANALOG_RX_LO_DAC_CTL, 0x00}, + {MSM89XX_PMIC_ANALOG_RX_LO_EN_CTL, 0x00}, + {MSM89XX_PMIC_ANALOG_SPKR_DAC_CTL, 0x83}, + {MSM89XX_PMIC_ANALOG_SPKR_DRV_CLIP_DET, 0x91}, + {MSM89XX_PMIC_ANALOG_SPKR_DRV_CTL, 0x29}, + {MSM89XX_PMIC_ANALOG_SPKR_ANA_BIAS_SET, 0x4D}, + {MSM89XX_PMIC_ANALOG_SPKR_OCP_CTL, 0xE1}, + {MSM89XX_PMIC_ANALOG_SPKR_PWRSTG_CTL, 0x1E}, + {MSM89XX_PMIC_ANALOG_SPKR_DRV_MISC, 0xCB}, + {MSM89XX_PMIC_ANALOG_SPKR_DRV_DBG, 0x00}, + {MSM89XX_PMIC_ANALOG_CURRENT_LIMIT, 0x02}, + {MSM89XX_PMIC_ANALOG_OUTPUT_VOLTAGE, 0x14}, + {MSM89XX_PMIC_ANALOG_BYPASS_MODE, 0x00}, + {MSM89XX_PMIC_ANALOG_BOOST_EN_CTL, 0x1F}, + {MSM89XX_PMIC_ANALOG_SLOPE_COMP_IP_ZERO, 0x8C}, + {MSM89XX_PMIC_ANALOG_RDSON_MAX_DUTY_CYCLE, 0xC0}, + {MSM89XX_PMIC_ANALOG_BOOST_TEST1_1, 0x00}, + {MSM89XX_PMIC_ANALOG_BOOST_TEST_2, 0x00}, + {MSM89XX_PMIC_ANALOG_SPKR_SAR_STATUS, 0x00}, + {MSM89XX_PMIC_ANALOG_SPKR_DRV_STATUS, 0x00}, + {MSM89XX_PMIC_ANALOG_PBUS_ADD_CSR, 0x00}, + {MSM89XX_PMIC_ANALOG_PBUS_ADD_SEL, 0x00}, + {MSM89XX_PMIC_ANALOG_SEC_ACCESS, 0x00}, + {MSM89XX_PMIC_ANALOG_PERPH_RESET_CTL1, 0x00}, + {MSM89XX_PMIC_ANALOG_PERPH_RESET_CTL2, 0x01}, + {MSM89XX_PMIC_ANALOG_PERPH_RESET_CTL3, 0x05}, + {MSM89XX_PMIC_ANALOG_PERPH_RESET_CTL4, 0x00}, + {MSM89XX_PMIC_ANALOG_INT_TEST1, 0x00}, + {MSM89XX_PMIC_ANALOG_INT_TEST_VAL, 0x00}, + {MSM89XX_PMIC_ANALOG_TRIM_NUM, 0x04}, + {MSM89XX_PMIC_ANALOG_TRIM_CTRL1, 0x00}, + {MSM89XX_PMIC_ANALOG_TRIM_CTRL2, 0x00}, + {MSM89XX_PMIC_ANALOG_TRIM_CTRL3, 0x00}, + {MSM89XX_PMIC_ANALOG_TRIM_CTRL4, 0x00}, +}; + +#endif diff --git a/audio-kernel/asoc/codecs/sdm660_cdc/msm-analog-cdc.c b/audio-kernel/asoc/codecs/sdm660_cdc/msm-analog-cdc.c new file mode 100644 index 000000000..64b99561b --- /dev/null +++ b/audio-kernel/asoc/codecs/sdm660_cdc/msm-analog-cdc.c @@ -0,0 +1,4665 @@ +/* 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 + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "msm-analog-cdc.h" +#include "msm-cdc-common.h" +#include "sdm660-cdc-irq.h" +#include "msm-analog-cdc-regmap.h" +#include "../../sdm660-common.h" +#include "../wcd-mbhc-v2-api.h" + +#define DRV_NAME "pmic_analog_codec" +#define SDM660_CDC_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\ + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\ + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |\ + SNDRV_PCM_RATE_192000) +#define SDM660_CDC_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_3LE) +#define MSM_DIG_CDC_STRING_LEN 80 +#define MSM_ANLG_CDC_VERSION_ENTRY_SIZE 32 + +#define CODEC_DT_MAX_PROP_SIZE 40 +#define MAX_ON_DEMAND_SUPPLY_NAME_LENGTH 64 +#define BUS_DOWN 1 + +/* + * 200 Milliseconds sufficient for DSP bring up in the lpass + * after Sub System Restart + */ +#define ADSP_STATE_READY_TIMEOUT_MS 200 + +#define EAR_PMD 0 +#define EAR_PMU 1 +#define SPK_PMD 2 +#define SPK_PMU 3 + +#define MICBIAS_DEFAULT_VAL 1800000 +#define MICBIAS_MIN_VAL 1600000 +#define MICBIAS_STEP_SIZE 50000 + +#define DEFAULT_BOOST_VOLTAGE 5000 +#define MIN_BOOST_VOLTAGE 4000 +#define MAX_BOOST_VOLTAGE 5550 +#define BOOST_VOLTAGE_STEP 50 + +#define SDM660_CDC_MBHC_BTN_COARSE_ADJ 100 /* in mV */ +#define SDM660_CDC_MBHC_BTN_FINE_ADJ 12 /* in mV */ + +#define VOLTAGE_CONVERTER(value, min_value, step_size)\ + ((value - min_value)/step_size) + +enum { + BOOST_SWITCH = 0, + BOOST_ALWAYS, + BYPASS_ALWAYS, + BOOST_ON_FOREVER, +}; + +static const DECLARE_TLV_DB_SCALE(analog_gain, 0, 25, 1); +static struct snd_soc_dai_driver msm_anlg_cdc_i2s_dai[]; +/* By default enable the internal speaker boost */ +static bool spkr_boost_en = true; + +static char on_demand_supply_name[][MAX_ON_DEMAND_SUPPLY_NAME_LENGTH] = { + "cdc-vdd-mic-bias", +}; + +static struct wcd_mbhc_register + wcd_mbhc_registers[WCD_MBHC_REG_FUNC_MAX] = { + WCD_MBHC_REGISTER("WCD_MBHC_L_DET_EN", + MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_1, 0x80, 7, 0), + WCD_MBHC_REGISTER("WCD_MBHC_GND_DET_EN", + MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_1, 0x40, 6, 0), + WCD_MBHC_REGISTER("WCD_MBHC_MECH_DETECTION_TYPE", + MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_1, 0x20, 5, 0), + WCD_MBHC_REGISTER("WCD_MBHC_MIC_CLAMP_CTL", + MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_1, 0x18, 3, 0), + WCD_MBHC_REGISTER("WCD_MBHC_ELECT_DETECTION_TYPE", + MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_2, 0x01, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HS_L_DET_PULL_UP_CTRL", + MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_2, 0xC0, 6, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HS_L_DET_PULL_UP_COMP_CTRL", + MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_2, 0x20, 5, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPHL_PLUG_TYPE", + MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_2, 0x10, 4, 0), + WCD_MBHC_REGISTER("WCD_MBHC_GND_PLUG_TYPE", + MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_2, 0x08, 3, 0), + WCD_MBHC_REGISTER("WCD_MBHC_SW_HPH_LP_100K_TO_GND", + MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_2, 0x01, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_ELECT_SCHMT_ISRC", + MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_2, 0x06, 1, 0), + WCD_MBHC_REGISTER("WCD_MBHC_FSM_EN", + MSM89XX_PMIC_ANALOG_MBHC_FSM_CTL, 0x80, 7, 0), + WCD_MBHC_REGISTER("WCD_MBHC_INSREM_DBNC", + MSM89XX_PMIC_ANALOG_MBHC_DBNC_TIMER, 0xF0, 4, 0), + WCD_MBHC_REGISTER("WCD_MBHC_BTN_DBNC", + MSM89XX_PMIC_ANALOG_MBHC_DBNC_TIMER, 0x0C, 2, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HS_VREF", + MSM89XX_PMIC_ANALOG_MBHC_BTN3_CTL, 0x03, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HS_COMP_RESULT", + MSM89XX_PMIC_ANALOG_MBHC_ZDET_ELECT_RESULT, 0x01, + 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_MIC_SCHMT_RESULT", + MSM89XX_PMIC_ANALOG_MBHC_ZDET_ELECT_RESULT, 0x02, + 1, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPHL_SCHMT_RESULT", + MSM89XX_PMIC_ANALOG_MBHC_ZDET_ELECT_RESULT, 0x08, + 3, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPHR_SCHMT_RESULT", + MSM89XX_PMIC_ANALOG_MBHC_ZDET_ELECT_RESULT, 0x04, + 2, 0), + WCD_MBHC_REGISTER("WCD_MBHC_OCP_FSM_EN", + MSM89XX_PMIC_ANALOG_RX_COM_OCP_CTL, 0x10, 4, 0), + WCD_MBHC_REGISTER("WCD_MBHC_BTN_RESULT", + MSM89XX_PMIC_ANALOG_MBHC_BTN_RESULT, 0xFF, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_BTN_ISRC_CTL", + MSM89XX_PMIC_ANALOG_MBHC_FSM_CTL, 0x70, 4, 0), + WCD_MBHC_REGISTER("WCD_MBHC_ELECT_RESULT", + MSM89XX_PMIC_ANALOG_MBHC_ZDET_ELECT_RESULT, 0xFF, + 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_MICB_CTRL", + MSM89XX_PMIC_ANALOG_MICB_2_EN, 0xC0, 6, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPH_CNP_WG_TIME", + MSM89XX_PMIC_ANALOG_RX_HPH_CNP_WG_TIME, 0xFC, 2, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPHR_PA_EN", + MSM89XX_PMIC_ANALOG_RX_HPH_CNP_EN, 0x10, 4, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPHL_PA_EN", + MSM89XX_PMIC_ANALOG_RX_HPH_CNP_EN, 0x20, 5, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPH_PA_EN", + MSM89XX_PMIC_ANALOG_RX_HPH_CNP_EN, 0x30, 4, 0), + WCD_MBHC_REGISTER("WCD_MBHC_SWCH_LEVEL_REMOVE", + MSM89XX_PMIC_ANALOG_MBHC_ZDET_ELECT_RESULT, + 0x10, 4, 0), + WCD_MBHC_REGISTER("WCD_MBHC_PULLDOWN_CTRL", + MSM89XX_PMIC_ANALOG_MICB_2_EN, 0x20, 5, 0), + WCD_MBHC_REGISTER("WCD_MBHC_ANC_DET_EN", 0, 0, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_FSM_STATUS", 0, 0, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_MUX_CTL", 0, 0, 0, 0), +}; + +/* Multiply gain_adj and offset by 1000 and 100 to avoid float arithmetic */ +static const struct wcd_imped_i_ref imped_i_ref[] = { + {I_h4_UA, 8, 800, 9000, 10000}, + {I_pt5_UA, 10, 100, 990, 4600}, + {I_14_UA, 17, 14, 1050, 700}, + {I_l4_UA, 10, 4, 1165, 110}, + {I_1_UA, 0, 1, 1200, 65}, +}; + +static const struct wcd_mbhc_intr intr_ids = { + .mbhc_sw_intr = MSM89XX_IRQ_MBHC_HS_DET, + .mbhc_btn_press_intr = MSM89XX_IRQ_MBHC_PRESS, + .mbhc_btn_release_intr = MSM89XX_IRQ_MBHC_RELEASE, + .mbhc_hs_ins_intr = MSM89XX_IRQ_MBHC_INSREM_DET1, + .mbhc_hs_rem_intr = MSM89XX_IRQ_MBHC_INSREM_DET, + .hph_left_ocp = MSM89XX_IRQ_HPHL_OCP, + .hph_right_ocp = MSM89XX_IRQ_HPHR_OCP, +}; + +static int msm_anlg_cdc_dt_parse_vreg_info(struct device *dev, + struct sdm660_cdc_regulator *vreg, + const char *vreg_name, + bool ondemand); +static struct sdm660_cdc_pdata *msm_anlg_cdc_populate_dt_pdata( + struct device *dev); +static int msm_anlg_cdc_enable_ext_mb_source(struct wcd_mbhc *wcd_mbhc, + bool turn_on); +static void msm_anlg_cdc_trim_btn_reg(struct snd_soc_codec *codec); +static void msm_anlg_cdc_set_micb_v(struct snd_soc_codec *codec); +static void msm_anlg_cdc_set_boost_v(struct snd_soc_codec *codec); +static void msm_anlg_cdc_set_auto_zeroing(struct snd_soc_codec *codec, + bool enable); +static void msm_anlg_cdc_configure_cap(struct snd_soc_codec *codec, + bool micbias1, bool micbias2); +static bool msm_anlg_cdc_use_mb(struct snd_soc_codec *codec); + +static int get_codec_version(struct sdm660_cdc_priv *sdm660_cdc) +{ + if (sdm660_cdc->codec_version == DRAX_CDC) + return DRAX_CDC; + else if (sdm660_cdc->codec_version == DIANGU) + return DIANGU; + else if (sdm660_cdc->codec_version == CAJON_2_0) + return CAJON_2_0; + else if (sdm660_cdc->codec_version == CAJON) + return CAJON; + else if (sdm660_cdc->codec_version == CONGA) + return CONGA; + else if (sdm660_cdc->pmic_rev == TOMBAK_2_0) + return TOMBAK_2_0; + else if (sdm660_cdc->pmic_rev == TOMBAK_1_0) + return TOMBAK_1_0; + + pr_err("%s: unsupported codec version\n", __func__); + return UNSUPPORTED; +} + +static void wcd_mbhc_meas_imped(struct snd_soc_codec *codec, + s16 *impedance_l, s16 *impedance_r) +{ + struct sdm660_cdc_priv *sdm660_cdc = + snd_soc_codec_get_drvdata(codec); + + if ((sdm660_cdc->imped_det_pin == WCD_MBHC_DET_BOTH) || + (sdm660_cdc->imped_det_pin == WCD_MBHC_DET_HPHL)) { + /* Enable ZDET_L_MEAS_EN */ + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_MBHC_FSM_CTL, + 0x08, 0x08); + /* Wait for 2ms for measurement to complete */ + usleep_range(2000, 2100); + /* Read Left impedance value from Result1 */ + *impedance_l = snd_soc_read(codec, + MSM89XX_PMIC_ANALOG_MBHC_BTN_RESULT); + /* Enable ZDET_R_MEAS_EN */ + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_MBHC_FSM_CTL, + 0x08, 0x00); + } + if ((sdm660_cdc->imped_det_pin == WCD_MBHC_DET_BOTH) || + (sdm660_cdc->imped_det_pin == WCD_MBHC_DET_HPHR)) { + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_MBHC_FSM_CTL, + 0x04, 0x04); + /* Wait for 2ms for measurement to complete */ + usleep_range(2000, 2100); + /* Read Right impedance value from Result1 */ + *impedance_r = snd_soc_read(codec, + MSM89XX_PMIC_ANALOG_MBHC_BTN_RESULT); + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_MBHC_FSM_CTL, + 0x04, 0x00); + } +} + +static void msm_anlg_cdc_set_ref_current(struct snd_soc_codec *codec, + enum wcd_curr_ref curr_ref) +{ + struct sdm660_cdc_priv *sdm660_cdc = + snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, "%s: curr_ref: %d\n", __func__, curr_ref); + + if (get_codec_version(sdm660_cdc) < CAJON) + dev_dbg(codec->dev, "%s: Setting ref current not required\n", + __func__); + + sdm660_cdc->imped_i_ref = imped_i_ref[curr_ref]; + + switch (curr_ref) { + case I_h4_UA: + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_MICB_2_EN, + 0x07, 0x01); + break; + case I_pt5_UA: + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_MICB_2_EN, + 0x07, 0x04); + break; + case I_14_UA: + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_MICB_2_EN, + 0x07, 0x03); + break; + case I_l4_UA: + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_MICB_2_EN, + 0x07, 0x01); + break; + case I_1_UA: + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_MICB_2_EN, + 0x07, 0x00); + break; + default: + pr_debug("%s: No ref current set\n", __func__); + break; + } +} + +static bool msm_anlg_cdc_adj_ref_current(struct snd_soc_codec *codec, + s16 *impedance_l, s16 *impedance_r) +{ + int i = 2; + s16 compare_imp = 0; + struct sdm660_cdc_priv *sdm660_cdc = + snd_soc_codec_get_drvdata(codec); + + if (sdm660_cdc->imped_det_pin == WCD_MBHC_DET_HPHR) + compare_imp = *impedance_r; + else + compare_imp = *impedance_l; + + if (get_codec_version(sdm660_cdc) < CAJON) { + dev_dbg(codec->dev, + "%s: Reference current adjustment not required\n", + __func__); + return false; + } + + while (compare_imp < imped_i_ref[i].min_val) { + msm_anlg_cdc_set_ref_current(codec, imped_i_ref[++i].curr_ref); + wcd_mbhc_meas_imped(codec, impedance_l, impedance_r); + compare_imp = (sdm660_cdc->imped_det_pin == + WCD_MBHC_DET_HPHR) ? *impedance_r : *impedance_l; + if (i >= I_1_UA) + break; + } + return true; +} + +void msm_anlg_cdc_spk_ext_pa_cb( + int (*codec_spk_ext_pa)(struct snd_soc_codec *codec, + int enable), struct snd_soc_codec *codec) +{ + struct sdm660_cdc_priv *sdm660_cdc; + + if (!codec) { + pr_err("%s: NULL codec pointer!\n", __func__); + return; + } + + sdm660_cdc = snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, "%s: Enter\n", __func__); + sdm660_cdc->codec_spk_ext_pa_cb = codec_spk_ext_pa; +} +EXPORT_SYMBOL(msm_anlg_cdc_spk_ext_pa_cb); + +static void msm_anlg_cdc_compute_impedance(struct snd_soc_codec *codec, s16 l, + s16 r, uint32_t *zl, uint32_t *zr, + bool high) +{ + struct sdm660_cdc_priv *sdm660_cdc = + snd_soc_codec_get_drvdata(codec); + uint32_t rl = 0, rr = 0; + struct wcd_imped_i_ref R = sdm660_cdc->imped_i_ref; + int codec_ver = get_codec_version(sdm660_cdc); + + switch (codec_ver) { + case TOMBAK_1_0: + case TOMBAK_2_0: + case CONGA: + if (high) { + dev_dbg(codec->dev, + "%s: This plug has high range impedance\n", + __func__); + rl = (uint32_t)(((100 * (l * 400 - 200))/96) - 230); + rr = (uint32_t)(((100 * (r * 400 - 200))/96) - 230); + } else { + dev_dbg(codec->dev, + "%s: This plug has low range impedance\n", + __func__); + rl = (uint32_t)(((1000 * (l * 2 - 1))/1165) - (13/10)); + rr = (uint32_t)(((1000 * (r * 2 - 1))/1165) - (13/10)); + } + break; + case CAJON: + case CAJON_2_0: + case DIANGU: + case DRAX_CDC: + if (sdm660_cdc->imped_det_pin == WCD_MBHC_DET_HPHL) { + rr = (uint32_t)(((DEFAULT_MULTIPLIER * (10 * r - 5)) - + (DEFAULT_OFFSET * DEFAULT_GAIN))/DEFAULT_GAIN); + rl = (uint32_t)(((10000 * (R.multiplier * (10 * l - 5))) + - R.offset * R.gain_adj)/(R.gain_adj * 100)); + } else if (sdm660_cdc->imped_det_pin == WCD_MBHC_DET_HPHR) { + rr = (uint32_t)(((10000 * (R.multiplier * (10 * r - 5))) + - R.offset * R.gain_adj)/(R.gain_adj * 100)); + rl = (uint32_t)(((DEFAULT_MULTIPLIER * (10 * l - 5))- + (DEFAULT_OFFSET * DEFAULT_GAIN))/DEFAULT_GAIN); + } else if (sdm660_cdc->imped_det_pin == WCD_MBHC_DET_NONE) { + rr = (uint32_t)(((DEFAULT_MULTIPLIER * (10 * r - 5)) - + (DEFAULT_OFFSET * DEFAULT_GAIN))/DEFAULT_GAIN); + rl = (uint32_t)(((DEFAULT_MULTIPLIER * (10 * l - 5))- + (DEFAULT_OFFSET * DEFAULT_GAIN))/DEFAULT_GAIN); + } else { + rr = (uint32_t)(((10000 * (R.multiplier * (10 * r - 5))) + - R.offset * R.gain_adj)/(R.gain_adj * 100)); + rl = (uint32_t)(((10000 * (R.multiplier * (10 * l - 5))) + - R.offset * R.gain_adj)/(R.gain_adj * 100)); + } + break; + default: + dev_dbg(codec->dev, "%s: No codec mentioned\n", __func__); + break; + } + *zl = rl; + *zr = rr; +} + +static struct firmware_cal *msm_anlg_cdc_get_hwdep_fw_cal( + struct wcd_mbhc *wcd_mbhc, + enum wcd_cal_type type) +{ + struct sdm660_cdc_priv *sdm660_cdc; + struct firmware_cal *hwdep_cal; + struct snd_soc_codec *codec = wcd_mbhc->codec; + + if (!codec) { + pr_err("%s: NULL codec pointer\n", __func__); + return NULL; + } + sdm660_cdc = snd_soc_codec_get_drvdata(codec); + hwdep_cal = wcdcal_get_fw_cal(sdm660_cdc->fw_data, type); + if (!hwdep_cal) { + dev_err(codec->dev, "%s: cal not sent by %d\n", + __func__, type); + return NULL; + } + return hwdep_cal; +} + +static void wcd9xxx_spmi_irq_control(struct snd_soc_codec *codec, + int irq, bool enable) +{ + if (enable) + wcd9xxx_spmi_enable_irq(irq); + else + wcd9xxx_spmi_disable_irq(irq); +} + +static void msm_anlg_cdc_mbhc_clk_setup(struct snd_soc_codec *codec, + bool enable) +{ + if (enable) + snd_soc_update_bits(codec, + MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL, + 0x08, 0x08); + else + snd_soc_update_bits(codec, + MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL, + 0x08, 0x00); +} + +static int msm_anlg_cdc_mbhc_map_btn_code_to_num(struct snd_soc_codec *codec) +{ + int btn_code; + int btn; + + btn_code = snd_soc_read(codec, MSM89XX_PMIC_ANALOG_MBHC_BTN_RESULT); + + switch (btn_code) { + case 0: + btn = 0; + break; + case 1: + btn = 1; + break; + case 3: + btn = 2; + break; + case 7: + btn = 3; + break; + case 15: + btn = 4; + break; + default: + btn = -EINVAL; + break; + }; + + return btn; +} + +static bool msm_anlg_cdc_spmi_lock_sleep(struct wcd_mbhc *mbhc, bool lock) +{ + if (lock) + return wcd9xxx_spmi_lock_sleep(); + wcd9xxx_spmi_unlock_sleep(); + return 0; +} + +static bool msm_anlg_cdc_micb_en_status(struct wcd_mbhc *mbhc, int micb_num) +{ + if (micb_num == MIC_BIAS_1) + return (snd_soc_read(mbhc->codec, + MSM89XX_PMIC_ANALOG_MICB_1_EN) & + 0x80); + if (micb_num == MIC_BIAS_2) + return (snd_soc_read(mbhc->codec, + MSM89XX_PMIC_ANALOG_MICB_2_EN) & + 0x80); + return false; +} + +static void msm_anlg_cdc_enable_master_bias(struct snd_soc_codec *codec, + bool enable) +{ + if (enable) + snd_soc_update_bits(codec, MSM89XX_PMIC_ANALOG_MASTER_BIAS_CTL, + 0x30, 0x30); + else + snd_soc_update_bits(codec, MSM89XX_PMIC_ANALOG_MASTER_BIAS_CTL, + 0x30, 0x00); +} + +static void msm_anlg_cdc_mbhc_common_micb_ctrl(struct snd_soc_codec *codec, + int event, bool enable) +{ + u16 reg; + u8 mask; + u8 val; + + switch (event) { + case MBHC_COMMON_MICB_PRECHARGE: + reg = MSM89XX_PMIC_ANALOG_MICB_1_CTL; + mask = 0x60; + val = (enable ? 0x60 : 0x00); + break; + case MBHC_COMMON_MICB_SET_VAL: + reg = MSM89XX_PMIC_ANALOG_MICB_1_VAL; + mask = 0xFF; + val = (enable ? 0xC0 : 0x00); + break; + case MBHC_COMMON_MICB_TAIL_CURR: + reg = MSM89XX_PMIC_ANALOG_MICB_1_EN; + mask = 0x04; + val = (enable ? 0x04 : 0x00); + break; + default: + dev_err(codec->dev, + "%s: Invalid event received\n", __func__); + return; + }; + snd_soc_update_bits(codec, reg, mask, val); +} + +static void msm_anlg_cdc_mbhc_internal_micbias_ctrl(struct snd_soc_codec *codec, + int micbias_num, + bool enable) +{ + if (micbias_num == 1) { + if (enable) + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_MICB_1_INT_RBIAS, + 0x10, 0x10); + else + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_MICB_1_INT_RBIAS, + 0x10, 0x00); + } +} + +static bool msm_anlg_cdc_mbhc_hph_pa_on_status(struct snd_soc_codec *codec) +{ + return (snd_soc_read(codec, MSM89XX_PMIC_ANALOG_RX_HPH_CNP_EN) & + 0x30) ? true : false; +} + +static void msm_anlg_cdc_mbhc_program_btn_thr(struct snd_soc_codec *codec, + s16 *btn_low, s16 *btn_high, + int num_btn, bool is_micbias) +{ + int i; + u32 course, fine, reg_val; + u16 reg_addr = MSM89XX_PMIC_ANALOG_MBHC_BTN0_ZDETL_CTL; + s16 *btn_voltage; + + btn_voltage = ((is_micbias) ? btn_high : btn_low); + + for (i = 0; i < num_btn; i++) { + course = (btn_voltage[i] / SDM660_CDC_MBHC_BTN_COARSE_ADJ); + fine = ((btn_voltage[i] % SDM660_CDC_MBHC_BTN_COARSE_ADJ) / + SDM660_CDC_MBHC_BTN_FINE_ADJ); + + reg_val = (course << 5) | (fine << 2); + snd_soc_update_bits(codec, reg_addr, 0xFC, reg_val); + dev_dbg(codec->dev, + "%s: course: %d fine: %d reg_addr: %x reg_val: %x\n", + __func__, course, fine, reg_addr, reg_val); + reg_addr++; + } +} + +static void msm_anlg_cdc_mbhc_calc_impedance(struct wcd_mbhc *mbhc, + uint32_t *zl, uint32_t *zr) +{ + struct snd_soc_codec *codec = mbhc->codec; + struct sdm660_cdc_priv *sdm660_cdc = + snd_soc_codec_get_drvdata(codec); + s16 impedance_l, impedance_r; + s16 impedance_l_fixed; + s16 reg0, reg1, reg2, reg3, reg4; + bool high = false; + bool min_range_used = false; + + WCD_MBHC_RSC_ASSERT_LOCKED(mbhc); + reg0 = snd_soc_read(codec, MSM89XX_PMIC_ANALOG_MBHC_DBNC_TIMER); + reg1 = snd_soc_read(codec, MSM89XX_PMIC_ANALOG_MBHC_BTN2_ZDETH_CTL); + reg2 = snd_soc_read(codec, MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_2); + reg3 = snd_soc_read(codec, MSM89XX_PMIC_ANALOG_MICB_2_EN); + reg4 = snd_soc_read(codec, MSM89XX_PMIC_ANALOG_MBHC_FSM_CTL); + + sdm660_cdc->imped_det_pin = WCD_MBHC_DET_BOTH; + mbhc->hph_type = WCD_MBHC_HPH_NONE; + + /* disable FSM and micbias and enable pullup*/ + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_MBHC_FSM_CTL, + 0x80, 0x00); + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_MICB_2_EN, + 0xA5, 0x25); + /* + * Enable legacy electrical detection current sources + * and disable fast ramp and enable manual switching + * of extra capacitance + */ + dev_dbg(codec->dev, "%s: Setup for impedance det\n", __func__); + + msm_anlg_cdc_set_ref_current(codec, I_h4_UA); + + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_2, + 0x06, 0x02); + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_MBHC_DBNC_TIMER, + 0x02, 0x02); + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_MBHC_BTN2_ZDETH_CTL, + 0x02, 0x00); + + dev_dbg(codec->dev, "%s: Start performing impedance detection\n", + __func__); + + wcd_mbhc_meas_imped(codec, &impedance_l, &impedance_r); + + if (impedance_l > 2 || impedance_r > 2) { + high = true; + if (!mbhc->mbhc_cfg->mono_stero_detection) { + /* Set ZDET_CHG to 0 to discharge ramp */ + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_MBHC_FSM_CTL, + 0x02, 0x00); + /* wait 40ms for the discharge ramp to complete */ + usleep_range(40000, 40100); + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_MBHC_BTN0_ZDETL_CTL, + 0x03, 0x00); + sdm660_cdc->imped_det_pin = (impedance_l > 2 && + impedance_r > 2) ? + WCD_MBHC_DET_NONE : + ((impedance_l > 2) ? + WCD_MBHC_DET_HPHR : + WCD_MBHC_DET_HPHL); + if (sdm660_cdc->imped_det_pin == WCD_MBHC_DET_NONE) + goto exit; + } else { + if (get_codec_version(sdm660_cdc) >= CAJON) { + if (impedance_l == 63 && impedance_r == 63) { + dev_dbg(codec->dev, + "%s: HPHL and HPHR are floating\n", + __func__); + sdm660_cdc->imped_det_pin = + WCD_MBHC_DET_NONE; + mbhc->hph_type = WCD_MBHC_HPH_NONE; + } else if (impedance_l == 63 + && impedance_r < 63) { + dev_dbg(codec->dev, + "%s: Mono HS with HPHL floating\n", + __func__); + sdm660_cdc->imped_det_pin = + WCD_MBHC_DET_HPHR; + mbhc->hph_type = WCD_MBHC_HPH_MONO; + } else if (impedance_r == 63 && + impedance_l < 63) { + dev_dbg(codec->dev, + "%s: Mono HS with HPHR floating\n", + __func__); + sdm660_cdc->imped_det_pin = + WCD_MBHC_DET_HPHL; + mbhc->hph_type = WCD_MBHC_HPH_MONO; + } else if (impedance_l > 3 && impedance_r > 3 && + (impedance_l == impedance_r)) { + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_2, + 0x06, 0x06); + wcd_mbhc_meas_imped(codec, &impedance_l, + &impedance_r); + if (impedance_r == impedance_l) + dev_dbg(codec->dev, + "%s: Mono Headset\n", + __func__); + sdm660_cdc->imped_det_pin = + WCD_MBHC_DET_NONE; + mbhc->hph_type = + WCD_MBHC_HPH_MONO; + } else { + dev_dbg(codec->dev, + "%s: STEREO headset is found\n", + __func__); + sdm660_cdc->imped_det_pin = + WCD_MBHC_DET_BOTH; + mbhc->hph_type = WCD_MBHC_HPH_STEREO; + } + } + } + } + + msm_anlg_cdc_set_ref_current(codec, I_pt5_UA); + msm_anlg_cdc_set_ref_current(codec, I_14_UA); + + /* Enable RAMP_L , RAMP_R & ZDET_CHG*/ + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_MBHC_BTN0_ZDETL_CTL, + 0x03, 0x03); + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_MBHC_FSM_CTL, + 0x02, 0x02); + /* wait for 50msec for the HW to apply ramp on HPHL and HPHR */ + usleep_range(50000, 50100); + /* Enable ZDET_DISCHG_CAP_CTL to add extra capacitance */ + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_MBHC_FSM_CTL, + 0x01, 0x01); + /* wait for 5msec for the voltage to get stable */ + usleep_range(5000, 5100); + + wcd_mbhc_meas_imped(codec, &impedance_l, &impedance_r); + + min_range_used = msm_anlg_cdc_adj_ref_current(codec, + &impedance_l, &impedance_r); + if (!mbhc->mbhc_cfg->mono_stero_detection) { + /* Set ZDET_CHG to 0 to discharge ramp */ + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_MBHC_FSM_CTL, + 0x02, 0x00); + /* wait for 40msec for the capacitor to discharge */ + usleep_range(40000, 40100); + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_MBHC_BTN0_ZDETL_CTL, + 0x03, 0x00); + goto exit; + } + + /* we are setting ref current to the minimun range or the measured + * value larger than the minimum value, so min_range_used is true. + * If the headset is mono headset with either HPHL or HPHR floating + * then we have already done the mono stereo detection and do not + * need to continue further. + */ + + if (!min_range_used || + sdm660_cdc->imped_det_pin == WCD_MBHC_DET_HPHL || + sdm660_cdc->imped_det_pin == WCD_MBHC_DET_HPHR) + goto exit; + + + /* Disable Set ZDET_CONN_RAMP_L and enable ZDET_CONN_FIXED_L */ + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_MBHC_BTN0_ZDETL_CTL, + 0x02, 0x00); + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_MBHC_BTN1_ZDETM_CTL, + 0x02, 0x02); + /* Set ZDET_CHG to 0 */ + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_MBHC_FSM_CTL, + 0x02, 0x00); + /* wait for 40msec for the capacitor to discharge */ + usleep_range(40000, 40100); + + /* Set ZDET_CONN_RAMP_R to 0 */ + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_MBHC_BTN0_ZDETL_CTL, + 0x01, 0x00); + /* Enable ZDET_L_MEAS_EN */ + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_MBHC_FSM_CTL, + 0x08, 0x08); + /* wait for 2msec for the HW to compute left inpedance value */ + usleep_range(2000, 2100); + /* Read Left impedance value from Result1 */ + impedance_l_fixed = snd_soc_read(codec, + MSM89XX_PMIC_ANALOG_MBHC_BTN_RESULT); + /* Disable ZDET_L_MEAS_EN */ + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_MBHC_FSM_CTL, + 0x08, 0x00); + /* + * Assume impedance_l is L1, impedance_l_fixed is L2. + * If the following condition is met, we can take this + * headset as mono one with impedance of L2. + * Otherwise, take it as stereo with impedance of L1. + * Condition: + * abs[(L2-0.5L1)/(L2+0.5L1)] < abs [(L2-L1)/(L2+L1)] + */ + if ((abs(impedance_l_fixed - impedance_l/2) * + (impedance_l_fixed + impedance_l)) >= + (abs(impedance_l_fixed - impedance_l) * + (impedance_l_fixed + impedance_l/2))) { + dev_dbg(codec->dev, + "%s: STEREO plug type detected\n", + __func__); + mbhc->hph_type = WCD_MBHC_HPH_STEREO; + } else { + dev_dbg(codec->dev, + "%s: MONO plug type detected\n", + __func__); + mbhc->hph_type = WCD_MBHC_HPH_MONO; + impedance_l = impedance_l_fixed; + } + /* Enable ZDET_CHG */ + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_MBHC_FSM_CTL, + 0x02, 0x02); + /* wait for 10msec for the capacitor to charge */ + usleep_range(10000, 10100); + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_MBHC_BTN0_ZDETL_CTL, + 0x02, 0x02); + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_MBHC_BTN1_ZDETM_CTL, + 0x02, 0x00); + /* Set ZDET_CHG to 0 to discharge HPHL */ + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_MBHC_FSM_CTL, + 0x02, 0x00); + /* wait for 40msec for the capacitor to discharge */ + usleep_range(40000, 40100); + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_MBHC_BTN0_ZDETL_CTL, + 0x02, 0x00); + +exit: + snd_soc_write(codec, MSM89XX_PMIC_ANALOG_MBHC_FSM_CTL, reg4); + snd_soc_write(codec, MSM89XX_PMIC_ANALOG_MICB_2_EN, reg3); + snd_soc_write(codec, MSM89XX_PMIC_ANALOG_MBHC_BTN2_ZDETH_CTL, reg1); + snd_soc_write(codec, MSM89XX_PMIC_ANALOG_MBHC_DBNC_TIMER, reg0); + snd_soc_write(codec, MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_2, reg2); + msm_anlg_cdc_compute_impedance(codec, impedance_l, impedance_r, + zl, zr, high); + + dev_dbg(codec->dev, "%s: RL %d ohm, RR %d ohm\n", __func__, *zl, *zr); + dev_dbg(codec->dev, "%s: Impedance detection completed\n", __func__); +} + +static int msm_anlg_cdc_dig_register_notifier(void *handle, + struct notifier_block *nblock, + bool enable) +{ + struct sdm660_cdc_priv *handle_cdc = handle; + + if (enable) + return blocking_notifier_chain_register(&handle_cdc->notifier, + nblock); + + return blocking_notifier_chain_unregister(&handle_cdc->notifier, + nblock); +} + +static int msm_anlg_cdc_mbhc_register_notifier(struct wcd_mbhc *wcd_mbhc, + struct notifier_block *nblock, + bool enable) +{ + struct snd_soc_codec *codec = wcd_mbhc->codec; + struct sdm660_cdc_priv *sdm660_cdc = + snd_soc_codec_get_drvdata(codec); + + if (enable) + return blocking_notifier_chain_register( + &sdm660_cdc->notifier_mbhc, + nblock); + + return blocking_notifier_chain_unregister(&sdm660_cdc->notifier_mbhc, + nblock); +} + +static int msm_anlg_cdc_request_irq(struct snd_soc_codec *codec, + int irq, irq_handler_t handler, + const char *name, void *data) +{ + return wcd9xxx_spmi_request_irq(irq, handler, name, data); +} + +static int msm_anlg_cdc_free_irq(struct snd_soc_codec *codec, + int irq, void *data) +{ + return wcd9xxx_spmi_free_irq(irq, data); +} + +static const struct wcd_mbhc_cb mbhc_cb = { + .enable_mb_source = msm_anlg_cdc_enable_ext_mb_source, + .trim_btn_reg = msm_anlg_cdc_trim_btn_reg, + .compute_impedance = msm_anlg_cdc_mbhc_calc_impedance, + .set_micbias_value = msm_anlg_cdc_set_micb_v, + .set_auto_zeroing = msm_anlg_cdc_set_auto_zeroing, + .get_hwdep_fw_cal = msm_anlg_cdc_get_hwdep_fw_cal, + .set_cap_mode = msm_anlg_cdc_configure_cap, + .register_notifier = msm_anlg_cdc_mbhc_register_notifier, + .request_irq = msm_anlg_cdc_request_irq, + .irq_control = wcd9xxx_spmi_irq_control, + .free_irq = msm_anlg_cdc_free_irq, + .clk_setup = msm_anlg_cdc_mbhc_clk_setup, + .map_btn_code_to_num = msm_anlg_cdc_mbhc_map_btn_code_to_num, + .lock_sleep = msm_anlg_cdc_spmi_lock_sleep, + .micbias_enable_status = msm_anlg_cdc_micb_en_status, + .mbhc_bias = msm_anlg_cdc_enable_master_bias, + .mbhc_common_micb_ctrl = msm_anlg_cdc_mbhc_common_micb_ctrl, + .micb_internal = msm_anlg_cdc_mbhc_internal_micbias_ctrl, + .hph_pa_on_status = msm_anlg_cdc_mbhc_hph_pa_on_status, + .set_btn_thr = msm_anlg_cdc_mbhc_program_btn_thr, + .extn_use_mb = msm_anlg_cdc_use_mb, +}; + +static const uint32_t wcd_imped_val[] = {4, 8, 12, 13, 16, + 20, 24, 28, 32, + 36, 40, 44, 48}; + +static void msm_anlg_cdc_dig_notifier_call(struct snd_soc_codec *codec, + const enum dig_cdc_notify_event event) +{ + struct sdm660_cdc_priv *sdm660_cdc = snd_soc_codec_get_drvdata(codec); + + pr_debug("%s: notifier call event %d\n", __func__, event); + blocking_notifier_call_chain(&sdm660_cdc->notifier, + event, NULL); +} + +static void msm_anlg_cdc_notifier_call(struct snd_soc_codec *codec, + const enum wcd_notify_event event) +{ + struct sdm660_cdc_priv *sdm660_cdc = + snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, "%s: notifier call event %d\n", __func__, event); + blocking_notifier_call_chain(&sdm660_cdc->notifier_mbhc, event, + &sdm660_cdc->mbhc); +} + +static void msm_anlg_cdc_boost_on(struct snd_soc_codec *codec) +{ + struct sdm660_cdc_priv *sdm660_cdc = + snd_soc_codec_get_drvdata(codec); + + snd_soc_update_bits(codec, + MSM89XX_PMIC_DIGITAL_PERPH_RESET_CTL3, 0x0F, 0x0F); + snd_soc_write(codec, MSM89XX_PMIC_ANALOG_SEC_ACCESS, 0xA5); + snd_soc_write(codec, MSM89XX_PMIC_ANALOG_PERPH_RESET_CTL3, 0x0F); + snd_soc_write(codec, MSM89XX_PMIC_ANALOG_MASTER_BIAS_CTL, 0x30); + if (get_codec_version(sdm660_cdc) < CAJON_2_0) + snd_soc_write(codec, MSM89XX_PMIC_ANALOG_CURRENT_LIMIT, 0x82); + else + snd_soc_write(codec, MSM89XX_PMIC_ANALOG_CURRENT_LIMIT, 0xA2); + snd_soc_update_bits(codec, MSM89XX_PMIC_ANALOG_SPKR_DRV_CTL, + 0x69, 0x69); + snd_soc_update_bits(codec, MSM89XX_PMIC_ANALOG_SPKR_DRV_DBG, + 0x01, 0x01); + snd_soc_update_bits(codec, MSM89XX_PMIC_ANALOG_SLOPE_COMP_IP_ZERO, + 0x88, 0x88); + snd_soc_update_bits(codec, MSM89XX_PMIC_ANALOG_SPKR_DAC_CTL, + 0x03, 0x03); + snd_soc_update_bits(codec, MSM89XX_PMIC_ANALOG_SPKR_OCP_CTL, + 0xE1, 0xE1); + if (get_codec_version(sdm660_cdc) < CAJON_2_0) { + snd_soc_update_bits(codec, MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL, + 0x20, 0x20); + /* Wait for 1ms after clock ctl enable */ + usleep_range(CODEC_DELAY_1_MS, CODEC_DELAY_1_1_MS); + snd_soc_update_bits(codec, MSM89XX_PMIC_ANALOG_BOOST_EN_CTL, + 0xDF, 0xDF); + usleep_range(CODEC_DELAY_1_MS, CODEC_DELAY_1_1_MS); + } else { + snd_soc_update_bits(codec, MSM89XX_PMIC_ANALOG_BOOST_EN_CTL, + 0x40, 0x00); + snd_soc_update_bits(codec, MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL, + 0x20, 0x20); + snd_soc_update_bits(codec, MSM89XX_PMIC_ANALOG_BOOST_EN_CTL, + 0x80, 0x80); + /* Wait for 500us after BOOST_EN to happen */ + usleep_range(500, 510); + snd_soc_update_bits(codec, MSM89XX_PMIC_ANALOG_BOOST_EN_CTL, + 0x40, 0x40); + /* Wait for 500us after BOOST pulse_skip */ + usleep_range(500, 510); + } +} + +static void msm_anlg_cdc_boost_off(struct snd_soc_codec *codec) +{ + snd_soc_update_bits(codec, MSM89XX_PMIC_ANALOG_BOOST_EN_CTL, + 0xDF, 0x5F); + snd_soc_update_bits(codec, MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL, + 0x20, 0x00); +} + +static void msm_anlg_cdc_bypass_on(struct snd_soc_codec *codec) +{ + struct sdm660_cdc_priv *sdm660_cdc = + snd_soc_codec_get_drvdata(codec); + + if (get_codec_version(sdm660_cdc) < CAJON_2_0) { + snd_soc_write(codec, + MSM89XX_PMIC_ANALOG_SEC_ACCESS, + 0xA5); + snd_soc_write(codec, + MSM89XX_PMIC_ANALOG_PERPH_RESET_CTL3, + 0x07); + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_BYPASS_MODE, + 0x02, 0x02); + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_BYPASS_MODE, + 0x01, 0x00); + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_BYPASS_MODE, + 0x40, 0x40); + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_BYPASS_MODE, + 0x80, 0x80); + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_BOOST_EN_CTL, + 0xDF, 0xDF); + } else { + snd_soc_update_bits(codec, + MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL, + 0x20, 0x20); + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_BYPASS_MODE, + 0x20, 0x20); + } +} + +static void msm_anlg_cdc_bypass_off(struct snd_soc_codec *codec) +{ + struct sdm660_cdc_priv *sdm660_cdc = + snd_soc_codec_get_drvdata(codec); + + if (get_codec_version(sdm660_cdc) < CAJON_2_0) { + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_BOOST_EN_CTL, + 0x80, 0x00); + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_BYPASS_MODE, + 0x80, 0x00); + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_BYPASS_MODE, + 0x02, 0x00); + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_BYPASS_MODE, + 0x40, 0x00); + } else { + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_BYPASS_MODE, + 0x20, 0x00); + snd_soc_update_bits(codec, + MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL, + 0x20, 0x00); + } +} + +static void msm_anlg_cdc_boost_mode_sequence(struct snd_soc_codec *codec, + int flag) +{ + struct sdm660_cdc_priv *sdm660_cdc = + snd_soc_codec_get_drvdata(codec); + + if (flag == EAR_PMU) { + switch (sdm660_cdc->boost_option) { + case BOOST_SWITCH: + if (sdm660_cdc->ear_pa_boost_set) { + msm_anlg_cdc_boost_off(codec); + msm_anlg_cdc_bypass_on(codec); + } + break; + case BOOST_ALWAYS: + msm_anlg_cdc_boost_on(codec); + break; + case BYPASS_ALWAYS: + msm_anlg_cdc_bypass_on(codec); + break; + case BOOST_ON_FOREVER: + msm_anlg_cdc_boost_on(codec); + break; + default: + dev_err(codec->dev, + "%s: invalid boost option: %d\n", __func__, + sdm660_cdc->boost_option); + break; + } + } else if (flag == EAR_PMD) { + switch (sdm660_cdc->boost_option) { + case BOOST_SWITCH: + if (sdm660_cdc->ear_pa_boost_set) + msm_anlg_cdc_bypass_off(codec); + break; + case BOOST_ALWAYS: + msm_anlg_cdc_boost_off(codec); + /* 80ms for EAR boost to settle down */ + msleep(80); + break; + case BYPASS_ALWAYS: + /* nothing to do as bypass on always */ + break; + case BOOST_ON_FOREVER: + /* nothing to do as boost on forever */ + break; + default: + dev_err(codec->dev, + "%s: invalid boost option: %d\n", __func__, + sdm660_cdc->boost_option); + break; + } + } else if (flag == SPK_PMU) { + switch (sdm660_cdc->boost_option) { + case BOOST_SWITCH: + if (sdm660_cdc->spk_boost_set) { + msm_anlg_cdc_bypass_off(codec); + msm_anlg_cdc_boost_on(codec); + } + break; + case BOOST_ALWAYS: + msm_anlg_cdc_boost_on(codec); + break; + case BYPASS_ALWAYS: + msm_anlg_cdc_bypass_on(codec); + break; + case BOOST_ON_FOREVER: + msm_anlg_cdc_boost_on(codec); + break; + default: + dev_err(codec->dev, + "%s: invalid boost option: %d\n", __func__, + sdm660_cdc->boost_option); + break; + } + } else if (flag == SPK_PMD) { + switch (sdm660_cdc->boost_option) { + case BOOST_SWITCH: + if (sdm660_cdc->spk_boost_set) { + msm_anlg_cdc_boost_off(codec); + /* + * Add 40 ms sleep for the spk + * boost to settle down + */ + msleep(40); + } + break; + case BOOST_ALWAYS: + msm_anlg_cdc_boost_off(codec); + /* + * Add 40 ms sleep for the spk + * boost to settle down + */ + msleep(40); + break; + case BYPASS_ALWAYS: + /* nothing to do as bypass on always */ + break; + case BOOST_ON_FOREVER: + /* nothing to do as boost on forever */ + break; + default: + dev_err(codec->dev, + "%s: invalid boost option: %d\n", __func__, + sdm660_cdc->boost_option); + break; + } + } +} + +static int msm_anlg_cdc_dt_parse_vreg_info(struct device *dev, + struct sdm660_cdc_regulator *vreg, const char *vreg_name, + bool ondemand) +{ + int len, ret = 0; + const __be32 *prop; + char prop_name[CODEC_DT_MAX_PROP_SIZE]; + struct device_node *regnode = NULL; + u32 prop_val; + + snprintf(prop_name, CODEC_DT_MAX_PROP_SIZE, "%s-supply", + vreg_name); + regnode = of_parse_phandle(dev->of_node, prop_name, 0); + + if (!regnode) { + dev_err(dev, "Looking up %s property in node %s failed\n", + prop_name, dev->of_node->full_name); + return -ENODEV; + } + + dev_dbg(dev, "Looking up %s property in node %s\n", + prop_name, dev->of_node->full_name); + + vreg->name = vreg_name; + vreg->ondemand = ondemand; + + snprintf(prop_name, CODEC_DT_MAX_PROP_SIZE, + "qcom,%s-voltage", vreg_name); + prop = of_get_property(dev->of_node, prop_name, &len); + + if (!prop || (len != (2 * sizeof(__be32)))) { + dev_err(dev, "%s %s property\n", + prop ? "invalid format" : "no", prop_name); + return -EINVAL; + } + vreg->min_uv = be32_to_cpup(&prop[0]); + vreg->max_uv = be32_to_cpup(&prop[1]); + + snprintf(prop_name, CODEC_DT_MAX_PROP_SIZE, + "qcom,%s-current", vreg_name); + + ret = of_property_read_u32(dev->of_node, prop_name, &prop_val); + if (ret) { + dev_err(dev, "Looking up %s property in node %s failed", + prop_name, dev->of_node->full_name); + return -EFAULT; + } + vreg->optimum_ua = prop_val; + + dev_dbg(dev, "%s: vol=[%d %d]uV, curr=[%d]uA, ond %d\n\n", vreg->name, + vreg->min_uv, vreg->max_uv, vreg->optimum_ua, vreg->ondemand); + return 0; +} + +static void msm_anlg_cdc_dt_parse_boost_info(struct snd_soc_codec *codec) +{ + struct sdm660_cdc_priv *sdm660_cdc_priv = + snd_soc_codec_get_drvdata(codec); + const char *prop_name = "qcom,cdc-boost-voltage"; + int boost_voltage, ret; + + ret = of_property_read_u32(codec->dev->of_node, prop_name, + &boost_voltage); + if (ret) { + dev_dbg(codec->dev, "Looking up %s property in node %s failed\n", + prop_name, codec->dev->of_node->full_name); + boost_voltage = DEFAULT_BOOST_VOLTAGE; + } + if (boost_voltage < MIN_BOOST_VOLTAGE || + boost_voltage > MAX_BOOST_VOLTAGE) { + dev_err(codec->dev, + "Incorrect boost voltage. Reverting to default\n"); + boost_voltage = DEFAULT_BOOST_VOLTAGE; + } + + sdm660_cdc_priv->boost_voltage = + VOLTAGE_CONVERTER(boost_voltage, MIN_BOOST_VOLTAGE, + BOOST_VOLTAGE_STEP); + dev_dbg(codec->dev, "Boost voltage value is: %d\n", + boost_voltage); +} + +static void msm_anlg_cdc_dt_parse_micbias_info(struct device *dev, + struct wcd_micbias_setting *micbias) +{ + const char *prop_name = "qcom,cdc-micbias-cfilt-mv"; + int ret; + + ret = of_property_read_u32(dev->of_node, prop_name, + &micbias->cfilt1_mv); + if (ret) { + dev_dbg(dev, "Looking up %s property in node %s failed", + prop_name, dev->of_node->full_name); + micbias->cfilt1_mv = MICBIAS_DEFAULT_VAL; + } +} + +static struct sdm660_cdc_pdata *msm_anlg_cdc_populate_dt_pdata( + struct device *dev) +{ + struct sdm660_cdc_pdata *pdata; + int ret, static_cnt, ond_cnt, idx, i; + const char *name = NULL; + const char *static_prop_name = "qcom,cdc-static-supplies"; + const char *ond_prop_name = "qcom,cdc-on-demand-supplies"; + + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return NULL; + + static_cnt = of_property_count_strings(dev->of_node, static_prop_name); + if (static_cnt < 0) { + dev_err(dev, "%s: Failed to get static supplies %d\n", __func__, + static_cnt); + ret = -EINVAL; + goto err; + } + + /* On-demand supply list is an optional property */ + ond_cnt = of_property_count_strings(dev->of_node, ond_prop_name); + if (ond_cnt < 0) + ond_cnt = 0; + + WARN_ON(static_cnt <= 0 || ond_cnt < 0); + if ((static_cnt + ond_cnt) > ARRAY_SIZE(pdata->regulator)) { + dev_err(dev, "%s: Num of supplies %u > max supported %zd\n", + __func__, (static_cnt + ond_cnt), + ARRAY_SIZE(pdata->regulator)); + ret = -EINVAL; + goto err; + } + + for (idx = 0; idx < static_cnt; idx++) { + ret = of_property_read_string_index(dev->of_node, + static_prop_name, idx, + &name); + if (ret) { + dev_err(dev, "%s: of read string %s idx %d error %d\n", + __func__, static_prop_name, idx, ret); + goto err; + } + + dev_dbg(dev, "%s: Found static cdc supply %s\n", __func__, + name); + ret = msm_anlg_cdc_dt_parse_vreg_info(dev, + &pdata->regulator[idx], + name, false); + if (ret) { + dev_err(dev, "%s:err parsing vreg for %s idx %d\n", + __func__, name, idx); + goto err; + } + } + + for (i = 0; i < ond_cnt; i++, idx++) { + ret = of_property_read_string_index(dev->of_node, ond_prop_name, + i, &name); + if (ret) { + dev_err(dev, "%s: err parsing on_demand for %s idx %d\n", + __func__, ond_prop_name, i); + goto err; + } + + dev_dbg(dev, "%s: Found on-demand cdc supply %s\n", __func__, + name); + ret = msm_anlg_cdc_dt_parse_vreg_info(dev, + &pdata->regulator[idx], + name, true); + if (ret) { + dev_err(dev, "%s: err parsing vreg on_demand for %s idx %d\n", + __func__, name, idx); + goto err; + } + } + msm_anlg_cdc_dt_parse_micbias_info(dev, &pdata->micbias); + + return pdata; +err: + devm_kfree(dev, pdata); + dev_err(dev, "%s: Failed to populate DT data ret = %d\n", + __func__, ret); + return NULL; +} + +static int msm_anlg_cdc_codec_enable_on_demand_supply( + struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + int ret = 0; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct sdm660_cdc_priv *sdm660_cdc = + snd_soc_codec_get_drvdata(codec); + struct on_demand_supply *supply; + + if (w->shift >= ON_DEMAND_SUPPLIES_MAX) { + dev_err(codec->dev, "%s: error index > MAX Demand supplies", + __func__); + ret = -EINVAL; + goto out; + } + dev_dbg(codec->dev, "%s: supply: %s event: %d ref: %d\n", + __func__, on_demand_supply_name[w->shift], event, + atomic_read(&sdm660_cdc->on_demand_list[w->shift].ref)); + + supply = &sdm660_cdc->on_demand_list[w->shift]; + WARN_ONCE(!supply->supply, "%s isn't defined\n", + on_demand_supply_name[w->shift]); + if (!supply->supply) { + dev_err(codec->dev, "%s: err supply not present ond for %d", + __func__, w->shift); + goto out; + } + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (atomic_inc_return(&supply->ref) == 1) { + ret = regulator_set_voltage(supply->supply, + supply->min_uv, + supply->max_uv); + if (ret) { + dev_err(codec->dev, + "Setting regulator voltage(en) for micbias with err = %d\n", + ret); + goto out; + } + ret = regulator_set_load(supply->supply, + supply->optimum_ua); + if (ret < 0) { + dev_err(codec->dev, + "Setting regulator optimum mode(en) failed for micbias with err = %d\n", + ret); + goto out; + } + ret = regulator_enable(supply->supply); + } + if (ret) + dev_err(codec->dev, "%s: Failed to enable %s\n", + __func__, + on_demand_supply_name[w->shift]); + break; + case SND_SOC_DAPM_POST_PMD: + if (atomic_read(&supply->ref) == 0) { + dev_dbg(codec->dev, "%s: %s supply has been disabled.\n", + __func__, on_demand_supply_name[w->shift]); + goto out; + } + if (atomic_dec_return(&supply->ref) == 0) { + ret = regulator_disable(supply->supply); + if (ret) + dev_err(codec->dev, "%s: Failed to disable %s\n", + __func__, + on_demand_supply_name[w->shift]); + ret = regulator_set_voltage(supply->supply, + 0, + supply->max_uv); + if (ret) { + dev_err(codec->dev, + "Setting regulator voltage(dis) failed for micbias with err = %d\n", + ret); + goto out; + } + ret = regulator_set_load(supply->supply, 0); + if (ret < 0) + dev_err(codec->dev, + "Setting regulator optimum mode(dis) failed for micbias with err = %d\n", + ret); + } + break; + default: + break; + } +out: + return ret; +} + +static int msm_anlg_cdc_codec_enable_clock_block(struct snd_soc_codec *codec, + int enable) +{ + struct msm_asoc_mach_data *pdata = NULL; + + pdata = snd_soc_card_get_drvdata(codec->component.card); + if (enable) { + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_MASTER_BIAS_CTL, 0x30, 0x30); + msm_anlg_cdc_dig_notifier_call(codec, DIG_CDC_EVENT_CLK_ON); + snd_soc_update_bits(codec, + MSM89XX_PMIC_DIGITAL_CDC_RST_CTL, 0x80, 0x80); + snd_soc_update_bits(codec, + MSM89XX_PMIC_DIGITAL_CDC_TOP_CLK_CTL, 0x0C, 0x0C); + } else { + snd_soc_update_bits(codec, + MSM89XX_PMIC_DIGITAL_CDC_TOP_CLK_CTL, 0x0C, 0x00); + } + return 0; +} + +static int msm_anlg_cdc_codec_enable_charge_pump(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct sdm660_cdc_priv *sdm660_cdc = + snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, "%s: event = %d\n", __func__, event); + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + msm_anlg_cdc_codec_enable_clock_block(codec, 1); + if (!(strcmp(w->name, "EAR CP"))) { + snd_soc_update_bits(codec, + MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL, + 0x80, 0x80); + msm_anlg_cdc_boost_mode_sequence(codec, EAR_PMU); + } else if (get_codec_version(sdm660_cdc) >= DIANGU) { + snd_soc_update_bits(codec, + MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL, + 0x80, 0x80); + } else { + snd_soc_update_bits(codec, + MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL, + 0xC0, 0xC0); + } + break; + case SND_SOC_DAPM_POST_PMU: + /* Wait for 1ms post powerup of chargepump */ + usleep_range(CODEC_DELAY_1_MS, CODEC_DELAY_1_1_MS); + break; + case SND_SOC_DAPM_POST_PMD: + /* Wait for 1ms post powerdown of chargepump */ + usleep_range(CODEC_DELAY_1_MS, CODEC_DELAY_1_1_MS); + if (!(strcmp(w->name, "EAR CP"))) { + snd_soc_update_bits(codec, + MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL, + 0x80, 0x00); + if (sdm660_cdc->boost_option != BOOST_ALWAYS) { + dev_dbg(codec->dev, + "%s: boost_option:%d, tear down ear\n", + __func__, sdm660_cdc->boost_option); + msm_anlg_cdc_boost_mode_sequence(codec, + EAR_PMD); + } + /* + * Reset pa select bit from ear to hph after ear pa + * is disabled and HPH DAC disable to reduce ear + * turn off pop and avoid HPH pop in concurrency + */ + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_RX_EAR_CTL, 0x80, 0x00); + } else { + if (get_codec_version(sdm660_cdc) < DIANGU) + snd_soc_update_bits(codec, + MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL, + 0x40, 0x00); + if (sdm660_cdc->rx_bias_count == 0) + snd_soc_update_bits(codec, + MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL, + 0x80, 0x00); + dev_dbg(codec->dev, "%s: rx_bias_count = %d\n", + __func__, sdm660_cdc->rx_bias_count); + } + break; + } + return 0; +} + +static int msm_anlg_cdc_ear_pa_boost_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct sdm660_cdc_priv *sdm660_cdc = + snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = + (sdm660_cdc->ear_pa_boost_set ? 1 : 0); + dev_dbg(codec->dev, "%s: sdm660_cdc->ear_pa_boost_set = %d\n", + __func__, sdm660_cdc->ear_pa_boost_set); + return 0; +} + +static int msm_anlg_cdc_ear_pa_boost_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct sdm660_cdc_priv *sdm660_cdc = + snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n", + __func__, ucontrol->value.integer.value[0]); + sdm660_cdc->ear_pa_boost_set = + (ucontrol->value.integer.value[0] ? true : false); + return 0; +} + +static int msm_anlg_cdc_pa_gain_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u8 ear_pa_gain; + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct sdm660_cdc_priv *sdm660_cdc = + snd_soc_codec_get_drvdata(codec); + + if (get_codec_version(sdm660_cdc) >= DIANGU) { + ear_pa_gain = snd_soc_read(codec, + MSM89XX_PMIC_ANALOG_RX_COM_BIAS_DAC); + ear_pa_gain = (ear_pa_gain >> 1) & 0x3; + + if (ear_pa_gain == 0x00) { + ucontrol->value.integer.value[0] = 3; + } else if (ear_pa_gain == 0x01) { + ucontrol->value.integer.value[0] = 2; + } else if (ear_pa_gain == 0x02) { + ucontrol->value.integer.value[0] = 1; + } else if (ear_pa_gain == 0x03) { + ucontrol->value.integer.value[0] = 0; + } else { + dev_err(codec->dev, + "%s: ERROR: Unsupported Ear Gain = 0x%x\n", + __func__, ear_pa_gain); + return -EINVAL; + } + } else { + ear_pa_gain = snd_soc_read(codec, + MSM89XX_PMIC_ANALOG_RX_EAR_CTL); + ear_pa_gain = (ear_pa_gain >> 5) & 0x1; + if (ear_pa_gain == 0x00) { + ucontrol->value.integer.value[0] = 0; + } else if (ear_pa_gain == 0x01) { + ucontrol->value.integer.value[0] = 3; + } else { + dev_err(codec->dev, + "%s: ERROR: Unsupported Ear Gain = 0x%x\n", + __func__, ear_pa_gain); + return -EINVAL; + } + } + dev_dbg(codec->dev, "%s: ear_pa_gain = 0x%x\n", __func__, ear_pa_gain); + return 0; +} + +static int msm_anlg_cdc_pa_gain_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u8 ear_pa_gain; + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct sdm660_cdc_priv *sdm660_cdc = + snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n", + __func__, ucontrol->value.integer.value[0]); + + if (get_codec_version(sdm660_cdc) >= DIANGU) { + switch (ucontrol->value.integer.value[0]) { + case 0: + ear_pa_gain = 0x06; + break; + case 1: + ear_pa_gain = 0x04; + break; + case 2: + ear_pa_gain = 0x02; + break; + case 3: + ear_pa_gain = 0x00; + break; + default: + return -EINVAL; + } + snd_soc_update_bits(codec, MSM89XX_PMIC_ANALOG_RX_COM_BIAS_DAC, + 0x06, ear_pa_gain); + } else { + switch (ucontrol->value.integer.value[0]) { + case 0: + ear_pa_gain = 0x00; + break; + case 3: + ear_pa_gain = 0x20; + break; + case 1: + case 2: + default: + return -EINVAL; + } + snd_soc_update_bits(codec, MSM89XX_PMIC_ANALOG_RX_EAR_CTL, + 0x20, ear_pa_gain); + } + return 0; +} + +static int msm_anlg_cdc_hph_mode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct sdm660_cdc_priv *sdm660_cdc = + snd_soc_codec_get_drvdata(codec); + + if (sdm660_cdc->hph_mode == NORMAL_MODE) { + ucontrol->value.integer.value[0] = 0; + } else if (sdm660_cdc->hph_mode == HD2_MODE) { + ucontrol->value.integer.value[0] = 1; + } else { + dev_err(codec->dev, "%s: ERROR: Default HPH Mode= %d\n", + __func__, sdm660_cdc->hph_mode); + } + + dev_dbg(codec->dev, "%s: sdm660_cdc->hph_mode = %d\n", __func__, + sdm660_cdc->hph_mode); + return 0; +} + +static int msm_anlg_cdc_hph_mode_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct sdm660_cdc_priv *sdm660_cdc = + snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n", + __func__, ucontrol->value.integer.value[0]); + + switch (ucontrol->value.integer.value[0]) { + case 0: + sdm660_cdc->hph_mode = NORMAL_MODE; + break; + case 1: + if (get_codec_version(sdm660_cdc) >= DIANGU) + sdm660_cdc->hph_mode = HD2_MODE; + break; + default: + sdm660_cdc->hph_mode = NORMAL_MODE; + break; + } + dev_dbg(codec->dev, "%s: sdm660_cdc->hph_mode_set = %d\n", + __func__, sdm660_cdc->hph_mode); + return 0; +} + +static int msm_anlg_cdc_boost_option_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct sdm660_cdc_priv *sdm660_cdc = + snd_soc_codec_get_drvdata(codec); + + if (sdm660_cdc->boost_option == BOOST_SWITCH) { + ucontrol->value.integer.value[0] = 0; + } else if (sdm660_cdc->boost_option == BOOST_ALWAYS) { + ucontrol->value.integer.value[0] = 1; + } else if (sdm660_cdc->boost_option == BYPASS_ALWAYS) { + ucontrol->value.integer.value[0] = 2; + } else if (sdm660_cdc->boost_option == BOOST_ON_FOREVER) { + ucontrol->value.integer.value[0] = 3; + } else { + dev_err(codec->dev, "%s: ERROR: Unsupported Boost option= %d\n", + __func__, sdm660_cdc->boost_option); + return -EINVAL; + } + + dev_dbg(codec->dev, "%s: sdm660_cdc->boost_option = %d\n", __func__, + sdm660_cdc->boost_option); + return 0; +} + +static int msm_anlg_cdc_boost_option_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct sdm660_cdc_priv *sdm660_cdc = + snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n", + __func__, ucontrol->value.integer.value[0]); + + switch (ucontrol->value.integer.value[0]) { + case 0: + sdm660_cdc->boost_option = BOOST_SWITCH; + break; + case 1: + sdm660_cdc->boost_option = BOOST_ALWAYS; + break; + case 2: + sdm660_cdc->boost_option = BYPASS_ALWAYS; + msm_anlg_cdc_bypass_on(codec); + break; + case 3: + sdm660_cdc->boost_option = BOOST_ON_FOREVER; + msm_anlg_cdc_boost_on(codec); + break; + default: + pr_err("%s: invalid boost option: %d\n", __func__, + sdm660_cdc->boost_option); + return -EINVAL; + } + dev_dbg(codec->dev, "%s: sdm660_cdc->boost_option_set = %d\n", + __func__, sdm660_cdc->boost_option); + return 0; +} + +static int msm_anlg_cdc_spk_boost_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct sdm660_cdc_priv *sdm660_cdc = + snd_soc_codec_get_drvdata(codec); + + if (sdm660_cdc->spk_boost_set == false) { + ucontrol->value.integer.value[0] = 0; + } else if (sdm660_cdc->spk_boost_set == true) { + ucontrol->value.integer.value[0] = 1; + } else { + dev_err(codec->dev, "%s: ERROR: Unsupported Speaker Boost = %d\n", + __func__, sdm660_cdc->spk_boost_set); + return -EINVAL; + } + + dev_dbg(codec->dev, "%s: sdm660_cdc->spk_boost_set = %d\n", __func__, + sdm660_cdc->spk_boost_set); + return 0; +} + +static int msm_anlg_cdc_spk_boost_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct sdm660_cdc_priv *sdm660_cdc = + snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n", + __func__, ucontrol->value.integer.value[0]); + + switch (ucontrol->value.integer.value[0]) { + case 0: + sdm660_cdc->spk_boost_set = false; + break; + case 1: + sdm660_cdc->spk_boost_set = true; + break; + default: + return -EINVAL; + } + dev_dbg(codec->dev, "%s: sdm660_cdc->spk_boost_set = %d\n", + __func__, sdm660_cdc->spk_boost_set); + return 0; +} + +static int msm_anlg_cdc_ext_spk_boost_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct sdm660_cdc_priv *sdm660_cdc = + snd_soc_codec_get_drvdata(codec); + + if (sdm660_cdc->ext_spk_boost_set == false) + ucontrol->value.integer.value[0] = 0; + else + ucontrol->value.integer.value[0] = 1; + + dev_dbg(codec->dev, "%s: sdm660_cdc->ext_spk_boost_set = %d\n", + __func__, sdm660_cdc->ext_spk_boost_set); + return 0; +} + +static int msm_anlg_cdc_ext_spk_boost_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct sdm660_cdc_priv *sdm660_cdc = + snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n", + __func__, ucontrol->value.integer.value[0]); + + switch (ucontrol->value.integer.value[0]) { + case 0: + sdm660_cdc->ext_spk_boost_set = false; + break; + case 1: + sdm660_cdc->ext_spk_boost_set = true; + break; + default: + return -EINVAL; + } + dev_dbg(codec->dev, "%s: sdm660_cdc->spk_boost_set = %d\n", + __func__, sdm660_cdc->spk_boost_set); + return 0; +} + +static const char * const msm_anlg_cdc_ear_pa_boost_ctrl_text[] = { + "DISABLE", "ENABLE"}; +static const struct soc_enum msm_anlg_cdc_ear_pa_boost_ctl_enum[] = { + SOC_ENUM_SINGLE_EXT(2, msm_anlg_cdc_ear_pa_boost_ctrl_text), +}; + +static const char * const msm_anlg_cdc_ear_pa_gain_text[] = { + "POS_1P5_DB", "POS_3_DB", "POS_4P5_DB", "POS_6_DB"}; +static const struct soc_enum msm_anlg_cdc_ear_pa_gain_enum[] = { + SOC_ENUM_SINGLE_EXT(4, msm_anlg_cdc_ear_pa_gain_text), +}; + +static const char * const msm_anlg_cdc_boost_option_ctrl_text[] = { + "BOOST_SWITCH", "BOOST_ALWAYS", "BYPASS_ALWAYS", + "BOOST_ON_FOREVER"}; +static const struct soc_enum msm_anlg_cdc_boost_option_ctl_enum[] = { + SOC_ENUM_SINGLE_EXT(4, msm_anlg_cdc_boost_option_ctrl_text), +}; +static const char * const msm_anlg_cdc_spk_boost_ctrl_text[] = { + "DISABLE", "ENABLE"}; +static const struct soc_enum msm_anlg_cdc_spk_boost_ctl_enum[] = { + SOC_ENUM_SINGLE_EXT(2, msm_anlg_cdc_spk_boost_ctrl_text), +}; + +static const char * const msm_anlg_cdc_ext_spk_boost_ctrl_text[] = { + "DISABLE", "ENABLE"}; +static const struct soc_enum msm_anlg_cdc_ext_spk_boost_ctl_enum[] = { + SOC_ENUM_SINGLE_EXT(2, msm_anlg_cdc_ext_spk_boost_ctrl_text), +}; + +static const char * const msm_anlg_cdc_hph_mode_ctrl_text[] = { + "NORMAL", "HD2"}; +static const struct soc_enum msm_anlg_cdc_hph_mode_ctl_enum[] = { + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(msm_anlg_cdc_hph_mode_ctrl_text), + msm_anlg_cdc_hph_mode_ctrl_text), +}; + +/*cut of frequency for high pass filter*/ +static const char * const cf_text[] = { + "MIN_3DB_4Hz", "MIN_3DB_75Hz", "MIN_3DB_150Hz" +}; + + +static const struct snd_kcontrol_new msm_anlg_cdc_snd_controls[] = { + + SOC_ENUM_EXT("RX HPH Mode", msm_anlg_cdc_hph_mode_ctl_enum[0], + msm_anlg_cdc_hph_mode_get, msm_anlg_cdc_hph_mode_set), + + SOC_ENUM_EXT("Boost Option", msm_anlg_cdc_boost_option_ctl_enum[0], + msm_anlg_cdc_boost_option_get, msm_anlg_cdc_boost_option_set), + + SOC_ENUM_EXT("EAR PA Boost", msm_anlg_cdc_ear_pa_boost_ctl_enum[0], + msm_anlg_cdc_ear_pa_boost_get, msm_anlg_cdc_ear_pa_boost_set), + + SOC_ENUM_EXT("EAR PA Gain", msm_anlg_cdc_ear_pa_gain_enum[0], + msm_anlg_cdc_pa_gain_get, msm_anlg_cdc_pa_gain_put), + + SOC_ENUM_EXT("Speaker Boost", msm_anlg_cdc_spk_boost_ctl_enum[0], + msm_anlg_cdc_spk_boost_get, msm_anlg_cdc_spk_boost_set), + + SOC_ENUM_EXT("Ext Spk Boost", msm_anlg_cdc_ext_spk_boost_ctl_enum[0], + msm_anlg_cdc_ext_spk_boost_get, msm_anlg_cdc_ext_spk_boost_set), + + SOC_SINGLE_TLV("ADC1 Volume", MSM89XX_PMIC_ANALOG_TX_1_EN, 3, + 8, 0, analog_gain), + SOC_SINGLE_TLV("ADC2 Volume", MSM89XX_PMIC_ANALOG_TX_2_EN, 3, + 8, 0, analog_gain), + SOC_SINGLE_TLV("ADC3 Volume", MSM89XX_PMIC_ANALOG_TX_3_EN, 3, + 8, 0, analog_gain), + + +}; + +static int tombak_hph_impedance_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret; + uint32_t zl, zr; + bool hphr; + struct soc_multi_mixer_control *mc; + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct sdm660_cdc_priv *priv = snd_soc_codec_get_drvdata(codec); + + mc = (struct soc_multi_mixer_control *)(kcontrol->private_value); + + hphr = mc->shift; + ret = wcd_mbhc_get_impedance(&priv->mbhc, &zl, &zr); + if (ret) + dev_dbg(codec->dev, "%s: Failed to get mbhc imped", __func__); + dev_dbg(codec->dev, "%s: zl %u, zr %u\n", __func__, zl, zr); + ucontrol->value.integer.value[0] = hphr ? zr : zl; + + return 0; +} + +static const struct snd_kcontrol_new impedance_detect_controls[] = { + SOC_SINGLE_EXT("HPHL Impedance", 0, 0, UINT_MAX, 0, + tombak_hph_impedance_get, NULL), + SOC_SINGLE_EXT("HPHR Impedance", 0, 1, UINT_MAX, 0, + tombak_hph_impedance_get, NULL), +}; + +static int tombak_get_hph_type(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct sdm660_cdc_priv *priv = snd_soc_codec_get_drvdata(codec); + struct wcd_mbhc *mbhc; + + if (!priv) { + dev_err(codec->dev, + "%s: sdm660_cdc-wcd private data is NULL\n", + __func__); + return -EINVAL; + } + + mbhc = &priv->mbhc; + if (!mbhc) { + dev_err(codec->dev, "%s: mbhc not initialized\n", __func__); + return -EINVAL; + } + + ucontrol->value.integer.value[0] = (u32) mbhc->hph_type; + dev_dbg(codec->dev, "%s: hph_type = %u\n", __func__, mbhc->hph_type); + + return 0; +} + +static const struct snd_kcontrol_new hph_type_detect_controls[] = { + SOC_SINGLE_EXT("HPH Type", 0, 0, UINT_MAX, 0, + tombak_get_hph_type, NULL), +}; + +static const char * const rdac2_mux_text[] = { + "ZERO", "RX2", "RX1" +}; + +static const struct snd_kcontrol_new adc1_switch = + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct soc_enum rdac2_mux_enum = + SOC_ENUM_SINGLE(MSM89XX_PMIC_DIGITAL_CDC_CONN_HPHR_DAC_CTL, + 0, 3, rdac2_mux_text); + +static const char * const adc2_mux_text[] = { + "ZERO", "INP2", "INP3" +}; + +static const char * const ext_spk_text[] = { + "Off", "On" +}; + +static const char * const wsa_spk_text[] = { + "ZERO", "WSA" +}; + +static const struct soc_enum adc2_enum = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, + ARRAY_SIZE(adc2_mux_text), adc2_mux_text); + +static const struct soc_enum ext_spk_enum = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, + ARRAY_SIZE(ext_spk_text), ext_spk_text); + +static const struct soc_enum wsa_spk_enum = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, + ARRAY_SIZE(wsa_spk_text), wsa_spk_text); + + + +static const struct snd_kcontrol_new ext_spk_mux = + SOC_DAPM_ENUM("Ext Spk Switch Mux", ext_spk_enum); + + + +static const struct snd_kcontrol_new tx_adc2_mux = + SOC_DAPM_ENUM("ADC2 MUX Mux", adc2_enum); + + +static const struct snd_kcontrol_new rdac2_mux = + SOC_DAPM_ENUM("RDAC2 MUX Mux", rdac2_mux_enum); + +static const char * const ear_text[] = { + "ZERO", "Switch", +}; + +static const struct soc_enum ear_enum = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(ear_text), ear_text); + +static const struct snd_kcontrol_new ear_pa_mux[] = { + SOC_DAPM_ENUM("EAR_S", ear_enum) +}; + +static const struct snd_kcontrol_new wsa_spk_mux[] = { + SOC_DAPM_ENUM("WSA Spk Switch", wsa_spk_enum) +}; + + + +static const char * const hph_text[] = { + "ZERO", "Switch", +}; + +static const struct soc_enum hph_enum = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(hph_text), hph_text); + +static const struct snd_kcontrol_new hphl_mux[] = { + SOC_DAPM_ENUM("HPHL", hph_enum) +}; + +static const struct snd_kcontrol_new hphr_mux[] = { + SOC_DAPM_ENUM("HPHR", hph_enum) +}; + +static const struct snd_kcontrol_new spkr_mux[] = { + SOC_DAPM_ENUM("SPK", hph_enum) +}; + +static const char * const lo_text[] = { + "ZERO", "Switch", +}; + +static const struct soc_enum lo_enum = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(hph_text), hph_text); + +static const struct snd_kcontrol_new lo_mux[] = { + SOC_DAPM_ENUM("LINE_OUT", lo_enum) +}; + +static void msm_anlg_cdc_codec_enable_adc_block(struct snd_soc_codec *codec, + int enable) +{ + struct sdm660_cdc_priv *wcd8x16 = snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, "%s %d\n", __func__, enable); + + if (enable) { + wcd8x16->adc_count++; + snd_soc_update_bits(codec, + MSM89XX_PMIC_DIGITAL_CDC_ANA_CLK_CTL, + 0x20, 0x20); + snd_soc_update_bits(codec, + MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL, + 0x10, 0x10); + } else { + wcd8x16->adc_count--; + if (!wcd8x16->adc_count) { + snd_soc_update_bits(codec, + MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL, + 0x10, 0x00); + snd_soc_update_bits(codec, + MSM89XX_PMIC_DIGITAL_CDC_ANA_CLK_CTL, + 0x20, 0x0); + } + } +} + +static int msm_anlg_cdc_codec_enable_adc(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + u16 adc_reg; + u8 init_bit_shift; + + dev_dbg(codec->dev, "%s %d\n", __func__, event); + + adc_reg = MSM89XX_PMIC_ANALOG_TX_1_2_TEST_CTL_2; + + if (w->reg == MSM89XX_PMIC_ANALOG_TX_1_EN) + init_bit_shift = 5; + else if ((w->reg == MSM89XX_PMIC_ANALOG_TX_2_EN) || + (w->reg == MSM89XX_PMIC_ANALOG_TX_3_EN)) + init_bit_shift = 4; + else { + dev_err(codec->dev, "%s: Error, invalid adc register\n", + __func__); + return -EINVAL; + } + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + msm_anlg_cdc_codec_enable_adc_block(codec, 1); + if (w->reg == MSM89XX_PMIC_ANALOG_TX_2_EN) + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_MICB_1_CTL, 0x02, 0x02); + /* + * Add delay of 10 ms to give sufficient time for the voltage + * to shoot up and settle so that the txfe init does not + * happen when the input voltage is changing too much. + */ + usleep_range(10000, 10010); + snd_soc_update_bits(codec, adc_reg, 1 << init_bit_shift, + 1 << init_bit_shift); + if (w->reg == MSM89XX_PMIC_ANALOG_TX_1_EN) + snd_soc_update_bits(codec, + MSM89XX_PMIC_DIGITAL_CDC_CONN_TX1_CTL, + 0x03, 0x00); + else if ((w->reg == MSM89XX_PMIC_ANALOG_TX_2_EN) || + (w->reg == MSM89XX_PMIC_ANALOG_TX_3_EN)) + snd_soc_update_bits(codec, + MSM89XX_PMIC_DIGITAL_CDC_CONN_TX2_CTL, + 0x03, 0x00); + /* Wait for 1ms to allow txfe settling time */ + usleep_range(CODEC_DELAY_1_MS, CODEC_DELAY_1_1_MS); + break; + case SND_SOC_DAPM_POST_PMU: + /* + * Add delay of 12 ms before deasserting the init + * to reduce the tx pop + */ + usleep_range(12000, 12010); + snd_soc_update_bits(codec, adc_reg, 1 << init_bit_shift, 0x00); + /* Wait for 1ms to allow txfe settling time post powerup */ + usleep_range(CODEC_DELAY_1_MS, CODEC_DELAY_1_1_MS); + break; + case SND_SOC_DAPM_POST_PMD: + msm_anlg_cdc_codec_enable_adc_block(codec, 0); + if (w->reg == MSM89XX_PMIC_ANALOG_TX_2_EN) + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_MICB_1_CTL, 0x02, 0x00); + if (w->reg == MSM89XX_PMIC_ANALOG_TX_1_EN) + snd_soc_update_bits(codec, + MSM89XX_PMIC_DIGITAL_CDC_CONN_TX1_CTL, + 0x03, 0x02); + else if ((w->reg == MSM89XX_PMIC_ANALOG_TX_2_EN) || + (w->reg == MSM89XX_PMIC_ANALOG_TX_3_EN)) + snd_soc_update_bits(codec, + MSM89XX_PMIC_DIGITAL_CDC_CONN_TX2_CTL, + 0x03, 0x02); + + break; + } + return 0; +} + +static int msm_anlg_cdc_codec_enable_spk_pa(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct sdm660_cdc_priv *sdm660_cdc = + snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, "%s %d %s\n", __func__, event, w->name); + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_update_bits(codec, + MSM89XX_PMIC_DIGITAL_CDC_ANA_CLK_CTL, 0x10, 0x10); + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_SPKR_PWRSTG_CTL, 0x01, 0x01); + switch (sdm660_cdc->boost_option) { + case BOOST_SWITCH: + if (!sdm660_cdc->spk_boost_set) + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_SPKR_DAC_CTL, + 0x10, 0x10); + break; + case BOOST_ALWAYS: + case BOOST_ON_FOREVER: + break; + case BYPASS_ALWAYS: + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_SPKR_DAC_CTL, + 0x10, 0x10); + break; + default: + dev_err(codec->dev, + "%s: invalid boost option: %d\n", __func__, + sdm660_cdc->boost_option); + break; + } + /* Wait for 1ms after SPK_DAC CTL setting */ + usleep_range(CODEC_DELAY_1_MS, CODEC_DELAY_1_1_MS); + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_SPKR_PWRSTG_CTL, 0xE0, 0xE0); + if (get_codec_version(sdm660_cdc) != TOMBAK_1_0) + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_RX_EAR_CTL, 0x01, 0x01); + break; + case SND_SOC_DAPM_POST_PMU: + /* Wait for 1ms after SPK_VBAT_LDO Enable */ + usleep_range(CODEC_DELAY_1_MS, CODEC_DELAY_1_1_MS); + switch (sdm660_cdc->boost_option) { + case BOOST_SWITCH: + if (sdm660_cdc->spk_boost_set) + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_SPKR_DRV_CTL, + 0xEF, 0xEF); + else + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_SPKR_DAC_CTL, + 0x10, 0x00); + break; + case BOOST_ALWAYS: + case BOOST_ON_FOREVER: + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_SPKR_DRV_CTL, + 0xEF, 0xEF); + break; + case BYPASS_ALWAYS: + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_SPKR_DAC_CTL, 0x10, 0x00); + break; + default: + dev_err(codec->dev, + "%s: invalid boost option: %d\n", __func__, + sdm660_cdc->boost_option); + break; + } + msm_anlg_cdc_dig_notifier_call(codec, + DIG_CDC_EVENT_RX3_MUTE_OFF); + snd_soc_update_bits(codec, w->reg, 0x80, 0x80); + break; + case SND_SOC_DAPM_PRE_PMD: + msm_anlg_cdc_dig_notifier_call(codec, + DIG_CDC_EVENT_RX3_MUTE_ON); + /* + * Add 1 ms sleep for the mute to take effect + */ + usleep_range(CODEC_DELAY_1_MS, CODEC_DELAY_1_1_MS); + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_SPKR_DAC_CTL, 0x10, 0x10); + if (get_codec_version(sdm660_cdc) < CAJON_2_0) + msm_anlg_cdc_boost_mode_sequence(codec, SPK_PMD); + snd_soc_update_bits(codec, w->reg, 0x80, 0x00); + switch (sdm660_cdc->boost_option) { + case BOOST_SWITCH: + if (sdm660_cdc->spk_boost_set) + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_SPKR_DRV_CTL, + 0xEF, 0x69); + break; + case BOOST_ALWAYS: + case BOOST_ON_FOREVER: + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_SPKR_DRV_CTL, + 0xEF, 0x69); + break; + case BYPASS_ALWAYS: + break; + default: + dev_err(codec->dev, + "%s: invalid boost option: %d\n", __func__, + sdm660_cdc->boost_option); + break; + } + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_SPKR_PWRSTG_CTL, 0xE0, 0x00); + /* Wait for 1ms to allow setting time for spkr path disable */ + usleep_range(CODEC_DELAY_1_MS, CODEC_DELAY_1_1_MS); + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_SPKR_PWRSTG_CTL, 0x01, 0x00); + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_SPKR_DAC_CTL, 0x10, 0x00); + if (get_codec_version(sdm660_cdc) != TOMBAK_1_0) + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_RX_EAR_CTL, 0x01, 0x00); + snd_soc_update_bits(codec, + MSM89XX_PMIC_DIGITAL_CDC_ANA_CLK_CTL, 0x10, 0x00); + if (get_codec_version(sdm660_cdc) >= CAJON_2_0) + msm_anlg_cdc_boost_mode_sequence(codec, SPK_PMD); + break; + } + return 0; +} + +static int msm_anlg_cdc_codec_enable_dig_clk(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct sdm660_cdc_priv *sdm660_cdc = + snd_soc_codec_get_drvdata(codec); + struct msm_asoc_mach_data *pdata = NULL; + + pdata = snd_soc_card_get_drvdata(codec->component.card); + + dev_dbg(codec->dev, "%s event %d w->name %s\n", __func__, + event, w->name); + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + msm_anlg_cdc_codec_enable_clock_block(codec, 1); + snd_soc_update_bits(codec, w->reg, 0x80, 0x80); + msm_anlg_cdc_boost_mode_sequence(codec, SPK_PMU); + break; + case SND_SOC_DAPM_POST_PMD: + if (sdm660_cdc->rx_bias_count == 0) + snd_soc_update_bits(codec, + MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL, + 0x80, 0x00); + } + return 0; +} + + + +static bool msm_anlg_cdc_use_mb(struct snd_soc_codec *codec) +{ + struct sdm660_cdc_priv *sdm660_cdc = + snd_soc_codec_get_drvdata(codec); + + if (get_codec_version(sdm660_cdc) < CAJON) + return true; + else + return false; +} + +static void msm_anlg_cdc_set_auto_zeroing(struct snd_soc_codec *codec, + bool enable) +{ + struct sdm660_cdc_priv *sdm660_cdc = + snd_soc_codec_get_drvdata(codec); + + if (get_codec_version(sdm660_cdc) < CONGA) { + if (enable) + /* + * Set autozeroing for special headset detection and + * buttons to work. + */ + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_MICB_2_EN, + 0x18, 0x10); + else + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_MICB_2_EN, + 0x18, 0x00); + + } else { + dev_dbg(codec->dev, + "%s: Auto Zeroing is not required from CONGA\n", + __func__); + } +} + +static void msm_anlg_cdc_trim_btn_reg(struct snd_soc_codec *codec) +{ + struct sdm660_cdc_priv *sdm660_cdc = + snd_soc_codec_get_drvdata(codec); + + if (get_codec_version(sdm660_cdc) == TOMBAK_1_0) { + pr_debug("%s: This device needs to be trimmed\n", __func__); + /* + * Calculate the trim value for each device used + * till is comes in production by hardware team + */ + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_SEC_ACCESS, + 0xA5, 0xA5); + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_TRIM_CTRL2, + 0xFF, 0x30); + } else { + dev_dbg(codec->dev, "%s: This device is trimmed at ATE\n", + __func__); + } +} + +static int msm_anlg_cdc_enable_ext_mb_source(struct wcd_mbhc *wcd_mbhc, + bool turn_on) +{ + int ret = 0; + static int count; + struct snd_soc_codec *codec = wcd_mbhc->codec; + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + + dev_dbg(codec->dev, "%s turn_on: %d count: %d\n", __func__, turn_on, + count); + if (turn_on) { + if (!count) { + ret = snd_soc_dapm_force_enable_pin(dapm, + "MICBIAS_REGULATOR"); + snd_soc_dapm_sync(dapm); + } + count++; + } else { + if (count > 0) + count--; + if (!count) { + ret = snd_soc_dapm_disable_pin(dapm, + "MICBIAS_REGULATOR"); + snd_soc_dapm_sync(dapm); + } + } + + if (ret) + dev_err(codec->dev, "%s: Failed to %s external micbias source\n", + __func__, turn_on ? "enable" : "disabled"); + else + dev_dbg(codec->dev, "%s: %s external micbias source\n", + __func__, turn_on ? "Enabled" : "Disabled"); + + return ret; +} + +static int msm_anlg_cdc_codec_enable_micbias(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct sdm660_cdc_priv *sdm660_cdc = + snd_soc_codec_get_drvdata(codec); + u16 micb_int_reg; + char *internal1_text = "Internal1"; + char *internal2_text = "Internal2"; + char *internal3_text = "Internal3"; + char *external2_text = "External2"; + char *external_text = "External"; + bool micbias2; + + dev_dbg(codec->dev, "%s %d\n", __func__, event); + switch (w->reg) { + case MSM89XX_PMIC_ANALOG_MICB_1_EN: + case MSM89XX_PMIC_ANALOG_MICB_2_EN: + micb_int_reg = MSM89XX_PMIC_ANALOG_MICB_1_INT_RBIAS; + break; + default: + dev_err(codec->dev, + "%s: Error, invalid micbias register 0x%x\n", + __func__, w->reg); + return -EINVAL; + } + + micbias2 = (snd_soc_read(codec, MSM89XX_PMIC_ANALOG_MICB_2_EN) & 0x80); + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (strnstr(w->name, internal1_text, strlen(w->name))) { + if (get_codec_version(sdm660_cdc) >= CAJON) + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_TX_1_2_ATEST_CTL_2, + 0x02, 0x02); + snd_soc_update_bits(codec, micb_int_reg, 0x80, 0x80); + } else if (strnstr(w->name, internal2_text, strlen(w->name))) { + snd_soc_update_bits(codec, micb_int_reg, 0x10, 0x10); + snd_soc_update_bits(codec, w->reg, 0x60, 0x00); + } else if (strnstr(w->name, internal3_text, strlen(w->name))) { + snd_soc_update_bits(codec, micb_int_reg, 0x2, 0x2); + /* + * update MSM89XX_PMIC_ANALOG_TX_1_2_ATEST_CTL_2 + * for external bias only, not for external2. + */ + } else if (!strnstr(w->name, external2_text, strlen(w->name)) && + strnstr(w->name, external_text, + strlen(w->name))) { + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_TX_1_2_ATEST_CTL_2, + 0x02, 0x02); + } + if (!strnstr(w->name, external_text, strlen(w->name))) + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_MICB_1_EN, 0x05, 0x04); + if (w->reg == MSM89XX_PMIC_ANALOG_MICB_1_EN) + msm_anlg_cdc_configure_cap(codec, true, micbias2); + + break; + case SND_SOC_DAPM_POST_PMU: + if (get_codec_version(sdm660_cdc) <= TOMBAK_2_0) + /* + * Wait for 20ms post micbias enable + * for version < tombak 2.0. + */ + usleep_range(20000, 20100); + if (strnstr(w->name, internal1_text, strlen(w->name))) { + snd_soc_update_bits(codec, micb_int_reg, 0x40, 0x40); + } else if (strnstr(w->name, internal2_text, strlen(w->name))) { + snd_soc_update_bits(codec, micb_int_reg, 0x08, 0x08); + msm_anlg_cdc_notifier_call(codec, + WCD_EVENT_POST_MICBIAS_2_ON); + } else if (strnstr(w->name, internal3_text, 30)) { + snd_soc_update_bits(codec, micb_int_reg, 0x01, 0x01); + } else if (strnstr(w->name, external2_text, strlen(w->name))) { + msm_anlg_cdc_notifier_call(codec, + WCD_EVENT_POST_MICBIAS_2_ON); + } + break; + case SND_SOC_DAPM_POST_PMD: + if (strnstr(w->name, internal1_text, strlen(w->name))) { + snd_soc_update_bits(codec, micb_int_reg, 0xC0, 0x40); + } else if (strnstr(w->name, internal2_text, strlen(w->name))) { + msm_anlg_cdc_notifier_call(codec, + WCD_EVENT_POST_MICBIAS_2_OFF); + } else if (strnstr(w->name, internal3_text, 30)) { + snd_soc_update_bits(codec, micb_int_reg, 0x2, 0x0); + } else if (strnstr(w->name, external2_text, strlen(w->name))) { + /* + * send micbias turn off event to mbhc driver and then + * break, as no need to set MICB_1_EN register. + */ + msm_anlg_cdc_notifier_call(codec, + WCD_EVENT_POST_MICBIAS_2_OFF); + break; + } + if (w->reg == MSM89XX_PMIC_ANALOG_MICB_1_EN) + msm_anlg_cdc_configure_cap(codec, false, micbias2); + break; + } + return 0; +} + +static void set_compander_mode(void *handle, int val) +{ + struct sdm660_cdc_priv *handle_cdc = handle; + struct snd_soc_codec *codec = handle_cdc->codec; + + if (get_codec_version(handle_cdc) >= DIANGU) { + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_RX_COM_BIAS_DAC, + 0x08, val); + }; +} + +static void update_clkdiv(void *handle, int val) +{ + struct sdm660_cdc_priv *handle_cdc = handle; + struct snd_soc_codec *codec = handle_cdc->codec; + + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_TX_1_2_TXFE_CLKDIV, + 0xFF, val); +} + +static int get_cdc_version(void *handle) +{ + struct sdm660_cdc_priv *sdm660_cdc = handle; + + return get_codec_version(sdm660_cdc); +} + +static int sdm660_wcd_codec_enable_vdd_spkr(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct sdm660_cdc_priv *sdm660_cdc = + snd_soc_codec_get_drvdata(codec); + int ret = 0; + + if (!sdm660_cdc->ext_spk_boost_set) { + dev_dbg(codec->dev, "%s: ext_boost not supported/disabled\n", + __func__); + return 0; + } + dev_dbg(codec->dev, "%s: %s %d\n", __func__, w->name, event); + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (sdm660_cdc->spkdrv_reg) { + ret = regulator_enable(sdm660_cdc->spkdrv_reg); + if (ret) + dev_err(codec->dev, + "%s Failed to enable spkdrv reg %s\n", + __func__, MSM89XX_VDD_SPKDRV_NAME); + } + break; + case SND_SOC_DAPM_POST_PMD: + if (sdm660_cdc->spkdrv_reg) { + ret = regulator_disable(sdm660_cdc->spkdrv_reg); + if (ret) + dev_err(codec->dev, + "%s: Failed to disable spkdrv_reg %s\n", + __func__, MSM89XX_VDD_SPKDRV_NAME); + } + break; + } + return 0; +} + + +/* The register address is the same as other codec so it can use resmgr */ +static int msm_anlg_cdc_codec_enable_rx_bias(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct sdm660_cdc_priv *sdm660_cdc = + snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, "%s %d\n", __func__, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + sdm660_cdc->rx_bias_count++; + if (sdm660_cdc->rx_bias_count == 1) { + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_RX_COM_BIAS_DAC, + 0x80, 0x80); + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_RX_COM_BIAS_DAC, + 0x01, 0x01); + } + break; + case SND_SOC_DAPM_POST_PMD: + sdm660_cdc->rx_bias_count--; + if (sdm660_cdc->rx_bias_count == 0) { + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_RX_COM_BIAS_DAC, + 0x01, 0x00); + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_RX_COM_BIAS_DAC, + 0x80, 0x00); + } + break; + } + dev_dbg(codec->dev, "%s rx_bias_count = %d\n", + __func__, sdm660_cdc->rx_bias_count); + return 0; +} + +static uint32_t wcd_get_impedance_value(uint32_t imped) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(wcd_imped_val) - 1; i++) { + if (imped >= wcd_imped_val[i] && + imped < wcd_imped_val[i + 1]) + break; + } + + pr_debug("%s: selected impedance value = %d\n", + __func__, wcd_imped_val[i]); + return wcd_imped_val[i]; +} + +static void wcd_imped_config(struct snd_soc_codec *codec, + uint32_t imped, bool set_gain) +{ + uint32_t value; + int codec_version; + struct sdm660_cdc_priv *sdm660_cdc = + snd_soc_codec_get_drvdata(codec); + + value = wcd_get_impedance_value(imped); + + if (value < wcd_imped_val[0]) { + dev_dbg(codec->dev, + "%s, detected impedance is less than 4 Ohm\n", + __func__); + return; + } + + codec_version = get_codec_version(sdm660_cdc); + + if (set_gain) { + switch (codec_version) { + case TOMBAK_1_0: + case TOMBAK_2_0: + case CONGA: + /* + * For 32Ohm load and higher loads, Set 0x19E + * bit 5 to 1 (POS_0_DB_DI). For loads lower + * than 32Ohm (such as 16Ohm load), Set 0x19E + * bit 5 to 0 (POS_M4P5_DB_DI) + */ + if (value >= 32) + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_RX_EAR_CTL, + 0x20, 0x20); + else + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_RX_EAR_CTL, + 0x20, 0x00); + break; + case CAJON: + case CAJON_2_0: + case DIANGU: + case DRAX_CDC: + if (value >= 13) { + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_RX_EAR_CTL, + 0x20, 0x20); + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_NCP_VCTRL, + 0x07, 0x07); + } else { + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_RX_EAR_CTL, + 0x20, 0x00); + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_NCP_VCTRL, + 0x07, 0x04); + } + break; + } + } else { + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_RX_EAR_CTL, + 0x20, 0x00); + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_NCP_VCTRL, + 0x07, 0x04); + } + + dev_dbg(codec->dev, "%s: Exit\n", __func__); +} + +static int msm_anlg_cdc_hphl_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + uint32_t impedl, impedr; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct sdm660_cdc_priv *sdm660_cdc = + snd_soc_codec_get_drvdata(codec); + int ret; + + dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event); + ret = wcd_mbhc_get_impedance(&sdm660_cdc->mbhc, + &impedl, &impedr); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (get_codec_version(sdm660_cdc) > CAJON) + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_RX_HPH_CNP_EN, + 0x08, 0x08); + if (get_codec_version(sdm660_cdc) == CAJON || + get_codec_version(sdm660_cdc) == CAJON_2_0) { + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_RX_HPH_L_TEST, + 0x80, 0x80); + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_RX_HPH_R_TEST, + 0x80, 0x80); + } + if (get_codec_version(sdm660_cdc) > CAJON) + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_RX_HPH_CNP_EN, + 0x08, 0x00); + if (sdm660_cdc->hph_mode == HD2_MODE) + msm_anlg_cdc_dig_notifier_call(codec, + DIG_CDC_EVENT_PRE_RX1_INT_ON); + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_RX_HPH_L_PA_DAC_CTL, 0x02, 0x02); + snd_soc_update_bits(codec, + MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL, 0x01, 0x01); + snd_soc_update_bits(codec, + MSM89XX_PMIC_DIGITAL_CDC_ANA_CLK_CTL, 0x02, 0x02); + if (!ret) + wcd_imped_config(codec, impedl, true); + else + dev_dbg(codec->dev, "Failed to get mbhc impedance %d\n", + ret); + break; + case SND_SOC_DAPM_POST_PMU: + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_RX_HPH_L_PA_DAC_CTL, 0x02, 0x00); + break; + case SND_SOC_DAPM_POST_PMD: + wcd_imped_config(codec, impedl, false); + snd_soc_update_bits(codec, + MSM89XX_PMIC_DIGITAL_CDC_ANA_CLK_CTL, 0x02, 0x00); + snd_soc_update_bits(codec, + MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL, 0x01, 0x00); + if (sdm660_cdc->hph_mode == HD2_MODE) + msm_anlg_cdc_dig_notifier_call(codec, + DIG_CDC_EVENT_POST_RX1_INT_OFF); + break; + } + return 0; +} + +static int msm_anlg_cdc_lo_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + + dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_update_bits(codec, + MSM89XX_PMIC_DIGITAL_CDC_ANA_CLK_CTL, 0x10, 0x10); + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_RX_LO_EN_CTL, 0x20, 0x20); + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_RX_LO_EN_CTL, 0x80, 0x80); + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_RX_LO_DAC_CTL, 0x08, 0x08); + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_RX_LO_DAC_CTL, 0x40, 0x40); + break; + case SND_SOC_DAPM_POST_PMU: + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_RX_LO_DAC_CTL, 0x80, 0x80); + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_RX_LO_DAC_CTL, 0x08, 0x00); + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_RX_LO_EN_CTL, 0x40, 0x40); + break; + case SND_SOC_DAPM_POST_PMD: + /* Wait for 20ms before powerdown of lineout_dac */ + usleep_range(20000, 20100); + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_RX_LO_DAC_CTL, 0x80, 0x00); + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_RX_LO_DAC_CTL, 0x40, 0x00); + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_RX_LO_DAC_CTL, 0x08, 0x00); + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_RX_LO_EN_CTL, 0x80, 0x00); + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_RX_LO_EN_CTL, 0x40, 0x00); + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_RX_LO_EN_CTL, 0x20, 0x00); + snd_soc_update_bits(codec, + MSM89XX_PMIC_DIGITAL_CDC_ANA_CLK_CTL, 0x10, 0x00); + break; + } + return 0; +} + +static int msm_anlg_cdc_hphr_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct sdm660_cdc_priv *sdm660_cdc = + snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (sdm660_cdc->hph_mode == HD2_MODE) + msm_anlg_cdc_dig_notifier_call(codec, + DIG_CDC_EVENT_PRE_RX2_INT_ON); + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_RX_HPH_R_PA_DAC_CTL, 0x02, 0x02); + snd_soc_update_bits(codec, + MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL, 0x02, 0x02); + snd_soc_update_bits(codec, + MSM89XX_PMIC_DIGITAL_CDC_ANA_CLK_CTL, 0x01, 0x01); + break; + case SND_SOC_DAPM_POST_PMU: + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_RX_HPH_R_PA_DAC_CTL, 0x02, 0x00); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_update_bits(codec, + MSM89XX_PMIC_DIGITAL_CDC_ANA_CLK_CTL, 0x01, 0x00); + snd_soc_update_bits(codec, + MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL, 0x02, 0x00); + if (sdm660_cdc->hph_mode == HD2_MODE) + msm_anlg_cdc_dig_notifier_call(codec, + DIG_CDC_EVENT_POST_RX2_INT_OFF); + break; + } + return 0; +} + +static int msm_anlg_cdc_hph_pa_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct sdm660_cdc_priv *sdm660_cdc = + snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, "%s: %s event = %d\n", __func__, w->name, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (w->shift == 5) + msm_anlg_cdc_notifier_call(codec, + WCD_EVENT_PRE_HPHL_PA_ON); + else if (w->shift == 4) + msm_anlg_cdc_notifier_call(codec, + WCD_EVENT_PRE_HPHR_PA_ON); + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_NCP_FBCTRL, 0x20, 0x20); + break; + + case SND_SOC_DAPM_POST_PMU: + /* Wait for 7ms to allow setting time for HPH_PA Enable */ + usleep_range(7000, 7100); + if (w->shift == 5) { + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_RX_HPH_L_TEST, 0x04, 0x04); + msm_anlg_cdc_dig_notifier_call(codec, + DIG_CDC_EVENT_RX1_MUTE_OFF); + } else if (w->shift == 4) { + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_RX_HPH_R_TEST, 0x04, 0x04); + msm_anlg_cdc_dig_notifier_call(codec, + DIG_CDC_EVENT_RX2_MUTE_OFF); + } + break; + + case SND_SOC_DAPM_PRE_PMD: + if (w->shift == 5) { + msm_anlg_cdc_dig_notifier_call(codec, + DIG_CDC_EVENT_RX1_MUTE_ON); + /* Wait for 20ms after HPHL RX digital mute */ + msleep(20); + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_RX_HPH_L_TEST, 0x04, 0x00); + msm_anlg_cdc_notifier_call(codec, + WCD_EVENT_PRE_HPHL_PA_OFF); + } else if (w->shift == 4) { + msm_anlg_cdc_dig_notifier_call(codec, + DIG_CDC_EVENT_RX2_MUTE_ON); + /* Wait for 20ms after HPHR RX digital mute */ + msleep(20); + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_RX_HPH_R_TEST, 0x04, 0x00); + msm_anlg_cdc_notifier_call(codec, + WCD_EVENT_PRE_HPHR_PA_OFF); + } + if (get_codec_version(sdm660_cdc) >= CAJON) { + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_RX_HPH_BIAS_CNP, + 0xF0, 0x30); + } + break; + case SND_SOC_DAPM_POST_PMD: + if (w->shift == 5) { + clear_bit(WCD_MBHC_HPHL_PA_OFF_ACK, + &sdm660_cdc->mbhc.hph_pa_dac_state); + msm_anlg_cdc_notifier_call(codec, + WCD_EVENT_POST_HPHL_PA_OFF); + } else if (w->shift == 4) { + clear_bit(WCD_MBHC_HPHR_PA_OFF_ACK, + &sdm660_cdc->mbhc.hph_pa_dac_state); + msm_anlg_cdc_notifier_call(codec, + WCD_EVENT_POST_HPHR_PA_OFF); + } + /* Wait for 15ms after HPH RX teardown */ + usleep_range(15000, 15100); + break; + } + return 0; +} + +static const struct snd_soc_dapm_route audio_map[] = { + /* RDAC Connections */ + {"HPHR DAC", NULL, "RDAC2 MUX"}, + {"RDAC2 MUX", "RX1", "PDM_IN_RX1"}, + {"RDAC2 MUX", "RX2", "PDM_IN_RX2"}, + + /* WSA */ + {"WSA_SPK OUT", NULL, "WSA Spk Switch"}, + {"WSA Spk Switch", "WSA", "EAR PA"}, + + /* Earpiece (RX MIX1) */ + {"EAR", NULL, "EAR_S"}, + {"EAR_S", "Switch", "EAR PA"}, + {"EAR PA", NULL, "RX_BIAS"}, + {"EAR PA", NULL, "HPHL DAC"}, + {"EAR PA", NULL, "HPHR DAC"}, + {"EAR PA", NULL, "EAR CP"}, + + /* Headset (RX MIX1 and RX MIX2) */ + {"HEADPHONE", NULL, "HPHL PA"}, + {"HEADPHONE", NULL, "HPHR PA"}, + + {"Ext Spk", NULL, "Ext Spk Switch"}, + {"Ext Spk Switch", "On", "HPHL PA"}, + {"Ext Spk Switch", "On", "HPHR PA"}, + + {"HPHL PA", NULL, "HPHL"}, + {"HPHR PA", NULL, "HPHR"}, + {"HPHL", "Switch", "HPHL DAC"}, + {"HPHR", "Switch", "HPHR DAC"}, + {"HPHL PA", NULL, "CP"}, + {"HPHL PA", NULL, "RX_BIAS"}, + {"HPHR PA", NULL, "CP"}, + {"HPHR PA", NULL, "RX_BIAS"}, + {"HPHL DAC", NULL, "PDM_IN_RX1"}, + + {"SPK_OUT", NULL, "SPK PA"}, + {"SPK PA", NULL, "SPK_RX_BIAS"}, + {"SPK PA", NULL, "SPK"}, + {"SPK", "Switch", "SPK DAC"}, + {"SPK DAC", NULL, "PDM_IN_RX3"}, + {"SPK DAC", NULL, "VDD_SPKDRV"}, + + /* lineout */ + {"LINEOUT", NULL, "LINEOUT PA"}, + {"LINEOUT PA", NULL, "SPK_RX_BIAS"}, + {"LINEOUT PA", NULL, "LINE_OUT"}, + {"LINE_OUT", "Switch", "LINEOUT DAC"}, + {"LINEOUT DAC", NULL, "PDM_IN_RX3"}, + + /* lineout to WSA */ + {"WSA_SPK OUT", NULL, "LINEOUT PA"}, + + {"PDM_IN_RX1", NULL, "RX1 CLK"}, + {"PDM_IN_RX2", NULL, "RX2 CLK"}, + {"PDM_IN_RX3", NULL, "RX3 CLK"}, + + {"ADC1_OUT", NULL, "ADC1"}, + {"ADC2_OUT", NULL, "ADC2"}, + {"ADC3_OUT", NULL, "ADC3"}, + + /* ADC Connections */ + {"ADC2", NULL, "ADC2 MUX"}, + {"ADC3", NULL, "ADC2 MUX"}, + {"ADC2 MUX", "INP2", "ADC2_INP2"}, + {"ADC2 MUX", "INP3", "ADC2_INP3"}, + + {"ADC1", NULL, "ADC1_INP1"}, + {"ADC1_INP1", "Switch", "AMIC1"}, + {"ADC2_INP2", NULL, "AMIC2"}, + {"ADC2_INP3", NULL, "AMIC3"}, + + {"MIC BIAS Internal1", NULL, "INT_LDO_H"}, + {"MIC BIAS Internal2", NULL, "INT_LDO_H"}, + {"MIC BIAS External", NULL, "INT_LDO_H"}, + {"MIC BIAS External2", NULL, "INT_LDO_H"}, + {"MIC BIAS Internal1", NULL, "MICBIAS_REGULATOR"}, + {"MIC BIAS Internal2", NULL, "MICBIAS_REGULATOR"}, + {"MIC BIAS External", NULL, "MICBIAS_REGULATOR"}, + {"MIC BIAS External2", NULL, "MICBIAS_REGULATOR"}, +}; + +static int msm_anlg_cdc_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct sdm660_cdc_priv *sdm660_cdc = + snd_soc_codec_get_drvdata(dai->codec); + + dev_dbg(dai->codec->dev, "%s(): substream = %s stream = %d\n", + __func__, + substream->name, substream->stream); + /* + * If status_mask is BUS_DOWN it means SSR is not complete. + * So return error. + */ + if (test_bit(BUS_DOWN, &sdm660_cdc->status_mask)) { + dev_err(dai->codec->dev, "Error, Device is not up post SSR\n"); + return -EINVAL; + } + return 0; +} + +static void msm_anlg_cdc_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + dev_dbg(dai->codec->dev, + "%s(): substream = %s stream = %d\n", __func__, + substream->name, substream->stream); +} + +int msm_anlg_cdc_mclk_enable(struct snd_soc_codec *codec, + int mclk_enable, bool dapm) +{ + struct sdm660_cdc_priv *sdm660_cdc = + snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, "%s: mclk_enable = %u, dapm = %d\n", + __func__, mclk_enable, dapm); + if (mclk_enable) { + sdm660_cdc->int_mclk0_enabled = true; + msm_anlg_cdc_codec_enable_clock_block(codec, 1); + } else { + if (!sdm660_cdc->int_mclk0_enabled) { + dev_err(codec->dev, "Error, MCLK already diabled\n"); + return -EINVAL; + } + sdm660_cdc->int_mclk0_enabled = false; + msm_anlg_cdc_codec_enable_clock_block(codec, 0); + } + return 0; +} +EXPORT_SYMBOL(msm_anlg_cdc_mclk_enable); + +static int msm_anlg_cdc_set_dai_sysclk(struct snd_soc_dai *dai, + int clk_id, unsigned int freq, int dir) +{ + dev_dbg(dai->codec->dev, "%s\n", __func__); + return 0; +} + +static int msm_anlg_cdc_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + dev_dbg(dai->codec->dev, "%s\n", __func__); + return 0; +} + +static int msm_anlg_cdc_set_channel_map(struct snd_soc_dai *dai, + unsigned int tx_num, unsigned int *tx_slot, + unsigned int rx_num, unsigned int *rx_slot) + +{ + dev_dbg(dai->codec->dev, "%s\n", __func__); + return 0; +} + +static int msm_anlg_cdc_get_channel_map(struct snd_soc_dai *dai, + unsigned int *tx_num, unsigned int *tx_slot, + unsigned int *rx_num, unsigned int *rx_slot) + +{ + dev_dbg(dai->codec->dev, "%s\n", __func__); + return 0; +} + +static struct snd_soc_dai_ops msm_anlg_cdc_dai_ops = { + .startup = msm_anlg_cdc_startup, + .shutdown = msm_anlg_cdc_shutdown, + .set_sysclk = msm_anlg_cdc_set_dai_sysclk, + .set_fmt = msm_anlg_cdc_set_dai_fmt, + .set_channel_map = msm_anlg_cdc_set_channel_map, + .get_channel_map = msm_anlg_cdc_get_channel_map, +}; + +static struct snd_soc_dai_driver msm_anlg_cdc_i2s_dai[] = { + { + .name = "msm_anlg_cdc_i2s_rx1", + .id = AIF1_PB, + .playback = { + .stream_name = "PDM Playback", + .rates = SDM660_CDC_RATES, + .formats = SDM660_CDC_FORMATS, + .rate_max = 192000, + .rate_min = 8000, + .channels_min = 1, + .channels_max = 3, + }, + .ops = &msm_anlg_cdc_dai_ops, + }, + { + .name = "msm_anlg_cdc_i2s_tx1", + .id = AIF1_CAP, + .capture = { + .stream_name = "PDM Capture", + .rates = SDM660_CDC_RATES, + .formats = SDM660_CDC_FORMATS, + .rate_max = 48000, + .rate_min = 8000, + .channels_min = 1, + .channels_max = 4, + }, + .ops = &msm_anlg_cdc_dai_ops, + }, + { + .name = "msm_anlg_cdc_i2s_tx2", + .id = AIF3_SVA, + .capture = { + .stream_name = "RecordSVA", + .rates = SDM660_CDC_RATES, + .formats = SDM660_CDC_FORMATS, + .rate_max = 48000, + .rate_min = 8000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &msm_anlg_cdc_dai_ops, + }, + { + .name = "msm_anlg_vifeedback", + .id = AIF2_VIFEED, + .capture = { + .stream_name = "VIfeed", + .rates = SDM660_CDC_RATES, + .formats = SDM660_CDC_FORMATS, + .rate_max = 48000, + .rate_min = 48000, + .channels_min = 2, + .channels_max = 2, + }, + .ops = &msm_anlg_cdc_dai_ops, + }, +}; + + +static int msm_anlg_cdc_codec_enable_lo_pa(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + + dev_dbg(codec->dev, "%s: %d %s\n", __func__, event, w->name); + switch (event) { + case SND_SOC_DAPM_POST_PMU: + msm_anlg_cdc_dig_notifier_call(codec, + DIG_CDC_EVENT_RX3_MUTE_OFF); + break; + case SND_SOC_DAPM_POST_PMD: + msm_anlg_cdc_dig_notifier_call(codec, + DIG_CDC_EVENT_RX3_MUTE_ON); + break; + } + + return 0; +} + +static int msm_anlg_cdc_codec_enable_spk_ext_pa(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct sdm660_cdc_priv *sdm660_cdc = + snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, "%s: %s event = %d\n", __func__, w->name, event); + switch (event) { + case SND_SOC_DAPM_POST_PMU: + dev_dbg(codec->dev, + "%s: enable external speaker PA\n", __func__); + if (sdm660_cdc->codec_spk_ext_pa_cb) + sdm660_cdc->codec_spk_ext_pa_cb(codec, 1); + break; + case SND_SOC_DAPM_PRE_PMD: + dev_dbg(codec->dev, + "%s: enable external speaker PA\n", __func__); + if (sdm660_cdc->codec_spk_ext_pa_cb) + sdm660_cdc->codec_spk_ext_pa_cb(codec, 0); + break; + } + return 0; +} + +static int msm_anlg_cdc_codec_enable_ear_pa(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct sdm660_cdc_priv *sdm660_cdc = + snd_soc_codec_get_drvdata(codec); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + dev_dbg(codec->dev, + "%s: Sleeping 20ms after select EAR PA\n", + __func__); + snd_soc_update_bits(codec, MSM89XX_PMIC_ANALOG_RX_EAR_CTL, + 0x80, 0x80); + if (get_codec_version(sdm660_cdc) < CONGA) + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_RX_HPH_CNP_WG_TIME, 0xFF, 0x2A); + if (get_codec_version(sdm660_cdc) >= DIANGU) { + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_RX_COM_BIAS_DAC, 0x08, 0x00); + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_RX_HPH_L_TEST, 0x04, 0x04); + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_RX_HPH_R_TEST, 0x04, 0x04); + } + break; + case SND_SOC_DAPM_POST_PMU: + dev_dbg(codec->dev, + "%s: Sleeping 20ms after enabling EAR PA\n", + __func__); + snd_soc_update_bits(codec, MSM89XX_PMIC_ANALOG_RX_EAR_CTL, + 0x40, 0x40); + /* Wait for 7ms after EAR PA enable */ + usleep_range(7000, 7100); + msm_anlg_cdc_dig_notifier_call(codec, + DIG_CDC_EVENT_RX1_MUTE_OFF); + break; + case SND_SOC_DAPM_PRE_PMD: + msm_anlg_cdc_dig_notifier_call(codec, + DIG_CDC_EVENT_RX1_MUTE_ON); + /* Wait for 20ms for RX digital mute to take effect */ + msleep(20); + if (sdm660_cdc->boost_option == BOOST_ALWAYS) { + dev_dbg(codec->dev, + "%s: boost_option:%d, tear down ear\n", + __func__, sdm660_cdc->boost_option); + msm_anlg_cdc_boost_mode_sequence(codec, EAR_PMD); + } + if (get_codec_version(sdm660_cdc) >= DIANGU) { + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_RX_HPH_L_TEST, 0x04, 0x0); + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_RX_HPH_R_TEST, 0x04, 0x0); + } + break; + case SND_SOC_DAPM_POST_PMD: + dev_dbg(codec->dev, + "%s: Sleeping 7ms after disabling EAR PA\n", + __func__); + snd_soc_update_bits(codec, MSM89XX_PMIC_ANALOG_RX_EAR_CTL, + 0x40, 0x00); + /* Wait for 7ms after EAR PA teardown */ + usleep_range(7000, 7100); + if (get_codec_version(sdm660_cdc) < CONGA) + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_RX_HPH_CNP_WG_TIME, 0xFF, 0x16); + if (get_codec_version(sdm660_cdc) >= DIANGU) + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_RX_COM_BIAS_DAC, 0x08, 0x08); + break; + } + return 0; +} + +static const struct snd_soc_dapm_widget msm_anlg_cdc_dapm_widgets[] = { + SND_SOC_DAPM_PGA_E("EAR PA", SND_SOC_NOPM, + 0, 0, NULL, 0, msm_anlg_cdc_codec_enable_ear_pa, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("HPHL PA", MSM89XX_PMIC_ANALOG_RX_HPH_CNP_EN, + 5, 0, NULL, 0, + msm_anlg_cdc_hph_pa_event, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("HPHR PA", MSM89XX_PMIC_ANALOG_RX_HPH_CNP_EN, + 4, 0, NULL, 0, + msm_anlg_cdc_hph_pa_event, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("SPK PA", SND_SOC_NOPM, + 0, 0, NULL, 0, msm_anlg_cdc_codec_enable_spk_pa, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("LINEOUT PA", MSM89XX_PMIC_ANALOG_RX_LO_EN_CTL, + 5, 0, NULL, 0, msm_anlg_cdc_codec_enable_lo_pa, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX("EAR_S", SND_SOC_NOPM, 0, 0, ear_pa_mux), + SND_SOC_DAPM_MUX("SPK", SND_SOC_NOPM, 0, 0, spkr_mux), + SND_SOC_DAPM_MUX("HPHL", SND_SOC_NOPM, 0, 0, hphl_mux), + SND_SOC_DAPM_MUX("HPHR", SND_SOC_NOPM, 0, 0, hphr_mux), + SND_SOC_DAPM_MUX("RDAC2 MUX", SND_SOC_NOPM, 0, 0, &rdac2_mux), + SND_SOC_DAPM_MUX("WSA Spk Switch", SND_SOC_NOPM, 0, 0, wsa_spk_mux), + SND_SOC_DAPM_MUX("Ext Spk Switch", SND_SOC_NOPM, 0, 0, &ext_spk_mux), + SND_SOC_DAPM_MUX("LINE_OUT", SND_SOC_NOPM, 0, 0, lo_mux), + SND_SOC_DAPM_MUX("ADC2 MUX", SND_SOC_NOPM, 0, 0, &tx_adc2_mux), + + SND_SOC_DAPM_MIXER_E("HPHL DAC", + MSM89XX_PMIC_ANALOG_RX_HPH_L_PA_DAC_CTL, 3, 0, NULL, + 0, msm_anlg_cdc_hphl_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("HPHR DAC", + MSM89XX_PMIC_ANALOG_RX_HPH_R_PA_DAC_CTL, 3, 0, NULL, + 0, msm_anlg_cdc_hphr_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER("ADC2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("ADC3", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_DAC("SPK DAC", NULL, MSM89XX_PMIC_ANALOG_SPKR_DAC_CTL, + 7, 0), + SND_SOC_DAPM_DAC_E("LINEOUT DAC", NULL, + SND_SOC_NOPM, 0, 0, msm_anlg_cdc_lo_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_SPK("Ext Spk", msm_anlg_cdc_codec_enable_spk_ext_pa), + + SND_SOC_DAPM_SWITCH("ADC1_INP1", SND_SOC_NOPM, 0, 0, + &adc1_switch), + SND_SOC_DAPM_SUPPLY("RX1 CLK", MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL, + 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("RX2 CLK", MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL, + 1, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("RX3 CLK", MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL, + 2, 0, msm_anlg_cdc_codec_enable_dig_clk, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("CP", MSM89XX_PMIC_ANALOG_NCP_EN, 0, 0, + msm_anlg_cdc_codec_enable_charge_pump, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("EAR CP", MSM89XX_PMIC_ANALOG_NCP_EN, 4, 0, + msm_anlg_cdc_codec_enable_charge_pump, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY_S("RX_BIAS", 1, SND_SOC_NOPM, + 0, 0, msm_anlg_cdc_codec_enable_rx_bias, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY_S("SPK_RX_BIAS", 1, SND_SOC_NOPM, 0, 0, + msm_anlg_cdc_codec_enable_rx_bias, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("VDD_SPKDRV", SND_SOC_NOPM, 0, 0, + sdm660_wcd_codec_enable_vdd_spkr, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("INT_LDO_H", SND_SOC_NOPM, 1, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("MICBIAS_REGULATOR", SND_SOC_NOPM, + ON_DEMAND_MICBIAS, 0, + msm_anlg_cdc_codec_enable_on_demand_supply, + SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MICBIAS_E("MIC BIAS Internal1", + MSM89XX_PMIC_ANALOG_MICB_1_EN, 7, 0, + msm_anlg_cdc_codec_enable_micbias, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MICBIAS_E("MIC BIAS Internal2", + MSM89XX_PMIC_ANALOG_MICB_2_EN, 7, 0, + msm_anlg_cdc_codec_enable_micbias, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MICBIAS_E("MIC BIAS Internal3", + MSM89XX_PMIC_ANALOG_MICB_1_EN, 7, 0, + msm_anlg_cdc_codec_enable_micbias, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_ADC_E("ADC1", NULL, MSM89XX_PMIC_ANALOG_TX_1_EN, 7, 0, + msm_anlg_cdc_codec_enable_adc, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("ADC2_INP2", + NULL, MSM89XX_PMIC_ANALOG_TX_2_EN, 7, 0, + msm_anlg_cdc_codec_enable_adc, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("ADC2_INP3", + NULL, MSM89XX_PMIC_ANALOG_TX_3_EN, 7, 0, + msm_anlg_cdc_codec_enable_adc, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MICBIAS_E("MIC BIAS External", + MSM89XX_PMIC_ANALOG_MICB_1_EN, 7, 0, + msm_anlg_cdc_codec_enable_micbias, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MICBIAS_E("MIC BIAS External2", + MSM89XX_PMIC_ANALOG_MICB_2_EN, 7, 0, + msm_anlg_cdc_codec_enable_micbias, SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_INPUT("AMIC1"), + SND_SOC_DAPM_INPUT("AMIC2"), + SND_SOC_DAPM_INPUT("AMIC3"), + SND_SOC_DAPM_AIF_IN("PDM_IN_RX1", "PDM Playback", + 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("PDM_IN_RX2", "PDM Playback", + 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("PDM_IN_RX3", "PDM Playback", + 0, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_OUTPUT("EAR"), + SND_SOC_DAPM_OUTPUT("WSA_SPK OUT"), + SND_SOC_DAPM_OUTPUT("HEADPHONE"), + SND_SOC_DAPM_OUTPUT("SPK_OUT"), + SND_SOC_DAPM_OUTPUT("LINEOUT"), + SND_SOC_DAPM_AIF_OUT("ADC1_OUT", "PDM Capture", + 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("ADC2_OUT", "PDM Capture", + 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("ADC3_OUT", "PDM Capture", + 0, SND_SOC_NOPM, 0, 0), +}; + +static const struct sdm660_cdc_reg_mask_val msm_anlg_cdc_reg_defaults[] = { + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SPKR_DAC_CTL, 0x03), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_CURRENT_LIMIT, 0x82), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SPKR_OCP_CTL, 0xE1), +}; + +static const struct sdm660_cdc_reg_mask_val + msm_anlg_cdc_reg_defaults_2_0[] = { + MSM89XX_REG_VAL(MSM89XX_PMIC_DIGITAL_SEC_ACCESS, 0xA5), + MSM89XX_REG_VAL(MSM89XX_PMIC_DIGITAL_PERPH_RESET_CTL3, 0x0F), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_TX_1_2_OPAMP_BIAS, 0x4F), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_NCP_FBCTRL, 0x28), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SPKR_DRV_CTL, 0x69), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SPKR_DRV_DBG, 0x01), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_BOOST_EN_CTL, 0x5F), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SLOPE_COMP_IP_ZERO, 0x88), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SEC_ACCESS, 0xA5), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_PERPH_RESET_CTL3, 0x0F), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_CURRENT_LIMIT, 0x82), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SPKR_DAC_CTL, 0x03), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SPKR_OCP_CTL, 0xE1), + MSM89XX_REG_VAL(MSM89XX_PMIC_DIGITAL_CDC_RST_CTL, 0x80), +}; + +static const struct sdm660_cdc_reg_mask_val conga_wcd_reg_defaults[] = { + MSM89XX_REG_VAL(MSM89XX_PMIC_DIGITAL_SEC_ACCESS, 0xA5), + MSM89XX_REG_VAL(MSM89XX_PMIC_DIGITAL_PERPH_RESET_CTL3, 0x0F), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SEC_ACCESS, 0xA5), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_PERPH_RESET_CTL3, 0x0F), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_TX_1_2_OPAMP_BIAS, 0x4C), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_NCP_FBCTRL, 0x28), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SPKR_DRV_CTL, 0x69), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SPKR_DRV_DBG, 0x01), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_PERPH_SUBTYPE, 0x0A), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SPKR_DAC_CTL, 0x03), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SPKR_OCP_CTL, 0xE1), + MSM89XX_REG_VAL(MSM89XX_PMIC_DIGITAL_CDC_RST_CTL, 0x80), +}; + +static const struct sdm660_cdc_reg_mask_val cajon_wcd_reg_defaults[] = { + MSM89XX_REG_VAL(MSM89XX_PMIC_DIGITAL_SEC_ACCESS, 0xA5), + MSM89XX_REG_VAL(MSM89XX_PMIC_DIGITAL_PERPH_RESET_CTL3, 0x0F), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SEC_ACCESS, 0xA5), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_PERPH_RESET_CTL3, 0x0F), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_TX_1_2_OPAMP_BIAS, 0x4C), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_CURRENT_LIMIT, 0x82), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_NCP_FBCTRL, 0xA8), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_NCP_VCTRL, 0xA4), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SPKR_ANA_BIAS_SET, 0x41), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SPKR_DRV_CTL, 0x69), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SPKR_DRV_DBG, 0x01), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SPKR_OCP_CTL, 0xE1), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SPKR_DAC_CTL, 0x03), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_RX_HPH_BIAS_PA, 0xFA), + MSM89XX_REG_VAL(MSM89XX_PMIC_DIGITAL_CDC_RST_CTL, 0x80), +}; + +static const struct sdm660_cdc_reg_mask_val cajon2p0_wcd_reg_defaults[] = { + MSM89XX_REG_VAL(MSM89XX_PMIC_DIGITAL_SEC_ACCESS, 0xA5), + MSM89XX_REG_VAL(MSM89XX_PMIC_DIGITAL_PERPH_RESET_CTL3, 0x0F), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SEC_ACCESS, 0xA5), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_PERPH_RESET_CTL3, 0x0F), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_TX_1_2_OPAMP_BIAS, 0x4C), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_CURRENT_LIMIT, 0xA2), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_NCP_FBCTRL, 0xA8), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_NCP_VCTRL, 0xA4), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SPKR_ANA_BIAS_SET, 0x41), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SPKR_DRV_CTL, 0x69), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SPKR_DRV_DBG, 0x01), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SPKR_OCP_CTL, 0xE1), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SPKR_DAC_CTL, 0x03), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_RX_EAR_STATUS, 0x10), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_BYPASS_MODE, 0x18), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_RX_HPH_BIAS_PA, 0xFA), + MSM89XX_REG_VAL(MSM89XX_PMIC_DIGITAL_CDC_RST_CTL, 0x80), +}; + +static void msm_anlg_cdc_update_reg_defaults(struct snd_soc_codec *codec) +{ + u32 i, version; + struct sdm660_cdc_priv *sdm660_cdc = + snd_soc_codec_get_drvdata(codec); + + version = get_codec_version(sdm660_cdc); + if (version == TOMBAK_1_0) { + for (i = 0; i < ARRAY_SIZE(msm_anlg_cdc_reg_defaults); i++) + snd_soc_write(codec, msm_anlg_cdc_reg_defaults[i].reg, + msm_anlg_cdc_reg_defaults[i].val); + } else if (version == TOMBAK_2_0) { + for (i = 0; i < ARRAY_SIZE(msm_anlg_cdc_reg_defaults_2_0); i++) + snd_soc_write(codec, + msm_anlg_cdc_reg_defaults_2_0[i].reg, + msm_anlg_cdc_reg_defaults_2_0[i].val); + } else if (version == CONGA) { + for (i = 0; i < ARRAY_SIZE(conga_wcd_reg_defaults); i++) + snd_soc_write(codec, + conga_wcd_reg_defaults[i].reg, + conga_wcd_reg_defaults[i].val); + } else if (version == CAJON) { + for (i = 0; i < ARRAY_SIZE(cajon_wcd_reg_defaults); i++) + snd_soc_write(codec, + cajon_wcd_reg_defaults[i].reg, + cajon_wcd_reg_defaults[i].val); + } else if (version == CAJON_2_0 || version == DIANGU + || version == DRAX_CDC) { + for (i = 0; i < ARRAY_SIZE(cajon2p0_wcd_reg_defaults); i++) + snd_soc_write(codec, + cajon2p0_wcd_reg_defaults[i].reg, + cajon2p0_wcd_reg_defaults[i].val); + } +} + +static const struct sdm660_cdc_reg_mask_val + msm_anlg_cdc_codec_reg_init_val[] = { + + /* Initialize current threshold to 350MA + * number of wait and run cycles to 4096 + */ + {MSM89XX_PMIC_ANALOG_RX_COM_OCP_CTL, 0xFF, 0x12}, + {MSM89XX_PMIC_ANALOG_RX_COM_OCP_COUNT, 0xFF, 0xFF}, +}; + +static void msm_anlg_cdc_codec_init_reg(struct snd_soc_codec *codec) +{ + u32 i; + + for (i = 0; i < ARRAY_SIZE(msm_anlg_cdc_codec_reg_init_val); i++) + snd_soc_update_bits(codec, + msm_anlg_cdc_codec_reg_init_val[i].reg, + msm_anlg_cdc_codec_reg_init_val[i].mask, + msm_anlg_cdc_codec_reg_init_val[i].val); +} + +static int msm_anlg_cdc_bringup(struct snd_soc_codec *codec) +{ + snd_soc_write(codec, + MSM89XX_PMIC_DIGITAL_SEC_ACCESS, + 0xA5); + snd_soc_write(codec, MSM89XX_PMIC_DIGITAL_PERPH_RESET_CTL4, 0x01); + snd_soc_write(codec, + MSM89XX_PMIC_ANALOG_SEC_ACCESS, + 0xA5); + snd_soc_write(codec, MSM89XX_PMIC_ANALOG_PERPH_RESET_CTL4, 0x01); + snd_soc_write(codec, + MSM89XX_PMIC_DIGITAL_SEC_ACCESS, + 0xA5); + snd_soc_write(codec, MSM89XX_PMIC_DIGITAL_PERPH_RESET_CTL4, 0x00); + snd_soc_write(codec, + MSM89XX_PMIC_ANALOG_SEC_ACCESS, + 0xA5); + snd_soc_write(codec, MSM89XX_PMIC_ANALOG_PERPH_RESET_CTL4, 0x00); + + return 0; +} + +static struct regulator *msm_anlg_cdc_find_regulator( + const struct sdm660_cdc_priv *sdm660_cdc, + const char *name) +{ + int i; + + for (i = 0; i < sdm660_cdc->num_of_supplies; i++) { + if (sdm660_cdc->supplies[i].supply && + !strcmp(sdm660_cdc->supplies[i].supply, name)) + return sdm660_cdc->supplies[i].consumer; + } + + dev_dbg(sdm660_cdc->dev, "Error: regulator not found:%s\n" + , name); + return NULL; +} + +static void msm_anlg_cdc_update_micbias_regulator( + const struct sdm660_cdc_priv *sdm660_cdc, + const char *name, + struct on_demand_supply *micbias_supply) +{ + int i; + struct sdm660_cdc_pdata *pdata = sdm660_cdc->dev->platform_data; + + for (i = 0; i < sdm660_cdc->num_of_supplies; i++) { + if (sdm660_cdc->supplies[i].supply && + !strcmp(sdm660_cdc->supplies[i].supply, name)) { + micbias_supply->supply = + sdm660_cdc->supplies[i].consumer; + micbias_supply->min_uv = pdata->regulator[i].min_uv; + micbias_supply->max_uv = pdata->regulator[i].max_uv; + micbias_supply->optimum_ua = + pdata->regulator[i].optimum_ua; + return; + } + } + + dev_err(sdm660_cdc->dev, "Error: regulator not found:%s\n", name); +} + +static int msm_anlg_cdc_device_down(struct snd_soc_codec *codec) +{ + struct msm_asoc_mach_data *pdata = NULL; + struct sdm660_cdc_priv *sdm660_cdc_priv = + snd_soc_codec_get_drvdata(codec); + unsigned int tx_1_en; + unsigned int tx_2_en; + + pdata = snd_soc_card_get_drvdata(codec->component.card); + dev_dbg(codec->dev, "%s: device down!\n", __func__); + + tx_1_en = snd_soc_read(codec, MSM89XX_PMIC_ANALOG_TX_1_EN); + tx_2_en = snd_soc_read(codec, MSM89XX_PMIC_ANALOG_TX_2_EN); + tx_1_en = tx_1_en & 0x7f; + tx_2_en = tx_2_en & 0x7f; + snd_soc_write(codec, + MSM89XX_PMIC_ANALOG_TX_1_EN, tx_1_en); + snd_soc_write(codec, + MSM89XX_PMIC_ANALOG_TX_2_EN, tx_2_en); + if (sdm660_cdc_priv->boost_option == BOOST_ON_FOREVER) { + if ((snd_soc_read(codec, MSM89XX_PMIC_ANALOG_SPKR_DRV_CTL) + & 0x80) == 0) { + msm_anlg_cdc_dig_notifier_call(codec, + DIG_CDC_EVENT_CLK_ON); + snd_soc_write(codec, + MSM89XX_PMIC_ANALOG_MASTER_BIAS_CTL, 0x30); + snd_soc_update_bits(codec, + MSM89XX_PMIC_DIGITAL_CDC_RST_CTL, 0x80, 0x80); + snd_soc_update_bits(codec, + MSM89XX_PMIC_DIGITAL_CDC_TOP_CLK_CTL, + 0x0C, 0x0C); + snd_soc_update_bits(codec, + MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL, + 0x84, 0x84); + snd_soc_update_bits(codec, + MSM89XX_PMIC_DIGITAL_CDC_ANA_CLK_CTL, + 0x10, 0x10); + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_SPKR_PWRSTG_CTL, + 0x1F, 0x1F); + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_RX_COM_BIAS_DAC, + 0x90, 0x90); + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_RX_EAR_CTL, + 0xFF, 0xFF); + /* Wait for 20us for boost settings to take effect */ + usleep_range(20, 21); + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_SPKR_PWRSTG_CTL, + 0xFF, 0xFF); + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_SPKR_DRV_CTL, + 0xE9, 0xE9); + } + } + 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); + snd_soc_update_bits(codec, MSM89XX_PMIC_ANALOG_SPKR_DRV_CTL, + 0x80, 0x00); + snd_soc_write(codec, + MSM89XX_PMIC_ANALOG_RX_HPH_L_PA_DAC_CTL, 0x20); + snd_soc_write(codec, + MSM89XX_PMIC_ANALOG_RX_HPH_R_PA_DAC_CTL, 0x20); + snd_soc_write(codec, + MSM89XX_PMIC_ANALOG_RX_EAR_CTL, 0x12); + snd_soc_write(codec, + MSM89XX_PMIC_ANALOG_SPKR_DAC_CTL, 0x93); + + msm_anlg_cdc_dig_notifier_call(codec, DIG_CDC_EVENT_SSR_DOWN); + atomic_set(&pdata->int_mclk0_enabled, false); + set_bit(BUS_DOWN, &sdm660_cdc_priv->status_mask); + snd_soc_card_change_online_state(codec->component.card, 0); + + return 0; +} + +static int msm_anlg_cdc_device_up(struct snd_soc_codec *codec) +{ + struct sdm660_cdc_priv *sdm660_cdc_priv = + snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, "%s: device up!\n", __func__); + + msm_anlg_cdc_dig_notifier_call(codec, DIG_CDC_EVENT_SSR_UP); + clear_bit(BUS_DOWN, &sdm660_cdc_priv->status_mask); + snd_soc_card_change_online_state(codec->component.card, 1); + /* delay is required to make sure sound card state updated */ + usleep_range(5000, 5100); + + snd_soc_write(codec, MSM89XX_PMIC_DIGITAL_INT_EN_SET, + MSM89XX_PMIC_DIGITAL_INT_EN_SET__POR); + snd_soc_write(codec, MSM89XX_PMIC_DIGITAL_INT_EN_CLR, + MSM89XX_PMIC_DIGITAL_INT_EN_CLR__POR); + + msm_anlg_cdc_set_boost_v(codec); + msm_anlg_cdc_set_micb_v(codec); + if (sdm660_cdc_priv->boost_option == BOOST_ON_FOREVER) + msm_anlg_cdc_boost_on(codec); + else if (sdm660_cdc_priv->boost_option == BYPASS_ALWAYS) + msm_anlg_cdc_bypass_on(codec); + + return 0; +} + +static int sdm660_cdc_notifier_service_cb(struct notifier_block *nb, + unsigned long opcode, void *ptr) +{ + struct snd_soc_codec *codec; + struct sdm660_cdc_priv *sdm660_cdc_priv = + container_of(nb, struct sdm660_cdc_priv, + audio_ssr_nb); + bool adsp_ready = false; + bool timedout; + unsigned long timeout; + static bool initial_boot = true; + + codec = sdm660_cdc_priv->codec; + dev_dbg(codec->dev, "%s: Service opcode 0x%lx\n", __func__, opcode); + + switch (opcode) { + case AUDIO_NOTIFIER_SERVICE_DOWN: + if (initial_boot) { + initial_boot = false; + break; + } + dev_dbg(codec->dev, + "ADSP is about to power down. teardown/reset codec\n"); + msm_anlg_cdc_device_down(codec); + break; + case AUDIO_NOTIFIER_SERVICE_UP: + if (initial_boot) + initial_boot = false; + dev_dbg(codec->dev, + "ADSP is about to power up. bring up codec\n"); + + if (!q6core_is_adsp_ready()) { + dev_dbg(codec->dev, + "ADSP isn't ready\n"); + timeout = jiffies + + msecs_to_jiffies(ADSP_STATE_READY_TIMEOUT_MS); + while (!(timedout = time_after(jiffies, timeout))) { + if (!q6core_is_adsp_ready()) { + dev_dbg(codec->dev, + "ADSP isn't ready\n"); + } else { + dev_dbg(codec->dev, + "ADSP is ready\n"); + adsp_ready = true; + goto powerup; + } + } + } else { + adsp_ready = true; + dev_dbg(codec->dev, "%s: DSP is ready\n", __func__); + } +powerup: + if (adsp_ready) + msm_anlg_cdc_device_up(codec); + break; + default: + break; + } + return NOTIFY_OK; +} + +int msm_anlg_cdc_hs_detect(struct snd_soc_codec *codec, + struct wcd_mbhc_config *mbhc_cfg) +{ + struct sdm660_cdc_priv *sdm660_cdc_priv = + snd_soc_codec_get_drvdata(codec); + + return wcd_mbhc_start(&sdm660_cdc_priv->mbhc, mbhc_cfg); +} +EXPORT_SYMBOL(msm_anlg_cdc_hs_detect); + +void msm_anlg_cdc_hs_detect_exit(struct snd_soc_codec *codec) +{ + struct sdm660_cdc_priv *sdm660_cdc_priv = + snd_soc_codec_get_drvdata(codec); + + wcd_mbhc_stop(&sdm660_cdc_priv->mbhc); +} +EXPORT_SYMBOL(msm_anlg_cdc_hs_detect_exit); + +void msm_anlg_cdc_update_int_spk_boost(bool enable) +{ + pr_debug("%s: enable = %d\n", __func__, enable); + spkr_boost_en = enable; +} +EXPORT_SYMBOL(msm_anlg_cdc_update_int_spk_boost); + +static void msm_anlg_cdc_set_micb_v(struct snd_soc_codec *codec) +{ + + struct sdm660_cdc_priv *sdm660_cdc = snd_soc_codec_get_drvdata(codec); + struct sdm660_cdc_pdata *pdata = sdm660_cdc->dev->platform_data; + u8 reg_val; + + reg_val = VOLTAGE_CONVERTER(pdata->micbias.cfilt1_mv, MICBIAS_MIN_VAL, + MICBIAS_STEP_SIZE); + dev_dbg(codec->dev, "cfilt1_mv %d reg_val %x\n", + (u32)pdata->micbias.cfilt1_mv, reg_val); + snd_soc_update_bits(codec, MSM89XX_PMIC_ANALOG_MICB_1_VAL, + 0xF8, (reg_val << 3)); +} + +static void msm_anlg_cdc_set_boost_v(struct snd_soc_codec *codec) +{ + struct sdm660_cdc_priv *sdm660_cdc_priv = + snd_soc_codec_get_drvdata(codec); + + snd_soc_update_bits(codec, MSM89XX_PMIC_ANALOG_OUTPUT_VOLTAGE, + 0x1F, sdm660_cdc_priv->boost_voltage); +} + +static void msm_anlg_cdc_configure_cap(struct snd_soc_codec *codec, + bool micbias1, bool micbias2) +{ + + struct msm_asoc_mach_data *pdata = NULL; + + pdata = snd_soc_card_get_drvdata(codec->component.card); + + pr_debug("\n %s: micbias1 %x micbias2 = %d\n", __func__, micbias1, + micbias2); + if (micbias1 && micbias2) { + if ((pdata->micbias1_cap_mode + == MICBIAS_EXT_BYP_CAP) || + (pdata->micbias2_cap_mode + == MICBIAS_EXT_BYP_CAP)) + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_MICB_1_EN, + 0x40, (MICBIAS_EXT_BYP_CAP << 6)); + else + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_MICB_1_EN, + 0x40, (MICBIAS_NO_EXT_BYP_CAP << 6)); + } else if (micbias2) { + snd_soc_update_bits(codec, MSM89XX_PMIC_ANALOG_MICB_1_EN, + 0x40, (pdata->micbias2_cap_mode << 6)); + } else if (micbias1) { + snd_soc_update_bits(codec, MSM89XX_PMIC_ANALOG_MICB_1_EN, + 0x40, (pdata->micbias1_cap_mode << 6)); + } else { + snd_soc_update_bits(codec, MSM89XX_PMIC_ANALOG_MICB_1_EN, + 0x40, 0x00); + } +} + +static ssize_t msm_anlg_codec_version_read(struct snd_info_entry *entry, + void *file_private_data, + struct file *file, + char __user *buf, size_t count, + loff_t pos) +{ + struct sdm660_cdc_priv *sdm660_cdc_priv; + char buffer[MSM_ANLG_CDC_VERSION_ENTRY_SIZE]; + int len = 0; + + sdm660_cdc_priv = (struct sdm660_cdc_priv *) entry->private_data; + if (!sdm660_cdc_priv) { + pr_err("%s: sdm660_cdc_priv is null\n", __func__); + return -EINVAL; + } + + switch (get_codec_version(sdm660_cdc_priv)) { + case DRAX_CDC: + len = snprintf(buffer, sizeof(buffer), "DRAX-CDC_1_0\n"); + break; + default: + len = snprintf(buffer, sizeof(buffer), "VER_UNDEFINED\n"); + } + + return simple_read_from_buffer(buf, count, &pos, buffer, len); +} + +static struct snd_info_entry_ops msm_anlg_codec_info_ops = { + .read = msm_anlg_codec_version_read, +}; + +/* + * msm_anlg_codec_info_create_codec_entry - creates pmic_analog module + * @codec_root: The parent directory + * @codec: Codec instance + * + * Creates pmic_analog module and version entry under the given + * parent directory. + * + * Return: 0 on success or negative error code on failure. + */ +int msm_anlg_codec_info_create_codec_entry(struct snd_info_entry *codec_root, + struct snd_soc_codec *codec) +{ + struct snd_info_entry *version_entry; + struct sdm660_cdc_priv *sdm660_cdc_priv; + struct snd_soc_card *card; + int ret; + + if (!codec_root || !codec) + return -EINVAL; + + sdm660_cdc_priv = snd_soc_codec_get_drvdata(codec); + card = codec->component.card; + sdm660_cdc_priv->entry = snd_info_create_subdir(codec_root->module, + "spmi0-03", + codec_root); + if (!sdm660_cdc_priv->entry) { + dev_dbg(codec->dev, "%s: failed to create pmic_analog entry\n", + __func__); + return -ENOMEM; + } + + version_entry = snd_info_create_card_entry(card->snd_card, + "version", + sdm660_cdc_priv->entry); + if (!version_entry) { + dev_dbg(codec->dev, "%s: failed to create pmic_analog version entry\n", + __func__); + return -ENOMEM; + } + + version_entry->private_data = sdm660_cdc_priv; + version_entry->size = MSM_ANLG_CDC_VERSION_ENTRY_SIZE; + version_entry->content = SNDRV_INFO_CONTENT_DATA; + version_entry->c.ops = &msm_anlg_codec_info_ops; + + if (snd_info_register(version_entry) < 0) { + snd_info_free_entry(version_entry); + return -ENOMEM; + } + sdm660_cdc_priv->version_entry = version_entry; + + sdm660_cdc_priv->audio_ssr_nb.notifier_call = + sdm660_cdc_notifier_service_cb; + ret = audio_notifier_register("pmic_analog_cdc", + AUDIO_NOTIFIER_ADSP_DOMAIN, + &sdm660_cdc_priv->audio_ssr_nb); + if (ret < 0) { + pr_err("%s: Audio notifier register failed ret = %d\n", + __func__, ret); + return ret; + } + return 0; +} +EXPORT_SYMBOL(msm_anlg_codec_info_create_codec_entry); + +static int msm_anlg_cdc_soc_probe(struct snd_soc_codec *codec) +{ + struct sdm660_cdc_priv *sdm660_cdc; + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + int ret; + + sdm660_cdc = dev_get_drvdata(codec->dev); + sdm660_cdc->codec = codec; + + /* codec resmgr module init */ + sdm660_cdc->spkdrv_reg = + msm_anlg_cdc_find_regulator(sdm660_cdc, + MSM89XX_VDD_SPKDRV_NAME); + sdm660_cdc->pmic_rev = + snd_soc_read(codec, + MSM89XX_PMIC_DIGITAL_REVISION1); + sdm660_cdc->codec_version = + snd_soc_read(codec, + MSM89XX_PMIC_DIGITAL_PERPH_SUBTYPE); + sdm660_cdc->analog_major_rev = + snd_soc_read(codec, + MSM89XX_PMIC_ANALOG_REVISION4); + + if (sdm660_cdc->codec_version == CONGA) { + dev_dbg(codec->dev, "%s :Conga REV: %d\n", __func__, + sdm660_cdc->codec_version); + sdm660_cdc->ext_spk_boost_set = true; + } else { + dev_dbg(codec->dev, "%s :PMIC REV: %d\n", __func__, + sdm660_cdc->pmic_rev); + if (sdm660_cdc->pmic_rev == TOMBAK_1_0 && + sdm660_cdc->codec_version == CAJON_2_0) { + if (sdm660_cdc->analog_major_rev == 0x02) { + sdm660_cdc->codec_version = DRAX_CDC; + dev_dbg(codec->dev, + "%s : Drax codec detected\n", __func__); + } else { + sdm660_cdc->codec_version = DIANGU; + dev_dbg(codec->dev, "%s : Diangu detected\n", + __func__); + } + } else if (sdm660_cdc->pmic_rev == TOMBAK_1_0 && + (snd_soc_read(codec, MSM89XX_PMIC_ANALOG_NCP_FBCTRL) + & 0x80)) { + sdm660_cdc->codec_version = CAJON; + dev_dbg(codec->dev, "%s : Cajon detected\n", __func__); + } else if (sdm660_cdc->pmic_rev == TOMBAK_2_0 && + (snd_soc_read(codec, MSM89XX_PMIC_ANALOG_NCP_FBCTRL) + & 0x80)) { + sdm660_cdc->codec_version = CAJON_2_0; + dev_dbg(codec->dev, "%s : Cajon 2.0 detected\n", + __func__); + } + } + /* + * set to default boost option BOOST_SWITCH, user mixer path can change + * it to BOOST_ALWAYS or BOOST_BYPASS based on solution chosen. + */ + sdm660_cdc->boost_option = BOOST_SWITCH; + sdm660_cdc->hph_mode = NORMAL_MODE; + + msm_anlg_cdc_dt_parse_boost_info(codec); + msm_anlg_cdc_set_boost_v(codec); + + snd_soc_add_codec_controls(codec, impedance_detect_controls, + ARRAY_SIZE(impedance_detect_controls)); + snd_soc_add_codec_controls(codec, hph_type_detect_controls, + ARRAY_SIZE(hph_type_detect_controls)); + + msm_anlg_cdc_bringup(codec); + msm_anlg_cdc_codec_init_reg(codec); + msm_anlg_cdc_update_reg_defaults(codec); + + wcd9xxx_spmi_set_codec(codec); + + msm_anlg_cdc_update_micbias_regulator( + sdm660_cdc, + on_demand_supply_name[ON_DEMAND_MICBIAS], + &sdm660_cdc->on_demand_list[ON_DEMAND_MICBIAS]); + atomic_set(&sdm660_cdc->on_demand_list[ON_DEMAND_MICBIAS].ref, + 0); + + sdm660_cdc->fw_data = devm_kzalloc(codec->dev, + sizeof(*(sdm660_cdc->fw_data)), + GFP_KERNEL); + if (!sdm660_cdc->fw_data) + return -ENOMEM; + + set_bit(WCD9XXX_MBHC_CAL, sdm660_cdc->fw_data->cal_bit); + ret = wcd_cal_create_hwdep(sdm660_cdc->fw_data, + WCD9XXX_CODEC_HWDEP_NODE, codec); + if (ret < 0) { + dev_err(codec->dev, "%s hwdep failed %d\n", __func__, ret); + return ret; + } + + wcd_mbhc_init(&sdm660_cdc->mbhc, codec, &mbhc_cb, &intr_ids, + wcd_mbhc_registers, true); + + sdm660_cdc->int_mclk0_enabled = false; + /*Update speaker boost configuration*/ + sdm660_cdc->spk_boost_set = spkr_boost_en; + pr_debug("%s: speaker boost configured = %d\n", + __func__, sdm660_cdc->spk_boost_set); + + /* Set initial MICBIAS voltage level */ + msm_anlg_cdc_set_micb_v(codec); + + /* Set initial cap mode */ + msm_anlg_cdc_configure_cap(codec, false, false); + + snd_soc_dapm_ignore_suspend(dapm, "PDM Playback"); + snd_soc_dapm_ignore_suspend(dapm, "PDM Capture"); + + snd_soc_dapm_sync(dapm); + + return 0; +} + +static int msm_anlg_cdc_soc_remove(struct snd_soc_codec *codec) +{ + struct sdm660_cdc_priv *sdm660_cdc_priv = + dev_get_drvdata(codec->dev); + + sdm660_cdc_priv->spkdrv_reg = NULL; + sdm660_cdc_priv->on_demand_list[ON_DEMAND_MICBIAS].supply = NULL; + atomic_set(&sdm660_cdc_priv->on_demand_list[ON_DEMAND_MICBIAS].ref, + 0); + wcd_mbhc_deinit(&sdm660_cdc_priv->mbhc); + + return 0; +} + +static int msm_anlg_cdc_enable_static_supplies_to_optimum( + struct sdm660_cdc_priv *sdm660_cdc, + struct sdm660_cdc_pdata *pdata) +{ + int i; + int ret = 0, rc = 0; + + for (i = 0; i < sdm660_cdc->num_of_supplies; i++) { + if (pdata->regulator[i].ondemand) + continue; + if (regulator_count_voltages( + sdm660_cdc->supplies[i].consumer) <= 0) + continue; + + rc = regulator_enable(sdm660_cdc->supplies[i].consumer); + if (rc) { + dev_err(sdm660_cdc->dev, "Failed to enable %s: %d\n", + sdm660_cdc->supplies[i].supply, rc); + break; + } + ret = regulator_set_voltage( + sdm660_cdc->supplies[i].consumer, + pdata->regulator[i].min_uv, + pdata->regulator[i].max_uv); + if (ret) { + dev_err(sdm660_cdc->dev, + "Setting volt failed for regulator %s err %d\n", + sdm660_cdc->supplies[i].supply, ret); + } + + ret = regulator_set_load(sdm660_cdc->supplies[i].consumer, + pdata->regulator[i].optimum_ua); + dev_dbg(sdm660_cdc->dev, "Regulator %s set optimum mode\n", + sdm660_cdc->supplies[i].supply); + } + + while (rc && i--) + if (!pdata->regulator[i].ondemand) + regulator_disable(sdm660_cdc->supplies[i].consumer); + return rc; +} + +static int msm_anlg_cdc_disable_static_supplies_to_optimum( + struct sdm660_cdc_priv *sdm660_cdc, + struct sdm660_cdc_pdata *pdata) +{ + int i; + int ret = 0; + + for (i = 0; i < sdm660_cdc->num_of_supplies; i++) { + if (pdata->regulator[i].ondemand) + continue; + if (regulator_count_voltages( + sdm660_cdc->supplies[i].consumer) <= 0) + continue; + regulator_set_voltage(sdm660_cdc->supplies[i].consumer, 0, + pdata->regulator[i].max_uv); + regulator_set_load(sdm660_cdc->supplies[i].consumer, 0); + ret = regulator_disable(sdm660_cdc->supplies[i].consumer); + if (ret) + dev_err(sdm660_cdc->dev, "Failed to disable %s: %d\n", + sdm660_cdc->supplies[i].supply, ret); + + dev_dbg(sdm660_cdc->dev, "Regulator %s disable\n", + sdm660_cdc->supplies[i].supply); + } + + return ret; +} + +static int msm_anlg_cdc_suspend(struct snd_soc_codec *codec) +{ + struct sdm660_cdc_priv *sdm660_cdc = snd_soc_codec_get_drvdata(codec); + struct sdm660_cdc_pdata *sdm660_cdc_pdata = + sdm660_cdc->dev->platform_data; + + msm_anlg_cdc_disable_static_supplies_to_optimum(sdm660_cdc, + sdm660_cdc_pdata); + return 0; +} + +static int msm_anlg_cdc_resume(struct snd_soc_codec *codec) +{ + struct msm_asoc_mach_data *pdata = NULL; + struct sdm660_cdc_priv *sdm660_cdc = snd_soc_codec_get_drvdata(codec); + struct sdm660_cdc_pdata *sdm660_cdc_pdata = + sdm660_cdc->dev->platform_data; + + pdata = snd_soc_card_get_drvdata(codec->component.card); + msm_anlg_cdc_enable_static_supplies_to_optimum(sdm660_cdc, + sdm660_cdc_pdata); + return 0; +} + +static struct regmap *msm_anlg_get_regmap(struct device *dev) +{ + return dev_get_regmap(dev->parent, NULL); +} + +static struct snd_soc_codec_driver soc_codec_dev_sdm660_cdc = { + .probe = msm_anlg_cdc_soc_probe, + .remove = msm_anlg_cdc_soc_remove, + .suspend = msm_anlg_cdc_suspend, + .resume = msm_anlg_cdc_resume, + .reg_word_size = 1, + .get_regmap = msm_anlg_get_regmap, + .component_driver = { + .controls = msm_anlg_cdc_snd_controls, + .num_controls = ARRAY_SIZE(msm_anlg_cdc_snd_controls), + .dapm_widgets = msm_anlg_cdc_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(msm_anlg_cdc_dapm_widgets), + .dapm_routes = audio_map, + .num_dapm_routes = ARRAY_SIZE(audio_map), + }, +}; + +static int msm_anlg_cdc_init_supplies(struct sdm660_cdc_priv *sdm660_cdc, + struct sdm660_cdc_pdata *pdata) +{ + int ret; + int i; + + sdm660_cdc->supplies = devm_kzalloc(sdm660_cdc->dev, + sizeof(struct regulator_bulk_data) * + ARRAY_SIZE(pdata->regulator), + GFP_KERNEL); + if (!sdm660_cdc->supplies) { + ret = -ENOMEM; + goto err; + } + + sdm660_cdc->num_of_supplies = 0; + if (ARRAY_SIZE(pdata->regulator) > MAX_REGULATOR) { + dev_err(sdm660_cdc->dev, "%s: Array Size out of bound\n", + __func__); + ret = -EINVAL; + goto err; + } + + for (i = 0; i < ARRAY_SIZE(pdata->regulator); i++) { + if (pdata->regulator[i].name) { + sdm660_cdc->supplies[i].supply = + pdata->regulator[i].name; + sdm660_cdc->num_of_supplies++; + } + } + + ret = devm_regulator_bulk_get(sdm660_cdc->dev, + sdm660_cdc->num_of_supplies, + sdm660_cdc->supplies); + if (ret != 0) { + dev_err(sdm660_cdc->dev, + "Failed to get supplies: err = %d\n", + ret); + goto err_supplies; + } + + for (i = 0; i < sdm660_cdc->num_of_supplies; i++) { + if (regulator_count_voltages( + sdm660_cdc->supplies[i].consumer) <= 0) + continue; + if (pdata->regulator[i].ondemand) { + ret = regulator_set_voltage( + sdm660_cdc->supplies[i].consumer, + 0, pdata->regulator[i].max_uv); + if (ret) { + dev_err(sdm660_cdc->dev, + "Setting regulator voltage failed for regulator %s err = %d\n", + sdm660_cdc->supplies[i].supply, ret); + goto err_supplies; + } + ret = regulator_set_load( + sdm660_cdc->supplies[i].consumer, 0); + if (ret < 0) { + dev_err(sdm660_cdc->dev, + "Setting regulator optimum mode failed for regulator %s err = %d\n", + sdm660_cdc->supplies[i].supply, ret); + goto err_supplies; + } else { + ret = 0; + continue; + } + } + ret = regulator_set_voltage(sdm660_cdc->supplies[i].consumer, + pdata->regulator[i].min_uv, + pdata->regulator[i].max_uv); + if (ret) { + dev_err(sdm660_cdc->dev, + "Setting regulator voltage failed for regulator %s err = %d\n", + sdm660_cdc->supplies[i].supply, ret); + goto err_supplies; + } + ret = regulator_set_load(sdm660_cdc->supplies[i].consumer, + pdata->regulator[i].optimum_ua); + if (ret < 0) { + dev_err(sdm660_cdc->dev, + "Setting regulator optimum mode failed for regulator %s err = %d\n", + sdm660_cdc->supplies[i].supply, ret); + goto err_supplies; + } else { + ret = 0; + } + } + + return ret; + +err_supplies: + devm_kfree(sdm660_cdc->dev, sdm660_cdc->supplies); +err: + return ret; +} + +static int msm_anlg_cdc_enable_static_supplies( + struct sdm660_cdc_priv *sdm660_cdc, + struct sdm660_cdc_pdata *pdata) +{ + int i; + int ret = 0; + + for (i = 0; i < sdm660_cdc->num_of_supplies; i++) { + if (pdata->regulator[i].ondemand) + continue; + ret = regulator_enable(sdm660_cdc->supplies[i].consumer); + if (ret) { + dev_err(sdm660_cdc->dev, "Failed to enable %s\n", + sdm660_cdc->supplies[i].supply); + break; + } + dev_dbg(sdm660_cdc->dev, "Enabled regulator %s\n", + sdm660_cdc->supplies[i].supply); + } + + while (ret && i--) + if (!pdata->regulator[i].ondemand) + regulator_disable(sdm660_cdc->supplies[i].consumer); + return ret; +} + +static void msm_anlg_cdc_disable_supplies(struct sdm660_cdc_priv *sdm660_cdc, + struct sdm660_cdc_pdata *pdata) +{ + int i; + + regulator_bulk_disable(sdm660_cdc->num_of_supplies, + sdm660_cdc->supplies); + for (i = 0; i < sdm660_cdc->num_of_supplies; i++) { + if (regulator_count_voltages( + sdm660_cdc->supplies[i].consumer) <= 0) + continue; + regulator_set_voltage(sdm660_cdc->supplies[i].consumer, 0, + pdata->regulator[i].max_uv); + regulator_set_load(sdm660_cdc->supplies[i].consumer, 0); + } +} + +static const struct of_device_id sdm660_codec_of_match[] = { + { .compatible = "qcom,pmic-analog-codec", }, + {}, +}; + +static void msm_anlg_add_child_devices(struct work_struct *work) +{ + struct sdm660_cdc_priv *pdata; + struct platform_device *pdev; + struct device_node *node; + struct msm_dig_ctrl_data *dig_ctrl_data = NULL, *temp; + int ret, ctrl_num = 0; + struct msm_dig_ctrl_platform_data *platdata; + char plat_dev_name[MSM_DIG_CDC_STRING_LEN]; + + pdata = container_of(work, struct sdm660_cdc_priv, + msm_anlg_add_child_devices_work); + if (!pdata) { + pr_err("%s: Memory for pdata does not exist\n", + __func__); + return; + } + if (!pdata->dev->of_node) { + dev_err(pdata->dev, + "%s: DT node for pdata does not exist\n", __func__); + return; + } + + platdata = &pdata->dig_plat_data; + + for_each_child_of_node(pdata->dev->of_node, node) { + if (!strcmp(node->name, "msm-dig-codec")) + strlcpy(plat_dev_name, "msm_digital_codec", + (MSM_DIG_CDC_STRING_LEN - 1)); + else + continue; + + pdev = platform_device_alloc(plat_dev_name, -1); + if (!pdev) { + dev_err(pdata->dev, "%s: pdev memory alloc failed\n", + __func__); + ret = -ENOMEM; + goto err; + } + pdev->dev.parent = pdata->dev; + pdev->dev.of_node = node; + + if (!strcmp(node->name, "msm-dig-codec")) { + ret = platform_device_add_data(pdev, platdata, + sizeof(*platdata)); + if (ret) { + dev_err(&pdev->dev, + "%s: cannot add plat data ctrl:%d\n", + __func__, ctrl_num); + goto fail_pdev_add; + } + } + + ret = platform_device_add(pdev); + if (ret) { + dev_err(&pdev->dev, + "%s: Cannot add platform device\n", + __func__); + goto fail_pdev_add; + } + + if (!strcmp(node->name, "msm-dig-codec")) { + temp = krealloc(dig_ctrl_data, + (ctrl_num + 1) * sizeof( + struct msm_dig_ctrl_data), + GFP_KERNEL); + if (!temp) { + dev_err(&pdev->dev, "out of memory\n"); + ret = -ENOMEM; + goto err; + } + dig_ctrl_data = temp; + dig_ctrl_data[ctrl_num].dig_pdev = pdev; + ctrl_num++; + dev_dbg(&pdev->dev, + "%s: Added digital codec device(s)\n", + __func__); + pdata->dig_ctrl_data = dig_ctrl_data; + } + pdata->pdev_child_devices[pdata->child_count++] = pdev; + } + + return; +fail_pdev_add: + platform_device_put(pdev); +err: + return; +} + +static int msm_anlg_cdc_probe(struct platform_device *pdev) +{ + int ret = 0; + struct sdm660_cdc_priv *sdm660_cdc = NULL; + struct sdm660_cdc_pdata *pdata; + int adsp_state; + + adsp_state = apr_get_subsys_state(); + if (adsp_state != APR_SUBSYS_LOADED) { + dev_err(&pdev->dev, "Adsp is not loaded yet %d\n", + adsp_state); + return -EPROBE_DEFER; + } + device_init_wakeup(&pdev->dev, true); + + if (pdev->dev.of_node) { + dev_dbg(&pdev->dev, "%s:Platform data from device tree\n", + __func__); + pdata = msm_anlg_cdc_populate_dt_pdata(&pdev->dev); + pdev->dev.platform_data = pdata; + } else { + dev_dbg(&pdev->dev, "%s:Platform data from board file\n", + __func__); + pdata = pdev->dev.platform_data; + } + if (pdata == NULL) { + dev_err(&pdev->dev, "%s:Platform data failed to populate\n", + __func__); + goto rtn; + } + sdm660_cdc = devm_kzalloc(&pdev->dev, sizeof(struct sdm660_cdc_priv), + GFP_KERNEL); + if (sdm660_cdc == NULL) { + ret = -ENOMEM; + goto rtn; + } + + sdm660_cdc->dev = &pdev->dev; + ret = msm_anlg_cdc_init_supplies(sdm660_cdc, pdata); + if (ret) { + dev_err(&pdev->dev, "%s: Fail to enable Codec supplies\n", + __func__); + goto rtn; + } + ret = msm_anlg_cdc_enable_static_supplies(sdm660_cdc, pdata); + if (ret) { + dev_err(&pdev->dev, + "%s: Fail to enable Codec pre-reset supplies\n", + __func__); + goto rtn; + } + /* Allow supplies to be ready */ + usleep_range(5, 6); + + wcd9xxx_spmi_set_dev(pdev, 0); + wcd9xxx_spmi_set_dev(pdev, 1); + if (wcd9xxx_spmi_irq_init()) { + dev_err(&pdev->dev, + "%s: irq initialization failed\n", __func__); + } else { + dev_dbg(&pdev->dev, + "%s: irq initialization passed\n", __func__); + } + dev_set_drvdata(&pdev->dev, sdm660_cdc); + + ret = snd_soc_register_codec(&pdev->dev, + &soc_codec_dev_sdm660_cdc, + msm_anlg_cdc_i2s_dai, + ARRAY_SIZE(msm_anlg_cdc_i2s_dai)); + if (ret) { + dev_err(&pdev->dev, + "%s:snd_soc_register_codec failed with error %d\n", + __func__, ret); + goto err_supplies; + } + BLOCKING_INIT_NOTIFIER_HEAD(&sdm660_cdc->notifier); + BLOCKING_INIT_NOTIFIER_HEAD(&sdm660_cdc->notifier_mbhc); + + sdm660_cdc->dig_plat_data.handle = (void *) sdm660_cdc; + sdm660_cdc->dig_plat_data.set_compander_mode = set_compander_mode; + sdm660_cdc->dig_plat_data.update_clkdiv = update_clkdiv; + sdm660_cdc->dig_plat_data.get_cdc_version = get_cdc_version; + sdm660_cdc->dig_plat_data.register_notifier = + msm_anlg_cdc_dig_register_notifier; + INIT_WORK(&sdm660_cdc->msm_anlg_add_child_devices_work, + msm_anlg_add_child_devices); + schedule_work(&sdm660_cdc->msm_anlg_add_child_devices_work); + + return ret; +err_supplies: + msm_anlg_cdc_disable_supplies(sdm660_cdc, pdata); +rtn: + return ret; +} + +static int msm_anlg_cdc_remove(struct platform_device *pdev) +{ + struct sdm660_cdc_priv *sdm660_cdc = dev_get_drvdata(&pdev->dev); + struct sdm660_cdc_pdata *pdata = sdm660_cdc->dev->platform_data; + int count; + + for (count = 0; count < sdm660_cdc->child_count && + count < ANLG_CDC_CHILD_DEVICES_MAX; count++) + platform_device_unregister( + sdm660_cdc->pdev_child_devices[count]); + snd_soc_unregister_codec(&pdev->dev); + msm_anlg_cdc_disable_supplies(sdm660_cdc, pdata); + wcd9xxx_spmi_irq_exit(); + devm_kfree(&pdev->dev, sdm660_cdc); + return 0; +} + +static struct platform_driver msm_anlg_codec_driver = { + .driver = { + .owner = THIS_MODULE, + .name = DRV_NAME, + .of_match_table = of_match_ptr(sdm660_codec_of_match) + }, + .probe = msm_anlg_cdc_probe, + .remove = msm_anlg_cdc_remove, +}; +module_platform_driver(msm_anlg_codec_driver); + +MODULE_DESCRIPTION("MSM Audio Analog codec driver"); +MODULE_LICENSE("GPL v2"); diff --git a/audio-kernel/asoc/codecs/sdm660_cdc/msm-analog-cdc.h b/audio-kernel/asoc/codecs/sdm660_cdc/msm-analog-cdc.h new file mode 100644 index 000000000..71d555f38 --- /dev/null +++ b/audio-kernel/asoc/codecs/sdm660_cdc/msm-analog-cdc.h @@ -0,0 +1,273 @@ +/* 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 + * 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 MSM_ANALOG_CDC_H +#define MSM_ANALOG_CDC_H + +#include +#include +#include +#include "../wcd-mbhc-v2.h" +#include "../wcdcal-hwdep.h" +#include "sdm660-cdc-registers.h" + +#define MICBIAS_EXT_BYP_CAP 0x00 +#define MICBIAS_NO_EXT_BYP_CAP 0x01 +#define ANLG_CDC_CHILD_DEVICES_MAX 1 + +#define MSM89XX_NUM_IRQ_REGS 2 +#define MAX_REGULATOR 7 +#define MSM89XX_REG_VAL(reg, val) {reg, 0, val} + +#define MSM89XX_VDD_SPKDRV_NAME "cdc-vdd-spkdrv" + +#define DEFAULT_MULTIPLIER 800 +#define DEFAULT_GAIN 9 +#define DEFAULT_OFFSET 100 + +extern const u8 msm89xx_pmic_cdc_reg_readable[MSM89XX_PMIC_CDC_CACHE_SIZE]; +extern const u8 msm89xx_cdc_core_reg_readable[MSM89XX_CDC_CORE_CACHE_SIZE]; +extern struct regmap_config msm89xx_cdc_core_regmap_config; +extern struct regmap_config msm89xx_pmic_cdc_regmap_config; + +enum wcd_curr_ref { + I_h4_UA = 0, + I_pt5_UA, + I_14_UA, + I_l4_UA, + I_1_UA, +}; + +enum wcd_mbhc_imp_det_pin { + WCD_MBHC_DET_NONE = 0, + WCD_MBHC_DET_HPHL, + WCD_MBHC_DET_HPHR, + WCD_MBHC_DET_BOTH, +}; + + +/* Each micbias can be assigned to one of three cfilters + * Vbatt_min >= .15V + ldoh_v + * ldoh_v >= .15v + cfiltx_mv + * If ldoh_v = 1.95 160 mv < cfiltx_mv < 1800 mv + * If ldoh_v = 2.35 200 mv < cfiltx_mv < 2200 mv + * If ldoh_v = 2.75 240 mv < cfiltx_mv < 2600 mv + * If ldoh_v = 2.85 250 mv < cfiltx_mv < 2700 mv + */ + +struct wcd_micbias_setting { + u8 ldoh_v; + u32 cfilt1_mv; /* in mv */ + u32 cfilt2_mv; /* in mv */ + u32 cfilt3_mv; /* in mv */ + /* Different WCD9xxx series codecs may not + * have 4 mic biases. If a codec has fewer + * mic biases, some of these properties will + * not be used. + */ + u8 bias1_cfilt_sel; + u8 bias2_cfilt_sel; + u8 bias3_cfilt_sel; + u8 bias4_cfilt_sel; + u8 bias1_cap_mode; + u8 bias2_cap_mode; + u8 bias3_cap_mode; + u8 bias4_cap_mode; + bool bias2_is_headset_only; +}; + +enum sdm660_cdc_pid_current { + MSM89XX_PID_MIC_2P5_UA, + MSM89XX_PID_MIC_5_UA, + MSM89XX_PID_MIC_10_UA, + MSM89XX_PID_MIC_20_UA, +}; + +struct sdm660_cdc_reg_mask_val { + u16 reg; + u8 mask; + u8 val; +}; + +enum { + /* INTR_REG 0 - Digital Periph */ + MSM89XX_IRQ_SPKR_CNP = 0, + MSM89XX_IRQ_SPKR_CLIP, + MSM89XX_IRQ_SPKR_OCP, + MSM89XX_IRQ_MBHC_INSREM_DET1, + MSM89XX_IRQ_MBHC_RELEASE, + MSM89XX_IRQ_MBHC_PRESS, + MSM89XX_IRQ_MBHC_INSREM_DET, + MSM89XX_IRQ_MBHC_HS_DET, + /* INTR_REG 1 - Analog Periph */ + MSM89XX_IRQ_EAR_OCP, + MSM89XX_IRQ_HPHR_OCP, + MSM89XX_IRQ_HPHL_OCP, + MSM89XX_IRQ_EAR_CNP, + MSM89XX_IRQ_HPHR_CNP, + MSM89XX_IRQ_HPHL_CNP, + MSM89XX_NUM_IRQS, +}; + +enum { + ON_DEMAND_MICBIAS = 0, + ON_DEMAND_SPKDRV, + ON_DEMAND_SUPPLIES_MAX, +}; + +/* + * The delay list is per codec HW specification. + * Please add delay in the list in the future instead + * of magic number + */ +enum { + CODEC_DELAY_1_MS = 1000, + CODEC_DELAY_1_1_MS = 1100, +}; + +struct sdm660_cdc_regulator { + const char *name; + int min_uv; + int max_uv; + int optimum_ua; + bool ondemand; + struct regulator *regulator; +}; + +struct on_demand_supply { + struct regulator *supply; + atomic_t ref; + int min_uv; + int max_uv; + int optimum_ua; +}; + +struct wcd_imped_i_ref { + enum wcd_curr_ref curr_ref; + int min_val; + int multiplier; + int gain_adj; + int offset; +}; + +enum sdm660_cdc_micbias_num { + MSM89XX_MICBIAS1 = 0, +}; + +/* Hold instance to digital codec platform device */ +struct msm_dig_ctrl_data { + struct platform_device *dig_pdev; +}; + +struct msm_dig_ctrl_platform_data { + void *handle; + void (*set_compander_mode)(void *handle, int val); + void (*update_clkdiv)(void *handle, int val); + int (*get_cdc_version)(void *handle); + int (*register_notifier)(void *handle, + struct notifier_block *nblock, + bool enable); +}; + +struct sdm660_cdc_priv { + struct device *dev; + u32 num_of_supplies; + struct regulator_bulk_data *supplies; + struct snd_soc_codec *codec; + struct work_struct msm_anlg_add_child_devices_work; + struct msm_dig_ctrl_platform_data dig_plat_data; + /* digital codec data structure */ + struct msm_dig_ctrl_data *dig_ctrl_data; + struct blocking_notifier_head notifier; + u16 pmic_rev; + u16 codec_version; + u16 analog_major_rev; + u32 boost_voltage; + u32 adc_count; + u32 rx_bias_count; + bool int_mclk0_enabled; + u16 boost_option; + /* mode to select hd2 */ + u32 hph_mode; + /* compander used for each rx chain */ + bool spk_boost_set; + bool ear_pa_boost_set; + bool ext_spk_boost_set; + struct on_demand_supply on_demand_list[ON_DEMAND_SUPPLIES_MAX]; + struct regulator *spkdrv_reg; + struct blocking_notifier_head notifier_mbhc; + /* mbhc module */ + struct wcd_mbhc mbhc; + /* cal info for codec */ + struct fw_info *fw_data; + struct notifier_block audio_ssr_nb; + int (*codec_spk_ext_pa_cb)(struct snd_soc_codec *codec, int enable); + unsigned long status_mask; + struct wcd_imped_i_ref imped_i_ref; + enum wcd_mbhc_imp_det_pin imped_det_pin; + /* Entry for version info */ + struct snd_info_entry *entry; + struct snd_info_entry *version_entry; + struct platform_device *pdev_child_devices + [ANLG_CDC_CHILD_DEVICES_MAX]; + int child_count; +}; + +struct sdm660_cdc_pdata { + struct wcd_micbias_setting micbias; + struct sdm660_cdc_regulator regulator[MAX_REGULATOR]; +}; + +#if IS_ENABLED(CONFIG_SND_SOC_ANALOG_CDC) +extern int msm_anlg_cdc_mclk_enable(struct snd_soc_codec *codec, + int mclk_enable, bool dapm); +extern int msm_anlg_cdc_hs_detect(struct snd_soc_codec *codec, + struct wcd_mbhc_config *mbhc_cfg); +extern void msm_anlg_cdc_hs_detect_exit(struct snd_soc_codec *codec); +extern void sdm660_cdc_update_int_spk_boost(bool enable); +extern void msm_anlg_cdc_spk_ext_pa_cb( + int (*codec_spk_ext_pa)(struct snd_soc_codec *codec, + int enable), struct snd_soc_codec *codec); +int msm_anlg_codec_info_create_codec_entry(struct snd_info_entry *codec_root, + struct snd_soc_codec *codec); +#else /* CONFIG_SND_SOC_ANALOG_CDC */ +static inline int msm_anlg_cdc_mclk_enable(struct snd_soc_codec *codec, + int mclk_enable, bool dapm) +{ + return 0; +} +static inline int msm_anlg_cdc_hs_detect(struct snd_soc_codec *codec, + struct wcd_mbhc_config *mbhc_cfg) +{ + return 0; +} +static inline void msm_anlg_cdc_hs_detect_exit(struct snd_soc_codec *codec) +{ + +} +static inline void sdm660_cdc_update_int_spk_boost(bool enable) +{ + +} +static inline void msm_anlg_cdc_spk_ext_pa_cb( + int (*codec_spk_ext_pa)(struct snd_soc_codec *codec, + int enable), struct snd_soc_codec *codec) +{ + +} +static inline int msm_anlg_codec_info_create_codec_entry( + struct snd_info_entry *codec_root, + struct snd_soc_codec *codec) +{ + return 0; +} +#endif /* CONFIG_SND_SOC_ANALOG_CDC */ +#endif diff --git a/audio-kernel/asoc/codecs/sdm660_cdc/msm-cdc-common.h b/audio-kernel/asoc/codecs/sdm660_cdc/msm-cdc-common.h new file mode 100644 index 000000000..1a490a492 --- /dev/null +++ b/audio-kernel/asoc/codecs/sdm660_cdc/msm-cdc-common.h @@ -0,0 +1,67 @@ +/* Copyright (c) 2016-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 + * 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 +#include "sdm660-cdc-registers.h" + +extern struct reg_default + msm89xx_cdc_core_defaults[MSM89XX_CDC_CORE_CACHE_SIZE]; +extern struct reg_default + msm89xx_pmic_cdc_defaults[MSM89XX_PMIC_CDC_CACHE_SIZE]; + +bool msm89xx_cdc_core_readable_reg(struct device *dev, unsigned int reg); +bool msm89xx_cdc_core_writeable_reg(struct device *dev, unsigned int reg); +bool msm89xx_cdc_core_volatile_reg(struct device *dev, unsigned int reg); + +enum { + AIF1_PB = 0, + AIF1_CAP, + AIF2_VIFEED, + AIF3_SVA, + NUM_CODEC_DAIS, +}; + +enum codec_versions { + TOMBAK_1_0, + TOMBAK_2_0, + CONGA, + CAJON, + CAJON_2_0, + DIANGU, + DRAX_CDC, + UNSUPPORTED, +}; + +/* Support different hph modes */ +enum { + NORMAL_MODE = 0, + HD2_MODE, +}; + +enum dig_cdc_notify_event { + DIG_CDC_EVENT_INVALID, + DIG_CDC_EVENT_CLK_ON, + DIG_CDC_EVENT_CLK_OFF, + DIG_CDC_EVENT_RX1_MUTE_ON, + DIG_CDC_EVENT_RX1_MUTE_OFF, + DIG_CDC_EVENT_RX2_MUTE_ON, + DIG_CDC_EVENT_RX2_MUTE_OFF, + DIG_CDC_EVENT_RX3_MUTE_ON, + DIG_CDC_EVENT_RX3_MUTE_OFF, + DIG_CDC_EVENT_PRE_RX1_INT_ON, + DIG_CDC_EVENT_PRE_RX2_INT_ON, + DIG_CDC_EVENT_POST_RX1_INT_OFF, + DIG_CDC_EVENT_POST_RX2_INT_OFF, + DIG_CDC_EVENT_SSR_DOWN, + DIG_CDC_EVENT_SSR_UP, + DIG_CDC_EVENT_LAST, +}; diff --git a/audio-kernel/asoc/codecs/sdm660_cdc/msm-digital-cdc-regmap.c b/audio-kernel/asoc/codecs/sdm660_cdc/msm-digital-cdc-regmap.c new file mode 100644 index 000000000..a62679174 --- /dev/null +++ b/audio-kernel/asoc/codecs/sdm660_cdc/msm-digital-cdc-regmap.c @@ -0,0 +1,452 @@ +/* + * 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 + * 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 +#include "msm-cdc-common.h" +#include "sdm660-cdc-registers.h" + +/* + * Default register reset values that are common across different versions + * are defined here. If a register reset value is changed based on version + * then remove it from this structure and add it in version specific + * structures. + */ +struct reg_default + msm89xx_cdc_core_defaults[MSM89XX_CDC_CORE_CACHE_SIZE] = { + {MSM89XX_CDC_CORE_CLK_RX_RESET_CTL, 0x00}, + {MSM89XX_CDC_CORE_CLK_TX_RESET_B1_CTL, 0x00}, + {MSM89XX_CDC_CORE_CLK_DMIC_B1_CTL, 0x00}, + {MSM89XX_CDC_CORE_CLK_RX_I2S_CTL, 0x13}, + {MSM89XX_CDC_CORE_CLK_TX_I2S_CTL, 0x13}, + {MSM89XX_CDC_CORE_CLK_OTHR_RESET_B1_CTL, 0x00}, + {MSM89XX_CDC_CORE_CLK_TX_CLK_EN_B1_CTL, 0x00}, + {MSM89XX_CDC_CORE_CLK_OTHR_CTL, 0x04}, + {MSM89XX_CDC_CORE_CLK_RX_B1_CTL, 0x00}, + {MSM89XX_CDC_CORE_CLK_MCLK_CTL, 0x00}, + {MSM89XX_CDC_CORE_CLK_PDM_CTL, 0x00}, + {MSM89XX_CDC_CORE_CLK_SD_CTL, 0x00}, + {MSM89XX_CDC_CORE_CLK_DMIC_B2_CTL, 0x00}, + {MSM89XX_CDC_CORE_CLK_RX_B2_CTL, 0x00}, + {MSM89XX_CDC_CORE_CLK_TX2_I2S_CTL, 0x13}, + {MSM89XX_CDC_CORE_RX1_B1_CTL, 0x00}, + {MSM89XX_CDC_CORE_RX2_B1_CTL, 0x00}, + {MSM89XX_CDC_CORE_RX3_B1_CTL, 0x00}, + {MSM89XX_CDC_CORE_RX1_B2_CTL, 0x00}, + {MSM89XX_CDC_CORE_RX2_B2_CTL, 0x00}, + {MSM89XX_CDC_CORE_RX3_B2_CTL, 0x00}, + {MSM89XX_CDC_CORE_RX1_B3_CTL, 0x00}, + {MSM89XX_CDC_CORE_RX2_B3_CTL, 0x00}, + {MSM89XX_CDC_CORE_RX3_B3_CTL, 0x00}, + {MSM89XX_CDC_CORE_RX1_B4_CTL, 0x00}, + {MSM89XX_CDC_CORE_RX2_B4_CTL, 0x00}, + {MSM89XX_CDC_CORE_RX3_B4_CTL, 0x00}, + {MSM89XX_CDC_CORE_RX1_B5_CTL, 0x68}, + {MSM89XX_CDC_CORE_RX2_B5_CTL, 0x68}, + {MSM89XX_CDC_CORE_RX3_B5_CTL, 0x68}, + {MSM89XX_CDC_CORE_RX1_B6_CTL, 0x00}, + {MSM89XX_CDC_CORE_RX2_B6_CTL, 0x00}, + {MSM89XX_CDC_CORE_RX3_B6_CTL, 0x00}, + {MSM89XX_CDC_CORE_RX1_VOL_CTL_B1_CTL, 0x00}, + {MSM89XX_CDC_CORE_RX2_VOL_CTL_B1_CTL, 0x00}, + {MSM89XX_CDC_CORE_RX3_VOL_CTL_B1_CTL, 0x00}, + {MSM89XX_CDC_CORE_RX1_VOL_CTL_B2_CTL, 0x00}, + {MSM89XX_CDC_CORE_RX2_VOL_CTL_B2_CTL, 0x00}, + {MSM89XX_CDC_CORE_RX3_VOL_CTL_B2_CTL, 0x00}, + {MSM89XX_CDC_CORE_TOP_GAIN_UPDATE, 0x00}, + {MSM89XX_CDC_CORE_TOP_CTL, 0x01}, + {MSM89XX_CDC_CORE_COMP0_B1_CTL, 0x30}, + {MSM89XX_CDC_CORE_COMP0_B2_CTL, 0xB5}, + {MSM89XX_CDC_CORE_COMP0_B3_CTL, 0x28}, + {MSM89XX_CDC_CORE_COMP0_B4_CTL, 0x37}, + {MSM89XX_CDC_CORE_COMP0_B5_CTL, 0x7F}, + {MSM89XX_CDC_CORE_COMP0_B6_CTL, 0x00}, + {MSM89XX_CDC_CORE_COMP0_SHUT_DOWN_STATUS, 0x03}, + {MSM89XX_CDC_CORE_COMP0_FS_CFG, 0x03}, + {MSM89XX_CDC_CORE_COMP0_DELAY_BUF_CTL, 0x02}, + {MSM89XX_CDC_CORE_DEBUG_DESER1_CTL, 0x00}, + {MSM89XX_CDC_CORE_DEBUG_DESER2_CTL, 0x00}, + {MSM89XX_CDC_CORE_DEBUG_B1_CTL_CFG, 0x00}, + {MSM89XX_CDC_CORE_DEBUG_B2_CTL_CFG, 0x00}, + {MSM89XX_CDC_CORE_DEBUG_B3_CTL_CFG, 0x00}, + {MSM89XX_CDC_CORE_IIR1_GAIN_B1_CTL, 0x00}, + {MSM89XX_CDC_CORE_IIR2_GAIN_B1_CTL, 0x00}, + {MSM89XX_CDC_CORE_IIR1_GAIN_B2_CTL, 0x00}, + {MSM89XX_CDC_CORE_IIR2_GAIN_B2_CTL, 0x00}, + {MSM89XX_CDC_CORE_IIR1_GAIN_B3_CTL, 0x00}, + {MSM89XX_CDC_CORE_IIR2_GAIN_B3_CTL, 0x00}, + {MSM89XX_CDC_CORE_IIR1_GAIN_B4_CTL, 0x00}, + {MSM89XX_CDC_CORE_IIR2_GAIN_B4_CTL, 0x00}, + {MSM89XX_CDC_CORE_IIR1_GAIN_B5_CTL, 0x00}, + {MSM89XX_CDC_CORE_IIR2_GAIN_B5_CTL, 0x00}, + {MSM89XX_CDC_CORE_IIR1_GAIN_B6_CTL, 0x00}, + {MSM89XX_CDC_CORE_IIR2_GAIN_B6_CTL, 0x00}, + {MSM89XX_CDC_CORE_IIR1_GAIN_B7_CTL, 0x00}, + {MSM89XX_CDC_CORE_IIR2_GAIN_B7_CTL, 0x00}, + {MSM89XX_CDC_CORE_IIR1_GAIN_B8_CTL, 0x00}, + {MSM89XX_CDC_CORE_IIR2_GAIN_B8_CTL, 0x00}, + {MSM89XX_CDC_CORE_IIR1_CTL, 0x40}, + {MSM89XX_CDC_CORE_IIR2_CTL, 0x40}, + {MSM89XX_CDC_CORE_IIR1_GAIN_TIMER_CTL, 0x00}, + {MSM89XX_CDC_CORE_IIR2_GAIN_TIMER_CTL, 0x00}, + {MSM89XX_CDC_CORE_IIR1_COEF_B1_CTL, 0x00}, + {MSM89XX_CDC_CORE_IIR2_COEF_B1_CTL, 0x00}, + {MSM89XX_CDC_CORE_IIR1_COEF_B2_CTL, 0x00}, + {MSM89XX_CDC_CORE_IIR2_COEF_B2_CTL, 0x00}, + {MSM89XX_CDC_CORE_CONN_RX1_B1_CTL, 0x00}, + {MSM89XX_CDC_CORE_CONN_RX1_B2_CTL, 0x00}, + {MSM89XX_CDC_CORE_CONN_RX1_B3_CTL, 0x00}, + {MSM89XX_CDC_CORE_CONN_RX2_B1_CTL, 0x00}, + {MSM89XX_CDC_CORE_CONN_RX2_B2_CTL, 0x00}, + {MSM89XX_CDC_CORE_CONN_RX2_B3_CTL, 0x00}, + {MSM89XX_CDC_CORE_CONN_RX3_B1_CTL, 0x00}, + {MSM89XX_CDC_CORE_CONN_RX3_B2_CTL, 0x00}, + {MSM89XX_CDC_CORE_CONN_TX_B1_CTL, 0x00}, + {MSM89XX_CDC_CORE_CONN_TX_B2_CTL, 0x00}, + {MSM89XX_CDC_CORE_CONN_EQ1_B1_CTL, 0x00}, + {MSM89XX_CDC_CORE_CONN_EQ1_B2_CTL, 0x00}, + {MSM89XX_CDC_CORE_CONN_EQ1_B3_CTL, 0x00}, + {MSM89XX_CDC_CORE_CONN_EQ1_B4_CTL, 0x00}, + {MSM89XX_CDC_CORE_CONN_EQ2_B1_CTL, 0x00}, + {MSM89XX_CDC_CORE_CONN_EQ2_B2_CTL, 0x00}, + {MSM89XX_CDC_CORE_CONN_EQ2_B3_CTL, 0x00}, + {MSM89XX_CDC_CORE_CONN_EQ2_B4_CTL, 0x00}, + {MSM89XX_CDC_CORE_CONN_TX_I2S_SD1_CTL, 0x00}, + {MSM89XX_CDC_CORE_CONN_TX_B3_CTL, 0x00}, + {MSM89XX_CDC_CORE_TX5_VOL_CTL_TIMER, 0x00}, + {MSM89XX_CDC_CORE_TX5_VOL_CTL_GAIN, 0x00}, + {MSM89XX_CDC_CORE_TX5_VOL_CTL_CFG, 0x00}, + {MSM89XX_CDC_CORE_TX5_MUX_CTL, 0x00}, + {MSM89XX_CDC_CORE_TX5_CLK_FS_CTL, 0x03}, + {MSM89XX_CDC_CORE_TX5_DMIC_CTL, 0x00}, + {MSM89XX_CDC_CORE_TX1_VOL_CTL_TIMER, 0x00}, + {MSM89XX_CDC_CORE_TX2_VOL_CTL_TIMER, 0x00}, + {MSM89XX_CDC_CORE_TX3_VOL_CTL_TIMER, 0x00}, + {MSM89XX_CDC_CORE_TX4_VOL_CTL_TIMER, 0x00}, + {MSM89XX_CDC_CORE_TX1_VOL_CTL_GAIN, 0x00}, + {MSM89XX_CDC_CORE_TX2_VOL_CTL_GAIN, 0x00}, + {MSM89XX_CDC_CORE_TX3_VOL_CTL_GAIN, 0x00}, + {MSM89XX_CDC_CORE_TX4_VOL_CTL_GAIN, 0x00}, + {MSM89XX_CDC_CORE_TX1_VOL_CTL_CFG, 0x00}, + {MSM89XX_CDC_CORE_TX2_VOL_CTL_CFG, 0x00}, + {MSM89XX_CDC_CORE_TX3_VOL_CTL_CFG, 0x00}, + {MSM89XX_CDC_CORE_TX4_VOL_CTL_CFG, 0x00}, + {MSM89XX_CDC_CORE_TX1_MUX_CTL, 0x00}, + {MSM89XX_CDC_CORE_TX2_MUX_CTL, 0x00}, + {MSM89XX_CDC_CORE_TX3_MUX_CTL, 0x00}, + {MSM89XX_CDC_CORE_TX4_MUX_CTL, 0x00}, + {MSM89XX_CDC_CORE_TX1_CLK_FS_CTL, 0x03}, + {MSM89XX_CDC_CORE_TX2_CLK_FS_CTL, 0x03}, + {MSM89XX_CDC_CORE_TX3_CLK_FS_CTL, 0x03}, + {MSM89XX_CDC_CORE_TX4_CLK_FS_CTL, 0x03}, + {MSM89XX_CDC_CORE_TX1_DMIC_CTL, 0x00}, + {MSM89XX_CDC_CORE_TX2_DMIC_CTL, 0x00}, + {MSM89XX_CDC_CORE_TX3_DMIC_CTL, 0x00}, + {MSM89XX_CDC_CORE_TX4_DMIC_CTL, 0x00}, +}; + +static const u8 msm89xx_cdc_core_reg_readable[MSM89XX_CDC_CORE_CACHE_SIZE] = { + [MSM89XX_CDC_CORE_CLK_RX_RESET_CTL] = 1, + [MSM89XX_CDC_CORE_CLK_TX_RESET_B1_CTL] = 1, + [MSM89XX_CDC_CORE_CLK_DMIC_B1_CTL] = 1, + [MSM89XX_CDC_CORE_CLK_RX_I2S_CTL] = 1, + [MSM89XX_CDC_CORE_CLK_TX_I2S_CTL] = 1, + [MSM89XX_CDC_CORE_CLK_OTHR_RESET_B1_CTL] = 1, + [MSM89XX_CDC_CORE_CLK_TX_CLK_EN_B1_CTL] = 1, + [MSM89XX_CDC_CORE_CLK_OTHR_CTL] = 1, + [MSM89XX_CDC_CORE_CLK_RX_B1_CTL] = 1, + [MSM89XX_CDC_CORE_CLK_MCLK_CTL] = 1, + [MSM89XX_CDC_CORE_CLK_PDM_CTL] = 1, + [MSM89XX_CDC_CORE_CLK_SD_CTL] = 1, + [MSM89XX_CDC_CORE_CLK_DMIC_B2_CTL] = 1, + [MSM89XX_CDC_CORE_CLK_RX_B2_CTL] = 1, + [MSM89XX_CDC_CORE_CLK_TX2_I2S_CTL] = 1, + [MSM89XX_CDC_CORE_RX1_B1_CTL] = 1, + [MSM89XX_CDC_CORE_RX2_B1_CTL] = 1, + [MSM89XX_CDC_CORE_RX3_B1_CTL] = 1, + [MSM89XX_CDC_CORE_RX1_B2_CTL] = 1, + [MSM89XX_CDC_CORE_RX2_B2_CTL] = 1, + [MSM89XX_CDC_CORE_RX3_B2_CTL] = 1, + [MSM89XX_CDC_CORE_RX1_B3_CTL] = 1, + [MSM89XX_CDC_CORE_RX2_B3_CTL] = 1, + [MSM89XX_CDC_CORE_RX3_B3_CTL] = 1, + [MSM89XX_CDC_CORE_RX1_B4_CTL] = 1, + [MSM89XX_CDC_CORE_RX2_B4_CTL] = 1, + [MSM89XX_CDC_CORE_RX3_B4_CTL] = 1, + [MSM89XX_CDC_CORE_RX1_B5_CTL] = 1, + [MSM89XX_CDC_CORE_RX2_B5_CTL] = 1, + [MSM89XX_CDC_CORE_RX3_B5_CTL] = 1, + [MSM89XX_CDC_CORE_RX1_B6_CTL] = 1, + [MSM89XX_CDC_CORE_RX2_B6_CTL] = 1, + [MSM89XX_CDC_CORE_RX3_B6_CTL] = 1, + [MSM89XX_CDC_CORE_RX1_VOL_CTL_B1_CTL] = 1, + [MSM89XX_CDC_CORE_RX2_VOL_CTL_B1_CTL] = 1, + [MSM89XX_CDC_CORE_RX3_VOL_CTL_B1_CTL] = 1, + [MSM89XX_CDC_CORE_RX1_VOL_CTL_B2_CTL] = 1, + [MSM89XX_CDC_CORE_RX2_VOL_CTL_B2_CTL] = 1, + [MSM89XX_CDC_CORE_RX3_VOL_CTL_B2_CTL] = 1, + [MSM89XX_CDC_CORE_TOP_GAIN_UPDATE] = 1, + [MSM89XX_CDC_CORE_TOP_CTL] = 1, + [MSM89XX_CDC_CORE_COMP0_B1_CTL] = 1, + [MSM89XX_CDC_CORE_COMP0_B2_CTL] = 1, + [MSM89XX_CDC_CORE_COMP0_B3_CTL] = 1, + [MSM89XX_CDC_CORE_COMP0_B4_CTL] = 1, + [MSM89XX_CDC_CORE_COMP0_B5_CTL] = 1, + [MSM89XX_CDC_CORE_COMP0_B6_CTL] = 1, + [MSM89XX_CDC_CORE_COMP0_SHUT_DOWN_STATUS] = 1, + [MSM89XX_CDC_CORE_COMP0_FS_CFG] = 1, + [MSM89XX_CDC_CORE_COMP0_DELAY_BUF_CTL] = 1, + [MSM89XX_CDC_CORE_DEBUG_DESER1_CTL] = 1, + [MSM89XX_CDC_CORE_DEBUG_DESER2_CTL] = 1, + [MSM89XX_CDC_CORE_DEBUG_B1_CTL_CFG] = 1, + [MSM89XX_CDC_CORE_DEBUG_B2_CTL_CFG] = 1, + [MSM89XX_CDC_CORE_DEBUG_B3_CTL_CFG] = 1, + [MSM89XX_CDC_CORE_IIR1_GAIN_B1_CTL] = 1, + [MSM89XX_CDC_CORE_IIR2_GAIN_B1_CTL] = 1, + [MSM89XX_CDC_CORE_IIR1_GAIN_B2_CTL] = 1, + [MSM89XX_CDC_CORE_IIR2_GAIN_B2_CTL] = 1, + [MSM89XX_CDC_CORE_IIR1_GAIN_B3_CTL] = 1, + [MSM89XX_CDC_CORE_IIR2_GAIN_B3_CTL] = 1, + [MSM89XX_CDC_CORE_IIR1_GAIN_B4_CTL] = 1, + [MSM89XX_CDC_CORE_IIR2_GAIN_B4_CTL] = 1, + [MSM89XX_CDC_CORE_IIR1_GAIN_B5_CTL] = 1, + [MSM89XX_CDC_CORE_IIR2_GAIN_B5_CTL] = 1, + [MSM89XX_CDC_CORE_IIR1_GAIN_B6_CTL] = 1, + [MSM89XX_CDC_CORE_IIR2_GAIN_B6_CTL] = 1, + [MSM89XX_CDC_CORE_IIR1_GAIN_B7_CTL] = 1, + [MSM89XX_CDC_CORE_IIR2_GAIN_B7_CTL] = 1, + [MSM89XX_CDC_CORE_IIR1_GAIN_B8_CTL] = 1, + [MSM89XX_CDC_CORE_IIR2_GAIN_B8_CTL] = 1, + [MSM89XX_CDC_CORE_IIR1_CTL] = 1, + [MSM89XX_CDC_CORE_IIR2_CTL] = 1, + [MSM89XX_CDC_CORE_IIR1_GAIN_TIMER_CTL] = 1, + [MSM89XX_CDC_CORE_IIR2_GAIN_TIMER_CTL] = 1, + [MSM89XX_CDC_CORE_IIR1_COEF_B1_CTL] = 1, + [MSM89XX_CDC_CORE_IIR2_COEF_B1_CTL] = 1, + [MSM89XX_CDC_CORE_IIR1_COEF_B2_CTL] = 1, + [MSM89XX_CDC_CORE_IIR2_COEF_B2_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_RX1_B1_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_RX1_B2_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_RX1_B3_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_RX2_B1_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_RX2_B2_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_RX2_B3_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_RX3_B1_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_RX3_B2_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_TX_B1_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_TX_B2_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_EQ1_B1_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_EQ1_B2_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_EQ1_B3_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_EQ1_B4_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_EQ2_B1_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_EQ2_B2_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_EQ2_B3_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_EQ2_B4_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_TX_I2S_SD1_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_TX_B3_CTL] = 1, + [MSM89XX_CDC_CORE_TX1_VOL_CTL_TIMER] = 1, + [MSM89XX_CDC_CORE_TX2_VOL_CTL_TIMER] = 1, + [MSM89XX_CDC_CORE_TX3_VOL_CTL_TIMER] = 1, + [MSM89XX_CDC_CORE_TX4_VOL_CTL_TIMER] = 1, + [MSM89XX_CDC_CORE_TX1_VOL_CTL_GAIN] = 1, + [MSM89XX_CDC_CORE_TX2_VOL_CTL_GAIN] = 1, + [MSM89XX_CDC_CORE_TX3_VOL_CTL_GAIN] = 1, + [MSM89XX_CDC_CORE_TX4_VOL_CTL_GAIN] = 1, + [MSM89XX_CDC_CORE_TX1_VOL_CTL_CFG] = 1, + [MSM89XX_CDC_CORE_TX2_VOL_CTL_CFG] = 1, + [MSM89XX_CDC_CORE_TX3_VOL_CTL_CFG] = 1, + [MSM89XX_CDC_CORE_TX4_VOL_CTL_CFG] = 1, + [MSM89XX_CDC_CORE_TX1_MUX_CTL] = 1, + [MSM89XX_CDC_CORE_TX2_MUX_CTL] = 1, + [MSM89XX_CDC_CORE_TX3_MUX_CTL] = 1, + [MSM89XX_CDC_CORE_TX4_MUX_CTL] = 1, + [MSM89XX_CDC_CORE_TX1_CLK_FS_CTL] = 1, + [MSM89XX_CDC_CORE_TX2_CLK_FS_CTL] = 1, + [MSM89XX_CDC_CORE_TX3_CLK_FS_CTL] = 1, + [MSM89XX_CDC_CORE_TX4_CLK_FS_CTL] = 1, + [MSM89XX_CDC_CORE_TX5_VOL_CTL_TIMER] = 1, + [MSM89XX_CDC_CORE_TX5_VOL_CTL_GAIN] = 1, + [MSM89XX_CDC_CORE_TX5_VOL_CTL_CFG] = 1, + [MSM89XX_CDC_CORE_TX5_MUX_CTL] = 1, + [MSM89XX_CDC_CORE_TX5_CLK_FS_CTL] = 1, + [MSM89XX_CDC_CORE_TX5_DMIC_CTL] = 1, + [MSM89XX_CDC_CORE_TX1_DMIC_CTL] = 1, + [MSM89XX_CDC_CORE_TX2_DMIC_CTL] = 1, + [MSM89XX_CDC_CORE_TX3_DMIC_CTL] = 1, + [MSM89XX_CDC_CORE_TX4_DMIC_CTL] = 1, +}; + +static const u8 msm89xx_cdc_core_reg_writeable[MSM89XX_CDC_CORE_CACHE_SIZE] = { + [MSM89XX_CDC_CORE_CLK_RX_RESET_CTL] = 1, + [MSM89XX_CDC_CORE_CLK_TX_RESET_B1_CTL] = 1, + [MSM89XX_CDC_CORE_CLK_DMIC_B1_CTL] = 1, + [MSM89XX_CDC_CORE_CLK_RX_I2S_CTL] = 1, + [MSM89XX_CDC_CORE_CLK_TX_I2S_CTL] = 1, + [MSM89XX_CDC_CORE_CLK_OTHR_RESET_B1_CTL] = 1, + [MSM89XX_CDC_CORE_CLK_TX_CLK_EN_B1_CTL] = 1, + [MSM89XX_CDC_CORE_CLK_OTHR_CTL] = 1, + [MSM89XX_CDC_CORE_CLK_RX_B1_CTL] = 1, + [MSM89XX_CDC_CORE_CLK_MCLK_CTL] = 1, + [MSM89XX_CDC_CORE_CLK_PDM_CTL] = 1, + [MSM89XX_CDC_CORE_CLK_SD_CTL] = 1, + [MSM89XX_CDC_CORE_CLK_DMIC_B2_CTL] = 1, + [MSM89XX_CDC_CORE_CLK_RX_B2_CTL] = 1, + [MSM89XX_CDC_CORE_CLK_TX2_I2S_CTL] = 1, + [MSM89XX_CDC_CORE_RX1_B1_CTL] = 1, + [MSM89XX_CDC_CORE_RX2_B1_CTL] = 1, + [MSM89XX_CDC_CORE_RX3_B1_CTL] = 1, + [MSM89XX_CDC_CORE_RX1_B2_CTL] = 1, + [MSM89XX_CDC_CORE_RX2_B2_CTL] = 1, + [MSM89XX_CDC_CORE_RX3_B2_CTL] = 1, + [MSM89XX_CDC_CORE_RX1_B3_CTL] = 1, + [MSM89XX_CDC_CORE_RX2_B3_CTL] = 1, + [MSM89XX_CDC_CORE_RX3_B3_CTL] = 1, + [MSM89XX_CDC_CORE_RX1_B4_CTL] = 1, + [MSM89XX_CDC_CORE_RX2_B4_CTL] = 1, + [MSM89XX_CDC_CORE_RX3_B4_CTL] = 1, + [MSM89XX_CDC_CORE_RX1_B5_CTL] = 1, + [MSM89XX_CDC_CORE_RX2_B5_CTL] = 1, + [MSM89XX_CDC_CORE_RX3_B5_CTL] = 1, + [MSM89XX_CDC_CORE_RX1_B6_CTL] = 1, + [MSM89XX_CDC_CORE_RX2_B6_CTL] = 1, + [MSM89XX_CDC_CORE_RX3_B6_CTL] = 1, + [MSM89XX_CDC_CORE_RX1_VOL_CTL_B1_CTL] = 1, + [MSM89XX_CDC_CORE_RX2_VOL_CTL_B1_CTL] = 1, + [MSM89XX_CDC_CORE_RX3_VOL_CTL_B1_CTL] = 1, + [MSM89XX_CDC_CORE_RX1_VOL_CTL_B2_CTL] = 1, + [MSM89XX_CDC_CORE_RX2_VOL_CTL_B2_CTL] = 1, + [MSM89XX_CDC_CORE_RX3_VOL_CTL_B2_CTL] = 1, + [MSM89XX_CDC_CORE_TOP_GAIN_UPDATE] = 1, + [MSM89XX_CDC_CORE_TOP_CTL] = 1, + [MSM89XX_CDC_CORE_COMP0_B1_CTL] = 1, + [MSM89XX_CDC_CORE_COMP0_B2_CTL] = 1, + [MSM89XX_CDC_CORE_COMP0_B3_CTL] = 1, + [MSM89XX_CDC_CORE_COMP0_B4_CTL] = 1, + [MSM89XX_CDC_CORE_COMP0_B5_CTL] = 1, + [MSM89XX_CDC_CORE_COMP0_B6_CTL] = 1, + [MSM89XX_CDC_CORE_COMP0_FS_CFG] = 1, + [MSM89XX_CDC_CORE_COMP0_DELAY_BUF_CTL] = 1, + [MSM89XX_CDC_CORE_DEBUG_DESER1_CTL] = 1, + [MSM89XX_CDC_CORE_DEBUG_DESER2_CTL] = 1, + [MSM89XX_CDC_CORE_DEBUG_B1_CTL_CFG] = 1, + [MSM89XX_CDC_CORE_DEBUG_B2_CTL_CFG] = 1, + [MSM89XX_CDC_CORE_DEBUG_B3_CTL_CFG] = 1, + [MSM89XX_CDC_CORE_IIR1_GAIN_B1_CTL] = 1, + [MSM89XX_CDC_CORE_IIR2_GAIN_B1_CTL] = 1, + [MSM89XX_CDC_CORE_IIR1_GAIN_B2_CTL] = 1, + [MSM89XX_CDC_CORE_IIR2_GAIN_B2_CTL] = 1, + [MSM89XX_CDC_CORE_IIR1_GAIN_B3_CTL] = 1, + [MSM89XX_CDC_CORE_IIR2_GAIN_B3_CTL] = 1, + [MSM89XX_CDC_CORE_IIR1_GAIN_B4_CTL] = 1, + [MSM89XX_CDC_CORE_IIR2_GAIN_B4_CTL] = 1, + [MSM89XX_CDC_CORE_IIR1_GAIN_B5_CTL] = 1, + [MSM89XX_CDC_CORE_IIR2_GAIN_B5_CTL] = 1, + [MSM89XX_CDC_CORE_IIR1_GAIN_B6_CTL] = 1, + [MSM89XX_CDC_CORE_IIR2_GAIN_B6_CTL] = 1, + [MSM89XX_CDC_CORE_IIR1_GAIN_B7_CTL] = 1, + [MSM89XX_CDC_CORE_IIR2_GAIN_B7_CTL] = 1, + [MSM89XX_CDC_CORE_IIR1_GAIN_B8_CTL] = 1, + [MSM89XX_CDC_CORE_IIR2_GAIN_B8_CTL] = 1, + [MSM89XX_CDC_CORE_IIR1_CTL] = 1, + [MSM89XX_CDC_CORE_IIR2_CTL] = 1, + [MSM89XX_CDC_CORE_IIR1_GAIN_TIMER_CTL] = 1, + [MSM89XX_CDC_CORE_IIR2_GAIN_TIMER_CTL] = 1, + [MSM89XX_CDC_CORE_IIR1_COEF_B1_CTL] = 1, + [MSM89XX_CDC_CORE_IIR2_COEF_B1_CTL] = 1, + [MSM89XX_CDC_CORE_IIR1_COEF_B2_CTL] = 1, + [MSM89XX_CDC_CORE_IIR2_COEF_B2_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_RX1_B1_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_RX1_B2_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_RX1_B3_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_RX2_B1_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_RX2_B2_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_RX2_B3_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_RX3_B1_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_RX3_B2_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_TX_B1_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_TX_B2_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_EQ1_B1_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_EQ1_B2_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_EQ1_B3_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_EQ1_B4_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_EQ2_B1_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_EQ2_B2_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_EQ2_B3_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_EQ2_B4_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_TX_I2S_SD1_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_TX_B3_CTL] = 1, + [MSM89XX_CDC_CORE_TX1_VOL_CTL_TIMER] = 1, + [MSM89XX_CDC_CORE_TX2_VOL_CTL_TIMER] = 1, + [MSM89XX_CDC_CORE_TX3_VOL_CTL_TIMER] = 1, + [MSM89XX_CDC_CORE_TX4_VOL_CTL_TIMER] = 1, + [MSM89XX_CDC_CORE_TX1_VOL_CTL_GAIN] = 1, + [MSM89XX_CDC_CORE_TX2_VOL_CTL_GAIN] = 1, + [MSM89XX_CDC_CORE_TX3_VOL_CTL_GAIN] = 1, + [MSM89XX_CDC_CORE_TX4_VOL_CTL_GAIN] = 1, + [MSM89XX_CDC_CORE_TX1_VOL_CTL_CFG] = 1, + [MSM89XX_CDC_CORE_TX2_VOL_CTL_CFG] = 1, + [MSM89XX_CDC_CORE_TX3_VOL_CTL_CFG] = 1, + [MSM89XX_CDC_CORE_TX4_VOL_CTL_CFG] = 1, + [MSM89XX_CDC_CORE_TX1_MUX_CTL] = 1, + [MSM89XX_CDC_CORE_TX2_MUX_CTL] = 1, + [MSM89XX_CDC_CORE_TX3_MUX_CTL] = 1, + [MSM89XX_CDC_CORE_TX4_MUX_CTL] = 1, + [MSM89XX_CDC_CORE_TX1_CLK_FS_CTL] = 1, + [MSM89XX_CDC_CORE_TX2_CLK_FS_CTL] = 1, + [MSM89XX_CDC_CORE_TX3_CLK_FS_CTL] = 1, + [MSM89XX_CDC_CORE_TX4_CLK_FS_CTL] = 1, + [MSM89XX_CDC_CORE_TX5_VOL_CTL_TIMER] = 1, + [MSM89XX_CDC_CORE_TX5_VOL_CTL_GAIN] = 1, + [MSM89XX_CDC_CORE_TX5_VOL_CTL_CFG] = 1, + [MSM89XX_CDC_CORE_TX5_MUX_CTL] = 1, + [MSM89XX_CDC_CORE_TX5_CLK_FS_CTL] = 1, + [MSM89XX_CDC_CORE_TX5_DMIC_CTL] = 1, + [MSM89XX_CDC_CORE_TX1_DMIC_CTL] = 1, + [MSM89XX_CDC_CORE_TX2_DMIC_CTL] = 1, + [MSM89XX_CDC_CORE_TX3_DMIC_CTL] = 1, + [MSM89XX_CDC_CORE_TX4_DMIC_CTL] = 1, +}; + +bool msm89xx_cdc_core_readable_reg(struct device *dev, unsigned int reg) +{ + return msm89xx_cdc_core_reg_readable[reg]; +} + +bool msm89xx_cdc_core_writeable_reg(struct device *dev, unsigned int reg) +{ + return msm89xx_cdc_core_reg_writeable[reg]; +} + +bool msm89xx_cdc_core_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case MSM89XX_CDC_CORE_RX1_B1_CTL: + case MSM89XX_CDC_CORE_RX2_B1_CTL: + case MSM89XX_CDC_CORE_RX3_B1_CTL: + case MSM89XX_CDC_CORE_RX1_B6_CTL: + case MSM89XX_CDC_CORE_RX2_B6_CTL: + case MSM89XX_CDC_CORE_RX3_B6_CTL: + case MSM89XX_CDC_CORE_TX1_VOL_CTL_CFG: + case MSM89XX_CDC_CORE_TX2_VOL_CTL_CFG: + case MSM89XX_CDC_CORE_TX3_VOL_CTL_CFG: + case MSM89XX_CDC_CORE_TX4_VOL_CTL_CFG: + case MSM89XX_CDC_CORE_TX5_VOL_CTL_CFG: + case MSM89XX_CDC_CORE_IIR1_COEF_B1_CTL: + case MSM89XX_CDC_CORE_IIR2_COEF_B1_CTL: + case MSM89XX_CDC_CORE_CLK_MCLK_CTL: + case MSM89XX_CDC_CORE_CLK_PDM_CTL: + return true; + default: + return false; + } +} diff --git a/audio-kernel/asoc/codecs/sdm660_cdc/msm-digital-cdc.c b/audio-kernel/asoc/codecs/sdm660_cdc/msm-digital-cdc.c new file mode 100644 index 000000000..61b2ad4db --- /dev/null +++ b/audio-kernel/asoc/codecs/sdm660_cdc/msm-digital-cdc.c @@ -0,0 +1,2224 @@ +/* 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 + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "sdm660-cdc-registers.h" +#include "msm-digital-cdc.h" +#include "msm-cdc-common.h" +#include "../../sdm660-common.h" + +#define DRV_NAME "msm_digital_codec" +#define MCLK_RATE_9P6MHZ 9600000 +#define MCLK_RATE_12P288MHZ 12288000 +#define TX_MUX_CTL_CUT_OFF_FREQ_MASK 0x30 +#define CF_MIN_3DB_4HZ 0x0 +#define CF_MIN_3DB_75HZ 0x1 +#define CF_MIN_3DB_150HZ 0x2 + +#define DEC_SVA 5 +#define MSM_DIG_CDC_VERSION_ENTRY_SIZE 32 + +static unsigned long rx_digital_gain_reg[] = { + MSM89XX_CDC_CORE_RX1_VOL_CTL_B2_CTL, + MSM89XX_CDC_CORE_RX2_VOL_CTL_B2_CTL, + MSM89XX_CDC_CORE_RX3_VOL_CTL_B2_CTL, +}; + +static unsigned long tx_digital_gain_reg[] = { + MSM89XX_CDC_CORE_TX1_VOL_CTL_GAIN, + MSM89XX_CDC_CORE_TX2_VOL_CTL_GAIN, + MSM89XX_CDC_CORE_TX3_VOL_CTL_GAIN, + MSM89XX_CDC_CORE_TX4_VOL_CTL_GAIN, + MSM89XX_CDC_CORE_TX5_VOL_CTL_GAIN, +}; + +#define SDM660_TX_UNMUTE_DELAY_MS 40 +static int tx_unmute_delay = SDM660_TX_UNMUTE_DELAY_MS; +module_param(tx_unmute_delay, int, 0664); +MODULE_PARM_DESC(tx_unmute_delay, "delay to unmute the tx path"); + +static const DECLARE_TLV_DB_SCALE(digital_gain, 0, 1, 0); + +struct snd_soc_codec *registered_digcodec; +struct hpf_work tx_hpf_work[NUM_DECIMATORS]; + +/* Codec supports 2 IIR filters */ +enum { + IIR1 = 0, + IIR2, + IIR_MAX, +}; + +static int msm_digcdc_clock_control(bool flag) +{ + int ret = -EINVAL; + struct msm_asoc_mach_data *pdata = NULL; + struct msm_dig_priv *msm_dig_cdc = + snd_soc_codec_get_drvdata(registered_digcodec); + + pdata = snd_soc_card_get_drvdata(registered_digcodec->component.card); + + if (flag) { + mutex_lock(&pdata->cdc_int_mclk0_mutex); + if (atomic_read(&pdata->int_mclk0_enabled) == false) { + if (pdata->native_clk_set) + pdata->digital_cdc_core_clk.clk_freq_in_hz = + NATIVE_MCLK_RATE; + else + pdata->digital_cdc_core_clk.clk_freq_in_hz = + DEFAULT_MCLK_RATE; + pdata->digital_cdc_core_clk.enable = 1; + ret = afe_set_lpass_clock_v2( + AFE_PORT_ID_INT0_MI2S_RX, + &pdata->digital_cdc_core_clk); + if (ret < 0) { + pr_err("%s:failed to enable the MCLK\n", + __func__); + /* + * Avoid access to lpass register + * as clock enable failed during SSR. + */ + if (ret == -ENODEV) + msm_dig_cdc->regmap->cache_only = true; + return ret; + } + pr_debug("enabled digital codec core clk\n"); + atomic_set(&pdata->int_mclk0_enabled, true); + schedule_delayed_work(&pdata->disable_int_mclk0_work, + 50); + } + } else { + mutex_unlock(&pdata->cdc_int_mclk0_mutex); + dev_dbg(registered_digcodec->dev, + "disable MCLK, workq to disable set already\n"); + } + return 0; +} + +static void enable_digital_callback(void *flag) +{ + msm_digcdc_clock_control(true); +} + +static void disable_digital_callback(void *flag) +{ + msm_digcdc_clock_control(false); + pr_debug("disable mclk happens in workq\n"); +} + +static int msm_dig_cdc_put_dec_enum(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *w = snd_soc_dapm_kcontrol_widget(kcontrol); + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int dec_mux, decimator; + char *dec_name = NULL; + char *widget_name = NULL; + char *temp; + u16 tx_mux_ctl_reg; + u8 adc_dmic_sel = 0x0; + int ret = 0; + char *dec_num; + + if (ucontrol->value.enumerated.item[0] > e->items) { + dev_err(codec->dev, "%s: Invalid enum value: %d\n", + __func__, ucontrol->value.enumerated.item[0]); + return -EINVAL; + } + dec_mux = ucontrol->value.enumerated.item[0]; + + widget_name = kstrndup(w->name, 15, GFP_KERNEL); + if (!widget_name) { + dev_err(codec->dev, "%s: failed to copy string\n", + __func__); + return -ENOMEM; + } + temp = widget_name; + + dec_name = strsep(&widget_name, " "); + widget_name = temp; + if (!dec_name) { + dev_err(codec->dev, "%s: Invalid decimator = %s\n", + __func__, w->name); + ret = -EINVAL; + goto out; + } + + dec_num = strpbrk(dec_name, "12345"); + if (dec_num == NULL) { + dev_err(codec->dev, "%s: Invalid DEC selected\n", __func__); + ret = -EINVAL; + goto out; + } + + ret = kstrtouint(dec_num, 10, &decimator); + if (ret < 0) { + dev_err(codec->dev, "%s: Invalid decimator = %s\n", + __func__, dec_name); + ret = -EINVAL; + goto out; + } + + dev_dbg(w->dapm->dev, "%s(): widget = %s decimator = %u dec_mux = %u\n" + , __func__, w->name, decimator, dec_mux); + + switch (decimator) { + case 1: + case 2: + case 3: + case 4: + case 5: + if ((dec_mux == 4) || (dec_mux == 5) || + (dec_mux == 6) || (dec_mux == 7)) + adc_dmic_sel = 0x1; + else + adc_dmic_sel = 0x0; + break; + default: + dev_err(codec->dev, "%s: Invalid Decimator = %u\n", + __func__, decimator); + ret = -EINVAL; + goto out; + } + + tx_mux_ctl_reg = + MSM89XX_CDC_CORE_TX1_MUX_CTL + 32 * (decimator - 1); + + if (decimator == DEC_SVA) + tx_mux_ctl_reg = MSM89XX_CDC_CORE_TX5_MUX_CTL; + + snd_soc_update_bits(codec, tx_mux_ctl_reg, 0x1, adc_dmic_sel); + + ret = snd_soc_dapm_put_enum_double(kcontrol, ucontrol); + +out: + kfree(widget_name); + return ret; +} + + +static int msm_dig_cdc_codec_config_compander(struct snd_soc_codec *codec, + int interp_n, int event) +{ + struct msm_dig_priv *dig_cdc = snd_soc_codec_get_drvdata(codec); + int comp_ch_bits_set = 0x03; + int comp_ch_value; + + dev_dbg(codec->dev, "%s: event %d shift %d, enabled %d\n", + __func__, event, interp_n, + dig_cdc->comp_enabled[interp_n]); + + /* compander is invalid */ + if (dig_cdc->comp_enabled[interp_n] != COMPANDER_1 && + dig_cdc->comp_enabled[interp_n]) { + dev_dbg(codec->dev, "%s: Invalid compander %d\n", __func__, + dig_cdc->comp_enabled[interp_n]); + return 0; + } + + if (SND_SOC_DAPM_EVENT_ON(event)) { + /* compander is not enabled */ + if (!dig_cdc->comp_enabled[interp_n]) { + dig_cdc->set_compander_mode(dig_cdc->handle, 0x00); + return 0; + }; + comp_ch_value = snd_soc_read(codec, + MSM89XX_CDC_CORE_COMP0_B1_CTL); + if (interp_n == 0) { + if (comp_ch_value & 0x02) { + dev_dbg(codec->dev, + "%s comp ch 1 already enabled\n", + __func__); + return 0; + } + } + if (interp_n == 1) { + if (comp_ch_value & 0x01) { + dev_dbg(codec->dev, + "%s comp ch 0 already enabled\n", + __func__); + return 0; + } + } + dig_cdc->set_compander_mode(dig_cdc->handle, 0x08); + /* Enable Compander Clock */ + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_COMP0_B2_CTL, 0x0F, 0x09); + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_CLK_RX_B2_CTL, 0x01, 0x01); + if (dig_cdc->comp_enabled[MSM89XX_RX1]) { + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_COMP0_B1_CTL, + 0x02, 0x02); + } + if (dig_cdc->comp_enabled[MSM89XX_RX2]) { + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_COMP0_B1_CTL, + 0x01, 0x01); + } + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_COMP0_B3_CTL, 0xFF, 0x01); + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_COMP0_B2_CTL, 0xF0, 0x50); + /* add sleep for compander to settle */ + usleep_range(1000, 1100); + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_COMP0_B3_CTL, 0xFF, 0x28); + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_COMP0_B2_CTL, 0xF0, 0xB0); + + /* Enable Compander GPIO */ + if (dig_cdc->codec_hph_comp_gpio) + dig_cdc->codec_hph_comp_gpio(1, codec); + } else if (SND_SOC_DAPM_EVENT_OFF(event)) { + /* Disable Compander GPIO */ + if (dig_cdc->codec_hph_comp_gpio) + dig_cdc->codec_hph_comp_gpio(0, codec); + + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_COMP0_B1_CTL, + 1 << interp_n, 0); + comp_ch_bits_set = snd_soc_read(codec, + MSM89XX_CDC_CORE_COMP0_B1_CTL); + if ((comp_ch_bits_set & 0x03) == 0x00) { + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_COMP0_B2_CTL, 0x0F, 0x05); + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_CLK_RX_B2_CTL, 0x01, 0x00); + } + } + return 0; +} + +/** + * msm_dig_cdc_hph_comp_cb - registers callback to codec by machine driver. + * + * @codec_hph_comp_gpio: function pointer to set comp gpio at machine driver + * @codec: codec pointer + * + */ +void msm_dig_cdc_hph_comp_cb( + int (*codec_hph_comp_gpio)(bool enable, struct snd_soc_codec *codec), + struct snd_soc_codec *codec) +{ + struct msm_dig_priv *dig_cdc = snd_soc_codec_get_drvdata(codec); + + pr_debug("%s: Enter\n", __func__); + dig_cdc->codec_hph_comp_gpio = codec_hph_comp_gpio; +} +EXPORT_SYMBOL(msm_dig_cdc_hph_comp_cb); + +static int msm_dig_cdc_codec_enable_interpolator(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct msm_dig_priv *msm_dig_cdc = snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, "%s %d %s\n", __func__, event, w->name); + + if (w->shift >= MSM89XX_RX_MAX || w->shift < 0) { + dev_err(codec->dev, "%s: wrong RX index: %d\n", + __func__, w->shift); + return -EINVAL; + } + switch (event) { + case SND_SOC_DAPM_POST_PMU: + msm_dig_cdc_codec_config_compander(codec, w->shift, event); + /* apply the digital gain after the interpolator is enabled*/ + if ((w->shift) < ARRAY_SIZE(rx_digital_gain_reg)) + snd_soc_write(codec, + rx_digital_gain_reg[w->shift], + snd_soc_read(codec, + rx_digital_gain_reg[w->shift]) + ); + break; + case SND_SOC_DAPM_POST_PMD: + msm_dig_cdc_codec_config_compander(codec, w->shift, event); + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_CLK_RX_RESET_CTL, + 1 << w->shift, 1 << w->shift); + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_CLK_RX_RESET_CTL, + 1 << w->shift, 0x0); + /* + * disable the mute enabled during the PMD of this device + */ + if ((w->shift == 0) && + (msm_dig_cdc->mute_mask & HPHL_PA_DISABLE)) { + pr_debug("disabling HPHL mute\n"); + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_RX1_B6_CTL, 0x01, 0x00); + msm_dig_cdc->mute_mask &= ~(HPHL_PA_DISABLE); + } else if ((w->shift == 1) && + (msm_dig_cdc->mute_mask & HPHR_PA_DISABLE)) { + pr_debug("disabling HPHR mute\n"); + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_RX2_B6_CTL, 0x01, 0x00); + msm_dig_cdc->mute_mask &= ~(HPHR_PA_DISABLE); + } else if ((w->shift == 2) && + (msm_dig_cdc->mute_mask & SPKR_PA_DISABLE)) { + pr_debug("disabling SPKR mute\n"); + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_RX3_B6_CTL, 0x01, 0x00); + msm_dig_cdc->mute_mask &= ~(SPKR_PA_DISABLE); + } + } + return 0; +} + +static int msm_dig_cdc_get_iir_enable_audio_mixer( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + int iir_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->reg; + int band_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + + ucontrol->value.integer.value[0] = + (snd_soc_read(codec, + (MSM89XX_CDC_CORE_IIR1_CTL + 64 * iir_idx)) & + (1 << band_idx)) != 0; + + dev_dbg(codec->dev, "%s: IIR #%d band #%d enable %d\n", __func__, + iir_idx, band_idx, + (uint32_t)ucontrol->value.integer.value[0]); + return 0; +} + +static int msm_dig_cdc_put_iir_enable_audio_mixer( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + int iir_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->reg; + int band_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + int value = ucontrol->value.integer.value[0]; + + /* Mask first 5 bits, 6-8 are reserved */ + snd_soc_update_bits(codec, + (MSM89XX_CDC_CORE_IIR1_CTL + 64 * iir_idx), + (1 << band_idx), (value << band_idx)); + + dev_dbg(codec->dev, "%s: IIR #%d band #%d enable %d\n", __func__, + iir_idx, band_idx, + ((snd_soc_read(codec, + (MSM89XX_CDC_CORE_IIR1_CTL + 64 * iir_idx)) & + (1 << band_idx)) != 0)); + + return 0; +} + +static uint32_t get_iir_band_coeff(struct snd_soc_codec *codec, + int iir_idx, int band_idx, + int coeff_idx) +{ + uint32_t value = 0; + + /* Address does not automatically update if reading */ + snd_soc_write(codec, + (MSM89XX_CDC_CORE_IIR1_COEF_B1_CTL + 64 * iir_idx), + ((band_idx * BAND_MAX + coeff_idx) + * sizeof(uint32_t)) & 0x7F); + + value |= snd_soc_read(codec, + (MSM89XX_CDC_CORE_IIR1_COEF_B2_CTL + 64 * iir_idx)); + + snd_soc_write(codec, + (MSM89XX_CDC_CORE_IIR1_COEF_B1_CTL + 64 * iir_idx), + ((band_idx * BAND_MAX + coeff_idx) + * sizeof(uint32_t) + 1) & 0x7F); + + value |= (snd_soc_read(codec, + (MSM89XX_CDC_CORE_IIR1_COEF_B2_CTL + 64 * iir_idx)) << 8); + + snd_soc_write(codec, + (MSM89XX_CDC_CORE_IIR1_COEF_B1_CTL + 64 * iir_idx), + ((band_idx * BAND_MAX + coeff_idx) + * sizeof(uint32_t) + 2) & 0x7F); + + value |= (snd_soc_read(codec, + (MSM89XX_CDC_CORE_IIR1_COEF_B2_CTL + 64 * iir_idx)) << 16); + + snd_soc_write(codec, + (MSM89XX_CDC_CORE_IIR1_COEF_B1_CTL + 64 * iir_idx), + ((band_idx * BAND_MAX + coeff_idx) + * sizeof(uint32_t) + 3) & 0x7F); + + /* Mask bits top 2 bits since they are reserved */ + value |= ((snd_soc_read(codec, (MSM89XX_CDC_CORE_IIR1_COEF_B2_CTL + + 64 * iir_idx)) & 0x3f) << 24); + + return value; + +} + +static void set_iir_band_coeff(struct snd_soc_codec *codec, + int iir_idx, int band_idx, + uint32_t value) +{ + snd_soc_write(codec, + (MSM89XX_CDC_CORE_IIR1_COEF_B2_CTL + 64 * iir_idx), + (value & 0xFF)); + + snd_soc_write(codec, + (MSM89XX_CDC_CORE_IIR1_COEF_B2_CTL + 64 * iir_idx), + (value >> 8) & 0xFF); + + snd_soc_write(codec, + (MSM89XX_CDC_CORE_IIR1_COEF_B2_CTL + 64 * iir_idx), + (value >> 16) & 0xFF); + + /* Mask top 2 bits, 7-8 are reserved */ + snd_soc_write(codec, + (MSM89XX_CDC_CORE_IIR1_COEF_B2_CTL + 64 * iir_idx), + (value >> 24) & 0x3F); + +} + +static int msm_dig_cdc_get_iir_band_audio_mixer( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + int iir_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->reg; + int band_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + + ucontrol->value.integer.value[0] = + get_iir_band_coeff(codec, iir_idx, band_idx, 0); + ucontrol->value.integer.value[1] = + get_iir_band_coeff(codec, iir_idx, band_idx, 1); + ucontrol->value.integer.value[2] = + get_iir_band_coeff(codec, iir_idx, band_idx, 2); + ucontrol->value.integer.value[3] = + get_iir_band_coeff(codec, iir_idx, band_idx, 3); + ucontrol->value.integer.value[4] = + get_iir_band_coeff(codec, iir_idx, band_idx, 4); + + dev_dbg(codec->dev, "%s: IIR #%d band #%d b0 = 0x%x\n" + "%s: IIR #%d band #%d b1 = 0x%x\n" + "%s: IIR #%d band #%d b2 = 0x%x\n" + "%s: IIR #%d band #%d a1 = 0x%x\n" + "%s: IIR #%d band #%d a2 = 0x%x\n", + __func__, iir_idx, band_idx, + (uint32_t)ucontrol->value.integer.value[0], + __func__, iir_idx, band_idx, + (uint32_t)ucontrol->value.integer.value[1], + __func__, iir_idx, band_idx, + (uint32_t)ucontrol->value.integer.value[2], + __func__, iir_idx, band_idx, + (uint32_t)ucontrol->value.integer.value[3], + __func__, iir_idx, band_idx, + (uint32_t)ucontrol->value.integer.value[4]); + return 0; +} + +static int msm_dig_cdc_put_iir_band_audio_mixer( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + int iir_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->reg; + int band_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + + /* Mask top bit it is reserved */ + /* Updates addr automatically for each B2 write */ + snd_soc_write(codec, + (MSM89XX_CDC_CORE_IIR1_COEF_B1_CTL + 64 * iir_idx), + (band_idx * BAND_MAX * sizeof(uint32_t)) & 0x7F); + + + set_iir_band_coeff(codec, iir_idx, band_idx, + ucontrol->value.integer.value[0]); + set_iir_band_coeff(codec, iir_idx, band_idx, + ucontrol->value.integer.value[1]); + set_iir_band_coeff(codec, iir_idx, band_idx, + ucontrol->value.integer.value[2]); + set_iir_band_coeff(codec, iir_idx, band_idx, + ucontrol->value.integer.value[3]); + set_iir_band_coeff(codec, iir_idx, band_idx, + ucontrol->value.integer.value[4]); + + dev_dbg(codec->dev, "%s: IIR #%d band #%d b0 = 0x%x\n" + "%s: IIR #%d band #%d b1 = 0x%x\n" + "%s: IIR #%d band #%d b2 = 0x%x\n" + "%s: IIR #%d band #%d a1 = 0x%x\n" + "%s: IIR #%d band #%d a2 = 0x%x\n", + __func__, iir_idx, band_idx, + get_iir_band_coeff(codec, iir_idx, band_idx, 0), + __func__, iir_idx, band_idx, + get_iir_band_coeff(codec, iir_idx, band_idx, 1), + __func__, iir_idx, band_idx, + get_iir_band_coeff(codec, iir_idx, band_idx, 2), + __func__, iir_idx, band_idx, + get_iir_band_coeff(codec, iir_idx, band_idx, 3), + __func__, iir_idx, band_idx, + get_iir_band_coeff(codec, iir_idx, band_idx, 4)); + return 0; +} + +static void tx_hpf_corner_freq_callback(struct work_struct *work) +{ + struct delayed_work *hpf_delayed_work; + struct hpf_work *hpf_work; + struct snd_soc_codec *codec; + struct msm_dig_priv *msm_dig_cdc; + u16 tx_mux_ctl_reg; + u8 hpf_cut_of_freq; + + hpf_delayed_work = to_delayed_work(work); + hpf_work = container_of(hpf_delayed_work, struct hpf_work, dwork); + codec = hpf_work->dig_cdc->codec; + msm_dig_cdc = hpf_work->dig_cdc; + hpf_cut_of_freq = hpf_work->tx_hpf_cut_of_freq; + + tx_mux_ctl_reg = MSM89XX_CDC_CORE_TX1_MUX_CTL + + (hpf_work->decimator - 1) * 32; + + if (hpf_work->decimator == DEC_SVA) + tx_mux_ctl_reg = MSM89XX_CDC_CORE_TX5_MUX_CTL; + + dev_dbg(codec->dev, "%s(): decimator %u hpf_cut_of_freq 0x%x\n", + __func__, hpf_work->decimator, (unsigned int)hpf_cut_of_freq); + msm_dig_cdc->update_clkdiv(msm_dig_cdc->handle, 0x51); + + snd_soc_update_bits(codec, tx_mux_ctl_reg, 0x30, hpf_cut_of_freq << 4); +} + +static int msm_dig_cdc_codec_set_iir_gain(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + int value = 0, reg; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + if (w->shift == 0) + reg = MSM89XX_CDC_CORE_IIR1_GAIN_B1_CTL; + else if (w->shift == 1) + reg = MSM89XX_CDC_CORE_IIR2_GAIN_B1_CTL; + else + goto ret; + value = snd_soc_read(codec, reg); + snd_soc_write(codec, reg, value); + break; + default: + pr_err("%s: event = %d not expected\n", __func__, event); + } +ret: + return 0; +} + +static int msm_dig_cdc_compander_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct msm_dig_priv *dig_cdc = snd_soc_codec_get_drvdata(codec); + int comp_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->reg; + int rx_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + + dev_dbg(codec->dev, "%s: msm_dig_cdc->comp[%d]_enabled[%d] = %d\n", + __func__, comp_idx, rx_idx, + dig_cdc->comp_enabled[rx_idx]); + + ucontrol->value.integer.value[0] = dig_cdc->comp_enabled[rx_idx]; + + dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n", + __func__, ucontrol->value.integer.value[0]); + + return 0; +} + +static int msm_dig_cdc_compander_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct msm_dig_priv *dig_cdc = snd_soc_codec_get_drvdata(codec); + int comp_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->reg; + int rx_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + int value = ucontrol->value.integer.value[0]; + + dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n", + __func__, ucontrol->value.integer.value[0]); + + if (dig_cdc->version >= DIANGU) { + if (!value) + dig_cdc->comp_enabled[rx_idx] = 0; + else + dig_cdc->comp_enabled[rx_idx] = comp_idx; + } + + dev_dbg(codec->dev, "%s: msm_dig_cdc->comp[%d]_enabled[%d] = %d\n", + __func__, comp_idx, rx_idx, + dig_cdc->comp_enabled[rx_idx]); + + return 0; +} + +static const struct snd_kcontrol_new compander_kcontrols[] = { + SOC_SINGLE_EXT("COMP0 RX1", COMPANDER_1, MSM89XX_RX1, 1, 0, + msm_dig_cdc_compander_get, msm_dig_cdc_compander_set), + + SOC_SINGLE_EXT("COMP0 RX2", COMPANDER_1, MSM89XX_RX2, 1, 0, + msm_dig_cdc_compander_get, msm_dig_cdc_compander_set), + +}; + +static int msm_dig_cdc_set_interpolator_rate(struct snd_soc_dai *dai, + u8 rx_fs_rate_reg_val, + u32 sample_rate) +{ + snd_soc_update_bits(dai->codec, + MSM89XX_CDC_CORE_RX1_B5_CTL, 0xF0, rx_fs_rate_reg_val); + snd_soc_update_bits(dai->codec, + MSM89XX_CDC_CORE_RX2_B5_CTL, 0xF0, rx_fs_rate_reg_val); + return 0; +} + +static int msm_dig_cdc_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + u8 tx_fs_rate, rx_fs_rate, rx_clk_fs_rate; + int ret; + + dev_dbg(dai->codec->dev, + "%s: dai_name = %s DAI-ID %x rate %d num_ch %d format %d\n", + __func__, dai->name, dai->id, params_rate(params), + params_channels(params), params_format(params)); + + switch (params_rate(params)) { + case 8000: + tx_fs_rate = 0x00; + rx_fs_rate = 0x00; + rx_clk_fs_rate = 0x00; + break; + case 16000: + tx_fs_rate = 0x20; + rx_fs_rate = 0x20; + rx_clk_fs_rate = 0x01; + break; + case 32000: + tx_fs_rate = 0x40; + rx_fs_rate = 0x40; + rx_clk_fs_rate = 0x02; + break; + case 44100: + case 48000: + tx_fs_rate = 0x60; + rx_fs_rate = 0x60; + rx_clk_fs_rate = 0x03; + break; + case 96000: + tx_fs_rate = 0x80; + rx_fs_rate = 0x80; + rx_clk_fs_rate = 0x04; + break; + case 192000: + tx_fs_rate = 0xA0; + rx_fs_rate = 0xA0; + rx_clk_fs_rate = 0x05; + break; + default: + dev_err(dai->codec->dev, + "%s: Invalid sampling rate %d\n", __func__, + params_rate(params)); + return -EINVAL; + } + + snd_soc_update_bits(dai->codec, + MSM89XX_CDC_CORE_CLK_RX_I2S_CTL, 0x0F, rx_clk_fs_rate); + + switch (substream->stream) { + case SNDRV_PCM_STREAM_CAPTURE: + break; + case SNDRV_PCM_STREAM_PLAYBACK: + ret = msm_dig_cdc_set_interpolator_rate(dai, rx_fs_rate, + params_rate(params)); + if (ret < 0) { + dev_err(dai->codec->dev, + "%s: set decimator rate failed %d\n", __func__, + ret); + return ret; + } + break; + default: + dev_err(dai->codec->dev, + "%s: Invalid stream type %d\n", __func__, + substream->stream); + return -EINVAL; + } + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + snd_soc_update_bits(dai->codec, + MSM89XX_CDC_CORE_CLK_RX_I2S_CTL, 0x20, 0x20); + break; + case SNDRV_PCM_FORMAT_S24_LE: + case SNDRV_PCM_FORMAT_S24_3LE: + snd_soc_update_bits(dai->codec, + MSM89XX_CDC_CORE_CLK_RX_I2S_CTL, 0x20, 0x00); + break; + default: + dev_err(dai->codec->dev, "%s: wrong format selected\n", + __func__); + return -EINVAL; + } + return 0; +} + +static int msm_dig_cdc_codec_enable_dmic(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct msm_dig_priv *dig_cdc = snd_soc_codec_get_drvdata(codec); + u8 dmic_clk_en; + u16 dmic_clk_reg; + s32 *dmic_clk_cnt; + unsigned int dmic; + int ret; + char *dmic_num = strpbrk(w->name, "1234"); + + if (dmic_num == NULL) { + dev_err(codec->dev, "%s: Invalid DMIC\n", __func__); + return -EINVAL; + } + + ret = kstrtouint(dmic_num, 10, &dmic); + if (ret < 0) { + dev_err(codec->dev, + "%s: Invalid DMIC line on the codec\n", __func__); + return -EINVAL; + } + + switch (dmic) { + case 1: + case 2: + dmic_clk_en = 0x01; + dmic_clk_cnt = &(dig_cdc->dmic_1_2_clk_cnt); + dmic_clk_reg = MSM89XX_CDC_CORE_CLK_DMIC_B1_CTL; + dev_dbg(codec->dev, + "%s() event %d DMIC%d dmic_1_2_clk_cnt %d\n", + __func__, event, dmic, *dmic_clk_cnt); + break; + case 3: + case 4: + dmic_clk_en = 0x01; + dmic_clk_cnt = &(dig_cdc->dmic_3_4_clk_cnt); + dmic_clk_reg = MSM89XX_CDC_CORE_CLK_DMIC_B2_CTL; + dev_dbg(codec->dev, + "%s() event %d DMIC%d dmic_3_4_clk_cnt %d\n", + __func__, event, dmic, *dmic_clk_cnt); + break; + default: + dev_err(codec->dev, "%s: Invalid DMIC Selection\n", __func__); + return -EINVAL; + } + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + (*dmic_clk_cnt)++; + if (*dmic_clk_cnt == 1) { + snd_soc_update_bits(codec, dmic_clk_reg, + 0x0E, 0x04); + snd_soc_update_bits(codec, dmic_clk_reg, + dmic_clk_en, dmic_clk_en); + } + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_TX1_DMIC_CTL + (dmic - 1) * 0x20, + 0x07, 0x02); + break; + case SND_SOC_DAPM_POST_PMD: + (*dmic_clk_cnt)--; + if (*dmic_clk_cnt == 0) + snd_soc_update_bits(codec, dmic_clk_reg, + dmic_clk_en, 0); + break; + } + return 0; +} + +static int msm_dig_cdc_codec_enable_dec(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct msm_asoc_mach_data *pdata = NULL; + unsigned int decimator; + struct msm_dig_priv *msm_dig_cdc = snd_soc_codec_get_drvdata(codec); + char *dec_name = NULL; + char *widget_name = NULL; + char *temp; + int ret = 0, i; + u16 dec_reset_reg, tx_vol_ctl_reg, tx_mux_ctl_reg; + u8 dec_hpf_cut_of_freq; + int offset; + char *dec_num; + + pdata = snd_soc_card_get_drvdata(codec->component.card); + dev_dbg(codec->dev, "%s %d\n", __func__, event); + + widget_name = kstrndup(w->name, 15, GFP_KERNEL); + if (!widget_name) + return -ENOMEM; + temp = widget_name; + + dec_name = strsep(&widget_name, " "); + widget_name = temp; + if (!dec_name) { + dev_err(codec->dev, + "%s: Invalid decimator = %s\n", __func__, w->name); + ret = -EINVAL; + goto out; + } + + dec_num = strpbrk(dec_name, "12345"); + if (dec_num == NULL) { + dev_err(codec->dev, "%s: Invalid Decimator\n", __func__); + ret = -EINVAL; + goto out; + } + + ret = kstrtouint(dec_num, 10, &decimator); + if (ret < 0) { + dev_err(codec->dev, + "%s: Invalid decimator = %s\n", __func__, dec_name); + ret = -EINVAL; + goto out; + } + + dev_dbg(codec->dev, + "%s(): widget = %s dec_name = %s decimator = %u\n", __func__, + w->name, dec_name, decimator); + + if (w->reg == MSM89XX_CDC_CORE_CLK_TX_CLK_EN_B1_CTL) { + dec_reset_reg = MSM89XX_CDC_CORE_CLK_TX_RESET_B1_CTL; + offset = 0; + } else { + dev_err(codec->dev, "%s: Error, incorrect dec\n", __func__); + ret = -EINVAL; + goto out; + } + + tx_vol_ctl_reg = MSM89XX_CDC_CORE_TX1_VOL_CTL_CFG + + 32 * (decimator - 1); + tx_mux_ctl_reg = MSM89XX_CDC_CORE_TX1_MUX_CTL + + 32 * (decimator - 1); + if (decimator == DEC_SVA) { + tx_vol_ctl_reg = MSM89XX_CDC_CORE_TX5_VOL_CTL_CFG; + tx_mux_ctl_reg = MSM89XX_CDC_CORE_TX5_MUX_CTL; + } + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* Enableable TX digital mute */ + snd_soc_update_bits(codec, tx_vol_ctl_reg, 0x01, 0x01); + for (i = 0; i < NUM_DECIMATORS; i++) { + if (decimator == i + 1) + msm_dig_cdc->dec_active[i] = true; + } + + dec_hpf_cut_of_freq = snd_soc_read(codec, tx_mux_ctl_reg); + + dec_hpf_cut_of_freq = (dec_hpf_cut_of_freq & 0x30) >> 4; + + tx_hpf_work[decimator - 1].tx_hpf_cut_of_freq = + dec_hpf_cut_of_freq; + + if (dec_hpf_cut_of_freq != CF_MIN_3DB_150HZ) { + + /* set cut of freq to CF_MIN_3DB_150HZ (0x1); */ + snd_soc_update_bits(codec, tx_mux_ctl_reg, 0x30, + CF_MIN_3DB_150HZ << 4); + } + msm_dig_cdc->update_clkdiv(msm_dig_cdc->handle, 0x42); + break; + case SND_SOC_DAPM_POST_PMU: + /* enable HPF */ + snd_soc_update_bits(codec, tx_mux_ctl_reg, 0x08, 0x00); + + schedule_delayed_work( + &msm_dig_cdc->tx_mute_dwork[decimator - 1].dwork, + msecs_to_jiffies(tx_unmute_delay)); + if (tx_hpf_work[decimator - 1].tx_hpf_cut_of_freq != + CF_MIN_3DB_150HZ) { + + schedule_delayed_work(&tx_hpf_work[decimator - 1].dwork, + msecs_to_jiffies(300)); + } + /* apply the digital gain after the decimator is enabled*/ + if ((w->shift) < ARRAY_SIZE(tx_digital_gain_reg)) + snd_soc_write(codec, + tx_digital_gain_reg[w->shift + offset], + snd_soc_read(codec, + tx_digital_gain_reg[w->shift + offset]) + ); + break; + case SND_SOC_DAPM_PRE_PMD: + snd_soc_update_bits(codec, tx_vol_ctl_reg, 0x01, 0x01); + msleep(20); + snd_soc_update_bits(codec, tx_mux_ctl_reg, 0x08, 0x08); + cancel_delayed_work_sync(&tx_hpf_work[decimator - 1].dwork); + cancel_delayed_work_sync( + &msm_dig_cdc->tx_mute_dwork[decimator - 1].dwork); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_update_bits(codec, dec_reset_reg, 1 << w->shift, + 1 << w->shift); + snd_soc_update_bits(codec, dec_reset_reg, 1 << w->shift, 0x0); + snd_soc_update_bits(codec, tx_mux_ctl_reg, 0x08, 0x08); + snd_soc_update_bits(codec, tx_mux_ctl_reg, 0x30, + (tx_hpf_work[decimator - 1].tx_hpf_cut_of_freq) << 4); + snd_soc_update_bits(codec, tx_vol_ctl_reg, 0x01, 0x00); + for (i = 0; i < NUM_DECIMATORS; i++) { + if (decimator == i + 1) + msm_dig_cdc->dec_active[i] = false; + } + break; + } +out: + kfree(widget_name); + return ret; +} + +static int msm_dig_cdc_event_notify(struct notifier_block *block, + unsigned long val, + void *data) +{ + enum dig_cdc_notify_event event = (enum dig_cdc_notify_event)val; + struct snd_soc_codec *codec = registered_digcodec; + struct msm_dig_priv *msm_dig_cdc = snd_soc_codec_get_drvdata(codec); + struct msm_asoc_mach_data *pdata = NULL; + int ret = -EINVAL; + + pdata = snd_soc_card_get_drvdata(codec->component.card); + + switch (event) { + case DIG_CDC_EVENT_CLK_ON: + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_CLK_PDM_CTL, 0x03, 0x03); + if (pdata->mclk_freq == MCLK_RATE_12P288MHZ || + pdata->native_clk_set) + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_TOP_CTL, 0x01, 0x00); + else if (pdata->mclk_freq == MCLK_RATE_9P6MHZ) + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_TOP_CTL, 0x01, 0x01); + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_CLK_MCLK_CTL, 0x01, 0x01); + break; + case DIG_CDC_EVENT_CLK_OFF: + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_CLK_PDM_CTL, 0x03, 0x00); + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_CLK_MCLK_CTL, 0x01, 0x00); + break; + case DIG_CDC_EVENT_RX1_MUTE_ON: + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_RX1_B6_CTL, 0x01, 0x01); + msm_dig_cdc->mute_mask |= HPHL_PA_DISABLE; + break; + case DIG_CDC_EVENT_RX1_MUTE_OFF: + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_RX1_B6_CTL, 0x01, 0x00); + msm_dig_cdc->mute_mask &= (~HPHL_PA_DISABLE); + break; + case DIG_CDC_EVENT_RX2_MUTE_ON: + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_RX2_B6_CTL, 0x01, 0x01); + msm_dig_cdc->mute_mask |= HPHR_PA_DISABLE; + break; + case DIG_CDC_EVENT_RX2_MUTE_OFF: + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_RX2_B6_CTL, 0x01, 0x00); + msm_dig_cdc->mute_mask &= (~HPHR_PA_DISABLE); + break; + case DIG_CDC_EVENT_RX3_MUTE_ON: + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_RX3_B6_CTL, 0x01, 0x01); + msm_dig_cdc->mute_mask |= SPKR_PA_DISABLE; + break; + case DIG_CDC_EVENT_RX3_MUTE_OFF: + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_RX3_B6_CTL, 0x01, 0x00); + msm_dig_cdc->mute_mask &= (~SPKR_PA_DISABLE); + break; + case DIG_CDC_EVENT_PRE_RX1_INT_ON: + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_RX1_B3_CTL, 0x3C, 0x28); + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_RX1_B4_CTL, 0x18, 0x10); + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_RX1_B3_CTL, 0x80, 0x80); + break; + case DIG_CDC_EVENT_PRE_RX2_INT_ON: + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_RX2_B3_CTL, 0x3C, 0x28); + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_RX2_B4_CTL, 0x18, 0x10); + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_RX2_B3_CTL, 0x80, 0x80); + break; + case DIG_CDC_EVENT_POST_RX1_INT_OFF: + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_RX1_B3_CTL, 0x3C, 0x00); + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_RX1_B4_CTL, 0x18, 0xFF); + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_RX1_B3_CTL, 0x80, 0x00); + break; + case DIG_CDC_EVENT_POST_RX2_INT_OFF: + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_RX2_B3_CTL, 0x3C, 0x00); + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_RX2_B4_CTL, 0x18, 0xFF); + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_RX2_B3_CTL, 0x80, 0x00); + break; + case DIG_CDC_EVENT_SSR_DOWN: + regcache_cache_only(msm_dig_cdc->regmap, true); + break; + case DIG_CDC_EVENT_SSR_UP: + regcache_cache_only(msm_dig_cdc->regmap, false); + regcache_mark_dirty(msm_dig_cdc->regmap); + + mutex_lock(&pdata->cdc_int_mclk0_mutex); + pdata->digital_cdc_core_clk.enable = 1; + ret = afe_set_lpass_clock_v2( + AFE_PORT_ID_INT0_MI2S_RX, + &pdata->digital_cdc_core_clk); + if (ret < 0) { + pr_err("%s:failed to enable the MCLK\n", + __func__); + mutex_unlock(&pdata->cdc_int_mclk0_mutex); + break; + } + mutex_unlock(&pdata->cdc_int_mclk0_mutex); + + regcache_sync(msm_dig_cdc->regmap); + + mutex_lock(&pdata->cdc_int_mclk0_mutex); + pdata->digital_cdc_core_clk.enable = 0; + afe_set_lpass_clock_v2( + AFE_PORT_ID_INT0_MI2S_RX, + &pdata->digital_cdc_core_clk); + mutex_unlock(&pdata->cdc_int_mclk0_mutex); + break; + case DIG_CDC_EVENT_INVALID: + default: + break; + } + return 0; +} + +static ssize_t msm_dig_codec_version_read(struct snd_info_entry *entry, + void *file_private_data, + struct file *file, + char __user *buf, size_t count, + loff_t pos) +{ + struct msm_dig_priv *msm_dig; + char buffer[MSM_DIG_CDC_VERSION_ENTRY_SIZE]; + int len = 0; + + msm_dig = (struct msm_dig_priv *) entry->private_data; + if (!msm_dig) { + pr_err("%s: msm_dig priv is null\n", __func__); + return -EINVAL; + } + + switch (msm_dig->version) { + case DRAX_CDC: + len = snprintf(buffer, sizeof(buffer), "SDM660-CDC_1_0\n"); + break; + default: + len = snprintf(buffer, sizeof(buffer), "VER_UNDEFINED\n"); + } + + return simple_read_from_buffer(buf, count, &pos, buffer, len); +} + +static struct snd_info_entry_ops msm_dig_codec_info_ops = { + .read = msm_dig_codec_version_read, +}; + +/* + * msm_dig_codec_info_create_codec_entry - creates msm_dig module + * @codec_root: The parent directory + * @codec: Codec instance + * + * Creates msm_dig module and version entry under the given + * parent directory. + * + * Return: 0 on success or negative error code on failure. + */ +int msm_dig_codec_info_create_codec_entry(struct snd_info_entry *codec_root, + struct snd_soc_codec *codec) +{ + struct snd_info_entry *version_entry; + struct msm_dig_priv *msm_dig; + struct snd_soc_card *card; + + if (!codec_root || !codec) + return -EINVAL; + + msm_dig = snd_soc_codec_get_drvdata(codec); + card = codec->component.card; + msm_dig->entry = snd_info_create_subdir(codec_root->module, + "msm_digital_codec", + codec_root); + if (!msm_dig->entry) { + dev_dbg(codec->dev, "%s: failed to create msm_digital entry\n", + __func__); + return -ENOMEM; + } + + version_entry = snd_info_create_card_entry(card->snd_card, + "version", + msm_dig->entry); + if (!version_entry) { + dev_dbg(codec->dev, "%s: failed to create msm_digital version entry\n", + __func__); + return -ENOMEM; + } + + version_entry->private_data = msm_dig; + version_entry->size = MSM_DIG_CDC_VERSION_ENTRY_SIZE; + version_entry->content = SNDRV_INFO_CONTENT_DATA; + version_entry->c.ops = &msm_dig_codec_info_ops; + + if (snd_info_register(version_entry) < 0) { + snd_info_free_entry(version_entry); + return -ENOMEM; + } + msm_dig->version_entry = version_entry; + if (msm_dig->get_cdc_version) + msm_dig->version = msm_dig->get_cdc_version(msm_dig->handle); + else + msm_dig->version = DRAX_CDC; + + return 0; +} +EXPORT_SYMBOL(msm_dig_codec_info_create_codec_entry); + +static void sdm660_tx_mute_update_callback(struct work_struct *work) +{ + struct tx_mute_work *tx_mute_dwork; + struct snd_soc_codec *codec = NULL; + struct msm_dig_priv *dig_cdc; + struct delayed_work *delayed_work; + u16 tx_vol_ctl_reg = 0; + u8 decimator = 0, i; + + delayed_work = to_delayed_work(work); + tx_mute_dwork = container_of(delayed_work, struct tx_mute_work, dwork); + dig_cdc = tx_mute_dwork->dig_cdc; + codec = dig_cdc->codec; + + for (i = 0; i < NUM_DECIMATORS; i++) { + if (dig_cdc->dec_active[i]) + decimator = i + 1; + if (decimator && decimator <= NUM_DECIMATORS) { + /* unmute decimators corresponding to Tx DAI's*/ + tx_vol_ctl_reg = + MSM89XX_CDC_CORE_TX1_VOL_CTL_CFG + + 32 * (decimator - 1); + if (decimator == DEC_SVA) + tx_vol_ctl_reg = + MSM89XX_CDC_CORE_TX5_VOL_CTL_CFG; + + snd_soc_update_bits(codec, tx_vol_ctl_reg, + 0x01, 0x00); + } + decimator = 0; + } +} + +static int msm_dig_cdc_soc_probe(struct snd_soc_codec *codec) +{ + struct msm_dig_priv *msm_dig_cdc = dev_get_drvdata(codec->dev); + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + int i, ret; + + msm_dig_cdc->codec = codec; + + snd_soc_add_codec_controls(codec, compander_kcontrols, + ARRAY_SIZE(compander_kcontrols)); + + for (i = 0; i < NUM_DECIMATORS; i++) { + tx_hpf_work[i].dig_cdc = msm_dig_cdc; + tx_hpf_work[i].decimator = i + 1; + INIT_DELAYED_WORK(&tx_hpf_work[i].dwork, + tx_hpf_corner_freq_callback); + msm_dig_cdc->tx_mute_dwork[i].dig_cdc = msm_dig_cdc; + msm_dig_cdc->tx_mute_dwork[i].decimator = i + 1; + INIT_DELAYED_WORK(&msm_dig_cdc->tx_mute_dwork[i].dwork, + sdm660_tx_mute_update_callback); + } + + for (i = 0; i < MSM89XX_RX_MAX; i++) + msm_dig_cdc->comp_enabled[i] = COMPANDER_NONE; + + /* Register event notifier */ + msm_dig_cdc->nblock.notifier_call = msm_dig_cdc_event_notify; + if (msm_dig_cdc->register_notifier) { + ret = msm_dig_cdc->register_notifier(msm_dig_cdc->handle, + &msm_dig_cdc->nblock, + true); + if (ret) { + pr_err("%s: Failed to register notifier %d\n", + __func__, ret); + return ret; + } + } + registered_digcodec = codec; + + snd_soc_dapm_ignore_suspend(dapm, "AIF1 Playback"); + snd_soc_dapm_ignore_suspend(dapm, "AIF1 Capture"); + snd_soc_dapm_ignore_suspend(dapm, "ADC1_IN"); + snd_soc_dapm_ignore_suspend(dapm, "ADC2_IN"); + snd_soc_dapm_ignore_suspend(dapm, "ADC3_IN"); + snd_soc_dapm_ignore_suspend(dapm, "PDM_OUT_RX1"); + snd_soc_dapm_ignore_suspend(dapm, "PDM_OUT_RX2"); + snd_soc_dapm_ignore_suspend(dapm, "PDM_OUT_RX3"); + + snd_soc_dapm_sync(dapm); + + return 0; +} + +static int msm_dig_cdc_soc_remove(struct snd_soc_codec *codec) +{ + struct msm_dig_priv *msm_dig_cdc = dev_get_drvdata(codec->dev); + + if (msm_dig_cdc->register_notifier) + msm_dig_cdc->register_notifier(msm_dig_cdc->handle, + &msm_dig_cdc->nblock, + false); + iounmap(msm_dig_cdc->dig_base); + return 0; +} + +static const struct snd_soc_dapm_route audio_dig_map[] = { + {"RX_I2S_CLK", NULL, "CDC_CONN"}, + {"I2S RX1", NULL, "RX_I2S_CLK"}, + {"I2S RX2", NULL, "RX_I2S_CLK"}, + {"I2S RX3", NULL, "RX_I2S_CLK"}, + + {"I2S TX1", NULL, "TX_I2S_CLK"}, + {"I2S TX2", NULL, "TX_I2S_CLK"}, + {"I2S TX3", NULL, "TX_I2S_CLK"}, + {"I2S TX4", NULL, "TX_I2S_CLK"}, + {"I2S TX5", NULL, "TX_I2S_CLK"}, + {"I2S TX6", NULL, "TX_I2S_CLK"}, + + {"I2S TX1", NULL, "DEC1 MUX"}, + {"I2S TX2", NULL, "DEC2 MUX"}, + {"I2S TX3", NULL, "I2S TX2 INP1"}, + {"I2S TX4", NULL, "I2S TX2 INP2"}, + {"I2S TX5", NULL, "DEC3 MUX"}, + {"I2S TX6", NULL, "I2S TX3 INP2"}, + + {"I2S TX2 INP1", "RX_MIX1", "RX1 MIX2"}, + {"I2S TX2 INP1", "DEC3", "DEC3 MUX"}, + {"I2S TX2 INP2", "RX_MIX2", "RX2 MIX2"}, + {"I2S TX2 INP2", "RX_MIX3", "RX3 MIX1"}, + {"I2S TX2 INP2", "DEC4", "DEC4 MUX"}, + {"I2S TX3 INP2", "DEC4", "DEC4 MUX"}, + {"I2S TX3 INP2", "DEC5", "DEC5 MUX"}, + + {"PDM_OUT_RX1", NULL, "RX1 CHAIN"}, + {"PDM_OUT_RX2", NULL, "RX2 CHAIN"}, + {"PDM_OUT_RX3", NULL, "RX3 CHAIN"}, + + {"RX1 CHAIN", NULL, "RX1 MIX2"}, + {"RX2 CHAIN", NULL, "RX2 MIX2"}, + {"RX3 CHAIN", NULL, "RX3 MIX1"}, + + {"RX1 MIX1", NULL, "RX1 MIX1 INP1"}, + {"RX1 MIX1", NULL, "RX1 MIX1 INP2"}, + {"RX1 MIX1", NULL, "RX1 MIX1 INP3"}, + {"RX2 MIX1", NULL, "RX2 MIX1 INP1"}, + {"RX2 MIX1", NULL, "RX2 MIX1 INP2"}, + {"RX3 MIX1", NULL, "RX3 MIX1 INP1"}, + {"RX3 MIX1", NULL, "RX3 MIX1 INP2"}, + {"RX1 MIX2", NULL, "RX1 MIX1"}, + {"RX1 MIX2", NULL, "RX1 MIX2 INP1"}, + {"RX2 MIX2", NULL, "RX2 MIX1"}, + {"RX2 MIX2", NULL, "RX2 MIX2 INP1"}, + + {"RX1 MIX1 INP1", "RX1", "I2S RX1"}, + {"RX1 MIX1 INP1", "RX2", "I2S RX2"}, + {"RX1 MIX1 INP1", "RX3", "I2S RX3"}, + {"RX1 MIX1 INP1", "IIR1", "IIR1"}, + {"RX1 MIX1 INP1", "IIR2", "IIR2"}, + {"RX1 MIX1 INP2", "RX1", "I2S RX1"}, + {"RX1 MIX1 INP2", "RX2", "I2S RX2"}, + {"RX1 MIX1 INP2", "RX3", "I2S RX3"}, + {"RX1 MIX1 INP2", "IIR1", "IIR1"}, + {"RX1 MIX1 INP2", "IIR2", "IIR2"}, + {"RX1 MIX1 INP3", "RX1", "I2S RX1"}, + {"RX1 MIX1 INP3", "RX2", "I2S RX2"}, + {"RX1 MIX1 INP3", "RX3", "I2S RX3"}, + + {"RX2 MIX1 INP1", "RX1", "I2S RX1"}, + {"RX2 MIX1 INP1", "RX2", "I2S RX2"}, + {"RX2 MIX1 INP1", "RX3", "I2S RX3"}, + {"RX2 MIX1 INP1", "IIR1", "IIR1"}, + {"RX2 MIX1 INP1", "IIR2", "IIR2"}, + {"RX2 MIX1 INP2", "RX1", "I2S RX1"}, + {"RX2 MIX1 INP2", "RX2", "I2S RX2"}, + {"RX2 MIX1 INP2", "RX3", "I2S RX3"}, + {"RX2 MIX1 INP2", "IIR1", "IIR1"}, + {"RX2 MIX1 INP2", "IIR2", "IIR2"}, + {"RX2 MIX1 INP3", "RX1", "I2S RX1"}, + {"RX2 MIX1 INP3", "RX2", "I2S RX2"}, + {"RX2 MIX1 INP3", "RX3", "I2S RX3"}, + + {"RX3 MIX1 INP1", "RX1", "I2S RX1"}, + {"RX3 MIX1 INP1", "RX2", "I2S RX2"}, + {"RX3 MIX1 INP1", "RX3", "I2S RX3"}, + {"RX3 MIX1 INP1", "IIR1", "IIR1"}, + {"RX3 MIX1 INP1", "IIR2", "IIR2"}, + {"RX3 MIX1 INP2", "RX1", "I2S RX1"}, + {"RX3 MIX1 INP2", "RX2", "I2S RX2"}, + {"RX3 MIX1 INP2", "RX3", "I2S RX3"}, + {"RX3 MIX1 INP2", "IIR1", "IIR1"}, + {"RX3 MIX1 INP2", "IIR2", "IIR2"}, + {"RX3 MIX1 INP3", "RX1", "I2S RX1"}, + {"RX3 MIX1 INP3", "RX2", "I2S RX2"}, + {"RX3 MIX1 INP3", "RX3", "I2S RX3"}, + + {"RX1 MIX2 INP1", "IIR1", "IIR1"}, + {"RX2 MIX2 INP1", "IIR1", "IIR1"}, + {"RX1 MIX2 INP1", "IIR2", "IIR2"}, + {"RX2 MIX2 INP1", "IIR2", "IIR2"}, + + /* Decimator Inputs */ + {"DEC1 MUX", "DMIC1", "DMIC1"}, + {"DEC1 MUX", "DMIC2", "DMIC2"}, + {"DEC1 MUX", "DMIC3", "DMIC3"}, + {"DEC1 MUX", "DMIC4", "DMIC4"}, + {"DEC1 MUX", "ADC1", "ADC1_IN"}, + {"DEC1 MUX", "ADC2", "ADC2_IN"}, + {"DEC1 MUX", "ADC3", "ADC3_IN"}, + {"DEC1 MUX", NULL, "CDC_CONN"}, + + {"DEC2 MUX", "DMIC1", "DMIC1"}, + {"DEC2 MUX", "DMIC2", "DMIC2"}, + {"DEC2 MUX", "DMIC3", "DMIC3"}, + {"DEC2 MUX", "DMIC4", "DMIC4"}, + {"DEC2 MUX", "ADC1", "ADC1_IN"}, + {"DEC2 MUX", "ADC2", "ADC2_IN"}, + {"DEC2 MUX", "ADC3", "ADC3_IN"}, + {"DEC2 MUX", NULL, "CDC_CONN"}, + + {"DEC3 MUX", "DMIC1", "DMIC1"}, + {"DEC3 MUX", "DMIC2", "DMIC2"}, + {"DEC3 MUX", "DMIC3", "DMIC3"}, + {"DEC3 MUX", "DMIC4", "DMIC4"}, + {"DEC3 MUX", "ADC1", "ADC1_IN"}, + {"DEC3 MUX", "ADC2", "ADC2_IN"}, + {"DEC3 MUX", "ADC3", "ADC3_IN"}, + {"DEC3 MUX", NULL, "CDC_CONN"}, + + {"DEC4 MUX", "DMIC1", "DMIC1"}, + {"DEC4 MUX", "DMIC2", "DMIC2"}, + {"DEC4 MUX", "DMIC3", "DMIC3"}, + {"DEC4 MUX", "DMIC4", "DMIC4"}, + {"DEC4 MUX", "ADC1", "ADC1_IN"}, + {"DEC4 MUX", "ADC2", "ADC2_IN"}, + {"DEC4 MUX", "ADC3", "ADC3_IN"}, + {"DEC4 MUX", NULL, "CDC_CONN"}, + + {"DEC5 MUX", "DMIC1", "DMIC1"}, + {"DEC5 MUX", "DMIC2", "DMIC2"}, + {"DEC5 MUX", "DMIC3", "DMIC3"}, + {"DEC5 MUX", "DMIC4", "DMIC4"}, + {"DEC5 MUX", "ADC1", "ADC1_IN"}, + {"DEC5 MUX", "ADC2", "ADC2_IN"}, + {"DEC5 MUX", "ADC3", "ADC3_IN"}, + {"DEC5 MUX", NULL, "CDC_CONN"}, + + {"IIR1", NULL, "IIR1 INP1 MUX"}, + {"IIR1 INP1 MUX", "DEC1", "DEC1 MUX"}, + {"IIR1 INP1 MUX", "DEC2", "DEC2 MUX"}, + {"IIR1 INP1 MUX", "DEC3", "DEC3 MUX"}, + {"IIR1 INP1 MUX", "DEC4", "DEC4 MUX"}, + {"IIR2", NULL, "IIR2 INP1 MUX"}, + {"IIR2 INP1 MUX", "DEC1", "DEC1 MUX"}, + {"IIR2 INP1 MUX", "DEC2", "DEC2 MUX"}, + {"IIR1 INP1 MUX", "DEC3", "DEC3 MUX"}, + {"IIR1 INP1 MUX", "DEC4", "DEC4 MUX"}, +}; + + +static const char * const i2s_tx2_inp1_text[] = { + "ZERO", "RX_MIX1", "DEC3" +}; + +static const char * const i2s_tx2_inp2_text[] = { + "ZERO", "RX_MIX2", "RX_MIX3", "DEC4" +}; + +static const char * const i2s_tx3_inp2_text[] = { + "DEC4", "DEC5" +}; + +static const char * const rx_mix1_text[] = { + "ZERO", "IIR1", "IIR2", "RX1", "RX2", "RX3" +}; + +static const char * const rx_mix2_text[] = { + "ZERO", "IIR1", "IIR2" +}; + +static const char * const dec_mux_text[] = { + "ZERO", "ADC1", "ADC2", "ADC3", "DMIC1", "DMIC2", "DMIC3", "DMIC4" +}; + +static const char * const iir_inp1_text[] = { + "ZERO", "DEC1", "DEC2", "RX1", "RX2", "RX3", "DEC3", "DEC4" +}; + +/* I2S TX MUXes */ +static const struct soc_enum i2s_tx2_inp1_chain_enum = + SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_CONN_TX_I2S_SD1_CTL, + 2, 3, i2s_tx2_inp1_text); + +static const struct soc_enum i2s_tx2_inp2_chain_enum = + SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_CONN_TX_I2S_SD1_CTL, + 0, 4, i2s_tx2_inp2_text); + +static const struct soc_enum i2s_tx3_inp2_chain_enum = + SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_CONN_TX_I2S_SD1_CTL, + 4, 2, i2s_tx3_inp2_text); + +/* RX1 MIX1 */ +static const struct soc_enum rx_mix1_inp1_chain_enum = + SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_CONN_RX1_B1_CTL, + 0, 6, rx_mix1_text); + +static const struct soc_enum rx_mix1_inp2_chain_enum = + SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_CONN_RX1_B1_CTL, + 3, 6, rx_mix1_text); + +static const struct soc_enum rx_mix1_inp3_chain_enum = + SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_CONN_RX1_B2_CTL, + 0, 6, rx_mix1_text); + +/* RX1 MIX2 */ +static const struct soc_enum rx_mix2_inp1_chain_enum = + SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_CONN_RX1_B3_CTL, + 0, 3, rx_mix2_text); + +/* RX2 MIX1 */ +static const struct soc_enum rx2_mix1_inp1_chain_enum = + SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_CONN_RX2_B1_CTL, + 0, 6, rx_mix1_text); + +static const struct soc_enum rx2_mix1_inp2_chain_enum = + SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_CONN_RX2_B1_CTL, + 3, 6, rx_mix1_text); + +static const struct soc_enum rx2_mix1_inp3_chain_enum = + SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_CONN_RX2_B1_CTL, + 0, 6, rx_mix1_text); + +/* RX2 MIX2 */ +static const struct soc_enum rx2_mix2_inp1_chain_enum = + SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_CONN_RX2_B3_CTL, + 0, 3, rx_mix2_text); + +/* RX3 MIX1 */ +static const struct soc_enum rx3_mix1_inp1_chain_enum = + SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_CONN_RX3_B1_CTL, + 0, 6, rx_mix1_text); + +static const struct soc_enum rx3_mix1_inp2_chain_enum = + SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_CONN_RX3_B1_CTL, + 3, 6, rx_mix1_text); + +static const struct soc_enum rx3_mix1_inp3_chain_enum = + SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_CONN_RX3_B1_CTL, + 0, 6, rx_mix1_text); + +/* DEC */ +static const struct soc_enum dec1_mux_enum = + SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_CONN_TX_B1_CTL, + 0, 8, dec_mux_text); + +static const struct soc_enum dec2_mux_enum = + SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_CONN_TX_B1_CTL, + 3, 8, dec_mux_text); + +static const struct soc_enum dec3_mux_enum = + SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_CONN_TX_B2_CTL, + 0, 8, dec_mux_text); + +static const struct soc_enum dec4_mux_enum = + SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_CONN_TX_B2_CTL, + 3, 8, dec_mux_text); + +static const struct soc_enum decsva_mux_enum = + SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_CONN_TX_B3_CTL, + 0, 8, dec_mux_text); + +static const struct soc_enum iir1_inp1_mux_enum = + SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_CONN_EQ1_B1_CTL, + 0, 8, iir_inp1_text); + +static const struct soc_enum iir2_inp1_mux_enum = + SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_CONN_EQ2_B1_CTL, + 0, 8, iir_inp1_text); + +/*cut of frequency for high pass filter*/ +static const char * const cf_text[] = { + "MIN_3DB_4Hz", "MIN_3DB_75Hz", "MIN_3DB_150Hz" +}; + +static const struct soc_enum cf_rxmix1_enum = + SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_RX1_B4_CTL, 0, 3, cf_text); + +static const struct soc_enum cf_rxmix2_enum = + SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_RX2_B4_CTL, 0, 3, cf_text); + +static const struct soc_enum cf_rxmix3_enum = + SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_RX3_B4_CTL, 0, 3, cf_text); + +static const struct snd_kcontrol_new rx3_mix1_inp1_mux = + SOC_DAPM_ENUM("RX3 MIX1 INP1 Mux", rx3_mix1_inp1_chain_enum); + +#define MSM89XX_DEC_ENUM(xname, xenum) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = snd_soc_info_enum_double, \ + .get = snd_soc_dapm_get_enum_double, \ + .put = msm_dig_cdc_put_dec_enum, \ + .private_value = (unsigned long)&xenum } + +static const struct snd_kcontrol_new dec1_mux = + MSM89XX_DEC_ENUM("DEC1 MUX Mux", dec1_mux_enum); + +static const struct snd_kcontrol_new dec2_mux = + MSM89XX_DEC_ENUM("DEC2 MUX Mux", dec2_mux_enum); + +static const struct snd_kcontrol_new dec3_mux = + MSM89XX_DEC_ENUM("DEC3 MUX Mux", dec3_mux_enum); + +static const struct snd_kcontrol_new dec4_mux = + MSM89XX_DEC_ENUM("DEC4 MUX Mux", dec4_mux_enum); + +static const struct snd_kcontrol_new decsva_mux = + MSM89XX_DEC_ENUM("DEC5 MUX Mux", decsva_mux_enum); + +static const struct snd_kcontrol_new i2s_tx2_inp1_mux = + SOC_DAPM_ENUM("I2S TX2 INP1 Mux", i2s_tx2_inp1_chain_enum); + +static const struct snd_kcontrol_new i2s_tx2_inp2_mux = + SOC_DAPM_ENUM("I2S TX2 INP2 Mux", i2s_tx2_inp2_chain_enum); + +static const struct snd_kcontrol_new i2s_tx3_inp2_mux = + SOC_DAPM_ENUM("I2S TX3 INP2 Mux", i2s_tx3_inp2_chain_enum); + +static const struct snd_kcontrol_new iir1_inp1_mux = + SOC_DAPM_ENUM("IIR1 INP1 Mux", iir1_inp1_mux_enum); + +static const struct snd_kcontrol_new iir2_inp1_mux = + SOC_DAPM_ENUM("IIR2 INP1 Mux", iir2_inp1_mux_enum); + +static const struct snd_kcontrol_new rx_mix1_inp1_mux = + SOC_DAPM_ENUM("RX1 MIX1 INP1 Mux", rx_mix1_inp1_chain_enum); + +static const struct snd_kcontrol_new rx_mix1_inp2_mux = + SOC_DAPM_ENUM("RX1 MIX1 INP2 Mux", rx_mix1_inp2_chain_enum); + +static const struct snd_kcontrol_new rx_mix1_inp3_mux = + SOC_DAPM_ENUM("RX1 MIX1 INP3 Mux", rx_mix1_inp3_chain_enum); + +static const struct snd_kcontrol_new rx2_mix1_inp1_mux = + SOC_DAPM_ENUM("RX2 MIX1 INP1 Mux", rx2_mix1_inp1_chain_enum); + +static const struct snd_kcontrol_new rx2_mix1_inp2_mux = + SOC_DAPM_ENUM("RX2 MIX1 INP2 Mux", rx2_mix1_inp2_chain_enum); + +static const struct snd_kcontrol_new rx2_mix1_inp3_mux = + SOC_DAPM_ENUM("RX2 MIX1 INP3 Mux", rx2_mix1_inp3_chain_enum); + +static const struct snd_kcontrol_new rx3_mix1_inp2_mux = + SOC_DAPM_ENUM("RX3 MIX1 INP2 Mux", rx3_mix1_inp2_chain_enum); + +static const struct snd_kcontrol_new rx3_mix1_inp3_mux = + SOC_DAPM_ENUM("RX3 MIX1 INP3 Mux", rx3_mix1_inp3_chain_enum); + +static const struct snd_kcontrol_new rx1_mix2_inp1_mux = + SOC_DAPM_ENUM("RX1 MIX2 INP1 Mux", rx_mix2_inp1_chain_enum); + +static const struct snd_kcontrol_new rx2_mix2_inp1_mux = + SOC_DAPM_ENUM("RX2 MIX2 INP1 Mux", rx2_mix2_inp1_chain_enum); + +static const struct snd_soc_dapm_widget msm_dig_dapm_widgets[] = { + SND_SOC_DAPM_AIF_IN("I2S RX1", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("I2S RX2", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("I2S RX3", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_AIF_OUT("I2S TX1", "AIF1 Capture", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("I2S TX2", "AIF1 Capture", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("I2S TX3", "AIF1 Capture", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("I2S TX4", "AIF1 Capture", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("I2S TX5", "AIF1 Capture", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("I2S TX6", "AIF2 Capture", 0, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_MIXER_E("RX1 MIX2", MSM89XX_CDC_CORE_CLK_RX_B1_CTL, + MSM89XX_RX1, 0, NULL, 0, + msm_dig_cdc_codec_enable_interpolator, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("RX2 MIX2", MSM89XX_CDC_CORE_CLK_RX_B1_CTL, + MSM89XX_RX2, 0, NULL, 0, + msm_dig_cdc_codec_enable_interpolator, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("RX3 MIX1", MSM89XX_CDC_CORE_CLK_RX_B1_CTL, + MSM89XX_RX3, 0, NULL, 0, + msm_dig_cdc_codec_enable_interpolator, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MIXER("RX1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX2 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_MIXER("RX1 CHAIN", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX2 CHAIN", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX3 CHAIN", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_MUX("RX1 MIX1 INP1", SND_SOC_NOPM, 0, 0, + &rx_mix1_inp1_mux), + SND_SOC_DAPM_MUX("RX1 MIX1 INP2", SND_SOC_NOPM, 0, 0, + &rx_mix1_inp2_mux), + SND_SOC_DAPM_MUX("RX1 MIX1 INP3", SND_SOC_NOPM, 0, 0, + &rx_mix1_inp3_mux), + + SND_SOC_DAPM_MUX("RX2 MIX1 INP1", SND_SOC_NOPM, 0, 0, + &rx2_mix1_inp1_mux), + SND_SOC_DAPM_MUX("RX2 MIX1 INP2", SND_SOC_NOPM, 0, 0, + &rx2_mix1_inp2_mux), + SND_SOC_DAPM_MUX("RX2 MIX1 INP3", SND_SOC_NOPM, 0, 0, + &rx2_mix1_inp3_mux), + + SND_SOC_DAPM_MUX("RX3 MIX1 INP1", SND_SOC_NOPM, 0, 0, + &rx3_mix1_inp1_mux), + SND_SOC_DAPM_MUX("RX3 MIX1 INP2", SND_SOC_NOPM, 0, 0, + &rx3_mix1_inp2_mux), + SND_SOC_DAPM_MUX("RX3 MIX1 INP3", SND_SOC_NOPM, 0, 0, + &rx3_mix1_inp3_mux), + + SND_SOC_DAPM_MUX("RX1 MIX2 INP1", SND_SOC_NOPM, 0, 0, + &rx1_mix2_inp1_mux), + SND_SOC_DAPM_MUX("RX2 MIX2 INP1", SND_SOC_NOPM, 0, 0, + &rx2_mix2_inp1_mux), + + SND_SOC_DAPM_SUPPLY_S("CDC_CONN", -2, MSM89XX_CDC_CORE_CLK_OTHR_CTL, + 2, 0, NULL, 0), + + SND_SOC_DAPM_MUX_E("DEC1 MUX", + MSM89XX_CDC_CORE_CLK_TX_CLK_EN_B1_CTL, 0, 0, + &dec1_mux, msm_dig_cdc_codec_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX_E("DEC2 MUX", + MSM89XX_CDC_CORE_CLK_TX_CLK_EN_B1_CTL, 1, 0, + &dec2_mux, msm_dig_cdc_codec_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX_E("DEC3 MUX", + MSM89XX_CDC_CORE_CLK_TX_CLK_EN_B1_CTL, 2, 0, + &dec3_mux, msm_dig_cdc_codec_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX_E("DEC4 MUX", + MSM89XX_CDC_CORE_CLK_TX_CLK_EN_B1_CTL, 3, 0, + &dec4_mux, msm_dig_cdc_codec_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX_E("DEC5 MUX", + MSM89XX_CDC_CORE_CLK_TX_CLK_EN_B1_CTL, 4, 0, + &decsva_mux, msm_dig_cdc_codec_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + /* Sidetone */ + SND_SOC_DAPM_MUX("IIR1 INP1 MUX", SND_SOC_NOPM, 0, 0, &iir1_inp1_mux), + SND_SOC_DAPM_PGA_E("IIR1", MSM89XX_CDC_CORE_CLK_SD_CTL, 0, 0, NULL, 0, + msm_dig_cdc_codec_set_iir_gain, SND_SOC_DAPM_POST_PMU), + + SND_SOC_DAPM_MUX("IIR2 INP1 MUX", SND_SOC_NOPM, 0, 0, &iir2_inp1_mux), + SND_SOC_DAPM_PGA_E("IIR2", MSM89XX_CDC_CORE_CLK_SD_CTL, 1, 0, NULL, 0, + msm_dig_cdc_codec_set_iir_gain, SND_SOC_DAPM_POST_PMU), + + SND_SOC_DAPM_SUPPLY("RX_I2S_CLK", + MSM89XX_CDC_CORE_CLK_RX_I2S_CTL, 4, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("TX_I2S_CLK", + MSM89XX_CDC_CORE_CLK_TX_I2S_CTL, 4, 0, NULL, 0), + + + SND_SOC_DAPM_MUX("I2S TX2 INP1", SND_SOC_NOPM, 0, 0, + &i2s_tx2_inp1_mux), + SND_SOC_DAPM_MUX("I2S TX2 INP2", SND_SOC_NOPM, 0, 0, + &i2s_tx2_inp2_mux), + SND_SOC_DAPM_MUX("I2S TX3 INP2", SND_SOC_NOPM, 0, 0, + &i2s_tx3_inp2_mux), + + /* Digital Mic Inputs */ + SND_SOC_DAPM_ADC_E("DMIC1", NULL, SND_SOC_NOPM, 0, 0, + msm_dig_cdc_codec_enable_dmic, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_ADC_E("DMIC2", NULL, SND_SOC_NOPM, 0, 0, + msm_dig_cdc_codec_enable_dmic, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_ADC_E("DMIC3", NULL, SND_SOC_NOPM, 0, 0, + msm_dig_cdc_codec_enable_dmic, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_ADC_E("DMIC4", NULL, SND_SOC_NOPM, 0, 0, + msm_dig_cdc_codec_enable_dmic, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_INPUT("ADC1_IN"), + SND_SOC_DAPM_INPUT("ADC2_IN"), + SND_SOC_DAPM_INPUT("ADC3_IN"), + SND_SOC_DAPM_OUTPUT("PDM_OUT_RX1"), + SND_SOC_DAPM_OUTPUT("PDM_OUT_RX2"), + SND_SOC_DAPM_OUTPUT("PDM_OUT_RX3"), +}; + +static const struct soc_enum cf_dec1_enum = + SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_TX1_MUX_CTL, 4, 3, cf_text); + +static const struct soc_enum cf_dec2_enum = + SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_TX2_MUX_CTL, 4, 3, cf_text); + +static const struct soc_enum cf_dec3_enum = + SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_TX3_MUX_CTL, 4, 3, cf_text); + +static const struct soc_enum cf_dec4_enum = + SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_TX4_MUX_CTL, 4, 3, cf_text); + +static const struct soc_enum cf_decsva_enum = + SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_TX5_MUX_CTL, 4, 3, cf_text); + +static const struct snd_kcontrol_new msm_dig_snd_controls[] = { + SOC_SINGLE_SX_TLV("DEC1 Volume", + MSM89XX_CDC_CORE_TX1_VOL_CTL_GAIN, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("DEC2 Volume", + MSM89XX_CDC_CORE_TX2_VOL_CTL_GAIN, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("DEC3 Volume", + MSM89XX_CDC_CORE_TX3_VOL_CTL_GAIN, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("DEC4 Volume", + MSM89XX_CDC_CORE_TX4_VOL_CTL_GAIN, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("DEC5 Volume", + MSM89XX_CDC_CORE_TX5_VOL_CTL_GAIN, + 0, -84, 40, digital_gain), + + SOC_SINGLE_SX_TLV("IIR1 INP1 Volume", + MSM89XX_CDC_CORE_IIR1_GAIN_B1_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("IIR1 INP2 Volume", + MSM89XX_CDC_CORE_IIR1_GAIN_B2_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("IIR1 INP3 Volume", + MSM89XX_CDC_CORE_IIR1_GAIN_B3_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("IIR1 INP4 Volume", + MSM89XX_CDC_CORE_IIR1_GAIN_B4_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("IIR2 INP1 Volume", + MSM89XX_CDC_CORE_IIR2_GAIN_B1_CTL, + 0, -84, 40, digital_gain), + + SOC_SINGLE_SX_TLV("RX1 Digital Volume", + MSM89XX_CDC_CORE_RX1_VOL_CTL_B2_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("RX2 Digital Volume", + MSM89XX_CDC_CORE_RX2_VOL_CTL_B2_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("RX3 Digital Volume", + MSM89XX_CDC_CORE_RX3_VOL_CTL_B2_CTL, + 0, -84, 40, digital_gain), + + SOC_SINGLE_EXT("IIR1 Enable Band1", IIR1, BAND1, 1, 0, + msm_dig_cdc_get_iir_enable_audio_mixer, + msm_dig_cdc_put_iir_enable_audio_mixer), + SOC_SINGLE_EXT("IIR1 Enable Band2", IIR1, BAND2, 1, 0, + msm_dig_cdc_get_iir_enable_audio_mixer, + msm_dig_cdc_put_iir_enable_audio_mixer), + SOC_SINGLE_EXT("IIR1 Enable Band3", IIR1, BAND3, 1, 0, + msm_dig_cdc_get_iir_enable_audio_mixer, + msm_dig_cdc_put_iir_enable_audio_mixer), + SOC_SINGLE_EXT("IIR1 Enable Band4", IIR1, BAND4, 1, 0, + msm_dig_cdc_get_iir_enable_audio_mixer, + msm_dig_cdc_put_iir_enable_audio_mixer), + SOC_SINGLE_EXT("IIR1 Enable Band5", IIR1, BAND5, 1, 0, + msm_dig_cdc_get_iir_enable_audio_mixer, + msm_dig_cdc_put_iir_enable_audio_mixer), + + SOC_SINGLE_EXT("IIR2 Enable Band1", IIR2, BAND1, 1, 0, + msm_dig_cdc_get_iir_enable_audio_mixer, + msm_dig_cdc_put_iir_enable_audio_mixer), + SOC_SINGLE_EXT("IIR2 Enable Band2", IIR2, BAND2, 1, 0, + msm_dig_cdc_get_iir_enable_audio_mixer, + msm_dig_cdc_put_iir_enable_audio_mixer), + SOC_SINGLE_EXT("IIR2 Enable Band3", IIR2, BAND3, 1, 0, + msm_dig_cdc_get_iir_enable_audio_mixer, + msm_dig_cdc_put_iir_enable_audio_mixer), + SOC_SINGLE_EXT("IIR2 Enable Band4", IIR2, BAND4, 1, 0, + msm_dig_cdc_get_iir_enable_audio_mixer, + msm_dig_cdc_put_iir_enable_audio_mixer), + SOC_SINGLE_EXT("IIR2 Enable Band5", IIR2, BAND5, 1, 0, + msm_dig_cdc_get_iir_enable_audio_mixer, + msm_dig_cdc_put_iir_enable_audio_mixer), + + SOC_SINGLE_MULTI_EXT("IIR1 Band1", IIR1, BAND1, 255, 0, 5, + msm_dig_cdc_get_iir_band_audio_mixer, + msm_dig_cdc_put_iir_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("IIR1 Band2", IIR1, BAND2, 255, 0, 5, + msm_dig_cdc_get_iir_band_audio_mixer, + msm_dig_cdc_put_iir_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("IIR1 Band3", IIR1, BAND3, 255, 0, 5, + msm_dig_cdc_get_iir_band_audio_mixer, + msm_dig_cdc_put_iir_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("IIR1 Band4", IIR1, BAND4, 255, 0, 5, + msm_dig_cdc_get_iir_band_audio_mixer, + msm_dig_cdc_put_iir_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("IIR1 Band5", IIR1, BAND5, 255, 0, 5, + msm_dig_cdc_get_iir_band_audio_mixer, + msm_dig_cdc_put_iir_band_audio_mixer), + + SOC_SINGLE_MULTI_EXT("IIR2 Band1", IIR2, BAND1, 255, 0, 5, + msm_dig_cdc_get_iir_band_audio_mixer, + msm_dig_cdc_put_iir_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("IIR2 Band2", IIR2, BAND2, 255, 0, 5, + msm_dig_cdc_get_iir_band_audio_mixer, + msm_dig_cdc_put_iir_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("IIR2 Band3", IIR2, BAND3, 255, 0, 5, + msm_dig_cdc_get_iir_band_audio_mixer, + msm_dig_cdc_put_iir_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("IIR2 Band4", IIR2, BAND4, 255, 0, 5, + msm_dig_cdc_get_iir_band_audio_mixer, + msm_dig_cdc_put_iir_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("IIR2 Band5", IIR2, BAND5, 255, 0, 5, + msm_dig_cdc_get_iir_band_audio_mixer, + msm_dig_cdc_put_iir_band_audio_mixer), + + SOC_SINGLE("RX1 HPF Switch", + MSM89XX_CDC_CORE_RX1_B5_CTL, 2, 1, 0), + SOC_SINGLE("RX2 HPF Switch", + MSM89XX_CDC_CORE_RX2_B5_CTL, 2, 1, 0), + SOC_SINGLE("RX3 HPF Switch", + MSM89XX_CDC_CORE_RX3_B5_CTL, 2, 1, 0), + + SOC_ENUM("RX1 HPF cut off", cf_rxmix1_enum), + SOC_ENUM("RX2 HPF cut off", cf_rxmix2_enum), + SOC_ENUM("RX3 HPF cut off", cf_rxmix3_enum), + + SOC_ENUM("TX1 HPF cut off", cf_dec1_enum), + SOC_ENUM("TX2 HPF cut off", cf_dec2_enum), + SOC_ENUM("TX3 HPF cut off", cf_dec3_enum), + SOC_ENUM("TX4 HPF cut off", cf_dec4_enum), + SOC_ENUM("TX5 HPF cut off", cf_decsva_enum), + SOC_SINGLE("TX1 HPF Switch", + MSM89XX_CDC_CORE_TX1_MUX_CTL, 3, 1, 0), + SOC_SINGLE("TX2 HPF Switch", + MSM89XX_CDC_CORE_TX2_MUX_CTL, 3, 1, 0), + SOC_SINGLE("TX3 HPF Switch", + MSM89XX_CDC_CORE_TX3_MUX_CTL, 3, 1, 0), + SOC_SINGLE("TX4 HPF Switch", + MSM89XX_CDC_CORE_TX4_MUX_CTL, 3, 1, 0), + SOC_SINGLE("TX5 HPF Switch", + MSM89XX_CDC_CORE_TX5_MUX_CTL, 3, 1, 0), +}; + +static struct snd_soc_dai_ops msm_dig_dai_ops = { + .hw_params = msm_dig_cdc_hw_params, +}; + + +static struct snd_soc_dai_driver msm_codec_dais[] = { + { + .name = "msm_dig_cdc_dai_rx1", + .id = AIF1_PB, + .playback = { /* Support maximum range */ + .stream_name = "AIF1 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_192000, + .rate_max = 192000, + .rate_min = 8000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE, + }, + .ops = &msm_dig_dai_ops, + }, + { + .name = "msm_dig_cdc_dai_tx1", + .id = AIF1_CAP, + .capture = { /* Support maximum range */ + .stream_name = "AIF1 Capture", + .channels_min = 1, + .channels_max = 4, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .ops = &msm_dig_dai_ops, + }, + { + .name = "msm_dig_cdc_dai_tx2", + .id = AIF3_SVA, + .capture = { /* Support maximum range */ + .stream_name = "AIF2 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .ops = &msm_dig_dai_ops, + }, + { + .name = "msm_dig_cdc_dai_vifeed", + .id = AIF2_VIFEED, + .capture = { /* Support maximum range */ + .stream_name = "AIF2 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .ops = &msm_dig_dai_ops, + }, +}; + +static struct regmap *msm_digital_get_regmap(struct device *dev) +{ + struct msm_dig_priv *msm_dig_cdc = dev_get_drvdata(dev); + + return msm_dig_cdc->regmap; +} + +static int msm_dig_cdc_suspend(struct snd_soc_codec *codec) +{ + struct msm_dig_priv *msm_dig_cdc = dev_get_drvdata(codec->dev); + + msm_dig_cdc->dapm_bias_off = 1; + return 0; +} + +static int msm_dig_cdc_resume(struct snd_soc_codec *codec) +{ + struct msm_dig_priv *msm_dig_cdc = dev_get_drvdata(codec->dev); + + msm_dig_cdc->dapm_bias_off = 0; + return 0; +} + +static struct snd_soc_codec_driver soc_msm_dig_codec = { + .probe = msm_dig_cdc_soc_probe, + .remove = msm_dig_cdc_soc_remove, + .suspend = msm_dig_cdc_suspend, + .resume = msm_dig_cdc_resume, + .get_regmap = msm_digital_get_regmap, + .component_driver = { + .controls = msm_dig_snd_controls, + .num_controls = ARRAY_SIZE(msm_dig_snd_controls), + .dapm_widgets = msm_dig_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(msm_dig_dapm_widgets), + .dapm_routes = audio_dig_map, + .num_dapm_routes = ARRAY_SIZE(audio_dig_map), + }, +}; + +const struct regmap_config msm_digital_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 8, + .lock = enable_digital_callback, + .unlock = disable_digital_callback, + .cache_type = REGCACHE_FLAT, + .reg_defaults = msm89xx_cdc_core_defaults, + .num_reg_defaults = MSM89XX_CDC_CORE_MAX_REGISTER, + .writeable_reg = msm89xx_cdc_core_writeable_reg, + .readable_reg = msm89xx_cdc_core_readable_reg, + .volatile_reg = msm89xx_cdc_core_volatile_reg, + .reg_format_endian = REGMAP_ENDIAN_NATIVE, + .val_format_endian = REGMAP_ENDIAN_NATIVE, + .max_register = MSM89XX_CDC_CORE_MAX_REGISTER, +}; + +static int msm_dig_cdc_probe(struct platform_device *pdev) +{ + int ret; + u32 dig_cdc_addr; + struct msm_dig_priv *msm_dig_cdc; + struct dig_ctrl_platform_data *pdata; + + msm_dig_cdc = devm_kzalloc(&pdev->dev, sizeof(struct msm_dig_priv), + GFP_KERNEL); + if (!msm_dig_cdc) + return -ENOMEM; + pdata = dev_get_platdata(&pdev->dev); + if (!pdata) { + dev_err(&pdev->dev, "%s: pdata from parent is NULL\n", + __func__); + ret = -EINVAL; + goto rtn; + } + + ret = of_property_read_u32(pdev->dev.of_node, "reg", + &dig_cdc_addr); + if (ret) { + dev_err(&pdev->dev, "%s: could not find %s entry in dt\n", + __func__, "reg"); + return ret; + } + + msm_dig_cdc->dig_base = ioremap(dig_cdc_addr, + MSM89XX_CDC_CORE_MAX_REGISTER); + if (msm_dig_cdc->dig_base == NULL) { + dev_err(&pdev->dev, "%s ioremap failed\n", __func__); + return -ENOMEM; + } + msm_dig_cdc->regmap = + devm_regmap_init_mmio_clk(&pdev->dev, NULL, + msm_dig_cdc->dig_base, &msm_digital_regmap_config); + + msm_dig_cdc->update_clkdiv = pdata->update_clkdiv; + msm_dig_cdc->set_compander_mode = pdata->set_compander_mode; + msm_dig_cdc->get_cdc_version = pdata->get_cdc_version; + msm_dig_cdc->handle = pdata->handle; + msm_dig_cdc->register_notifier = pdata->register_notifier; + + dev_set_drvdata(&pdev->dev, msm_dig_cdc); + snd_soc_register_codec(&pdev->dev, &soc_msm_dig_codec, + msm_codec_dais, ARRAY_SIZE(msm_codec_dais)); + dev_dbg(&pdev->dev, "%s: registered DIG CODEC 0x%x\n", + __func__, dig_cdc_addr); +rtn: + return ret; +} + +static int msm_dig_cdc_remove(struct platform_device *pdev) +{ + snd_soc_unregister_codec(&pdev->dev); + return 0; +} + +#ifdef CONFIG_PM +static int msm_dig_suspend(struct device *dev) +{ + struct msm_asoc_mach_data *pdata; + struct msm_dig_priv *msm_dig_cdc = dev_get_drvdata(dev); + + if (!registered_digcodec || !msm_dig_cdc) { + pr_debug("%s:digcodec not initialized, return\n", __func__); + return 0; + } + pdata = snd_soc_card_get_drvdata(registered_digcodec->component.card); + if (!pdata) { + pr_debug("%s:card not initialized, return\n", __func__); + return 0; + } + if (msm_dig_cdc->dapm_bias_off) { + pr_debug("%s: mclk cnt = %d, mclk_enabled = %d\n", + __func__, atomic_read(&pdata->int_mclk0_rsc_ref), + atomic_read(&pdata->int_mclk0_enabled)); + + if (atomic_read(&pdata->int_mclk0_enabled) == true) { + cancel_delayed_work_sync( + &pdata->disable_int_mclk0_work); + mutex_lock(&pdata->cdc_int_mclk0_mutex); + pdata->digital_cdc_core_clk.enable = 0; + afe_set_lpass_clock_v2(AFE_PORT_ID_INT0_MI2S_RX, + &pdata->digital_cdc_core_clk); + atomic_set(&pdata->int_mclk0_enabled, false); + mutex_unlock(&pdata->cdc_int_mclk0_mutex); + } + } + + return 0; +} + +static int msm_dig_resume(struct device *dev) +{ + return 0; +} + +static const struct dev_pm_ops msm_dig_pm_ops = { + .suspend_late = msm_dig_suspend, + .resume_early = msm_dig_resume, +}; +#endif + +static const struct of_device_id msm_dig_cdc_of_match[] = { + {.compatible = "qcom,msm-digital-codec"}, + {}, +}; + +static struct platform_driver msm_digcodec_driver = { + .driver = { + .owner = THIS_MODULE, + .name = DRV_NAME, + .of_match_table = msm_dig_cdc_of_match, +#ifdef CONFIG_PM + .pm = &msm_dig_pm_ops, +#endif + }, + .probe = msm_dig_cdc_probe, + .remove = msm_dig_cdc_remove, +}; +module_platform_driver(msm_digcodec_driver); + +MODULE_DESCRIPTION("MSM Audio Digital codec driver"); +MODULE_LICENSE("GPL v2"); diff --git a/audio-kernel/asoc/codecs/sdm660_cdc/msm-digital-cdc.h b/audio-kernel/asoc/codecs/sdm660_cdc/msm-digital-cdc.h new file mode 100644 index 000000000..7abb2045b --- /dev/null +++ b/audio-kernel/asoc/codecs/sdm660_cdc/msm-digital-cdc.h @@ -0,0 +1,116 @@ +/* Copyright (c) 2016-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 + * 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 MSM_DIGITAL_CDC_H +#define MSM_DIGITAL_CDC_H + +#define HPHL_PA_DISABLE (0x01 << 1) +#define HPHR_PA_DISABLE (0x01 << 2) +#define SPKR_PA_DISABLE (0x01 << 3) + +#define NUM_DECIMATORS 5 +/* Codec supports 1 compander */ +enum { + COMPANDER_NONE = 0, + COMPANDER_1, /* HPHL/R */ + COMPANDER_MAX, +}; + +/* Number of output I2S port */ +enum { + MSM89XX_RX1 = 0, + MSM89XX_RX2, + MSM89XX_RX3, + MSM89XX_RX_MAX, +}; + +struct tx_mute_work { + struct msm_dig_priv *dig_cdc; + u32 decimator; + struct delayed_work dwork; +}; + +struct msm_dig_priv { + struct snd_soc_codec *codec; + u32 comp_enabled[MSM89XX_RX_MAX]; + int (*codec_hph_comp_gpio)(bool enable, struct snd_soc_codec *codec); + s32 dmic_1_2_clk_cnt; + s32 dmic_3_4_clk_cnt; + bool dec_active[NUM_DECIMATORS]; + int version; + /* Entry for version info */ + struct snd_info_entry *entry; + struct snd_info_entry *version_entry; + char __iomem *dig_base; + struct regmap *regmap; + struct notifier_block nblock; + u32 mute_mask; + int dapm_bias_off; + void *handle; + void (*set_compander_mode)(void *handle, int val); + void (*update_clkdiv)(void *handle, int val); + int (*get_cdc_version)(void *handle); + int (*register_notifier)(void *handle, + struct notifier_block *nblock, + bool enable); + struct tx_mute_work tx_mute_dwork[NUM_DECIMATORS]; +}; + +struct dig_ctrl_platform_data { + void *handle; + void (*set_compander_mode)(void *handle, int val); + void (*update_clkdiv)(void *handle, int val); + int (*get_cdc_version)(void *handle); + int (*register_notifier)(void *handle, + struct notifier_block *nblock, + bool enable); +}; + +struct hpf_work { + struct msm_dig_priv *dig_cdc; + u32 decimator; + u8 tx_hpf_cut_of_freq; + struct delayed_work dwork; +}; + +/* Codec supports 5 bands */ +enum { + BAND1 = 0, + BAND2, + BAND3, + BAND4, + BAND5, + BAND_MAX, +}; + +#if IS_ENABLED(CONFIG_SND_SOC_DIGITAL_CDC) +extern void msm_dig_cdc_hph_comp_cb( + int (*codec_hph_comp_gpio)( + bool enable, struct snd_soc_codec *codec), + struct snd_soc_codec *codec); +int msm_dig_codec_info_create_codec_entry(struct snd_info_entry *codec_root, + struct snd_soc_codec *codec); +#else /* CONFIG_SND_SOC_DIGITAL_CDC */ +static inline void msm_dig_cdc_hph_comp_cb( + int (*codec_hph_comp_gpio)( + bool enable, struct snd_soc_codec *codec), + struct snd_soc_codec *codec) +{ + +} +static inline int msm_dig_codec_info_create_codec_entry( + struct snd_info_entry *codec_root, + struct snd_soc_codec *codec) +{ + return 0; +} +#endif /* CONFIG_SND_SOC_DIGITAL_CDC */ +#endif diff --git a/audio-kernel/asoc/codecs/sdm660_cdc/sdm660-cdc-irq.c b/audio-kernel/asoc/codecs/sdm660_cdc/sdm660-cdc-irq.c new file mode 100644 index 000000000..673b738b4 --- /dev/null +++ b/audio-kernel/asoc/codecs/sdm660_cdc/sdm660-cdc-irq.c @@ -0,0 +1,418 @@ +/* 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 + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "msm-analog-cdc.h" +#include "sdm660-cdc-irq.h" +#include "sdm660-cdc-registers.h" + +#define MAX_NUM_IRQS 14 +#define NUM_IRQ_REGS 2 +#define WCD9XXX_SYSTEM_RESUME_TIMEOUT_MS 700 + +#define BYTE_BIT_MASK(nr) (1UL << ((nr) % BITS_PER_BYTE)) +#define BIT_BYTE(nr) ((nr) / BITS_PER_BYTE) + +static irqreturn_t wcd9xxx_spmi_irq_handler(int linux_irq, void *data); + +char *irq_names[MAX_NUM_IRQS] = { + "spk_cnp_int", + "spk_clip_int", + "spk_ocp_int", + "ins_rem_det1", + "but_rel_det", + "but_press_det", + "ins_rem_det", + "mbhc_int", + "ear_ocp_int", + "hphr_ocp_int", + "hphl_ocp_det", + "ear_cnp_int", + "hphr_cnp_int", + "hphl_cnp_int" +}; + +int order[MAX_NUM_IRQS] = { + MSM89XX_IRQ_SPKR_CNP, + MSM89XX_IRQ_SPKR_CLIP, + MSM89XX_IRQ_SPKR_OCP, + MSM89XX_IRQ_MBHC_INSREM_DET1, + MSM89XX_IRQ_MBHC_RELEASE, + MSM89XX_IRQ_MBHC_PRESS, + MSM89XX_IRQ_MBHC_INSREM_DET, + MSM89XX_IRQ_MBHC_HS_DET, + MSM89XX_IRQ_EAR_OCP, + MSM89XX_IRQ_HPHR_OCP, + MSM89XX_IRQ_HPHL_OCP, + MSM89XX_IRQ_EAR_CNP, + MSM89XX_IRQ_HPHR_CNP, + MSM89XX_IRQ_HPHL_CNP, +}; + +enum wcd9xxx_spmi_pm_state { + WCD9XXX_PM_SLEEPABLE, + WCD9XXX_PM_AWAKE, + WCD9XXX_PM_ASLEEP, +}; + +struct wcd9xxx_spmi_map { + uint8_t handled[NUM_IRQ_REGS]; + uint8_t mask[NUM_IRQ_REGS]; + int linuxirq[MAX_NUM_IRQS]; + irq_handler_t handler[MAX_NUM_IRQS]; + struct platform_device *spmi[NUM_IRQ_REGS]; + struct snd_soc_codec *codec; + + enum wcd9xxx_spmi_pm_state pm_state; + struct mutex pm_lock; + /* pm_wq notifies change of pm_state */ + wait_queue_head_t pm_wq; + struct pm_qos_request pm_qos_req; + int wlock_holders; +}; + +struct wcd9xxx_spmi_map map; + +void wcd9xxx_spmi_enable_irq(int irq) +{ + pr_debug("%s: irqno =%d\n", __func__, irq); + + if (!(map.mask[BIT_BYTE(irq)] & (BYTE_BIT_MASK(irq)))) + return; + + map.mask[BIT_BYTE(irq)] &= + ~(BYTE_BIT_MASK(irq)); + + enable_irq(map.linuxirq[irq]); +} + +void wcd9xxx_spmi_disable_irq(int irq) +{ + pr_debug("%s: irqno =%d\n", __func__, irq); + + if (map.mask[BIT_BYTE(irq)] & (BYTE_BIT_MASK(irq))) + return; + + map.mask[BIT_BYTE(irq)] |= + (BYTE_BIT_MASK(irq)); + + disable_irq_nosync(map.linuxirq[irq]); +} + +int wcd9xxx_spmi_request_irq(int irq, irq_handler_t handler, + const char *name, void *priv) +{ + int rc; + unsigned long irq_flags; + + map.linuxirq[irq] = + platform_get_irq_byname(map.spmi[BIT_BYTE(irq)], + irq_names[irq]); + + if (strcmp(name, "mbhc sw intr")) + irq_flags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | + IRQF_ONESHOT; + else + irq_flags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | + IRQF_ONESHOT | IRQF_NO_SUSPEND; + pr_debug("%s: name:%s irq_flags = %lx\n", __func__, name, irq_flags); + + rc = devm_request_threaded_irq(&map.spmi[BIT_BYTE(irq)]->dev, + map.linuxirq[irq], NULL, + wcd9xxx_spmi_irq_handler, + irq_flags, + name, priv); + if (rc < 0) { + dev_err(&map.spmi[BIT_BYTE(irq)]->dev, + "Can't request %d IRQ\n", irq); + return rc; + } + + dev_dbg(&map.spmi[BIT_BYTE(irq)]->dev, + "irq %d linuxIRQ: %d\n", irq, map.linuxirq[irq]); + map.mask[BIT_BYTE(irq)] &= ~BYTE_BIT_MASK(irq); + map.handler[irq] = handler; + enable_irq_wake(map.linuxirq[irq]); + return 0; +} + +int wcd9xxx_spmi_free_irq(int irq, void *priv) +{ + devm_free_irq(&map.spmi[BIT_BYTE(irq)]->dev, map.linuxirq[irq], + priv); + map.mask[BIT_BYTE(irq)] |= BYTE_BIT_MASK(irq); + return 0; +} + +static int get_irq_bit(int linux_irq) +{ + int i = 0; + + for (; i < MAX_NUM_IRQS; i++) + if (map.linuxirq[i] == linux_irq) + return i; + + return i; +} + +static int get_order_irq(int i) +{ + return order[i]; +} + +static irqreturn_t wcd9xxx_spmi_irq_handler(int linux_irq, void *data) +{ + int irq, i, j; + unsigned long status[NUM_IRQ_REGS] = {0}; + + if (unlikely(wcd9xxx_spmi_lock_sleep() == false)) { + pr_err("Failed to hold suspend\n"); + return IRQ_NONE; + } + + irq = get_irq_bit(linux_irq); + if (irq == MAX_NUM_IRQS) + return IRQ_HANDLED; + + status[BIT_BYTE(irq)] |= BYTE_BIT_MASK(irq); + for (i = 0; i < NUM_IRQ_REGS; i++) { + status[i] |= snd_soc_read(map.codec, + BIT_BYTE(irq) * 0x100 + + MSM89XX_PMIC_DIGITAL_INT_LATCHED_STS); + status[i] &= ~map.mask[i]; + } + for (i = 0; i < MAX_NUM_IRQS; i++) { + j = get_order_irq(i); + if ((status[BIT_BYTE(j)] & BYTE_BIT_MASK(j)) && + ((map.handled[BIT_BYTE(j)] & + BYTE_BIT_MASK(j)) == 0)) { + map.handler[j](irq, data); + map.handled[BIT_BYTE(j)] |= + BYTE_BIT_MASK(j); + } + } + map.handled[BIT_BYTE(irq)] &= ~BYTE_BIT_MASK(irq); + wcd9xxx_spmi_unlock_sleep(); + + return IRQ_HANDLED; +} + +enum wcd9xxx_spmi_pm_state wcd9xxx_spmi_pm_cmpxchg( + enum wcd9xxx_spmi_pm_state o, + enum wcd9xxx_spmi_pm_state n) +{ + enum wcd9xxx_spmi_pm_state old; + + mutex_lock(&map.pm_lock); + old = map.pm_state; + if (old == o) + map.pm_state = n; + pr_debug("%s: map.pm_state = %d\n", __func__, map.pm_state); + mutex_unlock(&map.pm_lock); + return old; +} +EXPORT_SYMBOL(wcd9xxx_spmi_pm_cmpxchg); + +int wcd9xxx_spmi_suspend(pm_message_t pmesg) +{ + int ret = 0; + + pr_debug("%s: enter\n", __func__); + /* + * pm_qos_update_request() can be called after this suspend chain call + * started. thus suspend can be called while lock is being held + */ + mutex_lock(&map.pm_lock); + if (map.pm_state == WCD9XXX_PM_SLEEPABLE) { + pr_debug("%s: suspending system, state %d, wlock %d\n", + __func__, map.pm_state, + map.wlock_holders); + map.pm_state = WCD9XXX_PM_ASLEEP; + } else if (map.pm_state == WCD9XXX_PM_AWAKE) { + /* + * unlock to wait for pm_state == WCD9XXX_PM_SLEEPABLE + * then set to WCD9XXX_PM_ASLEEP + */ + pr_debug("%s: waiting to suspend system, state %d, wlock %d\n", + __func__, map.pm_state, + map.wlock_holders); + mutex_unlock(&map.pm_lock); + if (!(wait_event_timeout(map.pm_wq, + wcd9xxx_spmi_pm_cmpxchg( + WCD9XXX_PM_SLEEPABLE, + WCD9XXX_PM_ASLEEP) == + WCD9XXX_PM_SLEEPABLE, + HZ))) { + pr_debug("%s: suspend failed state %d, wlock %d\n", + __func__, map.pm_state, + map.wlock_holders); + ret = -EBUSY; + } else { + pr_debug("%s: done, state %d, wlock %d\n", __func__, + map.pm_state, + map.wlock_holders); + } + mutex_lock(&map.pm_lock); + } else if (map.pm_state == WCD9XXX_PM_ASLEEP) { + pr_warn("%s: system is already suspended, state %d, wlock %dn", + __func__, map.pm_state, + map.wlock_holders); + } + mutex_unlock(&map.pm_lock); + + return ret; +} +EXPORT_SYMBOL(wcd9xxx_spmi_suspend); + +int wcd9xxx_spmi_resume(void) +{ + int ret = 0; + + pr_debug("%s: enter\n", __func__); + mutex_lock(&map.pm_lock); + if (map.pm_state == WCD9XXX_PM_ASLEEP) { + pr_debug("%s: resuming system, state %d, wlock %d\n", __func__, + map.pm_state, + map.wlock_holders); + map.pm_state = WCD9XXX_PM_SLEEPABLE; + } else { + pr_warn("%s: system is already awake, state %d wlock %d\n", + __func__, map.pm_state, + map.wlock_holders); + } + mutex_unlock(&map.pm_lock); + wake_up_all(&map.pm_wq); + + return ret; +} +EXPORT_SYMBOL(wcd9xxx_spmi_resume); + +bool wcd9xxx_spmi_lock_sleep(void) +{ + /* + * wcd9xxx_spmi_{lock/unlock}_sleep will be called by + * wcd9xxx_spmi_irq_thread + * and its subroutines only motly. + * but btn0_lpress_fn is not wcd9xxx_spmi_irq_thread's subroutine and + * It can race with wcd9xxx_spmi_irq_thread. + * So need to embrace wlock_holders with mutex. + */ + mutex_lock(&map.pm_lock); + if (map.wlock_holders++ == 0) { + pr_debug("%s: holding wake lock\n", __func__); + pm_qos_update_request(&map.pm_qos_req, + msm_cpuidle_get_deep_idle_latency()); + pm_stay_awake(&map.spmi[0]->dev); + } + mutex_unlock(&map.pm_lock); + pr_debug("%s: wake lock counter %d\n", __func__, + map.wlock_holders); + pr_debug("%s: map.pm_state = %d\n", __func__, map.pm_state); + + if (!wait_event_timeout(map.pm_wq, + ((wcd9xxx_spmi_pm_cmpxchg( + WCD9XXX_PM_SLEEPABLE, + WCD9XXX_PM_AWAKE)) == + WCD9XXX_PM_SLEEPABLE || + (wcd9xxx_spmi_pm_cmpxchg( + WCD9XXX_PM_SLEEPABLE, + WCD9XXX_PM_AWAKE) == + WCD9XXX_PM_AWAKE)), + msecs_to_jiffies( + WCD9XXX_SYSTEM_RESUME_TIMEOUT_MS))) { + pr_warn("%s: system didn't resume within %dms, s %d, w %d\n", + __func__, + WCD9XXX_SYSTEM_RESUME_TIMEOUT_MS, map.pm_state, + map.wlock_holders); + wcd9xxx_spmi_unlock_sleep(); + return false; + } + wake_up_all(&map.pm_wq); + pr_debug("%s: leaving pm_state = %d\n", __func__, map.pm_state); + return true; +} +EXPORT_SYMBOL(wcd9xxx_spmi_lock_sleep); + +void wcd9xxx_spmi_unlock_sleep(void) +{ + mutex_lock(&map.pm_lock); + if (--map.wlock_holders == 0) { + pr_debug("%s: releasing wake lock pm_state %d -> %d\n", + __func__, map.pm_state, WCD9XXX_PM_SLEEPABLE); + /* + * if wcd9xxx_spmi_lock_sleep failed, pm_state would be still + * WCD9XXX_PM_ASLEEP, don't overwrite + */ + if (likely(map.pm_state == WCD9XXX_PM_AWAKE)) + map.pm_state = WCD9XXX_PM_SLEEPABLE; + pm_qos_update_request(&map.pm_qos_req, + PM_QOS_DEFAULT_VALUE); + pm_relax(&map.spmi[0]->dev); + } + mutex_unlock(&map.pm_lock); + pr_debug("%s: wake lock counter %d\n", __func__, + map.wlock_holders); + pr_debug("%s: map.pm_state = %d\n", __func__, map.pm_state); + wake_up_all(&map.pm_wq); +} +EXPORT_SYMBOL(wcd9xxx_spmi_unlock_sleep); + +void wcd9xxx_spmi_set_codec(struct snd_soc_codec *codec) +{ + map.codec = codec; +} + +void wcd9xxx_spmi_set_dev(struct platform_device *spmi, int i) +{ + if (i < NUM_IRQ_REGS) + map.spmi[i] = spmi; +} + +int wcd9xxx_spmi_irq_init(void) +{ + int i = 0; + + for (; i < MAX_NUM_IRQS; i++) + map.mask[BIT_BYTE(i)] |= BYTE_BIT_MASK(i); + mutex_init(&map.pm_lock); + map.wlock_holders = 0; + map.pm_state = WCD9XXX_PM_SLEEPABLE; + init_waitqueue_head(&map.pm_wq); + pm_qos_add_request(&map.pm_qos_req, + PM_QOS_CPU_DMA_LATENCY, + PM_QOS_DEFAULT_VALUE); + + return 0; +} + +void wcd9xxx_spmi_irq_exit(void) +{ + pm_qos_remove_request(&map.pm_qos_req); + mutex_destroy(&map.pm_lock); +} +MODULE_DESCRIPTION("MSM8x16 SPMI IRQ driver"); +MODULE_LICENSE("GPL v2"); diff --git a/audio-kernel/asoc/codecs/sdm660_cdc/sdm660-cdc-irq.h b/audio-kernel/asoc/codecs/sdm660_cdc/sdm660-cdc-irq.h new file mode 100644 index 000000000..0b9b56e12 --- /dev/null +++ b/audio-kernel/asoc/codecs/sdm660_cdc/sdm660-cdc-irq.h @@ -0,0 +1,35 @@ +/* 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 + * 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 __WCD9XXX_SPMI_IRQ_H__ +#define __WCD9XXX_SPMI_IRQ_H__ + +#include +#include +#include +#include +#include + +extern void wcd9xxx_spmi_enable_irq(int irq); +extern void wcd9xxx_spmi_disable_irq(int irq); +extern int wcd9xxx_spmi_request_irq(int irq, irq_handler_t handler, + const char *name, void *priv); +extern int wcd9xxx_spmi_free_irq(int irq, void *priv); +extern void wcd9xxx_spmi_set_codec(struct snd_soc_codec *codec); +extern void wcd9xxx_spmi_set_dev(struct platform_device *spmi, int i); +extern int wcd9xxx_spmi_irq_init(void); +extern void wcd9xxx_spmi_irq_exit(void); +extern int wcd9xxx_spmi_suspend(pm_message_t pmesg); +extern int wcd9xxx_spmi_resume(void); +bool wcd9xxx_spmi_lock_sleep(void); +void wcd9xxx_spmi_unlock_sleep(void); + +#endif diff --git a/audio-kernel/asoc/codecs/sdm660_cdc/sdm660-cdc-registers.h b/audio-kernel/asoc/codecs/sdm660_cdc/sdm660-cdc-registers.h new file mode 100644 index 000000000..1317ce169 --- /dev/null +++ b/audio-kernel/asoc/codecs/sdm660_cdc/sdm660-cdc-registers.h @@ -0,0 +1,603 @@ +/* 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 + * 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 SDM660_WCD_REGISTERS_H +#define SDM660_WCD_REGISTERS_H + +#define CDC_DIG_BASE 0xF000 +#define CDC_ANA_BASE 0xF100 + +#define MSM89XX_PMIC_DIGITAL_REVISION1 (CDC_DIG_BASE+0x000) +#define MSM89XX_PMIC_DIGITAL_REVISION1__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_REVISION2 (CDC_DIG_BASE+0x001) +#define MSM89XX_PMIC_DIGITAL_REVISION2__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_PERPH_TYPE (CDC_DIG_BASE+0x004) +#define MSM89XX_PMIC_DIGITAL_PERPH_TYPE__POR (0x23) +#define MSM89XX_PMIC_DIGITAL_PERPH_SUBTYPE (CDC_DIG_BASE+0x005) +#define MSM89XX_PMIC_DIGITAL_PERPH_SUBTYPE__POR (0x01) +#define MSM89XX_PMIC_DIGITAL_INT_RT_STS (CDC_DIG_BASE+0x010) +#define MSM89XX_PMIC_DIGITAL_INT_RT_STS__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_INT_SET_TYPE (CDC_DIG_BASE+0x011) +#define MSM89XX_PMIC_DIGITAL_INT_SET_TYPE__POR (0xFF) +#define MSM89XX_PMIC_DIGITAL_INT_POLARITY_HIGH (CDC_DIG_BASE+0x012) +#define MSM89XX_PMIC_DIGITAL_INT_POLARITY_HIGH__POR (0xFF) +#define MSM89XX_PMIC_DIGITAL_INT_POLARITY_LOW (CDC_DIG_BASE+0x013) +#define MSM89XX_PMIC_DIGITAL_INT_POLARITY_LOW__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_INT_LATCHED_CLR (CDC_DIG_BASE+0x014) +#define MSM89XX_PMIC_DIGITAL_INT_LATCHED_CLR__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_INT_EN_SET (CDC_DIG_BASE+0x015) +#define MSM89XX_PMIC_DIGITAL_INT_EN_SET__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_INT_EN_CLR (CDC_DIG_BASE+0x016) +#define MSM89XX_PMIC_DIGITAL_INT_EN_CLR__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_INT_LATCHED_STS (CDC_DIG_BASE+0x018) +#define MSM89XX_PMIC_DIGITAL_INT_LATCHED_STS__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_INT_PENDING_STS (CDC_DIG_BASE+0x019) +#define MSM89XX_PMIC_DIGITAL_INT_PENDING_STS__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_INT_MID_SEL (CDC_DIG_BASE+0x01A) +#define MSM89XX_PMIC_DIGITAL_INT_MID_SEL__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_INT_PRIORITY (CDC_DIG_BASE+0x01B) +#define MSM89XX_PMIC_DIGITAL_INT_PRIORITY__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_GPIO_MODE (CDC_DIG_BASE+0x040) +#define MSM89XX_PMIC_DIGITAL_GPIO_MODE__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_PIN_CTL_OE (CDC_DIG_BASE+0x041) +#define MSM89XX_PMIC_DIGITAL_PIN_CTL_OE__POR (0x01) +#define MSM89XX_PMIC_DIGITAL_PIN_CTL_DATA (CDC_DIG_BASE+0x042) +#define MSM89XX_PMIC_DIGITAL_PIN_CTL_DATA__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_PIN_STATUS (CDC_DIG_BASE+0x043) +#define MSM89XX_PMIC_DIGITAL_PIN_STATUS__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_HDRIVE_CTL (CDC_DIG_BASE+0x044) +#define MSM89XX_PMIC_DIGITAL_HDRIVE_CTL__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_CDC_RST_CTL (CDC_DIG_BASE+0x046) +#define MSM89XX_PMIC_DIGITAL_CDC_RST_CTL__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_CDC_TOP_CLK_CTL (CDC_DIG_BASE+0x048) +#define MSM89XX_PMIC_DIGITAL_CDC_TOP_CLK_CTL__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_CDC_ANA_CLK_CTL (CDC_DIG_BASE+0x049) +#define MSM89XX_PMIC_DIGITAL_CDC_ANA_CLK_CTL__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL (CDC_DIG_BASE+0x04A) +#define MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_CDC_CONN_TX1_CTL (CDC_DIG_BASE+0x050) +#define MSM89XX_PMIC_DIGITAL_CDC_CONN_TX1_CTL__POR (0x02) +#define MSM89XX_PMIC_DIGITAL_CDC_CONN_TX2_CTL (CDC_DIG_BASE+0x051) +#define MSM89XX_PMIC_DIGITAL_CDC_CONN_TX2_CTL__POR (0x02) +#define MSM89XX_PMIC_DIGITAL_CDC_CONN_HPHR_DAC_CTL (CDC_DIG_BASE+0x052) +#define MSM89XX_PMIC_DIGITAL_CDC_CONN_HPHR_DAC_CTL__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_CDC_CONN_RX1_CTL (CDC_DIG_BASE+0x053) +#define MSM89XX_PMIC_DIGITAL_CDC_CONN_RX1_CTL__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_CDC_CONN_RX2_CTL (CDC_DIG_BASE+0x054) +#define MSM89XX_PMIC_DIGITAL_CDC_CONN_RX2_CTL__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_CDC_CONN_RX3_CTL (CDC_DIG_BASE+0x055) +#define MSM89XX_PMIC_DIGITAL_CDC_CONN_RX3_CTL__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_CDC_CONN_RX_LB_CTL (CDC_DIG_BASE+0x056) +#define MSM89XX_PMIC_DIGITAL_CDC_CONN_RX_LB_CTL__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_CDC_RX_CTL1 (CDC_DIG_BASE+0x058) +#define MSM89XX_PMIC_DIGITAL_CDC_RX_CTL1__POR (0x7C) +#define MSM89XX_PMIC_DIGITAL_CDC_RX_CTL2 (CDC_DIG_BASE+0x059) +#define MSM89XX_PMIC_DIGITAL_CDC_RX_CTL2__POR (0x7C) +#define MSM89XX_PMIC_DIGITAL_CDC_RX_CTL3 (CDC_DIG_BASE+0x05A) +#define MSM89XX_PMIC_DIGITAL_CDC_RX_CTL3__POR (0x7C) +#define MSM89XX_PMIC_DIGITAL_DEM_BYPASS_DATA0 (CDC_DIG_BASE+0x05B) +#define MSM89XX_PMIC_DIGITAL_DEM_BYPASS_DATA0__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_DEM_BYPASS_DATA1 (CDC_DIG_BASE+0x05C) +#define MSM89XX_PMIC_DIGITAL_DEM_BYPASS_DATA1__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_DEM_BYPASS_DATA2 (CDC_DIG_BASE+0x05D) +#define MSM89XX_PMIC_DIGITAL_DEM_BYPASS_DATA2__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_DEM_BYPASS_DATA3 (CDC_DIG_BASE+0x05E) +#define MSM89XX_PMIC_DIGITAL_DEM_BYPASS_DATA3__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_DIG_DEBUG_CTL (CDC_DIG_BASE+0x068) +#define MSM89XX_PMIC_DIGITAL_DIG_DEBUG_CTL__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_DIG_DEBUG_EN (CDC_DIG_BASE+0x069) +#define MSM89XX_PMIC_DIGITAL_DIG_DEBUG_EN__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_SPARE_0 (CDC_DIG_BASE+0x070) +#define MSM89XX_PMIC_DIGITAL_SPARE_0__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_SPARE_1 (CDC_DIG_BASE+0x071) +#define MSM89XX_PMIC_DIGITAL_SPARE_1__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_SPARE_2 (CDC_DIG_BASE+0x072) +#define MSM89XX_PMIC_DIGITAL_SPARE_2__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_SEC_ACCESS (CDC_DIG_BASE+0x0D0) +#define MSM89XX_PMIC_DIGITAL_SEC_ACCESS__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_PERPH_RESET_CTL1 (CDC_DIG_BASE+0x0D8) +#define MSM89XX_PMIC_DIGITAL_PERPH_RESET_CTL1__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_PERPH_RESET_CTL2 (CDC_DIG_BASE+0x0D9) +#define MSM89XX_PMIC_DIGITAL_PERPH_RESET_CTL2__POR (0x01) +#define MSM89XX_PMIC_DIGITAL_PERPH_RESET_CTL3 (CDC_DIG_BASE+0x0DA) +#define MSM89XX_PMIC_DIGITAL_PERPH_RESET_CTL3__POR (0x05) +#define MSM89XX_PMIC_DIGITAL_PERPH_RESET_CTL4 (CDC_DIG_BASE+0x0DB) +#define MSM89XX_PMIC_DIGITAL_PERPH_RESET_CTL4__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_INT_TEST1 (CDC_DIG_BASE+0x0E0) +#define MSM89XX_PMIC_DIGITAL_INT_TEST1__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_INT_TEST_VAL (CDC_DIG_BASE+0x0E1) +#define MSM89XX_PMIC_DIGITAL_INT_TEST_VAL__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_TRIM_NUM (CDC_DIG_BASE+0x0F0) +#define MSM89XX_PMIC_DIGITAL_TRIM_NUM__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_TRIM_CTRL (CDC_DIG_BASE+0x0F1) +#define MSM89XX_PMIC_DIGITAL_TRIM_CTRL__POR (0x00) + +#define MSM89XX_PMIC_ANALOG_REVISION1 (CDC_ANA_BASE+0x00) +#define MSM89XX_PMIC_ANALOG_REVISION1__POR (0x00) +#define MSM89XX_PMIC_ANALOG_REVISION2 (CDC_ANA_BASE+0x01) +#define MSM89XX_PMIC_ANALOG_REVISION2__POR (0x00) +#define MSM89XX_PMIC_ANALOG_REVISION3 (CDC_ANA_BASE+0x02) +#define MSM89XX_PMIC_ANALOG_REVISION3__POR (0x00) +#define MSM89XX_PMIC_ANALOG_REVISION4 (CDC_ANA_BASE+0x03) +#define MSM89XX_PMIC_ANALOG_REVISION4__POR (0x00) +#define MSM89XX_PMIC_ANALOG_PERPH_TYPE (CDC_ANA_BASE+0x04) +#define MSM89XX_PMIC_ANALOG_PERPH_TYPE__POR (0x23) +#define MSM89XX_PMIC_ANALOG_PERPH_SUBTYPE (CDC_ANA_BASE+0x05) +#define MSM89XX_PMIC_ANALOG_PERPH_SUBTYPE__POR (0x09) +#define MSM89XX_PMIC_ANALOG_INT_RT_STS (CDC_ANA_BASE+0x10) +#define MSM89XX_PMIC_ANALOG_INT_RT_STS__POR (0x00) +#define MSM89XX_PMIC_ANALOG_INT_SET_TYPE (CDC_ANA_BASE+0x11) +#define MSM89XX_PMIC_ANALOG_INT_SET_TYPE__POR (0x3F) +#define MSM89XX_PMIC_ANALOG_INT_POLARITY_HIGH (CDC_ANA_BASE+0x12) +#define MSM89XX_PMIC_ANALOG_INT_POLARITY_HIGH__POR (0x3F) +#define MSM89XX_PMIC_ANALOG_INT_POLARITY_LOW (CDC_ANA_BASE+0x13) +#define MSM89XX_PMIC_ANALOG_INT_POLARITY_LOW__POR (0x00) +#define MSM89XX_PMIC_ANALOG_INT_LATCHED_CLR (CDC_ANA_BASE+0x14) +#define MSM89XX_PMIC_ANALOG_INT_LATCHED_CLR__POR (0x00) +#define MSM89XX_PMIC_ANALOG_INT_EN_SET (CDC_ANA_BASE+0x15) +#define MSM89XX_PMIC_ANALOG_INT_EN_SET__POR (0x00) +#define MSM89XX_PMIC_ANALOG_INT_EN_CLR (CDC_ANA_BASE+0x16) +#define MSM89XX_PMIC_ANALOG_INT_EN_CLR__POR (0x00) +#define MSM89XX_PMIC_ANALOG_INT_LATCHED_STS (CDC_ANA_BASE+0x18) +#define MSM89XX_PMIC_ANALOG_INT_LATCHED_STS__POR (0x00) +#define MSM89XX_PMIC_ANALOG_INT_PENDING_STS (CDC_ANA_BASE+0x19) +#define MSM89XX_PMIC_ANALOG_INT_PENDING_STS__POR (0x00) +#define MSM89XX_PMIC_ANALOG_INT_MID_SEL (CDC_ANA_BASE+0x1A) +#define MSM89XX_PMIC_ANALOG_INT_MID_SEL__POR (0x00) +#define MSM89XX_PMIC_ANALOG_INT_PRIORITY (CDC_ANA_BASE+0x1B) +#define MSM89XX_PMIC_ANALOG_INT_PRIORITY__POR (0x00) +#define MSM89XX_PMIC_ANALOG_MICB_1_EN (CDC_ANA_BASE+0x40) +#define MSM89XX_PMIC_ANALOG_MICB_1_EN__POR (0x00) +#define MSM89XX_PMIC_ANALOG_MICB_1_VAL (CDC_ANA_BASE+0x41) +#define MSM89XX_PMIC_ANALOG_MICB_1_VAL__POR (0x20) +#define MSM89XX_PMIC_ANALOG_MICB_1_CTL (CDC_ANA_BASE+0x42) +#define MSM89XX_PMIC_ANALOG_MICB_1_CTL__POR (0x00) +#define MSM89XX_PMIC_ANALOG_MICB_1_INT_RBIAS (CDC_ANA_BASE+0x43) +#define MSM89XX_PMIC_ANALOG_MICB_1_INT_RBIAS__POR (0x49) +#define MSM89XX_PMIC_ANALOG_MICB_2_EN (CDC_ANA_BASE+0x44) +#define MSM89XX_PMIC_ANALOG_MICB_2_EN__POR (0x20) +#define MSM89XX_PMIC_ANALOG_TX_1_2_ATEST_CTL_2 (CDC_ANA_BASE+0x45) +#define MSM89XX_PMIC_ANALOG_TX_1_2_ATEST_CTL_2__POR (0x00) +#define MSM89XX_PMIC_ANALOG_MASTER_BIAS_CTL (CDC_ANA_BASE+0x46) +#define MSM89XX_PMIC_ANALOG_MASTER_BIAS_CTL__POR (0x00) +#define MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_1 (CDC_ANA_BASE+0x47) +#define MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_1__POR (0x35) +#define MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_2 (CDC_ANA_BASE+0x50) +#define MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_2__POR (0x08) +#define MSM89XX_PMIC_ANALOG_MBHC_FSM_CTL (CDC_ANA_BASE+0x51) +#define MSM89XX_PMIC_ANALOG_MBHC_FSM_CTL__POR (0x00) +#define MSM89XX_PMIC_ANALOG_MBHC_DBNC_TIMER (CDC_ANA_BASE+0x52) +#define MSM89XX_PMIC_ANALOG_MBHC_DBNC_TIMER__POR (0x98) +#define MSM89XX_PMIC_ANALOG_MBHC_BTN0_ZDETL_CTL (CDC_ANA_BASE+0x53) +#define MSM89XX_PMIC_ANALOG_MBHC_BTN0_ZDETL_CTL__POR (0x00) +#define MSM89XX_PMIC_ANALOG_MBHC_BTN1_ZDETM_CTL (CDC_ANA_BASE+0x54) +#define MSM89XX_PMIC_ANALOG_MBHC_BTN1_ZDETM_CTL__POR (0x20) +#define MSM89XX_PMIC_ANALOG_MBHC_BTN2_ZDETH_CTL (CDC_ANA_BASE+0x55) +#define MSM89XX_PMIC_ANALOG_MBHC_BTN2_ZDETH_CTL__POR (0x40) +#define MSM89XX_PMIC_ANALOG_MBHC_BTN3_CTL (CDC_ANA_BASE+0x56) +#define MSM89XX_PMIC_ANALOG_MBHC_BTN3_CTL__POR (0x61) +#define MSM89XX_PMIC_ANALOG_MBHC_BTN4_CTL (CDC_ANA_BASE+0x57) +#define MSM89XX_PMIC_ANALOG_MBHC_BTN4_CTL__POR (0x80) +#define MSM89XX_PMIC_ANALOG_MBHC_BTN_RESULT (CDC_ANA_BASE+0x58) +#define MSM89XX_PMIC_ANALOG_MBHC_BTN_RESULT__POR (0x00) +#define MSM89XX_PMIC_ANALOG_MBHC_ZDET_ELECT_RESULT (CDC_ANA_BASE+0x59) +#define MSM89XX_PMIC_ANALOG_MBHC_ZDET_ELECT_RESULT__POR (0x00) +#define MSM89XX_PMIC_ANALOG_TX_1_EN (CDC_ANA_BASE+0x60) +#define MSM89XX_PMIC_ANALOG_TX_1_EN__POR (0x03) +#define MSM89XX_PMIC_ANALOG_TX_2_EN (CDC_ANA_BASE+0x61) +#define MSM89XX_PMIC_ANALOG_TX_2_EN__POR (0x03) +#define MSM89XX_PMIC_ANALOG_TX_1_2_TEST_CTL_1 (CDC_ANA_BASE+0x62) +#define MSM89XX_PMIC_ANALOG_TX_1_2_TEST_CTL_1__POR (0xBF) +#define MSM89XX_PMIC_ANALOG_TX_1_2_TEST_CTL_2 (CDC_ANA_BASE+0x63) +#define MSM89XX_PMIC_ANALOG_TX_1_2_TEST_CTL_2__POR (0x8C) +#define MSM89XX_PMIC_ANALOG_TX_1_2_ATEST_CTL (CDC_ANA_BASE+0x64) +#define MSM89XX_PMIC_ANALOG_TX_1_2_ATEST_CTL__POR (0x00) +#define MSM89XX_PMIC_ANALOG_TX_1_2_OPAMP_BIAS (CDC_ANA_BASE+0x65) +#define MSM89XX_PMIC_ANALOG_TX_1_2_OPAMP_BIAS__POR (0x6B) +#define MSM89XX_PMIC_ANALOG_TX_1_2_TXFE_CLKDIV (CDC_ANA_BASE+0x66) +#define MSM89XX_PMIC_ANALOG_TX_1_2_TXFE_CLKDIV__POR (0x51) +#define MSM89XX_PMIC_ANALOG_TX_3_EN (CDC_ANA_BASE+0x67) +#define MSM89XX_PMIC_ANALOG_TX_3_EN__POR (0x02) +#define MSM89XX_PMIC_ANALOG_NCP_EN (CDC_ANA_BASE+0x80) +#define MSM89XX_PMIC_ANALOG_NCP_EN__POR (0x26) +#define MSM89XX_PMIC_ANALOG_NCP_CLK (CDC_ANA_BASE+0x81) +#define MSM89XX_PMIC_ANALOG_NCP_CLK__POR (0x23) +#define MSM89XX_PMIC_ANALOG_NCP_DEGLITCH (CDC_ANA_BASE+0x82) +#define MSM89XX_PMIC_ANALOG_NCP_DEGLITCH__POR (0x5B) +#define MSM89XX_PMIC_ANALOG_NCP_FBCTRL (CDC_ANA_BASE+0x83) +#define MSM89XX_PMIC_ANALOG_NCP_FBCTRL__POR (0x08) +#define MSM89XX_PMIC_ANALOG_NCP_BIAS (CDC_ANA_BASE+0x84) +#define MSM89XX_PMIC_ANALOG_NCP_BIAS__POR (0x29) +#define MSM89XX_PMIC_ANALOG_NCP_VCTRL (CDC_ANA_BASE+0x85) +#define MSM89XX_PMIC_ANALOG_NCP_VCTRL__POR (0x24) +#define MSM89XX_PMIC_ANALOG_NCP_TEST (CDC_ANA_BASE+0x86) +#define MSM89XX_PMIC_ANALOG_NCP_TEST__POR (0x00) +#define MSM89XX_PMIC_ANALOG_NCP_CLIM_ADDR (CDC_ANA_BASE+0x87) +#define MSM89XX_PMIC_ANALOG_NCP_CLIM_ADDR__POR (0xD5) +#define MSM89XX_PMIC_ANALOG_RX_CLOCK_DIVIDER (CDC_ANA_BASE+0x90) +#define MSM89XX_PMIC_ANALOG_RX_CLOCK_DIVIDER__POR (0xE8) +#define MSM89XX_PMIC_ANALOG_RX_COM_OCP_CTL (CDC_ANA_BASE+0x91) +#define MSM89XX_PMIC_ANALOG_RX_COM_OCP_CTL__POR (0xCF) +#define MSM89XX_PMIC_ANALOG_RX_COM_OCP_COUNT (CDC_ANA_BASE+0x92) +#define MSM89XX_PMIC_ANALOG_RX_COM_OCP_COUNT__POR (0x6E) +#define MSM89XX_PMIC_ANALOG_RX_COM_BIAS_DAC (CDC_ANA_BASE+0x93) +#define MSM89XX_PMIC_ANALOG_RX_COM_BIAS_DAC__POR (0x18) +#define MSM89XX_PMIC_ANALOG_RX_HPH_BIAS_PA (CDC_ANA_BASE+0x94) +#define MSM89XX_PMIC_ANALOG_RX_HPH_BIAS_PA__POR (0x5A) +#define MSM89XX_PMIC_ANALOG_RX_HPH_BIAS_LDO_OCP (CDC_ANA_BASE+0x95) +#define MSM89XX_PMIC_ANALOG_RX_HPH_BIAS_LDO_OCP__POR (0x69) +#define MSM89XX_PMIC_ANALOG_RX_HPH_BIAS_CNP (CDC_ANA_BASE+0x96) +#define MSM89XX_PMIC_ANALOG_RX_HPH_BIAS_CNP__POR (0x29) +#define MSM89XX_PMIC_ANALOG_RX_HPH_CNP_EN (CDC_ANA_BASE+0x97) +#define MSM89XX_PMIC_ANALOG_RX_HPH_CNP_EN__POR (0x80) +#define MSM89XX_PMIC_ANALOG_RX_HPH_CNP_WG_CTL (CDC_ANA_BASE+0x98) +#define MSM89XX_PMIC_ANALOG_RX_HPH_CNP_WG_CTL__POR (0xDA) +#define MSM89XX_PMIC_ANALOG_RX_HPH_CNP_WG_TIME (CDC_ANA_BASE+0x99) +#define MSM89XX_PMIC_ANALOG_RX_HPH_CNP_WG_TIME__POR (0x16) +#define MSM89XX_PMIC_ANALOG_RX_HPH_L_TEST (CDC_ANA_BASE+0x9A) +#define MSM89XX_PMIC_ANALOG_RX_HPH_L_TEST__POR (0x00) +#define MSM89XX_PMIC_ANALOG_RX_HPH_L_PA_DAC_CTL (CDC_ANA_BASE+0x9B) +#define MSM89XX_PMIC_ANALOG_RX_HPH_L_PA_DAC_CTL__POR (0x20) +#define MSM89XX_PMIC_ANALOG_RX_HPH_R_TEST (CDC_ANA_BASE+0x9C) +#define MSM89XX_PMIC_ANALOG_RX_HPH_R_TEST__POR (0x00) +#define MSM89XX_PMIC_ANALOG_RX_HPH_R_PA_DAC_CTL (CDC_ANA_BASE+0x9D) +#define MSM89XX_PMIC_ANALOG_RX_HPH_R_PA_DAC_CTL__POR (0x20) +#define MSM89XX_PMIC_ANALOG_RX_EAR_CTL (CDC_ANA_BASE+0x9E) +#define MSM89XX_PMIC_ANALOG_RX_EAR_CTL___POR (0x12) +#define MSM89XX_PMIC_ANALOG_RX_ATEST (CDC_ANA_BASE+0x9F) +#define MSM89XX_PMIC_ANALOG_RX_ATEST__POR (0x00) +#define MSM89XX_PMIC_ANALOG_RX_HPH_STATUS (CDC_ANA_BASE+0xA0) +#define MSM89XX_PMIC_ANALOG_RX_HPH_STATUS__POR (0x0C) +#define MSM89XX_PMIC_ANALOG_RX_EAR_STATUS (CDC_ANA_BASE+0xA1) +#define MSM89XX_PMIC_ANALOG_RX_EAR_STATUS__POR (0x00) +#define MSM89XX_PMIC_ANALOG_RX_LO_DAC_CTL (CDC_ANA_BASE+0xAC) +#define MSM89XX_PMIC_ANALOG_RX_LO_DAC_CTL__POR (0x00) +#define MSM89XX_PMIC_ANALOG_RX_LO_EN_CTL (CDC_ANA_BASE+0xAD) +#define MSM89XX_PMIC_ANALOG_RX_RX_LO_EN_CTL__POR (0x00) +#define MSM89XX_PMIC_ANALOG_SPKR_DAC_CTL (CDC_ANA_BASE+0xB0) +#define MSM89XX_PMIC_ANALOG_SPKR_DAC_CTL__POR (0x83) +#define MSM89XX_PMIC_ANALOG_SPKR_DRV_CLIP_DET (CDC_ANA_BASE+0xB1) +#define MSM89XX_PMIC_ANALOG_SPKR_DRV_CLIP_DET__POR (0x91) +#define MSM89XX_PMIC_ANALOG_SPKR_DRV_CTL (CDC_ANA_BASE+0xB2) +#define MSM89XX_PMIC_ANALOG_SPKR_DRV_CTL__POR (0x29) +#define MSM89XX_PMIC_ANALOG_SPKR_ANA_BIAS_SET (CDC_ANA_BASE+0xB3) +#define MSM89XX_PMIC_ANALOG_SPKR_ANA_BIAS_SET__POR (0x4D) +#define MSM89XX_PMIC_ANALOG_SPKR_OCP_CTL (CDC_ANA_BASE+0xB4) +#define MSM89XX_PMIC_ANALOG_SPKR_OCP_CTL__POR (0xE1) +#define MSM89XX_PMIC_ANALOG_SPKR_PWRSTG_CTL (CDC_ANA_BASE+0xB5) +#define MSM89XX_PMIC_ANALOG_SPKR_PWRSTG_CTL__POR (0x1E) +#define MSM89XX_PMIC_ANALOG_SPKR_DRV_MISC (CDC_ANA_BASE+0xB6) +#define MSM89XX_PMIC_ANALOG_SPKR_DRV_MISC__POR (0xCB) +#define MSM89XX_PMIC_ANALOG_SPKR_DRV_DBG (CDC_ANA_BASE+0xB7) +#define MSM89XX_PMIC_ANALOG_SPKR_DRV_DBG__POR (0x00) +#define MSM89XX_PMIC_ANALOG_CURRENT_LIMIT (CDC_ANA_BASE+0xC0) +#define MSM89XX_PMIC_ANALOG_CURRENT_LIMIT__POR (0x02) +#define MSM89XX_PMIC_ANALOG_OUTPUT_VOLTAGE (CDC_ANA_BASE+0xC1) +#define MSM89XX_PMIC_ANALOG_OUTPUT_VOLTAGE__POR (0x14) +#define MSM89XX_PMIC_ANALOG_BYPASS_MODE (CDC_ANA_BASE+0xC2) +#define MSM89XX_PMIC_ANALOG_BYPASS_MODE__POR (0x00) +#define MSM89XX_PMIC_ANALOG_BOOST_EN_CTL (CDC_ANA_BASE+0xC3) +#define MSM89XX_PMIC_ANALOG_BOOST_EN_CTL__POR (0x1F) +#define MSM89XX_PMIC_ANALOG_SLOPE_COMP_IP_ZERO (CDC_ANA_BASE+0xC4) +#define MSM89XX_PMIC_ANALOG_SLOPE_COMP_IP_ZERO__POR (0x8C) +#define MSM89XX_PMIC_ANALOG_RDSON_MAX_DUTY_CYCLE (CDC_ANA_BASE+0xC5) +#define MSM89XX_PMIC_ANALOG_RDSON_MAX_DUTY_CYCLE__POR (0xC0) +#define MSM89XX_PMIC_ANALOG_BOOST_TEST1_1 (CDC_ANA_BASE+0xC6) +#define MSM89XX_PMIC_ANALOG_BOOST_TEST1_1__POR (0x00) +#define MSM89XX_PMIC_ANALOG_BOOST_TEST_2 (CDC_ANA_BASE+0xC7) +#define MSM89XX_PMIC_ANALOG_BOOST_TEST_2__POR (0x00) +#define MSM89XX_PMIC_ANALOG_SPKR_SAR_STATUS (CDC_ANA_BASE+0xC8) +#define MSM89XX_PMIC_ANALOG_SPKR_SAR_STATUS__POR (0x00) +#define MSM89XX_PMIC_ANALOG_SPKR_DRV_STATUS (CDC_ANA_BASE+0xC9) +#define MSM89XX_PMIC_ANALOG_SPKR_DRV_STATUS__POR (0x00) +#define MSM89XX_PMIC_ANALOG_PBUS_ADD_CSR (CDC_ANA_BASE+0xCE) +#define MSM89XX_PMIC_ANALOG_PBUS_ADD_CSR__POR (0x00) +#define MSM89XX_PMIC_ANALOG_PBUS_ADD_SEL (CDC_ANA_BASE+0xCF) +#define MSM89XX_PMIC_ANALOG_PBUS_ADD_SEL__POR (0x00) +#define MSM89XX_PMIC_ANALOG_SEC_ACCESS (CDC_ANA_BASE+0xD0) +#define MSM89XX_PMIC_ANALOG_SEC_ACCESS__POR (0x00) +#define MSM89XX_PMIC_ANALOG_PERPH_RESET_CTL1 (CDC_ANA_BASE+0xD8) +#define MSM89XX_PMIC_ANALOG_PERPH_RESET_CTL1__POR (0x00) +#define MSM89XX_PMIC_ANALOG_PERPH_RESET_CTL2 (CDC_ANA_BASE+0xD9) +#define MSM89XX_PMIC_ANALOG_PERPH_RESET_CTL2__POR (0x01) +#define MSM89XX_PMIC_ANALOG_PERPH_RESET_CTL3 (CDC_ANA_BASE+0xDA) +#define MSM89XX_PMIC_ANALOG_PERPH_RESET_CTL3__POR (0x05) +#define MSM89XX_PMIC_ANALOG_PERPH_RESET_CTL4 (CDC_ANA_BASE+0xDB) +#define MSM89XX_PMIC_ANALOG_PERPH_RESET_CTL4__POR (0x00) +#define MSM89XX_PMIC_ANALOG_INT_TEST1 (CDC_ANA_BASE+0xE0) +#define MSM89XX_PMIC_ANALOG_INT_TEST1__POR (0x00) +#define MSM89XX_PMIC_ANALOG_INT_TEST_VAL (CDC_ANA_BASE+0xE1) +#define MSM89XX_PMIC_ANALOG_INT_TEST_VAL__POR (0x00) +#define MSM89XX_PMIC_ANALOG_TRIM_NUM (CDC_ANA_BASE+0xF0) +#define MSM89XX_PMIC_ANALOG_TRIM_NUM__POR (0x04) +#define MSM89XX_PMIC_ANALOG_TRIM_CTRL1 (CDC_ANA_BASE+0xF1) +#define MSM89XX_PMIC_ANALOG_TRIM_CTRL1__POR (0x00) +#define MSM89XX_PMIC_ANALOG_TRIM_CTRL2 (CDC_ANA_BASE+0xF2) +#define MSM89XX_PMIC_ANALOG_TRIM_CTRL2__POR (0x00) +#define MSM89XX_PMIC_ANALOG_TRIM_CTRL3 (CDC_ANA_BASE+0xF3) +#define MSM89XX_PMIC_ANALOG_TRIM_CTRL3__POR (0x00) +#define MSM89XX_PMIC_ANALOG_TRIM_CTRL4 (CDC_ANA_BASE+0xF4) +#define MSM89XX_PMIC_ANALOG_TRIM_CTRL4__POR (0x00) + +#define MSM89XX_PMIC_CDC_NUM_REGISTERS \ + (MSM89XX_PMIC_ANALOG_TRIM_CTRL4+1) +#define MSM89XX_PMIC_CDC_MAX_REGISTER \ + (MSM89XX_PMIC_CDC_NUM_REGISTERS-1) +#define MSM89XX_PMIC_CDC_CACHE_SIZE \ + MSM89XX_PMIC_CDC_NUM_REGISTERS + + +#define MSM89XX_CDC_CORE_CLK_RX_RESET_CTL (0x00) +#define MSM89XX_CDC_CORE_CLK_RX_RESET_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_CLK_TX_RESET_B1_CTL (0x04) +#define MSM89XX_CDC_CORE_CLK_TX_RESET_B1_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_CLK_DMIC_B1_CTL (0x08) +#define MSM89XX_CDC_CORE_CLK_DMIC_B1_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_CLK_RX_I2S_CTL (0x0C) +#define MSM89XX_CDC_CORE_CLK_RX_I2S_CTL__POR (0x13) +#define MSM89XX_CDC_CORE_CLK_TX_I2S_CTL (0x10) +#define MSM89XX_CDC_CORE_CLK_TX_I2S_CTL__POR (0x13) +#define MSM89XX_CDC_CORE_CLK_OTHR_RESET_B1_CTL (0x14) +#define MSM89XX_CDC_CORE_CLK_OTHR_RESET_B1_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_CLK_TX_CLK_EN_B1_CTL (0x18) +#define MSM89XX_CDC_CORE_CLK_TX_CLK_EN_B1_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_CLK_OTHR_CTL (0x1C) +#define MSM89XX_CDC_CORE_CLK_OTHR_CTL__POR (0x04) +#define MSM89XX_CDC_CORE_CLK_RX_B1_CTL (0x20) +#define MSM89XX_CDC_CORE_CLK_RX_B1_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_CLK_MCLK_CTL (0x24) +#define MSM89XX_CDC_CORE_CLK_MCLK_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_CLK_PDM_CTL (0x28) +#define MSM89XX_CDC_CORE_CLK_PDM_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_CLK_SD_CTL (0x2C) +#define MSM89XX_CDC_CORE_CLK_SD_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_CLK_DMIC_B2_CTL (0x30) +#define MSM89XX_CDC_CORE_CLK_DMIC_B2_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_CLK_RX_B2_CTL (0x34) +#define MSM89XX_CDC_CORE_CLK_RX_B2_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_CLK_TX2_I2S_CTL (0x38) +#define MSM89XX_CDC_CORE_CLK_TX2_I2S_CTL__POR (0x13) +#define MSM89XX_CDC_CORE_RX1_B1_CTL (0x40) +#define MSM89XX_CDC_CORE_RX1_B1_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_RX2_B1_CTL (0x60) +#define MSM89XX_CDC_CORE_RX2_B1_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_RX3_B1_CTL (0x80) +#define MSM89XX_CDC_CORE_RX3_B1_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_RX1_B2_CTL (0x44) +#define MSM89XX_CDC_CORE_RX1_B2_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_RX2_B2_CTL (0x64) +#define MSM89XX_CDC_CORE_RX2_B2_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_RX3_B2_CTL (0x84) +#define MSM89XX_CDC_CORE_RX3_B2_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_RX1_B3_CTL (0x48) +#define MSM89XX_CDC_CORE_RX1_B3_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_RX2_B3_CTL (0x68) +#define MSM89XX_CDC_CORE_RX2_B3_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_RX3_B3_CTL (0x88) +#define MSM89XX_CDC_CORE_RX3_B3_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_RX1_B4_CTL (0x4C) +#define MSM89XX_CDC_CORE_RX1_B4_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_RX2_B4_CTL (0x6C) +#define MSM89XX_CDC_CORE_RX2_B4_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_RX3_B4_CTL (0x8C) +#define MSM89XX_CDC_CORE_RX3_B4_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_RX1_B5_CTL (0x50) +#define MSM89XX_CDC_CORE_RX1_B5_CTL__POR (0x68) +#define MSM89XX_CDC_CORE_RX2_B5_CTL (0x70) +#define MSM89XX_CDC_CORE_RX2_B5_CTL__POR (0x68) +#define MSM89XX_CDC_CORE_RX3_B5_CTL (0x90) +#define MSM89XX_CDC_CORE_RX3_B5_CTL__POR (0x68) +#define MSM89XX_CDC_CORE_RX1_B6_CTL (0x54) +#define MSM89XX_CDC_CORE_RX1_B6_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_RX2_B6_CTL (0x74) +#define MSM89XX_CDC_CORE_RX2_B6_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_RX3_B6_CTL (0x94) +#define MSM89XX_CDC_CORE_RX3_B6_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_RX1_VOL_CTL_B1_CTL (0x58) +#define MSM89XX_CDC_CORE_RX1_VOL_CTL_B1_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_RX2_VOL_CTL_B1_CTL (0x78) +#define MSM89XX_CDC_CORE_RX2_VOL_CTL_B1_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_RX3_VOL_CTL_B1_CTL (0x98) +#define MSM89XX_CDC_CORE_RX3_VOL_CTL_B1_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_RX1_VOL_CTL_B2_CTL (0x5C) +#define MSM89XX_CDC_CORE_RX1_VOL_CTL_B2_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_RX2_VOL_CTL_B2_CTL (0x7C) +#define MSM89XX_CDC_CORE_RX2_VOL_CTL_B2_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_RX3_VOL_CTL_B2_CTL (0x9C) +#define MSM89XX_CDC_CORE_RX3_VOL_CTL_B2_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_TOP_GAIN_UPDATE (0xA0) +#define MSM89XX_CDC_CORE_TOP_GAIN_UPDATE__POR (0x00) +#define MSM89XX_CDC_CORE_TOP_CTL (0xA4) +#define MSM89XX_CDC_CORE_TOP_CTL__POR (0x01) +#define MSM89XX_CDC_CORE_COMP0_B1_CTL (0xB0) +#define MSM89XX_CDC_CORE_COMP0_B1_CTL__POR (0x30) +#define MSM89XX_CDC_CORE_COMP0_B2_CTL (0xB4) +#define MSM89XX_CDC_CORE_COMP0_B2_CTL__POR (0xB5) +#define MSM89XX_CDC_CORE_COMP0_B3_CTL (0xB8) +#define MSM89XX_CDC_CORE_COMP0_B3_CTL__POR (0x28) +#define MSM89XX_CDC_CORE_COMP0_B4_CTL (0xBC) +#define MSM89XX_CDC_CORE_COMP0_B4_CTL__POR (0x37) +#define MSM89XX_CDC_CORE_COMP0_B5_CTL (0xC0) +#define MSM89XX_CDC_CORE_COMP0_B5_CTL__POR (0x7F) +#define MSM89XX_CDC_CORE_COMP0_B6_CTL (0xC4) +#define MSM89XX_CDC_CORE_COMP0_B6_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_COMP0_SHUT_DOWN_STATUS (0xC8) +#define MSM89XX_CDC_CORE_COMP0_SHUT_DOWN_STATUS__POR (0x03) +#define MSM89XX_CDC_CORE_COMP0_FS_CFG (0xCC) +#define MSM89XX_CDC_CORE_COMP0_FS_CFG__POR (0x03) +#define MSM89XX_CDC_CORE_COMP0_DELAY_BUF_CTL (0xD0) +#define MSM89XX_CDC_CORE_COMP0_DELAY_BUF_CTL__POR (0x02) +#define MSM89XX_CDC_CORE_DEBUG_DESER1_CTL (0xE0) +#define MSM89XX_CDC_CORE_DEBUG_DESER1_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_DEBUG_DESER2_CTL (0xE4) +#define MSM89XX_CDC_CORE_DEBUG_DESER2_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_DEBUG_B1_CTL_CFG (0xE8) +#define MSM89XX_CDC_CORE_DEBUG_B1_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_DEBUG_B2_CTL_CFG (0xEC) +#define MSM89XX_CDC_CORE_DEBUG_B2_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_DEBUG_B3_CTL_CFG (0xF0) +#define MSM89XX_CDC_CORE_DEBUG_B3_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_IIR1_GAIN_B1_CTL (0x100) +#define MSM89XX_CDC_CORE_IIR1_GAIN_B1_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_IIR2_GAIN_B1_CTL (0x140) +#define MSM89XX_CDC_CORE_IIR2_GAIN_B1_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_IIR1_GAIN_B2_CTL (0x104) +#define MSM89XX_CDC_CORE_IIR1_GAIN_B2_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_IIR2_GAIN_B2_CTL (0x144) +#define MSM89XX_CDC_CORE_IIR2_GAIN_B2_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_IIR1_GAIN_B3_CTL (0x108) +#define MSM89XX_CDC_CORE_IIR1_GAIN_B3_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_IIR2_GAIN_B3_CTL (0x148) +#define MSM89XX_CDC_CORE_IIR2_GAIN_B3_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_IIR1_GAIN_B4_CTL (0x10C) +#define MSM89XX_CDC_CORE_IIR1_GAIN_B4_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_IIR2_GAIN_B4_CTL (0x14C) +#define MSM89XX_CDC_CORE_IIR2_GAIN_B4_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_IIR1_GAIN_B5_CTL (0x110) +#define MSM89XX_CDC_CORE_IIR1_GAIN_B5_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_IIR2_GAIN_B5_CTL (0x150) +#define MSM89XX_CDC_CORE_IIR2_GAIN_B5_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_IIR1_GAIN_B6_CTL (0x114) +#define MSM89XX_CDC_CORE_IIR1_GAIN_B6_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_IIR2_GAIN_B6_CTL (0x154) +#define MSM89XX_CDC_CORE_IIR2_GAIN_B6_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_IIR1_GAIN_B7_CTL (0x118) +#define MSM89XX_CDC_CORE_IIR1_GAIN_B7_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_IIR2_GAIN_B7_CTL (0x158) +#define MSM89XX_CDC_CORE_IIR2_GAIN_B7_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_IIR1_GAIN_B8_CTL (0x11C) +#define MSM89XX_CDC_CORE_IIR1_GAIN_B8_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_IIR2_GAIN_B8_CTL (0x15C) +#define MSM89XX_CDC_CORE_IIR2_GAIN_B8_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_IIR1_CTL (0x120) +#define MSM89XX_CDC_CORE_IIR1_CTL__POR (0x40) +#define MSM89XX_CDC_CORE_IIR2_CTL (0x160) +#define MSM89XX_CDC_CORE_IIR2_CTL__POR (0x40) +#define MSM89XX_CDC_CORE_IIR1_GAIN_TIMER_CTL (0x124) +#define MSM89XX_CDC_CORE_IIR1_GAIN_TIMER_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_IIR2_GAIN_TIMER_CTL (0x164) +#define MSM89XX_CDC_CORE_IIR2_GAIN_TIMER_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_IIR1_COEF_B1_CTL (0x128) +#define MSM89XX_CDC_CORE_IIR1_COEF_B1_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_IIR2_COEF_B1_CTL (0x168) +#define MSM89XX_CDC_CORE_IIR2_COEF_B1_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_IIR1_COEF_B2_CTL (0x12C) +#define MSM89XX_CDC_CORE_IIR1_COEF_B2_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_IIR2_COEF_B2_CTL (0x16C) +#define MSM89XX_CDC_CORE_IIR2_COEF_B2_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_CONN_RX1_B1_CTL (0x180) +#define MSM89XX_CDC_CORE_CONN_RX1_B1_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_CONN_RX1_B2_CTL (0x184) +#define MSM89XX_CDC_CORE_CONN_RX1_B2_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_CONN_RX1_B3_CTL (0x188) +#define MSM89XX_CDC_CORE_CONN_RX1_B3_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_CONN_RX2_B1_CTL (0x18C) +#define MSM89XX_CDC_CORE_CONN_RX2_B1_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_CONN_RX2_B2_CTL (0x190) +#define MSM89XX_CDC_CORE_CONN_RX2_B2_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_CONN_RX2_B3_CTL (0x194) +#define MSM89XX_CDC_CORE_CONN_RX2_B3_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_CONN_RX3_B1_CTL (0x198) +#define MSM89XX_CDC_CORE_CONN_RX3_B1_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_CONN_RX3_B2_CTL (0x19C) +#define MSM89XX_CDC_CORE_CONN_RX3_B2_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_CONN_TX_B1_CTL (0x1A0) +#define MSM89XX_CDC_CORE_CONN_TX_B1_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_CONN_TX_B2_CTL (0x1A4) +#define MSM89XX_CDC_CORE_CONN_TX_B2_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_CONN_EQ1_B1_CTL (0x1A8) +#define MSM89XX_CDC_CORE_CONN_EQ1_B1_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_CONN_EQ1_B2_CTL (0x1AC) +#define MSM89XX_CDC_CORE_CONN_EQ1_B2_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_CONN_EQ1_B3_CTL (0x1B0) +#define MSM89XX_CDC_CORE_CONN_EQ1_B3_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_CONN_EQ1_B4_CTL (0x1B4) +#define MSM89XX_CDC_CORE_CONN_EQ1_B4_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_CONN_EQ2_B1_CTL (0x1B8) +#define MSM89XX_CDC_CORE_CONN_EQ2_B1_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_CONN_EQ2_B2_CTL (0x1BC) +#define MSM89XX_CDC_CORE_CONN_EQ2_B2_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_CONN_EQ2_B3_CTL (0x1C0) +#define MSM89XX_CDC_CORE_CONN_EQ2_B3_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_CONN_EQ2_B4_CTL (0x1C4) +#define MSM89XX_CDC_CORE_CONN_EQ2_B4_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_CONN_TX_I2S_SD1_CTL (0x1C8) +#define MSM89XX_CDC_CORE_CONN_TX_I2S_SD1_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_CONN_TX_B3_CTL (0x1CC) +#define MSM89XX_CDC_CORE_CONN_TX_B3_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_TX5_VOL_CTL_TIMER (0x1E0) +#define MSM89XX_CDC_CORE_TX5_VOL_CTL_TIMER__POR (0x00) +#define MSM89XX_CDC_CORE_TX5_VOL_CTL_GAIN (0x1E4) +#define MSM89XX_CDC_CORE_TX5_VOL_CTL_GAIN__POR (0x00) +#define MSM89XX_CDC_CORE_TX5_VOL_CTL_CFG (0x1E8) +#define MSM89XX_CDC_CORE_TX5_VOL_CTL_CFG__POR (0x00) +#define MSM89XX_CDC_CORE_TX5_MUX_CTL (0x1EC) +#define MSM89XX_CDC_CORE_TX5_MUX_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_TX5_CLK_FS_CTL (0x1F0) +#define MSM89XX_CDC_CORE_TX5_CLK_FS_CTL__POR (0x03) +#define MSM89XX_CDC_CORE_TX5_DMIC_CTL (0x1F4) +#define MSM89XX_CDC_CORE_TX5_DMIC_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_TX1_VOL_CTL_TIMER (0x280) +#define MSM89XX_CDC_CORE_TX1_VOL_CTL_TIMER__POR (0x00) +#define MSM89XX_CDC_CORE_TX2_VOL_CTL_TIMER (0x2A0) +#define MSM89XX_CDC_CORE_TX2_VOL_CTL_TIMER__POR (0x00) +#define MSM89XX_CDC_CORE_TX3_VOL_CTL_TIMER (0x2C0) +#define MSM89XX_CDC_CORE_TX3_VOL_CTL_TIMER__POR (0x00) +#define MSM89XX_CDC_CORE_TX4_VOL_CTL_TIMER (0x2E0) +#define MSM89XX_CDC_CORE_TX4_VOL_CTL_TIMER__POR (0x00) +#define MSM89XX_CDC_CORE_TX1_VOL_CTL_GAIN (0x284) +#define MSM89XX_CDC_CORE_TX1_VOL_CTL_GAIN__POR (0x00) +#define MSM89XX_CDC_CORE_TX2_VOL_CTL_GAIN (0x2A4) +#define MSM89XX_CDC_CORE_TX2_VOL_CTL_GAIN__POR (0x00) +#define MSM89XX_CDC_CORE_TX3_VOL_CTL_GAIN (0x2C4) +#define MSM89XX_CDC_CORE_TX3_VOL_CTL_GAIN__POR (0x00) +#define MSM89XX_CDC_CORE_TX4_VOL_CTL_GAIN (0x2E4) +#define MSM89XX_CDC_CORE_TX4_VOL_CTL_GAIN__POR (0x00) +#define MSM89XX_CDC_CORE_TX1_VOL_CTL_CFG (0x288) +#define MSM89XX_CDC_CORE_TX1_VOL_CTL_CFG__POR (0x00) +#define MSM89XX_CDC_CORE_TX2_VOL_CTL_CFG (0x2A8) +#define MSM89XX_CDC_CORE_TX2_VOL_CTL_CFG__POR (0x00) +#define MSM89XX_CDC_CORE_TX3_VOL_CTL_CFG (0x2C8) +#define MSM89XX_CDC_CORE_TX3_VOL_CTL_CFG__POR (0x00) +#define MSM89XX_CDC_CORE_TX4_VOL_CTL_CFG (0x2E8) +#define MSM89XX_CDC_CORE_TX4_VOL_CTL_CFG__POR (0x00) +#define MSM89XX_CDC_CORE_TX1_MUX_CTL (0x28C) +#define MSM89XX_CDC_CORE_TX1_MUX_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_TX2_MUX_CTL (0x2AC) +#define MSM89XX_CDC_CORE_TX2_MUX_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_TX3_MUX_CTL (0x2CC) +#define MSM89XX_CDC_CORE_TX3_MUX_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_TX4_MUX_CTL (0x2EC) +#define MSM89XX_CDC_CORE_TX4_MUX_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_TX1_CLK_FS_CTL (0x290) +#define MSM89XX_CDC_CORE_TX1_CLK_FS_CTL__POR (0x03) +#define MSM89XX_CDC_CORE_TX2_CLK_FS_CTL (0x2B0) +#define MSM89XX_CDC_CORE_TX2_CLK_FS_CTL__POR (0x03) +#define MSM89XX_CDC_CORE_TX3_CLK_FS_CTL (0x2D0) +#define MSM89XX_CDC_CORE_TX3_CLK_FS_CTL__POR (0x03) +#define MSM89XX_CDC_CORE_TX4_CLK_FS_CTL (0x2F0) +#define MSM89XX_CDC_CORE_TX4_CLK_FS_CTL__POR (0x03) +#define MSM89XX_CDC_CORE_TX1_DMIC_CTL (0x294) +#define MSM89XX_CDC_CORE_TX1_DMIC_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_TX2_DMIC_CTL (0x2B4) +#define MSM89XX_CDC_CORE_TX2_DMIC_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_TX3_DMIC_CTL (0x2D4) +#define MSM89XX_CDC_CORE_TX3_DMIC_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_TX4_DMIC_CTL (0x2F4) +#define MSM89XX_CDC_CORE_TX4_DMIC_CTL__POR (0x00) + +#define MSM89XX_CDC_CORE_NUM_REGISTERS \ + (MSM89XX_CDC_CORE_TX4_DMIC_CTL+1) +#define MSM89XX_CDC_CORE_MAX_REGISTER \ + (MSM89XX_CDC_CORE_NUM_REGISTERS-1) +#define MSM89XX_CDC_CORE_CACHE_SIZE \ + MSM89XX_CDC_CORE_NUM_REGISTERS +#endif diff --git a/audio-kernel/asoc/codecs/wcd-clsh.c b/audio-kernel/asoc/codecs/wcd-clsh.c new file mode 100644 index 000000000..82d2d1203 --- /dev/null +++ b/audio-kernel/asoc/codecs/wcd-clsh.c @@ -0,0 +1,577 @@ +/* + * Copyright (c) 2015-2019, 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 +#include +#include +#include +#include +#include +#include "wcd-clsh.h" + +#define WCD_USLEEP_RANGE 50 + +static void (*clsh_state_fp[NUM_CLSH_STATES])(struct snd_soc_codec *, + struct wcd_clsh_cdc_info *, + u8 req_state, bool en, int mode); + +static const char *mode_to_str(int mode) +{ + switch (mode) { + case CLS_H_NORMAL: + return WCD_CLSH_STRINGIFY(CLS_H_NORMAL); + case CLS_H_HIFI: + return WCD_CLSH_STRINGIFY(CLS_H_HIFI); + case CLS_H_LOHIFI: + return WCD_CLSH_STRINGIFY(CLS_H_LOHIFI); + case CLS_H_LP: + return WCD_CLSH_STRINGIFY(CLS_H_LP); + case CLS_H_ULP: + return WCD_CLSH_STRINGIFY(CLS_H_ULP); + case CLS_AB: + return WCD_CLSH_STRINGIFY(CLS_AB); + case CLS_AB_HIFI: + return WCD_CLSH_STRINGIFY(CLS_AB_HIFI); + default: + return WCD_CLSH_STRINGIFY(CLS_H_INVALID); + }; +} + +static const char *state_to_str(u8 state, char *buf, size_t buflen) +{ + int i; + int cnt = 0; + /* + * This array of strings should match with enum wcd_clsh_state_bit. + */ + static const char *const states[] = { + "STATE_EAR", + "STATE_HPH_L", + "STATE_HPH_R", + "STATE_AUX", + }; + + if (state == WCD_CLSH_STATE_IDLE) { + snprintf(buf, buflen, "[STATE_IDLE]"); + goto done; + } + + buf[0] = '\0'; + for (i = 0; i < ARRAY_SIZE(states); i++) { + if (!(state & (1 << i))) + continue; + cnt = snprintf(buf, buflen - cnt - 1, "%s%s%s", buf, + buf[0] == '\0' ? "[" : "|", + states[i]); + } + if (cnt > 0) + strlcat(buf + cnt, "]", buflen); + +done: + if (buf[0] == '\0') + snprintf(buf, buflen, "[STATE_UNKNOWN]"); + return buf; +} + +static inline int wcd_clsh_get_int_mode(struct wcd_clsh_cdc_info *clsh_d, + int clsh_state) +{ + int mode; + + if ((clsh_state != WCD_CLSH_STATE_EAR) && + (clsh_state != WCD_CLSH_STATE_HPHL) && + (clsh_state != WCD_CLSH_STATE_HPHR) && + (clsh_state != WCD_CLSH_STATE_AUX)) + mode = CLS_NONE; + else + mode = clsh_d->interpolator_modes[ffs(clsh_state)]; + + return mode; +} + +static inline void wcd_clsh_set_int_mode(struct wcd_clsh_cdc_info *clsh_d, + int clsh_state, int mode) +{ + if ((clsh_state != WCD_CLSH_STATE_EAR) && + (clsh_state != WCD_CLSH_STATE_HPHL) && + (clsh_state != WCD_CLSH_STATE_HPHR) && + (clsh_state != WCD_CLSH_STATE_AUX)) + return; + + clsh_d->interpolator_modes[ffs(clsh_state)] = mode; +} + +static inline void wcd_clsh_set_buck_mode(struct snd_soc_codec *codec, + int mode) +{ + if (mode == CLS_H_HIFI || mode == CLS_H_LOHIFI || + mode == CLS_AB_HIFI) + snd_soc_update_bits(codec, WCD9XXX_ANA_RX_SUPPLIES, + 0x08, 0x08); /* set to HIFI */ + else + snd_soc_update_bits(codec, WCD9XXX_ANA_RX_SUPPLIES, + 0x08, 0x00); /* set to default */ +} + +static inline void wcd_clsh_set_flyback_mode(struct snd_soc_codec *codec, + int mode) +{ + if (mode == CLS_H_HIFI || mode == CLS_H_LOHIFI || + mode == CLS_AB_HIFI) { + snd_soc_update_bits(codec, WCD9XXX_ANA_RX_SUPPLIES, + 0x04, 0x04); + snd_soc_update_bits(codec, WCD9XXX_FLYBACK_VNEG_CTRL_4, + 0xF0, 0x80); + } else { + snd_soc_update_bits(codec, WCD9XXX_ANA_RX_SUPPLIES, + 0x04, 0x00); /* set to Default */ + snd_soc_update_bits(codec, WCD9XXX_FLYBACK_VNEG_CTRL_4, + 0xF0, 0x70); + } +} + +static inline void wcd_clsh_force_iq_ctl(struct snd_soc_codec *codec, + int mode, bool enable) +{ + if (enable) { + snd_soc_update_bits(codec, WCD9XXX_FLYBACK_VNEGDAC_CTRL_2, + 0xE0, 0xA0); + /* 100usec delay is needed as per HW requirement */ + usleep_range(100, 110); + snd_soc_update_bits(codec, WCD9XXX_CLASSH_MODE_3, + 0x02, 0x02); + snd_soc_update_bits(codec, WCD9XXX_CLASSH_MODE_2, + 0xFF, 0x1C); + if (mode == CLS_H_LOHIFI) { + snd_soc_update_bits(codec, WCD9XXX_HPH_NEW_INT_PA_MISC2, + 0x20, 0x20); + snd_soc_update_bits(codec, WCD9XXX_RX_BIAS_HPH_LOWPOWER, + 0xF0, 0xC0); + snd_soc_update_bits(codec, WCD9XXX_HPH_PA_CTL1, + 0x0E, 0x02); + } + } else { + snd_soc_update_bits(codec, WCD9XXX_HPH_NEW_INT_PA_MISC2, + 0x20, 0x00); + snd_soc_update_bits(codec, WCD9XXX_RX_BIAS_HPH_LOWPOWER, + 0xF0, 0x80); + snd_soc_update_bits(codec, WCD9XXX_HPH_PA_CTL1, + 0x0E, 0x06); + } +} + +static void wcd_clsh_buck_ctrl(struct snd_soc_codec *codec, + struct wcd_clsh_cdc_info *clsh_d, + int mode, + bool enable) +{ + /* enable/disable buck */ + if ((enable && (++clsh_d->buck_users == 1)) || + (!enable && (--clsh_d->buck_users == 0))) { + snd_soc_update_bits(codec, WCD9XXX_ANA_RX_SUPPLIES, + (1 << 7), (enable << 7)); + /* + * 500us sleep is required after buck enable/disable + * as per HW requirement + */ + usleep_range(500, 510); + if (mode == CLS_H_LOHIFI || mode == CLS_H_ULP || + mode == CLS_H_HIFI || mode == CLS_H_LP) + snd_soc_update_bits(codec, WCD9XXX_CLASSH_MODE_3, + 0x02, 0x00); + + snd_soc_update_bits(codec, WCD9XXX_CLASSH_MODE_2, 0xFF, 0x3A); + /* 500usec delay is needed as per HW requirement */ + usleep_range(500, 500 + WCD_USLEEP_RANGE); + } + dev_dbg(codec->dev, "%s: buck_users %d, enable %d, mode: %s\n", + __func__, clsh_d->buck_users, enable, mode_to_str(mode)); +} + +static void wcd_clsh_flyback_ctrl(struct snd_soc_codec *codec, + struct wcd_clsh_cdc_info *clsh_d, + int mode, + bool enable) +{ + /* enable/disable flyback */ + if ((enable && (++clsh_d->flyback_users == 1)) || + (!enable && (--clsh_d->flyback_users == 0))) { + snd_soc_update_bits(codec, WCD9XXX_FLYBACK_VNEG_CTRL_1, + 0xE0, 0xE0); + snd_soc_update_bits(codec, WCD9XXX_ANA_RX_SUPPLIES, + (1 << 6), (enable << 6)); + /* + * 100us sleep is required after flyback enable/disable + * as per HW requirement + */ + usleep_range(100, 110); + snd_soc_update_bits(codec, WCD9XXX_FLYBACK_VNEGDAC_CTRL_2, + 0xE0, 0xE0); + /* 500usec delay is needed as per HW requirement */ + usleep_range(500, 500 + WCD_USLEEP_RANGE); + } + dev_dbg(codec->dev, "%s: flyback_users %d, enable %d, mode: %s\n", + __func__, clsh_d->flyback_users, enable, mode_to_str(mode)); +} + +static void wcd_clsh_set_hph_mode(struct snd_soc_codec *codec, + int mode) +{ + u8 val = 0; + + switch (mode) { + case CLS_H_NORMAL: + val = 0x00; + break; + case CLS_AB: + case CLS_H_ULP: + val = 0x0C; + break; + case CLS_AB_HIFI: + case CLS_H_HIFI: + val = 0x08; + break; + case CLS_H_LP: + case CLS_H_LOHIFI: + val = 0x04; + break; + default: + dev_err(codec->dev, "%s:Invalid mode %d\n", __func__, mode); + return; + }; + + snd_soc_update_bits(codec, WCD9XXX_ANA_HPH, 0x0C, val); +} + +static void wcd_clsh_set_flyback_current(struct snd_soc_codec *codec, int mode) +{ + + snd_soc_update_bits(codec, WCD9XXX_RX_BIAS_FLYB_BUFF, 0x0F, 0x0A); + snd_soc_update_bits(codec, WCD9XXX_RX_BIAS_FLYB_BUFF, 0xF0, 0xA0); + /* Sleep needed to avoid click and pop as per HW requirement */ + usleep_range(100, 110); +} + +static void wcd_clsh_set_buck_regulator_mode(struct snd_soc_codec *codec, + int mode) +{ + snd_soc_update_bits(codec, WCD9XXX_ANA_RX_SUPPLIES, + 0x02, 0x00); +} + +static void wcd_clsh_state_ear_aux(struct snd_soc_codec *codec, + struct wcd_clsh_cdc_info *clsh_d, + u8 req_state, bool is_enable, int mode) +{ + dev_dbg(codec->dev, "%s: mode: %s, %s\n", __func__, mode_to_str(mode), + is_enable ? "enable" : "disable"); +} + +static void wcd_clsh_state_hph_aux(struct snd_soc_codec *codec, + struct wcd_clsh_cdc_info *clsh_d, + u8 req_state, bool is_enable, int mode) +{ + dev_dbg(codec->dev, "%s: mode: %s, %s\n", __func__, mode_to_str(mode), + is_enable ? "enable" : "disable"); +} + +static void wcd_clsh_state_hph_ear(struct snd_soc_codec *codec, + struct wcd_clsh_cdc_info *clsh_d, + u8 req_state, bool is_enable, int mode) +{ + dev_dbg(codec->dev, "%s: mode: %s, %s\n", __func__, mode_to_str(mode), + is_enable ? "enable" : "disable"); +} + +static void wcd_clsh_state_hph_st(struct snd_soc_codec *codec, + struct wcd_clsh_cdc_info *clsh_d, + u8 req_state, bool is_enable, int mode) +{ + dev_dbg(codec->dev, "%s: mode: %s, %s\n", __func__, mode_to_str(mode), + is_enable ? "enable" : "disable"); +} + +static void wcd_clsh_state_hph_r(struct snd_soc_codec *codec, + struct wcd_clsh_cdc_info *clsh_d, + u8 req_state, bool is_enable, int mode) +{ + dev_dbg(codec->dev, "%s: mode: %s, %s\n", __func__, mode_to_str(mode), + is_enable ? "enable" : "disable"); + + if (mode == CLS_H_NORMAL) { + dev_dbg(codec->dev, "%s: Normal mode not applicable for hph_r\n", + __func__); + return; + } + + if (is_enable) { + wcd_clsh_set_buck_regulator_mode(codec, mode); + wcd_clsh_set_flyback_mode(codec, mode); + wcd_clsh_force_iq_ctl(codec, mode, true); + wcd_clsh_flyback_ctrl(codec, clsh_d, mode, true); + wcd_clsh_set_flyback_current(codec, mode); + wcd_clsh_set_buck_mode(codec, mode); + wcd_clsh_buck_ctrl(codec, clsh_d, mode, true); + wcd_clsh_set_hph_mode(codec, mode); + } else { + wcd_clsh_set_hph_mode(codec, CLS_H_NORMAL); + + /* buck and flyback set to default mode and disable */ + wcd_clsh_flyback_ctrl(codec, clsh_d, CLS_H_NORMAL, false); + wcd_clsh_buck_ctrl(codec, clsh_d, CLS_H_NORMAL, false); + wcd_clsh_force_iq_ctl(codec, CLS_H_NORMAL, false); + wcd_clsh_set_flyback_mode(codec, CLS_H_NORMAL); + wcd_clsh_set_buck_mode(codec, CLS_H_NORMAL); + } +} + +static void wcd_clsh_state_hph_l(struct snd_soc_codec *codec, + struct wcd_clsh_cdc_info *clsh_d, + u8 req_state, bool is_enable, int mode) +{ + dev_dbg(codec->dev, "%s: mode: %s, %s\n", __func__, mode_to_str(mode), + is_enable ? "enable" : "disable"); + + if (mode == CLS_H_NORMAL) { + dev_dbg(codec->dev, "%s: Normal mode not applicable for hph_l\n", + __func__); + return; + } + + if (is_enable) { + wcd_clsh_set_buck_regulator_mode(codec, mode); + wcd_clsh_set_flyback_mode(codec, mode); + wcd_clsh_force_iq_ctl(codec, mode, true); + wcd_clsh_flyback_ctrl(codec, clsh_d, mode, true); + wcd_clsh_set_flyback_current(codec, mode); + wcd_clsh_set_buck_mode(codec, mode); + wcd_clsh_buck_ctrl(codec, clsh_d, mode, true); + wcd_clsh_set_hph_mode(codec, mode); + } else { + wcd_clsh_set_hph_mode(codec, CLS_H_NORMAL); + + /* set buck and flyback to Default Mode */ + wcd_clsh_flyback_ctrl(codec, clsh_d, CLS_H_NORMAL, false); + wcd_clsh_buck_ctrl(codec, clsh_d, CLS_H_NORMAL, false); + wcd_clsh_force_iq_ctl(codec, CLS_H_NORMAL, false); + wcd_clsh_set_flyback_mode(codec, CLS_H_NORMAL); + wcd_clsh_set_buck_mode(codec, CLS_H_NORMAL); + } +} + +static void wcd_clsh_state_aux(struct snd_soc_codec *codec, + struct wcd_clsh_cdc_info *clsh_d, + u8 req_state, bool is_enable, int mode) +{ + dev_dbg(codec->dev, "%s: mode: %s, %s\n", __func__, mode_to_str(mode), + is_enable ? "enable" : "disable"); + + if (is_enable) { + wcd_clsh_set_buck_mode(codec, mode); + wcd_clsh_set_flyback_mode(codec, mode); + wcd_clsh_flyback_ctrl(codec, clsh_d, mode, true); + wcd_clsh_set_flyback_current(codec, mode); + wcd_clsh_buck_ctrl(codec, clsh_d, mode, true); + } else { + wcd_clsh_buck_ctrl(codec, clsh_d, mode, false); + wcd_clsh_flyback_ctrl(codec, clsh_d, mode, false); + wcd_clsh_set_flyback_mode(codec, CLS_H_NORMAL); + wcd_clsh_set_buck_mode(codec, CLS_H_NORMAL); + } +} + +static void wcd_clsh_state_ear(struct snd_soc_codec *codec, + struct wcd_clsh_cdc_info *clsh_d, + u8 req_state, bool is_enable, int mode) +{ + dev_dbg(codec->dev, "%s: mode: %s, %s\n", __func__, mode_to_str(mode), + is_enable ? "enable" : "disable"); + + if (is_enable) { + wcd_clsh_set_buck_regulator_mode(codec, mode); + wcd_clsh_set_flyback_mode(codec, mode); + wcd_clsh_force_iq_ctl(codec, mode, true); + wcd_clsh_flyback_ctrl(codec, clsh_d, mode, true); + wcd_clsh_set_flyback_current(codec, mode); + wcd_clsh_set_buck_mode(codec, mode); + wcd_clsh_buck_ctrl(codec, clsh_d, mode, true); + wcd_clsh_set_hph_mode(codec, mode); + } else { + wcd_clsh_set_hph_mode(codec, CLS_H_NORMAL); + + /* set buck and flyback to Default Mode */ + wcd_clsh_flyback_ctrl(codec, clsh_d, CLS_H_NORMAL, false); + wcd_clsh_buck_ctrl(codec, clsh_d, CLS_H_NORMAL, false); + wcd_clsh_force_iq_ctl(codec, CLS_H_NORMAL, false); + wcd_clsh_set_flyback_mode(codec, CLS_H_NORMAL); + wcd_clsh_set_buck_mode(codec, CLS_H_NORMAL); + } +} + +static void wcd_clsh_state_err(struct snd_soc_codec *codec, + struct wcd_clsh_cdc_info *clsh_d, + u8 req_state, bool is_enable, int mode) +{ + char msg[128]; + + dev_err(codec->dev, + "%s Wrong request for class H state machine requested to %s %s\n", + __func__, is_enable ? "enable" : "disable", + state_to_str(req_state, msg, sizeof(msg))); +} + +/* + * Function: wcd_clsh_is_state_valid + * Params: state + * Description: + * Provides information on valid states of Class H configuration + */ +static bool wcd_clsh_is_state_valid(u8 state) +{ + switch (state) { + case WCD_CLSH_STATE_IDLE: + case WCD_CLSH_STATE_EAR: + case WCD_CLSH_STATE_HPHL: + case WCD_CLSH_STATE_HPHR: + case WCD_CLSH_STATE_HPH_ST: + case WCD_CLSH_STATE_AUX: + case WCD_CLSH_STATE_HPHL_AUX: + case WCD_CLSH_STATE_HPHR_AUX: + case WCD_CLSH_STATE_HPH_ST_AUX: + case WCD_CLSH_STATE_EAR_AUX: + return true; + default: + return false; + }; +} + +/* + * Function: wcd_cls_h_fsm + * Params: codec, cdc_clsh_d, req_state, req_type, clsh_event + * Description: + * This function handles PRE DAC and POST DAC conditions of different devices + * and updates class H configuration of different combination of devices + * based on validity of their states. cdc_clsh_d will contain current + * class h state information + */ +void wcd_cls_h_fsm(struct snd_soc_codec *codec, + struct wcd_clsh_cdc_info *cdc_clsh_d, + u8 clsh_event, u8 req_state, + int int_mode) +{ + u8 old_state, new_state; + char msg0[128], msg1[128]; + + switch (clsh_event) { + case WCD_CLSH_EVENT_PRE_DAC: + old_state = cdc_clsh_d->state; + new_state = old_state | req_state; + + if (!wcd_clsh_is_state_valid(new_state)) { + dev_err(codec->dev, + "%s: Class-H not a valid new state: %s\n", + __func__, + state_to_str(new_state, msg0, sizeof(msg0))); + return; + } + if (new_state == old_state) { + dev_err(codec->dev, + "%s: Class-H already in requested state: %s\n", + __func__, + state_to_str(new_state, msg0, sizeof(msg0))); + return; + } + cdc_clsh_d->state = new_state; + wcd_clsh_set_int_mode(cdc_clsh_d, req_state, int_mode); + (*clsh_state_fp[new_state]) (codec, cdc_clsh_d, req_state, + CLSH_REQ_ENABLE, int_mode); + dev_dbg(codec->dev, + "%s: ClassH state transition from %s to %s\n", + __func__, state_to_str(old_state, msg0, sizeof(msg0)), + state_to_str(cdc_clsh_d->state, msg1, sizeof(msg1))); + break; + case WCD_CLSH_EVENT_POST_PA: + old_state = cdc_clsh_d->state; + new_state = old_state & (~req_state); + if (new_state < NUM_CLSH_STATES) { + if (!wcd_clsh_is_state_valid(old_state)) { + dev_err(codec->dev, + "%s:Invalid old state:%s\n", + __func__, + state_to_str(old_state, msg0, + sizeof(msg0))); + return; + } + if (new_state == old_state) { + dev_err(codec->dev, + "%s: Class-H already in requested state: %s\n", + __func__, + state_to_str(new_state, msg0, + sizeof(msg0))); + return; + } + (*clsh_state_fp[old_state]) (codec, cdc_clsh_d, + req_state, CLSH_REQ_DISABLE, + int_mode); + cdc_clsh_d->state = new_state; + wcd_clsh_set_int_mode(cdc_clsh_d, req_state, CLS_NONE); + dev_dbg(codec->dev, "%s: ClassH state transition from %s to %s\n", + __func__, state_to_str(old_state, msg0, + sizeof(msg0)), + state_to_str(cdc_clsh_d->state, msg1, + sizeof(msg1))); + } + break; + }; +} +EXPORT_SYMBOL(wcd_cls_h_fsm); + +/* + * wcd_cls_h_init: Called to init clsh info + * + * @clsh: pointer for clsh state information. + */ +void wcd_cls_h_init(struct wcd_clsh_cdc_info *clsh) +{ + int i; + + clsh->state = WCD_CLSH_STATE_IDLE; + + for (i = 0; i < NUM_CLSH_STATES; i++) + clsh_state_fp[i] = wcd_clsh_state_err; + + clsh_state_fp[WCD_CLSH_STATE_EAR] = wcd_clsh_state_ear; + clsh_state_fp[WCD_CLSH_STATE_HPHL] = wcd_clsh_state_hph_l; + clsh_state_fp[WCD_CLSH_STATE_HPHR] = wcd_clsh_state_hph_r; + clsh_state_fp[WCD_CLSH_STATE_HPH_ST] = wcd_clsh_state_hph_st; + clsh_state_fp[WCD_CLSH_STATE_AUX] = wcd_clsh_state_aux; + clsh_state_fp[WCD_CLSH_STATE_HPHL_AUX] = wcd_clsh_state_hph_aux; + clsh_state_fp[WCD_CLSH_STATE_HPHR_AUX] = wcd_clsh_state_hph_aux; + clsh_state_fp[WCD_CLSH_STATE_HPH_ST_AUX] = + wcd_clsh_state_hph_aux; + clsh_state_fp[WCD_CLSH_STATE_EAR_AUX] = wcd_clsh_state_ear_aux; + clsh_state_fp[WCD_CLSH_STATE_HPHL_EAR] = wcd_clsh_state_hph_ear; + clsh_state_fp[WCD_CLSH_STATE_HPHR_EAR] = wcd_clsh_state_hph_ear; + clsh_state_fp[WCD_CLSH_STATE_HPH_ST_EAR] = wcd_clsh_state_hph_ear; + /* Set interpolaotr modes to NONE */ + wcd_clsh_set_int_mode(clsh, WCD_CLSH_STATE_EAR, CLS_NONE); + wcd_clsh_set_int_mode(clsh, WCD_CLSH_STATE_HPHL, CLS_NONE); + wcd_clsh_set_int_mode(clsh, WCD_CLSH_STATE_HPHR, CLS_NONE); + wcd_clsh_set_int_mode(clsh, WCD_CLSH_STATE_AUX, CLS_NONE); + clsh->flyback_users = 0; + clsh->buck_users = 0; +} +EXPORT_SYMBOL(wcd_cls_h_init); + +MODULE_DESCRIPTION("WCD Class-H Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/audio-kernel/asoc/codecs/wcd-clsh.h b/audio-kernel/asoc/codecs/wcd-clsh.h new file mode 100644 index 000000000..df305bcf7 --- /dev/null +++ b/audio-kernel/asoc/codecs/wcd-clsh.h @@ -0,0 +1,104 @@ +/* + * 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 + * 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 _WCD_CLSH +#define _WCD_CLSH + +#include + +#define WCD_CLSH_STRINGIFY(s) __stringify(s) +#define CLSH_REQ_ENABLE true +#define CLSH_REQ_DISABLE false + +#define WCD_CLSH_EVENT_PRE_DAC 0x01 +#define WCD_CLSH_EVENT_POST_PA 0x02 +/* + * Basic states for Class H state machine. + * represented as a bit mask within a u8 data type + * bit 0: EAR mode + * bit 1: HPH Left mode + * bit 2: HPH Right mode + * bit 3: AUX mode + */ +#define WCD_CLSH_STATE_IDLE 0x00 +#define WCD_CLSH_STATE_EAR (0x01 << 0) +#define WCD_CLSH_STATE_HPHL (0x01 << 1) +#define WCD_CLSH_STATE_HPHR (0x01 << 2) +#define WCD_CLSH_STATE_AUX (0x01 << 3) + +/* + * Though number of CLSH states is 4, max state should be 5 + * because state array index starts from 1. + */ +#define WCD_CLSH_STATE_MAX 5 +#define NUM_CLSH_STATES (0x01 << WCD_CLSH_STATE_MAX) + +/* Derived State: Bits 1 and 2 should be set for Headphone stereo */ +#define WCD_CLSH_STATE_HPH_ST (WCD_CLSH_STATE_HPHL | \ + WCD_CLSH_STATE_HPHR) + +#define WCD_CLSH_STATE_HPHL_AUX (WCD_CLSH_STATE_HPHL | \ + WCD_CLSH_STATE_AUX) +#define WCD_CLSH_STATE_HPHR_AUX (WCD_CLSH_STATE_HPHR | \ + WCD_CLSH_STATE_AUX) +#define WCD_CLSH_STATE_HPH_ST_AUX (WCD_CLSH_STATE_HPH_ST | \ + WCD_CLSH_STATE_AUX) +#define WCD_CLSH_STATE_EAR_AUX (WCD_CLSH_STATE_EAR | \ + WCD_CLSH_STATE_AUX) +#define WCD_CLSH_STATE_HPHL_EAR (WCD_CLSH_STATE_HPHL | \ + WCD_CLSH_STATE_EAR) +#define WCD_CLSH_STATE_HPHR_EAR (WCD_CLSH_STATE_HPHR | \ + WCD_CLSH_STATE_EAR) +#define WCD_CLSH_STATE_HPH_ST_EAR (WCD_CLSH_STATE_HPH_ST | \ + WCD_CLSH_STATE_EAR) + +enum { + CLS_H_NORMAL = 0, /* Class-H Default */ + CLS_H_HIFI, /* Class-H HiFi */ + CLS_H_LP, /* Class-H Low Power */ + CLS_AB, /* Class-AB Low HIFI*/ + CLS_H_LOHIFI, /* LoHIFI */ + CLS_H_ULP, /* Ultra Low power */ + CLS_AB_HIFI, /* Class-AB */ + CLS_NONE, /* None of the above modes */ +}; + +/* Class H data that the codec driver will maintain */ +struct wcd_clsh_cdc_info { + u8 state; + int flyback_users; + int buck_users; + int interpolator_modes[WCD_CLSH_STATE_MAX]; +}; + +#ifdef CONFIG_SND_SOC_WCD9XXX_V2 +extern void wcd_cls_h_fsm(struct snd_soc_codec *codec, + struct wcd_clsh_cdc_info *cdc_clsh_d, + u8 clsh_event, u8 req_state, + int int_mode); + +extern void wcd_cls_h_init(struct wcd_clsh_cdc_info *clsh); +#else +extern void wcd_cls_h_fsm(struct snd_soc_codec *codec, + struct wcd_clsh_cdc_info *cdc_clsh_d, + u8 clsh_event, u8 req_state, + int int_mode) +{ +} + +extern void wcd_cls_h_init(struct wcd_clsh_cdc_info *clsh) +{ +} +#endif /* CONFIG_SND_SOC_WCD9XXX_V2 */ + +#endif diff --git a/audio-kernel/asoc/codecs/wcd-dsp-mgr.c b/audio-kernel/asoc/codecs/wcd-dsp-mgr.c new file mode 100644 index 000000000..b45a9b3b7 --- /dev/null +++ b/audio-kernel/asoc/codecs/wcd-dsp-mgr.c @@ -0,0 +1,1334 @@ +/* + * Copyright (c) 2016-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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "wcd-dsp-utils.h" + +/* Forward declarations */ +static char *wdsp_get_cmpnt_type_string(enum wdsp_cmpnt_type); + +/* Component related macros */ +#define WDSP_GET_COMPONENT(wdsp, x) ((x >= WDSP_CMPNT_TYPE_MAX || x < 0) ? \ + NULL : (&(wdsp->cmpnts[x]))) +#define WDSP_GET_CMPNT_TYPE_STR(x) wdsp_get_cmpnt_type_string(x) + +/* + * These #defines indicate the bit number in status field + * for each of the status. If bit is set, it indicates + * the status as done, else if bit is not set, it indicates + * the status is either failed or not done. + */ +#define WDSP_STATUS_INITIALIZED BIT(0) +#define WDSP_STATUS_CODE_DLOADED BIT(1) +#define WDSP_STATUS_DATA_DLOADED BIT(2) +#define WDSP_STATUS_BOOTED BIT(3) + +/* Helper macros for printing wdsp messages */ +#define WDSP_ERR(wdsp, fmt, ...) \ + dev_err(wdsp->mdev, "%s: " fmt "\n", __func__, ##__VA_ARGS__) +#define WDSP_DBG(wdsp, fmt, ...) \ + dev_dbg(wdsp->mdev, "%s: " fmt "\n", __func__, ##__VA_ARGS__) + +/* Helper macros for locking */ +#define WDSP_MGR_MUTEX_LOCK(wdsp, lock) \ +{ \ + WDSP_DBG(wdsp, "mutex_lock(%s)", \ + __stringify_1(lock)); \ + mutex_lock(&lock); \ +} + +#define WDSP_MGR_MUTEX_UNLOCK(wdsp, lock) \ +{ \ + WDSP_DBG(wdsp, "mutex_unlock(%s)", \ + __stringify_1(lock)); \ + mutex_unlock(&lock); \ +} + +/* Helper macros for using status mask */ +#define WDSP_SET_STATUS(wdsp, state) \ +{ \ + wdsp->status |= state; \ + WDSP_DBG(wdsp, "set 0x%lx, new_state = 0x%x", \ + state, wdsp->status); \ +} + +#define WDSP_CLEAR_STATUS(wdsp, state) \ +{ \ + wdsp->status &= (~state); \ + WDSP_DBG(wdsp, "clear 0x%lx, new_state = 0x%x", \ + state, wdsp->status); \ +} + +#define WDSP_STATUS_IS_SET(wdsp, state) (wdsp->status & state) + +/* SSR relate status macros */ +#define WDSP_SSR_STATUS_WDSP_READY BIT(0) +#define WDSP_SSR_STATUS_CDC_READY BIT(1) +#define WDSP_SSR_STATUS_READY \ + (WDSP_SSR_STATUS_WDSP_READY | WDSP_SSR_STATUS_CDC_READY) +#define WDSP_SSR_READY_WAIT_TIMEOUT (10 * HZ) + +enum wdsp_ssr_type { + + /* Init value, indicates there is no SSR in progress */ + WDSP_SSR_TYPE_NO_SSR = 0, + + /* + * Indicates WDSP crashed. The manager driver internally + * decides when to perform WDSP restart based on the + * users of wdsp. Hence there is no explicit WDSP_UP. + */ + WDSP_SSR_TYPE_WDSP_DOWN, + + /* Indicates codec hardware is down */ + WDSP_SSR_TYPE_CDC_DOWN, + + /* Indicates codec hardware is up, trigger to restart WDSP */ + WDSP_SSR_TYPE_CDC_UP, +}; + +struct wdsp_cmpnt { + + /* OF node of the phandle */ + struct device_node *np; + + /* + * Child component's dev_name, should be set in DT for the child's + * phandle if child's dev->of_node does not match the phandle->of_node + */ + const char *cdev_name; + + /* Child component's device node */ + struct device *cdev; + + /* Private data that component may want back on callbacks */ + void *priv_data; + + /* Child ops */ + struct wdsp_cmpnt_ops *ops; +}; + +struct wdsp_ramdump_data { + + /* Ramdump device */ + void *rd_dev; + + /* DMA address of the dump */ + dma_addr_t rd_addr; + + /* Virtual address of the dump */ + void *rd_v_addr; + + /* Data provided through error interrupt */ + struct wdsp_err_signal_arg err_data; +}; + +struct wdsp_mgr_priv { + + /* Manager driver's struct device pointer */ + struct device *mdev; + + /* Match struct for component framework */ + struct component_match *match; + + /* Manager's ops/function callbacks */ + struct wdsp_mgr_ops *ops; + + /* Array to store information for all expected components */ + struct wdsp_cmpnt cmpnts[WDSP_CMPNT_TYPE_MAX]; + + /* The filename of image to be downloaded */ + const char *img_fname; + + /* Keeps track of current state of manager driver */ + u32 status; + + /* Work to load the firmware image after component binding */ + struct work_struct load_fw_work; + + /* List of segments in image to be downloaded */ + struct list_head *seg_list; + + /* Base address of the image in memory */ + u32 base_addr; + + /* Instances using dsp */ + int dsp_users; + + /* Lock for serializing ops called by components */ + struct mutex api_mutex; + + struct wdsp_ramdump_data dump_data; + + /* SSR related */ + enum wdsp_ssr_type ssr_type; + struct mutex ssr_mutex; + struct work_struct ssr_work; + u16 ready_status; + struct completion ready_compl; + + /* Debugfs related */ + struct dentry *entry; + bool panic_on_error; +}; + +static char *wdsp_get_ssr_type_string(enum wdsp_ssr_type type) +{ + switch (type) { + case WDSP_SSR_TYPE_NO_SSR: + return "NO_SSR"; + case WDSP_SSR_TYPE_WDSP_DOWN: + return "WDSP_DOWN"; + case WDSP_SSR_TYPE_CDC_DOWN: + return "CDC_DOWN"; + case WDSP_SSR_TYPE_CDC_UP: + return "CDC_UP"; + default: + pr_err("%s: Invalid ssr_type %d\n", + __func__, type); + return "Invalid"; + } +} + +static char *wdsp_get_cmpnt_type_string(enum wdsp_cmpnt_type type) +{ + switch (type) { + case WDSP_CMPNT_CONTROL: + return "control"; + case WDSP_CMPNT_IPC: + return "ipc"; + case WDSP_CMPNT_TRANSPORT: + return "transport"; + default: + pr_err("%s: Invalid component type %d\n", + __func__, type); + return "Invalid"; + } +} + +static void __wdsp_clr_ready_locked(struct wdsp_mgr_priv *wdsp, + u16 value) +{ + wdsp->ready_status &= ~(value); + WDSP_DBG(wdsp, "ready_status = 0x%x", wdsp->ready_status); +} + +static void __wdsp_set_ready_locked(struct wdsp_mgr_priv *wdsp, + u16 value, bool mark_complete) +{ + wdsp->ready_status |= value; + WDSP_DBG(wdsp, "ready_status = 0x%x", wdsp->ready_status); + + if (mark_complete && + wdsp->ready_status == WDSP_SSR_STATUS_READY) { + WDSP_DBG(wdsp, "marking ready completion"); + complete(&wdsp->ready_compl); + } +} + +static void wdsp_broadcast_event_upseq(struct wdsp_mgr_priv *wdsp, + enum wdsp_event_type event, + void *data) +{ + struct wdsp_cmpnt *cmpnt; + int i; + + for (i = 0; i < WDSP_CMPNT_TYPE_MAX; i++) { + cmpnt = WDSP_GET_COMPONENT(wdsp, i); + if (cmpnt && cmpnt->ops && cmpnt->ops->event_handler) + cmpnt->ops->event_handler(cmpnt->cdev, cmpnt->priv_data, + event, data); + } +} + +static void wdsp_broadcast_event_downseq(struct wdsp_mgr_priv *wdsp, + enum wdsp_event_type event, + void *data) +{ + struct wdsp_cmpnt *cmpnt; + int i; + + for (i = WDSP_CMPNT_TYPE_MAX - 1; i >= 0; i--) { + cmpnt = WDSP_GET_COMPONENT(wdsp, i); + if (cmpnt && cmpnt->ops && cmpnt->ops->event_handler) + cmpnt->ops->event_handler(cmpnt->cdev, cmpnt->priv_data, + event, data); + } +} + +static int wdsp_unicast_event(struct wdsp_mgr_priv *wdsp, + enum wdsp_cmpnt_type type, + enum wdsp_event_type event, + void *data) +{ + struct wdsp_cmpnt *cmpnt; + int ret; + + cmpnt = WDSP_GET_COMPONENT(wdsp, type); + if (cmpnt && cmpnt->ops && cmpnt->ops->event_handler) { + ret = cmpnt->ops->event_handler(cmpnt->cdev, cmpnt->priv_data, + event, data); + } else { + WDSP_ERR(wdsp, "not valid event_handler for %s", + WDSP_GET_CMPNT_TYPE_STR(type)); + ret = -EINVAL; + } + + return ret; +} + +static void wdsp_deinit_components(struct wdsp_mgr_priv *wdsp) +{ + struct wdsp_cmpnt *cmpnt; + int i; + + for (i = WDSP_CMPNT_TYPE_MAX - 1; i >= 0; i--) { + cmpnt = WDSP_GET_COMPONENT(wdsp, i); + if (cmpnt && cmpnt->ops && cmpnt->ops->deinit) + cmpnt->ops->deinit(cmpnt->cdev, cmpnt->priv_data); + } +} + +static int wdsp_init_components(struct wdsp_mgr_priv *wdsp) +{ + struct wdsp_cmpnt *cmpnt; + int fail_idx = WDSP_CMPNT_TYPE_MAX; + int i, ret = 0; + + for (i = 0; i < WDSP_CMPNT_TYPE_MAX; i++) { + + cmpnt = WDSP_GET_COMPONENT(wdsp, i); + + /* Init is allowed to be NULL */ + if (!cmpnt->ops || !cmpnt->ops->init) + continue; + ret = cmpnt->ops->init(cmpnt->cdev, cmpnt->priv_data); + if (ret) { + WDSP_ERR(wdsp, "Init failed (%d) for component %s", + ret, WDSP_GET_CMPNT_TYPE_STR(i)); + fail_idx = i; + break; + } + } + + if (fail_idx < WDSP_CMPNT_TYPE_MAX) { + /* Undo init for already initialized components */ + for (i = fail_idx - 1; i >= 0; i--) { + struct wdsp_cmpnt *cmpnt = WDSP_GET_COMPONENT(wdsp, i); + + if (cmpnt->ops && cmpnt->ops->deinit) + cmpnt->ops->deinit(cmpnt->cdev, + cmpnt->priv_data); + } + } else { + wdsp_broadcast_event_downseq(wdsp, WDSP_EVENT_POST_INIT, NULL); + } + + return ret; +} + +static int wdsp_load_each_segment(struct wdsp_mgr_priv *wdsp, + struct wdsp_img_segment *seg) +{ + struct wdsp_img_section img_section; + int ret; + + WDSP_DBG(wdsp, + "base_addr 0x%x, split_fname %s, load_addr 0x%x, size 0x%zx", + wdsp->base_addr, seg->split_fname, seg->load_addr, seg->size); + + if (seg->load_addr < wdsp->base_addr) { + WDSP_ERR(wdsp, "Invalid addr 0x%x, base_addr = 0x%x", + seg->load_addr, wdsp->base_addr); + return -EINVAL; + } + + img_section.addr = seg->load_addr - wdsp->base_addr; + img_section.size = seg->size; + img_section.data = seg->data; + + ret = wdsp_unicast_event(wdsp, WDSP_CMPNT_TRANSPORT, + WDSP_EVENT_DLOAD_SECTION, + &img_section); + if (ret < 0) + WDSP_ERR(wdsp, + "Failed, err = %d for base_addr = 0x%x split_fname = %s, load_addr = 0x%x, size = 0x%zx", + ret, wdsp->base_addr, seg->split_fname, + seg->load_addr, seg->size); + return ret; +} + +static int wdsp_download_segments(struct wdsp_mgr_priv *wdsp, + unsigned int type) +{ + struct wdsp_cmpnt *ctl; + struct wdsp_img_segment *seg = NULL; + enum wdsp_event_type pre, post; + long status; + int ret; + + ctl = WDSP_GET_COMPONENT(wdsp, WDSP_CMPNT_CONTROL); + + if (type == WDSP_ELF_FLAG_RE) { + pre = WDSP_EVENT_PRE_DLOAD_CODE; + post = WDSP_EVENT_POST_DLOAD_CODE; + status = WDSP_STATUS_CODE_DLOADED; + } else if (type == WDSP_ELF_FLAG_WRITE) { + pre = WDSP_EVENT_PRE_DLOAD_DATA; + post = WDSP_EVENT_POST_DLOAD_DATA; + status = WDSP_STATUS_DATA_DLOADED; + } else { + WDSP_ERR(wdsp, "Invalid type %u", type); + return -EINVAL; + } + + ret = wdsp_get_segment_list(ctl->cdev, wdsp->img_fname, + type, wdsp->seg_list, &wdsp->base_addr); + if (ret < 0 || + list_empty(wdsp->seg_list)) { + WDSP_ERR(wdsp, "Error %d to get image segments for type %d", + ret, type); + wdsp_broadcast_event_downseq(wdsp, WDSP_EVENT_DLOAD_FAILED, + NULL); + goto done; + } + + /* Notify all components that image is about to be downloaded */ + wdsp_broadcast_event_upseq(wdsp, pre, NULL); + + /* Go through the list of segments and download one by one */ + list_for_each_entry(seg, wdsp->seg_list, list) { + ret = wdsp_load_each_segment(wdsp, seg); + if (ret) + goto dload_error; + } + + /* Flush the list before setting status and notifying components */ + wdsp_flush_segment_list(wdsp->seg_list); + + WDSP_SET_STATUS(wdsp, status); + + /* Notify all components that image is downloaded */ + wdsp_broadcast_event_downseq(wdsp, post, NULL); +done: + return ret; + +dload_error: + wdsp_flush_segment_list(wdsp->seg_list); + wdsp_broadcast_event_downseq(wdsp, WDSP_EVENT_DLOAD_FAILED, NULL); + return ret; +} + +static int wdsp_init_and_dload_code_sections(struct wdsp_mgr_priv *wdsp) +{ + int ret; + bool is_initialized; + + is_initialized = WDSP_STATUS_IS_SET(wdsp, WDSP_STATUS_INITIALIZED); + + if (!is_initialized) { + /* Components are not initialized yet, initialize them */ + ret = wdsp_init_components(wdsp); + if (ret < 0) { + WDSP_ERR(wdsp, "INIT failed, err = %d", ret); + goto done; + } + WDSP_SET_STATUS(wdsp, WDSP_STATUS_INITIALIZED); + } + + /* Download the read-execute sections of image */ + ret = wdsp_download_segments(wdsp, WDSP_ELF_FLAG_RE); + if (ret < 0) { + WDSP_ERR(wdsp, "Error %d to download code sections", ret); + goto done; + } +done: + return ret; +} + +static void wdsp_load_fw_image(struct work_struct *work) +{ + struct wdsp_mgr_priv *wdsp; + int ret; + + wdsp = container_of(work, struct wdsp_mgr_priv, load_fw_work); + if (!wdsp) { + pr_err("%s: Invalid private_data\n", __func__); + return; + } + + ret = wdsp_init_and_dload_code_sections(wdsp); + if (ret < 0) + WDSP_ERR(wdsp, "dload code sections failed, err = %d", ret); +} + +static int wdsp_enable_dsp(struct wdsp_mgr_priv *wdsp) +{ + int ret; + + /* Make sure wdsp is in good state */ + if (!WDSP_STATUS_IS_SET(wdsp, WDSP_STATUS_CODE_DLOADED)) { + WDSP_ERR(wdsp, "WDSP in invalid state 0x%x", wdsp->status); + return -EINVAL; + } + + /* + * Acquire SSR mutex lock to make sure enablement of DSP + * does not race with SSR handling. + */ + WDSP_MGR_MUTEX_LOCK(wdsp, wdsp->ssr_mutex); + /* Download the read-write sections of image */ + ret = wdsp_download_segments(wdsp, WDSP_ELF_FLAG_WRITE); + if (ret < 0) { + WDSP_ERR(wdsp, "Data section download failed, err = %d", ret); + goto done; + } + + wdsp_broadcast_event_upseq(wdsp, WDSP_EVENT_PRE_BOOTUP, NULL); + + ret = wdsp_unicast_event(wdsp, WDSP_CMPNT_CONTROL, + WDSP_EVENT_DO_BOOT, NULL); + if (ret < 0) { + WDSP_ERR(wdsp, "Failed to boot dsp, err = %d", ret); + WDSP_CLEAR_STATUS(wdsp, WDSP_STATUS_DATA_DLOADED); + goto done; + } + + wdsp_broadcast_event_downseq(wdsp, WDSP_EVENT_POST_BOOTUP, NULL); + WDSP_SET_STATUS(wdsp, WDSP_STATUS_BOOTED); +done: + WDSP_MGR_MUTEX_UNLOCK(wdsp, wdsp->ssr_mutex); + return ret; +} + +static int wdsp_disable_dsp(struct wdsp_mgr_priv *wdsp) +{ + int ret; + + WDSP_MGR_MUTEX_LOCK(wdsp, wdsp->ssr_mutex); + + /* + * If Disable happened while SSR is in progress, then set the SSR + * ready status indicating WDSP is now ready. Ignore the disable + * event here and let the SSR handler go through shutdown. + */ + if (wdsp->ssr_type != WDSP_SSR_TYPE_NO_SSR) { + __wdsp_set_ready_locked(wdsp, WDSP_SSR_STATUS_WDSP_READY, true); + WDSP_MGR_MUTEX_UNLOCK(wdsp, wdsp->ssr_mutex); + return 0; + } + + WDSP_MGR_MUTEX_UNLOCK(wdsp, wdsp->ssr_mutex); + + /* Make sure wdsp is in good state */ + if (!WDSP_STATUS_IS_SET(wdsp, WDSP_STATUS_BOOTED)) { + WDSP_ERR(wdsp, "wdsp in invalid state 0x%x", wdsp->status); + ret = -EINVAL; + goto done; + } + + wdsp_broadcast_event_downseq(wdsp, WDSP_EVENT_PRE_SHUTDOWN, NULL); + ret = wdsp_unicast_event(wdsp, WDSP_CMPNT_CONTROL, + WDSP_EVENT_DO_SHUTDOWN, NULL); + if (ret < 0) { + WDSP_ERR(wdsp, "Failed to shutdown dsp, err = %d", ret); + goto done; + } + + wdsp_broadcast_event_downseq(wdsp, WDSP_EVENT_POST_SHUTDOWN, NULL); + WDSP_CLEAR_STATUS(wdsp, WDSP_STATUS_BOOTED); + + /* Data sections are to be downloaded per boot */ + WDSP_CLEAR_STATUS(wdsp, WDSP_STATUS_DATA_DLOADED); +done: + return ret; +} + +static int wdsp_register_cmpnt_ops(struct device *wdsp_dev, + struct device *cdev, + void *priv_data, + struct wdsp_cmpnt_ops *ops) +{ + struct wdsp_mgr_priv *wdsp; + struct wdsp_cmpnt *cmpnt; + int i, ret; + + if (!wdsp_dev || !cdev || !ops) + return -EINVAL; + + wdsp = dev_get_drvdata(wdsp_dev); + + WDSP_MGR_MUTEX_LOCK(wdsp, wdsp->api_mutex); + + for (i = 0; i < WDSP_CMPNT_TYPE_MAX; i++) { + cmpnt = WDSP_GET_COMPONENT(wdsp, i); + if ((cdev->of_node && cdev->of_node == cmpnt->np) || + (cmpnt->cdev_name && + !strcmp(dev_name(cdev), cmpnt->cdev_name))) { + break; + } + } + + if (i == WDSP_CMPNT_TYPE_MAX) { + WDSP_ERR(wdsp, "Failed to register component dev %s", + dev_name(cdev)); + ret = -EINVAL; + goto done; + } + + cmpnt->cdev = cdev; + cmpnt->ops = ops; + cmpnt->priv_data = priv_data; +done: + WDSP_MGR_MUTEX_UNLOCK(wdsp, wdsp->api_mutex); + return 0; +} + +static struct device *wdsp_get_dev_for_cmpnt(struct device *wdsp_dev, + enum wdsp_cmpnt_type type) +{ + struct wdsp_mgr_priv *wdsp; + struct wdsp_cmpnt *cmpnt; + + if (!wdsp_dev || type >= WDSP_CMPNT_TYPE_MAX) + return NULL; + + wdsp = dev_get_drvdata(wdsp_dev); + cmpnt = WDSP_GET_COMPONENT(wdsp, type); + + return cmpnt->cdev; +} + +static int wdsp_get_devops_for_cmpnt(struct device *wdsp_dev, + enum wdsp_cmpnt_type type, + void *data) +{ + struct wdsp_mgr_priv *wdsp; + int ret = 0; + + if (!wdsp_dev || type >= WDSP_CMPNT_TYPE_MAX) + return -EINVAL; + + wdsp = dev_get_drvdata(wdsp_dev); + ret = wdsp_unicast_event(wdsp, type, + WDSP_EVENT_GET_DEVOPS, data); + if (ret) + WDSP_ERR(wdsp, "get_dev_ops failed for cmpnt type %d", + type); + return ret; +} + +static void wdsp_collect_ramdumps(struct wdsp_mgr_priv *wdsp) +{ + struct wdsp_img_section img_section; + struct wdsp_err_signal_arg *data = &wdsp->dump_data.err_data; + struct ramdump_segment rd_seg; + int ret = 0; + + if (wdsp->ssr_type != WDSP_SSR_TYPE_WDSP_DOWN || + !data->mem_dumps_enabled) { + WDSP_DBG(wdsp, "cannot dump memory, ssr_type %s, dumps %s", + wdsp_get_ssr_type_string(wdsp->ssr_type), + !(data->mem_dumps_enabled) ? "disabled" : "enabled"); + goto done; + } + + if (data->dump_size == 0 || + data->remote_start_addr < wdsp->base_addr) { + WDSP_ERR(wdsp, "Invalid start addr 0x%x or dump_size 0x%zx", + data->remote_start_addr, data->dump_size); + goto done; + } + + if (!wdsp->dump_data.rd_dev) { + WDSP_ERR(wdsp, "Ramdump device is not setup"); + goto done; + } + + WDSP_DBG(wdsp, "base_addr 0x%x, dump_start_addr 0x%x, dump_size 0x%zx", + wdsp->base_addr, data->remote_start_addr, data->dump_size); + + /* Allocate memory for dumps */ + wdsp->dump_data.rd_v_addr = dma_alloc_coherent(wdsp->mdev, + data->dump_size, + &wdsp->dump_data.rd_addr, + GFP_KERNEL); + if (!wdsp->dump_data.rd_v_addr) + goto done; + + img_section.addr = data->remote_start_addr - wdsp->base_addr; + img_section.size = data->dump_size; + img_section.data = wdsp->dump_data.rd_v_addr; + + ret = wdsp_unicast_event(wdsp, WDSP_CMPNT_TRANSPORT, + WDSP_EVENT_READ_SECTION, + &img_section); + if (ret < 0) { + WDSP_ERR(wdsp, "Failed to read dumps, size 0x%zx at addr 0x%x", + img_section.size, img_section.addr); + goto err_read_dumps; + } + + /* + * If panic_on_error flag is explicitly set through the debugfs, + * then cause a BUG here to aid debugging. + */ + BUG_ON(wdsp->panic_on_error); + + rd_seg.address = (unsigned long) wdsp->dump_data.rd_v_addr; + rd_seg.size = img_section.size; + rd_seg.v_address = wdsp->dump_data.rd_v_addr; + + ret = do_ramdump(wdsp->dump_data.rd_dev, &rd_seg, 1); + if (ret < 0) + WDSP_ERR(wdsp, "do_ramdump failed with error %d", ret); + +err_read_dumps: + dma_free_coherent(wdsp->mdev, data->dump_size, + wdsp->dump_data.rd_v_addr, wdsp->dump_data.rd_addr); +done: + return; +} + +static void wdsp_ssr_work_fn(struct work_struct *work) +{ + struct wdsp_mgr_priv *wdsp; + int ret; + + wdsp = container_of(work, struct wdsp_mgr_priv, ssr_work); + if (!wdsp) { + pr_err("%s: Invalid private_data\n", __func__); + return; + } + + WDSP_MGR_MUTEX_LOCK(wdsp, wdsp->ssr_mutex); + + /* Issue ramdumps and shutdown only if DSP is currently booted */ + if (WDSP_STATUS_IS_SET(wdsp, WDSP_STATUS_BOOTED)) { + wdsp_collect_ramdumps(wdsp); + ret = wdsp_unicast_event(wdsp, WDSP_CMPNT_CONTROL, + WDSP_EVENT_DO_SHUTDOWN, NULL); + if (ret < 0) + WDSP_ERR(wdsp, "Failed WDSP shutdown, err = %d", ret); + + wdsp_broadcast_event_downseq(wdsp, WDSP_EVENT_POST_SHUTDOWN, + NULL); + WDSP_CLEAR_STATUS(wdsp, WDSP_STATUS_BOOTED); + } + + WDSP_MGR_MUTEX_UNLOCK(wdsp, wdsp->ssr_mutex); + ret = wait_for_completion_timeout(&wdsp->ready_compl, + WDSP_SSR_READY_WAIT_TIMEOUT); + WDSP_MGR_MUTEX_LOCK(wdsp, wdsp->ssr_mutex); + if (ret == 0) { + WDSP_ERR(wdsp, "wait_for_ready timed out, status = 0x%x", + wdsp->ready_status); + goto done; + } + + /* Data sections are to downloaded per WDSP boot */ + WDSP_CLEAR_STATUS(wdsp, WDSP_STATUS_DATA_DLOADED); + + /* + * Even though code section could possible be retained on DSP + * crash, go ahead and still re-download just to avoid any + * memory corruption from previous crash. + */ + WDSP_CLEAR_STATUS(wdsp, WDSP_STATUS_CODE_DLOADED); + + /* If codec restarted, then all components must be re-initialized */ + if (wdsp->ssr_type == WDSP_SSR_TYPE_CDC_UP) { + wdsp_deinit_components(wdsp); + WDSP_CLEAR_STATUS(wdsp, WDSP_STATUS_INITIALIZED); + } + + ret = wdsp_init_and_dload_code_sections(wdsp); + if (ret < 0) { + WDSP_ERR(wdsp, "Failed to dload code sections err = %d", + ret); + goto done; + } + + /* SSR handling is finished, mark SSR type as NO_SSR */ + wdsp->ssr_type = WDSP_SSR_TYPE_NO_SSR; +done: + WDSP_MGR_MUTEX_UNLOCK(wdsp, wdsp->ssr_mutex); +} + +static int wdsp_ssr_handler(struct wdsp_mgr_priv *wdsp, void *arg, + enum wdsp_ssr_type ssr_type) +{ + enum wdsp_ssr_type current_ssr_type; + struct wdsp_err_signal_arg *err_data; + + WDSP_MGR_MUTEX_LOCK(wdsp, wdsp->ssr_mutex); + + current_ssr_type = wdsp->ssr_type; + WDSP_DBG(wdsp, "Current ssr_type %s, handling ssr_type %s", + wdsp_get_ssr_type_string(current_ssr_type), + wdsp_get_ssr_type_string(ssr_type)); + wdsp->ssr_type = ssr_type; + + if (arg) { + err_data = (struct wdsp_err_signal_arg *) arg; + memcpy(&wdsp->dump_data.err_data, err_data, + sizeof(*err_data)); + } else { + memset(&wdsp->dump_data.err_data, 0, + sizeof(wdsp->dump_data.err_data)); + } + + switch (ssr_type) { + + case WDSP_SSR_TYPE_WDSP_DOWN: + __wdsp_clr_ready_locked(wdsp, WDSP_SSR_STATUS_WDSP_READY); + wdsp_broadcast_event_downseq(wdsp, WDSP_EVENT_PRE_SHUTDOWN, + NULL); + reinit_completion(&wdsp->ready_compl); + schedule_work(&wdsp->ssr_work); + break; + + case WDSP_SSR_TYPE_CDC_DOWN: + __wdsp_clr_ready_locked(wdsp, WDSP_SSR_STATUS_CDC_READY); + /* + * If DSP is booted when CDC_DOWN is received, it needs + * to be shutdown. + */ + if (WDSP_STATUS_IS_SET(wdsp, WDSP_STATUS_BOOTED)) { + __wdsp_clr_ready_locked(wdsp, + WDSP_SSR_STATUS_WDSP_READY); + wdsp_broadcast_event_downseq(wdsp, + WDSP_EVENT_PRE_SHUTDOWN, + NULL); + } + reinit_completion(&wdsp->ready_compl); + schedule_work(&wdsp->ssr_work); + break; + + case WDSP_SSR_TYPE_CDC_UP: + __wdsp_set_ready_locked(wdsp, WDSP_SSR_STATUS_CDC_READY, true); + break; + + default: + WDSP_ERR(wdsp, "undefined ssr_type %d\n", ssr_type); + /* Revert back the ssr_type for undefined events */ + wdsp->ssr_type = current_ssr_type; + break; + } + + WDSP_MGR_MUTEX_UNLOCK(wdsp, wdsp->ssr_mutex); + + return 0; +} + +#ifdef CONFIG_DEBUG_FS +static int __wdsp_dbg_dump_locked(struct wdsp_mgr_priv *wdsp, void *arg) +{ + struct wdsp_err_signal_arg *err_data; + int ret = 0; + + /* If there is no SSR, set the SSR type to collect ramdumps */ + if (wdsp->ssr_type == WDSP_SSR_TYPE_NO_SSR) { + wdsp->ssr_type = WDSP_SSR_TYPE_WDSP_DOWN; + } else { + WDSP_DBG(wdsp, "SSR handling is running, skip debug ramdump"); + ret = 0; + goto done; + } + + if (arg) { + err_data = (struct wdsp_err_signal_arg *) arg; + memcpy(&wdsp->dump_data.err_data, err_data, + sizeof(*err_data)); + } else { + WDSP_DBG(wdsp, "Invalid input, arg is NULL"); + ret = -EINVAL; + goto done; + } + wdsp_collect_ramdumps(wdsp); + wdsp->ssr_type = WDSP_SSR_TYPE_NO_SSR; +done: + return ret; +} +static int wdsp_debug_dump_handler(struct wdsp_mgr_priv *wdsp, void *arg) +{ + int ret = 0; + + WDSP_MGR_MUTEX_LOCK(wdsp, wdsp->ssr_mutex); + ret = __wdsp_dbg_dump_locked(wdsp, arg); + WDSP_MGR_MUTEX_UNLOCK(wdsp, wdsp->ssr_mutex); + + return ret; +} +#else +static int __wdsp_dbg_dump_locked(struct wdsp_mgr_priv *wdsp, void *arg) +{ + return 0; +} + +static int wdsp_debug_dump_handler(struct wdsp_mgr_priv *wdsp, void *arg) +{ + return 0; +} +#endif + +static int wdsp_signal_handler(struct device *wdsp_dev, + enum wdsp_signal signal, void *arg) +{ + struct wdsp_mgr_priv *wdsp; + int ret; + + if (!wdsp_dev) + return -EINVAL; + + wdsp = dev_get_drvdata(wdsp_dev); + +#ifdef CONFIG_DEBUG_FS + if (signal != WDSP_DEBUG_DUMP_INTERNAL) + WDSP_MGR_MUTEX_LOCK(wdsp, wdsp->api_mutex); +#else + WDSP_MGR_MUTEX_LOCK(wdsp, wdsp->api_mutex); +#endif + + WDSP_DBG(wdsp, "Raised signal %d", signal); + + switch (signal) { + case WDSP_IPC1_INTR: + ret = wdsp_unicast_event(wdsp, WDSP_CMPNT_IPC, + WDSP_EVENT_IPC1_INTR, NULL); + break; + case WDSP_ERR_INTR: + ret = wdsp_ssr_handler(wdsp, arg, WDSP_SSR_TYPE_WDSP_DOWN); + break; + case WDSP_CDC_DOWN_SIGNAL: + ret = wdsp_ssr_handler(wdsp, arg, WDSP_SSR_TYPE_CDC_DOWN); + break; + case WDSP_CDC_UP_SIGNAL: + ret = wdsp_ssr_handler(wdsp, arg, WDSP_SSR_TYPE_CDC_UP); + break; + case WDSP_DEBUG_DUMP: + ret = wdsp_debug_dump_handler(wdsp, arg); + break; + case WDSP_DEBUG_DUMP_INTERNAL: + ret = __wdsp_dbg_dump_locked(wdsp, arg); + break; + default: + ret = -EINVAL; + break; + } + + if (ret < 0) + WDSP_ERR(wdsp, "handling signal %d failed with error %d", + signal, ret); + +#ifdef CONFIG_DEBUG_FS + if (signal != WDSP_DEBUG_DUMP_INTERNAL) + WDSP_MGR_MUTEX_UNLOCK(wdsp, wdsp->api_mutex); +#else + WDSP_MGR_MUTEX_UNLOCK(wdsp, wdsp->api_mutex); +#endif + + return ret; +} + +static int wdsp_vote_for_dsp(struct device *wdsp_dev, + bool vote) +{ + struct wdsp_mgr_priv *wdsp; + int ret = 0; + + if (!wdsp_dev) + return -EINVAL; + + wdsp = dev_get_drvdata(wdsp_dev); + + WDSP_MGR_MUTEX_LOCK(wdsp, wdsp->api_mutex); + WDSP_DBG(wdsp, "request %s, current users = %d", + vote ? "enable" : "disable", wdsp->dsp_users); + + if (vote) { + wdsp->dsp_users++; + if (wdsp->dsp_users == 1) + ret = wdsp_enable_dsp(wdsp); + } else { + if (wdsp->dsp_users == 0) + goto done; + + wdsp->dsp_users--; + if (wdsp->dsp_users == 0) + ret = wdsp_disable_dsp(wdsp); + } + + if (ret < 0) + WDSP_DBG(wdsp, "wdsp %s failed, err = %d", + vote ? "enable" : "disable", ret); + +done: + WDSP_MGR_MUTEX_UNLOCK(wdsp, wdsp->api_mutex); + return ret; +} + +static int wdsp_suspend(struct device *wdsp_dev) +{ + struct wdsp_mgr_priv *wdsp; + int rc = 0, i; + + if (!wdsp_dev) { + pr_err("%s: Invalid handle to device\n", __func__); + return -EINVAL; + } + + wdsp = dev_get_drvdata(wdsp_dev); + + for (i = WDSP_CMPNT_TYPE_MAX - 1; i >= 0; i--) { + rc = wdsp_unicast_event(wdsp, i, WDSP_EVENT_SUSPEND, NULL); + if (rc < 0) { + WDSP_ERR(wdsp, "component %s failed to suspend\n", + WDSP_GET_CMPNT_TYPE_STR(i)); + break; + } + } + + return rc; +} + +static int wdsp_resume(struct device *wdsp_dev) +{ + struct wdsp_mgr_priv *wdsp; + int rc = 0, i; + + if (!wdsp_dev) { + pr_err("%s: Invalid handle to device\n", __func__); + return -EINVAL; + } + + wdsp = dev_get_drvdata(wdsp_dev); + + for (i = 0; i < WDSP_CMPNT_TYPE_MAX; i++) { + rc = wdsp_unicast_event(wdsp, i, WDSP_EVENT_RESUME, NULL); + if (rc < 0) { + WDSP_ERR(wdsp, "component %s failed to resume\n", + WDSP_GET_CMPNT_TYPE_STR(i)); + break; + } + } + + return rc; +} + +static struct wdsp_mgr_ops wdsp_ops = { + .register_cmpnt_ops = wdsp_register_cmpnt_ops, + .get_dev_for_cmpnt = wdsp_get_dev_for_cmpnt, + .get_devops_for_cmpnt = wdsp_get_devops_for_cmpnt, + .signal_handler = wdsp_signal_handler, + .vote_for_dsp = wdsp_vote_for_dsp, + .suspend = wdsp_suspend, + .resume = wdsp_resume, +}; + +static int wdsp_mgr_compare_of(struct device *dev, void *data) +{ + struct wdsp_cmpnt *cmpnt = data; + + /* + * First try to match based on of_node, if of_node is not + * present, try to match on the dev_name + */ + return ((dev->of_node && dev->of_node == cmpnt->np) || + (cmpnt->cdev_name && + !strcmp(dev_name(dev), cmpnt->cdev_name))); +} + +static void wdsp_mgr_debugfs_init(struct wdsp_mgr_priv *wdsp) +{ + wdsp->entry = debugfs_create_dir("wdsp_mgr", NULL); + if (IS_ERR_OR_NULL(wdsp->entry)) + return; + + debugfs_create_bool("panic_on_error", 0644, + wdsp->entry, &wdsp->panic_on_error); +} + +static void wdsp_mgr_debugfs_remove(struct wdsp_mgr_priv *wdsp) +{ + debugfs_remove_recursive(wdsp->entry); + wdsp->entry = NULL; +} + +static int wdsp_mgr_bind(struct device *dev) +{ + struct wdsp_mgr_priv *wdsp = dev_get_drvdata(dev); + struct wdsp_cmpnt *cmpnt; + int ret, idx; + + wdsp->ops = &wdsp_ops; + + /* Setup ramdump device */ + wdsp->dump_data.rd_dev = create_ramdump_device("wdsp", dev); + if (!wdsp->dump_data.rd_dev) + dev_info(dev, "%s: create_ramdump_device failed\n", __func__); + + ret = component_bind_all(dev, wdsp->ops); + if (ret < 0) { + WDSP_ERR(wdsp, "component_bind_all failed %d\n", ret); + return ret; + } + + /* Make sure all components registered ops */ + for (idx = 0; idx < WDSP_CMPNT_TYPE_MAX; idx++) { + cmpnt = WDSP_GET_COMPONENT(wdsp, idx); + if (!cmpnt->cdev || !cmpnt->ops) { + WDSP_ERR(wdsp, "%s did not register ops\n", + WDSP_GET_CMPNT_TYPE_STR(idx)); + ret = -EINVAL; + component_unbind_all(dev, wdsp->ops); + break; + } + } + + wdsp_mgr_debugfs_init(wdsp); + + /* Schedule the work to download image if binding was successful. */ + if (!ret) + schedule_work(&wdsp->load_fw_work); + + return ret; +} + +static void wdsp_mgr_unbind(struct device *dev) +{ + struct wdsp_mgr_priv *wdsp = dev_get_drvdata(dev); + struct wdsp_cmpnt *cmpnt; + int idx; + + cancel_work_sync(&wdsp->load_fw_work); + + component_unbind_all(dev, wdsp->ops); + + wdsp_mgr_debugfs_remove(wdsp); + + if (wdsp->dump_data.rd_dev) { + destroy_ramdump_device(wdsp->dump_data.rd_dev); + wdsp->dump_data.rd_dev = NULL; + } + + /* Clear all status bits */ + wdsp->status = 0x00; + + /* clean up the components */ + for (idx = 0; idx < WDSP_CMPNT_TYPE_MAX; idx++) { + cmpnt = WDSP_GET_COMPONENT(wdsp, idx); + cmpnt->cdev = NULL; + cmpnt->ops = NULL; + cmpnt->priv_data = NULL; + } +} + +static const struct component_master_ops wdsp_master_ops = { + .bind = wdsp_mgr_bind, + .unbind = wdsp_mgr_unbind, +}; + +static void *wdsp_mgr_parse_phandle(struct wdsp_mgr_priv *wdsp, + int index) +{ + struct device *mdev = wdsp->mdev; + struct device_node *np; + struct wdsp_cmpnt *cmpnt = NULL; + struct of_phandle_args pargs; + u32 value; + int ret; + + ret = of_parse_phandle_with_fixed_args(mdev->of_node, + "qcom,wdsp-components", 1, + index, &pargs); + if (ret) { + WDSP_ERR(wdsp, "parse_phandle at index %d failed %d", + index, ret); + return NULL; + } + + np = pargs.np; + value = pargs.args[0]; + + if (value >= WDSP_CMPNT_TYPE_MAX) { + WDSP_ERR(wdsp, "invalid phandle_arg to of_node %s", np->name); + goto done; + } + + cmpnt = WDSP_GET_COMPONENT(wdsp, value); + if (cmpnt->np || cmpnt->cdev_name) { + WDSP_ERR(wdsp, "cmpnt %d already added", value); + cmpnt = NULL; + goto done; + } + + cmpnt->np = np; + of_property_read_string(np, "qcom,wdsp-cmpnt-dev-name", + &cmpnt->cdev_name); +done: + of_node_put(np); + return cmpnt; +} + +static int wdsp_mgr_parse_dt_entries(struct wdsp_mgr_priv *wdsp) +{ + struct device *dev = wdsp->mdev; + void *match_data; + int ph_idx, ret; + + ret = of_property_read_string(dev->of_node, "qcom,img-filename", + &wdsp->img_fname); + if (ret < 0) { + WDSP_ERR(wdsp, "Reading property %s failed, error = %d", + "qcom,img-filename", ret); + return ret; + } + + ret = of_count_phandle_with_args(dev->of_node, + "qcom,wdsp-components", + NULL); + if (ret == -ENOENT) { + WDSP_ERR(wdsp, "Property %s not defined in DT", + "qcom,wdsp-components"); + goto done; + } else if (ret != WDSP_CMPNT_TYPE_MAX * 2) { + WDSP_ERR(wdsp, "Invalid phandle + arg count %d, expected %d", + ret, WDSP_CMPNT_TYPE_MAX * 2); + ret = -EINVAL; + goto done; + } + + ret = 0; + + for (ph_idx = 0; ph_idx < WDSP_CMPNT_TYPE_MAX; ph_idx++) { + + match_data = wdsp_mgr_parse_phandle(wdsp, ph_idx); + if (!match_data) { + WDSP_ERR(wdsp, "component not found at idx %d", ph_idx); + ret = -EINVAL; + goto done; + } + + component_match_add(dev, &wdsp->match, + wdsp_mgr_compare_of, match_data); + } + +done: + return ret; +} + +static int wdsp_mgr_probe(struct platform_device *pdev) +{ + struct wdsp_mgr_priv *wdsp; + struct device *mdev = &pdev->dev; + int ret; + + wdsp = devm_kzalloc(mdev, sizeof(*wdsp), GFP_KERNEL); + if (!wdsp) + return -ENOMEM; + wdsp->mdev = mdev; + wdsp->seg_list = devm_kzalloc(mdev, sizeof(struct list_head), + GFP_KERNEL); + if (!wdsp->seg_list) { + devm_kfree(mdev, wdsp); + return -ENOMEM; + } + + ret = wdsp_mgr_parse_dt_entries(wdsp); + if (ret) + goto err_dt_parse; + + INIT_WORK(&wdsp->load_fw_work, wdsp_load_fw_image); + INIT_LIST_HEAD(wdsp->seg_list); + mutex_init(&wdsp->api_mutex); + mutex_init(&wdsp->ssr_mutex); + wdsp->ssr_type = WDSP_SSR_TYPE_NO_SSR; + wdsp->ready_status = WDSP_SSR_STATUS_READY; + INIT_WORK(&wdsp->ssr_work, wdsp_ssr_work_fn); + init_completion(&wdsp->ready_compl); + arch_setup_dma_ops(wdsp->mdev, 0, 0, NULL, 0); + dev_set_drvdata(mdev, wdsp); + + ret = component_master_add_with_match(mdev, &wdsp_master_ops, + wdsp->match); + if (ret < 0) { + WDSP_ERR(wdsp, "Failed to add master, err = %d", ret); + goto err_master_add; + } + + return 0; + +err_master_add: + mutex_destroy(&wdsp->api_mutex); + mutex_destroy(&wdsp->ssr_mutex); +err_dt_parse: + devm_kfree(mdev, wdsp->seg_list); + devm_kfree(mdev, wdsp); + dev_set_drvdata(mdev, NULL); + + return ret; +} + +static int wdsp_mgr_remove(struct platform_device *pdev) +{ + struct device *mdev = &pdev->dev; + struct wdsp_mgr_priv *wdsp = dev_get_drvdata(mdev); + + component_master_del(mdev, &wdsp_master_ops); + + mutex_destroy(&wdsp->api_mutex); + mutex_destroy(&wdsp->ssr_mutex); + devm_kfree(mdev, wdsp->seg_list); + devm_kfree(mdev, wdsp); + dev_set_drvdata(mdev, NULL); + + return 0; +}; + +static const struct of_device_id wdsp_mgr_dt_match[] = { + {.compatible = "qcom,wcd-dsp-mgr" }, + { } +}; + +static struct platform_driver wdsp_mgr_driver = { + .driver = { + .name = "wcd-dsp-mgr", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(wdsp_mgr_dt_match), + }, + .probe = wdsp_mgr_probe, + .remove = wdsp_mgr_remove, +}; + +int wcd_dsp_mgr_init(void) +{ + return platform_driver_register(&wdsp_mgr_driver); +} + +void wcd_dsp_mgr_exit(void) +{ + platform_driver_unregister(&wdsp_mgr_driver); +} + +MODULE_DESCRIPTION("WCD DSP manager driver"); +MODULE_DEVICE_TABLE(of, wdsp_mgr_dt_match); +MODULE_LICENSE("GPL v2"); diff --git a/audio-kernel/asoc/codecs/wcd-dsp-utils.c b/audio-kernel/asoc/codecs/wcd-dsp-utils.c new file mode 100644 index 000000000..1c95d48a1 --- /dev/null +++ b/audio-kernel/asoc/codecs/wcd-dsp-utils.c @@ -0,0 +1,232 @@ +/* + * Copyright (c) 2016-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 +#include +#include +#include +#include +#include +#include "wcd-dsp-utils.h" + +static bool wdsp_is_valid_elf_hdr(const struct elf32_hdr *ehdr, + size_t fw_size) +{ + if (fw_size < sizeof(*ehdr)) { + pr_err("%s: Firmware too small\n", __func__); + goto elf_check_fail; + } + + if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG) != 0) { + pr_err("%s: Not an ELF file\n", __func__); + goto elf_check_fail; + } + + if (ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN) { + pr_err("%s: Not an executable image\n", __func__); + goto elf_check_fail; + } + + if (ehdr->e_phnum == 0) { + pr_err("%s: no segments to load\n", __func__); + goto elf_check_fail; + } + + if (sizeof(struct elf32_phdr) * ehdr->e_phnum + + sizeof(struct elf32_hdr) > fw_size) { + pr_err("%s: Too small MDT file\n", __func__); + goto elf_check_fail; + } + + return true; + +elf_check_fail: + return false; +} + +static int wdsp_add_segment_to_list(struct device *dev, + const char *img_fname, + const struct elf32_phdr *phdr, + int phdr_idx, + struct list_head *seg_list) +{ + struct wdsp_img_segment *seg; + int ret = 0; + + /* Do not load segments with zero size */ + if (phdr->p_filesz == 0 || phdr->p_memsz == 0) + goto done; + + seg = kzalloc(sizeof(*seg), GFP_KERNEL); + if (!seg) { + ret = -ENOMEM; + goto done; + } + + snprintf(seg->split_fname, sizeof(seg->split_fname), + "%s.b%02d", img_fname, phdr_idx); + ret = request_firmware(&seg->split_fw, seg->split_fname, dev); + if (ret < 0) { + dev_err(dev, "%s: firmware %s not found\n", + __func__, seg->split_fname); + goto bad_seg; + } + + if (phdr->p_filesz != seg->split_fw->size) { + dev_err(dev, + "%s: %s size mismatch, phdr_size: 0x%x fw_size: 0x%zx", + __func__, seg->split_fname, phdr->p_filesz, + seg->split_fw->size); + ret = -EINVAL; + goto bad_elf; + } + + seg->load_addr = phdr->p_paddr; + seg->size = phdr->p_filesz; + seg->data = (u8 *) seg->split_fw->data; + + list_add_tail(&seg->list, seg_list); +done: + return ret; +bad_elf: + release_firmware(seg->split_fw); +bad_seg: + kfree(seg); + return ret; +} + +/* + * wdsp_flush_segment_list: Flush the list of segments + * @seg_list: List of segments to be flushed + * This API will traverse through the list of segments provided in + * seg_list, release the firmware for each segment and delete the + * segment from the list. + */ +void wdsp_flush_segment_list(struct list_head *seg_list) +{ + struct wdsp_img_segment *seg, *next; + + list_for_each_entry_safe(seg, next, seg_list, list) { + release_firmware(seg->split_fw); + list_del(&seg->list); + kfree(seg); + } +} +EXPORT_SYMBOL(wdsp_flush_segment_list); + +/* + * wdsp_get_segment_list: Get the list of requested segments + * @dev: struct device pointer of caller + * @img_fname: Image name for the mdt and split firmware files + * @segment_type: Requested segment type, should be either + * WDSP_ELF_FLAG_RE or WDSP_ELF_FLAG_WRITE + * @seg_list: An initialized head for list of segmented to be returned + * @entry_point: Pointer to return the entry point of the image + * This API will parse the mdt file for img_fname and create + * an struct wdsp_img_segment for each segment that matches segment_type + * and add this structure to list pointed by seg_list + */ +int wdsp_get_segment_list(struct device *dev, + const char *img_fname, + unsigned int segment_type, + struct list_head *seg_list, + u32 *entry_point) +{ + const struct firmware *fw; + const struct elf32_hdr *ehdr; + const struct elf32_phdr *phdr; + const u8 *elf_ptr; + char mdt_name[WDSP_IMG_NAME_LEN_MAX]; + int ret, phdr_idx; + bool segment_match; + + if (!dev) { + ret = -EINVAL; + pr_err("%s: Invalid device handle\n", __func__); + goto done; + } + + if (!img_fname || !seg_list || !entry_point) { + ret = -EINVAL; + dev_err(dev, "%s: Invalid input params\n", + __func__); + goto done; + } + + if (segment_type != WDSP_ELF_FLAG_RE && + segment_type != WDSP_ELF_FLAG_WRITE) { + dev_err(dev, "%s: Invalid request for segment_type %d\n", + __func__, segment_type); + ret = -EINVAL; + goto done; + } + + snprintf(mdt_name, sizeof(mdt_name), "%s.mdt", img_fname); + ret = request_firmware(&fw, mdt_name, dev); + if (ret < 0) { + dev_err(dev, "%s: firmware %s not found\n", + __func__, mdt_name); + goto done; + } + + ehdr = (struct elf32_hdr *) fw->data; + *entry_point = ehdr->e_entry; + if (!wdsp_is_valid_elf_hdr(ehdr, fw->size)) { + dev_err(dev, "%s: fw mdt %s is invalid\n", + __func__, mdt_name); + ret = -EINVAL; + goto bad_elf; + } + + elf_ptr = fw->data + sizeof(*ehdr); + for (phdr_idx = 0; phdr_idx < ehdr->e_phnum; phdr_idx++) { + phdr = (struct elf32_phdr *) elf_ptr; + segment_match = false; + + switch (segment_type) { + case WDSP_ELF_FLAG_RE: + /* + * Flag can be READ or EXECUTE or both but + * WRITE flag should not be set. + */ + if ((phdr->p_flags & segment_type) && + !(phdr->p_flags & WDSP_ELF_FLAG_WRITE)) + segment_match = true; + break; + case WDSP_ELF_FLAG_WRITE: + /* + * If WRITE flag is set, other flags do not + * matter. + */ + if (phdr->p_flags & segment_type) + segment_match = true; + break; + } + + if (segment_match) { + ret = wdsp_add_segment_to_list(dev, img_fname, phdr, + phdr_idx, seg_list); + if (ret < 0) { + wdsp_flush_segment_list(seg_list); + goto bad_elf; + } + } + elf_ptr = elf_ptr + sizeof(*phdr); + } + +bad_elf: + release_firmware(fw); +done: + return ret; +} +EXPORT_SYMBOL(wdsp_get_segment_list); diff --git a/audio-kernel/asoc/codecs/wcd-dsp-utils.h b/audio-kernel/asoc/codecs/wcd-dsp-utils.h new file mode 100644 index 000000000..a530a1c3b --- /dev/null +++ b/audio-kernel/asoc/codecs/wcd-dsp-utils.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2016-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 + * 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 __WCD_DSP_UTILS_H__ +#define __WCD_DSP_UTILS_H__ + +#define WDSP_IMG_NAME_LEN_MAX 64 + +#define WDSP_ELF_FLAG_EXECUTE (1 << 0) +#define WDSP_ELF_FLAG_WRITE (1 << 1) +#define WDSP_ELF_FLAG_READ (1 << 2) + +#define WDSP_ELF_FLAG_RE (WDSP_ELF_FLAG_READ | WDSP_ELF_FLAG_EXECUTE) + +struct wdsp_img_segment { + + /* Firmware for the slit image */ + const struct firmware *split_fw; + + /* Name of the split firmware file */ + char split_fname[WDSP_IMG_NAME_LEN_MAX]; + + /* Address where the segment is to be loaded */ + u32 load_addr; + + /* Buffer to hold the data to be loaded */ + u8 *data; + + /* Size of the data to be loaded */ + size_t size; + + /* List node pointing to next segment */ + struct list_head list; +}; + +int wdsp_get_segment_list(struct device *dev, const char *img_fname, + unsigned int segment_type, struct list_head *seg_list, + u32 *entry_point); +void wdsp_flush_segment_list(struct list_head *seg_list); + +#endif /* __WCD_DSP_UTILS_H__ */ diff --git a/audio-kernel/asoc/codecs/wcd-irq.c b/audio-kernel/asoc/codecs/wcd-irq.c new file mode 100644 index 000000000..48495f553 --- /dev/null +++ b/audio-kernel/asoc/codecs/wcd-irq.c @@ -0,0 +1,191 @@ +/* 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 +#include +#include +#include +#include +#include +#include +#include +#include + +static int wcd_map_irq(struct wcd_irq_info *irq_info, int irq) +{ + if (!irq_info) { + pr_err("%s: Null IRQ handle\n", __func__); + return -EINVAL; + } + return regmap_irq_get_virq(irq_info->irq_chip, irq); +} + +/** + * wcd_request_irq: Request a thread handler for the given IRQ + * @irq_info: pointer to IRQ info structure + * @irq: irq number + * @name: name for the IRQ thread + * @handler: irq handler + * @data: data pointer + * + * Returns 0 on success or error on failure + */ +int wcd_request_irq(struct wcd_irq_info *irq_info, int irq, const char *name, + irq_handler_t handler, void *data) +{ + if (!irq_info) { + pr_err("%s: Null IRQ handle\n", __func__); + return -EINVAL; + } + irq = wcd_map_irq(irq_info, irq); + if (irq < 0) + return irq; + + return request_threaded_irq(irq, NULL, handler, + IRQF_ONESHOT | IRQF_TRIGGER_RISING, + name, data); +} +EXPORT_SYMBOL(wcd_request_irq); + +/** + * wcd_free_irq: Free the IRQ resources allocated during request_irq + * @irq_info: pointer to IRQ info structure + * @irq: irq number + * @data: data pointer + */ +void wcd_free_irq(struct wcd_irq_info *irq_info, int irq, void *data) +{ + if (!irq_info) { + pr_err("%s: Null IRQ handle\n", __func__); + return; + } + + irq = wcd_map_irq(irq_info, irq); + if (irq < 0) + return; + + free_irq(irq, data); +} +EXPORT_SYMBOL(wcd_free_irq); + +/** + * wcd_enable_irq: Enable the given IRQ + * @irq_info: pointer to IRQ info structure + * @irq: irq number + */ +void wcd_enable_irq(struct wcd_irq_info *irq_info, int irq) +{ + if (!irq_info) + pr_err("%s: Null IRQ handle\n", __func__); + else + enable_irq(wcd_map_irq(irq_info, irq)); +} +EXPORT_SYMBOL(wcd_enable_irq); + +/** + * wcd_disable_irq: Disable the given IRQ + * @irq_info: pointer to IRQ info structure + * @irq: irq number + */ +void wcd_disable_irq(struct wcd_irq_info *irq_info, int irq) +{ + if (!irq_info) + pr_err("%s: Null IRQ handle\n", __func__); + else + disable_irq_nosync(wcd_map_irq(irq_info, irq)); +} +EXPORT_SYMBOL(wcd_disable_irq); + +static void wcd_irq_chip_disable(struct irq_data *data) +{ +} + +static void wcd_irq_chip_enable(struct irq_data *data) +{ +} + +static struct irq_chip wcd_irq_chip = { + .name = NULL, + .irq_disable = wcd_irq_chip_disable, + .irq_enable = wcd_irq_chip_enable, +}; + +static struct lock_class_key wcd_irq_lock_class; + +static int wcd_irq_chip_map(struct irq_domain *irqd, unsigned int virq, + irq_hw_number_t hw) +{ + irq_set_chip_and_handler(virq, &wcd_irq_chip, handle_simple_irq); + irq_set_lockdep_class(virq, &wcd_irq_lock_class); + irq_set_nested_thread(virq, 1); + irq_set_noprobe(virq); + + return 0; +} + +static const struct irq_domain_ops wcd_domain_ops = { + .map = wcd_irq_chip_map, +}; + +/** + * wcd_irq_init: Initializes IRQ module + * @irq_info: pointer to IRQ info structure + * + * Returns 0 on success or error on failure + */ +int wcd_irq_init(struct wcd_irq_info *irq_info, struct irq_domain **virq) +{ + int ret = 0; + + if (!irq_info) { + pr_err("%s: Null IRQ handle\n", __func__); + return -EINVAL; + } + + wcd_irq_chip.name = irq_info->codec_name; + + *virq = irq_domain_add_linear(NULL, 1, &wcd_domain_ops, NULL); + if (!(*virq)) { + pr_err("%s: Failed to add IRQ domain\n", __func__); + return -EINVAL; + } + + ret = devm_regmap_add_irq_chip(irq_info->dev, irq_info->regmap, + irq_create_mapping(*virq, 0), + IRQF_ONESHOT, 0, irq_info->wcd_regmap_irq_chip, + &irq_info->irq_chip); + if (ret) + pr_err("%s: Failed to add IRQs: %d\n", + __func__, ret); + + return ret; +} +EXPORT_SYMBOL(wcd_irq_init); + +/** + * wcd_irq_exit: Uninitialize regmap IRQ and free IRQ resources + * @irq_info: pointer to IRQ info structure + * + * Returns 0 on success or error on failure + */ +int wcd_irq_exit(struct wcd_irq_info *irq_info, struct irq_domain *virq) +{ + if (!irq_info) { + pr_err("%s: Null pointer handle\n", __func__); + return -EINVAL; + } + + regmap_del_irq_chip(irq_find_mapping(virq, 0), irq_info->irq_chip); + + return 0; +} +EXPORT_SYMBOL(wcd_irq_exit); diff --git a/audio-kernel/asoc/codecs/wcd-mbhc-adc.c b/audio-kernel/asoc/codecs/wcd-mbhc-adc.c new file mode 100644 index 000000000..d41b368da --- /dev/null +++ b/audio-kernel/asoc/codecs/wcd-mbhc-adc.c @@ -0,0 +1,1109 @@ +/* Copyright (c) 2017-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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "wcd-mbhc-adc.h" +#include "wcd-mbhc-v2.h" +#include "pdata.h" + +#define WCD_MBHC_ADC_HS_THRESHOLD_MV 1700 +#define WCD_MBHC_ADC_HPH_THRESHOLD_MV 75 +#define WCD_MBHC_ADC_MICBIAS_MV 1800 +#define WCD_MBHC_FAKE_INS_RETRY 4 + +static int wcd_mbhc_get_micbias(struct wcd_mbhc *mbhc) +{ + int micbias = 0; + u8 vout_ctl = 0; + + /* Read MBHC Micbias (Mic Bias2) voltage */ + WCD_MBHC_REG_READ(WCD_MBHC_MICB2_VOUT, vout_ctl); + + /* Formula for getting micbias from vout + * micbias = 1.0V + VOUT_CTL * 50mV + */ + micbias = 1000 + (vout_ctl * 50); + pr_debug("%s: vout_ctl: %d, micbias: %d\n", + __func__, vout_ctl, micbias); + + return micbias; +} + +static int wcd_get_voltage_from_adc(u8 val, int micbias) +{ + /* Formula for calculating voltage from ADC + * Voltage = ADC_RESULT*12.5mV*V_MICBIAS/1.8 + */ + return ((val * 125 * micbias)/(WCD_MBHC_ADC_MICBIAS_MV * 10)); +} + +static int wcd_measure_adc_continuous(struct wcd_mbhc *mbhc) +{ + u8 adc_result = 0; + int output_mv = 0; + int retry = 3; + u8 adc_en = 0; + + pr_debug("%s: enter\n", __func__); + + /* Pre-requisites for ADC continuous measurement */ + /* Read legacy electircal detection and disable */ + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_SCHMT_ISRC, 0x00); + /* Set ADC to continuous measurement */ + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ADC_MODE, 1); + /* Read ADC Enable bit to restore after adc measurement */ + WCD_MBHC_REG_READ(WCD_MBHC_ADC_EN, adc_en); + /* Disable ADC_ENABLE bit */ + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ADC_EN, 0); + /* Disable MBHC FSM */ + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 0); + /* Set the MUX selection to IN2P */ + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_MUX_CTL, MUX_CTL_IN2P); + /* Enable MBHC FSM */ + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 1); + /* Enable ADC_ENABLE bit */ + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ADC_EN, 1); + + while (retry--) { + /* wait for 3 msec before reading ADC result */ + usleep_range(3000, 3100); + + /* Read ADC result */ + WCD_MBHC_REG_READ(WCD_MBHC_ADC_RESULT, adc_result); + } + + /* Restore ADC Enable */ + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ADC_EN, adc_en); + /* Get voltage from ADC result */ + output_mv = wcd_get_voltage_from_adc(adc_result, + wcd_mbhc_get_micbias(mbhc)); + pr_debug("%s: adc_result: 0x%x, output_mv: %d\n", + __func__, adc_result, output_mv); + + return output_mv; +} + +static int wcd_measure_adc_once(struct wcd_mbhc *mbhc, int mux_ctl) +{ + u8 adc_timeout = 0; + u8 adc_complete = 0; + u8 adc_result = 0; + int retry = 6; + int ret = 0; + int output_mv = 0; + u8 adc_en = 0; + + pr_debug("%s: enter\n", __func__); + + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ADC_MODE, 0); + /* Read ADC Enable bit to restore after adc measurement */ + WCD_MBHC_REG_READ(WCD_MBHC_ADC_EN, adc_en); + /* Trigger ADC one time measurement */ + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ADC_EN, 0); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 0); + /* Set the appropriate MUX selection */ + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_MUX_CTL, mux_ctl); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 1); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ADC_EN, 1); + + while (retry--) { + /* wait for 600usec to get adc results */ + usleep_range(600, 610); + + /* check for ADC Timeout */ + WCD_MBHC_REG_READ(WCD_MBHC_ADC_TIMEOUT, adc_timeout); + if (adc_timeout) + continue; + + /* Read ADC complete bit */ + WCD_MBHC_REG_READ(WCD_MBHC_ADC_COMPLETE, adc_complete); + if (!adc_complete) + continue; + + /* Read ADC result */ + WCD_MBHC_REG_READ(WCD_MBHC_ADC_RESULT, adc_result); + + pr_debug("%s: ADC result: 0x%x\n", __func__, adc_result); + /* Get voltage from ADC result */ + output_mv = wcd_get_voltage_from_adc(adc_result, + wcd_mbhc_get_micbias(mbhc)); + break; + } + + /* Restore ADC Enable */ + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ADC_EN, adc_en); + + if (retry <= 0) { + pr_err("%s: adc complete: %d, adc timeout: %d\n", + __func__, adc_complete, adc_timeout); + ret = -EINVAL; + } else { + pr_debug("%s: adc complete: %d, adc timeout: %d output_mV: %d\n", + __func__, adc_complete, adc_timeout, output_mv); + ret = output_mv; + } + + pr_debug("%s: leave\n", __func__); + + return ret; +} + +static bool wcd_mbhc_adc_detect_anc_plug_type(struct wcd_mbhc *mbhc) +{ + bool anc_mic_found = false; + u16 fsm_en = 0; + u8 det = 0; + unsigned long retry = 0; + int valid_plug_cnt = 0, invalid_plug_cnt = 0; + int ret = 0; + u8 elect_ctl = 0; + u8 adc_mode = 0; + u8 vref = 0; + int vref_mv[] = {1650, 1500, 1600, 1700}; + + if (mbhc->mbhc_cfg->anc_micbias < MIC_BIAS_1 || + mbhc->mbhc_cfg->anc_micbias > MIC_BIAS_4) + return false; + + if (!mbhc->mbhc_cb->mbhc_micbias_control) + return false; + + /* Disable Detection done for ADC operation */ + WCD_MBHC_REG_READ(WCD_MBHC_DETECTION_DONE, det); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_DETECTION_DONE, 0); + + /* Mask ADC COMPLETE interrupt */ + wcd_mbhc_hs_elec_irq(mbhc, WCD_MBHC_ELEC_HS_INS, false); + + WCD_MBHC_REG_READ(WCD_MBHC_FSM_EN, fsm_en); + mbhc->mbhc_cb->mbhc_micbias_control(mbhc->codec, + mbhc->mbhc_cfg->anc_micbias, + MICB_ENABLE); + + /* Read legacy electircal detection and disable */ + WCD_MBHC_REG_READ(WCD_MBHC_ELECT_SCHMT_ISRC, elect_ctl); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_SCHMT_ISRC, 0x00); + + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ANC_DET_EN, 1); + WCD_MBHC_REG_READ(WCD_MBHC_ADC_MODE, adc_mode); + + /* + * wait for button debounce time 20ms. If 4-pole plug is inserted + * into 5-pole jack, then there will be a button press interrupt + * during anc plug detection. In that case though Hs_comp_res is 0, + * it should not be declared as ANC plug type + */ + usleep_range(20000, 20100); + + /* + * After enabling FSM, to handle slow insertion scenarios, + * check IN3 voltage is below the Vref + */ + WCD_MBHC_REG_READ(WCD_MBHC_HS_VREF, vref); + + do { + if (wcd_swch_level_remove(mbhc)) { + pr_debug("%s: Switch level is low\n", __func__); + goto done; + } + pr_debug("%s: Retry attempt %lu\n", __func__, retry + 1); + ret = wcd_measure_adc_once(mbhc, MUX_CTL_IN3P); + /* TODO - check the logic */ + if (ret && (ret < vref_mv[vref])) + valid_plug_cnt++; + else + invalid_plug_cnt++; + retry++; + } while (retry < ANC_DETECT_RETRY_CNT); + + pr_debug("%s: valid: %d, invalid: %d\n", __func__, valid_plug_cnt, + invalid_plug_cnt); + + /* decision logic */ + if (valid_plug_cnt > invalid_plug_cnt) + anc_mic_found = true; +done: + /* Restore ADC mode */ + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ADC_MODE, adc_mode); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ANC_DET_EN, 0); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 0); + /* Set the MUX selection to AUTO */ + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_MUX_CTL, MUX_CTL_AUTO); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 1); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, fsm_en); + /* Restore detection done */ + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_DETECTION_DONE, det); + + /* Restore electrical detection */ + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_SCHMT_ISRC, elect_ctl); + + mbhc->mbhc_cb->mbhc_micbias_control(mbhc->codec, + mbhc->mbhc_cfg->anc_micbias, + MICB_DISABLE); + pr_debug("%s: anc mic %sfound\n", __func__, + anc_mic_found ? "" : "not "); + + return anc_mic_found; +} + +/* To determine if cross connection occurred */ +static int wcd_check_cross_conn(struct wcd_mbhc *mbhc) +{ + enum wcd_mbhc_plug_type plug_type = MBHC_PLUG_TYPE_NONE; + int hphl_adc_res = 0, hphr_adc_res = 0; + u8 fsm_en = 0; + int ret = 0; + u8 adc_mode = 0; + u8 elect_ctl = 0; + u8 adc_en = 0; + + pr_debug("%s: enter\n", __func__); + /* Check for button press and plug detection */ + if (wcd_swch_level_remove(mbhc)) { + pr_debug("%s: Switch level is low\n", __func__); + return -EINVAL; + } + + /* If PA is enabled, dont check for cross-connection */ + if (mbhc->mbhc_cb->hph_pa_on_status) + if (mbhc->mbhc_cb->hph_pa_on_status(mbhc->codec)) + return -EINVAL; + + /* Read legacy electircal detection and disable */ + WCD_MBHC_REG_READ(WCD_MBHC_ELECT_SCHMT_ISRC, elect_ctl); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_SCHMT_ISRC, 0x00); + + /* Read and set ADC to single measurement */ + WCD_MBHC_REG_READ(WCD_MBHC_ADC_MODE, adc_mode); + /* Read ADC Enable bit to restore after adc measurement */ + WCD_MBHC_REG_READ(WCD_MBHC_ADC_EN, adc_en); + /* Read FSM status */ + WCD_MBHC_REG_READ(WCD_MBHC_FSM_EN, fsm_en); + + /* Get adc result for HPH L */ + hphl_adc_res = wcd_measure_adc_once(mbhc, MUX_CTL_HPH_L); + if (hphl_adc_res < 0) { + pr_err("%s: hphl_adc_res adc measurement failed\n", __func__); + ret = hphl_adc_res; + goto done; + } + + /* Get adc result for HPH R in mV */ + hphr_adc_res = wcd_measure_adc_once(mbhc, MUX_CTL_HPH_R); + if (hphr_adc_res < 0) { + pr_err("%s: hphr_adc_res adc measurement failed\n", __func__); + ret = hphr_adc_res; + goto done; + } + + if (hphl_adc_res > 100 || hphr_adc_res > 100) { + plug_type = MBHC_PLUG_TYPE_GND_MIC_SWAP; + pr_debug("%s: Cross connection identified\n", __func__); + } else { + pr_debug("%s: No Cross connection found\n", __func__); + } + +done: + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 0); + /* Set the MUX selection to Auto */ + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_MUX_CTL, MUX_CTL_AUTO); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 1); + + /* Restore ADC Enable */ + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ADC_EN, adc_en); + + /* Restore ADC mode */ + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ADC_MODE, adc_mode); + + /* Restore FSM state */ + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, fsm_en); + + /* Restore electrical detection */ + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_SCHMT_ISRC, elect_ctl); + + pr_debug("%s: leave, plug type: %d\n", __func__, plug_type); + + return (plug_type == MBHC_PLUG_TYPE_GND_MIC_SWAP) ? true : false; +} + +static bool wcd_mbhc_adc_check_for_spl_headset(struct wcd_mbhc *mbhc, + int *spl_hs_cnt) +{ + bool spl_hs = false; + int output_mv = 0; + int adc_threshold = 0, adc_hph_threshold = 0; + struct snd_soc_codec *codec = mbhc->codec; + struct wcd9xxx_pdata *pdata = dev_get_platdata(codec->dev->parent); + + pr_debug("%s: enter\n", __func__); + if (!mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic) + goto exit; + + /* Bump up MB2 to 2.7V */ + mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic(mbhc->codec, + mbhc->mbhc_cfg->mbhc_micbias, true); + usleep_range(10000, 10100); + + /* + * Use ADC single mode to minimize the chance of missing out + * btn press/relesae for HEADSET type during correct work. + */ + output_mv = wcd_measure_adc_once(mbhc, MUX_CTL_IN2P); + + if (mbhc->hs_thr && + (pdata->micbias.micb2_mv != WCD_MBHC_ADC_MICBIAS_MV)) + adc_threshold = mbhc->hs_thr; + else + adc_threshold = ((WCD_MBHC_ADC_HS_THRESHOLD_MV * + wcd_mbhc_get_micbias(mbhc))/WCD_MBHC_ADC_MICBIAS_MV); + + if (mbhc->hph_thr) + adc_hph_threshold = mbhc->hph_thr; + else + adc_hph_threshold = ((WCD_MBHC_ADC_HPH_THRESHOLD_MV * + wcd_mbhc_get_micbias(mbhc))/ + WCD_MBHC_ADC_MICBIAS_MV); + + if (output_mv > adc_threshold || output_mv < adc_hph_threshold) { + spl_hs = false; + } else { + spl_hs = true; + if (spl_hs_cnt) + *spl_hs_cnt += 1; + } + + /* MB2 back to 1.8v if the type is not special headset */ + if (spl_hs_cnt && (*spl_hs_cnt != WCD_MBHC_SPL_HS_CNT)) { + mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic(mbhc->codec, + mbhc->mbhc_cfg->mbhc_micbias, false); + /* Add 10ms delay for micbias to settle */ + usleep_range(10000, 10100); + } + + if (spl_hs) + pr_debug("%s: Detected special HS (%d)\n", __func__, spl_hs); + +exit: + pr_debug("%s: leave\n", __func__); + return spl_hs; +} + +static bool wcd_is_special_headset(struct wcd_mbhc *mbhc) +{ + int delay = 0; + bool ret = false; + bool is_spl_hs = false; + int output_mv = 0; + int adc_threshold = 0; + struct snd_soc_codec *codec = mbhc->codec; + struct wcd9xxx_pdata *pdata = dev_get_platdata(codec->dev->parent); + + /* + * Increase micbias to 2.7V to detect headsets with + * threshold on microphone + */ + if (mbhc->mbhc_cb->mbhc_micbias_control && + !mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic) { + pr_debug("%s: callback fn micb_ctrl_thr_mic not defined\n", + __func__); + return false; + } else if (mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic) { + ret = mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic(mbhc->codec, + MIC_BIAS_2, true); + if (ret) { + pr_err("%s: mbhc_micb_ctrl_thr_mic failed, ret: %d\n", + __func__, ret); + return false; + } + } + if (mbhc->hs_thr && + (pdata->micbias.micb2_mv != WCD_MBHC_ADC_MICBIAS_MV)) + adc_threshold = mbhc->hs_thr; + else + adc_threshold = ((WCD_MBHC_ADC_HS_THRESHOLD_MV * + wcd_mbhc_get_micbias(mbhc)) / + WCD_MBHC_ADC_MICBIAS_MV); + + while (!is_spl_hs) { + if (mbhc->hs_detect_work_stop) { + pr_debug("%s: stop requested: %d\n", __func__, + mbhc->hs_detect_work_stop); + break; + } + delay += 50; + /* Wait for 50ms for FSM to update result */ + msleep(50); + output_mv = wcd_measure_adc_once(mbhc, MUX_CTL_IN2P); + if (output_mv <= adc_threshold) { + pr_debug("%s: Special headset detected in %d msecs\n", + __func__, delay); + is_spl_hs = true; + } + + if (delay == SPECIAL_HS_DETECT_TIME_MS) { + pr_debug("%s: Spl headset not found in 2 sec\n", + __func__); + break; + } + } + if (is_spl_hs) { + pr_debug("%s: Headset with threshold found\n", __func__); + mbhc->micbias_enable = true; + ret = true; + } + if (mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic && + !mbhc->micbias_enable) + mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic(mbhc->codec, MIC_BIAS_2, + false); + pr_debug("%s: leave, micb_enable: %d\n", __func__, + mbhc->micbias_enable); + + return ret; +} + +static void wcd_mbhc_adc_update_fsm_source(struct wcd_mbhc *mbhc, + enum wcd_mbhc_plug_type plug_type) +{ + bool micbias2; + + micbias2 = mbhc->mbhc_cb->micbias_enable_status(mbhc, + MIC_BIAS_2); + switch (plug_type) { + case MBHC_PLUG_TYPE_HEADPHONE: + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_BTN_ISRC_CTL, 3); + break; + case MBHC_PLUG_TYPE_HEADSET: + case MBHC_PLUG_TYPE_ANC_HEADPHONE: + if (!mbhc->is_hs_recording && !micbias2) + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_BTN_ISRC_CTL, 3); + break; + default: + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_BTN_ISRC_CTL, 0); + break; + + }; +} + +/* should be called under interrupt context that hold suspend */ +static void wcd_schedule_hs_detect_plug(struct wcd_mbhc *mbhc, + struct work_struct *work) +{ + pr_debug("%s: scheduling correct_swch_plug\n", __func__); + WCD_MBHC_RSC_ASSERT_LOCKED(mbhc); + mbhc->hs_detect_work_stop = false; + mbhc->mbhc_cb->lock_sleep(mbhc, true); + schedule_work(work); +} + +/* called under codec_resource_lock acquisition */ +static void wcd_cancel_hs_detect_plug(struct wcd_mbhc *mbhc, + struct work_struct *work) +{ + pr_debug("%s: Canceling correct_plug_swch\n", __func__); + mbhc->hs_detect_work_stop = true; + WCD_MBHC_RSC_UNLOCK(mbhc); + if (cancel_work_sync(work)) { + pr_debug("%s: correct_plug_swch is canceled\n", + __func__); + mbhc->mbhc_cb->lock_sleep(mbhc, false); + } + WCD_MBHC_RSC_LOCK(mbhc); +} + +/* called under codec_resource_lock acquisition */ +static void wcd_mbhc_adc_detect_plug_type(struct wcd_mbhc *mbhc) +{ + struct snd_soc_codec *codec = mbhc->codec; + + pr_debug("%s: enter\n", __func__); + WCD_MBHC_RSC_ASSERT_LOCKED(mbhc); + + if (mbhc->mbhc_cb->hph_pull_down_ctrl) + mbhc->mbhc_cb->hph_pull_down_ctrl(codec, false); + + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_DETECTION_DONE, 0); + + if (mbhc->mbhc_cb->mbhc_micbias_control) { + mbhc->mbhc_cb->mbhc_micbias_control(codec, MIC_BIAS_2, + MICB_ENABLE); + } else { + pr_err("%s: Mic Bias is not enabled\n", __func__); + return; + } + + /* Re-initialize button press completion object */ + reinit_completion(&mbhc->btn_press_compl); + wcd_schedule_hs_detect_plug(mbhc, &mbhc->correct_plug_swch); + pr_debug("%s: leave\n", __func__); +} + +static void wcd_micbias_disable(struct wcd_mbhc *mbhc) +{ + if (mbhc->micbias_enable) { + mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic( + mbhc->codec, MIC_BIAS_2, false); + if (mbhc->mbhc_cb->set_micbias_value) + mbhc->mbhc_cb->set_micbias_value( + mbhc->codec); + mbhc->micbias_enable = false; + } +} + +static int wcd_mbhc_get_plug_from_adc(struct wcd_mbhc *mbhc, int adc_result) + +{ + enum wcd_mbhc_plug_type plug_type = MBHC_PLUG_TYPE_INVALID; + u32 hph_thr = 0, hs_thr = 0; + + if (mbhc->hs_thr) + hs_thr = mbhc->hs_thr; + else + hs_thr = WCD_MBHC_ADC_HS_THRESHOLD_MV; + + if (mbhc->hph_thr) + hph_thr = mbhc->hph_thr; + else + hph_thr = WCD_MBHC_ADC_HPH_THRESHOLD_MV; + + if (adc_result < hph_thr) + plug_type = MBHC_PLUG_TYPE_HEADPHONE; + else if (adc_result > hs_thr) + plug_type = MBHC_PLUG_TYPE_HIGH_HPH; + else + plug_type = MBHC_PLUG_TYPE_HEADSET; + pr_debug("%s: plug type is %d found\n", __func__, plug_type); + + return plug_type; +} + +static void wcd_correct_swch_plug(struct work_struct *work) +{ + struct wcd_mbhc *mbhc; + struct snd_soc_codec *codec; + enum wcd_mbhc_plug_type plug_type = MBHC_PLUG_TYPE_INVALID; + unsigned long timeout; + bool wrk_complete = false; + int pt_gnd_mic_swap_cnt = 0; + int no_gnd_mic_swap_cnt = 0; + bool is_pa_on = false, spl_hs = false, spl_hs_reported = false; + int ret = 0; + int spl_hs_count = 0; + int output_mv = 0; + int cross_conn; + int try = 0; + + pr_debug("%s: enter\n", __func__); + + mbhc = container_of(work, struct wcd_mbhc, correct_plug_swch); + codec = mbhc->codec; + + WCD_MBHC_RSC_LOCK(mbhc); + /* Mask ADC COMPLETE interrupt */ + wcd_mbhc_hs_elec_irq(mbhc, WCD_MBHC_ELEC_HS_INS, false); + WCD_MBHC_RSC_UNLOCK(mbhc); + + /* Check for cross connection */ + do { + cross_conn = wcd_check_cross_conn(mbhc); + try++; + } while (try < mbhc->swap_thr); + + if (cross_conn > 0) { + plug_type = MBHC_PLUG_TYPE_GND_MIC_SWAP; + pr_debug("%s: cross connection found, Plug type %d\n", + __func__, plug_type); + goto correct_plug_type; + } + /* Find plug type */ + output_mv = wcd_measure_adc_continuous(mbhc); + plug_type = wcd_mbhc_get_plug_from_adc(mbhc, output_mv); + + /* + * Report plug type if it is either headset or headphone + * else start the 3 sec loop + */ + if ((plug_type == MBHC_PLUG_TYPE_HEADSET || + plug_type == MBHC_PLUG_TYPE_HEADPHONE) && + (!wcd_swch_level_remove(mbhc))) { + WCD_MBHC_RSC_LOCK(mbhc); + wcd_mbhc_find_plug_and_report(mbhc, plug_type); + WCD_MBHC_RSC_UNLOCK(mbhc); + } + + /* + * Set DETECTION_DONE bit for HEADSET and ANC_HEADPHONE, + * so that btn press/release interrupt can be generated. + */ + if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADSET || + mbhc->current_plug == MBHC_PLUG_TYPE_ANC_HEADPHONE) { + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ADC_MODE, 0); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ADC_EN, 0); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_DETECTION_DONE, 1); + } + +correct_plug_type: + timeout = jiffies + msecs_to_jiffies(HS_DETECT_PLUG_TIME_MS); + while (!time_after(jiffies, timeout)) { + if (mbhc->hs_detect_work_stop) { + pr_debug("%s: stop requested: %d\n", __func__, + mbhc->hs_detect_work_stop); + wcd_micbias_disable(mbhc); + goto exit; + } + + /* allow sometime and re-check stop requested again */ + msleep(20); + if (mbhc->hs_detect_work_stop) { + pr_debug("%s: stop requested: %d\n", __func__, + mbhc->hs_detect_work_stop); + wcd_micbias_disable(mbhc); + goto exit; + } + + msleep(180); + /* + * Use ADC single mode to minimize the chance of missing out + * btn press/release for HEADSET type during correct work. + */ + output_mv = wcd_measure_adc_once(mbhc, MUX_CTL_IN2P); + + /* + * instead of hogging system by contineous polling, wait for + * sometime and re-check stop request again. + */ + plug_type = wcd_mbhc_get_plug_from_adc(mbhc, output_mv); + + if ((output_mv > WCD_MBHC_ADC_HS_THRESHOLD_MV) && + (spl_hs_count < WCD_MBHC_SPL_HS_CNT)) { + spl_hs = wcd_mbhc_adc_check_for_spl_headset(mbhc, + &spl_hs_count); + + if (spl_hs_count == WCD_MBHC_SPL_HS_CNT) { + output_mv = WCD_MBHC_ADC_HS_THRESHOLD_MV; + spl_hs = true; + mbhc->micbias_enable = true; + } + } + + if (mbhc->mbhc_cb->hph_pa_on_status) + is_pa_on = mbhc->mbhc_cb->hph_pa_on_status(mbhc->codec); + + if ((output_mv <= WCD_MBHC_ADC_HS_THRESHOLD_MV) && + (!is_pa_on)) { + /* Check for cross connection*/ + ret = wcd_check_cross_conn(mbhc); + if (ret < 0) + continue; + else if (ret > 0) { + pt_gnd_mic_swap_cnt++; + no_gnd_mic_swap_cnt = 0; + if (pt_gnd_mic_swap_cnt < + mbhc->swap_thr) { + continue; + } else if (pt_gnd_mic_swap_cnt > + mbhc->swap_thr) { + /* + * This is due to GND/MIC switch didn't + * work, Report unsupported plug. + */ + pr_debug("%s: switch did not work\n", + __func__); + plug_type = MBHC_PLUG_TYPE_GND_MIC_SWAP; + goto report; + } else { + plug_type = MBHC_PLUG_TYPE_GND_MIC_SWAP; + } + } else { + no_gnd_mic_swap_cnt++; + pt_gnd_mic_swap_cnt = 0; + plug_type = wcd_mbhc_get_plug_from_adc( + mbhc, output_mv); + if ((no_gnd_mic_swap_cnt < + mbhc->swap_thr) && + (spl_hs_count != WCD_MBHC_SPL_HS_CNT)) { + continue; + } else { + no_gnd_mic_swap_cnt = 0; + } + } + if ((pt_gnd_mic_swap_cnt == mbhc->swap_thr) && + (plug_type == MBHC_PLUG_TYPE_GND_MIC_SWAP)) { + /* + * if switch is toggled, check again, + * otherwise report unsupported plug + */ + if (mbhc->mbhc_cfg->swap_gnd_mic && + mbhc->mbhc_cfg->swap_gnd_mic(codec, + true)) { + pr_debug("%s: US_EU gpio present,flip switch\n" + , __func__); + continue; + } + } + } + + if (output_mv > WCD_MBHC_ADC_HS_THRESHOLD_MV) { + pr_debug("%s: cable is extension cable\n", __func__); + plug_type = MBHC_PLUG_TYPE_HIGH_HPH; + wrk_complete = true; + } else { + pr_debug("%s: cable might be headset: %d\n", __func__, + plug_type); + if (plug_type != MBHC_PLUG_TYPE_GND_MIC_SWAP) { + plug_type = wcd_mbhc_get_plug_from_adc( + mbhc, output_mv); + if (!spl_hs_reported && + spl_hs_count == WCD_MBHC_SPL_HS_CNT) { + spl_hs_reported = true; + WCD_MBHC_RSC_LOCK(mbhc); + wcd_mbhc_find_plug_and_report(mbhc, + plug_type); + WCD_MBHC_RSC_UNLOCK(mbhc); + continue; + } else if (spl_hs_reported) + continue; + /* + * Report headset only if not already reported + * and if there is not button press without + * release + */ + if ((mbhc->current_plug != + MBHC_PLUG_TYPE_HEADSET) && + (mbhc->current_plug != + MBHC_PLUG_TYPE_ANC_HEADPHONE) && + !wcd_swch_level_remove(mbhc)) { + pr_debug("%s: cable is %s headset\n", + __func__, + ((spl_hs_count == + WCD_MBHC_SPL_HS_CNT) ? + "special ":"")); + goto report; + } + } + wrk_complete = false; + } + } + if (!wrk_complete) { + /* + * If plug_tye is headset, we might have already reported either + * in detect_plug-type or in above while loop, no need to report + * again + */ + if ((plug_type == MBHC_PLUG_TYPE_HEADSET) || + (plug_type == MBHC_PLUG_TYPE_ANC_HEADPHONE)) { + pr_debug("%s: plug_type:0x%x already reported\n", + __func__, mbhc->current_plug); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ADC_MODE, 0); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ADC_EN, 0); + goto enable_supply; + } + } + if (plug_type == MBHC_PLUG_TYPE_HIGH_HPH) { + if (wcd_is_special_headset(mbhc)) { + pr_debug("%s: Special headset found %d\n", + __func__, plug_type); + plug_type = MBHC_PLUG_TYPE_HEADSET; + } else { + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_ISRC_EN, 1); + } + } + +report: + if (wcd_swch_level_remove(mbhc)) { + pr_debug("%s: Switch level is low\n", __func__); + goto exit; + } + + pr_debug("%s: Valid plug found, plug type %d wrk_cmpt %d btn_intr %d\n", + __func__, plug_type, wrk_complete, + mbhc->btn_press_intr); + + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ADC_MODE, 0); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ADC_EN, 0); + + WCD_MBHC_RSC_LOCK(mbhc); + wcd_mbhc_find_plug_and_report(mbhc, plug_type); + WCD_MBHC_RSC_UNLOCK(mbhc); +enable_supply: + /* + * Set DETECTION_DONE bit for HEADSET and ANC_HEADPHONE, + * so that btn press/release interrupt can be generated. + * For other plug type, clear the bit. + */ + if (plug_type == MBHC_PLUG_TYPE_HEADSET || + plug_type == MBHC_PLUG_TYPE_ANC_HEADPHONE) + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_DETECTION_DONE, 1); + else + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_DETECTION_DONE, 0); + + if (mbhc->mbhc_cb->mbhc_micbias_control) + wcd_mbhc_adc_update_fsm_source(mbhc, plug_type); +exit: + if (mbhc->mbhc_cb->mbhc_micbias_control && + !mbhc->micbias_enable) + mbhc->mbhc_cb->mbhc_micbias_control(codec, MIC_BIAS_2, + MICB_DISABLE); + + /* + * If plug type is corrected from special headset to headphone, + * clear the micbias enable flag, set micbias back to 1.8V and + * disable micbias. + */ + if (plug_type == MBHC_PLUG_TYPE_HEADPHONE && + mbhc->micbias_enable) { + if (mbhc->mbhc_cb->mbhc_micbias_control) + mbhc->mbhc_cb->mbhc_micbias_control( + codec, MIC_BIAS_2, + MICB_DISABLE); + if (mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic) + mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic( + codec, + MIC_BIAS_2, false); + if (mbhc->mbhc_cb->set_micbias_value) { + mbhc->mbhc_cb->set_micbias_value(codec); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_MICB_CTRL, 0); + } + mbhc->micbias_enable = false; + } + + if (mbhc->mbhc_cfg->detect_extn_cable && + ((plug_type == MBHC_PLUG_TYPE_HEADPHONE) || + (plug_type == MBHC_PLUG_TYPE_HEADSET)) && + !mbhc->hs_detect_work_stop) { + WCD_MBHC_RSC_LOCK(mbhc); + wcd_mbhc_hs_elec_irq(mbhc, WCD_MBHC_ELEC_HS_REM, true); + WCD_MBHC_RSC_UNLOCK(mbhc); + } + + /* + * Enable ADC COMPLETE interrupt for HEADPHONE. + * Btn release may happen after the correct work, ADC COMPLETE + * interrupt needs to be captured to correct plug type. + */ + if (plug_type == MBHC_PLUG_TYPE_HEADPHONE) { + WCD_MBHC_RSC_LOCK(mbhc); + wcd_mbhc_hs_elec_irq(mbhc, WCD_MBHC_ELEC_HS_INS, + true); + WCD_MBHC_RSC_UNLOCK(mbhc); + } + + if (mbhc->mbhc_cb->hph_pull_down_ctrl) + mbhc->mbhc_cb->hph_pull_down_ctrl(codec, true); + + mbhc->mbhc_cb->lock_sleep(mbhc, false); + pr_debug("%s: leave\n", __func__); +} + +static irqreturn_t wcd_mbhc_adc_hs_rem_irq(int irq, void *data) +{ + struct wcd_mbhc *mbhc = data; + unsigned long timeout; + int adc_threshold, output_mv, retry = 0; + bool hphpa_on = false; + u8 moisture_status = 0; + + pr_debug("%s: enter\n", __func__); + WCD_MBHC_RSC_LOCK(mbhc); + + timeout = jiffies + + msecs_to_jiffies(WCD_FAKE_REMOVAL_MIN_PERIOD_MS); + adc_threshold = ((WCD_MBHC_ADC_HS_THRESHOLD_MV * + wcd_mbhc_get_micbias(mbhc)) / + WCD_MBHC_ADC_MICBIAS_MV); + do { + retry++; + /* + * read output_mv every 10ms to look for + * any change in IN2_P + */ + usleep_range(10000, 10100); + output_mv = wcd_measure_adc_once(mbhc, MUX_CTL_IN2P); + + pr_debug("%s: Check for fake removal: output_mv %d\n", + __func__, output_mv); + if ((output_mv <= adc_threshold) && + retry > FAKE_REM_RETRY_ATTEMPTS) { + pr_debug("%s: headset is NOT actually removed\n", + __func__); + goto exit; + } + } while (!time_after(jiffies, timeout)); + + if (wcd_swch_level_remove(mbhc)) { + pr_debug("%s: Switch level is low ", __func__); + goto exit; + } + + if (mbhc->mbhc_cfg->moisture_en) { + if (mbhc->mbhc_cb->hph_pa_on_status) + if (mbhc->mbhc_cb->hph_pa_on_status(mbhc->codec)) { + hphpa_on = true; + WCD_MBHC_REG_UPDATE_BITS( + WCD_MBHC_HPHL_PA_EN, 0); + WCD_MBHC_REG_UPDATE_BITS( + WCD_MBHC_HPH_PA_EN, 0); + } + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HPHR_GND, 1); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HPHL_GND, 1); + /* wait for 50ms to get moisture status */ + usleep_range(50000, 50100); + + WCD_MBHC_REG_READ(WCD_MBHC_MOISTURE_STATUS, moisture_status); + } + + if (mbhc->mbhc_cfg->moisture_en && !moisture_status) { + pr_debug("%s: moisture present in jack\n", __func__); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_L_DET_EN, 0); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_MECH_DETECTION_TYPE, 1); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_L_DET_EN, 1); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 0); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_BTN_ISRC_CTL, 0); + mbhc->btn_press_intr = false; + mbhc->is_btn_press = false; + if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADSET) + wcd_mbhc_report_plug(mbhc, 0, SND_JACK_HEADSET); + else if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADPHONE) + wcd_mbhc_report_plug(mbhc, 0, SND_JACK_HEADPHONE); + else if (mbhc->current_plug == MBHC_PLUG_TYPE_GND_MIC_SWAP) + wcd_mbhc_report_plug(mbhc, 0, SND_JACK_UNSUPPORTED); + else if (mbhc->current_plug == MBHC_PLUG_TYPE_HIGH_HPH) + wcd_mbhc_report_plug(mbhc, 0, SND_JACK_LINEOUT); + } else { + /* + * ADC COMPLETE and ELEC_REM interrupts are both enabled for + * HEADPHONE, need to reject the ADC COMPLETE interrupt which + * follows ELEC_REM one when HEADPHONE is removed. + */ + if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADPHONE) + mbhc->extn_cable_hph_rem = true; + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_DETECTION_DONE, 0); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ADC_MODE, 0); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ADC_EN, 0); + wcd_mbhc_elec_hs_report_unplug(mbhc); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_BTN_ISRC_CTL, 0); + + if (hphpa_on) { + hphpa_on = false; + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HPHL_PA_EN, 1); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HPH_PA_EN, 1); + } + } +exit: + WCD_MBHC_RSC_UNLOCK(mbhc); + pr_debug("%s: leave\n", __func__); + return IRQ_HANDLED; +} + +static irqreturn_t wcd_mbhc_adc_hs_ins_irq(int irq, void *data) +{ + struct wcd_mbhc *mbhc = data; + u8 clamp_state = 0; + u8 clamp_retry = WCD_MBHC_FAKE_INS_RETRY; + + pr_debug("%s: enter\n", __func__); + + /* + * ADC COMPLETE and ELEC_REM interrupts are both enabled for HEADPHONE, + * need to reject the ADC COMPLETE interrupt which follows ELEC_REM one + * when HEADPHONE is removed. + */ + if (mbhc->extn_cable_hph_rem == true) { + mbhc->extn_cable_hph_rem = false; + pr_debug("%s: leave\n", __func__); + return IRQ_HANDLED; + } + + do { + WCD_MBHC_REG_READ(WCD_MBHC_IN2P_CLAMP_STATE, clamp_state); + if (clamp_state) { + pr_debug("%s: fake insertion irq, leave\n", __func__); + return IRQ_HANDLED; + } + /* + * check clamp for 120ms but at 30ms chunks to leave + * room for other interrupts to be processed + */ + usleep_range(30000, 30100); + } while (--clamp_retry); + + WCD_MBHC_RSC_LOCK(mbhc); + /* + * If current plug is headphone then there is no chance to + * get ADC complete interrupt, so connected cable should be + * headset not headphone. + */ + if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADPHONE) { + wcd_mbhc_hs_elec_irq(mbhc, WCD_MBHC_ELEC_HS_INS, false); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_DETECTION_DONE, 1); + wcd_mbhc_find_plug_and_report(mbhc, MBHC_PLUG_TYPE_HEADSET); + WCD_MBHC_RSC_UNLOCK(mbhc); + return IRQ_HANDLED; + } + + if (!mbhc->mbhc_cfg->detect_extn_cable) { + pr_debug("%s: Returning as Extension cable feature not enabled\n", + __func__); + WCD_MBHC_RSC_UNLOCK(mbhc); + return IRQ_HANDLED; + } + + pr_debug("%s: Disable electrical headset insertion interrupt\n", + __func__); + wcd_mbhc_hs_elec_irq(mbhc, WCD_MBHC_ELEC_HS_INS, false); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_SCHMT_ISRC, 0); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_ISRC_EN, 0); + mbhc->is_extn_cable = true; + mbhc->btn_press_intr = false; + wcd_mbhc_adc_detect_plug_type(mbhc); + WCD_MBHC_RSC_UNLOCK(mbhc); + pr_debug("%s: leave\n", __func__); + return IRQ_HANDLED; +} + +static struct wcd_mbhc_fn mbhc_fn = { + .wcd_mbhc_hs_ins_irq = wcd_mbhc_adc_hs_ins_irq, + .wcd_mbhc_hs_rem_irq = wcd_mbhc_adc_hs_rem_irq, + .wcd_mbhc_detect_plug_type = wcd_mbhc_adc_detect_plug_type, + .wcd_mbhc_detect_anc_plug_type = wcd_mbhc_adc_detect_anc_plug_type, + .wcd_cancel_hs_detect_plug = wcd_cancel_hs_detect_plug, +}; + +/* Function: wcd_mbhc_adc_init + * @mbhc: MBHC function pointer + * Description: Initialize MBHC ADC related function pointers to MBHC structure + */ +void wcd_mbhc_adc_init(struct wcd_mbhc *mbhc) +{ + if (!mbhc) { + pr_err("%s: mbhc is NULL\n", __func__); + return; + } + mbhc->mbhc_fn = &mbhc_fn; + INIT_WORK(&mbhc->correct_plug_swch, wcd_correct_swch_plug); +} +EXPORT_SYMBOL(wcd_mbhc_adc_init); diff --git a/audio-kernel/asoc/codecs/wcd-mbhc-adc.h b/audio-kernel/asoc/codecs/wcd-mbhc-adc.h new file mode 100644 index 000000000..31161089e --- /dev/null +++ b/audio-kernel/asoc/codecs/wcd-mbhc-adc.h @@ -0,0 +1,35 @@ +/* Copyright (c) 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 + * 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 __WCD_MBHC_ADC_H__ +#define __WCD_MBHC_ADC_H__ + +#include "wcd-mbhc-v2.h" + +enum wcd_mbhc_adc_mux_ctl { + MUX_CTL_AUTO = 0, + MUX_CTL_IN2P, + MUX_CTL_IN3P, + MUX_CTL_IN4P, + MUX_CTL_HPH_L, + MUX_CTL_HPH_R, + MUX_CTL_NONE, +}; + +#if IS_ENABLED(CONFIG_SND_SOC_WCD_MBHC_ADC) +void wcd_mbhc_adc_init(struct wcd_mbhc *mbhc); +#else +static inline void wcd_mbhc_adc_init(struct wcd_mbhc *mbhc) +{ + +} +#endif +#endif /* __WCD_MBHC_ADC_H__ */ diff --git a/audio-kernel/asoc/codecs/wcd-mbhc-legacy.c b/audio-kernel/asoc/codecs/wcd-mbhc-legacy.c new file mode 100644 index 000000000..9ebdeca01 --- /dev/null +++ b/audio-kernel/asoc/codecs/wcd-mbhc-legacy.c @@ -0,0 +1,1037 @@ +/* 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 + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "wcd-mbhc-legacy.h" +#include "wcd-mbhc-v2.h" + +static int det_extn_cable_en; +module_param(det_extn_cable_en, int, 0664); +MODULE_PARM_DESC(det_extn_cable_en, "enable/disable extn cable detect"); + +static bool wcd_mbhc_detect_anc_plug_type(struct wcd_mbhc *mbhc) +{ + bool anc_mic_found = false; + u16 val, hs_comp_res, btn_status = 0; + unsigned long retry = 0; + int valid_plug_cnt = 0, invalid_plug_cnt = 0; + int btn_status_cnt = 0; + bool is_check_btn_press = false; + + + if (mbhc->mbhc_cfg->anc_micbias < MIC_BIAS_1 || + mbhc->mbhc_cfg->anc_micbias > MIC_BIAS_4) + return false; + + if (!mbhc->mbhc_cb->mbhc_micbias_control) + return false; + + WCD_MBHC_REG_READ(WCD_MBHC_FSM_EN, val); + + if (val) + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 0); + + mbhc->mbhc_cb->mbhc_micbias_control(mbhc->codec, + mbhc->mbhc_cfg->anc_micbias, + MICB_ENABLE); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_MUX_CTL, 0x2); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ANC_DET_EN, 1); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 1); + /* + * wait for button debounce time 20ms. If 4-pole plug is inserted + * into 5-pole jack, then there will be a button press interrupt + * during anc plug detection. In that case though Hs_comp_res is 0, + * it should not be declared as ANC plug type + */ + usleep_range(20000, 20100); + + /* + * After enabling FSM, to handle slow insertion scenarios, + * check hs_comp_result for few times to see if the IN3 voltage + * is below the Vref + */ + do { + if (wcd_swch_level_remove(mbhc)) { + pr_debug("%s: Switch level is low\n", __func__); + goto exit; + } + pr_debug("%s: Retry attempt %lu\n", __func__, retry + 1); + WCD_MBHC_REG_READ(WCD_MBHC_HS_COMP_RESULT, hs_comp_res); + + if (!hs_comp_res) { + valid_plug_cnt++; + is_check_btn_press = true; + } else + invalid_plug_cnt++; + /* Wait 1ms before taking another reading */ + usleep_range(1000, 1100); + + WCD_MBHC_REG_READ(WCD_MBHC_FSM_STATUS, btn_status); + if (btn_status) + btn_status_cnt++; + + retry++; + } while (retry < ANC_DETECT_RETRY_CNT); + + pr_debug("%s: valid: %d, invalid: %d, btn_status_cnt: %d\n", + __func__, valid_plug_cnt, invalid_plug_cnt, btn_status_cnt); + + /* decision logic */ + if ((valid_plug_cnt > invalid_plug_cnt) && is_check_btn_press && + (btn_status_cnt == 0)) + anc_mic_found = true; +exit: + if (!val) + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 0); + + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ANC_DET_EN, 0); + + mbhc->mbhc_cb->mbhc_micbias_control(mbhc->codec, + mbhc->mbhc_cfg->anc_micbias, + MICB_DISABLE); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_MUX_CTL, 0x0); + pr_debug("%s: anc mic %sfound\n", __func__, + anc_mic_found ? "" : "not "); + return anc_mic_found; +} + +/* To determine if cross connection occurred */ +static int wcd_check_cross_conn(struct wcd_mbhc *mbhc) +{ + u16 swap_res = 0; + enum wcd_mbhc_plug_type plug_type = MBHC_PLUG_TYPE_NONE; + s16 reg1 = 0; + bool hphl_sch_res = 0, hphr_sch_res = 0; + + if (wcd_swch_level_remove(mbhc)) { + pr_debug("%s: Switch level is low\n", __func__); + return -EINVAL; + } + + /* If PA is enabled, dont check for cross-connection */ + if (mbhc->mbhc_cb->hph_pa_on_status) + if (mbhc->mbhc_cb->hph_pa_on_status(mbhc->codec)) + return false; + + WCD_MBHC_REG_READ(WCD_MBHC_ELECT_SCHMT_ISRC, reg1); + /* + * Check if there is any cross connection, + * Micbias and schmitt trigger (HPHL-HPHR) + * needs to be enabled. For some codecs like wcd9335, + * pull-up will already be enabled when this function + * is called for cross-connection identification. No + * need to enable micbias in that case. + */ + wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_MB); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_SCHMT_ISRC, 2); + + WCD_MBHC_REG_READ(WCD_MBHC_ELECT_RESULT, swap_res); + pr_debug("%s: swap_res%x\n", __func__, swap_res); + + /* + * Read reg hphl and hphr schmitt result with cross connection + * bit. These bits will both be "0" in case of cross connection + * otherwise, they stay at 1 + */ + WCD_MBHC_REG_READ(WCD_MBHC_HPHL_SCHMT_RESULT, hphl_sch_res); + WCD_MBHC_REG_READ(WCD_MBHC_HPHR_SCHMT_RESULT, hphr_sch_res); + if (!(hphl_sch_res || hphr_sch_res)) { + plug_type = MBHC_PLUG_TYPE_GND_MIC_SWAP; + pr_debug("%s: Cross connection identified\n", __func__); + } else { + pr_debug("%s: No Cross connection found\n", __func__); + } + + /* Disable schmitt trigger and restore micbias */ + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_SCHMT_ISRC, reg1); + pr_debug("%s: leave, plug type: %d\n", __func__, plug_type); + + return (plug_type == MBHC_PLUG_TYPE_GND_MIC_SWAP) ? true : false; +} + +static bool wcd_is_special_headset(struct wcd_mbhc *mbhc) +{ + struct snd_soc_codec *codec = mbhc->codec; + int delay = 0, rc; + bool ret = false; + u16 hs_comp_res; + bool is_spl_hs = false; + + /* + * Increase micbias to 2.7V to detect headsets with + * threshold on microphone + */ + if (mbhc->mbhc_cb->mbhc_micbias_control && + !mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic) { + pr_debug("%s: callback fn micb_ctrl_thr_mic not defined\n", + __func__); + return false; + } else if (mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic) { + rc = mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic(codec, + MIC_BIAS_2, true); + if (rc) { + pr_err("%s: Micbias control for thr mic failed, rc: %d\n", + __func__, rc); + return false; + } + } + + wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_MB); + + pr_debug("%s: special headset, start register writes\n", __func__); + + WCD_MBHC_REG_READ(WCD_MBHC_HS_COMP_RESULT, hs_comp_res); + while (!is_spl_hs) { + if (mbhc->hs_detect_work_stop) { + pr_debug("%s: stop requested: %d\n", __func__, + mbhc->hs_detect_work_stop); + break; + } + delay = delay + 50; + if (mbhc->mbhc_cb->mbhc_common_micb_ctrl) { + mbhc->mbhc_cb->mbhc_common_micb_ctrl(codec, + MBHC_COMMON_MICB_PRECHARGE, + true); + mbhc->mbhc_cb->mbhc_common_micb_ctrl(codec, + MBHC_COMMON_MICB_SET_VAL, + true); + } + /* Wait for 50msec for MICBIAS to settle down */ + msleep(50); + if (mbhc->mbhc_cb->set_auto_zeroing) + mbhc->mbhc_cb->set_auto_zeroing(codec, true); + /* Wait for 50msec for FSM to update result values */ + msleep(50); + WCD_MBHC_REG_READ(WCD_MBHC_HS_COMP_RESULT, hs_comp_res); + if (!(hs_comp_res)) { + pr_debug("%s: Special headset detected in %d msecs\n", + __func__, (delay * 2)); + is_spl_hs = true; + } + if (delay == SPECIAL_HS_DETECT_TIME_MS) { + pr_debug("%s: Spl headset didn't get detect in 4 sec\n", + __func__); + break; + } + } + if (is_spl_hs) { + pr_debug("%s: Headset with threshold found\n", __func__); + mbhc->micbias_enable = true; + ret = true; + } + if (mbhc->mbhc_cb->mbhc_common_micb_ctrl) + mbhc->mbhc_cb->mbhc_common_micb_ctrl(codec, + MBHC_COMMON_MICB_PRECHARGE, + false); + if (mbhc->mbhc_cb->set_micbias_value && !mbhc->micbias_enable) + mbhc->mbhc_cb->set_micbias_value(codec); + if (mbhc->mbhc_cb->set_auto_zeroing) + mbhc->mbhc_cb->set_auto_zeroing(codec, false); + + if (mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic && + !mbhc->micbias_enable) + mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic(codec, MIC_BIAS_2, + false); + + pr_debug("%s: leave, micb_enable: %d\n", __func__, + mbhc->micbias_enable); + return ret; +} + +static void wcd_mbhc_update_fsm_source(struct wcd_mbhc *mbhc, + enum wcd_mbhc_plug_type plug_type) +{ + bool micbias2; + + micbias2 = mbhc->mbhc_cb->micbias_enable_status(mbhc, + MIC_BIAS_2); + switch (plug_type) { + case MBHC_PLUG_TYPE_HEADPHONE: + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_BTN_ISRC_CTL, 3); + break; + case MBHC_PLUG_TYPE_HEADSET: + case MBHC_PLUG_TYPE_ANC_HEADPHONE: + if (!mbhc->is_hs_recording && !micbias2) + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_BTN_ISRC_CTL, 3); + break; + default: + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_BTN_ISRC_CTL, 0); + break; + + }; +} + +static void wcd_enable_mbhc_supply(struct wcd_mbhc *mbhc, + enum wcd_mbhc_plug_type plug_type) +{ + + struct snd_soc_codec *codec = mbhc->codec; + + /* + * Do not disable micbias if recording is going on or + * headset is inserted on the other side of the extn + * cable. If headset has been detected current source + * needs to be kept enabled for button detection to work. + * If the accessory type is invalid or unsupported, we + * dont need to enable either of them. + */ + if (det_extn_cable_en && mbhc->is_extn_cable && + mbhc->mbhc_cb && mbhc->mbhc_cb->extn_use_mb && + mbhc->mbhc_cb->extn_use_mb(codec)) { + if (plug_type == MBHC_PLUG_TYPE_HEADPHONE || + plug_type == MBHC_PLUG_TYPE_HEADSET) + wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_MB); + } else { + if (plug_type == MBHC_PLUG_TYPE_HEADSET) { + if (mbhc->is_hs_recording || mbhc->micbias_enable) { + wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_MB); + } else if ((test_bit(WCD_MBHC_EVENT_PA_HPHL, + &mbhc->event_state)) || + (test_bit(WCD_MBHC_EVENT_PA_HPHR, + &mbhc->event_state))) { + wcd_enable_curr_micbias(mbhc, + WCD_MBHC_EN_PULLUP); + } else { + wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_CS); + } + } else if (plug_type == MBHC_PLUG_TYPE_HEADPHONE) { + wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_CS); + } else { + wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_NONE); + } + } +} + +static bool wcd_mbhc_check_for_spl_headset(struct wcd_mbhc *mbhc, + int *spl_hs_cnt) +{ + u16 hs_comp_res_1_8v = 0, hs_comp_res_2_7v = 0; + bool spl_hs = false; + + if (!mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic) + goto done; + + if (!spl_hs_cnt) { + pr_err("%s: spl_hs_cnt is NULL\n", __func__); + goto done; + } + /* Read back hs_comp_res @ 1.8v Micbias */ + WCD_MBHC_REG_READ(WCD_MBHC_HS_COMP_RESULT, hs_comp_res_1_8v); + if (!hs_comp_res_1_8v) { + spl_hs = false; + goto done; + } + + /* Bump up MB2 to 2.7v */ + mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic(mbhc->codec, + mbhc->mbhc_cfg->mbhc_micbias, true); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 0); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 1); + usleep_range(10000, 10100); + + /* Read back HS_COMP_RESULT */ + WCD_MBHC_REG_READ(WCD_MBHC_HS_COMP_RESULT, hs_comp_res_2_7v); + if (!hs_comp_res_2_7v && hs_comp_res_1_8v) + spl_hs = true; + + if (spl_hs) + *spl_hs_cnt += 1; + + /* MB2 back to 1.8v */ + if (*spl_hs_cnt != WCD_MBHC_SPL_HS_CNT) { + mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic(mbhc->codec, + mbhc->mbhc_cfg->mbhc_micbias, false); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 0); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 1); + usleep_range(10000, 10100); + } + + if (spl_hs) + pr_debug("%s: Detected special HS (%d)\n", __func__, spl_hs); + +done: + return spl_hs; +} + +/* should be called under interrupt context that hold suspend */ +static void wcd_schedule_hs_detect_plug(struct wcd_mbhc *mbhc, + struct work_struct *work) +{ + pr_debug("%s: scheduling correct_swch_plug\n", __func__); + WCD_MBHC_RSC_ASSERT_LOCKED(mbhc); + mbhc->hs_detect_work_stop = false; + mbhc->mbhc_cb->lock_sleep(mbhc, true); + schedule_work(work); +} + +/* called under codec_resource_lock acquisition */ +static void wcd_cancel_hs_detect_plug(struct wcd_mbhc *mbhc, + struct work_struct *work) +{ + pr_debug("%s: Canceling correct_plug_swch\n", __func__); + mbhc->hs_detect_work_stop = true; + WCD_MBHC_RSC_UNLOCK(mbhc); + if (cancel_work_sync(work)) { + pr_debug("%s: correct_plug_swch is canceled\n", + __func__); + mbhc->mbhc_cb->lock_sleep(mbhc, false); + } + WCD_MBHC_RSC_LOCK(mbhc); +} + +/* called under codec_resource_lock acquisition */ +static void wcd_mbhc_detect_plug_type(struct wcd_mbhc *mbhc) +{ + struct snd_soc_codec *codec = mbhc->codec; + bool micbias1 = false; + + pr_debug("%s: enter\n", __func__); + WCD_MBHC_RSC_ASSERT_LOCKED(mbhc); + + if (mbhc->mbhc_cb->hph_pull_down_ctrl) + mbhc->mbhc_cb->hph_pull_down_ctrl(codec, false); + + if (mbhc->mbhc_cb->micbias_enable_status) + micbias1 = mbhc->mbhc_cb->micbias_enable_status(mbhc, + MIC_BIAS_1); + + if (mbhc->mbhc_cb->set_cap_mode) + mbhc->mbhc_cb->set_cap_mode(codec, micbias1, true); + + if (mbhc->mbhc_cb->mbhc_micbias_control) + mbhc->mbhc_cb->mbhc_micbias_control(codec, MIC_BIAS_2, + MICB_ENABLE); + else + wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_MB); + + /* Re-initialize button press completion object */ + reinit_completion(&mbhc->btn_press_compl); + wcd_schedule_hs_detect_plug(mbhc, &mbhc->correct_plug_swch); + pr_debug("%s: leave\n", __func__); +} + +static void wcd_correct_swch_plug(struct work_struct *work) +{ + struct wcd_mbhc *mbhc; + struct snd_soc_codec *codec; + enum wcd_mbhc_plug_type plug_type = MBHC_PLUG_TYPE_INVALID; + unsigned long timeout; + u16 hs_comp_res = 0, hphl_sch = 0, mic_sch = 0, btn_result = 0; + bool wrk_complete = false; + int pt_gnd_mic_swap_cnt = 0; + int no_gnd_mic_swap_cnt = 0; + bool is_pa_on = false, spl_hs = false, spl_hs_reported = false; + bool micbias2 = false; + bool micbias1 = false; + int ret = 0; + int rc, spl_hs_count = 0; + int cross_conn; + int try = 0; + + pr_debug("%s: enter\n", __func__); + + mbhc = container_of(work, struct wcd_mbhc, correct_plug_swch); + codec = mbhc->codec; + + /* + * Enable micbias/pullup for detection in correct work. + * This work will get scheduled from detect_plug_type which + * will already request for pullup/micbias. If the pullup/micbias + * is handled with ref-counts by individual codec drivers, there is + * no need to enabale micbias/pullup here + */ + + wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_MB); + + /* Enable HW FSM */ + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 1); + /* + * Check for any button press interrupts before starting 3-sec + * loop. + */ + rc = wait_for_completion_timeout(&mbhc->btn_press_compl, + msecs_to_jiffies(WCD_MBHC_BTN_PRESS_COMPL_TIMEOUT_MS)); + + WCD_MBHC_REG_READ(WCD_MBHC_BTN_RESULT, btn_result); + WCD_MBHC_REG_READ(WCD_MBHC_HS_COMP_RESULT, hs_comp_res); + + if (!rc) { + pr_debug("%s No btn press interrupt\n", __func__); + if (!btn_result && !hs_comp_res) + plug_type = MBHC_PLUG_TYPE_HEADSET; + else if (!btn_result && hs_comp_res) + plug_type = MBHC_PLUG_TYPE_HIGH_HPH; + else + plug_type = MBHC_PLUG_TYPE_INVALID; + } else { + if (!btn_result && !hs_comp_res) + plug_type = MBHC_PLUG_TYPE_HEADPHONE; + else + plug_type = MBHC_PLUG_TYPE_INVALID; + } + + do { + cross_conn = wcd_check_cross_conn(mbhc); + try++; + } while (try < mbhc->swap_thr); + + /* + * Check for cross connection 4 times. + * Consider the result of the fourth iteration. + */ + if (cross_conn > 0) { + pr_debug("%s: cross con found, start polling\n", + __func__); + plug_type = MBHC_PLUG_TYPE_GND_MIC_SWAP; + pr_debug("%s: Plug found, plug type is %d\n", + __func__, plug_type); + goto correct_plug_type; + } + + if ((plug_type == MBHC_PLUG_TYPE_HEADSET || + plug_type == MBHC_PLUG_TYPE_HEADPHONE) && + (!wcd_swch_level_remove(mbhc))) { + WCD_MBHC_RSC_LOCK(mbhc); + if (mbhc->current_plug == MBHC_PLUG_TYPE_HIGH_HPH) + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_DETECTION_TYPE, + 0); + wcd_mbhc_find_plug_and_report(mbhc, plug_type); + WCD_MBHC_RSC_UNLOCK(mbhc); + } + +correct_plug_type: + + timeout = jiffies + msecs_to_jiffies(HS_DETECT_PLUG_TIME_MS); + while (!time_after(jiffies, timeout)) { + if (mbhc->hs_detect_work_stop) { + pr_debug("%s: stop requested: %d\n", __func__, + mbhc->hs_detect_work_stop); + wcd_enable_curr_micbias(mbhc, + WCD_MBHC_EN_NONE); + if (mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic && + mbhc->micbias_enable) { + mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic( + mbhc->codec, MIC_BIAS_2, false); + if (mbhc->mbhc_cb->set_micbias_value) + mbhc->mbhc_cb->set_micbias_value( + mbhc->codec); + mbhc->micbias_enable = false; + } + goto exit; + } + if (mbhc->btn_press_intr) { + wcd_cancel_btn_work(mbhc); + mbhc->btn_press_intr = false; + } + /* Toggle FSM */ + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 0); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 1); + + /* allow sometime and re-check stop requested again */ + msleep(20); + if (mbhc->hs_detect_work_stop) { + pr_debug("%s: stop requested: %d\n", __func__, + mbhc->hs_detect_work_stop); + wcd_enable_curr_micbias(mbhc, + WCD_MBHC_EN_NONE); + if (mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic && + mbhc->micbias_enable) { + mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic( + mbhc->codec, MIC_BIAS_2, false); + if (mbhc->mbhc_cb->set_micbias_value) + mbhc->mbhc_cb->set_micbias_value( + mbhc->codec); + mbhc->micbias_enable = false; + } + goto exit; + } + WCD_MBHC_REG_READ(WCD_MBHC_HS_COMP_RESULT, hs_comp_res); + + pr_debug("%s: hs_comp_res: %x\n", __func__, hs_comp_res); + if (mbhc->mbhc_cb->hph_pa_on_status) + is_pa_on = mbhc->mbhc_cb->hph_pa_on_status(codec); + + /* + * instead of hogging system by contineous polling, wait for + * sometime and re-check stop request again. + */ + msleep(180); + if (hs_comp_res && (spl_hs_count < WCD_MBHC_SPL_HS_CNT)) { + spl_hs = wcd_mbhc_check_for_spl_headset(mbhc, + &spl_hs_count); + + if (spl_hs_count == WCD_MBHC_SPL_HS_CNT) { + hs_comp_res = 0; + spl_hs = true; + mbhc->micbias_enable = true; + } + } + + if ((!hs_comp_res) && (!is_pa_on)) { + /* Check for cross connection*/ + ret = wcd_check_cross_conn(mbhc); + if (ret < 0) { + continue; + } else if (ret > 0) { + pt_gnd_mic_swap_cnt++; + no_gnd_mic_swap_cnt = 0; + if (pt_gnd_mic_swap_cnt < + mbhc->swap_thr) { + continue; + } else if (pt_gnd_mic_swap_cnt > + mbhc->swap_thr) { + /* + * This is due to GND/MIC switch didn't + * work, Report unsupported plug. + */ + pr_debug("%s: switch didn't work\n", + __func__); + plug_type = MBHC_PLUG_TYPE_GND_MIC_SWAP; + goto report; + } else { + plug_type = MBHC_PLUG_TYPE_GND_MIC_SWAP; + } + } else { + no_gnd_mic_swap_cnt++; + pt_gnd_mic_swap_cnt = 0; + plug_type = MBHC_PLUG_TYPE_HEADSET; + if ((no_gnd_mic_swap_cnt < + GND_MIC_SWAP_THRESHOLD) && + (spl_hs_count != WCD_MBHC_SPL_HS_CNT)) { + continue; + } else { + no_gnd_mic_swap_cnt = 0; + } + } + if ((pt_gnd_mic_swap_cnt == mbhc->swap_thr) && + (plug_type == MBHC_PLUG_TYPE_GND_MIC_SWAP)) { + /* + * if switch is toggled, check again, + * otherwise report unsupported plug + */ + if (mbhc->mbhc_cfg->swap_gnd_mic && + mbhc->mbhc_cfg->swap_gnd_mic(codec, + true)) { + pr_debug("%s: US_EU gpio present,flip switch\n" + , __func__); + continue; + } + } + } + + WCD_MBHC_REG_READ(WCD_MBHC_HPHL_SCHMT_RESULT, hphl_sch); + WCD_MBHC_REG_READ(WCD_MBHC_MIC_SCHMT_RESULT, mic_sch); + if (hs_comp_res && !(hphl_sch || mic_sch)) { + pr_debug("%s: cable is extension cable\n", __func__); + plug_type = MBHC_PLUG_TYPE_HIGH_HPH; + wrk_complete = true; + } else { + pr_debug("%s: cable might be headset: %d\n", __func__, + plug_type); + if (!(plug_type == MBHC_PLUG_TYPE_GND_MIC_SWAP)) { + plug_type = MBHC_PLUG_TYPE_HEADSET; + if (!spl_hs_reported && + spl_hs_count == WCD_MBHC_SPL_HS_CNT) { + spl_hs_reported = true; + WCD_MBHC_RSC_LOCK(mbhc); + wcd_mbhc_find_plug_and_report(mbhc, + plug_type); + WCD_MBHC_RSC_UNLOCK(mbhc); + continue; + } else if (spl_hs_reported) + continue; + /* + * Report headset only if not already reported + * and if there is not button press without + * release + */ + if (((mbhc->current_plug != + MBHC_PLUG_TYPE_HEADSET) && + (mbhc->current_plug != + MBHC_PLUG_TYPE_ANC_HEADPHONE)) && + !wcd_swch_level_remove(mbhc) && + !mbhc->btn_press_intr) { + pr_debug("%s: cable is %sheadset\n", + __func__, + ((spl_hs_count == + WCD_MBHC_SPL_HS_CNT) ? + "special ":"")); + goto report; + } + } + wrk_complete = false; + } + } + if (!wrk_complete && mbhc->btn_press_intr) { + pr_debug("%s: Can be slow insertion of headphone\n", __func__); + wcd_cancel_btn_work(mbhc); + /* Report as headphone only if previously + * not reported as lineout + */ + if (!mbhc->force_linein) + plug_type = MBHC_PLUG_TYPE_HEADPHONE; + } + /* + * If plug_tye is headset, we might have already reported either in + * detect_plug-type or in above while loop, no need to report again + */ + if (!wrk_complete && ((plug_type == MBHC_PLUG_TYPE_HEADSET) || + (plug_type == MBHC_PLUG_TYPE_ANC_HEADPHONE))) { + pr_debug("%s: plug_type:0x%x already reported\n", + __func__, mbhc->current_plug); + goto enable_supply; + } + + if (plug_type == MBHC_PLUG_TYPE_HIGH_HPH && + (!det_extn_cable_en)) { + if (wcd_is_special_headset(mbhc)) { + pr_debug("%s: Special headset found %d\n", + __func__, plug_type); + plug_type = MBHC_PLUG_TYPE_HEADSET; + goto report; + } + } + +report: + if (wcd_swch_level_remove(mbhc)) { + pr_debug("%s: Switch level is low\n", __func__); + goto exit; + } + if (plug_type == MBHC_PLUG_TYPE_GND_MIC_SWAP && mbhc->btn_press_intr) { + pr_debug("%s: insertion of headphone with swap\n", __func__); + wcd_cancel_btn_work(mbhc); + plug_type = MBHC_PLUG_TYPE_HEADPHONE; + } + pr_debug("%s: Valid plug found, plug type %d wrk_cmpt %d btn_intr %d\n", + __func__, plug_type, wrk_complete, + mbhc->btn_press_intr); + WCD_MBHC_RSC_LOCK(mbhc); + wcd_mbhc_find_plug_and_report(mbhc, plug_type); + WCD_MBHC_RSC_UNLOCK(mbhc); +enable_supply: + if (mbhc->mbhc_cb->mbhc_micbias_control) + wcd_mbhc_update_fsm_source(mbhc, plug_type); + else + wcd_enable_mbhc_supply(mbhc, plug_type); +exit: + if (mbhc->mbhc_cb->mbhc_micbias_control && + !mbhc->micbias_enable) + mbhc->mbhc_cb->mbhc_micbias_control(codec, MIC_BIAS_2, + MICB_DISABLE); + + /* + * If plug type is corrected from special headset to headphone, + * clear the micbias enable flag, set micbias back to 1.8V and + * disable micbias. + */ + if (plug_type == MBHC_PLUG_TYPE_HEADPHONE && + mbhc->micbias_enable) { + if (mbhc->mbhc_cb->mbhc_micbias_control) + mbhc->mbhc_cb->mbhc_micbias_control( + codec, MIC_BIAS_2, + MICB_DISABLE); + if (mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic) + mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic( + codec, + MIC_BIAS_2, false); + if (mbhc->mbhc_cb->set_micbias_value) { + mbhc->mbhc_cb->set_micbias_value(codec); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_MICB_CTRL, 0); + } + mbhc->micbias_enable = false; + } + + if (mbhc->mbhc_cb->micbias_enable_status) { + micbias1 = mbhc->mbhc_cb->micbias_enable_status(mbhc, + MIC_BIAS_1); + micbias2 = mbhc->mbhc_cb->micbias_enable_status(mbhc, + MIC_BIAS_2); + } + + if (mbhc->mbhc_cfg->detect_extn_cable && + ((plug_type == MBHC_PLUG_TYPE_HEADPHONE) || + (plug_type == MBHC_PLUG_TYPE_HEADSET)) && + !mbhc->hs_detect_work_stop) { + WCD_MBHC_RSC_LOCK(mbhc); + wcd_mbhc_hs_elec_irq(mbhc, WCD_MBHC_ELEC_HS_REM, true); + WCD_MBHC_RSC_UNLOCK(mbhc); + } + if (mbhc->mbhc_cb->set_cap_mode) + mbhc->mbhc_cb->set_cap_mode(codec, micbias1, micbias2); + + if (mbhc->mbhc_cb->hph_pull_down_ctrl) + mbhc->mbhc_cb->hph_pull_down_ctrl(codec, true); + + mbhc->mbhc_cb->lock_sleep(mbhc, false); + pr_debug("%s: leave\n", __func__); +} + +static irqreturn_t wcd_mbhc_hs_rem_irq(int irq, void *data) +{ + struct wcd_mbhc *mbhc = data; + u8 hs_comp_result = 0, hphl_sch = 0, mic_sch = 0; + static u16 hphl_trigerred; + static u16 mic_trigerred; + unsigned long timeout; + bool removed = true; + int retry = 0; + bool hphpa_on = false; + u8 moisture_status = 0; + + pr_debug("%s: enter\n", __func__); + + WCD_MBHC_RSC_LOCK(mbhc); + + timeout = jiffies + + msecs_to_jiffies(WCD_FAKE_REMOVAL_MIN_PERIOD_MS); + do { + retry++; + /* + * read the result register every 10ms to look for + * any change in HS_COMP_RESULT bit + */ + usleep_range(10000, 10100); + WCD_MBHC_REG_READ(WCD_MBHC_HS_COMP_RESULT, hs_comp_result); + pr_debug("%s: Check result reg for fake removal: hs_comp_res %x\n", + __func__, hs_comp_result); + if ((!hs_comp_result) && + retry > FAKE_REM_RETRY_ATTEMPTS) { + removed = false; + break; + } + } while (!time_after(jiffies, timeout)); + + if (wcd_swch_level_remove(mbhc)) { + pr_debug("%s: Switch level is low ", __func__); + goto exit; + } + pr_debug("%s: headset %s actually removed\n", __func__, + removed ? "" : "not "); + + WCD_MBHC_REG_READ(WCD_MBHC_HPHL_SCHMT_RESULT, hphl_sch); + WCD_MBHC_REG_READ(WCD_MBHC_MIC_SCHMT_RESULT, mic_sch); + WCD_MBHC_REG_READ(WCD_MBHC_HS_COMP_RESULT, hs_comp_result); + + if (removed) { + if (mbhc->mbhc_cfg->moisture_en) { + if (mbhc->mbhc_cb->hph_pa_on_status) + if ( + mbhc->mbhc_cb->hph_pa_on_status(mbhc->codec)) { + hphpa_on = true; + WCD_MBHC_REG_UPDATE_BITS( + WCD_MBHC_HPHL_PA_EN, 0); + WCD_MBHC_REG_UPDATE_BITS( + WCD_MBHC_HPH_PA_EN, 0); + } + + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HPHR_GND, 1); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HPHL_GND, 1); + /* wait for 50ms to get moisture status */ + usleep_range(50000, 50100); + + WCD_MBHC_REG_READ( + WCD_MBHC_MOISTURE_STATUS, moisture_status); + } + + if (mbhc->mbhc_cfg->moisture_en && !moisture_status) { + pr_debug("%s: moisture present in jack\n", __func__); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_L_DET_EN, 0); + WCD_MBHC_REG_UPDATE_BITS( + WCD_MBHC_MECH_DETECTION_TYPE, 1); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_L_DET_EN, 1); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 0); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_BTN_ISRC_CTL, 0); + mbhc->btn_press_intr = false; + mbhc->is_btn_press = false; + if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADSET) + wcd_mbhc_report_plug( + mbhc, 0, SND_JACK_HEADSET); + else if (mbhc->current_plug == + MBHC_PLUG_TYPE_HEADPHONE) + wcd_mbhc_report_plug( + mbhc, 0, SND_JACK_HEADPHONE); + else if (mbhc->current_plug == + MBHC_PLUG_TYPE_GND_MIC_SWAP) + wcd_mbhc_report_plug( + mbhc, 0, SND_JACK_UNSUPPORTED); + else if (mbhc->current_plug == + MBHC_PLUG_TYPE_HIGH_HPH) + wcd_mbhc_report_plug( + mbhc, 0, SND_JACK_LINEOUT); + } else { + if (!(hphl_sch && mic_sch && hs_comp_result)) { + /* + * extension cable is still plugged in + * report it as LINEOUT device + */ + goto report_unplug; + } else { + if (!mic_sch) { + mic_trigerred++; + pr_debug( + "%s: Removal MIC trigerred %d\n", + __func__, mic_trigerred); + } + if (!hphl_sch) { + hphl_trigerred++; + pr_debug( + "%s: Removal HPHL trigerred %d\n", + __func__, hphl_trigerred); + } + if (mic_trigerred && hphl_trigerred) { + /* + * extension cable is still plugged in + * report it as LINEOUT device + */ + goto report_unplug; + } + } + } + } +exit: + WCD_MBHC_RSC_UNLOCK(mbhc); + pr_debug("%s: leave\n", __func__); + return IRQ_HANDLED; + +report_unplug: + wcd_mbhc_elec_hs_report_unplug(mbhc); + if (hphpa_on) { + hphpa_on = false; + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HPHL_PA_EN, 1); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HPH_PA_EN, 1); + } + hphl_trigerred = 0; + mic_trigerred = 0; + WCD_MBHC_RSC_UNLOCK(mbhc); + pr_debug("%s: leave\n", __func__); + return IRQ_HANDLED; +} + +static irqreturn_t wcd_mbhc_hs_ins_irq(int irq, void *data) +{ + struct wcd_mbhc *mbhc = data; + bool detection_type = 0, hphl_sch = 0, mic_sch = 0; + u16 elect_result = 0; + static u16 hphl_trigerred; + static u16 mic_trigerred; + + pr_debug("%s: enter\n", __func__); + if (!mbhc->mbhc_cfg->detect_extn_cable) { + pr_debug("%s: Returning as Extension cable feature not enabled\n", + __func__); + return IRQ_HANDLED; + } + WCD_MBHC_RSC_LOCK(mbhc); + + WCD_MBHC_REG_READ(WCD_MBHC_ELECT_DETECTION_TYPE, detection_type); + WCD_MBHC_REG_READ(WCD_MBHC_ELECT_RESULT, elect_result); + + pr_debug("%s: detection_type %d, elect_result %x\n", __func__, + detection_type, elect_result); + if (detection_type) { + /* check if both Left and MIC Schmitt triggers are triggered */ + WCD_MBHC_REG_READ(WCD_MBHC_HPHL_SCHMT_RESULT, hphl_sch); + WCD_MBHC_REG_READ(WCD_MBHC_MIC_SCHMT_RESULT, mic_sch); + if (hphl_sch && mic_sch) { + /* Go for plug type determination */ + pr_debug("%s: Go for plug type determination\n", + __func__); + goto determine_plug; + + } else { + if (mic_sch) { + mic_trigerred++; + pr_debug("%s: Insertion MIC trigerred %d\n", + __func__, mic_trigerred); + WCD_MBHC_REG_UPDATE_BITS( + WCD_MBHC_ELECT_SCHMT_ISRC, + 0); + msleep(20); + WCD_MBHC_REG_UPDATE_BITS( + WCD_MBHC_ELECT_SCHMT_ISRC, + 1); + } + if (hphl_sch) { + hphl_trigerred++; + pr_debug("%s: Insertion HPHL trigerred %d\n", + __func__, hphl_trigerred); + } + if (mic_trigerred && hphl_trigerred) { + /* Go for plug type determination */ + pr_debug("%s: Go for plug type determination\n", + __func__); + goto determine_plug; + } + } + } + WCD_MBHC_RSC_UNLOCK(mbhc); + pr_debug("%s: leave\n", __func__); + return IRQ_HANDLED; + +determine_plug: + /* + * Disable HPHL trigger and MIC Schmitt triggers. + * Setup for insertion detection. + */ + pr_debug("%s: Disable insertion interrupt\n", __func__); + wcd_mbhc_hs_elec_irq(mbhc, WCD_MBHC_ELEC_HS_INS, + false); + + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_SCHMT_ISRC, 0); + hphl_trigerred = 0; + mic_trigerred = 0; + mbhc->is_extn_cable = true; + mbhc->btn_press_intr = false; + wcd_mbhc_detect_plug_type(mbhc); + WCD_MBHC_RSC_UNLOCK(mbhc); + pr_debug("%s: leave\n", __func__); + return IRQ_HANDLED; +} + +static struct wcd_mbhc_fn mbhc_fn = { + .wcd_mbhc_hs_ins_irq = wcd_mbhc_hs_ins_irq, + .wcd_mbhc_hs_rem_irq = wcd_mbhc_hs_rem_irq, + .wcd_mbhc_detect_plug_type = wcd_mbhc_detect_plug_type, + .wcd_mbhc_detect_anc_plug_type = wcd_mbhc_detect_anc_plug_type, + .wcd_cancel_hs_detect_plug = wcd_cancel_hs_detect_plug, +}; + +/* Function: wcd_mbhc_legacy_init + * @mbhc: MBHC function pointer + * Description: Initialize MBHC legacy based function pointers to MBHC structure + */ +void wcd_mbhc_legacy_init(struct wcd_mbhc *mbhc) +{ + if (!mbhc) { + pr_err("%s: mbhc is NULL\n", __func__); + return; + } + mbhc->mbhc_fn = &mbhc_fn; + INIT_WORK(&mbhc->correct_plug_swch, wcd_correct_swch_plug); +} +EXPORT_SYMBOL(wcd_mbhc_legacy_init); diff --git a/audio-kernel/asoc/codecs/wcd-mbhc-legacy.h b/audio-kernel/asoc/codecs/wcd-mbhc-legacy.h new file mode 100644 index 000000000..af3a7dc6d --- /dev/null +++ b/audio-kernel/asoc/codecs/wcd-mbhc-legacy.h @@ -0,0 +1,26 @@ +/* Copyright (c) 2014-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 + * 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 __WCD_MBHC_LEGACY_H__ +#define __WCD_MBHC_LEGACY_H__ + +#include "wcdcal-hwdep.h" +#include "wcd-mbhc-v2.h" + +#if IS_ENABLED(CONFIG_SND_SOC_WCD_MBHC_LEGACY) +void wcd_mbhc_legacy_init(struct wcd_mbhc *mbhc); +#else +static inline void wcd_mbhc_legacy_init(struct wcd_mbhc *mbhc) +{ +} +#endif + +#endif /* __WCD_MBHC_LEGACY_H__ */ diff --git a/audio-kernel/asoc/codecs/wcd-mbhc-v2-api.h b/audio-kernel/asoc/codecs/wcd-mbhc-v2-api.h new file mode 100644 index 000000000..7b6e94507 --- /dev/null +++ b/audio-kernel/asoc/codecs/wcd-mbhc-v2-api.h @@ -0,0 +1,60 @@ +/* Copyright (c) 2014-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 + * 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 __WCD_MBHC_V2_API_H__ +#define __WCD_MBHC_V2_API_H__ + +#include "wcd-mbhc-v2.h" + +#if IS_ENABLED(CONFIG_SND_SOC_WCD_MBHC) +int wcd_mbhc_start(struct wcd_mbhc *mbhc, + struct wcd_mbhc_config *mbhc_cfg); +void wcd_mbhc_stop(struct wcd_mbhc *mbhc); +int wcd_mbhc_init(struct wcd_mbhc *mbhc, struct snd_soc_codec *codec, + const struct wcd_mbhc_cb *mbhc_cb, + const struct wcd_mbhc_intr *mbhc_cdc_intr_ids, + struct wcd_mbhc_register *wcd_mbhc_regs, + bool impedance_det_en); +int wcd_mbhc_get_impedance(struct wcd_mbhc *mbhc, uint32_t *zl, + uint32_t *zr); +void wcd_mbhc_deinit(struct wcd_mbhc *mbhc); + +#else +static inline void wcd_mbhc_stop(struct wcd_mbhc *mbhc) +{ +} +int wcd_mbhc_init(struct wcd_mbhc *mbhc, struct snd_soc_codec *codec, + const struct wcd_mbhc_cb *mbhc_cb, + const struct wcd_mbhc_intr *mbhc_cdc_intr_ids, + struct wcd_mbhc_register *wcd_mbhc_regs, + bool impedance_det_en) +{ + return 0; +} +static inline int wcd_mbhc_start(struct wcd_mbhc *mbhc, + struct wcd_mbhc_config *mbhc_cfg) +{ + return 0; +} +static inline int wcd_mbhc_get_impedance(struct wcd_mbhc *mbhc, + uint32_t *zl, + uint32_t *zr) +{ + *zl = 0; + *zr = 0; + return -EINVAL; +} +static inline void wcd_mbhc_deinit(struct wcd_mbhc *mbhc) +{ +} +#endif + +#endif /* __WCD_MBHC_V2_API_H__ */ diff --git a/audio-kernel/asoc/codecs/wcd-mbhc-v2.c b/audio-kernel/asoc/codecs/wcd-mbhc-v2.c new file mode 100644 index 000000000..3480c1e21 --- /dev/null +++ b/audio-kernel/asoc/codecs/wcd-mbhc-v2.c @@ -0,0 +1,2006 @@ +/* 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 + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "msm-cdc-pinctrl.h" +#include "wcdcal-hwdep.h" +#include "wcd-mbhc-legacy.h" +#include "wcd-mbhc-adc.h" +#include "wcd-mbhc-v2-api.h" + +void wcd_mbhc_jack_report(struct wcd_mbhc *mbhc, + struct snd_soc_jack *jack, int status, int mask) +{ + snd_soc_jack_report(jack, status, mask); +} +EXPORT_SYMBOL(wcd_mbhc_jack_report); + +static void __hphocp_off_report(struct wcd_mbhc *mbhc, u32 jack_status, + int irq) +{ + struct snd_soc_codec *codec = mbhc->codec; + + dev_dbg(codec->dev, "%s: clear ocp status %x\n", + __func__, jack_status); + + if (mbhc->hph_status & jack_status) { + mbhc->hph_status &= ~jack_status; + wcd_mbhc_jack_report(mbhc, &mbhc->headset_jack, + mbhc->hph_status, WCD_MBHC_JACK_MASK); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_OCP_FSM_EN, 0); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_OCP_FSM_EN, 1); + /* + * reset retry counter as PA is turned off signifying + * start of new OCP detection session + */ + if (mbhc->intr_ids->hph_left_ocp) + mbhc->hphlocp_cnt = 0; + else + mbhc->hphrocp_cnt = 0; + mbhc->mbhc_cb->irq_control(codec, irq, true); + } +} + +static void hphrocp_off_report(struct wcd_mbhc *mbhc, u32 jack_status) +{ + __hphocp_off_report(mbhc, SND_JACK_OC_HPHR, + mbhc->intr_ids->hph_right_ocp); +} + +static void hphlocp_off_report(struct wcd_mbhc *mbhc, u32 jack_status) +{ + __hphocp_off_report(mbhc, SND_JACK_OC_HPHL, + mbhc->intr_ids->hph_left_ocp); +} + +static void wcd_program_hs_vref(struct wcd_mbhc *mbhc) +{ + struct wcd_mbhc_plug_type_cfg *plug_type_cfg; + struct snd_soc_codec *codec = mbhc->codec; + u32 reg_val; + + plug_type_cfg = WCD_MBHC_CAL_PLUG_TYPE_PTR(mbhc->mbhc_cfg->calibration); + reg_val = ((plug_type_cfg->v_hs_max - HS_VREF_MIN_VAL) / 100); + + dev_dbg(codec->dev, "%s: reg_val = %x\n", __func__, reg_val); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HS_VREF, reg_val); +} + +static void wcd_program_btn_threshold(const struct wcd_mbhc *mbhc, bool micbias) +{ + struct wcd_mbhc_btn_detect_cfg *btn_det; + struct snd_soc_codec *codec = mbhc->codec; + struct snd_soc_card *card = codec->component.card; + s16 *btn_low, *btn_high; + + if (mbhc->mbhc_cfg->calibration == NULL) { + dev_err(card->dev, "%s: calibration data is NULL\n", __func__); + return; + } + + btn_det = WCD_MBHC_CAL_BTN_DET_PTR(mbhc->mbhc_cfg->calibration); + btn_low = btn_det->_v_btn_low; + btn_high = ((void *)&btn_det->_v_btn_low) + + (sizeof(btn_det->_v_btn_low[0]) * btn_det->num_btn); + + mbhc->mbhc_cb->set_btn_thr(codec, btn_low, btn_high, btn_det->num_btn, + micbias); +} + +void wcd_enable_curr_micbias(const struct wcd_mbhc *mbhc, + const enum wcd_mbhc_cs_mb_en_flag cs_mb_en) +{ + + /* + * Some codecs handle micbias/pullup enablement in codec + * drivers itself and micbias is not needed for regular + * plug type detection. So if micbias_control callback function + * is defined, just return. + */ + if (mbhc->mbhc_cb->mbhc_micbias_control) + return; + + pr_debug("%s: enter, cs_mb_en: %d\n", __func__, cs_mb_en); + + switch (cs_mb_en) { + case WCD_MBHC_EN_CS: + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_MICB_CTRL, 0); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_BTN_ISRC_CTL, 3); + /* Program Button threshold registers as per CS */ + wcd_program_btn_threshold(mbhc, false); + break; + case WCD_MBHC_EN_MB: + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_BTN_ISRC_CTL, 0); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 1); + + /* Disable PULL_UP_EN & enable MICBIAS */ + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_MICB_CTRL, 2); + /* Program Button threshold registers as per MICBIAS */ + wcd_program_btn_threshold(mbhc, true); + break; + case WCD_MBHC_EN_PULLUP: + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_BTN_ISRC_CTL, 3); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 1); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_MICB_CTRL, 1); + /* Program Button threshold registers as per MICBIAS */ + wcd_program_btn_threshold(mbhc, true); + break; + case WCD_MBHC_EN_NONE: + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_BTN_ISRC_CTL, 0); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 1); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_MICB_CTRL, 0); + break; + default: + pr_debug("%s: Invalid parameter", __func__); + break; + } + + pr_debug("%s: exit\n", __func__); +} +EXPORT_SYMBOL(wcd_enable_curr_micbias); + +static const char *wcd_mbhc_get_event_string(int event) +{ + switch (event) { + case WCD_EVENT_PRE_MICBIAS_2_OFF: + return WCD_MBHC_STRINGIFY(WCD_EVENT_PRE_MICBIAS_2_OFF); + case WCD_EVENT_POST_MICBIAS_2_OFF: + return WCD_MBHC_STRINGIFY(WCD_EVENT_POST_MICBIAS_2_OFF); + case WCD_EVENT_PRE_MICBIAS_2_ON: + return WCD_MBHC_STRINGIFY(WCD_EVENT_PRE_MICBIAS_2_ON); + case WCD_EVENT_POST_MICBIAS_2_ON: + return WCD_MBHC_STRINGIFY(WCD_EVENT_POST_MICBIAS_2_ON); + case WCD_EVENT_PRE_HPHL_PA_ON: + return WCD_MBHC_STRINGIFY(WCD_EVENT_PRE_HPHL_PA_ON); + case WCD_EVENT_POST_HPHL_PA_OFF: + return WCD_MBHC_STRINGIFY(WCD_EVENT_POST_HPHL_PA_OFF); + case WCD_EVENT_PRE_HPHR_PA_ON: + return WCD_MBHC_STRINGIFY(WCD_EVENT_PRE_HPHR_PA_ON); + case WCD_EVENT_POST_HPHR_PA_OFF: + return WCD_MBHC_STRINGIFY(WCD_EVENT_POST_HPHR_PA_OFF); + case WCD_EVENT_PRE_HPHR_PA_OFF: + return WCD_MBHC_STRINGIFY(WCD_EVENT_PRE_HPHR_PA_OFF); + case WCD_EVENT_PRE_HPHL_PA_OFF: + return WCD_MBHC_STRINGIFY(WCD_EVENT_PRE_HPHL_PA_OFF); + case WCD_EVENT_POST_DAPM_MICBIAS_2_ON: + return WCD_MBHC_STRINGIFY(WCD_EVENT_POST_DAPM_MICBIAS_2_ON); + case WCD_EVENT_PRE_DAPM_MICBIAS_2_ON: + return WCD_MBHC_STRINGIFY(WCD_EVENT_PRE_DAPM_MICBIAS_2_ON); + case WCD_EVENT_POST_DAPM_MICBIAS_2_OFF: + return WCD_MBHC_STRINGIFY(WCD_EVENT_POST_DAPM_MICBIAS_2_OFF); + case WCD_EVENT_PRE_DAPM_MICBIAS_2_OFF: + return WCD_MBHC_STRINGIFY(WCD_EVENT_PRE_DAPM_MICBIAS_2_OFF); + case WCD_EVENT_OCP_OFF: + return WCD_MBHC_STRINGIFY(WCD_EVENT_OCP_OFF); + case WCD_EVENT_OCP_ON: + return WCD_MBHC_STRINGIFY(WCD_EVENT_OCP_ON); + case WCD_EVENT_INVALID: + default: + return WCD_MBHC_STRINGIFY(WCD_EVENT_INVALID); + } +} + +static int wcd_event_notify(struct notifier_block *self, unsigned long val, + void *data) +{ + struct wcd_mbhc *mbhc = (struct wcd_mbhc *)data; + enum wcd_notify_event event = (enum wcd_notify_event)val; + struct snd_soc_codec *codec = mbhc->codec; + bool micbias2 = false; + bool micbias1 = false; + u8 fsm_en = 0; + + pr_debug("%s: event %s (%d)\n", __func__, + wcd_mbhc_get_event_string(event), event); + if (mbhc->mbhc_cb->micbias_enable_status) { + micbias2 = mbhc->mbhc_cb->micbias_enable_status(mbhc, + MIC_BIAS_2); + micbias1 = mbhc->mbhc_cb->micbias_enable_status(mbhc, + MIC_BIAS_1); + } + switch (event) { + /* MICBIAS usage change */ + case WCD_EVENT_POST_DAPM_MICBIAS_2_ON: + mbhc->is_hs_recording = true; + pr_debug("%s: is_capture: %d\n", __func__, + mbhc->is_hs_recording); + break; + case WCD_EVENT_POST_MICBIAS_2_ON: + if (!mbhc->micbias_enable) + goto out_micb_en; + if (mbhc->mbhc_cb->mbhc_common_micb_ctrl) { + mbhc->mbhc_cb->mbhc_common_micb_ctrl(codec, + MBHC_COMMON_MICB_PRECHARGE, + true); + mbhc->mbhc_cb->mbhc_common_micb_ctrl(codec, + MBHC_COMMON_MICB_SET_VAL, + true); + /* + * Special headset needs MICBIAS as 2.7V so wait for + * 50 msec for the MICBIAS to reach 2.7 volts. + */ + msleep(50); + } + if (mbhc->mbhc_cb->set_auto_zeroing) + mbhc->mbhc_cb->set_auto_zeroing(codec, true); + if (mbhc->mbhc_cb->mbhc_common_micb_ctrl) + mbhc->mbhc_cb->mbhc_common_micb_ctrl(codec, + MBHC_COMMON_MICB_PRECHARGE, + false); +out_micb_en: + /* Disable current source if micbias enabled */ + if (mbhc->mbhc_cb->mbhc_micbias_control) { + WCD_MBHC_REG_READ(WCD_MBHC_FSM_EN, fsm_en); + if (fsm_en) + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_BTN_ISRC_CTL, + 0); + } else { + mbhc->is_hs_recording = true; + wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_MB); + } + /* configure cap settings properly when micbias is enabled */ + if (mbhc->mbhc_cb->set_cap_mode) + mbhc->mbhc_cb->set_cap_mode(codec, micbias1, true); + break; + case WCD_EVENT_PRE_MICBIAS_2_OFF: + /* + * Before MICBIAS_2 is turned off, if FSM is enabled, + * make sure current source is enabled so as to detect + * button press/release events + */ + if (mbhc->mbhc_cb->mbhc_micbias_control && + !mbhc->micbias_enable) { + WCD_MBHC_REG_READ(WCD_MBHC_FSM_EN, fsm_en); + if (fsm_en) + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_BTN_ISRC_CTL, + 3); + } + break; + /* MICBIAS usage change */ + case WCD_EVENT_POST_DAPM_MICBIAS_2_OFF: + mbhc->is_hs_recording = false; + pr_debug("%s: is_capture: %d\n", __func__, + mbhc->is_hs_recording); + break; + case WCD_EVENT_POST_MICBIAS_2_OFF: + if (!mbhc->mbhc_cb->mbhc_micbias_control) + mbhc->is_hs_recording = false; + if (mbhc->micbias_enable) { + wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_MB); + break; + } + + if (mbhc->mbhc_cb->set_auto_zeroing) + mbhc->mbhc_cb->set_auto_zeroing(codec, false); + if (mbhc->mbhc_cb->set_micbias_value && !mbhc->micbias_enable) + mbhc->mbhc_cb->set_micbias_value(codec); + /* Enable PULL UP if PA's are enabled */ + if ((test_bit(WCD_MBHC_EVENT_PA_HPHL, &mbhc->event_state)) || + (test_bit(WCD_MBHC_EVENT_PA_HPHR, + &mbhc->event_state))) + /* enable pullup and cs, disable mb */ + wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_PULLUP); + else + /* enable current source and disable mb, pullup*/ + wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_CS); + + /* configure cap settings properly when micbias is disabled */ + if (mbhc->mbhc_cb->set_cap_mode) + mbhc->mbhc_cb->set_cap_mode(codec, micbias1, false); + break; + case WCD_EVENT_PRE_HPHL_PA_OFF: + mutex_lock(&mbhc->hphl_pa_lock); + break; + case WCD_EVENT_POST_HPHL_PA_OFF: + clear_bit(WCD_MBHC_HPHL_PA_OFF_ACK, &mbhc->hph_pa_dac_state); + if (mbhc->hph_status & SND_JACK_OC_HPHL) + hphlocp_off_report(mbhc, SND_JACK_OC_HPHL); + clear_bit(WCD_MBHC_EVENT_PA_HPHL, &mbhc->event_state); + /* check if micbias is enabled */ + if (micbias2) + /* Disable cs, pullup & enable micbias */ + wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_MB); + else + /* Disable micbias, pullup & enable cs */ + wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_CS); + mutex_unlock(&mbhc->hphl_pa_lock); + clear_bit(WCD_MBHC_ANC0_OFF_ACK, &mbhc->hph_anc_state); + break; + case WCD_EVENT_PRE_HPHR_PA_OFF: + mutex_lock(&mbhc->hphr_pa_lock); + break; + case WCD_EVENT_POST_HPHR_PA_OFF: + clear_bit(WCD_MBHC_HPHR_PA_OFF_ACK, &mbhc->hph_pa_dac_state); + if (mbhc->hph_status & SND_JACK_OC_HPHR) + hphrocp_off_report(mbhc, SND_JACK_OC_HPHR); + clear_bit(WCD_MBHC_EVENT_PA_HPHR, &mbhc->event_state); + /* check if micbias is enabled */ + if (micbias2) + /* Disable cs, pullup & enable micbias */ + wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_MB); + else + /* Disable micbias, pullup & enable cs */ + wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_CS); + mutex_unlock(&mbhc->hphr_pa_lock); + clear_bit(WCD_MBHC_ANC1_OFF_ACK, &mbhc->hph_anc_state); + break; + case WCD_EVENT_PRE_HPHL_PA_ON: + set_bit(WCD_MBHC_EVENT_PA_HPHL, &mbhc->event_state); + /* check if micbias is enabled */ + if (micbias2) + /* Disable cs, pullup & enable micbias */ + wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_MB); + else + /* Disable micbias, enable pullup & cs */ + wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_PULLUP); + break; + case WCD_EVENT_PRE_HPHR_PA_ON: + set_bit(WCD_MBHC_EVENT_PA_HPHR, &mbhc->event_state); + /* check if micbias is enabled */ + if (micbias2) + /* Disable cs, pullup & enable micbias */ + wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_MB); + else + /* Disable micbias, enable pullup & cs */ + wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_PULLUP); + break; + case WCD_EVENT_OCP_OFF: + mbhc->mbhc_cb->irq_control(mbhc->codec, + mbhc->intr_ids->hph_left_ocp, + false); + break; + case WCD_EVENT_OCP_ON: + mbhc->mbhc_cb->irq_control(mbhc->codec, + mbhc->intr_ids->hph_left_ocp, + true); + break; + default: + break; + } + return 0; +} + +int wcd_cancel_btn_work(struct wcd_mbhc *mbhc) +{ + int r; + + r = cancel_delayed_work_sync(&mbhc->mbhc_btn_dwork); + /* + * if scheduled mbhc.mbhc_btn_dwork is canceled from here, + * we have to unlock from here instead btn_work + */ + if (r) + mbhc->mbhc_cb->lock_sleep(mbhc, false); + return r; +} +EXPORT_SYMBOL(wcd_cancel_btn_work); + +bool wcd_swch_level_remove(struct wcd_mbhc *mbhc) +{ + u16 result2 = 0; + + WCD_MBHC_REG_READ(WCD_MBHC_SWCH_LEVEL_REMOVE, result2); + return (result2) ? true : false; +} +EXPORT_SYMBOL(wcd_swch_level_remove); + +static void wcd_mbhc_clr_and_turnon_hph_padac(struct wcd_mbhc *mbhc) +{ + bool pa_turned_on = false; + u8 wg_time = 0; + + WCD_MBHC_REG_READ(WCD_MBHC_HPH_CNP_WG_TIME, wg_time); + wg_time += 1; + + mutex_lock(&mbhc->hphr_pa_lock); + if (test_and_clear_bit(WCD_MBHC_HPHR_PA_OFF_ACK, + &mbhc->hph_pa_dac_state)) { + pr_debug("%s: HPHR clear flag and enable PA\n", __func__); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HPHR_PA_EN, 1); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HPHR_OCP_DET_EN, 1); + pa_turned_on = true; + } + mutex_unlock(&mbhc->hphr_pa_lock); + mutex_lock(&mbhc->hphl_pa_lock); + if (test_and_clear_bit(WCD_MBHC_HPHL_PA_OFF_ACK, + &mbhc->hph_pa_dac_state)) { + pr_debug("%s: HPHL clear flag and enable PA\n", __func__); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HPHL_PA_EN, 1); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HPHL_OCP_DET_EN, 1); + pa_turned_on = true; + } + mutex_unlock(&mbhc->hphl_pa_lock); + + if (pa_turned_on) { + pr_debug("%s: PA was turned on by MBHC and not by DAPM\n", + __func__); + usleep_range(wg_time * 1000, wg_time * 1000 + 50); + } + + if (test_and_clear_bit(WCD_MBHC_ANC0_OFF_ACK, + &mbhc->hph_anc_state)) { + usleep_range(20000, 20100); + pr_debug("%s: HPHL ANC clear flag and enable ANC_EN\n", + __func__); + if (mbhc->mbhc_cb->update_anc_state) + mbhc->mbhc_cb->update_anc_state(mbhc->codec, true, 0); + } + + if (test_and_clear_bit(WCD_MBHC_ANC1_OFF_ACK, + &mbhc->hph_anc_state)) { + usleep_range(20000, 20100); + pr_debug("%s: HPHR ANC clear flag and enable ANC_EN\n", + __func__); + if (mbhc->mbhc_cb->update_anc_state) + mbhc->mbhc_cb->update_anc_state(mbhc->codec, true, 1); + } + +} + +static bool wcd_mbhc_is_hph_pa_on(struct wcd_mbhc *mbhc) +{ + bool hph_pa_on = false; + + WCD_MBHC_REG_READ(WCD_MBHC_HPH_PA_EN, hph_pa_on); + + return (hph_pa_on) ? true : false; +} + +static void wcd_mbhc_set_and_turnoff_hph_padac(struct wcd_mbhc *mbhc) +{ + u8 wg_time = 0; + + WCD_MBHC_REG_READ(WCD_MBHC_HPH_CNP_WG_TIME, wg_time); + wg_time += 1; + + /* If headphone PA is on, check if userspace receives + * removal event to sync-up PA's state + */ + if (wcd_mbhc_is_hph_pa_on(mbhc)) { + pr_debug("%s PA is on, setting PA_OFF_ACK\n", __func__); + set_bit(WCD_MBHC_HPHL_PA_OFF_ACK, &mbhc->hph_pa_dac_state); + set_bit(WCD_MBHC_HPHR_PA_OFF_ACK, &mbhc->hph_pa_dac_state); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HPHL_OCP_DET_EN, 0); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HPHR_OCP_DET_EN, 0); + } else { + pr_debug("%s PA is off\n", __func__); + } + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HPH_PA_EN, 0); + usleep_range(wg_time * 1000, wg_time * 1000 + 50); + + + if (mbhc->mbhc_cb->is_anc_on && mbhc->mbhc_cb->is_anc_on(mbhc)) { + usleep_range(20000, 20100); + pr_debug("%s ANC is on, setting ANC_OFF_ACK\n", __func__); + set_bit(WCD_MBHC_ANC0_OFF_ACK, &mbhc->hph_anc_state); + set_bit(WCD_MBHC_ANC1_OFF_ACK, &mbhc->hph_anc_state); + if (mbhc->mbhc_cb->update_anc_state) { + mbhc->mbhc_cb->update_anc_state(mbhc->codec, false, 0); + mbhc->mbhc_cb->update_anc_state(mbhc->codec, false, 1); + } else { + pr_debug("%s ANC is off\n", __func__); + } + } +} + +int wcd_mbhc_get_impedance(struct wcd_mbhc *mbhc, uint32_t *zl, + uint32_t *zr) +{ + *zl = mbhc->zl; + *zr = mbhc->zr; + + if (*zl && *zr) + return 0; + else + return -EINVAL; +} +EXPORT_SYMBOL(wcd_mbhc_get_impedance); + +void wcd_mbhc_hs_elec_irq(struct wcd_mbhc *mbhc, int irq_type, + bool enable) +{ + int irq; + + WCD_MBHC_RSC_ASSERT_LOCKED(mbhc); + + if (irq_type == WCD_MBHC_ELEC_HS_INS) + irq = mbhc->intr_ids->mbhc_hs_ins_intr; + else if (irq_type == WCD_MBHC_ELEC_HS_REM) + irq = mbhc->intr_ids->mbhc_hs_rem_intr; + else { + pr_debug("%s: irq_type: %d, enable: %d\n", + __func__, irq_type, enable); + return; + } + + pr_debug("%s: irq: %d, enable: %d, intr_status:%lu\n", + __func__, irq, enable, mbhc->intr_status); + if ((test_bit(irq_type, &mbhc->intr_status)) != enable) { + mbhc->mbhc_cb->irq_control(mbhc->codec, irq, enable); + if (enable) + set_bit(irq_type, &mbhc->intr_status); + else + clear_bit(irq_type, &mbhc->intr_status); + } +} +EXPORT_SYMBOL(wcd_mbhc_hs_elec_irq); + +void wcd_mbhc_report_plug(struct wcd_mbhc *mbhc, int insertion, + enum snd_jack_types jack_type) +{ + struct snd_soc_codec *codec = mbhc->codec; + bool is_pa_on = false; + u8 fsm_en = 0; + + WCD_MBHC_RSC_ASSERT_LOCKED(mbhc); + + pr_debug("%s: enter insertion %d hph_status %x\n", + __func__, insertion, mbhc->hph_status); + if (!insertion) { + /* Report removal */ + mbhc->hph_status &= ~jack_type; + /* + * cancel possibly scheduled btn work and + * report release if we reported button press + */ + if (wcd_cancel_btn_work(mbhc)) { + pr_debug("%s: button press is canceled\n", __func__); + } else if (mbhc->buttons_pressed) { + pr_debug("%s: release of button press%d\n", + __func__, jack_type); + wcd_mbhc_jack_report(mbhc, &mbhc->button_jack, 0, + mbhc->buttons_pressed); + mbhc->buttons_pressed &= + ~WCD_MBHC_JACK_BUTTON_MASK; + } + + if (mbhc->micbias_enable) { + if (mbhc->mbhc_cb->mbhc_micbias_control) + mbhc->mbhc_cb->mbhc_micbias_control( + codec, MIC_BIAS_2, + MICB_DISABLE); + if (mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic) + mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic( + codec, + MIC_BIAS_2, false); + if (mbhc->mbhc_cb->set_micbias_value) { + mbhc->mbhc_cb->set_micbias_value(codec); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_MICB_CTRL, 0); + } + mbhc->micbias_enable = false; + } + + mbhc->hph_type = WCD_MBHC_HPH_NONE; + mbhc->zl = mbhc->zr = 0; + pr_debug("%s: Reporting removal %d(%x)\n", __func__, + jack_type, mbhc->hph_status); + wcd_mbhc_jack_report(mbhc, &mbhc->headset_jack, + mbhc->hph_status, WCD_MBHC_JACK_MASK); + wcd_mbhc_set_and_turnoff_hph_padac(mbhc); + hphrocp_off_report(mbhc, SND_JACK_OC_HPHR); + hphlocp_off_report(mbhc, SND_JACK_OC_HPHL); + mbhc->current_plug = MBHC_PLUG_TYPE_NONE; + mbhc->force_linein = false; + } else { + /* + * Report removal of current jack type. + * Headphone to headset shouldn't report headphone + * removal. + */ + if (mbhc->mbhc_cfg->detect_extn_cable && + (mbhc->current_plug == MBHC_PLUG_TYPE_HIGH_HPH || + jack_type == SND_JACK_LINEOUT) && + (mbhc->hph_status && mbhc->hph_status != jack_type)) { + + if (mbhc->micbias_enable && + mbhc->hph_status == SND_JACK_HEADSET) { + if (mbhc->mbhc_cb->mbhc_micbias_control) + mbhc->mbhc_cb->mbhc_micbias_control( + codec, MIC_BIAS_2, + MICB_DISABLE); + if (mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic) + mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic( + codec, + MIC_BIAS_2, false); + if (mbhc->mbhc_cb->set_micbias_value) { + mbhc->mbhc_cb->set_micbias_value( + codec); + WCD_MBHC_REG_UPDATE_BITS( + WCD_MBHC_MICB_CTRL, 0); + } + mbhc->micbias_enable = false; + } + mbhc->hph_type = WCD_MBHC_HPH_NONE; + mbhc->zl = mbhc->zr = 0; + pr_debug("%s: Reporting removal (%x)\n", + __func__, mbhc->hph_status); + wcd_mbhc_jack_report(mbhc, &mbhc->headset_jack, + 0, WCD_MBHC_JACK_MASK); + + if (mbhc->hph_status == SND_JACK_LINEOUT) { + + pr_debug("%s: Enable micbias\n", __func__); + /* Disable current source and enable micbias */ + wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_MB); + pr_debug("%s: set up elec removal detection\n", + __func__); + usleep_range(200, 210); + wcd_mbhc_hs_elec_irq(mbhc, + WCD_MBHC_ELEC_HS_REM, + true); + } + mbhc->hph_status &= ~(SND_JACK_HEADSET | + SND_JACK_LINEOUT | + SND_JACK_ANC_HEADPHONE | + SND_JACK_UNSUPPORTED); + mbhc->force_linein = false; + } + + if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADSET && + jack_type == SND_JACK_HEADPHONE) + mbhc->hph_status &= ~SND_JACK_HEADSET; + + /* Report insertion */ + if (jack_type == SND_JACK_HEADPHONE) + mbhc->current_plug = MBHC_PLUG_TYPE_HEADPHONE; + else if (jack_type == SND_JACK_UNSUPPORTED) + mbhc->current_plug = MBHC_PLUG_TYPE_GND_MIC_SWAP; + else if (jack_type == SND_JACK_HEADSET) { + mbhc->current_plug = MBHC_PLUG_TYPE_HEADSET; + mbhc->jiffies_atreport = jiffies; + } else if (jack_type == SND_JACK_LINEOUT) { + mbhc->current_plug = MBHC_PLUG_TYPE_HIGH_HPH; + } else if (jack_type == SND_JACK_ANC_HEADPHONE) + mbhc->current_plug = MBHC_PLUG_TYPE_ANC_HEADPHONE; + + if (mbhc->mbhc_cb->hph_pa_on_status) + is_pa_on = mbhc->mbhc_cb->hph_pa_on_status(codec); + + if (mbhc->impedance_detect && + mbhc->mbhc_cb->compute_impedance && + (mbhc->mbhc_cfg->linein_th != 0) && + (!is_pa_on)) { + /* Set MUX_CTL to AUTO for Z-det */ + WCD_MBHC_REG_READ(WCD_MBHC_FSM_EN, fsm_en); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 0); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_MUX_CTL, + MUX_CTL_AUTO); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 1); + mbhc->mbhc_cb->compute_impedance(mbhc, + &mbhc->zl, &mbhc->zr); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, + fsm_en); + if ((mbhc->zl > mbhc->mbhc_cfg->linein_th && + mbhc->zl < MAX_IMPED) && + (mbhc->zr > mbhc->mbhc_cfg->linein_th && + mbhc->zr < MAX_IMPED) && + (jack_type == SND_JACK_HEADPHONE)) { + jack_type = SND_JACK_LINEOUT; + mbhc->force_linein = true; + mbhc->current_plug = MBHC_PLUG_TYPE_HIGH_HPH; + if (mbhc->hph_status) { + mbhc->hph_status &= ~(SND_JACK_HEADSET | + SND_JACK_LINEOUT | + SND_JACK_UNSUPPORTED); + wcd_mbhc_jack_report(mbhc, + &mbhc->headset_jack, + mbhc->hph_status, + WCD_MBHC_JACK_MASK); + } + pr_debug("%s: Marking jack type as SND_JACK_LINEOUT\n", + __func__); + } + } + + mbhc->hph_status |= jack_type; + + pr_debug("%s: Reporting insertion %d(%x)\n", __func__, + jack_type, mbhc->hph_status); + wcd_mbhc_jack_report(mbhc, &mbhc->headset_jack, + (mbhc->hph_status | SND_JACK_MECHANICAL), + WCD_MBHC_JACK_MASK); + wcd_mbhc_clr_and_turnon_hph_padac(mbhc); + } + pr_debug("%s: leave hph_status %x\n", __func__, mbhc->hph_status); +} +EXPORT_SYMBOL(wcd_mbhc_report_plug); + +void wcd_mbhc_elec_hs_report_unplug(struct wcd_mbhc *mbhc) +{ + /* cancel pending button press */ + if (wcd_cancel_btn_work(mbhc)) + pr_debug("%s: button press is canceled\n", __func__); + /* cancel correct work function */ + if (mbhc->mbhc_fn->wcd_cancel_hs_detect_plug) + mbhc->mbhc_fn->wcd_cancel_hs_detect_plug(mbhc, + &mbhc->correct_plug_swch); + else + pr_info("%s: hs_detect_plug work not cancelled\n", __func__); + + pr_debug("%s: Report extension cable\n", __func__); + wcd_mbhc_report_plug(mbhc, 1, SND_JACK_LINEOUT); + /* + * If PA is enabled HPHL schmitt trigger can + * be unreliable, make sure to disable it + */ + if (test_bit(WCD_MBHC_EVENT_PA_HPHL, + &mbhc->event_state)) + wcd_mbhc_set_and_turnoff_hph_padac(mbhc); + /* + * Disable HPHL trigger and MIC Schmitt triggers. + * Setup for insertion detection. + */ + wcd_mbhc_hs_elec_irq(mbhc, WCD_MBHC_ELEC_HS_REM, + false); + wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_NONE); + /* Disable HW FSM */ + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 0); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_SCHMT_ISRC, 3); + + /* Set the detection type appropriately */ + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_DETECTION_TYPE, 1); + wcd_mbhc_hs_elec_irq(mbhc, WCD_MBHC_ELEC_HS_INS, + true); +} +EXPORT_SYMBOL(wcd_mbhc_elec_hs_report_unplug); + +void wcd_mbhc_find_plug_and_report(struct wcd_mbhc *mbhc, + enum wcd_mbhc_plug_type plug_type) +{ + bool anc_mic_found = false; + enum snd_jack_types jack_type; + + if (mbhc->deinit_in_progress) { + pr_info("%s: mbhc deinit in progess: ignore report\n", __func__); + return; + } + + pr_debug("%s: enter current_plug(%d) new_plug(%d)\n", + __func__, mbhc->current_plug, plug_type); + + WCD_MBHC_RSC_ASSERT_LOCKED(mbhc); + + if (mbhc->current_plug == plug_type) { + pr_debug("%s: cable already reported, exit\n", __func__); + goto exit; + } + + if (plug_type == MBHC_PLUG_TYPE_HEADPHONE) { + /* + * Nothing was reported previously + * report a headphone or unsupported + */ + wcd_mbhc_report_plug(mbhc, 1, SND_JACK_HEADPHONE); + } else if (plug_type == MBHC_PLUG_TYPE_GND_MIC_SWAP) { + if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADPHONE) + wcd_mbhc_report_plug(mbhc, 0, SND_JACK_HEADPHONE); + if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADSET) + wcd_mbhc_report_plug(mbhc, 0, SND_JACK_HEADSET); + wcd_mbhc_report_plug(mbhc, 1, SND_JACK_UNSUPPORTED); + } else if (plug_type == MBHC_PLUG_TYPE_HEADSET) { + if (mbhc->mbhc_cfg->enable_anc_mic_detect && + mbhc->mbhc_fn->wcd_mbhc_detect_anc_plug_type) + anc_mic_found = + mbhc->mbhc_fn->wcd_mbhc_detect_anc_plug_type(mbhc); + jack_type = SND_JACK_HEADSET; + if (anc_mic_found) + jack_type = SND_JACK_ANC_HEADPHONE; + + /* + * If Headphone was reported previously, this will + * only report the mic line + */ + wcd_mbhc_report_plug(mbhc, 1, jack_type); + } else if (plug_type == MBHC_PLUG_TYPE_HIGH_HPH) { + if (mbhc->mbhc_cfg->detect_extn_cable) { + /* High impedance device found. Report as LINEOUT */ + wcd_mbhc_report_plug(mbhc, 1, SND_JACK_LINEOUT); + pr_debug("%s: setup mic trigger for further detection\n", + __func__); + + /* Disable HW FSM and current source */ + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 0); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_BTN_ISRC_CTL, 0); + /* Setup for insertion detection */ + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_DETECTION_TYPE, + 1); + /* + * Enable HPHL trigger and MIC Schmitt triggers + * and request for elec insertion interrupts + */ + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_SCHMT_ISRC, + 3); + wcd_mbhc_hs_elec_irq(mbhc, WCD_MBHC_ELEC_HS_INS, + true); + } else { + wcd_mbhc_report_plug(mbhc, 1, SND_JACK_LINEOUT); + } + } else { + WARN(1, "Unexpected current plug_type %d, plug_type %d\n", + mbhc->current_plug, plug_type); + } +exit: + pr_debug("%s: leave\n", __func__); +} +EXPORT_SYMBOL(wcd_mbhc_find_plug_and_report); + +static bool wcd_mbhc_moisture_detect(struct wcd_mbhc *mbhc, bool detection_type) +{ + bool ret = false; + + if (!mbhc->mbhc_cfg->moisture_en || + !mbhc->mbhc_cfg->moisture_duty_cycle_en) + return ret; + + if (!mbhc->mbhc_cb->mbhc_get_moisture_status || + !mbhc->mbhc_cb->mbhc_moisture_polling_ctrl || + !mbhc->mbhc_cb->mbhc_moisture_detect_en) + return ret; + + if (mbhc->mbhc_cb->mbhc_get_moisture_status(mbhc)) { + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_L_DET_EN, 0); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_GND_DET_EN, 0); + mbhc->mbhc_cb->mbhc_moisture_polling_ctrl(mbhc, true); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_MECH_DETECTION_TYPE, + detection_type); + ret = true; + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_L_DET_EN, 1); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_GND_DET_EN, 1); + } else { + mbhc->mbhc_cb->mbhc_moisture_polling_ctrl(mbhc, false); + mbhc->mbhc_cb->mbhc_moisture_detect_en(mbhc, false); + } + + return ret; +} + +static void wcd_mbhc_swch_irq_handler(struct wcd_mbhc *mbhc) +{ + bool detection_type = 0; + bool micbias1 = false; + struct snd_soc_codec *codec = mbhc->codec; + enum snd_jack_types jack_type; + + dev_dbg(codec->dev, "%s: enter\n", __func__); + WCD_MBHC_RSC_LOCK(mbhc); + mbhc->in_swch_irq_handler = true; + + /* cancel pending button press */ + if (wcd_cancel_btn_work(mbhc)) + pr_debug("%s: button press is canceled\n", __func__); + + WCD_MBHC_REG_READ(WCD_MBHC_MECH_DETECTION_TYPE, detection_type); + + /* Set the detection type appropriately */ + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_MECH_DETECTION_TYPE, + !detection_type); + + pr_debug("%s: mbhc->current_plug: %d detection_type: %d\n", __func__, + mbhc->current_plug, detection_type); + if (mbhc->mbhc_fn->wcd_cancel_hs_detect_plug) + mbhc->mbhc_fn->wcd_cancel_hs_detect_plug(mbhc, + &mbhc->correct_plug_swch); + else + pr_info("%s: hs_detect_plug work not cancelled\n", __func__); + + if (mbhc->mbhc_cb->micbias_enable_status) + micbias1 = mbhc->mbhc_cb->micbias_enable_status(mbhc, + MIC_BIAS_1); + + if ((mbhc->current_plug == MBHC_PLUG_TYPE_NONE) && + detection_type) { + + /* If moisture is present, then enable polling, disable + * moisture detection and wait for interrupt + */ + if (wcd_mbhc_moisture_detect(mbhc, detection_type)) + goto done; + + /* Make sure MASTER_BIAS_CTL is enabled */ + mbhc->mbhc_cb->mbhc_bias(codec, true); + + if (mbhc->mbhc_cb->mbhc_common_micb_ctrl) + mbhc->mbhc_cb->mbhc_common_micb_ctrl(codec, + MBHC_COMMON_MICB_TAIL_CURR, true); + + if (!mbhc->mbhc_cfg->hs_ext_micbias && + mbhc->mbhc_cb->micb_internal) + /* + * Enable Tx2 RBias if the headset + * is using internal micbias + */ + mbhc->mbhc_cb->micb_internal(codec, 1, true); + + /* Remove micbias pulldown */ + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_PULLDOWN_CTRL, 0); + /* Apply trim if needed on the device */ + if (mbhc->mbhc_cb->trim_btn_reg) + mbhc->mbhc_cb->trim_btn_reg(codec); + /* Enable external voltage source to micbias if present */ + if (mbhc->mbhc_cb->enable_mb_source) + mbhc->mbhc_cb->enable_mb_source(mbhc, true); + mbhc->btn_press_intr = false; + mbhc->is_btn_press = false; + if (mbhc->mbhc_fn) + mbhc->mbhc_fn->wcd_mbhc_detect_plug_type(mbhc); + } else if ((mbhc->current_plug != MBHC_PLUG_TYPE_NONE) + && !detection_type) { + /* Disable external voltage source to micbias if present */ + if (mbhc->mbhc_cb->enable_mb_source) + mbhc->mbhc_cb->enable_mb_source(mbhc, false); + /* Disable HW FSM */ + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 0); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_BTN_ISRC_CTL, 0); + if (mbhc->mbhc_cb->mbhc_common_micb_ctrl) + mbhc->mbhc_cb->mbhc_common_micb_ctrl(codec, + MBHC_COMMON_MICB_TAIL_CURR, false); + + if (mbhc->mbhc_cb->set_cap_mode) + mbhc->mbhc_cb->set_cap_mode(codec, micbias1, false); + + mbhc->btn_press_intr = false; + mbhc->is_btn_press = false; + switch (mbhc->current_plug) { + case MBHC_PLUG_TYPE_HEADPHONE: + jack_type = SND_JACK_HEADPHONE; + break; + case MBHC_PLUG_TYPE_GND_MIC_SWAP: + jack_type = SND_JACK_UNSUPPORTED; + break; + case MBHC_PLUG_TYPE_HEADSET: + /* make sure to turn off Rbias */ + if (mbhc->mbhc_cb->micb_internal) + mbhc->mbhc_cb->micb_internal(codec, 1, false); + /* Pulldown micbias */ + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_PULLDOWN_CTRL, 1); + jack_type = SND_JACK_HEADSET; + break; + case MBHC_PLUG_TYPE_HIGH_HPH: + if (mbhc->mbhc_detection_logic == WCD_DETECTION_ADC) + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_ISRC_EN, 0); + mbhc->is_extn_cable = false; + jack_type = SND_JACK_LINEOUT; + break; + case MBHC_PLUG_TYPE_ANC_HEADPHONE: + jack_type = SND_JACK_ANC_HEADPHONE; + break; + default: + pr_info("%s: Invalid current plug: %d\n", + __func__, mbhc->current_plug); + jack_type = SND_JACK_UNSUPPORTED; + break; + } + wcd_mbhc_hs_elec_irq(mbhc, WCD_MBHC_ELEC_HS_REM, false); + wcd_mbhc_hs_elec_irq(mbhc, WCD_MBHC_ELEC_HS_INS, false); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_DETECTION_TYPE, 1); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_SCHMT_ISRC, 0); + mbhc->extn_cable_hph_rem = false; + wcd_mbhc_report_plug(mbhc, 0, jack_type); + + if (mbhc->mbhc_cfg->enable_usbc_analog) { + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_L_DET_EN, 0); + if (mbhc->mbhc_cb->clk_setup) + mbhc->mbhc_cb->clk_setup(mbhc->codec, false); + } + + if (mbhc->mbhc_cfg->moisture_en && + mbhc->mbhc_cfg->moisture_duty_cycle_en) { + if (mbhc->mbhc_cb->mbhc_moisture_polling_ctrl) + mbhc->mbhc_cb->mbhc_moisture_polling_ctrl(mbhc, + false); + if (mbhc->mbhc_cb->mbhc_moisture_detect_en) + mbhc->mbhc_cb->mbhc_moisture_detect_en(mbhc, + false); + } + + } else if (!detection_type) { + /* Disable external voltage source to micbias if present */ + if (mbhc->mbhc_cb->enable_mb_source) + mbhc->mbhc_cb->enable_mb_source(mbhc, false); + /* Disable HW FSM */ + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 0); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_BTN_ISRC_CTL, 0); + mbhc->extn_cable_hph_rem = false; + } + +done: + mbhc->in_swch_irq_handler = false; + WCD_MBHC_RSC_UNLOCK(mbhc); + pr_debug("%s: leave\n", __func__); +} + +static irqreturn_t wcd_mbhc_mech_plug_detect_irq(int irq, void *data) +{ + int r = IRQ_HANDLED; + struct wcd_mbhc *mbhc = data; + + pr_debug("%s: enter\n", __func__); + if (unlikely((mbhc->mbhc_cb->lock_sleep(mbhc, true)) == false)) { + pr_warn("%s: failed to hold suspend\n", __func__); + r = IRQ_NONE; + } else { + /* Call handler */ + wcd_mbhc_swch_irq_handler(mbhc); + mbhc->mbhc_cb->lock_sleep(mbhc, false); + } + pr_debug("%s: leave %d\n", __func__, r); + return r; +} + +int wcd_mbhc_get_button_mask(struct wcd_mbhc *mbhc) +{ + int mask = 0; + int btn; + + btn = mbhc->mbhc_cb->map_btn_code_to_num(mbhc->codec); + + switch (btn) { + case 0: + mask = SND_JACK_BTN_0; + break; + case 1: + mask = SND_JACK_BTN_1; + break; + case 2: + mask = SND_JACK_BTN_2; + break; + case 3: + mask = SND_JACK_BTN_3; + break; + case 4: + mask = SND_JACK_BTN_4; + break; + case 5: + mask = SND_JACK_BTN_5; + break; + default: + break; + } + + return mask; +} +EXPORT_SYMBOL(wcd_mbhc_get_button_mask); + +static void wcd_btn_lpress_fn(struct work_struct *work) +{ + struct delayed_work *dwork; + struct wcd_mbhc *mbhc; + s16 btn_result = 0; + + pr_debug("%s: Enter\n", __func__); + + dwork = to_delayed_work(work); + mbhc = container_of(dwork, struct wcd_mbhc, mbhc_btn_dwork); + + WCD_MBHC_REG_READ(WCD_MBHC_BTN_RESULT, btn_result); + if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADSET) { + pr_debug("%s: Reporting long button press event, btn_result: %d\n", + __func__, btn_result); + wcd_mbhc_jack_report(mbhc, &mbhc->button_jack, + mbhc->buttons_pressed, mbhc->buttons_pressed); + } + pr_debug("%s: leave\n", __func__); + mbhc->mbhc_cb->lock_sleep(mbhc, false); +} + +static bool wcd_mbhc_fw_validate(const void *data, size_t size) +{ + u32 cfg_offset; + struct wcd_mbhc_btn_detect_cfg *btn_cfg; + struct firmware_cal fw; + + fw.data = (void *)data; + fw.size = size; + + if (fw.size < WCD_MBHC_CAL_MIN_SIZE) + return false; + + /* + * Previous check guarantees that there is enough fw data up + * to num_btn + */ + btn_cfg = WCD_MBHC_CAL_BTN_DET_PTR(fw.data); + cfg_offset = (u32) ((void *) btn_cfg - (void *) fw.data); + if (fw.size < (cfg_offset + WCD_MBHC_CAL_BTN_SZ(btn_cfg))) + return false; + + return true; +} + +static irqreturn_t wcd_mbhc_btn_press_handler(int irq, void *data) +{ + struct wcd_mbhc *mbhc = data; + int mask; + unsigned long msec_val; + + pr_debug("%s: enter\n", __func__); + complete(&mbhc->btn_press_compl); + WCD_MBHC_RSC_LOCK(mbhc); + wcd_cancel_btn_work(mbhc); + if (wcd_swch_level_remove(mbhc)) { + pr_debug("%s: Switch level is low ", __func__); + goto done; + } + + mbhc->is_btn_press = true; + msec_val = jiffies_to_msecs(jiffies - mbhc->jiffies_atreport); + pr_debug("%s: msec_val = %ld\n", __func__, msec_val); + if (msec_val < MBHC_BUTTON_PRESS_THRESHOLD_MIN) { + pr_debug("%s: Too short, ignore button press\n", __func__); + goto done; + } + + /* If switch interrupt already kicked in, ignore button press */ + if (mbhc->in_swch_irq_handler) { + pr_debug("%s: Swtich level changed, ignore button press\n", + __func__); + goto done; + } + mask = wcd_mbhc_get_button_mask(mbhc); + if (mask == SND_JACK_BTN_0) + mbhc->btn_press_intr = true; + + if (mbhc->current_plug != MBHC_PLUG_TYPE_HEADSET) { + pr_debug("%s: Plug isn't headset, ignore button press\n", + __func__); + goto done; + } + mbhc->buttons_pressed |= mask; + mbhc->mbhc_cb->lock_sleep(mbhc, true); + if (schedule_delayed_work(&mbhc->mbhc_btn_dwork, + msecs_to_jiffies(400)) == 0) { + WARN(1, "Button pressed twice without release event\n"); + mbhc->mbhc_cb->lock_sleep(mbhc, false); + } +done: + pr_debug("%s: leave\n", __func__); + WCD_MBHC_RSC_UNLOCK(mbhc); + return IRQ_HANDLED; +} + +static irqreturn_t wcd_mbhc_release_handler(int irq, void *data) +{ + struct wcd_mbhc *mbhc = data; + int ret; + + pr_debug("%s: enter\n", __func__); + WCD_MBHC_RSC_LOCK(mbhc); + if (wcd_swch_level_remove(mbhc)) { + pr_debug("%s: Switch level is low ", __func__); + goto exit; + } + + if (mbhc->is_btn_press) { + mbhc->is_btn_press = false; + } else { + pr_debug("%s: This release is for fake btn press\n", __func__); + goto exit; + } + + /* + * If current plug is headphone then there is no chance to + * get btn release interrupt, so connected cable should be + * headset not headphone. + * For ADC MBHC, ADC_COMPLETE interrupt will be generated + * in this case. So skip the check here. + */ + if (mbhc->mbhc_detection_logic == WCD_DETECTION_LEGACY && + mbhc->current_plug == MBHC_PLUG_TYPE_HEADPHONE) { + wcd_mbhc_find_plug_and_report(mbhc, MBHC_PLUG_TYPE_HEADSET); + goto exit; + + } + if (mbhc->buttons_pressed & WCD_MBHC_JACK_BUTTON_MASK) { + ret = wcd_cancel_btn_work(mbhc); + if (ret == 0) { + pr_debug("%s: Reporting long button release event\n", + __func__); + wcd_mbhc_jack_report(mbhc, &mbhc->button_jack, + 0, mbhc->buttons_pressed); + } else { + if (mbhc->in_swch_irq_handler) { + pr_debug("%s: Switch irq kicked in, ignore\n", + __func__); + } else { + pr_debug("%s: Reporting btn press\n", + __func__); + wcd_mbhc_jack_report(mbhc, + &mbhc->button_jack, + mbhc->buttons_pressed, + mbhc->buttons_pressed); + pr_debug("%s: Reporting btn release\n", + __func__); + wcd_mbhc_jack_report(mbhc, + &mbhc->button_jack, + 0, mbhc->buttons_pressed); + } + } + mbhc->buttons_pressed &= ~WCD_MBHC_JACK_BUTTON_MASK; + } +exit: + pr_debug("%s: leave\n", __func__); + WCD_MBHC_RSC_UNLOCK(mbhc); + return IRQ_HANDLED; +} + +static irqreturn_t wcd_mbhc_hphl_ocp_irq(int irq, void *data) +{ + struct wcd_mbhc *mbhc = data; + int val; + + pr_debug("%s: received HPHL OCP irq\n", __func__); + if (mbhc) { + if (mbhc->mbhc_cb->hph_register_recovery) { + if (mbhc->mbhc_cb->hph_register_recovery(mbhc)) { + WCD_MBHC_REG_READ(WCD_MBHC_HPHR_OCP_STATUS, + val); + if ((val != -EINVAL) && val) + mbhc->is_hph_ocp_pending = true; + goto done; + } + } + + if (mbhc->hphlocp_cnt < OCP_ATTEMPT) { + mbhc->hphlocp_cnt++; + pr_debug("%s: retry, hphlocp_cnt: %d\n", __func__, + mbhc->hphlocp_cnt); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_OCP_FSM_EN, 0); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_OCP_FSM_EN, 1); + } else { + mbhc->mbhc_cb->irq_control(mbhc->codec, + mbhc->intr_ids->hph_left_ocp, + false); + mbhc->hph_status |= SND_JACK_OC_HPHL; + wcd_mbhc_jack_report(mbhc, &mbhc->headset_jack, + mbhc->hph_status, + WCD_MBHC_JACK_MASK); + } + } else { + pr_err("%s: Bad wcd9xxx_spmi private data\n", __func__); + } +done: + return IRQ_HANDLED; +} + +static irqreturn_t wcd_mbhc_hphr_ocp_irq(int irq, void *data) +{ + struct wcd_mbhc *mbhc = data; + + pr_debug("%s: received HPHR OCP irq\n", __func__); + + if (!mbhc) { + pr_err("%s: Bad mbhc private data\n", __func__); + goto done; + } + + if (mbhc->is_hph_ocp_pending) { + mbhc->is_hph_ocp_pending = false; + goto done; + } + + if (mbhc->mbhc_cb->hph_register_recovery) { + if (mbhc->mbhc_cb->hph_register_recovery(mbhc)) + /* register corruption, hence reset registers */ + goto done; + } + if (mbhc->hphrocp_cnt < OCP_ATTEMPT) { + mbhc->hphrocp_cnt++; + pr_debug("%s: retry, hphrocp_cnt: %d\n", __func__, + mbhc->hphrocp_cnt); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_OCP_FSM_EN, 0); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_OCP_FSM_EN, 1); + } else { + mbhc->mbhc_cb->irq_control(mbhc->codec, + mbhc->intr_ids->hph_right_ocp, + false); + mbhc->hph_status |= SND_JACK_OC_HPHR; + wcd_mbhc_jack_report(mbhc, &mbhc->headset_jack, + mbhc->hph_status, WCD_MBHC_JACK_MASK); + } +done: + return IRQ_HANDLED; +} + +static int wcd_mbhc_initialise(struct wcd_mbhc *mbhc) +{ + int ret = 0; + struct snd_soc_codec *codec = mbhc->codec; + + pr_debug("%s: enter\n", __func__); + WCD_MBHC_RSC_LOCK(mbhc); + + /* enable HS detection */ + if (mbhc->mbhc_cb->hph_pull_up_control_v2) + mbhc->mbhc_cb->hph_pull_up_control_v2(codec, + HS_PULLUP_I_DEFAULT); + else if (mbhc->mbhc_cb->hph_pull_up_control) + mbhc->mbhc_cb->hph_pull_up_control(codec, I_DEFAULT); + else + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HS_L_DET_PULL_UP_CTRL, 3); + + if (mbhc->mbhc_cfg->moisture_en && mbhc->mbhc_cb->mbhc_moisture_config + && !mbhc->mbhc_cfg->moisture_duty_cycle_en) + mbhc->mbhc_cb->mbhc_moisture_config(mbhc); + + /* + * For USB analog we need to override the switch configuration. + * Also, disable hph_l pull-up current source as HS_DET_L is driven + * by an external source + */ + if (mbhc->mbhc_cfg->enable_usbc_analog) { + if (mbhc->mbhc_cb->hph_pull_up_control_v2) + mbhc->mbhc_cb->hph_pull_up_control_v2(codec, + HS_PULLUP_I_OFF); + else if (mbhc->mbhc_cb->hph_pull_up_control) + mbhc->mbhc_cb->hph_pull_up_control(codec, I_OFF); + else + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HS_L_DET_PULL_UP_CTRL, + 0); + } + + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HPHL_PLUG_TYPE, mbhc->hphl_swh); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_GND_PLUG_TYPE, mbhc->gnd_swh); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_SW_HPH_LP_100K_TO_GND, 1); + if (mbhc->mbhc_cfg->gnd_det_en && mbhc->mbhc_cb->mbhc_gnd_det_ctrl) + mbhc->mbhc_cb->mbhc_gnd_det_ctrl(codec, true); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HS_L_DET_PULL_UP_COMP_CTRL, 1); + + /* + * Disable L_DET for USB-C analog audio to avoid spurious interrupts + * when a non-audio accessory is inserted. L_DET_EN sets to 1 when FSA + * I2C driver notifies that ANALOG_AUDIO_ADAPTER is inserted + */ + if (mbhc->mbhc_cfg->enable_usbc_analog) + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_L_DET_EN, 0); + else + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_L_DET_EN, 1); + + if (mbhc->mbhc_cfg->enable_usbc_analog) { + /* Insertion debounce set to 48ms */ + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_INSREM_DBNC, 4); + } else { + /* Insertion debounce set to 96ms */ + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_INSREM_DBNC, 6); + } + + /* Button Debounce set to 16ms */ + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_BTN_DBNC, 2); + + /* Enable micbias ramp */ + if (mbhc->mbhc_cb->mbhc_micb_ramp_control) + mbhc->mbhc_cb->mbhc_micb_ramp_control(codec, true); + /* enable bias */ + mbhc->mbhc_cb->mbhc_bias(codec, true); + /* enable MBHC clock */ + if (mbhc->mbhc_cb->clk_setup) { + if (mbhc->mbhc_cfg->enable_usbc_analog) + mbhc->mbhc_cb->clk_setup(codec, false); + else + mbhc->mbhc_cb->clk_setup(codec, true); + } + + /* program HS_VREF value */ + wcd_program_hs_vref(mbhc); + + wcd_program_btn_threshold(mbhc, false); + + + reinit_completion(&mbhc->btn_press_compl); + + WCD_MBHC_RSC_UNLOCK(mbhc); + pr_debug("%s: leave\n", __func__); + return ret; +} + +static void wcd_mbhc_fw_read(struct work_struct *work) +{ + struct delayed_work *dwork; + struct wcd_mbhc *mbhc; + struct snd_soc_codec *codec; + const struct firmware *fw; + struct firmware_cal *fw_data = NULL; + int ret = -1, retry = 0; + bool use_default_cal = false; + + dwork = to_delayed_work(work); + mbhc = container_of(dwork, struct wcd_mbhc, mbhc_firmware_dwork); + codec = mbhc->codec; + + while (retry < FW_READ_ATTEMPTS) { + retry++; + pr_debug("%s:Attempt %d to request MBHC firmware\n", + __func__, retry); + if (mbhc->mbhc_cb->get_hwdep_fw_cal) + fw_data = mbhc->mbhc_cb->get_hwdep_fw_cal(mbhc, + WCD9XXX_MBHC_CAL); + if (!fw_data) + ret = request_firmware(&fw, "wcd9320/wcd9320_mbhc.bin", + codec->dev); + /* + * if request_firmware and hwdep cal both fail then + * sleep for 4sec for the userspace to send data to kernel + * retry for few times before bailing out + */ + if ((ret != 0) && !fw_data) { + usleep_range(FW_READ_TIMEOUT, FW_READ_TIMEOUT + + WCD_MBHC_USLEEP_RANGE_MARGIN_US); + } else { + pr_debug("%s: MBHC Firmware read successful\n", + __func__); + break; + } + } + if (!fw_data) + pr_debug("%s: using request_firmware\n", __func__); + else + pr_debug("%s: using hwdep cal\n", __func__); + + if (ret != 0 && !fw_data) { + pr_err("%s: Cannot load MBHC firmware use default cal\n", + __func__); + use_default_cal = true; + } + if (!use_default_cal) { + const void *data; + size_t size; + + if (fw_data) { + data = fw_data->data; + size = fw_data->size; + } else { + data = fw->data; + size = fw->size; + } + if (wcd_mbhc_fw_validate(data, size) == false) { + pr_err("%s: Invalid MBHC cal data size use default cal\n", + __func__); + if (!fw_data) + release_firmware(fw); + } else { + if (fw_data) { + mbhc->mbhc_cfg->calibration = + (void *)fw_data->data; + mbhc->mbhc_cal = fw_data; + } else { + mbhc->mbhc_cfg->calibration = + (void *)fw->data; + mbhc->mbhc_fw = fw; + } + } + + } + + (void) wcd_mbhc_initialise(mbhc); +} + +static int wcd_mbhc_set_keycode(struct wcd_mbhc *mbhc) +{ + enum snd_jack_types type; + int i, ret, result = 0; + int *btn_key_code; + + btn_key_code = mbhc->mbhc_cfg->key_code; + + for (i = 0 ; i < WCD_MBHC_KEYCODE_NUM ; i++) { + if (btn_key_code[i] != 0) { + switch (i) { + case 0: + type = SND_JACK_BTN_0; + break; + case 1: + type = SND_JACK_BTN_1; + break; + case 2: + type = SND_JACK_BTN_2; + break; + case 3: + type = SND_JACK_BTN_3; + break; + case 4: + type = SND_JACK_BTN_4; + break; + case 5: + type = SND_JACK_BTN_5; + break; + default: + WARN_ONCE(1, "Wrong button number:%d\n", i); + result = -1; + return result; + } + ret = snd_jack_set_key(mbhc->button_jack.jack, + type, + btn_key_code[i]); + if (ret) { + pr_err("%s: Failed to set code for %d\n", + __func__, btn_key_code[i]); + result = -1; + return result; + } + input_set_capability( + mbhc->button_jack.jack->input_dev, + EV_KEY, btn_key_code[i]); + pr_debug("%s: set btn%d key code:%d\n", __func__, + i, btn_key_code[i]); + } + } + if (btn_key_code[0]) + mbhc->is_btn_already_regd = true; + return result; +} + +static int wcd_mbhc_usbc_ana_event_handler(struct notifier_block *nb, + unsigned long mode, void *ptr) +{ + struct wcd_mbhc *mbhc = container_of(nb, struct wcd_mbhc, fsa_nb); + + if (!mbhc) + return -EINVAL; + + dev_dbg(mbhc->codec->dev, "%s: mode = %lu\n", __func__, mode); + + if (mode == POWER_SUPPLY_TYPEC_SINK_AUDIO_ADAPTER) { + if (mbhc->mbhc_cb->clk_setup) + mbhc->mbhc_cb->clk_setup(mbhc->codec, true); + /* insertion detected, enable L_DET_EN */ + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_L_DET_EN, 1); + } + return 0; +} + +int wcd_mbhc_start(struct wcd_mbhc *mbhc, struct wcd_mbhc_config *mbhc_cfg) +{ + int rc = 0; + struct snd_soc_codec *codec; + struct snd_soc_card *card; + const char *usb_c_dt = "qcom,msm-mbhc-usbc-audio-supported"; + + if (!mbhc || !mbhc_cfg) + return -EINVAL; + + codec = mbhc->codec; + card = codec->component.card; + + /* update the mbhc config */ + mbhc->mbhc_cfg = mbhc_cfg; + + dev_dbg(mbhc->codec->dev, "%s: enter\n", __func__); + + /* check if USB C analog is defined on device tree */ + mbhc_cfg->enable_usbc_analog = 0; + if (of_find_property(card->dev->of_node, usb_c_dt, NULL)) { + rc = of_property_read_u32(card->dev->of_node, usb_c_dt, + &mbhc_cfg->enable_usbc_analog); + } + if (mbhc_cfg->enable_usbc_analog == 0 || rc != 0) { + dev_dbg(card->dev, + "%s: %s in dt node is missing or false\n", + __func__, usb_c_dt); + dev_dbg(card->dev, + "%s: skipping USB c analog configuration\n", __func__); + } + + /* Parse fsa switch handle */ + if (mbhc_cfg->enable_usbc_analog) { + dev_dbg(mbhc->codec->dev, "%s: usbc analog enabled\n", + __func__); + mbhc->swap_thr = GND_MIC_USBC_SWAP_THRESHOLD; + mbhc->fsa_np = of_parse_phandle(card->dev->of_node, + "fsa4480-i2c-handle", 0); + if (!mbhc->fsa_np) { + dev_err(card->dev, "%s: fsa4480 i2c node not found\n", + __func__); + rc = -EINVAL; + goto err; + } + } + + /* Set btn key code */ + if ((!mbhc->is_btn_already_regd) && wcd_mbhc_set_keycode(mbhc)) + pr_err("Set btn key code error!!!\n"); + + if (!mbhc->mbhc_cfg->read_fw_bin || + (mbhc->mbhc_cfg->read_fw_bin && mbhc->mbhc_fw) || + (mbhc->mbhc_cfg->read_fw_bin && mbhc->mbhc_cal)) { + rc = wcd_mbhc_initialise(mbhc); + if (rc) { + dev_err(card->dev, "%s: wcd mbhc initialize failed\n", + __func__); + goto err; + } + } else { + if (!mbhc->mbhc_fw || !mbhc->mbhc_cal) + schedule_delayed_work(&mbhc->mbhc_firmware_dwork, + usecs_to_jiffies(FW_READ_TIMEOUT)); + else + pr_err("%s: Skipping to read mbhc fw, 0x%pK %pK\n", + __func__, mbhc->mbhc_fw, mbhc->mbhc_cal); + } + + if (mbhc_cfg->enable_usbc_analog) { + mbhc->fsa_nb.notifier_call = wcd_mbhc_usbc_ana_event_handler; + mbhc->fsa_nb.priority = 0; + rc = fsa4480_reg_notifier(&mbhc->fsa_nb, mbhc->fsa_np); + } + + return rc; +err: + dev_dbg(mbhc->codec->dev, "%s: leave %d\n", __func__, rc); + return rc; +} +EXPORT_SYMBOL(wcd_mbhc_start); + +void wcd_mbhc_stop(struct wcd_mbhc *mbhc) +{ + pr_debug("%s: enter\n", __func__); + + if (mbhc->current_plug != MBHC_PLUG_TYPE_NONE) { + if (mbhc->mbhc_cb && mbhc->mbhc_cb->skip_imped_detect) + mbhc->mbhc_cb->skip_imped_detect(mbhc->codec); + } + mbhc->current_plug = MBHC_PLUG_TYPE_NONE; + mbhc->hph_status = 0; + if (mbhc->mbhc_cb && mbhc->mbhc_cb->irq_control) { + mbhc->mbhc_cb->irq_control(mbhc->codec, + mbhc->intr_ids->hph_left_ocp, + false); + mbhc->mbhc_cb->irq_control(mbhc->codec, + mbhc->intr_ids->hph_right_ocp, + false); + } + if (mbhc->mbhc_fw || mbhc->mbhc_cal) { + cancel_delayed_work_sync(&mbhc->mbhc_firmware_dwork); + if (!mbhc->mbhc_cal) + release_firmware(mbhc->mbhc_fw); + mbhc->mbhc_fw = NULL; + mbhc->mbhc_cal = NULL; + } + + if (mbhc->mbhc_cfg->enable_usbc_analog) + fsa4480_unreg_notifier(&mbhc->fsa_nb, mbhc->fsa_np); + + pr_debug("%s: leave\n", __func__); +} +EXPORT_SYMBOL(wcd_mbhc_stop); + +/* + * wcd_mbhc_init : initialize MBHC internal structures. + * + * NOTE: mbhc->mbhc_cfg is not YET configure so shouldn't be used + */ +int wcd_mbhc_init(struct wcd_mbhc *mbhc, struct snd_soc_codec *codec, + const struct wcd_mbhc_cb *mbhc_cb, + const struct wcd_mbhc_intr *mbhc_cdc_intr_ids, + struct wcd_mbhc_register *wcd_mbhc_regs, + bool impedance_det_en) +{ + int ret = 0; + int hph_swh = 0; + int gnd_swh = 0; + u32 hph_moist_config[3]; + struct snd_soc_card *card = codec->component.card; + const char *hph_switch = "qcom,msm-mbhc-hphl-swh"; + const char *gnd_switch = "qcom,msm-mbhc-gnd-swh"; + const char *hs_thre = "qcom,msm-mbhc-hs-mic-max-threshold-mv"; + const char *hph_thre = "qcom,msm-mbhc-hs-mic-min-threshold-mv"; + + pr_debug("%s: enter\n", __func__); + + ret = of_property_read_u32(card->dev->of_node, hph_switch, &hph_swh); + if (ret) { + dev_err(card->dev, + "%s: missing %s in dt node\n", __func__, hph_switch); + goto err; + } + + ret = of_property_read_u32(card->dev->of_node, gnd_switch, &gnd_swh); + if (ret) { + dev_err(card->dev, + "%s: missing %s in dt node\n", __func__, gnd_switch); + goto err; + } + + ret = of_property_read_u32(card->dev->of_node, hs_thre, + &(mbhc->hs_thr)); + if (ret) + dev_dbg(card->dev, + "%s: missing %s in dt node\n", __func__, hs_thre); + + ret = of_property_read_u32(card->dev->of_node, hph_thre, + &(mbhc->hph_thr)); + if (ret) + dev_dbg(card->dev, + "%s: missing %s in dt node\n", __func__, hph_thre); + + ret = of_property_read_u32_array(card->dev->of_node, + "qcom,msm-mbhc-moist-cfg", + hph_moist_config, 3); + if (ret) { + dev_dbg(card->dev, "%s: no qcom,msm-mbhc-moist-cfg in DT\n", + __func__); + mbhc->moist_vref = V_45_MV; + mbhc->moist_iref = I_3P0_UA; + mbhc->moist_rref = R_24_KOHM; + } else { + mbhc->moist_vref = hph_moist_config[0]; + mbhc->moist_iref = hph_moist_config[1]; + mbhc->moist_rref = hph_moist_config[2]; + } + + mbhc->in_swch_irq_handler = false; + mbhc->current_plug = MBHC_PLUG_TYPE_NONE; + mbhc->is_btn_press = false; + mbhc->codec = codec; + mbhc->intr_ids = mbhc_cdc_intr_ids; + mbhc->impedance_detect = impedance_det_en; + mbhc->hphl_swh = hph_swh; + mbhc->gnd_swh = gnd_swh; + mbhc->micbias_enable = false; + mbhc->mbhc_cb = mbhc_cb; + mbhc->btn_press_intr = false; + mbhc->is_hs_recording = false; + mbhc->is_extn_cable = false; + mbhc->extn_cable_hph_rem = false; + mbhc->hph_type = WCD_MBHC_HPH_NONE; + mbhc->wcd_mbhc_regs = wcd_mbhc_regs; + mbhc->swap_thr = GND_MIC_SWAP_THRESHOLD; + + if (mbhc->intr_ids == NULL) { + pr_err("%s: Interrupt mapping not provided\n", __func__); + return -EINVAL; + } + if (!mbhc->wcd_mbhc_regs) { + dev_err(codec->dev, "%s: mbhc registers are not defined\n", + __func__); + return -EINVAL; + } + + /* Check if IRQ and other required callbacks are defined or not */ + if (!mbhc_cb || !mbhc_cb->request_irq || !mbhc_cb->irq_control || + !mbhc_cb->free_irq || !mbhc_cb->map_btn_code_to_num || + !mbhc_cb->lock_sleep || !mbhc_cb->mbhc_bias || + !mbhc_cb->set_btn_thr) { + dev_err(codec->dev, "%s: required mbhc callbacks are not defined\n", + __func__); + return -EINVAL; + } + + /* No need to create new sound card jacks if is is already created */ + if (mbhc->headset_jack.jack == NULL) { + ret = snd_soc_card_jack_new(codec->component.card, + "Headset Jack", WCD_MBHC_JACK_MASK, + &mbhc->headset_jack, NULL, 0); + if (ret) { + pr_err("%s: Failed to create new jack\n", __func__); + return ret; + } + + ret = snd_soc_card_jack_new(codec->component.card, + "Button Jack", + WCD_MBHC_JACK_BUTTON_MASK, + &mbhc->button_jack, NULL, 0); + if (ret) { + pr_err("Failed to create new jack\n"); + return ret; + } + + ret = snd_jack_set_key(mbhc->button_jack.jack, + SND_JACK_BTN_0, + KEY_MEDIA); + if (ret) { + pr_err("%s: Failed to set code for btn-0\n", + __func__); + return ret; + } + + INIT_DELAYED_WORK(&mbhc->mbhc_firmware_dwork, + wcd_mbhc_fw_read); + INIT_DELAYED_WORK(&mbhc->mbhc_btn_dwork, wcd_btn_lpress_fn); + } + mutex_init(&mbhc->hphl_pa_lock); + mutex_init(&mbhc->hphr_pa_lock); + init_completion(&mbhc->btn_press_compl); + + /* Register event notifier */ + mbhc->nblock.notifier_call = wcd_event_notify; + if (mbhc->mbhc_cb->register_notifier) { + ret = mbhc->mbhc_cb->register_notifier(mbhc, &mbhc->nblock, + true); + if (ret) { + pr_err("%s: Failed to register notifier %d\n", + __func__, ret); + return ret; + } + } + + init_waitqueue_head(&mbhc->wait_btn_press); + mutex_init(&mbhc->codec_resource_lock); + + switch (mbhc->mbhc_detection_logic) { + case WCD_DETECTION_LEGACY: + wcd_mbhc_legacy_init(mbhc); + break; + case WCD_DETECTION_ADC: + wcd_mbhc_adc_init(mbhc); + break; + default: + pr_err("%s: Unknown detection logic type %d\n", + __func__, mbhc->mbhc_detection_logic); + break; + } + + if (!mbhc->mbhc_fn || + !mbhc->mbhc_fn->wcd_mbhc_hs_ins_irq || + !mbhc->mbhc_fn->wcd_mbhc_hs_rem_irq || + !mbhc->mbhc_fn->wcd_mbhc_detect_plug_type || + !mbhc->mbhc_fn->wcd_cancel_hs_detect_plug) { + pr_err("%s: mbhc function pointer is NULL\n", __func__); + goto err_mbhc_sw_irq; + } + ret = mbhc->mbhc_cb->request_irq(codec, mbhc->intr_ids->mbhc_sw_intr, + wcd_mbhc_mech_plug_detect_irq, + "mbhc sw intr", mbhc); + if (ret) { + pr_err("%s: Failed to request irq %d, ret = %d\n", __func__, + mbhc->intr_ids->mbhc_sw_intr, ret); + goto err_mbhc_sw_irq; + } + + ret = mbhc->mbhc_cb->request_irq(codec, + mbhc->intr_ids->mbhc_btn_press_intr, + wcd_mbhc_btn_press_handler, + "Button Press detect", mbhc); + if (ret) { + pr_err("%s: Failed to request irq %d\n", __func__, + mbhc->intr_ids->mbhc_btn_press_intr); + goto err_btn_press_irq; + } + + ret = mbhc->mbhc_cb->request_irq(codec, + mbhc->intr_ids->mbhc_btn_release_intr, + wcd_mbhc_release_handler, + "Button Release detect", mbhc); + if (ret) { + pr_err("%s: Failed to request irq %d\n", __func__, + mbhc->intr_ids->mbhc_btn_release_intr); + goto err_btn_release_irq; + } + + ret = mbhc->mbhc_cb->request_irq(codec, + mbhc->intr_ids->mbhc_hs_ins_intr, + mbhc->mbhc_fn->wcd_mbhc_hs_ins_irq, + "Elect Insert", mbhc); + if (ret) { + pr_err("%s: Failed to request irq %d\n", __func__, + mbhc->intr_ids->mbhc_hs_ins_intr); + goto err_mbhc_hs_ins_irq; + } + mbhc->mbhc_cb->irq_control(codec, mbhc->intr_ids->mbhc_hs_ins_intr, + false); + clear_bit(WCD_MBHC_ELEC_HS_INS, &mbhc->intr_status); + + ret = mbhc->mbhc_cb->request_irq(codec, + mbhc->intr_ids->mbhc_hs_rem_intr, + mbhc->mbhc_fn->wcd_mbhc_hs_rem_irq, + "Elect Remove", mbhc); + if (ret) { + pr_err("%s: Failed to request irq %d\n", __func__, + mbhc->intr_ids->mbhc_hs_rem_intr); + goto err_mbhc_hs_rem_irq; + } + mbhc->mbhc_cb->irq_control(codec, mbhc->intr_ids->mbhc_hs_rem_intr, + false); + clear_bit(WCD_MBHC_ELEC_HS_REM, &mbhc->intr_status); + + ret = mbhc->mbhc_cb->request_irq(codec, mbhc->intr_ids->hph_left_ocp, + wcd_mbhc_hphl_ocp_irq, "HPH_L OCP detect", + mbhc); + if (ret) { + pr_err("%s: Failed to request irq %d\n", __func__, + mbhc->intr_ids->hph_left_ocp); + goto err_hphl_ocp_irq; + } + + ret = mbhc->mbhc_cb->request_irq(codec, mbhc->intr_ids->hph_right_ocp, + wcd_mbhc_hphr_ocp_irq, "HPH_R OCP detect", + mbhc); + if (ret) { + pr_err("%s: Failed to request irq %d\n", __func__, + mbhc->intr_ids->hph_right_ocp); + goto err_hphr_ocp_irq; + } + + mbhc->deinit_in_progress = false; + pr_debug("%s: leave ret %d\n", __func__, ret); + return ret; + +err_hphr_ocp_irq: + mbhc->mbhc_cb->free_irq(codec, mbhc->intr_ids->hph_left_ocp, mbhc); +err_hphl_ocp_irq: + mbhc->mbhc_cb->free_irq(codec, mbhc->intr_ids->mbhc_hs_rem_intr, mbhc); +err_mbhc_hs_rem_irq: + mbhc->mbhc_cb->free_irq(codec, mbhc->intr_ids->mbhc_hs_ins_intr, mbhc); +err_mbhc_hs_ins_irq: + mbhc->mbhc_cb->free_irq(codec, mbhc->intr_ids->mbhc_btn_release_intr, + mbhc); +err_btn_release_irq: + mbhc->mbhc_cb->free_irq(codec, mbhc->intr_ids->mbhc_btn_press_intr, + mbhc); +err_btn_press_irq: + mbhc->mbhc_cb->free_irq(codec, mbhc->intr_ids->mbhc_sw_intr, mbhc); +err_mbhc_sw_irq: + if (mbhc->mbhc_cb->register_notifier) + mbhc->mbhc_cb->register_notifier(mbhc, &mbhc->nblock, false); + mutex_destroy(&mbhc->codec_resource_lock); +err: + pr_debug("%s: leave ret %d\n", __func__, ret); + return ret; +} +EXPORT_SYMBOL(wcd_mbhc_init); + +void wcd_mbhc_deinit(struct wcd_mbhc *mbhc) +{ + struct snd_soc_codec *codec = mbhc->codec; + + mbhc->mbhc_cb->free_irq(codec, mbhc->intr_ids->mbhc_sw_intr, mbhc); + mbhc->mbhc_cb->free_irq(codec, mbhc->intr_ids->mbhc_btn_press_intr, + mbhc); + mbhc->mbhc_cb->free_irq(codec, mbhc->intr_ids->mbhc_btn_release_intr, + mbhc); + mbhc->mbhc_cb->free_irq(codec, mbhc->intr_ids->mbhc_hs_ins_intr, mbhc); + mbhc->mbhc_cb->free_irq(codec, mbhc->intr_ids->mbhc_hs_rem_intr, mbhc); + mbhc->mbhc_cb->free_irq(codec, mbhc->intr_ids->hph_left_ocp, mbhc); + mbhc->mbhc_cb->free_irq(codec, mbhc->intr_ids->hph_right_ocp, mbhc); + if (mbhc->mbhc_cb && mbhc->mbhc_cb->register_notifier) + mbhc->mbhc_cb->register_notifier(mbhc, &mbhc->nblock, false); + if (mbhc->mbhc_fn->wcd_cancel_hs_detect_plug) { + WCD_MBHC_RSC_LOCK(mbhc); + mbhc->mbhc_fn->wcd_cancel_hs_detect_plug(mbhc, + &mbhc->correct_plug_swch); + WCD_MBHC_RSC_UNLOCK(mbhc); + } + mutex_destroy(&mbhc->codec_resource_lock); + mutex_destroy(&mbhc->hphl_pa_lock); + mutex_destroy(&mbhc->hphr_pa_lock); +} +EXPORT_SYMBOL(wcd_mbhc_deinit); + +static int __init mbhc_init(void) +{ + return 0; +} + +static void __exit mbhc_exit(void) +{ +} + +module_init(mbhc_init); +module_exit(mbhc_exit); + +MODULE_DESCRIPTION("wcd MBHC v2 module"); +MODULE_LICENSE("GPL v2"); diff --git a/audio-kernel/asoc/codecs/wcd-mbhc-v2.h b/audio-kernel/asoc/codecs/wcd-mbhc-v2.h new file mode 100644 index 000000000..fafac1408 --- /dev/null +++ b/audio-kernel/asoc/codecs/wcd-mbhc-v2.h @@ -0,0 +1,618 @@ +/* Copyright (c) 2014-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 __WCD_MBHC_V2_H__ +#define __WCD_MBHC_V2_H__ + +#include +#include +#include +#include "wcdcal-hwdep.h" +#include + +#define TOMBAK_MBHC_NC 0 +#define TOMBAK_MBHC_NO 1 +#define WCD_MBHC_DEF_BUTTONS 8 +#define WCD_MBHC_KEYCODE_NUM 8 +#define WCD_MBHC_USLEEP_RANGE_MARGIN_US 100 +#define WCD_MBHC_THR_HS_MICB_MV 2700 +/* z value defined in Ohms */ +#define WCD_MONO_HS_MIN_THR 2 +#define WCD_MBHC_STRINGIFY(s) __stringify(s) + +#define WCD_MBHC_REGISTER(rid, rreg, rmask, rshift, rinvert) \ +{ .id = rid, .reg = rreg, .mask = rmask, .offset = rshift, .invert = rinvert } + +#define WCD_MBHC_RSC_LOCK(mbhc) \ +{ \ + pr_debug("%s: Acquiring BCL\n", __func__); \ + mutex_lock(&mbhc->codec_resource_lock); \ + pr_debug("%s: Acquiring BCL done\n", __func__); \ +} + +#define WCD_MBHC_RSC_UNLOCK(mbhc) \ +{ \ + pr_debug("%s: Release BCL\n", __func__); \ + mutex_unlock(&mbhc->codec_resource_lock); \ +} + +#define WCD_MBHC_RSC_ASSERT_LOCKED(mbhc) \ +{ \ + WARN_ONCE(!mutex_is_locked(&mbhc->codec_resource_lock), \ + "%s: BCL should have acquired\n", __func__); \ +} + +/* + * Macros to update and read mbhc register bits. Check for + * "0" before updating or reading the register, because it + * is possible that one codec wants to write to that bit and + * other codec does not. + */ +#define WCD_MBHC_REG_UPDATE_BITS(function, val) \ +do { \ + if (mbhc->wcd_mbhc_regs[function].reg) { \ + snd_soc_update_bits(mbhc->codec, \ + mbhc->wcd_mbhc_regs[function].reg, \ + mbhc->wcd_mbhc_regs[function].mask, \ + val << (mbhc->wcd_mbhc_regs[function].offset)); \ + } \ +} while (0) + +#define WCD_MBHC_REG_READ(function, val) \ +do { \ + if (mbhc->wcd_mbhc_regs[function].reg) { \ + val = (((snd_soc_read(mbhc->codec, \ + mbhc->wcd_mbhc_regs[function].reg)) & \ + (mbhc->wcd_mbhc_regs[function].mask)) >> \ + (mbhc->wcd_mbhc_regs[function].offset)); \ + } else { \ + val = -EINVAL; \ + } \ +} while (0) + +#define WCD_MBHC_CAL_SIZE(buttons, rload) ( \ + sizeof(struct wcd_mbhc_general_cfg) + \ + sizeof(struct wcd_mbhc_plug_detect_cfg) + \ + ((sizeof(s16) + sizeof(s16)) * buttons) + \ + sizeof(struct wcd_mbhc_plug_type_cfg) + \ + sizeof(struct wcd_mbhc_btn_detect_cfg) + \ + sizeof(struct wcd_mbhc_imped_detect_cfg) + \ + ((sizeof(u16) + sizeof(u16)) * rload) \ + ) + +#define WCD_MBHC_CAL_GENERAL_PTR(cali) ( \ + (struct wcd_mbhc_general_cfg *) cali) +#define WCD_MBHC_CAL_PLUG_DET_PTR(cali) ( \ + (struct wcd_mbhc_plug_detect_cfg *) \ + &(WCD_MBHC_CAL_GENERAL_PTR(cali)[1])) +#define WCD_MBHC_CAL_PLUG_TYPE_PTR(cali) ( \ + (struct wcd_mbhc_plug_type_cfg *) \ + &(WCD_MBHC_CAL_PLUG_DET_PTR(cali)[1])) +#define WCD_MBHC_CAL_BTN_DET_PTR(cali) ( \ + (struct wcd_mbhc_btn_detect_cfg *) \ + &(WCD_MBHC_CAL_PLUG_TYPE_PTR(cali)[1])) +#define WCD_MBHC_CAL_IMPED_DET_PTR(cali) ( \ + (struct wcd_mbhc_imped_detect_cfg *) \ + (((void *)&WCD_MBHC_CAL_BTN_DET_PTR(cali)[1]) + \ + (WCD_MBHC_CAL_BTN_DET_PTR(cali)->num_btn * \ + (sizeof(WCD_MBHC_CAL_BTN_DET_PTR(cali)->_v_btn_low[0]) + \ + sizeof(WCD_MBHC_CAL_BTN_DET_PTR(cali)->_v_btn_high[0])))) \ + ) + +#define WCD_MBHC_CAL_MIN_SIZE ( \ + sizeof(struct wcd_mbhc_general_cfg) + \ + sizeof(struct wcd_mbhc_plug_detect_cfg) + \ + sizeof(struct wcd_mbhc_plug_type_cfg) + \ + sizeof(struct wcd_mbhc_btn_detect_cfg) + \ + sizeof(struct wcd_mbhc_imped_detect_cfg) + \ + (sizeof(u16)*2) \ + ) + +#define WCD_MBHC_CAL_BTN_SZ(cfg_ptr) ( \ + sizeof(struct wcd_mbhc_btn_detect_cfg) + \ + (cfg_ptr->num_btn * (sizeof(cfg_ptr->_v_btn_low[0]) + \ + sizeof(cfg_ptr->_v_btn_high[0])))) + +#define WCD_MBHC_CAL_IMPED_MIN_SZ ( \ + sizeof(struct wcd_mbhc_imped_detect_cfg) + sizeof(u16) * 2) + +#define WCD_MBHC_CAL_IMPED_SZ(cfg_ptr) ( \ + sizeof(struct wcd_mbhc_imped_detect_cfg) + \ + (cfg_ptr->_n_rload * \ + (sizeof(cfg_ptr->_rload[0]) + sizeof(cfg_ptr->_alpha[0])))) + +#define WCD_MBHC_JACK_MASK (SND_JACK_HEADSET | SND_JACK_OC_HPHL | \ + SND_JACK_OC_HPHR | SND_JACK_LINEOUT | \ + SND_JACK_MECHANICAL | SND_JACK_MICROPHONE2 | \ + SND_JACK_UNSUPPORTED) + +#define WCD_MBHC_JACK_BUTTON_MASK (SND_JACK_BTN_0 | SND_JACK_BTN_1 | \ + SND_JACK_BTN_2 | SND_JACK_BTN_3 | \ + SND_JACK_BTN_4 | SND_JACK_BTN_5) +#define OCP_ATTEMPT 20 +#define HS_DETECT_PLUG_TIME_MS (3 * 1000) +#define SPECIAL_HS_DETECT_TIME_MS (2 * 1000) +#define MBHC_BUTTON_PRESS_THRESHOLD_MIN 250 +#define GND_MIC_SWAP_THRESHOLD 4 +#define GND_MIC_USBC_SWAP_THRESHOLD 2 +#define WCD_FAKE_REMOVAL_MIN_PERIOD_MS 100 +#define HS_VREF_MIN_VAL 1400 +#define FW_READ_ATTEMPTS 15 +#define FW_READ_TIMEOUT 4000000 +#define FAKE_REM_RETRY_ATTEMPTS 3 +#define MAX_IMPED 60000 + +#define WCD_MBHC_BTN_PRESS_COMPL_TIMEOUT_MS 50 +#define ANC_DETECT_RETRY_CNT 7 +#define WCD_MBHC_SPL_HS_CNT 1 + +enum wcd_mbhc_detect_logic { + WCD_DETECTION_LEGACY, + WCD_DETECTION_ADC, +}; + +enum wcd_mbhc_cs_mb_en_flag { + WCD_MBHC_EN_CS = 0, + WCD_MBHC_EN_MB, + WCD_MBHC_EN_PULLUP, + WCD_MBHC_EN_NONE, +}; + +enum { + WCD_MBHC_ELEC_HS_INS, + WCD_MBHC_ELEC_HS_REM, +}; + +struct wcd_mbhc; +enum wcd_mbhc_register_function { + WCD_MBHC_L_DET_EN, + WCD_MBHC_GND_DET_EN, + WCD_MBHC_MECH_DETECTION_TYPE, + WCD_MBHC_MIC_CLAMP_CTL, + WCD_MBHC_ELECT_DETECTION_TYPE, + WCD_MBHC_HS_L_DET_PULL_UP_CTRL, + WCD_MBHC_HS_L_DET_PULL_UP_COMP_CTRL, + WCD_MBHC_HPHL_PLUG_TYPE, + WCD_MBHC_GND_PLUG_TYPE, + WCD_MBHC_SW_HPH_LP_100K_TO_GND, + WCD_MBHC_ELECT_SCHMT_ISRC, + WCD_MBHC_FSM_EN, + WCD_MBHC_INSREM_DBNC, + WCD_MBHC_BTN_DBNC, + WCD_MBHC_HS_VREF, + WCD_MBHC_HS_COMP_RESULT, + WCD_MBHC_IN2P_CLAMP_STATE, + WCD_MBHC_MIC_SCHMT_RESULT, + WCD_MBHC_HPHL_SCHMT_RESULT, + WCD_MBHC_HPHR_SCHMT_RESULT, + WCD_MBHC_OCP_FSM_EN, + WCD_MBHC_BTN_RESULT, + WCD_MBHC_BTN_ISRC_CTL, + WCD_MBHC_ELECT_RESULT, + WCD_MBHC_MICB_CTRL, /* Pull-up and micb control */ + WCD_MBHC_HPH_CNP_WG_TIME, + WCD_MBHC_HPHR_PA_EN, + WCD_MBHC_HPHL_PA_EN, + WCD_MBHC_HPH_PA_EN, + WCD_MBHC_SWCH_LEVEL_REMOVE, + WCD_MBHC_PULLDOWN_CTRL, + WCD_MBHC_ANC_DET_EN, + WCD_MBHC_FSM_STATUS, + WCD_MBHC_MUX_CTL, + WCD_MBHC_MOISTURE_STATUS, + WCD_MBHC_HPHR_GND, + WCD_MBHC_HPHL_GND, + WCD_MBHC_HPHL_OCP_DET_EN, + WCD_MBHC_HPHR_OCP_DET_EN, + WCD_MBHC_HPHL_OCP_STATUS, + WCD_MBHC_HPHR_OCP_STATUS, + WCD_MBHC_ADC_EN, + WCD_MBHC_ADC_COMPLETE, + WCD_MBHC_ADC_TIMEOUT, + WCD_MBHC_ADC_RESULT, + WCD_MBHC_MICB2_VOUT, + WCD_MBHC_ADC_MODE, + WCD_MBHC_DETECTION_DONE, + WCD_MBHC_ELECT_ISRC_EN, + WCD_MBHC_REG_FUNC_MAX, +}; + +enum wcd_mbhc_plug_type { + MBHC_PLUG_TYPE_INVALID = -1, + MBHC_PLUG_TYPE_NONE, + MBHC_PLUG_TYPE_HEADSET, + MBHC_PLUG_TYPE_HEADPHONE, + MBHC_PLUG_TYPE_HIGH_HPH, + MBHC_PLUG_TYPE_GND_MIC_SWAP, + MBHC_PLUG_TYPE_ANC_HEADPHONE, +}; + +enum pa_dac_ack_flags { + WCD_MBHC_HPHL_PA_OFF_ACK = 0, + WCD_MBHC_HPHR_PA_OFF_ACK, +}; + +enum anc_ack_flags { + WCD_MBHC_ANC0_OFF_ACK = 0, + WCD_MBHC_ANC1_OFF_ACK, +}; + +enum wcd_mbhc_btn_det_mem { + WCD_MBHC_BTN_DET_V_BTN_LOW, + WCD_MBHC_BTN_DET_V_BTN_HIGH +}; + +enum { + MIC_BIAS_1 = 1, + MIC_BIAS_2, + MIC_BIAS_3, + MIC_BIAS_4 +}; + +enum { + MICB_PULLUP_ENABLE, + MICB_PULLUP_DISABLE, + MICB_ENABLE, + MICB_DISABLE, +}; + +enum { + MBHC_COMMON_MICB_PRECHARGE, + MBHC_COMMON_MICB_SET_VAL, + MBHC_COMMON_MICB_TAIL_CURR, +}; + +enum wcd_notify_event { + WCD_EVENT_INVALID, + /* events for micbias ON and OFF */ + WCD_EVENT_PRE_MICBIAS_2_OFF, + WCD_EVENT_POST_MICBIAS_2_OFF, + WCD_EVENT_PRE_MICBIAS_2_ON, + WCD_EVENT_POST_MICBIAS_2_ON, + WCD_EVENT_PRE_DAPM_MICBIAS_2_OFF, + WCD_EVENT_POST_DAPM_MICBIAS_2_OFF, + WCD_EVENT_PRE_DAPM_MICBIAS_2_ON, + WCD_EVENT_POST_DAPM_MICBIAS_2_ON, + /* events for PA ON and OFF */ + WCD_EVENT_PRE_HPHL_PA_ON, + WCD_EVENT_POST_HPHL_PA_OFF, + WCD_EVENT_PRE_HPHR_PA_ON, + WCD_EVENT_POST_HPHR_PA_OFF, + WCD_EVENT_PRE_HPHL_PA_OFF, + WCD_EVENT_PRE_HPHR_PA_OFF, + WCD_EVENT_OCP_OFF, + WCD_EVENT_OCP_ON, + WCD_EVENT_LAST, +}; + +enum wcd_mbhc_event_state { + WCD_MBHC_EVENT_PA_HPHL, + WCD_MBHC_EVENT_PA_HPHR, +}; + +struct wcd_mbhc_general_cfg { + u8 t_ldoh; + u8 t_bg_fast_settle; + u8 t_shutdown_plug_rem; + u8 mbhc_nsa; + u8 mbhc_navg; + u8 v_micbias_l; + u8 v_micbias; + u8 mbhc_reserved; + u16 settle_wait; + u16 t_micbias_rampup; + u16 t_micbias_rampdown; + u16 t_supply_bringup; +} __packed; + +struct wcd_mbhc_plug_detect_cfg { + u32 mic_current; + u32 hph_current; + u16 t_mic_pid; + u16 t_ins_complete; + u16 t_ins_retry; + u16 v_removal_delta; + u8 micbias_slow_ramp; + u8 reserved0; + u8 reserved1; + u8 reserved2; +} __packed; + +struct wcd_mbhc_plug_type_cfg { + u8 av_detect; + u8 mono_detect; + u8 num_ins_tries; + u8 reserved0; + s16 v_no_mic; + s16 v_av_min; + s16 v_av_max; + s16 v_hs_min; + s16 v_hs_max; + u16 reserved1; +} __packed; + +struct wcd_mbhc_btn_detect_cfg { + s8 c[8]; + u8 nc; + u8 n_meas; + u8 mbhc_nsc; + u8 n_btn_meas; + u8 n_btn_con; + u8 num_btn; + u8 reserved0; + u8 reserved1; + u16 t_poll; + u16 t_bounce_wait; + u16 t_rel_timeout; + s16 v_btn_press_delta_sta; + s16 v_btn_press_delta_cic; + u16 t_btn0_timeout; + s16 _v_btn_low[0]; /* v_btn_low[num_btn] */ + s16 _v_btn_high[0]; /* v_btn_high[num_btn] */ + u8 _n_ready[2]; + u8 _n_cic[2]; + u8 _gain[2]; +} __packed; + +struct wcd_mbhc_imped_detect_cfg { + u8 _hs_imped_detect; + u8 _n_rload; + u8 _hph_keep_on; + u8 _repeat_rload_calc; + u16 _t_dac_ramp_time; + u16 _rhph_high; + u16 _rhph_low; + u16 _rload[0]; /* rload[n_rload] */ + u16 _alpha[0]; /* alpha[n_rload] */ + u16 _beta[3]; +} __packed; + +enum wcd_mbhc_hph_type { + WCD_MBHC_HPH_NONE = 0, + WCD_MBHC_HPH_MONO, + WCD_MBHC_HPH_STEREO, +}; + +/* + * These enum definitions are directly mapped to the register + * definitions + */ +enum mbhc_moisture_vref { + V_OFF, + V_45_MV, + V_100_MV, + V_225_MV, +}; + +enum mbhc_hs_pullup_iref { + I_DEFAULT = -1, + I_OFF = 0, + I_1P0_UA, + I_2P0_UA, + I_3P0_UA, +}; + +enum mbhc_hs_pullup_iref_v2 { + HS_PULLUP_I_DEFAULT = -1, + HS_PULLUP_I_3P0_UA = 0, + HS_PULLUP_I_2P25_UA, + HS_PULLUP_I_1P5_UA, + HS_PULLUP_I_0P75_UA, + HS_PULLUP_I_1P125_UA = 0x05, + HS_PULLUP_I_0P375_UA = 0x07, + HS_PULLUP_I_2P0_UA, + HS_PULLUP_I_1P0_UA = 0x0A, + HS_PULLUP_I_0P5_UA, + HS_PULLUP_I_0P25_UA = 0x0F, + HS_PULLUP_I_0P125_UA = 0x17, + HS_PULLUP_I_OFF, +}; + +enum mbhc_moisture_rref { + R_OFF, + R_24_KOHM, + R_84_KOHM, + R_184_KOHM, +}; + +struct wcd_mbhc_config { + bool read_fw_bin; + void *calibration; + bool detect_extn_cable; + bool mono_stero_detection; + bool (*swap_gnd_mic)(struct snd_soc_codec *codec, bool active); + bool hs_ext_micbias; + bool gnd_det_en; + int key_code[WCD_MBHC_KEYCODE_NUM]; + uint32_t linein_th; + bool moisture_en; + int mbhc_micbias; + int anc_micbias; + bool enable_anc_mic_detect; + u32 enable_usbc_analog; + bool moisture_duty_cycle_en; +}; + +struct wcd_mbhc_intr { + int mbhc_sw_intr; + int mbhc_btn_press_intr; + int mbhc_btn_release_intr; + int mbhc_hs_ins_intr; + int mbhc_hs_rem_intr; + int hph_left_ocp; + int hph_right_ocp; +}; + +struct wcd_mbhc_register { + const char *id; + u16 reg; + u8 mask; + u8 offset; + u8 invert; +}; + +struct wcd_mbhc_cb { + int (*enable_mb_source)(struct wcd_mbhc *, bool); + void (*trim_btn_reg)(struct snd_soc_codec *); + void (*compute_impedance)(struct wcd_mbhc *, uint32_t *, uint32_t *); + void (*set_micbias_value)(struct snd_soc_codec *); + void (*set_auto_zeroing)(struct snd_soc_codec *, bool); + struct firmware_cal * (*get_hwdep_fw_cal)(struct wcd_mbhc *, + enum wcd_cal_type); + void (*set_cap_mode)(struct snd_soc_codec *, bool, bool); + int (*register_notifier)(struct wcd_mbhc *, + struct notifier_block *nblock, + bool enable); + int (*request_irq)(struct snd_soc_codec *, + int, irq_handler_t, const char *, void *); + void (*irq_control)(struct snd_soc_codec *, + int irq, bool enable); + int (*free_irq)(struct snd_soc_codec *, + int irq, void *); + void (*clk_setup)(struct snd_soc_codec *, bool); + int (*map_btn_code_to_num)(struct snd_soc_codec *); + bool (*lock_sleep)(struct wcd_mbhc *, bool); + bool (*micbias_enable_status)(struct wcd_mbhc *, int); + void (*mbhc_bias)(struct snd_soc_codec *, bool); + void (*mbhc_common_micb_ctrl)(struct snd_soc_codec *, + int event, bool); + void (*micb_internal)(struct snd_soc_codec *, + int micb_num, bool); + bool (*hph_pa_on_status)(struct snd_soc_codec *); + void (*set_btn_thr)(struct snd_soc_codec *, s16 *, s16 *, + int num_btn, bool); + void (*hph_pull_up_control)(struct snd_soc_codec *, + enum mbhc_hs_pullup_iref); + int (*mbhc_micbias_control)(struct snd_soc_codec *, int, int req); + void (*mbhc_micb_ramp_control)(struct snd_soc_codec *, bool); + void (*skip_imped_detect)(struct snd_soc_codec *); + bool (*extn_use_mb)(struct snd_soc_codec *); + int (*mbhc_micb_ctrl_thr_mic)(struct snd_soc_codec *, int, bool); + void (*mbhc_gnd_det_ctrl)(struct snd_soc_codec *, bool); + void (*hph_pull_down_ctrl)(struct snd_soc_codec *, bool); + void (*mbhc_moisture_config)(struct wcd_mbhc *); + bool (*hph_register_recovery)(struct wcd_mbhc *); + void (*update_anc_state)(struct snd_soc_codec *codec, + bool enable, int anc_num); + bool (*is_anc_on)(struct wcd_mbhc *mbhc); + void (*hph_pull_up_control_v2)(struct snd_soc_codec *, int); + bool (*mbhc_get_moisture_status)(struct wcd_mbhc *); + void (*mbhc_moisture_polling_ctrl)(struct wcd_mbhc *, bool); + void (*mbhc_moisture_detect_en)(struct wcd_mbhc *, bool); +}; + +struct wcd_mbhc_fn { + irqreturn_t (*wcd_mbhc_hs_ins_irq)(int irq, void *data); + irqreturn_t (*wcd_mbhc_hs_rem_irq)(int irq, void *data); + void (*wcd_mbhc_detect_plug_type)(struct wcd_mbhc *mbhc); + bool (*wcd_mbhc_detect_anc_plug_type)(struct wcd_mbhc *mbhc); + void (*wcd_cancel_hs_detect_plug)(struct wcd_mbhc *mbhc, + struct work_struct *work); +}; + +struct wcd_mbhc { + /* Delayed work to report long button press */ + struct delayed_work mbhc_btn_dwork; + int buttons_pressed; + struct wcd_mbhc_config *mbhc_cfg; + const struct wcd_mbhc_cb *mbhc_cb; + + u32 hph_status; /* track headhpone status */ + u8 hphlocp_cnt; /* headphone left ocp retry */ + u8 hphrocp_cnt; /* headphone right ocp retry */ + + wait_queue_head_t wait_btn_press; + bool is_btn_press; + u8 current_plug; + bool in_swch_irq_handler; + bool hphl_swh; /*track HPHL switch NC / NO */ + bool gnd_swh; /*track GND switch NC / NO */ + u32 hs_thr; + u32 hph_thr; + u32 swap_thr; + u32 moist_vref; + u32 moist_iref; + u32 moist_rref; + u8 micbias1_cap_mode; /* track ext cap setting */ + u8 micbias2_cap_mode; /* track ext cap setting */ + bool hs_detect_work_stop; + bool micbias_enable; + bool btn_press_intr; + bool is_hs_recording; + bool is_extn_cable; + bool skip_imped_detection; + bool is_btn_already_regd; + bool extn_cable_hph_rem; + + struct snd_soc_codec *codec; + /* Work to perform MBHC Firmware Read */ + struct delayed_work mbhc_firmware_dwork; + const struct firmware *mbhc_fw; + struct firmware_cal *mbhc_cal; + + /* track PA/DAC state to sync with userspace */ + unsigned long hph_pa_dac_state; + unsigned long hph_anc_state; + unsigned long event_state; + unsigned long jiffies_atreport; + + /* impedance of hphl and hphr */ + uint32_t zl, zr; + bool impedance_detect; + + /* Holds type of Headset - Mono/Stereo */ + enum wcd_mbhc_hph_type hph_type; + + struct snd_soc_jack headset_jack; + struct snd_soc_jack button_jack; + struct mutex codec_resource_lock; + + /* Holds codec specific interrupt mapping */ + const struct wcd_mbhc_intr *intr_ids; + + /* Work to correct accessory type */ + struct work_struct correct_plug_swch; + struct notifier_block nblock; + + struct wcd_mbhc_register *wcd_mbhc_regs; + + struct completion btn_press_compl; + struct mutex hphl_pa_lock; + struct mutex hphr_pa_lock; + bool deinit_in_progress; + + /* Holds mbhc detection method - ADC/Legacy */ + unsigned int mbhc_detection_logic; + + unsigned long intr_status; + bool is_hph_ocp_pending; + + struct wcd_mbhc_fn *mbhc_fn; + bool force_linein; + struct device_node *fsa_np; + struct notifier_block fsa_nb; +}; + +void wcd_mbhc_find_plug_and_report(struct wcd_mbhc *mbhc, + enum wcd_mbhc_plug_type plug_type); +void wcd_mbhc_hs_elec_irq(struct wcd_mbhc *mbhc, int irq_type, bool enable); +void wcd_mbhc_elec_hs_report_unplug(struct wcd_mbhc *mbhc); +bool wcd_swch_level_remove(struct wcd_mbhc *mbhc); +void wcd_enable_curr_micbias(const struct wcd_mbhc *mbhc, + const enum wcd_mbhc_cs_mb_en_flag cs_mb_en); +void wcd_mbhc_jack_report(struct wcd_mbhc *mbhc, + struct snd_soc_jack *jack, int status, int mask); +int wcd_cancel_btn_work(struct wcd_mbhc *mbhc); +int wcd_mbhc_get_button_mask(struct wcd_mbhc *mbhc); +void wcd_mbhc_report_plug(struct wcd_mbhc *mbhc, int insertion, + enum snd_jack_types jack_type); + +#endif /* __WCD_MBHC_V2_H__ */ diff --git a/audio-kernel/asoc/codecs/wcd-spi-registers.h b/audio-kernel/asoc/codecs/wcd-spi-registers.h new file mode 100644 index 000000000..4e579696c --- /dev/null +++ b/audio-kernel/asoc/codecs/wcd-spi-registers.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2016, 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 __WCD_SPI_REGISTERS_H__ +#define __WCD_SPI_REGISTERS_H__ + +#include + +#define WCD_SPI_SLAVE_SANITY (0x00) +#define WCD_SPI_SLAVE_DEVICE_ID (0x04) +#define WCD_SPI_SLAVE_STATUS (0x08) +#define WCD_SPI_SLAVE_CONFIG (0x0c) +#define WCD_SPI_SLAVE_SW_RESET (0x10) +#define WCD_SPI_SLAVE_IRQ_STATUS (0x14) +#define WCD_SPI_SLAVE_IRQ_EN (0x18) +#define WCD_SPI_SLAVE_IRQ_CLR (0x1c) +#define WCD_SPI_SLAVE_IRQ_FORCE (0x20) +#define WCD_SPI_SLAVE_TX (0x24) +#define WCD_SPI_SLAVE_TEST_BUS_DATA (0x2c) +#define WCD_SPI_SLAVE_TEST_BUS_CTRL (0x30) +#define WCD_SPI_SLAVE_SW_RST_IRQ (0x34) +#define WCD_SPI_SLAVE_CHAR_CFG (0x38) +#define WCD_SPI_SLAVE_CHAR_DATA_MOSI (0x3c) +#define WCD_SPI_SLAVE_CHAR_DATA_CS_N (0x40) +#define WCD_SPI_SLAVE_CHAR_DATA_MISO (0x44) +#define WCD_SPI_SLAVE_TRNS_BYTE_CNT (0x4c) +#define WCD_SPI_SLAVE_TRNS_LEN (0x50) +#define WCD_SPI_SLAVE_FIFO_LEVEL (0x54) +#define WCD_SPI_SLAVE_GENERICS (0x58) +#define WCD_SPI_SLAVE_EXT_BASE_ADDR (0x5c) +#define WCD_SPI_MAX_REGISTER (0x5F) + +#endif /* End __WCD_SPI_REGISTERS_H__ */ diff --git a/audio-kernel/asoc/codecs/wcd-spi.c b/audio-kernel/asoc/codecs/wcd-spi.c new file mode 100644 index 000000000..bb9333485 --- /dev/null +++ b/audio-kernel/asoc/codecs/wcd-spi.c @@ -0,0 +1,1569 @@ +/* + * Copyright (c) 2016-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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "wcd-spi-registers.h" + +/* Byte manipulations */ +#define SHIFT_1_BYTES (8) +#define SHIFT_2_BYTES (16) +#define SHIFT_3_BYTES (24) + +/* Command opcodes */ +#define WCD_SPI_CMD_NOP (0x00) +#define WCD_SPI_CMD_WREN (0x06) +#define WCD_SPI_CMD_CLKREQ (0xDA) +#define WCD_SPI_CMD_RDSR (0x05) +#define WCD_SPI_CMD_IRR (0x81) +#define WCD_SPI_CMD_IRW (0x82) +#define WCD_SPI_CMD_MIOR (0x83) +#define WCD_SPI_CMD_FREAD (0x0B) +#define WCD_SPI_CMD_MIOW (0x02) +#define WCD_SPI_WRITE_FRAME_OPCODE \ + (WCD_SPI_CMD_MIOW << SHIFT_3_BYTES) +#define WCD_SPI_READ_FRAME_OPCODE \ + (WCD_SPI_CMD_MIOR << SHIFT_3_BYTES) +#define WCD_SPI_FREAD_FRAME_OPCODE \ + (WCD_SPI_CMD_FREAD << SHIFT_3_BYTES) + +/* Command lengths */ +#define WCD_SPI_OPCODE_LEN (0x01) +#define WCD_SPI_CMD_NOP_LEN (0x01) +#define WCD_SPI_CMD_WREN_LEN (0x01) +#define WCD_SPI_CMD_CLKREQ_LEN (0x04) +#define WCD_SPI_CMD_IRR_LEN (0x04) +#define WCD_SPI_CMD_IRW_LEN (0x06) +#define WCD_SPI_WRITE_SINGLE_LEN (0x08) +#define WCD_SPI_READ_SINGLE_LEN (0x13) +#define WCD_SPI_CMD_FREAD_LEN (0x13) + +/* Command delays */ +#define WCD_SPI_CLKREQ_DELAY_USECS (500) +#define WCD_SPI_CLK_OFF_TIMER_MS (500) +#define WCD_SPI_RESUME_TIMEOUT_MS 100 + +/* Command masks */ +#define WCD_CMD_ADDR_MASK \ + (0xFF | \ + (0xFF << SHIFT_1_BYTES) | \ + (0xFF << SHIFT_2_BYTES)) + +/* Clock ctrl request related */ +#define WCD_SPI_CLK_ENABLE true +#define WCD_SPI_CLK_DISABLE false +#define WCD_SPI_CLK_FLAG_DELAYED (1 << 0) +#define WCD_SPI_CLK_FLAG_IMMEDIATE (1 << 1) + +/* Internal addresses */ +#define WCD_SPI_ADDR_IPC_CTL_HOST (0x012014) + +/* Word sizes and min/max lengths */ +#define WCD_SPI_WORD_BYTE_CNT (4) +#define WCD_SPI_RW_MULTI_MIN_LEN (16) + +/* Max size is 32 bytes less than 64Kbytes */ +#define WCD_SPI_RW_MULTI_MAX_LEN ((64 * 1024) - 32) + +/* + * Max size for the pre-allocated buffers is the max + * possible read/write length + 32 bytes for the SPI + * read/write command header itself. + */ +#define WCD_SPI_RW_MAX_BUF_SIZE (WCD_SPI_RW_MULTI_MAX_LEN + 32) + +/* Alignment requirements */ +#define WCD_SPI_RW_MIN_ALIGN WCD_SPI_WORD_BYTE_CNT +#define WCD_SPI_RW_MULTI_ALIGN (16) + +/* Status mask bits */ +#define WCD_SPI_CLK_STATE_ENABLED BIT(0) +#define WCD_SPI_IS_SUSPENDED BIT(1) + +/* Locking related */ +#define WCD_SPI_MUTEX_LOCK(spi, lock) \ +{ \ + dev_vdbg(&spi->dev, "%s: mutex_lock(%s)\n", \ + __func__, __stringify_1(lock)); \ + mutex_lock(&lock); \ +} + +#define WCD_SPI_MUTEX_UNLOCK(spi, lock) \ +{ \ + dev_vdbg(&spi->dev, "%s: mutex_unlock(%s)\n", \ + __func__, __stringify_1(lock)); \ + mutex_unlock(&lock); \ +} + +struct wcd_spi_debug_data { + struct dentry *dir; + u32 addr; + u32 size; +}; + +struct wcd_spi_priv { + struct spi_device *spi; + u32 mem_base_addr; + + struct regmap *regmap; + + /* Message for single transfer */ + struct spi_message msg1; + struct spi_transfer xfer1; + + /* Message for two transfers */ + struct spi_message msg2; + struct spi_transfer xfer2[2]; + + /* Register access related */ + u32 reg_bytes; + u32 val_bytes; + + /* Clock requests related */ + struct mutex clk_mutex; + int clk_users; + unsigned long status_mask; + struct delayed_work clk_dwork; + + /* Transaction related */ + struct mutex xfer_mutex; + + struct device *m_dev; + struct wdsp_mgr_ops *m_ops; + + /* Debugfs related information */ + struct wcd_spi_debug_data debug_data; + + /* Completion object to indicate system resume completion */ + struct completion resume_comp; + + /* Buffers to hold memory used for transfers */ + void *tx_buf; + void *rx_buf; +}; + +enum xfer_request { + WCD_SPI_XFER_WRITE, + WCD_SPI_XFER_READ, +}; + + +static char *wcd_spi_xfer_req_str(enum xfer_request req) +{ + if (req == WCD_SPI_XFER_WRITE) + return "xfer_write"; + else if (req == WCD_SPI_XFER_READ) + return "xfer_read"; + else + return "xfer_invalid"; +} + +static void wcd_spi_reinit_xfer(struct spi_transfer *xfer) +{ + xfer->tx_buf = NULL; + xfer->rx_buf = NULL; + xfer->delay_usecs = 0; + xfer->len = 0; +} + +static bool wcd_spi_is_suspended(struct wcd_spi_priv *wcd_spi) +{ + return test_bit(WCD_SPI_IS_SUSPENDED, &wcd_spi->status_mask); +} + +static bool wcd_spi_can_suspend(struct wcd_spi_priv *wcd_spi) +{ + struct spi_device *spi = wcd_spi->spi; + + if (wcd_spi->clk_users > 0 || + test_bit(WCD_SPI_CLK_STATE_ENABLED, &wcd_spi->status_mask)) { + dev_err(&spi->dev, "%s: cannot suspend, clk_users = %d\n", + __func__, wcd_spi->clk_users); + return false; + } + + return true; +} + +static int wcd_spi_wait_for_resume(struct wcd_spi_priv *wcd_spi) +{ + struct spi_device *spi = wcd_spi->spi; + int rc = 0; + + WCD_SPI_MUTEX_LOCK(spi, wcd_spi->clk_mutex); + /* If the system is already in resumed state, return right away */ + if (!wcd_spi_is_suspended(wcd_spi)) + goto done; + + /* If suspended then wait for resume to happen */ + reinit_completion(&wcd_spi->resume_comp); + WCD_SPI_MUTEX_UNLOCK(spi, wcd_spi->clk_mutex); + rc = wait_for_completion_timeout(&wcd_spi->resume_comp, + msecs_to_jiffies(WCD_SPI_RESUME_TIMEOUT_MS)); + WCD_SPI_MUTEX_LOCK(spi, wcd_spi->clk_mutex); + if (rc == 0) { + dev_err(&spi->dev, "%s: failed to resume in %u msec\n", + __func__, WCD_SPI_RESUME_TIMEOUT_MS); + rc = -EIO; + goto done; + } + + dev_dbg(&spi->dev, "%s: resume successful\n", __func__); + rc = 0; +done: + WCD_SPI_MUTEX_UNLOCK(spi, wcd_spi->clk_mutex); + return rc; +} + +static int wcd_spi_read_single(struct spi_device *spi, + u32 remote_addr, u32 *val) +{ + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + struct spi_transfer *tx_xfer = &wcd_spi->xfer2[0]; + struct spi_transfer *rx_xfer = &wcd_spi->xfer2[1]; + u8 *tx_buf = wcd_spi->tx_buf; + u8 *rx_buf = wcd_spi->rx_buf; + u32 frame = 0; + int ret; + + dev_dbg(&spi->dev, "%s: remote_addr = 0x%x\n", + __func__, remote_addr); + + if (!tx_buf) { + dev_err(&spi->dev, "%s: tx_buf not allocated\n", + __func__); + return -ENOMEM; + } + + frame |= WCD_SPI_READ_FRAME_OPCODE; + frame |= remote_addr & WCD_CMD_ADDR_MASK; + + wcd_spi_reinit_xfer(tx_xfer); + frame = cpu_to_be32(frame); + memcpy(tx_buf, &frame, sizeof(frame)); + tx_xfer->tx_buf = tx_buf; + tx_xfer->len = WCD_SPI_READ_SINGLE_LEN; + + wcd_spi_reinit_xfer(rx_xfer); + rx_xfer->rx_buf = rx_buf; + rx_xfer->len = sizeof(*val); + + ret = spi_sync(spi, &wcd_spi->msg2); + if (ret) + dev_err(&spi->dev, "%s: spi_sync failed, err %d\n", + __func__, ret); + else + memcpy((u8*) val, rx_buf, sizeof(*val)); + + return ret; +} + +static int wcd_spi_read_multi(struct spi_device *spi, + u32 remote_addr, u8 *data, + size_t len) +{ + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + struct spi_transfer *xfer = &wcd_spi->xfer1; + u8 *tx_buf = wcd_spi->tx_buf; + u8 *rx_buf = wcd_spi->rx_buf; + u32 frame = 0; + int ret; + + dev_dbg(&spi->dev, "%s: addr 0x%x, len = %zd\n", + __func__, remote_addr, len); + + frame |= WCD_SPI_FREAD_FRAME_OPCODE; + frame |= remote_addr & WCD_CMD_ADDR_MASK; + + if (!tx_buf || !rx_buf) { + dev_err(&spi->dev, "%s: %s not allocated\n", __func__, + (!tx_buf) ? "tx_buf" : "rx_buf"); + return -ENOMEM; + } + + wcd_spi_reinit_xfer(xfer); + frame = cpu_to_be32(frame); + memcpy(tx_buf, &frame, sizeof(frame)); + xfer->tx_buf = tx_buf; + xfer->rx_buf = rx_buf; + xfer->len = WCD_SPI_CMD_FREAD_LEN + len; + + ret = spi_sync(spi, &wcd_spi->msg1); + if (ret) { + dev_err(&spi->dev, "%s: failed, err = %d\n", + __func__, ret); + goto done; + } + + memcpy(data, rx_buf + WCD_SPI_CMD_FREAD_LEN, len); +done: + return ret; +} + +static int wcd_spi_write_single(struct spi_device *spi, + u32 remote_addr, u32 val) +{ + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + struct spi_transfer *xfer = &wcd_spi->xfer1; + u8 *tx_buf = wcd_spi->tx_buf; + u32 frame = 0; + + dev_dbg(&spi->dev, "%s: remote_addr = 0x%x, val = 0x%x\n", + __func__, remote_addr, val); + + memset(tx_buf, 0, WCD_SPI_WRITE_SINGLE_LEN); + frame |= WCD_SPI_WRITE_FRAME_OPCODE; + frame |= (remote_addr & WCD_CMD_ADDR_MASK); + + frame = cpu_to_be32(frame); + memcpy(tx_buf, &frame, sizeof(frame)); + memcpy(tx_buf + sizeof(frame), &val, sizeof(val)); + + wcd_spi_reinit_xfer(xfer); + xfer->tx_buf = tx_buf; + xfer->len = WCD_SPI_WRITE_SINGLE_LEN; + + return spi_sync(spi, &wcd_spi->msg1); +} + +static int wcd_spi_write_multi(struct spi_device *spi, + u32 remote_addr, u8 *data, + size_t len) +{ + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + struct spi_transfer *xfer = &wcd_spi->xfer1; + u32 frame = 0; + u8 *tx_buf = wcd_spi->tx_buf; + int xfer_len, ret; + + dev_dbg(&spi->dev, "%s: addr = 0x%x len = %zd\n", + __func__, remote_addr, len); + + frame |= WCD_SPI_WRITE_FRAME_OPCODE; + frame |= (remote_addr & WCD_CMD_ADDR_MASK); + + frame = cpu_to_be32(frame); + xfer_len = len + sizeof(frame); + + if (!tx_buf) { + dev_err(&spi->dev, "%s: tx_buf not allocated\n", + __func__); + return -ENOMEM; + } + + memcpy(tx_buf, &frame, sizeof(frame)); + memcpy(tx_buf + sizeof(frame), data, len); + + wcd_spi_reinit_xfer(xfer); + xfer->tx_buf = tx_buf; + xfer->len = xfer_len; + + ret = spi_sync(spi, &wcd_spi->msg1); + if (ret < 0) + dev_err(&spi->dev, + "%s: Failed, addr = 0x%x, len = %zd\n", + __func__, remote_addr, len); + return ret; +} + +static int wcd_spi_transfer_split(struct spi_device *spi, + struct wcd_spi_msg *data_msg, + enum xfer_request xfer_req) +{ + u32 addr = data_msg->remote_addr; + u8 *data = data_msg->data; + int remain_size = data_msg->len; + int to_xfer, loop_cnt, ret = 0; + + /* Perform single writes until multi word alignment is met */ + loop_cnt = 1; + while (remain_size && + !IS_ALIGNED(addr, WCD_SPI_RW_MULTI_ALIGN)) { + if (xfer_req == WCD_SPI_XFER_WRITE) + ret = wcd_spi_write_single(spi, addr, + (*(u32 *)data)); + else + ret = wcd_spi_read_single(spi, addr, + (u32 *)data); + if (ret < 0) { + dev_err(&spi->dev, + "%s: %s fail iter(%d) start-word addr (0x%x)\n", + __func__, wcd_spi_xfer_req_str(xfer_req), + loop_cnt, addr); + goto done; + } + + addr += WCD_SPI_WORD_BYTE_CNT; + data += WCD_SPI_WORD_BYTE_CNT; + remain_size -= WCD_SPI_WORD_BYTE_CNT; + loop_cnt++; + } + + /* Perform multi writes for max allowed multi writes */ + loop_cnt = 1; + while (remain_size >= WCD_SPI_RW_MULTI_MAX_LEN) { + if (xfer_req == WCD_SPI_XFER_WRITE) + ret = wcd_spi_write_multi(spi, addr, data, + WCD_SPI_RW_MULTI_MAX_LEN); + else + ret = wcd_spi_read_multi(spi, addr, data, + WCD_SPI_RW_MULTI_MAX_LEN); + if (ret < 0) { + dev_err(&spi->dev, + "%s: %s fail iter(%d) max-write addr (0x%x)\n", + __func__, wcd_spi_xfer_req_str(xfer_req), + loop_cnt, addr); + goto done; + } + + addr += WCD_SPI_RW_MULTI_MAX_LEN; + data += WCD_SPI_RW_MULTI_MAX_LEN; + remain_size -= WCD_SPI_RW_MULTI_MAX_LEN; + loop_cnt++; + } + + /* + * Perform write for max possible data that is multiple + * of the minimum size for multi-write commands. + */ + to_xfer = remain_size - (remain_size % WCD_SPI_RW_MULTI_MIN_LEN); + if (remain_size >= WCD_SPI_RW_MULTI_MIN_LEN && + to_xfer > 0) { + if (xfer_req == WCD_SPI_XFER_WRITE) + ret = wcd_spi_write_multi(spi, addr, data, to_xfer); + else + ret = wcd_spi_read_multi(spi, addr, data, to_xfer); + if (ret < 0) { + dev_err(&spi->dev, + "%s: %s fail write addr (0x%x), size (0x%x)\n", + __func__, wcd_spi_xfer_req_str(xfer_req), + addr, to_xfer); + goto done; + } + + addr += to_xfer; + data += to_xfer; + remain_size -= to_xfer; + } + + /* Perform single writes for the last remaining data */ + loop_cnt = 1; + while (remain_size > 0) { + if (xfer_req == WCD_SPI_XFER_WRITE) + ret = wcd_spi_write_single(spi, addr, (*((u32 *)data))); + else + ret = wcd_spi_read_single(spi, addr, (u32 *) data); + if (ret < 0) { + dev_err(&spi->dev, + "%s: %s fail iter(%d) end-write addr (0x%x)\n", + __func__, wcd_spi_xfer_req_str(xfer_req), + loop_cnt, addr); + goto done; + } + + addr += WCD_SPI_WORD_BYTE_CNT; + data += WCD_SPI_WORD_BYTE_CNT; + remain_size -= WCD_SPI_WORD_BYTE_CNT; + loop_cnt++; + } + +done: + return ret; +} + +static int wcd_spi_cmd_nop(struct spi_device *spi) +{ + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + u8 *tx_buf = wcd_spi->tx_buf; + + tx_buf[0] = WCD_SPI_CMD_NOP; + + return spi_write(spi, tx_buf, WCD_SPI_CMD_NOP_LEN); +} + +static int wcd_spi_cmd_clkreq(struct spi_device *spi) +{ + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + struct spi_transfer *xfer = &wcd_spi->xfer1; + u8 *tx_buf = wcd_spi->tx_buf; + u8 cmd[WCD_SPI_CMD_CLKREQ_LEN] = { + WCD_SPI_CMD_CLKREQ, + 0xBA, 0x80, 0x00}; + + memcpy(tx_buf, cmd, WCD_SPI_CMD_CLKREQ_LEN); + wcd_spi_reinit_xfer(xfer); + xfer->tx_buf = tx_buf; + xfer->len = WCD_SPI_CMD_CLKREQ_LEN; + xfer->delay_usecs = WCD_SPI_CLKREQ_DELAY_USECS; + + return spi_sync(spi, &wcd_spi->msg1); +} + +static int wcd_spi_cmd_wr_en(struct spi_device *spi) +{ + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + u8 *tx_buf = wcd_spi->tx_buf; + + tx_buf[0] = WCD_SPI_CMD_WREN; + + return spi_write(spi, tx_buf, WCD_SPI_CMD_WREN_LEN); +} + +static int wcd_spi_cmd_rdsr(struct spi_device *spi, + u32 *rdsr_status) +{ + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + struct spi_transfer *tx_xfer = &wcd_spi->xfer2[0]; + struct spi_transfer *rx_xfer = &wcd_spi->xfer2[1]; + u8 *tx_buf = wcd_spi->tx_buf; + u8 *rx_buf = wcd_spi->rx_buf; + int ret; + + tx_buf[0] = WCD_SPI_CMD_RDSR; + wcd_spi_reinit_xfer(tx_xfer); + tx_xfer->tx_buf = tx_buf; + tx_xfer->len = WCD_SPI_OPCODE_LEN; + + memset(rx_buf, 0, sizeof(*rdsr_status)); + wcd_spi_reinit_xfer(rx_xfer); + rx_xfer->rx_buf = rx_buf; + rx_xfer->len = sizeof(*rdsr_status); + + ret = spi_sync(spi, &wcd_spi->msg2); + if (ret < 0) { + dev_err(&spi->dev, "%s: RDSR failed, err = %d\n", + __func__, ret); + goto done; + } + + *rdsr_status = be32_to_cpu(*((u32*)rx_buf)); + + dev_dbg(&spi->dev, "%s: RDSR success, value = 0x%x\n", + __func__, *rdsr_status); +done: + return ret; +} + +static int wcd_spi_clk_enable(struct spi_device *spi) +{ + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + int ret; + u32 rd_status = 0; + + ret = wcd_spi_cmd_nop(spi); + if (ret < 0) { + dev_err(&spi->dev, "%s: NOP1 failed, err = %d\n", + __func__, ret); + goto done; + } + + ret = wcd_spi_cmd_clkreq(spi); + if (ret < 0) { + dev_err(&spi->dev, "%s: CLK_REQ failed, err = %d\n", + __func__, ret); + goto done; + } + + ret = wcd_spi_cmd_nop(spi); + if (ret < 0) { + dev_err(&spi->dev, "%s: NOP2 failed, err = %d\n", + __func__, ret); + goto done; + } + wcd_spi_cmd_rdsr(spi, &rd_status); + /* + * Read status zero means reads are not + * happenning on the bus, possibly because + * clock request failed. + */ + if (rd_status) { + set_bit(WCD_SPI_CLK_STATE_ENABLED, + &wcd_spi->status_mask); + } else { + dev_err(&spi->dev, "%s: RDSR status is zero\n", + __func__); + ret = -EIO; + } +done: + return ret; +} + +static int wcd_spi_clk_disable(struct spi_device *spi) +{ + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + int ret; + + ret = wcd_spi_write_single(spi, WCD_SPI_ADDR_IPC_CTL_HOST, 0x01); + if (ret < 0) + dev_err(&spi->dev, "%s: Failed, err = %d\n", + __func__, ret); + /* + * clear this bit even if clock disable failed + * as the source clocks might get turned off. + */ + clear_bit(WCD_SPI_CLK_STATE_ENABLED, &wcd_spi->status_mask); + + return ret; +} + +static int wcd_spi_clk_ctrl(struct spi_device *spi, + bool request, u32 flags) +{ + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + int ret = 0; + const char *delay_str; + + delay_str = (flags == WCD_SPI_CLK_FLAG_DELAYED) ? + "delayed" : "immediate"; + + WCD_SPI_MUTEX_LOCK(spi, wcd_spi->clk_mutex); + + /* Reject any unbalanced disable request */ + if (wcd_spi->clk_users < 0 || + (!request && wcd_spi->clk_users == 0)) { + dev_err(&spi->dev, "%s: Unbalanced clk_users %d for %s\n", + __func__, wcd_spi->clk_users, + request ? "enable" : "disable"); + ret = -EINVAL; + + /* Reset the clk_users to 0 */ + wcd_spi->clk_users = 0; + + goto done; + } + + if (request == WCD_SPI_CLK_ENABLE) { + /* + * If the SPI bus is suspended, then return error + * as the transaction cannot be completed. + */ + if (wcd_spi_is_suspended(wcd_spi)) { + dev_err(&spi->dev, + "%s: SPI suspended, cannot enable clk\n", + __func__); + ret = -EIO; + goto done; + } + + /* Cancel the disable clk work */ + WCD_SPI_MUTEX_UNLOCK(spi, wcd_spi->clk_mutex); + cancel_delayed_work_sync(&wcd_spi->clk_dwork); + WCD_SPI_MUTEX_LOCK(spi, wcd_spi->clk_mutex); + + wcd_spi->clk_users++; + + /* + * If clk state is already set, + * then clk wasnt really disabled + */ + if (test_bit(WCD_SPI_CLK_STATE_ENABLED, &wcd_spi->status_mask)) + goto done; + else if (wcd_spi->clk_users == 1) + ret = wcd_spi_clk_enable(spi); + + } else { + wcd_spi->clk_users--; + + /* Clock is still voted for */ + if (wcd_spi->clk_users > 0) + goto done; + + /* + * If we are here, clk_users must be 0 and needs + * to be disabled. Call the disable based on the + * flags. + */ + if (flags == WCD_SPI_CLK_FLAG_DELAYED) { + schedule_delayed_work(&wcd_spi->clk_dwork, + msecs_to_jiffies(WCD_SPI_CLK_OFF_TIMER_MS)); + } else { + ret = wcd_spi_clk_disable(spi); + if (ret < 0) + dev_err(&spi->dev, + "%s: Failed to disable clk err = %d\n", + __func__, ret); + } + } + +done: + dev_dbg(&spi->dev, "%s: updated clk_users = %d, request_%s %s\n", + __func__, wcd_spi->clk_users, request ? "enable" : "disable", + request ? "" : delay_str); + WCD_SPI_MUTEX_UNLOCK(spi, wcd_spi->clk_mutex); + + return ret; +} + +static int wcd_spi_init(struct spi_device *spi) +{ + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + int ret; + + ret = wcd_spi_clk_ctrl(spi, WCD_SPI_CLK_ENABLE, + WCD_SPI_CLK_FLAG_IMMEDIATE); + if (ret < 0) + goto done; + + ret = wcd_spi_cmd_wr_en(spi); + if (ret < 0) + goto err_wr_en; + + /* + * In case spi_init is called after component deinit, + * it is possible hardware register state is also reset. + * Sync the regcache here so hardware state is updated + * to reflect the cache. + */ + regcache_sync(wcd_spi->regmap); + + regmap_write(wcd_spi->regmap, WCD_SPI_SLAVE_CONFIG, + 0x0F3D0800); + + /* Write the MTU to max allowed size */ + regmap_update_bits(wcd_spi->regmap, + WCD_SPI_SLAVE_TRNS_LEN, + 0xFFFF0000, 0xFFFF0000); +err_wr_en: + wcd_spi_clk_ctrl(spi, WCD_SPI_CLK_DISABLE, + WCD_SPI_CLK_FLAG_IMMEDIATE); +done: + return ret; +} + +static void wcd_spi_clk_work(struct work_struct *work) +{ + struct delayed_work *dwork; + struct wcd_spi_priv *wcd_spi; + struct spi_device *spi; + int ret; + + dwork = to_delayed_work(work); + wcd_spi = container_of(dwork, struct wcd_spi_priv, clk_dwork); + spi = wcd_spi->spi; + + WCD_SPI_MUTEX_LOCK(spi, wcd_spi->clk_mutex); + ret = wcd_spi_clk_disable(spi); + if (ret < 0) + dev_err(&spi->dev, + "%s: Failed to disable clk, err = %d\n", + __func__, ret); + WCD_SPI_MUTEX_UNLOCK(spi, wcd_spi->clk_mutex); +} + +static int __wcd_spi_data_xfer(struct spi_device *spi, + struct wcd_spi_msg *msg, + enum xfer_request xfer_req) +{ + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + int ret; + + /* Check for minimum alignment requirements */ + if (!IS_ALIGNED(msg->remote_addr, WCD_SPI_RW_MIN_ALIGN)) { + dev_err(&spi->dev, + "%s addr 0x%x is not aligned to 0x%x\n", + __func__, msg->remote_addr, WCD_SPI_RW_MIN_ALIGN); + return -EINVAL; + } else if (msg->len % WCD_SPI_WORD_BYTE_CNT) { + dev_err(&spi->dev, + "%s len 0x%zx is not multiple of %d\n", + __func__, msg->len, WCD_SPI_WORD_BYTE_CNT); + return -EINVAL; + } + + WCD_SPI_MUTEX_LOCK(spi, wcd_spi->xfer_mutex); + if (msg->len == WCD_SPI_WORD_BYTE_CNT) { + if (xfer_req == WCD_SPI_XFER_WRITE) + ret = wcd_spi_write_single(spi, msg->remote_addr, + (*((u32 *)msg->data))); + else + ret = wcd_spi_read_single(spi, msg->remote_addr, + (u32 *) msg->data); + } else { + ret = wcd_spi_transfer_split(spi, msg, xfer_req); + } + WCD_SPI_MUTEX_UNLOCK(spi, wcd_spi->xfer_mutex); + + return ret; +} + +static int wcd_spi_data_xfer(struct spi_device *spi, + struct wcd_spi_msg *msg, + enum xfer_request req) +{ + int ret, ret1; + + if (msg->len <= 0) { + dev_err(&spi->dev, "%s: Invalid size %zd\n", + __func__, msg->len); + return -EINVAL; + } + + /* Request for clock */ + ret = wcd_spi_clk_ctrl(spi, WCD_SPI_CLK_ENABLE, + WCD_SPI_CLK_FLAG_IMMEDIATE); + if (ret < 0) { + dev_err(&spi->dev, "%s: clk enable failed %d\n", + __func__, ret); + goto done; + } + + /* Perform the transaction */ + ret = __wcd_spi_data_xfer(spi, msg, req); + if (ret < 0) + dev_err(&spi->dev, + "%s: Failed %s, addr = 0x%x, size = 0x%zx, err = %d\n", + __func__, wcd_spi_xfer_req_str(req), + msg->remote_addr, msg->len, ret); + + /* Release the clock even if xfer failed */ + ret1 = wcd_spi_clk_ctrl(spi, WCD_SPI_CLK_DISABLE, + WCD_SPI_CLK_FLAG_DELAYED); + if (ret1 < 0) + dev_err(&spi->dev, "%s: clk disable failed %d\n", + __func__, ret1); +done: + return ret; +} + +/* + * wcd_spi_data_write: Write data to WCD SPI + * @spi: spi_device struct + * @msg: msg that needs to be written to WCD + * + * This API writes length of data to address specified. These details + * about the write are encapsulated in @msg. Write size should be multiple + * of 4 bytes and write address should be 4-byte aligned. + */ +static int wcd_spi_data_write(struct spi_device *spi, + struct wcd_spi_msg *msg) +{ + if (!spi || !msg) { + pr_err("%s: Invalid %s\n", __func__, + (!spi) ? "spi device" : "msg"); + return -EINVAL; + } + + dev_dbg_ratelimited(&spi->dev, "%s: addr = 0x%x, len = %zu\n", + __func__, msg->remote_addr, msg->len); + return wcd_spi_data_xfer(spi, msg, WCD_SPI_XFER_WRITE); +} + +/* + * wcd_spi_data_read: Read data from WCD SPI + * @spi: spi_device struct + * @msg: msg that needs to be read from WCD + * + * This API reads length of data from address specified. These details + * about the read are encapsulated in @msg. Read size should be multiple + * of 4 bytes and read address should be 4-byte aligned. + */ +static int wcd_spi_data_read(struct spi_device *spi, + struct wcd_spi_msg *msg) +{ + if (!spi || !msg) { + pr_err("%s: Invalid %s\n", __func__, + (!spi) ? "spi device" : "msg"); + return -EINVAL; + } + + dev_dbg_ratelimited(&spi->dev, "%s: addr = 0x%x,len = %zu\n", + __func__, msg->remote_addr, msg->len); + return wcd_spi_data_xfer(spi, msg, WCD_SPI_XFER_READ); +} + +static int wdsp_spi_dload_section(struct spi_device *spi, + void *data) +{ + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + struct wdsp_img_section *sec = data; + struct wcd_spi_msg msg; + int ret; + + dev_dbg(&spi->dev, "%s: addr = 0x%x, size = 0x%zx\n", + __func__, sec->addr, sec->size); + + msg.remote_addr = sec->addr + wcd_spi->mem_base_addr; + msg.data = sec->data; + msg.len = sec->size; + + ret = __wcd_spi_data_xfer(spi, &msg, WCD_SPI_XFER_WRITE); + if (ret < 0) + dev_err(&spi->dev, "%s: fail addr (0x%x) size (0x%zx)\n", + __func__, msg.remote_addr, msg.len); + return ret; +} + +static int wdsp_spi_read_section(struct spi_device *spi, void *data) +{ + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + struct wdsp_img_section *sec = data; + struct wcd_spi_msg msg; + int ret; + + msg.remote_addr = sec->addr + wcd_spi->mem_base_addr; + msg.data = sec->data; + msg.len = sec->size; + + dev_dbg(&spi->dev, "%s: addr = 0x%x, size = 0x%zx\n", + __func__, msg.remote_addr, msg.len); + + ret = wcd_spi_data_xfer(spi, &msg, WCD_SPI_XFER_READ); + if (ret < 0) + dev_err(&spi->dev, "%s: fail addr (0x%x) size (0x%zx)\n", + __func__, msg.remote_addr, msg.len); + return ret; +} + +static int wdsp_spi_event_handler(struct device *dev, void *priv_data, + enum wdsp_event_type event, + void *data) +{ + struct spi_device *spi = to_spi_device(dev); + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + struct wcd_spi_ops *spi_ops; + int ret = 0; + + dev_dbg(&spi->dev, "%s: event type %d\n", + __func__, event); + + switch (event) { + case WDSP_EVENT_POST_SHUTDOWN: + cancel_delayed_work_sync(&wcd_spi->clk_dwork); + WCD_SPI_MUTEX_LOCK(spi, wcd_spi->clk_mutex); + if (test_bit(WCD_SPI_CLK_STATE_ENABLED, &wcd_spi->status_mask)) + wcd_spi_clk_disable(spi); + wcd_spi->clk_users = 0; + WCD_SPI_MUTEX_UNLOCK(spi, wcd_spi->clk_mutex); + break; + + case WDSP_EVENT_PRE_DLOAD_CODE: + case WDSP_EVENT_PRE_DLOAD_DATA: + ret = wcd_spi_clk_ctrl(spi, WCD_SPI_CLK_ENABLE, + WCD_SPI_CLK_FLAG_IMMEDIATE); + if (ret < 0) + dev_err(&spi->dev, "%s: clk_req failed %d\n", + __func__, ret); + break; + + case WDSP_EVENT_POST_DLOAD_CODE: + case WDSP_EVENT_POST_DLOAD_DATA: + case WDSP_EVENT_DLOAD_FAILED: + + ret = wcd_spi_clk_ctrl(spi, WCD_SPI_CLK_DISABLE, + WCD_SPI_CLK_FLAG_IMMEDIATE); + if (ret < 0) + dev_err(&spi->dev, "%s: clk unvote failed %d\n", + __func__, ret); + break; + + case WDSP_EVENT_DLOAD_SECTION: + ret = wdsp_spi_dload_section(spi, data); + break; + + case WDSP_EVENT_READ_SECTION: + ret = wdsp_spi_read_section(spi, data); + break; + + case WDSP_EVENT_SUSPEND: + WCD_SPI_MUTEX_LOCK(spi, wcd_spi->clk_mutex); + if (!wcd_spi_can_suspend(wcd_spi)) + ret = -EBUSY; + WCD_SPI_MUTEX_UNLOCK(spi, wcd_spi->clk_mutex); + break; + + case WDSP_EVENT_RESUME: + ret = wcd_spi_wait_for_resume(wcd_spi); + break; + + case WDSP_EVENT_GET_DEVOPS: + if (!data) { + dev_err(&spi->dev, "%s: invalid data\n", + __func__); + ret = -EINVAL; + break; + } + + spi_ops = (struct wcd_spi_ops *) data; + spi_ops->spi_dev = spi; + spi_ops->read_dev = wcd_spi_data_read; + spi_ops->write_dev = wcd_spi_data_write; + break; + + default: + dev_dbg(&spi->dev, "%s: Unhandled event %d\n", + __func__, event); + break; + } + + return ret; +} + +static int wcd_spi_bus_gwrite(void *context, const void *reg, + size_t reg_len, const void *val, + size_t val_len) +{ + struct device *dev = context; + struct spi_device *spi = to_spi_device(dev); + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + u8 *tx_buf = wcd_spi->tx_buf; + + if (!reg || !val || reg_len != wcd_spi->reg_bytes || + val_len != wcd_spi->val_bytes) { + dev_err(&spi->dev, + "%s: Invalid input, reg_len = %zd, val_len = %zd", + __func__, reg_len, val_len); + return -EINVAL; + } + + memset(tx_buf, 0, WCD_SPI_CMD_IRW_LEN); + tx_buf[0] = WCD_SPI_CMD_IRW; + tx_buf[1] = *((u8 *)reg); + memcpy(tx_buf + WCD_SPI_OPCODE_LEN + reg_len, + val, val_len); + + return spi_write(spi, tx_buf, WCD_SPI_CMD_IRW_LEN); +} + +static int wcd_spi_bus_write(void *context, const void *data, + size_t count) +{ + struct device *dev = context; + struct spi_device *spi = to_spi_device(dev); + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + + if (count < (wcd_spi->reg_bytes + wcd_spi->val_bytes)) { + dev_err(&spi->dev, "%s: Invalid size %zd\n", + __func__, count); + WARN_ON(1); + return -EINVAL; + } + + return wcd_spi_bus_gwrite(context, data, wcd_spi->reg_bytes, + data + wcd_spi->reg_bytes, + count - wcd_spi->reg_bytes); +} + +static int wcd_spi_bus_read(void *context, const void *reg, + size_t reg_len, void *val, + size_t val_len) +{ + struct device *dev = context; + struct spi_device *spi = to_spi_device(dev); + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + struct spi_transfer *tx_xfer = &wcd_spi->xfer2[0]; + struct spi_transfer *rx_xfer = &wcd_spi->xfer2[1]; + u8 *tx_buf = wcd_spi->tx_buf; + u8 *rx_buf = wcd_spi->rx_buf; + int ret = 0; + + if (!reg || !val || reg_len != wcd_spi->reg_bytes || + val_len != wcd_spi->val_bytes) { + dev_err(&spi->dev, + "%s: Invalid input, reg_len = %zd, val_len = %zd", + __func__, reg_len, val_len); + return -EINVAL; + } + + memset(tx_buf, 0, WCD_SPI_CMD_IRR_LEN); + tx_buf[0] = WCD_SPI_CMD_IRR; + tx_buf[1] = *((u8 *)reg); + + wcd_spi_reinit_xfer(tx_xfer); + tx_xfer->tx_buf = tx_buf; + tx_xfer->rx_buf = NULL; + tx_xfer->len = WCD_SPI_CMD_IRR_LEN; + + wcd_spi_reinit_xfer(rx_xfer); + rx_xfer->tx_buf = NULL; + rx_xfer->rx_buf = rx_buf; + rx_xfer->len = val_len; + + ret = spi_sync(spi, &wcd_spi->msg2); + if (ret) { + dev_err(&spi->dev, "%s: spi_sync failed, err %d\n", + __func__, ret); + goto done; + } + + memcpy(val, rx_buf, val_len); + +done: + return ret; +} + +static struct regmap_bus wcd_spi_regmap_bus = { + .write = wcd_spi_bus_write, + .gather_write = wcd_spi_bus_gwrite, + .read = wcd_spi_bus_read, + .reg_format_endian_default = REGMAP_ENDIAN_NATIVE, + .val_format_endian_default = REGMAP_ENDIAN_BIG, +}; + +static int wcd_spi_state_show(struct seq_file *f, void *ptr) +{ + struct spi_device *spi = f->private; + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + const char *clk_state, *clk_mutex, *xfer_mutex; + + if (test_bit(WCD_SPI_CLK_STATE_ENABLED, &wcd_spi->status_mask)) + clk_state = "enabled"; + else + clk_state = "disabled"; + + clk_mutex = mutex_is_locked(&wcd_spi->clk_mutex) ? + "locked" : "unlocked"; + + xfer_mutex = mutex_is_locked(&wcd_spi->xfer_mutex) ? + "locked" : "unlocked"; + + seq_printf(f, "clk_state = %s\nclk_users = %d\n" + "clk_mutex = %s\nxfer_mutex = %s\n", + clk_state, wcd_spi->clk_users, clk_mutex, + xfer_mutex); + return 0; +} + +static int wcd_spi_state_open(struct inode *inode, struct file *file) +{ + return single_open(file, wcd_spi_state_show, inode->i_private); +} + +static const struct file_operations state_fops = { + .open = wcd_spi_state_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static ssize_t wcd_spi_debugfs_mem_read(struct file *file, char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct spi_device *spi = file->private_data; + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + struct wcd_spi_debug_data *dbg_data = &wcd_spi->debug_data; + struct wcd_spi_msg msg; + ssize_t buf_size, read_count = 0; + char *buf; + int ret; + + if (*ppos < 0 || !count) + return -EINVAL; + + if (dbg_data->size == 0 || dbg_data->addr == 0) { + dev_err(&spi->dev, + "%s: Invalid request, size = %u, addr = 0x%x\n", + __func__, dbg_data->size, dbg_data->addr); + return 0; + } + + buf_size = count < dbg_data->size ? count : dbg_data->size; + buf = kzalloc(buf_size, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + msg.data = buf; + msg.remote_addr = dbg_data->addr; + msg.len = buf_size; + msg.flags = 0; + + ret = wcd_spi_data_read(spi, &msg); + if (ret < 0) { + dev_err(&spi->dev, + "%s: Failed to read %zu bytes from addr 0x%x\n", + __func__, buf_size, msg.remote_addr); + goto done; + } + + read_count = simple_read_from_buffer(ubuf, count, ppos, buf, buf_size); + +done: + kfree(buf); + if (ret < 0) + return ret; + else + return read_count; +} + +static const struct file_operations mem_read_fops = { + .open = simple_open, + .read = wcd_spi_debugfs_mem_read, +}; + +static int wcd_spi_debugfs_init(struct spi_device *spi) +{ + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + struct wcd_spi_debug_data *dbg_data = &wcd_spi->debug_data; + int rc = 0; + + dbg_data->dir = debugfs_create_dir("wcd_spi", NULL); + if (IS_ERR_OR_NULL(dbg_data->dir)) { + dbg_data->dir = NULL; + rc = -ENODEV; + goto done; + } + + debugfs_create_file("state", 0444, dbg_data->dir, spi, &state_fops); + debugfs_create_u32("addr", 0644, dbg_data->dir, + &dbg_data->addr); + debugfs_create_u32("size", 0644, dbg_data->dir, + &dbg_data->size); + + debugfs_create_file("mem_read", 0444, dbg_data->dir, + spi, &mem_read_fops); +done: + return rc; +} + + +static const struct reg_default wcd_spi_defaults[] = { + {WCD_SPI_SLAVE_SANITY, 0xDEADBEEF}, + {WCD_SPI_SLAVE_DEVICE_ID, 0x00500000}, + {WCD_SPI_SLAVE_STATUS, 0x80100000}, + {WCD_SPI_SLAVE_CONFIG, 0x0F200808}, + {WCD_SPI_SLAVE_SW_RESET, 0x00000000}, + {WCD_SPI_SLAVE_IRQ_STATUS, 0x00000000}, + {WCD_SPI_SLAVE_IRQ_EN, 0x00000000}, + {WCD_SPI_SLAVE_IRQ_CLR, 0x00000000}, + {WCD_SPI_SLAVE_IRQ_FORCE, 0x00000000}, + {WCD_SPI_SLAVE_TX, 0x00000000}, + {WCD_SPI_SLAVE_TEST_BUS_DATA, 0x00000000}, + {WCD_SPI_SLAVE_TEST_BUS_CTRL, 0x00000000}, + {WCD_SPI_SLAVE_SW_RST_IRQ, 0x00000000}, + {WCD_SPI_SLAVE_CHAR_CFG, 0x00000000}, + {WCD_SPI_SLAVE_CHAR_DATA_MOSI, 0x00000000}, + {WCD_SPI_SLAVE_CHAR_DATA_CS_N, 0x00000000}, + {WCD_SPI_SLAVE_CHAR_DATA_MISO, 0x00000000}, + {WCD_SPI_SLAVE_TRNS_BYTE_CNT, 0x00000000}, + {WCD_SPI_SLAVE_TRNS_LEN, 0x00000000}, + {WCD_SPI_SLAVE_FIFO_LEVEL, 0x00000000}, + {WCD_SPI_SLAVE_GENERICS, 0x80000000}, + {WCD_SPI_SLAVE_EXT_BASE_ADDR, 0x00000000}, +}; + +static bool wcd_spi_is_volatile_reg(struct device *dev, + unsigned int reg) +{ + switch (reg) { + case WCD_SPI_SLAVE_SANITY: + case WCD_SPI_SLAVE_STATUS: + case WCD_SPI_SLAVE_IRQ_STATUS: + case WCD_SPI_SLAVE_TX: + case WCD_SPI_SLAVE_SW_RST_IRQ: + case WCD_SPI_SLAVE_TRNS_BYTE_CNT: + case WCD_SPI_SLAVE_FIFO_LEVEL: + case WCD_SPI_SLAVE_GENERICS: + return true; + } + + return false; +} + +static bool wcd_spi_is_readable_reg(struct device *dev, + unsigned int reg) +{ + switch (reg) { + case WCD_SPI_SLAVE_SW_RESET: + case WCD_SPI_SLAVE_IRQ_CLR: + case WCD_SPI_SLAVE_IRQ_FORCE: + return false; + } + + return true; +} + +static struct regmap_config wcd_spi_regmap_cfg = { + .reg_bits = 8, + .val_bits = 32, + .cache_type = REGCACHE_RBTREE, + .reg_defaults = wcd_spi_defaults, + .num_reg_defaults = ARRAY_SIZE(wcd_spi_defaults), + .max_register = WCD_SPI_MAX_REGISTER, + .volatile_reg = wcd_spi_is_volatile_reg, + .readable_reg = wcd_spi_is_readable_reg, +}; + +static int wdsp_spi_init(struct device *dev, void *priv_data) +{ + struct spi_device *spi = to_spi_device(dev); + int ret; + + ret = wcd_spi_init(spi); + if (ret < 0) + dev_err(&spi->dev, "%s: Init failed, err = %d\n", + __func__, ret); + return ret; +} + +static int wdsp_spi_deinit(struct device *dev, void *priv_data) +{ + struct spi_device *spi = to_spi_device(dev); + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + + /* + * Deinit means the hardware is reset. Mark the cache + * as dirty here, so init will sync the cache + */ + regcache_mark_dirty(wcd_spi->regmap); + + return 0; +} + +static struct wdsp_cmpnt_ops wdsp_spi_ops = { + .init = wdsp_spi_init, + .deinit = wdsp_spi_deinit, + .event_handler = wdsp_spi_event_handler, +}; + +static int wcd_spi_component_bind(struct device *dev, + struct device *master, + void *data) +{ + struct spi_device *spi = to_spi_device(dev); + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + int ret = 0; + + wcd_spi->m_dev = master; + wcd_spi->m_ops = data; + + if (wcd_spi->m_ops && + wcd_spi->m_ops->register_cmpnt_ops) + ret = wcd_spi->m_ops->register_cmpnt_ops(master, dev, + wcd_spi, + &wdsp_spi_ops); + if (ret) { + dev_err(dev, "%s: register_cmpnt_ops failed, err = %d\n", + __func__, ret); + goto done; + } + + wcd_spi->reg_bytes = DIV_ROUND_UP(wcd_spi_regmap_cfg.reg_bits, 8); + wcd_spi->val_bytes = DIV_ROUND_UP(wcd_spi_regmap_cfg.val_bits, 8); + + wcd_spi->regmap = devm_regmap_init(&spi->dev, &wcd_spi_regmap_bus, + &spi->dev, &wcd_spi_regmap_cfg); + if (IS_ERR(wcd_spi->regmap)) { + ret = PTR_ERR(wcd_spi->regmap); + dev_err(&spi->dev, "%s: Failed to allocate regmap, err = %d\n", + __func__, ret); + goto done; + } + + if (wcd_spi_debugfs_init(spi)) + dev_err(&spi->dev, "%s: Failed debugfs init\n", __func__); + + spi_message_init(&wcd_spi->msg1); + spi_message_add_tail(&wcd_spi->xfer1, &wcd_spi->msg1); + + spi_message_init(&wcd_spi->msg2); + spi_message_add_tail(&wcd_spi->xfer2[0], &wcd_spi->msg2); + spi_message_add_tail(&wcd_spi->xfer2[1], &wcd_spi->msg2); + + /* Pre-allocate the buffers */ + wcd_spi->tx_buf = kzalloc(WCD_SPI_RW_MAX_BUF_SIZE, + GFP_KERNEL | GFP_DMA); + if (!wcd_spi->tx_buf) { + ret = -ENOMEM; + goto done; + } + + wcd_spi->rx_buf = kzalloc(WCD_SPI_RW_MAX_BUF_SIZE, + GFP_KERNEL | GFP_DMA); + if (!wcd_spi->rx_buf) { + kfree(wcd_spi->tx_buf); + wcd_spi->tx_buf = NULL; + ret = -ENOMEM; + goto done; + } +done: + return ret; +} + +static void wcd_spi_component_unbind(struct device *dev, + struct device *master, + void *data) +{ + struct spi_device *spi = to_spi_device(dev); + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + struct wcd_spi_debug_data *dbg_data = &wcd_spi->debug_data; + + debugfs_remove_recursive(dbg_data->dir); + dbg_data->dir = NULL; + + wcd_spi->m_dev = NULL; + wcd_spi->m_ops = NULL; + + spi_transfer_del(&wcd_spi->xfer1); + spi_transfer_del(&wcd_spi->xfer2[0]); + spi_transfer_del(&wcd_spi->xfer2[1]); + + kfree(wcd_spi->tx_buf); + kfree(wcd_spi->rx_buf); + wcd_spi->tx_buf = NULL; + wcd_spi->rx_buf = NULL; +} + +static const struct component_ops wcd_spi_component_ops = { + .bind = wcd_spi_component_bind, + .unbind = wcd_spi_component_unbind, +}; + +static int wcd_spi_probe(struct spi_device *spi) +{ + struct wcd_spi_priv *wcd_spi; + int ret = 0; + + wcd_spi = devm_kzalloc(&spi->dev, sizeof(*wcd_spi), + GFP_KERNEL); + if (!wcd_spi) + return -ENOMEM; + + ret = of_property_read_u32(spi->dev.of_node, + "qcom,mem-base-addr", + &wcd_spi->mem_base_addr); + if (ret < 0) { + dev_err(&spi->dev, "%s: Missing %s DT entry", + __func__, "qcom,mem-base-addr"); + goto err_ret; + } + + dev_dbg(&spi->dev, + "%s: mem_base_addr 0x%x\n", __func__, wcd_spi->mem_base_addr); + + mutex_init(&wcd_spi->clk_mutex); + mutex_init(&wcd_spi->xfer_mutex); + INIT_DELAYED_WORK(&wcd_spi->clk_dwork, wcd_spi_clk_work); + init_completion(&wcd_spi->resume_comp); + + wcd_spi->spi = spi; + spi_set_drvdata(spi, wcd_spi); + + ret = component_add(&spi->dev, &wcd_spi_component_ops); + if (ret) { + dev_err(&spi->dev, "%s: component_add failed err = %d\n", + __func__, ret); + goto err_component_add; + } + + return ret; + +err_component_add: + mutex_destroy(&wcd_spi->clk_mutex); + mutex_destroy(&wcd_spi->xfer_mutex); +err_ret: + devm_kfree(&spi->dev, wcd_spi); + spi_set_drvdata(spi, NULL); + return ret; +} + +static int wcd_spi_remove(struct spi_device *spi) +{ + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + + component_del(&spi->dev, &wcd_spi_component_ops); + + mutex_destroy(&wcd_spi->clk_mutex); + mutex_destroy(&wcd_spi->xfer_mutex); + + devm_kfree(&spi->dev, wcd_spi); + spi_set_drvdata(spi, NULL); + + return 0; +} + +#ifdef CONFIG_PM +static int wcd_spi_suspend(struct device *dev) +{ + struct spi_device *spi = to_spi_device(dev); + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + int rc = 0; + + WCD_SPI_MUTEX_LOCK(spi, wcd_spi->clk_mutex); + if (!wcd_spi_can_suspend(wcd_spi)) { + rc = -EBUSY; + goto done; + } + + /* + * If we are here, it is okay to let the suspend go + * through for this driver. But, still need to notify + * the master to make sure all other components can suspend + * as well. + */ + if (wcd_spi->m_dev && wcd_spi->m_ops && + wcd_spi->m_ops->suspend) { + WCD_SPI_MUTEX_UNLOCK(spi, wcd_spi->clk_mutex); + rc = wcd_spi->m_ops->suspend(wcd_spi->m_dev); + WCD_SPI_MUTEX_LOCK(spi, wcd_spi->clk_mutex); + } + + if (rc == 0) + set_bit(WCD_SPI_IS_SUSPENDED, &wcd_spi->status_mask); + else + dev_dbg(&spi->dev, "%s: cannot suspend, err = %d\n", + __func__, rc); +done: + WCD_SPI_MUTEX_UNLOCK(spi, wcd_spi->clk_mutex); + return rc; +} + +static int wcd_spi_resume(struct device *dev) +{ + struct spi_device *spi = to_spi_device(dev); + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + + WCD_SPI_MUTEX_LOCK(spi, wcd_spi->clk_mutex); + clear_bit(WCD_SPI_IS_SUSPENDED, &wcd_spi->status_mask); + complete(&wcd_spi->resume_comp); + WCD_SPI_MUTEX_UNLOCK(spi, wcd_spi->clk_mutex); + + return 0; +} + +static const struct dev_pm_ops wcd_spi_pm_ops = { + .suspend = wcd_spi_suspend, + .resume = wcd_spi_resume, +}; +#endif + +static const struct of_device_id wcd_spi_of_match[] = { + { .compatible = "qcom,wcd-spi-v2", }, + { } +}; +MODULE_DEVICE_TABLE(of, wcd_spi_of_match); + +static struct spi_driver wcd_spi_driver = { + .driver = { + .name = "wcd-spi-v2", + .of_match_table = wcd_spi_of_match, +#ifdef CONFIG_PM + .pm = &wcd_spi_pm_ops, +#endif + }, + .probe = wcd_spi_probe, + .remove = wcd_spi_remove, +}; + +module_spi_driver(wcd_spi_driver); + +MODULE_DESCRIPTION("WCD SPI driver"); +MODULE_LICENSE("GPL v2"); diff --git a/audio-kernel/asoc/codecs/wcd9335-regmap.c b/audio-kernel/asoc/codecs/wcd9335-regmap.c new file mode 100644 index 000000000..a21257c85 --- /dev/null +++ b/audio-kernel/asoc/codecs/wcd9335-regmap.c @@ -0,0 +1,1611 @@ +/* + * 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 + * 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 +#include +#include "core.h" +#include "wcd9xxx-regmap.h" +#include "wcd9335_registers.h" + +static const struct reg_sequence wcd9335_1_x_defaults[] = { + { WCD9335_CODEC_RPM_CLK_GATE, 0x03, 0x00 }, + { WCD9335_CODEC_RPM_PWR_CPE_DRAM1_SHUTDOWN, 0x1f, 0x00 }, + { WCD9335_CHIP_TIER_CTRL_CHIP_ID_BYTE0, 0x00, 0x00 }, + { WCD9335_CHIP_TIER_CTRL_EFUSE_CTL, 0x00, 0x00 }, + { WCD9335_DATA_HUB_DATA_HUB_RX0_INP_CFG, 0x00, 0x00 }, + { WCD9335_DATA_HUB_DATA_HUB_RX1_INP_CFG, 0x00, 0x00 }, + { WCD9335_DATA_HUB_DATA_HUB_RX2_INP_CFG, 0x00, 0x00 }, + { WCD9335_DATA_HUB_DATA_HUB_RX3_INP_CFG, 0x00, 0x00 }, + { WCD9335_CPE_SS_CPARMAD_BUFRDY_INT_PERIOD, 0x14, 0x00 }, + { WCD9335_CPE_SS_SS_ERROR_INT_MASK, 0x3f, 0x00 }, + { WCD9335_SOC_MAD_AUDIO_IIR_CTL_VAL, 0x00, 0x00 }, + { WCD9335_BIAS_VBG_FINE_ADJ, 0x55, 0x00 }, + { WCD9335_SIDO_SIDO_CCL_2, 0x6c, 0x00 }, + { WCD9335_SIDO_SIDO_CCL_3, 0x2d, 0x00 }, + { WCD9335_SIDO_SIDO_CCL_8, 0x6c, 0x00 }, + { WCD9335_SIDO_SIDO_CCL_10, 0x6c, 0x00 }, + { WCD9335_SIDO_SIDO_DRIVER_2, 0x77, 0x00 }, + { WCD9335_SIDO_SIDO_DRIVER_3, 0x77, 0x00 }, + { WCD9335_SIDO_SIDO_TEST_2, 0x00, 0x00 }, + { WCD9335_MBHC_ZDET_ANA_CTL, 0x00, 0x00 }, + { WCD9335_MBHC_FSM_DEBUG, 0xc0, 0x00 }, + { WCD9335_TX_1_2_ATEST_REFCTL, 0x08, 0x00 }, + { WCD9335_TX_3_4_ATEST_REFCTL, 0x08, 0x00 }, + { WCD9335_TX_5_6_ATEST_REFCTL, 0x08, 0x00 }, + { WCD9335_FLYBACK_VNEG_CTRL_1, 0x67, 0x00 }, + { WCD9335_FLYBACK_VNEG_CTRL_4, 0x5f, 0x00 }, + { WCD9335_FLYBACK_VNEG_CTRL_9, 0x50, 0x00 }, + { WCD9335_FLYBACK_VNEG_DAC_CTRL_1, 0x65, 0x00 }, + { WCD9335_FLYBACK_VNEG_DAC_CTRL_4, 0x40, 0x00 }, + { WCD9335_RX_BIAS_HPH_PA, 0xaa, 0x00 }, + { WCD9335_RX_BIAS_HPH_LOWPOWER, 0x62, 0x00 }, + { WCD9335_HPH_PA_CTL2, 0x40, 0x00 }, + { WCD9335_HPH_L_EN, 0x00, 0x00 }, + { WCD9335_HPH_R_EN, 0x00, 0x00 }, + { WCD9335_HPH_R_ATEST, 0x50, 0x00 }, + { WCD9335_HPH_RDAC_LDO_CTL, 0x00, 0x00 }, + { WCD9335_CDC_TX0_TX_PATH_CFG0, 0x00, 0x00 }, + { WCD9335_CDC_TX0_TX_PATH_CFG1, 0x00, 0x00 }, + { WCD9335_CDC_TX0_TX_PATH_SEC2, 0x00, 0x00 }, + { WCD9335_CDC_TX0_TX_PATH_SEC3, 0x0c, 0x00 }, + { WCD9335_CDC_TX1_TX_PATH_CFG0, 0x00, 0x00 }, + { WCD9335_CDC_TX1_TX_PATH_CFG1, 0x00, 0x00 }, + { WCD9335_CDC_TX1_TX_PATH_SEC2, 0x00, 0x00 }, + { WCD9335_CDC_TX1_TX_PATH_SEC3, 0x0c, 0x00 }, + { WCD9335_CDC_TX2_TX_PATH_CFG0, 0x00, 0x00 }, + { WCD9335_CDC_TX3_TX_PATH_CFG0, 0x00, 0x00 }, + { WCD9335_CDC_TX4_TX_PATH_CFG0, 0x00, 0x00 }, + { WCD9335_CDC_TX5_TX_PATH_CFG0, 0x00, 0x00 }, + { WCD9335_CDC_TX6_TX_PATH_CFG0, 0x00, 0x00 }, + { WCD9335_CDC_TX7_TX_PATH_CFG0, 0x00, 0x00 }, + { WCD9335_CDC_TX8_TX_PATH_CFG0, 0x00, 0x00 }, + { WCD9335_CDC_TX2_TX_PATH_CFG1, 0x00, 0x00 }, + { WCD9335_CDC_TX3_TX_PATH_CFG1, 0x00, 0x00 }, + { WCD9335_CDC_TX4_TX_PATH_CFG1, 0x00, 0x00 }, + { WCD9335_CDC_TX5_TX_PATH_CFG1, 0x00, 0x00 }, + { WCD9335_CDC_TX6_TX_PATH_CFG1, 0x00, 0x00 }, + { WCD9335_CDC_TX7_TX_PATH_CFG1, 0x00, 0x00 }, + { WCD9335_CDC_TX8_TX_PATH_CFG1, 0x00, 0x00 }, + { WCD9335_CDC_TX2_TX_PATH_SEC2, 0x00, 0x00 }, + { WCD9335_CDC_TX3_TX_PATH_SEC2, 0x00, 0x00 }, + { WCD9335_CDC_TX4_TX_PATH_SEC2, 0x00, 0x00 }, + { WCD9335_CDC_TX5_TX_PATH_SEC2, 0x00, 0x00 }, + { WCD9335_CDC_TX6_TX_PATH_SEC2, 0x00, 0x00 }, + { WCD9335_CDC_TX7_TX_PATH_SEC2, 0x00, 0x00 }, + { WCD9335_CDC_TX8_TX_PATH_SEC2, 0x00, 0x00 }, + { WCD9335_CDC_TX2_TX_PATH_SEC3, 0x0c, 0x00 }, + { WCD9335_CDC_TX3_TX_PATH_SEC3, 0x0c, 0x00 }, + { WCD9335_CDC_TX4_TX_PATH_SEC3, 0x0c, 0x00 }, + { WCD9335_CDC_TX5_TX_PATH_SEC3, 0x0c, 0x00 }, + { WCD9335_CDC_TX6_TX_PATH_SEC3, 0x0c, 0x00 }, + { WCD9335_CDC_TX7_TX_PATH_SEC3, 0x0c, 0x00 }, + { WCD9335_CDC_TX8_TX_PATH_SEC3, 0x0c, 0x00 }, + { WCD9335_CDC_COMPANDER1_CTL7, 0x0c, 0x00 }, + { WCD9335_CDC_COMPANDER2_CTL7, 0x0c, 0x00 }, + { WCD9335_CDC_COMPANDER3_CTL7, 0x0c, 0x00 }, + { WCD9335_CDC_COMPANDER4_CTL7, 0x0c, 0x00 }, + { WCD9335_CDC_COMPANDER5_CTL7, 0x0c, 0x00 }, + { WCD9335_CDC_COMPANDER6_CTL7, 0x0c, 0x00 }, + { WCD9335_CDC_COMPANDER7_CTL7, 0x0c, 0x00 }, + { WCD9335_CDC_COMPANDER8_CTL7, 0x0c, 0x00 }, + { WCD9335_CDC_RX0_RX_PATH_CFG1, 0x04, 0x00 }, + { WCD9335_CDC_RX0_RX_PATH_MIX_CFG, 0x0e, 0x00 }, + { WCD9335_CDC_RX0_RX_PATH_SEC0, 0x00, 0x00 }, + { WCD9335_CDC_RX0_RX_PATH_SEC1, 0x00, 0x00 }, + { WCD9335_CDC_RX0_RX_PATH_MIX_SEC0, 0x00, 0x00 }, + { WCD9335_CDC_RX1_RX_PATH_CFG1, 0x04, 0x00 }, + { WCD9335_CDC_RX1_RX_PATH_MIX_CFG, 0x0e, 0x00 }, + { WCD9335_CDC_RX1_RX_PATH_SEC0, 0x00, 0x00 }, + { WCD9335_CDC_RX1_RX_PATH_SEC1, 0x00, 0x00 }, + { WCD9335_CDC_RX1_RX_PATH_MIX_SEC0, 0x00, 0x00 }, + { WCD9335_CDC_RX2_RX_PATH_CFG1, 0x04, 0x00 }, + { WCD9335_CDC_RX2_RX_PATH_MIX_CFG, 0x0e, 0x00 }, + { WCD9335_CDC_RX2_RX_PATH_SEC0, 0x00, 0x00 }, + { WCD9335_CDC_RX2_RX_PATH_SEC1, 0x00, 0x00 }, + { WCD9335_CDC_RX2_RX_PATH_MIX_SEC0, 0x00, 0x00 }, + { WCD9335_CDC_RX3_RX_PATH_CFG1, 0x04, 0x00 }, + { WCD9335_CDC_RX3_RX_PATH_MIX_CFG, 0x0e, 0x00 }, + { WCD9335_CDC_RX3_RX_PATH_SEC0, 0x00, 0x00 }, + { WCD9335_CDC_RX3_RX_PATH_SEC1, 0x00, 0x00 }, + { WCD9335_CDC_RX3_RX_PATH_MIX_SEC0, 0x00, 0x00 }, + { WCD9335_CDC_RX4_RX_PATH_CFG1, 0x04, 0x00 }, + { WCD9335_CDC_RX4_RX_PATH_MIX_CFG, 0x0e, 0x00 }, + { WCD9335_CDC_RX4_RX_PATH_SEC0, 0x00, 0x00 }, + { WCD9335_CDC_RX4_RX_PATH_SEC1, 0x00, 0x00 }, + { WCD9335_CDC_RX4_RX_PATH_MIX_SEC0, 0x00, 0x00 }, + { WCD9335_CDC_RX5_RX_PATH_CFG1, 0x04, 0x00 }, + { WCD9335_CDC_RX5_RX_PATH_MIX_CFG, 0x0e, 0x00 }, + { WCD9335_CDC_RX5_RX_PATH_SEC0, 0x00, 0x00 }, + { WCD9335_CDC_RX5_RX_PATH_SEC1, 0x00, 0x00 }, + { WCD9335_CDC_RX5_RX_PATH_MIX_SEC0, 0x00, 0x00 }, + { WCD9335_CDC_RX6_RX_PATH_CFG1, 0x04, 0x00 }, + { WCD9335_CDC_RX6_RX_PATH_MIX_CFG, 0x0e, 0x00 }, + { WCD9335_CDC_RX6_RX_PATH_SEC0, 0x00, 0x00 }, + { WCD9335_CDC_RX6_RX_PATH_SEC1, 0x00, 0x00 }, + { WCD9335_CDC_RX6_RX_PATH_MIX_SEC0, 0x00, 0x00 }, + { WCD9335_CDC_RX7_RX_PATH_CFG1, 0x04, 0x00 }, + { WCD9335_CDC_RX7_RX_PATH_MIX_CFG, 0x0e, 0x00 }, + { WCD9335_CDC_RX7_RX_PATH_SEC0, 0x00, 0x00 }, + { WCD9335_CDC_RX7_RX_PATH_SEC1, 0x00, 0x00 }, + { WCD9335_CDC_RX7_RX_PATH_MIX_SEC0, 0x00, 0x00 }, + { WCD9335_CDC_RX8_RX_PATH_CFG1, 0x04, 0x00 }, + { WCD9335_CDC_RX8_RX_PATH_MIX_CFG, 0x0e, 0x00 }, + { WCD9335_CDC_RX8_RX_PATH_SEC0, 0x00, 0x00 }, + { WCD9335_CDC_RX8_RX_PATH_SEC1, 0x00, 0x00 }, + { WCD9335_CDC_RX8_RX_PATH_MIX_SEC0, 0x00, 0x00 }, + { WCD9335_SPLINE_SRC0_CLK_RST_CTL_0, 0x00, 0x00 }, + { WCD9335_SPLINE_SRC1_CLK_RST_CTL_0, 0x00, 0x00 }, + { WCD9335_SPLINE_SRC2_CLK_RST_CTL_0, 0x00, 0x00 }, + { WCD9335_SPLINE_SRC3_CLK_RST_CTL_0, 0x00, 0x00 }, + { WCD9335_CDC_CLK_RST_CTRL_FS_CNT_CONTROL, 0x00, 0x00 }, + { WCD9335_TEST_DEBUG_NPL_DLY_TEST_1, 0x00, 0x00 }, + { WCD9335_TEST_DEBUG_NPL_DLY_TEST_2, 0x00, 0x00 }, +}; + +static const struct reg_sequence wcd9335_2_0_defaults[] = { + { WCD9335_CODEC_RPM_CLK_GATE, 0x07, 0x00 }, + { WCD9335_CODEC_RPM_PWR_CPE_DRAM1_SHUTDOWN, 0x3f, 0x00 }, + { WCD9335_CHIP_TIER_CTRL_CHIP_ID_BYTE0, 0x01, 0x00 }, + { WCD9335_CHIP_TIER_CTRL_EFUSE_CTL, 0x10, 0x00 }, + { WCD9335_DATA_HUB_DATA_HUB_RX0_INP_CFG, 0x08, 0x00 }, + { WCD9335_DATA_HUB_DATA_HUB_RX1_INP_CFG, 0x08, 0x00 }, + { WCD9335_DATA_HUB_DATA_HUB_RX2_INP_CFG, 0x08, 0x00 }, + { WCD9335_DATA_HUB_DATA_HUB_RX3_INP_CFG, 0x08, 0x00 }, + { WCD9335_CPE_SS_CPARMAD_BUFRDY_INT_PERIOD, 0x13, 0x00 }, + { WCD9335_CPE_SS_SS_ERROR_INT_MASK, 0xff, 0x00 }, + { WCD9335_SOC_MAD_AUDIO_IIR_CTL_VAL, 0x40, 0x00 }, + { WCD9335_BIAS_VBG_FINE_ADJ, 0xc5, 0x00 }, + { WCD9335_SIDO_SIDO_CCL_2, 0x92, 0x00 }, + { WCD9335_SIDO_SIDO_CCL_3, 0x35, 0x00 }, + { WCD9335_SIDO_SIDO_CCL_8, 0x6e, 0x00 }, + { WCD9335_SIDO_SIDO_CCL_10, 0x6e, 0x00 }, + { WCD9335_SIDO_SIDO_DRIVER_2, 0x55, 0x00 }, + { WCD9335_SIDO_SIDO_DRIVER_3, 0x55, 0x00 }, + { WCD9335_SIDO_SIDO_TEST_2, 0x0f, 0x00 }, + { WCD9335_MBHC_ZDET_ANA_CTL, 0x0f, 0x00 }, + { WCD9335_TX_1_2_ATEST_REFCTL, 0x0a, 0x00 }, + { WCD9335_TX_3_4_ATEST_REFCTL, 0x0a, 0x00 }, + { WCD9335_TX_5_6_ATEST_REFCTL, 0x0a, 0x00 }, + { WCD9335_FLYBACK_VNEG_CTRL_1, 0xeb, 0x00 }, + { WCD9335_FLYBACK_VNEG_CTRL_4, 0x7f, 0x00 }, + { WCD9335_FLYBACK_VNEG_CTRL_9, 0x64, 0x00 }, + { WCD9335_FLYBACK_VNEG_DAC_CTRL_1, 0xed, 0x00 }, + { WCD9335_RX_BIAS_HPH_PA, 0x9a, 0x00 }, + { WCD9335_RX_BIAS_HPH_LOWPOWER, 0x82, 0x00 }, + { WCD9335_HPH_PA_CTL2, 0x50, 0x00 }, + { WCD9335_HPH_L_EN, 0x80, 0x00 }, + { WCD9335_HPH_R_EN, 0x80, 0x00 }, + { WCD9335_HPH_R_ATEST, 0x54, 0x00 }, + { WCD9335_HPH_RDAC_LDO_CTL, 0x33, 0x00 }, + { WCD9335_CDC_TX0_TX_PATH_CFG0, 0x10, 0x00 }, + { WCD9335_CDC_TX0_TX_PATH_CFG1, 0x02, 0x00 }, + { WCD9335_CDC_TX0_TX_PATH_SEC2, 0x01, 0x00 }, + { WCD9335_CDC_TX0_TX_PATH_SEC3, 0x3c, 0x00 }, + { WCD9335_CDC_TX1_TX_PATH_CFG0, 0x10, 0x00 }, + { WCD9335_CDC_TX1_TX_PATH_CFG1, 0x02, 0x00 }, + { WCD9335_CDC_TX1_TX_PATH_SEC2, 0x01, 0x00 }, + { WCD9335_CDC_TX1_TX_PATH_SEC3, 0x3c, 0x00 }, + { WCD9335_CDC_TX2_TX_PATH_CFG0, 0x10, 0x00 }, + { WCD9335_CDC_TX3_TX_PATH_CFG0, 0x10, 0x00 }, + { WCD9335_CDC_TX4_TX_PATH_CFG0, 0x10, 0x00 }, + { WCD9335_CDC_TX5_TX_PATH_CFG0, 0x10, 0x00 }, + { WCD9335_CDC_TX6_TX_PATH_CFG0, 0x10, 0x00 }, + { WCD9335_CDC_TX7_TX_PATH_CFG0, 0x10, 0x00 }, + { WCD9335_CDC_TX8_TX_PATH_CFG0, 0x10, 0x00 }, + { WCD9335_CDC_TX2_TX_PATH_CFG1, 0x02, 0x00 }, + { WCD9335_CDC_TX3_TX_PATH_CFG1, 0x02, 0x00 }, + { WCD9335_CDC_TX4_TX_PATH_CFG1, 0x02, 0x00 }, + { WCD9335_CDC_TX5_TX_PATH_CFG1, 0x02, 0x00 }, + { WCD9335_CDC_TX6_TX_PATH_CFG1, 0x02, 0x00 }, + { WCD9335_CDC_TX7_TX_PATH_CFG1, 0x02, 0x00 }, + { WCD9335_CDC_TX8_TX_PATH_CFG1, 0x02, 0x00 }, + { WCD9335_CDC_TX2_TX_PATH_SEC2, 0x01, 0x00 }, + { WCD9335_CDC_TX3_TX_PATH_SEC2, 0x01, 0x00 }, + { WCD9335_CDC_TX4_TX_PATH_SEC2, 0x01, 0x00 }, + { WCD9335_CDC_TX5_TX_PATH_SEC2, 0x01, 0x00 }, + { WCD9335_CDC_TX6_TX_PATH_SEC2, 0x01, 0x00 }, + { WCD9335_CDC_TX7_TX_PATH_SEC2, 0x01, 0x00 }, + { WCD9335_CDC_TX8_TX_PATH_SEC2, 0x01, 0x00 }, + { WCD9335_CDC_TX2_TX_PATH_SEC3, 0x3c, 0x00 }, + { WCD9335_CDC_TX3_TX_PATH_SEC3, 0x3c, 0x00 }, + { WCD9335_CDC_TX4_TX_PATH_SEC3, 0x3c, 0x00 }, + { WCD9335_CDC_TX5_TX_PATH_SEC3, 0x3c, 0x00 }, + { WCD9335_CDC_TX6_TX_PATH_SEC3, 0x3c, 0x00 }, + { WCD9335_CDC_TX7_TX_PATH_SEC3, 0x3c, 0x00 }, + { WCD9335_CDC_TX8_TX_PATH_SEC3, 0x3c, 0x00 }, + { WCD9335_CDC_COMPANDER1_CTL7, 0x08, 0x00 }, + { WCD9335_CDC_COMPANDER2_CTL7, 0x08, 0x00 }, + { WCD9335_CDC_COMPANDER3_CTL7, 0x08, 0x00 }, + { WCD9335_CDC_COMPANDER4_CTL7, 0x08, 0x00 }, + { WCD9335_CDC_COMPANDER5_CTL7, 0x08, 0x00 }, + { WCD9335_CDC_COMPANDER6_CTL7, 0x08, 0x00 }, + { WCD9335_CDC_COMPANDER7_CTL7, 0x08, 0x00 }, + { WCD9335_CDC_COMPANDER8_CTL7, 0x08, 0x00 }, + { WCD9335_CDC_RX0_RX_PATH_CFG1, 0x44, 0x00 }, + { WCD9335_CDC_RX0_RX_PATH_MIX_CFG, 0x1e, 0x00 }, + { WCD9335_CDC_RX0_RX_PATH_SEC0, 0xfc, 0x00 }, + { WCD9335_CDC_RX0_RX_PATH_SEC1, 0x08, 0x00 }, + { WCD9335_CDC_RX0_RX_PATH_MIX_SEC0, 0x08, 0x00 }, + { WCD9335_CDC_RX1_RX_PATH_CFG1, 0x44, 0x00 }, + { WCD9335_CDC_RX1_RX_PATH_MIX_CFG, 0x1e, 0x00 }, + { WCD9335_CDC_RX1_RX_PATH_SEC0, 0xfc, 0x00 }, + { WCD9335_CDC_RX1_RX_PATH_SEC1, 0x08, 0x00 }, + { WCD9335_CDC_RX1_RX_PATH_MIX_SEC0, 0x08, 0x00 }, + { WCD9335_CDC_RX2_RX_PATH_CFG1, 0x44, 0x00 }, + { WCD9335_CDC_RX2_RX_PATH_MIX_CFG, 0x1e, 0x00 }, + { WCD9335_CDC_RX2_RX_PATH_SEC0, 0xfc, 0x00 }, + { WCD9335_CDC_RX2_RX_PATH_SEC1, 0x08, 0x00 }, + { WCD9335_CDC_RX2_RX_PATH_MIX_SEC0, 0x08, 0x00 }, + { WCD9335_CDC_RX3_RX_PATH_CFG1, 0x44, 0x00 }, + { WCD9335_CDC_RX3_RX_PATH_MIX_CFG, 0x1e, 0x00 }, + { WCD9335_CDC_RX3_RX_PATH_SEC0, 0xfc, 0x00 }, + { WCD9335_CDC_RX3_RX_PATH_SEC1, 0x08, 0x00 }, + { WCD9335_CDC_RX3_RX_PATH_MIX_SEC0, 0x08, 0x00 }, + { WCD9335_CDC_RX4_RX_PATH_CFG1, 0x44, 0x00 }, + { WCD9335_CDC_RX4_RX_PATH_MIX_CFG, 0x1e, 0x00 }, + { WCD9335_CDC_RX4_RX_PATH_SEC0, 0xfc, 0x00 }, + { WCD9335_CDC_RX4_RX_PATH_SEC1, 0x08, 0x00 }, + { WCD9335_CDC_RX4_RX_PATH_MIX_SEC0, 0x08, 0x00 }, + { WCD9335_CDC_RX5_RX_PATH_CFG1, 0x44, 0x00 }, + { WCD9335_CDC_RX5_RX_PATH_MIX_CFG, 0x1e, 0x00 }, + { WCD9335_CDC_RX5_RX_PATH_SEC0, 0xfc, 0x00 }, + { WCD9335_CDC_RX5_RX_PATH_SEC1, 0x08, 0x00 }, + { WCD9335_CDC_RX5_RX_PATH_MIX_SEC0, 0x08, 0x00 }, + { WCD9335_CDC_RX6_RX_PATH_CFG1, 0x44, 0x00 }, + { WCD9335_CDC_RX6_RX_PATH_MIX_CFG, 0x1e, 0x00 }, + { WCD9335_CDC_RX6_RX_PATH_SEC0, 0xfc, 0x00 }, + { WCD9335_CDC_RX6_RX_PATH_SEC1, 0x08, 0x00 }, + { WCD9335_CDC_RX6_RX_PATH_MIX_SEC0, 0x08, 0x00 }, + { WCD9335_CDC_RX7_RX_PATH_CFG1, 0x44, 0x00 }, + { WCD9335_CDC_RX7_RX_PATH_MIX_CFG, 0x1e, 0x00 }, + { WCD9335_CDC_RX7_RX_PATH_SEC0, 0xfc, 0x00 }, + { WCD9335_CDC_RX7_RX_PATH_SEC1, 0x08, 0x00 }, + { WCD9335_CDC_RX7_RX_PATH_MIX_SEC0, 0x08, 0x00 }, + { WCD9335_CDC_RX8_RX_PATH_CFG1, 0x44, 0x00 }, + { WCD9335_CDC_RX8_RX_PATH_MIX_CFG, 0x1e, 0x00 }, + { WCD9335_CDC_RX8_RX_PATH_SEC0, 0xfc, 0x00 }, + { WCD9335_CDC_RX8_RX_PATH_SEC1, 0x08, 0x00 }, + { WCD9335_CDC_RX8_RX_PATH_MIX_SEC0, 0x08, 0x00 }, + { WCD9335_SPLINE_SRC0_CLK_RST_CTL_0, 0x20, 0x00 }, + { WCD9335_SPLINE_SRC1_CLK_RST_CTL_0, 0x20, 0x00 }, + { WCD9335_SPLINE_SRC2_CLK_RST_CTL_0, 0x20, 0x00 }, + { WCD9335_SPLINE_SRC3_CLK_RST_CTL_0, 0x20, 0x00 }, + { WCD9335_CDC_CLK_RST_CTRL_FS_CNT_CONTROL, 0x0c, 0x00 }, + { WCD9335_TEST_DEBUG_NPL_DLY_TEST_1, 0x10, 0x00 }, + { WCD9335_TEST_DEBUG_NPL_DLY_TEST_2, 0x60, 0x00 }, + { WCD9335_DATA_HUB_NATIVE_FIFO_SYNC, 0x00, 0x00 }, + { WCD9335_DATA_HUB_NATIVE_FIFO_STATUS, 0x00, 0x00 }, + { WCD9335_CPE_SS_TX_PP_BUF_INT_PERIOD, 0x60, 0x00 }, + { WCD9335_CPE_SS_TX_PP_CFG, 0x3C, 0x00 }, + { WCD9335_CPE_SS_SVA_CFG, 0x00, 0x00 }, + { WCD9335_MBHC_FSM_STATUS, 0x00, 0x00 }, + { WCD9335_FLYBACK_CTRL_1, 0x45, 0x00 }, + { WCD9335_CDC_TX0_TX_PATH_SEC7, 0x25, 0x00 }, + { WCD9335_SPLINE_SRC0_STATUS, 0x00, 0x00 }, + { WCD9335_SPLINE_SRC1_STATUS, 0x00, 0x00 }, + { WCD9335_SPLINE_SRC2_STATUS, 0x00, 0x00 }, + { WCD9335_SPLINE_SRC3_STATUS, 0x00, 0x00 }, + { WCD9335_CDC_PROX_DETECT_PROX_CTL_REPEAT_PAT, 0x00, 0x00 }, +}; + +static const struct reg_default wcd9335_defaults[] = { + /* Page #0 registers */ + { WCD9335_PAGE0_PAGE_REGISTER, 0x00 }, + { WCD9335_CODEC_RPM_CLK_BYPASS, 0x00 }, + { WCD9335_CODEC_RPM_CLK_MCLK_CFG, 0x00 }, + { WCD9335_CODEC_RPM_RST_CTL, 0x00 }, + { WCD9335_CODEC_RPM_PWR_CDC_DIG_HM_CTL, 0x07 }, + { WCD9335_CODEC_RPM_PWR_CPE_DEEPSLP_1, 0x00 }, + { WCD9335_CODEC_RPM_PWR_CPE_DEEPSLP_2, 0x00 }, + { WCD9335_CODEC_RPM_PWR_CPE_DEEPSLP_3, 0x00 }, + { WCD9335_CODEC_RPM_PWR_CPE_IRAM_SHUTDOWN, 0x01 }, + { WCD9335_CODEC_RPM_PWR_CPE_DRAM0_SHUTDOWN_1, 0xff }, + { WCD9335_CODEC_RPM_PWR_CPE_DRAM0_SHUTDOWN_2, 0xff }, + { WCD9335_CODEC_RPM_INT_MASK, 0x3f }, + { WCD9335_CODEC_RPM_INT_STATUS, 0x00 }, + { WCD9335_CODEC_RPM_INT_CLEAR, 0x00 }, + { WCD9335_CHIP_TIER_CTRL_CHIP_ID_BYTE1, 0x00 }, + { WCD9335_CHIP_TIER_CTRL_CHIP_ID_BYTE2, 0x07 }, + { WCD9335_CHIP_TIER_CTRL_CHIP_ID_BYTE3, 0x01 }, + { WCD9335_CHIP_TIER_CTRL_EFUSE_TEST0, 0x00 }, + { WCD9335_CHIP_TIER_CTRL_EFUSE_TEST1, 0x00 }, + { WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT0, 0x00 }, + { WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT1, 0x00 }, + { WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT2, 0x00 }, + { WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT3, 0x00 }, + { WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT4, 0x00 }, + { WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT5, 0x00 }, + { WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT6, 0x00 }, + { WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT7, 0x00 }, + { WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT8, 0x00 }, + { WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT9, 0x00 }, + { WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT10, 0x00 }, + { WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT11, 0x00 }, + { WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT12, 0x00 }, + { WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT13, 0x00 }, + { WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT14, 0x00 }, + { WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT15, 0x00 }, + { WCD9335_CHIP_TIER_CTRL_EFUSE_STATUS, 0x00 }, + { WCD9335_CHIP_TIER_CTRL_I2C_SLAVE_ID_NONNEGO, 0x0d }, + { WCD9335_CHIP_TIER_CTRL_I2C_SLAVE_ID_1, 0x00 }, + { WCD9335_CHIP_TIER_CTRL_I2C_SLAVE_ID_2, 0x00 }, + { WCD9335_CHIP_TIER_CTRL_I2C_SLAVE_ID_3, 0x00 }, + { WCD9335_CHIP_TIER_CTRL_ANA_WAIT_STATE_CTL, 0xCC }, + { WCD9335_CHIP_TIER_CTRL_I2C_ACTIVE, 0x00 }, + { WCD9335_CHIP_TIER_CTRL_PROC1_MON_CTL, 0x06 }, + { WCD9335_CHIP_TIER_CTRL_PROC1_MON_STATUS, 0x00 }, + { WCD9335_CHIP_TIER_CTRL_PROC1_MON_CNT_MSB, 0x00 }, + { WCD9335_CHIP_TIER_CTRL_PROC1_MON_CNT_LSB, 0x00 }, + { WCD9335_CHIP_TIER_CTRL_PROC2_MON_CTL, 0x06 }, + { WCD9335_CHIP_TIER_CTRL_PROC2_MON_STATUS, 0x00 }, + { WCD9335_CHIP_TIER_CTRL_PROC2_MON_CNT_MSB, 0x00 }, + { WCD9335_CHIP_TIER_CTRL_PROC2_MON_CNT_LSB, 0x00 }, + { WCD9335_CHIP_TIER_CTRL_PROC3_MON_CTL, 0x06 }, + { WCD9335_CHIP_TIER_CTRL_PROC3_MON_STATUS, 0x00 }, + { WCD9335_CHIP_TIER_CTRL_PROC3_MON_CNT_MSB, 0x00 }, + { WCD9335_CHIP_TIER_CTRL_PROC3_MON_CNT_LSB, 0x00 }, + { WCD9335_DATA_HUB_DATA_HUB_RX_I2S_CTL, 0x0c }, + { WCD9335_DATA_HUB_DATA_HUB_TX_I2S_CTL, 0x0c }, + { WCD9335_DATA_HUB_DATA_HUB_I2S_CLK, 0x00 }, + { WCD9335_DATA_HUB_DATA_HUB_RX4_INP_CFG, 0x00 }, + { WCD9335_DATA_HUB_DATA_HUB_RX5_INP_CFG, 0x00 }, + { WCD9335_DATA_HUB_DATA_HUB_RX6_INP_CFG, 0x00 }, + { WCD9335_DATA_HUB_DATA_HUB_RX7_INP_CFG, 0x00 }, + { WCD9335_DATA_HUB_DATA_HUB_SB_TX0_INP_CFG, 0x00 }, + { WCD9335_DATA_HUB_DATA_HUB_SB_TX1_INP_CFG, 0x00 }, + { WCD9335_DATA_HUB_DATA_HUB_SB_TX2_INP_CFG, 0x00 }, + { WCD9335_DATA_HUB_DATA_HUB_SB_TX3_INP_CFG, 0x00 }, + { WCD9335_DATA_HUB_DATA_HUB_SB_TX4_INP_CFG, 0x00 }, + { WCD9335_DATA_HUB_DATA_HUB_SB_TX5_INP_CFG, 0x00 }, + { WCD9335_DATA_HUB_DATA_HUB_SB_TX6_INP_CFG, 0x00 }, + { WCD9335_DATA_HUB_DATA_HUB_SB_TX7_INP_CFG, 0x00 }, + { WCD9335_DATA_HUB_DATA_HUB_SB_TX8_INP_CFG, 0x00 }, + { WCD9335_DATA_HUB_DATA_HUB_SB_TX9_INP_CFG, 0x00 }, + { WCD9335_DATA_HUB_DATA_HUB_SB_TX10_INP_CFG, 0x00 }, + { WCD9335_DATA_HUB_DATA_HUB_SB_TX11_INP_CFG, 0x00 }, + { WCD9335_DATA_HUB_DATA_HUB_SB_TX13_INP_CFG, 0x00 }, + { WCD9335_DATA_HUB_DATA_HUB_SB_TX14_INP_CFG, 0x00 }, + { WCD9335_DATA_HUB_DATA_HUB_SB_TX15_INP_CFG, 0x00 }, + { WCD9335_DATA_HUB_DATA_HUB_TX_I2S_SD0_L_CFG, 0x00 }, + { WCD9335_DATA_HUB_DATA_HUB_TX_I2S_SD0_R_CFG, 0x00 }, + { WCD9335_DATA_HUB_DATA_HUB_TX_I2S_SD1_L_CFG, 0x00 }, + { WCD9335_DATA_HUB_DATA_HUB_TX_I2S_SD1_R_CFG, 0x00 }, + { WCD9335_INTR_CFG, 0x00 }, + { WCD9335_INTR_CLR_COMMIT, 0x00 }, + { WCD9335_INTR_PIN1_MASK0, 0xff }, + { WCD9335_INTR_PIN1_MASK1, 0xff }, + { WCD9335_INTR_PIN1_MASK2, 0xff }, + { WCD9335_INTR_PIN1_MASK3, 0xff }, + { WCD9335_INTR_PIN1_STATUS0, 0x00 }, + { WCD9335_INTR_PIN1_STATUS1, 0x00 }, + { WCD9335_INTR_PIN1_STATUS2, 0x00 }, + { WCD9335_INTR_PIN1_STATUS3, 0x00 }, + { WCD9335_INTR_PIN1_CLEAR0, 0x00 }, + { WCD9335_INTR_PIN1_CLEAR1, 0x00 }, + { WCD9335_INTR_PIN1_CLEAR2, 0x00 }, + { WCD9335_INTR_PIN1_CLEAR3, 0x00 }, + { WCD9335_INTR_PIN2_MASK0, 0xff }, + { WCD9335_INTR_PIN2_MASK1, 0xff }, + { WCD9335_INTR_PIN2_MASK2, 0xff }, + { WCD9335_INTR_PIN2_MASK3, 0xff }, + { WCD9335_INTR_PIN2_STATUS0, 0x00 }, + { WCD9335_INTR_PIN2_STATUS1, 0x00 }, + { WCD9335_INTR_PIN2_STATUS2, 0x00 }, + { WCD9335_INTR_PIN2_STATUS3, 0x00 }, + { WCD9335_INTR_PIN2_CLEAR0, 0x00 }, + { WCD9335_INTR_PIN2_CLEAR1, 0x00 }, + { WCD9335_INTR_PIN2_CLEAR2, 0x00 }, + { WCD9335_INTR_PIN2_CLEAR3, 0x00 }, + { WCD9335_INTR_LEVEL0, 0x03 }, + { WCD9335_INTR_LEVEL1, 0xe0 }, + { WCD9335_INTR_LEVEL2, 0x10 }, + { WCD9335_INTR_LEVEL3, 0x80 }, + { WCD9335_INTR_BYPASS0, 0x00 }, + { WCD9335_INTR_BYPASS1, 0x00 }, + { WCD9335_INTR_BYPASS2, 0x00 }, + { WCD9335_INTR_BYPASS3, 0x00 }, + { WCD9335_INTR_SET0, 0x00 }, + { WCD9335_INTR_SET1, 0x00 }, + { WCD9335_INTR_SET2, 0x00 }, + { WCD9335_INTR_SET3, 0x00 }, + /* Page #1 registers */ + { WCD9335_PAGE1_PAGE_REGISTER, 0x00 }, + { WCD9335_CPE_FLL_USER_CTL_0, 0x71 }, + { WCD9335_CPE_FLL_USER_CTL_1, 0x34 }, + { WCD9335_CPE_FLL_USER_CTL_2, 0x0b }, + { WCD9335_CPE_FLL_USER_CTL_3, 0x02 }, + { WCD9335_CPE_FLL_USER_CTL_4, 0x04 }, + { WCD9335_CPE_FLL_USER_CTL_5, 0x02 }, + { WCD9335_CPE_FLL_USER_CTL_6, 0x64 }, + { WCD9335_CPE_FLL_USER_CTL_7, 0x00 }, + { WCD9335_CPE_FLL_USER_CTL_8, 0x94 }, + { WCD9335_CPE_FLL_USER_CTL_9, 0x70 }, + { WCD9335_CPE_FLL_L_VAL_CTL_0, 0x40 }, + { WCD9335_CPE_FLL_L_VAL_CTL_1, 0x00 }, + { WCD9335_CPE_FLL_DSM_FRAC_CTL_0, 0x00 }, + { WCD9335_CPE_FLL_DSM_FRAC_CTL_1, 0xff }, + { WCD9335_CPE_FLL_CONFIG_CTL_0, 0x6b }, + { WCD9335_CPE_FLL_CONFIG_CTL_1, 0x05 }, + { WCD9335_CPE_FLL_CONFIG_CTL_2, 0x08 }, + { WCD9335_CPE_FLL_CONFIG_CTL_3, 0x00 }, + { WCD9335_CPE_FLL_CONFIG_CTL_4, 0x10 }, + { WCD9335_CPE_FLL_TEST_CTL_0, 0x00 }, + { WCD9335_CPE_FLL_TEST_CTL_1, 0x00 }, + { WCD9335_CPE_FLL_TEST_CTL_2, 0x00 }, + { WCD9335_CPE_FLL_TEST_CTL_3, 0x00 }, + { WCD9335_CPE_FLL_TEST_CTL_4, 0x00 }, + { WCD9335_CPE_FLL_TEST_CTL_5, 0x00 }, + { WCD9335_CPE_FLL_TEST_CTL_6, 0x00 }, + { WCD9335_CPE_FLL_TEST_CTL_7, 0x33 }, + { WCD9335_CPE_FLL_FREQ_CTL_0, 0x00 }, + { WCD9335_CPE_FLL_FREQ_CTL_1, 0x00 }, + { WCD9335_CPE_FLL_FREQ_CTL_2, 0x00 }, + { WCD9335_CPE_FLL_FREQ_CTL_3, 0x00 }, + { WCD9335_CPE_FLL_SSC_CTL_0, 0x00 }, + { WCD9335_CPE_FLL_SSC_CTL_1, 0x00 }, + { WCD9335_CPE_FLL_SSC_CTL_2, 0x00 }, + { WCD9335_CPE_FLL_SSC_CTL_3, 0x00 }, + { WCD9335_CPE_FLL_FLL_MODE, 0x20 }, + { WCD9335_CPE_FLL_STATUS_0, 0x00 }, + { WCD9335_CPE_FLL_STATUS_1, 0x00 }, + { WCD9335_CPE_FLL_STATUS_2, 0x00 }, + { WCD9335_CPE_FLL_STATUS_3, 0x00 }, + { WCD9335_I2S_FLL_USER_CTL_0, 0x41 }, + { WCD9335_I2S_FLL_USER_CTL_1, 0x94 }, + { WCD9335_I2S_FLL_USER_CTL_2, 0x08 }, + { WCD9335_I2S_FLL_USER_CTL_3, 0x02 }, + { WCD9335_I2S_FLL_USER_CTL_4, 0x04 }, + { WCD9335_I2S_FLL_USER_CTL_5, 0x02 }, + { WCD9335_I2S_FLL_USER_CTL_6, 0x40 }, + { WCD9335_I2S_FLL_USER_CTL_7, 0x00 }, + { WCD9335_I2S_FLL_USER_CTL_8, 0x5f }, + { WCD9335_I2S_FLL_USER_CTL_9, 0x02 }, + { WCD9335_I2S_FLL_L_VAL_CTL_0, 0x40 }, + { WCD9335_I2S_FLL_L_VAL_CTL_1, 0x00 }, + { WCD9335_I2S_FLL_DSM_FRAC_CTL_0, 0x00 }, + { WCD9335_I2S_FLL_DSM_FRAC_CTL_1, 0xff }, + { WCD9335_I2S_FLL_CONFIG_CTL_0, 0x6b }, + { WCD9335_I2S_FLL_CONFIG_CTL_1, 0x05 }, + { WCD9335_I2S_FLL_CONFIG_CTL_2, 0x08 }, + { WCD9335_I2S_FLL_CONFIG_CTL_3, 0x00 }, + { WCD9335_I2S_FLL_CONFIG_CTL_4, 0x30 }, + { WCD9335_I2S_FLL_TEST_CTL_0, 0x80 }, + { WCD9335_I2S_FLL_TEST_CTL_1, 0x00 }, + { WCD9335_I2S_FLL_TEST_CTL_2, 0x00 }, + { WCD9335_I2S_FLL_TEST_CTL_3, 0x00 }, + { WCD9335_I2S_FLL_TEST_CTL_4, 0x00 }, + { WCD9335_I2S_FLL_TEST_CTL_5, 0x00 }, + { WCD9335_I2S_FLL_TEST_CTL_6, 0x00 }, + { WCD9335_I2S_FLL_TEST_CTL_7, 0xff }, + { WCD9335_I2S_FLL_FREQ_CTL_0, 0x00 }, + { WCD9335_I2S_FLL_FREQ_CTL_1, 0x00 }, + { WCD9335_I2S_FLL_FREQ_CTL_2, 0x00 }, + { WCD9335_I2S_FLL_FREQ_CTL_3, 0x00 }, + { WCD9335_I2S_FLL_SSC_CTL_0, 0x00 }, + { WCD9335_I2S_FLL_SSC_CTL_1, 0x00 }, + { WCD9335_I2S_FLL_SSC_CTL_2, 0x00 }, + { WCD9335_I2S_FLL_SSC_CTL_3, 0x00 }, + { WCD9335_I2S_FLL_FLL_MODE, 0x00 }, + { WCD9335_I2S_FLL_STATUS_0, 0x00 }, + { WCD9335_I2S_FLL_STATUS_1, 0x00 }, + { WCD9335_I2S_FLL_STATUS_2, 0x00 }, + { WCD9335_I2S_FLL_STATUS_3, 0x00 }, + { WCD9335_SB_FLL_USER_CTL_0, 0x41 }, + { WCD9335_SB_FLL_USER_CTL_1, 0x94 }, + { WCD9335_SB_FLL_USER_CTL_2, 0x08 }, + { WCD9335_SB_FLL_USER_CTL_3, 0x02 }, + { WCD9335_SB_FLL_USER_CTL_4, 0x04 }, + { WCD9335_SB_FLL_USER_CTL_5, 0x02 }, + { WCD9335_SB_FLL_USER_CTL_6, 0x40 }, + { WCD9335_SB_FLL_USER_CTL_7, 0x00 }, + { WCD9335_SB_FLL_USER_CTL_8, 0x5e }, + { WCD9335_SB_FLL_USER_CTL_9, 0x01 }, + { WCD9335_SB_FLL_L_VAL_CTL_0, 0x40 }, + { WCD9335_SB_FLL_L_VAL_CTL_1, 0x00 }, + { WCD9335_SB_FLL_DSM_FRAC_CTL_0, 0x00 }, + { WCD9335_SB_FLL_DSM_FRAC_CTL_1, 0xff }, + { WCD9335_SB_FLL_CONFIG_CTL_0, 0x6b }, + { WCD9335_SB_FLL_CONFIG_CTL_1, 0x05 }, + { WCD9335_SB_FLL_CONFIG_CTL_2, 0x08 }, + { WCD9335_SB_FLL_CONFIG_CTL_3, 0x00 }, + { WCD9335_SB_FLL_CONFIG_CTL_4, 0x10 }, + { WCD9335_SB_FLL_TEST_CTL_0, 0x00 }, + { WCD9335_SB_FLL_TEST_CTL_1, 0x00 }, + { WCD9335_SB_FLL_TEST_CTL_2, 0x00 }, + { WCD9335_SB_FLL_TEST_CTL_3, 0x00 }, + { WCD9335_SB_FLL_TEST_CTL_4, 0x00 }, + { WCD9335_SB_FLL_TEST_CTL_5, 0x00 }, + { WCD9335_SB_FLL_TEST_CTL_6, 0x00 }, + { WCD9335_SB_FLL_TEST_CTL_7, 0xff }, + { WCD9335_SB_FLL_FREQ_CTL_0, 0x00 }, + { WCD9335_SB_FLL_FREQ_CTL_1, 0x00 }, + { WCD9335_SB_FLL_FREQ_CTL_2, 0x00 }, + { WCD9335_SB_FLL_FREQ_CTL_3, 0x00 }, + { WCD9335_SB_FLL_SSC_CTL_0, 0x00 }, + { WCD9335_SB_FLL_SSC_CTL_1, 0x00 }, + { WCD9335_SB_FLL_SSC_CTL_2, 0x00 }, + { WCD9335_SB_FLL_SSC_CTL_3, 0x00 }, + { WCD9335_SB_FLL_FLL_MODE, 0x00 }, + { WCD9335_SB_FLL_STATUS_0, 0x00 }, + { WCD9335_SB_FLL_STATUS_1, 0x00 }, + { WCD9335_SB_FLL_STATUS_2, 0x00 }, + { WCD9335_SB_FLL_STATUS_3, 0x00 }, + /* Page #2 registers */ + { WCD9335_PAGE2_PAGE_REGISTER, 0x00 }, + { WCD9335_CPE_SS_MEM_PTR_0, 0x00 }, + { WCD9335_CPE_SS_MEM_PTR_1, 0x00 }, + { WCD9335_CPE_SS_MEM_PTR_2, 0x00 }, + { WCD9335_CPE_SS_MEM_CTRL, 0x08 }, + { WCD9335_CPE_SS_MEM_BANK_0, 0x00 }, + { WCD9335_CPE_SS_MEM_BANK_1, 0x00 }, + { WCD9335_CPE_SS_MEM_BANK_2, 0x00 }, + { WCD9335_CPE_SS_MEM_BANK_3, 0x00 }, + { WCD9335_CPE_SS_MEM_BANK_4, 0x00 }, + { WCD9335_CPE_SS_MEM_BANK_5, 0x00 }, + { WCD9335_CPE_SS_MEM_BANK_6, 0x00 }, + { WCD9335_CPE_SS_MEM_BANK_7, 0x00 }, + { WCD9335_CPE_SS_MEM_BANK_8, 0x00 }, + { WCD9335_CPE_SS_MEM_BANK_9, 0x00 }, + { WCD9335_CPE_SS_MEM_BANK_10, 0x00 }, + { WCD9335_CPE_SS_MEM_BANK_11, 0x00 }, + { WCD9335_CPE_SS_MEM_BANK_12, 0x00 }, + { WCD9335_CPE_SS_MEM_BANK_13, 0x00 }, + { WCD9335_CPE_SS_MEM_BANK_14, 0x00 }, + { WCD9335_CPE_SS_MEM_BANK_15, 0x00 }, + { WCD9335_CPE_SS_INBOX1_TRG, 0x00 }, + { WCD9335_CPE_SS_INBOX2_TRG, 0x00 }, + { WCD9335_CPE_SS_INBOX1_0, 0x00 }, + { WCD9335_CPE_SS_INBOX1_1, 0x00 }, + { WCD9335_CPE_SS_INBOX1_2, 0x00 }, + { WCD9335_CPE_SS_INBOX1_3, 0x00 }, + { WCD9335_CPE_SS_INBOX1_4, 0x00 }, + { WCD9335_CPE_SS_INBOX1_5, 0x00 }, + { WCD9335_CPE_SS_INBOX1_6, 0x00 }, + { WCD9335_CPE_SS_INBOX1_7, 0x00 }, + { WCD9335_CPE_SS_INBOX1_8, 0x00 }, + { WCD9335_CPE_SS_INBOX1_9, 0x00 }, + { WCD9335_CPE_SS_INBOX1_10, 0x00 }, + { WCD9335_CPE_SS_INBOX1_11, 0x00 }, + { WCD9335_CPE_SS_INBOX1_12, 0x00 }, + { WCD9335_CPE_SS_INBOX1_13, 0x00 }, + { WCD9335_CPE_SS_INBOX1_14, 0x00 }, + { WCD9335_CPE_SS_INBOX1_15, 0x00 }, + { WCD9335_CPE_SS_OUTBOX1_0, 0x00 }, + { WCD9335_CPE_SS_OUTBOX1_1, 0x00 }, + { WCD9335_CPE_SS_OUTBOX1_2, 0x00 }, + { WCD9335_CPE_SS_OUTBOX1_3, 0x00 }, + { WCD9335_CPE_SS_OUTBOX1_4, 0x00 }, + { WCD9335_CPE_SS_OUTBOX1_5, 0x00 }, + { WCD9335_CPE_SS_OUTBOX1_6, 0x00 }, + { WCD9335_CPE_SS_OUTBOX1_7, 0x00 }, + { WCD9335_CPE_SS_OUTBOX1_8, 0x00 }, + { WCD9335_CPE_SS_OUTBOX1_9, 0x00 }, + { WCD9335_CPE_SS_OUTBOX1_10, 0x00 }, + { WCD9335_CPE_SS_OUTBOX1_11, 0x00 }, + { WCD9335_CPE_SS_OUTBOX1_12, 0x00 }, + { WCD9335_CPE_SS_OUTBOX1_13, 0x00 }, + { WCD9335_CPE_SS_OUTBOX1_14, 0x00 }, + { WCD9335_CPE_SS_OUTBOX1_15, 0x00 }, + { WCD9335_CPE_SS_INBOX2_0, 0x00 }, + { WCD9335_CPE_SS_INBOX2_1, 0x00 }, + { WCD9335_CPE_SS_INBOX2_2, 0x00 }, + { WCD9335_CPE_SS_INBOX2_3, 0x00 }, + { WCD9335_CPE_SS_INBOX2_4, 0x00 }, + { WCD9335_CPE_SS_INBOX2_5, 0x00 }, + { WCD9335_CPE_SS_INBOX2_6, 0x00 }, + { WCD9335_CPE_SS_INBOX2_7, 0x00 }, + { WCD9335_CPE_SS_INBOX2_8, 0x00 }, + { WCD9335_CPE_SS_INBOX2_9, 0x00 }, + { WCD9335_CPE_SS_INBOX2_10, 0x00 }, + { WCD9335_CPE_SS_INBOX2_11, 0x00 }, + { WCD9335_CPE_SS_INBOX2_12, 0x00 }, + { WCD9335_CPE_SS_INBOX2_13, 0x00 }, + { WCD9335_CPE_SS_INBOX2_14, 0x00 }, + { WCD9335_CPE_SS_INBOX2_15, 0x00 }, + { WCD9335_CPE_SS_OUTBOX2_0, 0x00 }, + { WCD9335_CPE_SS_OUTBOX2_1, 0x00 }, + { WCD9335_CPE_SS_OUTBOX2_2, 0x00 }, + { WCD9335_CPE_SS_OUTBOX2_3, 0x00 }, + { WCD9335_CPE_SS_OUTBOX2_4, 0x00 }, + { WCD9335_CPE_SS_OUTBOX2_5, 0x00 }, + { WCD9335_CPE_SS_OUTBOX2_6, 0x00 }, + { WCD9335_CPE_SS_OUTBOX2_7, 0x00 }, + { WCD9335_CPE_SS_OUTBOX2_8, 0x00 }, + { WCD9335_CPE_SS_OUTBOX2_9, 0x00 }, + { WCD9335_CPE_SS_OUTBOX2_10, 0x00 }, + { WCD9335_CPE_SS_OUTBOX2_11, 0x00 }, + { WCD9335_CPE_SS_OUTBOX2_12, 0x00 }, + { WCD9335_CPE_SS_OUTBOX2_13, 0x00 }, + { WCD9335_CPE_SS_OUTBOX2_14, 0x00 }, + { WCD9335_CPE_SS_OUTBOX2_15, 0x00 }, + { WCD9335_CPE_SS_OUTBOX1_ACK, 0x00 }, + { WCD9335_CPE_SS_OUTBOX2_ACK, 0x00 }, + { WCD9335_CPE_SS_EC_BUF_INT_PERIOD, 0x3c }, + { WCD9335_CPE_SS_US_BUF_INT_PERIOD, 0x60 }, + { WCD9335_CPE_SS_CFG, 0x41 }, + { WCD9335_CPE_SS_US_EC_MUX_CFG, 0x00 }, + { WCD9335_CPE_SS_MAD_CTL, 0x00 }, + { WCD9335_CPE_SS_CPAR_CTL, 0x00 }, + { WCD9335_CPE_SS_DMIC0_CTL, 0x00 }, + { WCD9335_CPE_SS_DMIC1_CTL, 0x00 }, + { WCD9335_CPE_SS_DMIC2_CTL, 0x00 }, + { WCD9335_CPE_SS_DMIC_CFG, 0x80 }, + { WCD9335_CPE_SS_CPAR_CFG, 0x00 }, + { WCD9335_CPE_SS_WDOG_CFG, 0x01 }, + { WCD9335_CPE_SS_BACKUP_INT, 0x00 }, + { WCD9335_CPE_SS_STATUS, 0x00 }, + { WCD9335_CPE_SS_CPE_OCD_CFG, 0x00 }, + { WCD9335_CPE_SS_SS_ERROR_INT_STATUS, 0x00 }, + { WCD9335_CPE_SS_SS_ERROR_INT_CLEAR, 0x00 }, + { WCD9335_SOC_MAD_MAIN_CTL_1, 0x00 }, + { WCD9335_SOC_MAD_MAIN_CTL_2, 0x00 }, + { WCD9335_SOC_MAD_AUDIO_CTL_1, 0x00 }, + { WCD9335_SOC_MAD_AUDIO_CTL_2, 0x00 }, + { WCD9335_SOC_MAD_AUDIO_CTL_3, 0x00 }, + { WCD9335_SOC_MAD_AUDIO_CTL_4, 0x00 }, + { WCD9335_SOC_MAD_AUDIO_CTL_5, 0x00 }, + { WCD9335_SOC_MAD_AUDIO_CTL_6, 0x00 }, + { WCD9335_SOC_MAD_AUDIO_CTL_7, 0x00 }, + { WCD9335_SOC_MAD_AUDIO_CTL_8, 0x00 }, + { WCD9335_SOC_MAD_AUDIO_IIR_CTL_PTR, 0x00 }, + { WCD9335_SOC_MAD_ULTR_CTL_1, 0x00 }, + { WCD9335_SOC_MAD_ULTR_CTL_2, 0x00 }, + { WCD9335_SOC_MAD_ULTR_CTL_3, 0x00 }, + { WCD9335_SOC_MAD_ULTR_CTL_4, 0x00 }, + { WCD9335_SOC_MAD_ULTR_CTL_5, 0x00 }, + { WCD9335_SOC_MAD_ULTR_CTL_6, 0x00 }, + { WCD9335_SOC_MAD_ULTR_CTL_7, 0x00 }, + { WCD9335_SOC_MAD_BEACON_CTL_1, 0x00 }, + { WCD9335_SOC_MAD_BEACON_CTL_2, 0x00 }, + { WCD9335_SOC_MAD_BEACON_CTL_3, 0x00 }, + { WCD9335_SOC_MAD_BEACON_CTL_4, 0x00 }, + { WCD9335_SOC_MAD_BEACON_CTL_5, 0x00 }, + { WCD9335_SOC_MAD_BEACON_CTL_6, 0x00 }, + { WCD9335_SOC_MAD_BEACON_CTL_7, 0x00 }, + { WCD9335_SOC_MAD_BEACON_CTL_8, 0x00 }, + { WCD9335_SOC_MAD_BEACON_IIR_CTL_PTR, 0x00 }, + { WCD9335_SOC_MAD_BEACON_IIR_CTL_VAL, 0x00 }, + { WCD9335_SOC_MAD_INP_SEL, 0x00 }, + /* Page #6 registers */ + { WCD9335_PAGE6_PAGE_REGISTER, 0x00 }, + { WCD9335_ANA_BIAS, 0x00 }, + { WCD9335_ANA_CLK_TOP, 0x00 }, + { WCD9335_ANA_RCO, 0x30 }, + { WCD9335_ANA_BUCK_VOUT_A, 0xb4 }, + { WCD9335_ANA_BUCK_VOUT_D, 0xb4 }, + { WCD9335_ANA_BUCK_CTL, 0x00 }, + { WCD9335_ANA_BUCK_STATUS, 0xe0 }, + { WCD9335_ANA_RX_SUPPLIES, 0x00 }, + { WCD9335_ANA_HPH, 0x00 }, + { WCD9335_ANA_EAR, 0x00 }, + { WCD9335_ANA_LO_1_2, 0x00 }, + { WCD9335_ANA_LO_3_4, 0x00 }, + { WCD9335_ANA_MAD_SETUP, 0x81 }, + { WCD9335_ANA_AMIC1, 0x20 }, + { WCD9335_ANA_AMIC2, 0x00 }, + { WCD9335_ANA_AMIC3, 0x20 }, + { WCD9335_ANA_AMIC4, 0x00 }, + { WCD9335_ANA_AMIC5, 0x20 }, + { WCD9335_ANA_AMIC6, 0x00 }, + { WCD9335_ANA_MBHC_MECH, 0x39 }, + { WCD9335_ANA_MBHC_ELECT, 0x08 }, + { WCD9335_ANA_MBHC_ZDET, 0x00 }, + { WCD9335_ANA_MBHC_RESULT_1, 0x00 }, + { WCD9335_ANA_MBHC_RESULT_2, 0x00 }, + { WCD9335_ANA_MBHC_RESULT_3, 0x00 }, + { WCD9335_ANA_MBHC_BTN0, 0x00 }, + { WCD9335_ANA_MBHC_BTN1, 0x10 }, + { WCD9335_ANA_MBHC_BTN2, 0x20 }, + { WCD9335_ANA_MBHC_BTN3, 0x30 }, + { WCD9335_ANA_MBHC_BTN4, 0x40 }, + { WCD9335_ANA_MBHC_BTN5, 0x50 }, + { WCD9335_ANA_MBHC_BTN6, 0x60 }, + { WCD9335_ANA_MBHC_BTN7, 0x70 }, + { WCD9335_ANA_MICB1, 0x10 }, + { WCD9335_ANA_MICB2, 0x10 }, + { WCD9335_ANA_MICB2_RAMP, 0x00 }, + { WCD9335_ANA_MICB3, 0x10 }, + { WCD9335_ANA_MICB4, 0x10 }, + { WCD9335_ANA_VBADC, 0x00 }, + { WCD9335_BIAS_CTL, 0x28 }, + { WCD9335_CLOCK_TEST_CTL, 0x00 }, + { WCD9335_RCO_CTRL_1, 0x44 }, + { WCD9335_RCO_CTRL_2, 0x44 }, + { WCD9335_RCO_CAL, 0x00 }, + { WCD9335_RCO_CAL_1, 0x00 }, + { WCD9335_RCO_CAL_2, 0x00 }, + { WCD9335_RCO_TEST_CTRL, 0x00 }, + { WCD9335_RCO_CAL_OUT_1, 0x00 }, + { WCD9335_RCO_CAL_OUT_2, 0x00 }, + { WCD9335_RCO_CAL_OUT_3, 0x00 }, + { WCD9335_RCO_CAL_OUT_4, 0x00 }, + { WCD9335_RCO_CAL_OUT_5, 0x00 }, + { WCD9335_SIDO_SIDO_MODE_1, 0x84 }, + { WCD9335_SIDO_SIDO_MODE_2, 0xfe }, + { WCD9335_SIDO_SIDO_MODE_3, 0xf6 }, + { WCD9335_SIDO_SIDO_MODE_4, 0x56 }, + { WCD9335_SIDO_SIDO_VCL_1, 0x00 }, + { WCD9335_SIDO_SIDO_VCL_2, 0x6c }, + { WCD9335_SIDO_SIDO_VCL_3, 0x44 }, + { WCD9335_SIDO_SIDO_CCL_1, 0x57 }, + { WCD9335_SIDO_SIDO_CCL_4, 0x61 }, + { WCD9335_SIDO_SIDO_CCL_5, 0x6d }, + { WCD9335_SIDO_SIDO_CCL_6, 0x60 }, + { WCD9335_SIDO_SIDO_CCL_7, 0x6f }, + { WCD9335_SIDO_SIDO_CCL_9, 0x6e }, + { WCD9335_SIDO_SIDO_FILTER_1, 0x92 }, + { WCD9335_SIDO_SIDO_FILTER_2, 0x54 }, + { WCD9335_SIDO_SIDO_DRIVER_1, 0x77 }, + { WCD9335_SIDO_SIDO_CAL_CODE_EXT_1, 0x9c }, + { WCD9335_SIDO_SIDO_CAL_CODE_EXT_2, 0x82 }, + { WCD9335_SIDO_SIDO_CAL_CODE_OUT_1, 0x00 }, + { WCD9335_SIDO_SIDO_CAL_CODE_OUT_2, 0x00 }, + { WCD9335_SIDO_SIDO_TEST_1, 0x00 }, + { WCD9335_MBHC_CTL_1, 0x32 }, + { WCD9335_MBHC_CTL_2, 0x01 }, + { WCD9335_MBHC_PLUG_DETECT_CTL, 0x69 }, + { WCD9335_MBHC_ZDET_RAMP_CTL, 0x00 }, + { WCD9335_MBHC_TEST_CTL, 0x00 }, + { WCD9335_VBADC_SUBBLOCK_EN, 0xfe }, + { WCD9335_VBADC_IBIAS_FE, 0x54 }, + { WCD9335_VBADC_BIAS_ADC, 0x51 }, + { WCD9335_VBADC_FE_CTRL, 0x1c }, + { WCD9335_VBADC_ADC_REF, 0x20 }, + { WCD9335_VBADC_ADC_IO, 0x80 }, + { WCD9335_VBADC_ADC_SAR, 0xff }, + { WCD9335_VBADC_DEBUG, 0x00 }, + { WCD9335_VBADC_ADC_DOUTMSB, 0x00 }, + { WCD9335_VBADC_ADC_DOUTLSB, 0x00 }, + { WCD9335_LDOH_MODE, 0x2b }, + { WCD9335_LDOH_BIAS, 0x68 }, + { WCD9335_LDOH_STB_LOADS, 0x00 }, + { WCD9335_LDOH_SLOWRAMP, 0x50 }, + { WCD9335_MICB1_TEST_CTL_1, 0x1a }, + { WCD9335_MICB1_TEST_CTL_2, 0x18 }, + { WCD9335_MICB1_TEST_CTL_3, 0xa4 }, + { WCD9335_MICB2_TEST_CTL_1, 0x1a }, + { WCD9335_MICB2_TEST_CTL_2, 0x18 }, + { WCD9335_MICB2_TEST_CTL_3, 0x24 }, + { WCD9335_MICB3_TEST_CTL_1, 0x1a }, + { WCD9335_MICB3_TEST_CTL_2, 0x18 }, + { WCD9335_MICB3_TEST_CTL_3, 0xa4 }, + { WCD9335_MICB4_TEST_CTL_1, 0x1a }, + { WCD9335_MICB4_TEST_CTL_2, 0x18 }, + { WCD9335_MICB4_TEST_CTL_3, 0xa4 }, + { WCD9335_TX_COM_ADC_VCM, 0x39 }, + { WCD9335_TX_COM_BIAS_ATEST, 0xc0 }, + { WCD9335_TX_COM_ADC_INT1_IB, 0x6f }, + { WCD9335_TX_COM_ADC_INT2_IB, 0x4f }, + { WCD9335_TX_COM_TXFE_DIV_CTL, 0x2e }, + { WCD9335_TX_COM_TXFE_DIV_START, 0x00 }, + { WCD9335_TX_COM_TXFE_DIV_STOP_9P6M, 0xc7 }, + { WCD9335_TX_COM_TXFE_DIV_STOP_12P288M, 0xff }, + { WCD9335_TX_1_2_TEST_EN, 0xcc }, + { WCD9335_TX_1_2_ADC_IB, 0x09 }, + { WCD9335_TX_1_2_TEST_CTL, 0x38 }, + { WCD9335_TX_1_2_TEST_BLK_EN, 0xff }, + { WCD9335_TX_1_2_TXFE_CLKDIV, 0x00 }, + { WCD9335_TX_1_2_SAR1_ERR, 0x00 }, + { WCD9335_TX_1_2_SAR2_ERR, 0x00 }, + { WCD9335_TX_3_4_TEST_EN, 0xcc }, + { WCD9335_TX_3_4_ADC_IB, 0x09 }, + { WCD9335_TX_3_4_TEST_CTL, 0x38 }, + { WCD9335_TX_3_4_TEST_BLK_EN, 0xff }, + { WCD9335_TX_3_4_TXFE_CLKDIV, 0x00 }, + { WCD9335_TX_3_4_SAR1_ERR, 0x00 }, + { WCD9335_TX_3_4_SAR2_ERR, 0x00 }, + { WCD9335_TX_5_6_TEST_EN, 0xcc }, + { WCD9335_TX_5_6_ADC_IB, 0x09 }, + { WCD9335_TX_5_6_TEST_CTL, 0x38 }, + { WCD9335_TX_5_6_TEST_BLK_EN, 0xff }, + { WCD9335_TX_5_6_TXFE_CLKDIV, 0x00 }, + { WCD9335_TX_5_6_SAR1_ERR, 0x00 }, + { WCD9335_TX_5_6_SAR2_ERR, 0x00 }, + { WCD9335_CLASSH_MODE_1, 0x40 }, + { WCD9335_CLASSH_MODE_2, 0x3a }, + { WCD9335_CLASSH_MODE_3, 0x00 }, + { WCD9335_CLASSH_CTRL_VCL_1, 0x70 }, + { WCD9335_CLASSH_CTRL_VCL_2, 0xa2 }, + { WCD9335_CLASSH_CTRL_CCL_1, 0x51 }, + { WCD9335_CLASSH_CTRL_CCL_2, 0x80 }, + { WCD9335_CLASSH_CTRL_CCL_3, 0x80 }, + { WCD9335_CLASSH_CTRL_CCL_4, 0x51 }, + { WCD9335_CLASSH_CTRL_CCL_5, 0x00 }, + { WCD9335_CLASSH_BUCK_TMUX_A_D, 0x00 }, + { WCD9335_CLASSH_BUCK_SW_DRV_CNTL, 0x77 }, + { WCD9335_CLASSH_SPARE, 0x00 }, + { WCD9335_FLYBACK_EN, 0x4e }, + { WCD9335_FLYBACK_VNEG_CTRL_2, 0x45 }, + { WCD9335_FLYBACK_VNEG_CTRL_3, 0x74 }, + { WCD9335_FLYBACK_VNEG_CTRL_5, 0x83 }, + { WCD9335_FLYBACK_VNEG_CTRL_6, 0x98 }, + { WCD9335_FLYBACK_VNEG_CTRL_7, 0xa9 }, + { WCD9335_FLYBACK_VNEG_CTRL_8, 0x68 }, + { WCD9335_FLYBACK_VNEG_DAC_CTRL_2, 0x50 }, + { WCD9335_FLYBACK_VNEG_DAC_CTRL_3, 0xa6 }, + { WCD9335_FLYBACK_TEST_CTL, 0x00 }, + { WCD9335_RX_AUX_SW_CTL, 0x00 }, + { WCD9335_RX_PA_AUX_IN_CONN, 0x00 }, + { WCD9335_RX_TIMER_DIV, 0x74 }, + { WCD9335_RX_OCP_CTL, 0x1f }, + { WCD9335_RX_OCP_COUNT, 0x77 }, + { WCD9335_RX_BIAS_EAR_DAC, 0xa0 }, + { WCD9335_RX_BIAS_EAR_AMP, 0xaa }, + { WCD9335_RX_BIAS_HPH_LDO, 0xa9 }, + { WCD9335_RX_BIAS_HPH_RDACBUFF_CNP2, 0x8a }, + { WCD9335_RX_BIAS_HPH_RDAC_LDO, 0x88 }, + { WCD9335_RX_BIAS_HPH_CNP1, 0x86 }, + { WCD9335_RX_BIAS_DIFFLO_PA, 0x80 }, + { WCD9335_RX_BIAS_DIFFLO_REF, 0x88 }, + { WCD9335_RX_BIAS_DIFFLO_LDO, 0x88 }, + { WCD9335_RX_BIAS_SELO_DAC_PA, 0xa8 }, + { WCD9335_RX_BIAS_BUCK_RST, 0x08 }, + { WCD9335_RX_BIAS_BUCK_VREF_ERRAMP, 0x44 }, + { WCD9335_RX_BIAS_FLYB_ERRAMP, 0x40 }, + { WCD9335_RX_BIAS_FLYB_BUFF, 0xaa }, + { WCD9335_RX_BIAS_FLYB_MID_RST, 0x44 }, + { WCD9335_HPH_L_STATUS, 0x04 }, + { WCD9335_HPH_R_STATUS, 0x04 }, + { WCD9335_HPH_CNP_EN, 0x80 }, + { WCD9335_HPH_CNP_WG_CTL, 0xda }, + { WCD9335_HPH_CNP_WG_TIME, 0x15 }, + { WCD9335_HPH_OCP_CTL, 0x28 }, + { WCD9335_HPH_AUTO_CHOP, 0x12 }, + { WCD9335_HPH_CHOP_CTL, 0x83 }, + { WCD9335_HPH_PA_CTL1, 0x46 }, + { WCD9335_HPH_L_TEST, 0x00 }, + { WCD9335_HPH_L_ATEST, 0x50 }, + { WCD9335_HPH_R_TEST, 0x00 }, + { WCD9335_HPH_RDAC_CLK_CTL1, 0x99 }, + { WCD9335_HPH_RDAC_CLK_CTL2, 0x9b }, + { WCD9335_HPH_RDAC_CHOP_CLK_LP_CTL, 0x00 }, + { WCD9335_HPH_REFBUFF_UHQA_CTL, 0xa8 }, + { WCD9335_HPH_REFBUFF_LP_CTL, 0x00 }, + { WCD9335_HPH_L_DAC_CTL, 0x00 }, + { WCD9335_HPH_R_DAC_CTL, 0x00 }, + { WCD9335_EAR_EN_REG, 0x60 }, + { WCD9335_EAR_CMBUFF, 0x0d }, + { WCD9335_EAR_ICTL, 0x40 }, + { WCD9335_EAR_EN_DBG_CTL, 0x00 }, + { WCD9335_EAR_CNP, 0xe0 }, + { WCD9335_EAR_DAC_CTL_ATEST, 0x00 }, + { WCD9335_EAR_STATUS_REG, 0x04 }, + { WCD9335_EAR_OUT_SHORT, 0x00 }, + { WCD9335_DIFF_LO_MISC, 0x03 }, + { WCD9335_DIFF_LO_LO2_COMPANDER, 0x00 }, + { WCD9335_DIFF_LO_LO1_COMPANDER, 0x00 }, + { WCD9335_DIFF_LO_COMMON, 0x40 }, + { WCD9335_DIFF_LO_BYPASS_EN, 0x00 }, + { WCD9335_DIFF_LO_CNP, 0x20 }, + { WCD9335_DIFF_LO_CORE_OUT_PROG, 0x00 }, + { WCD9335_DIFF_LO_LDO_OUT_PROG, 0x00 }, + { WCD9335_DIFF_LO_COM_SWCAP_REFBUF_FREQ, 0x9b }, + { WCD9335_DIFF_LO_COM_PA_FREQ, 0xb0 }, + { WCD9335_DIFF_LO_RESERVED_REG, 0x60 }, + { WCD9335_DIFF_LO_LO1_STATUS_1, 0x00 }, + { WCD9335_DIFF_LO_LO1_STATUS_2, 0x00 }, + { WCD9335_SE_LO_COM1, 0x80 }, + { WCD9335_SE_LO_COM2, 0x04 }, + { WCD9335_SE_LO_LO3_GAIN, 0x20 }, + { WCD9335_SE_LO_LO3_CTRL, 0x04 }, + { WCD9335_SE_LO_LO4_GAIN, 0x20 }, + { WCD9335_SE_LO_LO4_CTRL, 0x04 }, + { WCD9335_SE_LO_LO3_STATUS, 0x00 }, + { WCD9335_SE_LO_LO4_STATUS, 0x00 }, + /* Page #10 registers */ + { WCD9335_PAGE10_PAGE_REGISTER, 0x00 }, + { WCD9335_CDC_ANC0_CLK_RESET_CTL, 0x00 }, + { WCD9335_CDC_ANC0_MODE_1_CTL, 0x00 }, + { WCD9335_CDC_ANC0_MODE_2_CTL, 0x00 }, + { WCD9335_CDC_ANC0_FF_SHIFT, 0x00 }, + { WCD9335_CDC_ANC0_FB_SHIFT, 0x00 }, + { WCD9335_CDC_ANC0_LPF_FF_A_CTL, 0x00 }, + { WCD9335_CDC_ANC0_LPF_FF_B_CTL, 0x00 }, + { WCD9335_CDC_ANC0_LPF_FB_CTL, 0x00 }, + { WCD9335_CDC_ANC0_SMLPF_CTL, 0x00 }, + { WCD9335_CDC_ANC0_DCFLT_SHIFT_CTL, 0x00 }, + { WCD9335_CDC_ANC0_IIR_ADAPT_CTL, 0x00 }, + { WCD9335_CDC_ANC0_IIR_COEFF_1_CTL, 0x00 }, + { WCD9335_CDC_ANC0_IIR_COEFF_2_CTL, 0x00 }, + { WCD9335_CDC_ANC0_FF_A_GAIN_CTL, 0x00 }, + { WCD9335_CDC_ANC0_FF_B_GAIN_CTL, 0x00 }, + { WCD9335_CDC_ANC0_FB_GAIN_CTL, 0x00 }, + { WCD9335_CDC_ANC1_CLK_RESET_CTL, 0x00 }, + { WCD9335_CDC_ANC1_MODE_1_CTL, 0x00 }, + { WCD9335_CDC_ANC1_MODE_2_CTL, 0x00 }, + { WCD9335_CDC_ANC1_FF_SHIFT, 0x00 }, + { WCD9335_CDC_ANC1_FB_SHIFT, 0x00 }, + { WCD9335_CDC_ANC1_LPF_FF_A_CTL, 0x00 }, + { WCD9335_CDC_ANC1_LPF_FF_B_CTL, 0x00 }, + { WCD9335_CDC_ANC1_LPF_FB_CTL, 0x00 }, + { WCD9335_CDC_ANC1_SMLPF_CTL, 0x00 }, + { WCD9335_CDC_ANC1_DCFLT_SHIFT_CTL, 0x00 }, + { WCD9335_CDC_ANC1_IIR_ADAPT_CTL, 0x00 }, + { WCD9335_CDC_ANC1_IIR_COEFF_1_CTL, 0x00 }, + { WCD9335_CDC_ANC1_IIR_COEFF_2_CTL, 0x00 }, + { WCD9335_CDC_ANC1_FF_A_GAIN_CTL, 0x00 }, + { WCD9335_CDC_ANC1_FF_B_GAIN_CTL, 0x00 }, + { WCD9335_CDC_ANC1_FB_GAIN_CTL, 0x00 }, + { WCD9335_CDC_TX0_TX_PATH_CTL, 0x04 }, + { WCD9335_CDC_TX0_TX_VOL_CTL, 0x00 }, + { WCD9335_CDC_TX0_TX_PATH_192_CTL, 0x00 }, + { WCD9335_CDC_TX0_TX_PATH_192_CFG, 0x00 }, + { WCD9335_CDC_TX0_TX_PATH_SEC0, 0x00 }, + { WCD9335_CDC_TX0_TX_PATH_SEC1, 0x00 }, + { WCD9335_CDC_TX0_TX_PATH_SEC4, 0x20 }, + { WCD9335_CDC_TX0_TX_PATH_SEC5, 0x00 }, + { WCD9335_CDC_TX0_TX_PATH_SEC6, 0x00 }, + { WCD9335_CDC_TX1_TX_PATH_CTL, 0x04 }, + { WCD9335_CDC_TX1_TX_VOL_CTL, 0x00 }, + { WCD9335_CDC_TX1_TX_PATH_192_CTL, 0x00 }, + { WCD9335_CDC_TX1_TX_PATH_192_CFG, 0x00 }, + { WCD9335_CDC_TX1_TX_PATH_SEC0, 0x00 }, + { WCD9335_CDC_TX1_TX_PATH_SEC1, 0x00 }, + { WCD9335_CDC_TX1_TX_PATH_SEC4, 0x20 }, + { WCD9335_CDC_TX1_TX_PATH_SEC5, 0x00 }, + { WCD9335_CDC_TX1_TX_PATH_SEC6, 0x00 }, + { WCD9335_CDC_TX2_TX_PATH_CTL, 0x04 }, + { WCD9335_CDC_TX2_TX_VOL_CTL, 0x00 }, + { WCD9335_CDC_TX2_TX_PATH_192_CTL, 0x00 }, + { WCD9335_CDC_TX2_TX_PATH_192_CFG, 0x00 }, + { WCD9335_CDC_TX2_TX_PATH_SEC0, 0x00 }, + { WCD9335_CDC_TX2_TX_PATH_SEC1, 0x00 }, + { WCD9335_CDC_TX2_TX_PATH_SEC4, 0x20 }, + { WCD9335_CDC_TX2_TX_PATH_SEC5, 0x00 }, + { WCD9335_CDC_TX2_TX_PATH_SEC6, 0x00 }, + { WCD9335_CDC_TX3_TX_PATH_CTL, 0x04 }, + { WCD9335_CDC_TX3_TX_VOL_CTL, 0x00 }, + { WCD9335_CDC_TX3_TX_PATH_192_CTL, 0x00 }, + { WCD9335_CDC_TX3_TX_PATH_192_CFG, 0x00 }, + { WCD9335_CDC_TX3_TX_PATH_SEC0, 0x00 }, + { WCD9335_CDC_TX3_TX_PATH_SEC1, 0x00 }, + { WCD9335_CDC_TX3_TX_PATH_SEC4, 0x20 }, + { WCD9335_CDC_TX3_TX_PATH_SEC5, 0x00 }, + { WCD9335_CDC_TX3_TX_PATH_SEC6, 0x00 }, + { WCD9335_CDC_TX4_TX_PATH_CTL, 0x04 }, + { WCD9335_CDC_TX4_TX_VOL_CTL, 0x00 }, + { WCD9335_CDC_TX4_TX_PATH_192_CTL, 0x00 }, + { WCD9335_CDC_TX4_TX_PATH_192_CFG, 0x00 }, + { WCD9335_CDC_TX4_TX_PATH_SEC0, 0x00 }, + { WCD9335_CDC_TX4_TX_PATH_SEC1, 0x00 }, + { WCD9335_CDC_TX4_TX_PATH_SEC4, 0x20 }, + { WCD9335_CDC_TX4_TX_PATH_SEC5, 0x00 }, + { WCD9335_CDC_TX4_TX_PATH_SEC6, 0x00 }, + { WCD9335_CDC_TX5_TX_PATH_CTL, 0x04 }, + { WCD9335_CDC_TX5_TX_VOL_CTL, 0x00 }, + { WCD9335_CDC_TX5_TX_PATH_192_CTL, 0x00 }, + { WCD9335_CDC_TX5_TX_PATH_192_CFG, 0x00 }, + { WCD9335_CDC_TX5_TX_PATH_SEC0, 0x00 }, + { WCD9335_CDC_TX5_TX_PATH_SEC1, 0x00 }, + { WCD9335_CDC_TX5_TX_PATH_SEC4, 0x20 }, + { WCD9335_CDC_TX5_TX_PATH_SEC5, 0x00 }, + { WCD9335_CDC_TX5_TX_PATH_SEC6, 0x00 }, + { WCD9335_CDC_TX6_TX_PATH_CTL, 0x04 }, + { WCD9335_CDC_TX6_TX_VOL_CTL, 0x00 }, + { WCD9335_CDC_TX6_TX_PATH_192_CTL, 0x00 }, + { WCD9335_CDC_TX6_TX_PATH_192_CFG, 0x00 }, + { WCD9335_CDC_TX6_TX_PATH_SEC0, 0x00 }, + { WCD9335_CDC_TX6_TX_PATH_SEC1, 0x00 }, + { WCD9335_CDC_TX6_TX_PATH_SEC4, 0x20 }, + { WCD9335_CDC_TX6_TX_PATH_SEC5, 0x00 }, + { WCD9335_CDC_TX6_TX_PATH_SEC6, 0x00 }, + { WCD9335_CDC_TX7_TX_PATH_CTL, 0x04 }, + { WCD9335_CDC_TX7_TX_VOL_CTL, 0x00 }, + { WCD9335_CDC_TX7_TX_PATH_192_CTL, 0x00 }, + { WCD9335_CDC_TX7_TX_PATH_192_CFG, 0x00 }, + { WCD9335_CDC_TX7_TX_PATH_SEC0, 0x00 }, + { WCD9335_CDC_TX7_TX_PATH_SEC1, 0x00 }, + { WCD9335_CDC_TX7_TX_PATH_SEC4, 0x20 }, + { WCD9335_CDC_TX7_TX_PATH_SEC5, 0x00 }, + { WCD9335_CDC_TX7_TX_PATH_SEC6, 0x00 }, + { WCD9335_CDC_TX8_TX_PATH_CTL, 0x04 }, + { WCD9335_CDC_TX8_TX_VOL_CTL, 0x00 }, + { WCD9335_CDC_TX8_TX_PATH_192_CTL, 0x00 }, + { WCD9335_CDC_TX8_TX_PATH_192_CFG, 0x00 }, + { WCD9335_CDC_TX8_TX_PATH_SEC0, 0x00 }, + { WCD9335_CDC_TX8_TX_PATH_SEC1, 0x00 }, + { WCD9335_CDC_TX8_TX_PATH_SEC4, 0x20 }, + { WCD9335_CDC_TX8_TX_PATH_SEC5, 0x00 }, + { WCD9335_CDC_TX8_TX_PATH_SEC6, 0x00 }, + { WCD9335_CDC_TX9_SPKR_PROT_PATH_CTL, 0x02 }, + { WCD9335_CDC_TX9_SPKR_PROT_PATH_CFG0, 0x00 }, + { WCD9335_CDC_TX10_SPKR_PROT_PATH_CTL, 0x02 }, + { WCD9335_CDC_TX10_SPKR_PROT_PATH_CFG0, 0x00 }, + { WCD9335_CDC_TX11_SPKR_PROT_PATH_CTL, 0x02 }, + { WCD9335_CDC_TX11_SPKR_PROT_PATH_CFG0, 0x00 }, + { WCD9335_CDC_TX12_SPKR_PROT_PATH_CTL, 0x02 }, + { WCD9335_CDC_TX12_SPKR_PROT_PATH_CFG0, 0x00 }, + /* Page #11 registers */ + { WCD9335_PAGE11_PAGE_REGISTER, 0x00 }, + { WCD9335_CDC_COMPANDER1_CTL0, 0x60 }, + { WCD9335_CDC_COMPANDER1_CTL1, 0xdb }, + { WCD9335_CDC_COMPANDER1_CTL2, 0xff }, + { WCD9335_CDC_COMPANDER1_CTL3, 0x35 }, + { WCD9335_CDC_COMPANDER1_CTL4, 0xff }, + { WCD9335_CDC_COMPANDER1_CTL5, 0x00 }, + { WCD9335_CDC_COMPANDER1_CTL6, 0x01 }, + { WCD9335_CDC_COMPANDER2_CTL0, 0x60 }, + { WCD9335_CDC_COMPANDER2_CTL1, 0xdb }, + { WCD9335_CDC_COMPANDER2_CTL2, 0xff }, + { WCD9335_CDC_COMPANDER2_CTL3, 0x35 }, + { WCD9335_CDC_COMPANDER2_CTL4, 0xff }, + { WCD9335_CDC_COMPANDER2_CTL5, 0x00 }, + { WCD9335_CDC_COMPANDER2_CTL6, 0x01 }, + { WCD9335_CDC_COMPANDER3_CTL0, 0x60 }, + { WCD9335_CDC_COMPANDER3_CTL1, 0xdb }, + { WCD9335_CDC_COMPANDER3_CTL2, 0xff }, + { WCD9335_CDC_COMPANDER3_CTL3, 0x35 }, + { WCD9335_CDC_COMPANDER3_CTL4, 0xff }, + { WCD9335_CDC_COMPANDER3_CTL5, 0x00 }, + { WCD9335_CDC_COMPANDER3_CTL6, 0x01 }, + { WCD9335_CDC_COMPANDER4_CTL0, 0x60 }, + { WCD9335_CDC_COMPANDER4_CTL1, 0xdb }, + { WCD9335_CDC_COMPANDER4_CTL2, 0xff }, + { WCD9335_CDC_COMPANDER4_CTL3, 0x35 }, + { WCD9335_CDC_COMPANDER4_CTL4, 0xff }, + { WCD9335_CDC_COMPANDER4_CTL5, 0x00 }, + { WCD9335_CDC_COMPANDER4_CTL6, 0x01 }, + { WCD9335_CDC_COMPANDER5_CTL0, 0x60 }, + { WCD9335_CDC_COMPANDER5_CTL1, 0xdb }, + { WCD9335_CDC_COMPANDER5_CTL2, 0xff }, + { WCD9335_CDC_COMPANDER5_CTL3, 0x35 }, + { WCD9335_CDC_COMPANDER5_CTL4, 0xff }, + { WCD9335_CDC_COMPANDER5_CTL5, 0x00 }, + { WCD9335_CDC_COMPANDER5_CTL6, 0x01 }, + { WCD9335_CDC_COMPANDER6_CTL0, 0x60 }, + { WCD9335_CDC_COMPANDER6_CTL1, 0xdb }, + { WCD9335_CDC_COMPANDER6_CTL2, 0xff }, + { WCD9335_CDC_COMPANDER6_CTL3, 0x35 }, + { WCD9335_CDC_COMPANDER6_CTL4, 0xff }, + { WCD9335_CDC_COMPANDER6_CTL5, 0x00 }, + { WCD9335_CDC_COMPANDER6_CTL6, 0x01 }, + { WCD9335_CDC_COMPANDER7_CTL0, 0x60 }, + { WCD9335_CDC_COMPANDER7_CTL1, 0xdb }, + { WCD9335_CDC_COMPANDER7_CTL2, 0xff }, + { WCD9335_CDC_COMPANDER7_CTL3, 0x35 }, + { WCD9335_CDC_COMPANDER7_CTL4, 0xff }, + { WCD9335_CDC_COMPANDER7_CTL5, 0x00 }, + { WCD9335_CDC_COMPANDER7_CTL6, 0x01 }, + { WCD9335_CDC_COMPANDER8_CTL0, 0x60 }, + { WCD9335_CDC_COMPANDER8_CTL1, 0xdb }, + { WCD9335_CDC_COMPANDER8_CTL2, 0xff }, + { WCD9335_CDC_COMPANDER8_CTL3, 0x35 }, + { WCD9335_CDC_COMPANDER8_CTL4, 0xff }, + { WCD9335_CDC_COMPANDER8_CTL5, 0x00 }, + { WCD9335_CDC_COMPANDER8_CTL6, 0x01 }, + { WCD9335_CDC_RX0_RX_PATH_CTL, 0x04 }, + { WCD9335_CDC_RX0_RX_PATH_CFG0, 0x00 }, + { WCD9335_CDC_RX0_RX_PATH_CFG2, 0x8f }, + { WCD9335_CDC_RX0_RX_VOL_CTL, 0x00 }, + { WCD9335_CDC_RX0_RX_PATH_MIX_CTL, 0x04 }, + { WCD9335_CDC_RX0_RX_VOL_MIX_CTL, 0x00 }, + { WCD9335_CDC_RX0_RX_PATH_SEC2, 0x00 }, + { WCD9335_CDC_RX0_RX_PATH_SEC3, 0x00 }, + { WCD9335_CDC_RX0_RX_PATH_SEC5, 0x00 }, + { WCD9335_CDC_RX0_RX_PATH_SEC6, 0x00 }, + { WCD9335_CDC_RX0_RX_PATH_SEC7, 0x00 }, + { WCD9335_CDC_RX0_RX_PATH_MIX_SEC1, 0x00 }, + { WCD9335_CDC_RX1_RX_PATH_CTL, 0x04 }, + { WCD9335_CDC_RX1_RX_PATH_CFG0, 0x00 }, + { WCD9335_CDC_RX1_RX_PATH_CFG2, 0x8f }, + { WCD9335_CDC_RX1_RX_VOL_CTL, 0x00 }, + { WCD9335_CDC_RX1_RX_PATH_MIX_CTL, 0x04 }, + { WCD9335_CDC_RX1_RX_VOL_MIX_CTL, 0x00 }, + { WCD9335_CDC_RX1_RX_PATH_SEC2, 0x00 }, + { WCD9335_CDC_RX1_RX_PATH_SEC3, 0x00 }, + { WCD9335_CDC_RX1_RX_PATH_SEC4, 0x00 }, + { WCD9335_CDC_RX1_RX_PATH_SEC5, 0x00 }, + { WCD9335_CDC_RX1_RX_PATH_SEC6, 0x00 }, + { WCD9335_CDC_RX1_RX_PATH_SEC7, 0x00 }, + { WCD9335_CDC_RX1_RX_PATH_MIX_SEC1, 0x00 }, + { WCD9335_CDC_RX2_RX_PATH_CTL, 0x04 }, + { WCD9335_CDC_RX2_RX_PATH_CFG0, 0x00 }, + { WCD9335_CDC_RX2_RX_PATH_CFG2, 0x8f }, + { WCD9335_CDC_RX2_RX_VOL_CTL, 0x00 }, + { WCD9335_CDC_RX2_RX_PATH_MIX_CTL, 0x04 }, + { WCD9335_CDC_RX2_RX_VOL_MIX_CTL, 0x00 }, + { WCD9335_CDC_RX2_RX_PATH_SEC2, 0x00 }, + { WCD9335_CDC_RX2_RX_PATH_SEC3, 0x00 }, + { WCD9335_CDC_RX2_RX_PATH_SEC4, 0x00 }, + { WCD9335_CDC_RX2_RX_PATH_SEC5, 0x00 }, + { WCD9335_CDC_RX2_RX_PATH_SEC6, 0x00 }, + { WCD9335_CDC_RX2_RX_PATH_SEC7, 0x00 }, + { WCD9335_CDC_RX2_RX_PATH_MIX_SEC1, 0x00 }, + { WCD9335_CDC_RX3_RX_PATH_CTL, 0x04 }, + { WCD9335_CDC_RX3_RX_PATH_CFG0, 0x00 }, + { WCD9335_CDC_RX3_RX_PATH_CFG2, 0x8f }, + { WCD9335_CDC_RX3_RX_VOL_CTL, 0x00 }, + { WCD9335_CDC_RX3_RX_PATH_MIX_CTL, 0x04 }, + { WCD9335_CDC_RX3_RX_VOL_MIX_CTL, 0x00 }, + { WCD9335_CDC_RX3_RX_PATH_SEC2, 0x00 }, + { WCD9335_CDC_RX3_RX_PATH_SEC3, 0x00 }, + { WCD9335_CDC_RX3_RX_PATH_SEC5, 0x00 }, + { WCD9335_CDC_RX3_RX_PATH_SEC6, 0x00 }, + { WCD9335_CDC_RX3_RX_PATH_SEC7, 0x00 }, + { WCD9335_CDC_RX3_RX_PATH_MIX_SEC1, 0x00 }, + { WCD9335_CDC_RX4_RX_PATH_CTL, 0x04 }, + { WCD9335_CDC_RX4_RX_PATH_CFG0, 0x00 }, + { WCD9335_CDC_RX4_RX_PATH_CFG2, 0x8f }, + { WCD9335_CDC_RX4_RX_VOL_CTL, 0x00 }, + { WCD9335_CDC_RX4_RX_PATH_MIX_CTL, 0x04 }, + { WCD9335_CDC_RX4_RX_VOL_MIX_CTL, 0x00 }, + { WCD9335_CDC_RX4_RX_PATH_SEC2, 0x00 }, + { WCD9335_CDC_RX4_RX_PATH_SEC3, 0x00 }, + { WCD9335_CDC_RX4_RX_PATH_SEC5, 0x00 }, + { WCD9335_CDC_RX4_RX_PATH_SEC6, 0x00 }, + { WCD9335_CDC_RX4_RX_PATH_SEC7, 0x00 }, + { WCD9335_CDC_RX4_RX_PATH_MIX_SEC1, 0x00 }, + { WCD9335_CDC_RX5_RX_PATH_CTL, 0x04 }, + { WCD9335_CDC_RX5_RX_PATH_CFG0, 0x00 }, + { WCD9335_CDC_RX5_RX_PATH_CFG2, 0x8f }, + { WCD9335_CDC_RX5_RX_VOL_CTL, 0x00 }, + { WCD9335_CDC_RX5_RX_PATH_MIX_CTL, 0x04 }, + { WCD9335_CDC_RX5_RX_VOL_MIX_CTL, 0x00 }, + { WCD9335_CDC_RX5_RX_PATH_SEC2, 0x00 }, + { WCD9335_CDC_RX5_RX_PATH_SEC3, 0x00 }, + { WCD9335_CDC_RX5_RX_PATH_SEC5, 0x00 }, + { WCD9335_CDC_RX5_RX_PATH_SEC6, 0x00 }, + { WCD9335_CDC_RX5_RX_PATH_SEC7, 0x00 }, + { WCD9335_CDC_RX5_RX_PATH_MIX_SEC1, 0x00 }, + { WCD9335_CDC_RX6_RX_PATH_CTL, 0x04 }, + { WCD9335_CDC_RX6_RX_PATH_CFG0, 0x00 }, + { WCD9335_CDC_RX6_RX_PATH_CFG2, 0x8f }, + { WCD9335_CDC_RX6_RX_VOL_CTL, 0x00 }, + { WCD9335_CDC_RX6_RX_PATH_MIX_CTL, 0x04 }, + { WCD9335_CDC_RX6_RX_VOL_MIX_CTL, 0x00 }, + { WCD9335_CDC_RX6_RX_PATH_SEC2, 0x00 }, + { WCD9335_CDC_RX6_RX_PATH_SEC3, 0x00 }, + { WCD9335_CDC_RX6_RX_PATH_SEC5, 0x00 }, + { WCD9335_CDC_RX6_RX_PATH_SEC6, 0x00 }, + { WCD9335_CDC_RX6_RX_PATH_SEC7, 0x00 }, + { WCD9335_CDC_RX6_RX_PATH_MIX_SEC1, 0x00 }, + { WCD9335_CDC_RX7_RX_PATH_CTL, 0x04 }, + { WCD9335_CDC_RX7_RX_PATH_CFG0, 0x00 }, + { WCD9335_CDC_RX7_RX_PATH_CFG2, 0x8f }, + { WCD9335_CDC_RX7_RX_VOL_CTL, 0x00 }, + { WCD9335_CDC_RX7_RX_PATH_MIX_CTL, 0x04 }, + { WCD9335_CDC_RX7_RX_VOL_MIX_CTL, 0x00 }, + { WCD9335_CDC_RX7_RX_PATH_SEC2, 0x00 }, + { WCD9335_CDC_RX7_RX_PATH_SEC3, 0x00 }, + { WCD9335_CDC_RX7_RX_PATH_SEC5, 0x00 }, + { WCD9335_CDC_RX7_RX_PATH_SEC6, 0x00 }, + { WCD9335_CDC_RX7_RX_PATH_SEC7, 0x00 }, + { WCD9335_CDC_RX7_RX_PATH_MIX_SEC1, 0x00 }, + { WCD9335_CDC_RX8_RX_PATH_CTL, 0x04 }, + { WCD9335_CDC_RX8_RX_PATH_CFG0, 0x00 }, + { WCD9335_CDC_RX8_RX_PATH_CFG2, 0x8f }, + { WCD9335_CDC_RX8_RX_VOL_CTL, 0x00 }, + { WCD9335_CDC_RX8_RX_PATH_MIX_CTL, 0x04 }, + { WCD9335_CDC_RX8_RX_VOL_MIX_CTL, 0x00 }, + { WCD9335_CDC_RX8_RX_PATH_SEC2, 0x00 }, + { WCD9335_CDC_RX8_RX_PATH_SEC3, 0x00 }, + { WCD9335_CDC_RX8_RX_PATH_SEC5, 0x00 }, + { WCD9335_CDC_RX8_RX_PATH_SEC6, 0x00 }, + { WCD9335_CDC_RX8_RX_PATH_SEC7, 0x00 }, + { WCD9335_CDC_RX8_RX_PATH_MIX_SEC1, 0x00 }, + /* Page #12 registers */ + { WCD9335_PAGE12_PAGE_REGISTER, 0x00 }, + { WCD9335_CDC_CLSH_CRC, 0x00 }, + { WCD9335_CDC_CLSH_DLY_CTRL, 0x03 }, + { WCD9335_CDC_CLSH_DECAY_CTRL, 0x02 }, + { WCD9335_CDC_CLSH_HPH_V_PA, 0x1c }, + { WCD9335_CDC_CLSH_EAR_V_PA, 0x39 }, + { WCD9335_CDC_CLSH_HPH_V_HD, 0x0c }, + { WCD9335_CDC_CLSH_EAR_V_HD, 0x0c }, + { WCD9335_CDC_CLSH_K1_MSB, 0x01 }, + { WCD9335_CDC_CLSH_K1_LSB, 0x00 }, + { WCD9335_CDC_CLSH_K2_MSB, 0x00 }, + { WCD9335_CDC_CLSH_K2_LSB, 0x80 }, + { WCD9335_CDC_CLSH_IDLE_CTRL, 0x00 }, + { WCD9335_CDC_CLSH_IDLE_HPH, 0x00 }, + { WCD9335_CDC_CLSH_IDLE_EAR, 0x00 }, + { WCD9335_CDC_CLSH_TEST0, 0x07 }, + { WCD9335_CDC_CLSH_TEST1, 0x00 }, + { WCD9335_CDC_CLSH_OVR_VREF, 0x00 }, + { WCD9335_CDC_BOOST0_BOOST_PATH_CTL, 0x00 }, + { WCD9335_CDC_BOOST0_BOOST_CTL, 0xb2 }, + { WCD9335_CDC_BOOST0_BOOST_CFG1, 0x00 }, + { WCD9335_CDC_BOOST0_BOOST_CFG2, 0x00 }, + { WCD9335_CDC_BOOST1_BOOST_PATH_CTL, 0x00 }, + { WCD9335_CDC_BOOST1_BOOST_CTL, 0xb2 }, + { WCD9335_CDC_BOOST1_BOOST_CFG1, 0x00 }, + { WCD9335_CDC_BOOST1_BOOST_CFG2, 0x00 }, + { WCD9335_SWR_AHB_BRIDGE_WR_DATA_0, 0x00 }, + { WCD9335_SWR_AHB_BRIDGE_WR_DATA_1, 0x00 }, + { WCD9335_SWR_AHB_BRIDGE_WR_DATA_2, 0x00 }, + { WCD9335_SWR_AHB_BRIDGE_WR_DATA_3, 0x00 }, + { WCD9335_SWR_AHB_BRIDGE_WR_ADDR_0, 0x00 }, + { WCD9335_SWR_AHB_BRIDGE_WR_ADDR_1, 0x00 }, + { WCD9335_SWR_AHB_BRIDGE_WR_ADDR_2, 0x00 }, + { WCD9335_SWR_AHB_BRIDGE_WR_ADDR_3, 0x00 }, + { WCD9335_SWR_AHB_BRIDGE_RD_ADDR_0, 0x00 }, + { WCD9335_SWR_AHB_BRIDGE_RD_ADDR_1, 0x00 }, + { WCD9335_SWR_AHB_BRIDGE_RD_ADDR_2, 0x00 }, + { WCD9335_SWR_AHB_BRIDGE_RD_ADDR_3, 0x00 }, + { WCD9335_SWR_AHB_BRIDGE_RD_DATA_0, 0x00 }, + { WCD9335_SWR_AHB_BRIDGE_RD_DATA_1, 0x00 }, + { WCD9335_SWR_AHB_BRIDGE_RD_DATA_2, 0x00 }, + { WCD9335_SWR_AHB_BRIDGE_RD_DATA_3, 0x00 }, + { WCD9335_SWR_AHB_BRIDGE_ACCESS_CFG, 0x0f }, + { WCD9335_SWR_AHB_BRIDGE_ACCESS_STATUS, 0x03 }, + { WCD9335_CDC_VBAT_VBAT_PATH_CTL, 0x00 }, + { WCD9335_CDC_VBAT_VBAT_CFG, 0x0a }, + { WCD9335_CDC_VBAT_VBAT_ADC_CAL1, 0x00 }, + { WCD9335_CDC_VBAT_VBAT_ADC_CAL2, 0x00 }, + { WCD9335_CDC_VBAT_VBAT_ADC_CAL3, 0x04 }, + { WCD9335_CDC_VBAT_VBAT_PK_EST1, 0xe0 }, + { WCD9335_CDC_VBAT_VBAT_PK_EST2, 0x01 }, + { WCD9335_CDC_VBAT_VBAT_PK_EST3, 0x40 }, + { WCD9335_CDC_VBAT_VBAT_RF_PROC1, 0x2a }, + { WCD9335_CDC_VBAT_VBAT_RF_PROC2, 0x86 }, + { WCD9335_CDC_VBAT_VBAT_TAC1, 0x70 }, + { WCD9335_CDC_VBAT_VBAT_TAC2, 0x18 }, + { WCD9335_CDC_VBAT_VBAT_TAC3, 0x18 }, + { WCD9335_CDC_VBAT_VBAT_TAC4, 0x03 }, + { WCD9335_CDC_VBAT_VBAT_GAIN_UPD1, 0x01 }, + { WCD9335_CDC_VBAT_VBAT_GAIN_UPD2, 0x00 }, + { WCD9335_CDC_VBAT_VBAT_GAIN_UPD3, 0x64 }, + { WCD9335_CDC_VBAT_VBAT_GAIN_UPD4, 0x01 }, + { WCD9335_CDC_VBAT_VBAT_DEBUG1, 0x00 }, + { WCD9335_CDC_VBAT_VBAT_GAIN_UPD_MON, 0x00 }, + { WCD9335_CDC_VBAT_VBAT_GAIN_MON_VAL, 0x00 }, + { WCD9335_CDC_SIDETONE_SRC0_ST_SRC_PATH_CTL, 0x04 }, + { WCD9335_CDC_SIDETONE_SRC0_ST_SRC_PATH_CFG1, 0x00 }, + { WCD9335_CDC_SIDETONE_SRC1_ST_SRC_PATH_CTL, 0x04 }, + { WCD9335_CDC_SIDETONE_SRC1_ST_SRC_PATH_CFG1, 0x00 }, + /* Page #13 registers */ + { WCD9335_PAGE13_PAGE_REGISTER, 0x00 }, + { WCD9335_CDC_RX_INP_MUX_RX_INT0_CFG0, 0x00 }, + { WCD9335_CDC_RX_INP_MUX_RX_INT0_CFG1, 0x00 }, + { WCD9335_CDC_RX_INP_MUX_RX_INT1_CFG0, 0x00 }, + { WCD9335_CDC_RX_INP_MUX_RX_INT1_CFG1, 0x00 }, + { WCD9335_CDC_RX_INP_MUX_RX_INT2_CFG0, 0x00 }, + { WCD9335_CDC_RX_INP_MUX_RX_INT2_CFG1, 0x00 }, + { WCD9335_CDC_RX_INP_MUX_RX_INT3_CFG0, 0x00 }, + { WCD9335_CDC_RX_INP_MUX_RX_INT3_CFG1, 0x00 }, + { WCD9335_CDC_RX_INP_MUX_RX_INT4_CFG0, 0x00 }, + { WCD9335_CDC_RX_INP_MUX_RX_INT4_CFG1, 0x00 }, + { WCD9335_CDC_RX_INP_MUX_RX_INT5_CFG0, 0x00 }, + { WCD9335_CDC_RX_INP_MUX_RX_INT5_CFG1, 0x00 }, + { WCD9335_CDC_RX_INP_MUX_RX_INT6_CFG0, 0x00 }, + { WCD9335_CDC_RX_INP_MUX_RX_INT6_CFG1, 0x00 }, + { WCD9335_CDC_RX_INP_MUX_RX_INT7_CFG0, 0x00 }, + { WCD9335_CDC_RX_INP_MUX_RX_INT7_CFG1, 0x00 }, + { WCD9335_CDC_RX_INP_MUX_RX_INT8_CFG0, 0x00 }, + { WCD9335_CDC_RX_INP_MUX_RX_INT8_CFG1, 0x00 }, + { WCD9335_CDC_RX_INP_MUX_RX_MIX_CFG0, 0x00 }, + { WCD9335_CDC_RX_INP_MUX_RX_MIX_CFG1, 0x00 }, + { WCD9335_CDC_RX_INP_MUX_RX_MIX_CFG2, 0x00 }, + { WCD9335_CDC_RX_INP_MUX_RX_MIX_CFG3, 0x00 }, + { WCD9335_CDC_RX_INP_MUX_RX_MIX_CFG4, 0x00 }, + { WCD9335_CDC_RX_INP_MUX_SIDETONE_SRC_CFG0, 0x00 }, + { WCD9335_CDC_RX_INP_MUX_SIDETONE_SRC_CFG1, 0x00 }, + { WCD9335_CDC_RX_INP_MUX_ANC_CFG0, 0x00 }, + { WCD9335_CDC_RX_INP_MUX_SPLINE_SRC_CFG0, 0x00 }, + { WCD9335_CDC_TX_INP_MUX_ADC_MUX0_CFG0, 0x00 }, + { WCD9335_CDC_TX_INP_MUX_ADC_MUX0_CFG1, 0x00 }, + { WCD9335_CDC_TX_INP_MUX_ADC_MUX1_CFG0, 0x00 }, + { WCD9335_CDC_TX_INP_MUX_ADC_MUX1_CFG1, 0x00 }, + { WCD9335_CDC_TX_INP_MUX_ADC_MUX2_CFG0, 0x00 }, + { WCD9335_CDC_TX_INP_MUX_ADC_MUX2_CFG1, 0x00 }, + { WCD9335_CDC_TX_INP_MUX_ADC_MUX3_CFG0, 0x00 }, + { WCD9335_CDC_TX_INP_MUX_ADC_MUX3_CFG1, 0x00 }, + { WCD9335_CDC_TX_INP_MUX_ADC_MUX4_CFG0, 0x00 }, + { WCD9335_CDC_TX_INP_MUX_ADC_MUX5_CFG0, 0x00 }, + { WCD9335_CDC_TX_INP_MUX_ADC_MUX6_CFG0, 0x00 }, + { WCD9335_CDC_TX_INP_MUX_ADC_MUX7_CFG0, 0x00 }, + { WCD9335_CDC_TX_INP_MUX_ADC_MUX8_CFG0, 0x00 }, + { WCD9335_CDC_TX_INP_MUX_ADC_MUX10_CFG0, 0x00 }, + { WCD9335_CDC_TX_INP_MUX_ADC_MUX11_CFG0, 0x00 }, + { WCD9335_CDC_TX_INP_MUX_ADC_MUX12_CFG0, 0x00 }, + { WCD9335_CDC_TX_INP_MUX_ADC_MUX13_CFG0, 0x00 }, + { WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG0, 0x00 }, + { WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG1, 0x00 }, + { WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG2, 0x00 }, + { WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG3, 0x00 }, + { WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG0, 0x00 }, + { WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG1, 0x00 }, + { WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG2, 0x00 }, + { WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG3, 0x00 }, + { WCD9335_CDC_IF_ROUTER_TX_MUX_CFG0, 0x00 }, + { WCD9335_CDC_IF_ROUTER_TX_MUX_CFG1, 0x00 }, + { WCD9335_CDC_IF_ROUTER_TX_MUX_CFG2, 0x00 }, + { WCD9335_CDC_IF_ROUTER_TX_MUX_CFG3, 0x00 }, + { WCD9335_CDC_CLK_RST_CTRL_MCLK_CONTROL, 0x00 }, + { WCD9335_CDC_CLK_RST_CTRL_SWR_CONTROL, 0x00 }, + { WCD9335_CDC_PROX_DETECT_PROX_CTL, 0x08 }, + { WCD9335_CDC_PROX_DETECT_PROX_POLL_PERIOD0, 0x00 }, + { WCD9335_CDC_PROX_DETECT_PROX_POLL_PERIOD1, 0x4b }, + { WCD9335_CDC_PROX_DETECT_PROX_SIG_PATTERN_LSB, 0x00 }, + { WCD9335_CDC_PROX_DETECT_PROX_SIG_PATTERN_MSB, 0x00 }, + { WCD9335_CDC_PROX_DETECT_PROX_STATUS, 0x00 }, + { WCD9335_CDC_PROX_DETECT_PROX_TEST_CTRL, 0x00 }, + { WCD9335_CDC_PROX_DETECT_PROX_TEST_BUFF_LSB, 0x00 }, + { WCD9335_CDC_PROX_DETECT_PROX_TEST_BUFF_MSB, 0x00 }, + { WCD9335_CDC_PROX_DETECT_PROX_TEST_BUFF_LSB_RD, 0x00 }, + { WCD9335_CDC_PROX_DETECT_PROX_TEST_BUFF_MSB_RD, 0x00 }, + { WCD9335_CDC_SIDETONE_IIR0_IIR_PATH_CTL, 0x00 }, + { WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B1_CTL, 0x00 }, + { WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B2_CTL, 0x00 }, + { WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B3_CTL, 0x00 }, + { WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B4_CTL, 0x00 }, + { WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B5_CTL, 0x00 }, + { WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B6_CTL, 0x00 }, + { WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B7_CTL, 0x00 }, + { WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B8_CTL, 0x00 }, + { WCD9335_CDC_SIDETONE_IIR0_IIR_CTL, 0x40 }, + { WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_TIMER_CTL, 0x00 }, + { WCD9335_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL, 0x00 }, + { WCD9335_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL, 0x00 }, + { WCD9335_CDC_SIDETONE_IIR1_IIR_PATH_CTL, 0x00 }, + { WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B1_CTL, 0x00 }, + { WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B2_CTL, 0x00 }, + { WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B3_CTL, 0x00 }, + { WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B4_CTL, 0x00 }, + { WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B5_CTL, 0x00 }, + { WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B6_CTL, 0x00 }, + { WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B7_CTL, 0x00 }, + { WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B8_CTL, 0x00 }, + { WCD9335_CDC_SIDETONE_IIR1_IIR_CTL, 0x40 }, + { WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_TIMER_CTL, 0x00 }, + { WCD9335_CDC_SIDETONE_IIR1_IIR_COEF_B1_CTL, 0x00 }, + { WCD9335_CDC_SIDETONE_IIR1_IIR_COEF_B2_CTL, 0x00 }, + { WCD9335_CDC_TOP_TOP_CFG0, 0x00 }, + { WCD9335_CDC_TOP_TOP_CFG1, 0x00 }, + { WCD9335_CDC_TOP_TOP_CFG2, 0x00 }, + { WCD9335_CDC_TOP_TOP_CFG3, 0x18 }, + { WCD9335_CDC_TOP_TOP_CFG4, 0x00 }, + { WCD9335_CDC_TOP_TOP_CFG5, 0x00 }, + { WCD9335_CDC_TOP_TOP_CFG6, 0x00 }, + { WCD9335_CDC_TOP_TOP_CFG7, 0x00 }, + { WCD9335_CDC_TOP_HPHL_COMP_WR_LSB, 0x00 }, + { WCD9335_CDC_TOP_HPHL_COMP_WR_MSB, 0x00 }, + { WCD9335_CDC_TOP_HPHL_COMP_LUT, 0x00 }, + { WCD9335_CDC_TOP_HPHL_COMP_RD_LSB, 0x00 }, + { WCD9335_CDC_TOP_HPHL_COMP_RD_MSB, 0x00 }, + { WCD9335_CDC_TOP_HPHR_COMP_WR_LSB, 0x00 }, + { WCD9335_CDC_TOP_HPHR_COMP_WR_MSB, 0x00 }, + { WCD9335_CDC_TOP_HPHR_COMP_LUT, 0x00 }, + { WCD9335_CDC_TOP_HPHR_COMP_RD_LSB, 0x00 }, + { WCD9335_CDC_TOP_HPHR_COMP_RD_MSB, 0x00 }, + { WCD9335_CDC_TOP_DIFFL_COMP_WR_LSB, 0x00 }, + { WCD9335_CDC_TOP_DIFFL_COMP_WR_MSB, 0x00 }, + { WCD9335_CDC_TOP_DIFFL_COMP_LUT, 0x00 }, + { WCD9335_CDC_TOP_DIFFL_COMP_RD_LSB, 0x00 }, + { WCD9335_CDC_TOP_DIFFL_COMP_RD_MSB, 0x00 }, + { WCD9335_CDC_TOP_DIFFR_COMP_WR_LSB, 0x00 }, + { WCD9335_CDC_TOP_DIFFR_COMP_WR_MSB, 0x00 }, + { WCD9335_CDC_TOP_DIFFR_COMP_LUT, 0x00 }, + { WCD9335_CDC_TOP_DIFFR_COMP_RD_LSB, 0x00 }, + { WCD9335_CDC_TOP_DIFFR_COMP_RD_MSB, 0x00 }, + /* Page #0x80 registers */ + { WCD9335_PAGE80_PAGE_REGISTER, 0x00 }, + { WCD9335_TLMM_BIST_MODE_PINCFG, 0x00 }, + { WCD9335_TLMM_RF_PA_ON_PINCFG, 0x00 }, + { WCD9335_TLMM_INTR1_PINCFG, 0x00 }, + { WCD9335_TLMM_INTR2_PINCFG, 0x00 }, + { WCD9335_TLMM_SWR_DATA_PINCFG, 0x00 }, + { WCD9335_TLMM_SWR_CLK_PINCFG, 0x00 }, + { WCD9335_TLMM_SLIMBUS_DATA2_PINCFG, 0x00 }, + { WCD9335_TLMM_I2C_CLK_PINCFG, 0x00 }, + { WCD9335_TLMM_I2C_DATA_PINCFG, 0x00 }, + { WCD9335_TLMM_I2S_RX_SD0_PINCFG, 0x00 }, + { WCD9335_TLMM_I2S_RX_SD1_PINCFG, 0x00 }, + { WCD9335_TLMM_I2S_RX_SCK_PINCFG, 0x00 }, + { WCD9335_TLMM_I2S_RX_WS_PINCFG, 0x00 }, + { WCD9335_TLMM_I2S_TX_SD0_PINCFG, 0x00 }, + { WCD9335_TLMM_I2S_TX_SD1_PINCFG, 0x00 }, + { WCD9335_TLMM_I2S_TX_SCK_PINCFG, 0x00 }, + { WCD9335_TLMM_I2S_TX_WS_PINCFG, 0x00 }, + { WCD9335_TLMM_DMIC1_CLK_PINCFG, 0x00 }, + { WCD9335_TLMM_DMIC1_DATA_PINCFG, 0x00 }, + { WCD9335_TLMM_DMIC2_CLK_PINCFG, 0x00 }, + { WCD9335_TLMM_DMIC2_DATA_PINCFG, 0x00 }, + { WCD9335_TLMM_DMIC3_CLK_PINCFG, 0x00 }, + { WCD9335_TLMM_DMIC3_DATA_PINCFG, 0x00 }, + { WCD9335_TLMM_JTDI_PINCFG, 0x00 }, + { WCD9335_TLMM_JTDO_PINCFG, 0x00 }, + { WCD9335_TLMM_JTMS_PINCFG, 0x00 }, + { WCD9335_TLMM_JTCK_PINCFG, 0x00 }, + { WCD9335_TLMM_JTRST_PINCFG, 0x00 }, + { WCD9335_TEST_DEBUG_PIN_CTL_OE_0, 0x00 }, + { WCD9335_TEST_DEBUG_PIN_CTL_OE_1, 0x00 }, + { WCD9335_TEST_DEBUG_PIN_CTL_OE_2, 0x00 }, + { WCD9335_TEST_DEBUG_PIN_CTL_OE_3, 0x00 }, + { WCD9335_TEST_DEBUG_PIN_CTL_DATA_0, 0x00 }, + { WCD9335_TEST_DEBUG_PIN_CTL_DATA_1, 0x00 }, + { WCD9335_TEST_DEBUG_PIN_CTL_DATA_2, 0x00 }, + { WCD9335_TEST_DEBUG_PIN_CTL_DATA_3, 0x00 }, + { WCD9335_TEST_DEBUG_PAD_DRVCTL, 0x00 }, + { WCD9335_TEST_DEBUG_PIN_STATUS, 0x00 }, + { WCD9335_TEST_DEBUG_MEM_CTRL, 0x00 }, + { WCD9335_TEST_DEBUG_DEBUG_BUS_SEL, 0x00 }, + { WCD9335_TEST_DEBUG_DEBUG_JTAG, 0x00 }, + { WCD9335_TEST_DEBUG_DEBUG_EN_1, 0x00 }, + { WCD9335_TEST_DEBUG_DEBUG_EN_2, 0x00 }, + { WCD9335_TEST_DEBUG_DEBUG_EN_3, 0x00 }, +}; + +/* + * wcd9335_regmap_register_patch: Update register defaults based on version + * @regmap: handle to wcd9xxx regmap + * @version: wcd9335 version + * + * Returns error code in case of failure or 0 for success + */ +int wcd9335_regmap_register_patch(struct regmap *regmap, int version) +{ + int rc; + + if (!regmap) { + pr_err("%s: regmap struct is NULL\n", __func__); + return -EINVAL; + } + + switch (version) { + case TASHA_VERSION_1_0: + case TASHA_VERSION_1_1: + regcache_cache_only(regmap, true); + rc = regmap_multi_reg_write(regmap, wcd9335_1_x_defaults, + ARRAY_SIZE(wcd9335_1_x_defaults)); + regcache_cache_only(regmap, false); + break; + case TASHA_VERSION_2_0: + regcache_cache_only(regmap, true); + rc = regmap_multi_reg_write(regmap, wcd9335_2_0_defaults, + ARRAY_SIZE(wcd9335_2_0_defaults)); + regcache_cache_only(regmap, false); + break; + default: + pr_err("%s: unknown version: %d\n", __func__, version); + rc = -EINVAL; + break; + } + + return rc; +} +EXPORT_SYMBOL(wcd9335_regmap_register_patch); + +static bool wcd9335_is_readable_register(struct device *dev, unsigned int reg) +{ + u8 pg_num, reg_offset; + const u8 *reg_tbl = NULL; + + /* + * Get the page number from MSB of codec register. If its 0x80, assign + * the corresponding page index PAGE_0x80. + */ + pg_num = reg >> 0x8; + if (pg_num == 0x80) + pg_num = PAGE_0X80; + else if (pg_num >= 0xE) + return false; + + reg_tbl = wcd9335_reg[pg_num]; + reg_offset = reg & 0xFF; + + if (reg_tbl) + return reg_tbl[reg_offset]; + else + return false; +} + +static bool wcd9335_is_volatile_register(struct device *dev, unsigned int reg) +{ + /* + * registers from 0x000 to 0x0FF are volatile because + * this space contains registers related to interrupt + * status, mask etc + */ + if (reg < 0x100) + return true; + + /* IIR Coeff registers are not cacheable */ + if ((reg >= WCD9335_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL) && + (reg <= WCD9335_CDC_SIDETONE_IIR1_IIR_COEF_B2_CTL)) + return true; + + if ((reg >= WCD9335_CDC_ANC0_IIR_COEFF_1_CTL) && + (reg <= WCD9335_CDC_ANC0_FB_GAIN_CTL)) + return true; + + if ((reg >= WCD9335_CDC_ANC1_IIR_COEFF_1_CTL) && + (reg <= WCD9335_CDC_ANC1_FB_GAIN_CTL)) + return true; + /* + * CPE inbox and outbox registers are volatile + * since they can be updated in the codec hardware + * to indicate CPE status + */ + if (reg >= WCD9335_CPE_SS_MEM_PTR_0 && + reg <= WCD9335_CPE_SS_OUTBOX2_ACK) + return true; + + if (reg >= WCD9335_RCO_CAL_OUT_1 && + reg <= WCD9335_RCO_CAL_OUT_5) + return true; + + switch (reg) { + case WCD9335_CPE_SS_INBOX1_TRG: + case WCD9335_CPE_SS_INBOX2_TRG: + case WCD9335_SWR_AHB_BRIDGE_WR_DATA_0: + case WCD9335_SWR_AHB_BRIDGE_WR_DATA_1: + case WCD9335_SWR_AHB_BRIDGE_WR_DATA_2: + case WCD9335_SWR_AHB_BRIDGE_WR_DATA_3: + case WCD9335_SWR_AHB_BRIDGE_WR_ADDR_0: + case WCD9335_SWR_AHB_BRIDGE_WR_ADDR_1: + case WCD9335_SWR_AHB_BRIDGE_WR_ADDR_2: + case WCD9335_SWR_AHB_BRIDGE_WR_ADDR_3: + case WCD9335_SWR_AHB_BRIDGE_RD_DATA_0: + case WCD9335_SWR_AHB_BRIDGE_RD_DATA_1: + case WCD9335_SWR_AHB_BRIDGE_RD_DATA_2: + case WCD9335_SWR_AHB_BRIDGE_RD_DATA_3: + case WCD9335_SWR_AHB_BRIDGE_RD_ADDR_0: + case WCD9335_SWR_AHB_BRIDGE_RD_ADDR_1: + case WCD9335_SWR_AHB_BRIDGE_RD_ADDR_2: + case WCD9335_SWR_AHB_BRIDGE_RD_ADDR_3: + case WCD9335_ANA_BIAS: + case WCD9335_ANA_CLK_TOP: + case WCD9335_ANA_RCO: + case WCD9335_CDC_CLK_RST_CTRL_MCLK_CONTROL: + case WCD9335_ANA_MBHC_RESULT_3: + case WCD9335_ANA_MBHC_RESULT_2: + case WCD9335_ANA_MBHC_RESULT_1: + case WCD9335_ANA_MBHC_MECH: + case WCD9335_ANA_MBHC_ELECT: + case WCD9335_ANA_MBHC_ZDET: + case WCD9335_ANA_MICB2: + case WCD9335_CPE_SS_SS_ERROR_INT_STATUS: + case WCD9335_CPE_SS_SS_ERROR_INT_MASK: + case WCD9335_CPE_SS_SS_ERROR_INT_CLEAR: + case WCD9335_CPE_SS_STATUS: + case WCD9335_CPE_SS_BACKUP_INT: + case WCD9335_CPE_SS_CFG: + case WCD9335_SOC_MAD_MAIN_CTL_1: + case WCD9335_SOC_MAD_AUDIO_CTL_3: + case WCD9335_SOC_MAD_AUDIO_CTL_4: + case WCD9335_FLYBACK_EN: + case WCD9335_ANA_RX_SUPPLIES: + case WCD9335_CDC_CLK_RST_CTRL_FS_CNT_CONTROL: + case WCD9335_SIDO_SIDO_CCL_2: + case WCD9335_SIDO_SIDO_CCL_4: + case WCD9335_DATA_HUB_NATIVE_FIFO_STATUS: + case WCD9335_MBHC_FSM_STATUS: + case WCD9335_SPLINE_SRC0_STATUS: + case WCD9335_SPLINE_SRC1_STATUS: + case WCD9335_SPLINE_SRC2_STATUS: + case WCD9335_SPLINE_SRC3_STATUS: + case WCD9335_SIDO_SIDO_TEST_2: + case WCD9335_SIDO_SIDO_CCL_8: + case WCD9335_BIAS_VBG_FINE_ADJ: + case WCD9335_VBADC_ADC_DOUTMSB: + case WCD9335_VBADC_ADC_DOUTLSB: + case WCD9335_CDC_VBAT_VBAT_GAIN_MON_VAL: + case WCD9335_ANA_BUCK_CTL: + return true; + default: + return false; + } +} + +struct regmap_config wcd9335_regmap_config = { + .reg_bits = 16, + .val_bits = 8, + .cache_type = REGCACHE_RBTREE, + .reg_defaults = wcd9335_defaults, + .num_reg_defaults = ARRAY_SIZE(wcd9335_defaults), + .max_register = WCD9335_MAX_REGISTER, + .volatile_reg = wcd9335_is_volatile_register, + .readable_reg = wcd9335_is_readable_register, + .can_multi_write = true, +}; diff --git a/audio-kernel/asoc/codecs/wcd9335-tables.c b/audio-kernel/asoc/codecs/wcd9335-tables.c new file mode 100644 index 000000000..7df2778f3 --- /dev/null +++ b/audio-kernel/asoc/codecs/wcd9335-tables.c @@ -0,0 +1,1325 @@ +/* + * 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 + * 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 +#include "wcd9335_registers.h" + +#define WCD9335_REG(reg) ((reg) & 0xFF) + +const u8 wcd9335_page0_reg_readable[WCD9335_PAGE_SIZE] = { + [WCD9335_REG(WCD9335_PAGE0_PAGE_REGISTER)] = 1, + [WCD9335_REG(WCD9335_CODEC_RPM_CLK_BYPASS)] = 1, + [WCD9335_REG(WCD9335_CODEC_RPM_CLK_GATE)] = 1, + [WCD9335_REG(WCD9335_CODEC_RPM_CLK_MCLK_CFG)] = 1, + [WCD9335_REG(WCD9335_CODEC_RPM_RST_CTL)] = 1, + [WCD9335_REG(WCD9335_CODEC_RPM_PWR_CDC_DIG_HM_CTL)] = 1, + [WCD9335_REG(WCD9335_CODEC_RPM_PWR_CPE_DEEPSLP_1)] = 1, + [WCD9335_REG(WCD9335_CODEC_RPM_PWR_CPE_DEEPSLP_2)] = 1, + [WCD9335_REG(WCD9335_CODEC_RPM_PWR_CPE_DEEPSLP_3)] = 1, + [WCD9335_REG(WCD9335_CODEC_RPM_PWR_CPE_IRAM_SHUTDOWN)] = 1, + [WCD9335_REG(WCD9335_CODEC_RPM_PWR_CPE_DRAM1_SHUTDOWN)] = 1, + [WCD9335_REG(WCD9335_CODEC_RPM_PWR_CPE_DRAM0_SHUTDOWN_1)] = 1, + [WCD9335_REG(WCD9335_CODEC_RPM_PWR_CPE_DRAM0_SHUTDOWN_2)] = 1, + [WCD9335_REG(WCD9335_CODEC_RPM_INT_MASK)] = 1, + [WCD9335_REG(WCD9335_CODEC_RPM_INT_STATUS)] = 1, + [WCD9335_REG(WCD9335_CODEC_RPM_INT_CLEAR)] = 0, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_CHIP_ID_BYTE0)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_CHIP_ID_BYTE1)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_CHIP_ID_BYTE2)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_CHIP_ID_BYTE3)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_EFUSE_CTL)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_EFUSE_TEST0)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_EFUSE_TEST1)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT0)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT1)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT2)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT3)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT4)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT5)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT6)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT7)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT8)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT9)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT10)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT11)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT12)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT13)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT14)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT15)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_EFUSE_STATUS)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_I2C_SLAVE_ID_NONNEGO)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_I2C_SLAVE_ID_1)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_I2C_SLAVE_ID_2)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_I2C_SLAVE_ID_3)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_ANA_WAIT_STATE_CTL)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_I2C_ACTIVE)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_PROC1_MON_CTL)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_PROC1_MON_STATUS)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_PROC1_MON_CNT_MSB)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_PROC1_MON_CNT_LSB)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_PROC2_MON_CTL)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_PROC2_MON_STATUS)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_PROC2_MON_CNT_MSB)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_PROC2_MON_CNT_LSB)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_PROC3_MON_CTL)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_PROC3_MON_STATUS)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_PROC3_MON_CNT_MSB)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_PROC3_MON_CNT_LSB)] = 1, + [WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_RX_I2S_CTL)] = 1, + [WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_TX_I2S_CTL)] = 1, + [WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_I2S_CLK)] = 1, + [WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_RX0_INP_CFG)] = 1, + [WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_RX1_INP_CFG)] = 1, + [WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_RX2_INP_CFG)] = 1, + [WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_RX3_INP_CFG)] = 1, + [WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_RX4_INP_CFG)] = 1, + [WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_RX5_INP_CFG)] = 1, + [WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_RX6_INP_CFG)] = 1, + [WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_RX7_INP_CFG)] = 1, + [WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_SB_TX0_INP_CFG)] = 1, + [WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_SB_TX1_INP_CFG)] = 1, + [WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_SB_TX2_INP_CFG)] = 1, + [WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_SB_TX3_INP_CFG)] = 1, + [WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_SB_TX4_INP_CFG)] = 1, + [WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_SB_TX5_INP_CFG)] = 1, + [WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_SB_TX6_INP_CFG)] = 1, + [WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_SB_TX7_INP_CFG)] = 1, + [WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_SB_TX8_INP_CFG)] = 1, + [WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_SB_TX9_INP_CFG)] = 1, + [WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_SB_TX10_INP_CFG)] = 1, + [WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_SB_TX11_INP_CFG)] = 1, + [WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_SB_TX13_INP_CFG)] = 1, + [WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_SB_TX14_INP_CFG)] = 1, + [WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_SB_TX15_INP_CFG)] = 1, + [WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_TX_I2S_SD0_L_CFG)] = 1, + [WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_TX_I2S_SD0_R_CFG)] = 1, + [WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_TX_I2S_SD1_L_CFG)] = 1, + [WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_TX_I2S_SD1_R_CFG)] = 1, + [WCD9335_REG(WCD9335_DATA_HUB_NATIVE_FIFO_SYNC)] = 1, + [WCD9335_REG(WCD9335_DATA_HUB_NATIVE_FIFO_STATUS)] = 1, + [WCD9335_REG(WCD9335_INTR_CFG)] = 1, + [WCD9335_REG(WCD9335_INTR_CLR_COMMIT)] = 0, + [WCD9335_REG(WCD9335_INTR_PIN1_MASK0)] = 1, + [WCD9335_REG(WCD9335_INTR_PIN1_MASK1)] = 1, + [WCD9335_REG(WCD9335_INTR_PIN1_MASK2)] = 1, + [WCD9335_REG(WCD9335_INTR_PIN1_MASK3)] = 1, + [WCD9335_REG(WCD9335_INTR_PIN1_STATUS0)] = 1, + [WCD9335_REG(WCD9335_INTR_PIN1_STATUS1)] = 1, + [WCD9335_REG(WCD9335_INTR_PIN1_STATUS2)] = 1, + [WCD9335_REG(WCD9335_INTR_PIN1_STATUS3)] = 1, + [WCD9335_REG(WCD9335_INTR_PIN1_CLEAR0)] = 0, + [WCD9335_REG(WCD9335_INTR_PIN1_CLEAR1)] = 0, + [WCD9335_REG(WCD9335_INTR_PIN1_CLEAR2)] = 0, + [WCD9335_REG(WCD9335_INTR_PIN1_CLEAR3)] = 0, + [WCD9335_REG(WCD9335_INTR_PIN2_MASK0)] = 1, + [WCD9335_REG(WCD9335_INTR_PIN2_MASK1)] = 1, + [WCD9335_REG(WCD9335_INTR_PIN2_MASK2)] = 1, + [WCD9335_REG(WCD9335_INTR_PIN2_MASK3)] = 1, + [WCD9335_REG(WCD9335_INTR_PIN2_STATUS0)] = 1, + [WCD9335_REG(WCD9335_INTR_PIN2_STATUS1)] = 1, + [WCD9335_REG(WCD9335_INTR_PIN2_STATUS2)] = 1, + [WCD9335_REG(WCD9335_INTR_PIN2_STATUS3)] = 1, + [WCD9335_REG(WCD9335_INTR_PIN2_CLEAR0)] = 0, + [WCD9335_REG(WCD9335_INTR_PIN2_CLEAR1)] = 0, + [WCD9335_REG(WCD9335_INTR_PIN2_CLEAR2)] = 0, + [WCD9335_REG(WCD9335_INTR_PIN2_CLEAR3)] = 0, + [WCD9335_REG(WCD9335_INTR_LEVEL0)] = 1, + [WCD9335_REG(WCD9335_INTR_LEVEL1)] = 1, + [WCD9335_REG(WCD9335_INTR_LEVEL2)] = 1, + [WCD9335_REG(WCD9335_INTR_LEVEL3)] = 1, + [WCD9335_REG(WCD9335_INTR_BYPASS0)] = 1, + [WCD9335_REG(WCD9335_INTR_BYPASS1)] = 1, + [WCD9335_REG(WCD9335_INTR_BYPASS2)] = 1, + [WCD9335_REG(WCD9335_INTR_BYPASS3)] = 1, + [WCD9335_REG(WCD9335_INTR_SET0)] = 1, + [WCD9335_REG(WCD9335_INTR_SET1)] = 1, + [WCD9335_REG(WCD9335_INTR_SET2)] = 1, + [WCD9335_REG(WCD9335_INTR_SET3)] = 1, +}; + +const u8 wcd9335_page1_reg_readable[WCD9335_PAGE_SIZE] = { + [WCD9335_REG(WCD9335_PAGE1_PAGE_REGISTER)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_USER_CTL_0)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_USER_CTL_1)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_USER_CTL_2)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_USER_CTL_3)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_USER_CTL_4)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_USER_CTL_5)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_USER_CTL_6)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_USER_CTL_7)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_USER_CTL_8)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_USER_CTL_9)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_L_VAL_CTL_0)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_L_VAL_CTL_1)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_DSM_FRAC_CTL_0)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_DSM_FRAC_CTL_1)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_CONFIG_CTL_0)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_CONFIG_CTL_1)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_CONFIG_CTL_2)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_CONFIG_CTL_3)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_CONFIG_CTL_4)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_TEST_CTL_0)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_TEST_CTL_1)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_TEST_CTL_2)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_TEST_CTL_3)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_TEST_CTL_4)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_TEST_CTL_5)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_TEST_CTL_6)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_TEST_CTL_7)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_FREQ_CTL_0)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_FREQ_CTL_1)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_FREQ_CTL_2)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_FREQ_CTL_3)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_SSC_CTL_0)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_SSC_CTL_1)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_SSC_CTL_2)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_SSC_CTL_3)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_FLL_MODE)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_STATUS_0)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_STATUS_1)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_STATUS_2)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_STATUS_3)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_USER_CTL_0)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_USER_CTL_1)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_USER_CTL_2)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_USER_CTL_3)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_USER_CTL_4)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_USER_CTL_5)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_USER_CTL_6)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_USER_CTL_7)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_USER_CTL_8)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_USER_CTL_9)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_L_VAL_CTL_0)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_L_VAL_CTL_1)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_DSM_FRAC_CTL_0)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_DSM_FRAC_CTL_1)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_CONFIG_CTL_0)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_CONFIG_CTL_1)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_CONFIG_CTL_2)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_CONFIG_CTL_3)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_CONFIG_CTL_4)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_TEST_CTL_0)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_TEST_CTL_1)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_TEST_CTL_2)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_TEST_CTL_3)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_TEST_CTL_4)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_TEST_CTL_5)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_TEST_CTL_6)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_TEST_CTL_7)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_FREQ_CTL_0)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_FREQ_CTL_1)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_FREQ_CTL_2)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_FREQ_CTL_3)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_SSC_CTL_0)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_SSC_CTL_1)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_SSC_CTL_2)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_SSC_CTL_3)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_FLL_MODE)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_STATUS_0)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_STATUS_1)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_STATUS_2)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_STATUS_3)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_USER_CTL_0)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_USER_CTL_1)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_USER_CTL_2)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_USER_CTL_3)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_USER_CTL_4)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_USER_CTL_5)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_USER_CTL_6)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_USER_CTL_7)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_USER_CTL_8)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_USER_CTL_9)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_L_VAL_CTL_0)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_L_VAL_CTL_1)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_DSM_FRAC_CTL_0)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_DSM_FRAC_CTL_1)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_CONFIG_CTL_0)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_CONFIG_CTL_1)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_CONFIG_CTL_2)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_CONFIG_CTL_3)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_CONFIG_CTL_4)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_TEST_CTL_0)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_TEST_CTL_1)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_TEST_CTL_2)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_TEST_CTL_3)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_TEST_CTL_4)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_TEST_CTL_5)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_TEST_CTL_6)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_TEST_CTL_7)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_FREQ_CTL_0)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_FREQ_CTL_1)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_FREQ_CTL_2)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_FREQ_CTL_3)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_SSC_CTL_0)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_SSC_CTL_1)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_SSC_CTL_2)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_SSC_CTL_3)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_FLL_MODE)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_STATUS_0)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_STATUS_1)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_STATUS_2)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_STATUS_3)] = 1, +}; + +const u8 wcd9335_page2_reg_readable[WCD9335_PAGE_SIZE] = { + [WCD9335_REG(WCD9335_PAGE2_PAGE_REGISTER)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_MEM_PTR_0)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_MEM_PTR_1)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_MEM_PTR_2)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_MEM_CTRL)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_MEM_BANK_0)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_MEM_BANK_1)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_MEM_BANK_2)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_MEM_BANK_3)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_MEM_BANK_4)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_MEM_BANK_5)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_MEM_BANK_6)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_MEM_BANK_7)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_MEM_BANK_8)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_MEM_BANK_9)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_MEM_BANK_10)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_MEM_BANK_11)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_MEM_BANK_12)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_MEM_BANK_13)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_MEM_BANK_14)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_MEM_BANK_15)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_INBOX1_TRG)] = 0, + [WCD9335_REG(WCD9335_CPE_SS_INBOX2_TRG)] = 0, + [WCD9335_REG(WCD9335_CPE_SS_INBOX1_0)] = 0, + [WCD9335_REG(WCD9335_CPE_SS_INBOX1_1)] = 0, + [WCD9335_REG(WCD9335_CPE_SS_INBOX1_2)] = 0, + [WCD9335_REG(WCD9335_CPE_SS_INBOX1_3)] = 0, + [WCD9335_REG(WCD9335_CPE_SS_INBOX1_4)] = 0, + [WCD9335_REG(WCD9335_CPE_SS_INBOX1_5)] = 0, + [WCD9335_REG(WCD9335_CPE_SS_INBOX1_6)] = 0, + [WCD9335_REG(WCD9335_CPE_SS_INBOX1_7)] = 0, + [WCD9335_REG(WCD9335_CPE_SS_INBOX1_8)] = 0, + [WCD9335_REG(WCD9335_CPE_SS_INBOX1_9)] = 0, + [WCD9335_REG(WCD9335_CPE_SS_INBOX1_10)] = 0, + [WCD9335_REG(WCD9335_CPE_SS_INBOX1_11)] = 0, + [WCD9335_REG(WCD9335_CPE_SS_INBOX1_12)] = 0, + [WCD9335_REG(WCD9335_CPE_SS_INBOX1_13)] = 0, + [WCD9335_REG(WCD9335_CPE_SS_INBOX1_14)] = 0, + [WCD9335_REG(WCD9335_CPE_SS_INBOX1_15)] = 0, + [WCD9335_REG(WCD9335_CPE_SS_OUTBOX1_0)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_OUTBOX1_1)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_OUTBOX1_2)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_OUTBOX1_3)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_OUTBOX1_4)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_OUTBOX1_5)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_OUTBOX1_6)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_OUTBOX1_7)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_OUTBOX1_8)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_OUTBOX1_9)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_OUTBOX1_10)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_OUTBOX1_11)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_OUTBOX1_12)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_OUTBOX1_13)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_OUTBOX1_14)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_OUTBOX1_15)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_INBOX2_0)] = 0, + [WCD9335_REG(WCD9335_CPE_SS_INBOX2_1)] = 0, + [WCD9335_REG(WCD9335_CPE_SS_INBOX2_2)] = 0, + [WCD9335_REG(WCD9335_CPE_SS_INBOX2_3)] = 0, + [WCD9335_REG(WCD9335_CPE_SS_INBOX2_4)] = 0, + [WCD9335_REG(WCD9335_CPE_SS_INBOX2_5)] = 0, + [WCD9335_REG(WCD9335_CPE_SS_INBOX2_6)] = 0, + [WCD9335_REG(WCD9335_CPE_SS_INBOX2_7)] = 0, + [WCD9335_REG(WCD9335_CPE_SS_INBOX2_8)] = 0, + [WCD9335_REG(WCD9335_CPE_SS_INBOX2_9)] = 0, + [WCD9335_REG(WCD9335_CPE_SS_INBOX2_10)] = 0, + [WCD9335_REG(WCD9335_CPE_SS_INBOX2_11)] = 0, + [WCD9335_REG(WCD9335_CPE_SS_INBOX2_12)] = 0, + [WCD9335_REG(WCD9335_CPE_SS_INBOX2_13)] = 0, + [WCD9335_REG(WCD9335_CPE_SS_INBOX2_14)] = 0, + [WCD9335_REG(WCD9335_CPE_SS_INBOX2_15)] = 0, + [WCD9335_REG(WCD9335_CPE_SS_OUTBOX2_0)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_OUTBOX2_1)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_OUTBOX2_2)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_OUTBOX2_3)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_OUTBOX2_4)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_OUTBOX2_5)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_OUTBOX2_6)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_OUTBOX2_7)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_OUTBOX2_8)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_OUTBOX2_9)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_OUTBOX2_10)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_OUTBOX2_11)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_OUTBOX2_12)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_OUTBOX2_13)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_OUTBOX2_14)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_OUTBOX2_15)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_OUTBOX1_ACK)] = 0, + [WCD9335_REG(WCD9335_CPE_SS_OUTBOX2_ACK)] = 0, + [WCD9335_REG(WCD9335_CPE_SS_EC_BUF_INT_PERIOD)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_US_BUF_INT_PERIOD)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_CPARMAD_BUFRDY_INT_PERIOD)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_CFG)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_US_EC_MUX_CFG)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_MAD_CTL)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_CPAR_CTL)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_TX_PP_BUF_INT_PERIOD)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_TX_PP_CFG)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_DMIC0_CTL)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_DMIC1_CTL)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_DMIC2_CTL)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_DMIC_CFG)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_SVA_CFG)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_CPAR_CFG)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_WDOG_CFG)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_BACKUP_INT)] = 0, + [WCD9335_REG(WCD9335_CPE_SS_STATUS)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_CPE_OCD_CFG)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_SS_ERROR_INT_MASK)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_SS_ERROR_INT_STATUS)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_SS_ERROR_INT_CLEAR)] = 0, + [WCD9335_REG(WCD9335_SOC_MAD_MAIN_CTL_1)] = 1, + [WCD9335_REG(WCD9335_SOC_MAD_MAIN_CTL_2)] = 1, + [WCD9335_REG(WCD9335_SOC_MAD_AUDIO_CTL_1)] = 1, + [WCD9335_REG(WCD9335_SOC_MAD_AUDIO_CTL_2)] = 1, + [WCD9335_REG(WCD9335_SOC_MAD_AUDIO_CTL_3)] = 1, + [WCD9335_REG(WCD9335_SOC_MAD_AUDIO_CTL_4)] = 1, + [WCD9335_REG(WCD9335_SOC_MAD_AUDIO_CTL_5)] = 1, + [WCD9335_REG(WCD9335_SOC_MAD_AUDIO_CTL_6)] = 1, + [WCD9335_REG(WCD9335_SOC_MAD_AUDIO_CTL_7)] = 1, + [WCD9335_REG(WCD9335_SOC_MAD_AUDIO_CTL_8)] = 1, + [WCD9335_REG(WCD9335_SOC_MAD_AUDIO_IIR_CTL_PTR)] = 1, + [WCD9335_REG(WCD9335_SOC_MAD_AUDIO_IIR_CTL_VAL)] = 1, + [WCD9335_REG(WCD9335_SOC_MAD_ULTR_CTL_1)] = 1, + [WCD9335_REG(WCD9335_SOC_MAD_ULTR_CTL_2)] = 1, + [WCD9335_REG(WCD9335_SOC_MAD_ULTR_CTL_3)] = 1, + [WCD9335_REG(WCD9335_SOC_MAD_ULTR_CTL_4)] = 1, + [WCD9335_REG(WCD9335_SOC_MAD_ULTR_CTL_5)] = 1, + [WCD9335_REG(WCD9335_SOC_MAD_ULTR_CTL_6)] = 1, + [WCD9335_REG(WCD9335_SOC_MAD_ULTR_CTL_7)] = 1, + [WCD9335_REG(WCD9335_SOC_MAD_BEACON_CTL_1)] = 1, + [WCD9335_REG(WCD9335_SOC_MAD_BEACON_CTL_2)] = 1, + [WCD9335_REG(WCD9335_SOC_MAD_BEACON_CTL_3)] = 1, + [WCD9335_REG(WCD9335_SOC_MAD_BEACON_CTL_4)] = 1, + [WCD9335_REG(WCD9335_SOC_MAD_BEACON_CTL_5)] = 1, + [WCD9335_REG(WCD9335_SOC_MAD_BEACON_CTL_6)] = 1, + [WCD9335_REG(WCD9335_SOC_MAD_BEACON_CTL_7)] = 1, + [WCD9335_REG(WCD9335_SOC_MAD_BEACON_CTL_8)] = 1, + [WCD9335_REG(WCD9335_SOC_MAD_BEACON_IIR_CTL_PTR)] = 1, + [WCD9335_REG(WCD9335_SOC_MAD_BEACON_IIR_CTL_VAL)] = 1, + [WCD9335_REG(WCD9335_SOC_MAD_INP_SEL)] = 1, +}; + +const u8 wcd9335_page6_reg_readable[WCD9335_PAGE_SIZE] = { + [WCD9335_REG(WCD9335_PAGE6_PAGE_REGISTER)] = 1, + [WCD9335_REG(WCD9335_ANA_BIAS)] = 1, + [WCD9335_REG(WCD9335_ANA_CLK_TOP)] = 1, + [WCD9335_REG(WCD9335_ANA_RCO)] = 1, + [WCD9335_REG(WCD9335_ANA_BUCK_VOUT_A)] = 1, + [WCD9335_REG(WCD9335_ANA_BUCK_VOUT_D)] = 1, + [WCD9335_REG(WCD9335_ANA_BUCK_CTL)] = 1, + [WCD9335_REG(WCD9335_ANA_BUCK_STATUS)] = 1, + [WCD9335_REG(WCD9335_ANA_RX_SUPPLIES)] = 1, + [WCD9335_REG(WCD9335_ANA_HPH)] = 1, + [WCD9335_REG(WCD9335_ANA_EAR)] = 1, + [WCD9335_REG(WCD9335_ANA_LO_1_2)] = 1, + [WCD9335_REG(WCD9335_ANA_LO_3_4)] = 1, + [WCD9335_REG(WCD9335_ANA_MAD_SETUP)] = 1, + [WCD9335_REG(WCD9335_ANA_AMIC1)] = 1, + [WCD9335_REG(WCD9335_ANA_AMIC2)] = 1, + [WCD9335_REG(WCD9335_ANA_AMIC3)] = 1, + [WCD9335_REG(WCD9335_ANA_AMIC4)] = 1, + [WCD9335_REG(WCD9335_ANA_AMIC5)] = 1, + [WCD9335_REG(WCD9335_ANA_AMIC6)] = 1, + [WCD9335_REG(WCD9335_ANA_MBHC_MECH)] = 1, + [WCD9335_REG(WCD9335_ANA_MBHC_ELECT)] = 1, + [WCD9335_REG(WCD9335_ANA_MBHC_ZDET)] = 1, + [WCD9335_REG(WCD9335_ANA_MBHC_RESULT_1)] = 1, + [WCD9335_REG(WCD9335_ANA_MBHC_RESULT_2)] = 1, + [WCD9335_REG(WCD9335_ANA_MBHC_RESULT_3)] = 1, + [WCD9335_REG(WCD9335_ANA_MBHC_BTN0)] = 1, + [WCD9335_REG(WCD9335_ANA_MBHC_BTN1)] = 1, + [WCD9335_REG(WCD9335_ANA_MBHC_BTN2)] = 1, + [WCD9335_REG(WCD9335_ANA_MBHC_BTN3)] = 1, + [WCD9335_REG(WCD9335_ANA_MBHC_BTN4)] = 1, + [WCD9335_REG(WCD9335_ANA_MBHC_BTN5)] = 1, + [WCD9335_REG(WCD9335_ANA_MBHC_BTN6)] = 1, + [WCD9335_REG(WCD9335_ANA_MBHC_BTN7)] = 1, + [WCD9335_REG(WCD9335_ANA_MICB1)] = 1, + [WCD9335_REG(WCD9335_ANA_MICB2)] = 1, + [WCD9335_REG(WCD9335_ANA_MICB2_RAMP)] = 1, + [WCD9335_REG(WCD9335_ANA_MICB3)] = 1, + [WCD9335_REG(WCD9335_ANA_MICB4)] = 1, + [WCD9335_REG(WCD9335_ANA_VBADC)] = 1, + [WCD9335_REG(WCD9335_BIAS_CTL)] = 1, + [WCD9335_REG(WCD9335_BIAS_VBG_FINE_ADJ)] = 1, + [WCD9335_REG(WCD9335_CLOCK_TEST_CTL)] = 1, + [WCD9335_REG(WCD9335_RCO_CTRL_1)] = 1, + [WCD9335_REG(WCD9335_RCO_CTRL_2)] = 1, + [WCD9335_REG(WCD9335_RCO_CAL)] = 1, + [WCD9335_REG(WCD9335_RCO_CAL_1)] = 1, + [WCD9335_REG(WCD9335_RCO_CAL_2)] = 1, + [WCD9335_REG(WCD9335_RCO_TEST_CTRL)] = 1, + [WCD9335_REG(WCD9335_RCO_CAL_OUT_1)] = 1, + [WCD9335_REG(WCD9335_RCO_CAL_OUT_2)] = 1, + [WCD9335_REG(WCD9335_RCO_CAL_OUT_3)] = 1, + [WCD9335_REG(WCD9335_RCO_CAL_OUT_4)] = 1, + [WCD9335_REG(WCD9335_RCO_CAL_OUT_5)] = 1, + [WCD9335_REG(WCD9335_SIDO_SIDO_MODE_1)] = 1, + [WCD9335_REG(WCD9335_SIDO_SIDO_MODE_2)] = 1, + [WCD9335_REG(WCD9335_SIDO_SIDO_MODE_3)] = 1, + [WCD9335_REG(WCD9335_SIDO_SIDO_MODE_4)] = 1, + [WCD9335_REG(WCD9335_SIDO_SIDO_VCL_1)] = 1, + [WCD9335_REG(WCD9335_SIDO_SIDO_VCL_2)] = 1, + [WCD9335_REG(WCD9335_SIDO_SIDO_VCL_3)] = 1, + [WCD9335_REG(WCD9335_SIDO_SIDO_CCL_1)] = 1, + [WCD9335_REG(WCD9335_SIDO_SIDO_CCL_2)] = 1, + [WCD9335_REG(WCD9335_SIDO_SIDO_CCL_3)] = 1, + [WCD9335_REG(WCD9335_SIDO_SIDO_CCL_4)] = 1, + [WCD9335_REG(WCD9335_SIDO_SIDO_CCL_5)] = 1, + [WCD9335_REG(WCD9335_SIDO_SIDO_CCL_6)] = 1, + [WCD9335_REG(WCD9335_SIDO_SIDO_CCL_7)] = 1, + [WCD9335_REG(WCD9335_SIDO_SIDO_CCL_8)] = 1, + [WCD9335_REG(WCD9335_SIDO_SIDO_CCL_9)] = 1, + [WCD9335_REG(WCD9335_SIDO_SIDO_CCL_10)] = 1, + [WCD9335_REG(WCD9335_SIDO_SIDO_FILTER_1)] = 1, + [WCD9335_REG(WCD9335_SIDO_SIDO_FILTER_2)] = 1, + [WCD9335_REG(WCD9335_SIDO_SIDO_DRIVER_1)] = 1, + [WCD9335_REG(WCD9335_SIDO_SIDO_DRIVER_2)] = 1, + [WCD9335_REG(WCD9335_SIDO_SIDO_DRIVER_3)] = 1, + [WCD9335_REG(WCD9335_SIDO_SIDO_CAL_CODE_EXT_1)] = 1, + [WCD9335_REG(WCD9335_SIDO_SIDO_CAL_CODE_EXT_2)] = 1, + [WCD9335_REG(WCD9335_SIDO_SIDO_CAL_CODE_OUT_1)] = 1, + [WCD9335_REG(WCD9335_SIDO_SIDO_CAL_CODE_OUT_2)] = 1, + [WCD9335_REG(WCD9335_SIDO_SIDO_TEST_1)] = 1, + [WCD9335_REG(WCD9335_SIDO_SIDO_TEST_2)] = 1, + [WCD9335_REG(WCD9335_MBHC_CTL_1)] = 1, + [WCD9335_REG(WCD9335_MBHC_CTL_2)] = 1, + [WCD9335_REG(WCD9335_MBHC_PLUG_DETECT_CTL)] = 1, + [WCD9335_REG(WCD9335_MBHC_ZDET_ANA_CTL)] = 1, + [WCD9335_REG(WCD9335_MBHC_ZDET_RAMP_CTL)] = 1, + [WCD9335_REG(WCD9335_MBHC_FSM_DEBUG)] = 1, + [WCD9335_REG(WCD9335_MBHC_TEST_CTL)] = 1, + [WCD9335_REG(WCD9335_VBADC_SUBBLOCK_EN)] = 1, + [WCD9335_REG(WCD9335_VBADC_IBIAS_FE)] = 1, + [WCD9335_REG(WCD9335_VBADC_BIAS_ADC)] = 1, + [WCD9335_REG(WCD9335_VBADC_FE_CTRL)] = 1, + [WCD9335_REG(WCD9335_VBADC_ADC_REF)] = 1, + [WCD9335_REG(WCD9335_VBADC_ADC_IO)] = 1, + [WCD9335_REG(WCD9335_VBADC_ADC_SAR)] = 1, + [WCD9335_REG(WCD9335_VBADC_DEBUG)] = 1, + [WCD9335_REG(WCD9335_VBADC_ADC_DOUTMSB)] = 1, + [WCD9335_REG(WCD9335_VBADC_ADC_DOUTLSB)] = 1, + [WCD9335_REG(WCD9335_LDOH_MODE)] = 1, + [WCD9335_REG(WCD9335_LDOH_BIAS)] = 1, + [WCD9335_REG(WCD9335_LDOH_STB_LOADS)] = 1, + [WCD9335_REG(WCD9335_LDOH_SLOWRAMP)] = 1, + [WCD9335_REG(WCD9335_MICB1_TEST_CTL_1)] = 1, + [WCD9335_REG(WCD9335_MICB1_TEST_CTL_2)] = 1, + [WCD9335_REG(WCD9335_MICB1_TEST_CTL_3)] = 1, + [WCD9335_REG(WCD9335_MICB2_TEST_CTL_1)] = 1, + [WCD9335_REG(WCD9335_MICB2_TEST_CTL_2)] = 1, + [WCD9335_REG(WCD9335_MICB2_TEST_CTL_3)] = 1, + [WCD9335_REG(WCD9335_MICB3_TEST_CTL_1)] = 1, + [WCD9335_REG(WCD9335_MICB3_TEST_CTL_2)] = 1, + [WCD9335_REG(WCD9335_MICB3_TEST_CTL_3)] = 1, + [WCD9335_REG(WCD9335_MICB4_TEST_CTL_1)] = 1, + [WCD9335_REG(WCD9335_MICB4_TEST_CTL_2)] = 1, + [WCD9335_REG(WCD9335_MICB4_TEST_CTL_3)] = 1, + [WCD9335_REG(WCD9335_TX_COM_ADC_VCM)] = 1, + [WCD9335_REG(WCD9335_TX_COM_BIAS_ATEST)] = 1, + [WCD9335_REG(WCD9335_TX_COM_ADC_INT1_IB)] = 1, + [WCD9335_REG(WCD9335_TX_COM_ADC_INT2_IB)] = 1, + [WCD9335_REG(WCD9335_TX_COM_TXFE_DIV_CTL)] = 1, + [WCD9335_REG(WCD9335_TX_COM_TXFE_DIV_START)] = 1, + [WCD9335_REG(WCD9335_TX_COM_TXFE_DIV_STOP_9P6M)] = 1, + [WCD9335_REG(WCD9335_TX_COM_TXFE_DIV_STOP_12P288M)] = 1, + [WCD9335_REG(WCD9335_TX_1_2_TEST_EN)] = 1, + [WCD9335_REG(WCD9335_TX_1_2_ADC_IB)] = 1, + [WCD9335_REG(WCD9335_TX_1_2_ATEST_REFCTL)] = 1, + [WCD9335_REG(WCD9335_TX_1_2_TEST_CTL)] = 1, + [WCD9335_REG(WCD9335_TX_1_2_TEST_BLK_EN)] = 1, + [WCD9335_REG(WCD9335_TX_1_2_TXFE_CLKDIV)] = 1, + [WCD9335_REG(WCD9335_TX_1_2_SAR1_ERR)] = 1, + [WCD9335_REG(WCD9335_TX_1_2_SAR2_ERR)] = 1, + [WCD9335_REG(WCD9335_TX_3_4_TEST_EN)] = 1, + [WCD9335_REG(WCD9335_TX_3_4_ADC_IB)] = 1, + [WCD9335_REG(WCD9335_TX_3_4_ATEST_REFCTL)] = 1, + [WCD9335_REG(WCD9335_TX_3_4_TEST_CTL)] = 1, + [WCD9335_REG(WCD9335_TX_3_4_TEST_BLK_EN)] = 1, + [WCD9335_REG(WCD9335_TX_3_4_TXFE_CLKDIV)] = 1, + [WCD9335_REG(WCD9335_TX_3_4_SAR1_ERR)] = 1, + [WCD9335_REG(WCD9335_TX_3_4_SAR2_ERR)] = 1, + [WCD9335_REG(WCD9335_TX_5_6_TEST_EN)] = 1, + [WCD9335_REG(WCD9335_TX_5_6_ADC_IB)] = 1, + [WCD9335_REG(WCD9335_TX_5_6_ATEST_REFCTL)] = 1, + [WCD9335_REG(WCD9335_TX_5_6_TEST_CTL)] = 1, + [WCD9335_REG(WCD9335_TX_5_6_TEST_BLK_EN)] = 1, + [WCD9335_REG(WCD9335_TX_5_6_TXFE_CLKDIV)] = 1, + [WCD9335_REG(WCD9335_TX_5_6_SAR1_ERR)] = 1, + [WCD9335_REG(WCD9335_TX_5_6_SAR2_ERR)] = 1, + [WCD9335_REG(WCD9335_CLASSH_MODE_1)] = 1, + [WCD9335_REG(WCD9335_CLASSH_MODE_2)] = 1, + [WCD9335_REG(WCD9335_CLASSH_MODE_3)] = 1, + [WCD9335_REG(WCD9335_CLASSH_CTRL_VCL_1)] = 1, + [WCD9335_REG(WCD9335_CLASSH_CTRL_VCL_2)] = 1, + [WCD9335_REG(WCD9335_CLASSH_CTRL_CCL_1)] = 1, + [WCD9335_REG(WCD9335_CLASSH_CTRL_CCL_2)] = 1, + [WCD9335_REG(WCD9335_CLASSH_CTRL_CCL_3)] = 1, + [WCD9335_REG(WCD9335_CLASSH_CTRL_CCL_4)] = 1, + [WCD9335_REG(WCD9335_CLASSH_CTRL_CCL_5)] = 1, + [WCD9335_REG(WCD9335_CLASSH_BUCK_TMUX_A_D)] = 1, + [WCD9335_REG(WCD9335_CLASSH_BUCK_SW_DRV_CNTL)] = 1, + [WCD9335_REG(WCD9335_CLASSH_SPARE)] = 1, + [WCD9335_REG(WCD9335_FLYBACK_EN)] = 1, + [WCD9335_REG(WCD9335_FLYBACK_VNEG_CTRL_1)] = 1, + [WCD9335_REG(WCD9335_FLYBACK_VNEG_CTRL_2)] = 1, + [WCD9335_REG(WCD9335_FLYBACK_VNEG_CTRL_3)] = 1, + [WCD9335_REG(WCD9335_FLYBACK_VNEG_CTRL_4)] = 1, + [WCD9335_REG(WCD9335_FLYBACK_VNEG_CTRL_5)] = 1, + [WCD9335_REG(WCD9335_FLYBACK_VNEG_CTRL_6)] = 1, + [WCD9335_REG(WCD9335_FLYBACK_VNEG_CTRL_7)] = 1, + [WCD9335_REG(WCD9335_FLYBACK_VNEG_CTRL_8)] = 1, + [WCD9335_REG(WCD9335_FLYBACK_VNEG_CTRL_9)] = 1, + [WCD9335_REG(WCD9335_FLYBACK_VNEG_DAC_CTRL_1)] = 1, + [WCD9335_REG(WCD9335_FLYBACK_VNEG_DAC_CTRL_2)] = 1, + [WCD9335_REG(WCD9335_FLYBACK_VNEG_DAC_CTRL_3)] = 1, + [WCD9335_REG(WCD9335_FLYBACK_VNEG_DAC_CTRL_4)] = 1, + [WCD9335_REG(WCD9335_FLYBACK_TEST_CTL)] = 1, + [WCD9335_REG(WCD9335_RX_AUX_SW_CTL)] = 1, + [WCD9335_REG(WCD9335_RX_PA_AUX_IN_CONN)] = 1, + [WCD9335_REG(WCD9335_RX_TIMER_DIV)] = 1, + [WCD9335_REG(WCD9335_RX_OCP_CTL)] = 1, + [WCD9335_REG(WCD9335_RX_OCP_COUNT)] = 1, + [WCD9335_REG(WCD9335_RX_BIAS_EAR_DAC)] = 1, + [WCD9335_REG(WCD9335_RX_BIAS_EAR_AMP)] = 1, + [WCD9335_REG(WCD9335_RX_BIAS_HPH_LDO)] = 1, + [WCD9335_REG(WCD9335_RX_BIAS_HPH_PA)] = 1, + [WCD9335_REG(WCD9335_RX_BIAS_HPH_RDACBUFF_CNP2)] = 1, + [WCD9335_REG(WCD9335_RX_BIAS_HPH_RDAC_LDO)] = 1, + [WCD9335_REG(WCD9335_RX_BIAS_HPH_CNP1)] = 1, + [WCD9335_REG(WCD9335_RX_BIAS_HPH_LOWPOWER)] = 1, + [WCD9335_REG(WCD9335_RX_BIAS_DIFFLO_PA)] = 1, + [WCD9335_REG(WCD9335_RX_BIAS_DIFFLO_REF)] = 1, + [WCD9335_REG(WCD9335_RX_BIAS_DIFFLO_LDO)] = 1, + [WCD9335_REG(WCD9335_RX_BIAS_SELO_DAC_PA)] = 1, + [WCD9335_REG(WCD9335_RX_BIAS_BUCK_RST)] = 1, + [WCD9335_REG(WCD9335_RX_BIAS_BUCK_VREF_ERRAMP)] = 1, + [WCD9335_REG(WCD9335_RX_BIAS_FLYB_ERRAMP)] = 1, + [WCD9335_REG(WCD9335_RX_BIAS_FLYB_BUFF)] = 1, + [WCD9335_REG(WCD9335_RX_BIAS_FLYB_MID_RST)] = 1, + [WCD9335_REG(WCD9335_HPH_L_STATUS)] = 1, + [WCD9335_REG(WCD9335_HPH_R_STATUS)] = 1, + [WCD9335_REG(WCD9335_HPH_CNP_EN)] = 1, + [WCD9335_REG(WCD9335_HPH_CNP_WG_CTL)] = 1, + [WCD9335_REG(WCD9335_HPH_CNP_WG_TIME)] = 1, + [WCD9335_REG(WCD9335_HPH_OCP_CTL)] = 1, + [WCD9335_REG(WCD9335_HPH_AUTO_CHOP)] = 1, + [WCD9335_REG(WCD9335_HPH_CHOP_CTL)] = 1, + [WCD9335_REG(WCD9335_HPH_PA_CTL1)] = 1, + [WCD9335_REG(WCD9335_HPH_PA_CTL2)] = 1, + [WCD9335_REG(WCD9335_HPH_L_EN)] = 1, + [WCD9335_REG(WCD9335_HPH_L_TEST)] = 1, + [WCD9335_REG(WCD9335_HPH_L_ATEST)] = 1, + [WCD9335_REG(WCD9335_HPH_R_EN)] = 1, + [WCD9335_REG(WCD9335_HPH_R_TEST)] = 1, + [WCD9335_REG(WCD9335_HPH_R_ATEST)] = 1, + [WCD9335_REG(WCD9335_HPH_RDAC_CLK_CTL1)] = 1, + [WCD9335_REG(WCD9335_HPH_RDAC_CLK_CTL2)] = 1, + [WCD9335_REG(WCD9335_HPH_RDAC_LDO_CTL)] = 1, + [WCD9335_REG(WCD9335_HPH_RDAC_CHOP_CLK_LP_CTL)] = 1, + [WCD9335_REG(WCD9335_HPH_REFBUFF_UHQA_CTL)] = 1, + [WCD9335_REG(WCD9335_HPH_REFBUFF_LP_CTL)] = 1, + [WCD9335_REG(WCD9335_HPH_L_DAC_CTL)] = 1, + [WCD9335_REG(WCD9335_HPH_R_DAC_CTL)] = 1, + [WCD9335_REG(WCD9335_EAR_EN_REG)] = 1, + [WCD9335_REG(WCD9335_EAR_CMBUFF)] = 1, + [WCD9335_REG(WCD9335_EAR_ICTL)] = 1, + [WCD9335_REG(WCD9335_EAR_EN_DBG_CTL)] = 1, + [WCD9335_REG(WCD9335_EAR_CNP)] = 1, + [WCD9335_REG(WCD9335_EAR_DAC_CTL_ATEST)] = 1, + [WCD9335_REG(WCD9335_EAR_STATUS_REG)] = 1, + [WCD9335_REG(WCD9335_EAR_OUT_SHORT)] = 1, + [WCD9335_REG(WCD9335_DIFF_LO_MISC)] = 1, + [WCD9335_REG(WCD9335_DIFF_LO_LO2_COMPANDER)] = 1, + [WCD9335_REG(WCD9335_DIFF_LO_LO1_COMPANDER)] = 1, + [WCD9335_REG(WCD9335_DIFF_LO_COMMON)] = 1, + [WCD9335_REG(WCD9335_DIFF_LO_BYPASS_EN)] = 1, + [WCD9335_REG(WCD9335_DIFF_LO_CNP)] = 1, + [WCD9335_REG(WCD9335_DIFF_LO_CORE_OUT_PROG)] = 1, + [WCD9335_REG(WCD9335_DIFF_LO_LDO_OUT_PROG)] = 1, + [WCD9335_REG(WCD9335_DIFF_LO_COM_SWCAP_REFBUF_FREQ)] = 1, + [WCD9335_REG(WCD9335_DIFF_LO_COM_PA_FREQ)] = 1, + [WCD9335_REG(WCD9335_DIFF_LO_RESERVED_REG)] = 1, + [WCD9335_REG(WCD9335_DIFF_LO_LO1_STATUS_1)] = 1, + [WCD9335_REG(WCD9335_DIFF_LO_LO1_STATUS_2)] = 1, + [WCD9335_REG(WCD9335_SE_LO_COM1)] = 1, + [WCD9335_REG(WCD9335_SE_LO_COM2)] = 1, + [WCD9335_REG(WCD9335_SE_LO_LO3_GAIN)] = 1, + [WCD9335_REG(WCD9335_SE_LO_LO3_CTRL)] = 1, + [WCD9335_REG(WCD9335_SE_LO_LO4_GAIN)] = 1, + [WCD9335_REG(WCD9335_SE_LO_LO4_CTRL)] = 1, + [WCD9335_REG(WCD9335_SE_LO_LO3_STATUS)] = 1, + [WCD9335_REG(WCD9335_SE_LO_LO4_STATUS)] = 1, +}; + +const u8 wcd9335_page10_reg_readable[WCD9335_PAGE_SIZE] = { + [WCD9335_REG(WCD9335_PAGE10_PAGE_REGISTER)] = 1, + [WCD9335_REG(WCD9335_CDC_ANC0_CLK_RESET_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_ANC0_MODE_1_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_ANC0_MODE_2_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_ANC0_FF_SHIFT)] = 1, + [WCD9335_REG(WCD9335_CDC_ANC0_FB_SHIFT)] = 1, + [WCD9335_REG(WCD9335_CDC_ANC0_LPF_FF_A_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_ANC0_LPF_FF_B_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_ANC0_LPF_FB_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_ANC0_SMLPF_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_ANC0_DCFLT_SHIFT_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_ANC0_IIR_ADAPT_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_ANC0_IIR_COEFF_1_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_ANC0_IIR_COEFF_2_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_ANC0_FF_A_GAIN_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_ANC0_FF_B_GAIN_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_ANC0_FB_GAIN_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_ANC1_CLK_RESET_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_ANC1_MODE_1_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_ANC1_MODE_2_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_ANC1_FF_SHIFT)] = 1, + [WCD9335_REG(WCD9335_CDC_ANC1_FB_SHIFT)] = 1, + [WCD9335_REG(WCD9335_CDC_ANC1_LPF_FF_A_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_ANC1_LPF_FF_B_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_ANC1_LPF_FB_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_ANC1_SMLPF_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_ANC1_DCFLT_SHIFT_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_ANC1_IIR_ADAPT_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_ANC1_IIR_COEFF_1_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_ANC1_IIR_COEFF_2_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_ANC1_FF_A_GAIN_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_ANC1_FF_B_GAIN_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_ANC1_FB_GAIN_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_TX0_TX_PATH_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_TX0_TX_PATH_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_TX0_TX_PATH_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_TX0_TX_VOL_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_TX0_TX_PATH_192_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_TX0_TX_PATH_192_CFG)] = 1, + [WCD9335_REG(WCD9335_CDC_TX0_TX_PATH_SEC0)] = 1, + [WCD9335_REG(WCD9335_CDC_TX0_TX_PATH_SEC1)] = 1, + [WCD9335_REG(WCD9335_CDC_TX0_TX_PATH_SEC2)] = 1, + [WCD9335_REG(WCD9335_CDC_TX0_TX_PATH_SEC3)] = 1, + [WCD9335_REG(WCD9335_CDC_TX0_TX_PATH_SEC4)] = 1, + [WCD9335_REG(WCD9335_CDC_TX0_TX_PATH_SEC5)] = 1, + [WCD9335_REG(WCD9335_CDC_TX0_TX_PATH_SEC6)] = 1, + [WCD9335_REG(WCD9335_CDC_TX0_TX_PATH_SEC7)] = 1, + [WCD9335_REG(WCD9335_CDC_TX1_TX_PATH_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_TX1_TX_PATH_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_TX1_TX_PATH_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_TX1_TX_VOL_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_TX1_TX_PATH_192_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_TX1_TX_PATH_192_CFG)] = 1, + [WCD9335_REG(WCD9335_CDC_TX1_TX_PATH_SEC0)] = 1, + [WCD9335_REG(WCD9335_CDC_TX1_TX_PATH_SEC1)] = 1, + [WCD9335_REG(WCD9335_CDC_TX1_TX_PATH_SEC2)] = 1, + [WCD9335_REG(WCD9335_CDC_TX1_TX_PATH_SEC3)] = 1, + [WCD9335_REG(WCD9335_CDC_TX1_TX_PATH_SEC4)] = 1, + [WCD9335_REG(WCD9335_CDC_TX1_TX_PATH_SEC5)] = 1, + [WCD9335_REG(WCD9335_CDC_TX1_TX_PATH_SEC6)] = 1, + [WCD9335_REG(WCD9335_CDC_TX2_TX_PATH_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_TX2_TX_PATH_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_TX2_TX_PATH_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_TX2_TX_VOL_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_TX2_TX_PATH_192_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_TX2_TX_PATH_192_CFG)] = 1, + [WCD9335_REG(WCD9335_CDC_TX2_TX_PATH_SEC0)] = 1, + [WCD9335_REG(WCD9335_CDC_TX2_TX_PATH_SEC1)] = 1, + [WCD9335_REG(WCD9335_CDC_TX2_TX_PATH_SEC2)] = 1, + [WCD9335_REG(WCD9335_CDC_TX2_TX_PATH_SEC3)] = 1, + [WCD9335_REG(WCD9335_CDC_TX2_TX_PATH_SEC4)] = 1, + [WCD9335_REG(WCD9335_CDC_TX2_TX_PATH_SEC5)] = 1, + [WCD9335_REG(WCD9335_CDC_TX2_TX_PATH_SEC6)] = 1, + [WCD9335_REG(WCD9335_CDC_TX3_TX_PATH_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_TX3_TX_PATH_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_TX3_TX_PATH_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_TX3_TX_VOL_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_TX3_TX_PATH_192_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_TX3_TX_PATH_192_CFG)] = 1, + [WCD9335_REG(WCD9335_CDC_TX3_TX_PATH_SEC0)] = 1, + [WCD9335_REG(WCD9335_CDC_TX3_TX_PATH_SEC1)] = 1, + [WCD9335_REG(WCD9335_CDC_TX3_TX_PATH_SEC2)] = 1, + [WCD9335_REG(WCD9335_CDC_TX3_TX_PATH_SEC3)] = 1, + [WCD9335_REG(WCD9335_CDC_TX3_TX_PATH_SEC4)] = 1, + [WCD9335_REG(WCD9335_CDC_TX3_TX_PATH_SEC5)] = 1, + [WCD9335_REG(WCD9335_CDC_TX3_TX_PATH_SEC6)] = 1, + [WCD9335_REG(WCD9335_CDC_TX4_TX_PATH_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_TX4_TX_PATH_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_TX4_TX_PATH_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_TX4_TX_VOL_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_TX4_TX_PATH_192_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_TX4_TX_PATH_192_CFG)] = 1, + [WCD9335_REG(WCD9335_CDC_TX4_TX_PATH_SEC0)] = 1, + [WCD9335_REG(WCD9335_CDC_TX4_TX_PATH_SEC1)] = 1, + [WCD9335_REG(WCD9335_CDC_TX4_TX_PATH_SEC2)] = 1, + [WCD9335_REG(WCD9335_CDC_TX4_TX_PATH_SEC3)] = 1, + [WCD9335_REG(WCD9335_CDC_TX4_TX_PATH_SEC4)] = 1, + [WCD9335_REG(WCD9335_CDC_TX4_TX_PATH_SEC5)] = 1, + [WCD9335_REG(WCD9335_CDC_TX4_TX_PATH_SEC6)] = 1, + [WCD9335_REG(WCD9335_CDC_TX5_TX_PATH_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_TX5_TX_PATH_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_TX5_TX_PATH_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_TX5_TX_VOL_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_TX5_TX_PATH_192_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_TX5_TX_PATH_192_CFG)] = 1, + [WCD9335_REG(WCD9335_CDC_TX5_TX_PATH_SEC0)] = 1, + [WCD9335_REG(WCD9335_CDC_TX5_TX_PATH_SEC1)] = 1, + [WCD9335_REG(WCD9335_CDC_TX5_TX_PATH_SEC2)] = 1, + [WCD9335_REG(WCD9335_CDC_TX5_TX_PATH_SEC3)] = 1, + [WCD9335_REG(WCD9335_CDC_TX5_TX_PATH_SEC4)] = 1, + [WCD9335_REG(WCD9335_CDC_TX5_TX_PATH_SEC5)] = 1, + [WCD9335_REG(WCD9335_CDC_TX5_TX_PATH_SEC6)] = 1, + [WCD9335_REG(WCD9335_CDC_TX6_TX_PATH_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_TX6_TX_PATH_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_TX6_TX_PATH_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_TX6_TX_VOL_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_TX6_TX_PATH_192_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_TX6_TX_PATH_192_CFG)] = 1, + [WCD9335_REG(WCD9335_CDC_TX6_TX_PATH_SEC0)] = 1, + [WCD9335_REG(WCD9335_CDC_TX6_TX_PATH_SEC1)] = 1, + [WCD9335_REG(WCD9335_CDC_TX6_TX_PATH_SEC2)] = 1, + [WCD9335_REG(WCD9335_CDC_TX6_TX_PATH_SEC3)] = 1, + [WCD9335_REG(WCD9335_CDC_TX6_TX_PATH_SEC4)] = 1, + [WCD9335_REG(WCD9335_CDC_TX6_TX_PATH_SEC5)] = 1, + [WCD9335_REG(WCD9335_CDC_TX6_TX_PATH_SEC6)] = 1, + [WCD9335_REG(WCD9335_CDC_TX7_TX_PATH_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_TX7_TX_PATH_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_TX7_TX_PATH_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_TX7_TX_VOL_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_TX7_TX_PATH_192_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_TX7_TX_PATH_192_CFG)] = 1, + [WCD9335_REG(WCD9335_CDC_TX7_TX_PATH_SEC0)] = 1, + [WCD9335_REG(WCD9335_CDC_TX7_TX_PATH_SEC1)] = 1, + [WCD9335_REG(WCD9335_CDC_TX7_TX_PATH_SEC2)] = 1, + [WCD9335_REG(WCD9335_CDC_TX7_TX_PATH_SEC3)] = 1, + [WCD9335_REG(WCD9335_CDC_TX7_TX_PATH_SEC4)] = 1, + [WCD9335_REG(WCD9335_CDC_TX7_TX_PATH_SEC5)] = 1, + [WCD9335_REG(WCD9335_CDC_TX7_TX_PATH_SEC6)] = 1, + [WCD9335_REG(WCD9335_CDC_TX8_TX_PATH_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_TX8_TX_PATH_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_TX8_TX_PATH_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_TX8_TX_VOL_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_TX8_TX_PATH_192_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_TX8_TX_PATH_192_CFG)] = 1, + [WCD9335_REG(WCD9335_CDC_TX8_TX_PATH_SEC0)] = 1, + [WCD9335_REG(WCD9335_CDC_TX8_TX_PATH_SEC1)] = 1, + [WCD9335_REG(WCD9335_CDC_TX8_TX_PATH_SEC2)] = 1, + [WCD9335_REG(WCD9335_CDC_TX8_TX_PATH_SEC3)] = 1, + [WCD9335_REG(WCD9335_CDC_TX8_TX_PATH_SEC4)] = 1, + [WCD9335_REG(WCD9335_CDC_TX8_TX_PATH_SEC5)] = 1, + [WCD9335_REG(WCD9335_CDC_TX8_TX_PATH_SEC6)] = 1, + [WCD9335_REG(WCD9335_CDC_TX9_SPKR_PROT_PATH_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_TX9_SPKR_PROT_PATH_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_TX10_SPKR_PROT_PATH_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_TX10_SPKR_PROT_PATH_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_TX11_SPKR_PROT_PATH_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_TX11_SPKR_PROT_PATH_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_TX12_SPKR_PROT_PATH_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_TX12_SPKR_PROT_PATH_CFG0)] = 1, +}; + +const u8 wcd9335_page11_reg_readable[WCD9335_PAGE_SIZE] = { + [WCD9335_REG(WCD9335_PAGE11_PAGE_REGISTER)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER1_CTL0)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER1_CTL1)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER1_CTL2)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER1_CTL3)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER1_CTL4)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER1_CTL5)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER1_CTL6)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER1_CTL7)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER2_CTL0)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER2_CTL1)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER2_CTL2)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER2_CTL3)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER2_CTL4)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER2_CTL5)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER2_CTL6)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER2_CTL7)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER3_CTL0)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER3_CTL1)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER3_CTL2)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER3_CTL3)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER3_CTL4)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER3_CTL5)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER3_CTL6)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER3_CTL7)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER4_CTL0)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER4_CTL1)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER4_CTL2)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER4_CTL3)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER4_CTL4)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER4_CTL5)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER4_CTL6)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER4_CTL7)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER5_CTL0)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER5_CTL1)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER5_CTL2)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER5_CTL3)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER5_CTL4)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER5_CTL5)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER5_CTL6)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER5_CTL7)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER6_CTL0)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER6_CTL1)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER6_CTL2)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER6_CTL3)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER6_CTL4)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER6_CTL5)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER6_CTL6)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER6_CTL7)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER7_CTL0)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER7_CTL1)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER7_CTL2)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER7_CTL3)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER7_CTL4)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER7_CTL5)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER7_CTL6)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER7_CTL7)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER8_CTL0)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER8_CTL1)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER8_CTL2)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER8_CTL3)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER8_CTL4)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER8_CTL5)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER8_CTL6)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER8_CTL7)] = 1, + [WCD9335_REG(WCD9335_CDC_RX0_RX_PATH_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_RX0_RX_PATH_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX0_RX_PATH_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_RX0_RX_PATH_CFG2)] = 1, + [WCD9335_REG(WCD9335_CDC_RX0_RX_VOL_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_RX0_RX_PATH_MIX_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_RX0_RX_PATH_MIX_CFG)] = 1, + [WCD9335_REG(WCD9335_CDC_RX0_RX_VOL_MIX_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_RX0_RX_PATH_SEC0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX0_RX_PATH_SEC1)] = 1, + [WCD9335_REG(WCD9335_CDC_RX0_RX_PATH_SEC2)] = 1, + [WCD9335_REG(WCD9335_CDC_RX0_RX_PATH_SEC3)] = 1, + [WCD9335_REG(WCD9335_CDC_RX0_RX_PATH_SEC5)] = 1, + [WCD9335_REG(WCD9335_CDC_RX0_RX_PATH_SEC6)] = 1, + [WCD9335_REG(WCD9335_CDC_RX0_RX_PATH_SEC7)] = 1, + [WCD9335_REG(WCD9335_CDC_RX0_RX_PATH_MIX_SEC0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX0_RX_PATH_MIX_SEC1)] = 1, + [WCD9335_REG(WCD9335_CDC_RX1_RX_PATH_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_RX1_RX_PATH_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX1_RX_PATH_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_RX1_RX_PATH_CFG2)] = 1, + [WCD9335_REG(WCD9335_CDC_RX1_RX_VOL_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_RX1_RX_PATH_MIX_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_RX1_RX_PATH_MIX_CFG)] = 1, + [WCD9335_REG(WCD9335_CDC_RX1_RX_VOL_MIX_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_RX1_RX_PATH_SEC0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX1_RX_PATH_SEC1)] = 1, + [WCD9335_REG(WCD9335_CDC_RX1_RX_PATH_SEC2)] = 1, + [WCD9335_REG(WCD9335_CDC_RX1_RX_PATH_SEC3)] = 1, + [WCD9335_REG(WCD9335_CDC_RX1_RX_PATH_SEC4)] = 1, + [WCD9335_REG(WCD9335_CDC_RX1_RX_PATH_SEC5)] = 1, + [WCD9335_REG(WCD9335_CDC_RX1_RX_PATH_SEC6)] = 1, + [WCD9335_REG(WCD9335_CDC_RX1_RX_PATH_SEC7)] = 1, + [WCD9335_REG(WCD9335_CDC_RX1_RX_PATH_MIX_SEC0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX1_RX_PATH_MIX_SEC1)] = 1, + [WCD9335_REG(WCD9335_CDC_RX2_RX_PATH_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_RX2_RX_PATH_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX2_RX_PATH_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_RX2_RX_PATH_CFG2)] = 1, + [WCD9335_REG(WCD9335_CDC_RX2_RX_VOL_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_RX2_RX_PATH_MIX_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_RX2_RX_PATH_MIX_CFG)] = 1, + [WCD9335_REG(WCD9335_CDC_RX2_RX_VOL_MIX_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_RX2_RX_PATH_SEC0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX2_RX_PATH_SEC1)] = 1, + [WCD9335_REG(WCD9335_CDC_RX2_RX_PATH_SEC2)] = 1, + [WCD9335_REG(WCD9335_CDC_RX2_RX_PATH_SEC3)] = 1, + [WCD9335_REG(WCD9335_CDC_RX2_RX_PATH_SEC4)] = 1, + [WCD9335_REG(WCD9335_CDC_RX2_RX_PATH_SEC5)] = 1, + [WCD9335_REG(WCD9335_CDC_RX2_RX_PATH_SEC6)] = 1, + [WCD9335_REG(WCD9335_CDC_RX2_RX_PATH_SEC7)] = 1, + [WCD9335_REG(WCD9335_CDC_RX2_RX_PATH_MIX_SEC0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX2_RX_PATH_MIX_SEC1)] = 1, + [WCD9335_REG(WCD9335_CDC_RX3_RX_PATH_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_RX3_RX_PATH_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX3_RX_PATH_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_RX3_RX_PATH_CFG2)] = 1, + [WCD9335_REG(WCD9335_CDC_RX3_RX_VOL_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_RX3_RX_PATH_MIX_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_RX3_RX_PATH_MIX_CFG)] = 1, + [WCD9335_REG(WCD9335_CDC_RX3_RX_VOL_MIX_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_RX3_RX_PATH_SEC0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX3_RX_PATH_SEC1)] = 1, + [WCD9335_REG(WCD9335_CDC_RX3_RX_PATH_SEC2)] = 1, + [WCD9335_REG(WCD9335_CDC_RX3_RX_PATH_SEC3)] = 1, + [WCD9335_REG(WCD9335_CDC_RX3_RX_PATH_SEC5)] = 1, + [WCD9335_REG(WCD9335_CDC_RX3_RX_PATH_SEC6)] = 1, + [WCD9335_REG(WCD9335_CDC_RX3_RX_PATH_SEC7)] = 1, + [WCD9335_REG(WCD9335_CDC_RX3_RX_PATH_MIX_SEC0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX3_RX_PATH_MIX_SEC1)] = 1, + [WCD9335_REG(WCD9335_CDC_RX4_RX_PATH_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_RX4_RX_PATH_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX4_RX_PATH_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_RX4_RX_PATH_CFG2)] = 1, + [WCD9335_REG(WCD9335_CDC_RX4_RX_VOL_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_RX4_RX_PATH_MIX_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_RX4_RX_PATH_MIX_CFG)] = 1, + [WCD9335_REG(WCD9335_CDC_RX4_RX_VOL_MIX_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_RX4_RX_PATH_SEC0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX4_RX_PATH_SEC1)] = 1, + [WCD9335_REG(WCD9335_CDC_RX4_RX_PATH_SEC2)] = 1, + [WCD9335_REG(WCD9335_CDC_RX4_RX_PATH_SEC3)] = 1, + [WCD9335_REG(WCD9335_CDC_RX4_RX_PATH_SEC5)] = 1, + [WCD9335_REG(WCD9335_CDC_RX4_RX_PATH_SEC6)] = 1, + [WCD9335_REG(WCD9335_CDC_RX4_RX_PATH_SEC7)] = 1, + [WCD9335_REG(WCD9335_CDC_RX4_RX_PATH_MIX_SEC0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX4_RX_PATH_MIX_SEC1)] = 1, + [WCD9335_REG(WCD9335_CDC_RX5_RX_PATH_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_RX5_RX_PATH_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX5_RX_PATH_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_RX5_RX_PATH_CFG2)] = 1, + [WCD9335_REG(WCD9335_CDC_RX5_RX_VOL_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_RX5_RX_PATH_MIX_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_RX5_RX_PATH_MIX_CFG)] = 1, + [WCD9335_REG(WCD9335_CDC_RX5_RX_VOL_MIX_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_RX5_RX_PATH_SEC0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX5_RX_PATH_SEC1)] = 1, + [WCD9335_REG(WCD9335_CDC_RX5_RX_PATH_SEC2)] = 1, + [WCD9335_REG(WCD9335_CDC_RX5_RX_PATH_SEC3)] = 1, + [WCD9335_REG(WCD9335_CDC_RX5_RX_PATH_SEC5)] = 1, + [WCD9335_REG(WCD9335_CDC_RX5_RX_PATH_SEC6)] = 1, + [WCD9335_REG(WCD9335_CDC_RX5_RX_PATH_SEC7)] = 1, + [WCD9335_REG(WCD9335_CDC_RX5_RX_PATH_MIX_SEC0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX5_RX_PATH_MIX_SEC1)] = 1, + [WCD9335_REG(WCD9335_CDC_RX6_RX_PATH_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_RX6_RX_PATH_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX6_RX_PATH_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_RX6_RX_PATH_CFG2)] = 1, + [WCD9335_REG(WCD9335_CDC_RX6_RX_VOL_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_RX6_RX_PATH_MIX_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_RX6_RX_PATH_MIX_CFG)] = 1, + [WCD9335_REG(WCD9335_CDC_RX6_RX_VOL_MIX_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_RX6_RX_PATH_SEC0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX6_RX_PATH_SEC1)] = 1, + [WCD9335_REG(WCD9335_CDC_RX6_RX_PATH_SEC2)] = 1, + [WCD9335_REG(WCD9335_CDC_RX6_RX_PATH_SEC3)] = 1, + [WCD9335_REG(WCD9335_CDC_RX6_RX_PATH_SEC5)] = 1, + [WCD9335_REG(WCD9335_CDC_RX6_RX_PATH_SEC6)] = 1, + [WCD9335_REG(WCD9335_CDC_RX6_RX_PATH_SEC7)] = 1, + [WCD9335_REG(WCD9335_CDC_RX6_RX_PATH_MIX_SEC0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX6_RX_PATH_MIX_SEC1)] = 1, + [WCD9335_REG(WCD9335_CDC_RX7_RX_PATH_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_RX7_RX_PATH_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX7_RX_PATH_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_RX7_RX_PATH_CFG2)] = 1, + [WCD9335_REG(WCD9335_CDC_RX7_RX_VOL_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_RX7_RX_PATH_MIX_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_RX7_RX_PATH_MIX_CFG)] = 1, + [WCD9335_REG(WCD9335_CDC_RX7_RX_VOL_MIX_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_RX7_RX_PATH_SEC0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX7_RX_PATH_SEC1)] = 1, + [WCD9335_REG(WCD9335_CDC_RX7_RX_PATH_SEC2)] = 1, + [WCD9335_REG(WCD9335_CDC_RX7_RX_PATH_SEC3)] = 1, + [WCD9335_REG(WCD9335_CDC_RX7_RX_PATH_SEC5)] = 1, + [WCD9335_REG(WCD9335_CDC_RX7_RX_PATH_SEC6)] = 1, + [WCD9335_REG(WCD9335_CDC_RX7_RX_PATH_SEC7)] = 1, + [WCD9335_REG(WCD9335_CDC_RX7_RX_PATH_MIX_SEC0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX7_RX_PATH_MIX_SEC1)] = 1, + [WCD9335_REG(WCD9335_CDC_RX8_RX_PATH_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_RX8_RX_PATH_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX8_RX_PATH_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_RX8_RX_PATH_CFG2)] = 1, + [WCD9335_REG(WCD9335_CDC_RX8_RX_VOL_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_RX8_RX_PATH_MIX_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_RX8_RX_PATH_MIX_CFG)] = 1, + [WCD9335_REG(WCD9335_CDC_RX8_RX_VOL_MIX_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_RX8_RX_PATH_SEC0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX8_RX_PATH_SEC1)] = 1, + [WCD9335_REG(WCD9335_CDC_RX8_RX_PATH_SEC2)] = 1, + [WCD9335_REG(WCD9335_CDC_RX8_RX_PATH_SEC3)] = 1, + [WCD9335_REG(WCD9335_CDC_RX8_RX_PATH_SEC5)] = 1, + [WCD9335_REG(WCD9335_CDC_RX8_RX_PATH_SEC6)] = 1, + [WCD9335_REG(WCD9335_CDC_RX8_RX_PATH_SEC7)] = 1, + [WCD9335_REG(WCD9335_CDC_RX8_RX_PATH_MIX_SEC0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX8_RX_PATH_MIX_SEC1)] = 1, +}; + +const u8 wcd9335_page12_reg_readable[WCD9335_PAGE_SIZE] = { + [WCD9335_REG(WCD9335_PAGE12_PAGE_REGISTER)] = 1, + [WCD9335_REG(WCD9335_CDC_CLSH_CRC)] = 1, + [WCD9335_REG(WCD9335_CDC_CLSH_DLY_CTRL)] = 1, + [WCD9335_REG(WCD9335_CDC_CLSH_DECAY_CTRL)] = 1, + [WCD9335_REG(WCD9335_CDC_CLSH_HPH_V_PA)] = 1, + [WCD9335_REG(WCD9335_CDC_CLSH_EAR_V_PA)] = 1, + [WCD9335_REG(WCD9335_CDC_CLSH_HPH_V_HD)] = 1, + [WCD9335_REG(WCD9335_CDC_CLSH_EAR_V_HD)] = 1, + [WCD9335_REG(WCD9335_CDC_CLSH_K1_MSB)] = 1, + [WCD9335_REG(WCD9335_CDC_CLSH_K1_LSB)] = 1, + [WCD9335_REG(WCD9335_CDC_CLSH_K2_MSB)] = 1, + [WCD9335_REG(WCD9335_CDC_CLSH_K2_LSB)] = 1, + [WCD9335_REG(WCD9335_CDC_CLSH_IDLE_CTRL)] = 1, + [WCD9335_REG(WCD9335_CDC_CLSH_IDLE_HPH)] = 1, + [WCD9335_REG(WCD9335_CDC_CLSH_IDLE_EAR)] = 1, + [WCD9335_REG(WCD9335_CDC_CLSH_TEST0)] = 1, + [WCD9335_REG(WCD9335_CDC_CLSH_TEST1)] = 1, + [WCD9335_REG(WCD9335_CDC_CLSH_OVR_VREF)] = 1, + [WCD9335_REG(WCD9335_CDC_BOOST0_BOOST_PATH_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_BOOST0_BOOST_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_BOOST0_BOOST_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_BOOST0_BOOST_CFG2)] = 1, + [WCD9335_REG(WCD9335_CDC_BOOST1_BOOST_PATH_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_BOOST1_BOOST_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_BOOST1_BOOST_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_BOOST1_BOOST_CFG2)] = 1, + [WCD9335_REG(WCD9335_SWR_AHB_BRIDGE_WR_DATA_0)] = 1, + [WCD9335_REG(WCD9335_SWR_AHB_BRIDGE_WR_DATA_1)] = 1, + [WCD9335_REG(WCD9335_SWR_AHB_BRIDGE_WR_DATA_2)] = 1, + [WCD9335_REG(WCD9335_SWR_AHB_BRIDGE_WR_DATA_3)] = 1, + [WCD9335_REG(WCD9335_SWR_AHB_BRIDGE_WR_ADDR_0)] = 1, + [WCD9335_REG(WCD9335_SWR_AHB_BRIDGE_WR_ADDR_1)] = 1, + [WCD9335_REG(WCD9335_SWR_AHB_BRIDGE_WR_ADDR_2)] = 1, + [WCD9335_REG(WCD9335_SWR_AHB_BRIDGE_WR_ADDR_3)] = 1, + [WCD9335_REG(WCD9335_SWR_AHB_BRIDGE_RD_ADDR_0)] = 1, + [WCD9335_REG(WCD9335_SWR_AHB_BRIDGE_RD_ADDR_1)] = 1, + [WCD9335_REG(WCD9335_SWR_AHB_BRIDGE_RD_ADDR_2)] = 1, + [WCD9335_REG(WCD9335_SWR_AHB_BRIDGE_RD_ADDR_3)] = 1, + [WCD9335_REG(WCD9335_SWR_AHB_BRIDGE_RD_DATA_0)] = 1, + [WCD9335_REG(WCD9335_SWR_AHB_BRIDGE_RD_DATA_1)] = 1, + [WCD9335_REG(WCD9335_SWR_AHB_BRIDGE_RD_DATA_2)] = 1, + [WCD9335_REG(WCD9335_SWR_AHB_BRIDGE_RD_DATA_3)] = 1, + [WCD9335_REG(WCD9335_SWR_AHB_BRIDGE_ACCESS_CFG)] = 1, + [WCD9335_REG(WCD9335_SWR_AHB_BRIDGE_ACCESS_STATUS)] = 1, + [WCD9335_REG(WCD9335_CDC_VBAT_VBAT_PATH_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_VBAT_VBAT_CFG)] = 1, + [WCD9335_REG(WCD9335_CDC_VBAT_VBAT_ADC_CAL1)] = 1, + [WCD9335_REG(WCD9335_CDC_VBAT_VBAT_ADC_CAL2)] = 1, + [WCD9335_REG(WCD9335_CDC_VBAT_VBAT_ADC_CAL3)] = 1, + [WCD9335_REG(WCD9335_CDC_VBAT_VBAT_PK_EST1)] = 1, + [WCD9335_REG(WCD9335_CDC_VBAT_VBAT_PK_EST2)] = 1, + [WCD9335_REG(WCD9335_CDC_VBAT_VBAT_PK_EST3)] = 1, + [WCD9335_REG(WCD9335_CDC_VBAT_VBAT_RF_PROC1)] = 1, + [WCD9335_REG(WCD9335_CDC_VBAT_VBAT_RF_PROC2)] = 1, + [WCD9335_REG(WCD9335_CDC_VBAT_VBAT_TAC1)] = 1, + [WCD9335_REG(WCD9335_CDC_VBAT_VBAT_TAC2)] = 1, + [WCD9335_REG(WCD9335_CDC_VBAT_VBAT_TAC3)] = 1, + [WCD9335_REG(WCD9335_CDC_VBAT_VBAT_TAC4)] = 1, + [WCD9335_REG(WCD9335_CDC_VBAT_VBAT_GAIN_UPD1)] = 1, + [WCD9335_REG(WCD9335_CDC_VBAT_VBAT_GAIN_UPD2)] = 1, + [WCD9335_REG(WCD9335_CDC_VBAT_VBAT_GAIN_UPD3)] = 1, + [WCD9335_REG(WCD9335_CDC_VBAT_VBAT_GAIN_UPD4)] = 1, + [WCD9335_REG(WCD9335_CDC_VBAT_VBAT_DEBUG1)] = 1, + [WCD9335_REG(WCD9335_CDC_VBAT_VBAT_GAIN_UPD_MON)] = 0, + [WCD9335_REG(WCD9335_CDC_VBAT_VBAT_GAIN_MON_VAL)] = 1, + [WCD9335_REG(WCD9335_SPLINE_SRC0_CLK_RST_CTL_0)] = 1, + [WCD9335_REG(WCD9335_SPLINE_SRC0_STATUS)] = 1, + [WCD9335_REG(WCD9335_SPLINE_SRC1_CLK_RST_CTL_0)] = 1, + [WCD9335_REG(WCD9335_SPLINE_SRC1_STATUS)] = 1, + [WCD9335_REG(WCD9335_SPLINE_SRC2_CLK_RST_CTL_0)] = 1, + [WCD9335_REG(WCD9335_SPLINE_SRC2_STATUS)] = 1, + [WCD9335_REG(WCD9335_SPLINE_SRC3_CLK_RST_CTL_0)] = 1, + [WCD9335_REG(WCD9335_SPLINE_SRC3_STATUS)] = 1, + [WCD9335_REG(WCD9335_CDC_SIDETONE_SRC0_ST_SRC_PATH_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_SIDETONE_SRC0_ST_SRC_PATH_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_SIDETONE_SRC1_ST_SRC_PATH_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_SIDETONE_SRC1_ST_SRC_PATH_CFG1)] = 1, +}; + +const u8 wcd9335_page13_reg_readable[WCD9335_PAGE_SIZE] = { + [WCD9335_REG(WCD9335_PAGE13_PAGE_REGISTER)] = 1, + [WCD9335_REG(WCD9335_CDC_RX_INP_MUX_RX_INT0_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX_INP_MUX_RX_INT0_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_RX_INP_MUX_RX_INT1_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX_INP_MUX_RX_INT1_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_RX_INP_MUX_RX_INT2_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX_INP_MUX_RX_INT2_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_RX_INP_MUX_RX_INT3_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX_INP_MUX_RX_INT3_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_RX_INP_MUX_RX_INT4_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX_INP_MUX_RX_INT4_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_RX_INP_MUX_RX_INT5_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX_INP_MUX_RX_INT5_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_RX_INP_MUX_RX_INT6_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX_INP_MUX_RX_INT6_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_RX_INP_MUX_RX_INT7_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX_INP_MUX_RX_INT7_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_RX_INP_MUX_RX_INT8_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX_INP_MUX_RX_INT8_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_RX_INP_MUX_RX_MIX_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX_INP_MUX_RX_MIX_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_RX_INP_MUX_RX_MIX_CFG2)] = 1, + [WCD9335_REG(WCD9335_CDC_RX_INP_MUX_RX_MIX_CFG3)] = 1, + [WCD9335_REG(WCD9335_CDC_RX_INP_MUX_RX_MIX_CFG4)] = 1, + [WCD9335_REG(WCD9335_CDC_RX_INP_MUX_SIDETONE_SRC_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX_INP_MUX_SIDETONE_SRC_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_RX_INP_MUX_ANC_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX_INP_MUX_SPLINE_SRC_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_TX_INP_MUX_ADC_MUX0_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_TX_INP_MUX_ADC_MUX0_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_TX_INP_MUX_ADC_MUX1_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_TX_INP_MUX_ADC_MUX1_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_TX_INP_MUX_ADC_MUX2_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_TX_INP_MUX_ADC_MUX2_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_TX_INP_MUX_ADC_MUX3_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_TX_INP_MUX_ADC_MUX3_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_TX_INP_MUX_ADC_MUX4_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_TX_INP_MUX_ADC_MUX5_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_TX_INP_MUX_ADC_MUX6_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_TX_INP_MUX_ADC_MUX7_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_TX_INP_MUX_ADC_MUX8_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_TX_INP_MUX_ADC_MUX10_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_TX_INP_MUX_ADC_MUX11_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_TX_INP_MUX_ADC_MUX12_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_TX_INP_MUX_ADC_MUX13_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG2)] = 1, + [WCD9335_REG(WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG3)] = 1, + [WCD9335_REG(WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG2)] = 1, + [WCD9335_REG(WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG3)] = 1, + [WCD9335_REG(WCD9335_CDC_IF_ROUTER_TX_MUX_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_IF_ROUTER_TX_MUX_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_IF_ROUTER_TX_MUX_CFG2)] = 1, + [WCD9335_REG(WCD9335_CDC_IF_ROUTER_TX_MUX_CFG3)] = 1, + [WCD9335_REG(WCD9335_CDC_CLK_RST_CTRL_MCLK_CONTROL)] = 1, + [WCD9335_REG(WCD9335_CDC_CLK_RST_CTRL_FS_CNT_CONTROL)] = 1, + [WCD9335_REG(WCD9335_CDC_CLK_RST_CTRL_SWR_CONTROL)] = 1, + [WCD9335_REG(WCD9335_CDC_PROX_DETECT_PROX_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_PROX_DETECT_PROX_POLL_PERIOD0)] = 1, + [WCD9335_REG(WCD9335_CDC_PROX_DETECT_PROX_POLL_PERIOD1)] = 1, + [WCD9335_REG(WCD9335_CDC_PROX_DETECT_PROX_SIG_PATTERN_LSB)] = 1, + [WCD9335_REG(WCD9335_CDC_PROX_DETECT_PROX_SIG_PATTERN_MSB)] = 1, + [WCD9335_REG(WCD9335_CDC_PROX_DETECT_PROX_STATUS)] = 1, + [WCD9335_REG(WCD9335_CDC_PROX_DETECT_PROX_TEST_CTRL)] = 1, + [WCD9335_REG(WCD9335_CDC_PROX_DETECT_PROX_TEST_BUFF_LSB)] = 1, + [WCD9335_REG(WCD9335_CDC_PROX_DETECT_PROX_TEST_BUFF_MSB)] = 1, + [WCD9335_REG(WCD9335_CDC_PROX_DETECT_PROX_TEST_BUFF_LSB_RD)] = 1, + [WCD9335_REG(WCD9335_CDC_PROX_DETECT_PROX_TEST_BUFF_MSB_RD)] = 1, + [WCD9335_REG(WCD9335_CDC_PROX_DETECT_PROX_CTL_REPEAT_PAT)] = 1, + [WCD9335_REG(WCD9335_CDC_SIDETONE_IIR0_IIR_PATH_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B1_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B2_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B3_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B4_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B5_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B6_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B7_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B8_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_SIDETONE_IIR0_IIR_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_TIMER_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_SIDETONE_IIR1_IIR_PATH_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B1_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B2_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B3_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B4_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B5_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B6_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B7_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B8_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_SIDETONE_IIR1_IIR_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_TIMER_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_SIDETONE_IIR1_IIR_COEF_B1_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_SIDETONE_IIR1_IIR_COEF_B2_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_TOP_TOP_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_TOP_TOP_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_TOP_TOP_CFG2)] = 1, + [WCD9335_REG(WCD9335_CDC_TOP_TOP_CFG3)] = 1, + [WCD9335_REG(WCD9335_CDC_TOP_TOP_CFG4)] = 1, + [WCD9335_REG(WCD9335_CDC_TOP_TOP_CFG5)] = 1, + [WCD9335_REG(WCD9335_CDC_TOP_TOP_CFG6)] = 1, + [WCD9335_REG(WCD9335_CDC_TOP_TOP_CFG7)] = 1, + [WCD9335_REG(WCD9335_CDC_TOP_HPHL_COMP_WR_LSB)] = 1, + [WCD9335_REG(WCD9335_CDC_TOP_HPHL_COMP_WR_MSB)] = 1, + [WCD9335_REG(WCD9335_CDC_TOP_HPHL_COMP_LUT)] = 1, + [WCD9335_REG(WCD9335_CDC_TOP_HPHL_COMP_RD_LSB)] = 1, + [WCD9335_REG(WCD9335_CDC_TOP_HPHL_COMP_RD_MSB)] = 1, + [WCD9335_REG(WCD9335_CDC_TOP_HPHR_COMP_WR_LSB)] = 1, + [WCD9335_REG(WCD9335_CDC_TOP_HPHR_COMP_WR_MSB)] = 1, + [WCD9335_REG(WCD9335_CDC_TOP_HPHR_COMP_LUT)] = 1, + [WCD9335_REG(WCD9335_CDC_TOP_HPHR_COMP_RD_LSB)] = 1, + [WCD9335_REG(WCD9335_CDC_TOP_HPHR_COMP_RD_MSB)] = 1, + [WCD9335_REG(WCD9335_CDC_TOP_DIFFL_COMP_WR_LSB)] = 1, + [WCD9335_REG(WCD9335_CDC_TOP_DIFFL_COMP_WR_MSB)] = 1, + [WCD9335_REG(WCD9335_CDC_TOP_DIFFL_COMP_LUT)] = 1, + [WCD9335_REG(WCD9335_CDC_TOP_DIFFL_COMP_RD_LSB)] = 1, + [WCD9335_REG(WCD9335_CDC_TOP_DIFFL_COMP_RD_MSB)] = 1, + [WCD9335_REG(WCD9335_CDC_TOP_DIFFR_COMP_WR_LSB)] = 1, + [WCD9335_REG(WCD9335_CDC_TOP_DIFFR_COMP_WR_MSB)] = 1, + [WCD9335_REG(WCD9335_CDC_TOP_DIFFR_COMP_LUT)] = 1, + [WCD9335_REG(WCD9335_CDC_TOP_DIFFR_COMP_RD_LSB)] = 1, + [WCD9335_REG(WCD9335_CDC_TOP_DIFFR_COMP_RD_MSB)] = 1, +}; + +const u8 wcd9335_page_0x80_reg_readable[WCD9335_PAGE_SIZE] = { + [WCD9335_REG(WCD9335_PAGE80_PAGE_REGISTER)] = 1, + [WCD9335_REG(WCD9335_TLMM_BIST_MODE_PINCFG)] = 1, + [WCD9335_REG(WCD9335_TLMM_RF_PA_ON_PINCFG)] = 1, + [WCD9335_REG(WCD9335_TLMM_INTR1_PINCFG)] = 1, + [WCD9335_REG(WCD9335_TLMM_INTR2_PINCFG)] = 1, + [WCD9335_REG(WCD9335_TLMM_SWR_DATA_PINCFG)] = 1, + [WCD9335_REG(WCD9335_TLMM_SWR_CLK_PINCFG)] = 1, + [WCD9335_REG(WCD9335_TLMM_SLIMBUS_DATA2_PINCFG)] = 1, + [WCD9335_REG(WCD9335_TLMM_I2C_CLK_PINCFG)] = 1, + [WCD9335_REG(WCD9335_TLMM_I2C_DATA_PINCFG)] = 1, + [WCD9335_REG(WCD9335_TLMM_I2S_RX_SD0_PINCFG)] = 1, + [WCD9335_REG(WCD9335_TLMM_I2S_RX_SD1_PINCFG)] = 1, + [WCD9335_REG(WCD9335_TLMM_I2S_RX_SCK_PINCFG)] = 1, + [WCD9335_REG(WCD9335_TLMM_I2S_RX_WS_PINCFG)] = 1, + [WCD9335_REG(WCD9335_TLMM_I2S_TX_SD0_PINCFG)] = 1, + [WCD9335_REG(WCD9335_TLMM_I2S_TX_SD1_PINCFG)] = 1, + [WCD9335_REG(WCD9335_TLMM_I2S_TX_SCK_PINCFG)] = 1, + [WCD9335_REG(WCD9335_TLMM_I2S_TX_WS_PINCFG)] = 1, + [WCD9335_REG(WCD9335_TLMM_DMIC1_CLK_PINCFG)] = 1, + [WCD9335_REG(WCD9335_TLMM_DMIC1_DATA_PINCFG)] = 1, + [WCD9335_REG(WCD9335_TLMM_DMIC2_CLK_PINCFG)] = 1, + [WCD9335_REG(WCD9335_TLMM_DMIC2_DATA_PINCFG)] = 1, + [WCD9335_REG(WCD9335_TLMM_DMIC3_CLK_PINCFG)] = 1, + [WCD9335_REG(WCD9335_TLMM_DMIC3_DATA_PINCFG)] = 1, + [WCD9335_REG(WCD9335_TLMM_JTDI_PINCFG)] = 1, + [WCD9335_REG(WCD9335_TLMM_JTDO_PINCFG)] = 1, + [WCD9335_REG(WCD9335_TLMM_JTMS_PINCFG)] = 1, + [WCD9335_REG(WCD9335_TLMM_JTCK_PINCFG)] = 1, + [WCD9335_REG(WCD9335_TLMM_JTRST_PINCFG)] = 1, + [WCD9335_REG(WCD9335_TEST_DEBUG_PIN_CTL_OE_0)] = 1, + [WCD9335_REG(WCD9335_TEST_DEBUG_PIN_CTL_OE_1)] = 1, + [WCD9335_REG(WCD9335_TEST_DEBUG_PIN_CTL_OE_2)] = 1, + [WCD9335_REG(WCD9335_TEST_DEBUG_PIN_CTL_OE_3)] = 1, + [WCD9335_REG(WCD9335_TEST_DEBUG_PIN_CTL_DATA_0)] = 1, + [WCD9335_REG(WCD9335_TEST_DEBUG_PIN_CTL_DATA_1)] = 1, + [WCD9335_REG(WCD9335_TEST_DEBUG_PIN_CTL_DATA_2)] = 1, + [WCD9335_REG(WCD9335_TEST_DEBUG_PIN_CTL_DATA_3)] = 1, + [WCD9335_REG(WCD9335_TEST_DEBUG_PAD_DRVCTL)] = 1, + [WCD9335_REG(WCD9335_TEST_DEBUG_PIN_STATUS)] = 1, + [WCD9335_REG(WCD9335_TEST_DEBUG_NPL_DLY_TEST_1)] = 1, + [WCD9335_REG(WCD9335_TEST_DEBUG_NPL_DLY_TEST_2)] = 1, + [WCD9335_REG(WCD9335_TEST_DEBUG_MEM_CTRL)] = 1, + [WCD9335_REG(WCD9335_TEST_DEBUG_DEBUG_BUS_SEL)] = 1, + [WCD9335_REG(WCD9335_TEST_DEBUG_DEBUG_JTAG)] = 1, + [WCD9335_REG(WCD9335_TEST_DEBUG_DEBUG_EN_1)] = 1, + [WCD9335_REG(WCD9335_TEST_DEBUG_DEBUG_EN_2)] = 1, + [WCD9335_REG(WCD9335_TEST_DEBUG_DEBUG_EN_3)] = 1, +}; + +const u8 *wcd9335_reg[WCD9335_NUM_PAGES] = { + [PAGE_0] = wcd9335_page0_reg_readable, + [PAGE_1] = wcd9335_page1_reg_readable, + [PAGE_2] = wcd9335_page2_reg_readable, + [PAGE_6] = wcd9335_page6_reg_readable, + [PAGE_10] = wcd9335_page10_reg_readable, + [PAGE_11] = wcd9335_page11_reg_readable, + [PAGE_12] = wcd9335_page12_reg_readable, + [PAGE_13] = wcd9335_page13_reg_readable, + [PAGE_0X80] = wcd9335_page_0x80_reg_readable, +}; diff --git a/audio-kernel/asoc/codecs/wcd9335.c b/audio-kernel/asoc/codecs/wcd9335.c new file mode 100644 index 000000000..4706a0e08 --- /dev/null +++ b/audio-kernel/asoc/codecs/wcd9335.c @@ -0,0 +1,14393 @@ +/* + * Copyright (c) 2015-2019, 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "core.h" +#include "pdata.h" +#include "wcd9335.h" +#include "wcd-mbhc-v2.h" +#include "wcd9xxx-common-v2.h" +#include "wcd9xxx-resmgr-v2.h" +#include "wcd9xxx-irq.h" +#include "wcd9335_registers.h" +#include "wcd9335_irq.h" +#include "wcd_cpe_core.h" +#include "wcdcal-hwdep.h" +#include "wcd-mbhc-v2-api.h" + +#define TASHA_RX_PORT_START_NUMBER 16 + +#define WCD9335_RATES_MASK (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\ + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |\ + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000) +/* Fractional Rates */ +#define WCD9335_FRAC_RATES_MASK (SNDRV_PCM_RATE_44100) + +#define WCD9335_MIX_RATES_MASK (SNDRV_PCM_RATE_48000 |\ + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000) + +#define TASHA_FORMATS_S16_S24_LE (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S24_3LE) + +#define TASHA_FORMATS_S16_S24_S32_LE (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S24_3LE | \ + SNDRV_PCM_FMTBIT_S32_LE) + +#define TASHA_FORMATS (SNDRV_PCM_FMTBIT_S16_LE) + +/* + * Timeout in milli seconds and it is the wait time for + * slim channel removal interrupt to receive. + */ +#define TASHA_SLIM_CLOSE_TIMEOUT 1000 +#define TASHA_SLIM_IRQ_OVERFLOW (1 << 0) +#define TASHA_SLIM_IRQ_UNDERFLOW (1 << 1) +#define TASHA_SLIM_IRQ_PORT_CLOSED (1 << 2) +#define TASHA_MCLK_CLK_12P288MHZ 12288000 +#define TASHA_MCLK_CLK_9P6MHZ 9600000 + +#define TASHA_SLIM_PGD_PORT_INT_TX_EN0 (TASHA_SLIM_PGD_PORT_INT_EN0 + 2) + +#define TASHA_NUM_INTERPOLATORS 9 +#define TASHA_NUM_DECIMATORS 9 + +#define WCD9335_CHILD_DEVICES_MAX 6 + +#define BYTE_BIT_MASK(nr) (1 << ((nr) % BITS_PER_BYTE)) +#define TASHA_MAD_AUDIO_FIRMWARE_PATH "wcd9335/wcd9335_mad_audio.bin" +#define TASHA_CPE_SS_ERR_STATUS_MEM_ACCESS (1 << 0) +#define TASHA_CPE_SS_ERR_STATUS_WDOG_BITE (1 << 1) + +#define TASHA_CPE_FATAL_IRQS \ + (TASHA_CPE_SS_ERR_STATUS_WDOG_BITE | \ + TASHA_CPE_SS_ERR_STATUS_MEM_ACCESS) + +#define SLIM_BW_CLK_GEAR_9 6200000 +#define SLIM_BW_UNVOTE 0 + +#define CPE_FLL_CLK_75MHZ 75000000 +#define CPE_FLL_CLK_150MHZ 150000000 +#define WCD9335_REG_BITS 8 + +#define WCD9335_MAX_VALID_ADC_MUX 13 +#define WCD9335_INVALID_ADC_MUX 9 + +#define TASHA_DIG_CORE_REG_MIN WCD9335_CDC_ANC0_CLK_RESET_CTL +#define TASHA_DIG_CORE_REG_MAX 0xDFF + +/* Convert from vout ctl to micbias voltage in mV */ +#define WCD_VOUT_CTL_TO_MICB(v) (1000 + v * 50) + +#define TASHA_ZDET_NUM_MEASUREMENTS 900 +#define TASHA_MBHC_GET_C1(c) ((c & 0xC000) >> 14) +#define TASHA_MBHC_GET_X1(x) (x & 0x3FFF) +/* z value compared in milliOhm */ +#define TASHA_MBHC_IS_SECOND_RAMP_REQUIRED(z) ((z > 400000) || (z < 32000)) +#define TASHA_MBHC_ZDET_CONST (86 * 16384) +#define TASHA_MBHC_MOISTURE_VREF V_45_MV +#define TASHA_MBHC_MOISTURE_IREF I_3P0_UA + +#define TASHA_VERSION_ENTRY_SIZE 17 + +#define WCD9335_AMIC_PWR_LEVEL_LP 0 +#define WCD9335_AMIC_PWR_LEVEL_DEFAULT 1 +#define WCD9335_AMIC_PWR_LEVEL_HP 2 +#define WCD9335_AMIC_PWR_LVL_MASK 0x60 +#define WCD9335_AMIC_PWR_LVL_SHIFT 0x5 + +#define WCD9335_DEC_PWR_LVL_MASK 0x06 +#define WCD9335_DEC_PWR_LVL_LP 0x02 +#define WCD9335_DEC_PWR_LVL_HP 0x04 +#define WCD9335_DEC_PWR_LVL_DF 0x00 +#define WCD9335_STRING_LEN 100 + +#define CALCULATE_VOUT_D(req_mv) (((req_mv - 650) * 10) / 25) + +static int cpe_debug_mode; + +#define TASHA_MAX_MICBIAS 4 +#define DAPM_MICBIAS1_STANDALONE "MIC BIAS1 Standalone" +#define DAPM_MICBIAS2_STANDALONE "MIC BIAS2 Standalone" +#define DAPM_MICBIAS3_STANDALONE "MIC BIAS3 Standalone" +#define DAPM_MICBIAS4_STANDALONE "MIC BIAS4 Standalone" + +#define DAPM_LDO_H_STANDALONE "LDO_H" +module_param(cpe_debug_mode, int, 0664); +MODULE_PARM_DESC(cpe_debug_mode, "boot cpe in debug mode"); + +#define TASHA_DIG_CORE_COLLAPSE_TIMER_MS (5 * 1000) + +#define MAX_ON_DEMAND_SUPPLY_NAME_LENGTH 64 + +static char on_demand_supply_name[][MAX_ON_DEMAND_SUPPLY_NAME_LENGTH] = { + "cdc-vdd-mic-bias", +}; + +enum { + POWER_COLLAPSE, + POWER_RESUME, +}; + +enum tasha_sido_voltage { + SIDO_VOLTAGE_SVS_MV = 950, + SIDO_VOLTAGE_NOMINAL_MV = 1100, +}; + +static enum codec_variant codec_ver; + +static int dig_core_collapse_enable = 1; +module_param(dig_core_collapse_enable, int, 0664); +MODULE_PARM_DESC(dig_core_collapse_enable, "enable/disable power gating"); + +/* dig_core_collapse timer in seconds */ +static int dig_core_collapse_timer = (TASHA_DIG_CORE_COLLAPSE_TIMER_MS/1000); +module_param(dig_core_collapse_timer, int, 0664); +MODULE_PARM_DESC(dig_core_collapse_timer, "timer for power gating"); + +/* SVS Scaling enable/disable */ +static int svs_scaling_enabled = 1; +module_param(svs_scaling_enabled, int, 0664); +MODULE_PARM_DESC(svs_scaling_enabled, "enable/disable svs scaling"); + +/* SVS buck setting */ +static int sido_buck_svs_voltage = SIDO_VOLTAGE_SVS_MV; +module_param(sido_buck_svs_voltage, int, 0664); +MODULE_PARM_DESC(sido_buck_svs_voltage, + "setting for SVS voltage for SIDO BUCK"); + +#define TASHA_TX_UNMUTE_DELAY_MS 40 + +static int tx_unmute_delay = TASHA_TX_UNMUTE_DELAY_MS; +module_param(tx_unmute_delay, int, 0664); +MODULE_PARM_DESC(tx_unmute_delay, "delay to unmute the tx path"); + +static struct afe_param_slimbus_slave_port_cfg tasha_slimbus_slave_port_cfg = { + .minor_version = 1, + .slimbus_dev_id = AFE_SLIMBUS_DEVICE_1, + .slave_dev_pgd_la = 0, + .slave_dev_intfdev_la = 0, + .bit_width = 16, + .data_format = 0, + .num_channels = 1 +}; + +struct tasha_mbhc_zdet_param { + u16 ldo_ctl; + u16 noff; + u16 nshift; + u16 btn5; + u16 btn6; + u16 btn7; +}; + +static struct afe_param_cdc_reg_page_cfg tasha_cdc_reg_page_cfg = { + .minor_version = AFE_API_VERSION_CDC_REG_PAGE_CFG, + .enable = 1, + .proc_id = AFE_CDC_REG_PAGE_ASSIGN_PROC_ID_1, +}; + +static struct afe_param_cdc_reg_cfg audio_reg_cfg[] = { + { + 1, + (TASHA_REGISTER_START_OFFSET + WCD9335_SOC_MAD_MAIN_CTL_1), + HW_MAD_AUDIO_ENABLE, 0x1, WCD9335_REG_BITS, 0 + }, + { + 1, + (TASHA_REGISTER_START_OFFSET + WCD9335_SOC_MAD_AUDIO_CTL_3), + HW_MAD_AUDIO_SLEEP_TIME, 0xF, WCD9335_REG_BITS, 0 + }, + { + 1, + (TASHA_REGISTER_START_OFFSET + WCD9335_SOC_MAD_AUDIO_CTL_4), + HW_MAD_TX_AUDIO_SWITCH_OFF, 0x1, WCD9335_REG_BITS, 0 + }, + { + 1, + (TASHA_REGISTER_START_OFFSET + WCD9335_INTR_CFG), + MAD_AUDIO_INT_DEST_SELECT_REG, 0x2, WCD9335_REG_BITS, 0 + }, + { + 1, + (TASHA_REGISTER_START_OFFSET + WCD9335_INTR_PIN2_MASK3), + MAD_AUDIO_INT_MASK_REG, 0x1, WCD9335_REG_BITS, 0 + }, + { + 1, + (TASHA_REGISTER_START_OFFSET + WCD9335_INTR_PIN2_STATUS3), + MAD_AUDIO_INT_STATUS_REG, 0x1, WCD9335_REG_BITS, 0 + }, + { + 1, + (TASHA_REGISTER_START_OFFSET + WCD9335_INTR_PIN2_CLEAR3), + MAD_AUDIO_INT_CLEAR_REG, 0x1, WCD9335_REG_BITS, 0 + }, + { + 1, + (TASHA_REGISTER_START_OFFSET + WCD9335_INTR_CFG), + VBAT_INT_DEST_SELECT_REG, 0x2, WCD9335_REG_BITS, 0 + }, + { + 1, + (TASHA_REGISTER_START_OFFSET + WCD9335_INTR_PIN2_MASK3), + VBAT_INT_MASK_REG, 0x08, WCD9335_REG_BITS, 0 + }, + { + 1, + (TASHA_REGISTER_START_OFFSET + WCD9335_INTR_PIN2_STATUS3), + VBAT_INT_STATUS_REG, 0x08, WCD9335_REG_BITS, 0 + }, + { + 1, + (TASHA_REGISTER_START_OFFSET + WCD9335_INTR_PIN2_CLEAR3), + VBAT_INT_CLEAR_REG, 0x08, WCD9335_REG_BITS, 0 + }, + { + 1, + (TASHA_REGISTER_START_OFFSET + WCD9335_INTR_CFG), + VBAT_RELEASE_INT_DEST_SELECT_REG, 0x2, WCD9335_REG_BITS, 0 + }, + { + 1, + (TASHA_REGISTER_START_OFFSET + WCD9335_INTR_PIN2_MASK3), + VBAT_RELEASE_INT_MASK_REG, 0x10, WCD9335_REG_BITS, 0 + }, + { + 1, + (TASHA_REGISTER_START_OFFSET + WCD9335_INTR_PIN2_STATUS3), + VBAT_RELEASE_INT_STATUS_REG, 0x10, WCD9335_REG_BITS, 0 + }, + { + 1, + (TASHA_REGISTER_START_OFFSET + WCD9335_INTR_PIN2_CLEAR3), + VBAT_RELEASE_INT_CLEAR_REG, 0x10, WCD9335_REG_BITS, 0 + }, + { + 1, + (TASHA_REGISTER_START_OFFSET + TASHA_SB_PGD_PORT_TX_BASE), + SB_PGD_PORT_TX_WATERMARK_N, 0x1E, WCD9335_REG_BITS, 0x1 + }, + { + 1, + (TASHA_REGISTER_START_OFFSET + TASHA_SB_PGD_PORT_TX_BASE), + SB_PGD_PORT_TX_ENABLE_N, 0x1, WCD9335_REG_BITS, 0x1 + }, + { + 1, + (TASHA_REGISTER_START_OFFSET + TASHA_SB_PGD_PORT_RX_BASE), + SB_PGD_PORT_RX_WATERMARK_N, 0x1E, WCD9335_REG_BITS, 0x1 + }, + { + 1, + (TASHA_REGISTER_START_OFFSET + TASHA_SB_PGD_PORT_RX_BASE), + SB_PGD_PORT_RX_ENABLE_N, 0x1, WCD9335_REG_BITS, 0x1 + }, + { 1, + (TASHA_REGISTER_START_OFFSET + WCD9335_CDC_ANC0_IIR_ADAPT_CTL), + AANC_FF_GAIN_ADAPTIVE, 0x4, WCD9335_REG_BITS, 0 + }, + { 1, + (TASHA_REGISTER_START_OFFSET + WCD9335_CDC_ANC0_IIR_ADAPT_CTL), + AANC_FFGAIN_ADAPTIVE_EN, 0x8, WCD9335_REG_BITS, 0 + }, + { + 1, + (TASHA_REGISTER_START_OFFSET + WCD9335_CDC_ANC0_FF_A_GAIN_CTL), + AANC_GAIN_CONTROL, 0xFF, WCD9335_REG_BITS, 0 + }, +}; + +static struct afe_param_cdc_reg_cfg_data tasha_audio_reg_cfg = { + .num_registers = ARRAY_SIZE(audio_reg_cfg), + .reg_data = audio_reg_cfg, +}; + +static struct afe_param_id_cdc_aanc_version tasha_cdc_aanc_version = { + .cdc_aanc_minor_version = AFE_API_VERSION_CDC_AANC_VERSION, + .aanc_hw_version = AANC_HW_BLOCK_VERSION_2, +}; + +enum { + VI_SENSE_1, + VI_SENSE_2, + AIF4_SWITCH_VALUE, + AUDIO_NOMINAL, + CPE_NOMINAL, + HPH_PA_DELAY, + ANC_MIC_AMIC1, + ANC_MIC_AMIC2, + ANC_MIC_AMIC3, + ANC_MIC_AMIC4, + ANC_MIC_AMIC5, + ANC_MIC_AMIC6, + CLASSH_CONFIG, +}; + +enum { + AIF1_PB = 0, + AIF1_CAP, + AIF2_PB, + AIF2_CAP, + AIF3_PB, + AIF3_CAP, + AIF4_PB, + AIF_MIX1_PB, + AIF4_MAD_TX, + AIF4_VIFEED, + AIF5_CPE_TX, + NUM_CODEC_DAIS, +}; + +enum { + INTn_1_MIX_INP_SEL_ZERO = 0, + INTn_1_MIX_INP_SEL_DEC0, + INTn_1_MIX_INP_SEL_DEC1, + INTn_1_MIX_INP_SEL_IIR0, + INTn_1_MIX_INP_SEL_IIR1, + INTn_1_MIX_INP_SEL_RX0, + INTn_1_MIX_INP_SEL_RX1, + INTn_1_MIX_INP_SEL_RX2, + INTn_1_MIX_INP_SEL_RX3, + INTn_1_MIX_INP_SEL_RX4, + INTn_1_MIX_INP_SEL_RX5, + INTn_1_MIX_INP_SEL_RX6, + INTn_1_MIX_INP_SEL_RX7, + +}; + +#define IS_VALID_NATIVE_FIFO_PORT(inp) \ + ((inp >= INTn_1_MIX_INP_SEL_RX0) && \ + (inp <= INTn_1_MIX_INP_SEL_RX3)) + +enum { + INTn_2_INP_SEL_ZERO = 0, + INTn_2_INP_SEL_RX0, + INTn_2_INP_SEL_RX1, + INTn_2_INP_SEL_RX2, + INTn_2_INP_SEL_RX3, + INTn_2_INP_SEL_RX4, + INTn_2_INP_SEL_RX5, + INTn_2_INP_SEL_RX6, + INTn_2_INP_SEL_RX7, + INTn_2_INP_SEL_PROXIMITY, +}; + +enum { + INTERP_EAR = 0, + INTERP_HPHL, + INTERP_HPHR, + INTERP_LO1, + INTERP_LO2, + INTERP_LO3, + INTERP_LO4, + INTERP_SPKR1, + INTERP_SPKR2, +}; + +struct interp_sample_rate { + int sample_rate; + int rate_val; +}; + +static struct interp_sample_rate int_prim_sample_rate_val[] = { + {8000, 0x0}, /* 8K */ + {16000, 0x1}, /* 16K */ + {24000, -EINVAL},/* 24K */ + {32000, 0x3}, /* 32K */ + {48000, 0x4}, /* 48K */ + {96000, 0x5}, /* 96K */ + {192000, 0x6}, /* 192K */ + {384000, 0x7}, /* 384K */ + {44100, 0x8}, /* 44.1K */ +}; + +static struct interp_sample_rate int_mix_sample_rate_val[] = { + {48000, 0x4}, /* 48K */ + {96000, 0x5}, /* 96K */ + {192000, 0x6}, /* 192K */ +}; + +static const struct wcd9xxx_ch tasha_rx_chs[TASHA_RX_MAX] = { + WCD9XXX_CH(TASHA_RX_PORT_START_NUMBER, 0), + WCD9XXX_CH(TASHA_RX_PORT_START_NUMBER + 1, 1), + WCD9XXX_CH(TASHA_RX_PORT_START_NUMBER + 2, 2), + WCD9XXX_CH(TASHA_RX_PORT_START_NUMBER + 3, 3), + WCD9XXX_CH(TASHA_RX_PORT_START_NUMBER + 4, 4), + WCD9XXX_CH(TASHA_RX_PORT_START_NUMBER + 5, 5), + WCD9XXX_CH(TASHA_RX_PORT_START_NUMBER + 6, 6), + WCD9XXX_CH(TASHA_RX_PORT_START_NUMBER + 7, 7), + WCD9XXX_CH(TASHA_RX_PORT_START_NUMBER + 8, 8), + WCD9XXX_CH(TASHA_RX_PORT_START_NUMBER + 9, 9), + WCD9XXX_CH(TASHA_RX_PORT_START_NUMBER + 10, 10), + WCD9XXX_CH(TASHA_RX_PORT_START_NUMBER + 11, 11), + WCD9XXX_CH(TASHA_RX_PORT_START_NUMBER + 12, 12), +}; + +static const struct wcd9xxx_ch tasha_tx_chs[TASHA_TX_MAX] = { + WCD9XXX_CH(0, 0), + WCD9XXX_CH(1, 1), + WCD9XXX_CH(2, 2), + WCD9XXX_CH(3, 3), + WCD9XXX_CH(4, 4), + WCD9XXX_CH(5, 5), + WCD9XXX_CH(6, 6), + WCD9XXX_CH(7, 7), + WCD9XXX_CH(8, 8), + WCD9XXX_CH(9, 9), + WCD9XXX_CH(10, 10), + WCD9XXX_CH(11, 11), + WCD9XXX_CH(12, 12), + WCD9XXX_CH(13, 13), + WCD9XXX_CH(14, 14), + WCD9XXX_CH(15, 15), +}; + +static const u32 vport_slim_check_table[NUM_CODEC_DAIS] = { + /* Needs to define in the same order of DAI enum definitions */ + 0, + BIT(AIF2_CAP) | BIT(AIF3_CAP) | BIT(AIF4_MAD_TX) | BIT(AIF5_CPE_TX), + 0, + BIT(AIF1_CAP) | BIT(AIF3_CAP) | BIT(AIF4_MAD_TX) | BIT(AIF5_CPE_TX), + 0, + BIT(AIF1_CAP) | BIT(AIF2_CAP) | BIT(AIF4_MAD_TX) | BIT(AIF5_CPE_TX), + 0, + 0, + BIT(AIF1_CAP) | BIT(AIF2_CAP) | BIT(AIF3_CAP) | BIT(AIF5_CPE_TX), + 0, + BIT(AIF1_CAP) | BIT(AIF2_CAP) | BIT(AIF3_CAP) | BIT(AIF4_MAD_TX), +}; + +static const u32 vport_i2s_check_table[NUM_CODEC_DAIS] = { + 0, /* AIF1_PB */ + BIT(AIF2_CAP), /* AIF1_CAP */ + 0, /* AIF2_PB */ + BIT(AIF1_CAP), /* AIF2_CAP */ +}; + +/* Codec supports 2 IIR filters */ +enum { + IIR0 = 0, + IIR1, + IIR_MAX, +}; + +/* Each IIR has 5 Filter Stages */ +enum { + BAND1 = 0, + BAND2, + BAND3, + BAND4, + BAND5, + BAND_MAX, +}; + +enum { + COMPANDER_1, /* HPH_L */ + COMPANDER_2, /* HPH_R */ + COMPANDER_3, /* LO1_DIFF */ + COMPANDER_4, /* LO2_DIFF */ + COMPANDER_5, /* LO3_SE */ + COMPANDER_6, /* LO4_SE */ + COMPANDER_7, /* SWR SPK CH1 */ + COMPANDER_8, /* SWR SPK CH2 */ + COMPANDER_MAX, +}; + +enum { + SRC_IN_HPHL, + SRC_IN_LO1, + SRC_IN_HPHR, + SRC_IN_LO2, + SRC_IN_SPKRL, + SRC_IN_LO3, + SRC_IN_SPKRR, + SRC_IN_LO4, +}; + +enum { + SPLINE_SRC0, + SPLINE_SRC1, + SPLINE_SRC2, + SPLINE_SRC3, + SPLINE_SRC_MAX, +}; + +static const DECLARE_TLV_DB_SCALE(digital_gain, 0, 1, 0); +static const DECLARE_TLV_DB_SCALE(line_gain, 0, 7, 1); +static const DECLARE_TLV_DB_SCALE(analog_gain, 0, 25, 1); + +static struct snd_soc_dai_driver tasha_dai[]; +static int wcd9335_get_micb_vout_ctl_val(u32 micb_mv); + +static int tasha_config_compander(struct snd_soc_codec *, int, int); +static void tasha_codec_set_tx_hold(struct snd_soc_codec *, u16, bool); +static int tasha_codec_internal_rco_ctrl(struct snd_soc_codec *codec, + bool enable); + +/* Hold instance to soundwire platform device */ +struct tasha_swr_ctrl_data { + struct platform_device *swr_pdev; + struct ida swr_ida; +}; + +struct wcd_swr_ctrl_platform_data { + void *handle; /* holds codec private data */ + int (*read)(void *handle, int reg); + int (*write)(void *handle, int reg, int val); + int (*bulk_write)(void *handle, u32 *reg, u32 *val, size_t len); + int (*clk)(void *handle, bool enable); + int (*handle_irq)(void *handle, + irqreturn_t (*swrm_irq_handler)(int irq, + void *data), + void *swrm_handle, + int action); +}; + +static struct wcd_mbhc_register + wcd_mbhc_registers[WCD_MBHC_REG_FUNC_MAX] = { + WCD_MBHC_REGISTER("WCD_MBHC_L_DET_EN", + WCD9335_ANA_MBHC_MECH, 0x80, 7, 0), + WCD_MBHC_REGISTER("WCD_MBHC_GND_DET_EN", + WCD9335_ANA_MBHC_MECH, 0x40, 6, 0), + WCD_MBHC_REGISTER("WCD_MBHC_MECH_DETECTION_TYPE", + WCD9335_ANA_MBHC_MECH, 0x20, 5, 0), + WCD_MBHC_REGISTER("WCD_MBHC_MIC_CLAMP_CTL", + WCD9335_MBHC_PLUG_DETECT_CTL, 0x30, 4, 0), + WCD_MBHC_REGISTER("WCD_MBHC_ELECT_DETECTION_TYPE", + WCD9335_ANA_MBHC_ELECT, 0x08, 3, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HS_L_DET_PULL_UP_CTRL", + WCD9335_MBHC_PLUG_DETECT_CTL, 0xC0, 6, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HS_L_DET_PULL_UP_COMP_CTRL", + WCD9335_ANA_MBHC_MECH, 0x04, 2, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPHL_PLUG_TYPE", + WCD9335_ANA_MBHC_MECH, 0x10, 4, 0), + WCD_MBHC_REGISTER("WCD_MBHC_GND_PLUG_TYPE", + WCD9335_ANA_MBHC_MECH, 0x08, 3, 0), + WCD_MBHC_REGISTER("WCD_MBHC_SW_HPH_LP_100K_TO_GND", + WCD9335_ANA_MBHC_MECH, 0x01, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_ELECT_SCHMT_ISRC", + WCD9335_ANA_MBHC_ELECT, 0x06, 1, 0), + WCD_MBHC_REGISTER("WCD_MBHC_FSM_EN", + WCD9335_ANA_MBHC_ELECT, 0x80, 7, 0), + WCD_MBHC_REGISTER("WCD_MBHC_INSREM_DBNC", + WCD9335_MBHC_PLUG_DETECT_CTL, 0x0F, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_BTN_DBNC", + WCD9335_MBHC_CTL_1, 0x03, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HS_VREF", + WCD9335_MBHC_CTL_2, 0x03, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HS_COMP_RESULT", + WCD9335_ANA_MBHC_RESULT_3, 0x08, 3, 0), + WCD_MBHC_REGISTER("WCD_MBHC_IN2P_CLAMP_STATE", + WCD9335_ANA_MBHC_RESULT_3, 0x10, 4, 0), + WCD_MBHC_REGISTER("WCD_MBHC_MIC_SCHMT_RESULT", + WCD9335_ANA_MBHC_RESULT_3, 0x20, 5, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPHL_SCHMT_RESULT", + WCD9335_ANA_MBHC_RESULT_3, 0x80, 7, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPHR_SCHMT_RESULT", + WCD9335_ANA_MBHC_RESULT_3, 0x40, 6, 0), + WCD_MBHC_REGISTER("WCD_MBHC_OCP_FSM_EN", + WCD9335_HPH_OCP_CTL, 0x10, 4, 0), + WCD_MBHC_REGISTER("WCD_MBHC_BTN_RESULT", + WCD9335_ANA_MBHC_RESULT_3, 0x07, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_BTN_ISRC_CTL", + WCD9335_ANA_MBHC_ELECT, 0x70, 4, 0), + WCD_MBHC_REGISTER("WCD_MBHC_ELECT_RESULT", + WCD9335_ANA_MBHC_RESULT_3, 0xFF, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_MICB_CTRL", + WCD9335_ANA_MICB2, 0xC0, 6, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPH_CNP_WG_TIME", + WCD9335_HPH_CNP_WG_TIME, 0xFF, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPHR_PA_EN", + WCD9335_ANA_HPH, 0x40, 6, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPHL_PA_EN", + WCD9335_ANA_HPH, 0x80, 7, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPH_PA_EN", + WCD9335_ANA_HPH, 0xC0, 6, 0), + WCD_MBHC_REGISTER("WCD_MBHC_SWCH_LEVEL_REMOVE", + WCD9335_ANA_MBHC_RESULT_3, 0x10, 4, 0), + WCD_MBHC_REGISTER("WCD_MBHC_PULLDOWN_CTRL", + 0, 0, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_ANC_DET_EN", + WCD9335_ANA_MBHC_ZDET, 0x01, 0, 0), + /* + * MBHC FSM status register is only available in Tasha 2.0. + * So, init with 0 later once the version is known, then values + * will be updated. + */ + WCD_MBHC_REGISTER("WCD_MBHC_FSM_STATUS", + 0, 0, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_MUX_CTL", + WCD9335_MBHC_CTL_2, 0x70, 4, 0), + WCD_MBHC_REGISTER("WCD_MBHC_MOISTURE_STATUS", + WCD9335_MBHC_FSM_STATUS, 0X20, 5, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPHR_GND", + WCD9335_HPH_PA_CTL2, 0x40, 6, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPHL_GND", + WCD9335_HPH_PA_CTL2, 0x10, 4, 0), +}; + +static const struct wcd_mbhc_intr intr_ids = { + .mbhc_sw_intr = WCD9335_IRQ_MBHC_SW_DET, + .mbhc_btn_press_intr = WCD9335_IRQ_MBHC_BUTTON_PRESS_DET, + .mbhc_btn_release_intr = WCD9335_IRQ_MBHC_BUTTON_RELEASE_DET, + .mbhc_hs_ins_intr = WCD9335_IRQ_MBHC_ELECT_INS_REM_LEG_DET, + .mbhc_hs_rem_intr = WCD9335_IRQ_MBHC_ELECT_INS_REM_DET, + .hph_left_ocp = WCD9335_IRQ_HPH_PA_OCPL_FAULT, + .hph_right_ocp = WCD9335_IRQ_HPH_PA_OCPR_FAULT, +}; + +struct wcd_vbat { + bool is_enabled; + bool adc_config; + /* Variables to cache Vbat ADC output values */ + u16 dcp1; + u16 dcp2; +}; + +struct hpf_work { + struct tasha_priv *tasha; + u8 decimator; + u8 hpf_cut_off_freq; + struct delayed_work dwork; +}; + +#define WCD9335_SPK_ANC_EN_DELAY_MS 350 +static int spk_anc_en_delay = WCD9335_SPK_ANC_EN_DELAY_MS; +module_param(spk_anc_en_delay, int, 0664); +MODULE_PARM_DESC(spk_anc_en_delay, "delay to enable anc in speaker path"); + +struct spk_anc_work { + struct tasha_priv *tasha; + struct delayed_work dwork; +}; + +struct tx_mute_work { + struct tasha_priv *tasha; + u8 decimator; + struct delayed_work dwork; +}; + +struct tasha_priv { + struct device *dev; + struct wcd9xxx *wcd9xxx; + + struct snd_soc_codec *codec; + u32 adc_count; + u32 rx_bias_count; + s32 dmic_0_1_clk_cnt; + s32 dmic_2_3_clk_cnt; + s32 dmic_4_5_clk_cnt; + s32 ldo_h_users; + s32 micb_ref[TASHA_MAX_MICBIAS]; + s32 pullup_ref[TASHA_MAX_MICBIAS]; + + u32 anc_slot; + bool anc_func; + bool is_wsa_attach; + + /* Vbat module */ + struct wcd_vbat vbat; + + /* cal info for codec */ + struct fw_info *fw_data; + + /*track tasha interface type*/ + u8 intf_type; + + /* num of slim ports required */ + struct wcd9xxx_codec_dai_data dai[NUM_CODEC_DAIS]; + + /* SoundWire data structure */ + struct tasha_swr_ctrl_data *swr_ctrl_data; + int nr; + + /*compander*/ + int comp_enabled[COMPANDER_MAX]; + + /* Maintain the status of AUX PGA */ + int aux_pga_cnt; + u8 aux_l_gain; + u8 aux_r_gain; + + bool spkr_pa_widget_on; + struct regulator *spkdrv_reg; + struct regulator *spkdrv2_reg; + + bool mbhc_started; + /* class h specific data */ + struct wcd_clsh_cdc_data clsh_d; + + struct afe_param_cdc_slimbus_slave_cfg slimbus_slave_cfg; + + /* + * list used to save/restore registers at start and + * end of impedance measurement + */ + struct list_head reg_save_restore; + + /* handle to cpe core */ + struct wcd_cpe_core *cpe_core; + u32 current_cpe_clk_freq; + enum tasha_sido_voltage sido_voltage; + int sido_ccl_cnt; + + u32 ana_rx_supplies; + /* Multiplication factor used for impedance detection */ + int zdet_gain_mul_fact; + + /* to track the status */ + unsigned long status_mask; + + struct work_struct tasha_add_child_devices_work; + struct wcd_swr_ctrl_platform_data swr_plat_data; + + /* Port values for Rx and Tx codec_dai */ + unsigned int rx_port_value[TASHA_RX_MAX]; + unsigned int tx_port_value; + + unsigned int vi_feed_value; + /* Tasha Interpolator Mode Select for EAR, HPH_L and HPH_R */ + u32 hph_mode; + + u16 prim_int_users[TASHA_NUM_INTERPOLATORS]; + int spl_src_users[SPLINE_SRC_MAX]; + + struct wcd9xxx_resmgr_v2 *resmgr; + struct delayed_work power_gate_work; + struct mutex power_lock; + struct mutex sido_lock; + + /* mbhc module */ + struct wcd_mbhc mbhc; + struct blocking_notifier_head notifier; + struct mutex micb_lock; + + struct clk *wcd_ext_clk; + struct clk *wcd_native_clk; + struct mutex swr_read_lock; + struct mutex swr_write_lock; + struct mutex swr_clk_lock; + int swr_clk_users; + int native_clk_users; + int (*zdet_gpio_cb)(struct snd_soc_codec *codec, bool high); + + struct snd_info_entry *entry; + struct snd_info_entry *version_entry; + int power_active_ref; + + struct on_demand_supply on_demand_list[ON_DEMAND_SUPPLIES_MAX]; + + int (*machine_codec_event_cb)(struct snd_soc_codec *codec, + enum wcd9335_codec_event); + int spkr_gain_offset; + int spkr_mode; + int ear_spkr_gain; + struct hpf_work tx_hpf_work[TASHA_NUM_DECIMATORS]; + struct tx_mute_work tx_mute_dwork[TASHA_NUM_DECIMATORS]; + struct spk_anc_work spk_anc_dwork; + struct mutex codec_mutex; + int hph_l_gain; + int hph_r_gain; + int rx_7_count; + int rx_8_count; + bool clk_mode; + bool clk_internal; + /* Lock to prevent multiple functions voting at same time */ + struct mutex sb_clk_gear_lock; + /* Count for functions voting or un-voting */ + u32 ref_count; + /* Lock to protect mclk enablement */ + struct mutex mclk_lock; + + struct platform_device *pdev_child_devices + [WCD9335_CHILD_DEVICES_MAX]; + int child_count; +}; + +static int tasha_codec_vote_max_bw(struct snd_soc_codec *codec, + bool vote); + +static const struct tasha_reg_mask_val tasha_spkr_default[] = { + {WCD9335_CDC_COMPANDER7_CTL3, 0x80, 0x80}, + {WCD9335_CDC_COMPANDER8_CTL3, 0x80, 0x80}, + {WCD9335_CDC_COMPANDER7_CTL7, 0x01, 0x01}, + {WCD9335_CDC_COMPANDER8_CTL7, 0x01, 0x01}, + {WCD9335_CDC_BOOST0_BOOST_CTL, 0x7C, 0x58}, + {WCD9335_CDC_BOOST1_BOOST_CTL, 0x7C, 0x58}, +}; + +static const struct tasha_reg_mask_val tasha_spkr_mode1[] = { + {WCD9335_CDC_COMPANDER7_CTL3, 0x80, 0x00}, + {WCD9335_CDC_COMPANDER8_CTL3, 0x80, 0x00}, + {WCD9335_CDC_COMPANDER7_CTL7, 0x01, 0x00}, + {WCD9335_CDC_COMPANDER8_CTL7, 0x01, 0x00}, + {WCD9335_CDC_BOOST0_BOOST_CTL, 0x7C, 0x44}, + {WCD9335_CDC_BOOST1_BOOST_CTL, 0x7C, 0x44}, +}; + +/** + * tasha_set_spkr_gain_offset - offset the speaker path + * gain with the given offset value. + * + * @codec: codec instance + * @offset: Indicates speaker path gain offset value. + * + * Returns 0 on success or -EINVAL on error. + */ +int tasha_set_spkr_gain_offset(struct snd_soc_codec *codec, int offset) +{ + struct tasha_priv *priv = snd_soc_codec_get_drvdata(codec); + + if (!priv) + return -EINVAL; + + priv->spkr_gain_offset = offset; + return 0; +} +EXPORT_SYMBOL(tasha_set_spkr_gain_offset); + +/** + * tasha_set_spkr_mode - Configures speaker compander and smartboost + * settings based on speaker mode. + * + * @codec: codec instance + * @mode: Indicates speaker configuration mode. + * + * Returns 0 on success or -EINVAL on error. + */ +int tasha_set_spkr_mode(struct snd_soc_codec *codec, int mode) +{ + struct tasha_priv *priv = snd_soc_codec_get_drvdata(codec); + int i; + const struct tasha_reg_mask_val *regs; + int size; + + if (!priv) + return -EINVAL; + + switch (mode) { + case SPKR_MODE_1: + regs = tasha_spkr_mode1; + size = ARRAY_SIZE(tasha_spkr_mode1); + break; + default: + regs = tasha_spkr_default; + size = ARRAY_SIZE(tasha_spkr_default); + break; + } + + priv->spkr_mode = mode; + for (i = 0; i < size; i++) + snd_soc_update_bits(codec, regs[i].reg, + regs[i].mask, regs[i].val); + return 0; +} +EXPORT_SYMBOL(tasha_set_spkr_mode); + +static void tasha_enable_sido_buck(struct snd_soc_codec *codec) +{ + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + + snd_soc_update_bits(codec, WCD9335_ANA_RCO, 0x80, 0x80); + snd_soc_update_bits(codec, WCD9335_ANA_BUCK_CTL, 0x02, 0x02); + /* 100us sleep needed after IREF settings */ + usleep_range(100, 110); + snd_soc_update_bits(codec, WCD9335_ANA_BUCK_CTL, 0x04, 0x04); + /* 100us sleep needed after VREF settings */ + usleep_range(100, 110); + tasha->resmgr->sido_input_src = SIDO_SOURCE_RCO_BG; +} + +static void tasha_cdc_sido_ccl_enable(struct tasha_priv *tasha, bool ccl_flag) +{ + struct snd_soc_codec *codec = tasha->codec; + + if (!codec) + return; + + if (!TASHA_IS_2_0(tasha->wcd9xxx)) { + dev_dbg(codec->dev, "%s: tasha version < 2p0, return\n", + __func__); + return; + } + dev_dbg(codec->dev, "%s: sido_ccl_cnt=%d, ccl_flag:%d\n", + __func__, tasha->sido_ccl_cnt, ccl_flag); + if (ccl_flag) { + if (++tasha->sido_ccl_cnt == 1) + snd_soc_update_bits(codec, + WCD9335_SIDO_SIDO_CCL_10, 0xFF, 0x6E); + } else { + if (tasha->sido_ccl_cnt == 0) { + dev_dbg(codec->dev, "%s: sido_ccl already disabled\n", + __func__); + return; + } + if (--tasha->sido_ccl_cnt == 0) + snd_soc_update_bits(codec, + WCD9335_SIDO_SIDO_CCL_10, 0xFF, 0x02); + } +} + +static bool tasha_cdc_is_svs_enabled(struct tasha_priv *tasha) +{ + if (TASHA_IS_2_0(tasha->wcd9xxx) && + svs_scaling_enabled) + return true; + + return false; +} + +static int tasha_cdc_req_mclk_enable(struct tasha_priv *tasha, + bool enable) +{ + int ret = 0; + + mutex_lock(&tasha->mclk_lock); + if (enable) { + tasha_cdc_sido_ccl_enable(tasha, true); + ret = clk_prepare_enable(tasha->wcd_ext_clk); + if (ret) { + dev_err(tasha->dev, "%s: ext clk enable failed\n", + __func__); + goto unlock_mutex; + } + /* get BG */ + wcd_resmgr_enable_master_bias(tasha->resmgr); + /* get MCLK */ + wcd_resmgr_enable_clk_block(tasha->resmgr, WCD_CLK_MCLK); + } else { + /* put MCLK */ + wcd_resmgr_disable_clk_block(tasha->resmgr, WCD_CLK_MCLK); + /* put BG */ + wcd_resmgr_disable_master_bias(tasha->resmgr); + clk_disable_unprepare(tasha->wcd_ext_clk); + tasha_cdc_sido_ccl_enable(tasha, false); + } +unlock_mutex: + mutex_unlock(&tasha->mclk_lock); + return ret; +} + +static int tasha_cdc_check_sido_value(enum tasha_sido_voltage req_mv) +{ + if ((req_mv != SIDO_VOLTAGE_SVS_MV) && + (req_mv != SIDO_VOLTAGE_NOMINAL_MV)) + return -EINVAL; + + return 0; +} + +static void tasha_codec_apply_sido_voltage( + struct tasha_priv *tasha, + enum tasha_sido_voltage req_mv) +{ + u32 vout_d_val; + struct snd_soc_codec *codec = tasha->codec; + int ret; + + if (!codec) + return; + + if (!tasha_cdc_is_svs_enabled(tasha)) + return; + + if ((sido_buck_svs_voltage != SIDO_VOLTAGE_SVS_MV) && + (sido_buck_svs_voltage != SIDO_VOLTAGE_NOMINAL_MV)) + sido_buck_svs_voltage = SIDO_VOLTAGE_SVS_MV; + + ret = tasha_cdc_check_sido_value(req_mv); + if (ret < 0) { + dev_dbg(codec->dev, "%s: requested mv=%d not in range\n", + __func__, req_mv); + return; + } + if (req_mv == tasha->sido_voltage) { + dev_dbg(codec->dev, "%s: Already at requested mv=%d\n", + __func__, req_mv); + return; + } + if (req_mv == sido_buck_svs_voltage) { + if (test_bit(AUDIO_NOMINAL, &tasha->status_mask) || + test_bit(CPE_NOMINAL, &tasha->status_mask)) { + dev_dbg(codec->dev, + "%s: nominal client running, status_mask=%lu\n", + __func__, tasha->status_mask); + return; + } + } + /* compute the vout_d step value */ + vout_d_val = CALCULATE_VOUT_D(req_mv); + snd_soc_write(codec, WCD9335_ANA_BUCK_VOUT_D, vout_d_val & 0xFF); + snd_soc_update_bits(codec, WCD9335_ANA_BUCK_CTL, 0x80, 0x80); + + /* 1 msec sleep required after SIDO Vout_D voltage change */ + usleep_range(1000, 1100); + tasha->sido_voltage = req_mv; + dev_dbg(codec->dev, + "%s: updated SIDO buck Vout_D to %d, vout_d step = %u\n", + __func__, tasha->sido_voltage, vout_d_val); + + snd_soc_update_bits(codec, WCD9335_ANA_BUCK_CTL, + 0x80, 0x00); +} + +static int tasha_codec_update_sido_voltage( + struct tasha_priv *tasha, + enum tasha_sido_voltage req_mv) +{ + int ret = 0; + + if (!tasha_cdc_is_svs_enabled(tasha)) + return ret; + + mutex_lock(&tasha->sido_lock); + /* enable mclk before setting SIDO voltage */ + ret = tasha_cdc_req_mclk_enable(tasha, true); + if (ret) { + dev_err(tasha->dev, "%s: ext clk enable failed\n", + __func__); + goto err; + } + tasha_codec_apply_sido_voltage(tasha, req_mv); + tasha_cdc_req_mclk_enable(tasha, false); + +err: + mutex_unlock(&tasha->sido_lock); + return ret; +} + +int tasha_enable_efuse_sensing(struct snd_soc_codec *codec) +{ + struct tasha_priv *priv = snd_soc_codec_get_drvdata(codec); + + tasha_cdc_mclk_enable(codec, true, false); + + if (!TASHA_IS_2_0(priv->wcd9xxx)) + snd_soc_update_bits(codec, WCD9335_CHIP_TIER_CTRL_EFUSE_CTL, + 0x1E, 0x02); + snd_soc_update_bits(codec, WCD9335_CHIP_TIER_CTRL_EFUSE_CTL, + 0x01, 0x01); + /* + * 5ms sleep required after enabling efuse control + * before checking the status. + */ + usleep_range(5000, 5500); + if (!(snd_soc_read(codec, WCD9335_CHIP_TIER_CTRL_EFUSE_STATUS) & 0x01)) + WARN(1, "%s: Efuse sense is not complete\n", __func__); + + if (TASHA_IS_2_0(priv->wcd9xxx)) { + if (!(snd_soc_read(codec, + WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT0) & 0x40)) + snd_soc_update_bits(codec, WCD9335_HPH_R_ATEST, + 0x04, 0x00); + tasha_enable_sido_buck(codec); + } + + tasha_cdc_mclk_enable(codec, false, false); + + return 0; +} +EXPORT_SYMBOL(tasha_enable_efuse_sensing); + +void *tasha_get_afe_config(struct snd_soc_codec *codec, + enum afe_config_type config_type) +{ + struct tasha_priv *priv = snd_soc_codec_get_drvdata(codec); + + switch (config_type) { + case AFE_SLIMBUS_SLAVE_CONFIG: + return &priv->slimbus_slave_cfg; + case AFE_CDC_REGISTERS_CONFIG: + return &tasha_audio_reg_cfg; + case AFE_SLIMBUS_SLAVE_PORT_CONFIG: + return &tasha_slimbus_slave_port_cfg; + case AFE_AANC_VERSION: + return &tasha_cdc_aanc_version; + case AFE_CLIP_BANK_SEL: + return NULL; + case AFE_CDC_CLIP_REGISTERS_CONFIG: + return NULL; + case AFE_CDC_REGISTER_PAGE_CONFIG: + return &tasha_cdc_reg_page_cfg; + default: + dev_err(codec->dev, "%s: Unknown config_type 0x%x\n", + __func__, config_type); + return NULL; + } +} +EXPORT_SYMBOL(tasha_get_afe_config); + +/* + * tasha_event_register: Registers a machine driver callback + * function with codec private data for post ADSP sub-system + * restart (SSR). This callback function will be called from + * codec driver once codec comes out of reset after ADSP SSR. + * + * @machine_event_cb: callback function from machine driver + * @codec: Codec instance + * + * Return: none + */ +void tasha_event_register( + int (*machine_event_cb)(struct snd_soc_codec *codec, + enum wcd9335_codec_event), + struct snd_soc_codec *codec) +{ + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + + if (tasha) + tasha->machine_codec_event_cb = machine_event_cb; + else + dev_dbg(codec->dev, "%s: Invalid tasha_priv data\n", __func__); +} +EXPORT_SYMBOL(tasha_event_register); + +static int tasha_mbhc_request_irq(struct snd_soc_codec *codec, + int irq, irq_handler_t handler, + const char *name, void *data) +{ + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + struct wcd9xxx *wcd9xxx = tasha->wcd9xxx; + struct wcd9xxx_core_resource *core_res = + &wcd9xxx->core_res; + + return wcd9xxx_request_irq(core_res, irq, handler, name, data); +} + +static void tasha_mbhc_irq_control(struct snd_soc_codec *codec, + int irq, bool enable) +{ + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + struct wcd9xxx *wcd9xxx = tasha->wcd9xxx; + struct wcd9xxx_core_resource *core_res = + &wcd9xxx->core_res; + if (enable) + wcd9xxx_enable_irq(core_res, irq); + else + wcd9xxx_disable_irq(core_res, irq); +} + +static int tasha_mbhc_free_irq(struct snd_soc_codec *codec, + int irq, void *data) +{ + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + struct wcd9xxx *wcd9xxx = tasha->wcd9xxx; + struct wcd9xxx_core_resource *core_res = + &wcd9xxx->core_res; + + wcd9xxx_free_irq(core_res, irq, data); + return 0; +} + +static void tasha_mbhc_clk_setup(struct snd_soc_codec *codec, + bool enable) +{ + if (enable) + snd_soc_update_bits(codec, WCD9335_MBHC_CTL_1, + 0x80, 0x80); + else + snd_soc_update_bits(codec, WCD9335_MBHC_CTL_1, + 0x80, 0x00); +} + +static int tasha_mbhc_btn_to_num(struct snd_soc_codec *codec) +{ + return snd_soc_read(codec, WCD9335_ANA_MBHC_RESULT_3) & 0x7; +} + +static void tasha_mbhc_mbhc_bias_control(struct snd_soc_codec *codec, + bool enable) +{ + if (enable) + snd_soc_update_bits(codec, WCD9335_ANA_MBHC_ELECT, + 0x01, 0x01); + else + snd_soc_update_bits(codec, WCD9335_ANA_MBHC_ELECT, + 0x01, 0x00); +} + +static void tasha_mbhc_program_btn_thr(struct snd_soc_codec *codec, + s16 *btn_low, s16 *btn_high, + int num_btn, bool is_micbias) +{ + int i; + int vth; + + if (num_btn > WCD_MBHC_DEF_BUTTONS) { + dev_err(codec->dev, "%s: invalid number of buttons: %d\n", + __func__, num_btn); + return; + } + /* + * Tasha just needs one set of thresholds for button detection + * due to micbias voltage ramp to pullup upon button press. So + * btn_low and is_micbias are ignored and always program button + * thresholds using btn_high. + */ + for (i = 0; i < num_btn; i++) { + vth = ((btn_high[i] * 2) / 25) & 0x3F; + snd_soc_update_bits(codec, WCD9335_ANA_MBHC_BTN0 + i, + 0xFC, vth << 2); + dev_dbg(codec->dev, "%s: btn_high[%d]: %d, vth: %d\n", + __func__, i, btn_high[i], vth); + } +} + +static bool tasha_mbhc_lock_sleep(struct wcd_mbhc *mbhc, bool lock) +{ + struct snd_soc_codec *codec = mbhc->codec; + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + struct wcd9xxx *wcd9xxx = tasha->wcd9xxx; + struct wcd9xxx_core_resource *core_res = + &wcd9xxx->core_res; + if (lock) + return wcd9xxx_lock_sleep(core_res); + else { + wcd9xxx_unlock_sleep(core_res); + return 0; + } +} + +static int tasha_mbhc_register_notifier(struct wcd_mbhc *mbhc, + struct notifier_block *nblock, + bool enable) +{ + struct snd_soc_codec *codec = mbhc->codec; + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + + if (enable) + return blocking_notifier_chain_register(&tasha->notifier, + nblock); + else + return blocking_notifier_chain_unregister(&tasha->notifier, + nblock); +} + +static bool tasha_mbhc_micb_en_status(struct wcd_mbhc *mbhc, int micb_num) +{ + u8 val; + + if (micb_num == MIC_BIAS_2) { + val = (snd_soc_read(mbhc->codec, WCD9335_ANA_MICB2) >> 6); + if (val == 0x01) + return true; + } + return false; +} + +static bool tasha_mbhc_hph_pa_on_status(struct snd_soc_codec *codec) +{ + return (snd_soc_read(codec, WCD9335_ANA_HPH) & 0xC0) ? true : false; +} + +static void tasha_mbhc_hph_l_pull_up_control(struct snd_soc_codec *codec, + enum mbhc_hs_pullup_iref pull_up_cur) +{ + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + + if (!tasha) + return; + + /* Default pull up current to 2uA */ + if (pull_up_cur < I_OFF || pull_up_cur > I_3P0_UA || + pull_up_cur == I_DEFAULT) + pull_up_cur = I_2P0_UA; + + dev_dbg(codec->dev, "%s: HS pull up current:%d\n", + __func__, pull_up_cur); + + if (TASHA_IS_2_0(tasha->wcd9xxx)) + snd_soc_update_bits(codec, WCD9335_MBHC_PLUG_DETECT_CTL, + 0xC0, pull_up_cur << 6); + else + snd_soc_update_bits(codec, WCD9335_MBHC_PLUG_DETECT_CTL, + 0xC0, 0x40); +} + +static int tasha_enable_ext_mb_source(struct wcd_mbhc *mbhc, + bool turn_on) +{ + struct snd_soc_codec *codec = mbhc->codec; + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + int ret = 0; + struct on_demand_supply *supply; + + if (!tasha) + return -EINVAL; + + supply = &tasha->on_demand_list[ON_DEMAND_MICBIAS]; + if (!supply->supply) { + dev_dbg(codec->dev, "%s: warning supply not present ond for %s\n", + __func__, "onDemand Micbias"); + return ret; + } + + dev_dbg(codec->dev, "%s turn_on: %d count: %d\n", __func__, turn_on, + supply->ondemand_supply_count); + + if (turn_on) { + if (!(supply->ondemand_supply_count)) { + ret = snd_soc_dapm_force_enable_pin( + snd_soc_codec_get_dapm(codec), + "MICBIAS_REGULATOR"); + snd_soc_dapm_sync(snd_soc_codec_get_dapm(codec)); + } + supply->ondemand_supply_count++; + } else { + if (supply->ondemand_supply_count > 0) + supply->ondemand_supply_count--; + if (!(supply->ondemand_supply_count)) { + ret = snd_soc_dapm_disable_pin( + snd_soc_codec_get_dapm(codec), + "MICBIAS_REGULATOR"); + snd_soc_dapm_sync(snd_soc_codec_get_dapm(codec)); + } + } + + if (ret) + dev_err(codec->dev, "%s: Failed to %s external micbias source\n", + __func__, turn_on ? "enable" : "disabled"); + else + dev_dbg(codec->dev, "%s: %s external micbias source\n", + __func__, turn_on ? "Enabled" : "Disabled"); + + return ret; +} + +static int tasha_micbias_control(struct snd_soc_codec *codec, + int micb_num, + int req, bool is_dapm) +{ + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + int micb_index = micb_num - 1; + u16 micb_reg; + int pre_off_event = 0, post_off_event = 0; + int post_on_event = 0, post_dapm_off = 0; + int post_dapm_on = 0; + + if ((micb_index < 0) || (micb_index > TASHA_MAX_MICBIAS - 1)) { + dev_err(codec->dev, "%s: Invalid micbias index, micb_ind:%d\n", + __func__, micb_index); + return -EINVAL; + } + switch (micb_num) { + case MIC_BIAS_1: + micb_reg = WCD9335_ANA_MICB1; + break; + case MIC_BIAS_2: + micb_reg = WCD9335_ANA_MICB2; + pre_off_event = WCD_EVENT_PRE_MICBIAS_2_OFF; + post_off_event = WCD_EVENT_POST_MICBIAS_2_OFF; + post_on_event = WCD_EVENT_POST_MICBIAS_2_ON; + post_dapm_on = WCD_EVENT_POST_DAPM_MICBIAS_2_ON; + post_dapm_off = WCD_EVENT_POST_DAPM_MICBIAS_2_OFF; + break; + case MIC_BIAS_3: + micb_reg = WCD9335_ANA_MICB3; + break; + case MIC_BIAS_4: + micb_reg = WCD9335_ANA_MICB4; + break; + default: + dev_err(codec->dev, "%s: Invalid micbias number: %d\n", + __func__, micb_num); + return -EINVAL; + } + mutex_lock(&tasha->micb_lock); + + switch (req) { + case MICB_PULLUP_ENABLE: + tasha->pullup_ref[micb_index]++; + if ((tasha->pullup_ref[micb_index] == 1) && + (tasha->micb_ref[micb_index] == 0)) + snd_soc_update_bits(codec, micb_reg, 0xC0, 0x80); + break; + case MICB_PULLUP_DISABLE: + if (tasha->pullup_ref[micb_index] > 0) + tasha->pullup_ref[micb_index]--; + if ((tasha->pullup_ref[micb_index] == 0) && + (tasha->micb_ref[micb_index] == 0)) + snd_soc_update_bits(codec, micb_reg, 0xC0, 0x00); + break; + case MICB_ENABLE: + tasha->micb_ref[micb_index]++; + if (tasha->micb_ref[micb_index] == 1) { + snd_soc_update_bits(codec, micb_reg, 0xC0, 0x40); + if (post_on_event) + blocking_notifier_call_chain(&tasha->notifier, + post_on_event, &tasha->mbhc); + } + if (is_dapm && post_dapm_on) + blocking_notifier_call_chain(&tasha->notifier, + post_dapm_on, &tasha->mbhc); + break; + case MICB_DISABLE: + if (tasha->micb_ref[micb_index] > 0) + tasha->micb_ref[micb_index]--; + if ((tasha->micb_ref[micb_index] == 0) && + (tasha->pullup_ref[micb_index] > 0)) + snd_soc_update_bits(codec, micb_reg, 0xC0, 0x80); + else if ((tasha->micb_ref[micb_index] == 0) && + (tasha->pullup_ref[micb_index] == 0)) { + if (pre_off_event) + blocking_notifier_call_chain(&tasha->notifier, + pre_off_event, &tasha->mbhc); + snd_soc_update_bits(codec, micb_reg, 0xC0, 0x00); + if (post_off_event) + blocking_notifier_call_chain(&tasha->notifier, + post_off_event, &tasha->mbhc); + } + if (is_dapm && post_dapm_off) + blocking_notifier_call_chain(&tasha->notifier, + post_dapm_off, &tasha->mbhc); + break; + }; + + dev_dbg(codec->dev, "%s: micb_num:%d, micb_ref: %d, pullup_ref: %d\n", + __func__, micb_num, tasha->micb_ref[micb_index], + tasha->pullup_ref[micb_index]); + + mutex_unlock(&tasha->micb_lock); + + return 0; +} + +static int tasha_mbhc_request_micbias(struct snd_soc_codec *codec, + int micb_num, int req) +{ + int ret; + + /* + * If micbias is requested, make sure that there + * is vote to enable mclk + */ + if (req == MICB_ENABLE) + tasha_cdc_mclk_enable(codec, true, false); + + ret = tasha_micbias_control(codec, micb_num, req, false); + + /* + * Release vote for mclk while requesting for + * micbias disable + */ + if (req == MICB_DISABLE) + tasha_cdc_mclk_enable(codec, false, false); + + return ret; +} + +static void tasha_mbhc_micb_ramp_control(struct snd_soc_codec *codec, + bool enable) +{ + if (enable) { + snd_soc_update_bits(codec, WCD9335_ANA_MICB2_RAMP, + 0x1C, 0x0C); + snd_soc_update_bits(codec, WCD9335_ANA_MICB2_RAMP, + 0x80, 0x80); + } else { + snd_soc_update_bits(codec, WCD9335_ANA_MICB2_RAMP, + 0x80, 0x00); + snd_soc_update_bits(codec, WCD9335_ANA_MICB2_RAMP, + 0x1C, 0x00); + } +} + +static struct firmware_cal *tasha_get_hwdep_fw_cal(struct wcd_mbhc *mbhc, + enum wcd_cal_type type) +{ + struct tasha_priv *tasha; + struct firmware_cal *hwdep_cal; + struct snd_soc_codec *codec = mbhc->codec; + + if (!codec) { + pr_err("%s: NULL codec pointer\n", __func__); + return NULL; + } + tasha = snd_soc_codec_get_drvdata(codec); + hwdep_cal = wcdcal_get_fw_cal(tasha->fw_data, type); + if (!hwdep_cal) + dev_err(codec->dev, "%s: cal not sent by %d\n", + __func__, type); + + return hwdep_cal; +} + +static int tasha_mbhc_micb_adjust_voltage(struct snd_soc_codec *codec, + int req_volt, + int micb_num) +{ + int cur_vout_ctl, req_vout_ctl; + int micb_reg, micb_val, micb_en; + + switch (micb_num) { + case MIC_BIAS_1: + micb_reg = WCD9335_ANA_MICB1; + break; + case MIC_BIAS_2: + micb_reg = WCD9335_ANA_MICB2; + break; + case MIC_BIAS_3: + micb_reg = WCD9335_ANA_MICB3; + break; + case MIC_BIAS_4: + micb_reg = WCD9335_ANA_MICB4; + break; + default: + return -EINVAL; + } + + /* + * If requested micbias voltage is same as current micbias + * voltage, then just return. Otherwise, adjust voltage as + * per requested value. If micbias is already enabled, then + * to avoid slow micbias ramp-up or down enable pull-up + * momentarily, change the micbias value and then re-enable + * micbias. + */ + micb_val = snd_soc_read(codec, micb_reg); + micb_en = (micb_val & 0xC0) >> 6; + cur_vout_ctl = micb_val & 0x3F; + + req_vout_ctl = wcd9335_get_micb_vout_ctl_val(req_volt); + if (req_vout_ctl < 0) + return -EINVAL; + if (cur_vout_ctl == req_vout_ctl) + return 0; + + dev_dbg(codec->dev, "%s: micb_num: %d, cur_mv: %d, req_mv: %d, micb_en: %d\n", + __func__, micb_num, WCD_VOUT_CTL_TO_MICB(cur_vout_ctl), + req_volt, micb_en); + + if (micb_en == 0x1) + snd_soc_update_bits(codec, micb_reg, 0xC0, 0x80); + + snd_soc_update_bits(codec, micb_reg, 0x3F, req_vout_ctl); + + if (micb_en == 0x1) { + snd_soc_update_bits(codec, micb_reg, 0xC0, 0x40); + /* + * Add 2ms delay as per HW requirement after enabling + * micbias + */ + usleep_range(2000, 2100); + } + + return 0; +} + +static int tasha_mbhc_micb_ctrl_threshold_mic(struct snd_soc_codec *codec, + int micb_num, bool req_en) +{ + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + struct wcd9xxx_pdata *pdata = dev_get_platdata(codec->dev->parent); + int rc, micb_mv; + + if (micb_num != MIC_BIAS_2) + return -EINVAL; + + /* + * If device tree micbias level is already above the minimum + * voltage needed to detect threshold microphone, then do + * not change the micbias, just return. + */ + if (pdata->micbias.micb2_mv >= WCD_MBHC_THR_HS_MICB_MV) + return 0; + + micb_mv = req_en ? WCD_MBHC_THR_HS_MICB_MV : pdata->micbias.micb2_mv; + + mutex_lock(&tasha->micb_lock); + rc = tasha_mbhc_micb_adjust_voltage(codec, micb_mv, MIC_BIAS_2); + mutex_unlock(&tasha->micb_lock); + + return rc; +} + +static inline void tasha_mbhc_get_result_params(struct wcd9xxx *wcd9xxx, + s16 *d1_a, u16 noff, + int32_t *zdet) +{ + int i; + int val, val1; + s16 c1; + s32 x1, d1; + int32_t denom; + int minCode_param[] = { + 3277, 1639, 820, 410, 205, 103, 52, 26 + }; + + regmap_update_bits(wcd9xxx->regmap, WCD9335_ANA_MBHC_ZDET, 0x20, 0x20); + for (i = 0; i < TASHA_ZDET_NUM_MEASUREMENTS; i++) { + regmap_read(wcd9xxx->regmap, WCD9335_ANA_MBHC_RESULT_2, &val); + if (val & 0x80) + break; + } + val = val << 0x8; + regmap_read(wcd9xxx->regmap, WCD9335_ANA_MBHC_RESULT_1, &val1); + val |= val1; + regmap_update_bits(wcd9xxx->regmap, WCD9335_ANA_MBHC_ZDET, 0x20, 0x00); + x1 = TASHA_MBHC_GET_X1(val); + c1 = TASHA_MBHC_GET_C1(val); + /* If ramp is not complete, give additional 5ms */ + if ((c1 < 2) && x1) + usleep_range(5000, 5050); + + if (!c1 || !x1) { + dev_dbg(wcd9xxx->dev, + "%s: Impedance detect ramp error, c1=%d, x1=0x%x\n", + __func__, c1, x1); + goto ramp_down; + } + d1 = d1_a[c1]; + denom = (x1 * d1) - (1 << (14 - noff)); + if (denom > 0) + *zdet = (TASHA_MBHC_ZDET_CONST * 1000) / denom; + else if (x1 < minCode_param[noff]) + *zdet = TASHA_ZDET_FLOATING_IMPEDANCE; + + dev_dbg(wcd9xxx->dev, "%s: d1=%d, c1=%d, x1=0x%x, z_val=%d(milliOhm)\n", + __func__, d1, c1, x1, *zdet); +ramp_down: + i = 0; + while (x1) { + regmap_bulk_read(wcd9xxx->regmap, + WCD9335_ANA_MBHC_RESULT_1, (u8 *)&val, 2); + x1 = TASHA_MBHC_GET_X1(val); + i++; + if (i == TASHA_ZDET_NUM_MEASUREMENTS) + break; + } +} + +/* + * tasha_mbhc_zdet_gpio_ctrl: Register callback function for + * controlling the switch on hifi amps. Default switch state + * will put a 51ohm load in parallel to the hph load. So, + * impedance detection function will pull the gpio high + * to make the switch open. + * + * @zdet_gpio_cb: callback function from machine driver + * @codec: Codec instance + * + * Return: none + */ +void tasha_mbhc_zdet_gpio_ctrl( + int (*zdet_gpio_cb)(struct snd_soc_codec *codec, bool high), + struct snd_soc_codec *codec) +{ + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + + tasha->zdet_gpio_cb = zdet_gpio_cb; +} +EXPORT_SYMBOL(tasha_mbhc_zdet_gpio_ctrl); + +static void tasha_mbhc_zdet_ramp(struct snd_soc_codec *codec, + struct tasha_mbhc_zdet_param *zdet_param, + int32_t *zl, int32_t *zr, s16 *d1_a) +{ + struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent); + int32_t zdet = 0; + + snd_soc_update_bits(codec, WCD9335_MBHC_ZDET_ANA_CTL, 0x70, + zdet_param->ldo_ctl << 4); + snd_soc_update_bits(codec, WCD9335_ANA_MBHC_BTN5, 0xFC, + zdet_param->btn5); + snd_soc_update_bits(codec, WCD9335_ANA_MBHC_BTN6, 0xFC, + zdet_param->btn6); + snd_soc_update_bits(codec, WCD9335_ANA_MBHC_BTN7, 0xFC, + zdet_param->btn7); + snd_soc_update_bits(codec, WCD9335_MBHC_ZDET_ANA_CTL, 0x0F, + zdet_param->noff); + snd_soc_update_bits(codec, WCD9335_MBHC_ZDET_RAMP_CTL, 0x0F, + zdet_param->nshift); + + if (!zl) + goto z_right; + /* Start impedance measurement for HPH_L */ + regmap_update_bits(wcd9xxx->regmap, + WCD9335_ANA_MBHC_ZDET, 0x80, 0x80); + dev_dbg(wcd9xxx->dev, "%s: ramp for HPH_L, noff = %d\n", + __func__, zdet_param->noff); + tasha_mbhc_get_result_params(wcd9xxx, d1_a, zdet_param->noff, &zdet); + regmap_update_bits(wcd9xxx->regmap, + WCD9335_ANA_MBHC_ZDET, 0x80, 0x00); + + *zl = zdet; + +z_right: + if (!zr) + return; + /* Start impedance measurement for HPH_R */ + regmap_update_bits(wcd9xxx->regmap, + WCD9335_ANA_MBHC_ZDET, 0x40, 0x40); + dev_dbg(wcd9xxx->dev, "%s: ramp for HPH_R, noff = %d\n", + __func__, zdet_param->noff); + tasha_mbhc_get_result_params(wcd9xxx, d1_a, zdet_param->noff, &zdet); + regmap_update_bits(wcd9xxx->regmap, + WCD9335_ANA_MBHC_ZDET, 0x40, 0x00); + + *zr = zdet; +} + +static inline void tasha_wcd_mbhc_qfuse_cal(struct snd_soc_codec *codec, + int32_t *z_val, int flag_l_r) +{ + s16 q1; + int q1_cal; + + if (*z_val < (TASHA_ZDET_VAL_400/1000)) + q1 = snd_soc_read(codec, + WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT1 + (2 * flag_l_r)); + else + q1 = snd_soc_read(codec, + WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT2 + (2 * flag_l_r)); + if (q1 & 0x80) + q1_cal = (10000 - ((q1 & 0x7F) * 25)); + else + q1_cal = (10000 + (q1 * 25)); + if (q1_cal > 0) + *z_val = ((*z_val) * 10000) / q1_cal; +} + +static void tasha_wcd_mbhc_calc_impedance(struct wcd_mbhc *mbhc, uint32_t *zl, + uint32_t *zr) +{ + struct snd_soc_codec *codec = mbhc->codec; + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + struct wcd9xxx *wcd9xxx = tasha->wcd9xxx; + s16 reg0, reg1, reg2, reg3, reg4; + int32_t z1L, z1R, z1Ls; + int zMono, z_diff1, z_diff2; + bool is_fsm_disable = false; + bool is_change = false; + struct tasha_mbhc_zdet_param zdet_param[] = { + {4, 0, 4, 0x08, 0x14, 0x18}, /* < 32ohm */ + {2, 0, 3, 0x18, 0x7C, 0x90}, /* 32ohm < Z < 400ohm */ + {1, 4, 5, 0x18, 0x7C, 0x90}, /* 400ohm < Z < 1200ohm */ + {1, 6, 7, 0x18, 0x7C, 0x90}, /* >1200ohm */ + }; + struct tasha_mbhc_zdet_param *zdet_param_ptr = NULL; + s16 d1_a[][4] = { + {0, 30, 90, 30}, + {0, 30, 30, 5}, + {0, 30, 30, 5}, + {0, 30, 30, 5}, + }; + s16 *d1 = NULL; + + if (!TASHA_IS_2_0(wcd9xxx)) { + dev_dbg(codec->dev, "%s: Z-det is not supported for this codec version\n", + __func__); + *zl = 0; + *zr = 0; + return; + } + WCD_MBHC_RSC_ASSERT_LOCKED(mbhc); + + if (tasha->zdet_gpio_cb) + is_change = tasha->zdet_gpio_cb(codec, true); + + reg0 = snd_soc_read(codec, WCD9335_ANA_MBHC_BTN5); + reg1 = snd_soc_read(codec, WCD9335_ANA_MBHC_BTN6); + reg2 = snd_soc_read(codec, WCD9335_ANA_MBHC_BTN7); + reg3 = snd_soc_read(codec, WCD9335_MBHC_CTL_1); + reg4 = snd_soc_read(codec, WCD9335_MBHC_ZDET_ANA_CTL); + + if (snd_soc_read(codec, WCD9335_ANA_MBHC_ELECT) & 0x80) { + is_fsm_disable = true; + regmap_update_bits(wcd9xxx->regmap, + WCD9335_ANA_MBHC_ELECT, 0x80, 0x00); + } + + /* For NO-jack, disable L_DET_EN before Z-det measurements */ + if (mbhc->hphl_swh) + regmap_update_bits(wcd9xxx->regmap, + WCD9335_ANA_MBHC_MECH, 0x80, 0x00); + + /* Enable AZ */ + snd_soc_update_bits(codec, WCD9335_MBHC_CTL_1, 0x0C, 0x04); + /* Turn off 100k pull down on HPHL */ + regmap_update_bits(wcd9xxx->regmap, + WCD9335_ANA_MBHC_MECH, 0x01, 0x00); + + /* First get impedance on Left */ + d1 = d1_a[1]; + zdet_param_ptr = &zdet_param[1]; + tasha_mbhc_zdet_ramp(codec, zdet_param_ptr, &z1L, NULL, d1); + + if (!TASHA_MBHC_IS_SECOND_RAMP_REQUIRED(z1L)) + goto left_ch_impedance; + + /* second ramp for left ch */ + if (z1L < TASHA_ZDET_VAL_32) { + zdet_param_ptr = &zdet_param[0]; + d1 = d1_a[0]; + } else if ((z1L > TASHA_ZDET_VAL_400) && (z1L <= TASHA_ZDET_VAL_1200)) { + zdet_param_ptr = &zdet_param[2]; + d1 = d1_a[2]; + } else if (z1L > TASHA_ZDET_VAL_1200) { + zdet_param_ptr = &zdet_param[3]; + d1 = d1_a[3]; + } + tasha_mbhc_zdet_ramp(codec, zdet_param_ptr, &z1L, NULL, d1); + +left_ch_impedance: + if ((z1L == TASHA_ZDET_FLOATING_IMPEDANCE) || + (z1L > TASHA_ZDET_VAL_100K)) { + *zl = TASHA_ZDET_FLOATING_IMPEDANCE; + zdet_param_ptr = &zdet_param[1]; + d1 = d1_a[1]; + } else { + *zl = z1L/1000; + tasha_wcd_mbhc_qfuse_cal(codec, zl, 0); + } + dev_dbg(codec->dev, "%s: impedance on HPH_L = %d(ohms)\n", + __func__, *zl); + + /* start of right impedance ramp and calculation */ + tasha_mbhc_zdet_ramp(codec, zdet_param_ptr, NULL, &z1R, d1); + if (TASHA_MBHC_IS_SECOND_RAMP_REQUIRED(z1R)) { + if (((z1R > TASHA_ZDET_VAL_1200) && + (zdet_param_ptr->noff == 0x6)) || + ((*zl) != TASHA_ZDET_FLOATING_IMPEDANCE)) + goto right_ch_impedance; + /* second ramp for right ch */ + if (z1R < TASHA_ZDET_VAL_32) { + zdet_param_ptr = &zdet_param[0]; + d1 = d1_a[0]; + } else if ((z1R > TASHA_ZDET_VAL_400) && + (z1R <= TASHA_ZDET_VAL_1200)) { + zdet_param_ptr = &zdet_param[2]; + d1 = d1_a[2]; + } else if (z1R > TASHA_ZDET_VAL_1200) { + zdet_param_ptr = &zdet_param[3]; + d1 = d1_a[3]; + } + tasha_mbhc_zdet_ramp(codec, zdet_param_ptr, NULL, &z1R, d1); + } +right_ch_impedance: + if ((z1R == TASHA_ZDET_FLOATING_IMPEDANCE) || + (z1R > TASHA_ZDET_VAL_100K)) { + *zr = TASHA_ZDET_FLOATING_IMPEDANCE; + } else { + *zr = z1R/1000; + tasha_wcd_mbhc_qfuse_cal(codec, zr, 1); + } + dev_dbg(codec->dev, "%s: impedance on HPH_R = %d(ohms)\n", + __func__, *zr); + + /* mono/stereo detection */ + if ((*zl == TASHA_ZDET_FLOATING_IMPEDANCE) && + (*zr == TASHA_ZDET_FLOATING_IMPEDANCE)) { + dev_dbg(codec->dev, + "%s: plug type is invalid or extension cable\n", + __func__); + goto zdet_complete; + } + if ((*zl == TASHA_ZDET_FLOATING_IMPEDANCE) || + (*zr == TASHA_ZDET_FLOATING_IMPEDANCE) || + ((*zl < WCD_MONO_HS_MIN_THR) && (*zr > WCD_MONO_HS_MIN_THR)) || + ((*zl > WCD_MONO_HS_MIN_THR) && (*zr < WCD_MONO_HS_MIN_THR))) { + dev_dbg(codec->dev, + "%s: Mono plug type with one ch floating or shorted to GND\n", + __func__); + mbhc->hph_type = WCD_MBHC_HPH_MONO; + goto zdet_complete; + } + snd_soc_update_bits(codec, WCD9335_HPH_R_ATEST, 0x02, 0x02); + snd_soc_update_bits(codec, WCD9335_HPH_PA_CTL2, 0x40, 0x01); + if (*zl < (TASHA_ZDET_VAL_32/1000)) + tasha_mbhc_zdet_ramp(codec, &zdet_param[0], &z1Ls, NULL, d1); + else + tasha_mbhc_zdet_ramp(codec, &zdet_param[1], &z1Ls, NULL, d1); + snd_soc_update_bits(codec, WCD9335_HPH_PA_CTL2, 0x40, 0x00); + snd_soc_update_bits(codec, WCD9335_HPH_R_ATEST, 0x02, 0x00); + z1Ls /= 1000; + tasha_wcd_mbhc_qfuse_cal(codec, &z1Ls, 0); + /* parallel of left Z and 9 ohm pull down resistor */ + zMono = ((*zl) * 9) / ((*zl) + 9); + z_diff1 = (z1Ls > zMono) ? (z1Ls - zMono) : (zMono - z1Ls); + z_diff2 = ((*zl) > z1Ls) ? ((*zl) - z1Ls) : (z1Ls - (*zl)); + if ((z_diff1 * (*zl + z1Ls)) > (z_diff2 * (z1Ls + zMono))) { + dev_dbg(codec->dev, "%s: stereo plug type detected\n", + __func__); + mbhc->hph_type = WCD_MBHC_HPH_STEREO; + } else { + dev_dbg(codec->dev, "%s: MONO plug type detected\n", + __func__); + mbhc->hph_type = WCD_MBHC_HPH_MONO; + } + +zdet_complete: + snd_soc_write(codec, WCD9335_ANA_MBHC_BTN5, reg0); + snd_soc_write(codec, WCD9335_ANA_MBHC_BTN6, reg1); + snd_soc_write(codec, WCD9335_ANA_MBHC_BTN7, reg2); + /* Turn on 100k pull down on HPHL */ + regmap_update_bits(wcd9xxx->regmap, + WCD9335_ANA_MBHC_MECH, 0x01, 0x01); + + /* For NO-jack, re-enable L_DET_EN after Z-det measurements */ + if (mbhc->hphl_swh) + regmap_update_bits(wcd9xxx->regmap, + WCD9335_ANA_MBHC_MECH, 0x80, 0x80); + + snd_soc_write(codec, WCD9335_MBHC_ZDET_ANA_CTL, reg4); + snd_soc_write(codec, WCD9335_MBHC_CTL_1, reg3); + if (is_fsm_disable) + regmap_update_bits(wcd9xxx->regmap, + WCD9335_ANA_MBHC_ELECT, 0x80, 0x80); + if (tasha->zdet_gpio_cb && is_change) + tasha->zdet_gpio_cb(codec, false); +} + +static void tasha_mbhc_gnd_det_ctrl(struct snd_soc_codec *codec, bool enable) +{ + if (enable) { + snd_soc_update_bits(codec, WCD9335_ANA_MBHC_MECH, + 0x02, 0x02); + snd_soc_update_bits(codec, WCD9335_ANA_MBHC_MECH, + 0x40, 0x40); + } else { + snd_soc_update_bits(codec, WCD9335_ANA_MBHC_MECH, + 0x40, 0x00); + snd_soc_update_bits(codec, WCD9335_ANA_MBHC_MECH, + 0x02, 0x00); + } +} + +static void tasha_mbhc_hph_pull_down_ctrl(struct snd_soc_codec *codec, + bool enable) +{ + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + + if (enable) { + snd_soc_update_bits(codec, WCD9335_HPH_PA_CTL2, + 0x40, 0x40); + if (TASHA_IS_2_0(tasha->wcd9xxx)) + snd_soc_update_bits(codec, WCD9335_HPH_PA_CTL2, + 0x10, 0x10); + } else { + snd_soc_update_bits(codec, WCD9335_HPH_PA_CTL2, + 0x40, 0x00); + if (TASHA_IS_2_0(tasha->wcd9xxx)) + snd_soc_update_bits(codec, WCD9335_HPH_PA_CTL2, + 0x10, 0x00); + } +} + +static void tasha_mbhc_moisture_config(struct wcd_mbhc *mbhc) +{ + struct snd_soc_codec *codec = mbhc->codec; + + if (mbhc->moist_vref == V_OFF) + return; + + /* Donot enable moisture detection if jack type is NC */ + if (!mbhc->hphl_swh) { + dev_dbg(codec->dev, "%s: disable moisture detection for NC\n", + __func__); + return; + } + + snd_soc_update_bits(codec, WCD9335_MBHC_CTL_2, + 0x0C, mbhc->moist_vref << 2); + tasha_mbhc_hph_l_pull_up_control(codec, mbhc->moist_iref); +} + +static void tasha_update_anc_state(struct snd_soc_codec *codec, bool enable, + int anc_num) +{ + if (enable) + snd_soc_update_bits(codec, WCD9335_CDC_RX1_RX_PATH_CFG0 + + (20 * anc_num), 0x10, 0x10); + else + snd_soc_update_bits(codec, WCD9335_CDC_RX1_RX_PATH_CFG0 + + (20 * anc_num), 0x10, 0x00); +} + +static bool tasha_is_anc_on(struct wcd_mbhc *mbhc) +{ + bool anc_on = false; + u16 ancl, ancr; + + ancl = + (snd_soc_read(mbhc->codec, WCD9335_CDC_RX1_RX_PATH_CFG0)) & 0x10; + ancr = + (snd_soc_read(mbhc->codec, WCD9335_CDC_RX2_RX_PATH_CFG0)) & 0x10; + + anc_on = !!(ancl | ancr); + + return anc_on; +} + +static const struct wcd_mbhc_cb mbhc_cb = { + .request_irq = tasha_mbhc_request_irq, + .irq_control = tasha_mbhc_irq_control, + .free_irq = tasha_mbhc_free_irq, + .clk_setup = tasha_mbhc_clk_setup, + .map_btn_code_to_num = tasha_mbhc_btn_to_num, + .enable_mb_source = tasha_enable_ext_mb_source, + .mbhc_bias = tasha_mbhc_mbhc_bias_control, + .set_btn_thr = tasha_mbhc_program_btn_thr, + .lock_sleep = tasha_mbhc_lock_sleep, + .register_notifier = tasha_mbhc_register_notifier, + .micbias_enable_status = tasha_mbhc_micb_en_status, + .hph_pa_on_status = tasha_mbhc_hph_pa_on_status, + .hph_pull_up_control = tasha_mbhc_hph_l_pull_up_control, + .mbhc_micbias_control = tasha_mbhc_request_micbias, + .mbhc_micb_ramp_control = tasha_mbhc_micb_ramp_control, + .get_hwdep_fw_cal = tasha_get_hwdep_fw_cal, + .mbhc_micb_ctrl_thr_mic = tasha_mbhc_micb_ctrl_threshold_mic, + .compute_impedance = tasha_wcd_mbhc_calc_impedance, + .mbhc_gnd_det_ctrl = tasha_mbhc_gnd_det_ctrl, + .hph_pull_down_ctrl = tasha_mbhc_hph_pull_down_ctrl, + .mbhc_moisture_config = tasha_mbhc_moisture_config, + .update_anc_state = tasha_update_anc_state, + .is_anc_on = tasha_is_anc_on, +}; + +static int tasha_get_anc_slot(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = tasha->anc_slot; + return 0; +} + +static int tasha_put_anc_slot(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + + tasha->anc_slot = ucontrol->value.integer.value[0]; + return 0; +} + +static int tasha_get_anc_func(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = (tasha->anc_func == true ? 1 : 0); + return 0; +} + +static int tasha_put_anc_func(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + + mutex_lock(&tasha->codec_mutex); + tasha->anc_func = (!ucontrol->value.integer.value[0] ? false : true); + + dev_dbg(codec->dev, "%s: anc_func %x", __func__, tasha->anc_func); + + if (tasha->anc_func == true) { + snd_soc_dapm_enable_pin(dapm, "ANC LINEOUT2 PA"); + snd_soc_dapm_enable_pin(dapm, "ANC LINEOUT2"); + snd_soc_dapm_enable_pin(dapm, "ANC LINEOUT1 PA"); + snd_soc_dapm_enable_pin(dapm, "ANC LINEOUT1"); + snd_soc_dapm_enable_pin(dapm, "ANC HPHR PA"); + snd_soc_dapm_enable_pin(dapm, "ANC HPHR"); + snd_soc_dapm_enable_pin(dapm, "ANC HPHL PA"); + snd_soc_dapm_enable_pin(dapm, "ANC HPHL"); + snd_soc_dapm_enable_pin(dapm, "ANC EAR PA"); + snd_soc_dapm_enable_pin(dapm, "ANC EAR"); + snd_soc_dapm_enable_pin(dapm, "ANC SPK1 PA"); + snd_soc_dapm_disable_pin(dapm, "LINEOUT2"); + snd_soc_dapm_disable_pin(dapm, "LINEOUT2 PA"); + snd_soc_dapm_disable_pin(dapm, "LINEOUT1"); + snd_soc_dapm_disable_pin(dapm, "LINEOUT1 PA"); + snd_soc_dapm_disable_pin(dapm, "HPHR"); + snd_soc_dapm_disable_pin(dapm, "HPHL"); + snd_soc_dapm_disable_pin(dapm, "HPHR PA"); + snd_soc_dapm_disable_pin(dapm, "HPHL PA"); + snd_soc_dapm_disable_pin(dapm, "EAR PA"); + snd_soc_dapm_disable_pin(dapm, "EAR"); + } else { + snd_soc_dapm_disable_pin(dapm, "ANC LINEOUT2 PA"); + snd_soc_dapm_disable_pin(dapm, "ANC LINEOUT2"); + snd_soc_dapm_disable_pin(dapm, "ANC LINEOUT1 PA"); + snd_soc_dapm_disable_pin(dapm, "ANC LINEOUT1"); + snd_soc_dapm_disable_pin(dapm, "ANC HPHR"); + snd_soc_dapm_disable_pin(dapm, "ANC HPHL"); + snd_soc_dapm_disable_pin(dapm, "ANC HPHR PA"); + snd_soc_dapm_disable_pin(dapm, "ANC HPHL PA"); + snd_soc_dapm_disable_pin(dapm, "ANC EAR PA"); + snd_soc_dapm_disable_pin(dapm, "ANC EAR"); + snd_soc_dapm_disable_pin(dapm, "ANC SPK1 PA"); + snd_soc_dapm_enable_pin(dapm, "LINEOUT2"); + snd_soc_dapm_enable_pin(dapm, "LINEOUT2 PA"); + snd_soc_dapm_enable_pin(dapm, "LINEOUT1"); + snd_soc_dapm_enable_pin(dapm, "LINEOUT1 PA"); + snd_soc_dapm_enable_pin(dapm, "HPHR"); + snd_soc_dapm_enable_pin(dapm, "HPHL"); + snd_soc_dapm_enable_pin(dapm, "HPHR PA"); + snd_soc_dapm_enable_pin(dapm, "HPHL PA"); + snd_soc_dapm_enable_pin(dapm, "EAR PA"); + snd_soc_dapm_enable_pin(dapm, "EAR"); + } + mutex_unlock(&tasha->codec_mutex); + snd_soc_dapm_sync(dapm); + return 0; +} + +static int tasha_get_clkmode(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.enumerated.item[0] = tasha->clk_mode; + dev_dbg(codec->dev, "%s: clk_mode: %d\n", __func__, tasha->clk_mode); + + return 0; +} + +static int tasha_put_clkmode(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + + tasha->clk_mode = ucontrol->value.enumerated.item[0]; + dev_dbg(codec->dev, "%s: clk_mode: %d\n", __func__, tasha->clk_mode); + + return 0; +} + +static int tasha_get_iir_enable_audio_mixer( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + int iir_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->reg; + int band_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + /* IIR filter band registers are at integer multiples of 16 */ + u16 iir_reg = WCD9335_CDC_SIDETONE_IIR0_IIR_CTL + 16 * iir_idx; + + ucontrol->value.integer.value[0] = (snd_soc_read(codec, iir_reg) & + (1 << band_idx)) != 0; + + dev_dbg(codec->dev, "%s: IIR #%d band #%d enable %d\n", __func__, + iir_idx, band_idx, + (uint32_t)ucontrol->value.integer.value[0]); + return 0; +} + +static int tasha_hph_impedance_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + uint32_t zl, zr; + bool hphr; + struct soc_multi_mixer_control *mc; + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tasha_priv *priv = snd_soc_codec_get_drvdata(codec); + + mc = (struct soc_multi_mixer_control *)(kcontrol->private_value); + hphr = mc->shift; + wcd_mbhc_get_impedance(&priv->mbhc, &zl, &zr); + dev_dbg(codec->dev, "%s: zl=%u(ohms), zr=%u(ohms)\n", __func__, zl, zr); + ucontrol->value.integer.value[0] = hphr ? zr : zl; + + return 0; +} + +static const struct snd_kcontrol_new impedance_detect_controls[] = { + SOC_SINGLE_EXT("HPHL Impedance", 0, 0, UINT_MAX, 0, + tasha_hph_impedance_get, NULL), + SOC_SINGLE_EXT("HPHR Impedance", 0, 1, UINT_MAX, 0, + tasha_hph_impedance_get, NULL), +}; + +static int tasha_get_hph_type(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tasha_priv *priv = snd_soc_codec_get_drvdata(codec); + struct wcd_mbhc *mbhc; + + if (!priv) { + dev_dbg(codec->dev, "%s: wcd9335 private data is NULL\n", + __func__); + return 0; + } + + mbhc = &priv->mbhc; + if (!mbhc) { + dev_dbg(codec->dev, "%s: mbhc not initialized\n", __func__); + return 0; + } + + ucontrol->value.integer.value[0] = (u32) mbhc->hph_type; + dev_dbg(codec->dev, "%s: hph_type = %u\n", __func__, mbhc->hph_type); + + return 0; +} + +static const struct snd_kcontrol_new hph_type_detect_controls[] = { + SOC_SINGLE_EXT("HPH Type", 0, 0, UINT_MAX, 0, + tasha_get_hph_type, NULL), +}; + +static int tasha_vi_feed_mixer_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm); + struct tasha_priv *tasha_p = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = tasha_p->vi_feed_value; + + return 0; +} + +static int tasha_vi_feed_mixer_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm); + struct tasha_priv *tasha_p = snd_soc_codec_get_drvdata(codec); + struct wcd9xxx *core = tasha_p->wcd9xxx; + struct soc_multi_mixer_control *mixer = + ((struct soc_multi_mixer_control *)kcontrol->private_value); + u32 dai_id = widget->shift; + u32 port_id = mixer->shift; + u32 enable = ucontrol->value.integer.value[0]; + + dev_dbg(codec->dev, "%s: enable: %d, port_id:%d, dai_id: %d\n", + __func__, enable, port_id, dai_id); + + tasha_p->vi_feed_value = ucontrol->value.integer.value[0]; + + mutex_lock(&tasha_p->codec_mutex); + if (enable) { + if (port_id == TASHA_TX14 && !test_bit(VI_SENSE_1, + &tasha_p->status_mask)) { + list_add_tail(&core->tx_chs[TASHA_TX14].list, + &tasha_p->dai[dai_id].wcd9xxx_ch_list); + set_bit(VI_SENSE_1, &tasha_p->status_mask); + } + if (port_id == TASHA_TX15 && !test_bit(VI_SENSE_2, + &tasha_p->status_mask)) { + list_add_tail(&core->tx_chs[TASHA_TX15].list, + &tasha_p->dai[dai_id].wcd9xxx_ch_list); + set_bit(VI_SENSE_2, &tasha_p->status_mask); + } + } else { + if (port_id == TASHA_TX14 && test_bit(VI_SENSE_1, + &tasha_p->status_mask)) { + list_del_init(&core->tx_chs[TASHA_TX14].list); + clear_bit(VI_SENSE_1, &tasha_p->status_mask); + } + if (port_id == TASHA_TX15 && test_bit(VI_SENSE_2, + &tasha_p->status_mask)) { + list_del_init(&core->tx_chs[TASHA_TX15].list); + clear_bit(VI_SENSE_2, &tasha_p->status_mask); + } + } + mutex_unlock(&tasha_p->codec_mutex); + snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, enable, NULL); + + return 0; +} + +/* virtual port entries */ +static int slim_tx_mixer_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm); + struct tasha_priv *tasha_p = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = tasha_p->tx_port_value; + return 0; +} + +static int slim_tx_mixer_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm); + struct tasha_priv *tasha_p = snd_soc_codec_get_drvdata(codec); + struct wcd9xxx *core = dev_get_drvdata(codec->dev->parent); + struct snd_soc_dapm_update *update = NULL; + struct soc_multi_mixer_control *mixer = + ((struct soc_multi_mixer_control *)kcontrol->private_value); + u32 dai_id = widget->shift; + u32 port_id = mixer->shift; + u32 enable = ucontrol->value.integer.value[0]; + u32 vtable; + + + dev_dbg(codec->dev, "%s: wname %s cname %s value %u shift %d item %ld\n", + __func__, + widget->name, ucontrol->id.name, tasha_p->tx_port_value, + widget->shift, ucontrol->value.integer.value[0]); + + mutex_lock(&tasha_p->codec_mutex); + + if (tasha_p->intf_type == WCD9XXX_INTERFACE_TYPE_SLIMBUS) { + if (dai_id >= ARRAY_SIZE(vport_slim_check_table)) { + dev_err(codec->dev, "%s: dai_id: %d, out of bounds\n", + __func__, dai_id); + mutex_unlock(&tasha_p->codec_mutex); + return -EINVAL; + } + vtable = vport_slim_check_table[dai_id]; + } else { + if (dai_id >= ARRAY_SIZE(vport_i2s_check_table)) { + dev_err(codec->dev, "%s: dai_id: %d, out of bounds\n", + __func__, dai_id); + return -EINVAL; + } + vtable = vport_i2s_check_table[dai_id]; + } + switch (dai_id) { + case AIF1_CAP: + case AIF2_CAP: + case AIF3_CAP: + /* only add to the list if value not set */ + if (enable && !(tasha_p->tx_port_value & 1 << port_id)) { + + if (wcd9xxx_tx_vport_validation(vtable, port_id, + tasha_p->dai, NUM_CODEC_DAIS)) { + dev_dbg(codec->dev, "%s: TX%u is used by other virtual port\n", + __func__, port_id); + mutex_unlock(&tasha_p->codec_mutex); + return 0; + } + tasha_p->tx_port_value |= 1 << port_id; + list_add_tail(&core->tx_chs[port_id].list, + &tasha_p->dai[dai_id].wcd9xxx_ch_list + ); + } else if (!enable && (tasha_p->tx_port_value & + 1 << port_id)) { + tasha_p->tx_port_value &= ~(1 << port_id); + list_del_init(&core->tx_chs[port_id].list); + } else { + if (enable) + dev_dbg(codec->dev, "%s: TX%u port is used by\n" + "this virtual port\n", + __func__, port_id); + else + dev_dbg(codec->dev, "%s: TX%u port is not used by\n" + "this virtual port\n", + __func__, port_id); + /* avoid update power function */ + mutex_unlock(&tasha_p->codec_mutex); + return 0; + } + break; + case AIF4_MAD_TX: + case AIF5_CPE_TX: + break; + default: + pr_err("Unknown AIF %d\n", dai_id); + mutex_unlock(&tasha_p->codec_mutex); + return -EINVAL; + } + pr_debug("%s: name %s sname %s updated value %u shift %d\n", __func__, + widget->name, widget->sname, tasha_p->tx_port_value, + widget->shift); + + mutex_unlock(&tasha_p->codec_mutex); + snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, enable, update); + + return 0; +} + +static int slim_rx_mux_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm); + struct tasha_priv *tasha_p = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.enumerated.item[0] = + tasha_p->rx_port_value[widget->shift]; + return 0; +} + +static const char *const slim_rx_mux_text[] = { + "ZERO", "AIF1_PB", "AIF2_PB", "AIF3_PB", "AIF4_PB", "AIF_MIX1_PB" +}; + +static int slim_rx_mux_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm); + struct tasha_priv *tasha_p = snd_soc_codec_get_drvdata(codec); + struct wcd9xxx *core = dev_get_drvdata(codec->dev->parent); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + struct snd_soc_dapm_update *update = NULL; + unsigned int rx_port_value; + u32 port_id = widget->shift; + + tasha_p->rx_port_value[port_id] = ucontrol->value.enumerated.item[0]; + rx_port_value = tasha_p->rx_port_value[port_id]; + + pr_debug("%s: wname %s cname %s value %u shift %d item %ld\n", __func__, + widget->name, ucontrol->id.name, rx_port_value, + widget->shift, ucontrol->value.integer.value[0]); + + mutex_lock(&tasha_p->codec_mutex); + + if (tasha_p->intf_type != WCD9XXX_INTERFACE_TYPE_SLIMBUS) { + if (rx_port_value > 2) { + dev_err(codec->dev, "%s: invalid AIF for I2C mode\n", + __func__); + goto err; + } + } + /* value need to match the Virtual port and AIF number */ + switch (rx_port_value) { + case 0: + list_del_init(&core->rx_chs[port_id].list); + break; + case 1: + if (wcd9xxx_rx_vport_validation(port_id + + TASHA_RX_PORT_START_NUMBER, + &tasha_p->dai[AIF1_PB].wcd9xxx_ch_list)) { + dev_dbg(codec->dev, "%s: RX%u is used by current requesting AIF_PB itself\n", + __func__, port_id); + goto rtn; + } + list_add_tail(&core->rx_chs[port_id].list, + &tasha_p->dai[AIF1_PB].wcd9xxx_ch_list); + break; + case 2: + if (wcd9xxx_rx_vport_validation(port_id + + TASHA_RX_PORT_START_NUMBER, + &tasha_p->dai[AIF2_PB].wcd9xxx_ch_list)) { + dev_dbg(codec->dev, "%s: RX%u is used by current requesting AIF_PB itself\n", + __func__, port_id); + goto rtn; + } + list_add_tail(&core->rx_chs[port_id].list, + &tasha_p->dai[AIF2_PB].wcd9xxx_ch_list); + break; + case 3: + if (wcd9xxx_rx_vport_validation(port_id + + TASHA_RX_PORT_START_NUMBER, + &tasha_p->dai[AIF3_PB].wcd9xxx_ch_list)) { + dev_dbg(codec->dev, "%s: RX%u is used by current requesting AIF_PB itself\n", + __func__, port_id); + goto rtn; + } + list_add_tail(&core->rx_chs[port_id].list, + &tasha_p->dai[AIF3_PB].wcd9xxx_ch_list); + break; + case 4: + if (wcd9xxx_rx_vport_validation(port_id + + TASHA_RX_PORT_START_NUMBER, + &tasha_p->dai[AIF4_PB].wcd9xxx_ch_list)) { + dev_dbg(codec->dev, "%s: RX%u is used by current requesting AIF_PB itself\n", + __func__, port_id); + goto rtn; + } + list_add_tail(&core->rx_chs[port_id].list, + &tasha_p->dai[AIF4_PB].wcd9xxx_ch_list); + break; + case 5: + if (wcd9xxx_rx_vport_validation(port_id + + TASHA_RX_PORT_START_NUMBER, + &tasha_p->dai[AIF_MIX1_PB].wcd9xxx_ch_list)) { + dev_dbg(codec->dev, "%s: RX%u is used by current requesting AIF_PB itself\n", + __func__, port_id); + goto rtn; + } + list_add_tail(&core->rx_chs[port_id].list, + &tasha_p->dai[AIF_MIX1_PB].wcd9xxx_ch_list); + break; + default: + pr_err("Unknown AIF %d\n", rx_port_value); + goto err; + } +rtn: + mutex_unlock(&tasha_p->codec_mutex); + snd_soc_dapm_mux_update_power(widget->dapm, kcontrol, + rx_port_value, e, update); + + return 0; +err: + mutex_unlock(&tasha_p->codec_mutex); + return -EINVAL; +} + +static const struct soc_enum slim_rx_mux_enum = + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(slim_rx_mux_text), slim_rx_mux_text); + +static const struct snd_kcontrol_new slim_rx_mux[TASHA_RX_MAX] = { + SOC_DAPM_ENUM_EXT("SLIM RX0 Mux", slim_rx_mux_enum, + slim_rx_mux_get, slim_rx_mux_put), + SOC_DAPM_ENUM_EXT("SLIM RX1 Mux", slim_rx_mux_enum, + slim_rx_mux_get, slim_rx_mux_put), + SOC_DAPM_ENUM_EXT("SLIM RX2 Mux", slim_rx_mux_enum, + slim_rx_mux_get, slim_rx_mux_put), + SOC_DAPM_ENUM_EXT("SLIM RX3 Mux", slim_rx_mux_enum, + slim_rx_mux_get, slim_rx_mux_put), + SOC_DAPM_ENUM_EXT("SLIM RX4 Mux", slim_rx_mux_enum, + slim_rx_mux_get, slim_rx_mux_put), + SOC_DAPM_ENUM_EXT("SLIM RX5 Mux", slim_rx_mux_enum, + slim_rx_mux_get, slim_rx_mux_put), + SOC_DAPM_ENUM_EXT("SLIM RX6 Mux", slim_rx_mux_enum, + slim_rx_mux_get, slim_rx_mux_put), + SOC_DAPM_ENUM_EXT("SLIM RX7 Mux", slim_rx_mux_enum, + slim_rx_mux_get, slim_rx_mux_put), +}; + +static const struct snd_kcontrol_new aif4_vi_mixer[] = { + SOC_SINGLE_EXT("SPKR_VI_1", SND_SOC_NOPM, TASHA_TX14, 1, 0, + tasha_vi_feed_mixer_get, tasha_vi_feed_mixer_put), + SOC_SINGLE_EXT("SPKR_VI_2", SND_SOC_NOPM, TASHA_TX15, 1, 0, + tasha_vi_feed_mixer_get, tasha_vi_feed_mixer_put), +}; + +static const struct snd_kcontrol_new aif1_cap_mixer[] = { + SOC_SINGLE_EXT("SLIM TX0", SND_SOC_NOPM, TASHA_TX0, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX1", SND_SOC_NOPM, TASHA_TX1, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX2", SND_SOC_NOPM, TASHA_TX2, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX3", SND_SOC_NOPM, TASHA_TX3, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX4", SND_SOC_NOPM, TASHA_TX4, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX5", SND_SOC_NOPM, TASHA_TX5, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX6", SND_SOC_NOPM, TASHA_TX6, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX7", SND_SOC_NOPM, TASHA_TX7, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX8", SND_SOC_NOPM, TASHA_TX8, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX9", SND_SOC_NOPM, TASHA_TX9, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX10", SND_SOC_NOPM, TASHA_TX10, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX11", SND_SOC_NOPM, TASHA_TX11, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX13", SND_SOC_NOPM, TASHA_TX13, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), +}; + +static const struct snd_kcontrol_new aif2_cap_mixer[] = { + SOC_SINGLE_EXT("SLIM TX0", SND_SOC_NOPM, TASHA_TX0, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX1", SND_SOC_NOPM, TASHA_TX1, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX2", SND_SOC_NOPM, TASHA_TX2, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX3", SND_SOC_NOPM, TASHA_TX3, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX4", SND_SOC_NOPM, TASHA_TX4, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX5", SND_SOC_NOPM, TASHA_TX5, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX6", SND_SOC_NOPM, TASHA_TX6, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX7", SND_SOC_NOPM, TASHA_TX7, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX8", SND_SOC_NOPM, TASHA_TX8, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX9", SND_SOC_NOPM, TASHA_TX9, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX10", SND_SOC_NOPM, TASHA_TX10, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX11", SND_SOC_NOPM, TASHA_TX11, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX13", SND_SOC_NOPM, TASHA_TX13, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), +}; + +static const struct snd_kcontrol_new aif3_cap_mixer[] = { + SOC_SINGLE_EXT("SLIM TX0", SND_SOC_NOPM, TASHA_TX0, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX1", SND_SOC_NOPM, TASHA_TX1, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX2", SND_SOC_NOPM, TASHA_TX2, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX3", SND_SOC_NOPM, TASHA_TX3, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX4", SND_SOC_NOPM, TASHA_TX4, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX5", SND_SOC_NOPM, TASHA_TX5, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX6", SND_SOC_NOPM, TASHA_TX6, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX7", SND_SOC_NOPM, TASHA_TX7, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX8", SND_SOC_NOPM, TASHA_TX8, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX9", SND_SOC_NOPM, TASHA_TX9, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX10", SND_SOC_NOPM, TASHA_TX10, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX11", SND_SOC_NOPM, TASHA_TX11, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX13", SND_SOC_NOPM, TASHA_TX13, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), +}; + +static const struct snd_kcontrol_new aif4_mad_mixer[] = { + SOC_SINGLE_EXT("SLIM TX12", SND_SOC_NOPM, TASHA_TX12, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX13", SND_SOC_NOPM, TASHA_TX13, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX1", SND_SOC_NOPM, 0, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + +}; + +static const struct snd_kcontrol_new rx_int1_spline_mix_switch[] = { + SOC_DAPM_SINGLE("HPHL Switch", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new rx_int2_spline_mix_switch[] = { + SOC_DAPM_SINGLE("HPHR Switch", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new rx_int3_spline_mix_switch[] = { + SOC_DAPM_SINGLE("LO1 Switch", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new rx_int4_spline_mix_switch[] = { + SOC_DAPM_SINGLE("LO2 Switch", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new rx_int5_spline_mix_switch[] = { + SOC_DAPM_SINGLE("LO3 Switch", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new rx_int6_spline_mix_switch[] = { + SOC_DAPM_SINGLE("LO4 Switch", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new rx_int7_spline_mix_switch[] = { + SOC_DAPM_SINGLE("SPKRL Switch", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new rx_int8_spline_mix_switch[] = { + SOC_DAPM_SINGLE("SPKRR Switch", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new rx_int5_vbat_mix_switch[] = { + SOC_DAPM_SINGLE("LO3 VBAT Enable", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new rx_int6_vbat_mix_switch[] = { + SOC_DAPM_SINGLE("LO4 VBAT Enable", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new rx_int7_vbat_mix_switch[] = { + SOC_DAPM_SINGLE("SPKRL VBAT Enable", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new rx_int8_vbat_mix_switch[] = { + SOC_DAPM_SINGLE("SPKRR VBAT Enable", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new cpe_in_mix_switch[] = { + SOC_DAPM_SINGLE("MAD_BYPASS", SND_SOC_NOPM, 0, 1, 0) +}; + + + +static int tasha_put_iir_enable_audio_mixer( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + int iir_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->reg; + int band_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + bool iir_band_en_status; + int value = ucontrol->value.integer.value[0]; + u16 iir_reg = WCD9335_CDC_SIDETONE_IIR0_IIR_CTL + 16 * iir_idx; + + /* Mask first 5 bits, 6-8 are reserved */ + snd_soc_update_bits(codec, iir_reg, (1 << band_idx), + (value << band_idx)); + + iir_band_en_status = ((snd_soc_read(codec, iir_reg) & + (1 << band_idx)) != 0); + pr_debug("%s: IIR #%d band #%d enable %d\n", __func__, + iir_idx, band_idx, iir_band_en_status); + return 0; +} + +static uint32_t get_iir_band_coeff(struct snd_soc_codec *codec, + int iir_idx, int band_idx, + int coeff_idx) +{ + uint32_t value = 0; + + /* Address does not automatically update if reading */ + snd_soc_write(codec, + (WCD9335_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL + 16 * iir_idx), + ((band_idx * BAND_MAX + coeff_idx) + * sizeof(uint32_t)) & 0x7F); + + value |= snd_soc_read(codec, + (WCD9335_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL + 16 * iir_idx)); + + snd_soc_write(codec, + (WCD9335_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL + 16 * iir_idx), + ((band_idx * BAND_MAX + coeff_idx) + * sizeof(uint32_t) + 1) & 0x7F); + + value |= (snd_soc_read(codec, + (WCD9335_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL + + 16 * iir_idx)) << 8); + + snd_soc_write(codec, + (WCD9335_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL + 16 * iir_idx), + ((band_idx * BAND_MAX + coeff_idx) + * sizeof(uint32_t) + 2) & 0x7F); + + value |= (snd_soc_read(codec, + (WCD9335_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL + + 16 * iir_idx)) << 16); + + snd_soc_write(codec, + (WCD9335_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL + 16 * iir_idx), + ((band_idx * BAND_MAX + coeff_idx) + * sizeof(uint32_t) + 3) & 0x7F); + + /* Mask bits top 2 bits since they are reserved */ + value |= ((snd_soc_read(codec, + (WCD9335_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL + + 16 * iir_idx)) & 0x3F) << 24); + + return value; +} + +static int tasha_get_iir_band_audio_mixer( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + int iir_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->reg; + int band_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + + ucontrol->value.integer.value[0] = + get_iir_band_coeff(codec, iir_idx, band_idx, 0); + ucontrol->value.integer.value[1] = + get_iir_band_coeff(codec, iir_idx, band_idx, 1); + ucontrol->value.integer.value[2] = + get_iir_band_coeff(codec, iir_idx, band_idx, 2); + ucontrol->value.integer.value[3] = + get_iir_band_coeff(codec, iir_idx, band_idx, 3); + ucontrol->value.integer.value[4] = + get_iir_band_coeff(codec, iir_idx, band_idx, 4); + + pr_debug("%s: IIR #%d band #%d b0 = 0x%x\n" + "%s: IIR #%d band #%d b1 = 0x%x\n" + "%s: IIR #%d band #%d b2 = 0x%x\n" + "%s: IIR #%d band #%d a1 = 0x%x\n" + "%s: IIR #%d band #%d a2 = 0x%x\n", + __func__, iir_idx, band_idx, + (uint32_t)ucontrol->value.integer.value[0], + __func__, iir_idx, band_idx, + (uint32_t)ucontrol->value.integer.value[1], + __func__, iir_idx, band_idx, + (uint32_t)ucontrol->value.integer.value[2], + __func__, iir_idx, band_idx, + (uint32_t)ucontrol->value.integer.value[3], + __func__, iir_idx, band_idx, + (uint32_t)ucontrol->value.integer.value[4]); + return 0; +} + +static void set_iir_band_coeff(struct snd_soc_codec *codec, + int iir_idx, int band_idx, + uint32_t value) +{ + snd_soc_write(codec, + (WCD9335_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL + 16 * iir_idx), + (value & 0xFF)); + + snd_soc_write(codec, + (WCD9335_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL + 16 * iir_idx), + (value >> 8) & 0xFF); + + snd_soc_write(codec, + (WCD9335_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL + 16 * iir_idx), + (value >> 16) & 0xFF); + + /* Mask top 2 bits, 7-8 are reserved */ + snd_soc_write(codec, + (WCD9335_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL + 16 * iir_idx), + (value >> 24) & 0x3F); +} + +static void tasha_codec_enable_int_port(struct wcd9xxx_codec_dai_data *dai, + struct snd_soc_codec *codec) +{ + struct wcd9xxx_ch *ch; + int port_num = 0; + unsigned short reg = 0; + u8 val = 0; + struct tasha_priv *tasha_p; + + if (!dai || !codec) { + pr_err("%s: Invalid params\n", __func__); + return; + } + + tasha_p = snd_soc_codec_get_drvdata(codec); + list_for_each_entry(ch, &dai->wcd9xxx_ch_list, list) { + if (ch->port >= TASHA_RX_PORT_START_NUMBER) { + port_num = ch->port - TASHA_RX_PORT_START_NUMBER; + reg = TASHA_SLIM_PGD_PORT_INT_EN0 + (port_num / 8); + val = wcd9xxx_interface_reg_read(tasha_p->wcd9xxx, + reg); + if (!(val & BYTE_BIT_MASK(port_num))) { + val |= BYTE_BIT_MASK(port_num); + wcd9xxx_interface_reg_write( + tasha_p->wcd9xxx, reg, val); + val = wcd9xxx_interface_reg_read( + tasha_p->wcd9xxx, reg); + } + } else { + port_num = ch->port; + reg = TASHA_SLIM_PGD_PORT_INT_TX_EN0 + (port_num / 8); + val = wcd9xxx_interface_reg_read(tasha_p->wcd9xxx, + reg); + if (!(val & BYTE_BIT_MASK(port_num))) { + val |= BYTE_BIT_MASK(port_num); + wcd9xxx_interface_reg_write(tasha_p->wcd9xxx, + reg, val); + val = wcd9xxx_interface_reg_read( + tasha_p->wcd9xxx, reg); + } + } + } +} + +static int tasha_codec_enable_slim_chmask(struct wcd9xxx_codec_dai_data *dai, + bool up) +{ + int ret = 0; + struct wcd9xxx_ch *ch; + + if (up) { + list_for_each_entry(ch, &dai->wcd9xxx_ch_list, list) { + ret = wcd9xxx_get_slave_port(ch->ch_num); + if (ret < 0) { + pr_err("%s: Invalid slave port ID: %d\n", + __func__, ret); + ret = -EINVAL; + } else { + set_bit(ret, &dai->ch_mask); + } + } + } else { + ret = wait_event_timeout(dai->dai_wait, (dai->ch_mask == 0), + msecs_to_jiffies( + TASHA_SLIM_CLOSE_TIMEOUT)); + if (!ret) { + pr_err("%s: Slim close tx/rx wait timeout, ch_mask:0x%lx\n", + __func__, dai->ch_mask); + ret = -ETIMEDOUT; + } else { + ret = 0; + } + } + return ret; +} + +static int tasha_codec_enable_slimrx(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct wcd9xxx *core; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tasha_priv *tasha_p = snd_soc_codec_get_drvdata(codec); + int ret = 0; + struct wcd9xxx_codec_dai_data *dai; + + core = dev_get_drvdata(codec->dev->parent); + + dev_dbg(codec->dev, "%s: event called! codec name %s num_dai %d\n" + "stream name %s event %d\n", + __func__, codec->component.name, + codec->component.num_dai, w->sname, event); + + /* Execute the callback only if interface type is slimbus */ + if (tasha_p->intf_type != WCD9XXX_INTERFACE_TYPE_SLIMBUS) + return 0; + + dai = &tasha_p->dai[w->shift]; + dev_dbg(codec->dev, "%s: w->name %s w->shift %d event %d\n", + __func__, w->name, w->shift, event); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + dai->bus_down_in_recovery = false; + tasha_codec_enable_int_port(dai, codec); + (void) tasha_codec_enable_slim_chmask(dai, true); + ret = wcd9xxx_cfg_slim_sch_rx(core, &dai->wcd9xxx_ch_list, + dai->rate, dai->bit_width, + &dai->grph); + break; + case SND_SOC_DAPM_PRE_PMD: + tasha_codec_vote_max_bw(codec, true); + break; + case SND_SOC_DAPM_POST_PMD: + ret = wcd9xxx_disconnect_port(core, &dai->wcd9xxx_ch_list, + dai->grph); + dev_dbg(codec->dev, "%s: Disconnect RX port, ret = %d\n", + __func__, ret); + + if (!dai->bus_down_in_recovery) + ret = tasha_codec_enable_slim_chmask(dai, false); + else + dev_dbg(codec->dev, + "%s: bus in recovery skip enable slim_chmask", + __func__); + ret = wcd9xxx_close_slim_sch_rx(core, &dai->wcd9xxx_ch_list, + dai->grph); + break; + } + return ret; +} + +static int tasha_codec_enable_slimvi_feedback(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct wcd9xxx *core = NULL; + struct snd_soc_codec *codec = NULL; + struct tasha_priv *tasha_p = NULL; + int ret = 0; + struct wcd9xxx_codec_dai_data *dai = NULL; + + if (!w) { + pr_err("%s invalid params\n", __func__); + return -EINVAL; + } + codec = snd_soc_dapm_to_codec(w->dapm); + tasha_p = snd_soc_codec_get_drvdata(codec); + core = tasha_p->wcd9xxx; + + dev_dbg(codec->dev, "%s: num_dai %d stream name %s\n", + __func__, codec->component.num_dai, w->sname); + + /* Execute the callback only if interface type is slimbus */ + if (tasha_p->intf_type != WCD9XXX_INTERFACE_TYPE_SLIMBUS) { + dev_err(codec->dev, "%s Interface is not correct", __func__); + return 0; + } + + dev_dbg(codec->dev, "%s(): w->name %s event %d w->shift %d\n", + __func__, w->name, event, w->shift); + if (w->shift != AIF4_VIFEED) { + pr_err("%s Error in enabling the tx path\n", __func__); + ret = -EINVAL; + goto out_vi; + } + dai = &tasha_p->dai[w->shift]; + switch (event) { + case SND_SOC_DAPM_POST_PMU: + if (test_bit(VI_SENSE_1, &tasha_p->status_mask)) { + dev_dbg(codec->dev, "%s: spkr1 enabled\n", __func__); + /* Enable V&I sensing */ + snd_soc_update_bits(codec, + WCD9335_CDC_TX9_SPKR_PROT_PATH_CTL, 0x20, 0x20); + snd_soc_update_bits(codec, + WCD9335_CDC_TX10_SPKR_PROT_PATH_CTL, 0x20, + 0x20); + snd_soc_update_bits(codec, + WCD9335_CDC_TX9_SPKR_PROT_PATH_CTL, 0x0F, 0x00); + snd_soc_update_bits(codec, + WCD9335_CDC_TX10_SPKR_PROT_PATH_CTL, 0x0F, + 0x00); + snd_soc_update_bits(codec, + WCD9335_CDC_TX9_SPKR_PROT_PATH_CTL, 0x10, 0x10); + snd_soc_update_bits(codec, + WCD9335_CDC_TX10_SPKR_PROT_PATH_CTL, 0x10, + 0x10); + snd_soc_update_bits(codec, + WCD9335_CDC_TX9_SPKR_PROT_PATH_CTL, 0x20, 0x00); + snd_soc_update_bits(codec, + WCD9335_CDC_TX10_SPKR_PROT_PATH_CTL, 0x20, + 0x00); + } + if (test_bit(VI_SENSE_2, &tasha_p->status_mask)) { + pr_debug("%s: spkr2 enabled\n", __func__); + /* Enable V&I sensing */ + snd_soc_update_bits(codec, + WCD9335_CDC_TX11_SPKR_PROT_PATH_CTL, 0x20, + 0x20); + snd_soc_update_bits(codec, + WCD9335_CDC_TX12_SPKR_PROT_PATH_CTL, 0x20, + 0x20); + snd_soc_update_bits(codec, + WCD9335_CDC_TX11_SPKR_PROT_PATH_CTL, 0x0F, + 0x00); + snd_soc_update_bits(codec, + WCD9335_CDC_TX12_SPKR_PROT_PATH_CTL, 0x0F, + 0x00); + snd_soc_update_bits(codec, + WCD9335_CDC_TX11_SPKR_PROT_PATH_CTL, 0x10, + 0x10); + snd_soc_update_bits(codec, + WCD9335_CDC_TX12_SPKR_PROT_PATH_CTL, 0x10, + 0x10); + snd_soc_update_bits(codec, + WCD9335_CDC_TX11_SPKR_PROT_PATH_CTL, 0x20, + 0x00); + snd_soc_update_bits(codec, + WCD9335_CDC_TX12_SPKR_PROT_PATH_CTL, 0x20, + 0x00); + } + dai->bus_down_in_recovery = false; + tasha_codec_enable_int_port(dai, codec); + (void) tasha_codec_enable_slim_chmask(dai, true); + ret = wcd9xxx_cfg_slim_sch_tx(core, &dai->wcd9xxx_ch_list, + dai->rate, dai->bit_width, + &dai->grph); + break; + case SND_SOC_DAPM_POST_PMD: + ret = wcd9xxx_close_slim_sch_tx(core, &dai->wcd9xxx_ch_list, + dai->grph); + if (ret) + dev_err(codec->dev, "%s error in close_slim_sch_tx %d\n", + __func__, ret); + if (!dai->bus_down_in_recovery) + ret = tasha_codec_enable_slim_chmask(dai, false); + if (ret < 0) { + ret = wcd9xxx_disconnect_port(core, + &dai->wcd9xxx_ch_list, + dai->grph); + dev_dbg(codec->dev, "%s: Disconnect TX port, ret = %d\n", + __func__, ret); + } + if (test_bit(VI_SENSE_1, &tasha_p->status_mask)) { + /* Disable V&I sensing */ + dev_dbg(codec->dev, "%s: spkr1 disabled\n", __func__); + snd_soc_update_bits(codec, + WCD9335_CDC_TX9_SPKR_PROT_PATH_CTL, 0x20, 0x20); + snd_soc_update_bits(codec, + WCD9335_CDC_TX10_SPKR_PROT_PATH_CTL, 0x20, + 0x20); + snd_soc_update_bits(codec, + WCD9335_CDC_TX9_SPKR_PROT_PATH_CTL, 0x10, 0x00); + snd_soc_update_bits(codec, + WCD9335_CDC_TX10_SPKR_PROT_PATH_CTL, 0x10, + 0x00); + } + if (test_bit(VI_SENSE_2, &tasha_p->status_mask)) { + /* Disable V&I sensing */ + dev_dbg(codec->dev, "%s: spkr2 disabled\n", __func__); + snd_soc_update_bits(codec, + WCD9335_CDC_TX11_SPKR_PROT_PATH_CTL, 0x20, + 0x20); + snd_soc_update_bits(codec, + WCD9335_CDC_TX12_SPKR_PROT_PATH_CTL, 0x20, + 0x20); + snd_soc_update_bits(codec, + WCD9335_CDC_TX11_SPKR_PROT_PATH_CTL, 0x10, + 0x00); + snd_soc_update_bits(codec, + WCD9335_CDC_TX12_SPKR_PROT_PATH_CTL, 0x10, + 0x00); + } + break; + } +out_vi: + return ret; +} + +/* + * __tasha_codec_enable_slimtx: Enable the slimbus slave port + * for TX path + * @codec: Handle to the codec for which the slave port is to be + * enabled. + * @dai_data: The dai specific data for dai which is enabled. + */ +static int __tasha_codec_enable_slimtx(struct snd_soc_codec *codec, + int event, struct wcd9xxx_codec_dai_data *dai) +{ + struct wcd9xxx *core; + struct tasha_priv *tasha_p = snd_soc_codec_get_drvdata(codec); + int ret = 0; + + /* Execute the callback only if interface type is slimbus */ + if (tasha_p->intf_type != WCD9XXX_INTERFACE_TYPE_SLIMBUS) + return 0; + + dev_dbg(codec->dev, + "%s: event = %d\n", __func__, event); + core = dev_get_drvdata(codec->dev->parent); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + dai->bus_down_in_recovery = false; + tasha_codec_enable_int_port(dai, codec); + (void) tasha_codec_enable_slim_chmask(dai, true); + ret = wcd9xxx_cfg_slim_sch_tx(core, &dai->wcd9xxx_ch_list, + dai->rate, dai->bit_width, + &dai->grph); + break; + case SND_SOC_DAPM_POST_PMD: + ret = wcd9xxx_close_slim_sch_tx(core, &dai->wcd9xxx_ch_list, + dai->grph); + if (!dai->bus_down_in_recovery) + ret = tasha_codec_enable_slim_chmask(dai, false); + if (ret < 0) { + ret = wcd9xxx_disconnect_port(core, + &dai->wcd9xxx_ch_list, + dai->grph); + pr_debug("%s: Disconnect TX port, ret = %d\n", + __func__, ret); + } + + break; + } + + return ret; +} + +static int tasha_codec_enable_slimtx(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tasha_priv *tasha_p = snd_soc_codec_get_drvdata(codec); + struct wcd9xxx_codec_dai_data *dai; + + dev_dbg(codec->dev, + "%s: w->name %s, w->shift = %d, num_dai %d stream name %s\n", + __func__, w->name, w->shift, + codec->component.num_dai, w->sname); + + dai = &tasha_p->dai[w->shift]; + return __tasha_codec_enable_slimtx(codec, event, dai); +} + +static void tasha_codec_cpe_pp_set_cfg(struct snd_soc_codec *codec, int event) +{ + struct tasha_priv *tasha_p = snd_soc_codec_get_drvdata(codec); + struct wcd9xxx_codec_dai_data *dai; + u8 bit_width, rate, buf_period; + + dai = &tasha_p->dai[AIF4_MAD_TX]; + switch (event) { + case SND_SOC_DAPM_POST_PMU: + switch (dai->bit_width) { + case 32: + bit_width = 0xF; + break; + case 24: + bit_width = 0xE; + break; + case 20: + bit_width = 0xD; + break; + case 16: + default: + bit_width = 0x0; + break; + } + snd_soc_update_bits(codec, WCD9335_CPE_SS_TX_PP_CFG, 0x0F, + bit_width); + + switch (dai->rate) { + case 384000: + rate = 0x30; + break; + case 192000: + rate = 0x20; + break; + case 48000: + rate = 0x10; + break; + case 16000: + default: + rate = 0x00; + break; + } + snd_soc_update_bits(codec, WCD9335_CPE_SS_TX_PP_CFG, 0x70, + rate); + + buf_period = (dai->rate * (dai->bit_width/8)) / (16*1000); + snd_soc_update_bits(codec, WCD9335_CPE_SS_TX_PP_BUF_INT_PERIOD, + 0xFF, buf_period); + dev_dbg(codec->dev, "%s: PP buffer period= 0x%x\n", + __func__, buf_period); + break; + + case SND_SOC_DAPM_POST_PMD: + snd_soc_write(codec, WCD9335_CPE_SS_TX_PP_CFG, 0x3C); + snd_soc_write(codec, WCD9335_CPE_SS_TX_PP_BUF_INT_PERIOD, 0x60); + break; + + default: + break; + } +} + +/* + * tasha_codec_get_mad_port_id: Callback function that will be invoked + * to get the port ID for MAD. + * @codec: Handle to the codec + * @port_id: cpe port_id needs to enable + */ +static int tasha_codec_get_mad_port_id(struct snd_soc_codec *codec, + u16 *port_id) +{ + struct tasha_priv *tasha_p; + struct wcd9xxx_codec_dai_data *dai; + struct wcd9xxx_ch *ch; + + if (!port_id || !codec) + return -EINVAL; + + tasha_p = snd_soc_codec_get_drvdata(codec); + if (!tasha_p) + return -EINVAL; + + dai = &tasha_p->dai[AIF4_MAD_TX]; + list_for_each_entry(ch, &dai->wcd9xxx_ch_list, list) { + if (ch->port == TASHA_TX12) + *port_id = WCD_CPE_AFE_OUT_PORT_2; + else if (ch->port == TASHA_TX13) + *port_id = WCD_CPE_AFE_OUT_PORT_4; + else { + dev_err(codec->dev, "%s: invalid mad_port = %d\n", + __func__, ch->port); + return -EINVAL; + } + } + dev_dbg(codec->dev, "%s: port_id = %d\n", __func__, *port_id); + + return 0; +} + +/* + * tasha_codec_enable_slimtx_mad: Callback function that will be invoked + * to setup the slave port for MAD. + * @codec: Handle to the codec + * @event: Indicates whether to enable or disable the slave port + */ +static int tasha_codec_enable_slimtx_mad(struct snd_soc_codec *codec, + u8 event) +{ + struct tasha_priv *tasha_p = snd_soc_codec_get_drvdata(codec); + struct wcd9xxx_codec_dai_data *dai; + struct wcd9xxx_ch *ch; + int dapm_event = SND_SOC_DAPM_POST_PMU; + u16 port = 0; + int ret = 0; + + dai = &tasha_p->dai[AIF4_MAD_TX]; + + if (event == 0) + dapm_event = SND_SOC_DAPM_POST_PMD; + + dev_dbg(codec->dev, + "%s: mad_channel, event = 0x%x\n", + __func__, event); + + list_for_each_entry(ch, &dai->wcd9xxx_ch_list, list) { + dev_dbg(codec->dev, "%s: mad_port = %d, event = 0x%x\n", + __func__, ch->port, event); + if (ch->port == TASHA_TX13) { + tasha_codec_cpe_pp_set_cfg(codec, dapm_event); + port = TASHA_TX13; + break; + } + } + + ret = __tasha_codec_enable_slimtx(codec, dapm_event, dai); + + if (port == TASHA_TX13) { + switch (dapm_event) { + case SND_SOC_DAPM_POST_PMU: + snd_soc_update_bits(codec, + WCD9335_CODEC_RPM_PWR_CPE_DRAM1_SHUTDOWN, + 0x20, 0x00); + snd_soc_update_bits(codec, + WCD9335_DATA_HUB_DATA_HUB_SB_TX13_INP_CFG, + 0x03, 0x02); + snd_soc_update_bits(codec, WCD9335_CPE_SS_CFG, + 0x80, 0x80); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_update_bits(codec, + WCD9335_CODEC_RPM_PWR_CPE_DRAM1_SHUTDOWN, + 0x20, 0x20); + snd_soc_update_bits(codec, + WCD9335_DATA_HUB_DATA_HUB_SB_TX13_INP_CFG, + 0x03, 0x00); + snd_soc_update_bits(codec, WCD9335_CPE_SS_CFG, + 0x80, 0x00); + break; + } + } + + return ret; +} + +static int tasha_put_iir_band_audio_mixer( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + int iir_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->reg; + int band_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + + /* + * Mask top bit it is reserved + * Updates addr automatically for each B2 write + */ + snd_soc_write(codec, + (WCD9335_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL + 16 * iir_idx), + (band_idx * BAND_MAX * sizeof(uint32_t)) & 0x7F); + + set_iir_band_coeff(codec, iir_idx, band_idx, + ucontrol->value.integer.value[0]); + set_iir_band_coeff(codec, iir_idx, band_idx, + ucontrol->value.integer.value[1]); + set_iir_band_coeff(codec, iir_idx, band_idx, + ucontrol->value.integer.value[2]); + set_iir_band_coeff(codec, iir_idx, band_idx, + ucontrol->value.integer.value[3]); + set_iir_band_coeff(codec, iir_idx, band_idx, + ucontrol->value.integer.value[4]); + + pr_debug("%s: IIR #%d band #%d b0 = 0x%x\n" + "%s: IIR #%d band #%d b1 = 0x%x\n" + "%s: IIR #%d band #%d b2 = 0x%x\n" + "%s: IIR #%d band #%d a1 = 0x%x\n" + "%s: IIR #%d band #%d a2 = 0x%x\n", + __func__, iir_idx, band_idx, + get_iir_band_coeff(codec, iir_idx, band_idx, 0), + __func__, iir_idx, band_idx, + get_iir_band_coeff(codec, iir_idx, band_idx, 1), + __func__, iir_idx, band_idx, + get_iir_band_coeff(codec, iir_idx, band_idx, 2), + __func__, iir_idx, band_idx, + get_iir_band_coeff(codec, iir_idx, band_idx, 3), + __func__, iir_idx, band_idx, + get_iir_band_coeff(codec, iir_idx, band_idx, 4)); + return 0; +} + +static int tasha_get_compander(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + int comp = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = tasha->comp_enabled[comp]; + return 0; +} + +static int tasha_set_compander(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + int comp = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + int value = ucontrol->value.integer.value[0]; + + pr_debug("%s: Compander %d enable current %d, new %d\n", + __func__, comp + 1, tasha->comp_enabled[comp], value); + tasha->comp_enabled[comp] = value; + + /* Any specific register configuration for compander */ + switch (comp) { + case COMPANDER_1: + /* Set Gain Source Select based on compander enable/disable */ + snd_soc_update_bits(codec, WCD9335_HPH_L_EN, 0x20, + (value ? 0x00:0x20)); + break; + case COMPANDER_2: + snd_soc_update_bits(codec, WCD9335_HPH_R_EN, 0x20, + (value ? 0x00:0x20)); + break; + case COMPANDER_3: + break; + case COMPANDER_4: + break; + case COMPANDER_5: + snd_soc_update_bits(codec, WCD9335_SE_LO_LO3_GAIN, 0x20, + (value ? 0x00:0x20)); + break; + case COMPANDER_6: + snd_soc_update_bits(codec, WCD9335_SE_LO_LO4_GAIN, 0x20, + (value ? 0x00:0x20)); + break; + case COMPANDER_7: + break; + case COMPANDER_8: + break; + default: + /* + * if compander is not enabled for any interpolator, + * it does not cause any audio failure, so do not + * return error in this case, but just print a log + */ + dev_warn(codec->dev, "%s: unknown compander: %d\n", + __func__, comp); + }; + return 0; +} + +static void tasha_codec_init_flyback(struct snd_soc_codec *codec) +{ + snd_soc_update_bits(codec, WCD9335_HPH_L_EN, 0xC0, 0x00); + snd_soc_update_bits(codec, WCD9335_HPH_R_EN, 0xC0, 0x00); + snd_soc_update_bits(codec, WCD9335_RX_BIAS_FLYB_BUFF, 0x0F, 0x00); + snd_soc_update_bits(codec, WCD9335_RX_BIAS_FLYB_BUFF, 0xF0, 0x00); +} + +static int tasha_codec_enable_rx_bias(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + tasha->rx_bias_count++; + if (tasha->rx_bias_count == 1) { + if (TASHA_IS_2_0(tasha->wcd9xxx)) + tasha_codec_init_flyback(codec); + snd_soc_update_bits(codec, WCD9335_ANA_RX_SUPPLIES, + 0x01, 0x01); + } + break; + case SND_SOC_DAPM_POST_PMD: + tasha->rx_bias_count--; + if (!tasha->rx_bias_count) + snd_soc_update_bits(codec, WCD9335_ANA_RX_SUPPLIES, + 0x01, 0x00); + break; + }; + dev_dbg(codec->dev, "%s: Current RX BIAS user count: %d\n", __func__, + tasha->rx_bias_count); + + return 0; +} + +static void tasha_realign_anc_coeff(struct snd_soc_codec *codec, + u16 reg1, u16 reg2) +{ + u8 val1, val2, tmpval1, tmpval2; + + snd_soc_write(codec, reg1, 0x00); + tmpval1 = snd_soc_read(codec, reg2); + tmpval2 = snd_soc_read(codec, reg2); + snd_soc_write(codec, reg1, 0x00); + snd_soc_write(codec, reg2, 0xFF); + snd_soc_write(codec, reg1, 0x01); + snd_soc_write(codec, reg2, 0xFF); + + snd_soc_write(codec, reg1, 0x00); + val1 = snd_soc_read(codec, reg2); + val2 = snd_soc_read(codec, reg2); + + if (val1 == 0x0F && val2 == 0xFF) { + dev_dbg(codec->dev, "%s: ANC0 co-eff index re-aligned\n", + __func__); + snd_soc_read(codec, reg2); + snd_soc_write(codec, reg1, 0x00); + snd_soc_write(codec, reg2, tmpval2); + snd_soc_write(codec, reg1, 0x01); + snd_soc_write(codec, reg2, tmpval1); + } else if (val1 == 0xFF && val2 == 0x0F) { + dev_dbg(codec->dev, "%s: ANC1 co-eff index already aligned\n", + __func__); + snd_soc_write(codec, reg1, 0x00); + snd_soc_write(codec, reg2, tmpval1); + snd_soc_write(codec, reg1, 0x01); + snd_soc_write(codec, reg2, tmpval2); + } else { + dev_err(codec->dev, "%s: ANC0 co-eff index not aligned\n", + __func__); + } +} + +static int tasha_codec_enable_anc(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + const char *filename; + const struct firmware *fw; + int i; + int ret = 0; + int num_anc_slots; + struct wcd9xxx_anc_header *anc_head; + struct firmware_cal *hwdep_cal = NULL; + u32 anc_writes_size = 0; + u32 anc_cal_size = 0; + int anc_size_remaining; + u32 *anc_ptr; + u16 reg; + u8 mask, val; + size_t cal_size; + const void *data; + + if (!tasha->anc_func) + return 0; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + hwdep_cal = wcdcal_get_fw_cal(tasha->fw_data, WCD9XXX_ANC_CAL); + if (hwdep_cal) { + data = hwdep_cal->data; + cal_size = hwdep_cal->size; + dev_dbg(codec->dev, "%s: using hwdep calibration\n", + __func__); + } else { + filename = "wcd9335/wcd9335_anc.bin"; + ret = request_firmware(&fw, filename, codec->dev); + if (ret != 0) { + dev_err(codec->dev, + "Failed to acquire ANC data: %d\n", ret); + return -ENODEV; + } + if (!fw) { + dev_err(codec->dev, "failed to get anc fw"); + return -ENODEV; + } + data = fw->data; + cal_size = fw->size; + dev_dbg(codec->dev, + "%s: using request_firmware calibration\n", __func__); + } + if (cal_size < sizeof(struct wcd9xxx_anc_header)) { + dev_err(codec->dev, "Not enough data\n"); + ret = -ENOMEM; + goto err; + } + /* First number is the number of register writes */ + anc_head = (struct wcd9xxx_anc_header *)(data); + anc_ptr = (u32 *)(data + + sizeof(struct wcd9xxx_anc_header)); + anc_size_remaining = cal_size - + sizeof(struct wcd9xxx_anc_header); + num_anc_slots = anc_head->num_anc_slots; + + if (tasha->anc_slot >= num_anc_slots) { + dev_err(codec->dev, "Invalid ANC slot selected\n"); + ret = -EINVAL; + goto err; + } + for (i = 0; i < num_anc_slots; i++) { + if (anc_size_remaining < TASHA_PACKED_REG_SIZE) { + dev_err(codec->dev, + "Invalid register format\n"); + ret = -EINVAL; + goto err; + } + anc_writes_size = (u32)(*anc_ptr); + anc_size_remaining -= sizeof(u32); + anc_ptr += 1; + + if (anc_writes_size * TASHA_PACKED_REG_SIZE + > anc_size_remaining) { + dev_err(codec->dev, + "Invalid register format\n"); + ret = -EINVAL; + goto err; + } + + if (tasha->anc_slot == i) + break; + + anc_size_remaining -= (anc_writes_size * + TASHA_PACKED_REG_SIZE); + anc_ptr += anc_writes_size; + } + if (i == num_anc_slots) { + dev_err(codec->dev, "Selected ANC slot not present\n"); + ret = -EINVAL; + goto err; + } + + i = 0; + anc_cal_size = anc_writes_size; + + if (!strcmp(w->name, "RX INT0 DAC") || + !strcmp(w->name, "ANC SPK1 PA")) + tasha_realign_anc_coeff(codec, + WCD9335_CDC_ANC0_IIR_COEFF_1_CTL, + WCD9335_CDC_ANC0_IIR_COEFF_2_CTL); + + if (!strcmp(w->name, "RX INT1 DAC") || + !strcmp(w->name, "RX INT3 DAC")) { + tasha_realign_anc_coeff(codec, + WCD9335_CDC_ANC0_IIR_COEFF_1_CTL, + WCD9335_CDC_ANC0_IIR_COEFF_2_CTL); + anc_writes_size = anc_cal_size / 2; + snd_soc_update_bits(codec, + WCD9335_CDC_ANC0_CLK_RESET_CTL, 0x39, 0x39); + } else if (!strcmp(w->name, "RX INT2 DAC") || + !strcmp(w->name, "RX INT4 DAC")) { + tasha_realign_anc_coeff(codec, + WCD9335_CDC_ANC1_IIR_COEFF_1_CTL, + WCD9335_CDC_ANC1_IIR_COEFF_2_CTL); + i = anc_cal_size / 2; + snd_soc_update_bits(codec, + WCD9335_CDC_ANC1_CLK_RESET_CTL, 0x39, 0x39); + } + + for (; i < anc_writes_size; i++) { + TASHA_CODEC_UNPACK_ENTRY(anc_ptr[i], reg, mask, val); + snd_soc_write(codec, reg, (val & mask)); + } + if (!strcmp(w->name, "RX INT1 DAC") || + !strcmp(w->name, "RX INT3 DAC")) { + snd_soc_update_bits(codec, + WCD9335_CDC_ANC0_CLK_RESET_CTL, 0x08, 0x08); + } else if (!strcmp(w->name, "RX INT2 DAC") || + !strcmp(w->name, "RX INT4 DAC")) { + snd_soc_update_bits(codec, + WCD9335_CDC_ANC1_CLK_RESET_CTL, 0x08, 0x08); + } + + if (!hwdep_cal) + release_firmware(fw); + break; + case SND_SOC_DAPM_POST_PMU: + /* Remove ANC Rx from reset */ + snd_soc_update_bits(codec, WCD9335_CDC_ANC0_CLK_RESET_CTL, + 0x08, 0x00); + snd_soc_update_bits(codec, WCD9335_CDC_ANC1_CLK_RESET_CTL, + 0x08, 0x00); + break; + case SND_SOC_DAPM_POST_PMD: + if (!strcmp(w->name, "ANC HPHL PA") || + !strcmp(w->name, "ANC EAR PA") || + !strcmp(w->name, "ANC SPK1 PA") || + !strcmp(w->name, "ANC LINEOUT1 PA")) { + snd_soc_update_bits(codec, + WCD9335_CDC_ANC0_MODE_1_CTL, 0x30, 0x00); + msleep(50); + snd_soc_update_bits(codec, + WCD9335_CDC_ANC0_MODE_1_CTL, 0x01, 0x00); + snd_soc_update_bits(codec, + WCD9335_CDC_ANC0_CLK_RESET_CTL, 0x38, 0x38); + snd_soc_update_bits(codec, + WCD9335_CDC_ANC0_CLK_RESET_CTL, 0x07, 0x00); + snd_soc_update_bits(codec, + WCD9335_CDC_ANC0_CLK_RESET_CTL, 0x38, 0x00); + } else if (!strcmp(w->name, "ANC HPHR PA") || + !strcmp(w->name, "ANC LINEOUT2 PA")) { + snd_soc_update_bits(codec, + WCD9335_CDC_ANC1_MODE_1_CTL, 0x30, 0x00); + msleep(50); + snd_soc_update_bits(codec, + WCD9335_CDC_ANC1_MODE_1_CTL, 0x01, 0x00); + snd_soc_update_bits(codec, + WCD9335_CDC_ANC1_CLK_RESET_CTL, 0x38, 0x38); + snd_soc_update_bits(codec, + WCD9335_CDC_ANC1_CLK_RESET_CTL, 0x07, 0x00); + snd_soc_update_bits(codec, + WCD9335_CDC_ANC1_CLK_RESET_CTL, 0x38, 0x00); + } + break; + } + + return 0; +err: + if (!hwdep_cal) + release_firmware(fw); + return ret; +} + +static void tasha_codec_clear_anc_tx_hold(struct tasha_priv *tasha) +{ + if (test_and_clear_bit(ANC_MIC_AMIC1, &tasha->status_mask)) + tasha_codec_set_tx_hold(tasha->codec, WCD9335_ANA_AMIC1, false); + if (test_and_clear_bit(ANC_MIC_AMIC2, &tasha->status_mask)) + tasha_codec_set_tx_hold(tasha->codec, WCD9335_ANA_AMIC2, false); + if (test_and_clear_bit(ANC_MIC_AMIC3, &tasha->status_mask)) + tasha_codec_set_tx_hold(tasha->codec, WCD9335_ANA_AMIC3, false); + if (test_and_clear_bit(ANC_MIC_AMIC4, &tasha->status_mask)) + tasha_codec_set_tx_hold(tasha->codec, WCD9335_ANA_AMIC4, false); + if (test_and_clear_bit(ANC_MIC_AMIC5, &tasha->status_mask)) + tasha_codec_set_tx_hold(tasha->codec, WCD9335_ANA_AMIC5, false); + if (test_and_clear_bit(ANC_MIC_AMIC6, &tasha->status_mask)) + tasha_codec_set_tx_hold(tasha->codec, WCD9335_ANA_AMIC6, false); +} + +static void tasha_codec_hph_post_pa_config(struct tasha_priv *tasha, + int mode, int event) +{ + u8 scale_val = 0; + + if (!TASHA_IS_2_0(tasha->wcd9xxx)) + return; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + switch (mode) { + case CLS_H_HIFI: + scale_val = 0x3; + break; + case CLS_H_LOHIFI: + scale_val = 0x1; + break; + } + if (tasha->anc_func) { + /* Clear Tx FE HOLD if both PAs are enabled */ + if ((snd_soc_read(tasha->codec, WCD9335_ANA_HPH) & + 0xC0) == 0xC0) { + tasha_codec_clear_anc_tx_hold(tasha); + } + } + break; + case SND_SOC_DAPM_PRE_PMD: + scale_val = 0x6; + break; + } + + if (scale_val) + snd_soc_update_bits(tasha->codec, WCD9335_HPH_PA_CTL1, 0x0E, + scale_val << 1); + if (SND_SOC_DAPM_EVENT_ON(event)) { + if (tasha->comp_enabled[COMPANDER_1] || + tasha->comp_enabled[COMPANDER_2]) { + snd_soc_update_bits(tasha->codec, WCD9335_HPH_L_EN, + 0x20, 0x00); + snd_soc_update_bits(tasha->codec, WCD9335_HPH_R_EN, + 0x20, 0x00); + snd_soc_update_bits(tasha->codec, WCD9335_HPH_AUTO_CHOP, + 0x20, 0x20); + } + snd_soc_update_bits(tasha->codec, WCD9335_HPH_L_EN, 0x1F, + tasha->hph_l_gain); + snd_soc_update_bits(tasha->codec, WCD9335_HPH_R_EN, 0x1F, + tasha->hph_r_gain); + } + + if (SND_SOC_DAPM_EVENT_OFF(event)) { + snd_soc_update_bits(tasha->codec, WCD9335_HPH_AUTO_CHOP, 0x20, + 0x00); + } +} + +static void tasha_codec_override(struct snd_soc_codec *codec, + int mode, + int event) +{ + if (mode == CLS_AB) { + switch (event) { + case SND_SOC_DAPM_POST_PMU: + if (!(snd_soc_read(codec, + WCD9335_CDC_RX2_RX_PATH_CTL) & 0x10) && + (!(snd_soc_read(codec, + WCD9335_CDC_RX1_RX_PATH_CTL) & 0x10))) + snd_soc_update_bits(codec, + WCD9XXX_A_ANA_RX_SUPPLIES, 0x02, 0x02); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_update_bits(codec, + WCD9XXX_A_ANA_RX_SUPPLIES, 0x02, 0x00); + break; + } + } +} + +static int tasha_codec_enable_hphr_pa(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + int hph_mode = tasha->hph_mode; + int ret = 0; + + dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if ((!(strcmp(w->name, "ANC HPHR PA"))) && + (test_bit(HPH_PA_DELAY, &tasha->status_mask))) { + snd_soc_update_bits(codec, WCD9335_ANA_HPH, 0xC0, 0xC0); + } + set_bit(HPH_PA_DELAY, &tasha->status_mask); + if (!(strcmp(w->name, "HPHR PA"))) + snd_soc_update_bits(codec, WCD9335_ANA_HPH, 0x40, 0x40); + break; + case SND_SOC_DAPM_POST_PMU: + if (!(strcmp(w->name, "ANC HPHR PA"))) { + if ((snd_soc_read(codec, WCD9335_ANA_HPH) & 0xC0) + != 0xC0) + /* + * If PA_EN is not set (potentially in ANC case) + * then do nothing for POST_PMU and let left + * channel handle everything. + */ + break; + } + /* + * 7ms sleep is required after PA is enabled as per + * HW requirement + */ + if (test_bit(HPH_PA_DELAY, &tasha->status_mask)) { + usleep_range(7000, 7100); + clear_bit(HPH_PA_DELAY, &tasha->status_mask); + } + tasha_codec_hph_post_pa_config(tasha, hph_mode, event); + snd_soc_update_bits(codec, WCD9335_CDC_RX2_RX_PATH_CTL, + 0x10, 0x00); + /* Remove mix path mute if it is enabled */ + if ((snd_soc_read(codec, WCD9335_CDC_RX2_RX_PATH_MIX_CTL)) & + 0x10) + snd_soc_update_bits(codec, + WCD9335_CDC_RX2_RX_PATH_MIX_CTL, + 0x10, 0x00); + + if (!(strcmp(w->name, "ANC HPHR PA"))) { + /* Do everything needed for left channel */ + snd_soc_update_bits(codec, WCD9335_CDC_RX1_RX_PATH_CTL, + 0x10, 0x00); + /* Remove mix path mute if it is enabled */ + if ((snd_soc_read(codec, + WCD9335_CDC_RX1_RX_PATH_MIX_CTL)) & + 0x10) + snd_soc_update_bits(codec, + WCD9335_CDC_RX1_RX_PATH_MIX_CTL, + 0x10, 0x00); + /* Remove ANC Rx from reset */ + ret = tasha_codec_enable_anc(w, kcontrol, event); + } + tasha_codec_override(codec, hph_mode, event); + break; + + case SND_SOC_DAPM_PRE_PMD: + blocking_notifier_call_chain(&tasha->notifier, + WCD_EVENT_PRE_HPHR_PA_OFF, + &tasha->mbhc); + tasha_codec_hph_post_pa_config(tasha, hph_mode, event); + if (!(strcmp(w->name, "ANC HPHR PA")) || + !(strcmp(w->name, "HPHR PA"))) + snd_soc_update_bits(codec, WCD9335_ANA_HPH, 0x40, 0x00); + break; + case SND_SOC_DAPM_POST_PMD: + /* 5ms sleep is required after PA is disabled as per + * HW requirement + */ + usleep_range(5000, 5500); + tasha_codec_override(codec, hph_mode, event); + blocking_notifier_call_chain(&tasha->notifier, + WCD_EVENT_POST_HPHR_PA_OFF, + &tasha->mbhc); + + if (!(strcmp(w->name, "ANC HPHR PA"))) { + ret = tasha_codec_enable_anc(w, kcontrol, event); + snd_soc_update_bits(codec, + WCD9335_CDC_RX2_RX_PATH_CFG0, 0x10, 0x00); + } + break; + }; + + return ret; +} + +static int tasha_codec_enable_hphl_pa(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + int hph_mode = tasha->hph_mode; + int ret = 0; + + dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if ((!(strcmp(w->name, "ANC HPHL PA"))) && + (test_bit(HPH_PA_DELAY, &tasha->status_mask))) { + snd_soc_update_bits(codec, WCD9335_ANA_HPH, 0xC0, 0xC0); + } + if (!(strcmp(w->name, "HPHL PA"))) + snd_soc_update_bits(codec, WCD9335_ANA_HPH, 0x80, 0x80); + set_bit(HPH_PA_DELAY, &tasha->status_mask); + break; + case SND_SOC_DAPM_POST_PMU: + if (!(strcmp(w->name, "ANC HPHL PA"))) { + if ((snd_soc_read(codec, WCD9335_ANA_HPH) & 0xC0) + != 0xC0) + /* + * If PA_EN is not set (potentially in ANC case) + * then do nothing for POST_PMU and let right + * channel handle everything. + */ + break; + } + /* + * 7ms sleep is required after PA is enabled as per + * HW requirement + */ + if (test_bit(HPH_PA_DELAY, &tasha->status_mask)) { + usleep_range(7000, 7100); + clear_bit(HPH_PA_DELAY, &tasha->status_mask); + } + + tasha_codec_hph_post_pa_config(tasha, hph_mode, event); + snd_soc_update_bits(codec, WCD9335_CDC_RX1_RX_PATH_CTL, + 0x10, 0x00); + /* Remove mix path mute if it is enabled */ + if ((snd_soc_read(codec, WCD9335_CDC_RX1_RX_PATH_MIX_CTL)) & + 0x10) + snd_soc_update_bits(codec, + WCD9335_CDC_RX1_RX_PATH_MIX_CTL, + 0x10, 0x00); + + if (!(strcmp(w->name, "ANC HPHL PA"))) { + /* Do everything needed for right channel */ + snd_soc_update_bits(codec, WCD9335_CDC_RX2_RX_PATH_CTL, + 0x10, 0x00); + /* Remove mix path mute if it is enabled */ + if ((snd_soc_read(codec, + WCD9335_CDC_RX2_RX_PATH_MIX_CTL)) & + 0x10) + snd_soc_update_bits(codec, + WCD9335_CDC_RX2_RX_PATH_MIX_CTL, + 0x10, 0x00); + + /* Remove ANC Rx from reset */ + ret = tasha_codec_enable_anc(w, kcontrol, event); + } + tasha_codec_override(codec, hph_mode, event); + break; + case SND_SOC_DAPM_PRE_PMD: + blocking_notifier_call_chain(&tasha->notifier, + WCD_EVENT_PRE_HPHL_PA_OFF, + &tasha->mbhc); + tasha_codec_hph_post_pa_config(tasha, hph_mode, event); + if (!(strcmp(w->name, "ANC HPHL PA")) || + !(strcmp(w->name, "HPHL PA"))) + snd_soc_update_bits(codec, WCD9335_ANA_HPH, 0x80, 0x00); + break; + case SND_SOC_DAPM_POST_PMD: + /* 5ms sleep is required after PA is disabled as per + * HW requirement + */ + usleep_range(5000, 5500); + tasha_codec_override(codec, hph_mode, event); + blocking_notifier_call_chain(&tasha->notifier, + WCD_EVENT_POST_HPHL_PA_OFF, + &tasha->mbhc); + + if (!(strcmp(w->name, "ANC HPHL PA"))) { + ret = tasha_codec_enable_anc(w, kcontrol, event); + snd_soc_update_bits(codec, + WCD9335_CDC_RX1_RX_PATH_CFG0, 0x10, 0x00); + } + break; + }; + + return ret; +} + +static int tasha_codec_enable_lineout_pa(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + u16 lineout_vol_reg = 0, lineout_mix_vol_reg = 0; + int ret = 0; + + dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event); + + if (w->reg == WCD9335_ANA_LO_1_2) { + if (w->shift == 7) { + lineout_vol_reg = WCD9335_CDC_RX3_RX_PATH_CTL; + lineout_mix_vol_reg = WCD9335_CDC_RX3_RX_PATH_MIX_CTL; + } else if (w->shift == 6) { + lineout_vol_reg = WCD9335_CDC_RX4_RX_PATH_CTL; + lineout_mix_vol_reg = WCD9335_CDC_RX4_RX_PATH_MIX_CTL; + } + } else if (w->reg == WCD9335_ANA_LO_3_4) { + if (w->shift == 7) { + lineout_vol_reg = WCD9335_CDC_RX5_RX_PATH_CTL; + lineout_mix_vol_reg = WCD9335_CDC_RX5_RX_PATH_MIX_CTL; + } else if (w->shift == 6) { + lineout_vol_reg = WCD9335_CDC_RX6_RX_PATH_CTL; + lineout_mix_vol_reg = WCD9335_CDC_RX6_RX_PATH_MIX_CTL; + } + } else { + dev_err(codec->dev, "%s: Error enabling lineout PA\n", + __func__); + return -EINVAL; + } + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + /* 5ms sleep is required after PA is enabled as per + * HW requirement + */ + usleep_range(5000, 5500); + snd_soc_update_bits(codec, lineout_vol_reg, + 0x10, 0x00); + /* Remove mix path mute if it is enabled */ + if ((snd_soc_read(codec, lineout_mix_vol_reg)) & 0x10) + snd_soc_update_bits(codec, + lineout_mix_vol_reg, + 0x10, 0x00); + if (!(strcmp(w->name, "ANC LINEOUT1 PA")) || + !(strcmp(w->name, "ANC LINEOUT2 PA"))) + ret = tasha_codec_enable_anc(w, kcontrol, event); + tasha_codec_override(codec, CLS_AB, event); + break; + case SND_SOC_DAPM_POST_PMD: + /* 5ms sleep is required after PA is disabled as per + * HW requirement + */ + usleep_range(5000, 5500); + tasha_codec_override(codec, CLS_AB, event); + if (!(strcmp(w->name, "ANC LINEOUT1 PA")) || + !(strcmp(w->name, "ANC LINEOUT2 PA"))) { + ret = tasha_codec_enable_anc(w, kcontrol, event); + if (!(strcmp(w->name, "ANC LINEOUT1 PA"))) + snd_soc_update_bits(codec, + WCD9335_CDC_RX3_RX_PATH_CFG0, 0x10, 0x10); + else + snd_soc_update_bits(codec, + WCD9335_CDC_RX4_RX_PATH_CFG0, 0x10, 0x10); + } + break; + }; + + return ret; +} + +static void tasha_spk_anc_update_callback(struct work_struct *work) +{ + struct spk_anc_work *spk_anc_dwork; + struct tasha_priv *tasha; + struct delayed_work *delayed_work; + struct snd_soc_codec *codec; + + delayed_work = to_delayed_work(work); + spk_anc_dwork = container_of(delayed_work, struct spk_anc_work, dwork); + tasha = spk_anc_dwork->tasha; + codec = tasha->codec; + + snd_soc_update_bits(codec, WCD9335_CDC_RX7_RX_PATH_CFG0, 0x10, 0x10); +} + +static int tasha_codec_enable_spk_anc(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + int ret = 0; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, "%s %s %d %d\n", __func__, w->name, event, + tasha->anc_func); + + if (!tasha->anc_func) + return 0; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + ret = tasha_codec_enable_anc(w, kcontrol, event); + schedule_delayed_work(&tasha->spk_anc_dwork.dwork, + msecs_to_jiffies(spk_anc_en_delay)); + break; + case SND_SOC_DAPM_POST_PMD: + cancel_delayed_work_sync(&tasha->spk_anc_dwork.dwork); + snd_soc_update_bits(codec, WCD9335_CDC_RX7_RX_PATH_CFG0, + 0x10, 0x00); + ret = tasha_codec_enable_anc(w, kcontrol, event); + break; + } + return ret; +} + +static int tasha_codec_enable_ear_pa(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + int ret = 0; + + dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + /* 5ms sleep is required after PA is enabled as per + * HW requirement + */ + usleep_range(5000, 5500); + snd_soc_update_bits(codec, WCD9335_CDC_RX0_RX_PATH_CTL, + 0x10, 0x00); + /* Remove mix path mute if it is enabled */ + if ((snd_soc_read(codec, WCD9335_CDC_RX0_RX_PATH_MIX_CTL)) & + 0x10) + snd_soc_update_bits(codec, + WCD9335_CDC_RX0_RX_PATH_MIX_CTL, + 0x10, 0x00); + break; + case SND_SOC_DAPM_POST_PMD: + /* 5ms sleep is required after PA is disabled as per + * HW requirement + */ + usleep_range(5000, 5500); + + if (!(strcmp(w->name, "ANC EAR PA"))) { + ret = tasha_codec_enable_anc(w, kcontrol, event); + snd_soc_update_bits(codec, + WCD9335_CDC_RX0_RX_PATH_CFG0, 0x10, 0x00); + } + break; + }; + + return ret; +} + +static void tasha_codec_hph_mode_gain_opt(struct snd_soc_codec *codec, + u8 gain) +{ + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + u8 hph_l_en, hph_r_en; + u8 l_val, r_val; + u8 hph_pa_status; + bool is_hphl_pa, is_hphr_pa; + + hph_pa_status = snd_soc_read(codec, WCD9335_ANA_HPH); + is_hphl_pa = hph_pa_status >> 7; + is_hphr_pa = (hph_pa_status & 0x40) >> 6; + + hph_l_en = snd_soc_read(codec, WCD9335_HPH_L_EN); + hph_r_en = snd_soc_read(codec, WCD9335_HPH_R_EN); + + l_val = (hph_l_en & 0xC0) | 0x20 | gain; + r_val = (hph_r_en & 0xC0) | 0x20 | gain; + + /* + * Set HPH_L & HPH_R gain source selection to REGISTER + * for better click and pop only if corresponding PAs are + * not enabled. Also cache the values of the HPHL/R + * PA gains to be applied after PAs are enabled + */ + if ((l_val != hph_l_en) && !is_hphl_pa) { + snd_soc_write(codec, WCD9335_HPH_L_EN, l_val); + tasha->hph_l_gain = hph_l_en & 0x1F; + } + + if ((r_val != hph_r_en) && !is_hphr_pa) { + snd_soc_write(codec, WCD9335_HPH_R_EN, r_val); + tasha->hph_r_gain = hph_r_en & 0x1F; + } +} + +static void tasha_codec_hph_lohifi_config(struct snd_soc_codec *codec, + int event) +{ + if (SND_SOC_DAPM_EVENT_ON(event)) { + snd_soc_update_bits(codec, WCD9335_RX_BIAS_HPH_PA, 0x0F, 0x06); + snd_soc_update_bits(codec, WCD9335_RX_BIAS_HPH_RDACBUFF_CNP2, + 0xF0, 0x40); + snd_soc_update_bits(codec, WCD9335_HPH_CNP_WG_CTL, 0x07, 0x03); + snd_soc_update_bits(codec, WCD9335_HPH_PA_CTL2, 0x08, 0x08); + snd_soc_update_bits(codec, WCD9335_HPH_PA_CTL1, 0x0E, 0x0C); + tasha_codec_hph_mode_gain_opt(codec, 0x11); + } + + if (SND_SOC_DAPM_EVENT_OFF(event)) { + snd_soc_update_bits(codec, WCD9335_HPH_PA_CTL2, 0x08, 0x00); + snd_soc_update_bits(codec, WCD9335_HPH_CNP_WG_CTL, 0x07, 0x02); + snd_soc_write(codec, WCD9335_RX_BIAS_HPH_RDACBUFF_CNP2, 0x8A); + snd_soc_update_bits(codec, WCD9335_RX_BIAS_HPH_PA, 0x0F, 0x0A); + } +} + +static void tasha_codec_hph_lp_config(struct snd_soc_codec *codec, + int event) +{ + if (SND_SOC_DAPM_EVENT_ON(event)) { + snd_soc_update_bits(codec, WCD9335_HPH_PA_CTL1, 0x0E, 0x0C); + tasha_codec_hph_mode_gain_opt(codec, 0x10); + snd_soc_update_bits(codec, WCD9335_HPH_CNP_WG_CTL, 0x07, 0x03); + snd_soc_update_bits(codec, WCD9335_HPH_PA_CTL2, 0x08, 0x08); + snd_soc_update_bits(codec, WCD9335_HPH_PA_CTL2, 0x04, 0x04); + snd_soc_update_bits(codec, WCD9335_HPH_PA_CTL2, 0x20, 0x20); + snd_soc_update_bits(codec, WCD9335_HPH_RDAC_LDO_CTL, 0x07, + 0x01); + snd_soc_update_bits(codec, WCD9335_HPH_RDAC_LDO_CTL, 0x70, + 0x10); + snd_soc_update_bits(codec, WCD9335_RX_BIAS_HPH_RDAC_LDO, + 0x0F, 0x01); + snd_soc_update_bits(codec, WCD9335_RX_BIAS_HPH_RDAC_LDO, + 0xF0, 0x10); + } + + if (SND_SOC_DAPM_EVENT_OFF(event)) { + snd_soc_write(codec, WCD9335_RX_BIAS_HPH_RDAC_LDO, 0x88); + snd_soc_write(codec, WCD9335_HPH_RDAC_LDO_CTL, 0x33); + snd_soc_update_bits(codec, WCD9335_HPH_PA_CTL2, 0x20, 0x00); + snd_soc_update_bits(codec, WCD9335_HPH_PA_CTL2, 0x04, 0x00); + snd_soc_update_bits(codec, WCD9335_HPH_PA_CTL2, 0x08, 0x00); + snd_soc_update_bits(codec, WCD9335_HPH_CNP_WG_CTL, 0x07, 0x02); + snd_soc_update_bits(codec, WCD9335_HPH_R_EN, 0xC0, 0x80); + snd_soc_update_bits(codec, WCD9335_HPH_L_EN, 0xC0, 0x80); + } +} + +static void tasha_codec_hph_hifi_config(struct snd_soc_codec *codec, + int event) +{ + if (SND_SOC_DAPM_EVENT_ON(event)) { + snd_soc_update_bits(codec, WCD9335_HPH_CNP_WG_CTL, 0x07, 0x03); + snd_soc_update_bits(codec, WCD9335_HPH_PA_CTL2, 0x08, 0x08); + snd_soc_update_bits(codec, WCD9335_HPH_PA_CTL1, 0x0E, 0x0C); + tasha_codec_hph_mode_gain_opt(codec, 0x11); + } + + if (SND_SOC_DAPM_EVENT_OFF(event)) { + snd_soc_update_bits(codec, WCD9335_HPH_PA_CTL2, 0x08, 0x00); + snd_soc_update_bits(codec, WCD9335_HPH_CNP_WG_CTL, 0x07, 0x02); + } +} + +static void tasha_codec_hph_mode_config(struct snd_soc_codec *codec, + int event, int mode) +{ + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + + if (!TASHA_IS_2_0(tasha->wcd9xxx)) + return; + + switch (mode) { + case CLS_H_LP: + tasha_codec_hph_lp_config(codec, event); + break; + case CLS_H_LOHIFI: + tasha_codec_hph_lohifi_config(codec, event); + break; + case CLS_H_HIFI: + tasha_codec_hph_hifi_config(codec, event); + break; + } +} + +static int tasha_codec_hphr_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent); + int hph_mode = tasha->hph_mode; + u8 dem_inp; + int ret = 0; + + dev_dbg(codec->dev, "%s wname: %s event: %d hph_mode: %d\n", __func__, + w->name, event, hph_mode); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (tasha->anc_func) { + ret = tasha_codec_enable_anc(w, kcontrol, event); + /* 40 msec delay is needed to avoid click and pop */ + msleep(40); + } + + /* Read DEM INP Select */ + dem_inp = snd_soc_read(codec, WCD9335_CDC_RX2_RX_PATH_SEC0) & + 0x03; + if (((hph_mode == CLS_H_HIFI) || (hph_mode == CLS_H_LOHIFI) || + (hph_mode == CLS_H_LP)) && (dem_inp != 0x01)) { + dev_err(codec->dev, "%s: DEM Input not set correctly, hph_mode: %d\n", + __func__, hph_mode); + return -EINVAL; + } + wcd_clsh_fsm(codec, &tasha->clsh_d, + WCD_CLSH_EVENT_PRE_DAC, + WCD_CLSH_STATE_HPHR, + ((hph_mode == CLS_H_LOHIFI) ? + CLS_H_HIFI : hph_mode)); + + if (!(strcmp(w->name, "RX INT2 DAC"))) + snd_soc_update_bits(codec, WCD9335_ANA_HPH, 0x10, 0x10); + + tasha_codec_hph_mode_config(codec, event, hph_mode); + + if (tasha->anc_func) + snd_soc_update_bits(codec, + WCD9335_CDC_RX2_RX_PATH_CFG0, 0x10, 0x10); + + break; + case SND_SOC_DAPM_POST_PMU: + /* 1000us required as per HW requirement */ + usleep_range(1000, 1100); + if ((hph_mode == CLS_H_LP) && + (TASHA_IS_1_1(wcd9xxx))) { + snd_soc_update_bits(codec, WCD9335_HPH_L_DAC_CTL, + 0x03, 0x03); + } + break; + case SND_SOC_DAPM_PRE_PMD: + if ((hph_mode == CLS_H_LP) && + (TASHA_IS_1_1(wcd9xxx))) { + snd_soc_update_bits(codec, WCD9335_HPH_L_DAC_CTL, + 0x03, 0x00); + } + if (!(strcmp(w->name, "RX INT2 DAC"))) + snd_soc_update_bits(codec, WCD9335_ANA_HPH, 0x10, 0x00); + break; + case SND_SOC_DAPM_POST_PMD: + /* 1000us required as per HW requirement */ + usleep_range(1000, 1100); + + if (!(wcd_clsh_get_clsh_state(&tasha->clsh_d) & + WCD_CLSH_STATE_HPHL)) + tasha_codec_hph_mode_config(codec, event, hph_mode); + + wcd_clsh_fsm(codec, &tasha->clsh_d, + WCD_CLSH_EVENT_POST_PA, + WCD_CLSH_STATE_HPHR, + ((hph_mode == CLS_H_LOHIFI) ? + CLS_H_HIFI : hph_mode)); + break; + }; + + return ret; +} + +static int tasha_codec_hphl_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent); + int hph_mode = tasha->hph_mode; + u8 dem_inp; + int ret = 0; + uint32_t impedl = 0, impedr = 0; + + dev_dbg(codec->dev, "%s wname: %s event: %d hph_mode: %d\n", __func__, + w->name, event, hph_mode); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (tasha->anc_func) { + ret = tasha_codec_enable_anc(w, kcontrol, event); + /* 40 msec delay is needed to avoid click and pop */ + msleep(40); + } + + /* Read DEM INP Select */ + dem_inp = snd_soc_read(codec, WCD9335_CDC_RX1_RX_PATH_SEC0) & + 0x03; + if (((hph_mode == CLS_H_HIFI) || (hph_mode == CLS_H_LOHIFI) || + (hph_mode == CLS_H_LP)) && (dem_inp != 0x01)) { + dev_err(codec->dev, "%s: DEM Input not set correctly, hph_mode: %d\n", + __func__, hph_mode); + return -EINVAL; + } + wcd_clsh_fsm(codec, &tasha->clsh_d, + WCD_CLSH_EVENT_PRE_DAC, + WCD_CLSH_STATE_HPHL, + ((hph_mode == CLS_H_LOHIFI) ? + CLS_H_HIFI : hph_mode)); + + if (!(strcmp(w->name, "RX INT1 DAC"))) + snd_soc_update_bits(codec, WCD9335_ANA_HPH, 0x20, 0x20); + + tasha_codec_hph_mode_config(codec, event, hph_mode); + + if (tasha->anc_func) + snd_soc_update_bits(codec, + WCD9335_CDC_RX1_RX_PATH_CFG0, 0x10, 0x10); + + ret = wcd_mbhc_get_impedance(&tasha->mbhc, + &impedl, &impedr); + if (!ret) { + wcd_clsh_imped_config(codec, impedl, false); + set_bit(CLASSH_CONFIG, &tasha->status_mask); + } else { + dev_dbg(codec->dev, "%s: Failed to get mbhc impedance %d\n", + __func__, ret); + ret = 0; + } + + + break; + case SND_SOC_DAPM_POST_PMU: + /* 1000us required as per HW requirement */ + usleep_range(1000, 1100); + if ((hph_mode == CLS_H_LP) && + (TASHA_IS_1_1(wcd9xxx))) { + snd_soc_update_bits(codec, WCD9335_HPH_L_DAC_CTL, + 0x03, 0x03); + } + break; + case SND_SOC_DAPM_PRE_PMD: + if (!(strcmp(w->name, "RX INT1 DAC"))) + snd_soc_update_bits(codec, WCD9335_ANA_HPH, 0x20, 0x00); + if ((hph_mode == CLS_H_LP) && + (TASHA_IS_1_1(wcd9xxx))) { + snd_soc_update_bits(codec, WCD9335_HPH_L_DAC_CTL, + 0x03, 0x00); + } + break; + case SND_SOC_DAPM_POST_PMD: + /* 1000us required as per HW requirement */ + usleep_range(1000, 1100); + + if (!(wcd_clsh_get_clsh_state(&tasha->clsh_d) & + WCD_CLSH_STATE_HPHR)) + tasha_codec_hph_mode_config(codec, event, hph_mode); + wcd_clsh_fsm(codec, &tasha->clsh_d, + WCD_CLSH_EVENT_POST_PA, + WCD_CLSH_STATE_HPHL, + ((hph_mode == CLS_H_LOHIFI) ? + CLS_H_HIFI : hph_mode)); + + if (test_bit(CLASSH_CONFIG, &tasha->status_mask)) { + wcd_clsh_imped_config(codec, impedl, true); + clear_bit(CLASSH_CONFIG, &tasha->status_mask); + } else + dev_dbg(codec->dev, "%s: Failed to get mbhc impedance %d\n", + __func__, ret); + + + break; + }; + + return ret; +} + +static int tasha_codec_lineout_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + int ret = 0; + + dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (tasha->anc_func && + (!strcmp(w->name, "RX INT3 DAC") || + !strcmp(w->name, "RX INT4 DAC"))) + ret = tasha_codec_enable_anc(w, kcontrol, event); + + wcd_clsh_fsm(codec, &tasha->clsh_d, + WCD_CLSH_EVENT_PRE_DAC, + WCD_CLSH_STATE_LO, + CLS_AB); + + if (tasha->anc_func) { + if (!strcmp(w->name, "RX INT3 DAC")) + snd_soc_update_bits(codec, + WCD9335_CDC_RX3_RX_PATH_CFG0, 0x10, 0x10); + else if (!strcmp(w->name, "RX INT4 DAC")) + snd_soc_update_bits(codec, + WCD9335_CDC_RX4_RX_PATH_CFG0, 0x10, 0x10); + } + break; + case SND_SOC_DAPM_POST_PMD: + wcd_clsh_fsm(codec, &tasha->clsh_d, + WCD_CLSH_EVENT_POST_PA, + WCD_CLSH_STATE_LO, + CLS_AB); + break; + } + + return 0; +} + +static const struct snd_soc_dapm_widget tasha_dapm_i2s_widgets[] = { + SND_SOC_DAPM_SUPPLY("RX_I2S_CTL", WCD9335_DATA_HUB_DATA_HUB_RX_I2S_CTL, + 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("TX_I2S_CTL", WCD9335_DATA_HUB_DATA_HUB_TX_I2S_CTL, + 0, 0, NULL, 0), +}; + +static int tasha_codec_ear_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + int ret = 0; + + dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (tasha->anc_func) + ret = tasha_codec_enable_anc(w, kcontrol, event); + + wcd_clsh_fsm(codec, &tasha->clsh_d, + WCD_CLSH_EVENT_PRE_DAC, + WCD_CLSH_STATE_EAR, + CLS_H_NORMAL); + if (tasha->anc_func) + snd_soc_update_bits(codec, + WCD9335_CDC_RX0_RX_PATH_CFG0, 0x10, 0x10); + + break; + case SND_SOC_DAPM_POST_PMU: + break; + case SND_SOC_DAPM_PRE_PMD: + break; + case SND_SOC_DAPM_POST_PMD: + wcd_clsh_fsm(codec, &tasha->clsh_d, + WCD_CLSH_EVENT_POST_PA, + WCD_CLSH_STATE_EAR, + CLS_H_NORMAL); + break; + }; + + return ret; +} + +static int tasha_codec_spk_boost_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + u16 boost_path_ctl, boost_path_cfg1; + u16 reg, reg_mix; + + dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event); + + if (!strcmp(w->name, "RX INT7 CHAIN")) { + boost_path_ctl = WCD9335_CDC_BOOST0_BOOST_PATH_CTL; + boost_path_cfg1 = WCD9335_CDC_RX7_RX_PATH_CFG1; + reg = WCD9335_CDC_RX7_RX_PATH_CTL; + reg_mix = WCD9335_CDC_RX7_RX_PATH_MIX_CTL; + } else if (!strcmp(w->name, "RX INT8 CHAIN")) { + boost_path_ctl = WCD9335_CDC_BOOST1_BOOST_PATH_CTL; + boost_path_cfg1 = WCD9335_CDC_RX8_RX_PATH_CFG1; + reg = WCD9335_CDC_RX8_RX_PATH_CTL; + reg_mix = WCD9335_CDC_RX8_RX_PATH_MIX_CTL; + } else { + dev_err(codec->dev, "%s: unknown widget: %s\n", + __func__, w->name); + return -EINVAL; + } + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_update_bits(codec, boost_path_ctl, 0x10, 0x10); + snd_soc_update_bits(codec, boost_path_cfg1, 0x01, 0x01); + snd_soc_update_bits(codec, reg, 0x10, 0x00); + if ((snd_soc_read(codec, reg_mix)) & 0x10) + snd_soc_update_bits(codec, reg_mix, 0x10, 0x00); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_update_bits(codec, boost_path_cfg1, 0x01, 0x00); + snd_soc_update_bits(codec, boost_path_ctl, 0x10, 0x00); + break; + }; + + return 0; +} + +static u16 tasha_interp_get_primary_reg(u16 reg, u16 *ind) +{ + u16 prim_int_reg = 0; + + switch (reg) { + case WCD9335_CDC_RX0_RX_PATH_CTL: + case WCD9335_CDC_RX0_RX_PATH_MIX_CTL: + prim_int_reg = WCD9335_CDC_RX0_RX_PATH_CTL; + *ind = 0; + break; + case WCD9335_CDC_RX1_RX_PATH_CTL: + case WCD9335_CDC_RX1_RX_PATH_MIX_CTL: + prim_int_reg = WCD9335_CDC_RX1_RX_PATH_CTL; + *ind = 1; + break; + case WCD9335_CDC_RX2_RX_PATH_CTL: + case WCD9335_CDC_RX2_RX_PATH_MIX_CTL: + prim_int_reg = WCD9335_CDC_RX2_RX_PATH_CTL; + *ind = 2; + break; + case WCD9335_CDC_RX3_RX_PATH_CTL: + case WCD9335_CDC_RX3_RX_PATH_MIX_CTL: + prim_int_reg = WCD9335_CDC_RX3_RX_PATH_CTL; + *ind = 3; + break; + case WCD9335_CDC_RX4_RX_PATH_CTL: + case WCD9335_CDC_RX4_RX_PATH_MIX_CTL: + prim_int_reg = WCD9335_CDC_RX4_RX_PATH_CTL; + *ind = 4; + break; + case WCD9335_CDC_RX5_RX_PATH_CTL: + case WCD9335_CDC_RX5_RX_PATH_MIX_CTL: + prim_int_reg = WCD9335_CDC_RX5_RX_PATH_CTL; + *ind = 5; + break; + case WCD9335_CDC_RX6_RX_PATH_CTL: + case WCD9335_CDC_RX6_RX_PATH_MIX_CTL: + prim_int_reg = WCD9335_CDC_RX6_RX_PATH_CTL; + *ind = 6; + break; + case WCD9335_CDC_RX7_RX_PATH_CTL: + case WCD9335_CDC_RX7_RX_PATH_MIX_CTL: + prim_int_reg = WCD9335_CDC_RX7_RX_PATH_CTL; + *ind = 7; + break; + case WCD9335_CDC_RX8_RX_PATH_CTL: + case WCD9335_CDC_RX8_RX_PATH_MIX_CTL: + prim_int_reg = WCD9335_CDC_RX8_RX_PATH_CTL; + *ind = 8; + break; + }; + + return prim_int_reg; +} + +static void tasha_codec_hd2_control(struct snd_soc_codec *codec, + u16 prim_int_reg, int event) +{ + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + u16 hd2_scale_reg; + u16 hd2_enable_reg = 0; + + if (!TASHA_IS_2_0(tasha->wcd9xxx)) + return; + + if (prim_int_reg == WCD9335_CDC_RX1_RX_PATH_CTL) { + hd2_scale_reg = WCD9335_CDC_RX1_RX_PATH_SEC3; + hd2_enable_reg = WCD9335_CDC_RX1_RX_PATH_CFG0; + } + if (prim_int_reg == WCD9335_CDC_RX2_RX_PATH_CTL) { + hd2_scale_reg = WCD9335_CDC_RX2_RX_PATH_SEC3; + hd2_enable_reg = WCD9335_CDC_RX2_RX_PATH_CFG0; + } + + if (hd2_enable_reg && SND_SOC_DAPM_EVENT_ON(event)) { + snd_soc_update_bits(codec, hd2_scale_reg, 0x3C, 0x10); + snd_soc_update_bits(codec, hd2_scale_reg, 0x03, 0x01); + snd_soc_update_bits(codec, hd2_enable_reg, 0x04, 0x04); + } + + if (hd2_enable_reg && SND_SOC_DAPM_EVENT_OFF(event)) { + snd_soc_update_bits(codec, hd2_enable_reg, 0x04, 0x00); + snd_soc_update_bits(codec, hd2_scale_reg, 0x03, 0x00); + snd_soc_update_bits(codec, hd2_scale_reg, 0x3C, 0x00); + } +} + +static int tasha_codec_enable_prim_interpolator( + struct snd_soc_codec *codec, + u16 reg, int event) +{ + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + u16 prim_int_reg; + u16 ind = 0; + + prim_int_reg = tasha_interp_get_primary_reg(reg, &ind); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + tasha->prim_int_users[ind]++; + if (tasha->prim_int_users[ind] == 1) { + snd_soc_update_bits(codec, prim_int_reg, + 0x10, 0x10); + tasha_codec_hd2_control(codec, prim_int_reg, event); + snd_soc_update_bits(codec, prim_int_reg, + 1 << 0x5, 1 << 0x5); + } + if ((reg != prim_int_reg) && + ((snd_soc_read(codec, prim_int_reg)) & 0x10)) + snd_soc_update_bits(codec, reg, 0x10, 0x10); + break; + case SND_SOC_DAPM_POST_PMD: + tasha->prim_int_users[ind]--; + if (tasha->prim_int_users[ind] == 0) { + snd_soc_update_bits(codec, prim_int_reg, + 1 << 0x5, 0 << 0x5); + snd_soc_update_bits(codec, prim_int_reg, + 0x40, 0x40); + snd_soc_update_bits(codec, prim_int_reg, + 0x40, 0x00); + tasha_codec_hd2_control(codec, prim_int_reg, event); + } + break; + }; + + dev_dbg(codec->dev, "%s: primary interpolator: INT%d, users: %d\n", + __func__, ind, tasha->prim_int_users[ind]); + return 0; +} + +static int tasha_codec_enable_spline_src(struct snd_soc_codec *codec, + int src_num, + int event) +{ + u16 src_paired_reg = 0; + struct tasha_priv *tasha; + u16 rx_path_cfg_reg = WCD9335_CDC_RX1_RX_PATH_CFG0; + u16 rx_path_ctl_reg = WCD9335_CDC_RX1_RX_PATH_CTL; + int *src_users, count, spl_src = SPLINE_SRC0; + u16 src_clk_reg = WCD9335_SPLINE_SRC0_CLK_RST_CTL_0; + + tasha = snd_soc_codec_get_drvdata(codec); + + switch (src_num) { + case SRC_IN_HPHL: + rx_path_cfg_reg = WCD9335_CDC_RX1_RX_PATH_CFG0; + src_clk_reg = WCD9335_SPLINE_SRC0_CLK_RST_CTL_0; + src_paired_reg = WCD9335_SPLINE_SRC1_CLK_RST_CTL_0; + rx_path_ctl_reg = WCD9335_CDC_RX1_RX_PATH_CTL; + spl_src = SPLINE_SRC0; + break; + case SRC_IN_LO1: + rx_path_cfg_reg = WCD9335_CDC_RX3_RX_PATH_CFG0; + src_clk_reg = WCD9335_SPLINE_SRC0_CLK_RST_CTL_0; + src_paired_reg = WCD9335_SPLINE_SRC1_CLK_RST_CTL_0; + rx_path_ctl_reg = WCD9335_CDC_RX3_RX_PATH_CTL; + spl_src = SPLINE_SRC0; + break; + case SRC_IN_HPHR: + rx_path_cfg_reg = WCD9335_CDC_RX2_RX_PATH_CFG0; + src_clk_reg = WCD9335_SPLINE_SRC1_CLK_RST_CTL_0; + src_paired_reg = WCD9335_SPLINE_SRC0_CLK_RST_CTL_0; + rx_path_ctl_reg = WCD9335_CDC_RX2_RX_PATH_CTL; + spl_src = SPLINE_SRC1; + break; + case SRC_IN_LO2: + rx_path_cfg_reg = WCD9335_CDC_RX4_RX_PATH_CFG0; + src_clk_reg = WCD9335_SPLINE_SRC1_CLK_RST_CTL_0; + src_paired_reg = WCD9335_SPLINE_SRC0_CLK_RST_CTL_0; + rx_path_ctl_reg = WCD9335_CDC_RX4_RX_PATH_CTL; + spl_src = SPLINE_SRC1; + break; + case SRC_IN_SPKRL: + rx_path_cfg_reg = WCD9335_CDC_RX7_RX_PATH_CFG0; + src_clk_reg = WCD9335_SPLINE_SRC2_CLK_RST_CTL_0; + src_paired_reg = WCD9335_SPLINE_SRC3_CLK_RST_CTL_0; + rx_path_ctl_reg = WCD9335_CDC_RX7_RX_PATH_CTL; + spl_src = SPLINE_SRC2; + break; + case SRC_IN_LO3: + rx_path_cfg_reg = WCD9335_CDC_RX5_RX_PATH_CFG0; + src_clk_reg = WCD9335_SPLINE_SRC2_CLK_RST_CTL_0; + src_paired_reg = WCD9335_SPLINE_SRC3_CLK_RST_CTL_0; + rx_path_ctl_reg = WCD9335_CDC_RX5_RX_PATH_CTL; + spl_src = SPLINE_SRC2; + break; + case SRC_IN_SPKRR: + rx_path_cfg_reg = WCD9335_CDC_RX8_RX_PATH_CFG0; + src_clk_reg = WCD9335_SPLINE_SRC3_CLK_RST_CTL_0; + src_paired_reg = WCD9335_SPLINE_SRC2_CLK_RST_CTL_0; + rx_path_ctl_reg = WCD9335_CDC_RX8_RX_PATH_CTL; + spl_src = SPLINE_SRC3; + break; + case SRC_IN_LO4: + rx_path_cfg_reg = WCD9335_CDC_RX6_RX_PATH_CFG0; + src_clk_reg = WCD9335_SPLINE_SRC3_CLK_RST_CTL_0; + src_paired_reg = WCD9335_SPLINE_SRC2_CLK_RST_CTL_0; + rx_path_ctl_reg = WCD9335_CDC_RX6_RX_PATH_CTL; + spl_src = SPLINE_SRC3; + break; + }; + + src_users = &tasha->spl_src_users[spl_src]; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + count = *src_users; + count++; + if (count == 1) { + if ((snd_soc_read(codec, src_clk_reg) & 0x02) || + (snd_soc_read(codec, src_paired_reg) & 0x02)) { + snd_soc_update_bits(codec, src_clk_reg, 0x02, + 0x00); + snd_soc_update_bits(codec, src_paired_reg, + 0x02, 0x00); + } + snd_soc_update_bits(codec, src_clk_reg, 0x01, 0x01); + snd_soc_update_bits(codec, rx_path_cfg_reg, 0x80, + 0x80); + } + *src_users = count; + break; + case SND_SOC_DAPM_POST_PMD: + count = *src_users; + count--; + if (count == 0) { + snd_soc_update_bits(codec, rx_path_cfg_reg, 0x80, + 0x00); + snd_soc_update_bits(codec, src_clk_reg, 0x03, 0x02); + /* default sample rate */ + snd_soc_update_bits(codec, rx_path_ctl_reg, 0x0f, + 0x04); + } + *src_users = count; + break; + }; + + dev_dbg(codec->dev, "%s: Spline SRC%d, users: %d\n", + __func__, spl_src, *src_users); + return 0; +} + +static int tasha_codec_enable_spline_resampler(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + int ret = 0; + u8 src_in; + + src_in = snd_soc_read(codec, WCD9335_CDC_RX_INP_MUX_SPLINE_SRC_CFG0); + if (!(src_in & 0xFF)) { + dev_err(codec->dev, "%s: Spline SRC%u input not selected\n", + __func__, w->shift); + return -EINVAL; + } + + switch (w->shift) { + case SPLINE_SRC0: + ret = tasha_codec_enable_spline_src(codec, + ((src_in & 0x03) == 1) ? SRC_IN_HPHL : SRC_IN_LO1, + event); + break; + case SPLINE_SRC1: + ret = tasha_codec_enable_spline_src(codec, + ((src_in & 0x0C) == 4) ? SRC_IN_HPHR : SRC_IN_LO2, + event); + break; + case SPLINE_SRC2: + ret = tasha_codec_enable_spline_src(codec, + ((src_in & 0x30) == 0x10) ? SRC_IN_LO3 : SRC_IN_SPKRL, + event); + break; + case SPLINE_SRC3: + ret = tasha_codec_enable_spline_src(codec, + ((src_in & 0xC0) == 0x40) ? SRC_IN_LO4 : SRC_IN_SPKRR, + event); + break; + default: + dev_err(codec->dev, "%s: Invalid spline src:%u\n", __func__, + w->shift); + ret = -EINVAL; + }; + + return ret; +} + +static int tasha_codec_enable_swr(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tasha_priv *tasha; + int i, ch_cnt; + + tasha = snd_soc_codec_get_drvdata(codec); + + if (!tasha->nr) + return 0; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if ((strnstr(w->name, "INT7_", sizeof("RX INT7_"))) && + !tasha->rx_7_count) + tasha->rx_7_count++; + if ((strnstr(w->name, "INT8_", sizeof("RX INT8_"))) && + !tasha->rx_8_count) + tasha->rx_8_count++; + ch_cnt = tasha->rx_7_count + tasha->rx_8_count; + + for (i = 0; i < tasha->nr; i++) { + swrm_wcd_notify(tasha->swr_ctrl_data[i].swr_pdev, + SWR_DEVICE_UP, NULL); + swrm_wcd_notify(tasha->swr_ctrl_data[i].swr_pdev, + SWR_SET_NUM_RX_CH, &ch_cnt); + } + break; + case SND_SOC_DAPM_POST_PMD: + if ((strnstr(w->name, "INT7_", sizeof("RX INT7_"))) && + tasha->rx_7_count) + tasha->rx_7_count--; + if ((strnstr(w->name, "INT8_", sizeof("RX INT8_"))) && + tasha->rx_8_count) + tasha->rx_8_count--; + ch_cnt = tasha->rx_7_count + tasha->rx_8_count; + + for (i = 0; i < tasha->nr; i++) + swrm_wcd_notify(tasha->swr_ctrl_data[i].swr_pdev, + SWR_SET_NUM_RX_CH, &ch_cnt); + + break; + } + dev_dbg(tasha->dev, "%s: current swr ch cnt: %d\n", + __func__, tasha->rx_7_count + tasha->rx_8_count); + + return 0; +} + +static int tasha_codec_config_ear_spkr_gain(struct snd_soc_codec *codec, + int event, int gain_reg) +{ + int comp_gain_offset, val; + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + + switch (tasha->spkr_mode) { + /* Compander gain in SPKR_MODE1 case is 12 dB */ + case SPKR_MODE_1: + comp_gain_offset = -12; + break; + /* Default case compander gain is 15 dB */ + default: + comp_gain_offset = -15; + break; + } + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + /* Apply ear spkr gain only if compander is enabled */ + if (tasha->comp_enabled[COMPANDER_7] && + (gain_reg == WCD9335_CDC_RX7_RX_VOL_CTL || + gain_reg == WCD9335_CDC_RX7_RX_VOL_MIX_CTL) && + (tasha->ear_spkr_gain != 0)) { + /* For example, val is -8(-12+5-1) for 4dB of gain */ + val = comp_gain_offset + tasha->ear_spkr_gain - 1; + snd_soc_write(codec, gain_reg, val); + + dev_dbg(codec->dev, "%s: RX7 Volume %d dB\n", + __func__, val); + } + break; + case SND_SOC_DAPM_POST_PMD: + /* + * Reset RX7 volume to 0 dB if compander is enabled and + * ear_spkr_gain is non-zero. + */ + if (tasha->comp_enabled[COMPANDER_7] && + (gain_reg == WCD9335_CDC_RX7_RX_VOL_CTL || + gain_reg == WCD9335_CDC_RX7_RX_VOL_MIX_CTL) && + (tasha->ear_spkr_gain != 0)) { + snd_soc_write(codec, gain_reg, 0x0); + + dev_dbg(codec->dev, "%s: Reset RX7 Volume to 0 dB\n", + __func__); + } + break; + } + + return 0; +} + +static int tasha_codec_enable_mix_path(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + u16 gain_reg; + int offset_val = 0; + int val = 0; + + dev_dbg(codec->dev, "%s %d %s\n", __func__, event, w->name); + + switch (w->reg) { + case WCD9335_CDC_RX0_RX_PATH_MIX_CTL: + gain_reg = WCD9335_CDC_RX0_RX_VOL_MIX_CTL; + break; + case WCD9335_CDC_RX1_RX_PATH_MIX_CTL: + gain_reg = WCD9335_CDC_RX1_RX_VOL_MIX_CTL; + break; + case WCD9335_CDC_RX2_RX_PATH_MIX_CTL: + gain_reg = WCD9335_CDC_RX2_RX_VOL_MIX_CTL; + break; + case WCD9335_CDC_RX3_RX_PATH_MIX_CTL: + gain_reg = WCD9335_CDC_RX3_RX_VOL_MIX_CTL; + break; + case WCD9335_CDC_RX4_RX_PATH_MIX_CTL: + gain_reg = WCD9335_CDC_RX4_RX_VOL_MIX_CTL; + break; + case WCD9335_CDC_RX5_RX_PATH_MIX_CTL: + gain_reg = WCD9335_CDC_RX5_RX_VOL_MIX_CTL; + break; + case WCD9335_CDC_RX6_RX_PATH_MIX_CTL: + gain_reg = WCD9335_CDC_RX6_RX_VOL_MIX_CTL; + break; + case WCD9335_CDC_RX7_RX_PATH_MIX_CTL: + gain_reg = WCD9335_CDC_RX7_RX_VOL_MIX_CTL; + break; + case WCD9335_CDC_RX8_RX_PATH_MIX_CTL: + gain_reg = WCD9335_CDC_RX8_RX_VOL_MIX_CTL; + break; + default: + dev_err(codec->dev, "%s: No gain register avail for %s\n", + __func__, w->name); + return 0; + }; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + if ((tasha->spkr_gain_offset == RX_GAIN_OFFSET_M1P5_DB) && + (tasha->comp_enabled[COMPANDER_7] || + tasha->comp_enabled[COMPANDER_8]) && + (gain_reg == WCD9335_CDC_RX7_RX_VOL_MIX_CTL || + gain_reg == WCD9335_CDC_RX8_RX_VOL_MIX_CTL)) { + snd_soc_update_bits(codec, WCD9335_CDC_RX7_RX_PATH_SEC1, + 0x01, 0x01); + snd_soc_update_bits(codec, + WCD9335_CDC_RX7_RX_PATH_MIX_SEC0, + 0x01, 0x01); + snd_soc_update_bits(codec, WCD9335_CDC_RX8_RX_PATH_SEC1, + 0x01, 0x01); + snd_soc_update_bits(codec, + WCD9335_CDC_RX8_RX_PATH_MIX_SEC0, + 0x01, 0x01); + offset_val = -2; + } + val = snd_soc_read(codec, gain_reg); + val += offset_val; + snd_soc_write(codec, gain_reg, val); + tasha_codec_config_ear_spkr_gain(codec, event, gain_reg); + break; + case SND_SOC_DAPM_POST_PMD: + if ((tasha->spkr_gain_offset == RX_GAIN_OFFSET_M1P5_DB) && + (tasha->comp_enabled[COMPANDER_7] || + tasha->comp_enabled[COMPANDER_8]) && + (gain_reg == WCD9335_CDC_RX7_RX_VOL_MIX_CTL || + gain_reg == WCD9335_CDC_RX8_RX_VOL_MIX_CTL)) { + snd_soc_update_bits(codec, WCD9335_CDC_RX7_RX_PATH_SEC1, + 0x01, 0x00); + snd_soc_update_bits(codec, + WCD9335_CDC_RX7_RX_PATH_MIX_SEC0, + 0x01, 0x00); + snd_soc_update_bits(codec, WCD9335_CDC_RX8_RX_PATH_SEC1, + 0x01, 0x00); + snd_soc_update_bits(codec, + WCD9335_CDC_RX8_RX_PATH_MIX_SEC0, + 0x01, 0x00); + offset_val = 2; + val = snd_soc_read(codec, gain_reg); + val += offset_val; + snd_soc_write(codec, gain_reg, val); + } + tasha_codec_config_ear_spkr_gain(codec, event, gain_reg); + break; + }; + + return 0; +} + +static int __tasha_cdc_native_clk_enable(struct tasha_priv *tasha, + bool enable) +{ + int ret = 0; + struct snd_soc_codec *codec = tasha->codec; + + if (!tasha->wcd_native_clk) { + dev_err(tasha->dev, "%s: wcd native clock is NULL\n", __func__); + return -EINVAL; + } + + dev_dbg(tasha->dev, "%s: native_clk_enable = %u\n", __func__, enable); + + if (enable) { + ret = clk_prepare_enable(tasha->wcd_native_clk); + if (ret) { + dev_err(tasha->dev, "%s: native clk enable failed\n", + __func__); + goto err; + } + if (++tasha->native_clk_users == 1) { + snd_soc_update_bits(codec, WCD9335_CLOCK_TEST_CTL, + 0x10, 0x10); + snd_soc_update_bits(codec, WCD9335_CLOCK_TEST_CTL, + 0x80, 0x80); + snd_soc_update_bits(codec, WCD9335_CODEC_RPM_CLK_GATE, + 0x04, 0x00); + snd_soc_update_bits(codec, + WCD9335_CDC_CLK_RST_CTRL_MCLK_CONTROL, + 0x02, 0x02); + } + } else { + if (tasha->native_clk_users && + (--tasha->native_clk_users == 0)) { + snd_soc_update_bits(codec, + WCD9335_CDC_CLK_RST_CTRL_MCLK_CONTROL, + 0x02, 0x00); + snd_soc_update_bits(codec, WCD9335_CODEC_RPM_CLK_GATE, + 0x04, 0x04); + snd_soc_update_bits(codec, WCD9335_CLOCK_TEST_CTL, + 0x80, 0x00); + snd_soc_update_bits(codec, WCD9335_CLOCK_TEST_CTL, + 0x10, 0x00); + } + clk_disable_unprepare(tasha->wcd_native_clk); + } + + dev_dbg(codec->dev, "%s: native_clk_users: %d\n", __func__, + tasha->native_clk_users); +err: + return ret; +} + +static int tasha_codec_get_native_fifo_sync_mask(struct snd_soc_codec *codec, + int interp_n) +{ + int mask = 0; + u16 reg; + u8 val1, val2, inp0 = 0; + u8 inp1 = 0, inp2 = 0; + + reg = WCD9335_CDC_RX_INP_MUX_RX_INT1_CFG0 + (2 * interp_n) - 2; + + val1 = snd_soc_read(codec, reg); + val2 = snd_soc_read(codec, reg + 1); + + inp0 = val1 & 0x0F; + inp1 = (val1 >> 4) & 0x0F; + inp2 = (val2 >> 4) & 0x0F; + + if (IS_VALID_NATIVE_FIFO_PORT(inp0)) + mask |= (1 << (inp0 - 5)); + if (IS_VALID_NATIVE_FIFO_PORT(inp1)) + mask |= (1 << (inp1 - 5)); + if (IS_VALID_NATIVE_FIFO_PORT(inp2)) + mask |= (1 << (inp2 - 5)); + + dev_dbg(codec->dev, "%s: native fifo mask: 0x%x\n", __func__, mask); + if (!mask) + dev_err(codec->dev, "native fifo err,int:%d,inp0:%d,inp1:%d,inp2:%d\n", + interp_n, inp0, inp1, inp2); + return mask; +} + +static int tasha_enable_native_supply(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + int mask; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + u16 interp_reg; + + dev_dbg(codec->dev, "%s: event: %d, shift:%d\n", __func__, event, + w->shift); + + if (w->shift < INTERP_HPHL || w->shift > INTERP_LO2) + return -EINVAL; + + interp_reg = WCD9335_CDC_RX1_RX_PATH_CTL + 20 * (w->shift - 1); + + mask = tasha_codec_get_native_fifo_sync_mask(codec, w->shift); + if (!mask) + return -EINVAL; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* Adjust interpolator rate to 44P1_NATIVE */ + snd_soc_update_bits(codec, interp_reg, 0x0F, 0x09); + __tasha_cdc_native_clk_enable(tasha, true); + snd_soc_update_bits(codec, WCD9335_DATA_HUB_NATIVE_FIFO_SYNC, + mask, mask); + break; + case SND_SOC_DAPM_PRE_PMD: + snd_soc_update_bits(codec, WCD9335_DATA_HUB_NATIVE_FIFO_SYNC, + mask, 0x0); + __tasha_cdc_native_clk_enable(tasha, false); + /* Adjust interpolator rate to default */ + snd_soc_update_bits(codec, interp_reg, 0x0F, 0x04); + break; + } + + return 0; +} + +static int tasha_codec_enable_interpolator(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + u16 gain_reg; + u16 reg; + int val; + int offset_val = 0; + + dev_dbg(codec->dev, "%s %d %s\n", __func__, event, w->name); + + if (!(strcmp(w->name, "RX INT0 INTERP"))) { + reg = WCD9335_CDC_RX0_RX_PATH_CTL; + gain_reg = WCD9335_CDC_RX0_RX_VOL_CTL; + } else if (!(strcmp(w->name, "RX INT1 INTERP"))) { + reg = WCD9335_CDC_RX1_RX_PATH_CTL; + gain_reg = WCD9335_CDC_RX1_RX_VOL_CTL; + } else if (!(strcmp(w->name, "RX INT2 INTERP"))) { + reg = WCD9335_CDC_RX2_RX_PATH_CTL; + gain_reg = WCD9335_CDC_RX2_RX_VOL_CTL; + } else if (!(strcmp(w->name, "RX INT3 INTERP"))) { + reg = WCD9335_CDC_RX3_RX_PATH_CTL; + gain_reg = WCD9335_CDC_RX3_RX_VOL_CTL; + } else if (!(strcmp(w->name, "RX INT4 INTERP"))) { + reg = WCD9335_CDC_RX4_RX_PATH_CTL; + gain_reg = WCD9335_CDC_RX4_RX_VOL_CTL; + } else if (!(strcmp(w->name, "RX INT5 INTERP"))) { + reg = WCD9335_CDC_RX5_RX_PATH_CTL; + gain_reg = WCD9335_CDC_RX5_RX_VOL_CTL; + } else if (!(strcmp(w->name, "RX INT6 INTERP"))) { + reg = WCD9335_CDC_RX6_RX_PATH_CTL; + gain_reg = WCD9335_CDC_RX6_RX_VOL_CTL; + } else if (!(strcmp(w->name, "RX INT7 INTERP"))) { + reg = WCD9335_CDC_RX7_RX_PATH_CTL; + gain_reg = WCD9335_CDC_RX7_RX_VOL_CTL; + } else if (!(strcmp(w->name, "RX INT8 INTERP"))) { + reg = WCD9335_CDC_RX8_RX_PATH_CTL; + gain_reg = WCD9335_CDC_RX8_RX_VOL_CTL; + } else { + dev_err(codec->dev, "%s: Interpolator reg not found\n", + __func__); + return -EINVAL; + } + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + tasha_codec_vote_max_bw(codec, true); + /* Reset if needed */ + tasha_codec_enable_prim_interpolator(codec, reg, event); + break; + case SND_SOC_DAPM_POST_PMU: + tasha_config_compander(codec, w->shift, event); + /* apply gain after int clk is enabled */ + if ((tasha->spkr_gain_offset == RX_GAIN_OFFSET_M1P5_DB) && + (tasha->comp_enabled[COMPANDER_7] || + tasha->comp_enabled[COMPANDER_8]) && + (gain_reg == WCD9335_CDC_RX7_RX_VOL_CTL || + gain_reg == WCD9335_CDC_RX8_RX_VOL_CTL)) { + snd_soc_update_bits(codec, WCD9335_CDC_RX7_RX_PATH_SEC1, + 0x01, 0x01); + snd_soc_update_bits(codec, + WCD9335_CDC_RX7_RX_PATH_MIX_SEC0, + 0x01, 0x01); + snd_soc_update_bits(codec, WCD9335_CDC_RX8_RX_PATH_SEC1, + 0x01, 0x01); + snd_soc_update_bits(codec, + WCD9335_CDC_RX8_RX_PATH_MIX_SEC0, + 0x01, 0x01); + offset_val = -2; + } + val = snd_soc_read(codec, gain_reg); + val += offset_val; + snd_soc_write(codec, gain_reg, val); + tasha_codec_config_ear_spkr_gain(codec, event, gain_reg); + break; + case SND_SOC_DAPM_POST_PMD: + tasha_config_compander(codec, w->shift, event); + tasha_codec_enable_prim_interpolator(codec, reg, event); + if ((tasha->spkr_gain_offset == RX_GAIN_OFFSET_M1P5_DB) && + (tasha->comp_enabled[COMPANDER_7] || + tasha->comp_enabled[COMPANDER_8]) && + (gain_reg == WCD9335_CDC_RX7_RX_VOL_CTL || + gain_reg == WCD9335_CDC_RX8_RX_VOL_CTL)) { + snd_soc_update_bits(codec, WCD9335_CDC_RX7_RX_PATH_SEC1, + 0x01, 0x00); + snd_soc_update_bits(codec, + WCD9335_CDC_RX7_RX_PATH_MIX_SEC0, + 0x01, 0x00); + snd_soc_update_bits(codec, WCD9335_CDC_RX8_RX_PATH_SEC1, + 0x01, 0x00); + snd_soc_update_bits(codec, + WCD9335_CDC_RX8_RX_PATH_MIX_SEC0, + 0x01, 0x00); + offset_val = 2; + val = snd_soc_read(codec, gain_reg); + val += offset_val; + snd_soc_write(codec, gain_reg, val); + } + tasha_codec_config_ear_spkr_gain(codec, event, gain_reg); + break; + }; + + return 0; +} + +static int tasha_codec_set_iir_gain(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + + dev_dbg(codec->dev, "%s: event = %d\n", __func__, event); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: /* fall through */ + case SND_SOC_DAPM_PRE_PMD: + if (strnstr(w->name, "IIR0", sizeof("IIR0"))) { + snd_soc_write(codec, + WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B1_CTL, + snd_soc_read(codec, + WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B1_CTL)); + snd_soc_write(codec, + WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B2_CTL, + snd_soc_read(codec, + WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B2_CTL)); + snd_soc_write(codec, + WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B3_CTL, + snd_soc_read(codec, + WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B3_CTL)); + snd_soc_write(codec, + WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B4_CTL, + snd_soc_read(codec, + WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B4_CTL)); + } else { + snd_soc_write(codec, + WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B1_CTL, + snd_soc_read(codec, + WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B1_CTL)); + snd_soc_write(codec, + WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B2_CTL, + snd_soc_read(codec, + WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B2_CTL)); + snd_soc_write(codec, + WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B3_CTL, + snd_soc_read(codec, + WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B3_CTL)); + } + break; + } + return 0; +} + +static int tasha_codec_enable_on_demand_supply( + struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + int ret = 0; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + struct on_demand_supply *supply; + + if (w->shift >= ON_DEMAND_SUPPLIES_MAX) { + dev_err(codec->dev, "%s: error index > MAX Demand supplies", + __func__); + ret = -EINVAL; + goto out; + } + + dev_dbg(codec->dev, "%s: supply: %s event: %d\n", + __func__, on_demand_supply_name[w->shift], event); + + supply = &tasha->on_demand_list[w->shift]; + WARN_ONCE(!supply->supply, "%s isn't defined\n", + on_demand_supply_name[w->shift]); + if (!supply->supply) { + dev_err(codec->dev, "%s: err supply not present ond for %d", + __func__, w->shift); + goto out; + } + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + ret = regulator_enable(supply->supply); + if (ret) + dev_err(codec->dev, "%s: Failed to enable %s\n", + __func__, + on_demand_supply_name[w->shift]); + break; + case SND_SOC_DAPM_POST_PMD: + ret = regulator_disable(supply->supply); + if (ret) + dev_err(codec->dev, "%s: Failed to disable %s\n", + __func__, + on_demand_supply_name[w->shift]); + break; + default: + break; + }; + +out: + return ret; +} + +static int tasha_codec_find_amic_input(struct snd_soc_codec *codec, + int adc_mux_n) +{ + u16 mask, shift, adc_mux_in_reg; + u16 amic_mux_sel_reg; + bool is_amic; + + if (adc_mux_n < 0 || adc_mux_n > WCD9335_MAX_VALID_ADC_MUX || + adc_mux_n == WCD9335_INVALID_ADC_MUX) + return 0; + + /* Check whether adc mux input is AMIC or DMIC */ + if (adc_mux_n < 4) { + adc_mux_in_reg = WCD9335_CDC_TX_INP_MUX_ADC_MUX0_CFG1 + + 2 * adc_mux_n; + amic_mux_sel_reg = WCD9335_CDC_TX_INP_MUX_ADC_MUX0_CFG0 + + 2 * adc_mux_n; + mask = 0x03; + shift = 0; + } else { + adc_mux_in_reg = WCD9335_CDC_TX_INP_MUX_ADC_MUX4_CFG0 + + adc_mux_n - 4; + amic_mux_sel_reg = adc_mux_in_reg; + mask = 0xC0; + shift = 6; + } + is_amic = (((snd_soc_read(codec, adc_mux_in_reg) & mask) >> shift) + == 1); + if (!is_amic) + return 0; + + return snd_soc_read(codec, amic_mux_sel_reg) & 0x07; +} + +static void tasha_codec_set_tx_hold(struct snd_soc_codec *codec, + u16 amic_reg, bool set) +{ + u8 mask = 0x20; + u8 val; + + if (amic_reg == WCD9335_ANA_AMIC1 || + amic_reg == WCD9335_ANA_AMIC3 || + amic_reg == WCD9335_ANA_AMIC5) + mask = 0x40; + + val = set ? mask : 0x00; + + switch (amic_reg) { + case WCD9335_ANA_AMIC1: + case WCD9335_ANA_AMIC2: + snd_soc_update_bits(codec, WCD9335_ANA_AMIC2, mask, val); + break; + case WCD9335_ANA_AMIC3: + case WCD9335_ANA_AMIC4: + snd_soc_update_bits(codec, WCD9335_ANA_AMIC4, mask, val); + break; + case WCD9335_ANA_AMIC5: + case WCD9335_ANA_AMIC6: + snd_soc_update_bits(codec, WCD9335_ANA_AMIC6, mask, val); + break; + default: + dev_dbg(codec->dev, "%s: invalid amic: %d\n", + __func__, amic_reg); + break; + } +} + +static int tasha_codec_tx_adc_cfg(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + int adc_mux_n = w->shift; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + int amic_n; + + dev_dbg(codec->dev, "%s: event: %d\n", __func__, event); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + amic_n = tasha_codec_find_amic_input(codec, adc_mux_n); + if (amic_n) { + /* + * Prevent ANC Rx pop by leaving Tx FE in HOLD + * state until PA is up. Track AMIC being used + * so we can release the HOLD later. + */ + set_bit(ANC_MIC_AMIC1 + amic_n - 1, + &tasha->status_mask); + } + break; + default: + break; + } + + return 0; +} + +static u16 tasha_codec_get_amic_pwlvl_reg(struct snd_soc_codec *codec, int amic) +{ + u16 pwr_level_reg = 0; + + switch (amic) { + case 1: + case 2: + pwr_level_reg = WCD9335_ANA_AMIC1; + break; + + case 3: + case 4: + pwr_level_reg = WCD9335_ANA_AMIC3; + break; + + case 5: + case 6: + pwr_level_reg = WCD9335_ANA_AMIC5; + break; + default: + dev_dbg(codec->dev, "%s: invalid amic: %d\n", + __func__, amic); + break; + } + + return pwr_level_reg; +} + +#define TX_HPF_CUT_OFF_FREQ_MASK 0x60 +#define CF_MIN_3DB_4HZ 0x0 +#define CF_MIN_3DB_75HZ 0x1 +#define CF_MIN_3DB_150HZ 0x2 + +static void tasha_tx_hpf_corner_freq_callback(struct work_struct *work) +{ + struct delayed_work *hpf_delayed_work; + struct hpf_work *hpf_work; + struct tasha_priv *tasha; + struct snd_soc_codec *codec; + u16 dec_cfg_reg, amic_reg; + u8 hpf_cut_off_freq; + int amic_n; + + hpf_delayed_work = to_delayed_work(work); + hpf_work = container_of(hpf_delayed_work, struct hpf_work, dwork); + tasha = hpf_work->tasha; + codec = tasha->codec; + hpf_cut_off_freq = hpf_work->hpf_cut_off_freq; + + dec_cfg_reg = WCD9335_CDC_TX0_TX_PATH_CFG0 + 16 * hpf_work->decimator; + + dev_dbg(codec->dev, "%s: decimator %u hpf_cut_of_freq 0x%x\n", + __func__, hpf_work->decimator, hpf_cut_off_freq); + + amic_n = tasha_codec_find_amic_input(codec, hpf_work->decimator); + if (amic_n) { + amic_reg = WCD9335_ANA_AMIC1 + amic_n - 1; + tasha_codec_set_tx_hold(codec, amic_reg, false); + } + tasha_codec_vote_max_bw(codec, true); + snd_soc_update_bits(codec, dec_cfg_reg, TX_HPF_CUT_OFF_FREQ_MASK, + hpf_cut_off_freq << 5); + tasha_codec_vote_max_bw(codec, false); +} + +static void tasha_tx_mute_update_callback(struct work_struct *work) +{ + struct tx_mute_work *tx_mute_dwork; + struct tasha_priv *tasha; + struct delayed_work *delayed_work; + struct snd_soc_codec *codec; + u16 tx_vol_ctl_reg, hpf_gate_reg; + + delayed_work = to_delayed_work(work); + tx_mute_dwork = container_of(delayed_work, struct tx_mute_work, dwork); + tasha = tx_mute_dwork->tasha; + codec = tasha->codec; + + tx_vol_ctl_reg = WCD9335_CDC_TX0_TX_PATH_CTL + + 16 * tx_mute_dwork->decimator; + hpf_gate_reg = WCD9335_CDC_TX0_TX_PATH_SEC2 + + 16 * tx_mute_dwork->decimator; + snd_soc_update_bits(codec, hpf_gate_reg, 0x01, 0x01); + snd_soc_update_bits(codec, tx_vol_ctl_reg, 0x10, 0x00); +} + +static int tasha_codec_enable_dec(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + unsigned int decimator; + char *dec_adc_mux_name = NULL; + char *widget_name = NULL; + char *wname; + int ret = 0, amic_n; + u16 tx_vol_ctl_reg, pwr_level_reg = 0, dec_cfg_reg, hpf_gate_reg; + u16 tx_gain_ctl_reg; + char *dec; + u8 hpf_cut_off_freq; + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, "%s %d\n", __func__, event); + + widget_name = kstrndup(w->name, 15, GFP_KERNEL); + if (!widget_name) + return -ENOMEM; + + wname = widget_name; + dec_adc_mux_name = strsep(&widget_name, " "); + if (!dec_adc_mux_name) { + dev_err(codec->dev, "%s: Invalid decimator = %s\n", + __func__, w->name); + ret = -EINVAL; + goto out; + } + dec_adc_mux_name = widget_name; + + dec = strpbrk(dec_adc_mux_name, "012345678"); + if (!dec) { + dev_err(codec->dev, "%s: decimator index not found\n", + __func__); + ret = -EINVAL; + goto out; + } + + ret = kstrtouint(dec, 10, &decimator); + if (ret < 0) { + dev_err(codec->dev, "%s: Invalid decimator = %s\n", + __func__, wname); + ret = -EINVAL; + goto out; + } + + dev_dbg(codec->dev, "%s(): widget = %s decimator = %u\n", __func__, + w->name, decimator); + + tx_vol_ctl_reg = WCD9335_CDC_TX0_TX_PATH_CTL + 16 * decimator; + hpf_gate_reg = WCD9335_CDC_TX0_TX_PATH_SEC2 + 16 * decimator; + dec_cfg_reg = WCD9335_CDC_TX0_TX_PATH_CFG0 + 16 * decimator; + tx_gain_ctl_reg = WCD9335_CDC_TX0_TX_VOL_CTL + 16 * decimator; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + amic_n = tasha_codec_find_amic_input(codec, decimator); + if (amic_n) + pwr_level_reg = tasha_codec_get_amic_pwlvl_reg(codec, + amic_n); + + if (pwr_level_reg) { + switch ((snd_soc_read(codec, pwr_level_reg) & + WCD9335_AMIC_PWR_LVL_MASK) >> + WCD9335_AMIC_PWR_LVL_SHIFT) { + case WCD9335_AMIC_PWR_LEVEL_LP: + snd_soc_update_bits(codec, dec_cfg_reg, + WCD9335_DEC_PWR_LVL_MASK, + WCD9335_DEC_PWR_LVL_LP); + break; + + case WCD9335_AMIC_PWR_LEVEL_HP: + snd_soc_update_bits(codec, dec_cfg_reg, + WCD9335_DEC_PWR_LVL_MASK, + WCD9335_DEC_PWR_LVL_HP); + break; + case WCD9335_AMIC_PWR_LEVEL_DEFAULT: + default: + snd_soc_update_bits(codec, dec_cfg_reg, + WCD9335_DEC_PWR_LVL_MASK, + WCD9335_DEC_PWR_LVL_DF); + break; + } + } + hpf_cut_off_freq = (snd_soc_read(codec, dec_cfg_reg) & + TX_HPF_CUT_OFF_FREQ_MASK) >> 5; + tasha->tx_hpf_work[decimator].hpf_cut_off_freq = + hpf_cut_off_freq; + + if (hpf_cut_off_freq != CF_MIN_3DB_150HZ) + snd_soc_update_bits(codec, dec_cfg_reg, + TX_HPF_CUT_OFF_FREQ_MASK, + CF_MIN_3DB_150HZ << 5); + /* Enable TX PGA Mute */ + snd_soc_update_bits(codec, tx_vol_ctl_reg, 0x10, 0x10); + break; + case SND_SOC_DAPM_POST_PMU: + snd_soc_update_bits(codec, hpf_gate_reg, 0x01, 0x00); + + if (decimator == 0) { + snd_soc_write(codec, WCD9335_MBHC_ZDET_RAMP_CTL, 0x83); + snd_soc_write(codec, WCD9335_MBHC_ZDET_RAMP_CTL, 0xA3); + snd_soc_write(codec, WCD9335_MBHC_ZDET_RAMP_CTL, 0x83); + snd_soc_write(codec, WCD9335_MBHC_ZDET_RAMP_CTL, 0x03); + } + /* schedule work queue to Remove Mute */ + schedule_delayed_work(&tasha->tx_mute_dwork[decimator].dwork, + msecs_to_jiffies(tx_unmute_delay)); + if (tasha->tx_hpf_work[decimator].hpf_cut_off_freq != + CF_MIN_3DB_150HZ) + schedule_delayed_work( + &tasha->tx_hpf_work[decimator].dwork, + msecs_to_jiffies(300)); + /* apply gain after decimator is enabled */ + snd_soc_write(codec, tx_gain_ctl_reg, + snd_soc_read(codec, tx_gain_ctl_reg)); + break; + case SND_SOC_DAPM_PRE_PMD: + hpf_cut_off_freq = + tasha->tx_hpf_work[decimator].hpf_cut_off_freq; + snd_soc_update_bits(codec, tx_vol_ctl_reg, 0x10, 0x10); + if (cancel_delayed_work_sync( + &tasha->tx_hpf_work[decimator].dwork)) { + if (hpf_cut_off_freq != CF_MIN_3DB_150HZ) { + tasha_codec_vote_max_bw(codec, true); + snd_soc_update_bits(codec, dec_cfg_reg, + TX_HPF_CUT_OFF_FREQ_MASK, + hpf_cut_off_freq << 5); + tasha_codec_vote_max_bw(codec, false); + } + } + cancel_delayed_work_sync( + &tasha->tx_mute_dwork[decimator].dwork); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_update_bits(codec, tx_vol_ctl_reg, 0x10, 0x00); + break; + }; +out: + kfree(wname); + return ret; +} + +static u32 tasha_get_dmic_sample_rate(struct snd_soc_codec *codec, + unsigned int dmic, struct wcd9xxx_pdata *pdata) +{ + u8 tx_stream_fs; + u8 adc_mux_index = 0, adc_mux_sel = 0; + bool dec_found = false; + u16 adc_mux_ctl_reg, tx_fs_reg; + u32 dmic_fs; + + while (dec_found == 0 && adc_mux_index < WCD9335_MAX_VALID_ADC_MUX) { + if (adc_mux_index < 4) { + adc_mux_ctl_reg = WCD9335_CDC_TX_INP_MUX_ADC_MUX0_CFG0 + + (adc_mux_index * 2); + adc_mux_sel = ((snd_soc_read(codec, adc_mux_ctl_reg) & + 0x78) >> 3) - 1; + } else if (adc_mux_index < 9) { + adc_mux_ctl_reg = WCD9335_CDC_TX_INP_MUX_ADC_MUX4_CFG0 + + ((adc_mux_index - 4) * 1); + adc_mux_sel = ((snd_soc_read(codec, adc_mux_ctl_reg) & + 0x38) >> 3) - 1; + } else if (adc_mux_index == 9) { + ++adc_mux_index; + continue; + } + if (adc_mux_sel == dmic) + dec_found = true; + else + ++adc_mux_index; + } + + if (dec_found == true && adc_mux_index <= 8) { + tx_fs_reg = WCD9335_CDC_TX0_TX_PATH_CTL + (16 * adc_mux_index); + tx_stream_fs = snd_soc_read(codec, tx_fs_reg) & 0x0F; + dmic_fs = tx_stream_fs <= 4 ? WCD9XXX_DMIC_SAMPLE_RATE_2P4MHZ : + WCD9XXX_DMIC_SAMPLE_RATE_4P8MHZ; + + /* + * Check for ECPP path selection and DEC1 not connected to + * any other audio path to apply ECPP DMIC sample rate + */ + if ((adc_mux_index == 1) && + ((snd_soc_read(codec, WCD9335_CPE_SS_US_EC_MUX_CFG) + & 0x0F) == 0x0A) && + ((snd_soc_read(codec, WCD9335_CDC_IF_ROUTER_TX_MUX_CFG0) + & 0x0C) == 0x00)) { + dmic_fs = pdata->ecpp_dmic_sample_rate; + } + } else { + dmic_fs = pdata->dmic_sample_rate; + } + + return dmic_fs; +} + +static u8 tasha_get_dmic_clk_val(struct snd_soc_codec *codec, + u32 mclk_rate, u32 dmic_clk_rate) +{ + u32 div_factor; + u8 dmic_ctl_val; + + dev_dbg(codec->dev, + "%s: mclk_rate = %d, dmic_sample_rate = %d\n", + __func__, mclk_rate, dmic_clk_rate); + + /* Default value to return in case of error */ + if (mclk_rate == TASHA_MCLK_CLK_9P6MHZ) + dmic_ctl_val = WCD9335_DMIC_CLK_DIV_2; + else + dmic_ctl_val = WCD9335_DMIC_CLK_DIV_3; + + if (dmic_clk_rate == 0) { + dev_err(codec->dev, + "%s: dmic_sample_rate cannot be 0\n", + __func__); + goto done; + } + + div_factor = mclk_rate / dmic_clk_rate; + switch (div_factor) { + case 2: + dmic_ctl_val = WCD9335_DMIC_CLK_DIV_2; + break; + case 3: + dmic_ctl_val = WCD9335_DMIC_CLK_DIV_3; + break; + case 4: + dmic_ctl_val = WCD9335_DMIC_CLK_DIV_4; + break; + case 6: + dmic_ctl_val = WCD9335_DMIC_CLK_DIV_6; + break; + case 8: + dmic_ctl_val = WCD9335_DMIC_CLK_DIV_8; + break; + case 16: + dmic_ctl_val = WCD9335_DMIC_CLK_DIV_16; + break; + default: + dev_err(codec->dev, + "%s: Invalid div_factor %u, clk_rate(%u), dmic_rate(%u)\n", + __func__, div_factor, mclk_rate, dmic_clk_rate); + break; + } + +done: + return dmic_ctl_val; +} + +static int tasha_codec_enable_adc(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + + dev_dbg(codec->dev, "%s: event:%d\n", __func__, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + tasha_codec_set_tx_hold(codec, w->reg, true); + break; + default: + break; + } + + return 0; +} + +static int tasha_codec_enable_dmic(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + struct wcd9xxx_pdata *pdata = dev_get_platdata(codec->dev->parent); + u8 dmic_clk_en = 0x01; + u16 dmic_clk_reg; + s32 *dmic_clk_cnt; + u8 dmic_rate_val, dmic_rate_shift = 1; + unsigned int dmic; + u32 dmic_sample_rate; + int ret; + char *wname; + + wname = strpbrk(w->name, "012345"); + if (!wname) { + dev_err(codec->dev, "%s: widget not found\n", __func__); + return -EINVAL; + } + + ret = kstrtouint(wname, 10, &dmic); + if (ret < 0) { + dev_err(codec->dev, "%s: Invalid DMIC line on the codec\n", + __func__); + return -EINVAL; + } + + switch (dmic) { + case 0: + case 1: + dmic_clk_cnt = &(tasha->dmic_0_1_clk_cnt); + dmic_clk_reg = WCD9335_CPE_SS_DMIC0_CTL; + break; + case 2: + case 3: + dmic_clk_cnt = &(tasha->dmic_2_3_clk_cnt); + dmic_clk_reg = WCD9335_CPE_SS_DMIC1_CTL; + break; + case 4: + case 5: + dmic_clk_cnt = &(tasha->dmic_4_5_clk_cnt); + dmic_clk_reg = WCD9335_CPE_SS_DMIC2_CTL; + break; + default: + dev_err(codec->dev, "%s: Invalid DMIC Selection\n", + __func__); + return -EINVAL; + }; + dev_dbg(codec->dev, "%s: event %d DMIC%d dmic_clk_cnt %d\n", + __func__, event, dmic, *dmic_clk_cnt); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + dmic_sample_rate = tasha_get_dmic_sample_rate(codec, dmic, + pdata); + dmic_rate_val = + tasha_get_dmic_clk_val(codec, + pdata->mclk_rate, + dmic_sample_rate); + + (*dmic_clk_cnt)++; + if (*dmic_clk_cnt == 1) { + snd_soc_update_bits(codec, dmic_clk_reg, + 0x07 << dmic_rate_shift, + dmic_rate_val << dmic_rate_shift); + snd_soc_update_bits(codec, dmic_clk_reg, + dmic_clk_en, dmic_clk_en); + } + + break; + case SND_SOC_DAPM_POST_PMD: + dmic_rate_val = + tasha_get_dmic_clk_val(codec, + pdata->mclk_rate, + pdata->mad_dmic_sample_rate); + (*dmic_clk_cnt)--; + if (*dmic_clk_cnt == 0) { + snd_soc_update_bits(codec, dmic_clk_reg, + dmic_clk_en, 0); + snd_soc_update_bits(codec, dmic_clk_reg, + 0x07 << dmic_rate_shift, + dmic_rate_val << dmic_rate_shift); + } + break; + }; + + return 0; +} + +static int __tasha_codec_enable_micbias(struct snd_soc_dapm_widget *w, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + int micb_num; + + dev_dbg(codec->dev, "%s: wname: %s, event: %d\n", + __func__, w->name, event); + + if (strnstr(w->name, "MIC BIAS1", sizeof("MIC BIAS1"))) + micb_num = MIC_BIAS_1; + else if (strnstr(w->name, "MIC BIAS2", sizeof("MIC BIAS2"))) + micb_num = MIC_BIAS_2; + else if (strnstr(w->name, "MIC BIAS3", sizeof("MIC BIAS3"))) + micb_num = MIC_BIAS_3; + else if (strnstr(w->name, "MIC BIAS4", sizeof("MIC BIAS4"))) + micb_num = MIC_BIAS_4; + else + return -EINVAL; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* + * MIC BIAS can also be requested by MBHC, + * so use ref count to handle micbias pullup + * and enable requests + */ + tasha_micbias_control(codec, micb_num, MICB_ENABLE, true); + break; + case SND_SOC_DAPM_POST_PMU: + /* wait for cnp time */ + usleep_range(1000, 1100); + break; + case SND_SOC_DAPM_POST_PMD: + tasha_micbias_control(codec, micb_num, MICB_DISABLE, true); + break; + }; + + return 0; +} + +static int tasha_codec_ldo_h_control(struct snd_soc_dapm_widget *w, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + + if (SND_SOC_DAPM_EVENT_ON(event)) { + tasha->ldo_h_users++; + + if (tasha->ldo_h_users == 1) + snd_soc_update_bits(codec, WCD9335_LDOH_MODE, + 0x80, 0x80); + } + + if (SND_SOC_DAPM_EVENT_OFF(event)) { + tasha->ldo_h_users--; + + if (tasha->ldo_h_users < 0) + tasha->ldo_h_users = 0; + + if (tasha->ldo_h_users == 0) + snd_soc_update_bits(codec, WCD9335_LDOH_MODE, + 0x80, 0x00); + } + + return 0; +} + +static int tasha_codec_force_enable_ldo_h(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + wcd_resmgr_enable_master_bias(tasha->resmgr); + tasha_codec_ldo_h_control(w, event); + break; + case SND_SOC_DAPM_POST_PMD: + tasha_codec_ldo_h_control(w, event); + wcd_resmgr_disable_master_bias(tasha->resmgr); + break; + } + + return 0; +} + +static int tasha_codec_force_enable_micbias(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + int ret = 0; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + wcd_resmgr_enable_master_bias(tasha->resmgr); + tasha_cdc_mclk_enable(codec, true, true); + ret = __tasha_codec_enable_micbias(w, SND_SOC_DAPM_PRE_PMU); + /* Wait for 1ms for better cnp */ + usleep_range(1000, 1100); + tasha_cdc_mclk_enable(codec, false, true); + break; + case SND_SOC_DAPM_POST_PMD: + ret = __tasha_codec_enable_micbias(w, SND_SOC_DAPM_POST_PMD); + wcd_resmgr_disable_master_bias(tasha->resmgr); + break; + } + + return ret; +} + +static int tasha_codec_enable_micbias(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + return __tasha_codec_enable_micbias(w, event); +} + +static int tasha_codec_enable_standalone_ldo_h(struct snd_soc_codec *codec, + bool enable) +{ + int rc; + + if (enable) + rc = snd_soc_dapm_force_enable_pin( + snd_soc_codec_get_dapm(codec), + DAPM_LDO_H_STANDALONE); + else + rc = snd_soc_dapm_disable_pin( + snd_soc_codec_get_dapm(codec), + DAPM_LDO_H_STANDALONE); + + if (!rc) + snd_soc_dapm_sync(snd_soc_codec_get_dapm(codec)); + else + dev_err(codec->dev, "%s: ldo_h force %s pin failed\n", + __func__, (enable ? "enable" : "disable")); + + return rc; +} + +/* + * tasha_codec_enable_standalone_micbias - enable micbias standalone + * @codec: pointer to codec instance + * @micb_num: number of micbias to be enabled + * @enable: true to enable micbias or false to disable + * + * This function is used to enable micbias (1, 2, 3 or 4) during + * standalone independent of whether TX use-case is running or not + * + * Return: error code in case of failure or 0 for success + */ +int tasha_codec_enable_standalone_micbias(struct snd_soc_codec *codec, + int micb_num, + bool enable) +{ + const char * const micb_names[] = { + DAPM_MICBIAS1_STANDALONE, DAPM_MICBIAS2_STANDALONE, + DAPM_MICBIAS3_STANDALONE, DAPM_MICBIAS4_STANDALONE + }; + int micb_index = micb_num - 1; + int rc; + + if (!codec) { + pr_err("%s: Codec memory is NULL\n", __func__); + return -EINVAL; + } + + if ((micb_index < 0) || (micb_index > TASHA_MAX_MICBIAS - 1)) { + dev_err(codec->dev, "%s: Invalid micbias index, micb_ind:%d\n", + __func__, micb_index); + return -EINVAL; + } + + if (enable) + rc = snd_soc_dapm_force_enable_pin( + snd_soc_codec_get_dapm(codec), + micb_names[micb_index]); + else + rc = snd_soc_dapm_disable_pin(snd_soc_codec_get_dapm(codec), + micb_names[micb_index]); + + if (!rc) + snd_soc_dapm_sync(snd_soc_codec_get_dapm(codec)); + else + dev_err(codec->dev, "%s: micbias%d force %s pin failed\n", + __func__, micb_num, (enable ? "enable" : "disable")); + + return rc; +} +EXPORT_SYMBOL(tasha_codec_enable_standalone_micbias); + +static const char *const tasha_anc_func_text[] = {"OFF", "ON"}; +static const struct soc_enum tasha_anc_func_enum = + SOC_ENUM_SINGLE_EXT(2, tasha_anc_func_text); + +static const char *const tasha_clkmode_text[] = {"EXTERNAL", "INTERNAL"}; +static SOC_ENUM_SINGLE_EXT_DECL(tasha_clkmode_enum, tasha_clkmode_text); + +/* Cutoff frequency for high pass filter */ +static const char * const cf_text[] = { + "CF_NEG_3DB_4HZ", "CF_NEG_3DB_75HZ", "CF_NEG_3DB_150HZ" +}; + +static const char * const rx_cf_text[] = { + "CF_NEG_3DB_4HZ", "CF_NEG_3DB_75HZ", "CF_NEG_3DB_150HZ", + "CF_NEG_3DB_0P48HZ" +}; + +static const struct soc_enum cf_dec0_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX0_TX_PATH_CFG0, 5, 3, cf_text); + +static const struct soc_enum cf_dec1_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX1_TX_PATH_CFG0, 5, 3, cf_text); + +static const struct soc_enum cf_dec2_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX2_TX_PATH_CFG0, 5, 3, cf_text); + +static const struct soc_enum cf_dec3_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX3_TX_PATH_CFG0, 5, 3, cf_text); + +static const struct soc_enum cf_dec4_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX4_TX_PATH_CFG0, 5, 3, cf_text); + +static const struct soc_enum cf_dec5_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX5_TX_PATH_CFG0, 5, 3, cf_text); + +static const struct soc_enum cf_dec6_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX6_TX_PATH_CFG0, 5, 3, cf_text); + +static const struct soc_enum cf_dec7_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX7_TX_PATH_CFG0, 5, 3, cf_text); + +static const struct soc_enum cf_dec8_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX8_TX_PATH_CFG0, 5, 3, cf_text); + +static const struct soc_enum cf_int0_1_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX0_RX_PATH_CFG2, 0, 4, rx_cf_text); + +static SOC_ENUM_SINGLE_DECL(cf_int0_2_enum, WCD9335_CDC_RX0_RX_PATH_MIX_CFG, 2, + rx_cf_text); + +static const struct soc_enum cf_int1_1_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX1_RX_PATH_CFG2, 0, 4, rx_cf_text); + +static SOC_ENUM_SINGLE_DECL(cf_int1_2_enum, WCD9335_CDC_RX1_RX_PATH_MIX_CFG, 2, + rx_cf_text); + +static const struct soc_enum cf_int2_1_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX2_RX_PATH_CFG2, 0, 4, rx_cf_text); + +static SOC_ENUM_SINGLE_DECL(cf_int2_2_enum, WCD9335_CDC_RX2_RX_PATH_MIX_CFG, 2, + rx_cf_text); + +static const struct soc_enum cf_int3_1_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX3_RX_PATH_CFG2, 0, 4, rx_cf_text); + +static SOC_ENUM_SINGLE_DECL(cf_int3_2_enum, WCD9335_CDC_RX3_RX_PATH_MIX_CFG, 2, + rx_cf_text); + +static const struct soc_enum cf_int4_1_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX4_RX_PATH_CFG2, 0, 4, rx_cf_text); + +static SOC_ENUM_SINGLE_DECL(cf_int4_2_enum, WCD9335_CDC_RX4_RX_PATH_MIX_CFG, 2, + rx_cf_text); + +static const struct soc_enum cf_int5_1_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX5_RX_PATH_CFG2, 0, 4, rx_cf_text); + +static SOC_ENUM_SINGLE_DECL(cf_int5_2_enum, WCD9335_CDC_RX5_RX_PATH_MIX_CFG, 2, + rx_cf_text); + +static const struct soc_enum cf_int6_1_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX6_RX_PATH_CFG2, 0, 4, rx_cf_text); + +static SOC_ENUM_SINGLE_DECL(cf_int6_2_enum, WCD9335_CDC_RX6_RX_PATH_MIX_CFG, 2, + rx_cf_text); + +static const struct soc_enum cf_int7_1_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX7_RX_PATH_CFG2, 0, 4, rx_cf_text); + +static SOC_ENUM_SINGLE_DECL(cf_int7_2_enum, WCD9335_CDC_RX7_RX_PATH_MIX_CFG, 2, + rx_cf_text); + +static const struct soc_enum cf_int8_1_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX8_RX_PATH_CFG2, 0, 4, rx_cf_text); + +static SOC_ENUM_SINGLE_DECL(cf_int8_2_enum, WCD9335_CDC_RX8_RX_PATH_MIX_CFG, 2, + rx_cf_text); + +static const struct snd_soc_dapm_route audio_i2s_map[] = { + {"SLIM RX0 MUX", NULL, "RX_I2S_CTL"}, + {"SLIM RX1 MUX", NULL, "RX_I2S_CTL"}, + {"SLIM RX2 MUX", NULL, "RX_I2S_CTL"}, + {"SLIM RX3 MUX", NULL, "RX_I2S_CTL"}, + + {"SLIM TX6 MUX", NULL, "TX_I2S_CTL"}, + {"SLIM TX7 MUX", NULL, "TX_I2S_CTL"}, + {"SLIM TX8 MUX", NULL, "TX_I2S_CTL"}, + {"SLIM TX11 MUX", NULL, "TX_I2S_CTL"}, +}; + +static const struct snd_soc_dapm_route audio_map[] = { + + /* MAD */ + {"MAD_SEL MUX", "SPE", "MAD_CPE_INPUT"}, + {"MAD_SEL MUX", "MSM", "MADINPUT"}, + {"MADONOFF", "Switch", "MAD_SEL MUX"}, + {"MAD_BROADCAST", "Switch", "MAD_SEL MUX"}, + {"TX13 INP MUX", "CPE_TX_PP", "MADONOFF"}, + + /* CPE HW MAD bypass */ + {"CPE IN Mixer", "MAD_BYPASS", "SLIM TX1 MUX"}, + + {"AIF4_MAD Mixer", "SLIM TX1", "CPE IN Mixer"}, + {"AIF4_MAD Mixer", "SLIM TX12", "MADONOFF"}, + {"AIF4_MAD Mixer", "SLIM TX13", "TX13 INP MUX"}, + {"AIF4 MAD", NULL, "AIF4_MAD Mixer"}, + {"AIF4 MAD", NULL, "AIF4"}, + + {"EC BUF MUX INP", "DEC1", "ADC MUX1"}, + {"AIF5 CPE", NULL, "EC BUF MUX INP"}, + + /* SLIMBUS Connections */ + {"AIF1 CAP", NULL, "AIF1_CAP Mixer"}, + {"AIF2 CAP", NULL, "AIF2_CAP Mixer"}, + {"AIF3 CAP", NULL, "AIF3_CAP Mixer"}, + + /* VI Feedback */ + {"AIF4_VI Mixer", "SPKR_VI_1", "VIINPUT"}, + {"AIF4_VI Mixer", "SPKR_VI_2", "VIINPUT"}, + {"AIF4 VI", NULL, "AIF4_VI Mixer"}, + + /* SLIM_MIXER("AIF1_CAP Mixer"),*/ + {"AIF1_CAP Mixer", "SLIM TX0", "SLIM TX0 MUX"}, + {"AIF1_CAP Mixer", "SLIM TX1", "SLIM TX1 MUX"}, + {"AIF1_CAP Mixer", "SLIM TX2", "SLIM TX2 MUX"}, + {"AIF1_CAP Mixer", "SLIM TX3", "SLIM TX3 MUX"}, + {"AIF1_CAP Mixer", "SLIM TX4", "SLIM TX4 MUX"}, + {"AIF1_CAP Mixer", "SLIM TX5", "SLIM TX5 MUX"}, + {"AIF1_CAP Mixer", "SLIM TX6", "SLIM TX6 MUX"}, + {"AIF1_CAP Mixer", "SLIM TX7", "SLIM TX7 MUX"}, + {"AIF1_CAP Mixer", "SLIM TX8", "SLIM TX8 MUX"}, + {"AIF1_CAP Mixer", "SLIM TX9", "SLIM TX9 MUX"}, + {"AIF1_CAP Mixer", "SLIM TX10", "SLIM TX10 MUX"}, + {"AIF1_CAP Mixer", "SLIM TX11", "SLIM TX11 MUX"}, + {"AIF1_CAP Mixer", "SLIM TX13", "TX13 INP MUX"}, + /* SLIM_MIXER("AIF2_CAP Mixer"),*/ + {"AIF2_CAP Mixer", "SLIM TX0", "SLIM TX0 MUX"}, + {"AIF2_CAP Mixer", "SLIM TX1", "SLIM TX1 MUX"}, + {"AIF2_CAP Mixer", "SLIM TX2", "SLIM TX2 MUX"}, + {"AIF2_CAP Mixer", "SLIM TX3", "SLIM TX3 MUX"}, + {"AIF2_CAP Mixer", "SLIM TX4", "SLIM TX4 MUX"}, + {"AIF2_CAP Mixer", "SLIM TX5", "SLIM TX5 MUX"}, + {"AIF2_CAP Mixer", "SLIM TX6", "SLIM TX6 MUX"}, + {"AIF2_CAP Mixer", "SLIM TX7", "SLIM TX7 MUX"}, + {"AIF2_CAP Mixer", "SLIM TX8", "SLIM TX8 MUX"}, + {"AIF2_CAP Mixer", "SLIM TX9", "SLIM TX9 MUX"}, + {"AIF2_CAP Mixer", "SLIM TX10", "SLIM TX10 MUX"}, + {"AIF2_CAP Mixer", "SLIM TX11", "SLIM TX11 MUX"}, + {"AIF2_CAP Mixer", "SLIM TX13", "TX13 INP MUX"}, + /* SLIM_MIXER("AIF3_CAP Mixer"),*/ + {"AIF3_CAP Mixer", "SLIM TX0", "SLIM TX0 MUX"}, + {"AIF3_CAP Mixer", "SLIM TX1", "SLIM TX1 MUX"}, + {"AIF3_CAP Mixer", "SLIM TX2", "SLIM TX2 MUX"}, + {"AIF3_CAP Mixer", "SLIM TX3", "SLIM TX3 MUX"}, + {"AIF3_CAP Mixer", "SLIM TX4", "SLIM TX4 MUX"}, + {"AIF3_CAP Mixer", "SLIM TX5", "SLIM TX5 MUX"}, + {"AIF3_CAP Mixer", "SLIM TX6", "SLIM TX6 MUX"}, + {"AIF3_CAP Mixer", "SLIM TX7", "SLIM TX7 MUX"}, + {"AIF3_CAP Mixer", "SLIM TX8", "SLIM TX8 MUX"}, + {"AIF3_CAP Mixer", "SLIM TX9", "SLIM TX9 MUX"}, + {"AIF3_CAP Mixer", "SLIM TX10", "SLIM TX10 MUX"}, + {"AIF3_CAP Mixer", "SLIM TX11", "SLIM TX11 MUX"}, + {"AIF3_CAP Mixer", "SLIM TX13", "TX13 INP MUX"}, + + {"SLIM TX0 MUX", "DEC0", "ADC MUX0"}, + {"SLIM TX0 MUX", "RX_MIX_TX0", "RX MIX TX0 MUX"}, + {"SLIM TX0 MUX", "DEC0_192", "ADC US MUX0"}, + + {"SLIM TX1 MUX", "DEC1", "ADC MUX1"}, + {"SLIM TX1 MUX", "RX_MIX_TX1", "RX MIX TX1 MUX"}, + {"SLIM TX1 MUX", "DEC1_192", "ADC US MUX1"}, + + {"SLIM TX2 MUX", "DEC2", "ADC MUX2"}, + {"SLIM TX2 MUX", "RX_MIX_TX2", "RX MIX TX2 MUX"}, + {"SLIM TX2 MUX", "DEC2_192", "ADC US MUX2"}, + + {"SLIM TX3 MUX", "DEC3", "ADC MUX3"}, + {"SLIM TX3 MUX", "RX_MIX_TX3", "RX MIX TX3 MUX"}, + {"SLIM TX3 MUX", "DEC3_192", "ADC US MUX3"}, + + {"SLIM TX4 MUX", "DEC4", "ADC MUX4"}, + {"SLIM TX4 MUX", "RX_MIX_TX4", "RX MIX TX4 MUX"}, + {"SLIM TX4 MUX", "DEC4_192", "ADC US MUX4"}, + + {"SLIM TX5 MUX", "DEC5", "ADC MUX5"}, + {"SLIM TX5 MUX", "RX_MIX_TX5", "RX MIX TX5 MUX"}, + {"SLIM TX5 MUX", "DEC5_192", "ADC US MUX5"}, + + {"SLIM TX6 MUX", "DEC6", "ADC MUX6"}, + {"SLIM TX6 MUX", "RX_MIX_TX6", "RX MIX TX6 MUX"}, + {"SLIM TX6 MUX", "DEC6_192", "ADC US MUX6"}, + + {"SLIM TX7 MUX", "DEC7", "ADC MUX7"}, + {"SLIM TX7 MUX", "RX_MIX_TX7", "RX MIX TX7 MUX"}, + {"SLIM TX7 MUX", "DEC7_192", "ADC US MUX7"}, + + {"SLIM TX8 MUX", "DEC8", "ADC MUX8"}, + {"SLIM TX8 MUX", "RX_MIX_TX8", "RX MIX TX8 MUX"}, + {"SLIM TX8 MUX", "DEC8_192", "ADC US MUX8"}, + + {"SLIM TX9 MUX", "DEC7", "ADC MUX7"}, + {"SLIM TX9 MUX", "DEC7_192", "ADC US MUX7"}, + {"SLIM TX10 MUX", "DEC6", "ADC MUX6"}, + {"SLIM TX10 MUX", "DEC6_192", "ADC US MUX6"}, + + {"SLIM TX11 MUX", "DEC_0_5", "SLIM TX11 INP1 MUX"}, + {"SLIM TX11 MUX", "DEC_9_12", "SLIM TX11 INP1 MUX"}, + {"SLIM TX11 INP1 MUX", "DEC0", "ADC MUX0"}, + {"SLIM TX11 INP1 MUX", "DEC1", "ADC MUX1"}, + {"SLIM TX11 INP1 MUX", "DEC2", "ADC MUX2"}, + {"SLIM TX11 INP1 MUX", "DEC3", "ADC MUX3"}, + {"SLIM TX11 INP1 MUX", "DEC4", "ADC MUX4"}, + {"SLIM TX11 INP1 MUX", "DEC5", "ADC MUX5"}, + {"SLIM TX11 INP1 MUX", "RX_MIX_TX5", "RX MIX TX5 MUX"}, + + {"TX13 INP MUX", "MAD_BRDCST", "MAD_BROADCAST"}, + {"TX13 INP MUX", "CDC_DEC_5", "SLIM TX13 MUX"}, + {"SLIM TX13 MUX", "DEC5", "ADC MUX5"}, + + {"RX MIX TX0 MUX", "RX_MIX0", "RX INT0 SEC MIX"}, + {"RX MIX TX0 MUX", "RX_MIX1", "RX INT1 SEC MIX"}, + {"RX MIX TX0 MUX", "RX_MIX2", "RX INT2 SEC MIX"}, + {"RX MIX TX0 MUX", "RX_MIX3", "RX INT3 SEC MIX"}, + {"RX MIX TX0 MUX", "RX_MIX4", "RX INT4 SEC MIX"}, + {"RX MIX TX0 MUX", "RX_MIX5", "RX INT5 SEC MIX"}, + {"RX MIX TX0 MUX", "RX_MIX6", "RX INT6 SEC MIX"}, + {"RX MIX TX0 MUX", "RX_MIX7", "RX INT7 SEC MIX"}, + {"RX MIX TX0 MUX", "RX_MIX8", "RX INT8 SEC MIX"}, + {"RX MIX TX0 MUX", "RX_MIX_VBAT5", "RX INT5 VBAT"}, + {"RX MIX TX0 MUX", "RX_MIX_VBAT6", "RX INT6 VBAT"}, + {"RX MIX TX0 MUX", "RX_MIX_VBAT7", "RX INT7 VBAT"}, + {"RX MIX TX0 MUX", "RX_MIX_VBAT8", "RX INT8 VBAT"}, + + {"RX MIX TX1 MUX", "RX_MIX0", "RX INT0 SEC MIX"}, + {"RX MIX TX1 MUX", "RX_MIX1", "RX INT1 SEC MIX"}, + {"RX MIX TX1 MUX", "RX_MIX2", "RX INT2 SEC MIX"}, + {"RX MIX TX1 MUX", "RX_MIX3", "RX INT3 SEC MIX"}, + {"RX MIX TX1 MUX", "RX_MIX4", "RX INT4 SEC MIX"}, + {"RX MIX TX1 MUX", "RX_MIX5", "RX INT5 SEC MIX"}, + {"RX MIX TX1 MUX", "RX_MIX6", "RX INT6 SEC MIX"}, + {"RX MIX TX1 MUX", "RX_MIX7", "RX INT7 SEC MIX"}, + {"RX MIX TX1 MUX", "RX_MIX8", "RX INT8 SEC MIX"}, + {"RX MIX TX1 MUX", "RX_MIX_VBAT5", "RX INT5 VBAT"}, + {"RX MIX TX1 MUX", "RX_MIX_VBAT6", "RX INT6 VBAT"}, + {"RX MIX TX1 MUX", "RX_MIX_VBAT7", "RX INT7 VBAT"}, + {"RX MIX TX1 MUX", "RX_MIX_VBAT8", "RX INT8 VBAT"}, + + {"RX MIX TX2 MUX", "RX_MIX0", "RX INT0 SEC MIX"}, + {"RX MIX TX2 MUX", "RX_MIX1", "RX INT1 SEC MIX"}, + {"RX MIX TX2 MUX", "RX_MIX2", "RX INT2 SEC MIX"}, + {"RX MIX TX2 MUX", "RX_MIX3", "RX INT3 SEC MIX"}, + {"RX MIX TX2 MUX", "RX_MIX4", "RX INT4 SEC MIX"}, + {"RX MIX TX2 MUX", "RX_MIX5", "RX INT5 SEC MIX"}, + {"RX MIX TX2 MUX", "RX_MIX6", "RX INT6 SEC MIX"}, + {"RX MIX TX2 MUX", "RX_MIX7", "RX INT7 SEC MIX"}, + {"RX MIX TX2 MUX", "RX_MIX8", "RX INT8 SEC MIX"}, + {"RX MIX TX2 MUX", "RX_MIX_VBAT5", "RX INT5 VBAT"}, + {"RX MIX TX2 MUX", "RX_MIX_VBAT6", "RX INT6 VBAT"}, + {"RX MIX TX2 MUX", "RX_MIX_VBAT7", "RX INT7 VBAT"}, + {"RX MIX TX2 MUX", "RX_MIX_VBAT8", "RX INT8 VBAT"}, + + {"RX MIX TX3 MUX", "RX_MIX0", "RX INT0 SEC MIX"}, + {"RX MIX TX3 MUX", "RX_MIX1", "RX INT1 SEC MIX"}, + {"RX MIX TX3 MUX", "RX_MIX2", "RX INT2 SEC MIX"}, + {"RX MIX TX3 MUX", "RX_MIX3", "RX INT3 SEC MIX"}, + {"RX MIX TX3 MUX", "RX_MIX4", "RX INT4 SEC MIX"}, + {"RX MIX TX3 MUX", "RX_MIX5", "RX INT5 SEC MIX"}, + {"RX MIX TX3 MUX", "RX_MIX6", "RX INT6 SEC MIX"}, + {"RX MIX TX3 MUX", "RX_MIX7", "RX INT7 SEC MIX"}, + {"RX MIX TX3 MUX", "RX_MIX8", "RX INT8 SEC MIX"}, + {"RX MIX TX3 MUX", "RX_MIX_VBAT5", "RX INT5 VBAT"}, + {"RX MIX TX3 MUX", "RX_MIX_VBAT6", "RX INT6 VBAT"}, + {"RX MIX TX3 MUX", "RX_MIX_VBAT7", "RX INT7 VBAT"}, + {"RX MIX TX3 MUX", "RX_MIX_VBAT8", "RX INT8 VBAT"}, + + {"RX MIX TX4 MUX", "RX_MIX0", "RX INT0 SEC MIX"}, + {"RX MIX TX4 MUX", "RX_MIX1", "RX INT1 SEC MIX"}, + {"RX MIX TX4 MUX", "RX_MIX2", "RX INT2 SEC MIX"}, + {"RX MIX TX4 MUX", "RX_MIX3", "RX INT3 SEC MIX"}, + {"RX MIX TX4 MUX", "RX_MIX4", "RX INT4 SEC MIX"}, + {"RX MIX TX4 MUX", "RX_MIX5", "RX INT5 SEC MIX"}, + {"RX MIX TX4 MUX", "RX_MIX6", "RX INT6 SEC MIX"}, + {"RX MIX TX4 MUX", "RX_MIX7", "RX INT7 SEC MIX"}, + {"RX MIX TX4 MUX", "RX_MIX8", "RX INT8 SEC MIX"}, + {"RX MIX TX4 MUX", "RX_MIX_VBAT5", "RX INT5 VBAT"}, + {"RX MIX TX4 MUX", "RX_MIX_VBAT6", "RX INT6 VBAT"}, + {"RX MIX TX4 MUX", "RX_MIX_VBAT7", "RX INT7 VBAT"}, + {"RX MIX TX4 MUX", "RX_MIX_VBAT8", "RX INT8 VBAT"}, + + {"RX MIX TX5 MUX", "RX_MIX0", "RX INT0 SEC MIX"}, + {"RX MIX TX5 MUX", "RX_MIX1", "RX INT1 SEC MIX"}, + {"RX MIX TX5 MUX", "RX_MIX2", "RX INT2 SEC MIX"}, + {"RX MIX TX5 MUX", "RX_MIX3", "RX INT3 SEC MIX"}, + {"RX MIX TX5 MUX", "RX_MIX4", "RX INT4 SEC MIX"}, + {"RX MIX TX5 MUX", "RX_MIX5", "RX INT5 SEC MIX"}, + {"RX MIX TX5 MUX", "RX_MIX6", "RX INT6 SEC MIX"}, + {"RX MIX TX5 MUX", "RX_MIX7", "RX INT7 SEC MIX"}, + {"RX MIX TX5 MUX", "RX_MIX8", "RX INT8 SEC MIX"}, + {"RX MIX TX5 MUX", "RX_MIX_VBAT5", "RX INT5 VBAT"}, + {"RX MIX TX5 MUX", "RX_MIX_VBAT6", "RX INT6 VBAT"}, + {"RX MIX TX5 MUX", "RX_MIX_VBAT7", "RX INT7 VBAT"}, + {"RX MIX TX5 MUX", "RX_MIX_VBAT8", "RX INT8 VBAT"}, + + {"RX MIX TX6 MUX", "RX_MIX0", "RX INT0 SEC MIX"}, + {"RX MIX TX6 MUX", "RX_MIX1", "RX INT1 SEC MIX"}, + {"RX MIX TX6 MUX", "RX_MIX2", "RX INT2 SEC MIX"}, + {"RX MIX TX6 MUX", "RX_MIX3", "RX INT3 SEC MIX"}, + {"RX MIX TX6 MUX", "RX_MIX4", "RX INT4 SEC MIX"}, + {"RX MIX TX6 MUX", "RX_MIX5", "RX INT5 SEC MIX"}, + {"RX MIX TX6 MUX", "RX_MIX6", "RX INT6 SEC MIX"}, + {"RX MIX TX6 MUX", "RX_MIX7", "RX INT7 SEC MIX"}, + {"RX MIX TX6 MUX", "RX_MIX8", "RX INT8 SEC MIX"}, + {"RX MIX TX6 MUX", "RX_MIX_VBAT5", "RX INT5 VBAT"}, + {"RX MIX TX6 MUX", "RX_MIX_VBAT6", "RX INT6 VBAT"}, + {"RX MIX TX6 MUX", "RX_MIX_VBAT7", "RX INT7 VBAT"}, + {"RX MIX TX6 MUX", "RX_MIX_VBAT8", "RX INT8 VBAT"}, + + {"RX MIX TX7 MUX", "RX_MIX0", "RX INT0 SEC MIX"}, + {"RX MIX TX7 MUX", "RX_MIX1", "RX INT1 SEC MIX"}, + {"RX MIX TX7 MUX", "RX_MIX2", "RX INT2 SEC MIX"}, + {"RX MIX TX7 MUX", "RX_MIX3", "RX INT3 SEC MIX"}, + {"RX MIX TX7 MUX", "RX_MIX4", "RX INT4 SEC MIX"}, + {"RX MIX TX7 MUX", "RX_MIX5", "RX INT5 SEC MIX"}, + {"RX MIX TX7 MUX", "RX_MIX6", "RX INT6 SEC MIX"}, + {"RX MIX TX7 MUX", "RX_MIX7", "RX INT7 SEC MIX"}, + {"RX MIX TX7 MUX", "RX_MIX8", "RX INT8 SEC MIX"}, + {"RX MIX TX7 MUX", "RX_MIX_VBAT5", "RX INT5 VBAT"}, + {"RX MIX TX7 MUX", "RX_MIX_VBAT6", "RX INT6 VBAT"}, + {"RX MIX TX7 MUX", "RX_MIX_VBAT7", "RX INT7 VBAT"}, + {"RX MIX TX7 MUX", "RX_MIX_VBAT8", "RX INT8 VBAT"}, + + {"RX MIX TX8 MUX", "RX_MIX0", "RX INT0 SEC MIX"}, + {"RX MIX TX8 MUX", "RX_MIX1", "RX INT1 SEC MIX"}, + {"RX MIX TX8 MUX", "RX_MIX2", "RX INT2 SEC MIX"}, + {"RX MIX TX8 MUX", "RX_MIX3", "RX INT3 SEC MIX"}, + {"RX MIX TX8 MUX", "RX_MIX4", "RX INT4 SEC MIX"}, + {"RX MIX TX8 MUX", "RX_MIX5", "RX INT5 SEC MIX"}, + {"RX MIX TX8 MUX", "RX_MIX6", "RX INT6 SEC MIX"}, + {"RX MIX TX8 MUX", "RX_MIX7", "RX INT7 SEC MIX"}, + {"RX MIX TX8 MUX", "RX_MIX8", "RX INT8 SEC MIX"}, + {"RX MIX TX8 MUX", "RX_MIX_VBAT5", "RX INT5 VBAT"}, + {"RX MIX TX8 MUX", "RX_MIX_VBAT6", "RX INT6 VBAT"}, + {"RX MIX TX8 MUX", "RX_MIX_VBAT7", "RX INT7 VBAT"}, + {"RX MIX TX8 MUX", "RX_MIX_VBAT8", "RX INT8 VBAT"}, + + {"ADC US MUX0", "US_Switch", "ADC MUX0"}, + {"ADC US MUX1", "US_Switch", "ADC MUX1"}, + {"ADC US MUX2", "US_Switch", "ADC MUX2"}, + {"ADC US MUX3", "US_Switch", "ADC MUX3"}, + {"ADC US MUX4", "US_Switch", "ADC MUX4"}, + {"ADC US MUX5", "US_Switch", "ADC MUX5"}, + {"ADC US MUX6", "US_Switch", "ADC MUX6"}, + {"ADC US MUX7", "US_Switch", "ADC MUX7"}, + {"ADC US MUX8", "US_Switch", "ADC MUX8"}, + {"ADC MUX0", "DMIC", "DMIC MUX0"}, + {"ADC MUX0", "AMIC", "AMIC MUX0"}, + {"ADC MUX1", "DMIC", "DMIC MUX1"}, + {"ADC MUX1", "AMIC", "AMIC MUX1"}, + {"ADC MUX2", "DMIC", "DMIC MUX2"}, + {"ADC MUX2", "AMIC", "AMIC MUX2"}, + {"ADC MUX3", "DMIC", "DMIC MUX3"}, + {"ADC MUX3", "AMIC", "AMIC MUX3"}, + {"ADC MUX4", "DMIC", "DMIC MUX4"}, + {"ADC MUX4", "AMIC", "AMIC MUX4"}, + {"ADC MUX5", "DMIC", "DMIC MUX5"}, + {"ADC MUX5", "AMIC", "AMIC MUX5"}, + {"ADC MUX6", "DMIC", "DMIC MUX6"}, + {"ADC MUX6", "AMIC", "AMIC MUX6"}, + {"ADC MUX7", "DMIC", "DMIC MUX7"}, + {"ADC MUX7", "AMIC", "AMIC MUX7"}, + {"ADC MUX8", "DMIC", "DMIC MUX8"}, + {"ADC MUX8", "AMIC", "AMIC MUX8"}, + {"ADC MUX10", "DMIC", "DMIC MUX10"}, + {"ADC MUX10", "AMIC", "AMIC MUX10"}, + {"ADC MUX11", "DMIC", "DMIC MUX11"}, + {"ADC MUX11", "AMIC", "AMIC MUX11"}, + {"ADC MUX12", "DMIC", "DMIC MUX12"}, + {"ADC MUX12", "AMIC", "AMIC MUX12"}, + {"ADC MUX13", "DMIC", "DMIC MUX13"}, + {"ADC MUX13", "AMIC", "AMIC MUX13"}, + + {"ADC MUX0", "ANC_FB_TUNE1", "ADC MUX10"}, + {"ADC MUX0", "ANC_FB_TUNE1", "ADC MUX11"}, + {"ADC MUX0", "ANC_FB_TUNE2", "ADC MUX12"}, + {"ADC MUX0", "ANC_FB_TUNE2", "ADC MUX13"}, + {"ADC MUX1", "ANC_FB_TUNE1", "ADC MUX10"}, + {"ADC MUX1", "ANC_FB_TUNE1", "ADC MUX11"}, + {"ADC MUX1", "ANC_FB_TUNE2", "ADC MUX12"}, + {"ADC MUX1", "ANC_FB_TUNE2", "ADC MUX13"}, + {"ADC MUX2", "ANC_FB_TUNE1", "ADC MUX10"}, + {"ADC MUX2", "ANC_FB_TUNE1", "ADC MUX11"}, + {"ADC MUX2", "ANC_FB_TUNE2", "ADC MUX12"}, + {"ADC MUX2", "ANC_FB_TUNE2", "ADC MUX13"}, + {"ADC MUX3", "ANC_FB_TUNE1", "ADC MUX10"}, + {"ADC MUX3", "ANC_FB_TUNE1", "ADC MUX11"}, + {"ADC MUX3", "ANC_FB_TUNE2", "ADC MUX12"}, + {"ADC MUX3", "ANC_FB_TUNE2", "ADC MUX13"}, + {"ADC MUX4", "ANC_FB_TUNE1", "ADC MUX10"}, + {"ADC MUX4", "ANC_FB_TUNE1", "ADC MUX11"}, + {"ADC MUX4", "ANC_FB_TUNE2", "ADC MUX12"}, + {"ADC MUX4", "ANC_FB_TUNE2", "ADC MUX13"}, + {"ADC MUX5", "ANC_FB_TUNE1", "ADC MUX10"}, + {"ADC MUX5", "ANC_FB_TUNE1", "ADC MUX11"}, + {"ADC MUX5", "ANC_FB_TUNE2", "ADC MUX12"}, + {"ADC MUX5", "ANC_FB_TUNE2", "ADC MUX13"}, + {"ADC MUX6", "ANC_FB_TUNE1", "ADC MUX10"}, + {"ADC MUX6", "ANC_FB_TUNE1", "ADC MUX11"}, + {"ADC MUX6", "ANC_FB_TUNE2", "ADC MUX12"}, + {"ADC MUX6", "ANC_FB_TUNE2", "ADC MUX13"}, + {"ADC MUX7", "ANC_FB_TUNE1", "ADC MUX10"}, + {"ADC MUX7", "ANC_FB_TUNE1", "ADC MUX11"}, + {"ADC MUX7", "ANC_FB_TUNE2", "ADC MUX12"}, + {"ADC MUX7", "ANC_FB_TUNE2", "ADC MUX13"}, + {"ADC MUX8", "ANC_FB_TUNE1", "ADC MUX10"}, + {"ADC MUX8", "ANC_FB_TUNE1", "ADC MUX11"}, + {"ADC MUX8", "ANC_FB_TUNE2", "ADC MUX12"}, + {"ADC MUX8", "ANC_FB_TUNE2", "ADC MUX13"}, + + {"DMIC MUX0", "DMIC0", "DMIC0"}, + {"DMIC MUX0", "DMIC1", "DMIC1"}, + {"DMIC MUX0", "DMIC2", "DMIC2"}, + {"DMIC MUX0", "DMIC3", "DMIC3"}, + {"DMIC MUX0", "DMIC4", "DMIC4"}, + {"DMIC MUX0", "DMIC5", "DMIC5"}, + {"AMIC MUX0", "ADC1", "ADC1"}, + {"AMIC MUX0", "ADC2", "ADC2"}, + {"AMIC MUX0", "ADC3", "ADC3"}, + {"AMIC MUX0", "ADC4", "ADC4"}, + {"AMIC MUX0", "ADC5", "ADC5"}, + {"AMIC MUX0", "ADC6", "ADC6"}, + + {"DMIC MUX1", "DMIC0", "DMIC0"}, + {"DMIC MUX1", "DMIC1", "DMIC1"}, + {"DMIC MUX1", "DMIC2", "DMIC2"}, + {"DMIC MUX1", "DMIC3", "DMIC3"}, + {"DMIC MUX1", "DMIC4", "DMIC4"}, + {"DMIC MUX1", "DMIC5", "DMIC5"}, + {"AMIC MUX1", "ADC1", "ADC1"}, + {"AMIC MUX1", "ADC2", "ADC2"}, + {"AMIC MUX1", "ADC3", "ADC3"}, + {"AMIC MUX1", "ADC4", "ADC4"}, + {"AMIC MUX1", "ADC5", "ADC5"}, + {"AMIC MUX1", "ADC6", "ADC6"}, + + {"DMIC MUX2", "DMIC0", "DMIC0"}, + {"DMIC MUX2", "DMIC1", "DMIC1"}, + {"DMIC MUX2", "DMIC2", "DMIC2"}, + {"DMIC MUX2", "DMIC3", "DMIC3"}, + {"DMIC MUX2", "DMIC4", "DMIC4"}, + {"DMIC MUX2", "DMIC5", "DMIC5"}, + {"AMIC MUX2", "ADC1", "ADC1"}, + {"AMIC MUX2", "ADC2", "ADC2"}, + {"AMIC MUX2", "ADC3", "ADC3"}, + {"AMIC MUX2", "ADC4", "ADC4"}, + {"AMIC MUX2", "ADC5", "ADC5"}, + {"AMIC MUX2", "ADC6", "ADC6"}, + + {"DMIC MUX3", "DMIC0", "DMIC0"}, + {"DMIC MUX3", "DMIC1", "DMIC1"}, + {"DMIC MUX3", "DMIC2", "DMIC2"}, + {"DMIC MUX3", "DMIC3", "DMIC3"}, + {"DMIC MUX3", "DMIC4", "DMIC4"}, + {"DMIC MUX3", "DMIC5", "DMIC5"}, + {"AMIC MUX3", "ADC1", "ADC1"}, + {"AMIC MUX3", "ADC2", "ADC2"}, + {"AMIC MUX3", "ADC3", "ADC3"}, + {"AMIC MUX3", "ADC4", "ADC4"}, + {"AMIC MUX3", "ADC5", "ADC5"}, + {"AMIC MUX3", "ADC6", "ADC6"}, + + {"DMIC MUX4", "DMIC0", "DMIC0"}, + {"DMIC MUX4", "DMIC1", "DMIC1"}, + {"DMIC MUX4", "DMIC2", "DMIC2"}, + {"DMIC MUX4", "DMIC3", "DMIC3"}, + {"DMIC MUX4", "DMIC4", "DMIC4"}, + {"DMIC MUX4", "DMIC5", "DMIC5"}, + {"AMIC MUX4", "ADC1", "ADC1"}, + {"AMIC MUX4", "ADC2", "ADC2"}, + {"AMIC MUX4", "ADC3", "ADC3"}, + {"AMIC MUX4", "ADC4", "ADC4"}, + {"AMIC MUX4", "ADC5", "ADC5"}, + {"AMIC MUX4", "ADC6", "ADC6"}, + + {"DMIC MUX5", "DMIC0", "DMIC0"}, + {"DMIC MUX5", "DMIC1", "DMIC1"}, + {"DMIC MUX5", "DMIC2", "DMIC2"}, + {"DMIC MUX5", "DMIC3", "DMIC3"}, + {"DMIC MUX5", "DMIC4", "DMIC4"}, + {"DMIC MUX5", "DMIC5", "DMIC5"}, + {"AMIC MUX5", "ADC1", "ADC1"}, + {"AMIC MUX5", "ADC2", "ADC2"}, + {"AMIC MUX5", "ADC3", "ADC3"}, + {"AMIC MUX5", "ADC4", "ADC4"}, + {"AMIC MUX5", "ADC5", "ADC5"}, + {"AMIC MUX5", "ADC6", "ADC6"}, + + {"DMIC MUX6", "DMIC0", "DMIC0"}, + {"DMIC MUX6", "DMIC1", "DMIC1"}, + {"DMIC MUX6", "DMIC2", "DMIC2"}, + {"DMIC MUX6", "DMIC3", "DMIC3"}, + {"DMIC MUX6", "DMIC4", "DMIC4"}, + {"DMIC MUX6", "DMIC5", "DMIC5"}, + {"AMIC MUX6", "ADC1", "ADC1"}, + {"AMIC MUX6", "ADC2", "ADC2"}, + {"AMIC MUX6", "ADC3", "ADC3"}, + {"AMIC MUX6", "ADC4", "ADC4"}, + {"AMIC MUX6", "ADC5", "ADC5"}, + {"AMIC MUX6", "ADC6", "ADC6"}, + + {"DMIC MUX7", "DMIC0", "DMIC0"}, + {"DMIC MUX7", "DMIC1", "DMIC1"}, + {"DMIC MUX7", "DMIC2", "DMIC2"}, + {"DMIC MUX7", "DMIC3", "DMIC3"}, + {"DMIC MUX7", "DMIC4", "DMIC4"}, + {"DMIC MUX7", "DMIC5", "DMIC5"}, + {"AMIC MUX7", "ADC1", "ADC1"}, + {"AMIC MUX7", "ADC2", "ADC2"}, + {"AMIC MUX7", "ADC3", "ADC3"}, + {"AMIC MUX7", "ADC4", "ADC4"}, + {"AMIC MUX7", "ADC5", "ADC5"}, + {"AMIC MUX7", "ADC6", "ADC6"}, + + {"DMIC MUX8", "DMIC0", "DMIC0"}, + {"DMIC MUX8", "DMIC1", "DMIC1"}, + {"DMIC MUX8", "DMIC2", "DMIC2"}, + {"DMIC MUX8", "DMIC3", "DMIC3"}, + {"DMIC MUX8", "DMIC4", "DMIC4"}, + {"DMIC MUX8", "DMIC5", "DMIC5"}, + {"AMIC MUX8", "ADC1", "ADC1"}, + {"AMIC MUX8", "ADC2", "ADC2"}, + {"AMIC MUX8", "ADC3", "ADC3"}, + {"AMIC MUX8", "ADC4", "ADC4"}, + {"AMIC MUX8", "ADC5", "ADC5"}, + {"AMIC MUX8", "ADC6", "ADC6"}, + + {"DMIC MUX10", "DMIC0", "DMIC0"}, + {"DMIC MUX10", "DMIC1", "DMIC1"}, + {"DMIC MUX10", "DMIC2", "DMIC2"}, + {"DMIC MUX10", "DMIC3", "DMIC3"}, + {"DMIC MUX10", "DMIC4", "DMIC4"}, + {"DMIC MUX10", "DMIC5", "DMIC5"}, + {"AMIC MUX10", "ADC1", "ADC1"}, + {"AMIC MUX10", "ADC2", "ADC2"}, + {"AMIC MUX10", "ADC3", "ADC3"}, + {"AMIC MUX10", "ADC4", "ADC4"}, + {"AMIC MUX10", "ADC5", "ADC5"}, + {"AMIC MUX10", "ADC6", "ADC6"}, + + {"DMIC MUX11", "DMIC0", "DMIC0"}, + {"DMIC MUX11", "DMIC1", "DMIC1"}, + {"DMIC MUX11", "DMIC2", "DMIC2"}, + {"DMIC MUX11", "DMIC3", "DMIC3"}, + {"DMIC MUX11", "DMIC4", "DMIC4"}, + {"DMIC MUX11", "DMIC5", "DMIC5"}, + {"AMIC MUX11", "ADC1", "ADC1"}, + {"AMIC MUX11", "ADC2", "ADC2"}, + {"AMIC MUX11", "ADC3", "ADC3"}, + {"AMIC MUX11", "ADC4", "ADC4"}, + {"AMIC MUX11", "ADC5", "ADC5"}, + {"AMIC MUX11", "ADC6", "ADC6"}, + + {"DMIC MUX12", "DMIC0", "DMIC0"}, + {"DMIC MUX12", "DMIC1", "DMIC1"}, + {"DMIC MUX12", "DMIC2", "DMIC2"}, + {"DMIC MUX12", "DMIC3", "DMIC3"}, + {"DMIC MUX12", "DMIC4", "DMIC4"}, + {"DMIC MUX12", "DMIC5", "DMIC5"}, + {"AMIC MUX12", "ADC1", "ADC1"}, + {"AMIC MUX12", "ADC2", "ADC2"}, + {"AMIC MUX12", "ADC3", "ADC3"}, + {"AMIC MUX12", "ADC4", "ADC4"}, + {"AMIC MUX12", "ADC5", "ADC5"}, + {"AMIC MUX12", "ADC6", "ADC6"}, + + {"DMIC MUX13", "DMIC0", "DMIC0"}, + {"DMIC MUX13", "DMIC1", "DMIC1"}, + {"DMIC MUX13", "DMIC2", "DMIC2"}, + {"DMIC MUX13", "DMIC3", "DMIC3"}, + {"DMIC MUX13", "DMIC4", "DMIC4"}, + {"DMIC MUX13", "DMIC5", "DMIC5"}, + {"AMIC MUX13", "ADC1", "ADC1"}, + {"AMIC MUX13", "ADC2", "ADC2"}, + {"AMIC MUX13", "ADC3", "ADC3"}, + {"AMIC MUX13", "ADC4", "ADC4"}, + {"AMIC MUX13", "ADC5", "ADC5"}, + {"AMIC MUX13", "ADC6", "ADC6"}, + /* ADC Connections */ + {"ADC1", NULL, "AMIC1"}, + {"ADC2", NULL, "AMIC2"}, + {"ADC3", NULL, "AMIC3"}, + {"ADC4", NULL, "AMIC4"}, + {"ADC5", NULL, "AMIC5"}, + {"ADC6", NULL, "AMIC6"}, + + {"RX INT0_1 MIX1", NULL, "RX INT0_1 MIX1 INP0"}, + {"RX INT0_1 MIX1", NULL, "RX INT0_1 MIX1 INP1"}, + {"RX INT0_1 MIX1", NULL, "RX INT0_1 MIX1 INP2"}, + {"RX INT1_1 MIX1", NULL, "RX INT1_1 MIX1 INP0"}, + {"RX INT1_1 MIX1", NULL, "RX INT1_1 MIX1 INP1"}, + {"RX INT1_1 MIX1", NULL, "RX INT1_1 MIX1 INP2"}, + {"RX INT2_1 MIX1", NULL, "RX INT2_1 MIX1 INP0"}, + {"RX INT2_1 MIX1", NULL, "RX INT2_1 MIX1 INP1"}, + {"RX INT2_1 MIX1", NULL, "RX INT2_1 MIX1 INP2"}, + {"RX INT3_1 MIX1", NULL, "RX INT3_1 MIX1 INP0"}, + {"RX INT3_1 MIX1", NULL, "RX INT3_1 MIX1 INP1"}, + {"RX INT3_1 MIX1", NULL, "RX INT3_1 MIX1 INP2"}, + {"RX INT4_1 MIX1", NULL, "RX INT4_1 MIX1 INP0"}, + {"RX INT4_1 MIX1", NULL, "RX INT4_1 MIX1 INP1"}, + {"RX INT4_1 MIX1", NULL, "RX INT4_1 MIX1 INP2"}, + {"RX INT5_1 MIX1", NULL, "RX INT5_1 MIX1 INP0"}, + {"RX INT5_1 MIX1", NULL, "RX INT5_1 MIX1 INP1"}, + {"RX INT5_1 MIX1", NULL, "RX INT5_1 MIX1 INP2"}, + {"RX INT6_1 MIX1", NULL, "RX INT6_1 MIX1 INP0"}, + {"RX INT6_1 MIX1", NULL, "RX INT6_1 MIX1 INP1"}, + {"RX INT6_1 MIX1", NULL, "RX INT6_1 MIX1 INP2"}, + {"RX INT7_1 MIX1", NULL, "RX INT7_1 MIX1 INP0"}, + {"RX INT7_1 MIX1", NULL, "RX INT7_1 MIX1 INP1"}, + {"RX INT7_1 MIX1", NULL, "RX INT7_1 MIX1 INP2"}, + {"RX INT8_1 MIX1", NULL, "RX INT8_1 MIX1 INP0"}, + {"RX INT8_1 MIX1", NULL, "RX INT8_1 MIX1 INP1"}, + {"RX INT8_1 MIX1", NULL, "RX INT8_1 MIX1 INP2"}, + + {"RX INT0 SEC MIX", NULL, "RX INT0_1 MIX1"}, + {"RX INT0 MIX2", NULL, "RX INT0 SEC MIX"}, + {"RX INT0 MIX2", NULL, "RX INT0 MIX2 INP"}, + {"RX INT0 INTERP", NULL, "RX INT0 MIX2"}, + {"RX INT0 DEM MUX", "CLSH_DSM_OUT", "RX INT0 INTERP"}, + {"RX INT0 DAC", NULL, "RX INT0 DEM MUX"}, + {"RX INT0 DAC", NULL, "RX_BIAS"}, + {"EAR PA", NULL, "RX INT0 DAC"}, + {"EAR", NULL, "EAR PA"}, + + {"SPL SRC0 MUX", "SRC_IN_HPHL", "RX INT1_1 MIX1"}, + {"RX INT1 SPLINE MIX", NULL, "RX INT1_1 MIX1"}, + {"RX INT1 SPLINE MIX", "HPHL Switch", "SPL SRC0 MUX"}, + {"RX INT1_1 NATIVE MUX", "ON", "RX INT1_1 MIX1"}, + {"RX INT1 SPLINE MIX", NULL, "RX INT1_1 NATIVE MUX"}, + {"RX INT1_1 NATIVE MUX", NULL, "RX INT1 NATIVE SUPPLY"}, + {"RX INT1 SEC MIX", NULL, "RX INT1 SPLINE MIX"}, + {"RX INT1 MIX2", NULL, "RX INT1 SEC MIX"}, + {"RX INT1 MIX2", NULL, "RX INT1 MIX2 INP"}, + {"RX INT1 INTERP", NULL, "RX INT1 MIX2"}, + {"RX INT1 DEM MUX", "CLSH_DSM_OUT", "RX INT1 INTERP"}, + {"RX INT1 DAC", NULL, "RX INT1 DEM MUX"}, + {"RX INT1 DAC", NULL, "RX_BIAS"}, + {"HPHL PA", NULL, "RX INT1 DAC"}, + {"HPHL", NULL, "HPHL PA"}, + + {"SPL SRC1 MUX", "SRC_IN_HPHR", "RX INT2_1 MIX1"}, + {"RX INT2 SPLINE MIX", NULL, "RX INT2_1 MIX1"}, + {"RX INT2 SPLINE MIX", "HPHR Switch", "SPL SRC1 MUX"}, + {"RX INT2_1 NATIVE MUX", "ON", "RX INT2_1 MIX1"}, + {"RX INT2 SPLINE MIX", NULL, "RX INT2_1 NATIVE MUX"}, + {"RX INT2_1 NATIVE MUX", NULL, "RX INT2 NATIVE SUPPLY"}, + {"RX INT2 SEC MIX", NULL, "RX INT2 SPLINE MIX"}, + {"RX INT2 MIX2", NULL, "RX INT2 SEC MIX"}, + {"RX INT2 MIX2", NULL, "RX INT2 MIX2 INP"}, + {"RX INT2 INTERP", NULL, "RX INT2 MIX2"}, + {"RX INT2 DEM MUX", "CLSH_DSM_OUT", "RX INT2 INTERP"}, + {"RX INT2 DAC", NULL, "RX INT2 DEM MUX"}, + {"RX INT2 DAC", NULL, "RX_BIAS"}, + {"HPHR PA", NULL, "RX INT2 DAC"}, + {"HPHR", NULL, "HPHR PA"}, + + {"SPL SRC0 MUX", "SRC_IN_LO1", "RX INT3_1 MIX1"}, + {"RX INT3 SPLINE MIX", NULL, "RX INT3_1 MIX1"}, + {"RX INT3 SPLINE MIX", "LO1 Switch", "SPL SRC0 MUX"}, + {"RX INT3_1 NATIVE MUX", "ON", "RX INT3_1 MIX1"}, + {"RX INT3 SPLINE MIX", NULL, "RX INT3_1 NATIVE MUX"}, + {"RX INT3_1 NATIVE MUX", NULL, "RX INT3 NATIVE SUPPLY"}, + {"RX INT3 SEC MIX", NULL, "RX INT3 SPLINE MIX"}, + {"RX INT3 MIX2", NULL, "RX INT3 SEC MIX"}, + {"RX INT3 MIX2", NULL, "RX INT3 MIX2 INP"}, + {"RX INT3 INTERP", NULL, "RX INT3 MIX2"}, + {"RX INT3 DAC", NULL, "RX INT3 INTERP"}, + {"RX INT3 DAC", NULL, "RX_BIAS"}, + {"LINEOUT1 PA", NULL, "RX INT3 DAC"}, + {"LINEOUT1", NULL, "LINEOUT1 PA"}, + + {"SPL SRC1 MUX", "SRC_IN_LO2", "RX INT4_1 MIX1"}, + {"RX INT4 SPLINE MIX", NULL, "RX INT4_1 MIX1"}, + {"RX INT4 SPLINE MIX", "LO2 Switch", "SPL SRC1 MUX"}, + {"RX INT4_1 NATIVE MUX", "ON", "RX INT4_1 MIX1"}, + {"RX INT4 SPLINE MIX", NULL, "RX INT4_1 NATIVE MUX"}, + {"RX INT4_1 NATIVE MUX", NULL, "RX INT4 NATIVE SUPPLY"}, + {"RX INT4 SEC MIX", NULL, "RX INT4 SPLINE MIX"}, + {"RX INT4 MIX2", NULL, "RX INT4 SEC MIX"}, + {"RX INT4 MIX2", NULL, "RX INT4 MIX2 INP"}, + {"RX INT4 INTERP", NULL, "RX INT4 MIX2"}, + {"RX INT4 DAC", NULL, "RX INT4 INTERP"}, + {"RX INT4 DAC", NULL, "RX_BIAS"}, + {"LINEOUT2 PA", NULL, "RX INT4 DAC"}, + {"LINEOUT2", NULL, "LINEOUT2 PA"}, + + {"SPL SRC2 MUX", "SRC_IN_LO3", "RX INT5_1 MIX1"}, + {"RX INT5 SPLINE MIX", NULL, "RX INT5_1 MIX1"}, + {"RX INT5 SPLINE MIX", "LO3 Switch", "SPL SRC2 MUX"}, + {"RX INT5 SEC MIX", NULL, "RX INT5 SPLINE MIX"}, + {"RX INT5 MIX2", NULL, "RX INT5 SEC MIX"}, + {"RX INT5 INTERP", NULL, "RX INT5 MIX2"}, + + {"RX INT5 VBAT", "LO3 VBAT Enable", "RX INT5 INTERP"}, + {"RX INT5 DAC", NULL, "RX INT5 VBAT"}, + + {"RX INT5 DAC", NULL, "RX INT5 INTERP"}, + {"RX INT5 DAC", NULL, "RX_BIAS"}, + {"LINEOUT3 PA", NULL, "RX INT5 DAC"}, + {"LINEOUT3", NULL, "LINEOUT3 PA"}, + + {"SPL SRC3 MUX", "SRC_IN_LO4", "RX INT6_1 MIX1"}, + {"RX INT6 SPLINE MIX", NULL, "RX INT6_1 MIX1"}, + {"RX INT6 SPLINE MIX", "LO4 Switch", "SPL SRC3 MUX"}, + {"RX INT6 SEC MIX", NULL, "RX INT6 SPLINE MIX"}, + {"RX INT6 MIX2", NULL, "RX INT6 SEC MIX"}, + {"RX INT6 INTERP", NULL, "RX INT6 MIX2"}, + + {"RX INT6 VBAT", "LO4 VBAT Enable", "RX INT6 INTERP"}, + {"RX INT6 DAC", NULL, "RX INT6 VBAT"}, + + {"RX INT6 DAC", NULL, "RX INT6 INTERP"}, + {"RX INT6 DAC", NULL, "RX_BIAS"}, + {"LINEOUT4 PA", NULL, "RX INT6 DAC"}, + {"LINEOUT4", NULL, "LINEOUT4 PA"}, + + {"SPL SRC2 MUX", "SRC_IN_SPKRL", "RX INT7_1 MIX1"}, + {"RX INT7 SPLINE MIX", NULL, "RX INT7_1 MIX1"}, + {"RX INT7 SPLINE MIX", "SPKRL Switch", "SPL SRC2 MUX"}, + {"RX INT7 SEC MIX", NULL, "RX INT7 SPLINE MIX"}, + {"RX INT7 MIX2", NULL, "RX INT7 SEC MIX"}, + {"RX INT7 MIX2", NULL, "RX INT7 MIX2 INP"}, + + {"RX INT7 INTERP", NULL, "RX INT7 MIX2"}, + + {"RX INT7 VBAT", "SPKRL VBAT Enable", "RX INT7 INTERP"}, + {"RX INT7 CHAIN", NULL, "RX INT7 VBAT"}, + + {"RX INT7 CHAIN", NULL, "RX INT7 INTERP"}, + {"RX INT7 CHAIN", NULL, "RX_BIAS"}, + {"SPK1 OUT", NULL, "RX INT7 CHAIN"}, + + {"ANC SPKR PA Enable", "Switch", "RX INT7 CHAIN"}, + {"ANC SPK1 PA", NULL, "ANC SPKR PA Enable"}, + {"SPK1 OUT", NULL, "ANC SPK1 PA"}, + + {"SPL SRC3 MUX", "SRC_IN_SPKRR", "RX INT8_1 MIX1"}, + {"RX INT8 SPLINE MIX", NULL, "RX INT8_1 MIX1"}, + {"RX INT8 SPLINE MIX", "SPKRR Switch", "SPL SRC3 MUX"}, + {"RX INT8 SEC MIX", NULL, "RX INT8 SPLINE MIX"}, + {"RX INT8 INTERP", NULL, "RX INT8 SEC MIX"}, + + {"RX INT8 VBAT", "SPKRR VBAT Enable", "RX INT8 INTERP"}, + {"RX INT8 CHAIN", NULL, "RX INT8 VBAT"}, + + {"RX INT8 CHAIN", NULL, "RX INT8 INTERP"}, + {"RX INT8 CHAIN", NULL, "RX_BIAS"}, + {"SPK2 OUT", NULL, "RX INT8 CHAIN"}, + + {"ANC0 FB MUX", "ANC_IN_EAR", "RX INT0 MIX2"}, + {"ANC0 FB MUX", "ANC_IN_HPHL", "RX INT1 MIX2"}, + {"ANC0 FB MUX", "ANC_IN_LO1", "RX INT3 MIX2"}, + {"ANC0 FB MUX", "ANC_IN_EAR_SPKR", "RX INT7 MIX2"}, + {"ANC1 FB MUX", "ANC_IN_HPHR", "RX INT2 MIX2"}, + {"ANC1 FB MUX", "ANC_IN_LO2", "RX INT4 MIX2"}, + + {"ANC HPHL Enable", "Switch", "ADC MUX10"}, + {"ANC HPHL Enable", "Switch", "ADC MUX11"}, + {"RX INT1 MIX2", NULL, "ANC HPHL Enable"}, + + {"ANC HPHR Enable", "Switch", "ADC MUX12"}, + {"ANC HPHR Enable", "Switch", "ADC MUX13"}, + {"RX INT2 MIX2", NULL, "ANC HPHR Enable"}, + + {"ANC EAR Enable", "Switch", "ADC MUX10"}, + {"ANC EAR Enable", "Switch", "ADC MUX11"}, + {"RX INT0 MIX2", NULL, "ANC EAR Enable"}, + + {"ANC OUT EAR SPKR Enable", "Switch", "ADC MUX10"}, + {"ANC OUT EAR SPKR Enable", "Switch", "ADC MUX11"}, + {"RX INT7 MIX2", NULL, "ANC OUT EAR SPKR Enable"}, + + {"ANC LINEOUT1 Enable", "Switch", "ADC MUX10"}, + {"ANC LINEOUT1 Enable", "Switch", "ADC MUX11"}, + {"RX INT3 MIX2", NULL, "ANC LINEOUT1 Enable"}, + + {"ANC LINEOUT2 Enable", "Switch", "ADC MUX12"}, + {"ANC LINEOUT2 Enable", "Switch", "ADC MUX13"}, + {"RX INT4 MIX2", NULL, "ANC LINEOUT2 Enable"}, + + {"ANC EAR PA", NULL, "RX INT0 DAC"}, + {"ANC EAR", NULL, "ANC EAR PA"}, + {"ANC HPHL PA", NULL, "RX INT1 DAC"}, + {"ANC HPHL", NULL, "ANC HPHL PA"}, + {"ANC HPHR PA", NULL, "RX INT2 DAC"}, + {"ANC HPHR", NULL, "ANC HPHR PA"}, + {"ANC LINEOUT1 PA", NULL, "RX INT3 DAC"}, + {"ANC LINEOUT1", NULL, "ANC LINEOUT1 PA"}, + {"ANC LINEOUT2 PA", NULL, "RX INT4 DAC"}, + {"ANC LINEOUT2", NULL, "ANC LINEOUT2 PA"}, + + /* SLIM_MUX("AIF1_PB", "AIF1 PB"),*/ + {"SLIM RX0 MUX", "AIF1_PB", "AIF1 PB"}, + {"SLIM RX1 MUX", "AIF1_PB", "AIF1 PB"}, + {"SLIM RX2 MUX", "AIF1_PB", "AIF1 PB"}, + {"SLIM RX3 MUX", "AIF1_PB", "AIF1 PB"}, + {"SLIM RX4 MUX", "AIF1_PB", "AIF1 PB"}, + {"SLIM RX5 MUX", "AIF1_PB", "AIF1 PB"}, + {"SLIM RX6 MUX", "AIF1_PB", "AIF1 PB"}, + {"SLIM RX7 MUX", "AIF1_PB", "AIF1 PB"}, + /* SLIM_MUX("AIF2_PB", "AIF2 PB"),*/ + {"SLIM RX0 MUX", "AIF2_PB", "AIF2 PB"}, + {"SLIM RX1 MUX", "AIF2_PB", "AIF2 PB"}, + {"SLIM RX2 MUX", "AIF2_PB", "AIF2 PB"}, + {"SLIM RX3 MUX", "AIF2_PB", "AIF2 PB"}, + {"SLIM RX4 MUX", "AIF2_PB", "AIF2 PB"}, + {"SLIM RX5 MUX", "AIF2_PB", "AIF2 PB"}, + {"SLIM RX6 MUX", "AIF2_PB", "AIF2 PB"}, + {"SLIM RX7 MUX", "AIF2_PB", "AIF2 PB"}, + /* SLIM_MUX("AIF3_PB", "AIF3 PB"),*/ + {"SLIM RX0 MUX", "AIF3_PB", "AIF3 PB"}, + {"SLIM RX1 MUX", "AIF3_PB", "AIF3 PB"}, + {"SLIM RX2 MUX", "AIF3_PB", "AIF3 PB"}, + {"SLIM RX3 MUX", "AIF3_PB", "AIF3 PB"}, + {"SLIM RX4 MUX", "AIF3_PB", "AIF3 PB"}, + {"SLIM RX5 MUX", "AIF3_PB", "AIF3 PB"}, + {"SLIM RX6 MUX", "AIF3_PB", "AIF3 PB"}, + {"SLIM RX7 MUX", "AIF3_PB", "AIF3 PB"}, + /* SLIM_MUX("AIF4_PB", "AIF4 PB"),*/ + {"SLIM RX0 MUX", "AIF4_PB", "AIF4 PB"}, + {"SLIM RX1 MUX", "AIF4_PB", "AIF4 PB"}, + {"SLIM RX2 MUX", "AIF4_PB", "AIF4 PB"}, + {"SLIM RX3 MUX", "AIF4_PB", "AIF4 PB"}, + {"SLIM RX4 MUX", "AIF4_PB", "AIF4 PB"}, + {"SLIM RX5 MUX", "AIF4_PB", "AIF4 PB"}, + {"SLIM RX6 MUX", "AIF4_PB", "AIF4 PB"}, + {"SLIM RX7 MUX", "AIF4_PB", "AIF4 PB"}, + + /* SLIM_MUX("AIF_MIX1_PB", "AIF MIX1 PB"),*/ + {"SLIM RX0 MUX", "AIF_MIX1_PB", "AIF MIX1 PB"}, + {"SLIM RX1 MUX", "AIF_MIX1_PB", "AIF MIX1 PB"}, + {"SLIM RX2 MUX", "AIF_MIX1_PB", "AIF MIX1 PB"}, + {"SLIM RX3 MUX", "AIF_MIX1_PB", "AIF MIX1 PB"}, + {"SLIM RX4 MUX", "AIF_MIX1_PB", "AIF MIX1 PB"}, + {"SLIM RX5 MUX", "AIF_MIX1_PB", "AIF MIX1 PB"}, + {"SLIM RX6 MUX", "AIF_MIX1_PB", "AIF MIX1 PB"}, + {"SLIM RX7 MUX", "AIF_MIX1_PB", "AIF MIX1 PB"}, + + {"SLIM RX0", NULL, "SLIM RX0 MUX"}, + {"SLIM RX1", NULL, "SLIM RX1 MUX"}, + {"SLIM RX2", NULL, "SLIM RX2 MUX"}, + {"SLIM RX3", NULL, "SLIM RX3 MUX"}, + {"SLIM RX4", NULL, "SLIM RX4 MUX"}, + {"SLIM RX5", NULL, "SLIM RX5 MUX"}, + {"SLIM RX6", NULL, "SLIM RX6 MUX"}, + {"SLIM RX7", NULL, "SLIM RX7 MUX"}, + + {"RX INT0_1 MIX1 INP0", "RX0", "SLIM RX0"}, + {"RX INT0_1 MIX1 INP0", "RX1", "SLIM RX1"}, + {"RX INT0_1 MIX1 INP0", "RX2", "SLIM RX2"}, + {"RX INT0_1 MIX1 INP0", "RX3", "SLIM RX3"}, + {"RX INT0_1 MIX1 INP0", "RX4", "SLIM RX4"}, + {"RX INT0_1 MIX1 INP0", "RX5", "SLIM RX5"}, + {"RX INT0_1 MIX1 INP0", "RX6", "SLIM RX6"}, + {"RX INT0_1 MIX1 INP0", "RX7", "SLIM RX7"}, + {"RX INT0_1 MIX1 INP0", "IIR0", "IIR0"}, + {"RX INT0_1 MIX1 INP0", "IIR1", "IIR1"}, + {"RX INT0_1 MIX1 INP1", "RX0", "SLIM RX0"}, + {"RX INT0_1 MIX1 INP1", "RX1", "SLIM RX1"}, + {"RX INT0_1 MIX1 INP1", "RX2", "SLIM RX2"}, + {"RX INT0_1 MIX1 INP1", "RX3", "SLIM RX3"}, + {"RX INT0_1 MIX1 INP1", "RX4", "SLIM RX4"}, + {"RX INT0_1 MIX1 INP1", "RX5", "SLIM RX5"}, + {"RX INT0_1 MIX1 INP1", "RX6", "SLIM RX6"}, + {"RX INT0_1 MIX1 INP1", "RX7", "SLIM RX7"}, + {"RX INT0_1 MIX1 INP1", "IIR0", "IIR0"}, + {"RX INT0_1 MIX1 INP1", "IIR1", "IIR1"}, + {"RX INT0_1 MIX1 INP2", "RX0", "SLIM RX0"}, + {"RX INT0_1 MIX1 INP2", "RX1", "SLIM RX1"}, + {"RX INT0_1 MIX1 INP2", "RX2", "SLIM RX2"}, + {"RX INT0_1 MIX1 INP2", "RX3", "SLIM RX3"}, + {"RX INT0_1 MIX1 INP2", "RX4", "SLIM RX4"}, + {"RX INT0_1 MIX1 INP2", "RX5", "SLIM RX5"}, + {"RX INT0_1 MIX1 INP2", "RX6", "SLIM RX6"}, + {"RX INT0_1 MIX1 INP2", "RX7", "SLIM RX7"}, + {"RX INT0_1 MIX1 INP2", "IIR0", "IIR0"}, + {"RX INT0_1 MIX1 INP2", "IIR1", "IIR1"}, + + /* MIXing path INT0 */ + {"RX INT0_2 MUX", "RX0", "SLIM RX0"}, + {"RX INT0_2 MUX", "RX1", "SLIM RX1"}, + {"RX INT0_2 MUX", "RX2", "SLIM RX2"}, + {"RX INT0_2 MUX", "RX3", "SLIM RX3"}, + {"RX INT0_2 MUX", "RX4", "SLIM RX4"}, + {"RX INT0_2 MUX", "RX5", "SLIM RX5"}, + {"RX INT0_2 MUX", "RX6", "SLIM RX6"}, + {"RX INT0_2 MUX", "RX7", "SLIM RX7"}, + {"RX INT0 SEC MIX", NULL, "RX INT0_2 MUX"}, + + /* MIXing path INT1 */ + {"RX INT1_2 MUX", "RX0", "SLIM RX0"}, + {"RX INT1_2 MUX", "RX1", "SLIM RX1"}, + {"RX INT1_2 MUX", "RX2", "SLIM RX2"}, + {"RX INT1_2 MUX", "RX3", "SLIM RX3"}, + {"RX INT1_2 MUX", "RX4", "SLIM RX4"}, + {"RX INT1_2 MUX", "RX5", "SLIM RX5"}, + {"RX INT1_2 MUX", "RX6", "SLIM RX6"}, + {"RX INT1_2 MUX", "RX7", "SLIM RX7"}, + {"RX INT1 SEC MIX", NULL, "RX INT1_2 MUX"}, + + /* MIXing path INT2 */ + {"RX INT2_2 MUX", "RX0", "SLIM RX0"}, + {"RX INT2_2 MUX", "RX1", "SLIM RX1"}, + {"RX INT2_2 MUX", "RX2", "SLIM RX2"}, + {"RX INT2_2 MUX", "RX3", "SLIM RX3"}, + {"RX INT2_2 MUX", "RX4", "SLIM RX4"}, + {"RX INT2_2 MUX", "RX5", "SLIM RX5"}, + {"RX INT2_2 MUX", "RX6", "SLIM RX6"}, + {"RX INT2_2 MUX", "RX7", "SLIM RX7"}, + {"RX INT2 SEC MIX", NULL, "RX INT2_2 MUX"}, + + /* MIXing path INT3 */ + {"RX INT3_2 MUX", "RX0", "SLIM RX0"}, + {"RX INT3_2 MUX", "RX1", "SLIM RX1"}, + {"RX INT3_2 MUX", "RX2", "SLIM RX2"}, + {"RX INT3_2 MUX", "RX3", "SLIM RX3"}, + {"RX INT3_2 MUX", "RX4", "SLIM RX4"}, + {"RX INT3_2 MUX", "RX5", "SLIM RX5"}, + {"RX INT3_2 MUX", "RX6", "SLIM RX6"}, + {"RX INT3_2 MUX", "RX7", "SLIM RX7"}, + {"RX INT3 SEC MIX", NULL, "RX INT3_2 MUX"}, + + /* MIXing path INT4 */ + {"RX INT4_2 MUX", "RX0", "SLIM RX0"}, + {"RX INT4_2 MUX", "RX1", "SLIM RX1"}, + {"RX INT4_2 MUX", "RX2", "SLIM RX2"}, + {"RX INT4_2 MUX", "RX3", "SLIM RX3"}, + {"RX INT4_2 MUX", "RX4", "SLIM RX4"}, + {"RX INT4_2 MUX", "RX5", "SLIM RX5"}, + {"RX INT4_2 MUX", "RX6", "SLIM RX6"}, + {"RX INT4_2 MUX", "RX7", "SLIM RX7"}, + {"RX INT4 SEC MIX", NULL, "RX INT4_2 MUX"}, + + /* MIXing path INT5 */ + {"RX INT5_2 MUX", "RX0", "SLIM RX0"}, + {"RX INT5_2 MUX", "RX1", "SLIM RX1"}, + {"RX INT5_2 MUX", "RX2", "SLIM RX2"}, + {"RX INT5_2 MUX", "RX3", "SLIM RX3"}, + {"RX INT5_2 MUX", "RX4", "SLIM RX4"}, + {"RX INT5_2 MUX", "RX5", "SLIM RX5"}, + {"RX INT5_2 MUX", "RX6", "SLIM RX6"}, + {"RX INT5_2 MUX", "RX7", "SLIM RX7"}, + {"RX INT5 SEC MIX", NULL, "RX INT5_2 MUX"}, + + /* MIXing path INT6 */ + {"RX INT6_2 MUX", "RX0", "SLIM RX0"}, + {"RX INT6_2 MUX", "RX1", "SLIM RX1"}, + {"RX INT6_2 MUX", "RX2", "SLIM RX2"}, + {"RX INT6_2 MUX", "RX3", "SLIM RX3"}, + {"RX INT6_2 MUX", "RX4", "SLIM RX4"}, + {"RX INT6_2 MUX", "RX5", "SLIM RX5"}, + {"RX INT6_2 MUX", "RX6", "SLIM RX6"}, + {"RX INT6_2 MUX", "RX7", "SLIM RX7"}, + {"RX INT6 SEC MIX", NULL, "RX INT6_2 MUX"}, + + /* MIXing path INT7 */ + {"RX INT7_2 MUX", "RX0", "SLIM RX0"}, + {"RX INT7_2 MUX", "RX1", "SLIM RX1"}, + {"RX INT7_2 MUX", "RX2", "SLIM RX2"}, + {"RX INT7_2 MUX", "RX3", "SLIM RX3"}, + {"RX INT7_2 MUX", "RX4", "SLIM RX4"}, + {"RX INT7_2 MUX", "RX5", "SLIM RX5"}, + {"RX INT7_2 MUX", "RX6", "SLIM RX6"}, + {"RX INT7_2 MUX", "RX7", "SLIM RX7"}, + {"RX INT7 SEC MIX", NULL, "RX INT7_2 MUX"}, + + /* MIXing path INT8 */ + {"RX INT8_2 MUX", "RX0", "SLIM RX0"}, + {"RX INT8_2 MUX", "RX1", "SLIM RX1"}, + {"RX INT8_2 MUX", "RX2", "SLIM RX2"}, + {"RX INT8_2 MUX", "RX3", "SLIM RX3"}, + {"RX INT8_2 MUX", "RX4", "SLIM RX4"}, + {"RX INT8_2 MUX", "RX5", "SLIM RX5"}, + {"RX INT8_2 MUX", "RX6", "SLIM RX6"}, + {"RX INT8_2 MUX", "RX7", "SLIM RX7"}, + {"RX INT8 SEC MIX", NULL, "RX INT8_2 MUX"}, + + {"RX INT1_1 MIX1 INP0", "RX0", "SLIM RX0"}, + {"RX INT1_1 MIX1 INP0", "RX1", "SLIM RX1"}, + {"RX INT1_1 MIX1 INP0", "RX2", "SLIM RX2"}, + {"RX INT1_1 MIX1 INP0", "RX3", "SLIM RX3"}, + {"RX INT1_1 MIX1 INP0", "RX4", "SLIM RX4"}, + {"RX INT1_1 MIX1 INP0", "RX5", "SLIM RX5"}, + {"RX INT1_1 MIX1 INP0", "RX6", "SLIM RX6"}, + {"RX INT1_1 MIX1 INP0", "RX7", "SLIM RX7"}, + {"RX INT1_1 MIX1 INP0", "IIR0", "IIR0"}, + {"RX INT1_1 MIX1 INP0", "IIR1", "IIR1"}, + {"RX INT1_1 MIX1 INP1", "RX0", "SLIM RX0"}, + {"RX INT1_1 MIX1 INP1", "RX1", "SLIM RX1"}, + {"RX INT1_1 MIX1 INP1", "RX2", "SLIM RX2"}, + {"RX INT1_1 MIX1 INP1", "RX3", "SLIM RX3"}, + {"RX INT1_1 MIX1 INP1", "RX4", "SLIM RX4"}, + {"RX INT1_1 MIX1 INP1", "RX5", "SLIM RX5"}, + {"RX INT1_1 MIX1 INP1", "RX6", "SLIM RX6"}, + {"RX INT1_1 MIX1 INP1", "RX7", "SLIM RX7"}, + {"RX INT1_1 MIX1 INP1", "IIR0", "IIR0"}, + {"RX INT1_1 MIX1 INP1", "IIR1", "IIR1"}, + {"RX INT1_1 MIX1 INP2", "RX0", "SLIM RX0"}, + {"RX INT1_1 MIX1 INP2", "RX1", "SLIM RX1"}, + {"RX INT1_1 MIX1 INP2", "RX2", "SLIM RX2"}, + {"RX INT1_1 MIX1 INP2", "RX3", "SLIM RX3"}, + {"RX INT1_1 MIX1 INP2", "RX4", "SLIM RX4"}, + {"RX INT1_1 MIX1 INP2", "RX5", "SLIM RX5"}, + {"RX INT1_1 MIX1 INP2", "RX6", "SLIM RX6"}, + {"RX INT1_1 MIX1 INP2", "RX7", "SLIM RX7"}, + {"RX INT1_1 MIX1 INP2", "IIR0", "IIR0"}, + {"RX INT1_1 MIX1 INP2", "IIR1", "IIR1"}, + {"RX INT2_1 MIX1 INP0", "RX0", "SLIM RX0"}, + {"RX INT2_1 MIX1 INP0", "RX1", "SLIM RX1"}, + {"RX INT2_1 MIX1 INP0", "RX2", "SLIM RX2"}, + {"RX INT2_1 MIX1 INP0", "RX3", "SLIM RX3"}, + {"RX INT2_1 MIX1 INP0", "RX4", "SLIM RX4"}, + {"RX INT2_1 MIX1 INP0", "RX5", "SLIM RX5"}, + {"RX INT2_1 MIX1 INP0", "RX6", "SLIM RX6"}, + {"RX INT2_1 MIX1 INP0", "RX7", "SLIM RX7"}, + {"RX INT2_1 MIX1 INP0", "IIR0", "IIR0"}, + {"RX INT2_1 MIX1 INP0", "IIR1", "IIR1"}, + {"RX INT2_1 MIX1 INP1", "RX0", "SLIM RX0"}, + {"RX INT2_1 MIX1 INP1", "RX1", "SLIM RX1"}, + {"RX INT2_1 MIX1 INP1", "RX2", "SLIM RX2"}, + {"RX INT2_1 MIX1 INP1", "RX3", "SLIM RX3"}, + {"RX INT2_1 MIX1 INP1", "RX4", "SLIM RX4"}, + {"RX INT2_1 MIX1 INP1", "RX5", "SLIM RX5"}, + {"RX INT2_1 MIX1 INP1", "RX6", "SLIM RX6"}, + {"RX INT2_1 MIX1 INP1", "RX7", "SLIM RX7"}, + {"RX INT2_1 MIX1 INP1", "IIR0", "IIR0"}, + {"RX INT2_1 MIX1 INP1", "IIR1", "IIR1"}, + {"RX INT2_1 MIX1 INP2", "RX0", "SLIM RX0"}, + {"RX INT2_1 MIX1 INP2", "RX1", "SLIM RX1"}, + {"RX INT2_1 MIX1 INP2", "RX2", "SLIM RX2"}, + {"RX INT2_1 MIX1 INP2", "RX3", "SLIM RX3"}, + {"RX INT2_1 MIX1 INP2", "RX4", "SLIM RX4"}, + {"RX INT2_1 MIX1 INP2", "RX5", "SLIM RX5"}, + {"RX INT2_1 MIX1 INP2", "RX6", "SLIM RX6"}, + {"RX INT2_1 MIX1 INP2", "RX7", "SLIM RX7"}, + {"RX INT2_1 MIX1 INP2", "IIR0", "IIR0"}, + {"RX INT2_1 MIX1 INP2", "IIR1", "IIR1"}, + + {"RX INT3_1 MIX1 INP0", "RX0", "SLIM RX0"}, + {"RX INT3_1 MIX1 INP0", "RX1", "SLIM RX1"}, + {"RX INT3_1 MIX1 INP0", "RX2", "SLIM RX2"}, + {"RX INT3_1 MIX1 INP0", "RX3", "SLIM RX3"}, + {"RX INT3_1 MIX1 INP0", "RX4", "SLIM RX4"}, + {"RX INT3_1 MIX1 INP0", "RX5", "SLIM RX5"}, + {"RX INT3_1 MIX1 INP0", "RX6", "SLIM RX6"}, + {"RX INT3_1 MIX1 INP0", "RX7", "SLIM RX7"}, + {"RX INT3_1 MIX1 INP0", "IIR0", "IIR0"}, + {"RX INT3_1 MIX1 INP0", "IIR1", "IIR1"}, + {"RX INT3_1 MIX1 INP1", "RX0", "SLIM RX0"}, + {"RX INT3_1 MIX1 INP1", "RX1", "SLIM RX1"}, + {"RX INT3_1 MIX1 INP1", "RX2", "SLIM RX2"}, + {"RX INT3_1 MIX1 INP1", "RX3", "SLIM RX3"}, + {"RX INT3_1 MIX1 INP1", "RX4", "SLIM RX4"}, + {"RX INT3_1 MIX1 INP1", "RX5", "SLIM RX5"}, + {"RX INT3_1 MIX1 INP1", "RX6", "SLIM RX6"}, + {"RX INT3_1 MIX1 INP1", "RX7", "SLIM RX7"}, + {"RX INT3_1 MIX1 INP1", "IIR0", "IIR0"}, + {"RX INT3_1 MIX1 INP1", "IIR1", "IIR1"}, + {"RX INT3_1 MIX1 INP2", "RX0", "SLIM RX0"}, + {"RX INT3_1 MIX1 INP2", "RX1", "SLIM RX1"}, + {"RX INT3_1 MIX1 INP2", "RX2", "SLIM RX2"}, + {"RX INT3_1 MIX1 INP2", "RX3", "SLIM RX3"}, + {"RX INT3_1 MIX1 INP2", "RX4", "SLIM RX4"}, + {"RX INT3_1 MIX1 INP2", "RX5", "SLIM RX5"}, + {"RX INT3_1 MIX1 INP2", "RX6", "SLIM RX6"}, + {"RX INT3_1 MIX1 INP2", "RX7", "SLIM RX7"}, + {"RX INT3_1 MIX1 INP2", "IIR0", "IIR0"}, + {"RX INT3_1 MIX1 INP2", "IIR1", "IIR1"}, + + {"RX INT4_1 MIX1 INP0", "RX0", "SLIM RX0"}, + {"RX INT4_1 MIX1 INP0", "RX1", "SLIM RX1"}, + {"RX INT4_1 MIX1 INP0", "RX2", "SLIM RX2"}, + {"RX INT4_1 MIX1 INP0", "RX3", "SLIM RX3"}, + {"RX INT4_1 MIX1 INP0", "RX4", "SLIM RX4"}, + {"RX INT4_1 MIX1 INP0", "RX5", "SLIM RX5"}, + {"RX INT4_1 MIX1 INP0", "RX6", "SLIM RX6"}, + {"RX INT4_1 MIX1 INP0", "RX7", "SLIM RX7"}, + {"RX INT4_1 MIX1 INP0", "IIR0", "IIR0"}, + {"RX INT4_1 MIX1 INP0", "IIR1", "IIR1"}, + {"RX INT4_1 MIX1 INP1", "RX0", "SLIM RX0"}, + {"RX INT4_1 MIX1 INP1", "RX1", "SLIM RX1"}, + {"RX INT4_1 MIX1 INP1", "RX2", "SLIM RX2"}, + {"RX INT4_1 MIX1 INP1", "RX3", "SLIM RX3"}, + {"RX INT4_1 MIX1 INP1", "RX4", "SLIM RX4"}, + {"RX INT4_1 MIX1 INP1", "RX5", "SLIM RX5"}, + {"RX INT4_1 MIX1 INP1", "RX6", "SLIM RX6"}, + {"RX INT4_1 MIX1 INP1", "RX7", "SLIM RX7"}, + {"RX INT4_1 MIX1 INP1", "IIR0", "IIR0"}, + {"RX INT4_1 MIX1 INP1", "IIR1", "IIR1"}, + {"RX INT4_1 MIX1 INP2", "RX0", "SLIM RX0"}, + {"RX INT4_1 MIX1 INP2", "RX1", "SLIM RX1"}, + {"RX INT4_1 MIX1 INP2", "RX2", "SLIM RX2"}, + {"RX INT4_1 MIX1 INP2", "RX3", "SLIM RX3"}, + {"RX INT4_1 MIX1 INP2", "RX4", "SLIM RX4"}, + {"RX INT4_1 MIX1 INP2", "RX5", "SLIM RX5"}, + {"RX INT4_1 MIX1 INP2", "RX6", "SLIM RX6"}, + {"RX INT4_1 MIX1 INP2", "RX7", "SLIM RX7"}, + {"RX INT4_1 MIX1 INP2", "IIR0", "IIR0"}, + {"RX INT4_1 MIX1 INP2", "IIR1", "IIR1"}, + + {"RX INT5_1 MIX1 INP0", "RX0", "SLIM RX0"}, + {"RX INT5_1 MIX1 INP0", "RX1", "SLIM RX1"}, + {"RX INT5_1 MIX1 INP0", "RX2", "SLIM RX2"}, + {"RX INT5_1 MIX1 INP0", "RX3", "SLIM RX3"}, + {"RX INT5_1 MIX1 INP0", "RX4", "SLIM RX4"}, + {"RX INT5_1 MIX1 INP0", "RX5", "SLIM RX5"}, + {"RX INT5_1 MIX1 INP0", "RX6", "SLIM RX6"}, + {"RX INT5_1 MIX1 INP0", "RX7", "SLIM RX7"}, + {"RX INT5_1 MIX1 INP0", "IIR0", "IIR0"}, + {"RX INT5_1 MIX1 INP0", "IIR1", "IIR1"}, + {"RX INT5_1 MIX1 INP1", "RX0", "SLIM RX0"}, + {"RX INT5_1 MIX1 INP1", "RX1", "SLIM RX1"}, + {"RX INT5_1 MIX1 INP1", "RX2", "SLIM RX2"}, + {"RX INT5_1 MIX1 INP1", "RX3", "SLIM RX3"}, + {"RX INT5_1 MIX1 INP1", "RX4", "SLIM RX4"}, + {"RX INT5_1 MIX1 INP1", "RX5", "SLIM RX5"}, + {"RX INT5_1 MIX1 INP1", "RX6", "SLIM RX6"}, + {"RX INT5_1 MIX1 INP1", "RX7", "SLIM RX7"}, + {"RX INT5_1 MIX1 INP1", "IIR0", "IIR0"}, + {"RX INT5_1 MIX1 INP1", "IIR1", "IIR1"}, + {"RX INT5_1 MIX1 INP2", "RX0", "SLIM RX0"}, + {"RX INT5_1 MIX1 INP2", "RX1", "SLIM RX1"}, + {"RX INT5_1 MIX1 INP2", "RX2", "SLIM RX2"}, + {"RX INT5_1 MIX1 INP2", "RX3", "SLIM RX3"}, + {"RX INT5_1 MIX1 INP2", "RX4", "SLIM RX4"}, + {"RX INT5_1 MIX1 INP2", "RX5", "SLIM RX5"}, + {"RX INT5_1 MIX1 INP2", "RX6", "SLIM RX6"}, + {"RX INT5_1 MIX1 INP2", "RX7", "SLIM RX7"}, + {"RX INT5_1 MIX1 INP2", "IIR0", "IIR0"}, + {"RX INT5_1 MIX1 INP2", "IIR1", "IIR1"}, + + {"RX INT6_1 MIX1 INP0", "RX0", "SLIM RX0"}, + {"RX INT6_1 MIX1 INP0", "RX1", "SLIM RX1"}, + {"RX INT6_1 MIX1 INP0", "RX2", "SLIM RX2"}, + {"RX INT6_1 MIX1 INP0", "RX3", "SLIM RX3"}, + {"RX INT6_1 MIX1 INP0", "RX4", "SLIM RX4"}, + {"RX INT6_1 MIX1 INP0", "RX5", "SLIM RX5"}, + {"RX INT6_1 MIX1 INP0", "RX6", "SLIM RX6"}, + {"RX INT6_1 MIX1 INP0", "RX7", "SLIM RX7"}, + {"RX INT6_1 MIX1 INP0", "IIR0", "IIR0"}, + {"RX INT6_1 MIX1 INP0", "IIR1", "IIR1"}, + {"RX INT6_1 MIX1 INP1", "RX0", "SLIM RX0"}, + {"RX INT6_1 MIX1 INP1", "RX1", "SLIM RX1"}, + {"RX INT6_1 MIX1 INP1", "RX2", "SLIM RX2"}, + {"RX INT6_1 MIX1 INP1", "RX3", "SLIM RX3"}, + {"RX INT6_1 MIX1 INP1", "RX4", "SLIM RX4"}, + {"RX INT6_1 MIX1 INP1", "RX5", "SLIM RX5"}, + {"RX INT6_1 MIX1 INP1", "RX6", "SLIM RX6"}, + {"RX INT6_1 MIX1 INP1", "RX7", "SLIM RX7"}, + {"RX INT6_1 MIX1 INP1", "IIR0", "IIR0"}, + {"RX INT6_1 MIX1 INP1", "IIR1", "IIR1"}, + {"RX INT6_1 MIX1 INP2", "RX0", "SLIM RX0"}, + {"RX INT6_1 MIX1 INP2", "RX1", "SLIM RX1"}, + {"RX INT6_1 MIX1 INP2", "RX2", "SLIM RX2"}, + {"RX INT6_1 MIX1 INP2", "RX3", "SLIM RX3"}, + {"RX INT6_1 MIX1 INP2", "RX4", "SLIM RX4"}, + {"RX INT6_1 MIX1 INP2", "RX5", "SLIM RX5"}, + {"RX INT6_1 MIX1 INP2", "RX6", "SLIM RX6"}, + {"RX INT6_1 MIX1 INP2", "RX7", "SLIM RX7"}, + {"RX INT6_1 MIX1 INP2", "IIR0", "IIR0"}, + {"RX INT6_1 MIX1 INP2", "IIR1", "IIR1"}, + + {"RX INT7_1 MIX1 INP0", "RX0", "SLIM RX0"}, + {"RX INT7_1 MIX1 INP0", "RX1", "SLIM RX1"}, + {"RX INT7_1 MIX1 INP0", "RX2", "SLIM RX2"}, + {"RX INT7_1 MIX1 INP0", "RX3", "SLIM RX3"}, + {"RX INT7_1 MIX1 INP0", "RX4", "SLIM RX4"}, + {"RX INT7_1 MIX1 INP0", "RX5", "SLIM RX5"}, + {"RX INT7_1 MIX1 INP0", "RX6", "SLIM RX6"}, + {"RX INT7_1 MIX1 INP0", "RX7", "SLIM RX7"}, + {"RX INT7_1 MIX1 INP0", "IIR0", "IIR0"}, + {"RX INT7_1 MIX1 INP0", "IIR1", "IIR1"}, + {"RX INT7_1 MIX1 INP1", "RX0", "SLIM RX0"}, + {"RX INT7_1 MIX1 INP1", "RX1", "SLIM RX1"}, + {"RX INT7_1 MIX1 INP1", "RX2", "SLIM RX2"}, + {"RX INT7_1 MIX1 INP1", "RX3", "SLIM RX3"}, + {"RX INT7_1 MIX1 INP1", "RX4", "SLIM RX4"}, + {"RX INT7_1 MIX1 INP1", "RX5", "SLIM RX5"}, + {"RX INT7_1 MIX1 INP1", "RX6", "SLIM RX6"}, + {"RX INT7_1 MIX1 INP1", "RX7", "SLIM RX7"}, + {"RX INT7_1 MIX1 INP1", "IIR0", "IIR0"}, + {"RX INT7_1 MIX1 INP1", "IIR1", "IIR1"}, + {"RX INT7_1 MIX1 INP2", "RX0", "SLIM RX0"}, + {"RX INT7_1 MIX1 INP2", "RX1", "SLIM RX1"}, + {"RX INT7_1 MIX1 INP2", "RX2", "SLIM RX2"}, + {"RX INT7_1 MIX1 INP2", "RX3", "SLIM RX3"}, + {"RX INT7_1 MIX1 INP2", "RX4", "SLIM RX4"}, + {"RX INT7_1 MIX1 INP2", "RX5", "SLIM RX5"}, + {"RX INT7_1 MIX1 INP2", "RX6", "SLIM RX6"}, + {"RX INT7_1 MIX1 INP2", "RX7", "SLIM RX7"}, + {"RX INT7_1 MIX1 INP2", "IIR0", "IIR0"}, + {"RX INT7_1 MIX1 INP2", "IIR1", "IIR1"}, + + {"RX INT8_1 MIX1 INP0", "RX0", "SLIM RX0"}, + {"RX INT8_1 MIX1 INP0", "RX1", "SLIM RX1"}, + {"RX INT8_1 MIX1 INP0", "RX2", "SLIM RX2"}, + {"RX INT8_1 MIX1 INP0", "RX3", "SLIM RX3"}, + {"RX INT8_1 MIX1 INP0", "RX4", "SLIM RX4"}, + {"RX INT8_1 MIX1 INP0", "RX5", "SLIM RX5"}, + {"RX INT8_1 MIX1 INP0", "RX6", "SLIM RX6"}, + {"RX INT8_1 MIX1 INP0", "RX7", "SLIM RX7"}, + {"RX INT8_1 MIX1 INP0", "IIR0", "IIR0"}, + {"RX INT8_1 MIX1 INP0", "IIR1", "IIR1"}, + {"RX INT8_1 MIX1 INP1", "RX0", "SLIM RX0"}, + {"RX INT8_1 MIX1 INP1", "RX1", "SLIM RX1"}, + {"RX INT8_1 MIX1 INP1", "RX2", "SLIM RX2"}, + {"RX INT8_1 MIX1 INP1", "RX3", "SLIM RX3"}, + {"RX INT8_1 MIX1 INP1", "RX4", "SLIM RX4"}, + {"RX INT8_1 MIX1 INP1", "RX5", "SLIM RX5"}, + {"RX INT8_1 MIX1 INP1", "RX6", "SLIM RX6"}, + {"RX INT8_1 MIX1 INP1", "RX7", "SLIM RX7"}, + {"RX INT8_1 MIX1 INP1", "IIR0", "IIR0"}, + {"RX INT8_1 MIX1 INP1", "IIR1", "IIR1"}, + {"RX INT8_1 MIX1 INP2", "RX0", "SLIM RX0"}, + {"RX INT8_1 MIX1 INP2", "RX1", "SLIM RX1"}, + {"RX INT8_1 MIX1 INP2", "RX2", "SLIM RX2"}, + {"RX INT8_1 MIX1 INP2", "RX3", "SLIM RX3"}, + {"RX INT8_1 MIX1 INP2", "RX4", "SLIM RX4"}, + {"RX INT8_1 MIX1 INP2", "RX5", "SLIM RX5"}, + {"RX INT8_1 MIX1 INP2", "RX6", "SLIM RX6"}, + {"RX INT8_1 MIX1 INP2", "RX7", "SLIM RX7"}, + {"RX INT8_1 MIX1 INP2", "IIR0", "IIR0"}, + {"RX INT8_1 MIX1 INP2", "IIR1", "IIR1"}, + + /* SRC0, SRC1 inputs to Sidetone RX Mixer + * on RX0, RX1, RX2, RX3, RX4 and RX7 chains + */ + {"IIR0", NULL, "IIR0 INP0 MUX"}, + {"IIR0 INP0 MUX", "DEC0", "ADC MUX0"}, + {"IIR0 INP0 MUX", "DEC1", "ADC MUX1"}, + {"IIR0 INP0 MUX", "DEC2", "ADC MUX2"}, + {"IIR0 INP0 MUX", "DEC3", "ADC MUX3"}, + {"IIR0 INP0 MUX", "DEC4", "ADC MUX4"}, + {"IIR0 INP0 MUX", "DEC5", "ADC MUX5"}, + {"IIR0 INP0 MUX", "DEC6", "ADC MUX6"}, + {"IIR0 INP0 MUX", "DEC7", "ADC MUX7"}, + {"IIR0 INP0 MUX", "DEC8", "ADC MUX8"}, + {"IIR0 INP0 MUX", "RX0", "SLIM RX0"}, + {"IIR0 INP0 MUX", "RX1", "SLIM RX1"}, + {"IIR0 INP0 MUX", "RX2", "SLIM RX2"}, + {"IIR0 INP0 MUX", "RX3", "SLIM RX3"}, + {"IIR0 INP0 MUX", "RX4", "SLIM RX4"}, + {"IIR0 INP0 MUX", "RX5", "SLIM RX5"}, + {"IIR0 INP0 MUX", "RX6", "SLIM RX6"}, + {"IIR0 INP0 MUX", "RX7", "SLIM RX7"}, + {"IIR0", NULL, "IIR0 INP1 MUX"}, + {"IIR0 INP1 MUX", "DEC0", "ADC MUX0"}, + {"IIR0 INP1 MUX", "DEC1", "ADC MUX1"}, + {"IIR0 INP1 MUX", "DEC2", "ADC MUX2"}, + {"IIR0 INP1 MUX", "DEC3", "ADC MUX3"}, + {"IIR0 INP1 MUX", "DEC4", "ADC MUX4"}, + {"IIR0 INP1 MUX", "DEC5", "ADC MUX5"}, + {"IIR0 INP1 MUX", "DEC6", "ADC MUX6"}, + {"IIR0 INP1 MUX", "DEC7", "ADC MUX7"}, + {"IIR0 INP1 MUX", "DEC8", "ADC MUX8"}, + {"IIR0 INP1 MUX", "RX0", "SLIM RX0"}, + {"IIR0 INP1 MUX", "RX1", "SLIM RX1"}, + {"IIR0 INP1 MUX", "RX2", "SLIM RX2"}, + {"IIR0 INP1 MUX", "RX3", "SLIM RX3"}, + {"IIR0 INP1 MUX", "RX4", "SLIM RX4"}, + {"IIR0 INP1 MUX", "RX5", "SLIM RX5"}, + {"IIR0 INP1 MUX", "RX6", "SLIM RX6"}, + {"IIR0 INP1 MUX", "RX7", "SLIM RX7"}, + {"IIR0", NULL, "IIR0 INP2 MUX"}, + {"IIR0 INP2 MUX", "DEC0", "ADC MUX0"}, + {"IIR0 INP2 MUX", "DEC1", "ADC MUX1"}, + {"IIR0 INP2 MUX", "DEC2", "ADC MUX2"}, + {"IIR0 INP2 MUX", "DEC3", "ADC MUX3"}, + {"IIR0 INP2 MUX", "DEC4", "ADC MUX4"}, + {"IIR0 INP2 MUX", "DEC5", "ADC MUX5"}, + {"IIR0 INP2 MUX", "DEC6", "ADC MUX6"}, + {"IIR0 INP2 MUX", "DEC7", "ADC MUX7"}, + {"IIR0 INP2 MUX", "DEC8", "ADC MUX8"}, + {"IIR0 INP2 MUX", "RX0", "SLIM RX0"}, + {"IIR0 INP2 MUX", "RX1", "SLIM RX1"}, + {"IIR0 INP2 MUX", "RX2", "SLIM RX2"}, + {"IIR0 INP2 MUX", "RX3", "SLIM RX3"}, + {"IIR0 INP2 MUX", "RX4", "SLIM RX4"}, + {"IIR0 INP2 MUX", "RX5", "SLIM RX5"}, + {"IIR0 INP2 MUX", "RX6", "SLIM RX6"}, + {"IIR0 INP2 MUX", "RX7", "SLIM RX7"}, + {"IIR0", NULL, "IIR0 INP3 MUX"}, + {"IIR0 INP3 MUX", "DEC0", "ADC MUX0"}, + {"IIR0 INP3 MUX", "DEC1", "ADC MUX1"}, + {"IIR0 INP3 MUX", "DEC2", "ADC MUX2"}, + {"IIR0 INP3 MUX", "DEC3", "ADC MUX3"}, + {"IIR0 INP3 MUX", "DEC4", "ADC MUX4"}, + {"IIR0 INP3 MUX", "DEC5", "ADC MUX5"}, + {"IIR0 INP3 MUX", "DEC6", "ADC MUX6"}, + {"IIR0 INP3 MUX", "DEC7", "ADC MUX7"}, + {"IIR0 INP3 MUX", "DEC8", "ADC MUX8"}, + {"IIR0 INP3 MUX", "RX0", "SLIM RX0"}, + {"IIR0 INP3 MUX", "RX1", "SLIM RX1"}, + {"IIR0 INP3 MUX", "RX2", "SLIM RX2"}, + {"IIR0 INP3 MUX", "RX3", "SLIM RX3"}, + {"IIR0 INP3 MUX", "RX4", "SLIM RX4"}, + {"IIR0 INP3 MUX", "RX5", "SLIM RX5"}, + {"IIR0 INP3 MUX", "RX6", "SLIM RX6"}, + {"IIR0 INP3 MUX", "RX7", "SLIM RX7"}, + + {"IIR1", NULL, "IIR1 INP0 MUX"}, + {"IIR1 INP0 MUX", "DEC0", "ADC MUX0"}, + {"IIR1 INP0 MUX", "DEC1", "ADC MUX1"}, + {"IIR1 INP0 MUX", "DEC2", "ADC MUX2"}, + {"IIR1 INP0 MUX", "DEC3", "ADC MUX3"}, + {"IIR1 INP0 MUX", "DEC4", "ADC MUX4"}, + {"IIR1 INP0 MUX", "DEC5", "ADC MUX5"}, + {"IIR1 INP0 MUX", "DEC6", "ADC MUX6"}, + {"IIR1 INP0 MUX", "DEC7", "ADC MUX7"}, + {"IIR1 INP0 MUX", "DEC8", "ADC MUX8"}, + {"IIR1 INP0 MUX", "RX0", "SLIM RX0"}, + {"IIR1 INP0 MUX", "RX1", "SLIM RX1"}, + {"IIR1 INP0 MUX", "RX2", "SLIM RX2"}, + {"IIR1 INP0 MUX", "RX3", "SLIM RX3"}, + {"IIR1 INP0 MUX", "RX4", "SLIM RX4"}, + {"IIR1 INP0 MUX", "RX5", "SLIM RX5"}, + {"IIR1 INP0 MUX", "RX6", "SLIM RX6"}, + {"IIR1 INP0 MUX", "RX7", "SLIM RX7"}, + {"IIR1", NULL, "IIR1 INP1 MUX"}, + {"IIR1 INP1 MUX", "DEC0", "ADC MUX0"}, + {"IIR1 INP1 MUX", "DEC1", "ADC MUX1"}, + {"IIR1 INP1 MUX", "DEC2", "ADC MUX2"}, + {"IIR1 INP1 MUX", "DEC3", "ADC MUX3"}, + {"IIR1 INP1 MUX", "DEC4", "ADC MUX4"}, + {"IIR1 INP1 MUX", "DEC5", "ADC MUX5"}, + {"IIR1 INP1 MUX", "DEC6", "ADC MUX6"}, + {"IIR1 INP1 MUX", "DEC7", "ADC MUX7"}, + {"IIR1 INP1 MUX", "DEC8", "ADC MUX8"}, + {"IIR1 INP1 MUX", "RX0", "SLIM RX0"}, + {"IIR1 INP1 MUX", "RX1", "SLIM RX1"}, + {"IIR1 INP1 MUX", "RX2", "SLIM RX2"}, + {"IIR1 INP1 MUX", "RX3", "SLIM RX3"}, + {"IIR1 INP1 MUX", "RX4", "SLIM RX4"}, + {"IIR1 INP1 MUX", "RX5", "SLIM RX5"}, + {"IIR1 INP1 MUX", "RX6", "SLIM RX6"}, + {"IIR1 INP1 MUX", "RX7", "SLIM RX7"}, + {"IIR1", NULL, "IIR1 INP2 MUX"}, + {"IIR1 INP2 MUX", "DEC0", "ADC MUX0"}, + {"IIR1 INP2 MUX", "DEC1", "ADC MUX1"}, + {"IIR1 INP2 MUX", "DEC2", "ADC MUX2"}, + {"IIR1 INP2 MUX", "DEC3", "ADC MUX3"}, + {"IIR1 INP2 MUX", "DEC4", "ADC MUX4"}, + {"IIR1 INP2 MUX", "DEC5", "ADC MUX5"}, + {"IIR1 INP2 MUX", "DEC6", "ADC MUX6"}, + {"IIR1 INP2 MUX", "DEC7", "ADC MUX7"}, + {"IIR1 INP2 MUX", "DEC8", "ADC MUX8"}, + {"IIR1 INP2 MUX", "RX0", "SLIM RX0"}, + {"IIR1 INP2 MUX", "RX1", "SLIM RX1"}, + {"IIR1 INP2 MUX", "RX2", "SLIM RX2"}, + {"IIR1 INP2 MUX", "RX3", "SLIM RX3"}, + {"IIR1 INP2 MUX", "RX4", "SLIM RX4"}, + {"IIR1 INP2 MUX", "RX5", "SLIM RX5"}, + {"IIR1 INP2 MUX", "RX6", "SLIM RX6"}, + {"IIR1 INP2 MUX", "RX7", "SLIM RX7"}, + {"IIR1", NULL, "IIR1 INP3 MUX"}, + {"IIR1 INP3 MUX", "DEC0", "ADC MUX0"}, + {"IIR1 INP3 MUX", "DEC1", "ADC MUX1"}, + {"IIR1 INP3 MUX", "DEC2", "ADC MUX2"}, + {"IIR1 INP3 MUX", "DEC3", "ADC MUX3"}, + {"IIR1 INP3 MUX", "DEC4", "ADC MUX4"}, + {"IIR1 INP3 MUX", "DEC5", "ADC MUX5"}, + {"IIR1 INP3 MUX", "DEC6", "ADC MUX6"}, + {"IIR1 INP3 MUX", "DEC7", "ADC MUX7"}, + {"IIR1 INP3 MUX", "DEC8", "ADC MUX8"}, + {"IIR1 INP3 MUX", "RX0", "SLIM RX0"}, + {"IIR1 INP3 MUX", "RX1", "SLIM RX1"}, + {"IIR1 INP3 MUX", "RX2", "SLIM RX2"}, + {"IIR1 INP3 MUX", "RX3", "SLIM RX3"}, + {"IIR1 INP3 MUX", "RX4", "SLIM RX4"}, + {"IIR1 INP3 MUX", "RX5", "SLIM RX5"}, + {"IIR1 INP3 MUX", "RX6", "SLIM RX6"}, + {"IIR1 INP3 MUX", "RX7", "SLIM RX7"}, + + {"SRC0", NULL, "IIR0"}, + {"SRC1", NULL, "IIR1"}, + {"RX INT0 MIX2 INP", "SRC0", "SRC0"}, + {"RX INT0 MIX2 INP", "SRC1", "SRC1"}, + {"RX INT1 MIX2 INP", "SRC0", "SRC0"}, + {"RX INT1 MIX2 INP", "SRC1", "SRC1"}, + {"RX INT2 MIX2 INP", "SRC0", "SRC0"}, + {"RX INT2 MIX2 INP", "SRC1", "SRC1"}, + {"RX INT3 MIX2 INP", "SRC0", "SRC0"}, + {"RX INT3 MIX2 INP", "SRC1", "SRC1"}, + {"RX INT4 MIX2 INP", "SRC0", "SRC0"}, + {"RX INT4 MIX2 INP", "SRC1", "SRC1"}, + {"RX INT7 MIX2 INP", "SRC0", "SRC0"}, + {"RX INT7 MIX2 INP", "SRC1", "SRC1"}, +}; + +static int tasha_amic_pwr_lvl_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + u16 amic_reg; + + if (!strcmp(kcontrol->id.name, "AMIC_1_2 PWR MODE")) + amic_reg = WCD9335_ANA_AMIC1; + if (!strcmp(kcontrol->id.name, "AMIC_3_4 PWR MODE")) + amic_reg = WCD9335_ANA_AMIC3; + if (!strcmp(kcontrol->id.name, "AMIC_5_6 PWR MODE")) + amic_reg = WCD9335_ANA_AMIC5; + + ucontrol->value.integer.value[0] = + (snd_soc_read(codec, amic_reg) & WCD9335_AMIC_PWR_LVL_MASK) >> + WCD9335_AMIC_PWR_LVL_SHIFT; + + return 0; +} + +static int tasha_amic_pwr_lvl_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + u32 mode_val; + u16 amic_reg; + + mode_val = ucontrol->value.enumerated.item[0]; + + dev_dbg(codec->dev, "%s: mode: %d\n", + __func__, mode_val); + + if (!strcmp(kcontrol->id.name, "AMIC_1_2 PWR MODE")) + amic_reg = WCD9335_ANA_AMIC1; + if (!strcmp(kcontrol->id.name, "AMIC_3_4 PWR MODE")) + amic_reg = WCD9335_ANA_AMIC3; + if (!strcmp(kcontrol->id.name, "AMIC_5_6 PWR MODE")) + amic_reg = WCD9335_ANA_AMIC5; + + snd_soc_update_bits(codec, amic_reg, WCD9335_AMIC_PWR_LVL_MASK, + mode_val << WCD9335_AMIC_PWR_LVL_SHIFT); + + return 0; +} + +static int tasha_rx_hph_mode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = tasha->hph_mode; + return 0; +} + +static int tasha_rx_hph_mode_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + u32 mode_val; + + mode_val = ucontrol->value.enumerated.item[0]; + + dev_dbg(codec->dev, "%s: mode: %d\n", + __func__, mode_val); + + if (mode_val == 0) { + dev_warn(codec->dev, "%s:Invalid HPH Mode, default to Cls-H HiFi\n", + __func__); + mode_val = CLS_H_HIFI; + } + tasha->hph_mode = mode_val; + return 0; +} + +static const char *const tasha_conn_mad_text[] = { + "NOTUSED1", "ADC1", "ADC2", "ADC3", "ADC4", "ADC5", "ADC6", + "NOTUSED2", "DMIC0", "DMIC1", "DMIC2", "DMIC3", "DMIC4", + "DMIC5", "NOTUSED3", "NOTUSED4" +}; + +static const struct soc_enum tasha_conn_mad_enum = + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(tasha_conn_mad_text), + tasha_conn_mad_text); + +static int tasha_enable_ldo_h_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + u8 val = 0; + + if (codec) + val = snd_soc_read(codec, WCD9335_LDOH_MODE) & 0x80; + + ucontrol->value.integer.value[0] = !!val; + + return 0; +} + +static int tasha_enable_ldo_h_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + int value = ucontrol->value.integer.value[0]; + bool enable; + + enable = !!value; + if (codec) + tasha_codec_enable_standalone_ldo_h(codec, enable); + + return 0; +} + +static int tasha_mad_input_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u8 tasha_mad_input; + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + + tasha_mad_input = snd_soc_read(codec, + WCD9335_SOC_MAD_INP_SEL) & 0x0F; + ucontrol->value.integer.value[0] = tasha_mad_input; + + dev_dbg(codec->dev, + "%s: tasha_mad_input = %s\n", __func__, + tasha_conn_mad_text[tasha_mad_input]); + return 0; +} + +static int tasha_mad_input_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u8 tasha_mad_input; + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct snd_soc_card *card = codec->component.card; + char mad_amic_input_widget[6]; + const char *mad_input_widget; + const char *source_widget = NULL; + u32 adc, i, mic_bias_found = 0; + int ret = 0; + char *mad_input; + + tasha_mad_input = ucontrol->value.integer.value[0]; + + if (tasha_mad_input >= ARRAY_SIZE(tasha_conn_mad_text)) { + dev_err(codec->dev, + "%s: tasha_mad_input = %d out of bounds\n", + __func__, tasha_mad_input); + return -EINVAL; + } + + if (!strcmp(tasha_conn_mad_text[tasha_mad_input], "NOTUSED1") || + !strcmp(tasha_conn_mad_text[tasha_mad_input], "NOTUSED2") || + !strcmp(tasha_conn_mad_text[tasha_mad_input], "NOTUSED3") || + !strcmp(tasha_conn_mad_text[tasha_mad_input], "NOTUSED4")) { + dev_err(codec->dev, + "%s: Unsupported tasha_mad_input = %s\n", + __func__, tasha_conn_mad_text[tasha_mad_input]); + return -EINVAL; + } + + if (strnstr(tasha_conn_mad_text[tasha_mad_input], + "ADC", sizeof("ADC"))) { + mad_input = strpbrk(tasha_conn_mad_text[tasha_mad_input], + "123456"); + if (!mad_input) { + dev_err(codec->dev, "%s: Invalid MAD input %s\n", + __func__, + tasha_conn_mad_text[tasha_mad_input]); + return -EINVAL; + } + ret = kstrtouint(mad_input, 10, &adc); + if ((ret < 0) || (adc > 6)) { + dev_err(codec->dev, + "%s: Invalid ADC = %s\n", __func__, + tasha_conn_mad_text[tasha_mad_input]); + ret = -EINVAL; + } + + snprintf(mad_amic_input_widget, 6, "%s%u", "AMIC", adc); + + mad_input_widget = mad_amic_input_widget; + } else { + /* DMIC type input widget*/ + mad_input_widget = tasha_conn_mad_text[tasha_mad_input]; + } + + dev_dbg(codec->dev, + "%s: tasha input widget = %s\n", __func__, + mad_input_widget); + + for (i = 0; i < card->num_of_dapm_routes; i++) { + if (!strcmp(card->of_dapm_routes[i].sink, mad_input_widget)) { + source_widget = card->of_dapm_routes[i].source; + if (!source_widget) { + dev_err(codec->dev, + "%s: invalid source widget\n", + __func__); + return -EINVAL; + } + + if (strnstr(source_widget, + "MIC BIAS1", sizeof("MIC BIAS1"))) { + mic_bias_found = 1; + break; + } else if (strnstr(source_widget, + "MIC BIAS2", sizeof("MIC BIAS2"))) { + mic_bias_found = 2; + break; + } else if (strnstr(source_widget, + "MIC BIAS3", sizeof("MIC BIAS3"))) { + mic_bias_found = 3; + break; + } else if (strnstr(source_widget, + "MIC BIAS4", sizeof("MIC BIAS4"))) { + mic_bias_found = 4; + break; + } + } + } + + if (!mic_bias_found) { + dev_err(codec->dev, + "%s: mic bias source not found for input = %s\n", + __func__, mad_input_widget); + return -EINVAL; + } + + dev_dbg(codec->dev, + "%s: mic_bias found = %d\n", __func__, + mic_bias_found); + + snd_soc_update_bits(codec, WCD9335_SOC_MAD_INP_SEL, + 0x0F, tasha_mad_input); + snd_soc_update_bits(codec, WCD9335_ANA_MAD_SETUP, + 0x07, mic_bias_found); + + return 0; +} + +static int tasha_pinctl_mode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + u16 ctl_reg; + u8 reg_val, pinctl_position; + + pinctl_position = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + switch (pinctl_position >> 3) { + case 0: + ctl_reg = WCD9335_TEST_DEBUG_PIN_CTL_OE_0; + break; + case 1: + ctl_reg = WCD9335_TEST_DEBUG_PIN_CTL_OE_1; + break; + case 2: + ctl_reg = WCD9335_TEST_DEBUG_PIN_CTL_OE_2; + break; + case 3: + ctl_reg = WCD9335_TEST_DEBUG_PIN_CTL_OE_3; + break; + default: + dev_err(codec->dev, "%s: Invalid pinctl position = %d\n", + __func__, pinctl_position); + return -EINVAL; + } + + reg_val = snd_soc_read(codec, ctl_reg); + reg_val = (reg_val >> (pinctl_position & 0x07)) & 0x1; + ucontrol->value.integer.value[0] = reg_val; + + return 0; +} + +static int tasha_pinctl_mode_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + u16 ctl_reg, cfg_reg; + u8 ctl_val, cfg_val, pinctl_position, pinctl_mode, mask; + + /* 1- high or low; 0- high Z */ + pinctl_mode = ucontrol->value.integer.value[0]; + pinctl_position = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + + switch (pinctl_position >> 3) { + case 0: + ctl_reg = WCD9335_TEST_DEBUG_PIN_CTL_OE_0; + break; + case 1: + ctl_reg = WCD9335_TEST_DEBUG_PIN_CTL_OE_1; + break; + case 2: + ctl_reg = WCD9335_TEST_DEBUG_PIN_CTL_OE_2; + break; + case 3: + ctl_reg = WCD9335_TEST_DEBUG_PIN_CTL_OE_3; + break; + default: + dev_err(codec->dev, "%s: Invalid pinctl position = %d\n", + __func__, pinctl_position); + return -EINVAL; + } + + ctl_val = pinctl_mode << (pinctl_position & 0x07); + mask = 1 << (pinctl_position & 0x07); + snd_soc_update_bits(codec, ctl_reg, mask, ctl_val); + + cfg_reg = WCD9335_TLMM_BIST_MODE_PINCFG + pinctl_position; + if (!pinctl_mode) { + if (tasha->intf_type == WCD9XXX_INTERFACE_TYPE_SLIMBUS) + cfg_val = 0x4; + else + cfg_val = 0xC; + } else { + cfg_val = 0; + } + snd_soc_update_bits(codec, cfg_reg, 0x07, cfg_val); + + dev_dbg(codec->dev, "%s: reg=0x%x mask=0x%x val=%d reg=0x%x val=%d\n", + __func__, ctl_reg, mask, ctl_val, cfg_reg, cfg_val); + + return 0; +} + +static void wcd_vbat_adc_out_config_2_0(struct wcd_vbat *vbat, + struct snd_soc_codec *codec) +{ + u8 val1, val2; + + /* + * Measure dcp1 by using "ALT" branch of band gap + * voltage(Vbg) and use it in FAST mode + */ + snd_soc_update_bits(codec, WCD9335_BIAS_CTL, 0x82, 0x82); + snd_soc_update_bits(codec, WCD9335_CDC_VBAT_VBAT_PATH_CTL, 0x10, 0x10); + snd_soc_update_bits(codec, WCD9335_CDC_VBAT_VBAT_DEBUG1, 0x01, 0x01); + snd_soc_update_bits(codec, WCD9335_ANA_VBADC, 0x80, 0x80); + snd_soc_update_bits(codec, WCD9335_VBADC_SUBBLOCK_EN, 0x20, 0x00); + + snd_soc_update_bits(codec, WCD9335_VBADC_FE_CTRL, 0x20, 0x20); + /* Wait 100 usec after calibration select as Vbg */ + usleep_range(100, 110); + + snd_soc_update_bits(codec, WCD9335_VBADC_ADC_IO, 0x40, 0x40); + val1 = snd_soc_read(codec, WCD9335_VBADC_ADC_DOUTMSB); + val2 = snd_soc_read(codec, WCD9335_VBADC_ADC_DOUTLSB); + snd_soc_update_bits(codec, WCD9335_VBADC_ADC_IO, 0x40, 0x00); + + vbat->dcp1 = (((val1 & 0xFF) << 3) | (val2 & 0x07)); + + snd_soc_update_bits(codec, WCD9335_BIAS_CTL, 0x40, 0x40); + /* Wait 100 usec after selecting Vbg as 1.05V */ + usleep_range(100, 110); + + snd_soc_update_bits(codec, WCD9335_VBADC_ADC_IO, 0x40, 0x40); + val1 = snd_soc_read(codec, WCD9335_VBADC_ADC_DOUTMSB); + val2 = snd_soc_read(codec, WCD9335_VBADC_ADC_DOUTLSB); + snd_soc_update_bits(codec, WCD9335_VBADC_ADC_IO, 0x40, 0x00); + + vbat->dcp2 = (((val1 & 0xFF) << 3) | (val2 & 0x07)); + + dev_dbg(codec->dev, "%s: dcp1:0x%x, dcp2:0x%x\n", + __func__, vbat->dcp1, vbat->dcp2); + + snd_soc_write(codec, WCD9335_BIAS_CTL, 0x28); + /* Wait 100 usec after selecting Vbg as 0.85V */ + usleep_range(100, 110); + + snd_soc_update_bits(codec, WCD9335_VBADC_FE_CTRL, 0x20, 0x00); + snd_soc_update_bits(codec, WCD9335_VBADC_SUBBLOCK_EN, 0x20, 0x20); + snd_soc_update_bits(codec, WCD9335_ANA_VBADC, 0x80, 0x00); + + snd_soc_update_bits(codec, WCD9335_CDC_VBAT_VBAT_PATH_CTL, 0x10, 0x00); + snd_soc_update_bits(codec, WCD9335_CDC_VBAT_VBAT_DEBUG1, 0x01, 0x00); +} + +static void wcd_vbat_adc_out_config_1_x(struct wcd_vbat *vbat, + struct snd_soc_codec *codec) +{ + u8 val1, val2; + + /* + * Measure dcp1 by applying band gap voltage(Vbg) + * of 0.85V + */ + snd_soc_write(codec, WCD9335_ANA_BIAS, 0x20); + snd_soc_write(codec, WCD9335_BIAS_CTL, 0x28); + snd_soc_write(codec, WCD9335_BIAS_VBG_FINE_ADJ, 0x05); + snd_soc_write(codec, WCD9335_ANA_BIAS, 0xA0); + /* Wait 2 sec after enabling band gap bias */ + usleep_range(2000000, 2000100); + + snd_soc_write(codec, WCD9335_ANA_CLK_TOP, 0x82); + snd_soc_write(codec, WCD9335_ANA_CLK_TOP, 0x87); + snd_soc_update_bits(codec, WCD9335_CDC_VBAT_VBAT_PATH_CTL, 0x10, 0x10); + snd_soc_write(codec, WCD9335_CDC_VBAT_VBAT_CFG, 0x0D); + snd_soc_write(codec, WCD9335_CDC_VBAT_VBAT_DEBUG1, 0x01); + + snd_soc_write(codec, WCD9335_ANA_VBADC, 0x80); + snd_soc_write(codec, WCD9335_VBADC_SUBBLOCK_EN, 0xDE); + snd_soc_write(codec, WCD9335_VBADC_FE_CTRL, 0x3C); + /* Wait 1 msec after calibration select as Vbg */ + usleep_range(1000, 1100); + + snd_soc_write(codec, WCD9335_VBADC_ADC_IO, 0xC0); + val1 = snd_soc_read(codec, WCD9335_VBADC_ADC_DOUTMSB); + val2 = snd_soc_read(codec, WCD9335_VBADC_ADC_DOUTLSB); + snd_soc_write(codec, WCD9335_VBADC_ADC_IO, 0x80); + + vbat->dcp1 = (((val1 & 0xFF) << 3) | (val2 & 0x07)); + + /* + * Measure dcp2 by applying band gap voltage(Vbg) + * of 1.05V + */ + snd_soc_write(codec, WCD9335_ANA_BIAS, 0x80); + snd_soc_write(codec, WCD9335_ANA_BIAS, 0xC0); + snd_soc_write(codec, WCD9335_BIAS_CTL, 0x68); + /* Wait 2 msec after selecting Vbg as 1.05V */ + usleep_range(2000, 2100); + + snd_soc_write(codec, WCD9335_ANA_BIAS, 0x80); + /* Wait 1 sec after enabling band gap bias */ + usleep_range(1000000, 1000100); + + snd_soc_write(codec, WCD9335_VBADC_ADC_IO, 0xC0); + val1 = snd_soc_read(codec, WCD9335_VBADC_ADC_DOUTMSB); + val2 = snd_soc_read(codec, WCD9335_VBADC_ADC_DOUTLSB); + snd_soc_write(codec, WCD9335_VBADC_ADC_IO, 0x80); + + vbat->dcp2 = (((val1 & 0xFF) << 3) | (val2 & 0x07)); + + dev_dbg(codec->dev, "%s: dcp1:0x%x, dcp2:0x%x\n", + __func__, vbat->dcp1, vbat->dcp2); + + /* Reset the Vbat ADC configuration */ + snd_soc_write(codec, WCD9335_ANA_BIAS, 0x80); + snd_soc_write(codec, WCD9335_ANA_BIAS, 0xC0); + + snd_soc_write(codec, WCD9335_BIAS_CTL, 0x28); + /* Wait 2 msec after selecting Vbg as 0.85V */ + usleep_range(2000, 2100); + + snd_soc_write(codec, WCD9335_ANA_BIAS, 0xA0); + /* Wait 1 sec after enabling band gap bias */ + usleep_range(1000000, 1000100); + + snd_soc_write(codec, WCD9335_VBADC_FE_CTRL, 0x1C); + snd_soc_write(codec, WCD9335_VBADC_SUBBLOCK_EN, 0xFE); + snd_soc_write(codec, WCD9335_VBADC_ADC_IO, 0x80); + snd_soc_write(codec, WCD9335_ANA_VBADC, 0x00); + + snd_soc_write(codec, WCD9335_CDC_VBAT_VBAT_DEBUG1, 0x00); + snd_soc_write(codec, WCD9335_CDC_VBAT_VBAT_PATH_CTL, 0x00); + snd_soc_write(codec, WCD9335_CDC_VBAT_VBAT_CFG, 0x0A); +} + +static void wcd_vbat_adc_out_config(struct wcd_vbat *vbat, + struct snd_soc_codec *codec) +{ + struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent); + + if (!vbat->adc_config) { + tasha_cdc_mclk_enable(codec, true, false); + + if (TASHA_IS_2_0(wcd9xxx)) + wcd_vbat_adc_out_config_2_0(vbat, codec); + else + wcd_vbat_adc_out_config_1_x(vbat, codec); + + tasha_cdc_mclk_enable(codec, false, false); + vbat->adc_config = true; + } +} + +static int tasha_update_vbat_reg_config(struct snd_soc_codec *codec) +{ + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + struct firmware_cal *hwdep_cal = NULL; + struct vbat_monitor_reg *vbat_reg_ptr = NULL; + const void *data; + size_t cal_size, vbat_size_remaining; + int ret = 0, i; + u32 vbat_writes_size = 0; + u16 reg; + u8 mask, val, old_val; + + hwdep_cal = wcdcal_get_fw_cal(tasha->fw_data, WCD9XXX_VBAT_CAL); + if (hwdep_cal) { + data = hwdep_cal->data; + cal_size = hwdep_cal->size; + dev_dbg(codec->dev, "%s: using hwdep calibration\n", + __func__); + } else { + dev_err(codec->dev, "%s: Vbat cal not received\n", + __func__); + ret = -EINVAL; + goto done; + } + + if (cal_size < sizeof(*vbat_reg_ptr)) { + dev_err(codec->dev, + "%s: Incorrect size %zd for Vbat Cal, expected %zd\n", + __func__, cal_size, sizeof(*vbat_reg_ptr)); + ret = -EINVAL; + goto done; + } + + vbat_reg_ptr = (struct vbat_monitor_reg *) (data); + + if (!vbat_reg_ptr) { + dev_err(codec->dev, + "%s: Invalid calibration data for Vbat\n", + __func__); + ret = -EINVAL; + goto done; + } + + vbat_writes_size = vbat_reg_ptr->size; + vbat_size_remaining = cal_size - sizeof(u32); + dev_dbg(codec->dev, "%s: vbat_writes_sz: %d, vbat_sz_remaining: %zd\n", + __func__, vbat_writes_size, vbat_size_remaining); + + if ((vbat_writes_size * TASHA_PACKED_REG_SIZE) + > vbat_size_remaining) { + pr_err("%s: Incorrect Vbat calibration data\n", __func__); + ret = -EINVAL; + goto done; + } + + for (i = 0 ; i < vbat_writes_size; i++) { + TASHA_CODEC_UNPACK_ENTRY(vbat_reg_ptr->writes[i], + reg, mask, val); + old_val = snd_soc_read(codec, reg); + snd_soc_write(codec, reg, (old_val & ~mask) | (val & mask)); + } + +done: + return ret; +} + +static int tasha_vbat_adc_data_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + + wcd_vbat_adc_out_config(&tasha->vbat, codec); + + ucontrol->value.integer.value[0] = tasha->vbat.dcp1; + ucontrol->value.integer.value[1] = tasha->vbat.dcp2; + + dev_dbg(codec->dev, + "%s: Vbat ADC output values, Dcp1 : %lu, Dcp2: %lu\n", + __func__, ucontrol->value.integer.value[0], + ucontrol->value.integer.value[1]); + + return 0; +} + +static const char * const tasha_vbat_gsm_mode_text[] = { + "OFF", "ON"}; + +static const struct soc_enum tasha_vbat_gsm_mode_enum = + SOC_ENUM_SINGLE_EXT(2, tasha_vbat_gsm_mode_text); + +static int tasha_vbat_gsm_mode_func_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + + ucontrol->value.integer.value[0] = + ((snd_soc_read(codec, WCD9335_CDC_VBAT_VBAT_CFG) & 0x04) ? + 1 : 0); + + dev_dbg(codec->dev, "%s: value: %lu\n", __func__, + ucontrol->value.integer.value[0]); + + return 0; +} + +static int tasha_vbat_gsm_mode_func_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + + dev_dbg(codec->dev, "%s: value: %lu\n", __func__, + ucontrol->value.integer.value[0]); + + /* Set Vbat register configuration for GSM mode bit based on value */ + if (ucontrol->value.integer.value[0]) + snd_soc_update_bits(codec, WCD9335_CDC_VBAT_VBAT_CFG, + 0x04, 0x04); + else + snd_soc_update_bits(codec, WCD9335_CDC_VBAT_VBAT_CFG, + 0x04, 0x00); + + return 0; +} + +static int tasha_codec_vbat_enable_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + int ret = 0; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + u16 vbat_path_ctl, vbat_cfg, vbat_path_cfg; + + vbat_path_ctl = WCD9335_CDC_VBAT_VBAT_PATH_CTL; + vbat_cfg = WCD9335_CDC_VBAT_VBAT_CFG; + vbat_path_cfg = WCD9335_CDC_RX8_RX_PATH_CFG1; + + if (!strcmp(w->name, "RX INT8 VBAT")) + vbat_path_cfg = WCD9335_CDC_RX8_RX_PATH_CFG1; + else if (!strcmp(w->name, "RX INT7 VBAT")) + vbat_path_cfg = WCD9335_CDC_RX7_RX_PATH_CFG1; + else if (!strcmp(w->name, "RX INT6 VBAT")) + vbat_path_cfg = WCD9335_CDC_RX6_RX_PATH_CFG1; + else if (!strcmp(w->name, "RX INT5 VBAT")) + vbat_path_cfg = WCD9335_CDC_RX5_RX_PATH_CFG1; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + ret = tasha_update_vbat_reg_config(codec); + if (ret) { + dev_dbg(codec->dev, + "%s : VBAT isn't calibrated, So not enabling it\n", + __func__); + return 0; + } + snd_soc_write(codec, WCD9335_ANA_VBADC, 0x80); + snd_soc_update_bits(codec, vbat_path_cfg, 0x02, 0x02); + snd_soc_update_bits(codec, vbat_path_ctl, 0x10, 0x10); + snd_soc_update_bits(codec, vbat_cfg, 0x01, 0x01); + tasha->vbat.is_enabled = true; + break; + case SND_SOC_DAPM_POST_PMD: + if (tasha->vbat.is_enabled) { + snd_soc_update_bits(codec, vbat_cfg, 0x01, 0x00); + snd_soc_update_bits(codec, vbat_path_ctl, 0x10, 0x00); + snd_soc_update_bits(codec, vbat_path_cfg, 0x02, 0x00); + snd_soc_write(codec, WCD9335_ANA_VBADC, 0x00); + tasha->vbat.is_enabled = false; + } + break; + }; + + return ret; +} + +static const char * const rx_hph_mode_mux_text[] = { + "CLS_H_INVALID", "CLS_H_HIFI", "CLS_H_LP", "CLS_AB", "CLS_H_LOHIFI" +}; + +static const struct soc_enum rx_hph_mode_mux_enum = + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(rx_hph_mode_mux_text), + rx_hph_mode_mux_text); + +static const char * const amic_pwr_lvl_text[] = { + "LOW_PWR", "DEFAULT", "HIGH_PERF" +}; + +static const struct soc_enum amic_pwr_lvl_enum = + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(amic_pwr_lvl_text), + amic_pwr_lvl_text); + +static const struct snd_kcontrol_new tasha_snd_controls[] = { + SOC_SINGLE_SX_TLV("RX0 Digital Volume", WCD9335_CDC_RX0_RX_VOL_CTL, + 0, -84, 40, digital_gain), /* -84dB min - 40dB max */ + SOC_SINGLE_SX_TLV("RX1 Digital Volume", WCD9335_CDC_RX1_RX_VOL_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("RX2 Digital Volume", WCD9335_CDC_RX2_RX_VOL_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("RX3 Digital Volume", WCD9335_CDC_RX3_RX_VOL_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("RX4 Digital Volume", WCD9335_CDC_RX4_RX_VOL_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("RX5 Digital Volume", WCD9335_CDC_RX5_RX_VOL_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("RX6 Digital Volume", WCD9335_CDC_RX6_RX_VOL_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("RX7 Digital Volume", WCD9335_CDC_RX7_RX_VOL_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("RX8 Digital Volume", WCD9335_CDC_RX8_RX_VOL_CTL, + 0, -84, 40, digital_gain), + + SOC_SINGLE_SX_TLV("RX0 Mix Digital Volume", + WCD9335_CDC_RX0_RX_VOL_MIX_CTL, + 0, -84, 40, digital_gain), /* -84dB min - 40dB max */ + SOC_SINGLE_SX_TLV("RX1 Mix Digital Volume", + WCD9335_CDC_RX1_RX_VOL_MIX_CTL, + 0, -84, 40, digital_gain), /* -84dB min - 40dB max */ + SOC_SINGLE_SX_TLV("RX2 Mix Digital Volume", + WCD9335_CDC_RX2_RX_VOL_MIX_CTL, + 0, -84, 40, digital_gain), /* -84dB min - 40dB max */ + SOC_SINGLE_SX_TLV("RX3 Mix Digital Volume", + WCD9335_CDC_RX3_RX_VOL_MIX_CTL, + 0, -84, 40, digital_gain), /* -84dB min - 40dB max */ + SOC_SINGLE_SX_TLV("RX4 Mix Digital Volume", + WCD9335_CDC_RX4_RX_VOL_MIX_CTL, + 0, -84, 40, digital_gain), /* -84dB min - 40dB max */ + SOC_SINGLE_SX_TLV("RX5 Mix Digital Volume", + WCD9335_CDC_RX5_RX_VOL_MIX_CTL, + 0, -84, 40, digital_gain), /* -84dB min - 40dB max */ + SOC_SINGLE_SX_TLV("RX6 Mix Digital Volume", + WCD9335_CDC_RX6_RX_VOL_MIX_CTL, + 0, -84, 40, digital_gain), /* -84dB min - 40dB max */ + SOC_SINGLE_SX_TLV("RX7 Mix Digital Volume", + WCD9335_CDC_RX7_RX_VOL_MIX_CTL, + 0, -84, 40, digital_gain), /* -84dB min - 40dB max */ + SOC_SINGLE_SX_TLV("RX8 Mix Digital Volume", + WCD9335_CDC_RX8_RX_VOL_MIX_CTL, + 0, -84, 40, digital_gain), /* -84dB min - 40dB max */ + + SOC_SINGLE_SX_TLV("DEC0 Volume", WCD9335_CDC_TX0_TX_VOL_CTL, 0, + -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("DEC1 Volume", WCD9335_CDC_TX1_TX_VOL_CTL, 0, + -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("DEC2 Volume", WCD9335_CDC_TX2_TX_VOL_CTL, 0, + -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("DEC3 Volume", WCD9335_CDC_TX3_TX_VOL_CTL, 0, + -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("DEC4 Volume", WCD9335_CDC_TX4_TX_VOL_CTL, 0, + -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("DEC5 Volume", WCD9335_CDC_TX5_TX_VOL_CTL, 0, + -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("DEC6 Volume", WCD9335_CDC_TX6_TX_VOL_CTL, 0, + -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("DEC7 Volume", WCD9335_CDC_TX7_TX_VOL_CTL, 0, + -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("DEC8 Volume", WCD9335_CDC_TX8_TX_VOL_CTL, 0, + -84, 40, digital_gain), + + SOC_SINGLE_SX_TLV("IIR0 INP0 Volume", + WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B1_CTL, 0, -84, + 40, digital_gain), + SOC_SINGLE_SX_TLV("IIR0 INP1 Volume", + WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B2_CTL, 0, -84, + 40, digital_gain), + SOC_SINGLE_SX_TLV("IIR0 INP2 Volume", + WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B3_CTL, 0, -84, + 40, digital_gain), + SOC_SINGLE_SX_TLV("IIR0 INP3 Volume", + WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B4_CTL, 0, -84, + 40, digital_gain), + SOC_SINGLE_SX_TLV("IIR1 INP0 Volume", + WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B1_CTL, 0, -84, + 40, digital_gain), + SOC_SINGLE_SX_TLV("IIR1 INP1 Volume", + WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B2_CTL, 0, -84, + 40, digital_gain), + SOC_SINGLE_SX_TLV("IIR1 INP2 Volume", + WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B3_CTL, 0, -84, + 40, digital_gain), + SOC_SINGLE_SX_TLV("IIR1 INP3 Volume", + WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B4_CTL, 0, -84, + 40, digital_gain), + + SOC_SINGLE_EXT("ANC Slot", SND_SOC_NOPM, 0, 100, 0, tasha_get_anc_slot, + tasha_put_anc_slot), + SOC_ENUM_EXT("ANC Function", tasha_anc_func_enum, tasha_get_anc_func, + tasha_put_anc_func), + + SOC_ENUM_EXT("CLK MODE", tasha_clkmode_enum, tasha_get_clkmode, + tasha_put_clkmode), + + SOC_ENUM("TX0 HPF cut off", cf_dec0_enum), + SOC_ENUM("TX1 HPF cut off", cf_dec1_enum), + SOC_ENUM("TX2 HPF cut off", cf_dec2_enum), + SOC_ENUM("TX3 HPF cut off", cf_dec3_enum), + SOC_ENUM("TX4 HPF cut off", cf_dec4_enum), + SOC_ENUM("TX5 HPF cut off", cf_dec5_enum), + SOC_ENUM("TX6 HPF cut off", cf_dec6_enum), + SOC_ENUM("TX7 HPF cut off", cf_dec7_enum), + SOC_ENUM("TX8 HPF cut off", cf_dec8_enum), + + SOC_ENUM("RX INT0_1 HPF cut off", cf_int0_1_enum), + SOC_ENUM("RX INT0_2 HPF cut off", cf_int0_2_enum), + SOC_ENUM("RX INT1_1 HPF cut off", cf_int1_1_enum), + SOC_ENUM("RX INT1_2 HPF cut off", cf_int1_2_enum), + SOC_ENUM("RX INT2_1 HPF cut off", cf_int2_1_enum), + SOC_ENUM("RX INT2_2 HPF cut off", cf_int2_2_enum), + SOC_ENUM("RX INT3_1 HPF cut off", cf_int3_1_enum), + SOC_ENUM("RX INT3_2 HPF cut off", cf_int3_2_enum), + SOC_ENUM("RX INT4_1 HPF cut off", cf_int4_1_enum), + SOC_ENUM("RX INT4_2 HPF cut off", cf_int4_2_enum), + SOC_ENUM("RX INT5_1 HPF cut off", cf_int5_1_enum), + SOC_ENUM("RX INT5_2 HPF cut off", cf_int5_2_enum), + SOC_ENUM("RX INT6_1 HPF cut off", cf_int6_1_enum), + SOC_ENUM("RX INT6_2 HPF cut off", cf_int6_2_enum), + SOC_ENUM("RX INT7_1 HPF cut off", cf_int7_1_enum), + SOC_ENUM("RX INT7_2 HPF cut off", cf_int7_2_enum), + SOC_ENUM("RX INT8_1 HPF cut off", cf_int8_1_enum), + SOC_ENUM("RX INT8_2 HPF cut off", cf_int8_2_enum), + + SOC_SINGLE_EXT("IIR0 Enable Band1", IIR0, BAND1, 1, 0, + tasha_get_iir_enable_audio_mixer, tasha_put_iir_enable_audio_mixer), + SOC_SINGLE_EXT("IIR0 Enable Band2", IIR0, BAND2, 1, 0, + tasha_get_iir_enable_audio_mixer, tasha_put_iir_enable_audio_mixer), + SOC_SINGLE_EXT("IIR0 Enable Band3", IIR0, BAND3, 1, 0, + tasha_get_iir_enable_audio_mixer, tasha_put_iir_enable_audio_mixer), + SOC_SINGLE_EXT("IIR0 Enable Band4", IIR0, BAND4, 1, 0, + tasha_get_iir_enable_audio_mixer, tasha_put_iir_enable_audio_mixer), + SOC_SINGLE_EXT("IIR0 Enable Band5", IIR0, BAND5, 1, 0, + tasha_get_iir_enable_audio_mixer, tasha_put_iir_enable_audio_mixer), + SOC_SINGLE_EXT("IIR1 Enable Band1", IIR1, BAND1, 1, 0, + tasha_get_iir_enable_audio_mixer, tasha_put_iir_enable_audio_mixer), + SOC_SINGLE_EXT("IIR1 Enable Band2", IIR1, BAND2, 1, 0, + tasha_get_iir_enable_audio_mixer, tasha_put_iir_enable_audio_mixer), + SOC_SINGLE_EXT("IIR1 Enable Band3", IIR1, BAND3, 1, 0, + tasha_get_iir_enable_audio_mixer, tasha_put_iir_enable_audio_mixer), + SOC_SINGLE_EXT("IIR1 Enable Band4", IIR1, BAND4, 1, 0, + tasha_get_iir_enable_audio_mixer, tasha_put_iir_enable_audio_mixer), + SOC_SINGLE_EXT("IIR1 Enable Band5", IIR1, BAND5, 1, 0, + tasha_get_iir_enable_audio_mixer, tasha_put_iir_enable_audio_mixer), + + SOC_SINGLE_MULTI_EXT("IIR0 Band1", IIR0, BAND1, 255, 0, 5, + tasha_get_iir_band_audio_mixer, tasha_put_iir_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("IIR0 Band2", IIR0, BAND2, 255, 0, 5, + tasha_get_iir_band_audio_mixer, tasha_put_iir_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("IIR0 Band3", IIR0, BAND3, 255, 0, 5, + tasha_get_iir_band_audio_mixer, tasha_put_iir_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("IIR0 Band4", IIR0, BAND4, 255, 0, 5, + tasha_get_iir_band_audio_mixer, tasha_put_iir_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("IIR0 Band5", IIR0, BAND5, 255, 0, 5, + tasha_get_iir_band_audio_mixer, tasha_put_iir_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("IIR1 Band1", IIR1, BAND1, 255, 0, 5, + tasha_get_iir_band_audio_mixer, tasha_put_iir_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("IIR1 Band2", IIR1, BAND2, 255, 0, 5, + tasha_get_iir_band_audio_mixer, tasha_put_iir_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("IIR1 Band3", IIR1, BAND3, 255, 0, 5, + tasha_get_iir_band_audio_mixer, tasha_put_iir_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("IIR1 Band4", IIR1, BAND4, 255, 0, 5, + tasha_get_iir_band_audio_mixer, tasha_put_iir_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("IIR1 Band5", IIR1, BAND5, 255, 0, 5, + tasha_get_iir_band_audio_mixer, tasha_put_iir_band_audio_mixer), + + SOC_SINGLE_EXT("COMP1 Switch", SND_SOC_NOPM, COMPANDER_1, 1, 0, + tasha_get_compander, tasha_set_compander), + SOC_SINGLE_EXT("COMP2 Switch", SND_SOC_NOPM, COMPANDER_2, 1, 0, + tasha_get_compander, tasha_set_compander), + SOC_SINGLE_EXT("COMP3 Switch", SND_SOC_NOPM, COMPANDER_3, 1, 0, + tasha_get_compander, tasha_set_compander), + SOC_SINGLE_EXT("COMP4 Switch", SND_SOC_NOPM, COMPANDER_4, 1, 0, + tasha_get_compander, tasha_set_compander), + SOC_SINGLE_EXT("COMP5 Switch", SND_SOC_NOPM, COMPANDER_5, 1, 0, + tasha_get_compander, tasha_set_compander), + SOC_SINGLE_EXT("COMP6 Switch", SND_SOC_NOPM, COMPANDER_6, 1, 0, + tasha_get_compander, tasha_set_compander), + SOC_SINGLE_EXT("COMP7 Switch", SND_SOC_NOPM, COMPANDER_7, 1, 0, + tasha_get_compander, tasha_set_compander), + SOC_SINGLE_EXT("COMP8 Switch", SND_SOC_NOPM, COMPANDER_8, 1, 0, + tasha_get_compander, tasha_set_compander), + + SOC_ENUM_EXT("RX HPH Mode", rx_hph_mode_mux_enum, + tasha_rx_hph_mode_get, tasha_rx_hph_mode_put), + + SOC_ENUM_EXT("MAD Input", tasha_conn_mad_enum, + tasha_mad_input_get, tasha_mad_input_put), + SOC_SINGLE_EXT("LDO_H Enable", SND_SOC_NOPM, 0, 1, 0, + tasha_enable_ldo_h_get, tasha_enable_ldo_h_put), + + SOC_SINGLE_EXT("DMIC1_CLK_PIN_MODE", SND_SOC_NOPM, 17, 1, 0, + tasha_pinctl_mode_get, tasha_pinctl_mode_put), + + SOC_SINGLE_EXT("DMIC1_DATA_PIN_MODE", SND_SOC_NOPM, 18, 1, 0, + tasha_pinctl_mode_get, tasha_pinctl_mode_put), + + SOC_SINGLE_EXT("DMIC2_CLK_PIN_MODE", SND_SOC_NOPM, 19, 1, 0, + tasha_pinctl_mode_get, tasha_pinctl_mode_put), + + SOC_SINGLE_EXT("DMIC2_DATA_PIN_MODE", SND_SOC_NOPM, 20, 1, 0, + tasha_pinctl_mode_get, tasha_pinctl_mode_put), + + SOC_SINGLE_EXT("DMIC3_CLK_PIN_MODE", SND_SOC_NOPM, 21, 1, 0, + tasha_pinctl_mode_get, tasha_pinctl_mode_put), + + SOC_SINGLE_EXT("DMIC3_DATA_PIN_MODE", SND_SOC_NOPM, 22, 1, 0, + tasha_pinctl_mode_get, tasha_pinctl_mode_put), + SOC_ENUM_EXT("AMIC_1_2 PWR MODE", amic_pwr_lvl_enum, + tasha_amic_pwr_lvl_get, tasha_amic_pwr_lvl_put), + SOC_ENUM_EXT("AMIC_3_4 PWR MODE", amic_pwr_lvl_enum, + tasha_amic_pwr_lvl_get, tasha_amic_pwr_lvl_put), + SOC_ENUM_EXT("AMIC_5_6 PWR MODE", amic_pwr_lvl_enum, + tasha_amic_pwr_lvl_get, tasha_amic_pwr_lvl_put), + + SOC_SINGLE_MULTI_EXT("Vbat ADC data", SND_SOC_NOPM, 0, 0xFFFF, 0, 2, + tasha_vbat_adc_data_get, NULL), + + SOC_ENUM_EXT("GSM mode Enable", tasha_vbat_gsm_mode_enum, + tasha_vbat_gsm_mode_func_get, + tasha_vbat_gsm_mode_func_put), +}; + +static int tasha_put_dec_enum(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int val; + u16 mic_sel_reg; + u8 mic_sel; + + val = ucontrol->value.enumerated.item[0]; + if (val > e->items - 1) + return -EINVAL; + + dev_dbg(codec->dev, "%s: wname: %s, val: 0x%x\n", __func__, + widget->name, val); + + switch (e->reg) { + case WCD9335_CDC_TX_INP_MUX_ADC_MUX0_CFG1: + mic_sel_reg = WCD9335_CDC_TX0_TX_PATH_CFG0; + break; + case WCD9335_CDC_TX_INP_MUX_ADC_MUX1_CFG1: + mic_sel_reg = WCD9335_CDC_TX1_TX_PATH_CFG0; + break; + case WCD9335_CDC_TX_INP_MUX_ADC_MUX2_CFG1: + mic_sel_reg = WCD9335_CDC_TX2_TX_PATH_CFG0; + break; + case WCD9335_CDC_TX_INP_MUX_ADC_MUX3_CFG1: + mic_sel_reg = WCD9335_CDC_TX3_TX_PATH_CFG0; + break; + case WCD9335_CDC_TX_INP_MUX_ADC_MUX4_CFG0: + mic_sel_reg = WCD9335_CDC_TX4_TX_PATH_CFG0; + break; + case WCD9335_CDC_TX_INP_MUX_ADC_MUX5_CFG0: + mic_sel_reg = WCD9335_CDC_TX5_TX_PATH_CFG0; + break; + case WCD9335_CDC_TX_INP_MUX_ADC_MUX6_CFG0: + mic_sel_reg = WCD9335_CDC_TX6_TX_PATH_CFG0; + break; + case WCD9335_CDC_TX_INP_MUX_ADC_MUX7_CFG0: + mic_sel_reg = WCD9335_CDC_TX7_TX_PATH_CFG0; + break; + case WCD9335_CDC_TX_INP_MUX_ADC_MUX8_CFG0: + mic_sel_reg = WCD9335_CDC_TX8_TX_PATH_CFG0; + break; + default: + dev_err(codec->dev, "%s: e->reg: 0x%x not expected\n", + __func__, e->reg); + return -EINVAL; + } + + /* ADC: 0, DMIC: 1 */ + mic_sel = val ? 0x0 : 0x1; + snd_soc_update_bits(codec, mic_sel_reg, 1 << 7, mic_sel << 7); + + return snd_soc_dapm_put_enum_double(kcontrol, ucontrol); +} + +static int tasha_int_dem_inp_mux_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int val; + unsigned short look_ahead_dly_reg = WCD9335_CDC_RX0_RX_PATH_CFG0; + + val = ucontrol->value.enumerated.item[0]; + if (val >= e->items) + return -EINVAL; + + dev_dbg(codec->dev, "%s: wname: %s, val: 0x%x\n", __func__, + widget->name, val); + + if (e->reg == WCD9335_CDC_RX0_RX_PATH_SEC0) + look_ahead_dly_reg = WCD9335_CDC_RX0_RX_PATH_CFG0; + else if (e->reg == WCD9335_CDC_RX1_RX_PATH_SEC0) + look_ahead_dly_reg = WCD9335_CDC_RX1_RX_PATH_CFG0; + else if (e->reg == WCD9335_CDC_RX2_RX_PATH_SEC0) + look_ahead_dly_reg = WCD9335_CDC_RX2_RX_PATH_CFG0; + + /* Set Look Ahead Delay */ + snd_soc_update_bits(codec, look_ahead_dly_reg, + 0x08, (val ? 0x08 : 0x00)); + /* Set DEM INP Select */ + return snd_soc_dapm_put_enum_double(kcontrol, ucontrol); +} + +static int tasha_ear_pa_gain_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u8 ear_pa_gain; + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + + ear_pa_gain = snd_soc_read(codec, WCD9335_ANA_EAR); + + ear_pa_gain = (ear_pa_gain & 0x70) >> 4; + + ucontrol->value.integer.value[0] = ear_pa_gain; + + dev_dbg(codec->dev, "%s: ear_pa_gain = 0x%x\n", __func__, + ear_pa_gain); + + return 0; +} + +static int tasha_ear_pa_gain_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u8 ear_pa_gain; + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + + dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n", + __func__, ucontrol->value.integer.value[0]); + + ear_pa_gain = ucontrol->value.integer.value[0] << 4; + + snd_soc_update_bits(codec, WCD9335_ANA_EAR, 0x70, ear_pa_gain); + return 0; +} + +static int tasha_ear_spkr_pa_gain_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = tasha->ear_spkr_gain; + + dev_dbg(codec->dev, "%s: ear_spkr_gain = %ld\n", __func__, + ucontrol->value.integer.value[0]); + + return 0; +} + +static int tasha_ear_spkr_pa_gain_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n", + __func__, ucontrol->value.integer.value[0]); + + tasha->ear_spkr_gain = ucontrol->value.integer.value[0]; + + return 0; +} + +static int tasha_spkr_left_boost_stage_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u8 bst_state_max = 0; + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + + bst_state_max = snd_soc_read(codec, WCD9335_CDC_BOOST0_BOOST_CTL); + bst_state_max = (bst_state_max & 0x0c) >> 2; + ucontrol->value.integer.value[0] = bst_state_max; + dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n", + __func__, ucontrol->value.integer.value[0]); + + return 0; +} + +static int tasha_spkr_left_boost_stage_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u8 bst_state_max; + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + + dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n", + __func__, ucontrol->value.integer.value[0]); + bst_state_max = ucontrol->value.integer.value[0] << 2; + snd_soc_update_bits(codec, WCD9335_CDC_BOOST0_BOOST_CTL, + 0x0c, bst_state_max); + + return 0; +} + +static int tasha_spkr_right_boost_stage_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u8 bst_state_max = 0; + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + + bst_state_max = snd_soc_read(codec, WCD9335_CDC_BOOST1_BOOST_CTL); + bst_state_max = (bst_state_max & 0x0c) >> 2; + ucontrol->value.integer.value[0] = bst_state_max; + dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n", + __func__, ucontrol->value.integer.value[0]); + + return 0; +} + +static int tasha_spkr_right_boost_stage_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u8 bst_state_max; + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + + dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n", + __func__, ucontrol->value.integer.value[0]); + bst_state_max = ucontrol->value.integer.value[0] << 2; + snd_soc_update_bits(codec, WCD9335_CDC_BOOST1_BOOST_CTL, + 0x0c, bst_state_max); + + return 0; +} + +static int tasha_config_compander(struct snd_soc_codec *codec, int interp_n, + int event) +{ + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + int comp; + u16 comp_ctl0_reg, rx_path_cfg0_reg; + + /* EAR does not have compander */ + if (!interp_n) + return 0; + + comp = interp_n - 1; + dev_dbg(codec->dev, "%s: event %d compander %d, enabled %d\n", + __func__, event, comp + 1, tasha->comp_enabled[comp]); + + if (!tasha->comp_enabled[comp]) + return 0; + + comp_ctl0_reg = WCD9335_CDC_COMPANDER1_CTL0 + (comp * 8); + rx_path_cfg0_reg = WCD9335_CDC_RX1_RX_PATH_CFG0 + (comp * 20); + + if (SND_SOC_DAPM_EVENT_ON(event)) { + /* Enable Compander Clock */ + snd_soc_update_bits(codec, comp_ctl0_reg, 0x01, 0x01); + snd_soc_update_bits(codec, comp_ctl0_reg, 0x02, 0x02); + snd_soc_update_bits(codec, comp_ctl0_reg, 0x02, 0x00); + snd_soc_update_bits(codec, rx_path_cfg0_reg, 0x02, 0x02); + } + + if (SND_SOC_DAPM_EVENT_OFF(event)) { + snd_soc_update_bits(codec, comp_ctl0_reg, 0x04, 0x04); + snd_soc_update_bits(codec, rx_path_cfg0_reg, 0x02, 0x00); + snd_soc_update_bits(codec, comp_ctl0_reg, 0x02, 0x02); + snd_soc_update_bits(codec, comp_ctl0_reg, 0x02, 0x00); + snd_soc_update_bits(codec, comp_ctl0_reg, 0x01, 0x00); + snd_soc_update_bits(codec, comp_ctl0_reg, 0x04, 0x00); + } + + return 0; +} + +static int tasha_codec_config_mad(struct snd_soc_codec *codec) +{ + int ret = 0; + int idx; + const struct firmware *fw; + struct firmware_cal *hwdep_cal = NULL; + struct wcd_mad_audio_cal *mad_cal = NULL; + const void *data; + const char *filename = TASHA_MAD_AUDIO_FIRMWARE_PATH; + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + size_t cal_size; + + hwdep_cal = wcdcal_get_fw_cal(tasha->fw_data, WCD9XXX_MAD_CAL); + if (hwdep_cal) { + data = hwdep_cal->data; + cal_size = hwdep_cal->size; + dev_dbg(codec->dev, "%s: using hwdep calibration\n", + __func__); + } else { + ret = request_firmware(&fw, filename, codec->dev); + if (ret || !fw) { + dev_err(codec->dev, + "%s: MAD firmware acquire failed, err = %d\n", + __func__, ret); + return -ENODEV; + } + data = fw->data; + cal_size = fw->size; + dev_dbg(codec->dev, "%s: using request_firmware calibration\n", + __func__); + } + + if (cal_size < sizeof(*mad_cal)) { + dev_err(codec->dev, + "%s: Incorrect size %zd for MAD Cal, expected %zd\n", + __func__, cal_size, sizeof(*mad_cal)); + ret = -ENOMEM; + goto done; + } + + mad_cal = (struct wcd_mad_audio_cal *) (data); + if (!mad_cal) { + dev_err(codec->dev, + "%s: Invalid calibration data\n", + __func__); + ret = -EINVAL; + goto done; + } + + snd_soc_write(codec, WCD9335_SOC_MAD_MAIN_CTL_2, + mad_cal->microphone_info.cycle_time); + snd_soc_update_bits(codec, WCD9335_SOC_MAD_MAIN_CTL_1, 0xFF << 3, + ((uint16_t)mad_cal->microphone_info.settle_time) + << 3); + + /* Audio */ + snd_soc_write(codec, WCD9335_SOC_MAD_AUDIO_CTL_8, + mad_cal->audio_info.rms_omit_samples); + snd_soc_update_bits(codec, WCD9335_SOC_MAD_AUDIO_CTL_1, + 0x07 << 4, mad_cal->audio_info.rms_comp_time << 4); + snd_soc_update_bits(codec, WCD9335_SOC_MAD_AUDIO_CTL_2, 0x03 << 2, + mad_cal->audio_info.detection_mechanism << 2); + snd_soc_write(codec, WCD9335_SOC_MAD_AUDIO_CTL_7, + mad_cal->audio_info.rms_diff_threshold & 0x3F); + snd_soc_write(codec, WCD9335_SOC_MAD_AUDIO_CTL_5, + mad_cal->audio_info.rms_threshold_lsb); + snd_soc_write(codec, WCD9335_SOC_MAD_AUDIO_CTL_6, + mad_cal->audio_info.rms_threshold_msb); + + for (idx = 0; idx < ARRAY_SIZE(mad_cal->audio_info.iir_coefficients); + idx++) { + snd_soc_update_bits(codec, WCD9335_SOC_MAD_AUDIO_IIR_CTL_PTR, + 0x3F, idx); + snd_soc_write(codec, WCD9335_SOC_MAD_AUDIO_IIR_CTL_VAL, + mad_cal->audio_info.iir_coefficients[idx]); + dev_dbg(codec->dev, "%s:MAD Audio IIR Coef[%d] = 0X%x", + __func__, idx, + mad_cal->audio_info.iir_coefficients[idx]); + } + + /* Beacon */ + snd_soc_write(codec, WCD9335_SOC_MAD_BEACON_CTL_8, + mad_cal->beacon_info.rms_omit_samples); + snd_soc_update_bits(codec, WCD9335_SOC_MAD_BEACON_CTL_1, + 0x07 << 4, mad_cal->beacon_info.rms_comp_time << 4); + snd_soc_update_bits(codec, WCD9335_SOC_MAD_BEACON_CTL_2, 0x03 << 2, + mad_cal->beacon_info.detection_mechanism << 2); + snd_soc_write(codec, WCD9335_SOC_MAD_BEACON_CTL_7, + mad_cal->beacon_info.rms_diff_threshold & 0x1F); + snd_soc_write(codec, WCD9335_SOC_MAD_BEACON_CTL_5, + mad_cal->beacon_info.rms_threshold_lsb); + snd_soc_write(codec, WCD9335_SOC_MAD_BEACON_CTL_6, + mad_cal->beacon_info.rms_threshold_msb); + + for (idx = 0; idx < ARRAY_SIZE(mad_cal->beacon_info.iir_coefficients); + idx++) { + snd_soc_update_bits(codec, WCD9335_SOC_MAD_BEACON_IIR_CTL_PTR, + 0x3F, idx); + snd_soc_write(codec, WCD9335_SOC_MAD_BEACON_IIR_CTL_VAL, + mad_cal->beacon_info.iir_coefficients[idx]); + dev_dbg(codec->dev, "%s:MAD Beacon IIR Coef[%d] = 0X%x", + __func__, idx, + mad_cal->beacon_info.iir_coefficients[idx]); + } + + /* Ultrasound */ + snd_soc_update_bits(codec, WCD9335_SOC_MAD_ULTR_CTL_1, + 0x07 << 4, + mad_cal->ultrasound_info.rms_comp_time << 4); + snd_soc_update_bits(codec, WCD9335_SOC_MAD_ULTR_CTL_2, 0x03 << 2, + mad_cal->ultrasound_info.detection_mechanism << 2); + snd_soc_write(codec, WCD9335_SOC_MAD_ULTR_CTL_7, + mad_cal->ultrasound_info.rms_diff_threshold & 0x1F); + snd_soc_write(codec, WCD9335_SOC_MAD_ULTR_CTL_5, + mad_cal->ultrasound_info.rms_threshold_lsb); + snd_soc_write(codec, WCD9335_SOC_MAD_ULTR_CTL_6, + mad_cal->ultrasound_info.rms_threshold_msb); + +done: + if (!hwdep_cal) + release_firmware(fw); + + return ret; +} + +static int tasha_codec_enable_mad(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + int ret = 0; + + dev_dbg(codec->dev, + "%s: event = %d\n", __func__, event); + + /* Return if CPE INPUT is DEC1 */ + if (snd_soc_read(codec, WCD9335_CPE_SS_SVA_CFG) & 0x01) + return ret; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + + /* Turn on MAD clk */ + snd_soc_update_bits(codec, WCD9335_CPE_SS_MAD_CTL, + 0x01, 0x01); + + /* Undo reset for MAD */ + snd_soc_update_bits(codec, WCD9335_CPE_SS_MAD_CTL, + 0x02, 0x00); + ret = tasha_codec_config_mad(codec); + if (ret) + dev_err(codec->dev, + "%s: Failed to config MAD, err = %d\n", + __func__, ret); + break; + case SND_SOC_DAPM_POST_PMD: + /* Reset the MAD block */ + snd_soc_update_bits(codec, WCD9335_CPE_SS_MAD_CTL, + 0x02, 0x02); + /* Turn off MAD clk */ + snd_soc_update_bits(codec, WCD9335_CPE_SS_MAD_CTL, + 0x01, 0x00); + break; + } + + return ret; +} + +static int tasha_codec_configure_cpe_input(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + + dev_dbg(codec->dev, + "%s: event = %d\n", __func__, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* Configure CPE input as DEC1 */ + snd_soc_update_bits(codec, WCD9335_CPE_SS_SVA_CFG, + 0x01, 0x01); + + /* Configure DEC1 Tx out with sample rate as 16K */ + snd_soc_update_bits(codec, WCD9335_CDC_TX1_TX_PATH_CTL, + 0x0F, 0x01); + + break; + case SND_SOC_DAPM_POST_PMD: + /* Reset DEC1 Tx out sample rate */ + snd_soc_update_bits(codec, WCD9335_CDC_TX1_TX_PATH_CTL, + 0x0F, 0x04); + snd_soc_update_bits(codec, WCD9335_CPE_SS_SVA_CFG, + 0x01, 0x00); + + break; + } + + return 0; +} + + +static int tasha_codec_aif4_mixer_switch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm); + struct tasha_priv *tasha_p = snd_soc_codec_get_drvdata(codec); + + if (test_bit(AIF4_SWITCH_VALUE, &tasha_p->status_mask)) + ucontrol->value.integer.value[0] = 1; + else + ucontrol->value.integer.value[0] = 0; + + dev_dbg(codec->dev, "%s: AIF4 switch value = %ld\n", + __func__, ucontrol->value.integer.value[0]); + return 0; +} + +static int tasha_codec_aif4_mixer_switch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); + struct snd_soc_dapm_update *update = NULL; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm); + struct tasha_priv *tasha_p = snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, "%s: AIF4 switch value = %ld\n", + __func__, ucontrol->value.integer.value[0]); + + if (ucontrol->value.integer.value[0]) { + snd_soc_dapm_mixer_update_power(widget->dapm, + kcontrol, 1, update); + set_bit(AIF4_SWITCH_VALUE, &tasha_p->status_mask); + } else { + snd_soc_dapm_mixer_update_power(widget->dapm, + kcontrol, 0, update); + clear_bit(AIF4_SWITCH_VALUE, &tasha_p->status_mask); + } + + return 1; +} + +static const char * const tasha_ear_pa_gain_text[] = { + "G_6_DB", "G_4P5_DB", "G_3_DB", "G_1P5_DB", + "G_0_DB", "G_M2P5_DB", "UNDEFINED", "G_M12_DB" +}; + +static const char * const tasha_ear_spkr_pa_gain_text[] = { + "G_DEFAULT", "G_0_DB", "G_1_DB", "G_2_DB", "G_3_DB", "G_4_DB", + "G_5_DB", "G_6_DB" +}; + +static const char * const tasha_speaker_boost_stage_text[] = { + "NO_MAX_STATE", "MAX_STATE_1", "MAX_STATE_2" +}; + +static const struct soc_enum tasha_ear_pa_gain_enum = + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(tasha_ear_pa_gain_text), + tasha_ear_pa_gain_text); + +static const struct soc_enum tasha_ear_spkr_pa_gain_enum = + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(tasha_ear_spkr_pa_gain_text), + tasha_ear_spkr_pa_gain_text); + +static const struct soc_enum tasha_spkr_boost_stage_enum = + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(tasha_speaker_boost_stage_text), + tasha_speaker_boost_stage_text); + +static const struct snd_kcontrol_new tasha_analog_gain_controls[] = { + SOC_ENUM_EXT("EAR PA Gain", tasha_ear_pa_gain_enum, + tasha_ear_pa_gain_get, tasha_ear_pa_gain_put), + + SOC_SINGLE_TLV("HPHL Volume", WCD9335_HPH_L_EN, 0, 20, 1, + line_gain), + SOC_SINGLE_TLV("HPHR Volume", WCD9335_HPH_R_EN, 0, 20, 1, + line_gain), + SOC_SINGLE_TLV("LINEOUT1 Volume", WCD9335_DIFF_LO_LO1_COMPANDER, + 3, 16, 1, line_gain), + SOC_SINGLE_TLV("LINEOUT2 Volume", WCD9335_DIFF_LO_LO2_COMPANDER, + 3, 16, 1, line_gain), + SOC_SINGLE_TLV("LINEOUT3 Volume", WCD9335_SE_LO_LO3_GAIN, 0, 20, 1, + line_gain), + SOC_SINGLE_TLV("LINEOUT4 Volume", WCD9335_SE_LO_LO4_GAIN, 0, 20, 1, + line_gain), + + SOC_SINGLE_TLV("ADC1 Volume", WCD9335_ANA_AMIC1, 0, 20, 0, + analog_gain), + SOC_SINGLE_TLV("ADC2 Volume", WCD9335_ANA_AMIC2, 0, 20, 0, + analog_gain), + SOC_SINGLE_TLV("ADC3 Volume", WCD9335_ANA_AMIC3, 0, 20, 0, + analog_gain), + SOC_SINGLE_TLV("ADC4 Volume", WCD9335_ANA_AMIC4, 0, 20, 0, + analog_gain), + SOC_SINGLE_TLV("ADC5 Volume", WCD9335_ANA_AMIC5, 0, 20, 0, + analog_gain), + SOC_SINGLE_TLV("ADC6 Volume", WCD9335_ANA_AMIC6, 0, 20, 0, + analog_gain), +}; + +static const struct snd_kcontrol_new tasha_spkr_wsa_controls[] = { + SOC_ENUM_EXT("EAR SPKR PA Gain", tasha_ear_spkr_pa_gain_enum, + tasha_ear_spkr_pa_gain_get, tasha_ear_spkr_pa_gain_put), + + SOC_ENUM_EXT("SPKR Left Boost Max State", tasha_spkr_boost_stage_enum, + tasha_spkr_left_boost_stage_get, + tasha_spkr_left_boost_stage_put), + + SOC_ENUM_EXT("SPKR Right Boost Max State", tasha_spkr_boost_stage_enum, + tasha_spkr_right_boost_stage_get, + tasha_spkr_right_boost_stage_put), +}; + +static const char * const spl_src0_mux_text[] = { + "ZERO", "SRC_IN_HPHL", "SRC_IN_LO1", +}; + +static const char * const spl_src1_mux_text[] = { + "ZERO", "SRC_IN_HPHR", "SRC_IN_LO2", +}; + +static const char * const spl_src2_mux_text[] = { + "ZERO", "SRC_IN_LO3", "SRC_IN_SPKRL", +}; + +static const char * const spl_src3_mux_text[] = { + "ZERO", "SRC_IN_LO4", "SRC_IN_SPKRR", +}; + +static const char * const rx_int0_7_mix_mux_text[] = { + "ZERO", "RX0", "RX1", "RX2", "RX3", "RX4", "RX5", + "RX6", "RX7", "PROXIMITY" +}; + +static const char * const rx_int_mix_mux_text[] = { + "ZERO", "RX0", "RX1", "RX2", "RX3", "RX4", "RX5", + "RX6", "RX7" +}; + +static const char * const rx_prim_mix_text[] = { + "ZERO", "DEC0", "DEC1", "IIR0", "IIR1", "RX0", "RX1", "RX2", + "RX3", "RX4", "RX5", "RX6", "RX7" +}; + +static const char * const rx_sidetone_mix_text[] = { + "ZERO", "SRC0", "SRC1", "SRC_SUM" +}; + +static const char * const sb_tx0_mux_text[] = { + "ZERO", "RX_MIX_TX0", "DEC0", "DEC0_192" +}; + +static const char * const sb_tx1_mux_text[] = { + "ZERO", "RX_MIX_TX1", "DEC1", "DEC1_192" +}; + +static const char * const sb_tx2_mux_text[] = { + "ZERO", "RX_MIX_TX2", "DEC2", "DEC2_192" +}; + +static const char * const sb_tx3_mux_text[] = { + "ZERO", "RX_MIX_TX3", "DEC3", "DEC3_192" +}; + +static const char * const sb_tx4_mux_text[] = { + "ZERO", "RX_MIX_TX4", "DEC4", "DEC4_192" +}; + +static const char * const sb_tx5_mux_text[] = { + "ZERO", "RX_MIX_TX5", "DEC5", "DEC5_192" +}; + +static const char * const sb_tx6_mux_text[] = { + "ZERO", "RX_MIX_TX6", "DEC6", "DEC6_192" +}; + +static const char * const sb_tx7_mux_text[] = { + "ZERO", "RX_MIX_TX7", "DEC7", "DEC7_192" +}; + +static const char * const sb_tx8_mux_text[] = { + "ZERO", "RX_MIX_TX8", "DEC8", "DEC8_192" +}; + +static const char * const sb_tx9_mux_text[] = { + "ZERO", "DEC7", "DEC7_192" +}; + +static const char * const sb_tx10_mux_text[] = { + "ZERO", "DEC6", "DEC6_192" +}; + +static const char * const sb_tx11_mux_text[] = { + "DEC_0_5", "DEC_9_12", "MAD_AUDIO", "MAD_BRDCST" +}; + +static const char * const sb_tx11_inp1_mux_text[] = { + "ZERO", "DEC0", "DEC1", "DEC2", "DEC3", "DEC4", + "DEC5", "RX_MIX_TX5", "DEC9_10", "DEC11_12" +}; + +static const char * const sb_tx13_mux_text[] = { + "ZERO", "DEC5", "DEC5_192" +}; + +static const char * const tx13_inp_mux_text[] = { + "CDC_DEC_5", "MAD_BRDCST", "CPE_TX_PP" +}; + +static const char * const iir_inp_mux_text[] = { + "ZERO", "DEC0", "DEC1", "DEC2", "DEC3", "DEC4", "DEC5", "DEC6", + "DEC7", "DEC8", "RX0", "RX1", "RX2", "RX3", "RX4", "RX5", "RX6", "RX7" +}; + +static const char * const rx_int_dem_inp_mux_text[] = { + "NORMAL_DSM_OUT", "CLSH_DSM_OUT", +}; + +static const char * const rx_int0_interp_mux_text[] = { + "ZERO", "RX INT0 MIX2", +}; + +static const char * const rx_int1_interp_mux_text[] = { + "ZERO", "RX INT1 MIX2", +}; + +static const char * const rx_int2_interp_mux_text[] = { + "ZERO", "RX INT2 MIX2", +}; + +static const char * const rx_int3_interp_mux_text[] = { + "ZERO", "RX INT3 MIX2", +}; + +static const char * const rx_int4_interp_mux_text[] = { + "ZERO", "RX INT4 MIX2", +}; + +static const char * const rx_int5_interp_mux_text[] = { + "ZERO", "RX INT5 MIX2", +}; + +static const char * const rx_int6_interp_mux_text[] = { + "ZERO", "RX INT6 MIX2", +}; + +static const char * const rx_int7_interp_mux_text[] = { + "ZERO", "RX INT7 MIX2", +}; + +static const char * const rx_int8_interp_mux_text[] = { + "ZERO", "RX INT8 SEC MIX" +}; + +static const char * const mad_sel_text[] = { + "SPE", "MSM" +}; + +static const char * const adc_mux_text[] = { + "DMIC", "AMIC", "ANC_FB_TUNE1", "ANC_FB_TUNE2" +}; + +static const char * const dmic_mux_text[] = { + "ZERO", "DMIC0", "DMIC1", "DMIC2", "DMIC3", "DMIC4", "DMIC5", + "SMIC0", "SMIC1", "SMIC2", "SMIC3" +}; + +static const char * const dmic_mux_alt_text[] = { + "ZERO", "DMIC0", "DMIC1", "DMIC2", "DMIC3", "DMIC4", "DMIC5", +}; + +static const char * const amic_mux_text[] = { + "ZERO", "ADC1", "ADC2", "ADC3", "ADC4", "ADC5", "ADC6" +}; + +static const char * const rx_echo_mux_text[] = { + "ZERO", "RX_MIX0", "RX_MIX1", "RX_MIX2", "RX_MIX3", "RX_MIX4", + "RX_MIX5", "RX_MIX6", "RX_MIX7", "RX_MIX8", "RX_MIX_VBAT5", + "RX_MIX_VBAT6", "RX_MIX_VBAT7", "RX_MIX_VBAT8" +}; + +static const char * const anc0_fb_mux_text[] = { + "ZERO", "ANC_IN_HPHL", "ANC_IN_EAR", "ANC_IN_EAR_SPKR", + "ANC_IN_LO1" +}; + +static const char * const anc1_fb_mux_text[] = { + "ZERO", "ANC_IN_HPHR", "ANC_IN_LO2" +}; + +static const char * const native_mux_text[] = { + "OFF", "ON", +}; + +static const struct soc_enum spl_src0_mux_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_SPLINE_SRC_CFG0, 0, 3, + spl_src0_mux_text); + +static const struct soc_enum spl_src1_mux_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_SPLINE_SRC_CFG0, 2, 3, + spl_src1_mux_text); + +static const struct soc_enum spl_src2_mux_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_SPLINE_SRC_CFG0, 4, 3, + spl_src2_mux_text); + +static const struct soc_enum spl_src3_mux_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_SPLINE_SRC_CFG0, 6, 3, + spl_src3_mux_text); + +static const struct soc_enum rx_int0_2_mux_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT0_CFG1, 0, 10, + rx_int0_7_mix_mux_text); + +static const struct soc_enum rx_int1_2_mux_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT1_CFG1, 0, 9, + rx_int_mix_mux_text); + +static const struct soc_enum rx_int2_2_mux_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT2_CFG1, 0, 9, + rx_int_mix_mux_text); + +static const struct soc_enum rx_int3_2_mux_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT3_CFG1, 0, 9, + rx_int_mix_mux_text); + +static const struct soc_enum rx_int4_2_mux_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT4_CFG1, 0, 9, + rx_int_mix_mux_text); + +static const struct soc_enum rx_int5_2_mux_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT5_CFG1, 0, 9, + rx_int_mix_mux_text); + +static const struct soc_enum rx_int6_2_mux_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT6_CFG1, 0, 9, + rx_int_mix_mux_text); + +static const struct soc_enum rx_int7_2_mux_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT7_CFG1, 0, 10, + rx_int0_7_mix_mux_text); + +static const struct soc_enum rx_int8_2_mux_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT8_CFG1, 0, 9, + rx_int_mix_mux_text); + +static const struct soc_enum int1_1_native_enum = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(native_mux_text), + native_mux_text); + +static const struct soc_enum int2_1_native_enum = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(native_mux_text), + native_mux_text); + +static const struct soc_enum int3_1_native_enum = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(native_mux_text), + native_mux_text); + +static const struct soc_enum int4_1_native_enum = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(native_mux_text), + native_mux_text); + +static const struct soc_enum rx_int0_1_mix_inp0_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT0_CFG0, 0, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int0_1_mix_inp1_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT0_CFG0, 4, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int0_1_mix_inp2_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT0_CFG1, 4, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int1_1_mix_inp0_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT1_CFG0, 0, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int1_1_mix_inp1_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT1_CFG0, 4, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int1_1_mix_inp2_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT1_CFG1, 4, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int2_1_mix_inp0_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT2_CFG0, 0, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int2_1_mix_inp1_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT2_CFG0, 4, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int2_1_mix_inp2_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT2_CFG1, 4, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int3_1_mix_inp0_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT3_CFG0, 0, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int3_1_mix_inp1_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT3_CFG0, 4, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int3_1_mix_inp2_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT3_CFG1, 4, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int4_1_mix_inp0_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT4_CFG0, 0, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int4_1_mix_inp1_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT4_CFG0, 4, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int4_1_mix_inp2_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT4_CFG1, 4, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int5_1_mix_inp0_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT5_CFG0, 0, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int5_1_mix_inp1_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT5_CFG0, 4, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int5_1_mix_inp2_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT5_CFG1, 4, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int6_1_mix_inp0_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT6_CFG0, 0, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int6_1_mix_inp1_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT6_CFG0, 4, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int6_1_mix_inp2_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT6_CFG1, 4, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int7_1_mix_inp0_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT7_CFG0, 0, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int7_1_mix_inp1_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT7_CFG0, 4, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int7_1_mix_inp2_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT7_CFG1, 4, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int8_1_mix_inp0_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT8_CFG0, 0, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int8_1_mix_inp1_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT8_CFG0, 4, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int8_1_mix_inp2_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT8_CFG1, 4, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int0_sidetone_mix_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_SIDETONE_SRC_CFG0, 0, 4, + rx_sidetone_mix_text); + +static const struct soc_enum rx_int1_sidetone_mix_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_SIDETONE_SRC_CFG0, 2, 4, + rx_sidetone_mix_text); + +static const struct soc_enum rx_int2_sidetone_mix_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_SIDETONE_SRC_CFG0, 4, 4, + rx_sidetone_mix_text); + +static const struct soc_enum rx_int3_sidetone_mix_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_SIDETONE_SRC_CFG0, 6, 4, + rx_sidetone_mix_text); + +static const struct soc_enum rx_int4_sidetone_mix_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_SIDETONE_SRC_CFG1, 0, 4, + rx_sidetone_mix_text); + +static const struct soc_enum rx_int7_sidetone_mix_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_SIDETONE_SRC_CFG1, 2, 4, + rx_sidetone_mix_text); + +static const struct soc_enum tx_adc_mux0_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX0_CFG1, 0, 4, + adc_mux_text); + +static const struct soc_enum tx_adc_mux1_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX1_CFG1, 0, 4, + adc_mux_text); + +static const struct soc_enum tx_adc_mux2_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX2_CFG1, 0, 4, + adc_mux_text); + +static const struct soc_enum tx_adc_mux3_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX3_CFG1, 0, 4, + adc_mux_text); + +static const struct soc_enum tx_adc_mux4_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX4_CFG0, 6, 4, + adc_mux_text); + +static const struct soc_enum tx_adc_mux5_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX5_CFG0, 6, 4, + adc_mux_text); + +static const struct soc_enum tx_adc_mux6_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX6_CFG0, 6, 4, + adc_mux_text); + +static const struct soc_enum tx_adc_mux7_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX7_CFG0, 6, 4, + adc_mux_text); + +static const struct soc_enum tx_adc_mux8_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX8_CFG0, 6, 4, + adc_mux_text); + +static const struct soc_enum tx_adc_mux10_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX10_CFG0, 6, 4, + adc_mux_text); + +static const struct soc_enum tx_adc_mux11_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX11_CFG0, 6, 4, + adc_mux_text); + +static const struct soc_enum tx_adc_mux12_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX12_CFG0, 6, 4, + adc_mux_text); + +static const struct soc_enum tx_adc_mux13_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX13_CFG0, 6, 4, + adc_mux_text); + +static const struct soc_enum tx_dmic_mux0_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX0_CFG0, 3, 11, + dmic_mux_text); + +static const struct soc_enum tx_dmic_mux1_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX1_CFG0, 3, 11, + dmic_mux_text); + +static const struct soc_enum tx_dmic_mux2_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX2_CFG0, 3, 11, + dmic_mux_text); + +static const struct soc_enum tx_dmic_mux3_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX3_CFG0, 3, 11, + dmic_mux_text); + +static const struct soc_enum tx_dmic_mux4_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX4_CFG0, 3, 7, + dmic_mux_alt_text); + +static const struct soc_enum tx_dmic_mux5_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX5_CFG0, 3, 7, + dmic_mux_alt_text); + +static const struct soc_enum tx_dmic_mux6_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX6_CFG0, 3, 7, + dmic_mux_alt_text); + +static const struct soc_enum tx_dmic_mux7_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX7_CFG0, 3, 7, + dmic_mux_alt_text); + +static const struct soc_enum tx_dmic_mux8_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX8_CFG0, 3, 7, + dmic_mux_alt_text); + +static const struct soc_enum tx_dmic_mux10_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX10_CFG0, 3, 7, + dmic_mux_alt_text); + +static const struct soc_enum tx_dmic_mux11_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX11_CFG0, 3, 7, + dmic_mux_alt_text); + +static const struct soc_enum tx_dmic_mux12_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX12_CFG0, 3, 7, + dmic_mux_alt_text); + +static const struct soc_enum tx_dmic_mux13_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX13_CFG0, 3, 7, + dmic_mux_alt_text); + +static const struct soc_enum tx_amic_mux0_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX0_CFG0, 0, 7, + amic_mux_text); + +static const struct soc_enum tx_amic_mux1_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX1_CFG0, 0, 7, + amic_mux_text); + +static const struct soc_enum tx_amic_mux2_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX2_CFG0, 0, 7, + amic_mux_text); + +static const struct soc_enum tx_amic_mux3_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX3_CFG0, 0, 7, + amic_mux_text); + +static const struct soc_enum tx_amic_mux4_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX4_CFG0, 0, 7, + amic_mux_text); + +static const struct soc_enum tx_amic_mux5_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX5_CFG0, 0, 7, + amic_mux_text); + +static const struct soc_enum tx_amic_mux6_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX6_CFG0, 0, 7, + amic_mux_text); + +static const struct soc_enum tx_amic_mux7_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX7_CFG0, 0, 7, + amic_mux_text); + +static const struct soc_enum tx_amic_mux8_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX8_CFG0, 0, 7, + amic_mux_text); + +static const struct soc_enum tx_amic_mux10_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX10_CFG0, 0, 7, + amic_mux_text); + +static const struct soc_enum tx_amic_mux11_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX11_CFG0, 0, 7, + amic_mux_text); + +static const struct soc_enum tx_amic_mux12_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX12_CFG0, 0, 7, + amic_mux_text); + +static const struct soc_enum tx_amic_mux13_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX13_CFG0, 0, 7, + amic_mux_text); + +static const struct soc_enum sb_tx0_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_IF_ROUTER_TX_MUX_CFG0, 0, 4, + sb_tx0_mux_text); + +static const struct soc_enum sb_tx1_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_IF_ROUTER_TX_MUX_CFG0, 2, 4, + sb_tx1_mux_text); + +static const struct soc_enum sb_tx2_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_IF_ROUTER_TX_MUX_CFG0, 4, 4, + sb_tx2_mux_text); + +static const struct soc_enum sb_tx3_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_IF_ROUTER_TX_MUX_CFG0, 6, 4, + sb_tx3_mux_text); + +static const struct soc_enum sb_tx4_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_IF_ROUTER_TX_MUX_CFG1, 0, 4, + sb_tx4_mux_text); + +static const struct soc_enum sb_tx5_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_IF_ROUTER_TX_MUX_CFG1, 2, 4, + sb_tx5_mux_text); + +static const struct soc_enum sb_tx6_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_IF_ROUTER_TX_MUX_CFG1, 4, 4, + sb_tx6_mux_text); + +static const struct soc_enum sb_tx7_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_IF_ROUTER_TX_MUX_CFG1, 6, 4, + sb_tx7_mux_text); + +static const struct soc_enum sb_tx8_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_IF_ROUTER_TX_MUX_CFG2, 0, 4, + sb_tx8_mux_text); + +static const struct soc_enum sb_tx9_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_IF_ROUTER_TX_MUX_CFG2, 2, 3, + sb_tx9_mux_text); + +static const struct soc_enum sb_tx10_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_IF_ROUTER_TX_MUX_CFG2, 4, 3, + sb_tx10_mux_text); + +static const struct soc_enum sb_tx11_mux_enum = + SOC_ENUM_SINGLE(WCD9335_DATA_HUB_DATA_HUB_SB_TX11_INP_CFG, 0, 4, + sb_tx11_mux_text); + +static const struct soc_enum sb_tx11_inp1_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_IF_ROUTER_TX_MUX_CFG3, 0, 10, + sb_tx11_inp1_mux_text); + +static const struct soc_enum sb_tx13_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_IF_ROUTER_TX_MUX_CFG3, 4, 3, + sb_tx13_mux_text); + +static const struct soc_enum tx13_inp_mux_enum = + SOC_ENUM_SINGLE(WCD9335_DATA_HUB_DATA_HUB_SB_TX13_INP_CFG, 0, 3, + tx13_inp_mux_text); + +static const struct soc_enum rx_mix_tx0_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_MIX_CFG0, 0, 14, + rx_echo_mux_text); + +static const struct soc_enum rx_mix_tx1_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_MIX_CFG0, 4, 14, + rx_echo_mux_text); + +static const struct soc_enum rx_mix_tx2_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_MIX_CFG1, 0, 14, + rx_echo_mux_text); + +static const struct soc_enum rx_mix_tx3_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_MIX_CFG1, 4, 14, + rx_echo_mux_text); + +static const struct soc_enum rx_mix_tx4_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_MIX_CFG2, 0, 14, + rx_echo_mux_text); + +static const struct soc_enum rx_mix_tx5_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_MIX_CFG2, 4, 14, + rx_echo_mux_text); + +static const struct soc_enum rx_mix_tx6_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_MIX_CFG3, 0, 14, + rx_echo_mux_text); + +static const struct soc_enum rx_mix_tx7_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_MIX_CFG3, 4, 14, + rx_echo_mux_text); + +static const struct soc_enum rx_mix_tx8_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_MIX_CFG4, 0, 14, + rx_echo_mux_text); + +static const struct soc_enum iir0_inp0_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG0, 0, 18, + iir_inp_mux_text); + +static const struct soc_enum iir0_inp1_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG1, 0, 18, + iir_inp_mux_text); + +static const struct soc_enum iir0_inp2_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG2, 0, 18, + iir_inp_mux_text); + +static const struct soc_enum iir0_inp3_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG3, 0, 18, + iir_inp_mux_text); + +static const struct soc_enum iir1_inp0_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG0, 0, 18, + iir_inp_mux_text); + +static const struct soc_enum iir1_inp1_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG1, 0, 18, + iir_inp_mux_text); + +static const struct soc_enum iir1_inp2_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG2, 0, 18, + iir_inp_mux_text); + +static const struct soc_enum iir1_inp3_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG3, 0, 18, + iir_inp_mux_text); + +static const struct soc_enum rx_int0_dem_inp_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX0_RX_PATH_SEC0, 0, + ARRAY_SIZE(rx_int_dem_inp_mux_text), + rx_int_dem_inp_mux_text); + +static const struct soc_enum rx_int1_dem_inp_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX1_RX_PATH_SEC0, 0, + ARRAY_SIZE(rx_int_dem_inp_mux_text), + rx_int_dem_inp_mux_text); + +static const struct soc_enum rx_int2_dem_inp_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX2_RX_PATH_SEC0, 0, + ARRAY_SIZE(rx_int_dem_inp_mux_text), + rx_int_dem_inp_mux_text); + +static const struct soc_enum rx_int0_interp_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX0_RX_PATH_CTL, 5, 2, + rx_int0_interp_mux_text); + +static const struct soc_enum rx_int1_interp_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX1_RX_PATH_CTL, 5, 2, + rx_int1_interp_mux_text); + +static const struct soc_enum rx_int2_interp_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX2_RX_PATH_CTL, 5, 2, + rx_int2_interp_mux_text); + +static const struct soc_enum rx_int3_interp_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX3_RX_PATH_CTL, 5, 2, + rx_int3_interp_mux_text); + +static const struct soc_enum rx_int4_interp_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX4_RX_PATH_CTL, 5, 2, + rx_int4_interp_mux_text); + +static const struct soc_enum rx_int5_interp_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX5_RX_PATH_CTL, 5, 2, + rx_int5_interp_mux_text); + +static const struct soc_enum rx_int6_interp_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX6_RX_PATH_CTL, 5, 2, + rx_int6_interp_mux_text); + +static const struct soc_enum rx_int7_interp_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX7_RX_PATH_CTL, 5, 2, + rx_int7_interp_mux_text); + +static const struct soc_enum rx_int8_interp_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX8_RX_PATH_CTL, 5, 2, + rx_int8_interp_mux_text); + +static const struct soc_enum mad_sel_enum = + SOC_ENUM_SINGLE(WCD9335_CPE_SS_CFG, 0, 2, mad_sel_text); + +static const struct soc_enum anc0_fb_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_ANC_CFG0, 0, 5, + anc0_fb_mux_text); + +static const struct soc_enum anc1_fb_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_ANC_CFG0, 3, 3, + anc1_fb_mux_text); + +static const struct snd_kcontrol_new rx_int0_dem_inp_mux = + SOC_DAPM_ENUM_EXT("RX INT0 DEM MUX Mux", rx_int0_dem_inp_mux_enum, + snd_soc_dapm_get_enum_double, + tasha_int_dem_inp_mux_put); + +static const struct snd_kcontrol_new rx_int1_dem_inp_mux = + SOC_DAPM_ENUM_EXT("RX INT1 DEM MUX Mux", rx_int1_dem_inp_mux_enum, + snd_soc_dapm_get_enum_double, + tasha_int_dem_inp_mux_put); + +static const struct snd_kcontrol_new rx_int2_dem_inp_mux = + SOC_DAPM_ENUM_EXT("RX INT2 DEM MUX Mux", rx_int2_dem_inp_mux_enum, + snd_soc_dapm_get_enum_double, + tasha_int_dem_inp_mux_put); + +static const struct snd_kcontrol_new spl_src0_mux = + SOC_DAPM_ENUM("SPL SRC0 MUX Mux", spl_src0_mux_chain_enum); + +static const struct snd_kcontrol_new spl_src1_mux = + SOC_DAPM_ENUM("SPL SRC1 MUX Mux", spl_src1_mux_chain_enum); + +static const struct snd_kcontrol_new spl_src2_mux = + SOC_DAPM_ENUM("SPL SRC2 MUX Mux", spl_src2_mux_chain_enum); + +static const struct snd_kcontrol_new spl_src3_mux = + SOC_DAPM_ENUM("SPL SRC3 MUX Mux", spl_src3_mux_chain_enum); + +static const struct snd_kcontrol_new rx_int0_2_mux = + SOC_DAPM_ENUM("RX INT0_2 MUX Mux", rx_int0_2_mux_chain_enum); + +static const struct snd_kcontrol_new rx_int1_2_mux = + SOC_DAPM_ENUM("RX INT1_2 MUX Mux", rx_int1_2_mux_chain_enum); + +static const struct snd_kcontrol_new rx_int2_2_mux = + SOC_DAPM_ENUM("RX INT2_2 MUX Mux", rx_int2_2_mux_chain_enum); + +static const struct snd_kcontrol_new rx_int3_2_mux = + SOC_DAPM_ENUM("RX INT3_2 MUX Mux", rx_int3_2_mux_chain_enum); + +static const struct snd_kcontrol_new rx_int4_2_mux = + SOC_DAPM_ENUM("RX INT4_2 MUX Mux", rx_int4_2_mux_chain_enum); + +static const struct snd_kcontrol_new rx_int5_2_mux = + SOC_DAPM_ENUM("RX INT5_2 MUX Mux", rx_int5_2_mux_chain_enum); + +static const struct snd_kcontrol_new rx_int6_2_mux = + SOC_DAPM_ENUM("RX INT6_2 MUX Mux", rx_int6_2_mux_chain_enum); + +static const struct snd_kcontrol_new rx_int7_2_mux = + SOC_DAPM_ENUM("RX INT7_2 MUX Mux", rx_int7_2_mux_chain_enum); + +static const struct snd_kcontrol_new rx_int8_2_mux = + SOC_DAPM_ENUM("RX INT8_2 MUX Mux", rx_int8_2_mux_chain_enum); + +static const struct snd_kcontrol_new int1_1_native_mux = + SOC_DAPM_ENUM("RX INT1_1 NATIVE MUX Mux", int1_1_native_enum); + +static const struct snd_kcontrol_new int2_1_native_mux = + SOC_DAPM_ENUM("RX INT2_1 NATIVE MUX Mux", int2_1_native_enum); + +static const struct snd_kcontrol_new int3_1_native_mux = + SOC_DAPM_ENUM("RX INT3_1 NATIVE MUX Mux", int3_1_native_enum); + +static const struct snd_kcontrol_new int4_1_native_mux = + SOC_DAPM_ENUM("RX INT4_1 NATIVE MUX Mux", int4_1_native_enum); + +static const struct snd_kcontrol_new rx_int0_1_mix_inp0_mux = + SOC_DAPM_ENUM("RX INT0_1 MIX1 INP0 Mux", rx_int0_1_mix_inp0_chain_enum); + +static const struct snd_kcontrol_new rx_int0_1_mix_inp1_mux = + SOC_DAPM_ENUM("RX INT0_1 MIX1 INP1 Mux", rx_int0_1_mix_inp1_chain_enum); + +static const struct snd_kcontrol_new rx_int0_1_mix_inp2_mux = + SOC_DAPM_ENUM("RX INT0_1 MIX1 INP2 Mux", rx_int0_1_mix_inp2_chain_enum); + +static const struct snd_kcontrol_new rx_int1_1_mix_inp0_mux = + SOC_DAPM_ENUM("RX INT1_1 MIX1 INP0 Mux", rx_int1_1_mix_inp0_chain_enum); + +static const struct snd_kcontrol_new rx_int1_1_mix_inp1_mux = + SOC_DAPM_ENUM("RX INT1_1 MIX1 INP1 Mux", rx_int1_1_mix_inp1_chain_enum); + +static const struct snd_kcontrol_new rx_int1_1_mix_inp2_mux = + SOC_DAPM_ENUM("RX INT1_1 MIX1 INP2 Mux", rx_int1_1_mix_inp2_chain_enum); + +static const struct snd_kcontrol_new rx_int2_1_mix_inp0_mux = + SOC_DAPM_ENUM("RX INT2_1 MIX1 INP0 Mux", rx_int2_1_mix_inp0_chain_enum); + +static const struct snd_kcontrol_new rx_int2_1_mix_inp1_mux = + SOC_DAPM_ENUM("RX INT2_1 MIX1 INP1 Mux", rx_int2_1_mix_inp1_chain_enum); + +static const struct snd_kcontrol_new rx_int2_1_mix_inp2_mux = + SOC_DAPM_ENUM("RX INT2_1 MIX1 INP2 Mux", rx_int2_1_mix_inp2_chain_enum); + +static const struct snd_kcontrol_new rx_int3_1_mix_inp0_mux = + SOC_DAPM_ENUM("RX INT3_1 MIX1 INP0 Mux", rx_int3_1_mix_inp0_chain_enum); + +static const struct snd_kcontrol_new rx_int3_1_mix_inp1_mux = + SOC_DAPM_ENUM("RX INT3_1 MIX1 INP1 Mux", rx_int3_1_mix_inp1_chain_enum); + +static const struct snd_kcontrol_new rx_int3_1_mix_inp2_mux = + SOC_DAPM_ENUM("RX INT3_1 MIX1 INP2 Mux", rx_int3_1_mix_inp2_chain_enum); + +static const struct snd_kcontrol_new rx_int4_1_mix_inp0_mux = + SOC_DAPM_ENUM("RX INT4_1 MIX1 INP0 Mux", rx_int4_1_mix_inp0_chain_enum); + +static const struct snd_kcontrol_new rx_int4_1_mix_inp1_mux = + SOC_DAPM_ENUM("RX INT4_1 MIX1 INP1 Mux", rx_int4_1_mix_inp1_chain_enum); + +static const struct snd_kcontrol_new rx_int4_1_mix_inp2_mux = + SOC_DAPM_ENUM("RX INT4_1 MIX1 INP2 Mux", rx_int4_1_mix_inp2_chain_enum); + +static const struct snd_kcontrol_new rx_int5_1_mix_inp0_mux = + SOC_DAPM_ENUM("RX INT5_1 MIX1 INP0 Mux", rx_int5_1_mix_inp0_chain_enum); + +static const struct snd_kcontrol_new rx_int5_1_mix_inp1_mux = + SOC_DAPM_ENUM("RX INT5_1 MIX1 INP1 Mux", rx_int5_1_mix_inp1_chain_enum); + +static const struct snd_kcontrol_new rx_int5_1_mix_inp2_mux = + SOC_DAPM_ENUM("RX INT5_1 MIX1 INP2 Mux", rx_int5_1_mix_inp2_chain_enum); + +static const struct snd_kcontrol_new rx_int6_1_mix_inp0_mux = + SOC_DAPM_ENUM("RX INT6_1 MIX1 INP0 Mux", rx_int6_1_mix_inp0_chain_enum); + +static const struct snd_kcontrol_new rx_int6_1_mix_inp1_mux = + SOC_DAPM_ENUM("RX INT6_1 MIX1 INP1 Mux", rx_int6_1_mix_inp1_chain_enum); + +static const struct snd_kcontrol_new rx_int6_1_mix_inp2_mux = + SOC_DAPM_ENUM("RX INT6_1 MIX1 INP2 Mux", rx_int6_1_mix_inp2_chain_enum); + +static const struct snd_kcontrol_new rx_int7_1_mix_inp0_mux = + SOC_DAPM_ENUM("RX INT7_1 MIX1 INP0 Mux", rx_int7_1_mix_inp0_chain_enum); + +static const struct snd_kcontrol_new rx_int7_1_mix_inp1_mux = + SOC_DAPM_ENUM("RX INT7_1 MIX1 INP1 Mux", rx_int7_1_mix_inp1_chain_enum); + +static const struct snd_kcontrol_new rx_int7_1_mix_inp2_mux = + SOC_DAPM_ENUM("RX INT7_1 MIX1 INP2 Mux", rx_int7_1_mix_inp2_chain_enum); + +static const struct snd_kcontrol_new rx_int8_1_mix_inp0_mux = + SOC_DAPM_ENUM("RX INT8_1 MIX1 INP0 Mux", rx_int8_1_mix_inp0_chain_enum); + +static const struct snd_kcontrol_new rx_int8_1_mix_inp1_mux = + SOC_DAPM_ENUM("RX INT8_1 MIX1 INP1 Mux", rx_int8_1_mix_inp1_chain_enum); + +static const struct snd_kcontrol_new rx_int8_1_mix_inp2_mux = + SOC_DAPM_ENUM("RX INT8_1 MIX1 INP2 Mux", rx_int8_1_mix_inp2_chain_enum); + +static const struct snd_kcontrol_new rx_int0_mix2_inp_mux = + SOC_DAPM_ENUM("RX INT0 MIX2 INP Mux", rx_int0_sidetone_mix_chain_enum); + +static const struct snd_kcontrol_new rx_int1_mix2_inp_mux = + SOC_DAPM_ENUM("RX INT1 MIX2 INP Mux", rx_int1_sidetone_mix_chain_enum); + +static const struct snd_kcontrol_new rx_int2_mix2_inp_mux = + SOC_DAPM_ENUM("RX INT2 MIX2 INP Mux", rx_int2_sidetone_mix_chain_enum); + +static const struct snd_kcontrol_new rx_int3_mix2_inp_mux = + SOC_DAPM_ENUM("RX INT3 MIX2 INP Mux", rx_int3_sidetone_mix_chain_enum); + +static const struct snd_kcontrol_new rx_int4_mix2_inp_mux = + SOC_DAPM_ENUM("RX INT4 MIX2 INP Mux", rx_int4_sidetone_mix_chain_enum); + +static const struct snd_kcontrol_new rx_int7_mix2_inp_mux = + SOC_DAPM_ENUM("RX INT7 MIX2 INP Mux", rx_int7_sidetone_mix_chain_enum); + +static const struct snd_kcontrol_new tx_adc_mux0 = + SOC_DAPM_ENUM_EXT("ADC MUX0 Mux", tx_adc_mux0_chain_enum, + snd_soc_dapm_get_enum_double, + tasha_put_dec_enum); + +static const struct snd_kcontrol_new tx_adc_mux1 = + SOC_DAPM_ENUM_EXT("ADC MUX1 Mux", tx_adc_mux1_chain_enum, + snd_soc_dapm_get_enum_double, + tasha_put_dec_enum); + +static const struct snd_kcontrol_new tx_adc_mux2 = + SOC_DAPM_ENUM_EXT("ADC MUX2 Mux", tx_adc_mux2_chain_enum, + snd_soc_dapm_get_enum_double, + tasha_put_dec_enum); + +static const struct snd_kcontrol_new tx_adc_mux3 = + SOC_DAPM_ENUM_EXT("ADC MUX3 Mux", tx_adc_mux3_chain_enum, + snd_soc_dapm_get_enum_double, + tasha_put_dec_enum); + +static const struct snd_kcontrol_new tx_adc_mux4 = + SOC_DAPM_ENUM_EXT("ADC MUX4 Mux", tx_adc_mux4_chain_enum, + snd_soc_dapm_get_enum_double, + tasha_put_dec_enum); + +static const struct snd_kcontrol_new tx_adc_mux5 = + SOC_DAPM_ENUM_EXT("ADC MUX5 Mux", tx_adc_mux5_chain_enum, + snd_soc_dapm_get_enum_double, + tasha_put_dec_enum); + +static const struct snd_kcontrol_new tx_adc_mux6 = + SOC_DAPM_ENUM_EXT("ADC MUX6 Mux", tx_adc_mux6_chain_enum, + snd_soc_dapm_get_enum_double, + tasha_put_dec_enum); + +static const struct snd_kcontrol_new tx_adc_mux7 = + SOC_DAPM_ENUM_EXT("ADC MUX7 Mux", tx_adc_mux7_chain_enum, + snd_soc_dapm_get_enum_double, + tasha_put_dec_enum); + +static const struct snd_kcontrol_new tx_adc_mux8 = + SOC_DAPM_ENUM_EXT("ADC MUX8 Mux", tx_adc_mux8_chain_enum, + snd_soc_dapm_get_enum_double, + tasha_put_dec_enum); + +static const struct snd_kcontrol_new tx_adc_mux10 = + SOC_DAPM_ENUM("ADC MUX10 Mux", tx_adc_mux10_chain_enum); + +static const struct snd_kcontrol_new tx_adc_mux11 = + SOC_DAPM_ENUM("ADC MUX11 Mux", tx_adc_mux11_chain_enum); + +static const struct snd_kcontrol_new tx_adc_mux12 = + SOC_DAPM_ENUM("ADC MUX12 Mux", tx_adc_mux12_chain_enum); + +static const struct snd_kcontrol_new tx_adc_mux13 = + SOC_DAPM_ENUM("ADC MUX13 Mux", tx_adc_mux13_chain_enum); + +static const struct snd_kcontrol_new tx_dmic_mux0 = + SOC_DAPM_ENUM("DMIC MUX0 Mux", tx_dmic_mux0_enum); + +static const struct snd_kcontrol_new tx_dmic_mux1 = + SOC_DAPM_ENUM("DMIC MUX1 Mux", tx_dmic_mux1_enum); + +static const struct snd_kcontrol_new tx_dmic_mux2 = + SOC_DAPM_ENUM("DMIC MUX2 Mux", tx_dmic_mux2_enum); + +static const struct snd_kcontrol_new tx_dmic_mux3 = + SOC_DAPM_ENUM("DMIC MUX3 Mux", tx_dmic_mux3_enum); + +static const struct snd_kcontrol_new tx_dmic_mux4 = + SOC_DAPM_ENUM("DMIC MUX4 Mux", tx_dmic_mux4_enum); + +static const struct snd_kcontrol_new tx_dmic_mux5 = + SOC_DAPM_ENUM("DMIC MUX5 Mux", tx_dmic_mux5_enum); + +static const struct snd_kcontrol_new tx_dmic_mux6 = + SOC_DAPM_ENUM("DMIC MUX6 Mux", tx_dmic_mux6_enum); + +static const struct snd_kcontrol_new tx_dmic_mux7 = + SOC_DAPM_ENUM("DMIC MUX7 Mux", tx_dmic_mux7_enum); + +static const struct snd_kcontrol_new tx_dmic_mux8 = + SOC_DAPM_ENUM("DMIC MUX8 Mux", tx_dmic_mux8_enum); + +static const struct snd_kcontrol_new tx_dmic_mux10 = + SOC_DAPM_ENUM("DMIC MUX10 Mux", tx_dmic_mux10_enum); + +static const struct snd_kcontrol_new tx_dmic_mux11 = + SOC_DAPM_ENUM("DMIC MUX11 Mux", tx_dmic_mux11_enum); + +static const struct snd_kcontrol_new tx_dmic_mux12 = + SOC_DAPM_ENUM("DMIC MUX12 Mux", tx_dmic_mux12_enum); + +static const struct snd_kcontrol_new tx_dmic_mux13 = + SOC_DAPM_ENUM("DMIC MUX13 Mux", tx_dmic_mux13_enum); + +static const struct snd_kcontrol_new tx_amic_mux0 = + SOC_DAPM_ENUM("AMIC MUX0 Mux", tx_amic_mux0_enum); + +static const struct snd_kcontrol_new tx_amic_mux1 = + SOC_DAPM_ENUM("AMIC MUX1 Mux", tx_amic_mux1_enum); + +static const struct snd_kcontrol_new tx_amic_mux2 = + SOC_DAPM_ENUM("AMIC MUX2 Mux", tx_amic_mux2_enum); + +static const struct snd_kcontrol_new tx_amic_mux3 = + SOC_DAPM_ENUM("AMIC MUX3 Mux", tx_amic_mux3_enum); + +static const struct snd_kcontrol_new tx_amic_mux4 = + SOC_DAPM_ENUM("AMIC MUX4 Mux", tx_amic_mux4_enum); + +static const struct snd_kcontrol_new tx_amic_mux5 = + SOC_DAPM_ENUM("AMIC MUX5 Mux", tx_amic_mux5_enum); + +static const struct snd_kcontrol_new tx_amic_mux6 = + SOC_DAPM_ENUM("AMIC MUX6 Mux", tx_amic_mux6_enum); + +static const struct snd_kcontrol_new tx_amic_mux7 = + SOC_DAPM_ENUM("AMIC MUX7 Mux", tx_amic_mux7_enum); + +static const struct snd_kcontrol_new tx_amic_mux8 = + SOC_DAPM_ENUM("AMIC MUX8 Mux", tx_amic_mux8_enum); + +static const struct snd_kcontrol_new tx_amic_mux10 = + SOC_DAPM_ENUM("AMIC MUX10 Mux", tx_amic_mux10_enum); + +static const struct snd_kcontrol_new tx_amic_mux11 = + SOC_DAPM_ENUM("AMIC MUX11 Mux", tx_amic_mux11_enum); + +static const struct snd_kcontrol_new tx_amic_mux12 = + SOC_DAPM_ENUM("AMIC MUX12 Mux", tx_amic_mux12_enum); + +static const struct snd_kcontrol_new tx_amic_mux13 = + SOC_DAPM_ENUM("AMIC MUX13 Mux", tx_amic_mux13_enum); + +static const struct snd_kcontrol_new sb_tx0_mux = + SOC_DAPM_ENUM("SLIM TX0 MUX Mux", sb_tx0_mux_enum); + +static const struct snd_kcontrol_new sb_tx1_mux = + SOC_DAPM_ENUM("SLIM TX1 MUX Mux", sb_tx1_mux_enum); + +static const struct snd_kcontrol_new sb_tx2_mux = + SOC_DAPM_ENUM("SLIM TX2 MUX Mux", sb_tx2_mux_enum); + +static const struct snd_kcontrol_new sb_tx3_mux = + SOC_DAPM_ENUM("SLIM TX3 MUX Mux", sb_tx3_mux_enum); + +static const struct snd_kcontrol_new sb_tx4_mux = + SOC_DAPM_ENUM("SLIM TX4 MUX Mux", sb_tx4_mux_enum); + +static const struct snd_kcontrol_new sb_tx5_mux = + SOC_DAPM_ENUM("SLIM TX5 MUX Mux", sb_tx5_mux_enum); + +static const struct snd_kcontrol_new sb_tx6_mux = + SOC_DAPM_ENUM("SLIM TX6 MUX Mux", sb_tx6_mux_enum); + +static const struct snd_kcontrol_new sb_tx7_mux = + SOC_DAPM_ENUM("SLIM TX7 MUX Mux", sb_tx7_mux_enum); + +static const struct snd_kcontrol_new sb_tx8_mux = + SOC_DAPM_ENUM("SLIM TX8 MUX Mux", sb_tx8_mux_enum); + +static const struct snd_kcontrol_new sb_tx9_mux = + SOC_DAPM_ENUM("SLIM TX9 MUX Mux", sb_tx9_mux_enum); + +static const struct snd_kcontrol_new sb_tx10_mux = + SOC_DAPM_ENUM("SLIM TX10 MUX Mux", sb_tx10_mux_enum); + +static const struct snd_kcontrol_new sb_tx11_mux = + SOC_DAPM_ENUM("SLIM TX11 MUX Mux", sb_tx11_mux_enum); + +static const struct snd_kcontrol_new sb_tx11_inp1_mux = + SOC_DAPM_ENUM("SLIM TX11 INP1 MUX Mux", sb_tx11_inp1_mux_enum); + +static const struct snd_kcontrol_new sb_tx13_mux = + SOC_DAPM_ENUM("SLIM TX13 MUX Mux", sb_tx13_mux_enum); + +static const struct snd_kcontrol_new tx13_inp_mux = + SOC_DAPM_ENUM("TX13 INP MUX Mux", tx13_inp_mux_enum); + +static const struct snd_kcontrol_new rx_mix_tx0_mux = + SOC_DAPM_ENUM("RX MIX TX0 MUX Mux", rx_mix_tx0_mux_enum); + +static const struct snd_kcontrol_new rx_mix_tx1_mux = + SOC_DAPM_ENUM("RX MIX TX1 MUX Mux", rx_mix_tx1_mux_enum); + +static const struct snd_kcontrol_new rx_mix_tx2_mux = + SOC_DAPM_ENUM("RX MIX TX2 MUX Mux", rx_mix_tx2_mux_enum); + +static const struct snd_kcontrol_new rx_mix_tx3_mux = + SOC_DAPM_ENUM("RX MIX TX3 MUX Mux", rx_mix_tx3_mux_enum); + +static const struct snd_kcontrol_new rx_mix_tx4_mux = + SOC_DAPM_ENUM("RX MIX TX4 MUX Mux", rx_mix_tx4_mux_enum); + +static const struct snd_kcontrol_new rx_mix_tx5_mux = + SOC_DAPM_ENUM("RX MIX TX5 MUX Mux", rx_mix_tx5_mux_enum); + +static const struct snd_kcontrol_new rx_mix_tx6_mux = + SOC_DAPM_ENUM("RX MIX TX6 MUX Mux", rx_mix_tx6_mux_enum); + +static const struct snd_kcontrol_new rx_mix_tx7_mux = + SOC_DAPM_ENUM("RX MIX TX7 MUX Mux", rx_mix_tx7_mux_enum); + +static const struct snd_kcontrol_new rx_mix_tx8_mux = + SOC_DAPM_ENUM("RX MIX TX8 MUX Mux", rx_mix_tx8_mux_enum); + +static const struct snd_kcontrol_new iir0_inp0_mux = + SOC_DAPM_ENUM("IIR0 INP0 Mux", iir0_inp0_mux_enum); + +static const struct snd_kcontrol_new iir0_inp1_mux = + SOC_DAPM_ENUM("IIR0 INP1 Mux", iir0_inp1_mux_enum); + +static const struct snd_kcontrol_new iir0_inp2_mux = + SOC_DAPM_ENUM("IIR0 INP2 Mux", iir0_inp2_mux_enum); + +static const struct snd_kcontrol_new iir0_inp3_mux = + SOC_DAPM_ENUM("IIR0 INP3 Mux", iir0_inp3_mux_enum); + +static const struct snd_kcontrol_new iir1_inp0_mux = + SOC_DAPM_ENUM("IIR1 INP0 Mux", iir1_inp0_mux_enum); + +static const struct snd_kcontrol_new iir1_inp1_mux = + SOC_DAPM_ENUM("IIR1 INP1 Mux", iir1_inp1_mux_enum); + +static const struct snd_kcontrol_new iir1_inp2_mux = + SOC_DAPM_ENUM("IIR1 INP2 Mux", iir1_inp2_mux_enum); + +static const struct snd_kcontrol_new iir1_inp3_mux = + SOC_DAPM_ENUM("IIR1 INP3 Mux", iir1_inp3_mux_enum); + +static const struct snd_kcontrol_new rx_int0_interp_mux = + SOC_DAPM_ENUM("RX INT0 INTERP Mux", rx_int0_interp_mux_enum); + +static const struct snd_kcontrol_new rx_int1_interp_mux = + SOC_DAPM_ENUM("RX INT1 INTERP Mux", rx_int1_interp_mux_enum); + +static const struct snd_kcontrol_new rx_int2_interp_mux = + SOC_DAPM_ENUM("RX INT2 INTERP Mux", rx_int2_interp_mux_enum); + +static const struct snd_kcontrol_new rx_int3_interp_mux = + SOC_DAPM_ENUM("RX INT3 INTERP Mux", rx_int3_interp_mux_enum); + +static const struct snd_kcontrol_new rx_int4_interp_mux = + SOC_DAPM_ENUM("RX INT4 INTERP Mux", rx_int4_interp_mux_enum); + +static const struct snd_kcontrol_new rx_int5_interp_mux = + SOC_DAPM_ENUM("RX INT5 INTERP Mux", rx_int5_interp_mux_enum); + +static const struct snd_kcontrol_new rx_int6_interp_mux = + SOC_DAPM_ENUM("RX INT6 INTERP Mux", rx_int6_interp_mux_enum); + +static const struct snd_kcontrol_new rx_int7_interp_mux = + SOC_DAPM_ENUM("RX INT7 INTERP Mux", rx_int7_interp_mux_enum); + +static const struct snd_kcontrol_new rx_int8_interp_mux = + SOC_DAPM_ENUM("RX INT8 INTERP Mux", rx_int8_interp_mux_enum); + +static const struct snd_kcontrol_new mad_sel_mux = + SOC_DAPM_ENUM("MAD_SEL MUX Mux", mad_sel_enum); + +static const struct snd_kcontrol_new aif4_mad_switch = + SOC_DAPM_SINGLE("Switch", WCD9335_CPE_SS_CFG, 5, 1, 0); + +static const struct snd_kcontrol_new mad_brdcst_switch = + SOC_DAPM_SINGLE("Switch", WCD9335_CPE_SS_CFG, 6, 1, 0); + +static const struct snd_kcontrol_new aif4_switch_mixer_controls = + SOC_SINGLE_EXT("Switch", SND_SOC_NOPM, + 0, 1, 0, tasha_codec_aif4_mixer_switch_get, + tasha_codec_aif4_mixer_switch_put); + +static const struct snd_kcontrol_new anc_hphl_switch = + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct snd_kcontrol_new anc_hphr_switch = + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct snd_kcontrol_new anc_ear_switch = + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct snd_kcontrol_new anc_ear_spkr_switch = + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct snd_kcontrol_new anc_lineout1_switch = + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct snd_kcontrol_new anc_lineout2_switch = + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct snd_kcontrol_new anc_spkr_pa_switch = + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct snd_kcontrol_new adc_us_mux0_switch = + SOC_DAPM_SINGLE("US_Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct snd_kcontrol_new adc_us_mux1_switch = + SOC_DAPM_SINGLE("US_Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct snd_kcontrol_new adc_us_mux2_switch = + SOC_DAPM_SINGLE("US_Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct snd_kcontrol_new adc_us_mux3_switch = + SOC_DAPM_SINGLE("US_Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct snd_kcontrol_new adc_us_mux4_switch = + SOC_DAPM_SINGLE("US_Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct snd_kcontrol_new adc_us_mux5_switch = + SOC_DAPM_SINGLE("US_Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct snd_kcontrol_new adc_us_mux6_switch = + SOC_DAPM_SINGLE("US_Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct snd_kcontrol_new adc_us_mux7_switch = + SOC_DAPM_SINGLE("US_Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct snd_kcontrol_new adc_us_mux8_switch = + SOC_DAPM_SINGLE("US_Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct snd_kcontrol_new anc0_fb_mux = + SOC_DAPM_ENUM("ANC0 FB MUX Mux", anc0_fb_mux_enum); + +static const struct snd_kcontrol_new anc1_fb_mux = + SOC_DAPM_ENUM("ANC1 FB MUX Mux", anc1_fb_mux_enum); + +static int tasha_codec_ec_buf_mux_enable(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + + dev_dbg(codec->dev, "%s: event = %d name = %s\n", + __func__, event, w->name); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + snd_soc_write(codec, WCD9335_CPE_SS_EC_BUF_INT_PERIOD, 0x3B); + snd_soc_update_bits(codec, WCD9335_CPE_SS_CFG, 0x08, 0x08); + snd_soc_update_bits(codec, WCD9335_CDC_IF_ROUTER_TX_MUX_CFG0, + 0x08, 0x08); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_update_bits(codec, WCD9335_CDC_IF_ROUTER_TX_MUX_CFG0, + 0x08, 0x00); + snd_soc_update_bits(codec, WCD9335_CPE_SS_CFG, 0x08, 0x00); + snd_soc_write(codec, WCD9335_CPE_SS_EC_BUF_INT_PERIOD, 0x00); + break; + } + + return 0; +}; + +static const char * const ec_buf_mux_text[] = { + "ZERO", "RXMIXEC", "SB_RX0", "SB_RX1", "SB_RX2", "SB_RX3", + "I2S_RX_SD0_L", "I2S_RX_SD0_R", "I2S_RX_SD1_L", "I2S_RX_SD1_R", + "DEC1" +}; + +static SOC_ENUM_SINGLE_DECL(ec_buf_mux_enum, WCD9335_CPE_SS_US_EC_MUX_CFG, + 0, ec_buf_mux_text); + +static const struct snd_kcontrol_new ec_buf_mux = + SOC_DAPM_ENUM("EC BUF Mux", ec_buf_mux_enum); + +static const struct snd_soc_dapm_widget tasha_dapm_widgets[] = { + SND_SOC_DAPM_OUTPUT("EAR"), + SND_SOC_DAPM_OUTPUT("ANC EAR"), + SND_SOC_DAPM_AIF_IN_E("AIF1 PB", "AIF1 Playback", 0, SND_SOC_NOPM, + AIF1_PB, 0, tasha_codec_enable_slimrx, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_AIF_IN_E("AIF2 PB", "AIF2 Playback", 0, SND_SOC_NOPM, + AIF2_PB, 0, tasha_codec_enable_slimrx, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_AIF_IN_E("AIF3 PB", "AIF3 Playback", 0, SND_SOC_NOPM, + AIF3_PB, 0, tasha_codec_enable_slimrx, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_AIF_IN_E("AIF4 PB", "AIF4 Playback", 0, SND_SOC_NOPM, + AIF4_PB, 0, tasha_codec_enable_slimrx, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_AIF_IN_E("AIF MIX1 PB", "AIF Mix Playback", 0, + SND_SOC_NOPM, AIF_MIX1_PB, 0, + tasha_codec_enable_slimrx, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX("SLIM RX0 MUX", SND_SOC_NOPM, TASHA_RX0, 0, + &slim_rx_mux[TASHA_RX0]), + SND_SOC_DAPM_MUX("SLIM RX1 MUX", SND_SOC_NOPM, TASHA_RX1, 0, + &slim_rx_mux[TASHA_RX1]), + SND_SOC_DAPM_MUX("SLIM RX2 MUX", SND_SOC_NOPM, TASHA_RX2, 0, + &slim_rx_mux[TASHA_RX2]), + SND_SOC_DAPM_MUX("SLIM RX3 MUX", SND_SOC_NOPM, TASHA_RX3, 0, + &slim_rx_mux[TASHA_RX3]), + SND_SOC_DAPM_MUX("SLIM RX4 MUX", SND_SOC_NOPM, TASHA_RX4, 0, + &slim_rx_mux[TASHA_RX4]), + SND_SOC_DAPM_MUX("SLIM RX5 MUX", SND_SOC_NOPM, TASHA_RX5, 0, + &slim_rx_mux[TASHA_RX5]), + SND_SOC_DAPM_MUX("SLIM RX6 MUX", SND_SOC_NOPM, TASHA_RX6, 0, + &slim_rx_mux[TASHA_RX6]), + SND_SOC_DAPM_MUX("SLIM RX7 MUX", SND_SOC_NOPM, TASHA_RX7, 0, + &slim_rx_mux[TASHA_RX7]), + + SND_SOC_DAPM_MIXER("SLIM RX0", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM RX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM RX2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM RX3", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM RX4", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM RX5", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM RX6", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM RX7", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_MUX_E("SPL SRC0 MUX", SND_SOC_NOPM, SPLINE_SRC0, 0, + &spl_src0_mux, tasha_codec_enable_spline_resampler, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("SPL SRC1 MUX", SND_SOC_NOPM, SPLINE_SRC1, 0, + &spl_src1_mux, tasha_codec_enable_spline_resampler, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("SPL SRC2 MUX", SND_SOC_NOPM, SPLINE_SRC2, 0, + &spl_src2_mux, tasha_codec_enable_spline_resampler, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("SPL SRC3 MUX", SND_SOC_NOPM, SPLINE_SRC3, 0, + &spl_src3_mux, tasha_codec_enable_spline_resampler, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX_E("RX INT0_2 MUX", WCD9335_CDC_RX0_RX_PATH_MIX_CTL, + 5, 0, &rx_int0_2_mux, tasha_codec_enable_mix_path, + SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_MUX_E("RX INT1_2 MUX", WCD9335_CDC_RX1_RX_PATH_MIX_CTL, + 5, 0, &rx_int1_2_mux, tasha_codec_enable_mix_path, + SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_MUX_E("RX INT2_2 MUX", WCD9335_CDC_RX2_RX_PATH_MIX_CTL, + 5, 0, &rx_int2_2_mux, tasha_codec_enable_mix_path, + SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_MUX_E("RX INT3_2 MUX", WCD9335_CDC_RX3_RX_PATH_MIX_CTL, + 5, 0, &rx_int3_2_mux, tasha_codec_enable_mix_path, + SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_MUX_E("RX INT4_2 MUX", WCD9335_CDC_RX4_RX_PATH_MIX_CTL, + 5, 0, &rx_int4_2_mux, tasha_codec_enable_mix_path, + SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_MUX_E("RX INT5_2 MUX", WCD9335_CDC_RX5_RX_PATH_MIX_CTL, + 5, 0, &rx_int5_2_mux, tasha_codec_enable_mix_path, + SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_MUX_E("RX INT6_2 MUX", WCD9335_CDC_RX6_RX_PATH_MIX_CTL, + 5, 0, &rx_int6_2_mux, tasha_codec_enable_mix_path, + SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_MUX_E("RX INT7_2 MUX", WCD9335_CDC_RX7_RX_PATH_MIX_CTL, + 5, 0, &rx_int7_2_mux, tasha_codec_enable_mix_path, + SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_MUX_E("RX INT8_2 MUX", WCD9335_CDC_RX8_RX_PATH_MIX_CTL, + 5, 0, &rx_int8_2_mux, tasha_codec_enable_mix_path, + SND_SOC_DAPM_POST_PMU), + + SND_SOC_DAPM_MUX("RX INT0_1 MIX1 INP0", SND_SOC_NOPM, 0, 0, + &rx_int0_1_mix_inp0_mux), + SND_SOC_DAPM_MUX("RX INT0_1 MIX1 INP1", SND_SOC_NOPM, 0, 0, + &rx_int0_1_mix_inp1_mux), + SND_SOC_DAPM_MUX("RX INT0_1 MIX1 INP2", SND_SOC_NOPM, 0, 0, + &rx_int0_1_mix_inp2_mux), + SND_SOC_DAPM_MUX("RX INT1_1 MIX1 INP0", SND_SOC_NOPM, 0, 0, + &rx_int1_1_mix_inp0_mux), + SND_SOC_DAPM_MUX("RX INT1_1 MIX1 INP1", SND_SOC_NOPM, 0, 0, + &rx_int1_1_mix_inp1_mux), + SND_SOC_DAPM_MUX("RX INT1_1 MIX1 INP2", SND_SOC_NOPM, 0, 0, + &rx_int1_1_mix_inp2_mux), + SND_SOC_DAPM_MUX("RX INT2_1 MIX1 INP0", SND_SOC_NOPM, 0, 0, + &rx_int2_1_mix_inp0_mux), + SND_SOC_DAPM_MUX("RX INT2_1 MIX1 INP1", SND_SOC_NOPM, 0, 0, + &rx_int2_1_mix_inp1_mux), + SND_SOC_DAPM_MUX("RX INT2_1 MIX1 INP2", SND_SOC_NOPM, 0, 0, + &rx_int2_1_mix_inp2_mux), + SND_SOC_DAPM_MUX("RX INT3_1 MIX1 INP0", SND_SOC_NOPM, 0, 0, + &rx_int3_1_mix_inp0_mux), + SND_SOC_DAPM_MUX("RX INT3_1 MIX1 INP1", SND_SOC_NOPM, 0, 0, + &rx_int3_1_mix_inp1_mux), + SND_SOC_DAPM_MUX("RX INT3_1 MIX1 INP2", SND_SOC_NOPM, 0, 0, + &rx_int3_1_mix_inp2_mux), + SND_SOC_DAPM_MUX("RX INT4_1 MIX1 INP0", SND_SOC_NOPM, 0, 0, + &rx_int4_1_mix_inp0_mux), + SND_SOC_DAPM_MUX("RX INT4_1 MIX1 INP1", SND_SOC_NOPM, 0, 0, + &rx_int4_1_mix_inp1_mux), + SND_SOC_DAPM_MUX("RX INT4_1 MIX1 INP2", SND_SOC_NOPM, 0, 0, + &rx_int4_1_mix_inp2_mux), + SND_SOC_DAPM_MUX("RX INT5_1 MIX1 INP0", SND_SOC_NOPM, 0, 0, + &rx_int5_1_mix_inp0_mux), + SND_SOC_DAPM_MUX("RX INT5_1 MIX1 INP1", SND_SOC_NOPM, 0, 0, + &rx_int5_1_mix_inp1_mux), + SND_SOC_DAPM_MUX("RX INT5_1 MIX1 INP2", SND_SOC_NOPM, 0, 0, + &rx_int5_1_mix_inp2_mux), + SND_SOC_DAPM_MUX("RX INT6_1 MIX1 INP0", SND_SOC_NOPM, 0, 0, + &rx_int6_1_mix_inp0_mux), + SND_SOC_DAPM_MUX("RX INT6_1 MIX1 INP1", SND_SOC_NOPM, 0, 0, + &rx_int6_1_mix_inp1_mux), + SND_SOC_DAPM_MUX("RX INT6_1 MIX1 INP2", SND_SOC_NOPM, 0, 0, + &rx_int6_1_mix_inp2_mux), + SND_SOC_DAPM_MUX_E("RX INT7_1 MIX1 INP0", SND_SOC_NOPM, 0, 0, + &rx_int7_1_mix_inp0_mux, tasha_codec_enable_swr, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT7_1 MIX1 INP1", SND_SOC_NOPM, 0, 0, + &rx_int7_1_mix_inp1_mux, tasha_codec_enable_swr, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT7_1 MIX1 INP2", SND_SOC_NOPM, 0, 0, + &rx_int7_1_mix_inp2_mux, tasha_codec_enable_swr, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT8_1 MIX1 INP0", SND_SOC_NOPM, 0, 0, + &rx_int8_1_mix_inp0_mux, tasha_codec_enable_swr, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT8_1 MIX1 INP1", SND_SOC_NOPM, 0, 0, + &rx_int8_1_mix_inp1_mux, tasha_codec_enable_swr, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT8_1 MIX1 INP2", SND_SOC_NOPM, 0, 0, + &rx_int8_1_mix_inp2_mux, tasha_codec_enable_swr, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MIXER("RX INT0_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT0 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT1_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT1 SPLINE MIX", SND_SOC_NOPM, 0, 0, + rx_int1_spline_mix_switch, + ARRAY_SIZE(rx_int1_spline_mix_switch)), + SND_SOC_DAPM_MIXER("RX INT1 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT2_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT2 SPLINE MIX", SND_SOC_NOPM, 0, 0, + rx_int2_spline_mix_switch, + ARRAY_SIZE(rx_int2_spline_mix_switch)), + SND_SOC_DAPM_MIXER("RX INT2 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT3_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT3 SPLINE MIX", SND_SOC_NOPM, 0, 0, + rx_int3_spline_mix_switch, + ARRAY_SIZE(rx_int3_spline_mix_switch)), + SND_SOC_DAPM_MIXER("RX INT3 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT4_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT4 SPLINE MIX", SND_SOC_NOPM, 0, 0, + rx_int4_spline_mix_switch, + ARRAY_SIZE(rx_int4_spline_mix_switch)), + SND_SOC_DAPM_MIXER("RX INT4 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT5_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT5 SPLINE MIX", SND_SOC_NOPM, 0, 0, + rx_int5_spline_mix_switch, + ARRAY_SIZE(rx_int5_spline_mix_switch)), + SND_SOC_DAPM_MIXER("RX INT5 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_MIXER("RX INT6_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT6 SPLINE MIX", SND_SOC_NOPM, 0, 0, + rx_int6_spline_mix_switch, + ARRAY_SIZE(rx_int6_spline_mix_switch)), + SND_SOC_DAPM_MIXER("RX INT6 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_MIXER("RX INT7_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT7 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT7 SPLINE MIX", SND_SOC_NOPM, 0, 0, + rx_int7_spline_mix_switch, + ARRAY_SIZE(rx_int7_spline_mix_switch)), + + SND_SOC_DAPM_MIXER("RX INT8_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT8 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT8 SPLINE MIX", SND_SOC_NOPM, 0, 0, + rx_int8_spline_mix_switch, + ARRAY_SIZE(rx_int8_spline_mix_switch)), + + SND_SOC_DAPM_MIXER("RX INT0 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT1 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT2 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT3 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT4 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT5 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT6 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT7 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER_E("RX INT7 CHAIN", SND_SOC_NOPM, 0, 0, + NULL, 0, tasha_codec_spk_boost_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("RX INT8 CHAIN", SND_SOC_NOPM, 0, 0, + NULL, 0, tasha_codec_spk_boost_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MIXER_E("RX INT5 VBAT", SND_SOC_NOPM, 0, 0, + rx_int5_vbat_mix_switch, + ARRAY_SIZE(rx_int5_vbat_mix_switch), + tasha_codec_vbat_enable_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("RX INT6 VBAT", SND_SOC_NOPM, 0, 0, + rx_int6_vbat_mix_switch, + ARRAY_SIZE(rx_int6_vbat_mix_switch), + tasha_codec_vbat_enable_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("RX INT7 VBAT", SND_SOC_NOPM, 0, 0, + rx_int7_vbat_mix_switch, + ARRAY_SIZE(rx_int7_vbat_mix_switch), + tasha_codec_vbat_enable_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("RX INT8 VBAT", SND_SOC_NOPM, 0, 0, + rx_int8_vbat_mix_switch, + ARRAY_SIZE(rx_int8_vbat_mix_switch), + tasha_codec_vbat_enable_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX("RX INT0 MIX2 INP", WCD9335_CDC_RX0_RX_PATH_CFG1, 4, + 0, &rx_int0_mix2_inp_mux), + SND_SOC_DAPM_MUX("RX INT1 MIX2 INP", WCD9335_CDC_RX1_RX_PATH_CFG1, 4, + 0, &rx_int1_mix2_inp_mux), + SND_SOC_DAPM_MUX("RX INT2 MIX2 INP", WCD9335_CDC_RX2_RX_PATH_CFG1, 4, + 0, &rx_int2_mix2_inp_mux), + SND_SOC_DAPM_MUX("RX INT3 MIX2 INP", WCD9335_CDC_RX3_RX_PATH_CFG1, 4, + 0, &rx_int3_mix2_inp_mux), + SND_SOC_DAPM_MUX("RX INT4 MIX2 INP", WCD9335_CDC_RX4_RX_PATH_CFG1, 4, + 0, &rx_int4_mix2_inp_mux), + SND_SOC_DAPM_MUX("RX INT7 MIX2 INP", WCD9335_CDC_RX7_RX_PATH_CFG1, 4, + 0, &rx_int7_mix2_inp_mux), + + SND_SOC_DAPM_MUX("SLIM TX0 MUX", SND_SOC_NOPM, TASHA_TX0, 0, + &sb_tx0_mux), + SND_SOC_DAPM_MUX("SLIM TX1 MUX", SND_SOC_NOPM, TASHA_TX1, 0, + &sb_tx1_mux), + SND_SOC_DAPM_MUX("SLIM TX2 MUX", SND_SOC_NOPM, TASHA_TX2, 0, + &sb_tx2_mux), + SND_SOC_DAPM_MUX("SLIM TX3 MUX", SND_SOC_NOPM, TASHA_TX3, 0, + &sb_tx3_mux), + SND_SOC_DAPM_MUX("SLIM TX4 MUX", SND_SOC_NOPM, TASHA_TX4, 0, + &sb_tx4_mux), + SND_SOC_DAPM_MUX("SLIM TX5 MUX", SND_SOC_NOPM, TASHA_TX5, 0, + &sb_tx5_mux), + SND_SOC_DAPM_MUX("SLIM TX6 MUX", SND_SOC_NOPM, TASHA_TX6, 0, + &sb_tx6_mux), + SND_SOC_DAPM_MUX("SLIM TX7 MUX", SND_SOC_NOPM, TASHA_TX7, 0, + &sb_tx7_mux), + SND_SOC_DAPM_MUX("SLIM TX8 MUX", SND_SOC_NOPM, TASHA_TX8, 0, + &sb_tx8_mux), + SND_SOC_DAPM_MUX("SLIM TX9 MUX", SND_SOC_NOPM, TASHA_TX9, 0, + &sb_tx9_mux), + SND_SOC_DAPM_MUX("SLIM TX10 MUX", SND_SOC_NOPM, TASHA_TX10, 0, + &sb_tx10_mux), + SND_SOC_DAPM_MUX("SLIM TX11 MUX", SND_SOC_NOPM, TASHA_TX11, 0, + &sb_tx11_mux), + SND_SOC_DAPM_MUX("SLIM TX11 INP1 MUX", SND_SOC_NOPM, TASHA_TX11, 0, + &sb_tx11_inp1_mux), + SND_SOC_DAPM_MUX("SLIM TX13 MUX", SND_SOC_NOPM, TASHA_TX13, 0, + &sb_tx13_mux), + SND_SOC_DAPM_MUX("TX13 INP MUX", SND_SOC_NOPM, 0, 0, + &tx13_inp_mux), + + SND_SOC_DAPM_MUX_E("ADC MUX0", WCD9335_CDC_TX0_TX_PATH_CTL, 5, 0, + &tx_adc_mux0, tasha_codec_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX_E("ADC MUX1", WCD9335_CDC_TX1_TX_PATH_CTL, 5, 0, + &tx_adc_mux1, tasha_codec_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX_E("ADC MUX2", WCD9335_CDC_TX2_TX_PATH_CTL, 5, 0, + &tx_adc_mux2, tasha_codec_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX_E("ADC MUX3", WCD9335_CDC_TX3_TX_PATH_CTL, 5, 0, + &tx_adc_mux3, tasha_codec_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX_E("ADC MUX4", WCD9335_CDC_TX4_TX_PATH_CTL, 5, 0, + &tx_adc_mux4, tasha_codec_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX_E("ADC MUX5", WCD9335_CDC_TX5_TX_PATH_CTL, 5, 0, + &tx_adc_mux5, tasha_codec_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX_E("ADC MUX6", WCD9335_CDC_TX6_TX_PATH_CTL, 5, 0, + &tx_adc_mux6, tasha_codec_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX_E("ADC MUX7", WCD9335_CDC_TX7_TX_PATH_CTL, 5, 0, + &tx_adc_mux7, tasha_codec_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX_E("ADC MUX8", WCD9335_CDC_TX8_TX_PATH_CTL, 5, 0, + &tx_adc_mux8, tasha_codec_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX_E("ADC MUX10", SND_SOC_NOPM, 10, 0, + &tx_adc_mux10, tasha_codec_tx_adc_cfg, + SND_SOC_DAPM_POST_PMU), + + SND_SOC_DAPM_MUX_E("ADC MUX11", SND_SOC_NOPM, 11, 0, + &tx_adc_mux11, tasha_codec_tx_adc_cfg, + SND_SOC_DAPM_POST_PMU), + + SND_SOC_DAPM_MUX_E("ADC MUX12", SND_SOC_NOPM, 12, 0, + &tx_adc_mux12, tasha_codec_tx_adc_cfg, + SND_SOC_DAPM_POST_PMU), + + SND_SOC_DAPM_MUX_E("ADC MUX13", SND_SOC_NOPM, 13, 0, + &tx_adc_mux13, tasha_codec_tx_adc_cfg, + SND_SOC_DAPM_POST_PMU), + + SND_SOC_DAPM_MUX("DMIC MUX0", SND_SOC_NOPM, 0, 0, + &tx_dmic_mux0), + SND_SOC_DAPM_MUX("DMIC MUX1", SND_SOC_NOPM, 0, 0, + &tx_dmic_mux1), + SND_SOC_DAPM_MUX("DMIC MUX2", SND_SOC_NOPM, 0, 0, + &tx_dmic_mux2), + SND_SOC_DAPM_MUX("DMIC MUX3", SND_SOC_NOPM, 0, 0, + &tx_dmic_mux3), + SND_SOC_DAPM_MUX("DMIC MUX4", SND_SOC_NOPM, 0, 0, + &tx_dmic_mux4), + SND_SOC_DAPM_MUX("DMIC MUX5", SND_SOC_NOPM, 0, 0, + &tx_dmic_mux5), + SND_SOC_DAPM_MUX("DMIC MUX6", SND_SOC_NOPM, 0, 0, + &tx_dmic_mux6), + SND_SOC_DAPM_MUX("DMIC MUX7", SND_SOC_NOPM, 0, 0, + &tx_dmic_mux7), + SND_SOC_DAPM_MUX("DMIC MUX8", SND_SOC_NOPM, 0, 0, + &tx_dmic_mux8), + SND_SOC_DAPM_MUX("DMIC MUX10", SND_SOC_NOPM, 0, 0, + &tx_dmic_mux10), + SND_SOC_DAPM_MUX("DMIC MUX11", SND_SOC_NOPM, 0, 0, + &tx_dmic_mux11), + SND_SOC_DAPM_MUX("DMIC MUX12", SND_SOC_NOPM, 0, 0, + &tx_dmic_mux12), + SND_SOC_DAPM_MUX("DMIC MUX13", SND_SOC_NOPM, 0, 0, + &tx_dmic_mux13), + + SND_SOC_DAPM_MUX("AMIC MUX0", SND_SOC_NOPM, 0, 0, + &tx_amic_mux0), + SND_SOC_DAPM_MUX("AMIC MUX1", SND_SOC_NOPM, 0, 0, + &tx_amic_mux1), + SND_SOC_DAPM_MUX("AMIC MUX2", SND_SOC_NOPM, 0, 0, + &tx_amic_mux2), + SND_SOC_DAPM_MUX("AMIC MUX3", SND_SOC_NOPM, 0, 0, + &tx_amic_mux3), + SND_SOC_DAPM_MUX("AMIC MUX4", SND_SOC_NOPM, 0, 0, + &tx_amic_mux4), + SND_SOC_DAPM_MUX("AMIC MUX5", SND_SOC_NOPM, 0, 0, + &tx_amic_mux5), + SND_SOC_DAPM_MUX("AMIC MUX6", SND_SOC_NOPM, 0, 0, + &tx_amic_mux6), + SND_SOC_DAPM_MUX("AMIC MUX7", SND_SOC_NOPM, 0, 0, + &tx_amic_mux7), + SND_SOC_DAPM_MUX("AMIC MUX8", SND_SOC_NOPM, 0, 0, + &tx_amic_mux8), + SND_SOC_DAPM_MUX("AMIC MUX10", SND_SOC_NOPM, 0, 0, + &tx_amic_mux10), + SND_SOC_DAPM_MUX("AMIC MUX11", SND_SOC_NOPM, 0, 0, + &tx_amic_mux11), + SND_SOC_DAPM_MUX("AMIC MUX12", SND_SOC_NOPM, 0, 0, + &tx_amic_mux12), + SND_SOC_DAPM_MUX("AMIC MUX13", SND_SOC_NOPM, 0, 0, + &tx_amic_mux13), + + SND_SOC_DAPM_ADC_E("ADC1", NULL, WCD9335_ANA_AMIC1, 7, 0, + tasha_codec_enable_adc, SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_ADC_E("ADC2", NULL, WCD9335_ANA_AMIC2, 7, 0, + tasha_codec_enable_adc, SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_ADC_E("ADC3", NULL, WCD9335_ANA_AMIC3, 7, 0, + tasha_codec_enable_adc, SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_ADC_E("ADC4", NULL, WCD9335_ANA_AMIC4, 7, 0, + tasha_codec_enable_adc, SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_ADC_E("ADC5", NULL, WCD9335_ANA_AMIC5, 7, 0, + tasha_codec_enable_adc, SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_ADC_E("ADC6", NULL, WCD9335_ANA_AMIC6, 7, 0, + tasha_codec_enable_adc, SND_SOC_DAPM_PRE_PMU), + + SND_SOC_DAPM_SUPPLY("RX INT1 NATIVE SUPPLY", SND_SOC_NOPM, + INTERP_HPHL, 0, tasha_enable_native_supply, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), + + SND_SOC_DAPM_SUPPLY("RX INT2 NATIVE SUPPLY", SND_SOC_NOPM, + INTERP_HPHR, 0, tasha_enable_native_supply, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), + + SND_SOC_DAPM_SUPPLY("RX INT3 NATIVE SUPPLY", SND_SOC_NOPM, + INTERP_LO1, 0, tasha_enable_native_supply, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), + + SND_SOC_DAPM_SUPPLY("RX INT4 NATIVE SUPPLY", SND_SOC_NOPM, + INTERP_LO2, 0, tasha_enable_native_supply, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), + + SND_SOC_DAPM_INPUT("AMIC1"), + SND_SOC_DAPM_MICBIAS_E("MIC BIAS1", SND_SOC_NOPM, 0, 0, + tasha_codec_enable_micbias, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MICBIAS_E("MIC BIAS2", SND_SOC_NOPM, 0, 0, + tasha_codec_enable_micbias, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MICBIAS_E("MIC BIAS3", SND_SOC_NOPM, 0, 0, + tasha_codec_enable_micbias, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MICBIAS_E("MIC BIAS4", SND_SOC_NOPM, 0, 0, + tasha_codec_enable_micbias, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MICBIAS_E(DAPM_MICBIAS1_STANDALONE, SND_SOC_NOPM, 0, 0, + tasha_codec_force_enable_micbias, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MICBIAS_E(DAPM_MICBIAS2_STANDALONE, SND_SOC_NOPM, 0, 0, + tasha_codec_force_enable_micbias, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MICBIAS_E(DAPM_MICBIAS3_STANDALONE, SND_SOC_NOPM, 0, 0, + tasha_codec_force_enable_micbias, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MICBIAS_E(DAPM_MICBIAS4_STANDALONE, SND_SOC_NOPM, 0, 0, + tasha_codec_force_enable_micbias, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY(DAPM_LDO_H_STANDALONE, SND_SOC_NOPM, 0, 0, + tasha_codec_force_enable_ldo_h, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX("ANC0 FB MUX", SND_SOC_NOPM, 0, 0, &anc0_fb_mux), + SND_SOC_DAPM_MUX("ANC1 FB MUX", SND_SOC_NOPM, 0, 0, &anc1_fb_mux), + + SND_SOC_DAPM_INPUT("AMIC2"), + SND_SOC_DAPM_INPUT("AMIC3"), + SND_SOC_DAPM_INPUT("AMIC4"), + SND_SOC_DAPM_INPUT("AMIC5"), + SND_SOC_DAPM_INPUT("AMIC6"), + + SND_SOC_DAPM_AIF_OUT_E("AIF1 CAP", "AIF1 Capture", 0, SND_SOC_NOPM, + AIF1_CAP, 0, tasha_codec_enable_slimtx, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_AIF_OUT_E("AIF2 CAP", "AIF2 Capture", 0, SND_SOC_NOPM, + AIF2_CAP, 0, tasha_codec_enable_slimtx, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_AIF_OUT_E("AIF3 CAP", "AIF3 Capture", 0, SND_SOC_NOPM, + AIF3_CAP, 0, tasha_codec_enable_slimtx, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_AIF_OUT_E("AIF4 VI", "VIfeed", 0, SND_SOC_NOPM, + AIF4_VIFEED, 0, tasha_codec_enable_slimvi_feedback, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER("AIF4_VI Mixer", SND_SOC_NOPM, AIF4_VIFEED, 0, + aif4_vi_mixer, ARRAY_SIZE(aif4_vi_mixer)), + + SND_SOC_DAPM_MIXER("AIF1_CAP Mixer", SND_SOC_NOPM, AIF1_CAP, 0, + aif1_cap_mixer, ARRAY_SIZE(aif1_cap_mixer)), + + SND_SOC_DAPM_MIXER("AIF2_CAP Mixer", SND_SOC_NOPM, AIF2_CAP, 0, + aif2_cap_mixer, ARRAY_SIZE(aif2_cap_mixer)), + + SND_SOC_DAPM_MIXER("AIF3_CAP Mixer", SND_SOC_NOPM, AIF3_CAP, 0, + aif3_cap_mixer, ARRAY_SIZE(aif3_cap_mixer)), + + SND_SOC_DAPM_MIXER("AIF4_MAD Mixer", SND_SOC_NOPM, AIF4_MAD_TX, 0, + aif4_mad_mixer, ARRAY_SIZE(aif4_mad_mixer)), + + SND_SOC_DAPM_INPUT("VIINPUT"), + + SND_SOC_DAPM_AIF_OUT("AIF5 CPE", "AIF5 CPE TX", 0, SND_SOC_NOPM, + AIF5_CPE_TX, 0), + + SND_SOC_DAPM_MUX_E("EC BUF MUX INP", SND_SOC_NOPM, 0, 0, &ec_buf_mux, + tasha_codec_ec_buf_mux_enable, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + /* Digital Mic Inputs */ + SND_SOC_DAPM_ADC_E("DMIC0", NULL, SND_SOC_NOPM, 0, 0, + tasha_codec_enable_dmic, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_ADC_E("DMIC1", NULL, SND_SOC_NOPM, 0, 0, + tasha_codec_enable_dmic, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_ADC_E("DMIC2", NULL, SND_SOC_NOPM, 0, 0, + tasha_codec_enable_dmic, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_ADC_E("DMIC3", NULL, SND_SOC_NOPM, 0, 0, + tasha_codec_enable_dmic, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_ADC_E("DMIC4", NULL, SND_SOC_NOPM, 0, 0, + tasha_codec_enable_dmic, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_ADC_E("DMIC5", NULL, SND_SOC_NOPM, 0, 0, + tasha_codec_enable_dmic, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX("IIR0 INP0 MUX", SND_SOC_NOPM, 0, 0, &iir0_inp0_mux), + SND_SOC_DAPM_MUX("IIR0 INP1 MUX", SND_SOC_NOPM, 0, 0, &iir0_inp1_mux), + SND_SOC_DAPM_MUX("IIR0 INP2 MUX", SND_SOC_NOPM, 0, 0, &iir0_inp2_mux), + SND_SOC_DAPM_MUX("IIR0 INP3 MUX", SND_SOC_NOPM, 0, 0, &iir0_inp3_mux), + SND_SOC_DAPM_MUX("IIR1 INP0 MUX", SND_SOC_NOPM, 0, 0, &iir1_inp0_mux), + SND_SOC_DAPM_MUX("IIR1 INP1 MUX", SND_SOC_NOPM, 0, 0, &iir1_inp1_mux), + SND_SOC_DAPM_MUX("IIR1 INP2 MUX", SND_SOC_NOPM, 0, 0, &iir1_inp2_mux), + SND_SOC_DAPM_MUX("IIR1 INP3 MUX", SND_SOC_NOPM, 0, 0, &iir1_inp3_mux), + + SND_SOC_DAPM_MIXER_E("IIR0", WCD9335_CDC_SIDETONE_IIR0_IIR_PATH_CTL, + 4, 0, NULL, 0, tasha_codec_set_iir_gain, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_MIXER_E("IIR1", WCD9335_CDC_SIDETONE_IIR1_IIR_PATH_CTL, + 4, 0, NULL, 0, tasha_codec_set_iir_gain, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_MIXER("SRC0", WCD9335_CDC_SIDETONE_SRC0_ST_SRC_PATH_CTL, + 4, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SRC1", WCD9335_CDC_SIDETONE_SRC1_ST_SRC_PATH_CTL, + 4, 0, NULL, 0), + SND_SOC_DAPM_MIXER_E("CPE IN Mixer", SND_SOC_NOPM, 0, 0, + cpe_in_mix_switch, + ARRAY_SIZE(cpe_in_mix_switch), + tasha_codec_configure_cpe_input, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX("RX INT1_1 NATIVE MUX", SND_SOC_NOPM, 0, 0, + &int1_1_native_mux), + SND_SOC_DAPM_MUX("RX INT2_1 NATIVE MUX", SND_SOC_NOPM, 0, 0, + &int2_1_native_mux), + SND_SOC_DAPM_MUX("RX INT3_1 NATIVE MUX", SND_SOC_NOPM, 0, 0, + &int3_1_native_mux), + SND_SOC_DAPM_MUX("RX INT4_1 NATIVE MUX", SND_SOC_NOPM, 0, 0, + &int4_1_native_mux), + SND_SOC_DAPM_MUX("RX MIX TX0 MUX", SND_SOC_NOPM, 0, 0, + &rx_mix_tx0_mux), + SND_SOC_DAPM_MUX("RX MIX TX1 MUX", SND_SOC_NOPM, 0, 0, + &rx_mix_tx1_mux), + SND_SOC_DAPM_MUX("RX MIX TX2 MUX", SND_SOC_NOPM, 0, 0, + &rx_mix_tx2_mux), + SND_SOC_DAPM_MUX("RX MIX TX3 MUX", SND_SOC_NOPM, 0, 0, + &rx_mix_tx3_mux), + SND_SOC_DAPM_MUX("RX MIX TX4 MUX", SND_SOC_NOPM, 0, 0, + &rx_mix_tx4_mux), + SND_SOC_DAPM_MUX("RX MIX TX5 MUX", SND_SOC_NOPM, 0, 0, + &rx_mix_tx5_mux), + SND_SOC_DAPM_MUX("RX MIX TX6 MUX", SND_SOC_NOPM, 0, 0, + &rx_mix_tx6_mux), + SND_SOC_DAPM_MUX("RX MIX TX7 MUX", SND_SOC_NOPM, 0, 0, + &rx_mix_tx7_mux), + SND_SOC_DAPM_MUX("RX MIX TX8 MUX", SND_SOC_NOPM, 0, 0, + &rx_mix_tx8_mux), + + SND_SOC_DAPM_MUX("RX INT0 DEM MUX", SND_SOC_NOPM, 0, 0, + &rx_int0_dem_inp_mux), + SND_SOC_DAPM_MUX("RX INT1 DEM MUX", SND_SOC_NOPM, 0, 0, + &rx_int1_dem_inp_mux), + SND_SOC_DAPM_MUX("RX INT2 DEM MUX", SND_SOC_NOPM, 0, 0, + &rx_int2_dem_inp_mux), + + SND_SOC_DAPM_MUX_E("RX INT0 INTERP", SND_SOC_NOPM, + INTERP_EAR, 0, &rx_int0_interp_mux, + tasha_codec_enable_interpolator, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT1 INTERP", SND_SOC_NOPM, + INTERP_HPHL, 0, &rx_int1_interp_mux, + tasha_codec_enable_interpolator, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT2 INTERP", SND_SOC_NOPM, + INTERP_HPHR, 0, &rx_int2_interp_mux, + tasha_codec_enable_interpolator, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT3 INTERP", SND_SOC_NOPM, + INTERP_LO1, 0, &rx_int3_interp_mux, + tasha_codec_enable_interpolator, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT4 INTERP", SND_SOC_NOPM, + INTERP_LO2, 0, &rx_int4_interp_mux, + tasha_codec_enable_interpolator, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT5 INTERP", SND_SOC_NOPM, + INTERP_LO3, 0, &rx_int5_interp_mux, + tasha_codec_enable_interpolator, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT6 INTERP", SND_SOC_NOPM, + INTERP_LO4, 0, &rx_int6_interp_mux, + tasha_codec_enable_interpolator, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT7 INTERP", SND_SOC_NOPM, + INTERP_SPKR1, 0, &rx_int7_interp_mux, + tasha_codec_enable_interpolator, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT8 INTERP", SND_SOC_NOPM, + INTERP_SPKR2, 0, &rx_int8_interp_mux, + tasha_codec_enable_interpolator, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_DAC_E("RX INT0 DAC", NULL, SND_SOC_NOPM, + 0, 0, tasha_codec_ear_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_DAC_E("RX INT1 DAC", NULL, SND_SOC_NOPM, + 0, 0, tasha_codec_hphl_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_DAC_E("RX INT2 DAC", NULL, SND_SOC_NOPM, + 0, 0, tasha_codec_hphr_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_DAC_E("RX INT3 DAC", NULL, SND_SOC_NOPM, + 0, 0, tasha_codec_lineout_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_DAC_E("RX INT4 DAC", NULL, SND_SOC_NOPM, + 0, 0, tasha_codec_lineout_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_DAC_E("RX INT5 DAC", NULL, SND_SOC_NOPM, + 0, 0, tasha_codec_lineout_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_DAC_E("RX INT6 DAC", NULL, SND_SOC_NOPM, + 0, 0, tasha_codec_lineout_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("HPHL PA", SND_SOC_NOPM, 0, 0, NULL, 0, + tasha_codec_enable_hphl_pa, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("HPHR PA", SND_SOC_NOPM, 0, 0, NULL, 0, + tasha_codec_enable_hphr_pa, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("EAR PA", WCD9335_ANA_EAR, 7, 0, NULL, 0, + tasha_codec_enable_ear_pa, + SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("LINEOUT1 PA", WCD9335_ANA_LO_1_2, 7, 0, NULL, 0, + tasha_codec_enable_lineout_pa, + SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("LINEOUT2 PA", WCD9335_ANA_LO_1_2, 6, 0, NULL, 0, + tasha_codec_enable_lineout_pa, + SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("LINEOUT3 PA", WCD9335_ANA_LO_3_4, 7, 0, NULL, 0, + tasha_codec_enable_lineout_pa, + SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("LINEOUT4 PA", WCD9335_ANA_LO_3_4, 6, 0, NULL, 0, + tasha_codec_enable_lineout_pa, + SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("ANC EAR PA", WCD9335_ANA_EAR, 7, 0, NULL, 0, + tasha_codec_enable_ear_pa, + SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("ANC HPHL PA", SND_SOC_NOPM, 0, 0, NULL, 0, + tasha_codec_enable_hphl_pa, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("ANC HPHR PA", SND_SOC_NOPM, 0, 0, NULL, 0, + tasha_codec_enable_hphr_pa, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("ANC LINEOUT1 PA", WCD9335_ANA_LO_1_2, + 7, 0, NULL, 0, + tasha_codec_enable_lineout_pa, + SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("ANC LINEOUT2 PA", WCD9335_ANA_LO_1_2, + 6, 0, NULL, 0, + tasha_codec_enable_lineout_pa, + SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("ANC SPK1 PA", SND_SOC_NOPM, 0, 0, NULL, 0, + tasha_codec_enable_spk_anc, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_OUTPUT("HPHL"), + SND_SOC_DAPM_OUTPUT("HPHR"), + SND_SOC_DAPM_OUTPUT("ANC HPHL"), + SND_SOC_DAPM_OUTPUT("ANC HPHR"), + SND_SOC_DAPM_SUPPLY("RX_BIAS", SND_SOC_NOPM, 0, 0, + tasha_codec_enable_rx_bias, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_OUTPUT("SPK1 OUT"), + SND_SOC_DAPM_OUTPUT("SPK2 OUT"), + SND_SOC_DAPM_OUTPUT("LINEOUT1"), + SND_SOC_DAPM_OUTPUT("LINEOUT2"), + SND_SOC_DAPM_OUTPUT("LINEOUT3"), + SND_SOC_DAPM_OUTPUT("LINEOUT4"), + SND_SOC_DAPM_OUTPUT("ANC LINEOUT1"), + SND_SOC_DAPM_OUTPUT("ANC LINEOUT2"), + SND_SOC_DAPM_SUPPLY("MICBIAS_REGULATOR", SND_SOC_NOPM, + ON_DEMAND_MICBIAS, 0, + tasha_codec_enable_on_demand_supply, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_SWITCH("ADC US MUX0", WCD9335_CDC_TX0_TX_PATH_192_CTL, 0, + 0, &adc_us_mux0_switch), + SND_SOC_DAPM_SWITCH("ADC US MUX1", WCD9335_CDC_TX1_TX_PATH_192_CTL, 0, + 0, &adc_us_mux1_switch), + SND_SOC_DAPM_SWITCH("ADC US MUX2", WCD9335_CDC_TX2_TX_PATH_192_CTL, 0, + 0, &adc_us_mux2_switch), + SND_SOC_DAPM_SWITCH("ADC US MUX3", WCD9335_CDC_TX3_TX_PATH_192_CTL, 0, + 0, &adc_us_mux3_switch), + SND_SOC_DAPM_SWITCH("ADC US MUX4", WCD9335_CDC_TX4_TX_PATH_192_CTL, 0, + 0, &adc_us_mux4_switch), + SND_SOC_DAPM_SWITCH("ADC US MUX5", WCD9335_CDC_TX5_TX_PATH_192_CTL, 0, + 0, &adc_us_mux5_switch), + SND_SOC_DAPM_SWITCH("ADC US MUX6", WCD9335_CDC_TX6_TX_PATH_192_CTL, 0, + 0, &adc_us_mux6_switch), + SND_SOC_DAPM_SWITCH("ADC US MUX7", WCD9335_CDC_TX7_TX_PATH_192_CTL, 0, + 0, &adc_us_mux7_switch), + SND_SOC_DAPM_SWITCH("ADC US MUX8", WCD9335_CDC_TX8_TX_PATH_192_CTL, 0, + 0, &adc_us_mux8_switch), + /* MAD related widgets */ + SND_SOC_DAPM_AIF_OUT_E("AIF4 MAD", "AIF4 MAD TX", 0, + SND_SOC_NOPM, 0, 0, + tasha_codec_enable_mad, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX("MAD_SEL MUX", SND_SOC_NOPM, 0, 0, + &mad_sel_mux), + SND_SOC_DAPM_INPUT("MAD_CPE_INPUT"), + SND_SOC_DAPM_INPUT("MADINPUT"), + SND_SOC_DAPM_SWITCH("MADONOFF", SND_SOC_NOPM, 0, 0, + &aif4_mad_switch), + SND_SOC_DAPM_SWITCH("MAD_BROADCAST", SND_SOC_NOPM, 0, 0, + &mad_brdcst_switch), + SND_SOC_DAPM_SWITCH("AIF4", SND_SOC_NOPM, 0, 0, + &aif4_switch_mixer_controls), + SND_SOC_DAPM_SWITCH("ANC HPHL Enable", SND_SOC_NOPM, 0, 0, + &anc_hphl_switch), + SND_SOC_DAPM_SWITCH("ANC HPHR Enable", SND_SOC_NOPM, 0, 0, + &anc_hphr_switch), + SND_SOC_DAPM_SWITCH("ANC EAR Enable", SND_SOC_NOPM, 0, 0, + &anc_ear_switch), + SND_SOC_DAPM_SWITCH("ANC OUT EAR SPKR Enable", SND_SOC_NOPM, 0, 0, + &anc_ear_spkr_switch), + SND_SOC_DAPM_SWITCH("ANC LINEOUT1 Enable", SND_SOC_NOPM, 0, 0, + &anc_lineout1_switch), + SND_SOC_DAPM_SWITCH("ANC LINEOUT2 Enable", SND_SOC_NOPM, 0, 0, + &anc_lineout2_switch), + SND_SOC_DAPM_SWITCH("ANC SPKR PA Enable", SND_SOC_NOPM, 0, 0, + &anc_spkr_pa_switch), +}; + +static int tasha_get_channel_map(struct snd_soc_dai *dai, + unsigned int *tx_num, unsigned int *tx_slot, + unsigned int *rx_num, unsigned int *rx_slot) +{ + struct tasha_priv *tasha_p = snd_soc_codec_get_drvdata(dai->codec); + u32 i = 0; + struct wcd9xxx_ch *ch; + + switch (dai->id) { + case AIF1_PB: + case AIF2_PB: + case AIF3_PB: + case AIF4_PB: + case AIF_MIX1_PB: + if (!rx_slot || !rx_num) { + pr_err("%s: Invalid rx_slot %pK or rx_num %pK\n", + __func__, rx_slot, rx_num); + return -EINVAL; + } + list_for_each_entry(ch, &tasha_p->dai[dai->id].wcd9xxx_ch_list, + list) { + pr_debug("%s: slot_num %u ch->ch_num %d\n", + __func__, i, ch->ch_num); + rx_slot[i++] = ch->ch_num; + } + pr_debug("%s: rx_num %d\n", __func__, i); + *rx_num = i; + break; + case AIF1_CAP: + case AIF2_CAP: + case AIF3_CAP: + case AIF4_MAD_TX: + case AIF4_VIFEED: + if (!tx_slot || !tx_num) { + pr_err("%s: Invalid tx_slot %pK or tx_num %pK\n", + __func__, tx_slot, tx_num); + return -EINVAL; + } + list_for_each_entry(ch, &tasha_p->dai[dai->id].wcd9xxx_ch_list, + list) { + pr_debug("%s: slot_num %u ch->ch_num %d\n", + __func__, i, ch->ch_num); + tx_slot[i++] = ch->ch_num; + } + pr_debug("%s: tx_num %d\n", __func__, i); + *tx_num = i; + break; + + default: + pr_err("%s: Invalid DAI ID %x\n", __func__, dai->id); + break; + } + + return 0; +} + +static int tasha_set_channel_map(struct snd_soc_dai *dai, + unsigned int tx_num, unsigned int *tx_slot, + unsigned int rx_num, unsigned int *rx_slot) +{ + struct tasha_priv *tasha; + struct wcd9xxx *core; + struct wcd9xxx_codec_dai_data *dai_data = NULL; + + if (!dai) { + pr_err("%s: dai is empty\n", __func__); + return -EINVAL; + } + tasha = snd_soc_codec_get_drvdata(dai->codec); + core = dev_get_drvdata(dai->codec->dev->parent); + + if (!tx_slot || !rx_slot) { + pr_err("%s: Invalid tx_slot=%pK, rx_slot=%pK\n", + __func__, tx_slot, rx_slot); + return -EINVAL; + } + pr_debug("%s(): dai_name = %s DAI-ID %x tx_ch %d rx_ch %d\n" + "tasha->intf_type %d\n", + __func__, dai->name, dai->id, tx_num, rx_num, + tasha->intf_type); + + if (tasha->intf_type == WCD9XXX_INTERFACE_TYPE_SLIMBUS) { + wcd9xxx_init_slimslave(core, core->slim->laddr, + tx_num, tx_slot, rx_num, rx_slot); + /* Reserve TX12/TX13 for MAD data channel */ + dai_data = &tasha->dai[AIF4_MAD_TX]; + if (dai_data) { + if (TASHA_IS_2_0(tasha->wcd9xxx)) + list_add_tail(&core->tx_chs[TASHA_TX13].list, + &dai_data->wcd9xxx_ch_list); + else + list_add_tail(&core->tx_chs[TASHA_TX12].list, + &dai_data->wcd9xxx_ch_list); + } + } + return 0; +} + +static int tasha_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + pr_debug("%s(): substream = %s stream = %d\n", __func__, + substream->name, substream->stream); + + return 0; +} + +static void tasha_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(dai->codec); + + pr_debug("%s(): substream = %s stream = %d\n", __func__, + substream->name, substream->stream); + + if (tasha->intf_type == WCD9XXX_INTERFACE_TYPE_I2C) + return; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + tasha_codec_vote_max_bw(dai->codec, false); +} + +static int tasha_set_decimator_rate(struct snd_soc_dai *dai, + u8 tx_fs_rate_reg_val, u32 sample_rate) +{ + struct snd_soc_codec *codec = dai->codec; + struct wcd9xxx_ch *ch; + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + u32 tx_port = 0; + u8 shift = 0, shift_val = 0, tx_mux_sel = 0; + int decimator = -1; + u16 tx_port_reg = 0, tx_fs_reg = 0; + + list_for_each_entry(ch, &tasha->dai[dai->id].wcd9xxx_ch_list, list) { + tx_port = ch->port; + dev_dbg(codec->dev, "%s: dai->id = %d, tx_port = %d", + __func__, dai->id, tx_port); + + if ((tx_port < 0) || (tx_port == 12) || (tx_port >= 14)) { + dev_err(codec->dev, "%s: Invalid SLIM TX%u port. DAI ID: %d\n", + __func__, tx_port, dai->id); + return -EINVAL; + } + /* Find the SB TX MUX input - which decimator is connected */ + if (tx_port < 4) { + tx_port_reg = WCD9335_CDC_IF_ROUTER_TX_MUX_CFG0; + shift = (tx_port << 1); + shift_val = 0x03; + } else if ((tx_port >= 4) && (tx_port < 8)) { + tx_port_reg = WCD9335_CDC_IF_ROUTER_TX_MUX_CFG1; + shift = ((tx_port - 4) << 1); + shift_val = 0x03; + } else if ((tx_port >= 8) && (tx_port < 11)) { + tx_port_reg = WCD9335_CDC_IF_ROUTER_TX_MUX_CFG2; + shift = ((tx_port - 8) << 1); + shift_val = 0x03; + } else if (tx_port == 11) { + tx_port_reg = WCD9335_CDC_IF_ROUTER_TX_MUX_CFG3; + shift = 0; + shift_val = 0x0F; + } else if (tx_port == 13) { + tx_port_reg = WCD9335_CDC_IF_ROUTER_TX_MUX_CFG3; + shift = 4; + shift_val = 0x03; + } + tx_mux_sel = snd_soc_read(codec, tx_port_reg) & + (shift_val << shift); + tx_mux_sel = tx_mux_sel >> shift; + + if (tx_port <= 8) { + if ((tx_mux_sel == 0x2) || (tx_mux_sel == 0x3)) + decimator = tx_port; + } else if (tx_port <= 10) { + if ((tx_mux_sel == 0x1) || (tx_mux_sel == 0x2)) + decimator = ((tx_port == 9) ? 7 : 6); + } else if (tx_port == 11) { + if ((tx_mux_sel >= 1) && (tx_mux_sel < 7)) + decimator = tx_mux_sel - 1; + } else if (tx_port == 13) { + if ((tx_mux_sel == 0x1) || (tx_mux_sel == 0x2)) + decimator = 5; + } + + if (decimator >= 0) { + tx_fs_reg = WCD9335_CDC_TX0_TX_PATH_CTL + + 16 * decimator; + dev_dbg(codec->dev, "%s: set DEC%u (-> SLIM_TX%u) rate to %u\n", + __func__, decimator, tx_port, sample_rate); + snd_soc_update_bits(codec, tx_fs_reg, 0x0F, + tx_fs_rate_reg_val); + } else if ((tx_port <= 8) && (tx_mux_sel == 0x01)) { + /* Check if the TX Mux input is RX MIX TXn */ + dev_dbg(codec->dev, "%s: RX_MIX_TX%u going to SLIM TX%u\n", + __func__, tx_port, tx_port); + } else { + dev_err(codec->dev, "%s: ERROR: Invalid decimator: %d\n", + __func__, decimator); + return -EINVAL; + } + } + return 0; +} + +static int tasha_set_mix_interpolator_rate(struct snd_soc_dai *dai, + u8 int_mix_fs_rate_reg_val, + u32 sample_rate) +{ + u8 int_2_inp; + u32 j; + u16 int_mux_cfg1, int_fs_reg; + u8 int_mux_cfg1_val; + struct snd_soc_codec *codec = dai->codec; + struct wcd9xxx_ch *ch; + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + + list_for_each_entry(ch, &tasha->dai[dai->id].wcd9xxx_ch_list, list) { + int_2_inp = ch->port + INTn_2_INP_SEL_RX0 - + TASHA_RX_PORT_START_NUMBER; + if ((int_2_inp < INTn_2_INP_SEL_RX0) || + (int_2_inp > INTn_2_INP_SEL_RX7)) { + pr_err("%s: Invalid RX%u port, Dai ID is %d\n", + __func__, + (ch->port - TASHA_RX_PORT_START_NUMBER), + dai->id); + return -EINVAL; + } + + int_mux_cfg1 = WCD9335_CDC_RX_INP_MUX_RX_INT0_CFG1; + for (j = 0; j < TASHA_NUM_INTERPOLATORS; j++) { + int_mux_cfg1_val = snd_soc_read(codec, int_mux_cfg1) & + 0x0F; + if (int_mux_cfg1_val == int_2_inp) { + int_fs_reg = WCD9335_CDC_RX0_RX_PATH_MIX_CTL + + 20 * j; + pr_debug("%s: AIF_MIX_PB DAI(%d) connected to INT%u_2\n", + __func__, dai->id, j); + pr_debug("%s: set INT%u_2 sample rate to %u\n", + __func__, j, sample_rate); + snd_soc_update_bits(codec, int_fs_reg, + 0x0F, int_mix_fs_rate_reg_val); + } + int_mux_cfg1 += 2; + } + } + return 0; +} + +static int tasha_set_prim_interpolator_rate(struct snd_soc_dai *dai, + u8 int_prim_fs_rate_reg_val, + u32 sample_rate) +{ + u8 int_1_mix1_inp; + u32 j; + u16 int_mux_cfg0, int_mux_cfg1; + u16 int_fs_reg; + u8 int_mux_cfg0_val, int_mux_cfg1_val; + u8 inp0_sel, inp1_sel, inp2_sel; + struct snd_soc_codec *codec = dai->codec; + struct wcd9xxx_ch *ch; + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + + list_for_each_entry(ch, &tasha->dai[dai->id].wcd9xxx_ch_list, list) { + int_1_mix1_inp = ch->port + INTn_1_MIX_INP_SEL_RX0 - + TASHA_RX_PORT_START_NUMBER; + if ((int_1_mix1_inp < INTn_1_MIX_INP_SEL_RX0) || + (int_1_mix1_inp > INTn_1_MIX_INP_SEL_RX7)) { + pr_err("%s: Invalid RX%u port, Dai ID is %d\n", + __func__, + (ch->port - TASHA_RX_PORT_START_NUMBER), + dai->id); + return -EINVAL; + } + + int_mux_cfg0 = WCD9335_CDC_RX_INP_MUX_RX_INT0_CFG0; + + /* + * Loop through all interpolator MUX inputs and find out + * to which interpolator input, the slim rx port + * is connected + */ + for (j = 0; j < TASHA_NUM_INTERPOLATORS; j++) { + int_mux_cfg1 = int_mux_cfg0 + 1; + + int_mux_cfg0_val = snd_soc_read(codec, int_mux_cfg0); + int_mux_cfg1_val = snd_soc_read(codec, int_mux_cfg1); + inp0_sel = int_mux_cfg0_val & 0x0F; + inp1_sel = (int_mux_cfg0_val >> 4) & 0x0F; + inp2_sel = (int_mux_cfg1_val >> 4) & 0x0F; + if ((inp0_sel == int_1_mix1_inp) || + (inp1_sel == int_1_mix1_inp) || + (inp2_sel == int_1_mix1_inp)) { + int_fs_reg = WCD9335_CDC_RX0_RX_PATH_CTL + + 20 * j; + pr_debug("%s: AIF_PB DAI(%d) connected to INT%u_1\n", + __func__, dai->id, j); + pr_debug("%s: set INT%u_1 sample rate to %u\n", + __func__, j, sample_rate); + /* sample_rate is in Hz */ + if ((j == 0) && (sample_rate == 44100)) { + pr_info("%s: Cannot set 44.1KHz on INT0\n", + __func__); + } else + snd_soc_update_bits(codec, int_fs_reg, + 0x0F, int_prim_fs_rate_reg_val); + } + int_mux_cfg0 += 2; + } + } + + return 0; +} + + +static int tasha_set_interpolator_rate(struct snd_soc_dai *dai, + u32 sample_rate) +{ + int rate_val = 0; + int i, ret; + + /* set mixing path rate */ + for (i = 0; i < ARRAY_SIZE(int_mix_sample_rate_val); i++) { + if (sample_rate == + int_mix_sample_rate_val[i].sample_rate) { + rate_val = + int_mix_sample_rate_val[i].rate_val; + break; + } + } + if ((i == ARRAY_SIZE(int_mix_sample_rate_val)) || + (rate_val < 0)) + goto prim_rate; + ret = tasha_set_mix_interpolator_rate(dai, + (u8) rate_val, sample_rate); +prim_rate: + /* set primary path sample rate */ + for (i = 0; i < ARRAY_SIZE(int_prim_sample_rate_val); i++) { + if (sample_rate == + int_prim_sample_rate_val[i].sample_rate) { + rate_val = + int_prim_sample_rate_val[i].rate_val; + break; + } + } + if ((i == ARRAY_SIZE(int_prim_sample_rate_val)) || + (rate_val < 0)) + return -EINVAL; + ret = tasha_set_prim_interpolator_rate(dai, + (u8) rate_val, sample_rate); + return ret; +} + +static int tasha_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + pr_debug("%s(): substream = %s stream = %d\n", __func__, + substream->name, substream->stream); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + tasha_codec_vote_max_bw(dai->codec, false); + return 0; +} + +static int tasha_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(dai->codec); + int ret; + int tx_fs_rate = -EINVAL; + int rx_fs_rate = -EINVAL; + int i2s_bit_mode; + struct snd_soc_codec *codec = dai->codec; + + pr_debug("%s: dai_name = %s DAI-ID %x rate %d num_ch %d\n", __func__, + dai->name, dai->id, params_rate(params), + params_channels(params)); + + switch (substream->stream) { + case SNDRV_PCM_STREAM_PLAYBACK: + ret = tasha_set_interpolator_rate(dai, params_rate(params)); + if (ret) { + pr_err("%s: cannot set sample rate: %u\n", + __func__, params_rate(params)); + return ret; + } + switch (params_width(params)) { + case 16: + tasha->dai[dai->id].bit_width = 16; + i2s_bit_mode = 0x01; + break; + case 24: + tasha->dai[dai->id].bit_width = 24; + i2s_bit_mode = 0x00; + break; + default: + return -EINVAL; + } + tasha->dai[dai->id].rate = params_rate(params); + if (tasha->intf_type == WCD9XXX_INTERFACE_TYPE_I2C) { + switch (params_rate(params)) { + case 8000: + rx_fs_rate = 0; + break; + case 16000: + rx_fs_rate = 1; + break; + case 32000: + rx_fs_rate = 2; + break; + case 48000: + rx_fs_rate = 3; + break; + case 96000: + rx_fs_rate = 4; + break; + case 192000: + rx_fs_rate = 5; + break; + default: + dev_err(tasha->dev, + "%s: Invalid RX sample rate: %d\n", + __func__, params_rate(params)); + return -EINVAL; + }; + snd_soc_update_bits(codec, + WCD9335_DATA_HUB_DATA_HUB_RX_I2S_CTL, + 0x20, i2s_bit_mode << 5); + snd_soc_update_bits(codec, + WCD9335_DATA_HUB_DATA_HUB_RX_I2S_CTL, + 0x1c, (rx_fs_rate << 2)); + } + break; + case SNDRV_PCM_STREAM_CAPTURE: + switch (params_rate(params)) { + case 8000: + tx_fs_rate = 0; + break; + case 16000: + tx_fs_rate = 1; + break; + case 32000: + tx_fs_rate = 3; + break; + case 48000: + tx_fs_rate = 4; + break; + case 96000: + tx_fs_rate = 5; + break; + case 192000: + tx_fs_rate = 6; + break; + case 384000: + tx_fs_rate = 7; + break; + default: + dev_err(tasha->dev, "%s: Invalid TX sample rate: %d\n", + __func__, params_rate(params)); + return -EINVAL; + + }; + if (dai->id != AIF4_VIFEED && + dai->id != AIF4_MAD_TX) { + ret = tasha_set_decimator_rate(dai, tx_fs_rate, + params_rate(params)); + if (ret < 0) { + dev_err(tasha->dev, "%s: cannot set TX Decimator rate: %d\n", + __func__, tx_fs_rate); + return ret; + } + } + tasha->dai[dai->id].rate = params_rate(params); + switch (params_width(params)) { + case 16: + tasha->dai[dai->id].bit_width = 16; + i2s_bit_mode = 0x01; + break; + case 24: + tasha->dai[dai->id].bit_width = 24; + i2s_bit_mode = 0x00; + break; + case 32: + tasha->dai[dai->id].bit_width = 32; + i2s_bit_mode = 0x00; + break; + default: + dev_err(tasha->dev, "%s: Invalid format 0x%x\n", + __func__, params_width(params)); + return -EINVAL; + }; + if (tasha->intf_type == WCD9XXX_INTERFACE_TYPE_I2C) { + snd_soc_update_bits(codec, + WCD9335_DATA_HUB_DATA_HUB_TX_I2S_CTL, + 0x20, i2s_bit_mode << 5); + if (tx_fs_rate > 1) + tx_fs_rate--; + snd_soc_update_bits(codec, + WCD9335_DATA_HUB_DATA_HUB_TX_I2S_CTL, + 0x1c, tx_fs_rate << 2); + snd_soc_update_bits(codec, + WCD9335_DATA_HUB_DATA_HUB_TX_I2S_SD0_L_CFG, + 0x05, 0x05); + + snd_soc_update_bits(codec, + WCD9335_DATA_HUB_DATA_HUB_TX_I2S_SD0_R_CFG, + 0x05, 0x05); + + snd_soc_update_bits(codec, + WCD9335_DATA_HUB_DATA_HUB_TX_I2S_SD1_L_CFG, + 0x05, 0x05); + + snd_soc_update_bits(codec, + WCD9335_DATA_HUB_DATA_HUB_TX_I2S_SD1_R_CFG, + 0x05, 0x05); + } + break; + default: + pr_err("%s: Invalid stream type %d\n", __func__, + substream->stream); + return -EINVAL; + }; + if (dai->id == AIF4_VIFEED) + tasha->dai[dai->id].bit_width = 32; + + return 0; +} + +static int tasha_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(dai->codec); + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + /* CPU is master */ + if (tasha->intf_type == WCD9XXX_INTERFACE_TYPE_I2C) { + if (dai->id == AIF1_CAP) + snd_soc_update_bits(dai->codec, + WCD9335_DATA_HUB_DATA_HUB_TX_I2S_CTL, + 0x2, 0); + else if (dai->id == AIF1_PB) + snd_soc_update_bits(dai->codec, + WCD9335_DATA_HUB_DATA_HUB_RX_I2S_CTL, + 0x2, 0); + } + break; + case SND_SOC_DAIFMT_CBM_CFM: + /* CPU is slave */ + if (tasha->intf_type == WCD9XXX_INTERFACE_TYPE_I2C) { + if (dai->id == AIF1_CAP) + snd_soc_update_bits(dai->codec, + WCD9335_DATA_HUB_DATA_HUB_TX_I2S_CTL, + 0x2, 0x2); + else if (dai->id == AIF1_PB) + snd_soc_update_bits(dai->codec, + WCD9335_DATA_HUB_DATA_HUB_RX_I2S_CTL, + 0x2, 0x2); + } + break; + default: + return -EINVAL; + } + return 0; +} + +static int tasha_set_dai_sysclk(struct snd_soc_dai *dai, + int clk_id, unsigned int freq, int dir) +{ + pr_debug("%s\n", __func__); + return 0; +} + +static struct snd_soc_dai_ops tasha_dai_ops = { + .startup = tasha_startup, + .shutdown = tasha_shutdown, + .hw_params = tasha_hw_params, + .prepare = tasha_prepare, + .set_sysclk = tasha_set_dai_sysclk, + .set_fmt = tasha_set_dai_fmt, + .set_channel_map = tasha_set_channel_map, + .get_channel_map = tasha_get_channel_map, +}; + +static struct snd_soc_dai_driver tasha_dai[] = { + { + .name = "tasha_rx1", + .id = AIF1_PB, + .playback = { + .stream_name = "AIF1 Playback", + .rates = WCD9335_RATES_MASK | WCD9335_FRAC_RATES_MASK, + .formats = TASHA_FORMATS_S16_S24_LE, + .rate_max = 192000, + .rate_min = 8000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &tasha_dai_ops, + }, + { + .name = "tasha_tx1", + .id = AIF1_CAP, + .capture = { + .stream_name = "AIF1 Capture", + .rates = WCD9335_RATES_MASK, + .formats = TASHA_FORMATS_S16_S24_LE, + .rate_max = 192000, + .rate_min = 8000, + .channels_min = 1, + .channels_max = 4, + }, + .ops = &tasha_dai_ops, + }, + { + .name = "tasha_rx2", + .id = AIF2_PB, + .playback = { + .stream_name = "AIF2 Playback", + .rates = WCD9335_RATES_MASK | WCD9335_FRAC_RATES_MASK, + .formats = TASHA_FORMATS_S16_S24_LE, + .rate_min = 8000, + .rate_max = 192000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &tasha_dai_ops, + }, + { + .name = "tasha_tx2", + .id = AIF2_CAP, + .capture = { + .stream_name = "AIF2 Capture", + .rates = WCD9335_RATES_MASK, + .formats = TASHA_FORMATS_S16_S24_LE, + .rate_max = 192000, + .rate_min = 8000, + .channels_min = 1, + .channels_max = 8, + }, + .ops = &tasha_dai_ops, + }, + { + .name = "tasha_rx3", + .id = AIF3_PB, + .playback = { + .stream_name = "AIF3 Playback", + .rates = WCD9335_RATES_MASK | WCD9335_FRAC_RATES_MASK, + .formats = TASHA_FORMATS_S16_S24_LE, + .rate_min = 8000, + .rate_max = 192000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &tasha_dai_ops, + }, + { + .name = "tasha_tx3", + .id = AIF3_CAP, + .capture = { + .stream_name = "AIF3 Capture", + .rates = WCD9335_RATES_MASK, + .formats = TASHA_FORMATS_S16_S24_LE, + .rate_max = 48000, + .rate_min = 8000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &tasha_dai_ops, + }, + { + .name = "tasha_rx4", + .id = AIF4_PB, + .playback = { + .stream_name = "AIF4 Playback", + .rates = WCD9335_RATES_MASK | WCD9335_FRAC_RATES_MASK, + .formats = TASHA_FORMATS_S16_S24_LE, + .rate_min = 8000, + .rate_max = 192000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &tasha_dai_ops, + }, + { + .name = "tasha_mix_rx1", + .id = AIF_MIX1_PB, + .playback = { + .stream_name = "AIF Mix Playback", + .rates = WCD9335_RATES_MASK | WCD9335_FRAC_RATES_MASK, + .formats = TASHA_FORMATS_S16_S24_LE, + .rate_min = 8000, + .rate_max = 192000, + .channels_min = 1, + .channels_max = 8, + }, + .ops = &tasha_dai_ops, + }, + { + .name = "tasha_mad1", + .id = AIF4_MAD_TX, + .capture = { + .stream_name = "AIF4 MAD TX", + .rates = SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_192000 | SNDRV_PCM_RATE_384000, + .formats = TASHA_FORMATS_S16_S24_S32_LE, + .rate_min = 16000, + .rate_max = 384000, + .channels_min = 1, + .channels_max = 1, + }, + .ops = &tasha_dai_ops, + }, + { + .name = "tasha_vifeedback", + .id = AIF4_VIFEED, + .capture = { + .stream_name = "VIfeed", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_48000, + .formats = TASHA_FORMATS_S16_S24_S32_LE, + .rate_max = 48000, + .rate_min = 8000, + .channels_min = 1, + .channels_max = 4, + }, + .ops = &tasha_dai_ops, + }, + { + .name = "tasha_cpe", + .id = AIF5_CPE_TX, + .capture = { + .stream_name = "AIF5 CPE TX", + .rates = SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_48000, + .formats = TASHA_FORMATS_S16_S24_S32_LE, + .rate_min = 16000, + .rate_max = 48000, + .channels_min = 1, + .channels_max = 1, + }, + }, +}; + +static struct snd_soc_dai_driver tasha_i2s_dai[] = { + { + .name = "tasha_i2s_rx1", + .id = AIF1_PB, + .playback = { + .stream_name = "AIF1 Playback", + .rates = WCD9335_RATES_MASK, + .formats = TASHA_FORMATS_S16_S24_LE, + .rate_max = 192000, + .rate_min = 8000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &tasha_dai_ops, + }, + { + .name = "tasha_i2s_tx1", + .id = AIF1_CAP, + .capture = { + .stream_name = "AIF1 Capture", + .rates = WCD9335_RATES_MASK, + .formats = TASHA_FORMATS_S16_S24_LE, + .rate_max = 192000, + .rate_min = 8000, + .channels_min = 1, + .channels_max = 4, + }, + .ops = &tasha_dai_ops, + }, + { + .name = "tasha_i2s_rx2", + .id = AIF2_PB, + .playback = { + .stream_name = "AIF2 Playback", + .rates = WCD9335_RATES_MASK, + .formats = TASHA_FORMATS_S16_S24_LE, + .rate_max = 192000, + .rate_min = 8000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &tasha_dai_ops, + }, + { + .name = "tasha_i2s_tx2", + .id = AIF2_CAP, + .capture = { + .stream_name = "AIF2 Capture", + .rates = WCD9335_RATES_MASK, + .formats = TASHA_FORMATS_S16_S24_LE, + .rate_max = 192000, + .rate_min = 8000, + .channels_min = 1, + .channels_max = 4, + }, + .ops = &tasha_dai_ops, + }, +}; + +static void tasha_codec_power_gate_digital_core(struct tasha_priv *tasha) +{ + struct snd_soc_codec *codec = tasha->codec; + + if (!codec) + return; + + mutex_lock(&tasha->power_lock); + dev_dbg(codec->dev, "%s: Entering power gating function, %d\n", + __func__, tasha->power_active_ref); + + if (tasha->power_active_ref > 0) + goto exit; + + wcd9xxx_set_power_state(tasha->wcd9xxx, + WCD_REGION_POWER_COLLAPSE_BEGIN, + WCD9XXX_DIG_CORE_REGION_1); + snd_soc_update_bits(codec, WCD9335_CODEC_RPM_PWR_CDC_DIG_HM_CTL, + 0x04, 0x04); + snd_soc_update_bits(codec, WCD9335_CODEC_RPM_PWR_CDC_DIG_HM_CTL, + 0x01, 0x00); + snd_soc_update_bits(codec, WCD9335_CODEC_RPM_PWR_CDC_DIG_HM_CTL, + 0x02, 0x00); + clear_bit(AUDIO_NOMINAL, &tasha->status_mask); + tasha_codec_update_sido_voltage(tasha, sido_buck_svs_voltage); + wcd9xxx_set_power_state(tasha->wcd9xxx, WCD_REGION_POWER_DOWN, + WCD9XXX_DIG_CORE_REGION_1); +exit: + dev_dbg(codec->dev, "%s: Exiting power gating function, %d\n", + __func__, tasha->power_active_ref); + mutex_unlock(&tasha->power_lock); +} + +static void tasha_codec_power_gate_work(struct work_struct *work) +{ + struct tasha_priv *tasha; + struct delayed_work *dwork; + struct snd_soc_codec *codec; + + dwork = to_delayed_work(work); + tasha = container_of(dwork, struct tasha_priv, power_gate_work); + codec = tasha->codec; + + if (!codec) + return; + + tasha_codec_power_gate_digital_core(tasha); +} + +/* called under power_lock acquisition */ +static int tasha_dig_core_remove_power_collapse(struct snd_soc_codec *codec) +{ + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + + tasha_codec_vote_max_bw(codec, true); + snd_soc_write(codec, WCD9335_CODEC_RPM_PWR_CDC_DIG_HM_CTL, 0x5); + snd_soc_write(codec, WCD9335_CODEC_RPM_PWR_CDC_DIG_HM_CTL, 0x7); + snd_soc_write(codec, WCD9335_CODEC_RPM_PWR_CDC_DIG_HM_CTL, 0x3); + snd_soc_update_bits(codec, WCD9335_CODEC_RPM_RST_CTL, 0x02, 0x00); + snd_soc_update_bits(codec, WCD9335_CODEC_RPM_RST_CTL, 0x02, 0x02); + + wcd9xxx_set_power_state(tasha->wcd9xxx, + WCD_REGION_POWER_COLLAPSE_REMOVE, + WCD9XXX_DIG_CORE_REGION_1); + regcache_mark_dirty(codec->component.regmap); + regcache_sync_region(codec->component.regmap, + TASHA_DIG_CORE_REG_MIN, TASHA_DIG_CORE_REG_MAX); + tasha_codec_vote_max_bw(codec, false); + + return 0; +} + +static int tasha_dig_core_power_collapse(struct tasha_priv *tasha, + int req_state) +{ + struct snd_soc_codec *codec; + int cur_state; + + /* Exit if feature is disabled */ + if (!dig_core_collapse_enable) + return 0; + + mutex_lock(&tasha->power_lock); + if (req_state == POWER_COLLAPSE) + tasha->power_active_ref--; + else if (req_state == POWER_RESUME) + tasha->power_active_ref++; + else + goto unlock_mutex; + + if (tasha->power_active_ref < 0) { + dev_dbg(tasha->dev, "%s: power_active_ref is negative\n", + __func__); + goto unlock_mutex; + } + + codec = tasha->codec; + if (!codec) + goto unlock_mutex; + + if (req_state == POWER_COLLAPSE) { + if (tasha->power_active_ref == 0) { + schedule_delayed_work(&tasha->power_gate_work, + msecs_to_jiffies(dig_core_collapse_timer * 1000)); + } + } else if (req_state == POWER_RESUME) { + if (tasha->power_active_ref == 1) { + /* + * At this point, there can be two cases: + * 1. Core already in power collapse state + * 2. Timer kicked in and still did not expire or + * waiting for the power_lock + */ + cur_state = wcd9xxx_get_current_power_state( + tasha->wcd9xxx, + WCD9XXX_DIG_CORE_REGION_1); + if (cur_state == WCD_REGION_POWER_DOWN) + tasha_dig_core_remove_power_collapse(codec); + else { + mutex_unlock(&tasha->power_lock); + cancel_delayed_work_sync( + &tasha->power_gate_work); + mutex_lock(&tasha->power_lock); + } + } + } + +unlock_mutex: + mutex_unlock(&tasha->power_lock); + + return 0; +} + +static int __tasha_cdc_mclk_enable_locked(struct tasha_priv *tasha, + bool enable) +{ + int ret = 0; + + if (!tasha->wcd_ext_clk) { + dev_err(tasha->dev, "%s: wcd ext clock is NULL\n", __func__); + return -EINVAL; + } + + dev_dbg(tasha->dev, "%s: mclk_enable = %u\n", __func__, enable); + + if (enable) { + tasha_dig_core_power_collapse(tasha, POWER_RESUME); + ret = tasha_cdc_req_mclk_enable(tasha, true); + if (ret) + goto err; + + set_bit(AUDIO_NOMINAL, &tasha->status_mask); + tasha_codec_apply_sido_voltage(tasha, + SIDO_VOLTAGE_NOMINAL_MV); + } else { + if (!dig_core_collapse_enable) { + clear_bit(AUDIO_NOMINAL, &tasha->status_mask); + tasha_codec_update_sido_voltage(tasha, + sido_buck_svs_voltage); + } + tasha_cdc_req_mclk_enable(tasha, false); + tasha_dig_core_power_collapse(tasha, POWER_COLLAPSE); + } + +err: + return ret; +} + +static int __tasha_cdc_mclk_enable(struct tasha_priv *tasha, + bool enable) +{ + int ret; + + WCD9XXX_V2_BG_CLK_LOCK(tasha->resmgr); + ret = __tasha_cdc_mclk_enable_locked(tasha, enable); + WCD9XXX_V2_BG_CLK_UNLOCK(tasha->resmgr); + + return ret; +} + +int tasha_cdc_mclk_enable(struct snd_soc_codec *codec, int enable, bool dapm) +{ + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + + return __tasha_cdc_mclk_enable(tasha, enable); +} +EXPORT_SYMBOL(tasha_cdc_mclk_enable); + +int tasha_cdc_mclk_tx_enable(struct snd_soc_codec *codec, int enable, bool dapm) +{ + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + int ret = 0; + + dev_dbg(tasha->dev, "%s: clk_mode: %d, enable: %d, clk_internal: %d\n", + __func__, tasha->clk_mode, enable, tasha->clk_internal); + if (tasha->clk_mode || tasha->clk_internal) { + if (enable) { + tasha_cdc_sido_ccl_enable(tasha, true); + wcd_resmgr_enable_master_bias(tasha->resmgr); + tasha_dig_core_power_collapse(tasha, POWER_RESUME); + snd_soc_update_bits(codec, + WCD9335_CDC_CLK_RST_CTRL_FS_CNT_CONTROL, + 0x01, 0x01); + snd_soc_update_bits(codec, + WCD9335_CDC_CLK_RST_CTRL_MCLK_CONTROL, + 0x01, 0x01); + set_bit(CPE_NOMINAL, &tasha->status_mask); + tasha_codec_update_sido_voltage(tasha, + SIDO_VOLTAGE_NOMINAL_MV); + tasha->clk_internal = true; + } else { + tasha->clk_internal = false; + clear_bit(CPE_NOMINAL, &tasha->status_mask); + tasha_codec_update_sido_voltage(tasha, + sido_buck_svs_voltage); + tasha_dig_core_power_collapse(tasha, POWER_COLLAPSE); + wcd_resmgr_disable_master_bias(tasha->resmgr); + tasha_cdc_sido_ccl_enable(tasha, false); + } + } else { + ret = __tasha_cdc_mclk_enable(tasha, enable); + } + return ret; +} +EXPORT_SYMBOL(tasha_cdc_mclk_tx_enable); + +static ssize_t tasha_codec_version_read(struct snd_info_entry *entry, + void *file_private_data, struct file *file, + char __user *buf, size_t count, loff_t pos) +{ + struct tasha_priv *tasha; + struct wcd9xxx *wcd9xxx; + char buffer[TASHA_VERSION_ENTRY_SIZE]; + int len = 0; + + tasha = (struct tasha_priv *) entry->private_data; + if (!tasha) { + pr_err("%s: tasha priv is null\n", __func__); + return -EINVAL; + } + + wcd9xxx = tasha->wcd9xxx; + + if (wcd9xxx->codec_type->id_major == TASHA_MAJOR) { + if (TASHA_IS_1_0(wcd9xxx)) + len = snprintf(buffer, sizeof(buffer), "WCD9335_1_0\n"); + else if (TASHA_IS_1_1(wcd9xxx)) + len = snprintf(buffer, sizeof(buffer), "WCD9335_1_1\n"); + else + snprintf(buffer, sizeof(buffer), "VER_UNDEFINED\n"); + } else if (wcd9xxx->codec_type->id_major == TASHA2P0_MAJOR) { + len = snprintf(buffer, sizeof(buffer), "WCD9335_2_0\n"); + } else + len = snprintf(buffer, sizeof(buffer), "VER_UNDEFINED\n"); + + return simple_read_from_buffer(buf, count, &pos, buffer, len); +} + +static struct snd_info_entry_ops tasha_codec_info_ops = { + .read = tasha_codec_version_read, +}; + +/* + * tasha_codec_info_create_codec_entry - creates wcd9335 module + * @codec_root: The parent directory + * @codec: Codec instance + * + * Creates wcd9335 module and version entry under the given + * parent directory. + * + * Return: 0 on success or negative error code on failure. + */ +int tasha_codec_info_create_codec_entry(struct snd_info_entry *codec_root, + struct snd_soc_codec *codec) +{ + struct snd_info_entry *version_entry; + struct tasha_priv *tasha; + struct snd_soc_card *card; + + if (!codec_root || !codec) + return -EINVAL; + + tasha = snd_soc_codec_get_drvdata(codec); + card = codec->component.card; + tasha->entry = snd_info_create_subdir(codec_root->module, + "tasha", codec_root); + if (!tasha->entry) { + dev_dbg(codec->dev, "%s: failed to create wcd9335 entry\n", + __func__); + return -ENOMEM; + } + + version_entry = snd_info_create_card_entry(card->snd_card, + "version", + tasha->entry); + if (!version_entry) { + dev_dbg(codec->dev, "%s: failed to create wcd9335 version entry\n", + __func__); + return -ENOMEM; + } + + version_entry->private_data = tasha; + version_entry->size = TASHA_VERSION_ENTRY_SIZE; + version_entry->content = SNDRV_INFO_CONTENT_DATA; + version_entry->c.ops = &tasha_codec_info_ops; + + if (snd_info_register(version_entry) < 0) { + snd_info_free_entry(version_entry); + return -ENOMEM; + } + tasha->version_entry = version_entry; + + return 0; +} +EXPORT_SYMBOL(tasha_codec_info_create_codec_entry); + +static int __tasha_codec_internal_rco_ctrl( + struct snd_soc_codec *codec, bool enable) +{ + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + int ret = 0; + + if (enable) { + tasha_cdc_sido_ccl_enable(tasha, true); + if (wcd_resmgr_get_clk_type(tasha->resmgr) == + WCD_CLK_RCO) { + ret = wcd_resmgr_enable_clk_block(tasha->resmgr, + WCD_CLK_RCO); + } else { + ret = tasha_cdc_req_mclk_enable(tasha, true); + ret |= wcd_resmgr_enable_clk_block(tasha->resmgr, + WCD_CLK_RCO); + ret |= tasha_cdc_req_mclk_enable(tasha, false); + } + + } else { + ret = wcd_resmgr_disable_clk_block(tasha->resmgr, + WCD_CLK_RCO); + tasha_cdc_sido_ccl_enable(tasha, false); + } + + if (ret) { + dev_err(codec->dev, "%s: Error in %s RCO\n", + __func__, (enable ? "enabling" : "disabling")); + ret = -EINVAL; + } + + return ret; +} + +/* + * tasha_codec_internal_rco_ctrl() + * Make sure that the caller does not acquire + * BG_CLK_LOCK. + */ +static int tasha_codec_internal_rco_ctrl(struct snd_soc_codec *codec, + bool enable) +{ + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + int ret = 0; + + WCD9XXX_V2_BG_CLK_LOCK(tasha->resmgr); + ret = __tasha_codec_internal_rco_ctrl(codec, enable); + WCD9XXX_V2_BG_CLK_UNLOCK(tasha->resmgr); + return ret; +} + +/* + * tasha_mbhc_hs_detect: starts mbhc insertion/removal functionality + * @codec: handle to snd_soc_codec * + * @mbhc_cfg: handle to mbhc configuration structure + * return 0 if mbhc_start is success or error code in case of failure + */ +int tasha_mbhc_hs_detect(struct snd_soc_codec *codec, + struct wcd_mbhc_config *mbhc_cfg) +{ + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + + return wcd_mbhc_start(&tasha->mbhc, mbhc_cfg); +} +EXPORT_SYMBOL(tasha_mbhc_hs_detect); + +/* + * tasha_mbhc_hs_detect_exit: stop mbhc insertion/removal functionality + * @codec: handle to snd_soc_codec * + */ +void tasha_mbhc_hs_detect_exit(struct snd_soc_codec *codec) +{ + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + + wcd_mbhc_stop(&tasha->mbhc); +} +EXPORT_SYMBOL(tasha_mbhc_hs_detect_exit); + +static int wcd9335_get_micb_vout_ctl_val(u32 micb_mv) +{ + /* min micbias voltage is 1V and maximum is 2.85V */ + if (micb_mv < 1000 || micb_mv > 2850) { + pr_err("%s: unsupported micbias voltage\n", __func__); + return -EINVAL; + } + + return (micb_mv - 1000) / 50; +} + +static const struct tasha_reg_mask_val tasha_reg_update_reset_val_1_1[] = { + {WCD9335_RCO_CTRL_2, 0xFF, 0x47}, + {WCD9335_FLYBACK_VNEG_DAC_CTRL_4, 0xFF, 0x60}, +}; + +static const struct tasha_reg_mask_val tasha_codec_reg_init_val_1_1[] = { + {WCD9335_FLYBACK_VNEG_DAC_CTRL_1, 0xFF, 0x65}, + {WCD9335_FLYBACK_VNEG_DAC_CTRL_2, 0xFF, 0x52}, + {WCD9335_FLYBACK_VNEG_DAC_CTRL_3, 0xFF, 0xAF}, + {WCD9335_FLYBACK_VNEG_DAC_CTRL_4, 0xFF, 0x60}, + {WCD9335_FLYBACK_VNEG_CTRL_3, 0xFF, 0xF4}, + {WCD9335_FLYBACK_VNEG_CTRL_9, 0xFF, 0x40}, + {WCD9335_FLYBACK_VNEG_CTRL_2, 0xFF, 0x4F}, + {WCD9335_FLYBACK_EN, 0xFF, 0x6E}, + {WCD9335_CDC_RX2_RX_PATH_SEC0, 0xF8, 0xF8}, + {WCD9335_CDC_RX1_RX_PATH_SEC0, 0xF8, 0xF8}, +}; + +static const struct tasha_reg_mask_val tasha_codec_reg_init_val_1_0[] = { + {WCD9335_FLYBACK_VNEG_CTRL_3, 0xFF, 0x54}, + {WCD9335_CDC_RX2_RX_PATH_SEC0, 0xFC, 0xFC}, + {WCD9335_CDC_RX1_RX_PATH_SEC0, 0xFC, 0xFC}, +}; + +static const struct tasha_reg_mask_val tasha_codec_reg_init_val_2_0[] = { + {WCD9335_RCO_CTRL_2, 0x0F, 0x08}, + {WCD9335_RX_BIAS_FLYB_MID_RST, 0xF0, 0x10}, + {WCD9335_FLYBACK_CTRL_1, 0x20, 0x20}, + {WCD9335_HPH_OCP_CTL, 0xFF, 0x7A}, + {WCD9335_HPH_L_TEST, 0x01, 0x01}, + {WCD9335_HPH_R_TEST, 0x01, 0x01}, + {WCD9335_CDC_BOOST0_BOOST_CFG1, 0x3F, 0x12}, + {WCD9335_CDC_BOOST0_BOOST_CFG2, 0x1C, 0x08}, + {WCD9335_CDC_COMPANDER7_CTL7, 0x1E, 0x18}, + {WCD9335_CDC_BOOST1_BOOST_CFG1, 0x3F, 0x12}, + {WCD9335_CDC_BOOST1_BOOST_CFG2, 0x1C, 0x08}, + {WCD9335_CDC_COMPANDER8_CTL7, 0x1E, 0x18}, + {WCD9335_CDC_TX0_TX_PATH_SEC7, 0xFF, 0x45}, + {WCD9335_CDC_RX0_RX_PATH_SEC0, 0xFC, 0xF4}, + {WCD9335_HPH_REFBUFF_LP_CTL, 0x08, 0x08}, + {WCD9335_HPH_REFBUFF_LP_CTL, 0x06, 0x02}, + {WCD9335_DIFF_LO_CORE_OUT_PROG, 0xFC, 0xA0}, + {WCD9335_SE_LO_COM1, 0xFF, 0xC0}, + {WCD9335_CDC_RX3_RX_PATH_SEC0, 0xFC, 0xF4}, + {WCD9335_CDC_RX4_RX_PATH_SEC0, 0xFC, 0xF4}, + {WCD9335_CDC_RX5_RX_PATH_SEC0, 0xFC, 0xF8}, + {WCD9335_CDC_RX6_RX_PATH_SEC0, 0xFC, 0xF8}, +}; + +static const struct tasha_reg_mask_val tasha_codec_reg_defaults[] = { + {WCD9335_CODEC_RPM_CLK_GATE, 0x03, 0x00}, + {WCD9335_CODEC_RPM_CLK_MCLK_CFG, 0x03, 0x01}, + {WCD9335_CODEC_RPM_CLK_MCLK_CFG, 0x04, 0x04}, +}; + +static const struct tasha_reg_mask_val tasha_codec_reg_i2c_defaults[] = { + {WCD9335_ANA_CLK_TOP, 0x20, 0x20}, + {WCD9335_CODEC_RPM_CLK_GATE, 0x03, 0x01}, + {WCD9335_CODEC_RPM_CLK_MCLK_CFG, 0x03, 0x00}, + {WCD9335_CODEC_RPM_CLK_MCLK_CFG, 0x05, 0x05}, + {WCD9335_DATA_HUB_DATA_HUB_RX0_INP_CFG, 0x01, 0x01}, + {WCD9335_DATA_HUB_DATA_HUB_RX1_INP_CFG, 0x01, 0x01}, + {WCD9335_DATA_HUB_DATA_HUB_RX2_INP_CFG, 0x01, 0x01}, + {WCD9335_DATA_HUB_DATA_HUB_RX3_INP_CFG, 0x01, 0x01}, + {WCD9335_DATA_HUB_DATA_HUB_TX_I2S_SD0_L_CFG, 0x05, 0x05}, + {WCD9335_DATA_HUB_DATA_HUB_TX_I2S_SD0_R_CFG, 0x05, 0x05}, + {WCD9335_DATA_HUB_DATA_HUB_TX_I2S_SD1_L_CFG, 0x05, 0x05}, + {WCD9335_DATA_HUB_DATA_HUB_TX_I2S_SD1_R_CFG, 0x05, 0x05}, +}; + +static const struct tasha_reg_mask_val tasha_codec_reg_init_common_val[] = { + /* Rbuckfly/R_EAR(32) */ + {WCD9335_CDC_CLSH_K2_MSB, 0x0F, 0x00}, + {WCD9335_CDC_CLSH_K2_LSB, 0xFF, 0x60}, + {WCD9335_CPE_SS_DMIC_CFG, 0x80, 0x00}, + {WCD9335_CDC_BOOST0_BOOST_CTL, 0x7C, 0x58}, + {WCD9335_CDC_BOOST1_BOOST_CTL, 0x7C, 0x58}, + {WCD9335_CDC_RX7_RX_PATH_CFG1, 0x08, 0x08}, + {WCD9335_CDC_RX8_RX_PATH_CFG1, 0x08, 0x08}, + {WCD9335_ANA_LO_1_2, 0x3C, 0X3C}, + {WCD9335_DIFF_LO_COM_SWCAP_REFBUF_FREQ, 0x70, 0x00}, + {WCD9335_SOC_MAD_AUDIO_CTL_2, 0x03, 0x03}, + {WCD9335_CDC_TOP_TOP_CFG1, 0x02, 0x02}, + {WCD9335_CDC_TOP_TOP_CFG1, 0x01, 0x01}, + {WCD9335_EAR_CMBUFF, 0x08, 0x00}, + {WCD9335_CDC_TX9_SPKR_PROT_PATH_CFG0, 0x01, 0x01}, + {WCD9335_CDC_TX10_SPKR_PROT_PATH_CFG0, 0x01, 0x01}, + {WCD9335_CDC_TX11_SPKR_PROT_PATH_CFG0, 0x01, 0x01}, + {WCD9335_CDC_TX12_SPKR_PROT_PATH_CFG0, 0x01, 0x01}, + {WCD9335_CDC_COMPANDER7_CTL3, 0x80, 0x80}, + {WCD9335_CDC_COMPANDER8_CTL3, 0x80, 0x80}, + {WCD9335_CDC_COMPANDER7_CTL7, 0x01, 0x01}, + {WCD9335_CDC_COMPANDER8_CTL7, 0x01, 0x01}, + {WCD9335_CDC_RX0_RX_PATH_CFG0, 0x01, 0x01}, + {WCD9335_CDC_RX1_RX_PATH_CFG0, 0x01, 0x01}, + {WCD9335_CDC_RX2_RX_PATH_CFG0, 0x01, 0x01}, + {WCD9335_CDC_RX3_RX_PATH_CFG0, 0x01, 0x01}, + {WCD9335_CDC_RX4_RX_PATH_CFG0, 0x01, 0x01}, + {WCD9335_CDC_RX5_RX_PATH_CFG0, 0x01, 0x01}, + {WCD9335_CDC_RX6_RX_PATH_CFG0, 0x01, 0x01}, + {WCD9335_CDC_RX7_RX_PATH_CFG0, 0x01, 0x01}, + {WCD9335_CDC_RX8_RX_PATH_CFG0, 0x01, 0x01}, + {WCD9335_CDC_RX0_RX_PATH_MIX_CFG, 0x01, 0x01}, + {WCD9335_CDC_RX1_RX_PATH_MIX_CFG, 0x01, 0x01}, + {WCD9335_CDC_RX2_RX_PATH_MIX_CFG, 0x01, 0x01}, + {WCD9335_CDC_RX3_RX_PATH_MIX_CFG, 0x01, 0x01}, + {WCD9335_CDC_RX4_RX_PATH_MIX_CFG, 0x01, 0x01}, + {WCD9335_CDC_RX5_RX_PATH_MIX_CFG, 0x01, 0x01}, + {WCD9335_CDC_RX6_RX_PATH_MIX_CFG, 0x01, 0x01}, + {WCD9335_CDC_RX7_RX_PATH_MIX_CFG, 0x01, 0x01}, + {WCD9335_CDC_RX8_RX_PATH_MIX_CFG, 0x01, 0x01}, + {WCD9335_VBADC_IBIAS_FE, 0x0C, 0x08}, +}; + +static const struct tasha_reg_mask_val tasha_codec_reg_init_1_x_val[] = { + /* Enable TX HPF Filter & Linear Phase */ + {WCD9335_CDC_TX0_TX_PATH_CFG0, 0x11, 0x11}, + {WCD9335_CDC_TX1_TX_PATH_CFG0, 0x11, 0x11}, + {WCD9335_CDC_TX2_TX_PATH_CFG0, 0x11, 0x11}, + {WCD9335_CDC_TX3_TX_PATH_CFG0, 0x11, 0x11}, + {WCD9335_CDC_TX4_TX_PATH_CFG0, 0x11, 0x11}, + {WCD9335_CDC_TX5_TX_PATH_CFG0, 0x11, 0x11}, + {WCD9335_CDC_TX6_TX_PATH_CFG0, 0x11, 0x11}, + {WCD9335_CDC_TX7_TX_PATH_CFG0, 0x11, 0x11}, + {WCD9335_CDC_TX8_TX_PATH_CFG0, 0x11, 0x11}, + {WCD9335_CDC_RX0_RX_PATH_SEC0, 0xF8, 0xF8}, + {WCD9335_CDC_RX0_RX_PATH_SEC1, 0x08, 0x08}, + {WCD9335_CDC_RX1_RX_PATH_SEC1, 0x08, 0x08}, + {WCD9335_CDC_RX2_RX_PATH_SEC1, 0x08, 0x08}, + {WCD9335_CDC_RX3_RX_PATH_SEC1, 0x08, 0x08}, + {WCD9335_CDC_RX4_RX_PATH_SEC1, 0x08, 0x08}, + {WCD9335_CDC_RX5_RX_PATH_SEC1, 0x08, 0x08}, + {WCD9335_CDC_RX6_RX_PATH_SEC1, 0x08, 0x08}, + {WCD9335_CDC_RX7_RX_PATH_SEC1, 0x08, 0x08}, + {WCD9335_CDC_RX8_RX_PATH_SEC1, 0x08, 0x08}, + {WCD9335_CDC_RX0_RX_PATH_MIX_SEC0, 0x08, 0x08}, + {WCD9335_CDC_RX1_RX_PATH_MIX_SEC0, 0x08, 0x08}, + {WCD9335_CDC_RX2_RX_PATH_MIX_SEC0, 0x08, 0x08}, + {WCD9335_CDC_RX3_RX_PATH_MIX_SEC0, 0x08, 0x08}, + {WCD9335_CDC_RX4_RX_PATH_MIX_SEC0, 0x08, 0x08}, + {WCD9335_CDC_RX5_RX_PATH_MIX_SEC0, 0x08, 0x08}, + {WCD9335_CDC_RX6_RX_PATH_MIX_SEC0, 0x08, 0x08}, + {WCD9335_CDC_RX7_RX_PATH_MIX_SEC0, 0x08, 0x08}, + {WCD9335_CDC_RX8_RX_PATH_MIX_SEC0, 0x08, 0x08}, + {WCD9335_CDC_TX0_TX_PATH_SEC2, 0x01, 0x01}, + {WCD9335_CDC_TX1_TX_PATH_SEC2, 0x01, 0x01}, + {WCD9335_CDC_TX2_TX_PATH_SEC2, 0x01, 0x01}, + {WCD9335_CDC_TX3_TX_PATH_SEC2, 0x01, 0x01}, + {WCD9335_CDC_TX4_TX_PATH_SEC2, 0x01, 0x01}, + {WCD9335_CDC_TX5_TX_PATH_SEC2, 0x01, 0x01}, + {WCD9335_CDC_TX6_TX_PATH_SEC2, 0x01, 0x01}, + {WCD9335_CDC_TX7_TX_PATH_SEC2, 0x01, 0x01}, + {WCD9335_CDC_TX8_TX_PATH_SEC2, 0x01, 0x01}, + {WCD9335_CDC_RX3_RX_PATH_SEC0, 0xF8, 0xF0}, + {WCD9335_CDC_RX4_RX_PATH_SEC0, 0xF8, 0xF0}, + {WCD9335_CDC_RX5_RX_PATH_SEC0, 0xF8, 0xF8}, + {WCD9335_CDC_RX6_RX_PATH_SEC0, 0xF8, 0xF8}, + {WCD9335_RX_OCP_COUNT, 0xFF, 0xFF}, + {WCD9335_HPH_OCP_CTL, 0xF0, 0x70}, + {WCD9335_CPE_SS_CPAR_CFG, 0xFF, 0x00}, + {WCD9335_FLYBACK_VNEG_CTRL_1, 0xFF, 0x63}, + {WCD9335_FLYBACK_VNEG_CTRL_4, 0xFF, 0x7F}, + {WCD9335_CLASSH_CTRL_VCL_1, 0xFF, 0x60}, + {WCD9335_CLASSH_CTRL_CCL_5, 0xFF, 0x40}, + {WCD9335_RX_TIMER_DIV, 0xFF, 0x32}, + {WCD9335_SE_LO_COM2, 0xFF, 0x01}, + {WCD9335_MBHC_ZDET_ANA_CTL, 0x0F, 0x07}, + {WCD9335_RX_BIAS_HPH_PA, 0xF0, 0x60}, + {WCD9335_HPH_RDAC_LDO_CTL, 0x88, 0x88}, + {WCD9335_HPH_L_EN, 0x20, 0x20}, + {WCD9335_HPH_R_EN, 0x20, 0x20}, + {WCD9335_DIFF_LO_CORE_OUT_PROG, 0xFC, 0xD8}, + {WCD9335_CDC_RX5_RX_PATH_SEC3, 0xBD, 0xBD}, + {WCD9335_CDC_RX6_RX_PATH_SEC3, 0xBD, 0xBD}, + {WCD9335_DIFF_LO_COM_PA_FREQ, 0x70, 0x40}, +}; + +static void tasha_update_reg_reset_values(struct snd_soc_codec *codec) +{ + u32 i; + struct wcd9xxx *tasha_core = dev_get_drvdata(codec->dev->parent); + + if (TASHA_IS_1_1(tasha_core)) { + for (i = 0; i < ARRAY_SIZE(tasha_reg_update_reset_val_1_1); + i++) + snd_soc_write(codec, + tasha_reg_update_reset_val_1_1[i].reg, + tasha_reg_update_reset_val_1_1[i].val); + } +} + +static void tasha_codec_init_reg(struct snd_soc_codec *codec) +{ + u32 i; + struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent); + + for (i = 0; i < ARRAY_SIZE(tasha_codec_reg_init_common_val); i++) + snd_soc_update_bits(codec, + tasha_codec_reg_init_common_val[i].reg, + tasha_codec_reg_init_common_val[i].mask, + tasha_codec_reg_init_common_val[i].val); + + if (TASHA_IS_1_1(wcd9xxx) || + TASHA_IS_1_0(wcd9xxx)) + for (i = 0; i < ARRAY_SIZE(tasha_codec_reg_init_1_x_val); i++) + snd_soc_update_bits(codec, + tasha_codec_reg_init_1_x_val[i].reg, + tasha_codec_reg_init_1_x_val[i].mask, + tasha_codec_reg_init_1_x_val[i].val); + + if (TASHA_IS_1_1(wcd9xxx)) { + for (i = 0; i < ARRAY_SIZE(tasha_codec_reg_init_val_1_1); i++) + snd_soc_update_bits(codec, + tasha_codec_reg_init_val_1_1[i].reg, + tasha_codec_reg_init_val_1_1[i].mask, + tasha_codec_reg_init_val_1_1[i].val); + } else if (TASHA_IS_1_0(wcd9xxx)) { + for (i = 0; i < ARRAY_SIZE(tasha_codec_reg_init_val_1_0); i++) + snd_soc_update_bits(codec, + tasha_codec_reg_init_val_1_0[i].reg, + tasha_codec_reg_init_val_1_0[i].mask, + tasha_codec_reg_init_val_1_0[i].val); + } else if (TASHA_IS_2_0(wcd9xxx)) { + for (i = 0; i < ARRAY_SIZE(tasha_codec_reg_init_val_2_0); i++) + snd_soc_update_bits(codec, + tasha_codec_reg_init_val_2_0[i].reg, + tasha_codec_reg_init_val_2_0[i].mask, + tasha_codec_reg_init_val_2_0[i].val); + } +} + +static void tasha_update_reg_defaults(struct tasha_priv *tasha) +{ + u32 i; + struct wcd9xxx *wcd9xxx; + + wcd9xxx = tasha->wcd9xxx; + for (i = 0; i < ARRAY_SIZE(tasha_codec_reg_defaults); i++) + regmap_update_bits(wcd9xxx->regmap, + tasha_codec_reg_defaults[i].reg, + tasha_codec_reg_defaults[i].mask, + tasha_codec_reg_defaults[i].val); + + tasha->intf_type = wcd9xxx_get_intf_type(); + if (tasha->intf_type == WCD9XXX_INTERFACE_TYPE_I2C) + for (i = 0; i < ARRAY_SIZE(tasha_codec_reg_i2c_defaults); i++) + regmap_update_bits(wcd9xxx->regmap, + tasha_codec_reg_i2c_defaults[i].reg, + tasha_codec_reg_i2c_defaults[i].mask, + tasha_codec_reg_i2c_defaults[i].val); + +} + +static void tasha_slim_interface_init_reg(struct snd_soc_codec *codec) +{ + int i; + struct tasha_priv *priv = snd_soc_codec_get_drvdata(codec); + + for (i = 0; i < WCD9XXX_SLIM_NUM_PORT_REG; i++) + wcd9xxx_interface_reg_write(priv->wcd9xxx, + TASHA_SLIM_PGD_PORT_INT_EN0 + i, + 0xFF); +} + +static irqreturn_t tasha_slimbus_irq(int irq, void *data) +{ + struct tasha_priv *priv = data; + unsigned long status = 0; + int i, j, port_id, k; + u32 bit; + u8 val, int_val = 0; + bool tx, cleared; + unsigned short reg = 0; + + for (i = TASHA_SLIM_PGD_PORT_INT_STATUS_RX_0, j = 0; + i <= TASHA_SLIM_PGD_PORT_INT_STATUS_TX_1; i++, j++) { + val = wcd9xxx_interface_reg_read(priv->wcd9xxx, i); + status |= ((u32)val << (8 * j)); + } + + for_each_set_bit(j, &status, 32) { + tx = (j >= 16 ? true : false); + port_id = (tx ? j - 16 : j); + val = wcd9xxx_interface_reg_read(priv->wcd9xxx, + TASHA_SLIM_PGD_PORT_INT_RX_SOURCE0 + j); + if (val) { + if (!tx) + reg = TASHA_SLIM_PGD_PORT_INT_EN0 + + (port_id / 8); + else + reg = TASHA_SLIM_PGD_PORT_INT_TX_EN0 + + (port_id / 8); + int_val = wcd9xxx_interface_reg_read( + priv->wcd9xxx, reg); + /* + * Ignore interrupts for ports for which the + * interrupts are not specifically enabled. + */ + if (!(int_val & (1 << (port_id % 8)))) + continue; + } + if (val & TASHA_SLIM_IRQ_OVERFLOW) + pr_err_ratelimited( + "%s: overflow error on %s port %d, value %x\n", + __func__, (tx ? "TX" : "RX"), port_id, val); + if (val & TASHA_SLIM_IRQ_UNDERFLOW) + pr_err_ratelimited( + "%s: underflow error on %s port %d, value %x\n", + __func__, (tx ? "TX" : "RX"), port_id, val); + if ((val & TASHA_SLIM_IRQ_OVERFLOW) || + (val & TASHA_SLIM_IRQ_UNDERFLOW)) { + if (!tx) + reg = TASHA_SLIM_PGD_PORT_INT_EN0 + + (port_id / 8); + else + reg = TASHA_SLIM_PGD_PORT_INT_TX_EN0 + + (port_id / 8); + int_val = wcd9xxx_interface_reg_read( + priv->wcd9xxx, reg); + if (int_val & (1 << (port_id % 8))) { + int_val = int_val ^ (1 << (port_id % 8)); + wcd9xxx_interface_reg_write(priv->wcd9xxx, + reg, int_val); + } + } + if (val & TASHA_SLIM_IRQ_PORT_CLOSED) { + /* + * INT SOURCE register starts from RX to TX + * but port number in the ch_mask is in opposite way + */ + bit = (tx ? j - 16 : j + 16); + pr_debug("%s: %s port %d closed value %x, bit %u\n", + __func__, (tx ? "TX" : "RX"), port_id, val, + bit); + for (k = 0, cleared = false; k < NUM_CODEC_DAIS; k++) { + pr_debug("%s: priv->dai[%d].ch_mask = 0x%lx\n", + __func__, k, priv->dai[k].ch_mask); + if (test_and_clear_bit(bit, + &priv->dai[k].ch_mask)) { + cleared = true; + if (!priv->dai[k].ch_mask) + wake_up(&priv->dai[k].dai_wait); + /* + * There are cases when multiple DAIs + * might be using the same slimbus + * channel. Hence don't break here. + */ + } + } + WARN(!cleared, + "Couldn't find slimbus %s port %d for closing\n", + (tx ? "TX" : "RX"), port_id); + } + wcd9xxx_interface_reg_write(priv->wcd9xxx, + TASHA_SLIM_PGD_PORT_INT_CLR_RX_0 + + (j / 8), + 1 << (j % 8)); + } + + return IRQ_HANDLED; +} + +static int tasha_setup_irqs(struct tasha_priv *tasha) +{ + int ret = 0; + struct snd_soc_codec *codec = tasha->codec; + struct wcd9xxx *wcd9xxx = tasha->wcd9xxx; + struct wcd9xxx_core_resource *core_res = + &wcd9xxx->core_res; + + ret = wcd9xxx_request_irq(core_res, WCD9XXX_IRQ_SLIMBUS, + tasha_slimbus_irq, "SLIMBUS Slave", tasha); + if (ret) + pr_err("%s: Failed to request irq %d\n", __func__, + WCD9XXX_IRQ_SLIMBUS); + else + tasha_slim_interface_init_reg(codec); + + return ret; +} + +static void tasha_init_slim_slave_cfg(struct snd_soc_codec *codec) +{ + struct tasha_priv *priv = snd_soc_codec_get_drvdata(codec); + struct afe_param_cdc_slimbus_slave_cfg *cfg; + struct wcd9xxx *wcd9xxx = priv->wcd9xxx; + uint64_t eaddr = 0; + + cfg = &priv->slimbus_slave_cfg; + cfg->minor_version = 1; + cfg->tx_slave_port_offset = 0; + cfg->rx_slave_port_offset = 16; + + memcpy(&eaddr, &wcd9xxx->slim->e_addr, sizeof(wcd9xxx->slim->e_addr)); + WARN_ON(sizeof(wcd9xxx->slim->e_addr) != 6); + cfg->device_enum_addr_lsw = eaddr & 0xFFFFFFFF; + cfg->device_enum_addr_msw = eaddr >> 32; + + dev_dbg(codec->dev, "%s: slimbus logical address 0x%llx\n", + __func__, eaddr); +} + +static void tasha_cleanup_irqs(struct tasha_priv *tasha) +{ + struct wcd9xxx *wcd9xxx = tasha->wcd9xxx; + struct wcd9xxx_core_resource *core_res = + &wcd9xxx->core_res; + + wcd9xxx_free_irq(core_res, WCD9XXX_IRQ_SLIMBUS, tasha); +} + +static int tasha_handle_pdata(struct tasha_priv *tasha, + struct wcd9xxx_pdata *pdata) +{ + struct snd_soc_codec *codec = tasha->codec; + u8 dmic_ctl_val, mad_dmic_ctl_val; + u8 anc_ctl_value; + u32 def_dmic_rate, dmic_clk_drv; + int vout_ctl_1, vout_ctl_2, vout_ctl_3, vout_ctl_4; + int rc = 0; + + if (!pdata) { + dev_err(codec->dev, "%s: NULL pdata\n", __func__); + return -ENODEV; + } + + /* set micbias voltage */ + vout_ctl_1 = wcd9335_get_micb_vout_ctl_val(pdata->micbias.micb1_mv); + vout_ctl_2 = wcd9335_get_micb_vout_ctl_val(pdata->micbias.micb2_mv); + vout_ctl_3 = wcd9335_get_micb_vout_ctl_val(pdata->micbias.micb3_mv); + vout_ctl_4 = wcd9335_get_micb_vout_ctl_val(pdata->micbias.micb4_mv); + if (vout_ctl_1 < 0 || vout_ctl_2 < 0 || + vout_ctl_3 < 0 || vout_ctl_4 < 0) { + rc = -EINVAL; + goto done; + } + snd_soc_update_bits(codec, WCD9335_ANA_MICB1, 0x3F, vout_ctl_1); + snd_soc_update_bits(codec, WCD9335_ANA_MICB2, 0x3F, vout_ctl_2); + snd_soc_update_bits(codec, WCD9335_ANA_MICB3, 0x3F, vout_ctl_3); + snd_soc_update_bits(codec, WCD9335_ANA_MICB4, 0x3F, vout_ctl_4); + + /* Set the DMIC sample rate */ + switch (pdata->mclk_rate) { + case TASHA_MCLK_CLK_9P6MHZ: + def_dmic_rate = WCD9XXX_DMIC_SAMPLE_RATE_4P8MHZ; + break; + case TASHA_MCLK_CLK_12P288MHZ: + def_dmic_rate = WCD9XXX_DMIC_SAMPLE_RATE_4P096MHZ; + break; + default: + /* should never happen */ + dev_err(codec->dev, "%s: Invalid mclk_rate %d\n", + __func__, pdata->mclk_rate); + rc = -EINVAL; + goto done; + }; + + if (pdata->dmic_sample_rate == + WCD9XXX_DMIC_SAMPLE_RATE_UNDEFINED) { + dev_info(codec->dev, "%s: dmic_rate invalid default = %d\n", + __func__, def_dmic_rate); + pdata->dmic_sample_rate = def_dmic_rate; + } + if (pdata->mad_dmic_sample_rate == + WCD9XXX_DMIC_SAMPLE_RATE_UNDEFINED) { + dev_info(codec->dev, "%s: mad_dmic_rate invalid default = %d\n", + __func__, def_dmic_rate); + /* + * use dmic_sample_rate as the default for MAD + * if mad dmic sample rate is undefined + */ + pdata->mad_dmic_sample_rate = pdata->dmic_sample_rate; + } + if (pdata->ecpp_dmic_sample_rate == + WCD9XXX_DMIC_SAMPLE_RATE_UNDEFINED) { + dev_info(codec->dev, + "%s: ecpp_dmic_rate invalid default = %d\n", + __func__, def_dmic_rate); + /* + * use dmic_sample_rate as the default for ECPP DMIC + * if ecpp dmic sample rate is undefined + */ + pdata->ecpp_dmic_sample_rate = pdata->dmic_sample_rate; + } + + if (pdata->dmic_clk_drv == + WCD9XXX_DMIC_CLK_DRIVE_UNDEFINED) { + pdata->dmic_clk_drv = WCD9335_DMIC_CLK_DRIVE_DEFAULT; + dev_info(codec->dev, + "%s: dmic_clk_strength invalid, default = %d\n", + __func__, pdata->dmic_clk_drv); + } + + switch (pdata->dmic_clk_drv) { + case 2: + dmic_clk_drv = 0; + break; + case 4: + dmic_clk_drv = 1; + break; + case 8: + dmic_clk_drv = 2; + break; + case 16: + dmic_clk_drv = 3; + break; + default: + dev_err(codec->dev, + "%s: invalid dmic_clk_drv %d, using default\n", + __func__, pdata->dmic_clk_drv); + dmic_clk_drv = 0; + break; + } + + snd_soc_update_bits(codec, WCD9335_TEST_DEBUG_PAD_DRVCTL, + 0x0C, dmic_clk_drv << 2); + + /* + * Default the DMIC clk rates to mad_dmic_sample_rate, + * whereas, the anc/txfe dmic rates to dmic_sample_rate + * since the anc/txfe are independent of mad block. + */ + mad_dmic_ctl_val = tasha_get_dmic_clk_val(tasha->codec, + pdata->mclk_rate, + pdata->mad_dmic_sample_rate); + snd_soc_update_bits(codec, WCD9335_CPE_SS_DMIC0_CTL, + 0x0E, mad_dmic_ctl_val << 1); + snd_soc_update_bits(codec, WCD9335_CPE_SS_DMIC1_CTL, + 0x0E, mad_dmic_ctl_val << 1); + snd_soc_update_bits(codec, WCD9335_CPE_SS_DMIC2_CTL, + 0x0E, mad_dmic_ctl_val << 1); + + dmic_ctl_val = tasha_get_dmic_clk_val(tasha->codec, + pdata->mclk_rate, + pdata->dmic_sample_rate); + + if (dmic_ctl_val == WCD9335_DMIC_CLK_DIV_2) + anc_ctl_value = WCD9335_ANC_DMIC_X2_FULL_RATE; + else + anc_ctl_value = WCD9335_ANC_DMIC_X2_HALF_RATE; + + snd_soc_update_bits(codec, WCD9335_CDC_ANC0_MODE_2_CTL, + 0x40, anc_ctl_value << 6); + snd_soc_update_bits(codec, WCD9335_CDC_ANC0_MODE_2_CTL, + 0x20, anc_ctl_value << 5); + snd_soc_update_bits(codec, WCD9335_CDC_ANC1_MODE_2_CTL, + 0x40, anc_ctl_value << 6); + snd_soc_update_bits(codec, WCD9335_CDC_ANC1_MODE_2_CTL, + 0x20, anc_ctl_value << 5); +done: + return rc; +} + +static struct wcd_cpe_core *tasha_codec_get_cpe_core( + struct snd_soc_codec *codec) +{ + struct tasha_priv *priv = snd_soc_codec_get_drvdata(codec); + + return priv->cpe_core; +} + +static int tasha_codec_cpe_fll_update_divider( + struct snd_soc_codec *codec, u32 cpe_fll_rate) +{ + struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent); + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + + u32 div_val = 0, l_val = 0; + u32 computed_cpe_fll; + + if (cpe_fll_rate != CPE_FLL_CLK_75MHZ && + cpe_fll_rate != CPE_FLL_CLK_150MHZ) { + dev_err(codec->dev, + "%s: Invalid CPE fll rate request %u\n", + __func__, cpe_fll_rate); + return -EINVAL; + } + + if (wcd9xxx->mclk_rate == TASHA_MCLK_CLK_12P288MHZ) { + /* update divider to 10 and enable 5x divider */ + snd_soc_write(codec, WCD9335_CPE_FLL_USER_CTL_1, + 0x55); + div_val = 10; + } else if (wcd9xxx->mclk_rate == TASHA_MCLK_CLK_9P6MHZ) { + /* update divider to 8 and enable 2x divider */ + snd_soc_update_bits(codec, WCD9335_CPE_FLL_USER_CTL_0, + 0x7C, 0x70); + snd_soc_update_bits(codec, WCD9335_CPE_FLL_USER_CTL_1, + 0xE0, 0x20); + div_val = 8; + } else { + dev_err(codec->dev, + "%s: Invalid MCLK rate %u\n", + __func__, wcd9xxx->mclk_rate); + return -EINVAL; + } + + l_val = ((cpe_fll_rate / 1000) * div_val) / + (wcd9xxx->mclk_rate / 1000); + + /* If l_val was integer truncated, increment l_val once */ + computed_cpe_fll = (wcd9xxx->mclk_rate / div_val) * l_val; + if (computed_cpe_fll < cpe_fll_rate) + l_val++; + + + /* update L value LSB and MSB */ + snd_soc_write(codec, WCD9335_CPE_FLL_L_VAL_CTL_0, + (l_val & 0xFF)); + snd_soc_write(codec, WCD9335_CPE_FLL_L_VAL_CTL_1, + ((l_val >> 8) & 0xFF)); + + tasha->current_cpe_clk_freq = cpe_fll_rate; + dev_dbg(codec->dev, + "%s: updated l_val to %u for cpe_clk %u and mclk %u\n", + __func__, l_val, cpe_fll_rate, wcd9xxx->mclk_rate); + + return 0; +} + +static int __tasha_cdc_change_cpe_clk(struct snd_soc_codec *codec, + u32 clk_freq) +{ + int ret = 0; + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + + if (!tasha_cdc_is_svs_enabled(tasha)) { + dev_dbg(codec->dev, + "%s: SVS not enabled or tasha is not 2p0, return\n", + __func__); + return 0; + } + dev_dbg(codec->dev, "%s: clk_freq = %u\n", __func__, clk_freq); + + if (clk_freq == CPE_FLL_CLK_75MHZ) { + /* Change to SVS */ + snd_soc_update_bits(codec, WCD9335_CPE_FLL_FLL_MODE, + 0x08, 0x08); + if (tasha_codec_cpe_fll_update_divider(codec, clk_freq)) { + ret = -EINVAL; + goto done; + } + + snd_soc_update_bits(codec, WCD9335_CPE_FLL_FLL_MODE, + 0x10, 0x10); + + clear_bit(CPE_NOMINAL, &tasha->status_mask); + tasha_codec_update_sido_voltage(tasha, sido_buck_svs_voltage); + + } else if (clk_freq == CPE_FLL_CLK_150MHZ) { + /* change to nominal */ + snd_soc_update_bits(codec, WCD9335_CPE_FLL_FLL_MODE, + 0x08, 0x08); + + set_bit(CPE_NOMINAL, &tasha->status_mask); + tasha_codec_update_sido_voltage(tasha, SIDO_VOLTAGE_NOMINAL_MV); + + if (tasha_codec_cpe_fll_update_divider(codec, clk_freq)) { + ret = -EINVAL; + goto done; + } + snd_soc_update_bits(codec, WCD9335_CPE_FLL_FLL_MODE, + 0x10, 0x10); + } else { + dev_err(codec->dev, + "%s: Invalid clk_freq request %d for CPE FLL\n", + __func__, clk_freq); + ret = -EINVAL; + } + +done: + snd_soc_update_bits(codec, WCD9335_CPE_FLL_FLL_MODE, + 0x10, 0x00); + snd_soc_update_bits(codec, WCD9335_CPE_FLL_FLL_MODE, + 0x08, 0x00); + return ret; +} + + +static int tasha_codec_cpe_fll_enable(struct snd_soc_codec *codec, + bool enable) +{ + struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent); + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + u8 clk_sel_reg_val = 0x00; + + dev_dbg(codec->dev, "%s: enable = %s\n", + __func__, enable ? "true" : "false"); + + if (enable) { + if (tasha_cdc_is_svs_enabled(tasha)) { + /* FLL enable is always at SVS */ + if (__tasha_cdc_change_cpe_clk(codec, + CPE_FLL_CLK_75MHZ)) { + dev_err(codec->dev, + "%s: clk change to %d failed\n", + __func__, CPE_FLL_CLK_75MHZ); + return -EINVAL; + } + } else { + if (tasha_codec_cpe_fll_update_divider(codec, + CPE_FLL_CLK_75MHZ)) { + dev_err(codec->dev, + "%s: clk change to %d failed\n", + __func__, CPE_FLL_CLK_75MHZ); + return -EINVAL; + } + } + + if (TASHA_IS_1_0(wcd9xxx)) { + tasha_cdc_mclk_enable(codec, true, false); + clk_sel_reg_val = 0x02; + } + + /* Setup CPE reference clk */ + snd_soc_update_bits(codec, WCD9335_ANA_CLK_TOP, + 0x02, clk_sel_reg_val); + + /* enable CPE FLL reference clk */ + snd_soc_update_bits(codec, WCD9335_ANA_CLK_TOP, + 0x01, 0x01); + + /* program the PLL */ + snd_soc_update_bits(codec, WCD9335_CPE_FLL_USER_CTL_0, + 0x01, 0x01); + + /* TEST clk setting */ + snd_soc_update_bits(codec, WCD9335_CPE_FLL_TEST_CTL_0, + 0x80, 0x80); + /* set FLL mode to HW controlled */ + snd_soc_update_bits(codec, WCD9335_CPE_FLL_FLL_MODE, + 0x60, 0x00); + snd_soc_write(codec, WCD9335_CPE_FLL_FLL_MODE, 0x80); + } else { + /* disable CPE FLL reference clk */ + snd_soc_update_bits(codec, WCD9335_ANA_CLK_TOP, + 0x01, 0x00); + /* undo TEST clk setting */ + snd_soc_update_bits(codec, WCD9335_CPE_FLL_TEST_CTL_0, + 0x80, 0x00); + /* undo FLL mode to HW control */ + snd_soc_write(codec, WCD9335_CPE_FLL_FLL_MODE, 0x00); + snd_soc_update_bits(codec, WCD9335_CPE_FLL_FLL_MODE, + 0x60, 0x20); + /* undo the PLL */ + snd_soc_update_bits(codec, WCD9335_CPE_FLL_USER_CTL_0, + 0x01, 0x00); + + if (TASHA_IS_1_0(wcd9xxx)) + tasha_cdc_mclk_enable(codec, false, false); + + /* + * FLL could get disabled while at nominal, + * scale it back to SVS + */ + if (tasha_cdc_is_svs_enabled(tasha)) + __tasha_cdc_change_cpe_clk(codec, + CPE_FLL_CLK_75MHZ); + } + + return 0; + +} + +static void tasha_cdc_query_cpe_clk_plan(void *data, + struct cpe_svc_cfg_clk_plan *clk_freq) +{ + struct snd_soc_codec *codec = data; + struct tasha_priv *tasha; + u32 cpe_clk_khz; + + if (!codec) { + pr_err("%s: Invalid codec handle\n", + __func__); + return; + } + + tasha = snd_soc_codec_get_drvdata(codec); + cpe_clk_khz = tasha->current_cpe_clk_freq / 1000; + + dev_dbg(codec->dev, + "%s: current_clk_freq = %u\n", + __func__, tasha->current_cpe_clk_freq); + + clk_freq->current_clk_feq = cpe_clk_khz; + clk_freq->num_clk_freqs = 2; + + if (tasha_cdc_is_svs_enabled(tasha)) { + clk_freq->clk_freqs[0] = CPE_FLL_CLK_75MHZ / 1000; + clk_freq->clk_freqs[1] = CPE_FLL_CLK_150MHZ / 1000; + } else { + clk_freq->clk_freqs[0] = CPE_FLL_CLK_75MHZ; + clk_freq->clk_freqs[1] = CPE_FLL_CLK_150MHZ; + } +} + +static void tasha_cdc_change_cpe_clk(void *data, + u32 clk_freq) +{ + struct snd_soc_codec *codec = data; + struct tasha_priv *tasha; + u32 cpe_clk_khz, req_freq = 0; + + if (!codec) { + pr_err("%s: Invalid codec handle\n", + __func__); + return; + } + + tasha = snd_soc_codec_get_drvdata(codec); + cpe_clk_khz = tasha->current_cpe_clk_freq / 1000; + + if (tasha_cdc_is_svs_enabled(tasha)) { + if ((clk_freq * 1000) <= CPE_FLL_CLK_75MHZ) + req_freq = CPE_FLL_CLK_75MHZ; + else + req_freq = CPE_FLL_CLK_150MHZ; + } + + dev_dbg(codec->dev, + "%s: requested clk_freq = %u, current clk_freq = %u\n", + __func__, clk_freq * 1000, + tasha->current_cpe_clk_freq); + + if (tasha_cdc_is_svs_enabled(tasha)) { + if (__tasha_cdc_change_cpe_clk(codec, req_freq)) + dev_err(codec->dev, + "%s: clock/voltage scaling failed\n", + __func__); + } +} + +static int tasha_codec_slim_reserve_bw(struct snd_soc_codec *codec, + u32 bw_ops, bool commit) +{ + struct wcd9xxx *wcd9xxx; + + if (!codec) { + pr_err("%s: Invalid handle to codec\n", + __func__); + return -EINVAL; + } + + wcd9xxx = dev_get_drvdata(codec->dev->parent); + + if (!wcd9xxx) { + dev_err(codec->dev, "%s: Invalid parent drv_data\n", + __func__); + return -EINVAL; + } + + return wcd9xxx_slim_reserve_bw(wcd9xxx, bw_ops, commit); +} + +static int tasha_codec_vote_max_bw(struct snd_soc_codec *codec, + bool vote) +{ + u32 bw_ops; + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + + if (tasha->intf_type == WCD9XXX_INTERFACE_TYPE_I2C) + return 0; + + mutex_lock(&tasha->sb_clk_gear_lock); + if (vote) { + tasha->ref_count++; + if (tasha->ref_count == 1) { + bw_ops = SLIM_BW_CLK_GEAR_9; + tasha_codec_slim_reserve_bw(codec, + bw_ops, true); + } + } else if (!vote && tasha->ref_count > 0) { + tasha->ref_count--; + if (tasha->ref_count == 0) { + bw_ops = SLIM_BW_UNVOTE; + tasha_codec_slim_reserve_bw(codec, + bw_ops, true); + } + }; + + dev_dbg(codec->dev, "%s Value of counter after vote or un-vote is %d\n", + __func__, tasha->ref_count); + + mutex_unlock(&tasha->sb_clk_gear_lock); + + return 0; +} + +static int tasha_cpe_err_irq_control(struct snd_soc_codec *codec, + enum cpe_err_irq_cntl_type cntl_type, u8 *status) +{ + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + u8 irq_bits; + + if (TASHA_IS_2_0(tasha->wcd9xxx)) + irq_bits = 0xFF; + else + irq_bits = 0x3F; + + if (status) + irq_bits = (*status) & irq_bits; + + switch (cntl_type) { + case CPE_ERR_IRQ_MASK: + snd_soc_update_bits(codec, + WCD9335_CPE_SS_SS_ERROR_INT_MASK, + irq_bits, irq_bits); + break; + case CPE_ERR_IRQ_UNMASK: + snd_soc_update_bits(codec, + WCD9335_CPE_SS_SS_ERROR_INT_MASK, + irq_bits, 0x00); + break; + case CPE_ERR_IRQ_CLEAR: + snd_soc_write(codec, WCD9335_CPE_SS_SS_ERROR_INT_CLEAR, + irq_bits); + break; + case CPE_ERR_IRQ_STATUS: + if (!status) + return -EINVAL; + *status = snd_soc_read(codec, + WCD9335_CPE_SS_SS_ERROR_INT_STATUS); + break; + } + + return 0; +} + +static const struct wcd_cpe_cdc_cb cpe_cb = { + .cdc_clk_en = tasha_codec_internal_rco_ctrl, + .cpe_clk_en = tasha_codec_cpe_fll_enable, + .get_afe_out_port_id = tasha_codec_get_mad_port_id, + .lab_cdc_ch_ctl = tasha_codec_enable_slimtx_mad, + .cdc_ext_clk = tasha_cdc_mclk_enable, + .bus_vote_bw = tasha_codec_vote_max_bw, + .cpe_err_irq_control = tasha_cpe_err_irq_control, +}; + +static struct cpe_svc_init_param cpe_svc_params = { + .version = CPE_SVC_INIT_PARAM_V1, + .query_freq_plans_cb = tasha_cdc_query_cpe_clk_plan, + .change_freq_plan_cb = tasha_cdc_change_cpe_clk, +}; + +static int tasha_cpe_initialize(struct snd_soc_codec *codec) +{ + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + struct wcd_cpe_params cpe_params; + + memset(&cpe_params, 0, + sizeof(struct wcd_cpe_params)); + cpe_params.codec = codec; + cpe_params.get_cpe_core = tasha_codec_get_cpe_core; + cpe_params.cdc_cb = &cpe_cb; + cpe_params.dbg_mode = cpe_debug_mode; + cpe_params.cdc_major_ver = CPE_SVC_CODEC_WCD9335; + cpe_params.cdc_minor_ver = CPE_SVC_CODEC_V1P0; + cpe_params.cdc_id = CPE_SVC_CODEC_WCD9335; + + cpe_params.cdc_irq_info.cpe_engine_irq = + WCD9335_IRQ_SVA_OUTBOX1; + cpe_params.cdc_irq_info.cpe_err_irq = + WCD9335_IRQ_SVA_ERROR; + cpe_params.cdc_irq_info.cpe_fatal_irqs = + TASHA_CPE_FATAL_IRQS; + + cpe_svc_params.context = codec; + cpe_params.cpe_svc_params = &cpe_svc_params; + + tasha->cpe_core = wcd_cpe_init("cpe_9335", codec, + &cpe_params); + if (IS_ERR_OR_NULL(tasha->cpe_core)) { + dev_err(codec->dev, + "%s: Failed to enable CPE\n", + __func__); + return -EINVAL; + } + + return 0; +} + +static const struct wcd_resmgr_cb tasha_resmgr_cb = { + .cdc_rco_ctrl = __tasha_codec_internal_rco_ctrl, +}; + +static int tasha_device_down(struct wcd9xxx *wcd9xxx) +{ + struct snd_soc_codec *codec; + struct tasha_priv *priv; + int count; + int i = 0; + + codec = (struct snd_soc_codec *)(wcd9xxx->ssr_priv); + priv = snd_soc_codec_get_drvdata(codec); + snd_event_notify(priv->dev->parent, SND_EVENT_DOWN); + wcd_cpe_ssr_event(priv->cpe_core, WCD_CPE_BUS_DOWN_EVENT); + + if (!priv->swr_ctrl_data) + return -EINVAL; + + for (i = 0; i < priv->nr; i++) { + if (is_snd_event_fwk_enabled()) + swrm_wcd_notify( + priv->swr_ctrl_data[i].swr_pdev, + SWR_DEVICE_SSR_DOWN, NULL); + swrm_wcd_notify(priv->swr_ctrl_data[i].swr_pdev, + SWR_DEVICE_DOWN, NULL); + } + + if (!is_snd_event_fwk_enabled()) + snd_soc_card_change_online_state(codec->component.card, 0); + for (count = 0; count < NUM_CODEC_DAIS; count++) + priv->dai[count].bus_down_in_recovery = true; + + priv->resmgr->sido_input_src = SIDO_SOURCE_INTERNAL; + + return 0; +} + +static int tasha_post_reset_cb(struct wcd9xxx *wcd9xxx) +{ + int i, ret = 0; + struct wcd9xxx *control; + struct snd_soc_codec *codec; + struct tasha_priv *tasha; + struct wcd9xxx_pdata *pdata; + + codec = (struct snd_soc_codec *)(wcd9xxx->ssr_priv); + tasha = snd_soc_codec_get_drvdata(codec); + control = dev_get_drvdata(codec->dev->parent); + + wcd9xxx_set_power_state(tasha->wcd9xxx, + WCD_REGION_POWER_COLLAPSE_REMOVE, + WCD9XXX_DIG_CORE_REGION_1); + + mutex_lock(&tasha->codec_mutex); + + tasha_slimbus_slave_port_cfg.slave_dev_intfdev_la = + control->slim_slave->laddr; + tasha_slimbus_slave_port_cfg.slave_dev_pgd_la = + control->slim->laddr; + tasha_init_slim_slave_cfg(codec); + if (tasha->machine_codec_event_cb) + tasha->machine_codec_event_cb(codec, + WCD9335_CODEC_EVENT_CODEC_UP); + if (!is_snd_event_fwk_enabled()) + snd_soc_card_change_online_state(codec->component.card, 1); + + /* Class-H Init*/ + wcd_clsh_init(&tasha->clsh_d); + + for (i = 0; i < TASHA_MAX_MICBIAS; i++) + tasha->micb_ref[i] = 0; + + tasha_update_reg_defaults(tasha); + + tasha->codec = codec; + + dev_dbg(codec->dev, "%s: MCLK Rate = %x\n", + __func__, control->mclk_rate); + + if (control->mclk_rate == TASHA_MCLK_CLK_12P288MHZ) + snd_soc_update_bits(codec, WCD9335_CODEC_RPM_CLK_MCLK_CFG, + 0x03, 0x00); + else if (control->mclk_rate == TASHA_MCLK_CLK_9P6MHZ) + snd_soc_update_bits(codec, WCD9335_CODEC_RPM_CLK_MCLK_CFG, + 0x03, 0x01); + tasha_codec_init_reg(codec); + + wcd_resmgr_post_ssr_v2(tasha->resmgr); + + tasha_enable_efuse_sensing(codec); + + regcache_mark_dirty(codec->component.regmap); + regcache_sync(codec->component.regmap); + + pdata = dev_get_platdata(codec->dev->parent); + ret = tasha_handle_pdata(tasha, pdata); + if (ret < 0) + dev_err(codec->dev, "%s: invalid pdata\n", __func__); + + /* Reset reference counter for voting for max bw */ + tasha->ref_count = 0; + /* MBHC Init */ + wcd_mbhc_deinit(&tasha->mbhc); + tasha->mbhc_started = false; + + /* Initialize MBHC module */ + ret = wcd_mbhc_init(&tasha->mbhc, codec, &mbhc_cb, &intr_ids, + wcd_mbhc_registers, TASHA_ZDET_SUPPORTED); + if (ret) + dev_err(codec->dev, "%s: mbhc initialization failed\n", + __func__); + else + tasha_mbhc_hs_detect(codec, tasha->mbhc.mbhc_cfg); + + tasha_cleanup_irqs(tasha); + ret = tasha_setup_irqs(tasha); + if (ret) { + dev_err(codec->dev, "%s: tasha irq setup failed %d\n", + __func__, ret); + goto err; + } + + if (!tasha->swr_ctrl_data) { + ret = -EINVAL; + goto err; + } + + if (is_snd_event_fwk_enabled()) { + for (i = 0; i < tasha->nr; i++) + swrm_wcd_notify( + tasha->swr_ctrl_data[i].swr_pdev, + SWR_DEVICE_SSR_UP, NULL); + } + + tasha_set_spkr_mode(codec, tasha->spkr_mode); + wcd_cpe_ssr_event(tasha->cpe_core, WCD_CPE_BUS_UP_EVENT); + snd_event_notify(tasha->dev->parent, SND_EVENT_UP); +err: + mutex_unlock(&tasha->codec_mutex); + return ret; +} + +static struct regulator *tasha_codec_find_ondemand_regulator( + struct snd_soc_codec *codec, const char *name) +{ + int i; + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + struct wcd9xxx *wcd9xxx = tasha->wcd9xxx; + struct wcd9xxx_pdata *pdata = dev_get_platdata(codec->dev->parent); + + for (i = 0; i < wcd9xxx->num_of_supplies; ++i) { + if (pdata->regulator[i].ondemand && + wcd9xxx->supplies[i].supply && + !strcmp(wcd9xxx->supplies[i].supply, name)) + return wcd9xxx->supplies[i].consumer; + } + + dev_dbg(tasha->dev, "Warning: regulator not found:%s\n", + name); + return NULL; +} + +static void tasha_ssr_disable(struct device *dev, void *data) +{ + struct wcd9xxx *wcd9xxx = dev_get_drvdata(dev); + struct tasha_priv *tasha; + struct snd_soc_codec *codec; + int count = 0; + + if (!wcd9xxx) { + dev_dbg(dev, "%s: wcd9xxx pointer NULL.\n", __func__); + return; + } + codec = (struct snd_soc_codec *)(wcd9xxx->ssr_priv); + tasha = snd_soc_codec_get_drvdata(codec); + + for (count = 0; count < NUM_CODEC_DAIS; count++) + tasha->dai[count].bus_down_in_recovery = true; +} + +static const struct snd_event_ops tasha_ssr_ops = { + .disable = tasha_ssr_disable, +}; + +static int tasha_codec_probe(struct snd_soc_codec *codec) +{ + struct wcd9xxx *control; + struct tasha_priv *tasha; + struct wcd9xxx_pdata *pdata; + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + int i, ret; + void *ptr = NULL; + struct regulator *supply; + + control = dev_get_drvdata(codec->dev->parent); + + dev_info(codec->dev, "%s()\n", __func__); + tasha = snd_soc_codec_get_drvdata(codec); + tasha->intf_type = wcd9xxx_get_intf_type(); + + if (tasha->intf_type == WCD9XXX_INTERFACE_TYPE_SLIMBUS) { + control->dev_down = tasha_device_down; + control->post_reset = tasha_post_reset_cb; + control->ssr_priv = (void *)codec; + } + + /* Resource Manager post Init */ + ret = wcd_resmgr_post_init(tasha->resmgr, &tasha_resmgr_cb, codec); + if (ret) { + dev_err(codec->dev, "%s: wcd resmgr post init failed\n", + __func__); + goto err; + } + /* Class-H Init*/ + wcd_clsh_init(&tasha->clsh_d); + /* Default HPH Mode to Class-H HiFi */ + tasha->hph_mode = CLS_H_HIFI; + + tasha->codec = codec; + for (i = 0; i < COMPANDER_MAX; i++) + tasha->comp_enabled[i] = 0; + + tasha->spkr_gain_offset = RX_GAIN_OFFSET_0_DB; + tasha->intf_type = wcd9xxx_get_intf_type(); + tasha_update_reg_reset_values(codec); + pr_debug("%s: MCLK Rate = %x\n", __func__, control->mclk_rate); + if (control->mclk_rate == TASHA_MCLK_CLK_12P288MHZ) + snd_soc_update_bits(codec, WCD9335_CODEC_RPM_CLK_MCLK_CFG, + 0x03, 0x00); + else if (control->mclk_rate == TASHA_MCLK_CLK_9P6MHZ) + snd_soc_update_bits(codec, WCD9335_CODEC_RPM_CLK_MCLK_CFG, + 0x03, 0x01); + tasha_codec_init_reg(codec); + + tasha_enable_efuse_sensing(codec); + + pdata = dev_get_platdata(codec->dev->parent); + ret = tasha_handle_pdata(tasha, pdata); + if (ret < 0) { + pr_err("%s: bad pdata\n", __func__); + goto err; + } + + supply = tasha_codec_find_ondemand_regulator(codec, + on_demand_supply_name[ON_DEMAND_MICBIAS]); + if (supply) { + tasha->on_demand_list[ON_DEMAND_MICBIAS].supply = supply; + tasha->on_demand_list[ON_DEMAND_MICBIAS].ondemand_supply_count = + 0; + } + + tasha->fw_data = devm_kzalloc(codec->dev, + sizeof(*(tasha->fw_data)), GFP_KERNEL); + if (!tasha->fw_data) + goto err; + set_bit(WCD9XXX_ANC_CAL, tasha->fw_data->cal_bit); + set_bit(WCD9XXX_MBHC_CAL, tasha->fw_data->cal_bit); + set_bit(WCD9XXX_MAD_CAL, tasha->fw_data->cal_bit); + set_bit(WCD9XXX_VBAT_CAL, tasha->fw_data->cal_bit); + + ret = wcd_cal_create_hwdep(tasha->fw_data, + WCD9XXX_CODEC_HWDEP_NODE, codec); + if (ret < 0) { + dev_err(codec->dev, "%s hwdep failed %d\n", __func__, ret); + goto err_hwdep; + } + + /* Initialize MBHC module */ + if (TASHA_IS_2_0(tasha->wcd9xxx)) { + wcd_mbhc_registers[WCD_MBHC_FSM_STATUS].reg = + WCD9335_MBHC_FSM_STATUS; + wcd_mbhc_registers[WCD_MBHC_FSM_STATUS].mask = 0x01; + } + ret = wcd_mbhc_init(&tasha->mbhc, codec, &mbhc_cb, &intr_ids, + wcd_mbhc_registers, TASHA_ZDET_SUPPORTED); + if (ret) { + pr_err("%s: mbhc initialization failed\n", __func__); + goto err_hwdep; + } + + ptr = devm_kzalloc(codec->dev, (sizeof(tasha_rx_chs) + + sizeof(tasha_tx_chs)), GFP_KERNEL); + if (!ptr) { + ret = -ENOMEM; + goto err_hwdep; + } + + if (tasha->intf_type == WCD9XXX_INTERFACE_TYPE_I2C) { + snd_soc_dapm_new_controls(dapm, tasha_dapm_i2s_widgets, + ARRAY_SIZE(tasha_dapm_i2s_widgets)); + snd_soc_dapm_add_routes(dapm, audio_i2s_map, + ARRAY_SIZE(audio_i2s_map)); + for (i = 0; i < ARRAY_SIZE(tasha_i2s_dai); i++) { + INIT_LIST_HEAD(&tasha->dai[i].wcd9xxx_ch_list); + init_waitqueue_head(&tasha->dai[i].dai_wait); + } + } else if (tasha->intf_type == WCD9XXX_INTERFACE_TYPE_SLIMBUS) { + for (i = 0; i < NUM_CODEC_DAIS; i++) { + INIT_LIST_HEAD(&tasha->dai[i].wcd9xxx_ch_list); + init_waitqueue_head(&tasha->dai[i].dai_wait); + } + tasha_slimbus_slave_port_cfg.slave_dev_intfdev_la = + control->slim_slave->laddr; + tasha_slimbus_slave_port_cfg.slave_dev_pgd_la = + control->slim->laddr; + tasha_slimbus_slave_port_cfg.slave_port_mapping[0] = + TASHA_TX13; + tasha_init_slim_slave_cfg(codec); + } + + snd_soc_add_codec_controls(codec, impedance_detect_controls, + ARRAY_SIZE(impedance_detect_controls)); + snd_soc_add_codec_controls(codec, hph_type_detect_controls, + ARRAY_SIZE(hph_type_detect_controls)); + + snd_soc_add_codec_controls(codec, + tasha_analog_gain_controls, + ARRAY_SIZE(tasha_analog_gain_controls)); + if (tasha->is_wsa_attach) + snd_soc_add_codec_controls(codec, + tasha_spkr_wsa_controls, + ARRAY_SIZE(tasha_spkr_wsa_controls)); + control->num_rx_port = TASHA_RX_MAX; + control->rx_chs = ptr; + memcpy(control->rx_chs, tasha_rx_chs, sizeof(tasha_rx_chs)); + control->num_tx_port = TASHA_TX_MAX; + control->tx_chs = ptr + sizeof(tasha_rx_chs); + memcpy(control->tx_chs, tasha_tx_chs, sizeof(tasha_tx_chs)); + + snd_soc_dapm_ignore_suspend(dapm, "AIF1 Playback"); + snd_soc_dapm_ignore_suspend(dapm, "AIF1 Capture"); + snd_soc_dapm_ignore_suspend(dapm, "AIF2 Playback"); + snd_soc_dapm_ignore_suspend(dapm, "AIF2 Capture"); + + if (tasha->intf_type == WCD9XXX_INTERFACE_TYPE_SLIMBUS) { + snd_soc_dapm_ignore_suspend(dapm, "AIF3 Playback"); + snd_soc_dapm_ignore_suspend(dapm, "AIF3 Capture"); + snd_soc_dapm_ignore_suspend(dapm, "AIF4 Playback"); + snd_soc_dapm_ignore_suspend(dapm, "AIF Mix Playback"); + snd_soc_dapm_ignore_suspend(dapm, "AIF4 MAD TX"); + snd_soc_dapm_ignore_suspend(dapm, "VIfeed"); + snd_soc_dapm_ignore_suspend(dapm, "AIF5 CPE TX"); + } + + snd_soc_dapm_sync(dapm); + + ret = tasha_setup_irqs(tasha); + if (ret) { + pr_err("%s: tasha irq setup failed %d\n", __func__, ret); + goto err_pdata; + } + + ret = tasha_cpe_initialize(codec); + if (ret) { + dev_err(codec->dev, + "%s: cpe initialization failed, err = %d\n", + __func__, ret); + /* Do not fail probe if CPE failed */ + ret = 0; + } + + for (i = 0; i < TASHA_NUM_DECIMATORS; i++) { + tasha->tx_hpf_work[i].tasha = tasha; + tasha->tx_hpf_work[i].decimator = i; + INIT_DELAYED_WORK(&tasha->tx_hpf_work[i].dwork, + tasha_tx_hpf_corner_freq_callback); + } + + for (i = 0; i < TASHA_NUM_DECIMATORS; i++) { + tasha->tx_mute_dwork[i].tasha = tasha; + tasha->tx_mute_dwork[i].decimator = i; + INIT_DELAYED_WORK(&tasha->tx_mute_dwork[i].dwork, + tasha_tx_mute_update_callback); + } + + tasha->spk_anc_dwork.tasha = tasha; + INIT_DELAYED_WORK(&tasha->spk_anc_dwork.dwork, + tasha_spk_anc_update_callback); + + mutex_lock(&tasha->codec_mutex); + snd_soc_dapm_disable_pin(dapm, "ANC LINEOUT1"); + snd_soc_dapm_disable_pin(dapm, "ANC LINEOUT2"); + snd_soc_dapm_disable_pin(dapm, "ANC LINEOUT1 PA"); + snd_soc_dapm_disable_pin(dapm, "ANC LINEOUT2 PA"); + snd_soc_dapm_disable_pin(dapm, "ANC HPHL"); + snd_soc_dapm_disable_pin(dapm, "ANC HPHR"); + snd_soc_dapm_disable_pin(dapm, "ANC HPHL PA"); + snd_soc_dapm_disable_pin(dapm, "ANC HPHR PA"); + snd_soc_dapm_disable_pin(dapm, "ANC EAR PA"); + snd_soc_dapm_disable_pin(dapm, "ANC EAR"); + snd_soc_dapm_disable_pin(dapm, "ANC SPK1 PA"); + mutex_unlock(&tasha->codec_mutex); + snd_soc_dapm_sync(dapm); + + return ret; + +err_pdata: + devm_kfree(codec->dev, ptr); + control->rx_chs = NULL; + control->tx_chs = NULL; +err_hwdep: + devm_kfree(codec->dev, tasha->fw_data); + tasha->fw_data = NULL; +err: + return ret; +} + +static int tasha_codec_remove(struct snd_soc_codec *codec) +{ + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + struct wcd9xxx *control; + + control = dev_get_drvdata(codec->dev->parent); + control->num_rx_port = 0; + control->num_tx_port = 0; + control->rx_chs = NULL; + control->tx_chs = NULL; + + tasha_cleanup_irqs(tasha); + /* Cleanup MBHC */ + wcd_mbhc_deinit(&tasha->mbhc); + /* Cleanup resmgr */ + + return 0; +} + +static struct regmap *tasha_get_regmap(struct device *dev) +{ + struct wcd9xxx *control = dev_get_drvdata(dev->parent); + + return control->regmap; +} + +static struct snd_soc_codec_driver soc_codec_dev_tasha = { + .probe = tasha_codec_probe, + .remove = tasha_codec_remove, + .get_regmap = tasha_get_regmap, + .component_driver = { + .controls = tasha_snd_controls, + .num_controls = ARRAY_SIZE(tasha_snd_controls), + .dapm_widgets = tasha_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(tasha_dapm_widgets), + .dapm_routes = audio_map, + .num_dapm_routes = ARRAY_SIZE(audio_map), + }, +}; + +#ifdef CONFIG_PM +static int tasha_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct tasha_priv *tasha = platform_get_drvdata(pdev); + + dev_dbg(dev, "%s: system suspend\n", __func__); + if (cancel_delayed_work_sync(&tasha->power_gate_work)) + tasha_codec_power_gate_digital_core(tasha); + + return 0; +} + +static int tasha_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct tasha_priv *tasha = platform_get_drvdata(pdev); + + if (!tasha) { + dev_err(dev, "%s: tasha private data is NULL\n", __func__); + return -EINVAL; + } + dev_dbg(dev, "%s: system resume\n", __func__); + return 0; +} + +static const struct dev_pm_ops tasha_pm_ops = { + .suspend = tasha_suspend, + .resume = tasha_resume, +}; +#endif + +static int tasha_swrm_read(void *handle, int reg) +{ + struct tasha_priv *tasha; + struct wcd9xxx *wcd9xxx; + unsigned short swr_rd_addr_base; + unsigned short swr_rd_data_base; + int val, ret; + + if (!handle) { + pr_err("%s: NULL handle\n", __func__); + return -EINVAL; + } + tasha = (struct tasha_priv *)handle; + wcd9xxx = tasha->wcd9xxx; + + dev_dbg(tasha->dev, "%s: Reading soundwire register, 0x%x\n", + __func__, reg); + swr_rd_addr_base = WCD9335_SWR_AHB_BRIDGE_RD_ADDR_0; + swr_rd_data_base = WCD9335_SWR_AHB_BRIDGE_RD_DATA_0; + /* read_lock */ + mutex_lock(&tasha->swr_read_lock); + ret = regmap_bulk_write(wcd9xxx->regmap, swr_rd_addr_base, + (u8 *)®, 4); + if (ret < 0) { + pr_err("%s: RD Addr Failure\n", __func__); + goto err; + } + /* Check for RD status */ + ret = regmap_bulk_read(wcd9xxx->regmap, swr_rd_data_base, + (u8 *)&val, 4); + if (ret < 0) { + pr_err("%s: RD Data Failure\n", __func__); + goto err; + } + ret = val; +err: + /* read_unlock */ + mutex_unlock(&tasha->swr_read_lock); + return ret; +} + +static int tasha_swrm_i2s_bulk_write(struct wcd9xxx *wcd9xxx, + struct wcd9xxx_reg_val *bulk_reg, + size_t len) +{ + int i, ret = 0; + unsigned short swr_wr_addr_base; + unsigned short swr_wr_data_base; + + swr_wr_addr_base = WCD9335_SWR_AHB_BRIDGE_WR_ADDR_0; + swr_wr_data_base = WCD9335_SWR_AHB_BRIDGE_WR_DATA_0; + + for (i = 0; i < (len * 2); i += 2) { + /* First Write the Data to register */ + ret = regmap_bulk_write(wcd9xxx->regmap, + swr_wr_data_base, bulk_reg[i].buf, 4); + if (ret < 0) { + dev_err(wcd9xxx->dev, "%s: WR Data Failure\n", + __func__); + break; + } + /* Next Write Address */ + ret = regmap_bulk_write(wcd9xxx->regmap, + swr_wr_addr_base, bulk_reg[i+1].buf, 4); + if (ret < 0) { + dev_err(wcd9xxx->dev, "%s: WR Addr Failure\n", + __func__); + break; + } + } + return ret; +} + +static int tasha_swrm_bulk_write(void *handle, u32 *reg, u32 *val, size_t len) +{ + struct tasha_priv *tasha; + struct wcd9xxx *wcd9xxx; + struct wcd9xxx_reg_val *bulk_reg; + unsigned short swr_wr_addr_base; + unsigned short swr_wr_data_base; + int i, j, ret; + + if (!handle) { + pr_err("%s: NULL handle\n", __func__); + return -EINVAL; + } + if (len <= 0) { + pr_err("%s: Invalid size: %zu\n", __func__, len); + return -EINVAL; + } + tasha = (struct tasha_priv *)handle; + wcd9xxx = tasha->wcd9xxx; + + swr_wr_addr_base = WCD9335_SWR_AHB_BRIDGE_WR_ADDR_0; + swr_wr_data_base = WCD9335_SWR_AHB_BRIDGE_WR_DATA_0; + + bulk_reg = kzalloc((2 * len * sizeof(struct wcd9xxx_reg_val)), + GFP_KERNEL); + if (!bulk_reg) + return -ENOMEM; + + for (i = 0, j = 0; i < (len * 2); i += 2, j++) { + bulk_reg[i].reg = swr_wr_data_base; + bulk_reg[i].buf = (u8 *)(&val[j]); + bulk_reg[i].bytes = 4; + bulk_reg[i+1].reg = swr_wr_addr_base; + bulk_reg[i+1].buf = (u8 *)(®[j]); + bulk_reg[i+1].bytes = 4; + } + mutex_lock(&tasha->swr_write_lock); + + if (wcd9xxx_get_intf_type() == WCD9XXX_INTERFACE_TYPE_I2C) { + ret = tasha_swrm_i2s_bulk_write(wcd9xxx, bulk_reg, len); + if (ret) { + dev_err(tasha->dev, "%s: i2s bulk write failed, ret: %d\n", + __func__, ret); + } + } else { + ret = wcd9xxx_slim_bulk_write(wcd9xxx, bulk_reg, + (len * 2), false); + if (ret) { + dev_err(tasha->dev, "%s: swrm bulk write failed, ret: %d\n", + __func__, ret); + } + } + + mutex_unlock(&tasha->swr_write_lock); + kfree(bulk_reg); + + return ret; +} + +static int tasha_swrm_write(void *handle, int reg, int val) +{ + struct tasha_priv *tasha; + struct wcd9xxx *wcd9xxx; + unsigned short swr_wr_addr_base; + unsigned short swr_wr_data_base; + struct wcd9xxx_reg_val bulk_reg[2]; + int ret; + + if (!handle) { + pr_err("%s: NULL handle\n", __func__); + return -EINVAL; + } + tasha = (struct tasha_priv *)handle; + wcd9xxx = tasha->wcd9xxx; + + swr_wr_addr_base = WCD9335_SWR_AHB_BRIDGE_WR_ADDR_0; + swr_wr_data_base = WCD9335_SWR_AHB_BRIDGE_WR_DATA_0; + + /* First Write the Data to register */ + bulk_reg[0].reg = swr_wr_data_base; + bulk_reg[0].buf = (u8 *)(&val); + bulk_reg[0].bytes = 4; + bulk_reg[1].reg = swr_wr_addr_base; + bulk_reg[1].buf = (u8 *)(®); + bulk_reg[1].bytes = 4; + + mutex_lock(&tasha->swr_write_lock); + + if (wcd9xxx_get_intf_type() == WCD9XXX_INTERFACE_TYPE_I2C) { + ret = tasha_swrm_i2s_bulk_write(wcd9xxx, bulk_reg, 1); + if (ret) { + dev_err(tasha->dev, "%s: i2s swrm write failed, ret: %d\n", + __func__, ret); + } + } else { + ret = wcd9xxx_slim_bulk_write(wcd9xxx, bulk_reg, 2, false); + if (ret < 0) + pr_err("%s: WR Data Failure\n", __func__); + } + + mutex_unlock(&tasha->swr_write_lock); + return ret; +} + +static int tasha_swrm_clock(void *handle, bool enable) +{ + struct tasha_priv *tasha = (struct tasha_priv *) handle; + + mutex_lock(&tasha->swr_clk_lock); + + dev_dbg(tasha->dev, "%s: swrm clock %s\n", + __func__, (enable?"enable" : "disable")); + if (enable) { + tasha->swr_clk_users++; + if (tasha->swr_clk_users == 1) { + if (TASHA_IS_2_0(tasha->wcd9xxx)) + regmap_update_bits( + tasha->wcd9xxx->regmap, + WCD9335_TEST_DEBUG_NPL_DLY_TEST_1, + 0x10, 0x00); + __tasha_cdc_mclk_enable(tasha, true); + regmap_update_bits(tasha->wcd9xxx->regmap, + WCD9335_CDC_CLK_RST_CTRL_SWR_CONTROL, + 0x01, 0x01); + } + } else { + tasha->swr_clk_users--; + if (tasha->swr_clk_users == 0) { + regmap_update_bits(tasha->wcd9xxx->regmap, + WCD9335_CDC_CLK_RST_CTRL_SWR_CONTROL, + 0x01, 0x00); + __tasha_cdc_mclk_enable(tasha, false); + if (TASHA_IS_2_0(tasha->wcd9xxx)) + regmap_update_bits( + tasha->wcd9xxx->regmap, + WCD9335_TEST_DEBUG_NPL_DLY_TEST_1, + 0x10, 0x10); + } + } + dev_dbg(tasha->dev, "%s: swrm clock users %d\n", + __func__, tasha->swr_clk_users); + mutex_unlock(&tasha->swr_clk_lock); + return 0; +} + +static int tasha_swrm_handle_irq(void *handle, + irqreturn_t (*swrm_irq_handler)(int irq, + void *data), + void *swrm_handle, + int action) +{ + struct tasha_priv *tasha; + int ret = 0; + struct wcd9xxx *wcd9xxx; + + if (!handle) { + pr_err("%s: null handle received\n", __func__); + return -EINVAL; + } + tasha = (struct tasha_priv *) handle; + wcd9xxx = tasha->wcd9xxx; + + if (action) { + ret = wcd9xxx_request_irq(&wcd9xxx->core_res, + WCD9335_IRQ_SOUNDWIRE, + swrm_irq_handler, + "Tasha SWR Master", swrm_handle); + if (ret) + dev_err(tasha->dev, "%s: Failed to request irq %d\n", + __func__, WCD9335_IRQ_SOUNDWIRE); + } else + wcd9xxx_free_irq(&wcd9xxx->core_res, WCD9335_IRQ_SOUNDWIRE, + swrm_handle); + + return ret; +} + +static void tasha_add_child_devices(struct work_struct *work) +{ + struct tasha_priv *tasha; + struct platform_device *pdev; + struct device_node *node; + struct wcd9xxx *wcd9xxx; + struct tasha_swr_ctrl_data *swr_ctrl_data = NULL, *temp; + int ret, ctrl_num = 0; + struct wcd_swr_ctrl_platform_data *platdata; + char plat_dev_name[WCD9335_STRING_LEN]; + + tasha = container_of(work, struct tasha_priv, + tasha_add_child_devices_work); + if (!tasha) { + pr_err("%s: Memory for WCD9335 does not exist\n", + __func__); + return; + } + wcd9xxx = tasha->wcd9xxx; + if (!wcd9xxx) { + pr_err("%s: Memory for WCD9XXX does not exist\n", + __func__); + return; + } + if (!wcd9xxx->dev->of_node) { + pr_err("%s: DT node for wcd9xxx does not exist\n", + __func__); + return; + } + + platdata = &tasha->swr_plat_data; + tasha->child_count = 0; + + for_each_child_of_node(wcd9xxx->dev->of_node, node) { + if (!strcmp(node->name, "swr_master")) + strlcpy(plat_dev_name, "tasha_swr_ctrl", + (WCD9335_STRING_LEN - 1)); + else if (strnstr(node->name, "msm_cdc_pinctrl", + strlen("msm_cdc_pinctrl")) != NULL) + strlcpy(plat_dev_name, node->name, + (WCD9335_STRING_LEN - 1)); + else + continue; + + pdev = platform_device_alloc(plat_dev_name, -1); + if (!pdev) { + dev_err(wcd9xxx->dev, "%s: pdev memory alloc failed\n", + __func__); + ret = -ENOMEM; + goto err; + } + pdev->dev.parent = tasha->dev; + pdev->dev.of_node = node; + + if (!strcmp(node->name, "swr_master")) { + ret = platform_device_add_data(pdev, platdata, + sizeof(*platdata)); + if (ret) { + dev_err(&pdev->dev, + "%s: cannot add plat data ctrl:%d\n", + __func__, ctrl_num); + goto fail_pdev_add; + } + tasha->is_wsa_attach = true; + } + + ret = platform_device_add(pdev); + if (ret) { + dev_err(&pdev->dev, + "%s: Cannot add platform device\n", + __func__); + goto fail_pdev_add; + } + + if (!strcmp(node->name, "swr_master")) { + temp = krealloc(swr_ctrl_data, + (ctrl_num + 1) * sizeof( + struct tasha_swr_ctrl_data), + GFP_KERNEL); + if (!temp) { + dev_err(wcd9xxx->dev, "out of memory\n"); + ret = -ENOMEM; + goto err; + } + swr_ctrl_data = temp; + swr_ctrl_data[ctrl_num].swr_pdev = pdev; + ctrl_num++; + dev_dbg(&pdev->dev, + "%s: Added soundwire ctrl device(s)\n", + __func__); + tasha->nr = ctrl_num; + tasha->swr_ctrl_data = swr_ctrl_data; + } + + if (tasha->child_count < WCD9335_CHILD_DEVICES_MAX) + tasha->pdev_child_devices[tasha->child_count++] = pdev; + else + goto err; + } + + return; +fail_pdev_add: + platform_device_put(pdev); +err: + return; +} + +/* + * tasha_codec_ver: to get tasha codec version + * @codec: handle to snd_soc_codec * + * return enum codec_variant - version + */ +enum codec_variant tasha_codec_ver(void) +{ + return codec_ver; +} +EXPORT_SYMBOL(tasha_codec_ver); + +static int __tasha_enable_efuse_sensing(struct tasha_priv *tasha) +{ + int val, rc; + + __tasha_cdc_mclk_enable(tasha, true); + + regmap_update_bits(tasha->wcd9xxx->regmap, + WCD9335_CHIP_TIER_CTRL_EFUSE_CTL, 0x1E, 0x20); + regmap_update_bits(tasha->wcd9xxx->regmap, + WCD9335_CHIP_TIER_CTRL_EFUSE_CTL, 0x01, 0x01); + + /* + * 5ms sleep required after enabling efuse control + * before checking the status. + */ + usleep_range(5000, 5500); + rc = regmap_read(tasha->wcd9xxx->regmap, + WCD9335_CHIP_TIER_CTRL_EFUSE_STATUS, &val); + + if (rc || (!(val & 0x01))) + WARN(1, "%s: Efuse sense is not complete\n", __func__); + + __tasha_cdc_mclk_enable(tasha, false); + + return rc; +} + +void tasha_get_codec_ver(struct tasha_priv *tasha) +{ + int i; + int val; + struct tasha_reg_mask_val codec_reg[] = { + {WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT10, 0xFF, 0xFF}, + {WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT11, 0xFF, 0x83}, + {WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT12, 0xFF, 0x0A}, + }; + + __tasha_enable_efuse_sensing(tasha); + for (i = 0; i < ARRAY_SIZE(codec_reg); i++) { + regmap_read(tasha->wcd9xxx->regmap, codec_reg[i].reg, &val); + if (!(val && codec_reg[i].val)) { + codec_ver = WCD9335; + goto ret; + } + } + codec_ver = WCD9326; +ret: + pr_debug("%s: codec is %d\n", __func__, codec_ver); +} +EXPORT_SYMBOL(tasha_get_codec_ver); + +static int tasha_probe(struct platform_device *pdev) +{ + int ret = 0; + struct tasha_priv *tasha; + struct clk *wcd_ext_clk, *wcd_native_clk; + struct wcd9xxx_resmgr_v2 *resmgr; + struct wcd9xxx_power_region *cdc_pwr; + + if (wcd9xxx_get_intf_type() == WCD9XXX_INTERFACE_TYPE_I2C) { + if (apr_get_subsys_state() == APR_SUBSYS_DOWN) { + dev_err(&pdev->dev, "%s: dsp down\n", __func__); + return -EPROBE_DEFER; + } + } + + tasha = devm_kzalloc(&pdev->dev, sizeof(struct tasha_priv), + GFP_KERNEL); + if (!tasha) + return -ENOMEM; + platform_set_drvdata(pdev, tasha); + + tasha->wcd9xxx = dev_get_drvdata(pdev->dev.parent); + tasha->dev = &pdev->dev; + INIT_DELAYED_WORK(&tasha->power_gate_work, tasha_codec_power_gate_work); + mutex_init(&tasha->power_lock); + mutex_init(&tasha->sido_lock); + INIT_WORK(&tasha->tasha_add_child_devices_work, + tasha_add_child_devices); + BLOCKING_INIT_NOTIFIER_HEAD(&tasha->notifier); + mutex_init(&tasha->micb_lock); + mutex_init(&tasha->swr_read_lock); + mutex_init(&tasha->swr_write_lock); + mutex_init(&tasha->swr_clk_lock); + mutex_init(&tasha->sb_clk_gear_lock); + mutex_init(&tasha->mclk_lock); + + cdc_pwr = devm_kzalloc(&pdev->dev, sizeof(struct wcd9xxx_power_region), + GFP_KERNEL); + if (!cdc_pwr) { + ret = -ENOMEM; + goto err_cdc_pwr; + } + tasha->wcd9xxx->wcd9xxx_pwr[WCD9XXX_DIG_CORE_REGION_1] = cdc_pwr; + cdc_pwr->pwr_collapse_reg_min = TASHA_DIG_CORE_REG_MIN; + cdc_pwr->pwr_collapse_reg_max = TASHA_DIG_CORE_REG_MAX; + wcd9xxx_set_power_state(tasha->wcd9xxx, + WCD_REGION_POWER_COLLAPSE_REMOVE, + WCD9XXX_DIG_CORE_REGION_1); + + mutex_init(&tasha->codec_mutex); + /* + * Init resource manager so that if child nodes such as SoundWire + * requests for clock, resource manager can honor the request + */ + resmgr = wcd_resmgr_init(&tasha->wcd9xxx->core_res, NULL); + if (IS_ERR(resmgr)) { + ret = PTR_ERR(resmgr); + dev_err(&pdev->dev, "%s: Failed to initialize wcd resmgr\n", + __func__); + goto err_resmgr; + } + tasha->resmgr = resmgr; + tasha->swr_plat_data.handle = (void *) tasha; + tasha->swr_plat_data.read = tasha_swrm_read; + tasha->swr_plat_data.write = tasha_swrm_write; + tasha->swr_plat_data.bulk_write = tasha_swrm_bulk_write; + tasha->swr_plat_data.clk = tasha_swrm_clock; + tasha->swr_plat_data.handle_irq = tasha_swrm_handle_irq; + + /* Register for Clock */ + wcd_ext_clk = clk_get(tasha->wcd9xxx->dev, "wcd_clk"); + if (IS_ERR(wcd_ext_clk)) { + dev_err(tasha->wcd9xxx->dev, "%s: clk get %s failed\n", + __func__, "wcd_ext_clk"); + goto err_clk; + } + tasha->wcd_ext_clk = wcd_ext_clk; + tasha->sido_voltage = SIDO_VOLTAGE_NOMINAL_MV; + set_bit(AUDIO_NOMINAL, &tasha->status_mask); + tasha->sido_ccl_cnt = 0; + + /* Register native clk for 44.1 playback */ + wcd_native_clk = clk_get(tasha->wcd9xxx->dev, "wcd_native_clk"); + if (IS_ERR(wcd_native_clk)) + dev_dbg(tasha->wcd9xxx->dev, "%s: clk get %s failed\n", + __func__, "wcd_native_clk"); + else + tasha->wcd_native_clk = wcd_native_clk; + + if (wcd9xxx_get_intf_type() == WCD9XXX_INTERFACE_TYPE_SLIMBUS) + ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_tasha, + tasha_dai, ARRAY_SIZE(tasha_dai)); + else if (wcd9xxx_get_intf_type() == WCD9XXX_INTERFACE_TYPE_I2C) + ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_tasha, + tasha_i2s_dai, + ARRAY_SIZE(tasha_i2s_dai)); + else + ret = -EINVAL; + if (ret) { + dev_err(&pdev->dev, "%s: Codec registration failed, ret = %d\n", + __func__, ret); + goto err_cdc_reg; + } + /* Update codec register default values */ + tasha_update_reg_defaults(tasha); + schedule_work(&tasha->tasha_add_child_devices_work); + tasha_get_codec_ver(tasha); + ret = snd_event_client_register(pdev->dev.parent, &tasha_ssr_ops, NULL); + if (!ret) { + snd_event_notify(pdev->dev.parent, SND_EVENT_UP); + } else { + pr_err("%s: Registration with SND event fwk failed ret = %d\n", + __func__, ret); + ret = 0; + } + + dev_info(&pdev->dev, "%s: Tasha driver probe done\n", __func__); + return ret; + +err_cdc_reg: + clk_put(tasha->wcd_ext_clk); + if (tasha->wcd_native_clk) + clk_put(tasha->wcd_native_clk); +err_clk: + wcd_resmgr_remove(tasha->resmgr); +err_resmgr: + devm_kfree(&pdev->dev, cdc_pwr); +err_cdc_pwr: + mutex_destroy(&tasha->mclk_lock); + devm_kfree(&pdev->dev, tasha); + return ret; +} + +static int tasha_remove(struct platform_device *pdev) +{ + struct tasha_priv *tasha; + int count = 0; + + tasha = platform_get_drvdata(pdev); + + if (!tasha) + return -EINVAL; + + snd_event_client_deregister(pdev->dev.parent); + for (count = 0; count < tasha->child_count && + count < WCD9335_CHILD_DEVICES_MAX; count++) + platform_device_unregister(tasha->pdev_child_devices[count]); + + mutex_destroy(&tasha->codec_mutex); + clk_put(tasha->wcd_ext_clk); + if (tasha->wcd_native_clk) + clk_put(tasha->wcd_native_clk); + mutex_destroy(&tasha->mclk_lock); + mutex_destroy(&tasha->sb_clk_gear_lock); + snd_soc_unregister_codec(&pdev->dev); + devm_kfree(&pdev->dev, tasha); + return 0; +} + +static struct platform_driver tasha_codec_driver = { + .probe = tasha_probe, + .remove = tasha_remove, + .driver = { + .name = "tasha_codec", + .owner = THIS_MODULE, +#ifdef CONFIG_PM + .pm = &tasha_pm_ops, +#endif + }, +}; + +module_platform_driver(tasha_codec_driver); + +MODULE_DESCRIPTION("Tasha Codec driver"); +MODULE_LICENSE("GPL v2"); diff --git a/audio-kernel/asoc/codecs/wcd9335.h b/audio-kernel/asoc/codecs/wcd9335.h new file mode 100644 index 000000000..f5f103853 --- /dev/null +++ b/audio-kernel/asoc/codecs/wcd9335.h @@ -0,0 +1,238 @@ +/* + * 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 + * 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 WCD9335_H +#define WCD9335_H + +#include +#include +#include +#include "wcd9xxx-slimslave.h" +#include "wcd-mbhc-v2.h" + +#define TASHA_REG_VAL(reg, val) {reg, 0, val} + +#define TASHA_REGISTER_START_OFFSET 0x800 +#define TASHA_SB_PGD_PORT_RX_BASE 0x40 +#define TASHA_SB_PGD_PORT_TX_BASE 0x50 + +#define TASHA_ZDET_SUPPORTED true +/* z value defined in milliohm */ +#define TASHA_ZDET_VAL_32 32000 +#define TASHA_ZDET_VAL_400 400000 +#define TASHA_ZDET_VAL_1200 1200000 +#define TASHA_ZDET_VAL_100K 100000000 +/* z floating defined in ohms */ +#define TASHA_ZDET_FLOATING_IMPEDANCE 0x0FFFFFFE + +#define WCD9335_DMIC_CLK_DIV_2 0x0 +#define WCD9335_DMIC_CLK_DIV_3 0x1 +#define WCD9335_DMIC_CLK_DIV_4 0x2 +#define WCD9335_DMIC_CLK_DIV_6 0x3 +#define WCD9335_DMIC_CLK_DIV_8 0x4 +#define WCD9335_DMIC_CLK_DIV_16 0x5 +#define WCD9335_DMIC_CLK_DRIVE_DEFAULT 0x02 + +#define WCD9335_ANC_DMIC_X2_FULL_RATE 1 +#define WCD9335_ANC_DMIC_X2_HALF_RATE 0 + +/* Number of input and output Slimbus port */ +enum { + TASHA_RX0 = 0, + TASHA_RX1, + TASHA_RX2, + TASHA_RX3, + TASHA_RX4, + TASHA_RX5, + TASHA_RX6, + TASHA_RX7, + TASHA_RX8, + TASHA_RX9, + TASHA_RX10, + TASHA_RX11, + TASHA_RX12, + TASHA_RX_MAX, +}; + +enum { + TASHA_TX0 = 0, + TASHA_TX1, + TASHA_TX2, + TASHA_TX3, + TASHA_TX4, + TASHA_TX5, + TASHA_TX6, + TASHA_TX7, + TASHA_TX8, + TASHA_TX9, + TASHA_TX10, + TASHA_TX11, + TASHA_TX12, + TASHA_TX13, + TASHA_TX14, + TASHA_TX15, + TASHA_TX_MAX, +}; + +enum wcd9335_codec_event { + WCD9335_CODEC_EVENT_CODEC_UP = 0, +}; + +enum tasha_on_demand_supply { + ON_DEMAND_MICBIAS = 0, + ON_DEMAND_SUPPLIES_MAX, +}; + +/* structure used to put the defined + * ondemand supply for codec + * and count being used. + */ +struct on_demand_supply { + struct regulator *supply; + int ondemand_supply_count; +}; + +/* Dai data structure holds the + * dai specific info like rate, + * channel number etc. + */ +struct tasha_codec_dai_data { + u32 rate; + u32 *ch_num; + u32 ch_act; + u32 ch_tot; +}; + +/* Structure used to update codec + * register defaults after reset + */ +struct tasha_reg_mask_val { + u16 reg; + u8 mask; + u8 val; +}; + +/* Selects compander and smart boost settings + * for a given speaker mode + */ +enum { + SPKR_MODE_DEFAULT, + SPKR_MODE_1, /* COMP Gain = 12dB, Smartboost Max = 5.5V */ +}; + +/* + * Rx path gain offsets + */ +enum { + RX_GAIN_OFFSET_M1P5_DB, + RX_GAIN_OFFSET_0_DB, +}; + +#if IS_ENABLED(CONFIG_SND_SOC_WCD9335) +extern void *tasha_get_afe_config(struct snd_soc_codec *codec, + enum afe_config_type config_type); +extern int tasha_cdc_mclk_enable(struct snd_soc_codec *codec, int enable, + bool dapm); +extern int tasha_cdc_mclk_tx_enable(struct snd_soc_codec *codec, int enable, + bool dapm); +extern int tasha_enable_efuse_sensing(struct snd_soc_codec *codec); +extern int tasha_mbhc_hs_detect(struct snd_soc_codec *codec, + struct wcd_mbhc_config *mbhc_cfg); +extern void tasha_mbhc_hs_detect_exit(struct snd_soc_codec *codec); +extern void tasha_mbhc_zdet_gpio_ctrl( + int (*zdet_gpio_cb)(struct snd_soc_codec *codec, bool high), + struct snd_soc_codec *codec); +extern int tasha_codec_info_create_codec_entry( + struct snd_info_entry *codec_root, + struct snd_soc_codec *codec); +extern void tasha_event_register( + int (*machine_event_cb)(struct snd_soc_codec *codec, + enum wcd9335_codec_event), + struct snd_soc_codec *codec); +extern int tasha_codec_enable_standalone_micbias(struct snd_soc_codec *codec, + int micb_num, + bool enable); +extern int tasha_set_spkr_mode(struct snd_soc_codec *codec, int mode); +extern int tasha_set_spkr_gain_offset(struct snd_soc_codec *codec, int offset); +extern enum codec_variant tasha_codec_ver(void); +#else /* CONFIG_SND_SOC_WCD9335 */ +static inline void *tasha_get_afe_config(struct snd_soc_codec *codec, + enum afe_config_type config_type) +{ + return NULL; +} +static inline int tasha_cdc_mclk_enable(struct snd_soc_codec *codec, + int enable, + bool dapm) +{ + return 0; +} +static inline int tasha_cdc_mclk_tx_enable(struct snd_soc_codec *codec, + int enable, + bool dapm) +{ + return 0; +} +static inline int tasha_enable_efuse_sensing(struct snd_soc_codec *codec) +{ + return 0; +} +static inline int tasha_mbhc_hs_detect(struct snd_soc_codec *codec, + struct wcd_mbhc_config *mbhc_cfg) +{ + return 0; +} +static inline void tasha_mbhc_hs_detect_exit(struct snd_soc_codec *codec) +{ + +} +static inline void tasha_mbhc_zdet_gpio_ctrl( + int (*zdet_gpio_cb)(struct snd_soc_codec *codec, bool high), + struct snd_soc_codec *codec) +{ + +} +static inline int tasha_codec_info_create_codec_entry( + struct snd_info_entry *codec_root, + struct snd_soc_codec *codec) +{ + return 0; +} +static inline void tasha_event_register( + int (*machine_event_cb)(struct snd_soc_codec *codec, + enum wcd9335_codec_event), + struct snd_soc_codec *codec) +{ + +} +static inline int tasha_codec_enable_standalone_micbias( + struct snd_soc_codec *codec, + int micb_num, + bool enable) +{ + return 0; +} +static inline int tasha_set_spkr_mode(struct snd_soc_codec *codec, int mode) +{ + return 0; +} +static inline int tasha_set_spkr_gain_offset(struct snd_soc_codec *codec, + int offset) +{ + return 0; +} +static inline enum codec_variant tasha_codec_ver(void) +{ + return 0; +} +#endif /* CONFIG_SND_SOC_WCD9335 */ +#endif diff --git a/audio-kernel/asoc/codecs/wcd9335_irq.h b/audio-kernel/asoc/codecs/wcd9335_irq.h new file mode 100644 index 000000000..c666d3144 --- /dev/null +++ b/audio-kernel/asoc/codecs/wcd9335_irq.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 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 + * 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 __WCD9335_IRQ_H_ +#define __WCD9335_IRQ_H_ + +enum { + /* INTR_REG 0 */ + WCD9335_IRQ_FLL_LOCK_LOSS = 1, + WCD9335_IRQ_HPH_PA_OCPL_FAULT, + WCD9335_IRQ_HPH_PA_OCPR_FAULT, + WCD9335_IRQ_EAR_PA_OCP_FAULT, + WCD9335_IRQ_HPH_PA_CNPL_COMPLETE, + WCD9335_IRQ_HPH_PA_CNPR_COMPLETE, + WCD9335_IRQ_EAR_PA_CNP_COMPLETE, + /* INTR_REG 1 */ + WCD9335_IRQ_MBHC_SW_DET, + WCD9335_IRQ_MBHC_ELECT_INS_REM_DET, + WCD9335_IRQ_MBHC_BUTTON_PRESS_DET, + WCD9335_IRQ_MBHC_BUTTON_RELEASE_DET, + WCD9335_IRQ_MBHC_ELECT_INS_REM_LEG_DET, + WCD9335_IRQ_RESERVED_0, + WCD9335_IRQ_RESERVED_1, + WCD9335_IRQ_RESERVED_2, + /* INTR_REG 2 */ + WCD9335_IRQ_LINE_PA1_CNP_COMPLETE, + WCD9335_IRQ_LINE_PA2_CNP_COMPLETE, + WCD9335_IRQ_LINE_PA3_CNP_COMPLETE, + WCD9335_IRQ_LINE_PA4_CNP_COMPLETE, + WCD9335_IRQ_SOUNDWIRE, + WCD9335_IRQ_VDD_DIG_RAMP_COMPLETE, + WCD9335_IRQ_RCO_ERROR, + WCD9335_IRQ_SVA_ERROR, + /* INTR_REG 3 */ + WCD9335_IRQ_MAD_AUDIO, + WCD9335_IRQ_MAD_BEACON, + WCD9335_IRQ_MAD_ULTRASOUND, + WCD9335_IRQ_VBAT_ATTACK, + WCD9335_IRQ_VBAT_RESTORE, + WCD9335_IRQ_SVA_OUTBOX1, + WCD9335_IRQ_SVA_OUTBOX2, + WCD9335_NUM_IRQS, +}; + +#endif diff --git a/audio-kernel/asoc/codecs/wcd9335_registers.h b/audio-kernel/asoc/codecs/wcd9335_registers.h new file mode 100644 index 000000000..c50430d42 --- /dev/null +++ b/audio-kernel/asoc/codecs/wcd9335_registers.h @@ -0,0 +1,1348 @@ +/* + * Copyright (c) 2015, 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 _WCD9335_REGISTERS_H +#define _WCD9335_REGISTERS_H + +#define WCD9335_PAGE_SIZE 256 +#define WCD9335_NUM_PAGES 256 + +extern const u8 *wcd9335_reg[WCD9335_NUM_PAGES]; + +enum { + PAGE_0 = 0, + PAGE_1, + PAGE_2, + PAGE_6 = 6, + PAGE_10 = 0xA, + PAGE_11, + PAGE_12, + PAGE_13, + PAGE_0X80, +}; + +/* Page-0 Registers */ +#define WCD9335_PAGE0_PAGE_REGISTER 0x0000 +#define WCD9335_CODEC_RPM_CLK_BYPASS 0x0001 +#define WCD9335_CODEC_RPM_CLK_GATE 0x0002 +#define WCD9335_CODEC_RPM_CLK_MCLK_CFG 0x0003 +#define WCD9335_CODEC_RPM_RST_CTL 0x0009 +#define WCD9335_CODEC_RPM_PWR_CDC_DIG_HM_CTL 0x0011 +#define WCD9335_CODEC_RPM_PWR_CPE_DEEPSLP_1 0x0012 +#define WCD9335_CODEC_RPM_PWR_CPE_DEEPSLP_2 0x0013 +#define WCD9335_CODEC_RPM_PWR_CPE_DEEPSLP_3 0x0014 +#define WCD9335_CODEC_RPM_PWR_CPE_IRAM_SHUTDOWN 0x0015 +#define WCD9335_CODEC_RPM_PWR_CPE_DRAM1_SHUTDOWN 0x0016 +#define WCD9335_CODEC_RPM_PWR_CPE_DRAM0_SHUTDOWN_1 0x0017 +#define WCD9335_CODEC_RPM_PWR_CPE_DRAM0_SHUTDOWN_2 0x0018 +#define WCD9335_CODEC_RPM_INT_MASK 0x001d +#define WCD9335_CODEC_RPM_INT_STATUS 0x001e +#define WCD9335_CODEC_RPM_INT_CLEAR 0x001f +#define WCD9335_CHIP_TIER_CTRL_CHIP_ID_BYTE0 0x0021 +#define WCD9335_CHIP_TIER_CTRL_CHIP_ID_BYTE1 0x0022 +#define WCD9335_CHIP_TIER_CTRL_CHIP_ID_BYTE2 0x0023 +#define WCD9335_CHIP_TIER_CTRL_CHIP_ID_BYTE3 0x0024 +#define WCD9335_CHIP_TIER_CTRL_EFUSE_CTL 0x0025 +#define WCD9335_CHIP_TIER_CTRL_EFUSE_TEST0 0x0026 +#define WCD9335_CHIP_TIER_CTRL_EFUSE_TEST1 0x0027 +#define WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT0 0x0029 +#define WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT1 0x002a +#define WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT2 0x002b +#define WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT3 0x002c +#define WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT4 0x002d +#define WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT5 0x002e +#define WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT6 0x002f +#define WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT7 0x0030 +#define WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT8 0x0031 +#define WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT9 0x0032 +#define WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT10 0x0033 +#define WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT11 0x0034 +#define WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT12 0x0035 +#define WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT13 0x0036 +#define WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT14 0x0037 +#define WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT15 0x0038 +#define WCD9335_CHIP_TIER_CTRL_EFUSE_STATUS 0x0039 +#define WCD9335_CHIP_TIER_CTRL_I2C_SLAVE_ID_NONNEGO 0x003a +#define WCD9335_CHIP_TIER_CTRL_I2C_SLAVE_ID_1 0x003b +#define WCD9335_CHIP_TIER_CTRL_I2C_SLAVE_ID_2 0x003c +#define WCD9335_CHIP_TIER_CTRL_I2C_SLAVE_ID_3 0x003d +#define WCD9335_CHIP_TIER_CTRL_ANA_WAIT_STATE_CTL 0x003e +#define WCD9335_CHIP_TIER_CTRL_I2C_ACTIVE 0x003f +#define WCD9335_CHIP_TIER_CTRL_PROC1_MON_CTL 0x0041 +#define WCD9335_CHIP_TIER_CTRL_PROC1_MON_STATUS 0x0042 +#define WCD9335_CHIP_TIER_CTRL_PROC1_MON_CNT_MSB 0x0043 +#define WCD9335_CHIP_TIER_CTRL_PROC1_MON_CNT_LSB 0x0044 +#define WCD9335_CHIP_TIER_CTRL_PROC2_MON_CTL 0x0045 +#define WCD9335_CHIP_TIER_CTRL_PROC2_MON_STATUS 0x0046 +#define WCD9335_CHIP_TIER_CTRL_PROC2_MON_CNT_MSB 0x0047 +#define WCD9335_CHIP_TIER_CTRL_PROC2_MON_CNT_LSB 0x0048 +#define WCD9335_CHIP_TIER_CTRL_PROC3_MON_CTL 0x0049 +#define WCD9335_CHIP_TIER_CTRL_PROC3_MON_STATUS 0x004a +#define WCD9335_CHIP_TIER_CTRL_PROC3_MON_CNT_MSB 0x004b +#define WCD9335_CHIP_TIER_CTRL_PROC3_MON_CNT_LSB 0x004c +#define WCD9335_DATA_HUB_DATA_HUB_RX_I2S_CTL 0x0051 +#define WCD9335_DATA_HUB_DATA_HUB_TX_I2S_CTL 0x0052 +#define WCD9335_DATA_HUB_DATA_HUB_I2S_CLK 0x0053 +#define WCD9335_DATA_HUB_DATA_HUB_RX0_INP_CFG 0x0054 +#define WCD9335_DATA_HUB_DATA_HUB_RX1_INP_CFG 0x0055 +#define WCD9335_DATA_HUB_DATA_HUB_RX2_INP_CFG 0x0056 +#define WCD9335_DATA_HUB_DATA_HUB_RX3_INP_CFG 0x0057 +#define WCD9335_DATA_HUB_DATA_HUB_RX4_INP_CFG 0x0058 +#define WCD9335_DATA_HUB_DATA_HUB_RX5_INP_CFG 0x0059 +#define WCD9335_DATA_HUB_DATA_HUB_RX6_INP_CFG 0x005a +#define WCD9335_DATA_HUB_DATA_HUB_RX7_INP_CFG 0x005b +#define WCD9335_DATA_HUB_DATA_HUB_SB_TX0_INP_CFG 0x0061 +#define WCD9335_DATA_HUB_DATA_HUB_SB_TX1_INP_CFG 0x0062 +#define WCD9335_DATA_HUB_DATA_HUB_SB_TX2_INP_CFG 0x0063 +#define WCD9335_DATA_HUB_DATA_HUB_SB_TX3_INP_CFG 0x0064 +#define WCD9335_DATA_HUB_DATA_HUB_SB_TX4_INP_CFG 0x0065 +#define WCD9335_DATA_HUB_DATA_HUB_SB_TX5_INP_CFG 0x0066 +#define WCD9335_DATA_HUB_DATA_HUB_SB_TX6_INP_CFG 0x0067 +#define WCD9335_DATA_HUB_DATA_HUB_SB_TX7_INP_CFG 0x0068 +#define WCD9335_DATA_HUB_DATA_HUB_SB_TX8_INP_CFG 0x0069 +#define WCD9335_DATA_HUB_DATA_HUB_SB_TX9_INP_CFG 0x006a +#define WCD9335_DATA_HUB_DATA_HUB_SB_TX10_INP_CFG 0x006b +#define WCD9335_DATA_HUB_DATA_HUB_SB_TX11_INP_CFG 0x006c +#define WCD9335_DATA_HUB_DATA_HUB_SB_TX13_INP_CFG 0x006e +#define WCD9335_DATA_HUB_DATA_HUB_SB_TX14_INP_CFG 0x006f +#define WCD9335_DATA_HUB_DATA_HUB_SB_TX15_INP_CFG 0x0070 +#define WCD9335_DATA_HUB_DATA_HUB_TX_I2S_SD0_L_CFG 0x0071 +#define WCD9335_DATA_HUB_DATA_HUB_TX_I2S_SD0_R_CFG 0x0072 +#define WCD9335_DATA_HUB_DATA_HUB_TX_I2S_SD1_L_CFG 0x0073 +#define WCD9335_DATA_HUB_DATA_HUB_TX_I2S_SD1_R_CFG 0x0074 +#define WCD9335_DATA_HUB_NATIVE_FIFO_SYNC 0x0075 +#define WCD9335_DATA_HUB_NATIVE_FIFO_STATUS 0x007D +#define WCD9335_INTR_CFG 0x0081 +#define WCD9335_INTR_CLR_COMMIT 0x0082 +#define WCD9335_INTR_PIN1_MASK0 0x0089 +#define WCD9335_INTR_PIN1_MASK1 0x008a +#define WCD9335_INTR_PIN1_MASK2 0x008b +#define WCD9335_INTR_PIN1_MASK3 0x008c +#define WCD9335_INTR_PIN1_STATUS0 0x0091 +#define WCD9335_INTR_PIN1_STATUS1 0x0092 +#define WCD9335_INTR_PIN1_STATUS2 0x0093 +#define WCD9335_INTR_PIN1_STATUS3 0x0094 +#define WCD9335_INTR_PIN1_CLEAR0 0x0099 +#define WCD9335_INTR_PIN1_CLEAR1 0x009a +#define WCD9335_INTR_PIN1_CLEAR2 0x009b +#define WCD9335_INTR_PIN1_CLEAR3 0x009c +#define WCD9335_INTR_PIN2_MASK0 0x00a1 +#define WCD9335_INTR_PIN2_MASK1 0x00a2 +#define WCD9335_INTR_PIN2_MASK2 0x00a3 +#define WCD9335_INTR_PIN2_MASK3 0x00a4 +#define WCD9335_INTR_PIN2_STATUS0 0x00a9 +#define WCD9335_INTR_PIN2_STATUS1 0x00aa +#define WCD9335_INTR_PIN2_STATUS2 0x00ab +#define WCD9335_INTR_PIN2_STATUS3 0x00ac +#define WCD9335_INTR_PIN2_CLEAR0 0x00b1 +#define WCD9335_INTR_PIN2_CLEAR1 0x00b2 +#define WCD9335_INTR_PIN2_CLEAR2 0x00b3 +#define WCD9335_INTR_PIN2_CLEAR3 0x00b4 +#define WCD9335_INTR_LEVEL0 0x00e1 +#define WCD9335_INTR_LEVEL1 0x00e2 +#define WCD9335_INTR_LEVEL2 0x00e3 +#define WCD9335_INTR_LEVEL3 0x00e4 +#define WCD9335_INTR_BYPASS0 0x00e9 +#define WCD9335_INTR_BYPASS1 0x00ea +#define WCD9335_INTR_BYPASS2 0x00eb +#define WCD9335_INTR_BYPASS3 0x00ec +#define WCD9335_INTR_SET0 0x00f1 +#define WCD9335_INTR_SET1 0x00f2 +#define WCD9335_INTR_SET2 0x00f3 +#define WCD9335_INTR_SET3 0x00f4 + +/* Page-1 Registers */ +#define WCD9335_PAGE1_PAGE_REGISTER 0x0100 +#define WCD9335_CPE_FLL_USER_CTL_0 0x0101 +#define WCD9335_CPE_FLL_USER_CTL_1 0x0102 +#define WCD9335_CPE_FLL_USER_CTL_2 0x0103 +#define WCD9335_CPE_FLL_USER_CTL_3 0x0104 +#define WCD9335_CPE_FLL_USER_CTL_4 0x0105 +#define WCD9335_CPE_FLL_USER_CTL_5 0x0106 +#define WCD9335_CPE_FLL_USER_CTL_6 0x0107 +#define WCD9335_CPE_FLL_USER_CTL_7 0x0108 +#define WCD9335_CPE_FLL_USER_CTL_8 0x0109 +#define WCD9335_CPE_FLL_USER_CTL_9 0x010a +#define WCD9335_CPE_FLL_L_VAL_CTL_0 0x010b +#define WCD9335_CPE_FLL_L_VAL_CTL_1 0x010c +#define WCD9335_CPE_FLL_DSM_FRAC_CTL_0 0x010d +#define WCD9335_CPE_FLL_DSM_FRAC_CTL_1 0x010e +#define WCD9335_CPE_FLL_CONFIG_CTL_0 0x010f +#define WCD9335_CPE_FLL_CONFIG_CTL_1 0x0110 +#define WCD9335_CPE_FLL_CONFIG_CTL_2 0x0111 +#define WCD9335_CPE_FLL_CONFIG_CTL_3 0x0112 +#define WCD9335_CPE_FLL_CONFIG_CTL_4 0x0113 +#define WCD9335_CPE_FLL_TEST_CTL_0 0x0114 +#define WCD9335_CPE_FLL_TEST_CTL_1 0x0115 +#define WCD9335_CPE_FLL_TEST_CTL_2 0x0116 +#define WCD9335_CPE_FLL_TEST_CTL_3 0x0117 +#define WCD9335_CPE_FLL_TEST_CTL_4 0x0118 +#define WCD9335_CPE_FLL_TEST_CTL_5 0x0119 +#define WCD9335_CPE_FLL_TEST_CTL_6 0x011a +#define WCD9335_CPE_FLL_TEST_CTL_7 0x011b +#define WCD9335_CPE_FLL_FREQ_CTL_0 0x011c +#define WCD9335_CPE_FLL_FREQ_CTL_1 0x011d +#define WCD9335_CPE_FLL_FREQ_CTL_2 0x011e +#define WCD9335_CPE_FLL_FREQ_CTL_3 0x011f +#define WCD9335_CPE_FLL_SSC_CTL_0 0x0120 +#define WCD9335_CPE_FLL_SSC_CTL_1 0x0121 +#define WCD9335_CPE_FLL_SSC_CTL_2 0x0122 +#define WCD9335_CPE_FLL_SSC_CTL_3 0x0123 +#define WCD9335_CPE_FLL_FLL_MODE 0x0124 +#define WCD9335_CPE_FLL_STATUS_0 0x0125 +#define WCD9335_CPE_FLL_STATUS_1 0x0126 +#define WCD9335_CPE_FLL_STATUS_2 0x0127 +#define WCD9335_CPE_FLL_STATUS_3 0x0128 +#define WCD9335_I2S_FLL_USER_CTL_0 0x0141 +#define WCD9335_I2S_FLL_USER_CTL_1 0x0142 +#define WCD9335_I2S_FLL_USER_CTL_2 0x0143 +#define WCD9335_I2S_FLL_USER_CTL_3 0x0144 +#define WCD9335_I2S_FLL_USER_CTL_4 0x0145 +#define WCD9335_I2S_FLL_USER_CTL_5 0x0146 +#define WCD9335_I2S_FLL_USER_CTL_6 0x0147 +#define WCD9335_I2S_FLL_USER_CTL_7 0x0148 +#define WCD9335_I2S_FLL_USER_CTL_8 0x0149 +#define WCD9335_I2S_FLL_USER_CTL_9 0x014a +#define WCD9335_I2S_FLL_L_VAL_CTL_0 0x014b +#define WCD9335_I2S_FLL_L_VAL_CTL_1 0x014c +#define WCD9335_I2S_FLL_DSM_FRAC_CTL_0 0x014d +#define WCD9335_I2S_FLL_DSM_FRAC_CTL_1 0x014e +#define WCD9335_I2S_FLL_CONFIG_CTL_0 0x014f +#define WCD9335_I2S_FLL_CONFIG_CTL_1 0x0150 +#define WCD9335_I2S_FLL_CONFIG_CTL_2 0x0151 +#define WCD9335_I2S_FLL_CONFIG_CTL_3 0x0152 +#define WCD9335_I2S_FLL_CONFIG_CTL_4 0x0153 +#define WCD9335_I2S_FLL_TEST_CTL_0 0x0154 +#define WCD9335_I2S_FLL_TEST_CTL_1 0x0155 +#define WCD9335_I2S_FLL_TEST_CTL_2 0x0156 +#define WCD9335_I2S_FLL_TEST_CTL_3 0x0157 +#define WCD9335_I2S_FLL_TEST_CTL_4 0x0158 +#define WCD9335_I2S_FLL_TEST_CTL_5 0x0159 +#define WCD9335_I2S_FLL_TEST_CTL_6 0x015a +#define WCD9335_I2S_FLL_TEST_CTL_7 0x015b +#define WCD9335_I2S_FLL_FREQ_CTL_0 0x015c +#define WCD9335_I2S_FLL_FREQ_CTL_1 0x015d +#define WCD9335_I2S_FLL_FREQ_CTL_2 0x015e +#define WCD9335_I2S_FLL_FREQ_CTL_3 0x015f +#define WCD9335_I2S_FLL_SSC_CTL_0 0x0160 +#define WCD9335_I2S_FLL_SSC_CTL_1 0x0161 +#define WCD9335_I2S_FLL_SSC_CTL_2 0x0162 +#define WCD9335_I2S_FLL_SSC_CTL_3 0x0163 +#define WCD9335_I2S_FLL_FLL_MODE 0x0164 +#define WCD9335_I2S_FLL_STATUS_0 0x0165 +#define WCD9335_I2S_FLL_STATUS_1 0x0166 +#define WCD9335_I2S_FLL_STATUS_2 0x0167 +#define WCD9335_I2S_FLL_STATUS_3 0x0168 +#define WCD9335_SB_FLL_USER_CTL_0 0x0181 +#define WCD9335_SB_FLL_USER_CTL_1 0x0182 +#define WCD9335_SB_FLL_USER_CTL_2 0x0183 +#define WCD9335_SB_FLL_USER_CTL_3 0x0184 +#define WCD9335_SB_FLL_USER_CTL_4 0x0185 +#define WCD9335_SB_FLL_USER_CTL_5 0x0186 +#define WCD9335_SB_FLL_USER_CTL_6 0x0187 +#define WCD9335_SB_FLL_USER_CTL_7 0x0188 +#define WCD9335_SB_FLL_USER_CTL_8 0x0189 +#define WCD9335_SB_FLL_USER_CTL_9 0x018a +#define WCD9335_SB_FLL_L_VAL_CTL_0 0x018b +#define WCD9335_SB_FLL_L_VAL_CTL_1 0x018c +#define WCD9335_SB_FLL_DSM_FRAC_CTL_0 0x018d +#define WCD9335_SB_FLL_DSM_FRAC_CTL_1 0x018e +#define WCD9335_SB_FLL_CONFIG_CTL_0 0x018f +#define WCD9335_SB_FLL_CONFIG_CTL_1 0x0190 +#define WCD9335_SB_FLL_CONFIG_CTL_2 0x0191 +#define WCD9335_SB_FLL_CONFIG_CTL_3 0x0192 +#define WCD9335_SB_FLL_CONFIG_CTL_4 0x0193 +#define WCD9335_SB_FLL_TEST_CTL_0 0x0194 +#define WCD9335_SB_FLL_TEST_CTL_1 0x0195 +#define WCD9335_SB_FLL_TEST_CTL_2 0x0196 +#define WCD9335_SB_FLL_TEST_CTL_3 0x0197 +#define WCD9335_SB_FLL_TEST_CTL_4 0x0198 +#define WCD9335_SB_FLL_TEST_CTL_5 0x0199 +#define WCD9335_SB_FLL_TEST_CTL_6 0x019a +#define WCD9335_SB_FLL_TEST_CTL_7 0x019b +#define WCD9335_SB_FLL_FREQ_CTL_0 0x019c +#define WCD9335_SB_FLL_FREQ_CTL_1 0x019d +#define WCD9335_SB_FLL_FREQ_CTL_2 0x019e +#define WCD9335_SB_FLL_FREQ_CTL_3 0x019f +#define WCD9335_SB_FLL_SSC_CTL_0 0x01a0 +#define WCD9335_SB_FLL_SSC_CTL_1 0x01a1 +#define WCD9335_SB_FLL_SSC_CTL_2 0x01a2 +#define WCD9335_SB_FLL_SSC_CTL_3 0x01a3 +#define WCD9335_SB_FLL_FLL_MODE 0x01a4 +#define WCD9335_SB_FLL_STATUS_0 0x01a5 +#define WCD9335_SB_FLL_STATUS_1 0x01a6 +#define WCD9335_SB_FLL_STATUS_2 0x01a7 +#define WCD9335_SB_FLL_STATUS_3 0x01a8 + +/* Page-2 Registers */ +#define WCD9335_PAGE2_PAGE_REGISTER 0x0200 +#define WCD9335_CPE_SS_MEM_PTR_0 0x0201 +#define WCD9335_CPE_SS_MEM_PTR_1 0x0202 +#define WCD9335_CPE_SS_MEM_PTR_2 0x0203 +#define WCD9335_CPE_SS_MEM_CTRL 0x0205 +#define WCD9335_CPE_SS_MEM_BANK_0 0x0206 +#define WCD9335_CPE_SS_MEM_BANK_1 0x0207 +#define WCD9335_CPE_SS_MEM_BANK_2 0x0208 +#define WCD9335_CPE_SS_MEM_BANK_3 0x0209 +#define WCD9335_CPE_SS_MEM_BANK_4 0x020a +#define WCD9335_CPE_SS_MEM_BANK_5 0x020b +#define WCD9335_CPE_SS_MEM_BANK_6 0x020c +#define WCD9335_CPE_SS_MEM_BANK_7 0x020d +#define WCD9335_CPE_SS_MEM_BANK_8 0x020e +#define WCD9335_CPE_SS_MEM_BANK_9 0x020f +#define WCD9335_CPE_SS_MEM_BANK_10 0x0210 +#define WCD9335_CPE_SS_MEM_BANK_11 0x0211 +#define WCD9335_CPE_SS_MEM_BANK_12 0x0212 +#define WCD9335_CPE_SS_MEM_BANK_13 0x0213 +#define WCD9335_CPE_SS_MEM_BANK_14 0x0214 +#define WCD9335_CPE_SS_MEM_BANK_15 0x0215 +#define WCD9335_CPE_SS_INBOX1_TRG 0x0216 +#define WCD9335_CPE_SS_INBOX2_TRG 0x0217 +#define WCD9335_CPE_SS_INBOX1_0 0x0218 +#define WCD9335_CPE_SS_INBOX1_1 0x0219 +#define WCD9335_CPE_SS_INBOX1_2 0x021a +#define WCD9335_CPE_SS_INBOX1_3 0x021b +#define WCD9335_CPE_SS_INBOX1_4 0x021c +#define WCD9335_CPE_SS_INBOX1_5 0x021d +#define WCD9335_CPE_SS_INBOX1_6 0x021e +#define WCD9335_CPE_SS_INBOX1_7 0x021f +#define WCD9335_CPE_SS_INBOX1_8 0x0220 +#define WCD9335_CPE_SS_INBOX1_9 0x0221 +#define WCD9335_CPE_SS_INBOX1_10 0x0222 +#define WCD9335_CPE_SS_INBOX1_11 0x0223 +#define WCD9335_CPE_SS_INBOX1_12 0x0224 +#define WCD9335_CPE_SS_INBOX1_13 0x0225 +#define WCD9335_CPE_SS_INBOX1_14 0x0226 +#define WCD9335_CPE_SS_INBOX1_15 0x0227 +#define WCD9335_CPE_SS_OUTBOX1_0 0x0228 +#define WCD9335_CPE_SS_OUTBOX1_1 0x0229 +#define WCD9335_CPE_SS_OUTBOX1_2 0x022a +#define WCD9335_CPE_SS_OUTBOX1_3 0x022b +#define WCD9335_CPE_SS_OUTBOX1_4 0x022c +#define WCD9335_CPE_SS_OUTBOX1_5 0x022d +#define WCD9335_CPE_SS_OUTBOX1_6 0x022e +#define WCD9335_CPE_SS_OUTBOX1_7 0x022f +#define WCD9335_CPE_SS_OUTBOX1_8 0x0230 +#define WCD9335_CPE_SS_OUTBOX1_9 0x0231 +#define WCD9335_CPE_SS_OUTBOX1_10 0x0232 +#define WCD9335_CPE_SS_OUTBOX1_11 0x0233 +#define WCD9335_CPE_SS_OUTBOX1_12 0x0234 +#define WCD9335_CPE_SS_OUTBOX1_13 0x0235 +#define WCD9335_CPE_SS_OUTBOX1_14 0x0236 +#define WCD9335_CPE_SS_OUTBOX1_15 0x0237 +#define WCD9335_CPE_SS_INBOX2_0 0x0238 +#define WCD9335_CPE_SS_INBOX2_1 0x0239 +#define WCD9335_CPE_SS_INBOX2_2 0x023a +#define WCD9335_CPE_SS_INBOX2_3 0x023b +#define WCD9335_CPE_SS_INBOX2_4 0x023c +#define WCD9335_CPE_SS_INBOX2_5 0x023d +#define WCD9335_CPE_SS_INBOX2_6 0x023e +#define WCD9335_CPE_SS_INBOX2_7 0x023f +#define WCD9335_CPE_SS_INBOX2_8 0x0240 +#define WCD9335_CPE_SS_INBOX2_9 0x0241 +#define WCD9335_CPE_SS_INBOX2_10 0x0242 +#define WCD9335_CPE_SS_INBOX2_11 0x0243 +#define WCD9335_CPE_SS_INBOX2_12 0x0244 +#define WCD9335_CPE_SS_INBOX2_13 0x0245 +#define WCD9335_CPE_SS_INBOX2_14 0x0246 +#define WCD9335_CPE_SS_INBOX2_15 0x0247 +#define WCD9335_CPE_SS_OUTBOX2_0 0x0248 +#define WCD9335_CPE_SS_OUTBOX2_1 0x0249 +#define WCD9335_CPE_SS_OUTBOX2_2 0x024a +#define WCD9335_CPE_SS_OUTBOX2_3 0x024b +#define WCD9335_CPE_SS_OUTBOX2_4 0x024c +#define WCD9335_CPE_SS_OUTBOX2_5 0x024d +#define WCD9335_CPE_SS_OUTBOX2_6 0x024e +#define WCD9335_CPE_SS_OUTBOX2_7 0x024f +#define WCD9335_CPE_SS_OUTBOX2_8 0x0250 +#define WCD9335_CPE_SS_OUTBOX2_9 0x0251 +#define WCD9335_CPE_SS_OUTBOX2_10 0x0252 +#define WCD9335_CPE_SS_OUTBOX2_11 0x0253 +#define WCD9335_CPE_SS_OUTBOX2_12 0x0254 +#define WCD9335_CPE_SS_OUTBOX2_13 0x0255 +#define WCD9335_CPE_SS_OUTBOX2_14 0x0256 +#define WCD9335_CPE_SS_OUTBOX2_15 0x0257 +#define WCD9335_CPE_SS_OUTBOX1_ACK 0x0258 +#define WCD9335_CPE_SS_OUTBOX2_ACK 0x0259 +#define WCD9335_CPE_SS_EC_BUF_INT_PERIOD 0x025a +#define WCD9335_CPE_SS_US_BUF_INT_PERIOD 0x025b +#define WCD9335_CPE_SS_CPARMAD_BUFRDY_INT_PERIOD 0x025c +#define WCD9335_CPE_SS_CFG 0x025d +#define WCD9335_CPE_SS_US_EC_MUX_CFG 0x025e +#define WCD9335_CPE_SS_MAD_CTL 0x025f +#define WCD9335_CPE_SS_CPAR_CTL 0x0260 +#define WCD9335_CPE_SS_TX_PP_BUF_INT_PERIOD 0x0261 +#define WCD9335_CPE_SS_TX_PP_CFG 0x0262 +#define WCD9335_CPE_SS_DMIC0_CTL 0x0263 +#define WCD9335_CPE_SS_DMIC1_CTL 0x0264 +#define WCD9335_CPE_SS_DMIC2_CTL 0x0265 +#define WCD9335_CPE_SS_DMIC_CFG 0x0266 +#define WCD9335_CPE_SS_SVA_CFG 0x0267 +#define WCD9335_CPE_SS_CPAR_CFG 0x0271 +#define WCD9335_CPE_SS_WDOG_CFG 0x0272 +#define WCD9335_CPE_SS_BACKUP_INT 0x0273 +#define WCD9335_CPE_SS_STATUS 0x0274 +#define WCD9335_CPE_SS_CPE_OCD_CFG 0x0275 +#define WCD9335_CPE_SS_SS_ERROR_INT_MASK 0x0276 +#define WCD9335_CPE_SS_SS_ERROR_INT_STATUS 0x0277 +#define WCD9335_CPE_SS_SS_ERROR_INT_CLEAR 0x0278 +#define WCD9335_SOC_MAD_MAIN_CTL_1 0x0281 +#define WCD9335_SOC_MAD_MAIN_CTL_2 0x0282 +#define WCD9335_SOC_MAD_AUDIO_CTL_1 0x0283 +#define WCD9335_SOC_MAD_AUDIO_CTL_2 0x0284 +#define WCD9335_SOC_MAD_AUDIO_CTL_3 0x0285 +#define WCD9335_SOC_MAD_AUDIO_CTL_4 0x0286 +#define WCD9335_SOC_MAD_AUDIO_CTL_5 0x0287 +#define WCD9335_SOC_MAD_AUDIO_CTL_6 0x0288 +#define WCD9335_SOC_MAD_AUDIO_CTL_7 0x0289 +#define WCD9335_SOC_MAD_AUDIO_CTL_8 0x028a +#define WCD9335_SOC_MAD_AUDIO_IIR_CTL_PTR 0x028b +#define WCD9335_SOC_MAD_AUDIO_IIR_CTL_VAL 0x028c +#define WCD9335_SOC_MAD_ULTR_CTL_1 0x028d +#define WCD9335_SOC_MAD_ULTR_CTL_2 0x028e +#define WCD9335_SOC_MAD_ULTR_CTL_3 0x028f +#define WCD9335_SOC_MAD_ULTR_CTL_4 0x0290 +#define WCD9335_SOC_MAD_ULTR_CTL_5 0x0291 +#define WCD9335_SOC_MAD_ULTR_CTL_6 0x0292 +#define WCD9335_SOC_MAD_ULTR_CTL_7 0x0293 +#define WCD9335_SOC_MAD_BEACON_CTL_1 0x0294 +#define WCD9335_SOC_MAD_BEACON_CTL_2 0x0295 +#define WCD9335_SOC_MAD_BEACON_CTL_3 0x0296 +#define WCD9335_SOC_MAD_BEACON_CTL_4 0x0297 +#define WCD9335_SOC_MAD_BEACON_CTL_5 0x0298 +#define WCD9335_SOC_MAD_BEACON_CTL_6 0x0299 +#define WCD9335_SOC_MAD_BEACON_CTL_7 0x029a +#define WCD9335_SOC_MAD_BEACON_CTL_8 0x029b +#define WCD9335_SOC_MAD_BEACON_IIR_CTL_PTR 0x029c +#define WCD9335_SOC_MAD_BEACON_IIR_CTL_VAL 0x029d +#define WCD9335_SOC_MAD_INP_SEL 0x029e + +/* Page-6 Registers */ +#define WCD9335_PAGE6_PAGE_REGISTER 0x0600 +#define WCD9335_ANA_BIAS 0x0601 +#define WCD9335_ANA_CLK_TOP 0x0602 +#define WCD9335_ANA_RCO 0x0603 +#define WCD9335_ANA_BUCK_VOUT_A 0x0604 +#define WCD9335_ANA_BUCK_VOUT_D 0x0605 +#define WCD9335_ANA_BUCK_CTL 0x0606 +#define WCD9335_ANA_BUCK_STATUS 0x0607 +#define WCD9335_ANA_RX_SUPPLIES 0x0608 +#define WCD9335_ANA_HPH 0x0609 +#define WCD9335_ANA_EAR 0x060a +#define WCD9335_ANA_LO_1_2 0x060b +#define WCD9335_ANA_LO_3_4 0x060c +#define WCD9335_ANA_MAD_SETUP 0x060d +#define WCD9335_ANA_AMIC1 0x060e +#define WCD9335_ANA_AMIC2 0x060f +#define WCD9335_ANA_AMIC3 0x0610 +#define WCD9335_ANA_AMIC4 0x0611 +#define WCD9335_ANA_AMIC5 0x0612 +#define WCD9335_ANA_AMIC6 0x0613 +#define WCD9335_ANA_MBHC_MECH 0x0614 +#define WCD9335_ANA_MBHC_ELECT 0x0615 +#define WCD9335_ANA_MBHC_ZDET 0x0616 +#define WCD9335_ANA_MBHC_RESULT_1 0x0617 +#define WCD9335_ANA_MBHC_RESULT_2 0x0618 +#define WCD9335_ANA_MBHC_RESULT_3 0x0619 +#define WCD9335_ANA_MBHC_BTN0 0x061a +#define WCD9335_ANA_MBHC_BTN1 0x061b +#define WCD9335_ANA_MBHC_BTN2 0x061c +#define WCD9335_ANA_MBHC_BTN3 0x061d +#define WCD9335_ANA_MBHC_BTN4 0x061e +#define WCD9335_ANA_MBHC_BTN5 0x061f +#define WCD9335_ANA_MBHC_BTN6 0x0620 +#define WCD9335_ANA_MBHC_BTN7 0x0621 +#define WCD9335_ANA_MICB1 0x0622 +#define WCD9335_ANA_MICB2 0x0623 +#define WCD9335_ANA_MICB2_RAMP 0x0624 +#define WCD9335_ANA_MICB3 0x0625 +#define WCD9335_ANA_MICB4 0x0626 +#define WCD9335_ANA_VBADC 0x0627 +#define WCD9335_BIAS_CTL 0x0628 +#define WCD9335_BIAS_VBG_FINE_ADJ 0x0629 +#define WCD9335_CLOCK_TEST_CTL 0x062d +#define WCD9335_RCO_CTRL_1 0x062e +#define WCD9335_RCO_CTRL_2 0x062f +#define WCD9335_RCO_CAL 0x0630 +#define WCD9335_RCO_CAL_1 0x0631 +#define WCD9335_RCO_CAL_2 0x0632 +#define WCD9335_RCO_TEST_CTRL 0x0633 +#define WCD9335_RCO_CAL_OUT_1 0x0634 +#define WCD9335_RCO_CAL_OUT_2 0x0635 +#define WCD9335_RCO_CAL_OUT_3 0x0636 +#define WCD9335_RCO_CAL_OUT_4 0x0637 +#define WCD9335_RCO_CAL_OUT_5 0x0638 +#define WCD9335_SIDO_SIDO_MODE_1 0x063a +#define WCD9335_SIDO_SIDO_MODE_2 0x063b +#define WCD9335_SIDO_SIDO_MODE_3 0x063c +#define WCD9335_SIDO_SIDO_MODE_4 0x063d +#define WCD9335_SIDO_SIDO_VCL_1 0x063e +#define WCD9335_SIDO_SIDO_VCL_2 0x063f +#define WCD9335_SIDO_SIDO_VCL_3 0x0640 +#define WCD9335_SIDO_SIDO_CCL_1 0x0641 +#define WCD9335_SIDO_SIDO_CCL_2 0x0642 +#define WCD9335_SIDO_SIDO_CCL_3 0x0643 +#define WCD9335_SIDO_SIDO_CCL_4 0x0644 +#define WCD9335_SIDO_SIDO_CCL_5 0x0645 +#define WCD9335_SIDO_SIDO_CCL_6 0x0646 +#define WCD9335_SIDO_SIDO_CCL_7 0x0647 +#define WCD9335_SIDO_SIDO_CCL_8 0x0648 +#define WCD9335_SIDO_SIDO_CCL_9 0x0649 +#define WCD9335_SIDO_SIDO_CCL_10 0x064a +#define WCD9335_SIDO_SIDO_FILTER_1 0x064b +#define WCD9335_SIDO_SIDO_FILTER_2 0x064c +#define WCD9335_SIDO_SIDO_DRIVER_1 0x064d +#define WCD9335_SIDO_SIDO_DRIVER_2 0x064e +#define WCD9335_SIDO_SIDO_DRIVER_3 0x064f +#define WCD9335_SIDO_SIDO_CAL_CODE_EXT_1 0x0650 +#define WCD9335_SIDO_SIDO_CAL_CODE_EXT_2 0x0651 +#define WCD9335_SIDO_SIDO_CAL_CODE_OUT_1 0x0652 +#define WCD9335_SIDO_SIDO_CAL_CODE_OUT_2 0x0653 +#define WCD9335_SIDO_SIDO_TEST_1 0x0654 +#define WCD9335_SIDO_SIDO_TEST_2 0x0655 +#define WCD9335_MBHC_CTL_1 0x0656 +#define WCD9335_MBHC_CTL_2 0x0657 +#define WCD9335_MBHC_PLUG_DETECT_CTL 0x0658 +#define WCD9335_MBHC_ZDET_ANA_CTL 0x0659 +#define WCD9335_MBHC_ZDET_RAMP_CTL 0x065a +#define WCD9335_MBHC_FSM_DEBUG 0x065b /* v1.x */ +#define WCD9335_MBHC_FSM_STATUS 0x065b /* v2.0 */ +#define WCD9335_MBHC_TEST_CTL 0x065c +#define WCD9335_VBADC_SUBBLOCK_EN 0x065d +#define WCD9335_VBADC_IBIAS_FE 0x065e +#define WCD9335_VBADC_BIAS_ADC 0x065f +#define WCD9335_VBADC_FE_CTRL 0x0660 +#define WCD9335_VBADC_ADC_REF 0x0661 +#define WCD9335_VBADC_ADC_IO 0x0662 +#define WCD9335_VBADC_ADC_SAR 0x0663 +#define WCD9335_VBADC_DEBUG 0x0664 +#define WCD9335_VBADC_ADC_DOUTMSB 0x0665 +#define WCD9335_VBADC_ADC_DOUTLSB 0x0666 +#define WCD9335_LDOH_MODE 0x0667 +#define WCD9335_LDOH_BIAS 0x0668 +#define WCD9335_LDOH_STB_LOADS 0x0669 +#define WCD9335_LDOH_SLOWRAMP 0x066a +#define WCD9335_MICB1_TEST_CTL_1 0x066b +#define WCD9335_MICB1_TEST_CTL_2 0x066c +#define WCD9335_MICB1_TEST_CTL_3 0x066d +#define WCD9335_MICB2_TEST_CTL_1 0x066e +#define WCD9335_MICB2_TEST_CTL_2 0x066f +#define WCD9335_MICB2_TEST_CTL_3 0x0670 +#define WCD9335_MICB3_TEST_CTL_1 0x0671 +#define WCD9335_MICB3_TEST_CTL_2 0x0672 +#define WCD9335_MICB3_TEST_CTL_3 0x0673 +#define WCD9335_MICB4_TEST_CTL_1 0x0674 +#define WCD9335_MICB4_TEST_CTL_2 0x0675 +#define WCD9335_MICB4_TEST_CTL_3 0x0676 +#define WCD9335_TX_COM_ADC_VCM 0x0677 +#define WCD9335_TX_COM_BIAS_ATEST 0x0678 +#define WCD9335_TX_COM_ADC_INT1_IB 0x0679 +#define WCD9335_TX_COM_ADC_INT2_IB 0x067a +#define WCD9335_TX_COM_TXFE_DIV_CTL 0x067b +#define WCD9335_TX_COM_TXFE_DIV_START 0x067c +#define WCD9335_TX_COM_TXFE_DIV_STOP_9P6M 0x067d +#define WCD9335_TX_COM_TXFE_DIV_STOP_12P288M 0x067e +#define WCD9335_TX_1_2_TEST_EN 0x067f +#define WCD9335_TX_1_2_ADC_IB 0x0680 +#define WCD9335_TX_1_2_ATEST_REFCTL 0x0681 +#define WCD9335_TX_1_2_TEST_CTL 0x0682 +#define WCD9335_TX_1_2_TEST_BLK_EN 0x0683 +#define WCD9335_TX_1_2_TXFE_CLKDIV 0x0684 +#define WCD9335_TX_1_2_SAR1_ERR 0x0685 +#define WCD9335_TX_1_2_SAR2_ERR 0x0686 +#define WCD9335_TX_3_4_TEST_EN 0x0687 +#define WCD9335_TX_3_4_ADC_IB 0x0688 +#define WCD9335_TX_3_4_ATEST_REFCTL 0x0689 +#define WCD9335_TX_3_4_TEST_CTL 0x068a +#define WCD9335_TX_3_4_TEST_BLK_EN 0x068b +#define WCD9335_TX_3_4_TXFE_CLKDIV 0x068c +#define WCD9335_TX_3_4_SAR1_ERR 0x068d +#define WCD9335_TX_3_4_SAR2_ERR 0x068e +#define WCD9335_TX_5_6_TEST_EN 0x068f +#define WCD9335_TX_5_6_ADC_IB 0x0690 +#define WCD9335_TX_5_6_ATEST_REFCTL 0x0691 +#define WCD9335_TX_5_6_TEST_CTL 0x0692 +#define WCD9335_TX_5_6_TEST_BLK_EN 0x0693 +#define WCD9335_TX_5_6_TXFE_CLKDIV 0x0694 +#define WCD9335_TX_5_6_SAR1_ERR 0x0695 +#define WCD9335_TX_5_6_SAR2_ERR 0x0696 +#define WCD9335_CLASSH_MODE_1 0x0697 +#define WCD9335_CLASSH_MODE_2 0x0698 +#define WCD9335_CLASSH_MODE_3 0x0699 +#define WCD9335_CLASSH_CTRL_VCL_1 0x069a +#define WCD9335_CLASSH_CTRL_VCL_2 0x069b +#define WCD9335_CLASSH_CTRL_CCL_1 0x069c +#define WCD9335_CLASSH_CTRL_CCL_2 0x069d +#define WCD9335_CLASSH_CTRL_CCL_3 0x069e +#define WCD9335_CLASSH_CTRL_CCL_4 0x069f +#define WCD9335_CLASSH_CTRL_CCL_5 0x06a0 +#define WCD9335_CLASSH_BUCK_TMUX_A_D 0x06a1 +#define WCD9335_CLASSH_BUCK_SW_DRV_CNTL 0x06a2 +#define WCD9335_CLASSH_SPARE 0x06a3 +#define WCD9335_FLYBACK_EN 0x06a4 +#define WCD9335_FLYBACK_VNEG_CTRL_1 0x06a5 +#define WCD9335_FLYBACK_VNEG_CTRL_2 0x06a6 +#define WCD9335_FLYBACK_VNEG_CTRL_3 0x06a7 +#define WCD9335_FLYBACK_VNEG_CTRL_4 0x06a8 +#define WCD9335_FLYBACK_VNEG_CTRL_5 0x06a9 +#define WCD9335_FLYBACK_VNEG_CTRL_6 0x06aa +#define WCD9335_FLYBACK_VNEG_CTRL_7 0x06ab +#define WCD9335_FLYBACK_VNEG_CTRL_8 0x06ac +#define WCD9335_FLYBACK_VNEG_CTRL_9 0x06ad +#define WCD9335_FLYBACK_VNEG_DAC_CTRL_1 0x06ae +#define WCD9335_FLYBACK_VNEG_DAC_CTRL_2 0x06af +#define WCD9335_FLYBACK_VNEG_DAC_CTRL_3 0x06b0 +#define WCD9335_FLYBACK_VNEG_DAC_CTRL_4 0x06b1 /* v1.x */ +#define WCD9335_FLYBACK_CTRL_1 0x06b1 /* v2.0 */ +#define WCD9335_FLYBACK_TEST_CTL 0x06b2 +#define WCD9335_RX_AUX_SW_CTL 0x06b3 +#define WCD9335_RX_PA_AUX_IN_CONN 0x06b4 +#define WCD9335_RX_TIMER_DIV 0x06b5 +#define WCD9335_RX_OCP_CTL 0x06b6 +#define WCD9335_RX_OCP_COUNT 0x06b7 +#define WCD9335_RX_BIAS_EAR_DAC 0x06b8 +#define WCD9335_RX_BIAS_EAR_AMP 0x06b9 +#define WCD9335_RX_BIAS_HPH_LDO 0x06ba +#define WCD9335_RX_BIAS_HPH_PA 0x06bb +#define WCD9335_RX_BIAS_HPH_RDACBUFF_CNP2 0x06bc +#define WCD9335_RX_BIAS_HPH_RDAC_LDO 0x06bd +#define WCD9335_RX_BIAS_HPH_CNP1 0x06be +#define WCD9335_RX_BIAS_HPH_LOWPOWER 0x06bf +#define WCD9335_RX_BIAS_DIFFLO_PA 0x06c0 +#define WCD9335_RX_BIAS_DIFFLO_REF 0x06c1 +#define WCD9335_RX_BIAS_DIFFLO_LDO 0x06c2 +#define WCD9335_RX_BIAS_SELO_DAC_PA 0x06c3 +#define WCD9335_RX_BIAS_BUCK_RST 0x06c4 +#define WCD9335_RX_BIAS_BUCK_VREF_ERRAMP 0x06c5 +#define WCD9335_RX_BIAS_FLYB_ERRAMP 0x06c6 +#define WCD9335_RX_BIAS_FLYB_BUFF 0x06c7 +#define WCD9335_RX_BIAS_FLYB_MID_RST 0x06c8 +#define WCD9335_HPH_L_STATUS 0x06c9 +#define WCD9335_HPH_R_STATUS 0x06ca +#define WCD9335_HPH_CNP_EN 0x06cb +#define WCD9335_HPH_CNP_WG_CTL 0x06cc +#define WCD9335_HPH_CNP_WG_TIME 0x06cd +#define WCD9335_HPH_OCP_CTL 0x06ce +#define WCD9335_HPH_AUTO_CHOP 0x06cf +#define WCD9335_HPH_CHOP_CTL 0x06d0 +#define WCD9335_HPH_PA_CTL1 0x06d1 +#define WCD9335_HPH_PA_CTL2 0x06d2 +#define WCD9335_HPH_L_EN 0x06d3 +#define WCD9335_HPH_L_TEST 0x06d4 +#define WCD9335_HPH_L_ATEST 0x06d5 +#define WCD9335_HPH_R_EN 0x06d6 +#define WCD9335_HPH_R_TEST 0x06d7 +#define WCD9335_HPH_R_ATEST 0x06d8 +#define WCD9335_HPH_RDAC_CLK_CTL1 0x06d9 +#define WCD9335_HPH_RDAC_CLK_CTL2 0x06da +#define WCD9335_HPH_RDAC_LDO_CTL 0x06db +#define WCD9335_HPH_RDAC_CHOP_CLK_LP_CTL 0x06dc +#define WCD9335_HPH_REFBUFF_UHQA_CTL 0x06dd +#define WCD9335_HPH_REFBUFF_LP_CTL 0x06de +#define WCD9335_HPH_L_DAC_CTL 0x06df +#define WCD9335_HPH_R_DAC_CTL 0x06e0 +#define WCD9335_EAR_EN_REG 0x06e1 +#define WCD9335_EAR_CMBUFF 0x06e2 +#define WCD9335_EAR_ICTL 0x06e3 +#define WCD9335_EAR_EN_DBG_CTL 0x06e4 +#define WCD9335_EAR_CNP 0x06e5 +#define WCD9335_EAR_DAC_CTL_ATEST 0x06e6 +#define WCD9335_EAR_STATUS_REG 0x06e7 +#define WCD9335_EAR_OUT_SHORT 0x06e8 +#define WCD9335_DIFF_LO_MISC 0x06e9 +#define WCD9335_DIFF_LO_LO2_COMPANDER 0x06ea +#define WCD9335_DIFF_LO_LO1_COMPANDER 0x06eb +#define WCD9335_DIFF_LO_COMMON 0x06ec +#define WCD9335_DIFF_LO_BYPASS_EN 0x06ed +#define WCD9335_DIFF_LO_CNP 0x06ee +#define WCD9335_DIFF_LO_CORE_OUT_PROG 0x06ef +#define WCD9335_DIFF_LO_LDO_OUT_PROG 0x06f0 +#define WCD9335_DIFF_LO_COM_SWCAP_REFBUF_FREQ 0x06f1 +#define WCD9335_DIFF_LO_COM_PA_FREQ 0x06f2 +#define WCD9335_DIFF_LO_RESERVED_REG 0x06f3 +#define WCD9335_DIFF_LO_LO1_STATUS_1 0x06f4 +#define WCD9335_DIFF_LO_LO1_STATUS_2 0x06f5 +#define WCD9335_SE_LO_COM1 0x06f6 +#define WCD9335_SE_LO_COM2 0x06f7 +#define WCD9335_SE_LO_LO3_GAIN 0x06f8 +#define WCD9335_SE_LO_LO3_CTRL 0x06f9 +#define WCD9335_SE_LO_LO4_GAIN 0x06fa +#define WCD9335_SE_LO_LO4_CTRL 0x06fb +#define WCD9335_SE_LO_LO3_STATUS 0x06fe +#define WCD9335_SE_LO_LO4_STATUS 0x06ff + +/* Page-10 Registers */ +#define WCD9335_PAGE10_PAGE_REGISTER 0x0a00 +#define WCD9335_CDC_ANC0_CLK_RESET_CTL 0x0a01 +#define WCD9335_CDC_ANC0_MODE_1_CTL 0x0a02 +#define WCD9335_CDC_ANC0_MODE_2_CTL 0x0a03 +#define WCD9335_CDC_ANC0_FF_SHIFT 0x0a04 +#define WCD9335_CDC_ANC0_FB_SHIFT 0x0a05 +#define WCD9335_CDC_ANC0_LPF_FF_A_CTL 0x0a06 +#define WCD9335_CDC_ANC0_LPF_FF_B_CTL 0x0a07 +#define WCD9335_CDC_ANC0_LPF_FB_CTL 0x0a08 +#define WCD9335_CDC_ANC0_SMLPF_CTL 0x0a09 +#define WCD9335_CDC_ANC0_DCFLT_SHIFT_CTL 0x0a0a +#define WCD9335_CDC_ANC0_IIR_ADAPT_CTL 0x0a0b +#define WCD9335_CDC_ANC0_IIR_COEFF_1_CTL 0x0a0c +#define WCD9335_CDC_ANC0_IIR_COEFF_2_CTL 0x0a0d +#define WCD9335_CDC_ANC0_FF_A_GAIN_CTL 0x0a0e +#define WCD9335_CDC_ANC0_FF_B_GAIN_CTL 0x0a0f +#define WCD9335_CDC_ANC0_FB_GAIN_CTL 0x0a10 +#define WCD9335_CDC_ANC1_CLK_RESET_CTL 0x0a19 +#define WCD9335_CDC_ANC1_MODE_1_CTL 0x0a1a +#define WCD9335_CDC_ANC1_MODE_2_CTL 0x0a1b +#define WCD9335_CDC_ANC1_FF_SHIFT 0x0a1c +#define WCD9335_CDC_ANC1_FB_SHIFT 0x0a1d +#define WCD9335_CDC_ANC1_LPF_FF_A_CTL 0x0a1e +#define WCD9335_CDC_ANC1_LPF_FF_B_CTL 0x0a1f +#define WCD9335_CDC_ANC1_LPF_FB_CTL 0x0a20 +#define WCD9335_CDC_ANC1_SMLPF_CTL 0x0a21 +#define WCD9335_CDC_ANC1_DCFLT_SHIFT_CTL 0x0a22 +#define WCD9335_CDC_ANC1_IIR_ADAPT_CTL 0x0a23 +#define WCD9335_CDC_ANC1_IIR_COEFF_1_CTL 0x0a24 +#define WCD9335_CDC_ANC1_IIR_COEFF_2_CTL 0x0a25 +#define WCD9335_CDC_ANC1_FF_A_GAIN_CTL 0x0a26 +#define WCD9335_CDC_ANC1_FF_B_GAIN_CTL 0x0a27 +#define WCD9335_CDC_ANC1_FB_GAIN_CTL 0x0a28 +#define WCD9335_CDC_TX0_TX_PATH_CTL 0x0a31 +#define WCD9335_CDC_TX0_TX_PATH_CFG0 0x0a32 +#define WCD9335_CDC_TX0_TX_PATH_CFG1 0x0a33 +#define WCD9335_CDC_TX0_TX_VOL_CTL 0x0a34 +#define WCD9335_CDC_TX0_TX_PATH_192_CTL 0x0a35 +#define WCD9335_CDC_TX0_TX_PATH_192_CFG 0x0a36 +#define WCD9335_CDC_TX0_TX_PATH_SEC0 0x0a37 +#define WCD9335_CDC_TX0_TX_PATH_SEC1 0x0a38 +#define WCD9335_CDC_TX0_TX_PATH_SEC2 0x0a39 +#define WCD9335_CDC_TX0_TX_PATH_SEC3 0x0a3a +#define WCD9335_CDC_TX0_TX_PATH_SEC4 0x0a3b +#define WCD9335_CDC_TX0_TX_PATH_SEC5 0x0a3c +#define WCD9335_CDC_TX0_TX_PATH_SEC6 0x0a3d +#define WCD9335_CDC_TX0_TX_PATH_SEC7 0x0a3e +#define WCD9335_CDC_TX1_TX_PATH_CTL 0x0a41 +#define WCD9335_CDC_TX1_TX_PATH_CFG0 0x0a42 +#define WCD9335_CDC_TX1_TX_PATH_CFG1 0x0a43 +#define WCD9335_CDC_TX1_TX_VOL_CTL 0x0a44 +#define WCD9335_CDC_TX1_TX_PATH_192_CTL 0x0a45 +#define WCD9335_CDC_TX1_TX_PATH_192_CFG 0x0a46 +#define WCD9335_CDC_TX1_TX_PATH_SEC0 0x0a47 +#define WCD9335_CDC_TX1_TX_PATH_SEC1 0x0a48 +#define WCD9335_CDC_TX1_TX_PATH_SEC2 0x0a49 +#define WCD9335_CDC_TX1_TX_PATH_SEC3 0x0a4a +#define WCD9335_CDC_TX1_TX_PATH_SEC4 0x0a4b +#define WCD9335_CDC_TX1_TX_PATH_SEC5 0x0a4c +#define WCD9335_CDC_TX1_TX_PATH_SEC6 0x0a4d +#define WCD9335_CDC_TX2_TX_PATH_CTL 0x0a51 +#define WCD9335_CDC_TX2_TX_PATH_CFG0 0x0a52 +#define WCD9335_CDC_TX2_TX_PATH_CFG1 0x0a53 +#define WCD9335_CDC_TX2_TX_VOL_CTL 0x0a54 +#define WCD9335_CDC_TX2_TX_PATH_192_CTL 0x0a55 +#define WCD9335_CDC_TX2_TX_PATH_192_CFG 0x0a56 +#define WCD9335_CDC_TX2_TX_PATH_SEC0 0x0a57 +#define WCD9335_CDC_TX2_TX_PATH_SEC1 0x0a58 +#define WCD9335_CDC_TX2_TX_PATH_SEC2 0x0a59 +#define WCD9335_CDC_TX2_TX_PATH_SEC3 0x0a5a +#define WCD9335_CDC_TX2_TX_PATH_SEC4 0x0a5b +#define WCD9335_CDC_TX2_TX_PATH_SEC5 0x0a5c +#define WCD9335_CDC_TX2_TX_PATH_SEC6 0x0a5d +#define WCD9335_CDC_TX3_TX_PATH_CTL 0x0a61 +#define WCD9335_CDC_TX3_TX_PATH_CFG0 0x0a62 +#define WCD9335_CDC_TX3_TX_PATH_CFG1 0x0a63 +#define WCD9335_CDC_TX3_TX_VOL_CTL 0x0a64 +#define WCD9335_CDC_TX3_TX_PATH_192_CTL 0x0a65 +#define WCD9335_CDC_TX3_TX_PATH_192_CFG 0x0a66 +#define WCD9335_CDC_TX3_TX_PATH_SEC0 0x0a67 +#define WCD9335_CDC_TX3_TX_PATH_SEC1 0x0a68 +#define WCD9335_CDC_TX3_TX_PATH_SEC2 0x0a69 +#define WCD9335_CDC_TX3_TX_PATH_SEC3 0x0a6a +#define WCD9335_CDC_TX3_TX_PATH_SEC4 0x0a6b +#define WCD9335_CDC_TX3_TX_PATH_SEC5 0x0a6c +#define WCD9335_CDC_TX3_TX_PATH_SEC6 0x0a6d +#define WCD9335_CDC_TX4_TX_PATH_CTL 0x0a71 +#define WCD9335_CDC_TX4_TX_PATH_CFG0 0x0a72 +#define WCD9335_CDC_TX4_TX_PATH_CFG1 0x0a73 +#define WCD9335_CDC_TX4_TX_VOL_CTL 0x0a74 +#define WCD9335_CDC_TX4_TX_PATH_192_CTL 0x0a75 +#define WCD9335_CDC_TX4_TX_PATH_192_CFG 0x0a76 +#define WCD9335_CDC_TX4_TX_PATH_SEC0 0x0a77 +#define WCD9335_CDC_TX4_TX_PATH_SEC1 0x0a78 +#define WCD9335_CDC_TX4_TX_PATH_SEC2 0x0a79 +#define WCD9335_CDC_TX4_TX_PATH_SEC3 0x0a7a +#define WCD9335_CDC_TX4_TX_PATH_SEC4 0x0a7b +#define WCD9335_CDC_TX4_TX_PATH_SEC5 0x0a7c +#define WCD9335_CDC_TX4_TX_PATH_SEC6 0x0a7d +#define WCD9335_CDC_TX5_TX_PATH_CTL 0x0a81 +#define WCD9335_CDC_TX5_TX_PATH_CFG0 0x0a82 +#define WCD9335_CDC_TX5_TX_PATH_CFG1 0x0a83 +#define WCD9335_CDC_TX5_TX_VOL_CTL 0x0a84 +#define WCD9335_CDC_TX5_TX_PATH_192_CTL 0x0a85 +#define WCD9335_CDC_TX5_TX_PATH_192_CFG 0x0a86 +#define WCD9335_CDC_TX5_TX_PATH_SEC0 0x0a87 +#define WCD9335_CDC_TX5_TX_PATH_SEC1 0x0a88 +#define WCD9335_CDC_TX5_TX_PATH_SEC2 0x0a89 +#define WCD9335_CDC_TX5_TX_PATH_SEC3 0x0a8a +#define WCD9335_CDC_TX5_TX_PATH_SEC4 0x0a8b +#define WCD9335_CDC_TX5_TX_PATH_SEC5 0x0a8c +#define WCD9335_CDC_TX5_TX_PATH_SEC6 0x0a8d +#define WCD9335_CDC_TX6_TX_PATH_CTL 0x0a91 +#define WCD9335_CDC_TX6_TX_PATH_CFG0 0x0a92 +#define WCD9335_CDC_TX6_TX_PATH_CFG1 0x0a93 +#define WCD9335_CDC_TX6_TX_VOL_CTL 0x0a94 +#define WCD9335_CDC_TX6_TX_PATH_192_CTL 0x0a95 +#define WCD9335_CDC_TX6_TX_PATH_192_CFG 0x0a96 +#define WCD9335_CDC_TX6_TX_PATH_SEC0 0x0a97 +#define WCD9335_CDC_TX6_TX_PATH_SEC1 0x0a98 +#define WCD9335_CDC_TX6_TX_PATH_SEC2 0x0a99 +#define WCD9335_CDC_TX6_TX_PATH_SEC3 0x0a9a +#define WCD9335_CDC_TX6_TX_PATH_SEC4 0x0a9b +#define WCD9335_CDC_TX6_TX_PATH_SEC5 0x0a9c +#define WCD9335_CDC_TX6_TX_PATH_SEC6 0x0a9d +#define WCD9335_CDC_TX7_TX_PATH_CTL 0x0aa1 +#define WCD9335_CDC_TX7_TX_PATH_CFG0 0x0aa2 +#define WCD9335_CDC_TX7_TX_PATH_CFG1 0x0aa3 +#define WCD9335_CDC_TX7_TX_VOL_CTL 0x0aa4 +#define WCD9335_CDC_TX7_TX_PATH_192_CTL 0x0aa5 +#define WCD9335_CDC_TX7_TX_PATH_192_CFG 0x0aa6 +#define WCD9335_CDC_TX7_TX_PATH_SEC0 0x0aa7 +#define WCD9335_CDC_TX7_TX_PATH_SEC1 0x0aa8 +#define WCD9335_CDC_TX7_TX_PATH_SEC2 0x0aa9 +#define WCD9335_CDC_TX7_TX_PATH_SEC3 0x0aaa +#define WCD9335_CDC_TX7_TX_PATH_SEC4 0x0aab +#define WCD9335_CDC_TX7_TX_PATH_SEC5 0x0aac +#define WCD9335_CDC_TX7_TX_PATH_SEC6 0x0aad +#define WCD9335_CDC_TX8_TX_PATH_CTL 0x0ab1 +#define WCD9335_CDC_TX8_TX_PATH_CFG0 0x0ab2 +#define WCD9335_CDC_TX8_TX_PATH_CFG1 0x0ab3 +#define WCD9335_CDC_TX8_TX_VOL_CTL 0x0ab4 +#define WCD9335_CDC_TX8_TX_PATH_192_CTL 0x0ab5 +#define WCD9335_CDC_TX8_TX_PATH_192_CFG 0x0ab6 +#define WCD9335_CDC_TX8_TX_PATH_SEC0 0x0ab7 +#define WCD9335_CDC_TX8_TX_PATH_SEC1 0x0ab8 +#define WCD9335_CDC_TX8_TX_PATH_SEC2 0x0ab9 +#define WCD9335_CDC_TX8_TX_PATH_SEC3 0x0aba +#define WCD9335_CDC_TX8_TX_PATH_SEC4 0x0abb +#define WCD9335_CDC_TX8_TX_PATH_SEC5 0x0abc +#define WCD9335_CDC_TX8_TX_PATH_SEC6 0x0abd +#define WCD9335_CDC_TX9_SPKR_PROT_PATH_CTL 0x0ac2 +#define WCD9335_CDC_TX9_SPKR_PROT_PATH_CFG0 0x0ac3 +#define WCD9335_CDC_TX10_SPKR_PROT_PATH_CTL 0x0ac6 +#define WCD9335_CDC_TX10_SPKR_PROT_PATH_CFG0 0x0ac7 +#define WCD9335_CDC_TX11_SPKR_PROT_PATH_CTL 0x0aca +#define WCD9335_CDC_TX11_SPKR_PROT_PATH_CFG0 0x0acb +#define WCD9335_CDC_TX12_SPKR_PROT_PATH_CTL 0x0ace +#define WCD9335_CDC_TX12_SPKR_PROT_PATH_CFG0 0x0acf + +/* Page-11 Registers */ +#define WCD9335_PAGE11_PAGE_REGISTER 0x0b00 +#define WCD9335_CDC_COMPANDER1_CTL0 0x0b01 +#define WCD9335_CDC_COMPANDER1_CTL1 0x0b02 +#define WCD9335_CDC_COMPANDER1_CTL2 0x0b03 +#define WCD9335_CDC_COMPANDER1_CTL3 0x0b04 +#define WCD9335_CDC_COMPANDER1_CTL4 0x0b05 +#define WCD9335_CDC_COMPANDER1_CTL5 0x0b06 +#define WCD9335_CDC_COMPANDER1_CTL6 0x0b07 +#define WCD9335_CDC_COMPANDER1_CTL7 0x0b08 +#define WCD9335_CDC_COMPANDER2_CTL0 0x0b09 +#define WCD9335_CDC_COMPANDER2_CTL1 0x0b0a +#define WCD9335_CDC_COMPANDER2_CTL2 0x0b0b +#define WCD9335_CDC_COMPANDER2_CTL3 0x0b0c +#define WCD9335_CDC_COMPANDER2_CTL4 0x0b0d +#define WCD9335_CDC_COMPANDER2_CTL5 0x0b0e +#define WCD9335_CDC_COMPANDER2_CTL6 0x0b0f +#define WCD9335_CDC_COMPANDER2_CTL7 0x0b10 +#define WCD9335_CDC_COMPANDER3_CTL0 0x0b11 +#define WCD9335_CDC_COMPANDER3_CTL1 0x0b12 +#define WCD9335_CDC_COMPANDER3_CTL2 0x0b13 +#define WCD9335_CDC_COMPANDER3_CTL3 0x0b14 +#define WCD9335_CDC_COMPANDER3_CTL4 0x0b15 +#define WCD9335_CDC_COMPANDER3_CTL5 0x0b16 +#define WCD9335_CDC_COMPANDER3_CTL6 0x0b17 +#define WCD9335_CDC_COMPANDER3_CTL7 0x0b18 +#define WCD9335_CDC_COMPANDER4_CTL0 0x0b19 +#define WCD9335_CDC_COMPANDER4_CTL1 0x0b1a +#define WCD9335_CDC_COMPANDER4_CTL2 0x0b1b +#define WCD9335_CDC_COMPANDER4_CTL3 0x0b1c +#define WCD9335_CDC_COMPANDER4_CTL4 0x0b1d +#define WCD9335_CDC_COMPANDER4_CTL5 0x0b1e +#define WCD9335_CDC_COMPANDER4_CTL6 0x0b1f +#define WCD9335_CDC_COMPANDER4_CTL7 0x0b20 +#define WCD9335_CDC_COMPANDER5_CTL0 0x0b21 +#define WCD9335_CDC_COMPANDER5_CTL1 0x0b22 +#define WCD9335_CDC_COMPANDER5_CTL2 0x0b23 +#define WCD9335_CDC_COMPANDER5_CTL3 0x0b24 +#define WCD9335_CDC_COMPANDER5_CTL4 0x0b25 +#define WCD9335_CDC_COMPANDER5_CTL5 0x0b26 +#define WCD9335_CDC_COMPANDER5_CTL6 0x0b27 +#define WCD9335_CDC_COMPANDER5_CTL7 0x0b28 +#define WCD9335_CDC_COMPANDER6_CTL0 0x0b29 +#define WCD9335_CDC_COMPANDER6_CTL1 0x0b2a +#define WCD9335_CDC_COMPANDER6_CTL2 0x0b2b +#define WCD9335_CDC_COMPANDER6_CTL3 0x0b2c +#define WCD9335_CDC_COMPANDER6_CTL4 0x0b2d +#define WCD9335_CDC_COMPANDER6_CTL5 0x0b2e +#define WCD9335_CDC_COMPANDER6_CTL6 0x0b2f +#define WCD9335_CDC_COMPANDER6_CTL7 0x0b30 +#define WCD9335_CDC_COMPANDER7_CTL0 0x0b31 +#define WCD9335_CDC_COMPANDER7_CTL1 0x0b32 +#define WCD9335_CDC_COMPANDER7_CTL2 0x0b33 +#define WCD9335_CDC_COMPANDER7_CTL3 0x0b34 +#define WCD9335_CDC_COMPANDER7_CTL4 0x0b35 +#define WCD9335_CDC_COMPANDER7_CTL5 0x0b36 +#define WCD9335_CDC_COMPANDER7_CTL6 0x0b37 +#define WCD9335_CDC_COMPANDER7_CTL7 0x0b38 +#define WCD9335_CDC_COMPANDER8_CTL0 0x0b39 +#define WCD9335_CDC_COMPANDER8_CTL1 0x0b3a +#define WCD9335_CDC_COMPANDER8_CTL2 0x0b3b +#define WCD9335_CDC_COMPANDER8_CTL3 0x0b3c +#define WCD9335_CDC_COMPANDER8_CTL4 0x0b3d +#define WCD9335_CDC_COMPANDER8_CTL5 0x0b3e +#define WCD9335_CDC_COMPANDER8_CTL6 0x0b3f +#define WCD9335_CDC_COMPANDER8_CTL7 0x0b40 +#define WCD9335_CDC_RX0_RX_PATH_CTL 0x0b41 +#define WCD9335_CDC_RX0_RX_PATH_CFG0 0x0b42 +#define WCD9335_CDC_RX0_RX_PATH_CFG1 0x0b43 +#define WCD9335_CDC_RX0_RX_PATH_CFG2 0x0b44 +#define WCD9335_CDC_RX0_RX_VOL_CTL 0x0b45 +#define WCD9335_CDC_RX0_RX_PATH_MIX_CTL 0x0b46 +#define WCD9335_CDC_RX0_RX_PATH_MIX_CFG 0x0b47 +#define WCD9335_CDC_RX0_RX_VOL_MIX_CTL 0x0b48 +#define WCD9335_CDC_RX0_RX_PATH_SEC0 0x0b49 +#define WCD9335_CDC_RX0_RX_PATH_SEC1 0x0b4a +#define WCD9335_CDC_RX0_RX_PATH_SEC2 0x0b4b +#define WCD9335_CDC_RX0_RX_PATH_SEC3 0x0b4c +#define WCD9335_CDC_RX0_RX_PATH_SEC5 0x0b4e +#define WCD9335_CDC_RX0_RX_PATH_SEC6 0x0b4f +#define WCD9335_CDC_RX0_RX_PATH_SEC7 0x0b50 +#define WCD9335_CDC_RX0_RX_PATH_MIX_SEC0 0x0b51 +#define WCD9335_CDC_RX0_RX_PATH_MIX_SEC1 0x0b52 +#define WCD9335_CDC_RX1_RX_PATH_CTL 0x0b55 +#define WCD9335_CDC_RX1_RX_PATH_CFG0 0x0b56 +#define WCD9335_CDC_RX1_RX_PATH_CFG1 0x0b57 +#define WCD9335_CDC_RX1_RX_PATH_CFG2 0x0b58 +#define WCD9335_CDC_RX1_RX_VOL_CTL 0x0b59 +#define WCD9335_CDC_RX1_RX_PATH_MIX_CTL 0x0b5a +#define WCD9335_CDC_RX1_RX_PATH_MIX_CFG 0x0b5b +#define WCD9335_CDC_RX1_RX_VOL_MIX_CTL 0x0b5c +#define WCD9335_CDC_RX1_RX_PATH_SEC0 0x0b5d +#define WCD9335_CDC_RX1_RX_PATH_SEC1 0x0b5e +#define WCD9335_CDC_RX1_RX_PATH_SEC2 0x0b5f +#define WCD9335_CDC_RX1_RX_PATH_SEC3 0x0b60 +#define WCD9335_CDC_RX1_RX_PATH_SEC4 0x0b61 +#define WCD9335_CDC_RX1_RX_PATH_SEC5 0x0b62 +#define WCD9335_CDC_RX1_RX_PATH_SEC6 0x0b63 +#define WCD9335_CDC_RX1_RX_PATH_SEC7 0x0b64 +#define WCD9335_CDC_RX1_RX_PATH_MIX_SEC0 0x0b65 +#define WCD9335_CDC_RX1_RX_PATH_MIX_SEC1 0x0b66 +#define WCD9335_CDC_RX2_RX_PATH_CTL 0x0b69 +#define WCD9335_CDC_RX2_RX_PATH_CFG0 0x0b6a +#define WCD9335_CDC_RX2_RX_PATH_CFG1 0x0b6b +#define WCD9335_CDC_RX2_RX_PATH_CFG2 0x0b6c +#define WCD9335_CDC_RX2_RX_VOL_CTL 0x0b6d +#define WCD9335_CDC_RX2_RX_PATH_MIX_CTL 0x0b6e +#define WCD9335_CDC_RX2_RX_PATH_MIX_CFG 0x0b6f +#define WCD9335_CDC_RX2_RX_VOL_MIX_CTL 0x0b70 +#define WCD9335_CDC_RX2_RX_PATH_SEC0 0x0b71 +#define WCD9335_CDC_RX2_RX_PATH_SEC1 0x0b72 +#define WCD9335_CDC_RX2_RX_PATH_SEC2 0x0b73 +#define WCD9335_CDC_RX2_RX_PATH_SEC3 0x0b74 +#define WCD9335_CDC_RX2_RX_PATH_SEC4 0x0b75 +#define WCD9335_CDC_RX2_RX_PATH_SEC5 0x0b76 +#define WCD9335_CDC_RX2_RX_PATH_SEC6 0x0b77 +#define WCD9335_CDC_RX2_RX_PATH_SEC7 0x0b78 +#define WCD9335_CDC_RX2_RX_PATH_MIX_SEC0 0x0b79 +#define WCD9335_CDC_RX2_RX_PATH_MIX_SEC1 0x0b7a +#define WCD9335_CDC_RX3_RX_PATH_CTL 0x0b7d +#define WCD9335_CDC_RX3_RX_PATH_CFG0 0x0b7e +#define WCD9335_CDC_RX3_RX_PATH_CFG1 0x0b7f +#define WCD9335_CDC_RX3_RX_PATH_CFG2 0x0b80 +#define WCD9335_CDC_RX3_RX_VOL_CTL 0x0b81 +#define WCD9335_CDC_RX3_RX_PATH_MIX_CTL 0x0b82 +#define WCD9335_CDC_RX3_RX_PATH_MIX_CFG 0x0b83 +#define WCD9335_CDC_RX3_RX_VOL_MIX_CTL 0x0b84 +#define WCD9335_CDC_RX3_RX_PATH_SEC0 0x0b85 +#define WCD9335_CDC_RX3_RX_PATH_SEC1 0x0b86 +#define WCD9335_CDC_RX3_RX_PATH_SEC2 0x0b87 +#define WCD9335_CDC_RX3_RX_PATH_SEC3 0x0b88 +#define WCD9335_CDC_RX3_RX_PATH_SEC5 0x0b8a +#define WCD9335_CDC_RX3_RX_PATH_SEC6 0x0b8b +#define WCD9335_CDC_RX3_RX_PATH_SEC7 0x0b8c +#define WCD9335_CDC_RX3_RX_PATH_MIX_SEC0 0x0b8d +#define WCD9335_CDC_RX3_RX_PATH_MIX_SEC1 0x0b8e +#define WCD9335_CDC_RX4_RX_PATH_CTL 0x0b91 +#define WCD9335_CDC_RX4_RX_PATH_CFG0 0x0b92 +#define WCD9335_CDC_RX4_RX_PATH_CFG1 0x0b93 +#define WCD9335_CDC_RX4_RX_PATH_CFG2 0x0b94 +#define WCD9335_CDC_RX4_RX_VOL_CTL 0x0b95 +#define WCD9335_CDC_RX4_RX_PATH_MIX_CTL 0x0b96 +#define WCD9335_CDC_RX4_RX_PATH_MIX_CFG 0x0b97 +#define WCD9335_CDC_RX4_RX_VOL_MIX_CTL 0x0b98 +#define WCD9335_CDC_RX4_RX_PATH_SEC0 0x0b99 +#define WCD9335_CDC_RX4_RX_PATH_SEC1 0x0b9a +#define WCD9335_CDC_RX4_RX_PATH_SEC2 0x0b9b +#define WCD9335_CDC_RX4_RX_PATH_SEC3 0x0b9c +#define WCD9335_CDC_RX4_RX_PATH_SEC5 0x0b9e +#define WCD9335_CDC_RX4_RX_PATH_SEC6 0x0b9f +#define WCD9335_CDC_RX4_RX_PATH_SEC7 0x0ba0 +#define WCD9335_CDC_RX4_RX_PATH_MIX_SEC0 0x0ba1 +#define WCD9335_CDC_RX4_RX_PATH_MIX_SEC1 0x0ba2 +#define WCD9335_CDC_RX5_RX_PATH_CTL 0x0ba5 +#define WCD9335_CDC_RX5_RX_PATH_CFG0 0x0ba6 +#define WCD9335_CDC_RX5_RX_PATH_CFG1 0x0ba7 +#define WCD9335_CDC_RX5_RX_PATH_CFG2 0x0ba8 +#define WCD9335_CDC_RX5_RX_VOL_CTL 0x0ba9 +#define WCD9335_CDC_RX5_RX_PATH_MIX_CTL 0x0baa +#define WCD9335_CDC_RX5_RX_PATH_MIX_CFG 0x0bab +#define WCD9335_CDC_RX5_RX_VOL_MIX_CTL 0x0bac +#define WCD9335_CDC_RX5_RX_PATH_SEC0 0x0bad +#define WCD9335_CDC_RX5_RX_PATH_SEC1 0x0bae +#define WCD9335_CDC_RX5_RX_PATH_SEC2 0x0baf +#define WCD9335_CDC_RX5_RX_PATH_SEC3 0x0bb0 +#define WCD9335_CDC_RX5_RX_PATH_SEC5 0x0bb2 +#define WCD9335_CDC_RX5_RX_PATH_SEC6 0x0bb3 +#define WCD9335_CDC_RX5_RX_PATH_SEC7 0x0bb4 +#define WCD9335_CDC_RX5_RX_PATH_MIX_SEC0 0x0bb5 +#define WCD9335_CDC_RX5_RX_PATH_MIX_SEC1 0x0bb6 +#define WCD9335_CDC_RX6_RX_PATH_CTL 0x0bb9 +#define WCD9335_CDC_RX6_RX_PATH_CFG0 0x0bba +#define WCD9335_CDC_RX6_RX_PATH_CFG1 0x0bbb +#define WCD9335_CDC_RX6_RX_PATH_CFG2 0x0bbc +#define WCD9335_CDC_RX6_RX_VOL_CTL 0x0bbd +#define WCD9335_CDC_RX6_RX_PATH_MIX_CTL 0x0bbe +#define WCD9335_CDC_RX6_RX_PATH_MIX_CFG 0x0bbf +#define WCD9335_CDC_RX6_RX_VOL_MIX_CTL 0x0bc0 +#define WCD9335_CDC_RX6_RX_PATH_SEC0 0x0bc1 +#define WCD9335_CDC_RX6_RX_PATH_SEC1 0x0bc2 +#define WCD9335_CDC_RX6_RX_PATH_SEC2 0x0bc3 +#define WCD9335_CDC_RX6_RX_PATH_SEC3 0x0bc4 +#define WCD9335_CDC_RX6_RX_PATH_SEC5 0x0bc6 +#define WCD9335_CDC_RX6_RX_PATH_SEC6 0x0bc7 +#define WCD9335_CDC_RX6_RX_PATH_SEC7 0x0bc8 +#define WCD9335_CDC_RX6_RX_PATH_MIX_SEC0 0x0bc9 +#define WCD9335_CDC_RX6_RX_PATH_MIX_SEC1 0x0bca +#define WCD9335_CDC_RX7_RX_PATH_CTL 0x0bcd +#define WCD9335_CDC_RX7_RX_PATH_CFG0 0x0bce +#define WCD9335_CDC_RX7_RX_PATH_CFG1 0x0bcf +#define WCD9335_CDC_RX7_RX_PATH_CFG2 0x0bd0 +#define WCD9335_CDC_RX7_RX_VOL_CTL 0x0bd1 +#define WCD9335_CDC_RX7_RX_PATH_MIX_CTL 0x0bd2 +#define WCD9335_CDC_RX7_RX_PATH_MIX_CFG 0x0bd3 +#define WCD9335_CDC_RX7_RX_VOL_MIX_CTL 0x0bd4 +#define WCD9335_CDC_RX7_RX_PATH_SEC0 0x0bd5 +#define WCD9335_CDC_RX7_RX_PATH_SEC1 0x0bd6 +#define WCD9335_CDC_RX7_RX_PATH_SEC2 0x0bd7 +#define WCD9335_CDC_RX7_RX_PATH_SEC3 0x0bd8 +#define WCD9335_CDC_RX7_RX_PATH_SEC5 0x0bda +#define WCD9335_CDC_RX7_RX_PATH_SEC6 0x0bdb +#define WCD9335_CDC_RX7_RX_PATH_SEC7 0x0bdc +#define WCD9335_CDC_RX7_RX_PATH_MIX_SEC0 0x0bdd +#define WCD9335_CDC_RX7_RX_PATH_MIX_SEC1 0x0bde +#define WCD9335_CDC_RX8_RX_PATH_CTL 0x0be1 +#define WCD9335_CDC_RX8_RX_PATH_CFG0 0x0be2 +#define WCD9335_CDC_RX8_RX_PATH_CFG1 0x0be3 +#define WCD9335_CDC_RX8_RX_PATH_CFG2 0x0be4 +#define WCD9335_CDC_RX8_RX_VOL_CTL 0x0be5 +#define WCD9335_CDC_RX8_RX_PATH_MIX_CTL 0x0be6 +#define WCD9335_CDC_RX8_RX_PATH_MIX_CFG 0x0be7 +#define WCD9335_CDC_RX8_RX_VOL_MIX_CTL 0x0be8 +#define WCD9335_CDC_RX8_RX_PATH_SEC0 0x0be9 +#define WCD9335_CDC_RX8_RX_PATH_SEC1 0x0bea +#define WCD9335_CDC_RX8_RX_PATH_SEC2 0x0beb +#define WCD9335_CDC_RX8_RX_PATH_SEC3 0x0bec +#define WCD9335_CDC_RX8_RX_PATH_SEC5 0x0bee +#define WCD9335_CDC_RX8_RX_PATH_SEC6 0x0bef +#define WCD9335_CDC_RX8_RX_PATH_SEC7 0x0bf0 +#define WCD9335_CDC_RX8_RX_PATH_MIX_SEC0 0x0bf1 +#define WCD9335_CDC_RX8_RX_PATH_MIX_SEC1 0x0bf2 + +/* Page-12 Registers */ +#define WCD9335_PAGE12_PAGE_REGISTER 0x0c00 +#define WCD9335_CDC_CLSH_CRC 0x0c01 +#define WCD9335_CDC_CLSH_DLY_CTRL 0x0c02 +#define WCD9335_CDC_CLSH_DECAY_CTRL 0x0c03 +#define WCD9335_CDC_CLSH_HPH_V_PA 0x0c04 +#define WCD9335_CDC_CLSH_EAR_V_PA 0x0c05 +#define WCD9335_CDC_CLSH_HPH_V_HD 0x0c06 +#define WCD9335_CDC_CLSH_EAR_V_HD 0x0c07 +#define WCD9335_CDC_CLSH_K1_MSB 0x0c08 +#define WCD9335_CDC_CLSH_K1_LSB 0x0c09 +#define WCD9335_CDC_CLSH_K2_MSB 0x0c0a +#define WCD9335_CDC_CLSH_K2_LSB 0x0c0b +#define WCD9335_CDC_CLSH_IDLE_CTRL 0x0c0c +#define WCD9335_CDC_CLSH_IDLE_HPH 0x0c0d +#define WCD9335_CDC_CLSH_IDLE_EAR 0x0c0e +#define WCD9335_CDC_CLSH_TEST0 0x0c0f +#define WCD9335_CDC_CLSH_TEST1 0x0c10 +#define WCD9335_CDC_CLSH_OVR_VREF 0x0c11 +#define WCD9335_CDC_BOOST0_BOOST_PATH_CTL 0x0c19 +#define WCD9335_CDC_BOOST0_BOOST_CTL 0x0c1a +#define WCD9335_CDC_BOOST0_BOOST_CFG1 0x0c1b +#define WCD9335_CDC_BOOST0_BOOST_CFG2 0x0c1c +#define WCD9335_CDC_BOOST1_BOOST_PATH_CTL 0x0c21 +#define WCD9335_CDC_BOOST1_BOOST_CTL 0x0c22 +#define WCD9335_CDC_BOOST1_BOOST_CFG1 0x0c23 +#define WCD9335_CDC_BOOST1_BOOST_CFG2 0x0c24 +#define WCD9335_SWR_AHB_BRIDGE_WR_DATA_0 0x0c29 +#define WCD9335_SWR_AHB_BRIDGE_WR_DATA_1 0x0c2a +#define WCD9335_SWR_AHB_BRIDGE_WR_DATA_2 0x0c2b +#define WCD9335_SWR_AHB_BRIDGE_WR_DATA_3 0x0c2c +#define WCD9335_SWR_AHB_BRIDGE_WR_ADDR_0 0x0c2d +#define WCD9335_SWR_AHB_BRIDGE_WR_ADDR_1 0x0c2e +#define WCD9335_SWR_AHB_BRIDGE_WR_ADDR_2 0x0c2f +#define WCD9335_SWR_AHB_BRIDGE_WR_ADDR_3 0x0c30 +#define WCD9335_SWR_AHB_BRIDGE_RD_ADDR_0 0x0c31 +#define WCD9335_SWR_AHB_BRIDGE_RD_ADDR_1 0x0c32 +#define WCD9335_SWR_AHB_BRIDGE_RD_ADDR_2 0x0c33 +#define WCD9335_SWR_AHB_BRIDGE_RD_ADDR_3 0x0c34 +#define WCD9335_SWR_AHB_BRIDGE_RD_DATA_0 0x0c35 +#define WCD9335_SWR_AHB_BRIDGE_RD_DATA_1 0x0c36 +#define WCD9335_SWR_AHB_BRIDGE_RD_DATA_2 0x0c37 +#define WCD9335_SWR_AHB_BRIDGE_RD_DATA_3 0x0c38 +#define WCD9335_SWR_AHB_BRIDGE_ACCESS_CFG 0x0c39 +#define WCD9335_SWR_AHB_BRIDGE_ACCESS_STATUS 0x0c3a +#define WCD9335_CDC_VBAT_VBAT_PATH_CTL 0x0c3d +#define WCD9335_CDC_VBAT_VBAT_CFG 0x0c3e +#define WCD9335_CDC_VBAT_VBAT_ADC_CAL1 0x0c3f +#define WCD9335_CDC_VBAT_VBAT_ADC_CAL2 0x0c40 +#define WCD9335_CDC_VBAT_VBAT_ADC_CAL3 0x0c41 +#define WCD9335_CDC_VBAT_VBAT_PK_EST1 0x0c42 +#define WCD9335_CDC_VBAT_VBAT_PK_EST2 0x0c43 +#define WCD9335_CDC_VBAT_VBAT_PK_EST3 0x0c44 +#define WCD9335_CDC_VBAT_VBAT_RF_PROC1 0x0c45 +#define WCD9335_CDC_VBAT_VBAT_RF_PROC2 0x0c46 +#define WCD9335_CDC_VBAT_VBAT_TAC1 0x0c47 +#define WCD9335_CDC_VBAT_VBAT_TAC2 0x0c48 +#define WCD9335_CDC_VBAT_VBAT_TAC3 0x0c49 +#define WCD9335_CDC_VBAT_VBAT_TAC4 0x0c4a +#define WCD9335_CDC_VBAT_VBAT_GAIN_UPD1 0x0c4b +#define WCD9335_CDC_VBAT_VBAT_GAIN_UPD2 0x0c4c +#define WCD9335_CDC_VBAT_VBAT_GAIN_UPD3 0x0c4d +#define WCD9335_CDC_VBAT_VBAT_GAIN_UPD4 0x0c4e +#define WCD9335_CDC_VBAT_VBAT_DEBUG1 0x0c4f +#define WCD9335_CDC_VBAT_VBAT_GAIN_UPD_MON 0x0c50 +#define WCD9335_CDC_VBAT_VBAT_GAIN_MON_VAL 0x0c51 +#define WCD9335_SPLINE_SRC0_CLK_RST_CTL_0 0x0c55 +#define WCD9335_SPLINE_SRC0_STATUS 0x0c56 +#define WCD9335_SPLINE_SRC1_CLK_RST_CTL_0 0x0c6d +#define WCD9335_SPLINE_SRC1_STATUS 0x0c6e +#define WCD9335_SPLINE_SRC2_CLK_RST_CTL_0 0x0c85 +#define WCD9335_SPLINE_SRC2_STATUS 0x0c86 +#define WCD9335_SPLINE_SRC3_CLK_RST_CTL_0 0x0c9d +#define WCD9335_SPLINE_SRC3_STATUS 0x0c9e +#define WCD9335_CDC_SIDETONE_SRC0_ST_SRC_PATH_CTL 0x0cb5 +#define WCD9335_CDC_SIDETONE_SRC0_ST_SRC_PATH_CFG1 0x0cb6 +#define WCD9335_CDC_SIDETONE_SRC1_ST_SRC_PATH_CTL 0x0cb9 +#define WCD9335_CDC_SIDETONE_SRC1_ST_SRC_PATH_CFG1 0x0cba + +/* Page-13 Registers */ +#define WCD9335_PAGE13_PAGE_REGISTER 0x0d00 +#define WCD9335_CDC_RX_INP_MUX_RX_INT0_CFG0 0x0d01 +#define WCD9335_CDC_RX_INP_MUX_RX_INT0_CFG1 0x0d02 +#define WCD9335_CDC_RX_INP_MUX_RX_INT1_CFG0 0x0d03 +#define WCD9335_CDC_RX_INP_MUX_RX_INT1_CFG1 0x0d04 +#define WCD9335_CDC_RX_INP_MUX_RX_INT2_CFG0 0x0d05 +#define WCD9335_CDC_RX_INP_MUX_RX_INT2_CFG1 0x0d06 +#define WCD9335_CDC_RX_INP_MUX_RX_INT3_CFG0 0x0d07 +#define WCD9335_CDC_RX_INP_MUX_RX_INT3_CFG1 0x0d08 +#define WCD9335_CDC_RX_INP_MUX_RX_INT4_CFG0 0x0d09 +#define WCD9335_CDC_RX_INP_MUX_RX_INT4_CFG1 0x0d0a +#define WCD9335_CDC_RX_INP_MUX_RX_INT5_CFG0 0x0d0b +#define WCD9335_CDC_RX_INP_MUX_RX_INT5_CFG1 0x0d0c +#define WCD9335_CDC_RX_INP_MUX_RX_INT6_CFG0 0x0d0d +#define WCD9335_CDC_RX_INP_MUX_RX_INT6_CFG1 0x0d0e +#define WCD9335_CDC_RX_INP_MUX_RX_INT7_CFG0 0x0d0f +#define WCD9335_CDC_RX_INP_MUX_RX_INT7_CFG1 0x0d10 +#define WCD9335_CDC_RX_INP_MUX_RX_INT8_CFG0 0x0d11 +#define WCD9335_CDC_RX_INP_MUX_RX_INT8_CFG1 0x0d12 +#define WCD9335_CDC_RX_INP_MUX_RX_MIX_CFG0 0x0d13 +#define WCD9335_CDC_RX_INP_MUX_RX_MIX_CFG1 0x0d14 +#define WCD9335_CDC_RX_INP_MUX_RX_MIX_CFG2 0x0d15 +#define WCD9335_CDC_RX_INP_MUX_RX_MIX_CFG3 0x0d16 +#define WCD9335_CDC_RX_INP_MUX_RX_MIX_CFG4 0x0d17 +#define WCD9335_CDC_RX_INP_MUX_SIDETONE_SRC_CFG0 0x0d18 +#define WCD9335_CDC_RX_INP_MUX_SIDETONE_SRC_CFG1 0x0d19 +#define WCD9335_CDC_RX_INP_MUX_ANC_CFG0 0x0d1a +#define WCD9335_CDC_RX_INP_MUX_SPLINE_SRC_CFG0 0x0d1b +#define WCD9335_CDC_TX_INP_MUX_ADC_MUX0_CFG0 0x0d1d +#define WCD9335_CDC_TX_INP_MUX_ADC_MUX0_CFG1 0x0d1e +#define WCD9335_CDC_TX_INP_MUX_ADC_MUX1_CFG0 0x0d1f +#define WCD9335_CDC_TX_INP_MUX_ADC_MUX1_CFG1 0x0d20 +#define WCD9335_CDC_TX_INP_MUX_ADC_MUX2_CFG0 0x0d21 +#define WCD9335_CDC_TX_INP_MUX_ADC_MUX2_CFG1 0x0d22 +#define WCD9335_CDC_TX_INP_MUX_ADC_MUX3_CFG0 0x0d23 +#define WCD9335_CDC_TX_INP_MUX_ADC_MUX3_CFG1 0x0d24 +#define WCD9335_CDC_TX_INP_MUX_ADC_MUX4_CFG0 0x0d25 +#define WCD9335_CDC_TX_INP_MUX_ADC_MUX5_CFG0 0x0d26 +#define WCD9335_CDC_TX_INP_MUX_ADC_MUX6_CFG0 0x0d27 +#define WCD9335_CDC_TX_INP_MUX_ADC_MUX7_CFG0 0x0d28 +#define WCD9335_CDC_TX_INP_MUX_ADC_MUX8_CFG0 0x0d29 +#define WCD9335_CDC_TX_INP_MUX_ADC_MUX10_CFG0 0x0d2b +#define WCD9335_CDC_TX_INP_MUX_ADC_MUX11_CFG0 0x0d2c +#define WCD9335_CDC_TX_INP_MUX_ADC_MUX12_CFG0 0x0d2d +#define WCD9335_CDC_TX_INP_MUX_ADC_MUX13_CFG0 0x0d2e +#define WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG0 0x0d31 +#define WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG1 0x0d32 +#define WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG2 0x0d33 +#define WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG3 0x0d34 +#define WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG0 0x0d35 +#define WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG1 0x0d36 +#define WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG2 0x0d37 +#define WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG3 0x0d38 +#define WCD9335_CDC_IF_ROUTER_TX_MUX_CFG0 0x0d3a +#define WCD9335_CDC_IF_ROUTER_TX_MUX_CFG1 0x0d3b +#define WCD9335_CDC_IF_ROUTER_TX_MUX_CFG2 0x0d3c +#define WCD9335_CDC_IF_ROUTER_TX_MUX_CFG3 0x0d3d +#define WCD9335_CDC_CLK_RST_CTRL_MCLK_CONTROL 0x0d41 +#define WCD9335_CDC_CLK_RST_CTRL_FS_CNT_CONTROL 0x0d42 +#define WCD9335_CDC_CLK_RST_CTRL_SWR_CONTROL 0x0d43 +#define WCD9335_CDC_PROX_DETECT_PROX_CTL 0x0d49 +#define WCD9335_CDC_PROX_DETECT_PROX_POLL_PERIOD0 0x0d4a +#define WCD9335_CDC_PROX_DETECT_PROX_POLL_PERIOD1 0x0d4b +#define WCD9335_CDC_PROX_DETECT_PROX_SIG_PATTERN_LSB 0x0d4c +#define WCD9335_CDC_PROX_DETECT_PROX_SIG_PATTERN_MSB 0x0d4d +#define WCD9335_CDC_PROX_DETECT_PROX_STATUS 0x0d4e +#define WCD9335_CDC_PROX_DETECT_PROX_TEST_CTRL 0x0d4f +#define WCD9335_CDC_PROX_DETECT_PROX_TEST_BUFF_LSB 0x0d50 +#define WCD9335_CDC_PROX_DETECT_PROX_TEST_BUFF_MSB 0x0d51 +#define WCD9335_CDC_PROX_DETECT_PROX_TEST_BUFF_LSB_RD 0x0d52 +#define WCD9335_CDC_PROX_DETECT_PROX_TEST_BUFF_MSB_RD 0x0d53 +#define WCD9335_CDC_PROX_DETECT_PROX_CTL_REPEAT_PAT 0x0d54 +#define WCD9335_CDC_SIDETONE_IIR0_IIR_PATH_CTL 0x0d55 +#define WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B1_CTL 0x0d56 +#define WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B2_CTL 0x0d57 +#define WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B3_CTL 0x0d58 +#define WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B4_CTL 0x0d59 +#define WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B5_CTL 0x0d5a +#define WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B6_CTL 0x0d5b +#define WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B7_CTL 0x0d5c +#define WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B8_CTL 0x0d5d +#define WCD9335_CDC_SIDETONE_IIR0_IIR_CTL 0x0d5e +#define WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_TIMER_CTL 0x0d5f +#define WCD9335_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL 0x0d60 +#define WCD9335_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL 0x0d61 +#define WCD9335_CDC_SIDETONE_IIR1_IIR_PATH_CTL 0x0d65 +#define WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B1_CTL 0x0d66 +#define WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B2_CTL 0x0d67 +#define WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B3_CTL 0x0d68 +#define WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B4_CTL 0x0d69 +#define WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B5_CTL 0x0d6a +#define WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B6_CTL 0x0d6b +#define WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B7_CTL 0x0d6c +#define WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B8_CTL 0x0d6d +#define WCD9335_CDC_SIDETONE_IIR1_IIR_CTL 0x0d6e +#define WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_TIMER_CTL 0x0d6f +#define WCD9335_CDC_SIDETONE_IIR1_IIR_COEF_B1_CTL 0x0d70 +#define WCD9335_CDC_SIDETONE_IIR1_IIR_COEF_B2_CTL 0x0d71 +#define WCD9335_CDC_TOP_TOP_CFG0 0x0d81 +#define WCD9335_CDC_TOP_TOP_CFG1 0x0d82 +#define WCD9335_CDC_TOP_TOP_CFG2 0x0d83 +#define WCD9335_CDC_TOP_TOP_CFG3 0x0d84 +#define WCD9335_CDC_TOP_TOP_CFG4 0x0d85 +#define WCD9335_CDC_TOP_TOP_CFG5 0x0d86 +#define WCD9335_CDC_TOP_TOP_CFG6 0x0d87 +#define WCD9335_CDC_TOP_TOP_CFG7 0x0d88 +#define WCD9335_CDC_TOP_HPHL_COMP_WR_LSB 0x0d89 +#define WCD9335_CDC_TOP_HPHL_COMP_WR_MSB 0x0d8a +#define WCD9335_CDC_TOP_HPHL_COMP_LUT 0x0d8b +#define WCD9335_CDC_TOP_HPHL_COMP_RD_LSB 0x0d8c +#define WCD9335_CDC_TOP_HPHL_COMP_RD_MSB 0x0d8d +#define WCD9335_CDC_TOP_HPHR_COMP_WR_LSB 0x0d8e +#define WCD9335_CDC_TOP_HPHR_COMP_WR_MSB 0x0d8f +#define WCD9335_CDC_TOP_HPHR_COMP_LUT 0x0d90 +#define WCD9335_CDC_TOP_HPHR_COMP_RD_LSB 0x0d91 +#define WCD9335_CDC_TOP_HPHR_COMP_RD_MSB 0x0d92 +#define WCD9335_CDC_TOP_DIFFL_COMP_WR_LSB 0x0d93 +#define WCD9335_CDC_TOP_DIFFL_COMP_WR_MSB 0x0d94 +#define WCD9335_CDC_TOP_DIFFL_COMP_LUT 0x0d95 +#define WCD9335_CDC_TOP_DIFFL_COMP_RD_LSB 0x0d96 +#define WCD9335_CDC_TOP_DIFFL_COMP_RD_MSB 0x0d97 +#define WCD9335_CDC_TOP_DIFFR_COMP_WR_LSB 0x0d98 +#define WCD9335_CDC_TOP_DIFFR_COMP_WR_MSB 0x0d99 +#define WCD9335_CDC_TOP_DIFFR_COMP_LUT 0x0d9a +#define WCD9335_CDC_TOP_DIFFR_COMP_RD_LSB 0x0d9b +#define WCD9335_CDC_TOP_DIFFR_COMP_RD_MSB 0x0d9c + +/* Page-0x80 Registers */ +#define WCD9335_PAGE80_PAGE_REGISTER 0x8000 +#define WCD9335_TLMM_BIST_MODE_PINCFG 0x8001 +#define WCD9335_TLMM_RF_PA_ON_PINCFG 0x8002 +#define WCD9335_TLMM_INTR1_PINCFG 0x8003 +#define WCD9335_TLMM_INTR2_PINCFG 0x8004 +#define WCD9335_TLMM_SWR_DATA_PINCFG 0x8005 +#define WCD9335_TLMM_SWR_CLK_PINCFG 0x8006 +#define WCD9335_TLMM_SLIMBUS_DATA2_PINCFG 0x8007 +#define WCD9335_TLMM_I2C_CLK_PINCFG 0x8008 +#define WCD9335_TLMM_I2C_DATA_PINCFG 0x8009 +#define WCD9335_TLMM_I2S_RX_SD0_PINCFG 0x800a +#define WCD9335_TLMM_I2S_RX_SD1_PINCFG 0x800b +#define WCD9335_TLMM_I2S_RX_SCK_PINCFG 0x800c +#define WCD9335_TLMM_I2S_RX_WS_PINCFG 0x800d +#define WCD9335_TLMM_I2S_TX_SD0_PINCFG 0x800e +#define WCD9335_TLMM_I2S_TX_SD1_PINCFG 0x800f +#define WCD9335_TLMM_I2S_TX_SCK_PINCFG 0x8010 +#define WCD9335_TLMM_I2S_TX_WS_PINCFG 0x8011 +#define WCD9335_TLMM_DMIC1_CLK_PINCFG 0x8012 +#define WCD9335_TLMM_DMIC1_DATA_PINCFG 0x8013 +#define WCD9335_TLMM_DMIC2_CLK_PINCFG 0x8014 +#define WCD9335_TLMM_DMIC2_DATA_PINCFG 0x8015 +#define WCD9335_TLMM_DMIC3_CLK_PINCFG 0x8016 +#define WCD9335_TLMM_DMIC3_DATA_PINCFG 0x8017 +#define WCD9335_TLMM_JTDI_PINCFG 0x8018 +#define WCD9335_TLMM_JTDO_PINCFG 0x8019 +#define WCD9335_TLMM_JTMS_PINCFG 0x801a +#define WCD9335_TLMM_JTCK_PINCFG 0x801b +#define WCD9335_TLMM_JTRST_PINCFG 0x801c +#define WCD9335_TEST_DEBUG_PIN_CTL_OE_0 0x8031 +#define WCD9335_TEST_DEBUG_PIN_CTL_OE_1 0x8032 +#define WCD9335_TEST_DEBUG_PIN_CTL_OE_2 0x8033 +#define WCD9335_TEST_DEBUG_PIN_CTL_OE_3 0x8034 +#define WCD9335_TEST_DEBUG_PIN_CTL_DATA_0 0x8035 +#define WCD9335_TEST_DEBUG_PIN_CTL_DATA_1 0x8036 +#define WCD9335_TEST_DEBUG_PIN_CTL_DATA_2 0x8037 +#define WCD9335_TEST_DEBUG_PIN_CTL_DATA_3 0x8038 +#define WCD9335_TEST_DEBUG_PAD_DRVCTL 0x8039 +#define WCD9335_TEST_DEBUG_PIN_STATUS 0x803a +#define WCD9335_TEST_DEBUG_NPL_DLY_TEST_1 0x803b +#define WCD9335_TEST_DEBUG_NPL_DLY_TEST_2 0x803c +#define WCD9335_TEST_DEBUG_MEM_CTRL 0x803d +#define WCD9335_TEST_DEBUG_DEBUG_BUS_SEL 0x8041 +#define WCD9335_TEST_DEBUG_DEBUG_JTAG 0x8042 +#define WCD9335_TEST_DEBUG_DEBUG_EN_1 0x8043 +#define WCD9335_TEST_DEBUG_DEBUG_EN_2 0x8044 +#define WCD9335_TEST_DEBUG_DEBUG_EN_3 0x8045 +#define WCD9335_MAX_REGISTER 0x80FF + +/* SLIMBUS Slave Registers */ +#define TASHA_SLIM_PGD_PORT_INT_EN0 (0x30) +#define TASHA_SLIM_PGD_PORT_INT_STATUS_RX_0 (0x34) +#define TASHA_SLIM_PGD_PORT_INT_STATUS_RX_1 (0x35) +#define TASHA_SLIM_PGD_PORT_INT_STATUS_TX_0 (0x36) +#define TASHA_SLIM_PGD_PORT_INT_STATUS_TX_1 (0x37) +#define TASHA_SLIM_PGD_PORT_INT_CLR_RX_0 (0x38) +#define TASHA_SLIM_PGD_PORT_INT_CLR_RX_1 (0x39) +#define TASHA_SLIM_PGD_PORT_INT_CLR_TX_0 (0x3A) +#define TASHA_SLIM_PGD_PORT_INT_CLR_TX_1 (0x3B) +#define TASHA_SLIM_PGD_PORT_INT_RX_SOURCE0 (0x60) +#define TASHA_SLIM_PGD_PORT_INT_TX_SOURCE0 (0x70) + +/* Macros for Packing Register Writes into a U32 */ +#define TASHA_PACKED_REG_SIZE sizeof(u32) + +#define TASHA_CODEC_PACK_ENTRY(reg, mask, val) ((val & 0xff)|\ + ((mask & 0xff) << 8)|((reg & 0xffff) << 16)) +#define TASHA_CODEC_UNPACK_ENTRY(packed, reg, mask, val) \ + do { \ + ((reg) = ((packed >> 16) & (0xffff))); \ + ((mask) = ((packed >> 8) & (0xff))); \ + ((val) = ((packed) & (0xff))); \ + } while (0) +#endif diff --git a/audio-kernel/asoc/codecs/wcd934x/Android.mk b/audio-kernel/asoc/codecs/wcd934x/Android.mk new file mode 100644 index 000000000..3b5ef2fa1 --- /dev/null +++ b/audio-kernel/asoc/codecs/wcd934x/Android.mk @@ -0,0 +1,62 @@ +# Android makefile for audio kernel modules + +# Assume no targets will be supported + +# Check if this driver needs be built for current target +ifeq ($(call is-board-platform,sdm845),true) +AUDIO_SELECT := CONFIG_SND_SOC_SDM845=m +endif + +ifeq ($(call is-board-platform,msmnile),true) +AUDIO_SELECT := CONFIG_SND_SOC_SM8150=m +endif + +ifeq ($(call is-board-platform,$(MSMSTEPPE) $(TRINKET)),true) +AUDIO_SELECT := CONFIG_SND_SOC_SM6150=m +endif + +ifeq ($(call is-board-platform-in-list,msm8953 sdm670 qcs605),true) +AUDIO_SELECT := CONFIG_SND_SOC_SDM670=m +endif + +AUDIO_CHIPSET := audio +# Build/Package only in case of supported target +ifeq ($(call is-board-platform-in-list,msm8953 sdm845 sdm670 qcs605 msmnile $(MSMSTEPPE) $(TRINKET)),true) + +LOCAL_PATH := $(call my-dir) + +# This makefile is only for DLKM +ifneq ($(findstring vendor,$(LOCAL_PATH)),) + +ifneq ($(findstring opensource,$(LOCAL_PATH)),) + AUDIO_BLD_DIR := $(shell pwd)/vendor/qcom/opensource/audio-kernel +endif # opensource + +DLKM_DIR := $(TOP)/device/qcom/common/dlkm + +# Build audio.ko as $(AUDIO_CHIPSET)_audio.ko +########################################################### +# This is set once per LOCAL_PATH, not per (kernel) module +KBUILD_OPTIONS := AUDIO_ROOT=$(AUDIO_BLD_DIR) + +# We are actually building audio.ko here, as per the +# requirement we are specifying _audio.ko as LOCAL_MODULE. +# This means we need to rename the module to _audio.ko +# after audio.ko is built. +KBUILD_OPTIONS += MODNAME=wcd934x_dlkm +KBUILD_OPTIONS += BOARD_PLATFORM=$(TARGET_BOARD_PLATFORM) +KBUILD_OPTIONS += $(AUDIO_SELECT) + +########################################################### +include $(CLEAR_VARS) +LOCAL_MODULE := $(AUDIO_CHIPSET)_wcd934x.ko +LOCAL_MODULE_KBUILD_NAME := wcd934x_dlkm.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_DEBUG_ENABLE := true +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk +########################################################### +########################################################### + +endif # DLKM check +endif # supported target check diff --git a/audio-kernel/asoc/codecs/wcd934x/Kbuild b/audio-kernel/asoc/codecs/wcd934x/Kbuild new file mode 100644 index 000000000..34e7d6201 --- /dev/null +++ b/audio-kernel/asoc/codecs/wcd934x/Kbuild @@ -0,0 +1,140 @@ +# We can build either as part of a standalone Kernel build or as +# an external module. Determine which mechanism is being used +ifeq ($(MODNAME),) + KERNEL_BUILD := 1 +else + KERNEL_BUILD := 0 +endif + + + +ifeq ($(KERNEL_BUILD), 1) + # These are configurable via Kconfig for kernel-based builds + # Need to explicitly configure for Android-based builds + AUDIO_BLD_DIR := $(shell pwd)/kernel/msm-4.9 + AUDIO_ROOT := $(AUDIO_BLD_DIR)/techpack/audio +endif + +ifeq ($(KERNEL_BUILD), 0) + ifeq ($(CONFIG_ARCH_SDM845), y) + include $(AUDIO_ROOT)/config/sdm845auto.conf + export + INCS += -include $(AUDIO_ROOT)/config/sdm845autoconf.h + endif + ifeq ($(CONFIG_ARCH_SDM670), y) + include $(AUDIO_ROOT)/config/sdm670auto.conf + export + INCS += -include $(AUDIO_ROOT)/config/sdm670autoconf.h + endif + ifeq ($(CONFIG_ARCH_SDM450), y) + include $(AUDIO_ROOT)/config/sdm670auto.conf + export + INCS += -include $(AUDIO_ROOT)/config/sdm670autoconf.h + endif + ifeq ($(CONFIG_ARCH_SM6150), y) + include $(AUDIO_ROOT)/config/sm6150auto.conf + export + INCS += -include $(AUDIO_ROOT)/config/sm6150autoconf.h + endif + ifeq ($(CONFIG_ARCH_TRINKET), y) + include $(AUDIO_ROOT)/config/trinketauto.conf + export + INCS += -include $(AUDIO_ROOT)/config/trinketautoconf.h + endif + ifeq ($(CONFIG_ARCH_SM8150), y) + include $(AUDIO_ROOT)/config/sm8150auto.conf + export + INCS += -include $(AUDIO_ROOT)/config/sm8150autoconf.h + endif + ifeq ($(CONFIG_ARCH_SDMSHRIKE), y) + include $(AUDIO_ROOT)/config/sm8150auto.conf + export + INCS += -include $(AUDIO_ROOT)/config/sm8150autoconf.h + endif +endif + +# As per target team, build is done as follows: +# Defconfig : build with default flags +# Slub : defconfig + CONFIG_SLUB_DEBUG := y + +# CONFIG_SLUB_DEBUG_ON := y + CONFIG_PAGE_POISONING := y +# Perf : Using appropriate msmXXXX-perf_defconfig +# +# Shipment builds (user variants) should not have any debug feature +# enabled. This is identified using 'TARGET_BUILD_VARIANT'. Slub builds +# are identified using the CONFIG_SLUB_DEBUG_ON configuration. Since +# there is no other way to identify defconfig builds, QTI internal +# representation of perf builds (identified using the string 'perf'), +# is used to identify if the build is a slub or defconfig one. This +# way no critical debug feature will be enabled for perf and shipment +# builds. Other OEMs are also protected using the TARGET_BUILD_VARIANT +# config. + +############ UAPI ############ +UAPI_DIR := uapi +UAPI_INC := -I$(AUDIO_ROOT)/include/$(UAPI_DIR) + +############ COMMON ############ +COMMON_DIR := include +COMMON_INC := -I$(AUDIO_ROOT)/$(COMMON_DIR) + +############ WCD934X ############ + +# for WCD934X Codec +ifdef CONFIG_SND_SOC_WCD934X + WCD934X_OBJS += wcd934x.o + WCD934X_OBJS += wcd934x-dsp-cntl.o + WCD934X_OBJS += wcd934x-mbhc.o + WCD934X_OBJS += wcd934x-dsd.o +endif + +LINUX_INC += -Iinclude/linux + +INCS += $(COMMON_INC) \ + $(UAPI_INC) + +EXTRA_CFLAGS += $(INCS) + + +CDEFINES += -DANI_LITTLE_BYTE_ENDIAN \ + -DANI_LITTLE_BIT_ENDIAN \ + -DDOT11F_LITTLE_ENDIAN_HOST \ + -DANI_COMPILER_TYPE_GCC \ + -DANI_OS_TYPE_ANDROID=6 \ + -DPTT_SOCK_SVC_ENABLE \ + -Wall\ + -Werror\ + -D__linux__ + +KBUILD_CPPFLAGS += $(CDEFINES) + +# Currently, for versions of gcc which support it, the kernel Makefile +# is disabling the maybe-uninitialized warning. Re-enable it for the +# AUDIO driver. Note that we must use EXTRA_CFLAGS here so that it +# will override the kernel settings. +ifeq ($(call cc-option-yn, -Wmaybe-uninitialized),y) +EXTRA_CFLAGS += -Wmaybe-uninitialized +endif +#EXTRA_CFLAGS += -Wmissing-prototypes + +ifeq ($(call cc-option-yn, -Wheader-guard),y) +EXTRA_CFLAGS += -Wheader-guard +endif + +ifeq ($(KERNEL_BUILD), 0) +KBUILD_EXTRA_SYMBOLS +=$(OUT)/obj/vendor/qcom/opensource/audio-kernel/ipc/Module.symvers +KBUILD_EXTRA_SYMBOLS +=$(OUT)/obj/vendor/qcom/opensource/audio-kernel/dsp/Module.symvers +KBUILD_EXTRA_SYMBOLS +=$(OUT)/obj/vendor/qcom/opensource/audio-kernel/asoc/Module.symvers +KBUILD_EXTRA_SYMBOLS +=$(OUT)/obj/vendor/qcom/opensource/audio-kernel/asoc/codecs/Module.symvers +KBUILD_EXTRA_SYMBOLS +=$(OUT)/obj/vendor/qcom/opensource/audio-kernel/soc/Module.symvers +endif + +ifeq ($(CONFIG_SND_SOC_GCOV), y) +GCOV_PROFILE := y +endif + +# Module information used by KBuild framework +obj-$(CONFIG_SND_SOC_WCD934X) += wcd934x_dlkm.o +wcd934x_dlkm-y := $(WCD934X_OBJS) + +# inject some build related information +DEFINES += -DBUILD_TIMESTAMP=\"$(shell date -u +'%Y-%m-%dT%H:%M:%SZ')\" diff --git a/audio-kernel/asoc/codecs/wcd934x/wcd934x-dsd.c b/audio-kernel/asoc/codecs/wcd934x/wcd934x-dsd.c new file mode 100644 index 000000000..eea18c3d9 --- /dev/null +++ b/audio-kernel/asoc/codecs/wcd934x/wcd934x-dsd.c @@ -0,0 +1,772 @@ +/* Copyright (c) 2016-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 + * 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 +#include +#include +#include +#include +#include "wcd934x-dsd.h" + +#define DSD_VOLUME_MAX_0dB 0 +#define DSD_VOLUME_MIN_M110dB -110 + +#define DSD_VOLUME_RANGE_CHECK(x) ((x >= DSD_VOLUME_MIN_M110dB) &&\ + (x <= DSD_VOLUME_MAX_0dB)) +#define DSD_VOLUME_STEPS 3 +#define DSD_VOLUME_UPDATE_DELAY_MS 30 +#define DSD_VOLUME_USLEEP_MARGIN_US 100 +#define DSD_VOLUME_STEP_DELAY_US ((1000 * DSD_VOLUME_UPDATE_DELAY_MS) / \ + (2 * DSD_VOLUME_STEPS)) + +#define TAVIL_VERSION_1_0 0 +#define TAVIL_VERSION_1_1 1 + +static const DECLARE_TLV_DB_MINMAX(tavil_dsd_db_scale, DSD_VOLUME_MIN_M110dB, + DSD_VOLUME_MAX_0dB); + +static const char *const dsd_if_text[] = { + "ZERO", "RX0", "RX1", "RX2", "RX3", "RX4", "RX5", "RX6", "RX7", + "DSD_DATA_PAD" +}; + +static const char * const dsd_filt0_mux_text[] = { + "ZERO", "DSD_L IF MUX", +}; + +static const char * const dsd_filt1_mux_text[] = { + "ZERO", "DSD_R IF MUX", +}; + +static const struct soc_enum dsd_filt0_mux_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_DSD0_PATH_CTL, 0, + ARRAY_SIZE(dsd_filt0_mux_text), dsd_filt0_mux_text); + +static const struct soc_enum dsd_filt1_mux_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_DSD1_PATH_CTL, 0, + ARRAY_SIZE(dsd_filt1_mux_text), dsd_filt1_mux_text); + +static SOC_ENUM_SINGLE_DECL(dsd_l_if_enum, WCD934X_CDC_DSD0_CFG0, + 2, dsd_if_text); +static SOC_ENUM_SINGLE_DECL(dsd_r_if_enum, WCD934X_CDC_DSD1_CFG0, + 2, dsd_if_text); + +static const struct snd_kcontrol_new dsd_filt0_mux = + SOC_DAPM_ENUM("DSD Filt0 Mux", dsd_filt0_mux_enum); + +static const struct snd_kcontrol_new dsd_filt1_mux = + SOC_DAPM_ENUM("DSD Filt1 Mux", dsd_filt1_mux_enum); + +static const struct snd_kcontrol_new dsd_l_if_mux = + SOC_DAPM_ENUM("DSD Left If Mux", dsd_l_if_enum); +static const struct snd_kcontrol_new dsd_r_if_mux = + SOC_DAPM_ENUM("DSD Right If Mux", dsd_r_if_enum); + +static const struct snd_soc_dapm_route tavil_dsd_audio_map[] = { + {"DSD_L IF MUX", "RX0", "CDC_IF RX0 MUX"}, + {"DSD_L IF MUX", "RX1", "CDC_IF RX1 MUX"}, + {"DSD_L IF MUX", "RX2", "CDC_IF RX2 MUX"}, + {"DSD_L IF MUX", "RX3", "CDC_IF RX3 MUX"}, + {"DSD_L IF MUX", "RX4", "CDC_IF RX4 MUX"}, + {"DSD_L IF MUX", "RX5", "CDC_IF RX5 MUX"}, + {"DSD_L IF MUX", "RX6", "CDC_IF RX6 MUX"}, + {"DSD_L IF MUX", "RX7", "CDC_IF RX7 MUX"}, + + {"DSD_FILTER_0", NULL, "DSD_L IF MUX"}, + {"DSD_FILTER_0", NULL, "RX INT1 NATIVE SUPPLY"}, + {"RX INT1 MIX3", "DSD HPHL Switch", "DSD_FILTER_0"}, + + {"DSD_R IF MUX", "RX0", "CDC_IF RX0 MUX"}, + {"DSD_R IF MUX", "RX1", "CDC_IF RX1 MUX"}, + {"DSD_R IF MUX", "RX2", "CDC_IF RX2 MUX"}, + {"DSD_R IF MUX", "RX3", "CDC_IF RX3 MUX"}, + {"DSD_R IF MUX", "RX4", "CDC_IF RX4 MUX"}, + {"DSD_R IF MUX", "RX5", "CDC_IF RX5 MUX"}, + {"DSD_R IF MUX", "RX6", "CDC_IF RX6 MUX"}, + {"DSD_R IF MUX", "RX7", "CDC_IF RX7 MUX"}, + + {"DSD_FILTER_1", NULL, "DSD_R IF MUX"}, + {"DSD_FILTER_1", NULL, "RX INT2 NATIVE SUPPLY"}, + {"RX INT2 MIX3", "DSD HPHR Switch", "DSD_FILTER_1"}, + + {"DSD_FILTER_0", NULL, "RX INT3 NATIVE SUPPLY"}, + {"RX INT3 MIX3", "DSD LO1 Switch", "DSD_FILTER_0"}, + {"DSD_FILTER_1", NULL, "RX INT4 NATIVE SUPPLY"}, + {"RX INT4 MIX3", "DSD LO2 Switch", "DSD_FILTER_1"}, +}; + +static bool is_valid_dsd_interpolator(int interp_num) +{ + if ((interp_num == INTERP_HPHL) || (interp_num == INTERP_HPHR) || + (interp_num == INTERP_LO1) || (interp_num == INTERP_LO2)) + return true; + + return false; +} + +/** + * tavil_dsd_set_mixer_value - Set DSD HPH/LO mixer value + * + * @dsd_conf: pointer to dsd config + * @interp_num: Interpolator number (HPHL/R, LO1/2) + * @sw_value: Mixer switch value + * + * Returns 0 on success or -EINVAL on failure + */ +int tavil_dsd_set_mixer_value(struct tavil_dsd_config *dsd_conf, + int interp_num, int sw_value) +{ + if (!dsd_conf) + return -EINVAL; + + if (!is_valid_dsd_interpolator(interp_num)) + return -EINVAL; + + dsd_conf->dsd_interp_mixer[interp_num] = !!sw_value; + + return 0; +} +EXPORT_SYMBOL(tavil_dsd_set_mixer_value); + +/** + * tavil_dsd_get_current_mixer_value - Get DSD HPH/LO mixer value + * + * @dsd_conf: pointer to dsd config + * @interp_num: Interpolator number (HPHL/R, LO1/2) + * + * Returns current mixer val for success or -EINVAL for failure + */ +int tavil_dsd_get_current_mixer_value(struct tavil_dsd_config *dsd_conf, + int interp_num) +{ + if (!dsd_conf) + return -EINVAL; + + if (!is_valid_dsd_interpolator(interp_num)) + return -EINVAL; + + return dsd_conf->dsd_interp_mixer[interp_num]; +} +EXPORT_SYMBOL(tavil_dsd_get_current_mixer_value); + +/** + * tavil_dsd_set_out_select - DSD0/1 out select to HPH or LO + * + * @dsd_conf: pointer to dsd config + * @interp_num: Interpolator number (HPHL/R, LO1/2) + * + * Returns 0 for success or -EINVAL for failure + */ +int tavil_dsd_set_out_select(struct tavil_dsd_config *dsd_conf, + int interp_num) +{ + unsigned int reg, val; + struct snd_soc_codec *codec; + + if (!dsd_conf || !dsd_conf->codec) + return -EINVAL; + + codec = dsd_conf->codec; + + if (!is_valid_dsd_interpolator(interp_num)) { + dev_err(codec->dev, "%s: Invalid Interpolator: %d for DSD\n", + __func__, interp_num); + return -EINVAL; + } + + switch (interp_num) { + case INTERP_HPHL: + reg = WCD934X_CDC_DSD0_CFG0; + val = 0x00; + break; + case INTERP_HPHR: + reg = WCD934X_CDC_DSD1_CFG0; + val = 0x00; + break; + case INTERP_LO1: + reg = WCD934X_CDC_DSD0_CFG0; + val = 0x02; + break; + case INTERP_LO2: + reg = WCD934X_CDC_DSD1_CFG0; + val = 0x02; + break; + default: + return -EINVAL; + } + + snd_soc_update_bits(codec, reg, 0x02, val); + + return 0; +} +EXPORT_SYMBOL(tavil_dsd_set_out_select); + +/** + * tavil_dsd_reset - Reset DSD block + * + * @dsd_conf: pointer to dsd config + * + */ +void tavil_dsd_reset(struct tavil_dsd_config *dsd_conf) +{ + if (!dsd_conf || !dsd_conf->codec) + return; + + snd_soc_update_bits(dsd_conf->codec, WCD934X_CDC_DSD0_PATH_CTL, + 0x02, 0x02); + snd_soc_update_bits(dsd_conf->codec, WCD934X_CDC_DSD0_PATH_CTL, + 0x01, 0x00); + snd_soc_update_bits(dsd_conf->codec, WCD934X_CDC_DSD1_PATH_CTL, + 0x02, 0x02); + snd_soc_update_bits(dsd_conf->codec, WCD934X_CDC_DSD1_PATH_CTL, + 0x01, 0x00); +} +EXPORT_SYMBOL(tavil_dsd_reset); + +/** + * tavil_dsd_set_interp_rate - Set interpolator rate for DSD + * + * @dsd_conf: pointer to dsd config + * @rx_port: RX port number + * @sample_rate: Sample rate of the RX interpolator + * @sample_rate_val: Interpolator rate value + */ +void tavil_dsd_set_interp_rate(struct tavil_dsd_config *dsd_conf, u16 rx_port, + u32 sample_rate, u8 sample_rate_val) +{ + u8 dsd_inp_sel; + u8 dsd0_inp, dsd1_inp; + u8 val0, val1; + u8 dsd0_out_sel, dsd1_out_sel; + u16 int_fs_reg, interp_num = 0; + struct snd_soc_codec *codec; + + if (!dsd_conf || !dsd_conf->codec) + return; + + codec = dsd_conf->codec; + + dsd_inp_sel = DSD_INP_SEL_RX0 + rx_port - WCD934X_RX_PORT_START_NUMBER; + + val0 = snd_soc_read(codec, WCD934X_CDC_DSD0_CFG0); + val1 = snd_soc_read(codec, WCD934X_CDC_DSD1_CFG0); + dsd0_inp = (val0 & 0x3C) >> 2; + dsd1_inp = (val1 & 0x3C) >> 2; + dsd0_out_sel = (val0 & 0x02) >> 1; + dsd1_out_sel = (val1 & 0x02) >> 1; + + /* Set HPHL or LO1 interp rate based on out select */ + if (dsd_inp_sel == dsd0_inp) { + interp_num = dsd0_out_sel ? INTERP_LO1 : INTERP_HPHL; + dsd_conf->base_sample_rate[DSD0] = sample_rate; + } + + /* Set HPHR or LO2 interp rate based on out select */ + if (dsd_inp_sel == dsd1_inp) { + interp_num = dsd1_out_sel ? INTERP_LO2 : INTERP_HPHR; + dsd_conf->base_sample_rate[DSD1] = sample_rate; + } + + if (interp_num) { + int_fs_reg = WCD934X_CDC_RX0_RX_PATH_CTL + 20 * interp_num; + if ((snd_soc_read(codec, int_fs_reg) & 0x0f) < 0x09) { + dev_dbg(codec->dev, "%s: Set Interp %d to sample_rate val 0x%x\n", + __func__, interp_num, sample_rate_val); + snd_soc_update_bits(codec, int_fs_reg, 0x0F, + sample_rate_val); + } + } +} +EXPORT_SYMBOL(tavil_dsd_set_interp_rate); + +static int tavil_set_dsd_mode(struct snd_soc_codec *codec, int dsd_num, + u8 *pcm_rate_val) +{ + unsigned int dsd_out_sel_reg; + u8 dsd_mode; + u32 sample_rate; + struct tavil_dsd_config *dsd_conf = tavil_get_dsd_config(codec); + + if (!dsd_conf) + return -EINVAL; + + if ((dsd_num < 0) || (dsd_num > 1)) + return -EINVAL; + + sample_rate = dsd_conf->base_sample_rate[dsd_num]; + dsd_out_sel_reg = WCD934X_CDC_DSD0_CFG0 + dsd_num * 16; + + switch (sample_rate) { + case 176400: + dsd_mode = 0; /* DSD_64 */ + *pcm_rate_val = 0xb; + break; + case 352800: + dsd_mode = 1; /* DSD_128 */ + *pcm_rate_val = 0xc; + break; + default: + dev_err(codec->dev, "%s: Invalid DSD rate: %d\n", + __func__, sample_rate); + return -EINVAL; + } + + snd_soc_update_bits(codec, dsd_out_sel_reg, 0x01, dsd_mode); + + return 0; +} + +static void tavil_dsd_data_pull(struct snd_soc_codec *codec, int dsd_num, + u8 pcm_rate_val, bool enable) +{ + u8 clk_en, mute_en; + u8 dsd_inp_sel; + + if (enable) { + clk_en = 0x20; + mute_en = 0x10; + } else { + clk_en = 0x00; + mute_en = 0x00; + } + + if (dsd_num & 0x01) { + snd_soc_update_bits(codec, WCD934X_CDC_RX7_RX_PATH_MIX_CTL, + 0x20, clk_en); + dsd_inp_sel = (snd_soc_read(codec, WCD934X_CDC_DSD0_CFG0) & + 0x3C) >> 2; + dsd_inp_sel = (enable) ? dsd_inp_sel : 0; + if (dsd_inp_sel < 9) { + snd_soc_update_bits(codec, + WCD934X_CDC_RX_INP_MUX_RX_INT7_CFG1, + 0x0F, dsd_inp_sel); + snd_soc_update_bits(codec, + WCD934X_CDC_RX7_RX_PATH_MIX_CTL, + 0x0F, pcm_rate_val); + snd_soc_update_bits(codec, + WCD934X_CDC_RX7_RX_PATH_MIX_CTL, + 0x10, mute_en); + } + } + if (dsd_num & 0x02) { + snd_soc_update_bits(codec, WCD934X_CDC_RX8_RX_PATH_MIX_CTL, + 0x20, clk_en); + dsd_inp_sel = (snd_soc_read(codec, WCD934X_CDC_DSD1_CFG0) & + 0x3C) >> 2; + dsd_inp_sel = (enable) ? dsd_inp_sel : 0; + if (dsd_inp_sel < 9) { + snd_soc_update_bits(codec, + WCD934X_CDC_RX_INP_MUX_RX_INT8_CFG1, + 0x0F, dsd_inp_sel); + snd_soc_update_bits(codec, + WCD934X_CDC_RX8_RX_PATH_MIX_CTL, + 0x0F, pcm_rate_val); + snd_soc_update_bits(codec, + WCD934X_CDC_RX8_RX_PATH_MIX_CTL, + 0x10, mute_en); + } + } +} + +static void tavil_dsd_update_volume(struct tavil_dsd_config *dsd_conf) +{ + snd_soc_update_bits(dsd_conf->codec, WCD934X_CDC_TOP_TOP_CFG0, + 0x01, 0x01); + snd_soc_update_bits(dsd_conf->codec, WCD934X_CDC_TOP_TOP_CFG0, + 0x01, 0x00); +} + +static int tavil_enable_dsd(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tavil_dsd_config *dsd_conf = tavil_get_dsd_config(codec); + int rc, clk_users; + int interp_idx; + u8 pcm_rate_val; + + if (!dsd_conf) { + dev_err(codec->dev, "%s: null dsd_config pointer\n", __func__); + return -EINVAL; + } + + dev_dbg(codec->dev, "%s: DSD%d, event: %d\n", __func__, + w->shift, event); + + if (w->shift == DSD0) { + /* Read out select */ + if (snd_soc_read(codec, WCD934X_CDC_DSD0_CFG0) & 0x02) + interp_idx = INTERP_LO1; + else + interp_idx = INTERP_HPHL; + } else if (w->shift == DSD1) { + /* Read out select */ + if (snd_soc_read(codec, WCD934X_CDC_DSD1_CFG0) & 0x02) + interp_idx = INTERP_LO2; + else + interp_idx = INTERP_HPHR; + } else { + dev_err(codec->dev, "%s: Unsupported DSD:%d\n", + __func__, w->shift); + return -EINVAL; + } + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + clk_users = tavil_codec_enable_interp_clk(codec, event, + interp_idx); + + rc = tavil_set_dsd_mode(codec, w->shift, &pcm_rate_val); + if (rc) + return rc; + + tavil_dsd_data_pull(codec, (1 << w->shift), pcm_rate_val, + true); + + snd_soc_update_bits(codec, + WCD934X_CDC_CLK_RST_CTRL_DSD_CONTROL, 0x01, + 0x01); + if (w->shift == DSD0) { + snd_soc_update_bits(codec, WCD934X_CDC_DSD0_PATH_CTL, + 0x02, 0x02); + snd_soc_update_bits(codec, WCD934X_CDC_DSD0_PATH_CTL, + 0x02, 0x00); + snd_soc_update_bits(codec, WCD934X_CDC_DSD0_PATH_CTL, + 0x01, 0x01); + /* Apply Gain */ + snd_soc_write(codec, WCD934X_CDC_DSD0_CFG1, + dsd_conf->volume[DSD0]); + if (dsd_conf->version == TAVIL_VERSION_1_1) + tavil_dsd_update_volume(dsd_conf); + + } else if (w->shift == DSD1) { + snd_soc_update_bits(codec, WCD934X_CDC_DSD1_PATH_CTL, + 0x02, 0x02); + snd_soc_update_bits(codec, WCD934X_CDC_DSD1_PATH_CTL, + 0x02, 0x00); + snd_soc_update_bits(codec, WCD934X_CDC_DSD1_PATH_CTL, + 0x01, 0x01); + /* Apply Gain */ + snd_soc_write(codec, WCD934X_CDC_DSD1_CFG1, + dsd_conf->volume[DSD1]); + if (dsd_conf->version == TAVIL_VERSION_1_1) + tavil_dsd_update_volume(dsd_conf); + } + /* 10msec sleep required after DSD clock is set */ + usleep_range(10000, 10100); + + if (clk_users > 1) { + snd_soc_update_bits(codec, WCD934X_ANA_RX_SUPPLIES, + 0x02, 0x02); + if (w->shift == DSD0) + snd_soc_update_bits(codec, + WCD934X_CDC_DSD0_CFG2, + 0x04, 0x00); + if (w->shift == DSD1) + snd_soc_update_bits(codec, + WCD934X_CDC_DSD1_CFG2, + 0x04, 0x00); + + } + break; + case SND_SOC_DAPM_POST_PMD: + if (w->shift == DSD0) { + snd_soc_update_bits(codec, WCD934X_CDC_DSD0_CFG2, + 0x04, 0x04); + snd_soc_update_bits(codec, WCD934X_CDC_DSD0_PATH_CTL, + 0x01, 0x00); + } else if (w->shift == DSD1) { + snd_soc_update_bits(codec, WCD934X_CDC_DSD1_CFG2, + 0x04, 0x04); + snd_soc_update_bits(codec, WCD934X_CDC_DSD1_PATH_CTL, + 0x01, 0x00); + } + + tavil_codec_enable_interp_clk(codec, event, interp_idx); + + if (!(snd_soc_read(codec, WCD934X_CDC_DSD0_PATH_CTL) & 0x01) && + !(snd_soc_read(codec, WCD934X_CDC_DSD1_PATH_CTL) & 0x01)) { + snd_soc_update_bits(codec, + WCD934X_CDC_CLK_RST_CTRL_DSD_CONTROL, + 0x01, 0x00); + tavil_dsd_data_pull(codec, 0x03, 0x04, false); + tavil_dsd_reset(dsd_conf); + } + break; + } + + return 0; +} + +static int tavil_dsd_vol_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = DSD_VOLUME_MIN_M110dB; + uinfo->value.integer.max = DSD_VOLUME_MAX_0dB; + + return 0; +} + +static int tavil_dsd_vol_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tavil_dsd_config *dsd_conf = tavil_get_dsd_config(codec); + int nv[DSD_MAX], cv[DSD_MAX]; + int step_size, nv1; + int i, dsd_idx; + + if (!dsd_conf) + return 0; + + mutex_lock(&dsd_conf->vol_mutex); + + for (dsd_idx = DSD0; dsd_idx < DSD_MAX; dsd_idx++) { + cv[dsd_idx] = dsd_conf->volume[dsd_idx]; + nv[dsd_idx] = ucontrol->value.integer.value[dsd_idx]; + } + + if ((!DSD_VOLUME_RANGE_CHECK(nv[DSD0])) || + (!DSD_VOLUME_RANGE_CHECK(nv[DSD1]))) + goto done; + + for (dsd_idx = DSD0; dsd_idx < DSD_MAX; dsd_idx++) { + if (cv[dsd_idx] == nv[dsd_idx]) + continue; + + dev_dbg(codec->dev, "%s: DSD%d cur.vol: %d, new vol: %d\n", + __func__, dsd_idx, cv[dsd_idx], nv[dsd_idx]); + + step_size = (nv[dsd_idx] - cv[dsd_idx]) / + DSD_VOLUME_STEPS; + + nv1 = cv[dsd_idx]; + + for (i = 0; i < DSD_VOLUME_STEPS; i++) { + nv1 += step_size; + snd_soc_write(codec, + WCD934X_CDC_DSD0_CFG1 + 16 * dsd_idx, + nv1); + if (dsd_conf->version == TAVIL_VERSION_1_1) + tavil_dsd_update_volume(dsd_conf); + + /* sleep required after each volume step */ + usleep_range(DSD_VOLUME_STEP_DELAY_US, + (DSD_VOLUME_STEP_DELAY_US + + DSD_VOLUME_USLEEP_MARGIN_US)); + } + if (nv1 != nv[dsd_idx]) { + snd_soc_write(codec, + WCD934X_CDC_DSD0_CFG1 + 16 * dsd_idx, + nv[dsd_idx]); + + if (dsd_conf->version == TAVIL_VERSION_1_1) + tavil_dsd_update_volume(dsd_conf); + } + + dsd_conf->volume[dsd_idx] = nv[dsd_idx]; + } + +done: + mutex_unlock(&dsd_conf->vol_mutex); + + return 0; +} + +static int tavil_dsd_vol_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tavil_dsd_config *dsd_conf = tavil_get_dsd_config(codec); + + if (dsd_conf) { + ucontrol->value.integer.value[0] = dsd_conf->volume[DSD0]; + ucontrol->value.integer.value[1] = dsd_conf->volume[DSD1]; + } + + return 0; +} + +static const struct snd_kcontrol_new tavil_dsd_vol_controls[] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ), + .name = "DSD Volume", + .info = tavil_dsd_vol_info, + .get = tavil_dsd_vol_get, + .put = tavil_dsd_vol_put, + .tlv = { .p = tavil_dsd_db_scale }, + }, +}; + +static const struct snd_soc_dapm_widget tavil_dsd_widgets[] = { + SND_SOC_DAPM_MUX("DSD_L IF MUX", SND_SOC_NOPM, 0, 0, &dsd_l_if_mux), + SND_SOC_DAPM_MUX_E("DSD_FILTER_0", SND_SOC_NOPM, 0, 0, &dsd_filt0_mux, + tavil_enable_dsd, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX("DSD_R IF MUX", SND_SOC_NOPM, 0, 0, &dsd_r_if_mux), + SND_SOC_DAPM_MUX_E("DSD_FILTER_1", SND_SOC_NOPM, 1, 0, &dsd_filt1_mux, + tavil_enable_dsd, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +}; + +/** + * tavil_dsd_post_ssr_init - DSD intialization after subsystem restart + * + * @codec: pointer to snd_soc_codec + * + * Returns 0 on success or error on failure + */ +int tavil_dsd_post_ssr_init(struct tavil_dsd_config *dsd_conf) +{ + struct snd_soc_codec *codec; + + if (!dsd_conf || !dsd_conf->codec) + return -EINVAL; + + codec = dsd_conf->codec; + /* Disable DSD Interrupts */ + snd_soc_update_bits(codec, WCD934X_INTR_CODEC_MISC_MASK, 0x08, 0x08); + + /* DSD registers init */ + if (dsd_conf->version == TAVIL_VERSION_1_0) { + snd_soc_update_bits(codec, WCD934X_CDC_DSD0_CFG2, 0x02, 0x00); + snd_soc_update_bits(codec, WCD934X_CDC_DSD1_CFG2, 0x02, 0x00); + } + /* DSD0: Mute EN */ + snd_soc_update_bits(codec, WCD934X_CDC_DSD0_CFG2, 0x04, 0x04); + /* DSD1: Mute EN */ + snd_soc_update_bits(codec, WCD934X_CDC_DSD1_CFG2, 0x04, 0x04); + snd_soc_update_bits(codec, WCD934X_CDC_DEBUG_DSD0_DEBUG_CFG3, 0x10, + 0x10); + snd_soc_update_bits(codec, WCD934X_CDC_DEBUG_DSD1_DEBUG_CFG3, 0x10, + 0x10); + snd_soc_update_bits(codec, WCD934X_CDC_DEBUG_DSD0_DEBUG_CFG0, 0x0E, + 0x0A); + snd_soc_update_bits(codec, WCD934X_CDC_DEBUG_DSD1_DEBUG_CFG0, 0x0E, + 0x0A); + snd_soc_update_bits(codec, WCD934X_CDC_DEBUG_DSD0_DEBUG_CFG1, 0x07, + 0x04); + snd_soc_update_bits(codec, WCD934X_CDC_DEBUG_DSD1_DEBUG_CFG1, 0x07, + 0x04); + + /* Enable DSD Interrupts */ + snd_soc_update_bits(codec, WCD934X_INTR_CODEC_MISC_MASK, 0x08, 0x00); + + return 0; +} +EXPORT_SYMBOL(tavil_dsd_post_ssr_init); + +/** + * tavil_dsd_init - DSD intialization + * + * @codec: pointer to snd_soc_codec + * + * Returns pointer to tavil_dsd_config for success or NULL for failure + */ +struct tavil_dsd_config *tavil_dsd_init(struct snd_soc_codec *codec) +{ + struct snd_soc_dapm_context *dapm; + struct tavil_dsd_config *dsd_conf; + u8 val; + + if (!codec) + return NULL; + + dapm = snd_soc_codec_get_dapm(codec); + + /* Read efuse register to check if DSD is supported */ + val = snd_soc_read(codec, WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT14); + if (val & 0x80) { + dev_info(codec->dev, "%s: DSD unsupported for this codec version\n", + __func__); + return NULL; + } + + dsd_conf = devm_kzalloc(codec->dev, sizeof(struct tavil_dsd_config), + GFP_KERNEL); + if (!dsd_conf) + return NULL; + + dsd_conf->codec = codec; + + /* Read version */ + dsd_conf->version = snd_soc_read(codec, + WCD934X_CHIP_TIER_CTRL_CHIP_ID_BYTE0); + /* DSD registers init */ + if (dsd_conf->version == TAVIL_VERSION_1_0) { + snd_soc_update_bits(codec, WCD934X_CDC_DSD0_CFG2, 0x02, 0x00); + snd_soc_update_bits(codec, WCD934X_CDC_DSD1_CFG2, 0x02, 0x00); + } + /* DSD0: Mute EN */ + snd_soc_update_bits(codec, WCD934X_CDC_DSD0_CFG2, 0x04, 0x04); + /* DSD1: Mute EN */ + snd_soc_update_bits(codec, WCD934X_CDC_DSD1_CFG2, 0x04, 0x04); + snd_soc_update_bits(codec, WCD934X_CDC_DEBUG_DSD0_DEBUG_CFG3, 0x10, + 0x10); + snd_soc_update_bits(codec, WCD934X_CDC_DEBUG_DSD1_DEBUG_CFG3, 0x10, + 0x10); + snd_soc_update_bits(codec, WCD934X_CDC_DEBUG_DSD0_DEBUG_CFG0, 0x0E, + 0x0A); + snd_soc_update_bits(codec, WCD934X_CDC_DEBUG_DSD1_DEBUG_CFG0, 0x0E, + 0x0A); + snd_soc_update_bits(codec, WCD934X_CDC_DEBUG_DSD0_DEBUG_CFG1, 0x07, + 0x04); + snd_soc_update_bits(codec, WCD934X_CDC_DEBUG_DSD1_DEBUG_CFG1, 0x07, + 0x04); + + snd_soc_dapm_new_controls(dapm, tavil_dsd_widgets, + ARRAY_SIZE(tavil_dsd_widgets)); + + snd_soc_dapm_add_routes(dapm, tavil_dsd_audio_map, + ARRAY_SIZE(tavil_dsd_audio_map)); + + mutex_init(&dsd_conf->vol_mutex); + dsd_conf->volume[DSD0] = DSD_VOLUME_MAX_0dB; + dsd_conf->volume[DSD1] = DSD_VOLUME_MAX_0dB; + + snd_soc_add_codec_controls(codec, tavil_dsd_vol_controls, + ARRAY_SIZE(tavil_dsd_vol_controls)); + + /* Enable DSD Interrupts */ + snd_soc_update_bits(codec, WCD934X_INTR_CODEC_MISC_MASK, 0x08, 0x00); + + return dsd_conf; +} +EXPORT_SYMBOL(tavil_dsd_init); + +/** + * tavil_dsd_deinit - DSD de-intialization + * + * @dsd_conf: pointer to tavil_dsd_config + */ +void tavil_dsd_deinit(struct tavil_dsd_config *dsd_conf) +{ + struct snd_soc_codec *codec; + + if (!dsd_conf) + return; + + codec = dsd_conf->codec; + + mutex_destroy(&dsd_conf->vol_mutex); + + /* Disable DSD Interrupts */ + snd_soc_update_bits(codec, WCD934X_INTR_CODEC_MISC_MASK, 0x08, 0x08); + + devm_kfree(codec->dev, dsd_conf); +} +EXPORT_SYMBOL(tavil_dsd_deinit); diff --git a/audio-kernel/asoc/codecs/wcd934x/wcd934x-dsd.h b/audio-kernel/asoc/codecs/wcd934x/wcd934x-dsd.h new file mode 100644 index 000000000..834b96cd1 --- /dev/null +++ b/audio-kernel/asoc/codecs/wcd934x/wcd934x-dsd.h @@ -0,0 +1,97 @@ +/* Copyright (c) 2016-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 + * 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 __WCD934X_DSD_H__ +#define __WCD934X_DSD_H__ + +#include +#include "wcd934x.h" + +enum { + DSD0, + DSD1, + DSD_MAX, +}; + +enum { + DSD_INP_SEL_ZERO = 0, + DSD_INP_SEL_RX0, + DSD_INP_SEL_RX1, + DSD_INP_SEL_RX2, + DSD_INP_SEL_RX3, + DSD_INP_SEL_RX4, + DSD_INP_SEL_RX5, + DSD_INP_SEL_RX6, + DSD_INP_SEL_RX7, +}; + +struct tavil_dsd_config { + struct snd_soc_codec *codec; + unsigned int dsd_interp_mixer[INTERP_MAX]; + u32 base_sample_rate[DSD_MAX]; + int volume[DSD_MAX]; + struct mutex vol_mutex; + int version; +}; + +#if IS_ENABLED(CONFIG_SND_SOC_WCD934X_DSD) +int tavil_dsd_set_mixer_value(struct tavil_dsd_config *dsd_conf, + int interp_num, int sw_value); +int tavil_dsd_get_current_mixer_value(struct tavil_dsd_config *dsd_conf, + int interp_num); +int tavil_dsd_set_out_select(struct tavil_dsd_config *dsd_conf, + int interp_num); +void tavil_dsd_reset(struct tavil_dsd_config *dsd_conf); +void tavil_dsd_set_interp_rate(struct tavil_dsd_config *dsd_conf, u16 rx_port, + u32 sample_rate, u8 sample_rate_val); +struct tavil_dsd_config *tavil_dsd_init(struct snd_soc_codec *codec); +void tavil_dsd_deinit(struct tavil_dsd_config *dsd_config); +int tavil_dsd_post_ssr_init(struct tavil_dsd_config *dsd_config); +#else +int tavil_dsd_set_mixer_value(struct tavil_dsd_config *dsd_conf, + int interp_num, int sw_value) +{ + return 0; +} + +int tavil_dsd_get_current_mixer_value(struct tavil_dsd_config *dsd_conf, + int interp_num) +{ + return 0; +} + +int tavil_dsd_set_out_select(struct tavil_dsd_config *dsd_conf, + int interp_num) +{ + return 0; +} + +void tavil_dsd_reset(struct tavil_dsd_config *dsd_conf) +{ } + +void tavil_dsd_set_interp_rate(struct tavil_dsd_config *dsd_conf, u16 rx_port, + u32 sample_rate, u8 sample_rate_val) +{ } + +struct tavil_dsd_config *tavil_dsd_init(struct snd_soc_codec *codec) +{ + return NULL; +} + +void tavil_dsd_deinit(struct tavil_dsd_config *dsd_config) +{ } +int tavil_dsd_post_ssr_init(struct tavil_dsd_config *dsd_config) +{ + return 0; +} +#endif +#endif diff --git a/audio-kernel/asoc/codecs/wcd934x/wcd934x-dsp-cntl.c b/audio-kernel/asoc/codecs/wcd934x/wcd934x-dsp-cntl.c new file mode 100644 index 000000000..f18cc81a9 --- /dev/null +++ b/audio-kernel/asoc/codecs/wcd934x/wcd934x-dsp-cntl.c @@ -0,0 +1,1490 @@ +/* + * Copyright (c) 2016-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 +#include +#include +#include +#include +#include +#include +#include "wcd934x.h" +#include "wcd934x-dsp-cntl.h" +#include "../wcd9xxx-irq.h" +#include "../core.h" + +#define WCD_CNTL_DIR_NAME_LEN_MAX 32 +#define WCD_CPE_FLL_MAX_RETRIES 5 +#define WCD_MEM_ENABLE_MAX_RETRIES 20 +#define WCD_DSP_BOOT_TIMEOUT_MS 3000 +#define WCD_SYSFS_ENTRY_MAX_LEN 8 +#define WCD_PROCFS_ENTRY_MAX_LEN 16 +#define WCD_934X_RAMDUMP_START_ADDR 0x20100000 +#define WCD_934X_RAMDUMP_SIZE ((1024 * 1024) - 128) +#define WCD_MISCDEV_CMD_MAX_LEN 11 + +#define WCD_CNTL_MUTEX_LOCK(codec, lock) \ +{ \ + dev_dbg(codec->dev, "%s: mutex_lock(%s)\n", \ + __func__, __stringify_1(lock)); \ + mutex_lock(&lock); \ +} + +#define WCD_CNTL_MUTEX_UNLOCK(codec, lock) \ +{ \ + dev_dbg(codec->dev, "%s: mutex_unlock(%s)\n", \ + __func__, __stringify_1(lock)); \ + mutex_unlock(&lock); \ +} + +enum wcd_mem_type { + WCD_MEM_TYPE_ALWAYS_ON, + WCD_MEM_TYPE_SWITCHABLE, +}; + +struct wcd_cntl_attribute { + struct attribute attr; + ssize_t (*show)(struct wcd_dsp_cntl *cntl, char *buf); + ssize_t (*store)(struct wcd_dsp_cntl *cntl, const char *buf, + ssize_t count); +}; + +#define WCD_CNTL_ATTR(_name, _mode, _show, _store) \ +static struct wcd_cntl_attribute cntl_attr_##_name = { \ + .attr = {.name = __stringify(_name), .mode = _mode}, \ + .show = _show, \ + .store = _store, \ +} + +#define to_wcd_cntl_attr(a) \ + container_of((a), struct wcd_cntl_attribute, attr) + +#define to_wcd_cntl(kobj) \ + container_of((kobj), struct wcd_dsp_cntl, wcd_kobj) + +static u8 mem_enable_values[] = { + 0xFE, 0xFC, 0xF8, 0xF0, + 0xE0, 0xC0, 0x80, 0x00, +}; + +#ifdef CONFIG_DEBUG_FS +#define WCD_CNTL_SET_ERR_IRQ_FLAG(cntl)\ + atomic_cmpxchg(&cntl->err_irq_flag, 0, 1) +#define WCD_CNTL_CLR_ERR_IRQ_FLAG(cntl)\ + atomic_set(&cntl->err_irq_flag, 0) + +static u16 wdsp_reg_for_debug_dump[] = { + WCD934X_CPE_SS_CPE_CTL, + WCD934X_CPE_SS_PWR_SYS_PSTATE_CTL_0, + WCD934X_CPE_SS_PWR_SYS_PSTATE_CTL_1, + WCD934X_CPE_SS_PWR_CPEFLL_CTL, + WCD934X_CPE_SS_PWR_CPE_SYSMEM_DEEPSLP_0, + WCD934X_CPE_SS_PWR_CPE_SYSMEM_DEEPSLP_1, + WCD934X_CPE_SS_PWR_CPE_SYSMEM_DEEPSLP_OVERRIDE, + WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_0, + WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_1, + WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_2, + WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_3, + WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_4, + WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_5, + WCD934X_CPE_SS_PWR_CPE_DRAM1_SHUTDOWN, + WCD934X_CPE_SS_SOC_SW_COLLAPSE_CTL, + WCD934X_CPE_SS_MAD_CTL, + WCD934X_CPE_SS_CPAR_CTL, + WCD934X_CPE_SS_CPAR_CFG, + WCD934X_CPE_SS_WDOG_CFG, + WCD934X_CPE_SS_STATUS, + WCD934X_CPE_SS_SS_ERROR_INT_MASK_0A, + WCD934X_CPE_SS_SS_ERROR_INT_MASK_0B, + WCD934X_CPE_SS_SS_ERROR_INT_MASK_1A, + WCD934X_CPE_SS_SS_ERROR_INT_MASK_1B, + WCD934X_CPE_SS_SS_ERROR_INT_STATUS_0A, + WCD934X_CPE_SS_SS_ERROR_INT_STATUS_0B, + WCD934X_CPE_SS_SS_ERROR_INT_STATUS_1A, + WCD934X_CPE_SS_SS_ERROR_INT_STATUS_1B, +}; + +static void wcd_cntl_collect_debug_dumps(struct wcd_dsp_cntl *cntl, + bool internal) +{ + struct snd_soc_codec *codec = cntl->codec; + struct wdsp_err_signal_arg arg; + enum wdsp_signal signal; + int i; + u8 val; + + /* If WDSP SSR happens, skip collecting debug dumps */ + if (WCD_CNTL_SET_ERR_IRQ_FLAG(cntl) != 0) + return; + + /* Mask all error interrupts */ + snd_soc_write(codec, WCD934X_CPE_SS_SS_ERROR_INT_MASK_0A, + 0xFF); + snd_soc_write(codec, WCD934X_CPE_SS_SS_ERROR_INT_MASK_0B, + 0xFF); + + /* Collect important WDSP registers dump for debug use */ + pr_err("%s: Dump the WDSP registers for debug use\n", __func__); + for (i = 0; i < sizeof(wdsp_reg_for_debug_dump)/sizeof(u16); i++) { + val = snd_soc_read(codec, wdsp_reg_for_debug_dump[i]); + pr_err("%s: reg = 0x%x, val = 0x%x\n", __func__, + wdsp_reg_for_debug_dump[i], val); + } + + /* Trigger NMI in WDSP to sync and update the memory */ + snd_soc_write(codec, WCD934X_CPE_SS_BACKUP_INT, 0x02); + + /* Collect WDSP ramdump for debug use */ + if (cntl->m_dev && cntl->m_ops && cntl->m_ops->signal_handler) { + arg.mem_dumps_enabled = cntl->ramdump_enable; + arg.remote_start_addr = WCD_934X_RAMDUMP_START_ADDR; + arg.dump_size = WCD_934X_RAMDUMP_SIZE; + signal = internal ? WDSP_DEBUG_DUMP_INTERNAL : WDSP_DEBUG_DUMP; + cntl->m_ops->signal_handler(cntl->m_dev, signal, &arg); + } + + /* Unmask the fatal irqs */ + snd_soc_write(codec, WCD934X_CPE_SS_SS_ERROR_INT_MASK_0A, + ~(cntl->irqs.fatal_irqs & 0xFF)); + snd_soc_write(codec, WCD934X_CPE_SS_SS_ERROR_INT_MASK_0B, + ~((cntl->irqs.fatal_irqs >> 8) & 0xFF)); + + WCD_CNTL_CLR_ERR_IRQ_FLAG(cntl); +} +#else +#define WCD_CNTL_SET_ERR_IRQ_FLAG(cntl) 0 +#define WCD_CNTL_CLR_ERR_IRQ_FLAG(cntl) do {} while (0) +static void wcd_cntl_collect_debug_dumps(struct wcd_dsp_cntl *cntl, + bool internal) +{ +} +#endif + +static ssize_t wdsp_boot_show(struct wcd_dsp_cntl *cntl, char *buf) +{ + return snprintf(buf, WCD_SYSFS_ENTRY_MAX_LEN, + "%u", cntl->boot_reqs); +} + +static ssize_t wdsp_boot_store(struct wcd_dsp_cntl *cntl, + const char *buf, ssize_t count) +{ + u32 val; + bool vote; + int ret; + + ret = kstrtou32(buf, 10, &val); + if (ret) { + dev_err(cntl->codec->dev, + "%s: Invalid entry, ret = %d\n", __func__, ret); + return -EINVAL; + } + + if (val > 0) { + cntl->boot_reqs++; + vote = true; + } else { + cntl->boot_reqs--; + vote = false; + } + + if (cntl->m_dev && cntl->m_ops && + cntl->m_ops->vote_for_dsp) + ret = cntl->m_ops->vote_for_dsp(cntl->m_dev, vote); + else + ret = -EINVAL; + + if (ret < 0) + dev_err(cntl->codec->dev, + "%s: failed to %s dsp\n", __func__, + vote ? "enable" : "disable"); + return count; +} + +WCD_CNTL_ATTR(boot, 0660, wdsp_boot_show, wdsp_boot_store); + +static ssize_t wcd_cntl_sysfs_show(struct kobject *kobj, + struct attribute *attr, char *buf) +{ + struct wcd_cntl_attribute *wcd_attr = to_wcd_cntl_attr(attr); + struct wcd_dsp_cntl *cntl = to_wcd_cntl(kobj); + ssize_t ret = -EINVAL; + + if (cntl && wcd_attr->show) + ret = wcd_attr->show(cntl, buf); + + return ret; +} + +static ssize_t wcd_cntl_sysfs_store(struct kobject *kobj, + struct attribute *attr, const char *buf, + size_t count) +{ + struct wcd_cntl_attribute *wcd_attr = to_wcd_cntl_attr(attr); + struct wcd_dsp_cntl *cntl = to_wcd_cntl(kobj); + ssize_t ret = -EINVAL; + + if (cntl && wcd_attr->store) + ret = wcd_attr->store(cntl, buf, count); + + return ret; +} + +static const struct sysfs_ops wcd_cntl_sysfs_ops = { + .show = wcd_cntl_sysfs_show, + .store = wcd_cntl_sysfs_store, +}; + +static struct kobj_type wcd_cntl_ktype = { + .sysfs_ops = &wcd_cntl_sysfs_ops, +}; + +static void wcd_cntl_change_online_state(struct wcd_dsp_cntl *cntl, + u8 online) +{ + struct wdsp_ssr_entry *ssr_entry = &cntl->ssr_entry; + unsigned long ret; + + WCD_CNTL_MUTEX_LOCK(cntl->codec, cntl->ssr_mutex); + ssr_entry->offline = !online; + /* Make sure the write is complete */ + wmb(); + ret = xchg(&ssr_entry->offline_change, 1); + wake_up_interruptible(&ssr_entry->offline_poll_wait); + dev_dbg(cntl->codec->dev, + "%s: requested %u, offline %u offline_change %u, ret = %ldn", + __func__, online, ssr_entry->offline, + ssr_entry->offline_change, ret); + WCD_CNTL_MUTEX_UNLOCK(cntl->codec, cntl->ssr_mutex); +} + +static ssize_t wdsp_ssr_entry_read(struct snd_info_entry *entry, + void *file_priv_data, struct file *file, + char __user *buf, size_t count, loff_t pos) +{ + int len = 0; + char buffer[WCD_PROCFS_ENTRY_MAX_LEN]; + struct wcd_dsp_cntl *cntl; + struct wdsp_ssr_entry *ssr_entry; + ssize_t ret; + u8 offline; + + cntl = (struct wcd_dsp_cntl *) entry->private_data; + if (!cntl) { + pr_err("%s: Invalid private data for SSR procfs entry\n", + __func__); + return -EINVAL; + } + + ssr_entry = &cntl->ssr_entry; + + WCD_CNTL_MUTEX_LOCK(cntl->codec, cntl->ssr_mutex); + offline = ssr_entry->offline; + /* Make sure the read is complete */ + rmb(); + dev_dbg(cntl->codec->dev, "%s: offline = %s\n", __func__, + offline ? "true" : "false"); + len = snprintf(buffer, sizeof(buffer), "%s\n", + offline ? "OFFLINE" : "ONLINE"); + ret = simple_read_from_buffer(buf, count, &pos, buffer, len); + WCD_CNTL_MUTEX_UNLOCK(cntl->codec, cntl->ssr_mutex); + + return ret; +} + +static unsigned int wdsp_ssr_entry_poll(struct snd_info_entry *entry, + void *private_data, struct file *file, + poll_table *wait) +{ + struct wcd_dsp_cntl *cntl; + struct wdsp_ssr_entry *ssr_entry; + unsigned int ret = 0; + + if (!entry || !entry->private_data) { + pr_err("%s: %s is NULL\n", __func__, + (!entry) ? "entry" : "private_data"); + return -EINVAL; + } + + cntl = (struct wcd_dsp_cntl *) entry->private_data; + ssr_entry = &cntl->ssr_entry; + + dev_dbg(cntl->codec->dev, "%s: Poll wait, offline = %u\n", + __func__, ssr_entry->offline); + poll_wait(file, &ssr_entry->offline_poll_wait, wait); + dev_dbg(cntl->codec->dev, "%s: Woken up Poll wait, offline = %u\n", + __func__, ssr_entry->offline); + + WCD_CNTL_MUTEX_LOCK(cntl->codec, cntl->ssr_mutex); + if (xchg(&ssr_entry->offline_change, 0)) + ret = POLLIN | POLLPRI | POLLRDNORM; + dev_dbg(cntl->codec->dev, "%s: ret (%d) from poll_wait\n", + __func__, ret); + WCD_CNTL_MUTEX_UNLOCK(cntl->codec, cntl->ssr_mutex); + + return ret; +} + +static struct snd_info_entry_ops wdsp_ssr_entry_ops = { + .read = wdsp_ssr_entry_read, + .poll = wdsp_ssr_entry_poll, +}; + +static int wcd_cntl_cpe_fll_calibrate(struct wcd_dsp_cntl *cntl) +{ + struct snd_soc_codec *codec = cntl->codec; + int ret = 0, retry = 0; + u8 cal_lsb, cal_msb; + u8 lock_det; + + /* Make sure clocks are gated */ + snd_soc_update_bits(codec, WCD934X_CPE_SS_CPE_CTL, + 0x05, 0x00); + + /* Enable CPE FLL reference clock */ + snd_soc_update_bits(codec, WCD934X_CLK_SYS_MCLK2_PRG1, + 0x80, 0x80); + + snd_soc_update_bits(codec, WCD934X_CPE_FLL_USER_CTL_5, + 0xF3, 0x13); + snd_soc_write(codec, WCD934X_CPE_FLL_L_VAL_CTL_0, 0x50); + + /* Disable CPAR reset and Enable CPAR clk */ + snd_soc_update_bits(codec, WCD934X_CPE_SS_CPAR_CTL, + 0x02, 0x02); + + /* Write calibration l-value based on cdc clk rate */ + if (cntl->clk_rate == 9600000) { + cal_lsb = 0x6d; + cal_msb = 0x00; + } else { + cal_lsb = 0x56; + cal_msb = 0x00; + } + snd_soc_write(codec, WCD934X_CPE_FLL_USER_CTL_6, cal_lsb); + snd_soc_write(codec, WCD934X_CPE_FLL_USER_CTL_7, cal_msb); + + /* FLL mode to follow power up sequence */ + snd_soc_update_bits(codec, WCD934X_CPE_FLL_FLL_MODE, + 0x60, 0x00); + + /* HW controlled CPE FLL */ + snd_soc_update_bits(codec, WCD934X_CPE_FLL_FLL_MODE, + 0x80, 0x80); + + /* Force on CPE FLL */ + snd_soc_update_bits(codec, WCD934X_CPE_SS_CPAR_CFG, + 0x04, 0x04); + + do { + /* Time for FLL calibration to complete */ + usleep_range(1000, 1100); + lock_det = snd_soc_read(codec, WCD934X_CPE_FLL_STATUS_3); + retry++; + } while (!(lock_det & 0x01) && + retry <= WCD_CPE_FLL_MAX_RETRIES); + + if (!(lock_det & 0x01)) { + dev_err(codec->dev, "%s: lock detect not set, 0x%02x\n", + __func__, lock_det); + ret = -EIO; + goto err_lock_det; + } + + snd_soc_update_bits(codec, WCD934X_CPE_FLL_FLL_MODE, + 0x60, 0x20); + snd_soc_update_bits(codec, WCD934X_CPE_SS_CPAR_CFG, + 0x04, 0x00); + return ret; + +err_lock_det: + /* Undo the register settings */ + snd_soc_update_bits(codec, WCD934X_CPE_SS_CPAR_CFG, + 0x04, 0x00); + snd_soc_update_bits(codec, WCD934X_CPE_FLL_FLL_MODE, + 0x80, 0x00); + snd_soc_update_bits(codec, WCD934X_CPE_SS_CPAR_CTL, + 0x02, 0x00); + return ret; +} + +static void wcd_cntl_config_cpar(struct wcd_dsp_cntl *cntl) +{ + struct snd_soc_codec *codec = cntl->codec; + u8 nom_lo, nom_hi, svs2_lo, svs2_hi; + + /* Configure CPAR */ + nom_hi = svs2_hi = 0; + if (cntl->clk_rate == 9600000) { + nom_lo = 0x90; + svs2_lo = 0x50; + } else { + nom_lo = 0x70; + svs2_lo = 0x3e; + } + + snd_soc_write(codec, WCD934X_TEST_DEBUG_LVAL_NOM_LOW, nom_lo); + snd_soc_write(codec, WCD934X_TEST_DEBUG_LVAL_NOM_HIGH, nom_hi); + snd_soc_write(codec, WCD934X_TEST_DEBUG_LVAL_SVS_SVS2_LOW, svs2_lo); + snd_soc_write(codec, WCD934X_TEST_DEBUG_LVAL_SVS_SVS2_HIGH, svs2_hi); + + snd_soc_update_bits(codec, WCD934X_CPE_SS_PWR_CPEFLL_CTL, + 0x03, 0x03); +} + +static int wcd_cntl_cpe_fll_ctrl(struct wcd_dsp_cntl *cntl, + bool enable) +{ + struct snd_soc_codec *codec = cntl->codec; + int ret = 0; + + if (enable) { + ret = wcd_cntl_cpe_fll_calibrate(cntl); + if (ret < 0) { + dev_err(codec->dev, + "%s: cpe_fll_cal failed, err = %d\n", + __func__, ret); + goto done; + } + + wcd_cntl_config_cpar(cntl); + + /* Enable AHB CLK and CPE CLK*/ + snd_soc_update_bits(codec, WCD934X_CPE_SS_CPE_CTL, + 0x05, 0x05); + } else { + /* Disable AHB CLK and CPE CLK */ + snd_soc_update_bits(codec, WCD934X_CPE_SS_CPE_CTL, + 0x05, 0x00); + /* Reset the CPAR mode for CPE FLL */ + snd_soc_write(codec, WCD934X_CPE_FLL_FLL_MODE, 0x20); + snd_soc_update_bits(codec, WCD934X_CPE_SS_CPAR_CFG, + 0x04, 0x00); + snd_soc_update_bits(codec, WCD934X_CPE_SS_CPAR_CTL, + 0x02, 0x00); + } +done: + return ret; +} + +static int wcd_cntl_clocks_enable(struct wcd_dsp_cntl *cntl) +{ + struct snd_soc_codec *codec = cntl->codec; + int ret; + + WCD_CNTL_MUTEX_LOCK(codec, cntl->clk_mutex); + /* Enable codec clock */ + if (cntl->cdc_cb && cntl->cdc_cb->cdc_clk_en) + ret = cntl->cdc_cb->cdc_clk_en(codec, true); + else + ret = -EINVAL; + + if (ret < 0) { + dev_err(codec->dev, + "%s: Failed to enable cdc clk, err = %d\n", + __func__, ret); + goto done; + } + /* Pull CPAR out of reset */ + snd_soc_update_bits(codec, WCD934X_CPE_SS_CPAR_CTL, 0x04, 0x00); + + /* Configure and Enable CPE FLL clock */ + ret = wcd_cntl_cpe_fll_ctrl(cntl, true); + if (ret < 0) { + dev_err(codec->dev, + "%s: Failed to enable cpe clk, err = %d\n", + __func__, ret); + goto err_cpe_clk; + } + cntl->is_clk_enabled = true; + + /* Ungate the CPR clock */ + snd_soc_update_bits(codec, WCD934X_CODEC_RPM_CLK_GATE, 0x10, 0x00); +done: + WCD_CNTL_MUTEX_UNLOCK(codec, cntl->clk_mutex); + return ret; + +err_cpe_clk: + if (cntl->cdc_cb && cntl->cdc_cb->cdc_clk_en) + cntl->cdc_cb->cdc_clk_en(codec, false); + + snd_soc_update_bits(codec, WCD934X_CPE_SS_CPAR_CTL, 0x04, 0x04); + WCD_CNTL_MUTEX_UNLOCK(codec, cntl->clk_mutex); + return ret; +} + +static int wcd_cntl_clocks_disable(struct wcd_dsp_cntl *cntl) +{ + struct snd_soc_codec *codec = cntl->codec; + int ret = 0; + + WCD_CNTL_MUTEX_LOCK(codec, cntl->clk_mutex); + if (!cntl->is_clk_enabled) { + dev_info(codec->dev, "%s: clocks already disabled\n", + __func__); + goto done; + } + + /* Gate the CPR clock */ + snd_soc_update_bits(codec, WCD934X_CODEC_RPM_CLK_GATE, 0x10, 0x10); + + /* Disable CPE FLL clock */ + ret = wcd_cntl_cpe_fll_ctrl(cntl, false); + if (ret < 0) + dev_err(codec->dev, + "%s: Failed to disable cpe clk, err = %d\n", + __func__, ret); + + /* + * Even if CPE FLL disable failed, go ahead and disable + * the codec clock + */ + if (cntl->cdc_cb && cntl->cdc_cb->cdc_clk_en) + ret = cntl->cdc_cb->cdc_clk_en(codec, false); + else + ret = -EINVAL; + + cntl->is_clk_enabled = false; + + /* Put CPAR in reset */ + snd_soc_update_bits(codec, WCD934X_CPE_SS_CPAR_CTL, 0x04, 0x04); +done: + WCD_CNTL_MUTEX_UNLOCK(codec, cntl->clk_mutex); + return ret; +} + +static void wcd_cntl_cpar_ctrl(struct wcd_dsp_cntl *cntl, + bool enable) +{ + struct snd_soc_codec *codec = cntl->codec; + + if (enable) + snd_soc_update_bits(codec, WCD934X_CPE_SS_CPAR_CTL, 0x03, 0x03); + else + snd_soc_update_bits(codec, WCD934X_CPE_SS_CPAR_CTL, 0x03, 0x00); +} + +static int wcd_cntl_enable_memory(struct wcd_dsp_cntl *cntl, + enum wcd_mem_type mem_type) +{ + struct snd_soc_codec *codec = cntl->codec; + struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent); + int loop_cnt = 0; + u8 status; + int ret = 0; + + + switch (mem_type) { + + case WCD_MEM_TYPE_ALWAYS_ON: + + /* 512KB of always on region */ + wcd9xxx_slim_write_repeat(wcd9xxx, + WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_0, + ARRAY_SIZE(mem_enable_values), + mem_enable_values); + wcd9xxx_slim_write_repeat(wcd9xxx, + WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_1, + ARRAY_SIZE(mem_enable_values), + mem_enable_values); + break; + + case WCD_MEM_TYPE_SWITCHABLE: + + snd_soc_update_bits(codec, WCD934X_CPE_SS_SOC_SW_COLLAPSE_CTL, + 0x04, 0x00); + snd_soc_update_bits(codec, WCD934X_TEST_DEBUG_MEM_CTRL, + 0x80, 0x80); + snd_soc_update_bits(codec, WCD934X_CPE_SS_SOC_SW_COLLAPSE_CTL, + 0x01, 0x01); + do { + loop_cnt++; + /* Time to enable the power domain for memory */ + usleep_range(100, 150); + status = snd_soc_read(codec, + WCD934X_CPE_SS_SOC_SW_COLLAPSE_CTL); + } while ((status & 0x02) != 0x02 && + loop_cnt != WCD_MEM_ENABLE_MAX_RETRIES); + + if ((status & 0x02) != 0x02) { + dev_err(cntl->codec->dev, + "%s: power domain not enabled, status = 0x%02x\n", + __func__, status); + ret = -EIO; + goto done; + } + + /* Rest of the memory */ + wcd9xxx_slim_write_repeat(wcd9xxx, + WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_2, + ARRAY_SIZE(mem_enable_values), + mem_enable_values); + wcd9xxx_slim_write_repeat(wcd9xxx, + WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_3, + ARRAY_SIZE(mem_enable_values), + mem_enable_values); + + snd_soc_write(codec, WCD934X_CPE_SS_PWR_CPE_DRAM1_SHUTDOWN, + 0x05); + break; + + default: + dev_err(cntl->codec->dev, "%s: Invalid mem_type %d\n", + __func__, mem_type); + ret = -EINVAL; + break; + } +done: + /* Make sure Deep sleep of memories is enabled for all banks */ + snd_soc_write(codec, WCD934X_CPE_SS_PWR_CPE_SYSMEM_DEEPSLP_0, 0xFF); + snd_soc_write(codec, WCD934X_CPE_SS_PWR_CPE_SYSMEM_DEEPSLP_1, 0x0F); + + return ret; +} + +static void wcd_cntl_disable_memory(struct wcd_dsp_cntl *cntl, + enum wcd_mem_type mem_type) +{ + struct snd_soc_codec *codec = cntl->codec; + u8 val; + + switch (mem_type) { + case WCD_MEM_TYPE_ALWAYS_ON: + snd_soc_write(codec, WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_1, + 0xFF); + snd_soc_write(codec, WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_0, + 0xFF); + break; + case WCD_MEM_TYPE_SWITCHABLE: + snd_soc_write(codec, WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_3, + 0xFF); + snd_soc_write(codec, WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_2, + 0xFF); + snd_soc_write(codec, WCD934X_CPE_SS_PWR_CPE_DRAM1_SHUTDOWN, + 0x07); + + snd_soc_update_bits(codec, WCD934X_CPE_SS_SOC_SW_COLLAPSE_CTL, + 0x01, 0x00); + val = snd_soc_read(codec, WCD934X_CPE_SS_SOC_SW_COLLAPSE_CTL); + if (val & 0x02) + dev_err(codec->dev, + "%s: Disable switchable failed, val = 0x%02x", + __func__, val); + + snd_soc_update_bits(codec, WCD934X_TEST_DEBUG_MEM_CTRL, + 0x80, 0x00); + break; + default: + dev_err(cntl->codec->dev, "%s: Invalid mem_type %d\n", + __func__, mem_type); + break; + } + + snd_soc_write(codec, WCD934X_CPE_SS_PWR_CPE_SYSMEM_DEEPSLP_0, 0xFF); + snd_soc_write(codec, WCD934X_CPE_SS_PWR_CPE_SYSMEM_DEEPSLP_1, 0x0F); +} + +static void wcd_cntl_do_shutdown(struct wcd_dsp_cntl *cntl) +{ + struct snd_soc_codec *codec = cntl->codec; + + /* Disable WDOG */ + snd_soc_update_bits(codec, WCD934X_CPE_SS_WDOG_CFG, + 0x3F, 0x01); + + /* Put WDSP in reset state */ + snd_soc_update_bits(codec, WCD934X_CPE_SS_CPE_CTL, + 0x02, 0x00); + + /* If DSP transitions from boot to shutdown, then vote for SVS */ + if (cntl->is_wdsp_booted) + cntl->cdc_cb->cdc_vote_svs(codec, true); + cntl->is_wdsp_booted = false; +} + +static int wcd_cntl_do_boot(struct wcd_dsp_cntl *cntl) +{ + struct snd_soc_codec *codec = cntl->codec; + int ret = 0; + + /* + * Debug mode is set from debugfs file node. If debug_mode + * is set, then do not configure the watchdog timer. This + * will be required for debugging the DSP firmware. + */ + if (cntl->debug_mode) { + snd_soc_update_bits(codec, WCD934X_CPE_SS_WDOG_CFG, + 0x3F, 0x01); + } else { + snd_soc_update_bits(codec, WCD934X_CPE_SS_WDOG_CFG, + 0x3F, 0x21); + } + + /* Make sure all the error interrupts are cleared */ + snd_soc_write(codec, WCD934X_CPE_SS_SS_ERROR_INT_CLEAR_0A, 0xFF); + snd_soc_write(codec, WCD934X_CPE_SS_SS_ERROR_INT_CLEAR_0B, 0xFF); + + reinit_completion(&cntl->boot_complete); + + /* Remove WDSP out of reset */ + snd_soc_update_bits(codec, WCD934X_CPE_SS_CPE_CTL, + 0x02, 0x02); + + /* + * In debug mode, DSP may not boot up normally, + * wait indefinitely for DSP to boot. + */ + if (cntl->debug_mode) { + wait_for_completion(&cntl->boot_complete); + dev_dbg(codec->dev, "%s: WDSP booted in dbg mode\n", __func__); + cntl->is_wdsp_booted = true; + goto done; + } + + /* Boot in normal mode */ + ret = wait_for_completion_timeout(&cntl->boot_complete, + msecs_to_jiffies(WCD_DSP_BOOT_TIMEOUT_MS)); + if (!ret) { + dev_err(codec->dev, "%s: WDSP boot timed out\n", + __func__); + if (cntl->dbg_dmp_enable) + wcd_cntl_collect_debug_dumps(cntl, true); + ret = -ETIMEDOUT; + goto err_boot; + } else { + /* + * Re-initialize the return code to 0, as in success case, + * it will hold the remaining time for completion timeout + */ + ret = 0; + } + + dev_dbg(codec->dev, "%s: WDSP booted in normal mode\n", __func__); + cntl->is_wdsp_booted = true; + + /* Enable WDOG */ + snd_soc_update_bits(codec, WCD934X_CPE_SS_WDOG_CFG, + 0x10, 0x10); +done: + /* If dsp booted up, then remove vote on SVS */ + if (cntl->is_wdsp_booted) + cntl->cdc_cb->cdc_vote_svs(codec, false); + + return ret; +err_boot: + /* call shutdown to perform cleanup */ + wcd_cntl_do_shutdown(cntl); + return ret; +} + +static irqreturn_t wcd_cntl_ipc_irq(int irq, void *data) +{ + struct wcd_dsp_cntl *cntl = data; + int ret; + + complete(&cntl->boot_complete); + + if (cntl->m_dev && cntl->m_ops && + cntl->m_ops->signal_handler) + ret = cntl->m_ops->signal_handler(cntl->m_dev, WDSP_IPC1_INTR, + NULL); + else + ret = -EINVAL; + + if (ret < 0) + dev_err(cntl->codec->dev, + "%s: Failed to handle irq %d\n", __func__, irq); + + return IRQ_HANDLED; +} + +static irqreturn_t wcd_cntl_err_irq(int irq, void *data) +{ + struct wcd_dsp_cntl *cntl = data; + struct snd_soc_codec *codec = cntl->codec; + struct wdsp_err_signal_arg arg; + u16 status = 0; + u8 reg_val; + int rc, ret = 0; + + reg_val = snd_soc_read(codec, WCD934X_CPE_SS_SS_ERROR_INT_STATUS_0A); + status = status | reg_val; + + reg_val = snd_soc_read(codec, WCD934X_CPE_SS_SS_ERROR_INT_STATUS_0B); + status = status | (reg_val << 8); + + dev_info(codec->dev, "%s: error interrupt status = 0x%x\n", + __func__, status); + + if ((status & cntl->irqs.fatal_irqs) && + (cntl->m_dev && cntl->m_ops && cntl->m_ops->signal_handler)) { + /* + * If WDSP SSR happens, skip collecting debug dumps. + * If debug dumps collecting happens first, WDSP_ERR_INTR + * will be blocked in signal_handler and get processed later. + */ + rc = WCD_CNTL_SET_ERR_IRQ_FLAG(cntl); + arg.mem_dumps_enabled = cntl->ramdump_enable; + arg.remote_start_addr = WCD_934X_RAMDUMP_START_ADDR; + arg.dump_size = WCD_934X_RAMDUMP_SIZE; + ret = cntl->m_ops->signal_handler(cntl->m_dev, WDSP_ERR_INTR, + &arg); + if (ret < 0) + dev_err(cntl->codec->dev, + "%s: Failed to handle fatal irq 0x%x\n", + __func__, status & cntl->irqs.fatal_irqs); + wcd_cntl_change_online_state(cntl, 0); + if (rc == 0) + WCD_CNTL_CLR_ERR_IRQ_FLAG(cntl); + } else { + dev_err(cntl->codec->dev, "%s: Invalid signal_handler\n", + __func__); + } + + return IRQ_HANDLED; +} + +static int wcd_control_handler(struct device *dev, void *priv_data, + enum wdsp_event_type event, void *data) +{ + struct wcd_dsp_cntl *cntl = priv_data; + struct snd_soc_codec *codec = cntl->codec; + int ret = 0; + + switch (event) { + case WDSP_EVENT_POST_INIT: + case WDSP_EVENT_POST_DLOAD_CODE: + case WDSP_EVENT_DLOAD_FAILED: + case WDSP_EVENT_POST_SHUTDOWN: + + /* Disable CPAR */ + wcd_cntl_cpar_ctrl(cntl, false); + /* Disable all the clocks */ + ret = wcd_cntl_clocks_disable(cntl); + if (ret < 0) + dev_err(codec->dev, + "%s: Failed to disable clocks, err = %d\n", + __func__, ret); + + if (event == WDSP_EVENT_POST_DLOAD_CODE) + /* Mark DSP online since code download is complete */ + wcd_cntl_change_online_state(cntl, 1); + break; + + case WDSP_EVENT_PRE_DLOAD_DATA: + case WDSP_EVENT_PRE_DLOAD_CODE: + + /* Enable all the clocks */ + ret = wcd_cntl_clocks_enable(cntl); + if (ret < 0) { + dev_err(codec->dev, + "%s: Failed to enable clocks, err = %d\n", + __func__, ret); + goto done; + } + + /* Enable CPAR */ + wcd_cntl_cpar_ctrl(cntl, true); + + if (event == WDSP_EVENT_PRE_DLOAD_CODE) + wcd_cntl_enable_memory(cntl, WCD_MEM_TYPE_ALWAYS_ON); + else if (event == WDSP_EVENT_PRE_DLOAD_DATA) + wcd_cntl_enable_memory(cntl, WCD_MEM_TYPE_SWITCHABLE); + break; + + case WDSP_EVENT_DO_BOOT: + + ret = wcd_cntl_do_boot(cntl); + if (ret < 0) + dev_err(codec->dev, + "%s: WDSP boot failed, err = %d\n", + __func__, ret); + break; + + case WDSP_EVENT_DO_SHUTDOWN: + + wcd_cntl_do_shutdown(cntl); + wcd_cntl_disable_memory(cntl, WCD_MEM_TYPE_SWITCHABLE); + break; + + default: + dev_dbg(codec->dev, "%s: unhandled event %d\n", + __func__, event); + } + +done: + return ret; +} + +static int wcd_cntl_sysfs_init(char *dir, struct wcd_dsp_cntl *cntl) +{ + struct snd_soc_codec *codec = cntl->codec; + int ret = 0; + + ret = kobject_init_and_add(&cntl->wcd_kobj, &wcd_cntl_ktype, + kernel_kobj, dir); + if (ret < 0) { + dev_err(codec->dev, + "%s: Failed to add kobject %s, err = %d\n", + __func__, dir, ret); + goto done; + } + + ret = sysfs_create_file(&cntl->wcd_kobj, &cntl_attr_boot.attr); + if (ret < 0) { + dev_err(codec->dev, + "%s: Failed to add wdsp_boot sysfs entry to %s\n", + __func__, dir); + goto fail_create_file; + } + + return ret; + +fail_create_file: + kobject_put(&cntl->wcd_kobj); +done: + return ret; +} + +static void wcd_cntl_sysfs_remove(struct wcd_dsp_cntl *cntl) +{ + sysfs_remove_file(&cntl->wcd_kobj, &cntl_attr_boot.attr); + kobject_put(&cntl->wcd_kobj); +} + +static void wcd_cntl_debugfs_init(char *dir, struct wcd_dsp_cntl *cntl) +{ + struct snd_soc_codec *codec = cntl->codec; + + cntl->entry = debugfs_create_dir(dir, NULL); + if (IS_ERR_OR_NULL(dir)) { + dev_err(codec->dev, "%s debugfs_create_dir failed for %s\n", + __func__, dir); + goto done; + } + + debugfs_create_u32("debug_mode", 0644, + cntl->entry, &cntl->debug_mode); + debugfs_create_bool("ramdump_enable", 0644, + cntl->entry, &cntl->ramdump_enable); + debugfs_create_bool("debug_dump_enable", 0644, + cntl->entry, &cntl->dbg_dmp_enable); +done: + return; +} + +static void wcd_cntl_debugfs_remove(struct wcd_dsp_cntl *cntl) +{ + if (cntl) + debugfs_remove(cntl->entry); +} + +static int wcd_miscdev_release(struct inode *inode, struct file *filep) +{ + struct wcd_dsp_cntl *cntl = container_of(filep->private_data, + struct wcd_dsp_cntl, miscdev); + if (!cntl->m_dev || !cntl->m_ops || + !cntl->m_ops->vote_for_dsp) { + dev_err(cntl->codec->dev, + "%s: DSP not ready to boot\n", __func__); + return -EINVAL; + } + + /* Make sure the DSP users goes to zero upon closing dev node */ + while (cntl->boot_reqs > 0) { + cntl->m_ops->vote_for_dsp(cntl->m_dev, false); + cntl->boot_reqs--; + } + + return 0; +} + +static ssize_t wcd_miscdev_write(struct file *filep, const char __user *ubuf, + size_t count, loff_t *pos) +{ + struct wcd_dsp_cntl *cntl = container_of(filep->private_data, + struct wcd_dsp_cntl, miscdev); + char val[WCD_MISCDEV_CMD_MAX_LEN + 1]; + bool vote; + int ret = 0; + + memset(val, 0, WCD_MISCDEV_CMD_MAX_LEN + 1); + + if (count == 0 || count > WCD_MISCDEV_CMD_MAX_LEN) { + pr_err("%s: Invalid count = %zd\n", __func__, count); + ret = -EINVAL; + goto done; + } + + ret = copy_from_user(val, ubuf, count); + if (ret < 0) { + dev_err(cntl->codec->dev, + "%s: copy_from_user failed, err = %d\n", + __func__, ret); + ret = -EFAULT; + goto done; + } + + if (val[0] == '1') { + cntl->boot_reqs++; + vote = true; + } else if (val[0] == '0') { + if (cntl->boot_reqs == 0) { + dev_err(cntl->codec->dev, + "%s: WDSP already disabled\n", + __func__); + ret = -EINVAL; + goto done; + } + cntl->boot_reqs--; + vote = false; + } else if (!strcmp(val, "DEBUG_DUMP")) { + if (cntl->dbg_dmp_enable) { + dev_dbg(cntl->codec->dev, + "%s: Collect dumps for debug use\n", __func__); + wcd_cntl_collect_debug_dumps(cntl, false); + } + /* + * simply ignore the request from userspace + * if dbg_dump_enable is not set from debugfs + */ + goto done; + } else { + dev_err(cntl->codec->dev, "%s: Invalid value %s\n", + __func__, val); + ret = -EINVAL; + goto done; + } + + dev_dbg(cntl->codec->dev, + "%s: booted = %s, ref_cnt = %d, vote = %s\n", + __func__, cntl->is_wdsp_booted ? "true" : "false", + cntl->boot_reqs, vote ? "true" : "false"); + + if (cntl->m_dev && cntl->m_ops && + cntl->m_ops->vote_for_dsp) + ret = cntl->m_ops->vote_for_dsp(cntl->m_dev, vote); + else + ret = -EINVAL; +done: + if (ret) + return ret; + else + return count; +} + +static const struct file_operations wcd_miscdev_fops = { + .write = wcd_miscdev_write, + .release = wcd_miscdev_release, +}; + +static int wcd_cntl_miscdev_create(struct wcd_dsp_cntl *cntl) +{ + snprintf(cntl->miscdev_name, ARRAY_SIZE(cntl->miscdev_name), + "wcd_dsp%u_control", cntl->dsp_instance); + cntl->miscdev.minor = MISC_DYNAMIC_MINOR; + cntl->miscdev.name = cntl->miscdev_name; + cntl->miscdev.fops = &wcd_miscdev_fops; + cntl->miscdev.parent = cntl->codec->dev; + + return misc_register(&cntl->miscdev); +} + +static void wcd_cntl_miscdev_destroy(struct wcd_dsp_cntl *cntl) +{ + misc_deregister(&cntl->miscdev); +} + +static int wcd_control_init(struct device *dev, void *priv_data) +{ + struct wcd_dsp_cntl *cntl = priv_data; + struct snd_soc_codec *codec = cntl->codec; + struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent); + struct wcd9xxx_core_resource *core_res = &wcd9xxx->core_res; + int ret; + bool err_irq_requested = false; + + ret = wcd9xxx_request_irq(core_res, + cntl->irqs.cpe_ipc1_irq, + wcd_cntl_ipc_irq, "CPE IPC1", + cntl); + if (ret < 0) { + dev_err(codec->dev, + "%s: Failed to request cpe ipc irq, err = %d\n", + __func__, ret); + goto done; + } + + /* Unmask the fatal irqs */ + snd_soc_write(codec, WCD934X_CPE_SS_SS_ERROR_INT_MASK_0A, + ~(cntl->irqs.fatal_irqs & 0xFF)); + snd_soc_write(codec, WCD934X_CPE_SS_SS_ERROR_INT_MASK_0B, + ~((cntl->irqs.fatal_irqs >> 8) & 0xFF)); + + /* + * CPE ERR irq is used only for error reporting from WCD DSP, + * even if this request fails, DSP can be function normally. + * Continuing with init even if the CPE ERR irq request fails. + */ + if (wcd9xxx_request_irq(core_res, cntl->irqs.cpe_err_irq, + wcd_cntl_err_irq, "CPE ERR", cntl)) + dev_info(codec->dev, "%s: Failed request_irq(cpe_err_irq)", + __func__); + else + err_irq_requested = true; + + + /* Enable all the clocks */ + ret = wcd_cntl_clocks_enable(cntl); + if (ret < 0) { + dev_err(codec->dev, "%s: Failed to enable clocks, err = %d\n", + __func__, ret); + goto err_clk_enable; + } + wcd_cntl_cpar_ctrl(cntl, true); + + return 0; + +err_clk_enable: + /* Mask all error interrupts */ + snd_soc_write(codec, WCD934X_CPE_SS_SS_ERROR_INT_MASK_0A, 0xFF); + snd_soc_write(codec, WCD934X_CPE_SS_SS_ERROR_INT_MASK_0B, 0xFF); + + /* Free the irq's requested */ + wcd9xxx_free_irq(core_res, cntl->irqs.cpe_ipc1_irq, cntl); + + if (err_irq_requested) + wcd9xxx_free_irq(core_res, cntl->irqs.cpe_err_irq, cntl); +done: + return ret; +} + +static int wcd_control_deinit(struct device *dev, void *priv_data) +{ + struct wcd_dsp_cntl *cntl = priv_data; + struct snd_soc_codec *codec = cntl->codec; + struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent); + struct wcd9xxx_core_resource *core_res = &wcd9xxx->core_res; + + wcd_cntl_clocks_disable(cntl); + wcd_cntl_cpar_ctrl(cntl, false); + + /* Mask all error interrupts */ + snd_soc_write(codec, WCD934X_CPE_SS_SS_ERROR_INT_MASK_0A, 0xFF); + snd_soc_write(codec, WCD934X_CPE_SS_SS_ERROR_INT_MASK_0B, 0xFF); + + /* Free the irq's requested */ + wcd9xxx_free_irq(core_res, cntl->irqs.cpe_err_irq, cntl); + wcd9xxx_free_irq(core_res, cntl->irqs.cpe_ipc1_irq, cntl); + + return 0; +} + +static struct wdsp_cmpnt_ops control_ops = { + .init = wcd_control_init, + .deinit = wcd_control_deinit, + .event_handler = wcd_control_handler, +}; + +static int wcd_ctrl_component_bind(struct device *dev, + struct device *master, + void *data) +{ + struct wcd_dsp_cntl *cntl; + struct snd_soc_codec *codec; + struct snd_card *card; + struct snd_info_entry *entry; + char proc_name[WCD_PROCFS_ENTRY_MAX_LEN]; + char wcd_cntl_dir_name[WCD_CNTL_DIR_NAME_LEN_MAX]; + int ret = 0; + + if (!dev || !master || !data) { + pr_err("%s: Invalid parameters\n", __func__); + return -EINVAL; + } + + cntl = tavil_get_wcd_dsp_cntl(dev); + if (!cntl) { + dev_err(dev, "%s: Failed to get cntl reference\n", + __func__); + return -EINVAL; + } + + cntl->m_dev = master; + cntl->m_ops = data; + + if (!cntl->m_ops->register_cmpnt_ops) { + dev_err(dev, "%s: invalid master callback register_cmpnt_ops\n", + __func__); + ret = -EINVAL; + goto done; + } + + ret = cntl->m_ops->register_cmpnt_ops(master, dev, cntl, &control_ops); + if (ret) { + dev_err(dev, "%s: register_cmpnt_ops failed, err = %d\n", + __func__, ret); + goto done; + } + + ret = wcd_cntl_miscdev_create(cntl); + if (ret < 0) { + dev_err(dev, "%s: misc dev register failed, err = %d\n", + __func__, ret); + goto done; + } + + snprintf(wcd_cntl_dir_name, WCD_CNTL_DIR_NAME_LEN_MAX, + "%s%d", "wdsp", cntl->dsp_instance); + ret = wcd_cntl_sysfs_init(wcd_cntl_dir_name, cntl); + if (ret < 0) { + dev_err(dev, "%s: sysfs_init failed, err = %d\n", + __func__, ret); + goto err_sysfs_init; + } + + wcd_cntl_debugfs_init(wcd_cntl_dir_name, cntl); + + codec = cntl->codec; + card = codec->component.card->snd_card; + snprintf(proc_name, WCD_PROCFS_ENTRY_MAX_LEN, "%s%d%s", "cpe", + cntl->dsp_instance, "_state"); + entry = snd_info_create_card_entry(card, proc_name, card->proc_root); + if (!entry) { + /* Do not treat this as Fatal error */ + dev_err(dev, "%s: Failed to create procfs entry %s\n", + __func__, proc_name); + goto err_sysfs_init; + } + + cntl->ssr_entry.entry = entry; + cntl->ssr_entry.offline = 1; + entry->size = WCD_PROCFS_ENTRY_MAX_LEN; + entry->content = SNDRV_INFO_CONTENT_DATA; + entry->c.ops = &wdsp_ssr_entry_ops; + entry->private_data = cntl; + ret = snd_info_register(entry); + if (ret < 0) { + dev_err(dev, "%s: Failed to register entry %s, err = %d\n", + __func__, proc_name, ret); + snd_info_free_entry(entry); + /* Let bind still happen even if creating the entry failed */ + ret = 0; + } +done: + return ret; + +err_sysfs_init: + wcd_cntl_miscdev_destroy(cntl); + return ret; +} + +static void wcd_ctrl_component_unbind(struct device *dev, + struct device *master, + void *data) +{ + struct wcd_dsp_cntl *cntl; + + if (!dev) { + pr_err("%s: Invalid device\n", __func__); + return; + } + + cntl = tavil_get_wcd_dsp_cntl(dev); + if (!cntl) { + dev_err(dev, "%s: Failed to get cntl reference\n", + __func__); + return; + } + + cntl->m_dev = NULL; + cntl->m_ops = NULL; + + /* Remove the sysfs entries */ + wcd_cntl_sysfs_remove(cntl); + + /* Remove the debugfs entries */ + wcd_cntl_debugfs_remove(cntl); + + /* Remove the misc device */ + wcd_cntl_miscdev_destroy(cntl); +} + +static const struct component_ops wcd_ctrl_component_ops = { + .bind = wcd_ctrl_component_bind, + .unbind = wcd_ctrl_component_unbind, +}; + +/* + * wcd_dsp_ssr_event: handle the SSR event raised by caller. + * @cntl: Handle to the wcd_dsp_cntl structure + * @event: The SSR event to be handled + * + * Notifies the manager driver about the SSR event. + * Returns 0 on success and negative error code on error. + */ +int wcd_dsp_ssr_event(struct wcd_dsp_cntl *cntl, enum cdc_ssr_event event) +{ + int ret = 0; + + if (!cntl) { + pr_err("%s: Invalid handle to control\n", __func__); + return -EINVAL; + } + + if (!cntl->m_dev || !cntl->m_ops || !cntl->m_ops->signal_handler) { + dev_err(cntl->codec->dev, + "%s: Invalid signal_handler callback\n", __func__); + return -EINVAL; + } + + switch (event) { + case WCD_CDC_DOWN_EVENT: + ret = cntl->m_ops->signal_handler(cntl->m_dev, + WDSP_CDC_DOWN_SIGNAL, + NULL); + if (ret < 0) + dev_err(cntl->codec->dev, + "%s: WDSP_CDC_DOWN_SIGNAL failed, err = %d\n", + __func__, ret); + wcd_cntl_change_online_state(cntl, 0); + break; + case WCD_CDC_UP_EVENT: + ret = cntl->m_ops->signal_handler(cntl->m_dev, + WDSP_CDC_UP_SIGNAL, + NULL); + if (ret < 0) + dev_err(cntl->codec->dev, + "%s: WDSP_CDC_UP_SIGNAL failed, err = %d\n", + __func__, ret); + break; + default: + dev_err(cntl->codec->dev, "%s: Invalid event %d\n", + __func__, event); + ret = -EINVAL; + break; + } + + return ret; +} +EXPORT_SYMBOL(wcd_dsp_ssr_event); + +/* + * wcd_dsp_cntl_init: Initialize the wcd-dsp control + * @codec: pointer to the codec handle + * @params: Parameters required to initialize wcd-dsp control + * + * This API is expected to be invoked by the codec driver and + * provide information essential for the wcd dsp control to + * configure and initialize the dsp + */ +void wcd_dsp_cntl_init(struct snd_soc_codec *codec, + struct wcd_dsp_params *params, + struct wcd_dsp_cntl **cntl) +{ + struct wcd_dsp_cntl *control; + int ret; + + if (!codec || !params) { + pr_err("%s: Invalid handle to %s\n", __func__, + (!codec) ? "codec" : "params"); + *cntl = NULL; + return; + } + + if (*cntl) { + pr_err("%s: cntl is non NULL, maybe already initialized ?\n", + __func__); + return; + } + + if (!params->cb || !params->cb->cdc_clk_en || + !params->cb->cdc_vote_svs) { + dev_err(codec->dev, + "%s: clk_en and vote_svs callbacks must be provided\n", + __func__); + return; + } + + control = kzalloc(sizeof(*control), GFP_KERNEL); + if (!(control)) + return; + + control->codec = codec; + control->clk_rate = params->clk_rate; + control->cdc_cb = params->cb; + control->dsp_instance = params->dsp_instance; + memcpy(&control->irqs, ¶ms->irqs, sizeof(control->irqs)); + init_completion(&control->boot_complete); + mutex_init(&control->clk_mutex); + mutex_init(&control->ssr_mutex); + init_waitqueue_head(&control->ssr_entry.offline_poll_wait); + WCD_CNTL_CLR_ERR_IRQ_FLAG(control); + + /* + * The default state of WDSP is in SVS mode. + * Vote for SVS now, the vote will be removed only + * after DSP is booted up. + */ + control->cdc_cb->cdc_vote_svs(codec, true); + + /* + * If this is the last component needed by master to be ready, + * then component_bind will be called within the component_add. + * Hence, the data pointer should be assigned before component_add, + * so that we can access it during this component's bind call. + */ + *cntl = control; + ret = component_add(codec->dev, &wcd_ctrl_component_ops); + if (ret) { + dev_err(codec->dev, "%s: component_add failed, err = %d\n", + __func__, ret); + kfree(*cntl); + *cntl = NULL; + } +} +EXPORT_SYMBOL(wcd_dsp_cntl_init); + +/* + * wcd_dsp_cntl_deinit: De-initialize the wcd-dsp control + * @cntl: The struct wcd_dsp_cntl to de-initialize + * + * This API is intended to be invoked by the codec driver + * to de-initialize the wcd dsp control + */ +void wcd_dsp_cntl_deinit(struct wcd_dsp_cntl **cntl) +{ + struct wcd_dsp_cntl *control = *cntl; + struct snd_soc_codec *codec; + + /* If control is NULL, there is nothing to de-initialize */ + if (!control) + return; + codec = control->codec; + + /* + * Calling shutdown will cleanup all register states, + * irrespective of DSP was booted up or not. + */ + wcd_cntl_do_shutdown(control); + wcd_cntl_disable_memory(control, WCD_MEM_TYPE_SWITCHABLE); + wcd_cntl_disable_memory(control, WCD_MEM_TYPE_ALWAYS_ON); + + component_del(codec->dev, &wcd_ctrl_component_ops); + + mutex_destroy(&control->clk_mutex); + mutex_destroy(&control->ssr_mutex); + kfree(*cntl); + *cntl = NULL; +} +EXPORT_SYMBOL(wcd_dsp_cntl_deinit); diff --git a/audio-kernel/asoc/codecs/wcd934x/wcd934x-dsp-cntl.h b/audio-kernel/asoc/codecs/wcd934x/wcd934x-dsp-cntl.h new file mode 100644 index 000000000..5ce4fd85d --- /dev/null +++ b/audio-kernel/asoc/codecs/wcd934x/wcd934x-dsp-cntl.h @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2016-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 __WCD934X_DSP_CNTL_H__ +#define __WCD934X_DSP_CNTL_H__ + +#include +#include +#include + +enum cdc_ssr_event { + WCD_CDC_DOWN_EVENT, + WCD_CDC_UP_EVENT, +}; + +struct wcd_dsp_cdc_cb { + /* Callback to enable codec clock */ + int (*cdc_clk_en)(struct snd_soc_codec *, bool); + /* Callback to vote and unvote for SVS2 mode */ + void (*cdc_vote_svs)(struct snd_soc_codec *, bool); +}; + +struct wcd_dsp_irq_info { + /* IPC interrupt */ + int cpe_ipc1_irq; + + /* CPE error summary interrupt */ + int cpe_err_irq; + + /* + * Bit mask to indicate which of the + * error interrupts are to be considered + * as fatal. + */ + u16 fatal_irqs; +}; + +struct wcd_dsp_params { + struct wcd_dsp_cdc_cb *cb; + struct wcd_dsp_irq_info irqs; + + /* Rate at which the codec clock operates */ + u32 clk_rate; + + /* + * Represents the dsp instance, will be used + * to create sysfs and debugfs entries with + * directory wdsp + */ + u32 dsp_instance; +}; + +struct wdsp_ssr_entry { + u8 offline; + u8 offline_change; + wait_queue_head_t offline_poll_wait; + struct snd_info_entry *entry; +}; + +struct wcd_dsp_cntl { + /* Handle to codec */ + struct snd_soc_codec *codec; + + /* Clk rate of the codec clock */ + u32 clk_rate; + + /* Callbacks to codec driver */ + const struct wcd_dsp_cdc_cb *cdc_cb; + + /* Completion to indicate WDSP boot done */ + struct completion boot_complete; + + struct wcd_dsp_irq_info irqs; + u32 dsp_instance; + + /* Sysfs entries related */ + int boot_reqs; + struct kobject wcd_kobj; + + /* Debugfs related */ + struct dentry *entry; + u32 debug_mode; + bool ramdump_enable; + bool dbg_dmp_enable; + + /* WDSP manager drivers data */ + struct device *m_dev; + struct wdsp_mgr_ops *m_ops; + + /* clk related */ + struct mutex clk_mutex; + bool is_clk_enabled; + + /* Keep track of WDSP boot status */ + bool is_wdsp_booted; + + /* SSR related */ + struct wdsp_ssr_entry ssr_entry; + struct mutex ssr_mutex; + + /* Misc device related */ + char miscdev_name[256]; + struct miscdevice miscdev; +#ifdef CONFIG_DEBUG_FS + /* Debug dump related */ + atomic_t err_irq_flag; +#endif +}; + +void wcd_dsp_cntl_init(struct snd_soc_codec *codec, + struct wcd_dsp_params *params, + struct wcd_dsp_cntl **cntl); +void wcd_dsp_cntl_deinit(struct wcd_dsp_cntl **cntl); +int wcd_dsp_ssr_event(struct wcd_dsp_cntl *cntl, enum cdc_ssr_event event); +#endif /* end __WCD_DSP_CONTROL_H__ */ diff --git a/audio-kernel/asoc/codecs/wcd934x/wcd934x-mbhc.c b/audio-kernel/asoc/codecs/wcd934x/wcd934x-mbhc.c new file mode 100644 index 000000000..bfe1e17a4 --- /dev/null +++ b/audio-kernel/asoc/codecs/wcd934x/wcd934x-mbhc.c @@ -0,0 +1,1153 @@ +/* + * 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 + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "wcd934x.h" +#include "wcd934x-mbhc.h" +#include +#include "wcd934x_irq.h" +#include "../core.h" +#include "../pdata.h" +#include "../wcd9xxx-irq.h" +#include "../wcdcal-hwdep.h" +#include "../wcd-mbhc-v2-api.h" + +#define TAVIL_ZDET_SUPPORTED true +/* Z value defined in milliohm */ +#define TAVIL_ZDET_VAL_32 32000 +#define TAVIL_ZDET_VAL_400 400000 +#define TAVIL_ZDET_VAL_1200 1200000 +#define TAVIL_ZDET_VAL_100K 100000000 +/* Z floating defined in ohms */ +#define TAVIL_ZDET_FLOATING_IMPEDANCE 0x0FFFFFFE + +#define TAVIL_ZDET_NUM_MEASUREMENTS 900 +#define TAVIL_MBHC_GET_C1(c) ((c & 0xC000) >> 14) +#define TAVIL_MBHC_GET_X1(x) (x & 0x3FFF) +/* Z value compared in milliOhm */ +#define TAVIL_MBHC_IS_SECOND_RAMP_REQUIRED(z) ((z > 400000) || (z < 32000)) +#define TAVIL_MBHC_ZDET_CONST (86 * 16384) +#define TAVIL_MBHC_MOISTURE_RREF R_24_KOHM + +static struct wcd_mbhc_register + wcd_mbhc_registers[WCD_MBHC_REG_FUNC_MAX] = { + WCD_MBHC_REGISTER("WCD_MBHC_L_DET_EN", + WCD934X_ANA_MBHC_MECH, 0x80, 7, 0), + WCD_MBHC_REGISTER("WCD_MBHC_GND_DET_EN", + WCD934X_ANA_MBHC_MECH, 0x40, 6, 0), + WCD_MBHC_REGISTER("WCD_MBHC_MECH_DETECTION_TYPE", + WCD934X_ANA_MBHC_MECH, 0x20, 5, 0), + WCD_MBHC_REGISTER("WCD_MBHC_MIC_CLAMP_CTL", + WCD934X_MBHC_NEW_PLUG_DETECT_CTL, 0x30, 4, 0), + WCD_MBHC_REGISTER("WCD_MBHC_ELECT_DETECTION_TYPE", + WCD934X_ANA_MBHC_ELECT, 0x08, 3, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HS_L_DET_PULL_UP_CTRL", + WCD934X_MBHC_NEW_PLUG_DETECT_CTL, 0xC0, 6, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HS_L_DET_PULL_UP_COMP_CTRL", + WCD934X_ANA_MBHC_MECH, 0x04, 2, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPHL_PLUG_TYPE", + WCD934X_ANA_MBHC_MECH, 0x10, 4, 0), + WCD_MBHC_REGISTER("WCD_MBHC_GND_PLUG_TYPE", + WCD934X_ANA_MBHC_MECH, 0x08, 3, 0), + WCD_MBHC_REGISTER("WCD_MBHC_SW_HPH_LP_100K_TO_GND", + WCD934X_ANA_MBHC_MECH, 0x01, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_ELECT_SCHMT_ISRC", + WCD934X_ANA_MBHC_ELECT, 0x06, 1, 0), + WCD_MBHC_REGISTER("WCD_MBHC_FSM_EN", + WCD934X_ANA_MBHC_ELECT, 0x80, 7, 0), + WCD_MBHC_REGISTER("WCD_MBHC_INSREM_DBNC", + WCD934X_MBHC_NEW_PLUG_DETECT_CTL, 0x0F, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_BTN_DBNC", + WCD934X_MBHC_NEW_CTL_1, 0x03, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HS_VREF", + WCD934X_MBHC_NEW_CTL_2, 0x03, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HS_COMP_RESULT", + WCD934X_ANA_MBHC_RESULT_3, 0x08, 3, 0), + WCD_MBHC_REGISTER("WCD_MBHC_IN2P_CLAMP_STATE", + WCD934X_ANA_MBHC_RESULT_3, 0x10, 4, 0), + WCD_MBHC_REGISTER("WCD_MBHC_MIC_SCHMT_RESULT", + WCD934X_ANA_MBHC_RESULT_3, 0x20, 5, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPHL_SCHMT_RESULT", + WCD934X_ANA_MBHC_RESULT_3, 0x80, 7, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPHR_SCHMT_RESULT", + WCD934X_ANA_MBHC_RESULT_3, 0x40, 6, 0), + WCD_MBHC_REGISTER("WCD_MBHC_OCP_FSM_EN", + WCD934X_HPH_OCP_CTL, 0x10, 4, 0), + WCD_MBHC_REGISTER("WCD_MBHC_BTN_RESULT", + WCD934X_ANA_MBHC_RESULT_3, 0x07, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_BTN_ISRC_CTL", + WCD934X_ANA_MBHC_ELECT, 0x70, 4, 0), + WCD_MBHC_REGISTER("WCD_MBHC_ELECT_RESULT", + WCD934X_ANA_MBHC_RESULT_3, 0xFF, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_MICB_CTRL", + WCD934X_ANA_MICB2, 0xC0, 6, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPH_CNP_WG_TIME", + WCD934X_HPH_CNP_WG_TIME, 0xFF, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPHR_PA_EN", + WCD934X_ANA_HPH, 0x40, 6, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPHL_PA_EN", + WCD934X_ANA_HPH, 0x80, 7, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPH_PA_EN", + WCD934X_ANA_HPH, 0xC0, 6, 0), + WCD_MBHC_REGISTER("WCD_MBHC_SWCH_LEVEL_REMOVE", + WCD934X_ANA_MBHC_RESULT_3, 0x10, 4, 0), + WCD_MBHC_REGISTER("WCD_MBHC_PULLDOWN_CTRL", + 0, 0, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_ANC_DET_EN", + WCD934X_MBHC_CTL_BCS, 0x02, 1, 0), + WCD_MBHC_REGISTER("WCD_MBHC_FSM_STATUS", + WCD934X_MBHC_STATUS_SPARE_1, 0x01, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_MUX_CTL", + WCD934X_MBHC_NEW_CTL_2, 0x70, 4, 0), + WCD_MBHC_REGISTER("WCD_MBHC_MOISTURE_STATUS", + WCD934X_MBHC_NEW_FSM_STATUS, 0x20, 5, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPHR_GND", + WCD934X_HPH_PA_CTL2, 0x40, 6, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPHL_GND", + WCD934X_HPH_PA_CTL2, 0x10, 4, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPHL_OCP_DET_EN", + WCD934X_HPH_L_TEST, 0x01, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPHR_OCP_DET_EN", + WCD934X_HPH_R_TEST, 0x01, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPHL_OCP_STATUS", + WCD934X_INTR_PIN1_STATUS0, 0x04, 2, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPHR_OCP_STATUS", + WCD934X_INTR_PIN1_STATUS0, 0x08, 3, 0), + WCD_MBHC_REGISTER("WCD_MBHC_ADC_EN", + WCD934X_MBHC_NEW_CTL_1, 0x08, 3, 0), + WCD_MBHC_REGISTER("WCD_MBHC_ADC_COMPLETE", WCD934X_MBHC_NEW_FSM_STATUS, + 0x40, 6, 0), + WCD_MBHC_REGISTER("WCD_MBHC_ADC_TIMEOUT", WCD934X_MBHC_NEW_FSM_STATUS, + 0x80, 7, 0), + WCD_MBHC_REGISTER("WCD_MBHC_ADC_RESULT", WCD934X_MBHC_NEW_ADC_RESULT, + 0xFF, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_MICB2_VOUT", WCD934X_ANA_MICB2, 0x3F, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_ADC_MODE", + WCD934X_MBHC_NEW_CTL_1, 0x10, 4, 0), + WCD_MBHC_REGISTER("WCD_MBHC_DETECTION_DONE", + WCD934X_MBHC_NEW_CTL_1, 0x04, 2, 0), + WCD_MBHC_REGISTER("WCD_MBHC_ELECT_ISRC_EN", + WCD934X_ANA_MBHC_ZDET, 0x02, 1, 0), +}; + +static const struct wcd_mbhc_intr intr_ids = { + .mbhc_sw_intr = WCD934X_IRQ_MBHC_SW_DET, + .mbhc_btn_press_intr = WCD934X_IRQ_MBHC_BUTTON_PRESS_DET, + .mbhc_btn_release_intr = WCD934X_IRQ_MBHC_BUTTON_RELEASE_DET, + .mbhc_hs_ins_intr = WCD934X_IRQ_MBHC_ELECT_INS_REM_LEG_DET, + .mbhc_hs_rem_intr = WCD934X_IRQ_MBHC_ELECT_INS_REM_DET, + .hph_left_ocp = WCD934X_IRQ_HPH_PA_OCPL_FAULT, + .hph_right_ocp = WCD934X_IRQ_HPH_PA_OCPR_FAULT, +}; + + +static char on_demand_supply_name[][MAX_ON_DEMAND_SUPPLY_NAME_LENGTH] = { + "cdc-vdd-mic-bias", +}; + +struct tavil_mbhc_zdet_param { + u16 ldo_ctl; + u16 noff; + u16 nshift; + u16 btn5; + u16 btn6; + u16 btn7; +}; + +static int tavil_mbhc_request_irq(struct snd_soc_codec *codec, + int irq, irq_handler_t handler, + const char *name, void *data) +{ + struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent); + struct wcd9xxx_core_resource *core_res = + &wcd9xxx->core_res; + + return wcd9xxx_request_irq(core_res, irq, handler, name, data); +} + +static void tavil_mbhc_irq_control(struct snd_soc_codec *codec, + int irq, bool enable) +{ + struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent); + struct wcd9xxx_core_resource *core_res = + &wcd9xxx->core_res; + if (enable) + wcd9xxx_enable_irq(core_res, irq); + else + wcd9xxx_disable_irq(core_res, irq); +} + +static int tavil_mbhc_free_irq(struct snd_soc_codec *codec, + int irq, void *data) +{ + struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent); + struct wcd9xxx_core_resource *core_res = + &wcd9xxx->core_res; + + wcd9xxx_free_irq(core_res, irq, data); + return 0; +} + +static void tavil_mbhc_clk_setup(struct snd_soc_codec *codec, + bool enable) +{ + if (enable) + snd_soc_update_bits(codec, WCD934X_MBHC_NEW_CTL_1, + 0x80, 0x80); + else + snd_soc_update_bits(codec, WCD934X_MBHC_NEW_CTL_1, + 0x80, 0x00); +} + +static int tavil_mbhc_btn_to_num(struct snd_soc_codec *codec) +{ + return snd_soc_read(codec, WCD934X_ANA_MBHC_RESULT_3) & 0x7; +} + +static int tavil_enable_ext_mb_source(struct wcd_mbhc *mbhc, + bool turn_on) +{ + struct wcd934x_mbhc *wcd934x_mbhc; + struct snd_soc_codec *codec = mbhc->codec; + struct wcd934x_on_demand_supply *supply; + int ret = 0; + + wcd934x_mbhc = container_of(mbhc, struct wcd934x_mbhc, wcd_mbhc); + + supply = &wcd934x_mbhc->on_demand_list[WCD934X_ON_DEMAND_MICBIAS]; + if (!supply->supply) { + dev_dbg(codec->dev, "%s: warning supply not present ond for %s\n", + __func__, "onDemand Micbias"); + return ret; + } + + dev_dbg(codec->dev, "%s turn_on: %d count: %d\n", __func__, turn_on, + supply->ondemand_supply_count); + + if (turn_on) { + if (!(supply->ondemand_supply_count)) { + ret = snd_soc_dapm_force_enable_pin( + snd_soc_codec_get_dapm(codec), + "MICBIAS_REGULATOR"); + snd_soc_dapm_sync(snd_soc_codec_get_dapm(codec)); + } + supply->ondemand_supply_count++; + } else { + if (supply->ondemand_supply_count > 0) + supply->ondemand_supply_count--; + if (!(supply->ondemand_supply_count)) { + ret = snd_soc_dapm_disable_pin( + snd_soc_codec_get_dapm(codec), + "MICBIAS_REGULATOR"); + snd_soc_dapm_sync(snd_soc_codec_get_dapm(codec)); + } + } + + if (ret) + dev_err(codec->dev, "%s: Failed to %s external micbias source\n", + __func__, turn_on ? "enable" : "disabled"); + else + dev_dbg(codec->dev, "%s: %s external micbias source\n", + __func__, turn_on ? "Enabled" : "Disabled"); + + return ret; +} + +static void tavil_mbhc_mbhc_bias_control(struct snd_soc_codec *codec, + bool enable) +{ + if (enable) + snd_soc_update_bits(codec, WCD934X_ANA_MBHC_ELECT, + 0x01, 0x01); + else + snd_soc_update_bits(codec, WCD934X_ANA_MBHC_ELECT, + 0x01, 0x00); +} + +static void tavil_mbhc_program_btn_thr(struct snd_soc_codec *codec, + s16 *btn_low, s16 *btn_high, + int num_btn, bool is_micbias) +{ + int i; + int vth; + + if (num_btn > WCD_MBHC_DEF_BUTTONS) { + dev_err(codec->dev, "%s: invalid number of buttons: %d\n", + __func__, num_btn); + return; + } + /* + * Tavil just needs one set of thresholds for button detection + * due to micbias voltage ramp to pullup upon button press. So + * btn_low and is_micbias are ignored and always program button + * thresholds using btn_high. + */ + for (i = 0; i < num_btn; i++) { + vth = ((btn_high[i] * 2) / 25) & 0x3F; + snd_soc_update_bits(codec, WCD934X_ANA_MBHC_BTN0 + i, + 0xFC, vth << 2); + dev_dbg(codec->dev, "%s: btn_high[%d]: %d, vth: %d\n", + __func__, i, btn_high[i], vth); + } +} + +static bool tavil_mbhc_lock_sleep(struct wcd_mbhc *mbhc, bool lock) +{ + struct snd_soc_codec *codec = mbhc->codec; + struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent); + struct wcd9xxx_core_resource *core_res = + &wcd9xxx->core_res; + bool ret = 0; + + if (lock) + ret = wcd9xxx_lock_sleep(core_res); + else + wcd9xxx_unlock_sleep(core_res); + + return ret; +} + +static int tavil_mbhc_register_notifier(struct wcd_mbhc *mbhc, + struct notifier_block *nblock, + bool enable) +{ + struct wcd934x_mbhc *wcd934x_mbhc; + + wcd934x_mbhc = container_of(mbhc, struct wcd934x_mbhc, wcd_mbhc); + + if (enable) + return blocking_notifier_chain_register(&wcd934x_mbhc->notifier, + nblock); + else + return blocking_notifier_chain_unregister( + &wcd934x_mbhc->notifier, nblock); +} + +static bool tavil_mbhc_micb_en_status(struct wcd_mbhc *mbhc, int micb_num) +{ + u8 val; + + if (micb_num == MIC_BIAS_2) { + val = (snd_soc_read(mbhc->codec, WCD934X_ANA_MICB2) >> 6); + if (val == 0x01) + return true; + } + return false; +} + +static bool tavil_mbhc_hph_pa_on_status(struct snd_soc_codec *codec) +{ + return (snd_soc_read(codec, WCD934X_ANA_HPH) & 0xC0) ? true : false; +} + +static void tavil_mbhc_hph_l_pull_up_control( + struct snd_soc_codec *codec, + enum mbhc_hs_pullup_iref pull_up_cur) +{ + /* Default pull up current to 2uA */ + if (pull_up_cur < I_OFF || pull_up_cur > I_3P0_UA || + pull_up_cur == I_DEFAULT) + pull_up_cur = I_2P0_UA; + + dev_dbg(codec->dev, "%s: HS pull up current:%d\n", + __func__, pull_up_cur); + + snd_soc_update_bits(codec, WCD934X_MBHC_NEW_PLUG_DETECT_CTL, + 0xC0, pull_up_cur << 6); +} + +static int tavil_mbhc_request_micbias(struct snd_soc_codec *codec, + int micb_num, int req) +{ + int ret; + + /* + * If micbias is requested, make sure that there + * is vote to enable mclk + */ + if (req == MICB_ENABLE) + tavil_cdc_mclk_enable(codec, true); + + ret = tavil_micbias_control(codec, micb_num, req, false); + + /* + * Release vote for mclk while requesting for + * micbias disable + */ + if (req == MICB_DISABLE) + tavil_cdc_mclk_enable(codec, false); + + return ret; +} + +static void tavil_mbhc_micb_ramp_control(struct snd_soc_codec *codec, + bool enable) +{ + if (enable) { + snd_soc_update_bits(codec, WCD934X_ANA_MICB2_RAMP, + 0x1C, 0x0C); + snd_soc_update_bits(codec, WCD934X_ANA_MICB2_RAMP, + 0x80, 0x80); + } else { + snd_soc_update_bits(codec, WCD934X_ANA_MICB2_RAMP, + 0x80, 0x00); + snd_soc_update_bits(codec, WCD934X_ANA_MICB2_RAMP, + 0x1C, 0x00); + } +} + +static struct firmware_cal *tavil_get_hwdep_fw_cal(struct wcd_mbhc *mbhc, + enum wcd_cal_type type) +{ + struct wcd934x_mbhc *wcd934x_mbhc; + struct firmware_cal *hwdep_cal; + struct snd_soc_codec *codec = mbhc->codec; + + wcd934x_mbhc = container_of(mbhc, struct wcd934x_mbhc, wcd_mbhc); + + if (!codec) { + pr_err("%s: NULL codec pointer\n", __func__); + return NULL; + } + hwdep_cal = wcdcal_get_fw_cal(wcd934x_mbhc->fw_data, type); + if (!hwdep_cal) + dev_err(codec->dev, "%s: cal not sent by %d\n", + __func__, type); + + return hwdep_cal; +} + +static int tavil_mbhc_micb_ctrl_threshold_mic(struct snd_soc_codec *codec, + int micb_num, bool req_en) +{ + struct wcd9xxx_pdata *pdata = dev_get_platdata(codec->dev->parent); + int rc, micb_mv; + + if (micb_num != MIC_BIAS_2) + return -EINVAL; + + /* + * If device tree micbias level is already above the minimum + * voltage needed to detect threshold microphone, then do + * not change the micbias, just return. + */ + if (pdata->micbias.micb2_mv >= WCD_MBHC_THR_HS_MICB_MV) + return 0; + + micb_mv = req_en ? WCD_MBHC_THR_HS_MICB_MV : pdata->micbias.micb2_mv; + + rc = tavil_mbhc_micb_adjust_voltage(codec, micb_mv, MIC_BIAS_2); + + return rc; +} + +static inline void tavil_mbhc_get_result_params(struct wcd9xxx *wcd9xxx, + s16 *d1_a, u16 noff, + int32_t *zdet) +{ + int i; + int val, val1; + s16 c1; + s32 x1, d1; + int32_t denom; + int minCode_param[] = { + 3277, 1639, 820, 410, 205, 103, 52, 26 + }; + + regmap_update_bits(wcd9xxx->regmap, WCD934X_ANA_MBHC_ZDET, 0x20, 0x20); + for (i = 0; i < TAVIL_ZDET_NUM_MEASUREMENTS; i++) { + regmap_read(wcd9xxx->regmap, WCD934X_ANA_MBHC_RESULT_2, &val); + if (val & 0x80) + break; + } + val = val << 0x8; + regmap_read(wcd9xxx->regmap, WCD934X_ANA_MBHC_RESULT_1, &val1); + val |= val1; + regmap_update_bits(wcd9xxx->regmap, WCD934X_ANA_MBHC_ZDET, 0x20, 0x00); + x1 = TAVIL_MBHC_GET_X1(val); + c1 = TAVIL_MBHC_GET_C1(val); + /* If ramp is not complete, give additional 5ms */ + if ((c1 < 2) && x1) + usleep_range(5000, 5050); + + if (!c1 || !x1) { + dev_dbg(wcd9xxx->dev, + "%s: Impedance detect ramp error, c1=%d, x1=0x%x\n", + __func__, c1, x1); + goto ramp_down; + } + d1 = d1_a[c1]; + denom = (x1 * d1) - (1 << (14 - noff)); + if (denom > 0) + *zdet = (TAVIL_MBHC_ZDET_CONST * 1000) / denom; + else if (x1 < minCode_param[noff]) + *zdet = TAVIL_ZDET_FLOATING_IMPEDANCE; + + dev_dbg(wcd9xxx->dev, "%s: d1=%d, c1=%d, x1=0x%x, z_val=%d(milliOhm)\n", + __func__, d1, c1, x1, *zdet); +ramp_down: + i = 0; + while (x1) { + regmap_bulk_read(wcd9xxx->regmap, + WCD934X_ANA_MBHC_RESULT_1, (u8 *)&val, 2); + x1 = TAVIL_MBHC_GET_X1(val); + i++; + if (i == TAVIL_ZDET_NUM_MEASUREMENTS) + break; + } +} + +static void tavil_mbhc_zdet_ramp(struct snd_soc_codec *codec, + struct tavil_mbhc_zdet_param *zdet_param, + int32_t *zl, int32_t *zr, s16 *d1_a) +{ + struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent); + int32_t zdet = 0; + + snd_soc_update_bits(codec, WCD934X_MBHC_NEW_ZDET_ANA_CTL, 0x70, + zdet_param->ldo_ctl << 4); + snd_soc_update_bits(codec, WCD934X_ANA_MBHC_BTN5, 0xFC, + zdet_param->btn5); + snd_soc_update_bits(codec, WCD934X_ANA_MBHC_BTN6, 0xFC, + zdet_param->btn6); + snd_soc_update_bits(codec, WCD934X_ANA_MBHC_BTN7, 0xFC, + zdet_param->btn7); + snd_soc_update_bits(codec, WCD934X_MBHC_NEW_ZDET_ANA_CTL, 0x0F, + zdet_param->noff); + snd_soc_update_bits(codec, WCD934X_MBHC_NEW_ZDET_RAMP_CTL, 0x0F, + zdet_param->nshift); + + if (!zl) + goto z_right; + /* Start impedance measurement for HPH_L */ + regmap_update_bits(wcd9xxx->regmap, + WCD934X_ANA_MBHC_ZDET, 0x80, 0x80); + dev_dbg(wcd9xxx->dev, "%s: ramp for HPH_L, noff = %d\n", + __func__, zdet_param->noff); + tavil_mbhc_get_result_params(wcd9xxx, d1_a, zdet_param->noff, &zdet); + regmap_update_bits(wcd9xxx->regmap, + WCD934X_ANA_MBHC_ZDET, 0x80, 0x00); + + *zl = zdet; + +z_right: + if (!zr) + return; + /* Start impedance measurement for HPH_R */ + regmap_update_bits(wcd9xxx->regmap, + WCD934X_ANA_MBHC_ZDET, 0x40, 0x40); + dev_dbg(wcd9xxx->dev, "%s: ramp for HPH_R, noff = %d\n", + __func__, zdet_param->noff); + tavil_mbhc_get_result_params(wcd9xxx, d1_a, zdet_param->noff, &zdet); + regmap_update_bits(wcd9xxx->regmap, + WCD934X_ANA_MBHC_ZDET, 0x40, 0x00); + + *zr = zdet; +} + +static inline void tavil_wcd_mbhc_qfuse_cal(struct snd_soc_codec *codec, + int32_t *z_val, int flag_l_r) +{ + s16 q1; + int q1_cal; + + if (*z_val < (TAVIL_ZDET_VAL_400/1000)) + q1 = snd_soc_read(codec, + WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT1 + (2 * flag_l_r)); + else + q1 = snd_soc_read(codec, + WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT2 + (2 * flag_l_r)); + if (q1 & 0x80) + q1_cal = (10000 - ((q1 & 0x7F) * 25)); + else + q1_cal = (10000 + (q1 * 25)); + if (q1_cal > 0) + *z_val = ((*z_val) * 10000) / q1_cal; +} + +static void tavil_wcd_mbhc_calc_impedance(struct wcd_mbhc *mbhc, uint32_t *zl, + uint32_t *zr) +{ + struct snd_soc_codec *codec = mbhc->codec; + struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent); + s16 reg0, reg1, reg2, reg3, reg4; + int32_t z1L, z1R, z1Ls; + int zMono, z_diff1, z_diff2; + bool is_fsm_disable = false; + struct tavil_mbhc_zdet_param zdet_param[] = { + {4, 0, 4, 0x08, 0x14, 0x18}, /* < 32ohm */ + {2, 0, 3, 0x18, 0x7C, 0x90}, /* 32ohm < Z < 400ohm */ + {1, 4, 5, 0x18, 0x7C, 0x90}, /* 400ohm < Z < 1200ohm */ + {1, 6, 7, 0x18, 0x7C, 0x90}, /* >1200ohm */ + }; + struct tavil_mbhc_zdet_param *zdet_param_ptr = NULL; + s16 d1_a[][4] = { + {0, 30, 90, 30}, + {0, 30, 30, 5}, + {0, 30, 30, 5}, + {0, 30, 30, 5}, + }; + s16 *d1 = NULL; + + WCD_MBHC_RSC_ASSERT_LOCKED(mbhc); + + reg0 = snd_soc_read(codec, WCD934X_ANA_MBHC_BTN5); + reg1 = snd_soc_read(codec, WCD934X_ANA_MBHC_BTN6); + reg2 = snd_soc_read(codec, WCD934X_ANA_MBHC_BTN7); + reg3 = snd_soc_read(codec, WCD934X_MBHC_CTL_CLK); + reg4 = snd_soc_read(codec, WCD934X_MBHC_NEW_ZDET_ANA_CTL); + + if (snd_soc_read(codec, WCD934X_ANA_MBHC_ELECT) & 0x80) { + is_fsm_disable = true; + regmap_update_bits(wcd9xxx->regmap, + WCD934X_ANA_MBHC_ELECT, 0x80, 0x00); + } + + /* For NO-jack, disable L_DET_EN before Z-det measurements */ + if (mbhc->hphl_swh) + regmap_update_bits(wcd9xxx->regmap, + WCD934X_ANA_MBHC_MECH, 0x80, 0x00); + + /* Turn off 100k pull down on HPHL */ + regmap_update_bits(wcd9xxx->regmap, + WCD934X_ANA_MBHC_MECH, 0x01, 0x00); + + /* First get impedance on Left */ + d1 = d1_a[1]; + zdet_param_ptr = &zdet_param[1]; + tavil_mbhc_zdet_ramp(codec, zdet_param_ptr, &z1L, NULL, d1); + + if (!TAVIL_MBHC_IS_SECOND_RAMP_REQUIRED(z1L)) + goto left_ch_impedance; + + /* Second ramp for left ch */ + if (z1L < TAVIL_ZDET_VAL_32) { + zdet_param_ptr = &zdet_param[0]; + d1 = d1_a[0]; + } else if ((z1L > TAVIL_ZDET_VAL_400) && (z1L <= TAVIL_ZDET_VAL_1200)) { + zdet_param_ptr = &zdet_param[2]; + d1 = d1_a[2]; + } else if (z1L > TAVIL_ZDET_VAL_1200) { + zdet_param_ptr = &zdet_param[3]; + d1 = d1_a[3]; + } + tavil_mbhc_zdet_ramp(codec, zdet_param_ptr, &z1L, NULL, d1); + +left_ch_impedance: + if ((z1L == TAVIL_ZDET_FLOATING_IMPEDANCE) || + (z1L > TAVIL_ZDET_VAL_100K)) { + *zl = TAVIL_ZDET_FLOATING_IMPEDANCE; + zdet_param_ptr = &zdet_param[1]; + d1 = d1_a[1]; + } else { + *zl = z1L/1000; + tavil_wcd_mbhc_qfuse_cal(codec, zl, 0); + } + dev_dbg(codec->dev, "%s: impedance on HPH_L = %d(ohms)\n", + __func__, *zl); + + /* Start of right impedance ramp and calculation */ + tavil_mbhc_zdet_ramp(codec, zdet_param_ptr, NULL, &z1R, d1); + if (TAVIL_MBHC_IS_SECOND_RAMP_REQUIRED(z1R)) { + if (((z1R > TAVIL_ZDET_VAL_1200) && + (zdet_param_ptr->noff == 0x6)) || + ((*zl) != TAVIL_ZDET_FLOATING_IMPEDANCE)) + goto right_ch_impedance; + /* Second ramp for right ch */ + if (z1R < TAVIL_ZDET_VAL_32) { + zdet_param_ptr = &zdet_param[0]; + d1 = d1_a[0]; + } else if ((z1R > TAVIL_ZDET_VAL_400) && + (z1R <= TAVIL_ZDET_VAL_1200)) { + zdet_param_ptr = &zdet_param[2]; + d1 = d1_a[2]; + } else if (z1R > TAVIL_ZDET_VAL_1200) { + zdet_param_ptr = &zdet_param[3]; + d1 = d1_a[3]; + } + tavil_mbhc_zdet_ramp(codec, zdet_param_ptr, NULL, &z1R, d1); + } +right_ch_impedance: + if ((z1R == TAVIL_ZDET_FLOATING_IMPEDANCE) || + (z1R > TAVIL_ZDET_VAL_100K)) { + *zr = TAVIL_ZDET_FLOATING_IMPEDANCE; + } else { + *zr = z1R/1000; + tavil_wcd_mbhc_qfuse_cal(codec, zr, 1); + } + dev_dbg(codec->dev, "%s: impedance on HPH_R = %d(ohms)\n", + __func__, *zr); + + /* Mono/stereo detection */ + if ((*zl == TAVIL_ZDET_FLOATING_IMPEDANCE) && + (*zr == TAVIL_ZDET_FLOATING_IMPEDANCE)) { + dev_dbg(codec->dev, + "%s: plug type is invalid or extension cable\n", + __func__); + goto zdet_complete; + } + if ((*zl == TAVIL_ZDET_FLOATING_IMPEDANCE) || + (*zr == TAVIL_ZDET_FLOATING_IMPEDANCE) || + ((*zl < WCD_MONO_HS_MIN_THR) && (*zr > WCD_MONO_HS_MIN_THR)) || + ((*zl > WCD_MONO_HS_MIN_THR) && (*zr < WCD_MONO_HS_MIN_THR))) { + dev_dbg(codec->dev, + "%s: Mono plug type with one ch floating or shorted to GND\n", + __func__); + mbhc->hph_type = WCD_MBHC_HPH_MONO; + goto zdet_complete; + } + snd_soc_update_bits(codec, WCD934X_HPH_R_ATEST, 0x02, 0x02); + snd_soc_update_bits(codec, WCD934X_HPH_PA_CTL2, 0x40, 0x01); + if (*zl < (TAVIL_ZDET_VAL_32/1000)) + tavil_mbhc_zdet_ramp(codec, &zdet_param[0], &z1Ls, NULL, d1); + else + tavil_mbhc_zdet_ramp(codec, &zdet_param[1], &z1Ls, NULL, d1); + snd_soc_update_bits(codec, WCD934X_HPH_PA_CTL2, 0x40, 0x00); + snd_soc_update_bits(codec, WCD934X_HPH_R_ATEST, 0x02, 0x00); + z1Ls /= 1000; + tavil_wcd_mbhc_qfuse_cal(codec, &z1Ls, 0); + /* Parallel of left Z and 9 ohm pull down resistor */ + zMono = ((*zl) * 9) / ((*zl) + 9); + z_diff1 = (z1Ls > zMono) ? (z1Ls - zMono) : (zMono - z1Ls); + z_diff2 = ((*zl) > z1Ls) ? ((*zl) - z1Ls) : (z1Ls - (*zl)); + if ((z_diff1 * (*zl + z1Ls)) > (z_diff2 * (z1Ls + zMono))) { + dev_dbg(codec->dev, "%s: stereo plug type detected\n", + __func__); + mbhc->hph_type = WCD_MBHC_HPH_STEREO; + } else { + dev_dbg(codec->dev, "%s: MONO plug type detected\n", + __func__); + mbhc->hph_type = WCD_MBHC_HPH_MONO; + } + +zdet_complete: + snd_soc_write(codec, WCD934X_ANA_MBHC_BTN5, reg0); + snd_soc_write(codec, WCD934X_ANA_MBHC_BTN6, reg1); + snd_soc_write(codec, WCD934X_ANA_MBHC_BTN7, reg2); + /* Turn on 100k pull down on HPHL */ + regmap_update_bits(wcd9xxx->regmap, + WCD934X_ANA_MBHC_MECH, 0x01, 0x01); + + /* For NO-jack, re-enable L_DET_EN after Z-det measurements */ + if (mbhc->hphl_swh) + regmap_update_bits(wcd9xxx->regmap, + WCD934X_ANA_MBHC_MECH, 0x80, 0x80); + + snd_soc_write(codec, WCD934X_MBHC_NEW_ZDET_ANA_CTL, reg4); + snd_soc_write(codec, WCD934X_MBHC_CTL_CLK, reg3); + if (is_fsm_disable) + regmap_update_bits(wcd9xxx->regmap, + WCD934X_ANA_MBHC_ELECT, 0x80, 0x80); +} + +static void tavil_mbhc_gnd_det_ctrl(struct snd_soc_codec *codec, bool enable) +{ + if (enable) { + snd_soc_update_bits(codec, WCD934X_ANA_MBHC_MECH, + 0x02, 0x02); + snd_soc_update_bits(codec, WCD934X_ANA_MBHC_MECH, + 0x40, 0x40); + } else { + snd_soc_update_bits(codec, WCD934X_ANA_MBHC_MECH, + 0x40, 0x00); + snd_soc_update_bits(codec, WCD934X_ANA_MBHC_MECH, + 0x02, 0x00); + } +} + +static void tavil_mbhc_hph_pull_down_ctrl(struct snd_soc_codec *codec, + bool enable) +{ + if (enable) { + snd_soc_update_bits(codec, WCD934X_HPH_PA_CTL2, + 0x40, 0x40); + snd_soc_update_bits(codec, WCD934X_HPH_PA_CTL2, + 0x10, 0x10); + } else { + snd_soc_update_bits(codec, WCD934X_HPH_PA_CTL2, + 0x40, 0x00); + snd_soc_update_bits(codec, WCD934X_HPH_PA_CTL2, + 0x10, 0x00); + } +} +static void tavil_mbhc_moisture_config(struct wcd_mbhc *mbhc) +{ + struct snd_soc_codec *codec = mbhc->codec; + + if ((mbhc->moist_rref == R_OFF) || + (mbhc->mbhc_cfg->enable_usbc_analog)) { + snd_soc_update_bits(codec, WCD934X_MBHC_NEW_CTL_2, + 0x0C, R_OFF << 2); + return; + } + + /* Donot enable moisture detection if jack type is NC */ + if (!mbhc->hphl_swh) { + dev_dbg(codec->dev, "%s: disable moisture detection for NC\n", + __func__); + snd_soc_update_bits(codec, WCD934X_MBHC_NEW_CTL_2, + 0x0C, R_OFF << 2); + return; + } + + snd_soc_update_bits(codec, WCD934X_MBHC_NEW_CTL_2, + 0x0C, mbhc->moist_rref << 2); +} + +static bool tavil_hph_register_recovery(struct wcd_mbhc *mbhc) +{ + struct snd_soc_codec *codec = mbhc->codec; + struct wcd934x_mbhc *wcd934x_mbhc = tavil_soc_get_mbhc(codec); + + if (!wcd934x_mbhc) + return false; + + wcd934x_mbhc->is_hph_recover = false; + snd_soc_dapm_force_enable_pin(snd_soc_codec_get_dapm(codec), + "RESET_HPH_REGISTERS"); + snd_soc_dapm_sync(snd_soc_codec_get_dapm(codec)); + + snd_soc_dapm_disable_pin(snd_soc_codec_get_dapm(codec), + "RESET_HPH_REGISTERS"); + snd_soc_dapm_sync(snd_soc_codec_get_dapm(codec)); + + return wcd934x_mbhc->is_hph_recover; +} + +static void tavil_update_anc_state(struct snd_soc_codec *codec, bool enable, + int anc_num) +{ + if (enable) + snd_soc_update_bits(codec, WCD934X_CDC_RX1_RX_PATH_CFG0 + + (20 * anc_num), 0x10, 0x10); + else + snd_soc_update_bits(codec, WCD934X_CDC_RX1_RX_PATH_CFG0 + + (20 * anc_num), 0x10, 0x00); +} + +static bool tavil_is_anc_on(struct wcd_mbhc *mbhc) +{ + bool anc_on = false; + u16 ancl, ancr; + + ancl = + (snd_soc_read(mbhc->codec, WCD934X_CDC_RX1_RX_PATH_CFG0)) & 0x10; + ancr = + (snd_soc_read(mbhc->codec, WCD934X_CDC_RX2_RX_PATH_CFG0)) & 0x10; + + anc_on = !!(ancl | ancr); + + return anc_on; +} + +static const struct wcd_mbhc_cb mbhc_cb = { + .request_irq = tavil_mbhc_request_irq, + .irq_control = tavil_mbhc_irq_control, + .free_irq = tavil_mbhc_free_irq, + .clk_setup = tavil_mbhc_clk_setup, + .map_btn_code_to_num = tavil_mbhc_btn_to_num, + .enable_mb_source = tavil_enable_ext_mb_source, + .mbhc_bias = tavil_mbhc_mbhc_bias_control, + .set_btn_thr = tavil_mbhc_program_btn_thr, + .lock_sleep = tavil_mbhc_lock_sleep, + .register_notifier = tavil_mbhc_register_notifier, + .micbias_enable_status = tavil_mbhc_micb_en_status, + .hph_pa_on_status = tavil_mbhc_hph_pa_on_status, + .hph_pull_up_control = tavil_mbhc_hph_l_pull_up_control, + .mbhc_micbias_control = tavil_mbhc_request_micbias, + .mbhc_micb_ramp_control = tavil_mbhc_micb_ramp_control, + .get_hwdep_fw_cal = tavil_get_hwdep_fw_cal, + .mbhc_micb_ctrl_thr_mic = tavil_mbhc_micb_ctrl_threshold_mic, + .compute_impedance = tavil_wcd_mbhc_calc_impedance, + .mbhc_gnd_det_ctrl = tavil_mbhc_gnd_det_ctrl, + .hph_pull_down_ctrl = tavil_mbhc_hph_pull_down_ctrl, + .mbhc_moisture_config = tavil_mbhc_moisture_config, + .hph_register_recovery = tavil_hph_register_recovery, + .update_anc_state = tavil_update_anc_state, + .is_anc_on = tavil_is_anc_on, +}; + +static struct regulator *tavil_codec_find_ondemand_regulator( + struct snd_soc_codec *codec, const char *name) +{ + int i; + struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent); + struct wcd9xxx_pdata *pdata = dev_get_platdata(codec->dev->parent); + + for (i = 0; i < wcd9xxx->num_of_supplies; ++i) { + if (pdata->regulator[i].ondemand && + wcd9xxx->supplies[i].supply && + !strcmp(wcd9xxx->supplies[i].supply, name)) + return wcd9xxx->supplies[i].consumer; + } + + dev_dbg(codec->dev, "Warning: regulator not found:%s\n", + name); + return NULL; +} + +static int tavil_get_hph_type(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct wcd934x_mbhc *wcd934x_mbhc = tavil_soc_get_mbhc(codec); + struct wcd_mbhc *mbhc; + + if (!wcd934x_mbhc) { + dev_err(codec->dev, "%s: mbhc not initialized!\n", __func__); + return -EINVAL; + } + + mbhc = &wcd934x_mbhc->wcd_mbhc; + + ucontrol->value.integer.value[0] = (u32) mbhc->hph_type; + dev_dbg(codec->dev, "%s: hph_type = %u\n", __func__, mbhc->hph_type); + + return 0; +} + +static int tavil_hph_impedance_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + uint32_t zl, zr; + bool hphr; + struct soc_multi_mixer_control *mc; + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct wcd934x_mbhc *wcd934x_mbhc = tavil_soc_get_mbhc(codec); + + if (!wcd934x_mbhc) { + dev_err(codec->dev, "%s: mbhc not initialized!\n", __func__); + return -EINVAL; + } + + mc = (struct soc_multi_mixer_control *)(kcontrol->private_value); + hphr = mc->shift; + wcd_mbhc_get_impedance(&wcd934x_mbhc->wcd_mbhc, &zl, &zr); + dev_dbg(codec->dev, "%s: zl=%u(ohms), zr=%u(ohms)\n", __func__, zl, zr); + ucontrol->value.integer.value[0] = hphr ? zr : zl; + + return 0; +} + +static const struct snd_kcontrol_new hph_type_detect_controls[] = { + SOC_SINGLE_EXT("HPH Type", 0, 0, UINT_MAX, 0, + tavil_get_hph_type, NULL), +}; + +static const struct snd_kcontrol_new impedance_detect_controls[] = { + SOC_SINGLE_EXT("HPHL Impedance", 0, 0, UINT_MAX, 0, + tavil_hph_impedance_get, NULL), + SOC_SINGLE_EXT("HPHR Impedance", 0, 1, UINT_MAX, 0, + tavil_hph_impedance_get, NULL), +}; + +/* + * tavil_mbhc_get_impedance: get impedance of headphone left and right channels + * @wcd934x_mbhc: handle to struct wcd934x_mbhc * + * @zl: handle to left-ch impedance + * @zr: handle to right-ch impedance + * return 0 for success or error code in case of failure + */ +int tavil_mbhc_get_impedance(struct wcd934x_mbhc *wcd934x_mbhc, + uint32_t *zl, uint32_t *zr) +{ + if (!wcd934x_mbhc) { + pr_err("%s: mbhc not initialized!\n", __func__); + return -EINVAL; + } + if (!zl || !zr) { + pr_err("%s: zl or zr null!\n", __func__); + return -EINVAL; + } + + return wcd_mbhc_get_impedance(&wcd934x_mbhc->wcd_mbhc, zl, zr); +} +EXPORT_SYMBOL(tavil_mbhc_get_impedance); + +/* + * tavil_mbhc_hs_detect: starts mbhc insertion/removal functionality + * @codec: handle to snd_soc_codec * + * @mbhc_cfg: handle to mbhc configuration structure + * return 0 if mbhc_start is success or error code in case of failure + */ +int tavil_mbhc_hs_detect(struct snd_soc_codec *codec, + struct wcd_mbhc_config *mbhc_cfg) +{ + struct wcd934x_mbhc *wcd934x_mbhc = tavil_soc_get_mbhc(codec); + + if (!wcd934x_mbhc) { + dev_err(codec->dev, "%s: mbhc not initialized!\n", __func__); + return -EINVAL; + } + + return wcd_mbhc_start(&wcd934x_mbhc->wcd_mbhc, mbhc_cfg); +} +EXPORT_SYMBOL(tavil_mbhc_hs_detect); + +/* + * tavil_mbhc_hs_detect_exit: stop mbhc insertion/removal functionality + * @codec: handle to snd_soc_codec * + */ +void tavil_mbhc_hs_detect_exit(struct snd_soc_codec *codec) +{ + struct wcd934x_mbhc *wcd934x_mbhc = tavil_soc_get_mbhc(codec); + + if (!wcd934x_mbhc) { + dev_err(codec->dev, "%s: mbhc not initialized!\n", __func__); + return; + } + wcd_mbhc_stop(&wcd934x_mbhc->wcd_mbhc); +} +EXPORT_SYMBOL(tavil_mbhc_hs_detect_exit); + +/* + * tavil_mbhc_post_ssr_init: initialize mbhc for tavil post subsystem restart + * @mbhc: poniter to wcd934x_mbhc structure + * @codec: handle to snd_soc_codec * + * + * return 0 if mbhc_init is success or error code in case of failure + */ +int tavil_mbhc_post_ssr_init(struct wcd934x_mbhc *mbhc, + struct snd_soc_codec *codec) +{ + int ret; + struct wcd_mbhc *wcd_mbhc; + + if (!mbhc || !codec) + return -EINVAL; + + wcd_mbhc = &mbhc->wcd_mbhc; + if (wcd_mbhc == NULL) { + pr_err("%s: wcd_mbhc is NULL\n", __func__); + return -EINVAL; + } + + tavil_mbhc_hs_detect_exit(codec); + wcd_mbhc_deinit(wcd_mbhc); + ret = wcd_mbhc_init(wcd_mbhc, codec, &mbhc_cb, &intr_ids, + wcd_mbhc_registers, TAVIL_ZDET_SUPPORTED); + if (ret) { + dev_err(codec->dev, "%s: mbhc initialization failed\n", + __func__); + goto done; + } + if (wcd_mbhc->mbhc_detection_logic == WCD_DETECTION_LEGACY) { + snd_soc_update_bits(codec, WCD934X_MBHC_NEW_CTL_1, 0x04, 0x04); + snd_soc_update_bits(codec, WCD934X_MBHC_CTL_BCS, 0x01, 0x01); + } + +done: + return ret; +} +EXPORT_SYMBOL(tavil_mbhc_post_ssr_init); + +/* + * tavil_mbhc_init: initialize mbhc for tavil + * @mbhc: poniter to wcd934x_mbhc struct pointer to store the configs + * @codec: handle to snd_soc_codec * + * @fw_data: handle to firmware data + * + * return 0 if mbhc_init is success or error code in case of failure + */ +int tavil_mbhc_init(struct wcd934x_mbhc **mbhc, struct snd_soc_codec *codec, + struct fw_info *fw_data) +{ + struct regulator *supply; + struct wcd934x_mbhc *wcd934x_mbhc; + struct wcd_mbhc *wcd_mbhc; + int ret; + + wcd934x_mbhc = devm_kzalloc(codec->dev, sizeof(struct wcd934x_mbhc), + GFP_KERNEL); + if (!wcd934x_mbhc) + return -ENOMEM; + + wcd934x_mbhc->wcd9xxx = dev_get_drvdata(codec->dev->parent); + wcd934x_mbhc->fw_data = fw_data; + BLOCKING_INIT_NOTIFIER_HEAD(&wcd934x_mbhc->notifier); + wcd_mbhc = &wcd934x_mbhc->wcd_mbhc; + if (wcd_mbhc == NULL) { + pr_err("%s: wcd_mbhc is NULL\n", __func__); + ret = -EINVAL; + goto err; + } + + + /* Setting default mbhc detection logic to ADC for Tavil */ + wcd_mbhc->mbhc_detection_logic = WCD_DETECTION_ADC; + + ret = wcd_mbhc_init(wcd_mbhc, codec, &mbhc_cb, + &intr_ids, wcd_mbhc_registers, + TAVIL_ZDET_SUPPORTED); + if (ret) { + dev_err(codec->dev, "%s: mbhc initialization failed\n", + __func__); + goto err; + } + + supply = tavil_codec_find_ondemand_regulator(codec, + on_demand_supply_name[WCD934X_ON_DEMAND_MICBIAS]); + if (supply) { + wcd934x_mbhc->on_demand_list[ + WCD934X_ON_DEMAND_MICBIAS].supply = + supply; + wcd934x_mbhc->on_demand_list[ + WCD934X_ON_DEMAND_MICBIAS].ondemand_supply_count = + 0; + } + + (*mbhc) = wcd934x_mbhc; + snd_soc_add_codec_controls(codec, impedance_detect_controls, + ARRAY_SIZE(impedance_detect_controls)); + snd_soc_add_codec_controls(codec, hph_type_detect_controls, + ARRAY_SIZE(hph_type_detect_controls)); + + if (wcd_mbhc->mbhc_detection_logic == WCD_DETECTION_LEGACY) { + snd_soc_update_bits(codec, WCD934X_MBHC_NEW_CTL_1, 0x04, 0x04); + snd_soc_update_bits(codec, WCD934X_MBHC_CTL_BCS, 0x01, 0x01); + } + + return 0; +err: + devm_kfree(codec->dev, wcd934x_mbhc); + return ret; +} +EXPORT_SYMBOL(tavil_mbhc_init); + +/* + * tavil_mbhc_deinit: deinitialize mbhc for tavil + * @codec: handle to snd_soc_codec * + */ +void tavil_mbhc_deinit(struct snd_soc_codec *codec) +{ + struct wcd934x_mbhc *wcd934x_mbhc = tavil_soc_get_mbhc(codec); + + if (wcd934x_mbhc) { + wcd_mbhc_deinit(&wcd934x_mbhc->wcd_mbhc); + devm_kfree(codec->dev, wcd934x_mbhc); + } +} +EXPORT_SYMBOL(tavil_mbhc_deinit); diff --git a/audio-kernel/asoc/codecs/wcd934x/wcd934x-mbhc.h b/audio-kernel/asoc/codecs/wcd934x/wcd934x-mbhc.h new file mode 100644 index 000000000..53c886da0 --- /dev/null +++ b/audio-kernel/asoc/codecs/wcd934x/wcd934x-mbhc.h @@ -0,0 +1,84 @@ +/* + * 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 + * 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 __WCD934X_MBHC_H__ +#define __WCD934X_MBHC_H__ +#include "../wcd-mbhc-v2.h" + +enum wcd934x_on_demand_supply_name { + WCD934X_ON_DEMAND_MICBIAS = 0, + WCD934X_ON_DEMAND_SUPPLIES_MAX, +}; + +struct wcd934x_on_demand_supply { + struct regulator *supply; + int ondemand_supply_count; +}; + +struct wcd934x_mbhc { + struct wcd_mbhc wcd_mbhc; + struct blocking_notifier_head notifier; + struct wcd934x_on_demand_supply on_demand_list[ + WCD934X_ON_DEMAND_SUPPLIES_MAX]; + struct wcd9xxx *wcd9xxx; + struct fw_info *fw_data; + bool mbhc_started; + bool is_hph_recover; +}; + +#if IS_ENABLED(CONFIG_SND_SOC_WCD934X_MBHC) +extern int tavil_mbhc_init(struct wcd934x_mbhc **mbhc, + struct snd_soc_codec *codec, + struct fw_info *fw_data); +extern void tavil_mbhc_hs_detect_exit(struct snd_soc_codec *codec); +extern int tavil_mbhc_hs_detect(struct snd_soc_codec *codec, + struct wcd_mbhc_config *mbhc_cfg); +extern void tavil_mbhc_deinit(struct snd_soc_codec *codec); +extern int tavil_mbhc_post_ssr_init(struct wcd934x_mbhc *mbhc, + struct snd_soc_codec *codec); +extern int tavil_mbhc_get_impedance(struct wcd934x_mbhc *wcd934x_mbhc, + uint32_t *zl, uint32_t *zr); +#else +static inline int tavil_mbhc_init(struct wcd934x_mbhc **mbhc, + struct snd_soc_codec *codec, + struct fw_info *fw_data) +{ + return 0; +} +static inline void tavil_mbhc_hs_detect_exit(struct snd_soc_codec *codec) +{ +} +static inline int tavil_mbhc_hs_detect(struct snd_soc_codec *codec, + struct wcd_mbhc_config *mbhc_cfg) +{ + return 0; +} +static inline void tavil_mbhc_deinit(struct snd_soc_codec *codec) +{ +} +static inline int tavil_mbhc_post_ssr_init(struct wcd934x_mbhc *mbhc, + struct snd_soc_codec *codec) +{ + return 0; +} +static inline int tavil_mbhc_get_impedance(struct wcd934x_mbhc *wcd934x_mbhc, + uint32_t *zl, uint32_t *zr) +{ + if (zl) + *zl = 0; + if (zr) + *zr = 0; + return -EINVAL; +} +#endif + +#endif /* __WCD934X_MBHC_H__ */ diff --git a/audio-kernel/asoc/codecs/wcd934x/wcd934x-regmap.c b/audio-kernel/asoc/codecs/wcd934x/wcd934x-regmap.c new file mode 100644 index 000000000..e62b69c69 --- /dev/null +++ b/audio-kernel/asoc/codecs/wcd934x/wcd934x-regmap.c @@ -0,0 +1,1955 @@ +/* + * Copyright (c) 2016-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 +#include +#include +#include "../core.h" +#include "../wcd9xxx-regmap.h" + + +static const struct reg_sequence wcd934x_1_1_defaults[] = { + { WCD934X_CHIP_TIER_CTRL_CHIP_ID_BYTE0, 0x01 }, + { WCD934X_BIAS_VBG_FINE_ADJ, 0x75 }, + { WCD934X_HPH_REFBUFF_LP_CTL, 0x0E }, + { WCD934X_EAR_DAC_CTL_ATEST, 0x08 }, + { WCD934X_SIDO_NEW_VOUT_A_STARTUP, 0x17 }, + { WCD934X_HPH_NEW_INT_RDAC_GAIN_CTL, 0x40 }, + { WCD934X_HPH_NEW_INT_RDAC_HD2_CTL_L, 0x81 }, + { WCD934X_HPH_NEW_INT_RDAC_HD2_CTL_R, 0x81 }, +}; + +static const struct reg_default wcd934x_defaults[] = { + { WCD934X_PAGE0_PAGE_REGISTER, 0x00 }, + { WCD934X_CODEC_RPM_CLK_BYPASS, 0x00 }, + { WCD934X_CODEC_RPM_CLK_GATE, 0x1f }, + { WCD934X_CODEC_RPM_CLK_MCLK_CFG, 0x00 }, + { WCD934X_CODEC_RPM_CLK_MCLK2_CFG, 0x02 }, + { WCD934X_CODEC_RPM_I2S_DSD_CLK_SEL, 0x00 }, + { WCD934X_CODEC_RPM_RST_CTL, 0x00 }, + { WCD934X_CODEC_RPM_PWR_CDC_DIG_HM_CTL, 0x04 }, + { WCD934X_CHIP_TIER_CTRL_CHIP_ID_BYTE0, 0x00 }, + { WCD934X_CHIP_TIER_CTRL_CHIP_ID_BYTE1, 0x00 }, + { WCD934X_CHIP_TIER_CTRL_CHIP_ID_BYTE2, 0x08 }, + { WCD934X_CHIP_TIER_CTRL_CHIP_ID_BYTE3, 0x01 }, + { WCD934X_CHIP_TIER_CTRL_EFUSE_CTL, 0x10 }, + { WCD934X_CHIP_TIER_CTRL_EFUSE_TEST0, 0x00 }, + { WCD934X_CHIP_TIER_CTRL_EFUSE_TEST1, 0x00 }, + { WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT0, 0x00 }, + { WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT1, 0x00 }, + { WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT2, 0x00 }, + { WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT3, 0x00 }, + { WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT4, 0x00 }, + { WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT5, 0x00 }, + { WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT6, 0x00 }, + { WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT7, 0x00 }, + { WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT8, 0x00 }, + { WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT9, 0x00 }, + { WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT10, 0x00 }, + { WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT11, 0x00 }, + { WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT12, 0x00 }, + { WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT13, 0x00 }, + { WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT14, 0x00 }, + { WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT15, 0x00 }, + { WCD934X_CHIP_TIER_CTRL_EFUSE_STATUS, 0x00 }, + { WCD934X_CHIP_TIER_CTRL_I2C_SLAVE_ID_NONNEGO, 0x0d }, + { WCD934X_CHIP_TIER_CTRL_I2C_SLAVE_ID_1, 0x00 }, + { WCD934X_CHIP_TIER_CTRL_I2C_SLAVE_ID_2, 0x00 }, + { WCD934X_CHIP_TIER_CTRL_I2C_SLAVE_ID_3, 0x00 }, + { WCD934X_CHIP_TIER_CTRL_ANA_WAIT_STATE_CTL, 0xcc }, + { WCD934X_CHIP_TIER_CTRL_SLNQ_WAIT_STATE_CTL, 0xcc }, + { WCD934X_CHIP_TIER_CTRL_I2C_ACTIVE, 0x00 }, + { WCD934X_CHIP_TIER_CTRL_ALT_FUNC_EN, 0x00 }, + { WCD934X_CHIP_TIER_CTRL_GPIO_CTL_OE, 0x00 }, + { WCD934X_CHIP_TIER_CTRL_GPIO_CTL_DATA, 0x00 }, + { WCD934X_DATA_HUB_RX0_CFG, 0x00 }, + { WCD934X_DATA_HUB_RX1_CFG, 0x00 }, + { WCD934X_DATA_HUB_RX2_CFG, 0x00 }, + { WCD934X_DATA_HUB_RX3_CFG, 0x00 }, + { WCD934X_DATA_HUB_RX4_CFG, 0x00 }, + { WCD934X_DATA_HUB_RX5_CFG, 0x00 }, + { WCD934X_DATA_HUB_RX6_CFG, 0x00 }, + { WCD934X_DATA_HUB_RX7_CFG, 0x00 }, + { WCD934X_DATA_HUB_SB_TX0_INP_CFG, 0x00 }, + { WCD934X_DATA_HUB_SB_TX1_INP_CFG, 0x00 }, + { WCD934X_DATA_HUB_SB_TX2_INP_CFG, 0x00 }, + { WCD934X_DATA_HUB_SB_TX3_INP_CFG, 0x00 }, + { WCD934X_DATA_HUB_SB_TX4_INP_CFG, 0x00 }, + { WCD934X_DATA_HUB_SB_TX5_INP_CFG, 0x00 }, + { WCD934X_DATA_HUB_SB_TX6_INP_CFG, 0x00 }, + { WCD934X_DATA_HUB_SB_TX7_INP_CFG, 0x00 }, + { WCD934X_DATA_HUB_SB_TX8_INP_CFG, 0x00 }, + { WCD934X_DATA_HUB_SB_TX9_INP_CFG, 0x00 }, + { WCD934X_DATA_HUB_SB_TX10_INP_CFG, 0x00 }, + { WCD934X_DATA_HUB_SB_TX11_INP_CFG, 0x00 }, + { WCD934X_DATA_HUB_SB_TX13_INP_CFG, 0x00 }, + { WCD934X_DATA_HUB_SB_TX14_INP_CFG, 0x00 }, + { WCD934X_DATA_HUB_SB_TX15_INP_CFG, 0x00 }, + { WCD934X_DATA_HUB_I2S_TX0_CFG, 0x00 }, + { WCD934X_DATA_HUB_I2S_TX1_0_CFG, 0x00 }, + { WCD934X_DATA_HUB_I2S_TX1_1_CFG, 0x00 }, + { WCD934X_DATA_HUB_I2S_0_CTL, 0x0c }, + { WCD934X_DATA_HUB_I2S_1_CTL, 0x0c }, + { WCD934X_DATA_HUB_I2S_2_CTL, 0x0c }, + { WCD934X_DATA_HUB_I2S_3_CTL, 0x0c }, + { WCD934X_DATA_HUB_I2S_CLKSRC_CTL, 0x00 }, + { WCD934X_DATA_HUB_I2S_COMMON_CTL, 0x00 }, + { WCD934X_DATA_HUB_I2S_0_TDM_CTL, 0x00 }, + { WCD934X_DATA_HUB_I2S_STATUS, 0x00 }, + { WCD934X_DMA_RDMA_CTL_0, 0x00 }, + { WCD934X_DMA_CH_2_3_CFG_RDMA_0, 0xff }, + { WCD934X_DMA_CH_0_1_CFG_RDMA_0, 0xff }, + { WCD934X_DMA_RDMA_CTL_1, 0x00 }, + { WCD934X_DMA_CH_2_3_CFG_RDMA_1, 0xff }, + { WCD934X_DMA_CH_0_1_CFG_RDMA_1, 0xff }, + { WCD934X_DMA_RDMA_CTL_2, 0x00 }, + { WCD934X_DMA_CH_2_3_CFG_RDMA_2, 0xff }, + { WCD934X_DMA_CH_0_1_CFG_RDMA_2, 0xff }, + { WCD934X_DMA_RDMA_CTL_3, 0x00 }, + { WCD934X_DMA_CH_2_3_CFG_RDMA_3, 0xff }, + { WCD934X_DMA_CH_0_1_CFG_RDMA_3, 0xff }, + { WCD934X_DMA_RDMA_CTL_4, 0x00 }, + { WCD934X_DMA_CH_2_3_CFG_RDMA_4, 0xff }, + { WCD934X_DMA_CH_0_1_CFG_RDMA_4, 0xff }, + { WCD934X_DMA_RDMA4_PRT_CFG, 0x00 }, + { WCD934X_DMA_RDMA_SBTX0_7_CFG, 0x00 }, + { WCD934X_DMA_RDMA_SBTX8_11_CFG, 0x00 }, + { WCD934X_DMA_WDMA_CTL_0, 0x00 }, + { WCD934X_DMA_CH_4_5_CFG_WDMA_0, 0x00 }, + { WCD934X_DMA_CH_2_3_CFG_WDMA_0, 0x00 }, + { WCD934X_DMA_CH_0_1_CFG_WDMA_0, 0x00 }, + { WCD934X_DMA_WDMA_CTL_1, 0x00 }, + { WCD934X_DMA_CH_4_5_CFG_WDMA_1, 0x00 }, + { WCD934X_DMA_CH_2_3_CFG_WDMA_1, 0x00 }, + { WCD934X_DMA_CH_0_1_CFG_WDMA_1, 0x00 }, + { WCD934X_DMA_WDMA_CTL_2, 0x00 }, + { WCD934X_DMA_CH_4_5_CFG_WDMA_2, 0x00 }, + { WCD934X_DMA_CH_2_3_CFG_WDMA_2, 0x00 }, + { WCD934X_DMA_CH_0_1_CFG_WDMA_2, 0x00 }, + { WCD934X_DMA_WDMA_CTL_3, 0x00 }, + { WCD934X_DMA_CH_4_5_CFG_WDMA_3, 0x00 }, + { WCD934X_DMA_CH_2_3_CFG_WDMA_3, 0x00 }, + { WCD934X_DMA_CH_0_1_CFG_WDMA_3, 0x00 }, + { WCD934X_DMA_WDMA_CTL_4, 0x00 }, + { WCD934X_DMA_CH_4_5_CFG_WDMA_4, 0x00 }, + { WCD934X_DMA_CH_2_3_CFG_WDMA_4, 0x00 }, + { WCD934X_DMA_CH_0_1_CFG_WDMA_4, 0x00 }, + { WCD934X_DMA_WDMA0_PRT_CFG, 0x00 }, + { WCD934X_DMA_WDMA3_PRT_CFG, 0x00 }, + { WCD934X_DMA_WDMA4_PRT0_3_CFG, 0x00 }, + { WCD934X_DMA_WDMA4_PRT4_7_CFG, 0x00 }, + { WCD934X_PAGE1_PAGE_REGISTER, 0x00 }, + { WCD934X_CPE_FLL_USER_CTL_0, 0x71 }, + { WCD934X_CPE_FLL_USER_CTL_1, 0x34 }, + { WCD934X_CPE_FLL_USER_CTL_2, 0x0b }, + { WCD934X_CPE_FLL_USER_CTL_3, 0x02 }, + { WCD934X_CPE_FLL_USER_CTL_4, 0x04 }, + { WCD934X_CPE_FLL_USER_CTL_5, 0x02 }, + { WCD934X_CPE_FLL_USER_CTL_6, 0x6e }, + { WCD934X_CPE_FLL_USER_CTL_7, 0x00 }, + { WCD934X_CPE_FLL_USER_CTL_8, 0x94 }, + { WCD934X_CPE_FLL_USER_CTL_9, 0x50 }, + { WCD934X_CPE_FLL_L_VAL_CTL_0, 0x53 }, + { WCD934X_CPE_FLL_L_VAL_CTL_1, 0x00 }, + { WCD934X_CPE_FLL_DSM_FRAC_CTL_0, 0x00 }, + { WCD934X_CPE_FLL_DSM_FRAC_CTL_1, 0xff }, + { WCD934X_CPE_FLL_CONFIG_CTL_0, 0x6b }, + { WCD934X_CPE_FLL_CONFIG_CTL_1, 0x05 }, + { WCD934X_CPE_FLL_CONFIG_CTL_2, 0x08 }, + { WCD934X_CPE_FLL_CONFIG_CTL_3, 0x00 }, + { WCD934X_CPE_FLL_CONFIG_CTL_4, 0x10 }, + { WCD934X_CPE_FLL_TEST_CTL_0, 0x80 }, + { WCD934X_CPE_FLL_TEST_CTL_1, 0x00 }, + { WCD934X_CPE_FLL_TEST_CTL_2, 0x00 }, + { WCD934X_CPE_FLL_TEST_CTL_3, 0x00 }, + { WCD934X_CPE_FLL_TEST_CTL_4, 0x00 }, + { WCD934X_CPE_FLL_TEST_CTL_5, 0x00 }, + { WCD934X_CPE_FLL_TEST_CTL_6, 0x00 }, + { WCD934X_CPE_FLL_TEST_CTL_7, 0x33 }, + { WCD934X_CPE_FLL_FREQ_CTL_0, 0x00 }, + { WCD934X_CPE_FLL_FREQ_CTL_1, 0x00 }, + { WCD934X_CPE_FLL_FREQ_CTL_2, 0x00 }, + { WCD934X_CPE_FLL_FREQ_CTL_3, 0x00 }, + { WCD934X_CPE_FLL_SSC_CTL_0, 0x00 }, + { WCD934X_CPE_FLL_SSC_CTL_1, 0x00 }, + { WCD934X_CPE_FLL_SSC_CTL_2, 0x00 }, + { WCD934X_CPE_FLL_SSC_CTL_3, 0x00 }, + { WCD934X_CPE_FLL_FLL_MODE, 0x20 }, + { WCD934X_CPE_FLL_STATUS_0, 0x00 }, + { WCD934X_CPE_FLL_STATUS_1, 0x00 }, + { WCD934X_CPE_FLL_STATUS_2, 0x00 }, + { WCD934X_CPE_FLL_STATUS_3, 0x00 }, + { WCD934X_I2S_FLL_USER_CTL_0, 0x41 }, + { WCD934X_I2S_FLL_USER_CTL_1, 0x94 }, + { WCD934X_I2S_FLL_USER_CTL_2, 0x08 }, + { WCD934X_I2S_FLL_USER_CTL_3, 0x02 }, + { WCD934X_I2S_FLL_USER_CTL_4, 0x04 }, + { WCD934X_I2S_FLL_USER_CTL_5, 0x02 }, + { WCD934X_I2S_FLL_USER_CTL_6, 0x40 }, + { WCD934X_I2S_FLL_USER_CTL_7, 0x00 }, + { WCD934X_I2S_FLL_USER_CTL_8, 0x5f }, + { WCD934X_I2S_FLL_USER_CTL_9, 0x02 }, + { WCD934X_I2S_FLL_L_VAL_CTL_0, 0x40 }, + { WCD934X_I2S_FLL_L_VAL_CTL_1, 0x00 }, + { WCD934X_I2S_FLL_DSM_FRAC_CTL_0, 0x00 }, + { WCD934X_I2S_FLL_DSM_FRAC_CTL_1, 0xff }, + { WCD934X_I2S_FLL_CONFIG_CTL_0, 0x6b }, + { WCD934X_I2S_FLL_CONFIG_CTL_1, 0x05 }, + { WCD934X_I2S_FLL_CONFIG_CTL_2, 0x08 }, + { WCD934X_I2S_FLL_CONFIG_CTL_3, 0x00 }, + { WCD934X_I2S_FLL_CONFIG_CTL_4, 0x30 }, + { WCD934X_I2S_FLL_TEST_CTL_0, 0x80 }, + { WCD934X_I2S_FLL_TEST_CTL_1, 0x00 }, + { WCD934X_I2S_FLL_TEST_CTL_2, 0x00 }, + { WCD934X_I2S_FLL_TEST_CTL_3, 0x00 }, + { WCD934X_I2S_FLL_TEST_CTL_4, 0x00 }, + { WCD934X_I2S_FLL_TEST_CTL_5, 0x00 }, + { WCD934X_I2S_FLL_TEST_CTL_6, 0x00 }, + { WCD934X_I2S_FLL_TEST_CTL_7, 0xff }, + { WCD934X_I2S_FLL_FREQ_CTL_0, 0x00 }, + { WCD934X_I2S_FLL_FREQ_CTL_1, 0x00 }, + { WCD934X_I2S_FLL_FREQ_CTL_2, 0x00 }, + { WCD934X_I2S_FLL_FREQ_CTL_3, 0x00 }, + { WCD934X_I2S_FLL_SSC_CTL_0, 0x00 }, + { WCD934X_I2S_FLL_SSC_CTL_1, 0x00 }, + { WCD934X_I2S_FLL_SSC_CTL_2, 0x00 }, + { WCD934X_I2S_FLL_SSC_CTL_3, 0x00 }, + { WCD934X_I2S_FLL_FLL_MODE, 0x00 }, + { WCD934X_I2S_FLL_STATUS_0, 0x00 }, + { WCD934X_I2S_FLL_STATUS_1, 0x00 }, + { WCD934X_I2S_FLL_STATUS_2, 0x00 }, + { WCD934X_I2S_FLL_STATUS_3, 0x00 }, + { WCD934X_SB_FLL_USER_CTL_0, 0x41 }, + { WCD934X_SB_FLL_USER_CTL_1, 0x94 }, + { WCD934X_SB_FLL_USER_CTL_2, 0x08 }, + { WCD934X_SB_FLL_USER_CTL_3, 0x02 }, + { WCD934X_SB_FLL_USER_CTL_4, 0x04 }, + { WCD934X_SB_FLL_USER_CTL_5, 0x02 }, + { WCD934X_SB_FLL_USER_CTL_6, 0x40 }, + { WCD934X_SB_FLL_USER_CTL_7, 0x00 }, + { WCD934X_SB_FLL_USER_CTL_8, 0x5e }, + { WCD934X_SB_FLL_USER_CTL_9, 0x01 }, + { WCD934X_SB_FLL_L_VAL_CTL_0, 0x40 }, + { WCD934X_SB_FLL_L_VAL_CTL_1, 0x00 }, + { WCD934X_SB_FLL_DSM_FRAC_CTL_0, 0x00 }, + { WCD934X_SB_FLL_DSM_FRAC_CTL_1, 0xff }, + { WCD934X_SB_FLL_CONFIG_CTL_0, 0x6b }, + { WCD934X_SB_FLL_CONFIG_CTL_1, 0x05 }, + { WCD934X_SB_FLL_CONFIG_CTL_2, 0x08 }, + { WCD934X_SB_FLL_CONFIG_CTL_3, 0x00 }, + { WCD934X_SB_FLL_CONFIG_CTL_4, 0x10 }, + { WCD934X_SB_FLL_TEST_CTL_0, 0x00 }, + { WCD934X_SB_FLL_TEST_CTL_1, 0x00 }, + { WCD934X_SB_FLL_TEST_CTL_2, 0x00 }, + { WCD934X_SB_FLL_TEST_CTL_3, 0x00 }, + { WCD934X_SB_FLL_TEST_CTL_4, 0x00 }, + { WCD934X_SB_FLL_TEST_CTL_5, 0x00 }, + { WCD934X_SB_FLL_TEST_CTL_6, 0x00 }, + { WCD934X_SB_FLL_TEST_CTL_7, 0xff }, + { WCD934X_SB_FLL_FREQ_CTL_0, 0x00 }, + { WCD934X_SB_FLL_FREQ_CTL_1, 0x00 }, + { WCD934X_SB_FLL_FREQ_CTL_2, 0x00 }, + { WCD934X_SB_FLL_FREQ_CTL_3, 0x00 }, + { WCD934X_SB_FLL_SSC_CTL_0, 0x00 }, + { WCD934X_SB_FLL_SSC_CTL_1, 0x00 }, + { WCD934X_SB_FLL_SSC_CTL_2, 0x00 }, + { WCD934X_SB_FLL_SSC_CTL_3, 0x00 }, + { WCD934X_SB_FLL_FLL_MODE, 0x00 }, + { WCD934X_SB_FLL_STATUS_0, 0x00 }, + { WCD934X_SB_FLL_STATUS_1, 0x00 }, + { WCD934X_SB_FLL_STATUS_2, 0x00 }, + { WCD934X_SB_FLL_STATUS_3, 0x00 }, + { WCD934X_PAGE2_PAGE_REGISTER, 0x00 }, + { WCD934X_CPE_SS_CPE_CTL, 0x05 }, + { WCD934X_CPE_SS_PWR_SYS_PSTATE_CTL_0, 0x01 }, + { WCD934X_CPE_SS_PWR_SYS_PSTATE_CTL_1, 0x00 }, + { WCD934X_CPE_SS_PWR_CPEFLL_CTL, 0x02 }, + { WCD934X_CPE_SS_PWR_CPE_SYSMEM_DEEPSLP_0, 0xff }, + { WCD934X_CPE_SS_PWR_CPE_SYSMEM_DEEPSLP_1, 0x0f }, + { WCD934X_CPE_SS_PWR_CPE_SYSMEM_DEEPSLP_OVERRIDE, 0x00 }, + { WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_0, 0xff }, + { WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_1, 0xff }, + { WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_2, 0xff }, + { WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_3, 0xff }, + { WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_4, 0xff }, + { WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_5, 0xff }, + { WCD934X_CPE_SS_PWR_CPE_DRAM1_SHUTDOWN, 0x07 }, + { WCD934X_CPE_SS_SOC_SW_COLLAPSE_CTL, 0x00 }, + { WCD934X_CPE_SS_SOC_SW_COLLAPSE_OVERRIDE_CTL, 0x20 }, + { WCD934X_CPE_SS_SOC_SW_COLLAPSE_OVERRIDE_CTL1, 0x00 }, + { WCD934X_CPE_SS_US_BUF_INT_PERIOD, 0x60 }, + { WCD934X_CPE_SS_CPARMAD_BUFRDY_INT_PERIOD, 0x13 }, + { WCD934X_CPE_SS_SVA_CFG, 0x41 }, + { WCD934X_CPE_SS_US_CFG, 0x00 }, + { WCD934X_CPE_SS_MAD_CTL, 0x00 }, + { WCD934X_CPE_SS_CPAR_CTL, 0x00 }, + { WCD934X_CPE_SS_DMIC0_CTL, 0x00 }, + { WCD934X_CPE_SS_DMIC1_CTL, 0x00 }, + { WCD934X_CPE_SS_DMIC2_CTL, 0x00 }, + { WCD934X_CPE_SS_DMIC_CFG, 0x80 }, + { WCD934X_CPE_SS_CPAR_CFG, 0x00 }, + { WCD934X_CPE_SS_WDOG_CFG, 0x01 }, + { WCD934X_CPE_SS_BACKUP_INT, 0x00 }, + { WCD934X_CPE_SS_STATUS, 0x00 }, + { WCD934X_CPE_SS_CPE_OCD_CFG, 0x00 }, + { WCD934X_CPE_SS_SS_ERROR_INT_MASK_0A, 0xff }, + { WCD934X_CPE_SS_SS_ERROR_INT_MASK_0B, 0x3f }, + { WCD934X_CPE_SS_SS_ERROR_INT_MASK_1A, 0xff }, + { WCD934X_CPE_SS_SS_ERROR_INT_MASK_1B, 0x3f }, + { WCD934X_CPE_SS_SS_ERROR_INT_STATUS_0A, 0x00 }, + { WCD934X_CPE_SS_SS_ERROR_INT_STATUS_0B, 0x00 }, + { WCD934X_CPE_SS_SS_ERROR_INT_STATUS_1A, 0x00 }, + { WCD934X_CPE_SS_SS_ERROR_INT_STATUS_1B, 0x00 }, + { WCD934X_CPE_SS_SS_ERROR_INT_CLEAR_0A, 0x00 }, + { WCD934X_CPE_SS_SS_ERROR_INT_CLEAR_0B, 0x00 }, + { WCD934X_CPE_SS_SS_ERROR_INT_CLEAR_1A, 0x00 }, + { WCD934X_CPE_SS_SS_ERROR_INT_CLEAR_1B, 0x00 }, + { WCD934X_SOC_MAD_MAIN_CTL_1, 0x00 }, + { WCD934X_SOC_MAD_MAIN_CTL_2, 0x00 }, + { WCD934X_SOC_MAD_AUDIO_CTL_1, 0x00 }, + { WCD934X_SOC_MAD_AUDIO_CTL_2, 0x00 }, + { WCD934X_SOC_MAD_AUDIO_CTL_3, 0x00 }, + { WCD934X_SOC_MAD_AUDIO_CTL_4, 0x00 }, + { WCD934X_SOC_MAD_AUDIO_CTL_5, 0x00 }, + { WCD934X_SOC_MAD_AUDIO_CTL_6, 0x00 }, + { WCD934X_SOC_MAD_AUDIO_CTL_7, 0x00 }, + { WCD934X_SOC_MAD_AUDIO_CTL_8, 0x00 }, + { WCD934X_SOC_MAD_AUDIO_IIR_CTL_PTR, 0x00 }, + { WCD934X_SOC_MAD_AUDIO_IIR_CTL_VAL, 0x40 }, + { WCD934X_SOC_MAD_ULTR_CTL_1, 0x00 }, + { WCD934X_SOC_MAD_ULTR_CTL_2, 0x00 }, + { WCD934X_SOC_MAD_ULTR_CTL_3, 0x00 }, + { WCD934X_SOC_MAD_ULTR_CTL_4, 0x00 }, + { WCD934X_SOC_MAD_ULTR_CTL_5, 0x00 }, + { WCD934X_SOC_MAD_ULTR_CTL_6, 0x00 }, + { WCD934X_SOC_MAD_ULTR_CTL_7, 0x00 }, + { WCD934X_SOC_MAD_BEACON_CTL_1, 0x00 }, + { WCD934X_SOC_MAD_BEACON_CTL_2, 0x00 }, + { WCD934X_SOC_MAD_BEACON_CTL_3, 0x00 }, + { WCD934X_SOC_MAD_BEACON_CTL_4, 0x00 }, + { WCD934X_SOC_MAD_BEACON_CTL_5, 0x00 }, + { WCD934X_SOC_MAD_BEACON_CTL_6, 0x00 }, + { WCD934X_SOC_MAD_BEACON_CTL_7, 0x00 }, + { WCD934X_SOC_MAD_BEACON_CTL_8, 0x00 }, + { WCD934X_SOC_MAD_BEACON_IIR_CTL_PTR, 0x00 }, + { WCD934X_SOC_MAD_BEACON_IIR_CTL_VAL, 0x00 }, + { WCD934X_SOC_MAD_INP_SEL, 0x00 }, + { WCD934X_PAGE4_PAGE_REGISTER, 0x00 }, + { WCD934X_INTR_CFG, 0x00 }, + { WCD934X_INTR_CLR_COMMIT, 0x00 }, + { WCD934X_INTR_PIN1_MASK0, 0xff }, + { WCD934X_INTR_PIN1_MASK1, 0xff }, + { WCD934X_INTR_PIN1_MASK2, 0xff }, + { WCD934X_INTR_PIN1_MASK3, 0xff }, + { WCD934X_INTR_PIN1_STATUS0, 0x00 }, + { WCD934X_INTR_PIN1_STATUS1, 0x00 }, + { WCD934X_INTR_PIN1_STATUS2, 0x00 }, + { WCD934X_INTR_PIN1_STATUS3, 0x00 }, + { WCD934X_INTR_PIN1_CLEAR0, 0x00 }, + { WCD934X_INTR_PIN1_CLEAR1, 0x00 }, + { WCD934X_INTR_PIN1_CLEAR2, 0x00 }, + { WCD934X_INTR_PIN1_CLEAR3, 0x00 }, + { WCD934X_INTR_PIN2_MASK3, 0xff }, + { WCD934X_INTR_PIN2_STATUS3, 0x00 }, + { WCD934X_INTR_PIN2_CLEAR3, 0x00 }, + { WCD934X_INTR_CPESS_SUMRY_MASK2, 0xff }, + { WCD934X_INTR_CPESS_SUMRY_MASK3, 0xff }, + { WCD934X_INTR_CPESS_SUMRY_STATUS2, 0x00 }, + { WCD934X_INTR_CPESS_SUMRY_STATUS3, 0x00 }, + { WCD934X_INTR_CPESS_SUMRY_CLEAR2, 0x00 }, + { WCD934X_INTR_CPESS_SUMRY_CLEAR3, 0x00 }, + { WCD934X_INTR_LEVEL0, 0x03 }, + { WCD934X_INTR_LEVEL1, 0xe0 }, + { WCD934X_INTR_LEVEL2, 0x94 }, + { WCD934X_INTR_LEVEL3, 0x80 }, + { WCD934X_INTR_BYPASS0, 0x00 }, + { WCD934X_INTR_BYPASS1, 0x00 }, + { WCD934X_INTR_BYPASS2, 0x00 }, + { WCD934X_INTR_BYPASS3, 0x00 }, + { WCD934X_INTR_SET0, 0x00 }, + { WCD934X_INTR_SET1, 0x00 }, + { WCD934X_INTR_SET2, 0x00 }, + { WCD934X_INTR_SET3, 0x00 }, + { WCD934X_INTR_CODEC_MISC_MASK, 0x7f }, + { WCD934X_INTR_CODEC_MISC_STATUS, 0x00 }, + { WCD934X_INTR_CODEC_MISC_CLEAR, 0x00 }, + { WCD934X_PAGE5_PAGE_REGISTER, 0x00 }, + { WCD934X_SLNQ_DIG_DEVICE, 0x49 }, + { WCD934X_SLNQ_DIG_REVISION, 0x01 }, + { WCD934X_SLNQ_DIG_H_COMMAND, 0x00 }, + { WCD934X_SLNQ_DIG_NUMBER_OF_BYTE_MSB, 0x00 }, + { WCD934X_SLNQ_DIG_NUMBER_OF_BYTE_LSB, 0x00 }, + { WCD934X_SLNQ_DIG_MASTER_ADDRESS_MSB, 0x00 }, + { WCD934X_SLNQ_DIG_MASTER_ADDRESS_LSB, 0x00 }, + { WCD934X_SLNQ_DIG_SLAVE_ADDRESS_MSB, 0x00 }, + { WCD934X_SLNQ_DIG_SLAVE_ADDRESS_LSB, 0x00 }, + { WCD934X_SLNQ_DIG_TIMER0_INTERRUPT_MSB, 0x40 }, + { WCD934X_SLNQ_DIG_TIMER0_INTERRUPT_LSB, 0x00 }, + { WCD934X_SLNQ_DIG_TIMER1_INTERRUPT_MSB, 0x40 }, + { WCD934X_SLNQ_DIG_TIMER1_INTERRUPT_LSB, 0x00 }, + { WCD934X_SLNQ_DIG_TIMER2_INTERRUPT_MSB, 0x40 }, + { WCD934X_SLNQ_DIG_TIMER2_INTERRUPT_LSB, 0x00 }, + { WCD934X_SLNQ_DIG_COMM_CTL, 0x00 }, + { WCD934X_SLNQ_DIG_FRAME_CTRL, 0x01 }, + { WCD934X_SLNQ_DIG_PDM_2ND_DATA_CH1_2, 0x77 }, + { WCD934X_SLNQ_DIG_PDM_2ND_DATA_CH3_4, 0x77 }, + { WCD934X_SLNQ_DIG_PDM_2ND_DATA_CH5, 0x70 }, + { WCD934X_SLNQ_DIG_SW_EVENT_RD, 0x00 }, + { WCD934X_SLNQ_DIG_SW_EVENT_CTRL, 0x00 }, + { WCD934X_SLNQ_DIG_PDM_SELECT_1, 0x12 }, + { WCD934X_SLNQ_DIG_PDM_SELECT_2, 0x34 }, + { WCD934X_SLNQ_DIG_PDM_SELECT_3, 0x55 }, + { WCD934X_SLNQ_DIG_PDM_SAMPLING_FREQ, 0x01 }, + { WCD934X_SLNQ_DIG_PDM_DC_CONVERSION_CTL, 0x00 }, + { WCD934X_SLNQ_DIG_PDM_DC_CONVERSION_SEL, 0x11 }, + { WCD934X_SLNQ_DIG_PDM_DC_CONV_CHA_MSB, 0x00 }, + { WCD934X_SLNQ_DIG_PDM_DC_CONV_CHA_LSB, 0x00 }, + { WCD934X_SLNQ_DIG_PDM_DC_CONV_CHB_MSB, 0x00 }, + { WCD934X_SLNQ_DIG_PDM_DC_CONV_CHB_LSB, 0x00 }, + { WCD934X_SLNQ_DIG_RAM_CNTRL, 0x01 }, + { WCD934X_SLNQ_DIG_SRAM_BANK, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_0, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_1, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_2, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_3, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_4, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_5, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_6, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_7, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_8, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_9, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_A, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_B, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_C, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_D, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_E, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_F, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_10, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_11, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_12, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_13, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_14, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_15, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_16, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_17, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_18, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_19, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_1A, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_1B, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_1C, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_1D, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_1E, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_1F, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_20, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_21, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_22, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_23, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_24, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_25, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_26, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_27, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_28, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_29, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_2A, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_2B, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_2C, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_2D, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_2E, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_2F, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_30, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_31, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_32, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_33, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_34, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_35, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_36, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_37, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_38, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_39, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_3A, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_3B, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_3C, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_3D, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_3E, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_3F, 0x00 }, + { WCD934X_SLNQ_DIG_TOP_CTRL1, 0x00 }, + { WCD934X_SLNQ_DIG_TOP_CTRL2, 0x00 }, + { WCD934X_SLNQ_DIG_PDM_CTRL, 0x00 }, + { WCD934X_SLNQ_DIG_PDM_MUTE_CTRL, 0x20 }, + { WCD934X_SLNQ_DIG_DEC_BYPASS_CTRL, 0x00 }, + { WCD934X_SLNQ_DIG_DEC_BYPASS_STATUS, 0x00 }, + { WCD934X_SLNQ_DIG_DEC_BYPASS_FS, 0x00 }, + { WCD934X_SLNQ_DIG_DEC_BYPASS_IN_SEL, 0x00 }, + { WCD934X_SLNQ_DIG_GPOUT_ENABLE, 0x00 }, + { WCD934X_SLNQ_DIG_GPOUT_VAL, 0x00 }, + { WCD934X_SLNQ_DIG_ANA_INTERRUPT_MASK, 0x00 }, + { WCD934X_SLNQ_DIG_ANA_INTERRUPT_STATUS, 0x00 }, + { WCD934X_SLNQ_DIG_ANA_INTERRUPT_CLR, 0x00 }, + { WCD934X_SLNQ_DIG_IP_TESTING, 0x00 }, + { WCD934X_SLNQ_DIG_INTERRUPT_CNTRL, 0x0f }, + { WCD934X_SLNQ_DIG_INTERRUPT_CNT, 0x00 }, + { WCD934X_SLNQ_DIG_INTERRUPT_CNT_MSB, 0xff }, + { WCD934X_SLNQ_DIG_INTERRUPT_CNT_LSB, 0xff }, + { WCD934X_SLNQ_DIG_INTERRUPT_MASK0, 0xff }, + { WCD934X_SLNQ_DIG_INTERRUPT_MASK1, 0xff }, + { WCD934X_SLNQ_DIG_INTERRUPT_MASK2, 0xff }, + { WCD934X_SLNQ_DIG_INTERRUPT_MASK3, 0xff }, + { WCD934X_SLNQ_DIG_INTERRUPT_MASK4, 0x1f }, + { WCD934X_SLNQ_DIG_INTERRUPT_STATUS0, 0x00 }, + { WCD934X_SLNQ_DIG_INTERRUPT_STATUS1, 0x00 }, + { WCD934X_SLNQ_DIG_INTERRUPT_STATUS2, 0x00 }, + { WCD934X_SLNQ_DIG_INTERRUPT_STATUS3, 0x00 }, + { WCD934X_SLNQ_DIG_INTERRUPT_STATUS4, 0x00 }, + { WCD934X_SLNQ_DIG_INTERRUPT_CLR0, 0x00 }, + { WCD934X_SLNQ_DIG_INTERRUPT_CLR1, 0x00 }, + { WCD934X_SLNQ_DIG_INTERRUPT_CLR2, 0x00 }, + { WCD934X_SLNQ_DIG_INTERRUPT_CLR3, 0x00 }, + { WCD934X_SLNQ_DIG_INTERRUPT_CLR4, 0x00 }, + { WCD934X_ANA_PAGE_REGISTER, 0x00 }, + { WCD934X_ANA_BIAS, 0x00 }, + { WCD934X_ANA_RCO, 0x00 }, + { WCD934X_ANA_PAGE6_SPARE2, 0x00 }, + { WCD934X_ANA_PAGE6_SPARE3, 0x00 }, + { WCD934X_ANA_BUCK_CTL, 0x00 }, + { WCD934X_ANA_BUCK_STATUS, 0x00 }, + { WCD934X_ANA_RX_SUPPLIES, 0x00 }, + { WCD934X_ANA_HPH, 0x0c }, + { WCD934X_ANA_EAR, 0x00 }, + { WCD934X_ANA_LO_1_2, 0x3c }, + { WCD934X_ANA_MAD_SETUP, 0x01 }, + { WCD934X_ANA_AMIC1, 0x20 }, + { WCD934X_ANA_AMIC2, 0x00 }, + { WCD934X_ANA_AMIC3, 0x20 }, + { WCD934X_ANA_AMIC4, 0x00 }, + { WCD934X_ANA_MBHC_MECH, 0x39 }, + { WCD934X_ANA_MBHC_ELECT, 0x08 }, + { WCD934X_ANA_MBHC_ZDET, 0x00 }, + { WCD934X_ANA_MBHC_RESULT_1, 0x00 }, + { WCD934X_ANA_MBHC_RESULT_2, 0x00 }, + { WCD934X_ANA_MBHC_RESULT_3, 0x00 }, + { WCD934X_ANA_MBHC_BTN0, 0x00 }, + { WCD934X_ANA_MBHC_BTN1, 0x10 }, + { WCD934X_ANA_MBHC_BTN2, 0x20 }, + { WCD934X_ANA_MBHC_BTN3, 0x30 }, + { WCD934X_ANA_MBHC_BTN4, 0x40 }, + { WCD934X_ANA_MBHC_BTN5, 0x50 }, + { WCD934X_ANA_MBHC_BTN6, 0x60 }, + { WCD934X_ANA_MBHC_BTN7, 0x70 }, + { WCD934X_ANA_MICB1, 0x10 }, + { WCD934X_ANA_MICB2, 0x10 }, + { WCD934X_ANA_MICB2_RAMP, 0x00 }, + { WCD934X_ANA_MICB3, 0x10 }, + { WCD934X_ANA_MICB4, 0x10 }, + { WCD934X_ANA_VBADC, 0x00 }, + { WCD934X_BIAS_CTL, 0x28 }, + { WCD934X_BIAS_VBG_FINE_ADJ, 0x65 }, + { WCD934X_RCO_CTRL_1, 0x44 }, + { WCD934X_RCO_CTRL_2, 0x48 }, + { WCD934X_RCO_CAL, 0x00 }, + { WCD934X_RCO_CAL_1, 0x00 }, + { WCD934X_RCO_CAL_2, 0x00 }, + { WCD934X_RCO_TEST_CTRL, 0x00 }, + { WCD934X_RCO_CAL_OUT_1, 0x00 }, + { WCD934X_RCO_CAL_OUT_2, 0x00 }, + { WCD934X_RCO_CAL_OUT_3, 0x00 }, + { WCD934X_RCO_CAL_OUT_4, 0x00 }, + { WCD934X_RCO_CAL_OUT_5, 0x00 }, + { WCD934X_SIDO_MODE_1, 0x84 }, + { WCD934X_SIDO_MODE_2, 0xfe }, + { WCD934X_SIDO_MODE_3, 0xf6 }, + { WCD934X_SIDO_MODE_4, 0x56 }, + { WCD934X_SIDO_VCL_1, 0x00 }, + { WCD934X_SIDO_VCL_2, 0x6c }, + { WCD934X_SIDO_VCL_3, 0x44 }, + { WCD934X_SIDO_CCL_1, 0x57 }, + { WCD934X_SIDO_CCL_2, 0x92 }, + { WCD934X_SIDO_CCL_3, 0x35 }, + { WCD934X_SIDO_CCL_4, 0x61 }, + { WCD934X_SIDO_CCL_5, 0x6d }, + { WCD934X_SIDO_CCL_6, 0x60 }, + { WCD934X_SIDO_CCL_7, 0x6f }, + { WCD934X_SIDO_CCL_8, 0x6f }, + { WCD934X_SIDO_CCL_9, 0x6e }, + { WCD934X_SIDO_CCL_10, 0x26 }, + { WCD934X_SIDO_FILTER_1, 0x92 }, + { WCD934X_SIDO_FILTER_2, 0x54 }, + { WCD934X_SIDO_DRIVER_1, 0x77 }, + { WCD934X_SIDO_DRIVER_2, 0x55 }, + { WCD934X_SIDO_DRIVER_3, 0x55 }, + { WCD934X_SIDO_CAL_CODE_EXT_1, 0x9c }, + { WCD934X_SIDO_CAL_CODE_EXT_2, 0x82 }, + { WCD934X_SIDO_CAL_CODE_OUT_1, 0x00 }, + { WCD934X_SIDO_CAL_CODE_OUT_2, 0x00 }, + { WCD934X_SIDO_TEST_1, 0x00 }, + { WCD934X_SIDO_TEST_2, 0x00 }, + { WCD934X_MBHC_CTL_CLK, 0x30 }, + { WCD934X_MBHC_CTL_ANA, 0x00 }, + { WCD934X_MBHC_CTL_SPARE_1, 0x00 }, + { WCD934X_MBHC_CTL_SPARE_2, 0x00 }, + { WCD934X_MBHC_CTL_BCS, 0x00 }, + { WCD934X_MBHC_STATUS_SPARE_1, 0x00 }, + { WCD934X_MBHC_TEST_CTL, 0x00 }, + { WCD934X_VBADC_SUBBLOCK_EN, 0xde }, + { WCD934X_VBADC_IBIAS_FE, 0x58 }, + { WCD934X_VBADC_BIAS_ADC, 0x51 }, + { WCD934X_VBADC_FE_CTRL, 0x1c }, + { WCD934X_VBADC_ADC_REF, 0x20 }, + { WCD934X_VBADC_ADC_IO, 0x80 }, + { WCD934X_VBADC_ADC_SAR, 0xff }, + { WCD934X_VBADC_DEBUG, 0x00 }, + { WCD934X_LDOH_MODE, 0x2b }, + { WCD934X_LDOH_BIAS, 0x68 }, + { WCD934X_LDOH_STB_LOADS, 0x00 }, + { WCD934X_LDOH_SLOWRAMP, 0x50 }, + { WCD934X_MICB1_TEST_CTL_1, 0x1a }, + { WCD934X_MICB1_TEST_CTL_2, 0x18 }, + { WCD934X_MICB1_TEST_CTL_3, 0xa4 }, + { WCD934X_MICB2_TEST_CTL_1, 0x1a }, + { WCD934X_MICB2_TEST_CTL_2, 0x18 }, + { WCD934X_MICB2_TEST_CTL_3, 0xa4 }, + { WCD934X_MICB3_TEST_CTL_1, 0x1a }, + { WCD934X_MICB3_TEST_CTL_2, 0x18 }, + { WCD934X_MICB3_TEST_CTL_3, 0xa4 }, + { WCD934X_MICB4_TEST_CTL_1, 0x1a }, + { WCD934X_MICB4_TEST_CTL_2, 0x18 }, + { WCD934X_MICB4_TEST_CTL_3, 0xa4 }, + { WCD934X_TX_COM_ADC_VCM, 0x39 }, + { WCD934X_TX_COM_BIAS_ATEST, 0xc0 }, + { WCD934X_TX_COM_ADC_INT1_IB, 0x6f }, + { WCD934X_TX_COM_ADC_INT2_IB, 0x4f }, + { WCD934X_TX_COM_TXFE_DIV_CTL, 0x2e }, + { WCD934X_TX_COM_TXFE_DIV_START, 0x00 }, + { WCD934X_TX_COM_TXFE_DIV_STOP_9P6M, 0xc7 }, + { WCD934X_TX_COM_TXFE_DIV_STOP_12P288M, 0xff }, + { WCD934X_TX_1_2_TEST_EN, 0xcc }, + { WCD934X_TX_1_2_ADC_IB, 0x09 }, + { WCD934X_TX_1_2_ATEST_REFCTL, 0x0a }, + { WCD934X_TX_1_2_TEST_CTL, 0x38 }, + { WCD934X_TX_1_2_TEST_BLK_EN, 0xff }, + { WCD934X_TX_1_2_TXFE_CLKDIV, 0x00 }, + { WCD934X_TX_1_2_SAR1_ERR, 0x00 }, + { WCD934X_TX_1_2_SAR2_ERR, 0x00 }, + { WCD934X_TX_3_4_TEST_EN, 0xcc }, + { WCD934X_TX_3_4_ADC_IB, 0x09 }, + { WCD934X_TX_3_4_ATEST_REFCTL, 0x0a }, + { WCD934X_TX_3_4_TEST_CTL, 0x38 }, + { WCD934X_TX_3_4_TEST_BLK_EN, 0xff }, + { WCD934X_TX_3_4_TXFE_CLKDIV, 0x00 }, + { WCD934X_TX_3_4_SAR1_ERR, 0x00 }, + { WCD934X_TX_3_4_SAR2_ERR, 0x00 }, + { WCD934X_CLASSH_MODE_1, 0x40 }, + { WCD934X_CLASSH_MODE_2, 0x3a }, + { WCD934X_CLASSH_MODE_3, 0x00 }, + { WCD934X_CLASSH_CTRL_VCL_1, 0x70 }, + { WCD934X_CLASSH_CTRL_VCL_2, 0x82 }, + { WCD934X_CLASSH_CTRL_CCL_1, 0x31 }, + { WCD934X_CLASSH_CTRL_CCL_2, 0x80 }, + { WCD934X_CLASSH_CTRL_CCL_3, 0x80 }, + { WCD934X_CLASSH_CTRL_CCL_4, 0x51 }, + { WCD934X_CLASSH_CTRL_CCL_5, 0x00 }, + { WCD934X_CLASSH_BUCK_TMUX_A_D, 0x00 }, + { WCD934X_CLASSH_BUCK_SW_DRV_CNTL, 0x77 }, + { WCD934X_CLASSH_SPARE, 0x00 }, + { WCD934X_FLYBACK_EN, 0x4e }, + { WCD934X_FLYBACK_VNEG_CTRL_1, 0x0b }, + { WCD934X_FLYBACK_VNEG_CTRL_2, 0x45 }, + { WCD934X_FLYBACK_VNEG_CTRL_3, 0x74 }, + { WCD934X_FLYBACK_VNEG_CTRL_4, 0x7f }, + { WCD934X_FLYBACK_VNEG_CTRL_5, 0x83 }, + { WCD934X_FLYBACK_VNEG_CTRL_6, 0x98 }, + { WCD934X_FLYBACK_VNEG_CTRL_7, 0xa9 }, + { WCD934X_FLYBACK_VNEG_CTRL_8, 0x68 }, + { WCD934X_FLYBACK_VNEG_CTRL_9, 0x64 }, + { WCD934X_FLYBACK_VNEGDAC_CTRL_1, 0xed }, + { WCD934X_FLYBACK_VNEGDAC_CTRL_2, 0xf0 }, + { WCD934X_FLYBACK_VNEGDAC_CTRL_3, 0xa6 }, + { WCD934X_FLYBACK_CTRL_1, 0x65 }, + { WCD934X_FLYBACK_TEST_CTL, 0x00 }, + { WCD934X_RX_AUX_SW_CTL, 0x00 }, + { WCD934X_RX_PA_AUX_IN_CONN, 0x00 }, + { WCD934X_RX_TIMER_DIV, 0x32 }, + { WCD934X_RX_OCP_CTL, 0x1f }, + { WCD934X_RX_OCP_COUNT, 0x77 }, + { WCD934X_RX_BIAS_EAR_DAC, 0xa0 }, + { WCD934X_RX_BIAS_EAR_AMP, 0xaa }, + { WCD934X_RX_BIAS_HPH_LDO, 0xa9 }, + { WCD934X_RX_BIAS_HPH_PA, 0xaa }, + { WCD934X_RX_BIAS_HPH_RDACBUFF_CNP2, 0x8a }, + { WCD934X_RX_BIAS_HPH_RDAC_LDO, 0x88 }, + { WCD934X_RX_BIAS_HPH_CNP1, 0x82 }, + { WCD934X_RX_BIAS_HPH_LOWPOWER, 0x82 }, + { WCD934X_RX_BIAS_DIFFLO_PA, 0x80 }, + { WCD934X_RX_BIAS_DIFFLO_REF, 0x88 }, + { WCD934X_RX_BIAS_DIFFLO_LDO, 0x88 }, + { WCD934X_RX_BIAS_SELO_DAC_PA, 0xa8 }, + { WCD934X_RX_BIAS_BUCK_RST, 0x08 }, + { WCD934X_RX_BIAS_BUCK_VREF_ERRAMP, 0x44 }, + { WCD934X_RX_BIAS_FLYB_ERRAMP, 0x40 }, + { WCD934X_RX_BIAS_FLYB_BUFF, 0xaa }, + { WCD934X_RX_BIAS_FLYB_MID_RST, 0x14 }, + { WCD934X_HPH_L_STATUS, 0x04 }, + { WCD934X_HPH_R_STATUS, 0x04 }, + { WCD934X_HPH_CNP_EN, 0x80 }, + { WCD934X_HPH_CNP_WG_CTL, 0x9a }, + { WCD934X_HPH_CNP_WG_TIME, 0x14 }, + { WCD934X_HPH_OCP_CTL, 0x28 }, + { WCD934X_HPH_AUTO_CHOP, 0x16 }, + { WCD934X_HPH_CHOP_CTL, 0x83 }, + { WCD934X_HPH_PA_CTL1, 0x46 }, + { WCD934X_HPH_PA_CTL2, 0x50 }, + { WCD934X_HPH_L_EN, 0x80 }, + { WCD934X_HPH_L_TEST, 0xe0 }, + { WCD934X_HPH_L_ATEST, 0x50 }, + { WCD934X_HPH_R_EN, 0x80 }, + { WCD934X_HPH_R_TEST, 0xe0 }, + { WCD934X_HPH_R_ATEST, 0x54 }, + { WCD934X_HPH_RDAC_CLK_CTL1, 0x99 }, + { WCD934X_HPH_RDAC_CLK_CTL2, 0x9b }, + { WCD934X_HPH_RDAC_LDO_CTL, 0x33 }, + { WCD934X_HPH_RDAC_CHOP_CLK_LP_CTL, 0x00 }, + { WCD934X_HPH_REFBUFF_UHQA_CTL, 0xa8 }, + { WCD934X_HPH_REFBUFF_LP_CTL, 0x0a }, + { WCD934X_HPH_L_DAC_CTL, 0x00 }, + { WCD934X_HPH_R_DAC_CTL, 0x00 }, + { WCD934X_EAR_EN_REG, 0x60 }, + { WCD934X_EAR_CMBUFF, 0x05 }, + { WCD934X_EAR_ICTL, 0x40 }, + { WCD934X_EAR_EN_DBG_CTL, 0x00 }, + { WCD934X_EAR_CNP, 0xe0 }, + { WCD934X_EAR_DAC_CTL_ATEST, 0x00 }, + { WCD934X_EAR_STATUS_REG, 0x04 }, + { WCD934X_EAR_EAR_MISC, 0x28 }, + { WCD934X_DIFF_LO_MISC, 0x03 }, + { WCD934X_DIFF_LO_LO2_COMPANDER, 0x00 }, + { WCD934X_DIFF_LO_LO1_COMPANDER, 0x00 }, + { WCD934X_DIFF_LO_COMMON, 0x40 }, + { WCD934X_DIFF_LO_BYPASS_EN, 0x00 }, + { WCD934X_DIFF_LO_CNP, 0x20 }, + { WCD934X_DIFF_LO_CORE_OUT_PROG, 0xa0 }, + { WCD934X_DIFF_LO_LDO_OUT_PROG, 0x00 }, + { WCD934X_DIFF_LO_COM_SWCAP_REFBUF_FREQ, 0x8b }, + { WCD934X_DIFF_LO_COM_PA_FREQ, 0xb0 }, + { WCD934X_DIFF_LO_RESERVED_REG, 0x60 }, + { WCD934X_DIFF_LO_LO1_STATUS_1, 0x00 }, + { WCD934X_DIFF_LO_LO1_STATUS_2, 0x00 }, + { WCD934X_ANA_NEW_PAGE_REGISTER, 0x00 }, + { WCD934X_HPH_NEW_ANA_HPH2, 0x00 }, + { WCD934X_HPH_NEW_ANA_HPH3, 0x00 }, + { WCD934X_SLNQ_ANA_EN, 0x02 }, + { WCD934X_SLNQ_ANA_STATUS, 0x00 }, + { WCD934X_SLNQ_ANA_LDO_CONFIG, 0xea }, + { WCD934X_SLNQ_ANA_LDO_OCP_CONFIG, 0x95 }, + { WCD934X_SLNQ_ANA_TX_LDO_CONFIG, 0xb6 }, + { WCD934X_SLNQ_ANA_TX_DRV_CONFIG, 0x26 }, + { WCD934X_SLNQ_ANA_RX_CONFIG_1, 0x64 }, + { WCD934X_SLNQ_ANA_RX_CONFIG_2, 0x40 }, + { WCD934X_SLNQ_ANA_PLL_ENABLES, 0x00 }, + { WCD934X_SLNQ_ANA_PLL_PRESET, 0x08 }, + { WCD934X_SLNQ_ANA_PLL_STATUS, 0x00 }, + { WCD934X_CLK_SYS_PLL_ENABLES, 0x00 }, + { WCD934X_CLK_SYS_PLL_PRESET, 0x00 }, + { WCD934X_CLK_SYS_PLL_STATUS, 0x00 }, + { WCD934X_CLK_SYS_MCLK_PRG, 0x00 }, + { WCD934X_CLK_SYS_MCLK2_PRG1, 0x00 }, + { WCD934X_CLK_SYS_MCLK2_PRG2, 0x00 }, + { WCD934X_CLK_SYS_XO_PRG, 0x00 }, + { WCD934X_CLK_SYS_XO_CAP_XTP, 0x00 }, + { WCD934X_CLK_SYS_XO_CAP_XTM, 0x00 }, + { WCD934X_BOOST_BST_EN_DLY, 0x40 }, + { WCD934X_BOOST_CTRL_ILIM, 0x9c }, + { WCD934X_BOOST_VOUT_SETTING, 0xca }, + { WCD934X_SIDO_NEW_VOUT_A_STARTUP, 0x05 }, + { WCD934X_SIDO_NEW_VOUT_D_STARTUP, 0x0d }, + { WCD934X_SIDO_NEW_VOUT_D_FREQ1, 0x07 }, + { WCD934X_SIDO_NEW_VOUT_D_FREQ2, 0x00 }, + { WCD934X_MBHC_NEW_ELECT_REM_CLAMP_CTL, 0x00 }, + { WCD934X_MBHC_NEW_CTL_1, 0x02 }, + { WCD934X_MBHC_NEW_CTL_2, 0x05 }, + { WCD934X_MBHC_NEW_PLUG_DETECT_CTL, 0xe9 }, + { WCD934X_MBHC_NEW_ZDET_ANA_CTL, 0x0f }, + { WCD934X_MBHC_NEW_ZDET_RAMP_CTL, 0x00 }, + { WCD934X_MBHC_NEW_FSM_STATUS, 0x00 }, + { WCD934X_MBHC_NEW_ADC_RESULT, 0x00 }, + { WCD934X_TX_NEW_AMIC_4_5_SEL, 0x00 }, + { WCD934X_VBADC_NEW_ADC_MODE, 0x10 }, + { WCD934X_VBADC_NEW_ADC_DOUTMSB, 0x00 }, + { WCD934X_VBADC_NEW_ADC_DOUTLSB, 0x00 }, + { WCD934X_HPH_NEW_INT_RDAC_GAIN_CTL, 0x00 }, + { WCD934X_HPH_NEW_INT_RDAC_HD2_CTL, 0xa0 }, + { WCD934X_HPH_NEW_INT_RDAC_VREF_CTL, 0x10 }, + { WCD934X_HPH_NEW_INT_RDAC_OVERRIDE_CTL, 0x00 }, + { WCD934X_HPH_NEW_INT_RDAC_MISC1, 0x00 }, + { WCD934X_HPH_NEW_INT_PA_MISC1, 0x22 }, + { WCD934X_HPH_NEW_INT_PA_MISC2, 0x00 }, + { WCD934X_HPH_NEW_INT_PA_RDAC_MISC, 0x00 }, + { WCD934X_HPH_NEW_INT_HPH_TIMER1, 0xfe }, + { WCD934X_HPH_NEW_INT_HPH_TIMER2, 0x02 }, + { WCD934X_HPH_NEW_INT_HPH_TIMER3, 0x4e }, + { WCD934X_HPH_NEW_INT_HPH_TIMER4, 0x54 }, + { WCD934X_HPH_NEW_INT_PA_RDAC_MISC2, 0x00 }, + { WCD934X_HPH_NEW_INT_PA_RDAC_MISC3, 0x00 }, + { WCD934X_RX_NEW_INT_HPH_RDAC_BIAS_LOHIFI, 0x62 }, + { WCD934X_RX_NEW_INT_HPH_RDAC_BIAS_ULP, 0x01 }, + { WCD934X_RX_NEW_INT_HPH_RDAC_LDO_LP, 0x11 }, + { WCD934X_SLNQ_INT_ANA_INT_LDO_TEST, 0x0d }, + { WCD934X_SLNQ_INT_ANA_INT_LDO_DEBUG_1, 0x85 }, + { WCD934X_SLNQ_INT_ANA_INT_LDO_DEBUG_2, 0xb4 }, + { WCD934X_SLNQ_INT_ANA_INT_TX_LDO_TEST, 0x16 }, + { WCD934X_SLNQ_INT_ANA_INT_TX_DRV_TEST, 0x00 }, + { WCD934X_SLNQ_INT_ANA_INT_RX_TEST, 0x00 }, + { WCD934X_SLNQ_INT_ANA_INT_RX_TEST_STATUS, 0x00 }, + { WCD934X_SLNQ_INT_ANA_INT_RX_DEBUG_1, 0x50 }, + { WCD934X_SLNQ_INT_ANA_INT_RX_DEBUG_2, 0x04 }, + { WCD934X_SLNQ_INT_ANA_INT_CLK_CTRL, 0x00 }, + { WCD934X_SLNQ_INT_ANA_INT_RESERVED_1, 0x00 }, + { WCD934X_SLNQ_INT_ANA_INT_RESERVED_2, 0x00 }, + { WCD934X_SLNQ_INT_ANA_INT_PLL_POST_DIV_REG0, 0x00 }, + { WCD934X_SLNQ_INT_ANA_INT_PLL_POST_DIV_REG1, 0x00 }, + { WCD934X_SLNQ_INT_ANA_INT_PLL_REF_DIV_REG0, 0x00 }, + { WCD934X_SLNQ_INT_ANA_INT_PLL_REF_DIV_REG1, 0x00 }, + { WCD934X_SLNQ_INT_ANA_INT_PLL_FILTER_REG0, 0x00 }, + { WCD934X_SLNQ_INT_ANA_INT_PLL_FILTER_REG1, 0x00 }, + { WCD934X_SLNQ_INT_ANA_INT_PLL_L_VAL, 0x00 }, + { WCD934X_SLNQ_INT_ANA_INT_PLL_M_VAL, 0x00 }, + { WCD934X_SLNQ_INT_ANA_INT_PLL_N_VAL, 0x00 }, + { WCD934X_SLNQ_INT_ANA_INT_PLL_TEST_REG0, 0x00 }, + { WCD934X_SLNQ_INT_ANA_INT_PLL_PFD_CP_DSM_PROG, 0x00 }, + { WCD934X_SLNQ_INT_ANA_INT_PLL_VCO_PROG, 0x00 }, + { WCD934X_SLNQ_INT_ANA_INT_PLL_TEST_REG1, 0x00 }, + { WCD934X_SLNQ_INT_ANA_INT_PLL_LDO_LOCK_CFG, 0x00 }, + { WCD934X_SLNQ_INT_ANA_INT_PLL_DIG_LOCK_DET_CFG, 0x00 }, + { WCD934X_CLK_SYS_INT_POST_DIV_REG0, 0x00 }, + { WCD934X_CLK_SYS_INT_POST_DIV_REG1, 0x00 }, + { WCD934X_CLK_SYS_INT_REF_DIV_REG0, 0x00 }, + { WCD934X_CLK_SYS_INT_REF_DIV_REG1, 0x00 }, + { WCD934X_CLK_SYS_INT_FILTER_REG0, 0x00 }, + { WCD934X_CLK_SYS_INT_FILTER_REG1, 0x00 }, + { WCD934X_CLK_SYS_INT_PLL_L_VAL, 0x00 }, + { WCD934X_CLK_SYS_INT_PLL_M_VAL, 0x00 }, + { WCD934X_CLK_SYS_INT_PLL_N_VAL, 0x00 }, + { WCD934X_CLK_SYS_INT_TEST_REG0, 0x00 }, + { WCD934X_CLK_SYS_INT_PFD_CP_DSM_PROG, 0x00 }, + { WCD934X_CLK_SYS_INT_VCO_PROG, 0x00 }, + { WCD934X_CLK_SYS_INT_TEST_REG1, 0x00 }, + { WCD934X_CLK_SYS_INT_LDO_LOCK_CFG, 0x00 }, + { WCD934X_CLK_SYS_INT_DIG_LOCK_DET_CFG, 0x00 }, + { WCD934X_CLK_SYS_INT_CLK_TEST1, 0x00 }, + { WCD934X_CLK_SYS_INT_CLK_TEST2, 0x00 }, + { WCD934X_CLK_SYS_INT_CLK_TEST3, 0x00 }, + { WCD934X_CLK_SYS_INT_XO_TEST1, 0x98 }, + { WCD934X_CLK_SYS_INT_XO_TEST2, 0x00 }, + { WCD934X_BOOST_INT_VCOMP_HYST, 0x02 }, + { WCD934X_BOOST_INT_VLOOP_FILTER, 0xef }, + { WCD934X_BOOST_INT_CTRL_IDELTA, 0xa8 }, + { WCD934X_BOOST_INT_CTRL_ILIM_STARTUP, 0x17 }, + { WCD934X_BOOST_INT_CTRL_MIN_ONTIME, 0x5f }, + { WCD934X_BOOST_INT_CTRL_MAX_ONTIME, 0x88 }, + { WCD934X_BOOST_INT_CTRL_TIMING, 0x0a }, + { WCD934X_BOOST_INT_TMUX_A_D, 0x00 }, + { WCD934X_BOOST_INT_SW_DRV_CNTL, 0xf8 }, + { WCD934X_BOOST_INT_SPARE1, 0x00 }, + { WCD934X_BOOST_INT_SPARE2, 0x00 }, + { WCD934X_SIDO_NEW_INT_RAMP_STATUS, 0x00 }, + { WCD934X_SIDO_NEW_INT_SPARE_1, 0x00 }, + { WCD934X_SIDO_NEW_INT_DEBUG_VOUT_SETTING_A, 0x64 }, + { WCD934X_SIDO_NEW_INT_DEBUG_VOUT_SETTING_D, 0x40 }, + { WCD934X_SIDO_NEW_INT_RAMP_INC_WAIT, 0x24 }, + { WCD934X_SIDO_NEW_INT_DYNAMIC_IPEAK_CTL, 0x09 }, + { WCD934X_SIDO_NEW_INT_RAMP_IBLEED_CTL, 0x7d }, + { WCD934X_SIDO_NEW_INT_DEBUG_CPROVR_TEST, 0x00 }, + { WCD934X_SIDO_NEW_INT_RAMP_CTL_A, 0x14 }, + { WCD934X_SIDO_NEW_INT_RAMP_CTL_D, 0x14 }, + { WCD934X_SIDO_NEW_INT_RAMP_TIMEOUT_PERIOD, 0x33 }, + { WCD934X_SIDO_NEW_INT_DYNAMIC_IPEAK_SETTING1, 0x3f }, + { WCD934X_SIDO_NEW_INT_DYNAMIC_IPEAK_SETTING2, 0x74 }, + { WCD934X_SIDO_NEW_INT_DYNAMIC_IPEAK_SETTING3, 0x33 }, + { WCD934X_SIDO_NEW_INT_HIGH_ACCU_MODE_SEL1, 0x1d }, + { WCD934X_SIDO_NEW_INT_HIGH_ACCU_MODE_SEL2, 0x0a }, + { WCD934X_MBHC_NEW_INT_SLNQ_HPF, 0x50 }, + { WCD934X_MBHC_NEW_INT_SLNQ_REF, 0x24 }, + { WCD934X_MBHC_NEW_INT_SLNQ_COMP, 0x50 }, + { WCD934X_MBHC_NEW_INT_SPARE_2, 0x00 }, + { WCD934X_PAGE10_PAGE_REGISTER, 0x00 }, + { WCD934X_CDC_ANC0_CLK_RESET_CTL, 0x00 }, + { WCD934X_CDC_ANC0_MODE_1_CTL, 0x00 }, + { WCD934X_CDC_ANC0_MODE_2_CTL, 0x00 }, + { WCD934X_CDC_ANC0_FF_SHIFT, 0x00 }, + { WCD934X_CDC_ANC0_FB_SHIFT, 0x00 }, + { WCD934X_CDC_ANC0_LPF_FF_A_CTL, 0x00 }, + { WCD934X_CDC_ANC0_LPF_FF_B_CTL, 0x00 }, + { WCD934X_CDC_ANC0_LPF_FB_CTL, 0x00 }, + { WCD934X_CDC_ANC0_SMLPF_CTL, 0x00 }, + { WCD934X_CDC_ANC0_DCFLT_SHIFT_CTL, 0x00 }, + { WCD934X_CDC_ANC0_IIR_ADAPT_CTL, 0x00 }, + { WCD934X_CDC_ANC0_IIR_COEFF_1_CTL, 0x00 }, + { WCD934X_CDC_ANC0_IIR_COEFF_2_CTL, 0x00 }, + { WCD934X_CDC_ANC0_FF_A_GAIN_CTL, 0x00 }, + { WCD934X_CDC_ANC0_FF_B_GAIN_CTL, 0x00 }, + { WCD934X_CDC_ANC0_FB_GAIN_CTL, 0x00 }, + { WCD934X_CDC_ANC0_RC_COMMON_CTL, 0x00 }, + { WCD934X_CDC_ANC0_FIFO_COMMON_CTL, 0x88 }, + { WCD934X_CDC_ANC0_RC0_STATUS_FMIN_CNTR, 0x00 }, + { WCD934X_CDC_ANC0_RC1_STATUS_FMIN_CNTR, 0x00 }, + { WCD934X_CDC_ANC0_RC0_STATUS_FMAX_CNTR, 0x00 }, + { WCD934X_CDC_ANC0_RC1_STATUS_FMAX_CNTR, 0x00 }, + { WCD934X_CDC_ANC0_STATUS_FIFO, 0x00 }, + { WCD934X_CDC_ANC1_CLK_RESET_CTL, 0x00 }, + { WCD934X_CDC_ANC1_MODE_1_CTL, 0x00 }, + { WCD934X_CDC_ANC1_MODE_2_CTL, 0x00 }, + { WCD934X_CDC_ANC1_FF_SHIFT, 0x00 }, + { WCD934X_CDC_ANC1_FB_SHIFT, 0x00 }, + { WCD934X_CDC_ANC1_LPF_FF_A_CTL, 0x00 }, + { WCD934X_CDC_ANC1_LPF_FF_B_CTL, 0x00 }, + { WCD934X_CDC_ANC1_LPF_FB_CTL, 0x00 }, + { WCD934X_CDC_ANC1_SMLPF_CTL, 0x00 }, + { WCD934X_CDC_ANC1_DCFLT_SHIFT_CTL, 0x00 }, + { WCD934X_CDC_ANC1_IIR_ADAPT_CTL, 0x00 }, + { WCD934X_CDC_ANC1_IIR_COEFF_1_CTL, 0x00 }, + { WCD934X_CDC_ANC1_IIR_COEFF_2_CTL, 0x00 }, + { WCD934X_CDC_ANC1_FF_A_GAIN_CTL, 0x00 }, + { WCD934X_CDC_ANC1_FF_B_GAIN_CTL, 0x00 }, + { WCD934X_CDC_ANC1_FB_GAIN_CTL, 0x00 }, + { WCD934X_CDC_ANC1_RC_COMMON_CTL, 0x00 }, + { WCD934X_CDC_ANC1_FIFO_COMMON_CTL, 0x88 }, + { WCD934X_CDC_ANC1_RC0_STATUS_FMIN_CNTR, 0x00 }, + { WCD934X_CDC_ANC1_RC1_STATUS_FMIN_CNTR, 0x00 }, + { WCD934X_CDC_ANC1_RC0_STATUS_FMAX_CNTR, 0x00 }, + { WCD934X_CDC_ANC1_RC1_STATUS_FMAX_CNTR, 0x00 }, + { WCD934X_CDC_ANC1_STATUS_FIFO, 0x00 }, + { WCD934X_CDC_TX0_TX_PATH_CTL, 0x04 }, + { WCD934X_CDC_TX0_TX_PATH_CFG0, 0x10 }, + { WCD934X_CDC_TX0_TX_PATH_CFG1, 0x03 }, + { WCD934X_CDC_TX0_TX_VOL_CTL, 0x00 }, + { WCD934X_CDC_TX0_TX_PATH_192_CTL, 0x00 }, + { WCD934X_CDC_TX0_TX_PATH_192_CFG, 0x00 }, + { WCD934X_CDC_TX0_TX_PATH_SEC0, 0x00 }, + { WCD934X_CDC_TX0_TX_PATH_SEC1, 0x00 }, + { WCD934X_CDC_TX0_TX_PATH_SEC2, 0x01 }, + { WCD934X_CDC_TX0_TX_PATH_SEC3, 0x3c }, + { WCD934X_CDC_TX0_TX_PATH_SEC4, 0x20 }, + { WCD934X_CDC_TX0_TX_PATH_SEC5, 0x00 }, + { WCD934X_CDC_TX0_TX_PATH_SEC6, 0x00 }, + { WCD934X_CDC_TX0_TX_PATH_SEC7, 0x25 }, + { WCD934X_CDC_TX1_TX_PATH_CTL, 0x04 }, + { WCD934X_CDC_TX1_TX_PATH_CFG0, 0x10 }, + { WCD934X_CDC_TX1_TX_PATH_CFG1, 0x03 }, + { WCD934X_CDC_TX1_TX_VOL_CTL, 0x00 }, + { WCD934X_CDC_TX1_TX_PATH_192_CTL, 0x00 }, + { WCD934X_CDC_TX1_TX_PATH_192_CFG, 0x00 }, + { WCD934X_CDC_TX1_TX_PATH_SEC0, 0x00 }, + { WCD934X_CDC_TX1_TX_PATH_SEC1, 0x00 }, + { WCD934X_CDC_TX1_TX_PATH_SEC2, 0x01 }, + { WCD934X_CDC_TX1_TX_PATH_SEC3, 0x3c }, + { WCD934X_CDC_TX1_TX_PATH_SEC4, 0x20 }, + { WCD934X_CDC_TX1_TX_PATH_SEC5, 0x00 }, + { WCD934X_CDC_TX1_TX_PATH_SEC6, 0x00 }, + { WCD934X_CDC_TX2_TX_PATH_CTL, 0x04 }, + { WCD934X_CDC_TX2_TX_PATH_CFG0, 0x10 }, + { WCD934X_CDC_TX2_TX_PATH_CFG1, 0x03 }, + { WCD934X_CDC_TX2_TX_VOL_CTL, 0x00 }, + { WCD934X_CDC_TX2_TX_PATH_192_CTL, 0x00 }, + { WCD934X_CDC_TX2_TX_PATH_192_CFG, 0x00 }, + { WCD934X_CDC_TX2_TX_PATH_SEC0, 0x00 }, + { WCD934X_CDC_TX2_TX_PATH_SEC1, 0x00 }, + { WCD934X_CDC_TX2_TX_PATH_SEC2, 0x01 }, + { WCD934X_CDC_TX2_TX_PATH_SEC3, 0x3c }, + { WCD934X_CDC_TX2_TX_PATH_SEC4, 0x20 }, + { WCD934X_CDC_TX2_TX_PATH_SEC5, 0x00 }, + { WCD934X_CDC_TX2_TX_PATH_SEC6, 0x00 }, + { WCD934X_CDC_TX3_TX_PATH_CTL, 0x04 }, + { WCD934X_CDC_TX3_TX_PATH_CFG0, 0x10 }, + { WCD934X_CDC_TX3_TX_PATH_CFG1, 0x03 }, + { WCD934X_CDC_TX3_TX_VOL_CTL, 0x00 }, + { WCD934X_CDC_TX3_TX_PATH_192_CTL, 0x00 }, + { WCD934X_CDC_TX3_TX_PATH_192_CFG, 0x00 }, + { WCD934X_CDC_TX3_TX_PATH_SEC0, 0x00 }, + { WCD934X_CDC_TX3_TX_PATH_SEC1, 0x00 }, + { WCD934X_CDC_TX3_TX_PATH_SEC2, 0x01 }, + { WCD934X_CDC_TX3_TX_PATH_SEC3, 0x3c }, + { WCD934X_CDC_TX3_TX_PATH_SEC4, 0x20 }, + { WCD934X_CDC_TX3_TX_PATH_SEC5, 0x00 }, + { WCD934X_CDC_TX3_TX_PATH_SEC6, 0x00 }, + { WCD934X_CDC_TX4_TX_PATH_CTL, 0x04 }, + { WCD934X_CDC_TX4_TX_PATH_CFG0, 0x10 }, + { WCD934X_CDC_TX4_TX_PATH_CFG1, 0x03 }, + { WCD934X_CDC_TX4_TX_VOL_CTL, 0x00 }, + { WCD934X_CDC_TX4_TX_PATH_192_CTL, 0x00 }, + { WCD934X_CDC_TX4_TX_PATH_192_CFG, 0x00 }, + { WCD934X_CDC_TX4_TX_PATH_SEC0, 0x00 }, + { WCD934X_CDC_TX4_TX_PATH_SEC1, 0x00 }, + { WCD934X_CDC_TX4_TX_PATH_SEC2, 0x01 }, + { WCD934X_CDC_TX4_TX_PATH_SEC3, 0x3c }, + { WCD934X_CDC_TX4_TX_PATH_SEC4, 0x20 }, + { WCD934X_CDC_TX4_TX_PATH_SEC5, 0x00 }, + { WCD934X_CDC_TX4_TX_PATH_SEC6, 0x00 }, + { WCD934X_CDC_TX5_TX_PATH_CTL, 0x04 }, + { WCD934X_CDC_TX5_TX_PATH_CFG0, 0x10 }, + { WCD934X_CDC_TX5_TX_PATH_CFG1, 0x03 }, + { WCD934X_CDC_TX5_TX_VOL_CTL, 0x00 }, + { WCD934X_CDC_TX5_TX_PATH_192_CTL, 0x00 }, + { WCD934X_CDC_TX5_TX_PATH_192_CFG, 0x00 }, + { WCD934X_CDC_TX5_TX_PATH_SEC0, 0x00 }, + { WCD934X_CDC_TX5_TX_PATH_SEC1, 0x00 }, + { WCD934X_CDC_TX5_TX_PATH_SEC2, 0x01 }, + { WCD934X_CDC_TX5_TX_PATH_SEC3, 0x3c }, + { WCD934X_CDC_TX5_TX_PATH_SEC4, 0x20 }, + { WCD934X_CDC_TX5_TX_PATH_SEC5, 0x00 }, + { WCD934X_CDC_TX5_TX_PATH_SEC6, 0x00 }, + { WCD934X_CDC_TX6_TX_PATH_CTL, 0x04 }, + { WCD934X_CDC_TX6_TX_PATH_CFG0, 0x10 }, + { WCD934X_CDC_TX6_TX_PATH_CFG1, 0x03 }, + { WCD934X_CDC_TX6_TX_VOL_CTL, 0x00 }, + { WCD934X_CDC_TX6_TX_PATH_192_CTL, 0x00 }, + { WCD934X_CDC_TX6_TX_PATH_192_CFG, 0x00 }, + { WCD934X_CDC_TX6_TX_PATH_SEC0, 0x00 }, + { WCD934X_CDC_TX6_TX_PATH_SEC1, 0x00 }, + { WCD934X_CDC_TX6_TX_PATH_SEC2, 0x01 }, + { WCD934X_CDC_TX6_TX_PATH_SEC3, 0x3c }, + { WCD934X_CDC_TX6_TX_PATH_SEC4, 0x20 }, + { WCD934X_CDC_TX6_TX_PATH_SEC5, 0x00 }, + { WCD934X_CDC_TX6_TX_PATH_SEC6, 0x00 }, + { WCD934X_CDC_TX7_TX_PATH_CTL, 0x04 }, + { WCD934X_CDC_TX7_TX_PATH_CFG0, 0x10 }, + { WCD934X_CDC_TX7_TX_PATH_CFG1, 0x03 }, + { WCD934X_CDC_TX7_TX_VOL_CTL, 0x00 }, + { WCD934X_CDC_TX7_TX_PATH_192_CTL, 0x00 }, + { WCD934X_CDC_TX7_TX_PATH_192_CFG, 0x00 }, + { WCD934X_CDC_TX7_TX_PATH_SEC0, 0x00 }, + { WCD934X_CDC_TX7_TX_PATH_SEC1, 0x00 }, + { WCD934X_CDC_TX7_TX_PATH_SEC2, 0x01 }, + { WCD934X_CDC_TX7_TX_PATH_SEC3, 0x3c }, + { WCD934X_CDC_TX7_TX_PATH_SEC4, 0x20 }, + { WCD934X_CDC_TX7_TX_PATH_SEC5, 0x00 }, + { WCD934X_CDC_TX7_TX_PATH_SEC6, 0x00 }, + { WCD934X_CDC_TX8_TX_PATH_CTL, 0x04 }, + { WCD934X_CDC_TX8_TX_PATH_CFG0, 0x10 }, + { WCD934X_CDC_TX8_TX_PATH_CFG1, 0x03 }, + { WCD934X_CDC_TX8_TX_VOL_CTL, 0x00 }, + { WCD934X_CDC_TX8_TX_PATH_192_CTL, 0x00 }, + { WCD934X_CDC_TX8_TX_PATH_192_CFG, 0x00 }, + { WCD934X_CDC_TX8_TX_PATH_SEC0, 0x00 }, + { WCD934X_CDC_TX8_TX_PATH_SEC1, 0x00 }, + { WCD934X_CDC_TX8_TX_PATH_SEC2, 0x01 }, + { WCD934X_CDC_TX8_TX_PATH_SEC3, 0x3c }, + { WCD934X_CDC_TX8_TX_PATH_SEC4, 0x20 }, + { WCD934X_CDC_TX8_TX_PATH_SEC5, 0x00 }, + { WCD934X_CDC_TX8_TX_PATH_SEC6, 0x00 }, + { WCD934X_CDC_TX9_SPKR_PROT_PATH_CTL, 0x02 }, + { WCD934X_CDC_TX9_SPKR_PROT_PATH_CFG0, 0x00 }, + { WCD934X_CDC_TX10_SPKR_PROT_PATH_CTL, 0x02 }, + { WCD934X_CDC_TX10_SPKR_PROT_PATH_CFG0, 0x00 }, + { WCD934X_CDC_TX11_SPKR_PROT_PATH_CTL, 0x02 }, + { WCD934X_CDC_TX11_SPKR_PROT_PATH_CFG0, 0x00 }, + { WCD934X_CDC_TX12_SPKR_PROT_PATH_CTL, 0x02 }, + { WCD934X_CDC_TX12_SPKR_PROT_PATH_CFG0, 0x00 }, + { WCD934X_PAGE11_PAGE_REGISTER, 0x00 }, + { WCD934X_CDC_COMPANDER1_CTL0, 0x60 }, + { WCD934X_CDC_COMPANDER1_CTL1, 0xdb }, + { WCD934X_CDC_COMPANDER1_CTL2, 0xff }, + { WCD934X_CDC_COMPANDER1_CTL3, 0x35 }, + { WCD934X_CDC_COMPANDER1_CTL4, 0xff }, + { WCD934X_CDC_COMPANDER1_CTL5, 0x00 }, + { WCD934X_CDC_COMPANDER1_CTL6, 0x01 }, + { WCD934X_CDC_COMPANDER1_CTL7, 0x08 }, + { WCD934X_CDC_COMPANDER2_CTL0, 0x60 }, + { WCD934X_CDC_COMPANDER2_CTL1, 0xdb }, + { WCD934X_CDC_COMPANDER2_CTL2, 0xff }, + { WCD934X_CDC_COMPANDER2_CTL3, 0x35 }, + { WCD934X_CDC_COMPANDER2_CTL4, 0xff }, + { WCD934X_CDC_COMPANDER2_CTL5, 0x00 }, + { WCD934X_CDC_COMPANDER2_CTL6, 0x01 }, + { WCD934X_CDC_COMPANDER2_CTL7, 0x08 }, + { WCD934X_CDC_COMPANDER3_CTL0, 0x60 }, + { WCD934X_CDC_COMPANDER3_CTL1, 0xdb }, + { WCD934X_CDC_COMPANDER3_CTL2, 0xff }, + { WCD934X_CDC_COMPANDER3_CTL3, 0x35 }, + { WCD934X_CDC_COMPANDER3_CTL4, 0xff }, + { WCD934X_CDC_COMPANDER3_CTL5, 0x00 }, + { WCD934X_CDC_COMPANDER3_CTL6, 0x01 }, + { WCD934X_CDC_COMPANDER3_CTL7, 0x08 }, + { WCD934X_CDC_COMPANDER4_CTL0, 0x60 }, + { WCD934X_CDC_COMPANDER4_CTL1, 0xdb }, + { WCD934X_CDC_COMPANDER4_CTL2, 0xff }, + { WCD934X_CDC_COMPANDER4_CTL3, 0x35 }, + { WCD934X_CDC_COMPANDER4_CTL4, 0xff }, + { WCD934X_CDC_COMPANDER4_CTL5, 0x00 }, + { WCD934X_CDC_COMPANDER4_CTL6, 0x01 }, + { WCD934X_CDC_COMPANDER4_CTL7, 0x08 }, + { WCD934X_CDC_COMPANDER7_CTL0, 0x60 }, + { WCD934X_CDC_COMPANDER7_CTL1, 0xdb }, + { WCD934X_CDC_COMPANDER7_CTL2, 0xff }, + { WCD934X_CDC_COMPANDER7_CTL3, 0x35 }, + { WCD934X_CDC_COMPANDER7_CTL4, 0xff }, + { WCD934X_CDC_COMPANDER7_CTL5, 0x00 }, + { WCD934X_CDC_COMPANDER7_CTL6, 0x01 }, + { WCD934X_CDC_COMPANDER7_CTL7, 0x08 }, + { WCD934X_CDC_COMPANDER8_CTL0, 0x60 }, + { WCD934X_CDC_COMPANDER8_CTL1, 0xdb }, + { WCD934X_CDC_COMPANDER8_CTL2, 0xff }, + { WCD934X_CDC_COMPANDER8_CTL3, 0x35 }, + { WCD934X_CDC_COMPANDER8_CTL4, 0xff }, + { WCD934X_CDC_COMPANDER8_CTL5, 0x00 }, + { WCD934X_CDC_COMPANDER8_CTL6, 0x01 }, + { WCD934X_CDC_COMPANDER8_CTL7, 0x08 }, + { WCD934X_CDC_RX0_RX_PATH_CTL, 0x04 }, + { WCD934X_CDC_RX0_RX_PATH_CFG0, 0x00 }, + { WCD934X_CDC_RX0_RX_PATH_CFG1, 0x64 }, + { WCD934X_CDC_RX0_RX_PATH_CFG2, 0x8f }, + { WCD934X_CDC_RX0_RX_VOL_CTL, 0x00 }, + { WCD934X_CDC_RX0_RX_PATH_MIX_CTL, 0x04 }, + { WCD934X_CDC_RX0_RX_PATH_MIX_CFG, 0x7e }, + { WCD934X_CDC_RX0_RX_VOL_MIX_CTL, 0x00 }, + { WCD934X_CDC_RX0_RX_PATH_SEC0, 0xfc }, + { WCD934X_CDC_RX0_RX_PATH_SEC1, 0x08 }, + { WCD934X_CDC_RX0_RX_PATH_SEC2, 0x00 }, + { WCD934X_CDC_RX0_RX_PATH_SEC3, 0x00 }, + { WCD934X_CDC_RX0_RX_PATH_SEC5, 0x00 }, + { WCD934X_CDC_RX0_RX_PATH_SEC6, 0x00 }, + { WCD934X_CDC_RX0_RX_PATH_SEC7, 0x00 }, + { WCD934X_CDC_RX0_RX_PATH_MIX_SEC0, 0x08 }, + { WCD934X_CDC_RX0_RX_PATH_MIX_SEC1, 0x00 }, + { WCD934X_CDC_RX0_RX_PATH_DSMDEM_CTL, 0x00 }, + { WCD934X_CDC_RX1_RX_PATH_CTL, 0x04 }, + { WCD934X_CDC_RX1_RX_PATH_CFG0, 0x00 }, + { WCD934X_CDC_RX1_RX_PATH_CFG1, 0x64 }, + { WCD934X_CDC_RX1_RX_PATH_CFG2, 0x8f }, + { WCD934X_CDC_RX1_RX_VOL_CTL, 0x00 }, + { WCD934X_CDC_RX1_RX_PATH_MIX_CTL, 0x04 }, + { WCD934X_CDC_RX1_RX_PATH_MIX_CFG, 0x7e }, + { WCD934X_CDC_RX1_RX_VOL_MIX_CTL, 0x00 }, + { WCD934X_CDC_RX1_RX_PATH_SEC0, 0xfc }, + { WCD934X_CDC_RX1_RX_PATH_SEC1, 0x08 }, + { WCD934X_CDC_RX1_RX_PATH_SEC2, 0x00 }, + { WCD934X_CDC_RX1_RX_PATH_SEC3, 0x00 }, + { WCD934X_CDC_RX1_RX_PATH_SEC4, 0x00 }, + { WCD934X_CDC_RX1_RX_PATH_SEC5, 0x00 }, + { WCD934X_CDC_RX1_RX_PATH_SEC6, 0x00 }, + { WCD934X_CDC_RX1_RX_PATH_SEC7, 0x00 }, + { WCD934X_CDC_RX1_RX_PATH_MIX_SEC0, 0x08 }, + { WCD934X_CDC_RX1_RX_PATH_MIX_SEC1, 0x00 }, + { WCD934X_CDC_RX1_RX_PATH_DSMDEM_CTL, 0x00 }, + { WCD934X_CDC_RX2_RX_PATH_CTL, 0x04 }, + { WCD934X_CDC_RX2_RX_PATH_CFG0, 0x00 }, + { WCD934X_CDC_RX2_RX_PATH_CFG1, 0x64 }, + { WCD934X_CDC_RX2_RX_PATH_CFG2, 0x8f }, + { WCD934X_CDC_RX2_RX_VOL_CTL, 0x00 }, + { WCD934X_CDC_RX2_RX_PATH_MIX_CTL, 0x04 }, + { WCD934X_CDC_RX2_RX_PATH_MIX_CFG, 0x7e }, + { WCD934X_CDC_RX2_RX_VOL_MIX_CTL, 0x00 }, + { WCD934X_CDC_RX2_RX_PATH_SEC0, 0xfc }, + { WCD934X_CDC_RX2_RX_PATH_SEC1, 0x08 }, + { WCD934X_CDC_RX2_RX_PATH_SEC2, 0x00 }, + { WCD934X_CDC_RX2_RX_PATH_SEC3, 0x00 }, + { WCD934X_CDC_RX2_RX_PATH_SEC4, 0x00 }, + { WCD934X_CDC_RX2_RX_PATH_SEC5, 0x00 }, + { WCD934X_CDC_RX2_RX_PATH_SEC6, 0x00 }, + { WCD934X_CDC_RX2_RX_PATH_SEC7, 0x00 }, + { WCD934X_CDC_RX2_RX_PATH_MIX_SEC0, 0x08 }, + { WCD934X_CDC_RX2_RX_PATH_MIX_SEC1, 0x00 }, + { WCD934X_CDC_RX2_RX_PATH_DSMDEM_CTL, 0x00 }, + { WCD934X_CDC_RX3_RX_PATH_CTL, 0x04 }, + { WCD934X_CDC_RX3_RX_PATH_CFG0, 0x00 }, + { WCD934X_CDC_RX3_RX_PATH_CFG1, 0x64 }, + { WCD934X_CDC_RX3_RX_PATH_CFG2, 0x8f }, + { WCD934X_CDC_RX3_RX_VOL_CTL, 0x00 }, + { WCD934X_CDC_RX3_RX_PATH_MIX_CTL, 0x04 }, + { WCD934X_CDC_RX3_RX_PATH_MIX_CFG, 0x7e }, + { WCD934X_CDC_RX3_RX_VOL_MIX_CTL, 0x00 }, + { WCD934X_CDC_RX3_RX_PATH_SEC0, 0xfc }, + { WCD934X_CDC_RX3_RX_PATH_SEC1, 0x08 }, + { WCD934X_CDC_RX3_RX_PATH_SEC2, 0x00 }, + { WCD934X_CDC_RX3_RX_PATH_SEC3, 0x00 }, + { WCD934X_CDC_RX3_RX_PATH_SEC5, 0x00 }, + { WCD934X_CDC_RX3_RX_PATH_SEC6, 0x00 }, + { WCD934X_CDC_RX3_RX_PATH_SEC7, 0x00 }, + { WCD934X_CDC_RX3_RX_PATH_MIX_SEC0, 0x08 }, + { WCD934X_CDC_RX3_RX_PATH_MIX_SEC1, 0x00 }, + { WCD934X_CDC_RX3_RX_PATH_DSMDEM_CTL, 0x00 }, + { WCD934X_CDC_RX4_RX_PATH_CTL, 0x04 }, + { WCD934X_CDC_RX4_RX_PATH_CFG0, 0x00 }, + { WCD934X_CDC_RX4_RX_PATH_CFG1, 0x64 }, + { WCD934X_CDC_RX4_RX_PATH_CFG2, 0x8f }, + { WCD934X_CDC_RX4_RX_VOL_CTL, 0x00 }, + { WCD934X_CDC_RX4_RX_PATH_MIX_CTL, 0x04 }, + { WCD934X_CDC_RX4_RX_PATH_MIX_CFG, 0x7e }, + { WCD934X_CDC_RX4_RX_VOL_MIX_CTL, 0x00 }, + { WCD934X_CDC_RX4_RX_PATH_SEC0, 0xfc }, + { WCD934X_CDC_RX4_RX_PATH_SEC1, 0x08 }, + { WCD934X_CDC_RX4_RX_PATH_SEC2, 0x00 }, + { WCD934X_CDC_RX4_RX_PATH_SEC3, 0x00 }, + { WCD934X_CDC_RX4_RX_PATH_SEC5, 0x00 }, + { WCD934X_CDC_RX4_RX_PATH_SEC6, 0x00 }, + { WCD934X_CDC_RX4_RX_PATH_SEC7, 0x00 }, + { WCD934X_CDC_RX4_RX_PATH_MIX_SEC0, 0x08 }, + { WCD934X_CDC_RX4_RX_PATH_MIX_SEC1, 0x00 }, + { WCD934X_CDC_RX4_RX_PATH_DSMDEM_CTL, 0x00 }, + { WCD934X_CDC_RX7_RX_PATH_CTL, 0x04 }, + { WCD934X_CDC_RX7_RX_PATH_CFG0, 0x00 }, + { WCD934X_CDC_RX7_RX_PATH_CFG1, 0x64 }, + { WCD934X_CDC_RX7_RX_PATH_CFG2, 0x8f }, + { WCD934X_CDC_RX7_RX_VOL_CTL, 0x00 }, + { WCD934X_CDC_RX7_RX_PATH_MIX_CTL, 0x04 }, + { WCD934X_CDC_RX7_RX_PATH_MIX_CFG, 0x7e }, + { WCD934X_CDC_RX7_RX_VOL_MIX_CTL, 0x00 }, + { WCD934X_CDC_RX7_RX_PATH_SEC0, 0x04 }, + { WCD934X_CDC_RX7_RX_PATH_SEC1, 0x08 }, + { WCD934X_CDC_RX7_RX_PATH_SEC2, 0x00 }, + { WCD934X_CDC_RX7_RX_PATH_SEC3, 0x00 }, + { WCD934X_CDC_RX7_RX_PATH_SEC5, 0x00 }, + { WCD934X_CDC_RX7_RX_PATH_SEC6, 0x00 }, + { WCD934X_CDC_RX7_RX_PATH_SEC7, 0x00 }, + { WCD934X_CDC_RX7_RX_PATH_MIX_SEC0, 0x08 }, + { WCD934X_CDC_RX7_RX_PATH_MIX_SEC1, 0x00 }, + { WCD934X_CDC_RX7_RX_PATH_DSMDEM_CTL, 0x00 }, + { WCD934X_CDC_RX8_RX_PATH_CTL, 0x04 }, + { WCD934X_CDC_RX8_RX_PATH_CFG0, 0x00 }, + { WCD934X_CDC_RX8_RX_PATH_CFG1, 0x64 }, + { WCD934X_CDC_RX8_RX_PATH_CFG2, 0x8f }, + { WCD934X_CDC_RX8_RX_VOL_CTL, 0x00 }, + { WCD934X_CDC_RX8_RX_PATH_MIX_CTL, 0x04 }, + { WCD934X_CDC_RX8_RX_PATH_MIX_CFG, 0x7e }, + { WCD934X_CDC_RX8_RX_VOL_MIX_CTL, 0x00 }, + { WCD934X_CDC_RX8_RX_PATH_SEC0, 0x04 }, + { WCD934X_CDC_RX8_RX_PATH_SEC1, 0x08 }, + { WCD934X_CDC_RX8_RX_PATH_SEC2, 0x00 }, + { WCD934X_CDC_RX8_RX_PATH_SEC3, 0x00 }, + { WCD934X_CDC_RX8_RX_PATH_SEC5, 0x00 }, + { WCD934X_CDC_RX8_RX_PATH_SEC6, 0x00 }, + { WCD934X_CDC_RX8_RX_PATH_SEC7, 0x00 }, + { WCD934X_CDC_RX8_RX_PATH_MIX_SEC0, 0x08 }, + { WCD934X_CDC_RX8_RX_PATH_MIX_SEC1, 0x00 }, + { WCD934X_CDC_RX8_RX_PATH_DSMDEM_CTL, 0x00 }, + { WCD934X_PAGE12_PAGE_REGISTER, 0x00 }, + { WCD934X_CDC_CLSH_CRC, 0x00 }, + { WCD934X_CDC_CLSH_DLY_CTRL, 0x03 }, + { WCD934X_CDC_CLSH_DECAY_CTRL, 0x02 }, + { WCD934X_CDC_CLSH_HPH_V_PA, 0x1c }, + { WCD934X_CDC_CLSH_EAR_V_PA, 0x39 }, + { WCD934X_CDC_CLSH_HPH_V_HD, 0x0c }, + { WCD934X_CDC_CLSH_EAR_V_HD, 0x0c }, + { WCD934X_CDC_CLSH_K1_MSB, 0x01 }, + { WCD934X_CDC_CLSH_K1_LSB, 0x00 }, + { WCD934X_CDC_CLSH_K2_MSB, 0x00 }, + { WCD934X_CDC_CLSH_K2_LSB, 0x80 }, + { WCD934X_CDC_CLSH_IDLE_CTRL, 0x00 }, + { WCD934X_CDC_CLSH_IDLE_HPH, 0x00 }, + { WCD934X_CDC_CLSH_IDLE_EAR, 0x00 }, + { WCD934X_CDC_CLSH_TEST0, 0x07 }, + { WCD934X_CDC_CLSH_TEST1, 0x00 }, + { WCD934X_CDC_CLSH_OVR_VREF, 0x00 }, + { WCD934X_CDC_BOOST0_BOOST_PATH_CTL, 0x00 }, + { WCD934X_CDC_BOOST0_BOOST_CTL, 0xb2 }, + { WCD934X_CDC_BOOST0_BOOST_CFG1, 0x00 }, + { WCD934X_CDC_BOOST0_BOOST_CFG2, 0x00 }, + { WCD934X_CDC_BOOST1_BOOST_PATH_CTL, 0x00 }, + { WCD934X_CDC_BOOST1_BOOST_CTL, 0xb2 }, + { WCD934X_CDC_BOOST1_BOOST_CFG1, 0x00 }, + { WCD934X_CDC_BOOST1_BOOST_CFG2, 0x00 }, + { WCD934X_CDC_VBAT_VBAT_PATH_CTL, 0x00 }, + { WCD934X_CDC_VBAT_VBAT_CFG, 0x1a }, + { WCD934X_CDC_VBAT_VBAT_ADC_CAL1, 0x00 }, + { WCD934X_CDC_VBAT_VBAT_ADC_CAL2, 0x00 }, + { WCD934X_CDC_VBAT_VBAT_ADC_CAL3, 0x04 }, + { WCD934X_CDC_VBAT_VBAT_PK_EST1, 0xe0 }, + { WCD934X_CDC_VBAT_VBAT_PK_EST2, 0x01 }, + { WCD934X_CDC_VBAT_VBAT_PK_EST3, 0x40 }, + { WCD934X_CDC_VBAT_VBAT_RF_PROC1, 0x2a }, + { WCD934X_CDC_VBAT_VBAT_RF_PROC2, 0x86 }, + { WCD934X_CDC_VBAT_VBAT_TAC1, 0x70 }, + { WCD934X_CDC_VBAT_VBAT_TAC2, 0x18 }, + { WCD934X_CDC_VBAT_VBAT_TAC3, 0x18 }, + { WCD934X_CDC_VBAT_VBAT_TAC4, 0x03 }, + { WCD934X_CDC_VBAT_VBAT_GAIN_UPD1, 0x01 }, + { WCD934X_CDC_VBAT_VBAT_GAIN_UPD2, 0x00 }, + { WCD934X_CDC_VBAT_VBAT_GAIN_UPD3, 0x64 }, + { WCD934X_CDC_VBAT_VBAT_GAIN_UPD4, 0x01 }, + { WCD934X_CDC_VBAT_VBAT_DEBUG1, 0x00 }, + { WCD934X_CDC_VBAT_VBAT_GAIN_UPD_MON, 0x00 }, + { WCD934X_CDC_VBAT_VBAT_GAIN_MON_VAL, 0x00 }, + { WCD934X_CDC_VBAT_VBAT_BAN, 0x0c }, + { WCD934X_MIXING_ASRC0_CLK_RST_CTL, 0x00 }, + { WCD934X_MIXING_ASRC0_CTL0, 0x00 }, + { WCD934X_MIXING_ASRC0_CTL1, 0x00 }, + { WCD934X_MIXING_ASRC0_FIFO_CTL, 0xa8 }, + { WCD934X_MIXING_ASRC0_STATUS_FMIN_CNTR_LSB, 0x00 }, + { WCD934X_MIXING_ASRC0_STATUS_FMIN_CNTR_MSB, 0x00 }, + { WCD934X_MIXING_ASRC0_STATUS_FMAX_CNTR_LSB, 0x00 }, + { WCD934X_MIXING_ASRC0_STATUS_FMAX_CNTR_MSB, 0x00 }, + { WCD934X_MIXING_ASRC0_STATUS_FIFO, 0x00 }, + { WCD934X_MIXING_ASRC1_CLK_RST_CTL, 0x00 }, + { WCD934X_MIXING_ASRC1_CTL0, 0x00 }, + { WCD934X_MIXING_ASRC1_CTL1, 0x00 }, + { WCD934X_MIXING_ASRC1_FIFO_CTL, 0xa8 }, + { WCD934X_MIXING_ASRC1_STATUS_FMIN_CNTR_LSB, 0x00 }, + { WCD934X_MIXING_ASRC1_STATUS_FMIN_CNTR_MSB, 0x00 }, + { WCD934X_MIXING_ASRC1_STATUS_FMAX_CNTR_LSB, 0x00 }, + { WCD934X_MIXING_ASRC1_STATUS_FMAX_CNTR_MSB, 0x00 }, + { WCD934X_MIXING_ASRC1_STATUS_FIFO, 0x00 }, + { WCD934X_MIXING_ASRC2_CLK_RST_CTL, 0x00 }, + { WCD934X_MIXING_ASRC2_CTL0, 0x00 }, + { WCD934X_MIXING_ASRC2_CTL1, 0x00 }, + { WCD934X_MIXING_ASRC2_FIFO_CTL, 0xa8 }, + { WCD934X_MIXING_ASRC2_STATUS_FMIN_CNTR_LSB, 0x00 }, + { WCD934X_MIXING_ASRC2_STATUS_FMIN_CNTR_MSB, 0x00 }, + { WCD934X_MIXING_ASRC2_STATUS_FMAX_CNTR_LSB, 0x00 }, + { WCD934X_MIXING_ASRC2_STATUS_FMAX_CNTR_MSB, 0x00 }, + { WCD934X_MIXING_ASRC2_STATUS_FIFO, 0x00 }, + { WCD934X_MIXING_ASRC3_CLK_RST_CTL, 0x00 }, + { WCD934X_MIXING_ASRC3_CTL0, 0x00 }, + { WCD934X_MIXING_ASRC3_CTL1, 0x00 }, + { WCD934X_MIXING_ASRC3_FIFO_CTL, 0xa8 }, + { WCD934X_MIXING_ASRC3_STATUS_FMIN_CNTR_LSB, 0x00 }, + { WCD934X_MIXING_ASRC3_STATUS_FMIN_CNTR_MSB, 0x00 }, + { WCD934X_MIXING_ASRC3_STATUS_FMAX_CNTR_LSB, 0x00 }, + { WCD934X_MIXING_ASRC3_STATUS_FMAX_CNTR_MSB, 0x00 }, + { WCD934X_MIXING_ASRC3_STATUS_FIFO, 0x00 }, + { WCD934X_SWR_AHB_BRIDGE_WR_DATA_0, 0x00 }, + { WCD934X_SWR_AHB_BRIDGE_WR_DATA_1, 0x00 }, + { WCD934X_SWR_AHB_BRIDGE_WR_DATA_2, 0x00 }, + { WCD934X_SWR_AHB_BRIDGE_WR_DATA_3, 0x00 }, + { WCD934X_SWR_AHB_BRIDGE_WR_ADDR_0, 0x00 }, + { WCD934X_SWR_AHB_BRIDGE_WR_ADDR_1, 0x00 }, + { WCD934X_SWR_AHB_BRIDGE_WR_ADDR_2, 0x00 }, + { WCD934X_SWR_AHB_BRIDGE_WR_ADDR_3, 0x00 }, + { WCD934X_SWR_AHB_BRIDGE_RD_ADDR_0, 0x00 }, + { WCD934X_SWR_AHB_BRIDGE_RD_ADDR_1, 0x00 }, + { WCD934X_SWR_AHB_BRIDGE_RD_ADDR_2, 0x00 }, + { WCD934X_SWR_AHB_BRIDGE_RD_ADDR_3, 0x00 }, + { WCD934X_SWR_AHB_BRIDGE_RD_DATA_0, 0x00 }, + { WCD934X_SWR_AHB_BRIDGE_RD_DATA_1, 0x00 }, + { WCD934X_SWR_AHB_BRIDGE_RD_DATA_2, 0x00 }, + { WCD934X_SWR_AHB_BRIDGE_RD_DATA_3, 0x00 }, + { WCD934X_SWR_AHB_BRIDGE_ACCESS_CFG, 0x0f }, + { WCD934X_SWR_AHB_BRIDGE_ACCESS_STATUS, 0x03 }, + { WCD934X_CDC_SIDETONE_SRC0_ST_SRC_PATH_CTL, 0x04 }, + { WCD934X_CDC_SIDETONE_SRC0_ST_SRC_PATH_CFG1, 0x00 }, + { WCD934X_CDC_SIDETONE_SRC1_ST_SRC_PATH_CTL, 0x04 }, + { WCD934X_CDC_SIDETONE_SRC1_ST_SRC_PATH_CFG1, 0x00 }, + { WCD934X_SIDETONE_ASRC0_CLK_RST_CTL, 0x00 }, + { WCD934X_SIDETONE_ASRC0_CTL0, 0x00 }, + { WCD934X_SIDETONE_ASRC0_CTL1, 0x00 }, + { WCD934X_SIDETONE_ASRC0_FIFO_CTL, 0xa8 }, + { WCD934X_SIDETONE_ASRC0_STATUS_FMIN_CNTR_LSB, 0x00 }, + { WCD934X_SIDETONE_ASRC0_STATUS_FMIN_CNTR_MSB, 0x00 }, + { WCD934X_SIDETONE_ASRC0_STATUS_FMAX_CNTR_LSB, 0x00 }, + { WCD934X_SIDETONE_ASRC0_STATUS_FMAX_CNTR_MSB, 0x00 }, + { WCD934X_SIDETONE_ASRC0_STATUS_FIFO, 0x00 }, + { WCD934X_SIDETONE_ASRC1_CLK_RST_CTL, 0x00 }, + { WCD934X_SIDETONE_ASRC1_CTL0, 0x00 }, + { WCD934X_SIDETONE_ASRC1_CTL1, 0x00 }, + { WCD934X_SIDETONE_ASRC1_FIFO_CTL, 0xa8 }, + { WCD934X_SIDETONE_ASRC1_STATUS_FMIN_CNTR_LSB, 0x00 }, + { WCD934X_SIDETONE_ASRC1_STATUS_FMIN_CNTR_MSB, 0x00 }, + { WCD934X_SIDETONE_ASRC1_STATUS_FMAX_CNTR_LSB, 0x00 }, + { WCD934X_SIDETONE_ASRC1_STATUS_FMAX_CNTR_MSB, 0x00 }, + { WCD934X_SIDETONE_ASRC1_STATUS_FIFO, 0x00 }, + { WCD934X_EC_REF_HQ0_EC_REF_HQ_PATH_CTL, 0x00 }, + { WCD934X_EC_REF_HQ0_EC_REF_HQ_CFG0, 0x01 }, + { WCD934X_EC_REF_HQ1_EC_REF_HQ_PATH_CTL, 0x00 }, + { WCD934X_EC_REF_HQ1_EC_REF_HQ_CFG0, 0x01 }, + { WCD934X_EC_ASRC0_CLK_RST_CTL, 0x00 }, + { WCD934X_EC_ASRC0_CTL0, 0x00 }, + { WCD934X_EC_ASRC0_CTL1, 0x00 }, + { WCD934X_EC_ASRC0_FIFO_CTL, 0xa8 }, + { WCD934X_EC_ASRC0_STATUS_FMIN_CNTR_LSB, 0x00 }, + { WCD934X_EC_ASRC0_STATUS_FMIN_CNTR_MSB, 0x00 }, + { WCD934X_EC_ASRC0_STATUS_FMAX_CNTR_LSB, 0x00 }, + { WCD934X_EC_ASRC0_STATUS_FMAX_CNTR_MSB, 0x00 }, + { WCD934X_EC_ASRC0_STATUS_FIFO, 0x00 }, + { WCD934X_EC_ASRC1_CLK_RST_CTL, 0x00 }, + { WCD934X_EC_ASRC1_CTL0, 0x00 }, + { WCD934X_EC_ASRC1_CTL1, 0x00 }, + { WCD934X_EC_ASRC1_FIFO_CTL, 0xa8 }, + { WCD934X_EC_ASRC1_STATUS_FMIN_CNTR_LSB, 0x00 }, + { WCD934X_EC_ASRC1_STATUS_FMIN_CNTR_MSB, 0x00 }, + { WCD934X_EC_ASRC1_STATUS_FMAX_CNTR_LSB, 0x00 }, + { WCD934X_EC_ASRC1_STATUS_FMAX_CNTR_MSB, 0x00 }, + { WCD934X_EC_ASRC1_STATUS_FIFO, 0x00 }, + { WCD934X_PAGE13_PAGE_REGISTER, 0x00 }, + { WCD934X_CDC_RX_INP_MUX_RX_INT0_CFG0, 0x00 }, + { WCD934X_CDC_RX_INP_MUX_RX_INT0_CFG1, 0x00 }, + { WCD934X_CDC_RX_INP_MUX_RX_INT1_CFG0, 0x00 }, + { WCD934X_CDC_RX_INP_MUX_RX_INT1_CFG1, 0x00 }, + { WCD934X_CDC_RX_INP_MUX_RX_INT2_CFG0, 0x00 }, + { WCD934X_CDC_RX_INP_MUX_RX_INT2_CFG1, 0x00 }, + { WCD934X_CDC_RX_INP_MUX_RX_INT3_CFG0, 0x00 }, + { WCD934X_CDC_RX_INP_MUX_RX_INT3_CFG1, 0x00 }, + { WCD934X_CDC_RX_INP_MUX_RX_INT4_CFG0, 0x00 }, + { WCD934X_CDC_RX_INP_MUX_RX_INT4_CFG1, 0x00 }, + { WCD934X_CDC_RX_INP_MUX_RX_INT7_CFG0, 0x00 }, + { WCD934X_CDC_RX_INP_MUX_RX_INT7_CFG1, 0x00 }, + { WCD934X_CDC_RX_INP_MUX_RX_INT8_CFG0, 0x00 }, + { WCD934X_CDC_RX_INP_MUX_RX_INT8_CFG1, 0x00 }, + { WCD934X_CDC_RX_INP_MUX_RX_MIX_CFG0, 0x00 }, + { WCD934X_CDC_RX_INP_MUX_RX_MIX_CFG1, 0x00 }, + { WCD934X_CDC_RX_INP_MUX_RX_MIX_CFG2, 0x00 }, + { WCD934X_CDC_RX_INP_MUX_RX_MIX_CFG3, 0x00 }, + { WCD934X_CDC_RX_INP_MUX_RX_MIX_CFG4, 0x00 }, + { WCD934X_CDC_RX_INP_MUX_SIDETONE_SRC_CFG0, 0x00 }, + { WCD934X_CDC_RX_INP_MUX_SIDETONE_SRC_CFG1, 0x00 }, + { WCD934X_CDC_RX_INP_MUX_ANC_CFG0, 0x00 }, + { WCD934X_CDC_RX_INP_MUX_SPLINE_ASRC_CFG0, 0x00 }, + { WCD934X_CDC_RX_INP_MUX_EC_REF_HQ_CFG0, 0x00 }, + { WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG0, 0x00 }, + { WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG1, 0x00 }, + { WCD934X_CDC_TX_INP_MUX_ADC_MUX1_CFG0, 0x00 }, + { WCD934X_CDC_TX_INP_MUX_ADC_MUX1_CFG1, 0x00 }, + { WCD934X_CDC_TX_INP_MUX_ADC_MUX2_CFG0, 0x00 }, + { WCD934X_CDC_TX_INP_MUX_ADC_MUX2_CFG1, 0x00 }, + { WCD934X_CDC_TX_INP_MUX_ADC_MUX3_CFG0, 0x00 }, + { WCD934X_CDC_TX_INP_MUX_ADC_MUX3_CFG1, 0x00 }, + { WCD934X_CDC_TX_INP_MUX_ADC_MUX4_CFG0, 0x00 }, + { WCD934X_CDC_TX_INP_MUX_ADC_MUX5_CFG0, 0x00 }, + { WCD934X_CDC_TX_INP_MUX_ADC_MUX6_CFG0, 0x00 }, + { WCD934X_CDC_TX_INP_MUX_ADC_MUX7_CFG0, 0x00 }, + { WCD934X_CDC_TX_INP_MUX_ADC_MUX8_CFG0, 0x00 }, + { WCD934X_CDC_TX_INP_MUX_ADC_MUX10_CFG0, 0x00 }, + { WCD934X_CDC_TX_INP_MUX_ADC_MUX11_CFG0, 0x00 }, + { WCD934X_CDC_TX_INP_MUX_ADC_MUX12_CFG0, 0x00 }, + { WCD934X_CDC_TX_INP_MUX_ADC_MUX13_CFG0, 0x00 }, + { WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG0, 0x00 }, + { WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG1, 0x00 }, + { WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG2, 0x00 }, + { WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG3, 0x00 }, + { WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG0, 0x00 }, + { WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG1, 0x00 }, + { WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG2, 0x00 }, + { WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG3, 0x00 }, + { WCD934X_CDC_IF_ROUTER_TX_MUX_CFG0, 0x00 }, + { WCD934X_CDC_IF_ROUTER_TX_MUX_CFG1, 0x00 }, + { WCD934X_CDC_IF_ROUTER_TX_MUX_CFG2, 0x00 }, + { WCD934X_CDC_IF_ROUTER_TX_MUX_CFG3, 0x00 }, + { WCD934X_CDC_CLK_RST_CTRL_MCLK_CONTROL, 0x00 }, + { WCD934X_CDC_CLK_RST_CTRL_FS_CNT_CONTROL, 0x0c }, + { WCD934X_CDC_CLK_RST_CTRL_SWR_CONTROL, 0x00 }, + { WCD934X_CDC_CLK_RST_CTRL_DSD_CONTROL, 0x00 }, + { WCD934X_CDC_CLK_RST_CTRL_ASRC_SHARE_CONTROL, 0x0f }, + { WCD934X_CDC_CLK_RST_CTRL_GFM_CONTROL, 0x00 }, + { WCD934X_CDC_PROX_DETECT_PROX_CTL, 0x08 }, + { WCD934X_CDC_PROX_DETECT_PROX_POLL_PERIOD0, 0x00 }, + { WCD934X_CDC_PROX_DETECT_PROX_POLL_PERIOD1, 0x4b }, + { WCD934X_CDC_PROX_DETECT_PROX_SIG_PATTERN_LSB, 0x00 }, + { WCD934X_CDC_PROX_DETECT_PROX_SIG_PATTERN_MSB, 0x00 }, + { WCD934X_CDC_PROX_DETECT_PROX_STATUS, 0x00 }, + { WCD934X_CDC_PROX_DETECT_PROX_TEST_CTRL, 0x00 }, + { WCD934X_CDC_PROX_DETECT_PROX_TEST_BUFF_LSB, 0x00 }, + { WCD934X_CDC_PROX_DETECT_PROX_TEST_BUFF_MSB, 0x00 }, + { WCD934X_CDC_PROX_DETECT_PROX_TEST_BUFF_LSB_RD, 0x00 }, + { WCD934X_CDC_PROX_DETECT_PROX_TEST_BUFF_MSB_RD, 0x00 }, + { WCD934X_CDC_PROX_DETECT_PROX_CTL_REPEAT_PAT, 0x00 }, + { WCD934X_CDC_SIDETONE_IIR0_IIR_PATH_CTL, 0x00 }, + { WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B1_CTL, 0x00 }, + { WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B2_CTL, 0x00 }, + { WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B3_CTL, 0x00 }, + { WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B4_CTL, 0x00 }, + { WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B5_CTL, 0x00 }, + { WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B6_CTL, 0x00 }, + { WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B7_CTL, 0x00 }, + { WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B8_CTL, 0x00 }, + { WCD934X_CDC_SIDETONE_IIR0_IIR_CTL, 0x40 }, + { WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_TIMER_CTL, 0x00 }, + { WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL, 0x00 }, + { WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL, 0x00 }, + { WCD934X_CDC_SIDETONE_IIR1_IIR_PATH_CTL, 0x00 }, + { WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B1_CTL, 0x00 }, + { WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B2_CTL, 0x00 }, + { WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B3_CTL, 0x00 }, + { WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B4_CTL, 0x00 }, + { WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B5_CTL, 0x00 }, + { WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B6_CTL, 0x00 }, + { WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B7_CTL, 0x00 }, + { WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B8_CTL, 0x00 }, + { WCD934X_CDC_SIDETONE_IIR1_IIR_CTL, 0x40 }, + { WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_TIMER_CTL, 0x00 }, + { WCD934X_CDC_SIDETONE_IIR1_IIR_COEF_B1_CTL, 0x00 }, + { WCD934X_CDC_SIDETONE_IIR1_IIR_COEF_B2_CTL, 0x00 }, + { WCD934X_CDC_TOP_TOP_CFG0, 0x00 }, + { WCD934X_CDC_TOP_TOP_CFG1, 0x00 }, + { WCD934X_CDC_TOP_TOP_CFG7, 0x00 }, + { WCD934X_CDC_TOP_HPHL_COMP_WR_LSB, 0x00 }, + { WCD934X_CDC_TOP_HPHL_COMP_WR_MSB, 0x00 }, + { WCD934X_CDC_TOP_HPHL_COMP_LUT, 0x00 }, + { WCD934X_CDC_TOP_HPHL_COMP_RD_LSB, 0x00 }, + { WCD934X_CDC_TOP_HPHL_COMP_RD_MSB, 0x00 }, + { WCD934X_CDC_TOP_HPHR_COMP_WR_LSB, 0x00 }, + { WCD934X_CDC_TOP_HPHR_COMP_WR_MSB, 0x00 }, + { WCD934X_CDC_TOP_HPHR_COMP_LUT, 0x00 }, + { WCD934X_CDC_TOP_HPHR_COMP_RD_LSB, 0x00 }, + { WCD934X_CDC_TOP_HPHR_COMP_RD_MSB, 0x00 }, + { WCD934X_CDC_TOP_DIFFL_COMP_WR_LSB, 0x00 }, + { WCD934X_CDC_TOP_DIFFL_COMP_WR_MSB, 0x00 }, + { WCD934X_CDC_TOP_DIFFL_COMP_LUT, 0x00 }, + { WCD934X_CDC_TOP_DIFFL_COMP_RD_LSB, 0x00 }, + { WCD934X_CDC_TOP_DIFFL_COMP_RD_MSB, 0x00 }, + { WCD934X_CDC_TOP_DIFFR_COMP_WR_LSB, 0x00 }, + { WCD934X_CDC_TOP_DIFFR_COMP_WR_MSB, 0x00 }, + { WCD934X_CDC_TOP_DIFFR_COMP_LUT, 0x00 }, + { WCD934X_CDC_TOP_DIFFR_COMP_RD_LSB, 0x00 }, + { WCD934X_CDC_TOP_DIFFR_COMP_RD_MSB, 0x00 }, + { WCD934X_CDC_DSD0_PATH_CTL, 0x00 }, + { WCD934X_CDC_DSD0_CFG0, 0x00 }, + { WCD934X_CDC_DSD0_CFG1, 0x00 }, + { WCD934X_CDC_DSD0_CFG2, 0x42 }, + { WCD934X_CDC_DSD0_CFG3, 0x00 }, + { WCD934X_CDC_DSD0_CFG4, 0x02 }, + { WCD934X_CDC_DSD0_CFG5, 0x00 }, + { WCD934X_CDC_DSD1_PATH_CTL, 0x00 }, + { WCD934X_CDC_DSD1_CFG0, 0x00 }, + { WCD934X_CDC_DSD1_CFG1, 0x00 }, + { WCD934X_CDC_DSD1_CFG2, 0x42 }, + { WCD934X_CDC_DSD1_CFG3, 0x00 }, + { WCD934X_CDC_DSD1_CFG4, 0x02 }, + { WCD934X_CDC_DSD1_CFG5, 0x00 }, + { WCD934X_CDC_RX_IDLE_DET_PATH_CTL, 0x00 }, + { WCD934X_CDC_RX_IDLE_DET_CFG0, 0x07 }, + { WCD934X_CDC_RX_IDLE_DET_CFG1, 0x3c }, + { WCD934X_CDC_RX_IDLE_DET_CFG2, 0x00 }, + { WCD934X_CDC_RX_IDLE_DET_CFG3, 0x00 }, + { WCD934X_PAGE14_PAGE_REGISTER, 0x00 }, + { WCD934X_CDC_RATE_EST0_RE_CLK_RST_CTL, 0x00 }, + { WCD934X_CDC_RATE_EST0_RE_CTL, 0x09 }, + { WCD934X_CDC_RATE_EST0_RE_PULSE_SUPR_CTL, 0x06 }, + { WCD934X_CDC_RATE_EST0_RE_TIMER, 0x01 }, + { WCD934X_CDC_RATE_EST0_RE_BW_SW, 0x20 }, + { WCD934X_CDC_RATE_EST0_RE_THRESH, 0xa0 }, + { WCD934X_CDC_RATE_EST0_RE_STATUS, 0x00 }, + { WCD934X_CDC_RATE_EST0_RE_DIAG_CTRL, 0x00 }, + { WCD934X_CDC_RATE_EST0_RE_DIAG_TIMER2, 0x00 }, + { WCD934X_CDC_RATE_EST0_RE_DIAG_OFFSET_BW1, 0x00 }, + { WCD934X_CDC_RATE_EST0_RE_DIAG_OFFSET_BW2, 0x00 }, + { WCD934X_CDC_RATE_EST0_RE_DIAG_OFFSET_BW3, 0x00 }, + { WCD934X_CDC_RATE_EST0_RE_DIAG_OFFSET_BW4, 0x00 }, + { WCD934X_CDC_RATE_EST0_RE_DIAG_OFFSET_BW5, 0x00 }, + { WCD934X_CDC_RATE_EST0_RE_DIAG_LIMIT_BW1, 0x08 }, + { WCD934X_CDC_RATE_EST0_RE_DIAG_LIMIT_BW2, 0x07 }, + { WCD934X_CDC_RATE_EST0_RE_DIAG_LIMIT_BW3, 0x05 }, + { WCD934X_CDC_RATE_EST0_RE_DIAG_LIMIT_BW4, 0x05 }, + { WCD934X_CDC_RATE_EST0_RE_DIAG_LIMIT_BW5, 0x05 }, + { WCD934X_CDC_RATE_EST0_RE_DIAG_LIMITD1_BW1, 0x08 }, + { WCD934X_CDC_RATE_EST0_RE_DIAG_LIMITD1_BW2, 0x07 }, + { WCD934X_CDC_RATE_EST0_RE_DIAG_LIMITD1_BW3, 0x05 }, + { WCD934X_CDC_RATE_EST0_RE_DIAG_LIMITD1_BW4, 0x05 }, + { WCD934X_CDC_RATE_EST0_RE_DIAG_LIMITD1_BW5, 0x05 }, + { WCD934X_CDC_RATE_EST0_RE_DIAG_HYST_BW1, 0x03 }, + { WCD934X_CDC_RATE_EST0_RE_DIAG_HYST_BW2, 0x03 }, + { WCD934X_CDC_RATE_EST0_RE_DIAG_HYST_BW3, 0x03 }, + { WCD934X_CDC_RATE_EST0_RE_DIAG_HYST_BW4, 0x03 }, + { WCD934X_CDC_RATE_EST0_RE_DIAG_HYST_BW5, 0x03 }, + { WCD934X_CDC_RATE_EST0_RE_RMAX_DIAG, 0x00 }, + { WCD934X_CDC_RATE_EST0_RE_RMIN_DIAG, 0x00 }, + { WCD934X_CDC_RATE_EST0_RE_PH_DET, 0x00 }, + { WCD934X_CDC_RATE_EST0_RE_DIAG_CLR, 0x00 }, + { WCD934X_CDC_RATE_EST0_RE_MB_SW_STATE, 0x00 }, + { WCD934X_CDC_RATE_EST0_RE_MAST_DIAG_STATE, 0x00 }, + { WCD934X_CDC_RATE_EST0_RE_RATE_OUT_7_0, 0x00 }, + { WCD934X_CDC_RATE_EST0_RE_RATE_OUT_15_8, 0x00 }, + { WCD934X_CDC_RATE_EST0_RE_RATE_OUT_23_16, 0x00 }, + { WCD934X_CDC_RATE_EST0_RE_RATE_OUT_31_24, 0x00 }, + { WCD934X_CDC_RATE_EST0_RE_RATE_OUT_39_32, 0x00 }, + { WCD934X_CDC_RATE_EST0_RE_RATE_OUT_40_43, 0x00 }, + { WCD934X_CDC_RATE_EST1_RE_CLK_RST_CTL, 0x00 }, + { WCD934X_CDC_RATE_EST1_RE_CTL, 0x09 }, + { WCD934X_CDC_RATE_EST1_RE_PULSE_SUPR_CTL, 0x06 }, + { WCD934X_CDC_RATE_EST1_RE_TIMER, 0x01 }, + { WCD934X_CDC_RATE_EST1_RE_BW_SW, 0x20 }, + { WCD934X_CDC_RATE_EST1_RE_THRESH, 0xa0 }, + { WCD934X_CDC_RATE_EST1_RE_STATUS, 0x00 }, + { WCD934X_CDC_RATE_EST1_RE_DIAG_CTRL, 0x00 }, + { WCD934X_CDC_RATE_EST1_RE_DIAG_TIMER2, 0x00 }, + { WCD934X_CDC_RATE_EST1_RE_DIAG_OFFSET_BW1, 0x00 }, + { WCD934X_CDC_RATE_EST1_RE_DIAG_OFFSET_BW2, 0x00 }, + { WCD934X_CDC_RATE_EST1_RE_DIAG_OFFSET_BW3, 0x00 }, + { WCD934X_CDC_RATE_EST1_RE_DIAG_OFFSET_BW4, 0x00 }, + { WCD934X_CDC_RATE_EST1_RE_DIAG_OFFSET_BW5, 0x00 }, + { WCD934X_CDC_RATE_EST1_RE_DIAG_LIMIT_BW1, 0x08 }, + { WCD934X_CDC_RATE_EST1_RE_DIAG_LIMIT_BW2, 0x07 }, + { WCD934X_CDC_RATE_EST1_RE_DIAG_LIMIT_BW3, 0x05 }, + { WCD934X_CDC_RATE_EST1_RE_DIAG_LIMIT_BW4, 0x05 }, + { WCD934X_CDC_RATE_EST1_RE_DIAG_LIMIT_BW5, 0x05 }, + { WCD934X_CDC_RATE_EST1_RE_DIAG_LIMITD1_BW1, 0x08 }, + { WCD934X_CDC_RATE_EST1_RE_DIAG_LIMITD1_BW2, 0x07 }, + { WCD934X_CDC_RATE_EST1_RE_DIAG_LIMITD1_BW3, 0x05 }, + { WCD934X_CDC_RATE_EST1_RE_DIAG_LIMITD1_BW4, 0x05 }, + { WCD934X_CDC_RATE_EST1_RE_DIAG_LIMITD1_BW5, 0x05 }, + { WCD934X_CDC_RATE_EST1_RE_DIAG_HYST_BW1, 0x03 }, + { WCD934X_CDC_RATE_EST1_RE_DIAG_HYST_BW2, 0x03 }, + { WCD934X_CDC_RATE_EST1_RE_DIAG_HYST_BW3, 0x03 }, + { WCD934X_CDC_RATE_EST1_RE_DIAG_HYST_BW4, 0x03 }, + { WCD934X_CDC_RATE_EST1_RE_DIAG_HYST_BW5, 0x03 }, + { WCD934X_CDC_RATE_EST1_RE_RMAX_DIAG, 0x00 }, + { WCD934X_CDC_RATE_EST1_RE_RMIN_DIAG, 0x00 }, + { WCD934X_CDC_RATE_EST1_RE_PH_DET, 0x00 }, + { WCD934X_CDC_RATE_EST1_RE_DIAG_CLR, 0x00 }, + { WCD934X_CDC_RATE_EST1_RE_MB_SW_STATE, 0x00 }, + { WCD934X_CDC_RATE_EST1_RE_MAST_DIAG_STATE, 0x00 }, + { WCD934X_CDC_RATE_EST1_RE_RATE_OUT_7_0, 0x00 }, + { WCD934X_CDC_RATE_EST1_RE_RATE_OUT_15_8, 0x00 }, + { WCD934X_CDC_RATE_EST1_RE_RATE_OUT_23_16, 0x00 }, + { WCD934X_CDC_RATE_EST1_RE_RATE_OUT_31_24, 0x00 }, + { WCD934X_CDC_RATE_EST1_RE_RATE_OUT_39_32, 0x00 }, + { WCD934X_CDC_RATE_EST1_RE_RATE_OUT_40_43, 0x00 }, + { WCD934X_CDC_RATE_EST2_RE_CLK_RST_CTL, 0x00 }, + { WCD934X_CDC_RATE_EST2_RE_CTL, 0x09 }, + { WCD934X_CDC_RATE_EST2_RE_PULSE_SUPR_CTL, 0x06 }, + { WCD934X_CDC_RATE_EST2_RE_TIMER, 0x01 }, + { WCD934X_CDC_RATE_EST2_RE_BW_SW, 0x20 }, + { WCD934X_CDC_RATE_EST2_RE_THRESH, 0xa0 }, + { WCD934X_CDC_RATE_EST2_RE_STATUS, 0x00 }, + { WCD934X_CDC_RATE_EST2_RE_DIAG_CTRL, 0x00 }, + { WCD934X_CDC_RATE_EST2_RE_DIAG_TIMER2, 0x00 }, + { WCD934X_CDC_RATE_EST2_RE_DIAG_OFFSET_BW1, 0x00 }, + { WCD934X_CDC_RATE_EST2_RE_DIAG_OFFSET_BW2, 0x00 }, + { WCD934X_CDC_RATE_EST2_RE_DIAG_OFFSET_BW3, 0x00 }, + { WCD934X_CDC_RATE_EST2_RE_DIAG_OFFSET_BW4, 0x00 }, + { WCD934X_CDC_RATE_EST2_RE_DIAG_OFFSET_BW5, 0x00 }, + { WCD934X_CDC_RATE_EST2_RE_DIAG_LIMIT_BW1, 0x08 }, + { WCD934X_CDC_RATE_EST2_RE_DIAG_LIMIT_BW2, 0x07 }, + { WCD934X_CDC_RATE_EST2_RE_DIAG_LIMIT_BW3, 0x05 }, + { WCD934X_CDC_RATE_EST2_RE_DIAG_LIMIT_BW4, 0x05 }, + { WCD934X_CDC_RATE_EST2_RE_DIAG_LIMIT_BW5, 0x05 }, + { WCD934X_CDC_RATE_EST2_RE_DIAG_LIMITD1_BW1, 0x08 }, + { WCD934X_CDC_RATE_EST2_RE_DIAG_LIMITD1_BW2, 0x07 }, + { WCD934X_CDC_RATE_EST2_RE_DIAG_LIMITD1_BW3, 0x05 }, + { WCD934X_CDC_RATE_EST2_RE_DIAG_LIMITD1_BW4, 0x05 }, + { WCD934X_CDC_RATE_EST2_RE_DIAG_LIMITD1_BW5, 0x05 }, + { WCD934X_CDC_RATE_EST2_RE_DIAG_HYST_BW1, 0x03 }, + { WCD934X_CDC_RATE_EST2_RE_DIAG_HYST_BW2, 0x03 }, + { WCD934X_CDC_RATE_EST2_RE_DIAG_HYST_BW3, 0x03 }, + { WCD934X_CDC_RATE_EST2_RE_DIAG_HYST_BW4, 0x03 }, + { WCD934X_CDC_RATE_EST2_RE_DIAG_HYST_BW5, 0x03 }, + { WCD934X_CDC_RATE_EST2_RE_RMAX_DIAG, 0x00 }, + { WCD934X_CDC_RATE_EST2_RE_RMIN_DIAG, 0x00 }, + { WCD934X_CDC_RATE_EST2_RE_PH_DET, 0x00 }, + { WCD934X_CDC_RATE_EST2_RE_DIAG_CLR, 0x00 }, + { WCD934X_CDC_RATE_EST2_RE_MB_SW_STATE, 0x00 }, + { WCD934X_CDC_RATE_EST2_RE_MAST_DIAG_STATE, 0x00 }, + { WCD934X_CDC_RATE_EST2_RE_RATE_OUT_7_0, 0x00 }, + { WCD934X_CDC_RATE_EST2_RE_RATE_OUT_15_8, 0x00 }, + { WCD934X_CDC_RATE_EST2_RE_RATE_OUT_23_16, 0x00 }, + { WCD934X_CDC_RATE_EST2_RE_RATE_OUT_31_24, 0x00 }, + { WCD934X_CDC_RATE_EST2_RE_RATE_OUT_39_32, 0x00 }, + { WCD934X_CDC_RATE_EST2_RE_RATE_OUT_40_43, 0x00 }, + { WCD934X_CDC_RATE_EST3_RE_CLK_RST_CTL, 0x00 }, + { WCD934X_CDC_RATE_EST3_RE_CTL, 0x09 }, + { WCD934X_CDC_RATE_EST3_RE_PULSE_SUPR_CTL, 0x06 }, + { WCD934X_CDC_RATE_EST3_RE_TIMER, 0x01 }, + { WCD934X_CDC_RATE_EST3_RE_BW_SW, 0x20 }, + { WCD934X_CDC_RATE_EST3_RE_THRESH, 0xa0 }, + { WCD934X_CDC_RATE_EST3_RE_STATUS, 0x00 }, + { WCD934X_CDC_RATE_EST3_RE_DIAG_CTRL, 0x00 }, + { WCD934X_CDC_RATE_EST3_RE_DIAG_TIMER2, 0x00 }, + { WCD934X_CDC_RATE_EST3_RE_DIAG_OFFSET_BW1, 0x00 }, + { WCD934X_CDC_RATE_EST3_RE_DIAG_OFFSET_BW2, 0x00 }, + { WCD934X_CDC_RATE_EST3_RE_DIAG_OFFSET_BW3, 0x00 }, + { WCD934X_CDC_RATE_EST3_RE_DIAG_OFFSET_BW4, 0x00 }, + { WCD934X_CDC_RATE_EST3_RE_DIAG_OFFSET_BW5, 0x00 }, + { WCD934X_CDC_RATE_EST3_RE_DIAG_LIMIT_BW1, 0x08 }, + { WCD934X_CDC_RATE_EST3_RE_DIAG_LIMIT_BW2, 0x07 }, + { WCD934X_CDC_RATE_EST3_RE_DIAG_LIMIT_BW3, 0x05 }, + { WCD934X_CDC_RATE_EST3_RE_DIAG_LIMIT_BW4, 0x05 }, + { WCD934X_CDC_RATE_EST3_RE_DIAG_LIMIT_BW5, 0x05 }, + { WCD934X_CDC_RATE_EST3_RE_DIAG_LIMITD1_BW1, 0x08 }, + { WCD934X_CDC_RATE_EST3_RE_DIAG_LIMITD1_BW2, 0x07 }, + { WCD934X_CDC_RATE_EST3_RE_DIAG_LIMITD1_BW3, 0x05 }, + { WCD934X_CDC_RATE_EST3_RE_DIAG_LIMITD1_BW4, 0x05 }, + { WCD934X_CDC_RATE_EST3_RE_DIAG_LIMITD1_BW5, 0x05 }, + { WCD934X_CDC_RATE_EST3_RE_DIAG_HYST_BW1, 0x03 }, + { WCD934X_CDC_RATE_EST3_RE_DIAG_HYST_BW2, 0x03 }, + { WCD934X_CDC_RATE_EST3_RE_DIAG_HYST_BW3, 0x03 }, + { WCD934X_CDC_RATE_EST3_RE_DIAG_HYST_BW4, 0x03 }, + { WCD934X_CDC_RATE_EST3_RE_DIAG_HYST_BW5, 0x03 }, + { WCD934X_CDC_RATE_EST3_RE_RMAX_DIAG, 0x00 }, + { WCD934X_CDC_RATE_EST3_RE_RMIN_DIAG, 0x00 }, + { WCD934X_CDC_RATE_EST3_RE_PH_DET, 0x00 }, + { WCD934X_CDC_RATE_EST3_RE_DIAG_CLR, 0x00 }, + { WCD934X_CDC_RATE_EST3_RE_MB_SW_STATE, 0x00 }, + { WCD934X_CDC_RATE_EST3_RE_MAST_DIAG_STATE, 0x00 }, + { WCD934X_CDC_RATE_EST3_RE_RATE_OUT_7_0, 0x00 }, + { WCD934X_CDC_RATE_EST3_RE_RATE_OUT_15_8, 0x00 }, + { WCD934X_CDC_RATE_EST3_RE_RATE_OUT_23_16, 0x00 }, + { WCD934X_CDC_RATE_EST3_RE_RATE_OUT_31_24, 0x00 }, + { WCD934X_CDC_RATE_EST3_RE_RATE_OUT_39_32, 0x00 }, + { WCD934X_CDC_RATE_EST3_RE_RATE_OUT_40_43, 0x00 }, + { WCD934X_PAGE15_PAGE_REGISTER, 0x00 }, + { WCD934X_SPLINE_SRC0_CLK_RST_CTL_0, 0x20 }, + { WCD934X_SPLINE_SRC0_STATUS, 0x00 }, + { WCD934X_SPLINE_SRC1_CLK_RST_CTL_0, 0x20 }, + { WCD934X_SPLINE_SRC1_STATUS, 0x00 }, + { WCD934X_SPLINE_SRC2_CLK_RST_CTL_0, 0x20 }, + { WCD934X_SPLINE_SRC2_STATUS, 0x00 }, + { WCD934X_SPLINE_SRC3_CLK_RST_CTL_0, 0x20 }, + { WCD934X_SPLINE_SRC3_STATUS, 0x00 }, + { WCD934X_CDC_DEBUG_DSD0_DEBUG_CFG0, 0x11 }, + { WCD934X_CDC_DEBUG_DSD0_DEBUG_CFG1, 0x20 }, + { WCD934X_CDC_DEBUG_DSD0_DEBUG_CFG2, 0x00 }, + { WCD934X_CDC_DEBUG_DSD0_DEBUG_CFG3, 0x08 }, + { WCD934X_CDC_DEBUG_DSD1_DEBUG_CFG0, 0x11 }, + { WCD934X_CDC_DEBUG_DSD1_DEBUG_CFG1, 0x20 }, + { WCD934X_CDC_DEBUG_DSD1_DEBUG_CFG2, 0x00 }, + { WCD934X_CDC_DEBUG_DSD1_DEBUG_CFG3, 0x08 }, + { WCD934X_CDC_DEBUG_SPLINE_SRC_DEBUG_CFG0, 0x00 }, + { WCD934X_CDC_DEBUG_SPLINE_SRC_DEBUG_CFG1, 0x00 }, + { WCD934X_CDC_DEBUG_RC_RE_ASRC_DEBUG_CFG0, 0x00 }, + { WCD934X_CDC_DEBUG_ANC0_RC0_FIFO_CTL, 0x4c }, + { WCD934X_CDC_DEBUG_ANC0_RC1_FIFO_CTL, 0x4c }, + { WCD934X_CDC_DEBUG_ANC1_RC0_FIFO_CTL, 0x4c }, + { WCD934X_CDC_DEBUG_ANC1_RC1_FIFO_CTL, 0x4c }, + { WCD934X_CDC_DEBUG_ANC_RC_RST_DBG_CNTR, 0x00 }, + { WCD934X_PAGE80_PAGE_REGISTER, 0x00 }, + { WCD934X_CODEC_CPR_WR_DATA_0, 0x00 }, + { WCD934X_CODEC_CPR_WR_DATA_1, 0x00 }, + { WCD934X_CODEC_CPR_WR_DATA_2, 0x00 }, + { WCD934X_CODEC_CPR_WR_DATA_3, 0x00 }, + { WCD934X_CODEC_CPR_WR_ADDR_0, 0x00 }, + { WCD934X_CODEC_CPR_WR_ADDR_1, 0x00 }, + { WCD934X_CODEC_CPR_WR_ADDR_2, 0x00 }, + { WCD934X_CODEC_CPR_WR_ADDR_3, 0x00 }, + { WCD934X_CODEC_CPR_RD_ADDR_0, 0x00 }, + { WCD934X_CODEC_CPR_RD_ADDR_1, 0x00 }, + { WCD934X_CODEC_CPR_RD_ADDR_2, 0x00 }, + { WCD934X_CODEC_CPR_RD_ADDR_3, 0x00 }, + { WCD934X_CODEC_CPR_RD_DATA_0, 0x00 }, + { WCD934X_CODEC_CPR_RD_DATA_1, 0x00 }, + { WCD934X_CODEC_CPR_RD_DATA_2, 0x00 }, + { WCD934X_CODEC_CPR_RD_DATA_3, 0x00 }, + { WCD934X_CODEC_CPR_ACCESS_CFG, 0x0f }, + { WCD934X_CODEC_CPR_ACCESS_STATUS, 0x03 }, + { WCD934X_CODEC_CPR_NOM_CX_VDD, 0xb4 }, + { WCD934X_CODEC_CPR_SVS_CX_VDD, 0x5c }, + { WCD934X_CODEC_CPR_SVS2_CX_VDD, 0x40 }, + { WCD934X_CODEC_CPR_NOM_MX_VDD, 0xb4 }, + { WCD934X_CODEC_CPR_SVS_MX_VDD, 0xb4 }, + { WCD934X_CODEC_CPR_SVS2_MX_VDD, 0xa0 }, + { WCD934X_CODEC_CPR_SVS2_MIN_CX_VDD, 0x28 }, + { WCD934X_CODEC_CPR_MAX_SVS2_STEP, 0x08 }, + { WCD934X_CODEC_CPR_CTL, 0x00 }, + { WCD934X_CODEC_CPR_SW_MODECHNG_STATUS, 0x00 }, + { WCD934X_CODEC_CPR_SW_MODECHNG_START, 0x00 }, + { WCD934X_CODEC_CPR_CPR_STATUS, 0x00 }, + { WCD934X_PAGE128_PAGE_REGISTER, 0x00 }, + { WCD934X_TLMM_BIST_MODE_PINCFG, 0x00 }, + { WCD934X_TLMM_RF_PA_ON_PINCFG, 0x00 }, + { WCD934X_TLMM_INTR1_PINCFG, 0x00 }, + { WCD934X_TLMM_INTR2_PINCFG, 0x00 }, + { WCD934X_TLMM_SWR_DATA_PINCFG, 0x00 }, + { WCD934X_TLMM_SWR_CLK_PINCFG, 0x00 }, + { WCD934X_TLMM_I2S_2_SCK_PINCFG, 0x00 }, + { WCD934X_TLMM_SLIMBUS_DATA1_PINCFG, 0x00 }, + { WCD934X_TLMM_SLIMBUS_DATA2_PINCFG, 0x00 }, + { WCD934X_TLMM_SLIMBUS_CLK_PINCFG, 0x00 }, + { WCD934X_TLMM_I2C_CLK_PINCFG, 0x00 }, + { WCD934X_TLMM_I2C_DATA_PINCFG, 0x00 }, + { WCD934X_TLMM_I2S_0_RX_PINCFG, 0x00 }, + { WCD934X_TLMM_I2S_0_TX_PINCFG, 0x00 }, + { WCD934X_TLMM_I2S_0_SCK_PINCFG, 0x00 }, + { WCD934X_TLMM_I2S_0_WS_PINCFG, 0x00 }, + { WCD934X_TLMM_I2S_1_RX_PINCFG, 0x00 }, + { WCD934X_TLMM_I2S_1_TX_PINCFG, 0x00 }, + { WCD934X_TLMM_I2S_1_SCK_PINCFG, 0x00 }, + { WCD934X_TLMM_I2S_1_WS_PINCFG, 0x00 }, + { WCD934X_TLMM_DMIC1_CLK_PINCFG, 0x00 }, + { WCD934X_TLMM_DMIC1_DATA_PINCFG, 0x00 }, + { WCD934X_TLMM_DMIC2_CLK_PINCFG, 0x00 }, + { WCD934X_TLMM_DMIC2_DATA_PINCFG, 0x00 }, + { WCD934X_TLMM_DMIC3_CLK_PINCFG, 0x00 }, + { WCD934X_TLMM_DMIC3_DATA_PINCFG, 0x00 }, + { WCD934X_TLMM_JTCK_PINCFG, 0x00 }, + { WCD934X_TLMM_GPIO1_PINCFG, 0x00 }, + { WCD934X_TLMM_GPIO2_PINCFG, 0x00 }, + { WCD934X_TLMM_GPIO3_PINCFG, 0x00 }, + { WCD934X_TLMM_GPIO4_PINCFG, 0x00 }, + { WCD934X_TLMM_SPI_S_CSN_PINCFG, 0x00 }, + { WCD934X_TLMM_SPI_S_CLK_PINCFG, 0x00 }, + { WCD934X_TLMM_SPI_S_DOUT_PINCFG, 0x00 }, + { WCD934X_TLMM_SPI_S_DIN_PINCFG, 0x00 }, + { WCD934X_TLMM_BA_N_PINCFG, 0x00 }, + { WCD934X_TLMM_GPIO0_PINCFG, 0x00 }, + { WCD934X_TLMM_I2S_2_RX_PINCFG, 0x00 }, + { WCD934X_TLMM_I2S_2_WS_PINCFG, 0x00 }, + { WCD934X_TEST_DEBUG_PIN_CTL_OE_0, 0x00 }, + { WCD934X_TEST_DEBUG_PIN_CTL_OE_1, 0x00 }, + { WCD934X_TEST_DEBUG_PIN_CTL_OE_2, 0x00 }, + { WCD934X_TEST_DEBUG_PIN_CTL_OE_3, 0x00 }, + { WCD934X_TEST_DEBUG_PIN_CTL_OE_4, 0x00 }, + { WCD934X_TEST_DEBUG_PIN_CTL_DATA_0, 0x00 }, + { WCD934X_TEST_DEBUG_PIN_CTL_DATA_1, 0x00 }, + { WCD934X_TEST_DEBUG_PIN_CTL_DATA_2, 0x00 }, + { WCD934X_TEST_DEBUG_PIN_CTL_DATA_3, 0x00 }, + { WCD934X_TEST_DEBUG_PIN_CTL_DATA_4, 0x00 }, + { WCD934X_TEST_DEBUG_PAD_DRVCTL_0, 0x00 }, + { WCD934X_TEST_DEBUG_PAD_DRVCTL_1, 0x00 }, + { WCD934X_TEST_DEBUG_PIN_STATUS, 0x00 }, + { WCD934X_TEST_DEBUG_NPL_DLY_TEST_1, 0x10 }, + { WCD934X_TEST_DEBUG_NPL_DLY_TEST_2, 0x60 }, + { WCD934X_TEST_DEBUG_MEM_CTRL, 0x00 }, + { WCD934X_TEST_DEBUG_DEBUG_BUS_SEL, 0x00 }, + { WCD934X_TEST_DEBUG_DEBUG_JTAG, 0x00 }, + { WCD934X_TEST_DEBUG_DEBUG_EN_1, 0x00 }, + { WCD934X_TEST_DEBUG_DEBUG_EN_2, 0x00 }, + { WCD934X_TEST_DEBUG_DEBUG_EN_3, 0x00 }, + { WCD934X_TEST_DEBUG_DEBUG_EN_4, 0x00 }, + { WCD934X_TEST_DEBUG_DEBUG_EN_5, 0x00 }, + { WCD934X_TEST_DEBUG_ANA_DTEST_DIR, 0x00 }, + { WCD934X_TEST_DEBUG_PAD_INP_DISABLE_0, 0x00 }, + { WCD934X_TEST_DEBUG_PAD_INP_DISABLE_1, 0x00 }, + { WCD934X_TEST_DEBUG_PAD_INP_DISABLE_2, 0x00 }, + { WCD934X_TEST_DEBUG_PAD_INP_DISABLE_3, 0x00 }, + { WCD934X_TEST_DEBUG_PAD_INP_DISABLE_4, 0x00 }, + { WCD934X_TEST_DEBUG_SYSMEM_CTRL, 0x00 }, + { WCD934X_TEST_DEBUG_SOC_SW_PWR_SEQ_DELAY, 0x00 }, + { WCD934X_TEST_DEBUG_LVAL_NOM_LOW, 0x96 }, + { WCD934X_TEST_DEBUG_LVAL_NOM_HIGH, 0x00 }, + { WCD934X_TEST_DEBUG_LVAL_SVS_SVS2_LOW, 0x53 }, + { WCD934X_TEST_DEBUG_LVAL_SVS_SVS2_HIGH, 0x00 }, + { WCD934X_TEST_DEBUG_SPI_SLAVE_CHAR, 0x00 }, + { WCD934X_TEST_DEBUG_CODEC_DIAGS, 0x00 }, +}; + +/* + * wcd934x_regmap_register_patch: Update register defaults based on version + * @regmap: handle to wcd9xxx regmap + * @version: wcd934x version + * + * Returns error code in case of failure or 0 for success + */ +int wcd934x_regmap_register_patch(struct regmap *regmap, int revision) +{ + int rc = 0; + + if (!regmap) { + pr_err("%s: regmap struct is NULL\n", __func__); + return -EINVAL; + } + + switch (revision) { + case TAVIL_VERSION_1_1: + case TAVIL_VERSION_WCD9340_1_1: + case TAVIL_VERSION_WCD9341_1_1: + regcache_cache_only(regmap, true); + rc = regmap_multi_reg_write(regmap, wcd934x_1_1_defaults, + ARRAY_SIZE(wcd934x_1_1_defaults)); + regcache_cache_only(regmap, false); + break; + } + + return rc; +} +EXPORT_SYMBOL(wcd934x_regmap_register_patch); + +static bool wcd934x_is_readable_register(struct device *dev, unsigned int reg) +{ + u8 pg_num, reg_offset; + const u8 *reg_tbl = NULL; + + /* + * Get the page number from MSB of codec register. If its 0x80, assign + * the corresponding page index PAGE_0x80. + */ + pg_num = reg >> 0x8; + if (pg_num == 0x80) + pg_num = WCD934X_PAGE_0X80; + else if (pg_num == 0x50) + pg_num = WCD934X_PAGE_0x50; + else if (pg_num > 0xF) + return false; + + reg_tbl = wcd934x_reg[pg_num]; + reg_offset = reg & 0xFF; + + if (reg_tbl && reg_tbl[reg_offset]) + return true; + else + return false; +} + +static bool wcd934x_is_volatile_register(struct device *dev, unsigned int reg) +{ + u8 pg_num, reg_offset; + const u8 *reg_tbl = NULL; + + pg_num = reg >> 0x8; + if (pg_num == 0x80) + pg_num = WCD934X_PAGE_0X80; + else if (pg_num == 0x50) + pg_num = WCD934X_PAGE_0x50; + else if (pg_num > 0xF) + return false; + + reg_tbl = wcd934x_reg[pg_num]; + reg_offset = reg & 0xFF; + + if (reg_tbl && reg_tbl[reg_offset] == WCD934X_READ) + return true; + + /* IIR Coeff registers are not cacheable */ + if ((reg >= WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL) && + (reg <= WCD934X_CDC_SIDETONE_IIR1_IIR_COEF_B2_CTL)) + return true; + + if ((reg >= WCD934X_CDC_ANC0_IIR_COEFF_1_CTL) && + (reg <= WCD934X_CDC_ANC0_FB_GAIN_CTL)) + return true; + + if ((reg >= WCD934X_CDC_ANC1_IIR_COEFF_1_CTL) && + (reg <= WCD934X_CDC_ANC1_FB_GAIN_CTL)) + return true; + + if ((reg >= WCD934X_CODEC_CPR_WR_DATA_0) && + (reg <= WCD934X_CODEC_CPR_RD_DATA_3)) + return true; + + /* + * Need to mark volatile for registers that are writable but + * only few bits are read-only + */ + switch (reg) { + case WCD934X_CPE_SS_SOC_SW_COLLAPSE_CTL: + case WCD934X_CPE_SS_PWR_SYS_PSTATE_CTL_0: + case WCD934X_CPE_SS_PWR_SYS_PSTATE_CTL_1: + case WCD934X_CPE_SS_CPAR_CTL: + case WCD934X_CPE_SS_STATUS: + case WCD934X_CODEC_RPM_RST_CTL: + case WCD934X_SIDO_NEW_VOUT_A_STARTUP: + case WCD934X_SIDO_NEW_VOUT_D_STARTUP: + case WCD934X_CODEC_RPM_PWR_CDC_DIG_HM_CTL: + case WCD934X_ANA_MBHC_MECH: + case WCD934X_ANA_MBHC_ELECT: + case WCD934X_ANA_MBHC_ZDET: + case WCD934X_ANA_MICB2: + case WCD934X_CODEC_RPM_CLK_MCLK_CFG: + case WCD934X_CLK_SYS_MCLK_PRG: + case WCD934X_CHIP_TIER_CTRL_EFUSE_CTL: + case WCD934X_ANA_BIAS: + case WCD934X_ANA_BUCK_CTL: + case WCD934X_ANA_RCO: + case WCD934X_CODEC_RPM_CLK_GATE: + case WCD934X_BIAS_VBG_FINE_ADJ: + case WCD934X_CODEC_CPR_SVS_CX_VDD: + case WCD934X_CODEC_CPR_SVS2_CX_VDD: + return true; + } + + return false; +} + +struct regmap_config wcd934x_regmap_config = { + .reg_bits = 16, + .val_bits = 8, + .cache_type = REGCACHE_RBTREE, + .reg_defaults = wcd934x_defaults, + .num_reg_defaults = ARRAY_SIZE(wcd934x_defaults), + .max_register = WCD934X_MAX_REGISTER, + .volatile_reg = wcd934x_is_volatile_register, + .readable_reg = wcd934x_is_readable_register, + .can_multi_write = true, +}; diff --git a/audio-kernel/asoc/codecs/wcd934x/wcd934x-routing.h b/audio-kernel/asoc/codecs/wcd934x/wcd934x-routing.h new file mode 100644 index 000000000..8adeef79a --- /dev/null +++ b/audio-kernel/asoc/codecs/wcd934x/wcd934x-routing.h @@ -0,0 +1,1306 @@ +/* + * 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 + * 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 WCD934X_ROUTING_H +#define WCD934X_ROUTING_H + +#include + +const struct snd_soc_dapm_route tavil_slim_audio_map[] = { + + {"AIF4 MAD", NULL, "AIF4_MAD Mixer"}, + + /* Virtual input widget Mixer SLIMBUS*/ + {"AIF1_CAP Mixer", "SLIM TX0", "SLIM TX0"}, + {"AIF1_CAP Mixer", "SLIM TX1", "SLIM TX1"}, + {"AIF1_CAP Mixer", "SLIM TX2", "SLIM TX2"}, + {"AIF1_CAP Mixer", "SLIM TX3", "SLIM TX3"}, + {"AIF1_CAP Mixer", "SLIM TX4", "SLIM TX4"}, + {"AIF1_CAP Mixer", "SLIM TX5", "SLIM TX5"}, + {"AIF1_CAP Mixer", "SLIM TX6", "SLIM TX6"}, + {"AIF1_CAP Mixer", "SLIM TX7", "SLIM TX7"}, + {"AIF1_CAP Mixer", "SLIM TX8", "SLIM TX8"}, + {"AIF1_CAP Mixer", "SLIM TX9", "SLIM TX9"}, + {"AIF1_CAP Mixer", "SLIM TX10", "SLIM TX10"}, + {"AIF1_CAP Mixer", "SLIM TX11", "SLIM TX11"}, + {"AIF1_CAP Mixer", "SLIM TX13", "SLIM TX13"}, + + {"AIF2_CAP Mixer", "SLIM TX0", "SLIM TX0"}, + {"AIF2_CAP Mixer", "SLIM TX1", "SLIM TX1"}, + {"AIF2_CAP Mixer", "SLIM TX2", "SLIM TX2"}, + {"AIF2_CAP Mixer", "SLIM TX3", "SLIM TX3"}, + {"AIF2_CAP Mixer", "SLIM TX4", "SLIM TX4"}, + {"AIF2_CAP Mixer", "SLIM TX5", "SLIM TX5"}, + {"AIF2_CAP Mixer", "SLIM TX6", "SLIM TX6"}, + {"AIF2_CAP Mixer", "SLIM TX7", "SLIM TX7"}, + {"AIF2_CAP Mixer", "SLIM TX8", "SLIM TX8"}, + {"AIF2_CAP Mixer", "SLIM TX9", "SLIM TX9"}, + {"AIF2_CAP Mixer", "SLIM TX10", "SLIM TX10"}, + {"AIF2_CAP Mixer", "SLIM TX11", "SLIM TX11"}, + {"AIF2_CAP Mixer", "SLIM TX13", "SLIM TX13"}, + + {"AIF3_CAP Mixer", "SLIM TX0", "SLIM TX0"}, + {"AIF3_CAP Mixer", "SLIM TX1", "SLIM TX1"}, + {"AIF3_CAP Mixer", "SLIM TX2", "SLIM TX2"}, + {"AIF3_CAP Mixer", "SLIM TX3", "SLIM TX3"}, + {"AIF3_CAP Mixer", "SLIM TX4", "SLIM TX4"}, + {"AIF3_CAP Mixer", "SLIM TX5", "SLIM TX5"}, + {"AIF3_CAP Mixer", "SLIM TX6", "SLIM TX6"}, + {"AIF3_CAP Mixer", "SLIM TX7", "SLIM TX7"}, + {"AIF3_CAP Mixer", "SLIM TX8", "SLIM TX8"}, + {"AIF3_CAP Mixer", "SLIM TX9", "SLIM TX9"}, + {"AIF3_CAP Mixer", "SLIM TX10", "SLIM TX10"}, + {"AIF3_CAP Mixer", "SLIM TX11", "SLIM TX11"}, + {"AIF3_CAP Mixer", "SLIM TX13", "SLIM TX13"}, + + {"AIF4_MAD Mixer", "SLIM TX13", "SLIM TX13"}, + + /* CDC Tx interface with SLIMBUS */ + {"SLIM TX0", NULL, "CDC_IF TX0 MUX"}, + {"SLIM TX1", NULL, "CDC_IF TX1 MUX"}, + {"SLIM TX2", NULL, "CDC_IF TX2 MUX"}, + {"SLIM TX3", NULL, "CDC_IF TX3 MUX"}, + {"SLIM TX4", NULL, "CDC_IF TX4 MUX"}, + {"SLIM TX5", NULL, "CDC_IF TX5 MUX"}, + {"SLIM TX6", NULL, "CDC_IF TX6 MUX"}, + {"SLIM TX7", NULL, "CDC_IF TX7 MUX"}, + {"SLIM TX8", NULL, "CDC_IF TX8 MUX"}, + {"SLIM TX9", NULL, "CDC_IF TX9 MUX"}, + {"SLIM TX10", NULL, "CDC_IF TX10 MUX"}, + {"SLIM TX11", NULL, "CDC_IF TX11 MUX"}, + {"SLIM TX13", NULL, "CDC_IF TX13 MUX"}, + + {"SLIM RX0 MUX", "AIF1_PB", "AIF1 PB"}, + {"SLIM RX1 MUX", "AIF1_PB", "AIF1 PB"}, + {"SLIM RX2 MUX", "AIF1_PB", "AIF1 PB"}, + {"SLIM RX3 MUX", "AIF1_PB", "AIF1 PB"}, + {"SLIM RX4 MUX", "AIF1_PB", "AIF1 PB"}, + {"SLIM RX5 MUX", "AIF1_PB", "AIF1 PB"}, + {"SLIM RX6 MUX", "AIF1_PB", "AIF1 PB"}, + {"SLIM RX7 MUX", "AIF1_PB", "AIF1 PB"}, + + {"SLIM RX0 MUX", "AIF2_PB", "AIF2 PB"}, + {"SLIM RX1 MUX", "AIF2_PB", "AIF2 PB"}, + {"SLIM RX2 MUX", "AIF2_PB", "AIF2 PB"}, + {"SLIM RX3 MUX", "AIF2_PB", "AIF2 PB"}, + {"SLIM RX4 MUX", "AIF2_PB", "AIF2 PB"}, + {"SLIM RX5 MUX", "AIF2_PB", "AIF2 PB"}, + {"SLIM RX6 MUX", "AIF2_PB", "AIF2 PB"}, + {"SLIM RX7 MUX", "AIF2_PB", "AIF2 PB"}, + + {"SLIM RX0 MUX", "AIF3_PB", "AIF3 PB"}, + {"SLIM RX1 MUX", "AIF3_PB", "AIF3 PB"}, + {"SLIM RX2 MUX", "AIF3_PB", "AIF3 PB"}, + {"SLIM RX3 MUX", "AIF3_PB", "AIF3 PB"}, + {"SLIM RX4 MUX", "AIF3_PB", "AIF3 PB"}, + {"SLIM RX5 MUX", "AIF3_PB", "AIF3 PB"}, + {"SLIM RX6 MUX", "AIF3_PB", "AIF3 PB"}, + {"SLIM RX7 MUX", "AIF3_PB", "AIF3 PB"}, + + {"SLIM RX0 MUX", "AIF4_PB", "AIF4 PB"}, + {"SLIM RX1 MUX", "AIF4_PB", "AIF4 PB"}, + {"SLIM RX2 MUX", "AIF4_PB", "AIF4 PB"}, + {"SLIM RX3 MUX", "AIF4_PB", "AIF4 PB"}, + {"SLIM RX4 MUX", "AIF4_PB", "AIF4 PB"}, + {"SLIM RX5 MUX", "AIF4_PB", "AIF4 PB"}, + {"SLIM RX6 MUX", "AIF4_PB", "AIF4 PB"}, + {"SLIM RX7 MUX", "AIF4_PB", "AIF4 PB"}, + + {"SLIM RX0", NULL, "SLIM RX0 MUX"}, + {"SLIM RX1", NULL, "SLIM RX1 MUX"}, + {"SLIM RX2", NULL, "SLIM RX2 MUX"}, + {"SLIM RX3", NULL, "SLIM RX3 MUX"}, + {"SLIM RX4", NULL, "SLIM RX4 MUX"}, + {"SLIM RX5", NULL, "SLIM RX5 MUX"}, + {"SLIM RX6", NULL, "SLIM RX6 MUX"}, + {"SLIM RX7", NULL, "SLIM RX7 MUX"}, + + /* CDC Rx interface with SLIMBUS */ + {"CDC_IF RX0 MUX", "SLIM RX0", "SLIM RX0"}, + {"CDC_IF RX1 MUX", "SLIM RX1", "SLIM RX1"}, + {"CDC_IF RX2 MUX", "SLIM RX2", "SLIM RX2"}, + {"CDC_IF RX3 MUX", "SLIM RX3", "SLIM RX3"}, + {"CDC_IF RX4 MUX", "SLIM RX4", "SLIM RX4"}, + {"CDC_IF RX5 MUX", "SLIM RX5", "SLIM RX5"}, + {"CDC_IF RX6 MUX", "SLIM RX6", "SLIM RX6"}, + {"CDC_IF RX7 MUX", "SLIM RX7", "SLIM RX7"}, + + /* VI Feedback */ + {"AIF4_VI Mixer", "SPKR_VI_1", "VIINPUT"}, + {"AIF4_VI Mixer", "SPKR_VI_2", "VIINPUT"}, + {"AIF4 VI", NULL, "AIF4_VI Mixer"}, + +}; + +const struct snd_soc_dapm_route tavil_i2s_audio_map[] = { + + /* Virtual input widget Mixer I2S*/ + {"AIF1_CAP Mixer", "I2S TX1", "I2S TX1"}, + {"AIF1_CAP Mixer", "I2S TX2", "I2S TX2"}, + {"AIF1_CAP Mixer", "I2S TX3", "I2S TX3"}, + {"AIF1_CAP Mixer", "I2S TX4", "I2S TX4"}, + {"AIF1_CAP Mixer", "I2S TX5", "I2S TX5"}, + {"AIF1_CAP Mixer", "I2S TX6", "I2S TX6"}, + {"AIF1_CAP Mixer", "I2S TX7", "I2S TX7"}, + + {"AIF2_CAP Mixer", "I2S TX8", "I2S TX8"}, + {"AIF2_CAP Mixer", "I2S TX11", "I2S TX11"}, + + {"AIF3_CAP Mixer", "I2S TX0", "I2S TX0"}, + {"AIF3_CAP Mixer", "I2S TX1", "I2S TX1"}, + + /* CDC Tx interface with I2S */ + {"I2S TX0", NULL, "CDC_IF TX0 MUX"}, + {"I2S TX1", NULL, "CDC_IF TX1 MUX"}, + {"I2S TX2", NULL, "CDC_IF TX2 MUX"}, + {"I2S TX3", NULL, "CDC_IF TX3 MUX"}, + {"I2S TX4", NULL, "CDC_IF TX4 MUX"}, + {"I2S TX5", NULL, "CDC_IF TX5 MUX"}, + {"I2S TX6", NULL, "CDC_IF TX6 MUX"}, + {"I2S TX7", NULL, "CDC_IF TX7 MUX"}, + {"I2S TX8", NULL, "CDC_IF TX8 MUX"}, + {"I2S TX11", NULL, "CDC_IF TX11 MUX"}, + + {"I2S RX0 MUX", "AIF1_PB", "AIF1 PB"}, + {"I2S RX1 MUX", "AIF1_PB", "AIF1 PB"}, + {"I2S RX2 MUX", "AIF1_PB", "AIF1 PB"}, + {"I2S RX3 MUX", "AIF1_PB", "AIF1 PB"}, + {"I2S RX4 MUX", "AIF1_PB", "AIF1 PB"}, + {"I2S RX5 MUX", "AIF1_PB", "AIF1 PB"}, + {"I2S RX6 MUX", "AIF1_PB", "AIF1 PB"}, + {"I2S RX7 MUX", "AIF1_PB", "AIF1 PB"}, + + {"I2S RX2 MUX", "AIF2_PB", "AIF2 PB"}, + {"I2S RX3 MUX", "AIF2_PB", "AIF2 PB"}, + + {"I2S RX4 MUX", "AIF3_PB", "AIF3 PB"}, + {"I2S RX5 MUX", "AIF3_PB", "AIF3 PB"}, + + {"I2S RX0", NULL, "I2S RX0 MUX"}, + {"I2S RX1", NULL, "I2S RX1 MUX"}, + {"I2S RX2", NULL, "I2S RX2 MUX"}, + {"I2S RX3", NULL, "I2S RX3 MUX"}, + {"I2S RX4", NULL, "I2S RX4 MUX"}, + {"I2S RX5", NULL, "I2S RX5 MUX"}, + {"I2S RX6", NULL, "I2S RX6 MUX"}, + {"I2S RX7", NULL, "I2S RX7 MUX"}, + + /* CDC Rx interface with I2S */ + {"CDC_IF RX0 MUX", "I2S RX0", "I2S RX0"}, + {"CDC_IF RX1 MUX", "I2S RX1", "I2S RX1"}, + {"CDC_IF RX2 MUX", "I2S RX2", "I2S RX2"}, + {"CDC_IF RX3 MUX", "I2S RX3", "I2S RX3"}, + {"CDC_IF RX4 MUX", "I2S RX4", "I2S RX4"}, + {"CDC_IF RX5 MUX", "I2S RX5", "I2S RX5"}, + {"CDC_IF RX6 MUX", "I2S RX6", "I2S RX6"}, + {"CDC_IF RX7 MUX", "I2S RX7", "I2S RX7"}, + +}; + +const struct snd_soc_dapm_route tavil_audio_map[] = { + + /* + * AIF CAP to Mixer routes are common + * for both SLIM as well as I2S + */ + + /* Virtual input widgets */ + {"AIF1 CAP", NULL, "AIF1_CAP Mixer"}, + {"AIF2 CAP", NULL, "AIF2_CAP Mixer"}, + {"AIF3 CAP", NULL, "AIF3_CAP Mixer"}, + + /* WDMA3 */ + {"WDMA3 PORT0 MUX", "DEC0", "ADC MUX0"}, + {"WDMA3 PORT0 MUX", "RX_MIX_TX0", "RX MIX TX0 MUX"}, + {"WDMA3 PORT1 MUX", "DEC1", "ADC MUX1"}, + {"WDMA3 PORT1 MUX", "RX_MIX_TX1", "RX MIX TX1 MUX"}, + {"WDMA3 PORT2 MUX", "DEC2", "ADC MUX2"}, + {"WDMA3 PORT2 MUX", "RX_MIX_TX2", "RX MIX TX2 MUX"}, + {"WDMA3 PORT3 MUX", "DEC3", "ADC MUX3"}, + {"WDMA3 PORT3 MUX", "RX_MIX_TX3", "RX MIX TX3 MUX"}, + {"WDMA3 PORT4 MUX", "DEC4", "ADC MUX4"}, + {"WDMA3 PORT4 MUX", "RX_MIX_TX4", "RX MIX TX4 MUX"}, + {"WDMA3 PORT5 MUX", "DEC5", "ADC MUX5"}, + {"WDMA3 PORT5 MUX", "RX_MIX_TX5", "RX MIX TX5 MUX"}, + {"WDMA3 PORT6 MUX", "DEC6", "ADC MUX6"}, + {"WDMA3 PORT6 MUX", "RX_MIX_TX6", "RX MIX TX6 MUX"}, + + {"WDMA3 CH0 MUX", "PORT_0", "WDMA3 PORT0 MUX"}, + {"WDMA3 CH0 MUX", "PORT_1", "WDMA3 PORT1 MUX"}, + {"WDMA3 CH0 MUX", "PORT_2", "WDMA3 PORT2 MUX"}, + {"WDMA3 CH0 MUX", "PORT_3", "WDMA3 PORT3 MUX"}, + {"WDMA3 CH0 MUX", "PORT_4", "WDMA3 PORT4 MUX"}, + {"WDMA3 CH0 MUX", "PORT_5", "WDMA3 PORT5 MUX"}, + {"WDMA3 CH0 MUX", "PORT_6", "WDMA3 PORT6 MUX"}, + {"WDMA3 CH0 MUX", "PORT_7", "ADC MUX7"}, + {"WDMA3 CH0 MUX", "PORT_8", "ADC MUX8"}, + + {"WDMA3 CH1 MUX", "PORT_0", "WDMA3 PORT0 MUX"}, + {"WDMA3 CH1 MUX", "PORT_1", "WDMA3 PORT1 MUX"}, + {"WDMA3 CH1 MUX", "PORT_2", "WDMA3 PORT2 MUX"}, + {"WDMA3 CH1 MUX", "PORT_3", "WDMA3 PORT3 MUX"}, + {"WDMA3 CH1 MUX", "PORT_4", "WDMA3 PORT4 MUX"}, + {"WDMA3 CH1 MUX", "PORT_5", "WDMA3 PORT5 MUX"}, + {"WDMA3 CH1 MUX", "PORT_6", "WDMA3 PORT6 MUX"}, + {"WDMA3 CH1 MUX", "PORT_7", "ADC MUX7"}, + {"WDMA3 CH1 MUX", "PORT_8", "ADC MUX8"}, + + {"WDMA3 CH2 MUX", "PORT_0", "WDMA3 PORT0 MUX"}, + {"WDMA3 CH2 MUX", "PORT_1", "WDMA3 PORT1 MUX"}, + {"WDMA3 CH2 MUX", "PORT_2", "WDMA3 PORT2 MUX"}, + {"WDMA3 CH2 MUX", "PORT_3", "WDMA3 PORT3 MUX"}, + {"WDMA3 CH2 MUX", "PORT_4", "WDMA3 PORT4 MUX"}, + {"WDMA3 CH2 MUX", "PORT_5", "WDMA3 PORT5 MUX"}, + {"WDMA3 CH2 MUX", "PORT_6", "WDMA3 PORT6 MUX"}, + {"WDMA3 CH2 MUX", "PORT_7", "ADC MUX7"}, + {"WDMA3 CH2 MUX", "PORT_8", "ADC MUX8"}, + + {"WDMA3 CH3 MUX", "PORT_0", "WDMA3 PORT0 MUX"}, + {"WDMA3 CH3 MUX", "PORT_1", "WDMA3 PORT1 MUX"}, + {"WDMA3 CH3 MUX", "PORT_2", "WDMA3 PORT2 MUX"}, + {"WDMA3 CH3 MUX", "PORT_3", "WDMA3 PORT3 MUX"}, + {"WDMA3 CH3 MUX", "PORT_4", "WDMA3 PORT4 MUX"}, + {"WDMA3 CH3 MUX", "PORT_5", "WDMA3 PORT5 MUX"}, + {"WDMA3 CH3 MUX", "PORT_6", "WDMA3 PORT6 MUX"}, + {"WDMA3 CH3 MUX", "PORT_7", "ADC MUX7"}, + {"WDMA3 CH3 MUX", "PORT_8", "ADC MUX8"}, + + {"WDMA3_CH_MIXER", NULL, "WDMA3 CH0 MUX"}, + {"WDMA3_CH_MIXER", NULL, "WDMA3 CH1 MUX"}, + {"WDMA3_CH_MIXER", NULL, "WDMA3 CH2 MUX"}, + {"WDMA3_CH_MIXER", NULL, "WDMA3 CH3 MUX"}, + + {"WDMA3_ON_OFF", "Switch", "WDMA3_CH_MIXER"}, + {"WDMA3_OUT", NULL, "WDMA3_ON_OFF"}, + + /* MAD */ + {"MAD_SEL MUX", "SPE", "MAD_CPE_INPUT"}, + {"MAD_SEL MUX", "MSM", "MADINPUT"}, + + {"MAD_INP MUX", "MAD", "MAD_SEL MUX"}, + {"MAD_INP MUX", "DEC1", "ADC MUX1"}, + + {"MAD_BROADCAST", "Switch", "MAD_INP MUX"}, + {"MAD_CPE1", "Switch", "MAD_INP MUX"}, + {"MAD_CPE2", "Switch", "MAD_INP MUX"}, + + {"MAD_CPE_OUT1", NULL, "MAD_CPE1"}, + {"MAD_CPE_OUT2", NULL, "MAD_CPE2"}, + + {"CDC_IF TX0 MUX", "DEC0", "ADC MUX0"}, + {"CDC_IF TX0 MUX", "RX_MIX_TX0", "RX MIX TX0 MUX"}, + {"CDC_IF TX0 MUX", "DEC0_192", "ADC US MUX0"}, + + {"CDC_IF TX1 MUX", "DEC1", "ADC MUX1"}, + {"CDC_IF TX1 MUX", "RX_MIX_TX1", "RX MIX TX1 MUX"}, + {"CDC_IF TX1 MUX", "DEC1_192", "ADC US MUX1"}, + + {"CDC_IF TX2 MUX", "DEC2", "ADC MUX2"}, + {"CDC_IF TX2 MUX", "RX_MIX_TX2", "RX MIX TX2 MUX"}, + {"CDC_IF TX2 MUX", "DEC2_192", "ADC US MUX2"}, + + {"CDC_IF TX3 MUX", "DEC3", "ADC MUX3"}, + {"CDC_IF TX3 MUX", "RX_MIX_TX3", "RX MIX TX3 MUX"}, + {"CDC_IF TX3 MUX", "DEC3_192", "ADC US MUX3"}, + + {"CDC_IF TX4 MUX", "DEC4", "ADC MUX4"}, + {"CDC_IF TX4 MUX", "RX_MIX_TX4", "RX MIX TX4 MUX"}, + {"CDC_IF TX4 MUX", "DEC4_192", "ADC US MUX4"}, + + {"CDC_IF TX5 MUX", "DEC5", "ADC MUX5"}, + {"CDC_IF TX5 MUX", "RX_MIX_TX5", "RX MIX TX5 MUX"}, + {"CDC_IF TX5 MUX", "DEC5_192", "ADC US MUX5"}, + + {"CDC_IF TX6 MUX", "DEC6", "ADC MUX6"}, + {"CDC_IF TX6 MUX", "RX_MIX_TX6", "RX MIX TX6 MUX"}, + {"CDC_IF TX6 MUX", "DEC6_192", "ADC US MUX6"}, + + {"CDC_IF TX7 MUX", "DEC7", "ADC MUX7"}, + {"CDC_IF TX7 MUX", "RX_MIX_TX7", "RX MIX TX7 MUX"}, + {"CDC_IF TX7 MUX", "DEC7_192", "ADC US MUX7"}, + + {"CDC_IF TX8 MUX", "DEC8", "ADC MUX8"}, + {"CDC_IF TX8 MUX", "RX_MIX_TX8", "RX MIX TX8 MUX"}, + {"CDC_IF TX8 MUX", "DEC8_192", "ADC US MUX8"}, + + {"CDC_IF TX9 MUX", "DEC7", "ADC MUX7"}, + {"CDC_IF TX9 MUX", "DEC7_192", "ADC US MUX7"}, + {"CDC_IF TX10 MUX", "DEC6", "ADC MUX6"}, + {"CDC_IF TX10 MUX", "DEC6_192", "ADC US MUX6"}, + + {"CDC_IF TX11 MUX", "DEC_0_5", "CDC_IF TX11 INP1 MUX"}, + {"CDC_IF TX11 MUX", "DEC_9_12", "CDC_IF TX11 INP1 MUX"}, + {"CDC_IF TX11 INP1 MUX", "DEC0", "ADC MUX0"}, + {"CDC_IF TX11 INP1 MUX", "DEC1", "ADC MUX1"}, + {"CDC_IF TX11 INP1 MUX", "DEC2", "ADC MUX2"}, + {"CDC_IF TX11 INP1 MUX", "DEC3", "ADC MUX3"}, + {"CDC_IF TX11 INP1 MUX", "DEC4", "ADC MUX4"}, + {"CDC_IF TX11 INP1 MUX", "DEC5", "ADC MUX5"}, + {"CDC_IF TX11 INP1 MUX", "RX_MIX_TX5", "RX MIX TX5 MUX"}, + + {"CDC_IF TX13 MUX", "MAD_BRDCST", "MAD_BROADCAST"}, + {"CDC_IF TX13 MUX", "CDC_DEC_5", "CDC_IF TX13 INP1 MUX"}, + {"CDC_IF TX13 INP1 MUX", "DEC5", "ADC MUX5"}, + {"CDC_IF TX13 INP1 MUX", "DEC5_192", "ADC US MUX5"}, + + {"RX MIX TX0 MUX", "RX_MIX0", "RX INT0 SEC MIX"}, + {"RX MIX TX0 MUX", "RX_MIX1", "RX INT1 SEC MIX"}, + {"RX MIX TX0 MUX", "RX_MIX2", "RX INT2 SEC MIX"}, + {"RX MIX TX0 MUX", "RX_MIX3", "RX INT3 SEC MIX"}, + {"RX MIX TX0 MUX", "RX_MIX4", "RX INT4 SEC MIX"}, + {"RX MIX TX0 MUX", "RX_MIX7", "RX INT7 SEC MIX"}, + {"RX MIX TX0 MUX", "RX_MIX8", "RX INT8 SEC MIX"}, + + {"RX MIX TX1 MUX", "RX_MIX0", "RX INT0 SEC MIX"}, + {"RX MIX TX1 MUX", "RX_MIX1", "RX INT1 SEC MIX"}, + {"RX MIX TX1 MUX", "RX_MIX2", "RX INT2 SEC MIX"}, + {"RX MIX TX1 MUX", "RX_MIX3", "RX INT3 SEC MIX"}, + {"RX MIX TX1 MUX", "RX_MIX4", "RX INT4 SEC MIX"}, + {"RX MIX TX1 MUX", "RX_MIX7", "RX INT7 SEC MIX"}, + {"RX MIX TX1 MUX", "RX_MIX8", "RX INT8 SEC MIX"}, + + {"RX MIX TX2 MUX", "RX_MIX0", "RX INT0 SEC MIX"}, + {"RX MIX TX2 MUX", "RX_MIX1", "RX INT1 SEC MIX"}, + {"RX MIX TX2 MUX", "RX_MIX2", "RX INT2 SEC MIX"}, + {"RX MIX TX2 MUX", "RX_MIX3", "RX INT3 SEC MIX"}, + {"RX MIX TX2 MUX", "RX_MIX4", "RX INT4 SEC MIX"}, + {"RX MIX TX2 MUX", "RX_MIX7", "RX INT7 SEC MIX"}, + {"RX MIX TX2 MUX", "RX_MIX8", "RX INT8 SEC MIX"}, + + {"RX MIX TX3 MUX", "RX_MIX0", "RX INT0 SEC MIX"}, + {"RX MIX TX3 MUX", "RX_MIX1", "RX INT1 SEC MIX"}, + {"RX MIX TX3 MUX", "RX_MIX2", "RX INT2 SEC MIX"}, + {"RX MIX TX3 MUX", "RX_MIX3", "RX INT3 SEC MIX"}, + {"RX MIX TX3 MUX", "RX_MIX4", "RX INT4 SEC MIX"}, + {"RX MIX TX3 MUX", "RX_MIX7", "RX INT7 SEC MIX"}, + {"RX MIX TX3 MUX", "RX_MIX8", "RX INT8 SEC MIX"}, + + {"RX MIX TX4 MUX", "RX_MIX0", "RX INT0 SEC MIX"}, + {"RX MIX TX4 MUX", "RX_MIX1", "RX INT1 SEC MIX"}, + {"RX MIX TX4 MUX", "RX_MIX2", "RX INT2 SEC MIX"}, + {"RX MIX TX4 MUX", "RX_MIX3", "RX INT3 SEC MIX"}, + {"RX MIX TX4 MUX", "RX_MIX4", "RX INT4 SEC MIX"}, + {"RX MIX TX4 MUX", "RX_MIX7", "RX INT7 SEC MIX"}, + {"RX MIX TX4 MUX", "RX_MIX8", "RX INT8 SEC MIX"}, + + {"RX MIX TX5 MUX", "RX_MIX0", "RX INT0 SEC MIX"}, + {"RX MIX TX5 MUX", "RX_MIX1", "RX INT1 SEC MIX"}, + {"RX MIX TX5 MUX", "RX_MIX2", "RX INT2 SEC MIX"}, + {"RX MIX TX5 MUX", "RX_MIX3", "RX INT3 SEC MIX"}, + {"RX MIX TX5 MUX", "RX_MIX4", "RX INT4 SEC MIX"}, + {"RX MIX TX5 MUX", "RX_MIX7", "RX INT7 SEC MIX"}, + {"RX MIX TX5 MUX", "RX_MIX8", "RX INT8 SEC MIX"}, + + {"RX MIX TX6 MUX", "RX_MIX0", "RX INT0 SEC MIX"}, + {"RX MIX TX6 MUX", "RX_MIX1", "RX INT1 SEC MIX"}, + {"RX MIX TX6 MUX", "RX_MIX2", "RX INT2 SEC MIX"}, + {"RX MIX TX6 MUX", "RX_MIX3", "RX INT3 SEC MIX"}, + {"RX MIX TX6 MUX", "RX_MIX4", "RX INT4 SEC MIX"}, + {"RX MIX TX6 MUX", "RX_MIX7", "RX INT7 SEC MIX"}, + {"RX MIX TX6 MUX", "RX_MIX8", "RX INT8 SEC MIX"}, + + {"RX MIX TX7 MUX", "RX_MIX0", "RX INT0 SEC MIX"}, + {"RX MIX TX7 MUX", "RX_MIX1", "RX INT1 SEC MIX"}, + {"RX MIX TX7 MUX", "RX_MIX2", "RX INT2 SEC MIX"}, + {"RX MIX TX7 MUX", "RX_MIX3", "RX INT3 SEC MIX"}, + {"RX MIX TX7 MUX", "RX_MIX4", "RX INT4 SEC MIX"}, + {"RX MIX TX7 MUX", "RX_MIX7", "RX INT7 SEC MIX"}, + {"RX MIX TX7 MUX", "RX_MIX8", "RX INT8 SEC MIX"}, + + {"RX MIX TX8 MUX", "RX_MIX0", "RX INT0 SEC MIX"}, + {"RX MIX TX8 MUX", "RX_MIX1", "RX INT1 SEC MIX"}, + {"RX MIX TX8 MUX", "RX_MIX2", "RX INT2 SEC MIX"}, + {"RX MIX TX8 MUX", "RX_MIX3", "RX INT3 SEC MIX"}, + {"RX MIX TX8 MUX", "RX_MIX4", "RX INT4 SEC MIX"}, + {"RX MIX TX8 MUX", "RX_MIX7", "RX INT7 SEC MIX"}, + {"RX MIX TX8 MUX", "RX_MIX8", "RX INT8 SEC MIX"}, + + {"ADC US MUX0", "US_Switch", "ADC MUX0"}, + {"ADC US MUX1", "US_Switch", "ADC MUX1"}, + {"ADC US MUX2", "US_Switch", "ADC MUX2"}, + {"ADC US MUX3", "US_Switch", "ADC MUX3"}, + {"ADC US MUX4", "US_Switch", "ADC MUX4"}, + {"ADC US MUX5", "US_Switch", "ADC MUX5"}, + {"ADC US MUX6", "US_Switch", "ADC MUX6"}, + {"ADC US MUX7", "US_Switch", "ADC MUX7"}, + {"ADC US MUX8", "US_Switch", "ADC MUX8"}, + + {"ADC MUX0", "DMIC", "DMIC MUX0"}, + {"ADC MUX0", "AMIC", "AMIC MUX0"}, + {"ADC MUX1", "DMIC", "DMIC MUX1"}, + {"ADC MUX1", "AMIC", "AMIC MUX1"}, + {"ADC MUX2", "DMIC", "DMIC MUX2"}, + {"ADC MUX2", "AMIC", "AMIC MUX2"}, + {"ADC MUX3", "DMIC", "DMIC MUX3"}, + {"ADC MUX3", "AMIC", "AMIC MUX3"}, + {"ADC MUX4", "DMIC", "DMIC MUX4"}, + {"ADC MUX4", "AMIC", "AMIC MUX4"}, + {"ADC MUX5", "DMIC", "DMIC MUX5"}, + {"ADC MUX5", "AMIC", "AMIC MUX5"}, + {"ADC MUX6", "DMIC", "DMIC MUX6"}, + {"ADC MUX6", "AMIC", "AMIC MUX6"}, + {"ADC MUX7", "DMIC", "DMIC MUX7"}, + {"ADC MUX7", "AMIC", "AMIC MUX7"}, + {"ADC MUX8", "DMIC", "DMIC MUX8"}, + {"ADC MUX8", "AMIC", "AMIC MUX8"}, + {"ADC MUX10", "DMIC", "DMIC MUX10"}, + {"ADC MUX10", "AMIC", "AMIC MUX10"}, + {"ADC MUX11", "DMIC", "DMIC MUX11"}, + {"ADC MUX11", "AMIC", "AMIC MUX11"}, + {"ADC MUX12", "DMIC", "DMIC MUX12"}, + {"ADC MUX12", "AMIC", "AMIC MUX12"}, + {"ADC MUX13", "DMIC", "DMIC MUX13"}, + {"ADC MUX13", "AMIC", "AMIC MUX13"}, + + {"ADC MUX0", "ANC_FB_TUNE1", "ADC MUX10"}, + {"ADC MUX0", "ANC_FB_TUNE1", "ADC MUX11"}, + {"ADC MUX0", "ANC_FB_TUNE2", "ADC MUX12"}, + {"ADC MUX0", "ANC_FB_TUNE2", "ADC MUX13"}, + {"ADC MUX1", "ANC_FB_TUNE1", "ADC MUX10"}, + {"ADC MUX1", "ANC_FB_TUNE1", "ADC MUX11"}, + {"ADC MUX1", "ANC_FB_TUNE2", "ADC MUX12"}, + {"ADC MUX1", "ANC_FB_TUNE2", "ADC MUX13"}, + {"ADC MUX2", "ANC_FB_TUNE1", "ADC MUX10"}, + {"ADC MUX2", "ANC_FB_TUNE1", "ADC MUX11"}, + {"ADC MUX2", "ANC_FB_TUNE2", "ADC MUX12"}, + {"ADC MUX2", "ANC_FB_TUNE2", "ADC MUX13"}, + {"ADC MUX3", "ANC_FB_TUNE1", "ADC MUX10"}, + {"ADC MUX3", "ANC_FB_TUNE1", "ADC MUX11"}, + {"ADC MUX3", "ANC_FB_TUNE2", "ADC MUX12"}, + {"ADC MUX3", "ANC_FB_TUNE2", "ADC MUX13"}, + {"ADC MUX4", "ANC_FB_TUNE1", "ADC MUX10"}, + {"ADC MUX4", "ANC_FB_TUNE1", "ADC MUX11"}, + {"ADC MUX4", "ANC_FB_TUNE2", "ADC MUX12"}, + {"ADC MUX4", "ANC_FB_TUNE2", "ADC MUX13"}, + {"ADC MUX5", "ANC_FB_TUNE1", "ADC MUX10"}, + {"ADC MUX5", "ANC_FB_TUNE1", "ADC MUX11"}, + {"ADC MUX5", "ANC_FB_TUNE2", "ADC MUX12"}, + {"ADC MUX5", "ANC_FB_TUNE2", "ADC MUX13"}, + {"ADC MUX6", "ANC_FB_TUNE1", "ADC MUX10"}, + {"ADC MUX6", "ANC_FB_TUNE1", "ADC MUX11"}, + {"ADC MUX6", "ANC_FB_TUNE2", "ADC MUX12"}, + {"ADC MUX6", "ANC_FB_TUNE2", "ADC MUX13"}, + {"ADC MUX7", "ANC_FB_TUNE1", "ADC MUX10"}, + {"ADC MUX7", "ANC_FB_TUNE1", "ADC MUX11"}, + {"ADC MUX7", "ANC_FB_TUNE2", "ADC MUX12"}, + {"ADC MUX7", "ANC_FB_TUNE2", "ADC MUX13"}, + {"ADC MUX8", "ANC_FB_TUNE1", "ADC MUX10"}, + {"ADC MUX8", "ANC_FB_TUNE1", "ADC MUX11"}, + {"ADC MUX8", "ANC_FB_TUNE2", "ADC MUX12"}, + {"ADC MUX8", "ANC_FB_TUNE2", "ADC MUX13"}, + + {"DMIC MUX0", "DMIC0", "DMIC0"}, + {"DMIC MUX0", "DMIC1", "DMIC1"}, + {"DMIC MUX0", "DMIC2", "DMIC2"}, + {"DMIC MUX0", "DMIC3", "DMIC3"}, + {"DMIC MUX0", "DMIC4", "DMIC4"}, + {"DMIC MUX0", "DMIC5", "DMIC5"}, + {"AMIC MUX0", "ADC1", "ADC1"}, + {"AMIC MUX0", "ADC2", "ADC2"}, + {"AMIC MUX0", "ADC3", "ADC3"}, + {"AMIC MUX0", "ADC4", "ADC4"}, + + {"DMIC MUX1", "DMIC0", "DMIC0"}, + {"DMIC MUX1", "DMIC1", "DMIC1"}, + {"DMIC MUX1", "DMIC2", "DMIC2"}, + {"DMIC MUX1", "DMIC3", "DMIC3"}, + {"DMIC MUX1", "DMIC4", "DMIC4"}, + {"DMIC MUX1", "DMIC5", "DMIC5"}, + {"AMIC MUX1", "ADC1", "ADC1"}, + {"AMIC MUX1", "ADC2", "ADC2"}, + {"AMIC MUX1", "ADC3", "ADC3"}, + {"AMIC MUX1", "ADC4", "ADC4"}, + + {"DMIC MUX2", "DMIC0", "DMIC0"}, + {"DMIC MUX2", "DMIC1", "DMIC1"}, + {"DMIC MUX2", "DMIC2", "DMIC2"}, + {"DMIC MUX2", "DMIC3", "DMIC3"}, + {"DMIC MUX2", "DMIC4", "DMIC4"}, + {"DMIC MUX2", "DMIC5", "DMIC5"}, + {"AMIC MUX2", "ADC1", "ADC1"}, + {"AMIC MUX2", "ADC2", "ADC2"}, + {"AMIC MUX2", "ADC3", "ADC3"}, + {"AMIC MUX2", "ADC4", "ADC4"}, + + {"DMIC MUX3", "DMIC0", "DMIC0"}, + {"DMIC MUX3", "DMIC1", "DMIC1"}, + {"DMIC MUX3", "DMIC2", "DMIC2"}, + {"DMIC MUX3", "DMIC3", "DMIC3"}, + {"DMIC MUX3", "DMIC4", "DMIC4"}, + {"DMIC MUX3", "DMIC5", "DMIC5"}, + {"AMIC MUX3", "ADC1", "ADC1"}, + {"AMIC MUX3", "ADC2", "ADC2"}, + {"AMIC MUX3", "ADC3", "ADC3"}, + {"AMIC MUX3", "ADC4", "ADC4"}, + + {"DMIC MUX4", "DMIC0", "DMIC0"}, + {"DMIC MUX4", "DMIC1", "DMIC1"}, + {"DMIC MUX4", "DMIC2", "DMIC2"}, + {"DMIC MUX4", "DMIC3", "DMIC3"}, + {"DMIC MUX4", "DMIC4", "DMIC4"}, + {"DMIC MUX4", "DMIC5", "DMIC5"}, + {"AMIC MUX4", "ADC1", "ADC1"}, + {"AMIC MUX4", "ADC2", "ADC2"}, + {"AMIC MUX4", "ADC3", "ADC3"}, + {"AMIC MUX4", "ADC4", "ADC4"}, + + {"DMIC MUX5", "DMIC0", "DMIC0"}, + {"DMIC MUX5", "DMIC1", "DMIC1"}, + {"DMIC MUX5", "DMIC2", "DMIC2"}, + {"DMIC MUX5", "DMIC3", "DMIC3"}, + {"DMIC MUX5", "DMIC4", "DMIC4"}, + {"DMIC MUX5", "DMIC5", "DMIC5"}, + {"AMIC MUX5", "ADC1", "ADC1"}, + {"AMIC MUX5", "ADC2", "ADC2"}, + {"AMIC MUX5", "ADC3", "ADC3"}, + {"AMIC MUX5", "ADC4", "ADC4"}, + + {"DMIC MUX6", "DMIC0", "DMIC0"}, + {"DMIC MUX6", "DMIC1", "DMIC1"}, + {"DMIC MUX6", "DMIC2", "DMIC2"}, + {"DMIC MUX6", "DMIC3", "DMIC3"}, + {"DMIC MUX6", "DMIC4", "DMIC4"}, + {"DMIC MUX6", "DMIC5", "DMIC5"}, + {"AMIC MUX6", "ADC1", "ADC1"}, + {"AMIC MUX6", "ADC2", "ADC2"}, + {"AMIC MUX6", "ADC3", "ADC3"}, + {"AMIC MUX6", "ADC4", "ADC4"}, + + {"DMIC MUX7", "DMIC0", "DMIC0"}, + {"DMIC MUX7", "DMIC1", "DMIC1"}, + {"DMIC MUX7", "DMIC2", "DMIC2"}, + {"DMIC MUX7", "DMIC3", "DMIC3"}, + {"DMIC MUX7", "DMIC4", "DMIC4"}, + {"DMIC MUX7", "DMIC5", "DMIC5"}, + {"AMIC MUX7", "ADC1", "ADC1"}, + {"AMIC MUX7", "ADC2", "ADC2"}, + {"AMIC MUX7", "ADC3", "ADC3"}, + {"AMIC MUX7", "ADC4", "ADC4"}, + + {"DMIC MUX8", "DMIC0", "DMIC0"}, + {"DMIC MUX8", "DMIC1", "DMIC1"}, + {"DMIC MUX8", "DMIC2", "DMIC2"}, + {"DMIC MUX8", "DMIC3", "DMIC3"}, + {"DMIC MUX8", "DMIC4", "DMIC4"}, + {"DMIC MUX8", "DMIC5", "DMIC5"}, + {"AMIC MUX8", "ADC1", "ADC1"}, + {"AMIC MUX8", "ADC2", "ADC2"}, + {"AMIC MUX8", "ADC3", "ADC3"}, + {"AMIC MUX8", "ADC4", "ADC4"}, + + {"DMIC MUX10", "DMIC0", "DMIC0"}, + {"DMIC MUX10", "DMIC1", "DMIC1"}, + {"DMIC MUX10", "DMIC2", "DMIC2"}, + {"DMIC MUX10", "DMIC3", "DMIC3"}, + {"DMIC MUX10", "DMIC4", "DMIC4"}, + {"DMIC MUX10", "DMIC5", "DMIC5"}, + {"AMIC MUX10", "ADC1", "ADC1"}, + {"AMIC MUX10", "ADC2", "ADC2"}, + {"AMIC MUX10", "ADC3", "ADC3"}, + {"AMIC MUX10", "ADC4", "ADC4"}, + + {"DMIC MUX11", "DMIC0", "DMIC0"}, + {"DMIC MUX11", "DMIC1", "DMIC1"}, + {"DMIC MUX11", "DMIC2", "DMIC2"}, + {"DMIC MUX11", "DMIC3", "DMIC3"}, + {"DMIC MUX11", "DMIC4", "DMIC4"}, + {"DMIC MUX11", "DMIC5", "DMIC5"}, + {"AMIC MUX11", "ADC1", "ADC1"}, + {"AMIC MUX11", "ADC2", "ADC2"}, + {"AMIC MUX11", "ADC3", "ADC3"}, + {"AMIC MUX11", "ADC4", "ADC4"}, + + {"DMIC MUX12", "DMIC0", "DMIC0"}, + {"DMIC MUX12", "DMIC1", "DMIC1"}, + {"DMIC MUX12", "DMIC2", "DMIC2"}, + {"DMIC MUX12", "DMIC3", "DMIC3"}, + {"DMIC MUX12", "DMIC4", "DMIC4"}, + {"DMIC MUX12", "DMIC5", "DMIC5"}, + {"AMIC MUX12", "ADC1", "ADC1"}, + {"AMIC MUX12", "ADC2", "ADC2"}, + {"AMIC MUX12", "ADC3", "ADC3"}, + {"AMIC MUX12", "ADC4", "ADC4"}, + + {"DMIC MUX13", "DMIC0", "DMIC0"}, + {"DMIC MUX13", "DMIC1", "DMIC1"}, + {"DMIC MUX13", "DMIC2", "DMIC2"}, + {"DMIC MUX13", "DMIC3", "DMIC3"}, + {"DMIC MUX13", "DMIC4", "DMIC4"}, + {"DMIC MUX13", "DMIC5", "DMIC5"}, + {"AMIC MUX13", "ADC1", "ADC1"}, + {"AMIC MUX13", "ADC2", "ADC2"}, + {"AMIC MUX13", "ADC3", "ADC3"}, + {"AMIC MUX13", "ADC4", "ADC4"}, + + {"AMIC4_5 SEL", "AMIC4", "AMIC4"}, + {"AMIC4_5 SEL", "AMIC5", "AMIC5"}, + + {"ADC1", NULL, "AMIC1"}, + {"ADC2", NULL, "AMIC2"}, + {"ADC3", NULL, "AMIC3"}, + {"ADC4", NULL, "AMIC4_5 SEL"}, + + {"RX INT0_1 MIX1 INP0", "RX0", "CDC_IF RX0 MUX"}, + {"RX INT0_1 MIX1 INP0", "RX1", "CDC_IF RX1 MUX"}, + {"RX INT0_1 MIX1 INP0", "RX2", "CDC_IF RX2 MUX"}, + {"RX INT0_1 MIX1 INP0", "RX3", "CDC_IF RX3 MUX"}, + {"RX INT0_1 MIX1 INP0", "RX4", "CDC_IF RX4 MUX"}, + {"RX INT0_1 MIX1 INP0", "RX5", "CDC_IF RX5 MUX"}, + {"RX INT0_1 MIX1 INP0", "RX6", "CDC_IF RX6 MUX"}, + {"RX INT0_1 MIX1 INP0", "RX7", "CDC_IF RX7 MUX"}, + {"RX INT0_1 MIX1 INP0", "IIR0", "IIR0"}, + {"RX INT0_1 MIX1 INP0", "IIR1", "IIR1"}, + {"RX INT0_1 MIX1 INP1", "RX0", "CDC_IF RX0 MUX"}, + {"RX INT0_1 MIX1 INP1", "RX1", "CDC_IF RX1 MUX"}, + {"RX INT0_1 MIX1 INP1", "RX2", "CDC_IF RX2 MUX"}, + {"RX INT0_1 MIX1 INP1", "RX3", "CDC_IF RX3 MUX"}, + {"RX INT0_1 MIX1 INP1", "RX4", "CDC_IF RX4 MUX"}, + {"RX INT0_1 MIX1 INP1", "RX5", "CDC_IF RX5 MUX"}, + {"RX INT0_1 MIX1 INP1", "RX6", "CDC_IF RX6 MUX"}, + {"RX INT0_1 MIX1 INP1", "RX7", "CDC_IF RX7 MUX"}, + {"RX INT0_1 MIX1 INP1", "IIR0", "IIR0"}, + {"RX INT0_1 MIX1 INP1", "IIR1", "IIR1"}, + {"RX INT0_1 MIX1 INP2", "RX0", "CDC_IF RX0 MUX"}, + {"RX INT0_1 MIX1 INP2", "RX1", "CDC_IF RX1 MUX"}, + {"RX INT0_1 MIX1 INP2", "RX2", "CDC_IF RX2 MUX"}, + {"RX INT0_1 MIX1 INP2", "RX3", "CDC_IF RX3 MUX"}, + {"RX INT0_1 MIX1 INP2", "RX4", "CDC_IF RX4 MUX"}, + {"RX INT0_1 MIX1 INP2", "RX5", "CDC_IF RX5 MUX"}, + {"RX INT0_1 MIX1 INP2", "RX6", "CDC_IF RX6 MUX"}, + {"RX INT0_1 MIX1 INP2", "RX7", "CDC_IF RX7 MUX"}, + {"RX INT0_1 MIX1 INP2", "IIR0", "IIR0"}, + {"RX INT0_1 MIX1 INP2", "IIR1", "IIR1"}, + + {"RX INT1_1 MIX1 INP0", "RX0", "CDC_IF RX0 MUX"}, + {"RX INT1_1 MIX1 INP0", "RX1", "CDC_IF RX1 MUX"}, + {"RX INT1_1 MIX1 INP0", "RX2", "CDC_IF RX2 MUX"}, + {"RX INT1_1 MIX1 INP0", "RX3", "CDC_IF RX3 MUX"}, + {"RX INT1_1 MIX1 INP0", "RX4", "CDC_IF RX4 MUX"}, + {"RX INT1_1 MIX1 INP0", "RX5", "CDC_IF RX5 MUX"}, + {"RX INT1_1 MIX1 INP0", "RX6", "CDC_IF RX6 MUX"}, + {"RX INT1_1 MIX1 INP0", "RX7", "CDC_IF RX7 MUX"}, + {"RX INT1_1 MIX1 INP0", "IIR0", "IIR0"}, + {"RX INT1_1 MIX1 INP0", "IIR1", "IIR1"}, + {"RX INT1_1 MIX1 INP1", "RX0", "CDC_IF RX0 MUX"}, + {"RX INT1_1 MIX1 INP1", "RX1", "CDC_IF RX1 MUX"}, + {"RX INT1_1 MIX1 INP1", "RX2", "CDC_IF RX2 MUX"}, + {"RX INT1_1 MIX1 INP1", "RX3", "CDC_IF RX3 MUX"}, + {"RX INT1_1 MIX1 INP1", "RX4", "CDC_IF RX4 MUX"}, + {"RX INT1_1 MIX1 INP1", "RX5", "CDC_IF RX5 MUX"}, + {"RX INT1_1 MIX1 INP1", "RX6", "CDC_IF RX6 MUX"}, + {"RX INT1_1 MIX1 INP1", "RX7", "CDC_IF RX7 MUX"}, + {"RX INT1_1 MIX1 INP1", "IIR0", "IIR0"}, + {"RX INT1_1 MIX1 INP1", "IIR1", "IIR1"}, + {"RX INT1_1 MIX1 INP2", "RX0", "CDC_IF RX0 MUX"}, + {"RX INT1_1 MIX1 INP2", "RX1", "CDC_IF RX1 MUX"}, + {"RX INT1_1 MIX1 INP2", "RX2", "CDC_IF RX2 MUX"}, + {"RX INT1_1 MIX1 INP2", "RX3", "CDC_IF RX3 MUX"}, + {"RX INT1_1 MIX1 INP2", "RX4", "CDC_IF RX4 MUX"}, + {"RX INT1_1 MIX1 INP2", "RX5", "CDC_IF RX5 MUX"}, + {"RX INT1_1 MIX1 INP2", "RX6", "CDC_IF RX6 MUX"}, + {"RX INT1_1 MIX1 INP2", "RX7", "CDC_IF RX7 MUX"}, + {"RX INT1_1 MIX1 INP2", "IIR0", "IIR0"}, + {"RX INT1_1 MIX1 INP2", "IIR1", "IIR1"}, + {"RX INT2_1 MIX1 INP0", "RX0", "CDC_IF RX0 MUX"}, + {"RX INT2_1 MIX1 INP0", "RX1", "CDC_IF RX1 MUX"}, + {"RX INT2_1 MIX1 INP0", "RX2", "CDC_IF RX2 MUX"}, + {"RX INT2_1 MIX1 INP0", "RX3", "CDC_IF RX3 MUX"}, + {"RX INT2_1 MIX1 INP0", "RX4", "CDC_IF RX4 MUX"}, + {"RX INT2_1 MIX1 INP0", "RX5", "CDC_IF RX5 MUX"}, + {"RX INT2_1 MIX1 INP0", "RX6", "CDC_IF RX6 MUX"}, + {"RX INT2_1 MIX1 INP0", "RX7", "CDC_IF RX7 MUX"}, + {"RX INT2_1 MIX1 INP0", "IIR0", "IIR0"}, + {"RX INT2_1 MIX1 INP0", "IIR1", "IIR1"}, + {"RX INT2_1 MIX1 INP1", "RX0", "CDC_IF RX0 MUX"}, + {"RX INT2_1 MIX1 INP1", "RX1", "CDC_IF RX1 MUX"}, + {"RX INT2_1 MIX1 INP1", "RX2", "CDC_IF RX2 MUX"}, + {"RX INT2_1 MIX1 INP1", "RX3", "CDC_IF RX3 MUX"}, + {"RX INT2_1 MIX1 INP1", "RX4", "CDC_IF RX4 MUX"}, + {"RX INT2_1 MIX1 INP1", "RX5", "CDC_IF RX5 MUX"}, + {"RX INT2_1 MIX1 INP1", "RX6", "CDC_IF RX6 MUX"}, + {"RX INT2_1 MIX1 INP1", "RX7", "CDC_IF RX7 MUX"}, + {"RX INT2_1 MIX1 INP1", "IIR0", "IIR0"}, + {"RX INT2_1 MIX1 INP1", "IIR1", "IIR1"}, + {"RX INT2_1 MIX1 INP2", "RX0", "CDC_IF RX0 MUX"}, + {"RX INT2_1 MIX1 INP2", "RX1", "CDC_IF RX1 MUX"}, + {"RX INT2_1 MIX1 INP2", "RX2", "CDC_IF RX2 MUX"}, + {"RX INT2_1 MIX1 INP2", "RX3", "CDC_IF RX3 MUX"}, + {"RX INT2_1 MIX1 INP2", "RX4", "CDC_IF RX4 MUX"}, + {"RX INT2_1 MIX1 INP2", "RX5", "CDC_IF RX5 MUX"}, + {"RX INT2_1 MIX1 INP2", "RX6", "CDC_IF RX6 MUX"}, + {"RX INT2_1 MIX1 INP2", "RX7", "CDC_IF RX7 MUX"}, + {"RX INT2_1 MIX1 INP2", "IIR0", "IIR0"}, + {"RX INT2_1 MIX1 INP2", "IIR1", "IIR1"}, + + {"RX INT3_1 MIX1 INP0", "RX0", "CDC_IF RX0 MUX"}, + {"RX INT3_1 MIX1 INP0", "RX1", "CDC_IF RX1 MUX"}, + {"RX INT3_1 MIX1 INP0", "RX2", "CDC_IF RX2 MUX"}, + {"RX INT3_1 MIX1 INP0", "RX3", "CDC_IF RX3 MUX"}, + {"RX INT3_1 MIX1 INP0", "RX4", "CDC_IF RX4 MUX"}, + {"RX INT3_1 MIX1 INP0", "RX5", "CDC_IF RX5 MUX"}, + {"RX INT3_1 MIX1 INP0", "RX6", "CDC_IF RX6 MUX"}, + {"RX INT3_1 MIX1 INP0", "RX7", "CDC_IF RX7 MUX"}, + {"RX INT3_1 MIX1 INP0", "IIR0", "IIR0"}, + {"RX INT3_1 MIX1 INP0", "IIR1", "IIR1"}, + {"RX INT3_1 MIX1 INP1", "RX0", "CDC_IF RX0 MUX"}, + {"RX INT3_1 MIX1 INP1", "RX1", "CDC_IF RX1 MUX"}, + {"RX INT3_1 MIX1 INP1", "RX2", "CDC_IF RX2 MUX"}, + {"RX INT3_1 MIX1 INP1", "RX3", "CDC_IF RX3 MUX"}, + {"RX INT3_1 MIX1 INP1", "RX4", "CDC_IF RX4 MUX"}, + {"RX INT3_1 MIX1 INP1", "RX5", "CDC_IF RX5 MUX"}, + {"RX INT3_1 MIX1 INP1", "RX6", "CDC_IF RX6 MUX"}, + {"RX INT3_1 MIX1 INP1", "RX7", "CDC_IF RX7 MUX"}, + {"RX INT3_1 MIX1 INP1", "IIR0", "IIR0"}, + {"RX INT3_1 MIX1 INP1", "IIR1", "IIR1"}, + {"RX INT3_1 MIX1 INP2", "RX0", "CDC_IF RX0 MUX"}, + {"RX INT3_1 MIX1 INP2", "RX1", "CDC_IF RX1 MUX"}, + {"RX INT3_1 MIX1 INP2", "RX2", "CDC_IF RX2 MUX"}, + {"RX INT3_1 MIX1 INP2", "RX3", "CDC_IF RX3 MUX"}, + {"RX INT3_1 MIX1 INP2", "RX4", "CDC_IF RX4 MUX"}, + {"RX INT3_1 MIX1 INP2", "RX5", "CDC_IF RX5 MUX"}, + {"RX INT3_1 MIX1 INP2", "RX6", "CDC_IF RX6 MUX"}, + {"RX INT3_1 MIX1 INP2", "RX7", "CDC_IF RX7 MUX"}, + {"RX INT3_1 MIX1 INP2", "IIR0", "IIR0"}, + {"RX INT3_1 MIX1 INP2", "IIR1", "IIR1"}, + + {"RX INT4_1 MIX1 INP0", "RX0", "CDC_IF RX0 MUX"}, + {"RX INT4_1 MIX1 INP0", "RX1", "CDC_IF RX1 MUX"}, + {"RX INT4_1 MIX1 INP0", "RX2", "CDC_IF RX2 MUX"}, + {"RX INT4_1 MIX1 INP0", "RX3", "CDC_IF RX3 MUX"}, + {"RX INT4_1 MIX1 INP0", "RX4", "CDC_IF RX4 MUX"}, + {"RX INT4_1 MIX1 INP0", "RX5", "CDC_IF RX5 MUX"}, + {"RX INT4_1 MIX1 INP0", "RX6", "CDC_IF RX6 MUX"}, + {"RX INT4_1 MIX1 INP0", "RX7", "CDC_IF RX7 MUX"}, + {"RX INT4_1 MIX1 INP0", "IIR0", "IIR0"}, + {"RX INT4_1 MIX1 INP0", "IIR1", "IIR1"}, + {"RX INT4_1 MIX1 INP1", "RX0", "CDC_IF RX0 MUX"}, + {"RX INT4_1 MIX1 INP1", "RX1", "CDC_IF RX1 MUX"}, + {"RX INT4_1 MIX1 INP1", "RX2", "CDC_IF RX2 MUX"}, + {"RX INT4_1 MIX1 INP1", "RX3", "CDC_IF RX3 MUX"}, + {"RX INT4_1 MIX1 INP1", "RX4", "CDC_IF RX4 MUX"}, + {"RX INT4_1 MIX1 INP1", "RX5", "CDC_IF RX5 MUX"}, + {"RX INT4_1 MIX1 INP1", "RX6", "CDC_IF RX6 MUX"}, + {"RX INT4_1 MIX1 INP1", "RX7", "CDC_IF RX7 MUX"}, + {"RX INT4_1 MIX1 INP1", "IIR0", "IIR0"}, + {"RX INT4_1 MIX1 INP1", "IIR1", "IIR1"}, + {"RX INT4_1 MIX1 INP2", "RX0", "CDC_IF RX0 MUX"}, + {"RX INT4_1 MIX1 INP2", "RX1", "CDC_IF RX1 MUX"}, + {"RX INT4_1 MIX1 INP2", "RX2", "CDC_IF RX2 MUX"}, + {"RX INT4_1 MIX1 INP2", "RX3", "CDC_IF RX3 MUX"}, + {"RX INT4_1 MIX1 INP2", "RX4", "CDC_IF RX4 MUX"}, + {"RX INT4_1 MIX1 INP2", "RX5", "CDC_IF RX5 MUX"}, + {"RX INT4_1 MIX1 INP2", "RX6", "CDC_IF RX6 MUX"}, + {"RX INT4_1 MIX1 INP2", "RX7", "CDC_IF RX7 MUX"}, + {"RX INT4_1 MIX1 INP2", "IIR0", "IIR0"}, + {"RX INT4_1 MIX1 INP2", "IIR1", "IIR1"}, + + {"RX INT7_1 MIX1 INP0", "RX0", "CDC_IF RX0 MUX"}, + {"RX INT7_1 MIX1 INP0", "RX1", "CDC_IF RX1 MUX"}, + {"RX INT7_1 MIX1 INP0", "RX2", "CDC_IF RX2 MUX"}, + {"RX INT7_1 MIX1 INP0", "RX3", "CDC_IF RX3 MUX"}, + {"RX INT7_1 MIX1 INP0", "RX4", "CDC_IF RX4 MUX"}, + {"RX INT7_1 MIX1 INP0", "RX5", "CDC_IF RX5 MUX"}, + {"RX INT7_1 MIX1 INP0", "RX6", "CDC_IF RX6 MUX"}, + {"RX INT7_1 MIX1 INP0", "RX7", "CDC_IF RX7 MUX"}, + {"RX INT7_1 MIX1 INP0", "IIR0", "IIR0"}, + {"RX INT7_1 MIX1 INP0", "IIR1", "IIR1"}, + {"RX INT7_1 MIX1 INP1", "RX0", "CDC_IF RX0 MUX"}, + {"RX INT7_1 MIX1 INP1", "RX1", "CDC_IF RX1 MUX"}, + {"RX INT7_1 MIX1 INP1", "RX2", "CDC_IF RX2 MUX"}, + {"RX INT7_1 MIX1 INP1", "RX3", "CDC_IF RX3 MUX"}, + {"RX INT7_1 MIX1 INP1", "RX4", "CDC_IF RX4 MUX"}, + {"RX INT7_1 MIX1 INP1", "RX5", "CDC_IF RX5 MUX"}, + {"RX INT7_1 MIX1 INP1", "RX6", "CDC_IF RX6 MUX"}, + {"RX INT7_1 MIX1 INP1", "RX7", "CDC_IF RX7 MUX"}, + {"RX INT7_1 MIX1 INP1", "IIR0", "IIR0"}, + {"RX INT7_1 MIX1 INP1", "IIR1", "IIR1"}, + {"RX INT7_1 MIX1 INP2", "RX0", "CDC_IF RX0 MUX"}, + {"RX INT7_1 MIX1 INP2", "RX1", "CDC_IF RX1 MUX"}, + {"RX INT7_1 MIX1 INP2", "RX2", "CDC_IF RX2 MUX"}, + {"RX INT7_1 MIX1 INP2", "RX3", "CDC_IF RX3 MUX"}, + {"RX INT7_1 MIX1 INP2", "RX4", "CDC_IF RX4 MUX"}, + {"RX INT7_1 MIX1 INP2", "RX5", "CDC_IF RX5 MUX"}, + {"RX INT7_1 MIX1 INP2", "RX6", "CDC_IF RX6 MUX"}, + {"RX INT7_1 MIX1 INP2", "RX7", "CDC_IF RX7 MUX"}, + {"RX INT7_1 MIX1 INP2", "IIR0", "IIR0"}, + {"RX INT7_1 MIX1 INP2", "IIR1", "IIR1"}, + + {"RX INT8_1 MIX1 INP0", "RX0", "CDC_IF RX0 MUX"}, + {"RX INT8_1 MIX1 INP0", "RX1", "CDC_IF RX1 MUX"}, + {"RX INT8_1 MIX1 INP0", "RX2", "CDC_IF RX2 MUX"}, + {"RX INT8_1 MIX1 INP0", "RX3", "CDC_IF RX3 MUX"}, + {"RX INT8_1 MIX1 INP0", "RX4", "CDC_IF RX4 MUX"}, + {"RX INT8_1 MIX1 INP0", "RX5", "CDC_IF RX5 MUX"}, + {"RX INT8_1 MIX1 INP0", "RX6", "CDC_IF RX6 MUX"}, + {"RX INT8_1 MIX1 INP0", "RX7", "CDC_IF RX7 MUX"}, + {"RX INT8_1 MIX1 INP0", "IIR0", "IIR0"}, + {"RX INT8_1 MIX1 INP0", "IIR1", "IIR1"}, + {"RX INT8_1 MIX1 INP1", "RX0", "CDC_IF RX0 MUX"}, + {"RX INT8_1 MIX1 INP1", "RX1", "CDC_IF RX1 MUX"}, + {"RX INT8_1 MIX1 INP1", "RX2", "CDC_IF RX2 MUX"}, + {"RX INT8_1 MIX1 INP1", "RX3", "CDC_IF RX3 MUX"}, + {"RX INT8_1 MIX1 INP1", "RX4", "CDC_IF RX4 MUX"}, + {"RX INT8_1 MIX1 INP1", "RX5", "CDC_IF RX5 MUX"}, + {"RX INT8_1 MIX1 INP1", "RX6", "CDC_IF RX6 MUX"}, + {"RX INT8_1 MIX1 INP1", "RX7", "CDC_IF RX7 MUX"}, + {"RX INT8_1 MIX1 INP1", "IIR0", "IIR0"}, + {"RX INT8_1 MIX1 INP1", "IIR1", "IIR1"}, + {"RX INT8_1 MIX1 INP2", "RX0", "CDC_IF RX0 MUX"}, + {"RX INT8_1 MIX1 INP2", "RX1", "CDC_IF RX1 MUX"}, + {"RX INT8_1 MIX1 INP2", "RX2", "CDC_IF RX2 MUX"}, + {"RX INT8_1 MIX1 INP2", "RX3", "CDC_IF RX3 MUX"}, + {"RX INT8_1 MIX1 INP2", "RX4", "CDC_IF RX4 MUX"}, + {"RX INT8_1 MIX1 INP2", "RX5", "CDC_IF RX5 MUX"}, + {"RX INT8_1 MIX1 INP2", "RX6", "CDC_IF RX6 MUX"}, + {"RX INT8_1 MIX1 INP2", "RX7", "CDC_IF RX7 MUX"}, + {"RX INT8_1 MIX1 INP2", "IIR0", "IIR0"}, + {"RX INT8_1 MIX1 INP2", "IIR1", "IIR1"}, + + {"RX INT0_1 MIX1", NULL, "RX INT0_1 MIX1 INP0"}, + {"RX INT0_1 MIX1", NULL, "RX INT0_1 MIX1 INP1"}, + {"RX INT0_1 MIX1", NULL, "RX INT0_1 MIX1 INP2"}, + {"RX INT1_1 MIX1", NULL, "RX INT1_1 MIX1 INP0"}, + {"RX INT1_1 MIX1", NULL, "RX INT1_1 MIX1 INP1"}, + {"RX INT1_1 MIX1", NULL, "RX INT1_1 MIX1 INP2"}, + {"RX INT2_1 MIX1", NULL, "RX INT2_1 MIX1 INP0"}, + {"RX INT2_1 MIX1", NULL, "RX INT2_1 MIX1 INP1"}, + {"RX INT2_1 MIX1", NULL, "RX INT2_1 MIX1 INP2"}, + {"RX INT3_1 MIX1", NULL, "RX INT3_1 MIX1 INP0"}, + {"RX INT3_1 MIX1", NULL, "RX INT3_1 MIX1 INP1"}, + {"RX INT3_1 MIX1", NULL, "RX INT3_1 MIX1 INP2"}, + {"RX INT4_1 MIX1", NULL, "RX INT4_1 MIX1 INP0"}, + {"RX INT4_1 MIX1", NULL, "RX INT4_1 MIX1 INP1"}, + {"RX INT4_1 MIX1", NULL, "RX INT4_1 MIX1 INP2"}, + {"RX INT7_1 MIX1", NULL, "RX INT7_1 MIX1 INP0"}, + {"RX INT7_1 MIX1", NULL, "RX INT7_1 MIX1 INP1"}, + {"RX INT7_1 MIX1", NULL, "RX INT7_1 MIX1 INP2"}, + {"RX INT8_1 MIX1", NULL, "RX INT8_1 MIX1 INP0"}, + {"RX INT8_1 MIX1", NULL, "RX INT8_1 MIX1 INP1"}, + {"RX INT8_1 MIX1", NULL, "RX INT8_1 MIX1 INP2"}, + + /* Mixing path INT0 */ + {"RX INT0_2 MUX", "RX0", "CDC_IF RX0 MUX"}, + {"RX INT0_2 MUX", "RX1", "CDC_IF RX1 MUX"}, + {"RX INT0_2 MUX", "RX2", "CDC_IF RX2 MUX"}, + {"RX INT0_2 MUX", "RX3", "CDC_IF RX3 MUX"}, + {"RX INT0_2 MUX", "RX4", "CDC_IF RX4 MUX"}, + {"RX INT0_2 MUX", "RX5", "CDC_IF RX5 MUX"}, + {"RX INT0_2 MUX", "RX6", "CDC_IF RX6 MUX"}, + {"RX INT0_2 MUX", "RX7", "CDC_IF RX7 MUX"}, + {"RX INT0_2 INTERP", NULL, "RX INT0_2 MUX"}, + {"RX INT0 SEC MIX", NULL, "RX INT0_2 INTERP"}, + + /* Mixing path INT1 */ + {"RX INT1_2 MUX", "RX0", "CDC_IF RX0 MUX"}, + {"RX INT1_2 MUX", "RX1", "CDC_IF RX1 MUX"}, + {"RX INT1_2 MUX", "RX2", "CDC_IF RX2 MUX"}, + {"RX INT1_2 MUX", "RX3", "CDC_IF RX3 MUX"}, + {"RX INT1_2 MUX", "RX4", "CDC_IF RX4 MUX"}, + {"RX INT1_2 MUX", "RX5", "CDC_IF RX5 MUX"}, + {"RX INT1_2 MUX", "RX6", "CDC_IF RX6 MUX"}, + {"RX INT1_2 MUX", "RX7", "CDC_IF RX7 MUX"}, + {"RX INT1_2 INTERP", NULL, "RX INT1_2 MUX"}, + {"RX INT1 SEC MIX", NULL, "RX INT1_2 INTERP"}, + + /* Mixing path INT2 */ + {"RX INT2_2 MUX", "RX0", "CDC_IF RX0 MUX"}, + {"RX INT2_2 MUX", "RX1", "CDC_IF RX1 MUX"}, + {"RX INT2_2 MUX", "RX2", "CDC_IF RX2 MUX"}, + {"RX INT2_2 MUX", "RX3", "CDC_IF RX3 MUX"}, + {"RX INT2_2 MUX", "RX4", "CDC_IF RX4 MUX"}, + {"RX INT2_2 MUX", "RX5", "CDC_IF RX5 MUX"}, + {"RX INT2_2 MUX", "RX6", "CDC_IF RX6 MUX"}, + {"RX INT2_2 MUX", "RX7", "CDC_IF RX7 MUX"}, + {"RX INT2_2 INTERP", NULL, "RX INT2_2 MUX"}, + {"RX INT2 SEC MIX", NULL, "RX INT2_2 INTERP"}, + + /* Mixing path INT3 */ + {"RX INT3_2 MUX", "RX0", "CDC_IF RX0 MUX"}, + {"RX INT3_2 MUX", "RX1", "CDC_IF RX1 MUX"}, + {"RX INT3_2 MUX", "RX2", "CDC_IF RX2 MUX"}, + {"RX INT3_2 MUX", "RX3", "CDC_IF RX3 MUX"}, + {"RX INT3_2 MUX", "RX4", "CDC_IF RX4 MUX"}, + {"RX INT3_2 MUX", "RX5", "CDC_IF RX5 MUX"}, + {"RX INT3_2 MUX", "RX6", "CDC_IF RX6 MUX"}, + {"RX INT3_2 MUX", "RX7", "CDC_IF RX7 MUX"}, + {"RX INT3_2 INTERP", NULL, "RX INT3_2 MUX"}, + {"RX INT3 SEC MIX", NULL, "RX INT3_2 INTERP"}, + + /* Mixing path INT4 */ + {"RX INT4_2 MUX", "RX0", "CDC_IF RX0 MUX"}, + {"RX INT4_2 MUX", "RX1", "CDC_IF RX1 MUX"}, + {"RX INT4_2 MUX", "RX2", "CDC_IF RX2 MUX"}, + {"RX INT4_2 MUX", "RX3", "CDC_IF RX3 MUX"}, + {"RX INT4_2 MUX", "RX4", "CDC_IF RX4 MUX"}, + {"RX INT4_2 MUX", "RX5", "CDC_IF RX5 MUX"}, + {"RX INT4_2 MUX", "RX6", "CDC_IF RX6 MUX"}, + {"RX INT4_2 MUX", "RX7", "CDC_IF RX7 MUX"}, + {"RX INT4_2 INTERP", NULL, "RX INT4_2 MUX"}, + {"RX INT4 SEC MIX", NULL, "RX INT4_2 INTERP"}, + + /* Mixing path INT7 */ + {"RX INT7_2 MUX", "RX0", "CDC_IF RX0 MUX"}, + {"RX INT7_2 MUX", "RX1", "CDC_IF RX1 MUX"}, + {"RX INT7_2 MUX", "RX2", "CDC_IF RX2 MUX"}, + {"RX INT7_2 MUX", "RX3", "CDC_IF RX3 MUX"}, + {"RX INT7_2 MUX", "RX4", "CDC_IF RX4 MUX"}, + {"RX INT7_2 MUX", "RX5", "CDC_IF RX5 MUX"}, + {"RX INT7_2 MUX", "RX6", "CDC_IF RX6 MUX"}, + {"RX INT7_2 MUX", "RX7", "CDC_IF RX7 MUX"}, + {"RX INT7_2 INTERP", NULL, "RX INT7_2 MUX"}, + {"RX INT7 SEC MIX", NULL, "RX INT7_2 INTERP"}, + + /* Mixing path INT8 */ + {"RX INT8_2 MUX", "RX0", "CDC_IF RX0 MUX"}, + {"RX INT8_2 MUX", "RX1", "CDC_IF RX1 MUX"}, + {"RX INT8_2 MUX", "RX2", "CDC_IF RX2 MUX"}, + {"RX INT8_2 MUX", "RX3", "CDC_IF RX3 MUX"}, + {"RX INT8_2 MUX", "RX4", "CDC_IF RX4 MUX"}, + {"RX INT8_2 MUX", "RX5", "CDC_IF RX5 MUX"}, + {"RX INT8_2 MUX", "RX6", "CDC_IF RX6 MUX"}, + {"RX INT8_2 MUX", "RX7", "CDC_IF RX7 MUX"}, + {"RX INT8_2 INTERP", NULL, "RX INT8_2 MUX"}, + {"RX INT8 SEC MIX", NULL, "RX INT8_2 INTERP"}, + + {"RX INT0_1 INTERP", NULL, "RX INT0_1 MIX1"}, + {"RX INT0 SEC MIX", NULL, "RX INT0_1 INTERP"}, + {"RX INT0 MIX2", NULL, "RX INT0 SEC MIX"}, + {"RX INT0 MIX2", NULL, "RX INT0 MIX2 INP"}, + {"RX INT0 DEM MUX", "CLSH_DSM_OUT", "RX INT0 MIX2"}, + {"RX INT0 DAC", NULL, "RX INT0 DEM MUX"}, + {"RX INT0 DAC", NULL, "RX_BIAS"}, + {"EAR PA", NULL, "RX INT0 DAC"}, + {"EAR", NULL, "EAR PA"}, + + {"RX INT1_1 INTERP", NULL, "RX INT1_1 MIX1"}, + {"RX INT1 SEC MIX", NULL, "RX INT1_1 INTERP"}, + {"RX INT1 MIX2", NULL, "RX INT1 SEC MIX"}, + {"RX INT1 MIX2", NULL, "RX INT1 MIX2 INP"}, + {"RX INT1 MIX3", NULL, "RX INT1 MIX2"}, + {"RX INT1 DEM MUX", "CLSH_DSM_OUT", "RX INT1 MIX3"}, + {"RX INT1 DAC", NULL, "RX INT1 DEM MUX"}, + {"RX INT1 DAC", NULL, "RX_BIAS"}, + {"HPHL PA", NULL, "RX INT1 DAC"}, + {"HPHL", NULL, "HPHL PA"}, + + {"RX INT2_1 INTERP", NULL, "RX INT2_1 MIX1"}, + {"RX INT2 SEC MIX", NULL, "RX INT2_1 INTERP"}, + {"RX INT2 MIX2", NULL, "RX INT2 SEC MIX"}, + {"RX INT2 MIX2", NULL, "RX INT2 MIX2 INP"}, + {"RX INT2 MIX3", NULL, "RX INT2 MIX2"}, + {"RX INT2 DEM MUX", "CLSH_DSM_OUT", "RX INT2 MIX3"}, + {"RX INT2 DAC", NULL, "RX INT2 DEM MUX"}, + {"RX INT2 DAC", NULL, "RX_BIAS"}, + {"HPHR PA", NULL, "RX INT2 DAC"}, + {"HPHR", NULL, "HPHR PA"}, + + {"RX INT3_1 INTERP", NULL, "RX INT3_1 MIX1"}, + {"RX INT3 SEC MIX", NULL, "RX INT3_1 INTERP"}, + {"RX INT3 MIX2", NULL, "RX INT3 SEC MIX"}, + {"RX INT3 MIX2", NULL, "RX INT3 MIX2 INP"}, + {"RX INT3 MIX3", NULL, "RX INT3 MIX2"}, + {"RX INT3 DAC", NULL, "RX INT3 MIX3"}, + {"RX INT3 DAC", NULL, "RX_BIAS"}, + {"LINEOUT1 PA", NULL, "RX INT3 DAC"}, + {"LINEOUT1", NULL, "LINEOUT1 PA"}, + + {"RX INT4_1 INTERP", NULL, "RX INT4_1 MIX1"}, + {"RX INT4 SEC MIX", NULL, "RX INT4_1 INTERP"}, + {"RX INT4 SEC MIX", NULL, "RX INT4_1 MIX1"}, + {"RX INT4 MIX2", NULL, "RX INT4 SEC MIX"}, + {"RX INT4 MIX2", NULL, "RX INT4 MIX2 INP"}, + {"RX INT4 MIX3", NULL, "RX INT4 MIX2"}, + {"RX INT4 DAC", NULL, "RX INT4 MIX3"}, + {"RX INT4 DAC", NULL, "RX_BIAS"}, + {"LINEOUT2 PA", NULL, "RX INT4 DAC"}, + {"LINEOUT2", NULL, "LINEOUT2 PA"}, + + {"RX INT7_1 INTERP", NULL, "RX INT7_1 MIX1"}, + {"RX INT7 SEC MIX", NULL, "RX INT7_1 INTERP"}, + {"RX INT7 MIX2", NULL, "RX INT7 SEC MIX"}, + {"RX INT7 MIX2", NULL, "RX INT7 MIX2 INP"}, + {"RX INT7 CHAIN", NULL, "RX INT7 MIX2"}, + {"RX INT7 CHAIN", NULL, "RX_BIAS"}, + {"SPK1 OUT", NULL, "RX INT7 CHAIN"}, + + {"RX INT8_1 INTERP", NULL, "RX INT8_1 MIX1"}, + {"RX INT8 SEC MIX", NULL, "RX INT8_1 INTERP"}, + {"RX INT8 SEC MIX", NULL, "RX INT8_1 MIX1"}, + {"RX INT8 CHAIN", NULL, "RX INT8 SEC MIX"}, + {"RX INT8 CHAIN", NULL, "RX_BIAS"}, + {"SPK2 OUT", NULL, "RX INT8 CHAIN"}, + + /* ANC Routing */ + {"ANC0 FB MUX", "ANC_IN_EAR", "RX INT0 MIX2"}, + {"ANC0 FB MUX", "ANC_IN_HPHL", "RX INT1 MIX2"}, + {"ANC0 FB MUX", "ANC_IN_LO1", "RX INT3 MIX2"}, + {"ANC0 FB MUX", "ANC_IN_EAR_SPKR", "RX INT7 MIX2"}, + {"ANC1 FB MUX", "ANC_IN_HPHR", "RX INT2 MIX2"}, + {"ANC1 FB MUX", "ANC_IN_LO2", "RX INT4 MIX2"}, + + {"ANC OUT EAR Enable", "Switch", "ADC MUX10"}, + {"ANC OUT EAR Enable", "Switch", "ADC MUX11"}, + {"RX INT0 MIX2", NULL, "ANC OUT EAR Enable"}, + + {"ANC OUT HPHL Enable", "Switch", "ADC MUX10"}, + {"ANC OUT HPHL Enable", "Switch", "ADC MUX11"}, + {"RX INT1 MIX2", NULL, "ANC OUT HPHL Enable"}, + + {"ANC OUT HPHR Enable", "Switch", "ADC MUX12"}, + {"ANC OUT HPHR Enable", "Switch", "ADC MUX13"}, + {"RX INT2 MIX2", NULL, "ANC OUT HPHR Enable"}, + + {"ANC EAR PA", NULL, "RX INT0 DAC"}, + {"ANC EAR", NULL, "ANC EAR PA"}, + + {"ANC HPHL PA", NULL, "RX INT1 DAC"}, + {"ANC HPHL", NULL, "ANC HPHL PA"}, + + {"ANC HPHR PA", NULL, "RX INT2 DAC"}, + {"ANC HPHR", NULL, "ANC HPHR PA"}, + + {"ANC OUT EAR SPKR Enable", "Switch", "ADC MUX10"}, + {"ANC OUT EAR SPKR Enable", "Switch", "ADC MUX11"}, + {"RX INT7 MIX2", NULL, "ANC OUT EAR SPKR Enable"}, + + {"ANC SPKR PA Enable", "Switch", "RX INT7 CHAIN"}, + {"ANC SPK1 PA", NULL, "ANC SPKR PA Enable"}, + {"SPK1 OUT", NULL, "ANC SPK1 PA"}, + + /* + * SRC0, SRC1 inputs to Sidetone RX Mixer + * on RX0, RX1, RX2, RX3, RX4 and RX7 chains + */ + {"IIR0", NULL, "IIR0 INP0 MUX"}, + {"IIR0 INP0 MUX", "DEC0", "ADC MUX0"}, + {"IIR0 INP0 MUX", "DEC1", "ADC MUX1"}, + {"IIR0 INP0 MUX", "DEC2", "ADC MUX2"}, + {"IIR0 INP0 MUX", "DEC3", "ADC MUX3"}, + {"IIR0 INP0 MUX", "DEC4", "ADC MUX4"}, + {"IIR0 INP0 MUX", "DEC5", "ADC MUX5"}, + {"IIR0 INP0 MUX", "DEC6", "ADC MUX6"}, + {"IIR0 INP0 MUX", "DEC7", "ADC MUX7"}, + {"IIR0 INP0 MUX", "DEC8", "ADC MUX8"}, + {"IIR0 INP0 MUX", "RX0", "CDC_IF RX0 MUX"}, + {"IIR0 INP0 MUX", "RX1", "CDC_IF RX1 MUX"}, + {"IIR0 INP0 MUX", "RX2", "CDC_IF RX2 MUX"}, + {"IIR0 INP0 MUX", "RX3", "CDC_IF RX3 MUX"}, + {"IIR0 INP0 MUX", "RX4", "CDC_IF RX4 MUX"}, + {"IIR0 INP0 MUX", "RX5", "CDC_IF RX5 MUX"}, + {"IIR0 INP0 MUX", "RX6", "CDC_IF RX6 MUX"}, + {"IIR0 INP0 MUX", "RX7", "CDC_IF RX7 MUX"}, + {"IIR0", NULL, "IIR0 INP1 MUX"}, + {"IIR0 INP1 MUX", "DEC0", "ADC MUX0"}, + {"IIR0 INP1 MUX", "DEC1", "ADC MUX1"}, + {"IIR0 INP1 MUX", "DEC2", "ADC MUX2"}, + {"IIR0 INP1 MUX", "DEC3", "ADC MUX3"}, + {"IIR0 INP1 MUX", "DEC4", "ADC MUX4"}, + {"IIR0 INP1 MUX", "DEC5", "ADC MUX5"}, + {"IIR0 INP1 MUX", "DEC6", "ADC MUX6"}, + {"IIR0 INP1 MUX", "DEC7", "ADC MUX7"}, + {"IIR0 INP1 MUX", "DEC8", "ADC MUX8"}, + {"IIR0 INP1 MUX", "RX0", "CDC_IF RX0 MUX"}, + {"IIR0 INP1 MUX", "RX1", "CDC_IF RX1 MUX"}, + {"IIR0 INP1 MUX", "RX2", "CDC_IF RX2 MUX"}, + {"IIR0 INP1 MUX", "RX3", "CDC_IF RX3 MUX"}, + {"IIR0 INP1 MUX", "RX4", "CDC_IF RX4 MUX"}, + {"IIR0 INP1 MUX", "RX5", "CDC_IF RX5 MUX"}, + {"IIR0 INP1 MUX", "RX6", "CDC_IF RX6 MUX"}, + {"IIR0 INP1 MUX", "RX7", "CDC_IF RX7 MUX"}, + {"IIR0", NULL, "IIR0 INP2 MUX"}, + {"IIR0 INP2 MUX", "DEC0", "ADC MUX0"}, + {"IIR0 INP2 MUX", "DEC1", "ADC MUX1"}, + {"IIR0 INP2 MUX", "DEC2", "ADC MUX2"}, + {"IIR0 INP2 MUX", "DEC3", "ADC MUX3"}, + {"IIR0 INP2 MUX", "DEC4", "ADC MUX4"}, + {"IIR0 INP2 MUX", "DEC5", "ADC MUX5"}, + {"IIR0 INP2 MUX", "DEC6", "ADC MUX6"}, + {"IIR0 INP2 MUX", "DEC7", "ADC MUX7"}, + {"IIR0 INP2 MUX", "DEC8", "ADC MUX8"}, + {"IIR0 INP2 MUX", "RX0", "CDC_IF RX0 MUX"}, + {"IIR0 INP2 MUX", "RX1", "CDC_IF RX1 MUX"}, + {"IIR0 INP2 MUX", "RX2", "CDC_IF RX2 MUX"}, + {"IIR0 INP2 MUX", "RX3", "CDC_IF RX3 MUX"}, + {"IIR0 INP2 MUX", "RX4", "CDC_IF RX4 MUX"}, + {"IIR0 INP2 MUX", "RX5", "CDC_IF RX5 MUX"}, + {"IIR0 INP2 MUX", "RX6", "CDC_IF RX6 MUX"}, + {"IIR0 INP2 MUX", "RX7", "CDC_IF RX7 MUX"}, + {"IIR0", NULL, "IIR0 INP3 MUX"}, + {"IIR0 INP3 MUX", "DEC0", "ADC MUX0"}, + {"IIR0 INP3 MUX", "DEC1", "ADC MUX1"}, + {"IIR0 INP3 MUX", "DEC2", "ADC MUX2"}, + {"IIR0 INP3 MUX", "DEC3", "ADC MUX3"}, + {"IIR0 INP3 MUX", "DEC4", "ADC MUX4"}, + {"IIR0 INP3 MUX", "DEC5", "ADC MUX5"}, + {"IIR0 INP3 MUX", "DEC6", "ADC MUX6"}, + {"IIR0 INP3 MUX", "DEC7", "ADC MUX7"}, + {"IIR0 INP3 MUX", "DEC8", "ADC MUX8"}, + {"IIR0 INP3 MUX", "RX0", "CDC_IF RX0 MUX"}, + {"IIR0 INP3 MUX", "RX1", "CDC_IF RX1 MUX"}, + {"IIR0 INP3 MUX", "RX2", "CDC_IF RX2 MUX"}, + {"IIR0 INP3 MUX", "RX3", "CDC_IF RX3 MUX"}, + {"IIR0 INP3 MUX", "RX4", "CDC_IF RX4 MUX"}, + {"IIR0 INP3 MUX", "RX5", "CDC_IF RX5 MUX"}, + {"IIR0 INP3 MUX", "RX6", "CDC_IF RX6 MUX"}, + {"IIR0 INP3 MUX", "RX7", "CDC_IF RX7 MUX"}, + + {"IIR1", NULL, "IIR1 INP0 MUX"}, + {"IIR1 INP0 MUX", "DEC0", "ADC MUX0"}, + {"IIR1 INP0 MUX", "DEC1", "ADC MUX1"}, + {"IIR1 INP0 MUX", "DEC2", "ADC MUX2"}, + {"IIR1 INP0 MUX", "DEC3", "ADC MUX3"}, + {"IIR1 INP0 MUX", "DEC4", "ADC MUX4"}, + {"IIR1 INP0 MUX", "DEC5", "ADC MUX5"}, + {"IIR1 INP0 MUX", "DEC6", "ADC MUX6"}, + {"IIR1 INP0 MUX", "DEC7", "ADC MUX7"}, + {"IIR1 INP0 MUX", "DEC8", "ADC MUX8"}, + {"IIR1 INP0 MUX", "RX0", "CDC_IF RX0 MUX"}, + {"IIR1 INP0 MUX", "RX1", "CDC_IF RX1 MUX"}, + {"IIR1 INP0 MUX", "RX2", "CDC_IF RX2 MUX"}, + {"IIR1 INP0 MUX", "RX3", "CDC_IF RX3 MUX"}, + {"IIR1 INP0 MUX", "RX4", "CDC_IF RX4 MUX"}, + {"IIR1 INP0 MUX", "RX5", "CDC_IF RX5 MUX"}, + {"IIR1 INP0 MUX", "RX6", "CDC_IF RX6 MUX"}, + {"IIR1 INP0 MUX", "RX7", "CDC_IF RX7 MUX"}, + {"IIR1", NULL, "IIR1 INP1 MUX"}, + {"IIR1 INP1 MUX", "DEC0", "ADC MUX0"}, + {"IIR1 INP1 MUX", "DEC1", "ADC MUX1"}, + {"IIR1 INP1 MUX", "DEC2", "ADC MUX2"}, + {"IIR1 INP1 MUX", "DEC3", "ADC MUX3"}, + {"IIR1 INP1 MUX", "DEC4", "ADC MUX4"}, + {"IIR1 INP1 MUX", "DEC5", "ADC MUX5"}, + {"IIR1 INP1 MUX", "DEC6", "ADC MUX6"}, + {"IIR1 INP1 MUX", "DEC7", "ADC MUX7"}, + {"IIR1 INP1 MUX", "DEC8", "ADC MUX8"}, + {"IIR1 INP1 MUX", "RX0", "CDC_IF RX0 MUX"}, + {"IIR1 INP1 MUX", "RX1", "CDC_IF RX1 MUX"}, + {"IIR1 INP1 MUX", "RX2", "CDC_IF RX2 MUX"}, + {"IIR1 INP1 MUX", "RX3", "CDC_IF RX3 MUX"}, + {"IIR1 INP1 MUX", "RX4", "CDC_IF RX4 MUX"}, + {"IIR1 INP1 MUX", "RX5", "CDC_IF RX5 MUX"}, + {"IIR1 INP1 MUX", "RX6", "CDC_IF RX6 MUX"}, + {"IIR1 INP1 MUX", "RX7", "CDC_IF RX7 MUX"}, + {"IIR1", NULL, "IIR1 INP2 MUX"}, + {"IIR1 INP2 MUX", "DEC0", "ADC MUX0"}, + {"IIR1 INP2 MUX", "DEC1", "ADC MUX1"}, + {"IIR1 INP2 MUX", "DEC2", "ADC MUX2"}, + {"IIR1 INP2 MUX", "DEC3", "ADC MUX3"}, + {"IIR1 INP2 MUX", "DEC4", "ADC MUX4"}, + {"IIR1 INP2 MUX", "DEC5", "ADC MUX5"}, + {"IIR1 INP2 MUX", "DEC6", "ADC MUX6"}, + {"IIR1 INP2 MUX", "DEC7", "ADC MUX7"}, + {"IIR1 INP2 MUX", "DEC8", "ADC MUX8"}, + {"IIR1 INP2 MUX", "RX0", "CDC_IF RX0 MUX"}, + {"IIR1 INP2 MUX", "RX1", "CDC_IF RX1 MUX"}, + {"IIR1 INP2 MUX", "RX2", "CDC_IF RX2 MUX"}, + {"IIR1 INP2 MUX", "RX3", "CDC_IF RX3 MUX"}, + {"IIR1 INP2 MUX", "RX4", "CDC_IF RX4 MUX"}, + {"IIR1 INP2 MUX", "RX5", "CDC_IF RX5 MUX"}, + {"IIR1 INP2 MUX", "RX6", "CDC_IF RX6 MUX"}, + {"IIR1 INP2 MUX", "RX7", "CDC_IF RX7 MUX"}, + {"IIR1", NULL, "IIR1 INP3 MUX"}, + {"IIR1 INP3 MUX", "DEC0", "ADC MUX0"}, + {"IIR1 INP3 MUX", "DEC1", "ADC MUX1"}, + {"IIR1 INP3 MUX", "DEC2", "ADC MUX2"}, + {"IIR1 INP3 MUX", "DEC3", "ADC MUX3"}, + {"IIR1 INP3 MUX", "DEC4", "ADC MUX4"}, + {"IIR1 INP3 MUX", "DEC5", "ADC MUX5"}, + {"IIR1 INP3 MUX", "DEC6", "ADC MUX6"}, + {"IIR1 INP3 MUX", "DEC7", "ADC MUX7"}, + {"IIR1 INP3 MUX", "DEC8", "ADC MUX8"}, + {"IIR1 INP3 MUX", "RX0", "CDC_IF RX0 MUX"}, + {"IIR1 INP3 MUX", "RX1", "CDC_IF RX1 MUX"}, + {"IIR1 INP3 MUX", "RX2", "CDC_IF RX2 MUX"}, + {"IIR1 INP3 MUX", "RX3", "CDC_IF RX3 MUX"}, + {"IIR1 INP3 MUX", "RX4", "CDC_IF RX4 MUX"}, + {"IIR1 INP3 MUX", "RX5", "CDC_IF RX5 MUX"}, + {"IIR1 INP3 MUX", "RX6", "CDC_IF RX6 MUX"}, + {"IIR1 INP3 MUX", "RX7", "CDC_IF RX7 MUX"}, + + {"SRC0", NULL, "IIR0"}, + {"SRC1", NULL, "IIR1"}, + {"RX INT0 MIX2 INP", "SRC0", "SRC0"}, + {"RX INT0 MIX2 INP", "SRC1", "SRC1"}, + {"RX INT1 MIX2 INP", "SRC0", "SRC0"}, + {"RX INT1 MIX2 INP", "SRC1", "SRC1"}, + {"RX INT2 MIX2 INP", "SRC0", "SRC0"}, + {"RX INT2 MIX2 INP", "SRC1", "SRC1"}, + {"RX INT3 MIX2 INP", "SRC0", "SRC0"}, + {"RX INT3 MIX2 INP", "SRC1", "SRC1"}, + {"RX INT4 MIX2 INP", "SRC0", "SRC0"}, + {"RX INT4 MIX2 INP", "SRC1", "SRC1"}, + {"RX INT7 MIX2 INP", "SRC0", "SRC0"}, + {"RX INT7 MIX2 INP", "SRC1", "SRC1"}, + + /* Native clk main path routing */ + {"RX INT1_1 NATIVE MUX", "ON", "RX INT1_1 MIX1"}, + {"RX INT1_1 INTERP", NULL, "RX INT1_1 NATIVE MUX"}, + {"RX INT1_1 NATIVE MUX", NULL, "RX INT1 NATIVE SUPPLY"}, + + {"RX INT2_1 NATIVE MUX", "ON", "RX INT2_1 MIX1"}, + {"RX INT2_1 INTERP", NULL, "RX INT2_1 NATIVE MUX"}, + {"RX INT2_1 NATIVE MUX", NULL, "RX INT2 NATIVE SUPPLY"}, + + {"RX INT3_1 NATIVE MUX", "ON", "RX INT3_1 MIX1"}, + {"RX INT3_1 INTERP", NULL, "RX INT3_1 NATIVE MUX"}, + {"RX INT3_1 NATIVE MUX", NULL, "RX INT3 NATIVE SUPPLY"}, + + {"RX INT4_1 NATIVE MUX", "ON", "RX INT4_1 MIX1"}, + {"RX INT4_1 INTERP", NULL, "RX INT4_1 NATIVE MUX"}, + {"RX INT4_1 NATIVE MUX", NULL, "RX INT4 NATIVE SUPPLY"}, + + /* Native clk mix path routing */ + {"RX INT1_2 NATIVE MUX", "ON", "RX INT1_2 MUX"}, + {"RX INT1_2 INTERP", NULL, "RX INT1_2 NATIVE MUX"}, + {"RX INT1_2 NATIVE MUX", NULL, "RX INT1 NATIVE SUPPLY"}, + + {"RX INT2_2 NATIVE MUX", "ON", "RX INT2_2 MUX"}, + {"RX INT2_2 INTERP", NULL, "RX INT2_2 NATIVE MUX"}, + {"RX INT2_2 NATIVE MUX", NULL, "RX INT2 NATIVE SUPPLY"}, + + {"RX INT3_2 NATIVE MUX", "ON", "RX INT3_2 MUX"}, + {"RX INT3_2 INTERP", NULL, "RX INT3_2 NATIVE MUX"}, + {"RX INT3_2 NATIVE MUX", NULL, "RX INT3 NATIVE SUPPLY"}, + + {"RX INT4_2 NATIVE MUX", "ON", "RX INT4_2 MUX"}, + {"RX INT4_2 INTERP", NULL, "RX INT4_2 NATIVE MUX"}, + {"RX INT4_2 NATIVE MUX", NULL, "RX INT4 NATIVE SUPPLY"}, + + {"RX INT7_2 NATIVE MUX", "ON", "RX INT7_2 MUX"}, + {"RX INT7_2 INTERP", NULL, "RX INT7_2 NATIVE MUX"}, + {"RX INT7_2 NATIVE MUX", NULL, "RX INT7 NATIVE SUPPLY"}, + + {"RX INT8_2 NATIVE MUX", "ON", "RX INT8_2 MUX"}, + {"RX INT8_2 INTERP", NULL, "RX INT8_2 NATIVE MUX"}, + {"RX INT8_2 NATIVE MUX", NULL, "RX INT8 NATIVE SUPPLY"}, + + /* ASRC Routing */ + {"ASRC0 MUX", "ASRC_IN_HPHL", "RX INT1_2 INTERP"}, + {"RX INT1 SEC MIX", "HPHL Switch", "ASRC0 MUX"}, + + {"ASRC1 MUX", "ASRC_IN_HPHR", "RX INT2_2 INTERP"}, + {"RX INT2 SEC MIX", "HPHR Switch", "ASRC1 MUX"}, + + {"ASRC0 MUX", "ASRC_IN_LO1", "RX INT3_2 INTERP"}, + {"RX INT3 SEC MIX", "LO1 Switch", "ASRC0 MUX"}, + + {"ASRC1 MUX", "ASRC_IN_LO2", "RX INT4_2 INTERP"}, + {"RX INT4 SEC MIX", "LO2 Switch", "ASRC1 MUX"}, + + {"ASRC2 MUX", "ASRC_IN_SPKR1", "RX INT7_2 INTERP"}, + {"RX INT7 SEC MIX", NULL, "ASRC2 MUX"}, + + {"ASRC3 MUX", "ASRC_IN_SPKR2", "RX INT8_2 INTERP"}, + {"RX INT8 SEC MIX", NULL, "ASRC3 MUX"}, +}; + +#endif diff --git a/audio-kernel/asoc/codecs/wcd934x/wcd934x-tables.c b/audio-kernel/asoc/codecs/wcd934x/wcd934x-tables.c new file mode 100644 index 000000000..4e6c2fb2a --- /dev/null +++ b/audio-kernel/asoc/codecs/wcd934x/wcd934x-tables.c @@ -0,0 +1,2155 @@ +/* + * Copyright (c) 2016, 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 +#include + +#define WCD934X_REG(reg) ((reg) & 0xFF) + +const u8 wcd934x_page0_reg_access[WCD934X_PAGE_SIZE] = { + [WCD934X_REG(WCD934X_PAGE0_PAGE_REGISTER)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_RPM_CLK_BYPASS)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_RPM_CLK_GATE)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_RPM_CLK_MCLK_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_RPM_CLK_MCLK2_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_RPM_I2S_DSD_CLK_SEL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_RPM_RST_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_RPM_PWR_CDC_DIG_HM_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CHIP_TIER_CTRL_CHIP_ID_BYTE0)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CHIP_TIER_CTRL_CHIP_ID_BYTE1)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CHIP_TIER_CTRL_CHIP_ID_BYTE2)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CHIP_TIER_CTRL_CHIP_ID_BYTE3)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CHIP_TIER_CTRL_EFUSE_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CHIP_TIER_CTRL_EFUSE_TEST0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CHIP_TIER_CTRL_EFUSE_TEST1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT0)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT1)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT2)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT3)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT4)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT5)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT6)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT7)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT8)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT9)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT10)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT11)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT12)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT13)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT14)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT15)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CHIP_TIER_CTRL_EFUSE_STATUS)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CHIP_TIER_CTRL_I2C_SLAVE_ID_NONNEGO)] = + WCD934X_READ, + [WCD934X_REG(WCD934X_CHIP_TIER_CTRL_I2C_SLAVE_ID_1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CHIP_TIER_CTRL_I2C_SLAVE_ID_2)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CHIP_TIER_CTRL_I2C_SLAVE_ID_3)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CHIP_TIER_CTRL_ANA_WAIT_STATE_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CHIP_TIER_CTRL_SLNQ_WAIT_STATE_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CHIP_TIER_CTRL_I2C_ACTIVE)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CHIP_TIER_CTRL_ALT_FUNC_EN)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CHIP_TIER_CTRL_GPIO_CTL_OE)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CHIP_TIER_CTRL_GPIO_CTL_DATA)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DATA_HUB_RX0_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DATA_HUB_RX1_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DATA_HUB_RX2_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DATA_HUB_RX3_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DATA_HUB_RX4_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DATA_HUB_RX5_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DATA_HUB_RX6_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DATA_HUB_RX7_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DATA_HUB_SB_TX0_INP_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DATA_HUB_SB_TX1_INP_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DATA_HUB_SB_TX2_INP_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DATA_HUB_SB_TX3_INP_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DATA_HUB_SB_TX4_INP_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DATA_HUB_SB_TX5_INP_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DATA_HUB_SB_TX6_INP_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DATA_HUB_SB_TX7_INP_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DATA_HUB_SB_TX8_INP_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DATA_HUB_SB_TX9_INP_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DATA_HUB_SB_TX10_INP_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DATA_HUB_SB_TX11_INP_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DATA_HUB_SB_TX13_INP_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DATA_HUB_SB_TX14_INP_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DATA_HUB_SB_TX15_INP_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DATA_HUB_I2S_TX0_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DATA_HUB_I2S_TX1_0_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DATA_HUB_I2S_TX1_1_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DATA_HUB_I2S_0_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DATA_HUB_I2S_1_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DATA_HUB_I2S_2_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DATA_HUB_I2S_3_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DATA_HUB_I2S_CLKSRC_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DATA_HUB_I2S_COMMON_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DATA_HUB_I2S_0_TDM_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DATA_HUB_I2S_STATUS)] = WCD934X_READ, + [WCD934X_REG(WCD934X_DMA_RDMA_CTL_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_CH_2_3_CFG_RDMA_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_CH_0_1_CFG_RDMA_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_RDMA_CTL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_CH_2_3_CFG_RDMA_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_CH_0_1_CFG_RDMA_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_RDMA_CTL_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_CH_2_3_CFG_RDMA_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_CH_0_1_CFG_RDMA_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_RDMA_CTL_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_CH_2_3_CFG_RDMA_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_CH_0_1_CFG_RDMA_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_RDMA_CTL_4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_CH_2_3_CFG_RDMA_4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_CH_0_1_CFG_RDMA_4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_RDMA4_PRT_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_RDMA_SBTX0_7_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_RDMA_SBTX8_11_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_WDMA_CTL_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_CH_4_5_CFG_WDMA_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_CH_2_3_CFG_WDMA_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_CH_0_1_CFG_WDMA_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_WDMA_CTL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_CH_4_5_CFG_WDMA_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_CH_2_3_CFG_WDMA_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_CH_0_1_CFG_WDMA_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_WDMA_CTL_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_CH_4_5_CFG_WDMA_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_CH_2_3_CFG_WDMA_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_CH_0_1_CFG_WDMA_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_WDMA_CTL_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_CH_4_5_CFG_WDMA_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_CH_2_3_CFG_WDMA_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_CH_0_1_CFG_WDMA_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_WDMA_CTL_4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_CH_4_5_CFG_WDMA_4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_CH_2_3_CFG_WDMA_4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_CH_0_1_CFG_WDMA_4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_WDMA0_PRT_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_WDMA3_PRT_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_WDMA4_PRT0_3_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_WDMA4_PRT4_7_CFG)] = WCD934X_READ_WRITE, +}; + +const u8 wcd934x_page1_reg_access[WCD934X_PAGE_SIZE] = { + [WCD934X_REG(WCD934X_PAGE1_PAGE_REGISTER)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_FLL_USER_CTL_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_FLL_USER_CTL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_FLL_USER_CTL_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_FLL_USER_CTL_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_FLL_USER_CTL_4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_FLL_USER_CTL_5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_FLL_USER_CTL_6)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_FLL_USER_CTL_7)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_FLL_USER_CTL_8)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_FLL_USER_CTL_9)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_FLL_L_VAL_CTL_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_FLL_L_VAL_CTL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_FLL_DSM_FRAC_CTL_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_FLL_DSM_FRAC_CTL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_FLL_CONFIG_CTL_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_FLL_CONFIG_CTL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_FLL_CONFIG_CTL_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_FLL_CONFIG_CTL_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_FLL_CONFIG_CTL_4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_FLL_TEST_CTL_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_FLL_TEST_CTL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_FLL_TEST_CTL_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_FLL_TEST_CTL_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_FLL_TEST_CTL_4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_FLL_TEST_CTL_5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_FLL_TEST_CTL_6)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_FLL_TEST_CTL_7)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_FLL_FREQ_CTL_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_FLL_FREQ_CTL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_FLL_FREQ_CTL_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_FLL_FREQ_CTL_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_FLL_SSC_CTL_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_FLL_SSC_CTL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_FLL_SSC_CTL_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_FLL_SSC_CTL_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_FLL_FLL_MODE)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_FLL_STATUS_0)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CPE_FLL_STATUS_1)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CPE_FLL_STATUS_2)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CPE_FLL_STATUS_3)] = WCD934X_READ, + [WCD934X_REG(WCD934X_I2S_FLL_USER_CTL_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_I2S_FLL_USER_CTL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_I2S_FLL_USER_CTL_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_I2S_FLL_USER_CTL_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_I2S_FLL_USER_CTL_4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_I2S_FLL_USER_CTL_5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_I2S_FLL_USER_CTL_6)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_I2S_FLL_USER_CTL_7)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_I2S_FLL_USER_CTL_8)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_I2S_FLL_USER_CTL_9)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_I2S_FLL_L_VAL_CTL_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_I2S_FLL_L_VAL_CTL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_I2S_FLL_DSM_FRAC_CTL_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_I2S_FLL_DSM_FRAC_CTL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_I2S_FLL_CONFIG_CTL_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_I2S_FLL_CONFIG_CTL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_I2S_FLL_CONFIG_CTL_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_I2S_FLL_CONFIG_CTL_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_I2S_FLL_CONFIG_CTL_4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_I2S_FLL_TEST_CTL_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_I2S_FLL_TEST_CTL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_I2S_FLL_TEST_CTL_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_I2S_FLL_TEST_CTL_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_I2S_FLL_TEST_CTL_4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_I2S_FLL_TEST_CTL_5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_I2S_FLL_TEST_CTL_6)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_I2S_FLL_TEST_CTL_7)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_I2S_FLL_FREQ_CTL_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_I2S_FLL_FREQ_CTL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_I2S_FLL_FREQ_CTL_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_I2S_FLL_FREQ_CTL_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_I2S_FLL_SSC_CTL_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_I2S_FLL_SSC_CTL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_I2S_FLL_SSC_CTL_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_I2S_FLL_SSC_CTL_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_I2S_FLL_FLL_MODE)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_I2S_FLL_STATUS_0)] = WCD934X_READ, + [WCD934X_REG(WCD934X_I2S_FLL_STATUS_1)] = WCD934X_READ, + [WCD934X_REG(WCD934X_I2S_FLL_STATUS_2)] = WCD934X_READ, + [WCD934X_REG(WCD934X_I2S_FLL_STATUS_3)] = WCD934X_READ, + [WCD934X_REG(WCD934X_SB_FLL_USER_CTL_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SB_FLL_USER_CTL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SB_FLL_USER_CTL_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SB_FLL_USER_CTL_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SB_FLL_USER_CTL_4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SB_FLL_USER_CTL_5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SB_FLL_USER_CTL_6)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SB_FLL_USER_CTL_7)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SB_FLL_USER_CTL_8)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SB_FLL_USER_CTL_9)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SB_FLL_L_VAL_CTL_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SB_FLL_L_VAL_CTL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SB_FLL_DSM_FRAC_CTL_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SB_FLL_DSM_FRAC_CTL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SB_FLL_CONFIG_CTL_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SB_FLL_CONFIG_CTL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SB_FLL_CONFIG_CTL_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SB_FLL_CONFIG_CTL_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SB_FLL_CONFIG_CTL_4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SB_FLL_TEST_CTL_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SB_FLL_TEST_CTL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SB_FLL_TEST_CTL_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SB_FLL_TEST_CTL_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SB_FLL_TEST_CTL_4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SB_FLL_TEST_CTL_5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SB_FLL_TEST_CTL_6)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SB_FLL_TEST_CTL_7)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SB_FLL_FREQ_CTL_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SB_FLL_FREQ_CTL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SB_FLL_FREQ_CTL_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SB_FLL_FREQ_CTL_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SB_FLL_SSC_CTL_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SB_FLL_SSC_CTL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SB_FLL_SSC_CTL_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SB_FLL_SSC_CTL_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SB_FLL_FLL_MODE)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SB_FLL_STATUS_0)] = WCD934X_READ, + [WCD934X_REG(WCD934X_SB_FLL_STATUS_1)] = WCD934X_READ, + [WCD934X_REG(WCD934X_SB_FLL_STATUS_2)] = WCD934X_READ, + [WCD934X_REG(WCD934X_SB_FLL_STATUS_3)] = WCD934X_READ, +}; + +const u8 wcd934x_page2_reg_access[WCD934X_PAGE_SIZE] = { + [WCD934X_REG(WCD934X_PAGE2_PAGE_REGISTER)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_CPE_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_PWR_SYS_PSTATE_CTL_0)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_PWR_SYS_PSTATE_CTL_1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_PWR_CPEFLL_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_PWR_CPE_SYSMEM_DEEPSLP_0)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_PWR_CPE_SYSMEM_DEEPSLP_1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_PWR_CPE_SYSMEM_DEEPSLP_OVERRIDE)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_0)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_2)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_3)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_4)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_5)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_PWR_CPE_DRAM1_SHUTDOWN)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_SOC_SW_COLLAPSE_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_SOC_SW_COLLAPSE_OVERRIDE_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_SOC_SW_COLLAPSE_OVERRIDE_CTL1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_US_BUF_INT_PERIOD)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_CPARMAD_BUFRDY_INT_PERIOD)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_SVA_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_US_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_MAD_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_CPAR_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_DMIC0_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_DMIC1_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_DMIC2_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_DMIC_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_CPAR_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_WDOG_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_BACKUP_INT)] = WCD934X_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_STATUS)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_CPE_OCD_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_SS_ERROR_INT_MASK_0A)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_SS_ERROR_INT_MASK_0B)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_SS_ERROR_INT_MASK_1A)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_SS_ERROR_INT_MASK_1B)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_SS_ERROR_INT_STATUS_0A)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CPE_SS_SS_ERROR_INT_STATUS_0B)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CPE_SS_SS_ERROR_INT_STATUS_1A)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CPE_SS_SS_ERROR_INT_STATUS_1B)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CPE_SS_SS_ERROR_INT_CLEAR_0A)] = WCD934X_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_SS_ERROR_INT_CLEAR_0B)] = WCD934X_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_SS_ERROR_INT_CLEAR_1A)] = WCD934X_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_SS_ERROR_INT_CLEAR_1B)] = WCD934X_WRITE, + [WCD934X_REG(WCD934X_SOC_MAD_MAIN_CTL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SOC_MAD_MAIN_CTL_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SOC_MAD_AUDIO_CTL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SOC_MAD_AUDIO_CTL_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SOC_MAD_AUDIO_CTL_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SOC_MAD_AUDIO_CTL_4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SOC_MAD_AUDIO_CTL_5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SOC_MAD_AUDIO_CTL_6)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SOC_MAD_AUDIO_CTL_7)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SOC_MAD_AUDIO_CTL_8)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SOC_MAD_AUDIO_IIR_CTL_PTR)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SOC_MAD_AUDIO_IIR_CTL_VAL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SOC_MAD_ULTR_CTL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SOC_MAD_ULTR_CTL_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SOC_MAD_ULTR_CTL_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SOC_MAD_ULTR_CTL_4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SOC_MAD_ULTR_CTL_5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SOC_MAD_ULTR_CTL_6)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SOC_MAD_ULTR_CTL_7)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SOC_MAD_BEACON_CTL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SOC_MAD_BEACON_CTL_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SOC_MAD_BEACON_CTL_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SOC_MAD_BEACON_CTL_4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SOC_MAD_BEACON_CTL_5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SOC_MAD_BEACON_CTL_6)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SOC_MAD_BEACON_CTL_7)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SOC_MAD_BEACON_CTL_8)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SOC_MAD_BEACON_IIR_CTL_PTR)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SOC_MAD_BEACON_IIR_CTL_VAL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SOC_MAD_INP_SEL)] = WCD934X_READ_WRITE, +}; + +const u8 wcd934x_page4_reg_access[WCD934X_PAGE_SIZE] = { + [WCD934X_REG(WCD934X_PAGE4_PAGE_REGISTER)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_INTR_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_INTR_CLR_COMMIT)] = WCD934X_WRITE, + [WCD934X_REG(WCD934X_INTR_PIN1_MASK0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_INTR_PIN1_MASK1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_INTR_PIN1_MASK2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_INTR_PIN1_MASK3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_INTR_PIN1_STATUS0)] = WCD934X_READ, + [WCD934X_REG(WCD934X_INTR_PIN1_STATUS1)] = WCD934X_READ, + [WCD934X_REG(WCD934X_INTR_PIN1_STATUS2)] = WCD934X_READ, + [WCD934X_REG(WCD934X_INTR_PIN1_STATUS3)] = WCD934X_READ, + [WCD934X_REG(WCD934X_INTR_PIN1_CLEAR0)] = WCD934X_WRITE, + [WCD934X_REG(WCD934X_INTR_PIN1_CLEAR1)] = WCD934X_WRITE, + [WCD934X_REG(WCD934X_INTR_PIN1_CLEAR2)] = WCD934X_WRITE, + [WCD934X_REG(WCD934X_INTR_PIN1_CLEAR3)] = WCD934X_WRITE, + [WCD934X_REG(WCD934X_INTR_PIN2_MASK3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_INTR_PIN2_STATUS3)] = WCD934X_READ, + [WCD934X_REG(WCD934X_INTR_PIN2_CLEAR3)] = WCD934X_WRITE, + [WCD934X_REG(WCD934X_INTR_CPESS_SUMRY_MASK2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_INTR_CPESS_SUMRY_MASK3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_INTR_CPESS_SUMRY_STATUS2)] = WCD934X_READ, + [WCD934X_REG(WCD934X_INTR_CPESS_SUMRY_STATUS3)] = WCD934X_READ, + [WCD934X_REG(WCD934X_INTR_CPESS_SUMRY_CLEAR2)] = WCD934X_WRITE, + [WCD934X_REG(WCD934X_INTR_CPESS_SUMRY_CLEAR3)] = WCD934X_WRITE, + [WCD934X_REG(WCD934X_INTR_LEVEL0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_INTR_LEVEL1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_INTR_LEVEL2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_INTR_LEVEL3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_INTR_BYPASS0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_INTR_BYPASS1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_INTR_BYPASS2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_INTR_BYPASS3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_INTR_SET0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_INTR_SET1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_INTR_SET2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_INTR_SET3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_INTR_CODEC_MISC_MASK)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_INTR_CODEC_MISC_STATUS)] = WCD934X_READ, + [WCD934X_REG(WCD934X_INTR_CODEC_MISC_CLEAR)] = WCD934X_WRITE, +}; + +const u8 wcd934x_page5_reg_access[WCD934X_PAGE_SIZE] = { + [WCD934X_REG(WCD934X_PAGE5_PAGE_REGISTER)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_DEVICE)] = WCD934X_READ, + [WCD934X_REG(WCD934X_SLNQ_DIG_REVISION)] = WCD934X_READ, + [WCD934X_REG(WCD934X_SLNQ_DIG_H_COMMAND)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_NUMBER_OF_BYTE_MSB)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_NUMBER_OF_BYTE_LSB)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_MASTER_ADDRESS_MSB)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_MASTER_ADDRESS_LSB)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SLAVE_ADDRESS_MSB)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SLAVE_ADDRESS_LSB)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_TIMER0_INTERRUPT_MSB)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_TIMER0_INTERRUPT_LSB)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_TIMER1_INTERRUPT_MSB)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_TIMER1_INTERRUPT_LSB)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_TIMER2_INTERRUPT_MSB)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_TIMER2_INTERRUPT_LSB)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_COMM_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_FRAME_CTRL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_PDM_2ND_DATA_CH1_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_PDM_2ND_DATA_CH3_4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_PDM_2ND_DATA_CH5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SW_EVENT_RD)] = WCD934X_READ, + [WCD934X_REG(WCD934X_SLNQ_DIG_SW_EVENT_CTRL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_PDM_SELECT_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_PDM_SELECT_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_PDM_SELECT_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_PDM_SAMPLING_FREQ)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_PDM_DC_CONVERSION_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_PDM_DC_CONVERSION_SEL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_PDM_DC_CONV_CHA_MSB)] = WCD934X_READ, + [WCD934X_REG(WCD934X_SLNQ_DIG_PDM_DC_CONV_CHA_LSB)] = WCD934X_READ, + [WCD934X_REG(WCD934X_SLNQ_DIG_PDM_DC_CONV_CHB_MSB)] = WCD934X_READ, + [WCD934X_REG(WCD934X_SLNQ_DIG_PDM_DC_CONV_CHB_LSB)] = WCD934X_READ, + [WCD934X_REG(WCD934X_SLNQ_DIG_RAM_CNTRL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BANK)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_6)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_7)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_8)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_9)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_A)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_B)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_C)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_D)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_E)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_F)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_10)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_11)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_12)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_13)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_14)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_15)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_16)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_17)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_18)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_19)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_1A)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_1B)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_1C)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_1D)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_1E)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_1F)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_20)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_21)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_22)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_23)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_24)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_25)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_26)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_27)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_28)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_29)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_2A)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_2B)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_2C)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_2D)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_2E)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_2F)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_30)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_31)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_32)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_33)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_34)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_35)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_36)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_37)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_38)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_39)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_3A)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_3B)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_3C)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_3D)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_3E)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_3F)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_TOP_CTRL1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_TOP_CTRL2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_PDM_CTRL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_PDM_MUTE_CTRL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_DEC_BYPASS_CTRL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_DEC_BYPASS_STATUS)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_DEC_BYPASS_FS)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_DEC_BYPASS_IN_SEL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_GPOUT_ENABLE)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_GPOUT_VAL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_ANA_INTERRUPT_MASK)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_ANA_INTERRUPT_STATUS)] = WCD934X_READ, + [WCD934X_REG(WCD934X_SLNQ_DIG_ANA_INTERRUPT_CLR)] = WCD934X_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_IP_TESTING)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_INTERRUPT_CNTRL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_INTERRUPT_CNT)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_INTERRUPT_CNT_MSB)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_INTERRUPT_CNT_LSB)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_INTERRUPT_MASK0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_INTERRUPT_MASK1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_INTERRUPT_MASK2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_INTERRUPT_MASK3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_INTERRUPT_MASK4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_INTERRUPT_STATUS0)] = WCD934X_READ, + [WCD934X_REG(WCD934X_SLNQ_DIG_INTERRUPT_STATUS1)] = WCD934X_READ, + [WCD934X_REG(WCD934X_SLNQ_DIG_INTERRUPT_STATUS2)] = WCD934X_READ, + [WCD934X_REG(WCD934X_SLNQ_DIG_INTERRUPT_STATUS3)] = WCD934X_READ, + [WCD934X_REG(WCD934X_SLNQ_DIG_INTERRUPT_STATUS4)] = WCD934X_READ, + [WCD934X_REG(WCD934X_SLNQ_DIG_INTERRUPT_CLR0)] = WCD934X_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_INTERRUPT_CLR1)] = WCD934X_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_INTERRUPT_CLR2)] = WCD934X_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_INTERRUPT_CLR3)] = WCD934X_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_INTERRUPT_CLR4)] = WCD934X_WRITE, +}; + +const u8 wcd934x_page6_reg_access[WCD934X_PAGE_SIZE] = { + [WCD934X_REG(WCD934X_ANA_PAGE_REGISTER)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_ANA_BIAS)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_ANA_RCO)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_ANA_PAGE6_SPARE2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_ANA_PAGE6_SPARE3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_ANA_BUCK_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_ANA_BUCK_STATUS)] = WCD934X_READ, + [WCD934X_REG(WCD934X_ANA_RX_SUPPLIES)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_ANA_HPH)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_ANA_EAR)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_ANA_LO_1_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_ANA_MAD_SETUP)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_ANA_AMIC1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_ANA_AMIC2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_ANA_AMIC3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_ANA_AMIC4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_ANA_MBHC_MECH)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_ANA_MBHC_ELECT)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_ANA_MBHC_ZDET)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_ANA_MBHC_RESULT_1)] = WCD934X_READ, + [WCD934X_REG(WCD934X_ANA_MBHC_RESULT_2)] = WCD934X_READ, + [WCD934X_REG(WCD934X_ANA_MBHC_RESULT_3)] = WCD934X_READ, + [WCD934X_REG(WCD934X_ANA_MBHC_BTN0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_ANA_MBHC_BTN1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_ANA_MBHC_BTN2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_ANA_MBHC_BTN3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_ANA_MBHC_BTN4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_ANA_MBHC_BTN5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_ANA_MBHC_BTN6)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_ANA_MBHC_BTN7)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_ANA_MICB1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_ANA_MICB2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_ANA_MICB2_RAMP)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_ANA_MICB3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_ANA_MICB4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_ANA_VBADC)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_BIAS_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_BIAS_VBG_FINE_ADJ)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_RCO_CTRL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_RCO_CTRL_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_RCO_CAL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_RCO_CAL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_RCO_CAL_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_RCO_TEST_CTRL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_RCO_CAL_OUT_1)] = WCD934X_READ, + [WCD934X_REG(WCD934X_RCO_CAL_OUT_2)] = WCD934X_READ, + [WCD934X_REG(WCD934X_RCO_CAL_OUT_3)] = WCD934X_READ, + [WCD934X_REG(WCD934X_RCO_CAL_OUT_4)] = WCD934X_READ, + [WCD934X_REG(WCD934X_RCO_CAL_OUT_5)] = WCD934X_READ, + [WCD934X_REG(WCD934X_SIDO_MODE_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_MODE_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_MODE_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_MODE_4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_VCL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_VCL_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_VCL_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_CCL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_CCL_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_CCL_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_CCL_4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_CCL_5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_CCL_6)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_CCL_7)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_CCL_8)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_CCL_9)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_CCL_10)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_FILTER_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_FILTER_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_DRIVER_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_DRIVER_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_DRIVER_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_CAL_CODE_EXT_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_CAL_CODE_EXT_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_CAL_CODE_OUT_1)] = WCD934X_READ, + [WCD934X_REG(WCD934X_SIDO_CAL_CODE_OUT_2)] = WCD934X_READ, + [WCD934X_REG(WCD934X_SIDO_TEST_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_TEST_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MBHC_CTL_CLK)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MBHC_CTL_ANA)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MBHC_CTL_SPARE_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MBHC_CTL_SPARE_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MBHC_CTL_BCS)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MBHC_STATUS_SPARE_1)] = WCD934X_READ, + [WCD934X_REG(WCD934X_MBHC_TEST_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_VBADC_SUBBLOCK_EN)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_VBADC_IBIAS_FE)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_VBADC_BIAS_ADC)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_VBADC_FE_CTRL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_VBADC_ADC_REF)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_VBADC_ADC_IO)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_VBADC_ADC_SAR)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_VBADC_DEBUG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_LDOH_MODE)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_LDOH_BIAS)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_LDOH_STB_LOADS)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_LDOH_SLOWRAMP)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MICB1_TEST_CTL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MICB1_TEST_CTL_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MICB1_TEST_CTL_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MICB2_TEST_CTL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MICB2_TEST_CTL_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MICB2_TEST_CTL_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MICB3_TEST_CTL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MICB3_TEST_CTL_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MICB3_TEST_CTL_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MICB4_TEST_CTL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MICB4_TEST_CTL_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MICB4_TEST_CTL_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TX_COM_ADC_VCM)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TX_COM_BIAS_ATEST)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TX_COM_ADC_INT1_IB)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TX_COM_ADC_INT2_IB)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TX_COM_TXFE_DIV_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TX_COM_TXFE_DIV_START)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TX_COM_TXFE_DIV_STOP_9P6M)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TX_COM_TXFE_DIV_STOP_12P288M)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TX_1_2_TEST_EN)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TX_1_2_ADC_IB)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TX_1_2_ATEST_REFCTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TX_1_2_TEST_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TX_1_2_TEST_BLK_EN)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TX_1_2_TXFE_CLKDIV)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TX_1_2_SAR1_ERR)] = WCD934X_READ, + [WCD934X_REG(WCD934X_TX_1_2_SAR2_ERR)] = WCD934X_READ, + [WCD934X_REG(WCD934X_TX_3_4_TEST_EN)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TX_3_4_ADC_IB)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TX_3_4_ATEST_REFCTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TX_3_4_TEST_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TX_3_4_TEST_BLK_EN)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TX_3_4_TXFE_CLKDIV)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TX_3_4_SAR1_ERR)] = WCD934X_READ, + [WCD934X_REG(WCD934X_TX_3_4_SAR2_ERR)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CLASSH_MODE_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLASSH_MODE_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLASSH_MODE_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLASSH_CTRL_VCL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLASSH_CTRL_VCL_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLASSH_CTRL_CCL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLASSH_CTRL_CCL_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLASSH_CTRL_CCL_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLASSH_CTRL_CCL_4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLASSH_CTRL_CCL_5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLASSH_BUCK_TMUX_A_D)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLASSH_BUCK_SW_DRV_CNTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLASSH_SPARE)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_FLYBACK_EN)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_FLYBACK_VNEG_CTRL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_FLYBACK_VNEG_CTRL_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_FLYBACK_VNEG_CTRL_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_FLYBACK_VNEG_CTRL_4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_FLYBACK_VNEG_CTRL_5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_FLYBACK_VNEG_CTRL_6)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_FLYBACK_VNEG_CTRL_7)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_FLYBACK_VNEG_CTRL_8)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_FLYBACK_VNEG_CTRL_9)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_FLYBACK_VNEGDAC_CTRL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_FLYBACK_VNEGDAC_CTRL_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_FLYBACK_VNEGDAC_CTRL_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_FLYBACK_CTRL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_FLYBACK_TEST_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_RX_AUX_SW_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_RX_PA_AUX_IN_CONN)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_RX_TIMER_DIV)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_RX_OCP_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_RX_OCP_COUNT)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_RX_BIAS_EAR_DAC)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_RX_BIAS_EAR_AMP)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_RX_BIAS_HPH_LDO)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_RX_BIAS_HPH_PA)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_RX_BIAS_HPH_RDACBUFF_CNP2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_RX_BIAS_HPH_RDAC_LDO)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_RX_BIAS_HPH_CNP1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_RX_BIAS_HPH_LOWPOWER)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_RX_BIAS_DIFFLO_PA)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_RX_BIAS_DIFFLO_REF)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_RX_BIAS_DIFFLO_LDO)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_RX_BIAS_SELO_DAC_PA)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_RX_BIAS_BUCK_RST)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_RX_BIAS_BUCK_VREF_ERRAMP)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_RX_BIAS_FLYB_ERRAMP)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_RX_BIAS_FLYB_BUFF)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_RX_BIAS_FLYB_MID_RST)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_HPH_L_STATUS)] = WCD934X_READ, + [WCD934X_REG(WCD934X_HPH_R_STATUS)] = WCD934X_READ, + [WCD934X_REG(WCD934X_HPH_CNP_EN)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_HPH_CNP_WG_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_HPH_CNP_WG_TIME)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_HPH_OCP_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_HPH_AUTO_CHOP)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_HPH_CHOP_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_HPH_PA_CTL1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_HPH_PA_CTL2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_HPH_L_EN)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_HPH_L_TEST)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_HPH_L_ATEST)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_HPH_R_EN)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_HPH_R_TEST)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_HPH_R_ATEST)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_HPH_RDAC_CLK_CTL1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_HPH_RDAC_CLK_CTL2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_HPH_RDAC_LDO_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_HPH_RDAC_CHOP_CLK_LP_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_HPH_REFBUFF_UHQA_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_HPH_REFBUFF_LP_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_HPH_L_DAC_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_HPH_R_DAC_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_EAR_EN_REG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_EAR_CMBUFF)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_EAR_ICTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_EAR_EN_DBG_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_EAR_CNP)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_EAR_DAC_CTL_ATEST)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_EAR_STATUS_REG)] = WCD934X_READ, + [WCD934X_REG(WCD934X_EAR_EAR_MISC)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DIFF_LO_MISC)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DIFF_LO_LO2_COMPANDER)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DIFF_LO_LO1_COMPANDER)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DIFF_LO_COMMON)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DIFF_LO_BYPASS_EN)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DIFF_LO_CNP)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DIFF_LO_CORE_OUT_PROG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DIFF_LO_LDO_OUT_PROG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DIFF_LO_COM_SWCAP_REFBUF_FREQ)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DIFF_LO_COM_PA_FREQ)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DIFF_LO_RESERVED_REG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DIFF_LO_LO1_STATUS_1)] = WCD934X_READ, + [WCD934X_REG(WCD934X_DIFF_LO_LO1_STATUS_2)] = WCD934X_READ, +}; + +const u8 wcd934x_page7_reg_access[WCD934X_PAGE_SIZE] = { + [WCD934X_REG(WCD934X_ANA_NEW_PAGE_REGISTER)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_HPH_NEW_ANA_HPH2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_HPH_NEW_ANA_HPH3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_ANA_EN)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_ANA_STATUS)] = WCD934X_READ, + [WCD934X_REG(WCD934X_SLNQ_ANA_LDO_CONFIG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_ANA_LDO_OCP_CONFIG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_ANA_TX_LDO_CONFIG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_ANA_TX_DRV_CONFIG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_ANA_RX_CONFIG_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_ANA_RX_CONFIG_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_ANA_PLL_ENABLES)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_ANA_PLL_PRESET)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_ANA_PLL_STATUS)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CLK_SYS_PLL_ENABLES)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLK_SYS_PLL_PRESET)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLK_SYS_PLL_STATUS)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CLK_SYS_MCLK_PRG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLK_SYS_MCLK2_PRG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLK_SYS_MCLK2_PRG2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLK_SYS_XO_PRG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLK_SYS_XO_CAP_XTP)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLK_SYS_XO_CAP_XTM)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_BOOST_BST_EN_DLY)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_BOOST_CTRL_ILIM)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_BOOST_VOUT_SETTING)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_NEW_VOUT_A_STARTUP)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_NEW_VOUT_D_STARTUP)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_NEW_VOUT_D_FREQ1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_NEW_VOUT_D_FREQ2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MBHC_NEW_ELECT_REM_CLAMP_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MBHC_NEW_CTL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MBHC_NEW_CTL_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MBHC_NEW_PLUG_DETECT_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MBHC_NEW_ZDET_ANA_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MBHC_NEW_ZDET_RAMP_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MBHC_NEW_FSM_STATUS)] = WCD934X_READ, + [WCD934X_REG(WCD934X_MBHC_NEW_ADC_RESULT)] = WCD934X_READ, + [WCD934X_REG(WCD934X_TX_NEW_AMIC_4_5_SEL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_VBADC_NEW_ADC_MODE)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_VBADC_NEW_ADC_DOUTMSB)] = WCD934X_READ, + [WCD934X_REG(WCD934X_VBADC_NEW_ADC_DOUTLSB)] = WCD934X_READ, + [WCD934X_REG(WCD934X_HPH_NEW_INT_RDAC_GAIN_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_HPH_NEW_INT_RDAC_HD2_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_HPH_NEW_INT_RDAC_VREF_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_HPH_NEW_INT_RDAC_OVERRIDE_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_HPH_NEW_INT_RDAC_MISC1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_HPH_NEW_INT_PA_MISC1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_HPH_NEW_INT_PA_MISC2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_HPH_NEW_INT_PA_RDAC_MISC)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_HPH_NEW_INT_HPH_TIMER1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_HPH_NEW_INT_HPH_TIMER2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_HPH_NEW_INT_HPH_TIMER3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_HPH_NEW_INT_HPH_TIMER4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_HPH_NEW_INT_PA_RDAC_MISC2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_HPH_NEW_INT_PA_RDAC_MISC3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_RX_NEW_INT_HPH_RDAC_BIAS_LOHIFI)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_RX_NEW_INT_HPH_RDAC_BIAS_ULP)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_RX_NEW_INT_HPH_RDAC_LDO_LP)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_INT_ANA_INT_LDO_TEST)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_INT_ANA_INT_LDO_DEBUG_1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_INT_ANA_INT_LDO_DEBUG_2)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_INT_ANA_INT_TX_LDO_TEST)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_INT_ANA_INT_TX_DRV_TEST)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_INT_ANA_INT_RX_TEST)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_INT_ANA_INT_RX_TEST_STATUS)] = WCD934X_READ, + [WCD934X_REG(WCD934X_SLNQ_INT_ANA_INT_RX_DEBUG_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_INT_ANA_INT_RX_DEBUG_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_INT_ANA_INT_CLK_CTRL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_INT_ANA_INT_RESERVED_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_INT_ANA_INT_RESERVED_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_INT_ANA_INT_PLL_POST_DIV_REG0)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_INT_ANA_INT_PLL_POST_DIV_REG1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_INT_ANA_INT_PLL_REF_DIV_REG0)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_INT_ANA_INT_PLL_REF_DIV_REG1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_INT_ANA_INT_PLL_FILTER_REG0)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_INT_ANA_INT_PLL_FILTER_REG1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_INT_ANA_INT_PLL_L_VAL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_INT_ANA_INT_PLL_M_VAL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_INT_ANA_INT_PLL_N_VAL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_INT_ANA_INT_PLL_TEST_REG0)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_INT_ANA_INT_PLL_PFD_CP_DSM_PROG)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_INT_ANA_INT_PLL_VCO_PROG)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_INT_ANA_INT_PLL_TEST_REG1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_INT_ANA_INT_PLL_LDO_LOCK_CFG)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_INT_ANA_INT_PLL_DIG_LOCK_DET_CFG)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLK_SYS_INT_POST_DIV_REG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLK_SYS_INT_POST_DIV_REG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLK_SYS_INT_REF_DIV_REG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLK_SYS_INT_REF_DIV_REG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLK_SYS_INT_FILTER_REG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLK_SYS_INT_FILTER_REG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLK_SYS_INT_PLL_L_VAL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLK_SYS_INT_PLL_M_VAL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLK_SYS_INT_PLL_N_VAL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLK_SYS_INT_TEST_REG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLK_SYS_INT_PFD_CP_DSM_PROG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLK_SYS_INT_VCO_PROG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLK_SYS_INT_TEST_REG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLK_SYS_INT_LDO_LOCK_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLK_SYS_INT_DIG_LOCK_DET_CFG)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLK_SYS_INT_CLK_TEST1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLK_SYS_INT_CLK_TEST2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLK_SYS_INT_CLK_TEST3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLK_SYS_INT_XO_TEST1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLK_SYS_INT_XO_TEST2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_BOOST_INT_VCOMP_HYST)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_BOOST_INT_VLOOP_FILTER)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_BOOST_INT_CTRL_IDELTA)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_BOOST_INT_CTRL_ILIM_STARTUP)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_BOOST_INT_CTRL_MIN_ONTIME)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_BOOST_INT_CTRL_MAX_ONTIME)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_BOOST_INT_CTRL_TIMING)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_BOOST_INT_TMUX_A_D)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_BOOST_INT_SW_DRV_CNTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_BOOST_INT_SPARE1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_BOOST_INT_SPARE2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_NEW_INT_RAMP_STATUS)] = WCD934X_READ, + [WCD934X_REG(WCD934X_SIDO_NEW_INT_SPARE_1)] = WCD934X_READ, + [WCD934X_REG(WCD934X_SIDO_NEW_INT_DEBUG_VOUT_SETTING_A)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_NEW_INT_DEBUG_VOUT_SETTING_D)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_NEW_INT_RAMP_INC_WAIT)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_NEW_INT_DYNAMIC_IPEAK_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_NEW_INT_RAMP_IBLEED_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_NEW_INT_DEBUG_CPROVR_TEST)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_NEW_INT_RAMP_CTL_A)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_NEW_INT_RAMP_CTL_D)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_NEW_INT_RAMP_TIMEOUT_PERIOD)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_NEW_INT_DYNAMIC_IPEAK_SETTING1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_NEW_INT_DYNAMIC_IPEAK_SETTING2)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_NEW_INT_DYNAMIC_IPEAK_SETTING3)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_NEW_INT_HIGH_ACCU_MODE_SEL1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_NEW_INT_HIGH_ACCU_MODE_SEL2)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MBHC_NEW_INT_SLNQ_HPF)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MBHC_NEW_INT_SLNQ_REF)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MBHC_NEW_INT_SLNQ_COMP)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MBHC_NEW_INT_SPARE_2)] = WCD934X_READ_WRITE, + +}; + +const u8 wcd934x_page10_reg_access[WCD934X_PAGE_SIZE] = { + [WCD934X_REG(WCD934X_PAGE10_PAGE_REGISTER)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_ANC0_CLK_RESET_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_ANC0_MODE_1_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_ANC0_MODE_2_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_ANC0_FF_SHIFT)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_ANC0_FB_SHIFT)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_ANC0_LPF_FF_A_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_ANC0_LPF_FF_B_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_ANC0_LPF_FB_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_ANC0_SMLPF_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_ANC0_DCFLT_SHIFT_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_ANC0_IIR_ADAPT_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_ANC0_IIR_COEFF_1_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_ANC0_IIR_COEFF_2_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_ANC0_FF_A_GAIN_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_ANC0_FF_B_GAIN_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_ANC0_FB_GAIN_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_ANC0_RC_COMMON_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_ANC0_FIFO_COMMON_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_ANC0_RC0_STATUS_FMIN_CNTR)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_ANC0_RC1_STATUS_FMIN_CNTR)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_ANC0_RC0_STATUS_FMAX_CNTR)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_ANC0_RC1_STATUS_FMAX_CNTR)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_ANC0_STATUS_FIFO)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_ANC1_CLK_RESET_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_ANC1_MODE_1_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_ANC1_MODE_2_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_ANC1_FF_SHIFT)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_ANC1_FB_SHIFT)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_ANC1_LPF_FF_A_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_ANC1_LPF_FF_B_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_ANC1_LPF_FB_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_ANC1_SMLPF_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_ANC1_DCFLT_SHIFT_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_ANC1_IIR_ADAPT_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_ANC1_IIR_COEFF_1_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_ANC1_IIR_COEFF_2_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_ANC1_FF_A_GAIN_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_ANC1_FF_B_GAIN_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_ANC1_FB_GAIN_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_ANC1_RC_COMMON_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_ANC1_FIFO_COMMON_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_ANC1_RC0_STATUS_FMIN_CNTR)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_ANC1_RC1_STATUS_FMIN_CNTR)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_ANC1_RC0_STATUS_FMAX_CNTR)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_ANC1_RC1_STATUS_FMAX_CNTR)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_ANC1_STATUS_FIFO)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_TX0_TX_PATH_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX0_TX_PATH_CFG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX0_TX_PATH_CFG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX0_TX_VOL_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX0_TX_PATH_192_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX0_TX_PATH_192_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX0_TX_PATH_SEC0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX0_TX_PATH_SEC1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX0_TX_PATH_SEC2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX0_TX_PATH_SEC3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX0_TX_PATH_SEC4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX0_TX_PATH_SEC5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX0_TX_PATH_SEC6)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX0_TX_PATH_SEC7)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX1_TX_PATH_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX1_TX_PATH_CFG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX1_TX_PATH_CFG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX1_TX_VOL_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX1_TX_PATH_192_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX1_TX_PATH_192_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX1_TX_PATH_SEC0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX1_TX_PATH_SEC1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX1_TX_PATH_SEC2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX1_TX_PATH_SEC3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX1_TX_PATH_SEC4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX1_TX_PATH_SEC5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX1_TX_PATH_SEC6)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX2_TX_PATH_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX2_TX_PATH_CFG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX2_TX_PATH_CFG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX2_TX_VOL_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX2_TX_PATH_192_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX2_TX_PATH_192_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX2_TX_PATH_SEC0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX2_TX_PATH_SEC1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX2_TX_PATH_SEC2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX2_TX_PATH_SEC3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX2_TX_PATH_SEC4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX2_TX_PATH_SEC5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX2_TX_PATH_SEC6)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX3_TX_PATH_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX3_TX_PATH_CFG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX3_TX_PATH_CFG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX3_TX_VOL_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX3_TX_PATH_192_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX3_TX_PATH_192_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX3_TX_PATH_SEC0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX3_TX_PATH_SEC1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX3_TX_PATH_SEC2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX3_TX_PATH_SEC3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX3_TX_PATH_SEC4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX3_TX_PATH_SEC5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX3_TX_PATH_SEC6)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX4_TX_PATH_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX4_TX_PATH_CFG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX4_TX_PATH_CFG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX4_TX_VOL_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX4_TX_PATH_192_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX4_TX_PATH_192_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX4_TX_PATH_SEC0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX4_TX_PATH_SEC1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX4_TX_PATH_SEC2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX4_TX_PATH_SEC3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX4_TX_PATH_SEC4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX4_TX_PATH_SEC5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX4_TX_PATH_SEC6)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX5_TX_PATH_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX5_TX_PATH_CFG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX5_TX_PATH_CFG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX5_TX_VOL_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX5_TX_PATH_192_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX5_TX_PATH_192_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX5_TX_PATH_SEC0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX5_TX_PATH_SEC1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX5_TX_PATH_SEC2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX5_TX_PATH_SEC3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX5_TX_PATH_SEC4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX5_TX_PATH_SEC5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX5_TX_PATH_SEC6)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX6_TX_PATH_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX6_TX_PATH_CFG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX6_TX_PATH_CFG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX6_TX_VOL_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX6_TX_PATH_192_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX6_TX_PATH_192_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX6_TX_PATH_SEC0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX6_TX_PATH_SEC1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX6_TX_PATH_SEC2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX6_TX_PATH_SEC3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX6_TX_PATH_SEC4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX6_TX_PATH_SEC5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX6_TX_PATH_SEC6)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX7_TX_PATH_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX7_TX_PATH_CFG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX7_TX_PATH_CFG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX7_TX_VOL_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX7_TX_PATH_192_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX7_TX_PATH_192_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX7_TX_PATH_SEC0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX7_TX_PATH_SEC1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX7_TX_PATH_SEC2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX7_TX_PATH_SEC3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX7_TX_PATH_SEC4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX7_TX_PATH_SEC5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX7_TX_PATH_SEC6)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX8_TX_PATH_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX8_TX_PATH_CFG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX8_TX_PATH_CFG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX8_TX_VOL_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX8_TX_PATH_192_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX8_TX_PATH_192_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX8_TX_PATH_SEC0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX8_TX_PATH_SEC1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX8_TX_PATH_SEC2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX8_TX_PATH_SEC3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX8_TX_PATH_SEC4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX8_TX_PATH_SEC5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX8_TX_PATH_SEC6)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX9_SPKR_PROT_PATH_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX9_SPKR_PROT_PATH_CFG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX10_SPKR_PROT_PATH_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX10_SPKR_PROT_PATH_CFG0)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX11_SPKR_PROT_PATH_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX11_SPKR_PROT_PATH_CFG0)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX12_SPKR_PROT_PATH_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX12_SPKR_PROT_PATH_CFG0)] = + WCD934X_READ_WRITE, +}; + +const u8 wcd934x_page11_reg_access[WCD934X_PAGE_SIZE] = { + [WCD934X_REG(WCD934X_PAGE11_PAGE_REGISTER)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER1_CTL0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER1_CTL1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER1_CTL2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER1_CTL3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER1_CTL4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER1_CTL5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER1_CTL6)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_COMPANDER1_CTL7)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER2_CTL0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER2_CTL1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER2_CTL2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER2_CTL3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER2_CTL4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER2_CTL5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER2_CTL6)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_COMPANDER2_CTL7)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER3_CTL0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER3_CTL1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER3_CTL2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER3_CTL3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER3_CTL4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER3_CTL5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER3_CTL6)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_COMPANDER3_CTL7)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER4_CTL0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER4_CTL1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER4_CTL2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER4_CTL3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER4_CTL4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER4_CTL5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER4_CTL6)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_COMPANDER4_CTL7)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER7_CTL0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER7_CTL1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER7_CTL2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER7_CTL3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER7_CTL4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER7_CTL5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER7_CTL6)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_COMPANDER7_CTL7)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER8_CTL0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER8_CTL1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER8_CTL2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER8_CTL3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER8_CTL4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER8_CTL5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER8_CTL6)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_COMPANDER8_CTL7)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX0_RX_PATH_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX0_RX_PATH_CFG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX0_RX_PATH_CFG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX0_RX_PATH_CFG2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX0_RX_VOL_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX0_RX_PATH_MIX_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX0_RX_PATH_MIX_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX0_RX_VOL_MIX_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX0_RX_PATH_SEC0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX0_RX_PATH_SEC1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX0_RX_PATH_SEC2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX0_RX_PATH_SEC3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX0_RX_PATH_SEC5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX0_RX_PATH_SEC6)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX0_RX_PATH_SEC7)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX0_RX_PATH_MIX_SEC0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX0_RX_PATH_MIX_SEC1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX0_RX_PATH_DSMDEM_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX1_RX_PATH_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX1_RX_PATH_CFG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX1_RX_PATH_CFG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX1_RX_PATH_CFG2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX1_RX_VOL_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX1_RX_PATH_MIX_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX1_RX_PATH_MIX_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX1_RX_VOL_MIX_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX1_RX_PATH_SEC0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX1_RX_PATH_SEC1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX1_RX_PATH_SEC2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX1_RX_PATH_SEC3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX1_RX_PATH_SEC4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX1_RX_PATH_SEC5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX1_RX_PATH_SEC6)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX1_RX_PATH_SEC7)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX1_RX_PATH_MIX_SEC0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX1_RX_PATH_MIX_SEC1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX1_RX_PATH_DSMDEM_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX2_RX_PATH_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX2_RX_PATH_CFG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX2_RX_PATH_CFG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX2_RX_PATH_CFG2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX2_RX_VOL_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX2_RX_PATH_MIX_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX2_RX_PATH_MIX_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX2_RX_VOL_MIX_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX2_RX_PATH_SEC0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX2_RX_PATH_SEC1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX2_RX_PATH_SEC2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX2_RX_PATH_SEC3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX2_RX_PATH_SEC4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX2_RX_PATH_SEC5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX2_RX_PATH_SEC6)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX2_RX_PATH_SEC7)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX2_RX_PATH_MIX_SEC0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX2_RX_PATH_MIX_SEC1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX2_RX_PATH_DSMDEM_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX3_RX_PATH_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX3_RX_PATH_CFG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX3_RX_PATH_CFG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX3_RX_PATH_CFG2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX3_RX_VOL_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX3_RX_PATH_MIX_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX3_RX_PATH_MIX_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX3_RX_VOL_MIX_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX3_RX_PATH_SEC0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX3_RX_PATH_SEC1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX3_RX_PATH_SEC2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX3_RX_PATH_SEC3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX3_RX_PATH_SEC5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX3_RX_PATH_SEC6)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX3_RX_PATH_SEC7)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX3_RX_PATH_MIX_SEC0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX3_RX_PATH_MIX_SEC1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX3_RX_PATH_DSMDEM_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX4_RX_PATH_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX4_RX_PATH_CFG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX4_RX_PATH_CFG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX4_RX_PATH_CFG2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX4_RX_VOL_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX4_RX_PATH_MIX_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX4_RX_PATH_MIX_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX4_RX_VOL_MIX_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX4_RX_PATH_SEC0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX4_RX_PATH_SEC1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX4_RX_PATH_SEC2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX4_RX_PATH_SEC3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX4_RX_PATH_SEC5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX4_RX_PATH_SEC6)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX4_RX_PATH_SEC7)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX4_RX_PATH_MIX_SEC0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX4_RX_PATH_MIX_SEC1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX4_RX_PATH_DSMDEM_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX7_RX_PATH_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX7_RX_PATH_CFG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX7_RX_PATH_CFG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX7_RX_PATH_CFG2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX7_RX_VOL_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX7_RX_PATH_MIX_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX7_RX_PATH_MIX_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX7_RX_VOL_MIX_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX7_RX_PATH_SEC0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX7_RX_PATH_SEC1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX7_RX_PATH_SEC2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX7_RX_PATH_SEC3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX7_RX_PATH_SEC5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX7_RX_PATH_SEC6)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX7_RX_PATH_SEC7)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX7_RX_PATH_MIX_SEC0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX7_RX_PATH_MIX_SEC1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX7_RX_PATH_DSMDEM_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX8_RX_PATH_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX8_RX_PATH_CFG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX8_RX_PATH_CFG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX8_RX_PATH_CFG2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX8_RX_VOL_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX8_RX_PATH_MIX_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX8_RX_PATH_MIX_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX8_RX_VOL_MIX_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX8_RX_PATH_SEC0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX8_RX_PATH_SEC1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX8_RX_PATH_SEC2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX8_RX_PATH_SEC3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX8_RX_PATH_SEC5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX8_RX_PATH_SEC6)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX8_RX_PATH_SEC7)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX8_RX_PATH_MIX_SEC0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX8_RX_PATH_MIX_SEC1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX8_RX_PATH_DSMDEM_CTL)] = WCD934X_READ_WRITE, +}; + +const u8 wcd934x_page12_reg_access[WCD934X_PAGE_SIZE] = { + [WCD934X_REG(WCD934X_PAGE12_PAGE_REGISTER)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_CLSH_CRC)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_CLSH_DLY_CTRL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_CLSH_DECAY_CTRL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_CLSH_HPH_V_PA)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_CLSH_EAR_V_PA)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_CLSH_HPH_V_HD)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_CLSH_EAR_V_HD)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_CLSH_K1_MSB)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_CLSH_K1_LSB)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_CLSH_K2_MSB)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_CLSH_K2_LSB)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_CLSH_IDLE_CTRL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_CLSH_IDLE_HPH)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_CLSH_IDLE_EAR)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_CLSH_TEST0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_CLSH_TEST1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_CLSH_OVR_VREF)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_BOOST0_BOOST_PATH_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_BOOST0_BOOST_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_BOOST0_BOOST_CFG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_BOOST0_BOOST_CFG2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_BOOST1_BOOST_PATH_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_BOOST1_BOOST_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_BOOST1_BOOST_CFG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_BOOST1_BOOST_CFG2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_VBAT_VBAT_PATH_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_VBAT_VBAT_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_VBAT_VBAT_ADC_CAL1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_VBAT_VBAT_ADC_CAL2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_VBAT_VBAT_ADC_CAL3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_VBAT_VBAT_PK_EST1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_VBAT_VBAT_PK_EST2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_VBAT_VBAT_PK_EST3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_VBAT_VBAT_RF_PROC1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_VBAT_VBAT_RF_PROC2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_VBAT_VBAT_TAC1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_VBAT_VBAT_TAC2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_VBAT_VBAT_TAC3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_VBAT_VBAT_TAC4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_VBAT_VBAT_GAIN_UPD1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_VBAT_VBAT_GAIN_UPD2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_VBAT_VBAT_GAIN_UPD3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_VBAT_VBAT_GAIN_UPD4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_VBAT_VBAT_DEBUG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_VBAT_VBAT_GAIN_UPD_MON)] = WCD934X_WRITE, + [WCD934X_REG(WCD934X_CDC_VBAT_VBAT_GAIN_MON_VAL)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_VBAT_VBAT_BAN)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MIXING_ASRC0_CLK_RST_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MIXING_ASRC0_CTL0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MIXING_ASRC0_CTL1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MIXING_ASRC0_FIFO_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MIXING_ASRC0_STATUS_FMIN_CNTR_LSB)] = WCD934X_READ, + [WCD934X_REG(WCD934X_MIXING_ASRC0_STATUS_FMIN_CNTR_MSB)] = WCD934X_READ, + [WCD934X_REG(WCD934X_MIXING_ASRC0_STATUS_FMAX_CNTR_LSB)] = WCD934X_READ, + [WCD934X_REG(WCD934X_MIXING_ASRC0_STATUS_FMAX_CNTR_MSB)] = WCD934X_READ, + [WCD934X_REG(WCD934X_MIXING_ASRC0_STATUS_FIFO)] = WCD934X_READ, + [WCD934X_REG(WCD934X_MIXING_ASRC1_CLK_RST_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MIXING_ASRC1_CTL0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MIXING_ASRC1_CTL1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MIXING_ASRC1_FIFO_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MIXING_ASRC1_STATUS_FMIN_CNTR_LSB)] = WCD934X_READ, + [WCD934X_REG(WCD934X_MIXING_ASRC1_STATUS_FMIN_CNTR_MSB)] = WCD934X_READ, + [WCD934X_REG(WCD934X_MIXING_ASRC1_STATUS_FMAX_CNTR_LSB)] = WCD934X_READ, + [WCD934X_REG(WCD934X_MIXING_ASRC1_STATUS_FMAX_CNTR_MSB)] = WCD934X_READ, + [WCD934X_REG(WCD934X_MIXING_ASRC1_STATUS_FIFO)] = WCD934X_READ, + [WCD934X_REG(WCD934X_MIXING_ASRC2_CLK_RST_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MIXING_ASRC2_CTL0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MIXING_ASRC2_CTL1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MIXING_ASRC2_FIFO_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MIXING_ASRC2_STATUS_FMIN_CNTR_LSB)] = WCD934X_READ, + [WCD934X_REG(WCD934X_MIXING_ASRC2_STATUS_FMIN_CNTR_MSB)] = WCD934X_READ, + [WCD934X_REG(WCD934X_MIXING_ASRC2_STATUS_FMAX_CNTR_LSB)] = WCD934X_READ, + [WCD934X_REG(WCD934X_MIXING_ASRC2_STATUS_FMAX_CNTR_MSB)] = WCD934X_READ, + [WCD934X_REG(WCD934X_MIXING_ASRC2_STATUS_FIFO)] = WCD934X_READ, + [WCD934X_REG(WCD934X_MIXING_ASRC3_CLK_RST_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MIXING_ASRC3_CTL0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MIXING_ASRC3_CTL1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MIXING_ASRC3_FIFO_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MIXING_ASRC3_STATUS_FMIN_CNTR_LSB)] = WCD934X_READ, + [WCD934X_REG(WCD934X_MIXING_ASRC3_STATUS_FMIN_CNTR_MSB)] = WCD934X_READ, + [WCD934X_REG(WCD934X_MIXING_ASRC3_STATUS_FMAX_CNTR_LSB)] = WCD934X_READ, + [WCD934X_REG(WCD934X_MIXING_ASRC3_STATUS_FMAX_CNTR_MSB)] = WCD934X_READ, + [WCD934X_REG(WCD934X_MIXING_ASRC3_STATUS_FIFO)] = WCD934X_READ, + [WCD934X_REG(WCD934X_SWR_AHB_BRIDGE_WR_DATA_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SWR_AHB_BRIDGE_WR_DATA_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SWR_AHB_BRIDGE_WR_DATA_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SWR_AHB_BRIDGE_WR_DATA_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SWR_AHB_BRIDGE_WR_ADDR_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SWR_AHB_BRIDGE_WR_ADDR_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SWR_AHB_BRIDGE_WR_ADDR_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SWR_AHB_BRIDGE_WR_ADDR_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SWR_AHB_BRIDGE_RD_ADDR_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SWR_AHB_BRIDGE_RD_ADDR_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SWR_AHB_BRIDGE_RD_ADDR_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SWR_AHB_BRIDGE_RD_ADDR_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SWR_AHB_BRIDGE_RD_DATA_0)] = WCD934X_READ, + [WCD934X_REG(WCD934X_SWR_AHB_BRIDGE_RD_DATA_1)] = WCD934X_READ, + [WCD934X_REG(WCD934X_SWR_AHB_BRIDGE_RD_DATA_2)] = WCD934X_READ, + [WCD934X_REG(WCD934X_SWR_AHB_BRIDGE_RD_DATA_3)] = WCD934X_READ, + [WCD934X_REG(WCD934X_SWR_AHB_BRIDGE_ACCESS_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SWR_AHB_BRIDGE_ACCESS_STATUS)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_SIDETONE_SRC0_ST_SRC_PATH_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_SIDETONE_SRC0_ST_SRC_PATH_CFG1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_SIDETONE_SRC1_ST_SRC_PATH_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_SIDETONE_SRC1_ST_SRC_PATH_CFG1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDETONE_ASRC0_CLK_RST_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDETONE_ASRC0_CTL0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDETONE_ASRC0_CTL1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDETONE_ASRC0_FIFO_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDETONE_ASRC0_STATUS_FMIN_CNTR_LSB)] = + WCD934X_READ, + [WCD934X_REG(WCD934X_SIDETONE_ASRC0_STATUS_FMIN_CNTR_MSB)] = + WCD934X_READ, + [WCD934X_REG(WCD934X_SIDETONE_ASRC0_STATUS_FMAX_CNTR_LSB)] = + WCD934X_READ, + [WCD934X_REG(WCD934X_SIDETONE_ASRC0_STATUS_FMAX_CNTR_MSB)] = + WCD934X_READ, + [WCD934X_REG(WCD934X_SIDETONE_ASRC0_STATUS_FIFO)] = WCD934X_READ, + [WCD934X_REG(WCD934X_SIDETONE_ASRC1_CLK_RST_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDETONE_ASRC1_CTL0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDETONE_ASRC1_CTL1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDETONE_ASRC1_FIFO_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDETONE_ASRC1_STATUS_FMIN_CNTR_LSB)] = + WCD934X_READ, + [WCD934X_REG(WCD934X_SIDETONE_ASRC1_STATUS_FMIN_CNTR_MSB)] = + WCD934X_READ, + [WCD934X_REG(WCD934X_SIDETONE_ASRC1_STATUS_FMAX_CNTR_LSB)] = + WCD934X_READ, + [WCD934X_REG(WCD934X_SIDETONE_ASRC1_STATUS_FMAX_CNTR_MSB)] = + WCD934X_READ, + [WCD934X_REG(WCD934X_SIDETONE_ASRC1_STATUS_FIFO)] = WCD934X_READ, + [WCD934X_REG(WCD934X_EC_REF_HQ0_EC_REF_HQ_PATH_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_EC_REF_HQ0_EC_REF_HQ_CFG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_EC_REF_HQ1_EC_REF_HQ_PATH_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_EC_REF_HQ1_EC_REF_HQ_CFG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_EC_ASRC0_CLK_RST_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_EC_ASRC0_CTL0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_EC_ASRC0_CTL1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_EC_ASRC0_FIFO_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_EC_ASRC0_STATUS_FMIN_CNTR_LSB)] = WCD934X_READ, + [WCD934X_REG(WCD934X_EC_ASRC0_STATUS_FMIN_CNTR_MSB)] = WCD934X_READ, + [WCD934X_REG(WCD934X_EC_ASRC0_STATUS_FMAX_CNTR_LSB)] = WCD934X_READ, + [WCD934X_REG(WCD934X_EC_ASRC0_STATUS_FMAX_CNTR_MSB)] = WCD934X_READ, + [WCD934X_REG(WCD934X_EC_ASRC0_STATUS_FIFO)] = WCD934X_READ, + [WCD934X_REG(WCD934X_EC_ASRC1_CLK_RST_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_EC_ASRC1_CTL0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_EC_ASRC1_CTL1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_EC_ASRC1_FIFO_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_EC_ASRC1_STATUS_FMIN_CNTR_LSB)] = WCD934X_READ, + [WCD934X_REG(WCD934X_EC_ASRC1_STATUS_FMIN_CNTR_MSB)] = WCD934X_READ, + [WCD934X_REG(WCD934X_EC_ASRC1_STATUS_FMAX_CNTR_LSB)] = WCD934X_READ, + [WCD934X_REG(WCD934X_EC_ASRC1_STATUS_FMAX_CNTR_MSB)] = WCD934X_READ, + [WCD934X_REG(WCD934X_EC_ASRC1_STATUS_FIFO)] = WCD934X_READ, +}; + +const u8 wcd934x_page13_reg_access[WCD934X_PAGE_SIZE] = { + [WCD934X_REG(WCD934X_PAGE13_PAGE_REGISTER)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX_INP_MUX_RX_INT0_CFG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX_INP_MUX_RX_INT0_CFG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX_INP_MUX_RX_INT1_CFG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX_INP_MUX_RX_INT1_CFG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX_INP_MUX_RX_INT2_CFG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX_INP_MUX_RX_INT2_CFG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX_INP_MUX_RX_INT3_CFG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX_INP_MUX_RX_INT3_CFG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX_INP_MUX_RX_INT4_CFG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX_INP_MUX_RX_INT4_CFG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX_INP_MUX_RX_INT7_CFG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX_INP_MUX_RX_INT7_CFG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX_INP_MUX_RX_INT8_CFG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX_INP_MUX_RX_INT8_CFG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX_INP_MUX_RX_MIX_CFG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX_INP_MUX_RX_MIX_CFG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX_INP_MUX_RX_MIX_CFG2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX_INP_MUX_RX_MIX_CFG3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX_INP_MUX_RX_MIX_CFG4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX_INP_MUX_SIDETONE_SRC_CFG0)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX_INP_MUX_SIDETONE_SRC_CFG1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX_INP_MUX_ANC_CFG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX_INP_MUX_SPLINE_ASRC_CFG0)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX_INP_MUX_EC_REF_HQ_CFG0)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG0)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX_INP_MUX_ADC_MUX1_CFG0)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX_INP_MUX_ADC_MUX1_CFG1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX_INP_MUX_ADC_MUX2_CFG0)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX_INP_MUX_ADC_MUX2_CFG1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX_INP_MUX_ADC_MUX3_CFG0)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX_INP_MUX_ADC_MUX3_CFG1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX_INP_MUX_ADC_MUX4_CFG0)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX_INP_MUX_ADC_MUX5_CFG0)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX_INP_MUX_ADC_MUX6_CFG0)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX_INP_MUX_ADC_MUX7_CFG0)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX_INP_MUX_ADC_MUX8_CFG0)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX_INP_MUX_ADC_MUX10_CFG0)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX_INP_MUX_ADC_MUX11_CFG0)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX_INP_MUX_ADC_MUX12_CFG0)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX_INP_MUX_ADC_MUX13_CFG0)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG0)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG2)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG3)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG0)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG2)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG3)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_IF_ROUTER_TX_MUX_CFG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_IF_ROUTER_TX_MUX_CFG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_IF_ROUTER_TX_MUX_CFG2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_IF_ROUTER_TX_MUX_CFG3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_CLK_RST_CTRL_MCLK_CONTROL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_CLK_RST_CTRL_FS_CNT_CONTROL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_CLK_RST_CTRL_SWR_CONTROL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_CLK_RST_CTRL_DSD_CONTROL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_CLK_RST_CTRL_ASRC_SHARE_CONTROL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_CLK_RST_CTRL_GFM_CONTROL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_PROX_DETECT_PROX_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_PROX_DETECT_PROX_POLL_PERIOD0)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_PROX_DETECT_PROX_POLL_PERIOD1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_PROX_DETECT_PROX_SIG_PATTERN_LSB)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_PROX_DETECT_PROX_SIG_PATTERN_MSB)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_PROX_DETECT_PROX_STATUS)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_PROX_DETECT_PROX_TEST_CTRL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_PROX_DETECT_PROX_TEST_BUFF_LSB)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_PROX_DETECT_PROX_TEST_BUFF_MSB)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_PROX_DETECT_PROX_TEST_BUFF_LSB_RD)] = + WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_PROX_DETECT_PROX_TEST_BUFF_MSB_RD)] = + WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_PROX_DETECT_PROX_CTL_REPEAT_PAT)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_SIDETONE_IIR0_IIR_PATH_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B1_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B2_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B3_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B4_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B5_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B6_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B7_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B8_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_SIDETONE_IIR0_IIR_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_TIMER_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_SIDETONE_IIR1_IIR_PATH_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B1_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B2_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B3_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B4_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B5_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B6_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B7_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B8_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_SIDETONE_IIR1_IIR_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_TIMER_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_SIDETONE_IIR1_IIR_COEF_B1_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_SIDETONE_IIR1_IIR_COEF_B2_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TOP_TOP_CFG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TOP_TOP_CFG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TOP_TOP_CFG7)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TOP_HPHL_COMP_WR_LSB)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TOP_HPHL_COMP_WR_MSB)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TOP_HPHL_COMP_LUT)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TOP_HPHL_COMP_RD_LSB)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_TOP_HPHL_COMP_RD_MSB)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_TOP_HPHR_COMP_WR_LSB)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TOP_HPHR_COMP_WR_MSB)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TOP_HPHR_COMP_LUT)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TOP_HPHR_COMP_RD_LSB)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_TOP_HPHR_COMP_RD_MSB)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_TOP_DIFFL_COMP_WR_LSB)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TOP_DIFFL_COMP_WR_MSB)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TOP_DIFFL_COMP_LUT)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TOP_DIFFL_COMP_RD_LSB)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_TOP_DIFFL_COMP_RD_MSB)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_TOP_DIFFR_COMP_WR_LSB)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TOP_DIFFR_COMP_WR_MSB)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TOP_DIFFR_COMP_LUT)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TOP_DIFFR_COMP_RD_LSB)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_TOP_DIFFR_COMP_RD_MSB)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_DSD0_PATH_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_DSD0_CFG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_DSD0_CFG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_DSD0_CFG2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_DSD0_CFG3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_DSD0_CFG4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_DSD0_CFG5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_DSD1_PATH_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_DSD1_CFG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_DSD1_CFG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_DSD1_CFG2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_DSD1_CFG3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_DSD1_CFG4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_DSD1_CFG5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX_IDLE_DET_PATH_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX_IDLE_DET_CFG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX_IDLE_DET_CFG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX_IDLE_DET_CFG2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX_IDLE_DET_CFG3)] = WCD934X_READ_WRITE, +}; + +const u8 wcd934x_page14_reg_access[WCD934X_PAGE_SIZE] = { + [WCD934X_REG(WCD934X_PAGE14_PAGE_REGISTER)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_CLK_RST_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_PULSE_SUPR_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_TIMER)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_BW_SW)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_THRESH)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_STATUS)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_DIAG_CTRL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_DIAG_TIMER2)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_DIAG_OFFSET_BW1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_DIAG_OFFSET_BW2)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_DIAG_OFFSET_BW3)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_DIAG_OFFSET_BW4)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_DIAG_OFFSET_BW5)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_DIAG_LIMIT_BW1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_DIAG_LIMIT_BW2)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_DIAG_LIMIT_BW3)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_DIAG_LIMIT_BW4)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_DIAG_LIMIT_BW5)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_DIAG_LIMITD1_BW1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_DIAG_LIMITD1_BW2)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_DIAG_LIMITD1_BW3)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_DIAG_LIMITD1_BW4)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_DIAG_LIMITD1_BW5)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_DIAG_HYST_BW1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_DIAG_HYST_BW2)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_DIAG_HYST_BW3)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_DIAG_HYST_BW4)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_DIAG_HYST_BW5)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_RMAX_DIAG)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_RMIN_DIAG)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_PH_DET)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_DIAG_CLR)] = WCD934X_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_MB_SW_STATE)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_MAST_DIAG_STATE)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_RATE_OUT_7_0)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_RATE_OUT_15_8)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_RATE_OUT_23_16)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_RATE_OUT_31_24)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_RATE_OUT_39_32)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_RATE_OUT_40_43)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_CLK_RST_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_PULSE_SUPR_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_TIMER)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_BW_SW)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_THRESH)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_STATUS)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_DIAG_CTRL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_DIAG_TIMER2)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_DIAG_OFFSET_BW1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_DIAG_OFFSET_BW2)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_DIAG_OFFSET_BW3)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_DIAG_OFFSET_BW4)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_DIAG_OFFSET_BW5)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_DIAG_LIMIT_BW1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_DIAG_LIMIT_BW2)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_DIAG_LIMIT_BW3)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_DIAG_LIMIT_BW4)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_DIAG_LIMIT_BW5)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_DIAG_LIMITD1_BW1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_DIAG_LIMITD1_BW2)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_DIAG_LIMITD1_BW3)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_DIAG_LIMITD1_BW4)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_DIAG_LIMITD1_BW5)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_DIAG_HYST_BW1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_DIAG_HYST_BW2)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_DIAG_HYST_BW3)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_DIAG_HYST_BW4)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_DIAG_HYST_BW5)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_RMAX_DIAG)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_RMIN_DIAG)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_PH_DET)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_DIAG_CLR)] = WCD934X_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_MB_SW_STATE)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_MAST_DIAG_STATE)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_RATE_OUT_7_0)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_RATE_OUT_15_8)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_RATE_OUT_23_16)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_RATE_OUT_31_24)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_RATE_OUT_39_32)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_RATE_OUT_40_43)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_CLK_RST_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_PULSE_SUPR_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_TIMER)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_BW_SW)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_THRESH)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_STATUS)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_DIAG_CTRL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_DIAG_TIMER2)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_DIAG_OFFSET_BW1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_DIAG_OFFSET_BW2)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_DIAG_OFFSET_BW3)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_DIAG_OFFSET_BW4)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_DIAG_OFFSET_BW5)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_DIAG_LIMIT_BW1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_DIAG_LIMIT_BW2)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_DIAG_LIMIT_BW3)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_DIAG_LIMIT_BW4)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_DIAG_LIMIT_BW5)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_DIAG_LIMITD1_BW1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_DIAG_LIMITD1_BW2)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_DIAG_LIMITD1_BW3)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_DIAG_LIMITD1_BW4)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_DIAG_LIMITD1_BW5)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_DIAG_HYST_BW1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_DIAG_HYST_BW2)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_DIAG_HYST_BW3)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_DIAG_HYST_BW4)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_DIAG_HYST_BW5)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_RMAX_DIAG)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_RMIN_DIAG)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_PH_DET)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_DIAG_CLR)] = WCD934X_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_MB_SW_STATE)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_MAST_DIAG_STATE)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_RATE_OUT_7_0)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_RATE_OUT_15_8)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_RATE_OUT_23_16)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_RATE_OUT_31_24)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_RATE_OUT_39_32)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_RATE_OUT_40_43)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_CLK_RST_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_PULSE_SUPR_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_TIMER)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_BW_SW)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_THRESH)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_STATUS)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_DIAG_CTRL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_DIAG_TIMER2)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_DIAG_OFFSET_BW1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_DIAG_OFFSET_BW2)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_DIAG_OFFSET_BW3)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_DIAG_OFFSET_BW4)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_DIAG_OFFSET_BW5)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_DIAG_LIMIT_BW1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_DIAG_LIMIT_BW2)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_DIAG_LIMIT_BW3)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_DIAG_LIMIT_BW4)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_DIAG_LIMIT_BW5)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_DIAG_LIMITD1_BW1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_DIAG_LIMITD1_BW2)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_DIAG_LIMITD1_BW3)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_DIAG_LIMITD1_BW4)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_DIAG_LIMITD1_BW5)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_DIAG_HYST_BW1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_DIAG_HYST_BW2)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_DIAG_HYST_BW3)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_DIAG_HYST_BW4)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_DIAG_HYST_BW5)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_RMAX_DIAG)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_RMIN_DIAG)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_PH_DET)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_DIAG_CLR)] = WCD934X_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_MB_SW_STATE)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_MAST_DIAG_STATE)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_RATE_OUT_7_0)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_RATE_OUT_15_8)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_RATE_OUT_23_16)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_RATE_OUT_31_24)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_RATE_OUT_39_32)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_RATE_OUT_40_43)] = WCD934X_READ, +}; + +const u8 wcd934x_page15_reg_access[WCD934X_PAGE_SIZE] = { + [WCD934X_REG(WCD934X_PAGE15_PAGE_REGISTER)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SPLINE_SRC0_CLK_RST_CTL_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SPLINE_SRC0_STATUS)] = WCD934X_READ, + [WCD934X_REG(WCD934X_SPLINE_SRC1_CLK_RST_CTL_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SPLINE_SRC1_STATUS)] = WCD934X_READ, + [WCD934X_REG(WCD934X_SPLINE_SRC2_CLK_RST_CTL_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SPLINE_SRC2_STATUS)] = WCD934X_READ, + [WCD934X_REG(WCD934X_SPLINE_SRC3_CLK_RST_CTL_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SPLINE_SRC3_STATUS)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_DEBUG_DSD0_DEBUG_CFG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_DEBUG_DSD0_DEBUG_CFG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_DEBUG_DSD0_DEBUG_CFG2)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_DEBUG_DSD0_DEBUG_CFG3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_DEBUG_DSD1_DEBUG_CFG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_DEBUG_DSD1_DEBUG_CFG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_DEBUG_DSD1_DEBUG_CFG2)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_DEBUG_DSD1_DEBUG_CFG3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_DEBUG_SPLINE_SRC_DEBUG_CFG0)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_DEBUG_SPLINE_SRC_DEBUG_CFG1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_DEBUG_RC_RE_ASRC_DEBUG_CFG0)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_DEBUG_ANC0_RC0_FIFO_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_DEBUG_ANC0_RC1_FIFO_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_DEBUG_ANC1_RC0_FIFO_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_DEBUG_ANC1_RC1_FIFO_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_DEBUG_ANC_RC_RST_DBG_CNTR)] = + WCD934X_READ_WRITE, +}; + +const u8 wcd934x_page_0x50_reg_access[WCD934X_PAGE_SIZE] = { + [WCD934X_REG(WCD934X_PAGE80_PAGE_REGISTER)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_WR_DATA_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_WR_DATA_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_WR_DATA_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_WR_DATA_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_WR_ADDR_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_WR_ADDR_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_WR_ADDR_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_WR_ADDR_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_RD_ADDR_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_RD_ADDR_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_RD_ADDR_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_RD_ADDR_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_RD_DATA_0)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CODEC_CPR_RD_DATA_1)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CODEC_CPR_RD_DATA_2)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CODEC_CPR_RD_DATA_3)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CODEC_CPR_ACCESS_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_ACCESS_STATUS)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CODEC_CPR_NOM_CX_VDD)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_SVS_CX_VDD)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_SVS2_CX_VDD)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_NOM_MX_VDD)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_SVS_MX_VDD)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_SVS2_MX_VDD)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_SVS2_MIN_CX_VDD)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_MAX_SVS2_STEP)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_SW_MODECHNG_STATUS)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CODEC_CPR_SW_MODECHNG_START)] = WCD934X_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_CPR_STATUS)] = WCD934X_READ_WRITE, +}; + +const u8 wcd934x_page_0x80_reg_access[WCD934X_PAGE_SIZE] = { + [WCD934X_REG(WCD934X_PAGE80_PAGE_REGISTER)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_WR_DATA_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_WR_DATA_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_WR_DATA_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_WR_DATA_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_WR_ADDR_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_WR_ADDR_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_WR_ADDR_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_WR_ADDR_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_RD_ADDR_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_RD_ADDR_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_RD_ADDR_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_RD_ADDR_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_RD_DATA_0)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CODEC_CPR_RD_DATA_1)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CODEC_CPR_RD_DATA_2)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CODEC_CPR_RD_DATA_3)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CODEC_CPR_ACCESS_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_ACCESS_STATUS)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CODEC_CPR_NOM_CX_VDD)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_SVS_CX_VDD)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_SVS2_CX_VDD)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_NOM_MX_VDD)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_SVS_MX_VDD)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_SVS2_MX_VDD)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_SVS2_MIN_CX_VDD)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_MAX_SVS2_STEP)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_SW_MODECHNG_STATUS)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CODEC_CPR_SW_MODECHNG_START)] = WCD934X_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_CPR_STATUS)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_PAGE128_PAGE_REGISTER)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_BIST_MODE_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_RF_PA_ON_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_INTR1_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_INTR2_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_SWR_DATA_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_SWR_CLK_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_I2S_2_SCK_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_SLIMBUS_DATA1_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_SLIMBUS_DATA2_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_SLIMBUS_CLK_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_I2C_CLK_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_I2C_DATA_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_I2S_0_RX_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_I2S_0_TX_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_I2S_0_SCK_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_I2S_0_WS_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_I2S_1_RX_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_I2S_1_TX_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_I2S_1_SCK_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_I2S_1_WS_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_DMIC1_CLK_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_DMIC1_DATA_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_DMIC2_CLK_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_DMIC2_DATA_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_DMIC3_CLK_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_DMIC3_DATA_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_JTCK_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_GPIO1_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_GPIO2_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_GPIO3_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_GPIO4_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_SPI_S_CSN_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_SPI_S_CLK_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_SPI_S_DOUT_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_SPI_S_DIN_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_BA_N_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_GPIO0_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_I2S_2_RX_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_I2S_2_WS_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TEST_DEBUG_PIN_CTL_OE_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TEST_DEBUG_PIN_CTL_OE_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TEST_DEBUG_PIN_CTL_OE_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TEST_DEBUG_PIN_CTL_OE_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TEST_DEBUG_PIN_CTL_OE_4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TEST_DEBUG_PIN_CTL_DATA_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TEST_DEBUG_PIN_CTL_DATA_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TEST_DEBUG_PIN_CTL_DATA_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TEST_DEBUG_PIN_CTL_DATA_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TEST_DEBUG_PIN_CTL_DATA_4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TEST_DEBUG_PAD_DRVCTL_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TEST_DEBUG_PAD_DRVCTL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TEST_DEBUG_PIN_STATUS)] = WCD934X_READ, + [WCD934X_REG(WCD934X_TEST_DEBUG_NPL_DLY_TEST_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TEST_DEBUG_NPL_DLY_TEST_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TEST_DEBUG_MEM_CTRL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TEST_DEBUG_DEBUG_BUS_SEL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TEST_DEBUG_DEBUG_JTAG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TEST_DEBUG_DEBUG_EN_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TEST_DEBUG_DEBUG_EN_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TEST_DEBUG_DEBUG_EN_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TEST_DEBUG_DEBUG_EN_4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TEST_DEBUG_DEBUG_EN_5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TEST_DEBUG_ANA_DTEST_DIR)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TEST_DEBUG_PAD_INP_DISABLE_0)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TEST_DEBUG_PAD_INP_DISABLE_1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TEST_DEBUG_PAD_INP_DISABLE_2)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TEST_DEBUG_PAD_INP_DISABLE_3)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TEST_DEBUG_PAD_INP_DISABLE_4)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TEST_DEBUG_SYSMEM_CTRL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TEST_DEBUG_SOC_SW_PWR_SEQ_DELAY)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TEST_DEBUG_LVAL_NOM_LOW)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TEST_DEBUG_LVAL_NOM_HIGH)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TEST_DEBUG_LVAL_SVS_SVS2_LOW)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TEST_DEBUG_LVAL_SVS_SVS2_HIGH)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TEST_DEBUG_SPI_SLAVE_CHAR)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TEST_DEBUG_CODEC_DIAGS)] = WCD934X_READ, +}; + +const u8 * const wcd934x_reg[WCD934X_NUM_PAGES] = { + [WCD934X_PAGE_0] = wcd934x_page0_reg_access, + [WCD934X_PAGE_1] = wcd934x_page1_reg_access, + [WCD934X_PAGE_2] = wcd934x_page2_reg_access, + [WCD934X_PAGE_4] = wcd934x_page4_reg_access, + [WCD934X_PAGE_5] = wcd934x_page5_reg_access, + [WCD934X_PAGE_6] = wcd934x_page6_reg_access, + [WCD934X_PAGE_7] = wcd934x_page7_reg_access, + [WCD934X_PAGE_10] = wcd934x_page10_reg_access, + [WCD934X_PAGE_11] = wcd934x_page11_reg_access, + [WCD934X_PAGE_12] = wcd934x_page12_reg_access, + [WCD934X_PAGE_13] = wcd934x_page13_reg_access, + [WCD934X_PAGE_14] = wcd934x_page14_reg_access, + [WCD934X_PAGE_15] = wcd934x_page15_reg_access, + [WCD934X_PAGE_0x50] = wcd934x_page_0x50_reg_access, + [WCD934X_PAGE_0X80] = wcd934x_page_0x80_reg_access, +}; diff --git a/audio-kernel/asoc/codecs/wcd934x/wcd934x.c b/audio-kernel/asoc/codecs/wcd934x/wcd934x.c new file mode 100644 index 000000000..9eea652df --- /dev/null +++ b/audio-kernel/asoc/codecs/wcd934x/wcd934x.c @@ -0,0 +1,11282 @@ +/* Copyright (c) 2015-2019, 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "wcd934x.h" +#include "wcd934x-mbhc.h" +#include "wcd934x-routing.h" +#include "wcd934x-dsp-cntl.h" +#include "wcd934x_irq.h" +#include "../core.h" +#include "../pdata.h" +#include "../wcd9xxx-irq.h" +#include "../wcd9xxx-common-v2.h" +#include "../wcd9xxx-resmgr-v2.h" +#include "../wcdcal-hwdep.h" +#include "wcd934x-dsd.h" + +#define WCD934X_RATES_MASK (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\ + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |\ + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000 |\ + SNDRV_PCM_RATE_384000) +/* Fractional Rates */ +#define WCD934X_FRAC_RATES_MASK (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_88200 |\ + SNDRV_PCM_RATE_176400) + +#define WCD934X_FORMATS_S16_S24_LE (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE) + +#define WCD934X_FORMATS_S16_S24_S32_LE (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) + +#define WCD934X_FORMATS_S16_LE (SNDRV_PCM_FMTBIT_S16_LE) + +#define MICB_LOAD_PROP "qcom,vreg-micb" +#define MICB_LOAD_DEFAULT 30400 + +/* Macros for packing register writes into a U32 */ +#define WCD934X_PACKED_REG_SIZE sizeof(u32) +#define WCD934X_CODEC_UNPACK_ENTRY(packed, reg, mask, val) \ + do { \ + ((reg) = ((packed >> 16) & (0xffff))); \ + ((mask) = ((packed >> 8) & (0xff))); \ + ((val) = ((packed) & (0xff))); \ + } while (0) + +#define STRING(name) #name +#define WCD_DAPM_ENUM(name, reg, offset, text) \ +static SOC_ENUM_SINGLE_DECL(name##_enum, reg, offset, text); \ +static const struct snd_kcontrol_new name##_mux = \ + SOC_DAPM_ENUM(STRING(name), name##_enum) + +#define WCD_DAPM_ENUM_EXT(name, reg, offset, text, getname, putname) \ +static SOC_ENUM_SINGLE_DECL(name##_enum, reg, offset, text); \ +static const struct snd_kcontrol_new name##_mux = \ + SOC_DAPM_ENUM_EXT(STRING(name), name##_enum, getname, putname) + +#define WCD_DAPM_MUX(name, shift, kctl) \ + SND_SOC_DAPM_MUX(name, SND_SOC_NOPM, shift, 0, &kctl##_mux) + +/* + * Timeout in milli seconds and it is the wait time for + * slim channel removal interrupt to receive. + */ +#define WCD934X_SLIM_CLOSE_TIMEOUT 1000 +#define WCD934X_SLIM_IRQ_OVERFLOW (1 << 0) +#define WCD934X_SLIM_IRQ_UNDERFLOW (1 << 1) +#define WCD934X_SLIM_IRQ_PORT_CLOSED (1 << 2) +#define WCD934X_MCLK_CLK_12P288MHZ 12288000 +#define WCD934X_MCLK_CLK_9P6MHZ 9600000 + +#define WCD934X_INTERP_MUX_NUM_INPUTS 3 +#define WCD934X_NUM_INTERPOLATORS 9 +#define WCD934X_NUM_DECIMATORS 9 +#define WCD934X_RX_PATH_CTL_OFFSET 20 + +#define BYTE_BIT_MASK(nr) (1 << ((nr) % BITS_PER_BYTE)) + +#define WCD934X_REG_BITS 8 +#define WCD934X_MAX_VALID_ADC_MUX 13 +#define WCD934X_INVALID_ADC_MUX 9 + +#define WCD934X_AMIC_PWR_LEVEL_LP 0 +#define WCD934X_AMIC_PWR_LEVEL_DEFAULT 1 +#define WCD934X_AMIC_PWR_LEVEL_HP 2 +#define WCD934X_AMIC_PWR_LEVEL_HYBRID 3 +#define WCD934X_AMIC_PWR_LVL_MASK 0x60 +#define WCD934X_AMIC_PWR_LVL_SHIFT 0x5 + +#define WCD934X_DMIC_DRV_CTL_MASK 0x0C +#define WCD934X_DMIC_DRV_CTL_SHIFT 0x02 + +#define WCD934X_DEC_PWR_LVL_MASK 0x06 +#define WCD934X_DEC_PWR_LVL_LP 0x02 +#define WCD934X_DEC_PWR_LVL_HP 0x04 +#define WCD934X_DEC_PWR_LVL_DF 0x00 +#define WCD934X_DEC_PWR_LVL_HYBRID WCD934X_DEC_PWR_LVL_DF +#define WCD934X_STRING_LEN 100 + +#define WCD934X_CDC_SIDETONE_IIR_COEFF_MAX 5 +#define WCD934X_CDC_REPEAT_WRITES_MAX 16 +#define WCD934X_DIG_CORE_REG_MIN WCD934X_CDC_ANC0_CLK_RESET_CTL +#define WCD934X_DIG_CORE_REG_MAX 0xFFF + +#define WCD934X_CHILD_DEVICES_MAX 6 + +#define WCD934X_MAX_MICBIAS 4 +#define DAPM_MICBIAS1_STANDALONE "MIC BIAS1 Standalone" +#define DAPM_MICBIAS2_STANDALONE "MIC BIAS2 Standalone" +#define DAPM_MICBIAS3_STANDALONE "MIC BIAS3 Standalone" +#define DAPM_MICBIAS4_STANDALONE "MIC BIAS4 Standalone" + +#define TX_HPF_CUT_OFF_FREQ_MASK 0x60 +#define CF_MIN_3DB_4HZ 0x0 +#define CF_MIN_3DB_75HZ 0x1 +#define CF_MIN_3DB_150HZ 0x2 + +#define SB_OF_UF_MAX_RETRY_CNT 5 +#define CPE_ERR_WDOG_BITE BIT(0) +#define CPE_FATAL_IRQS CPE_ERR_WDOG_BITE + +#define WCD934X_MAD_AUDIO_FIRMWARE_PATH "wcd934x/wcd934x_mad_audio.bin" + +#define TAVIL_VERSION_ENTRY_SIZE 17 + +#define WCD934X_DIG_CORE_COLLAPSE_TIMER_MS (5 * 1000) + +enum { + POWER_COLLAPSE, + POWER_RESUME, +}; + +static int dig_core_collapse_enable = 1; +module_param(dig_core_collapse_enable, int, 0664); +MODULE_PARM_DESC(dig_core_collapse_enable, "enable/disable power gating"); + +/* dig_core_collapse timer in seconds */ +static int dig_core_collapse_timer = (WCD934X_DIG_CORE_COLLAPSE_TIMER_MS/1000); +module_param(dig_core_collapse_timer, int, 0664); +MODULE_PARM_DESC(dig_core_collapse_timer, "timer for power gating"); + +#define TAVIL_HPH_REG_RANGE_1 (WCD934X_HPH_R_DAC_CTL - WCD934X_HPH_CNP_EN + 1) +#define TAVIL_HPH_REG_RANGE_2 (WCD934X_HPH_NEW_ANA_HPH3 -\ + WCD934X_HPH_NEW_ANA_HPH2 + 1) +#define TAVIL_HPH_REG_RANGE_3 (WCD934X_HPH_NEW_INT_PA_RDAC_MISC3 -\ + WCD934X_HPH_NEW_INT_RDAC_GAIN_CTL + 1) +#define TAVIL_HPH_TOTAL_REG (TAVIL_HPH_REG_RANGE_1 + TAVIL_HPH_REG_RANGE_2 +\ + TAVIL_HPH_REG_RANGE_3) + +enum { + VI_SENSE_1, + VI_SENSE_2, + AUDIO_NOMINAL, + HPH_PA_DELAY, + CLSH_Z_CONFIG, + ANC_MIC_AMIC1, + ANC_MIC_AMIC2, + ANC_MIC_AMIC3, + ANC_MIC_AMIC4, + CLK_INTERNAL, + CLK_MODE, +}; + +enum { + AIF1_PB = 0, + AIF1_CAP, + AIF2_PB, + AIF2_CAP, + AIF3_PB, + AIF3_CAP, + AIF4_PB, + AIF4_VIFEED, + AIF4_MAD_TX, + NUM_CODEC_DAIS, +}; + +enum { + INTn_1_INP_SEL_ZERO = 0, + INTn_1_INP_SEL_DEC0, + INTn_1_INP_SEL_DEC1, + INTn_1_INP_SEL_IIR0, + INTn_1_INP_SEL_IIR1, + INTn_1_INP_SEL_RX0, + INTn_1_INP_SEL_RX1, + INTn_1_INP_SEL_RX2, + INTn_1_INP_SEL_RX3, + INTn_1_INP_SEL_RX4, + INTn_1_INP_SEL_RX5, + INTn_1_INP_SEL_RX6, + INTn_1_INP_SEL_RX7, +}; + +enum { + INTn_2_INP_SEL_ZERO = 0, + INTn_2_INP_SEL_RX0, + INTn_2_INP_SEL_RX1, + INTn_2_INP_SEL_RX2, + INTn_2_INP_SEL_RX3, + INTn_2_INP_SEL_RX4, + INTn_2_INP_SEL_RX5, + INTn_2_INP_SEL_RX6, + INTn_2_INP_SEL_RX7, + INTn_2_INP_SEL_PROXIMITY, +}; + +enum { + INTERP_MAIN_PATH, + INTERP_MIX_PATH, +}; + +struct tavil_idle_detect_config { + u8 hph_idle_thr; + u8 hph_idle_detect_en; +}; + +struct tavil_cpr_reg_defaults { + int wr_data; + int wr_addr; +}; + +struct interp_sample_rate { + int sample_rate; + int rate_val; +}; + +static struct interp_sample_rate sr_val_tbl[] = { + {8000, 0x0}, {16000, 0x1}, {32000, 0x3}, {48000, 0x4}, {96000, 0x5}, + {192000, 0x6}, {384000, 0x7}, {44100, 0x9}, {88200, 0xA}, + {176400, 0xB}, {352800, 0xC}, +}; + +static const struct wcd9xxx_ch tavil_rx_chs[WCD934X_RX_MAX] = { + WCD9XXX_CH(WCD934X_RX_PORT_START_NUMBER, 0), + WCD9XXX_CH(WCD934X_RX_PORT_START_NUMBER + 1, 1), + WCD9XXX_CH(WCD934X_RX_PORT_START_NUMBER + 2, 2), + WCD9XXX_CH(WCD934X_RX_PORT_START_NUMBER + 3, 3), + WCD9XXX_CH(WCD934X_RX_PORT_START_NUMBER + 4, 4), + WCD9XXX_CH(WCD934X_RX_PORT_START_NUMBER + 5, 5), + WCD9XXX_CH(WCD934X_RX_PORT_START_NUMBER + 6, 6), + WCD9XXX_CH(WCD934X_RX_PORT_START_NUMBER + 7, 7), +}; + +static const struct wcd9xxx_ch tavil_tx_chs[WCD934X_TX_MAX] = { + WCD9XXX_CH(0, 0), + WCD9XXX_CH(1, 1), + WCD9XXX_CH(2, 2), + WCD9XXX_CH(3, 3), + WCD9XXX_CH(4, 4), + WCD9XXX_CH(5, 5), + WCD9XXX_CH(6, 6), + WCD9XXX_CH(7, 7), + WCD9XXX_CH(8, 8), + WCD9XXX_CH(9, 9), + WCD9XXX_CH(10, 10), + WCD9XXX_CH(11, 11), + WCD9XXX_CH(12, 12), + WCD9XXX_CH(13, 13), + WCD9XXX_CH(14, 14), + WCD9XXX_CH(15, 15), +}; + +static const u32 vport_slim_check_table[NUM_CODEC_DAIS] = { + 0, /* AIF1_PB */ + BIT(AIF2_CAP) | BIT(AIF3_CAP) | BIT(AIF4_MAD_TX), /* AIF1_CAP */ + 0, /* AIF2_PB */ + BIT(AIF1_CAP) | BIT(AIF3_CAP) | BIT(AIF4_MAD_TX), /* AIF2_CAP */ + 0, /* AIF3_PB */ + BIT(AIF1_CAP) | BIT(AIF2_CAP) | BIT(AIF4_MAD_TX), /* AIF3_CAP */ + 0, /* AIF4_PB */ +}; + +/* Codec supports 2 IIR filters */ +enum { + IIR0 = 0, + IIR1, + IIR_MAX, +}; + +/* Each IIR has 5 Filter Stages */ +enum { + BAND1 = 0, + BAND2, + BAND3, + BAND4, + BAND5, + BAND_MAX, +}; + +enum { + COMPANDER_1, /* HPH_L */ + COMPANDER_2, /* HPH_R */ + COMPANDER_3, /* LO1_DIFF */ + COMPANDER_4, /* LO2_DIFF */ + COMPANDER_5, /* LO3_SE - not used in Tavil */ + COMPANDER_6, /* LO4_SE - not used in Tavil */ + COMPANDER_7, /* SWR SPK CH1 */ + COMPANDER_8, /* SWR SPK CH2 */ + COMPANDER_MAX, +}; + +enum { + ASRC_IN_HPHL, + ASRC_IN_LO1, + ASRC_IN_HPHR, + ASRC_IN_LO2, + ASRC_IN_SPKR1, + ASRC_IN_SPKR2, + ASRC_INVALID, +}; + +enum { + ASRC0, + ASRC1, + ASRC2, + ASRC3, + ASRC_MAX, +}; + +enum { + CONV_88P2K_TO_384K, + CONV_96K_TO_352P8K, + CONV_352P8K_TO_384K, + CONV_384K_TO_352P8K, + CONV_384K_TO_384K, + CONV_96K_TO_384K, +}; + +static struct afe_param_slimbus_slave_port_cfg tavil_slimbus_slave_port_cfg = { + .minor_version = 1, + .slimbus_dev_id = AFE_SLIMBUS_DEVICE_1, + .slave_dev_pgd_la = 0, + .slave_dev_intfdev_la = 0, + .bit_width = 16, + .data_format = 0, + .num_channels = 1 +}; + +static struct afe_param_cdc_reg_page_cfg tavil_cdc_reg_page_cfg = { + .minor_version = AFE_API_VERSION_CDC_REG_PAGE_CFG, + .enable = 1, + .proc_id = AFE_CDC_REG_PAGE_ASSIGN_PROC_ID_1, +}; + +static struct afe_param_cdc_reg_cfg audio_reg_cfg[] = { + { + 1, + (WCD934X_REGISTER_START_OFFSET + WCD934X_SOC_MAD_MAIN_CTL_1), + HW_MAD_AUDIO_ENABLE, 0x1, WCD934X_REG_BITS, 0 + }, + { + 1, + (WCD934X_REGISTER_START_OFFSET + WCD934X_SOC_MAD_AUDIO_CTL_3), + HW_MAD_AUDIO_SLEEP_TIME, 0xF, WCD934X_REG_BITS, 0 + }, + { + 1, + (WCD934X_REGISTER_START_OFFSET + WCD934X_SOC_MAD_AUDIO_CTL_4), + HW_MAD_TX_AUDIO_SWITCH_OFF, 0x1, WCD934X_REG_BITS, 0 + }, + { + 1, + (WCD934X_REGISTER_START_OFFSET + WCD934X_INTR_CFG), + MAD_AUDIO_INT_DEST_SELECT_REG, 0x2, WCD934X_REG_BITS, 0 + }, + { + 1, + (WCD934X_REGISTER_START_OFFSET + WCD934X_INTR_PIN2_MASK3), + MAD_AUDIO_INT_MASK_REG, 0x1, WCD934X_REG_BITS, 0 + }, + { + 1, + (WCD934X_REGISTER_START_OFFSET + WCD934X_INTR_PIN2_STATUS3), + MAD_AUDIO_INT_STATUS_REG, 0x1, WCD934X_REG_BITS, 0 + }, + { + 1, + (WCD934X_REGISTER_START_OFFSET + WCD934X_INTR_PIN2_CLEAR3), + MAD_AUDIO_INT_CLEAR_REG, 0x1, WCD934X_REG_BITS, 0 + }, + { + 1, + (WCD934X_REGISTER_START_OFFSET + WCD934X_SB_PGD_PORT_TX_BASE), + SB_PGD_PORT_TX_WATERMARK_N, 0x1E, WCD934X_REG_BITS, 0x1 + }, + { + 1, + (WCD934X_REGISTER_START_OFFSET + WCD934X_SB_PGD_PORT_TX_BASE), + SB_PGD_PORT_TX_ENABLE_N, 0x1, WCD934X_REG_BITS, 0x1 + }, + { + 1, + (WCD934X_REGISTER_START_OFFSET + WCD934X_SB_PGD_PORT_RX_BASE), + SB_PGD_PORT_RX_WATERMARK_N, 0x1E, WCD934X_REG_BITS, 0x1 + }, + { + 1, + (WCD934X_REGISTER_START_OFFSET + WCD934X_SB_PGD_PORT_RX_BASE), + SB_PGD_PORT_RX_ENABLE_N, 0x1, WCD934X_REG_BITS, 0x1 + }, + { + 1, + (WCD934X_REGISTER_START_OFFSET + + WCD934X_CDC_ANC0_IIR_ADAPT_CTL), + AANC_FF_GAIN_ADAPTIVE, 0x4, WCD934X_REG_BITS, 0 + }, + { + 1, + (WCD934X_REGISTER_START_OFFSET + + WCD934X_CDC_ANC0_IIR_ADAPT_CTL), + AANC_FFGAIN_ADAPTIVE_EN, 0x8, WCD934X_REG_BITS, 0 + }, + { + 1, + (WCD934X_REGISTER_START_OFFSET + + WCD934X_CDC_ANC0_FF_A_GAIN_CTL), + AANC_GAIN_CONTROL, 0xFF, WCD934X_REG_BITS, 0 + }, + { + 1, + (WCD934X_REGISTER_START_OFFSET + + SB_PGD_TX_PORT_MULTI_CHANNEL_0(0)), + SB_PGD_TX_PORTn_MULTI_CHNL_0, 0xFF, WCD934X_REG_BITS, 0x4 + }, + { + 1, + (WCD934X_REGISTER_START_OFFSET + + SB_PGD_TX_PORT_MULTI_CHANNEL_1(0)), + SB_PGD_TX_PORTn_MULTI_CHNL_1, 0xFF, WCD934X_REG_BITS, 0x4 + }, + { + 1, + (WCD934X_REGISTER_START_OFFSET + + SB_PGD_RX_PORT_MULTI_CHANNEL_0(0x180, 0)), + SB_PGD_RX_PORTn_MULTI_CHNL_0, 0xFF, WCD934X_REG_BITS, 0x4 + }, + { + 1, + (WCD934X_REGISTER_START_OFFSET + + SB_PGD_RX_PORT_MULTI_CHANNEL_0(0x181, 0)), + SB_PGD_RX_PORTn_MULTI_CHNL_1, 0xFF, WCD934X_REG_BITS, 0x4 + }, +}; + +static struct afe_param_cdc_reg_cfg_data tavil_audio_reg_cfg = { + .num_registers = ARRAY_SIZE(audio_reg_cfg), + .reg_data = audio_reg_cfg, +}; + +static struct afe_param_id_cdc_aanc_version tavil_cdc_aanc_version = { + .cdc_aanc_minor_version = AFE_API_VERSION_CDC_AANC_VERSION, + .aanc_hw_version = AANC_HW_BLOCK_VERSION_2, +}; + +static const DECLARE_TLV_DB_SCALE(digital_gain, 0, 1, 0); +static const DECLARE_TLV_DB_SCALE(line_gain, 0, 7, 1); +static const DECLARE_TLV_DB_SCALE(analog_gain, 0, 25, 1); + +#define WCD934X_TX_UNMUTE_DELAY_MS 40 + +static int tx_unmute_delay = WCD934X_TX_UNMUTE_DELAY_MS; +module_param(tx_unmute_delay, int, 0664); +MODULE_PARM_DESC(tx_unmute_delay, "delay to unmute the tx path"); + +static void tavil_codec_set_tx_hold(struct snd_soc_codec *, u16, bool); + +/* Hold instance to soundwire platform device */ +struct tavil_swr_ctrl_data { + struct platform_device *swr_pdev; +}; + +struct wcd_swr_ctrl_platform_data { + void *handle; /* holds codec private data */ + int (*read)(void *handle, int reg); + int (*write)(void *handle, int reg, int val); + int (*bulk_write)(void *handle, u32 *reg, u32 *val, size_t len); + int (*clk)(void *handle, bool enable); + int (*handle_irq)(void *handle, + irqreturn_t (*swrm_irq_handler)(int irq, void *data), + void *swrm_handle, int action); +}; + +/* Holds all Soundwire and speaker related information */ +struct wcd934x_swr { + struct tavil_swr_ctrl_data *ctrl_data; + struct wcd_swr_ctrl_platform_data plat_data; + struct mutex read_mutex; + struct mutex write_mutex; + struct mutex clk_mutex; + int spkr_gain_offset; + int spkr_mode; + int clk_users; + int rx_7_count; + int rx_8_count; +}; + +struct tx_mute_work { + struct tavil_priv *tavil; + u8 decimator; + struct delayed_work dwork; +}; + +#define WCD934X_SPK_ANC_EN_DELAY_MS 550 +static int spk_anc_en_delay = WCD934X_SPK_ANC_EN_DELAY_MS; +module_param(spk_anc_en_delay, int, 0664); +MODULE_PARM_DESC(spk_anc_en_delay, "delay to enable anc in speaker path"); + +struct spk_anc_work { + struct tavil_priv *tavil; + struct delayed_work dwork; +}; + +struct hpf_work { + struct tavil_priv *tavil; + u8 decimator; + u8 hpf_cut_off_freq; + struct delayed_work dwork; +}; + +struct tavil_priv { + struct device *dev; + struct wcd9xxx *wcd9xxx; + struct snd_soc_codec *codec; + u32 rx_bias_count; + s32 dmic_0_1_clk_cnt; + s32 dmic_2_3_clk_cnt; + s32 dmic_4_5_clk_cnt; + s32 micb_ref[TAVIL_MAX_MICBIAS]; + s32 pullup_ref[TAVIL_MAX_MICBIAS]; + + /* ANC related */ + u32 anc_slot; + bool anc_func; + + /* compander */ + int comp_enabled[COMPANDER_MAX]; + int ear_spkr_gain; + + /* class h specific data */ + struct wcd_clsh_cdc_data clsh_d; + /* Tavil Interpolator Mode Select for EAR, HPH_L and HPH_R */ + u32 hph_mode; + + /* Mad switch reference count */ + int mad_switch_cnt; + + /* track tavil interface type */ + u8 intf_type; + + /* to track the status */ + unsigned long status_mask; + + struct afe_param_cdc_slimbus_slave_cfg slimbus_slave_cfg; + + /* num of slim ports required */ + struct wcd9xxx_codec_dai_data dai[NUM_CODEC_DAIS]; + /* Port values for Rx and Tx codec_dai */ + unsigned int rx_port_value[WCD934X_RX_MAX]; + unsigned int tx_port_value; + + struct wcd9xxx_resmgr_v2 *resmgr; + struct wcd934x_swr swr; + struct mutex micb_lock; + + struct delayed_work power_gate_work; + struct mutex power_lock; + + struct clk *wcd_ext_clk; + + /* mbhc module */ + struct wcd934x_mbhc *mbhc; + + struct mutex codec_mutex; + struct work_struct tavil_add_child_devices_work; + struct hpf_work tx_hpf_work[WCD934X_NUM_DECIMATORS]; + struct tx_mute_work tx_mute_dwork[WCD934X_NUM_DECIMATORS]; + struct spk_anc_work spk_anc_dwork; + + unsigned int vi_feed_value; + + /* DSP control */ + struct wcd_dsp_cntl *wdsp_cntl; + + /* cal info for codec */ + struct fw_info *fw_data; + + /* Entry for version info */ + struct snd_info_entry *entry; + struct snd_info_entry *version_entry; + + /* SVS voting related */ + struct mutex svs_mutex; + int svs_ref_cnt; + + int native_clk_users; + /* ASRC users count */ + int asrc_users[ASRC_MAX]; + int asrc_output_mode[ASRC_MAX]; + /* Main path clock users count */ + int main_clk_users[WCD934X_NUM_INTERPOLATORS]; + struct tavil_dsd_config *dsd_config; + struct tavil_idle_detect_config idle_det_cfg; + + int power_active_ref; + u8 sidetone_coeff_array[IIR_MAX][BAND_MAX] + [WCD934X_CDC_SIDETONE_IIR_COEFF_MAX * 4]; + + struct spi_device *spi; + struct platform_device *pdev_child_devices + [WCD934X_CHILD_DEVICES_MAX]; + int child_count; + struct regulator *micb_load; + int micb_load_low; + int micb_load_high; + u8 dmic_drv_ctl; + unsigned short slim_tx_of_uf_cnt[WCD934X_TX_MAX][SB_PORT_ERR_MAX]; +}; + +static const struct tavil_reg_mask_val tavil_spkr_default[] = { + {WCD934X_CDC_COMPANDER7_CTL3, 0x80, 0x80}, + {WCD934X_CDC_COMPANDER8_CTL3, 0x80, 0x80}, + {WCD934X_CDC_COMPANDER7_CTL7, 0x01, 0x01}, + {WCD934X_CDC_COMPANDER8_CTL7, 0x01, 0x01}, + {WCD934X_CDC_BOOST0_BOOST_CTL, 0x7C, 0x58}, + {WCD934X_CDC_BOOST1_BOOST_CTL, 0x7C, 0x58}, +}; + +static const struct tavil_reg_mask_val tavil_spkr_mode1[] = { + {WCD934X_CDC_COMPANDER7_CTL3, 0x80, 0x00}, + {WCD934X_CDC_COMPANDER8_CTL3, 0x80, 0x00}, + {WCD934X_CDC_COMPANDER7_CTL7, 0x01, 0x00}, + {WCD934X_CDC_COMPANDER8_CTL7, 0x01, 0x00}, + {WCD934X_CDC_BOOST0_BOOST_CTL, 0x7C, 0x44}, + {WCD934X_CDC_BOOST1_BOOST_CTL, 0x7C, 0x44}, +}; + +static int __tavil_enable_efuse_sensing(struct tavil_priv *tavil); + +/** + * tavil_set_spkr_gain_offset - offset the speaker path + * gain with the given offset value. + * + * @codec: codec instance + * @offset: Indicates speaker path gain offset value. + * + * Returns 0 on success or -EINVAL on error. + */ +int tavil_set_spkr_gain_offset(struct snd_soc_codec *codec, int offset) +{ + struct tavil_priv *priv = snd_soc_codec_get_drvdata(codec); + + if (!priv) + return -EINVAL; + + priv->swr.spkr_gain_offset = offset; + return 0; +} +EXPORT_SYMBOL(tavil_set_spkr_gain_offset); + +/** + * tavil_set_spkr_mode - Configures speaker compander and smartboost + * settings based on speaker mode. + * + * @codec: codec instance + * @mode: Indicates speaker configuration mode. + * + * Returns 0 on success or -EINVAL on error. + */ +int tavil_set_spkr_mode(struct snd_soc_codec *codec, int mode) +{ + struct tavil_priv *priv = snd_soc_codec_get_drvdata(codec); + int i; + const struct tavil_reg_mask_val *regs; + int size; + + if (!priv) + return -EINVAL; + + switch (mode) { + case WCD934X_SPKR_MODE_1: + regs = tavil_spkr_mode1; + size = ARRAY_SIZE(tavil_spkr_mode1); + break; + default: + regs = tavil_spkr_default; + size = ARRAY_SIZE(tavil_spkr_default); + break; + } + + priv->swr.spkr_mode = mode; + for (i = 0; i < size; i++) + snd_soc_update_bits(codec, regs[i].reg, + regs[i].mask, regs[i].val); + return 0; +} +EXPORT_SYMBOL(tavil_set_spkr_mode); + +/** + * tavil_get_afe_config - returns specific codec configuration to afe to write + * + * @codec: codec instance + * @config_type: Indicates type of configuration to write. + */ +void *tavil_get_afe_config(struct snd_soc_codec *codec, + enum afe_config_type config_type) +{ + struct tavil_priv *priv = snd_soc_codec_get_drvdata(codec); + + switch (config_type) { + case AFE_SLIMBUS_SLAVE_CONFIG: + return &priv->slimbus_slave_cfg; + case AFE_CDC_REGISTERS_CONFIG: + return &tavil_audio_reg_cfg; + case AFE_SLIMBUS_SLAVE_PORT_CONFIG: + return &tavil_slimbus_slave_port_cfg; + case AFE_AANC_VERSION: + return &tavil_cdc_aanc_version; + case AFE_CDC_REGISTER_PAGE_CONFIG: + return &tavil_cdc_reg_page_cfg; + default: + dev_info(codec->dev, "%s: Unknown config_type 0x%x\n", + __func__, config_type); + return NULL; + } +} +EXPORT_SYMBOL(tavil_get_afe_config); + +static bool is_tavil_playback_dai(int dai_id) +{ + if ((dai_id == AIF1_PB) || (dai_id == AIF2_PB) || + (dai_id == AIF3_PB) || (dai_id == AIF4_PB)) + return true; + + return false; +} + +static int tavil_find_playback_dai_id_for_port(int port_id, + struct tavil_priv *tavil) +{ + struct wcd9xxx_codec_dai_data *dai; + struct wcd9xxx_ch *ch; + int i, slv_port_id; + + for (i = AIF1_PB; i < NUM_CODEC_DAIS; i++) { + if (!is_tavil_playback_dai(i)) + continue; + + dai = &tavil->dai[i]; + list_for_each_entry(ch, &dai->wcd9xxx_ch_list, list) { + slv_port_id = wcd9xxx_get_slave_port(ch->ch_num); + if ((slv_port_id > 0) && (slv_port_id == port_id)) + return i; + } + } + + return -EINVAL; +} + +static void tavil_vote_svs(struct tavil_priv *tavil, bool vote) +{ + struct wcd9xxx *wcd9xxx; + + wcd9xxx = tavil->wcd9xxx; + + mutex_lock(&tavil->svs_mutex); + if (vote) { + tavil->svs_ref_cnt++; + if (tavil->svs_ref_cnt == 1) + regmap_update_bits(wcd9xxx->regmap, + WCD934X_CPE_SS_PWR_SYS_PSTATE_CTL_0, + 0x01, 0x01); + } else { + /* Do not decrement ref count if it is already 0 */ + if (tavil->svs_ref_cnt == 0) + goto done; + + tavil->svs_ref_cnt--; + if (tavil->svs_ref_cnt == 0) + regmap_update_bits(wcd9xxx->regmap, + WCD934X_CPE_SS_PWR_SYS_PSTATE_CTL_0, + 0x01, 0x00); + } +done: + dev_dbg(tavil->dev, "%s: vote = %s, updated ref cnt = %u\n", __func__, + vote ? "vote" : "Unvote", tavil->svs_ref_cnt); + mutex_unlock(&tavil->svs_mutex); +} + +static int tavil_get_anc_slot(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = tavil->anc_slot; + return 0; +} + +static int tavil_put_anc_slot(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + + tavil->anc_slot = ucontrol->value.integer.value[0]; + return 0; +} + +static int tavil_get_anc_func(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = (tavil->anc_func == true ? 1 : 0); + return 0; +} + +static int tavil_put_anc_func(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + + mutex_lock(&tavil->codec_mutex); + tavil->anc_func = (!ucontrol->value.integer.value[0] ? false : true); + dev_dbg(codec->dev, "%s: anc_func %x", __func__, tavil->anc_func); + + if (tavil->anc_func == true) { + snd_soc_dapm_enable_pin(dapm, "ANC EAR PA"); + snd_soc_dapm_enable_pin(dapm, "ANC EAR"); + snd_soc_dapm_enable_pin(dapm, "ANC SPK1 PA"); + snd_soc_dapm_enable_pin(dapm, "ANC HPHL PA"); + snd_soc_dapm_enable_pin(dapm, "ANC HPHR PA"); + snd_soc_dapm_enable_pin(dapm, "ANC HPHL"); + snd_soc_dapm_enable_pin(dapm, "ANC HPHR"); + snd_soc_dapm_disable_pin(dapm, "EAR PA"); + snd_soc_dapm_disable_pin(dapm, "EAR"); + snd_soc_dapm_disable_pin(dapm, "HPHL PA"); + snd_soc_dapm_disable_pin(dapm, "HPHR PA"); + snd_soc_dapm_disable_pin(dapm, "HPHL"); + snd_soc_dapm_disable_pin(dapm, "HPHR"); + } else { + snd_soc_dapm_disable_pin(dapm, "ANC EAR PA"); + snd_soc_dapm_disable_pin(dapm, "ANC EAR"); + snd_soc_dapm_disable_pin(dapm, "ANC SPK1 PA"); + snd_soc_dapm_disable_pin(dapm, "ANC HPHL PA"); + snd_soc_dapm_disable_pin(dapm, "ANC HPHR PA"); + snd_soc_dapm_disable_pin(dapm, "ANC HPHL"); + snd_soc_dapm_disable_pin(dapm, "ANC HPHR"); + snd_soc_dapm_enable_pin(dapm, "EAR PA"); + snd_soc_dapm_enable_pin(dapm, "EAR"); + snd_soc_dapm_enable_pin(dapm, "HPHL"); + snd_soc_dapm_enable_pin(dapm, "HPHR"); + snd_soc_dapm_enable_pin(dapm, "HPHL PA"); + snd_soc_dapm_enable_pin(dapm, "HPHR PA"); + } + mutex_unlock(&tavil->codec_mutex); + + snd_soc_dapm_sync(dapm); + return 0; +} + +static int tavil_codec_enable_anc(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + const char *filename; + const struct firmware *fw; + int i; + int ret = 0; + int num_anc_slots; + struct wcd9xxx_anc_header *anc_head; + struct firmware_cal *hwdep_cal = NULL; + u32 anc_writes_size = 0; + int anc_size_remaining; + u32 *anc_ptr; + u16 reg; + u8 mask, val; + size_t cal_size; + const void *data; + + if (!tavil->anc_func) + return 0; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + hwdep_cal = wcdcal_get_fw_cal(tavil->fw_data, WCD9XXX_ANC_CAL); + if (hwdep_cal) { + data = hwdep_cal->data; + cal_size = hwdep_cal->size; + dev_dbg(codec->dev, "%s: using hwdep calibration, cal_size %zd", + __func__, cal_size); + } else { + filename = "WCD934X/WCD934X_anc.bin"; + ret = request_firmware(&fw, filename, codec->dev); + if (ret < 0) { + dev_err(codec->dev, "%s: Failed to acquire ANC data: %d\n", + __func__, ret); + return ret; + } + if (!fw) { + dev_err(codec->dev, "%s: Failed to get anc fw\n", + __func__); + return -ENODEV; + } + data = fw->data; + cal_size = fw->size; + dev_dbg(codec->dev, "%s: using request_firmware calibration\n", + __func__); + } + if (cal_size < sizeof(struct wcd9xxx_anc_header)) { + dev_err(codec->dev, "%s: Invalid cal_size %zd\n", + __func__, cal_size); + ret = -EINVAL; + goto err; + } + /* First number is the number of register writes */ + anc_head = (struct wcd9xxx_anc_header *)(data); + anc_ptr = (u32 *)(data + sizeof(struct wcd9xxx_anc_header)); + anc_size_remaining = cal_size - + sizeof(struct wcd9xxx_anc_header); + num_anc_slots = anc_head->num_anc_slots; + + if (tavil->anc_slot >= num_anc_slots) { + dev_err(codec->dev, "%s: Invalid ANC slot selected\n", + __func__); + ret = -EINVAL; + goto err; + } + for (i = 0; i < num_anc_slots; i++) { + if (anc_size_remaining < WCD934X_PACKED_REG_SIZE) { + dev_err(codec->dev, "%s: Invalid register format\n", + __func__); + ret = -EINVAL; + goto err; + } + anc_writes_size = (u32)(*anc_ptr); + anc_size_remaining -= sizeof(u32); + anc_ptr += 1; + + if ((anc_writes_size * WCD934X_PACKED_REG_SIZE) > + anc_size_remaining) { + dev_err(codec->dev, "%s: Invalid register format\n", + __func__); + ret = -EINVAL; + goto err; + } + + if (tavil->anc_slot == i) + break; + + anc_size_remaining -= (anc_writes_size * + WCD934X_PACKED_REG_SIZE); + anc_ptr += anc_writes_size; + } + if (i == num_anc_slots) { + dev_err(codec->dev, "%s: Selected ANC slot not present\n", + __func__); + ret = -EINVAL; + goto err; + } + + i = 0; + + if (!strcmp(w->name, "RX INT1 DAC") || + !strcmp(w->name, "RX INT3 DAC")) + anc_writes_size = anc_writes_size / 2; + else if (!strcmp(w->name, "RX INT2 DAC") || + !strcmp(w->name, "RX INT4 DAC")) + i = anc_writes_size / 2; + + for (; i < anc_writes_size; i++) { + WCD934X_CODEC_UNPACK_ENTRY(anc_ptr[i], reg, mask, val); + snd_soc_write(codec, reg, (val & mask)); + } + + /* Rate converter clk enable and set bypass mode */ + if (!strcmp(w->name, "RX INT0 DAC") || + !strcmp(w->name, "RX INT1 DAC") || + !strcmp(w->name, "ANC SPK1 PA")) { + snd_soc_update_bits(codec, + WCD934X_CDC_ANC0_RC_COMMON_CTL, + 0x05, 0x05); + if (!strcmp(w->name, "RX INT1 DAC")) { + snd_soc_update_bits(codec, + WCD934X_CDC_ANC0_FIFO_COMMON_CTL, + 0x66, 0x66); + } + } else if (!strcmp(w->name, "RX INT2 DAC")) { + snd_soc_update_bits(codec, + WCD934X_CDC_ANC1_RC_COMMON_CTL, + 0x05, 0x05); + snd_soc_update_bits(codec, + WCD934X_CDC_ANC1_FIFO_COMMON_CTL, + 0x66, 0x66); + } + if (!strcmp(w->name, "RX INT1 DAC")) + snd_soc_update_bits(codec, + WCD934X_CDC_ANC0_CLK_RESET_CTL, 0x08, 0x08); + else if (!strcmp(w->name, "RX INT2 DAC")) + snd_soc_update_bits(codec, + WCD934X_CDC_ANC1_CLK_RESET_CTL, 0x08, 0x08); + + if (!hwdep_cal) + release_firmware(fw); + break; + + case SND_SOC_DAPM_POST_PMU: + if (!strcmp(w->name, "ANC HPHL PA") || + !strcmp(w->name, "ANC HPHR PA")) { + /* Remove ANC Rx from reset */ + snd_soc_update_bits(codec, + WCD934X_CDC_ANC0_CLK_RESET_CTL, + 0x08, 0x00); + snd_soc_update_bits(codec, + WCD934X_CDC_ANC1_CLK_RESET_CTL, + 0x08, 0x00); + } + + break; + + case SND_SOC_DAPM_POST_PMD: + snd_soc_update_bits(codec, WCD934X_CDC_ANC0_RC_COMMON_CTL, + 0x05, 0x00); + if (!strcmp(w->name, "ANC EAR PA") || + !strcmp(w->name, "ANC SPK1 PA") || + !strcmp(w->name, "ANC HPHL PA")) { + snd_soc_update_bits(codec, WCD934X_CDC_ANC0_MODE_1_CTL, + 0x30, 0x00); + msleep(50); + snd_soc_update_bits(codec, WCD934X_CDC_ANC0_MODE_1_CTL, + 0x01, 0x00); + snd_soc_update_bits(codec, + WCD934X_CDC_ANC0_CLK_RESET_CTL, + 0x38, 0x38); + snd_soc_update_bits(codec, + WCD934X_CDC_ANC0_CLK_RESET_CTL, + 0x07, 0x00); + snd_soc_update_bits(codec, + WCD934X_CDC_ANC0_CLK_RESET_CTL, + 0x38, 0x00); + } else if (!strcmp(w->name, "ANC HPHR PA")) { + snd_soc_update_bits(codec, WCD934X_CDC_ANC1_MODE_1_CTL, + 0x30, 0x00); + msleep(50); + snd_soc_update_bits(codec, WCD934X_CDC_ANC1_MODE_1_CTL, + 0x01, 0x00); + snd_soc_update_bits(codec, + WCD934X_CDC_ANC1_CLK_RESET_CTL, + 0x38, 0x38); + snd_soc_update_bits(codec, + WCD934X_CDC_ANC1_CLK_RESET_CTL, + 0x07, 0x00); + snd_soc_update_bits(codec, + WCD934X_CDC_ANC1_CLK_RESET_CTL, + 0x38, 0x00); + } + break; + } + + return 0; +err: + if (!hwdep_cal) + release_firmware(fw); + return ret; +} + +static int tavil_get_clkmode(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tavil_priv *tavil_p = snd_soc_codec_get_drvdata(codec); + + if (test_bit(CLK_MODE, &tavil_p->status_mask)) + ucontrol->value.enumerated.item[0] = 1; + else + ucontrol->value.enumerated.item[0] = 0; + + dev_dbg(codec->dev, "%s: is_low_power_clock: %s\n", __func__, + test_bit(CLK_MODE, &tavil_p->status_mask) ? "true" : "false"); + + return 0; +} + +static int tavil_put_clkmode(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tavil_priv *tavil_p = snd_soc_codec_get_drvdata(codec); + + if (ucontrol->value.enumerated.item[0]) + set_bit(CLK_MODE, &tavil_p->status_mask); + else + clear_bit(CLK_MODE, &tavil_p->status_mask); + + dev_dbg(codec->dev, "%s: is_low_power_clock: %s\n", __func__, + test_bit(CLK_MODE, &tavil_p->status_mask) ? "true" : "false"); + + return 0; +} + +static int tavil_vi_feed_mixer_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm); + struct tavil_priv *tavil_p = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = tavil_p->vi_feed_value; + + return 0; +} + +static int tavil_vi_feed_mixer_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm); + struct tavil_priv *tavil_p = snd_soc_codec_get_drvdata(codec); + struct wcd9xxx *core = dev_get_drvdata(codec->dev->parent); + struct soc_multi_mixer_control *mixer = + ((struct soc_multi_mixer_control *)kcontrol->private_value); + u32 dai_id = widget->shift; + u32 port_id = mixer->shift; + u32 enable = ucontrol->value.integer.value[0]; + + dev_dbg(codec->dev, "%s: enable: %d, port_id:%d, dai_id: %d\n", + __func__, enable, port_id, dai_id); + + tavil_p->vi_feed_value = ucontrol->value.integer.value[0]; + + mutex_lock(&tavil_p->codec_mutex); + if (enable) { + if (port_id == WCD934X_TX14 && !test_bit(VI_SENSE_1, + &tavil_p->status_mask)) { + list_add_tail(&core->tx_chs[WCD934X_TX14].list, + &tavil_p->dai[dai_id].wcd9xxx_ch_list); + set_bit(VI_SENSE_1, &tavil_p->status_mask); + } + if (port_id == WCD934X_TX15 && !test_bit(VI_SENSE_2, + &tavil_p->status_mask)) { + list_add_tail(&core->tx_chs[WCD934X_TX15].list, + &tavil_p->dai[dai_id].wcd9xxx_ch_list); + set_bit(VI_SENSE_2, &tavil_p->status_mask); + } + } else { + if (port_id == WCD934X_TX14 && test_bit(VI_SENSE_1, + &tavil_p->status_mask)) { + list_del_init(&core->tx_chs[WCD934X_TX14].list); + clear_bit(VI_SENSE_1, &tavil_p->status_mask); + } + if (port_id == WCD934X_TX15 && test_bit(VI_SENSE_2, + &tavil_p->status_mask)) { + list_del_init(&core->tx_chs[WCD934X_TX15].list); + clear_bit(VI_SENSE_2, &tavil_p->status_mask); + } + } + mutex_unlock(&tavil_p->codec_mutex); + snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, enable, NULL); + + return 0; +} + +static int slim_tx_mixer_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm); + struct tavil_priv *tavil_p = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = tavil_p->tx_port_value; + return 0; +} + +static int slim_tx_mixer_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm); + struct tavil_priv *tavil_p = snd_soc_codec_get_drvdata(codec); + struct wcd9xxx *core = dev_get_drvdata(codec->dev->parent); + struct snd_soc_dapm_update *update = NULL; + struct soc_multi_mixer_control *mixer = + ((struct soc_multi_mixer_control *)kcontrol->private_value); + u32 dai_id = widget->shift; + u32 port_id = mixer->shift; + u32 enable = ucontrol->value.integer.value[0]; + u32 vtable; + + dev_dbg(codec->dev, "%s: wname %s cname %s value %u shift %d item %ld\n", + __func__, + widget->name, ucontrol->id.name, tavil_p->tx_port_value, + widget->shift, ucontrol->value.integer.value[0]); + + mutex_lock(&tavil_p->codec_mutex); + if (dai_id >= ARRAY_SIZE(vport_slim_check_table)) { + dev_err(codec->dev, "%s: dai_id: %d, out of bounds\n", + __func__, dai_id); + mutex_unlock(&tavil_p->codec_mutex); + return -EINVAL; + } + vtable = vport_slim_check_table[dai_id]; + + switch (dai_id) { + case AIF1_CAP: + case AIF2_CAP: + case AIF3_CAP: + /* only add to the list if value not set */ + if (enable && !(tavil_p->tx_port_value & 1 << port_id)) { + if (wcd9xxx_tx_vport_validation(vtable, port_id, + tavil_p->dai, NUM_CODEC_DAIS)) { + dev_dbg(codec->dev, "%s: TX%u is used by other virtual port\n", + __func__, port_id); + mutex_unlock(&tavil_p->codec_mutex); + return 0; + } + tavil_p->tx_port_value |= 1 << port_id; + list_add_tail(&core->tx_chs[port_id].list, + &tavil_p->dai[dai_id].wcd9xxx_ch_list); + } else if (!enable && (tavil_p->tx_port_value & + 1 << port_id)) { + tavil_p->tx_port_value &= ~(1 << port_id); + list_del_init(&core->tx_chs[port_id].list); + } else { + if (enable) + dev_dbg(codec->dev, "%s: TX%u port is used by\n" + "this virtual port\n", + __func__, port_id); + else + dev_dbg(codec->dev, "%s: TX%u port is not used by\n" + "this virtual port\n", + __func__, port_id); + /* avoid update power function */ + mutex_unlock(&tavil_p->codec_mutex); + return 0; + } + break; + case AIF4_MAD_TX: + break; + default: + dev_err(codec->dev, "Unknown AIF %d\n", dai_id); + mutex_unlock(&tavil_p->codec_mutex); + return -EINVAL; + } + dev_dbg(codec->dev, "%s: name %s sname %s updated value %u shift %d\n", + __func__, widget->name, widget->sname, tavil_p->tx_port_value, + widget->shift); + + mutex_unlock(&tavil_p->codec_mutex); + snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, enable, update); + + return 0; +} + +static int i2s_tx_mixer_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm); + struct tavil_priv *tavil_p = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = tavil_p->tx_port_value; + return 0; +} + +static int i2s_tx_mixer_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm); + struct tavil_priv *tavil_p = snd_soc_codec_get_drvdata(codec); + struct snd_soc_dapm_update *update = NULL; + struct soc_multi_mixer_control *mixer = + (struct soc_multi_mixer_control *)kcontrol->private_value; + u32 dai_id = widget->shift; + u32 port_id = mixer->shift; + u32 enable = ucontrol->value.integer.value[0]; + u32 vtable; + + dev_dbg(codec->dev, "%s: wname %s cname %s value %u shift %d item %ld\n", + __func__, + widget->name, ucontrol->id.name, tavil_p->tx_port_value, + widget->shift, ucontrol->value.integer.value[0]); + + mutex_lock(&tavil_p->codec_mutex); + if (dai_id >= ARRAY_SIZE(vport_slim_check_table)) { + dev_err(codec->dev, "%s: dai_id: %d, out of bounds\n", + __func__, dai_id); + mutex_unlock(&tavil_p->codec_mutex); + return -EINVAL; + } + vtable = vport_slim_check_table[dai_id]; + + switch (dai_id) { + case AIF1_CAP: + case AIF2_CAP: + case AIF3_CAP: + /* only add to the list if value not set */ + if (enable && !(tavil_p->tx_port_value & 1 << port_id)) { + if (wcd9xxx_tx_vport_validation(vtable, port_id, + tavil_p->dai, NUM_CODEC_DAIS)) { + dev_dbg(codec->dev, "%s: TX%u is used by other virtual port\n", + __func__, port_id); + mutex_unlock(&tavil_p->codec_mutex); + return 0; + } + tavil_p->tx_port_value |= 1 << port_id; + } else if (!enable && (tavil_p->tx_port_value & + 1 << port_id)) { + tavil_p->tx_port_value &= ~(1 << port_id); + } else { + if (enable) + dev_dbg(codec->dev, "%s: TX%u port is used by\n" + "this virtual port\n", + __func__, port_id); + else + dev_dbg(codec->dev, "%s: TX%u port is not used by\n" + "this virtual port\n", + __func__, port_id); + /* avoid update power function */ + mutex_unlock(&tavil_p->codec_mutex); + return 0; + } + break; + default: + dev_err(codec->dev, "Unknown AIF %d\n", dai_id); + mutex_unlock(&tavil_p->codec_mutex); + return -EINVAL; + } + dev_dbg(codec->dev, "%s: name %s sname %s updated value %u shift %d\n", + __func__, widget->name, widget->sname, tavil_p->tx_port_value, + widget->shift); + + mutex_unlock(&tavil_p->codec_mutex); + snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, enable, update); + + return 0; +} + +static int slim_rx_mux_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm); + struct tavil_priv *tavil_p = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.enumerated.item[0] = + tavil_p->rx_port_value[widget->shift]; + return 0; +} + +static int slim_rx_mux_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm); + struct tavil_priv *tavil_p = snd_soc_codec_get_drvdata(codec); + struct wcd9xxx *core = dev_get_drvdata(codec->dev->parent); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + struct snd_soc_dapm_update *update = NULL; + unsigned int rx_port_value; + u32 port_id = widget->shift; + + tavil_p->rx_port_value[port_id] = ucontrol->value.enumerated.item[0]; + rx_port_value = tavil_p->rx_port_value[port_id]; + + mutex_lock(&tavil_p->codec_mutex); + dev_dbg(codec->dev, "%s: wname %s cname %s value %u shift %d item %ld\n", + __func__, widget->name, ucontrol->id.name, + rx_port_value, widget->shift, + ucontrol->value.integer.value[0]); + + /* value need to match the Virtual port and AIF number */ + switch (rx_port_value) { + case 0: + list_del_init(&core->rx_chs[port_id].list); + break; + case 1: + if (wcd9xxx_rx_vport_validation(port_id + + WCD934X_RX_PORT_START_NUMBER, + &tavil_p->dai[AIF1_PB].wcd9xxx_ch_list)) { + dev_dbg(codec->dev, "%s: RX%u is used by current requesting AIF_PB itself\n", + __func__, port_id); + goto rtn; + } + list_add_tail(&core->rx_chs[port_id].list, + &tavil_p->dai[AIF1_PB].wcd9xxx_ch_list); + break; + case 2: + if (wcd9xxx_rx_vport_validation(port_id + + WCD934X_RX_PORT_START_NUMBER, + &tavil_p->dai[AIF2_PB].wcd9xxx_ch_list)) { + dev_dbg(codec->dev, "%s: RX%u is used by current requesting AIF_PB itself\n", + __func__, port_id); + goto rtn; + } + list_add_tail(&core->rx_chs[port_id].list, + &tavil_p->dai[AIF2_PB].wcd9xxx_ch_list); + break; + case 3: + if (wcd9xxx_rx_vport_validation(port_id + + WCD934X_RX_PORT_START_NUMBER, + &tavil_p->dai[AIF3_PB].wcd9xxx_ch_list)) { + dev_dbg(codec->dev, "%s: RX%u is used by current requesting AIF_PB itself\n", + __func__, port_id); + goto rtn; + } + list_add_tail(&core->rx_chs[port_id].list, + &tavil_p->dai[AIF3_PB].wcd9xxx_ch_list); + break; + case 4: + if (wcd9xxx_rx_vport_validation(port_id + + WCD934X_RX_PORT_START_NUMBER, + &tavil_p->dai[AIF4_PB].wcd9xxx_ch_list)) { + dev_dbg(codec->dev, "%s: RX%u is used by current requesting AIF_PB itself\n", + __func__, port_id); + goto rtn; + } + list_add_tail(&core->rx_chs[port_id].list, + &tavil_p->dai[AIF4_PB].wcd9xxx_ch_list); + break; + default: + dev_err(codec->dev, "Unknown AIF %d\n", rx_port_value); + goto err; + } +rtn: + mutex_unlock(&tavil_p->codec_mutex); + snd_soc_dapm_mux_update_power(widget->dapm, kcontrol, + rx_port_value, e, update); + + return 0; +err: + mutex_unlock(&tavil_p->codec_mutex); + return -EINVAL; +} + +static void tavil_codec_enable_slim_port_intr( + struct wcd9xxx_codec_dai_data *dai, + struct snd_soc_codec *codec) +{ + struct wcd9xxx_ch *ch; + int port_num = 0; + unsigned short reg = 0; + u8 val = 0; + struct tavil_priv *tavil_p; + + if (!dai || !codec) { + pr_err("%s: Invalid params\n", __func__); + return; + } + + tavil_p = snd_soc_codec_get_drvdata(codec); + list_for_each_entry(ch, &dai->wcd9xxx_ch_list, list) { + if (ch->port >= WCD934X_RX_PORT_START_NUMBER) { + port_num = ch->port - WCD934X_RX_PORT_START_NUMBER; + reg = WCD934X_SLIM_PGD_PORT_INT_RX_EN0 + (port_num / 8); + val = wcd9xxx_interface_reg_read(tavil_p->wcd9xxx, + reg); + if (!(val & BYTE_BIT_MASK(port_num))) { + val |= BYTE_BIT_MASK(port_num); + wcd9xxx_interface_reg_write( + tavil_p->wcd9xxx, reg, val); + val = wcd9xxx_interface_reg_read( + tavil_p->wcd9xxx, reg); + } + } else { + port_num = ch->port; + reg = WCD934X_SLIM_PGD_PORT_INT_TX_EN0 + (port_num / 8); + val = wcd9xxx_interface_reg_read(tavil_p->wcd9xxx, + reg); + if (!(val & BYTE_BIT_MASK(port_num))) { + val |= BYTE_BIT_MASK(port_num); + wcd9xxx_interface_reg_write(tavil_p->wcd9xxx, + reg, val); + val = wcd9xxx_interface_reg_read( + tavil_p->wcd9xxx, reg); + } + } + } +} + +static int tavil_codec_enable_slim_chmask(struct wcd9xxx_codec_dai_data *dai, + bool up) +{ + int ret = 0; + struct wcd9xxx_ch *ch; + + if (up) { + list_for_each_entry(ch, &dai->wcd9xxx_ch_list, list) { + ret = wcd9xxx_get_slave_port(ch->ch_num); + if (ret < 0) { + pr_err("%s: Invalid slave port ID: %d\n", + __func__, ret); + ret = -EINVAL; + } else { + set_bit(ret, &dai->ch_mask); + } + } + } else { + ret = wait_event_timeout(dai->dai_wait, (dai->ch_mask == 0), + msecs_to_jiffies( + WCD934X_SLIM_CLOSE_TIMEOUT)); + if (!ret) { + pr_err("%s: Slim close tx/rx wait timeout, ch_mask:0x%lx\n", + __func__, dai->ch_mask); + ret = -ETIMEDOUT; + } else { + ret = 0; + } + } + return ret; +} + +static void tavil_codec_mute_dsd(struct snd_soc_codec *codec, + struct list_head *ch_list) +{ + u8 dsd0_in; + u8 dsd1_in; + struct wcd9xxx_ch *ch; + + /* Read DSD Input Ports */ + dsd0_in = (snd_soc_read(codec, WCD934X_CDC_DSD0_CFG0) & 0x3C) >> 2; + dsd1_in = (snd_soc_read(codec, WCD934X_CDC_DSD1_CFG0) & 0x3C) >> 2; + + if ((dsd0_in == 0) && (dsd1_in == 0)) + return; + + /* + * Check if the ports getting disabled are connected to DSD inputs. + * If connected, enable DSD mute to avoid DC entering into DSD Filter + */ + list_for_each_entry(ch, ch_list, list) { + if (ch->port == (dsd0_in + WCD934X_RX_PORT_START_NUMBER - 1)) + snd_soc_update_bits(codec, WCD934X_CDC_DSD0_CFG2, + 0x04, 0x04); + if (ch->port == (dsd1_in + WCD934X_RX_PORT_START_NUMBER - 1)) + snd_soc_update_bits(codec, WCD934X_CDC_DSD1_CFG2, + 0x04, 0x04); + } +} + +static int tavil_codec_set_i2s_rx_ch(struct snd_soc_dapm_widget *w, + u32 i2s_reg, bool up) +{ + int rx_fs_rate = -EINVAL; + int i2s_bit_mode; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tavil_priv *tavil_p = snd_soc_codec_get_drvdata(codec); + struct wcd9xxx_codec_dai_data *dai; + + dai = &tavil_p->dai[w->shift]; + dev_dbg(tavil_p->dev, "%s: %d up/down, %d width, %d rate\n", + __func__, up, dai->bit_width, dai->rate); + if (up) { + if (dai->bit_width == 16) + i2s_bit_mode = 0x01; + else + i2s_bit_mode = 0x00; + + switch (dai->rate) { + case 8000: + rx_fs_rate = 0; + break; + case 16000: + rx_fs_rate = 1; + break; + case 32000: + rx_fs_rate = 2; + break; + case 48000: + rx_fs_rate = 3; + break; + case 96000: + rx_fs_rate = 4; + break; + case 192000: + rx_fs_rate = 5; + break; + case 384000: + rx_fs_rate = 6; + break; + default: + dev_err(tavil_p->dev, "%s: Invalid RX sample rate: %d\n", + __func__, dai->rate); + return -EINVAL; + }; + snd_soc_update_bits(codec, i2s_reg, + 0x40, i2s_bit_mode << 6); + snd_soc_update_bits(codec, i2s_reg, + 0x3c, (rx_fs_rate << 2)); + } else { + snd_soc_update_bits(codec, i2s_reg, + 0x40, 0x00); + snd_soc_update_bits(codec, i2s_reg, + 0x3c, 0x00); + } + return 0; +} + +static int tavil_codec_set_i2s_tx_ch(struct snd_soc_dapm_widget *w, + u32 i2s_reg, bool up) +{ + int tx_fs_rate = -EINVAL; + int i2s_bit_mode; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tavil_priv *tavil_p = snd_soc_codec_get_drvdata(codec); + struct wcd9xxx_codec_dai_data *dai; + + dai = &tavil_p->dai[w->shift]; + if (up) { + if (dai->bit_width == 16) + i2s_bit_mode = 0x01; + else + i2s_bit_mode = 0x00; + + snd_soc_update_bits(codec, i2s_reg, 0x40, i2s_bit_mode << 6); + + switch (dai->rate) { + case 8000: + tx_fs_rate = 0; + break; + case 16000: + tx_fs_rate = 1; + break; + case 32000: + tx_fs_rate = 2; + break; + case 48000: + tx_fs_rate = 3; + break; + case 96000: + tx_fs_rate = 4; + break; + case 192000: + tx_fs_rate = 5; + break; + case 384000: + tx_fs_rate = 6; + break; + default: + dev_err(tavil_p->dev, "%s: Invalid sample rate: %d\n", + __func__, dai->rate); + return -EINVAL; + }; + + snd_soc_update_bits(codec, i2s_reg, 0x3c, tx_fs_rate << 2); + + snd_soc_update_bits(codec, + WCD934X_DATA_HUB_I2S_TX0_CFG, + 0x03, 0x01); + + snd_soc_update_bits(codec, + WCD934X_DATA_HUB_I2S_TX0_CFG, + 0x0C, 0x01); + + snd_soc_update_bits(codec, + WCD934X_DATA_HUB_I2S_TX1_0_CFG, + 0x03, 0x01); + + snd_soc_update_bits(codec, + WCD934X_DATA_HUB_I2S_TX1_1_CFG, + 0x05, 0x05); + } else { + snd_soc_update_bits(codec, i2s_reg, 0x40, 0x00); + snd_soc_update_bits(codec, i2s_reg, 0x3c, 0x00); + + snd_soc_update_bits(codec, + WCD934X_DATA_HUB_I2S_TX0_CFG, + 0x03, 0x00); + + snd_soc_update_bits(codec, + WCD934X_DATA_HUB_I2S_TX0_CFG, + 0x0C, 0x00); + + snd_soc_update_bits(codec, + WCD934X_DATA_HUB_I2S_TX1_0_CFG, + 0x03, 0x00); + + snd_soc_update_bits(codec, + WCD934X_DATA_HUB_I2S_TX1_1_CFG, + 0x05, 0x00); + } + return 0; +} + +static int tavil_codec_enable_rx_i2c(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tavil_priv *tavil_p = snd_soc_codec_get_drvdata(codec); + int ret = -EINVAL; + u32 i2s_reg; + + switch (tavil_p->rx_port_value[w->shift]) { + case AIF1_PB: + case AIF1_CAP: + i2s_reg = WCD934X_DATA_HUB_I2S_0_CTL; + break; + case AIF2_PB: + case AIF2_CAP: + i2s_reg = WCD934X_DATA_HUB_I2S_1_CTL; + break; + case AIF3_PB: + case AIF3_CAP: + i2s_reg = WCD934X_DATA_HUB_I2S_2_CTL; + break; + default: + dev_err(codec->dev, "%s Invalid i2s Id received", __func__); + return -EINVAL; + } + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + ret = tavil_codec_set_i2s_rx_ch(w, i2s_reg, true); + break; + case SND_SOC_DAPM_POST_PMD: + ret = tavil_codec_set_i2s_rx_ch(w, i2s_reg, false); + break; + } + + return ret; +} + +static int tavil_codec_enable_rx(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct wcd9xxx *core; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tavil_priv *tavil_p = snd_soc_codec_get_drvdata(codec); + int ret = 0; + struct wcd9xxx_codec_dai_data *dai; + struct tavil_dsd_config *dsd_conf = tavil_p->dsd_config; + + core = dev_get_drvdata(codec->dev->parent); + + dev_dbg(codec->dev, "%s: event called! codec name %s num_dai %d\n" + "stream name %s event %d\n", + __func__, codec->component.name, + codec->component.num_dai, w->sname, event); + + dai = &tavil_p->dai[w->shift]; + dev_dbg(codec->dev, "%s: w->name %s w->shift %d event %d\n", + __func__, w->name, w->shift, event); + + if (tavil_p->intf_type == WCD9XXX_INTERFACE_TYPE_I2C) { + ret = tavil_codec_enable_rx_i2c(w, kcontrol, event); + return ret; + } + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + dai->bus_down_in_recovery = false; + tavil_codec_enable_slim_port_intr(dai, codec); + (void) tavil_codec_enable_slim_chmask(dai, true); + ret = wcd9xxx_cfg_slim_sch_rx(core, &dai->wcd9xxx_ch_list, + dai->rate, dai->bit_width, + &dai->grph); + break; + case SND_SOC_DAPM_POST_PMD: + if (dsd_conf) + tavil_codec_mute_dsd(codec, &dai->wcd9xxx_ch_list); + + ret = wcd9xxx_disconnect_port(core, &dai->wcd9xxx_ch_list, + dai->grph); + dev_dbg(codec->dev, "%s: Disconnect RX port, ret = %d\n", + __func__, ret); + + if (!dai->bus_down_in_recovery) + ret = tavil_codec_enable_slim_chmask(dai, false); + else + dev_dbg(codec->dev, + "%s: bus in recovery skip enable slim_chmask", + __func__); + ret = wcd9xxx_close_slim_sch_rx(core, &dai->wcd9xxx_ch_list, + dai->grph); + break; + } + return ret; +} + +static int tavil_codec_enable_tx_i2c(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tavil_priv *tavil_p = snd_soc_codec_get_drvdata(codec); + int ret = -EINVAL; + u32 i2s_reg; + + switch (tavil_p->rx_port_value[w->shift]) { + case AIF1_PB: + case AIF1_CAP: + i2s_reg = WCD934X_DATA_HUB_I2S_0_CTL; + break; + case AIF2_PB: + case AIF2_CAP: + i2s_reg = WCD934X_DATA_HUB_I2S_1_CTL; + break; + case AIF3_PB: + case AIF3_CAP: + i2s_reg = WCD934X_DATA_HUB_I2S_2_CTL; + break; + default: + dev_err(codec->dev, "%s Invalid i2s Id received", __func__); + return -EINVAL; + } + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + ret = tavil_codec_set_i2s_tx_ch(w, i2s_reg, true); + break; + case SND_SOC_DAPM_POST_PMD: + ret = tavil_codec_set_i2s_tx_ch(w, i2s_reg, false); + break; + } + + return ret; +} + +static int tavil_codec_enable_tx(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tavil_priv *tavil_p = snd_soc_codec_get_drvdata(codec); + struct wcd9xxx_codec_dai_data *dai; + struct wcd9xxx *core; + struct wcd9xxx_ch *ch; + int ret = 0; + + dev_dbg(codec->dev, + "%s: w->name %s, w->shift = %d, num_dai %d stream name %s\n", + __func__, w->name, w->shift, + codec->component.num_dai, w->sname); + + dai = &tavil_p->dai[w->shift]; + core = dev_get_drvdata(codec->dev->parent); + + if (tavil_p->intf_type == WCD9XXX_INTERFACE_TYPE_I2C) { + ret = tavil_codec_enable_tx_i2c(w, kcontrol, event); + return ret; + } + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + dai->bus_down_in_recovery = false; + tavil_codec_enable_slim_port_intr(dai, codec); + (void) tavil_codec_enable_slim_chmask(dai, true); + ret = wcd9xxx_cfg_slim_sch_tx(core, &dai->wcd9xxx_ch_list, + dai->rate, dai->bit_width, + &dai->grph); + break; + case SND_SOC_DAPM_POST_PMD: + ret = wcd9xxx_close_slim_sch_tx(core, &dai->wcd9xxx_ch_list, + dai->grph); + if (!dai->bus_down_in_recovery) + ret = tavil_codec_enable_slim_chmask(dai, false); + if (ret < 0) { + ret = wcd9xxx_disconnect_port(core, + &dai->wcd9xxx_ch_list, + dai->grph); + dev_dbg(codec->dev, "%s: Disconnect RX port, ret = %d\n", + __func__, ret); + } + /* reset overflow and underflow counts */ + list_for_each_entry(ch, &dai->wcd9xxx_ch_list, list) { + tavil_p->slim_tx_of_uf_cnt[ch->port][SB_PORT_ERR_OF] + = 0; + tavil_p->slim_tx_of_uf_cnt[ch->port][SB_PORT_ERR_UF] + = 0; + } + break; + } + return ret; +} + +static int tavil_codec_enable_slimvi_feedback(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct wcd9xxx *core = NULL; + struct snd_soc_codec *codec = NULL; + struct tavil_priv *tavil_p = NULL; + struct wcd9xxx_ch *ch; + int ret = 0; + struct wcd9xxx_codec_dai_data *dai = NULL; + + codec = snd_soc_dapm_to_codec(w->dapm); + tavil_p = snd_soc_codec_get_drvdata(codec); + core = dev_get_drvdata(codec->dev->parent); + + dev_dbg(codec->dev, + "%s: num_dai %d stream name %s w->name %s event %d shift %d\n", + __func__, codec->component.num_dai, w->sname, + w->name, event, w->shift); + + if (w->shift != AIF4_VIFEED) { + pr_err("%s Error in enabling the tx path\n", __func__); + ret = -EINVAL; + goto done; + } + dai = &tavil_p->dai[w->shift]; + switch (event) { + case SND_SOC_DAPM_POST_PMU: + if (test_bit(VI_SENSE_1, &tavil_p->status_mask)) { + dev_dbg(codec->dev, "%s: spkr1 enabled\n", __func__); + /* Enable V&I sensing */ + snd_soc_update_bits(codec, + WCD934X_CDC_TX9_SPKR_PROT_PATH_CTL, 0x20, 0x20); + snd_soc_update_bits(codec, + WCD934X_CDC_TX10_SPKR_PROT_PATH_CTL, 0x20, + 0x20); + snd_soc_update_bits(codec, + WCD934X_CDC_TX9_SPKR_PROT_PATH_CTL, 0x0F, 0x00); + snd_soc_update_bits(codec, + WCD934X_CDC_TX10_SPKR_PROT_PATH_CTL, 0x0F, + 0x00); + snd_soc_update_bits(codec, + WCD934X_CDC_TX9_SPKR_PROT_PATH_CTL, 0x10, 0x10); + snd_soc_update_bits(codec, + WCD934X_CDC_TX10_SPKR_PROT_PATH_CTL, 0x10, + 0x10); + snd_soc_update_bits(codec, + WCD934X_CDC_TX9_SPKR_PROT_PATH_CTL, 0x20, 0x00); + snd_soc_update_bits(codec, + WCD934X_CDC_TX10_SPKR_PROT_PATH_CTL, 0x20, + 0x00); + } + if (test_bit(VI_SENSE_2, &tavil_p->status_mask)) { + pr_debug("%s: spkr2 enabled\n", __func__); + /* Enable V&I sensing */ + snd_soc_update_bits(codec, + WCD934X_CDC_TX11_SPKR_PROT_PATH_CTL, 0x20, + 0x20); + snd_soc_update_bits(codec, + WCD934X_CDC_TX12_SPKR_PROT_PATH_CTL, 0x20, + 0x20); + snd_soc_update_bits(codec, + WCD934X_CDC_TX11_SPKR_PROT_PATH_CTL, 0x0F, + 0x00); + snd_soc_update_bits(codec, + WCD934X_CDC_TX12_SPKR_PROT_PATH_CTL, 0x0F, + 0x00); + snd_soc_update_bits(codec, + WCD934X_CDC_TX11_SPKR_PROT_PATH_CTL, 0x10, + 0x10); + snd_soc_update_bits(codec, + WCD934X_CDC_TX12_SPKR_PROT_PATH_CTL, 0x10, + 0x10); + snd_soc_update_bits(codec, + WCD934X_CDC_TX11_SPKR_PROT_PATH_CTL, 0x20, + 0x00); + snd_soc_update_bits(codec, + WCD934X_CDC_TX12_SPKR_PROT_PATH_CTL, 0x20, + 0x00); + } + dai->bus_down_in_recovery = false; + tavil_codec_enable_slim_port_intr(dai, codec); + (void) tavil_codec_enable_slim_chmask(dai, true); + ret = wcd9xxx_cfg_slim_sch_tx(core, &dai->wcd9xxx_ch_list, + dai->rate, dai->bit_width, + &dai->grph); + break; + case SND_SOC_DAPM_POST_PMD: + ret = wcd9xxx_close_slim_sch_tx(core, &dai->wcd9xxx_ch_list, + dai->grph); + if (ret) + dev_err(codec->dev, "%s error in close_slim_sch_tx %d\n", + __func__, ret); + if (!dai->bus_down_in_recovery) + ret = tavil_codec_enable_slim_chmask(dai, false); + if (ret < 0) { + ret = wcd9xxx_disconnect_port(core, + &dai->wcd9xxx_ch_list, + dai->grph); + dev_dbg(codec->dev, "%s: Disconnect TX port, ret = %d\n", + __func__, ret); + } + /* reset over.f and under.f counts */ + list_for_each_entry(ch, &dai->wcd9xxx_ch_list, list) { + tavil_p->slim_tx_of_uf_cnt[ch->port][SB_PORT_ERR_OF] + = 0; + tavil_p->slim_tx_of_uf_cnt[ch->port][SB_PORT_ERR_UF] + = 0; + } + if (test_bit(VI_SENSE_1, &tavil_p->status_mask)) { + /* Disable V&I sensing */ + dev_dbg(codec->dev, "%s: spkr1 disabled\n", __func__); + snd_soc_update_bits(codec, + WCD934X_CDC_TX9_SPKR_PROT_PATH_CTL, 0x20, 0x20); + snd_soc_update_bits(codec, + WCD934X_CDC_TX10_SPKR_PROT_PATH_CTL, 0x20, + 0x20); + snd_soc_update_bits(codec, + WCD934X_CDC_TX9_SPKR_PROT_PATH_CTL, 0x10, 0x00); + snd_soc_update_bits(codec, + WCD934X_CDC_TX10_SPKR_PROT_PATH_CTL, 0x10, + 0x00); + } + if (test_bit(VI_SENSE_2, &tavil_p->status_mask)) { + /* Disable V&I sensing */ + dev_dbg(codec->dev, "%s: spkr2 disabled\n", __func__); + snd_soc_update_bits(codec, + WCD934X_CDC_TX11_SPKR_PROT_PATH_CTL, 0x20, + 0x20); + snd_soc_update_bits(codec, + WCD934X_CDC_TX12_SPKR_PROT_PATH_CTL, 0x20, + 0x20); + snd_soc_update_bits(codec, + WCD934X_CDC_TX11_SPKR_PROT_PATH_CTL, 0x10, + 0x00); + snd_soc_update_bits(codec, + WCD934X_CDC_TX12_SPKR_PROT_PATH_CTL, 0x10, + 0x00); + } + break; + } +done: + return ret; +} + +static int tavil_codec_enable_rx_bias(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + tavil->rx_bias_count++; + if (tavil->rx_bias_count == 1) { + snd_soc_update_bits(codec, WCD934X_ANA_RX_SUPPLIES, + 0x01, 0x01); + } + break; + case SND_SOC_DAPM_POST_PMD: + tavil->rx_bias_count--; + if (!tavil->rx_bias_count) + snd_soc_update_bits(codec, WCD934X_ANA_RX_SUPPLIES, + 0x01, 0x00); + break; + }; + dev_dbg(codec->dev, "%s: Current RX BIAS user count: %d\n", __func__, + tavil->rx_bias_count); + + return 0; +} + +static void tavil_spk_anc_update_callback(struct work_struct *work) +{ + struct spk_anc_work *spk_anc_dwork; + struct tavil_priv *tavil; + struct delayed_work *delayed_work; + struct snd_soc_codec *codec; + + delayed_work = to_delayed_work(work); + spk_anc_dwork = container_of(delayed_work, struct spk_anc_work, dwork); + tavil = spk_anc_dwork->tavil; + codec = tavil->codec; + + snd_soc_update_bits(codec, WCD934X_CDC_RX7_RX_PATH_CFG0, 0x10, 0x10); +} + +static int tavil_codec_enable_spkr_anc(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + int ret = 0; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + + if (!tavil->anc_func) + return 0; + + dev_dbg(codec->dev, "%s: w: %s event: %d anc: %d\n", __func__, + w->name, event, tavil->anc_func); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + ret = tavil_codec_enable_anc(w, kcontrol, event); + schedule_delayed_work(&tavil->spk_anc_dwork.dwork, + msecs_to_jiffies(spk_anc_en_delay)); + break; + case SND_SOC_DAPM_POST_PMD: + cancel_delayed_work_sync(&tavil->spk_anc_dwork.dwork); + snd_soc_update_bits(codec, WCD934X_CDC_RX7_RX_PATH_CFG0, + 0x10, 0x00); + ret = tavil_codec_enable_anc(w, kcontrol, event); + break; + } + return ret; +} + +static int tavil_codec_enable_ear_pa(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + int ret = 0; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + + dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + /* + * 5ms sleep is required after PA is enabled as per + * HW requirement + */ + usleep_range(5000, 5500); + snd_soc_update_bits(codec, WCD934X_CDC_RX0_RX_PATH_CTL, + 0x10, 0x00); + /* Remove mix path mute if it is enabled */ + if ((snd_soc_read(codec, WCD934X_CDC_RX0_RX_PATH_MIX_CTL)) & + 0x10) + snd_soc_update_bits(codec, + WCD934X_CDC_RX0_RX_PATH_MIX_CTL, + 0x10, 0x00); + break; + case SND_SOC_DAPM_POST_PMD: + /* + * 5ms sleep is required after PA is disabled as per + * HW requirement + */ + usleep_range(5000, 5500); + + if (!(strcmp(w->name, "ANC EAR PA"))) { + ret = tavil_codec_enable_anc(w, kcontrol, event); + snd_soc_update_bits(codec, WCD934X_CDC_RX0_RX_PATH_CFG0, + 0x10, 0x00); + } + break; + }; + + return ret; +} + +static void tavil_codec_override(struct snd_soc_codec *codec, int mode, + int event) +{ + if (mode == CLS_AB || mode == CLS_AB_HIFI) { + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + case SND_SOC_DAPM_POST_PMU: + snd_soc_update_bits(codec, + WCD9XXX_A_ANA_RX_SUPPLIES, 0x02, 0x02); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_update_bits(codec, + WCD9XXX_A_ANA_RX_SUPPLIES, 0x02, 0x00); + break; + } + } +} + +static void tavil_codec_clear_anc_tx_hold(struct tavil_priv *tavil) +{ + if (test_and_clear_bit(ANC_MIC_AMIC1, &tavil->status_mask)) + tavil_codec_set_tx_hold(tavil->codec, WCD934X_ANA_AMIC1, false); + if (test_and_clear_bit(ANC_MIC_AMIC2, &tavil->status_mask)) + tavil_codec_set_tx_hold(tavil->codec, WCD934X_ANA_AMIC2, false); + if (test_and_clear_bit(ANC_MIC_AMIC3, &tavil->status_mask)) + tavil_codec_set_tx_hold(tavil->codec, WCD934X_ANA_AMIC3, false); + if (test_and_clear_bit(ANC_MIC_AMIC4, &tavil->status_mask)) + tavil_codec_set_tx_hold(tavil->codec, WCD934X_ANA_AMIC4, false); +} + + +static void tavil_ocp_control(struct snd_soc_codec *codec, bool enable) +{ + if (enable) { + snd_soc_update_bits(codec, WCD934X_HPH_OCP_CTL, 0x10, 0x10); + snd_soc_update_bits(codec, WCD934X_RX_OCP_CTL, 0x0F, 0x02); + } else { + snd_soc_update_bits(codec, WCD934X_RX_OCP_CTL, 0x0F, 0x0F); + snd_soc_update_bits(codec, WCD934X_HPH_OCP_CTL, 0x10, 0x00); + } +} + +static int tavil_codec_enable_hphr_pa(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + struct tavil_dsd_config *dsd_conf = tavil->dsd_config; + int ret = 0; + + dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + tavil_ocp_control(codec, false); + if (TAVIL_IS_1_0(tavil->wcd9xxx)) + snd_soc_update_bits(codec, WCD934X_HPH_REFBUFF_LP_CTL, + 0x06, (0x03 << 1)); + + if ((!(strcmp(w->name, "ANC HPHR PA"))) && + (test_bit(HPH_PA_DELAY, &tavil->status_mask))) + snd_soc_update_bits(codec, WCD934X_ANA_HPH, 0xC0, 0xC0); + + set_bit(HPH_PA_DELAY, &tavil->status_mask); + if (dsd_conf && + (snd_soc_read(codec, WCD934X_CDC_DSD1_PATH_CTL) & 0x01)) { + /* Set regulator mode to AB if DSD is enabled */ + snd_soc_update_bits(codec, WCD934X_ANA_RX_SUPPLIES, + 0x02, 0x02); + } + break; + case SND_SOC_DAPM_POST_PMU: + if ((!(strcmp(w->name, "ANC HPHR PA")))) { + if ((snd_soc_read(codec, WCD934X_ANA_HPH) & 0xC0) + != 0xC0) + /* + * If PA_EN is not set (potentially in ANC case) + * then do nothing for POST_PMU and let left + * channel handle everything. + */ + break; + } + /* + * 7ms sleep is required after PA is enabled as per + * HW requirement. If compander is disabled, then + * 20ms delay is needed. + */ + if (test_bit(HPH_PA_DELAY, &tavil->status_mask)) { + if (!tavil->comp_enabled[COMPANDER_2]) + usleep_range(20000, 20100); + else + usleep_range(7000, 7100); + clear_bit(HPH_PA_DELAY, &tavil->status_mask); + } + if (tavil->anc_func) { + /* Clear Tx FE HOLD if both PAs are enabled */ + if ((snd_soc_read(tavil->codec, WCD934X_ANA_HPH) & + 0xC0) == 0xC0) + tavil_codec_clear_anc_tx_hold(tavil); + } + + snd_soc_update_bits(codec, WCD934X_HPH_R_TEST, 0x01, 0x01); + + /* Remove mute */ + snd_soc_update_bits(codec, WCD934X_CDC_RX2_RX_PATH_CTL, + 0x10, 0x00); + /* Enable GM3 boost */ + snd_soc_update_bits(codec, WCD934X_HPH_CNP_WG_CTL, + 0x80, 0x80); + /* Enable AutoChop timer at the end of power up */ + snd_soc_update_bits(codec, WCD934X_HPH_NEW_INT_HPH_TIMER1, + 0x02, 0x02); + /* Remove mix path mute if it is enabled */ + if ((snd_soc_read(codec, WCD934X_CDC_RX2_RX_PATH_MIX_CTL)) & + 0x10) + snd_soc_update_bits(codec, + WCD934X_CDC_RX2_RX_PATH_MIX_CTL, + 0x10, 0x00); + if (dsd_conf && + (snd_soc_read(codec, WCD934X_CDC_DSD1_PATH_CTL) & 0x01)) + snd_soc_update_bits(codec, WCD934X_CDC_DSD1_CFG2, + 0x04, 0x00); + if (!(strcmp(w->name, "ANC HPHR PA"))) { + pr_debug("%s:Do everything needed for left channel\n", + __func__); + /* Do everything needed for left channel */ + snd_soc_update_bits(codec, WCD934X_HPH_L_TEST, + 0x01, 0x01); + + /* Remove mute */ + snd_soc_update_bits(codec, WCD934X_CDC_RX1_RX_PATH_CTL, + 0x10, 0x00); + + /* Remove mix path mute if it is enabled */ + if ((snd_soc_read(codec, + WCD934X_CDC_RX1_RX_PATH_MIX_CTL)) & + 0x10) + snd_soc_update_bits(codec, + WCD934X_CDC_RX1_RX_PATH_MIX_CTL, + 0x10, 0x00); + + if (dsd_conf && (snd_soc_read(codec, + WCD934X_CDC_DSD0_PATH_CTL) & + 0x01)) + snd_soc_update_bits(codec, + WCD934X_CDC_DSD0_CFG2, + 0x04, 0x00); + /* Remove ANC Rx from reset */ + ret = tavil_codec_enable_anc(w, kcontrol, event); + } + tavil_codec_override(codec, tavil->hph_mode, event); + tavil_ocp_control(codec, true); + break; + case SND_SOC_DAPM_PRE_PMD: + tavil_ocp_control(codec, false); + blocking_notifier_call_chain(&tavil->mbhc->notifier, + WCD_EVENT_PRE_HPHR_PA_OFF, + &tavil->mbhc->wcd_mbhc); + /* Enable DSD Mute before PA disable */ + if (dsd_conf && + (snd_soc_read(codec, WCD934X_CDC_DSD1_PATH_CTL) & 0x01)) + snd_soc_update_bits(codec, WCD934X_CDC_DSD1_CFG2, + 0x04, 0x04); + snd_soc_update_bits(codec, WCD934X_HPH_R_TEST, 0x01, 0x00); + snd_soc_update_bits(codec, WCD934X_CDC_RX2_RX_PATH_CTL, + 0x10, 0x10); + snd_soc_update_bits(codec, WCD934X_CDC_RX2_RX_PATH_MIX_CTL, + 0x10, 0x10); + if (!(strcmp(w->name, "ANC HPHR PA"))) + snd_soc_update_bits(codec, WCD934X_ANA_HPH, 0x40, 0x00); + break; + case SND_SOC_DAPM_POST_PMD: + /* + * 5ms sleep is required after PA disable. If compander is + * disabled, then 20ms delay is needed after PA disable. + */ + if (!tavil->comp_enabled[COMPANDER_2]) + usleep_range(20000, 20100); + else + usleep_range(5000, 5100); + tavil_codec_override(codec, tavil->hph_mode, event); + blocking_notifier_call_chain(&tavil->mbhc->notifier, + WCD_EVENT_POST_HPHR_PA_OFF, + &tavil->mbhc->wcd_mbhc); + if (TAVIL_IS_1_0(tavil->wcd9xxx)) + snd_soc_update_bits(codec, WCD934X_HPH_REFBUFF_LP_CTL, + 0x06, 0x0); + if (!(strcmp(w->name, "ANC HPHR PA"))) { + ret = tavil_codec_enable_anc(w, kcontrol, event); + snd_soc_update_bits(codec, + WCD934X_CDC_RX2_RX_PATH_CFG0, + 0x10, 0x00); + } + tavil_ocp_control(codec, true); + break; + }; + + return ret; +} + +static int tavil_codec_enable_hphl_pa(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + struct tavil_dsd_config *dsd_conf = tavil->dsd_config; + int ret = 0; + + dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + tavil_ocp_control(codec, false); + if (TAVIL_IS_1_0(tavil->wcd9xxx)) + snd_soc_update_bits(codec, WCD934X_HPH_REFBUFF_LP_CTL, + 0x06, (0x03 << 1)); + if ((!(strcmp(w->name, "ANC HPHL PA"))) && + (test_bit(HPH_PA_DELAY, &tavil->status_mask))) + snd_soc_update_bits(codec, WCD934X_ANA_HPH, + 0xC0, 0xC0); + set_bit(HPH_PA_DELAY, &tavil->status_mask); + if (dsd_conf && + (snd_soc_read(codec, WCD934X_CDC_DSD0_PATH_CTL) & 0x01)) { + /* Set regulator mode to AB if DSD is enabled */ + snd_soc_update_bits(codec, WCD934X_ANA_RX_SUPPLIES, + 0x02, 0x02); + } + break; + case SND_SOC_DAPM_POST_PMU: + if (!(strcmp(w->name, "ANC HPHL PA"))) { + if ((snd_soc_read(codec, WCD934X_ANA_HPH) & 0xC0) + != 0xC0) + /* + * If PA_EN is not set (potentially in ANC + * case) then do nothing for POST_PMU and + * let right channel handle everything. + */ + break; + } + /* + * 7ms sleep is required after PA is enabled as per + * HW requirement. If compander is disabled, then + * 20ms delay is needed. + */ + if (test_bit(HPH_PA_DELAY, &tavil->status_mask)) { + if (!tavil->comp_enabled[COMPANDER_1]) + usleep_range(20000, 20100); + else + usleep_range(7000, 7100); + clear_bit(HPH_PA_DELAY, &tavil->status_mask); + } + if (tavil->anc_func) { + /* Clear Tx FE HOLD if both PAs are enabled */ + if ((snd_soc_read(tavil->codec, WCD934X_ANA_HPH) & + 0xC0) == 0xC0) + tavil_codec_clear_anc_tx_hold(tavil); + } + + snd_soc_update_bits(codec, WCD934X_HPH_L_TEST, 0x01, 0x01); + /* Remove Mute on primary path */ + snd_soc_update_bits(codec, WCD934X_CDC_RX1_RX_PATH_CTL, + 0x10, 0x00); + /* Enable GM3 boost */ + snd_soc_update_bits(codec, WCD934X_HPH_CNP_WG_CTL, + 0x80, 0x80); + /* Enable AutoChop timer at the end of power up */ + snd_soc_update_bits(codec, WCD934X_HPH_NEW_INT_HPH_TIMER1, + 0x02, 0x02); + /* Remove mix path mute if it is enabled */ + if ((snd_soc_read(codec, WCD934X_CDC_RX1_RX_PATH_MIX_CTL)) & + 0x10) + snd_soc_update_bits(codec, + WCD934X_CDC_RX1_RX_PATH_MIX_CTL, + 0x10, 0x00); + if (dsd_conf && + (snd_soc_read(codec, WCD934X_CDC_DSD0_PATH_CTL) & 0x01)) + snd_soc_update_bits(codec, WCD934X_CDC_DSD0_CFG2, + 0x04, 0x00); + if (!(strcmp(w->name, "ANC HPHL PA"))) { + pr_debug("%s:Do everything needed for right channel\n", + __func__); + + /* Do everything needed for right channel */ + snd_soc_update_bits(codec, WCD934X_HPH_R_TEST, + 0x01, 0x01); + + /* Remove mute */ + snd_soc_update_bits(codec, WCD934X_CDC_RX2_RX_PATH_CTL, + 0x10, 0x00); + + /* Remove mix path mute if it is enabled */ + if ((snd_soc_read(codec, + WCD934X_CDC_RX2_RX_PATH_MIX_CTL)) & + 0x10) + snd_soc_update_bits(codec, + WCD934X_CDC_RX2_RX_PATH_MIX_CTL, + 0x10, 0x00); + if (dsd_conf && (snd_soc_read(codec, + WCD934X_CDC_DSD1_PATH_CTL) & 0x01)) + snd_soc_update_bits(codec, + WCD934X_CDC_DSD1_CFG2, + 0x04, 0x00); + /* Remove ANC Rx from reset */ + ret = tavil_codec_enable_anc(w, kcontrol, event); + } + tavil_codec_override(codec, tavil->hph_mode, event); + tavil_ocp_control(codec, true); + break; + case SND_SOC_DAPM_PRE_PMD: + tavil_ocp_control(codec, false); + blocking_notifier_call_chain(&tavil->mbhc->notifier, + WCD_EVENT_PRE_HPHL_PA_OFF, + &tavil->mbhc->wcd_mbhc); + /* Enable DSD Mute before PA disable */ + if (dsd_conf && + (snd_soc_read(codec, WCD934X_CDC_DSD0_PATH_CTL) & 0x01)) + snd_soc_update_bits(codec, WCD934X_CDC_DSD0_CFG2, + 0x04, 0x04); + + snd_soc_update_bits(codec, WCD934X_HPH_L_TEST, 0x01, 0x00); + snd_soc_update_bits(codec, WCD934X_CDC_RX1_RX_PATH_CTL, + 0x10, 0x10); + snd_soc_update_bits(codec, WCD934X_CDC_RX1_RX_PATH_MIX_CTL, + 0x10, 0x10); + if (!(strcmp(w->name, "ANC HPHL PA"))) + snd_soc_update_bits(codec, WCD934X_ANA_HPH, + 0x80, 0x00); + break; + case SND_SOC_DAPM_POST_PMD: + /* + * 5ms sleep is required after PA disable. If compander is + * disabled, then 20ms delay is needed after PA disable. + */ + if (!tavil->comp_enabled[COMPANDER_1]) + usleep_range(20000, 20100); + else + usleep_range(5000, 5100); + tavil_codec_override(codec, tavil->hph_mode, event); + blocking_notifier_call_chain(&tavil->mbhc->notifier, + WCD_EVENT_POST_HPHL_PA_OFF, + &tavil->mbhc->wcd_mbhc); + if (TAVIL_IS_1_0(tavil->wcd9xxx)) + snd_soc_update_bits(codec, WCD934X_HPH_REFBUFF_LP_CTL, + 0x06, 0x0); + if (!(strcmp(w->name, "ANC HPHL PA"))) { + ret = tavil_codec_enable_anc(w, kcontrol, event); + snd_soc_update_bits(codec, + WCD934X_CDC_RX1_RX_PATH_CFG0, 0x10, 0x00); + } + tavil_ocp_control(codec, true); + break; + }; + + return ret; +} + +static int tavil_codec_enable_lineout_pa(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + u16 lineout_vol_reg = 0, lineout_mix_vol_reg = 0; + u16 dsd_mute_reg = 0, dsd_clk_reg = 0; + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + struct tavil_dsd_config *dsd_conf = tavil->dsd_config; + + dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event); + + if (w->reg == WCD934X_ANA_LO_1_2) { + if (w->shift == 7) { + lineout_vol_reg = WCD934X_CDC_RX3_RX_PATH_CTL; + lineout_mix_vol_reg = WCD934X_CDC_RX3_RX_PATH_MIX_CTL; + dsd_mute_reg = WCD934X_CDC_DSD0_CFG2; + dsd_clk_reg = WCD934X_CDC_DSD0_PATH_CTL; + } else if (w->shift == 6) { + lineout_vol_reg = WCD934X_CDC_RX4_RX_PATH_CTL; + lineout_mix_vol_reg = WCD934X_CDC_RX4_RX_PATH_MIX_CTL; + dsd_mute_reg = WCD934X_CDC_DSD1_CFG2; + dsd_clk_reg = WCD934X_CDC_DSD1_PATH_CTL; + } + } else { + dev_err(codec->dev, "%s: Error enabling lineout PA\n", + __func__); + return -EINVAL; + } + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + tavil_codec_override(codec, CLS_AB, event); + break; + case SND_SOC_DAPM_POST_PMU: + /* + * 5ms sleep is required after PA is enabled as per + * HW requirement + */ + usleep_range(5000, 5500); + snd_soc_update_bits(codec, lineout_vol_reg, + 0x10, 0x00); + /* Remove mix path mute if it is enabled */ + if ((snd_soc_read(codec, lineout_mix_vol_reg)) & 0x10) + snd_soc_update_bits(codec, + lineout_mix_vol_reg, + 0x10, 0x00); + if (dsd_conf && (snd_soc_read(codec, dsd_clk_reg) & 0x01)) + snd_soc_update_bits(codec, dsd_mute_reg, 0x04, 0x00); + break; + case SND_SOC_DAPM_PRE_PMD: + if (dsd_conf && (snd_soc_read(codec, dsd_clk_reg) & 0x01)) + snd_soc_update_bits(codec, dsd_mute_reg, 0x04, 0x04); + break; + case SND_SOC_DAPM_POST_PMD: + /* + * 5ms sleep is required after PA is disabled as per + * HW requirement + */ + usleep_range(5000, 5500); + tavil_codec_override(codec, CLS_AB, event); + default: + break; + }; + + return 0; +} + +static int i2s_rx_mux_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm); + struct tavil_priv *tavil_p = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.enumerated.item[0] = + tavil_p->rx_port_value[widget->shift]; + return 0; +} + +static int i2s_rx_mux_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm); + struct tavil_priv *tavil_p = snd_soc_codec_get_drvdata(codec); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + struct snd_soc_dapm_update *update = NULL; + unsigned int rx_port_value; + u32 port_id = widget->shift; + + tavil_p->rx_port_value[port_id] = ucontrol->value.enumerated.item[0]; + rx_port_value = tavil_p->rx_port_value[port_id]; + + dev_dbg(codec->dev, "%s: wname %s cname %s value %u shift %d item %ld\n", + __func__, widget->name, ucontrol->id.name, + rx_port_value, widget->shift, + ucontrol->value.integer.value[0]); + + snd_soc_dapm_mux_update_power(widget->dapm, kcontrol, + rx_port_value, e, update); + return 0; +} + +static int tavil_codec_enable_i2s_path(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + int ret = 0; + u32 i2s_reg; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tavil_priv *tavil_p = snd_soc_codec_get_drvdata(codec); + + switch (tavil_p->rx_port_value[w->shift]) { + case AIF1_PB: + case AIF1_CAP: + i2s_reg = WCD934X_DATA_HUB_I2S_0_CTL; + break; + case AIF2_PB: + case AIF2_CAP: + i2s_reg = WCD934X_DATA_HUB_I2S_1_CTL; + break; + case AIF3_PB: + case AIF3_CAP: + i2s_reg = WCD934X_DATA_HUB_I2S_2_CTL; + break; + default: + dev_err(codec->dev, "%s Invalid i2s Id received", __func__); + return -EINVAL; + } + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + ret = snd_soc_update_bits(codec, i2s_reg, 0x01, 0x01); + break; + case SND_SOC_DAPM_POST_PMD: + ret = snd_soc_update_bits(codec, i2s_reg, 0x01, 0x00); + break; + } + + return ret; +} + +static int tavil_codec_ear_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + int ret = 0; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* Disable AutoChop timer during power up */ + snd_soc_update_bits(codec, WCD934X_HPH_NEW_INT_HPH_TIMER1, + 0x02, 0x00); + + if (tavil->anc_func) + ret = tavil_codec_enable_anc(w, kcontrol, event); + + wcd_clsh_fsm(codec, &tavil->clsh_d, + WCD_CLSH_EVENT_PRE_DAC, + WCD_CLSH_STATE_EAR, + CLS_H_NORMAL); + if (tavil->anc_func) + snd_soc_update_bits(codec, WCD934X_CDC_RX0_RX_PATH_CFG0, + 0x10, 0x10); + break; + case SND_SOC_DAPM_POST_PMD: + wcd_clsh_fsm(codec, &tavil->clsh_d, + WCD_CLSH_EVENT_POST_PA, + WCD_CLSH_STATE_EAR, + CLS_H_NORMAL); + break; + default: + break; + }; + + return ret; +} + +static int tavil_codec_hphr_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + int hph_mode = tavil->hph_mode; + u8 dem_inp; + struct tavil_dsd_config *dsd_conf = tavil->dsd_config; + int ret = 0; + + dev_dbg(codec->dev, "%s wname: %s event: %d hph_mode: %d\n", __func__, + w->name, event, hph_mode); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (tavil->anc_func) { + ret = tavil_codec_enable_anc(w, kcontrol, event); + /* 40 msec delay is needed to avoid click and pop */ + msleep(40); + } + /* Read DEM INP Select */ + dem_inp = snd_soc_read(codec, WCD934X_CDC_RX2_RX_PATH_SEC0) & + 0x03; + if (((hph_mode == CLS_H_HIFI) || (hph_mode == CLS_H_LOHIFI) || + (hph_mode == CLS_H_LP)) && (dem_inp != 0x01)) { + dev_err(codec->dev, "%s: DEM Input not set correctly, hph_mode: %d\n", + __func__, hph_mode); + return -EINVAL; + } + if ((hph_mode != CLS_H_LP) && (hph_mode != CLS_H_ULP)) + /* Ripple freq control enable */ + snd_soc_update_bits(codec, + WCD934X_SIDO_NEW_VOUT_D_FREQ2, + 0x01, 0x01); + /* Disable AutoChop timer during power up */ + snd_soc_update_bits(codec, WCD934X_HPH_NEW_INT_HPH_TIMER1, + 0x02, 0x00); + /* Set RDAC gain */ + if (TAVIL_IS_1_0(tavil->wcd9xxx)) + snd_soc_update_bits(codec, + WCD934X_HPH_NEW_INT_RDAC_GAIN_CTL, + 0xF0, 0x40); + if (dsd_conf && + (snd_soc_read(codec, WCD934X_CDC_DSD1_PATH_CTL) & 0x01)) + hph_mode = CLS_H_HIFI; + + wcd_clsh_fsm(codec, &tavil->clsh_d, + WCD_CLSH_EVENT_PRE_DAC, + WCD_CLSH_STATE_HPHR, + hph_mode); + if (tavil->anc_func) + snd_soc_update_bits(codec, + WCD934X_CDC_RX2_RX_PATH_CFG0, + 0x10, 0x10); + break; + case SND_SOC_DAPM_POST_PMD: + /* 1000us required as per HW requirement */ + usleep_range(1000, 1100); + wcd_clsh_fsm(codec, &tavil->clsh_d, + WCD_CLSH_EVENT_POST_PA, + WCD_CLSH_STATE_HPHR, + hph_mode); + if ((hph_mode != CLS_H_LP) && (hph_mode != CLS_H_ULP)) + /* Ripple freq control disable */ + snd_soc_update_bits(codec, + WCD934X_SIDO_NEW_VOUT_D_FREQ2, + 0x01, 0x0); + /* Re-set RDAC gain */ + if (TAVIL_IS_1_0(tavil->wcd9xxx)) + snd_soc_update_bits(codec, + WCD934X_HPH_NEW_INT_RDAC_GAIN_CTL, + 0xF0, 0x0); + break; + default: + break; + }; + + return 0; +} + +static int tavil_codec_hphl_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + int hph_mode = tavil->hph_mode; + u8 dem_inp; + int ret = 0; + struct tavil_dsd_config *dsd_conf = tavil->dsd_config; + uint32_t impedl = 0, impedr = 0; + + dev_dbg(codec->dev, "%s wname: %s event: %d hph_mode: %d\n", __func__, + w->name, event, hph_mode); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (tavil->anc_func) { + ret = tavil_codec_enable_anc(w, kcontrol, event); + /* 40 msec delay is needed to avoid click and pop */ + msleep(40); + } + /* Read DEM INP Select */ + dem_inp = snd_soc_read(codec, WCD934X_CDC_RX1_RX_PATH_SEC0) & + 0x03; + if (((hph_mode == CLS_H_HIFI) || (hph_mode == CLS_H_LOHIFI) || + (hph_mode == CLS_H_LP)) && (dem_inp != 0x01)) { + dev_err(codec->dev, "%s: DEM Input not set correctly, hph_mode: %d\n", + __func__, hph_mode); + return -EINVAL; + } + if ((hph_mode != CLS_H_LP) && (hph_mode != CLS_H_ULP)) + /* Ripple freq control enable */ + snd_soc_update_bits(codec, + WCD934X_SIDO_NEW_VOUT_D_FREQ2, + 0x01, 0x01); + /* Disable AutoChop timer during power up */ + snd_soc_update_bits(codec, WCD934X_HPH_NEW_INT_HPH_TIMER1, + 0x02, 0x00); + /* Set RDAC gain */ + if (TAVIL_IS_1_0(tavil->wcd9xxx)) + snd_soc_update_bits(codec, + WCD934X_HPH_NEW_INT_RDAC_GAIN_CTL, + 0xF0, 0x40); + if (dsd_conf && + (snd_soc_read(codec, WCD934X_CDC_DSD0_PATH_CTL) & 0x01)) + hph_mode = CLS_H_HIFI; + + wcd_clsh_fsm(codec, &tavil->clsh_d, + WCD_CLSH_EVENT_PRE_DAC, + WCD_CLSH_STATE_HPHL, + hph_mode); + + if (tavil->anc_func) + snd_soc_update_bits(codec, + WCD934X_CDC_RX1_RX_PATH_CFG0, + 0x10, 0x10); + + ret = tavil_mbhc_get_impedance(tavil->mbhc, + &impedl, &impedr); + if (!ret) { + wcd_clsh_imped_config(codec, impedl, false); + set_bit(CLSH_Z_CONFIG, &tavil->status_mask); + } else { + dev_dbg(codec->dev, "%s: Failed to get mbhc impedance %d\n", + __func__, ret); + ret = 0; + } + + break; + case SND_SOC_DAPM_POST_PMD: + /* 1000us required as per HW requirement */ + usleep_range(1000, 1100); + wcd_clsh_fsm(codec, &tavil->clsh_d, + WCD_CLSH_EVENT_POST_PA, + WCD_CLSH_STATE_HPHL, + hph_mode); + if ((hph_mode != CLS_H_LP) && (hph_mode != CLS_H_ULP)) + /* Ripple freq control disable */ + snd_soc_update_bits(codec, + WCD934X_SIDO_NEW_VOUT_D_FREQ2, + 0x01, 0x0); + /* Re-set RDAC gain */ + if (TAVIL_IS_1_0(tavil->wcd9xxx)) + snd_soc_update_bits(codec, + WCD934X_HPH_NEW_INT_RDAC_GAIN_CTL, + 0xF0, 0x0); + + if (test_bit(CLSH_Z_CONFIG, &tavil->status_mask)) { + wcd_clsh_imped_config(codec, impedl, true); + clear_bit(CLSH_Z_CONFIG, &tavil->status_mask); + } + break; + default: + break; + }; + + return ret; +} + +static int tavil_codec_lineout_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + wcd_clsh_fsm(codec, &tavil->clsh_d, + WCD_CLSH_EVENT_PRE_DAC, + WCD_CLSH_STATE_LO, + CLS_AB); + break; + case SND_SOC_DAPM_POST_PMD: + wcd_clsh_fsm(codec, &tavil->clsh_d, + WCD_CLSH_EVENT_POST_PA, + WCD_CLSH_STATE_LO, + CLS_AB); + break; + } + + return 0; +} + +static int tavil_codec_spk_boost_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + u16 boost_path_ctl, boost_path_cfg1; + u16 reg, reg_mix; + + dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event); + + if (!strcmp(w->name, "RX INT7 CHAIN")) { + boost_path_ctl = WCD934X_CDC_BOOST0_BOOST_PATH_CTL; + boost_path_cfg1 = WCD934X_CDC_RX7_RX_PATH_CFG1; + reg = WCD934X_CDC_RX7_RX_PATH_CTL; + reg_mix = WCD934X_CDC_RX7_RX_PATH_MIX_CTL; + } else if (!strcmp(w->name, "RX INT8 CHAIN")) { + boost_path_ctl = WCD934X_CDC_BOOST1_BOOST_PATH_CTL; + boost_path_cfg1 = WCD934X_CDC_RX8_RX_PATH_CFG1; + reg = WCD934X_CDC_RX8_RX_PATH_CTL; + reg_mix = WCD934X_CDC_RX8_RX_PATH_MIX_CTL; + } else { + dev_err(codec->dev, "%s: unknown widget: %s\n", + __func__, w->name); + return -EINVAL; + } + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_update_bits(codec, boost_path_cfg1, 0x01, 0x01); + snd_soc_update_bits(codec, boost_path_ctl, 0x10, 0x10); + snd_soc_update_bits(codec, reg, 0x10, 0x00); + if ((snd_soc_read(codec, reg_mix)) & 0x10) + snd_soc_update_bits(codec, reg_mix, 0x10, 0x00); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_update_bits(codec, boost_path_ctl, 0x10, 0x00); + snd_soc_update_bits(codec, boost_path_cfg1, 0x01, 0x00); + break; + }; + + return 0; +} + +static int __tavil_codec_enable_swr(struct snd_soc_dapm_widget *w, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tavil_priv *tavil; + int ch_cnt = 0; + + tavil = snd_soc_codec_get_drvdata(codec); + + if (!tavil->swr.ctrl_data) + return -EINVAL; + if (!tavil->swr.ctrl_data[0].swr_pdev) + return -EINVAL; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (((strnstr(w->name, "INT7_", sizeof("RX INT7_"))) || + (strnstr(w->name, "INT7 MIX2", + sizeof("RX INT7 MIX2"))))) + tavil->swr.rx_7_count++; + if ((strnstr(w->name, "INT8_", sizeof("RX INT8_"))) && + !tavil->swr.rx_8_count) + tavil->swr.rx_8_count++; + ch_cnt = !!(tavil->swr.rx_7_count) + tavil->swr.rx_8_count; + + if (wcd9xxx_get_current_power_state(tavil->wcd9xxx, + WCD9XXX_DIG_CORE_REGION_1) + != WCD_REGION_POWER_COLLAPSE_REMOVE) + goto done; + + swrm_wcd_notify(tavil->swr.ctrl_data[0].swr_pdev, + SWR_DEVICE_UP, NULL); + swrm_wcd_notify(tavil->swr.ctrl_data[0].swr_pdev, + SWR_SET_NUM_RX_CH, &ch_cnt); + break; + case SND_SOC_DAPM_POST_PMD: + if ((strnstr(w->name, "INT7_", sizeof("RX INT7_"))) || + (strnstr(w->name, "INT7 MIX2", + sizeof("RX INT7 MIX2")))) + tavil->swr.rx_7_count--; + if ((strnstr(w->name, "INT8_", sizeof("RX INT8_"))) && + tavil->swr.rx_8_count) + tavil->swr.rx_8_count--; + ch_cnt = !!(tavil->swr.rx_7_count) + tavil->swr.rx_8_count; + + swrm_wcd_notify(tavil->swr.ctrl_data[0].swr_pdev, + SWR_SET_NUM_RX_CH, &ch_cnt); + + break; + } +done: + dev_dbg(tavil->dev, "%s: %s: current swr ch cnt: %d\n", + __func__, w->name, ch_cnt); + + return 0; +} + +static int tavil_codec_enable_swr(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + return __tavil_codec_enable_swr(w, event); +} + +static int tavil_codec_config_mad(struct snd_soc_codec *codec) +{ + int ret = 0; + int idx; + const struct firmware *fw; + struct firmware_cal *hwdep_cal = NULL; + struct wcd_mad_audio_cal *mad_cal = NULL; + const void *data; + const char *filename = WCD934X_MAD_AUDIO_FIRMWARE_PATH; + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + size_t cal_size; + + hwdep_cal = wcdcal_get_fw_cal(tavil->fw_data, WCD9XXX_MAD_CAL); + if (hwdep_cal) { + data = hwdep_cal->data; + cal_size = hwdep_cal->size; + dev_dbg(codec->dev, "%s: using hwdep calibration\n", + __func__); + } else { + ret = request_firmware(&fw, filename, codec->dev); + if (ret || !fw) { + dev_err(codec->dev, + "%s: MAD firmware acquire failed, err = %d\n", + __func__, ret); + return -ENODEV; + } + data = fw->data; + cal_size = fw->size; + dev_dbg(codec->dev, "%s: using request_firmware calibration\n", + __func__); + } + + if (cal_size < sizeof(*mad_cal)) { + dev_err(codec->dev, + "%s: Incorrect size %zd for MAD Cal, expected %zd\n", + __func__, cal_size, sizeof(*mad_cal)); + ret = -ENOMEM; + goto done; + } + + mad_cal = (struct wcd_mad_audio_cal *) (data); + if (!mad_cal) { + dev_err(codec->dev, + "%s: Invalid calibration data\n", + __func__); + ret = -EINVAL; + goto done; + } + + snd_soc_write(codec, WCD934X_SOC_MAD_MAIN_CTL_2, + mad_cal->microphone_info.cycle_time); + snd_soc_update_bits(codec, WCD934X_SOC_MAD_MAIN_CTL_1, 0xFF << 3, + ((uint16_t)mad_cal->microphone_info.settle_time) + << 3); + + /* Audio */ + snd_soc_write(codec, WCD934X_SOC_MAD_AUDIO_CTL_8, + mad_cal->audio_info.rms_omit_samples); + snd_soc_update_bits(codec, WCD934X_SOC_MAD_AUDIO_CTL_1, + 0x07 << 4, mad_cal->audio_info.rms_comp_time << 4); + snd_soc_update_bits(codec, WCD934X_SOC_MAD_AUDIO_CTL_2, 0x03 << 2, + mad_cal->audio_info.detection_mechanism << 2); + snd_soc_write(codec, WCD934X_SOC_MAD_AUDIO_CTL_7, + mad_cal->audio_info.rms_diff_threshold & 0x3F); + snd_soc_write(codec, WCD934X_SOC_MAD_AUDIO_CTL_5, + mad_cal->audio_info.rms_threshold_lsb); + snd_soc_write(codec, WCD934X_SOC_MAD_AUDIO_CTL_6, + mad_cal->audio_info.rms_threshold_msb); + + for (idx = 0; idx < ARRAY_SIZE(mad_cal->audio_info.iir_coefficients); + idx++) { + snd_soc_update_bits(codec, WCD934X_SOC_MAD_AUDIO_IIR_CTL_PTR, + 0x3F, idx); + snd_soc_write(codec, WCD934X_SOC_MAD_AUDIO_IIR_CTL_VAL, + mad_cal->audio_info.iir_coefficients[idx]); + dev_dbg(codec->dev, "%s:MAD Audio IIR Coef[%d] = 0X%x", + __func__, idx, + mad_cal->audio_info.iir_coefficients[idx]); + } + + /* Beacon */ + snd_soc_write(codec, WCD934X_SOC_MAD_BEACON_CTL_8, + mad_cal->beacon_info.rms_omit_samples); + snd_soc_update_bits(codec, WCD934X_SOC_MAD_BEACON_CTL_1, + 0x07 << 4, mad_cal->beacon_info.rms_comp_time << 4); + snd_soc_update_bits(codec, WCD934X_SOC_MAD_BEACON_CTL_2, 0x03 << 2, + mad_cal->beacon_info.detection_mechanism << 2); + snd_soc_write(codec, WCD934X_SOC_MAD_BEACON_CTL_7, + mad_cal->beacon_info.rms_diff_threshold & 0x1F); + snd_soc_write(codec, WCD934X_SOC_MAD_BEACON_CTL_5, + mad_cal->beacon_info.rms_threshold_lsb); + snd_soc_write(codec, WCD934X_SOC_MAD_BEACON_CTL_6, + mad_cal->beacon_info.rms_threshold_msb); + + for (idx = 0; idx < ARRAY_SIZE(mad_cal->beacon_info.iir_coefficients); + idx++) { + snd_soc_update_bits(codec, WCD934X_SOC_MAD_BEACON_IIR_CTL_PTR, + 0x3F, idx); + snd_soc_write(codec, WCD934X_SOC_MAD_BEACON_IIR_CTL_VAL, + mad_cal->beacon_info.iir_coefficients[idx]); + dev_dbg(codec->dev, "%s:MAD Beacon IIR Coef[%d] = 0X%x", + __func__, idx, + mad_cal->beacon_info.iir_coefficients[idx]); + } + + /* Ultrasound */ + snd_soc_update_bits(codec, WCD934X_SOC_MAD_ULTR_CTL_1, + 0x07 << 4, + mad_cal->ultrasound_info.rms_comp_time << 4); + snd_soc_update_bits(codec, WCD934X_SOC_MAD_ULTR_CTL_2, 0x03 << 2, + mad_cal->ultrasound_info.detection_mechanism << 2); + snd_soc_write(codec, WCD934X_SOC_MAD_ULTR_CTL_7, + mad_cal->ultrasound_info.rms_diff_threshold & 0x1F); + snd_soc_write(codec, WCD934X_SOC_MAD_ULTR_CTL_5, + mad_cal->ultrasound_info.rms_threshold_lsb); + snd_soc_write(codec, WCD934X_SOC_MAD_ULTR_CTL_6, + mad_cal->ultrasound_info.rms_threshold_msb); + +done: + if (!hwdep_cal) + release_firmware(fw); + + return ret; +} + +static int __tavil_codec_enable_mad(struct snd_soc_codec *codec, bool enable) +{ + int rc = 0; + + /* Return if CPE INPUT is DEC1 */ + if (snd_soc_read(codec, WCD934X_CPE_SS_SVA_CFG) & 0x04) { + dev_dbg(codec->dev, "%s: MAD is bypassed, skip mad %s\n", + __func__, enable ? "enable" : "disable"); + return rc; + } + + dev_dbg(codec->dev, "%s: enable = %s\n", __func__, + enable ? "enable" : "disable"); + + if (enable) { + snd_soc_update_bits(codec, WCD934X_SOC_MAD_AUDIO_CTL_2, + 0x03, 0x03); + rc = tavil_codec_config_mad(codec); + if (rc < 0) { + snd_soc_update_bits(codec, WCD934X_SOC_MAD_AUDIO_CTL_2, + 0x03, 0x00); + goto done; + } + + /* Turn on MAD clk */ + snd_soc_update_bits(codec, WCD934X_CPE_SS_MAD_CTL, + 0x01, 0x01); + + /* Undo reset for MAD */ + snd_soc_update_bits(codec, WCD934X_CPE_SS_MAD_CTL, + 0x02, 0x00); + } else { + snd_soc_update_bits(codec, WCD934X_SOC_MAD_AUDIO_CTL_2, + 0x03, 0x00); + /* Reset the MAD block */ + snd_soc_update_bits(codec, WCD934X_CPE_SS_MAD_CTL, + 0x02, 0x02); + /* Turn off MAD clk */ + snd_soc_update_bits(codec, WCD934X_CPE_SS_MAD_CTL, + 0x01, 0x00); + } +done: + return rc; +} + +static int tavil_codec_ape_enable_mad(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + int rc = 0; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_update_bits(codec, WCD934X_CPE_SS_SVA_CFG, 0x40, 0x40); + rc = __tavil_codec_enable_mad(codec, true); + break; + case SND_SOC_DAPM_PRE_PMD: + snd_soc_update_bits(codec, WCD934X_CPE_SS_SVA_CFG, 0x40, 0x00); + __tavil_codec_enable_mad(codec, false); + break; + } + + dev_dbg(tavil->dev, "%s: event = %d\n", __func__, event); + return rc; +} + +static int tavil_codec_cpe_mad_ctl(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + int rc = 0; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + tavil->mad_switch_cnt++; + if (tavil->mad_switch_cnt != 1) + goto done; + + snd_soc_update_bits(codec, WCD934X_CPE_SS_SVA_CFG, 0x20, 0x20); + rc = __tavil_codec_enable_mad(codec, true); + if (rc < 0) { + tavil->mad_switch_cnt--; + goto done; + } + + break; + case SND_SOC_DAPM_PRE_PMD: + tavil->mad_switch_cnt--; + if (tavil->mad_switch_cnt != 0) + goto done; + + snd_soc_update_bits(codec, WCD934X_CPE_SS_SVA_CFG, 0x20, 0x00); + __tavil_codec_enable_mad(codec, false); + break; + } +done: + dev_dbg(tavil->dev, "%s: event = %d, mad_switch_cnt = %d\n", + __func__, event, tavil->mad_switch_cnt); + return rc; +} + +static int tavil_get_asrc_mode(struct tavil_priv *tavil, int asrc, + u8 main_sr, u8 mix_sr) +{ + u8 asrc_output_mode; + int asrc_mode = CONV_88P2K_TO_384K; + + if ((asrc < 0) || (asrc >= ASRC_MAX)) + return 0; + + asrc_output_mode = tavil->asrc_output_mode[asrc]; + + if (asrc_output_mode) { + /* + * If Mix sample rate is < 96KHz, use 96K to 352.8K + * conversion, or else use 384K to 352.8K conversion + */ + if (mix_sr < 5) + asrc_mode = CONV_96K_TO_352P8K; + else + asrc_mode = CONV_384K_TO_352P8K; + } else { + /* Integer main and Fractional mix path */ + if (main_sr < 8 && mix_sr > 9) { + asrc_mode = CONV_352P8K_TO_384K; + } else if (main_sr > 8 && mix_sr < 8) { + /* Fractional main and Integer mix path */ + if (mix_sr < 5) + asrc_mode = CONV_96K_TO_352P8K; + else + asrc_mode = CONV_384K_TO_352P8K; + } else if (main_sr < 8 && mix_sr < 8) { + /* Integer main and Integer mix path */ + asrc_mode = CONV_96K_TO_384K; + } + } + + return asrc_mode; +} + +static int tavil_codec_wdma3_ctl(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* Fix to 16KHz */ + snd_soc_update_bits(codec, WCD934X_DMA_WDMA_CTL_3, + 0xF0, 0x10); + /* Select mclk_1 */ + snd_soc_update_bits(codec, WCD934X_DMA_WDMA_CTL_3, + 0x02, 0x00); + /* Enable DMA */ + snd_soc_update_bits(codec, WCD934X_DMA_WDMA_CTL_3, + 0x01, 0x01); + break; + + case SND_SOC_DAPM_POST_PMD: + /* Disable DMA */ + snd_soc_update_bits(codec, WCD934X_DMA_WDMA_CTL_3, + 0x01, 0x00); + break; + + }; + + return 0; +} + +static int tavil_codec_enable_asrc(struct snd_soc_codec *codec, + int asrc_in, int event) +{ + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + u16 cfg_reg, ctl_reg, clk_reg, asrc_ctl, mix_ctl_reg, paired_reg; + int asrc, ret = 0; + u8 main_sr, mix_sr, asrc_mode = 0; + + switch (asrc_in) { + case ASRC_IN_HPHL: + cfg_reg = WCD934X_CDC_RX1_RX_PATH_CFG0; + ctl_reg = WCD934X_CDC_RX1_RX_PATH_CTL; + clk_reg = WCD934X_MIXING_ASRC0_CLK_RST_CTL; + paired_reg = WCD934X_MIXING_ASRC1_CLK_RST_CTL; + asrc_ctl = WCD934X_MIXING_ASRC0_CTL1; + asrc = ASRC0; + break; + case ASRC_IN_LO1: + cfg_reg = WCD934X_CDC_RX3_RX_PATH_CFG0; + ctl_reg = WCD934X_CDC_RX3_RX_PATH_CTL; + clk_reg = WCD934X_MIXING_ASRC0_CLK_RST_CTL; + paired_reg = WCD934X_MIXING_ASRC1_CLK_RST_CTL; + asrc_ctl = WCD934X_MIXING_ASRC0_CTL1; + asrc = ASRC0; + break; + case ASRC_IN_HPHR: + cfg_reg = WCD934X_CDC_RX2_RX_PATH_CFG0; + ctl_reg = WCD934X_CDC_RX2_RX_PATH_CTL; + clk_reg = WCD934X_MIXING_ASRC1_CLK_RST_CTL; + paired_reg = WCD934X_MIXING_ASRC0_CLK_RST_CTL; + asrc_ctl = WCD934X_MIXING_ASRC1_CTL1; + asrc = ASRC1; + break; + case ASRC_IN_LO2: + cfg_reg = WCD934X_CDC_RX4_RX_PATH_CFG0; + ctl_reg = WCD934X_CDC_RX4_RX_PATH_CTL; + clk_reg = WCD934X_MIXING_ASRC1_CLK_RST_CTL; + paired_reg = WCD934X_MIXING_ASRC0_CLK_RST_CTL; + asrc_ctl = WCD934X_MIXING_ASRC1_CTL1; + asrc = ASRC1; + break; + case ASRC_IN_SPKR1: + cfg_reg = WCD934X_CDC_RX7_RX_PATH_CFG0; + ctl_reg = WCD934X_CDC_RX7_RX_PATH_CTL; + clk_reg = WCD934X_MIXING_ASRC2_CLK_RST_CTL; + paired_reg = WCD934X_MIXING_ASRC3_CLK_RST_CTL; + asrc_ctl = WCD934X_MIXING_ASRC2_CTL1; + asrc = ASRC2; + break; + case ASRC_IN_SPKR2: + cfg_reg = WCD934X_CDC_RX8_RX_PATH_CFG0; + ctl_reg = WCD934X_CDC_RX8_RX_PATH_CTL; + clk_reg = WCD934X_MIXING_ASRC3_CLK_RST_CTL; + paired_reg = WCD934X_MIXING_ASRC2_CLK_RST_CTL; + asrc_ctl = WCD934X_MIXING_ASRC3_CTL1; + asrc = ASRC3; + break; + default: + dev_err(codec->dev, "%s: Invalid asrc input :%d\n", __func__, + asrc_in); + ret = -EINVAL; + goto done; + }; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (tavil->asrc_users[asrc] == 0) { + if ((snd_soc_read(codec, clk_reg) & 0x02) || + (snd_soc_read(codec, paired_reg) & 0x02)) { + snd_soc_update_bits(codec, clk_reg, + 0x02, 0x00); + snd_soc_update_bits(codec, paired_reg, + 0x02, 0x00); + } + snd_soc_update_bits(codec, cfg_reg, 0x80, 0x80); + snd_soc_update_bits(codec, clk_reg, 0x01, 0x01); + main_sr = snd_soc_read(codec, ctl_reg) & 0x0F; + mix_ctl_reg = ctl_reg + 5; + mix_sr = snd_soc_read(codec, mix_ctl_reg) & 0x0F; + asrc_mode = tavil_get_asrc_mode(tavil, asrc, + main_sr, mix_sr); + dev_dbg(codec->dev, "%s: main_sr:%d mix_sr:%d asrc_mode %d\n", + __func__, main_sr, mix_sr, asrc_mode); + snd_soc_update_bits(codec, asrc_ctl, 0x07, asrc_mode); + } + tavil->asrc_users[asrc]++; + break; + case SND_SOC_DAPM_POST_PMD: + tavil->asrc_users[asrc]--; + if (tavil->asrc_users[asrc] <= 0) { + tavil->asrc_users[asrc] = 0; + snd_soc_update_bits(codec, asrc_ctl, 0x07, 0x00); + snd_soc_update_bits(codec, cfg_reg, 0x80, 0x00); + snd_soc_update_bits(codec, clk_reg, 0x03, 0x02); + } + break; + }; + + dev_dbg(codec->dev, "%s: ASRC%d, users: %d\n", + __func__, asrc, tavil->asrc_users[asrc]); + +done: + return ret; +} + +static int tavil_codec_enable_asrc_resampler(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + int ret = 0; + u8 cfg, asrc_in; + + cfg = snd_soc_read(codec, WCD934X_CDC_RX_INP_MUX_SPLINE_ASRC_CFG0); + if (!(cfg & 0xFF)) { + dev_err(codec->dev, "%s: ASRC%u input not selected\n", + __func__, w->shift); + return -EINVAL; + } + + switch (w->shift) { + case ASRC0: + asrc_in = ((cfg & 0x03) == 1) ? ASRC_IN_HPHL : ASRC_IN_LO1; + ret = tavil_codec_enable_asrc(codec, asrc_in, event); + break; + case ASRC1: + asrc_in = ((cfg & 0x0C) == 4) ? ASRC_IN_HPHR : ASRC_IN_LO2; + ret = tavil_codec_enable_asrc(codec, asrc_in, event); + break; + case ASRC2: + asrc_in = ((cfg & 0x30) == 0x20) ? ASRC_IN_SPKR1 : ASRC_INVALID; + ret = tavil_codec_enable_asrc(codec, asrc_in, event); + break; + case ASRC3: + asrc_in = ((cfg & 0xC0) == 0x80) ? ASRC_IN_SPKR2 : ASRC_INVALID; + ret = tavil_codec_enable_asrc(codec, asrc_in, event); + break; + default: + dev_err(codec->dev, "%s: Invalid asrc:%u\n", __func__, + w->shift); + ret = -EINVAL; + break; + }; + + return ret; +} + +static int tavil_enable_native_supply(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (++tavil->native_clk_users == 1) { + snd_soc_update_bits(codec, WCD934X_CLK_SYS_PLL_ENABLES, + 0x01, 0x01); + usleep_range(100, 120); + snd_soc_update_bits(codec, WCD934X_CLK_SYS_MCLK2_PRG1, + 0x06, 0x02); + snd_soc_update_bits(codec, WCD934X_CLK_SYS_MCLK2_PRG1, + 0x01, 0x01); + snd_soc_update_bits(codec, WCD934X_CODEC_RPM_CLK_GATE, + 0x04, 0x00); + usleep_range(30, 50); + snd_soc_update_bits(codec, + WCD934X_CDC_CLK_RST_CTRL_MCLK_CONTROL, + 0x02, 0x02); + snd_soc_update_bits(codec, + WCD934X_CDC_CLK_RST_CTRL_FS_CNT_CONTROL, + 0x10, 0x10); + } + break; + case SND_SOC_DAPM_PRE_PMD: + if (tavil->native_clk_users && + (--tavil->native_clk_users == 0)) { + snd_soc_update_bits(codec, + WCD934X_CDC_CLK_RST_CTRL_FS_CNT_CONTROL, + 0x10, 0x00); + snd_soc_update_bits(codec, + WCD934X_CDC_CLK_RST_CTRL_MCLK_CONTROL, + 0x02, 0x00); + snd_soc_update_bits(codec, WCD934X_CODEC_RPM_CLK_GATE, + 0x04, 0x04); + snd_soc_update_bits(codec, WCD934X_CLK_SYS_MCLK2_PRG1, + 0x01, 0x00); + snd_soc_update_bits(codec, WCD934X_CLK_SYS_MCLK2_PRG1, + 0x06, 0x00); + snd_soc_update_bits(codec, WCD934X_CLK_SYS_PLL_ENABLES, + 0x01, 0x00); + } + break; + } + + dev_dbg(codec->dev, "%s: native_clk_users: %d, event: %d\n", + __func__, tavil->native_clk_users, event); + + return 0; +} + +static void tavil_codec_hphdelay_lutbypass(struct snd_soc_codec *codec, + u16 interp_idx, int event) +{ + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + u8 hph_dly_mask; + u16 hph_lut_bypass_reg = 0; + u16 hph_comp_ctrl7 = 0; + + + switch (interp_idx) { + case INTERP_HPHL: + hph_dly_mask = 1; + hph_lut_bypass_reg = WCD934X_CDC_TOP_HPHL_COMP_LUT; + hph_comp_ctrl7 = WCD934X_CDC_COMPANDER1_CTL7; + break; + case INTERP_HPHR: + hph_dly_mask = 2; + hph_lut_bypass_reg = WCD934X_CDC_TOP_HPHR_COMP_LUT; + hph_comp_ctrl7 = WCD934X_CDC_COMPANDER2_CTL7; + break; + default: + break; + } + + if (hph_lut_bypass_reg && SND_SOC_DAPM_EVENT_ON(event)) { + snd_soc_update_bits(codec, WCD934X_CDC_CLSH_TEST0, + hph_dly_mask, 0x0); + snd_soc_update_bits(codec, hph_lut_bypass_reg, 0x80, 0x80); + if (tavil->hph_mode == CLS_H_ULP) + snd_soc_update_bits(codec, hph_comp_ctrl7, 0x20, 0x20); + } + + if (hph_lut_bypass_reg && SND_SOC_DAPM_EVENT_OFF(event)) { + snd_soc_update_bits(codec, WCD934X_CDC_CLSH_TEST0, + hph_dly_mask, hph_dly_mask); + snd_soc_update_bits(codec, hph_lut_bypass_reg, 0x80, 0x00); + snd_soc_update_bits(codec, hph_comp_ctrl7, 0x20, 0x0); + } +} + +static void tavil_codec_hd2_control(struct tavil_priv *priv, + u16 interp_idx, int event) +{ + u16 hd2_scale_reg; + u16 hd2_enable_reg = 0; + struct snd_soc_codec *codec = priv->codec; + + if (TAVIL_IS_1_1(priv->wcd9xxx)) + return; + + switch (interp_idx) { + case INTERP_HPHL: + hd2_scale_reg = WCD934X_CDC_RX1_RX_PATH_SEC3; + hd2_enable_reg = WCD934X_CDC_RX1_RX_PATH_CFG0; + break; + case INTERP_HPHR: + hd2_scale_reg = WCD934X_CDC_RX2_RX_PATH_SEC3; + hd2_enable_reg = WCD934X_CDC_RX2_RX_PATH_CFG0; + break; + } + + if (hd2_enable_reg && SND_SOC_DAPM_EVENT_ON(event)) { + snd_soc_update_bits(codec, hd2_scale_reg, 0x3C, 0x14); + snd_soc_update_bits(codec, hd2_enable_reg, 0x04, 0x04); + } + + if (hd2_enable_reg && SND_SOC_DAPM_EVENT_OFF(event)) { + snd_soc_update_bits(codec, hd2_enable_reg, 0x04, 0x00); + snd_soc_update_bits(codec, hd2_scale_reg, 0x3C, 0x00); + } +} + +static int tavil_codec_config_ear_spkr_gain(struct snd_soc_codec *codec, + int event, int gain_reg) +{ + int comp_gain_offset, val; + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + + switch (tavil->swr.spkr_mode) { + /* Compander gain in SPKR_MODE1 case is 12 dB */ + case WCD934X_SPKR_MODE_1: + comp_gain_offset = -12; + break; + /* Default case compander gain is 15 dB */ + default: + comp_gain_offset = -15; + break; + } + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + /* Apply ear spkr gain only if compander is enabled */ + if (tavil->comp_enabled[COMPANDER_7] && + (gain_reg == WCD934X_CDC_RX7_RX_VOL_CTL || + gain_reg == WCD934X_CDC_RX7_RX_VOL_MIX_CTL) && + (tavil->ear_spkr_gain != 0)) { + /* For example, val is -8(-12+5-1) for 4dB of gain */ + val = comp_gain_offset + tavil->ear_spkr_gain - 1; + snd_soc_write(codec, gain_reg, val); + + dev_dbg(codec->dev, "%s: RX7 Volume %d dB\n", + __func__, val); + } + break; + case SND_SOC_DAPM_POST_PMD: + /* + * Reset RX7 volume to 0 dB if compander is enabled and + * ear_spkr_gain is non-zero. + */ + if (tavil->comp_enabled[COMPANDER_7] && + (gain_reg == WCD934X_CDC_RX7_RX_VOL_CTL || + gain_reg == WCD934X_CDC_RX7_RX_VOL_MIX_CTL) && + (tavil->ear_spkr_gain != 0)) { + snd_soc_write(codec, gain_reg, 0x0); + + dev_dbg(codec->dev, "%s: Reset RX7 Volume to 0 dB\n", + __func__); + } + break; + } + + return 0; +} + +static int tavil_config_compander(struct snd_soc_codec *codec, int interp_n, + int event) +{ + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + int comp; + u16 comp_ctl0_reg, rx_path_cfg0_reg; + + /* EAR does not have compander */ + if (!interp_n) + return 0; + + comp = interp_n - 1; + dev_dbg(codec->dev, "%s: event %d compander %d, enabled %d\n", + __func__, event, comp + 1, tavil->comp_enabled[comp]); + + if (!tavil->comp_enabled[comp]) + return 0; + + comp_ctl0_reg = WCD934X_CDC_COMPANDER1_CTL0 + (comp * 8); + rx_path_cfg0_reg = WCD934X_CDC_RX1_RX_PATH_CFG0 + (comp * 20); + + if (SND_SOC_DAPM_EVENT_ON(event)) { + /* Enable Compander Clock */ + snd_soc_update_bits(codec, comp_ctl0_reg, 0x01, 0x01); + snd_soc_update_bits(codec, comp_ctl0_reg, 0x02, 0x02); + snd_soc_update_bits(codec, comp_ctl0_reg, 0x02, 0x00); + snd_soc_update_bits(codec, rx_path_cfg0_reg, 0x02, 0x02); + } + + if (SND_SOC_DAPM_EVENT_OFF(event)) { + snd_soc_update_bits(codec, rx_path_cfg0_reg, 0x02, 0x00); + snd_soc_update_bits(codec, comp_ctl0_reg, 0x04, 0x04); + snd_soc_update_bits(codec, comp_ctl0_reg, 0x02, 0x02); + snd_soc_update_bits(codec, comp_ctl0_reg, 0x02, 0x00); + snd_soc_update_bits(codec, comp_ctl0_reg, 0x01, 0x00); + snd_soc_update_bits(codec, comp_ctl0_reg, 0x04, 0x00); + } + + return 0; +} + +static void tavil_codec_idle_detect_control(struct snd_soc_codec *codec, + int interp, int event) +{ + int reg = 0, mask, val; + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + + if (!tavil->idle_det_cfg.hph_idle_detect_en) + return; + + if (interp == INTERP_HPHL) { + reg = WCD934X_CDC_RX_IDLE_DET_PATH_CTL; + mask = 0x01; + val = 0x01; + } + if (interp == INTERP_HPHR) { + reg = WCD934X_CDC_RX_IDLE_DET_PATH_CTL; + mask = 0x02; + val = 0x02; + } + + if (reg && SND_SOC_DAPM_EVENT_ON(event)) + snd_soc_update_bits(codec, reg, mask, val); + + if (reg && SND_SOC_DAPM_EVENT_OFF(event)) { + snd_soc_update_bits(codec, reg, mask, 0x00); + tavil->idle_det_cfg.hph_idle_thr = 0; + snd_soc_write(codec, WCD934X_CDC_RX_IDLE_DET_CFG3, 0x0); + } +} + +/** + * tavil_codec_enable_interp_clk - Enable main path Interpolator + * clock. + * + * @codec: Codec instance + * @event: Indicates speaker path gain offset value + * @intp_idx: Interpolator index + * Returns number of main clock users + */ +int tavil_codec_enable_interp_clk(struct snd_soc_codec *codec, + int event, int interp_idx) +{ + struct tavil_priv *tavil; + u16 main_reg; + + if (!codec) { + pr_err("%s: codec is NULL\n", __func__); + return -EINVAL; + } + + tavil = snd_soc_codec_get_drvdata(codec); + main_reg = WCD934X_CDC_RX0_RX_PATH_CTL + (interp_idx * 20); + + if (SND_SOC_DAPM_EVENT_ON(event)) { + if (tavil->main_clk_users[interp_idx] == 0) { + /* Main path PGA mute enable */ + snd_soc_update_bits(codec, main_reg, 0x10, 0x10); + /* Clk enable */ + snd_soc_update_bits(codec, main_reg, 0x20, 0x20); + tavil_codec_idle_detect_control(codec, interp_idx, + event); + tavil_codec_hd2_control(tavil, interp_idx, event); + tavil_codec_hphdelay_lutbypass(codec, interp_idx, + event); + tavil_config_compander(codec, interp_idx, event); + } + tavil->main_clk_users[interp_idx]++; + } + + if (SND_SOC_DAPM_EVENT_OFF(event)) { + tavil->main_clk_users[interp_idx]--; + if (tavil->main_clk_users[interp_idx] <= 0) { + tavil->main_clk_users[interp_idx] = 0; + tavil_config_compander(codec, interp_idx, event); + tavil_codec_hphdelay_lutbypass(codec, interp_idx, + event); + tavil_codec_hd2_control(tavil, interp_idx, event); + tavil_codec_idle_detect_control(codec, interp_idx, + event); + /* Clk Disable */ + snd_soc_update_bits(codec, main_reg, 0x20, 0x00); + /* Reset enable and disable */ + snd_soc_update_bits(codec, main_reg, 0x40, 0x40); + snd_soc_update_bits(codec, main_reg, 0x40, 0x00); + /* Reset rate to 48K*/ + snd_soc_update_bits(codec, main_reg, 0x0F, 0x04); + } + } + + dev_dbg(codec->dev, "%s event %d main_clk_users %d\n", + __func__, event, tavil->main_clk_users[interp_idx]); + + return tavil->main_clk_users[interp_idx]; +} +EXPORT_SYMBOL(tavil_codec_enable_interp_clk); + +static int tavil_anc_out_switch_cb(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + + tavil_codec_enable_interp_clk(codec, event, w->shift); + + return 0; +} +static int tavil_codec_set_idle_detect_thr(struct snd_soc_codec *codec, + int interp, int path_type) +{ + int port_id[4] = { 0, 0, 0, 0 }; + int *port_ptr, num_ports; + int bit_width = 0, i; + int mux_reg, mux_reg_val; + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + int dai_id, idle_thr; + + if ((interp != INTERP_HPHL) && (interp != INTERP_HPHR)) + return 0; + + if (!tavil->idle_det_cfg.hph_idle_detect_en) + return 0; + + port_ptr = &port_id[0]; + num_ports = 0; + + /* + * Read interpolator MUX input registers and find + * which slimbus port is connected and store the port + * numbers in port_id array. + */ + if (path_type == INTERP_MIX_PATH) { + mux_reg = WCD934X_CDC_RX_INP_MUX_RX_INT1_CFG1 + + 2 * (interp - 1); + mux_reg_val = snd_soc_read(codec, mux_reg) & 0x0f; + + if ((mux_reg_val >= INTn_2_INP_SEL_RX0) && + (mux_reg_val < INTn_2_INP_SEL_PROXIMITY)) { + *port_ptr++ = mux_reg_val + + WCD934X_RX_PORT_START_NUMBER - 1; + num_ports++; + } + } + + if (path_type == INTERP_MAIN_PATH) { + mux_reg = WCD934X_CDC_RX_INP_MUX_RX_INT1_CFG0 + + 2 * (interp - 1); + mux_reg_val = snd_soc_read(codec, mux_reg) & 0x0f; + i = WCD934X_INTERP_MUX_NUM_INPUTS; + + while (i) { + if ((mux_reg_val >= INTn_1_INP_SEL_RX0) && + (mux_reg_val <= INTn_1_INP_SEL_RX7)) { + *port_ptr++ = mux_reg_val + + WCD934X_RX_PORT_START_NUMBER - + INTn_1_INP_SEL_RX0; + num_ports++; + } + mux_reg_val = (snd_soc_read(codec, mux_reg) & + 0xf0) >> 4; + mux_reg += 1; + i--; + } + } + + dev_dbg(codec->dev, "%s: num_ports: %d, ports[%d %d %d %d]\n", + __func__, num_ports, port_id[0], port_id[1], + port_id[2], port_id[3]); + + i = 0; + while (num_ports) { + dai_id = tavil_find_playback_dai_id_for_port(port_id[i++], + tavil); + + if ((dai_id >= 0) && (dai_id < NUM_CODEC_DAIS)) { + dev_dbg(codec->dev, "%s: dai_id: %d bit_width: %d\n", + __func__, dai_id, + tavil->dai[dai_id].bit_width); + + if (tavil->dai[dai_id].bit_width > bit_width) + bit_width = tavil->dai[dai_id].bit_width; + } + + num_ports--; + } + + switch (bit_width) { + case 16: + idle_thr = 0xff; /* F16 */ + break; + case 24: + case 32: + idle_thr = 0x03; /* F22 */ + break; + default: + idle_thr = 0x00; + break; + } + + dev_dbg(codec->dev, "%s: (new) idle_thr: %d, (cur) idle_thr: %d\n", + __func__, idle_thr, tavil->idle_det_cfg.hph_idle_thr); + + if ((tavil->idle_det_cfg.hph_idle_thr == 0) || + (idle_thr < tavil->idle_det_cfg.hph_idle_thr)) { + snd_soc_write(codec, WCD934X_CDC_RX_IDLE_DET_CFG3, idle_thr); + tavil->idle_det_cfg.hph_idle_thr = idle_thr; + } + + return 0; +} + +static int tavil_codec_enable_mix_path(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + u16 gain_reg, mix_reg; + int offset_val = 0; + int val = 0; + + if (w->shift >= WCD934X_NUM_INTERPOLATORS || + w->shift == INTERP_LO3_NA || w->shift == INTERP_LO4_NA) { + dev_err(codec->dev, "%s: Invalid Interpolator value %d for name %s\n", + __func__, w->shift, w->name); + return -EINVAL; + }; + + gain_reg = WCD934X_CDC_RX0_RX_VOL_MIX_CTL + + (w->shift * WCD934X_RX_PATH_CTL_OFFSET); + mix_reg = WCD934X_CDC_RX0_RX_PATH_MIX_CTL + + (w->shift * WCD934X_RX_PATH_CTL_OFFSET); + + if (w->shift == INTERP_SPKR1 || w->shift == INTERP_SPKR2) + __tavil_codec_enable_swr(w, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + tavil_codec_set_idle_detect_thr(codec, w->shift, + INTERP_MIX_PATH); + tavil_codec_enable_interp_clk(codec, event, w->shift); + /* Clk enable */ + snd_soc_update_bits(codec, mix_reg, 0x20, 0x20); + break; + case SND_SOC_DAPM_POST_PMU: + if ((tavil->swr.spkr_gain_offset == + WCD934X_RX_GAIN_OFFSET_M1P5_DB) && + (tavil->comp_enabled[COMPANDER_7] || + tavil->comp_enabled[COMPANDER_8]) && + (gain_reg == WCD934X_CDC_RX7_RX_VOL_MIX_CTL || + gain_reg == WCD934X_CDC_RX8_RX_VOL_MIX_CTL)) { + snd_soc_update_bits(codec, WCD934X_CDC_RX7_RX_PATH_SEC1, + 0x01, 0x01); + snd_soc_update_bits(codec, + WCD934X_CDC_RX7_RX_PATH_MIX_SEC0, + 0x01, 0x01); + snd_soc_update_bits(codec, WCD934X_CDC_RX8_RX_PATH_SEC1, + 0x01, 0x01); + snd_soc_update_bits(codec, + WCD934X_CDC_RX8_RX_PATH_MIX_SEC0, + 0x01, 0x01); + offset_val = -2; + } + val = snd_soc_read(codec, gain_reg); + val += offset_val; + snd_soc_write(codec, gain_reg, val); + tavil_codec_config_ear_spkr_gain(codec, event, gain_reg); + break; + case SND_SOC_DAPM_POST_PMD: + /* Clk Disable */ + snd_soc_update_bits(codec, mix_reg, 0x20, 0x00); + tavil_codec_enable_interp_clk(codec, event, w->shift); + /* Reset enable and disable */ + snd_soc_update_bits(codec, mix_reg, 0x40, 0x40); + snd_soc_update_bits(codec, mix_reg, 0x40, 0x00); + + if ((tavil->swr.spkr_gain_offset == + WCD934X_RX_GAIN_OFFSET_M1P5_DB) && + (tavil->comp_enabled[COMPANDER_7] || + tavil->comp_enabled[COMPANDER_8]) && + (gain_reg == WCD934X_CDC_RX7_RX_VOL_MIX_CTL || + gain_reg == WCD934X_CDC_RX8_RX_VOL_MIX_CTL)) { + snd_soc_update_bits(codec, WCD934X_CDC_RX7_RX_PATH_SEC1, + 0x01, 0x00); + snd_soc_update_bits(codec, + WCD934X_CDC_RX7_RX_PATH_MIX_SEC0, + 0x01, 0x00); + snd_soc_update_bits(codec, WCD934X_CDC_RX8_RX_PATH_SEC1, + 0x01, 0x00); + snd_soc_update_bits(codec, + WCD934X_CDC_RX8_RX_PATH_MIX_SEC0, + 0x01, 0x00); + offset_val = 2; + val = snd_soc_read(codec, gain_reg); + val += offset_val; + snd_soc_write(codec, gain_reg, val); + } + tavil_codec_config_ear_spkr_gain(codec, event, gain_reg); + break; + }; + dev_dbg(codec->dev, "%s event %d name %s\n", __func__, event, w->name); + + return 0; +} + +/** + * tavil_get_dsd_config - Get pointer to dsd config structure + * + * @codec: pointer to snd_soc_codec structure + * + * Returns pointer to tavil_dsd_config structure + */ +struct tavil_dsd_config *tavil_get_dsd_config(struct snd_soc_codec *codec) +{ + struct tavil_priv *tavil; + + if (!codec) + return NULL; + + tavil = snd_soc_codec_get_drvdata(codec); + + if (!tavil) + return NULL; + + return tavil->dsd_config; +} +EXPORT_SYMBOL(tavil_get_dsd_config); + +static int tavil_codec_enable_main_path(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + u16 gain_reg; + u16 reg; + int val; + int offset_val = 0; + + dev_dbg(codec->dev, "%s %d %s\n", __func__, event, w->name); + + if (w->shift >= WCD934X_NUM_INTERPOLATORS || + w->shift == INTERP_LO3_NA || w->shift == INTERP_LO4_NA) { + dev_err(codec->dev, "%s: Invalid Interpolator value %d for name %s\n", + __func__, w->shift, w->name); + return -EINVAL; + }; + + reg = WCD934X_CDC_RX0_RX_PATH_CTL + (w->shift * + WCD934X_RX_PATH_CTL_OFFSET); + gain_reg = WCD934X_CDC_RX0_RX_VOL_CTL + (w->shift * + WCD934X_RX_PATH_CTL_OFFSET); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + tavil_codec_set_idle_detect_thr(codec, w->shift, + INTERP_MAIN_PATH); + tavil_codec_enable_interp_clk(codec, event, w->shift); + break; + case SND_SOC_DAPM_POST_PMU: + /* apply gain after int clk is enabled */ + if ((tavil->swr.spkr_gain_offset == + WCD934X_RX_GAIN_OFFSET_M1P5_DB) && + (tavil->comp_enabled[COMPANDER_7] || + tavil->comp_enabled[COMPANDER_8]) && + (gain_reg == WCD934X_CDC_RX7_RX_VOL_CTL || + gain_reg == WCD934X_CDC_RX8_RX_VOL_CTL)) { + snd_soc_update_bits(codec, WCD934X_CDC_RX7_RX_PATH_SEC1, + 0x01, 0x01); + snd_soc_update_bits(codec, + WCD934X_CDC_RX7_RX_PATH_MIX_SEC0, + 0x01, 0x01); + snd_soc_update_bits(codec, WCD934X_CDC_RX8_RX_PATH_SEC1, + 0x01, 0x01); + snd_soc_update_bits(codec, + WCD934X_CDC_RX8_RX_PATH_MIX_SEC0, + 0x01, 0x01); + offset_val = -2; + } + val = snd_soc_read(codec, gain_reg); + val += offset_val; + snd_soc_write(codec, gain_reg, val); + tavil_codec_config_ear_spkr_gain(codec, event, gain_reg); + break; + case SND_SOC_DAPM_POST_PMD: + tavil_codec_enable_interp_clk(codec, event, w->shift); + + if ((tavil->swr.spkr_gain_offset == + WCD934X_RX_GAIN_OFFSET_M1P5_DB) && + (tavil->comp_enabled[COMPANDER_7] || + tavil->comp_enabled[COMPANDER_8]) && + (gain_reg == WCD934X_CDC_RX7_RX_VOL_CTL || + gain_reg == WCD934X_CDC_RX8_RX_VOL_CTL)) { + snd_soc_update_bits(codec, WCD934X_CDC_RX7_RX_PATH_SEC1, + 0x01, 0x00); + snd_soc_update_bits(codec, + WCD934X_CDC_RX7_RX_PATH_MIX_SEC0, + 0x01, 0x00); + snd_soc_update_bits(codec, WCD934X_CDC_RX8_RX_PATH_SEC1, + 0x01, 0x00); + snd_soc_update_bits(codec, + WCD934X_CDC_RX8_RX_PATH_MIX_SEC0, + 0x01, 0x00); + offset_val = 2; + val = snd_soc_read(codec, gain_reg); + val += offset_val; + snd_soc_write(codec, gain_reg, val); + } + tavil_codec_config_ear_spkr_gain(codec, event, gain_reg); + break; + }; + + return 0; +} + +static int tavil_codec_set_iir_gain(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + + dev_dbg(codec->dev, "%s: event = %d\n", __func__, event); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: /* fall through */ + case SND_SOC_DAPM_PRE_PMD: + if (strnstr(w->name, "IIR0", sizeof("IIR0"))) { + snd_soc_write(codec, + WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B1_CTL, + snd_soc_read(codec, + WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B1_CTL)); + snd_soc_write(codec, + WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B2_CTL, + snd_soc_read(codec, + WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B2_CTL)); + snd_soc_write(codec, + WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B3_CTL, + snd_soc_read(codec, + WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B3_CTL)); + snd_soc_write(codec, + WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B4_CTL, + snd_soc_read(codec, + WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B4_CTL)); + } else { + snd_soc_write(codec, + WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B1_CTL, + snd_soc_read(codec, + WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B1_CTL)); + snd_soc_write(codec, + WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B2_CTL, + snd_soc_read(codec, + WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B2_CTL)); + snd_soc_write(codec, + WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B3_CTL, + snd_soc_read(codec, + WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B3_CTL)); + } + break; + } + return 0; +} + +static int tavil_codec_find_amic_input(struct snd_soc_codec *codec, + int adc_mux_n) +{ + u16 mask, shift, adc_mux_in_reg; + u16 amic_mux_sel_reg; + bool is_amic; + + if (adc_mux_n < 0 || adc_mux_n > WCD934X_MAX_VALID_ADC_MUX || + adc_mux_n == WCD934X_INVALID_ADC_MUX) + return 0; + + if (adc_mux_n < 3) { + adc_mux_in_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG1 + + 2 * adc_mux_n; + mask = 0x03; + shift = 0; + amic_mux_sel_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG0 + + 2 * adc_mux_n; + } else if (adc_mux_n < 4) { + adc_mux_in_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX3_CFG1; + mask = 0x03; + shift = 0; + amic_mux_sel_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG0 + + 2 * adc_mux_n; + } else if (adc_mux_n < 7) { + adc_mux_in_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG1 + + 2 * (adc_mux_n - 4); + mask = 0x0C; + shift = 2; + amic_mux_sel_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX4_CFG0 + + adc_mux_n - 4; + } else if (adc_mux_n < 8) { + adc_mux_in_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX3_CFG1; + mask = 0x0C; + shift = 2; + amic_mux_sel_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX4_CFG0 + + adc_mux_n - 4; + } else if (adc_mux_n < 12) { + adc_mux_in_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG1 + + 2 * (((adc_mux_n == 8) ? (adc_mux_n - 8) : + (adc_mux_n - 9))); + mask = 0x30; + shift = 4; + amic_mux_sel_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX8_CFG0 + + ((adc_mux_n == 8) ? (adc_mux_n - 8) : + (adc_mux_n - 9)); + } else if (adc_mux_n < 13) { + adc_mux_in_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX3_CFG1; + mask = 0x30; + shift = 4; + amic_mux_sel_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX4_CFG0 + + adc_mux_n - 5; + } else { + adc_mux_in_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG1; + mask = 0xC0; + shift = 6; + amic_mux_sel_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX4_CFG0 + + adc_mux_n - 5; + } + + is_amic = (((snd_soc_read(codec, adc_mux_in_reg) & mask) >> shift) + == 1); + if (!is_amic) + return 0; + + return snd_soc_read(codec, amic_mux_sel_reg) & 0x07; +} + +static void tavil_codec_set_tx_hold(struct snd_soc_codec *codec, + u16 amic_reg, bool set) +{ + u8 mask = 0x20; + u8 val; + + if (amic_reg == WCD934X_ANA_AMIC1 || + amic_reg == WCD934X_ANA_AMIC3) + mask = 0x40; + + val = set ? mask : 0x00; + + switch (amic_reg) { + case WCD934X_ANA_AMIC1: + case WCD934X_ANA_AMIC2: + snd_soc_update_bits(codec, WCD934X_ANA_AMIC2, mask, val); + break; + case WCD934X_ANA_AMIC3: + case WCD934X_ANA_AMIC4: + snd_soc_update_bits(codec, WCD934X_ANA_AMIC4, mask, val); + break; + default: + dev_dbg(codec->dev, "%s: invalid amic: %d\n", + __func__, amic_reg); + break; + } +} + +static int tavil_codec_tx_adc_cfg(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + int adc_mux_n = w->shift; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + int amic_n; + + dev_dbg(codec->dev, "%s: event: %d\n", __func__, event); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + amic_n = tavil_codec_find_amic_input(codec, adc_mux_n); + if (amic_n) { + /* + * Prevent ANC Rx pop by leaving Tx FE in HOLD + * state until PA is up. Track AMIC being used + * so we can release the HOLD later. + */ + set_bit(ANC_MIC_AMIC1 + amic_n - 1, + &tavil->status_mask); + } + break; + default: + break; + } + + return 0; +} + +static u16 tavil_codec_get_amic_pwlvl_reg(struct snd_soc_codec *codec, int amic) +{ + u16 pwr_level_reg = 0; + + switch (amic) { + case 1: + case 2: + pwr_level_reg = WCD934X_ANA_AMIC1; + break; + + case 3: + case 4: + pwr_level_reg = WCD934X_ANA_AMIC3; + break; + default: + dev_dbg(codec->dev, "%s: invalid amic: %d\n", + __func__, amic); + break; + } + + return pwr_level_reg; +} + +#define TX_HPF_CUT_OFF_FREQ_MASK 0x60 +#define CF_MIN_3DB_4HZ 0x0 +#define CF_MIN_3DB_75HZ 0x1 +#define CF_MIN_3DB_150HZ 0x2 + +static void tavil_tx_hpf_corner_freq_callback(struct work_struct *work) +{ + struct delayed_work *hpf_delayed_work; + struct hpf_work *hpf_work; + struct tavil_priv *tavil; + struct snd_soc_codec *codec; + u16 dec_cfg_reg, amic_reg, go_bit_reg; + u8 hpf_cut_off_freq; + int amic_n; + + hpf_delayed_work = to_delayed_work(work); + hpf_work = container_of(hpf_delayed_work, struct hpf_work, dwork); + tavil = hpf_work->tavil; + codec = tavil->codec; + hpf_cut_off_freq = hpf_work->hpf_cut_off_freq; + + dec_cfg_reg = WCD934X_CDC_TX0_TX_PATH_CFG0 + 16 * hpf_work->decimator; + go_bit_reg = dec_cfg_reg + 7; + + dev_dbg(codec->dev, "%s: decimator %u hpf_cut_of_freq 0x%x\n", + __func__, hpf_work->decimator, hpf_cut_off_freq); + + amic_n = tavil_codec_find_amic_input(codec, hpf_work->decimator); + if (amic_n) { + amic_reg = WCD934X_ANA_AMIC1 + amic_n - 1; + tavil_codec_set_tx_hold(codec, amic_reg, false); + } + snd_soc_update_bits(codec, dec_cfg_reg, TX_HPF_CUT_OFF_FREQ_MASK, + hpf_cut_off_freq << 5); + snd_soc_update_bits(codec, go_bit_reg, 0x02, 0x02); + /* Minimum 1 clk cycle delay is required as per HW spec */ + usleep_range(1000, 1010); + snd_soc_update_bits(codec, go_bit_reg, 0x02, 0x00); +} + +static void tavil_tx_mute_update_callback(struct work_struct *work) +{ + struct tx_mute_work *tx_mute_dwork; + struct tavil_priv *tavil; + struct delayed_work *delayed_work; + struct snd_soc_codec *codec; + u16 tx_vol_ctl_reg, hpf_gate_reg; + + delayed_work = to_delayed_work(work); + tx_mute_dwork = container_of(delayed_work, struct tx_mute_work, dwork); + tavil = tx_mute_dwork->tavil; + codec = tavil->codec; + + tx_vol_ctl_reg = WCD934X_CDC_TX0_TX_PATH_CTL + + 16 * tx_mute_dwork->decimator; + hpf_gate_reg = WCD934X_CDC_TX0_TX_PATH_SEC2 + + 16 * tx_mute_dwork->decimator; + snd_soc_update_bits(codec, tx_vol_ctl_reg, 0x10, 0x00); +} + +static int tavil_codec_enable_rx_path_clk(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + u16 sidetone_reg; + + dev_dbg(codec->dev, "%s %d %d\n", __func__, event, w->shift); + sidetone_reg = WCD934X_CDC_RX0_RX_PATH_CFG1 + 0x14*(w->shift); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (!strcmp(w->name, "RX INT7 MIX2 INP")) + __tavil_codec_enable_swr(w, event); + tavil_codec_enable_interp_clk(codec, event, w->shift); + snd_soc_update_bits(codec, sidetone_reg, 0x10, 0x10); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_update_bits(codec, sidetone_reg, 0x10, 0x00); + tavil_codec_enable_interp_clk(codec, event, w->shift); + if (!strcmp(w->name, "RX INT7 MIX2 INP")) + __tavil_codec_enable_swr(w, event); + break; + default: + break; + }; + return 0; +} + +static int tavil_codec_enable_dec(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + unsigned int decimator; + char *dec_adc_mux_name = NULL; + char *widget_name = NULL; + char *wname; + int ret = 0, amic_n; + u16 tx_vol_ctl_reg, pwr_level_reg = 0, dec_cfg_reg, hpf_gate_reg; + u16 tx_gain_ctl_reg; + char *dec; + u8 hpf_cut_off_freq; + + dev_dbg(codec->dev, "%s %d\n", __func__, event); + + widget_name = kstrndup(w->name, 15, GFP_KERNEL); + if (!widget_name) + return -ENOMEM; + + wname = widget_name; + dec_adc_mux_name = strsep(&widget_name, " "); + if (!dec_adc_mux_name) { + dev_err(codec->dev, "%s: Invalid decimator = %s\n", + __func__, w->name); + ret = -EINVAL; + goto out; + } + dec_adc_mux_name = widget_name; + + dec = strpbrk(dec_adc_mux_name, "012345678"); + if (!dec) { + dev_err(codec->dev, "%s: decimator index not found\n", + __func__); + ret = -EINVAL; + goto out; + } + + ret = kstrtouint(dec, 10, &decimator); + if (ret < 0) { + dev_err(codec->dev, "%s: Invalid decimator = %s\n", + __func__, wname); + ret = -EINVAL; + goto out; + } + + dev_dbg(codec->dev, "%s(): widget = %s decimator = %u\n", __func__, + w->name, decimator); + + tx_vol_ctl_reg = WCD934X_CDC_TX0_TX_PATH_CTL + 16 * decimator; + hpf_gate_reg = WCD934X_CDC_TX0_TX_PATH_SEC2 + 16 * decimator; + dec_cfg_reg = WCD934X_CDC_TX0_TX_PATH_CFG0 + 16 * decimator; + tx_gain_ctl_reg = WCD934X_CDC_TX0_TX_VOL_CTL + 16 * decimator; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + amic_n = tavil_codec_find_amic_input(codec, decimator); + if (amic_n) + pwr_level_reg = tavil_codec_get_amic_pwlvl_reg(codec, + amic_n); + + if (pwr_level_reg) { + switch ((snd_soc_read(codec, pwr_level_reg) & + WCD934X_AMIC_PWR_LVL_MASK) >> + WCD934X_AMIC_PWR_LVL_SHIFT) { + case WCD934X_AMIC_PWR_LEVEL_LP: + snd_soc_update_bits(codec, dec_cfg_reg, + WCD934X_DEC_PWR_LVL_MASK, + WCD934X_DEC_PWR_LVL_LP); + break; + + case WCD934X_AMIC_PWR_LEVEL_HP: + snd_soc_update_bits(codec, dec_cfg_reg, + WCD934X_DEC_PWR_LVL_MASK, + WCD934X_DEC_PWR_LVL_HP); + break; + case WCD934X_AMIC_PWR_LEVEL_DEFAULT: + case WCD934X_AMIC_PWR_LEVEL_HYBRID: + default: + snd_soc_update_bits(codec, dec_cfg_reg, + WCD934X_DEC_PWR_LVL_MASK, + WCD934X_DEC_PWR_LVL_DF); + break; + } + } + /* Enable TX PGA Mute */ + snd_soc_update_bits(codec, tx_vol_ctl_reg, 0x10, 0x10); + break; + case SND_SOC_DAPM_POST_PMU: + hpf_cut_off_freq = (snd_soc_read(codec, dec_cfg_reg) & + TX_HPF_CUT_OFF_FREQ_MASK) >> 5; + + tavil->tx_hpf_work[decimator].hpf_cut_off_freq = + hpf_cut_off_freq; + if (hpf_cut_off_freq != CF_MIN_3DB_150HZ) { + snd_soc_update_bits(codec, dec_cfg_reg, + TX_HPF_CUT_OFF_FREQ_MASK, + CF_MIN_3DB_150HZ << 5); + snd_soc_update_bits(codec, hpf_gate_reg, 0x02, 0x02); + /* + * Minimum 1 clk cycle delay is required as per + * HW spec. + */ + usleep_range(1000, 1010); + snd_soc_update_bits(codec, hpf_gate_reg, 0x02, 0x00); + } + /* schedule work queue to Remove Mute */ + schedule_delayed_work(&tavil->tx_mute_dwork[decimator].dwork, + msecs_to_jiffies(tx_unmute_delay)); + if (tavil->tx_hpf_work[decimator].hpf_cut_off_freq != + CF_MIN_3DB_150HZ) + schedule_delayed_work( + &tavil->tx_hpf_work[decimator].dwork, + msecs_to_jiffies(300)); + /* apply gain after decimator is enabled */ + snd_soc_write(codec, tx_gain_ctl_reg, + snd_soc_read(codec, tx_gain_ctl_reg)); + break; + case SND_SOC_DAPM_PRE_PMD: + hpf_cut_off_freq = + tavil->tx_hpf_work[decimator].hpf_cut_off_freq; + snd_soc_update_bits(codec, tx_vol_ctl_reg, 0x10, 0x10); + if (cancel_delayed_work_sync( + &tavil->tx_hpf_work[decimator].dwork)) { + if (hpf_cut_off_freq != CF_MIN_3DB_150HZ) { + snd_soc_update_bits(codec, dec_cfg_reg, + TX_HPF_CUT_OFF_FREQ_MASK, + hpf_cut_off_freq << 5); + snd_soc_update_bits(codec, hpf_gate_reg, + 0x02, 0x02); + /* + * Minimum 1 clk cycle delay is required as per + * HW spec. + */ + usleep_range(1000, 1010); + snd_soc_update_bits(codec, hpf_gate_reg, + 0x02, 0x00); + } + } + cancel_delayed_work_sync( + &tavil->tx_mute_dwork[decimator].dwork); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_update_bits(codec, tx_vol_ctl_reg, 0x10, 0x00); + snd_soc_update_bits(codec, dec_cfg_reg, + WCD934X_DEC_PWR_LVL_MASK, + WCD934X_DEC_PWR_LVL_DF); + break; + }; +out: + kfree(wname); + return ret; +} + +static u32 tavil_get_dmic_sample_rate(struct snd_soc_codec *codec, + unsigned int dmic, + struct wcd9xxx_pdata *pdata) +{ + u8 tx_stream_fs; + u8 adc_mux_index = 0, adc_mux_sel = 0; + bool dec_found = false; + u16 adc_mux_ctl_reg, tx_fs_reg; + u32 dmic_fs; + + while (dec_found == 0 && adc_mux_index < WCD934X_MAX_VALID_ADC_MUX) { + if (adc_mux_index < 4) { + adc_mux_ctl_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG0 + + (adc_mux_index * 2); + } else if (adc_mux_index < WCD934X_INVALID_ADC_MUX) { + adc_mux_ctl_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX4_CFG0 + + adc_mux_index - 4; + } else if (adc_mux_index == WCD934X_INVALID_ADC_MUX) { + ++adc_mux_index; + continue; + } + adc_mux_sel = ((snd_soc_read(codec, adc_mux_ctl_reg) & + 0xF8) >> 3) - 1; + + if (adc_mux_sel == dmic) { + dec_found = true; + break; + } + + ++adc_mux_index; + } + + if (dec_found && adc_mux_index <= 8) { + tx_fs_reg = WCD934X_CDC_TX0_TX_PATH_CTL + (16 * adc_mux_index); + tx_stream_fs = snd_soc_read(codec, tx_fs_reg) & 0x0F; + if (tx_stream_fs <= 4) { + if (pdata->dmic_sample_rate <= + WCD9XXX_DMIC_SAMPLE_RATE_2P4MHZ) + dmic_fs = pdata->dmic_sample_rate; + else + dmic_fs = WCD9XXX_DMIC_SAMPLE_RATE_2P4MHZ; + } else + dmic_fs = WCD9XXX_DMIC_SAMPLE_RATE_4P8MHZ; + } else { + dmic_fs = pdata->dmic_sample_rate; + } + + return dmic_fs; +} + +static u8 tavil_get_dmic_clk_val(struct snd_soc_codec *codec, + u32 mclk_rate, u32 dmic_clk_rate) +{ + u32 div_factor; + u8 dmic_ctl_val; + + dev_dbg(codec->dev, + "%s: mclk_rate = %d, dmic_sample_rate = %d\n", + __func__, mclk_rate, dmic_clk_rate); + + /* Default value to return in case of error */ + if (mclk_rate == WCD934X_MCLK_CLK_9P6MHZ) + dmic_ctl_val = WCD934X_DMIC_CLK_DIV_2; + else + dmic_ctl_val = WCD934X_DMIC_CLK_DIV_3; + + if (dmic_clk_rate == 0) { + dev_err(codec->dev, + "%s: dmic_sample_rate cannot be 0\n", + __func__); + goto done; + } + + div_factor = mclk_rate / dmic_clk_rate; + switch (div_factor) { + case 2: + dmic_ctl_val = WCD934X_DMIC_CLK_DIV_2; + break; + case 3: + dmic_ctl_val = WCD934X_DMIC_CLK_DIV_3; + break; + case 4: + dmic_ctl_val = WCD934X_DMIC_CLK_DIV_4; + break; + case 6: + dmic_ctl_val = WCD934X_DMIC_CLK_DIV_6; + break; + case 8: + dmic_ctl_val = WCD934X_DMIC_CLK_DIV_8; + break; + case 16: + dmic_ctl_val = WCD934X_DMIC_CLK_DIV_16; + break; + default: + dev_err(codec->dev, + "%s: Invalid div_factor %u, clk_rate(%u), dmic_rate(%u)\n", + __func__, div_factor, mclk_rate, dmic_clk_rate); + break; + } + +done: + return dmic_ctl_val; +} + +static int tavil_codec_enable_adc(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + + dev_dbg(codec->dev, "%s: event:%d\n", __func__, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + tavil_codec_set_tx_hold(codec, w->reg, true); + break; + default: + break; + } + + return 0; +} + +static int tavil_codec_enable_dmic(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + struct wcd9xxx_pdata *pdata = dev_get_platdata(codec->dev->parent); + u8 dmic_clk_en = 0x01; + u16 dmic_clk_reg; + s32 *dmic_clk_cnt; + u8 dmic_rate_val, dmic_rate_shift = 1; + unsigned int dmic; + u32 dmic_sample_rate; + int ret; + char *wname; + + wname = strpbrk(w->name, "012345"); + if (!wname) { + dev_err(codec->dev, "%s: widget not found\n", __func__); + return -EINVAL; + } + + ret = kstrtouint(wname, 10, &dmic); + if (ret < 0) { + dev_err(codec->dev, "%s: Invalid DMIC line on the codec\n", + __func__); + return -EINVAL; + } + + switch (dmic) { + case 0: + case 1: + dmic_clk_cnt = &(tavil->dmic_0_1_clk_cnt); + dmic_clk_reg = WCD934X_CPE_SS_DMIC0_CTL; + break; + case 2: + case 3: + dmic_clk_cnt = &(tavil->dmic_2_3_clk_cnt); + dmic_clk_reg = WCD934X_CPE_SS_DMIC1_CTL; + break; + case 4: + case 5: + dmic_clk_cnt = &(tavil->dmic_4_5_clk_cnt); + dmic_clk_reg = WCD934X_CPE_SS_DMIC2_CTL; + break; + default: + dev_err(codec->dev, "%s: Invalid DMIC Selection\n", + __func__); + return -EINVAL; + }; + dev_dbg(codec->dev, "%s: event %d DMIC%d dmic_clk_cnt %d\n", + __func__, event, dmic, *dmic_clk_cnt); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + dmic_sample_rate = tavil_get_dmic_sample_rate(codec, dmic, + pdata); + dmic_rate_val = + tavil_get_dmic_clk_val(codec, + pdata->mclk_rate, + dmic_sample_rate); + + snd_soc_update_bits(codec, WCD934X_TEST_DEBUG_PAD_DRVCTL_0, + WCD934X_DMIC_DRV_CTL_MASK, + tavil->dmic_drv_ctl << + WCD934X_DMIC_DRV_CTL_SHIFT); + + (*dmic_clk_cnt)++; + if (*dmic_clk_cnt == 1) { + snd_soc_update_bits(codec, dmic_clk_reg, + 0x07 << dmic_rate_shift, + dmic_rate_val << dmic_rate_shift); + snd_soc_update_bits(codec, dmic_clk_reg, + dmic_clk_en, dmic_clk_en); + } + + break; + case SND_SOC_DAPM_POST_PMD: + dmic_rate_val = + tavil_get_dmic_clk_val(codec, + pdata->mclk_rate, + pdata->mad_dmic_sample_rate); + (*dmic_clk_cnt)--; + if (*dmic_clk_cnt == 0) { + snd_soc_update_bits(codec, dmic_clk_reg, + dmic_clk_en, 0); + snd_soc_update_bits(codec, dmic_clk_reg, + 0x07 << dmic_rate_shift, + dmic_rate_val << dmic_rate_shift); + } + break; + }; + + return 0; +} + +/* + * tavil_mbhc_micb_adjust_voltage: adjust specific micbias voltage + * @codec: handle to snd_soc_codec * + * @req_volt: micbias voltage to be set + * @micb_num: micbias to be set, e.g. micbias1 or micbias2 + * + * return 0 if adjustment is success or error code in case of failure + */ +int tavil_mbhc_micb_adjust_voltage(struct snd_soc_codec *codec, + int req_volt, int micb_num) +{ + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + int cur_vout_ctl, req_vout_ctl; + int micb_reg, micb_val, micb_en; + int ret = 0; + + switch (micb_num) { + case MIC_BIAS_1: + micb_reg = WCD934X_ANA_MICB1; + break; + case MIC_BIAS_2: + micb_reg = WCD934X_ANA_MICB2; + break; + case MIC_BIAS_3: + micb_reg = WCD934X_ANA_MICB3; + break; + case MIC_BIAS_4: + micb_reg = WCD934X_ANA_MICB4; + break; + default: + return -EINVAL; + } + mutex_lock(&tavil->micb_lock); + + /* + * If requested micbias voltage is same as current micbias + * voltage, then just return. Otherwise, adjust voltage as + * per requested value. If micbias is already enabled, then + * to avoid slow micbias ramp-up or down enable pull-up + * momentarily, change the micbias value and then re-enable + * micbias. + */ + micb_val = snd_soc_read(codec, micb_reg); + micb_en = (micb_val & 0xC0) >> 6; + cur_vout_ctl = micb_val & 0x3F; + + req_vout_ctl = wcd934x_get_micb_vout_ctl_val(req_volt); + if (req_vout_ctl < 0) { + ret = -EINVAL; + goto exit; + } + if (cur_vout_ctl == req_vout_ctl) { + ret = 0; + goto exit; + } + + dev_dbg(codec->dev, "%s: micb_num: %d, cur_mv: %d, req_mv: %d, micb_en: %d\n", + __func__, micb_num, WCD_VOUT_CTL_TO_MICB(cur_vout_ctl), + req_volt, micb_en); + + if (micb_en == 0x1) + snd_soc_update_bits(codec, micb_reg, 0xC0, 0x80); + + snd_soc_update_bits(codec, micb_reg, 0x3F, req_vout_ctl); + + if (micb_en == 0x1) { + snd_soc_update_bits(codec, micb_reg, 0xC0, 0x40); + /* + * Add 2ms delay as per HW requirement after enabling + * micbias + */ + usleep_range(2000, 2100); + } +exit: + mutex_unlock(&tavil->micb_lock); + return ret; +} +EXPORT_SYMBOL(tavil_mbhc_micb_adjust_voltage); + +/* + * tavil_micbias_control: enable/disable micbias + * @codec: handle to snd_soc_codec * + * @micb_num: micbias to be enabled/disabled, e.g. micbias1 or micbias2 + * @req: control requested, enable/disable or pullup enable/disable + * @is_dapm: triggered by dapm or not + * + * return 0 if control is success or error code in case of failure + */ +int tavil_micbias_control(struct snd_soc_codec *codec, + int micb_num, int req, bool is_dapm) +{ + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + int micb_index = micb_num - 1; + u16 micb_reg; + int pre_off_event = 0, post_off_event = 0; + int post_on_event = 0, post_dapm_off = 0; + int post_dapm_on = 0; + + if ((micb_index < 0) || (micb_index > TAVIL_MAX_MICBIAS - 1)) { + dev_err(codec->dev, "%s: Invalid micbias index, micb_ind:%d\n", + __func__, micb_index); + return -EINVAL; + } + + switch (micb_num) { + case MIC_BIAS_1: + micb_reg = WCD934X_ANA_MICB1; + break; + case MIC_BIAS_2: + micb_reg = WCD934X_ANA_MICB2; + pre_off_event = WCD_EVENT_PRE_MICBIAS_2_OFF; + post_off_event = WCD_EVENT_POST_MICBIAS_2_OFF; + post_on_event = WCD_EVENT_POST_MICBIAS_2_ON; + post_dapm_on = WCD_EVENT_POST_DAPM_MICBIAS_2_ON; + post_dapm_off = WCD_EVENT_POST_DAPM_MICBIAS_2_OFF; + break; + case MIC_BIAS_3: + micb_reg = WCD934X_ANA_MICB3; + break; + case MIC_BIAS_4: + micb_reg = WCD934X_ANA_MICB4; + break; + default: + dev_err(codec->dev, "%s: Invalid micbias number: %d\n", + __func__, micb_num); + return -EINVAL; + } + mutex_lock(&tavil->micb_lock); + + switch (req) { + case MICB_PULLUP_ENABLE: + tavil->pullup_ref[micb_index]++; + if ((tavil->pullup_ref[micb_index] == 1) && + (tavil->micb_ref[micb_index] == 0)) + snd_soc_update_bits(codec, micb_reg, 0xC0, 0x80); + break; + case MICB_PULLUP_DISABLE: + if (tavil->pullup_ref[micb_index] > 0) + tavil->pullup_ref[micb_index]--; + if ((tavil->pullup_ref[micb_index] == 0) && + (tavil->micb_ref[micb_index] == 0)) + snd_soc_update_bits(codec, micb_reg, 0xC0, 0x00); + break; + case MICB_ENABLE: + tavil->micb_ref[micb_index]++; + if (tavil->micb_ref[micb_index] == 1) { + if (tavil->micb_load) + regulator_set_load(tavil->micb_load, + tavil->micb_load_high); + snd_soc_update_bits(codec, micb_reg, 0xC0, 0x40); + if (post_on_event && tavil->mbhc) + blocking_notifier_call_chain( + &tavil->mbhc->notifier, + post_on_event, + &tavil->mbhc->wcd_mbhc); + } + if (is_dapm && post_dapm_on && tavil->mbhc) + blocking_notifier_call_chain(&tavil->mbhc->notifier, + post_dapm_on, &tavil->mbhc->wcd_mbhc); + break; + case MICB_DISABLE: + if (tavil->micb_ref[micb_index] > 0) + tavil->micb_ref[micb_index]--; + if ((tavil->micb_ref[micb_index] == 0) && + (tavil->pullup_ref[micb_index] > 0)) + snd_soc_update_bits(codec, micb_reg, 0xC0, 0x80); + else if ((tavil->micb_ref[micb_index] == 0) && + (tavil->pullup_ref[micb_index] == 0)) { + if (pre_off_event && tavil->mbhc) + blocking_notifier_call_chain( + &tavil->mbhc->notifier, + pre_off_event, + &tavil->mbhc->wcd_mbhc); + snd_soc_update_bits(codec, micb_reg, 0xC0, 0x00); + if (post_off_event && tavil->mbhc) + blocking_notifier_call_chain( + &tavil->mbhc->notifier, + post_off_event, + &tavil->mbhc->wcd_mbhc); + if (tavil->micb_load) + regulator_set_load(tavil->micb_load, + tavil->micb_load_low); + } + if (is_dapm && post_dapm_off && tavil->mbhc) + blocking_notifier_call_chain(&tavil->mbhc->notifier, + post_dapm_off, &tavil->mbhc->wcd_mbhc); + break; + }; + + dev_dbg(codec->dev, "%s: micb_num:%d, micb_ref: %d, pullup_ref: %d\n", + __func__, micb_num, tavil->micb_ref[micb_index], + tavil->pullup_ref[micb_index]); + + mutex_unlock(&tavil->micb_lock); + + return 0; +} +EXPORT_SYMBOL(tavil_micbias_control); + +static int __tavil_codec_enable_micbias(struct snd_soc_dapm_widget *w, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + int micb_num; + + dev_dbg(codec->dev, "%s: wname: %s, event: %d\n", + __func__, w->name, event); + + if (strnstr(w->name, "MIC BIAS1", sizeof("MIC BIAS1"))) + micb_num = MIC_BIAS_1; + else if (strnstr(w->name, "MIC BIAS2", sizeof("MIC BIAS2"))) + micb_num = MIC_BIAS_2; + else if (strnstr(w->name, "MIC BIAS3", sizeof("MIC BIAS3"))) + micb_num = MIC_BIAS_3; + else if (strnstr(w->name, "MIC BIAS4", sizeof("MIC BIAS4"))) + micb_num = MIC_BIAS_4; + else + return -EINVAL; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* + * MIC BIAS can also be requested by MBHC, + * so use ref count to handle micbias pullup + * and enable requests + */ + tavil_micbias_control(codec, micb_num, MICB_ENABLE, true); + break; + case SND_SOC_DAPM_POST_PMU: + /* wait for cnp time */ + usleep_range(1000, 1100); + break; + case SND_SOC_DAPM_POST_PMD: + tavil_micbias_control(codec, micb_num, MICB_DISABLE, true); + break; + }; + + return 0; +} + +/* + * tavil_codec_enable_standalone_micbias - enable micbias standalone + * @codec: pointer to codec instance + * @micb_num: number of micbias to be enabled + * @enable: true to enable micbias or false to disable + * + * This function is used to enable micbias (1, 2, 3 or 4) during + * standalone independent of whether TX use-case is running or not + * + * Return: error code in case of failure or 0 for success + */ +int tavil_codec_enable_standalone_micbias(struct snd_soc_codec *codec, + int micb_num, + bool enable) +{ + const char * const micb_names[] = { + DAPM_MICBIAS1_STANDALONE, DAPM_MICBIAS2_STANDALONE, + DAPM_MICBIAS3_STANDALONE, DAPM_MICBIAS4_STANDALONE + }; + int micb_index = micb_num - 1; + int rc; + + if (!codec) { + pr_err("%s: Codec memory is NULL\n", __func__); + return -EINVAL; + } + + if ((micb_index < 0) || (micb_index > TAVIL_MAX_MICBIAS - 1)) { + dev_err(codec->dev, "%s: Invalid micbias index, micb_ind:%d\n", + __func__, micb_index); + return -EINVAL; + } + + if (enable) + rc = snd_soc_dapm_force_enable_pin( + snd_soc_codec_get_dapm(codec), + micb_names[micb_index]); + else + rc = snd_soc_dapm_disable_pin(snd_soc_codec_get_dapm(codec), + micb_names[micb_index]); + + if (!rc) + snd_soc_dapm_sync(snd_soc_codec_get_dapm(codec)); + else + dev_err(codec->dev, "%s: micbias%d force %s pin failed\n", + __func__, micb_num, (enable ? "enable" : "disable")); + + return rc; +} +EXPORT_SYMBOL(tavil_codec_enable_standalone_micbias); + +static int tavil_codec_force_enable_micbias(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + int ret = 0; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + wcd_resmgr_enable_master_bias(tavil->resmgr); + tavil_cdc_mclk_enable(codec, true); + ret = __tavil_codec_enable_micbias(w, SND_SOC_DAPM_PRE_PMU); + /* Wait for 1ms for better cnp */ + usleep_range(1000, 1100); + tavil_cdc_mclk_enable(codec, false); + break; + case SND_SOC_DAPM_POST_PMD: + ret = __tavil_codec_enable_micbias(w, SND_SOC_DAPM_POST_PMD); + wcd_resmgr_disable_master_bias(tavil->resmgr); + break; + } + + return ret; +} + +static int tavil_codec_enable_micbias(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + return __tavil_codec_enable_micbias(w, event); +} + + +static const struct reg_sequence tavil_hph_reset_tbl[] = { + { WCD934X_HPH_CNP_EN, 0x80 }, + { WCD934X_HPH_CNP_WG_CTL, 0x9A }, + { WCD934X_HPH_CNP_WG_TIME, 0x14 }, + { WCD934X_HPH_OCP_CTL, 0x28 }, + { WCD934X_HPH_AUTO_CHOP, 0x16 }, + { WCD934X_HPH_CHOP_CTL, 0x83 }, + { WCD934X_HPH_PA_CTL1, 0x46 }, + { WCD934X_HPH_PA_CTL2, 0x50 }, + { WCD934X_HPH_L_EN, 0x80 }, + { WCD934X_HPH_L_TEST, 0xE0 }, + { WCD934X_HPH_L_ATEST, 0x50 }, + { WCD934X_HPH_R_EN, 0x80 }, + { WCD934X_HPH_R_TEST, 0xE0 }, + { WCD934X_HPH_R_ATEST, 0x54 }, + { WCD934X_HPH_RDAC_CLK_CTL1, 0x99 }, + { WCD934X_HPH_RDAC_CLK_CTL2, 0x9B }, + { WCD934X_HPH_RDAC_LDO_CTL, 0x33 }, + { WCD934X_HPH_RDAC_CHOP_CLK_LP_CTL, 0x00 }, + { WCD934X_HPH_REFBUFF_UHQA_CTL, 0xA8 }, +}; + +static const struct reg_sequence tavil_hph_reset_tbl_1_0[] = { + { WCD934X_HPH_REFBUFF_LP_CTL, 0x0A }, + { WCD934X_HPH_L_DAC_CTL, 0x00 }, + { WCD934X_HPH_R_DAC_CTL, 0x00 }, + { WCD934X_HPH_NEW_ANA_HPH2, 0x00 }, + { WCD934X_HPH_NEW_ANA_HPH3, 0x00 }, + { WCD934X_HPH_NEW_INT_RDAC_GAIN_CTL, 0x00 }, + { WCD934X_HPH_NEW_INT_RDAC_HD2_CTL, 0xA0 }, + { WCD934X_HPH_NEW_INT_RDAC_VREF_CTL, 0x10 }, + { WCD934X_HPH_NEW_INT_RDAC_OVERRIDE_CTL, 0x00 }, + { WCD934X_HPH_NEW_INT_RDAC_MISC1, 0x00 }, + { WCD934X_HPH_NEW_INT_PA_MISC1, 0x22 }, + { WCD934X_HPH_NEW_INT_PA_MISC2, 0x00 }, + { WCD934X_HPH_NEW_INT_PA_RDAC_MISC, 0x00 }, + { WCD934X_HPH_NEW_INT_HPH_TIMER1, 0xFE }, + { WCD934X_HPH_NEW_INT_HPH_TIMER2, 0x2 }, + { WCD934X_HPH_NEW_INT_HPH_TIMER3, 0x4e}, + { WCD934X_HPH_NEW_INT_HPH_TIMER4, 0x54 }, + { WCD934X_HPH_NEW_INT_PA_RDAC_MISC2, 0x00 }, + { WCD934X_HPH_NEW_INT_PA_RDAC_MISC3, 0x00 }, +}; + +static const struct reg_sequence tavil_hph_reset_tbl_1_1[] = { + { WCD934X_HPH_REFBUFF_LP_CTL, 0x0E }, + { WCD934X_HPH_L_DAC_CTL, 0x00 }, + { WCD934X_HPH_R_DAC_CTL, 0x00 }, + { WCD934X_HPH_NEW_ANA_HPH2, 0x00 }, + { WCD934X_HPH_NEW_ANA_HPH3, 0x00 }, + { WCD934X_HPH_NEW_INT_RDAC_GAIN_CTL, 0x40 }, + { WCD934X_HPH_NEW_INT_RDAC_HD2_CTL, 0x81 }, + { WCD934X_HPH_NEW_INT_RDAC_VREF_CTL, 0x10 }, + { WCD934X_HPH_NEW_INT_RDAC_OVERRIDE_CTL, 0x00 }, + { WCD934X_HPH_NEW_INT_RDAC_MISC1, 0x81 }, + { WCD934X_HPH_NEW_INT_PA_MISC1, 0x22 }, + { WCD934X_HPH_NEW_INT_PA_MISC2, 0x00 }, + { WCD934X_HPH_NEW_INT_PA_RDAC_MISC, 0x00 }, + { WCD934X_HPH_NEW_INT_HPH_TIMER1, 0xFE }, + { WCD934X_HPH_NEW_INT_HPH_TIMER2, 0x2 }, + { WCD934X_HPH_NEW_INT_HPH_TIMER3, 0x4e}, + { WCD934X_HPH_NEW_INT_HPH_TIMER4, 0x54 }, + { WCD934X_HPH_NEW_INT_PA_RDAC_MISC2, 0x00 }, + { WCD934X_HPH_NEW_INT_PA_RDAC_MISC3, 0x00 }, +}; + +static const struct tavil_reg_mask_val tavil_pa_disable[] = { + { WCD934X_CDC_RX1_RX_PATH_CTL, 0x30, 0x10 }, /* RX1 mute enable */ + { WCD934X_CDC_RX2_RX_PATH_CTL, 0x30, 0x10 }, /* RX2 mute enable */ + { WCD934X_HPH_CNP_WG_CTL, 0x80, 0x00 }, /* GM3 boost disable */ + { WCD934X_ANA_HPH, 0x80, 0x00 }, /* HPHL PA disable */ + { WCD934X_ANA_HPH, 0x40, 0x00 }, /* HPHR PA disable */ + { WCD934X_ANA_HPH, 0x20, 0x00 }, /* HPHL REF dsable */ + { WCD934X_ANA_HPH, 0x10, 0x00 }, /* HPHR REF disable */ +}; + +static const struct tavil_reg_mask_val tavil_ocp_en_seq[] = { + { WCD934X_RX_OCP_CTL, 0x0F, 0x02 }, /* OCP number of attempts is 2 */ + { WCD934X_HPH_OCP_CTL, 0xFA, 0x3A }, /* OCP current limit */ + { WCD934X_HPH_L_TEST, 0x01, 0x01 }, /* Enable HPHL OCP */ + { WCD934X_HPH_R_TEST, 0x01, 0x01 }, /* Enable HPHR OCP */ +}; + +static const struct tavil_reg_mask_val tavil_ocp_en_seq_1[] = { + { WCD934X_RX_OCP_CTL, 0x0F, 0x02 }, /* OCP number of attempts is 2 */ + { WCD934X_HPH_OCP_CTL, 0xFA, 0x3A }, /* OCP current limit */ +}; + +/* LO-HIFI */ +static const struct tavil_reg_mask_val tavil_pre_pa_en_lohifi[] = { + { WCD934X_HPH_NEW_INT_HPH_TIMER1, 0x02, 0x00 }, + { WCD934X_FLYBACK_VNEG_CTRL_4, 0xf0, 0x80 }, + { WCD934X_HPH_NEW_INT_PA_MISC2, 0x20, 0x20 }, + { WCD934X_HPH_NEW_INT_RDAC_GAIN_CTL, 0xf0, 0x40 }, + { WCD934X_HPH_CNP_WG_CTL, 0x80, 0x00 }, + { WCD934X_RX_BIAS_HPH_LOWPOWER, 0xf0, 0xc0 }, + { WCD934X_HPH_PA_CTL1, 0x0e, 0x02 }, + { WCD934X_HPH_REFBUFF_LP_CTL, 0x06, 0x06 }, +}; + +static const struct tavil_reg_mask_val tavil_pre_pa_en[] = { + { WCD934X_HPH_NEW_INT_HPH_TIMER1, 0x02, 0x00 }, + { WCD934X_HPH_NEW_INT_PA_MISC2, 0x20, 0x0 }, + { WCD934X_HPH_NEW_INT_RDAC_GAIN_CTL, 0xf0, 0x40 }, + { WCD934X_HPH_CNP_WG_CTL, 0x80, 0x00 }, + { WCD934X_RX_BIAS_HPH_LOWPOWER, 0xf0, 0x80 }, + { WCD934X_HPH_PA_CTL1, 0x0e, 0x06 }, + { WCD934X_HPH_REFBUFF_LP_CTL, 0x06, 0x06 }, +}; + +static const struct tavil_reg_mask_val tavil_post_pa_en[] = { + { WCD934X_HPH_L_TEST, 0x01, 0x01 }, /* Enable HPHL OCP */ + { WCD934X_HPH_R_TEST, 0x01, 0x01 }, /* Enable HPHR OCP */ + { WCD934X_CDC_RX1_RX_PATH_CTL, 0x30, 0x20 }, /* RX1 mute disable */ + { WCD934X_CDC_RX2_RX_PATH_CTL, 0x30, 0x20 }, /* RX2 mute disable */ + { WCD934X_HPH_CNP_WG_CTL, 0x80, 0x80 }, /* GM3 boost enable */ + { WCD934X_HPH_NEW_INT_HPH_TIMER1, 0x02, 0x02 }, +}; + +static void tavil_codec_hph_reg_range_read(struct regmap *map, u8 *buf) +{ + regmap_bulk_read(map, WCD934X_HPH_CNP_EN, buf, TAVIL_HPH_REG_RANGE_1); + regmap_bulk_read(map, WCD934X_HPH_NEW_ANA_HPH2, + buf + TAVIL_HPH_REG_RANGE_1, TAVIL_HPH_REG_RANGE_2); + regmap_bulk_read(map, WCD934X_HPH_NEW_INT_RDAC_GAIN_CTL, + buf + TAVIL_HPH_REG_RANGE_1 + TAVIL_HPH_REG_RANGE_2, + TAVIL_HPH_REG_RANGE_3); +} + +static void tavil_codec_hph_reg_recover(struct tavil_priv *tavil, + struct regmap *map, int pa_status) +{ + int i; + unsigned int reg; + + blocking_notifier_call_chain(&tavil->mbhc->notifier, + WCD_EVENT_OCP_OFF, + &tavil->mbhc->wcd_mbhc); + + if (pa_status & 0xC0) + goto pa_en_restore; + + dev_dbg(tavil->dev, "%s: HPH PA in disable state (0x%x)\n", + __func__, pa_status); + + regmap_write_bits(map, WCD934X_CDC_RX1_RX_PATH_CTL, 0x10, 0x10); + regmap_write_bits(map, WCD934X_CDC_RX2_RX_PATH_CTL, 0x10, 0x10); + regmap_write_bits(map, WCD934X_ANA_HPH, 0xC0, 0x00); + regmap_write_bits(map, WCD934X_ANA_HPH, 0x30, 0x00); + regmap_write_bits(map, WCD934X_CDC_RX1_RX_PATH_CTL, 0x10, 0x00); + regmap_write_bits(map, WCD934X_CDC_RX2_RX_PATH_CTL, 0x10, 0x00); + + /* Restore to HW defaults */ + regmap_multi_reg_write(map, tavil_hph_reset_tbl, + ARRAY_SIZE(tavil_hph_reset_tbl)); + if (TAVIL_IS_1_1(tavil->wcd9xxx)) + regmap_multi_reg_write(map, tavil_hph_reset_tbl_1_1, + ARRAY_SIZE(tavil_hph_reset_tbl_1_1)); + if (TAVIL_IS_1_0(tavil->wcd9xxx)) + regmap_multi_reg_write(map, tavil_hph_reset_tbl_1_0, + ARRAY_SIZE(tavil_hph_reset_tbl_1_0)); + + for (i = 0; i < ARRAY_SIZE(tavil_ocp_en_seq); i++) + regmap_write_bits(map, tavil_ocp_en_seq[i].reg, + tavil_ocp_en_seq[i].mask, + tavil_ocp_en_seq[i].val); + goto end; + + +pa_en_restore: + dev_dbg(tavil->dev, "%s: HPH PA in enable state (0x%x)\n", + __func__, pa_status); + + /* Disable PA and other registers before restoring */ + for (i = 0; i < ARRAY_SIZE(tavil_pa_disable); i++) { + if (TAVIL_IS_1_1(tavil->wcd9xxx) && + (tavil_pa_disable[i].reg == WCD934X_HPH_CNP_WG_CTL)) + continue; + regmap_write_bits(map, tavil_pa_disable[i].reg, + tavil_pa_disable[i].mask, + tavil_pa_disable[i].val); + } + + regmap_multi_reg_write(map, tavil_hph_reset_tbl, + ARRAY_SIZE(tavil_hph_reset_tbl)); + if (TAVIL_IS_1_1(tavil->wcd9xxx)) + regmap_multi_reg_write(map, tavil_hph_reset_tbl_1_1, + ARRAY_SIZE(tavil_hph_reset_tbl_1_1)); + if (TAVIL_IS_1_0(tavil->wcd9xxx)) + regmap_multi_reg_write(map, tavil_hph_reset_tbl_1_0, + ARRAY_SIZE(tavil_hph_reset_tbl_1_0)); + + for (i = 0; i < ARRAY_SIZE(tavil_ocp_en_seq_1); i++) + regmap_write_bits(map, tavil_ocp_en_seq_1[i].reg, + tavil_ocp_en_seq_1[i].mask, + tavil_ocp_en_seq_1[i].val); + + if (tavil->hph_mode == CLS_H_LOHIFI) { + for (i = 0; i < ARRAY_SIZE(tavil_pre_pa_en_lohifi); i++) { + reg = tavil_pre_pa_en_lohifi[i].reg; + if ((TAVIL_IS_1_1(tavil->wcd9xxx)) && + ((reg == WCD934X_HPH_NEW_INT_RDAC_GAIN_CTL) || + (reg == WCD934X_HPH_CNP_WG_CTL) || + (reg == WCD934X_HPH_REFBUFF_LP_CTL))) + continue; + regmap_write_bits(map, + tavil_pre_pa_en_lohifi[i].reg, + tavil_pre_pa_en_lohifi[i].mask, + tavil_pre_pa_en_lohifi[i].val); + } + } else { + for (i = 0; i < ARRAY_SIZE(tavil_pre_pa_en); i++) { + reg = tavil_pre_pa_en[i].reg; + if ((TAVIL_IS_1_1(tavil->wcd9xxx)) && + ((reg == WCD934X_HPH_NEW_INT_RDAC_GAIN_CTL) || + (reg == WCD934X_HPH_CNP_WG_CTL) || + (reg == WCD934X_HPH_REFBUFF_LP_CTL))) + continue; + regmap_write_bits(map, tavil_pre_pa_en[i].reg, + tavil_pre_pa_en[i].mask, + tavil_pre_pa_en[i].val); + } + } + + if (TAVIL_IS_1_1(tavil->wcd9xxx)) { + regmap_write(map, WCD934X_HPH_NEW_INT_RDAC_HD2_CTL_L, 0x84); + regmap_write(map, WCD934X_HPH_NEW_INT_RDAC_HD2_CTL_R, 0x84); + } + + regmap_write_bits(map, WCD934X_ANA_HPH, 0x0C, pa_status & 0x0C); + regmap_write_bits(map, WCD934X_ANA_HPH, 0x30, 0x30); + /* wait for 100usec after HPH DAC is enabled */ + usleep_range(100, 110); + regmap_write(map, WCD934X_ANA_HPH, pa_status); + /* Sleep for 7msec after PA is enabled */ + usleep_range(7000, 7100); + + for (i = 0; i < ARRAY_SIZE(tavil_post_pa_en); i++) { + if ((TAVIL_IS_1_1(tavil->wcd9xxx)) && + (tavil_post_pa_en[i].reg == WCD934X_HPH_CNP_WG_CTL)) + continue; + regmap_write_bits(map, tavil_post_pa_en[i].reg, + tavil_post_pa_en[i].mask, + tavil_post_pa_en[i].val); + } + +end: + tavil->mbhc->is_hph_recover = true; + blocking_notifier_call_chain( + &tavil->mbhc->notifier, + WCD_EVENT_OCP_ON, + &tavil->mbhc->wcd_mbhc); +} + +static int tavil_codec_reset_hph_registers(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent); + u8 cache_val[TAVIL_HPH_TOTAL_REG]; + u8 hw_val[TAVIL_HPH_TOTAL_REG]; + int pa_status; + int ret; + + dev_dbg(wcd9xxx->dev, "%s: event: %d\n", __func__, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + memset(cache_val, 0, TAVIL_HPH_TOTAL_REG); + memset(hw_val, 0, TAVIL_HPH_TOTAL_REG); + + regmap_read(wcd9xxx->regmap, WCD934X_ANA_HPH, &pa_status); + + tavil_codec_hph_reg_range_read(wcd9xxx->regmap, cache_val); + + /* Read register values from HW directly */ + regcache_cache_bypass(wcd9xxx->regmap, true); + tavil_codec_hph_reg_range_read(wcd9xxx->regmap, hw_val); + regcache_cache_bypass(wcd9xxx->regmap, false); + + /* compare both the registers to know if there is corruption */ + ret = memcmp(cache_val, hw_val, TAVIL_HPH_TOTAL_REG); + + /* If both the values are same, it means no corruption */ + if (ret) { + dev_dbg(codec->dev, "%s: cache and hw reg are not same\n", + __func__); + tavil_codec_hph_reg_recover(tavil, wcd9xxx->regmap, + pa_status); + } else { + dev_dbg(codec->dev, "%s: cache and hw reg are same\n", + __func__); + tavil->mbhc->is_hph_recover = false; + } + break; + default: + break; + }; + + return 0; +} + +static void tavil_restore_iir_coeff(struct tavil_priv *tavil, int iir_idx, + int band_idx) +{ + u16 reg_add; + int no_of_reg = 0; + + regmap_write(tavil->wcd9xxx->regmap, + (WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL + 16 * iir_idx), + (band_idx * BAND_MAX * sizeof(uint32_t)) & 0x7F); + + reg_add = WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL + 16 * iir_idx; + + if (tavil->intf_type != WCD9XXX_INTERFACE_TYPE_SLIMBUS) + return; + /* + * Since wcd9xxx_slim_write_repeat() supports only maximum of 16 + * registers at a time, split total 20 writes(5 coefficients per + * band and 4 writes per coefficient) into 16 and 4. + */ + no_of_reg = WCD934X_CDC_REPEAT_WRITES_MAX; + wcd9xxx_slim_write_repeat(tavil->wcd9xxx, reg_add, no_of_reg, + &tavil->sidetone_coeff_array[iir_idx][band_idx][0]); + + no_of_reg = (WCD934X_CDC_SIDETONE_IIR_COEFF_MAX * 4) - + WCD934X_CDC_REPEAT_WRITES_MAX; + wcd9xxx_slim_write_repeat(tavil->wcd9xxx, reg_add, no_of_reg, + &tavil->sidetone_coeff_array[iir_idx][band_idx] + [WCD934X_CDC_REPEAT_WRITES_MAX]); +} + +static int tavil_iir_enable_audio_mixer_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + int iir_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->reg; + int band_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + /* IIR filter band registers are at integer multiples of 16 */ + u16 iir_reg = WCD934X_CDC_SIDETONE_IIR0_IIR_CTL + 16 * iir_idx; + + ucontrol->value.integer.value[0] = (snd_soc_read(codec, iir_reg) & + (1 << band_idx)) != 0; + + dev_dbg(codec->dev, "%s: IIR #%d band #%d enable %d\n", __func__, + iir_idx, band_idx, + (uint32_t)ucontrol->value.integer.value[0]); + return 0; +} + +static int tavil_iir_enable_audio_mixer_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + int iir_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->reg; + int band_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + bool iir_band_en_status; + int value = ucontrol->value.integer.value[0]; + u16 iir_reg = WCD934X_CDC_SIDETONE_IIR0_IIR_CTL + 16 * iir_idx; + + tavil_restore_iir_coeff(tavil, iir_idx, band_idx); + + /* Mask first 5 bits, 6-8 are reserved */ + snd_soc_update_bits(codec, iir_reg, (1 << band_idx), + (value << band_idx)); + + iir_band_en_status = ((snd_soc_read(codec, iir_reg) & + (1 << band_idx)) != 0); + dev_dbg(codec->dev, "%s: IIR #%d band #%d enable %d\n", __func__, + iir_idx, band_idx, iir_band_en_status); + return 0; +} + +static uint32_t get_iir_band_coeff(struct snd_soc_codec *codec, + int iir_idx, int band_idx, + int coeff_idx) +{ + uint32_t value = 0; + + /* Address does not automatically update if reading */ + snd_soc_write(codec, + (WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL + 16 * iir_idx), + ((band_idx * BAND_MAX + coeff_idx) + * sizeof(uint32_t)) & 0x7F); + + value |= snd_soc_read(codec, + (WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL + 16 * iir_idx)); + + snd_soc_write(codec, + (WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL + 16 * iir_idx), + ((band_idx * BAND_MAX + coeff_idx) + * sizeof(uint32_t) + 1) & 0x7F); + + value |= (snd_soc_read(codec, + (WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL + + 16 * iir_idx)) << 8); + + snd_soc_write(codec, + (WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL + 16 * iir_idx), + ((band_idx * BAND_MAX + coeff_idx) + * sizeof(uint32_t) + 2) & 0x7F); + + value |= (snd_soc_read(codec, + (WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL + + 16 * iir_idx)) << 16); + + snd_soc_write(codec, + (WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL + 16 * iir_idx), + ((band_idx * BAND_MAX + coeff_idx) + * sizeof(uint32_t) + 3) & 0x7F); + + /* Mask bits top 2 bits since they are reserved */ + value |= ((snd_soc_read(codec, + (WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL + + 16 * iir_idx)) & 0x3F) << 24); + + return value; +} + +static int tavil_iir_band_audio_mixer_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + int iir_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->reg; + int band_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + + ucontrol->value.integer.value[0] = + get_iir_band_coeff(codec, iir_idx, band_idx, 0); + ucontrol->value.integer.value[1] = + get_iir_band_coeff(codec, iir_idx, band_idx, 1); + ucontrol->value.integer.value[2] = + get_iir_band_coeff(codec, iir_idx, band_idx, 2); + ucontrol->value.integer.value[3] = + get_iir_band_coeff(codec, iir_idx, band_idx, 3); + ucontrol->value.integer.value[4] = + get_iir_band_coeff(codec, iir_idx, band_idx, 4); + + dev_dbg(codec->dev, "%s: IIR #%d band #%d b0 = 0x%x\n" + "%s: IIR #%d band #%d b1 = 0x%x\n" + "%s: IIR #%d band #%d b2 = 0x%x\n" + "%s: IIR #%d band #%d a1 = 0x%x\n" + "%s: IIR #%d band #%d a2 = 0x%x\n", + __func__, iir_idx, band_idx, + (uint32_t)ucontrol->value.integer.value[0], + __func__, iir_idx, band_idx, + (uint32_t)ucontrol->value.integer.value[1], + __func__, iir_idx, band_idx, + (uint32_t)ucontrol->value.integer.value[2], + __func__, iir_idx, band_idx, + (uint32_t)ucontrol->value.integer.value[3], + __func__, iir_idx, band_idx, + (uint32_t)ucontrol->value.integer.value[4]); + return 0; +} + +static void set_iir_band_coeff(struct snd_soc_codec *codec, + int iir_idx, int band_idx, + uint32_t value) +{ + snd_soc_write(codec, + (WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL + 16 * iir_idx), + (value & 0xFF)); + + snd_soc_write(codec, + (WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL + 16 * iir_idx), + (value >> 8) & 0xFF); + + snd_soc_write(codec, + (WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL + 16 * iir_idx), + (value >> 16) & 0xFF); + + /* Mask top 2 bits, 7-8 are reserved */ + snd_soc_write(codec, + (WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL + 16 * iir_idx), + (value >> 24) & 0x3F); +} + +static int tavil_iir_band_audio_mixer_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + int iir_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->reg; + int band_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + int coeff_idx, idx = 0; + + /* + * Mask top bit it is reserved + * Updates addr automatically for each B2 write + */ + snd_soc_write(codec, + (WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL + 16 * iir_idx), + (band_idx * BAND_MAX * sizeof(uint32_t)) & 0x7F); + + /* Store the coefficients in sidetone coeff array */ + for (coeff_idx = 0; coeff_idx < WCD934X_CDC_SIDETONE_IIR_COEFF_MAX; + coeff_idx++) { + uint32_t value = ucontrol->value.integer.value[coeff_idx]; + + set_iir_band_coeff(codec, iir_idx, band_idx, value); + + /* Four 8 bit values(one 32 bit) per coefficient */ + tavil->sidetone_coeff_array[iir_idx][band_idx][idx++] = + (value & 0xFF); + tavil->sidetone_coeff_array[iir_idx][band_idx][idx++] = + (value >> 8) & 0xFF; + tavil->sidetone_coeff_array[iir_idx][band_idx][idx++] = + (value >> 16) & 0xFF; + tavil->sidetone_coeff_array[iir_idx][band_idx][idx++] = + (value >> 24) & 0xFF; + } + + pr_debug("%s: IIR #%d band #%d b0 = 0x%x\n" + "%s: IIR #%d band #%d b1 = 0x%x\n" + "%s: IIR #%d band #%d b2 = 0x%x\n" + "%s: IIR #%d band #%d a1 = 0x%x\n" + "%s: IIR #%d band #%d a2 = 0x%x\n", + __func__, iir_idx, band_idx, + get_iir_band_coeff(codec, iir_idx, band_idx, 0), + __func__, iir_idx, band_idx, + get_iir_band_coeff(codec, iir_idx, band_idx, 1), + __func__, iir_idx, band_idx, + get_iir_band_coeff(codec, iir_idx, band_idx, 2), + __func__, iir_idx, band_idx, + get_iir_band_coeff(codec, iir_idx, band_idx, 3), + __func__, iir_idx, band_idx, + get_iir_band_coeff(codec, iir_idx, band_idx, 4)); + return 0; +} + +static int tavil_compander_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + int comp = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = tavil->comp_enabled[comp]; + return 0; +} + +static int tavil_compander_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + int comp = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + int value = ucontrol->value.integer.value[0]; + + dev_dbg(codec->dev, "%s: Compander %d enable current %d, new %d\n", + __func__, comp + 1, tavil->comp_enabled[comp], value); + tavil->comp_enabled[comp] = value; + + /* Any specific register configuration for compander */ + switch (comp) { + case COMPANDER_1: + /* Set Gain Source Select based on compander enable/disable */ + snd_soc_update_bits(codec, WCD934X_HPH_L_EN, 0x20, + (value ? 0x00:0x20)); + break; + case COMPANDER_2: + snd_soc_update_bits(codec, WCD934X_HPH_R_EN, 0x20, + (value ? 0x00:0x20)); + break; + case COMPANDER_3: + case COMPANDER_4: + case COMPANDER_7: + case COMPANDER_8: + break; + default: + /* + * if compander is not enabled for any interpolator, + * it does not cause any audio failure, so do not + * return error in this case, but just print a log + */ + dev_warn(codec->dev, "%s: unknown compander: %d\n", + __func__, comp); + }; + return 0; +} + +static int tavil_hph_asrc_mode_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + int index = -EINVAL; + + if (!strcmp(kcontrol->id.name, "ASRC0 Output Mode")) + index = ASRC0; + if (!strcmp(kcontrol->id.name, "ASRC1 Output Mode")) + index = ASRC1; + + if (tavil && (index >= 0) && (index < ASRC_MAX)) + tavil->asrc_output_mode[index] = + ucontrol->value.integer.value[0]; + + return 0; +} + +static int tavil_hph_asrc_mode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + int val = 0; + int index = -EINVAL; + + if (!strcmp(kcontrol->id.name, "ASRC0 Output Mode")) + index = ASRC0; + if (!strcmp(kcontrol->id.name, "ASRC1 Output Mode")) + index = ASRC1; + + if (tavil && (index >= 0) && (index < ASRC_MAX)) + val = tavil->asrc_output_mode[index]; + + ucontrol->value.integer.value[0] = val; + + return 0; +} + +static int tavil_hph_idle_detect_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + int val = 0; + + if (tavil) + val = tavil->idle_det_cfg.hph_idle_detect_en; + + ucontrol->value.integer.value[0] = val; + + return 0; +} + +static int tavil_hph_idle_detect_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + + if (tavil) + tavil->idle_det_cfg.hph_idle_detect_en = + ucontrol->value.integer.value[0]; + + return 0; +} + +static int tavil_dmic_pin_mode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + u16 dmic_pin; + u8 reg_val, pinctl_position; + + pinctl_position = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + + dmic_pin = pinctl_position & 0x07; + reg_val = snd_soc_read(codec, + WCD934X_TLMM_DMIC1_CLK_PINCFG + dmic_pin - 1); + + ucontrol->value.integer.value[0] = !!reg_val; + + return 0; +} + +static int tavil_dmic_pin_mode_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + u16 ctl_reg, cfg_reg, dmic_pin; + u8 ctl_val, cfg_val, pinctl_position, pinctl_mode, mask; + + /* 0- high or low; 1- high Z */ + pinctl_mode = ucontrol->value.integer.value[0]; + pinctl_position = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + + switch (pinctl_position >> 3) { + case 0: + ctl_reg = WCD934X_TEST_DEBUG_PIN_CTL_OE_0; + break; + case 1: + ctl_reg = WCD934X_TEST_DEBUG_PIN_CTL_OE_1; + break; + case 2: + ctl_reg = WCD934X_TEST_DEBUG_PIN_CTL_OE_2; + break; + case 3: + ctl_reg = WCD934X_TEST_DEBUG_PIN_CTL_OE_3; + break; + default: + dev_err(codec->dev, "%s: Invalid pinctl position = %d\n", + __func__, pinctl_position); + return -EINVAL; + } + + ctl_val = ~(pinctl_mode << (pinctl_position & 0x07)); + mask = 1 << (pinctl_position & 0x07); + snd_soc_update_bits(codec, ctl_reg, mask, ctl_val); + + dmic_pin = pinctl_position & 0x07; + cfg_reg = WCD934X_TLMM_DMIC1_CLK_PINCFG + dmic_pin - 1; + if (pinctl_mode) { + if (tavil->intf_type == WCD9XXX_INTERFACE_TYPE_SLIMBUS) + cfg_val = 0x6; + else + cfg_val = 0xD; + } else + cfg_val = 0; + snd_soc_update_bits(codec, cfg_reg, 0x1F, cfg_val); + + dev_dbg(codec->dev, "%s: reg=0x%x mask=0x%x val=%d reg=0x%x val=%d\n", + __func__, ctl_reg, mask, ctl_val, cfg_reg, cfg_val); + + return 0; +} + +static int tavil_amic_pwr_lvl_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + u16 amic_reg = 0; + + if (!strcmp(kcontrol->id.name, "AMIC_1_2 PWR MODE")) + amic_reg = WCD934X_ANA_AMIC1; + if (!strcmp(kcontrol->id.name, "AMIC_3_4 PWR MODE")) + amic_reg = WCD934X_ANA_AMIC3; + + if (amic_reg) + ucontrol->value.integer.value[0] = + (snd_soc_read(codec, amic_reg) & + WCD934X_AMIC_PWR_LVL_MASK) >> + WCD934X_AMIC_PWR_LVL_SHIFT; + return 0; +} + +static int tavil_amic_pwr_lvl_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + u32 mode_val; + u16 amic_reg = 0; + + mode_val = ucontrol->value.enumerated.item[0]; + + dev_dbg(codec->dev, "%s: mode: %d\n", __func__, mode_val); + + if (!strcmp(kcontrol->id.name, "AMIC_1_2 PWR MODE")) + amic_reg = WCD934X_ANA_AMIC1; + if (!strcmp(kcontrol->id.name, "AMIC_3_4 PWR MODE")) + amic_reg = WCD934X_ANA_AMIC3; + + if (amic_reg) + snd_soc_update_bits(codec, amic_reg, WCD934X_AMIC_PWR_LVL_MASK, + mode_val << WCD934X_AMIC_PWR_LVL_SHIFT); + return 0; +} + +static const char *const tavil_conn_mad_text[] = { + "NOTUSED1", "ADC1", "ADC2", "ADC3", "ADC4", "NOTUSED5", + "NOTUSED6", "NOTUSED2", "DMIC0", "DMIC1", "DMIC2", "DMIC3", + "DMIC4", "DMIC5", "NOTUSED3", "NOTUSED4" +}; + +static const struct soc_enum tavil_conn_mad_enum = + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(tavil_conn_mad_text), + tavil_conn_mad_text); + +static int tavil_mad_input_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + u8 tavil_mad_input; + + tavil_mad_input = snd_soc_read(codec, WCD934X_SOC_MAD_INP_SEL) & 0x0F; + ucontrol->value.integer.value[0] = tavil_mad_input; + + dev_dbg(codec->dev, "%s: tavil_mad_input = %s\n", __func__, + tavil_conn_mad_text[tavil_mad_input]); + + return 0; +} + +static int tavil_mad_input_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct snd_soc_card *card = codec->component.card; + u8 tavil_mad_input; + char mad_amic_input_widget[6]; + const char *mad_input_widget; + const char *source_widget = NULL; + u32 adc, i, mic_bias_found = 0; + int ret = 0; + char *mad_input; + bool is_adc_input = false; + + tavil_mad_input = ucontrol->value.integer.value[0]; + + if (tavil_mad_input >= sizeof(tavil_conn_mad_text)/ + sizeof(tavil_conn_mad_text[0])) { + dev_err(codec->dev, + "%s: tavil_mad_input = %d out of bounds\n", + __func__, tavil_mad_input); + return -EINVAL; + } + + if (strnstr(tavil_conn_mad_text[tavil_mad_input], "NOTUSED", + sizeof("NOTUSED"))) { + dev_dbg(codec->dev, + "%s: Unsupported tavil_mad_input = %s\n", + __func__, tavil_conn_mad_text[tavil_mad_input]); + /* Make sure the MAD register is updated */ + snd_soc_update_bits(codec, WCD934X_ANA_MAD_SETUP, + 0x88, 0x00); + return -EINVAL; + } + + if (strnstr(tavil_conn_mad_text[tavil_mad_input], + "ADC", sizeof("ADC"))) { + mad_input = strpbrk(tavil_conn_mad_text[tavil_mad_input], + "1234"); + if (!mad_input) { + dev_err(codec->dev, "%s: Invalid MAD input %s\n", + __func__, tavil_conn_mad_text[tavil_mad_input]); + return -EINVAL; + } + + ret = kstrtouint(mad_input, 10, &adc); + if ((ret < 0) || (adc > 4)) { + dev_err(codec->dev, "%s: Invalid ADC = %s\n", __func__, + tavil_conn_mad_text[tavil_mad_input]); + return -EINVAL; + } + + /*AMIC4 and AMIC5 share ADC4*/ + if ((adc == 4) && + (snd_soc_read(codec, WCD934X_TX_NEW_AMIC_4_5_SEL) & 0x10)) + adc = 5; + + snprintf(mad_amic_input_widget, 6, "%s%u", "AMIC", adc); + + mad_input_widget = mad_amic_input_widget; + is_adc_input = true; + } else { + /* DMIC type input widget*/ + mad_input_widget = tavil_conn_mad_text[tavil_mad_input]; + } + + dev_dbg(codec->dev, + "%s: tavil input widget = %s, adc_input = %s\n", __func__, + mad_input_widget, is_adc_input ? "true" : "false"); + + for (i = 0; i < card->num_of_dapm_routes; i++) { + if (!strcmp(card->of_dapm_routes[i].sink, mad_input_widget)) { + source_widget = card->of_dapm_routes[i].source; + if (!source_widget) { + dev_err(codec->dev, + "%s: invalid source widget\n", + __func__); + return -EINVAL; + } + + if (strnstr(source_widget, + "MIC BIAS1", sizeof("MIC BIAS1"))) { + mic_bias_found = 1; + break; + } else if (strnstr(source_widget, + "MIC BIAS2", sizeof("MIC BIAS2"))) { + mic_bias_found = 2; + break; + } else if (strnstr(source_widget, + "MIC BIAS3", sizeof("MIC BIAS3"))) { + mic_bias_found = 3; + break; + } else if (strnstr(source_widget, + "MIC BIAS4", sizeof("MIC BIAS4"))) { + mic_bias_found = 4; + break; + } + } + } + + if (!mic_bias_found) { + dev_err(codec->dev, "%s: mic bias not found for input %s\n", + __func__, mad_input_widget); + return -EINVAL; + } + + dev_dbg(codec->dev, "%s: mic_bias found = %d\n", __func__, + mic_bias_found); + + snd_soc_update_bits(codec, WCD934X_SOC_MAD_INP_SEL, + 0x0F, tavil_mad_input); + snd_soc_update_bits(codec, WCD934X_ANA_MAD_SETUP, + 0x07, mic_bias_found); + /* for all adc inputs, mad should be in micbias mode with BG enabled */ + if (is_adc_input) + snd_soc_update_bits(codec, WCD934X_ANA_MAD_SETUP, + 0x88, 0x88); + else + snd_soc_update_bits(codec, WCD934X_ANA_MAD_SETUP, + 0x88, 0x00); + return 0; +} + +static int tavil_ear_pa_gain_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u8 ear_pa_gain; + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + + ear_pa_gain = snd_soc_read(codec, WCD934X_ANA_EAR); + + ear_pa_gain = (ear_pa_gain & 0x70) >> 4; + + ucontrol->value.integer.value[0] = ear_pa_gain; + + dev_dbg(codec->dev, "%s: ear_pa_gain = 0x%x\n", __func__, + ear_pa_gain); + + return 0; +} + +static int tavil_ear_pa_gain_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u8 ear_pa_gain; + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + + dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n", + __func__, ucontrol->value.integer.value[0]); + + ear_pa_gain = ucontrol->value.integer.value[0] << 4; + + snd_soc_update_bits(codec, WCD934X_ANA_EAR, 0x70, ear_pa_gain); + return 0; +} + +static int tavil_ear_spkr_pa_gain_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = tavil->ear_spkr_gain; + + dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n", + __func__, ucontrol->value.integer.value[0]); + + return 0; +} + +static int tavil_ear_spkr_pa_gain_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + + tavil->ear_spkr_gain = ucontrol->value.integer.value[0]; + + dev_dbg(codec->dev, "%s: gain = %d\n", __func__, tavil->ear_spkr_gain); + + return 0; +} + +static int tavil_spkr_left_boost_stage_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u8 bst_state_max = 0; + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + + bst_state_max = snd_soc_read(codec, WCD934X_CDC_BOOST0_BOOST_CTL); + bst_state_max = (bst_state_max & 0x0c) >> 2; + ucontrol->value.integer.value[0] = bst_state_max; + dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n", + __func__, ucontrol->value.integer.value[0]); + + return 0; +} + +static int tavil_spkr_left_boost_stage_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u8 bst_state_max; + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + + dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n", + __func__, ucontrol->value.integer.value[0]); + bst_state_max = ucontrol->value.integer.value[0] << 2; + snd_soc_update_bits(codec, WCD934X_CDC_BOOST0_BOOST_CTL, + 0x0c, bst_state_max); + + return 0; +} + +static int tavil_spkr_right_boost_stage_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u8 bst_state_max = 0; + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + + bst_state_max = snd_soc_read(codec, WCD934X_CDC_BOOST1_BOOST_CTL); + bst_state_max = (bst_state_max & 0x0c) >> 2; + ucontrol->value.integer.value[0] = bst_state_max; + dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n", + __func__, ucontrol->value.integer.value[0]); + + return 0; +} + +static int tavil_spkr_right_boost_stage_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u8 bst_state_max; + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + + dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n", + __func__, ucontrol->value.integer.value[0]); + bst_state_max = ucontrol->value.integer.value[0] << 2; + snd_soc_update_bits(codec, WCD934X_CDC_BOOST1_BOOST_CTL, + 0x0c, bst_state_max); + + return 0; +} + +static int tavil_rx_hph_mode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = tavil->hph_mode; + return 0; +} + +static int tavil_rx_hph_mode_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + u32 mode_val; + + mode_val = ucontrol->value.enumerated.item[0]; + + dev_dbg(codec->dev, "%s: mode: %d\n", __func__, mode_val); + + if (mode_val == 0) { + dev_warn(codec->dev, "%s:Invalid HPH Mode, default to Cls-H LOHiFi\n", + __func__); + mode_val = CLS_H_LOHIFI; + } + tavil->hph_mode = mode_val; + return 0; +} + +static int tavil_dmic_drv_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = tavil->dmic_drv_ctl; + return 0; +} + +static int tavil_dmic_drv_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + u32 dmic_drv_val; + + dmic_drv_val = ucontrol->value.enumerated.item[0]; + + tavil->dmic_drv_ctl = dmic_drv_val; + snd_soc_update_bits(codec, WCD934X_TEST_DEBUG_PAD_DRVCTL_0, + WCD934X_DMIC_DRV_CTL_MASK, + tavil->dmic_drv_ctl << + WCD934X_DMIC_DRV_CTL_SHIFT); + return 0; +} + +static const char * const dmic_drv_ctl_text[] = { + "DRV_2MA", "DRV_4MA", "DRV_8MA", "DRV_16MA", +}; + +static const struct soc_enum dmic_drv_ctl_enum = + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(dmic_drv_ctl_text), + dmic_drv_ctl_text); + +static const char * const rx_hph_mode_mux_text[] = { + "CLS_H_INVALID", "CLS_H_HIFI", "CLS_H_LP", "CLS_AB", "CLS_H_LOHIFI", + "CLS_H_ULP", "CLS_AB_HIFI", +}; + +static const struct soc_enum rx_hph_mode_mux_enum = + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(rx_hph_mode_mux_text), + rx_hph_mode_mux_text); + +static const char *const tavil_anc_func_text[] = {"OFF", "ON"}; +static const struct soc_enum tavil_anc_func_enum = + SOC_ENUM_SINGLE_EXT(2, tavil_anc_func_text); + +static const char *const tavil_clkmode_text[] = {"EXTERNAL", "INTERNAL"}; +static SOC_ENUM_SINGLE_EXT_DECL(tavil_clkmode_enum, tavil_clkmode_text); + +/* Cutoff frequency for high pass filter */ +static const char * const cf_text[] = { + "CF_NEG_3DB_4HZ", "CF_NEG_3DB_75HZ", "CF_NEG_3DB_150HZ" +}; + +static const char * const rx_cf_text[] = { + "CF_NEG_3DB_4HZ", "CF_NEG_3DB_75HZ", "CF_NEG_3DB_150HZ", + "CF_NEG_3DB_0P48HZ" +}; + +static const char * const amic_pwr_lvl_text[] = { + "LOW_PWR", "DEFAULT", "HIGH_PERF", "HYBRID" +}; + +static const char * const hph_idle_detect_text[] = { + "OFF", "ON" +}; + +static const char * const asrc_mode_text[] = { + "INT", "FRAC" +}; + +static const char * const tavil_ear_pa_gain_text[] = { + "G_6_DB", "G_4P5_DB", "G_3_DB", "G_1P5_DB", + "G_0_DB", "G_M2P5_DB", "UNDEFINED", "G_M12_DB" +}; + +static const char * const tavil_ear_spkr_pa_gain_text[] = { + "G_DEFAULT", "G_0_DB", "G_1_DB", "G_2_DB", "G_3_DB", + "G_4_DB", "G_5_DB", "G_6_DB" +}; + +static const char * const tavil_speaker_boost_stage_text[] = { + "NO_MAX_STATE", "MAX_STATE_1", "MAX_STATE_2" +}; + +static SOC_ENUM_SINGLE_EXT_DECL(tavil_ear_pa_gain_enum, tavil_ear_pa_gain_text); +static SOC_ENUM_SINGLE_EXT_DECL(tavil_ear_spkr_pa_gain_enum, + tavil_ear_spkr_pa_gain_text); +static SOC_ENUM_SINGLE_EXT_DECL(tavil_spkr_boost_stage_enum, + tavil_speaker_boost_stage_text); +static SOC_ENUM_SINGLE_EXT_DECL(amic_pwr_lvl_enum, amic_pwr_lvl_text); +static SOC_ENUM_SINGLE_EXT_DECL(hph_idle_detect_enum, hph_idle_detect_text); +static SOC_ENUM_SINGLE_EXT_DECL(asrc_mode_enum, asrc_mode_text); +static SOC_ENUM_SINGLE_DECL(cf_dec0_enum, WCD934X_CDC_TX0_TX_PATH_CFG0, 5, + cf_text); +static SOC_ENUM_SINGLE_DECL(cf_dec1_enum, WCD934X_CDC_TX1_TX_PATH_CFG0, 5, + cf_text); +static SOC_ENUM_SINGLE_DECL(cf_dec2_enum, WCD934X_CDC_TX2_TX_PATH_CFG0, 5, + cf_text); +static SOC_ENUM_SINGLE_DECL(cf_dec3_enum, WCD934X_CDC_TX3_TX_PATH_CFG0, 5, + cf_text); +static SOC_ENUM_SINGLE_DECL(cf_dec4_enum, WCD934X_CDC_TX4_TX_PATH_CFG0, 5, + cf_text); +static SOC_ENUM_SINGLE_DECL(cf_dec5_enum, WCD934X_CDC_TX5_TX_PATH_CFG0, 5, + cf_text); +static SOC_ENUM_SINGLE_DECL(cf_dec6_enum, WCD934X_CDC_TX6_TX_PATH_CFG0, 5, + cf_text); +static SOC_ENUM_SINGLE_DECL(cf_dec7_enum, WCD934X_CDC_TX7_TX_PATH_CFG0, 5, + cf_text); +static SOC_ENUM_SINGLE_DECL(cf_dec8_enum, WCD934X_CDC_TX8_TX_PATH_CFG0, 5, + cf_text); +static SOC_ENUM_SINGLE_DECL(cf_int0_1_enum, WCD934X_CDC_RX0_RX_PATH_CFG2, 0, + rx_cf_text); +static SOC_ENUM_SINGLE_DECL(cf_int0_2_enum, WCD934X_CDC_RX0_RX_PATH_MIX_CFG, 2, + rx_cf_text); +static SOC_ENUM_SINGLE_DECL(cf_int1_1_enum, WCD934X_CDC_RX1_RX_PATH_CFG2, 0, + rx_cf_text); +static SOC_ENUM_SINGLE_DECL(cf_int1_2_enum, WCD934X_CDC_RX1_RX_PATH_MIX_CFG, 2, + rx_cf_text); +static SOC_ENUM_SINGLE_DECL(cf_int2_1_enum, WCD934X_CDC_RX2_RX_PATH_CFG2, 0, + rx_cf_text); +static SOC_ENUM_SINGLE_DECL(cf_int2_2_enum, WCD934X_CDC_RX2_RX_PATH_MIX_CFG, 2, + rx_cf_text); +static SOC_ENUM_SINGLE_DECL(cf_int3_1_enum, WCD934X_CDC_RX3_RX_PATH_CFG2, 0, + rx_cf_text); +static SOC_ENUM_SINGLE_DECL(cf_int3_2_enum, WCD934X_CDC_RX3_RX_PATH_MIX_CFG, 2, + rx_cf_text); +static SOC_ENUM_SINGLE_DECL(cf_int4_1_enum, WCD934X_CDC_RX4_RX_PATH_CFG2, 0, + rx_cf_text); +static SOC_ENUM_SINGLE_DECL(cf_int4_2_enum, WCD934X_CDC_RX4_RX_PATH_MIX_CFG, 2, + rx_cf_text); +static SOC_ENUM_SINGLE_DECL(cf_int7_1_enum, WCD934X_CDC_RX7_RX_PATH_CFG2, 0, + rx_cf_text); +static SOC_ENUM_SINGLE_DECL(cf_int7_2_enum, WCD934X_CDC_RX7_RX_PATH_MIX_CFG, 2, + rx_cf_text); +static SOC_ENUM_SINGLE_DECL(cf_int8_1_enum, WCD934X_CDC_RX8_RX_PATH_CFG2, 0, + rx_cf_text); +static SOC_ENUM_SINGLE_DECL(cf_int8_2_enum, WCD934X_CDC_RX8_RX_PATH_MIX_CFG, 2, + rx_cf_text); + +static const struct snd_kcontrol_new tavil_snd_controls[] = { + SOC_ENUM_EXT("EAR PA Gain", tavil_ear_pa_gain_enum, + tavil_ear_pa_gain_get, tavil_ear_pa_gain_put), + SOC_ENUM_EXT("EAR SPKR PA Gain", tavil_ear_spkr_pa_gain_enum, + tavil_ear_spkr_pa_gain_get, tavil_ear_spkr_pa_gain_put), + SOC_ENUM_EXT("SPKR Left Boost Max State", tavil_spkr_boost_stage_enum, + tavil_spkr_left_boost_stage_get, + tavil_spkr_left_boost_stage_put), + SOC_ENUM_EXT("SPKR Right Boost Max State", tavil_spkr_boost_stage_enum, + tavil_spkr_right_boost_stage_get, + tavil_spkr_right_boost_stage_put), + SOC_SINGLE_TLV("HPHL Volume", WCD934X_HPH_L_EN, 0, 20, 1, line_gain), + SOC_SINGLE_TLV("HPHR Volume", WCD934X_HPH_R_EN, 0, 20, 1, line_gain), + SOC_SINGLE_TLV("LINEOUT1 Volume", WCD934X_DIFF_LO_LO1_COMPANDER, + 3, 16, 1, line_gain), + SOC_SINGLE_TLV("LINEOUT2 Volume", WCD934X_DIFF_LO_LO2_COMPANDER, + 3, 16, 1, line_gain), + SOC_SINGLE_TLV("ADC1 Volume", WCD934X_ANA_AMIC1, 0, 20, 0, analog_gain), + SOC_SINGLE_TLV("ADC2 Volume", WCD934X_ANA_AMIC2, 0, 20, 0, analog_gain), + SOC_SINGLE_TLV("ADC3 Volume", WCD934X_ANA_AMIC3, 0, 20, 0, analog_gain), + SOC_SINGLE_TLV("ADC4 Volume", WCD934X_ANA_AMIC4, 0, 20, 0, analog_gain), + + SOC_SINGLE_SX_TLV("RX0 Digital Volume", WCD934X_CDC_RX0_RX_VOL_CTL, + 0, -84, 40, digital_gain), /* -84dB min - 40dB max */ + SOC_SINGLE_SX_TLV("RX1 Digital Volume", WCD934X_CDC_RX1_RX_VOL_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("RX2 Digital Volume", WCD934X_CDC_RX2_RX_VOL_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("RX3 Digital Volume", WCD934X_CDC_RX3_RX_VOL_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("RX4 Digital Volume", WCD934X_CDC_RX4_RX_VOL_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("RX7 Digital Volume", WCD934X_CDC_RX7_RX_VOL_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("RX8 Digital Volume", WCD934X_CDC_RX8_RX_VOL_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("RX0 Mix Digital Volume", + WCD934X_CDC_RX0_RX_VOL_MIX_CTL, 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("RX1 Mix Digital Volume", + WCD934X_CDC_RX1_RX_VOL_MIX_CTL, 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("RX2 Mix Digital Volume", + WCD934X_CDC_RX2_RX_VOL_MIX_CTL, 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("RX3 Mix Digital Volume", + WCD934X_CDC_RX3_RX_VOL_MIX_CTL, 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("RX4 Mix Digital Volume", + WCD934X_CDC_RX4_RX_VOL_MIX_CTL, 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("RX7 Mix Digital Volume", + WCD934X_CDC_RX7_RX_VOL_MIX_CTL, 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("RX8 Mix Digital Volume", + WCD934X_CDC_RX8_RX_VOL_MIX_CTL, 0, -84, 40, digital_gain), + + SOC_SINGLE_SX_TLV("DEC0 Volume", WCD934X_CDC_TX0_TX_VOL_CTL, 0, + -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("DEC1 Volume", WCD934X_CDC_TX1_TX_VOL_CTL, 0, + -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("DEC2 Volume", WCD934X_CDC_TX2_TX_VOL_CTL, 0, + -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("DEC3 Volume", WCD934X_CDC_TX3_TX_VOL_CTL, 0, + -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("DEC4 Volume", WCD934X_CDC_TX4_TX_VOL_CTL, 0, + -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("DEC5 Volume", WCD934X_CDC_TX5_TX_VOL_CTL, 0, + -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("DEC6 Volume", WCD934X_CDC_TX6_TX_VOL_CTL, 0, + -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("DEC7 Volume", WCD934X_CDC_TX7_TX_VOL_CTL, 0, + -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("DEC8 Volume", WCD934X_CDC_TX8_TX_VOL_CTL, 0, + -84, 40, digital_gain), + + SOC_SINGLE_SX_TLV("IIR0 INP0 Volume", + WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B1_CTL, 0, -84, 40, + digital_gain), + SOC_SINGLE_SX_TLV("IIR0 INP1 Volume", + WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B2_CTL, 0, -84, 40, + digital_gain), + SOC_SINGLE_SX_TLV("IIR0 INP2 Volume", + WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B3_CTL, 0, -84, 40, + digital_gain), + SOC_SINGLE_SX_TLV("IIR0 INP3 Volume", + WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B4_CTL, 0, -84, 40, + digital_gain), + SOC_SINGLE_SX_TLV("IIR1 INP0 Volume", + WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B1_CTL, 0, -84, 40, + digital_gain), + SOC_SINGLE_SX_TLV("IIR1 INP1 Volume", + WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B2_CTL, 0, -84, 40, + digital_gain), + SOC_SINGLE_SX_TLV("IIR1 INP2 Volume", + WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B3_CTL, 0, -84, 40, + digital_gain), + SOC_SINGLE_SX_TLV("IIR1 INP3 Volume", + WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B4_CTL, 0, -84, 40, + digital_gain), + + SOC_SINGLE_EXT("ANC Slot", SND_SOC_NOPM, 0, 100, 0, tavil_get_anc_slot, + tavil_put_anc_slot), + SOC_ENUM_EXT("ANC Function", tavil_anc_func_enum, tavil_get_anc_func, + tavil_put_anc_func), + + SOC_ENUM_EXT("CLK MODE", tavil_clkmode_enum, tavil_get_clkmode, + tavil_put_clkmode), + + SOC_ENUM("TX0 HPF cut off", cf_dec0_enum), + SOC_ENUM("TX1 HPF cut off", cf_dec1_enum), + SOC_ENUM("TX2 HPF cut off", cf_dec2_enum), + SOC_ENUM("TX3 HPF cut off", cf_dec3_enum), + SOC_ENUM("TX4 HPF cut off", cf_dec4_enum), + SOC_ENUM("TX5 HPF cut off", cf_dec5_enum), + SOC_ENUM("TX6 HPF cut off", cf_dec6_enum), + SOC_ENUM("TX7 HPF cut off", cf_dec7_enum), + SOC_ENUM("TX8 HPF cut off", cf_dec8_enum), + + SOC_ENUM("RX INT0_1 HPF cut off", cf_int0_1_enum), + SOC_ENUM("RX INT0_2 HPF cut off", cf_int0_2_enum), + SOC_ENUM("RX INT1_1 HPF cut off", cf_int1_1_enum), + SOC_ENUM("RX INT1_2 HPF cut off", cf_int1_2_enum), + SOC_ENUM("RX INT2_1 HPF cut off", cf_int2_1_enum), + SOC_ENUM("RX INT2_2 HPF cut off", cf_int2_2_enum), + SOC_ENUM("RX INT3_1 HPF cut off", cf_int3_1_enum), + SOC_ENUM("RX INT3_2 HPF cut off", cf_int3_2_enum), + SOC_ENUM("RX INT4_1 HPF cut off", cf_int4_1_enum), + SOC_ENUM("RX INT4_2 HPF cut off", cf_int4_2_enum), + SOC_ENUM("RX INT7_1 HPF cut off", cf_int7_1_enum), + SOC_ENUM("RX INT7_2 HPF cut off", cf_int7_2_enum), + SOC_ENUM("RX INT8_1 HPF cut off", cf_int8_1_enum), + SOC_ENUM("RX INT8_2 HPF cut off", cf_int8_2_enum), + + SOC_ENUM_EXT("RX HPH Mode", rx_hph_mode_mux_enum, + tavil_rx_hph_mode_get, tavil_rx_hph_mode_put), + + SOC_SINGLE_EXT("IIR0 Enable Band1", IIR0, BAND1, 1, 0, + tavil_iir_enable_audio_mixer_get, + tavil_iir_enable_audio_mixer_put), + SOC_SINGLE_EXT("IIR0 Enable Band2", IIR0, BAND2, 1, 0, + tavil_iir_enable_audio_mixer_get, + tavil_iir_enable_audio_mixer_put), + SOC_SINGLE_EXT("IIR0 Enable Band3", IIR0, BAND3, 1, 0, + tavil_iir_enable_audio_mixer_get, + tavil_iir_enable_audio_mixer_put), + SOC_SINGLE_EXT("IIR0 Enable Band4", IIR0, BAND4, 1, 0, + tavil_iir_enable_audio_mixer_get, + tavil_iir_enable_audio_mixer_put), + SOC_SINGLE_EXT("IIR0 Enable Band5", IIR0, BAND5, 1, 0, + tavil_iir_enable_audio_mixer_get, + tavil_iir_enable_audio_mixer_put), + SOC_SINGLE_EXT("IIR1 Enable Band1", IIR1, BAND1, 1, 0, + tavil_iir_enable_audio_mixer_get, + tavil_iir_enable_audio_mixer_put), + SOC_SINGLE_EXT("IIR1 Enable Band2", IIR1, BAND2, 1, 0, + tavil_iir_enable_audio_mixer_get, + tavil_iir_enable_audio_mixer_put), + SOC_SINGLE_EXT("IIR1 Enable Band3", IIR1, BAND3, 1, 0, + tavil_iir_enable_audio_mixer_get, + tavil_iir_enable_audio_mixer_put), + SOC_SINGLE_EXT("IIR1 Enable Band4", IIR1, BAND4, 1, 0, + tavil_iir_enable_audio_mixer_get, + tavil_iir_enable_audio_mixer_put), + SOC_SINGLE_EXT("IIR1 Enable Band5", IIR1, BAND5, 1, 0, + tavil_iir_enable_audio_mixer_get, + tavil_iir_enable_audio_mixer_put), + + SOC_SINGLE_MULTI_EXT("IIR0 Band1", IIR0, BAND1, 255, 0, 5, + tavil_iir_band_audio_mixer_get, tavil_iir_band_audio_mixer_put), + SOC_SINGLE_MULTI_EXT("IIR0 Band2", IIR0, BAND2, 255, 0, 5, + tavil_iir_band_audio_mixer_get, tavil_iir_band_audio_mixer_put), + SOC_SINGLE_MULTI_EXT("IIR0 Band3", IIR0, BAND3, 255, 0, 5, + tavil_iir_band_audio_mixer_get, tavil_iir_band_audio_mixer_put), + SOC_SINGLE_MULTI_EXT("IIR0 Band4", IIR0, BAND4, 255, 0, 5, + tavil_iir_band_audio_mixer_get, tavil_iir_band_audio_mixer_put), + SOC_SINGLE_MULTI_EXT("IIR0 Band5", IIR0, BAND5, 255, 0, 5, + tavil_iir_band_audio_mixer_get, tavil_iir_band_audio_mixer_put), + SOC_SINGLE_MULTI_EXT("IIR1 Band1", IIR1, BAND1, 255, 0, 5, + tavil_iir_band_audio_mixer_get, tavil_iir_band_audio_mixer_put), + SOC_SINGLE_MULTI_EXT("IIR1 Band2", IIR1, BAND2, 255, 0, 5, + tavil_iir_band_audio_mixer_get, tavil_iir_band_audio_mixer_put), + SOC_SINGLE_MULTI_EXT("IIR1 Band3", IIR1, BAND3, 255, 0, 5, + tavil_iir_band_audio_mixer_get, tavil_iir_band_audio_mixer_put), + SOC_SINGLE_MULTI_EXT("IIR1 Band4", IIR1, BAND4, 255, 0, 5, + tavil_iir_band_audio_mixer_get, tavil_iir_band_audio_mixer_put), + SOC_SINGLE_MULTI_EXT("IIR1 Band5", IIR1, BAND5, 255, 0, 5, + tavil_iir_band_audio_mixer_get, tavil_iir_band_audio_mixer_put), + + SOC_SINGLE_EXT("COMP1 Switch", SND_SOC_NOPM, COMPANDER_1, 1, 0, + tavil_compander_get, tavil_compander_put), + SOC_SINGLE_EXT("COMP2 Switch", SND_SOC_NOPM, COMPANDER_2, 1, 0, + tavil_compander_get, tavil_compander_put), + SOC_SINGLE_EXT("COMP3 Switch", SND_SOC_NOPM, COMPANDER_3, 1, 0, + tavil_compander_get, tavil_compander_put), + SOC_SINGLE_EXT("COMP4 Switch", SND_SOC_NOPM, COMPANDER_4, 1, 0, + tavil_compander_get, tavil_compander_put), + SOC_SINGLE_EXT("COMP7 Switch", SND_SOC_NOPM, COMPANDER_7, 1, 0, + tavil_compander_get, tavil_compander_put), + SOC_SINGLE_EXT("COMP8 Switch", SND_SOC_NOPM, COMPANDER_8, 1, 0, + tavil_compander_get, tavil_compander_put), + + SOC_ENUM_EXT("ASRC0 Output Mode", asrc_mode_enum, + tavil_hph_asrc_mode_get, tavil_hph_asrc_mode_put), + SOC_ENUM_EXT("ASRC1 Output Mode", asrc_mode_enum, + tavil_hph_asrc_mode_get, tavil_hph_asrc_mode_put), + + SOC_ENUM_EXT("HPH Idle Detect", hph_idle_detect_enum, + tavil_hph_idle_detect_get, tavil_hph_idle_detect_put), + + SOC_ENUM_EXT("MAD Input", tavil_conn_mad_enum, + tavil_mad_input_get, tavil_mad_input_put), + + SOC_SINGLE_EXT("DMIC1_CLK_PIN_MODE", SND_SOC_NOPM, 17, 1, 0, + tavil_dmic_pin_mode_get, tavil_dmic_pin_mode_put), + + SOC_SINGLE_EXT("DMIC1_DATA_PIN_MODE", SND_SOC_NOPM, 18, 1, 0, + tavil_dmic_pin_mode_get, tavil_dmic_pin_mode_put), + + SOC_SINGLE_EXT("DMIC2_CLK_PIN_MODE", SND_SOC_NOPM, 19, 1, 0, + tavil_dmic_pin_mode_get, tavil_dmic_pin_mode_put), + + SOC_SINGLE_EXT("DMIC2_DATA_PIN_MODE", SND_SOC_NOPM, 20, 1, 0, + tavil_dmic_pin_mode_get, tavil_dmic_pin_mode_put), + + SOC_SINGLE_EXT("DMIC3_CLK_PIN_MODE", SND_SOC_NOPM, 21, 1, 0, + tavil_dmic_pin_mode_get, tavil_dmic_pin_mode_put), + + SOC_SINGLE_EXT("DMIC3_DATA_PIN_MODE", SND_SOC_NOPM, 22, 1, 0, + tavil_dmic_pin_mode_get, tavil_dmic_pin_mode_put), + SOC_ENUM_EXT("AMIC_1_2 PWR MODE", amic_pwr_lvl_enum, + tavil_amic_pwr_lvl_get, tavil_amic_pwr_lvl_put), + SOC_ENUM_EXT("AMIC_3_4 PWR MODE", amic_pwr_lvl_enum, + tavil_amic_pwr_lvl_get, tavil_amic_pwr_lvl_put), + SOC_ENUM_EXT("AMIC_5_6 PWR MODE", amic_pwr_lvl_enum, + tavil_amic_pwr_lvl_get, tavil_amic_pwr_lvl_put), + + SOC_ENUM_EXT("DMIC Drive Ctl", dmic_drv_ctl_enum, + tavil_dmic_drv_get, tavil_dmic_drv_put), +}; + +static int tavil_dec_enum_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int val; + u16 mic_sel_reg = 0; + u8 mic_sel; + + val = ucontrol->value.enumerated.item[0]; + if (val > e->items - 1) + return -EINVAL; + + dev_dbg(codec->dev, "%s: wname: %s, val: 0x%x\n", __func__, + widget->name, val); + + switch (e->reg) { + case WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG1: + if (e->shift_l == 0) + mic_sel_reg = WCD934X_CDC_TX0_TX_PATH_CFG0; + else if (e->shift_l == 2) + mic_sel_reg = WCD934X_CDC_TX4_TX_PATH_CFG0; + else if (e->shift_l == 4) + mic_sel_reg = WCD934X_CDC_TX8_TX_PATH_CFG0; + break; + case WCD934X_CDC_TX_INP_MUX_ADC_MUX1_CFG1: + if (e->shift_l == 0) + mic_sel_reg = WCD934X_CDC_TX1_TX_PATH_CFG0; + else if (e->shift_l == 2) + mic_sel_reg = WCD934X_CDC_TX5_TX_PATH_CFG0; + break; + case WCD934X_CDC_TX_INP_MUX_ADC_MUX2_CFG1: + if (e->shift_l == 0) + mic_sel_reg = WCD934X_CDC_TX2_TX_PATH_CFG0; + else if (e->shift_l == 2) + mic_sel_reg = WCD934X_CDC_TX6_TX_PATH_CFG0; + break; + case WCD934X_CDC_TX_INP_MUX_ADC_MUX3_CFG1: + if (e->shift_l == 0) + mic_sel_reg = WCD934X_CDC_TX3_TX_PATH_CFG0; + else if (e->shift_l == 2) + mic_sel_reg = WCD934X_CDC_TX7_TX_PATH_CFG0; + break; + default: + dev_err(codec->dev, "%s: e->reg: 0x%x not expected\n", + __func__, e->reg); + return -EINVAL; + } + + /* ADC: 0, DMIC: 1 */ + mic_sel = val ? 0x0 : 0x1; + if (mic_sel_reg) + snd_soc_update_bits(codec, mic_sel_reg, 1 << 7, mic_sel << 7); + + return snd_soc_dapm_put_enum_double(kcontrol, ucontrol); +} + +static int tavil_int_dem_inp_mux_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int val; + unsigned short look_ahead_dly_reg = WCD934X_CDC_RX0_RX_PATH_CFG0; + + val = ucontrol->value.enumerated.item[0]; + if (val >= e->items) + return -EINVAL; + + dev_dbg(codec->dev, "%s: wname: %s, val: 0x%x\n", __func__, + widget->name, val); + + if (e->reg == WCD934X_CDC_RX0_RX_PATH_SEC0) + look_ahead_dly_reg = WCD934X_CDC_RX0_RX_PATH_CFG0; + else if (e->reg == WCD934X_CDC_RX1_RX_PATH_SEC0) + look_ahead_dly_reg = WCD934X_CDC_RX1_RX_PATH_CFG0; + else if (e->reg == WCD934X_CDC_RX2_RX_PATH_SEC0) + look_ahead_dly_reg = WCD934X_CDC_RX2_RX_PATH_CFG0; + + /* Set Look Ahead Delay */ + snd_soc_update_bits(codec, look_ahead_dly_reg, + 0x08, (val ? 0x08 : 0x00)); + /* Set DEM INP Select */ + return snd_soc_dapm_put_enum_double(kcontrol, ucontrol); +} + +static const char * const rx_int0_7_mix_mux_text[] = { + "ZERO", "RX0", "RX1", "RX2", "RX3", "RX4", "RX5", + "RX6", "RX7", "PROXIMITY" +}; + +static const char * const rx_int_mix_mux_text[] = { + "ZERO", "RX0", "RX1", "RX2", "RX3", "RX4", "RX5", + "RX6", "RX7" +}; + +static const char * const rx_prim_mix_text[] = { + "ZERO", "DEC0", "DEC1", "IIR0", "IIR1", "RX0", "RX1", "RX2", + "RX3", "RX4", "RX5", "RX6", "RX7" +}; + +static const char * const rx_sidetone_mix_text[] = { + "ZERO", "SRC0", "SRC1", "SRC_SUM" +}; + +static const char * const cdc_if_tx0_mux_text[] = { + "ZERO", "RX_MIX_TX0", "DEC0", "DEC0_192" +}; +static const char * const cdc_if_tx1_mux_text[] = { + "ZERO", "RX_MIX_TX1", "DEC1", "DEC1_192" +}; +static const char * const cdc_if_tx2_mux_text[] = { + "ZERO", "RX_MIX_TX2", "DEC2", "DEC2_192" +}; +static const char * const cdc_if_tx3_mux_text[] = { + "ZERO", "RX_MIX_TX3", "DEC3", "DEC3_192" +}; +static const char * const cdc_if_tx4_mux_text[] = { + "ZERO", "RX_MIX_TX4", "DEC4", "DEC4_192" +}; +static const char * const cdc_if_tx5_mux_text[] = { + "ZERO", "RX_MIX_TX5", "DEC5", "DEC5_192" +}; +static const char * const cdc_if_tx6_mux_text[] = { + "ZERO", "RX_MIX_TX6", "DEC6", "DEC6_192" +}; +static const char * const cdc_if_tx7_mux_text[] = { + "ZERO", "RX_MIX_TX7", "DEC7", "DEC7_192" +}; +static const char * const cdc_if_tx8_mux_text[] = { + "ZERO", "RX_MIX_TX8", "DEC8", "DEC8_192" +}; +static const char * const cdc_if_tx9_mux_text[] = { + "ZERO", "DEC7", "DEC7_192" +}; +static const char * const cdc_if_tx10_mux_text[] = { + "ZERO", "DEC6", "DEC6_192" +}; +static const char * const cdc_if_tx11_mux_text[] = { + "DEC_0_5", "DEC_9_12", "MAD_AUDIO", "MAD_BRDCST" +}; +static const char * const cdc_if_tx11_inp1_mux_text[] = { + "ZERO", "DEC0", "DEC1", "DEC2", "DEC3", "DEC4", + "DEC5", "RX_MIX_TX5", "DEC9_10", "DEC11_12" +}; +static const char * const cdc_if_tx13_mux_text[] = { + "CDC_DEC_5", "MAD_BRDCST" +}; +static const char * const cdc_if_tx13_inp1_mux_text[] = { + "ZERO", "DEC5", "DEC5_192" +}; + +static const char * const iir_inp_mux_text[] = { + "ZERO", "DEC0", "DEC1", "DEC2", "DEC3", "DEC4", "DEC5", "DEC6", + "DEC7", "DEC8", "RX0", "RX1", "RX2", "RX3", "RX4", "RX5", "RX6", "RX7" +}; + +static const char * const rx_int_dem_inp_mux_text[] = { + "NORMAL_DSM_OUT", "CLSH_DSM_OUT", +}; + +static const char * const rx_int0_1_interp_mux_text[] = { + "ZERO", "RX INT0_1 MIX1", +}; + +static const char * const rx_int1_1_interp_mux_text[] = { + "ZERO", "RX INT1_1 MIX1", +}; + +static const char * const rx_int2_1_interp_mux_text[] = { + "ZERO", "RX INT2_1 MIX1", +}; + +static const char * const rx_int3_1_interp_mux_text[] = { + "ZERO", "RX INT3_1 MIX1", +}; + +static const char * const rx_int4_1_interp_mux_text[] = { + "ZERO", "RX INT4_1 MIX1", +}; + +static const char * const rx_int7_1_interp_mux_text[] = { + "ZERO", "RX INT7_1 MIX1", +}; + +static const char * const rx_int8_1_interp_mux_text[] = { + "ZERO", "RX INT8_1 MIX1", +}; + +static const char * const rx_int0_2_interp_mux_text[] = { + "ZERO", "RX INT0_2 MUX", +}; + +static const char * const rx_int1_2_interp_mux_text[] = { + "ZERO", "RX INT1_2 MUX", +}; + +static const char * const rx_int2_2_interp_mux_text[] = { + "ZERO", "RX INT2_2 MUX", +}; + +static const char * const rx_int3_2_interp_mux_text[] = { + "ZERO", "RX INT3_2 MUX", +}; + +static const char * const rx_int4_2_interp_mux_text[] = { + "ZERO", "RX INT4_2 MUX", +}; + +static const char * const rx_int7_2_interp_mux_text[] = { + "ZERO", "RX INT7_2 MUX", +}; + +static const char * const rx_int8_2_interp_mux_text[] = { + "ZERO", "RX INT8_2 MUX", +}; + +static const char * const mad_sel_txt[] = { + "SPE", "MSM" +}; + +static const char * const mad_inp_mux_txt[] = { + "MAD", "DEC1" +}; + +static const char * const adc_mux_text[] = { + "DMIC", "AMIC", "ANC_FB_TUNE1", "ANC_FB_TUNE2" +}; + +static const char * const dmic_mux_text[] = { + "ZERO", "DMIC0", "DMIC1", "DMIC2", "DMIC3", "DMIC4", "DMIC5" +}; + +static const char * const amic_mux_text[] = { + "ZERO", "ADC1", "ADC2", "ADC3", "ADC4" +}; + +static const char * const amic4_5_sel_text[] = { + "AMIC4", "AMIC5" +}; + +static const char * const anc0_fb_mux_text[] = { + "ZERO", "ANC_IN_HPHL", "ANC_IN_EAR", "ANC_IN_EAR_SPKR", + "ANC_IN_LO1" +}; + +static const char * const anc1_fb_mux_text[] = { + "ZERO", "ANC_IN_HPHR", "ANC_IN_LO2" +}; + +static const char * const rx_echo_mux_text[] = { + "ZERO", "RX_MIX0", "RX_MIX1", "RX_MIX2", "RX_MIX3", "RX_MIX4", + "RX_MIX5", "RX_MIX6", "RX_MIX7", "RX_MIX8" +}; + +static const char *const slim_rx_mux_text[] = { + "ZERO", "AIF1_PB", "AIF2_PB", "AIF3_PB", "AIF4_PB" +}; + +static const char *const i2s_rx01_mux_text[] = { + "ZERO", "AIF1_PB", "AIF2_PB", "AIF3_PB" +}; + +static const char *const i2s_rx23_mux_text[] = { + "ZERO", "AIF1_PB", "AIF2_PB", "AIF3_PB" +}; + +static const char *const i2s_rx45_mux_text[] = { + "ZERO", "AIF1_PB", "AIF2_PB", "AIF3_PB" +}; + +static const char *const i2s_rx67_mux_text[] = { + "ZERO", "AIF1_PB", "AIF2_PB", "AIF3_PB" +}; + +static const char *const cdc_if_rx0_mux_text[] = { + "SLIM RX0", "I2S RX0" +}; +static const char *const cdc_if_rx1_mux_text[] = { + "SLIM RX1", "I2S RX1" +}; +static const char *const cdc_if_rx2_mux_text[] = { + "SLIM RX2", "I2S RX2" +}; +static const char *const cdc_if_rx3_mux_text[] = { + "SLIM RX3", "I2S RX3" +}; +static const char *const cdc_if_rx4_mux_text[] = { + "SLIM RX4", "I2S RX4" +}; +static const char *const cdc_if_rx5_mux_text[] = { + "SLIM RX5", "I2S RX5" +}; +static const char *const cdc_if_rx6_mux_text[] = { + "SLIM RX6", "I2S RX6" +}; +static const char *const cdc_if_rx7_mux_text[] = { + "SLIM RX7", "I2S RX7" +}; + +static const char * const asrc0_mux_text[] = { + "ZERO", "ASRC_IN_HPHL", "ASRC_IN_LO1", +}; + +static const char * const asrc1_mux_text[] = { + "ZERO", "ASRC_IN_HPHR", "ASRC_IN_LO2", +}; + +static const char * const asrc2_mux_text[] = { + "ZERO", "ASRC_IN_SPKR1", +}; + +static const char * const asrc3_mux_text[] = { + "ZERO", "ASRC_IN_SPKR2", +}; + +static const char * const native_mux_text[] = { + "OFF", "ON", +}; + +static const char *const wdma3_port0_text[] = { + "RX_MIX_TX0", "DEC0" +}; + +static const char *const wdma3_port1_text[] = { + "RX_MIX_TX1", "DEC1" +}; + +static const char *const wdma3_port2_text[] = { + "RX_MIX_TX2", "DEC2" +}; + +static const char *const wdma3_port3_text[] = { + "RX_MIX_TX3", "DEC3" +}; + +static const char *const wdma3_port4_text[] = { + "RX_MIX_TX4", "DEC4" +}; + +static const char *const wdma3_port5_text[] = { + "RX_MIX_TX5", "DEC5" +}; + +static const char *const wdma3_port6_text[] = { + "RX_MIX_TX6", "DEC6" +}; + +static const char *const wdma3_ch_text[] = { + "PORT_0", "PORT_1", "PORT_2", "PORT_3", "PORT_4", + "PORT_5", "PORT_6", "PORT_7", "PORT_8", +}; + +static const struct snd_kcontrol_new aif4_vi_mixer[] = { + SOC_SINGLE_EXT("SPKR_VI_1", SND_SOC_NOPM, WCD934X_TX14, 1, 0, + tavil_vi_feed_mixer_get, tavil_vi_feed_mixer_put), + SOC_SINGLE_EXT("SPKR_VI_2", SND_SOC_NOPM, WCD934X_TX15, 1, 0, + tavil_vi_feed_mixer_get, tavil_vi_feed_mixer_put), +}; + +static const struct snd_kcontrol_new aif1_slim_cap_mixer[] = { + SOC_SINGLE_EXT("SLIM TX0", SND_SOC_NOPM, WCD934X_TX0, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX1", SND_SOC_NOPM, WCD934X_TX1, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX2", SND_SOC_NOPM, WCD934X_TX2, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX3", SND_SOC_NOPM, WCD934X_TX3, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX4", SND_SOC_NOPM, WCD934X_TX4, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX5", SND_SOC_NOPM, WCD934X_TX5, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX6", SND_SOC_NOPM, WCD934X_TX6, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX7", SND_SOC_NOPM, WCD934X_TX7, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX8", SND_SOC_NOPM, WCD934X_TX8, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX9", SND_SOC_NOPM, WCD934X_TX9, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX10", SND_SOC_NOPM, WCD934X_TX10, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX11", SND_SOC_NOPM, WCD934X_TX11, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX13", SND_SOC_NOPM, WCD934X_TX13, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), +}; + +static const struct snd_kcontrol_new aif1_i2s_cap_mixer[] = { + SOC_SINGLE_EXT("I2S TX1", SND_SOC_NOPM, WCD934X_TX1, 1, 0, + i2s_tx_mixer_get, i2s_tx_mixer_put), + SOC_SINGLE_EXT("I2S TX2", SND_SOC_NOPM, WCD934X_TX2, 1, 0, + i2s_tx_mixer_get, i2s_tx_mixer_put), + SOC_SINGLE_EXT("I2S TX3", SND_SOC_NOPM, WCD934X_TX3, 1, 0, + i2s_tx_mixer_get, i2s_tx_mixer_put), + SOC_SINGLE_EXT("I2S TX4", SND_SOC_NOPM, WCD934X_TX4, 1, 0, + i2s_tx_mixer_get, i2s_tx_mixer_put), + SOC_SINGLE_EXT("I2S TX5", SND_SOC_NOPM, WCD934X_TX5, 1, 0, + i2s_tx_mixer_get, i2s_tx_mixer_put), + SOC_SINGLE_EXT("I2S TX6", SND_SOC_NOPM, WCD934X_TX6, 1, 0, + i2s_tx_mixer_get, i2s_tx_mixer_put), + SOC_SINGLE_EXT("I2S TX7", SND_SOC_NOPM, WCD934X_TX7, 1, 0, + i2s_tx_mixer_get, i2s_tx_mixer_put), +}; + +static const struct snd_kcontrol_new aif2_slim_cap_mixer[] = { + SOC_SINGLE_EXT("SLIM TX0", SND_SOC_NOPM, WCD934X_TX0, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX1", SND_SOC_NOPM, WCD934X_TX1, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX2", SND_SOC_NOPM, WCD934X_TX2, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX3", SND_SOC_NOPM, WCD934X_TX3, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX4", SND_SOC_NOPM, WCD934X_TX4, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX5", SND_SOC_NOPM, WCD934X_TX5, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX6", SND_SOC_NOPM, WCD934X_TX6, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX7", SND_SOC_NOPM, WCD934X_TX7, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX8", SND_SOC_NOPM, WCD934X_TX8, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX9", SND_SOC_NOPM, WCD934X_TX9, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX10", SND_SOC_NOPM, WCD934X_TX10, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX11", SND_SOC_NOPM, WCD934X_TX11, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX13", SND_SOC_NOPM, WCD934X_TX13, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), +}; + +static const struct snd_kcontrol_new aif2_i2s_cap_mixer[] = { + SOC_SINGLE_EXT("I2S TX8", SND_SOC_NOPM, WCD934X_TX8, 1, 0, + i2s_tx_mixer_get, i2s_tx_mixer_put), + SOC_SINGLE_EXT("I2S TX11", SND_SOC_NOPM, WCD934X_TX11, 1, 0, + i2s_tx_mixer_get, i2s_tx_mixer_put), +}; + +static const struct snd_kcontrol_new aif3_slim_cap_mixer[] = { + SOC_SINGLE_EXT("SLIM TX0", SND_SOC_NOPM, WCD934X_TX0, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX1", SND_SOC_NOPM, WCD934X_TX1, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX2", SND_SOC_NOPM, WCD934X_TX2, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX3", SND_SOC_NOPM, WCD934X_TX3, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX4", SND_SOC_NOPM, WCD934X_TX4, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX5", SND_SOC_NOPM, WCD934X_TX5, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX6", SND_SOC_NOPM, WCD934X_TX6, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX7", SND_SOC_NOPM, WCD934X_TX7, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX8", SND_SOC_NOPM, WCD934X_TX8, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX9", SND_SOC_NOPM, WCD934X_TX9, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX10", SND_SOC_NOPM, WCD934X_TX10, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX11", SND_SOC_NOPM, WCD934X_TX11, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX13", SND_SOC_NOPM, WCD934X_TX13, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), +}; + +static const struct snd_kcontrol_new aif3_i2s_cap_mixer[] = { + SOC_SINGLE_EXT("I2S TX0", SND_SOC_NOPM, WCD934X_TX0, 1, 0, + i2s_tx_mixer_get, i2s_tx_mixer_put), + SOC_SINGLE_EXT("I2S TX1", SND_SOC_NOPM, WCD934X_TX1, 1, 0, + i2s_tx_mixer_get, i2s_tx_mixer_put), +}; + +static const struct snd_kcontrol_new aif4_slim_mad_mixer[] = { + SOC_SINGLE_EXT("SLIM TX13", SND_SOC_NOPM, WCD934X_TX13, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), +}; + +WCD_DAPM_ENUM_EXT(slim_rx0, SND_SOC_NOPM, 0, slim_rx_mux_text, + slim_rx_mux_get, slim_rx_mux_put); +WCD_DAPM_ENUM_EXT(slim_rx1, SND_SOC_NOPM, 0, slim_rx_mux_text, + slim_rx_mux_get, slim_rx_mux_put); +WCD_DAPM_ENUM_EXT(slim_rx2, SND_SOC_NOPM, 0, slim_rx_mux_text, + slim_rx_mux_get, slim_rx_mux_put); +WCD_DAPM_ENUM_EXT(slim_rx3, SND_SOC_NOPM, 0, slim_rx_mux_text, + slim_rx_mux_get, slim_rx_mux_put); +WCD_DAPM_ENUM_EXT(slim_rx4, SND_SOC_NOPM, 0, slim_rx_mux_text, + slim_rx_mux_get, slim_rx_mux_put); +WCD_DAPM_ENUM_EXT(slim_rx5, SND_SOC_NOPM, 0, slim_rx_mux_text, + slim_rx_mux_get, slim_rx_mux_put); +WCD_DAPM_ENUM_EXT(slim_rx6, SND_SOC_NOPM, 0, slim_rx_mux_text, + slim_rx_mux_get, slim_rx_mux_put); +WCD_DAPM_ENUM_EXT(slim_rx7, SND_SOC_NOPM, 0, slim_rx_mux_text, + slim_rx_mux_get, slim_rx_mux_put); + +WCD_DAPM_ENUM(cdc_if_rx0, SND_SOC_NOPM, 0, cdc_if_rx0_mux_text); +WCD_DAPM_ENUM(cdc_if_rx1, SND_SOC_NOPM, 0, cdc_if_rx1_mux_text); +WCD_DAPM_ENUM(cdc_if_rx2, SND_SOC_NOPM, 0, cdc_if_rx2_mux_text); +WCD_DAPM_ENUM(cdc_if_rx3, SND_SOC_NOPM, 0, cdc_if_rx3_mux_text); +WCD_DAPM_ENUM(cdc_if_rx4, SND_SOC_NOPM, 0, cdc_if_rx4_mux_text); +WCD_DAPM_ENUM(cdc_if_rx5, SND_SOC_NOPM, 0, cdc_if_rx5_mux_text); +WCD_DAPM_ENUM(cdc_if_rx6, SND_SOC_NOPM, 0, cdc_if_rx6_mux_text); +WCD_DAPM_ENUM(cdc_if_rx7, SND_SOC_NOPM, 0, cdc_if_rx7_mux_text); + +WCD_DAPM_ENUM(rx_int0_2, WCD934X_CDC_RX_INP_MUX_RX_INT0_CFG1, 0, + rx_int0_7_mix_mux_text); +WCD_DAPM_ENUM(rx_int1_2, WCD934X_CDC_RX_INP_MUX_RX_INT1_CFG1, 0, + rx_int_mix_mux_text); +WCD_DAPM_ENUM(rx_int2_2, WCD934X_CDC_RX_INP_MUX_RX_INT2_CFG1, 0, + rx_int_mix_mux_text); +WCD_DAPM_ENUM(rx_int3_2, WCD934X_CDC_RX_INP_MUX_RX_INT3_CFG1, 0, + rx_int_mix_mux_text); +WCD_DAPM_ENUM(rx_int4_2, WCD934X_CDC_RX_INP_MUX_RX_INT4_CFG1, 0, + rx_int_mix_mux_text); +WCD_DAPM_ENUM(rx_int7_2, WCD934X_CDC_RX_INP_MUX_RX_INT7_CFG1, 0, + rx_int0_7_mix_mux_text); +WCD_DAPM_ENUM(rx_int8_2, WCD934X_CDC_RX_INP_MUX_RX_INT8_CFG1, 0, + rx_int_mix_mux_text); + +WCD_DAPM_ENUM(rx_int0_1_mix_inp0, WCD934X_CDC_RX_INP_MUX_RX_INT0_CFG0, 0, + rx_prim_mix_text); +WCD_DAPM_ENUM(rx_int0_1_mix_inp1, WCD934X_CDC_RX_INP_MUX_RX_INT0_CFG0, 4, + rx_prim_mix_text); +WCD_DAPM_ENUM(rx_int0_1_mix_inp2, WCD934X_CDC_RX_INP_MUX_RX_INT0_CFG1, 4, + rx_prim_mix_text); +WCD_DAPM_ENUM(rx_int1_1_mix_inp0, WCD934X_CDC_RX_INP_MUX_RX_INT1_CFG0, 0, + rx_prim_mix_text); +WCD_DAPM_ENUM(rx_int1_1_mix_inp1, WCD934X_CDC_RX_INP_MUX_RX_INT1_CFG0, 4, + rx_prim_mix_text); +WCD_DAPM_ENUM(rx_int1_1_mix_inp2, WCD934X_CDC_RX_INP_MUX_RX_INT1_CFG1, 4, + rx_prim_mix_text); +WCD_DAPM_ENUM(rx_int2_1_mix_inp0, WCD934X_CDC_RX_INP_MUX_RX_INT2_CFG0, 0, + rx_prim_mix_text); +WCD_DAPM_ENUM(rx_int2_1_mix_inp1, WCD934X_CDC_RX_INP_MUX_RX_INT2_CFG0, 4, + rx_prim_mix_text); +WCD_DAPM_ENUM(rx_int2_1_mix_inp2, WCD934X_CDC_RX_INP_MUX_RX_INT2_CFG1, 4, + rx_prim_mix_text); +WCD_DAPM_ENUM(rx_int3_1_mix_inp0, WCD934X_CDC_RX_INP_MUX_RX_INT3_CFG0, 0, + rx_prim_mix_text); +WCD_DAPM_ENUM(rx_int3_1_mix_inp1, WCD934X_CDC_RX_INP_MUX_RX_INT3_CFG0, 4, + rx_prim_mix_text); +WCD_DAPM_ENUM(rx_int3_1_mix_inp2, WCD934X_CDC_RX_INP_MUX_RX_INT3_CFG1, 4, + rx_prim_mix_text); +WCD_DAPM_ENUM(rx_int4_1_mix_inp0, WCD934X_CDC_RX_INP_MUX_RX_INT4_CFG0, 0, + rx_prim_mix_text); +WCD_DAPM_ENUM(rx_int4_1_mix_inp1, WCD934X_CDC_RX_INP_MUX_RX_INT4_CFG0, 4, + rx_prim_mix_text); +WCD_DAPM_ENUM(rx_int4_1_mix_inp2, WCD934X_CDC_RX_INP_MUX_RX_INT4_CFG1, 4, + rx_prim_mix_text); +WCD_DAPM_ENUM(rx_int7_1_mix_inp0, WCD934X_CDC_RX_INP_MUX_RX_INT7_CFG0, 0, + rx_prim_mix_text); +WCD_DAPM_ENUM(rx_int7_1_mix_inp1, WCD934X_CDC_RX_INP_MUX_RX_INT7_CFG0, 4, + rx_prim_mix_text); +WCD_DAPM_ENUM(rx_int7_1_mix_inp2, WCD934X_CDC_RX_INP_MUX_RX_INT7_CFG1, 4, + rx_prim_mix_text); +WCD_DAPM_ENUM(rx_int8_1_mix_inp0, WCD934X_CDC_RX_INP_MUX_RX_INT8_CFG0, 0, + rx_prim_mix_text); +WCD_DAPM_ENUM(rx_int8_1_mix_inp1, WCD934X_CDC_RX_INP_MUX_RX_INT8_CFG0, 4, + rx_prim_mix_text); +WCD_DAPM_ENUM(rx_int8_1_mix_inp2, WCD934X_CDC_RX_INP_MUX_RX_INT8_CFG1, 4, + rx_prim_mix_text); + +WCD_DAPM_ENUM(rx_int0_mix2_inp, WCD934X_CDC_RX_INP_MUX_SIDETONE_SRC_CFG0, 0, + rx_sidetone_mix_text); +WCD_DAPM_ENUM(rx_int1_mix2_inp, WCD934X_CDC_RX_INP_MUX_SIDETONE_SRC_CFG0, 2, + rx_sidetone_mix_text); +WCD_DAPM_ENUM(rx_int2_mix2_inp, WCD934X_CDC_RX_INP_MUX_SIDETONE_SRC_CFG0, 4, + rx_sidetone_mix_text); +WCD_DAPM_ENUM(rx_int3_mix2_inp, WCD934X_CDC_RX_INP_MUX_SIDETONE_SRC_CFG0, 6, + rx_sidetone_mix_text); +WCD_DAPM_ENUM(rx_int4_mix2_inp, WCD934X_CDC_RX_INP_MUX_SIDETONE_SRC_CFG1, 0, + rx_sidetone_mix_text); +WCD_DAPM_ENUM(rx_int7_mix2_inp, WCD934X_CDC_RX_INP_MUX_SIDETONE_SRC_CFG1, 2, + rx_sidetone_mix_text); + +WCD_DAPM_ENUM(tx_adc_mux10, WCD934X_CDC_TX_INP_MUX_ADC_MUX1_CFG1, 4, + adc_mux_text); +WCD_DAPM_ENUM(tx_adc_mux11, WCD934X_CDC_TX_INP_MUX_ADC_MUX2_CFG1, 4, + adc_mux_text); +WCD_DAPM_ENUM(tx_adc_mux12, WCD934X_CDC_TX_INP_MUX_ADC_MUX3_CFG1, 4, + adc_mux_text); +WCD_DAPM_ENUM(tx_adc_mux13, WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG1, 6, + adc_mux_text); + + +WCD_DAPM_ENUM(tx_dmic_mux0, WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG0, 3, + dmic_mux_text); +WCD_DAPM_ENUM(tx_dmic_mux1, WCD934X_CDC_TX_INP_MUX_ADC_MUX1_CFG0, 3, + dmic_mux_text); +WCD_DAPM_ENUM(tx_dmic_mux2, WCD934X_CDC_TX_INP_MUX_ADC_MUX2_CFG0, 3, + dmic_mux_text); +WCD_DAPM_ENUM(tx_dmic_mux3, WCD934X_CDC_TX_INP_MUX_ADC_MUX3_CFG0, 3, + dmic_mux_text); +WCD_DAPM_ENUM(tx_dmic_mux4, WCD934X_CDC_TX_INP_MUX_ADC_MUX4_CFG0, 3, + dmic_mux_text); +WCD_DAPM_ENUM(tx_dmic_mux5, WCD934X_CDC_TX_INP_MUX_ADC_MUX5_CFG0, 3, + dmic_mux_text); +WCD_DAPM_ENUM(tx_dmic_mux6, WCD934X_CDC_TX_INP_MUX_ADC_MUX6_CFG0, 3, + dmic_mux_text); +WCD_DAPM_ENUM(tx_dmic_mux7, WCD934X_CDC_TX_INP_MUX_ADC_MUX7_CFG0, 3, + dmic_mux_text); +WCD_DAPM_ENUM(tx_dmic_mux8, WCD934X_CDC_TX_INP_MUX_ADC_MUX8_CFG0, 3, + dmic_mux_text); +WCD_DAPM_ENUM(tx_dmic_mux10, WCD934X_CDC_TX_INP_MUX_ADC_MUX10_CFG0, 3, + dmic_mux_text); +WCD_DAPM_ENUM(tx_dmic_mux11, WCD934X_CDC_TX_INP_MUX_ADC_MUX11_CFG0, 3, + dmic_mux_text); +WCD_DAPM_ENUM(tx_dmic_mux12, WCD934X_CDC_TX_INP_MUX_ADC_MUX12_CFG0, 3, + dmic_mux_text); +WCD_DAPM_ENUM(tx_dmic_mux13, WCD934X_CDC_TX_INP_MUX_ADC_MUX13_CFG0, 3, + dmic_mux_text); + + +WCD_DAPM_ENUM(tx_amic_mux0, WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG0, 0, + amic_mux_text); +WCD_DAPM_ENUM(tx_amic_mux1, WCD934X_CDC_TX_INP_MUX_ADC_MUX1_CFG0, 0, + amic_mux_text); +WCD_DAPM_ENUM(tx_amic_mux2, WCD934X_CDC_TX_INP_MUX_ADC_MUX2_CFG0, 0, + amic_mux_text); +WCD_DAPM_ENUM(tx_amic_mux3, WCD934X_CDC_TX_INP_MUX_ADC_MUX3_CFG0, 0, + amic_mux_text); +WCD_DAPM_ENUM(tx_amic_mux4, WCD934X_CDC_TX_INP_MUX_ADC_MUX4_CFG0, 0, + amic_mux_text); +WCD_DAPM_ENUM(tx_amic_mux5, WCD934X_CDC_TX_INP_MUX_ADC_MUX5_CFG0, 0, + amic_mux_text); +WCD_DAPM_ENUM(tx_amic_mux6, WCD934X_CDC_TX_INP_MUX_ADC_MUX6_CFG0, 0, + amic_mux_text); +WCD_DAPM_ENUM(tx_amic_mux7, WCD934X_CDC_TX_INP_MUX_ADC_MUX7_CFG0, 0, + amic_mux_text); +WCD_DAPM_ENUM(tx_amic_mux8, WCD934X_CDC_TX_INP_MUX_ADC_MUX8_CFG0, 0, + amic_mux_text); +WCD_DAPM_ENUM(tx_amic_mux10, WCD934X_CDC_TX_INP_MUX_ADC_MUX10_CFG0, 0, + amic_mux_text); +WCD_DAPM_ENUM(tx_amic_mux11, WCD934X_CDC_TX_INP_MUX_ADC_MUX11_CFG0, 0, + amic_mux_text); +WCD_DAPM_ENUM(tx_amic_mux12, WCD934X_CDC_TX_INP_MUX_ADC_MUX12_CFG0, 0, + amic_mux_text); +WCD_DAPM_ENUM(tx_amic_mux13, WCD934X_CDC_TX_INP_MUX_ADC_MUX13_CFG0, 0, + amic_mux_text); + +WCD_DAPM_ENUM(tx_amic4_5, WCD934X_TX_NEW_AMIC_4_5_SEL, 7, amic4_5_sel_text); + +WCD_DAPM_ENUM(cdc_if_tx0, WCD934X_CDC_IF_ROUTER_TX_MUX_CFG0, 0, + cdc_if_tx0_mux_text); +WCD_DAPM_ENUM(cdc_if_tx1, WCD934X_CDC_IF_ROUTER_TX_MUX_CFG0, 2, + cdc_if_tx1_mux_text); +WCD_DAPM_ENUM(cdc_if_tx2, WCD934X_CDC_IF_ROUTER_TX_MUX_CFG0, 4, + cdc_if_tx2_mux_text); +WCD_DAPM_ENUM(cdc_if_tx3, WCD934X_CDC_IF_ROUTER_TX_MUX_CFG0, 6, + cdc_if_tx3_mux_text); +WCD_DAPM_ENUM(cdc_if_tx4, WCD934X_CDC_IF_ROUTER_TX_MUX_CFG1, 0, + cdc_if_tx4_mux_text); +WCD_DAPM_ENUM(cdc_if_tx5, WCD934X_CDC_IF_ROUTER_TX_MUX_CFG1, 2, + cdc_if_tx5_mux_text); +WCD_DAPM_ENUM(cdc_if_tx6, WCD934X_CDC_IF_ROUTER_TX_MUX_CFG1, 4, + cdc_if_tx6_mux_text); +WCD_DAPM_ENUM(cdc_if_tx7, WCD934X_CDC_IF_ROUTER_TX_MUX_CFG1, 6, + cdc_if_tx7_mux_text); +WCD_DAPM_ENUM(cdc_if_tx8, WCD934X_CDC_IF_ROUTER_TX_MUX_CFG2, 0, + cdc_if_tx8_mux_text); +WCD_DAPM_ENUM(cdc_if_tx9, WCD934X_CDC_IF_ROUTER_TX_MUX_CFG2, 2, + cdc_if_tx9_mux_text); +WCD_DAPM_ENUM(cdc_if_tx10, WCD934X_CDC_IF_ROUTER_TX_MUX_CFG2, 4, + cdc_if_tx10_mux_text); +WCD_DAPM_ENUM(cdc_if_tx11_inp1, WCD934X_CDC_IF_ROUTER_TX_MUX_CFG3, 0, + cdc_if_tx11_inp1_mux_text); +WCD_DAPM_ENUM(cdc_if_tx11, WCD934X_DATA_HUB_SB_TX11_INP_CFG, 0, + cdc_if_tx11_mux_text); +WCD_DAPM_ENUM(cdc_if_tx13_inp1, WCD934X_CDC_IF_ROUTER_TX_MUX_CFG3, 4, + cdc_if_tx13_inp1_mux_text); +WCD_DAPM_ENUM(cdc_if_tx13, WCD934X_DATA_HUB_SB_TX13_INP_CFG, 0, + cdc_if_tx13_mux_text); + +WCD_DAPM_ENUM(rx_mix_tx0, WCD934X_CDC_RX_INP_MUX_RX_MIX_CFG0, 0, + rx_echo_mux_text); +WCD_DAPM_ENUM(rx_mix_tx1, WCD934X_CDC_RX_INP_MUX_RX_MIX_CFG0, 4, + rx_echo_mux_text); +WCD_DAPM_ENUM(rx_mix_tx2, WCD934X_CDC_RX_INP_MUX_RX_MIX_CFG1, 0, + rx_echo_mux_text); +WCD_DAPM_ENUM(rx_mix_tx3, WCD934X_CDC_RX_INP_MUX_RX_MIX_CFG1, 4, + rx_echo_mux_text); +WCD_DAPM_ENUM(rx_mix_tx4, WCD934X_CDC_RX_INP_MUX_RX_MIX_CFG2, 0, + rx_echo_mux_text); +WCD_DAPM_ENUM(rx_mix_tx5, WCD934X_CDC_RX_INP_MUX_RX_MIX_CFG2, 4, + rx_echo_mux_text); +WCD_DAPM_ENUM(rx_mix_tx6, WCD934X_CDC_RX_INP_MUX_RX_MIX_CFG3, 0, + rx_echo_mux_text); +WCD_DAPM_ENUM(rx_mix_tx7, WCD934X_CDC_RX_INP_MUX_RX_MIX_CFG3, 4, + rx_echo_mux_text); +WCD_DAPM_ENUM(rx_mix_tx8, WCD934X_CDC_RX_INP_MUX_RX_MIX_CFG4, 0, + rx_echo_mux_text); + +WCD_DAPM_ENUM(iir0_inp0, WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG0, 0, + iir_inp_mux_text); +WCD_DAPM_ENUM(iir0_inp1, WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG1, 0, + iir_inp_mux_text); +WCD_DAPM_ENUM(iir0_inp2, WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG2, 0, + iir_inp_mux_text); +WCD_DAPM_ENUM(iir0_inp3, WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG3, 0, + iir_inp_mux_text); +WCD_DAPM_ENUM(iir1_inp0, WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG0, 0, + iir_inp_mux_text); +WCD_DAPM_ENUM(iir1_inp1, WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG1, 0, + iir_inp_mux_text); +WCD_DAPM_ENUM(iir1_inp2, WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG2, 0, + iir_inp_mux_text); +WCD_DAPM_ENUM(iir1_inp3, WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG3, 0, + iir_inp_mux_text); + +WCD_DAPM_ENUM(rx_int0_1_interp, SND_SOC_NOPM, 0, rx_int0_1_interp_mux_text); +WCD_DAPM_ENUM(rx_int1_1_interp, SND_SOC_NOPM, 0, rx_int1_1_interp_mux_text); +WCD_DAPM_ENUM(rx_int2_1_interp, SND_SOC_NOPM, 0, rx_int2_1_interp_mux_text); +WCD_DAPM_ENUM(rx_int3_1_interp, SND_SOC_NOPM, 0, rx_int3_1_interp_mux_text); +WCD_DAPM_ENUM(rx_int4_1_interp, SND_SOC_NOPM, 0, rx_int4_1_interp_mux_text); +WCD_DAPM_ENUM(rx_int7_1_interp, SND_SOC_NOPM, 0, rx_int7_1_interp_mux_text); +WCD_DAPM_ENUM(rx_int8_1_interp, SND_SOC_NOPM, 0, rx_int8_1_interp_mux_text); + +WCD_DAPM_ENUM(rx_int0_2_interp, SND_SOC_NOPM, 0, rx_int0_2_interp_mux_text); +WCD_DAPM_ENUM(rx_int1_2_interp, SND_SOC_NOPM, 0, rx_int1_2_interp_mux_text); +WCD_DAPM_ENUM(rx_int2_2_interp, SND_SOC_NOPM, 0, rx_int2_2_interp_mux_text); +WCD_DAPM_ENUM(rx_int3_2_interp, SND_SOC_NOPM, 0, rx_int3_2_interp_mux_text); +WCD_DAPM_ENUM(rx_int4_2_interp, SND_SOC_NOPM, 0, rx_int4_2_interp_mux_text); +WCD_DAPM_ENUM(rx_int7_2_interp, SND_SOC_NOPM, 0, rx_int7_2_interp_mux_text); +WCD_DAPM_ENUM(rx_int8_2_interp, SND_SOC_NOPM, 0, rx_int8_2_interp_mux_text); + +WCD_DAPM_ENUM(mad_sel, WCD934X_CPE_SS_SVA_CFG, 0, + mad_sel_txt); + +WCD_DAPM_ENUM(mad_inp_mux, WCD934X_CPE_SS_SVA_CFG, 2, + mad_inp_mux_txt); + +WCD_DAPM_ENUM_EXT(rx_int0_dem_inp, WCD934X_CDC_RX0_RX_PATH_SEC0, 0, + rx_int_dem_inp_mux_text, snd_soc_dapm_get_enum_double, + tavil_int_dem_inp_mux_put); +WCD_DAPM_ENUM_EXT(rx_int1_dem_inp, WCD934X_CDC_RX1_RX_PATH_SEC0, 0, + rx_int_dem_inp_mux_text, snd_soc_dapm_get_enum_double, + tavil_int_dem_inp_mux_put); +WCD_DAPM_ENUM_EXT(rx_int2_dem_inp, WCD934X_CDC_RX2_RX_PATH_SEC0, 0, + rx_int_dem_inp_mux_text, snd_soc_dapm_get_enum_double, + tavil_int_dem_inp_mux_put); + +WCD_DAPM_ENUM_EXT(tx_adc_mux0, WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG1, 0, + adc_mux_text, snd_soc_dapm_get_enum_double, tavil_dec_enum_put); +WCD_DAPM_ENUM_EXT(tx_adc_mux1, WCD934X_CDC_TX_INP_MUX_ADC_MUX1_CFG1, 0, + adc_mux_text, snd_soc_dapm_get_enum_double, tavil_dec_enum_put); +WCD_DAPM_ENUM_EXT(tx_adc_mux2, WCD934X_CDC_TX_INP_MUX_ADC_MUX2_CFG1, 0, + adc_mux_text, snd_soc_dapm_get_enum_double, tavil_dec_enum_put); +WCD_DAPM_ENUM_EXT(tx_adc_mux3, WCD934X_CDC_TX_INP_MUX_ADC_MUX3_CFG1, 0, + adc_mux_text, snd_soc_dapm_get_enum_double, tavil_dec_enum_put); +WCD_DAPM_ENUM_EXT(tx_adc_mux4, WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG1, 2, + adc_mux_text, snd_soc_dapm_get_enum_double, tavil_dec_enum_put); +WCD_DAPM_ENUM_EXT(tx_adc_mux5, WCD934X_CDC_TX_INP_MUX_ADC_MUX1_CFG1, 2, + adc_mux_text, snd_soc_dapm_get_enum_double, tavil_dec_enum_put); +WCD_DAPM_ENUM_EXT(tx_adc_mux6, WCD934X_CDC_TX_INP_MUX_ADC_MUX2_CFG1, 2, + adc_mux_text, snd_soc_dapm_get_enum_double, tavil_dec_enum_put); +WCD_DAPM_ENUM_EXT(tx_adc_mux7, WCD934X_CDC_TX_INP_MUX_ADC_MUX3_CFG1, 2, + adc_mux_text, snd_soc_dapm_get_enum_double, tavil_dec_enum_put); +WCD_DAPM_ENUM_EXT(tx_adc_mux8, WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG1, 4, + adc_mux_text, snd_soc_dapm_get_enum_double, tavil_dec_enum_put); + +WCD_DAPM_ENUM(asrc0, WCD934X_CDC_RX_INP_MUX_SPLINE_ASRC_CFG0, 0, + asrc0_mux_text); +WCD_DAPM_ENUM(asrc1, WCD934X_CDC_RX_INP_MUX_SPLINE_ASRC_CFG0, 2, + asrc1_mux_text); +WCD_DAPM_ENUM(asrc2, WCD934X_CDC_RX_INP_MUX_SPLINE_ASRC_CFG0, 4, + asrc2_mux_text); +WCD_DAPM_ENUM(asrc3, WCD934X_CDC_RX_INP_MUX_SPLINE_ASRC_CFG0, 6, + asrc3_mux_text); + +WCD_DAPM_ENUM(int1_1_native, SND_SOC_NOPM, 0, native_mux_text); +WCD_DAPM_ENUM(int2_1_native, SND_SOC_NOPM, 0, native_mux_text); +WCD_DAPM_ENUM(int3_1_native, SND_SOC_NOPM, 0, native_mux_text); +WCD_DAPM_ENUM(int4_1_native, SND_SOC_NOPM, 0, native_mux_text); + +WCD_DAPM_ENUM(int1_2_native, SND_SOC_NOPM, 0, native_mux_text); +WCD_DAPM_ENUM(int2_2_native, SND_SOC_NOPM, 0, native_mux_text); +WCD_DAPM_ENUM(int3_2_native, SND_SOC_NOPM, 0, native_mux_text); +WCD_DAPM_ENUM(int4_2_native, SND_SOC_NOPM, 0, native_mux_text); +WCD_DAPM_ENUM(int7_2_native, SND_SOC_NOPM, 0, native_mux_text); +WCD_DAPM_ENUM(int8_2_native, SND_SOC_NOPM, 0, native_mux_text); + +WCD_DAPM_ENUM(anc0_fb, WCD934X_CDC_RX_INP_MUX_ANC_CFG0, 0, anc0_fb_mux_text); +WCD_DAPM_ENUM(anc1_fb, WCD934X_CDC_RX_INP_MUX_ANC_CFG0, 3, anc1_fb_mux_text); + +WCD_DAPM_ENUM_EXT(i2s_rx0, SND_SOC_NOPM, 0, i2s_rx01_mux_text, + i2s_rx_mux_get, i2s_rx_mux_put); +WCD_DAPM_ENUM_EXT(i2s_rx1, SND_SOC_NOPM, 0, i2s_rx01_mux_text, + i2s_rx_mux_get, i2s_rx_mux_put); +WCD_DAPM_ENUM_EXT(i2s_rx2, SND_SOC_NOPM, 0, i2s_rx23_mux_text, + i2s_rx_mux_get, i2s_rx_mux_put); +WCD_DAPM_ENUM_EXT(i2s_rx3, SND_SOC_NOPM, 0, i2s_rx23_mux_text, + i2s_rx_mux_get, i2s_rx_mux_put); +WCD_DAPM_ENUM_EXT(i2s_rx4, SND_SOC_NOPM, 0, i2s_rx45_mux_text, + i2s_rx_mux_get, i2s_rx_mux_put); +WCD_DAPM_ENUM_EXT(i2s_rx5, SND_SOC_NOPM, 0, i2s_rx45_mux_text, + i2s_rx_mux_get, i2s_rx_mux_put); +WCD_DAPM_ENUM_EXT(i2s_rx6, SND_SOC_NOPM, 0, i2s_rx67_mux_text, + i2s_rx_mux_get, i2s_rx_mux_put); +WCD_DAPM_ENUM_EXT(i2s_rx7, SND_SOC_NOPM, 0, i2s_rx67_mux_text, + i2s_rx_mux_get, i2s_rx_mux_put); + +WCD_DAPM_ENUM(wdma3_port0, WCD934X_DMA_WDMA3_PRT_CFG, 0, wdma3_port0_text); +WCD_DAPM_ENUM(wdma3_port1, WCD934X_DMA_WDMA3_PRT_CFG, 1, wdma3_port1_text); +WCD_DAPM_ENUM(wdma3_port2, WCD934X_DMA_WDMA3_PRT_CFG, 2, wdma3_port2_text); +WCD_DAPM_ENUM(wdma3_port3, WCD934X_DMA_WDMA3_PRT_CFG, 3, wdma3_port3_text); +WCD_DAPM_ENUM(wdma3_port4, WCD934X_DMA_WDMA3_PRT_CFG, 4, wdma3_port4_text); +WCD_DAPM_ENUM(wdma3_port5, WCD934X_DMA_WDMA3_PRT_CFG, 5, wdma3_port5_text); +WCD_DAPM_ENUM(wdma3_port6, WCD934X_DMA_WDMA3_PRT_CFG, 6, wdma3_port6_text); + +WCD_DAPM_ENUM(wdma3_ch0, WCD934X_DMA_CH_0_1_CFG_WDMA_3, 0, wdma3_ch_text); +WCD_DAPM_ENUM(wdma3_ch1, WCD934X_DMA_CH_0_1_CFG_WDMA_3, 4, wdma3_ch_text); +WCD_DAPM_ENUM(wdma3_ch2, WCD934X_DMA_CH_2_3_CFG_WDMA_3, 0, wdma3_ch_text); +WCD_DAPM_ENUM(wdma3_ch3, WCD934X_DMA_CH_2_3_CFG_WDMA_3, 4, wdma3_ch_text); + +static const struct snd_kcontrol_new anc_ear_switch = + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct snd_kcontrol_new anc_ear_spkr_switch = + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct snd_kcontrol_new anc_spkr_pa_switch = + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct snd_kcontrol_new anc_hphl_pa_switch = + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct snd_kcontrol_new anc_hphr_pa_switch = + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct snd_kcontrol_new mad_cpe1_switch = + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct snd_kcontrol_new mad_cpe2_switch = + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct snd_kcontrol_new mad_brdcst_switch = + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct snd_kcontrol_new adc_us_mux0_switch = + SOC_DAPM_SINGLE("US_Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct snd_kcontrol_new adc_us_mux1_switch = + SOC_DAPM_SINGLE("US_Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct snd_kcontrol_new adc_us_mux2_switch = + SOC_DAPM_SINGLE("US_Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct snd_kcontrol_new adc_us_mux3_switch = + SOC_DAPM_SINGLE("US_Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct snd_kcontrol_new adc_us_mux4_switch = + SOC_DAPM_SINGLE("US_Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct snd_kcontrol_new adc_us_mux5_switch = + SOC_DAPM_SINGLE("US_Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct snd_kcontrol_new adc_us_mux6_switch = + SOC_DAPM_SINGLE("US_Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct snd_kcontrol_new adc_us_mux7_switch = + SOC_DAPM_SINGLE("US_Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct snd_kcontrol_new adc_us_mux8_switch = + SOC_DAPM_SINGLE("US_Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct snd_kcontrol_new rx_int1_asrc_switch[] = { + SOC_DAPM_SINGLE("HPHL Switch", SND_SOC_NOPM, 0, 1, 0), +}; + +static const struct snd_kcontrol_new rx_int2_asrc_switch[] = { + SOC_DAPM_SINGLE("HPHR Switch", SND_SOC_NOPM, 0, 1, 0), +}; + +static const struct snd_kcontrol_new rx_int3_asrc_switch[] = { + SOC_DAPM_SINGLE("LO1 Switch", SND_SOC_NOPM, 0, 1, 0), +}; + +static const struct snd_kcontrol_new rx_int4_asrc_switch[] = { + SOC_DAPM_SINGLE("LO2 Switch", SND_SOC_NOPM, 0, 1, 0), +}; + +static const struct snd_kcontrol_new wdma3_onoff_switch = + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct snd_soc_dapm_widget tavil_dapm_i2s_widgets[] = { + + SND_SOC_DAPM_MUX_E("I2S RX0 MUX", SND_SOC_NOPM, WCD934X_RX0, 0, + &i2s_rx0_mux, tavil_codec_enable_i2s_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("I2S RX1 MUX", SND_SOC_NOPM, WCD934X_RX1, 0, + &i2s_rx1_mux, tavil_codec_enable_i2s_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("I2S RX2 MUX", SND_SOC_NOPM, WCD934X_RX2, 0, + &i2s_rx2_mux, tavil_codec_enable_i2s_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("I2S RX3 MUX", SND_SOC_NOPM, WCD934X_RX3, 0, + &i2s_rx3_mux, tavil_codec_enable_i2s_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("I2S RX4 MUX", SND_SOC_NOPM, WCD934X_RX4, 0, + &i2s_rx4_mux, tavil_codec_enable_i2s_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("I2S RX5 MUX", SND_SOC_NOPM, WCD934X_RX5, 0, + &i2s_rx5_mux, tavil_codec_enable_i2s_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("I2S RX6 MUX", SND_SOC_NOPM, WCD934X_RX6, 0, + &i2s_rx6_mux, tavil_codec_enable_i2s_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("I2S RX7 MUX", SND_SOC_NOPM, WCD934X_RX7, 0, + &i2s_rx7_mux, tavil_codec_enable_i2s_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MIXER("I2S RX0", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I2S RX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I2S RX2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I2S RX3", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I2S RX4", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I2S RX5", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I2S RX6", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I2S RX7", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_MIXER_E("I2S TX0", SND_SOC_NOPM, WCD934X_TX0, 0, NULL, 0, + tavil_codec_enable_i2s_path, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("I2S TX1", SND_SOC_NOPM, WCD934X_TX1, 0, NULL, 0, + tavil_codec_enable_i2s_path, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("I2S TX2", SND_SOC_NOPM, WCD934X_TX2, 0, NULL, 0, + tavil_codec_enable_i2s_path, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("I2S TX3", SND_SOC_NOPM, WCD934X_TX3, 0, NULL, 0, + tavil_codec_enable_i2s_path, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("I2S TX4", SND_SOC_NOPM, WCD934X_TX4, 0, NULL, 0, + tavil_codec_enable_i2s_path, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("I2S TX5", SND_SOC_NOPM, WCD934X_TX5, 0, NULL, 0, + tavil_codec_enable_i2s_path, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("I2S TX6", SND_SOC_NOPM, WCD934X_TX6, 0, NULL, 0, + tavil_codec_enable_i2s_path, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("I2S TX7", SND_SOC_NOPM, WCD934X_TX7, 0, NULL, 0, + tavil_codec_enable_i2s_path, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("I2S TX8", SND_SOC_NOPM, WCD934X_TX8, 0, NULL, 0, + tavil_codec_enable_i2s_path, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("I2S TX11", SND_SOC_NOPM, WCD934X_TX11, 0, NULL, 0, + tavil_codec_enable_i2s_path, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MIXER("AIF1_CAP Mixer", SND_SOC_NOPM, AIF1_CAP, 0, + aif1_i2s_cap_mixer, ARRAY_SIZE(aif1_i2s_cap_mixer)), + SND_SOC_DAPM_MIXER("AIF2_CAP Mixer", SND_SOC_NOPM, AIF2_CAP, 0, + aif2_i2s_cap_mixer, ARRAY_SIZE(aif2_i2s_cap_mixer)), + SND_SOC_DAPM_MIXER("AIF3_CAP Mixer", SND_SOC_NOPM, AIF3_CAP, 0, + aif3_i2s_cap_mixer, ARRAY_SIZE(aif3_i2s_cap_mixer)), +}; + +static int tavil_dsd_mixer_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_context *dapm = + snd_soc_dapm_kcontrol_dapm(kcontrol); + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(dapm); + struct tavil_priv *tavil_p = snd_soc_codec_get_drvdata(codec); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct tavil_dsd_config *dsd_conf = tavil_p->dsd_config; + int val; + + val = tavil_dsd_get_current_mixer_value(dsd_conf, mc->shift); + + ucontrol->value.integer.value[0] = ((val < 0) ? 0 : val); + + return 0; +} + +static int tavil_dsd_mixer_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct snd_soc_dapm_context *dapm = + snd_soc_dapm_kcontrol_dapm(kcontrol); + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(dapm); + struct tavil_priv *tavil_p = snd_soc_codec_get_drvdata(codec); + unsigned int wval = ucontrol->value.integer.value[0]; + struct tavil_dsd_config *dsd_conf = tavil_p->dsd_config; + + if (!dsd_conf) + return 0; + + mutex_lock(&tavil_p->codec_mutex); + + tavil_dsd_set_out_select(dsd_conf, mc->shift); + tavil_dsd_set_mixer_value(dsd_conf, mc->shift, wval); + + mutex_unlock(&tavil_p->codec_mutex); + snd_soc_dapm_mixer_update_power(dapm, kcontrol, wval, NULL); + + return 0; +} + +static const struct snd_kcontrol_new hphl_mixer[] = { + SOC_SINGLE_EXT("DSD HPHL Switch", SND_SOC_NOPM, INTERP_HPHL, 1, 0, + tavil_dsd_mixer_get, tavil_dsd_mixer_put), +}; + +static const struct snd_kcontrol_new hphr_mixer[] = { + SOC_SINGLE_EXT("DSD HPHR Switch", SND_SOC_NOPM, INTERP_HPHR, 1, 0, + tavil_dsd_mixer_get, tavil_dsd_mixer_put), +}; + +static const struct snd_kcontrol_new lo1_mixer[] = { + SOC_SINGLE_EXT("DSD LO1 Switch", SND_SOC_NOPM, INTERP_LO1, 1, 0, + tavil_dsd_mixer_get, tavil_dsd_mixer_put), +}; + +static const struct snd_kcontrol_new lo2_mixer[] = { + SOC_SINGLE_EXT("DSD LO2 Switch", SND_SOC_NOPM, INTERP_LO2, 1, 0, + tavil_dsd_mixer_get, tavil_dsd_mixer_put), +}; + +static const struct snd_soc_dapm_widget tavil_dapm_slim_widgets[] = { + SND_SOC_DAPM_AIF_IN_E("AIF4 PB", "AIF4 Playback", 0, SND_SOC_NOPM, + AIF4_PB, 0, tavil_codec_enable_rx, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_AIF_OUT_E("AIF4 VI", "VIfeed", 0, SND_SOC_NOPM, + AIF4_VIFEED, 0, + tavil_codec_enable_slimvi_feedback, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_AIF_OUT("AIF4 MAD", "AIF4 MAD TX", 0, + SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_MIXER("AIF4_VI Mixer", SND_SOC_NOPM, AIF4_VIFEED, 0, + aif4_vi_mixer, ARRAY_SIZE(aif4_vi_mixer)), + SND_SOC_DAPM_INPUT("VIINPUT"), + + WCD_DAPM_MUX("SLIM RX0 MUX", WCD934X_RX0, slim_rx0), + WCD_DAPM_MUX("SLIM RX1 MUX", WCD934X_RX1, slim_rx1), + WCD_DAPM_MUX("SLIM RX2 MUX", WCD934X_RX2, slim_rx2), + WCD_DAPM_MUX("SLIM RX3 MUX", WCD934X_RX3, slim_rx3), + WCD_DAPM_MUX("SLIM RX4 MUX", WCD934X_RX4, slim_rx4), + WCD_DAPM_MUX("SLIM RX5 MUX", WCD934X_RX5, slim_rx5), + WCD_DAPM_MUX("SLIM RX6 MUX", WCD934X_RX6, slim_rx6), + WCD_DAPM_MUX("SLIM RX7 MUX", WCD934X_RX7, slim_rx7), + + SND_SOC_DAPM_MIXER("SLIM RX0", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM RX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM RX2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM RX3", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM RX4", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM RX5", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM RX6", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM RX7", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_MIXER("AIF1_CAP Mixer", SND_SOC_NOPM, AIF1_CAP, 0, + aif1_slim_cap_mixer, + ARRAY_SIZE(aif1_slim_cap_mixer)), + SND_SOC_DAPM_MIXER("AIF2_CAP Mixer", SND_SOC_NOPM, AIF2_CAP, 0, + aif2_slim_cap_mixer, + ARRAY_SIZE(aif2_slim_cap_mixer)), + SND_SOC_DAPM_MIXER("AIF3_CAP Mixer", SND_SOC_NOPM, AIF3_CAP, 0, + aif3_slim_cap_mixer, + ARRAY_SIZE(aif3_slim_cap_mixer)), + SND_SOC_DAPM_MIXER("AIF4_MAD Mixer", SND_SOC_NOPM, AIF4_MAD_TX, 0, + aif4_slim_mad_mixer, + ARRAY_SIZE(aif4_slim_mad_mixer)), +}; + +static const struct snd_soc_dapm_widget tavil_dapm_widgets[] = { + SND_SOC_DAPM_AIF_IN_E("AIF1 PB", "AIF1 Playback", 0, SND_SOC_NOPM, + AIF1_PB, 0, tavil_codec_enable_rx, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_AIF_IN_E("AIF2 PB", "AIF2 Playback", 0, SND_SOC_NOPM, + AIF2_PB, 0, tavil_codec_enable_rx, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_AIF_IN_E("AIF3 PB", "AIF3 Playback", 0, SND_SOC_NOPM, + AIF3_PB, 0, tavil_codec_enable_rx, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + WCD_DAPM_MUX("CDC_IF RX0 MUX", WCD934X_RX0, cdc_if_rx0), + WCD_DAPM_MUX("CDC_IF RX1 MUX", WCD934X_RX1, cdc_if_rx1), + WCD_DAPM_MUX("CDC_IF RX2 MUX", WCD934X_RX2, cdc_if_rx2), + WCD_DAPM_MUX("CDC_IF RX3 MUX", WCD934X_RX3, cdc_if_rx3), + WCD_DAPM_MUX("CDC_IF RX4 MUX", WCD934X_RX4, cdc_if_rx4), + WCD_DAPM_MUX("CDC_IF RX5 MUX", WCD934X_RX5, cdc_if_rx5), + WCD_DAPM_MUX("CDC_IF RX6 MUX", WCD934X_RX6, cdc_if_rx6), + WCD_DAPM_MUX("CDC_IF RX7 MUX", WCD934X_RX7, cdc_if_rx7), + + SND_SOC_DAPM_MUX_E("RX INT0_2 MUX", SND_SOC_NOPM, INTERP_EAR, 0, + &rx_int0_2_mux, tavil_codec_enable_mix_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT1_2 MUX", SND_SOC_NOPM, INTERP_HPHL, 0, + &rx_int1_2_mux, tavil_codec_enable_mix_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT2_2 MUX", SND_SOC_NOPM, INTERP_HPHR, 0, + &rx_int2_2_mux, tavil_codec_enable_mix_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT3_2 MUX", SND_SOC_NOPM, INTERP_LO1, 0, + &rx_int3_2_mux, tavil_codec_enable_mix_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT4_2 MUX", SND_SOC_NOPM, INTERP_LO2, 0, + &rx_int4_2_mux, tavil_codec_enable_mix_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT7_2 MUX", SND_SOC_NOPM, INTERP_SPKR1, 0, + &rx_int7_2_mux, tavil_codec_enable_mix_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT8_2 MUX", SND_SOC_NOPM, INTERP_SPKR2, 0, + &rx_int8_2_mux, tavil_codec_enable_mix_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + + WCD_DAPM_MUX("RX INT0_1 MIX1 INP0", 0, rx_int0_1_mix_inp0), + WCD_DAPM_MUX("RX INT0_1 MIX1 INP1", 0, rx_int0_1_mix_inp1), + WCD_DAPM_MUX("RX INT0_1 MIX1 INP2", 0, rx_int0_1_mix_inp2), + WCD_DAPM_MUX("RX INT1_1 MIX1 INP0", 0, rx_int1_1_mix_inp0), + WCD_DAPM_MUX("RX INT1_1 MIX1 INP1", 0, rx_int1_1_mix_inp1), + WCD_DAPM_MUX("RX INT1_1 MIX1 INP2", 0, rx_int1_1_mix_inp2), + WCD_DAPM_MUX("RX INT2_1 MIX1 INP0", 0, rx_int2_1_mix_inp0), + WCD_DAPM_MUX("RX INT2_1 MIX1 INP1", 0, rx_int2_1_mix_inp1), + WCD_DAPM_MUX("RX INT2_1 MIX1 INP2", 0, rx_int2_1_mix_inp2), + WCD_DAPM_MUX("RX INT3_1 MIX1 INP0", 0, rx_int3_1_mix_inp0), + WCD_DAPM_MUX("RX INT3_1 MIX1 INP1", 0, rx_int3_1_mix_inp1), + WCD_DAPM_MUX("RX INT3_1 MIX1 INP2", 0, rx_int3_1_mix_inp2), + WCD_DAPM_MUX("RX INT4_1 MIX1 INP0", 0, rx_int4_1_mix_inp0), + WCD_DAPM_MUX("RX INT4_1 MIX1 INP1", 0, rx_int4_1_mix_inp1), + WCD_DAPM_MUX("RX INT4_1 MIX1 INP2", 0, rx_int4_1_mix_inp2), + + SND_SOC_DAPM_MUX_E("RX INT7_1 MIX1 INP0", SND_SOC_NOPM, 0, 0, + &rx_int7_1_mix_inp0_mux, tavil_codec_enable_swr, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT7_1 MIX1 INP1", SND_SOC_NOPM, 0, 0, + &rx_int7_1_mix_inp1_mux, tavil_codec_enable_swr, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT7_1 MIX1 INP2", SND_SOC_NOPM, 0, 0, + &rx_int7_1_mix_inp2_mux, tavil_codec_enable_swr, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT8_1 MIX1 INP0", SND_SOC_NOPM, 0, 0, + &rx_int8_1_mix_inp0_mux, tavil_codec_enable_swr, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT8_1 MIX1 INP1", SND_SOC_NOPM, 0, 0, + &rx_int8_1_mix_inp1_mux, tavil_codec_enable_swr, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT8_1 MIX1 INP2", SND_SOC_NOPM, 0, 0, + &rx_int8_1_mix_inp2_mux, tavil_codec_enable_swr, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MIXER("RX INT0_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT0 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT1_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT1 SEC MIX", SND_SOC_NOPM, 0, 0, + rx_int1_asrc_switch, ARRAY_SIZE(rx_int1_asrc_switch)), + SND_SOC_DAPM_MIXER("RX INT2_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT2 SEC MIX", SND_SOC_NOPM, 0, 0, + rx_int2_asrc_switch, ARRAY_SIZE(rx_int2_asrc_switch)), + SND_SOC_DAPM_MIXER("RX INT3_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT3 SEC MIX", SND_SOC_NOPM, 0, 0, + rx_int3_asrc_switch, ARRAY_SIZE(rx_int3_asrc_switch)), + SND_SOC_DAPM_MIXER("RX INT4_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT4 SEC MIX", SND_SOC_NOPM, 0, 0, + rx_int4_asrc_switch, ARRAY_SIZE(rx_int4_asrc_switch)), + SND_SOC_DAPM_MIXER("RX INT7_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT7 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT8_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT8 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_MIXER("RX INT0 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT1 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT1 MIX3", SND_SOC_NOPM, 0, 0, hphl_mixer, + ARRAY_SIZE(hphl_mixer)), + SND_SOC_DAPM_MIXER("RX INT2 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT2 MIX3", SND_SOC_NOPM, 0, 0, hphr_mixer, + ARRAY_SIZE(hphr_mixer)), + SND_SOC_DAPM_MIXER("RX INT3 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT3 MIX3", SND_SOC_NOPM, 0, 0, lo1_mixer, + ARRAY_SIZE(lo1_mixer)), + SND_SOC_DAPM_MIXER("RX INT4 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT4 MIX3", SND_SOC_NOPM, 0, 0, lo2_mixer, + ARRAY_SIZE(lo2_mixer)), + SND_SOC_DAPM_MIXER("RX INT7 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER_E("RX INT7 CHAIN", SND_SOC_NOPM, 0, 0, + NULL, 0, tavil_codec_spk_boost_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("RX INT8 CHAIN", SND_SOC_NOPM, 0, 0, + NULL, 0, tavil_codec_spk_boost_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX_E("RX INT0 MIX2 INP", SND_SOC_NOPM, INTERP_EAR, + 0, &rx_int0_mix2_inp_mux, tavil_codec_enable_rx_path_clk, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT1 MIX2 INP", SND_SOC_NOPM, INTERP_HPHL, + 0, &rx_int1_mix2_inp_mux, tavil_codec_enable_rx_path_clk, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT2 MIX2 INP", SND_SOC_NOPM, INTERP_HPHR, + 0, &rx_int2_mix2_inp_mux, tavil_codec_enable_rx_path_clk, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT3 MIX2 INP", SND_SOC_NOPM, INTERP_LO1, + 0, &rx_int3_mix2_inp_mux, tavil_codec_enable_rx_path_clk, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT4 MIX2 INP", SND_SOC_NOPM, INTERP_LO2, + 0, &rx_int4_mix2_inp_mux, tavil_codec_enable_rx_path_clk, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT7 MIX2 INP", SND_SOC_NOPM, INTERP_SPKR1, + 0, &rx_int7_mix2_inp_mux, tavil_codec_enable_rx_path_clk, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + WCD_DAPM_MUX("CDC_IF TX0 MUX", WCD934X_TX0, cdc_if_tx0), + WCD_DAPM_MUX("CDC_IF TX1 MUX", WCD934X_TX1, cdc_if_tx1), + WCD_DAPM_MUX("CDC_IF TX2 MUX", WCD934X_TX2, cdc_if_tx2), + WCD_DAPM_MUX("CDC_IF TX3 MUX", WCD934X_TX3, cdc_if_tx3), + WCD_DAPM_MUX("CDC_IF TX4 MUX", WCD934X_TX4, cdc_if_tx4), + WCD_DAPM_MUX("CDC_IF TX5 MUX", WCD934X_TX5, cdc_if_tx5), + WCD_DAPM_MUX("CDC_IF TX6 MUX", WCD934X_TX6, cdc_if_tx6), + WCD_DAPM_MUX("CDC_IF TX7 MUX", WCD934X_TX7, cdc_if_tx7), + WCD_DAPM_MUX("CDC_IF TX8 MUX", WCD934X_TX8, cdc_if_tx8), + WCD_DAPM_MUX("CDC_IF TX9 MUX", WCD934X_TX9, cdc_if_tx9), + WCD_DAPM_MUX("CDC_IF TX10 MUX", WCD934X_TX10, cdc_if_tx10), + WCD_DAPM_MUX("CDC_IF TX11 MUX", WCD934X_TX11, cdc_if_tx11), + WCD_DAPM_MUX("CDC_IF TX11 INP1 MUX", WCD934X_TX11, cdc_if_tx11_inp1), + WCD_DAPM_MUX("CDC_IF TX13 MUX", WCD934X_TX13, cdc_if_tx13), + WCD_DAPM_MUX("CDC_IF TX13 INP1 MUX", WCD934X_TX13, cdc_if_tx13_inp1), + + SND_SOC_DAPM_MUX_E("ADC MUX0", WCD934X_CDC_TX0_TX_PATH_CTL, 5, 0, + &tx_adc_mux0_mux, tavil_codec_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX_E("ADC MUX1", WCD934X_CDC_TX1_TX_PATH_CTL, 5, 0, + &tx_adc_mux1_mux, tavil_codec_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX_E("ADC MUX2", WCD934X_CDC_TX2_TX_PATH_CTL, 5, 0, + &tx_adc_mux2_mux, tavil_codec_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX_E("ADC MUX3", WCD934X_CDC_TX3_TX_PATH_CTL, 5, 0, + &tx_adc_mux3_mux, tavil_codec_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX_E("ADC MUX4", WCD934X_CDC_TX4_TX_PATH_CTL, 5, 0, + &tx_adc_mux4_mux, tavil_codec_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX_E("ADC MUX5", WCD934X_CDC_TX5_TX_PATH_CTL, 5, 0, + &tx_adc_mux5_mux, tavil_codec_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX_E("ADC MUX6", WCD934X_CDC_TX6_TX_PATH_CTL, 5, 0, + &tx_adc_mux6_mux, tavil_codec_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX_E("ADC MUX7", WCD934X_CDC_TX7_TX_PATH_CTL, 5, 0, + &tx_adc_mux7_mux, tavil_codec_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX_E("ADC MUX8", WCD934X_CDC_TX8_TX_PATH_CTL, 5, 0, + &tx_adc_mux8_mux, tavil_codec_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX_E("ADC MUX10", SND_SOC_NOPM, 10, 0, &tx_adc_mux10_mux, + tavil_codec_tx_adc_cfg, SND_SOC_DAPM_POST_PMU), + + SND_SOC_DAPM_MUX_E("ADC MUX11", SND_SOC_NOPM, 11, 0, &tx_adc_mux11_mux, + tavil_codec_tx_adc_cfg, SND_SOC_DAPM_POST_PMU), + + SND_SOC_DAPM_MUX_E("ADC MUX12", SND_SOC_NOPM, 12, 0, &tx_adc_mux12_mux, + tavil_codec_tx_adc_cfg, SND_SOC_DAPM_POST_PMU), + + SND_SOC_DAPM_MUX_E("ADC MUX13", SND_SOC_NOPM, 13, 0, &tx_adc_mux13_mux, + tavil_codec_tx_adc_cfg, SND_SOC_DAPM_POST_PMU), + + WCD_DAPM_MUX("DMIC MUX0", 0, tx_dmic_mux0), + WCD_DAPM_MUX("DMIC MUX1", 0, tx_dmic_mux1), + WCD_DAPM_MUX("DMIC MUX2", 0, tx_dmic_mux2), + WCD_DAPM_MUX("DMIC MUX3", 0, tx_dmic_mux3), + WCD_DAPM_MUX("DMIC MUX4", 0, tx_dmic_mux4), + WCD_DAPM_MUX("DMIC MUX5", 0, tx_dmic_mux5), + WCD_DAPM_MUX("DMIC MUX6", 0, tx_dmic_mux6), + WCD_DAPM_MUX("DMIC MUX7", 0, tx_dmic_mux7), + WCD_DAPM_MUX("DMIC MUX8", 0, tx_dmic_mux8), + WCD_DAPM_MUX("DMIC MUX10", 0, tx_dmic_mux10), + WCD_DAPM_MUX("DMIC MUX11", 0, tx_dmic_mux11), + WCD_DAPM_MUX("DMIC MUX12", 0, tx_dmic_mux12), + WCD_DAPM_MUX("DMIC MUX13", 0, tx_dmic_mux13), + + WCD_DAPM_MUX("AMIC MUX0", 0, tx_amic_mux0), + WCD_DAPM_MUX("AMIC MUX1", 0, tx_amic_mux1), + WCD_DAPM_MUX("AMIC MUX2", 0, tx_amic_mux2), + WCD_DAPM_MUX("AMIC MUX3", 0, tx_amic_mux3), + WCD_DAPM_MUX("AMIC MUX4", 0, tx_amic_mux4), + WCD_DAPM_MUX("AMIC MUX5", 0, tx_amic_mux5), + WCD_DAPM_MUX("AMIC MUX6", 0, tx_amic_mux6), + WCD_DAPM_MUX("AMIC MUX7", 0, tx_amic_mux7), + WCD_DAPM_MUX("AMIC MUX8", 0, tx_amic_mux8), + WCD_DAPM_MUX("AMIC MUX10", 0, tx_amic_mux10), + WCD_DAPM_MUX("AMIC MUX11", 0, tx_amic_mux11), + WCD_DAPM_MUX("AMIC MUX12", 0, tx_amic_mux12), + WCD_DAPM_MUX("AMIC MUX13", 0, tx_amic_mux13), + + SND_SOC_DAPM_ADC_E("ADC1", NULL, WCD934X_ANA_AMIC1, 7, 0, + tavil_codec_enable_adc, SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_ADC_E("ADC2", NULL, WCD934X_ANA_AMIC2, 7, 0, + tavil_codec_enable_adc, SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_ADC_E("ADC3", NULL, WCD934X_ANA_AMIC3, 7, 0, + tavil_codec_enable_adc, SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_ADC_E("ADC4", NULL, WCD934X_ANA_AMIC4, 7, 0, + tavil_codec_enable_adc, SND_SOC_DAPM_PRE_PMU), + + WCD_DAPM_MUX("AMIC4_5 SEL", 0, tx_amic4_5), + + WCD_DAPM_MUX("ANC0 FB MUX", 0, anc0_fb), + WCD_DAPM_MUX("ANC1 FB MUX", 0, anc1_fb), + + SND_SOC_DAPM_INPUT("AMIC1"), + SND_SOC_DAPM_INPUT("AMIC2"), + SND_SOC_DAPM_INPUT("AMIC3"), + SND_SOC_DAPM_INPUT("AMIC4"), + SND_SOC_DAPM_INPUT("AMIC5"), + + SND_SOC_DAPM_MICBIAS_E("MIC BIAS1", SND_SOC_NOPM, 0, 0, + tavil_codec_enable_micbias, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MICBIAS_E("MIC BIAS2", SND_SOC_NOPM, 0, 0, + tavil_codec_enable_micbias, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MICBIAS_E("MIC BIAS3", SND_SOC_NOPM, 0, 0, + tavil_codec_enable_micbias, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MICBIAS_E("MIC BIAS4", SND_SOC_NOPM, 0, 0, + tavil_codec_enable_micbias, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + /* + * Not supply widget, this is used to recover HPH registers. + * It is not connected to any other widgets + */ + SND_SOC_DAPM_SUPPLY("RESET_HPH_REGISTERS", SND_SOC_NOPM, + 0, 0, tavil_codec_reset_hph_registers, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MICBIAS_E(DAPM_MICBIAS1_STANDALONE, SND_SOC_NOPM, 0, 0, + tavil_codec_force_enable_micbias, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MICBIAS_E(DAPM_MICBIAS2_STANDALONE, SND_SOC_NOPM, 0, 0, + tavil_codec_force_enable_micbias, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MICBIAS_E(DAPM_MICBIAS3_STANDALONE, SND_SOC_NOPM, 0, 0, + tavil_codec_force_enable_micbias, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MICBIAS_E(DAPM_MICBIAS4_STANDALONE, SND_SOC_NOPM, 0, 0, + tavil_codec_force_enable_micbias, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_AIF_OUT_E("AIF1 CAP", "AIF1 Capture", 0, SND_SOC_NOPM, + AIF1_CAP, 0, tavil_codec_enable_tx, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_AIF_OUT_E("AIF2 CAP", "AIF2 Capture", 0, SND_SOC_NOPM, + AIF2_CAP, 0, tavil_codec_enable_tx, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_AIF_OUT_E("AIF3 CAP", "AIF3 Capture", 0, SND_SOC_NOPM, + AIF3_CAP, 0, tavil_codec_enable_tx, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MIXER("SLIM TX0", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM TX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM TX2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM TX3", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM TX4", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM TX5", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM TX6", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM TX7", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM TX8", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM TX9", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM TX10", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM TX11", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM TX13", SND_SOC_NOPM, 0, 0, NULL, 0), + + /* Digital Mic Inputs */ + SND_SOC_DAPM_ADC_E("DMIC0", NULL, SND_SOC_NOPM, 0, 0, + tavil_codec_enable_dmic, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("DMIC1", NULL, SND_SOC_NOPM, 0, 0, + tavil_codec_enable_dmic, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("DMIC2", NULL, SND_SOC_NOPM, 0, 0, + tavil_codec_enable_dmic, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("DMIC3", NULL, SND_SOC_NOPM, 0, 0, + tavil_codec_enable_dmic, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("DMIC4", NULL, SND_SOC_NOPM, 0, 0, + tavil_codec_enable_dmic, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("DMIC5", NULL, SND_SOC_NOPM, 0, 0, + tavil_codec_enable_dmic, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + WCD_DAPM_MUX("IIR0 INP0 MUX", 0, iir0_inp0), + WCD_DAPM_MUX("IIR0 INP1 MUX", 0, iir0_inp1), + WCD_DAPM_MUX("IIR0 INP2 MUX", 0, iir0_inp2), + WCD_DAPM_MUX("IIR0 INP3 MUX", 0, iir0_inp3), + WCD_DAPM_MUX("IIR1 INP0 MUX", 0, iir1_inp0), + WCD_DAPM_MUX("IIR1 INP1 MUX", 0, iir1_inp1), + WCD_DAPM_MUX("IIR1 INP2 MUX", 0, iir1_inp2), + WCD_DAPM_MUX("IIR1 INP3 MUX", 0, iir1_inp3), + + SND_SOC_DAPM_MIXER_E("IIR0", WCD934X_CDC_SIDETONE_IIR0_IIR_PATH_CTL, + 4, 0, NULL, 0, tavil_codec_set_iir_gain, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_MIXER_E("IIR1", WCD934X_CDC_SIDETONE_IIR1_IIR_PATH_CTL, + 4, 0, NULL, 0, tavil_codec_set_iir_gain, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_MIXER("SRC0", WCD934X_CDC_SIDETONE_SRC0_ST_SRC_PATH_CTL, + 4, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SRC1", WCD934X_CDC_SIDETONE_SRC1_ST_SRC_PATH_CTL, + 4, 0, NULL, 0), + + WCD_DAPM_MUX("RX MIX TX0 MUX", 0, rx_mix_tx0), + WCD_DAPM_MUX("RX MIX TX1 MUX", 0, rx_mix_tx1), + WCD_DAPM_MUX("RX MIX TX2 MUX", 0, rx_mix_tx2), + WCD_DAPM_MUX("RX MIX TX3 MUX", 0, rx_mix_tx3), + WCD_DAPM_MUX("RX MIX TX4 MUX", 0, rx_mix_tx4), + WCD_DAPM_MUX("RX MIX TX5 MUX", 0, rx_mix_tx5), + WCD_DAPM_MUX("RX MIX TX6 MUX", 0, rx_mix_tx6), + WCD_DAPM_MUX("RX MIX TX7 MUX", 0, rx_mix_tx7), + WCD_DAPM_MUX("RX MIX TX8 MUX", 0, rx_mix_tx8), + WCD_DAPM_MUX("RX INT0 DEM MUX", 0, rx_int0_dem_inp), + WCD_DAPM_MUX("RX INT1 DEM MUX", 0, rx_int1_dem_inp), + WCD_DAPM_MUX("RX INT2 DEM MUX", 0, rx_int2_dem_inp), + + SND_SOC_DAPM_MUX_E("RX INT0_1 INTERP", SND_SOC_NOPM, INTERP_EAR, 0, + &rx_int0_1_interp_mux, tavil_codec_enable_main_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT1_1 INTERP", SND_SOC_NOPM, INTERP_HPHL, 0, + &rx_int1_1_interp_mux, tavil_codec_enable_main_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT2_1 INTERP", SND_SOC_NOPM, INTERP_HPHR, 0, + &rx_int2_1_interp_mux, tavil_codec_enable_main_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT3_1 INTERP", SND_SOC_NOPM, INTERP_LO1, 0, + &rx_int3_1_interp_mux, tavil_codec_enable_main_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT4_1 INTERP", SND_SOC_NOPM, INTERP_LO2, 0, + &rx_int4_1_interp_mux, tavil_codec_enable_main_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT7_1 INTERP", SND_SOC_NOPM, INTERP_SPKR1, 0, + &rx_int7_1_interp_mux, tavil_codec_enable_main_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT8_1 INTERP", SND_SOC_NOPM, INTERP_SPKR2, 0, + &rx_int8_1_interp_mux, tavil_codec_enable_main_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + + WCD_DAPM_MUX("RX INT0_2 INTERP", 0, rx_int0_2_interp), + WCD_DAPM_MUX("RX INT1_2 INTERP", 0, rx_int1_2_interp), + WCD_DAPM_MUX("RX INT2_2 INTERP", 0, rx_int2_2_interp), + WCD_DAPM_MUX("RX INT3_2 INTERP", 0, rx_int3_2_interp), + WCD_DAPM_MUX("RX INT4_2 INTERP", 0, rx_int4_2_interp), + WCD_DAPM_MUX("RX INT7_2 INTERP", 0, rx_int7_2_interp), + WCD_DAPM_MUX("RX INT8_2 INTERP", 0, rx_int8_2_interp), + + SND_SOC_DAPM_SWITCH("ADC US MUX0", WCD934X_CDC_TX0_TX_PATH_192_CTL, 0, + 0, &adc_us_mux0_switch), + SND_SOC_DAPM_SWITCH("ADC US MUX1", WCD934X_CDC_TX1_TX_PATH_192_CTL, 0, + 0, &adc_us_mux1_switch), + SND_SOC_DAPM_SWITCH("ADC US MUX2", WCD934X_CDC_TX2_TX_PATH_192_CTL, 0, + 0, &adc_us_mux2_switch), + SND_SOC_DAPM_SWITCH("ADC US MUX3", WCD934X_CDC_TX3_TX_PATH_192_CTL, 0, + 0, &adc_us_mux3_switch), + SND_SOC_DAPM_SWITCH("ADC US MUX4", WCD934X_CDC_TX4_TX_PATH_192_CTL, 0, + 0, &adc_us_mux4_switch), + SND_SOC_DAPM_SWITCH("ADC US MUX5", WCD934X_CDC_TX5_TX_PATH_192_CTL, 0, + 0, &adc_us_mux5_switch), + SND_SOC_DAPM_SWITCH("ADC US MUX6", WCD934X_CDC_TX6_TX_PATH_192_CTL, 0, + 0, &adc_us_mux6_switch), + SND_SOC_DAPM_SWITCH("ADC US MUX7", WCD934X_CDC_TX7_TX_PATH_192_CTL, 0, + 0, &adc_us_mux7_switch), + SND_SOC_DAPM_SWITCH("ADC US MUX8", WCD934X_CDC_TX8_TX_PATH_192_CTL, 0, + 0, &adc_us_mux8_switch), + + /* MAD related widgets */ + SND_SOC_DAPM_INPUT("MAD_CPE_INPUT"), + SND_SOC_DAPM_INPUT("MADINPUT"), + + WCD_DAPM_MUX("MAD_SEL MUX", 0, mad_sel), + WCD_DAPM_MUX("MAD_INP MUX", 0, mad_inp_mux), + + SND_SOC_DAPM_SWITCH_E("MAD_BROADCAST", SND_SOC_NOPM, 0, 0, + &mad_brdcst_switch, tavil_codec_ape_enable_mad, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), + + SND_SOC_DAPM_SWITCH_E("MAD_CPE1", SND_SOC_NOPM, 0, 0, + &mad_cpe1_switch, tavil_codec_cpe_mad_ctl, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_SWITCH_E("MAD_CPE2", SND_SOC_NOPM, 0, 0, + &mad_cpe2_switch, tavil_codec_cpe_mad_ctl, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), + + SND_SOC_DAPM_OUTPUT("MAD_CPE_OUT1"), + SND_SOC_DAPM_OUTPUT("MAD_CPE_OUT2"), + + SND_SOC_DAPM_DAC_E("RX INT0 DAC", NULL, SND_SOC_NOPM, + 0, 0, tavil_codec_ear_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_DAC_E("RX INT1 DAC", NULL, WCD934X_ANA_HPH, + 5, 0, tavil_codec_hphl_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_DAC_E("RX INT2 DAC", NULL, WCD934X_ANA_HPH, + 4, 0, tavil_codec_hphr_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_DAC_E("RX INT3 DAC", NULL, SND_SOC_NOPM, + 0, 0, tavil_codec_lineout_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_DAC_E("RX INT4 DAC", NULL, SND_SOC_NOPM, + 0, 0, tavil_codec_lineout_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_PGA_E("EAR PA", WCD934X_ANA_EAR, 7, 0, NULL, 0, + tavil_codec_enable_ear_pa, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("HPHL PA", WCD934X_ANA_HPH, 7, 0, NULL, 0, + tavil_codec_enable_hphl_pa, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("HPHR PA", WCD934X_ANA_HPH, 6, 0, NULL, 0, + tavil_codec_enable_hphr_pa, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("LINEOUT1 PA", WCD934X_ANA_LO_1_2, 7, 0, NULL, 0, + tavil_codec_enable_lineout_pa, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("LINEOUT2 PA", WCD934X_ANA_LO_1_2, 6, 0, NULL, 0, + tavil_codec_enable_lineout_pa, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("ANC EAR PA", WCD934X_ANA_EAR, 7, 0, NULL, 0, + tavil_codec_enable_ear_pa, SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("ANC SPK1 PA", SND_SOC_NOPM, 0, 0, NULL, 0, + tavil_codec_enable_spkr_anc, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("ANC HPHL PA", SND_SOC_NOPM, 0, 0, NULL, 0, + tavil_codec_enable_hphl_pa, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("ANC HPHR PA", SND_SOC_NOPM, 0, 0, NULL, 0, + tavil_codec_enable_hphr_pa, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_OUTPUT("EAR"), + SND_SOC_DAPM_OUTPUT("HPHL"), + SND_SOC_DAPM_OUTPUT("HPHR"), + SND_SOC_DAPM_OUTPUT("LINEOUT1"), + SND_SOC_DAPM_OUTPUT("LINEOUT2"), + SND_SOC_DAPM_OUTPUT("SPK1 OUT"), + SND_SOC_DAPM_OUTPUT("SPK2 OUT"), + SND_SOC_DAPM_OUTPUT("ANC EAR"), + SND_SOC_DAPM_OUTPUT("ANC HPHL"), + SND_SOC_DAPM_OUTPUT("ANC HPHR"), + + SND_SOC_DAPM_SWITCH("ANC OUT EAR Enable", SND_SOC_NOPM, 0, 0, + &anc_ear_switch), + SND_SOC_DAPM_SWITCH("ANC OUT EAR SPKR Enable", SND_SOC_NOPM, 0, 0, + &anc_ear_spkr_switch), + SND_SOC_DAPM_SWITCH("ANC SPKR PA Enable", SND_SOC_NOPM, 0, 0, + &anc_spkr_pa_switch), + + SND_SOC_DAPM_SWITCH_E("ANC OUT HPHL Enable", SND_SOC_NOPM, INTERP_HPHL, + 0, &anc_hphl_pa_switch, tavil_anc_out_switch_cb, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_SWITCH_E("ANC OUT HPHR Enable", SND_SOC_NOPM, INTERP_HPHR, + 0, &anc_hphr_pa_switch, tavil_anc_out_switch_cb, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), + + SND_SOC_DAPM_SUPPLY("RX_BIAS", SND_SOC_NOPM, 0, 0, + tavil_codec_enable_rx_bias, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_SUPPLY("RX INT1 NATIVE SUPPLY", SND_SOC_NOPM, + INTERP_HPHL, 0, tavil_enable_native_supply, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_SUPPLY("RX INT2 NATIVE SUPPLY", SND_SOC_NOPM, + INTERP_HPHR, 0, tavil_enable_native_supply, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_SUPPLY("RX INT3 NATIVE SUPPLY", SND_SOC_NOPM, + INTERP_LO1, 0, tavil_enable_native_supply, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_SUPPLY("RX INT4 NATIVE SUPPLY", SND_SOC_NOPM, + INTERP_LO2, 0, tavil_enable_native_supply, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_SUPPLY("RX INT7 NATIVE SUPPLY", SND_SOC_NOPM, + INTERP_SPKR1, 0, tavil_enable_native_supply, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_SUPPLY("RX INT8 NATIVE SUPPLY", SND_SOC_NOPM, + INTERP_SPKR2, 0, tavil_enable_native_supply, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), + + WCD_DAPM_MUX("RX INT1_1 NATIVE MUX", 0, int1_1_native), + WCD_DAPM_MUX("RX INT2_1 NATIVE MUX", 0, int2_1_native), + WCD_DAPM_MUX("RX INT3_1 NATIVE MUX", 0, int3_1_native), + WCD_DAPM_MUX("RX INT4_1 NATIVE MUX", 0, int4_1_native), + + WCD_DAPM_MUX("RX INT1_2 NATIVE MUX", 0, int1_2_native), + WCD_DAPM_MUX("RX INT2_2 NATIVE MUX", 0, int2_2_native), + WCD_DAPM_MUX("RX INT3_2 NATIVE MUX", 0, int3_2_native), + WCD_DAPM_MUX("RX INT4_2 NATIVE MUX", 0, int4_2_native), + WCD_DAPM_MUX("RX INT7_2 NATIVE MUX", 0, int7_2_native), + WCD_DAPM_MUX("RX INT8_2 NATIVE MUX", 0, int8_2_native), + + SND_SOC_DAPM_MUX_E("ASRC0 MUX", SND_SOC_NOPM, ASRC0, 0, + &asrc0_mux, tavil_codec_enable_asrc_resampler, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("ASRC1 MUX", SND_SOC_NOPM, ASRC1, 0, + &asrc1_mux, tavil_codec_enable_asrc_resampler, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("ASRC2 MUX", SND_SOC_NOPM, ASRC2, 0, + &asrc2_mux, tavil_codec_enable_asrc_resampler, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("ASRC3 MUX", SND_SOC_NOPM, ASRC3, 0, + &asrc3_mux, tavil_codec_enable_asrc_resampler, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + /* WDMA3 widgets */ + WCD_DAPM_MUX("WDMA3 PORT0 MUX", 0, wdma3_port0), + WCD_DAPM_MUX("WDMA3 PORT1 MUX", 1, wdma3_port1), + WCD_DAPM_MUX("WDMA3 PORT2 MUX", 2, wdma3_port2), + WCD_DAPM_MUX("WDMA3 PORT3 MUX", 3, wdma3_port3), + WCD_DAPM_MUX("WDMA3 PORT4 MUX", 4, wdma3_port4), + WCD_DAPM_MUX("WDMA3 PORT5 MUX", 5, wdma3_port5), + WCD_DAPM_MUX("WDMA3 PORT6 MUX", 6, wdma3_port6), + + WCD_DAPM_MUX("WDMA3 CH0 MUX", 0, wdma3_ch0), + WCD_DAPM_MUX("WDMA3 CH1 MUX", 4, wdma3_ch1), + WCD_DAPM_MUX("WDMA3 CH2 MUX", 0, wdma3_ch2), + WCD_DAPM_MUX("WDMA3 CH3 MUX", 4, wdma3_ch3), + + SND_SOC_DAPM_MIXER("WDMA3_CH_MIXER", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_SWITCH_E("WDMA3_ON_OFF", SND_SOC_NOPM, 0, 0, + &wdma3_onoff_switch, tavil_codec_wdma3_ctl, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_OUTPUT("WDMA3_OUT"), +}; + +static int tavil_get_channel_map(struct snd_soc_dai *dai, + unsigned int *tx_num, unsigned int *tx_slot, + unsigned int *rx_num, unsigned int *rx_slot) +{ + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(dai->codec); + u32 i = 0; + struct wcd9xxx_ch *ch; + int ret = 0; + + switch (dai->id) { + case AIF1_PB: + case AIF2_PB: + case AIF3_PB: + case AIF4_PB: + if (!rx_slot || !rx_num) { + dev_err(tavil->dev, "%s: Invalid rx_slot 0x%pK or rx_num 0x%pK\n", + __func__, rx_slot, rx_num); + ret = -EINVAL; + break; + } + list_for_each_entry(ch, &tavil->dai[dai->id].wcd9xxx_ch_list, + list) { + dev_dbg(tavil->dev, "%s: slot_num %u ch->ch_num %d\n", + __func__, i, ch->ch_num); + rx_slot[i++] = ch->ch_num; + } + *rx_num = i; + dev_dbg(tavil->dev, "%s: dai_name = %s dai_id = %x rx_num = %d\n", + __func__, dai->name, dai->id, i); + if (*rx_num == 0) { + dev_err(tavil->dev, "%s: Channel list empty for dai_name = %s dai_id = %x\n", + __func__, dai->name, dai->id); + ret = -EINVAL; + } + break; + case AIF1_CAP: + case AIF2_CAP: + case AIF3_CAP: + case AIF4_MAD_TX: + case AIF4_VIFEED: + if (!tx_slot || !tx_num) { + dev_err(tavil->dev, "%s: Invalid tx_slot 0x%pK or tx_num 0x%pK\n", + __func__, tx_slot, tx_num); + ret = -EINVAL; + break; + } + list_for_each_entry(ch, &tavil->dai[dai->id].wcd9xxx_ch_list, + list) { + dev_dbg(tavil->dev, "%s: slot_num %u ch->ch_num %d\n", + __func__, i, ch->ch_num); + tx_slot[i++] = ch->ch_num; + } + *tx_num = i; + dev_dbg(tavil->dev, "%s: dai_name = %s dai_id = %x tx_num = %d\n", + __func__, dai->name, dai->id, i); + if (*tx_num == 0) { + dev_err(tavil->dev, "%s: Channel list empty for dai_name = %s dai_id = %x\n", + __func__, dai->name, dai->id); + ret = -EINVAL; + } + break; + default: + dev_err(tavil->dev, "%s: Invalid DAI ID %x\n", + __func__, dai->id); + ret = -EINVAL; + break; + } + + return ret; +} + +static int tavil_set_channel_map(struct snd_soc_dai *dai, + unsigned int tx_num, unsigned int *tx_slot, + unsigned int rx_num, unsigned int *rx_slot) +{ + struct tavil_priv *tavil; + struct wcd9xxx *core; + struct wcd9xxx_codec_dai_data *dai_data = NULL; + + tavil = snd_soc_codec_get_drvdata(dai->codec); + core = dev_get_drvdata(dai->codec->dev->parent); + + if (!tx_slot || !rx_slot) { + dev_err(tavil->dev, "%s: Invalid tx_slot 0x%pK, rx_slot 0x%pK\n", + __func__, tx_slot, rx_slot); + return -EINVAL; + } + dev_dbg(tavil->dev, "%s(): dai_name = %s DAI-ID %x tx_ch %d rx_ch %d\n", + __func__, dai->name, dai->id, tx_num, rx_num); + + wcd9xxx_init_slimslave(core, core->slim->laddr, + tx_num, tx_slot, rx_num, rx_slot); + /* Reserve TX13 for MAD data channel */ + dai_data = &tavil->dai[AIF4_MAD_TX]; + if (dai_data) + list_add_tail(&core->tx_chs[WCD934X_TX13].list, + &dai_data->wcd9xxx_ch_list); + + return 0; +} + +static int tavil_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + pr_debug("%s(): substream = %s stream = %d\n", __func__, + substream->name, substream->stream); + + return 0; +} + +static void tavil_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + pr_debug("%s(): substream = %s stream = %d\n", __func__, + substream->name, substream->stream); +} + +static int tavil_set_decimator_rate(struct snd_soc_dai *dai, + u32 sample_rate) +{ + struct snd_soc_codec *codec = dai->codec; + struct wcd9xxx_ch *ch; + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + u32 tx_port = 0, tx_fs_rate = 0; + u8 shift = 0, shift_val = 0, tx_mux_sel = 0; + int decimator = -1; + u16 tx_port_reg = 0, tx_fs_reg = 0; + + switch (sample_rate) { + case 8000: + tx_fs_rate = 0; + break; + case 16000: + tx_fs_rate = 1; + break; + case 32000: + tx_fs_rate = 3; + break; + case 48000: + tx_fs_rate = 4; + break; + case 96000: + tx_fs_rate = 5; + break; + case 192000: + tx_fs_rate = 6; + break; + default: + dev_err(tavil->dev, "%s: Invalid TX sample rate: %d\n", + __func__, sample_rate); + return -EINVAL; + + }; + + list_for_each_entry(ch, &tavil->dai[dai->id].wcd9xxx_ch_list, list) { + tx_port = ch->port; + dev_dbg(codec->dev, "%s: dai->id = %d, tx_port = %d", + __func__, dai->id, tx_port); + + if ((tx_port < 0) || (tx_port == 12) || (tx_port >= 14)) { + dev_err(codec->dev, "%s: Invalid SLIM TX%u port. DAI ID: %d\n", + __func__, tx_port, dai->id); + return -EINVAL; + } + /* Find the SB TX MUX input - which decimator is connected */ + if (tx_port < 4) { + tx_port_reg = WCD934X_CDC_IF_ROUTER_TX_MUX_CFG0; + shift = (tx_port << 1); + shift_val = 0x03; + } else if ((tx_port >= 4) && (tx_port < 8)) { + tx_port_reg = WCD934X_CDC_IF_ROUTER_TX_MUX_CFG1; + shift = ((tx_port - 4) << 1); + shift_val = 0x03; + } else if ((tx_port >= 8) && (tx_port < 11)) { + tx_port_reg = WCD934X_CDC_IF_ROUTER_TX_MUX_CFG2; + shift = ((tx_port - 8) << 1); + shift_val = 0x03; + } else if (tx_port == 11) { + tx_port_reg = WCD934X_CDC_IF_ROUTER_TX_MUX_CFG3; + shift = 0; + shift_val = 0x0F; + } else if (tx_port == 13) { + tx_port_reg = WCD934X_CDC_IF_ROUTER_TX_MUX_CFG3; + shift = 4; + shift_val = 0x03; + } + tx_mux_sel = snd_soc_read(codec, tx_port_reg) & + (shift_val << shift); + tx_mux_sel = tx_mux_sel >> shift; + + if (tx_port <= 8) { + if ((tx_mux_sel == 0x2) || (tx_mux_sel == 0x3)) + decimator = tx_port; + } else if (tx_port <= 10) { + if ((tx_mux_sel == 0x1) || (tx_mux_sel == 0x2)) + decimator = ((tx_port == 9) ? 7 : 6); + } else if (tx_port == 11) { + if ((tx_mux_sel >= 1) && (tx_mux_sel < 7)) + decimator = tx_mux_sel - 1; + } else if (tx_port == 13) { + if ((tx_mux_sel == 0x1) || (tx_mux_sel == 0x2)) + decimator = 5; + } + + if (decimator >= 0) { + tx_fs_reg = WCD934X_CDC_TX0_TX_PATH_CTL + + 16 * decimator; + dev_dbg(codec->dev, "%s: set DEC%u (-> SLIM_TX%u) rate to %u\n", + __func__, decimator, tx_port, sample_rate); + snd_soc_update_bits(codec, tx_fs_reg, 0x0F, tx_fs_rate); + } else if ((tx_port <= 8) && (tx_mux_sel == 0x01)) { + /* Check if the TX Mux input is RX MIX TXn */ + dev_dbg(codec->dev, "%s: RX_MIX_TX%u going to CDC_IF TX%u\n", + __func__, tx_port, tx_port); + } else { + dev_err(codec->dev, "%s: ERROR: Invalid decimator: %d\n", + __func__, decimator); + return -EINVAL; + } + } + return 0; +} + +static int tavil_set_mix_interpolator_rate(struct snd_soc_dai *dai, + u8 rate_reg_val, + u32 sample_rate) +{ + u8 int_2_inp; + u32 j; + u16 int_mux_cfg1, int_fs_reg; + u8 int_mux_cfg1_val; + struct snd_soc_codec *codec = dai->codec; + struct wcd9xxx_ch *ch; + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + + list_for_each_entry(ch, &tavil->dai[dai->id].wcd9xxx_ch_list, list) { + int_2_inp = INTn_2_INP_SEL_RX0 + ch->port - + WCD934X_RX_PORT_START_NUMBER; + if ((int_2_inp < INTn_2_INP_SEL_RX0) || + (int_2_inp > INTn_2_INP_SEL_RX7)) { + dev_err(codec->dev, "%s: Invalid RX%u port, Dai ID is %d\n", + __func__, + (ch->port - WCD934X_RX_PORT_START_NUMBER), + dai->id); + return -EINVAL; + } + + int_mux_cfg1 = WCD934X_CDC_RX_INP_MUX_RX_INT0_CFG1; + for (j = 0; j < WCD934X_NUM_INTERPOLATORS; j++) { + /* Interpolators 5 and 6 are not aviliable in Tavil */ + if (j == INTERP_LO3_NA || j == INTERP_LO4_NA) { + int_mux_cfg1 += 2; + continue; + } + int_mux_cfg1_val = snd_soc_read(codec, int_mux_cfg1) & + 0x0F; + if (int_mux_cfg1_val == int_2_inp) { + /* + * Ear mix path supports only 48, 96, 192, + * 384KHz only + */ + if ((j == INTERP_EAR) && + (rate_reg_val < 0x4 || + rate_reg_val > 0x7)) { + dev_err_ratelimited(codec->dev, + "%s: Invalid rate for AIF_PB DAI(%d)\n", + __func__, dai->id); + return -EINVAL; + } + + int_fs_reg = WCD934X_CDC_RX0_RX_PATH_MIX_CTL + + 20 * j; + dev_dbg(codec->dev, "%s: AIF_PB DAI(%d) connected to INT%u_2\n", + __func__, dai->id, j); + dev_dbg(codec->dev, "%s: set INT%u_2 sample rate to %u\n", + __func__, j, sample_rate); + snd_soc_update_bits(codec, int_fs_reg, 0x0F, + rate_reg_val); + } + int_mux_cfg1 += 2; + } + } + return 0; +} + +static int tavil_set_prim_interpolator_rate(struct snd_soc_dai *dai, + u8 rate_reg_val, + u32 sample_rate) +{ + u8 int_1_mix1_inp; + u32 j; + u16 int_mux_cfg0, int_mux_cfg1; + u16 int_fs_reg; + u8 int_mux_cfg0_val, int_mux_cfg1_val; + u8 inp0_sel, inp1_sel, inp2_sel; + struct snd_soc_codec *codec = dai->codec; + struct wcd9xxx_ch *ch; + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + struct tavil_dsd_config *dsd_conf = tavil->dsd_config; + + list_for_each_entry(ch, &tavil->dai[dai->id].wcd9xxx_ch_list, list) { + int_1_mix1_inp = INTn_1_INP_SEL_RX0 + ch->port - + WCD934X_RX_PORT_START_NUMBER; + if ((int_1_mix1_inp < INTn_1_INP_SEL_RX0) || + (int_1_mix1_inp > INTn_1_INP_SEL_RX7)) { + dev_err(codec->dev, "%s: Invalid RX%u port, Dai ID is %d\n", + __func__, + (ch->port - WCD934X_RX_PORT_START_NUMBER), + dai->id); + return -EINVAL; + } + + int_mux_cfg0 = WCD934X_CDC_RX_INP_MUX_RX_INT0_CFG0; + + /* + * Loop through all interpolator MUX inputs and find out + * to which interpolator input, the slim rx port + * is connected + */ + for (j = 0; j < WCD934X_NUM_INTERPOLATORS; j++) { + /* Interpolators 5 and 6 are not aviliable in Tavil */ + if (j == INTERP_LO3_NA || j == INTERP_LO4_NA) { + int_mux_cfg0 += 2; + continue; + } + int_mux_cfg1 = int_mux_cfg0 + 1; + + int_mux_cfg0_val = snd_soc_read(codec, int_mux_cfg0); + int_mux_cfg1_val = snd_soc_read(codec, int_mux_cfg1); + inp0_sel = int_mux_cfg0_val & 0x0F; + inp1_sel = (int_mux_cfg0_val >> 4) & 0x0F; + inp2_sel = (int_mux_cfg1_val >> 4) & 0x0F; + if ((inp0_sel == int_1_mix1_inp) || + (inp1_sel == int_1_mix1_inp) || + (inp2_sel == int_1_mix1_inp)) { + /* + * Ear and speaker primary path does not support + * native sample rates + */ + if ((j == INTERP_EAR || j == INTERP_SPKR1 || + j == INTERP_SPKR2) && + (rate_reg_val > 0x7)) { + dev_err_ratelimited(codec->dev, + "%s: Invalid rate for AIF_PB DAI(%d)\n", + __func__, dai->id); + return -EINVAL; + } + + int_fs_reg = WCD934X_CDC_RX0_RX_PATH_CTL + + 20 * j; + dev_dbg(codec->dev, + "%s: AIF_PB DAI(%d) connected to INT%u_1\n", + __func__, dai->id, j); + dev_dbg(codec->dev, + "%s: set INT%u_1 sample rate to %u\n", + __func__, j, sample_rate); + snd_soc_update_bits(codec, int_fs_reg, 0x0F, + rate_reg_val); + } + int_mux_cfg0 += 2; + } + if (dsd_conf) + tavil_dsd_set_interp_rate(dsd_conf, ch->port, + sample_rate, rate_reg_val); + } + + return 0; +} + + +static int tavil_set_interpolator_rate(struct snd_soc_dai *dai, + u32 sample_rate) +{ + struct snd_soc_codec *codec = dai->codec; + int rate_val = 0; + int i, ret; + + for (i = 0; i < ARRAY_SIZE(sr_val_tbl); i++) { + if (sample_rate == sr_val_tbl[i].sample_rate) { + rate_val = sr_val_tbl[i].rate_val; + break; + } + } + if ((i == ARRAY_SIZE(sr_val_tbl)) || (rate_val < 0)) { + dev_err(codec->dev, "%s: Unsupported sample rate: %d\n", + __func__, sample_rate); + return -EINVAL; + } + + ret = tavil_set_prim_interpolator_rate(dai, (u8)rate_val, sample_rate); + if (ret) + return ret; + ret = tavil_set_mix_interpolator_rate(dai, (u8)rate_val, sample_rate); + if (ret) + return ret; + + return ret; +} + +static int tavil_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + pr_debug("%s(): substream = %s stream = %d\n", __func__, + substream->name, substream->stream); + return 0; +} + +static int tavil_vi_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(dai->codec); + + dev_dbg(tavil->dev, "%s: dai_name = %s DAI-ID %x rate %d num_ch %d\n", + __func__, dai->name, dai->id, params_rate(params), + params_channels(params)); + + tavil->dai[dai->id].rate = params_rate(params); + tavil->dai[dai->id].bit_width = 32; + + return 0; +} + +static int tavil_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(dai->codec); + int ret = 0; + + dev_dbg(tavil->dev, "%s: dai_name = %s DAI-ID %x rate %d num_ch %d\n", + __func__, dai->name, dai->id, params_rate(params), + params_channels(params)); + + switch (substream->stream) { + case SNDRV_PCM_STREAM_PLAYBACK: + ret = tavil_set_interpolator_rate(dai, params_rate(params)); + if (ret) { + dev_err(tavil->dev, "%s: cannot set sample rate: %u\n", + __func__, params_rate(params)); + return ret; + } + switch (params_width(params)) { + case 16: + tavil->dai[dai->id].bit_width = 16; + break; + case 24: + tavil->dai[dai->id].bit_width = 24; + break; + case 32: + tavil->dai[dai->id].bit_width = 32; + break; + default: + return -EINVAL; + } + tavil->dai[dai->id].rate = params_rate(params); + break; + case SNDRV_PCM_STREAM_CAPTURE: + if (dai->id != AIF4_MAD_TX) + ret = tavil_set_decimator_rate(dai, + params_rate(params)); + if (ret) { + dev_err(tavil->dev, "%s: cannot set TX Decimator rate: %d\n", + __func__, ret); + return ret; + } + switch (params_width(params)) { + case 16: + tavil->dai[dai->id].bit_width = 16; + break; + case 24: + tavil->dai[dai->id].bit_width = 24; + break; + default: + dev_err(tavil->dev, "%s: Invalid format 0x%x\n", + __func__, params_width(params)); + return -EINVAL; + }; + tavil->dai[dai->id].rate = params_rate(params); + break; + default: + dev_err(tavil->dev, "%s: Invalid stream type %d\n", __func__, + substream->stream); + return -EINVAL; + }; + + return 0; +} + +static int tavil_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + u32 i2s_reg; + + switch (dai->id) { + case AIF1_PB: + case AIF1_CAP: + i2s_reg = WCD934X_DATA_HUB_I2S_0_CTL; + break; + case AIF2_PB: + case AIF2_CAP: + i2s_reg = WCD934X_DATA_HUB_I2S_1_CTL; + break; + case AIF3_PB: + case AIF3_CAP: + i2s_reg = WCD934X_DATA_HUB_I2S_2_CTL; + break; + default: + dev_err(dai->codec->dev, "%s Invalid i2s Id", __func__); + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + /* CPU is master */ + snd_soc_update_bits(dai->codec, i2s_reg, 0x2, 0x0); + break; + case SND_SOC_DAIFMT_CBM_CFM: + /* CPU is slave */ + snd_soc_update_bits(dai->codec, i2s_reg, 0x2, 0x2); + break; + default: + return -EINVAL; + } + return 0; +} + +static struct snd_soc_dai_ops tavil_dai_ops = { + .startup = tavil_startup, + .shutdown = tavil_shutdown, + .hw_params = tavil_hw_params, + .prepare = tavil_prepare, + .set_channel_map = tavil_set_channel_map, + .get_channel_map = tavil_get_channel_map, +}; + +static struct snd_soc_dai_ops tavil_i2s_dai_ops = { + .startup = tavil_startup, + .shutdown = tavil_shutdown, + .hw_params = tavil_hw_params, + .prepare = tavil_prepare, + .set_fmt = tavil_set_dai_fmt, +}; + +static struct snd_soc_dai_ops tavil_vi_dai_ops = { + .hw_params = tavil_vi_hw_params, + .set_channel_map = tavil_set_channel_map, + .get_channel_map = tavil_get_channel_map, +}; + +static struct snd_soc_dai_driver tavil_slim_dai[] = { + { + .name = "tavil_rx1", + .id = AIF1_PB, + .playback = { + .stream_name = "AIF1 Playback", + .rates = WCD934X_RATES_MASK | WCD934X_FRAC_RATES_MASK, + .formats = WCD934X_FORMATS_S16_S24_S32_LE, + .rate_min = 8000, + .rate_max = 384000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &tavil_dai_ops, + }, + { + .name = "tavil_tx1", + .id = AIF1_CAP, + .capture = { + .stream_name = "AIF1 Capture", + .rates = WCD934X_RATES_MASK, + .formats = WCD934X_FORMATS_S16_S24_LE, + .rate_min = 8000, + .rate_max = 192000, + .channels_min = 1, + .channels_max = 4, + }, + .ops = &tavil_dai_ops, + }, + { + .name = "tavil_rx2", + .id = AIF2_PB, + .playback = { + .stream_name = "AIF2 Playback", + .rates = WCD934X_RATES_MASK | WCD934X_FRAC_RATES_MASK, + .formats = WCD934X_FORMATS_S16_S24_S32_LE, + .rate_min = 8000, + .rate_max = 384000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &tavil_dai_ops, + }, + { + .name = "tavil_tx2", + .id = AIF2_CAP, + .capture = { + .stream_name = "AIF2 Capture", + .rates = WCD934X_RATES_MASK, + .formats = WCD934X_FORMATS_S16_S24_LE, + .rate_min = 8000, + .rate_max = 192000, + .channels_min = 1, + .channels_max = 4, + }, + .ops = &tavil_dai_ops, + }, + { + .name = "tavil_rx3", + .id = AIF3_PB, + .playback = { + .stream_name = "AIF3 Playback", + .rates = WCD934X_RATES_MASK | WCD934X_FRAC_RATES_MASK, + .formats = WCD934X_FORMATS_S16_S24_S32_LE, + .rate_min = 8000, + .rate_max = 384000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &tavil_dai_ops, + }, + { + .name = "tavil_tx3", + .id = AIF3_CAP, + .capture = { + .stream_name = "AIF3 Capture", + .rates = WCD934X_RATES_MASK, + .formats = WCD934X_FORMATS_S16_S24_LE, + .rate_min = 8000, + .rate_max = 192000, + .channels_min = 1, + .channels_max = 4, + }, + .ops = &tavil_dai_ops, + }, + { + .name = "tavil_rx4", + .id = AIF4_PB, + .playback = { + .stream_name = "AIF4 Playback", + .rates = WCD934X_RATES_MASK | WCD934X_FRAC_RATES_MASK, + .formats = WCD934X_FORMATS_S16_S24_S32_LE, + .rate_min = 8000, + .rate_max = 384000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &tavil_dai_ops, + }, + { + .name = "tavil_vifeedback", + .id = AIF4_VIFEED, + .capture = { + .stream_name = "VIfeed", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_48000, + .formats = WCD934X_FORMATS_S16_S24_S32_LE, + .rate_min = 8000, + .rate_max = 48000, + .channels_min = 1, + .channels_max = 4, + }, + .ops = &tavil_vi_dai_ops, + }, + { + .name = "tavil_mad1", + .id = AIF4_MAD_TX, + .capture = { + .stream_name = "AIF4 MAD TX", + .rates = SNDRV_PCM_RATE_16000, + .formats = WCD934X_FORMATS_S16_LE, + .rate_min = 16000, + .rate_max = 16000, + .channels_min = 1, + .channels_max = 1, + }, + .ops = &tavil_dai_ops, + }, +}; + +static struct snd_soc_dai_driver tavil_i2s_dai[] = { + { + .name = "tavil_i2s_rx1", + .id = AIF1_PB, + .playback = { + .stream_name = "AIF1 Playback", + .rates = WCD934X_RATES_MASK, + .formats = WCD934X_FORMATS_S16_S24_S32_LE, + .rate_min = 8000, + .rate_max = 384000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &tavil_i2s_dai_ops, + }, + { + .name = "tavil_i2s_tx1", + .id = AIF1_CAP, + .capture = { + .stream_name = "AIF1 Capture", + .rates = WCD934X_RATES_MASK, + .formats = WCD934X_FORMATS_S16_S24_LE, + .rate_min = 8000, + .rate_max = 384000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &tavil_i2s_dai_ops, + }, + { + .name = "tavil_i2s_rx2", + .id = AIF2_PB, + .playback = { + .stream_name = "AIF2 Playback", + .rates = WCD934X_RATES_MASK, + .formats = WCD934X_FORMATS_S16_S24_S32_LE, + .rate_min = 8000, + .rate_max = 384000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &tavil_i2s_dai_ops, + }, + { + .name = "tavil_i2s_tx2", + .id = AIF2_CAP, + .capture = { + .stream_name = "AIF2 Capture", + .rates = WCD934X_RATES_MASK, + .formats = WCD934X_FORMATS_S16_S24_LE, + .rate_min = 8000, + .rate_max = 384000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &tavil_i2s_dai_ops, + }, + { + .name = "tavil_i2s_rx3", + .id = AIF3_PB, + .playback = { + .stream_name = "AIF3 Playback", + .rates = WCD934X_RATES_MASK, + .formats = WCD934X_FORMATS_S16_S24_S32_LE, + .rate_min = 8000, + .rate_max = 384000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &tavil_i2s_dai_ops, + }, + { + .name = "tavil_i2s_tx3", + .id = AIF3_CAP, + .capture = { + .stream_name = "AIF3 Capture", + .rates = WCD934X_RATES_MASK, + .formats = WCD934X_FORMATS_S16_S24_LE, + .rate_min = 8000, + .rate_max = 384000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &tavil_i2s_dai_ops, + }, +}; + +static void tavil_codec_power_gate_digital_core(struct tavil_priv *tavil) +{ + mutex_lock(&tavil->power_lock); + dev_dbg(tavil->dev, "%s: Entering power gating function, %d\n", + __func__, tavil->power_active_ref); + + if (tavil->power_active_ref > 0) + goto exit; + + wcd9xxx_set_power_state(tavil->wcd9xxx, + WCD_REGION_POWER_COLLAPSE_BEGIN, + WCD9XXX_DIG_CORE_REGION_1); + regmap_update_bits(tavil->wcd9xxx->regmap, + WCD934X_CODEC_RPM_PWR_CDC_DIG_HM_CTL, 0x04, 0x04); + regmap_update_bits(tavil->wcd9xxx->regmap, + WCD934X_CODEC_RPM_PWR_CDC_DIG_HM_CTL, 0x01, 0x00); + regmap_update_bits(tavil->wcd9xxx->regmap, + WCD934X_CODEC_RPM_PWR_CDC_DIG_HM_CTL, 0x02, 0x00); + wcd9xxx_set_power_state(tavil->wcd9xxx, WCD_REGION_POWER_DOWN, + WCD9XXX_DIG_CORE_REGION_1); +exit: + dev_dbg(tavil->dev, "%s: Exiting power gating function, %d\n", + __func__, tavil->power_active_ref); + mutex_unlock(&tavil->power_lock); +} + +static void tavil_codec_power_gate_work(struct work_struct *work) +{ + struct tavil_priv *tavil; + struct delayed_work *dwork; + + dwork = to_delayed_work(work); + tavil = container_of(dwork, struct tavil_priv, power_gate_work); + + tavil_codec_power_gate_digital_core(tavil); +} + +/* called under power_lock acquisition */ +static int tavil_dig_core_remove_power_collapse(struct tavil_priv *tavil) +{ + regmap_write(tavil->wcd9xxx->regmap, + WCD934X_CODEC_RPM_PWR_CDC_DIG_HM_CTL, 0x05); + regmap_write(tavil->wcd9xxx->regmap, + WCD934X_CODEC_RPM_PWR_CDC_DIG_HM_CTL, 0x07); + regmap_update_bits(tavil->wcd9xxx->regmap, + WCD934X_CODEC_RPM_RST_CTL, 0x02, 0x00); + regmap_update_bits(tavil->wcd9xxx->regmap, + WCD934X_CODEC_RPM_RST_CTL, 0x02, 0x02); + regmap_write(tavil->wcd9xxx->regmap, + WCD934X_CODEC_RPM_PWR_CDC_DIG_HM_CTL, 0x03); + + wcd9xxx_set_power_state(tavil->wcd9xxx, + WCD_REGION_POWER_COLLAPSE_REMOVE, + WCD9XXX_DIG_CORE_REGION_1); + regcache_mark_dirty(tavil->wcd9xxx->regmap); + regcache_sync_region(tavil->wcd9xxx->regmap, + WCD934X_DIG_CORE_REG_MIN, + WCD934X_DIG_CORE_REG_MAX); + + return 0; +} + +static int tavil_dig_core_power_collapse(struct tavil_priv *tavil, + int req_state) +{ + int cur_state; + + /* Exit if feature is disabled */ + if (!dig_core_collapse_enable) + return 0; + + mutex_lock(&tavil->power_lock); + if (req_state == POWER_COLLAPSE) + tavil->power_active_ref--; + else if (req_state == POWER_RESUME) + tavil->power_active_ref++; + else + goto unlock_mutex; + + if (tavil->power_active_ref < 0) { + dev_dbg(tavil->dev, + "%s: power_active_ref is negative, reset it\n", + __func__); + tavil->power_active_ref = 0; + goto unlock_mutex; + } + + if (req_state == POWER_COLLAPSE) { + if (tavil->power_active_ref == 0) { + schedule_delayed_work(&tavil->power_gate_work, + msecs_to_jiffies(dig_core_collapse_timer * 1000)); + } + } else if (req_state == POWER_RESUME) { + if (tavil->power_active_ref == 1) { + /* + * At this point, there can be two cases: + * 1. Core already in power collapse state + * 2. Timer kicked in and still did not expire or + * waiting for the power_lock + */ + cur_state = wcd9xxx_get_current_power_state( + tavil->wcd9xxx, + WCD9XXX_DIG_CORE_REGION_1); + if (cur_state == WCD_REGION_POWER_DOWN) { + tavil_dig_core_remove_power_collapse(tavil); + } else { + mutex_unlock(&tavil->power_lock); + cancel_delayed_work_sync( + &tavil->power_gate_work); + mutex_lock(&tavil->power_lock); + } + } + } + +unlock_mutex: + mutex_unlock(&tavil->power_lock); + + return 0; +} + +static int tavil_cdc_req_mclk_enable(struct tavil_priv *tavil, + bool enable) +{ + int ret = 0; + + if (enable) { + ret = clk_prepare_enable(tavil->wcd_ext_clk); + if (ret) { + dev_err(tavil->dev, "%s: ext clk enable failed\n", + __func__); + goto done; + } + /* get BG */ + wcd_resmgr_enable_master_bias(tavil->resmgr); + /* get MCLK */ + wcd_resmgr_enable_clk_block(tavil->resmgr, WCD_CLK_MCLK); + } else { + /* put MCLK */ + wcd_resmgr_disable_clk_block(tavil->resmgr, WCD_CLK_MCLK); + /* put BG */ + wcd_resmgr_disable_master_bias(tavil->resmgr); + clk_disable_unprepare(tavil->wcd_ext_clk); + } + +done: + return ret; +} + +static int __tavil_cdc_mclk_enable_locked(struct tavil_priv *tavil, + bool enable) +{ + int ret = 0; + + if (!tavil->wcd_ext_clk) { + dev_err(tavil->dev, "%s: wcd ext clock is NULL\n", __func__); + return -EINVAL; + } + + dev_dbg(tavil->dev, "%s: mclk_enable = %u\n", __func__, enable); + + if (enable) { + tavil_dig_core_power_collapse(tavil, POWER_RESUME); + tavil_vote_svs(tavil, true); + ret = tavil_cdc_req_mclk_enable(tavil, true); + if (ret) + goto done; + } else { + tavil_cdc_req_mclk_enable(tavil, false); + tavil_vote_svs(tavil, false); + tavil_dig_core_power_collapse(tavil, POWER_COLLAPSE); + } + +done: + return ret; +} + +static int __tavil_cdc_mclk_enable(struct tavil_priv *tavil, + bool enable) +{ + int ret; + + WCD9XXX_V2_BG_CLK_LOCK(tavil->resmgr); + ret = __tavil_cdc_mclk_enable_locked(tavil, enable); + if (enable) + wcd_resmgr_set_sido_input_src(tavil->resmgr, + SIDO_SOURCE_RCO_BG); + WCD9XXX_V2_BG_CLK_UNLOCK(tavil->resmgr); + + return ret; +} + +static ssize_t tavil_codec_version_read(struct snd_info_entry *entry, + void *file_private_data, + struct file *file, + char __user *buf, size_t count, + loff_t pos) +{ + struct tavil_priv *tavil; + struct wcd9xxx *wcd9xxx; + char buffer[TAVIL_VERSION_ENTRY_SIZE]; + int len = 0; + + tavil = (struct tavil_priv *) entry->private_data; + if (!tavil) { + pr_err("%s: tavil priv is null\n", __func__); + return -EINVAL; + } + + wcd9xxx = tavil->wcd9xxx; + + switch (wcd9xxx->version) { + case TAVIL_VERSION_WCD9340_1_0: + len = snprintf(buffer, sizeof(buffer), "WCD9340_1_0\n"); + break; + case TAVIL_VERSION_WCD9341_1_0: + len = snprintf(buffer, sizeof(buffer), "WCD9341_1_0\n"); + break; + case TAVIL_VERSION_WCD9340_1_1: + len = snprintf(buffer, sizeof(buffer), "WCD9340_1_1\n"); + break; + case TAVIL_VERSION_WCD9341_1_1: + len = snprintf(buffer, sizeof(buffer), "WCD9341_1_1\n"); + break; + default: + len = snprintf(buffer, sizeof(buffer), "VER_UNDEFINED\n"); + } + + return simple_read_from_buffer(buf, count, &pos, buffer, len); +} + +static struct snd_info_entry_ops tavil_codec_info_ops = { + .read = tavil_codec_version_read, +}; + +/* + * tavil_codec_info_create_codec_entry - creates wcd934x module + * @codec_root: The parent directory + * @codec: Codec instance + * + * Creates wcd934x module and version entry under the given + * parent directory. + * + * Return: 0 on success or negative error code on failure. + */ +int tavil_codec_info_create_codec_entry(struct snd_info_entry *codec_root, + struct snd_soc_codec *codec) +{ + struct snd_info_entry *version_entry; + struct tavil_priv *tavil; + struct snd_soc_card *card; + + if (!codec_root || !codec) + return -EINVAL; + + tavil = snd_soc_codec_get_drvdata(codec); + card = codec->component.card; + tavil->entry = snd_info_create_subdir(codec_root->module, + "tavil", codec_root); + if (!tavil->entry) { + dev_dbg(codec->dev, "%s: failed to create wcd934x entry\n", + __func__); + return -ENOMEM; + } + + version_entry = snd_info_create_card_entry(card->snd_card, + "version", + tavil->entry); + if (!version_entry) { + dev_dbg(codec->dev, "%s: failed to create wcd934x version entry\n", + __func__); + return -ENOMEM; + } + + version_entry->private_data = tavil; + version_entry->size = TAVIL_VERSION_ENTRY_SIZE; + version_entry->content = SNDRV_INFO_CONTENT_DATA; + version_entry->c.ops = &tavil_codec_info_ops; + + if (snd_info_register(version_entry) < 0) { + snd_info_free_entry(version_entry); + return -ENOMEM; + } + tavil->version_entry = version_entry; + + return 0; +} +EXPORT_SYMBOL(tavil_codec_info_create_codec_entry); + +/** + * tavil_cdc_mclk_enable - Enable/disable codec mclk + * + * @codec: codec instance + * @enable: Indicates clk enable or disable + * + * Returns 0 on Success and error on failure + */ +int tavil_cdc_mclk_enable(struct snd_soc_codec *codec, bool enable) +{ + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + + return __tavil_cdc_mclk_enable(tavil, enable); +} +EXPORT_SYMBOL(tavil_cdc_mclk_enable); + +static int __tavil_codec_internal_rco_ctrl(struct snd_soc_codec *codec, + bool enable) +{ + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + int ret = 0; + + if (enable) { + if (wcd_resmgr_get_clk_type(tavil->resmgr) == + WCD_CLK_RCO) { + ret = wcd_resmgr_enable_clk_block(tavil->resmgr, + WCD_CLK_RCO); + } else { + ret = tavil_cdc_req_mclk_enable(tavil, true); + if (ret) { + dev_err(codec->dev, + "%s: mclk_enable failed, err = %d\n", + __func__, ret); + goto done; + } + wcd_resmgr_set_sido_input_src(tavil->resmgr, + SIDO_SOURCE_RCO_BG); + ret = wcd_resmgr_enable_clk_block(tavil->resmgr, + WCD_CLK_RCO); + ret |= tavil_cdc_req_mclk_enable(tavil, false); + } + + } else { + ret = wcd_resmgr_disable_clk_block(tavil->resmgr, + WCD_CLK_RCO); + } + + if (ret) { + dev_err(codec->dev, "%s: Error in %s RCO\n", + __func__, (enable ? "enabling" : "disabling")); + ret = -EINVAL; + } + +done: + return ret; +} + +/* + * tavil_codec_internal_rco_ctrl: Enable/Disable codec's RCO clock + * @codec: Handle to the codec + * @enable: Indicates whether clock should be enabled or disabled + */ +static int tavil_codec_internal_rco_ctrl(struct snd_soc_codec *codec, + bool enable) +{ + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + int ret = 0; + + WCD9XXX_V2_BG_CLK_LOCK(tavil->resmgr); + ret = __tavil_codec_internal_rco_ctrl(codec, enable); + WCD9XXX_V2_BG_CLK_UNLOCK(tavil->resmgr); + return ret; +} + +/* + * tavil_cdc_mclk_tx_enable: Enable/Disable codec's clock for TX path + * @codec: Handle to codec + * @enable: Indicates whether clock should be enabled or disabled + */ +int tavil_cdc_mclk_tx_enable(struct snd_soc_codec *codec, bool enable) +{ + struct tavil_priv *tavil_p; + int ret = 0; + bool clk_mode; + bool clk_internal; + + if (!codec) + return -EINVAL; + + tavil_p = snd_soc_codec_get_drvdata(codec); + clk_mode = test_bit(CLK_MODE, &tavil_p->status_mask); + clk_internal = test_bit(CLK_INTERNAL, &tavil_p->status_mask); + + dev_dbg(codec->dev, "%s: clkmode: %d, enable: %d, clk_internal: %d\n", + __func__, clk_mode, enable, clk_internal); + + if (clk_mode || clk_internal) { + if (enable) { + wcd_resmgr_enable_master_bias(tavil_p->resmgr); + tavil_dig_core_power_collapse(tavil_p, POWER_RESUME); + tavil_vote_svs(tavil_p, true); + ret = tavil_codec_internal_rco_ctrl(codec, enable); + set_bit(CLK_INTERNAL, &tavil_p->status_mask); + } else { + clear_bit(CLK_INTERNAL, &tavil_p->status_mask); + tavil_codec_internal_rco_ctrl(codec, enable); + tavil_vote_svs(tavil_p, false); + tavil_dig_core_power_collapse(tavil_p, POWER_COLLAPSE); + wcd_resmgr_disable_master_bias(tavil_p->resmgr); + } + } else { + ret = __tavil_cdc_mclk_enable(tavil_p, enable); + } + + return ret; +} +EXPORT_SYMBOL(tavil_cdc_mclk_tx_enable); + +static const struct wcd_resmgr_cb tavil_resmgr_cb = { + .cdc_rco_ctrl = __tavil_codec_internal_rco_ctrl, +}; + +static const struct tavil_reg_mask_val tavil_codec_mclk2_1_1_defaults[] = { + {WCD934X_CLK_SYS_MCLK2_PRG1, 0x60, 0x20}, +}; + +static const struct tavil_reg_mask_val tavil_codec_mclk2_1_0_defaults[] = { + /* + * PLL Settings: + * Clock Root: MCLK2, + * Clock Source: EXT_CLK, + * Clock Destination: MCLK2 + * Clock Freq In: 19.2MHz, + * Clock Freq Out: 11.2896MHz + */ + {WCD934X_CLK_SYS_MCLK2_PRG1, 0x60, 0x20}, + {WCD934X_CLK_SYS_INT_POST_DIV_REG0, 0xFF, 0x5E}, + {WCD934X_CLK_SYS_INT_POST_DIV_REG1, 0x1F, 0x1F}, + {WCD934X_CLK_SYS_INT_REF_DIV_REG0, 0xFF, 0x54}, + {WCD934X_CLK_SYS_INT_REF_DIV_REG1, 0xFF, 0x01}, + {WCD934X_CLK_SYS_INT_FILTER_REG1, 0x07, 0x04}, + {WCD934X_CLK_SYS_INT_PLL_L_VAL, 0xFF, 0x93}, + {WCD934X_CLK_SYS_INT_PLL_N_VAL, 0xFF, 0xFA}, + {WCD934X_CLK_SYS_INT_TEST_REG0, 0xFF, 0x90}, + {WCD934X_CLK_SYS_INT_PFD_CP_DSM_PROG, 0xFF, 0x7E}, + {WCD934X_CLK_SYS_INT_VCO_PROG, 0xFF, 0xF8}, + {WCD934X_CLK_SYS_INT_TEST_REG1, 0xFF, 0x68}, + {WCD934X_CLK_SYS_INT_LDO_LOCK_CFG, 0xFF, 0x40}, + {WCD934X_CLK_SYS_INT_DIG_LOCK_DET_CFG, 0xFF, 0x32}, +}; + +static const struct tavil_reg_mask_val tavil_codec_reg_defaults[] = { + {WCD934X_BIAS_VBG_FINE_ADJ, 0xFF, 0x75}, + {WCD934X_CODEC_CPR_SVS_CX_VDD, 0xFF, 0x7C}, /* value in svs mode */ + {WCD934X_CODEC_CPR_SVS2_CX_VDD, 0xFF, 0x58}, /* value in svs2 mode */ + {WCD934X_CDC_RX0_RX_PATH_DSMDEM_CTL, 0x01, 0x01}, + {WCD934X_CDC_RX1_RX_PATH_DSMDEM_CTL, 0x01, 0x01}, + {WCD934X_CDC_RX2_RX_PATH_DSMDEM_CTL, 0x01, 0x01}, + {WCD934X_CDC_RX3_RX_PATH_DSMDEM_CTL, 0x01, 0x01}, + {WCD934X_CDC_RX4_RX_PATH_DSMDEM_CTL, 0x01, 0x01}, + {WCD934X_CDC_RX7_RX_PATH_DSMDEM_CTL, 0x01, 0x01}, + {WCD934X_CDC_RX8_RX_PATH_DSMDEM_CTL, 0x01, 0x01}, + {WCD934X_CDC_COMPANDER8_CTL7, 0x1E, 0x18}, + {WCD934X_CDC_COMPANDER7_CTL7, 0x1E, 0x18}, + {WCD934X_CDC_RX0_RX_PATH_SEC0, 0x08, 0x0}, + {WCD934X_CDC_CLSH_DECAY_CTRL, 0x03, 0x0}, + {WCD934X_MICB1_TEST_CTL_2, 0x07, 0x01}, + {WCD934X_CDC_BOOST0_BOOST_CFG1, 0x3F, 0x12}, + {WCD934X_CDC_BOOST0_BOOST_CFG2, 0x1C, 0x08}, + {WCD934X_CDC_BOOST1_BOOST_CFG1, 0x3F, 0x12}, + {WCD934X_CDC_BOOST1_BOOST_CFG2, 0x1C, 0x08}, + {WCD934X_CPE_SS_CPARMAD_BUFRDY_INT_PERIOD, 0x1F, 0x09}, + {WCD934X_CDC_TX0_TX_PATH_CFG1, 0x01, 0x00}, + {WCD934X_CDC_TX1_TX_PATH_CFG1, 0x01, 0x00}, + {WCD934X_CDC_TX2_TX_PATH_CFG1, 0x01, 0x00}, + {WCD934X_CDC_TX3_TX_PATH_CFG1, 0x01, 0x00}, + {WCD934X_CDC_TX4_TX_PATH_CFG1, 0x01, 0x00}, + {WCD934X_CDC_TX5_TX_PATH_CFG1, 0x01, 0x00}, + {WCD934X_CDC_TX6_TX_PATH_CFG1, 0x01, 0x00}, + {WCD934X_CDC_TX7_TX_PATH_CFG1, 0x01, 0x00}, + {WCD934X_CDC_TX8_TX_PATH_CFG1, 0x01, 0x00}, + {WCD934X_RX_OCP_CTL, 0x0F, 0x02}, /* OCP number of attempts is 2 */ + {WCD934X_HPH_OCP_CTL, 0xFF, 0x3A}, /* OCP current limit */ + {WCD934X_HPH_L_TEST, 0x01, 0x01}, + {WCD934X_HPH_R_TEST, 0x01, 0x01}, + {WCD934X_CPE_FLL_CONFIG_CTL_2, 0xFF, 0x20}, + {WCD934X_MBHC_NEW_CTL_2, 0x0C, 0x00}, + {WCD934X_CODEC_RPM_CLK_MCLK_CFG, 0x04, 0x04}, +}; + +static const struct tavil_reg_mask_val tavil_codec_reg_init_1_1_val[] = { + {WCD934X_CDC_COMPANDER1_CTL7, 0x1E, 0x06}, + {WCD934X_CDC_COMPANDER2_CTL7, 0x1E, 0x06}, + {WCD934X_HPH_NEW_INT_RDAC_HD2_CTL_L, 0xFF, 0x84}, + {WCD934X_HPH_NEW_INT_RDAC_HD2_CTL_R, 0xFF, 0x84}, + {WCD934X_CDC_RX3_RX_PATH_SEC0, 0xFC, 0xF4}, + {WCD934X_CDC_RX4_RX_PATH_SEC0, 0xFC, 0xF4}, +}; + +static const struct tavil_cpr_reg_defaults cpr_defaults[] = { + { 0x00000820, 0x00000094 }, + { 0x00000fC0, 0x00000048 }, + { 0x0000f000, 0x00000044 }, + { 0x0000bb80, 0xC0000178 }, + { 0x00000000, 0x00000160 }, + { 0x10854522, 0x00000060 }, + { 0x10854509, 0x00000064 }, + { 0x108544dd, 0x00000068 }, + { 0x108544ad, 0x0000006C }, + { 0x0000077E, 0x00000070 }, + { 0x000007da, 0x00000074 }, + { 0x00000000, 0x00000078 }, + { 0x00000000, 0x0000007C }, + { 0x00042029, 0x00000080 }, + { 0x4002002A, 0x00000090 }, + { 0x4002002B, 0x00000090 }, +}; + +static const struct tavil_reg_mask_val tavil_codec_reg_init_common_val[] = { + {WCD934X_CDC_CLSH_K2_MSB, 0x0F, 0x00}, + {WCD934X_CDC_CLSH_K2_LSB, 0xFF, 0x60}, + {WCD934X_CPE_SS_DMIC_CFG, 0x80, 0x00}, + {WCD934X_CDC_BOOST0_BOOST_CTL, 0x7C, 0x58}, + {WCD934X_CDC_BOOST1_BOOST_CTL, 0x7C, 0x58}, + {WCD934X_CDC_RX7_RX_PATH_CFG1, 0x08, 0x08}, + {WCD934X_CDC_RX8_RX_PATH_CFG1, 0x08, 0x08}, + {WCD934X_CDC_TOP_TOP_CFG1, 0x02, 0x02}, + {WCD934X_CDC_TOP_TOP_CFG1, 0x01, 0x01}, + {WCD934X_CDC_TX9_SPKR_PROT_PATH_CFG0, 0x01, 0x01}, + {WCD934X_CDC_TX10_SPKR_PROT_PATH_CFG0, 0x01, 0x01}, + {WCD934X_CDC_TX11_SPKR_PROT_PATH_CFG0, 0x01, 0x01}, + {WCD934X_CDC_TX12_SPKR_PROT_PATH_CFG0, 0x01, 0x01}, + {WCD934X_DATA_HUB_SB_TX11_INP_CFG, 0x01, 0x01}, + {WCD934X_CDC_CLK_RST_CTRL_FS_CNT_CONTROL, 0x01, 0x01}, + {WCD934X_CDC_COMPANDER7_CTL3, 0x80, 0x80}, + {WCD934X_CDC_COMPANDER8_CTL3, 0x80, 0x80}, + {WCD934X_CDC_COMPANDER7_CTL7, 0x01, 0x01}, + {WCD934X_CDC_COMPANDER8_CTL7, 0x01, 0x01}, + {WCD934X_CODEC_RPM_CLK_GATE, 0x08, 0x00}, + {WCD934X_TLMM_DMIC3_CLK_PINCFG, 0xFF, 0x0a}, + {WCD934X_TLMM_DMIC3_DATA_PINCFG, 0xFF, 0x0a}, + {WCD934X_CPE_SS_SVA_CFG, 0x60, 0x00}, + {WCD934X_CPE_SS_CPAR_CFG, 0x10, 0x10}, + {WCD934X_MICB1_TEST_CTL_1, 0xff, 0xfa}, + {WCD934X_MICB2_TEST_CTL_1, 0xff, 0xfa}, + {WCD934X_MICB3_TEST_CTL_1, 0xff, 0xfa}, + {WCD934X_MICB4_TEST_CTL_1, 0xff, 0xfa}, +}; + +static void tavil_codec_init_reg(struct tavil_priv *priv) +{ + struct snd_soc_codec *codec = priv->codec; + u32 i; + + for (i = 0; i < ARRAY_SIZE(tavil_codec_reg_init_common_val); i++) + snd_soc_update_bits(codec, + tavil_codec_reg_init_common_val[i].reg, + tavil_codec_reg_init_common_val[i].mask, + tavil_codec_reg_init_common_val[i].val); + + if (TAVIL_IS_1_1(priv->wcd9xxx)) { + for (i = 0; i < ARRAY_SIZE(tavil_codec_reg_init_1_1_val); i++) + snd_soc_update_bits(codec, + tavil_codec_reg_init_1_1_val[i].reg, + tavil_codec_reg_init_1_1_val[i].mask, + tavil_codec_reg_init_1_1_val[i].val); + } +} + +static const struct tavil_reg_mask_val tavil_codec_reg_i2c_defaults[] = { + {WCD934X_CLK_SYS_MCLK_PRG, 0x40, 0x00}, + {WCD934X_CODEC_RPM_CLK_GATE, 0x03, 0x01}, + {WCD934X_CODEC_RPM_CLK_MCLK_CFG, 0x03, 0x00}, + {WCD934X_CODEC_RPM_CLK_MCLK_CFG, 0x05, 0x05}, + {WCD934X_DATA_HUB_RX0_CFG, 0x71, 0x31}, + {WCD934X_DATA_HUB_RX1_CFG, 0x71, 0x31}, + {WCD934X_DATA_HUB_RX2_CFG, 0x03, 0x01}, + {WCD934X_DATA_HUB_RX3_CFG, 0x03, 0x01}, + {WCD934X_DATA_HUB_I2S_TX0_CFG, 0x01, 0x01}, + {WCD934X_DATA_HUB_I2S_TX0_CFG, 0x04, 0x01}, + {WCD934X_DATA_HUB_I2S_TX1_0_CFG, 0x01, 0x01}, + {WCD934X_DATA_HUB_I2S_TX1_1_CFG, 0x05, 0x05}, + {WCD934X_CHIP_TIER_CTRL_ALT_FUNC_EN, 0x1, 0x1}, +}; + +static void tavil_update_reg_defaults(struct tavil_priv *tavil) +{ + u32 i; + struct wcd9xxx *wcd9xxx; + + wcd9xxx = tavil->wcd9xxx; + for (i = 0; i < ARRAY_SIZE(tavil_codec_reg_defaults); i++) + regmap_update_bits(wcd9xxx->regmap, + tavil_codec_reg_defaults[i].reg, + tavil_codec_reg_defaults[i].mask, + tavil_codec_reg_defaults[i].val); + + if (tavil->intf_type == WCD9XXX_INTERFACE_TYPE_I2C) { + for (i = 0; i < ARRAY_SIZE(tavil_codec_reg_i2c_defaults); i++) { + regmap_update_bits(wcd9xxx->regmap, + tavil_codec_reg_i2c_defaults[i].reg, + tavil_codec_reg_i2c_defaults[i].mask, + tavil_codec_reg_i2c_defaults[i].val); + } + } +} + +static void tavil_update_cpr_defaults(struct tavil_priv *tavil) +{ + int i; + struct wcd9xxx *wcd9xxx; + + wcd9xxx = tavil->wcd9xxx; + if (!TAVIL_IS_1_1(wcd9xxx)) + return; + + __tavil_cdc_mclk_enable(tavil, true); + + regmap_write(wcd9xxx->regmap, WCD934X_CODEC_CPR_SVS2_MIN_CX_VDD, 0x2C); + regmap_update_bits(wcd9xxx->regmap, WCD934X_CODEC_RPM_CLK_GATE, + 0x10, 0x00); + + for (i = 0; i < ARRAY_SIZE(cpr_defaults); i++) { + regmap_bulk_write(wcd9xxx->regmap, + WCD934X_CODEC_CPR_WR_DATA_0, + (u8 *)&cpr_defaults[i].wr_data, 4); + regmap_bulk_write(wcd9xxx->regmap, + WCD934X_CODEC_CPR_WR_ADDR_0, + (u8 *)&cpr_defaults[i].wr_addr, 4); + } + + __tavil_cdc_mclk_enable(tavil, false); +} + +static void tavil_slim_interface_init_reg(struct snd_soc_codec *codec) +{ + int i; + struct tavil_priv *priv = snd_soc_codec_get_drvdata(codec); + + for (i = 0; i < WCD9XXX_SLIM_NUM_PORT_REG; i++) + wcd9xxx_interface_reg_write(priv->wcd9xxx, + WCD934X_SLIM_PGD_PORT_INT_RX_EN0 + i, + 0xFF); +} + +static irqreturn_t tavil_misc_irq(int irq, void *data) +{ + struct tavil_priv *tavil = data; + int misc_val; + + /* Find source of interrupt */ + regmap_read(tavil->wcd9xxx->regmap, WCD934X_INTR_CODEC_MISC_STATUS, + &misc_val); + + if (misc_val & 0x08) { + dev_info(tavil->dev, "%s: irq: %d, DSD DC detected!\n", + __func__, irq); + /* DSD DC interrupt, reset DSD path */ + tavil_dsd_reset(tavil->dsd_config); + } else { + dev_err(tavil->dev, "%s: Codec misc irq: %d, val: 0x%x\n", + __func__, irq, misc_val); + } + + /* Clear interrupt status */ + regmap_update_bits(tavil->wcd9xxx->regmap, + WCD934X_INTR_CODEC_MISC_CLEAR, misc_val, 0x00); + + return IRQ_HANDLED; +} + +static irqreturn_t tavil_slimbus_irq(int irq, void *data) +{ + struct tavil_priv *tavil = data; + unsigned long status = 0; + int i, j, port_id, k; + u32 bit; + u8 val, int_val = 0; + bool tx, cleared; + unsigned short reg = 0; + + for (i = WCD934X_SLIM_PGD_PORT_INT_STATUS_RX_0, j = 0; + i <= WCD934X_SLIM_PGD_PORT_INT_STATUS_TX_1; i++, j++) { + val = wcd9xxx_interface_reg_read(tavil->wcd9xxx, i); + status |= ((u32)val << (8 * j)); + } + + for_each_set_bit(j, &status, 32) { + tx = (j >= 16 ? true : false); + port_id = (tx ? j - 16 : j); + val = wcd9xxx_interface_reg_read(tavil->wcd9xxx, + WCD934X_SLIM_PGD_PORT_INT_RX_SOURCE0 + j); + if (val) { + if (!tx) + reg = WCD934X_SLIM_PGD_PORT_INT_RX_EN0 + + (port_id / 8); + else + reg = WCD934X_SLIM_PGD_PORT_INT_TX_EN0 + + (port_id / 8); + int_val = wcd9xxx_interface_reg_read( + tavil->wcd9xxx, reg); + /* + * Ignore interrupts for ports for which the + * interrupts are not specifically enabled. + */ + if (!(int_val & (1 << (port_id % 8)))) + continue; + } + if (val & WCD934X_SLIM_IRQ_OVERFLOW) + dev_err_ratelimited(tavil->dev, "%s: overflow error on %s port %d, value %x\n", + __func__, (tx ? "TX" : "RX"), port_id, val); + if (val & WCD934X_SLIM_IRQ_UNDERFLOW) + dev_err_ratelimited(tavil->dev, "%s: underflow error on %s port %d, value %x\n", + __func__, (tx ? "TX" : "RX"), port_id, val); + if (tx) { + /* inc count */ + if (val & WCD934X_SLIM_IRQ_OVERFLOW) { + tavil->slim_tx_of_uf_cnt[port_id] + [SB_PORT_ERR_OF]++; + dev_err_ratelimited(tavil->dev, "%s: tx port(%d) overflow cnt: %d\n", + __func__, port_id, + tavil->slim_tx_of_uf_cnt[port_id] + [SB_PORT_ERR_OF]); + } + if (val & WCD934X_SLIM_IRQ_UNDERFLOW) { + tavil->slim_tx_of_uf_cnt[port_id] + [SB_PORT_ERR_UF]++; + dev_err_ratelimited(tavil->dev, "%s: tx port(%d) underflow cnt: %d\n", + __func__, port_id, + tavil->slim_tx_of_uf_cnt[port_id] + [SB_PORT_ERR_UF]); + } + + } + + if ((val & WCD934X_SLIM_IRQ_OVERFLOW) || + (val & WCD934X_SLIM_IRQ_UNDERFLOW)) { + if (!tx) + reg = WCD934X_SLIM_PGD_PORT_INT_RX_EN0 + + (port_id / 8); + else if ((tavil->slim_tx_of_uf_cnt[port_id] + [SB_PORT_ERR_OF] > SB_OF_UF_MAX_RETRY_CNT) || + (tavil->slim_tx_of_uf_cnt[port_id] + [SB_PORT_ERR_UF] > SB_OF_UF_MAX_RETRY_CNT)) + reg = WCD934X_SLIM_PGD_PORT_INT_TX_EN0 + + (port_id / 8); + else + goto skip_port_disable; + int_val = wcd9xxx_interface_reg_read( + tavil->wcd9xxx, reg); + if (int_val & (1 << (port_id % 8))) { + int_val = int_val ^ (1 << (port_id % 8)); + wcd9xxx_interface_reg_write(tavil->wcd9xxx, + reg, int_val); + } + } +skip_port_disable: + if (val & WCD934X_SLIM_IRQ_PORT_CLOSED) { + /* + * INT SOURCE register starts from RX to TX + * but port number in the ch_mask is in opposite way + */ + bit = (tx ? j - 16 : j + 16); + dev_dbg(tavil->dev, "%s: %s port %d closed value %x, bit %u\n", + __func__, (tx ? "TX" : "RX"), port_id, val, + bit); + for (k = 0, cleared = false; k < NUM_CODEC_DAIS; k++) { + dev_dbg(tavil->dev, "%s: tavil->dai[%d].ch_mask = 0x%lx\n", + __func__, k, tavil->dai[k].ch_mask); + if (test_and_clear_bit(bit, + &tavil->dai[k].ch_mask)) { + cleared = true; + if (!tavil->dai[k].ch_mask) + wake_up( + &tavil->dai[k].dai_wait); + /* + * There are cases when multiple DAIs + * might be using the same slimbus + * channel. Hence don't break here. + */ + } + } + WARN(!cleared, + "Couldn't find slimbus %s port %d for closing\n", + (tx ? "TX" : "RX"), port_id); + } + wcd9xxx_interface_reg_write(tavil->wcd9xxx, + WCD934X_SLIM_PGD_PORT_INT_CLR_RX_0 + + (j / 8), + 1 << (j % 8)); + } + + return IRQ_HANDLED; +} + +static int tavil_setup_irqs(struct tavil_priv *tavil) +{ + int ret = 0; + struct snd_soc_codec *codec = tavil->codec; + struct wcd9xxx *wcd9xxx = tavil->wcd9xxx; + struct wcd9xxx_core_resource *core_res = + &wcd9xxx->core_res; + + ret = wcd9xxx_request_irq(core_res, WCD9XXX_IRQ_SLIMBUS, + tavil_slimbus_irq, "SLIMBUS Slave", tavil); + if (ret) + dev_err(codec->dev, "%s: Failed to request irq %d\n", __func__, + WCD9XXX_IRQ_SLIMBUS); + else + tavil_slim_interface_init_reg(codec); + + /* Register for misc interrupts as well */ + ret = wcd9xxx_request_irq(core_res, WCD934X_IRQ_MISC, + tavil_misc_irq, "CDC MISC Irq", tavil); + if (ret) + dev_err(codec->dev, "%s: Failed to request cdc misc irq\n", + __func__); + + return ret; +} + +static void tavil_init_slim_slave_cfg(struct snd_soc_codec *codec) +{ + struct tavil_priv *priv = snd_soc_codec_get_drvdata(codec); + struct afe_param_cdc_slimbus_slave_cfg *cfg; + struct wcd9xxx *wcd9xxx = priv->wcd9xxx; + uint64_t eaddr = 0; + + cfg = &priv->slimbus_slave_cfg; + cfg->minor_version = 1; + cfg->tx_slave_port_offset = 0; + cfg->rx_slave_port_offset = 16; + + memcpy(&eaddr, &wcd9xxx->slim->e_addr, sizeof(wcd9xxx->slim->e_addr)); + WARN_ON(sizeof(wcd9xxx->slim->e_addr) != 6); + cfg->device_enum_addr_lsw = eaddr & 0xFFFFFFFF; + cfg->device_enum_addr_msw = eaddr >> 32; + + dev_dbg(codec->dev, "%s: slimbus logical address 0x%llx\n", + __func__, eaddr); +} + +static void tavil_cleanup_irqs(struct tavil_priv *tavil) +{ + struct wcd9xxx *wcd9xxx = tavil->wcd9xxx; + struct wcd9xxx_core_resource *core_res = + &wcd9xxx->core_res; + + wcd9xxx_free_irq(core_res, WCD9XXX_IRQ_SLIMBUS, tavil); + wcd9xxx_free_irq(core_res, WCD934X_IRQ_MISC, tavil); +} + +/* + * wcd934x_get_micb_vout_ctl_val: converts micbias from volts to register value + * @micb_mv: micbias in mv + * + * return register value converted + */ +int wcd934x_get_micb_vout_ctl_val(u32 micb_mv) +{ + /* min micbias voltage is 1V and maximum is 2.85V */ + if (micb_mv < 1000 || micb_mv > 2850) { + pr_err("%s: unsupported micbias voltage\n", __func__); + return -EINVAL; + } + + return (micb_mv - 1000) / 50; +} +EXPORT_SYMBOL(wcd934x_get_micb_vout_ctl_val); + +static int tavil_handle_pdata(struct tavil_priv *tavil, + struct wcd9xxx_pdata *pdata) +{ + struct snd_soc_codec *codec = tavil->codec; + u8 mad_dmic_ctl_val; + u8 anc_ctl_value; + u32 def_dmic_rate, dmic_clk_drv; + int vout_ctl_1, vout_ctl_2, vout_ctl_3, vout_ctl_4; + int rc = 0; + + if (!pdata) { + dev_err(codec->dev, "%s: NULL pdata\n", __func__); + return -ENODEV; + } + + /* set micbias voltage */ + vout_ctl_1 = wcd934x_get_micb_vout_ctl_val(pdata->micbias.micb1_mv); + vout_ctl_2 = wcd934x_get_micb_vout_ctl_val(pdata->micbias.micb2_mv); + vout_ctl_3 = wcd934x_get_micb_vout_ctl_val(pdata->micbias.micb3_mv); + vout_ctl_4 = wcd934x_get_micb_vout_ctl_val(pdata->micbias.micb4_mv); + if (vout_ctl_1 < 0 || vout_ctl_2 < 0 || + vout_ctl_3 < 0 || vout_ctl_4 < 0) { + rc = -EINVAL; + goto done; + } + snd_soc_update_bits(codec, WCD934X_ANA_MICB1, 0x3F, vout_ctl_1); + snd_soc_update_bits(codec, WCD934X_ANA_MICB2, 0x3F, vout_ctl_2); + snd_soc_update_bits(codec, WCD934X_ANA_MICB3, 0x3F, vout_ctl_3); + snd_soc_update_bits(codec, WCD934X_ANA_MICB4, 0x3F, vout_ctl_4); + + /* Set the DMIC sample rate */ + switch (pdata->mclk_rate) { + case WCD934X_MCLK_CLK_9P6MHZ: + def_dmic_rate = WCD9XXX_DMIC_SAMPLE_RATE_4P8MHZ; + break; + case WCD934X_MCLK_CLK_12P288MHZ: + def_dmic_rate = WCD9XXX_DMIC_SAMPLE_RATE_4P096MHZ; + break; + default: + /* should never happen */ + dev_err(codec->dev, "%s: Invalid mclk_rate %d\n", + __func__, pdata->mclk_rate); + rc = -EINVAL; + goto done; + }; + + if (pdata->dmic_sample_rate == + WCD9XXX_DMIC_SAMPLE_RATE_UNDEFINED) { + dev_info(codec->dev, "%s: dmic_rate invalid default = %d\n", + __func__, def_dmic_rate); + pdata->dmic_sample_rate = def_dmic_rate; + } + if (pdata->mad_dmic_sample_rate == + WCD9XXX_DMIC_SAMPLE_RATE_UNDEFINED) { + dev_info(codec->dev, "%s: mad_dmic_rate invalid default = %d\n", + __func__, def_dmic_rate); + /* + * use dmic_sample_rate as the default for MAD + * if mad dmic sample rate is undefined + */ + pdata->mad_dmic_sample_rate = pdata->dmic_sample_rate; + } + + if (pdata->dmic_clk_drv == + WCD9XXX_DMIC_CLK_DRIVE_UNDEFINED) { + pdata->dmic_clk_drv = WCD934X_DMIC_CLK_DRIVE_DEFAULT; + dev_dbg(codec->dev, + "%s: dmic_clk_strength invalid, default = %d\n", + __func__, pdata->dmic_clk_drv); + } + + switch (pdata->dmic_clk_drv) { + case 2: + dmic_clk_drv = 0; + break; + case 4: + dmic_clk_drv = 1; + break; + case 8: + dmic_clk_drv = 2; + break; + case 16: + dmic_clk_drv = 3; + break; + default: + dev_err(codec->dev, + "%s: invalid dmic_clk_drv %d, using default\n", + __func__, pdata->dmic_clk_drv); + dmic_clk_drv = 0; + break; + } + + snd_soc_update_bits(codec, WCD934X_TEST_DEBUG_PAD_DRVCTL_0, + 0x0C, dmic_clk_drv << 2); + + /* + * Default the DMIC clk rates to mad_dmic_sample_rate, + * whereas, the anc/txfe dmic rates to dmic_sample_rate + * since the anc/txfe are independent of mad block. + */ + mad_dmic_ctl_val = tavil_get_dmic_clk_val(tavil->codec, + pdata->mclk_rate, + pdata->mad_dmic_sample_rate); + snd_soc_update_bits(codec, WCD934X_CPE_SS_DMIC0_CTL, + 0x0E, mad_dmic_ctl_val << 1); + snd_soc_update_bits(codec, WCD934X_CPE_SS_DMIC1_CTL, + 0x0E, mad_dmic_ctl_val << 1); + snd_soc_update_bits(codec, WCD934X_CPE_SS_DMIC2_CTL, + 0x0E, mad_dmic_ctl_val << 1); + + if (dmic_clk_drv == WCD934X_DMIC_CLK_DIV_2) + anc_ctl_value = WCD934X_ANC_DMIC_X2_FULL_RATE; + else + anc_ctl_value = WCD934X_ANC_DMIC_X2_HALF_RATE; + + snd_soc_update_bits(codec, WCD934X_CDC_ANC0_MODE_2_CTL, + 0x40, anc_ctl_value << 6); + snd_soc_update_bits(codec, WCD934X_CDC_ANC0_MODE_2_CTL, + 0x20, anc_ctl_value << 5); + snd_soc_update_bits(codec, WCD934X_CDC_ANC1_MODE_2_CTL, + 0x40, anc_ctl_value << 6); + snd_soc_update_bits(codec, WCD934X_CDC_ANC1_MODE_2_CTL, + 0x20, anc_ctl_value << 5); + +done: + return rc; +} + +static void tavil_cdc_vote_svs(struct snd_soc_codec *codec, bool vote) +{ + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + + return tavil_vote_svs(tavil, vote); +} + +static struct wcd_dsp_cdc_cb cdc_cb = { + .cdc_clk_en = tavil_codec_internal_rco_ctrl, + .cdc_vote_svs = tavil_cdc_vote_svs, +}; + +static int tavil_wdsp_initialize(struct snd_soc_codec *codec) +{ + struct wcd9xxx *control; + struct tavil_priv *tavil; + struct wcd_dsp_params params; + int ret = 0; + + control = dev_get_drvdata(codec->dev->parent); + tavil = snd_soc_codec_get_drvdata(codec); + + params.cb = &cdc_cb; + params.irqs.cpe_ipc1_irq = WCD934X_IRQ_CPE1_INTR; + params.irqs.cpe_err_irq = WCD934X_IRQ_CPE_ERROR; + params.irqs.fatal_irqs = CPE_FATAL_IRQS; + params.clk_rate = control->mclk_rate; + params.dsp_instance = 0; + + wcd_dsp_cntl_init(codec, ¶ms, &tavil->wdsp_cntl); + if (!tavil->wdsp_cntl) { + dev_err(tavil->dev, "%s: wcd-dsp-control init failed\n", + __func__); + ret = -EINVAL; + } + + return ret; +} + +/* + * tavil_soc_get_mbhc: get wcd934x_mbhc handle of corresponding codec + * @codec: handle to snd_soc_codec * + * + * return wcd934x_mbhc handle or error code in case of failure + */ +struct wcd934x_mbhc *tavil_soc_get_mbhc(struct snd_soc_codec *codec) +{ + struct tavil_priv *tavil; + + if (!codec) { + pr_err("%s: Invalid params, NULL codec\n", __func__); + return NULL; + } + tavil = snd_soc_codec_get_drvdata(codec); + + if (!tavil) { + pr_err("%s: Invalid params, NULL tavil\n", __func__); + return NULL; + } + + return tavil->mbhc; +} +EXPORT_SYMBOL(tavil_soc_get_mbhc); + +static void tavil_mclk2_reg_defaults(struct tavil_priv *tavil) +{ + int i; + struct snd_soc_codec *codec = tavil->codec; + + if (TAVIL_IS_1_0(tavil->wcd9xxx)) { + /* MCLK2 configuration */ + for (i = 0; i < ARRAY_SIZE(tavil_codec_mclk2_1_0_defaults); i++) + snd_soc_update_bits(codec, + tavil_codec_mclk2_1_0_defaults[i].reg, + tavil_codec_mclk2_1_0_defaults[i].mask, + tavil_codec_mclk2_1_0_defaults[i].val); + } + if (TAVIL_IS_1_1(tavil->wcd9xxx)) { + /* MCLK2 configuration */ + for (i = 0; i < ARRAY_SIZE(tavil_codec_mclk2_1_1_defaults); i++) + snd_soc_update_bits(codec, + tavil_codec_mclk2_1_1_defaults[i].reg, + tavil_codec_mclk2_1_1_defaults[i].mask, + tavil_codec_mclk2_1_1_defaults[i].val); + } +} + +static int tavil_device_down(struct wcd9xxx *wcd9xxx) +{ + struct snd_soc_codec *codec; + struct tavil_priv *priv; + int count; + int decimator; + int ret; + + codec = (struct snd_soc_codec *)(wcd9xxx->ssr_priv); + if (!codec->component.card) { + dev_err(codec->dev, "%s: sound card is not enumerated.\n", + __func__); + return -EINVAL; + } + + priv = snd_soc_codec_get_drvdata(codec); + for (count = 0; count < NUM_CODEC_DAIS; count++) + priv->dai[count].bus_down_in_recovery = true; + snd_event_notify(priv->dev->parent, SND_EVENT_DOWN); + + priv->mbhc->wcd_mbhc.deinit_in_progress = true; + if (delayed_work_pending(&priv->spk_anc_dwork.dwork)) + cancel_delayed_work(&priv->spk_anc_dwork.dwork); + for (decimator = 0; decimator < WCD934X_NUM_DECIMATORS; decimator++) { + if (delayed_work_pending + (&priv->tx_mute_dwork[decimator].dwork)) + cancel_delayed_work + (&priv->tx_mute_dwork[decimator].dwork); + if (delayed_work_pending + (&priv->tx_hpf_work[decimator].dwork)) + cancel_delayed_work + (&priv->tx_hpf_work[decimator].dwork); + } + if (delayed_work_pending(&priv->power_gate_work)) + cancel_delayed_work_sync(&priv->power_gate_work); + if (delayed_work_pending(&priv->mbhc->wcd_mbhc.mbhc_btn_dwork)) { + ret = cancel_delayed_work(&priv->mbhc->wcd_mbhc.mbhc_btn_dwork); + if (ret) + priv->mbhc->wcd_mbhc.mbhc_cb->lock_sleep + (&priv->mbhc->wcd_mbhc, false); + } + + if (priv->swr.ctrl_data) { + if (is_snd_event_fwk_enabled()) + swrm_wcd_notify(priv->swr.ctrl_data[0].swr_pdev, + SWR_DEVICE_SSR_DOWN, NULL); + swrm_wcd_notify(priv->swr.ctrl_data[0].swr_pdev, + SWR_DEVICE_DOWN, NULL); + } + tavil_dsd_reset(priv->dsd_config); + if (!is_snd_event_fwk_enabled()) + snd_soc_card_change_online_state(codec->component.card, 0); + wcd_dsp_ssr_event(priv->wdsp_cntl, WCD_CDC_DOWN_EVENT); + wcd_resmgr_set_sido_input_src_locked(priv->resmgr, + SIDO_SOURCE_INTERNAL); + + return 0; +} + +static int tavil_post_reset_cb(struct wcd9xxx *wcd9xxx) +{ + int i, ret = 0; + struct wcd9xxx *control; + struct snd_soc_codec *codec; + struct tavil_priv *tavil; + struct wcd9xxx_pdata *pdata; + struct wcd_mbhc *mbhc; + + codec = (struct snd_soc_codec *)(wcd9xxx->ssr_priv); + if (!codec->component.card) { + dev_err(codec->dev, "%s: sound card is not enumerated.\n", + __func__); + return -EINVAL; + } + tavil = snd_soc_codec_get_drvdata(codec); + control = dev_get_drvdata(codec->dev->parent); + + wcd9xxx_set_power_state(tavil->wcd9xxx, + WCD_REGION_POWER_COLLAPSE_REMOVE, + WCD9XXX_DIG_CORE_REGION_1); + + mutex_lock(&tavil->codec_mutex); + + tavil_vote_svs(tavil, true); + tavil_slimbus_slave_port_cfg.slave_dev_intfdev_la = + control->slim_slave->laddr; + tavil_slimbus_slave_port_cfg.slave_dev_pgd_la = + control->slim->laddr; + tavil_init_slim_slave_cfg(codec); + if (!is_snd_event_fwk_enabled()) + snd_soc_card_change_online_state(codec->component.card, 1); + + for (i = 0; i < TAVIL_MAX_MICBIAS; i++) + tavil->micb_ref[i] = 0; + + dev_dbg(codec->dev, "%s: MCLK Rate = %x\n", + __func__, control->mclk_rate); + + if (control->mclk_rate == WCD934X_MCLK_CLK_12P288MHZ) + snd_soc_update_bits(codec, WCD934X_CODEC_RPM_CLK_MCLK_CFG, + 0x03, 0x00); + else if (control->mclk_rate == WCD934X_MCLK_CLK_9P6MHZ) + snd_soc_update_bits(codec, WCD934X_CODEC_RPM_CLK_MCLK_CFG, + 0x03, 0x01); + tavil_update_reg_defaults(tavil); + wcd_resmgr_post_ssr_v2(tavil->resmgr); + tavil_codec_init_reg(tavil); + __tavil_enable_efuse_sensing(tavil); + tavil_mclk2_reg_defaults(tavil); + + __tavil_cdc_mclk_enable(tavil, true); + regcache_mark_dirty(codec->component.regmap); + regcache_sync(codec->component.regmap); + __tavil_cdc_mclk_enable(tavil, false); + + tavil_update_cpr_defaults(tavil); + + pdata = dev_get_platdata(codec->dev->parent); + ret = tavil_handle_pdata(tavil, pdata); + if (ret < 0) + dev_err(codec->dev, "%s: invalid pdata\n", __func__); + + /* Initialize MBHC module */ + mbhc = &tavil->mbhc->wcd_mbhc; + ret = tavil_mbhc_post_ssr_init(tavil->mbhc, codec); + if (ret) { + dev_err(codec->dev, "%s: mbhc initialization failed\n", + __func__); + goto done; + } else { + tavil_mbhc_hs_detect(codec, mbhc->mbhc_cfg); + } + + /* DSD initialization */ + ret = tavil_dsd_post_ssr_init(tavil->dsd_config); + if (ret) + dev_dbg(tavil->dev, "%s: DSD init failed\n", __func__); + + tavil_cleanup_irqs(tavil); + ret = tavil_setup_irqs(tavil); + if (ret) { + dev_err(codec->dev, "%s: tavil irq setup failed %d\n", + __func__, ret); + goto done; + } + + if (tavil->swr.ctrl_data && is_snd_event_fwk_enabled()) + swrm_wcd_notify(tavil->swr.ctrl_data[0].swr_pdev, + SWR_DEVICE_SSR_UP, NULL); + tavil_set_spkr_mode(codec, tavil->swr.spkr_mode); + /* + * Once the codec initialization is completed, the svs vote + * can be released allowing the codec to go to SVS2. + */ + tavil_vote_svs(tavil, false); + wcd_dsp_ssr_event(tavil->wdsp_cntl, WCD_CDC_UP_EVENT); + snd_event_notify(tavil->dev->parent, SND_EVENT_UP); + +done: + mutex_unlock(&tavil->codec_mutex); + return ret; +} + +static int tavil_soc_codec_probe(struct snd_soc_codec *codec) +{ + struct wcd9xxx *control; + struct tavil_priv *tavil; + struct wcd9xxx_pdata *pdata; + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + int i, ret; + void *ptr = NULL; + + control = dev_get_drvdata(codec->dev->parent); + + dev_info(codec->dev, "%s()\n", __func__); + tavil = snd_soc_codec_get_drvdata(codec); + tavil->intf_type = wcd9xxx_get_intf_type(); + + control->dev_down = tavil_device_down; + control->post_reset = tavil_post_reset_cb; + control->ssr_priv = (void *)codec; + + /* Resource Manager post Init */ + ret = wcd_resmgr_post_init(tavil->resmgr, &tavil_resmgr_cb, codec); + if (ret) { + dev_err(codec->dev, "%s: wcd resmgr post init failed\n", + __func__); + goto err; + } + /* Class-H Init */ + wcd_clsh_init(&tavil->clsh_d); + /* Default HPH Mode to Class-H Low HiFi */ + tavil->hph_mode = CLS_H_LOHIFI; + + tavil->fw_data = devm_kzalloc(codec->dev, sizeof(*(tavil->fw_data)), + GFP_KERNEL); + if (!tavil->fw_data) + goto err; + + set_bit(WCD9XXX_ANC_CAL, tavil->fw_data->cal_bit); + set_bit(WCD9XXX_MBHC_CAL, tavil->fw_data->cal_bit); + set_bit(WCD9XXX_MAD_CAL, tavil->fw_data->cal_bit); + set_bit(WCD9XXX_VBAT_CAL, tavil->fw_data->cal_bit); + + ret = wcd_cal_create_hwdep(tavil->fw_data, + WCD9XXX_CODEC_HWDEP_NODE, codec); + if (ret < 0) { + dev_err(codec->dev, "%s hwdep failed %d\n", __func__, ret); + goto err_hwdep; + } + + /* Initialize MBHC module */ + ret = tavil_mbhc_init(&tavil->mbhc, codec, tavil->fw_data); + if (ret) { + pr_err("%s: mbhc initialization failed\n", __func__); + goto err_hwdep; + } + + tavil->codec = codec; + for (i = 0; i < COMPANDER_MAX; i++) + tavil->comp_enabled[i] = 0; + + tavil_codec_init_reg(tavil); + + pdata = dev_get_platdata(codec->dev->parent); + ret = tavil_handle_pdata(tavil, pdata); + if (ret < 0) { + dev_err(codec->dev, "%s: bad pdata\n", __func__); + goto err_hwdep; + } + + ptr = devm_kzalloc(codec->dev, (sizeof(tavil_rx_chs) + + sizeof(tavil_tx_chs)), GFP_KERNEL); + if (!ptr) { + ret = -ENOMEM; + goto err_hwdep; + } + + for (i = 0; i < NUM_CODEC_DAIS; i++) { + INIT_LIST_HEAD(&tavil->dai[i].wcd9xxx_ch_list); + init_waitqueue_head(&tavil->dai[i].dai_wait); + } + + if (tavil->intf_type == WCD9XXX_INTERFACE_TYPE_SLIMBUS) { + snd_soc_dapm_new_controls(dapm, tavil_dapm_slim_widgets, + ARRAY_SIZE(tavil_dapm_slim_widgets)); + snd_soc_dapm_add_routes(dapm, tavil_slim_audio_map, + ARRAY_SIZE(tavil_slim_audio_map)); + tavil_slimbus_slave_port_cfg.slave_dev_intfdev_la = + control->slim_slave->laddr; + tavil_slimbus_slave_port_cfg.slave_dev_pgd_la = + control->slim->laddr; + tavil_slimbus_slave_port_cfg.slave_port_mapping[0] = + WCD934X_TX13; + tavil_init_slim_slave_cfg(codec); + } else { + snd_soc_dapm_new_controls(dapm, tavil_dapm_i2s_widgets, + ARRAY_SIZE(tavil_dapm_i2s_widgets)); + snd_soc_dapm_add_routes(dapm, tavil_i2s_audio_map, + ARRAY_SIZE(tavil_i2s_audio_map)); + } + + control->num_rx_port = WCD934X_RX_MAX; + control->rx_chs = ptr; + memcpy(control->rx_chs, tavil_rx_chs, sizeof(tavil_rx_chs)); + control->num_tx_port = WCD934X_TX_MAX; + control->tx_chs = ptr + sizeof(tavil_rx_chs); + memcpy(control->tx_chs, tavil_tx_chs, sizeof(tavil_tx_chs)); + + ret = tavil_setup_irqs(tavil); + if (ret) { + dev_err(tavil->dev, "%s: tavil irq setup failed %d\n", + __func__, ret); + goto err_pdata; + } + + for (i = 0; i < WCD934X_NUM_DECIMATORS; i++) { + tavil->tx_hpf_work[i].tavil = tavil; + tavil->tx_hpf_work[i].decimator = i; + INIT_DELAYED_WORK(&tavil->tx_hpf_work[i].dwork, + tavil_tx_hpf_corner_freq_callback); + + tavil->tx_mute_dwork[i].tavil = tavil; + tavil->tx_mute_dwork[i].decimator = i; + INIT_DELAYED_WORK(&tavil->tx_mute_dwork[i].dwork, + tavil_tx_mute_update_callback); + } + + tavil->spk_anc_dwork.tavil = tavil; + INIT_DELAYED_WORK(&tavil->spk_anc_dwork.dwork, + tavil_spk_anc_update_callback); + + tavil_mclk2_reg_defaults(tavil); + + /* DSD initialization */ + tavil->dsd_config = tavil_dsd_init(codec); + if (IS_ERR_OR_NULL(tavil->dsd_config)) + dev_dbg(tavil->dev, "%s: DSD init failed\n", __func__); + + mutex_lock(&tavil->codec_mutex); + snd_soc_dapm_disable_pin(dapm, "ANC EAR PA"); + snd_soc_dapm_disable_pin(dapm, "ANC EAR"); + snd_soc_dapm_disable_pin(dapm, "ANC HPHL PA"); + snd_soc_dapm_disable_pin(dapm, "ANC HPHR PA"); + snd_soc_dapm_disable_pin(dapm, "ANC HPHL"); + snd_soc_dapm_disable_pin(dapm, "ANC HPHR"); + snd_soc_dapm_enable_pin(dapm, "ANC SPK1 PA"); + mutex_unlock(&tavil->codec_mutex); + + snd_soc_dapm_ignore_suspend(dapm, "AIF1 Playback"); + snd_soc_dapm_ignore_suspend(dapm, "AIF1 Capture"); + snd_soc_dapm_ignore_suspend(dapm, "AIF2 Playback"); + snd_soc_dapm_ignore_suspend(dapm, "AIF2 Capture"); + snd_soc_dapm_ignore_suspend(dapm, "AIF3 Playback"); + snd_soc_dapm_ignore_suspend(dapm, "AIF3 Capture"); + if (tavil->intf_type == WCD9XXX_INTERFACE_TYPE_SLIMBUS) { + snd_soc_dapm_ignore_suspend(dapm, "AIF4 Playback"); + snd_soc_dapm_ignore_suspend(dapm, "AIF4 MAD TX"); + snd_soc_dapm_ignore_suspend(dapm, "VIfeed"); + } + + snd_soc_dapm_sync(dapm); + + tavil_wdsp_initialize(codec); + + /* + * Once the codec initialization is completed, the svs vote + * can be released allowing the codec to go to SVS2. + */ + tavil_vote_svs(tavil, false); + + return ret; + +err_pdata: + devm_kfree(codec->dev, ptr); + control->rx_chs = NULL; + control->tx_chs = NULL; +err_hwdep: + devm_kfree(codec->dev, tavil->fw_data); + tavil->fw_data = NULL; +err: + return ret; +} + +static int tavil_soc_codec_remove(struct snd_soc_codec *codec) +{ + struct wcd9xxx *control; + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + + control = dev_get_drvdata(codec->dev->parent); + devm_kfree(codec->dev, control->rx_chs); + /* slimslave deinit in wcd core looks for this value */ + control->num_rx_port = 0; + control->num_tx_port = 0; + control->rx_chs = NULL; + control->tx_chs = NULL; + tavil_cleanup_irqs(tavil); + + if (tavil->wdsp_cntl) + wcd_dsp_cntl_deinit(&tavil->wdsp_cntl); + + /* Deinitialize MBHC module */ + tavil_mbhc_deinit(codec); + tavil->mbhc = NULL; + + return 0; +} + +static struct regmap *tavil_get_regmap(struct device *dev) +{ + struct wcd9xxx *control = dev_get_drvdata(dev->parent); + + return control->regmap; +} + +static struct snd_soc_codec_driver soc_codec_dev_tavil = { + .probe = tavil_soc_codec_probe, + .remove = tavil_soc_codec_remove, + .get_regmap = tavil_get_regmap, + .component_driver = { + .controls = tavil_snd_controls, + .num_controls = ARRAY_SIZE(tavil_snd_controls), + .dapm_widgets = tavil_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(tavil_dapm_widgets), + .dapm_routes = tavil_audio_map, + .num_dapm_routes = ARRAY_SIZE(tavil_audio_map), + }, +}; + +#ifdef CONFIG_PM +static int tavil_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct tavil_priv *tavil = platform_get_drvdata(pdev); + + if (!tavil) { + dev_err(dev, "%s: tavil private data is NULL\n", __func__); + return -EINVAL; + } + dev_dbg(dev, "%s: system suspend\n", __func__); + if (delayed_work_pending(&tavil->power_gate_work) && + cancel_delayed_work_sync(&tavil->power_gate_work)) + tavil_codec_power_gate_digital_core(tavil); + return 0; +} + +static int tavil_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct tavil_priv *tavil = platform_get_drvdata(pdev); + + if (!tavil) { + dev_err(dev, "%s: tavil private data is NULL\n", __func__); + return -EINVAL; + } + dev_dbg(dev, "%s: system resume\n", __func__); + return 0; +} + +static const struct dev_pm_ops tavil_pm_ops = { + .suspend = tavil_suspend, + .resume = tavil_resume, +}; +#endif + +static int wcd9xxx_swrm_i2c_bulk_write(struct wcd9xxx *wcd9xxx, + struct wcd9xxx_reg_val *bulk_reg, + size_t len) +{ + int i, ret = 0; + unsigned short swr_wr_addr_base; + unsigned short swr_wr_data_base; + + swr_wr_addr_base = WCD934X_SWR_AHB_BRIDGE_WR_ADDR_0; + swr_wr_data_base = WCD934X_SWR_AHB_BRIDGE_WR_DATA_0; + + for (i = 0; i < (len * 2); i += 2) { + /* First Write the Data to register */ + ret = regmap_bulk_write(wcd9xxx->regmap, + swr_wr_data_base, bulk_reg[i].buf, 4); + if (ret < 0) { + dev_err(wcd9xxx->dev, "%s: WR Data Failure\n", + __func__); + break; + } + /* Next Write Address */ + ret = regmap_bulk_write(wcd9xxx->regmap, + swr_wr_addr_base, + bulk_reg[i+1].buf, 4); + if (ret < 0) { + dev_err(wcd9xxx->dev, "%s: WR Addr Failure\n", + __func__); + break; + } + } + return ret; +} + +static int tavil_swrm_read(void *handle, int reg) +{ + struct tavil_priv *tavil; + struct wcd9xxx *wcd9xxx; + unsigned short swr_rd_addr_base; + unsigned short swr_rd_data_base; + int val, ret; + + if (!handle) { + pr_err("%s: NULL handle\n", __func__); + return -EINVAL; + } + tavil = (struct tavil_priv *)handle; + wcd9xxx = tavil->wcd9xxx; + + dev_dbg(tavil->dev, "%s: Reading soundwire register, 0x%x\n", + __func__, reg); + swr_rd_addr_base = WCD934X_SWR_AHB_BRIDGE_RD_ADDR_0; + swr_rd_data_base = WCD934X_SWR_AHB_BRIDGE_RD_DATA_0; + + mutex_lock(&tavil->swr.read_mutex); + ret = regmap_bulk_write(wcd9xxx->regmap, swr_rd_addr_base, + (u8 *)®, 4); + if (ret < 0) { + dev_err(tavil->dev, "%s: RD Addr Failure\n", __func__); + goto done; + } + ret = regmap_bulk_read(wcd9xxx->regmap, swr_rd_data_base, + (u8 *)&val, 4); + if (ret < 0) { + dev_err(tavil->dev, "%s: RD Data Failure\n", __func__); + goto done; + } + ret = val; +done: + mutex_unlock(&tavil->swr.read_mutex); + + return ret; +} + +static int tavil_swrm_bulk_write(void *handle, u32 *reg, u32 *val, size_t len) +{ + struct tavil_priv *tavil; + struct wcd9xxx *wcd9xxx; + struct wcd9xxx_reg_val *bulk_reg; + unsigned short swr_wr_addr_base; + unsigned short swr_wr_data_base; + int i, j, ret; + + if (!handle || !reg || !val) { + pr_err("%s: NULL parameter\n", __func__); + return -EINVAL; + } + if (len <= 0) { + pr_err("%s: Invalid size: %zu\n", __func__, len); + return -EINVAL; + } + tavil = (struct tavil_priv *)handle; + wcd9xxx = tavil->wcd9xxx; + + swr_wr_addr_base = WCD934X_SWR_AHB_BRIDGE_WR_ADDR_0; + swr_wr_data_base = WCD934X_SWR_AHB_BRIDGE_WR_DATA_0; + + bulk_reg = kzalloc((2 * len * sizeof(struct wcd9xxx_reg_val)), + GFP_KERNEL); + if (!bulk_reg) + return -ENOMEM; + + for (i = 0, j = 0; i < (len * 2); i += 2, j++) { + bulk_reg[i].reg = swr_wr_data_base; + bulk_reg[i].buf = (u8 *)(&val[j]); + bulk_reg[i].bytes = 4; + bulk_reg[i+1].reg = swr_wr_addr_base; + bulk_reg[i+1].buf = (u8 *)(®[j]); + bulk_reg[i+1].bytes = 4; + } + + mutex_lock(&tavil->swr.write_mutex); + if (tavil->intf_type == WCD9XXX_INTERFACE_TYPE_SLIMBUS) + ret = wcd9xxx_slim_bulk_write(wcd9xxx, bulk_reg, + (len * 2), false); + else + ret = wcd9xxx_swrm_i2c_bulk_write(wcd9xxx, bulk_reg, len); + if (ret) { + dev_err(tavil->dev, "%s: swrm bulk write failed, ret: %d\n", + __func__, ret); + } + mutex_unlock(&tavil->swr.write_mutex); + + kfree(bulk_reg); + return ret; +} + +static int tavil_swrm_write(void *handle, int reg, int val) +{ + struct tavil_priv *tavil; + struct wcd9xxx *wcd9xxx; + unsigned short swr_wr_addr_base; + unsigned short swr_wr_data_base; + struct wcd9xxx_reg_val bulk_reg[2]; + int ret; + + if (!handle) { + pr_err("%s: NULL handle\n", __func__); + return -EINVAL; + } + tavil = (struct tavil_priv *)handle; + wcd9xxx = tavil->wcd9xxx; + + swr_wr_addr_base = WCD934X_SWR_AHB_BRIDGE_WR_ADDR_0; + swr_wr_data_base = WCD934X_SWR_AHB_BRIDGE_WR_DATA_0; + + /* First Write the Data to register */ + bulk_reg[0].reg = swr_wr_data_base; + bulk_reg[0].buf = (u8 *)(&val); + bulk_reg[0].bytes = 4; + bulk_reg[1].reg = swr_wr_addr_base; + bulk_reg[1].buf = (u8 *)(®); + bulk_reg[1].bytes = 4; + + mutex_lock(&tavil->swr.write_mutex); + if (tavil->intf_type == WCD9XXX_INTERFACE_TYPE_SLIMBUS) + ret = wcd9xxx_slim_bulk_write(wcd9xxx, bulk_reg, 2, false); + else + ret = wcd9xxx_swrm_i2c_bulk_write(wcd9xxx, bulk_reg, 1); + if (ret < 0) + dev_err(tavil->dev, "%s: WR Data Failure\n", __func__); + mutex_unlock(&tavil->swr.write_mutex); + + return ret; +} + +static int tavil_swrm_clock(void *handle, bool enable) +{ + struct tavil_priv *tavil; + + if (!handle) { + pr_err("%s: NULL handle\n", __func__); + return -EINVAL; + } + tavil = (struct tavil_priv *)handle; + + mutex_lock(&tavil->swr.clk_mutex); + dev_dbg(tavil->dev, "%s: swrm clock %s\n", + __func__, (enable?"enable" : "disable")); + if (enable) { + tavil->swr.clk_users++; + if (tavil->swr.clk_users == 1) { + regmap_update_bits(tavil->wcd9xxx->regmap, + WCD934X_TEST_DEBUG_NPL_DLY_TEST_1, + 0x10, 0x00); + __tavil_cdc_mclk_enable(tavil, true); + regmap_update_bits(tavil->wcd9xxx->regmap, + WCD934X_CDC_CLK_RST_CTRL_SWR_CONTROL, + 0x01, 0x01); + } + } else { + tavil->swr.clk_users--; + if (tavil->swr.clk_users == 0) { + regmap_update_bits(tavil->wcd9xxx->regmap, + WCD934X_CDC_CLK_RST_CTRL_SWR_CONTROL, + 0x01, 0x00); + __tavil_cdc_mclk_enable(tavil, false); + regmap_update_bits(tavil->wcd9xxx->regmap, + WCD934X_TEST_DEBUG_NPL_DLY_TEST_1, + 0x10, 0x10); + } + } + dev_dbg(tavil->dev, "%s: swrm clock users %d\n", + __func__, tavil->swr.clk_users); + mutex_unlock(&tavil->swr.clk_mutex); + + return 0; +} + +static int tavil_swrm_handle_irq(void *handle, + irqreturn_t (*swrm_irq_handler)(int irq, + void *data), + void *swrm_handle, + int action) +{ + struct tavil_priv *tavil; + int ret = 0; + struct wcd9xxx *wcd9xxx; + + if (!handle) { + pr_err("%s: NULL handle\n", __func__); + return -EINVAL; + } + tavil = (struct tavil_priv *) handle; + wcd9xxx = tavil->wcd9xxx; + + if (action) { + ret = wcd9xxx_request_irq(&wcd9xxx->core_res, + WCD934X_IRQ_SOUNDWIRE, + swrm_irq_handler, + "Tavil SWR Master", swrm_handle); + if (ret) + dev_err(tavil->dev, "%s: Failed to request irq %d\n", + __func__, WCD934X_IRQ_SOUNDWIRE); + } else + wcd9xxx_free_irq(&wcd9xxx->core_res, WCD934X_IRQ_SOUNDWIRE, + swrm_handle); + + return ret; +} + +static void tavil_codec_add_spi_device(struct tavil_priv *tavil, + struct device_node *node) +{ + struct spi_master *master; + struct spi_device *spi; + u32 prop_value; + int rc; + + /* Read the master bus num from DT node */ + rc = of_property_read_u32(node, "qcom,master-bus-num", + &prop_value); + if (rc < 0) { + dev_err(tavil->dev, "%s: prop %s not found in node %s", + __func__, "qcom,master-bus-num", node->full_name); + goto done; + } + + /* Get the reference to SPI master */ + master = spi_busnum_to_master(prop_value); + if (!master) { + dev_err(tavil->dev, "%s: Invalid spi_master for bus_num %u\n", + __func__, prop_value); + goto done; + } + + /* Allocate the spi device */ + spi = spi_alloc_device(master); + if (!spi) { + dev_err(tavil->dev, "%s: spi_alloc_device failed\n", + __func__); + goto err_spi_alloc_dev; + } + + /* Initialize device properties */ + if (of_modalias_node(node, spi->modalias, + sizeof(spi->modalias)) < 0) { + dev_err(tavil->dev, "%s: cannot find modalias for %s\n", + __func__, node->full_name); + goto err_dt_parse; + } + + rc = of_property_read_u32(node, "qcom,chip-select", + &prop_value); + if (rc < 0) { + dev_err(tavil->dev, "%s: prop %s not found in node %s", + __func__, "qcom,chip-select", node->full_name); + goto err_dt_parse; + } + spi->chip_select = prop_value; + + rc = of_property_read_u32(node, "qcom,max-frequency", + &prop_value); + if (rc < 0) { + dev_err(tavil->dev, "%s: prop %s not found in node %s", + __func__, "qcom,max-frequency", node->full_name); + goto err_dt_parse; + } + spi->max_speed_hz = prop_value; + + spi->dev.of_node = node; + + rc = spi_add_device(spi); + if (rc < 0) { + dev_err(tavil->dev, "%s: spi_add_device failed\n", __func__); + goto err_dt_parse; + } + + tavil->spi = spi; + /* Put the reference to SPI master */ + put_device(&master->dev); + + return; + +err_dt_parse: + spi_dev_put(spi); + +err_spi_alloc_dev: + /* Put the reference to SPI master */ + put_device(&master->dev); +done: + return; +} + +static void tavil_add_child_devices(struct work_struct *work) +{ + struct tavil_priv *tavil; + struct platform_device *pdev; + struct device_node *node; + struct wcd9xxx *wcd9xxx; + struct tavil_swr_ctrl_data *swr_ctrl_data = NULL, *temp; + int ret, ctrl_num = 0; + struct wcd_swr_ctrl_platform_data *platdata; + char plat_dev_name[WCD934X_STRING_LEN]; + + tavil = container_of(work, struct tavil_priv, + tavil_add_child_devices_work); + if (!tavil) { + pr_err("%s: Memory for WCD934X does not exist\n", + __func__); + return; + } + wcd9xxx = tavil->wcd9xxx; + if (!wcd9xxx) { + pr_err("%s: Memory for WCD9XXX does not exist\n", + __func__); + return; + } + if (!wcd9xxx->dev->of_node) { + dev_err(wcd9xxx->dev, "%s: DT node for wcd9xxx does not exist\n", + __func__); + return; + } + + platdata = &tavil->swr.plat_data; + tavil->child_count = 0; + + for_each_child_of_node(wcd9xxx->dev->of_node, node) { + + /* Parse and add the SPI device node */ + if (!strcmp(node->name, "wcd_spi")) { + tavil_codec_add_spi_device(tavil, node); + continue; + } + + /* Parse other child device nodes and add platform device */ + if (!strcmp(node->name, "swr_master")) + strlcpy(plat_dev_name, "tavil_swr_ctrl", + (WCD934X_STRING_LEN - 1)); + else if (strnstr(node->name, "msm_cdc_pinctrl", + strlen("msm_cdc_pinctrl")) != NULL) + strlcpy(plat_dev_name, node->name, + (WCD934X_STRING_LEN - 1)); + else + continue; + + pdev = platform_device_alloc(plat_dev_name, -1); + if (!pdev) { + dev_err(wcd9xxx->dev, "%s: pdev memory alloc failed\n", + __func__); + ret = -ENOMEM; + goto err_mem; + } + pdev->dev.parent = tavil->dev; + pdev->dev.of_node = node; + + if (strcmp(node->name, "swr_master") == 0) { + ret = platform_device_add_data(pdev, platdata, + sizeof(*platdata)); + if (ret) { + dev_err(&pdev->dev, + "%s: cannot add plat data ctrl:%d\n", + __func__, ctrl_num); + goto err_pdev_add; + } + } + + ret = platform_device_add(pdev); + if (ret) { + dev_err(&pdev->dev, + "%s: Cannot add platform device\n", + __func__); + goto err_pdev_add; + } + + if (strcmp(node->name, "swr_master") == 0) { + temp = krealloc(swr_ctrl_data, + (ctrl_num + 1) * sizeof( + struct tavil_swr_ctrl_data), + GFP_KERNEL); + if (!temp) { + dev_err(wcd9xxx->dev, "out of memory\n"); + ret = -ENOMEM; + goto err_pdev_add; + } + swr_ctrl_data = temp; + swr_ctrl_data[ctrl_num].swr_pdev = pdev; + ctrl_num++; + dev_dbg(&pdev->dev, + "%s: Added soundwire ctrl device(s)\n", + __func__); + tavil->swr.ctrl_data = swr_ctrl_data; + } + if (tavil->child_count < WCD934X_CHILD_DEVICES_MAX) + tavil->pdev_child_devices[tavil->child_count++] = pdev; + else + goto err_mem; + } + + return; + +err_pdev_add: + platform_device_put(pdev); +err_mem: + return; +} + +static int __tavil_enable_efuse_sensing(struct tavil_priv *tavil) +{ + int val, rc; + + WCD9XXX_V2_BG_CLK_LOCK(tavil->resmgr); + __tavil_cdc_mclk_enable_locked(tavil, true); + + regmap_update_bits(tavil->wcd9xxx->regmap, + WCD934X_CHIP_TIER_CTRL_EFUSE_CTL, 0x1E, 0x10); + regmap_update_bits(tavil->wcd9xxx->regmap, + WCD934X_CHIP_TIER_CTRL_EFUSE_CTL, 0x01, 0x01); + /* + * 5ms sleep required after enabling efuse control + * before checking the status. + */ + usleep_range(5000, 5500); + wcd_resmgr_set_sido_input_src(tavil->resmgr, + SIDO_SOURCE_RCO_BG); + + WCD9XXX_V2_BG_CLK_UNLOCK(tavil->resmgr); + + rc = regmap_read(tavil->wcd9xxx->regmap, + WCD934X_CHIP_TIER_CTRL_EFUSE_STATUS, &val); + if (rc || (!(val & 0x01))) + WARN(1, "%s: Efuse sense is not complete val=%x, ret=%d\n", + __func__, val, rc); + + __tavil_cdc_mclk_enable(tavil, false); + + return rc; +} + +static void ___tavil_get_codec_fine_version(struct tavil_priv *tavil) +{ + int val1, val2, version; + struct regmap *regmap; + u16 id_minor; + u32 version_mask = 0; + + regmap = tavil->wcd9xxx->regmap; + version = tavil->wcd9xxx->version; + id_minor = tavil->wcd9xxx->codec_type->id_minor; + + regmap_read(regmap, WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT14, &val1); + regmap_read(regmap, WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT15, &val2); + + dev_dbg(tavil->dev, "%s: chip version :0x%x 0x:%x\n", + __func__, val1, val2); + + version_mask |= (!!((u8)val1 & 0x80)) << DSD_DISABLED_MASK; + version_mask |= (!!((u8)val2 & 0x01)) << SLNQ_DISABLED_MASK; + + switch (version_mask) { + case DSD_DISABLED | SLNQ_DISABLED: + if (id_minor == cpu_to_le16(0)) + version = TAVIL_VERSION_WCD9340_1_0; + else if (id_minor == cpu_to_le16(0x01)) + version = TAVIL_VERSION_WCD9340_1_1; + break; + case SLNQ_DISABLED: + if (id_minor == cpu_to_le16(0)) + version = TAVIL_VERSION_WCD9341_1_0; + else if (id_minor == cpu_to_le16(0x01)) + version = TAVIL_VERSION_WCD9341_1_1; + break; + } + + tavil->wcd9xxx->version = version; + tavil->wcd9xxx->codec_type->version = version; +} + +/* + * tavil_get_wcd_dsp_cntl: Get the reference to wcd_dsp_cntl + * @dev: Device pointer for codec device + * + * This API gets the reference to codec's struct wcd_dsp_cntl + */ +struct wcd_dsp_cntl *tavil_get_wcd_dsp_cntl(struct device *dev) +{ + struct platform_device *pdev; + struct tavil_priv *tavil; + + if (!dev) { + pr_err("%s: Invalid device\n", __func__); + return NULL; + } + + pdev = to_platform_device(dev); + tavil = platform_get_drvdata(pdev); + + return tavil->wdsp_cntl; +} +EXPORT_SYMBOL(tavil_get_wcd_dsp_cntl); + +static void wcd934x_ssr_disable(struct device *dev, void *data) +{ + struct wcd9xxx *wcd9xxx = dev_get_drvdata(dev); + struct tavil_priv *tavil; + struct snd_soc_codec *codec; + int count = 0; + + if (!wcd9xxx) { + dev_dbg(dev, "%s: wcd9xxx pointer NULL.\n", __func__); + return; + } + codec = (struct snd_soc_codec *)(wcd9xxx->ssr_priv); + tavil = snd_soc_codec_get_drvdata(codec); + + for (count = 0; count < NUM_CODEC_DAIS; count++) + tavil->dai[count].bus_down_in_recovery = true; +} + +static const struct snd_event_ops wcd934x_ssr_ops = { + .disable = wcd934x_ssr_disable, +}; + +static int tavil_probe(struct platform_device *pdev) +{ + int ret = 0, len = 0; + struct tavil_priv *tavil; + struct clk *wcd_ext_clk; + struct wcd9xxx_resmgr_v2 *resmgr; + struct wcd9xxx_power_region *cdc_pwr; + const __be32 *micb_prop; + + tavil = devm_kzalloc(&pdev->dev, sizeof(struct tavil_priv), + GFP_KERNEL); + if (!tavil) + return -ENOMEM; + + tavil->intf_type = wcd9xxx_get_intf_type(); + if (tavil->intf_type != WCD9XXX_INTERFACE_TYPE_I2C && + tavil->intf_type != WCD9XXX_INTERFACE_TYPE_SLIMBUS) { + devm_kfree(&pdev->dev, tavil); + return -EPROBE_DEFER; + } + + if (tavil->intf_type == WCD9XXX_INTERFACE_TYPE_I2C) { + if (apr_get_subsys_state() == APR_SUBSYS_DOWN) { + dev_dbg(&pdev->dev, "%s: dsp down\n", __func__); + devm_kfree(&pdev->dev, tavil); + return -EPROBE_DEFER; + } + } + + platform_set_drvdata(pdev, tavil); + + tavil->wcd9xxx = dev_get_drvdata(pdev->dev.parent); + tavil->dev = &pdev->dev; + INIT_DELAYED_WORK(&tavil->power_gate_work, tavil_codec_power_gate_work); + mutex_init(&tavil->power_lock); + INIT_WORK(&tavil->tavil_add_child_devices_work, + tavil_add_child_devices); + mutex_init(&tavil->micb_lock); + mutex_init(&tavil->swr.read_mutex); + mutex_init(&tavil->swr.write_mutex); + mutex_init(&tavil->swr.clk_mutex); + mutex_init(&tavil->codec_mutex); + mutex_init(&tavil->svs_mutex); + + /* + * Codec hardware by default comes up in SVS mode. + * Initialize the svs_ref_cnt to 1 to reflect the hardware + * state in the driver. + */ + tavil->svs_ref_cnt = 1; + + cdc_pwr = devm_kzalloc(&pdev->dev, sizeof(struct wcd9xxx_power_region), + GFP_KERNEL); + if (!cdc_pwr) { + ret = -ENOMEM; + goto err_resmgr; + } + tavil->wcd9xxx->wcd9xxx_pwr[WCD9XXX_DIG_CORE_REGION_1] = cdc_pwr; + cdc_pwr->pwr_collapse_reg_min = WCD934X_DIG_CORE_REG_MIN; + cdc_pwr->pwr_collapse_reg_max = WCD934X_DIG_CORE_REG_MAX; + wcd9xxx_set_power_state(tavil->wcd9xxx, + WCD_REGION_POWER_COLLAPSE_REMOVE, + WCD9XXX_DIG_CORE_REGION_1); + /* + * Init resource manager so that if child nodes such as SoundWire + * requests for clock, resource manager can honor the request + */ + resmgr = wcd_resmgr_init(&tavil->wcd9xxx->core_res, NULL); + if (IS_ERR(resmgr)) { + ret = PTR_ERR(resmgr); + dev_err(&pdev->dev, "%s: Failed to initialize wcd resmgr\n", + __func__); + goto err_resmgr; + } + tavil->resmgr = resmgr; + tavil->swr.plat_data.handle = (void *) tavil; + tavil->swr.plat_data.read = tavil_swrm_read; + tavil->swr.plat_data.write = tavil_swrm_write; + tavil->swr.plat_data.bulk_write = tavil_swrm_bulk_write; + tavil->swr.plat_data.clk = tavil_swrm_clock; + tavil->swr.plat_data.handle_irq = tavil_swrm_handle_irq; + tavil->swr.spkr_gain_offset = WCD934X_RX_GAIN_OFFSET_0_DB; + + /* Register for Clock */ + wcd_ext_clk = clk_get(tavil->wcd9xxx->dev, "wcd_clk"); + if (IS_ERR(wcd_ext_clk)) { + dev_err(tavil->wcd9xxx->dev, "%s: clk get %s failed\n", + __func__, "wcd_ext_clk"); + goto err_clk; + } + tavil->wcd_ext_clk = wcd_ext_clk; + set_bit(AUDIO_NOMINAL, &tavil->status_mask); + /* Update codec register default values */ + dev_dbg(&pdev->dev, "%s: MCLK Rate = %x\n", __func__, + tavil->wcd9xxx->mclk_rate); + if (tavil->wcd9xxx->mclk_rate == WCD934X_MCLK_CLK_12P288MHZ) + regmap_update_bits(tavil->wcd9xxx->regmap, + WCD934X_CODEC_RPM_CLK_MCLK_CFG, + 0x03, 0x00); + else if (tavil->wcd9xxx->mclk_rate == WCD934X_MCLK_CLK_9P6MHZ) + regmap_update_bits(tavil->wcd9xxx->regmap, + WCD934X_CODEC_RPM_CLK_MCLK_CFG, + 0x03, 0x01); + tavil_update_reg_defaults(tavil); + __tavil_enable_efuse_sensing(tavil); + ___tavil_get_codec_fine_version(tavil); + tavil_update_cpr_defaults(tavil); + + /* Register with soc framework */ + if (tavil->intf_type == WCD9XXX_INTERFACE_TYPE_I2C) + ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_tavil, + tavil_i2s_dai, + ARRAY_SIZE(tavil_i2s_dai)); + else + ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_tavil, + tavil_slim_dai, + ARRAY_SIZE(tavil_slim_dai)); + + if (ret) { + dev_err(&pdev->dev, "%s: Codec registration failed\n", + __func__); + goto err_cdc_reg; + } + schedule_work(&tavil->tavil_add_child_devices_work); + + ret = snd_event_client_register(pdev->dev.parent, &wcd934x_ssr_ops, NULL); + if (!ret) { + snd_event_notify(pdev->dev.parent, SND_EVENT_UP); + } else { + pr_err("%s: Registration with SND event fwk failed ret = %d\n", + __func__, ret); + ret = 0; + } + + tavil->micb_load = NULL; + + if (of_get_property(tavil->wcd9xxx->dev->of_node, + "qcom,vreg-micb-supply", NULL)) { + micb_prop = of_get_property(tavil->wcd9xxx->dev->of_node, + "qcom,cdc-vdd-mic-bias-current", + &len); + if (!micb_prop || (len != (2 * sizeof(__be32)))) { + tavil->micb_load_low = MICB_LOAD_DEFAULT; + tavil->micb_load_high = MICB_LOAD_DEFAULT; + } else { + tavil->micb_load_low = be32_to_cpup(&micb_prop[0]); + tavil->micb_load_high = be32_to_cpup(&micb_prop[1]); + } + tavil->micb_load = regulator_get(&pdev->dev, MICB_LOAD_PROP); + if (IS_ERR(tavil->micb_load)) + dev_dbg(tavil->dev, "%s micb load get failed\n", + __func__); + } + + return ret; + +err_cdc_reg: + clk_put(tavil->wcd_ext_clk); +err_clk: + wcd_resmgr_remove(tavil->resmgr); +err_resmgr: + mutex_destroy(&tavil->micb_lock); + mutex_destroy(&tavil->svs_mutex); + mutex_destroy(&tavil->codec_mutex); + mutex_destroy(&tavil->swr.read_mutex); + mutex_destroy(&tavil->swr.write_mutex); + mutex_destroy(&tavil->swr.clk_mutex); + devm_kfree(&pdev->dev, tavil); + + return ret; +} + +static int tavil_remove(struct platform_device *pdev) +{ + struct tavil_priv *tavil; + int count = 0; + + tavil = platform_get_drvdata(pdev); + if (!tavil) + return -EINVAL; + + /* do dsd deinit before codec->component->regmap becomes freed */ + if (tavil->dsd_config) { + tavil_dsd_deinit(tavil->dsd_config); + tavil->dsd_config = NULL; + } + + snd_event_client_deregister(pdev->dev.parent); + + if (tavil->spi) + spi_unregister_device(tavil->spi); + for (count = 0; count < tavil->child_count && + count < WCD934X_CHILD_DEVICES_MAX; count++) + platform_device_unregister(tavil->pdev_child_devices[count]); + + if (tavil->micb_load) + regulator_put(tavil->micb_load); + + mutex_destroy(&tavil->micb_lock); + mutex_destroy(&tavil->svs_mutex); + mutex_destroy(&tavil->codec_mutex); + mutex_destroy(&tavil->swr.read_mutex); + mutex_destroy(&tavil->swr.write_mutex); + mutex_destroy(&tavil->swr.clk_mutex); + + snd_soc_unregister_codec(&pdev->dev); + clk_put(tavil->wcd_ext_clk); + wcd_resmgr_remove(tavil->resmgr); + devm_kfree(&pdev->dev, tavil); + return 0; +} + +static struct platform_driver tavil_codec_driver = { + .probe = tavil_probe, + .remove = tavil_remove, + .driver = { + .name = "tavil_codec", + .owner = THIS_MODULE, +#ifdef CONFIG_PM + .pm = &tavil_pm_ops, +#endif + }, +}; + +module_platform_driver(tavil_codec_driver); + +MODULE_DESCRIPTION("Tavil Codec driver"); +MODULE_LICENSE("GPL v2"); diff --git a/audio-kernel/asoc/codecs/wcd934x/wcd934x.h b/audio-kernel/asoc/codecs/wcd934x/wcd934x.h new file mode 100644 index 000000000..0f2e2a396 --- /dev/null +++ b/audio-kernel/asoc/codecs/wcd934x/wcd934x.h @@ -0,0 +1,232 @@ +/* + * Copyright (c) 2015-2019, 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 WCD934X_H +#define WCD934X_H + +#include +#include "wcd934x-dsp-cntl.h" +#include "../wcd9xxx-slimslave.h" +#include "../wcd9xxx-common-v2.h" +#include "../wcd-mbhc-v2.h" + +#define WCD934X_REGISTER_START_OFFSET 0x800 +#define WCD934X_SB_PGD_PORT_RX_BASE 0x40 +#define WCD934X_SB_PGD_PORT_TX_BASE 0x50 +#define WCD934X_RX_PORT_START_NUMBER 16 + +#define WCD934X_DMIC_CLK_DIV_2 0x0 +#define WCD934X_DMIC_CLK_DIV_3 0x1 +#define WCD934X_DMIC_CLK_DIV_4 0x2 +#define WCD934X_DMIC_CLK_DIV_6 0x3 +#define WCD934X_DMIC_CLK_DIV_8 0x4 +#define WCD934X_DMIC_CLK_DIV_16 0x5 +#define WCD934X_DMIC_CLK_DRIVE_DEFAULT 0x02 + +#define WCD934X_ANC_DMIC_X2_FULL_RATE 1 +#define WCD934X_ANC_DMIC_X2_HALF_RATE 0 + +#define TAVIL_MAX_MICBIAS 4 +#define TAVIL_NUM_INTERPOLATORS 9 +#define MAX_ON_DEMAND_SUPPLY_NAME_LENGTH 64 + +/* Convert from vout ctl to micbias voltage in mV */ +#define WCD_VOUT_CTL_TO_MICB(v) (1000 + v * 50) + +/* Feature masks to distinguish codec version */ +#define DSD_DISABLED_MASK 0 +#define SLNQ_DISABLED_MASK 1 + +#define DSD_DISABLED (1 << DSD_DISABLED_MASK) +#define SLNQ_DISABLED (1 << SLNQ_DISABLED_MASK) + +/* Number of input and output Slimbus port */ +enum { + WCD934X_RX0 = 0, + WCD934X_RX1, + WCD934X_RX2, + WCD934X_RX3, + WCD934X_RX4, + WCD934X_RX5, + WCD934X_RX6, + WCD934X_RX7, + WCD934X_RX_MAX, +}; + +enum { + WCD934X_TX0 = 0, + WCD934X_TX1, + WCD934X_TX2, + WCD934X_TX3, + WCD934X_TX4, + WCD934X_TX5, + WCD934X_TX6, + WCD934X_TX7, + WCD934X_TX8, + WCD934X_TX9, + WCD934X_TX10, + WCD934X_TX11, + WCD934X_TX12, + WCD934X_TX13, + WCD934X_TX14, + WCD934X_TX15, + WCD934X_TX_MAX, +}; + +enum { + INTERP_EAR = 0, + INTERP_HPHL, + INTERP_HPHR, + INTERP_LO1, + INTERP_LO2, + INTERP_LO3_NA, /* LO3 not avalible in Tavil*/ + INTERP_LO4_NA, + INTERP_SPKR1, + INTERP_SPKR2, + INTERP_MAX, +}; + +/* WCD934X slimbus slave port error status */ +enum { + SB_PORT_ERR_OF, /* SB port overflow */ + SB_PORT_ERR_UF, /* SB port underflow */ + SB_PORT_ERR_MAX, +}; + +/* + * Selects compander and smart boost settings + * for a given speaker mode + */ +enum { + WCD934X_SPKR_MODE_DEFAULT, + WCD934X_SPKR_MODE_1, /* COMP Gain = 12dB, Smartboost Max = 5.5V */ +}; + +/* + * Rx path gain offsets + */ +enum { + WCD934X_RX_GAIN_OFFSET_M1P5_DB, + WCD934X_RX_GAIN_OFFSET_0_DB, +}; + +/* + * Dai data structure holds the + * dai specific info like rate, + * channel number etc. + */ +struct tavil_codec_dai_data { + u32 rate; + u32 *ch_num; + u32 ch_act; + u32 ch_tot; +}; + +/* + * Structure used to update codec + * register defaults after reset + */ +struct tavil_reg_mask_val { + u16 reg; + u8 mask; + u8 val; +}; + +#if IS_ENABLED(CONFIG_SND_SOC_WCD934X) +extern void *tavil_get_afe_config(struct snd_soc_codec *codec, + enum afe_config_type config_type); +extern int tavil_cdc_mclk_enable(struct snd_soc_codec *codec, bool enable); +extern int tavil_cdc_mclk_tx_enable(struct snd_soc_codec *codec, bool enable); +extern int tavil_set_spkr_mode(struct snd_soc_codec *codec, int mode); +extern int tavil_set_spkr_gain_offset(struct snd_soc_codec *codec, int offset); +extern struct wcd_dsp_cntl *tavil_get_wcd_dsp_cntl(struct device *dev); +extern int wcd934x_get_micb_vout_ctl_val(u32 micb_mv); +extern int tavil_micbias_control(struct snd_soc_codec *codec, + int micb_num, + int req, bool is_dapm); +extern int tavil_mbhc_micb_adjust_voltage(struct snd_soc_codec *codec, + int req_volt, + int micb_num); +extern struct wcd934x_mbhc *tavil_soc_get_mbhc(struct snd_soc_codec *codec); +extern int tavil_codec_enable_interp_clk(struct snd_soc_codec *codec, + int event, int intp_idx); +extern struct tavil_dsd_config *tavil_get_dsd_config( + struct snd_soc_codec *codec); +extern int tavil_codec_info_create_codec_entry( + struct snd_info_entry *codec_root, + struct snd_soc_codec *codec); +#else +extern void *tavil_get_afe_config(struct snd_soc_codec *codec, + enum afe_config_type config_type) +{ + return NULL; +} +extern int tavil_cdc_mclk_enable(struct snd_soc_codec *codec, bool enable) +{ + return 0; +} +extern int tavil_cdc_mclk_tx_enable(struct snd_soc_codec *codec, bool enable) +{ + return 0; +} +extern int tavil_set_spkr_mode(struct snd_soc_codec *codec, int mode) +{ + return 0; +} +extern int tavil_set_spkr_gain_offset(struct snd_soc_codec *codec, int offset) +{ + return 0; +} +extern struct wcd_dsp_cntl *tavil_get_wcd_dsp_cntl(struct device *dev) +{ + return NULL; +} +extern int wcd934x_get_micb_vout_ctl_val(u32 micb_mv) +{ + return 0; +} +extern int tavil_micbias_control(struct snd_soc_codec *codec, + int micb_num, + int req, bool is_dapm) +{ + return 0; +} +extern int tavil_mbhc_micb_adjust_voltage(struct snd_soc_codec *codec, + int req_volt, + int micb_num) +{ + return 0; +} +extern struct wcd934x_mbhc *tavil_soc_get_mbhc(struct snd_soc_codec *codec) +{ + return NULL; +} +extern int tavil_codec_enable_interp_clk(struct snd_soc_codec *codec, + int event, int intp_idx) +{ + return 0; +} +extern struct tavil_dsd_config *tavil_get_dsd_config( + struct snd_soc_codec *codec) +{ + return NULL; +} +extern int tavil_codec_info_create_codec_entry( + struct snd_info_entry *codec_root, + struct snd_soc_codec *codec) +{ + return 0; +} + +#endif + +#endif diff --git a/audio-kernel/asoc/codecs/wcd934x/wcd934x_irq.h b/audio-kernel/asoc/codecs/wcd934x/wcd934x_irq.h new file mode 100644 index 000000000..1a18be376 --- /dev/null +++ b/audio-kernel/asoc/codecs/wcd934x/wcd934x_irq.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 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 + * 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 __WCD934X_IRQ_H_ +#define __WCD934X_IRQ_H_ + +enum { + /* INTR_REG 0 */ + WCD934X_IRQ_MISC = 1, + WCD934X_IRQ_HPH_PA_OCPL_FAULT, + WCD934X_IRQ_HPH_PA_OCPR_FAULT, + WCD934X_IRQ_EAR_PA_OCP_FAULT, + WCD934X_IRQ_HPH_PA_CNPL_COMPLETE, + WCD934X_IRQ_HPH_PA_CNPR_COMPLETE, + WCD934X_IRQ_EAR_PA_CNP_COMPLETE, + /* INTR_REG 1 */ + WCD934X_IRQ_MBHC_SW_DET, + WCD934X_IRQ_MBHC_ELECT_INS_REM_DET, + WCD934X_IRQ_MBHC_BUTTON_PRESS_DET, + WCD934X_IRQ_MBHC_BUTTON_RELEASE_DET, + WCD934X_IRQ_MBHC_ELECT_INS_REM_LEG_DET, + WCD934X_IRQ_RESERVED_0, + WCD934X_IRQ_RESERVED_1, + WCD934X_IRQ_RESERVED_2, + /* INTR_REG 2 */ + WCD934X_IRQ_LINE_PA1_CNP_COMPLETE, + WCD934X_IRQ_LINE_PA2_CNP_COMPLETE, + WCD934X_IRQ_SLNQ_ANALOG_ERROR, + WCD934X_IRQ_RESERVED_3, + WCD934X_IRQ_SOUNDWIRE, + WCD934X_IRQ_VDD_DIG_RAMP_COMPLETE, + WCD934X_IRQ_RCO_ERROR, + WCD934X_IRQ_CPE_ERROR, + /* INTR_REG 3 */ + WCD934X_IRQ_MAD_AUDIO, + WCD934X_IRQ_MAD_BEACON, + WCD934X_IRQ_MAD_ULTRASOUND, + WCD934X_IRQ_VBAT_ATTACK, + WCD934X_IRQ_VBAT_RESTORE, + WCD934X_IRQ_CPE1_INTR, + WCD934X_IRQ_RESERVED_4, + WCD934X_IRQ_SLNQ_DIGITAL, + WCD934X_NUM_IRQS, +}; + +#endif diff --git a/audio-kernel/asoc/codecs/wcd9360/Android.mk b/audio-kernel/asoc/codecs/wcd9360/Android.mk new file mode 100644 index 000000000..492061265 --- /dev/null +++ b/audio-kernel/asoc/codecs/wcd9360/Android.mk @@ -0,0 +1,50 @@ +# Android makefile for audio kernel modules + +# Assume no targets will be supported + +# Check if this driver needs be built for current target +ifeq ($(call is-board-platform,msmnile),true) +AUDIO_SELECT := CONFIG_SND_SOC_SM8150=m +endif + +AUDIO_CHIPSET := audio +# Build/Package only in case of supported target +ifeq ($(call is-board-platform-in-list,msmnile),true) + +LOCAL_PATH := $(call my-dir) + +# This makefile is only for DLKM +ifneq ($(findstring vendor,$(LOCAL_PATH)),) + +ifneq ($(findstring opensource,$(LOCAL_PATH)),) + AUDIO_BLD_DIR := $(shell pwd)/vendor/qcom/opensource/audio-kernel +endif # opensource + +DLKM_DIR := $(TOP)/device/qcom/common/dlkm + +# Build audio.ko as $(AUDIO_CHIPSET)_audio.ko +########################################################### +# This is set once per LOCAL_PATH, not per (kernel) module +KBUILD_OPTIONS := AUDIO_ROOT=$(AUDIO_BLD_DIR) + +# We are actually building audio.ko here, as per the +# requirement we are specifying _audio.ko as LOCAL_MODULE. +# This means we need to rename the module to _audio.ko +# after audio.ko is built. +KBUILD_OPTIONS += MODNAME=wcd9360_dlkm +KBUILD_OPTIONS += BOARD_PLATFORM=$(TARGET_BOARD_PLATFORM) +KBUILD_OPTIONS += $(AUDIO_SELECT) + +########################################################### +include $(CLEAR_VARS) +LOCAL_MODULE := $(AUDIO_CHIPSET)_wcd9360.ko +LOCAL_MODULE_KBUILD_NAME := wcd9360_dlkm.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_DEBUG_ENABLE := true +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk +########################################################### +########################################################### + +endif # DLKM check +endif # supported target check diff --git a/audio-kernel/asoc/codecs/wcd9360/Kbuild b/audio-kernel/asoc/codecs/wcd9360/Kbuild new file mode 100644 index 000000000..8753c005b --- /dev/null +++ b/audio-kernel/asoc/codecs/wcd9360/Kbuild @@ -0,0 +1,114 @@ +# We can build either as part of a standalone Kernel build or as +# an external module. Determine which mechanism is being used +ifeq ($(MODNAME),) + KERNEL_BUILD := 1 +else + KERNEL_BUILD := 0 +endif + + + +ifeq ($(KERNEL_BUILD), 1) + # These are configurable via Kconfig for kernel-based builds + # Need to explicitly configure for Android-based builds + AUDIO_BLD_DIR := $(shell pwd)/kernel/msm-4.14 + AUDIO_ROOT := $(AUDIO_BLD_DIR)/techpack/audio +endif + +ifeq ($(KERNEL_BUILD), 0) + ifeq ($(CONFIG_ARCH_SM8150), y) + include $(AUDIO_ROOT)/config/sm8150auto.conf + export + INCS += -include $(AUDIO_ROOT)/config/sm8150autoconf.h + endif + + ifeq ($(CONFIG_ARCH_SDMSHRIKE), y) + include $(AUDIO_ROOT)/config/sm8150auto.conf + export + INCS += -include $(AUDIO_ROOT)/config/sm8150autoconf.h + endif +endif + +# As per target team, build is done as follows: +# Defconfig : build with default flags +# Slub : defconfig + CONFIG_SLUB_DEBUG := y + +# CONFIG_SLUB_DEBUG_ON := y + CONFIG_PAGE_POISONING := y +# Perf : Using appropriate msmXXXX-perf_defconfig +# +# Shipment builds (user variants) should not have any debug feature +# enabled. This is identified using 'TARGET_BUILD_VARIANT'. Slub builds +# are identified using the CONFIG_SLUB_DEBUG_ON configuration. Since +# there is no other way to identify defconfig builds, QTI internal +# representation of perf builds (identified using the string 'perf'), +# is used to identify if the build is a slub or defconfig one. This +# way no critical debug feature will be enabled for perf and shipment +# builds. Other OEMs are also protected using the TARGET_BUILD_VARIANT +# config. + +############ UAPI ############ +UAPI_DIR := uapi +UAPI_INC := -I$(AUDIO_ROOT)/include/$(UAPI_DIR) + +############ COMMON ############ +COMMON_DIR := include +COMMON_INC := -I$(AUDIO_ROOT)/$(COMMON_DIR) + +############ WCD934X ############ + +# for WCD9360 Codec +ifdef CONFIG_SND_SOC_WCD9360 + WCD9360_OBJS += wcd9360.o + WCD9360_OBJS += wcd9360-dsp-cntl.o +endif + +LINUX_INC += -Iinclude/linux + +INCS += $(COMMON_INC) \ + $(UAPI_INC) + +EXTRA_CFLAGS += $(INCS) + + +CDEFINES += -DANI_LITTLE_BYTE_ENDIAN \ + -DANI_LITTLE_BIT_ENDIAN \ + -DDOT11F_LITTLE_ENDIAN_HOST \ + -DANI_COMPILER_TYPE_GCC \ + -DANI_OS_TYPE_ANDROID=6 \ + -DPTT_SOCK_SVC_ENABLE \ + -Wall\ + -Werror\ + -D__linux__ + +KBUILD_CPPFLAGS += $(CDEFINES) + +# Currently, for versions of gcc which support it, the kernel Makefile +# is disabling the maybe-uninitialized warning. Re-enable it for the +# AUDIO driver. Note that we must use EXTRA_CFLAGS here so that it +# will override the kernel settings. +ifeq ($(call cc-option-yn, -Wmaybe-uninitialized),y) +EXTRA_CFLAGS += -Wmaybe-uninitialized +endif +#EXTRA_CFLAGS += -Wmissing-prototypes + +ifeq ($(call cc-option-yn, -Wheader-guard),y) +EXTRA_CFLAGS += -Wheader-guard +endif + +ifeq ($(KERNEL_BUILD), 0) +KBUILD_EXTRA_SYMBOLS +=$(OUT)/obj/vendor/qcom/opensource/audio-kernel/ipc/Module.symvers +KBUILD_EXTRA_SYMBOLS +=$(OUT)/obj/vendor/qcom/opensource/audio-kernel/dsp/Module.symvers +KBUILD_EXTRA_SYMBOLS +=$(OUT)/obj/vendor/qcom/opensource/audio-kernel/asoc/Module.symvers +KBUILD_EXTRA_SYMBOLS +=$(OUT)/obj/vendor/qcom/opensource/audio-kernel/asoc/codecs/Module.symvers +KBUILD_EXTRA_SYMBOLS +=$(OUT)/obj/vendor/qcom/opensource/audio-kernel/soc/Module.symvers +endif + +ifeq ($(CONFIG_SND_SOC_GCOV), y) +GCOV_PROFILE := y +endif + +# Module information used by KBuild framework +obj-$(CONFIG_SND_SOC_WCD9360) += wcd9360_dlkm.o +wcd9360_dlkm-y := $(WCD9360_OBJS) + +# inject some build related information +DEFINES += -DBUILD_TIMESTAMP=\"$(shell date -u +'%Y-%m-%dT%H:%M:%SZ')\" diff --git a/audio-kernel/asoc/codecs/wcd9360/wcd9360-defaults.h b/audio-kernel/asoc/codecs/wcd9360/wcd9360-defaults.h new file mode 100644 index 000000000..5557ddafa --- /dev/null +++ b/audio-kernel/asoc/codecs/wcd9360/wcd9360-defaults.h @@ -0,0 +1,2231 @@ +/* + * 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 __WCD9360_DEFAULTS_H__ +#define __WCD9360_DEFAULTS_H__ + +#include +#include + +#define WCD9360_REG(reg) ((reg) & 0xFF) + +static const struct reg_default wcd9360_defaults[] = { +{WCD9360_PAGE0_PAGE_REGISTER, 0x00}, +{WCD9360_CODEC_RPM_CLK_BYPASS, 0x00}, +{WCD9360_CODEC_RPM_CLK_GATE, 0x1F}, +{WCD9360_CODEC_RPM_CLK_MCLK_CFG, 0x00}, +{WCD9360_CODEC_RPM_CLK_MCLK2_CFG, 0x02}, +{WCD9360_CODEC_RPM_I2S_DSD_CLK_SEL, 0x00}, +{WCD9360_CODEC_RPM_RST_CTL, 0x00}, +{WCD9360_CODEC_RPM_PWR_CDC_DIG_HM_CTL, 0x04}, +{WCD9360_CHIP_TIER_CTRL_CHIP_ID_BYTE0, 0x10}, +{WCD9360_CHIP_TIER_CTRL_CHIP_ID_BYTE1, 0x60}, +{WCD9360_CHIP_TIER_CTRL_CHIP_ID_BYTE2, 0x93}, +{WCD9360_CHIP_TIER_CTRL_CHIP_ID_BYTE3, 0x01}, +{WCD9360_CHIP_TIER_CTRL_EFUSE_CTL, 0x10}, +{WCD9360_CHIP_TIER_CTRL_EFUSE_TEST0, 0x00}, +{WCD9360_CHIP_TIER_CTRL_EFUSE_TEST1, 0x00}, +{WCD9360_CHIP_TIER_CTRL_EFUSE_STATUS, 0x00}, +{WCD9360_CHIP_TIER_CTRL_I2C_SLAVE_ID_NONNEGO, 0x0D}, +{WCD9360_CHIP_TIER_CTRL_I2C_SLAVE_ID_1, 0x00}, +{WCD9360_CHIP_TIER_CTRL_I2C_SLAVE_ID_2, 0x00}, +{WCD9360_CHIP_TIER_CTRL_I2C_SLAVE_ID_3, 0x00}, +{WCD9360_CHIP_TIER_CTRL_ANA_WAIT_STATE_CTL, 0x44}, +{WCD9360_CHIP_TIER_CTRL_I2C_ACTIVE, 0x00}, +{WCD9360_CHIP_TIER_CTRL_ALT_FUNC_EN, 0x00}, +{WCD9360_CHIP_TIER_CTRL_GPIO_CTL_OE, 0x00}, +{WCD9360_CHIP_TIER_CTRL_GPIO_CTL_DATA, 0x00}, +{WCD9360_DATA_HUB_RX0_CFG, 0x00}, +{WCD9360_DATA_HUB_RX1_CFG, 0x00}, +{WCD9360_DATA_HUB_RX2_CFG, 0x00}, +{WCD9360_DATA_HUB_RX3_CFG, 0x00}, +{WCD9360_DATA_HUB_RX4_CFG, 0x00}, +{WCD9360_DATA_HUB_RX5_CFG, 0x00}, +{WCD9360_DATA_HUB_RX6_CFG, 0x00}, +{WCD9360_DATA_HUB_RX7_CFG, 0x00}, +{WCD9360_DATA_HUB_SB_TX0_INP_CFG, 0x00}, +{WCD9360_DATA_HUB_SB_TX1_INP_CFG, 0x00}, +{WCD9360_DATA_HUB_SB_TX2_INP_CFG, 0x00}, +{WCD9360_DATA_HUB_SB_TX3_INP_CFG, 0x00}, +{WCD9360_DATA_HUB_SB_TX4_INP_CFG, 0x00}, +{WCD9360_DATA_HUB_SB_TX5_INP_CFG, 0x00}, +{WCD9360_DATA_HUB_SB_TX6_INP_CFG, 0x00}, +{WCD9360_DATA_HUB_SB_TX7_INP_CFG, 0x00}, +{WCD9360_DATA_HUB_SB_TX8_INP_CFG, 0x00}, +{WCD9360_DATA_HUB_SB_TX9_INP_CFG, 0x00}, +{WCD9360_DATA_HUB_SB_TX10_INP_CFG, 0x00}, +{WCD9360_DATA_HUB_SB_TX11_INP_CFG, 0x00}, +{WCD9360_DATA_HUB_SB_TX12_INP_CFG, 0x00}, +{WCD9360_DATA_HUB_SB_TX13_INP_CFG, 0x00}, +{WCD9360_DATA_HUB_SB_TX14_INP_CFG, 0x00}, +{WCD9360_DATA_HUB_SB_TX15_INP_CFG, 0x00}, +{WCD9360_DATA_HUB_I2S_TX0_CFG, 0x00}, +{WCD9360_DATA_HUB_I2S_TX0_CFG2, 0x00}, +{WCD9360_DATA_HUB_I2S_TX1_0_CFG, 0x00}, +{WCD9360_DATA_HUB_I2S_TX1_1_CFG, 0x00}, +{WCD9360_DATA_HUB_DATA_HUB_CFG, 0x03}, +{WCD9360_DATA_HUB_I2S_0_CTL, 0x0C}, +{WCD9360_DATA_HUB_I2S_1_CTL, 0x0C}, +{WCD9360_DATA_HUB_I2S_0_CTL2, 0x01}, +{WCD9360_DATA_HUB_I2S_1_CTL2, 0x01}, +{WCD9360_DATA_HUB_I2S_CLKSRC_CTL, 0x00}, +{WCD9360_DATA_HUB_I2S_COMMON_CTL, 0x00}, +{WCD9360_DATA_HUB_I2S_0_TDM_CTL, 0x00}, +{WCD9360_DATA_HUB_I2S_0_TDM_CTL2, 0x05}, +{WCD9360_DATA_HUB_I2S_0_TDM_CH_RX, 0x00}, +{WCD9360_DATA_HUB_I2S_0_TDM_CH_TX, 0x00}, +{WCD9360_DATA_HUB_I2S_0_TDM_CFG, 0x00}, +{WCD9360_DATA_HUB_I2S_0_TDM_STRETCH, 0x00}, +{WCD9360_DATA_HUB_I2S_RESET_CTL, 0x00}, +{WCD9360_DMA_RDMA_CTL_0, 0x00}, +{WCD9360_DMA_RDMA_CTL_1, 0x00}, +{WCD9360_DMA_RDMA_CTL_2, 0x00}, +{WCD9360_DMA_RDMA_CTL_3, 0x00}, +{WCD9360_DMA_RDMA_CTL_4, 0x00}, +{WCD9360_DMA_CH_2_3_CFG_RDMA_0, 0xFF}, +{WCD9360_DMA_CH_2_3_CFG_RDMA_1, 0xFF}, +{WCD9360_DMA_CH_2_3_CFG_RDMA_2, 0xFF}, +{WCD9360_DMA_CH_2_3_CFG_RDMA_3, 0xFF}, +{WCD9360_DMA_CH_2_3_CFG_RDMA_4, 0xFF}, +{WCD9360_DMA_CH_0_1_CFG_RDMA_0, 0xFF}, +{WCD9360_DMA_CH_0_1_CFG_RDMA_1, 0xFF}, +{WCD9360_DMA_CH_0_1_CFG_RDMA_2, 0xFF}, +{WCD9360_DMA_CH_0_1_CFG_RDMA_3, 0xFF}, +{WCD9360_DMA_CH_0_1_CFG_RDMA_4, 0xFF}, +{WCD9360_DMA_RDMA4_PRT_CFG, 0x00}, +{WCD9360_DMA_RDMA_SBTX0_7_CFG, 0x00}, +{WCD9360_DMA_RDMA_SBTX8_10_CFG, 0x00}, +{WCD9360_DMA_WDMA_CTL_0, 0x00}, +{WCD9360_DMA_WDMA_CTL_1, 0x00}, +{WCD9360_DMA_WDMA_CTL_2, 0x00}, +{WCD9360_DMA_WDMA_CTL_3, 0x00}, +{WCD9360_DMA_WDMA_CTL_4, 0x00}, +{WCD9360_DMA_CH_4_5_CFG_WDMA_0, 0x00}, +{WCD9360_DMA_CH_4_5_CFG_WDMA_1, 0x00}, +{WCD9360_DMA_CH_4_5_CFG_WDMA_2, 0x00}, +{WCD9360_DMA_CH_4_5_CFG_WDMA_3, 0x00}, +{WCD9360_DMA_CH_4_5_CFG_WDMA_4, 0x00}, +{WCD9360_DMA_CH_2_3_CFG_WDMA_0, 0x00}, +{WCD9360_DMA_CH_2_3_CFG_WDMA_1, 0x00}, +{WCD9360_DMA_CH_2_3_CFG_WDMA_2, 0x00}, +{WCD9360_DMA_CH_2_3_CFG_WDMA_3, 0x00}, +{WCD9360_DMA_CH_2_3_CFG_WDMA_4, 0x00}, +{WCD9360_DMA_CH_0_1_CFG_WDMA_0, 0x00}, +{WCD9360_DMA_CH_0_1_CFG_WDMA_1, 0x00}, +{WCD9360_DMA_CH_0_1_CFG_WDMA_2, 0x00}, +{WCD9360_DMA_CH_0_1_CFG_WDMA_3, 0x00}, +{WCD9360_DMA_CH_0_1_CFG_WDMA_4, 0x00}, +{WCD9360_DMA_WDMA0_PRT_CFG, 0x00}, +{WCD9360_DMA_WDMA3_PRT_CFG, 0x00}, +{WCD9360_DMA_WDMA4_PRT0_3_CFG, 0x00}, +{WCD9360_DMA_WDMA4_PRT4_7_CFG, 0x00}, +{WCD9360_PAGE1_PAGE_REGISTER, 0x00}, +{WCD9360_CPE_FLL_USER_CTL_0, 0x71}, +{WCD9360_CPE_FLL_USER_CTL_1, 0x34}, +{WCD9360_CPE_FLL_USER_CTL_2, 0x0B}, +{WCD9360_CPE_FLL_USER_CTL_3, 0x02}, +{WCD9360_CPE_FLL_USER_CTL_4, 0x04}, +{WCD9360_CPE_FLL_USER_CTL_5, 0x02}, +{WCD9360_CPE_FLL_USER_CTL_6, 0x6E}, +{WCD9360_CPE_FLL_USER_CTL_7, 0x00}, +{WCD9360_CPE_FLL_USER_CTL_8, 0x94}, +{WCD9360_CPE_FLL_USER_CTL_9, 0x50}, +{WCD9360_CPE_FLL_L_VAL_CTL_0, 0x53}, +{WCD9360_CPE_FLL_L_VAL_CTL_1, 0x00}, +{WCD9360_CPE_FLL_DSM_FRAC_CTL_0, 0x00}, +{WCD9360_CPE_FLL_DSM_FRAC_CTL_1, 0xFF}, +{WCD9360_CPE_FLL_CONFIG_CTL_0, 0x6B}, +{WCD9360_CPE_FLL_CONFIG_CTL_1, 0x05}, +{WCD9360_CPE_FLL_CONFIG_CTL_2, 0x20}, +{WCD9360_CPE_FLL_CONFIG_CTL_3, 0x00}, +{WCD9360_CPE_FLL_CONFIG_CTL_4, 0x10}, +{WCD9360_CPE_FLL_TEST_CTL_0, 0x80}, +{WCD9360_CPE_FLL_TEST_CTL_1, 0x00}, +{WCD9360_CPE_FLL_TEST_CTL_2, 0x00}, +{WCD9360_CPE_FLL_TEST_CTL_3, 0x00}, +{WCD9360_CPE_FLL_TEST_CTL_4, 0x00}, +{WCD9360_CPE_FLL_TEST_CTL_5, 0x00}, +{WCD9360_CPE_FLL_TEST_CTL_6, 0x04}, +{WCD9360_CPE_FLL_TEST_CTL_7, 0x33}, +{WCD9360_CPE_FLL_FREQ_CTL_0, 0x00}, +{WCD9360_CPE_FLL_FREQ_CTL_1, 0x00}, +{WCD9360_CPE_FLL_FREQ_CTL_2, 0x00}, +{WCD9360_CPE_FLL_FREQ_CTL_3, 0x00}, +{WCD9360_CPE_FLL_SSC_CTL_0, 0x00}, +{WCD9360_CPE_FLL_SSC_CTL_1, 0x00}, +{WCD9360_CPE_FLL_SSC_CTL_2, 0x00}, +{WCD9360_CPE_FLL_SSC_CTL_3, 0x00}, +{WCD9360_CPE_FLL_FLL_MODE, 0x20}, +{WCD9360_CPE_FLL_STATUS_0, 0x00}, +{WCD9360_CPE_FLL_STATUS_1, 0x00}, +{WCD9360_CPE_FLL_STATUS_2, 0x00}, +{WCD9360_CPE_FLL_STATUS_3, 0x00}, +{WCD9360_I2S_FLL_USER_CTL_0, 0x41}, +{WCD9360_I2S_FLL_USER_CTL_1, 0x94}, +{WCD9360_I2S_FLL_USER_CTL_2, 0x08}, +{WCD9360_I2S_FLL_USER_CTL_3, 0x02}, +{WCD9360_I2S_FLL_USER_CTL_4, 0x04}, +{WCD9360_I2S_FLL_USER_CTL_5, 0x02}, +{WCD9360_I2S_FLL_USER_CTL_6, 0x40}, +{WCD9360_I2S_FLL_USER_CTL_7, 0x00}, +{WCD9360_I2S_FLL_USER_CTL_8, 0x5F}, +{WCD9360_I2S_FLL_USER_CTL_9, 0x02}, +{WCD9360_I2S_FLL_L_VAL_CTL_0, 0x40}, +{WCD9360_I2S_FLL_L_VAL_CTL_1, 0x00}, +{WCD9360_I2S_FLL_DSM_FRAC_CTL_0, 0x00}, +{WCD9360_I2S_FLL_DSM_FRAC_CTL_1, 0xFF}, +{WCD9360_I2S_FLL_CONFIG_CTL_0, 0x6B}, +{WCD9360_I2S_FLL_CONFIG_CTL_1, 0x05}, +{WCD9360_I2S_FLL_CONFIG_CTL_2, 0x20}, +{WCD9360_I2S_FLL_CONFIG_CTL_3, 0x00}, +{WCD9360_I2S_FLL_CONFIG_CTL_4, 0x30}, +{WCD9360_I2S_FLL_TEST_CTL_0, 0x80}, +{WCD9360_I2S_FLL_TEST_CTL_1, 0x00}, +{WCD9360_I2S_FLL_TEST_CTL_2, 0x00}, +{WCD9360_I2S_FLL_TEST_CTL_3, 0x00}, +{WCD9360_I2S_FLL_TEST_CTL_4, 0x00}, +{WCD9360_I2S_FLL_TEST_CTL_5, 0x00}, +{WCD9360_I2S_FLL_TEST_CTL_6, 0x04}, +{WCD9360_I2S_FLL_TEST_CTL_7, 0xFF}, +{WCD9360_I2S_FLL_FREQ_CTL_0, 0x00}, +{WCD9360_I2S_FLL_FREQ_CTL_1, 0x00}, +{WCD9360_I2S_FLL_FREQ_CTL_2, 0x00}, +{WCD9360_I2S_FLL_FREQ_CTL_3, 0x00}, +{WCD9360_I2S_FLL_SSC_CTL_0, 0x00}, +{WCD9360_I2S_FLL_SSC_CTL_1, 0x00}, +{WCD9360_I2S_FLL_SSC_CTL_2, 0x00}, +{WCD9360_I2S_FLL_SSC_CTL_3, 0x00}, +{WCD9360_I2S_FLL_FLL_MODE, 0x00}, +{WCD9360_I2S_FLL_STATUS_0, 0x00}, +{WCD9360_I2S_FLL_STATUS_1, 0x00}, +{WCD9360_I2S_FLL_STATUS_2, 0x00}, +{WCD9360_I2S_FLL_STATUS_3, 0x00}, +{WCD9360_PAGE2_PAGE_REGISTER, 0x00}, +{WCD9360_CPE_SS_CPE_CTL, 0x05}, +{WCD9360_CPE_SS_PWR_SYS_PSTATE_CTL_0, 0x01}, +{WCD9360_CPE_SS_PWR_SYS_PSTATE_CTL_1, 0x00}, +{WCD9360_CPE_SS_PWR_CPEFLL_CTL, 0x02}, +{WCD9360_CPE_SS_PWR_CPE_SYSMEM_DEEPSLP_0, 0xFF}, +{WCD9360_CPE_SS_PWR_CPE_SYSMEM_DEEPSLP_1, 0x03}, +{WCD9360_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_0, 0xFF}, +{WCD9360_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_1, 0xFF}, +{WCD9360_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_2, 0xFF}, +{WCD9360_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_3, 0xFF}, +{WCD9360_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_4, 0xFF}, +{WCD9360_CPE_SS_PWR_CPE_DRAM1_SHUTDOWN, 0x07}, +{WCD9360_CPE_SS_US_BUF_INT_PERIOD, 0x5F}, +{WCD9360_CPE_SS_CPARMAD_BUFRDY_INT_PERIOD, 0x13}, +{WCD9360_CPE_SS_SVA_CFG, 0x41}, +{WCD9360_CPE_SS_US_CFG, 0x00}, +{WCD9360_CPE_SS_MAD_CTL, 0x00}, +{WCD9360_CPE_SS_CPAR_CTL, 0x00}, +{WCD9360_CPE_SS_DMIC0_CTL, 0x00}, +{WCD9360_CPE_SS_DMIC1_CTL, 0x00}, +{WCD9360_CPE_SS_DMIC2_CTL, 0x00}, +{WCD9360_CPE_SS_DMIC_CFG, 0x80}, +{WCD9360_CPE_SS_CPAR_CFG, 0x00}, +{WCD9360_CPE_SS_WDOG_CFG, 0x01}, +{WCD9360_CPE_SS_BACKUP_INT, 0x00}, +{WCD9360_CPE_SS_STATUS, 0x00}, +{WCD9360_CPE_SS_CPE_OCD_CFG, 0x00}, +{WCD9360_CPE_SS_SS_ERROR_INT_MASK_0A, 0xFF}, +{WCD9360_CPE_SS_SS_ERROR_INT_MASK_0B, 0x3F}, +{WCD9360_CPE_SS_SS_ERROR_INT_MASK_1A, 0xFF}, +{WCD9360_CPE_SS_SS_ERROR_INT_MASK_1B, 0x3F}, +{WCD9360_CPE_SS_SS_ERROR_INT_STATUS_0A, 0x00}, +{WCD9360_CPE_SS_SS_ERROR_INT_STATUS_0B, 0x00}, +{WCD9360_CPE_SS_SS_ERROR_INT_STATUS_1A, 0x00}, +{WCD9360_CPE_SS_SS_ERROR_INT_STATUS_1B, 0x00}, +{WCD9360_CPE_SS_SS_ERROR_INT_CLEAR_0A, 0x00}, +{WCD9360_CPE_SS_SS_ERROR_INT_CLEAR_0B, 0x00}, +{WCD9360_CPE_SS_SS_ERROR_INT_CLEAR_1A, 0x00}, +{WCD9360_CPE_SS_SS_ERROR_INT_CLEAR_1B, 0x00}, +{WCD9360_CPE_SS_DMIC3_CTL, 0x00}, +{WCD9360_CPE_SS_WDOG_RESET, 0x00}, +{WCD9360_CPE_SS_LPASS_MCLK_PRG, 0x00}, +{WCD9360_CPE_SS_LPASS_IPC_IN_0, 0x00}, +{WCD9360_CPE_SS_LPASS_IPC_IN_1, 0x00}, +{WCD9360_CPE_SS_LPASS_IPC_IN_2, 0x00}, +{WCD9360_CPE_SS_LPASS_IPC_IN_3, 0x00}, +{WCD9360_CPE_SS_LPASS_IPC_IN_4, 0x00}, +{WCD9360_CPE_SS_LPASS_IPC_IN_5, 0x00}, +{WCD9360_CPE_SS_LPASS_IPC_IN_6, 0x00}, +{WCD9360_CPE_SS_LPASS_IPC_IN_7, 0x00}, +{WCD9360_CPE_SS_LPASS_IPC_IN_8, 0x00}, +{WCD9360_CPE_SS_LPASS_IPC_IN_9, 0x00}, +{WCD9360_CPE_SS_LPASS_IPC_IN_10, 0x00}, +{WCD9360_CPE_SS_LPASS_IPC_IN_11, 0x00}, +{WCD9360_CPE_SS_LPASS_IPC_IN_12, 0x00}, +{WCD9360_CPE_SS_LPASS_IPC_IN_13, 0x00}, +{WCD9360_CPE_SS_LPASS_IPC_IN_14, 0x00}, +{WCD9360_CPE_SS_LPASS_IPC_IN_15, 0x00}, +{WCD9360_CPE_SS_LPASS_IPC_OUT_0, 0x00}, +{WCD9360_CPE_SS_LPASS_IPC_OUT_1, 0x00}, +{WCD9360_CPE_SS_LPASS_IPC_OUT_2, 0x00}, +{WCD9360_CPE_SS_LPASS_IPC_OUT_3, 0x00}, +{WCD9360_CPE_SS_LPASS_IPC_OUT_4, 0x00}, +{WCD9360_CPE_SS_LPASS_IPC_OUT_5, 0x00}, +{WCD9360_CPE_SS_LPASS_IPC_OUT_6, 0x00}, +{WCD9360_CPE_SS_LPASS_IPC_OUT_7, 0x00}, +{WCD9360_CPE_SS_LPASS_IPC_OUT_8, 0x00}, +{WCD9360_CPE_SS_LPASS_IPC_OUT_9, 0x00}, +{WCD9360_CPE_SS_LPASS_IPC_OUT_10, 0x00}, +{WCD9360_CPE_SS_LPASS_IPC_OUT_11, 0x00}, +{WCD9360_CPE_SS_LPASS_IPC_OUT_12, 0x00}, +{WCD9360_CPE_SS_LPASS_IPC_OUT_13, 0x00}, +{WCD9360_CPE_SS_LPASS_IPC_OUT_14, 0x00}, +{WCD9360_CPE_SS_LPASS_IPC_OUT_15, 0x00}, +{WCD9360_CPE_SS_LPASS_ARB_CTL, 0x00}, +{WCD9360_CPE_SS_MEM_DEEPSLEEP_RD_0, 0x00}, +{WCD9360_CPE_SS_MEM_DEEPSLEEP_RD_1, 0x00}, +{WCD9360_CPE_SS_MEM_DEEPSLEEP_BYPASS_0, 0x00}, +{WCD9360_CPE_SS_MEM_DEEPSLEEP_BYPASS_1, 0x00}, +{WCD9360_SOC_MAD_MAIN_CTL_1, 0x00}, +{WCD9360_SOC_MAD_MAIN_CTL_2, 0x00}, +{WCD9360_SOC_MAD_AUDIO_CTL_1, 0x00}, +{WCD9360_SOC_MAD_AUDIO_CTL_2, 0x00}, +{WCD9360_SOC_MAD_AUDIO_CTL_3, 0x00}, +{WCD9360_SOC_MAD_AUDIO_CTL_4, 0x00}, +{WCD9360_SOC_MAD_AUDIO_CTL_5, 0x00}, +{WCD9360_SOC_MAD_AUDIO_CTL_6, 0x00}, +{WCD9360_SOC_MAD_AUDIO_CTL_7, 0x00}, +{WCD9360_SOC_MAD_AUDIO_CTL_8, 0x00}, +{WCD9360_SOC_MAD_AUDIO_IIR_CTL_PTR, 0x00}, +{WCD9360_SOC_MAD_AUDIO_IIR_CTL_VAL, 0x40}, +{WCD9360_SOC_MAD_ULTR_CTL_1, 0x00}, +{WCD9360_SOC_MAD_ULTR_CTL_2, 0x00}, +{WCD9360_SOC_MAD_ULTR_CTL_3, 0x00}, +{WCD9360_SOC_MAD_ULTR_CTL_4, 0x00}, +{WCD9360_SOC_MAD_ULTR_CTL_5, 0x00}, +{WCD9360_SOC_MAD_ULTR_CTL_6, 0x00}, +{WCD9360_SOC_MAD_ULTR_CTL_7, 0x00}, +{WCD9360_SOC_MAD_BEACON_CTL_1, 0x00}, +{WCD9360_SOC_MAD_BEACON_CTL_2, 0x00}, +{WCD9360_SOC_MAD_BEACON_CTL_3, 0x00}, +{WCD9360_SOC_MAD_BEACON_CTL_4, 0x00}, +{WCD9360_SOC_MAD_BEACON_CTL_5, 0x00}, +{WCD9360_SOC_MAD_BEACON_CTL_6, 0x00}, +{WCD9360_SOC_MAD_BEACON_CTL_7, 0x00}, +{WCD9360_SOC_MAD_BEACON_CTL_8, 0x00}, +{WCD9360_SOC_MAD_BEACON_IIR_CTL_PTR, 0x00}, +{WCD9360_SOC_MAD_BEACON_IIR_CTL_VAL, 0x00}, +{WCD9360_SOC_MAD_INP_SEL, 0x00}, +{WCD9360_SOC_MAD_MAD2_INP_SEL, 0x00}, +{WCD9360_SWR_SAMPLE_PACK_SWR_SAMPLE_PACK_CTRL, 0x00}, +{WCD9360_SWR_SAMPLE_PACK_SWR_SAMPLE_PACK_STATUS, 0x00}, +{WCD9360_SWR_SAMPLE_PACK_SWR_SAMPLE_PACK_FS, 0x00}, +{WCD9360_SWR_SAMPLE_PACK_SWR_SAMPLE_PACK_IN_SEL, 0x00}, +{WCD9360_EFUSE_VALUE_EFUSE_VAL_OUT0, 0x00}, +{WCD9360_EFUSE_VALUE_EFUSE_VAL_OUT1, 0x00}, +{WCD9360_EFUSE_VALUE_EFUSE_VAL_OUT2, 0x00}, +{WCD9360_EFUSE_VALUE_EFUSE_VAL_OUT3, 0x00}, +{WCD9360_EFUSE_VALUE_EFUSE_VAL_OUT4, 0x00}, +{WCD9360_EFUSE_VALUE_EFUSE_VAL_OUT5, 0x00}, +{WCD9360_EFUSE_VALUE_EFUSE_VAL_OUT6, 0x00}, +{WCD9360_EFUSE_VALUE_EFUSE_VAL_OUT7, 0x00}, +{WCD9360_EFUSE_VALUE_EFUSE_VAL_OUT8, 0x00}, +{WCD9360_EFUSE_VALUE_EFUSE_VAL_OUT9, 0x00}, +{WCD9360_EFUSE_VALUE_EFUSE_VAL_OUT10, 0x00}, +{WCD9360_EFUSE_VALUE_EFUSE_VAL_OUT11, 0x00}, +{WCD9360_EFUSE_VALUE_EFUSE_VAL_OUT12, 0x00}, +{WCD9360_EFUSE_VALUE_EFUSE_VAL_OUT13, 0x00}, +{WCD9360_EFUSE_VALUE_EFUSE_VAL_OUT14, 0x00}, +{WCD9360_EFUSE_VALUE_EFUSE_VAL_OUT15, 0x00}, +{WCD9360_EFUSE_VALUE_EFUSE2_VAL_OUT0, 0x00}, +{WCD9360_EFUSE_VALUE_EFUSE2_VAL_OUT1, 0x00}, +{WCD9360_EFUSE_VALUE_EFUSE2_VAL_OUT2, 0x00}, +{WCD9360_EFUSE_VALUE_EFUSE2_VAL_OUT3, 0x00}, +{WCD9360_EFUSE_VALUE_EFUSE2_VAL_OUT4, 0x00}, +{WCD9360_EFUSE_VALUE_EFUSE2_VAL_OUT5, 0x00}, +{WCD9360_EFUSE_VALUE_EFUSE2_VAL_OUT6, 0x00}, +{WCD9360_EFUSE_VALUE_EFUSE2_VAL_OUT7, 0x00}, +{WCD9360_EFUSE_VALUE_EFUSE2_VAL_OUT8, 0x00}, +{WCD9360_EFUSE_VALUE_EFUSE2_VAL_OUT9, 0x00}, +{WCD9360_EFUSE_VALUE_EFUSE2_VAL_OUT10, 0x00}, +{WCD9360_EFUSE_VALUE_EFUSE2_VAL_OUT11, 0x00}, +{WCD9360_EFUSE_VALUE_EFUSE2_VAL_OUT12, 0x00}, +{WCD9360_EFUSE_VALUE_EFUSE2_VAL_OUT13, 0x00}, +{WCD9360_EFUSE_VALUE_EFUSE2_VAL_OUT14, 0x00}, +{WCD9360_EFUSE_VALUE_EFUSE2_VAL_OUT15, 0x00}, +{WCD9360_PAGE4_PAGE_REGISTER, 0x00}, +{WCD9360_INTR_CFG, 0x00}, +{WCD9360_INTR_CLR_COMMIT, 0x00}, +{WCD9360_INTR_PIN1_MASK0, 0xFF}, +{WCD9360_INTR_PIN1_MASK1, 0xFF}, +{WCD9360_INTR_PIN1_MASK2, 0xFF}, +{WCD9360_INTR_PIN1_MASK3, 0xFF}, +{WCD9360_INTR_PIN1_STATUS0, 0x00}, +{WCD9360_INTR_PIN1_STATUS1, 0x00}, +{WCD9360_INTR_PIN1_STATUS2, 0x00}, +{WCD9360_INTR_PIN1_STATUS3, 0x00}, +{WCD9360_INTR_PIN1_CLEAR0, 0x00}, +{WCD9360_INTR_PIN1_CLEAR1, 0x00}, +{WCD9360_INTR_PIN1_CLEAR2, 0x00}, +{WCD9360_INTR_PIN1_CLEAR3, 0x00}, +{WCD9360_INTR_PIN2_MASK3, 0xFF}, +{WCD9360_INTR_PIN2_STATUS3, 0x00}, +{WCD9360_INTR_PIN2_CLEAR3, 0x00}, +{WCD9360_INTR_CPESS_SUMRY_MASK2, 0xFF}, +{WCD9360_INTR_CPESS_SUMRY_MASK3, 0xFF}, +{WCD9360_INTR_CPESS_SUMRY_STATUS2, 0x00}, +{WCD9360_INTR_CPESS_SUMRY_STATUS3, 0x00}, +{WCD9360_INTR_CPESS_SUMRY_CLEAR2, 0x00}, +{WCD9360_INTR_CPESS_SUMRY_CLEAR3, 0x00}, +{WCD9360_INTR_LEVEL0, 0x3B}, +{WCD9360_INTR_LEVEL1, 0x00}, +{WCD9360_INTR_LEVEL2, 0xD0}, +{WCD9360_INTR_LEVEL3, 0x00}, +{WCD9360_INTR_BYPASS0, 0x00}, +{WCD9360_INTR_BYPASS1, 0x00}, +{WCD9360_INTR_BYPASS2, 0x00}, +{WCD9360_INTR_BYPASS3, 0x00}, +{WCD9360_INTR_SET0, 0x00}, +{WCD9360_INTR_SET1, 0x00}, +{WCD9360_INTR_SET2, 0x00}, +{WCD9360_INTR_SET3, 0x00}, +{WCD9360_INTR_CODEC_MISC_MASK, 0x73}, +{WCD9360_INTR_CODEC_MISC_STATUS, 0x00}, +{WCD9360_INTR_CODEC_MISC_CLEAR, 0x00}, +{WCD9360_ANA_PAGE_REGISTER, 0x00}, +{WCD9360_ANA_BIAS, 0x00}, +{WCD9360_ANA_RCO, 0x00}, +{WCD9360_ANA_BUCK_CTL, 0x00}, +{WCD9360_ANA_BUCK_STATUS, 0x00}, +{WCD9360_ANA_EAR, 0x00}, +{WCD9360_ANA_MAD_SETUP, 0x01}, +{WCD9360_ANA_AMIC1, 0x20}, +{WCD9360_ANA_AMIC2, 0x00}, +{WCD9360_ANA_AMIC3, 0x20}, +{WCD9360_ANA_AMIC4, 0x00}, +{WCD9360_ANA_MICB1, 0x10}, +{WCD9360_ANA_MICB2, 0x10}, +{WCD9360_ANA_MICB3, 0x10}, +{WCD9360_ANA_MICB4, 0x10}, +{WCD9360_BIAS_CTL, 0x2A}, +{WCD9360_BIAS_VBG_FINE_ADJ, 0x55}, +{WCD9360_RCO_CTRL_1, 0x44}, +{WCD9360_RCO_CTRL_2, 0x48}, +{WCD9360_RCO_CAL, 0x00}, +{WCD9360_RCO_CAL_1, 0x00}, +{WCD9360_RCO_CAL_2, 0x00}, +{WCD9360_RCO_TEST_CTRL, 0x00}, +{WCD9360_RCO_CAL_OUT_1, 0x00}, +{WCD9360_RCO_CAL_OUT_2, 0x00}, +{WCD9360_RCO_CAL_OUT_3, 0x00}, +{WCD9360_RCO_CAL_OUT_4, 0x00}, +{WCD9360_RCO_CAL_OUT_5, 0x00}, +{WCD9360_SIDO_MODE_1, 0x84}, +{WCD9360_SIDO_MODE_2, 0xFE}, +{WCD9360_SIDO_MODE_3, 0xF6}, +{WCD9360_SIDO_MODE_4, 0x56}, +{WCD9360_SIDO_VCL_1, 0x00}, +{WCD9360_SIDO_VCL_2, 0x6C}, +{WCD9360_SIDO_VCL_3, 0x44}, +{WCD9360_SIDO_CCL_1, 0x57}, +{WCD9360_SIDO_CCL_2, 0x92}, +{WCD9360_SIDO_CCL_3, 0x35}, +{WCD9360_SIDO_CCL_4, 0x61}, +{WCD9360_SIDO_CCL_5, 0x6D}, +{WCD9360_SIDO_CCL_6, 0x60}, +{WCD9360_SIDO_CCL_7, 0x6F}, +{WCD9360_SIDO_CCL_8, 0x6F}, +{WCD9360_SIDO_CCL_9, 0x6E}, +{WCD9360_SIDO_CCL_10, 0x26}, +{WCD9360_SIDO_FILTER_1, 0x92}, +{WCD9360_SIDO_FILTER_2, 0x54}, +{WCD9360_SIDO_DRIVER_1, 0x77}, +{WCD9360_SIDO_DRIVER_2, 0x55}, +{WCD9360_SIDO_DRIVER_3, 0x55}, +{WCD9360_SIDO_CAL_CODE_EXT_1, 0x9C}, +{WCD9360_SIDO_CAL_CODE_EXT_2, 0x82}, +{WCD9360_SIDO_CAL_CODE_OUT_1, 0x00}, +{WCD9360_SIDO_CAL_CODE_OUT_2, 0x00}, +{WCD9360_SIDO_TEST_1, 0x00}, +{WCD9360_SIDO_TEST_2, 0x00}, +{WCD9360_LDOH_MODE, 0x2B}, +{WCD9360_LDOH_BIAS, 0x68}, +{WCD9360_LDOH_STB_LOADS, 0x00}, +{WCD9360_LDOH_SLOWRAMP, 0x50}, +{WCD9360_MICB1_TEST_CTL_1, 0x1A}, +{WCD9360_MICB1_TEST_CTL_2, 0x18}, +{WCD9360_MICB1_TEST_CTL_3, 0xA4}, +{WCD9360_MICB2_TEST_CTL_1, 0x1A}, +{WCD9360_MICB2_TEST_CTL_2, 0x18}, +{WCD9360_MICB2_TEST_CTL_3, 0xA4}, +{WCD9360_MICB3_TEST_CTL_1, 0x1A}, +{WCD9360_MICB3_TEST_CTL_2, 0x18}, +{WCD9360_MICB3_TEST_CTL_3, 0xA4}, +{WCD9360_MICB4_TEST_CTL_1, 0x1A}, +{WCD9360_MICB4_TEST_CTL_2, 0x18}, +{WCD9360_MICB4_TEST_CTL_3, 0xA4}, +{WCD9360_TX_COM_ADC_VCM, 0x39}, +{WCD9360_TX_COM_BIAS_ATEST, 0xC0}, +{WCD9360_TX_COM_ADC_INT1_IB, 0x6F}, +{WCD9360_TX_COM_ADC_INT2_IB, 0x4F}, +{WCD9360_TX_COM_TXFE_DIV_CTL, 0x2E}, +{WCD9360_TX_COM_TXFE_DIV_START, 0x00}, +{WCD9360_TX_COM_TXFE_DIV_STOP_9P6M, 0xC7}, +{WCD9360_TX_COM_TXFE_DIV_STOP_12P288M, 0xFF}, +{WCD9360_TX_1_2_TEST_EN, 0xCC}, +{WCD9360_TX_1_2_ADC_IB, 0x09}, +{WCD9360_TX_1_2_ATEST_REFCTL, 0x0A}, +{WCD9360_TX_1_2_TEST_CTL, 0x38}, +{WCD9360_TX_1_2_TEST_BLK_EN, 0xFF}, +{WCD9360_TX_1_2_TXFE_CLKDIV, 0x00}, +{WCD9360_TX_1_2_SAR1_ERR, 0x00}, +{WCD9360_TX_1_2_SAR2_ERR, 0x00}, +{WCD9360_TX_3_4_TEST_EN, 0xCC}, +{WCD9360_TX_3_4_ADC_IB, 0x09}, +{WCD9360_TX_3_4_ATEST_REFCTL, 0x0A}, +{WCD9360_TX_3_4_TEST_CTL, 0x38}, +{WCD9360_TX_3_4_TEST_BLK_EN, 0xFF}, +{WCD9360_TX_3_4_TXFE_CLKDIV, 0x00}, +{WCD9360_TX_3_4_SAR1_ERR, 0x00}, +{WCD9360_TX_3_4_SAR2_ERR, 0x00}, +{WCD9360_RX_RX_EAR_BIAS_CON_1, 0x2A}, +{WCD9360_RX_RX_EAR_BIAS_CON_2, 0x8A}, +{WCD9360_RX_RX_AUX_BIAS_CON_1, 0x2A}, +{WCD9360_RX_RX_AUX_BIAS_CON_2, 0x8A}, +{WCD9360_RX_RX_BIAS_ATEST, 0x0C}, +{WCD9360_RX_RXTOP_RESERVED, 0x00}, +{WCD9360_EAR_EAR_EN_REG, 0x22}, +{WCD9360_EAR_EAR_PA_CON, 0x44}, +{WCD9360_EAR_EAR_SP_CON, 0xDB}, +{WCD9360_EAR_EAR_DAC_CON, 0x00}, +{WCD9360_EAR_EAR_CNP_FSM_CON, 0xB6}, +{WCD9360_EAR_DAC_CTL_TEST, 0x00}, +{WCD9360_EAR_STATUS_REG, 0x00}, +{WCD9360_EAR_EAR_COMPANDER_CON, 0x02}, +{WCD9360_ANA_NEW_PAGE_REGISTER, 0x00}, +{WCD9360_CLK_SYS_PLL_ENABLES, 0x00}, +{WCD9360_CLK_SYS_PLL_PRESET, 0x00}, +{WCD9360_CLK_SYS_PLL_STATUS, 0x00}, +{WCD9360_CLK_SYS_MCLK_PRG, 0x10}, +{WCD9360_CLK_SYS_MCLK2_PRG1, 0x00}, +{WCD9360_CLK_SYS_MCLK_MISC, 0x00}, +{WCD9360_SIDO_NEW_VOUT_A_STARTUP, 0x17}, +{WCD9360_SIDO_NEW_VOUT_D_STARTUP, 0x0D}, +{WCD9360_SIDO_NEW_VOUT_D_FREQ1, 0x07}, +{WCD9360_SIDO_NEW_VOUT_D_FREQ2, 0x00}, +{WCD9360_AUX_ANA_EAR, 0x00}, +{WCD9360_LDORXTX_LDORXTX, 0x10}, +{WCD9360_DIE_CRACK_CTL, 0x00}, +{WCD9360_DIE_CRACK_OUT, 0x00}, +{WCD9360_LOOP_BACK_EN, 0x00}, +{WCD9360_CLK_SYS_INT_POST_DIV_REG0, 0x00}, +{WCD9360_CLK_SYS_INT_POST_DIV_REG1, 0x00}, +{WCD9360_CLK_SYS_INT_REF_DIV_REG0, 0x00}, +{WCD9360_CLK_SYS_INT_REF_DIV_REG1, 0x00}, +{WCD9360_CLK_SYS_INT_FILTER_REG0, 0x00}, +{WCD9360_CLK_SYS_INT_FILTER_REG1, 0x00}, +{WCD9360_CLK_SYS_INT_PLL_L_VAL, 0x00}, +{WCD9360_CLK_SYS_INT_PLL_M_VAL, 0x00}, +{WCD9360_CLK_SYS_INT_PLL_N_VAL, 0x00}, +{WCD9360_CLK_SYS_INT_TEST_REG0, 0x00}, +{WCD9360_CLK_SYS_INT_PFD_CP_DSM_PROG, 0x00}, +{WCD9360_CLK_SYS_INT_VCO_PROG, 0x00}, +{WCD9360_CLK_SYS_INT_TEST_REG1, 0x00}, +{WCD9360_CLK_SYS_INT_LDO_LOCK_CFG, 0x00}, +{WCD9360_CLK_SYS_INT_DIG_LOCK_DET_CFG, 0x00}, +{WCD9360_CLK_SYS_INT_CLK_TEST1, 0x00}, +{WCD9360_CLK_SYS_INT_CLK_TEST2, 0x00}, +{WCD9360_CLK_SYS_INT_CLK_TEST3, 0x00}, +{WCD9360_SIDO_NEW_INT_RAMP_STATUS, 0x00}, +{WCD9360_SIDO_NEW_INT_SPARE_1, 0x00}, +{WCD9360_SIDO_NEW_INT_DEBUG_VOUT_SETTING_A, 0x64}, +{WCD9360_SIDO_NEW_INT_DEBUG_VOUT_SETTING_D, 0x40}, +{WCD9360_SIDO_NEW_INT_RAMP_INC_WAIT, 0x24}, +{WCD9360_SIDO_NEW_INT_DYNAMIC_IPEAK_CTL, 0x09}, +{WCD9360_SIDO_NEW_INT_RAMP_IBLEED_CTL, 0x7D}, +{WCD9360_SIDO_NEW_INT_DEBUG_CPROVR_TEST, 0x00}, +{WCD9360_SIDO_NEW_INT_RAMP_CTL_A, 0x14}, +{WCD9360_SIDO_NEW_INT_RAMP_CTL_D, 0x14}, +{WCD9360_SIDO_NEW_INT_RAMP_TIMEOUT_PERIOD, 0x33}, +{WCD9360_SIDO_NEW_INT_DYNAMIC_IPEAK_SETTING1, 0x3F}, +{WCD9360_SIDO_NEW_INT_DYNAMIC_IPEAK_SETTING2, 0x74}, +{WCD9360_SIDO_NEW_INT_DYNAMIC_IPEAK_SETTING3, 0x33}, +{WCD9360_SIDO_NEW_INT_HIGH_ACCU_MODE_SEL1, 0x1D}, +{WCD9360_SIDO_NEW_INT_HIGH_ACCU_MODE_SEL2, 0x0A}, +{WCD9360_EAR_INT_NEW_EAR_CHOPPER_CON, 0xA8}, +{WCD9360_EAR_INT_NEW_EAR_VCM_GEN_CON1, 0x42}, +{WCD9360_EAR_INT_NEW_EAR_VCM_GEN_CON2, 0x22}, +{WCD9360_EAR_INT_NEW_EAR_DYNAMIC_BIAS, 0x00}, +{WCD9360_AUX_INT_AUX_EN_REG, 0x22}, +{WCD9360_AUX_INT_AUX_PA_CON, 0x46}, +{WCD9360_AUX_INT_AUX_SP_CON, 0xD2}, +{WCD9360_AUX_INT_AUX_DAC_CON, 0x00}, +{WCD9360_AUX_INT_AUX_CNP_FSM_CON, 0xB6}, +{WCD9360_AUX_INT_AUX_TEST, 0x00}, +{WCD9360_AUX_INT_STATUS_REG, 0x00}, +{WCD9360_AUX_INT_AUX_MISC, 0x00}, +{WCD9360_LDORXTX_INT_ANA_LDORXTX_CTRL1, 0xFF}, +{WCD9360_LDORXTX_INT_ANA_LDORXTX_CTRL2, 0xD8}, +{WCD9360_LDORXTX_INT_ANA_LDORXTX_CTRL3, 0x80}, +{WCD9360_LDORXTX_INT_ANA_LDORXTX_CTRL4, 0xFF}, +{WCD9360_LDORXTX_INT_ANA_LDORXTX_CTRL5, 0x09}, +{WCD9360_LDORXTX_INT_ANA_LDORXTX_STATUS, 0x00}, +{WCD9360_DIE_CRACK_INT_INT1, 0x02}, +{WCD9360_DIE_CRACK_INT_INT2, 0x60}, +{WCD9360_LOOP_BACK_INT_SPARE, 0x00}, +{WCD9360_PAGE10_PAGE_REGISTER, 0x00}, +{WCD9360_CDC_ANC0_CLK_RESET_CTL, 0x00}, +{WCD9360_CDC_ANC0_MODE_1_CTL, 0x00}, +{WCD9360_CDC_ANC0_MODE_2_CTL, 0x00}, +{WCD9360_CDC_ANC0_FF_SHIFT, 0x00}, +{WCD9360_CDC_ANC0_FB_SHIFT, 0x00}, +{WCD9360_CDC_ANC0_LPF_FF_A_CTL, 0x00}, +{WCD9360_CDC_ANC0_LPF_FF_B_CTL, 0x00}, +{WCD9360_CDC_ANC0_LPF_FB_CTL, 0x00}, +{WCD9360_CDC_ANC0_SMLPF_CTL, 0x00}, +{WCD9360_CDC_ANC0_DCFLT_SHIFT_CTL, 0x00}, +{WCD9360_CDC_ANC0_IIR_ADAPT_CTL, 0x00}, +{WCD9360_CDC_ANC0_IIR_COEFF_1_CTL, 0x00}, +{WCD9360_CDC_ANC0_IIR_COEFF_2_CTL, 0x00}, +{WCD9360_CDC_ANC0_FF_A_GAIN_CTL, 0x00}, +{WCD9360_CDC_ANC0_FF_B_GAIN_CTL, 0x00}, +{WCD9360_CDC_ANC0_FB_GAIN_CTL, 0x00}, +{WCD9360_CDC_TX0_TX_PATH_CTL, 0x04}, +{WCD9360_CDC_TX0_TX_PATH_CFG0, 0x10}, +{WCD9360_CDC_TX0_TX_PATH_CFG1, 0x03}, +{WCD9360_CDC_TX0_TX_VOL_CTL, 0x00}, +{WCD9360_CDC_TX0_TX_PATH_192_CTL, 0x00}, +{WCD9360_CDC_TX0_TX_PATH_192_CFG, 0x00}, +{WCD9360_CDC_TX0_TX_PATH_SEC0, 0x00}, +{WCD9360_CDC_TX0_TX_PATH_SEC1, 0x00}, +{WCD9360_CDC_TX0_TX_PATH_SEC2, 0x01}, +{WCD9360_CDC_TX0_TX_PATH_SEC3, 0x3C}, +{WCD9360_CDC_TX0_TX_PATH_SEC4, 0x20}, +{WCD9360_CDC_TX0_TX_PATH_SEC5, 0x00}, +{WCD9360_CDC_TX0_TX_PATH_SEC6, 0x00}, +{WCD9360_CDC_TX1_TX_PATH_CTL, 0x04}, +{WCD9360_CDC_TX1_TX_PATH_CFG0, 0x10}, +{WCD9360_CDC_TX1_TX_PATH_CFG1, 0x03}, +{WCD9360_CDC_TX1_TX_VOL_CTL, 0x00}, +{WCD9360_CDC_TX1_TX_PATH_192_CTL, 0x00}, +{WCD9360_CDC_TX1_TX_PATH_192_CFG, 0x00}, +{WCD9360_CDC_TX1_TX_PATH_SEC0, 0x00}, +{WCD9360_CDC_TX1_TX_PATH_SEC1, 0x00}, +{WCD9360_CDC_TX1_TX_PATH_SEC2, 0x01}, +{WCD9360_CDC_TX1_TX_PATH_SEC3, 0x3C}, +{WCD9360_CDC_TX1_TX_PATH_SEC4, 0x20}, +{WCD9360_CDC_TX1_TX_PATH_SEC5, 0x00}, +{WCD9360_CDC_TX1_TX_PATH_SEC6, 0x00}, +{WCD9360_CDC_TX2_TX_PATH_CTL, 0x04}, +{WCD9360_CDC_TX2_TX_PATH_CFG0, 0x10}, +{WCD9360_CDC_TX2_TX_PATH_CFG1, 0x03}, +{WCD9360_CDC_TX2_TX_VOL_CTL, 0x00}, +{WCD9360_CDC_TX2_TX_PATH_192_CTL, 0x00}, +{WCD9360_CDC_TX2_TX_PATH_192_CFG, 0x00}, +{WCD9360_CDC_TX2_TX_PATH_SEC0, 0x00}, +{WCD9360_CDC_TX2_TX_PATH_SEC1, 0x00}, +{WCD9360_CDC_TX2_TX_PATH_SEC2, 0x01}, +{WCD9360_CDC_TX2_TX_PATH_SEC3, 0x3C}, +{WCD9360_CDC_TX2_TX_PATH_SEC4, 0x20}, +{WCD9360_CDC_TX2_TX_PATH_SEC5, 0x00}, +{WCD9360_CDC_TX2_TX_PATH_SEC6, 0x00}, +{WCD9360_CDC_TX3_TX_PATH_CTL, 0x04}, +{WCD9360_CDC_TX3_TX_PATH_CFG0, 0x10}, +{WCD9360_CDC_TX3_TX_PATH_CFG1, 0x03}, +{WCD9360_CDC_TX3_TX_VOL_CTL, 0x00}, +{WCD9360_CDC_TX3_TX_PATH_192_CTL, 0x00}, +{WCD9360_CDC_TX3_TX_PATH_192_CFG, 0x00}, +{WCD9360_CDC_TX3_TX_PATH_SEC0, 0x00}, +{WCD9360_CDC_TX3_TX_PATH_SEC1, 0x00}, +{WCD9360_CDC_TX3_TX_PATH_SEC2, 0x01}, +{WCD9360_CDC_TX3_TX_PATH_SEC3, 0x3C}, +{WCD9360_CDC_TX3_TX_PATH_SEC4, 0x20}, +{WCD9360_CDC_TX3_TX_PATH_SEC5, 0x00}, +{WCD9360_CDC_TX3_TX_PATH_SEC6, 0x00}, +{WCD9360_CDC_TX4_TX_PATH_CTL, 0x04}, +{WCD9360_CDC_TX4_TX_PATH_CFG0, 0x10}, +{WCD9360_CDC_TX4_TX_PATH_CFG1, 0x03}, +{WCD9360_CDC_TX4_TX_VOL_CTL, 0x00}, +{WCD9360_CDC_TX4_TX_PATH_192_CTL, 0x00}, +{WCD9360_CDC_TX4_TX_PATH_192_CFG, 0x00}, +{WCD9360_CDC_TX4_TX_PATH_SEC0, 0x00}, +{WCD9360_CDC_TX4_TX_PATH_SEC1, 0x00}, +{WCD9360_CDC_TX4_TX_PATH_SEC2, 0x01}, +{WCD9360_CDC_TX4_TX_PATH_SEC3, 0x3C}, +{WCD9360_CDC_TX4_TX_PATH_SEC4, 0x20}, +{WCD9360_CDC_TX4_TX_PATH_SEC5, 0x00}, +{WCD9360_CDC_TX4_TX_PATH_SEC6, 0x00}, +{WCD9360_CDC_TX5_TX_PATH_CTL, 0x04}, +{WCD9360_CDC_TX5_TX_PATH_CFG0, 0x10}, +{WCD9360_CDC_TX5_TX_PATH_CFG1, 0x03}, +{WCD9360_CDC_TX5_TX_VOL_CTL, 0x00}, +{WCD9360_CDC_TX5_TX_PATH_192_CTL, 0x00}, +{WCD9360_CDC_TX5_TX_PATH_192_CFG, 0x00}, +{WCD9360_CDC_TX5_TX_PATH_SEC0, 0x00}, +{WCD9360_CDC_TX5_TX_PATH_SEC1, 0x00}, +{WCD9360_CDC_TX5_TX_PATH_SEC2, 0x01}, +{WCD9360_CDC_TX5_TX_PATH_SEC3, 0x3C}, +{WCD9360_CDC_TX5_TX_PATH_SEC4, 0x20}, +{WCD9360_CDC_TX5_TX_PATH_SEC5, 0x00}, +{WCD9360_CDC_TX5_TX_PATH_SEC6, 0x00}, +{WCD9360_CDC_TX6_TX_PATH_CTL, 0x04}, +{WCD9360_CDC_TX6_TX_PATH_CFG0, 0x10}, +{WCD9360_CDC_TX6_TX_PATH_CFG1, 0x03}, +{WCD9360_CDC_TX6_TX_VOL_CTL, 0x00}, +{WCD9360_CDC_TX6_TX_PATH_192_CTL, 0x00}, +{WCD9360_CDC_TX6_TX_PATH_192_CFG, 0x00}, +{WCD9360_CDC_TX6_TX_PATH_SEC0, 0x00}, +{WCD9360_CDC_TX6_TX_PATH_SEC1, 0x00}, +{WCD9360_CDC_TX6_TX_PATH_SEC2, 0x01}, +{WCD9360_CDC_TX6_TX_PATH_SEC3, 0x3C}, +{WCD9360_CDC_TX6_TX_PATH_SEC4, 0x20}, +{WCD9360_CDC_TX6_TX_PATH_SEC5, 0x00}, +{WCD9360_CDC_TX6_TX_PATH_SEC6, 0x00}, +{WCD9360_CDC_TX7_TX_PATH_CTL, 0x04}, +{WCD9360_CDC_TX7_TX_PATH_CFG0, 0x10}, +{WCD9360_CDC_TX7_TX_PATH_CFG1, 0x03}, +{WCD9360_CDC_TX7_TX_VOL_CTL, 0x00}, +{WCD9360_CDC_TX7_TX_PATH_192_CTL, 0x00}, +{WCD9360_CDC_TX7_TX_PATH_192_CFG, 0x00}, +{WCD9360_CDC_TX7_TX_PATH_SEC0, 0x00}, +{WCD9360_CDC_TX7_TX_PATH_SEC1, 0x00}, +{WCD9360_CDC_TX7_TX_PATH_SEC2, 0x01}, +{WCD9360_CDC_TX7_TX_PATH_SEC3, 0x3C}, +{WCD9360_CDC_TX7_TX_PATH_SEC4, 0x20}, +{WCD9360_CDC_TX7_TX_PATH_SEC5, 0x00}, +{WCD9360_CDC_TX7_TX_PATH_SEC6, 0x00}, +{WCD9360_CDC_TX8_TX_PATH_CTL, 0x04}, +{WCD9360_CDC_TX8_TX_PATH_CFG0, 0x10}, +{WCD9360_CDC_TX8_TX_PATH_CFG1, 0x03}, +{WCD9360_CDC_TX8_TX_VOL_CTL, 0x00}, +{WCD9360_CDC_TX8_TX_PATH_192_CTL, 0x00}, +{WCD9360_CDC_TX8_TX_PATH_192_CFG, 0x00}, +{WCD9360_CDC_TX8_TX_PATH_SEC0, 0x00}, +{WCD9360_CDC_TX8_TX_PATH_SEC1, 0x00}, +{WCD9360_CDC_TX8_TX_PATH_SEC2, 0x01}, +{WCD9360_CDC_TX8_TX_PATH_SEC3, 0x3C}, +{WCD9360_CDC_TX8_TX_PATH_SEC4, 0x20}, +{WCD9360_CDC_TX8_TX_PATH_SEC5, 0x00}, +{WCD9360_CDC_TX8_TX_PATH_SEC6, 0x00}, +{WCD9360_CDC_TX9_SPKR_PROT_PATH_CTL, 0x02}, +{WCD9360_CDC_TX9_SPKR_PROT_PATH_CFG0, 0x00}, +{WCD9360_CDC_TX10_SPKR_PROT_PATH_CTL, 0x02}, +{WCD9360_CDC_TX10_SPKR_PROT_PATH_CFG0, 0x00}, +{WCD9360_CDC_TX11_SPKR_PROT_PATH_CTL, 0x02}, +{WCD9360_CDC_TX11_SPKR_PROT_PATH_CFG0, 0x00}, +{WCD9360_CDC_TX12_SPKR_PROT_PATH_CTL, 0x02}, +{WCD9360_CDC_TX12_SPKR_PROT_PATH_CFG0, 0x00}, +{WCD9360_PAGE11_PAGE_REGISTER, 0x00}, +{WCD9360_CDC_COMPANDER0_CTL0, 0x60}, +{WCD9360_CDC_COMPANDER0_CTL1, 0xDB}, +{WCD9360_CDC_COMPANDER0_CTL2, 0xFF}, +{WCD9360_CDC_COMPANDER0_CTL3, 0x35}, +{WCD9360_CDC_COMPANDER0_CTL4, 0xFF}, +{WCD9360_CDC_COMPANDER0_CTL5, 0x00}, +{WCD9360_CDC_COMPANDER0_CTL6, 0x01}, +{WCD9360_CDC_COMPANDER0_CTL7, 0x06}, +{WCD9360_CDC_COMPANDER7_CTL0, 0x60}, +{WCD9360_CDC_COMPANDER7_CTL1, 0xDB}, +{WCD9360_CDC_COMPANDER7_CTL2, 0xFF}, +{WCD9360_CDC_COMPANDER7_CTL3, 0x35}, +{WCD9360_CDC_COMPANDER7_CTL4, 0xFF}, +{WCD9360_CDC_COMPANDER7_CTL5, 0x00}, +{WCD9360_CDC_COMPANDER7_CTL6, 0x01}, +{WCD9360_CDC_COMPANDER7_CTL7, 0x06}, +{WCD9360_CDC_COMPANDER8_CTL0, 0x60}, +{WCD9360_CDC_COMPANDER8_CTL1, 0xDB}, +{WCD9360_CDC_COMPANDER8_CTL2, 0xFF}, +{WCD9360_CDC_COMPANDER8_CTL3, 0x35}, +{WCD9360_CDC_COMPANDER8_CTL4, 0xFF}, +{WCD9360_CDC_COMPANDER8_CTL5, 0x00}, +{WCD9360_CDC_COMPANDER8_CTL6, 0x01}, +{WCD9360_CDC_COMPANDER8_CTL7, 0x06}, +{WCD9360_CDC_RX0_RX_PATH_CTL, 0x04}, +{WCD9360_CDC_RX0_RX_PATH_CFG0, 0x00}, +{WCD9360_CDC_RX0_RX_PATH_CFG1, 0x64}, +{WCD9360_CDC_RX0_RX_PATH_CFG2, 0x80}, +{WCD9360_CDC_RX0_RX_VOL_CTL, 0x00}, +{WCD9360_CDC_RX0_RX_PATH_MIX_CTL, 0x04}, +{WCD9360_CDC_RX0_RX_PATH_MIX_CFG, 0x12}, +{WCD9360_CDC_RX0_RX_VOL_MIX_CTL, 0x00}, +{WCD9360_CDC_RX0_RX_PATH_SEC0, 0xFC}, +{WCD9360_CDC_RX0_RX_PATH_SEC1, 0x08}, +{WCD9360_CDC_RX0_RX_PATH_SEC2, 0x00}, +{WCD9360_CDC_RX0_RX_PATH_SEC3, 0x00}, +{WCD9360_CDC_RX0_RX_PATH_SEC5, 0x00}, +{WCD9360_CDC_RX0_RX_PATH_SEC6, 0x00}, +{WCD9360_CDC_RX0_RX_PATH_SEC7, 0x00}, +{WCD9360_CDC_RX0_RX_PATH_MIX_SEC0, 0x08}, +{WCD9360_CDC_RX0_RX_PATH_MIX_SEC1, 0x00}, +{WCD9360_CDC_RX0_RX_PATH_DSMDEM_CTL, 0x00}, +{WCD9360_CDC_RX9_RX_PATH_CTL, 0x04}, +{WCD9360_CDC_RX9_RX_PATH_CFG0, 0x00}, +{WCD9360_CDC_RX9_RX_PATH_CFG1, 0x64}, +{WCD9360_CDC_RX9_RX_PATH_CFG2, 0x80}, +{WCD9360_CDC_RX9_RX_VOL_CTL, 0x00}, +{WCD9360_CDC_RX9_RX_PATH_MIX_CTL, 0x04}, +{WCD9360_CDC_RX9_RX_PATH_MIX_CFG, 0x12}, +{WCD9360_CDC_RX9_RX_VOL_MIX_CTL, 0x00}, +{WCD9360_CDC_RX9_RX_PATH_SEC0, 0xFC}, +{WCD9360_CDC_RX9_RX_PATH_SEC1, 0x08}, +{WCD9360_CDC_RX9_RX_PATH_SEC2, 0x00}, +{WCD9360_CDC_RX9_RX_PATH_SEC3, 0x00}, +{WCD9360_CDC_RX9_RX_PATH_SEC5, 0x00}, +{WCD9360_CDC_RX9_RX_PATH_SEC6, 0x00}, +{WCD9360_CDC_RX9_RX_PATH_SEC7, 0x00}, +{WCD9360_CDC_RX9_RX_PATH_MIX_SEC0, 0x08}, +{WCD9360_CDC_RX9_RX_PATH_MIX_SEC1, 0x00}, +{WCD9360_CDC_RX9_RX_PATH_DSMDEM_CTL, 0x00}, +{WCD9360_CDC_RX7_RX_PATH_CTL, 0x04}, +{WCD9360_CDC_RX7_RX_PATH_CFG0, 0x00}, +{WCD9360_CDC_RX7_RX_PATH_CFG1, 0x64}, +{WCD9360_CDC_RX7_RX_PATH_CFG2, 0x80}, +{WCD9360_CDC_RX7_RX_VOL_CTL, 0x00}, +{WCD9360_CDC_RX7_RX_PATH_MIX_CTL, 0x04}, +{WCD9360_CDC_RX7_RX_PATH_MIX_CFG, 0x12}, +{WCD9360_CDC_RX7_RX_VOL_MIX_CTL, 0x00}, +{WCD9360_CDC_RX7_RX_PATH_SEC0, 0x04}, +{WCD9360_CDC_RX7_RX_PATH_SEC1, 0x08}, +{WCD9360_CDC_RX7_RX_PATH_SEC2, 0x00}, +{WCD9360_CDC_RX7_RX_PATH_SEC3, 0x00}, +{WCD9360_CDC_RX7_RX_PATH_SEC5, 0x00}, +{WCD9360_CDC_RX7_RX_PATH_SEC6, 0x00}, +{WCD9360_CDC_RX7_RX_PATH_SEC7, 0x00}, +{WCD9360_CDC_RX7_RX_PATH_MIX_SEC0, 0x08}, +{WCD9360_CDC_RX7_RX_PATH_MIX_SEC1, 0x00}, +{WCD9360_CDC_RX7_RX_PATH_DSMDEM_CTL, 0x00}, +{WCD9360_CDC_RX8_RX_PATH_CTL, 0x04}, +{WCD9360_CDC_RX8_RX_PATH_CFG0, 0x00}, +{WCD9360_CDC_RX8_RX_PATH_CFG1, 0x64}, +{WCD9360_CDC_RX8_RX_PATH_CFG2, 0x80}, +{WCD9360_CDC_RX8_RX_VOL_CTL, 0x00}, +{WCD9360_CDC_RX8_RX_PATH_MIX_CTL, 0x04}, +{WCD9360_CDC_RX8_RX_PATH_MIX_CFG, 0x12}, +{WCD9360_CDC_RX8_RX_VOL_MIX_CTL, 0x00}, +{WCD9360_CDC_RX8_RX_PATH_SEC0, 0x04}, +{WCD9360_CDC_RX8_RX_PATH_SEC1, 0x08}, +{WCD9360_CDC_RX8_RX_PATH_SEC2, 0x00}, +{WCD9360_CDC_RX8_RX_PATH_SEC3, 0x00}, +{WCD9360_CDC_RX8_RX_PATH_SEC5, 0x00}, +{WCD9360_CDC_RX8_RX_PATH_SEC6, 0x00}, +{WCD9360_CDC_RX8_RX_PATH_SEC7, 0x00}, +{WCD9360_CDC_RX8_RX_PATH_MIX_SEC0, 0x08}, +{WCD9360_CDC_RX8_RX_PATH_MIX_SEC1, 0x00}, +{WCD9360_CDC_RX8_RX_PATH_DSMDEM_CTL, 0x00}, +{WCD9360_PAGE12_PAGE_REGISTER, 0x00}, +{WCD9360_CDC_BOOST0_BOOST_PATH_CTL, 0x00}, +{WCD9360_CDC_BOOST0_BOOST_CTL, 0xD0}, +{WCD9360_CDC_BOOST0_BOOST_CFG1, 0x89}, +{WCD9360_CDC_BOOST0_BOOST_CFG2, 0x04}, +{WCD9360_CDC_BOOST1_BOOST_PATH_CTL, 0x00}, +{WCD9360_CDC_BOOST1_BOOST_CTL, 0xD0}, +{WCD9360_CDC_BOOST1_BOOST_CFG1, 0x89}, +{WCD9360_CDC_BOOST1_BOOST_CFG2, 0x04}, +{WCD9360_MIXING_ASRC2_CLK_RST_CTL, 0x00}, +{WCD9360_MIXING_ASRC2_CTL0, 0x00}, +{WCD9360_MIXING_ASRC2_CTL1, 0x00}, +{WCD9360_MIXING_ASRC2_FIFO_CTL, 0xA8}, +{WCD9360_MIXING_ASRC2_STATUS_FMIN_CNTR_LSB, 0x00}, +{WCD9360_MIXING_ASRC2_STATUS_FMIN_CNTR_MSB, 0x00}, +{WCD9360_MIXING_ASRC2_STATUS_FMAX_CNTR_LSB, 0x00}, +{WCD9360_MIXING_ASRC2_STATUS_FMAX_CNTR_MSB, 0x00}, +{WCD9360_MIXING_ASRC2_STATUS_FIFO, 0x00}, +{WCD9360_MIXING_ASRC3_CLK_RST_CTL, 0x00}, +{WCD9360_MIXING_ASRC3_CTL0, 0x00}, +{WCD9360_MIXING_ASRC3_CTL1, 0x00}, +{WCD9360_MIXING_ASRC3_FIFO_CTL, 0xA8}, +{WCD9360_MIXING_ASRC3_STATUS_FMIN_CNTR_LSB, 0x00}, +{WCD9360_MIXING_ASRC3_STATUS_FMIN_CNTR_MSB, 0x00}, +{WCD9360_MIXING_ASRC3_STATUS_FMAX_CNTR_LSB, 0x00}, +{WCD9360_MIXING_ASRC3_STATUS_FMAX_CNTR_MSB, 0x00}, +{WCD9360_MIXING_ASRC3_STATUS_FIFO, 0x00}, +{WCD9360_SWR_AHB_BRIDGE_WR_DATA_0, 0x00}, +{WCD9360_SWR_AHB_BRIDGE_WR_DATA_1, 0x00}, +{WCD9360_SWR_AHB_BRIDGE_WR_DATA_2, 0x00}, +{WCD9360_SWR_AHB_BRIDGE_WR_DATA_3, 0x00}, +{WCD9360_SWR_AHB_BRIDGE_WR_ADDR_0, 0x00}, +{WCD9360_SWR_AHB_BRIDGE_WR_ADDR_1, 0x00}, +{WCD9360_SWR_AHB_BRIDGE_WR_ADDR_2, 0x00}, +{WCD9360_SWR_AHB_BRIDGE_WR_ADDR_3, 0x00}, +{WCD9360_SWR_AHB_BRIDGE_RD_ADDR_0, 0x00}, +{WCD9360_SWR_AHB_BRIDGE_RD_ADDR_1, 0x00}, +{WCD9360_SWR_AHB_BRIDGE_RD_ADDR_2, 0x00}, +{WCD9360_SWR_AHB_BRIDGE_RD_ADDR_3, 0x00}, +{WCD9360_SWR_AHB_BRIDGE_RD_DATA_0, 0x00}, +{WCD9360_SWR_AHB_BRIDGE_RD_DATA_1, 0x00}, +{WCD9360_SWR_AHB_BRIDGE_RD_DATA_2, 0x00}, +{WCD9360_SWR_AHB_BRIDGE_RD_DATA_3, 0x00}, +{WCD9360_SWR_AHB_BRIDGE_ACCESS_CFG, 0x0F}, +{WCD9360_SWR_AHB_BRIDGE_ACCESS_STATUS, 0x03}, +{WCD9360_CDC_SIDETONE_SRC0_ST_SRC_PATH_CTL, 0x04}, +{WCD9360_CDC_SIDETONE_SRC0_ST_SRC_PATH_CFG1, 0x00}, +{WCD9360_SIDETONE_ASRC0_CLK_RST_CTL, 0x00}, +{WCD9360_SIDETONE_ASRC0_CTL0, 0x00}, +{WCD9360_SIDETONE_ASRC0_CTL1, 0x00}, +{WCD9360_SIDETONE_ASRC0_FIFO_CTL, 0xA8}, +{WCD9360_SIDETONE_ASRC0_STATUS_FMIN_CNTR_LSB, 0x00}, +{WCD9360_SIDETONE_ASRC0_STATUS_FMIN_CNTR_MSB, 0x00}, +{WCD9360_SIDETONE_ASRC0_STATUS_FMAX_CNTR_LSB, 0x00}, +{WCD9360_SIDETONE_ASRC0_STATUS_FMAX_CNTR_MSB, 0x00}, +{WCD9360_SIDETONE_ASRC0_STATUS_FIFO, 0x00}, +{WCD9360_EC_REF_HQ0_EC_REF_HQ_PATH_CTL, 0x00}, +{WCD9360_EC_REF_HQ0_EC_REF_HQ_CFG0, 0x01}, +{WCD9360_EC_REF_HQ1_EC_REF_HQ_PATH_CTL, 0x00}, +{WCD9360_EC_REF_HQ1_EC_REF_HQ_CFG0, 0x01}, +{WCD9360_EC_ASRC0_CLK_RST_CTL, 0x00}, +{WCD9360_EC_ASRC0_CTL0, 0x00}, +{WCD9360_EC_ASRC0_CTL1, 0x00}, +{WCD9360_EC_ASRC0_FIFO_CTL, 0xA8}, +{WCD9360_EC_ASRC0_STATUS_FMIN_CNTR_LSB, 0x00}, +{WCD9360_EC_ASRC0_STATUS_FMIN_CNTR_MSB, 0x00}, +{WCD9360_EC_ASRC0_STATUS_FMAX_CNTR_LSB, 0x00}, +{WCD9360_EC_ASRC0_STATUS_FMAX_CNTR_MSB, 0x00}, +{WCD9360_EC_ASRC0_STATUS_FIFO, 0x00}, +{WCD9360_EC_ASRC1_CLK_RST_CTL, 0x00}, +{WCD9360_EC_ASRC1_CTL0, 0x00}, +{WCD9360_EC_ASRC1_CTL1, 0x00}, +{WCD9360_EC_ASRC1_FIFO_CTL, 0xA8}, +{WCD9360_EC_ASRC1_STATUS_FMIN_CNTR_LSB, 0x00}, +{WCD9360_EC_ASRC1_STATUS_FMIN_CNTR_MSB, 0x00}, +{WCD9360_EC_ASRC1_STATUS_FMAX_CNTR_LSB, 0x00}, +{WCD9360_EC_ASRC1_STATUS_FMAX_CNTR_MSB, 0x00}, +{WCD9360_EC_ASRC1_STATUS_FIFO, 0x00}, +{WCD9360_PAGE13_PAGE_REGISTER, 0x00}, +{WCD9360_CDC_RX_INP_MUX_RX_INT0_CFG0, 0x00}, +{WCD9360_CDC_RX_INP_MUX_RX_INT0_CFG1, 0x00}, +{WCD9360_CDC_RX_INP_MUX_RX_INT9_CFG0, 0x00}, +{WCD9360_CDC_RX_INP_MUX_RX_INT9_CFG1, 0x00}, +{WCD9360_CDC_RX_INP_MUX_RX_INT7_CFG0, 0x00}, +{WCD9360_CDC_RX_INP_MUX_RX_INT7_CFG1, 0x00}, +{WCD9360_CDC_RX_INP_MUX_RX_INT8_CFG0, 0x00}, +{WCD9360_CDC_RX_INP_MUX_RX_INT8_CFG1, 0x00}, +{WCD9360_CDC_RX_INP_MUX_RX_MIX_CFG0, 0x00}, +{WCD9360_CDC_RX_INP_MUX_RX_MIX_CFG1, 0x00}, +{WCD9360_CDC_RX_INP_MUX_RX_MIX_CFG2, 0x00}, +{WCD9360_CDC_RX_INP_MUX_RX_MIX_CFG3, 0x00}, +{WCD9360_CDC_RX_INP_MUX_RX_MIX_CFG4, 0x00}, +{WCD9360_CDC_RX_INP_MUX_SIDETONE_SRC_CFG0, 0x00}, +{WCD9360_CDC_RX_INP_MUX_SIDETONE_SRC_CFG1, 0x00}, +{WCD9360_CDC_RX_INP_MUX_ANC_CFG0, 0x00}, +{WCD9360_CDC_RX_INP_MUX_SPLINE_ASRC_CFG0, 0x00}, +{WCD9360_CDC_RX_INP_MUX_EC_REF_HQ_CFG0, 0x00}, +{WCD9360_CDC_TX_INP_MUX_ADC_MUX0_CFG0, 0x00}, +{WCD9360_CDC_TX_INP_MUX_ADC_MUX0_CFG1, 0x00}, +{WCD9360_CDC_TX_INP_MUX_ADC_MUX1_CFG0, 0x00}, +{WCD9360_CDC_TX_INP_MUX_ADC_MUX1_CFG1, 0x00}, +{WCD9360_CDC_TX_INP_MUX_ADC_MUX2_CFG0, 0x00}, +{WCD9360_CDC_TX_INP_MUX_ADC_MUX2_CFG1, 0x00}, +{WCD9360_CDC_TX_INP_MUX_ADC_MUX3_CFG0, 0x00}, +{WCD9360_CDC_TX_INP_MUX_ADC_MUX3_CFG1, 0x00}, +{WCD9360_CDC_TX_INP_MUX_ADC_MUX4_CFG0, 0x00}, +{WCD9360_CDC_TX_INP_MUX_ADC_MUX5_CFG0, 0x00}, +{WCD9360_CDC_TX_INP_MUX_ADC_MUX6_CFG0, 0x00}, +{WCD9360_CDC_TX_INP_MUX_ADC_MUX7_CFG0, 0x00}, +{WCD9360_CDC_TX_INP_MUX_ADC_MUX8_CFG0, 0x00}, +{WCD9360_CDC_TX_INP_MUX_ADC_MUX10_CFG0, 0x00}, +{WCD9360_CDC_TX_INP_MUX_ADC_MUX11_CFG0, 0x00}, +{WCD9360_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG0, 0x00}, +{WCD9360_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG1, 0x00}, +{WCD9360_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG2, 0x00}, +{WCD9360_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG3, 0x00}, +{WCD9360_CDC_IF_ROUTER_TX_MUX_CFG0, 0x00}, +{WCD9360_CDC_IF_ROUTER_TX_MUX_CFG1, 0x00}, +{WCD9360_CDC_IF_ROUTER_TX_MUX_CFG2, 0x00}, +{WCD9360_CDC_IF_ROUTER_TX_MUX_CFG3, 0x00}, +{WCD9360_CDC_CLK_RST_CTRL_MCLK_CONTROL, 0x00}, +{WCD9360_CDC_CLK_RST_CTRL_FS_CNT_CONTROL, 0x00}, +{WCD9360_CDC_CLK_RST_CTRL_SWR_CONTROL, 0x00}, +{WCD9360_CDC_CLK_RST_CTRL_ASRC_SHARE_CONTROL, 0x0A}, +{WCD9360_CDC_PROX_DETECT_PROX_CTL, 0x08}, +{WCD9360_CDC_PROX_DETECT_PROX_POLL_PERIOD0, 0x00}, +{WCD9360_CDC_PROX_DETECT_PROX_POLL_PERIOD1, 0x4B}, +{WCD9360_CDC_PROX_DETECT_PROX_SIG_PATTERN_LSB, 0x00}, +{WCD9360_CDC_PROX_DETECT_PROX_SIG_PATTERN_MSB, 0x00}, +{WCD9360_CDC_PROX_DETECT_PROX_STATUS, 0x00}, +{WCD9360_CDC_PROX_DETECT_PROX_TEST_CTRL, 0x00}, +{WCD9360_CDC_PROX_DETECT_PROX_TEST_BUFF_LSB, 0x00}, +{WCD9360_CDC_PROX_DETECT_PROX_TEST_BUFF_MSB, 0x00}, +{WCD9360_CDC_PROX_DETECT_PROX_TEST_BUFF_LSB_RD, 0x00}, +{WCD9360_CDC_PROX_DETECT_PROX_TEST_BUFF_MSB_RD, 0x00}, +{WCD9360_CDC_PROX_DETECT_PROX_CTL_REPEAT_PAT, 0x00}, +{WCD9360_CDC_SIDETONE_IIR0_IIR_PATH_CTL, 0x00}, +{WCD9360_CDC_SIDETONE_IIR0_IIR_GAIN_B1_CTL, 0x00}, +{WCD9360_CDC_SIDETONE_IIR0_IIR_GAIN_B2_CTL, 0x00}, +{WCD9360_CDC_SIDETONE_IIR0_IIR_GAIN_B3_CTL, 0x00}, +{WCD9360_CDC_SIDETONE_IIR0_IIR_GAIN_B4_CTL, 0x00}, +{WCD9360_CDC_SIDETONE_IIR0_IIR_GAIN_B5_CTL, 0x00}, +{WCD9360_CDC_SIDETONE_IIR0_IIR_GAIN_B6_CTL, 0x00}, +{WCD9360_CDC_SIDETONE_IIR0_IIR_GAIN_B7_CTL, 0x00}, +{WCD9360_CDC_SIDETONE_IIR0_IIR_GAIN_B8_CTL, 0x00}, +{WCD9360_CDC_SIDETONE_IIR0_IIR_CTL, 0x40}, +{WCD9360_CDC_SIDETONE_IIR0_IIR_GAIN_TIMER_CTL, 0x00}, +{WCD9360_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL, 0x00}, +{WCD9360_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL, 0x00}, +{WCD9360_CDC_TOP_TOP_CFG0, 0x00}, +{WCD9360_CDC_TOP_TOP_CFG1, 0x00}, +{WCD9360_CDC_TOP_TOP_CFG7, 0x00}, +{WCD9360_CDC_TOP_EAR_COMP_WR_LSB, 0x00}, +{WCD9360_CDC_TOP_EAR_COMP_WR_MSB, 0x00}, +{WCD9360_CDC_TOP_EAR_COMP_LUT, 0x00}, +{WCD9360_CDC_TOP_EAR_COMP_RD_LSB, 0x00}, +{WCD9360_CDC_TOP_EAR_COMP_RD_MSB, 0x00}, +{WCD9360_CDC_TOP_TOP_DEBUG, 0x00}, +{WCD9360_PAGE80_PAGE_REGISTER, 0x00}, +{WCD9360_CODEC_CPR_WR_DATA_0, 0x00}, +{WCD9360_CODEC_CPR_WR_DATA_1, 0x00}, +{WCD9360_CODEC_CPR_WR_DATA_2, 0x00}, +{WCD9360_CODEC_CPR_WR_DATA_3, 0x00}, +{WCD9360_CODEC_CPR_WR_ADDR_0, 0x00}, +{WCD9360_CODEC_CPR_WR_ADDR_1, 0x00}, +{WCD9360_CODEC_CPR_WR_ADDR_2, 0x00}, +{WCD9360_CODEC_CPR_WR_ADDR_3, 0x00}, +{WCD9360_CODEC_CPR_RD_ADDR_0, 0x00}, +{WCD9360_CODEC_CPR_RD_ADDR_1, 0x00}, +{WCD9360_CODEC_CPR_RD_ADDR_2, 0x00}, +{WCD9360_CODEC_CPR_RD_ADDR_3, 0x00}, +{WCD9360_CODEC_CPR_RD_DATA_0, 0x00}, +{WCD9360_CODEC_CPR_RD_DATA_1, 0x00}, +{WCD9360_CODEC_CPR_RD_DATA_2, 0x00}, +{WCD9360_CODEC_CPR_RD_DATA_3, 0x00}, +{WCD9360_CODEC_CPR_ACCESS_CFG, 0x0F}, +{WCD9360_CODEC_CPR_ACCESS_STATUS, 0x03}, +{WCD9360_CODEC_CPR_NOM_CX_VDD, 0xB4}, +{WCD9360_CODEC_CPR_SVS_CX_VDD, 0x7C}, +{WCD9360_CODEC_CPR_SVS2_CX_VDD, 0x58}, +{WCD9360_CODEC_CPR_NOM_MX_VDD, 0xB4}, +{WCD9360_CODEC_CPR_SVS_MX_VDD, 0xB4}, +{WCD9360_CODEC_CPR_SVS2_MX_VDD, 0xA0}, +{WCD9360_CODEC_CPR_SVS2_MIN_CX_VDD, 0x2C}, +{WCD9360_CODEC_CPR_MAX_SVS2_STEP, 0x08}, +{WCD9360_CODEC_CPR_CTL, 0x00}, +{WCD9360_CODEC_CPR_SW_MODECHNG_STATUS, 0x00}, +{WCD9360_CODEC_CPR_SW_MODECHNG_START, 0x00}, +{WCD9360_CODEC_CPR_CPR_STATUS, 0x00}, +{WCD9360_PAGE128_PAGE_REGISTER, 0x00}, +{WCD9360_TLMM_JTCK_PINCFG, 0x00}, +{WCD9360_TLMM_INTR1_PINCFG, 0x00}, +{WCD9360_TLMM_INTR2_PINCFG, 0x00}, +{WCD9360_TLMM_SWR_DATA_PINCFG, 0x00}, +{WCD9360_TLMM_SWR_CLK_PINCFG, 0x00}, +{WCD9360_TLMM_SLIMBUS_DATA1_PINCFG, 0x00}, +{WCD9360_TLMM_SLIMBUS_DATA2_PINCFG, 0x00}, +{WCD9360_TLMM_SLIMBUS_CLK_PINCFG, 0x00}, +{WCD9360_TLMM_I2C_CLK_PINCFG, 0x00}, +{WCD9360_TLMM_I2C_DATA_PINCFG, 0x00}, +{WCD9360_TLMM_I2S_0_RX_PINCFG, 0x00}, +{WCD9360_TLMM_I2S_0_TX_PINCFG, 0x00}, +{WCD9360_TLMM_I2S_0_SCK_PINCFG, 0x00}, +{WCD9360_TLMM_I2S_0_WS_PINCFG, 0x00}, +{WCD9360_TLMM_I2S_1_RX_PINCFG, 0x00}, +{WCD9360_TLMM_I2S_1_TX_PINCFG, 0x00}, +{WCD9360_TLMM_I2S_1_SCK_PINCFG, 0x00}, +{WCD9360_TLMM_I2S_1_WS_PINCFG, 0x00}, +{WCD9360_TLMM_DMIC1_CLK_PINCFG, 0x00}, +{WCD9360_TLMM_DMIC1_DATA_PINCFG, 0x00}, +{WCD9360_TLMM_DMIC2_CLK_PINCFG, 0x00}, +{WCD9360_TLMM_DMIC2_DATA_PINCFG, 0x00}, +{WCD9360_TLMM_GPIO1_PINCFG, 0x00}, +{WCD9360_TLMM_GPIO2_PINCFG, 0x00}, +{WCD9360_TLMM_GPIO3_PINCFG, 0x00}, +{WCD9360_TLMM_GPIO4_PINCFG, 0x00}, +{WCD9360_TLMM_SPI_S_CSN_PINCFG, 0x00}, +{WCD9360_TLMM_SPI_S_CLK_PINCFG, 0x00}, +{WCD9360_TLMM_SPI_S_DOUT_PINCFG, 0x00}, +{WCD9360_TLMM_SPI_S_DIN_PINCFG, 0x00}, +{WCD9360_TLMM_GPIO0_PINCFG, 0x00}, +{WCD9360_TLMM_DMIC3_CLK_PINCFG, 0x00}, +{WCD9360_TLMM_DMIC3_DATA_PINCFG, 0x00}, +{WCD9360_TLMM_DMIC4_CLK_PINCFG, 0x00}, +{WCD9360_TLMM_DMIC4_DATA_PINCFG, 0x00}, +{WCD9360_TEST_DEBUG_PIN_CTL_OE_0, 0x00}, +{WCD9360_TEST_DEBUG_PIN_CTL_OE_1, 0x00}, +{WCD9360_TEST_DEBUG_PIN_CTL_OE_2, 0x00}, +{WCD9360_TEST_DEBUG_PIN_CTL_OE_3, 0x00}, +{WCD9360_TEST_DEBUG_PIN_CTL_OE_4, 0x00}, +{WCD9360_TEST_DEBUG_PIN_CTL_DATA_0, 0x00}, +{WCD9360_TEST_DEBUG_PIN_CTL_DATA_1, 0x00}, +{WCD9360_TEST_DEBUG_PIN_CTL_DATA_2, 0x00}, +{WCD9360_TEST_DEBUG_PIN_CTL_DATA_3, 0x00}, +{WCD9360_TEST_DEBUG_PIN_CTL_DATA_4, 0x00}, +{WCD9360_TEST_DEBUG_PAD_DRVCTL_0, 0x00}, +{WCD9360_TEST_DEBUG_PAD_DRVCTL_1, 0x00}, +{WCD9360_TEST_DEBUG_PIN_STATUS, 0x00}, +{WCD9360_TEST_DEBUG_NPL_DLY_TEST_1, 0x10}, +{WCD9360_TEST_DEBUG_NPL_DLY_TEST_2, 0x60}, +{WCD9360_TEST_DEBUG_MEM_CTRL, 0x00}, +{WCD9360_TEST_DEBUG_DEBUG_BUS_SEL, 0x00}, +{WCD9360_TEST_DEBUG_DEBUG_JTAG, 0x00}, +{WCD9360_TEST_DEBUG_DEBUG_EN_1, 0x00}, +{WCD9360_TEST_DEBUG_DEBUG_EN_2, 0x00}, +{WCD9360_TEST_DEBUG_DEBUG_EN_3, 0x00}, +{WCD9360_TEST_DEBUG_DEBUG_EN_4, 0x00}, +{WCD9360_TEST_DEBUG_DEBUG_EN_5, 0x00}, +{WCD9360_TEST_DEBUG_ANA_DTEST_DIR, 0x00}, +{WCD9360_TEST_DEBUG_PAD_INP_DISABLE_0, 0x00}, +{WCD9360_TEST_DEBUG_PAD_INP_DISABLE_1, 0x00}, +{WCD9360_TEST_DEBUG_PAD_INP_DISABLE_2, 0x00}, +{WCD9360_TEST_DEBUG_PAD_INP_DISABLE_3, 0x00}, +{WCD9360_TEST_DEBUG_PAD_INP_DISABLE_4, 0x00}, +{WCD9360_TEST_DEBUG_SYSMEM_CTRL, 0x00}, +{WCD9360_TEST_DEBUG_LVAL_NOM_LOW, 0x96}, +{WCD9360_TEST_DEBUG_LVAL_NOM_HIGH, 0x00}, +{WCD9360_TEST_DEBUG_LVAL_SVS_SVS2_LOW, 0x53}, +{WCD9360_TEST_DEBUG_LVAL_SVS_SVS2_HIGH, 0x00}, +{WCD9360_TEST_DEBUG_SPI_SLAVE_CHAR, 0x00}, +{WCD9360_TEST_DEBUG_CODEC_DIAGS, 0x00}, +}; + + +const u8 wcd9360_page0_reg_access[WCD9360_PAGE_SIZE] = { +[WCD9360_REG(WCD9360_PAGE0_PAGE_REGISTER)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CODEC_RPM_CLK_BYPASS)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CODEC_RPM_CLK_GATE)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CODEC_RPM_CLK_MCLK_CFG)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CODEC_RPM_CLK_MCLK2_CFG)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CODEC_RPM_I2S_DSD_CLK_SEL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CODEC_RPM_RST_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CODEC_RPM_PWR_CDC_DIG_HM_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CHIP_TIER_CTRL_CHIP_ID_BYTE0)] = WCD9360_RO, +[WCD9360_REG(WCD9360_CHIP_TIER_CTRL_CHIP_ID_BYTE1)] = WCD9360_RO, +[WCD9360_REG(WCD9360_CHIP_TIER_CTRL_CHIP_ID_BYTE2)] = WCD9360_RO, +[WCD9360_REG(WCD9360_CHIP_TIER_CTRL_CHIP_ID_BYTE3)] = WCD9360_RO, +[WCD9360_REG(WCD9360_CHIP_TIER_CTRL_EFUSE_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CHIP_TIER_CTRL_EFUSE_TEST0)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CHIP_TIER_CTRL_EFUSE_TEST1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CHIP_TIER_CTRL_EFUSE_STATUS)] = WCD9360_RO, +[WCD9360_REG(WCD9360_CHIP_TIER_CTRL_I2C_SLAVE_ID_NONNEGO)] = WCD9360_RO, +[WCD9360_REG(WCD9360_CHIP_TIER_CTRL_I2C_SLAVE_ID_1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CHIP_TIER_CTRL_I2C_SLAVE_ID_2)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CHIP_TIER_CTRL_I2C_SLAVE_ID_3)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CHIP_TIER_CTRL_ANA_WAIT_STATE_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CHIP_TIER_CTRL_I2C_ACTIVE)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CHIP_TIER_CTRL_ALT_FUNC_EN)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CHIP_TIER_CTRL_GPIO_CTL_OE)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CHIP_TIER_CTRL_GPIO_CTL_DATA)] = WCD9360_RW, +[WCD9360_REG(WCD9360_DATA_HUB_RX0_CFG)] = WCD9360_RW, +[WCD9360_REG(WCD9360_DATA_HUB_RX1_CFG)] = WCD9360_RW, +[WCD9360_REG(WCD9360_DATA_HUB_RX2_CFG)] = WCD9360_RW, +[WCD9360_REG(WCD9360_DATA_HUB_RX3_CFG)] = WCD9360_RW, +[WCD9360_REG(WCD9360_DATA_HUB_RX4_CFG)] = WCD9360_RW, +[WCD9360_REG(WCD9360_DATA_HUB_RX5_CFG)] = WCD9360_RW, +[WCD9360_REG(WCD9360_DATA_HUB_RX6_CFG)] = WCD9360_RW, +[WCD9360_REG(WCD9360_DATA_HUB_RX7_CFG)] = WCD9360_RW, +[WCD9360_REG(WCD9360_DATA_HUB_SB_TX0_INP_CFG)] = WCD9360_RW, +[WCD9360_REG(WCD9360_DATA_HUB_SB_TX1_INP_CFG)] = WCD9360_RW, +[WCD9360_REG(WCD9360_DATA_HUB_SB_TX2_INP_CFG)] = WCD9360_RW, +[WCD9360_REG(WCD9360_DATA_HUB_SB_TX3_INP_CFG)] = WCD9360_RW, +[WCD9360_REG(WCD9360_DATA_HUB_SB_TX4_INP_CFG)] = WCD9360_RW, +[WCD9360_REG(WCD9360_DATA_HUB_SB_TX5_INP_CFG)] = WCD9360_RW, +[WCD9360_REG(WCD9360_DATA_HUB_SB_TX6_INP_CFG)] = WCD9360_RW, +[WCD9360_REG(WCD9360_DATA_HUB_SB_TX7_INP_CFG)] = WCD9360_RW, +[WCD9360_REG(WCD9360_DATA_HUB_SB_TX8_INP_CFG)] = WCD9360_RW, +[WCD9360_REG(WCD9360_DATA_HUB_SB_TX9_INP_CFG)] = WCD9360_RW, +[WCD9360_REG(WCD9360_DATA_HUB_SB_TX10_INP_CFG)] = WCD9360_RW, +[WCD9360_REG(WCD9360_DATA_HUB_SB_TX11_INP_CFG)] = WCD9360_RW, +[WCD9360_REG(WCD9360_DATA_HUB_SB_TX12_INP_CFG)] = WCD9360_RW, +[WCD9360_REG(WCD9360_DATA_HUB_SB_TX13_INP_CFG)] = WCD9360_RW, +[WCD9360_REG(WCD9360_DATA_HUB_SB_TX14_INP_CFG)] = WCD9360_RW, +[WCD9360_REG(WCD9360_DATA_HUB_SB_TX15_INP_CFG)] = WCD9360_RW, +[WCD9360_REG(WCD9360_DATA_HUB_I2S_TX0_CFG)] = WCD9360_RW, +[WCD9360_REG(WCD9360_DATA_HUB_I2S_TX0_CFG2)] = WCD9360_RW, +[WCD9360_REG(WCD9360_DATA_HUB_I2S_TX1_0_CFG)] = WCD9360_RW, +[WCD9360_REG(WCD9360_DATA_HUB_I2S_TX1_1_CFG)] = WCD9360_RW, +[WCD9360_REG(WCD9360_DATA_HUB_DATA_HUB_CFG)] = WCD9360_RW, +[WCD9360_REG(WCD9360_DATA_HUB_I2S_0_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_DATA_HUB_I2S_1_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_DATA_HUB_I2S_0_CTL2)] = WCD9360_RW, +[WCD9360_REG(WCD9360_DATA_HUB_I2S_1_CTL2)] = WCD9360_RW, +[WCD9360_REG(WCD9360_DATA_HUB_I2S_CLKSRC_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_DATA_HUB_I2S_COMMON_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_DATA_HUB_I2S_0_TDM_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_DATA_HUB_I2S_0_TDM_CTL2)] = WCD9360_RW, +[WCD9360_REG(WCD9360_DATA_HUB_I2S_0_TDM_CH_RX)] = WCD9360_RW, +[WCD9360_REG(WCD9360_DATA_HUB_I2S_0_TDM_CH_TX)] = WCD9360_RW, +[WCD9360_REG(WCD9360_DATA_HUB_I2S_0_TDM_CFG)] = WCD9360_RW, +[WCD9360_REG(WCD9360_DATA_HUB_I2S_0_TDM_STRETCH)] = WCD9360_RW, +[WCD9360_REG(WCD9360_DATA_HUB_I2S_RESET_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_DMA_RDMA_CTL_0)] = WCD9360_RW, +[WCD9360_REG(WCD9360_DMA_RDMA_CTL_1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_DMA_RDMA_CTL_2)] = WCD9360_RW, +[WCD9360_REG(WCD9360_DMA_RDMA_CTL_3)] = WCD9360_RW, +[WCD9360_REG(WCD9360_DMA_RDMA_CTL_4)] = WCD9360_RW, +[WCD9360_REG(WCD9360_DMA_CH_2_3_CFG_RDMA_0)] = WCD9360_RW, +[WCD9360_REG(WCD9360_DMA_CH_2_3_CFG_RDMA_1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_DMA_CH_2_3_CFG_RDMA_2)] = WCD9360_RW, +[WCD9360_REG(WCD9360_DMA_CH_2_3_CFG_RDMA_3)] = WCD9360_RW, +[WCD9360_REG(WCD9360_DMA_CH_2_3_CFG_RDMA_4)] = WCD9360_RW, +[WCD9360_REG(WCD9360_DMA_CH_0_1_CFG_RDMA_0)] = WCD9360_RW, +[WCD9360_REG(WCD9360_DMA_CH_0_1_CFG_RDMA_1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_DMA_CH_0_1_CFG_RDMA_2)] = WCD9360_RW, +[WCD9360_REG(WCD9360_DMA_CH_0_1_CFG_RDMA_3)] = WCD9360_RW, +[WCD9360_REG(WCD9360_DMA_CH_0_1_CFG_RDMA_4)] = WCD9360_RW, +[WCD9360_REG(WCD9360_DMA_RDMA4_PRT_CFG)] = WCD9360_RW, +[WCD9360_REG(WCD9360_DMA_RDMA_SBTX0_7_CFG)] = WCD9360_RW, +[WCD9360_REG(WCD9360_DMA_RDMA_SBTX8_10_CFG)] = WCD9360_RW, +[WCD9360_REG(WCD9360_DMA_WDMA_CTL_0)] = WCD9360_RW, +[WCD9360_REG(WCD9360_DMA_WDMA_CTL_1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_DMA_WDMA_CTL_2)] = WCD9360_RW, +[WCD9360_REG(WCD9360_DMA_WDMA_CTL_3)] = WCD9360_RW, +[WCD9360_REG(WCD9360_DMA_WDMA_CTL_4)] = WCD9360_RW, +[WCD9360_REG(WCD9360_DMA_CH_4_5_CFG_WDMA_0)] = WCD9360_RW, +[WCD9360_REG(WCD9360_DMA_CH_4_5_CFG_WDMA_1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_DMA_CH_4_5_CFG_WDMA_2)] = WCD9360_RW, +[WCD9360_REG(WCD9360_DMA_CH_4_5_CFG_WDMA_3)] = WCD9360_RW, +[WCD9360_REG(WCD9360_DMA_CH_4_5_CFG_WDMA_4)] = WCD9360_RW, +[WCD9360_REG(WCD9360_DMA_CH_2_3_CFG_WDMA_0)] = WCD9360_RW, +[WCD9360_REG(WCD9360_DMA_CH_2_3_CFG_WDMA_1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_DMA_CH_2_3_CFG_WDMA_2)] = WCD9360_RW, +[WCD9360_REG(WCD9360_DMA_CH_2_3_CFG_WDMA_3)] = WCD9360_RW, +[WCD9360_REG(WCD9360_DMA_CH_2_3_CFG_WDMA_4)] = WCD9360_RW, +[WCD9360_REG(WCD9360_DMA_CH_0_1_CFG_WDMA_0)] = WCD9360_RW, +[WCD9360_REG(WCD9360_DMA_CH_0_1_CFG_WDMA_1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_DMA_CH_0_1_CFG_WDMA_2)] = WCD9360_RW, +[WCD9360_REG(WCD9360_DMA_CH_0_1_CFG_WDMA_3)] = WCD9360_RW, +[WCD9360_REG(WCD9360_DMA_CH_0_1_CFG_WDMA_4)] = WCD9360_RW, +[WCD9360_REG(WCD9360_DMA_WDMA0_PRT_CFG)] = WCD9360_RW, +[WCD9360_REG(WCD9360_DMA_WDMA3_PRT_CFG)] = WCD9360_RW, +[WCD9360_REG(WCD9360_DMA_WDMA4_PRT0_3_CFG)] = WCD9360_RW, +[WCD9360_REG(WCD9360_DMA_WDMA4_PRT4_7_CFG)] = WCD9360_RW, +}; + +const u8 wcd9360_page1_reg_access[WCD9360_PAGE_SIZE] = { +[WCD9360_REG(WCD9360_PAGE1_PAGE_REGISTER)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CPE_FLL_USER_CTL_0)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CPE_FLL_USER_CTL_1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CPE_FLL_USER_CTL_2)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CPE_FLL_USER_CTL_3)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CPE_FLL_USER_CTL_4)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CPE_FLL_USER_CTL_5)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CPE_FLL_USER_CTL_6)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CPE_FLL_USER_CTL_7)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CPE_FLL_USER_CTL_8)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CPE_FLL_USER_CTL_9)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CPE_FLL_L_VAL_CTL_0)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CPE_FLL_L_VAL_CTL_1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CPE_FLL_DSM_FRAC_CTL_0)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CPE_FLL_DSM_FRAC_CTL_1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CPE_FLL_CONFIG_CTL_0)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CPE_FLL_CONFIG_CTL_1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CPE_FLL_CONFIG_CTL_2)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CPE_FLL_CONFIG_CTL_3)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CPE_FLL_CONFIG_CTL_4)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CPE_FLL_TEST_CTL_0)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CPE_FLL_TEST_CTL_1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CPE_FLL_TEST_CTL_2)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CPE_FLL_TEST_CTL_3)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CPE_FLL_TEST_CTL_4)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CPE_FLL_TEST_CTL_5)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CPE_FLL_TEST_CTL_6)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CPE_FLL_TEST_CTL_7)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CPE_FLL_FREQ_CTL_0)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CPE_FLL_FREQ_CTL_1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CPE_FLL_FREQ_CTL_2)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CPE_FLL_FREQ_CTL_3)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CPE_FLL_SSC_CTL_0)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CPE_FLL_SSC_CTL_1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CPE_FLL_SSC_CTL_2)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CPE_FLL_SSC_CTL_3)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CPE_FLL_FLL_MODE)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CPE_FLL_STATUS_0)] = WCD9360_RO, +[WCD9360_REG(WCD9360_CPE_FLL_STATUS_1)] = WCD9360_RO, +[WCD9360_REG(WCD9360_CPE_FLL_STATUS_2)] = WCD9360_RO, +[WCD9360_REG(WCD9360_CPE_FLL_STATUS_3)] = WCD9360_RO, +[WCD9360_REG(WCD9360_I2S_FLL_USER_CTL_0)] = WCD9360_RW, +[WCD9360_REG(WCD9360_I2S_FLL_USER_CTL_1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_I2S_FLL_USER_CTL_2)] = WCD9360_RW, +[WCD9360_REG(WCD9360_I2S_FLL_USER_CTL_3)] = WCD9360_RW, +[WCD9360_REG(WCD9360_I2S_FLL_USER_CTL_4)] = WCD9360_RW, +[WCD9360_REG(WCD9360_I2S_FLL_USER_CTL_5)] = WCD9360_RW, +[WCD9360_REG(WCD9360_I2S_FLL_USER_CTL_6)] = WCD9360_RW, +[WCD9360_REG(WCD9360_I2S_FLL_USER_CTL_7)] = WCD9360_RW, +[WCD9360_REG(WCD9360_I2S_FLL_USER_CTL_8)] = WCD9360_RW, +[WCD9360_REG(WCD9360_I2S_FLL_USER_CTL_9)] = WCD9360_RW, +[WCD9360_REG(WCD9360_I2S_FLL_L_VAL_CTL_0)] = WCD9360_RW, +[WCD9360_REG(WCD9360_I2S_FLL_L_VAL_CTL_1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_I2S_FLL_DSM_FRAC_CTL_0)] = WCD9360_RW, +[WCD9360_REG(WCD9360_I2S_FLL_DSM_FRAC_CTL_1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_I2S_FLL_CONFIG_CTL_0)] = WCD9360_RW, +[WCD9360_REG(WCD9360_I2S_FLL_CONFIG_CTL_1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_I2S_FLL_CONFIG_CTL_2)] = WCD9360_RW, +[WCD9360_REG(WCD9360_I2S_FLL_CONFIG_CTL_3)] = WCD9360_RW, +[WCD9360_REG(WCD9360_I2S_FLL_CONFIG_CTL_4)] = WCD9360_RW, +[WCD9360_REG(WCD9360_I2S_FLL_TEST_CTL_0)] = WCD9360_RW, +[WCD9360_REG(WCD9360_I2S_FLL_TEST_CTL_1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_I2S_FLL_TEST_CTL_2)] = WCD9360_RW, +[WCD9360_REG(WCD9360_I2S_FLL_TEST_CTL_3)] = WCD9360_RW, +[WCD9360_REG(WCD9360_I2S_FLL_TEST_CTL_4)] = WCD9360_RW, +[WCD9360_REG(WCD9360_I2S_FLL_TEST_CTL_5)] = WCD9360_RW, +[WCD9360_REG(WCD9360_I2S_FLL_TEST_CTL_6)] = WCD9360_RW, +[WCD9360_REG(WCD9360_I2S_FLL_TEST_CTL_7)] = WCD9360_RW, +[WCD9360_REG(WCD9360_I2S_FLL_FREQ_CTL_0)] = WCD9360_RW, +[WCD9360_REG(WCD9360_I2S_FLL_FREQ_CTL_1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_I2S_FLL_FREQ_CTL_2)] = WCD9360_RW, +[WCD9360_REG(WCD9360_I2S_FLL_FREQ_CTL_3)] = WCD9360_RW, +[WCD9360_REG(WCD9360_I2S_FLL_SSC_CTL_0)] = WCD9360_RW, +[WCD9360_REG(WCD9360_I2S_FLL_SSC_CTL_1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_I2S_FLL_SSC_CTL_2)] = WCD9360_RW, +[WCD9360_REG(WCD9360_I2S_FLL_SSC_CTL_3)] = WCD9360_RW, +[WCD9360_REG(WCD9360_I2S_FLL_FLL_MODE)] = WCD9360_RW, +[WCD9360_REG(WCD9360_I2S_FLL_STATUS_0)] = WCD9360_RO, +[WCD9360_REG(WCD9360_I2S_FLL_STATUS_1)] = WCD9360_RO, +[WCD9360_REG(WCD9360_I2S_FLL_STATUS_2)] = WCD9360_RO, +[WCD9360_REG(WCD9360_I2S_FLL_STATUS_3)] = WCD9360_RO, +}; + +const u8 wcd9360_page2_reg_access[WCD9360_PAGE_SIZE] = { +[WCD9360_REG(WCD9360_PAGE2_PAGE_REGISTER)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CPE_SS_CPE_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CPE_SS_PWR_SYS_PSTATE_CTL_0)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CPE_SS_PWR_SYS_PSTATE_CTL_1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CPE_SS_PWR_CPEFLL_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CPE_SS_PWR_CPE_SYSMEM_DEEPSLP_0)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CPE_SS_PWR_CPE_SYSMEM_DEEPSLP_1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_0)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_2)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_3)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_4)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CPE_SS_PWR_CPE_DRAM1_SHUTDOWN)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CPE_SS_US_BUF_INT_PERIOD)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CPE_SS_CPARMAD_BUFRDY_INT_PERIOD)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CPE_SS_SVA_CFG)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CPE_SS_US_CFG)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CPE_SS_MAD_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CPE_SS_CPAR_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CPE_SS_DMIC0_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CPE_SS_DMIC1_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CPE_SS_DMIC2_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CPE_SS_DMIC_CFG)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CPE_SS_CPAR_CFG)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CPE_SS_WDOG_CFG)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CPE_SS_BACKUP_INT)] = WCD9360_WO, +[WCD9360_REG(WCD9360_CPE_SS_STATUS)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CPE_SS_CPE_OCD_CFG)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CPE_SS_SS_ERROR_INT_MASK_0A)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CPE_SS_SS_ERROR_INT_MASK_0B)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CPE_SS_SS_ERROR_INT_MASK_1A)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CPE_SS_SS_ERROR_INT_MASK_1B)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CPE_SS_SS_ERROR_INT_STATUS_0A)] = WCD9360_RO, +[WCD9360_REG(WCD9360_CPE_SS_SS_ERROR_INT_STATUS_0B)] = WCD9360_RO, +[WCD9360_REG(WCD9360_CPE_SS_SS_ERROR_INT_STATUS_1A)] = WCD9360_RO, +[WCD9360_REG(WCD9360_CPE_SS_SS_ERROR_INT_STATUS_1B)] = WCD9360_RO, +[WCD9360_REG(WCD9360_CPE_SS_SS_ERROR_INT_CLEAR_0A)] = WCD9360_WO, +[WCD9360_REG(WCD9360_CPE_SS_SS_ERROR_INT_CLEAR_0B)] = WCD9360_WO, +[WCD9360_REG(WCD9360_CPE_SS_SS_ERROR_INT_CLEAR_1A)] = WCD9360_WO, +[WCD9360_REG(WCD9360_CPE_SS_SS_ERROR_INT_CLEAR_1B)] = WCD9360_WO, +[WCD9360_REG(WCD9360_CPE_SS_DMIC3_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CPE_SS_WDOG_RESET)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CPE_SS_LPASS_MCLK_PRG)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CPE_SS_LPASS_IPC_IN_0)] = WCD9360_WO, +[WCD9360_REG(WCD9360_CPE_SS_LPASS_IPC_IN_1)] = WCD9360_WO, +[WCD9360_REG(WCD9360_CPE_SS_LPASS_IPC_IN_2)] = WCD9360_WO, +[WCD9360_REG(WCD9360_CPE_SS_LPASS_IPC_IN_3)] = WCD9360_WO, +[WCD9360_REG(WCD9360_CPE_SS_LPASS_IPC_IN_4)] = WCD9360_WO, +[WCD9360_REG(WCD9360_CPE_SS_LPASS_IPC_IN_5)] = WCD9360_WO, +[WCD9360_REG(WCD9360_CPE_SS_LPASS_IPC_IN_6)] = WCD9360_WO, +[WCD9360_REG(WCD9360_CPE_SS_LPASS_IPC_IN_7)] = WCD9360_WO, +[WCD9360_REG(WCD9360_CPE_SS_LPASS_IPC_IN_8)] = WCD9360_WO, +[WCD9360_REG(WCD9360_CPE_SS_LPASS_IPC_IN_9)] = WCD9360_WO, +[WCD9360_REG(WCD9360_CPE_SS_LPASS_IPC_IN_10)] = WCD9360_WO, +[WCD9360_REG(WCD9360_CPE_SS_LPASS_IPC_IN_11)] = WCD9360_WO, +[WCD9360_REG(WCD9360_CPE_SS_LPASS_IPC_IN_12)] = WCD9360_WO, +[WCD9360_REG(WCD9360_CPE_SS_LPASS_IPC_IN_13)] = WCD9360_WO, +[WCD9360_REG(WCD9360_CPE_SS_LPASS_IPC_IN_14)] = WCD9360_WO, +[WCD9360_REG(WCD9360_CPE_SS_LPASS_IPC_IN_15)] = WCD9360_WO, +[WCD9360_REG(WCD9360_CPE_SS_LPASS_IPC_OUT_0)] = WCD9360_RO, +[WCD9360_REG(WCD9360_CPE_SS_LPASS_IPC_OUT_1)] = WCD9360_RO, +[WCD9360_REG(WCD9360_CPE_SS_LPASS_IPC_OUT_2)] = WCD9360_RO, +[WCD9360_REG(WCD9360_CPE_SS_LPASS_IPC_OUT_3)] = WCD9360_RO, +[WCD9360_REG(WCD9360_CPE_SS_LPASS_IPC_OUT_4)] = WCD9360_RO, +[WCD9360_REG(WCD9360_CPE_SS_LPASS_IPC_OUT_5)] = WCD9360_RO, +[WCD9360_REG(WCD9360_CPE_SS_LPASS_IPC_OUT_6)] = WCD9360_RO, +[WCD9360_REG(WCD9360_CPE_SS_LPASS_IPC_OUT_7)] = WCD9360_RO, +[WCD9360_REG(WCD9360_CPE_SS_LPASS_IPC_OUT_8)] = WCD9360_RO, +[WCD9360_REG(WCD9360_CPE_SS_LPASS_IPC_OUT_9)] = WCD9360_RO, +[WCD9360_REG(WCD9360_CPE_SS_LPASS_IPC_OUT_10)] = WCD9360_RO, +[WCD9360_REG(WCD9360_CPE_SS_LPASS_IPC_OUT_11)] = WCD9360_RO, +[WCD9360_REG(WCD9360_CPE_SS_LPASS_IPC_OUT_12)] = WCD9360_RO, +[WCD9360_REG(WCD9360_CPE_SS_LPASS_IPC_OUT_13)] = WCD9360_RO, +[WCD9360_REG(WCD9360_CPE_SS_LPASS_IPC_OUT_14)] = WCD9360_RO, +[WCD9360_REG(WCD9360_CPE_SS_LPASS_IPC_OUT_15)] = WCD9360_RO, +[WCD9360_REG(WCD9360_CPE_SS_LPASS_ARB_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CPE_SS_MEM_DEEPSLEEP_RD_0)] = WCD9360_RO, +[WCD9360_REG(WCD9360_CPE_SS_MEM_DEEPSLEEP_RD_1)] = WCD9360_RO, +[WCD9360_REG(WCD9360_CPE_SS_MEM_DEEPSLEEP_BYPASS_0)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CPE_SS_MEM_DEEPSLEEP_BYPASS_1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_SOC_MAD_MAIN_CTL_1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_SOC_MAD_MAIN_CTL_2)] = WCD9360_RW, +[WCD9360_REG(WCD9360_SOC_MAD_AUDIO_CTL_1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_SOC_MAD_AUDIO_CTL_2)] = WCD9360_RW, +[WCD9360_REG(WCD9360_SOC_MAD_AUDIO_CTL_3)] = WCD9360_RW, +[WCD9360_REG(WCD9360_SOC_MAD_AUDIO_CTL_4)] = WCD9360_RW, +[WCD9360_REG(WCD9360_SOC_MAD_AUDIO_CTL_5)] = WCD9360_RW, +[WCD9360_REG(WCD9360_SOC_MAD_AUDIO_CTL_6)] = WCD9360_RW, +[WCD9360_REG(WCD9360_SOC_MAD_AUDIO_CTL_7)] = WCD9360_RW, +[WCD9360_REG(WCD9360_SOC_MAD_AUDIO_CTL_8)] = WCD9360_RW, +[WCD9360_REG(WCD9360_SOC_MAD_AUDIO_IIR_CTL_PTR)] = WCD9360_RW, +[WCD9360_REG(WCD9360_SOC_MAD_AUDIO_IIR_CTL_VAL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_SOC_MAD_ULTR_CTL_1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_SOC_MAD_ULTR_CTL_2)] = WCD9360_RW, +[WCD9360_REG(WCD9360_SOC_MAD_ULTR_CTL_3)] = WCD9360_RW, +[WCD9360_REG(WCD9360_SOC_MAD_ULTR_CTL_4)] = WCD9360_RW, +[WCD9360_REG(WCD9360_SOC_MAD_ULTR_CTL_5)] = WCD9360_RW, +[WCD9360_REG(WCD9360_SOC_MAD_ULTR_CTL_6)] = WCD9360_RW, +[WCD9360_REG(WCD9360_SOC_MAD_ULTR_CTL_7)] = WCD9360_RW, +[WCD9360_REG(WCD9360_SOC_MAD_BEACON_CTL_1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_SOC_MAD_BEACON_CTL_2)] = WCD9360_RW, +[WCD9360_REG(WCD9360_SOC_MAD_BEACON_CTL_3)] = WCD9360_RW, +[WCD9360_REG(WCD9360_SOC_MAD_BEACON_CTL_4)] = WCD9360_RW, +[WCD9360_REG(WCD9360_SOC_MAD_BEACON_CTL_5)] = WCD9360_RW, +[WCD9360_REG(WCD9360_SOC_MAD_BEACON_CTL_6)] = WCD9360_RW, +[WCD9360_REG(WCD9360_SOC_MAD_BEACON_CTL_7)] = WCD9360_RW, +[WCD9360_REG(WCD9360_SOC_MAD_BEACON_CTL_8)] = WCD9360_RW, +[WCD9360_REG(WCD9360_SOC_MAD_BEACON_IIR_CTL_PTR)] = WCD9360_RW, +[WCD9360_REG(WCD9360_SOC_MAD_BEACON_IIR_CTL_VAL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_SOC_MAD_INP_SEL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_SOC_MAD_MAD2_INP_SEL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_SWR_SAMPLE_PACK_SWR_SAMPLE_PACK_CTRL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_SWR_SAMPLE_PACK_SWR_SAMPLE_PACK_STATUS)] = WCD9360_RW, +[WCD9360_REG(WCD9360_SWR_SAMPLE_PACK_SWR_SAMPLE_PACK_FS)] = WCD9360_RW, +[WCD9360_REG(WCD9360_SWR_SAMPLE_PACK_SWR_SAMPLE_PACK_IN_SEL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_EFUSE_VALUE_EFUSE_VAL_OUT0)] = WCD9360_RO, +[WCD9360_REG(WCD9360_EFUSE_VALUE_EFUSE_VAL_OUT1)] = WCD9360_RO, +[WCD9360_REG(WCD9360_EFUSE_VALUE_EFUSE_VAL_OUT2)] = WCD9360_RO, +[WCD9360_REG(WCD9360_EFUSE_VALUE_EFUSE_VAL_OUT3)] = WCD9360_RO, +[WCD9360_REG(WCD9360_EFUSE_VALUE_EFUSE_VAL_OUT4)] = WCD9360_RO, +[WCD9360_REG(WCD9360_EFUSE_VALUE_EFUSE_VAL_OUT5)] = WCD9360_RO, +[WCD9360_REG(WCD9360_EFUSE_VALUE_EFUSE_VAL_OUT6)] = WCD9360_RO, +[WCD9360_REG(WCD9360_EFUSE_VALUE_EFUSE_VAL_OUT7)] = WCD9360_RO, +[WCD9360_REG(WCD9360_EFUSE_VALUE_EFUSE_VAL_OUT8)] = WCD9360_RO, +[WCD9360_REG(WCD9360_EFUSE_VALUE_EFUSE_VAL_OUT9)] = WCD9360_RO, +[WCD9360_REG(WCD9360_EFUSE_VALUE_EFUSE_VAL_OUT10)] = WCD9360_RO, +[WCD9360_REG(WCD9360_EFUSE_VALUE_EFUSE_VAL_OUT11)] = WCD9360_RO, +[WCD9360_REG(WCD9360_EFUSE_VALUE_EFUSE_VAL_OUT12)] = WCD9360_RO, +[WCD9360_REG(WCD9360_EFUSE_VALUE_EFUSE_VAL_OUT13)] = WCD9360_RO, +[WCD9360_REG(WCD9360_EFUSE_VALUE_EFUSE_VAL_OUT14)] = WCD9360_RO, +[WCD9360_REG(WCD9360_EFUSE_VALUE_EFUSE_VAL_OUT15)] = WCD9360_RO, +[WCD9360_REG(WCD9360_EFUSE_VALUE_EFUSE2_VAL_OUT0)] = WCD9360_RO, +[WCD9360_REG(WCD9360_EFUSE_VALUE_EFUSE2_VAL_OUT1)] = WCD9360_RO, +[WCD9360_REG(WCD9360_EFUSE_VALUE_EFUSE2_VAL_OUT2)] = WCD9360_RO, +[WCD9360_REG(WCD9360_EFUSE_VALUE_EFUSE2_VAL_OUT3)] = WCD9360_RO, +[WCD9360_REG(WCD9360_EFUSE_VALUE_EFUSE2_VAL_OUT4)] = WCD9360_RO, +[WCD9360_REG(WCD9360_EFUSE_VALUE_EFUSE2_VAL_OUT5)] = WCD9360_RO, +[WCD9360_REG(WCD9360_EFUSE_VALUE_EFUSE2_VAL_OUT6)] = WCD9360_RO, +[WCD9360_REG(WCD9360_EFUSE_VALUE_EFUSE2_VAL_OUT7)] = WCD9360_RO, +[WCD9360_REG(WCD9360_EFUSE_VALUE_EFUSE2_VAL_OUT8)] = WCD9360_RO, +[WCD9360_REG(WCD9360_EFUSE_VALUE_EFUSE2_VAL_OUT9)] = WCD9360_RO, +[WCD9360_REG(WCD9360_EFUSE_VALUE_EFUSE2_VAL_OUT10)] = WCD9360_RO, +[WCD9360_REG(WCD9360_EFUSE_VALUE_EFUSE2_VAL_OUT11)] = WCD9360_RO, +[WCD9360_REG(WCD9360_EFUSE_VALUE_EFUSE2_VAL_OUT12)] = WCD9360_RO, +[WCD9360_REG(WCD9360_EFUSE_VALUE_EFUSE2_VAL_OUT13)] = WCD9360_RO, +[WCD9360_REG(WCD9360_EFUSE_VALUE_EFUSE2_VAL_OUT14)] = WCD9360_RO, +[WCD9360_REG(WCD9360_EFUSE_VALUE_EFUSE2_VAL_OUT15)] = WCD9360_RO, +}; + +const u8 wcd9360_page4_reg_access[WCD9360_PAGE_SIZE] = { +[WCD9360_REG(WCD9360_PAGE4_PAGE_REGISTER)] = WCD9360_RW, +[WCD9360_REG(WCD9360_INTR_CFG)] = WCD9360_RW, +[WCD9360_REG(WCD9360_INTR_CLR_COMMIT)] = WCD9360_WO, +[WCD9360_REG(WCD9360_INTR_PIN1_MASK0)] = WCD9360_RW, +[WCD9360_REG(WCD9360_INTR_PIN1_MASK1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_INTR_PIN1_MASK2)] = WCD9360_RW, +[WCD9360_REG(WCD9360_INTR_PIN1_MASK3)] = WCD9360_RW, +[WCD9360_REG(WCD9360_INTR_PIN1_STATUS0)] = WCD9360_RO, +[WCD9360_REG(WCD9360_INTR_PIN1_STATUS1)] = WCD9360_RO, +[WCD9360_REG(WCD9360_INTR_PIN1_STATUS2)] = WCD9360_RO, +[WCD9360_REG(WCD9360_INTR_PIN1_STATUS3)] = WCD9360_RO, +[WCD9360_REG(WCD9360_INTR_PIN1_CLEAR0)] = WCD9360_WO, +[WCD9360_REG(WCD9360_INTR_PIN1_CLEAR1)] = WCD9360_WO, +[WCD9360_REG(WCD9360_INTR_PIN1_CLEAR2)] = WCD9360_WO, +[WCD9360_REG(WCD9360_INTR_PIN1_CLEAR3)] = WCD9360_WO, +[WCD9360_REG(WCD9360_INTR_PIN2_MASK3)] = WCD9360_RW, +[WCD9360_REG(WCD9360_INTR_PIN2_STATUS3)] = WCD9360_RO, +[WCD9360_REG(WCD9360_INTR_PIN2_CLEAR3)] = WCD9360_WO, +[WCD9360_REG(WCD9360_INTR_CPESS_SUMRY_MASK2)] = WCD9360_RW, +[WCD9360_REG(WCD9360_INTR_CPESS_SUMRY_MASK3)] = WCD9360_RW, +[WCD9360_REG(WCD9360_INTR_CPESS_SUMRY_STATUS2)] = WCD9360_RO, +[WCD9360_REG(WCD9360_INTR_CPESS_SUMRY_STATUS3)] = WCD9360_RO, +[WCD9360_REG(WCD9360_INTR_CPESS_SUMRY_CLEAR2)] = WCD9360_WO, +[WCD9360_REG(WCD9360_INTR_CPESS_SUMRY_CLEAR3)] = WCD9360_WO, +[WCD9360_REG(WCD9360_INTR_LEVEL0)] = WCD9360_RW, +[WCD9360_REG(WCD9360_INTR_LEVEL1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_INTR_LEVEL2)] = WCD9360_RW, +[WCD9360_REG(WCD9360_INTR_LEVEL3)] = WCD9360_RW, +[WCD9360_REG(WCD9360_INTR_BYPASS0)] = WCD9360_RW, +[WCD9360_REG(WCD9360_INTR_BYPASS1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_INTR_BYPASS2)] = WCD9360_RW, +[WCD9360_REG(WCD9360_INTR_BYPASS3)] = WCD9360_RW, +[WCD9360_REG(WCD9360_INTR_SET0)] = WCD9360_RW, +[WCD9360_REG(WCD9360_INTR_SET1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_INTR_SET2)] = WCD9360_RW, +[WCD9360_REG(WCD9360_INTR_SET3)] = WCD9360_RW, +[WCD9360_REG(WCD9360_INTR_CODEC_MISC_MASK)] = WCD9360_RW, +[WCD9360_REG(WCD9360_INTR_CODEC_MISC_STATUS)] = WCD9360_RO, +[WCD9360_REG(WCD9360_INTR_CODEC_MISC_CLEAR)] = WCD9360_WO, +}; + +const u8 wcd9360_page6_reg_access[WCD9360_PAGE_SIZE] = { +[WCD9360_REG(WCD9360_ANA_PAGE_REGISTER)] = WCD9360_RW, +[WCD9360_REG(WCD9360_ANA_BIAS)] = WCD9360_RW, +[WCD9360_REG(WCD9360_ANA_AMIC_INPUT_SWITCH_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_ANA_RCO)] = WCD9360_RW, +[WCD9360_REG(WCD9360_ANA_BUCK_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_ANA_BUCK_STATUS)] = WCD9360_RO, +[WCD9360_REG(WCD9360_ANA_EAR)] = WCD9360_RW, +[WCD9360_REG(WCD9360_ANA_MAD_SETUP)] = WCD9360_RW, +[WCD9360_REG(WCD9360_ANA_AMIC1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_ANA_AMIC2)] = WCD9360_RW, +[WCD9360_REG(WCD9360_ANA_AMIC3)] = WCD9360_RW, +[WCD9360_REG(WCD9360_ANA_AMIC4)] = WCD9360_RW, +[WCD9360_REG(WCD9360_ANA_MICB1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_ANA_MICB2)] = WCD9360_RW, +[WCD9360_REG(WCD9360_ANA_MICB3)] = WCD9360_RW, +[WCD9360_REG(WCD9360_ANA_MICB4)] = WCD9360_RW, +[WCD9360_REG(WCD9360_BIAS_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_BIAS_VBG_FINE_ADJ)] = WCD9360_RW, +[WCD9360_REG(WCD9360_RCO_CTRL_1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_RCO_CTRL_2)] = WCD9360_RW, +[WCD9360_REG(WCD9360_RCO_CAL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_RCO_CAL_1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_RCO_CAL_2)] = WCD9360_RW, +[WCD9360_REG(WCD9360_RCO_TEST_CTRL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_RCO_CAL_OUT_1)] = WCD9360_RO, +[WCD9360_REG(WCD9360_RCO_CAL_OUT_2)] = WCD9360_RO, +[WCD9360_REG(WCD9360_RCO_CAL_OUT_3)] = WCD9360_RO, +[WCD9360_REG(WCD9360_RCO_CAL_OUT_4)] = WCD9360_RO, +[WCD9360_REG(WCD9360_RCO_CAL_OUT_5)] = WCD9360_RO, +[WCD9360_REG(WCD9360_SIDO_MODE_1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_SIDO_MODE_2)] = WCD9360_RW, +[WCD9360_REG(WCD9360_SIDO_MODE_3)] = WCD9360_RW, +[WCD9360_REG(WCD9360_SIDO_MODE_4)] = WCD9360_RW, +[WCD9360_REG(WCD9360_SIDO_VCL_1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_SIDO_VCL_2)] = WCD9360_RW, +[WCD9360_REG(WCD9360_SIDO_VCL_3)] = WCD9360_RW, +[WCD9360_REG(WCD9360_SIDO_CCL_1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_SIDO_CCL_2)] = WCD9360_RW, +[WCD9360_REG(WCD9360_SIDO_CCL_3)] = WCD9360_RW, +[WCD9360_REG(WCD9360_SIDO_CCL_4)] = WCD9360_RW, +[WCD9360_REG(WCD9360_SIDO_CCL_5)] = WCD9360_RW, +[WCD9360_REG(WCD9360_SIDO_CCL_6)] = WCD9360_RW, +[WCD9360_REG(WCD9360_SIDO_CCL_7)] = WCD9360_RW, +[WCD9360_REG(WCD9360_SIDO_CCL_8)] = WCD9360_RW, +[WCD9360_REG(WCD9360_SIDO_CCL_9)] = WCD9360_RW, +[WCD9360_REG(WCD9360_SIDO_CCL_10)] = WCD9360_RW, +[WCD9360_REG(WCD9360_SIDO_FILTER_1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_SIDO_FILTER_2)] = WCD9360_RW, +[WCD9360_REG(WCD9360_SIDO_DRIVER_1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_SIDO_DRIVER_2)] = WCD9360_RW, +[WCD9360_REG(WCD9360_SIDO_DRIVER_3)] = WCD9360_RW, +[WCD9360_REG(WCD9360_SIDO_CAL_CODE_EXT_1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_SIDO_CAL_CODE_EXT_2)] = WCD9360_RW, +[WCD9360_REG(WCD9360_SIDO_CAL_CODE_OUT_1)] = WCD9360_RO, +[WCD9360_REG(WCD9360_SIDO_CAL_CODE_OUT_2)] = WCD9360_RO, +[WCD9360_REG(WCD9360_SIDO_TEST_1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_SIDO_TEST_2)] = WCD9360_RW, +[WCD9360_REG(WCD9360_LDOH_MODE)] = WCD9360_RW, +[WCD9360_REG(WCD9360_LDOH_BIAS)] = WCD9360_RW, +[WCD9360_REG(WCD9360_LDOH_STB_LOADS)] = WCD9360_RW, +[WCD9360_REG(WCD9360_LDOH_SLOWRAMP)] = WCD9360_RW, +[WCD9360_REG(WCD9360_MICB1_TEST_CTL_1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_MICB1_TEST_CTL_2)] = WCD9360_RW, +[WCD9360_REG(WCD9360_MICB1_TEST_CTL_3)] = WCD9360_RW, +[WCD9360_REG(WCD9360_MICB2_TEST_CTL_1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_MICB2_TEST_CTL_2)] = WCD9360_RW, +[WCD9360_REG(WCD9360_MICB2_TEST_CTL_3)] = WCD9360_RW, +[WCD9360_REG(WCD9360_MICB3_TEST_CTL_1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_MICB3_TEST_CTL_2)] = WCD9360_RW, +[WCD9360_REG(WCD9360_MICB3_TEST_CTL_3)] = WCD9360_RW, +[WCD9360_REG(WCD9360_MICB4_TEST_CTL_1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_MICB4_TEST_CTL_2)] = WCD9360_RW, +[WCD9360_REG(WCD9360_MICB4_TEST_CTL_3)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TX_COM_ADC_VCM)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TX_COM_BIAS_ATEST)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TX_COM_ADC_INT1_IB)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TX_COM_ADC_INT2_IB)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TX_COM_TXFE_DIV_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TX_COM_TXFE_DIV_START)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TX_COM_TXFE_DIV_STOP_9P6M)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TX_COM_TXFE_DIV_STOP_12P288M)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TX_1_2_TEST_EN)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TX_1_2_ADC_IB)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TX_1_2_ATEST_REFCTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TX_1_2_TEST_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TX_1_2_TEST_BLK_EN)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TX_1_2_TXFE_CLKDIV)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TX_1_2_SAR1_ERR)] = WCD9360_RO, +[WCD9360_REG(WCD9360_TX_1_2_SAR2_ERR)] = WCD9360_RO, +[WCD9360_REG(WCD9360_TX_3_4_TEST_EN)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TX_3_4_ADC_IB)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TX_3_4_ATEST_REFCTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TX_3_4_TEST_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TX_3_4_TEST_BLK_EN)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TX_3_4_TXFE_CLKDIV)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TX_3_4_SAR1_ERR)] = WCD9360_RO, +[WCD9360_REG(WCD9360_TX_3_4_SAR2_ERR)] = WCD9360_RO, +[WCD9360_REG(WCD9360_RX_RX_EAR_BIAS_CON_1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_RX_RX_EAR_BIAS_CON_2)] = WCD9360_RW, +[WCD9360_REG(WCD9360_RX_RX_AUX_BIAS_CON_1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_RX_RX_AUX_BIAS_CON_2)] = WCD9360_RW, +[WCD9360_REG(WCD9360_RX_RX_BIAS_ATEST)] = WCD9360_RW, +[WCD9360_REG(WCD9360_RX_RXTOP_RESERVED)] = WCD9360_RW, +[WCD9360_REG(WCD9360_EAR_EAR_EN_REG)] = WCD9360_RW, +[WCD9360_REG(WCD9360_EAR_EAR_PA_CON)] = WCD9360_RW, +[WCD9360_REG(WCD9360_EAR_EAR_SP_CON)] = WCD9360_RW, +[WCD9360_REG(WCD9360_EAR_EAR_DAC_CON)] = WCD9360_RW, +[WCD9360_REG(WCD9360_EAR_EAR_CNP_FSM_CON)] = WCD9360_RW, +[WCD9360_REG(WCD9360_EAR_DAC_CTL_TEST)] = WCD9360_RW, +[WCD9360_REG(WCD9360_EAR_STATUS_REG)] = WCD9360_RO, +[WCD9360_REG(WCD9360_EAR_EAR_COMPANDER_CON)] = WCD9360_RW, +}; + +const u8 wcd9360_page7_reg_access[WCD9360_PAGE_SIZE] = { +[WCD9360_REG(WCD9360_ANA_NEW_PAGE_REGISTER)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CLK_SYS_PLL_ENABLES)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CLK_SYS_PLL_PRESET)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CLK_SYS_PLL_STATUS)] = WCD9360_RO, +[WCD9360_REG(WCD9360_CLK_SYS_MCLK_PRG)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CLK_SYS_MCLK2_PRG1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CLK_SYS_MCLK_MISC)] = WCD9360_RW, +[WCD9360_REG(WCD9360_SIDO_NEW_VOUT_A_STARTUP)] = WCD9360_RW, +[WCD9360_REG(WCD9360_SIDO_NEW_VOUT_D_STARTUP)] = WCD9360_RW, +[WCD9360_REG(WCD9360_SIDO_NEW_VOUT_D_FREQ1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_SIDO_NEW_VOUT_D_FREQ2)] = WCD9360_RW, +[WCD9360_REG(WCD9360_AUX_ANA_EAR)] = WCD9360_RW, +[WCD9360_REG(WCD9360_LDORXTX_LDORXTX)] = WCD9360_RW, +[WCD9360_REG(WCD9360_DIE_CRACK_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_DIE_CRACK_OUT)] = WCD9360_RO, +[WCD9360_REG(WCD9360_LOOP_BACK_EN)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CLK_SYS_INT_POST_DIV_REG0)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CLK_SYS_INT_POST_DIV_REG1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CLK_SYS_INT_REF_DIV_REG0)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CLK_SYS_INT_REF_DIV_REG1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CLK_SYS_INT_FILTER_REG0)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CLK_SYS_INT_FILTER_REG1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CLK_SYS_INT_PLL_L_VAL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CLK_SYS_INT_PLL_M_VAL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CLK_SYS_INT_PLL_N_VAL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CLK_SYS_INT_TEST_REG0)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CLK_SYS_INT_PFD_CP_DSM_PROG)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CLK_SYS_INT_VCO_PROG)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CLK_SYS_INT_TEST_REG1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CLK_SYS_INT_LDO_LOCK_CFG)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CLK_SYS_INT_DIG_LOCK_DET_CFG)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CLK_SYS_INT_CLK_TEST1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CLK_SYS_INT_CLK_TEST2)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CLK_SYS_INT_CLK_TEST3)] = WCD9360_RW, +[WCD9360_REG(WCD9360_SIDO_NEW_INT_RAMP_STATUS)] = WCD9360_RO, +[WCD9360_REG(WCD9360_SIDO_NEW_INT_SPARE_1)] = WCD9360_RO, +[WCD9360_REG(WCD9360_SIDO_NEW_INT_DEBUG_VOUT_SETTING_A)] = WCD9360_RW, +[WCD9360_REG(WCD9360_SIDO_NEW_INT_DEBUG_VOUT_SETTING_D)] = WCD9360_RW, +[WCD9360_REG(WCD9360_SIDO_NEW_INT_RAMP_INC_WAIT)] = WCD9360_RW, +[WCD9360_REG(WCD9360_SIDO_NEW_INT_DYNAMIC_IPEAK_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_SIDO_NEW_INT_RAMP_IBLEED_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_SIDO_NEW_INT_DEBUG_CPROVR_TEST)] = WCD9360_RW, +[WCD9360_REG(WCD9360_SIDO_NEW_INT_RAMP_CTL_A)] = WCD9360_RW, +[WCD9360_REG(WCD9360_SIDO_NEW_INT_RAMP_CTL_D)] = WCD9360_RW, +[WCD9360_REG(WCD9360_SIDO_NEW_INT_RAMP_TIMEOUT_PERIOD)] = WCD9360_RW, +[WCD9360_REG(WCD9360_SIDO_NEW_INT_DYNAMIC_IPEAK_SETTING1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_SIDO_NEW_INT_DYNAMIC_IPEAK_SETTING2)] = WCD9360_RW, +[WCD9360_REG(WCD9360_SIDO_NEW_INT_DYNAMIC_IPEAK_SETTING3)] = WCD9360_RW, +[WCD9360_REG(WCD9360_SIDO_NEW_INT_HIGH_ACCU_MODE_SEL1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_SIDO_NEW_INT_HIGH_ACCU_MODE_SEL2)] = WCD9360_RW, +[WCD9360_REG(WCD9360_EAR_INT_NEW_EAR_CHOPPER_CON)] = WCD9360_RW, +[WCD9360_REG(WCD9360_EAR_INT_NEW_EAR_VCM_GEN_CON1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_EAR_INT_NEW_EAR_VCM_GEN_CON2)] = WCD9360_RW, +[WCD9360_REG(WCD9360_EAR_INT_NEW_EAR_DYNAMIC_BIAS)] = WCD9360_RW, +[WCD9360_REG(WCD9360_AUX_INT_AUX_EN_REG)] = WCD9360_RW, +[WCD9360_REG(WCD9360_AUX_INT_AUX_PA_CON)] = WCD9360_RW, +[WCD9360_REG(WCD9360_AUX_INT_AUX_SP_CON)] = WCD9360_RW, +[WCD9360_REG(WCD9360_AUX_INT_AUX_DAC_CON)] = WCD9360_RW, +[WCD9360_REG(WCD9360_AUX_INT_AUX_CNP_FSM_CON)] = WCD9360_RW, +[WCD9360_REG(WCD9360_AUX_INT_AUX_TEST)] = WCD9360_RW, +[WCD9360_REG(WCD9360_AUX_INT_STATUS_REG)] = WCD9360_RO, +[WCD9360_REG(WCD9360_AUX_INT_AUX_MISC)] = WCD9360_RW, +[WCD9360_REG(WCD9360_LDORXTX_INT_ANA_LDORXTX_CTRL1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_LDORXTX_INT_ANA_LDORXTX_CTRL2)] = WCD9360_RW, +[WCD9360_REG(WCD9360_LDORXTX_INT_ANA_LDORXTX_CTRL3)] = WCD9360_RW, +[WCD9360_REG(WCD9360_LDORXTX_INT_ANA_LDORXTX_CTRL4)] = WCD9360_RW, +[WCD9360_REG(WCD9360_LDORXTX_INT_ANA_LDORXTX_CTRL5)] = WCD9360_RW, +[WCD9360_REG(WCD9360_LDORXTX_INT_ANA_LDORXTX_STATUS)] = WCD9360_RO, +[WCD9360_REG(WCD9360_DIE_CRACK_INT_INT1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_DIE_CRACK_INT_INT2)] = WCD9360_RW, +[WCD9360_REG(WCD9360_LOOP_BACK_INT_SPARE)] = WCD9360_RW, +}; + +const u8 wcd9360_page10_reg_access[WCD9360_PAGE_SIZE] = { +[WCD9360_REG(WCD9360_PAGE10_PAGE_REGISTER)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_ANC0_CLK_RESET_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_ANC0_MODE_1_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_ANC0_MODE_2_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_ANC0_FF_SHIFT)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_ANC0_FB_SHIFT)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_ANC0_LPF_FF_A_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_ANC0_LPF_FF_B_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_ANC0_LPF_FB_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_ANC0_SMLPF_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_ANC0_DCFLT_SHIFT_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_ANC0_IIR_ADAPT_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_ANC0_IIR_COEFF_1_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_ANC0_IIR_COEFF_2_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_ANC0_FF_A_GAIN_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_ANC0_FF_B_GAIN_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_ANC0_FB_GAIN_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX0_TX_PATH_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX0_TX_PATH_CFG0)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX0_TX_PATH_CFG1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX0_TX_VOL_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX0_TX_PATH_192_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX0_TX_PATH_192_CFG)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX0_TX_PATH_SEC0)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX0_TX_PATH_SEC1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX0_TX_PATH_SEC2)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX0_TX_PATH_SEC3)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX0_TX_PATH_SEC4)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX0_TX_PATH_SEC5)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX0_TX_PATH_SEC6)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX1_TX_PATH_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX1_TX_PATH_CFG0)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX1_TX_PATH_CFG1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX1_TX_VOL_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX1_TX_PATH_192_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX1_TX_PATH_192_CFG)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX1_TX_PATH_SEC0)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX1_TX_PATH_SEC1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX1_TX_PATH_SEC2)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX1_TX_PATH_SEC3)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX1_TX_PATH_SEC4)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX1_TX_PATH_SEC5)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX1_TX_PATH_SEC6)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX2_TX_PATH_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX2_TX_PATH_CFG0)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX2_TX_PATH_CFG1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX2_TX_VOL_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX2_TX_PATH_192_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX2_TX_PATH_192_CFG)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX2_TX_PATH_SEC0)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX2_TX_PATH_SEC1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX2_TX_PATH_SEC2)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX2_TX_PATH_SEC3)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX2_TX_PATH_SEC4)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX2_TX_PATH_SEC5)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX2_TX_PATH_SEC6)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX3_TX_PATH_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX3_TX_PATH_CFG0)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX3_TX_PATH_CFG1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX3_TX_VOL_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX3_TX_PATH_192_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX3_TX_PATH_192_CFG)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX3_TX_PATH_SEC0)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX3_TX_PATH_SEC1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX3_TX_PATH_SEC2)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX3_TX_PATH_SEC3)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX3_TX_PATH_SEC4)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX3_TX_PATH_SEC5)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX3_TX_PATH_SEC6)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX4_TX_PATH_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX4_TX_PATH_CFG0)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX4_TX_PATH_CFG1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX4_TX_VOL_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX4_TX_PATH_192_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX4_TX_PATH_192_CFG)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX4_TX_PATH_SEC0)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX4_TX_PATH_SEC1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX4_TX_PATH_SEC2)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX4_TX_PATH_SEC3)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX4_TX_PATH_SEC4)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX4_TX_PATH_SEC5)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX4_TX_PATH_SEC6)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX5_TX_PATH_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX5_TX_PATH_CFG0)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX5_TX_PATH_CFG1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX5_TX_VOL_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX5_TX_PATH_192_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX5_TX_PATH_192_CFG)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX5_TX_PATH_SEC0)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX5_TX_PATH_SEC1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX5_TX_PATH_SEC2)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX5_TX_PATH_SEC3)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX5_TX_PATH_SEC4)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX5_TX_PATH_SEC5)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX5_TX_PATH_SEC6)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX6_TX_PATH_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX6_TX_PATH_CFG0)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX6_TX_PATH_CFG1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX6_TX_VOL_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX6_TX_PATH_192_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX6_TX_PATH_192_CFG)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX6_TX_PATH_SEC0)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX6_TX_PATH_SEC1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX6_TX_PATH_SEC2)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX6_TX_PATH_SEC3)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX6_TX_PATH_SEC4)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX6_TX_PATH_SEC5)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX6_TX_PATH_SEC6)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX7_TX_PATH_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX7_TX_PATH_CFG0)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX7_TX_PATH_CFG1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX7_TX_VOL_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX7_TX_PATH_192_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX7_TX_PATH_192_CFG)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX7_TX_PATH_SEC0)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX7_TX_PATH_SEC1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX7_TX_PATH_SEC2)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX7_TX_PATH_SEC3)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX7_TX_PATH_SEC4)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX7_TX_PATH_SEC5)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX7_TX_PATH_SEC6)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX8_TX_PATH_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX8_TX_PATH_CFG0)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX8_TX_PATH_CFG1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX8_TX_VOL_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX8_TX_PATH_192_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX8_TX_PATH_192_CFG)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX8_TX_PATH_SEC0)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX8_TX_PATH_SEC1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX8_TX_PATH_SEC2)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX8_TX_PATH_SEC3)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX8_TX_PATH_SEC4)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX8_TX_PATH_SEC5)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX8_TX_PATH_SEC6)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX9_SPKR_PROT_PATH_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX9_SPKR_PROT_PATH_CFG0)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX10_SPKR_PROT_PATH_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX10_SPKR_PROT_PATH_CFG0)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX11_SPKR_PROT_PATH_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX11_SPKR_PROT_PATH_CFG0)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX12_SPKR_PROT_PATH_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX12_SPKR_PROT_PATH_CFG0)] = WCD9360_RW, +}; + +const u8 wcd9360_page11_reg_access[WCD9360_PAGE_SIZE] = { +[WCD9360_REG(WCD9360_PAGE11_PAGE_REGISTER)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_COMPANDER0_CTL0)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_COMPANDER0_CTL1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_COMPANDER0_CTL2)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_COMPANDER0_CTL3)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_COMPANDER0_CTL4)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_COMPANDER0_CTL5)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_COMPANDER0_CTL6)] = WCD9360_RO, +[WCD9360_REG(WCD9360_CDC_COMPANDER0_CTL7)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_COMPANDER7_CTL0)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_COMPANDER7_CTL1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_COMPANDER7_CTL2)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_COMPANDER7_CTL3)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_COMPANDER7_CTL4)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_COMPANDER7_CTL5)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_COMPANDER7_CTL6)] = WCD9360_RO, +[WCD9360_REG(WCD9360_CDC_COMPANDER7_CTL7)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_COMPANDER8_CTL0)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_COMPANDER8_CTL1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_COMPANDER8_CTL2)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_COMPANDER8_CTL3)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_COMPANDER8_CTL4)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_COMPANDER8_CTL5)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_COMPANDER8_CTL6)] = WCD9360_RO, +[WCD9360_REG(WCD9360_CDC_COMPANDER8_CTL7)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_RX0_RX_PATH_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_RX0_RX_PATH_CFG0)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_RX0_RX_PATH_CFG1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_RX0_RX_PATH_CFG2)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_RX0_RX_VOL_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_RX0_RX_PATH_MIX_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_RX0_RX_PATH_MIX_CFG)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_RX0_RX_VOL_MIX_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_RX0_RX_PATH_SEC0)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_RX0_RX_PATH_SEC1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_RX0_RX_PATH_SEC2)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_RX0_RX_PATH_SEC3)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_RX0_RX_PATH_SEC5)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_RX0_RX_PATH_SEC6)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_RX0_RX_PATH_SEC7)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_RX0_RX_PATH_MIX_SEC0)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_RX0_RX_PATH_MIX_SEC1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_RX0_RX_PATH_DSMDEM_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_RX9_RX_PATH_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_RX9_RX_PATH_CFG0)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_RX9_RX_PATH_CFG1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_RX9_RX_PATH_CFG2)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_RX9_RX_VOL_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_RX9_RX_PATH_MIX_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_RX9_RX_PATH_MIX_CFG)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_RX9_RX_VOL_MIX_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_RX9_RX_PATH_SEC0)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_RX9_RX_PATH_SEC1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_RX9_RX_PATH_SEC2)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_RX9_RX_PATH_SEC3)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_RX9_RX_PATH_SEC5)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_RX9_RX_PATH_SEC6)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_RX9_RX_PATH_SEC7)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_RX9_RX_PATH_MIX_SEC0)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_RX9_RX_PATH_MIX_SEC1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_RX9_RX_PATH_DSMDEM_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_RX7_RX_PATH_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_RX7_RX_PATH_CFG0)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_RX7_RX_PATH_CFG1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_RX7_RX_PATH_CFG2)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_RX7_RX_VOL_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_RX7_RX_PATH_MIX_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_RX7_RX_PATH_MIX_CFG)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_RX7_RX_VOL_MIX_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_RX7_RX_PATH_SEC0)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_RX7_RX_PATH_SEC1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_RX7_RX_PATH_SEC2)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_RX7_RX_PATH_SEC3)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_RX7_RX_PATH_SEC5)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_RX7_RX_PATH_SEC6)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_RX7_RX_PATH_SEC7)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_RX7_RX_PATH_MIX_SEC0)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_RX7_RX_PATH_MIX_SEC1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_RX7_RX_PATH_DSMDEM_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_RX8_RX_PATH_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_RX8_RX_PATH_CFG0)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_RX8_RX_PATH_CFG1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_RX8_RX_PATH_CFG2)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_RX8_RX_VOL_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_RX8_RX_PATH_MIX_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_RX8_RX_PATH_MIX_CFG)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_RX8_RX_VOL_MIX_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_RX8_RX_PATH_SEC0)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_RX8_RX_PATH_SEC1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_RX8_RX_PATH_SEC2)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_RX8_RX_PATH_SEC3)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_RX8_RX_PATH_SEC5)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_RX8_RX_PATH_SEC6)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_RX8_RX_PATH_SEC7)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_RX8_RX_PATH_MIX_SEC0)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_RX8_RX_PATH_MIX_SEC1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_RX8_RX_PATH_DSMDEM_CTL)] = WCD9360_RW, +}; + +const u8 wcd9360_page12_reg_access[WCD9360_PAGE_SIZE] = { +[WCD9360_REG(WCD9360_PAGE12_PAGE_REGISTER)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_BOOST0_BOOST_PATH_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_BOOST0_BOOST_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_BOOST0_BOOST_CFG1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_BOOST0_BOOST_CFG2)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_BOOST1_BOOST_PATH_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_BOOST1_BOOST_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_BOOST1_BOOST_CFG1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_BOOST1_BOOST_CFG2)] = WCD9360_RW, +[WCD9360_REG(WCD9360_MIXING_ASRC2_CLK_RST_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_MIXING_ASRC2_CTL0)] = WCD9360_RW, +[WCD9360_REG(WCD9360_MIXING_ASRC2_CTL1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_MIXING_ASRC2_FIFO_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_MIXING_ASRC2_STATUS_FMIN_CNTR_LSB)] = WCD9360_RO, +[WCD9360_REG(WCD9360_MIXING_ASRC2_STATUS_FMIN_CNTR_MSB)] = WCD9360_RO, +[WCD9360_REG(WCD9360_MIXING_ASRC2_STATUS_FMAX_CNTR_LSB)] = WCD9360_RO, +[WCD9360_REG(WCD9360_MIXING_ASRC2_STATUS_FMAX_CNTR_MSB)] = WCD9360_RO, +[WCD9360_REG(WCD9360_MIXING_ASRC2_STATUS_FIFO)] = WCD9360_RO, +[WCD9360_REG(WCD9360_MIXING_ASRC3_CLK_RST_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_MIXING_ASRC3_CTL0)] = WCD9360_RW, +[WCD9360_REG(WCD9360_MIXING_ASRC3_CTL1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_MIXING_ASRC3_FIFO_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_MIXING_ASRC3_STATUS_FMIN_CNTR_LSB)] = WCD9360_RO, +[WCD9360_REG(WCD9360_MIXING_ASRC3_STATUS_FMIN_CNTR_MSB)] = WCD9360_RO, +[WCD9360_REG(WCD9360_MIXING_ASRC3_STATUS_FMAX_CNTR_LSB)] = WCD9360_RO, +[WCD9360_REG(WCD9360_MIXING_ASRC3_STATUS_FMAX_CNTR_MSB)] = WCD9360_RO, +[WCD9360_REG(WCD9360_MIXING_ASRC3_STATUS_FIFO)] = WCD9360_RO, +[WCD9360_REG(WCD9360_SWR_AHB_BRIDGE_WR_DATA_0)] = WCD9360_RW, +[WCD9360_REG(WCD9360_SWR_AHB_BRIDGE_WR_DATA_1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_SWR_AHB_BRIDGE_WR_DATA_2)] = WCD9360_RW, +[WCD9360_REG(WCD9360_SWR_AHB_BRIDGE_WR_DATA_3)] = WCD9360_RW, +[WCD9360_REG(WCD9360_SWR_AHB_BRIDGE_WR_ADDR_0)] = WCD9360_RW, +[WCD9360_REG(WCD9360_SWR_AHB_BRIDGE_WR_ADDR_1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_SWR_AHB_BRIDGE_WR_ADDR_2)] = WCD9360_RW, +[WCD9360_REG(WCD9360_SWR_AHB_BRIDGE_WR_ADDR_3)] = WCD9360_RW, +[WCD9360_REG(WCD9360_SWR_AHB_BRIDGE_RD_ADDR_0)] = WCD9360_RW, +[WCD9360_REG(WCD9360_SWR_AHB_BRIDGE_RD_ADDR_1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_SWR_AHB_BRIDGE_RD_ADDR_2)] = WCD9360_RW, +[WCD9360_REG(WCD9360_SWR_AHB_BRIDGE_RD_ADDR_3)] = WCD9360_RW, +[WCD9360_REG(WCD9360_SWR_AHB_BRIDGE_RD_DATA_0)] = WCD9360_RO, +[WCD9360_REG(WCD9360_SWR_AHB_BRIDGE_RD_DATA_1)] = WCD9360_RO, +[WCD9360_REG(WCD9360_SWR_AHB_BRIDGE_RD_DATA_2)] = WCD9360_RO, +[WCD9360_REG(WCD9360_SWR_AHB_BRIDGE_RD_DATA_3)] = WCD9360_RO, +[WCD9360_REG(WCD9360_SWR_AHB_BRIDGE_ACCESS_CFG)] = WCD9360_RW, +[WCD9360_REG(WCD9360_SWR_AHB_BRIDGE_ACCESS_STATUS)] = WCD9360_RO, +[WCD9360_REG(WCD9360_CDC_SIDETONE_SRC0_ST_SRC_PATH_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_SIDETONE_SRC0_ST_SRC_PATH_CFG1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_SIDETONE_ASRC0_CLK_RST_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_SIDETONE_ASRC0_CTL0)] = WCD9360_RW, +[WCD9360_REG(WCD9360_SIDETONE_ASRC0_CTL1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_SIDETONE_ASRC0_FIFO_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_SIDETONE_ASRC0_STATUS_FMIN_CNTR_LSB)] = WCD9360_RO, +[WCD9360_REG(WCD9360_SIDETONE_ASRC0_STATUS_FMIN_CNTR_MSB)] = WCD9360_RO, +[WCD9360_REG(WCD9360_SIDETONE_ASRC0_STATUS_FMAX_CNTR_LSB)] = WCD9360_RO, +[WCD9360_REG(WCD9360_SIDETONE_ASRC0_STATUS_FMAX_CNTR_MSB)] = WCD9360_RO, +[WCD9360_REG(WCD9360_SIDETONE_ASRC0_STATUS_FIFO)] = WCD9360_RO, +[WCD9360_REG(WCD9360_EC_REF_HQ0_EC_REF_HQ_PATH_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_EC_REF_HQ0_EC_REF_HQ_CFG0)] = WCD9360_RW, +[WCD9360_REG(WCD9360_EC_REF_HQ1_EC_REF_HQ_PATH_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_EC_REF_HQ1_EC_REF_HQ_CFG0)] = WCD9360_RW, +[WCD9360_REG(WCD9360_EC_ASRC0_CLK_RST_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_EC_ASRC0_CTL0)] = WCD9360_RW, +[WCD9360_REG(WCD9360_EC_ASRC0_CTL1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_EC_ASRC0_FIFO_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_EC_ASRC0_STATUS_FMIN_CNTR_LSB)] = WCD9360_RO, +[WCD9360_REG(WCD9360_EC_ASRC0_STATUS_FMIN_CNTR_MSB)] = WCD9360_RO, +[WCD9360_REG(WCD9360_EC_ASRC0_STATUS_FMAX_CNTR_LSB)] = WCD9360_RO, +[WCD9360_REG(WCD9360_EC_ASRC0_STATUS_FMAX_CNTR_MSB)] = WCD9360_RO, +[WCD9360_REG(WCD9360_EC_ASRC0_STATUS_FIFO)] = WCD9360_RO, +[WCD9360_REG(WCD9360_EC_ASRC1_CLK_RST_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_EC_ASRC1_CTL0)] = WCD9360_RW, +[WCD9360_REG(WCD9360_EC_ASRC1_CTL1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_EC_ASRC1_FIFO_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_EC_ASRC1_STATUS_FMIN_CNTR_LSB)] = WCD9360_RO, +[WCD9360_REG(WCD9360_EC_ASRC1_STATUS_FMIN_CNTR_MSB)] = WCD9360_RO, +[WCD9360_REG(WCD9360_EC_ASRC1_STATUS_FMAX_CNTR_LSB)] = WCD9360_RO, +[WCD9360_REG(WCD9360_EC_ASRC1_STATUS_FMAX_CNTR_MSB)] = WCD9360_RO, +[WCD9360_REG(WCD9360_EC_ASRC1_STATUS_FIFO)] = WCD9360_RO, +}; + +const u8 wcd9360_page13_reg_access[WCD9360_PAGE_SIZE] = { +[WCD9360_REG(WCD9360_PAGE13_PAGE_REGISTER)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_RX_INP_MUX_RX_INT0_CFG0)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_RX_INP_MUX_RX_INT0_CFG1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_RX_INP_MUX_RX_INT9_CFG0)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_RX_INP_MUX_RX_INT9_CFG1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_RX_INP_MUX_RX_INT7_CFG0)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_RX_INP_MUX_RX_INT7_CFG1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_RX_INP_MUX_RX_INT8_CFG0)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_RX_INP_MUX_RX_INT8_CFG1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_RX_INP_MUX_RX_MIX_CFG0)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_RX_INP_MUX_RX_MIX_CFG1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_RX_INP_MUX_RX_MIX_CFG2)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_RX_INP_MUX_RX_MIX_CFG3)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_RX_INP_MUX_RX_MIX_CFG4)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_RX_INP_MUX_SIDETONE_SRC_CFG0)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_RX_INP_MUX_SIDETONE_SRC_CFG1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_RX_INP_MUX_ANC_CFG0)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_RX_INP_MUX_SPLINE_ASRC_CFG0)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_RX_INP_MUX_EC_REF_HQ_CFG0)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX_INP_MUX_ADC_MUX0_CFG0)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX_INP_MUX_ADC_MUX0_CFG1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX_INP_MUX_ADC_MUX1_CFG0)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX_INP_MUX_ADC_MUX1_CFG1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX_INP_MUX_ADC_MUX2_CFG0)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX_INP_MUX_ADC_MUX2_CFG1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX_INP_MUX_ADC_MUX3_CFG0)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX_INP_MUX_ADC_MUX3_CFG1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX_INP_MUX_ADC_MUX4_CFG0)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX_INP_MUX_ADC_MUX5_CFG0)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX_INP_MUX_ADC_MUX6_CFG0)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX_INP_MUX_ADC_MUX7_CFG0)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX_INP_MUX_ADC_MUX8_CFG0)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX_INP_MUX_ADC_MUX10_CFG0)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TX_INP_MUX_ADC_MUX11_CFG0)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG0)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG2)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG3)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_IF_ROUTER_TX_MUX_CFG0)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_IF_ROUTER_TX_MUX_CFG1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_IF_ROUTER_TX_MUX_CFG2)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_IF_ROUTER_TX_MUX_CFG3)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_CLK_RST_CTRL_MCLK_CONTROL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_CLK_RST_CTRL_FS_CNT_CONTROL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_CLK_RST_CTRL_SWR_CONTROL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_CLK_RST_CTRL_ASRC_SHARE_CONTROL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_PROX_DETECT_PROX_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_PROX_DETECT_PROX_POLL_PERIOD0)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_PROX_DETECT_PROX_POLL_PERIOD1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_PROX_DETECT_PROX_SIG_PATTERN_LSB)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_PROX_DETECT_PROX_SIG_PATTERN_MSB)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_PROX_DETECT_PROX_STATUS)] = WCD9360_RO, +[WCD9360_REG(WCD9360_CDC_PROX_DETECT_PROX_TEST_CTRL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_PROX_DETECT_PROX_TEST_BUFF_LSB)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_PROX_DETECT_PROX_TEST_BUFF_MSB)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_PROX_DETECT_PROX_TEST_BUFF_LSB_RD)] = WCD9360_RO, +[WCD9360_REG(WCD9360_CDC_PROX_DETECT_PROX_TEST_BUFF_MSB_RD)] = WCD9360_RO, +[WCD9360_REG(WCD9360_CDC_PROX_DETECT_PROX_CTL_REPEAT_PAT)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_SIDETONE_IIR0_IIR_PATH_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_SIDETONE_IIR0_IIR_GAIN_B1_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_SIDETONE_IIR0_IIR_GAIN_B2_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_SIDETONE_IIR0_IIR_GAIN_B3_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_SIDETONE_IIR0_IIR_GAIN_B4_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_SIDETONE_IIR0_IIR_GAIN_B5_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_SIDETONE_IIR0_IIR_GAIN_B6_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_SIDETONE_IIR0_IIR_GAIN_B7_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_SIDETONE_IIR0_IIR_GAIN_B8_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_SIDETONE_IIR0_IIR_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_SIDETONE_IIR0_IIR_GAIN_TIMER_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TOP_TOP_CFG0)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TOP_TOP_CFG1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TOP_TOP_CFG7)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TOP_EAR_COMP_WR_LSB)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TOP_EAR_COMP_WR_MSB)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TOP_EAR_COMP_LUT)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CDC_TOP_EAR_COMP_RD_LSB)] = WCD9360_RO, +[WCD9360_REG(WCD9360_CDC_TOP_EAR_COMP_RD_MSB)] = WCD9360_RO, +[WCD9360_REG(WCD9360_CDC_TOP_TOP_DEBUG)] = WCD9360_RW, +}; + +const u8 wcd9360_page80_reg_access[WCD9360_PAGE_SIZE] = { +[WCD9360_REG(WCD9360_PAGE80_PAGE_REGISTER)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CODEC_CPR_WR_DATA_0)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CODEC_CPR_WR_DATA_1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CODEC_CPR_WR_DATA_2)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CODEC_CPR_WR_DATA_3)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CODEC_CPR_WR_ADDR_0)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CODEC_CPR_WR_ADDR_1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CODEC_CPR_WR_ADDR_2)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CODEC_CPR_WR_ADDR_3)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CODEC_CPR_RD_ADDR_0)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CODEC_CPR_RD_ADDR_1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CODEC_CPR_RD_ADDR_2)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CODEC_CPR_RD_ADDR_3)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CODEC_CPR_RD_DATA_0)] = WCD9360_RO, +[WCD9360_REG(WCD9360_CODEC_CPR_RD_DATA_1)] = WCD9360_RO, +[WCD9360_REG(WCD9360_CODEC_CPR_RD_DATA_2)] = WCD9360_RO, +[WCD9360_REG(WCD9360_CODEC_CPR_RD_DATA_3)] = WCD9360_RO, +[WCD9360_REG(WCD9360_CODEC_CPR_ACCESS_CFG)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CODEC_CPR_ACCESS_STATUS)] = WCD9360_RO, +[WCD9360_REG(WCD9360_CODEC_CPR_NOM_CX_VDD)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CODEC_CPR_SVS_CX_VDD)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CODEC_CPR_SVS2_CX_VDD)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CODEC_CPR_NOM_MX_VDD)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CODEC_CPR_SVS_MX_VDD)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CODEC_CPR_SVS2_MX_VDD)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CODEC_CPR_SVS2_MIN_CX_VDD)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CODEC_CPR_MAX_SVS2_STEP)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CODEC_CPR_CTL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_CODEC_CPR_SW_MODECHNG_STATUS)] = WCD9360_RO, +[WCD9360_REG(WCD9360_CODEC_CPR_SW_MODECHNG_START)] = WCD9360_WO, +[WCD9360_REG(WCD9360_CODEC_CPR_CPR_STATUS)] = WCD9360_RW, +}; + +const u8 wcd9360_page128_reg_access[WCD9360_PAGE_SIZE] = { +[WCD9360_REG(WCD9360_PAGE128_PAGE_REGISTER)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TLMM_JTCK_PINCFG)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TLMM_INTR1_PINCFG)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TLMM_INTR2_PINCFG)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TLMM_SWR_DATA_PINCFG)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TLMM_SWR_CLK_PINCFG)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TLMM_SLIMBUS_DATA1_PINCFG)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TLMM_SLIMBUS_DATA2_PINCFG)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TLMM_SLIMBUS_CLK_PINCFG)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TLMM_I2C_CLK_PINCFG)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TLMM_I2C_DATA_PINCFG)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TLMM_I2S_0_RX_PINCFG)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TLMM_I2S_0_TX_PINCFG)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TLMM_I2S_0_SCK_PINCFG)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TLMM_I2S_0_WS_PINCFG)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TLMM_I2S_1_RX_PINCFG)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TLMM_I2S_1_TX_PINCFG)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TLMM_I2S_1_SCK_PINCFG)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TLMM_I2S_1_WS_PINCFG)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TLMM_DMIC1_CLK_PINCFG)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TLMM_DMIC1_DATA_PINCFG)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TLMM_DMIC2_CLK_PINCFG)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TLMM_DMIC2_DATA_PINCFG)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TLMM_GPIO1_PINCFG)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TLMM_GPIO2_PINCFG)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TLMM_GPIO3_PINCFG)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TLMM_GPIO4_PINCFG)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TLMM_SPI_S_CSN_PINCFG)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TLMM_SPI_S_CLK_PINCFG)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TLMM_SPI_S_DOUT_PINCFG)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TLMM_SPI_S_DIN_PINCFG)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TLMM_GPIO0_PINCFG)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TLMM_DMIC3_CLK_PINCFG)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TLMM_DMIC3_DATA_PINCFG)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TLMM_DMIC4_CLK_PINCFG)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TLMM_DMIC4_DATA_PINCFG)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TEST_DEBUG_PIN_CTL_OE_0)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TEST_DEBUG_PIN_CTL_OE_1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TEST_DEBUG_PIN_CTL_OE_2)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TEST_DEBUG_PIN_CTL_OE_3)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TEST_DEBUG_PIN_CTL_OE_4)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TEST_DEBUG_PIN_CTL_DATA_0)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TEST_DEBUG_PIN_CTL_DATA_1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TEST_DEBUG_PIN_CTL_DATA_2)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TEST_DEBUG_PIN_CTL_DATA_3)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TEST_DEBUG_PIN_CTL_DATA_4)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TEST_DEBUG_PAD_DRVCTL_0)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TEST_DEBUG_PAD_DRVCTL_1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TEST_DEBUG_PIN_STATUS)] = WCD9360_RO, +[WCD9360_REG(WCD9360_TEST_DEBUG_NPL_DLY_TEST_1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TEST_DEBUG_NPL_DLY_TEST_2)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TEST_DEBUG_MEM_CTRL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TEST_DEBUG_DEBUG_BUS_SEL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TEST_DEBUG_DEBUG_JTAG)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TEST_DEBUG_DEBUG_EN_1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TEST_DEBUG_DEBUG_EN_2)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TEST_DEBUG_DEBUG_EN_3)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TEST_DEBUG_DEBUG_EN_4)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TEST_DEBUG_DEBUG_EN_5)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TEST_DEBUG_ANA_DTEST_DIR)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TEST_DEBUG_PAD_INP_DISABLE_0)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TEST_DEBUG_PAD_INP_DISABLE_1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TEST_DEBUG_PAD_INP_DISABLE_2)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TEST_DEBUG_PAD_INP_DISABLE_3)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TEST_DEBUG_PAD_INP_DISABLE_4)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TEST_DEBUG_SYSMEM_CTRL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TEST_DEBUG_LVAL_NOM_LOW)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TEST_DEBUG_LVAL_NOM_HIGH)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TEST_DEBUG_LVAL_SVS_SVS2_LOW)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TEST_DEBUG_LVAL_SVS_SVS2_HIGH)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TEST_DEBUG_SPI_SLAVE_CHAR)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TEST_DEBUG_CODEC_DIAGS)] = WCD9360_RO, +[WCD9360_REG(WCD9360_TEST_DEBUG_PAD_TEST)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TEST_DEBUG_DEBUG_MUX_BIT_0)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TEST_DEBUG_DEBUG_MUX_BIT_1)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TEST_DEBUG_DEBUG_MUX_BIT_2)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TEST_DEBUG_DEBUG_MUX_BIT_3)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TEST_DEBUG_DEBUG_MUX_BIT_4)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TEST_DEBUG_DEBUG_MUX_BIT_5)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TEST_DEBUG_DEBUG_MUX_BIT_6)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TEST_DEBUG_DEBUG_MUX_BIT_7)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TEST_DEBUG_DEBUG_MUX_BIT_8)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TEST_DEBUG_DEBUG_MUX_BIT_9)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TEST_DEBUG_DEBUG_MUX_BIT_10)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TEST_DEBUG_DEBUG_MUX_BIT_11)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TEST_DEBUG_DEBUG_MUX_BIT_12)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TEST_DEBUG_DEBUG_MUX_BIT_13)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TEST_DEBUG_DEBUG_MUX_BIT_14)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TEST_DEBUG_DEBUG_MUX_BIT_15)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TEST_DEBUG_DEBUG_MUX_BIT_16)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TEST_DEBUG_DEBUG_MUX_BIT_17)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TEST_DEBUG_DEBUG_MUX_BIT_18)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TEST_DEBUG_DEBUG_MUX_BIT_19)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TEST_DEBUG_DEBUG_MUX_BIT_20)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TEST_DEBUG_DEBUG_MUX_BIT_21)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TEST_DEBUG_DEBUG_MUX_BIT_22)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TEST_DEBUG_DEBUG_MUX_BIT_23)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TEST_DEBUG_DEBUG_MUX_BIT_24)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TEST_DEBUG_DEBUG_MUX_BIT_25)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TEST_DEBUG_DEBUG_MUX_BIT_26)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TEST_DEBUG_DEBUG_MUX_BIT_27)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TEST_DEBUG_DEBUG_MUX_BIT_28)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TEST_DEBUG_DEBUG_MUX_BIT_29)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TEST_DEBUG_DEBUG_MUX_BIT_30)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TEST_DEBUG_DEBUG_MUX_BIT_31)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TEST_DEBUG_DEBUG_MUX_RD_CTRL)] = WCD9360_RW, +[WCD9360_REG(WCD9360_TEST_DEBUG_DEBUG_MUX_RD_7_0)] = WCD9360_RO, +[WCD9360_REG(WCD9360_TEST_DEBUG_DEBUG_MUX_RD_15_8)] = WCD9360_RO, +[WCD9360_REG(WCD9360_TEST_DEBUG_DEBUG_MUX_RD_23_16)] = WCD9360_RO, +[WCD9360_REG(WCD9360_TEST_DEBUG_DEBUG_MUX_RD_31_24)] = WCD9360_RO, + +}; + +const u8 * const wcd9360_reg[WCD9360_PAGE_MAX] = { +[WCD9360_PAGE_0] = wcd9360_page0_reg_access, +[WCD9360_PAGE_1] = wcd9360_page1_reg_access, +[WCD9360_PAGE_2] = wcd9360_page2_reg_access, +[WCD9360_PAGE_4] = wcd9360_page4_reg_access, +[WCD9360_PAGE_6] = wcd9360_page6_reg_access, +[WCD9360_PAGE_7] = wcd9360_page7_reg_access, +[WCD9360_PAGE_10] = wcd9360_page10_reg_access, +[WCD9360_PAGE_11] = wcd9360_page11_reg_access, +[WCD9360_PAGE_12] = wcd9360_page12_reg_access, +[WCD9360_PAGE_13] = wcd9360_page13_reg_access, +[WCD9360_PAGE_80] = wcd9360_page80_reg_access, +[WCD9360_PAGE_128] = wcd9360_page128_reg_access, +}; + +#endif diff --git a/audio-kernel/asoc/codecs/wcd9360/wcd9360-dsp-cntl.c b/audio-kernel/asoc/codecs/wcd9360/wcd9360-dsp-cntl.c new file mode 100644 index 000000000..37536f86c --- /dev/null +++ b/audio-kernel/asoc/codecs/wcd9360/wcd9360-dsp-cntl.c @@ -0,0 +1,1357 @@ +/* + * Copyright (c) 2016-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 +#include +#include +#include +#include +#include +#include +#include "wcd9360.h" +#include "wcd9360-dsp-cntl.h" +#include "../wcd9xxx-irq.h" +#include "../core.h" + +#define WCD_CNTL_DIR_NAME_LEN_MAX 32 +#define WCD_CPE_FLL_MAX_RETRIES 5 +#define WCD_MEM_ENABLE_MAX_RETRIES 20 +#define WCD_DSP_BOOT_TIMEOUT_MS 3000 +#define WCD_SYSFS_ENTRY_MAX_LEN 8 +#define WCD_PROCFS_ENTRY_MAX_LEN 16 +#define WCD_9360_RAMDUMP_START_ADDR 0x20100000 +#define WCD_9360_RAMDUMP_SIZE ((1024 * 1024) - 128) +#define WCD_MISCDEV_CMD_MAX_LEN 2 + +#define WCD_CNTL_MUTEX_LOCK(codec, lock) \ +{ \ + dev_dbg(codec->dev, "%s: mutex_lock(%s)\n", \ + __func__, __stringify_1(lock)); \ + mutex_lock(&lock); \ +} + +#define WCD_CNTL_MUTEX_UNLOCK(codec, lock) \ +{ \ + dev_dbg(codec->dev, "%s: mutex_unlock(%s)\n", \ + __func__, __stringify_1(lock)); \ + mutex_unlock(&lock); \ +} + +enum wcd_mem_type { + WCD_MEM_TYPE_ALWAYS_ON, + WCD_MEM_TYPE_SWITCHABLE, +}; + +struct wcd_cntl_attribute { + struct attribute attr; + ssize_t (*show)(struct wcd_dsp_cntl *cntl, char *buf); + ssize_t (*store)(struct wcd_dsp_cntl *cntl, const char *buf, + ssize_t count); +}; + +#define WCD_CNTL_ATTR(_name, _mode, _show, _store) \ +static struct wcd_cntl_attribute cntl_attr_##_name = { \ + .attr = {.name = __stringify(_name), .mode = _mode}, \ + .show = _show, \ + .store = _store, \ +} + +#define to_wcd_cntl_attr(a) \ + container_of((a), struct wcd_cntl_attribute, attr) + +#define to_wcd_cntl(kobj) \ + container_of((kobj), struct wcd_dsp_cntl, wcd_kobj) + +static u8 mem_enable_values[] = { + 0xFE, 0xFC, 0xF8, 0xF0, + 0xE0, 0xC0, 0x80, 0x00, +}; + +static ssize_t wdsp_boot_show(struct wcd_dsp_cntl *cntl, char *buf) +{ + return snprintf(buf, WCD_SYSFS_ENTRY_MAX_LEN, + "%u", cntl->boot_reqs); +} + +static ssize_t wdsp_boot_store(struct wcd_dsp_cntl *cntl, + const char *buf, ssize_t count) +{ + u32 val; + bool vote; + int ret; + + ret = kstrtou32(buf, 10, &val); + if (ret) { + dev_err(cntl->codec->dev, + "%s: Invalid entry, ret = %d\n", __func__, ret); + return -EINVAL; + } + + if (val > 0) { + cntl->boot_reqs++; + vote = true; + } else { + cntl->boot_reqs--; + vote = false; + } + + if (cntl->m_dev && cntl->m_ops && + cntl->m_ops->vote_for_dsp) + ret = cntl->m_ops->vote_for_dsp(cntl->m_dev, vote); + else + ret = -EINVAL; + + if (ret < 0) + dev_err(cntl->codec->dev, + "%s: failed to %s dsp\n", __func__, + vote ? "enable" : "disable"); + return count; +} + +WCD_CNTL_ATTR(boot, 0660, wdsp_boot_show, wdsp_boot_store); + +static ssize_t wcd_cntl_sysfs_show(struct kobject *kobj, + struct attribute *attr, char *buf) +{ + struct wcd_cntl_attribute *wcd_attr = to_wcd_cntl_attr(attr); + struct wcd_dsp_cntl *cntl = to_wcd_cntl(kobj); + ssize_t ret = -EINVAL; + + if (cntl && wcd_attr->show) + ret = wcd_attr->show(cntl, buf); + + return ret; +} + +static ssize_t wcd_cntl_sysfs_store(struct kobject *kobj, + struct attribute *attr, const char *buf, + size_t count) +{ + struct wcd_cntl_attribute *wcd_attr = to_wcd_cntl_attr(attr); + struct wcd_dsp_cntl *cntl = to_wcd_cntl(kobj); + ssize_t ret = -EINVAL; + + if (cntl && wcd_attr->store) + ret = wcd_attr->store(cntl, buf, count); + + return ret; +} + +static const struct sysfs_ops wcd_cntl_sysfs_ops = { + .show = wcd_cntl_sysfs_show, + .store = wcd_cntl_sysfs_store, +}; + +static struct kobj_type wcd_cntl_ktype = { + .sysfs_ops = &wcd_cntl_sysfs_ops, +}; + +static void wcd_cntl_change_online_state(struct wcd_dsp_cntl *cntl, + u8 online) +{ + struct wdsp_ssr_entry *ssr_entry = &cntl->ssr_entry; + unsigned long ret; + + WCD_CNTL_MUTEX_LOCK(cntl->codec, cntl->ssr_mutex); + ssr_entry->offline = !online; + /* Make sure the write is complete */ + wmb(); + ret = xchg(&ssr_entry->offline_change, 1); + wake_up_interruptible(&ssr_entry->offline_poll_wait); + dev_dbg(cntl->codec->dev, + "%s: requested %u, offline %u offline_change %u, ret = %ldn", + __func__, online, ssr_entry->offline, + ssr_entry->offline_change, ret); + WCD_CNTL_MUTEX_UNLOCK(cntl->codec, cntl->ssr_mutex); +} + +static ssize_t wdsp_ssr_entry_read(struct snd_info_entry *entry, + void *file_priv_data, struct file *file, + char __user *buf, size_t count, loff_t pos) +{ + int len = 0; + char buffer[WCD_PROCFS_ENTRY_MAX_LEN]; + struct wcd_dsp_cntl *cntl; + struct wdsp_ssr_entry *ssr_entry; + ssize_t ret; + u8 offline; + + cntl = (struct wcd_dsp_cntl *) entry->private_data; + if (!cntl) { + pr_err("%s: Invalid private data for SSR procfs entry\n", + __func__); + return -EINVAL; + } + + ssr_entry = &cntl->ssr_entry; + + WCD_CNTL_MUTEX_LOCK(cntl->codec, cntl->ssr_mutex); + offline = ssr_entry->offline; + /* Make sure the read is complete */ + rmb(); + dev_dbg(cntl->codec->dev, "%s: offline = %s\n", __func__, + offline ? "true" : "false"); + len = snprintf(buffer, sizeof(buffer), "%s\n", + offline ? "OFFLINE" : "ONLINE"); + ret = simple_read_from_buffer(buf, count, &pos, buffer, len); + WCD_CNTL_MUTEX_UNLOCK(cntl->codec, cntl->ssr_mutex); + + return ret; +} + +static unsigned int wdsp_ssr_entry_poll(struct snd_info_entry *entry, + void *private_data, struct file *file, + poll_table *wait) +{ + struct wcd_dsp_cntl *cntl; + struct wdsp_ssr_entry *ssr_entry; + unsigned int ret = 0; + + if (!entry || !entry->private_data) { + pr_err("%s: %s is NULL\n", __func__, + (!entry) ? "entry" : "private_data"); + return -EINVAL; + } + + cntl = (struct wcd_dsp_cntl *) entry->private_data; + ssr_entry = &cntl->ssr_entry; + + dev_dbg(cntl->codec->dev, "%s: Poll wait, offline = %u\n", + __func__, ssr_entry->offline); + poll_wait(file, &ssr_entry->offline_poll_wait, wait); + dev_dbg(cntl->codec->dev, "%s: Woken up Poll wait, offline = %u\n", + __func__, ssr_entry->offline); + + WCD_CNTL_MUTEX_LOCK(cntl->codec, cntl->ssr_mutex); + if (xchg(&ssr_entry->offline_change, 0)) + ret = POLLIN | POLLPRI | POLLRDNORM; + dev_dbg(cntl->codec->dev, "%s: ret (%d) from poll_wait\n", + __func__, ret); + WCD_CNTL_MUTEX_UNLOCK(cntl->codec, cntl->ssr_mutex); + + return ret; +} + +static struct snd_info_entry_ops wdsp_ssr_entry_ops = { + .read = wdsp_ssr_entry_read, + .poll = wdsp_ssr_entry_poll, +}; + +static int wcd_cntl_cpe_fll_calibrate(struct wcd_dsp_cntl *cntl) +{ + struct snd_soc_codec *codec = cntl->codec; + int ret = 0, retry = 0; + u8 cal_lsb, cal_msb; + u8 lock_det; + + /* Make sure clocks are gated */ + snd_soc_update_bits(codec, WCD9360_CPE_SS_CPE_CTL, + 0x05, 0x00); + + /* Enable CPE FLL reference clock */ + snd_soc_update_bits(codec, WCD9360_CLK_SYS_MCLK2_PRG1, + 0x80, 0x80); + + snd_soc_update_bits(codec, WCD9360_CPE_FLL_USER_CTL_5, + 0xF3, 0x13); + snd_soc_write(codec, WCD9360_CPE_FLL_L_VAL_CTL_0, 0x50); + + /* Disable CPAR reset and Enable CPAR clk */ + snd_soc_update_bits(codec, WCD9360_CPE_SS_CPAR_CTL, + 0x02, 0x02); + + /* Write calibration l-value based on cdc clk rate */ + if (cntl->clk_rate == 9600000) { + cal_lsb = 0x6d; + cal_msb = 0x00; + } else { + cal_lsb = 0x56; + cal_msb = 0x00; + } + snd_soc_write(codec, WCD9360_CPE_FLL_USER_CTL_6, cal_lsb); + snd_soc_write(codec, WCD9360_CPE_FLL_USER_CTL_7, cal_msb); + + /* FLL mode to follow power up sequence */ + snd_soc_update_bits(codec, WCD9360_CPE_FLL_FLL_MODE, + 0x60, 0x00); + + /* HW controlled CPE FLL */ + snd_soc_update_bits(codec, WCD9360_CPE_FLL_FLL_MODE, + 0x80, 0x80); + + /* Force on CPE FLL */ + snd_soc_update_bits(codec, WCD9360_CPE_SS_CPAR_CFG, + 0x04, 0x04); + + do { + /* Time for FLL calibration to complete */ + usleep_range(1000, 1100); + lock_det = snd_soc_read(codec, WCD9360_CPE_FLL_STATUS_3); + retry++; + } while (!(lock_det & 0x01) && + retry <= WCD_CPE_FLL_MAX_RETRIES); + + if (!(lock_det & 0x01)) { + dev_err(codec->dev, "%s: lock detect not set, 0x%02x\n", + __func__, lock_det); + ret = -EIO; + goto err_lock_det; + } + + snd_soc_update_bits(codec, WCD9360_CPE_FLL_FLL_MODE, + 0x60, 0x20); + snd_soc_update_bits(codec, WCD9360_CPE_SS_CPAR_CFG, + 0x04, 0x00); + return ret; + +err_lock_det: + /* Undo the register settings */ + snd_soc_update_bits(codec, WCD9360_CPE_SS_CPAR_CFG, + 0x04, 0x00); + snd_soc_update_bits(codec, WCD9360_CPE_FLL_FLL_MODE, + 0x80, 0x00); + snd_soc_update_bits(codec, WCD9360_CPE_SS_CPAR_CTL, + 0x02, 0x00); + return ret; +} + +static void wcd_cntl_config_cpar(struct wcd_dsp_cntl *cntl) +{ + struct snd_soc_codec *codec = cntl->codec; + u8 nom_lo, nom_hi, svs2_lo, svs2_hi; + + /* Configure CPAR */ + nom_hi = svs2_hi = 0; + if (cntl->clk_rate == 9600000) { + nom_lo = 0x90; + svs2_lo = 0x50; + } else { + nom_lo = 0x70; + svs2_lo = 0x3e; + } + + snd_soc_write(codec, WCD9360_TEST_DEBUG_LVAL_NOM_LOW, nom_lo); + snd_soc_write(codec, WCD9360_TEST_DEBUG_LVAL_NOM_HIGH, nom_hi); + snd_soc_write(codec, WCD9360_TEST_DEBUG_LVAL_SVS_SVS2_LOW, svs2_lo); + snd_soc_write(codec, WCD9360_TEST_DEBUG_LVAL_SVS_SVS2_HIGH, svs2_hi); + + snd_soc_update_bits(codec, WCD9360_CPE_SS_PWR_CPEFLL_CTL, + 0x03, 0x03); +} + +static int wcd_cntl_cpe_fll_ctrl(struct wcd_dsp_cntl *cntl, + bool enable) +{ + struct snd_soc_codec *codec = cntl->codec; + int ret = 0; + + if (enable) { + ret = wcd_cntl_cpe_fll_calibrate(cntl); + if (ret < 0) { + dev_err(codec->dev, + "%s: cpe_fll_cal failed, err = %d\n", + __func__, ret); + goto done; + } + + wcd_cntl_config_cpar(cntl); + + /* Enable AHB CLK and CPE CLK*/ + snd_soc_update_bits(codec, WCD9360_CPE_SS_CPE_CTL, + 0x05, 0x05); + } else { + /* Disable AHB CLK and CPE CLK */ + snd_soc_update_bits(codec, WCD9360_CPE_SS_CPE_CTL, + 0x05, 0x00); + /* Reset the CPAR mode for CPE FLL */ + snd_soc_write(codec, WCD9360_CPE_FLL_FLL_MODE, 0x20); + snd_soc_update_bits(codec, WCD9360_CPE_SS_CPAR_CFG, + 0x04, 0x00); + snd_soc_update_bits(codec, WCD9360_CPE_SS_CPAR_CTL, + 0x02, 0x00); + } +done: + return ret; +} + +static int wcd_cntl_clocks_enable(struct wcd_dsp_cntl *cntl) +{ + struct snd_soc_codec *codec = cntl->codec; + int ret; + + WCD_CNTL_MUTEX_LOCK(codec, cntl->clk_mutex); + /* Enable codec clock */ + if (cntl->cdc_cb && cntl->cdc_cb->cdc_clk_en) + ret = cntl->cdc_cb->cdc_clk_en(codec, true); + else + ret = -EINVAL; + + if (ret < 0) { + dev_err(codec->dev, + "%s: Failed to enable cdc clk, err = %d\n", + __func__, ret); + goto done; + } + /* Pull CPAR out of reset */ + snd_soc_update_bits(codec, WCD9360_CPE_SS_CPAR_CTL, 0x04, 0x00); + + /* Configure and Enable CPE FLL clock */ + ret = wcd_cntl_cpe_fll_ctrl(cntl, true); + if (ret < 0) { + dev_err(codec->dev, + "%s: Failed to enable cpe clk, err = %d\n", + __func__, ret); + goto err_cpe_clk; + } + cntl->is_clk_enabled = true; + + /* Ungate the CPR clock */ + snd_soc_update_bits(codec, WCD9360_CODEC_RPM_CLK_GATE, 0x10, 0x00); +done: + WCD_CNTL_MUTEX_UNLOCK(codec, cntl->clk_mutex); + return ret; + +err_cpe_clk: + if (cntl->cdc_cb && cntl->cdc_cb->cdc_clk_en) + cntl->cdc_cb->cdc_clk_en(codec, false); + + snd_soc_update_bits(codec, WCD9360_CPE_SS_CPAR_CTL, 0x04, 0x04); + WCD_CNTL_MUTEX_UNLOCK(codec, cntl->clk_mutex); + return ret; +} + +static int wcd_cntl_clocks_disable(struct wcd_dsp_cntl *cntl) +{ + struct snd_soc_codec *codec = cntl->codec; + int ret = 0; + + WCD_CNTL_MUTEX_LOCK(codec, cntl->clk_mutex); + if (!cntl->is_clk_enabled) { + dev_info(codec->dev, "%s: clocks already disabled\n", + __func__); + goto done; + } + + /* Gate the CPR clock */ + snd_soc_update_bits(codec, WCD9360_CODEC_RPM_CLK_GATE, 0x10, 0x10); + + /* Disable CPE FLL clock */ + ret = wcd_cntl_cpe_fll_ctrl(cntl, false); + if (ret < 0) + dev_err(codec->dev, + "%s: Failed to disable cpe clk, err = %d\n", + __func__, ret); + + /* + * Even if CPE FLL disable failed, go ahead and disable + * the codec clock + */ + if (cntl->cdc_cb && cntl->cdc_cb->cdc_clk_en) + ret = cntl->cdc_cb->cdc_clk_en(codec, false); + else + ret = -EINVAL; + + cntl->is_clk_enabled = false; + + /* Put CPAR in reset */ + snd_soc_update_bits(codec, WCD9360_CPE_SS_CPAR_CTL, 0x04, 0x04); +done: + WCD_CNTL_MUTEX_UNLOCK(codec, cntl->clk_mutex); + return ret; +} + +static void wcd_cntl_cpar_ctrl(struct wcd_dsp_cntl *cntl, + bool enable) +{ + struct snd_soc_codec *codec = cntl->codec; + + if (enable) + snd_soc_update_bits(codec, WCD9360_CPE_SS_CPAR_CTL, 0x03, 0x03); + else + snd_soc_update_bits(codec, WCD9360_CPE_SS_CPAR_CTL, 0x03, 0x00); +} + +static int wcd_cntl_enable_memory(struct wcd_dsp_cntl *cntl, + enum wcd_mem_type mem_type) +{ + struct snd_soc_codec *codec = cntl->codec; + struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent); + int loop_cnt = 0; + u8 status = 0; + int ret = 0; + + + switch (mem_type) { + + case WCD_MEM_TYPE_ALWAYS_ON: + + /* 512KB of always on region */ + wcd9xxx_slim_write_repeat(wcd9xxx, + WCD9360_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_0, + ARRAY_SIZE(mem_enable_values), + mem_enable_values); + wcd9xxx_slim_write_repeat(wcd9xxx, + WCD9360_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_1, + ARRAY_SIZE(mem_enable_values), + mem_enable_values); + break; + + case WCD_MEM_TYPE_SWITCHABLE: + + snd_soc_update_bits(codec, WCD9360_TEST_DEBUG_MEM_CTRL, + 0x80, 0x80); + do { + loop_cnt++; + /* Time to enable the power domain for memory */ + usleep_range(100, 150); + } while ((status & 0x02) != 0x02 && + loop_cnt != WCD_MEM_ENABLE_MAX_RETRIES); + + if ((status & 0x02) != 0x02) { + dev_err(cntl->codec->dev, + "%s: power domain not enabled, status = 0x%02x\n", + __func__, status); + ret = -EIO; + goto done; + } + + /* Rest of the memory */ + wcd9xxx_slim_write_repeat(wcd9xxx, + WCD9360_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_2, + ARRAY_SIZE(mem_enable_values), + mem_enable_values); + wcd9xxx_slim_write_repeat(wcd9xxx, + WCD9360_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_3, + ARRAY_SIZE(mem_enable_values), + mem_enable_values); + + snd_soc_write(codec, WCD9360_CPE_SS_PWR_CPE_DRAM1_SHUTDOWN, + 0x05); + break; + + default: + dev_err(cntl->codec->dev, "%s: Invalid mem_type %d\n", + __func__, mem_type); + ret = -EINVAL; + break; + } +done: + /* Make sure Deep sleep of memories is enabled for all banks */ + snd_soc_write(codec, WCD9360_CPE_SS_PWR_CPE_SYSMEM_DEEPSLP_0, 0xFF); + snd_soc_write(codec, WCD9360_CPE_SS_PWR_CPE_SYSMEM_DEEPSLP_1, 0x0F); + + return ret; +} + +static void wcd_cntl_disable_memory(struct wcd_dsp_cntl *cntl, + enum wcd_mem_type mem_type) +{ + struct snd_soc_codec *codec = cntl->codec; + + switch (mem_type) { + case WCD_MEM_TYPE_ALWAYS_ON: + snd_soc_write(codec, WCD9360_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_1, + 0xFF); + snd_soc_write(codec, WCD9360_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_0, + 0xFF); + break; + case WCD_MEM_TYPE_SWITCHABLE: + snd_soc_write(codec, WCD9360_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_3, + 0xFF); + snd_soc_write(codec, WCD9360_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_2, + 0xFF); + snd_soc_write(codec, WCD9360_CPE_SS_PWR_CPE_DRAM1_SHUTDOWN, + 0x07); + + snd_soc_update_bits(codec, WCD9360_TEST_DEBUG_MEM_CTRL, + 0x80, 0x00); + break; + default: + dev_err(cntl->codec->dev, "%s: Invalid mem_type %d\n", + __func__, mem_type); + break; + } + + snd_soc_write(codec, WCD9360_CPE_SS_PWR_CPE_SYSMEM_DEEPSLP_0, 0xFF); + snd_soc_write(codec, WCD9360_CPE_SS_PWR_CPE_SYSMEM_DEEPSLP_1, 0x0F); +} + +static void wcd_cntl_do_shutdown(struct wcd_dsp_cntl *cntl) +{ + struct snd_soc_codec *codec = cntl->codec; + + /* Disable WDOG */ + snd_soc_update_bits(codec, WCD9360_CPE_SS_WDOG_CFG, + 0x3F, 0x01); + + /* Put WDSP in reset state */ + snd_soc_update_bits(codec, WCD9360_CPE_SS_CPE_CTL, + 0x02, 0x00); + + /* If DSP transitions from boot to shutdown, then vote for SVS */ + if (cntl->is_wdsp_booted) + cntl->cdc_cb->cdc_vote_svs(codec, true); + cntl->is_wdsp_booted = false; +} + +static int wcd_cntl_do_boot(struct wcd_dsp_cntl *cntl) +{ + struct snd_soc_codec *codec = cntl->codec; + int ret = 0; + + /* + * Debug mode is set from debugfs file node. If debug_mode + * is set, then do not configure the watchdog timer. This + * will be required for debugging the DSP firmware. + */ + if (cntl->debug_mode) { + snd_soc_update_bits(codec, WCD9360_CPE_SS_WDOG_CFG, + 0x3F, 0x01); + } else { + snd_soc_update_bits(codec, WCD9360_CPE_SS_WDOG_CFG, + 0x3F, 0x21); + } + + /* Make sure all the error interrupts are cleared */ + snd_soc_write(codec, WCD9360_CPE_SS_SS_ERROR_INT_CLEAR_0A, 0xFF); + snd_soc_write(codec, WCD9360_CPE_SS_SS_ERROR_INT_CLEAR_0B, 0xFF); + + reinit_completion(&cntl->boot_complete); + + /* Remove WDSP out of reset */ + snd_soc_update_bits(codec, WCD9360_CPE_SS_CPE_CTL, + 0x02, 0x02); + + /* + * In debug mode, DSP may not boot up normally, + * wait indefinitely for DSP to boot. + */ + if (cntl->debug_mode) { + wait_for_completion(&cntl->boot_complete); + dev_dbg(codec->dev, "%s: WDSP booted in dbg mode\n", __func__); + cntl->is_wdsp_booted = true; + goto done; + } + + /* Boot in normal mode */ + ret = wait_for_completion_timeout(&cntl->boot_complete, + msecs_to_jiffies(WCD_DSP_BOOT_TIMEOUT_MS)); + if (!ret) { + dev_err(codec->dev, "%s: WDSP boot timed out\n", + __func__); + ret = -ETIMEDOUT; + goto err_boot; + } else { + /* + * Re-initialize the return code to 0, as in success case, + * it will hold the remaining time for completion timeout + */ + ret = 0; + } + + dev_dbg(codec->dev, "%s: WDSP booted in normal mode\n", __func__); + cntl->is_wdsp_booted = true; + + /* Enable WDOG */ + snd_soc_update_bits(codec, WCD9360_CPE_SS_WDOG_CFG, + 0x10, 0x10); +done: + /* If dsp booted up, then remove vote on SVS */ + if (cntl->is_wdsp_booted) + cntl->cdc_cb->cdc_vote_svs(codec, false); + + return ret; +err_boot: + /* call shutdown to perform cleanup */ + wcd_cntl_do_shutdown(cntl); + return ret; +} + +static irqreturn_t wcd_cntl_ipc_irq(int irq, void *data) +{ + struct wcd_dsp_cntl *cntl = data; + int ret; + + complete(&cntl->boot_complete); + + if (cntl->m_dev && cntl->m_ops && + cntl->m_ops->signal_handler) + ret = cntl->m_ops->signal_handler(cntl->m_dev, WDSP_IPC1_INTR, + NULL); + else + ret = -EINVAL; + + if (ret < 0) + dev_err(cntl->codec->dev, + "%s: Failed to handle irq %d\n", __func__, irq); + + return IRQ_HANDLED; +} + +static irqreturn_t wcd_cntl_err_irq(int irq, void *data) +{ + struct wcd_dsp_cntl *cntl = data; + struct snd_soc_codec *codec = cntl->codec; + struct wdsp_err_signal_arg arg; + u16 status = 0; + u8 reg_val; + int ret = 0; + + reg_val = snd_soc_read(codec, WCD9360_CPE_SS_SS_ERROR_INT_STATUS_0A); + status = status | reg_val; + + reg_val = snd_soc_read(codec, WCD9360_CPE_SS_SS_ERROR_INT_STATUS_0B); + status = status | (reg_val << 8); + + dev_info(codec->dev, "%s: error interrupt status = 0x%x\n", + __func__, status); + + if ((status & cntl->irqs.fatal_irqs) && + (cntl->m_dev && cntl->m_ops && cntl->m_ops->signal_handler)) { + arg.mem_dumps_enabled = cntl->ramdump_enable; + arg.remote_start_addr = WCD_9360_RAMDUMP_START_ADDR; + arg.dump_size = WCD_9360_RAMDUMP_SIZE; + ret = cntl->m_ops->signal_handler(cntl->m_dev, WDSP_ERR_INTR, + &arg); + if (ret < 0) + dev_err(cntl->codec->dev, + "%s: Failed to handle fatal irq 0x%x\n", + __func__, status & cntl->irqs.fatal_irqs); + wcd_cntl_change_online_state(cntl, 0); + } else { + dev_err(cntl->codec->dev, "%s: Invalid signal_handler\n", + __func__); + } + + return IRQ_HANDLED; +} + +static int wcd_control_handler(struct device *dev, void *priv_data, + enum wdsp_event_type event, void *data) +{ + struct wcd_dsp_cntl *cntl = priv_data; + struct snd_soc_codec *codec = cntl->codec; + int ret = 0; + + switch (event) { + case WDSP_EVENT_POST_INIT: + case WDSP_EVENT_POST_DLOAD_CODE: + case WDSP_EVENT_DLOAD_FAILED: + case WDSP_EVENT_POST_SHUTDOWN: + + /* Disable CPAR */ + wcd_cntl_cpar_ctrl(cntl, false); + /* Disable all the clocks */ + ret = wcd_cntl_clocks_disable(cntl); + if (ret < 0) + dev_err(codec->dev, + "%s: Failed to disable clocks, err = %d\n", + __func__, ret); + + if (event == WDSP_EVENT_POST_DLOAD_CODE) + /* Mark DSP online since code download is complete */ + wcd_cntl_change_online_state(cntl, 1); + break; + + case WDSP_EVENT_PRE_DLOAD_DATA: + case WDSP_EVENT_PRE_DLOAD_CODE: + + /* Enable all the clocks */ + ret = wcd_cntl_clocks_enable(cntl); + if (ret < 0) { + dev_err(codec->dev, + "%s: Failed to enable clocks, err = %d\n", + __func__, ret); + goto done; + } + + /* Enable CPAR */ + wcd_cntl_cpar_ctrl(cntl, true); + + if (event == WDSP_EVENT_PRE_DLOAD_CODE) + wcd_cntl_enable_memory(cntl, WCD_MEM_TYPE_ALWAYS_ON); + else if (event == WDSP_EVENT_PRE_DLOAD_DATA) + wcd_cntl_enable_memory(cntl, WCD_MEM_TYPE_SWITCHABLE); + break; + + case WDSP_EVENT_DO_BOOT: + + ret = wcd_cntl_do_boot(cntl); + if (ret < 0) + dev_err(codec->dev, + "%s: WDSP boot failed, err = %d\n", + __func__, ret); + break; + + case WDSP_EVENT_DO_SHUTDOWN: + + wcd_cntl_do_shutdown(cntl); + wcd_cntl_disable_memory(cntl, WCD_MEM_TYPE_SWITCHABLE); + break; + + default: + dev_dbg(codec->dev, "%s: unhandled event %d\n", + __func__, event); + } + +done: + return ret; +} + +static int wcd_cntl_sysfs_init(char *dir, struct wcd_dsp_cntl *cntl) +{ + struct snd_soc_codec *codec = cntl->codec; + int ret = 0; + + ret = kobject_init_and_add(&cntl->wcd_kobj, &wcd_cntl_ktype, + kernel_kobj, dir); + if (ret < 0) { + dev_err(codec->dev, + "%s: Failed to add kobject %s, err = %d\n", + __func__, dir, ret); + goto done; + } + + ret = sysfs_create_file(&cntl->wcd_kobj, &cntl_attr_boot.attr); + if (ret < 0) { + dev_err(codec->dev, + "%s: Failed to add wdsp_boot sysfs entry to %s\n", + __func__, dir); + goto fail_create_file; + } + + return ret; + +fail_create_file: + kobject_put(&cntl->wcd_kobj); +done: + return ret; +} + +static void wcd_cntl_sysfs_remove(struct wcd_dsp_cntl *cntl) +{ + sysfs_remove_file(&cntl->wcd_kobj, &cntl_attr_boot.attr); + kobject_put(&cntl->wcd_kobj); +} + +static void wcd_cntl_debugfs_init(char *dir, struct wcd_dsp_cntl *cntl) +{ + struct snd_soc_codec *codec = cntl->codec; + + cntl->entry = debugfs_create_dir(dir, NULL); + if (IS_ERR_OR_NULL(dir)) { + dev_err(codec->dev, "%s debugfs_create_dir failed for %s\n", + __func__, dir); + goto done; + } + + debugfs_create_u32("debug_mode", 0644, + cntl->entry, &cntl->debug_mode); + debugfs_create_bool("ramdump_enable", 0644, + cntl->entry, &cntl->ramdump_enable); +done: + return; +} + +static void wcd_cntl_debugfs_remove(struct wcd_dsp_cntl *cntl) +{ + if (cntl) + debugfs_remove(cntl->entry); +} + +static int wcd_miscdev_release(struct inode *inode, struct file *filep) +{ + struct wcd_dsp_cntl *cntl = container_of(filep->private_data, + struct wcd_dsp_cntl, miscdev); + if (!cntl->m_dev || !cntl->m_ops || + !cntl->m_ops->vote_for_dsp) { + dev_err(cntl->codec->dev, + "%s: DSP not ready to boot\n", __func__); + return -EINVAL; + } + + /* Make sure the DSP users goes to zero upon closing dev node */ + while (cntl->boot_reqs > 0) { + cntl->m_ops->vote_for_dsp(cntl->m_dev, false); + cntl->boot_reqs--; + } + + return 0; +} + +static ssize_t wcd_miscdev_write(struct file *filep, const char __user *ubuf, + size_t count, loff_t *pos) +{ + struct wcd_dsp_cntl *cntl = container_of(filep->private_data, + struct wcd_dsp_cntl, miscdev); + char val[WCD_MISCDEV_CMD_MAX_LEN + 1]; + bool vote; + int ret = 0; + + memset(val, 0, WCD_MISCDEV_CMD_MAX_LEN + 1); + + if (count == 0 || count > WCD_MISCDEV_CMD_MAX_LEN) { + pr_err("%s: Invalid count = %zd\n", __func__, count); + ret = -EINVAL; + goto done; + } + + ret = copy_from_user(val, ubuf, count); + if (ret < 0) { + dev_err(cntl->codec->dev, + "%s: copy_from_user failed, err = %d\n", + __func__, ret); + ret = -EFAULT; + goto done; + } + + if (val[0] == '1') { + cntl->boot_reqs++; + vote = true; + } else if (val[0] == '0') { + if (cntl->boot_reqs == 0) { + dev_err(cntl->codec->dev, + "%s: WDSP already disabled\n", __func__); + ret = -EINVAL; + goto done; + } + cntl->boot_reqs--; + vote = false; + } else { + dev_err(cntl->codec->dev, "%s: Invalid value %s\n", + __func__, val); + ret = -EINVAL; + goto done; + } + + dev_dbg(cntl->codec->dev, + "%s: booted = %s, ref_cnt = %d, vote = %s\n", + __func__, cntl->is_wdsp_booted ? "true" : "false", + cntl->boot_reqs, vote ? "true" : "false"); + + if (cntl->m_dev && cntl->m_ops && + cntl->m_ops->vote_for_dsp) + ret = cntl->m_ops->vote_for_dsp(cntl->m_dev, vote); + else + ret = -EINVAL; +done: + if (ret) + return ret; + else + return count; +} + +static const struct file_operations wcd_miscdev_fops = { + .write = wcd_miscdev_write, + .release = wcd_miscdev_release, +}; + +static int wcd_cntl_miscdev_create(struct wcd_dsp_cntl *cntl) +{ + snprintf(cntl->miscdev_name, ARRAY_SIZE(cntl->miscdev_name), + "wcd_dsp%u_control", cntl->dsp_instance); + cntl->miscdev.minor = MISC_DYNAMIC_MINOR; + cntl->miscdev.name = cntl->miscdev_name; + cntl->miscdev.fops = &wcd_miscdev_fops; + cntl->miscdev.parent = cntl->codec->dev; + + return misc_register(&cntl->miscdev); +} + +static void wcd_cntl_miscdev_destroy(struct wcd_dsp_cntl *cntl) +{ + misc_deregister(&cntl->miscdev); +} + +static int wcd_control_init(struct device *dev, void *priv_data) +{ + struct wcd_dsp_cntl *cntl = priv_data; + struct snd_soc_codec *codec = cntl->codec; + struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent); + struct wcd9xxx_core_resource *core_res = &wcd9xxx->core_res; + int ret; + bool err_irq_requested = false; + + ret = wcd9xxx_request_irq(core_res, + cntl->irqs.cpe_ipc1_irq, + wcd_cntl_ipc_irq, "CPE IPC1", + cntl); + if (ret < 0) { + dev_err(codec->dev, + "%s: Failed to request cpe ipc irq, err = %d\n", + __func__, ret); + goto done; + } + + /* Unmask the fatal irqs */ + snd_soc_write(codec, WCD9360_CPE_SS_SS_ERROR_INT_MASK_0A, + ~(cntl->irqs.fatal_irqs & 0xFF)); + snd_soc_write(codec, WCD9360_CPE_SS_SS_ERROR_INT_MASK_0B, + ~((cntl->irqs.fatal_irqs >> 8) & 0xFF)); + + /* + * CPE ERR irq is used only for error reporting from WCD DSP, + * even if this request fails, DSP can be function normally. + * Continuing with init even if the CPE ERR irq request fails. + */ + if (wcd9xxx_request_irq(core_res, cntl->irqs.cpe_err_irq, + wcd_cntl_err_irq, "CPE ERR", cntl)) + dev_info(codec->dev, "%s: Failed request_irq(cpe_err_irq)", + __func__); + else + err_irq_requested = true; + + + /* Enable all the clocks */ + ret = wcd_cntl_clocks_enable(cntl); + if (ret < 0) { + dev_err(codec->dev, "%s: Failed to enable clocks, err = %d\n", + __func__, ret); + goto err_clk_enable; + } + wcd_cntl_cpar_ctrl(cntl, true); + + return 0; + +err_clk_enable: + /* Mask all error interrupts */ + snd_soc_write(codec, WCD9360_CPE_SS_SS_ERROR_INT_MASK_0A, 0xFF); + snd_soc_write(codec, WCD9360_CPE_SS_SS_ERROR_INT_MASK_0B, 0xFF); + + /* Free the irq's requested */ + wcd9xxx_free_irq(core_res, cntl->irqs.cpe_ipc1_irq, cntl); + + if (err_irq_requested) + wcd9xxx_free_irq(core_res, cntl->irqs.cpe_err_irq, cntl); +done: + return ret; +} + +static int wcd_control_deinit(struct device *dev, void *priv_data) +{ + struct wcd_dsp_cntl *cntl = priv_data; + struct snd_soc_codec *codec = cntl->codec; + struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent); + struct wcd9xxx_core_resource *core_res = &wcd9xxx->core_res; + + wcd_cntl_clocks_disable(cntl); + wcd_cntl_cpar_ctrl(cntl, false); + + /* Mask all error interrupts */ + snd_soc_write(codec, WCD9360_CPE_SS_SS_ERROR_INT_MASK_0A, 0xFF); + snd_soc_write(codec, WCD9360_CPE_SS_SS_ERROR_INT_MASK_0B, 0xFF); + + /* Free the irq's requested */ + wcd9xxx_free_irq(core_res, cntl->irqs.cpe_err_irq, cntl); + wcd9xxx_free_irq(core_res, cntl->irqs.cpe_ipc1_irq, cntl); + + return 0; +} + +static struct wdsp_cmpnt_ops control_ops = { + .init = wcd_control_init, + .deinit = wcd_control_deinit, + .event_handler = wcd_control_handler, +}; + +static int wcd_ctrl_component_bind(struct device *dev, + struct device *master, + void *data) +{ + struct wcd_dsp_cntl *cntl; + struct snd_soc_codec *codec; + struct snd_card *card; + struct snd_info_entry *entry; + char proc_name[WCD_PROCFS_ENTRY_MAX_LEN]; + char wcd_cntl_dir_name[WCD_CNTL_DIR_NAME_LEN_MAX]; + int ret = 0; + + if (!dev || !master || !data) { + pr_err("%s: Invalid parameters\n", __func__); + return -EINVAL; + } + + cntl = (struct wcd_dsp_cntl *) pahu_get_wcd_dsp_cntl(dev); + if (!cntl) { + dev_err(dev, "%s: Failed to get cntl reference\n", + __func__); + return -EINVAL; + } + + cntl->m_dev = master; + cntl->m_ops = data; + + if (!cntl->m_ops->register_cmpnt_ops) { + dev_err(dev, "%s: invalid master callback register_cmpnt_ops\n", + __func__); + ret = -EINVAL; + goto done; + } + + ret = cntl->m_ops->register_cmpnt_ops(master, dev, cntl, &control_ops); + if (ret) { + dev_err(dev, "%s: register_cmpnt_ops failed, err = %d\n", + __func__, ret); + goto done; + } + + ret = wcd_cntl_miscdev_create(cntl); + if (ret < 0) { + dev_err(dev, "%s: misc dev register failed, err = %d\n", + __func__, ret); + goto done; + } + + snprintf(wcd_cntl_dir_name, WCD_CNTL_DIR_NAME_LEN_MAX, + "%s%d", "wdsp", cntl->dsp_instance); + ret = wcd_cntl_sysfs_init(wcd_cntl_dir_name, cntl); + if (ret < 0) { + dev_err(dev, "%s: sysfs_init failed, err = %d\n", + __func__, ret); + goto err_sysfs_init; + } + + wcd_cntl_debugfs_init(wcd_cntl_dir_name, cntl); + + codec = cntl->codec; + card = codec->component.card->snd_card; + snprintf(proc_name, WCD_PROCFS_ENTRY_MAX_LEN, "%s%d%s", "cpe", + cntl->dsp_instance, "_state"); + entry = snd_info_create_card_entry(card, proc_name, card->proc_root); + if (!entry) { + /* Do not treat this as Fatal error */ + dev_err(dev, "%s: Failed to create procfs entry %s\n", + __func__, proc_name); + goto err_sysfs_init; + } + + cntl->ssr_entry.entry = entry; + cntl->ssr_entry.offline = 1; + entry->size = WCD_PROCFS_ENTRY_MAX_LEN; + entry->content = SNDRV_INFO_CONTENT_DATA; + entry->c.ops = &wdsp_ssr_entry_ops; + entry->private_data = cntl; + ret = snd_info_register(entry); + if (ret < 0) { + dev_err(dev, "%s: Failed to register entry %s, err = %d\n", + __func__, proc_name, ret); + snd_info_free_entry(entry); + /* Let bind still happen even if creating the entry failed */ + ret = 0; + } +done: + return ret; + +err_sysfs_init: + wcd_cntl_miscdev_destroy(cntl); + return ret; +} + +static void wcd_ctrl_component_unbind(struct device *dev, + struct device *master, + void *data) +{ + struct wcd_dsp_cntl *cntl; + + if (!dev) { + pr_err("%s: Invalid device\n", __func__); + return; + } + + cntl = (struct wcd_dsp_cntl *) pahu_get_wcd_dsp_cntl(dev); + if (!cntl) { + dev_err(dev, "%s: Failed to get cntl reference\n", + __func__); + return; + } + + cntl->m_dev = NULL; + cntl->m_ops = NULL; + + /* Remove the sysfs entries */ + wcd_cntl_sysfs_remove(cntl); + + /* Remove the debugfs entries */ + wcd_cntl_debugfs_remove(cntl); + + /* Remove the misc device */ + wcd_cntl_miscdev_destroy(cntl); +} + +static const struct component_ops wcd_ctrl_component_ops = { + .bind = wcd_ctrl_component_bind, + .unbind = wcd_ctrl_component_unbind, +}; + +/* + * wcd9360_dsp_ssr_event: handle the SSR event raised by caller. + * @cntl: Handle to the wcd_dsp_cntl structure + * @event: The SSR event to be handled + * + * Notifies the manager driver about the SSR event. + * Returns 0 on success and negative error code on error. + */ +int wcd9360_dsp_ssr_event(struct wcd_dsp_cntl *cntl, enum cdc_ssr_event event) +{ + int ret = 0; + + if (!cntl) { + pr_err("%s: Invalid handle to control\n", __func__); + return -EINVAL; + } + + if (!cntl->m_dev || !cntl->m_ops || !cntl->m_ops->signal_handler) { + dev_err(cntl->codec->dev, + "%s: Invalid signal_handler callback\n", __func__); + return -EINVAL; + } + + switch (event) { + case WCD_CDC_DOWN_EVENT: + ret = cntl->m_ops->signal_handler(cntl->m_dev, + WDSP_CDC_DOWN_SIGNAL, + NULL); + if (ret < 0) + dev_err(cntl->codec->dev, + "%s: WDSP_CDC_DOWN_SIGNAL failed, err = %d\n", + __func__, ret); + wcd_cntl_change_online_state(cntl, 0); + break; + case WCD_CDC_UP_EVENT: + ret = cntl->m_ops->signal_handler(cntl->m_dev, + WDSP_CDC_UP_SIGNAL, + NULL); + if (ret < 0) + dev_err(cntl->codec->dev, + "%s: WDSP_CDC_UP_SIGNAL failed, err = %d\n", + __func__, ret); + break; + default: + dev_err(cntl->codec->dev, "%s: Invalid event %d\n", + __func__, event); + ret = -EINVAL; + break; + } + + return ret; +} +EXPORT_SYMBOL(wcd9360_dsp_ssr_event); + +/* + * wcd9360_dsp_cntl_init: Initialize the wcd-dsp control + * @codec: pointer to the codec handle + * @params: Parameters required to initialize wcd-dsp control + * + * This API is expected to be invoked by the codec driver and + * provide information essential for the wcd dsp control to + * configure and initialize the dsp + */ +void wcd9360_dsp_cntl_init(struct snd_soc_codec *codec, + struct wcd_dsp_params *params, + struct wcd_dsp_cntl **cntl) +{ + struct wcd_dsp_cntl *control; + int ret; + + if (!codec || !params) { + pr_err("%s: Invalid handle to %s\n", __func__, + (!codec) ? "codec" : "params"); + *cntl = NULL; + return; + } + + if (*cntl) { + pr_err("%s: cntl is non NULL, maybe already initialized ?\n", + __func__); + return; + } + + if (!params->cb || !params->cb->cdc_clk_en || + !params->cb->cdc_vote_svs) { + dev_err(codec->dev, + "%s: clk_en and vote_svs callbacks must be provided\n", + __func__); + return; + } + + control = kzalloc(sizeof(*control), GFP_KERNEL); + if (!(control)) + return; + + control->codec = codec; + control->clk_rate = params->clk_rate; + control->cdc_cb = params->cb; + control->dsp_instance = params->dsp_instance; + memcpy(&control->irqs, ¶ms->irqs, sizeof(control->irqs)); + init_completion(&control->boot_complete); + mutex_init(&control->clk_mutex); + mutex_init(&control->ssr_mutex); + init_waitqueue_head(&control->ssr_entry.offline_poll_wait); + + /* + * The default state of WDSP is in SVS mode. + * Vote for SVS now, the vote will be removed only + * after DSP is booted up. + */ + control->cdc_cb->cdc_vote_svs(codec, true); + + /* + * If this is the last component needed by master to be ready, + * then component_bind will be called within the component_add. + * Hence, the data pointer should be assigned before component_add, + * so that we can access it during this component's bind call. + */ + *cntl = control; + ret = component_add(codec->dev, &wcd_ctrl_component_ops); + if (ret) { + dev_err(codec->dev, "%s: component_add failed, err = %d\n", + __func__, ret); + kfree(*cntl); + *cntl = NULL; + } +} +EXPORT_SYMBOL(wcd9360_dsp_cntl_init); + +/* + * wcd9360_dsp_cntl_deinit: De-initialize the wcd-dsp control + * @cntl: The struct wcd_dsp_cntl to de-initialize + * + * This API is intended to be invoked by the codec driver + * to de-initialize the wcd dsp control + */ +void wcd9360_dsp_cntl_deinit(struct wcd_dsp_cntl **cntl) +{ + struct wcd_dsp_cntl *control = *cntl; + struct snd_soc_codec *codec; + + /* If control is NULL, there is nothing to de-initialize */ + if (!control) + return; + codec = control->codec; + + /* + * Calling shutdown will cleanup all register states, + * irrespective of DSP was booted up or not. + */ + wcd_cntl_do_shutdown(control); + wcd_cntl_disable_memory(control, WCD_MEM_TYPE_SWITCHABLE); + wcd_cntl_disable_memory(control, WCD_MEM_TYPE_ALWAYS_ON); + + component_del(codec->dev, &wcd_ctrl_component_ops); + + mutex_destroy(&control->clk_mutex); + mutex_destroy(&control->ssr_mutex); + kfree(*cntl); + *cntl = NULL; +} +EXPORT_SYMBOL(wcd9360_dsp_cntl_deinit); diff --git a/audio-kernel/asoc/codecs/wcd9360/wcd9360-dsp-cntl.h b/audio-kernel/asoc/codecs/wcd9360/wcd9360-dsp-cntl.h new file mode 100644 index 000000000..db8b43105 --- /dev/null +++ b/audio-kernel/asoc/codecs/wcd9360/wcd9360-dsp-cntl.h @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2016-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 __WCD9360_DSP_CNTL_H__ +#define __WCD9360_DSP_CNTL_H__ + +#include +#include +#include + +enum cdc_ssr_event { + WCD_CDC_DOWN_EVENT, + WCD_CDC_UP_EVENT, +}; + +struct wcd_dsp_cdc_cb { + /* Callback to enable codec clock */ + int (*cdc_clk_en)(struct snd_soc_codec *, bool); + /* Callback to vote and unvote for SVS2 mode */ + void (*cdc_vote_svs)(struct snd_soc_codec *, bool); +}; + +struct wcd_dsp_irq_info { + /* IPC interrupt */ + int cpe_ipc1_irq; + + /* CPE error summary interrupt */ + int cpe_err_irq; + + /* + * Bit mask to indicate which of the + * error interrupts are to be considered + * as fatal. + */ + u16 fatal_irqs; +}; + +struct wcd_dsp_params { + struct wcd_dsp_cdc_cb *cb; + struct wcd_dsp_irq_info irqs; + + /* Rate at which the codec clock operates */ + u32 clk_rate; + + /* + * Represents the dsp instance, will be used + * to create sysfs and debugfs entries with + * directory wdsp + */ + u32 dsp_instance; +}; + +struct wdsp_ssr_entry { + u8 offline; + u8 offline_change; + wait_queue_head_t offline_poll_wait; + struct snd_info_entry *entry; +}; + +struct wcd_dsp_cntl { + /* Handle to codec */ + struct snd_soc_codec *codec; + + /* Clk rate of the codec clock */ + u32 clk_rate; + + /* Callbacks to codec driver */ + const struct wcd_dsp_cdc_cb *cdc_cb; + + /* Completion to indicate WDSP boot done */ + struct completion boot_complete; + + struct wcd_dsp_irq_info irqs; + u32 dsp_instance; + + /* Sysfs entries related */ + int boot_reqs; + struct kobject wcd_kobj; + + /* Debugfs related */ + struct dentry *entry; + u32 debug_mode; + bool ramdump_enable; + + /* WDSP manager drivers data */ + struct device *m_dev; + struct wdsp_mgr_ops *m_ops; + + /* clk related */ + struct mutex clk_mutex; + bool is_clk_enabled; + + /* Keep track of WDSP boot status */ + bool is_wdsp_booted; + + /* SSR related */ + struct wdsp_ssr_entry ssr_entry; + struct mutex ssr_mutex; + + /* Misc device related */ + char miscdev_name[256]; + struct miscdevice miscdev; +}; + +void wcd9360_dsp_cntl_init(struct snd_soc_codec *codec, + struct wcd_dsp_params *params, + struct wcd_dsp_cntl **cntl); +void wcd9360_dsp_cntl_deinit(struct wcd_dsp_cntl **cntl); +int wcd9360_dsp_ssr_event(struct wcd_dsp_cntl *cntl, enum cdc_ssr_event event); +#endif /* end __WCD_DSP_CONTROL_H__ */ diff --git a/audio-kernel/asoc/codecs/wcd9360/wcd9360-irq.h b/audio-kernel/asoc/codecs/wcd9360/wcd9360-irq.h new file mode 100644 index 000000000..fa7944770 --- /dev/null +++ b/audio-kernel/asoc/codecs/wcd9360/wcd9360-irq.h @@ -0,0 +1,56 @@ +/* + * 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 __WCD9360_IRQ_H_ +#define __WCD9360_IRQ_H_ + +enum { + /* INTR_REG 0 */ + WCD9360_IRQ_MISC = 1, + WCD9360_IRQ_RESERVED_0, + WCD9360_IRQ_LDO_RXTX_SCD, + WCD9360_IRQ_EAR_PA_SCD, + WCD9360_IRQ_AUX_PA_SCD, + WCD9360_IRQ_AUX_PA_CNP_COMPLETE, + WCD9360_IRQ_EAR_PA_CNP_COMPLETE, + /* INTR_REG 1 */ + WCD9360_IRQ_RESERVED_1, + WCD9360_IRQ_RESERVED_2, + WCD9360_IRQ_RESERVED_3, + WCD9360_IRQ_RESERVED_4, + WCD9360_IRQ_RESERVED_5, + WCD9360_IRQ_RESERVED_6, + WCD9360_IRQ_RESERVED_7, + WCD9360_IRQ_RESERVED_8, + /* INTR_REG 2 */ + WCD9360_IRQ_RESERVED_9, + WCD9360_IRQ_RESERVED_10, + WCD9360_IRQ_RESERVED_11, + WCD9360_IRQ_RESERVED_12, + WCD9360_IRQ_SOUNDWIRE, + WCD9360_IRQ_RESERVED_13, + WCD9360_IRQ_RCO_ERROR, + WCD9360_IRQ_CPE_ERROR, + /* INTR_REG 3 */ + WCD9360_IRQ_MAD_AUDIO, + WCD9360_IRQ_MAD_BEACON, + WCD9360_IRQ_MAD_ULTRASOUND, + WCD9360_IRQ_RESERVED_14, + WCD9360_IRQ_RESERVED_15, + WCD9360_IRQ_CPE1_INTR, + WCD9360_IRQ_CPE2_INTR, + WCD9360_IRQ_CPE_LPASS_ACK, + WCD9360_NUM_IRQS, +}; + +#endif diff --git a/audio-kernel/asoc/codecs/wcd9360/wcd9360-regmap.c b/audio-kernel/asoc/codecs/wcd9360/wcd9360-regmap.c new file mode 100644 index 000000000..b74c1fb5c --- /dev/null +++ b/audio-kernel/asoc/codecs/wcd9360/wcd9360-regmap.c @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2016-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 +#include +#include +#include "../core.h" +#include "../wcd9xxx-regmap.h" +#include "wcd9360-defaults.h" + +static bool wcd9360_is_readable_register(struct device *dev, unsigned int reg) +{ + u8 pg_num, reg_offset; + const u8 *reg_tbl = NULL; + + /* + * Get the page number from MSB of codec register. If its 0x80, assign + * the corresponding page index PAGE_0x80. + */ + pg_num = reg >> 8; + if (pg_num == 128) + pg_num = WCD9360_PAGE_128; + else if (pg_num == 80) + pg_num = WCD9360_PAGE_80; + else if (pg_num > 15) + return false; + + reg_tbl = wcd9360_reg[pg_num]; + reg_offset = reg & 0xFF; + + if (reg_tbl && reg_tbl[reg_offset]) + return true; + else + return false; +} + +static bool wcd9360_is_volatile_register(struct device *dev, unsigned int reg) +{ + u8 pg_num, reg_offset; + const u8 *reg_tbl = NULL; + + pg_num = reg >> 8; + + if (pg_num == 1 || pg_num == 2 || + pg_num == 6 || pg_num == 7) + return true; + else if (pg_num == 128) + pg_num = WCD9360_PAGE_128; + else if (pg_num == 80) + pg_num = WCD9360_PAGE_80; + else if (pg_num > 15) + return false; + + reg_tbl = wcd9360_reg[pg_num]; + reg_offset = reg & 0xFF; + + if (reg_tbl && reg_tbl[reg_offset] == WCD9360_RO) + return true; + + if ((reg >= WCD9360_CODEC_RPM_RST_CTL) && + (reg <= WCD9360_CHIP_TIER_CTRL_ALT_FUNC_EN)) + return true; + + if ((reg >= WCD9360_CDC_ANC0_IIR_COEFF_1_CTL) && + (reg <= WCD9360_CDC_ANC0_FB_GAIN_CTL)) + return true; + + if ((reg >= WCD9360_CODEC_CPR_WR_DATA_0) && + (reg <= WCD9360_CODEC_CPR_RD_DATA_3)) + return true; + + /* + * Need to mark volatile for registers that are writable but + * only few bits are read-only + */ + switch (reg) { + case WCD9360_CODEC_RPM_CLK_BYPASS: + case WCD9360_CODEC_RPM_CLK_GATE: + case WCD9360_CODEC_RPM_CLK_MCLK_CFG: + case WCD9360_CODEC_CPR_SVS_CX_VDD: + case WCD9360_CODEC_CPR_SVS2_CX_VDD: + case WCD9360_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL: + case WCD9360_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL: + return true; + } + + return false; +} + +struct regmap_config wcd9360_regmap_config = { + .reg_bits = 16, + .val_bits = 8, + .cache_type = REGCACHE_RBTREE, + .reg_defaults = wcd9360_defaults, + .num_reg_defaults = ARRAY_SIZE(wcd9360_defaults), + .max_register = WCD9360_MAX_REGISTER, + .volatile_reg = wcd9360_is_volatile_register, + .readable_reg = wcd9360_is_readable_register, + .can_multi_write = true, +}; diff --git a/audio-kernel/asoc/codecs/wcd9360/wcd9360-routing.h b/audio-kernel/asoc/codecs/wcd9360/wcd9360-routing.h new file mode 100644 index 000000000..c305d2e23 --- /dev/null +++ b/audio-kernel/asoc/codecs/wcd9360/wcd9360-routing.h @@ -0,0 +1,882 @@ +/* + * 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 + * 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 __WCD9360_ROUTING_H__ +#define __WCD9360_ROUTING_H__ + +#include + +const struct snd_soc_dapm_route pahu_slim_audio_map[] = { + + {"AIF4 MAD", NULL, "AIF4_MAD Mixer"}, + + /* Virtual input widget Mixer SLIMBUS */ + {"AIF1_CAP Mixer", "SLIM TX0", "SLIM TX0"}, + {"AIF1_CAP Mixer", "SLIM TX1", "SLIM TX1"}, + {"AIF1_CAP Mixer", "SLIM TX2", "SLIM TX2"}, + {"AIF1_CAP Mixer", "SLIM TX3", "SLIM TX3"}, + {"AIF1_CAP Mixer", "SLIM TX4", "SLIM TX4"}, + {"AIF1_CAP Mixer", "SLIM TX5", "SLIM TX5"}, + {"AIF1_CAP Mixer", "SLIM TX6", "SLIM TX6"}, + {"AIF1_CAP Mixer", "SLIM TX7", "SLIM TX7"}, + {"AIF1_CAP Mixer", "SLIM TX8", "SLIM TX8"}, + {"AIF1_CAP Mixer", "SLIM TX9", "SLIM TX9"}, + {"AIF1_CAP Mixer", "SLIM TX10", "SLIM TX10"}, + {"AIF1_CAP Mixer", "SLIM TX11", "SLIM TX11"}, + {"AIF1_CAP Mixer", "SLIM TX13", "SLIM TX13"}, + + {"AIF2_CAP Mixer", "SLIM TX0", "SLIM TX0"}, + {"AIF2_CAP Mixer", "SLIM TX1", "SLIM TX1"}, + {"AIF2_CAP Mixer", "SLIM TX2", "SLIM TX2"}, + {"AIF2_CAP Mixer", "SLIM TX3", "SLIM TX3"}, + {"AIF2_CAP Mixer", "SLIM TX4", "SLIM TX4"}, + {"AIF2_CAP Mixer", "SLIM TX5", "SLIM TX5"}, + {"AIF2_CAP Mixer", "SLIM TX6", "SLIM TX6"}, + {"AIF2_CAP Mixer", "SLIM TX7", "SLIM TX7"}, + {"AIF2_CAP Mixer", "SLIM TX8", "SLIM TX8"}, + {"AIF2_CAP Mixer", "SLIM TX9", "SLIM TX9"}, + {"AIF2_CAP Mixer", "SLIM TX10", "SLIM TX10"}, + {"AIF2_CAP Mixer", "SLIM TX11", "SLIM TX11"}, + {"AIF2_CAP Mixer", "SLIM TX13", "SLIM TX13"}, + + {"AIF3_CAP Mixer", "SLIM TX0", "SLIM TX0"}, + {"AIF3_CAP Mixer", "SLIM TX1", "SLIM TX1"}, + {"AIF3_CAP Mixer", "SLIM TX2", "SLIM TX2"}, + {"AIF3_CAP Mixer", "SLIM TX3", "SLIM TX3"}, + {"AIF3_CAP Mixer", "SLIM TX4", "SLIM TX4"}, + {"AIF3_CAP Mixer", "SLIM TX5", "SLIM TX5"}, + {"AIF3_CAP Mixer", "SLIM TX6", "SLIM TX6"}, + {"AIF3_CAP Mixer", "SLIM TX7", "SLIM TX7"}, + {"AIF3_CAP Mixer", "SLIM TX8", "SLIM TX8"}, + {"AIF3_CAP Mixer", "SLIM TX9", "SLIM TX9"}, + {"AIF3_CAP Mixer", "SLIM TX10", "SLIM TX10"}, + {"AIF3_CAP Mixer", "SLIM TX11", "SLIM TX11"}, + {"AIF3_CAP Mixer", "SLIM TX13", "SLIM TX13"}, + + {"AIF4_MAD Mixer", "SLIM TX13", "SLIM TX13"}, + + /* CDC Tx interface with SLIMBUS */ + {"SLIM TX0", NULL, "CDC_IF TX0 MUX"}, + {"SLIM TX1", NULL, "CDC_IF TX1 MUX"}, + {"SLIM TX2", NULL, "CDC_IF TX2 MUX"}, + {"SLIM TX3", NULL, "CDC_IF TX3 MUX"}, + {"SLIM TX4", NULL, "CDC_IF TX4 MUX"}, + {"SLIM TX5", NULL, "CDC_IF TX5 MUX"}, + {"SLIM TX6", NULL, "CDC_IF TX6 MUX"}, + {"SLIM TX7", NULL, "CDC_IF TX7 MUX"}, + {"SLIM TX8", NULL, "CDC_IF TX8 MUX"}, + {"SLIM TX9", NULL, "CDC_IF TX9 MUX"}, + {"SLIM TX10", NULL, "CDC_IF TX10 MUX2"}, + {"SLIM TX11", NULL, "CDC_IF TX11 MUX2"}, + {"SLIM TX13", NULL, "CDC_IF TX13 MUX"}, + + {"SLIM RX0 MUX", "AIF1_PB", "AIF1 PB"}, + {"SLIM RX1 MUX", "AIF1_PB", "AIF1 PB"}, + {"SLIM RX2 MUX", "AIF1_PB", "AIF1 PB"}, + {"SLIM RX3 MUX", "AIF1_PB", "AIF1 PB"}, + {"SLIM RX4 MUX", "AIF1_PB", "AIF1 PB"}, + {"SLIM RX5 MUX", "AIF1_PB", "AIF1 PB"}, + {"SLIM RX6 MUX", "AIF1_PB", "AIF1 PB"}, + {"SLIM RX7 MUX", "AIF1_PB", "AIF1 PB"}, + + {"SLIM RX0 MUX", "AIF2_PB", "AIF2 PB"}, + {"SLIM RX1 MUX", "AIF2_PB", "AIF2 PB"}, + {"SLIM RX2 MUX", "AIF2_PB", "AIF2 PB"}, + {"SLIM RX3 MUX", "AIF2_PB", "AIF2 PB"}, + {"SLIM RX4 MUX", "AIF2_PB", "AIF2 PB"}, + {"SLIM RX5 MUX", "AIF2_PB", "AIF2 PB"}, + {"SLIM RX6 MUX", "AIF2_PB", "AIF2 PB"}, + {"SLIM RX7 MUX", "AIF2_PB", "AIF2 PB"}, + + {"SLIM RX0 MUX", "AIF3_PB", "AIF3 PB"}, + {"SLIM RX1 MUX", "AIF3_PB", "AIF3 PB"}, + {"SLIM RX2 MUX", "AIF3_PB", "AIF3 PB"}, + {"SLIM RX3 MUX", "AIF3_PB", "AIF3 PB"}, + {"SLIM RX4 MUX", "AIF3_PB", "AIF3 PB"}, + {"SLIM RX5 MUX", "AIF3_PB", "AIF3 PB"}, + {"SLIM RX6 MUX", "AIF3_PB", "AIF3 PB"}, + {"SLIM RX7 MUX", "AIF3_PB", "AIF3 PB"}, + + {"SLIM RX0 MUX", "AIF4_PB", "AIF4 PB"}, + {"SLIM RX1 MUX", "AIF4_PB", "AIF4 PB"}, + {"SLIM RX2 MUX", "AIF4_PB", "AIF4 PB"}, + {"SLIM RX3 MUX", "AIF4_PB", "AIF4 PB"}, + {"SLIM RX4 MUX", "AIF4_PB", "AIF4 PB"}, + {"SLIM RX5 MUX", "AIF4_PB", "AIF4 PB"}, + {"SLIM RX6 MUX", "AIF4_PB", "AIF4 PB"}, + {"SLIM RX7 MUX", "AIF4_PB", "AIF4 PB"}, + + {"SLIM RX0", NULL, "SLIM RX0 MUX"}, + {"SLIM RX1", NULL, "SLIM RX1 MUX"}, + {"SLIM RX2", NULL, "SLIM RX2 MUX"}, + {"SLIM RX3", NULL, "SLIM RX3 MUX"}, + {"SLIM RX4", NULL, "SLIM RX4 MUX"}, + {"SLIM RX5", NULL, "SLIM RX5 MUX"}, + {"SLIM RX6", NULL, "SLIM RX6 MUX"}, + {"SLIM RX7", NULL, "SLIM RX7 MUX"}, + + /* CDC Rx interface with SLIMBUS */ + {"CDC_IF RX0 MUX", "SLIM RX0", "SLIM RX0"}, + {"CDC_IF RX1 MUX", "SLIM RX1", "SLIM RX1"}, + {"CDC_IF RX2 MUX", "SLIM RX2", "SLIM RX2"}, + {"CDC_IF RX3 MUX", "SLIM RX3", "SLIM RX3"}, + {"CDC_IF RX4 MUX", "SLIM RX4", "SLIM RX4"}, + {"CDC_IF RX5 MUX", "SLIM RX5", "SLIM RX5"}, + {"CDC_IF RX6 MUX", "SLIM RX6", "SLIM RX6"}, + {"CDC_IF RX7 MUX", "SLIM RX7", "SLIM RX7"}, + + /* VI Feedback */ + {"AIF4_VI Mixer", "SPKR_VI_1", "VIINPUT"}, + {"AIF4_VI Mixer", "SPKR_VI_2", "VIINPUT"}, + {"AIF4 VI", NULL, "AIF4_VI Mixer"}, +}; + +const struct snd_soc_dapm_route pahu_audio_map[] = { + + /* Virtual input widgets */ + {"AIF1 CAP", NULL, "AIF1_CAP Mixer"}, + {"AIF2 CAP", NULL, "AIF2_CAP Mixer"}, + {"AIF3 CAP", NULL, "AIF3_CAP Mixer"}, + + /* WDMA3 */ + {"WDMA3 PORT0 MUX", "DEC0", "ADC MUX0"}, + {"WDMA3 PORT0 MUX", "RX_MIX_TX0", "RX MIX TX0 MUX"}, + {"WDMA3 PORT1 MUX", "DEC1", "ADC MUX1"}, + {"WDMA3 PORT1 MUX", "RX_MIX_TX1", "RX MIX TX1 MUX"}, + {"WDMA3 PORT2 MUX", "DEC2", "ADC MUX2"}, + {"WDMA3 PORT2 MUX", "RX_MIX_TX2", "RX MIX TX2 MUX"}, + {"WDMA3 PORT3 MUX", "DEC3", "ADC MUX3"}, + {"WDMA3 PORT3 MUX", "RX_MIX_TX3", "RX MIX TX3 MUX"}, + {"WDMA3 PORT4 MUX", "DEC4", "ADC MUX4"}, + {"WDMA3 PORT4 MUX", "RX_MIX_TX4", "RX MIX TX4 MUX"}, + {"WDMA3 PORT5 MUX", "DEC5", "ADC MUX5"}, + {"WDMA3 PORT5 MUX", "RX_MIX_TX5", "RX MIX TX5 MUX"}, + {"WDMA3 PORT6 MUX", "DEC6", "ADC MUX6"}, + {"WDMA3 PORT6 MUX", "RX_MIX_TX6", "RX MIX TX6 MUX"}, + + {"WDMA3 CH0 MUX", "PORT_0", "WDMA3 PORT0 MUX"}, + {"WDMA3 CH0 MUX", "PORT_1", "WDMA3 PORT1 MUX"}, + {"WDMA3 CH0 MUX", "PORT_2", "WDMA3 PORT2 MUX"}, + {"WDMA3 CH0 MUX", "PORT_3", "WDMA3 PORT3 MUX"}, + {"WDMA3 CH0 MUX", "PORT_4", "WDMA3 PORT4 MUX"}, + {"WDMA3 CH0 MUX", "PORT_5", "WDMA3 PORT5 MUX"}, + {"WDMA3 CH0 MUX", "PORT_6", "WDMA3 PORT6 MUX"}, + {"WDMA3 CH0 MUX", "PORT_7", "ADC MUX7"}, + {"WDMA3 CH0 MUX", "PORT_8", "ADC MUX8"}, + + {"WDMA3 CH1 MUX", "PORT_0", "WDMA3 PORT0 MUX"}, + {"WDMA3 CH1 MUX", "PORT_1", "WDMA3 PORT1 MUX"}, + {"WDMA3 CH1 MUX", "PORT_2", "WDMA3 PORT2 MUX"}, + {"WDMA3 CH1 MUX", "PORT_3", "WDMA3 PORT3 MUX"}, + {"WDMA3 CH1 MUX", "PORT_4", "WDMA3 PORT4 MUX"}, + {"WDMA3 CH1 MUX", "PORT_5", "WDMA3 PORT5 MUX"}, + {"WDMA3 CH1 MUX", "PORT_6", "WDMA3 PORT6 MUX"}, + {"WDMA3 CH1 MUX", "PORT_7", "ADC MUX7"}, + {"WDMA3 CH1 MUX", "PORT_8", "ADC MUX8"}, + + {"WDMA3 CH2 MUX", "PORT_0", "WDMA3 PORT0 MUX"}, + {"WDMA3 CH2 MUX", "PORT_1", "WDMA3 PORT1 MUX"}, + {"WDMA3 CH2 MUX", "PORT_2", "WDMA3 PORT2 MUX"}, + {"WDMA3 CH2 MUX", "PORT_3", "WDMA3 PORT3 MUX"}, + {"WDMA3 CH2 MUX", "PORT_4", "WDMA3 PORT4 MUX"}, + {"WDMA3 CH2 MUX", "PORT_5", "WDMA3 PORT5 MUX"}, + {"WDMA3 CH2 MUX", "PORT_6", "WDMA3 PORT6 MUX"}, + {"WDMA3 CH2 MUX", "PORT_7", "ADC MUX7"}, + {"WDMA3 CH2 MUX", "PORT_8", "ADC MUX8"}, + + {"WDMA3 CH3 MUX", "PORT_0", "WDMA3 PORT0 MUX"}, + {"WDMA3 CH3 MUX", "PORT_1", "WDMA3 PORT1 MUX"}, + {"WDMA3 CH3 MUX", "PORT_2", "WDMA3 PORT2 MUX"}, + {"WDMA3 CH3 MUX", "PORT_3", "WDMA3 PORT3 MUX"}, + {"WDMA3 CH3 MUX", "PORT_4", "WDMA3 PORT4 MUX"}, + {"WDMA3 CH3 MUX", "PORT_5", "WDMA3 PORT5 MUX"}, + {"WDMA3 CH3 MUX", "PORT_6", "WDMA3 PORT6 MUX"}, + {"WDMA3 CH3 MUX", "PORT_7", "ADC MUX7"}, + {"WDMA3 CH3 MUX", "PORT_8", "ADC MUX8"}, + + {"WDMA3_CH_MIXER", NULL, "WDMA3 CH0 MUX"}, + {"WDMA3_CH_MIXER", NULL, "WDMA3 CH1 MUX"}, + {"WDMA3_CH_MIXER", NULL, "WDMA3 CH2 MUX"}, + {"WDMA3_CH_MIXER", NULL, "WDMA3 CH3 MUX"}, + + {"WDMA3_ON_OFF", "Switch", "WDMA3_CH_MIXER"}, + {"WDMA3_OUT", NULL, "WDMA3_ON_OFF"}, + + /* MAD */ + {"MAD_SEL MUX", "SPE", "MAD_CPE_INPUT"}, + {"MAD_SEL MUX", "MSM", "MADINPUT"}, + + {"MAD_INP MUX", "MAD", "MAD_SEL MUX"}, + {"MAD_INP MUX", "DEC1", "ADC MUX1"}, + + {"MAD_BROADCAST", "Switch", "MAD_INP MUX"}, + {"MAD_CPE1", "Switch", "MAD_INP MUX"}, + {"MAD_CPE2", "Switch", "MAD_INP MUX"}, + + {"MAD_CPE_OUT1", NULL, "MAD_CPE1"}, + {"MAD_CPE_OUT2", NULL, "MAD_CPE2"}, + + {"CDC_IF TX0 MUX", "DEC0", "ADC MUX0"}, + {"CDC_IF TX0 MUX", "RX_MIX_TX0", "RX MIX TX0 MUX"}, + {"CDC_IF TX0 MUX", "DEC0_192", "ADC US MUX0"}, + + {"CDC_IF TX1 MUX", "DEC1", "ADC MUX1"}, + {"CDC_IF TX1 MUX", "RX_MIX_TX1", "RX MIX TX1 MUX"}, + {"CDC_IF TX1 MUX", "DEC1_192", "ADC US MUX1"}, + + {"CDC_IF TX2 MUX", "DEC2", "ADC MUX2"}, + {"CDC_IF TX2 MUX", "RX_MIX_TX2", "RX MIX TX2 MUX"}, + {"CDC_IF TX2 MUX", "DEC2_192", "ADC US MUX2"}, + + {"CDC_IF TX3 MUX", "DEC3", "ADC MUX3"}, + {"CDC_IF TX3 MUX", "RX_MIX_TX3", "RX MIX TX3 MUX"}, + {"CDC_IF TX3 MUX", "DEC3_192", "ADC US MUX3"}, + + {"CDC_IF TX4 MUX", "DEC4", "ADC MUX4"}, + {"CDC_IF TX4 MUX", "RX_MIX_TX4", "RX MIX TX4 MUX"}, + {"CDC_IF TX4 MUX", "DEC4_192", "ADC US MUX4"}, + + {"CDC_IF TX5 MUX", "DEC5", "ADC MUX5"}, + {"CDC_IF TX5 MUX", "RX_MIX_TX5", "RX MIX TX5 MUX"}, + {"CDC_IF TX5 MUX", "DEC5_192", "ADC US MUX5"}, + + {"CDC_IF TX6 MUX", "DEC6", "ADC MUX6"}, + {"CDC_IF TX6 MUX", "RX_MIX_TX6", "RX MIX TX6 MUX"}, + {"CDC_IF TX6 MUX", "DEC6_192", "ADC US MUX6"}, + + {"CDC_IF TX7 MUX", "DEC7", "ADC MUX7"}, + {"CDC_IF TX7 MUX", "RX_MIX_TX7", "RX MIX TX7 MUX"}, + {"CDC_IF TX7 MUX", "DEC7_192", "ADC US MUX7"}, + + {"CDC_IF TX8 MUX", "DEC8", "ADC MUX8"}, + {"CDC_IF TX8 MUX", "RX_MIX_TX8", "RX MIX TX8 MUX"}, + {"CDC_IF TX8 MUX", "DEC8_192", "ADC US MUX8"}, + + {"CDC_IF TX9 MUX", "DEC7", "ADC MUX7"}, + {"CDC_IF TX9 MUX", "DEC7_192", "ADC US MUX7"}, + {"CDC_IF TX10 MUX", "DEC6", "ADC MUX6"}, + {"CDC_IF TX10 MUX", "DEC6_192", "ADC US MUX6"}, + {"CDC_IF TX10 MUX2", "TX10_MUX1", "CDC_IF TX10 MUX"}, + + {"CDC_IF TX11 MUX2", "TX11_MUX1", "CDC_IF TX11 MUX"}, + {"CDC_IF TX11 MUX", "DEC_0_5", "CDC_IF TX11 INP1 MUX"}, + {"CDC_IF TX11 MUX", "DEC_9_12", "CDC_IF TX11 INP1 MUX"}, + {"CDC_IF TX11 INP1 MUX", "DEC0", "ADC MUX0"}, + {"CDC_IF TX11 INP1 MUX", "DEC1", "ADC MUX1"}, + {"CDC_IF TX11 INP1 MUX", "DEC2", "ADC MUX2"}, + {"CDC_IF TX11 INP1 MUX", "DEC3", "ADC MUX3"}, + {"CDC_IF TX11 INP1 MUX", "DEC4", "ADC MUX4"}, + {"CDC_IF TX11 INP1 MUX", "DEC5", "ADC MUX5"}, + {"CDC_IF TX11 INP1 MUX", "RX_MIX_TX5", "RX MIX TX5 MUX"}, + + {"CDC_IF TX13 MUX", "MAD_BRDCST", "MAD_BROADCAST"}, + {"CDC_IF TX13 MUX", "CDC_DEC_5", "CDC_IF TX13 INP1 MUX"}, + {"CDC_IF TX13 INP1 MUX", "DEC5", "ADC MUX5"}, + {"CDC_IF TX13 INP1 MUX", "DEC5_192", "ADC US MUX5"}, + + {"RX MIX TX0 MUX", "RX_MIX0", "RX INT0 SEC MIX"}, + {"RX MIX TX0 MUX", "RX_MIX7", "RX INT7 SEC MIX"}, + {"RX MIX TX0 MUX", "RX_MIX8", "RX INT8 SEC MIX"}, + {"RX MIX TX0 MUX", "RX_MIX9", "RX INT9 SEC MIX"}, + + {"RX MIX TX1 MUX", "RX_MIX0", "RX INT0 SEC MIX"}, + {"RX MIX TX1 MUX", "RX_MIX7", "RX INT7 SEC MIX"}, + {"RX MIX TX1 MUX", "RX_MIX8", "RX INT8 SEC MIX"}, + {"RX MIX TX1 MUX", "RX_MIX9", "RX INT9 SEC MIX"}, + + {"RX MIX TX2 MUX", "RX_MIX0", "RX INT0 SEC MIX"}, + {"RX MIX TX2 MUX", "RX_MIX7", "RX INT7 SEC MIX"}, + {"RX MIX TX2 MUX", "RX_MIX8", "RX INT8 SEC MIX"}, + {"RX MIX TX2 MUX", "RX_MIX9", "RX INT9 SEC MIX"}, + + {"RX MIX TX3 MUX", "RX_MIX0", "RX INT0 SEC MIX"}, + {"RX MIX TX3 MUX", "RX_MIX7", "RX INT7 SEC MIX"}, + {"RX MIX TX3 MUX", "RX_MIX8", "RX INT8 SEC MIX"}, + {"RX MIX TX3 MUX", "RX_MIX9", "RX INT9 SEC MIX"}, + + {"RX MIX TX4 MUX", "RX_MIX0", "RX INT0 SEC MIX"}, + {"RX MIX TX4 MUX", "RX_MIX7", "RX INT7 SEC MIX"}, + {"RX MIX TX4 MUX", "RX_MIX8", "RX INT8 SEC MIX"}, + {"RX MIX TX4 MUX", "RX_MIX9", "RX INT9 SEC MIX"}, + + {"RX MIX TX5 MUX", "RX_MIX0", "RX INT0 SEC MIX"}, + {"RX MIX TX5 MUX", "RX_MIX7", "RX INT7 SEC MIX"}, + {"RX MIX TX5 MUX", "RX_MIX8", "RX INT8 SEC MIX"}, + {"RX MIX TX5 MUX", "RX_MIX9", "RX INT9 SEC MIX"}, + + {"RX MIX TX6 MUX", "RX_MIX0", "RX INT0 SEC MIX"}, + {"RX MIX TX6 MUX", "RX_MIX7", "RX INT7 SEC MIX"}, + {"RX MIX TX6 MUX", "RX_MIX8", "RX INT8 SEC MIX"}, + {"RX MIX TX6 MUX", "RX_MIX9", "RX INT9 SEC MIX"}, + + {"RX MIX TX7 MUX", "RX_MIX0", "RX INT0 SEC MIX"}, + {"RX MIX TX7 MUX", "RX_MIX7", "RX INT7 SEC MIX"}, + {"RX MIX TX7 MUX", "RX_MIX8", "RX INT8 SEC MIX"}, + {"RX MIX TX7 MUX", "RX_MIX9", "RX INT9 SEC MIX"}, + + {"RX MIX TX8 MUX", "RX_MIX0", "RX INT0 SEC MIX"}, + {"RX MIX TX8 MUX", "RX_MIX7", "RX INT7 SEC MIX"}, + {"RX MIX TX8 MUX", "RX_MIX8", "RX INT8 SEC MIX"}, + {"RX MIX TX8 MUX", "RX_MIX9", "RX INT9 SEC MIX"}, + + {"ADC US MUX0", "US_Switch", "ADC MUX0"}, + {"ADC US MUX1", "US_Switch", "ADC MUX1"}, + {"ADC US MUX2", "US_Switch", "ADC MUX2"}, + {"ADC US MUX3", "US_Switch", "ADC MUX3"}, + {"ADC US MUX4", "US_Switch", "ADC MUX4"}, + {"ADC US MUX5", "US_Switch", "ADC MUX5"}, + {"ADC US MUX6", "US_Switch", "ADC MUX6"}, + {"ADC US MUX7", "US_Switch", "ADC MUX7"}, + {"ADC US MUX8", "US_Switch", "ADC MUX8"}, + + {"ADC MUX0", "DMIC", "DMIC MUX0"}, + {"ADC MUX0", "AMIC", "AMIC MUX0"}, + {"ADC MUX1", "DMIC", "DMIC MUX1"}, + {"ADC MUX1", "AMIC", "AMIC MUX1"}, + {"ADC MUX2", "DMIC", "DMIC MUX2"}, + {"ADC MUX2", "AMIC", "AMIC MUX2"}, + {"ADC MUX3", "DMIC", "DMIC MUX3"}, + {"ADC MUX3", "AMIC", "AMIC MUX3"}, + {"ADC MUX4", "DMIC", "DMIC MUX4"}, + {"ADC MUX4", "AMIC", "AMIC MUX4"}, + {"ADC MUX5", "DMIC", "DMIC MUX5"}, + {"ADC MUX5", "AMIC", "AMIC MUX5"}, + {"ADC MUX6", "DMIC", "DMIC MUX6"}, + {"ADC MUX6", "AMIC", "AMIC MUX6"}, + {"ADC MUX7", "DMIC", "DMIC MUX7"}, + {"ADC MUX7", "AMIC", "AMIC MUX7"}, + {"ADC MUX8", "DMIC", "DMIC MUX8"}, + {"ADC MUX8", "AMIC", "AMIC MUX8"}, + {"ADC MUX10", "DMIC", "DMIC MUX10"}, + {"ADC MUX10", "AMIC", "AMIC MUX10"}, + {"ADC MUX11", "DMIC", "DMIC MUX11"}, + {"ADC MUX11", "AMIC", "AMIC MUX11"}, + + {"ADC MUX0", "ANC_FB_TUNE1", "ADC MUX10"}, + {"ADC MUX0", "ANC_FB_TUNE1", "ADC MUX11"}, + {"ADC MUX1", "ANC_FB_TUNE1", "ADC MUX10"}, + {"ADC MUX1", "ANC_FB_TUNE1", "ADC MUX11"}, + {"ADC MUX2", "ANC_FB_TUNE1", "ADC MUX10"}, + {"ADC MUX2", "ANC_FB_TUNE1", "ADC MUX11"}, + {"ADC MUX3", "ANC_FB_TUNE1", "ADC MUX10"}, + {"ADC MUX3", "ANC_FB_TUNE1", "ADC MUX11"}, + {"ADC MUX4", "ANC_FB_TUNE1", "ADC MUX10"}, + {"ADC MUX4", "ANC_FB_TUNE1", "ADC MUX11"}, + {"ADC MUX5", "ANC_FB_TUNE1", "ADC MUX10"}, + {"ADC MUX5", "ANC_FB_TUNE1", "ADC MUX11"}, + {"ADC MUX6", "ANC_FB_TUNE1", "ADC MUX10"}, + {"ADC MUX6", "ANC_FB_TUNE1", "ADC MUX11"}, + {"ADC MUX7", "ANC_FB_TUNE1", "ADC MUX10"}, + {"ADC MUX7", "ANC_FB_TUNE1", "ADC MUX11"}, + {"ADC MUX8", "ANC_FB_TUNE1", "ADC MUX10"}, + {"ADC MUX8", "ANC_FB_TUNE1", "ADC MUX11"}, + + {"DMIC MUX0", "DMIC0", "DMIC0"}, + {"DMIC MUX0", "DMIC1", "DMIC1"}, + {"DMIC MUX0", "DMIC2", "DMIC2"}, + {"DMIC MUX0", "DMIC3", "DMIC3"}, + {"DMIC MUX0", "DMIC4", "DMIC4"}, + {"DMIC MUX0", "DMIC5", "DMIC5"}, + {"DMIC MUX0", "DMIC6", "DMIC6"}, + {"DMIC MUX0", "DMIC7", "DMIC7"}, + {"AMIC MUX0", "ADC1", "ADC1"}, + {"AMIC MUX0", "ADC2", "ADC2"}, + {"AMIC MUX0", "ADC3", "ADC3"}, + {"AMIC MUX0", "ADC4", "ADC4"}, + + {"DMIC MUX1", "DMIC0", "DMIC0"}, + {"DMIC MUX1", "DMIC1", "DMIC1"}, + {"DMIC MUX1", "DMIC2", "DMIC2"}, + {"DMIC MUX1", "DMIC3", "DMIC3"}, + {"DMIC MUX1", "DMIC4", "DMIC4"}, + {"DMIC MUX1", "DMIC5", "DMIC5"}, + {"DMIC MUX1", "DMIC6", "DMIC6"}, + {"DMIC MUX1", "DMIC7", "DMIC7"}, + {"AMIC MUX1", "ADC1", "ADC1"}, + {"AMIC MUX1", "ADC2", "ADC2"}, + {"AMIC MUX1", "ADC3", "ADC3"}, + {"AMIC MUX1", "ADC4", "ADC4"}, + + {"DMIC MUX2", "DMIC0", "DMIC0"}, + {"DMIC MUX2", "DMIC1", "DMIC1"}, + {"DMIC MUX2", "DMIC2", "DMIC2"}, + {"DMIC MUX2", "DMIC3", "DMIC3"}, + {"DMIC MUX2", "DMIC4", "DMIC4"}, + {"DMIC MUX2", "DMIC5", "DMIC5"}, + {"DMIC MUX2", "DMIC6", "DMIC6"}, + {"DMIC MUX2", "DMIC7", "DMIC7"}, + {"AMIC MUX2", "ADC1", "ADC1"}, + {"AMIC MUX2", "ADC2", "ADC2"}, + {"AMIC MUX2", "ADC3", "ADC3"}, + {"AMIC MUX2", "ADC4", "ADC4"}, + + {"DMIC MUX3", "DMIC0", "DMIC0"}, + {"DMIC MUX3", "DMIC1", "DMIC1"}, + {"DMIC MUX3", "DMIC2", "DMIC2"}, + {"DMIC MUX3", "DMIC3", "DMIC3"}, + {"DMIC MUX3", "DMIC4", "DMIC4"}, + {"DMIC MUX3", "DMIC5", "DMIC5"}, + {"DMIC MUX3", "DMIC6", "DMIC6"}, + {"DMIC MUX3", "DMIC7", "DMIC7"}, + {"AMIC MUX3", "ADC1", "ADC1"}, + {"AMIC MUX3", "ADC2", "ADC2"}, + {"AMIC MUX3", "ADC3", "ADC3"}, + {"AMIC MUX3", "ADC4", "ADC4"}, + + {"DMIC MUX4", "DMIC0", "DMIC0"}, + {"DMIC MUX4", "DMIC1", "DMIC1"}, + {"DMIC MUX4", "DMIC2", "DMIC2"}, + {"DMIC MUX4", "DMIC3", "DMIC3"}, + {"DMIC MUX4", "DMIC4", "DMIC4"}, + {"DMIC MUX4", "DMIC5", "DMIC5"}, + {"DMIC MUX4", "DMIC6", "DMIC6"}, + {"DMIC MUX4", "DMIC7", "DMIC7"}, + {"AMIC MUX4", "ADC1", "ADC1"}, + {"AMIC MUX4", "ADC2", "ADC2"}, + {"AMIC MUX4", "ADC3", "ADC3"}, + {"AMIC MUX4", "ADC4", "ADC4"}, + + {"DMIC MUX5", "DMIC0", "DMIC0"}, + {"DMIC MUX5", "DMIC1", "DMIC1"}, + {"DMIC MUX5", "DMIC2", "DMIC2"}, + {"DMIC MUX5", "DMIC3", "DMIC3"}, + {"DMIC MUX5", "DMIC4", "DMIC4"}, + {"DMIC MUX5", "DMIC5", "DMIC5"}, + {"DMIC MUX5", "DMIC6", "DMIC6"}, + {"DMIC MUX5", "DMIC7", "DMIC7"}, + {"AMIC MUX5", "ADC1", "ADC1"}, + {"AMIC MUX5", "ADC2", "ADC2"}, + {"AMIC MUX5", "ADC3", "ADC3"}, + {"AMIC MUX5", "ADC4", "ADC4"}, + + {"DMIC MUX6", "DMIC0", "DMIC0"}, + {"DMIC MUX6", "DMIC1", "DMIC1"}, + {"DMIC MUX6", "DMIC2", "DMIC2"}, + {"DMIC MUX6", "DMIC3", "DMIC3"}, + {"DMIC MUX6", "DMIC4", "DMIC4"}, + {"DMIC MUX6", "DMIC5", "DMIC5"}, + {"DMIC MUX6", "DMIC6", "DMIC6"}, + {"DMIC MUX6", "DMIC7", "DMIC7"}, + {"AMIC MUX6", "ADC1", "ADC1"}, + {"AMIC MUX6", "ADC2", "ADC2"}, + {"AMIC MUX6", "ADC3", "ADC3"}, + {"AMIC MUX6", "ADC4", "ADC4"}, + + {"DMIC MUX7", "DMIC0", "DMIC0"}, + {"DMIC MUX7", "DMIC1", "DMIC1"}, + {"DMIC MUX7", "DMIC2", "DMIC2"}, + {"DMIC MUX7", "DMIC3", "DMIC3"}, + {"DMIC MUX7", "DMIC4", "DMIC4"}, + {"DMIC MUX7", "DMIC5", "DMIC5"}, + {"DMIC MUX7", "DMIC6", "DMIC6"}, + {"DMIC MUX7", "DMIC7", "DMIC7"}, + {"AMIC MUX7", "ADC1", "ADC1"}, + {"AMIC MUX7", "ADC2", "ADC2"}, + {"AMIC MUX7", "ADC3", "ADC3"}, + {"AMIC MUX7", "ADC4", "ADC4"}, + + {"DMIC MUX8", "DMIC0", "DMIC0"}, + {"DMIC MUX8", "DMIC1", "DMIC1"}, + {"DMIC MUX8", "DMIC2", "DMIC2"}, + {"DMIC MUX8", "DMIC3", "DMIC3"}, + {"DMIC MUX8", "DMIC4", "DMIC4"}, + {"DMIC MUX8", "DMIC5", "DMIC5"}, + {"DMIC MUX8", "DMIC6", "DMIC6"}, + {"DMIC MUX8", "DMIC7", "DMIC7"}, + {"AMIC MUX8", "ADC1", "ADC1"}, + {"AMIC MUX8", "ADC2", "ADC2"}, + {"AMIC MUX8", "ADC3", "ADC3"}, + {"AMIC MUX8", "ADC4", "ADC4"}, + + {"DMIC MUX10", "DMIC0", "DMIC0"}, + {"DMIC MUX10", "DMIC1", "DMIC1"}, + {"DMIC MUX10", "DMIC2", "DMIC2"}, + {"DMIC MUX10", "DMIC3", "DMIC3"}, + {"DMIC MUX10", "DMIC4", "DMIC4"}, + {"DMIC MUX10", "DMIC5", "DMIC5"}, + {"DMIC MUX10", "DMIC6", "DMIC6"}, + {"DMIC MUX10", "DMIC7", "DMIC7"}, + {"AMIC MUX10", "ADC1", "ADC1"}, + {"AMIC MUX10", "ADC2", "ADC2"}, + {"AMIC MUX10", "ADC3", "ADC3"}, + {"AMIC MUX10", "ADC4", "ADC4"}, + + {"DMIC MUX11", "DMIC0", "DMIC0"}, + {"DMIC MUX11", "DMIC1", "DMIC1"}, + {"DMIC MUX11", "DMIC2", "DMIC2"}, + {"DMIC MUX11", "DMIC3", "DMIC3"}, + {"DMIC MUX11", "DMIC4", "DMIC4"}, + {"DMIC MUX11", "DMIC5", "DMIC5"}, + {"DMIC MUX11", "DMIC6", "DMIC6"}, + {"DMIC MUX11", "DMIC7", "DMIC7"}, + {"AMIC MUX11", "ADC1", "ADC1"}, + {"AMIC MUX11", "ADC2", "ADC2"}, + {"AMIC MUX11", "ADC3", "ADC3"}, + {"AMIC MUX11", "ADC4", "ADC4"}, + + {"ADC2_IN", "AMIC1", "AMIC1"}, + {"ADC2_IN", "AMIC2", "AMIC2"}, + {"ADC4_IN", "AMIC3", "AMIC3"}, + {"ADC4_IN", "AMIC4", "AMIC4"}, + + {"ADC1", NULL, "AMIC1"}, + {"ADC2", NULL, "ADC2_IN"}, + {"ADC3", NULL, "AMIC3"}, + {"ADC4", NULL, "ADC4_IN"}, + + {"ADC1", NULL, "LDO_RXTX"}, + {"ADC2", NULL, "LDO_RXTX"}, + {"ADC3", NULL, "LDO_RXTX"}, + {"ADC4", NULL, "LDO_RXTX"}, + + {"RX INT0_1 MIX1 INP0", "RX0", "CDC_IF RX0 MUX"}, + {"RX INT0_1 MIX1 INP0", "RX1", "CDC_IF RX1 MUX"}, + {"RX INT0_1 MIX1 INP0", "RX2", "CDC_IF RX2 MUX"}, + {"RX INT0_1 MIX1 INP0", "RX3", "CDC_IF RX3 MUX"}, + {"RX INT0_1 MIX1 INP0", "RX4", "CDC_IF RX4 MUX"}, + {"RX INT0_1 MIX1 INP0", "RX5", "CDC_IF RX5 MUX"}, + {"RX INT0_1 MIX1 INP0", "RX6", "CDC_IF RX6 MUX"}, + {"RX INT0_1 MIX1 INP0", "RX7", "CDC_IF RX7 MUX"}, + {"RX INT0_1 MIX1 INP0", "IIR0", "IIR0"}, + {"RX INT0_1 MIX1 INP1", "RX0", "CDC_IF RX0 MUX"}, + {"RX INT0_1 MIX1 INP1", "RX1", "CDC_IF RX1 MUX"}, + {"RX INT0_1 MIX1 INP1", "RX2", "CDC_IF RX2 MUX"}, + {"RX INT0_1 MIX1 INP1", "RX3", "CDC_IF RX3 MUX"}, + {"RX INT0_1 MIX1 INP1", "RX4", "CDC_IF RX4 MUX"}, + {"RX INT0_1 MIX1 INP1", "RX5", "CDC_IF RX5 MUX"}, + {"RX INT0_1 MIX1 INP1", "RX6", "CDC_IF RX6 MUX"}, + {"RX INT0_1 MIX1 INP1", "RX7", "CDC_IF RX7 MUX"}, + {"RX INT0_1 MIX1 INP1", "IIR0", "IIR0"}, + {"RX INT0_1 MIX1 INP2", "RX0", "CDC_IF RX0 MUX"}, + {"RX INT0_1 MIX1 INP2", "RX1", "CDC_IF RX1 MUX"}, + {"RX INT0_1 MIX1 INP2", "RX2", "CDC_IF RX2 MUX"}, + {"RX INT0_1 MIX1 INP2", "RX3", "CDC_IF RX3 MUX"}, + {"RX INT0_1 MIX1 INP2", "RX4", "CDC_IF RX4 MUX"}, + {"RX INT0_1 MIX1 INP2", "RX5", "CDC_IF RX5 MUX"}, + {"RX INT0_1 MIX1 INP2", "RX6", "CDC_IF RX6 MUX"}, + {"RX INT0_1 MIX1 INP2", "RX7", "CDC_IF RX7 MUX"}, + {"RX INT0_1 MIX1 INP2", "IIR0", "IIR0"}, + + {"RX INT7_1 MIX1 INP0", "RX0", "CDC_IF RX0 MUX"}, + {"RX INT7_1 MIX1 INP0", "RX1", "CDC_IF RX1 MUX"}, + {"RX INT7_1 MIX1 INP0", "RX2", "CDC_IF RX2 MUX"}, + {"RX INT7_1 MIX1 INP0", "RX3", "CDC_IF RX3 MUX"}, + {"RX INT7_1 MIX1 INP0", "RX4", "CDC_IF RX4 MUX"}, + {"RX INT7_1 MIX1 INP0", "RX5", "CDC_IF RX5 MUX"}, + {"RX INT7_1 MIX1 INP0", "RX6", "CDC_IF RX6 MUX"}, + {"RX INT7_1 MIX1 INP0", "RX7", "CDC_IF RX7 MUX"}, + {"RX INT7_1 MIX1 INP0", "IIR0", "IIR0"}, + {"RX INT7_1 MIX1 INP1", "RX0", "CDC_IF RX0 MUX"}, + {"RX INT7_1 MIX1 INP1", "RX1", "CDC_IF RX1 MUX"}, + {"RX INT7_1 MIX1 INP1", "RX2", "CDC_IF RX2 MUX"}, + {"RX INT7_1 MIX1 INP1", "RX3", "CDC_IF RX3 MUX"}, + {"RX INT7_1 MIX1 INP1", "RX4", "CDC_IF RX4 MUX"}, + {"RX INT7_1 MIX1 INP1", "RX5", "CDC_IF RX5 MUX"}, + {"RX INT7_1 MIX1 INP1", "RX6", "CDC_IF RX6 MUX"}, + {"RX INT7_1 MIX1 INP1", "RX7", "CDC_IF RX7 MUX"}, + {"RX INT7_1 MIX1 INP1", "IIR0", "IIR0"}, + {"RX INT7_1 MIX1 INP2", "RX0", "CDC_IF RX0 MUX"}, + {"RX INT7_1 MIX1 INP2", "RX1", "CDC_IF RX1 MUX"}, + {"RX INT7_1 MIX1 INP2", "RX2", "CDC_IF RX2 MUX"}, + {"RX INT7_1 MIX1 INP2", "RX3", "CDC_IF RX3 MUX"}, + {"RX INT7_1 MIX1 INP2", "RX4", "CDC_IF RX4 MUX"}, + {"RX INT7_1 MIX1 INP2", "RX5", "CDC_IF RX5 MUX"}, + {"RX INT7_1 MIX1 INP2", "RX6", "CDC_IF RX6 MUX"}, + {"RX INT7_1 MIX1 INP2", "RX7", "CDC_IF RX7 MUX"}, + {"RX INT7_1 MIX1 INP2", "IIR0", "IIR0"}, + + {"RX INT8_1 MIX1 INP0", "RX0", "CDC_IF RX0 MUX"}, + {"RX INT8_1 MIX1 INP0", "RX1", "CDC_IF RX1 MUX"}, + {"RX INT8_1 MIX1 INP0", "RX2", "CDC_IF RX2 MUX"}, + {"RX INT8_1 MIX1 INP0", "RX3", "CDC_IF RX3 MUX"}, + {"RX INT8_1 MIX1 INP0", "RX4", "CDC_IF RX4 MUX"}, + {"RX INT8_1 MIX1 INP0", "RX5", "CDC_IF RX5 MUX"}, + {"RX INT8_1 MIX1 INP0", "RX6", "CDC_IF RX6 MUX"}, + {"RX INT8_1 MIX1 INP0", "RX7", "CDC_IF RX7 MUX"}, + {"RX INT8_1 MIX1 INP0", "IIR0", "IIR0"}, + {"RX INT8_1 MIX1 INP1", "RX0", "CDC_IF RX0 MUX"}, + {"RX INT8_1 MIX1 INP1", "RX1", "CDC_IF RX1 MUX"}, + {"RX INT8_1 MIX1 INP1", "RX2", "CDC_IF RX2 MUX"}, + {"RX INT8_1 MIX1 INP1", "RX3", "CDC_IF RX3 MUX"}, + {"RX INT8_1 MIX1 INP1", "RX4", "CDC_IF RX4 MUX"}, + {"RX INT8_1 MIX1 INP1", "RX5", "CDC_IF RX5 MUX"}, + {"RX INT8_1 MIX1 INP1", "RX6", "CDC_IF RX6 MUX"}, + {"RX INT8_1 MIX1 INP1", "RX7", "CDC_IF RX7 MUX"}, + {"RX INT8_1 MIX1 INP1", "IIR0", "IIR0"}, + {"RX INT8_1 MIX1 INP2", "RX0", "CDC_IF RX0 MUX"}, + {"RX INT8_1 MIX1 INP2", "RX1", "CDC_IF RX1 MUX"}, + {"RX INT8_1 MIX1 INP2", "RX2", "CDC_IF RX2 MUX"}, + {"RX INT8_1 MIX1 INP2", "RX3", "CDC_IF RX3 MUX"}, + {"RX INT8_1 MIX1 INP2", "RX4", "CDC_IF RX4 MUX"}, + {"RX INT8_1 MIX1 INP2", "RX5", "CDC_IF RX5 MUX"}, + {"RX INT8_1 MIX1 INP2", "RX6", "CDC_IF RX6 MUX"}, + {"RX INT8_1 MIX1 INP2", "RX7", "CDC_IF RX7 MUX"}, + {"RX INT8_1 MIX1 INP2", "IIR0", "IIR0"}, + + {"RX INT9_1 MIX1 INP0", "RX0", "CDC_IF RX0 MUX"}, + {"RX INT9_1 MIX1 INP0", "RX1", "CDC_IF RX1 MUX"}, + {"RX INT9_1 MIX1 INP0", "RX2", "CDC_IF RX2 MUX"}, + {"RX INT9_1 MIX1 INP0", "RX3", "CDC_IF RX3 MUX"}, + {"RX INT9_1 MIX1 INP0", "RX4", "CDC_IF RX4 MUX"}, + {"RX INT9_1 MIX1 INP0", "RX5", "CDC_IF RX5 MUX"}, + {"RX INT9_1 MIX1 INP0", "RX6", "CDC_IF RX6 MUX"}, + {"RX INT9_1 MIX1 INP0", "RX7", "CDC_IF RX7 MUX"}, + {"RX INT9_1 MIX1 INP0", "IIR0", "IIR0"}, + {"RX INT9_1 MIX1 INP1", "RX0", "CDC_IF RX0 MUX"}, + {"RX INT9_1 MIX1 INP1", "RX1", "CDC_IF RX1 MUX"}, + {"RX INT9_1 MIX1 INP1", "RX2", "CDC_IF RX2 MUX"}, + {"RX INT9_1 MIX1 INP1", "RX3", "CDC_IF RX3 MUX"}, + {"RX INT9_1 MIX1 INP1", "RX4", "CDC_IF RX4 MUX"}, + {"RX INT9_1 MIX1 INP1", "RX5", "CDC_IF RX5 MUX"}, + {"RX INT9_1 MIX1 INP1", "RX6", "CDC_IF RX6 MUX"}, + {"RX INT9_1 MIX1 INP1", "RX7", "CDC_IF RX7 MUX"}, + {"RX INT9_1 MIX1 INP1", "IIR0", "IIR0"}, + {"RX INT9_1 MIX1 INP2", "RX0", "CDC_IF RX0 MUX"}, + {"RX INT9_1 MIX1 INP2", "RX1", "CDC_IF RX1 MUX"}, + {"RX INT9_1 MIX1 INP2", "RX2", "CDC_IF RX2 MUX"}, + {"RX INT9_1 MIX1 INP2", "RX3", "CDC_IF RX3 MUX"}, + {"RX INT9_1 MIX1 INP2", "RX4", "CDC_IF RX4 MUX"}, + {"RX INT9_1 MIX1 INP2", "RX5", "CDC_IF RX5 MUX"}, + {"RX INT9_1 MIX1 INP2", "RX6", "CDC_IF RX6 MUX"}, + {"RX INT9_1 MIX1 INP2", "RX7", "CDC_IF RX7 MUX"}, + {"RX INT9_1 MIX1 INP2", "IIR0", "IIR0"}, + + {"RX INT0_1 MIX1", NULL, "RX INT0_1 MIX1 INP0"}, + {"RX INT0_1 MIX1", NULL, "RX INT0_1 MIX1 INP1"}, + {"RX INT0_1 MIX1", NULL, "RX INT0_1 MIX1 INP2"}, + {"RX INT7_1 MIX1", NULL, "RX INT7_1 MIX1 INP0"}, + {"RX INT7_1 MIX1", NULL, "RX INT7_1 MIX1 INP1"}, + {"RX INT7_1 MIX1", NULL, "RX INT7_1 MIX1 INP2"}, + {"RX INT8_1 MIX1", NULL, "RX INT8_1 MIX1 INP0"}, + {"RX INT8_1 MIX1", NULL, "RX INT8_1 MIX1 INP1"}, + {"RX INT8_1 MIX1", NULL, "RX INT8_1 MIX1 INP2"}, + {"RX INT9_1 MIX1", NULL, "RX INT9_1 MIX1 INP0"}, + {"RX INT9_1 MIX1", NULL, "RX INT9_1 MIX1 INP1"}, + {"RX INT9_1 MIX1", NULL, "RX INT9_1 MIX1 INP2"}, + + /* Mixing path INT0 */ + {"RX INT0_2 MUX", "RX0", "CDC_IF RX0 MUX"}, + {"RX INT0_2 MUX", "RX1", "CDC_IF RX1 MUX"}, + {"RX INT0_2 MUX", "RX2", "CDC_IF RX2 MUX"}, + {"RX INT0_2 MUX", "RX3", "CDC_IF RX3 MUX"}, + {"RX INT0_2 MUX", "RX4", "CDC_IF RX4 MUX"}, + {"RX INT0_2 MUX", "RX5", "CDC_IF RX5 MUX"}, + {"RX INT0_2 MUX", "RX6", "CDC_IF RX6 MUX"}, + {"RX INT0_2 MUX", "RX7", "CDC_IF RX7 MUX"}, + {"RX INT0_2 INTERP", NULL, "RX INT0_2 MUX"}, + {"RX INT0 SEC MIX", NULL, "RX INT0_2 INTERP"}, + + /* Mixing path INT7 */ + {"RX INT7_2 MUX", "RX0", "CDC_IF RX0 MUX"}, + {"RX INT7_2 MUX", "RX1", "CDC_IF RX1 MUX"}, + {"RX INT7_2 MUX", "RX2", "CDC_IF RX2 MUX"}, + {"RX INT7_2 MUX", "RX3", "CDC_IF RX3 MUX"}, + {"RX INT7_2 MUX", "RX4", "CDC_IF RX4 MUX"}, + {"RX INT7_2 MUX", "RX5", "CDC_IF RX5 MUX"}, + {"RX INT7_2 MUX", "RX6", "CDC_IF RX6 MUX"}, + {"RX INT7_2 MUX", "RX7", "CDC_IF RX7 MUX"}, + {"RX INT7_2 INTERP", NULL, "RX INT7_2 MUX"}, + {"RX INT7 SEC MIX", NULL, "RX INT7_2 INTERP"}, + + /* Mixing path INT8 */ + {"RX INT8_2 MUX", "RX0", "CDC_IF RX0 MUX"}, + {"RX INT8_2 MUX", "RX1", "CDC_IF RX1 MUX"}, + {"RX INT8_2 MUX", "RX2", "CDC_IF RX2 MUX"}, + {"RX INT8_2 MUX", "RX3", "CDC_IF RX3 MUX"}, + {"RX INT8_2 MUX", "RX4", "CDC_IF RX4 MUX"}, + {"RX INT8_2 MUX", "RX5", "CDC_IF RX5 MUX"}, + {"RX INT8_2 MUX", "RX6", "CDC_IF RX6 MUX"}, + {"RX INT8_2 MUX", "RX7", "CDC_IF RX7 MUX"}, + {"RX INT8_2 INTERP", NULL, "RX INT8_2 MUX"}, + {"RX INT8 SEC MIX", NULL, "RX INT8_2 INTERP"}, + + /* Mixing path INT9 */ + {"RX INT9_2 MUX", "RX0", "CDC_IF RX0 MUX"}, + {"RX INT9_2 MUX", "RX1", "CDC_IF RX1 MUX"}, + {"RX INT9_2 MUX", "RX2", "CDC_IF RX2 MUX"}, + {"RX INT9_2 MUX", "RX3", "CDC_IF RX3 MUX"}, + {"RX INT9_2 MUX", "RX4", "CDC_IF RX4 MUX"}, + {"RX INT9_2 MUX", "RX5", "CDC_IF RX5 MUX"}, + {"RX INT9_2 MUX", "RX6", "CDC_IF RX6 MUX"}, + {"RX INT9_2 MUX", "RX7", "CDC_IF RX7 MUX"}, + {"RX INT9_2 INTERP", NULL, "RX INT9_2 MUX"}, + {"RX INT9 SEC MIX", NULL, "RX INT9_2 INTERP"}, + + {"RX INT0_1 INTERP", NULL, "RX INT0_1 MIX1"}, + {"RX INT0 SEC MIX", NULL, "RX INT0_1 INTERP"}, + {"RX INT0 MIX2", NULL, "RX INT0 SEC MIX"}, + {"RX INT0 MIX2", NULL, "RX INT0 MIX2 INP"}, + {"RX INT0 DEM MUX", NULL, "RX INT0 MIX2"}, + {"RX INT0 DAC", NULL, "RX INT0 DEM MUX"}, + {"RX INT0 DAC", NULL, "LDO_RXTX"}, + {"EAR PA", NULL, "RX INT0 DAC"}, + {"EAR", NULL, "EAR PA"}, + + {"RX INT7_1 INTERP", NULL, "RX INT7_1 MIX1"}, + {"RX INT7 SEC MIX", NULL, "RX INT7_1 INTERP"}, + {"RX INT7 MIX2", NULL, "RX INT7 SEC MIX"}, + {"RX INT7 MIX2", NULL, "RX INT7 MIX2 INP"}, + {"RX INT7 CHAIN", NULL, "RX INT7 MIX2"}, + {"SPK1 OUT", NULL, "RX INT7 CHAIN"}, + + {"RX INT8_1 INTERP", NULL, "RX INT8_1 MIX1"}, + {"RX INT8 SEC MIX", NULL, "RX INT8_1 INTERP"}, + {"RX INT8 SEC MIX", NULL, "RX INT8_1 MIX1"}, + {"RX INT8 CHAIN", NULL, "RX INT8 SEC MIX"}, + {"SPK2 OUT", NULL, "RX INT8 CHAIN"}, + + {"RX INT9_1 INTERP", NULL, "RX INT9_1 MIX1"}, + {"RX INT9 SEC MIX", NULL, "RX INT9_1 INTERP"}, + {"RX INT9 MIX2", NULL, "RX INT9 SEC MIX"}, + {"RX INT9 MIX2", NULL, "RX INT9 MIX2 INP"}, + {"RX INT9 DEM MUX", NULL, "RX INT9 MIX2"}, + {"RX INT9 DAC", NULL, "RX INT9 DEM MUX"}, + {"RX INT9 DAC", NULL, "LDO_RXTX"}, + {"AUX PA", NULL, "RX INT9 DAC"}, + {"AUX", NULL, "AUX PA"}, + + /* ANC Routing */ + {"ANC0 FB MUX", "ANC_IN_EAR", "RX INT0 MIX2"}, + {"ANC0 FB MUX", "ANC_IN_EAR_SPKR", "RX INT7 MIX2"}, + + {"ANC OUT EAR Enable", "Switch", "ADC MUX10"}, + {"ANC OUT EAR Enable", "Switch", "ADC MUX11"}, + {"RX INT0 MIX2", NULL, "ANC OUT EAR Enable"}, + + {"ANC EAR PA", NULL, "RX INT0 DAC"}, + {"ANC EAR", NULL, "ANC EAR PA"}, + + {"ANC OUT EAR SPKR Enable", "Switch", "ADC MUX10"}, + {"ANC OUT EAR SPKR Enable", "Switch", "ADC MUX11"}, + {"RX INT7 MIX2", NULL, "ANC OUT EAR SPKR Enable"}, + + {"ANC SPKR PA Enable", "Switch", "RX INT7 CHAIN"}, + {"ANC SPK1 PA", NULL, "ANC SPKR PA Enable"}, + {"SPK1 OUT", NULL, "ANC SPK1 PA"}, + + /* + * SRC0 input to Sidetone RX Mixer + * on RX0, RX1, RX2, RX3, RX4 and RX7 chains + */ + {"IIR0", NULL, "IIR0 INP0 MUX"}, + {"IIR0 INP0 MUX", "DEC0", "ADC MUX0"}, + {"IIR0 INP0 MUX", "DEC1", "ADC MUX1"}, + {"IIR0 INP0 MUX", "DEC2", "ADC MUX2"}, + {"IIR0 INP0 MUX", "DEC3", "ADC MUX3"}, + {"IIR0 INP0 MUX", "DEC4", "ADC MUX4"}, + {"IIR0 INP0 MUX", "DEC5", "ADC MUX5"}, + {"IIR0 INP0 MUX", "DEC6", "ADC MUX6"}, + {"IIR0 INP0 MUX", "DEC7", "ADC MUX7"}, + {"IIR0 INP0 MUX", "DEC8", "ADC MUX8"}, + {"IIR0 INP0 MUX", "RX0", "CDC_IF RX0 MUX"}, + {"IIR0 INP0 MUX", "RX1", "CDC_IF RX1 MUX"}, + {"IIR0 INP0 MUX", "RX2", "CDC_IF RX2 MUX"}, + {"IIR0 INP0 MUX", "RX3", "CDC_IF RX3 MUX"}, + {"IIR0 INP0 MUX", "RX4", "CDC_IF RX4 MUX"}, + {"IIR0 INP0 MUX", "RX5", "CDC_IF RX5 MUX"}, + {"IIR0 INP0 MUX", "RX6", "CDC_IF RX6 MUX"}, + {"IIR0 INP0 MUX", "RX7", "CDC_IF RX7 MUX"}, + {"IIR0", NULL, "IIR0 INP1 MUX"}, + {"IIR0 INP1 MUX", "DEC0", "ADC MUX0"}, + {"IIR0 INP1 MUX", "DEC1", "ADC MUX1"}, + {"IIR0 INP1 MUX", "DEC2", "ADC MUX2"}, + {"IIR0 INP1 MUX", "DEC3", "ADC MUX3"}, + {"IIR0 INP1 MUX", "DEC4", "ADC MUX4"}, + {"IIR0 INP1 MUX", "DEC5", "ADC MUX5"}, + {"IIR0 INP1 MUX", "DEC6", "ADC MUX6"}, + {"IIR0 INP1 MUX", "DEC7", "ADC MUX7"}, + {"IIR0 INP1 MUX", "DEC8", "ADC MUX8"}, + {"IIR0 INP1 MUX", "RX0", "CDC_IF RX0 MUX"}, + {"IIR0 INP1 MUX", "RX1", "CDC_IF RX1 MUX"}, + {"IIR0 INP1 MUX", "RX2", "CDC_IF RX2 MUX"}, + {"IIR0 INP1 MUX", "RX3", "CDC_IF RX3 MUX"}, + {"IIR0 INP1 MUX", "RX4", "CDC_IF RX4 MUX"}, + {"IIR0 INP1 MUX", "RX5", "CDC_IF RX5 MUX"}, + {"IIR0 INP1 MUX", "RX6", "CDC_IF RX6 MUX"}, + {"IIR0 INP1 MUX", "RX7", "CDC_IF RX7 MUX"}, + {"IIR0", NULL, "IIR0 INP2 MUX"}, + {"IIR0 INP2 MUX", "DEC0", "ADC MUX0"}, + {"IIR0 INP2 MUX", "DEC1", "ADC MUX1"}, + {"IIR0 INP2 MUX", "DEC2", "ADC MUX2"}, + {"IIR0 INP2 MUX", "DEC3", "ADC MUX3"}, + {"IIR0 INP2 MUX", "DEC4", "ADC MUX4"}, + {"IIR0 INP2 MUX", "DEC5", "ADC MUX5"}, + {"IIR0 INP2 MUX", "DEC6", "ADC MUX6"}, + {"IIR0 INP2 MUX", "DEC7", "ADC MUX7"}, + {"IIR0 INP2 MUX", "DEC8", "ADC MUX8"}, + {"IIR0 INP2 MUX", "RX0", "CDC_IF RX0 MUX"}, + {"IIR0 INP2 MUX", "RX1", "CDC_IF RX1 MUX"}, + {"IIR0 INP2 MUX", "RX2", "CDC_IF RX2 MUX"}, + {"IIR0 INP2 MUX", "RX3", "CDC_IF RX3 MUX"}, + {"IIR0 INP2 MUX", "RX4", "CDC_IF RX4 MUX"}, + {"IIR0 INP2 MUX", "RX5", "CDC_IF RX5 MUX"}, + {"IIR0 INP2 MUX", "RX6", "CDC_IF RX6 MUX"}, + {"IIR0 INP2 MUX", "RX7", "CDC_IF RX7 MUX"}, + {"IIR0", NULL, "IIR0 INP3 MUX"}, + {"IIR0 INP3 MUX", "DEC0", "ADC MUX0"}, + {"IIR0 INP3 MUX", "DEC1", "ADC MUX1"}, + {"IIR0 INP3 MUX", "DEC2", "ADC MUX2"}, + {"IIR0 INP3 MUX", "DEC3", "ADC MUX3"}, + {"IIR0 INP3 MUX", "DEC4", "ADC MUX4"}, + {"IIR0 INP3 MUX", "DEC5", "ADC MUX5"}, + {"IIR0 INP3 MUX", "DEC6", "ADC MUX6"}, + {"IIR0 INP3 MUX", "DEC7", "ADC MUX7"}, + {"IIR0 INP3 MUX", "DEC8", "ADC MUX8"}, + {"IIR0 INP3 MUX", "RX0", "CDC_IF RX0 MUX"}, + {"IIR0 INP3 MUX", "RX1", "CDC_IF RX1 MUX"}, + {"IIR0 INP3 MUX", "RX2", "CDC_IF RX2 MUX"}, + {"IIR0 INP3 MUX", "RX3", "CDC_IF RX3 MUX"}, + {"IIR0 INP3 MUX", "RX4", "CDC_IF RX4 MUX"}, + {"IIR0 INP3 MUX", "RX5", "CDC_IF RX5 MUX"}, + {"IIR0 INP3 MUX", "RX6", "CDC_IF RX6 MUX"}, + {"IIR0 INP3 MUX", "RX7", "CDC_IF RX7 MUX"}, + + {"SRC0", NULL, "IIR0"}, + {"RX INT0 MIX2 INP", "SRC0", "SRC0"}, + {"RX INT7 MIX2 INP", "SRC0", "SRC0"}, + {"RX INT9 MIX2 INP", "SRC0", "SRC0"}, + + /* Native clk mix path routing */ + {"RX INT7_2 NATIVE MUX", "ON", "RX INT7_2 MUX"}, + {"RX INT7_2 INTERP", NULL, "RX INT7_2 NATIVE MUX"}, + {"RX INT7_2 NATIVE MUX", NULL, "RX INT7 NATIVE SUPPLY"}, + + {"RX INT8_2 NATIVE MUX", "ON", "RX INT8_2 MUX"}, + {"RX INT8_2 INTERP", NULL, "RX INT8_2 NATIVE MUX"}, + {"RX INT8_2 NATIVE MUX", NULL, "RX INT8 NATIVE SUPPLY"}, + + /* ASRC Routing */ + {"ASRC2 MUX", "ASRC_IN_SPKR1", "RX INT7_2 INTERP"}, + {"RX INT7 SEC MIX", NULL, "ASRC2 MUX"}, + + {"ASRC3 MUX", "ASRC_IN_SPKR2", "RX INT8_2 INTERP"}, + {"RX INT8 SEC MIX", NULL, "ASRC3 MUX"}, + + /* SLIMBUS-I2S Bridge interface */ + {"I2S TX1_0 MUX", "SB_RX2", "SLIM RX2"}, + + {"I2S TX1_1 MUX", "SB_RX0", "SLIM RX0"}, + {"I2S TX1_1 MUX", "SB_RX1", "SLIM RX1"}, + {"I2S TX1_1 MUX", "SB_RX2", "SLIM RX2"}, + {"I2S TX1_1 MUX", "SB_RX3", "SLIM RX3"}, + + {"I2S TX1 MIXER", NULL, "I2S TX1_0 MUX"}, + {"I2S TX1 MIXER", NULL, "I2S TX1_1 MUX"}, + {"I2S1 CAP", NULL, "I2S TX1 MIXER"}, + + {"CDC_IF TX10 MUX2", "I2SRX1_0_BRDG", "CDC_IF RX2 MUX"}, + {"CDC_IF TX11 MUX2", "I2SRX1_1_BRDG", "CDC_IF RX3 MUX"}, + {"CDC_IF RX2 MUX", "I2SRX1_0", "I2S1 PB"}, + {"CDC_IF RX3 MUX", "I2SRX1_1", "I2S1 PB"}, +}; + +#endif diff --git a/audio-kernel/asoc/codecs/wcd9360/wcd9360.c b/audio-kernel/asoc/codecs/wcd9360/wcd9360.c new file mode 100644 index 000000000..075a77fb6 --- /dev/null +++ b/audio-kernel/asoc/codecs/wcd9360/wcd9360.c @@ -0,0 +1,8101 @@ +/* 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 + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "wcd9360.h" +#include "wcd9360-routing.h" +#include "wcd9360-dsp-cntl.h" +#include "wcd9360-irq.h" +#include "../core.h" +#include "../pdata.h" +#include "../wcd9xxx-irq.h" +#include "../wcd9xxx-common-v2.h" +#include "../wcd9xxx-resmgr-v2.h" +#include "../wcdcal-hwdep.h" +#include "../msm-cdc-supply.h" + + +#define WCD9360_RATES_MASK (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\ + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |\ + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000 |\ + SNDRV_PCM_RATE_384000) +/* Fractional Rates */ +#define WCD9360_FRAC_RATES_MASK (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_88200 |\ + SNDRV_PCM_RATE_176400) + +#define WCD9360_FORMATS_S16_S24_LE (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE) + +#define WCD9360_FORMATS_S16_S24_S32_LE (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) + +#define WCD9360_FORMATS_S16_LE (SNDRV_PCM_FMTBIT_S16_LE) + +/* Macros for packing register writes into a U32 */ +#define WCD9360_PACKED_REG_SIZE sizeof(u32) +#define WCD9360_CODEC_UNPACK_ENTRY(packed, reg, mask, val) \ + do { \ + ((reg) = ((packed >> 16) & (0xffff))); \ + ((mask) = ((packed >> 8) & (0xff))); \ + ((val) = ((packed) & (0xff))); \ + } while (0) + +#define STRING(name) #name +#define WCD_DAPM_ENUM(name, reg, offset, text) \ +static SOC_ENUM_SINGLE_DECL(name##_enum, reg, offset, text); \ +static const struct snd_kcontrol_new name##_mux = \ + SOC_DAPM_ENUM(STRING(name), name##_enum) + +#define WCD_DAPM_ENUM_EXT(name, reg, offset, text, getname, putname) \ +static SOC_ENUM_SINGLE_DECL(name##_enum, reg, offset, text); \ +static const struct snd_kcontrol_new name##_mux = \ + SOC_DAPM_ENUM_EXT(STRING(name), name##_enum, getname, putname) + +#define WCD_DAPM_MUX(name, shift, kctl) \ + SND_SOC_DAPM_MUX(name, SND_SOC_NOPM, shift, 0, &kctl##_mux) + +/* + * Timeout in milli seconds and it is the wait time for + * slim channel removal interrupt to receive. + */ +#define WCD9360_SLIM_CLOSE_TIMEOUT 1000 +#define WCD9360_SLIM_IRQ_OVERFLOW (1 << 0) +#define WCD9360_SLIM_IRQ_UNDERFLOW (1 << 1) +#define WCD9360_SLIM_IRQ_PORT_CLOSED (1 << 2) + +#define WCD9360_MCLK_CLK_9P6MHZ 9600000 + +#define WCD9360_INTERP_MUX_NUM_INPUTS 3 +#define WCD9360_NUM_INTERPOLATORS 10 +#define WCD9360_NUM_DECIMATORS 9 +#define WCD9360_RX_PATH_CTL_OFFSET 20 +#define WCD9360_TLMM_DMIC_PINCFG_OFFSET 15 + +#define BYTE_BIT_MASK(nr) (1 << ((nr) % BITS_PER_BYTE)) + +#define WCD9360_REG_BITS 8 +#define WCD9360_MAX_VALID_ADC_MUX 11 +#define WCD9360_INVALID_ADC_MUX 9 + +#define WCD9360_AMIC_PWR_LEVEL_LP 0 +#define WCD9360_AMIC_PWR_LEVEL_DEFAULT 1 +#define WCD9360_AMIC_PWR_LEVEL_HP 2 +#define WCD9360_AMIC_PWR_LVL_MASK 0x60 +#define WCD9360_AMIC_PWR_LVL_SHIFT 0x5 + +#define WCD9360_DEC_PWR_LVL_MASK 0x06 +#define WCD9360_DEC_PWR_LVL_LP 0x02 +#define WCD9360_DEC_PWR_LVL_HP 0x04 +#define WCD9360_DEC_PWR_LVL_DF 0x00 +#define WCD9360_STRING_LEN 100 + +#define WCD9360_CDC_SIDETONE_IIR_COEFF_MAX 5 +#define WCD9360_CDC_REPEAT_WRITES_MAX 16 +#define WCD9360_DIG_CORE_REG_MIN WCD9360_CDC_ANC0_CLK_RESET_CTL +#define WCD9360_DIG_CORE_REG_MAX 0xFFF + +#define WCD9360_CHILD_DEVICES_MAX 6 + +#define WCD9360_MAX_MICBIAS 4 +#define DAPM_MICBIAS1_STANDALONE "MIC BIAS1 Standalone" +#define DAPM_MICBIAS2_STANDALONE "MIC BIAS2 Standalone" +#define DAPM_MICBIAS3_STANDALONE "MIC BIAS3 Standalone" +#define DAPM_MICBIAS4_STANDALONE "MIC BIAS4 Standalone" + +#define WCD9360_LDO_RXTX_SUPPLY_NAME "cdc-vdd-ldo-rxtx" + +#define TX_HPF_CUT_OFF_FREQ_MASK 0x60 +#define CF_MIN_3DB_4HZ 0x0 +#define CF_MIN_3DB_75HZ 0x1 +#define CF_MIN_3DB_150HZ 0x2 + +#define CPE_ERR_WDOG_BITE BIT(0) +#define CPE_FATAL_IRQS CPE_ERR_WDOG_BITE + +#define WCD9360_MAD_AUDIO_FIRMWARE_PATH "wcd9360/wcd9360_mad_audio.bin" + +#define PAHU_VERSION_ENTRY_SIZE 17 + +#define WCD9360_DIG_CORE_COLLAPSE_TIMER_MS (5 * 1000) + +enum { + INTERP_EAR = 0, + /* Headset and Lineout are not avalible in pahu */ + INTERP_HPHL_NA, + INTERP_HPHR_NA, + INTERP_LO1_NA, + INTERP_LO2_NA, + INTERP_LO3_NA, + INTERP_LO4_NA, + INTERP_SPKR1, + INTERP_SPKR2, + INTERP_AUX, + INTERP_MAX, +}; + +enum { + POWER_COLLAPSE, + POWER_RESUME, +}; + +static int dig_core_collapse_enable = 1; +module_param(dig_core_collapse_enable, int, 0664); +MODULE_PARM_DESC(dig_core_collapse_enable, "enable/disable power gating"); + +/* dig_core_collapse timer in seconds */ +static int dig_core_collapse_timer = (WCD9360_DIG_CORE_COLLAPSE_TIMER_MS/1000); +module_param(dig_core_collapse_timer, int, 0664); +MODULE_PARM_DESC(dig_core_collapse_timer, "timer for power gating"); + +enum { + VI_SENSE_1, + VI_SENSE_2, + CLK_INTERNAL, + CLK_MODE, +}; + +enum { + AIF1_PB = 0, + AIF1_CAP, + AIF2_PB, + AIF2_CAP, + AIF3_PB, + AIF3_CAP, + AIF4_PB, + AIF4_VIFEED, + AIF4_MAD_TX, + I2S1_PB, + I2S1_CAP, + NUM_CODEC_DAIS, +}; + +enum { + INTn_1_INP_SEL_ZERO = 0, + INTn_1_INP_SEL_DEC0, + INTn_1_INP_SEL_DEC1, + INTn_1_INP_SEL_IIR0, + INTn_1_INP_SEL_NA, + INTn_1_INP_SEL_RX0, + INTn_1_INP_SEL_RX1, + INTn_1_INP_SEL_RX2, + INTn_1_INP_SEL_RX3, + INTn_1_INP_SEL_RX4, + INTn_1_INP_SEL_RX5, + INTn_1_INP_SEL_RX6, + INTn_1_INP_SEL_RX7, +}; + +enum { + INTn_2_INP_SEL_ZERO = 0, + INTn_2_INP_SEL_RX0, + INTn_2_INP_SEL_RX1, + INTn_2_INP_SEL_RX2, + INTn_2_INP_SEL_RX3, + INTn_2_INP_SEL_RX4, + INTn_2_INP_SEL_RX5, + INTn_2_INP_SEL_RX6, + INTn_2_INP_SEL_RX7, + INTn_2_INP_SEL_PROXIMITY, +}; + +enum { + INTERP_MAIN_PATH, + INTERP_MIX_PATH, +}; + +struct pahu_cpr_reg_defaults { + int wr_data; + int wr_addr; +}; + +struct interp_sample_rate { + int sample_rate; + int rate_val; +}; + +static struct interp_sample_rate sr_val_tbl[] = { + {8000, 0x0}, {16000, 0x1}, {32000, 0x3}, {48000, 0x4}, {96000, 0x5}, + {192000, 0x6}, {384000, 0x7}, {44100, 0x9}, {88200, 0xA}, + {176400, 0xB}, {352800, 0xC}, +}; + +static const struct wcd9xxx_ch pahu_rx_chs[WCD9360_RX_MAX] = { + WCD9XXX_CH(WCD9360_RX_PORT_START_NUMBER, 0), + WCD9XXX_CH(WCD9360_RX_PORT_START_NUMBER + 1, 1), + WCD9XXX_CH(WCD9360_RX_PORT_START_NUMBER + 2, 2), + WCD9XXX_CH(WCD9360_RX_PORT_START_NUMBER + 3, 3), + WCD9XXX_CH(WCD9360_RX_PORT_START_NUMBER + 4, 4), + WCD9XXX_CH(WCD9360_RX_PORT_START_NUMBER + 5, 5), + WCD9XXX_CH(WCD9360_RX_PORT_START_NUMBER + 6, 6), + WCD9XXX_CH(WCD9360_RX_PORT_START_NUMBER + 7, 7), +}; + +static const struct wcd9xxx_ch pahu_tx_chs[WCD9360_TX_MAX] = { + WCD9XXX_CH(0, 0), + WCD9XXX_CH(1, 1), + WCD9XXX_CH(2, 2), + WCD9XXX_CH(3, 3), + WCD9XXX_CH(4, 4), + WCD9XXX_CH(5, 5), + WCD9XXX_CH(6, 6), + WCD9XXX_CH(7, 7), + WCD9XXX_CH(8, 8), + WCD9XXX_CH(9, 9), + WCD9XXX_CH(10, 10), + WCD9XXX_CH(11, 11), + WCD9XXX_CH(12, 12), + WCD9XXX_CH(13, 13), + WCD9XXX_CH(14, 14), + WCD9XXX_CH(15, 15), +}; + +static const u32 vport_slim_check_table[NUM_CODEC_DAIS] = { + 0, /* AIF1_PB */ + BIT(AIF2_CAP) | BIT(AIF3_CAP) | BIT(AIF4_MAD_TX), /* AIF1_CAP */ + 0, /* AIF2_PB */ + BIT(AIF1_CAP) | BIT(AIF3_CAP) | BIT(AIF4_MAD_TX), /* AIF2_CAP */ + 0, /* AIF3_PB */ + BIT(AIF1_CAP) | BIT(AIF2_CAP) | BIT(AIF4_MAD_TX), /* AIF3_CAP */ + 0, /* AIF4_PB */ +}; + +/* Codec supports 2 IIR filters */ +enum { + IIR0 = 0, + IIR_MAX, +}; + +/* Each IIR has 5 Filter Stages */ +enum { + BAND1 = 0, + BAND2, + BAND3, + BAND4, + BAND5, + BAND_MAX, +}; + +enum { + COMPANDER_0, /* EAR */ + COMPANDER_1, /* HPH_L */ + COMPANDER_2, /* HPH_R */ + COMPANDER_3, /* LO1_DIFF */ + COMPANDER_4, /* LO2_DIFF */ + COMPANDER_5, /* LO3_SE */ + COMPANDER_6, /* LO4_SE */ + COMPANDER_7, /* SWR SPK CH1 */ + COMPANDER_8, /* SWR SPK CH2 */ + COMPANDER_9, /* AUX */ + COMPANDER_MAX, +}; + +enum { + ASRC_IN_SPKR1, + ASRC_IN_SPKR2, + ASRC_INVALID, +}; + +enum { + ASRC2, + ASRC3, + ASRC_MAX, +}; + +enum { + CONV_88P2K_TO_384K, + CONV_96K_TO_352P8K, + CONV_352P8K_TO_384K, + CONV_384K_TO_352P8K, + CONV_384K_TO_384K, + CONV_96K_TO_384K, +}; + +static struct afe_param_slimbus_slave_port_cfg pahu_slimbus_slave_port_cfg = { + .minor_version = 1, + .slimbus_dev_id = AFE_SLIMBUS_DEVICE_1, + .slave_dev_pgd_la = 0, + .slave_dev_intfdev_la = 0, + .bit_width = 16, + .data_format = 0, + .num_channels = 1 +}; + +static struct afe_param_cdc_reg_page_cfg pahu_cdc_reg_page_cfg = { + .minor_version = AFE_API_VERSION_CDC_REG_PAGE_CFG, + .enable = 1, + .proc_id = AFE_CDC_REG_PAGE_ASSIGN_PROC_ID_1, +}; + +static struct afe_param_cdc_reg_cfg audio_reg_cfg[] = { + { + 1, + (WCD9360_REGISTER_START_OFFSET + WCD9360_SOC_MAD_MAIN_CTL_1), + HW_MAD_AUDIO_ENABLE, 0x1, WCD9360_REG_BITS, 0 + }, + { + 1, + (WCD9360_REGISTER_START_OFFSET + WCD9360_SOC_MAD_AUDIO_CTL_3), + HW_MAD_AUDIO_SLEEP_TIME, 0xF, WCD9360_REG_BITS, 0 + }, + { + 1, + (WCD9360_REGISTER_START_OFFSET + WCD9360_SOC_MAD_AUDIO_CTL_4), + HW_MAD_TX_AUDIO_SWITCH_OFF, 0x1, WCD9360_REG_BITS, 0 + }, + { + 1, + (WCD9360_REGISTER_START_OFFSET + WCD9360_INTR_CFG), + MAD_AUDIO_INT_DEST_SELECT_REG, 0x2, WCD9360_REG_BITS, 0 + }, + { + 1, + (WCD9360_REGISTER_START_OFFSET + WCD9360_INTR_PIN2_MASK3), + MAD_AUDIO_INT_MASK_REG, 0x1, WCD9360_REG_BITS, 0 + }, + { + 1, + (WCD9360_REGISTER_START_OFFSET + WCD9360_INTR_PIN2_STATUS3), + MAD_AUDIO_INT_STATUS_REG, 0x1, WCD9360_REG_BITS, 0 + }, + { + 1, + (WCD9360_REGISTER_START_OFFSET + WCD9360_INTR_PIN2_CLEAR3), + MAD_AUDIO_INT_CLEAR_REG, 0x1, WCD9360_REG_BITS, 0 + }, + { + 1, + (WCD9360_REGISTER_START_OFFSET + WCD9360_SB_PGD_PORT_TX_BASE), + SB_PGD_PORT_TX_WATERMARK_N, 0x1E, WCD9360_REG_BITS, 0x1 + }, + { + 1, + (WCD9360_REGISTER_START_OFFSET + WCD9360_SB_PGD_PORT_TX_BASE), + SB_PGD_PORT_TX_ENABLE_N, 0x1, WCD9360_REG_BITS, 0x1 + }, + { + 1, + (WCD9360_REGISTER_START_OFFSET + WCD9360_SB_PGD_PORT_RX_BASE), + SB_PGD_PORT_RX_WATERMARK_N, 0x1E, WCD9360_REG_BITS, 0x1 + }, + { + 1, + (WCD9360_REGISTER_START_OFFSET + WCD9360_SB_PGD_PORT_RX_BASE), + SB_PGD_PORT_RX_ENABLE_N, 0x1, WCD9360_REG_BITS, 0x1 + }, + { + 1, + (WCD9360_REGISTER_START_OFFSET + + WCD9360_CDC_ANC0_IIR_ADAPT_CTL), + AANC_FF_GAIN_ADAPTIVE, 0x4, WCD9360_REG_BITS, 0 + }, + { + 1, + (WCD9360_REGISTER_START_OFFSET + + WCD9360_CDC_ANC0_IIR_ADAPT_CTL), + AANC_FFGAIN_ADAPTIVE_EN, 0x8, WCD9360_REG_BITS, 0 + }, + { + 1, + (WCD9360_REGISTER_START_OFFSET + + WCD9360_CDC_ANC0_FF_A_GAIN_CTL), + AANC_GAIN_CONTROL, 0xFF, WCD9360_REG_BITS, 0 + }, + { + 1, + (WCD9360_REGISTER_START_OFFSET + + SB_PGD_TX_PORT_MULTI_CHANNEL_0(0)), + SB_PGD_TX_PORTn_MULTI_CHNL_0, 0xFF, WCD9360_REG_BITS, 0x4 + }, + { + 1, + (WCD9360_REGISTER_START_OFFSET + + SB_PGD_TX_PORT_MULTI_CHANNEL_1(0)), + SB_PGD_TX_PORTn_MULTI_CHNL_1, 0xFF, WCD9360_REG_BITS, 0x4 + }, + { + 1, + (WCD9360_REGISTER_START_OFFSET + + SB_PGD_RX_PORT_MULTI_CHANNEL_0(0x180, 0)), + SB_PGD_RX_PORTn_MULTI_CHNL_0, 0xFF, WCD9360_REG_BITS, 0x4 + }, + { + 1, + (WCD9360_REGISTER_START_OFFSET + + SB_PGD_RX_PORT_MULTI_CHANNEL_0(0x181, 0)), + SB_PGD_RX_PORTn_MULTI_CHNL_1, 0xFF, WCD9360_REG_BITS, 0x4 + }, +}; + +static struct afe_param_cdc_reg_cfg_data pahu_audio_reg_cfg = { + .num_registers = ARRAY_SIZE(audio_reg_cfg), + .reg_data = audio_reg_cfg, +}; + +static struct afe_param_id_cdc_aanc_version pahu_cdc_aanc_version = { + .cdc_aanc_minor_version = AFE_API_VERSION_CDC_AANC_VERSION, + .aanc_hw_version = AANC_HW_BLOCK_VERSION_2, +}; + +static const DECLARE_TLV_DB_SCALE(digital_gain, 0, 1, 0); +static const DECLARE_TLV_DB_SCALE(analog_gain, 0, 25, 1); + +#define WCD9360_TX_UNMUTE_DELAY_MS 40 + +static int tx_unmute_delay = WCD9360_TX_UNMUTE_DELAY_MS; +module_param(tx_unmute_delay, int, 0664); +MODULE_PARM_DESC(tx_unmute_delay, "delay to unmute the tx path"); + +static void pahu_codec_set_tx_hold(struct snd_soc_codec *, u16, bool); + +/* Hold instance to soundwire platform device */ +struct pahu_swr_ctrl_data { + struct platform_device *swr_pdev; +}; + +struct wcd_swr_ctrl_platform_data { + void *handle; /* holds codec private data */ + int (*read)(void *handle, int reg); + int (*write)(void *handle, int reg, int val); + int (*bulk_write)(void *handle, u32 *reg, u32 *val, size_t len); + int (*clk)(void *handle, bool enable); + int (*handle_irq)(void *handle, + irqreturn_t (*swrm_irq_handler)(int irq, void *data), + void *swrm_handle, int action); +}; + +/* Holds all Soundwire and speaker related information */ +struct wcd9360_swr { + struct pahu_swr_ctrl_data *ctrl_data; + struct wcd_swr_ctrl_platform_data plat_data; + struct mutex read_mutex; + struct mutex write_mutex; + struct mutex clk_mutex; + int spkr_gain_offset; + int spkr_mode; + int clk_users; + int rx_7_count; + int rx_8_count; +}; + +struct tx_mute_work { + struct pahu_priv *pahu; + u8 decimator; + struct delayed_work dwork; +}; + +#define WCD9360_SPK_ANC_EN_DELAY_MS 550 +static int spk_anc_en_delay = WCD9360_SPK_ANC_EN_DELAY_MS; +module_param(spk_anc_en_delay, int, 0664); +MODULE_PARM_DESC(spk_anc_en_delay, "delay to enable anc in speaker path"); + +struct spk_anc_work { + struct pahu_priv *pahu; + struct delayed_work dwork; +}; + +struct hpf_work { + struct pahu_priv *pahu; + u8 decimator; + u8 hpf_cut_off_freq; + struct delayed_work dwork; +}; + +struct pahu_priv { + struct device *dev; + struct wcd9xxx *wcd9xxx; + struct snd_soc_codec *codec; + s32 ldo_rxtx_cnt; + s32 dmic_0_1_clk_cnt; + s32 dmic_2_3_clk_cnt; + s32 dmic_4_5_clk_cnt; + s32 dmic_6_7_clk_cnt; + s32 micb_ref[PAHU_MAX_MICBIAS]; + s32 pullup_ref[PAHU_MAX_MICBIAS]; + + /* ANC related */ + u32 anc_slot; + bool anc_func; + + /* compander */ + int comp_enabled[COMPANDER_MAX]; + int ear_spkr_gain; + + /* Mad switch reference count */ + int mad_switch_cnt; + + /* track pahu interface type */ + u8 intf_type; + + /* to track the status */ + unsigned long status_mask; + + struct afe_param_cdc_slimbus_slave_cfg slimbus_slave_cfg; + + /* num of slim ports required */ + struct wcd9xxx_codec_dai_data dai[NUM_CODEC_DAIS]; + /* Port values for Rx and Tx codec_dai */ + unsigned int rx_port_value[WCD9360_RX_MAX]; + unsigned int tx_port_value; + + struct wcd9xxx_resmgr_v2 *resmgr; + struct wcd9360_swr swr; + struct mutex micb_lock; + + struct delayed_work power_gate_work; + struct mutex power_lock; + + struct clk *wcd_ext_clk; + + struct mutex codec_mutex; + struct work_struct pahu_add_child_devices_work; + struct hpf_work tx_hpf_work[WCD9360_NUM_DECIMATORS]; + struct tx_mute_work tx_mute_dwork[WCD9360_NUM_DECIMATORS]; + struct spk_anc_work spk_anc_dwork; + + unsigned int vi_feed_value; + + /* DSP control */ + struct wcd_dsp_cntl *wdsp_cntl; + + /* cal info for codec */ + struct fw_info *fw_data; + + /* Entry for version info */ + struct snd_info_entry *entry; + struct snd_info_entry *version_entry; + + /* SVS voting related */ + struct mutex svs_mutex; + int svs_ref_cnt; + + int native_clk_users; + /* ASRC users count */ + int asrc_users[ASRC_MAX]; + int asrc_output_mode[ASRC_MAX]; + /* Main path clock users count */ + int main_clk_users[WCD9360_NUM_INTERPOLATORS]; + + int power_active_ref; + u8 sidetone_coeff_array[IIR_MAX][BAND_MAX] + [WCD9360_CDC_SIDETONE_IIR_COEFF_MAX * 4]; + + struct spi_device *spi; + struct platform_device *pdev_child_devices + [WCD9360_CHILD_DEVICES_MAX]; + int child_count; + int i2s_ref_cnt; +}; + +static const struct pahu_reg_mask_val pahu_spkr_default[] = { + {WCD9360_CDC_COMPANDER7_CTL3, 0x80, 0x80}, + {WCD9360_CDC_COMPANDER8_CTL3, 0x80, 0x80}, + {WCD9360_CDC_COMPANDER7_CTL7, 0x01, 0x01}, + {WCD9360_CDC_COMPANDER8_CTL7, 0x01, 0x01}, + {WCD9360_CDC_BOOST0_BOOST_CTL, 0x7C, 0x50}, + {WCD9360_CDC_BOOST1_BOOST_CTL, 0x7C, 0x50}, +}; + +static const struct pahu_reg_mask_val pahu_spkr_mode1[] = { + {WCD9360_CDC_COMPANDER7_CTL3, 0x80, 0x00}, + {WCD9360_CDC_COMPANDER8_CTL3, 0x80, 0x00}, + {WCD9360_CDC_COMPANDER7_CTL7, 0x01, 0x00}, + {WCD9360_CDC_COMPANDER8_CTL7, 0x01, 0x00}, + {WCD9360_CDC_BOOST0_BOOST_CTL, 0x7C, 0x44}, + {WCD9360_CDC_BOOST1_BOOST_CTL, 0x7C, 0x44}, +}; + +static int __pahu_enable_efuse_sensing(struct pahu_priv *pahu); + +/** + * pahu_set_spkr_gain_offset - offset the speaker path + * gain with the given offset value. + * + * @codec: codec instance + * @offset: Indicates speaker path gain offset value. + * + * Returns 0 on success or -EINVAL on error. + */ +int pahu_set_spkr_gain_offset(struct snd_soc_codec *codec, int offset) +{ + struct pahu_priv *priv = snd_soc_codec_get_drvdata(codec); + + if (!priv) + return -EINVAL; + + priv->swr.spkr_gain_offset = offset; + return 0; +} +EXPORT_SYMBOL(pahu_set_spkr_gain_offset); + +/** + * pahu_set_spkr_mode - Configures speaker compander and smartboost + * settings based on speaker mode. + * + * @codec: codec instance + * @mode: Indicates speaker configuration mode. + * + * Returns 0 on success or -EINVAL on error. + */ +int pahu_set_spkr_mode(struct snd_soc_codec *codec, int mode) +{ + struct pahu_priv *priv = snd_soc_codec_get_drvdata(codec); + int i; + const struct pahu_reg_mask_val *regs; + int size; + + if (!priv) + return -EINVAL; + + switch (mode) { + case WCD9360_SPKR_MODE_1: + regs = pahu_spkr_mode1; + size = ARRAY_SIZE(pahu_spkr_mode1); + break; + default: + regs = pahu_spkr_default; + size = ARRAY_SIZE(pahu_spkr_default); + break; + } + + priv->swr.spkr_mode = mode; + for (i = 0; i < size; i++) + snd_soc_update_bits(codec, regs[i].reg, + regs[i].mask, regs[i].val); + return 0; +} +EXPORT_SYMBOL(pahu_set_spkr_mode); + +/** + * pahu_get_afe_config - returns specific codec configuration to afe to write + * + * @codec: codec instance + * @config_type: Indicates type of configuration to write. + */ +void *pahu_get_afe_config(struct snd_soc_codec *codec, + enum afe_config_type config_type) +{ + struct pahu_priv *priv = snd_soc_codec_get_drvdata(codec); + + switch (config_type) { + case AFE_SLIMBUS_SLAVE_CONFIG: + return &priv->slimbus_slave_cfg; + case AFE_CDC_REGISTERS_CONFIG: + return &pahu_audio_reg_cfg; + case AFE_SLIMBUS_SLAVE_PORT_CONFIG: + return &pahu_slimbus_slave_port_cfg; + case AFE_AANC_VERSION: + return &pahu_cdc_aanc_version; + case AFE_CDC_REGISTER_PAGE_CONFIG: + return &pahu_cdc_reg_page_cfg; + default: + dev_info(codec->dev, "%s: Unknown config_type 0x%x\n", + __func__, config_type); + return NULL; + } +} +EXPORT_SYMBOL(pahu_get_afe_config); + +static void pahu_vote_svs(struct pahu_priv *pahu, bool vote) +{ + struct wcd9xxx *wcd9xxx; + + wcd9xxx = pahu->wcd9xxx; + + mutex_lock(&pahu->svs_mutex); + if (vote) { + pahu->svs_ref_cnt++; + if (pahu->svs_ref_cnt == 1) + regmap_update_bits(wcd9xxx->regmap, + WCD9360_CPE_SS_PWR_SYS_PSTATE_CTL_0, + 0x01, 0x01); + } else { + /* Do not decrement ref count if it is already 0 */ + if (pahu->svs_ref_cnt == 0) + goto done; + + pahu->svs_ref_cnt--; + if (pahu->svs_ref_cnt == 0) + regmap_update_bits(wcd9xxx->regmap, + WCD9360_CPE_SS_PWR_SYS_PSTATE_CTL_0, + 0x01, 0x00); + } +done: + dev_dbg(pahu->dev, "%s: vote = %s, updated ref cnt = %u\n", __func__, + vote ? "vote" : "Unvote", pahu->svs_ref_cnt); + mutex_unlock(&pahu->svs_mutex); +} + +static int pahu_get_anc_slot(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct pahu_priv *pahu = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = pahu->anc_slot; + return 0; +} + +static int pahu_put_anc_slot(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct pahu_priv *pahu = snd_soc_codec_get_drvdata(codec); + + pahu->anc_slot = ucontrol->value.integer.value[0]; + return 0; +} + +static int pahu_get_anc_func(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct pahu_priv *pahu = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = (pahu->anc_func == true ? 1 : 0); + return 0; +} + +static int pahu_put_anc_func(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct pahu_priv *pahu = snd_soc_codec_get_drvdata(codec); + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + + mutex_lock(&pahu->codec_mutex); + pahu->anc_func = (!ucontrol->value.integer.value[0] ? false : true); + dev_dbg(codec->dev, "%s: anc_func %x", __func__, pahu->anc_func); + + if (pahu->anc_func == true) { + snd_soc_dapm_enable_pin(dapm, "ANC EAR PA"); + snd_soc_dapm_enable_pin(dapm, "ANC EAR"); + snd_soc_dapm_enable_pin(dapm, "ANC SPK1 PA"); + snd_soc_dapm_disable_pin(dapm, "EAR PA"); + snd_soc_dapm_disable_pin(dapm, "EAR"); + } else { + snd_soc_dapm_disable_pin(dapm, "ANC EAR PA"); + snd_soc_dapm_disable_pin(dapm, "ANC EAR"); + snd_soc_dapm_disable_pin(dapm, "ANC SPK1 PA"); + snd_soc_dapm_enable_pin(dapm, "EAR PA"); + snd_soc_dapm_enable_pin(dapm, "EAR"); + } + mutex_unlock(&pahu->codec_mutex); + + snd_soc_dapm_sync(dapm); + return 0; +} + +static int pahu_codec_enable_anc(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct pahu_priv *pahu = snd_soc_codec_get_drvdata(codec); + const char *filename; + const struct firmware *fw; + int i; + int ret = 0; + int num_anc_slots; + struct wcd9xxx_anc_header *anc_head; + struct firmware_cal *hwdep_cal = NULL; + u32 anc_writes_size = 0; + int anc_size_remaining; + u32 *anc_ptr; + u16 reg; + u8 mask, val; + size_t cal_size; + const void *data; + + if (!pahu->anc_func) + return 0; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + hwdep_cal = wcdcal_get_fw_cal(pahu->fw_data, WCD9XXX_ANC_CAL); + if (hwdep_cal) { + data = hwdep_cal->data; + cal_size = hwdep_cal->size; + dev_dbg(codec->dev, "%s: using hwdep calibration, cal_size %zd", + __func__, cal_size); + } else { + filename = "wcd9360/WCD9360_anc.bin"; + ret = request_firmware(&fw, filename, codec->dev); + if (ret < 0) { + dev_err(codec->dev, "%s: Failed to acquire ANC data: %d\n", + __func__, ret); + return ret; + } + if (!fw) { + dev_err(codec->dev, "%s: Failed to get anc fw\n", + __func__); + return -ENODEV; + } + data = fw->data; + cal_size = fw->size; + dev_dbg(codec->dev, "%s: using request_firmware calibration\n", + __func__); + } + if (cal_size < sizeof(struct wcd9xxx_anc_header)) { + dev_err(codec->dev, "%s: Invalid cal_size %zd\n", + __func__, cal_size); + ret = -EINVAL; + goto err; + } + /* First number is the number of register writes */ + anc_head = (struct wcd9xxx_anc_header *)(data); + anc_ptr = (u32 *)(data + sizeof(struct wcd9xxx_anc_header)); + anc_size_remaining = cal_size - + sizeof(struct wcd9xxx_anc_header); + num_anc_slots = anc_head->num_anc_slots; + + if (pahu->anc_slot >= num_anc_slots) { + dev_err(codec->dev, "%s: Invalid ANC slot selected\n", + __func__); + ret = -EINVAL; + goto err; + } + for (i = 0; i < num_anc_slots; i++) { + if (anc_size_remaining < WCD9360_PACKED_REG_SIZE) { + dev_err(codec->dev, "%s: Invalid register format\n", + __func__); + ret = -EINVAL; + goto err; + } + anc_writes_size = (u32)(*anc_ptr); + anc_size_remaining -= sizeof(u32); + anc_ptr += 1; + + if ((anc_writes_size * WCD9360_PACKED_REG_SIZE) > + anc_size_remaining) { + dev_err(codec->dev, "%s: Invalid register format\n", + __func__); + ret = -EINVAL; + goto err; + } + + if (pahu->anc_slot == i) + break; + + anc_size_remaining -= (anc_writes_size * + WCD9360_PACKED_REG_SIZE); + anc_ptr += anc_writes_size; + } + if (i == num_anc_slots) { + dev_err(codec->dev, "%s: Selected ANC slot not present\n", + __func__); + ret = -EINVAL; + goto err; + } + + for (i = 0; i < anc_writes_size; i++) { + WCD9360_CODEC_UNPACK_ENTRY(anc_ptr[i], reg, mask, val); + snd_soc_write(codec, reg, (val & mask)); + } + + if (!hwdep_cal) + release_firmware(fw); + break; + + case SND_SOC_DAPM_POST_PMU: + break; + + case SND_SOC_DAPM_POST_PMD: + if (!strcmp(w->name, "ANC EAR PA") || + !strcmp(w->name, "ANC SPK1 PA")) { + snd_soc_update_bits(codec, WCD9360_CDC_ANC0_MODE_1_CTL, + 0x30, 0x00); + msleep(50); + snd_soc_update_bits(codec, WCD9360_CDC_ANC0_MODE_1_CTL, + 0x01, 0x00); + snd_soc_update_bits(codec, + WCD9360_CDC_ANC0_CLK_RESET_CTL, + 0x38, 0x38); + snd_soc_update_bits(codec, + WCD9360_CDC_ANC0_CLK_RESET_CTL, + 0x07, 0x00); + snd_soc_update_bits(codec, + WCD9360_CDC_ANC0_CLK_RESET_CTL, + 0x38, 0x00); + } + break; + } + + return 0; +err: + if (!hwdep_cal) + release_firmware(fw); + return ret; +} + +static int pahu_get_clkmode(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct pahu_priv *pahu_p = snd_soc_codec_get_drvdata(codec); + + if (test_bit(CLK_MODE, &pahu_p->status_mask)) + ucontrol->value.enumerated.item[0] = 1; + else + ucontrol->value.enumerated.item[0] = 0; + + dev_dbg(codec->dev, "%s: is_low_power_clock: %s\n", __func__, + test_bit(CLK_MODE, &pahu_p->status_mask) ? "true" : "false"); + + return 0; +} + +static int pahu_put_clkmode(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct pahu_priv *pahu_p = snd_soc_codec_get_drvdata(codec); + + if (ucontrol->value.enumerated.item[0]) + set_bit(CLK_MODE, &pahu_p->status_mask); + else + clear_bit(CLK_MODE, &pahu_p->status_mask); + + dev_dbg(codec->dev, "%s: is_low_power_clock: %s\n", __func__, + test_bit(CLK_MODE, &pahu_p->status_mask) ? "true" : "false"); + + return 0; +} + +static int pahu_vi_feed_mixer_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm); + struct pahu_priv *pahu_p = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = pahu_p->vi_feed_value; + + return 0; +} + +static int pahu_vi_feed_mixer_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm); + struct pahu_priv *pahu_p = snd_soc_codec_get_drvdata(codec); + struct wcd9xxx *core = dev_get_drvdata(codec->dev->parent); + struct soc_multi_mixer_control *mixer = + ((struct soc_multi_mixer_control *)kcontrol->private_value); + u32 dai_id = widget->shift; + u32 port_id = mixer->shift; + u32 enable = ucontrol->value.integer.value[0]; + + dev_dbg(codec->dev, "%s: enable: %d, port_id:%d, dai_id: %d\n", + __func__, enable, port_id, dai_id); + + pahu_p->vi_feed_value = ucontrol->value.integer.value[0]; + + mutex_lock(&pahu_p->codec_mutex); + if (enable) { + if (port_id == WCD9360_TX14 && !test_bit(VI_SENSE_1, + &pahu_p->status_mask)) { + list_add_tail(&core->tx_chs[WCD9360_TX14].list, + &pahu_p->dai[dai_id].wcd9xxx_ch_list); + set_bit(VI_SENSE_1, &pahu_p->status_mask); + } + if (port_id == WCD9360_TX15 && !test_bit(VI_SENSE_2, + &pahu_p->status_mask)) { + list_add_tail(&core->tx_chs[WCD9360_TX15].list, + &pahu_p->dai[dai_id].wcd9xxx_ch_list); + set_bit(VI_SENSE_2, &pahu_p->status_mask); + } + } else { + if (port_id == WCD9360_TX14 && test_bit(VI_SENSE_1, + &pahu_p->status_mask)) { + list_del_init(&core->tx_chs[WCD9360_TX14].list); + clear_bit(VI_SENSE_1, &pahu_p->status_mask); + } + if (port_id == WCD9360_TX15 && test_bit(VI_SENSE_2, + &pahu_p->status_mask)) { + list_del_init(&core->tx_chs[WCD9360_TX15].list); + clear_bit(VI_SENSE_2, &pahu_p->status_mask); + } + } + mutex_unlock(&pahu_p->codec_mutex); + snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, enable, NULL); + + return 0; +} + +static int slim_tx_mixer_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm); + struct pahu_priv *pahu_p = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = pahu_p->tx_port_value; + return 0; +} + +static int slim_tx_mixer_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm); + struct pahu_priv *pahu_p = snd_soc_codec_get_drvdata(codec); + struct wcd9xxx *core = dev_get_drvdata(codec->dev->parent); + struct snd_soc_dapm_update *update = NULL; + struct soc_multi_mixer_control *mixer = + ((struct soc_multi_mixer_control *)kcontrol->private_value); + u32 dai_id = widget->shift; + u32 port_id = mixer->shift; + u32 enable = ucontrol->value.integer.value[0]; + u32 vtable; + + dev_dbg(codec->dev, "%s: wname %s cname %s value %u shift %d item %ld\n", + __func__, + widget->name, ucontrol->id.name, pahu_p->tx_port_value, + widget->shift, ucontrol->value.integer.value[0]); + + mutex_lock(&pahu_p->codec_mutex); + if (dai_id >= ARRAY_SIZE(vport_slim_check_table)) { + dev_err(codec->dev, "%s: dai_id: %d, out of bounds\n", + __func__, dai_id); + mutex_unlock(&pahu_p->codec_mutex); + return -EINVAL; + } + vtable = vport_slim_check_table[dai_id]; + + switch (dai_id) { + case AIF1_CAP: + case AIF2_CAP: + case AIF3_CAP: + /* only add to the list if value not set */ + if (enable && !(pahu_p->tx_port_value & 1 << port_id)) { + if (wcd9xxx_tx_vport_validation(vtable, port_id, + pahu_p->dai, NUM_CODEC_DAIS)) { + dev_dbg(codec->dev, "%s: TX%u is used by other virtual port\n", + __func__, port_id); + mutex_unlock(&pahu_p->codec_mutex); + return 0; + } + pahu_p->tx_port_value |= 1 << port_id; + list_add_tail(&core->tx_chs[port_id].list, + &pahu_p->dai[dai_id].wcd9xxx_ch_list); + } else if (!enable && (pahu_p->tx_port_value & + 1 << port_id)) { + pahu_p->tx_port_value &= ~(1 << port_id); + list_del_init(&core->tx_chs[port_id].list); + } else { + if (enable) + dev_dbg(codec->dev, "%s: TX%u port is used by\n" + "this virtual port\n", + __func__, port_id); + else + dev_dbg(codec->dev, "%s: TX%u port is not used by\n" + "this virtual port\n", + __func__, port_id); + /* avoid update power function */ + mutex_unlock(&pahu_p->codec_mutex); + return 0; + } + break; + case AIF4_MAD_TX: + break; + default: + dev_err(codec->dev, "Unknown AIF %d\n", dai_id); + mutex_unlock(&pahu_p->codec_mutex); + return -EINVAL; + } + dev_dbg(codec->dev, "%s: name %s sname %s updated value %u shift %d\n", + __func__, widget->name, widget->sname, pahu_p->tx_port_value, + widget->shift); + + mutex_unlock(&pahu_p->codec_mutex); + snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, enable, update); + + return 0; +} + +static int slim_rx_mux_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm); + struct pahu_priv *pahu_p = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.enumerated.item[0] = + pahu_p->rx_port_value[widget->shift]; + return 0; +} + +static int slim_rx_mux_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm); + struct pahu_priv *pahu_p = snd_soc_codec_get_drvdata(codec); + struct wcd9xxx *core = dev_get_drvdata(codec->dev->parent); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + struct snd_soc_dapm_update *update = NULL; + unsigned int rx_port_value; + u32 port_id = widget->shift; + + pahu_p->rx_port_value[port_id] = ucontrol->value.enumerated.item[0]; + rx_port_value = pahu_p->rx_port_value[port_id]; + + mutex_lock(&pahu_p->codec_mutex); + dev_dbg(codec->dev, "%s: wname %s cname %s value %u shift %d item %ld\n", + __func__, widget->name, ucontrol->id.name, + rx_port_value, widget->shift, + ucontrol->value.integer.value[0]); + + /* value need to match the Virtual port and AIF number */ + switch (rx_port_value) { + case 0: + list_del_init(&core->rx_chs[port_id].list); + break; + case 1: + if (wcd9xxx_rx_vport_validation(port_id + + WCD9360_RX_PORT_START_NUMBER, + &pahu_p->dai[AIF1_PB].wcd9xxx_ch_list)) { + dev_dbg(codec->dev, "%s: RX%u is used by current requesting AIF_PB itself\n", + __func__, port_id); + goto rtn; + } + list_add_tail(&core->rx_chs[port_id].list, + &pahu_p->dai[AIF1_PB].wcd9xxx_ch_list); + break; + case 2: + if (wcd9xxx_rx_vport_validation(port_id + + WCD9360_RX_PORT_START_NUMBER, + &pahu_p->dai[AIF2_PB].wcd9xxx_ch_list)) { + dev_dbg(codec->dev, "%s: RX%u is used by current requesting AIF_PB itself\n", + __func__, port_id); + goto rtn; + } + list_add_tail(&core->rx_chs[port_id].list, + &pahu_p->dai[AIF2_PB].wcd9xxx_ch_list); + break; + case 3: + if (wcd9xxx_rx_vport_validation(port_id + + WCD9360_RX_PORT_START_NUMBER, + &pahu_p->dai[AIF3_PB].wcd9xxx_ch_list)) { + dev_dbg(codec->dev, "%s: RX%u is used by current requesting AIF_PB itself\n", + __func__, port_id); + goto rtn; + } + list_add_tail(&core->rx_chs[port_id].list, + &pahu_p->dai[AIF3_PB].wcd9xxx_ch_list); + break; + case 4: + if (wcd9xxx_rx_vport_validation(port_id + + WCD9360_RX_PORT_START_NUMBER, + &pahu_p->dai[AIF4_PB].wcd9xxx_ch_list)) { + dev_dbg(codec->dev, "%s: RX%u is used by current requesting AIF_PB itself\n", + __func__, port_id); + goto rtn; + } + list_add_tail(&core->rx_chs[port_id].list, + &pahu_p->dai[AIF4_PB].wcd9xxx_ch_list); + break; + default: + dev_err(codec->dev, "Unknown AIF %d\n", rx_port_value); + goto err; + } +rtn: + mutex_unlock(&pahu_p->codec_mutex); + snd_soc_dapm_mux_update_power(widget->dapm, kcontrol, + rx_port_value, e, update); + + return 0; +err: + mutex_unlock(&pahu_p->codec_mutex); + return -EINVAL; +} + +static void pahu_codec_enable_slim_port_intr( + struct wcd9xxx_codec_dai_data *dai, + struct snd_soc_codec *codec) +{ + struct wcd9xxx_ch *ch; + int port_num = 0; + unsigned short reg = 0; + u8 val = 0; + struct pahu_priv *pahu_p; + + if (!dai || !codec) { + pr_err("%s: Invalid params\n", __func__); + return; + } + + pahu_p = snd_soc_codec_get_drvdata(codec); + list_for_each_entry(ch, &dai->wcd9xxx_ch_list, list) { + if (ch->port >= WCD9360_RX_PORT_START_NUMBER) { + port_num = ch->port - WCD9360_RX_PORT_START_NUMBER; + reg = WCD9360_SLIM_PGD_PORT_INT_RX_EN0 + (port_num / 8); + val = wcd9xxx_interface_reg_read(pahu_p->wcd9xxx, + reg); + if (!(val & BYTE_BIT_MASK(port_num))) { + val |= BYTE_BIT_MASK(port_num); + wcd9xxx_interface_reg_write( + pahu_p->wcd9xxx, reg, val); + val = wcd9xxx_interface_reg_read( + pahu_p->wcd9xxx, reg); + } + } else { + port_num = ch->port; + reg = WCD9360_SLIM_PGD_PORT_INT_TX_EN0 + (port_num / 8); + val = wcd9xxx_interface_reg_read(pahu_p->wcd9xxx, + reg); + if (!(val & BYTE_BIT_MASK(port_num))) { + val |= BYTE_BIT_MASK(port_num); + wcd9xxx_interface_reg_write(pahu_p->wcd9xxx, + reg, val); + val = wcd9xxx_interface_reg_read( + pahu_p->wcd9xxx, reg); + } + } + } +} + +static int pahu_codec_enable_slim_chmask(struct wcd9xxx_codec_dai_data *dai, + bool up) +{ + int ret = 0; + struct wcd9xxx_ch *ch; + + if (up) { + list_for_each_entry(ch, &dai->wcd9xxx_ch_list, list) { + ret = wcd9xxx_get_slave_port(ch->ch_num); + if (ret < 0) { + pr_err("%s: Invalid slave port ID: %d\n", + __func__, ret); + ret = -EINVAL; + } else { + set_bit(ret, &dai->ch_mask); + } + } + } else { + ret = wait_event_timeout(dai->dai_wait, (dai->ch_mask == 0), + msecs_to_jiffies( + WCD9360_SLIM_CLOSE_TIMEOUT)); + if (!ret) { + pr_err("%s: Slim close tx/rx wait timeout, ch_mask:0x%lx\n", + __func__, dai->ch_mask); + ret = -ETIMEDOUT; + } else { + ret = 0; + } + } + return ret; +} + +static int pahu_codec_enable_slimrx(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct wcd9xxx *core; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct pahu_priv *pahu_p = snd_soc_codec_get_drvdata(codec); + int ret = 0; + struct wcd9xxx_codec_dai_data *dai; + + core = dev_get_drvdata(codec->dev->parent); + + dev_dbg(codec->dev, "%s: event called! codec name %s num_dai %d\n" + "stream name %s event %d\n", + __func__, codec->component.name, + codec->component.num_dai, w->sname, event); + + dai = &pahu_p->dai[w->shift]; + dev_dbg(codec->dev, "%s: w->name %s w->shift %d event %d\n", + __func__, w->name, w->shift, event); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + dai->bus_down_in_recovery = false; + pahu_codec_enable_slim_port_intr(dai, codec); + (void) pahu_codec_enable_slim_chmask(dai, true); + ret = wcd9xxx_cfg_slim_sch_rx(core, &dai->wcd9xxx_ch_list, + dai->rate, dai->bit_width, + &dai->grph); + break; + case SND_SOC_DAPM_POST_PMD: + ret = wcd9xxx_disconnect_port(core, &dai->wcd9xxx_ch_list, + dai->grph); + dev_dbg(codec->dev, "%s: Disconnect RX port, ret = %d\n", + __func__, ret); + + if (!dai->bus_down_in_recovery) + ret = pahu_codec_enable_slim_chmask(dai, false); + else + dev_dbg(codec->dev, + "%s: bus in recovery skip enable slim_chmask", + __func__); + ret = wcd9xxx_close_slim_sch_rx(core, &dai->wcd9xxx_ch_list, + dai->grph); + break; + } + return ret; +} + +static int pahu_codec_enable_slimtx(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct pahu_priv *pahu_p = snd_soc_codec_get_drvdata(codec); + struct wcd9xxx_codec_dai_data *dai; + struct wcd9xxx *core; + int ret = 0; + + dev_dbg(codec->dev, + "%s: w->name %s, w->shift = %d, num_dai %d stream name %s\n", + __func__, w->name, w->shift, + codec->component.num_dai, w->sname); + + dai = &pahu_p->dai[w->shift]; + core = dev_get_drvdata(codec->dev->parent); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + dai->bus_down_in_recovery = false; + pahu_codec_enable_slim_port_intr(dai, codec); + (void) pahu_codec_enable_slim_chmask(dai, true); + ret = wcd9xxx_cfg_slim_sch_tx(core, &dai->wcd9xxx_ch_list, + dai->rate, dai->bit_width, + &dai->grph); + break; + case SND_SOC_DAPM_POST_PMD: + ret = wcd9xxx_close_slim_sch_tx(core, &dai->wcd9xxx_ch_list, + dai->grph); + if (!dai->bus_down_in_recovery) + ret = pahu_codec_enable_slim_chmask(dai, false); + if (ret < 0) { + ret = wcd9xxx_disconnect_port(core, + &dai->wcd9xxx_ch_list, + dai->grph); + dev_dbg(codec->dev, "%s: Disconnect RX port, ret = %d\n", + __func__, ret); + } + break; + } + return ret; +} + +static int pahu_codec_enable_slimvi_feedback(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct wcd9xxx *core = NULL; + struct snd_soc_codec *codec = NULL; + struct pahu_priv *pahu_p = NULL; + int ret = 0; + struct wcd9xxx_codec_dai_data *dai = NULL; + + codec = snd_soc_dapm_to_codec(w->dapm); + pahu_p = snd_soc_codec_get_drvdata(codec); + core = dev_get_drvdata(codec->dev->parent); + + dev_dbg(codec->dev, + "%s: num_dai %d stream name %s w->name %s event %d shift %d\n", + __func__, codec->component.num_dai, w->sname, + w->name, event, w->shift); + + if (w->shift != AIF4_VIFEED) { + pr_err("%s Error in enabling the tx path\n", __func__); + ret = -EINVAL; + goto done; + } + dai = &pahu_p->dai[w->shift]; + switch (event) { + case SND_SOC_DAPM_POST_PMU: + if (test_bit(VI_SENSE_1, &pahu_p->status_mask)) { + dev_dbg(codec->dev, "%s: spkr1 enabled\n", __func__); + /* Enable V&I sensing */ + snd_soc_update_bits(codec, + WCD9360_CDC_TX9_SPKR_PROT_PATH_CTL, 0x20, 0x20); + snd_soc_update_bits(codec, + WCD9360_CDC_TX10_SPKR_PROT_PATH_CTL, 0x20, + 0x20); + snd_soc_update_bits(codec, + WCD9360_CDC_TX9_SPKR_PROT_PATH_CTL, 0x0F, 0x00); + snd_soc_update_bits(codec, + WCD9360_CDC_TX10_SPKR_PROT_PATH_CTL, 0x0F, + 0x00); + snd_soc_update_bits(codec, + WCD9360_CDC_TX9_SPKR_PROT_PATH_CTL, 0x10, 0x10); + snd_soc_update_bits(codec, + WCD9360_CDC_TX10_SPKR_PROT_PATH_CTL, 0x10, + 0x10); + snd_soc_update_bits(codec, + WCD9360_CDC_TX9_SPKR_PROT_PATH_CTL, 0x20, 0x00); + snd_soc_update_bits(codec, + WCD9360_CDC_TX10_SPKR_PROT_PATH_CTL, 0x20, + 0x00); + } + if (test_bit(VI_SENSE_2, &pahu_p->status_mask)) { + pr_debug("%s: spkr2 enabled\n", __func__); + /* Enable V&I sensing */ + snd_soc_update_bits(codec, + WCD9360_CDC_TX11_SPKR_PROT_PATH_CTL, 0x20, + 0x20); + snd_soc_update_bits(codec, + WCD9360_CDC_TX12_SPKR_PROT_PATH_CTL, 0x20, + 0x20); + snd_soc_update_bits(codec, + WCD9360_CDC_TX11_SPKR_PROT_PATH_CTL, 0x0F, + 0x00); + snd_soc_update_bits(codec, + WCD9360_CDC_TX12_SPKR_PROT_PATH_CTL, 0x0F, + 0x00); + snd_soc_update_bits(codec, + WCD9360_CDC_TX11_SPKR_PROT_PATH_CTL, 0x10, + 0x10); + snd_soc_update_bits(codec, + WCD9360_CDC_TX12_SPKR_PROT_PATH_CTL, 0x10, + 0x10); + snd_soc_update_bits(codec, + WCD9360_CDC_TX11_SPKR_PROT_PATH_CTL, 0x20, + 0x00); + snd_soc_update_bits(codec, + WCD9360_CDC_TX12_SPKR_PROT_PATH_CTL, 0x20, + 0x00); + } + dai->bus_down_in_recovery = false; + pahu_codec_enable_slim_port_intr(dai, codec); + (void) pahu_codec_enable_slim_chmask(dai, true); + ret = wcd9xxx_cfg_slim_sch_tx(core, &dai->wcd9xxx_ch_list, + dai->rate, dai->bit_width, + &dai->grph); + break; + case SND_SOC_DAPM_POST_PMD: + ret = wcd9xxx_close_slim_sch_tx(core, &dai->wcd9xxx_ch_list, + dai->grph); + if (ret) + dev_err(codec->dev, "%s error in close_slim_sch_tx %d\n", + __func__, ret); + if (!dai->bus_down_in_recovery) + ret = pahu_codec_enable_slim_chmask(dai, false); + if (ret < 0) { + ret = wcd9xxx_disconnect_port(core, + &dai->wcd9xxx_ch_list, + dai->grph); + dev_dbg(codec->dev, "%s: Disconnect TX port, ret = %d\n", + __func__, ret); + } + if (test_bit(VI_SENSE_1, &pahu_p->status_mask)) { + /* Disable V&I sensing */ + dev_dbg(codec->dev, "%s: spkr1 disabled\n", __func__); + snd_soc_update_bits(codec, + WCD9360_CDC_TX9_SPKR_PROT_PATH_CTL, 0x20, 0x20); + snd_soc_update_bits(codec, + WCD9360_CDC_TX10_SPKR_PROT_PATH_CTL, 0x20, + 0x20); + snd_soc_update_bits(codec, + WCD9360_CDC_TX9_SPKR_PROT_PATH_CTL, 0x10, 0x00); + snd_soc_update_bits(codec, + WCD9360_CDC_TX10_SPKR_PROT_PATH_CTL, 0x10, + 0x00); + } + if (test_bit(VI_SENSE_2, &pahu_p->status_mask)) { + /* Disable V&I sensing */ + dev_dbg(codec->dev, "%s: spkr2 disabled\n", __func__); + snd_soc_update_bits(codec, + WCD9360_CDC_TX11_SPKR_PROT_PATH_CTL, 0x20, + 0x20); + snd_soc_update_bits(codec, + WCD9360_CDC_TX12_SPKR_PROT_PATH_CTL, 0x20, + 0x20); + snd_soc_update_bits(codec, + WCD9360_CDC_TX11_SPKR_PROT_PATH_CTL, 0x10, + 0x00); + snd_soc_update_bits(codec, + WCD9360_CDC_TX12_SPKR_PROT_PATH_CTL, 0x10, + 0x00); + } + break; + } +done: + return ret; +} + +static void pahu_codec_enable_i2s(struct snd_soc_codec *codec, bool enable) +{ + struct pahu_priv *pahu = snd_soc_codec_get_drvdata(codec); + + if (enable) { + if (++pahu->i2s_ref_cnt == 1) + snd_soc_update_bits(codec, WCD9360_DATA_HUB_I2S_1_CTL, + 0x01, 0x01); + } else { + if (--pahu->i2s_ref_cnt == 0) + snd_soc_update_bits(codec, WCD9360_DATA_HUB_I2S_1_CTL, + 0x01, 0x00); + } +} + +static int pahu_i2s_aif_rx_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + + dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event); + switch(event) { + case SND_SOC_DAPM_PRE_PMU: + pahu_cdc_mclk_enable(codec, true); + break; + case SND_SOC_DAPM_POST_PMU: + pahu_codec_enable_i2s(codec, true); + break; + case SND_SOC_DAPM_PRE_PMD: + pahu_codec_enable_i2s(codec, false); + break; + case SND_SOC_DAPM_POST_PMD: + pahu_cdc_mclk_enable(codec, false); + break; + } + + return 0; +} + +static int pahu_i2s_aif_tx_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + + dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event); + switch(event) { + case SND_SOC_DAPM_PRE_PMU: + pahu_cdc_mclk_enable(codec, true); + break; + case SND_SOC_DAPM_POST_PMU: + pahu_codec_enable_i2s(codec, true); + break; + case SND_SOC_DAPM_PRE_PMD: + pahu_codec_enable_i2s(codec, false); + break; + case SND_SOC_DAPM_POST_PMD: + pahu_cdc_mclk_enable(codec, false); + break; + } + + return 0; +} + +static int pahu_codec_enable_ldo_rxtx(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct pahu_priv *pahu = snd_soc_codec_get_drvdata(codec); + struct wcd9xxx_pdata *pdata = dev_get_platdata(codec->dev->parent); + + dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + pahu->ldo_rxtx_cnt++; + if (pahu->ldo_rxtx_cnt == 1) { + /* Enable VDD_LDO_RxTx regulator */ + msm_cdc_enable_ondemand_supply(pahu->wcd9xxx->dev, + pahu->wcd9xxx->supplies, + pdata->regulator, + pdata->num_supplies, + WCD9360_LDO_RXTX_SUPPLY_NAME); + + snd_soc_update_bits(codec, WCD9360_LDORXTX_LDORXTX, + 0x80, 0x80); + /* + * 200us sleep is required after LDO_RXTX is enabled as per + * HW requirement + */ + usleep_range(200, 250); + } + break; + case SND_SOC_DAPM_POST_PMD: + pahu->ldo_rxtx_cnt--; + if (pahu->ldo_rxtx_cnt < 0) + pahu->ldo_rxtx_cnt = 0; + + if (!pahu->ldo_rxtx_cnt) { + snd_soc_update_bits(codec, WCD9360_LDORXTX_LDORXTX, + 0x80, 0x00); + /* Disable VDD_LDO_RxTx regulator */ + msm_cdc_disable_ondemand_supply(pahu->wcd9xxx->dev, + pahu->wcd9xxx->supplies, + pdata->regulator, + pdata->num_supplies, + WCD9360_LDO_RXTX_SUPPLY_NAME); + } + break; + }; + dev_dbg(codec->dev, "%s: Current LDO RXTX user count: %d\n", __func__, + pahu->ldo_rxtx_cnt); + + return 0; +} + +static void pahu_spk_anc_update_callback(struct work_struct *work) +{ + struct spk_anc_work *spk_anc_dwork; + struct pahu_priv *pahu; + struct delayed_work *delayed_work; + struct snd_soc_codec *codec; + + delayed_work = to_delayed_work(work); + spk_anc_dwork = container_of(delayed_work, struct spk_anc_work, dwork); + pahu = spk_anc_dwork->pahu; + codec = pahu->codec; + + snd_soc_update_bits(codec, WCD9360_CDC_RX7_RX_PATH_CFG0, 0x10, 0x10); +} + +static int pahu_codec_enable_spkr_anc(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + int ret = 0; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct pahu_priv *pahu = snd_soc_codec_get_drvdata(codec); + + if (!pahu->anc_func) + return 0; + + dev_dbg(codec->dev, "%s: w: %s event: %d anc: %d\n", __func__, + w->name, event, pahu->anc_func); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + ret = pahu_codec_enable_anc(w, kcontrol, event); + schedule_delayed_work(&pahu->spk_anc_dwork.dwork, + msecs_to_jiffies(spk_anc_en_delay)); + break; + case SND_SOC_DAPM_POST_PMD: + cancel_delayed_work_sync(&pahu->spk_anc_dwork.dwork); + snd_soc_update_bits(codec, WCD9360_CDC_RX7_RX_PATH_CFG0, + 0x10, 0x00); + ret = pahu_codec_enable_anc(w, kcontrol, event); + break; + } + return ret; +} + +static int pahu_codec_enable_aux_pa(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + int ret = 0; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + + dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + /* + * 5ms sleep is required after PA is enabled as per + * HW requirement + */ + usleep_range(5000, 5500); + snd_soc_update_bits(codec, WCD9360_CDC_RX9_RX_PATH_CTL, + 0x10, 0x00); + /* Remove mix path mute if it is enabled */ + if ((snd_soc_read(codec, WCD9360_CDC_RX9_RX_PATH_MIX_CTL)) & + 0x10) + snd_soc_update_bits(codec, + WCD9360_CDC_RX9_RX_PATH_MIX_CTL, + 0x10, 0x00); + break; + default: + break; + }; + + return ret; +} + +static int pahu_codec_enable_ear_pa(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + int ret = 0; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + + dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + /* + * 5ms sleep is required after PA is enabled as per + * HW requirement + */ + usleep_range(5000, 5500); + snd_soc_update_bits(codec, WCD9360_CDC_RX0_RX_PATH_CTL, + 0x10, 0x00); + /* Remove mix path mute if it is enabled */ + if ((snd_soc_read(codec, WCD9360_CDC_RX0_RX_PATH_MIX_CTL)) & + 0x10) + snd_soc_update_bits(codec, + WCD9360_CDC_RX0_RX_PATH_MIX_CTL, + 0x10, 0x00); + break; + case SND_SOC_DAPM_POST_PMD: + /* + * 5ms sleep is required after PA is disabled as per + * HW requirement + */ + usleep_range(5000, 5500); + + if (!(strcmp(w->name, "ANC EAR PA"))) { + ret = pahu_codec_enable_anc(w, kcontrol, event); + snd_soc_update_bits(codec, WCD9360_CDC_RX0_RX_PATH_CFG0, + 0x10, 0x00); + } + break; + }; + + return ret; +} + +static int pahu_codec_ear_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + int ret = 0; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct pahu_priv *pahu = snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (pahu->anc_func) { + ret = pahu_codec_enable_anc(w, kcontrol, event); + snd_soc_update_bits(codec, WCD9360_CDC_RX0_RX_PATH_CFG0, + 0x10, 0x10); + } + break; + default: + break; + }; + + return ret; +} + +static int pahu_codec_spk_boost_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + u16 boost_path_ctl, boost_path_cfg1; + u16 reg, reg_mix; + + dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event); + + if (!strcmp(w->name, "RX INT7 CHAIN")) { + boost_path_ctl = WCD9360_CDC_BOOST0_BOOST_PATH_CTL; + boost_path_cfg1 = WCD9360_CDC_RX7_RX_PATH_CFG1; + reg = WCD9360_CDC_RX7_RX_PATH_CTL; + reg_mix = WCD9360_CDC_RX7_RX_PATH_MIX_CTL; + } else if (!strcmp(w->name, "RX INT8 CHAIN")) { + boost_path_ctl = WCD9360_CDC_BOOST1_BOOST_PATH_CTL; + boost_path_cfg1 = WCD9360_CDC_RX8_RX_PATH_CFG1; + reg = WCD9360_CDC_RX8_RX_PATH_CTL; + reg_mix = WCD9360_CDC_RX8_RX_PATH_MIX_CTL; + } else { + dev_err(codec->dev, "%s: unknown widget: %s\n", + __func__, w->name); + return -EINVAL; + } + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_update_bits(codec, boost_path_cfg1, 0x01, 0x01); + snd_soc_update_bits(codec, boost_path_ctl, 0x10, 0x10); + snd_soc_update_bits(codec, reg, 0x10, 0x00); + if ((snd_soc_read(codec, reg_mix)) & 0x10) + snd_soc_update_bits(codec, reg_mix, 0x10, 0x00); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_update_bits(codec, boost_path_ctl, 0x10, 0x00); + snd_soc_update_bits(codec, boost_path_cfg1, 0x01, 0x00); + break; + }; + + return 0; +} + +static int __pahu_codec_enable_swr(struct snd_soc_dapm_widget *w, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct pahu_priv *pahu; + int ch_cnt = 0; + + pahu = snd_soc_codec_get_drvdata(codec); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (((strnstr(w->name, "INT7_", sizeof("RX INT7_"))) || + (strnstr(w->name, "INT7 MIX2", + sizeof("RX INT7 MIX2"))))) + pahu->swr.rx_7_count++; + if ((strnstr(w->name, "INT8_", sizeof("RX INT8_"))) && + !pahu->swr.rx_8_count) + pahu->swr.rx_8_count++; + ch_cnt = !!(pahu->swr.rx_7_count) + pahu->swr.rx_8_count; + + swrm_wcd_notify(pahu->swr.ctrl_data[0].swr_pdev, + SWR_DEVICE_UP, NULL); + swrm_wcd_notify(pahu->swr.ctrl_data[0].swr_pdev, + SWR_SET_NUM_RX_CH, &ch_cnt); + break; + case SND_SOC_DAPM_POST_PMD: + if ((strnstr(w->name, "INT7_", sizeof("RX INT7_"))) || + (strnstr(w->name, "INT7 MIX2", + sizeof("RX INT7 MIX2")))) + pahu->swr.rx_7_count--; + if ((strnstr(w->name, "INT8_", sizeof("RX INT8_"))) && + pahu->swr.rx_8_count) + pahu->swr.rx_8_count--; + ch_cnt = !!(pahu->swr.rx_7_count) + pahu->swr.rx_8_count; + + swrm_wcd_notify(pahu->swr.ctrl_data[0].swr_pdev, + SWR_SET_NUM_RX_CH, &ch_cnt); + + break; + } + dev_dbg(pahu->dev, "%s: %s: current swr ch cnt: %d\n", + __func__, w->name, ch_cnt); + + return 0; +} + +static int pahu_codec_enable_swr(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + return __pahu_codec_enable_swr(w, event); +} + +static int pahu_codec_config_mad(struct snd_soc_codec *codec) +{ + int ret = 0; + int idx; + const struct firmware *fw; + struct firmware_cal *hwdep_cal = NULL; + struct wcd_mad_audio_cal *mad_cal = NULL; + const void *data; + const char *filename = WCD9360_MAD_AUDIO_FIRMWARE_PATH; + struct pahu_priv *pahu = snd_soc_codec_get_drvdata(codec); + size_t cal_size; + + hwdep_cal = wcdcal_get_fw_cal(pahu->fw_data, WCD9XXX_MAD_CAL); + if (hwdep_cal) { + data = hwdep_cal->data; + cal_size = hwdep_cal->size; + dev_dbg(codec->dev, "%s: using hwdep calibration\n", + __func__); + } else { + ret = request_firmware(&fw, filename, codec->dev); + if (ret || !fw) { + dev_err(codec->dev, + "%s: MAD firmware acquire failed, err = %d\n", + __func__, ret); + return -ENODEV; + } + data = fw->data; + cal_size = fw->size; + dev_dbg(codec->dev, "%s: using request_firmware calibration\n", + __func__); + } + + if (cal_size < sizeof(*mad_cal)) { + dev_err(codec->dev, + "%s: Incorrect size %zd for MAD Cal, expected %zd\n", + __func__, cal_size, sizeof(*mad_cal)); + ret = -ENOMEM; + goto done; + } + + mad_cal = (struct wcd_mad_audio_cal *) (data); + if (!mad_cal) { + dev_err(codec->dev, + "%s: Invalid calibration data\n", + __func__); + ret = -EINVAL; + goto done; + } + + snd_soc_write(codec, WCD9360_SOC_MAD_MAIN_CTL_2, + mad_cal->microphone_info.cycle_time); + snd_soc_update_bits(codec, WCD9360_SOC_MAD_MAIN_CTL_1, 0xFF << 3, + ((uint16_t)mad_cal->microphone_info.settle_time) + << 3); + + /* Audio */ + snd_soc_write(codec, WCD9360_SOC_MAD_AUDIO_CTL_8, + mad_cal->audio_info.rms_omit_samples); + snd_soc_update_bits(codec, WCD9360_SOC_MAD_AUDIO_CTL_1, + 0x07 << 4, mad_cal->audio_info.rms_comp_time << 4); + snd_soc_update_bits(codec, WCD9360_SOC_MAD_AUDIO_CTL_2, 0x03 << 2, + mad_cal->audio_info.detection_mechanism << 2); + snd_soc_write(codec, WCD9360_SOC_MAD_AUDIO_CTL_7, + mad_cal->audio_info.rms_diff_threshold & 0x3F); + snd_soc_write(codec, WCD9360_SOC_MAD_AUDIO_CTL_5, + mad_cal->audio_info.rms_threshold_lsb); + snd_soc_write(codec, WCD9360_SOC_MAD_AUDIO_CTL_6, + mad_cal->audio_info.rms_threshold_msb); + + for (idx = 0; idx < ARRAY_SIZE(mad_cal->audio_info.iir_coefficients); + idx++) { + snd_soc_update_bits(codec, WCD9360_SOC_MAD_AUDIO_IIR_CTL_PTR, + 0x3F, idx); + snd_soc_write(codec, WCD9360_SOC_MAD_AUDIO_IIR_CTL_VAL, + mad_cal->audio_info.iir_coefficients[idx]); + dev_dbg(codec->dev, "%s:MAD Audio IIR Coef[%d] = 0X%x", + __func__, idx, + mad_cal->audio_info.iir_coefficients[idx]); + } + + /* Beacon */ + snd_soc_write(codec, WCD9360_SOC_MAD_BEACON_CTL_8, + mad_cal->beacon_info.rms_omit_samples); + snd_soc_update_bits(codec, WCD9360_SOC_MAD_BEACON_CTL_1, + 0x07 << 4, mad_cal->beacon_info.rms_comp_time << 4); + snd_soc_update_bits(codec, WCD9360_SOC_MAD_BEACON_CTL_2, 0x03 << 2, + mad_cal->beacon_info.detection_mechanism << 2); + snd_soc_write(codec, WCD9360_SOC_MAD_BEACON_CTL_7, + mad_cal->beacon_info.rms_diff_threshold & 0x1F); + snd_soc_write(codec, WCD9360_SOC_MAD_BEACON_CTL_5, + mad_cal->beacon_info.rms_threshold_lsb); + snd_soc_write(codec, WCD9360_SOC_MAD_BEACON_CTL_6, + mad_cal->beacon_info.rms_threshold_msb); + + for (idx = 0; idx < ARRAY_SIZE(mad_cal->beacon_info.iir_coefficients); + idx++) { + snd_soc_update_bits(codec, WCD9360_SOC_MAD_BEACON_IIR_CTL_PTR, + 0x3F, idx); + snd_soc_write(codec, WCD9360_SOC_MAD_BEACON_IIR_CTL_VAL, + mad_cal->beacon_info.iir_coefficients[idx]); + dev_dbg(codec->dev, "%s:MAD Beacon IIR Coef[%d] = 0X%x", + __func__, idx, + mad_cal->beacon_info.iir_coefficients[idx]); + } + + /* Ultrasound */ + snd_soc_update_bits(codec, WCD9360_SOC_MAD_ULTR_CTL_1, + 0x07 << 4, + mad_cal->ultrasound_info.rms_comp_time << 4); + snd_soc_update_bits(codec, WCD9360_SOC_MAD_ULTR_CTL_2, 0x03 << 2, + mad_cal->ultrasound_info.detection_mechanism << 2); + snd_soc_write(codec, WCD9360_SOC_MAD_ULTR_CTL_7, + mad_cal->ultrasound_info.rms_diff_threshold & 0x1F); + snd_soc_write(codec, WCD9360_SOC_MAD_ULTR_CTL_5, + mad_cal->ultrasound_info.rms_threshold_lsb); + snd_soc_write(codec, WCD9360_SOC_MAD_ULTR_CTL_6, + mad_cal->ultrasound_info.rms_threshold_msb); + +done: + if (!hwdep_cal) + release_firmware(fw); + + return ret; +} + +static int __pahu_codec_enable_mad(struct snd_soc_codec *codec, bool enable) +{ + int rc = 0; + + /* Return if CPE INPUT is DEC1 */ + if (snd_soc_read(codec, WCD9360_CPE_SS_SVA_CFG) & 0x04) { + dev_dbg(codec->dev, "%s: MAD is bypassed, skip mad %s\n", + __func__, enable ? "enable" : "disable"); + return rc; + } + + dev_dbg(codec->dev, "%s: enable = %s\n", __func__, + enable ? "enable" : "disable"); + + if (enable) { + snd_soc_update_bits(codec, WCD9360_SOC_MAD_AUDIO_CTL_2, + 0x03, 0x03); + rc = pahu_codec_config_mad(codec); + if (rc < 0) { + snd_soc_update_bits(codec, WCD9360_SOC_MAD_AUDIO_CTL_2, + 0x03, 0x00); + goto done; + } + + /* Turn on MAD clk */ + snd_soc_update_bits(codec, WCD9360_CPE_SS_MAD_CTL, + 0x01, 0x01); + + /* Undo reset for MAD */ + snd_soc_update_bits(codec, WCD9360_CPE_SS_MAD_CTL, + 0x02, 0x00); + snd_soc_update_bits(codec, WCD9360_CODEC_RPM_CLK_MCLK_CFG, + 0x04, 0x04); + } else { + snd_soc_update_bits(codec, WCD9360_SOC_MAD_AUDIO_CTL_2, + 0x03, 0x00); + /* Reset the MAD block */ + snd_soc_update_bits(codec, WCD9360_CPE_SS_MAD_CTL, + 0x02, 0x02); + /* Turn off MAD clk */ + snd_soc_update_bits(codec, WCD9360_CPE_SS_MAD_CTL, + 0x01, 0x00); + snd_soc_update_bits(codec, WCD9360_CODEC_RPM_CLK_MCLK_CFG, + 0x04, 0x00); + } +done: + return rc; +} + +static int pahu_codec_ape_enable_mad(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct pahu_priv *pahu = snd_soc_codec_get_drvdata(codec); + int rc = 0; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_update_bits(codec, WCD9360_CPE_SS_SVA_CFG, 0x40, 0x40); + rc = __pahu_codec_enable_mad(codec, true); + break; + case SND_SOC_DAPM_PRE_PMD: + snd_soc_update_bits(codec, WCD9360_CPE_SS_SVA_CFG, 0x40, 0x00); + __pahu_codec_enable_mad(codec, false); + break; + } + + dev_dbg(pahu->dev, "%s: event = %d\n", __func__, event); + return rc; +} + +static int pahu_codec_cpe_mad_ctl(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct pahu_priv *pahu = snd_soc_codec_get_drvdata(codec); + int rc = 0; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + pahu->mad_switch_cnt++; + if (pahu->mad_switch_cnt != 1) + goto done; + + snd_soc_update_bits(codec, WCD9360_CPE_SS_SVA_CFG, 0x20, 0x20); + rc = __pahu_codec_enable_mad(codec, true); + if (rc < 0) { + pahu->mad_switch_cnt--; + goto done; + } + + break; + case SND_SOC_DAPM_PRE_PMD: + pahu->mad_switch_cnt--; + if (pahu->mad_switch_cnt != 0) + goto done; + + snd_soc_update_bits(codec, WCD9360_CPE_SS_SVA_CFG, 0x20, 0x00); + __pahu_codec_enable_mad(codec, false); + break; + } +done: + dev_dbg(pahu->dev, "%s: event = %d, mad_switch_cnt = %d\n", + __func__, event, pahu->mad_switch_cnt); + return rc; +} + +static int pahu_get_asrc_mode(struct pahu_priv *pahu, int asrc, + u8 main_sr, u8 mix_sr) +{ + u8 asrc_output_mode; + int asrc_mode = CONV_88P2K_TO_384K; + + if ((asrc < 0) || (asrc >= ASRC_MAX)) + return 0; + + asrc_output_mode = pahu->asrc_output_mode[asrc]; + + if (asrc_output_mode) { + /* + * If Mix sample rate is < 96KHz, use 96K to 352.8K + * conversion, or else use 384K to 352.8K conversion + */ + if (mix_sr < 5) + asrc_mode = CONV_96K_TO_352P8K; + else + asrc_mode = CONV_384K_TO_352P8K; + } else { + /* Integer main and Fractional mix path */ + if (main_sr < 8 && mix_sr > 9) { + asrc_mode = CONV_352P8K_TO_384K; + } else if (main_sr > 8 && mix_sr < 8) { + /* Fractional main and Integer mix path */ + if (mix_sr < 5) + asrc_mode = CONV_96K_TO_352P8K; + else + asrc_mode = CONV_384K_TO_352P8K; + } else if (main_sr < 8 && mix_sr < 8) { + /* Integer main and Integer mix path */ + asrc_mode = CONV_96K_TO_384K; + } + } + + return asrc_mode; +} + +static int pahu_codec_wdma3_ctl(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* Fix to 16KHz */ + snd_soc_update_bits(codec, WCD9360_DMA_WDMA_CTL_3, + 0xF0, 0x10); + /* Select mclk_1 */ + snd_soc_update_bits(codec, WCD9360_DMA_WDMA_CTL_3, + 0x02, 0x00); + /* Enable DMA */ + snd_soc_update_bits(codec, WCD9360_DMA_WDMA_CTL_3, + 0x01, 0x01); + break; + + case SND_SOC_DAPM_POST_PMD: + /* Disable DMA */ + snd_soc_update_bits(codec, WCD9360_DMA_WDMA_CTL_3, + 0x01, 0x00); + break; + + }; + + return 0; +} +static int pahu_codec_enable_asrc(struct snd_soc_codec *codec, + int asrc_in, int event) +{ + struct pahu_priv *pahu = snd_soc_codec_get_drvdata(codec); + u16 cfg_reg, ctl_reg, clk_reg, asrc_ctl, mix_ctl_reg, paired_reg; + int asrc, ret = 0; + u8 main_sr, mix_sr, asrc_mode = 0; + + switch (asrc_in) { + case ASRC_IN_SPKR1: + cfg_reg = WCD9360_CDC_RX7_RX_PATH_CFG0; + ctl_reg = WCD9360_CDC_RX7_RX_PATH_CTL; + clk_reg = WCD9360_MIXING_ASRC2_CLK_RST_CTL; + paired_reg = WCD9360_MIXING_ASRC2_CLK_RST_CTL; + asrc_ctl = WCD9360_MIXING_ASRC2_CTL1; + asrc = ASRC2; + break; + case ASRC_IN_SPKR2: + cfg_reg = WCD9360_CDC_RX8_RX_PATH_CFG0; + ctl_reg = WCD9360_CDC_RX8_RX_PATH_CTL; + clk_reg = WCD9360_MIXING_ASRC3_CLK_RST_CTL; + paired_reg = WCD9360_MIXING_ASRC3_CLK_RST_CTL; + asrc_ctl = WCD9360_MIXING_ASRC3_CTL1; + asrc = ASRC3; + break; + default: + dev_err(codec->dev, "%s: Invalid asrc input :%d\n", __func__, + asrc_in); + ret = -EINVAL; + goto done; + }; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (pahu->asrc_users[asrc] == 0) { + if ((snd_soc_read(codec, clk_reg) & 0x02) || + (snd_soc_read(codec, paired_reg) & 0x02)) { + snd_soc_update_bits(codec, clk_reg, + 0x02, 0x00); + snd_soc_update_bits(codec, paired_reg, + 0x02, 0x00); + } + snd_soc_update_bits(codec, cfg_reg, 0x80, 0x80); + snd_soc_update_bits(codec, clk_reg, 0x01, 0x01); + main_sr = snd_soc_read(codec, ctl_reg) & 0x0F; + mix_ctl_reg = ctl_reg + 5; + mix_sr = snd_soc_read(codec, mix_ctl_reg) & 0x0F; + asrc_mode = pahu_get_asrc_mode(pahu, asrc, + main_sr, mix_sr); + dev_dbg(codec->dev, "%s: main_sr:%d mix_sr:%d asrc_mode %d\n", + __func__, main_sr, mix_sr, asrc_mode); + snd_soc_update_bits(codec, asrc_ctl, 0x07, asrc_mode); + } + pahu->asrc_users[asrc]++; + break; + case SND_SOC_DAPM_POST_PMD: + pahu->asrc_users[asrc]--; + if (pahu->asrc_users[asrc] <= 0) { + pahu->asrc_users[asrc] = 0; + snd_soc_update_bits(codec, asrc_ctl, 0x07, 0x00); + snd_soc_update_bits(codec, cfg_reg, 0x80, 0x00); + snd_soc_update_bits(codec, clk_reg, 0x03, 0x02); + } + break; + }; + + dev_dbg(codec->dev, "%s: ASRC%d, users: %d\n", + __func__, asrc, pahu->asrc_users[asrc]); + +done: + return ret; +} + +static int pahu_codec_enable_asrc_resampler(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + int ret = 0; + u8 cfg, asrc_in; + + cfg = snd_soc_read(codec, WCD9360_CDC_RX_INP_MUX_SPLINE_ASRC_CFG0); + if (!(cfg & 0xFF)) { + dev_err(codec->dev, "%s: ASRC%u input not selected\n", + __func__, w->shift); + return -EINVAL; + } + + switch (w->shift) { + case ASRC2: + asrc_in = ((cfg & 0x30) == 0x20) ? ASRC_IN_SPKR1 : ASRC_INVALID; + ret = pahu_codec_enable_asrc(codec, asrc_in, event); + break; + case ASRC3: + asrc_in = ((cfg & 0xC0) == 0x80) ? ASRC_IN_SPKR2 : ASRC_INVALID; + ret = pahu_codec_enable_asrc(codec, asrc_in, event); + break; + default: + dev_err(codec->dev, "%s: Invalid asrc:%u\n", __func__, + w->shift); + ret = -EINVAL; + break; + }; + + return ret; +} + +static int pahu_enable_native_supply(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct pahu_priv *pahu = snd_soc_codec_get_drvdata(codec); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (++pahu->native_clk_users == 1) { + snd_soc_update_bits(codec, WCD9360_CLK_SYS_PLL_ENABLES, + 0x01, 0x01); + usleep_range(100, 120); + snd_soc_update_bits(codec, WCD9360_CLK_SYS_MCLK2_PRG1, + 0x06, 0x02); + snd_soc_update_bits(codec, WCD9360_CLK_SYS_MCLK2_PRG1, + 0x01, 0x01); + snd_soc_update_bits(codec, WCD9360_CODEC_RPM_CLK_GATE, + 0x04, 0x00); + /* Add sleep as per HW register sequence */ + usleep_range(30, 50); + snd_soc_update_bits(codec, + WCD9360_CDC_CLK_RST_CTRL_MCLK_CONTROL, + 0x02, 0x02); + snd_soc_update_bits(codec, + WCD9360_CDC_CLK_RST_CTRL_FS_CNT_CONTROL, + 0x10, 0x10); + } + break; + case SND_SOC_DAPM_PRE_PMD: + if (pahu->native_clk_users && + (--pahu->native_clk_users == 0)) { + snd_soc_update_bits(codec, + WCD9360_CDC_CLK_RST_CTRL_FS_CNT_CONTROL, + 0x10, 0x00); + snd_soc_update_bits(codec, + WCD9360_CDC_CLK_RST_CTRL_MCLK_CONTROL, + 0x02, 0x00); + snd_soc_update_bits(codec, WCD9360_CODEC_RPM_CLK_GATE, + 0x04, 0x04); + snd_soc_update_bits(codec, WCD9360_CLK_SYS_MCLK2_PRG1, + 0x01, 0x00); + snd_soc_update_bits(codec, WCD9360_CLK_SYS_MCLK2_PRG1, + 0x06, 0x00); + snd_soc_update_bits(codec, WCD9360_CLK_SYS_PLL_ENABLES, + 0x01, 0x00); + } + break; + } + + dev_dbg(codec->dev, "%s: native_clk_users: %d, event: %d\n", + __func__, pahu->native_clk_users, event); + + return 0; +} + +static int pahu_codec_config_ear_spkr_gain(struct snd_soc_codec *codec, + int event, int gain_reg) +{ + int comp_gain_offset, val; + struct pahu_priv *pahu = snd_soc_codec_get_drvdata(codec); + + switch (pahu->swr.spkr_mode) { + /* Compander gain in SPKR_MODE1 case is 12 dB */ + case WCD9360_SPKR_MODE_1: + comp_gain_offset = -12; + break; + /* Default case compander gain is 15 dB */ + default: + comp_gain_offset = -15; + break; + } + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + /* Apply ear spkr gain only if compander is enabled */ + if (pahu->comp_enabled[COMPANDER_7] && + (gain_reg == WCD9360_CDC_RX7_RX_VOL_CTL || + gain_reg == WCD9360_CDC_RX7_RX_VOL_MIX_CTL) && + (pahu->ear_spkr_gain != 0)) { + /* For example, val is -8(-12+5-1) for 4dB of gain */ + val = comp_gain_offset + pahu->ear_spkr_gain - 1; + snd_soc_write(codec, gain_reg, val); + + dev_dbg(codec->dev, "%s: RX7 Volume %d dB\n", + __func__, val); + } + break; + case SND_SOC_DAPM_POST_PMD: + /* + * Reset RX7 volume to 0 dB if compander is enabled and + * ear_spkr_gain is non-zero. + */ + if (pahu->comp_enabled[COMPANDER_7] && + (gain_reg == WCD9360_CDC_RX7_RX_VOL_CTL || + gain_reg == WCD9360_CDC_RX7_RX_VOL_MIX_CTL) && + (pahu->ear_spkr_gain != 0)) { + snd_soc_write(codec, gain_reg, 0x0); + + dev_dbg(codec->dev, "%s: Reset RX7 Volume to 0 dB\n", + __func__); + } + break; + } + + return 0; +} + +static int pahu_config_compander(struct snd_soc_codec *codec, int interp_n, + int event) +{ + struct pahu_priv *pahu = snd_soc_codec_get_drvdata(codec); + int comp; + u16 comp_ctl0_reg, rx_path_cfg0_reg; + + /* HPH, LO are not valid and AUX does not have compander */ + if (((interp_n >= INTERP_HPHL_NA) && (interp_n <= INTERP_LO4_NA)) || + (interp_n == INTERP_AUX)) + return 0; + + comp = interp_n; + dev_dbg(codec->dev, "%s: event %d compander %d, enabled %d\n", + __func__, event, comp, pahu->comp_enabled[comp]); + + if (!pahu->comp_enabled[comp]) + return 0; + + comp_ctl0_reg = WCD9360_CDC_COMPANDER0_CTL0 + (comp * 8); + rx_path_cfg0_reg = WCD9360_CDC_RX0_RX_PATH_CFG0 + (comp * 20); + + if (SND_SOC_DAPM_EVENT_ON(event)) { + /* Enable Compander Clock */ + snd_soc_update_bits(codec, comp_ctl0_reg, 0x01, 0x01); + /* Soft reset */ + snd_soc_update_bits(codec, comp_ctl0_reg, 0x02, 0x02); + snd_soc_update_bits(codec, comp_ctl0_reg, 0x02, 0x00); + /* Compander enable */ + snd_soc_update_bits(codec, rx_path_cfg0_reg, 0x02, 0x02); + } + + if (SND_SOC_DAPM_EVENT_OFF(event)) { + snd_soc_update_bits(codec, rx_path_cfg0_reg, 0x02, 0x00); + snd_soc_update_bits(codec, comp_ctl0_reg, 0x04, 0x04); + snd_soc_update_bits(codec, comp_ctl0_reg, 0x02, 0x02); + snd_soc_update_bits(codec, comp_ctl0_reg, 0x02, 0x00); + snd_soc_update_bits(codec, comp_ctl0_reg, 0x01, 0x00); + snd_soc_update_bits(codec, comp_ctl0_reg, 0x04, 0x00); + } + + return 0; +} + +/** + * pahu_codec_enable_interp_clk - Enable main path Interpolator + * clock. + * + * @codec: Codec instance + * @event: Indicates speaker path gain offset value + * @intp_idx: Interpolator index + * Returns number of main clock users + */ +int pahu_codec_enable_interp_clk(struct snd_soc_codec *codec, + int event, int interp_idx) +{ + struct pahu_priv *pahu; + u16 main_reg; + + if (!codec) { + pr_err("%s: codec is NULL\n", __func__); + return -EINVAL; + } + + pahu = snd_soc_codec_get_drvdata(codec); + + main_reg = WCD9360_CDC_RX0_RX_PATH_CTL + + (interp_idx * WCD9360_RX_PATH_CTL_OFFSET); + + if (interp_idx == INTERP_AUX) + main_reg = WCD9360_CDC_RX9_RX_PATH_CTL; + + if (SND_SOC_DAPM_EVENT_ON(event)) { + if (pahu->main_clk_users[interp_idx] == 0) { + /* Main path PGA mute enable */ + snd_soc_update_bits(codec, main_reg, 0x10, 0x10); + /* Clk enable */ + snd_soc_update_bits(codec, main_reg, 0x20, 0x20); + pahu_config_compander(codec, interp_idx, event); + } + pahu->main_clk_users[interp_idx]++; + } + + if (SND_SOC_DAPM_EVENT_OFF(event)) { + pahu->main_clk_users[interp_idx]--; + if (pahu->main_clk_users[interp_idx] <= 0) { + pahu->main_clk_users[interp_idx] = 0; + pahu_config_compander(codec, interp_idx, event); + /* Clk Disable */ + snd_soc_update_bits(codec, main_reg, 0x20, 0x00); + /* Reset enable and disable */ + snd_soc_update_bits(codec, main_reg, 0x40, 0x40); + snd_soc_update_bits(codec, main_reg, 0x40, 0x00); + /* Reset rate to 48K*/ + snd_soc_update_bits(codec, main_reg, 0x0F, 0x04); + } + } + + dev_dbg(codec->dev, "%s event %d main_clk_users %d\n", + __func__, event, pahu->main_clk_users[interp_idx]); + + return pahu->main_clk_users[interp_idx]; +} +EXPORT_SYMBOL(pahu_codec_enable_interp_clk); + +static int pahu_codec_enable_mix_path(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct pahu_priv *pahu = snd_soc_codec_get_drvdata(codec); + u16 gain_reg, mix_reg; + int offset_val = 0; + int val = 0; + + if (w->shift >= WCD9360_NUM_INTERPOLATORS || + ((w->shift >= INTERP_HPHL_NA) && (w->shift <= INTERP_LO4_NA))) { + dev_err(codec->dev, "%s: Invalid Interpolator value %d for name %s\n", + __func__, w->shift, w->name); + return -EINVAL; + }; + + gain_reg = WCD9360_CDC_RX0_RX_VOL_MIX_CTL + + (w->shift * WCD9360_RX_PATH_CTL_OFFSET); + mix_reg = WCD9360_CDC_RX0_RX_PATH_MIX_CTL + + (w->shift * WCD9360_RX_PATH_CTL_OFFSET); + + if (w->shift == INTERP_AUX) { + gain_reg = WCD9360_CDC_RX9_RX_VOL_MIX_CTL; + mix_reg = WCD9360_CDC_RX9_RX_PATH_MIX_CTL; + } + + if (w->shift == INTERP_SPKR1 || w->shift == INTERP_SPKR2) + __pahu_codec_enable_swr(w, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + pahu_codec_enable_interp_clk(codec, event, w->shift); + /* Clk enable */ + snd_soc_update_bits(codec, mix_reg, 0x20, 0x20); + break; + case SND_SOC_DAPM_POST_PMU: + if ((pahu->swr.spkr_gain_offset == + WCD9360_RX_GAIN_OFFSET_M1P5_DB) && + (pahu->comp_enabled[COMPANDER_7] || + pahu->comp_enabled[COMPANDER_8]) && + (gain_reg == WCD9360_CDC_RX7_RX_VOL_MIX_CTL || + gain_reg == WCD9360_CDC_RX8_RX_VOL_MIX_CTL)) { + snd_soc_update_bits(codec, WCD9360_CDC_RX7_RX_PATH_SEC1, + 0x01, 0x01); + snd_soc_update_bits(codec, + WCD9360_CDC_RX7_RX_PATH_MIX_SEC0, + 0x01, 0x01); + snd_soc_update_bits(codec, WCD9360_CDC_RX8_RX_PATH_SEC1, + 0x01, 0x01); + snd_soc_update_bits(codec, + WCD9360_CDC_RX8_RX_PATH_MIX_SEC0, + 0x01, 0x01); + offset_val = -2; + } + val = snd_soc_read(codec, gain_reg); + val += offset_val; + snd_soc_write(codec, gain_reg, val); + pahu_codec_config_ear_spkr_gain(codec, event, gain_reg); + break; + case SND_SOC_DAPM_POST_PMD: + /* Clk Disable */ + snd_soc_update_bits(codec, mix_reg, 0x20, 0x00); + pahu_codec_enable_interp_clk(codec, event, w->shift); + /* Reset enable and disable */ + snd_soc_update_bits(codec, mix_reg, 0x40, 0x40); + snd_soc_update_bits(codec, mix_reg, 0x40, 0x00); + + if ((pahu->swr.spkr_gain_offset == + WCD9360_RX_GAIN_OFFSET_M1P5_DB) && + (pahu->comp_enabled[COMPANDER_7] || + pahu->comp_enabled[COMPANDER_8]) && + (gain_reg == WCD9360_CDC_RX7_RX_VOL_MIX_CTL || + gain_reg == WCD9360_CDC_RX8_RX_VOL_MIX_CTL)) { + snd_soc_update_bits(codec, WCD9360_CDC_RX7_RX_PATH_SEC1, + 0x01, 0x00); + snd_soc_update_bits(codec, + WCD9360_CDC_RX7_RX_PATH_MIX_SEC0, + 0x01, 0x00); + snd_soc_update_bits(codec, WCD9360_CDC_RX8_RX_PATH_SEC1, + 0x01, 0x00); + snd_soc_update_bits(codec, + WCD9360_CDC_RX8_RX_PATH_MIX_SEC0, + 0x01, 0x00); + offset_val = 2; + val = snd_soc_read(codec, gain_reg); + val += offset_val; + snd_soc_write(codec, gain_reg, val); + } + pahu_codec_config_ear_spkr_gain(codec, event, gain_reg); + break; + }; + dev_dbg(codec->dev, "%s event %d name %s\n", __func__, event, w->name); + + return 0; +} + +static int pahu_codec_enable_main_path(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct pahu_priv *pahu = snd_soc_codec_get_drvdata(codec); + u16 gain_reg; + u16 reg; + int val; + int offset_val = 0; + + dev_dbg(codec->dev, "%s %d %s\n", __func__, event, w->name); + + if (w->shift >= WCD9360_NUM_INTERPOLATORS || + ((w->shift >= INTERP_HPHL_NA) && (w->shift <= INTERP_LO4_NA))) { + dev_err(codec->dev, "%s: Invalid Interpolator value %d for name %s\n", + __func__, w->shift, w->name); + return -EINVAL; + }; + + reg = WCD9360_CDC_RX0_RX_PATH_CTL + (w->shift * + WCD9360_RX_PATH_CTL_OFFSET); + gain_reg = WCD9360_CDC_RX0_RX_VOL_CTL + (w->shift * + WCD9360_RX_PATH_CTL_OFFSET); + + if (w->shift == INTERP_AUX) { + reg = WCD9360_CDC_RX9_RX_PATH_CTL; + gain_reg = WCD9360_CDC_RX9_RX_VOL_CTL; + } + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + pahu_codec_enable_interp_clk(codec, event, w->shift); + break; + case SND_SOC_DAPM_POST_PMU: + /* apply gain after int clk is enabled */ + if ((pahu->swr.spkr_gain_offset == + WCD9360_RX_GAIN_OFFSET_M1P5_DB) && + (pahu->comp_enabled[COMPANDER_7] || + pahu->comp_enabled[COMPANDER_8]) && + (gain_reg == WCD9360_CDC_RX7_RX_VOL_CTL || + gain_reg == WCD9360_CDC_RX8_RX_VOL_CTL)) { + snd_soc_update_bits(codec, WCD9360_CDC_RX7_RX_PATH_SEC1, + 0x01, 0x01); + snd_soc_update_bits(codec, + WCD9360_CDC_RX7_RX_PATH_MIX_SEC0, + 0x01, 0x01); + snd_soc_update_bits(codec, WCD9360_CDC_RX8_RX_PATH_SEC1, + 0x01, 0x01); + snd_soc_update_bits(codec, + WCD9360_CDC_RX8_RX_PATH_MIX_SEC0, + 0x01, 0x01); + offset_val = -2; + } + val = snd_soc_read(codec, gain_reg); + val += offset_val; + snd_soc_write(codec, gain_reg, val); + pahu_codec_config_ear_spkr_gain(codec, event, gain_reg); + break; + case SND_SOC_DAPM_POST_PMD: + pahu_codec_enable_interp_clk(codec, event, w->shift); + + if ((pahu->swr.spkr_gain_offset == + WCD9360_RX_GAIN_OFFSET_M1P5_DB) && + (pahu->comp_enabled[COMPANDER_7] || + pahu->comp_enabled[COMPANDER_8]) && + (gain_reg == WCD9360_CDC_RX7_RX_VOL_CTL || + gain_reg == WCD9360_CDC_RX8_RX_VOL_CTL)) { + snd_soc_update_bits(codec, WCD9360_CDC_RX7_RX_PATH_SEC1, + 0x01, 0x00); + snd_soc_update_bits(codec, + WCD9360_CDC_RX7_RX_PATH_MIX_SEC0, + 0x01, 0x00); + snd_soc_update_bits(codec, WCD9360_CDC_RX8_RX_PATH_SEC1, + 0x01, 0x00); + snd_soc_update_bits(codec, + WCD9360_CDC_RX8_RX_PATH_MIX_SEC0, + 0x01, 0x00); + offset_val = 2; + val = snd_soc_read(codec, gain_reg); + val += offset_val; + snd_soc_write(codec, gain_reg, val); + } + pahu_codec_config_ear_spkr_gain(codec, event, gain_reg); + break; + }; + + return 0; +} + +static int pahu_codec_set_iir_gain(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + + dev_dbg(codec->dev, "%s: event = %d\n", __func__, event); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: /* fall through */ + case SND_SOC_DAPM_PRE_PMD: + if (strnstr(w->name, "IIR0", sizeof("IIR0"))) { + snd_soc_write(codec, + WCD9360_CDC_SIDETONE_IIR0_IIR_GAIN_B1_CTL, + snd_soc_read(codec, + WCD9360_CDC_SIDETONE_IIR0_IIR_GAIN_B1_CTL)); + snd_soc_write(codec, + WCD9360_CDC_SIDETONE_IIR0_IIR_GAIN_B2_CTL, + snd_soc_read(codec, + WCD9360_CDC_SIDETONE_IIR0_IIR_GAIN_B2_CTL)); + snd_soc_write(codec, + WCD9360_CDC_SIDETONE_IIR0_IIR_GAIN_B3_CTL, + snd_soc_read(codec, + WCD9360_CDC_SIDETONE_IIR0_IIR_GAIN_B3_CTL)); + snd_soc_write(codec, + WCD9360_CDC_SIDETONE_IIR0_IIR_GAIN_B4_CTL, + snd_soc_read(codec, + WCD9360_CDC_SIDETONE_IIR0_IIR_GAIN_B4_CTL)); + } + break; + } + return 0; +} + +static int pahu_codec_find_amic_input(struct snd_soc_codec *codec, + int adc_mux_n) +{ + u16 mask, shift, adc_mux_in_reg; + u16 amic_mux_sel_reg; + bool is_amic; + + if (adc_mux_n < 0 || adc_mux_n > WCD9360_MAX_VALID_ADC_MUX || + adc_mux_n == WCD9360_INVALID_ADC_MUX) + return 0; + + if (adc_mux_n < 3) { + adc_mux_in_reg = WCD9360_CDC_TX_INP_MUX_ADC_MUX0_CFG1 + + 2 * adc_mux_n; + mask = 0x03; + shift = 0; + amic_mux_sel_reg = WCD9360_CDC_TX_INP_MUX_ADC_MUX0_CFG0 + + 2 * adc_mux_n; + } else if (adc_mux_n < 4) { + adc_mux_in_reg = WCD9360_CDC_TX_INP_MUX_ADC_MUX3_CFG1; + mask = 0x03; + shift = 0; + amic_mux_sel_reg = WCD9360_CDC_TX_INP_MUX_ADC_MUX0_CFG0 + + 2 * adc_mux_n; + } else if (adc_mux_n < 7) { + adc_mux_in_reg = WCD9360_CDC_TX_INP_MUX_ADC_MUX0_CFG1 + + 2 * (adc_mux_n - 4); + mask = 0x0C; + shift = 2; + amic_mux_sel_reg = WCD9360_CDC_TX_INP_MUX_ADC_MUX4_CFG0 + + adc_mux_n - 4; + } else if (adc_mux_n < 8) { + adc_mux_in_reg = WCD9360_CDC_TX_INP_MUX_ADC_MUX3_CFG1; + mask = 0x0C; + shift = 2; + amic_mux_sel_reg = WCD9360_CDC_TX_INP_MUX_ADC_MUX4_CFG0 + + adc_mux_n - 4; + } else if (adc_mux_n < 12) { + adc_mux_in_reg = WCD9360_CDC_TX_INP_MUX_ADC_MUX0_CFG1 + + 2 * (((adc_mux_n == 8) ? (adc_mux_n - 8) : + (adc_mux_n - 9))); + mask = 0x30; + shift = 4; + amic_mux_sel_reg = WCD9360_CDC_TX_INP_MUX_ADC_MUX8_CFG0 + + ((adc_mux_n == 8) ? (adc_mux_n - 8) : + (adc_mux_n - 9)); + } + + is_amic = (((snd_soc_read(codec, adc_mux_in_reg) & mask) >> shift) + == 1); + if (!is_amic) + return 0; + + return snd_soc_read(codec, amic_mux_sel_reg) & 0x07; +} + +static void pahu_codec_set_tx_hold(struct snd_soc_codec *codec, + u16 amic_reg, bool set) +{ + u8 mask = 0x20; + u8 val; + + if (amic_reg == WCD9360_ANA_AMIC1 || + amic_reg == WCD9360_ANA_AMIC3) + mask = 0x40; + + val = set ? mask : 0x00; + + switch (amic_reg) { + case WCD9360_ANA_AMIC1: + case WCD9360_ANA_AMIC2: + snd_soc_update_bits(codec, WCD9360_ANA_AMIC2, mask, val); + break; + case WCD9360_ANA_AMIC3: + case WCD9360_ANA_AMIC4: + snd_soc_update_bits(codec, WCD9360_ANA_AMIC4, mask, val); + break; + default: + dev_dbg(codec->dev, "%s: invalid amic: %d\n", + __func__, amic_reg); + break; + } +} + +static int pahu_codec_tx_adc_cfg(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + int adc_mux_n = w->shift; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + int amic_n; + + dev_dbg(codec->dev, "%s: event: %d\n", __func__, event); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + amic_n = pahu_codec_find_amic_input(codec, adc_mux_n); + break; + default: + break; + } + + return 0; +} + +static u16 pahu_codec_get_amic_pwlvl_reg(struct snd_soc_codec *codec, int amic) +{ + u16 pwr_level_reg = 0; + + switch (amic) { + case 1: + case 2: + pwr_level_reg = WCD9360_ANA_AMIC1; + break; + + case 3: + case 4: + pwr_level_reg = WCD9360_ANA_AMIC3; + break; + default: + dev_dbg(codec->dev, "%s: invalid amic: %d\n", + __func__, amic); + break; + } + + return pwr_level_reg; +} + +#define TX_HPF_CUT_OFF_FREQ_MASK 0x60 +#define CF_MIN_3DB_4HZ 0x0 +#define CF_MIN_3DB_75HZ 0x1 +#define CF_MIN_3DB_150HZ 0x2 + +static void pahu_tx_hpf_corner_freq_callback(struct work_struct *work) +{ + struct delayed_work *hpf_delayed_work; + struct hpf_work *hpf_work; + struct pahu_priv *pahu; + struct snd_soc_codec *codec; + u16 dec_cfg_reg, amic_reg, go_bit_reg; + u8 hpf_cut_off_freq; + int amic_n; + + hpf_delayed_work = to_delayed_work(work); + hpf_work = container_of(hpf_delayed_work, struct hpf_work, dwork); + pahu = hpf_work->pahu; + codec = pahu->codec; + hpf_cut_off_freq = hpf_work->hpf_cut_off_freq; + + dec_cfg_reg = WCD9360_CDC_TX0_TX_PATH_CFG0 + 16 * hpf_work->decimator; + go_bit_reg = dec_cfg_reg + 7; + + dev_dbg(codec->dev, "%s: decimator %u hpf_cut_of_freq 0x%x\n", + __func__, hpf_work->decimator, hpf_cut_off_freq); + + amic_n = pahu_codec_find_amic_input(codec, hpf_work->decimator); + if (amic_n) { + amic_reg = WCD9360_ANA_AMIC1 + amic_n - 1; + pahu_codec_set_tx_hold(codec, amic_reg, false); + } + snd_soc_update_bits(codec, dec_cfg_reg, TX_HPF_CUT_OFF_FREQ_MASK, + hpf_cut_off_freq << 5); + snd_soc_update_bits(codec, go_bit_reg, 0x02, 0x02); + /* Minimum 1 clk cycle delay is required as per HW spec */ + usleep_range(1000, 1010); + snd_soc_update_bits(codec, go_bit_reg, 0x02, 0x00); +} + +static void pahu_tx_mute_update_callback(struct work_struct *work) +{ + struct tx_mute_work *tx_mute_dwork; + struct pahu_priv *pahu; + struct delayed_work *delayed_work; + struct snd_soc_codec *codec; + u16 tx_vol_ctl_reg; + + delayed_work = to_delayed_work(work); + tx_mute_dwork = container_of(delayed_work, struct tx_mute_work, dwork); + pahu = tx_mute_dwork->pahu; + codec = pahu->codec; + + tx_vol_ctl_reg = WCD9360_CDC_TX0_TX_PATH_CTL + + 16 * tx_mute_dwork->decimator; + snd_soc_update_bits(codec, tx_vol_ctl_reg, 0x10, 0x00); +} + +static int pahu_codec_enable_rx_path_clk(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + u16 sidetone_reg; + + dev_dbg(codec->dev, "%s %d %d\n", __func__, event, w->shift); + sidetone_reg = WCD9360_CDC_RX0_RX_PATH_CFG1 + 0x14*(w->shift); + + if (w->shift == INTERP_AUX) + sidetone_reg = WCD9360_CDC_RX9_RX_PATH_CFG1; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (!strcmp(w->name, "RX INT7 MIX2 INP")) + __pahu_codec_enable_swr(w, event); + pahu_codec_enable_interp_clk(codec, event, w->shift); + snd_soc_update_bits(codec, sidetone_reg, 0x10, 0x10); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_update_bits(codec, sidetone_reg, 0x10, 0x00); + pahu_codec_enable_interp_clk(codec, event, w->shift); + if (!strcmp(w->name, "RX INT7 MIX2 INP")) + __pahu_codec_enable_swr(w, event); + break; + default: + break; + }; + return 0; +} + +static int pahu_codec_enable_dec(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct pahu_priv *pahu = snd_soc_codec_get_drvdata(codec); + unsigned int decimator; + char *dec_adc_mux_name = NULL; + char *widget_name = NULL; + char *wname; + int ret = 0, amic_n; + u16 tx_vol_ctl_reg, pwr_level_reg = 0, dec_cfg_reg, hpf_gate_reg; + u16 tx_gain_ctl_reg; + char *dec; + u8 hpf_cut_off_freq; + + dev_dbg(codec->dev, "%s %d\n", __func__, event); + + widget_name = kstrndup(w->name, 15, GFP_KERNEL); + if (!widget_name) + return -ENOMEM; + + wname = widget_name; + dec_adc_mux_name = strsep(&widget_name, " "); + if (!dec_adc_mux_name) { + dev_err(codec->dev, "%s: Invalid decimator = %s\n", + __func__, w->name); + ret = -EINVAL; + goto out; + } + dec_adc_mux_name = widget_name; + + dec = strpbrk(dec_adc_mux_name, "012345678"); + if (!dec) { + dev_err(codec->dev, "%s: decimator index not found\n", + __func__); + ret = -EINVAL; + goto out; + } + + ret = kstrtouint(dec, 10, &decimator); + if (ret < 0) { + dev_err(codec->dev, "%s: Invalid decimator = %s\n", + __func__, wname); + ret = -EINVAL; + goto out; + } + + dev_dbg(codec->dev, "%s(): widget = %s decimator = %u\n", __func__, + w->name, decimator); + + tx_vol_ctl_reg = WCD9360_CDC_TX0_TX_PATH_CTL + 16 * decimator; + hpf_gate_reg = WCD9360_CDC_TX0_TX_PATH_SEC2 + 16 * decimator; + dec_cfg_reg = WCD9360_CDC_TX0_TX_PATH_CFG0 + 16 * decimator; + tx_gain_ctl_reg = WCD9360_CDC_TX0_TX_VOL_CTL + 16 * decimator; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + amic_n = pahu_codec_find_amic_input(codec, decimator); + if (amic_n) + pwr_level_reg = pahu_codec_get_amic_pwlvl_reg(codec, + amic_n); + + if (pwr_level_reg) { + switch ((snd_soc_read(codec, pwr_level_reg) & + WCD9360_AMIC_PWR_LVL_MASK) >> + WCD9360_AMIC_PWR_LVL_SHIFT) { + case WCD9360_AMIC_PWR_LEVEL_LP: + snd_soc_update_bits(codec, dec_cfg_reg, + WCD9360_DEC_PWR_LVL_MASK, + WCD9360_DEC_PWR_LVL_LP); + break; + + case WCD9360_AMIC_PWR_LEVEL_HP: + snd_soc_update_bits(codec, dec_cfg_reg, + WCD9360_DEC_PWR_LVL_MASK, + WCD9360_DEC_PWR_LVL_HP); + break; + case WCD9360_AMIC_PWR_LEVEL_DEFAULT: + default: + snd_soc_update_bits(codec, dec_cfg_reg, + WCD9360_DEC_PWR_LVL_MASK, + WCD9360_DEC_PWR_LVL_DF); + break; + } + } + /* Enable TX PGA Mute */ + snd_soc_update_bits(codec, tx_vol_ctl_reg, 0x10, 0x10); + break; + case SND_SOC_DAPM_POST_PMU: + hpf_cut_off_freq = (snd_soc_read(codec, dec_cfg_reg) & + TX_HPF_CUT_OFF_FREQ_MASK) >> 5; + + pahu->tx_hpf_work[decimator].hpf_cut_off_freq = + hpf_cut_off_freq; + if (hpf_cut_off_freq != CF_MIN_3DB_150HZ) { + snd_soc_update_bits(codec, dec_cfg_reg, + TX_HPF_CUT_OFF_FREQ_MASK, + CF_MIN_3DB_150HZ << 5); + snd_soc_update_bits(codec, hpf_gate_reg, 0x02, 0x02); + /* + * Minimum 1 clk cycle delay is required as per + * HW spec. + */ + usleep_range(1000, 1010); + snd_soc_update_bits(codec, hpf_gate_reg, 0x02, 0x00); + } + /* schedule work queue to Remove Mute */ + schedule_delayed_work(&pahu->tx_mute_dwork[decimator].dwork, + msecs_to_jiffies(tx_unmute_delay)); + if (pahu->tx_hpf_work[decimator].hpf_cut_off_freq != + CF_MIN_3DB_150HZ) + schedule_delayed_work( + &pahu->tx_hpf_work[decimator].dwork, + msecs_to_jiffies(300)); + /* apply gain after decimator is enabled */ + snd_soc_write(codec, tx_gain_ctl_reg, + snd_soc_read(codec, tx_gain_ctl_reg)); + break; + case SND_SOC_DAPM_PRE_PMD: + hpf_cut_off_freq = + pahu->tx_hpf_work[decimator].hpf_cut_off_freq; + snd_soc_update_bits(codec, tx_vol_ctl_reg, 0x10, 0x10); + if (cancel_delayed_work_sync( + &pahu->tx_hpf_work[decimator].dwork)) { + if (hpf_cut_off_freq != CF_MIN_3DB_150HZ) { + snd_soc_update_bits(codec, dec_cfg_reg, + TX_HPF_CUT_OFF_FREQ_MASK, + hpf_cut_off_freq << 5); + snd_soc_update_bits(codec, hpf_gate_reg, + 0x02, 0x02); + /* + * Minimum 1 clk cycle delay is required as per + * HW spec. + */ + usleep_range(1000, 1010); + snd_soc_update_bits(codec, hpf_gate_reg, + 0x02, 0x00); + } + } + cancel_delayed_work_sync( + &pahu->tx_mute_dwork[decimator].dwork); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_update_bits(codec, tx_vol_ctl_reg, 0x10, 0x00); + snd_soc_update_bits(codec, dec_cfg_reg, + WCD9360_DEC_PWR_LVL_MASK, + WCD9360_DEC_PWR_LVL_DF); + break; + }; +out: + kfree(wname); + return ret; +} + +static u32 pahu_get_dmic_sample_rate(struct snd_soc_codec *codec, + unsigned int dmic, + struct wcd9xxx_pdata *pdata) +{ + u8 tx_stream_fs; + u8 adc_mux_index = 0, adc_mux_sel = 0; + bool dec_found = false; + u16 adc_mux_ctl_reg, tx_fs_reg; + u32 dmic_fs; + + while (dec_found == 0 && adc_mux_index < WCD9360_MAX_VALID_ADC_MUX) { + if (adc_mux_index < 4) { + adc_mux_ctl_reg = WCD9360_CDC_TX_INP_MUX_ADC_MUX0_CFG0 + + (adc_mux_index * 2); + } else if (adc_mux_index < WCD9360_INVALID_ADC_MUX) { + adc_mux_ctl_reg = WCD9360_CDC_TX_INP_MUX_ADC_MUX4_CFG0 + + adc_mux_index - 4; + } else if (adc_mux_index == WCD9360_INVALID_ADC_MUX) { + ++adc_mux_index; + continue; + } + adc_mux_sel = ((snd_soc_read(codec, adc_mux_ctl_reg) & + 0xF8) >> 3) - 1; + + if (adc_mux_sel == dmic) { + dec_found = true; + break; + } + + ++adc_mux_index; + } + + if (dec_found && adc_mux_index <= 8) { + tx_fs_reg = WCD9360_CDC_TX0_TX_PATH_CTL + (16 * adc_mux_index); + tx_stream_fs = snd_soc_read(codec, tx_fs_reg) & 0x0F; + if (tx_stream_fs <= 4) { + if (pdata->dmic_sample_rate <= + WCD9XXX_DMIC_SAMPLE_RATE_2P4MHZ) + dmic_fs = pdata->dmic_sample_rate; + else + dmic_fs = WCD9XXX_DMIC_SAMPLE_RATE_2P4MHZ; + } else + dmic_fs = WCD9XXX_DMIC_SAMPLE_RATE_4P8MHZ; + } else { + dmic_fs = pdata->dmic_sample_rate; + } + + return dmic_fs; +} + +static u8 pahu_get_dmic_clk_val(struct snd_soc_codec *codec, + u32 dmic_clk_rate) +{ + u32 div_factor; + u8 dmic_ctl_val = WCD9360_DMIC_CLK_DIV_2; + + dev_dbg(codec->dev, "%s: dmic_sample_rate = %d\n", + __func__, dmic_clk_rate); + + if (dmic_clk_rate == 0) { + dev_err(codec->dev, "%s: dmic_sample_rate cannot be 0\n", + __func__); + goto done; + } + + div_factor = WCD9360_MCLK_CLK_9P6MHZ / dmic_clk_rate; + switch (div_factor) { + case 2: + dmic_ctl_val = WCD9360_DMIC_CLK_DIV_2; + break; + case 3: + dmic_ctl_val = WCD9360_DMIC_CLK_DIV_3; + break; + case 4: + dmic_ctl_val = WCD9360_DMIC_CLK_DIV_4; + break; + case 6: + dmic_ctl_val = WCD9360_DMIC_CLK_DIV_6; + break; + case 8: + dmic_ctl_val = WCD9360_DMIC_CLK_DIV_8; + break; + case 16: + dmic_ctl_val = WCD9360_DMIC_CLK_DIV_16; + break; + default: + dev_err(codec->dev, + "%s: Invalid div_factor %u, dmic_rate(%u)\n", + __func__, div_factor, dmic_clk_rate); + break; + } + +done: + return dmic_ctl_val; +} + +static int pahu_codec_enable_adc(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + + dev_dbg(codec->dev, "%s: event:%d\n", __func__, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + pahu_codec_set_tx_hold(codec, w->reg, true); + break; + default: + break; + } + + return 0; +} + +static int pahu_codec_enable_dmic(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct pahu_priv *pahu = snd_soc_codec_get_drvdata(codec); + struct wcd9xxx_pdata *pdata = dev_get_platdata(codec->dev->parent); + u8 dmic_clk_en = 0x01; + u16 dmic_clk_reg; + s32 *dmic_clk_cnt; + u8 dmic_rate_val, dmic_rate_shift = 1; + unsigned int dmic; + u32 dmic_sample_rate; + + dmic = w->shift; + switch (dmic) { + case 0: + case 1: + dmic_clk_cnt = &(pahu->dmic_0_1_clk_cnt); + dmic_clk_reg = WCD9360_CPE_SS_DMIC0_CTL; + break; + case 2: + case 3: + dmic_clk_cnt = &(pahu->dmic_2_3_clk_cnt); + dmic_clk_reg = WCD9360_CPE_SS_DMIC1_CTL; + break; + case 4: + case 5: + dmic_clk_cnt = &(pahu->dmic_4_5_clk_cnt); + dmic_clk_reg = WCD9360_CPE_SS_DMIC2_CTL; + break; + case 6: + case 7: + dmic_clk_cnt = &(pahu->dmic_6_7_clk_cnt); + dmic_clk_reg = WCD9360_CPE_SS_DMIC3_CTL; + break; + default: + dev_err(codec->dev, "%s: Invalid DMIC Selection\n", + __func__); + return -EINVAL; + }; + dev_dbg(codec->dev, "%s: event %d DMIC%d dmic_clk_cnt %d\n", + __func__, event, dmic, *dmic_clk_cnt); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + dmic_sample_rate = pahu_get_dmic_sample_rate(codec, dmic, + pdata); + dmic_rate_val = + pahu_get_dmic_clk_val(codec, + dmic_sample_rate); + + (*dmic_clk_cnt)++; + if (*dmic_clk_cnt == 1) { + snd_soc_update_bits(codec, dmic_clk_reg, + 0x07 << dmic_rate_shift, + dmic_rate_val << dmic_rate_shift); + snd_soc_update_bits(codec, dmic_clk_reg, + dmic_clk_en, dmic_clk_en); + } + + break; + case SND_SOC_DAPM_POST_PMD: + dmic_rate_val = + pahu_get_dmic_clk_val(codec, + pdata->mad_dmic_sample_rate); + (*dmic_clk_cnt)--; + if (*dmic_clk_cnt == 0) { + snd_soc_update_bits(codec, dmic_clk_reg, + dmic_clk_en, 0); + snd_soc_update_bits(codec, dmic_clk_reg, + 0x07 << dmic_rate_shift, + dmic_rate_val << dmic_rate_shift); + } + break; + }; + + return 0; +} + +/* + * pahu_micbias_control: enable/disable micbias + * @codec: handle to snd_soc_codec * + * @micb_num: micbias to be enabled/disabled, e.g. micbias1 or micbias2 + * @req: control requested, enable/disable or pullup enable/disable + * @is_dapm: triggered by dapm or not + * + * return 0 if control is success or error code in case of failure + */ +int pahu_micbias_control(struct snd_soc_codec *codec, + int micb_num, int req, bool is_dapm) +{ + struct pahu_priv *pahu = snd_soc_codec_get_drvdata(codec); + int micb_index = micb_num - 1; + u16 micb_reg; + + if ((micb_index < 0) || (micb_index > PAHU_MAX_MICBIAS - 1)) { + dev_err(codec->dev, "%s: Invalid micbias index, micb_ind:%d\n", + __func__, micb_index); + return -EINVAL; + } + + switch (micb_num) { + case WCD9360_MIC_BIAS_1: + micb_reg = WCD9360_ANA_MICB1; + break; + case WCD9360_MIC_BIAS_2: + micb_reg = WCD9360_ANA_MICB2; + break; + case WCD9360_MIC_BIAS_3: + micb_reg = WCD9360_ANA_MICB3; + break; + case WCD9360_MIC_BIAS_4: + micb_reg = WCD9360_ANA_MICB4; + break; + default: + dev_err(codec->dev, "%s: Invalid micbias number: %d\n", + __func__, micb_num); + return -EINVAL; + } + mutex_lock(&pahu->micb_lock); + + switch (req) { + case WCD9360_MICB_PULLUP_ENABLE: + pahu->pullup_ref[micb_index]++; + if ((pahu->pullup_ref[micb_index] == 1) && + (pahu->micb_ref[micb_index] == 0)) + snd_soc_update_bits(codec, micb_reg, 0xC0, 0x80); + break; + case WCD9360_MICB_PULLUP_DISABLE: + if (pahu->pullup_ref[micb_index] > 0) + pahu->pullup_ref[micb_index]--; + if ((pahu->pullup_ref[micb_index] == 0) && + (pahu->micb_ref[micb_index] == 0)) + snd_soc_update_bits(codec, micb_reg, 0xC0, 0x00); + break; + case WCD9360_MICB_ENABLE: + pahu->micb_ref[micb_index]++; + if (pahu->micb_ref[micb_index] == 1) + snd_soc_update_bits(codec, micb_reg, 0xC0, 0x40); + break; + case WCD9360_MICB_DISABLE: + if (pahu->micb_ref[micb_index] > 0) + pahu->micb_ref[micb_index]--; + if ((pahu->micb_ref[micb_index] == 0) && + (pahu->pullup_ref[micb_index] > 0)) + snd_soc_update_bits(codec, micb_reg, 0xC0, 0x80); + else if ((pahu->micb_ref[micb_index] == 0) && + (pahu->pullup_ref[micb_index] == 0)) + snd_soc_update_bits(codec, micb_reg, 0xC0, 0x00); + break; + } + + dev_dbg(codec->dev, "%s: micb_num:%d, micb_ref: %d\n", + __func__, micb_num, pahu->micb_ref[micb_index]); + + mutex_unlock(&pahu->micb_lock); + + return 0; +} +EXPORT_SYMBOL(pahu_micbias_control); + +static int __pahu_codec_enable_micbias(struct snd_soc_dapm_widget *w, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + int micb_num; + + dev_dbg(codec->dev, "%s: wname: %s, event: %d\n", + __func__, w->name, event); + + if (strnstr(w->name, "MIC BIAS1", sizeof("MIC BIAS1"))) + micb_num = WCD9360_MIC_BIAS_1; + else if (strnstr(w->name, "MIC BIAS2", sizeof("MIC BIAS2"))) + micb_num = WCD9360_MIC_BIAS_2; + else if (strnstr(w->name, "MIC BIAS3", sizeof("MIC BIAS3"))) + micb_num = WCD9360_MIC_BIAS_3; + else if (strnstr(w->name, "MIC BIAS4", sizeof("MIC BIAS4"))) + micb_num = WCD9360_MIC_BIAS_4; + else + return -EINVAL; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* + * Use ref count to handle micbias pullup + * and enable requests + */ + pahu_micbias_control(codec, micb_num, WCD9360_MICB_ENABLE, + true); + break; + case SND_SOC_DAPM_POST_PMU: + /* wait for cnp time */ + usleep_range(1000, 1100); + break; + case SND_SOC_DAPM_POST_PMD: + pahu_micbias_control(codec, micb_num, WCD9360_MICB_DISABLE, + true); + break; + }; + + return 0; +} + +/* + * pahu_codec_enable_standalone_micbias - enable micbias standalone + * @codec: pointer to codec instance + * @micb_num: number of micbias to be enabled + * @enable: true to enable micbias or false to disable + * + * This function is used to enable micbias (1, 2, 3 or 4) during + * standalone independent of whether TX use-case is running or not + * + * Return: error code in case of failure or 0 for success + */ +int pahu_codec_enable_standalone_micbias(struct snd_soc_codec *codec, + int micb_num, + bool enable) +{ + const char * const micb_names[] = { + DAPM_MICBIAS1_STANDALONE, DAPM_MICBIAS2_STANDALONE, + DAPM_MICBIAS3_STANDALONE, DAPM_MICBIAS4_STANDALONE + }; + int micb_index = micb_num - 1; + int rc; + + if (!codec) { + pr_err("%s: Codec memory is NULL\n", __func__); + return -EINVAL; + } + + if ((micb_index < 0) || (micb_index > PAHU_MAX_MICBIAS - 1)) { + dev_err(codec->dev, "%s: Invalid micbias index, micb_ind:%d\n", + __func__, micb_index); + return -EINVAL; + } + + if (enable) + rc = snd_soc_dapm_force_enable_pin( + snd_soc_codec_get_dapm(codec), + micb_names[micb_index]); + else + rc = snd_soc_dapm_disable_pin(snd_soc_codec_get_dapm(codec), + micb_names[micb_index]); + + if (!rc) + snd_soc_dapm_sync(snd_soc_codec_get_dapm(codec)); + else + dev_err(codec->dev, "%s: micbias%d force %s pin failed\n", + __func__, micb_num, (enable ? "enable" : "disable")); + + return rc; +} +EXPORT_SYMBOL(pahu_codec_enable_standalone_micbias); + +static int pahu_codec_force_enable_micbias(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + int ret = 0; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct pahu_priv *pahu = snd_soc_codec_get_drvdata(codec); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + wcd_resmgr_enable_master_bias(pahu->resmgr); + pahu_cdc_mclk_enable(codec, true); + ret = __pahu_codec_enable_micbias(w, SND_SOC_DAPM_PRE_PMU); + /* Wait for 1ms for better cnp */ + usleep_range(1000, 1100); + pahu_cdc_mclk_enable(codec, false); + break; + case SND_SOC_DAPM_POST_PMD: + ret = __pahu_codec_enable_micbias(w, SND_SOC_DAPM_POST_PMD); + wcd_resmgr_disable_master_bias(pahu->resmgr); + break; + } + + return ret; +} + +static int pahu_codec_enable_micbias(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + return __pahu_codec_enable_micbias(w, event); +} + +static void pahu_restore_iir_coeff(struct pahu_priv *pahu, int iir_idx, + int band_idx) +{ + u16 reg_add; + int no_of_reg = 0; + + regmap_write(pahu->wcd9xxx->regmap, + (WCD9360_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL + 16 * iir_idx), + (band_idx * BAND_MAX * sizeof(uint32_t)) & 0x7F); + reg_add = WCD9360_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL + 16 * iir_idx; + + if (pahu->intf_type != WCD9XXX_INTERFACE_TYPE_SLIMBUS) + return; + /* + * Since wcd9xxx_slim_write_repeat() supports only maximum of 16 + * registers at a time, split total 20 writes(5 coefficients per + * band and 4 writes per coefficient) into 16 and 4. + */ + no_of_reg = WCD9360_CDC_REPEAT_WRITES_MAX; + wcd9xxx_slim_write_repeat(pahu->wcd9xxx, reg_add, no_of_reg, + &pahu->sidetone_coeff_array[iir_idx][band_idx][0]); + + no_of_reg = (WCD9360_CDC_SIDETONE_IIR_COEFF_MAX * 4) - + WCD9360_CDC_REPEAT_WRITES_MAX; + wcd9xxx_slim_write_repeat(pahu->wcd9xxx, reg_add, no_of_reg, + &pahu->sidetone_coeff_array[iir_idx][band_idx] + [WCD9360_CDC_REPEAT_WRITES_MAX]); +} + +static int pahu_iir_enable_audio_mixer_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + int iir_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->reg; + int band_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + /* IIR filter band registers are at integer multiples of 16 */ + u16 iir_reg = WCD9360_CDC_SIDETONE_IIR0_IIR_CTL + 16 * iir_idx; + + ucontrol->value.integer.value[0] = (snd_soc_read(codec, iir_reg) & + (1 << band_idx)) != 0; + + dev_dbg(codec->dev, "%s: IIR #%d band #%d enable %d\n", __func__, + iir_idx, band_idx, + (uint32_t)ucontrol->value.integer.value[0]); + return 0; +} + +static int pahu_iir_enable_audio_mixer_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct pahu_priv *pahu = snd_soc_codec_get_drvdata(codec); + int iir_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->reg; + int band_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + bool iir_band_en_status; + int value = ucontrol->value.integer.value[0]; + u16 iir_reg = WCD9360_CDC_SIDETONE_IIR0_IIR_CTL + 16 * iir_idx; + + pahu_restore_iir_coeff(pahu, iir_idx, band_idx); + + /* Mask first 5 bits, 6-8 are reserved */ + snd_soc_update_bits(codec, iir_reg, (1 << band_idx), + (value << band_idx)); + + iir_band_en_status = ((snd_soc_read(codec, iir_reg) & + (1 << band_idx)) != 0); + dev_dbg(codec->dev, "%s: IIR #%d band #%d enable %d\n", __func__, + iir_idx, band_idx, iir_band_en_status); + return 0; +} + +static uint32_t get_iir_band_coeff(struct snd_soc_codec *codec, + int iir_idx, int band_idx, + int coeff_idx) +{ + uint32_t value = 0; + + /* Address does not automatically update if reading */ + snd_soc_write(codec, + (WCD9360_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL + 16 * iir_idx), + ((band_idx * BAND_MAX + coeff_idx) + * sizeof(uint32_t)) & 0x7F); + + value |= snd_soc_read(codec, + (WCD9360_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL + 16 * iir_idx)); + + snd_soc_write(codec, + (WCD9360_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL + 16 * iir_idx), + ((band_idx * BAND_MAX + coeff_idx) + * sizeof(uint32_t) + 1) & 0x7F); + + value |= (snd_soc_read(codec, + (WCD9360_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL + + 16 * iir_idx)) << 8); + + snd_soc_write(codec, + (WCD9360_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL + 16 * iir_idx), + ((band_idx * BAND_MAX + coeff_idx) + * sizeof(uint32_t) + 2) & 0x7F); + + value |= (snd_soc_read(codec, + (WCD9360_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL + + 16 * iir_idx)) << 16); + + snd_soc_write(codec, + (WCD9360_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL + 16 * iir_idx), + ((band_idx * BAND_MAX + coeff_idx) + * sizeof(uint32_t) + 3) & 0x7F); + + /* Mask bits top 2 bits since they are reserved */ + value |= ((snd_soc_read(codec, + (WCD9360_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL + + 16 * iir_idx)) & 0x3F) << 24); + + return value; +} + +static int pahu_iir_band_audio_mixer_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + int iir_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->reg; + int band_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + + ucontrol->value.integer.value[0] = + get_iir_band_coeff(codec, iir_idx, band_idx, 0); + ucontrol->value.integer.value[1] = + get_iir_band_coeff(codec, iir_idx, band_idx, 1); + ucontrol->value.integer.value[2] = + get_iir_band_coeff(codec, iir_idx, band_idx, 2); + ucontrol->value.integer.value[3] = + get_iir_band_coeff(codec, iir_idx, band_idx, 3); + ucontrol->value.integer.value[4] = + get_iir_band_coeff(codec, iir_idx, band_idx, 4); + + dev_dbg(codec->dev, "%s: IIR #%d band #%d b0 = 0x%x\n" + "%s: IIR #%d band #%d b1 = 0x%x\n" + "%s: IIR #%d band #%d b2 = 0x%x\n" + "%s: IIR #%d band #%d a1 = 0x%x\n" + "%s: IIR #%d band #%d a2 = 0x%x\n", + __func__, iir_idx, band_idx, + (uint32_t)ucontrol->value.integer.value[0], + __func__, iir_idx, band_idx, + (uint32_t)ucontrol->value.integer.value[1], + __func__, iir_idx, band_idx, + (uint32_t)ucontrol->value.integer.value[2], + __func__, iir_idx, band_idx, + (uint32_t)ucontrol->value.integer.value[3], + __func__, iir_idx, band_idx, + (uint32_t)ucontrol->value.integer.value[4]); + return 0; +} + +static void set_iir_band_coeff(struct snd_soc_codec *codec, + int iir_idx, int band_idx, + uint32_t value) +{ + snd_soc_write(codec, + (WCD9360_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL + 16 * iir_idx), + (value & 0xFF)); + + snd_soc_write(codec, + (WCD9360_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL + 16 * iir_idx), + (value >> 8) & 0xFF); + + snd_soc_write(codec, + (WCD9360_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL + 16 * iir_idx), + (value >> 16) & 0xFF); + + /* Mask top 2 bits, 7-8 are reserved */ + snd_soc_write(codec, + (WCD9360_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL + 16 * iir_idx), + (value >> 24) & 0x3F); +} + +static int pahu_iir_band_audio_mixer_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct pahu_priv *pahu = snd_soc_codec_get_drvdata(codec); + int iir_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->reg; + int band_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + int coeff_idx, idx = 0; + + /* + * Mask top bit it is reserved + * Updates addr automatically for each B2 write + */ + snd_soc_write(codec, + (WCD9360_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL + 16 * iir_idx), + (band_idx * BAND_MAX * sizeof(uint32_t)) & 0x7F); + + /* Store the coefficients in sidetone coeff array */ + for (coeff_idx = 0; coeff_idx < WCD9360_CDC_SIDETONE_IIR_COEFF_MAX; + coeff_idx++) { + uint32_t value = ucontrol->value.integer.value[coeff_idx]; + + set_iir_band_coeff(codec, iir_idx, band_idx, value); + + /* Four 8 bit values(one 32 bit) per coefficient */ + pahu->sidetone_coeff_array[iir_idx][band_idx][idx++] = + (value & 0xFF); + pahu->sidetone_coeff_array[iir_idx][band_idx][idx++] = + ((value >> 8) & 0xFF); + pahu->sidetone_coeff_array[iir_idx][band_idx][idx++] = + ((value >> 16) & 0xFF); + pahu->sidetone_coeff_array[iir_idx][band_idx][idx++] = + ((value >> 24) & 0xFF); + } + + pr_debug("%s: IIR #%d band #%d b0 = 0x%x\n" + "%s: IIR #%d band #%d b1 = 0x%x\n" + "%s: IIR #%d band #%d b2 = 0x%x\n" + "%s: IIR #%d band #%d a1 = 0x%x\n" + "%s: IIR #%d band #%d a2 = 0x%x\n", + __func__, iir_idx, band_idx, + get_iir_band_coeff(codec, iir_idx, band_idx, 0), + __func__, iir_idx, band_idx, + get_iir_band_coeff(codec, iir_idx, band_idx, 1), + __func__, iir_idx, band_idx, + get_iir_band_coeff(codec, iir_idx, band_idx, 2), + __func__, iir_idx, band_idx, + get_iir_band_coeff(codec, iir_idx, band_idx, 3), + __func__, iir_idx, band_idx, + get_iir_band_coeff(codec, iir_idx, band_idx, 4)); + return 0; +} + +static int pahu_compander_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + int comp = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + struct pahu_priv *pahu = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = pahu->comp_enabled[comp]; + return 0; +} + +static int pahu_compander_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct pahu_priv *pahu = snd_soc_codec_get_drvdata(codec); + int comp = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + int value = ucontrol->value.integer.value[0]; + + dev_dbg(codec->dev, "%s: Compander %d enable current %d, new %d\n", + __func__, comp + 1, pahu->comp_enabled[comp], value); + pahu->comp_enabled[comp] = value; + + return 0; +} + +static int pahu_dmic_pin_mode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + u16 offset; + u8 reg_val, pinctl_position; + + pinctl_position = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + + offset = pinctl_position - WCD9360_TLMM_DMIC_PINCFG_OFFSET; + reg_val = snd_soc_read(codec, + WCD9360_TLMM_DMIC1_CLK_PINCFG + offset); + + ucontrol->value.integer.value[0] = !!reg_val; + + return 0; +} + +static int pahu_dmic_pin_mode_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct pahu_priv *pahu = snd_soc_codec_get_drvdata(codec); + u16 ctl_reg, cfg_reg, offset; + u8 ctl_val, cfg_val, pinctl_position, pinctl_mode, mask; + + /* 0- high or low; 1- high Z */ + pinctl_mode = ucontrol->value.integer.value[0]; + pinctl_position = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + + switch (pinctl_position >> 3) { + case 0: + ctl_reg = WCD9360_TEST_DEBUG_PIN_CTL_OE_0; + break; + case 1: + ctl_reg = WCD9360_TEST_DEBUG_PIN_CTL_OE_1; + break; + case 2: + ctl_reg = WCD9360_TEST_DEBUG_PIN_CTL_OE_2; + break; + case 3: + ctl_reg = WCD9360_TEST_DEBUG_PIN_CTL_OE_3; + break; + default: + dev_err(codec->dev, "%s: Invalid pinctl position = %d\n", + __func__, pinctl_position); + return -EINVAL; + } + + ctl_val = ~(pinctl_mode << (pinctl_position & 0x07)); + mask = 1 << (pinctl_position & 0x07); + snd_soc_update_bits(codec, ctl_reg, mask, ctl_val); + + offset = pinctl_position - WCD9360_TLMM_DMIC_PINCFG_OFFSET; + cfg_reg = WCD9360_TLMM_DMIC1_CLK_PINCFG + offset; + if (pinctl_mode) { + if (pahu->intf_type == WCD9XXX_INTERFACE_TYPE_SLIMBUS) + cfg_val = 0x5; + else + cfg_val = 0xD; + } else + cfg_val = 0; + snd_soc_update_bits(codec, cfg_reg, 0x1F, cfg_val); + + dev_dbg(codec->dev, "%s: reg=0x%x mask=0x%x val=%d reg=0x%x val=%d\n", + __func__, ctl_reg, mask, ctl_val, cfg_reg, cfg_val); + + return 0; +} + +static int pahu_amic_pwr_lvl_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + u16 amic_reg = 0; + + if (!strcmp(kcontrol->id.name, "AMIC_1_2 PWR MODE")) + amic_reg = WCD9360_ANA_AMIC1; + if (!strcmp(kcontrol->id.name, "AMIC_3_4 PWR MODE")) + amic_reg = WCD9360_ANA_AMIC3; + + if (amic_reg) + ucontrol->value.integer.value[0] = + (snd_soc_read(codec, amic_reg) & + WCD9360_AMIC_PWR_LVL_MASK) >> + WCD9360_AMIC_PWR_LVL_SHIFT; + return 0; +} + +static int pahu_amic_pwr_lvl_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + u32 mode_val; + u16 amic_reg = 0; + + mode_val = ucontrol->value.enumerated.item[0]; + + dev_dbg(codec->dev, "%s: mode: %d\n", __func__, mode_val); + + if (!strcmp(kcontrol->id.name, "AMIC_1_2 PWR MODE")) + amic_reg = WCD9360_ANA_AMIC1; + if (!strcmp(kcontrol->id.name, "AMIC_3_4 PWR MODE")) + amic_reg = WCD9360_ANA_AMIC3; + + if (amic_reg) + snd_soc_update_bits(codec, amic_reg, WCD9360_AMIC_PWR_LVL_MASK, + mode_val << WCD9360_AMIC_PWR_LVL_SHIFT); + return 0; +} + +static const char *const pahu_conn_mad_text[] = { + "NOTUSED1", "ADC1", "ADC2", "ADC3", "ADC4", "NOTUSED5", + "NOTUSED6", "NOTUSED2", "DMIC0", "DMIC1", "DMIC2", "DMIC3", + "DMIC4", "DMIC5", "DMIC6", "DMIC7" +}; + +static const struct soc_enum pahu_conn_mad_enum = + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(pahu_conn_mad_text), + pahu_conn_mad_text); + +static int pahu_mad_input_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + u8 pahu_mad_input; + + pahu_mad_input = snd_soc_read(codec, WCD9360_SOC_MAD_INP_SEL) & 0x0F; + ucontrol->value.integer.value[0] = pahu_mad_input; + + dev_dbg(codec->dev, "%s: pahu_mad_input = %s\n", __func__, + pahu_conn_mad_text[pahu_mad_input]); + + return 0; +} + +static int pahu_mad_input_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct snd_soc_card *card = codec->component.card; + u8 pahu_mad_input; + char mad_amic_input_widget[6]; + const char *mad_input_widget; + const char *source_widget = NULL; + u32 adc, i, mic_bias_found = 0; + int ret = 0; + char *mad_input; + bool is_adc_input = false; + + pahu_mad_input = ucontrol->value.integer.value[0]; + + if (pahu_mad_input >= sizeof(pahu_conn_mad_text)/ + sizeof(pahu_conn_mad_text[0])) { + dev_err(codec->dev, + "%s: pahu_mad_input = %d out of bounds\n", + __func__, pahu_mad_input); + return -EINVAL; + } + + if (strnstr(pahu_conn_mad_text[pahu_mad_input], "NOTUSED", + sizeof("NOTUSED"))) { + dev_dbg(codec->dev, + "%s: Unsupported pahu_mad_input = %s\n", + __func__, pahu_conn_mad_text[pahu_mad_input]); + /* Make sure the MAD register is updated */ + snd_soc_update_bits(codec, WCD9360_ANA_MAD_SETUP, + 0x88, 0x00); + return -EINVAL; + } + + if (strnstr(pahu_conn_mad_text[pahu_mad_input], + "ADC", sizeof("ADC"))) { + mad_input = strpbrk(pahu_conn_mad_text[pahu_mad_input], + "1234"); + if (!mad_input) { + dev_err(codec->dev, "%s: Invalid MAD input %s\n", + __func__, pahu_conn_mad_text[pahu_mad_input]); + return -EINVAL; + } + + ret = kstrtouint(mad_input, 10, &adc); + if ((ret < 0) || (adc > 4)) { + dev_err(codec->dev, "%s: Invalid ADC = %s\n", __func__, + pahu_conn_mad_text[pahu_mad_input]); + return -EINVAL; + } + + snprintf(mad_amic_input_widget, 6, "%s%u", "AMIC", adc); + + mad_input_widget = mad_amic_input_widget; + is_adc_input = true; + } else { + /* DMIC type input widget*/ + mad_input_widget = pahu_conn_mad_text[pahu_mad_input]; + } + + dev_dbg(codec->dev, + "%s: pahu input widget = %s, adc_input = %s\n", __func__, + mad_input_widget, is_adc_input ? "true" : "false"); + + for (i = 0; i < card->num_of_dapm_routes; i++) { + if (!strcmp(card->of_dapm_routes[i].sink, mad_input_widget)) { + source_widget = card->of_dapm_routes[i].source; + if (!source_widget) { + dev_err(codec->dev, + "%s: invalid source widget\n", + __func__); + return -EINVAL; + } + + if (strnstr(source_widget, + "MIC BIAS1", sizeof("MIC BIAS1"))) { + mic_bias_found = 1; + break; + } else if (strnstr(source_widget, + "MIC BIAS2", sizeof("MIC BIAS2"))) { + mic_bias_found = 2; + break; + } else if (strnstr(source_widget, + "MIC BIAS3", sizeof("MIC BIAS3"))) { + mic_bias_found = 3; + break; + } else if (strnstr(source_widget, + "MIC BIAS4", sizeof("MIC BIAS4"))) { + mic_bias_found = 4; + break; + } + } + } + + if (!mic_bias_found) { + dev_err(codec->dev, "%s: mic bias not found for input %s\n", + __func__, mad_input_widget); + return -EINVAL; + } + + dev_dbg(codec->dev, "%s: mic_bias found = %d\n", __func__, + mic_bias_found); + + snd_soc_update_bits(codec, WCD9360_SOC_MAD_INP_SEL, + 0x0F, pahu_mad_input); + snd_soc_update_bits(codec, WCD9360_ANA_MAD_SETUP, + 0x07, mic_bias_found); + /* for all adc inputs, mad should be in micbias mode with BG enabled */ + if (is_adc_input) + snd_soc_update_bits(codec, WCD9360_ANA_MAD_SETUP, + 0x88, 0x88); + else + snd_soc_update_bits(codec, WCD9360_ANA_MAD_SETUP, + 0x88, 0x00); + return 0; +} + +static int pahu_ear_spkr_pa_gain_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct pahu_priv *pahu = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = pahu->ear_spkr_gain; + + dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n", + __func__, ucontrol->value.integer.value[0]); + + return 0; +} + +static int pahu_ear_spkr_pa_gain_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct pahu_priv *pahu = snd_soc_codec_get_drvdata(codec); + + pahu->ear_spkr_gain = ucontrol->value.integer.value[0]; + + dev_dbg(codec->dev, "%s: gain = %d\n", __func__, pahu->ear_spkr_gain); + + return 0; +} + +static int pahu_spkr_left_boost_stage_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u8 bst_state_max = 0; + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + + bst_state_max = snd_soc_read(codec, WCD9360_CDC_BOOST0_BOOST_CTL); + bst_state_max = (bst_state_max & 0x0c) >> 2; + ucontrol->value.integer.value[0] = bst_state_max; + dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n", + __func__, ucontrol->value.integer.value[0]); + + return 0; +} + +static int pahu_spkr_left_boost_stage_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u8 bst_state_max; + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + + dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n", + __func__, ucontrol->value.integer.value[0]); + bst_state_max = ucontrol->value.integer.value[0] << 2; + snd_soc_update_bits(codec, WCD9360_CDC_BOOST0_BOOST_CTL, + 0x0c, bst_state_max); + + return 0; +} + +static int pahu_spkr_right_boost_stage_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u8 bst_state_max = 0; + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + + bst_state_max = snd_soc_read(codec, WCD9360_CDC_BOOST1_BOOST_CTL); + bst_state_max = (bst_state_max & 0x0c) >> 2; + ucontrol->value.integer.value[0] = bst_state_max; + dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n", + __func__, ucontrol->value.integer.value[0]); + + return 0; +} + +static int pahu_spkr_right_boost_stage_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u8 bst_state_max; + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + + dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n", + __func__, ucontrol->value.integer.value[0]); + bst_state_max = ucontrol->value.integer.value[0] << 2; + snd_soc_update_bits(codec, WCD9360_CDC_BOOST1_BOOST_CTL, + 0x0c, bst_state_max); + + return 0; +} + +static const char *const pahu_anc_func_text[] = {"OFF", "ON"}; +static const struct soc_enum pahu_anc_func_enum = + SOC_ENUM_SINGLE_EXT(2, pahu_anc_func_text); + +static const char *const pahu_clkmode_text[] = {"EXTERNAL", "INTERNAL"}; +static SOC_ENUM_SINGLE_EXT_DECL(pahu_clkmode_enum, pahu_clkmode_text); + +/* Cutoff frequency for high pass filter */ +static const char * const cf_text[] = { + "CF_NEG_3DB_4HZ", "CF_NEG_3DB_75HZ", "CF_NEG_3DB_150HZ" +}; + +static const char * const rx_cf_text[] = { + "CF_NEG_3DB_4HZ", "CF_NEG_3DB_75HZ", "CF_NEG_3DB_150HZ", + "CF_NEG_3DB_0P48HZ" +}; + +static const char * const amic_pwr_lvl_text[] = { + "LOW_PWR", "DEFAULT", "HIGH_PERF", "HYBRID" +}; + +static const char * const pahu_ear_pa_gain_text[] = { + "G_6_DB", "G_4P5_DB", "G_3_DB", "G_1P5_DB", + "G_0_DB", "G_M2P5_DB", "UNDEFINED", "G_M12_DB" +}; + +static const char * const pahu_ear_spkr_pa_gain_text[] = { + "G_DEFAULT", "G_0_DB", "G_1_DB", "G_2_DB", "G_3_DB", + "G_4_DB", "G_5_DB", "G_6_DB" +}; + +static const char * const pahu_speaker_boost_stage_text[] = { + "NO_MAX_STATE", "MAX_STATE_1", "MAX_STATE_2" +}; + +static SOC_ENUM_SINGLE_EXT_DECL(pahu_ear_pa_gain_enum, pahu_ear_pa_gain_text); +static SOC_ENUM_SINGLE_EXT_DECL(pahu_ear_spkr_pa_gain_enum, + pahu_ear_spkr_pa_gain_text); +static SOC_ENUM_SINGLE_EXT_DECL(pahu_spkr_boost_stage_enum, + pahu_speaker_boost_stage_text); +static SOC_ENUM_SINGLE_EXT_DECL(amic_pwr_lvl_enum, amic_pwr_lvl_text); +static SOC_ENUM_SINGLE_DECL(cf_dec0_enum, WCD9360_CDC_TX0_TX_PATH_CFG0, 5, + cf_text); +static SOC_ENUM_SINGLE_DECL(cf_dec1_enum, WCD9360_CDC_TX1_TX_PATH_CFG0, 5, + cf_text); +static SOC_ENUM_SINGLE_DECL(cf_dec2_enum, WCD9360_CDC_TX2_TX_PATH_CFG0, 5, + cf_text); +static SOC_ENUM_SINGLE_DECL(cf_dec3_enum, WCD9360_CDC_TX3_TX_PATH_CFG0, 5, + cf_text); +static SOC_ENUM_SINGLE_DECL(cf_dec4_enum, WCD9360_CDC_TX4_TX_PATH_CFG0, 5, + cf_text); +static SOC_ENUM_SINGLE_DECL(cf_dec5_enum, WCD9360_CDC_TX5_TX_PATH_CFG0, 5, + cf_text); +static SOC_ENUM_SINGLE_DECL(cf_dec6_enum, WCD9360_CDC_TX6_TX_PATH_CFG0, 5, + cf_text); +static SOC_ENUM_SINGLE_DECL(cf_dec7_enum, WCD9360_CDC_TX7_TX_PATH_CFG0, 5, + cf_text); +static SOC_ENUM_SINGLE_DECL(cf_dec8_enum, WCD9360_CDC_TX8_TX_PATH_CFG0, 5, + cf_text); +static SOC_ENUM_SINGLE_DECL(cf_int0_1_enum, WCD9360_CDC_RX0_RX_PATH_CFG2, 0, + rx_cf_text); +static SOC_ENUM_SINGLE_DECL(cf_int0_2_enum, WCD9360_CDC_RX0_RX_PATH_MIX_CFG, 2, + rx_cf_text); +static SOC_ENUM_SINGLE_DECL(cf_int7_1_enum, WCD9360_CDC_RX7_RX_PATH_CFG2, 0, + rx_cf_text); +static SOC_ENUM_SINGLE_DECL(cf_int7_2_enum, WCD9360_CDC_RX7_RX_PATH_MIX_CFG, 2, + rx_cf_text); +static SOC_ENUM_SINGLE_DECL(cf_int8_1_enum, WCD9360_CDC_RX8_RX_PATH_CFG2, 0, + rx_cf_text); +static SOC_ENUM_SINGLE_DECL(cf_int8_2_enum, WCD9360_CDC_RX8_RX_PATH_MIX_CFG, 2, + rx_cf_text); +static SOC_ENUM_SINGLE_DECL(cf_int9_1_enum, WCD9360_CDC_RX9_RX_PATH_CFG2, 0, + rx_cf_text); +static SOC_ENUM_SINGLE_DECL(cf_int9_2_enum, WCD9360_CDC_RX9_RX_PATH_MIX_CFG, 2, + rx_cf_text); + +static const struct snd_kcontrol_new pahu_snd_controls[] = { + SOC_ENUM_EXT("EAR SPKR PA Gain", pahu_ear_spkr_pa_gain_enum, + pahu_ear_spkr_pa_gain_get, pahu_ear_spkr_pa_gain_put), + SOC_ENUM_EXT("SPKR Left Boost Max State", pahu_spkr_boost_stage_enum, + pahu_spkr_left_boost_stage_get, + pahu_spkr_left_boost_stage_put), + SOC_ENUM_EXT("SPKR Right Boost Max State", pahu_spkr_boost_stage_enum, + pahu_spkr_right_boost_stage_get, + pahu_spkr_right_boost_stage_put), + SOC_SINGLE_TLV("ADC1 Volume", WCD9360_ANA_AMIC1, 0, 20, 0, analog_gain), + SOC_SINGLE_TLV("ADC2 Volume", WCD9360_ANA_AMIC2, 0, 20, 0, analog_gain), + SOC_SINGLE_TLV("ADC3 Volume", WCD9360_ANA_AMIC3, 0, 20, 0, analog_gain), + SOC_SINGLE_TLV("ADC4 Volume", WCD9360_ANA_AMIC4, 0, 20, 0, analog_gain), + + SOC_SINGLE_SX_TLV("RX0 Digital Volume", WCD9360_CDC_RX0_RX_VOL_CTL, + 0, -84, 40, digital_gain), /* -84dB min - 40dB max */ + SOC_SINGLE_SX_TLV("RX7 Digital Volume", WCD9360_CDC_RX7_RX_VOL_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("RX8 Digital Volume", WCD9360_CDC_RX8_RX_VOL_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("RX9 Digital Volume", WCD9360_CDC_RX9_RX_VOL_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("RX0 Mix Digital Volume", + WCD9360_CDC_RX0_RX_VOL_MIX_CTL, 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("RX7 Mix Digital Volume", + WCD9360_CDC_RX7_RX_VOL_MIX_CTL, 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("RX8 Mix Digital Volume", + WCD9360_CDC_RX8_RX_VOL_MIX_CTL, 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("RX9 Mix Digital Volume", + WCD9360_CDC_RX9_RX_VOL_MIX_CTL, 0, -84, 40, digital_gain), + + SOC_SINGLE_SX_TLV("DEC0 Volume", WCD9360_CDC_TX0_TX_VOL_CTL, 0, + -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("DEC1 Volume", WCD9360_CDC_TX1_TX_VOL_CTL, 0, + -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("DEC2 Volume", WCD9360_CDC_TX2_TX_VOL_CTL, 0, + -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("DEC3 Volume", WCD9360_CDC_TX3_TX_VOL_CTL, 0, + -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("DEC4 Volume", WCD9360_CDC_TX4_TX_VOL_CTL, 0, + -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("DEC5 Volume", WCD9360_CDC_TX5_TX_VOL_CTL, 0, + -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("DEC6 Volume", WCD9360_CDC_TX6_TX_VOL_CTL, 0, + -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("DEC7 Volume", WCD9360_CDC_TX7_TX_VOL_CTL, 0, + -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("DEC8 Volume", WCD9360_CDC_TX8_TX_VOL_CTL, 0, + -84, 40, digital_gain), + + SOC_SINGLE_SX_TLV("IIR0 INP0 Volume", + WCD9360_CDC_SIDETONE_IIR0_IIR_GAIN_B1_CTL, 0, -84, 40, + digital_gain), + SOC_SINGLE_SX_TLV("IIR0 INP1 Volume", + WCD9360_CDC_SIDETONE_IIR0_IIR_GAIN_B2_CTL, 0, -84, 40, + digital_gain), + SOC_SINGLE_SX_TLV("IIR0 INP2 Volume", + WCD9360_CDC_SIDETONE_IIR0_IIR_GAIN_B3_CTL, 0, -84, 40, + digital_gain), + SOC_SINGLE_SX_TLV("IIR0 INP3 Volume", + WCD9360_CDC_SIDETONE_IIR0_IIR_GAIN_B4_CTL, 0, -84, 40, + digital_gain), + + SOC_SINGLE_EXT("ANC Slot", SND_SOC_NOPM, 0, 100, 0, pahu_get_anc_slot, + pahu_put_anc_slot), + SOC_ENUM_EXT("ANC Function", pahu_anc_func_enum, pahu_get_anc_func, + pahu_put_anc_func), + + SOC_ENUM_EXT("CLK MODE", pahu_clkmode_enum, pahu_get_clkmode, + pahu_put_clkmode), + + SOC_ENUM("TX0 HPF cut off", cf_dec0_enum), + SOC_ENUM("TX1 HPF cut off", cf_dec1_enum), + SOC_ENUM("TX2 HPF cut off", cf_dec2_enum), + SOC_ENUM("TX3 HPF cut off", cf_dec3_enum), + SOC_ENUM("TX4 HPF cut off", cf_dec4_enum), + SOC_ENUM("TX5 HPF cut off", cf_dec5_enum), + SOC_ENUM("TX6 HPF cut off", cf_dec6_enum), + SOC_ENUM("TX7 HPF cut off", cf_dec7_enum), + SOC_ENUM("TX8 HPF cut off", cf_dec8_enum), + + SOC_ENUM("RX INT0_1 HPF cut off", cf_int0_1_enum), + SOC_ENUM("RX INT0_2 HPF cut off", cf_int0_2_enum), + SOC_ENUM("RX INT7_1 HPF cut off", cf_int7_1_enum), + SOC_ENUM("RX INT7_2 HPF cut off", cf_int7_2_enum), + SOC_ENUM("RX INT8_1 HPF cut off", cf_int8_1_enum), + SOC_ENUM("RX INT8_2 HPF cut off", cf_int8_2_enum), + SOC_ENUM("RX INT9_1 HPF cut off", cf_int9_1_enum), + SOC_ENUM("RX INT9_2 HPF cut off", cf_int9_2_enum), + + SOC_SINGLE_EXT("IIR0 Enable Band1", IIR0, BAND1, 1, 0, + pahu_iir_enable_audio_mixer_get, + pahu_iir_enable_audio_mixer_put), + SOC_SINGLE_EXT("IIR0 Enable Band2", IIR0, BAND2, 1, 0, + pahu_iir_enable_audio_mixer_get, + pahu_iir_enable_audio_mixer_put), + SOC_SINGLE_EXT("IIR0 Enable Band3", IIR0, BAND3, 1, 0, + pahu_iir_enable_audio_mixer_get, + pahu_iir_enable_audio_mixer_put), + SOC_SINGLE_EXT("IIR0 Enable Band4", IIR0, BAND4, 1, 0, + pahu_iir_enable_audio_mixer_get, + pahu_iir_enable_audio_mixer_put), + SOC_SINGLE_EXT("IIR0 Enable Band5", IIR0, BAND5, 1, 0, + pahu_iir_enable_audio_mixer_get, + pahu_iir_enable_audio_mixer_put), + + SOC_SINGLE_MULTI_EXT("IIR0 Band1", IIR0, BAND1, 255, 0, 5, + pahu_iir_band_audio_mixer_get, pahu_iir_band_audio_mixer_put), + SOC_SINGLE_MULTI_EXT("IIR0 Band2", IIR0, BAND2, 255, 0, 5, + pahu_iir_band_audio_mixer_get, pahu_iir_band_audio_mixer_put), + SOC_SINGLE_MULTI_EXT("IIR0 Band3", IIR0, BAND3, 255, 0, 5, + pahu_iir_band_audio_mixer_get, pahu_iir_band_audio_mixer_put), + SOC_SINGLE_MULTI_EXT("IIR0 Band4", IIR0, BAND4, 255, 0, 5, + pahu_iir_band_audio_mixer_get, pahu_iir_band_audio_mixer_put), + SOC_SINGLE_MULTI_EXT("IIR0 Band5", IIR0, BAND5, 255, 0, 5, + pahu_iir_band_audio_mixer_get, pahu_iir_band_audio_mixer_put), + + SOC_SINGLE_EXT("COMP0 Switch", SND_SOC_NOPM, COMPANDER_0, 1, 0, + pahu_compander_get, pahu_compander_put), + SOC_SINGLE_EXT("COMP7 Switch", SND_SOC_NOPM, COMPANDER_7, 1, 0, + pahu_compander_get, pahu_compander_put), + SOC_SINGLE_EXT("COMP8 Switch", SND_SOC_NOPM, COMPANDER_8, 1, 0, + pahu_compander_get, pahu_compander_put), + + SOC_ENUM_EXT("MAD Input", pahu_conn_mad_enum, + pahu_mad_input_get, pahu_mad_input_put), + + SOC_SINGLE_EXT("DMIC1_CLK_PIN_MODE", SND_SOC_NOPM, 15, 1, 0, + pahu_dmic_pin_mode_get, pahu_dmic_pin_mode_put), + + SOC_SINGLE_EXT("DMIC1_DATA_PIN_MODE", SND_SOC_NOPM, 16, 1, 0, + pahu_dmic_pin_mode_get, pahu_dmic_pin_mode_put), + + SOC_SINGLE_EXT("DMIC2_CLK_PIN_MODE", SND_SOC_NOPM, 17, 1, 0, + pahu_dmic_pin_mode_get, pahu_dmic_pin_mode_put), + + SOC_SINGLE_EXT("DMIC2_DATA_PIN_MODE", SND_SOC_NOPM, 18, 1, 0, + pahu_dmic_pin_mode_get, pahu_dmic_pin_mode_put), + + SOC_SINGLE_EXT("DMIC3_CLK_PIN_MODE", SND_SOC_NOPM, 28, 1, 0, + pahu_dmic_pin_mode_get, pahu_dmic_pin_mode_put), + + SOC_SINGLE_EXT("DMIC3_DATA_PIN_MODE", SND_SOC_NOPM, 29, 1, 0, + pahu_dmic_pin_mode_get, pahu_dmic_pin_mode_put), + + SOC_SINGLE_EXT("DMIC4_CLK_PIN_MODE", SND_SOC_NOPM, 30, 1, 0, + pahu_dmic_pin_mode_get, pahu_dmic_pin_mode_put), + + SOC_SINGLE_EXT("DMIC4_DATA_PIN_MODE", SND_SOC_NOPM, 31, 1, 0, + pahu_dmic_pin_mode_get, pahu_dmic_pin_mode_put), + + SOC_ENUM_EXT("AMIC_1_2 PWR MODE", amic_pwr_lvl_enum, + pahu_amic_pwr_lvl_get, pahu_amic_pwr_lvl_put), + SOC_ENUM_EXT("AMIC_3_4 PWR MODE", amic_pwr_lvl_enum, + pahu_amic_pwr_lvl_get, pahu_amic_pwr_lvl_put), +}; + +static int pahu_dec_enum_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int val; + u16 mic_sel_reg = 0; + u8 mic_sel; + + val = ucontrol->value.enumerated.item[0]; + if (val > e->items - 1) + return -EINVAL; + + dev_dbg(codec->dev, "%s: wname: %s, val: 0x%x\n", __func__, + widget->name, val); + + switch (e->reg) { + case WCD9360_CDC_TX_INP_MUX_ADC_MUX0_CFG1: + if (e->shift_l == 0) + mic_sel_reg = WCD9360_CDC_TX0_TX_PATH_CFG0; + else if (e->shift_l == 2) + mic_sel_reg = WCD9360_CDC_TX4_TX_PATH_CFG0; + else if (e->shift_l == 4) + mic_sel_reg = WCD9360_CDC_TX8_TX_PATH_CFG0; + break; + case WCD9360_CDC_TX_INP_MUX_ADC_MUX1_CFG1: + if (e->shift_l == 0) + mic_sel_reg = WCD9360_CDC_TX1_TX_PATH_CFG0; + else if (e->shift_l == 2) + mic_sel_reg = WCD9360_CDC_TX5_TX_PATH_CFG0; + break; + case WCD9360_CDC_TX_INP_MUX_ADC_MUX2_CFG1: + if (e->shift_l == 0) + mic_sel_reg = WCD9360_CDC_TX2_TX_PATH_CFG0; + else if (e->shift_l == 2) + mic_sel_reg = WCD9360_CDC_TX6_TX_PATH_CFG0; + break; + case WCD9360_CDC_TX_INP_MUX_ADC_MUX3_CFG1: + if (e->shift_l == 0) + mic_sel_reg = WCD9360_CDC_TX3_TX_PATH_CFG0; + else if (e->shift_l == 2) + mic_sel_reg = WCD9360_CDC_TX7_TX_PATH_CFG0; + break; + default: + dev_err(codec->dev, "%s: e->reg: 0x%x not expected\n", + __func__, e->reg); + return -EINVAL; + } + + /* ADC: 0, DMIC: 1 */ + mic_sel = val ? 0x0 : 0x1; + if (mic_sel_reg) + snd_soc_update_bits(codec, mic_sel_reg, 1 << 7, mic_sel << 7); + + return snd_soc_dapm_put_enum_double(kcontrol, ucontrol); +} + +static int pahu_int_dem_inp_mux_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int val; + unsigned short look_ahead_dly_reg = WCD9360_CDC_RX0_RX_PATH_CFG0; + + val = ucontrol->value.enumerated.item[0]; + if (val >= e->items) + return -EINVAL; + + dev_dbg(codec->dev, "%s: wname: %s, val: 0x%x\n", __func__, + widget->name, val); + + if (e->reg == WCD9360_CDC_RX0_RX_PATH_SEC0) + look_ahead_dly_reg = WCD9360_CDC_RX0_RX_PATH_CFG0; + else if (e->reg == WCD9360_CDC_RX9_RX_PATH_SEC0) + look_ahead_dly_reg = WCD9360_CDC_RX9_RX_PATH_CFG0; + + /* Set Look Ahead Delay */ + snd_soc_update_bits(codec, look_ahead_dly_reg, + 0x08, (val ? 0x08 : 0x00)); + /* Set DEM INP Select */ + return snd_soc_dapm_put_enum_double(kcontrol, ucontrol); +} + +static const char * const rx_int0_7_mix_mux_text[] = { + "ZERO", "RX0", "RX1", "RX2", "RX3", "RX4", "RX5", + "RX6", "RX7", "PROXIMITY", "IIR0" +}; + +static const char * const rx_int_mix_mux_text[] = { + "ZERO", "RX0", "RX1", "RX2", "RX3", "RX4", "RX5", + "RX6", "RX7", "NA", "IIR0" +}; + +static const char * const rx_prim_mix_text[] = { + "ZERO", "DEC0", "DEC1", "IIR0", "INVALID", "RX0", "RX1", "RX2", + "RX3", "RX4", "RX5", "RX6", "RX7" +}; + +static const char * const rx_sidetone_mix_text[] = { + "ZERO", "SRC0" +}; + +static const char * const cdc_if_tx0_mux_text[] = { + "ZERO", "RX_MIX_TX0", "DEC0", "DEC0_192" +}; +static const char * const cdc_if_tx1_mux_text[] = { + "ZERO", "RX_MIX_TX1", "DEC1", "DEC1_192" +}; +static const char * const cdc_if_tx2_mux_text[] = { + "ZERO", "RX_MIX_TX2", "DEC2", "DEC2_192" +}; +static const char * const cdc_if_tx3_mux_text[] = { + "ZERO", "RX_MIX_TX3", "DEC3", "DEC3_192" +}; +static const char * const cdc_if_tx4_mux_text[] = { + "ZERO", "RX_MIX_TX4", "DEC4", "DEC4_192" +}; +static const char * const cdc_if_tx5_mux_text[] = { + "ZERO", "RX_MIX_TX5", "DEC5", "DEC5_192" +}; +static const char * const cdc_if_tx6_mux_text[] = { + "ZERO", "RX_MIX_TX6", "DEC6", "DEC6_192" +}; +static const char * const cdc_if_tx7_mux_text[] = { + "ZERO", "RX_MIX_TX7", "DEC7", "DEC7_192" +}; +static const char * const cdc_if_tx8_mux_text[] = { + "ZERO", "RX_MIX_TX8", "DEC8", "DEC8_192" +}; +static const char * const cdc_if_tx9_mux_text[] = { + "ZERO", "DEC7", "DEC7_192" +}; +static const char * const cdc_if_tx10_mux_text[] = { + "ZERO", "DEC6", "DEC6_192" +}; +static const char * const cdc_if_tx10_mux2_text[] = { + "TX10_MUX1", "I2SRX1_0_BRDG" +}; +static const char * const cdc_if_tx11_mux2_text[] = { + "TX11_MUX1", "I2SRX1_1_BRDG", "SWR_PACKED_PDM" +}; +static const char * const cdc_if_tx11_mux_text[] = { + "RDMA_TX11", "DEC_0_5", "DEC_9_12", "MAD_AUDIO", "MAD_BRDCST" +}; +static const char * const cdc_if_tx11_inp1_mux_text[] = { + "ZERO", "DEC0", "DEC1", "DEC2", "DEC3", "DEC4", + "DEC5", "RX_MIX_TX5", "DEC9_10", "DEC11_12" +}; +static const char * const cdc_if_tx13_mux_text[] = { + "CDC_DEC_5", "MAD_BRDCST" +}; +static const char * const cdc_if_tx13_inp1_mux_text[] = { + "ZERO", "DEC5", "DEC5_192" +}; + +static const char * const iir_inp_mux_text[] = { + "ZERO", "DEC0", "DEC1", "DEC2", "DEC3", "DEC4", "DEC5", "DEC6", + "DEC7", "DEC8", "RX0", "RX1", "RX2", "RX3", "RX4", "RX5", "RX6", "RX7" +}; + +static const char * const rx_int_dem_inp_mux_text[] = { + "NORMAL_DSM_OUT", "NOT_VALID", "ADC_LOOPBACK" +}; + +static const char * const rx_int0_1_interp_mux_text[] = { + "ZERO", "RX INT0_1 MIX1", +}; + +static const char * const rx_int7_1_interp_mux_text[] = { + "ZERO", "RX INT7_1 MIX1", +}; + +static const char * const rx_int8_1_interp_mux_text[] = { + "ZERO", "RX INT8_1 MIX1", +}; + +static const char * const rx_int9_1_interp_mux_text[] = { + "ZERO", "RX INT9_1 MIX1", +}; + +static const char * const rx_int0_2_interp_mux_text[] = { + "ZERO", "RX INT0_2 MUX", +}; + +static const char * const rx_int7_2_interp_mux_text[] = { + "ZERO", "RX INT7_2 MUX", +}; + +static const char * const rx_int8_2_interp_mux_text[] = { + "ZERO", "RX INT8_2 MUX", +}; + +static const char * const rx_int9_2_interp_mux_text[] = { + "ZERO", "RX INT9_2 MUX", +}; + +static const char * const mad_sel_txt[] = { + "SPE", "MSM" +}; + +static const char * const mad_inp_mux_txt[] = { + "MAD", "DEC1" +}; + +static const char * const adc_mux_text[] = { + "DMIC", "AMIC", "ANC_FB_TUNE1" +}; + +static const char * const dmic_mux_text[] = { + "ZERO", "DMIC0", "DMIC1", "DMIC2", "DMIC3", "DMIC4", "DMIC5", + "NA", "NA", "NA", "NA", "NA", "NA", "NA", "NA", "NA", "DMIC6", + "DMIC7" +}; + +static const char * const amic_mux_text[] = { + "ZERO", "ADC1", "ADC2", "ADC3", "ADC4" +}; + +static const char * const adc2_in_text[] = { + "AMIC2", "AMIC1" +}; + +static const char * const adc4_in_text[] = { + "AMIC4", "AMIC3" +}; + +static const char * const anc0_fb_mux_text[] = { + "ZERO", "INVALID", "ANC_IN_EAR", "ANC_IN_EAR_SPKR", +}; + +static const char * const rx_echo_mux_text[] = { + "ZERO", "RX_MIX0", "NA", "NA", "NA", "NA", "NA", "NA", + "RX_MIX7", "RX_MIX8", "NA", "NA", "NA", "NA", "RX_MIX9" +}; + +static const char *const slim_rx_mux_text[] = { + "ZERO", "AIF1_PB", "AIF2_PB", "AIF3_PB", "AIF4_PB" +}; + +static const char *const cdc_if_rx0_mux_text[] = { + "SLIM RX0", "I2S RX0" +}; +static const char *const cdc_if_rx1_mux_text[] = { + "SLIM RX1", "I2S RX1" +}; +static const char *const cdc_if_rx2_mux_text[] = { + "SLIM RX2", "I2SRX1_0", "I2SRX0_2" +}; +static const char *const cdc_if_rx3_mux_text[] = { + "SLIM RX3", "I2SRX1_1", "I2SRX0_3" +}; +static const char *const cdc_if_rx4_mux_text[] = { + "SLIM RX4", "I2S RX4" +}; +static const char *const cdc_if_rx5_mux_text[] = { + "SLIM RX5", "I2S RX5" +}; +static const char *const cdc_if_rx6_mux_text[] = { + "SLIM RX6", "I2S RX6" +}; +static const char *const cdc_if_rx7_mux_text[] = { + "SLIM RX7", "I2S RX7" +}; + +static const char * const asrc2_mux_text[] = { + "ZERO", "ASRC_IN_SPKR1", +}; + +static const char * const asrc3_mux_text[] = { + "ZERO", "ASRC_IN_SPKR2", +}; + +static const char * const native_mux_text[] = { + "OFF", "ON", +}; + +static const char *const wdma3_port0_text[] = { + "RX_MIX_TX0", "DEC0" +}; + +static const char *const wdma3_port1_text[] = { + "RX_MIX_TX1", "DEC1" +}; + +static const char *const wdma3_port2_text[] = { + "RX_MIX_TX2", "DEC2" +}; + +static const char *const wdma3_port3_text[] = { + "RX_MIX_TX3", "DEC3" +}; + +static const char *const wdma3_port4_text[] = { + "RX_MIX_TX4", "DEC4" +}; + +static const char *const wdma3_port5_text[] = { + "RX_MIX_TX5", "DEC5" +}; + +static const char *const wdma3_port6_text[] = { + "RX_MIX_TX6", "DEC6" +}; + +static const char *const wdma3_ch_text[] = { + "PORT_0", "PORT_1", "PORT_2", "PORT_3", "PORT_4", + "PORT_5", "PORT_6", "PORT_7", "PORT_8", +}; + +static const struct snd_kcontrol_new aif4_vi_mixer[] = { + SOC_SINGLE_EXT("SPKR_VI_1", SND_SOC_NOPM, WCD9360_TX14, 1, 0, + pahu_vi_feed_mixer_get, pahu_vi_feed_mixer_put), + SOC_SINGLE_EXT("SPKR_VI_2", SND_SOC_NOPM, WCD9360_TX15, 1, 0, + pahu_vi_feed_mixer_get, pahu_vi_feed_mixer_put), +}; + +static const struct snd_kcontrol_new aif1_cap_mixer[] = { + SOC_SINGLE_EXT("SLIM TX0", SND_SOC_NOPM, WCD9360_TX0, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX1", SND_SOC_NOPM, WCD9360_TX1, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX2", SND_SOC_NOPM, WCD9360_TX2, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX3", SND_SOC_NOPM, WCD9360_TX3, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX4", SND_SOC_NOPM, WCD9360_TX4, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX5", SND_SOC_NOPM, WCD9360_TX5, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX6", SND_SOC_NOPM, WCD9360_TX6, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX7", SND_SOC_NOPM, WCD9360_TX7, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX8", SND_SOC_NOPM, WCD9360_TX8, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX9", SND_SOC_NOPM, WCD9360_TX9, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX10", SND_SOC_NOPM, WCD9360_TX10, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX11", SND_SOC_NOPM, WCD9360_TX11, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX13", SND_SOC_NOPM, WCD9360_TX13, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), +}; + +static const struct snd_kcontrol_new aif2_cap_mixer[] = { + SOC_SINGLE_EXT("SLIM TX0", SND_SOC_NOPM, WCD9360_TX0, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX1", SND_SOC_NOPM, WCD9360_TX1, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX2", SND_SOC_NOPM, WCD9360_TX2, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX3", SND_SOC_NOPM, WCD9360_TX3, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX4", SND_SOC_NOPM, WCD9360_TX4, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX5", SND_SOC_NOPM, WCD9360_TX5, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX6", SND_SOC_NOPM, WCD9360_TX6, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX7", SND_SOC_NOPM, WCD9360_TX7, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX8", SND_SOC_NOPM, WCD9360_TX8, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX9", SND_SOC_NOPM, WCD9360_TX9, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX10", SND_SOC_NOPM, WCD9360_TX10, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX11", SND_SOC_NOPM, WCD9360_TX11, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX13", SND_SOC_NOPM, WCD9360_TX13, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), +}; + +static const struct snd_kcontrol_new aif3_cap_mixer[] = { + SOC_SINGLE_EXT("SLIM TX0", SND_SOC_NOPM, WCD9360_TX0, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX1", SND_SOC_NOPM, WCD9360_TX1, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX2", SND_SOC_NOPM, WCD9360_TX2, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX3", SND_SOC_NOPM, WCD9360_TX3, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX4", SND_SOC_NOPM, WCD9360_TX4, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX5", SND_SOC_NOPM, WCD9360_TX5, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX6", SND_SOC_NOPM, WCD9360_TX6, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX7", SND_SOC_NOPM, WCD9360_TX7, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX8", SND_SOC_NOPM, WCD9360_TX8, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX9", SND_SOC_NOPM, WCD9360_TX9, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX10", SND_SOC_NOPM, WCD9360_TX10, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX11", SND_SOC_NOPM, WCD9360_TX11, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX13", SND_SOC_NOPM, WCD9360_TX13, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), +}; + +static const struct snd_kcontrol_new aif4_mad_mixer[] = { + SOC_SINGLE_EXT("SLIM TX13", SND_SOC_NOPM, WCD9360_TX13, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), +}; + +WCD_DAPM_ENUM_EXT(slim_rx0, SND_SOC_NOPM, 0, slim_rx_mux_text, + slim_rx_mux_get, slim_rx_mux_put); +WCD_DAPM_ENUM_EXT(slim_rx1, SND_SOC_NOPM, 0, slim_rx_mux_text, + slim_rx_mux_get, slim_rx_mux_put); +WCD_DAPM_ENUM_EXT(slim_rx2, SND_SOC_NOPM, 0, slim_rx_mux_text, + slim_rx_mux_get, slim_rx_mux_put); +WCD_DAPM_ENUM_EXT(slim_rx3, SND_SOC_NOPM, 0, slim_rx_mux_text, + slim_rx_mux_get, slim_rx_mux_put); +WCD_DAPM_ENUM_EXT(slim_rx4, SND_SOC_NOPM, 0, slim_rx_mux_text, + slim_rx_mux_get, slim_rx_mux_put); +WCD_DAPM_ENUM_EXT(slim_rx5, SND_SOC_NOPM, 0, slim_rx_mux_text, + slim_rx_mux_get, slim_rx_mux_put); +WCD_DAPM_ENUM_EXT(slim_rx6, SND_SOC_NOPM, 0, slim_rx_mux_text, + slim_rx_mux_get, slim_rx_mux_put); +WCD_DAPM_ENUM_EXT(slim_rx7, SND_SOC_NOPM, 0, slim_rx_mux_text, + slim_rx_mux_get, slim_rx_mux_put); + +WCD_DAPM_ENUM(cdc_if_rx0, SND_SOC_NOPM, 0, cdc_if_rx0_mux_text); +WCD_DAPM_ENUM(cdc_if_rx1, SND_SOC_NOPM, 0, cdc_if_rx1_mux_text); +WCD_DAPM_ENUM(cdc_if_rx2, SND_SOC_NOPM, 0, cdc_if_rx2_mux_text); +WCD_DAPM_ENUM(cdc_if_rx3, SND_SOC_NOPM, 0, cdc_if_rx3_mux_text); +WCD_DAPM_ENUM(cdc_if_rx4, SND_SOC_NOPM, 0, cdc_if_rx4_mux_text); +WCD_DAPM_ENUM(cdc_if_rx5, SND_SOC_NOPM, 0, cdc_if_rx5_mux_text); +WCD_DAPM_ENUM(cdc_if_rx6, SND_SOC_NOPM, 0, cdc_if_rx6_mux_text); +WCD_DAPM_ENUM(cdc_if_rx7, SND_SOC_NOPM, 0, cdc_if_rx7_mux_text); + +WCD_DAPM_ENUM(rx_int0_2, WCD9360_CDC_RX_INP_MUX_RX_INT0_CFG1, 0, + rx_int0_7_mix_mux_text); +WCD_DAPM_ENUM(rx_int7_2, WCD9360_CDC_RX_INP_MUX_RX_INT7_CFG1, 0, + rx_int0_7_mix_mux_text); +WCD_DAPM_ENUM(rx_int8_2, WCD9360_CDC_RX_INP_MUX_RX_INT8_CFG1, 0, + rx_int_mix_mux_text); +WCD_DAPM_ENUM(rx_int9_2, WCD9360_CDC_RX_INP_MUX_RX_INT9_CFG1, 0, + rx_int0_7_mix_mux_text); + +WCD_DAPM_ENUM(rx_int0_1_mix_inp0, WCD9360_CDC_RX_INP_MUX_RX_INT0_CFG0, 0, + rx_prim_mix_text); +WCD_DAPM_ENUM(rx_int0_1_mix_inp1, WCD9360_CDC_RX_INP_MUX_RX_INT0_CFG0, 4, + rx_prim_mix_text); +WCD_DAPM_ENUM(rx_int0_1_mix_inp2, WCD9360_CDC_RX_INP_MUX_RX_INT0_CFG1, 4, + rx_prim_mix_text); +WCD_DAPM_ENUM(rx_int7_1_mix_inp0, WCD9360_CDC_RX_INP_MUX_RX_INT7_CFG0, 0, + rx_prim_mix_text); +WCD_DAPM_ENUM(rx_int7_1_mix_inp1, WCD9360_CDC_RX_INP_MUX_RX_INT7_CFG0, 4, + rx_prim_mix_text); +WCD_DAPM_ENUM(rx_int7_1_mix_inp2, WCD9360_CDC_RX_INP_MUX_RX_INT7_CFG1, 4, + rx_prim_mix_text); +WCD_DAPM_ENUM(rx_int8_1_mix_inp0, WCD9360_CDC_RX_INP_MUX_RX_INT8_CFG0, 0, + rx_prim_mix_text); +WCD_DAPM_ENUM(rx_int8_1_mix_inp1, WCD9360_CDC_RX_INP_MUX_RX_INT8_CFG0, 4, + rx_prim_mix_text); +WCD_DAPM_ENUM(rx_int8_1_mix_inp2, WCD9360_CDC_RX_INP_MUX_RX_INT8_CFG1, 4, + rx_prim_mix_text); +WCD_DAPM_ENUM(rx_int9_1_mix_inp0, WCD9360_CDC_RX_INP_MUX_RX_INT9_CFG0, 0, + rx_prim_mix_text); +WCD_DAPM_ENUM(rx_int9_1_mix_inp1, WCD9360_CDC_RX_INP_MUX_RX_INT9_CFG0, 4, + rx_prim_mix_text); +WCD_DAPM_ENUM(rx_int9_1_mix_inp2, WCD9360_CDC_RX_INP_MUX_RX_INT9_CFG1, 4, + rx_prim_mix_text); + +WCD_DAPM_ENUM(rx_int0_mix2_inp, WCD9360_CDC_RX_INP_MUX_SIDETONE_SRC_CFG0, 0, + rx_sidetone_mix_text); +WCD_DAPM_ENUM(rx_int7_mix2_inp, WCD9360_CDC_RX_INP_MUX_SIDETONE_SRC_CFG1, 2, + rx_sidetone_mix_text); +WCD_DAPM_ENUM(rx_int9_mix2_inp, WCD9360_CDC_RX_INP_MUX_SIDETONE_SRC_CFG1, 4, + rx_sidetone_mix_text); + +WCD_DAPM_ENUM(tx_adc_mux10, WCD9360_CDC_TX_INP_MUX_ADC_MUX1_CFG1, 4, + adc_mux_text); +WCD_DAPM_ENUM(tx_adc_mux11, WCD9360_CDC_TX_INP_MUX_ADC_MUX2_CFG1, 4, + adc_mux_text); + +WCD_DAPM_ENUM(tx_dmic_mux0, WCD9360_CDC_TX_INP_MUX_ADC_MUX0_CFG0, 3, + dmic_mux_text); +WCD_DAPM_ENUM(tx_dmic_mux1, WCD9360_CDC_TX_INP_MUX_ADC_MUX1_CFG0, 3, + dmic_mux_text); +WCD_DAPM_ENUM(tx_dmic_mux2, WCD9360_CDC_TX_INP_MUX_ADC_MUX2_CFG0, 3, + dmic_mux_text); +WCD_DAPM_ENUM(tx_dmic_mux3, WCD9360_CDC_TX_INP_MUX_ADC_MUX3_CFG0, 3, + dmic_mux_text); +WCD_DAPM_ENUM(tx_dmic_mux4, WCD9360_CDC_TX_INP_MUX_ADC_MUX4_CFG0, 3, + dmic_mux_text); +WCD_DAPM_ENUM(tx_dmic_mux5, WCD9360_CDC_TX_INP_MUX_ADC_MUX5_CFG0, 3, + dmic_mux_text); +WCD_DAPM_ENUM(tx_dmic_mux6, WCD9360_CDC_TX_INP_MUX_ADC_MUX6_CFG0, 3, + dmic_mux_text); +WCD_DAPM_ENUM(tx_dmic_mux7, WCD9360_CDC_TX_INP_MUX_ADC_MUX7_CFG0, 3, + dmic_mux_text); +WCD_DAPM_ENUM(tx_dmic_mux8, WCD9360_CDC_TX_INP_MUX_ADC_MUX8_CFG0, 3, + dmic_mux_text); +WCD_DAPM_ENUM(tx_dmic_mux10, WCD9360_CDC_TX_INP_MUX_ADC_MUX10_CFG0, 3, + dmic_mux_text); +WCD_DAPM_ENUM(tx_dmic_mux11, WCD9360_CDC_TX_INP_MUX_ADC_MUX11_CFG0, 3, + dmic_mux_text); + +WCD_DAPM_ENUM(tx_amic_mux0, WCD9360_CDC_TX_INP_MUX_ADC_MUX0_CFG0, 0, + amic_mux_text); +WCD_DAPM_ENUM(tx_amic_mux1, WCD9360_CDC_TX_INP_MUX_ADC_MUX1_CFG0, 0, + amic_mux_text); +WCD_DAPM_ENUM(tx_amic_mux2, WCD9360_CDC_TX_INP_MUX_ADC_MUX2_CFG0, 0, + amic_mux_text); +WCD_DAPM_ENUM(tx_amic_mux3, WCD9360_CDC_TX_INP_MUX_ADC_MUX3_CFG0, 0, + amic_mux_text); +WCD_DAPM_ENUM(tx_amic_mux4, WCD9360_CDC_TX_INP_MUX_ADC_MUX4_CFG0, 0, + amic_mux_text); +WCD_DAPM_ENUM(tx_amic_mux5, WCD9360_CDC_TX_INP_MUX_ADC_MUX5_CFG0, 0, + amic_mux_text); +WCD_DAPM_ENUM(tx_amic_mux6, WCD9360_CDC_TX_INP_MUX_ADC_MUX6_CFG0, 0, + amic_mux_text); +WCD_DAPM_ENUM(tx_amic_mux7, WCD9360_CDC_TX_INP_MUX_ADC_MUX7_CFG0, 0, + amic_mux_text); +WCD_DAPM_ENUM(tx_amic_mux8, WCD9360_CDC_TX_INP_MUX_ADC_MUX8_CFG0, 0, + amic_mux_text); +WCD_DAPM_ENUM(tx_amic_mux10, WCD9360_CDC_TX_INP_MUX_ADC_MUX10_CFG0, 0, + amic_mux_text); +WCD_DAPM_ENUM(tx_amic_mux11, WCD9360_CDC_TX_INP_MUX_ADC_MUX11_CFG0, 0, + amic_mux_text); + +WCD_DAPM_ENUM(tx_adc2_in, WCD9360_ANA_AMIC_INPUT_SWITCH_CTL, 7, adc2_in_text); +WCD_DAPM_ENUM(tx_adc4_in, WCD9360_ANA_AMIC_INPUT_SWITCH_CTL, 6, adc4_in_text); + +WCD_DAPM_ENUM(cdc_if_tx0, WCD9360_CDC_IF_ROUTER_TX_MUX_CFG0, 0, + cdc_if_tx0_mux_text); +WCD_DAPM_ENUM(cdc_if_tx1, WCD9360_CDC_IF_ROUTER_TX_MUX_CFG0, 2, + cdc_if_tx1_mux_text); +WCD_DAPM_ENUM(cdc_if_tx2, WCD9360_CDC_IF_ROUTER_TX_MUX_CFG0, 4, + cdc_if_tx2_mux_text); +WCD_DAPM_ENUM(cdc_if_tx3, WCD9360_CDC_IF_ROUTER_TX_MUX_CFG0, 6, + cdc_if_tx3_mux_text); +WCD_DAPM_ENUM(cdc_if_tx4, WCD9360_CDC_IF_ROUTER_TX_MUX_CFG1, 0, + cdc_if_tx4_mux_text); +WCD_DAPM_ENUM(cdc_if_tx5, WCD9360_CDC_IF_ROUTER_TX_MUX_CFG1, 2, + cdc_if_tx5_mux_text); +WCD_DAPM_ENUM(cdc_if_tx6, WCD9360_CDC_IF_ROUTER_TX_MUX_CFG1, 4, + cdc_if_tx6_mux_text); +WCD_DAPM_ENUM(cdc_if_tx7, WCD9360_CDC_IF_ROUTER_TX_MUX_CFG1, 6, + cdc_if_tx7_mux_text); +WCD_DAPM_ENUM(cdc_if_tx8, WCD9360_CDC_IF_ROUTER_TX_MUX_CFG2, 0, + cdc_if_tx8_mux_text); +WCD_DAPM_ENUM(cdc_if_tx9, WCD9360_CDC_IF_ROUTER_TX_MUX_CFG2, 2, + cdc_if_tx9_mux_text); +WCD_DAPM_ENUM(cdc_if_tx10, WCD9360_CDC_IF_ROUTER_TX_MUX_CFG2, 4, + cdc_if_tx10_mux_text); +WCD_DAPM_ENUM(cdc_if_tx10_inp2, WCD9360_DATA_HUB_SB_TX10_INP_CFG, 3, + cdc_if_tx10_mux2_text); +WCD_DAPM_ENUM(cdc_if_tx11_inp1, WCD9360_CDC_IF_ROUTER_TX_MUX_CFG3, 0, + cdc_if_tx11_inp1_mux_text); +WCD_DAPM_ENUM(cdc_if_tx11, WCD9360_DATA_HUB_SB_TX11_INP_CFG, 0, + cdc_if_tx11_mux_text); +WCD_DAPM_ENUM(cdc_if_tx11_inp2, WCD9360_DATA_HUB_SB_TX11_INP_CFG, 3, + cdc_if_tx11_mux2_text); +WCD_DAPM_ENUM(cdc_if_tx13_inp1, WCD9360_CDC_IF_ROUTER_TX_MUX_CFG3, 4, + cdc_if_tx13_inp1_mux_text); +WCD_DAPM_ENUM(cdc_if_tx13, WCD9360_DATA_HUB_SB_TX13_INP_CFG, 0, + cdc_if_tx13_mux_text); + +WCD_DAPM_ENUM(rx_mix_tx0, WCD9360_CDC_RX_INP_MUX_RX_MIX_CFG0, 0, + rx_echo_mux_text); +WCD_DAPM_ENUM(rx_mix_tx1, WCD9360_CDC_RX_INP_MUX_RX_MIX_CFG0, 4, + rx_echo_mux_text); +WCD_DAPM_ENUM(rx_mix_tx2, WCD9360_CDC_RX_INP_MUX_RX_MIX_CFG1, 0, + rx_echo_mux_text); +WCD_DAPM_ENUM(rx_mix_tx3, WCD9360_CDC_RX_INP_MUX_RX_MIX_CFG1, 4, + rx_echo_mux_text); +WCD_DAPM_ENUM(rx_mix_tx4, WCD9360_CDC_RX_INP_MUX_RX_MIX_CFG2, 0, + rx_echo_mux_text); +WCD_DAPM_ENUM(rx_mix_tx5, WCD9360_CDC_RX_INP_MUX_RX_MIX_CFG2, 4, + rx_echo_mux_text); +WCD_DAPM_ENUM(rx_mix_tx6, WCD9360_CDC_RX_INP_MUX_RX_MIX_CFG3, 0, + rx_echo_mux_text); +WCD_DAPM_ENUM(rx_mix_tx7, WCD9360_CDC_RX_INP_MUX_RX_MIX_CFG3, 4, + rx_echo_mux_text); +WCD_DAPM_ENUM(rx_mix_tx8, WCD9360_CDC_RX_INP_MUX_RX_MIX_CFG4, 0, + rx_echo_mux_text); + +WCD_DAPM_ENUM(iir0_inp0, WCD9360_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG0, 0, + iir_inp_mux_text); +WCD_DAPM_ENUM(iir0_inp1, WCD9360_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG1, 0, + iir_inp_mux_text); +WCD_DAPM_ENUM(iir0_inp2, WCD9360_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG2, 0, + iir_inp_mux_text); +WCD_DAPM_ENUM(iir0_inp3, WCD9360_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG3, 0, + iir_inp_mux_text); + +WCD_DAPM_ENUM(rx_int0_1_interp, SND_SOC_NOPM, 0, rx_int0_1_interp_mux_text); +WCD_DAPM_ENUM(rx_int7_1_interp, SND_SOC_NOPM, 0, rx_int7_1_interp_mux_text); +WCD_DAPM_ENUM(rx_int8_1_interp, SND_SOC_NOPM, 0, rx_int8_1_interp_mux_text); +WCD_DAPM_ENUM(rx_int9_1_interp, SND_SOC_NOPM, 0, rx_int9_1_interp_mux_text); + +WCD_DAPM_ENUM(rx_int0_2_interp, SND_SOC_NOPM, 0, rx_int0_2_interp_mux_text); +WCD_DAPM_ENUM(rx_int7_2_interp, SND_SOC_NOPM, 0, rx_int7_2_interp_mux_text); +WCD_DAPM_ENUM(rx_int8_2_interp, SND_SOC_NOPM, 0, rx_int8_2_interp_mux_text); +WCD_DAPM_ENUM(rx_int9_2_interp, SND_SOC_NOPM, 0, rx_int9_2_interp_mux_text); + +WCD_DAPM_ENUM(mad_sel, WCD9360_CPE_SS_SVA_CFG, 0, + mad_sel_txt); + +WCD_DAPM_ENUM(mad_inp_mux, WCD9360_CPE_SS_SVA_CFG, 2, + mad_inp_mux_txt); + +WCD_DAPM_ENUM_EXT(rx_int0_dem_inp, WCD9360_CDC_RX0_RX_PATH_SEC0, 0, + rx_int_dem_inp_mux_text, snd_soc_dapm_get_enum_double, + pahu_int_dem_inp_mux_put); + +WCD_DAPM_ENUM_EXT(rx_int9_dem_inp, WCD9360_CDC_RX9_RX_PATH_SEC0, 0, + rx_int_dem_inp_mux_text, snd_soc_dapm_get_enum_double, + pahu_int_dem_inp_mux_put); + +WCD_DAPM_ENUM_EXT(tx_adc_mux0, WCD9360_CDC_TX_INP_MUX_ADC_MUX0_CFG1, 0, + adc_mux_text, snd_soc_dapm_get_enum_double, pahu_dec_enum_put); +WCD_DAPM_ENUM_EXT(tx_adc_mux1, WCD9360_CDC_TX_INP_MUX_ADC_MUX1_CFG1, 0, + adc_mux_text, snd_soc_dapm_get_enum_double, pahu_dec_enum_put); +WCD_DAPM_ENUM_EXT(tx_adc_mux2, WCD9360_CDC_TX_INP_MUX_ADC_MUX2_CFG1, 0, + adc_mux_text, snd_soc_dapm_get_enum_double, pahu_dec_enum_put); +WCD_DAPM_ENUM_EXT(tx_adc_mux3, WCD9360_CDC_TX_INP_MUX_ADC_MUX3_CFG1, 0, + adc_mux_text, snd_soc_dapm_get_enum_double, pahu_dec_enum_put); +WCD_DAPM_ENUM_EXT(tx_adc_mux4, WCD9360_CDC_TX_INP_MUX_ADC_MUX0_CFG1, 2, + adc_mux_text, snd_soc_dapm_get_enum_double, pahu_dec_enum_put); +WCD_DAPM_ENUM_EXT(tx_adc_mux5, WCD9360_CDC_TX_INP_MUX_ADC_MUX1_CFG1, 2, + adc_mux_text, snd_soc_dapm_get_enum_double, pahu_dec_enum_put); +WCD_DAPM_ENUM_EXT(tx_adc_mux6, WCD9360_CDC_TX_INP_MUX_ADC_MUX2_CFG1, 2, + adc_mux_text, snd_soc_dapm_get_enum_double, pahu_dec_enum_put); +WCD_DAPM_ENUM_EXT(tx_adc_mux7, WCD9360_CDC_TX_INP_MUX_ADC_MUX3_CFG1, 2, + adc_mux_text, snd_soc_dapm_get_enum_double, pahu_dec_enum_put); +WCD_DAPM_ENUM_EXT(tx_adc_mux8, WCD9360_CDC_TX_INP_MUX_ADC_MUX0_CFG1, 4, + adc_mux_text, snd_soc_dapm_get_enum_double, pahu_dec_enum_put); + +WCD_DAPM_ENUM(asrc2, WCD9360_CDC_RX_INP_MUX_SPLINE_ASRC_CFG0, 4, + asrc2_mux_text); +WCD_DAPM_ENUM(asrc3, WCD9360_CDC_RX_INP_MUX_SPLINE_ASRC_CFG0, 6, + asrc3_mux_text); + +WCD_DAPM_ENUM(int7_2_native, SND_SOC_NOPM, 0, native_mux_text); +WCD_DAPM_ENUM(int8_2_native, SND_SOC_NOPM, 0, native_mux_text); + +WCD_DAPM_ENUM(anc0_fb, WCD9360_CDC_RX_INP_MUX_ANC_CFG0, 0, anc0_fb_mux_text); + +WCD_DAPM_ENUM(wdma3_port0, WCD9360_DMA_WDMA3_PRT_CFG, 0, wdma3_port0_text); +WCD_DAPM_ENUM(wdma3_port1, WCD9360_DMA_WDMA3_PRT_CFG, 1, wdma3_port1_text); +WCD_DAPM_ENUM(wdma3_port2, WCD9360_DMA_WDMA3_PRT_CFG, 2, wdma3_port2_text); +WCD_DAPM_ENUM(wdma3_port3, WCD9360_DMA_WDMA3_PRT_CFG, 3, wdma3_port3_text); +WCD_DAPM_ENUM(wdma3_port4, WCD9360_DMA_WDMA3_PRT_CFG, 4, wdma3_port4_text); +WCD_DAPM_ENUM(wdma3_port5, WCD9360_DMA_WDMA3_PRT_CFG, 5, wdma3_port5_text); +WCD_DAPM_ENUM(wdma3_port6, WCD9360_DMA_WDMA3_PRT_CFG, 6, wdma3_port6_text); + +WCD_DAPM_ENUM(wdma3_ch0, WCD9360_DMA_CH_0_1_CFG_WDMA_3, 0, wdma3_ch_text); +WCD_DAPM_ENUM(wdma3_ch1, WCD9360_DMA_CH_0_1_CFG_WDMA_3, 4, wdma3_ch_text); +WCD_DAPM_ENUM(wdma3_ch2, WCD9360_DMA_CH_2_3_CFG_WDMA_3, 0, wdma3_ch_text); +WCD_DAPM_ENUM(wdma3_ch3, WCD9360_DMA_CH_2_3_CFG_WDMA_3, 4, wdma3_ch_text); + +static const struct snd_kcontrol_new anc_ear_switch = + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct snd_kcontrol_new anc_ear_spkr_switch = + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct snd_kcontrol_new anc_spkr_pa_switch = + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct snd_kcontrol_new mad_cpe1_switch = + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct snd_kcontrol_new mad_cpe2_switch = + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct snd_kcontrol_new mad_brdcst_switch = + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct snd_kcontrol_new adc_us_mux0_switch = + SOC_DAPM_SINGLE("US_Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct snd_kcontrol_new adc_us_mux1_switch = + SOC_DAPM_SINGLE("US_Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct snd_kcontrol_new adc_us_mux2_switch = + SOC_DAPM_SINGLE("US_Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct snd_kcontrol_new adc_us_mux3_switch = + SOC_DAPM_SINGLE("US_Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct snd_kcontrol_new adc_us_mux4_switch = + SOC_DAPM_SINGLE("US_Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct snd_kcontrol_new adc_us_mux5_switch = + SOC_DAPM_SINGLE("US_Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct snd_kcontrol_new adc_us_mux6_switch = + SOC_DAPM_SINGLE("US_Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct snd_kcontrol_new adc_us_mux7_switch = + SOC_DAPM_SINGLE("US_Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct snd_kcontrol_new adc_us_mux8_switch = + SOC_DAPM_SINGLE("US_Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct snd_kcontrol_new wdma3_onoff_switch = + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0); + +static const char *const i2s_tx1_0_txt[] = { + "ZERO", "SB_TX8", "SB_RX2", "SB_TX12" +}; + +static const char *const i2s_tx1_1_txt[] = { + "ZERO", "SB_RX0", "SB_RX1", "SB_RX2", "SB_RX3", "SB_TX11" +}; + +WCD_DAPM_ENUM(i2s_tx1_0_inp, WCD9360_DATA_HUB_I2S_TX1_0_CFG, 0, i2s_tx1_0_txt); +WCD_DAPM_ENUM(i2s_tx1_1_inp, WCD9360_DATA_HUB_I2S_TX1_1_CFG, 0, i2s_tx1_1_txt); + +static const struct snd_soc_dapm_widget pahu_dapm_widgets[] = { + SND_SOC_DAPM_AIF_IN_E("AIF1 PB", "AIF1 Playback", 0, SND_SOC_NOPM, + AIF1_PB, 0, pahu_codec_enable_slimrx, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_AIF_IN_E("AIF2 PB", "AIF2 Playback", 0, SND_SOC_NOPM, + AIF2_PB, 0, pahu_codec_enable_slimrx, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_AIF_IN_E("AIF3 PB", "AIF3 Playback", 0, SND_SOC_NOPM, + AIF3_PB, 0, pahu_codec_enable_slimrx, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_AIF_IN_E("AIF4 PB", "AIF4 Playback", 0, SND_SOC_NOPM, + AIF4_PB, 0, pahu_codec_enable_slimrx, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_AIF_IN_E("I2S1 PB", "I2S1 Playback", 0, SND_SOC_NOPM, + I2S1_PB, 0, pahu_i2s_aif_rx_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + WCD_DAPM_MUX("SLIM RX0 MUX", WCD9360_RX0, slim_rx0), + WCD_DAPM_MUX("SLIM RX1 MUX", WCD9360_RX1, slim_rx1), + WCD_DAPM_MUX("SLIM RX2 MUX", WCD9360_RX2, slim_rx2), + WCD_DAPM_MUX("SLIM RX3 MUX", WCD9360_RX3, slim_rx3), + WCD_DAPM_MUX("SLIM RX4 MUX", WCD9360_RX4, slim_rx4), + WCD_DAPM_MUX("SLIM RX5 MUX", WCD9360_RX5, slim_rx5), + WCD_DAPM_MUX("SLIM RX6 MUX", WCD9360_RX6, slim_rx6), + WCD_DAPM_MUX("SLIM RX7 MUX", WCD9360_RX7, slim_rx7), + + SND_SOC_DAPM_MIXER("SLIM RX0", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM RX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM RX2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM RX3", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM RX4", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM RX5", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM RX6", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM RX7", SND_SOC_NOPM, 0, 0, NULL, 0), + + WCD_DAPM_MUX("CDC_IF RX0 MUX", WCD9360_RX0, cdc_if_rx0), + WCD_DAPM_MUX("CDC_IF RX1 MUX", WCD9360_RX1, cdc_if_rx1), + WCD_DAPM_MUX("CDC_IF RX2 MUX", WCD9360_RX2, cdc_if_rx2), + WCD_DAPM_MUX("CDC_IF RX3 MUX", WCD9360_RX3, cdc_if_rx3), + WCD_DAPM_MUX("CDC_IF RX4 MUX", WCD9360_RX4, cdc_if_rx4), + WCD_DAPM_MUX("CDC_IF RX5 MUX", WCD9360_RX5, cdc_if_rx5), + WCD_DAPM_MUX("CDC_IF RX6 MUX", WCD9360_RX6, cdc_if_rx6), + WCD_DAPM_MUX("CDC_IF RX7 MUX", WCD9360_RX7, cdc_if_rx7), + + SND_SOC_DAPM_MUX_E("RX INT0_2 MUX", SND_SOC_NOPM, INTERP_EAR, 0, + &rx_int0_2_mux, pahu_codec_enable_mix_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT7_2 MUX", SND_SOC_NOPM, INTERP_SPKR1, 0, + &rx_int7_2_mux, pahu_codec_enable_mix_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT8_2 MUX", SND_SOC_NOPM, INTERP_SPKR2, 0, + &rx_int8_2_mux, pahu_codec_enable_mix_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT9_2 MUX", SND_SOC_NOPM, INTERP_AUX, 0, + &rx_int9_2_mux, pahu_codec_enable_mix_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + + WCD_DAPM_MUX("RX INT0_1 MIX1 INP0", 0, rx_int0_1_mix_inp0), + WCD_DAPM_MUX("RX INT0_1 MIX1 INP1", 0, rx_int0_1_mix_inp1), + WCD_DAPM_MUX("RX INT0_1 MIX1 INP2", 0, rx_int0_1_mix_inp2), + + SND_SOC_DAPM_MUX_E("RX INT7_1 MIX1 INP0", SND_SOC_NOPM, 0, 0, + &rx_int7_1_mix_inp0_mux, pahu_codec_enable_swr, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT7_1 MIX1 INP1", SND_SOC_NOPM, 0, 0, + &rx_int7_1_mix_inp1_mux, pahu_codec_enable_swr, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT7_1 MIX1 INP2", SND_SOC_NOPM, 0, 0, + &rx_int7_1_mix_inp2_mux, pahu_codec_enable_swr, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT8_1 MIX1 INP0", SND_SOC_NOPM, 0, 0, + &rx_int8_1_mix_inp0_mux, pahu_codec_enable_swr, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT8_1 MIX1 INP1", SND_SOC_NOPM, 0, 0, + &rx_int8_1_mix_inp1_mux, pahu_codec_enable_swr, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT8_1 MIX1 INP2", SND_SOC_NOPM, 0, 0, + &rx_int8_1_mix_inp2_mux, pahu_codec_enable_swr, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + WCD_DAPM_MUX("RX INT9_1 MIX1 INP0", 0, rx_int9_1_mix_inp0), + WCD_DAPM_MUX("RX INT9_1 MIX1 INP1", 0, rx_int9_1_mix_inp1), + WCD_DAPM_MUX("RX INT9_1 MIX1 INP2", 0, rx_int9_1_mix_inp2), + + SND_SOC_DAPM_MIXER("RX INT0_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT0 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT7_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT7 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT8_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT8 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT9_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT9 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_MIXER("RX INT0 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT7 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT9 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_MIXER_E("RX INT7 CHAIN", SND_SOC_NOPM, 0, 0, + NULL, 0, pahu_codec_spk_boost_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("RX INT8 CHAIN", SND_SOC_NOPM, 0, 0, + NULL, 0, pahu_codec_spk_boost_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX_E("RX INT0 MIX2 INP", SND_SOC_NOPM, INTERP_EAR, + 0, &rx_int0_mix2_inp_mux, pahu_codec_enable_rx_path_clk, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT7 MIX2 INP", SND_SOC_NOPM, INTERP_SPKR1, + 0, &rx_int7_mix2_inp_mux, pahu_codec_enable_rx_path_clk, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT9 MIX2 INP", SND_SOC_NOPM, INTERP_AUX, + 0, &rx_int9_mix2_inp_mux, pahu_codec_enable_rx_path_clk, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + WCD_DAPM_MUX("CDC_IF TX0 MUX", WCD9360_TX0, cdc_if_tx0), + WCD_DAPM_MUX("CDC_IF TX1 MUX", WCD9360_TX1, cdc_if_tx1), + WCD_DAPM_MUX("CDC_IF TX2 MUX", WCD9360_TX2, cdc_if_tx2), + WCD_DAPM_MUX("CDC_IF TX3 MUX", WCD9360_TX3, cdc_if_tx3), + WCD_DAPM_MUX("CDC_IF TX4 MUX", WCD9360_TX4, cdc_if_tx4), + WCD_DAPM_MUX("CDC_IF TX5 MUX", WCD9360_TX5, cdc_if_tx5), + WCD_DAPM_MUX("CDC_IF TX6 MUX", WCD9360_TX6, cdc_if_tx6), + WCD_DAPM_MUX("CDC_IF TX7 MUX", WCD9360_TX7, cdc_if_tx7), + WCD_DAPM_MUX("CDC_IF TX8 MUX", WCD9360_TX8, cdc_if_tx8), + WCD_DAPM_MUX("CDC_IF TX9 MUX", WCD9360_TX9, cdc_if_tx9), + WCD_DAPM_MUX("CDC_IF TX10 MUX", WCD9360_TX10, cdc_if_tx10), + WCD_DAPM_MUX("CDC_IF TX11 MUX", WCD9360_TX11, cdc_if_tx11), + WCD_DAPM_MUX("CDC_IF TX11 INP1 MUX", WCD9360_TX11, cdc_if_tx11_inp1), + WCD_DAPM_MUX("CDC_IF TX13 MUX", WCD9360_TX13, cdc_if_tx13), + WCD_DAPM_MUX("CDC_IF TX13 INP1 MUX", WCD9360_TX13, cdc_if_tx13_inp1), + WCD_DAPM_MUX("CDC_IF TX10 MUX2", WCD9360_TX10, cdc_if_tx10_inp2), + WCD_DAPM_MUX("CDC_IF TX11 MUX2", WCD9360_TX11, cdc_if_tx11_inp2), + + SND_SOC_DAPM_MUX_E("ADC MUX0", WCD9360_CDC_TX0_TX_PATH_CTL, 5, 0, + &tx_adc_mux0_mux, pahu_codec_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX_E("ADC MUX1", WCD9360_CDC_TX1_TX_PATH_CTL, 5, 0, + &tx_adc_mux1_mux, pahu_codec_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX_E("ADC MUX2", WCD9360_CDC_TX2_TX_PATH_CTL, 5, 0, + &tx_adc_mux2_mux, pahu_codec_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX_E("ADC MUX3", WCD9360_CDC_TX3_TX_PATH_CTL, 5, 0, + &tx_adc_mux3_mux, pahu_codec_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX_E("ADC MUX4", WCD9360_CDC_TX4_TX_PATH_CTL, 5, 0, + &tx_adc_mux4_mux, pahu_codec_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX_E("ADC MUX5", WCD9360_CDC_TX5_TX_PATH_CTL, 5, 0, + &tx_adc_mux5_mux, pahu_codec_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX_E("ADC MUX6", WCD9360_CDC_TX6_TX_PATH_CTL, 5, 0, + &tx_adc_mux6_mux, pahu_codec_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX_E("ADC MUX7", WCD9360_CDC_TX7_TX_PATH_CTL, 5, 0, + &tx_adc_mux7_mux, pahu_codec_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX_E("ADC MUX8", WCD9360_CDC_TX8_TX_PATH_CTL, 5, 0, + &tx_adc_mux8_mux, pahu_codec_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX_E("ADC MUX10", SND_SOC_NOPM, 10, 0, &tx_adc_mux10_mux, + pahu_codec_tx_adc_cfg, SND_SOC_DAPM_POST_PMU), + + SND_SOC_DAPM_MUX_E("ADC MUX11", SND_SOC_NOPM, 11, 0, &tx_adc_mux11_mux, + pahu_codec_tx_adc_cfg, SND_SOC_DAPM_POST_PMU), + + WCD_DAPM_MUX("DMIC MUX0", 0, tx_dmic_mux0), + WCD_DAPM_MUX("DMIC MUX1", 0, tx_dmic_mux1), + WCD_DAPM_MUX("DMIC MUX2", 0, tx_dmic_mux2), + WCD_DAPM_MUX("DMIC MUX3", 0, tx_dmic_mux3), + WCD_DAPM_MUX("DMIC MUX4", 0, tx_dmic_mux4), + WCD_DAPM_MUX("DMIC MUX5", 0, tx_dmic_mux5), + WCD_DAPM_MUX("DMIC MUX6", 0, tx_dmic_mux6), + WCD_DAPM_MUX("DMIC MUX7", 0, tx_dmic_mux7), + WCD_DAPM_MUX("DMIC MUX8", 0, tx_dmic_mux8), + WCD_DAPM_MUX("DMIC MUX10", 0, tx_dmic_mux10), + WCD_DAPM_MUX("DMIC MUX11", 0, tx_dmic_mux11), + + WCD_DAPM_MUX("AMIC MUX0", 0, tx_amic_mux0), + WCD_DAPM_MUX("AMIC MUX1", 0, tx_amic_mux1), + WCD_DAPM_MUX("AMIC MUX2", 0, tx_amic_mux2), + WCD_DAPM_MUX("AMIC MUX3", 0, tx_amic_mux3), + WCD_DAPM_MUX("AMIC MUX4", 0, tx_amic_mux4), + WCD_DAPM_MUX("AMIC MUX5", 0, tx_amic_mux5), + WCD_DAPM_MUX("AMIC MUX6", 0, tx_amic_mux6), + WCD_DAPM_MUX("AMIC MUX7", 0, tx_amic_mux7), + WCD_DAPM_MUX("AMIC MUX8", 0, tx_amic_mux8), + WCD_DAPM_MUX("AMIC MUX10", 0, tx_amic_mux10), + WCD_DAPM_MUX("AMIC MUX11", 0, tx_amic_mux11), + + SND_SOC_DAPM_ADC_E("ADC1", NULL, WCD9360_ANA_AMIC1, 7, 0, + pahu_codec_enable_adc, SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_ADC_E("ADC2", NULL, WCD9360_ANA_AMIC2, 7, 0, + pahu_codec_enable_adc, SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_ADC_E("ADC3", NULL, WCD9360_ANA_AMIC3, 7, 0, + pahu_codec_enable_adc, SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_ADC_E("ADC4", NULL, WCD9360_ANA_AMIC4, 7, 0, + pahu_codec_enable_adc, SND_SOC_DAPM_PRE_PMU), + + WCD_DAPM_MUX("ANC0 FB MUX", 0, anc0_fb), + + WCD_DAPM_MUX("ADC2_IN", 0, tx_adc2_in), + WCD_DAPM_MUX("ADC4_IN", 0, tx_adc4_in), + + SND_SOC_DAPM_INPUT("AMIC1"), + SND_SOC_DAPM_INPUT("AMIC2"), + SND_SOC_DAPM_INPUT("AMIC3"), + SND_SOC_DAPM_INPUT("AMIC4"), + + SND_SOC_DAPM_MICBIAS_E("MIC BIAS1", SND_SOC_NOPM, 0, 0, + pahu_codec_enable_micbias, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MICBIAS_E("MIC BIAS2", SND_SOC_NOPM, 0, 0, + pahu_codec_enable_micbias, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MICBIAS_E("MIC BIAS3", SND_SOC_NOPM, 0, 0, + pahu_codec_enable_micbias, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MICBIAS_E("MIC BIAS4", SND_SOC_NOPM, 0, 0, + pahu_codec_enable_micbias, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MICBIAS_E(DAPM_MICBIAS1_STANDALONE, SND_SOC_NOPM, 0, 0, + pahu_codec_force_enable_micbias, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MICBIAS_E(DAPM_MICBIAS2_STANDALONE, SND_SOC_NOPM, 0, 0, + pahu_codec_force_enable_micbias, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MICBIAS_E(DAPM_MICBIAS3_STANDALONE, SND_SOC_NOPM, 0, 0, + pahu_codec_force_enable_micbias, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MICBIAS_E(DAPM_MICBIAS4_STANDALONE, SND_SOC_NOPM, 0, 0, + pahu_codec_force_enable_micbias, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_AIF_OUT_E("AIF1 CAP", "AIF1 Capture", 0, SND_SOC_NOPM, + AIF1_CAP, 0, pahu_codec_enable_slimtx, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_AIF_OUT_E("AIF2 CAP", "AIF2 Capture", 0, SND_SOC_NOPM, + AIF2_CAP, 0, pahu_codec_enable_slimtx, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_AIF_OUT_E("AIF3 CAP", "AIF3 Capture", 0, SND_SOC_NOPM, + AIF3_CAP, 0, pahu_codec_enable_slimtx, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + WCD_DAPM_MUX("I2S TX1_0 MUX", 0, i2s_tx1_0_inp), + WCD_DAPM_MUX("I2S TX1_1 MUX", 0, i2s_tx1_1_inp), + SND_SOC_DAPM_MIXER("I2S TX1 MIXER", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_AIF_OUT_E("I2S1 CAP", "I2S1 Capture", 0, + SND_SOC_NOPM, I2S1_CAP, 0, pahu_i2s_aif_tx_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MIXER("AIF1_CAP Mixer", SND_SOC_NOPM, AIF1_CAP, 0, + aif1_cap_mixer, ARRAY_SIZE(aif1_cap_mixer)), + SND_SOC_DAPM_MIXER("AIF2_CAP Mixer", SND_SOC_NOPM, AIF2_CAP, 0, + aif2_cap_mixer, ARRAY_SIZE(aif2_cap_mixer)), + SND_SOC_DAPM_MIXER("AIF3_CAP Mixer", SND_SOC_NOPM, AIF3_CAP, 0, + aif3_cap_mixer, ARRAY_SIZE(aif3_cap_mixer)), + SND_SOC_DAPM_MIXER("AIF4_MAD Mixer", SND_SOC_NOPM, AIF4_MAD_TX, 0, + aif4_mad_mixer, ARRAY_SIZE(aif4_mad_mixer)), + + SND_SOC_DAPM_AIF_OUT_E("AIF4 VI", "VIfeed", 0, SND_SOC_NOPM, + AIF4_VIFEED, 0, pahu_codec_enable_slimvi_feedback, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_AIF_OUT("AIF4 MAD", "AIF4 MAD TX", 0, + SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_MIXER("AIF4_VI Mixer", SND_SOC_NOPM, AIF4_VIFEED, 0, + aif4_vi_mixer, ARRAY_SIZE(aif4_vi_mixer)), + SND_SOC_DAPM_INPUT("VIINPUT"), + + SND_SOC_DAPM_MIXER("SLIM TX0", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM TX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM TX2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM TX3", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM TX4", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM TX5", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM TX6", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM TX7", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM TX8", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM TX9", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM TX10", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM TX11", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM TX13", SND_SOC_NOPM, 0, 0, NULL, 0), + + /* Digital Mic Inputs */ + SND_SOC_DAPM_ADC_E("DMIC0", NULL, SND_SOC_NOPM, 0, 0, + pahu_codec_enable_dmic, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("DMIC1", NULL, SND_SOC_NOPM, 1, 0, + pahu_codec_enable_dmic, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("DMIC2", NULL, SND_SOC_NOPM, 2, 0, + pahu_codec_enable_dmic, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("DMIC3", NULL, SND_SOC_NOPM, 3, 0, + pahu_codec_enable_dmic, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("DMIC4", NULL, SND_SOC_NOPM, 4, 0, + pahu_codec_enable_dmic, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("DMIC5", NULL, SND_SOC_NOPM, 5, 0, + pahu_codec_enable_dmic, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("DMIC6", NULL, SND_SOC_NOPM, 6, 0, + pahu_codec_enable_dmic, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("DMIC7", NULL, SND_SOC_NOPM, 7, 0, + pahu_codec_enable_dmic, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + WCD_DAPM_MUX("IIR0 INP0 MUX", 0, iir0_inp0), + WCD_DAPM_MUX("IIR0 INP1 MUX", 0, iir0_inp1), + WCD_DAPM_MUX("IIR0 INP2 MUX", 0, iir0_inp2), + WCD_DAPM_MUX("IIR0 INP3 MUX", 0, iir0_inp3), + + SND_SOC_DAPM_MIXER_E("IIR0", WCD9360_CDC_SIDETONE_IIR0_IIR_PATH_CTL, + 4, 0, NULL, 0, pahu_codec_set_iir_gain, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_MIXER("SRC0", WCD9360_CDC_SIDETONE_SRC0_ST_SRC_PATH_CTL, + 4, 0, NULL, 0), + + WCD_DAPM_MUX("RX MIX TX0 MUX", 0, rx_mix_tx0), + WCD_DAPM_MUX("RX MIX TX1 MUX", 0, rx_mix_tx1), + WCD_DAPM_MUX("RX MIX TX2 MUX", 0, rx_mix_tx2), + WCD_DAPM_MUX("RX MIX TX3 MUX", 0, rx_mix_tx3), + WCD_DAPM_MUX("RX MIX TX4 MUX", 0, rx_mix_tx4), + WCD_DAPM_MUX("RX MIX TX5 MUX", 0, rx_mix_tx5), + WCD_DAPM_MUX("RX MIX TX6 MUX", 0, rx_mix_tx6), + WCD_DAPM_MUX("RX MIX TX7 MUX", 0, rx_mix_tx7), + WCD_DAPM_MUX("RX MIX TX8 MUX", 0, rx_mix_tx8), + WCD_DAPM_MUX("RX INT0 DEM MUX", 0, rx_int0_dem_inp), + WCD_DAPM_MUX("RX INT9 DEM MUX", 0, rx_int9_dem_inp), + + SND_SOC_DAPM_MUX_E("RX INT0_1 INTERP", SND_SOC_NOPM, INTERP_EAR, 0, + &rx_int0_1_interp_mux, pahu_codec_enable_main_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT7_1 INTERP", SND_SOC_NOPM, INTERP_SPKR1, 0, + &rx_int7_1_interp_mux, pahu_codec_enable_main_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT8_1 INTERP", SND_SOC_NOPM, INTERP_SPKR2, 0, + &rx_int8_1_interp_mux, pahu_codec_enable_main_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT9_1 INTERP", SND_SOC_NOPM, INTERP_AUX, 0, + &rx_int9_1_interp_mux, pahu_codec_enable_main_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + + WCD_DAPM_MUX("RX INT0_2 INTERP", 0, rx_int0_2_interp), + WCD_DAPM_MUX("RX INT7_2 INTERP", 0, rx_int7_2_interp), + WCD_DAPM_MUX("RX INT8_2 INTERP", 0, rx_int8_2_interp), + WCD_DAPM_MUX("RX INT9_2 INTERP", 0, rx_int9_2_interp), + + SND_SOC_DAPM_SWITCH("ADC US MUX0", WCD9360_CDC_TX0_TX_PATH_192_CTL, 0, + 0, &adc_us_mux0_switch), + SND_SOC_DAPM_SWITCH("ADC US MUX1", WCD9360_CDC_TX1_TX_PATH_192_CTL, 0, + 0, &adc_us_mux1_switch), + SND_SOC_DAPM_SWITCH("ADC US MUX2", WCD9360_CDC_TX2_TX_PATH_192_CTL, 0, + 0, &adc_us_mux2_switch), + SND_SOC_DAPM_SWITCH("ADC US MUX3", WCD9360_CDC_TX3_TX_PATH_192_CTL, 0, + 0, &adc_us_mux3_switch), + SND_SOC_DAPM_SWITCH("ADC US MUX4", WCD9360_CDC_TX4_TX_PATH_192_CTL, 0, + 0, &adc_us_mux4_switch), + SND_SOC_DAPM_SWITCH("ADC US MUX5", WCD9360_CDC_TX5_TX_PATH_192_CTL, 0, + 0, &adc_us_mux5_switch), + SND_SOC_DAPM_SWITCH("ADC US MUX6", WCD9360_CDC_TX6_TX_PATH_192_CTL, 0, + 0, &adc_us_mux6_switch), + SND_SOC_DAPM_SWITCH("ADC US MUX7", WCD9360_CDC_TX7_TX_PATH_192_CTL, 0, + 0, &adc_us_mux7_switch), + SND_SOC_DAPM_SWITCH("ADC US MUX8", WCD9360_CDC_TX8_TX_PATH_192_CTL, 0, + 0, &adc_us_mux8_switch), + + /* MAD related widgets */ + SND_SOC_DAPM_INPUT("MAD_CPE_INPUT"), + SND_SOC_DAPM_INPUT("MADINPUT"), + + WCD_DAPM_MUX("MAD_SEL MUX", 0, mad_sel), + WCD_DAPM_MUX("MAD_INP MUX", 0, mad_inp_mux), + + SND_SOC_DAPM_SWITCH_E("MAD_BROADCAST", SND_SOC_NOPM, 0, 0, + &mad_brdcst_switch, pahu_codec_ape_enable_mad, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), + + SND_SOC_DAPM_SWITCH_E("MAD_CPE1", SND_SOC_NOPM, 0, 0, + &mad_cpe1_switch, pahu_codec_cpe_mad_ctl, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_SWITCH_E("MAD_CPE2", SND_SOC_NOPM, 0, 0, + &mad_cpe2_switch, pahu_codec_cpe_mad_ctl, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), + + SND_SOC_DAPM_OUTPUT("MAD_CPE_OUT1"), + SND_SOC_DAPM_OUTPUT("MAD_CPE_OUT2"), + + SND_SOC_DAPM_DAC_E("RX INT0 DAC", NULL, SND_SOC_NOPM, + 0, 0, pahu_codec_ear_dac_event, SND_SOC_DAPM_PRE_PMU), + + SND_SOC_DAPM_PGA_E("EAR PA", WCD9360_ANA_EAR, 7, 0, NULL, 0, + pahu_codec_enable_ear_pa, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("ANC EAR PA", WCD9360_ANA_EAR, 7, 0, NULL, 0, + pahu_codec_enable_ear_pa, SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("ANC SPK1 PA", SND_SOC_NOPM, 0, 0, NULL, 0, + pahu_codec_enable_spkr_anc, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_OUTPUT("EAR"), + SND_SOC_DAPM_OUTPUT("SPK1 OUT"), + SND_SOC_DAPM_OUTPUT("SPK2 OUT"), + SND_SOC_DAPM_OUTPUT("ANC EAR"), + + SND_SOC_DAPM_SWITCH("ANC OUT EAR Enable", SND_SOC_NOPM, 0, 0, + &anc_ear_switch), + SND_SOC_DAPM_SWITCH("ANC OUT EAR SPKR Enable", SND_SOC_NOPM, 0, 0, + &anc_ear_spkr_switch), + SND_SOC_DAPM_SWITCH("ANC SPKR PA Enable", SND_SOC_NOPM, 0, 0, + &anc_spkr_pa_switch), + + SND_SOC_DAPM_DAC("RX INT9 DAC", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_PGA_E("AUX PA", WCD9360_AUX_ANA_EAR, 7, 0, NULL, 0, + pahu_codec_enable_aux_pa, SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_OUTPUT("AUX"), + + + SND_SOC_DAPM_SUPPLY("LDO_RXTX", SND_SOC_NOPM, 0, 0, + pahu_codec_enable_ldo_rxtx, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_SUPPLY("RX INT7 NATIVE SUPPLY", SND_SOC_NOPM, + INTERP_SPKR1, 0, pahu_enable_native_supply, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_SUPPLY("RX INT8 NATIVE SUPPLY", SND_SOC_NOPM, + INTERP_SPKR2, 0, pahu_enable_native_supply, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), + + WCD_DAPM_MUX("RX INT7_2 NATIVE MUX", 0, int7_2_native), + WCD_DAPM_MUX("RX INT8_2 NATIVE MUX", 0, int8_2_native), + + SND_SOC_DAPM_MUX_E("ASRC2 MUX", SND_SOC_NOPM, ASRC2, 0, + &asrc2_mux, pahu_codec_enable_asrc_resampler, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("ASRC3 MUX", SND_SOC_NOPM, ASRC3, 0, + &asrc3_mux, pahu_codec_enable_asrc_resampler, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + /* WDMA3 widgets */ + WCD_DAPM_MUX("WDMA3 PORT0 MUX", 0, wdma3_port0), + WCD_DAPM_MUX("WDMA3 PORT1 MUX", 1, wdma3_port1), + WCD_DAPM_MUX("WDMA3 PORT2 MUX", 2, wdma3_port2), + WCD_DAPM_MUX("WDMA3 PORT3 MUX", 3, wdma3_port3), + WCD_DAPM_MUX("WDMA3 PORT4 MUX", 4, wdma3_port4), + WCD_DAPM_MUX("WDMA3 PORT5 MUX", 5, wdma3_port5), + WCD_DAPM_MUX("WDMA3 PORT6 MUX", 6, wdma3_port6), + + WCD_DAPM_MUX("WDMA3 CH0 MUX", 0, wdma3_ch0), + WCD_DAPM_MUX("WDMA3 CH1 MUX", 4, wdma3_ch1), + WCD_DAPM_MUX("WDMA3 CH2 MUX", 0, wdma3_ch2), + WCD_DAPM_MUX("WDMA3 CH3 MUX", 4, wdma3_ch3), + + SND_SOC_DAPM_MIXER("WDMA3_CH_MIXER", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_SWITCH_E("WDMA3_ON_OFF", SND_SOC_NOPM, 0, 0, + &wdma3_onoff_switch, pahu_codec_wdma3_ctl, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_OUTPUT("WDMA3_OUT"), +}; + +static int pahu_get_channel_map(struct snd_soc_dai *dai, + unsigned int *tx_num, unsigned int *tx_slot, + unsigned int *rx_num, unsigned int *rx_slot) +{ + struct pahu_priv *pahu = snd_soc_codec_get_drvdata(dai->codec); + u32 i = 0; + struct wcd9xxx_ch *ch; + int ret = 0; + + switch (dai->id) { + case AIF1_PB: + case AIF2_PB: + case AIF3_PB: + case AIF4_PB: + if (!rx_slot || !rx_num) { + dev_err(pahu->dev, "%s: Invalid rx_slot 0x%pK or rx_num 0x%pK\n", + __func__, rx_slot, rx_num); + ret = -EINVAL; + break; + } + list_for_each_entry(ch, &pahu->dai[dai->id].wcd9xxx_ch_list, + list) { + dev_dbg(pahu->dev, "%s: slot_num %u ch->ch_num %d\n", + __func__, i, ch->ch_num); + rx_slot[i++] = ch->ch_num; + } + *rx_num = i; + dev_dbg(pahu->dev, "%s: dai_name = %s dai_id = %x rx_num = %d\n", + __func__, dai->name, dai->id, i); + if (*rx_num == 0) { + dev_err(pahu->dev, "%s: Channel list empty for dai_name = %s dai_id = %x\n", + __func__, dai->name, dai->id); + ret = -EINVAL; + } + break; + case AIF1_CAP: + case AIF2_CAP: + case AIF3_CAP: + case AIF4_MAD_TX: + case AIF4_VIFEED: + if (!tx_slot || !tx_num) { + dev_err(pahu->dev, "%s: Invalid tx_slot 0x%pK or tx_num 0x%pK\n", + __func__, tx_slot, tx_num); + ret = -EINVAL; + break; + } + list_for_each_entry(ch, &pahu->dai[dai->id].wcd9xxx_ch_list, + list) { + dev_dbg(pahu->dev, "%s: slot_num %u ch->ch_num %d\n", + __func__, i, ch->ch_num); + tx_slot[i++] = ch->ch_num; + } + *tx_num = i; + dev_dbg(pahu->dev, "%s: dai_name = %s dai_id = %x tx_num = %d\n", + __func__, dai->name, dai->id, i); + if (*tx_num == 0) { + dev_err(pahu->dev, "%s: Channel list empty for dai_name = %s dai_id = %x\n", + __func__, dai->name, dai->id); + ret = -EINVAL; + } + break; + default: + dev_err(pahu->dev, "%s: Invalid DAI ID %x\n", + __func__, dai->id); + ret = -EINVAL; + break; + } + + return ret; +} + +static int pahu_set_channel_map(struct snd_soc_dai *dai, + unsigned int tx_num, unsigned int *tx_slot, + unsigned int rx_num, unsigned int *rx_slot) +{ + struct pahu_priv *pahu; + struct wcd9xxx *core; + struct wcd9xxx_codec_dai_data *dai_data = NULL; + + pahu = snd_soc_codec_get_drvdata(dai->codec); + core = dev_get_drvdata(dai->codec->dev->parent); + + if (!tx_slot || !rx_slot) { + dev_err(pahu->dev, "%s: Invalid tx_slot 0x%pK, rx_slot 0x%pK\n", + __func__, tx_slot, rx_slot); + return -EINVAL; + } + dev_dbg(pahu->dev, "%s(): dai_name = %s DAI-ID %x tx_ch %d rx_ch %d\n", + __func__, dai->name, dai->id, tx_num, rx_num); + + wcd9xxx_init_slimslave(core, core->slim->laddr, + tx_num, tx_slot, rx_num, rx_slot); + /* Reserve TX13 for MAD data channel */ + dai_data = &pahu->dai[AIF4_MAD_TX]; + if (dai_data) + list_add_tail(&core->tx_chs[WCD9360_TX13].list, + &dai_data->wcd9xxx_ch_list); + + return 0; +} + +static int pahu_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + pr_debug("%s(): substream = %s stream = %d\n", __func__, + substream->name, substream->stream); + + return 0; +} + +static void pahu_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + pr_debug("%s(): substream = %s stream = %d\n", __func__, + substream->name, substream->stream); +} + +static int pahu_set_decimator_rate(struct snd_soc_dai *dai, + u32 sample_rate) +{ + struct snd_soc_codec *codec = dai->codec; + struct wcd9xxx_ch *ch; + struct pahu_priv *pahu = snd_soc_codec_get_drvdata(codec); + u32 tx_port = 0, tx_fs_rate = 0; + u8 shift = 0, shift_val = 0, tx_mux_sel = 0; + int decimator = -1; + u16 tx_port_reg = 0, tx_fs_reg = 0; + + switch (sample_rate) { + case 8000: + tx_fs_rate = 0; + break; + case 16000: + tx_fs_rate = 1; + break; + case 32000: + tx_fs_rate = 3; + break; + case 48000: + tx_fs_rate = 4; + break; + case 96000: + tx_fs_rate = 5; + break; + case 192000: + tx_fs_rate = 6; + break; + default: + dev_err(pahu->dev, "%s: Invalid TX sample rate: %d\n", + __func__, sample_rate); + return -EINVAL; + + }; + + list_for_each_entry(ch, &pahu->dai[dai->id].wcd9xxx_ch_list, list) { + tx_port = ch->port; + dev_dbg(codec->dev, "%s: dai->id = %d, tx_port = %d", + __func__, dai->id, tx_port); + + if ((tx_port < 0) || (tx_port == 12) || (tx_port >= 14)) { + dev_err(codec->dev, "%s: Invalid SLIM TX%u port. DAI ID: %d\n", + __func__, tx_port, dai->id); + return -EINVAL; + } + /* Find the SB TX MUX input - which decimator is connected */ + if (tx_port < 4) { + tx_port_reg = WCD9360_CDC_IF_ROUTER_TX_MUX_CFG0; + shift = (tx_port << 1); + shift_val = 0x03; + } else if ((tx_port >= 4) && (tx_port < 8)) { + tx_port_reg = WCD9360_CDC_IF_ROUTER_TX_MUX_CFG1; + shift = ((tx_port - 4) << 1); + shift_val = 0x03; + } else if ((tx_port >= 8) && (tx_port < 11)) { + tx_port_reg = WCD9360_CDC_IF_ROUTER_TX_MUX_CFG2; + shift = ((tx_port - 8) << 1); + shift_val = 0x03; + } else if (tx_port == 11) { + tx_port_reg = WCD9360_CDC_IF_ROUTER_TX_MUX_CFG3; + shift = 0; + shift_val = 0x0F; + } else if (tx_port == 13) { + tx_port_reg = WCD9360_CDC_IF_ROUTER_TX_MUX_CFG3; + shift = 4; + shift_val = 0x03; + } + tx_mux_sel = snd_soc_read(codec, tx_port_reg) & + (shift_val << shift); + tx_mux_sel = tx_mux_sel >> shift; + + if (tx_port <= 8) { + if ((tx_mux_sel == 0x2) || (tx_mux_sel == 0x3)) + decimator = tx_port; + } else if (tx_port <= 10) { + if ((tx_mux_sel == 0x1) || (tx_mux_sel == 0x2)) + decimator = ((tx_port == 9) ? 7 : 6); + } else if (tx_port == 11) { + if ((tx_mux_sel >= 1) && (tx_mux_sel < 7)) + decimator = tx_mux_sel - 1; + } else if (tx_port == 13) { + if ((tx_mux_sel == 0x1) || (tx_mux_sel == 0x2)) + decimator = 5; + } + + if (decimator >= 0) { + tx_fs_reg = WCD9360_CDC_TX0_TX_PATH_CTL + + 16 * decimator; + dev_dbg(codec->dev, "%s: set DEC%u (-> SLIM_TX%u) rate to %u\n", + __func__, decimator, tx_port, sample_rate); + snd_soc_update_bits(codec, tx_fs_reg, 0x0F, tx_fs_rate); + } else if ((tx_port <= 8) && (tx_mux_sel == 0x01)) { + /* Check if the TX Mux input is RX MIX TXn */ + dev_dbg(codec->dev, "%s: RX_MIX_TX%u going to CDC_IF TX%u\n", + __func__, tx_port, tx_port); + } else { + dev_err(codec->dev, "%s: ERROR: Invalid decimator: %d\n", + __func__, decimator); + return -EINVAL; + } + } + return 0; +} + +static int pahu_set_mix_interpolator_rate(struct snd_soc_dai *dai, + u8 rate_reg_val, + u32 sample_rate) +{ + u8 int_2_inp; + u32 j; + u16 int_mux_cfg1, int_fs_reg; + u8 int_mux_cfg1_val; + struct snd_soc_codec *codec = dai->codec; + struct wcd9xxx_ch *ch; + struct pahu_priv *pahu = snd_soc_codec_get_drvdata(codec); + + list_for_each_entry(ch, &pahu->dai[dai->id].wcd9xxx_ch_list, list) { + int_2_inp = INTn_2_INP_SEL_RX0 + ch->port - + WCD9360_RX_PORT_START_NUMBER; + if ((int_2_inp < INTn_2_INP_SEL_RX0) || + (int_2_inp > INTn_2_INP_SEL_RX7)) { + dev_err(codec->dev, "%s: Invalid RX%u port, Dai ID is %d\n", + __func__, + (ch->port - WCD9360_RX_PORT_START_NUMBER), + dai->id); + return -EINVAL; + } + + for (j = 0; j < WCD9360_NUM_INTERPOLATORS; j++) { + if (j == INTERP_EAR) { + int_mux_cfg1 = + WCD9360_CDC_RX_INP_MUX_RX_INT0_CFG1; + int_fs_reg = WCD9360_CDC_RX0_RX_PATH_MIX_CTL; + } else if (j == INTERP_SPKR1) { + int_mux_cfg1 = + WCD9360_CDC_RX_INP_MUX_RX_INT7_CFG1; + int_fs_reg = WCD9360_CDC_RX7_RX_PATH_MIX_CTL; + } else if (j == INTERP_SPKR2) { + int_mux_cfg1 = + WCD9360_CDC_RX_INP_MUX_RX_INT8_CFG1; + int_fs_reg = WCD9360_CDC_RX8_RX_PATH_MIX_CTL; + } else if (j == INTERP_AUX) { + int_mux_cfg1 = + WCD9360_CDC_RX_INP_MUX_RX_INT9_CFG1; + int_fs_reg = WCD9360_CDC_RX9_RX_PATH_MIX_CTL; + } else { + continue; + } + + int_mux_cfg1_val = snd_soc_read(codec, int_mux_cfg1) & + 0x0F; + if (int_mux_cfg1_val == int_2_inp) { + /* + * Ear mix path supports only 48, 96, 192, + * 384KHz only + */ + if ((j == INTERP_EAR || j == INTERP_AUX) && + (rate_reg_val < 0x4 || rate_reg_val > 0x7)) { + dev_err_ratelimited(codec->dev, + "%s: Invalid rate for AIF_PB DAI(%d)\n", + __func__, dai->id); + return -EINVAL; + } + + dev_dbg(codec->dev, "%s: AIF_PB DAI(%d) connected to INT%u_2\n", + __func__, dai->id, j); + dev_dbg(codec->dev, "%s: set INT%u_2 sample rate to %u\n", + __func__, j, sample_rate); + snd_soc_update_bits(codec, int_fs_reg, 0x0F, + rate_reg_val); + } + } + } + return 0; +} + +static int pahu_set_prim_interpolator_rate(struct snd_soc_dai *dai, + u8 rate_reg_val, + u32 sample_rate) +{ + u8 int_1_mix1_inp; + u32 j; + u16 int_mux_cfg0, int_mux_cfg1; + u16 int_fs_reg; + u8 int_mux_cfg0_val, int_mux_cfg1_val; + u8 inp0_sel, inp1_sel, inp2_sel; + struct snd_soc_codec *codec = dai->codec; + struct wcd9xxx_ch *ch; + struct pahu_priv *pahu = snd_soc_codec_get_drvdata(codec); + + list_for_each_entry(ch, &pahu->dai[dai->id].wcd9xxx_ch_list, list) { + int_1_mix1_inp = INTn_1_INP_SEL_RX0 + ch->port - + WCD9360_RX_PORT_START_NUMBER; + if ((int_1_mix1_inp < INTn_1_INP_SEL_RX0) || + (int_1_mix1_inp > INTn_1_INP_SEL_RX7)) { + dev_err(codec->dev, "%s: Invalid RX%u port, Dai ID is %d\n", + __func__, + (ch->port - WCD9360_RX_PORT_START_NUMBER), + dai->id); + return -EINVAL; + } + + /* + * Loop through all interpolator MUX inputs and find out + * to which interpolator input, the slim rx port + * is connected + */ + for (j = 0; j < WCD9360_NUM_INTERPOLATORS; j++) { + if (j == INTERP_EAR) { + int_mux_cfg0 = + WCD9360_CDC_RX_INP_MUX_RX_INT0_CFG0; + int_fs_reg = WCD9360_CDC_RX0_RX_PATH_CTL; + } else if (j == INTERP_SPKR1) { + int_mux_cfg0 = + WCD9360_CDC_RX_INP_MUX_RX_INT7_CFG0; + int_fs_reg = WCD9360_CDC_RX7_RX_PATH_CTL; + } else if (j == INTERP_SPKR2) { + int_mux_cfg0 = + WCD9360_CDC_RX_INP_MUX_RX_INT8_CFG0; + int_fs_reg = WCD9360_CDC_RX8_RX_PATH_CTL; + } else if (j == INTERP_AUX) { + int_mux_cfg0 = + WCD9360_CDC_RX_INP_MUX_RX_INT9_CFG0; + int_fs_reg = WCD9360_CDC_RX9_RX_PATH_CTL; + } else { + continue; + } + int_mux_cfg1 = int_mux_cfg0 + 1; + + int_mux_cfg0_val = snd_soc_read(codec, int_mux_cfg0); + int_mux_cfg1_val = snd_soc_read(codec, int_mux_cfg1); + inp0_sel = int_mux_cfg0_val & 0x0F; + inp1_sel = (int_mux_cfg0_val >> 4) & 0x0F; + inp2_sel = (int_mux_cfg1_val >> 4) & 0x0F; + if ((inp0_sel == int_1_mix1_inp) || + (inp1_sel == int_1_mix1_inp) || + (inp2_sel == int_1_mix1_inp)) { + /* + * Primary path does not support + * native sample rates + */ + if (rate_reg_val > 0x7) { + dev_err_ratelimited(codec->dev, + "%s: Invalid rate for AIF_PB DAI(%d)\n", + __func__, dai->id); + return -EINVAL; + } + dev_dbg(codec->dev, + "%s: AIF_PB DAI(%d) connected to INT%u_1\n", + __func__, dai->id, j); + dev_dbg(codec->dev, + "%s: set INT%u_1 sample rate to %u\n", + __func__, j, sample_rate); + snd_soc_update_bits(codec, int_fs_reg, 0x0F, + rate_reg_val); + } + int_mux_cfg0 += 2; + } + } + + return 0; +} + + +static int pahu_set_interpolator_rate(struct snd_soc_dai *dai, + u32 sample_rate) +{ + struct snd_soc_codec *codec = dai->codec; + int rate_val = 0; + int i, ret; + + for (i = 0; i < ARRAY_SIZE(sr_val_tbl); i++) { + if (sample_rate == sr_val_tbl[i].sample_rate) { + rate_val = sr_val_tbl[i].rate_val; + break; + } + } + if ((i == ARRAY_SIZE(sr_val_tbl)) || (rate_val < 0)) { + dev_err(codec->dev, "%s: Unsupported sample rate: %d\n", + __func__, sample_rate); + return -EINVAL; + } + + ret = pahu_set_prim_interpolator_rate(dai, (u8)rate_val, sample_rate); + if (ret) + return ret; + ret = pahu_set_mix_interpolator_rate(dai, (u8)rate_val, sample_rate); + if (ret) + return ret; + + return ret; +} + +static int pahu_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + pr_debug("%s(): substream = %s stream = %d\n", __func__, + substream->name, substream->stream); + return 0; +} + +static int pahu_vi_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct pahu_priv *pahu = snd_soc_codec_get_drvdata(dai->codec); + + dev_dbg(pahu->dev, "%s: dai_name = %s DAI-ID %x rate %d num_ch %d\n", + __func__, dai->name, dai->id, params_rate(params), + params_channels(params)); + + pahu->dai[dai->id].rate = params_rate(params); + pahu->dai[dai->id].bit_width = 32; + + return 0; +} + +static int pahu_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct pahu_priv *pahu = snd_soc_codec_get_drvdata(dai->codec); + int ret = 0; + + dev_dbg(pahu->dev, "%s: dai_name = %s DAI-ID %x rate %d num_ch %d\n", + __func__, dai->name, dai->id, params_rate(params), + params_channels(params)); + + switch (substream->stream) { + case SNDRV_PCM_STREAM_PLAYBACK: + ret = pahu_set_interpolator_rate(dai, params_rate(params)); + if (ret) { + dev_err(pahu->dev, "%s: cannot set sample rate: %u\n", + __func__, params_rate(params)); + return ret; + } + switch (params_width(params)) { + case 16: + pahu->dai[dai->id].bit_width = 16; + break; + case 24: + pahu->dai[dai->id].bit_width = 24; + break; + case 32: + pahu->dai[dai->id].bit_width = 32; + break; + default: + return -EINVAL; + } + pahu->dai[dai->id].rate = params_rate(params); + break; + case SNDRV_PCM_STREAM_CAPTURE: + if (dai->id != AIF4_MAD_TX) + ret = pahu_set_decimator_rate(dai, + params_rate(params)); + if (ret) { + dev_err(pahu->dev, "%s: cannot set TX Decimator rate: %d\n", + __func__, ret); + return ret; + } + switch (params_width(params)) { + case 16: + pahu->dai[dai->id].bit_width = 16; + break; + case 24: + pahu->dai[dai->id].bit_width = 24; + break; + default: + dev_err(pahu->dev, "%s: Invalid format 0x%x\n", + __func__, params_width(params)); + return -EINVAL; + }; + pahu->dai[dai->id].rate = params_rate(params); + break; + default: + dev_err(pahu->dev, "%s: Invalid stream type %d\n", __func__, + substream->stream); + return -EINVAL; + }; + + return 0; +} + +static int pahu_i2s_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct pahu_priv *pahu = snd_soc_codec_get_drvdata(dai->codec); + + dev_dbg(dai->dev, "%s: dai_name = %s DAI-ID %x rate %d num_ch %d\n", + __func__, dai->name, dai->id, params_rate(params), + params_channels(params)); + + pahu->dai[dai->id].rate = params_rate(params); + pahu->dai[dai->id].bit_width = params_width(params); + + return 0; +} + +static struct snd_soc_dai_ops pahu_dai_ops = { + .startup = pahu_startup, + .shutdown = pahu_shutdown, + .hw_params = pahu_hw_params, + .prepare = pahu_prepare, + .set_channel_map = pahu_set_channel_map, + .get_channel_map = pahu_get_channel_map, +}; + +static struct snd_soc_dai_ops pahu_vi_dai_ops = { + .hw_params = pahu_vi_hw_params, + .set_channel_map = pahu_set_channel_map, + .get_channel_map = pahu_get_channel_map, +}; + +static struct snd_soc_dai_ops pahu_i2s_dai_ops = { + .hw_params = pahu_i2s_hw_params, +}; + +static struct snd_soc_dai_driver pahu_dai[] = { + { + .name = "pahu_rx1", + .id = AIF1_PB, + .playback = { + .stream_name = "AIF1 Playback", + .rates = WCD9360_RATES_MASK | WCD9360_FRAC_RATES_MASK, + .formats = WCD9360_FORMATS_S16_S24_S32_LE, + .rate_min = 8000, + .rate_max = 384000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &pahu_dai_ops, + }, + { + .name = "pahu_tx1", + .id = AIF1_CAP, + .capture = { + .stream_name = "AIF1 Capture", + .rates = WCD9360_RATES_MASK, + .formats = WCD9360_FORMATS_S16_S24_LE, + .rate_min = 8000, + .rate_max = 192000, + .channels_min = 1, + .channels_max = 8, + }, + .ops = &pahu_dai_ops, + }, + { + .name = "pahu_rx2", + .id = AIF2_PB, + .playback = { + .stream_name = "AIF2 Playback", + .rates = WCD9360_RATES_MASK | WCD9360_FRAC_RATES_MASK, + .formats = WCD9360_FORMATS_S16_S24_S32_LE, + .rate_min = 8000, + .rate_max = 384000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &pahu_dai_ops, + }, + { + .name = "pahu_tx2", + .id = AIF2_CAP, + .capture = { + .stream_name = "AIF2 Capture", + .rates = WCD9360_RATES_MASK, + .formats = WCD9360_FORMATS_S16_S24_LE, + .rate_min = 8000, + .rate_max = 192000, + .channels_min = 1, + .channels_max = 8, + }, + .ops = &pahu_dai_ops, + }, + { + .name = "pahu_rx3", + .id = AIF3_PB, + .playback = { + .stream_name = "AIF3 Playback", + .rates = WCD9360_RATES_MASK | WCD9360_FRAC_RATES_MASK, + .formats = WCD9360_FORMATS_S16_S24_S32_LE, + .rate_min = 8000, + .rate_max = 384000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &pahu_dai_ops, + }, + { + .name = "pahu_tx3", + .id = AIF3_CAP, + .capture = { + .stream_name = "AIF3 Capture", + .rates = WCD9360_RATES_MASK, + .formats = WCD9360_FORMATS_S16_S24_LE, + .rate_min = 8000, + .rate_max = 192000, + .channels_min = 1, + .channels_max = 8, + }, + .ops = &pahu_dai_ops, + }, + { + .name = "pahu_rx4", + .id = AIF4_PB, + .playback = { + .stream_name = "AIF4 Playback", + .rates = WCD9360_RATES_MASK | WCD9360_FRAC_RATES_MASK, + .formats = WCD9360_FORMATS_S16_S24_S32_LE, + .rate_min = 8000, + .rate_max = 384000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &pahu_dai_ops, + }, + { + .name = "pahu_vifeedback", + .id = AIF4_VIFEED, + .capture = { + .stream_name = "VIfeed", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_48000, + .formats = WCD9360_FORMATS_S16_S24_S32_LE, + .rate_min = 8000, + .rate_max = 48000, + .channels_min = 1, + .channels_max = 4, + }, + .ops = &pahu_vi_dai_ops, + }, + { + .name = "pahu_mad1", + .id = AIF4_MAD_TX, + .capture = { + .stream_name = "AIF4 MAD TX", + .rates = SNDRV_PCM_RATE_16000, + .formats = WCD9360_FORMATS_S16_LE, + .rate_min = 16000, + .rate_max = 16000, + .channels_min = 1, + .channels_max = 1, + }, + .ops = &pahu_dai_ops, + }, + { + .name = "pahu_i2s1_rx", + .id = I2S1_PB, + .playback = { + .stream_name = "I2S1 Playback", + .rates = WCD9360_RATES_MASK | WCD9360_FRAC_RATES_MASK, + .formats = WCD9360_FORMATS_S16_S24_S32_LE, + .rate_min = 8000, + .rate_max = 384000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &pahu_i2s_dai_ops, + }, + { + .name = "pahu_i2s1_tx", + .id = I2S1_CAP, + .capture = { + .stream_name = "I2S1 Capture", + .rates = WCD9360_RATES_MASK | WCD9360_FRAC_RATES_MASK, + .formats = WCD9360_FORMATS_S16_S24_S32_LE, + .rate_min = 8000, + .rate_max = 192000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &pahu_i2s_dai_ops, + }, +}; + +static void pahu_codec_power_gate_digital_core(struct pahu_priv *pahu) +{ + mutex_lock(&pahu->power_lock); + dev_dbg(pahu->dev, "%s: Entering power gating function, %d\n", + __func__, pahu->power_active_ref); + + if (pahu->power_active_ref > 0) + goto exit; + + wcd9xxx_set_power_state(pahu->wcd9xxx, + WCD_REGION_POWER_COLLAPSE_BEGIN, + WCD9XXX_DIG_CORE_REGION_1); + regmap_update_bits(pahu->wcd9xxx->regmap, + WCD9360_CODEC_RPM_PWR_CDC_DIG_HM_CTL, 0x04, 0x04); + regmap_update_bits(pahu->wcd9xxx->regmap, + WCD9360_CODEC_RPM_PWR_CDC_DIG_HM_CTL, 0x01, 0x00); + regmap_update_bits(pahu->wcd9xxx->regmap, + WCD9360_CODEC_RPM_PWR_CDC_DIG_HM_CTL, 0x02, 0x00); + wcd9xxx_set_power_state(pahu->wcd9xxx, WCD_REGION_POWER_DOWN, + WCD9XXX_DIG_CORE_REGION_1); +exit: + dev_dbg(pahu->dev, "%s: Exiting power gating function, %d\n", + __func__, pahu->power_active_ref); + mutex_unlock(&pahu->power_lock); +} + +static void pahu_codec_power_gate_work(struct work_struct *work) +{ + struct pahu_priv *pahu; + struct delayed_work *dwork; + + dwork = to_delayed_work(work); + pahu = container_of(dwork, struct pahu_priv, power_gate_work); + + pahu_codec_power_gate_digital_core(pahu); +} + +/* called under power_lock acquisition */ +static int pahu_dig_core_remove_power_collapse(struct pahu_priv *pahu) +{ + regmap_write(pahu->wcd9xxx->regmap, + WCD9360_CODEC_RPM_PWR_CDC_DIG_HM_CTL, 0x05); + regmap_write(pahu->wcd9xxx->regmap, + WCD9360_CODEC_RPM_PWR_CDC_DIG_HM_CTL, 0x07); + regmap_update_bits(pahu->wcd9xxx->regmap, + WCD9360_CODEC_RPM_RST_CTL, 0x02, 0x00); + regmap_update_bits(pahu->wcd9xxx->regmap, + WCD9360_CODEC_RPM_RST_CTL, 0x02, 0x02); + regmap_write(pahu->wcd9xxx->regmap, + WCD9360_CODEC_RPM_PWR_CDC_DIG_HM_CTL, 0x03); + + wcd9xxx_set_power_state(pahu->wcd9xxx, + WCD_REGION_POWER_COLLAPSE_REMOVE, + WCD9XXX_DIG_CORE_REGION_1); + regcache_mark_dirty(pahu->wcd9xxx->regmap); + regcache_sync_region(pahu->wcd9xxx->regmap, + WCD9360_DIG_CORE_REG_MIN, + WCD9360_DIG_CORE_REG_MAX); + return 0; +} + +static int pahu_dig_core_power_collapse(struct pahu_priv *pahu, + int req_state) +{ + int cur_state; + + /* Exit if feature is disabled */ + if (!dig_core_collapse_enable) + return 0; + + mutex_lock(&pahu->power_lock); + if (req_state == POWER_COLLAPSE) + pahu->power_active_ref--; + else if (req_state == POWER_RESUME) + pahu->power_active_ref++; + else + goto unlock_mutex; + + if (pahu->power_active_ref < 0) { + dev_dbg(pahu->dev, "%s: power_active_ref is negative\n", + __func__); + goto unlock_mutex; + } + + if (req_state == POWER_COLLAPSE) { + if (pahu->power_active_ref == 0) { + schedule_delayed_work(&pahu->power_gate_work, + msecs_to_jiffies(dig_core_collapse_timer * 1000)); + } + } else if (req_state == POWER_RESUME) { + if (pahu->power_active_ref == 1) { + /* + * At this point, there can be two cases: + * 1. Core already in power collapse state + * 2. Timer kicked in and still did not expire or + * waiting for the power_lock + */ + cur_state = wcd9xxx_get_current_power_state( + pahu->wcd9xxx, + WCD9XXX_DIG_CORE_REGION_1); + if (cur_state == WCD_REGION_POWER_DOWN) { + pahu_dig_core_remove_power_collapse(pahu); + } else { + mutex_unlock(&pahu->power_lock); + cancel_delayed_work_sync( + &pahu->power_gate_work); + mutex_lock(&pahu->power_lock); + } + } + } + +unlock_mutex: + mutex_unlock(&pahu->power_lock); + + return 0; +} + +static int pahu_cdc_req_mclk_enable(struct pahu_priv *pahu, + bool enable) +{ + int ret = 0; + + if (enable) { + ret = clk_prepare_enable(pahu->wcd_ext_clk); + if (ret) { + dev_err(pahu->dev, "%s: ext clk enable failed\n", + __func__); + goto done; + } + /* get BG */ + wcd_resmgr_enable_master_bias(pahu->resmgr); + /* get MCLK */ + wcd_resmgr_enable_clk_block(pahu->resmgr, WCD_CLK_MCLK); + } else { + /* put MCLK */ + wcd_resmgr_disable_clk_block(pahu->resmgr, WCD_CLK_MCLK); + /* put BG */ + wcd_resmgr_disable_master_bias(pahu->resmgr); + clk_disable_unprepare(pahu->wcd_ext_clk); + } + +done: + return ret; +} + +static int __pahu_cdc_mclk_enable_locked(struct pahu_priv *pahu, + bool enable) +{ + int ret = 0; + + if (!pahu->wcd_ext_clk) { + dev_err(pahu->dev, "%s: wcd ext clock is NULL\n", __func__); + return -EINVAL; + } + + dev_dbg(pahu->dev, "%s: mclk_enable = %u\n", __func__, enable); + + if (enable) { + pahu_dig_core_power_collapse(pahu, POWER_RESUME); + pahu_vote_svs(pahu, true); + ret = pahu_cdc_req_mclk_enable(pahu, true); + if (ret) + goto done; + } else { + pahu_cdc_req_mclk_enable(pahu, false); + pahu_vote_svs(pahu, false); + pahu_dig_core_power_collapse(pahu, POWER_COLLAPSE); + } + +done: + return ret; +} + +static int __pahu_cdc_mclk_enable(struct pahu_priv *pahu, + bool enable) +{ + int ret; + + WCD9XXX_V2_BG_CLK_LOCK(pahu->resmgr); + ret = __pahu_cdc_mclk_enable_locked(pahu, enable); + if (enable) + wcd_resmgr_set_sido_input_src(pahu->resmgr, + SIDO_SOURCE_RCO_BG); + WCD9XXX_V2_BG_CLK_UNLOCK(pahu->resmgr); + + return ret; +} + +static ssize_t pahu_codec_version_read(struct snd_info_entry *entry, + void *file_private_data, + struct file *file, + char __user *buf, size_t count, + loff_t pos) +{ + struct pahu_priv *pahu; + struct wcd9xxx *wcd9xxx; + char buffer[PAHU_VERSION_ENTRY_SIZE]; + int len = 0; + + pahu = (struct pahu_priv *) entry->private_data; + if (!pahu) { + pr_err("%s: pahu priv is null\n", __func__); + return -EINVAL; + } + + wcd9xxx = pahu->wcd9xxx; + + switch (wcd9xxx->version) { + case PAHU_VERSION_1_0: + len = snprintf(buffer, sizeof(buffer), "WCD9360_1_0\n"); + break; + default: + len = snprintf(buffer, sizeof(buffer), "VER_UNDEFINED\n"); + } + + return simple_read_from_buffer(buf, count, &pos, buffer, len); +} + +static struct snd_info_entry_ops pahu_codec_info_ops = { + .read = pahu_codec_version_read, +}; + +/* + * pahu_codec_info_create_codec_entry - creates wcd9360 module + * @codec_root: The parent directory + * @codec: Codec instance + * + * Creates wcd9360 module and version entry under the given + * parent directory. + * + * Return: 0 on success or negative error code on failure. + */ +int pahu_codec_info_create_codec_entry(struct snd_info_entry *codec_root, + struct snd_soc_codec *codec) +{ + struct snd_info_entry *version_entry; + struct pahu_priv *pahu; + struct snd_soc_card *card; + + if (!codec_root || !codec) + return -EINVAL; + + pahu = snd_soc_codec_get_drvdata(codec); + card = codec->component.card; + pahu->entry = snd_info_create_subdir(codec_root->module, + "pahu", codec_root); + if (!pahu->entry) { + dev_dbg(codec->dev, "%s: failed to create wcd9360 entry\n", + __func__); + return -ENOMEM; + } + + version_entry = snd_info_create_card_entry(card->snd_card, + "version", + pahu->entry); + if (!version_entry) { + dev_dbg(codec->dev, "%s: failed to create wcd9360 version entry\n", + __func__); + return -ENOMEM; + } + + version_entry->private_data = pahu; + version_entry->size = PAHU_VERSION_ENTRY_SIZE; + version_entry->content = SNDRV_INFO_CONTENT_DATA; + version_entry->c.ops = &pahu_codec_info_ops; + + if (snd_info_register(version_entry) < 0) { + snd_info_free_entry(version_entry); + return -ENOMEM; + } + pahu->version_entry = version_entry; + + return 0; +} +EXPORT_SYMBOL(pahu_codec_info_create_codec_entry); + +/** + * pahu_cdc_mclk_enable - Enable/disable codec mclk + * + * @codec: codec instance + * @enable: Indicates clk enable or disable + * + * Returns 0 on Success and error on failure + */ +int pahu_cdc_mclk_enable(struct snd_soc_codec *codec, bool enable) +{ + struct pahu_priv *pahu = snd_soc_codec_get_drvdata(codec); + + return __pahu_cdc_mclk_enable(pahu, enable); +} +EXPORT_SYMBOL(pahu_cdc_mclk_enable); + +static int __pahu_codec_internal_rco_ctrl(struct snd_soc_codec *codec, + bool enable) +{ + struct pahu_priv *pahu = snd_soc_codec_get_drvdata(codec); + int ret = 0; + + if (enable) { + if (wcd_resmgr_get_clk_type(pahu->resmgr) == + WCD_CLK_RCO) { + ret = wcd_resmgr_enable_clk_block(pahu->resmgr, + WCD_CLK_RCO); + } else { + ret = pahu_cdc_req_mclk_enable(pahu, true); + if (ret) { + dev_err(codec->dev, + "%s: mclk_enable failed, err = %d\n", + __func__, ret); + goto done; + } + wcd_resmgr_set_sido_input_src(pahu->resmgr, + SIDO_SOURCE_RCO_BG); + ret = wcd_resmgr_enable_clk_block(pahu->resmgr, + WCD_CLK_RCO); + ret |= pahu_cdc_req_mclk_enable(pahu, false); + } + + } else { + ret = wcd_resmgr_disable_clk_block(pahu->resmgr, + WCD_CLK_RCO); + } + + if (ret) { + dev_err(codec->dev, "%s: Error in %s RCO\n", + __func__, (enable ? "enabling" : "disabling")); + ret = -EINVAL; + } + +done: + return ret; +} + +/* + * pahu_codec_internal_rco_ctrl: Enable/Disable codec's RCO clock + * @codec: Handle to the codec + * @enable: Indicates whether clock should be enabled or disabled + */ +static int pahu_codec_internal_rco_ctrl(struct snd_soc_codec *codec, + bool enable) +{ + struct pahu_priv *pahu = snd_soc_codec_get_drvdata(codec); + int ret = 0; + + WCD9XXX_V2_BG_CLK_LOCK(pahu->resmgr); + ret = __pahu_codec_internal_rco_ctrl(codec, enable); + WCD9XXX_V2_BG_CLK_UNLOCK(pahu->resmgr); + return ret; +} + +/* + * pahu_cdc_mclk_tx_enable: Enable/Disable codec's clock for TX path + * @codec: Handle to codec + * @enable: Indicates whether clock should be enabled or disabled + */ +int pahu_cdc_mclk_tx_enable(struct snd_soc_codec *codec, bool enable) +{ + struct pahu_priv *pahu_p; + int ret = 0; + bool clk_mode; + bool clk_internal; + + if (!codec) + return -EINVAL; + + pahu_p = snd_soc_codec_get_drvdata(codec); + clk_mode = test_bit(CLK_MODE, &pahu_p->status_mask); + clk_internal = test_bit(CLK_INTERNAL, &pahu_p->status_mask); + + dev_dbg(codec->dev, "%s: clkmode: %d, enable: %d, clk_internal: %d\n", + __func__, clk_mode, enable, clk_internal); + + if (clk_mode || clk_internal) { + if (enable) { + wcd_resmgr_enable_master_bias(pahu_p->resmgr); + pahu_dig_core_power_collapse(pahu_p, POWER_RESUME); + pahu_vote_svs(pahu_p, true); + ret = pahu_codec_internal_rco_ctrl(codec, enable); + set_bit(CLK_INTERNAL, &pahu_p->status_mask); + } else { + clear_bit(CLK_INTERNAL, &pahu_p->status_mask); + pahu_codec_internal_rco_ctrl(codec, enable); + pahu_vote_svs(pahu_p, false); + pahu_dig_core_power_collapse(pahu_p, POWER_COLLAPSE); + wcd_resmgr_disable_master_bias(pahu_p->resmgr); + } + } else { + ret = __pahu_cdc_mclk_enable(pahu_p, enable); + } + + return ret; +} +EXPORT_SYMBOL(pahu_cdc_mclk_tx_enable); + +static const struct wcd_resmgr_cb pahu_resmgr_cb = { + .cdc_rco_ctrl = __pahu_codec_internal_rco_ctrl, +}; + +static const struct pahu_reg_mask_val pahu_codec_mclk2_1_0_defaults[] = { + /* + * PLL Settings: + * Clock Root: MCLK2, + * Clock Source: EXT_CLK, + * Clock Destination: MCLK2 + * Clock Freq In: 19.2MHz, + * Clock Freq Out: 11.2896MHz + */ + {WCD9360_CLK_SYS_MCLK2_PRG1, 0x60, 0x20}, + {WCD9360_CLK_SYS_INT_POST_DIV_REG0, 0xFF, 0x5E}, + {WCD9360_CLK_SYS_INT_POST_DIV_REG1, 0x1F, 0x1F}, + {WCD9360_CLK_SYS_INT_REF_DIV_REG0, 0xFF, 0x54}, + {WCD9360_CLK_SYS_INT_REF_DIV_REG1, 0xFF, 0x01}, + {WCD9360_CLK_SYS_INT_FILTER_REG1, 0x07, 0x04}, + {WCD9360_CLK_SYS_INT_PLL_L_VAL, 0xFF, 0x93}, + {WCD9360_CLK_SYS_INT_PLL_N_VAL, 0xFF, 0xFA}, + {WCD9360_CLK_SYS_INT_TEST_REG0, 0xFF, 0x90}, + {WCD9360_CLK_SYS_INT_PFD_CP_DSM_PROG, 0xFF, 0x7E}, + {WCD9360_CLK_SYS_INT_VCO_PROG, 0xFF, 0xF8}, + {WCD9360_CLK_SYS_INT_TEST_REG1, 0xFF, 0x68}, + {WCD9360_CLK_SYS_INT_LDO_LOCK_CFG, 0xFF, 0x40}, + {WCD9360_CLK_SYS_INT_DIG_LOCK_DET_CFG, 0xFF, 0x32}, +}; + +static const struct pahu_reg_mask_val pahu_codec_reg_defaults[] = { + {WCD9360_BIAS_VBG_FINE_ADJ, 0xFF, 0x75}, + {WCD9360_CODEC_RPM_CLK_MCLK_CFG, 0x03, 0x01}, + {WCD9360_CODEC_CPR_SVS_CX_VDD, 0xFF, 0x7C}, /* value in svs mode */ + {WCD9360_CODEC_CPR_SVS2_CX_VDD, 0xFF, 0x58}, /* value in svs2 mode */ + {WCD9360_CDC_RX0_RX_PATH_DSMDEM_CTL, 0x01, 0x01}, + {WCD9360_CDC_RX7_RX_PATH_DSMDEM_CTL, 0x01, 0x01}, + {WCD9360_CDC_RX8_RX_PATH_DSMDEM_CTL, 0x01, 0x01}, + {WCD9360_CDC_RX9_RX_PATH_DSMDEM_CTL, 0x01, 0x01}, + {WCD9360_CDC_COMPANDER8_CTL7, 0x1E, 0x18}, + {WCD9360_CDC_COMPANDER7_CTL7, 0x1E, 0x18}, + {WCD9360_CDC_RX0_RX_PATH_SEC0, 0x08, 0x00}, + {WCD9360_CDC_RX9_RX_PATH_SEC0, 0x08, 0x00}, + {WCD9360_MICB1_TEST_CTL_2, 0x07, 0x01}, + {WCD9360_CDC_BOOST0_BOOST_CFG1, 0x3F, 0x12}, + {WCD9360_CDC_BOOST0_BOOST_CFG2, 0x1C, 0x08}, + {WCD9360_CDC_BOOST1_BOOST_CFG1, 0x3F, 0x12}, + {WCD9360_CDC_BOOST1_BOOST_CFG2, 0x1C, 0x08}, + {WCD9360_CPE_SS_CPARMAD_BUFRDY_INT_PERIOD, 0x1F, 0x09}, + {WCD9360_CDC_TX0_TX_PATH_CFG1, 0x01, 0x00}, + {WCD9360_CDC_TX1_TX_PATH_CFG1, 0x01, 0x00}, + {WCD9360_CDC_TX2_TX_PATH_CFG1, 0x01, 0x00}, + {WCD9360_CDC_TX3_TX_PATH_CFG1, 0x01, 0x00}, + {WCD9360_CDC_TX4_TX_PATH_CFG1, 0x01, 0x00}, + {WCD9360_CDC_TX5_TX_PATH_CFG1, 0x01, 0x00}, + {WCD9360_CDC_TX6_TX_PATH_CFG1, 0x01, 0x00}, + {WCD9360_CDC_TX7_TX_PATH_CFG1, 0x01, 0x00}, + {WCD9360_CDC_TX8_TX_PATH_CFG1, 0x01, 0x00}, + {WCD9360_CPE_FLL_CONFIG_CTL_2, 0xFF, 0x20}, + {WCD9360_CPE_SS_DMIC_CFG, 0x80, 0x00}, + {WCD9360_CDC_BOOST0_BOOST_CTL, 0x70, 0x50}, + {WCD9360_CDC_BOOST1_BOOST_CTL, 0x70, 0x50}, + {WCD9360_CDC_RX7_RX_PATH_CFG1, 0x08, 0x08}, + {WCD9360_CDC_RX8_RX_PATH_CFG1, 0x08, 0x08}, + {WCD9360_CDC_TOP_TOP_CFG1, 0x02, 0x02}, + {WCD9360_CDC_TOP_TOP_CFG1, 0x01, 0x01}, + {WCD9360_CDC_TOP_EAR_COMP_LUT, 0x80, 0x80}, + {WCD9360_EAR_EAR_DAC_CON, 0x06, 0x02}, + {WCD9360_AUX_INT_AUX_DAC_CON, 0x06, 0x02}, + {WCD9360_CDC_TX9_SPKR_PROT_PATH_CFG0, 0x01, 0x01}, + {WCD9360_CDC_TX10_SPKR_PROT_PATH_CFG0, 0x01, 0x01}, + {WCD9360_CDC_TX11_SPKR_PROT_PATH_CFG0, 0x01, 0x01}, + {WCD9360_CDC_TX12_SPKR_PROT_PATH_CFG0, 0x01, 0x01}, + {WCD9360_DATA_HUB_SB_TX11_INP_CFG, 0x01, 0x01}, + {WCD9360_CDC_CLK_RST_CTRL_FS_CNT_CONTROL, 0x01, 0x01}, + {WCD9360_CDC_COMPANDER7_CTL3, 0x80, 0x80}, + {WCD9360_CDC_COMPANDER8_CTL3, 0x80, 0x80}, + {WCD9360_CDC_COMPANDER7_CTL7, 0x01, 0x01}, + {WCD9360_CDC_COMPANDER8_CTL7, 0x01, 0x01}, + {WCD9360_CODEC_RPM_CLK_GATE, 0x08, 0x00}, + {WCD9360_CPE_SS_SVA_CFG, 0x60, 0x00}, + {WCD9360_CPE_SS_CPAR_CFG, 0x10, 0x10}, +}; + +static const struct pahu_cpr_reg_defaults cpr_defaults[] = { + { 0x00000820, 0x00000094 }, + { 0x00000fC0, 0x00000048 }, + { 0x0000f000, 0x00000044 }, + { 0x0000bb80, 0xC0000178 }, + { 0x00000000, 0x00000160 }, + { 0x10854522, 0x00000060 }, + { 0x10854509, 0x00000064 }, + { 0x108544dd, 0x00000068 }, + { 0x108544ad, 0x0000006C }, + { 0x0000077E, 0x00000070 }, + { 0x000007da, 0x00000074 }, + { 0x00000000, 0x00000078 }, + { 0x00000000, 0x0000007C }, + { 0x00042029, 0x00000080 }, + { 0x4002002A, 0x00000090 }, + { 0x4002002B, 0x00000090 }, +}; + +static void pahu_update_reg_defaults(struct pahu_priv *pahu) +{ + u32 i; + struct wcd9xxx *wcd9xxx; + + wcd9xxx = pahu->wcd9xxx; + for (i = 0; i < ARRAY_SIZE(pahu_codec_reg_defaults); i++) + regmap_update_bits(wcd9xxx->regmap, + pahu_codec_reg_defaults[i].reg, + pahu_codec_reg_defaults[i].mask, + pahu_codec_reg_defaults[i].val); +} + +static void pahu_update_cpr_defaults(struct pahu_priv *pahu) +{ + int i; + struct wcd9xxx *wcd9xxx; + + wcd9xxx = pahu->wcd9xxx; + + __pahu_cdc_mclk_enable(pahu, true); + + regmap_update_bits(wcd9xxx->regmap, WCD9360_CODEC_RPM_CLK_GATE, + 0x10, 0x00); + + for (i = 0; i < ARRAY_SIZE(cpr_defaults); i++) { + regmap_bulk_write(wcd9xxx->regmap, + WCD9360_CODEC_CPR_WR_DATA_0, + (u8 *)&cpr_defaults[i].wr_data, 4); + regmap_bulk_write(wcd9xxx->regmap, + WCD9360_CODEC_CPR_WR_ADDR_0, + (u8 *)&cpr_defaults[i].wr_addr, 4); + } + + __pahu_cdc_mclk_enable(pahu, false); +} + +static void pahu_slim_interface_init_reg(struct snd_soc_codec *codec) +{ + int i; + struct pahu_priv *priv = snd_soc_codec_get_drvdata(codec); + + for (i = 0; i < WCD9XXX_SLIM_NUM_PORT_REG; i++) + wcd9xxx_interface_reg_write(priv->wcd9xxx, + WCD9360_SLIM_PGD_PORT_INT_RX_EN0 + i, + 0xFF); +} + +static irqreturn_t pahu_misc_irq(int irq, void *data) +{ + struct pahu_priv *pahu = data; + int misc_val; + + /* Find source of interrupt */ + regmap_read(pahu->wcd9xxx->regmap, WCD9360_INTR_CODEC_MISC_STATUS, + &misc_val); + + dev_dbg(pahu->dev, "%s: Codec misc irq: %d, val: 0x%x\n", + __func__, irq, misc_val); + + /* Clear interrupt status */ + regmap_update_bits(pahu->wcd9xxx->regmap, + WCD9360_INTR_CODEC_MISC_CLEAR, misc_val, 0x00); + + return IRQ_HANDLED; +} + +static irqreturn_t pahu_slimbus_irq(int irq, void *data) +{ + struct pahu_priv *pahu = data; + unsigned long status = 0; + int i, j, port_id, k; + u32 bit; + u8 val, int_val = 0; + bool tx, cleared; + unsigned short reg = 0; + + for (i = WCD9360_SLIM_PGD_PORT_INT_STATUS_RX_0, j = 0; + i <= WCD9360_SLIM_PGD_PORT_INT_STATUS_TX_1; i++, j++) { + val = wcd9xxx_interface_reg_read(pahu->wcd9xxx, i); + status |= ((u32)val << (8 * j)); + } + + for_each_set_bit(j, &status, 32) { + tx = (j >= 16 ? true : false); + port_id = (tx ? j - 16 : j); + val = wcd9xxx_interface_reg_read(pahu->wcd9xxx, + WCD9360_SLIM_PGD_PORT_INT_RX_SOURCE0 + j); + if (val) { + if (!tx) + reg = WCD9360_SLIM_PGD_PORT_INT_RX_EN0 + + (port_id / 8); + else + reg = WCD9360_SLIM_PGD_PORT_INT_TX_EN0 + + (port_id / 8); + int_val = wcd9xxx_interface_reg_read( + pahu->wcd9xxx, reg); + /* + * Ignore interrupts for ports for which the + * interrupts are not specifically enabled. + */ + if (!(int_val & (1 << (port_id % 8)))) + continue; + } + if (val & WCD9360_SLIM_IRQ_OVERFLOW) + dev_err_ratelimited(pahu->dev, "%s: overflow error on %s port %d, value %x\n", + __func__, (tx ? "TX" : "RX"), port_id, val); + if (val & WCD9360_SLIM_IRQ_UNDERFLOW) + dev_err_ratelimited(pahu->dev, "%s: underflow error on %s port %d, value %x\n", + __func__, (tx ? "TX" : "RX"), port_id, val); + if ((val & WCD9360_SLIM_IRQ_OVERFLOW) || + (val & WCD9360_SLIM_IRQ_UNDERFLOW)) { + if (!tx) + reg = WCD9360_SLIM_PGD_PORT_INT_RX_EN0 + + (port_id / 8); + else + reg = WCD9360_SLIM_PGD_PORT_INT_TX_EN0 + + (port_id / 8); + int_val = wcd9xxx_interface_reg_read( + pahu->wcd9xxx, reg); + if (int_val & (1 << (port_id % 8))) { + int_val = int_val ^ (1 << (port_id % 8)); + wcd9xxx_interface_reg_write(pahu->wcd9xxx, + reg, int_val); + } + } + if (val & WCD9360_SLIM_IRQ_PORT_CLOSED) { + /* + * INT SOURCE register starts from RX to TX + * but port number in the ch_mask is in opposite way + */ + bit = (tx ? j - 16 : j + 16); + dev_dbg(pahu->dev, "%s: %s port %d closed value %x, bit %u\n", + __func__, (tx ? "TX" : "RX"), port_id, val, + bit); + for (k = 0, cleared = false; k < NUM_CODEC_DAIS; k++) { + dev_dbg(pahu->dev, "%s: pahu->dai[%d].ch_mask = 0x%lx\n", + __func__, k, pahu->dai[k].ch_mask); + if (test_and_clear_bit(bit, + &pahu->dai[k].ch_mask)) { + cleared = true; + if (!pahu->dai[k].ch_mask) + wake_up( + &pahu->dai[k].dai_wait); + /* + * There are cases when multiple DAIs + * might be using the same slimbus + * channel. Hence don't break here. + */ + } + } + } + wcd9xxx_interface_reg_write(pahu->wcd9xxx, + WCD9360_SLIM_PGD_PORT_INT_CLR_RX_0 + + (j / 8), + 1 << (j % 8)); + } + + return IRQ_HANDLED; +} + +static int pahu_setup_irqs(struct pahu_priv *pahu) +{ + int ret = 0; + struct snd_soc_codec *codec = pahu->codec; + struct wcd9xxx *wcd9xxx = pahu->wcd9xxx; + struct wcd9xxx_core_resource *core_res = + &wcd9xxx->core_res; + + ret = wcd9xxx_request_irq(core_res, WCD9XXX_IRQ_SLIMBUS, + pahu_slimbus_irq, "SLIMBUS Slave", pahu); + if (ret) + dev_err(codec->dev, "%s: Failed to request irq %d\n", __func__, + WCD9XXX_IRQ_SLIMBUS); + else + pahu_slim_interface_init_reg(codec); + + /* Register for misc interrupts as well */ + ret = wcd9xxx_request_irq(core_res, WCD9360_IRQ_MISC, + pahu_misc_irq, "CDC MISC Irq", pahu); + if (ret) + dev_err(codec->dev, "%s: Failed to request cdc misc irq\n", + __func__); + + return ret; +} + +static void pahu_init_slim_slave_cfg(struct snd_soc_codec *codec) +{ + struct pahu_priv *priv = snd_soc_codec_get_drvdata(codec); + struct afe_param_cdc_slimbus_slave_cfg *cfg; + struct wcd9xxx *wcd9xxx = priv->wcd9xxx; + uint64_t eaddr = 0; + + cfg = &priv->slimbus_slave_cfg; + cfg->minor_version = 1; + cfg->tx_slave_port_offset = 0; + cfg->rx_slave_port_offset = 16; + + memcpy(&eaddr, &wcd9xxx->slim->e_addr, sizeof(wcd9xxx->slim->e_addr)); + WARN_ON(sizeof(wcd9xxx->slim->e_addr) != 6); + cfg->device_enum_addr_lsw = eaddr & 0xFFFFFFFF; + cfg->device_enum_addr_msw = eaddr >> 32; + + dev_dbg(codec->dev, "%s: slimbus logical address 0x%llx\n", + __func__, eaddr); +} + +static void pahu_cleanup_irqs(struct pahu_priv *pahu) +{ + struct wcd9xxx *wcd9xxx = pahu->wcd9xxx; + struct wcd9xxx_core_resource *core_res = + &wcd9xxx->core_res; + + wcd9xxx_free_irq(core_res, WCD9XXX_IRQ_SLIMBUS, pahu); + wcd9xxx_free_irq(core_res, WCD9360_IRQ_MISC, pahu); +} + +/* + * wcd9360_get_micb_vout_ctl_val: converts micbias from volts to register value + * @micb_mv: micbias in mv + * + * return register value converted + */ +int wcd9360_get_micb_vout_ctl_val(u32 micb_mv) +{ + /* min micbias voltage is 1V and maximum is 2.85V */ + if (micb_mv < 1000 || micb_mv > 2850) { + pr_err("%s: unsupported micbias voltage\n", __func__); + return -EINVAL; + } + + return (micb_mv - 1000) / 50; +} +EXPORT_SYMBOL(wcd9360_get_micb_vout_ctl_val); + +static int pahu_handle_pdata(struct pahu_priv *pahu, + struct wcd9xxx_pdata *pdata) +{ + struct snd_soc_codec *codec = pahu->codec; + u8 mad_dmic_ctl_val; + u8 anc_ctl_value; + u32 dmic_clk_drv; + int vout_ctl_1, vout_ctl_2, vout_ctl_3, vout_ctl_4; + int rc = 0; + + if (!pdata) { + dev_err(codec->dev, "%s: NULL pdata\n", __func__); + return -ENODEV; + } + + /* set micbias voltage */ + vout_ctl_1 = wcd9360_get_micb_vout_ctl_val(pdata->micbias.micb1_mv); + vout_ctl_2 = wcd9360_get_micb_vout_ctl_val(pdata->micbias.micb2_mv); + vout_ctl_3 = wcd9360_get_micb_vout_ctl_val(pdata->micbias.micb3_mv); + vout_ctl_4 = wcd9360_get_micb_vout_ctl_val(pdata->micbias.micb4_mv); + if (vout_ctl_1 < 0 || vout_ctl_2 < 0 || + vout_ctl_3 < 0 || vout_ctl_4 < 0) { + rc = -EINVAL; + goto done; + } + snd_soc_update_bits(codec, WCD9360_ANA_MICB1, 0x3F, vout_ctl_1); + snd_soc_update_bits(codec, WCD9360_ANA_MICB2, 0x3F, vout_ctl_2); + snd_soc_update_bits(codec, WCD9360_ANA_MICB3, 0x3F, vout_ctl_3); + snd_soc_update_bits(codec, WCD9360_ANA_MICB4, 0x3F, vout_ctl_4); + + if (pdata->dmic_sample_rate == + WCD9XXX_DMIC_SAMPLE_RATE_UNDEFINED) { + dev_info(codec->dev, "%s: dmic_rate invalid default = %d\n", + __func__, WCD9XXX_DMIC_SAMPLE_RATE_4P8MHZ); + pdata->dmic_sample_rate = WCD9XXX_DMIC_SAMPLE_RATE_4P8MHZ; + } + if (pdata->mad_dmic_sample_rate == + WCD9XXX_DMIC_SAMPLE_RATE_UNDEFINED) { + dev_info(codec->dev, "%s: mad_dmic_rate invalid default = %d\n", + __func__, WCD9XXX_DMIC_SAMPLE_RATE_4P8MHZ); + /* + * use dmic_sample_rate as the default for MAD + * if mad dmic sample rate is undefined + */ + pdata->mad_dmic_sample_rate = pdata->dmic_sample_rate; + } + + if (pdata->dmic_clk_drv == + WCD9XXX_DMIC_CLK_DRIVE_UNDEFINED) { + pdata->dmic_clk_drv = WCD9360_DMIC_CLK_DRIVE_DEFAULT; + dev_dbg(codec->dev, + "%s: dmic_clk_strength invalid, default = %d\n", + __func__, pdata->dmic_clk_drv); + } + + switch (pdata->dmic_clk_drv) { + case 2: + dmic_clk_drv = 0; + break; + case 4: + dmic_clk_drv = 1; + break; + case 8: + dmic_clk_drv = 2; + break; + case 16: + dmic_clk_drv = 3; + break; + default: + dev_err(codec->dev, + "%s: invalid dmic_clk_drv %d, using default\n", + __func__, pdata->dmic_clk_drv); + dmic_clk_drv = 0; + break; + } + + snd_soc_update_bits(codec, WCD9360_TEST_DEBUG_PAD_DRVCTL_0, + 0x0C, dmic_clk_drv << 2); + + /* + * Default the DMIC clk rates to mad_dmic_sample_rate, + * whereas, the anc/txfe dmic rates to dmic_sample_rate + * since the anc/txfe are independent of mad block. + */ + mad_dmic_ctl_val = pahu_get_dmic_clk_val(pahu->codec, + pdata->mad_dmic_sample_rate); + snd_soc_update_bits(codec, WCD9360_CPE_SS_DMIC0_CTL, + 0x0E, mad_dmic_ctl_val << 1); + snd_soc_update_bits(codec, WCD9360_CPE_SS_DMIC1_CTL, + 0x0E, mad_dmic_ctl_val << 1); + snd_soc_update_bits(codec, WCD9360_CPE_SS_DMIC2_CTL, + 0x0E, mad_dmic_ctl_val << 1); + + if (dmic_clk_drv == WCD9360_DMIC_CLK_DIV_2) + anc_ctl_value = WCD9360_ANC_DMIC_X2_FULL_RATE; + else + anc_ctl_value = WCD9360_ANC_DMIC_X2_HALF_RATE; + + snd_soc_update_bits(codec, WCD9360_CDC_ANC0_MODE_2_CTL, + 0x40, anc_ctl_value << 6); + snd_soc_update_bits(codec, WCD9360_CDC_ANC0_MODE_2_CTL, + 0x20, anc_ctl_value << 5); + +done: + return rc; +} + +static void pahu_cdc_vote_svs(struct snd_soc_codec *codec, bool vote) +{ + struct pahu_priv *pahu = snd_soc_codec_get_drvdata(codec); + + return pahu_vote_svs(pahu, vote); +} + +static struct wcd_dsp_cdc_cb cdc_cb = { + .cdc_clk_en = pahu_codec_internal_rco_ctrl, + .cdc_vote_svs = pahu_cdc_vote_svs, +}; + +static int pahu_wdsp_initialize(struct snd_soc_codec *codec) +{ + struct wcd9xxx *control; + struct pahu_priv *pahu; + struct wcd_dsp_params params; + int ret = 0; + + control = dev_get_drvdata(codec->dev->parent); + pahu = snd_soc_codec_get_drvdata(codec); + + params.cb = &cdc_cb; + params.irqs.cpe_ipc1_irq = WCD9360_IRQ_CPE1_INTR; + params.irqs.cpe_err_irq = WCD9360_IRQ_CPE_ERROR; + params.irqs.fatal_irqs = CPE_FATAL_IRQS; + params.clk_rate = control->mclk_rate; + params.dsp_instance = 0; + + wcd9360_dsp_cntl_init(codec, ¶ms, &pahu->wdsp_cntl); + if (!pahu->wdsp_cntl) { + dev_err(pahu->dev, "%s: wcd-dsp-control init failed\n", + __func__); + ret = -EINVAL; + } + + return ret; +} + +static void pahu_mclk2_reg_defaults(struct pahu_priv *pahu) +{ + int i; + struct snd_soc_codec *codec = pahu->codec; + + /* MCLK2 configuration */ + for (i = 0; i < ARRAY_SIZE(pahu_codec_mclk2_1_0_defaults); i++) + snd_soc_update_bits(codec, + pahu_codec_mclk2_1_0_defaults[i].reg, + pahu_codec_mclk2_1_0_defaults[i].mask, + pahu_codec_mclk2_1_0_defaults[i].val); +} + +static int pahu_device_down(struct wcd9xxx *wcd9xxx) +{ + struct snd_soc_codec *codec; + struct pahu_priv *priv; + int count; + + codec = (struct snd_soc_codec *)(wcd9xxx->ssr_priv); + priv = snd_soc_codec_get_drvdata(codec); + if (priv->swr.ctrl_data) + swrm_wcd_notify(priv->swr.ctrl_data[0].swr_pdev, + SWR_DEVICE_DOWN, NULL); + snd_soc_card_change_online_state(codec->component.card, 0); + for (count = 0; count < NUM_CODEC_DAIS; count++) + priv->dai[count].bus_down_in_recovery = true; + wcd9360_dsp_ssr_event(priv->wdsp_cntl, WCD_CDC_DOWN_EVENT); + wcd_resmgr_set_sido_input_src_locked(priv->resmgr, + SIDO_SOURCE_INTERNAL); + + return 0; +} + +static int pahu_post_reset_cb(struct wcd9xxx *wcd9xxx) +{ + int i, ret = 0; + struct wcd9xxx *control; + struct snd_soc_codec *codec; + struct pahu_priv *pahu; + struct wcd9xxx_pdata *pdata; + + codec = (struct snd_soc_codec *)(wcd9xxx->ssr_priv); + pahu = snd_soc_codec_get_drvdata(codec); + control = dev_get_drvdata(codec->dev->parent); + + wcd9xxx_set_power_state(pahu->wcd9xxx, + WCD_REGION_POWER_COLLAPSE_REMOVE, + WCD9XXX_DIG_CORE_REGION_1); + + mutex_lock(&pahu->codec_mutex); + + pahu_vote_svs(pahu, true); + pahu_slimbus_slave_port_cfg.slave_dev_intfdev_la = + control->slim_slave->laddr; + pahu_slimbus_slave_port_cfg.slave_dev_pgd_la = + control->slim->laddr; + pahu_init_slim_slave_cfg(codec); + snd_soc_card_change_online_state(codec->component.card, 1); + + for (i = 0; i < PAHU_MAX_MICBIAS; i++) + pahu->micb_ref[i] = 0; + + dev_dbg(codec->dev, "%s: MCLK Rate = %x\n", + __func__, control->mclk_rate); + + pahu_update_reg_defaults(pahu); + wcd_resmgr_post_ssr_v2(pahu->resmgr); + __pahu_enable_efuse_sensing(pahu); + pahu_mclk2_reg_defaults(pahu); + + __pahu_cdc_mclk_enable(pahu, true); + regcache_mark_dirty(codec->component.regmap); + regcache_sync(codec->component.regmap); + __pahu_cdc_mclk_enable(pahu, false); + + pahu_update_cpr_defaults(pahu); + + pdata = dev_get_platdata(codec->dev->parent); + ret = pahu_handle_pdata(pahu, pdata); + if (ret < 0) + dev_err(codec->dev, "%s: invalid pdata\n", __func__); + + pahu_cleanup_irqs(pahu); + ret = pahu_setup_irqs(pahu); + if (ret) { + dev_err(codec->dev, "%s: pahu irq setup failed %d\n", + __func__, ret); + goto done; + } + + pahu_set_spkr_mode(codec, pahu->swr.spkr_mode); + /* + * Once the codec initialization is completed, the svs vote + * can be released allowing the codec to go to SVS2. + */ + pahu_vote_svs(pahu, false); + wcd9360_dsp_ssr_event(pahu->wdsp_cntl, WCD_CDC_UP_EVENT); + +done: + mutex_unlock(&pahu->codec_mutex); + return ret; +} + +static int pahu_soc_codec_probe(struct snd_soc_codec *codec) +{ + struct wcd9xxx *control; + struct pahu_priv *pahu; + struct wcd9xxx_pdata *pdata; + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + int i, ret; + void *ptr = NULL; + + control = dev_get_drvdata(codec->dev->parent); + + dev_info(codec->dev, "%s()\n", __func__); + pahu = snd_soc_codec_get_drvdata(codec); + pahu->intf_type = wcd9xxx_get_intf_type(); + + control->dev_down = pahu_device_down; + control->post_reset = pahu_post_reset_cb; + control->ssr_priv = (void *)codec; + + /* Resource Manager post Init */ + ret = wcd_resmgr_post_init(pahu->resmgr, &pahu_resmgr_cb, codec); + if (ret) { + dev_err(codec->dev, "%s: wcd resmgr post init failed\n", + __func__); + goto err; + } + + pahu->fw_data = devm_kzalloc(codec->dev, sizeof(*(pahu->fw_data)), + GFP_KERNEL); + if (!pahu->fw_data) + goto err; + + set_bit(WCD9XXX_ANC_CAL, pahu->fw_data->cal_bit); + set_bit(WCD9XXX_MAD_CAL, pahu->fw_data->cal_bit); + + ret = wcd_cal_create_hwdep(pahu->fw_data, + WCD9XXX_CODEC_HWDEP_NODE, codec); + if (ret < 0) { + dev_err(codec->dev, "%s hwdep failed %d\n", __func__, ret); + goto err_hwdep; + } + + pahu->codec = codec; + for (i = 0; i < COMPANDER_MAX; i++) + pahu->comp_enabled[i] = 0; + + pdata = dev_get_platdata(codec->dev->parent); + ret = pahu_handle_pdata(pahu, pdata); + if (ret < 0) { + dev_err(codec->dev, "%s: bad pdata\n", __func__); + goto err_hwdep; + } + + ptr = devm_kzalloc(codec->dev, (sizeof(pahu_rx_chs) + + sizeof(pahu_tx_chs)), GFP_KERNEL); + if (!ptr) { + ret = -ENOMEM; + goto err_hwdep; + } + + snd_soc_dapm_add_routes(dapm, pahu_slim_audio_map, + ARRAY_SIZE(pahu_slim_audio_map)); + for (i = 0; i < NUM_CODEC_DAIS; i++) { + INIT_LIST_HEAD(&pahu->dai[i].wcd9xxx_ch_list); + init_waitqueue_head(&pahu->dai[i].dai_wait); + } + pahu_slimbus_slave_port_cfg.slave_dev_intfdev_la = + control->slim_slave->laddr; + pahu_slimbus_slave_port_cfg.slave_dev_pgd_la = + control->slim->laddr; + pahu_slimbus_slave_port_cfg.slave_port_mapping[0] = + WCD9360_TX13; + pahu_init_slim_slave_cfg(codec); + + control->num_rx_port = WCD9360_RX_MAX; + control->rx_chs = ptr; + memcpy(control->rx_chs, pahu_rx_chs, sizeof(pahu_rx_chs)); + control->num_tx_port = WCD9360_TX_MAX; + control->tx_chs = ptr + sizeof(pahu_rx_chs); + memcpy(control->tx_chs, pahu_tx_chs, sizeof(pahu_tx_chs)); + + ret = pahu_setup_irqs(pahu); + if (ret) { + dev_err(pahu->dev, "%s: pahu irq setup failed %d\n", + __func__, ret); + goto err_pdata; + } + + for (i = 0; i < WCD9360_NUM_DECIMATORS; i++) { + pahu->tx_hpf_work[i].pahu = pahu; + pahu->tx_hpf_work[i].decimator = i; + INIT_DELAYED_WORK(&pahu->tx_hpf_work[i].dwork, + pahu_tx_hpf_corner_freq_callback); + + pahu->tx_mute_dwork[i].pahu = pahu; + pahu->tx_mute_dwork[i].decimator = i; + INIT_DELAYED_WORK(&pahu->tx_mute_dwork[i].dwork, + pahu_tx_mute_update_callback); + } + + pahu->spk_anc_dwork.pahu = pahu; + INIT_DELAYED_WORK(&pahu->spk_anc_dwork.dwork, + pahu_spk_anc_update_callback); + + pahu_mclk2_reg_defaults(pahu); + + mutex_lock(&pahu->codec_mutex); + snd_soc_dapm_disable_pin(dapm, "ANC EAR PA"); + snd_soc_dapm_disable_pin(dapm, "ANC EAR"); + snd_soc_dapm_enable_pin(dapm, "ANC SPK1 PA"); + mutex_unlock(&pahu->codec_mutex); + + snd_soc_dapm_ignore_suspend(dapm, "AIF1 Playback"); + snd_soc_dapm_ignore_suspend(dapm, "AIF1 Capture"); + snd_soc_dapm_ignore_suspend(dapm, "AIF2 Playback"); + snd_soc_dapm_ignore_suspend(dapm, "AIF2 Capture"); + snd_soc_dapm_ignore_suspend(dapm, "AIF3 Playback"); + snd_soc_dapm_ignore_suspend(dapm, "AIF3 Capture"); + snd_soc_dapm_ignore_suspend(dapm, "AIF4 Playback"); + snd_soc_dapm_ignore_suspend(dapm, "AIF4 MAD TX"); + snd_soc_dapm_ignore_suspend(dapm, "VIfeed"); + snd_soc_dapm_ignore_suspend(dapm, "I2S1 Playback"); + snd_soc_dapm_ignore_suspend(dapm, "I2S1 Capture"); + + snd_soc_dapm_sync(dapm); + + pahu_wdsp_initialize(codec); + + /* + * Once the codec initialization is completed, the svs vote + * can be released allowing the codec to go to SVS2. + */ + pahu_vote_svs(pahu, false); + + return ret; + +err_pdata: + devm_kfree(codec->dev, ptr); + control->rx_chs = NULL; + control->tx_chs = NULL; +err_hwdep: + devm_kfree(codec->dev, pahu->fw_data); + pahu->fw_data = NULL; +err: + return ret; +} + +static int pahu_soc_codec_remove(struct snd_soc_codec *codec) +{ + struct wcd9xxx *control; + struct pahu_priv *pahu = snd_soc_codec_get_drvdata(codec); + + control = dev_get_drvdata(codec->dev->parent); + devm_kfree(codec->dev, control->rx_chs); + /* slimslave deinit in wcd core looks for this value */ + control->num_rx_port = 0; + control->num_tx_port = 0; + control->rx_chs = NULL; + control->tx_chs = NULL; + pahu_cleanup_irqs(pahu); + + if (pahu->wdsp_cntl) + wcd9360_dsp_cntl_deinit(&pahu->wdsp_cntl); + + return 0; +} + +static struct regmap *pahu_get_regmap(struct device *dev) +{ + struct wcd9xxx *control = dev_get_drvdata(dev->parent); + + return control->regmap; +} + +static struct snd_soc_codec_driver soc_codec_dev_pahu = { + .probe = pahu_soc_codec_probe, + .remove = pahu_soc_codec_remove, + .get_regmap = pahu_get_regmap, + .component_driver = { + .controls = pahu_snd_controls, + .num_controls = ARRAY_SIZE(pahu_snd_controls), + .dapm_widgets = pahu_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(pahu_dapm_widgets), + .dapm_routes = pahu_audio_map, + .num_dapm_routes = ARRAY_SIZE(pahu_audio_map), + }, +}; + +#ifdef CONFIG_PM +static int pahu_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct pahu_priv *pahu = platform_get_drvdata(pdev); + + if (!pahu) { + dev_err(dev, "%s: pahu private data is NULL\n", __func__); + return -EINVAL; + } + dev_dbg(dev, "%s: system suspend\n", __func__); + if (delayed_work_pending(&pahu->power_gate_work) && + cancel_delayed_work_sync(&pahu->power_gate_work)) + pahu_codec_power_gate_digital_core(pahu); + return 0; +} + +static int pahu_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct pahu_priv *pahu = platform_get_drvdata(pdev); + + if (!pahu) { + dev_err(dev, "%s: pahu private data is NULL\n", __func__); + return -EINVAL; + } + dev_dbg(dev, "%s: system resume\n", __func__); + return 0; +} + +static const struct dev_pm_ops pahu_pm_ops = { + .suspend = pahu_suspend, + .resume = pahu_resume, +}; +#endif + +static int pahu_swrm_read(void *handle, int reg) +{ + struct pahu_priv *pahu; + struct wcd9xxx *wcd9xxx; + unsigned short swr_rd_addr_base; + unsigned short swr_rd_data_base; + int val, ret; + + if (!handle) { + pr_err("%s: NULL handle\n", __func__); + return -EINVAL; + } + pahu = (struct pahu_priv *)handle; + wcd9xxx = pahu->wcd9xxx; + + dev_dbg(pahu->dev, "%s: Reading soundwire register, 0x%x\n", + __func__, reg); + swr_rd_addr_base = WCD9360_SWR_AHB_BRIDGE_RD_ADDR_0; + swr_rd_data_base = WCD9360_SWR_AHB_BRIDGE_RD_DATA_0; + + mutex_lock(&pahu->swr.read_mutex); + ret = regmap_bulk_write(wcd9xxx->regmap, swr_rd_addr_base, + (u8 *)®, 4); + if (ret < 0) { + dev_err(pahu->dev, "%s: RD Addr Failure\n", __func__); + goto done; + } + ret = regmap_bulk_read(wcd9xxx->regmap, swr_rd_data_base, + (u8 *)&val, 4); + if (ret < 0) { + dev_err(pahu->dev, "%s: RD Data Failure\n", __func__); + goto done; + } + ret = val; +done: + mutex_unlock(&pahu->swr.read_mutex); + + return ret; +} + +static int pahu_swrm_bulk_write(void *handle, u32 *reg, u32 *val, size_t len) +{ + struct pahu_priv *pahu; + struct wcd9xxx *wcd9xxx; + struct wcd9xxx_reg_val *bulk_reg; + unsigned short swr_wr_addr_base; + unsigned short swr_wr_data_base; + int i, j, ret; + + if (!handle || !reg || !val) { + pr_err("%s: NULL parameter\n", __func__); + return -EINVAL; + } + if (len <= 0) { + pr_err("%s: Invalid size: %zu\n", __func__, len); + return -EINVAL; + } + pahu = (struct pahu_priv *)handle; + wcd9xxx = pahu->wcd9xxx; + + swr_wr_addr_base = WCD9360_SWR_AHB_BRIDGE_WR_ADDR_0; + swr_wr_data_base = WCD9360_SWR_AHB_BRIDGE_WR_DATA_0; + + bulk_reg = kzalloc((2 * len * sizeof(struct wcd9xxx_reg_val)), + GFP_KERNEL); + if (!bulk_reg) + return -ENOMEM; + + for (i = 0, j = 0; i < (len * 2); i += 2, j++) { + bulk_reg[i].reg = swr_wr_data_base; + bulk_reg[i].buf = (u8 *)(&val[j]); + bulk_reg[i].bytes = 4; + bulk_reg[i+1].reg = swr_wr_addr_base; + bulk_reg[i+1].buf = (u8 *)(®[j]); + bulk_reg[i+1].bytes = 4; + } + + mutex_lock(&pahu->swr.write_mutex); + ret = wcd9xxx_slim_bulk_write(wcd9xxx, bulk_reg, + (len * 2), false); + if (ret) { + dev_err(pahu->dev, "%s: swrm bulk write failed, ret: %d\n", + __func__, ret); + } + mutex_unlock(&pahu->swr.write_mutex); + + kfree(bulk_reg); + return ret; +} + +static int pahu_swrm_write(void *handle, int reg, int val) +{ + struct pahu_priv *pahu; + struct wcd9xxx *wcd9xxx; + unsigned short swr_wr_addr_base; + unsigned short swr_wr_data_base; + struct wcd9xxx_reg_val bulk_reg[2]; + int ret; + + if (!handle) { + pr_err("%s: NULL handle\n", __func__); + return -EINVAL; + } + pahu = (struct pahu_priv *)handle; + wcd9xxx = pahu->wcd9xxx; + + swr_wr_addr_base = WCD9360_SWR_AHB_BRIDGE_WR_ADDR_0; + swr_wr_data_base = WCD9360_SWR_AHB_BRIDGE_WR_DATA_0; + + /* First Write the Data to register */ + bulk_reg[0].reg = swr_wr_data_base; + bulk_reg[0].buf = (u8 *)(&val); + bulk_reg[0].bytes = 4; + bulk_reg[1].reg = swr_wr_addr_base; + bulk_reg[1].buf = (u8 *)(®); + bulk_reg[1].bytes = 4; + + mutex_lock(&pahu->swr.write_mutex); + ret = wcd9xxx_slim_bulk_write(wcd9xxx, bulk_reg, 2, false); + if (ret < 0) + dev_err(pahu->dev, "%s: WR Data Failure\n", __func__); + mutex_unlock(&pahu->swr.write_mutex); + + return ret; +} + +static int pahu_swrm_clock(void *handle, bool enable) +{ + struct pahu_priv *pahu; + + if (!handle) { + pr_err("%s: NULL handle\n", __func__); + return -EINVAL; + } + pahu = (struct pahu_priv *)handle; + + mutex_lock(&pahu->swr.clk_mutex); + dev_dbg(pahu->dev, "%s: swrm clock %s\n", + __func__, (enable?"enable" : "disable")); + if (enable) { + pahu->swr.clk_users++; + if (pahu->swr.clk_users == 1) { + regmap_update_bits(pahu->wcd9xxx->regmap, + WCD9360_TEST_DEBUG_NPL_DLY_TEST_1, + 0x10, 0x00); + __pahu_cdc_mclk_enable(pahu, true); + regmap_update_bits(pahu->wcd9xxx->regmap, + WCD9360_CDC_CLK_RST_CTRL_SWR_CONTROL, + 0x01, 0x01); + } + } else { + pahu->swr.clk_users--; + if (pahu->swr.clk_users == 0) { + regmap_update_bits(pahu->wcd9xxx->regmap, + WCD9360_CDC_CLK_RST_CTRL_SWR_CONTROL, + 0x01, 0x00); + __pahu_cdc_mclk_enable(pahu, false); + regmap_update_bits(pahu->wcd9xxx->regmap, + WCD9360_TEST_DEBUG_NPL_DLY_TEST_1, + 0x10, 0x10); + } + } + dev_dbg(pahu->dev, "%s: swrm clock users %d\n", + __func__, pahu->swr.clk_users); + mutex_unlock(&pahu->swr.clk_mutex); + + return 0; +} + +static int pahu_swrm_handle_irq(void *handle, + irqreturn_t (*swrm_irq_handler)(int irq, + void *data), + void *swrm_handle, + int action) +{ + struct pahu_priv *pahu; + int ret = 0; + struct wcd9xxx *wcd9xxx; + + if (!handle) { + pr_err("%s: NULL handle\n", __func__); + return -EINVAL; + } + pahu = (struct pahu_priv *) handle; + wcd9xxx = pahu->wcd9xxx; + + if (action) { + ret = wcd9xxx_request_irq(&wcd9xxx->core_res, + WCD9360_IRQ_SOUNDWIRE, + swrm_irq_handler, + "Pahu SWR Master", swrm_handle); + if (ret) + dev_err(pahu->dev, "%s: Failed to request irq %d\n", + __func__, WCD9360_IRQ_SOUNDWIRE); + } else + wcd9xxx_free_irq(&wcd9xxx->core_res, WCD9360_IRQ_SOUNDWIRE, + swrm_handle); + + return ret; +} + +static void pahu_codec_add_spi_device(struct pahu_priv *pahu, + struct device_node *node) +{ + struct spi_master *master; + struct spi_device *spi; + u32 prop_value; + int rc; + + /* Read the master bus num from DT node */ + rc = of_property_read_u32(node, "qcom,master-bus-num", + &prop_value); + if (rc < 0) { + dev_err(pahu->dev, "%s: prop %s not found in node %s", + __func__, "qcom,master-bus-num", node->full_name); + goto done; + } + + /* Get the reference to SPI master */ + master = spi_busnum_to_master(prop_value); + if (!master) { + dev_err(pahu->dev, "%s: Invalid spi_master for bus_num %u\n", + __func__, prop_value); + goto done; + } + + /* Allocate the spi device */ + spi = spi_alloc_device(master); + if (!spi) { + dev_err(pahu->dev, "%s: spi_alloc_device failed\n", + __func__); + goto err_spi_alloc_dev; + } + + /* Initialize device properties */ + if (of_modalias_node(node, spi->modalias, + sizeof(spi->modalias)) < 0) { + dev_err(pahu->dev, "%s: cannot find modalias for %s\n", + __func__, node->full_name); + goto err_dt_parse; + } + + rc = of_property_read_u32(node, "qcom,chip-select", + &prop_value); + if (rc < 0) { + dev_err(pahu->dev, "%s: prop %s not found in node %s", + __func__, "qcom,chip-select", node->full_name); + goto err_dt_parse; + } + spi->chip_select = prop_value; + + rc = of_property_read_u32(node, "qcom,max-frequency", + &prop_value); + if (rc < 0) { + dev_err(pahu->dev, "%s: prop %s not found in node %s", + __func__, "qcom,max-frequency", node->full_name); + goto err_dt_parse; + } + spi->max_speed_hz = prop_value; + + spi->dev.of_node = node; + + rc = spi_add_device(spi); + if (rc < 0) { + dev_err(pahu->dev, "%s: spi_add_device failed\n", __func__); + goto err_dt_parse; + } + + pahu->spi = spi; + /* Put the reference to SPI master */ + put_device(&master->dev); + + return; + +err_dt_parse: + spi_dev_put(spi); + +err_spi_alloc_dev: + /* Put the reference to SPI master */ + put_device(&master->dev); +done: + return; +} + +static void pahu_add_child_devices(struct work_struct *work) +{ + struct pahu_priv *pahu; + struct platform_device *pdev; + struct device_node *node; + struct wcd9xxx *wcd9xxx; + struct pahu_swr_ctrl_data *swr_ctrl_data = NULL, *temp; + int ret, ctrl_num = 0; + struct wcd_swr_ctrl_platform_data *platdata; + char plat_dev_name[WCD9360_STRING_LEN]; + + pahu = container_of(work, struct pahu_priv, + pahu_add_child_devices_work); + if (!pahu) { + pr_err("%s: Memory for wcd9360 does not exist\n", + __func__); + return; + } + wcd9xxx = pahu->wcd9xxx; + if (!wcd9xxx) { + pr_err("%s: Memory for WCD9XXX does not exist\n", + __func__); + return; + } + if (!wcd9xxx->dev->of_node) { + dev_err(wcd9xxx->dev, "%s: DT node for wcd9xxx does not exist\n", + __func__); + return; + } + + platdata = &pahu->swr.plat_data; + pahu->child_count = 0; + + for_each_child_of_node(wcd9xxx->dev->of_node, node) { + + /* Parse and add the SPI device node */ + if (!strcmp(node->name, "wcd_spi")) { + pahu_codec_add_spi_device(pahu, node); + continue; + } + + /* Parse other child device nodes and add platform device */ + if (!strcmp(node->name, "swr_master")) + strlcpy(plat_dev_name, "pahu_swr_ctrl", + (WCD9360_STRING_LEN - 1)); + else if (strnstr(node->name, "msm_cdc_pinctrl", + strlen("msm_cdc_pinctrl")) != NULL) + strlcpy(plat_dev_name, node->name, + (WCD9360_STRING_LEN - 1)); + else + continue; + + pdev = platform_device_alloc(plat_dev_name, -1); + if (!pdev) { + dev_err(wcd9xxx->dev, "%s: pdev memory alloc failed\n", + __func__); + ret = -ENOMEM; + goto err_mem; + } + pdev->dev.parent = pahu->dev; + pdev->dev.of_node = node; + + if (strcmp(node->name, "swr_master") == 0) { + ret = platform_device_add_data(pdev, platdata, + sizeof(*platdata)); + if (ret) { + dev_err(&pdev->dev, + "%s: cannot add plat data ctrl:%d\n", + __func__, ctrl_num); + goto err_pdev_add; + } + } + + ret = platform_device_add(pdev); + if (ret) { + dev_err(&pdev->dev, + "%s: Cannot add platform device\n", + __func__); + goto err_pdev_add; + } + + if (strcmp(node->name, "swr_master") == 0) { + temp = krealloc(swr_ctrl_data, + (ctrl_num + 1) * sizeof( + struct pahu_swr_ctrl_data), + GFP_KERNEL); + if (!temp) { + dev_err(wcd9xxx->dev, "out of memory\n"); + ret = -ENOMEM; + goto err_pdev_add; + } + swr_ctrl_data = temp; + swr_ctrl_data[ctrl_num].swr_pdev = pdev; + ctrl_num++; + dev_dbg(&pdev->dev, + "%s: Added soundwire ctrl device(s)\n", + __func__); + pahu->swr.ctrl_data = swr_ctrl_data; + } + if (pahu->child_count < WCD9360_CHILD_DEVICES_MAX) + pahu->pdev_child_devices[pahu->child_count++] = pdev; + else + goto err_mem; + } + + return; + +err_pdev_add: + platform_device_put(pdev); +err_mem: + return; +} + +static int __pahu_enable_efuse_sensing(struct pahu_priv *pahu) +{ + int val, rc; + + WCD9XXX_V2_BG_CLK_LOCK(pahu->resmgr); + __pahu_cdc_mclk_enable_locked(pahu, true); + + regmap_update_bits(pahu->wcd9xxx->regmap, + WCD9360_CHIP_TIER_CTRL_EFUSE_CTL, 0x1E, 0x10); + regmap_update_bits(pahu->wcd9xxx->regmap, + WCD9360_CHIP_TIER_CTRL_EFUSE_CTL, 0x01, 0x01); + /* + * 5ms sleep required after enabling efuse control + * before checking the status. + */ + usleep_range(5000, 5500); + wcd_resmgr_set_sido_input_src(pahu->resmgr, + SIDO_SOURCE_RCO_BG); + + WCD9XXX_V2_BG_CLK_UNLOCK(pahu->resmgr); + + rc = regmap_read(pahu->wcd9xxx->regmap, + WCD9360_CHIP_TIER_CTRL_EFUSE_STATUS, &val); + if (rc || (!(val & 0x01))) + WARN(1, "%s: Efuse sense is not complete val=%x, ret=%d\n", + __func__, val, rc); + + __pahu_cdc_mclk_enable(pahu, false); + + return rc; +} + +/* + * pahu_get_wcd_dsp_cntl: Get the reference to wcd_dsp_cntl + * @dev: Device pointer for codec device + * + * This API gets the reference to codec's struct wcd_dsp_cntl + */ +void *pahu_get_wcd_dsp_cntl(struct device *dev) +{ + struct platform_device *pdev; + struct pahu_priv *pahu; + + if (!dev) { + pr_err("%s: Invalid device\n", __func__); + return NULL; + } + + pdev = to_platform_device(dev); + pahu = platform_get_drvdata(pdev); + + return pahu->wdsp_cntl; +} +EXPORT_SYMBOL(pahu_get_wcd_dsp_cntl); + +static int pahu_probe(struct platform_device *pdev) +{ + int ret = 0; + struct pahu_priv *pahu; + struct clk *wcd_ext_clk; + struct wcd9xxx_resmgr_v2 *resmgr; + struct wcd9xxx_power_region *cdc_pwr; + + pahu = devm_kzalloc(&pdev->dev, sizeof(struct pahu_priv), + GFP_KERNEL); + if (!pahu) + return -ENOMEM; + + platform_set_drvdata(pdev, pahu); + + pahu->wcd9xxx = dev_get_drvdata(pdev->dev.parent); + pahu->dev = &pdev->dev; + INIT_DELAYED_WORK(&pahu->power_gate_work, pahu_codec_power_gate_work); + mutex_init(&pahu->power_lock); + INIT_WORK(&pahu->pahu_add_child_devices_work, + pahu_add_child_devices); + mutex_init(&pahu->micb_lock); + mutex_init(&pahu->swr.read_mutex); + mutex_init(&pahu->swr.write_mutex); + mutex_init(&pahu->swr.clk_mutex); + mutex_init(&pahu->codec_mutex); + mutex_init(&pahu->svs_mutex); + + /* + * Codec hardware by default comes up in SVS mode. + * Initialize the svs_ref_cnt to 1 to reflect the hardware + * state in the driver. + */ + pahu->svs_ref_cnt = 1; + + cdc_pwr = devm_kzalloc(&pdev->dev, sizeof(struct wcd9xxx_power_region), + GFP_KERNEL); + if (!cdc_pwr) { + ret = -ENOMEM; + goto err_resmgr; + } + pahu->wcd9xxx->wcd9xxx_pwr[WCD9XXX_DIG_CORE_REGION_1] = cdc_pwr; + cdc_pwr->pwr_collapse_reg_min = WCD9360_DIG_CORE_REG_MIN; + cdc_pwr->pwr_collapse_reg_max = WCD9360_DIG_CORE_REG_MAX; + wcd9xxx_set_power_state(pahu->wcd9xxx, + WCD_REGION_POWER_COLLAPSE_REMOVE, + WCD9XXX_DIG_CORE_REGION_1); + /* + * Init resource manager so that if child nodes such as SoundWire + * requests for clock, resource manager can honor the request + */ + resmgr = wcd_resmgr_init(&pahu->wcd9xxx->core_res, NULL); + if (IS_ERR(resmgr)) { + ret = PTR_ERR(resmgr); + dev_err(&pdev->dev, "%s: Failed to initialize wcd resmgr\n", + __func__); + goto err_resmgr; + } + pahu->resmgr = resmgr; + pahu->swr.plat_data.handle = (void *) pahu; + pahu->swr.plat_data.read = pahu_swrm_read; + pahu->swr.plat_data.write = pahu_swrm_write; + pahu->swr.plat_data.bulk_write = pahu_swrm_bulk_write; + pahu->swr.plat_data.clk = pahu_swrm_clock; + pahu->swr.plat_data.handle_irq = pahu_swrm_handle_irq; + pahu->swr.spkr_gain_offset = WCD9360_RX_GAIN_OFFSET_0_DB; + + /* Register for Clock */ + wcd_ext_clk = clk_get(pahu->wcd9xxx->dev, "wcd_clk"); + if (IS_ERR(wcd_ext_clk)) { + dev_err(pahu->wcd9xxx->dev, "%s: clk get %s failed\n", + __func__, "wcd_ext_clk"); + goto err_clk; + } + pahu->wcd_ext_clk = wcd_ext_clk; + dev_dbg(&pdev->dev, "%s: MCLK Rate = %x\n", __func__, + pahu->wcd9xxx->mclk_rate); + /* Probe defer if mlck is failed */ + ret = clk_prepare_enable(pahu->wcd_ext_clk); + if (ret) { + dev_dbg(pahu->dev, "%s: ext clk enable failed\n", + __func__); + ret = -EPROBE_DEFER; + goto err_cdc_reg; + } + clk_disable_unprepare(pahu->wcd_ext_clk); + + /* Update codec register default values */ + pahu_update_reg_defaults(pahu); + __pahu_enable_efuse_sensing(pahu); + pahu_update_cpr_defaults(pahu); + + /* Register with soc framework */ + ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_pahu, + pahu_dai, ARRAY_SIZE(pahu_dai)); + if (ret) { + dev_err(&pdev->dev, "%s: Codec registration failed\n", + __func__); + goto err_cdc_reg; + } + schedule_work(&pahu->pahu_add_child_devices_work); + + return ret; + +err_cdc_reg: + clk_put(pahu->wcd_ext_clk); +err_clk: + wcd_resmgr_remove(pahu->resmgr); +err_resmgr: + mutex_destroy(&pahu->micb_lock); + mutex_destroy(&pahu->svs_mutex); + mutex_destroy(&pahu->codec_mutex); + mutex_destroy(&pahu->swr.read_mutex); + mutex_destroy(&pahu->swr.write_mutex); + mutex_destroy(&pahu->swr.clk_mutex); + devm_kfree(&pdev->dev, pahu); + + return ret; +} + +static int pahu_remove(struct platform_device *pdev) +{ + struct pahu_priv *pahu; + int count = 0; + + pahu = platform_get_drvdata(pdev); + if (!pahu) + return -EINVAL; + + if (pahu->spi) + spi_unregister_device(pahu->spi); + for (count = 0; count < pahu->child_count && + count < WCD9360_CHILD_DEVICES_MAX; count++) + platform_device_unregister(pahu->pdev_child_devices[count]); + + mutex_destroy(&pahu->micb_lock); + mutex_destroy(&pahu->svs_mutex); + mutex_destroy(&pahu->codec_mutex); + mutex_destroy(&pahu->swr.read_mutex); + mutex_destroy(&pahu->swr.write_mutex); + mutex_destroy(&pahu->swr.clk_mutex); + + snd_soc_unregister_codec(&pdev->dev); + clk_put(pahu->wcd_ext_clk); + wcd_resmgr_remove(pahu->resmgr); + devm_kfree(&pdev->dev, pahu); + return 0; +} + +static struct platform_driver pahu_codec_driver = { + .probe = pahu_probe, + .remove = pahu_remove, + .driver = { + .name = "pahu_codec", + .owner = THIS_MODULE, +#ifdef CONFIG_PM + .pm = &pahu_pm_ops, +#endif + }, +}; + +module_platform_driver(pahu_codec_driver); + +MODULE_DESCRIPTION("Pahu Codec driver"); +MODULE_LICENSE("GPL v2"); diff --git a/audio-kernel/asoc/codecs/wcd9360/wcd9360.h b/audio-kernel/asoc/codecs/wcd9360/wcd9360.h new file mode 100644 index 000000000..04fb76d1a --- /dev/null +++ b/audio-kernel/asoc/codecs/wcd9360/wcd9360.h @@ -0,0 +1,180 @@ +/* + * 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 + * 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 __WCD9360_H__ +#define __WCD9360_H__ + +#include +#include "../wcd9xxx-slimslave.h" +#include "../wcd9xxx-common-v2.h" + +#define WCD9360_REGISTER_START_OFFSET 0x800 +#define WCD9360_SB_PGD_PORT_RX_BASE 0x40 +#define WCD9360_SB_PGD_PORT_TX_BASE 0x50 +#define WCD9360_RX_PORT_START_NUMBER 16 + +#define WCD9360_DMIC_CLK_DIV_2 0x0 +#define WCD9360_DMIC_CLK_DIV_3 0x1 +#define WCD9360_DMIC_CLK_DIV_4 0x2 +#define WCD9360_DMIC_CLK_DIV_6 0x3 +#define WCD9360_DMIC_CLK_DIV_8 0x4 +#define WCD9360_DMIC_CLK_DIV_16 0x5 +#define WCD9360_DMIC_CLK_DRIVE_DEFAULT 0x02 + +#define WCD9360_ANC_DMIC_X2_FULL_RATE 1 +#define WCD9360_ANC_DMIC_X2_HALF_RATE 0 + +#define PAHU_MAX_MICBIAS 4 +#define PAHU_NUM_INTERPOLATORS 10 +#define MAX_ON_DEMAND_SUPPLY_NAME_LENGTH 64 + +/* Convert from vout ctl to micbias voltage in mV */ +#define WCD_VOUT_CTL_TO_MICB(v) (1000 + v * 50) + + +/* Number of input and output Slimbus port */ +enum { + WCD9360_RX0 = 0, + WCD9360_RX1, + WCD9360_RX2, + WCD9360_RX3, + WCD9360_RX4, + WCD9360_RX5, + WCD9360_RX6, + WCD9360_RX7, + WCD9360_RX_MAX, +}; + +enum { + WCD9360_TX0 = 0, + WCD9360_TX1, + WCD9360_TX2, + WCD9360_TX3, + WCD9360_TX4, + WCD9360_TX5, + WCD9360_TX6, + WCD9360_TX7, + WCD9360_TX8, + WCD9360_TX9, + WCD9360_TX10, + WCD9360_TX11, + WCD9360_TX12, + WCD9360_TX13, + WCD9360_TX14, + WCD9360_TX15, + WCD9360_TX_MAX, +}; + +/* + * Selects compander and smart boost settings + * for a given speaker mode + */ +enum { + WCD9360_SPKR_MODE_DEFAULT, + WCD9360_SPKR_MODE_1, /* COMP Gain = 12dB, Smartboost Max = 5.5V */ +}; + +/* + * Rx path gain offsets + */ +enum { + WCD9360_RX_GAIN_OFFSET_M1P5_DB, + WCD9360_RX_GAIN_OFFSET_0_DB, +}; + +enum { + WCD9360_MIC_BIAS_1 = 1, + WCD9360_MIC_BIAS_2, + WCD9360_MIC_BIAS_3, + WCD9360_MIC_BIAS_4 +}; + +enum { + WCD9360_MICB_PULLUP_ENABLE, + WCD9360_MICB_PULLUP_DISABLE, + WCD9360_MICB_ENABLE, + WCD9360_MICB_DISABLE, +}; + +/* + * Dai data structure holds the + * dai specific info like rate, + * channel number etc. + */ +struct pahu_codec_dai_data { + u32 rate; + u32 *ch_num; + u32 ch_act; + u32 ch_tot; +}; + +/* + * Structure used to update codec + * register defaults after reset + */ +struct pahu_reg_mask_val { + u16 reg; + u8 mask; + u8 val; +}; + +#if IS_ENABLED(CONFIG_SND_SOC_WCD9360) +extern void *pahu_get_afe_config(struct snd_soc_codec *codec, + enum afe_config_type config_type); +extern int pahu_cdc_mclk_enable(struct snd_soc_codec *codec, bool enable); +extern int pahu_cdc_mclk_tx_enable(struct snd_soc_codec *codec, bool enable); +extern int pahu_set_spkr_mode(struct snd_soc_codec *codec, int mode); +extern int pahu_set_spkr_gain_offset(struct snd_soc_codec *codec, int offset); +extern void *pahu_get_wcd_dsp_cntl(struct device *dev); +extern int wcd9360_get_micb_vout_ctl_val(u32 micb_mv); +extern int pahu_codec_info_create_codec_entry( + struct snd_info_entry *codec_root, + struct snd_soc_codec *codec); +#else +extern void *pahu_get_afe_config(struct snd_soc_codec *codec, + enum afe_config_type config_type) +{ + return NULL; +} +extern int pahu_cdc_mclk_enable(struct snd_soc_codec *codec, bool enable) +{ + return 0; +} +extern int pahu_cdc_mclk_tx_enable(struct snd_soc_codec *codec, bool enable) +{ + return 0; +} +extern int pahu_set_spkr_mode(struct snd_soc_codec *codec, int mode) +{ + return 0; +} +extern int pahu_set_spkr_gain_offset(struct snd_soc_codec *codec, int offset) +{ + return 0; +} +extern void *pahu_get_wcd_dsp_cntl(struct device *dev) +{ + return NULL; +} +extern int wcd9360_get_micb_vout_ctl_val(u32 micb_mv) +{ + return 0; +} +extern int pahu_codec_info_create_codec_entry( + struct snd_info_entry *codec_root, + struct snd_soc_codec *codec) +{ + return 0; +} +#endif + +#endif diff --git a/audio-kernel/asoc/codecs/wcd937x/Android.mk b/audio-kernel/asoc/codecs/wcd937x/Android.mk new file mode 100644 index 000000000..94f556f0a --- /dev/null +++ b/audio-kernel/asoc/codecs/wcd937x/Android.mk @@ -0,0 +1,57 @@ +# Android makefile for audio kernel modules + +# Assume no targets will be supported + +# Check if this driver needs be built for current target +ifeq ($(call is-board-platform,$(MSMSTEPPE) $(TRINKET)),true) +AUDIO_SELECT := CONFIG_SND_SOC_SM6150=m +endif + +AUDIO_CHIPSET := audio +# Build/Package only in case of supported target +ifeq ($(call is-board-platform-in-list,msm8953 sdm845 sdm670 qcs605 msmnile $(MSMSTEPPE) $(TRINKET)),true) + +LOCAL_PATH := $(call my-dir) + +# This makefile is only for DLKM +ifneq ($(findstring vendor,$(LOCAL_PATH)),) + +ifneq ($(findstring opensource,$(LOCAL_PATH)),) + AUDIO_BLD_DIR := $(shell pwd)/vendor/qcom/opensource/audio-kernel +endif # opensource + +DLKM_DIR := $(TOP)/device/qcom/common/dlkm + +# Build audio.ko as $(AUDIO_CHIPSET)_audio.ko +########################################################### +# This is set once per LOCAL_PATH, not per (kernel) module +KBUILD_OPTIONS := AUDIO_ROOT=$(AUDIO_BLD_DIR) + +# We are actually building audio.ko here, as per the +# requirement we are specifying _audio.ko as LOCAL_MODULE. +# This means we need to rename the module to _audio.ko +# after audio.ko is built. +KBUILD_OPTIONS += MODNAME=wcd937x_dlkm +KBUILD_OPTIONS += BOARD_PLATFORM=$(TARGET_BOARD_PLATFORM) +KBUILD_OPTIONS += $(AUDIO_SELECT) + +########################################################### +include $(CLEAR_VARS) +LOCAL_MODULE := $(AUDIO_CHIPSET)_wcd937x.ko +LOCAL_MODULE_KBUILD_NAME := wcd937x_dlkm.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_DEBUG_ENABLE := true +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk +########################################################### +include $(CLEAR_VARS) +LOCAL_MODULE := $(AUDIO_CHIPSET)_wcd937x_slave.ko +LOCAL_MODULE_KBUILD_NAME := wcd937x_slave_dlkm.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_DEBUG_ENABLE := true +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk +########################################################### + +endif # DLKM check +endif # supported target check diff --git a/audio-kernel/asoc/codecs/wcd937x/Kbuild b/audio-kernel/asoc/codecs/wcd937x/Kbuild new file mode 100644 index 000000000..bfc6f4fd5 --- /dev/null +++ b/audio-kernel/asoc/codecs/wcd937x/Kbuild @@ -0,0 +1,122 @@ +# We can build either as part of a standalone Kernel build or as +# an external module. Determine which mechanism is being used +ifeq ($(MODNAME),) + KERNEL_BUILD := 1 +else + KERNEL_BUILD := 0 +endif + + + +ifeq ($(KERNEL_BUILD), 1) + # These are configurable via Kconfig for kernel-based builds + # Need to explicitly configure for Android-based builds + AUDIO_BLD_DIR := $(ANDROID_BUILD_TOP)/kernel/msm-4.9 + AUDIO_ROOT := $(AUDIO_BLD_DIR)/techpack/audio +endif + +ifeq ($(KERNEL_BUILD), 0) + ifeq ($(CONFIG_ARCH_SM6150), y) + include $(AUDIO_ROOT)/config/sm6150auto.conf + export + INCS += -include $(AUDIO_ROOT)/config/sm6150autoconf.h + endif + ifeq ($(CONFIG_ARCH_TRINKET), y) + include $(AUDIO_ROOT)/config/trinketauto.conf + export + INCS += -include $(AUDIO_ROOT)/config/trinketautoconf.h + endif +endif + +# As per target team, build is done as follows: +# Defconfig : build with default flags +# Slub : defconfig + CONFIG_SLUB_DEBUG := y + +# CONFIG_SLUB_DEBUG_ON := y + CONFIG_PAGE_POISONING := y +# Perf : Using appropriate msmXXXX-perf_defconfig +# +# Shipment builds (user variants) should not have any debug feature +# enabled. This is identified using 'TARGET_BUILD_VARIANT'. Slub builds +# are identified using the CONFIG_SLUB_DEBUG_ON configuration. Since +# there is no other way to identify defconfig builds, QTI internal +# representation of perf builds (identified using the string 'perf'), +# is used to identify if the build is a slub or defconfig one. This +# way no critical debug feature will be enabled for perf and shipment +# builds. Other OEMs are also protected using the TARGET_BUILD_VARIANT +# config. + +############ UAPI ############ +UAPI_DIR := uapi +UAPI_INC := -I$(AUDIO_ROOT)/include/$(UAPI_DIR) + +############ COMMON ############ +COMMON_DIR := include +COMMON_INC := -I$(AUDIO_ROOT)/$(COMMON_DIR) + +############ WCD937X ############ + +# for WCD937X Codec +ifdef CONFIG_SND_SOC_WCD937X + WCD937X_OBJS += wcd937x.o + WCD937X_OBJS += wcd937x-regmap.o + WCD937X_OBJS += wcd937x-tables.o + WCD937X_OBJS += wcd937x-mbhc.o +endif + +ifdef CONFIG_SND_SOC_WCD937X_SLAVE + WCD937X_SLAVE_OBJS += wcd937x_slave.o +endif + +LINUX_INC += -Iinclude/linux + +INCS += $(COMMON_INC) \ + $(UAPI_INC) + +EXTRA_CFLAGS += $(INCS) + + +CDEFINES += -DANI_LITTLE_BYTE_ENDIAN \ + -DANI_LITTLE_BIT_ENDIAN \ + -DDOT11F_LITTLE_ENDIAN_HOST \ + -DANI_COMPILER_TYPE_GCC \ + -DANI_OS_TYPE_ANDROID=6 \ + -DPTT_SOCK_SVC_ENABLE \ + -Wall\ + -Werror\ + -D__linux__ + +KBUILD_CPPFLAGS += $(CDEFINES) + +# Currently, for versions of gcc which support it, the kernel Makefile +# is disabling the maybe-uninitialized warning. Re-enable it for the +# AUDIO driver. Note that we must use EXTRA_CFLAGS here so that it +# will override the kernel settings. +ifeq ($(call cc-option-yn, -Wmaybe-uninitialized),y) +EXTRA_CFLAGS += -Wmaybe-uninitialized +endif +#EXTRA_CFLAGS += -Wmissing-prototypes + +ifeq ($(call cc-option-yn, -Wheader-guard),y) +EXTRA_CFLAGS += -Wheader-guard +endif + +ifeq ($(KERNEL_BUILD), 0) +KBUILD_EXTRA_SYMBOLS +=$(OUT)/obj/vendor/qcom/opensource/audio-kernel/ipc/Module.symvers +KBUILD_EXTRA_SYMBOLS +=$(OUT)/obj/vendor/qcom/opensource/audio-kernel/dsp/Module.symvers +KBUILD_EXTRA_SYMBOLS +=$(OUT)/obj/vendor/qcom/opensource/audio-kernel/asoc/Module.symvers +KBUILD_EXTRA_SYMBOLS +=$(OUT)/obj/vendor/qcom/opensource/audio-kernel/asoc/codecs/Module.symvers +KBUILD_EXTRA_SYMBOLS +=$(OUT)/obj/vendor/qcom/opensource/audio-kernel/soc/Module.symvers +endif + +ifeq ($(CONFIG_SND_SOC_GCOV), y) +GCOV_PROFILE := y +endif + +# Module information used by KBuild framework +obj-$(CONFIG_SND_SOC_WCD937X) += wcd937x_dlkm.o +wcd937x_dlkm-y := $(WCD937X_OBJS) + +obj-$(CONFIG_SND_SOC_WCD937X_SLAVE) += wcd937x_slave_dlkm.o +wcd937x_slave_dlkm-y := $(WCD937X_SLAVE_OBJS) + +# inject some build related information +DEFINES += -DBUILD_TIMESTAMP=\"$(shell date -u +'%Y-%m-%dT%H:%M:%SZ')\" diff --git a/audio-kernel/asoc/codecs/wcd937x/internal.h b/audio-kernel/asoc/codecs/wcd937x/internal.h new file mode 100644 index 000000000..439514d73 --- /dev/null +++ b/audio-kernel/asoc/codecs/wcd937x/internal.h @@ -0,0 +1,178 @@ +/* Copyright (c) 2018-2019, 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 _WCD937X_INTERNAL_H +#define _WCD937X_INTERNAL_H + +#include "../wcd-clsh.h" +#include "../wcd-mbhc-v2.h" +#include "asoc/wcd-irq.h" +#include "wcd937x-mbhc.h" + +#define WCD937X_MAX_MICBIAS 3 + +/* Convert from vout ctl to micbias voltage in mV */ +#define WCD_VOUT_CTL_TO_MICB(v) (1000 + v * 50) +#define MAX_PORT 8 +#define MAX_CH_PER_PORT 8 + +extern struct regmap_config wcd937x_regmap_config; + +struct codec_port_info { + u32 slave_port_type; + u32 master_port_type; + u32 ch_mask; + u32 num_ch; + u32 ch_rate; +}; +struct wcd937x_priv { + struct device *dev; + + int variant; + struct snd_soc_codec *codec; + struct device_node *rst_np; + struct regmap *regmap; + + struct swr_device *rx_swr_dev; + struct swr_device *tx_swr_dev; + + s32 micb_ref[WCD937X_MAX_MICBIAS]; + s32 pullup_ref[WCD937X_MAX_MICBIAS]; + + struct fw_info *fw_data; + struct device_node *wcd_rst_np; + + struct mutex micb_lock; + s32 dmic_0_1_clk_cnt; + s32 dmic_2_3_clk_cnt; + s32 dmic_4_5_clk_cnt; + /* class h specific info */ + struct wcd_clsh_cdc_info clsh_info; + /* mbhc module */ + struct wcd937x_mbhc *mbhc; + + u32 hph_mode; + bool comp1_enable; + bool comp2_enable; + + struct irq_domain *virq; + struct wcd_irq_info irq_info; + u32 rx_clk_cnt; + int num_irq_regs; + /* to track the status */ + unsigned long status_mask; + + u8 num_tx_ports; + u8 num_rx_ports; + struct codec_port_info + tx_port_mapping[MAX_PORT][MAX_CH_PER_PORT]; + struct codec_port_info + rx_port_mapping[MAX_PORT][MAX_CH_PER_PORT]; + struct regulator_bulk_data *supplies; + struct notifier_block nblock; + /* wcd callback to bolero */ + void *handle; + int (*update_wcd_event)(void *handle, u16 event, u32 data); + int (*register_notifier)(void *handle, + struct notifier_block *nblock, + bool enable); + int (*wakeup)(void *handle, bool enable); + u32 version; + /* Entry for version info */ + struct snd_info_entry *entry; + struct snd_info_entry *version_entry; + int ana_clk_count; + struct mutex ana_tx_clk_lock; +}; + +struct wcd937x_micbias_setting { + u8 ldoh_v; + u32 cfilt1_mv; + u32 micb1_mv; + u32 micb2_mv; + u32 micb3_mv; + u8 bias1_cfilt_sel; +}; + +struct wcd937x_pdata { + struct device_node *rst_np; + struct device_node *rx_slave; + struct device_node *tx_slave; + struct wcd937x_micbias_setting micbias; + + struct cdc_regulator *regulator; + int num_supplies; +}; + +struct wcd_ctrl_platform_data { + void *handle; + int (*update_wcd_event)(void *handle, u16 event, u32 data); + int (*register_notifier)(void *handle, + struct notifier_block *nblock, + bool enable); +}; + +enum { + WCD_RX1, + WCD_RX2, + WCD_RX3 +}; + +enum { + BOLERO_WCD_EVT_TX_CH_HOLD_CLEAR = 1, + BOLERO_WCD_EVT_PA_OFF_PRE_SSR, + BOLERO_WCD_EVT_SSR_DOWN, + BOLERO_WCD_EVT_SSR_UP, +}; + +enum { + WCD_BOLERO_EVT_RX_MUTE = 1, /* for RX mute/unmute */ + WCD_BOLERO_EVT_IMPED_TRUE, /* for imped true */ + WCD_BOLERO_EVT_IMPED_FALSE, /* for imped false */ +}; + +enum { + /* INTR_CTRL_INT_MASK_0 */ + WCD937X_IRQ_MBHC_BUTTON_PRESS_DET = 0, + WCD937X_IRQ_MBHC_BUTTON_RELEASE_DET, + WCD937X_IRQ_MBHC_ELECT_INS_REM_DET, + WCD937X_IRQ_MBHC_ELECT_INS_REM_LEG_DET, + WCD937X_IRQ_MBHC_SW_DET, + WCD937X_IRQ_HPHR_OCP_INT, + WCD937X_IRQ_HPHR_CNP_INT, + WCD937X_IRQ_HPHL_OCP_INT, + + /* INTR_CTRL_INT_MASK_1 */ + WCD937X_IRQ_HPHL_CNP_INT, + WCD937X_IRQ_EAR_CNP_INT, + WCD937X_IRQ_EAR_SCD_INT, + WCD937X_IRQ_AUX_CNP_INT, + WCD937X_IRQ_AUX_SCD_INT, + WCD937X_IRQ_HPHL_PDM_WD_INT, + WCD937X_IRQ_HPHR_PDM_WD_INT, + WCD937X_IRQ_AUX_PDM_WD_INT, + + /* INTR_CTRL_INT_MASK_2 */ + WCD937X_IRQ_LDORT_SCD_INT, + WCD937X_IRQ_MBHC_MOISTURE_INT, + WCD937X_IRQ_HPHL_SURGE_DET_INT, + WCD937X_IRQ_HPHR_SURGE_DET_INT, + WCD937X_NUM_IRQS, +}; + +extern struct wcd937x_mbhc *wcd937x_soc_get_mbhc(struct snd_soc_codec *codec); +extern int wcd937x_mbhc_micb_adjust_voltage(struct snd_soc_codec *codec, + int volt, int micb_num); +extern int wcd937x_get_micb_vout_ctl_val(u32 micb_mv); +extern int wcd937x_micbias_control(struct snd_soc_codec *codec, int micb_num, + int req, bool is_dapm); +#endif diff --git a/audio-kernel/asoc/codecs/wcd937x/wcd937x-mbhc.c b/audio-kernel/asoc/codecs/wcd937x/wcd937x-mbhc.c new file mode 100644 index 000000000..906768c02 --- /dev/null +++ b/audio-kernel/asoc/codecs/wcd937x/wcd937x-mbhc.c @@ -0,0 +1,1107 @@ +/* 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "wcd937x-registers.h" +#include "../wcdcal-hwdep.h" +#include "../wcd-mbhc-v2-api.h" +#include "internal.h" + +#define WCD937X_ZDET_SUPPORTED true +/* Z value defined in milliohm */ +#define WCD937X_ZDET_VAL_32 32000 +#define WCD937X_ZDET_VAL_400 400000 +#define WCD937X_ZDET_VAL_1200 1200000 +#define WCD937X_ZDET_VAL_100K 100000000 +/* Z floating defined in ohms */ +#define WCD937X_ZDET_FLOATING_IMPEDANCE 0x0FFFFFFE + +#define WCD937X_ZDET_NUM_MEASUREMENTS 900 +#define WCD937X_MBHC_GET_C1(c) ((c & 0xC000) >> 14) +#define WCD937X_MBHC_GET_X1(x) (x & 0x3FFF) +/* Z value compared in milliOhm */ +#define WCD937X_MBHC_IS_SECOND_RAMP_REQUIRED(z) ((z > 400000) || (z < 32000)) +#define WCD937X_MBHC_ZDET_CONST (86 * 16384) +#define WCD937X_MBHC_MOISTURE_RREF R_24_KOHM + +static struct wcd_mbhc_register + wcd_mbhc_registers[WCD_MBHC_REG_FUNC_MAX] = { + WCD_MBHC_REGISTER("WCD_MBHC_L_DET_EN", + WCD937X_ANA_MBHC_MECH, 0x80, 7, 0), + WCD_MBHC_REGISTER("WCD_MBHC_GND_DET_EN", + WCD937X_ANA_MBHC_MECH, 0x40, 6, 0), + WCD_MBHC_REGISTER("WCD_MBHC_MECH_DETECTION_TYPE", + WCD937X_ANA_MBHC_MECH, 0x20, 5, 0), + WCD_MBHC_REGISTER("WCD_MBHC_MIC_CLAMP_CTL", + WCD937X_MBHC_NEW_PLUG_DETECT_CTL, 0x30, 4, 0), + WCD_MBHC_REGISTER("WCD_MBHC_ELECT_DETECTION_TYPE", + WCD937X_ANA_MBHC_ELECT, 0x08, 3, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HS_L_DET_PULL_UP_CTRL", + WCD937X_MBHC_NEW_INT_MECH_DET_CURRENT, 0x1F, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HS_L_DET_PULL_UP_COMP_CTRL", + WCD937X_ANA_MBHC_MECH, 0x04, 2, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPHL_PLUG_TYPE", + WCD937X_ANA_MBHC_MECH, 0x10, 4, 0), + WCD_MBHC_REGISTER("WCD_MBHC_GND_PLUG_TYPE", + WCD937X_ANA_MBHC_MECH, 0x08, 3, 0), + WCD_MBHC_REGISTER("WCD_MBHC_SW_HPH_LP_100K_TO_GND", + WCD937X_ANA_MBHC_MECH, 0x01, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_ELECT_SCHMT_ISRC", + WCD937X_ANA_MBHC_ELECT, 0x06, 1, 0), + WCD_MBHC_REGISTER("WCD_MBHC_FSM_EN", + WCD937X_ANA_MBHC_ELECT, 0x80, 7, 0), + WCD_MBHC_REGISTER("WCD_MBHC_INSREM_DBNC", + WCD937X_MBHC_NEW_PLUG_DETECT_CTL, 0x0F, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_BTN_DBNC", + WCD937X_MBHC_NEW_CTL_1, 0x03, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HS_VREF", + WCD937X_MBHC_NEW_CTL_2, 0x03, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HS_COMP_RESULT", + WCD937X_ANA_MBHC_RESULT_3, 0x08, 3, 0), + WCD_MBHC_REGISTER("WCD_MBHC_IN2P_CLAMP_STATE", + WCD937X_ANA_MBHC_RESULT_3, 0x10, 4, 0), + WCD_MBHC_REGISTER("WCD_MBHC_MIC_SCHMT_RESULT", + WCD937X_ANA_MBHC_RESULT_3, 0x20, 5, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPHL_SCHMT_RESULT", + WCD937X_ANA_MBHC_RESULT_3, 0x80, 7, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPHR_SCHMT_RESULT", + WCD937X_ANA_MBHC_RESULT_3, 0x40, 6, 0), + WCD_MBHC_REGISTER("WCD_MBHC_OCP_FSM_EN", + WCD937X_HPH_OCP_CTL, 0x10, 4, 0), + WCD_MBHC_REGISTER("WCD_MBHC_BTN_RESULT", + WCD937X_ANA_MBHC_RESULT_3, 0x07, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_BTN_ISRC_CTL", + WCD937X_ANA_MBHC_ELECT, 0x70, 4, 0), + WCD_MBHC_REGISTER("WCD_MBHC_ELECT_RESULT", + WCD937X_ANA_MBHC_RESULT_3, 0xFF, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_MICB_CTRL", + WCD937X_ANA_MICB2, 0xC0, 6, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPH_CNP_WG_TIME", + WCD937X_HPH_CNP_WG_TIME, 0xFF, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPHR_PA_EN", + WCD937X_ANA_HPH, 0x40, 6, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPHL_PA_EN", + WCD937X_ANA_HPH, 0x80, 7, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPH_PA_EN", + WCD937X_ANA_HPH, 0xC0, 6, 0), + WCD_MBHC_REGISTER("WCD_MBHC_SWCH_LEVEL_REMOVE", + WCD937X_ANA_MBHC_RESULT_3, 0x10, 4, 0), + WCD_MBHC_REGISTER("WCD_MBHC_PULLDOWN_CTRL", + 0, 0, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_ANC_DET_EN", + WCD937X_MBHC_CTL_BCS, 0x02, 1, 0), + WCD_MBHC_REGISTER("WCD_MBHC_FSM_STATUS", + WCD937X_MBHC_NEW_FSM_STATUS, 0x01, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_MUX_CTL", + WCD937X_MBHC_NEW_CTL_2, 0x70, 4, 0), + WCD_MBHC_REGISTER("WCD_MBHC_MOISTURE_STATUS", + WCD937X_MBHC_NEW_FSM_STATUS, 0x20, 5, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPHR_GND", + WCD937X_HPH_PA_CTL2, 0x40, 6, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPHL_GND", + WCD937X_HPH_PA_CTL2, 0x10, 4, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPHL_OCP_DET_EN", + WCD937X_HPH_L_TEST, 0x01, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPHR_OCP_DET_EN", + WCD937X_HPH_R_TEST, 0x01, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPHL_OCP_STATUS", + WCD937X_DIGITAL_INTR_STATUS_0, 0x80, 7, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPHR_OCP_STATUS", + WCD937X_DIGITAL_INTR_STATUS_0, 0x20, 5, 0), + WCD_MBHC_REGISTER("WCD_MBHC_ADC_EN", + WCD937X_MBHC_NEW_CTL_1, 0x08, 3, 0), + WCD_MBHC_REGISTER("WCD_MBHC_ADC_COMPLETE", WCD937X_MBHC_NEW_FSM_STATUS, + 0x40, 6, 0), + WCD_MBHC_REGISTER("WCD_MBHC_ADC_TIMEOUT", WCD937X_MBHC_NEW_FSM_STATUS, + 0x80, 7, 0), + WCD_MBHC_REGISTER("WCD_MBHC_ADC_RESULT", WCD937X_MBHC_NEW_ADC_RESULT, + 0xFF, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_MICB2_VOUT", WCD937X_ANA_MICB2, 0x3F, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_ADC_MODE", + WCD937X_MBHC_NEW_CTL_1, 0x10, 4, 0), + WCD_MBHC_REGISTER("WCD_MBHC_DETECTION_DONE", + WCD937X_MBHC_NEW_CTL_1, 0x04, 2, 0), + WCD_MBHC_REGISTER("WCD_MBHC_ELECT_ISRC_EN", + WCD937X_ANA_MBHC_ZDET, 0x02, 1, 0), +}; + +static const struct wcd_mbhc_intr intr_ids = { + .mbhc_sw_intr = WCD937X_IRQ_MBHC_SW_DET, + .mbhc_btn_press_intr = WCD937X_IRQ_MBHC_BUTTON_PRESS_DET, + .mbhc_btn_release_intr = WCD937X_IRQ_MBHC_BUTTON_RELEASE_DET, + .mbhc_hs_ins_intr = WCD937X_IRQ_MBHC_ELECT_INS_REM_LEG_DET, + .mbhc_hs_rem_intr = WCD937X_IRQ_MBHC_ELECT_INS_REM_DET, + .hph_left_ocp = WCD937X_IRQ_HPHL_OCP_INT, + .hph_right_ocp = WCD937X_IRQ_HPHR_OCP_INT, +}; + +struct wcd937x_mbhc_zdet_param { + u16 ldo_ctl; + u16 noff; + u16 nshift; + u16 btn5; + u16 btn6; + u16 btn7; +}; + +static int wcd937x_mbhc_request_irq(struct snd_soc_codec *codec, + int irq, irq_handler_t handler, + const char *name, void *data) +{ + struct wcd937x_priv *wcd937x = dev_get_drvdata(codec->dev); + + return wcd_request_irq(&wcd937x->irq_info, irq, name, handler, data); +} + +static void wcd937x_mbhc_irq_control(struct snd_soc_codec *codec, + int irq, bool enable) +{ + struct wcd937x_priv *wcd937x = dev_get_drvdata(codec->dev); + + if (enable) + wcd_enable_irq(&wcd937x->irq_info, irq); + else + wcd_disable_irq(&wcd937x->irq_info, irq); +} + +static int wcd937x_mbhc_free_irq(struct snd_soc_codec *codec, + int irq, void *data) +{ + struct wcd937x_priv *wcd937x = dev_get_drvdata(codec->dev); + + wcd_free_irq(&wcd937x->irq_info, irq, data); + + return 0; +} + +static void wcd937x_mbhc_clk_setup(struct snd_soc_codec *codec, + bool enable) +{ + if (enable) + snd_soc_update_bits(codec, WCD937X_MBHC_NEW_CTL_1, + 0x80, 0x80); + else + snd_soc_update_bits(codec, WCD937X_MBHC_NEW_CTL_1, + 0x80, 0x00); +} + +static int wcd937x_mbhc_btn_to_num(struct snd_soc_codec *codec) +{ + return snd_soc_read(codec, WCD937X_ANA_MBHC_RESULT_3) & 0x7; +} + +static void wcd937x_mbhc_mbhc_bias_control(struct snd_soc_codec *codec, + bool enable) +{ + if (enable) + snd_soc_update_bits(codec, WCD937X_ANA_MBHC_ELECT, + 0x01, 0x01); + else + snd_soc_update_bits(codec, WCD937X_ANA_MBHC_ELECT, + 0x01, 0x00); +} + +static void wcd937x_mbhc_program_btn_thr(struct snd_soc_codec *codec, + s16 *btn_low, s16 *btn_high, + int num_btn, bool is_micbias) +{ + int i; + int vth; + + if (num_btn > WCD_MBHC_DEF_BUTTONS) { + dev_err(codec->dev, "%s: invalid number of buttons: %d\n", + __func__, num_btn); + return; + } + + for (i = 0; i < num_btn; i++) { + vth = ((btn_high[i] * 2) / 25) & 0x3F; + snd_soc_update_bits(codec, WCD937X_ANA_MBHC_BTN0 + i, + 0xFC, vth << 2); + dev_dbg(codec->dev, "%s: btn_high[%d]: %d, vth: %d\n", + __func__, i, btn_high[i], vth); + } +} + +static bool wcd937x_mbhc_lock_sleep(struct wcd_mbhc *mbhc, bool lock) +{ + struct snd_soc_codec *codec = mbhc->codec; + struct wcd937x_priv *wcd937x = dev_get_drvdata(codec->dev); + + wcd937x->wakeup((void*)wcd937x, lock); + return true; +} + +static int wcd937x_mbhc_register_notifier(struct wcd_mbhc *mbhc, + struct notifier_block *nblock, + bool enable) +{ + struct wcd937x_mbhc *wcd937x_mbhc; + + wcd937x_mbhc = container_of(mbhc, struct wcd937x_mbhc, wcd_mbhc); + + if (enable) + return blocking_notifier_chain_register(&wcd937x_mbhc->notifier, + nblock); + else + return blocking_notifier_chain_unregister( + &wcd937x_mbhc->notifier, nblock); +} + +static bool wcd937x_mbhc_micb_en_status(struct wcd_mbhc *mbhc, int micb_num) +{ + u8 val = 0; + + if (micb_num == MIC_BIAS_2) { + val = ((snd_soc_read(mbhc->codec, WCD937X_ANA_MICB2) & 0xC0) + >> 6); + if (val == 0x01) + return true; + } + return false; +} + +static bool wcd937x_mbhc_hph_pa_on_status(struct snd_soc_codec *codec) +{ + return (snd_soc_read(codec, WCD937X_ANA_HPH) & 0xC0) ? true : false; +} + +static void wcd937x_mbhc_hph_l_pull_up_control(struct snd_soc_codec *codec, + int pull_up_cur) +{ + /* Default pull up current to 2uA */ + if (pull_up_cur > HS_PULLUP_I_OFF || pull_up_cur < HS_PULLUP_I_3P0_UA || + pull_up_cur == HS_PULLUP_I_DEFAULT) + pull_up_cur = HS_PULLUP_I_2P0_UA; + + dev_dbg(codec->dev, "%s: HS pull up current:%d\n", + __func__, pull_up_cur); + + snd_soc_update_bits(codec, WCD937X_MBHC_NEW_INT_MECH_DET_CURRENT, + 0x1F, pull_up_cur); +} + +static int wcd937x_mbhc_request_micbias(struct snd_soc_codec *codec, + int micb_num, int req) +{ + int ret = 0; + + ret = wcd937x_micbias_control(codec, micb_num, req, false); + + return ret; +} + +static void wcd937x_mbhc_micb_ramp_control(struct snd_soc_codec *codec, + bool enable) +{ + if (enable) { + snd_soc_update_bits(codec, WCD937X_ANA_MICB2_RAMP, + 0x1C, 0x0C); + snd_soc_update_bits(codec, WCD937X_ANA_MICB2_RAMP, + 0x80, 0x80); + } else { + snd_soc_update_bits(codec, WCD937X_ANA_MICB2_RAMP, + 0x80, 0x00); + snd_soc_update_bits(codec, WCD937X_ANA_MICB2_RAMP, + 0x1C, 0x00); + } +} + +static struct firmware_cal *wcd937x_get_hwdep_fw_cal(struct wcd_mbhc *mbhc, + enum wcd_cal_type type) +{ + struct wcd937x_mbhc *wcd937x_mbhc; + struct firmware_cal *hwdep_cal; + struct snd_soc_codec *codec = mbhc->codec; + + wcd937x_mbhc = container_of(mbhc, struct wcd937x_mbhc, wcd_mbhc); + + if (!codec) { + pr_err("%s: NULL codec pointer\n", __func__); + return NULL; + } + hwdep_cal = wcdcal_get_fw_cal(wcd937x_mbhc->fw_data, type); + if (!hwdep_cal) + dev_err(codec->dev, "%s: cal not sent by %d\n", + __func__, type); + + return hwdep_cal; +} + +static int wcd937x_mbhc_micb_ctrl_threshold_mic(struct snd_soc_codec *codec, + int micb_num, bool req_en) +{ + struct wcd937x_pdata *pdata = dev_get_platdata(codec->dev); + int rc, micb_mv; + + if (micb_num != MIC_BIAS_2) + return -EINVAL; + /* + * If device tree micbias level is already above the minimum + * voltage needed to detect threshold microphone, then do + * not change the micbias, just return. + */ + if (pdata->micbias.micb2_mv >= WCD_MBHC_THR_HS_MICB_MV) + return 0; + + micb_mv = req_en ? WCD_MBHC_THR_HS_MICB_MV : pdata->micbias.micb2_mv; + + rc = wcd937x_mbhc_micb_adjust_voltage(codec, micb_mv, MIC_BIAS_2); + + return rc; +} + +static inline void wcd937x_mbhc_get_result_params(struct wcd937x_priv *wcd937x, + s16 *d1_a, u16 noff, + int32_t *zdet) +{ + int i; + int val, val1; + s16 c1; + s32 x1, d1; + int32_t denom; + int minCode_param[] = { + 3277, 1639, 820, 410, 205, 103, 52, 26 + }; + + regmap_update_bits(wcd937x->regmap, WCD937X_ANA_MBHC_ZDET, 0x20, 0x20); + for (i = 0; i < WCD937X_ZDET_NUM_MEASUREMENTS; i++) { + regmap_read(wcd937x->regmap, WCD937X_ANA_MBHC_RESULT_2, &val); + if (val & 0x80) + break; + } + val = val << 0x8; + regmap_read(wcd937x->regmap, WCD937X_ANA_MBHC_RESULT_1, &val1); + val |= val1; + regmap_update_bits(wcd937x->regmap, WCD937X_ANA_MBHC_ZDET, 0x20, 0x00); + x1 = WCD937X_MBHC_GET_X1(val); + c1 = WCD937X_MBHC_GET_C1(val); + /* If ramp is not complete, give additional 5ms */ + if ((c1 < 2) && x1) + usleep_range(5000, 5050); + + if (!c1 || !x1) { + dev_dbg(wcd937x->dev, + "%s: Impedance detect ramp error, c1=%d, x1=0x%x\n", + __func__, c1, x1); + goto ramp_down; + } + d1 = d1_a[c1]; + denom = (x1 * d1) - (1 << (14 - noff)); + if (denom > 0) + *zdet = (WCD937X_MBHC_ZDET_CONST * 1000) / denom; + else if (x1 < minCode_param[noff]) + *zdet = WCD937X_ZDET_FLOATING_IMPEDANCE; + + dev_dbg(wcd937x->dev, "%s: d1=%d, c1=%d, x1=0x%x, z_val=%d(milliOhm)\n", + __func__, d1, c1, x1, *zdet); +ramp_down: + i = 0; + while (x1) { + regmap_read(wcd937x->regmap, WCD937X_ANA_MBHC_RESULT_1, &val); + regmap_read(wcd937x->regmap, WCD937X_ANA_MBHC_RESULT_2, &val1); + val = val << 0x8; + val |= val1; + x1 = WCD937X_MBHC_GET_X1(val); + i++; + if (i == WCD937X_ZDET_NUM_MEASUREMENTS) + break; + } +} + +static void wcd937x_mbhc_zdet_ramp(struct snd_soc_codec *codec, + struct wcd937x_mbhc_zdet_param *zdet_param, + int32_t *zl, int32_t *zr, s16 *d1_a) +{ + struct wcd937x_priv *wcd937x = dev_get_drvdata(codec->dev); + int32_t zdet = 0; + + snd_soc_update_bits(codec, WCD937X_MBHC_NEW_ZDET_ANA_CTL, 0x70, + zdet_param->ldo_ctl << 4); + snd_soc_update_bits(codec, WCD937X_ANA_MBHC_BTN5, 0xFC, + zdet_param->btn5); + snd_soc_update_bits(codec, WCD937X_ANA_MBHC_BTN6, 0xFC, + zdet_param->btn6); + snd_soc_update_bits(codec, WCD937X_ANA_MBHC_BTN7, 0xFC, + zdet_param->btn7); + snd_soc_update_bits(codec, WCD937X_MBHC_NEW_ZDET_ANA_CTL, 0x0F, + zdet_param->noff); + snd_soc_update_bits(codec, WCD937X_MBHC_NEW_ZDET_RAMP_CTL, 0x0F, + zdet_param->nshift); + + if (!zl) + goto z_right; + /* Start impedance measurement for HPH_L */ + regmap_update_bits(wcd937x->regmap, + WCD937X_ANA_MBHC_ZDET, 0x80, 0x80); + dev_dbg(wcd937x->dev, "%s: ramp for HPH_L, noff = %d\n", + __func__, zdet_param->noff); + wcd937x_mbhc_get_result_params(wcd937x, d1_a, zdet_param->noff, &zdet); + regmap_update_bits(wcd937x->regmap, + WCD937X_ANA_MBHC_ZDET, 0x80, 0x00); + + *zl = zdet; + +z_right: + if (!zr) + return; + /* Start impedance measurement for HPH_R */ + regmap_update_bits(wcd937x->regmap, + WCD937X_ANA_MBHC_ZDET, 0x40, 0x40); + dev_dbg(wcd937x->dev, "%s: ramp for HPH_R, noff = %d\n", + __func__, zdet_param->noff); + wcd937x_mbhc_get_result_params(wcd937x, d1_a, zdet_param->noff, &zdet); + regmap_update_bits(wcd937x->regmap, + WCD937X_ANA_MBHC_ZDET, 0x40, 0x00); + + *zr = zdet; +} + +static inline void wcd937x_wcd_mbhc_qfuse_cal(struct snd_soc_codec *codec, + int32_t *z_val, int flag_l_r) +{ + s16 q1; + int q1_cal; + + if (*z_val < (WCD937X_ZDET_VAL_400/1000)) + q1 = snd_soc_read(codec, + WCD937X_DIGITAL_EFUSE_REG_23 + (2 * flag_l_r)); + else + q1 = snd_soc_read(codec, + WCD937X_DIGITAL_EFUSE_REG_24 + (2 * flag_l_r)); + if (q1 & 0x80) + q1_cal = (10000 - ((q1 & 0x7F) * 25)); + else + q1_cal = (10000 + (q1 * 25)); + if (q1_cal > 0) + *z_val = ((*z_val) * 10000) / q1_cal; +} + +static void wcd937x_wcd_mbhc_calc_impedance(struct wcd_mbhc *mbhc, uint32_t *zl, + uint32_t *zr) +{ + struct snd_soc_codec *codec = mbhc->codec; + struct wcd937x_priv *wcd937x = dev_get_drvdata(codec->dev); + s16 reg0, reg1, reg2, reg3, reg4; + int32_t z1L, z1R, z1Ls; + int zMono, z_diff1, z_diff2; + bool is_fsm_disable = false; + struct wcd937x_mbhc_zdet_param zdet_param[] = { + {4, 0, 4, 0x08, 0x14, 0x18}, /* < 32ohm */ + {2, 0, 3, 0x18, 0x7C, 0x90}, /* 32ohm < Z < 400ohm */ + {1, 4, 5, 0x18, 0x7C, 0x90}, /* 400ohm < Z < 1200ohm */ + {1, 6, 7, 0x18, 0x7C, 0x90}, /* >1200ohm */ + }; + struct wcd937x_mbhc_zdet_param *zdet_param_ptr = NULL; + s16 d1_a[][4] = { + {0, 30, 90, 30}, + {0, 30, 30, 5}, + {0, 30, 30, 5}, + {0, 30, 30, 5}, + }; + s16 *d1 = NULL; + + WCD_MBHC_RSC_ASSERT_LOCKED(mbhc); + + reg0 = snd_soc_read(codec, WCD937X_ANA_MBHC_BTN5); + reg1 = snd_soc_read(codec, WCD937X_ANA_MBHC_BTN6); + reg2 = snd_soc_read(codec, WCD937X_ANA_MBHC_BTN7); + reg3 = snd_soc_read(codec, WCD937X_MBHC_CTL_CLK); + reg4 = snd_soc_read(codec, WCD937X_MBHC_NEW_ZDET_ANA_CTL); + + if (snd_soc_read(codec, WCD937X_ANA_MBHC_ELECT) & 0x80) { + is_fsm_disable = true; + regmap_update_bits(wcd937x->regmap, + WCD937X_ANA_MBHC_ELECT, 0x80, 0x00); + } + + /* For NO-jack, disable L_DET_EN before Z-det measurements */ + if (mbhc->hphl_swh) + regmap_update_bits(wcd937x->regmap, + WCD937X_ANA_MBHC_MECH, 0x80, 0x00); + + /* Turn off 100k pull down on HPHL */ + regmap_update_bits(wcd937x->regmap, + WCD937X_ANA_MBHC_MECH, 0x01, 0x00); + + /* First get impedance on Left */ + d1 = d1_a[1]; + zdet_param_ptr = &zdet_param[1]; + wcd937x_mbhc_zdet_ramp(codec, zdet_param_ptr, &z1L, NULL, d1); + + if (!WCD937X_MBHC_IS_SECOND_RAMP_REQUIRED(z1L)) + goto left_ch_impedance; + + /* Second ramp for left ch */ + if (z1L < WCD937X_ZDET_VAL_32) { + zdet_param_ptr = &zdet_param[0]; + d1 = d1_a[0]; + } else if ((z1L > WCD937X_ZDET_VAL_400) && + (z1L <= WCD937X_ZDET_VAL_1200)) { + zdet_param_ptr = &zdet_param[2]; + d1 = d1_a[2]; + } else if (z1L > WCD937X_ZDET_VAL_1200) { + zdet_param_ptr = &zdet_param[3]; + d1 = d1_a[3]; + } + wcd937x_mbhc_zdet_ramp(codec, zdet_param_ptr, &z1L, NULL, d1); + +left_ch_impedance: + if ((z1L == WCD937X_ZDET_FLOATING_IMPEDANCE) || + (z1L > WCD937X_ZDET_VAL_100K)) { + *zl = WCD937X_ZDET_FLOATING_IMPEDANCE; + zdet_param_ptr = &zdet_param[1]; + d1 = d1_a[1]; + } else { + *zl = z1L/1000; + wcd937x_wcd_mbhc_qfuse_cal(codec, zl, 0); + } + dev_dbg(codec->dev, "%s: impedance on HPH_L = %d(ohms)\n", + __func__, *zl); + + /* Start of right impedance ramp and calculation */ + wcd937x_mbhc_zdet_ramp(codec, zdet_param_ptr, NULL, &z1R, d1); + if (WCD937X_MBHC_IS_SECOND_RAMP_REQUIRED(z1R)) { + if (((z1R > WCD937X_ZDET_VAL_1200) && + (zdet_param_ptr->noff == 0x6)) || + ((*zl) != WCD937X_ZDET_FLOATING_IMPEDANCE)) + goto right_ch_impedance; + /* Second ramp for right ch */ + if (z1R < WCD937X_ZDET_VAL_32) { + zdet_param_ptr = &zdet_param[0]; + d1 = d1_a[0]; + } else if ((z1R > WCD937X_ZDET_VAL_400) && + (z1R <= WCD937X_ZDET_VAL_1200)) { + zdet_param_ptr = &zdet_param[2]; + d1 = d1_a[2]; + } else if (z1R > WCD937X_ZDET_VAL_1200) { + zdet_param_ptr = &zdet_param[3]; + d1 = d1_a[3]; + } + wcd937x_mbhc_zdet_ramp(codec, zdet_param_ptr, NULL, &z1R, d1); + } +right_ch_impedance: + if ((z1R == WCD937X_ZDET_FLOATING_IMPEDANCE) || + (z1R > WCD937X_ZDET_VAL_100K)) { + *zr = WCD937X_ZDET_FLOATING_IMPEDANCE; + } else { + *zr = z1R/1000; + wcd937x_wcd_mbhc_qfuse_cal(codec, zr, 1); + } + dev_dbg(codec->dev, "%s: impedance on HPH_R = %d(ohms)\n", + __func__, *zr); + + /* Mono/stereo detection */ + if ((*zl == WCD937X_ZDET_FLOATING_IMPEDANCE) && + (*zr == WCD937X_ZDET_FLOATING_IMPEDANCE)) { + dev_dbg(codec->dev, + "%s: plug type is invalid or extension cable\n", + __func__); + goto zdet_complete; + } + if ((*zl == WCD937X_ZDET_FLOATING_IMPEDANCE) || + (*zr == WCD937X_ZDET_FLOATING_IMPEDANCE) || + ((*zl < WCD_MONO_HS_MIN_THR) && (*zr > WCD_MONO_HS_MIN_THR)) || + ((*zl > WCD_MONO_HS_MIN_THR) && (*zr < WCD_MONO_HS_MIN_THR))) { + dev_dbg(codec->dev, + "%s: Mono plug type with one ch floating or shorted to GND\n", + __func__); + mbhc->hph_type = WCD_MBHC_HPH_MONO; + goto zdet_complete; + } + snd_soc_update_bits(codec, WCD937X_HPH_R_ATEST, 0x02, 0x02); + snd_soc_update_bits(codec, WCD937X_HPH_PA_CTL2, 0x40, 0x01); + if (*zl < (WCD937X_ZDET_VAL_32/1000)) + wcd937x_mbhc_zdet_ramp(codec, &zdet_param[0], &z1Ls, NULL, d1); + else + wcd937x_mbhc_zdet_ramp(codec, &zdet_param[1], &z1Ls, NULL, d1); + snd_soc_update_bits(codec, WCD937X_HPH_PA_CTL2, 0x40, 0x00); + snd_soc_update_bits(codec, WCD937X_HPH_R_ATEST, 0x02, 0x00); + z1Ls /= 1000; + wcd937x_wcd_mbhc_qfuse_cal(codec, &z1Ls, 0); + /* Parallel of left Z and 9 ohm pull down resistor */ + zMono = ((*zl) * 9) / ((*zl) + 9); + z_diff1 = (z1Ls > zMono) ? (z1Ls - zMono) : (zMono - z1Ls); + z_diff2 = ((*zl) > z1Ls) ? ((*zl) - z1Ls) : (z1Ls - (*zl)); + if ((z_diff1 * (*zl + z1Ls)) > (z_diff2 * (z1Ls + zMono))) { + dev_dbg(codec->dev, "%s: stereo plug type detected\n", + __func__); + mbhc->hph_type = WCD_MBHC_HPH_STEREO; + } else { + dev_dbg(codec->dev, "%s: MONO plug type detected\n", + __func__); + mbhc->hph_type = WCD_MBHC_HPH_MONO; + } + +zdet_complete: + snd_soc_write(codec, WCD937X_ANA_MBHC_BTN5, reg0); + snd_soc_write(codec, WCD937X_ANA_MBHC_BTN6, reg1); + snd_soc_write(codec, WCD937X_ANA_MBHC_BTN7, reg2); + /* Turn on 100k pull down on HPHL */ + regmap_update_bits(wcd937x->regmap, + WCD937X_ANA_MBHC_MECH, 0x01, 0x01); + + /* For NO-jack, re-enable L_DET_EN after Z-det measurements */ + if (mbhc->hphl_swh) + regmap_update_bits(wcd937x->regmap, + WCD937X_ANA_MBHC_MECH, 0x80, 0x80); + + snd_soc_write(codec, WCD937X_MBHC_NEW_ZDET_ANA_CTL, reg4); + snd_soc_write(codec, WCD937X_MBHC_CTL_CLK, reg3); + if (is_fsm_disable) + regmap_update_bits(wcd937x->regmap, + WCD937X_ANA_MBHC_ELECT, 0x80, 0x80); +} + +static void wcd937x_mbhc_gnd_det_ctrl(struct snd_soc_codec *codec, bool enable) +{ + if (enable) { + snd_soc_update_bits(codec, WCD937X_ANA_MBHC_MECH, + 0x02, 0x02); + snd_soc_update_bits(codec, WCD937X_ANA_MBHC_MECH, + 0x40, 0x40); + } else { + snd_soc_update_bits(codec, WCD937X_ANA_MBHC_MECH, + 0x40, 0x00); + snd_soc_update_bits(codec, WCD937X_ANA_MBHC_MECH, + 0x02, 0x00); + } +} + +static void wcd937x_mbhc_hph_pull_down_ctrl(struct snd_soc_codec *codec, + bool enable) +{ + if (enable) { + snd_soc_update_bits(codec, WCD937X_HPH_PA_CTL2, + 0x40, 0x40); + snd_soc_update_bits(codec, WCD937X_HPH_PA_CTL2, + 0x10, 0x10); + } else { + snd_soc_update_bits(codec, WCD937X_HPH_PA_CTL2, + 0x40, 0x00); + snd_soc_update_bits(codec, WCD937X_HPH_PA_CTL2, + 0x10, 0x00); + } +} + +static void wcd937x_mbhc_moisture_config(struct wcd_mbhc *mbhc) +{ + struct snd_soc_codec *codec = mbhc->codec; + + if ((mbhc->moist_rref == R_OFF) || + (mbhc->mbhc_cfg->enable_usbc_analog)) { + snd_soc_update_bits(codec, WCD937X_MBHC_NEW_CTL_2, + 0x0C, R_OFF << 2); + return; + } + + /* Do not enable moisture detection if jack type is NC */ + if (!mbhc->hphl_swh) { + dev_dbg(codec->dev, "%s: disable moisture detection for NC\n", + __func__); + snd_soc_update_bits(codec, WCD937X_MBHC_NEW_CTL_2, + 0x0C, R_OFF << 2); + return; + } + + snd_soc_update_bits(codec, WCD937X_MBHC_NEW_CTL_2, + 0x0C, mbhc->moist_rref << 2); +} + +static void wcd937x_mbhc_moisture_detect_en(struct wcd_mbhc *mbhc, bool enable) +{ + struct snd_soc_codec *codec = mbhc->codec; + + if (enable) + snd_soc_update_bits(codec, WCD937X_MBHC_NEW_CTL_2, + 0x0C, mbhc->moist_rref << 2); + else + snd_soc_update_bits(codec, WCD937X_MBHC_NEW_CTL_2, + 0x0C, R_OFF << 2); +} + +static bool wcd937x_mbhc_get_moisture_status(struct wcd_mbhc *mbhc) +{ + struct snd_soc_codec *codec = mbhc->codec; + bool ret = false; + + if ((mbhc->moist_rref == R_OFF) || + (mbhc->mbhc_cfg->enable_usbc_analog)) { + snd_soc_update_bits(codec, WCD937X_MBHC_NEW_CTL_2, + 0x0C, R_OFF << 2); + goto done; + } + + /* Do not enable moisture detection if jack type is NC */ + if (!mbhc->hphl_swh) { + dev_dbg(codec->dev, "%s: disable moisture detection for NC\n", + __func__); + snd_soc_update_bits(codec, WCD937X_MBHC_NEW_CTL_2, + 0x0C, R_OFF << 2); + goto done; + } + + /* If moisture_en is already enabled, then skip to plug type + * detection. + */ + if ((snd_soc_read(codec, WCD937X_MBHC_NEW_CTL_2) & 0x0C)) + goto done; + + wcd937x_mbhc_moisture_detect_en(mbhc, true); + /* Read moisture comparator status */ + ret = ((snd_soc_read(codec, WCD937X_MBHC_NEW_FSM_STATUS) + & 0x20) ? 0 : 1); + +done: + return ret; + +} + +static void wcd937x_mbhc_moisture_polling_ctrl(struct wcd_mbhc *mbhc, + bool enable) +{ + struct snd_soc_codec *codec = mbhc->codec; + + snd_soc_update_bits(codec, + WCD937X_MBHC_NEW_INT_MOISTURE_DET_POLLING_CTRL, + 0x04, (enable << 2)); +} + +static const struct wcd_mbhc_cb mbhc_cb = { + .request_irq = wcd937x_mbhc_request_irq, + .irq_control = wcd937x_mbhc_irq_control, + .free_irq = wcd937x_mbhc_free_irq, + .clk_setup = wcd937x_mbhc_clk_setup, + .map_btn_code_to_num = wcd937x_mbhc_btn_to_num, + .mbhc_bias = wcd937x_mbhc_mbhc_bias_control, + .set_btn_thr = wcd937x_mbhc_program_btn_thr, + .lock_sleep = wcd937x_mbhc_lock_sleep, + .register_notifier = wcd937x_mbhc_register_notifier, + .micbias_enable_status = wcd937x_mbhc_micb_en_status, + .hph_pa_on_status = wcd937x_mbhc_hph_pa_on_status, + .hph_pull_up_control_v2 = wcd937x_mbhc_hph_l_pull_up_control, + .mbhc_micbias_control = wcd937x_mbhc_request_micbias, + .mbhc_micb_ramp_control = wcd937x_mbhc_micb_ramp_control, + .get_hwdep_fw_cal = wcd937x_get_hwdep_fw_cal, + .mbhc_micb_ctrl_thr_mic = wcd937x_mbhc_micb_ctrl_threshold_mic, + .compute_impedance = wcd937x_wcd_mbhc_calc_impedance, + .mbhc_gnd_det_ctrl = wcd937x_mbhc_gnd_det_ctrl, + .hph_pull_down_ctrl = wcd937x_mbhc_hph_pull_down_ctrl, + .mbhc_moisture_config = wcd937x_mbhc_moisture_config, + .mbhc_get_moisture_status = wcd937x_mbhc_get_moisture_status, + .mbhc_moisture_polling_ctrl = wcd937x_mbhc_moisture_polling_ctrl, + .mbhc_moisture_detect_en = wcd937x_mbhc_moisture_detect_en, +}; + +static int wcd937x_get_hph_type(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct wcd937x_mbhc *wcd937x_mbhc = wcd937x_soc_get_mbhc(codec); + struct wcd_mbhc *mbhc; + + if (!wcd937x_mbhc) { + dev_err(codec->dev, "%s: mbhc not initialized!\n", __func__); + return -EINVAL; + } + + mbhc = &wcd937x_mbhc->wcd_mbhc; + + ucontrol->value.integer.value[0] = (u32) mbhc->hph_type; + dev_dbg(codec->dev, "%s: hph_type = %u\n", __func__, mbhc->hph_type); + + return 0; +} + +static int wcd937x_hph_impedance_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + uint32_t zl, zr; + bool hphr; + struct soc_multi_mixer_control *mc; + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct wcd937x_mbhc *wcd937x_mbhc = wcd937x_soc_get_mbhc(codec); + + if (!wcd937x_mbhc) { + dev_err(codec->dev, "%s: mbhc not initialized!\n", __func__); + return -EINVAL; + } + + mc = (struct soc_multi_mixer_control *)(kcontrol->private_value); + hphr = mc->shift; + wcd_mbhc_get_impedance(&wcd937x_mbhc->wcd_mbhc, &zl, &zr); + dev_dbg(codec->dev, "%s: zl=%u(ohms), zr=%u(ohms)\n", __func__, zl, zr); + ucontrol->value.integer.value[0] = hphr ? zr : zl; + + return 0; +} + +static const struct snd_kcontrol_new hph_type_detect_controls[] = { + SOC_SINGLE_EXT("HPH Type", 0, 0, UINT_MAX, 0, + wcd937x_get_hph_type, NULL), +}; + +static const struct snd_kcontrol_new impedance_detect_controls[] = { + SOC_SINGLE_EXT("HPHL Impedance", 0, 0, UINT_MAX, 0, + wcd937x_hph_impedance_get, NULL), + SOC_SINGLE_EXT("HPHR Impedance", 0, 1, UINT_MAX, 0, + wcd937x_hph_impedance_get, NULL), +}; + +/* + * wcd937x_mbhc_get_impedance: get impedance of headphone + * left and right channels + * @wcd937x_mbhc: handle to struct wcd937x_mbhc * + * @zl: handle to left-ch impedance + * @zr: handle to right-ch impedance + * return 0 for success or error code in case of failure + */ +int wcd937x_mbhc_get_impedance(struct wcd937x_mbhc *wcd937x_mbhc, + uint32_t *zl, uint32_t *zr) +{ + if (!wcd937x_mbhc) { + pr_err("%s: mbhc not initialized!\n", __func__); + return -EINVAL; + } + if (!zl || !zr) { + pr_err("%s: zl or zr null!\n", __func__); + return -EINVAL; + } + + return wcd_mbhc_get_impedance(&wcd937x_mbhc->wcd_mbhc, zl, zr); +} +EXPORT_SYMBOL(wcd937x_mbhc_get_impedance); + +/* + * wcd937x_mbhc_hs_detect: starts mbhc insertion/removal functionality + * @codec: handle to snd_soc_codec * + * @mbhc_cfg: handle to mbhc configuration structure + * return 0 if mbhc_start is success or error code in case of failure + */ +int wcd937x_mbhc_hs_detect(struct snd_soc_codec *codec, + struct wcd_mbhc_config *mbhc_cfg) +{ + struct wcd937x_priv *wcd937x = NULL; + struct wcd937x_mbhc *wcd937x_mbhc = NULL; + + if (!codec) { + pr_err("%s: codec is NULL\n", __func__); + return -EINVAL; + } + + wcd937x = snd_soc_codec_get_drvdata(codec); + if (!wcd937x) { + pr_err("%s: wcd937x is NULL\n", __func__); + return -EINVAL; + } + + wcd937x_mbhc = wcd937x->mbhc; + if (!wcd937x_mbhc) { + dev_err(codec->dev, "%s: mbhc not initialized!\n", __func__); + return -EINVAL; + } + + return wcd_mbhc_start(&wcd937x_mbhc->wcd_mbhc, mbhc_cfg); +} +EXPORT_SYMBOL(wcd937x_mbhc_hs_detect); + +/* + * wcd937x_mbhc_hs_detect_exit: stop mbhc insertion/removal functionality + * @codec: handle to snd_soc_codec * + */ +void wcd937x_mbhc_hs_detect_exit(struct snd_soc_codec *codec) +{ + struct wcd937x_priv *wcd937x = NULL; + struct wcd937x_mbhc *wcd937x_mbhc = NULL; + + if (!codec) { + pr_err("%s: codec is NULL\n", __func__); + return; + } + + wcd937x = snd_soc_codec_get_drvdata(codec); + if (!wcd937x) { + pr_err("%s: wcd937x is NULL\n", __func__); + return; + } + + wcd937x_mbhc = wcd937x->mbhc; + if (!wcd937x_mbhc) { + dev_err(codec->dev, "%s: mbhc not initialized!\n", __func__); + return; + } + wcd_mbhc_stop(&wcd937x_mbhc->wcd_mbhc); +} +EXPORT_SYMBOL(wcd937x_mbhc_hs_detect_exit); + +/* + * wcd937x_mbhc_ssr_down: stop mbhc during + * wcd937x subsystem restart + * @mbhc: pointer to wcd937x_mbhc structure + * @codec: handle to snd_soc_codec * + */ +void wcd937x_mbhc_ssr_down(struct wcd937x_mbhc *mbhc, + struct snd_soc_codec *codec) +{ + struct wcd_mbhc *wcd_mbhc = NULL; + + if (!mbhc || !codec) + return; + + wcd_mbhc = &mbhc->wcd_mbhc; + if (wcd_mbhc == NULL) { + dev_err(codec->dev, "%s: wcd_mbhc is NULL\n", __func__); + return; + } + + wcd937x_mbhc_hs_detect_exit(codec); + wcd_mbhc_deinit(wcd_mbhc); +} +EXPORT_SYMBOL(wcd937x_mbhc_ssr_down); + +/* + * wcd937x_mbhc_post_ssr_init: initialize mbhc for + * wcd937x post subsystem restart + * @mbhc: poniter to wcd937x_mbhc structure + * @codec: handle to snd_soc_codec * + * + * return 0 if mbhc_init is success or error code in case of failure + */ +int wcd937x_mbhc_post_ssr_init(struct wcd937x_mbhc *mbhc, + struct snd_soc_codec *codec) +{ + int ret = 0; + struct wcd_mbhc *wcd_mbhc = NULL; + + if (!mbhc || !codec) + return -EINVAL; + + wcd_mbhc = &mbhc->wcd_mbhc; + if (wcd_mbhc == NULL) { + pr_err("%s: wcd_mbhc is NULL\n", __func__); + return -EINVAL; + } + + snd_soc_update_bits(codec, WCD937X_ANA_MBHC_MECH, + 0x20, 0x20); + ret = wcd_mbhc_init(wcd_mbhc, codec, &mbhc_cb, &intr_ids, + wcd_mbhc_registers, WCD937X_ZDET_SUPPORTED); + if (ret) { + dev_err(codec->dev, "%s: mbhc initialization failed\n", + __func__); + goto done; + } + +done: + return ret; +} +EXPORT_SYMBOL(wcd937x_mbhc_post_ssr_init); + +/* + * wcd937x_mbhc_init: initialize mbhc for wcd937x + * @mbhc: poniter to wcd937x_mbhc struct pointer to store the configs + * @codec: handle to snd_soc_codec * + * @fw_data: handle to firmware data + * + * return 0 if mbhc_init is success or error code in case of failure + */ +int wcd937x_mbhc_init(struct wcd937x_mbhc **mbhc, struct snd_soc_codec *codec, + struct fw_info *fw_data) +{ + struct wcd937x_mbhc *wcd937x_mbhc = NULL; + struct wcd_mbhc *wcd_mbhc = NULL; + int ret = 0; + + if (!codec) { + pr_err("%s: codec is NULL\n", __func__); + return -EINVAL; + } + + wcd937x_mbhc = devm_kzalloc(codec->dev, sizeof(struct wcd937x_mbhc), + GFP_KERNEL); + if (!wcd937x_mbhc) + return -ENOMEM; + + wcd937x_mbhc->fw_data = fw_data; + BLOCKING_INIT_NOTIFIER_HEAD(&wcd937x_mbhc->notifier); + wcd_mbhc = &wcd937x_mbhc->wcd_mbhc; + if (wcd_mbhc == NULL) { + pr_err("%s: wcd_mbhc is NULL\n", __func__); + ret = -EINVAL; + goto err; + } + + + /* Setting default mbhc detection logic to ADC */ + wcd_mbhc->mbhc_detection_logic = WCD_DETECTION_ADC; + + ret = wcd_mbhc_init(wcd_mbhc, codec, &mbhc_cb, + &intr_ids, wcd_mbhc_registers, + WCD937X_ZDET_SUPPORTED); + if (ret) { + dev_err(codec->dev, "%s: mbhc initialization failed\n", + __func__); + goto err; + } + + (*mbhc) = wcd937x_mbhc; + snd_soc_add_codec_controls(codec, impedance_detect_controls, + ARRAY_SIZE(impedance_detect_controls)); + snd_soc_add_codec_controls(codec, hph_type_detect_controls, + ARRAY_SIZE(hph_type_detect_controls)); + + return 0; +err: + devm_kfree(codec->dev, wcd937x_mbhc); + return ret; +} +EXPORT_SYMBOL(wcd937x_mbhc_init); + +/* + * wcd937x_mbhc_deinit: deinitialize mbhc for wcd937x + * @codec: handle to snd_soc_codec * + */ +void wcd937x_mbhc_deinit(struct snd_soc_codec *codec) +{ + struct wcd937x_priv *wcd937x; + struct wcd937x_mbhc *wcd937x_mbhc; + + if (!codec) { + pr_err("%s: codec is NULL\n", __func__); + return; + } + + wcd937x = snd_soc_codec_get_drvdata(codec); + if (!wcd937x) { + pr_err("%s: wcd937x is NULL\n", __func__); + return; + } + + wcd937x_mbhc = wcd937x->mbhc; + if (wcd937x_mbhc) { + wcd_mbhc_deinit(&wcd937x_mbhc->wcd_mbhc); + devm_kfree(codec->dev, wcd937x_mbhc); + } +} +EXPORT_SYMBOL(wcd937x_mbhc_deinit); diff --git a/audio-kernel/asoc/codecs/wcd937x/wcd937x-mbhc.h b/audio-kernel/asoc/codecs/wcd937x/wcd937x-mbhc.h new file mode 100644 index 000000000..634cba6cb --- /dev/null +++ b/audio-kernel/asoc/codecs/wcd937x/wcd937x-mbhc.h @@ -0,0 +1,74 @@ +/* Copyright (c) 2018-2019, 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 __WCD937X_MBHC_H__ +#define __WCD937X_MBHC_H__ +#include "../wcd-mbhc-v2.h" + +struct wcd937x_mbhc { + struct wcd_mbhc wcd_mbhc; + struct blocking_notifier_head notifier; + struct fw_info *fw_data; +}; + +#if IS_ENABLED(CONFIG_SND_SOC_WCD937X) +extern int wcd937x_mbhc_init(struct wcd937x_mbhc **mbhc, + struct snd_soc_codec *codec, + struct fw_info *fw_data); +extern void wcd937x_mbhc_hs_detect_exit(struct snd_soc_codec *codec); +extern int wcd937x_mbhc_hs_detect(struct snd_soc_codec *codec, + struct wcd_mbhc_config *mbhc_cfg); +extern void wcd937x_mbhc_deinit(struct snd_soc_codec *codec); +extern int wcd937x_mbhc_post_ssr_init(struct wcd937x_mbhc *mbhc, + struct snd_soc_codec *codec); +extern void wcd937x_mbhc_ssr_down(struct wcd937x_mbhc *mbhc, + struct snd_soc_codec *codec); +extern int wcd937x_mbhc_get_impedance(struct wcd937x_mbhc *wcd937x_mbhc, + uint32_t *zl, uint32_t *zr); +#else +static inline int wcd937x_mbhc_init(struct wcd937x_mbhc **mbhc, + struct snd_soc_codec *codec, + struct fw_info *fw_data) +{ + return 0; +} +static inline void wcd937x_mbhc_hs_detect_exit(struct snd_soc_codec *codec) +{ +} +static inline int wcd937x_mbhc_hs_detect(struct snd_soc_codec *codec, + struct wcd_mbhc_config *mbhc_cfg) +{ + return 0; +} +static inline void wcd937x_mbhc_deinit(struct snd_soc_codec *codec) +{ +} +static inline int wcd937x_mbhc_post_ssr_init(struct wcd937x_mbhc *mbhc, + struct snd_soc_codec *codec) +{ + return 0; +} +static inline void wcd937x_mbhc_ssr_down(struct wcd937x_mbhc *mbhc, + struct snd_soc_codec *codec) +{ +} +static inline int wcd937x_mbhc_get_impedance(struct wcd937x_mbhc *wcd937x_mbhc, + uint32_t *zl, uint32_t *zr) +{ + if (zl) + *zl = 0; + if (zr) + *zr = 0; + return -EINVAL; +} +#endif + +#endif /* __WCD937X_MBHC_H__ */ diff --git a/audio-kernel/asoc/codecs/wcd937x/wcd937x-registers.h b/audio-kernel/asoc/codecs/wcd937x/wcd937x-registers.h new file mode 100644 index 000000000..1f89f803c --- /dev/null +++ b/audio-kernel/asoc/codecs/wcd937x/wcd937x-registers.h @@ -0,0 +1,446 @@ +/* + * 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 _WCD937X_REGISTERS_H +#define _WCD937X_REGISTERS_H + +#define WCD937X_BASE_ADDRESS 0x3000 + +#define WCD937X_REG(reg) (reg - WCD937X_BASE_ADDRESS) + +enum { + REG_NO_ACCESS, + RD_REG, + WR_REG, + RD_WR_REG +}; + +#define WCD937X_ANA_BIAS (WCD937X_BASE_ADDRESS+0x001) +#define WCD937X_ANA_RX_SUPPLIES (WCD937X_BASE_ADDRESS+0x008) +#define WCD937X_ANA_HPH (WCD937X_BASE_ADDRESS+0x009) +#define WCD937X_ANA_EAR (WCD937X_BASE_ADDRESS+0x00A) +#define WCD937X_ANA_EAR_COMPANDER_CTL (WCD937X_BASE_ADDRESS+0x00B) +#define WCD937X_ANA_TX_CH1 (WCD937X_BASE_ADDRESS+0x00E) +#define WCD937X_ANA_TX_CH2 (WCD937X_BASE_ADDRESS+0x00F) +#define WCD937X_ANA_TX_CH3 (WCD937X_BASE_ADDRESS+0x010) +#define WCD937X_ANA_TX_CH3_HPF (WCD937X_BASE_ADDRESS+0x011) +#define WCD937X_ANA_MICB1_MICB2_DSP_EN_LOGIC (WCD937X_BASE_ADDRESS+0x012) +#define WCD937X_ANA_MICB3_DSP_EN_LOGIC (WCD937X_BASE_ADDRESS+0x013) +#define WCD937X_ANA_MBHC_MECH (WCD937X_BASE_ADDRESS+0x014) +#define WCD937X_ANA_MBHC_ELECT (WCD937X_BASE_ADDRESS+0x015) +#define WCD937X_ANA_MBHC_ZDET (WCD937X_BASE_ADDRESS+0x016) +#define WCD937X_ANA_MBHC_RESULT_1 (WCD937X_BASE_ADDRESS+0x017) +#define WCD937X_ANA_MBHC_RESULT_2 (WCD937X_BASE_ADDRESS+0x018) +#define WCD937X_ANA_MBHC_RESULT_3 (WCD937X_BASE_ADDRESS+0x019) +#define WCD937X_ANA_MBHC_BTN0 (WCD937X_BASE_ADDRESS+0x01A) +#define WCD937X_ANA_MBHC_BTN1 (WCD937X_BASE_ADDRESS+0x01B) +#define WCD937X_ANA_MBHC_BTN2 (WCD937X_BASE_ADDRESS+0x01C) +#define WCD937X_ANA_MBHC_BTN3 (WCD937X_BASE_ADDRESS+0x01D) +#define WCD937X_ANA_MBHC_BTN4 (WCD937X_BASE_ADDRESS+0x01E) +#define WCD937X_ANA_MBHC_BTN5 (WCD937X_BASE_ADDRESS+0x01F) +#define WCD937X_ANA_MBHC_BTN6 (WCD937X_BASE_ADDRESS+0x020) +#define WCD937X_ANA_MBHC_BTN7 (WCD937X_BASE_ADDRESS+0x021) +#define WCD937X_ANA_MICB1 (WCD937X_BASE_ADDRESS+0x022) +#define WCD937X_ANA_MICB2 (WCD937X_BASE_ADDRESS+0x023) +#define WCD937X_ANA_MICB2_RAMP (WCD937X_BASE_ADDRESS+0x024) +#define WCD937X_ANA_MICB3 (WCD937X_BASE_ADDRESS+0x025) +#define WCD937X_BIAS_CTL (WCD937X_BASE_ADDRESS+0x028) +#define WCD937X_BIAS_VBG_FINE_ADJ (WCD937X_BASE_ADDRESS+0x029) +#define WCD937X_LDOL_VDDCX_ADJUST (WCD937X_BASE_ADDRESS+0x040) +#define WCD937X_LDOL_DISABLE_LDOL (WCD937X_BASE_ADDRESS+0x041) +#define WCD937X_MBHC_CTL_CLK (WCD937X_BASE_ADDRESS+0x056) +#define WCD937X_MBHC_CTL_ANA (WCD937X_BASE_ADDRESS+0x057) +#define WCD937X_MBHC_CTL_SPARE_1 (WCD937X_BASE_ADDRESS+0x058) +#define WCD937X_MBHC_CTL_SPARE_2 (WCD937X_BASE_ADDRESS+0x059) +#define WCD937X_MBHC_CTL_BCS (WCD937X_BASE_ADDRESS+0x05A) +#define WCD937X_MBHC_MOISTURE_DET_FSM_STATUS (WCD937X_BASE_ADDRESS+0x05B) +#define WCD937X_MBHC_TEST_CTL (WCD937X_BASE_ADDRESS+0x05C) +#define WCD937X_LDOH_MODE (WCD937X_BASE_ADDRESS+0x067) +#define WCD937X_LDOH_BIAS (WCD937X_BASE_ADDRESS+0x068) +#define WCD937X_LDOH_STB_LOADS (WCD937X_BASE_ADDRESS+0x069) +#define WCD937X_LDOH_SLOWRAMP (WCD937X_BASE_ADDRESS+0x06A) +#define WCD937X_MICB1_TEST_CTL_1 (WCD937X_BASE_ADDRESS+0x06B) +#define WCD937X_MICB1_TEST_CTL_2 (WCD937X_BASE_ADDRESS+0x06C) +#define WCD937X_MICB1_TEST_CTL_3 (WCD937X_BASE_ADDRESS+0x06D) +#define WCD937X_MICB2_TEST_CTL_1 (WCD937X_BASE_ADDRESS+0x06E) +#define WCD937X_MICB2_TEST_CTL_2 (WCD937X_BASE_ADDRESS+0x06F) +#define WCD937X_MICB2_TEST_CTL_3 (WCD937X_BASE_ADDRESS+0x070) +#define WCD937X_MICB3_TEST_CTL_1 (WCD937X_BASE_ADDRESS+0x071) +#define WCD937X_MICB3_TEST_CTL_2 (WCD937X_BASE_ADDRESS+0x072) +#define WCD937X_MICB3_TEST_CTL_3 (WCD937X_BASE_ADDRESS+0x073) +#define WCD937X_TX_COM_ADC_VCM (WCD937X_BASE_ADDRESS+0x077) +#define WCD937X_TX_COM_BIAS_ATEST (WCD937X_BASE_ADDRESS+0x078) +#define WCD937X_TX_COM_ADC_INT1_IB (WCD937X_BASE_ADDRESS+0x079) +#define WCD937X_TX_COM_ADC_INT2_IB (WCD937X_BASE_ADDRESS+0x07A) +#define WCD937X_TX_COM_TXFE_DIV_CTL (WCD937X_BASE_ADDRESS+0x07B) +#define WCD937X_TX_COM_TXFE_DIV_START (WCD937X_BASE_ADDRESS+0x07C) +#define WCD937X_TX_COM_TXFE_DIV_STOP_9P6M (WCD937X_BASE_ADDRESS+0x07D) +#define WCD937X_TX_COM_TXFE_DIV_STOP_12P288M (WCD937X_BASE_ADDRESS+0x07E) +#define WCD937X_TX_1_2_TEST_EN (WCD937X_BASE_ADDRESS+0x07F) +#define WCD937X_TX_1_2_ADC_IB (WCD937X_BASE_ADDRESS+0x080) +#define WCD937X_TX_1_2_ATEST_REFCTL (WCD937X_BASE_ADDRESS+0x081) +#define WCD937X_TX_1_2_TEST_CTL (WCD937X_BASE_ADDRESS+0x082) +#define WCD937X_TX_1_2_TEST_BLK_EN (WCD937X_BASE_ADDRESS+0x083) +#define WCD937X_TX_1_2_TXFE_CLKDIV (WCD937X_BASE_ADDRESS+0x084) +#define WCD937X_TX_1_2_SAR2_ERR (WCD937X_BASE_ADDRESS+0x085) +#define WCD937X_TX_1_2_SAR1_ERR (WCD937X_BASE_ADDRESS+0x086) +#define WCD937X_TX_3_TEST_EN (WCD937X_BASE_ADDRESS+0x087) +#define WCD937X_TX_3_ADC_IB (WCD937X_BASE_ADDRESS+0x088) +#define WCD937X_TX_3_ATEST_REFCTL (WCD937X_BASE_ADDRESS+0x089) +#define WCD937X_TX_3_TEST_CTL (WCD937X_BASE_ADDRESS+0x08A) +#define WCD937X_TX_3_TEST_BLK_EN (WCD937X_BASE_ADDRESS+0x08B) +#define WCD937X_TX_3_TXFE_CLKDIV (WCD937X_BASE_ADDRESS+0x08C) +#define WCD937X_TX_3_SPARE_MONO (WCD937X_BASE_ADDRESS+0x08D) +#define WCD937X_TX_3_SAR1_ERR (WCD937X_BASE_ADDRESS+0x08E) +#define WCD937X_CLASSH_MODE_1 (WCD937X_BASE_ADDRESS+0x097) +#define WCD937X_CLASSH_MODE_2 (WCD937X_BASE_ADDRESS+0x098) +#define WCD937X_CLASSH_MODE_3 (WCD937X_BASE_ADDRESS+0x099) +#define WCD937X_CLASSH_CTRL_VCL_1 (WCD937X_BASE_ADDRESS+0x09A) +#define WCD937X_CLASSH_CTRL_VCL_2 (WCD937X_BASE_ADDRESS+0x09B) +#define WCD937X_CLASSH_CTRL_CCL_1 (WCD937X_BASE_ADDRESS+0x09C) +#define WCD937X_CLASSH_CTRL_CCL_2 (WCD937X_BASE_ADDRESS+0x09D) +#define WCD937X_CLASSH_CTRL_CCL_3 (WCD937X_BASE_ADDRESS+0x09E) +#define WCD937X_CLASSH_CTRL_CCL_4 (WCD937X_BASE_ADDRESS+0x09F) +#define WCD937X_CLASSH_CTRL_CCL_5 (WCD937X_BASE_ADDRESS+0x0A0) +#define WCD937X_CLASSH_BUCK_TMUX_A_D (WCD937X_BASE_ADDRESS+0x0A1) +#define WCD937X_CLASSH_BUCK_SW_DRV_CNTL (WCD937X_BASE_ADDRESS+0x0A2) +#define WCD937X_CLASSH_SPARE (WCD937X_BASE_ADDRESS+0x0A3) +#define WCD937X_FLYBACK_EN (WCD937X_BASE_ADDRESS+0x0A4) +#define WCD937X_FLYBACK_VNEG_CTRL_1 (WCD937X_BASE_ADDRESS+0x0A5) +#define WCD937X_FLYBACK_VNEG_CTRL_2 (WCD937X_BASE_ADDRESS+0x0A6) +#define WCD937X_FLYBACK_VNEG_CTRL_3 (WCD937X_BASE_ADDRESS+0x0A7) +#define WCD937X_FLYBACK_VNEG_CTRL_4 (WCD937X_BASE_ADDRESS+0x0A8) +#define WCD937X_FLYBACK_VNEG_CTRL_5 (WCD937X_BASE_ADDRESS+0x0A9) +#define WCD937X_FLYBACK_VNEG_CTRL_6 (WCD937X_BASE_ADDRESS+0x0AA) +#define WCD937X_FLYBACK_VNEG_CTRL_7 (WCD937X_BASE_ADDRESS+0x0AB) +#define WCD937X_FLYBACK_VNEG_CTRL_8 (WCD937X_BASE_ADDRESS+0x0AC) +#define WCD937X_FLYBACK_VNEG_CTRL_9 (WCD937X_BASE_ADDRESS+0x0AD) +#define WCD937X_FLYBACK_VNEGDAC_CTRL_1 (WCD937X_BASE_ADDRESS+0x0AE) +#define WCD937X_FLYBACK_VNEGDAC_CTRL_2 (WCD937X_BASE_ADDRESS+0x0AF) +#define WCD937X_FLYBACK_VNEGDAC_CTRL_3 (WCD937X_BASE_ADDRESS+0x0B0) +#define WCD937X_FLYBACK_CTRL_1 (WCD937X_BASE_ADDRESS+0x0B1) +#define WCD937X_FLYBACK_TEST_CTL (WCD937X_BASE_ADDRESS+0x0B2) +#define WCD937X_RX_AUX_SW_CTL (WCD937X_BASE_ADDRESS+0x0B3) +#define WCD937X_RX_PA_AUX_IN_CONN (WCD937X_BASE_ADDRESS+0x0B4) +#define WCD937X_RX_TIMER_DIV (WCD937X_BASE_ADDRESS+0x0B5) +#define WCD937X_RX_OCP_CTL (WCD937X_BASE_ADDRESS+0x0B6) +#define WCD937X_RX_OCP_COUNT (WCD937X_BASE_ADDRESS+0x0B7) +#define WCD937X_RX_BIAS_EAR_DAC (WCD937X_BASE_ADDRESS+0x0B8) +#define WCD937X_RX_BIAS_EAR_AMP (WCD937X_BASE_ADDRESS+0x0B9) +#define WCD937X_RX_BIAS_HPH_LDO (WCD937X_BASE_ADDRESS+0x0BA) +#define WCD937X_RX_BIAS_HPH_PA (WCD937X_BASE_ADDRESS+0x0BB) +#define WCD937X_RX_BIAS_HPH_RDACBUFF_CNP2 (WCD937X_BASE_ADDRESS+0x0BC) +#define WCD937X_RX_BIAS_HPH_RDAC_LDO (WCD937X_BASE_ADDRESS+0x0BD) +#define WCD937X_RX_BIAS_HPH_CNP1 (WCD937X_BASE_ADDRESS+0x0BE) +#define WCD937X_RX_BIAS_HPH_LOWPOWER (WCD937X_BASE_ADDRESS+0x0BF) +#define WCD937X_RX_BIAS_AUX_DAC (WCD937X_BASE_ADDRESS+0x0C0) +#define WCD937X_RX_BIAS_AUX_AMP (WCD937X_BASE_ADDRESS+0x0C1) +#define WCD937X_RX_BIAS_VNEGDAC_BLEEDER (WCD937X_BASE_ADDRESS+0x0C2) +#define WCD937X_RX_BIAS_MISC (WCD937X_BASE_ADDRESS+0x0C3) +#define WCD937X_RX_BIAS_BUCK_RST (WCD937X_BASE_ADDRESS+0x0C4) +#define WCD937X_RX_BIAS_BUCK_VREF_ERRAMP (WCD937X_BASE_ADDRESS+0x0C5) +#define WCD937X_RX_BIAS_FLYB_ERRAMP (WCD937X_BASE_ADDRESS+0x0C6) +#define WCD937X_RX_BIAS_FLYB_BUFF (WCD937X_BASE_ADDRESS+0x0C7) +#define WCD937X_RX_BIAS_FLYB_MID_RST (WCD937X_BASE_ADDRESS+0x0C8) +#define WCD937X_HPH_L_STATUS (WCD937X_BASE_ADDRESS+0x0C9) +#define WCD937X_HPH_R_STATUS (WCD937X_BASE_ADDRESS+0x0CA) +#define WCD937X_HPH_CNP_EN (WCD937X_BASE_ADDRESS+0x0CB) +#define WCD937X_HPH_CNP_WG_CTL (WCD937X_BASE_ADDRESS+0x0CC) +#define WCD937X_HPH_CNP_WG_TIME (WCD937X_BASE_ADDRESS+0x0CD) +#define WCD937X_HPH_OCP_CTL (WCD937X_BASE_ADDRESS+0x0CE) +#define WCD937X_HPH_AUTO_CHOP (WCD937X_BASE_ADDRESS+0x0CF) +#define WCD937X_HPH_CHOP_CTL (WCD937X_BASE_ADDRESS+0x0D0) +#define WCD937X_HPH_PA_CTL1 (WCD937X_BASE_ADDRESS+0x0D1) +#define WCD937X_HPH_PA_CTL2 (WCD937X_BASE_ADDRESS+0x0D2) +#define WCD937X_HPH_L_EN (WCD937X_BASE_ADDRESS+0x0D3) +#define WCD937X_HPH_L_TEST (WCD937X_BASE_ADDRESS+0x0D4) +#define WCD937X_HPH_L_ATEST (WCD937X_BASE_ADDRESS+0x0D5) +#define WCD937X_HPH_R_EN (WCD937X_BASE_ADDRESS+0x0D6) +#define WCD937X_HPH_R_TEST (WCD937X_BASE_ADDRESS+0x0D7) +#define WCD937X_HPH_R_ATEST (WCD937X_BASE_ADDRESS+0x0D8) +#define WCD937X_HPH_RDAC_CLK_CTL1 (WCD937X_BASE_ADDRESS+0x0D9) +#define WCD937X_HPH_RDAC_CLK_CTL2 (WCD937X_BASE_ADDRESS+0x0DA) +#define WCD937X_HPH_RDAC_LDO_CTL (WCD937X_BASE_ADDRESS+0x0DB) +#define WCD937X_HPH_RDAC_CHOP_CLK_LP_CTL (WCD937X_BASE_ADDRESS+0x0DC) +#define WCD937X_HPH_REFBUFF_UHQA_CTL (WCD937X_BASE_ADDRESS+0x0DD) +#define WCD937X_HPH_REFBUFF_LP_CTL (WCD937X_BASE_ADDRESS+0x0DE) +#define WCD937X_HPH_L_DAC_CTL (WCD937X_BASE_ADDRESS+0x0DF) +#define WCD937X_HPH_R_DAC_CTL (WCD937X_BASE_ADDRESS+0x0E0) +#define WCD937X_HPH_SURGE_HPHLR_SURGE_COMP_SEL (WCD937X_BASE_ADDRESS+0x0E1) +#define WCD937X_HPH_SURGE_HPHLR_SURGE_EN (WCD937X_BASE_ADDRESS+0x0E2) +#define WCD937X_HPH_SURGE_HPHLR_SURGE_MISC1 (WCD937X_BASE_ADDRESS+0x0E3) +#define WCD937X_HPH_SURGE_HPHLR_SURGE_STATUS (WCD937X_BASE_ADDRESS+0x0E4) +#define WCD937X_EAR_EAR_EN_REG (WCD937X_BASE_ADDRESS+0x0E9) +#define WCD937X_EAR_EAR_PA_CON (WCD937X_BASE_ADDRESS+0x0EA) +#define WCD937X_EAR_EAR_SP_CON (WCD937X_BASE_ADDRESS+0x0EB) +#define WCD937X_EAR_EAR_DAC_CON (WCD937X_BASE_ADDRESS+0x0EC) +#define WCD937X_EAR_EAR_CNP_FSM_CON (WCD937X_BASE_ADDRESS+0x0ED) +#define WCD937X_EAR_TEST_CTL (WCD937X_BASE_ADDRESS+0x0EE) +#define WCD937X_EAR_STATUS_REG_1 (WCD937X_BASE_ADDRESS+0x0EF) +#define WCD937X_EAR_STATUS_REG_2 (WCD937X_BASE_ADDRESS+0x0F0) +#define WCD937X_ANA_NEW_PAGE_REGISTER (WCD937X_BASE_ADDRESS+0x100) +#define WCD937X_HPH_NEW_ANA_HPH2 (WCD937X_BASE_ADDRESS+0x101) +#define WCD937X_HPH_NEW_ANA_HPH3 (WCD937X_BASE_ADDRESS+0x102) +#define WCD937X_SLEEP_CTL (WCD937X_BASE_ADDRESS+0x103) +#define WCD937X_SLEEP_WATCHDOG_CTL (WCD937X_BASE_ADDRESS+0x104) +#define WCD937X_MBHC_NEW_ELECT_REM_CLAMP_CTL (WCD937X_BASE_ADDRESS+0x11F) +#define WCD937X_MBHC_NEW_CTL_1 (WCD937X_BASE_ADDRESS+0x120) +#define WCD937X_MBHC_NEW_CTL_2 (WCD937X_BASE_ADDRESS+0x121) +#define WCD937X_MBHC_NEW_PLUG_DETECT_CTL (WCD937X_BASE_ADDRESS+0x122) +#define WCD937X_MBHC_NEW_ZDET_ANA_CTL (WCD937X_BASE_ADDRESS+0x123) +#define WCD937X_MBHC_NEW_ZDET_RAMP_CTL (WCD937X_BASE_ADDRESS+0x124) +#define WCD937X_MBHC_NEW_FSM_STATUS (WCD937X_BASE_ADDRESS+0x125) +#define WCD937X_MBHC_NEW_ADC_RESULT (WCD937X_BASE_ADDRESS+0x126) +#define WCD937X_TX_NEW_TX_CH2_SEL (WCD937X_BASE_ADDRESS+0x127) +#define WCD937X_AUX_AUXPA (WCD937X_BASE_ADDRESS+0x128) +#define WCD937X_LDORXTX_MODE (WCD937X_BASE_ADDRESS+0x129) +#define WCD937X_LDORXTX_CONFIG (WCD937X_BASE_ADDRESS+0x12A) +#define WCD937X_DIE_CRACK_DIE_CRK_DET_EN (WCD937X_BASE_ADDRESS+0x12C) +#define WCD937X_DIE_CRACK_DIE_CRK_DET_OUT (WCD937X_BASE_ADDRESS+0x12D) +#define WCD937X_HPH_NEW_INT_RDAC_GAIN_CTL (WCD937X_BASE_ADDRESS+0x132) +#define WCD937X_HPH_NEW_INT_RDAC_HD2_CTL_L (WCD937X_BASE_ADDRESS+0x133) +#define WCD937X_HPH_NEW_INT_RDAC_VREF_CTL (WCD937X_BASE_ADDRESS+0x134) +#define WCD937X_HPH_NEW_INT_RDAC_OVERRIDE_CTL (WCD937X_BASE_ADDRESS+0x135) +#define WCD937X_HPH_NEW_INT_RDAC_HD2_CTL_R (WCD937X_BASE_ADDRESS+0x136) +#define WCD937X_HPH_NEW_INT_PA_MISC1 (WCD937X_BASE_ADDRESS+0x137) +#define WCD937X_HPH_NEW_INT_PA_MISC2 (WCD937X_BASE_ADDRESS+0x138) +#define WCD937X_HPH_NEW_INT_PA_RDAC_MISC (WCD937X_BASE_ADDRESS+0x139) +#define WCD937X_HPH_NEW_INT_HPH_TIMER1 (WCD937X_BASE_ADDRESS+0x13A) +#define WCD937X_HPH_NEW_INT_HPH_TIMER2 (WCD937X_BASE_ADDRESS+0x13B) +#define WCD937X_HPH_NEW_INT_HPH_TIMER3 (WCD937X_BASE_ADDRESS+0x13C) +#define WCD937X_HPH_NEW_INT_HPH_TIMER4 (WCD937X_BASE_ADDRESS+0x13D) +#define WCD937X_HPH_NEW_INT_PA_RDAC_MISC2 (WCD937X_BASE_ADDRESS+0x13E) +#define WCD937X_HPH_NEW_INT_PA_RDAC_MISC3 (WCD937X_BASE_ADDRESS+0x13F) +#define WCD937X_RX_NEW_INT_HPH_RDAC_BIAS_LOHIFI (WCD937X_BASE_ADDRESS+0x145) +#define WCD937X_RX_NEW_INT_HPH_RDAC_BIAS_ULP (WCD937X_BASE_ADDRESS+0x146) +#define WCD937X_RX_NEW_INT_HPH_RDAC_LDO_LP (WCD937X_BASE_ADDRESS+0x147) +#define WCD937X_MBHC_NEW_INT_MOISTURE_DET_DC_CTRL (WCD937X_BASE_ADDRESS+0x1AF) +#define WCD937X_MBHC_NEW_INT_MOISTURE_DET_POLLING_CTRL \ + (WCD937X_BASE_ADDRESS+0x1B0) +#define WCD937X_MBHC_NEW_INT_MECH_DET_CURRENT (WCD937X_BASE_ADDRESS+0x1B1) +#define WCD937X_MBHC_NEW_INT_SPARE_2 (WCD937X_BASE_ADDRESS+0x1B2) +#define WCD937X_EAR_INT_NEW_EAR_CHOPPER_CON (WCD937X_BASE_ADDRESS+0x1B7) +#define WCD937X_EAR_INT_NEW_CNP_VCM_CON1 (WCD937X_BASE_ADDRESS+0x1B8) +#define WCD937X_EAR_INT_NEW_CNP_VCM_CON2 (WCD937X_BASE_ADDRESS+0x1B9) +#define WCD937X_EAR_INT_NEW_EAR_DYNAMIC_BIAS (WCD937X_BASE_ADDRESS+0x1BA) +#define WCD937X_AUX_INT_EN_REG (WCD937X_BASE_ADDRESS+0x1BD) +#define WCD937X_AUX_INT_PA_CTRL (WCD937X_BASE_ADDRESS+0x1BE) +#define WCD937X_AUX_INT_SP_CTRL (WCD937X_BASE_ADDRESS+0x1BF) +#define WCD937X_AUX_INT_DAC_CTRL (WCD937X_BASE_ADDRESS+0x1C0) +#define WCD937X_AUX_INT_CLK_CTRL (WCD937X_BASE_ADDRESS+0x1C1) +#define WCD937X_AUX_INT_TEST_CTRL (WCD937X_BASE_ADDRESS+0x1C2) +#define WCD937X_AUX_INT_STATUS_REG (WCD937X_BASE_ADDRESS+0x1C3) +#define WCD937X_AUX_INT_MISC (WCD937X_BASE_ADDRESS+0x1C4) +#define WCD937X_LDORXTX_INT_BIAS (WCD937X_BASE_ADDRESS+0x1C5) +#define WCD937X_LDORXTX_INT_STB_LOADS_DTEST (WCD937X_BASE_ADDRESS+0x1C6) +#define WCD937X_LDORXTX_INT_TEST0 (WCD937X_BASE_ADDRESS+0x1C7) +#define WCD937X_LDORXTX_INT_STARTUP_TIMER (WCD937X_BASE_ADDRESS+0x1C8) +#define WCD937X_LDORXTX_INT_TEST1 (WCD937X_BASE_ADDRESS+0x1C9) +#define WCD937X_LDORXTX_INT_STATUS (WCD937X_BASE_ADDRESS+0x1CA) +#define WCD937X_SLEEP_INT_WATCHDOG_CTL_1 (WCD937X_BASE_ADDRESS+0x1D0) +#define WCD937X_SLEEP_INT_WATCHDOG_CTL_2 (WCD937X_BASE_ADDRESS+0x1D1) +#define WCD937X_DIE_CRACK_INT_DIE_CRK_DET_INT1 (WCD937X_BASE_ADDRESS+0x1D3) +#define WCD937X_DIE_CRACK_INT_DIE_CRK_DET_INT2 (WCD937X_BASE_ADDRESS+0x1D4) +#define WCD937X_DIGITAL_PAGE_REGISTER (WCD937X_BASE_ADDRESS+0x400) +#define WCD937X_DIGITAL_CHIP_ID0 (WCD937X_BASE_ADDRESS+0x401) +#define WCD937X_DIGITAL_CHIP_ID1 (WCD937X_BASE_ADDRESS+0x402) +#define WCD937X_DIGITAL_CHIP_ID2 (WCD937X_BASE_ADDRESS+0x403) +#define WCD937X_DIGITAL_CHIP_ID3 (WCD937X_BASE_ADDRESS+0x404) +#define WCD937X_DIGITAL_CDC_RST_CTL (WCD937X_BASE_ADDRESS+0x406) +#define WCD937X_DIGITAL_TOP_CLK_CFG (WCD937X_BASE_ADDRESS+0x407) +#define WCD937X_DIGITAL_CDC_ANA_CLK_CTL (WCD937X_BASE_ADDRESS+0x408) +#define WCD937X_DIGITAL_CDC_DIG_CLK_CTL (WCD937X_BASE_ADDRESS+0x409) +#define WCD937X_DIGITAL_SWR_RST_EN (WCD937X_BASE_ADDRESS+0x40A) +#define WCD937X_DIGITAL_CDC_PATH_MODE (WCD937X_BASE_ADDRESS+0x40B) +#define WCD937X_DIGITAL_CDC_RX_RST (WCD937X_BASE_ADDRESS+0x40C) +#define WCD937X_DIGITAL_CDC_RX0_CTL (WCD937X_BASE_ADDRESS+0x40D) +#define WCD937X_DIGITAL_CDC_RX1_CTL (WCD937X_BASE_ADDRESS+0x40E) +#define WCD937X_DIGITAL_CDC_RX2_CTL (WCD937X_BASE_ADDRESS+0x40F) +#define WCD937X_DIGITAL_DEM_BYPASS_DATA0 (WCD937X_BASE_ADDRESS+0x410) +#define WCD937X_DIGITAL_DEM_BYPASS_DATA1 (WCD937X_BASE_ADDRESS+0x411) +#define WCD937X_DIGITAL_DEM_BYPASS_DATA2 (WCD937X_BASE_ADDRESS+0x412) +#define WCD937X_DIGITAL_DEM_BYPASS_DATA3 (WCD937X_BASE_ADDRESS+0x413) +#define WCD937X_DIGITAL_CDC_COMP_CTL_0 (WCD937X_BASE_ADDRESS+0x414) +#define WCD937X_DIGITAL_CDC_RX_DELAY_CTL (WCD937X_BASE_ADDRESS+0x417) +#define WCD937X_DIGITAL_CDC_HPH_DSM_A1_0 (WCD937X_BASE_ADDRESS+0x418) +#define WCD937X_DIGITAL_CDC_HPH_DSM_A1_1 (WCD937X_BASE_ADDRESS+0x419) +#define WCD937X_DIGITAL_CDC_HPH_DSM_A2_0 (WCD937X_BASE_ADDRESS+0x41A) +#define WCD937X_DIGITAL_CDC_HPH_DSM_A2_1 (WCD937X_BASE_ADDRESS+0x41B) +#define WCD937X_DIGITAL_CDC_HPH_DSM_A3_0 (WCD937X_BASE_ADDRESS+0x41C) +#define WCD937X_DIGITAL_CDC_HPH_DSM_A3_1 (WCD937X_BASE_ADDRESS+0x41D) +#define WCD937X_DIGITAL_CDC_HPH_DSM_A4_0 (WCD937X_BASE_ADDRESS+0x41E) +#define WCD937X_DIGITAL_CDC_HPH_DSM_A4_1 (WCD937X_BASE_ADDRESS+0x41F) +#define WCD937X_DIGITAL_CDC_HPH_DSM_A5_0 (WCD937X_BASE_ADDRESS+0x420) +#define WCD937X_DIGITAL_CDC_HPH_DSM_A5_1 (WCD937X_BASE_ADDRESS+0x421) +#define WCD937X_DIGITAL_CDC_HPH_DSM_A6_0 (WCD937X_BASE_ADDRESS+0x422) +#define WCD937X_DIGITAL_CDC_HPH_DSM_A7_0 (WCD937X_BASE_ADDRESS+0x423) +#define WCD937X_DIGITAL_CDC_HPH_DSM_C_0 (WCD937X_BASE_ADDRESS+0x424) +#define WCD937X_DIGITAL_CDC_HPH_DSM_C_1 (WCD937X_BASE_ADDRESS+0x425) +#define WCD937X_DIGITAL_CDC_HPH_DSM_C_2 (WCD937X_BASE_ADDRESS+0x426) +#define WCD937X_DIGITAL_CDC_HPH_DSM_C_3 (WCD937X_BASE_ADDRESS+0x427) +#define WCD937X_DIGITAL_CDC_HPH_DSM_R1 (WCD937X_BASE_ADDRESS+0x428) +#define WCD937X_DIGITAL_CDC_HPH_DSM_R2 (WCD937X_BASE_ADDRESS+0x429) +#define WCD937X_DIGITAL_CDC_HPH_DSM_R3 (WCD937X_BASE_ADDRESS+0x42A) +#define WCD937X_DIGITAL_CDC_HPH_DSM_R4 (WCD937X_BASE_ADDRESS+0x42B) +#define WCD937X_DIGITAL_CDC_HPH_DSM_R5 (WCD937X_BASE_ADDRESS+0x42C) +#define WCD937X_DIGITAL_CDC_HPH_DSM_R6 (WCD937X_BASE_ADDRESS+0x42D) +#define WCD937X_DIGITAL_CDC_HPH_DSM_R7 (WCD937X_BASE_ADDRESS+0x42E) +#define WCD937X_DIGITAL_CDC_AUX_DSM_A1_0 (WCD937X_BASE_ADDRESS+0x42F) +#define WCD937X_DIGITAL_CDC_AUX_DSM_A1_1 (WCD937X_BASE_ADDRESS+0x430) +#define WCD937X_DIGITAL_CDC_AUX_DSM_A2_0 (WCD937X_BASE_ADDRESS+0x431) +#define WCD937X_DIGITAL_CDC_AUX_DSM_A2_1 (WCD937X_BASE_ADDRESS+0x432) +#define WCD937X_DIGITAL_CDC_AUX_DSM_A3_0 (WCD937X_BASE_ADDRESS+0x433) +#define WCD937X_DIGITAL_CDC_AUX_DSM_A3_1 (WCD937X_BASE_ADDRESS+0x434) +#define WCD937X_DIGITAL_CDC_AUX_DSM_A4_0 (WCD937X_BASE_ADDRESS+0x435) +#define WCD937X_DIGITAL_CDC_AUX_DSM_A4_1 (WCD937X_BASE_ADDRESS+0x436) +#define WCD937X_DIGITAL_CDC_AUX_DSM_A5_0 (WCD937X_BASE_ADDRESS+0x437) +#define WCD937X_DIGITAL_CDC_AUX_DSM_A5_1 (WCD937X_BASE_ADDRESS+0x438) +#define WCD937X_DIGITAL_CDC_AUX_DSM_A6_0 (WCD937X_BASE_ADDRESS+0x439) +#define WCD937X_DIGITAL_CDC_AUX_DSM_A7_0 (WCD937X_BASE_ADDRESS+0x43A) +#define WCD937X_DIGITAL_CDC_AUX_DSM_C_0 (WCD937X_BASE_ADDRESS+0x43B) +#define WCD937X_DIGITAL_CDC_AUX_DSM_C_1 (WCD937X_BASE_ADDRESS+0x43C) +#define WCD937X_DIGITAL_CDC_AUX_DSM_C_2 (WCD937X_BASE_ADDRESS+0x43D) +#define WCD937X_DIGITAL_CDC_AUX_DSM_C_3 (WCD937X_BASE_ADDRESS+0x43E) +#define WCD937X_DIGITAL_CDC_AUX_DSM_R1 (WCD937X_BASE_ADDRESS+0x43F) +#define WCD937X_DIGITAL_CDC_AUX_DSM_R2 (WCD937X_BASE_ADDRESS+0x440) +#define WCD937X_DIGITAL_CDC_AUX_DSM_R3 (WCD937X_BASE_ADDRESS+0x441) +#define WCD937X_DIGITAL_CDC_AUX_DSM_R4 (WCD937X_BASE_ADDRESS+0x442) +#define WCD937X_DIGITAL_CDC_AUX_DSM_R5 (WCD937X_BASE_ADDRESS+0x443) +#define WCD937X_DIGITAL_CDC_AUX_DSM_R6 (WCD937X_BASE_ADDRESS+0x444) +#define WCD937X_DIGITAL_CDC_AUX_DSM_R7 (WCD937X_BASE_ADDRESS+0x445) +#define WCD937X_DIGITAL_CDC_HPH_GAIN_RX_0 (WCD937X_BASE_ADDRESS+0x446) +#define WCD937X_DIGITAL_CDC_HPH_GAIN_RX_1 (WCD937X_BASE_ADDRESS+0x447) +#define WCD937X_DIGITAL_CDC_HPH_GAIN_DSD_0 (WCD937X_BASE_ADDRESS+0x448) +#define WCD937X_DIGITAL_CDC_HPH_GAIN_DSD_1 (WCD937X_BASE_ADDRESS+0x449) +#define WCD937X_DIGITAL_CDC_HPH_GAIN_DSD_2 (WCD937X_BASE_ADDRESS+0x44A) +#define WCD937X_DIGITAL_CDC_AUX_GAIN_DSD_0 (WCD937X_BASE_ADDRESS+0x44B) +#define WCD937X_DIGITAL_CDC_AUX_GAIN_DSD_1 (WCD937X_BASE_ADDRESS+0x44C) +#define WCD937X_DIGITAL_CDC_AUX_GAIN_DSD_2 (WCD937X_BASE_ADDRESS+0x44D) +#define WCD937X_DIGITAL_CDC_HPH_GAIN_CTL (WCD937X_BASE_ADDRESS+0x44E) +#define WCD937X_DIGITAL_CDC_AUX_GAIN_CTL (WCD937X_BASE_ADDRESS+0x44F) +#define WCD937X_DIGITAL_CDC_EAR_PATH_CTL (WCD937X_BASE_ADDRESS+0x450) +#define WCD937X_DIGITAL_CDC_SWR_CLH (WCD937X_BASE_ADDRESS+0x451) +#define WCD937X_DIGITAL_SWR_CLH_BYP (WCD937X_BASE_ADDRESS+0x452) +#define WCD937X_DIGITAL_CDC_TX0_CTL (WCD937X_BASE_ADDRESS+0x453) +#define WCD937X_DIGITAL_CDC_TX1_CTL (WCD937X_BASE_ADDRESS+0x454) +#define WCD937X_DIGITAL_CDC_TX2_CTL (WCD937X_BASE_ADDRESS+0x455) +#define WCD937X_DIGITAL_CDC_TX_RST (WCD937X_BASE_ADDRESS+0x456) +#define WCD937X_DIGITAL_CDC_REQ_CTL (WCD937X_BASE_ADDRESS+0x457) +#define WCD937X_DIGITAL_CDC_AMIC_CTL (WCD937X_BASE_ADDRESS+0x45A) +#define WCD937X_DIGITAL_CDC_DMIC_CTL (WCD937X_BASE_ADDRESS+0x45B) +#define WCD937X_DIGITAL_CDC_DMIC0_CTL (WCD937X_BASE_ADDRESS+0x45C) +#define WCD937X_DIGITAL_CDC_DMIC1_CTL (WCD937X_BASE_ADDRESS+0x45D) +#define WCD937X_DIGITAL_CDC_DMIC2_CTL (WCD937X_BASE_ADDRESS+0x45E) +#define WCD937X_DIGITAL_EFUSE_CTL (WCD937X_BASE_ADDRESS+0x45F) +#define WCD937X_DIGITAL_EFUSE_PRG_CTL (WCD937X_BASE_ADDRESS+0x460) +#define WCD937X_DIGITAL_EFUSE_TEST_CTL_0 (WCD937X_BASE_ADDRESS+0x461) +#define WCD937X_DIGITAL_EFUSE_TEST_CTL_1 (WCD937X_BASE_ADDRESS+0x462) +#define WCD937X_DIGITAL_EFUSE_T_DATA_0 (WCD937X_BASE_ADDRESS+0x463) +#define WCD937X_DIGITAL_EFUSE_T_DATA_1 (WCD937X_BASE_ADDRESS+0x464) +#define WCD937X_DIGITAL_PDM_WD_CTL0 (WCD937X_BASE_ADDRESS+0x465) +#define WCD937X_DIGITAL_PDM_WD_CTL1 (WCD937X_BASE_ADDRESS+0x466) +#define WCD937X_DIGITAL_PDM_WD_CTL2 (WCD937X_BASE_ADDRESS+0x467) +#define WCD937X_DIGITAL_INTR_MODE (WCD937X_BASE_ADDRESS+0x46A) +#define WCD937X_DIGITAL_INTR_MASK_0 (WCD937X_BASE_ADDRESS+0x46B) +#define WCD937X_DIGITAL_INTR_MASK_1 (WCD937X_BASE_ADDRESS+0x46C) +#define WCD937X_DIGITAL_INTR_MASK_2 (WCD937X_BASE_ADDRESS+0x46D) +#define WCD937X_DIGITAL_INTR_STATUS_0 (WCD937X_BASE_ADDRESS+0x46E) +#define WCD937X_DIGITAL_INTR_STATUS_1 (WCD937X_BASE_ADDRESS+0x46F) +#define WCD937X_DIGITAL_INTR_STATUS_2 (WCD937X_BASE_ADDRESS+0x470) +#define WCD937X_DIGITAL_INTR_CLEAR_0 (WCD937X_BASE_ADDRESS+0x471) +#define WCD937X_DIGITAL_INTR_CLEAR_1 (WCD937X_BASE_ADDRESS+0x472) +#define WCD937X_DIGITAL_INTR_CLEAR_2 (WCD937X_BASE_ADDRESS+0x473) +#define WCD937X_DIGITAL_INTR_LEVEL_0 (WCD937X_BASE_ADDRESS+0x474) +#define WCD937X_DIGITAL_INTR_LEVEL_1 (WCD937X_BASE_ADDRESS+0x475) +#define WCD937X_DIGITAL_INTR_LEVEL_2 (WCD937X_BASE_ADDRESS+0x476) +#define WCD937X_DIGITAL_INTR_SET_0 (WCD937X_BASE_ADDRESS+0x477) +#define WCD937X_DIGITAL_INTR_SET_1 (WCD937X_BASE_ADDRESS+0x478) +#define WCD937X_DIGITAL_INTR_SET_2 (WCD937X_BASE_ADDRESS+0x479) +#define WCD937X_DIGITAL_INTR_TEST_0 (WCD937X_BASE_ADDRESS+0x47A) +#define WCD937X_DIGITAL_INTR_TEST_1 (WCD937X_BASE_ADDRESS+0x47B) +#define WCD937X_DIGITAL_INTR_TEST_2 (WCD937X_BASE_ADDRESS+0x47C) +#define WCD937X_DIGITAL_CDC_CONN_RX0_CTL (WCD937X_BASE_ADDRESS+0x47F) +#define WCD937X_DIGITAL_CDC_CONN_RX1_CTL (WCD937X_BASE_ADDRESS+0x480) +#define WCD937X_DIGITAL_CDC_CONN_RX2_CTL (WCD937X_BASE_ADDRESS+0x481) +#define WCD937X_DIGITAL_CDC_CONN_TX_CTL (WCD937X_BASE_ADDRESS+0x482) +#define WCD937X_DIGITAL_LOOP_BACK_MODE (WCD937X_BASE_ADDRESS+0x483) +#define WCD937X_DIGITAL_SWR_DAC_TEST (WCD937X_BASE_ADDRESS+0x484) +#define WCD937X_DIGITAL_SWR_HM_TEST_RX_0 (WCD937X_BASE_ADDRESS+0x485) +#define WCD937X_DIGITAL_SWR_HM_TEST_TX_0 (WCD937X_BASE_ADDRESS+0x491) +#define WCD937X_DIGITAL_SWR_HM_TEST_RX_1 (WCD937X_BASE_ADDRESS+0x492) +#define WCD937X_DIGITAL_SWR_HM_TEST_TX_1 (WCD937X_BASE_ADDRESS+0x493) +#define WCD937X_DIGITAL_SWR_HM_TEST (WCD937X_BASE_ADDRESS+0x494) +#define WCD937X_DIGITAL_PAD_CTL_PDM_RX0 (WCD937X_BASE_ADDRESS+0x495) +#define WCD937X_DIGITAL_PAD_CTL_PDM_RX1 (WCD937X_BASE_ADDRESS+0x496) +#define WCD937X_DIGITAL_PAD_CTL_PDM_RX2 (WCD937X_BASE_ADDRESS+0x497) +#define WCD937X_DIGITAL_PAD_CTL_PDM_TX (WCD937X_BASE_ADDRESS+0x498) +#define WCD937X_DIGITAL_PAD_INP_DIS_0 (WCD937X_BASE_ADDRESS+0x499) +#define WCD937X_DIGITAL_PAD_INP_DIS_1 (WCD937X_BASE_ADDRESS+0x49A) +#define WCD937X_DIGITAL_DRIVE_STRENGTH_0 (WCD937X_BASE_ADDRESS+0x49B) +#define WCD937X_DIGITAL_DRIVE_STRENGTH_1 (WCD937X_BASE_ADDRESS+0x49C) +#define WCD937X_DIGITAL_DRIVE_STRENGTH_2 (WCD937X_BASE_ADDRESS+0x49D) +#define WCD937X_DIGITAL_RX_DATA_EDGE_CTL (WCD937X_BASE_ADDRESS+0x49E) +#define WCD937X_DIGITAL_TX_DATA_EDGE_CTL (WCD937X_BASE_ADDRESS+0x49F) +#define WCD937X_DIGITAL_GPIO_MODE (WCD937X_BASE_ADDRESS+0x4A0) +#define WCD937X_DIGITAL_PIN_CTL_OE (WCD937X_BASE_ADDRESS+0x4A1) +#define WCD937X_DIGITAL_PIN_CTL_DATA_0 (WCD937X_BASE_ADDRESS+0x4A2) +#define WCD937X_DIGITAL_PIN_CTL_DATA_1 (WCD937X_BASE_ADDRESS+0x4A3) +#define WCD937X_DIGITAL_PIN_STATUS_0 (WCD937X_BASE_ADDRESS+0x4A4) +#define WCD937X_DIGITAL_PIN_STATUS_1 (WCD937X_BASE_ADDRESS+0x4A5) +#define WCD937X_DIGITAL_DIG_DEBUG_CTL (WCD937X_BASE_ADDRESS+0x4A6) +#define WCD937X_DIGITAL_DIG_DEBUG_EN (WCD937X_BASE_ADDRESS+0x4A7) +#define WCD937X_DIGITAL_ANA_CSR_DBG_ADD (WCD937X_BASE_ADDRESS+0x4A8) +#define WCD937X_DIGITAL_ANA_CSR_DBG_CTL (WCD937X_BASE_ADDRESS+0x4A9) +#define WCD937X_DIGITAL_SSP_DBG (WCD937X_BASE_ADDRESS+0x4AA) +#define WCD937X_DIGITAL_MODE_STATUS_0 (WCD937X_BASE_ADDRESS+0x4AB) +#define WCD937X_DIGITAL_MODE_STATUS_1 (WCD937X_BASE_ADDRESS+0x4AC) +#define WCD937X_DIGITAL_SPARE_0 (WCD937X_BASE_ADDRESS+0x4AD) +#define WCD937X_DIGITAL_SPARE_1 (WCD937X_BASE_ADDRESS+0x4AE) +#define WCD937X_DIGITAL_SPARE_2 (WCD937X_BASE_ADDRESS+0x4AF) +#define WCD937X_DIGITAL_EFUSE_REG_0 (WCD937X_BASE_ADDRESS+0x4B0) +#define WCD937X_DIGITAL_EFUSE_REG_1 (WCD937X_BASE_ADDRESS+0x4B1) +#define WCD937X_DIGITAL_EFUSE_REG_2 (WCD937X_BASE_ADDRESS+0x4B2) +#define WCD937X_DIGITAL_EFUSE_REG_3 (WCD937X_BASE_ADDRESS+0x4B3) +#define WCD937X_DIGITAL_EFUSE_REG_4 (WCD937X_BASE_ADDRESS+0x4B4) +#define WCD937X_DIGITAL_EFUSE_REG_5 (WCD937X_BASE_ADDRESS+0x4B5) +#define WCD937X_DIGITAL_EFUSE_REG_6 (WCD937X_BASE_ADDRESS+0x4B6) +#define WCD937X_DIGITAL_EFUSE_REG_7 (WCD937X_BASE_ADDRESS+0x4B7) +#define WCD937X_DIGITAL_EFUSE_REG_8 (WCD937X_BASE_ADDRESS+0x4B8) +#define WCD937X_DIGITAL_EFUSE_REG_9 (WCD937X_BASE_ADDRESS+0x4B9) +#define WCD937X_DIGITAL_EFUSE_REG_10 (WCD937X_BASE_ADDRESS+0x4BA) +#define WCD937X_DIGITAL_EFUSE_REG_11 (WCD937X_BASE_ADDRESS+0x4BB) +#define WCD937X_DIGITAL_EFUSE_REG_12 (WCD937X_BASE_ADDRESS+0x4BC) +#define WCD937X_DIGITAL_EFUSE_REG_13 (WCD937X_BASE_ADDRESS+0x4BD) +#define WCD937X_DIGITAL_EFUSE_REG_14 (WCD937X_BASE_ADDRESS+0x4BE) +#define WCD937X_DIGITAL_EFUSE_REG_15 (WCD937X_BASE_ADDRESS+0x4BF) +#define WCD937X_DIGITAL_EFUSE_REG_16 (WCD937X_BASE_ADDRESS+0x4C0) +#define WCD937X_DIGITAL_EFUSE_REG_17 (WCD937X_BASE_ADDRESS+0x4C1) +#define WCD937X_DIGITAL_EFUSE_REG_18 (WCD937X_BASE_ADDRESS+0x4C2) +#define WCD937X_DIGITAL_EFUSE_REG_19 (WCD937X_BASE_ADDRESS+0x4C3) +#define WCD937X_DIGITAL_EFUSE_REG_20 (WCD937X_BASE_ADDRESS+0x4C4) +#define WCD937X_DIGITAL_EFUSE_REG_21 (WCD937X_BASE_ADDRESS+0x4C5) +#define WCD937X_DIGITAL_EFUSE_REG_22 (WCD937X_BASE_ADDRESS+0x4C6) +#define WCD937X_DIGITAL_EFUSE_REG_23 (WCD937X_BASE_ADDRESS+0x4C7) +#define WCD937X_DIGITAL_EFUSE_REG_24 (WCD937X_BASE_ADDRESS+0x4C8) +#define WCD937X_DIGITAL_EFUSE_REG_25 (WCD937X_BASE_ADDRESS+0x4C9) +#define WCD937X_DIGITAL_EFUSE_REG_26 (WCD937X_BASE_ADDRESS+0x4CA) +#define WCD937X_DIGITAL_EFUSE_REG_27 (WCD937X_BASE_ADDRESS+0x4CB) +#define WCD937X_DIGITAL_EFUSE_REG_28 (WCD937X_BASE_ADDRESS+0x4CC) +#define WCD937X_DIGITAL_EFUSE_REG_29 (WCD937X_BASE_ADDRESS+0x4CD) +#define WCD937X_DIGITAL_EFUSE_REG_30 (WCD937X_BASE_ADDRESS+0x4CE) +#define WCD937X_DIGITAL_EFUSE_REG_31 (WCD937X_BASE_ADDRESS+0x4CF) + +#define WCD937X_REGISTERS_MAX_SIZE (WCD937X_BASE_ADDRESS+0x4D0) +#define WCD937X_MAX_REGISTER (WCD937X_REGISTERS_MAX_SIZE - 1) + +#endif diff --git a/audio-kernel/asoc/codecs/wcd937x/wcd937x-regmap.c b/audio-kernel/asoc/codecs/wcd937x/wcd937x-regmap.c new file mode 100644 index 000000000..0d15c105f --- /dev/null +++ b/audio-kernel/asoc/codecs/wcd937x/wcd937x-regmap.c @@ -0,0 +1,471 @@ +/* + * 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 +#include +#include "wcd937x-registers.h" + +extern const u8 wcd937x_reg_access[WCD937X_REGISTERS_MAX_SIZE]; + +static const struct reg_default wcd937x_defaults[] = { + { WCD937X_ANA_BIAS, 0x00 }, + { WCD937X_ANA_RX_SUPPLIES, 0x00 }, + { WCD937X_ANA_HPH, 0x0C }, + { WCD937X_ANA_EAR, 0x00 }, + { WCD937X_ANA_EAR_COMPANDER_CTL, 0x02 }, + { WCD937X_ANA_TX_CH1, 0x20 }, + { WCD937X_ANA_TX_CH2, 0x00 }, + { WCD937X_ANA_TX_CH3, 0x20 }, + { WCD937X_ANA_TX_CH3_HPF, 0x00 }, + { WCD937X_ANA_MICB1_MICB2_DSP_EN_LOGIC, 0x00 }, + { WCD937X_ANA_MICB3_DSP_EN_LOGIC, 0x00 }, + { WCD937X_ANA_MBHC_MECH, 0x39 }, + { WCD937X_ANA_MBHC_ELECT, 0x08 }, + { WCD937X_ANA_MBHC_ZDET, 0x00 }, + { WCD937X_ANA_MBHC_RESULT_1, 0x00 }, + { WCD937X_ANA_MBHC_RESULT_2, 0x00 }, + { WCD937X_ANA_MBHC_RESULT_3, 0x00 }, + { WCD937X_ANA_MBHC_BTN0, 0x00 }, + { WCD937X_ANA_MBHC_BTN1, 0x10 }, + { WCD937X_ANA_MBHC_BTN2, 0x20 }, + { WCD937X_ANA_MBHC_BTN3, 0x30 }, + { WCD937X_ANA_MBHC_BTN4, 0x40 }, + { WCD937X_ANA_MBHC_BTN5, 0x50 }, + { WCD937X_ANA_MBHC_BTN6, 0x60 }, + { WCD937X_ANA_MBHC_BTN7, 0x70 }, + { WCD937X_ANA_MICB1, 0x10 }, + { WCD937X_ANA_MICB2, 0x10 }, + { WCD937X_ANA_MICB2_RAMP, 0x00 }, + { WCD937X_ANA_MICB3, 0x10 }, + { WCD937X_BIAS_CTL, 0x2A }, + { WCD937X_BIAS_VBG_FINE_ADJ, 0x55 }, + { WCD937X_LDOL_VDDCX_ADJUST, 0x01 }, + { WCD937X_LDOL_DISABLE_LDOL, 0x00 }, + { WCD937X_MBHC_CTL_CLK, 0x00 }, + { WCD937X_MBHC_CTL_ANA, 0x00 }, + { WCD937X_MBHC_CTL_SPARE_1, 0x00 }, + { WCD937X_MBHC_CTL_SPARE_2, 0x00 }, + { WCD937X_MBHC_CTL_BCS, 0x00 }, + { WCD937X_MBHC_MOISTURE_DET_FSM_STATUS, 0x00 }, + { WCD937X_MBHC_TEST_CTL, 0x00 }, + { WCD937X_LDOH_MODE, 0x2B }, + { WCD937X_LDOH_BIAS, 0x68 }, + { WCD937X_LDOH_STB_LOADS, 0x00 }, + { WCD937X_LDOH_SLOWRAMP, 0x50 }, + { WCD937X_MICB1_TEST_CTL_1, 0x1A }, + { WCD937X_MICB1_TEST_CTL_2, 0x18 }, + { WCD937X_MICB1_TEST_CTL_3, 0xA4 }, + { WCD937X_MICB2_TEST_CTL_1, 0x1A }, + { WCD937X_MICB2_TEST_CTL_2, 0x18 }, + { WCD937X_MICB2_TEST_CTL_3, 0xA4 }, + { WCD937X_MICB3_TEST_CTL_1, 0x1A }, + { WCD937X_MICB3_TEST_CTL_2, 0x18 }, + { WCD937X_MICB3_TEST_CTL_3, 0xA4 }, + { WCD937X_TX_COM_ADC_VCM, 0x39 }, + { WCD937X_TX_COM_BIAS_ATEST, 0xC0 }, + { WCD937X_TX_COM_ADC_INT1_IB, 0x6F }, + { WCD937X_TX_COM_ADC_INT2_IB, 0x4F }, + { WCD937X_TX_COM_TXFE_DIV_CTL, 0x2E }, + { WCD937X_TX_COM_TXFE_DIV_START, 0x00 }, + { WCD937X_TX_COM_TXFE_DIV_STOP_9P6M, 0xC7 }, + { WCD937X_TX_COM_TXFE_DIV_STOP_12P288M, 0xFF }, + { WCD937X_TX_1_2_TEST_EN, 0xCC }, + { WCD937X_TX_1_2_ADC_IB, 0x09 }, + { WCD937X_TX_1_2_ATEST_REFCTL, 0x0A }, + { WCD937X_TX_1_2_TEST_CTL, 0x38 }, + { WCD937X_TX_1_2_TEST_BLK_EN, 0xFF }, + { WCD937X_TX_1_2_TXFE_CLKDIV, 0x00 }, + { WCD937X_TX_1_2_SAR2_ERR, 0x00 }, + { WCD937X_TX_1_2_SAR1_ERR, 0x00 }, + { WCD937X_TX_3_TEST_EN, 0xCC }, + { WCD937X_TX_3_ADC_IB, 0x09 }, + { WCD937X_TX_3_ATEST_REFCTL, 0x0A }, + { WCD937X_TX_3_TEST_CTL, 0x38 }, + { WCD937X_TX_3_TEST_BLK_EN, 0xFF }, + { WCD937X_TX_3_TXFE_CLKDIV, 0x00 }, + { WCD937X_TX_3_SPARE_MONO, 0x00 }, + { WCD937X_TX_3_SAR1_ERR, 0x00 }, + { WCD937X_CLASSH_MODE_1, 0x40 }, + { WCD937X_CLASSH_MODE_2, 0x3A }, + { WCD937X_CLASSH_MODE_3, 0x00 }, + { WCD937X_CLASSH_CTRL_VCL_1, 0x70 }, + { WCD937X_CLASSH_CTRL_VCL_2, 0x82 }, + { WCD937X_CLASSH_CTRL_CCL_1, 0x31 }, + { WCD937X_CLASSH_CTRL_CCL_2, 0x80 }, + { WCD937X_CLASSH_CTRL_CCL_3, 0x80 }, + { WCD937X_CLASSH_CTRL_CCL_4, 0x51 }, + { WCD937X_CLASSH_CTRL_CCL_5, 0x00 }, + { WCD937X_CLASSH_BUCK_TMUX_A_D, 0x00 }, + { WCD937X_CLASSH_BUCK_SW_DRV_CNTL, 0x77 }, + { WCD937X_CLASSH_SPARE, 0x00 }, + { WCD937X_FLYBACK_EN, 0x4E }, + { WCD937X_FLYBACK_VNEG_CTRL_1, 0x0B }, + { WCD937X_FLYBACK_VNEG_CTRL_2, 0x45 }, + { WCD937X_FLYBACK_VNEG_CTRL_3, 0x74 }, + { WCD937X_FLYBACK_VNEG_CTRL_4, 0x7F }, + { WCD937X_FLYBACK_VNEG_CTRL_5, 0x83 }, + { WCD937X_FLYBACK_VNEG_CTRL_6, 0x98 }, + { WCD937X_FLYBACK_VNEG_CTRL_7, 0xA9 }, + { WCD937X_FLYBACK_VNEG_CTRL_8, 0x68 }, + { WCD937X_FLYBACK_VNEG_CTRL_9, 0x64 }, + { WCD937X_FLYBACK_VNEGDAC_CTRL_1, 0xED }, + { WCD937X_FLYBACK_VNEGDAC_CTRL_2, 0xF0 }, + { WCD937X_FLYBACK_VNEGDAC_CTRL_3, 0xA6 }, + { WCD937X_FLYBACK_CTRL_1, 0x65 }, + { WCD937X_FLYBACK_TEST_CTL, 0x00 }, + { WCD937X_RX_AUX_SW_CTL, 0x00 }, + { WCD937X_RX_PA_AUX_IN_CONN, 0x00 }, + { WCD937X_RX_TIMER_DIV, 0x32 }, + { WCD937X_RX_OCP_CTL, 0x1F }, + { WCD937X_RX_OCP_COUNT, 0x77 }, + { WCD937X_RX_BIAS_EAR_DAC, 0xA0 }, + { WCD937X_RX_BIAS_EAR_AMP, 0xAA }, + { WCD937X_RX_BIAS_HPH_LDO, 0xA9 }, + { WCD937X_RX_BIAS_HPH_PA, 0xAA }, + { WCD937X_RX_BIAS_HPH_RDACBUFF_CNP2, 0x8A }, + { WCD937X_RX_BIAS_HPH_RDAC_LDO, 0x88 }, + { WCD937X_RX_BIAS_HPH_CNP1, 0x82 }, + { WCD937X_RX_BIAS_HPH_LOWPOWER, 0x82 }, + { WCD937X_RX_BIAS_AUX_DAC, 0xA0 }, + { WCD937X_RX_BIAS_AUX_AMP, 0xAA }, + { WCD937X_RX_BIAS_VNEGDAC_BLEEDER, 0x50 }, + { WCD937X_RX_BIAS_MISC, 0x00 }, + { WCD937X_RX_BIAS_BUCK_RST, 0x08 }, + { WCD937X_RX_BIAS_BUCK_VREF_ERRAMP, 0x44 }, + { WCD937X_RX_BIAS_FLYB_ERRAMP, 0x40 }, + { WCD937X_RX_BIAS_FLYB_BUFF, 0xAA }, + { WCD937X_RX_BIAS_FLYB_MID_RST, 0x14 }, + { WCD937X_HPH_L_STATUS, 0x04 }, + { WCD937X_HPH_R_STATUS, 0x04 }, + { WCD937X_HPH_CNP_EN, 0x80 }, + { WCD937X_HPH_CNP_WG_CTL, 0x9A }, + { WCD937X_HPH_CNP_WG_TIME, 0x14 }, + { WCD937X_HPH_OCP_CTL, 0x28 }, + { WCD937X_HPH_AUTO_CHOP, 0x16 }, + { WCD937X_HPH_CHOP_CTL, 0x83 }, + { WCD937X_HPH_PA_CTL1, 0x46 }, + { WCD937X_HPH_PA_CTL2, 0x50 }, + { WCD937X_HPH_L_EN, 0x80 }, + { WCD937X_HPH_L_TEST, 0xE0 }, + { WCD937X_HPH_L_ATEST, 0x50 }, + { WCD937X_HPH_R_EN, 0x80 }, + { WCD937X_HPH_R_TEST, 0xE0 }, + { WCD937X_HPH_R_ATEST, 0x54 }, + { WCD937X_HPH_RDAC_CLK_CTL1, 0x99 }, + { WCD937X_HPH_RDAC_CLK_CTL2, 0x9B }, + { WCD937X_HPH_RDAC_LDO_CTL, 0x33 }, + { WCD937X_HPH_RDAC_CHOP_CLK_LP_CTL, 0x00 }, + { WCD937X_HPH_REFBUFF_UHQA_CTL, 0xA8 }, + { WCD937X_HPH_REFBUFF_LP_CTL, 0x0E }, + { WCD937X_HPH_L_DAC_CTL, 0x20 }, + { WCD937X_HPH_R_DAC_CTL, 0x20 }, + { WCD937X_HPH_SURGE_HPHLR_SURGE_COMP_SEL, 0x55 }, + { WCD937X_HPH_SURGE_HPHLR_SURGE_EN, 0x19 }, + { WCD937X_HPH_SURGE_HPHLR_SURGE_MISC1, 0xA0 }, + { WCD937X_HPH_SURGE_HPHLR_SURGE_STATUS, 0x00 }, + { WCD937X_EAR_EAR_EN_REG, 0x22 }, + { WCD937X_EAR_EAR_PA_CON, 0x44 }, + { WCD937X_EAR_EAR_SP_CON, 0xDB }, + { WCD937X_EAR_EAR_DAC_CON, 0x80 }, + { WCD937X_EAR_EAR_CNP_FSM_CON, 0xB2 }, + { WCD937X_EAR_TEST_CTL, 0x00 }, + { WCD937X_EAR_STATUS_REG_1, 0x00 }, + { WCD937X_EAR_STATUS_REG_2, 0x00 }, + { WCD937X_ANA_NEW_PAGE_REGISTER, 0x00 }, + { WCD937X_HPH_NEW_ANA_HPH2, 0x00 }, + { WCD937X_HPH_NEW_ANA_HPH3, 0x00 }, + { WCD937X_SLEEP_CTL, 0x16 }, + { WCD937X_SLEEP_WATCHDOG_CTL, 0x00 }, + { WCD937X_MBHC_NEW_ELECT_REM_CLAMP_CTL, 0x00 }, + { WCD937X_MBHC_NEW_CTL_1, 0x02 }, + { WCD937X_MBHC_NEW_CTL_2, 0x05 }, + { WCD937X_MBHC_NEW_PLUG_DETECT_CTL, 0xE9 }, + { WCD937X_MBHC_NEW_ZDET_ANA_CTL, 0x0F }, + { WCD937X_MBHC_NEW_ZDET_RAMP_CTL, 0x00 }, + { WCD937X_MBHC_NEW_FSM_STATUS, 0x00 }, + { WCD937X_MBHC_NEW_ADC_RESULT, 0x00 }, + { WCD937X_TX_NEW_TX_CH2_SEL, 0x00 }, + { WCD937X_AUX_AUXPA, 0x00 }, + { WCD937X_LDORXTX_MODE, 0x0C }, + { WCD937X_LDORXTX_CONFIG, 0x10 }, + { WCD937X_DIE_CRACK_DIE_CRK_DET_EN, 0x00 }, + { WCD937X_DIE_CRACK_DIE_CRK_DET_OUT, 0x00 }, + { WCD937X_HPH_NEW_INT_RDAC_GAIN_CTL, 0x40 }, + { WCD937X_HPH_NEW_INT_RDAC_HD2_CTL_L, 0x81 }, + { WCD937X_HPH_NEW_INT_RDAC_VREF_CTL, 0x10 }, + { WCD937X_HPH_NEW_INT_RDAC_OVERRIDE_CTL, 0x00 }, + { WCD937X_HPH_NEW_INT_RDAC_HD2_CTL_R, 0x81 }, + { WCD937X_HPH_NEW_INT_PA_MISC1, 0x22 }, + { WCD937X_HPH_NEW_INT_PA_MISC2, 0x00 }, + { WCD937X_HPH_NEW_INT_PA_RDAC_MISC, 0x00 }, + { WCD937X_HPH_NEW_INT_HPH_TIMER1, 0xFE }, + { WCD937X_HPH_NEW_INT_HPH_TIMER2, 0x02 }, + { WCD937X_HPH_NEW_INT_HPH_TIMER3, 0x4E }, + { WCD937X_HPH_NEW_INT_HPH_TIMER4, 0x54 }, + { WCD937X_HPH_NEW_INT_PA_RDAC_MISC2, 0x00 }, + { WCD937X_HPH_NEW_INT_PA_RDAC_MISC3, 0x00 }, + { WCD937X_RX_NEW_INT_HPH_RDAC_BIAS_LOHIFI, 0x62 }, + { WCD937X_RX_NEW_INT_HPH_RDAC_BIAS_ULP, 0x01 }, + { WCD937X_RX_NEW_INT_HPH_RDAC_LDO_LP, 0x11 }, + { WCD937X_MBHC_NEW_INT_MOISTURE_DET_DC_CTRL, 0x57 }, + { WCD937X_MBHC_NEW_INT_MOISTURE_DET_POLLING_CTRL, 0x01 }, + { WCD937X_MBHC_NEW_INT_MECH_DET_CURRENT, 0x00 }, + { WCD937X_MBHC_NEW_INT_SPARE_2, 0x00 }, + { WCD937X_EAR_INT_NEW_EAR_CHOPPER_CON, 0xA8 }, + { WCD937X_EAR_INT_NEW_CNP_VCM_CON1, 0x42 }, + { WCD937X_EAR_INT_NEW_CNP_VCM_CON2, 0x22 }, + { WCD937X_EAR_INT_NEW_EAR_DYNAMIC_BIAS, 0x00 }, + { WCD937X_AUX_INT_EN_REG, 0x00 }, + { WCD937X_AUX_INT_PA_CTRL, 0x06 }, + { WCD937X_AUX_INT_SP_CTRL, 0xD2 }, + { WCD937X_AUX_INT_DAC_CTRL, 0x80 }, + { WCD937X_AUX_INT_CLK_CTRL, 0x50 }, + { WCD937X_AUX_INT_TEST_CTRL, 0x00 }, + { WCD937X_AUX_INT_STATUS_REG, 0x00 }, + { WCD937X_AUX_INT_MISC, 0x00 }, + { WCD937X_LDORXTX_INT_BIAS, 0x6E }, + { WCD937X_LDORXTX_INT_STB_LOADS_DTEST, 0x50 }, + { WCD937X_LDORXTX_INT_TEST0, 0x1C }, + { WCD937X_LDORXTX_INT_STARTUP_TIMER, 0xFF }, + { WCD937X_LDORXTX_INT_TEST1, 0x1F }, + { WCD937X_LDORXTX_INT_STATUS, 0x00 }, + { WCD937X_SLEEP_INT_WATCHDOG_CTL_1, 0x0A }, + { WCD937X_SLEEP_INT_WATCHDOG_CTL_2, 0x0A }, + { WCD937X_DIE_CRACK_INT_DIE_CRK_DET_INT1, 0x02 }, + { WCD937X_DIE_CRACK_INT_DIE_CRK_DET_INT2, 0x60 }, + { WCD937X_DIGITAL_PAGE_REGISTER, 0x00 }, + { WCD937X_DIGITAL_CHIP_ID0, 0x00 }, + { WCD937X_DIGITAL_CHIP_ID1, 0x00 }, + { WCD937X_DIGITAL_CHIP_ID2, 0x0A }, + { WCD937X_DIGITAL_CHIP_ID3, 0x01 }, + { WCD937X_DIGITAL_CDC_RST_CTL, 0x03 }, + { WCD937X_DIGITAL_TOP_CLK_CFG, 0x00 }, + { WCD937X_DIGITAL_CDC_ANA_CLK_CTL, 0x00 }, + { WCD937X_DIGITAL_CDC_DIG_CLK_CTL, 0x00 }, + { WCD937X_DIGITAL_SWR_RST_EN, 0x00 }, + { WCD937X_DIGITAL_CDC_PATH_MODE, 0x55 }, + { WCD937X_DIGITAL_CDC_RX_RST, 0x00 }, + { WCD937X_DIGITAL_CDC_RX0_CTL, 0xFC }, + { WCD937X_DIGITAL_CDC_RX1_CTL, 0xFC }, + { WCD937X_DIGITAL_CDC_RX2_CTL, 0xFC }, + { WCD937X_DIGITAL_DEM_BYPASS_DATA0, 0x55 }, + { WCD937X_DIGITAL_DEM_BYPASS_DATA1, 0x55 }, + { WCD937X_DIGITAL_DEM_BYPASS_DATA2, 0x55 }, + { WCD937X_DIGITAL_DEM_BYPASS_DATA3, 0x01 }, + { WCD937X_DIGITAL_CDC_COMP_CTL_0, 0x00 }, + { WCD937X_DIGITAL_CDC_RX_DELAY_CTL, 0x66 }, + { WCD937X_DIGITAL_CDC_HPH_DSM_A1_0, 0x00 }, + { WCD937X_DIGITAL_CDC_HPH_DSM_A1_1, 0x01 }, + { WCD937X_DIGITAL_CDC_HPH_DSM_A2_0, 0x63 }, + { WCD937X_DIGITAL_CDC_HPH_DSM_A2_1, 0x04 }, + { WCD937X_DIGITAL_CDC_HPH_DSM_A3_0, 0xAC }, + { WCD937X_DIGITAL_CDC_HPH_DSM_A3_1, 0x04 }, + { WCD937X_DIGITAL_CDC_HPH_DSM_A4_0, 0x1A }, + { WCD937X_DIGITAL_CDC_HPH_DSM_A4_1, 0x03 }, + { WCD937X_DIGITAL_CDC_HPH_DSM_A5_0, 0xBC }, + { WCD937X_DIGITAL_CDC_HPH_DSM_A5_1, 0x02 }, + { WCD937X_DIGITAL_CDC_HPH_DSM_A6_0, 0xC7 }, + { WCD937X_DIGITAL_CDC_HPH_DSM_A7_0, 0xF8 }, + { WCD937X_DIGITAL_CDC_HPH_DSM_C_0, 0x47 }, + { WCD937X_DIGITAL_CDC_HPH_DSM_C_1, 0x43 }, + { WCD937X_DIGITAL_CDC_HPH_DSM_C_2, 0xB1 }, + { WCD937X_DIGITAL_CDC_HPH_DSM_C_3, 0x17 }, + { WCD937X_DIGITAL_CDC_HPH_DSM_R1, 0x4B }, + { WCD937X_DIGITAL_CDC_HPH_DSM_R2, 0x27 }, + { WCD937X_DIGITAL_CDC_HPH_DSM_R3, 0x32 }, + { WCD937X_DIGITAL_CDC_HPH_DSM_R4, 0x57 }, + { WCD937X_DIGITAL_CDC_HPH_DSM_R4, 0x63 }, + { WCD937X_DIGITAL_CDC_HPH_DSM_R4, 0x7C }, + { WCD937X_DIGITAL_CDC_HPH_DSM_R4, 0x57 }, + { WCD937X_DIGITAL_CDC_AUX_DSM_A1_0, 0x00 }, + { WCD937X_DIGITAL_CDC_AUX_DSM_A1_1, 0x01 }, + { WCD937X_DIGITAL_CDC_AUX_DSM_A2_0, 0x96 }, + { WCD937X_DIGITAL_CDC_AUX_DSM_A2_1, 0x09 }, + { WCD937X_DIGITAL_CDC_AUX_DSM_A3_0, 0xAB }, + { WCD937X_DIGITAL_CDC_AUX_DSM_A3_1, 0x05 }, + { WCD937X_DIGITAL_CDC_AUX_DSM_A4_0, 0x1C }, + { WCD937X_DIGITAL_CDC_AUX_DSM_A4_1, 0x02 }, + { WCD937X_DIGITAL_CDC_AUX_DSM_A5_0, 0x17 }, + { WCD937X_DIGITAL_CDC_AUX_DSM_A5_1, 0x02 }, + { WCD937X_DIGITAL_CDC_AUX_DSM_A6_0, 0xAA }, + { WCD937X_DIGITAL_CDC_AUX_DSM_A7_0, 0xE3 }, + { WCD937X_DIGITAL_CDC_AUX_DSM_C_0, 0x69 }, + { WCD937X_DIGITAL_CDC_AUX_DSM_C_1, 0x54 }, + { WCD937X_DIGITAL_CDC_AUX_DSM_C_2, 0x02 }, + { WCD937X_DIGITAL_CDC_AUX_DSM_C_3, 0x15 }, + { WCD937X_DIGITAL_CDC_AUX_DSM_R1, 0xA4 }, + { WCD937X_DIGITAL_CDC_AUX_DSM_R2, 0xB5 }, + { WCD937X_DIGITAL_CDC_AUX_DSM_R3, 0x86 }, + { WCD937X_DIGITAL_CDC_AUX_DSM_R4, 0x85 }, + { WCD937X_DIGITAL_CDC_AUX_DSM_R5, 0xAA }, + { WCD937X_DIGITAL_CDC_AUX_DSM_R6, 0xE2 }, + { WCD937X_DIGITAL_CDC_AUX_DSM_R7, 0x62 }, + { WCD937X_DIGITAL_CDC_HPH_GAIN_RX_0, 0x55 }, + { WCD937X_DIGITAL_CDC_HPH_GAIN_RX_1, 0xA9 }, + { WCD937X_DIGITAL_CDC_HPH_GAIN_DSD_0, 0x3D }, + { WCD937X_DIGITAL_CDC_HPH_GAIN_DSD_1, 0x2E }, + { WCD937X_DIGITAL_CDC_HPH_GAIN_DSD_2, 0x01 }, + { WCD937X_DIGITAL_CDC_AUX_GAIN_DSD_0, 0x00 }, + { WCD937X_DIGITAL_CDC_AUX_GAIN_DSD_1, 0xFC }, + { WCD937X_DIGITAL_CDC_AUX_GAIN_DSD_2, 0x01 }, + { WCD937X_DIGITAL_CDC_HPH_GAIN_CTL, 0x00 }, + { WCD937X_DIGITAL_CDC_AUX_GAIN_CTL, 0x00 }, + { WCD937X_DIGITAL_CDC_EAR_PATH_CTL, 0x00 }, + { WCD937X_DIGITAL_CDC_SWR_CLH, 0x00 }, + { WCD937X_DIGITAL_SWR_CLH_BYP, 0x00 }, + { WCD937X_DIGITAL_CDC_TX0_CTL, 0x68 }, + { WCD937X_DIGITAL_CDC_TX1_CTL, 0x68 }, + { WCD937X_DIGITAL_CDC_TX2_CTL, 0x68 }, + { WCD937X_DIGITAL_CDC_TX_RST, 0x00 }, + { WCD937X_DIGITAL_CDC_REQ_CTL, 0x01 }, + { WCD937X_DIGITAL_CDC_AMIC_CTL, 0x07 }, + { WCD937X_DIGITAL_CDC_DMIC_CTL, 0x00 }, + { WCD937X_DIGITAL_CDC_DMIC0_CTL, 0x01 }, + { WCD937X_DIGITAL_CDC_DMIC1_CTL, 0x01 }, + { WCD937X_DIGITAL_CDC_DMIC2_CTL, 0x01 }, + { WCD937X_DIGITAL_EFUSE_CTL, 0x2B }, + { WCD937X_DIGITAL_EFUSE_PRG_CTL, 0x00 }, + { WCD937X_DIGITAL_EFUSE_TEST_CTL_0, 0x00 }, + { WCD937X_DIGITAL_EFUSE_TEST_CTL_1, 0x00 }, + { WCD937X_DIGITAL_EFUSE_T_DATA_0, 0x00 }, + { WCD937X_DIGITAL_EFUSE_T_DATA_1, 0x00 }, + { WCD937X_DIGITAL_PDM_WD_CTL0, 0x00 }, + { WCD937X_DIGITAL_PDM_WD_CTL1, 0x00 }, + { WCD937X_DIGITAL_PDM_WD_CTL2, 0x00 }, + { WCD937X_DIGITAL_INTR_MODE, 0x00 }, + { WCD937X_DIGITAL_INTR_MASK_0, 0xFF }, + { WCD937X_DIGITAL_INTR_MASK_1, 0xFF }, + { WCD937X_DIGITAL_INTR_MASK_2, 0xFF }, + { WCD937X_DIGITAL_INTR_STATUS_0, 0x00 }, + { WCD937X_DIGITAL_INTR_STATUS_1, 0x00 }, + { WCD937X_DIGITAL_INTR_STATUS_2, 0x00 }, + { WCD937X_DIGITAL_INTR_CLEAR_0, 0x00 }, + { WCD937X_DIGITAL_INTR_CLEAR_1, 0x00 }, + { WCD937X_DIGITAL_INTR_CLEAR_2, 0x00 }, + { WCD937X_DIGITAL_INTR_LEVEL_0, 0x00 }, + { WCD937X_DIGITAL_INTR_LEVEL_1, 0x00 }, + { WCD937X_DIGITAL_INTR_LEVEL_2, 0x00 }, + { WCD937X_DIGITAL_INTR_SET_0, 0x00 }, + { WCD937X_DIGITAL_INTR_SET_1, 0x00 }, + { WCD937X_DIGITAL_INTR_SET_2, 0x00 }, + { WCD937X_DIGITAL_INTR_TEST_0, 0x00 }, + { WCD937X_DIGITAL_INTR_TEST_1, 0x00 }, + { WCD937X_DIGITAL_INTR_TEST_2, 0x00 }, + { WCD937X_DIGITAL_CDC_CONN_RX0_CTL, 0x00 }, + { WCD937X_DIGITAL_CDC_CONN_RX1_CTL, 0x00 }, + { WCD937X_DIGITAL_CDC_CONN_RX2_CTL, 0x00 }, + { WCD937X_DIGITAL_CDC_CONN_TX_CTL, 0x00 }, + { WCD937X_DIGITAL_LOOP_BACK_MODE, 0x00 }, + { WCD937X_DIGITAL_SWR_DAC_TEST, 0x00 }, + { WCD937X_DIGITAL_SWR_HM_TEST_RX_0, 0x40 }, + { WCD937X_DIGITAL_SWR_HM_TEST_TX_0, 0x40 }, + { WCD937X_DIGITAL_SWR_HM_TEST_RX_1, 0x00 }, + { WCD937X_DIGITAL_SWR_HM_TEST_TX_1, 0x00 }, + { WCD937X_DIGITAL_SWR_HM_TEST, 0x00 }, + { WCD937X_DIGITAL_PAD_CTL_PDM_RX0, 0xF1 }, + { WCD937X_DIGITAL_PAD_CTL_PDM_RX1, 0xF1 }, + { WCD937X_DIGITAL_PAD_CTL_PDM_RX2, 0xF1 }, + { WCD937X_DIGITAL_PAD_CTL_PDM_TX, 0xF1 }, + { WCD937X_DIGITAL_PAD_INP_DIS_1, 0x00 }, + { WCD937X_DIGITAL_DRIVE_STRENGTH_0, 0x00 }, + { WCD937X_DIGITAL_DRIVE_STRENGTH_1, 0x00 }, + { WCD937X_DIGITAL_DRIVE_STRENGTH_2, 0x00 }, + { WCD937X_DIGITAL_RX_DATA_EDGE_CTL, 0x1F }, + { WCD937X_DIGITAL_TX_DATA_EDGE_CTL, 0x10 }, + { WCD937X_DIGITAL_GPIO_MODE, 0x00 }, + { WCD937X_DIGITAL_PIN_CTL_OE, 0x00 }, + { WCD937X_DIGITAL_PIN_CTL_DATA_0, 0x00 }, + { WCD937X_DIGITAL_PIN_CTL_DATA_1, 0x00 }, + { WCD937X_DIGITAL_PIN_STATUS_0, 0x00 }, + { WCD937X_DIGITAL_PIN_STATUS_1, 0x00 }, + { WCD937X_DIGITAL_DIG_DEBUG_CTL, 0x00 }, + { WCD937X_DIGITAL_DIG_DEBUG_EN, 0x00 }, + { WCD937X_DIGITAL_ANA_CSR_DBG_ADD, 0x00 }, + { WCD937X_DIGITAL_ANA_CSR_DBG_CTL, 0x48 }, + { WCD937X_DIGITAL_SSP_DBG, 0x00 }, + { WCD937X_DIGITAL_MODE_STATUS_0, 0x00 }, + { WCD937X_DIGITAL_MODE_STATUS_1, 0x00 }, + { WCD937X_DIGITAL_SPARE_0, 0x00 }, + { WCD937X_DIGITAL_SPARE_1, 0x00 }, + { WCD937X_DIGITAL_SPARE_2, 0x00 }, + { WCD937X_DIGITAL_EFUSE_REG_0, 0xFF }, + { WCD937X_DIGITAL_EFUSE_REG_1, 0xFF }, + { WCD937X_DIGITAL_EFUSE_REG_2, 0xFF }, + { WCD937X_DIGITAL_EFUSE_REG_3, 0xFF }, + { WCD937X_DIGITAL_EFUSE_REG_4, 0xFF }, + { WCD937X_DIGITAL_EFUSE_REG_5, 0xFF }, + { WCD937X_DIGITAL_EFUSE_REG_6, 0xFF }, + { WCD937X_DIGITAL_EFUSE_REG_7, 0xFF }, + { WCD937X_DIGITAL_EFUSE_REG_8, 0xFF }, + { WCD937X_DIGITAL_EFUSE_REG_9, 0xFF }, + { WCD937X_DIGITAL_EFUSE_REG_10, 0xFF }, + { WCD937X_DIGITAL_EFUSE_REG_11, 0xFF }, + { WCD937X_DIGITAL_EFUSE_REG_12, 0xFF }, + { WCD937X_DIGITAL_EFUSE_REG_13, 0xFF }, + { WCD937X_DIGITAL_EFUSE_REG_14, 0xFF }, + { WCD937X_DIGITAL_EFUSE_REG_15, 0xFF }, + { WCD937X_DIGITAL_EFUSE_REG_16, 0xFF }, + { WCD937X_DIGITAL_EFUSE_REG_17, 0xFF }, + { WCD937X_DIGITAL_EFUSE_REG_18, 0xFF }, + { WCD937X_DIGITAL_EFUSE_REG_19, 0xFF }, + { WCD937X_DIGITAL_EFUSE_REG_20, 0x0E }, + { WCD937X_DIGITAL_EFUSE_REG_21, 0x8F }, + { WCD937X_DIGITAL_EFUSE_REG_22, 0x16 }, + { WCD937X_DIGITAL_EFUSE_REG_23, 0x00 }, + { WCD937X_DIGITAL_EFUSE_REG_24, 0x00 }, + { WCD937X_DIGITAL_EFUSE_REG_25, 0x00 }, + { WCD937X_DIGITAL_EFUSE_REG_26, 0x00 }, + { WCD937X_DIGITAL_EFUSE_REG_27, 0x00 }, + { WCD937X_DIGITAL_EFUSE_REG_28, 0x00 }, + { WCD937X_DIGITAL_EFUSE_REG_29, 0x00 }, + { WCD937X_DIGITAL_EFUSE_REG_30, 0x00 }, + { WCD937X_DIGITAL_EFUSE_REG_31, 0x00 }, +}; + +static bool wcd937x_readable_register(struct device *dev, unsigned int reg) +{ + if(reg <= WCD937X_BASE_ADDRESS) + return 0; + return wcd937x_reg_access[WCD937X_REG(reg)] & RD_REG; +} + +static bool wcd937x_writeable_register(struct device *dev, unsigned int reg) +{ + if(reg <= WCD937X_BASE_ADDRESS) + return 0; + return wcd937x_reg_access[WCD937X_REG(reg)] & WR_REG; +} + +static bool wcd937x_volatile_register(struct device *dev, unsigned int reg) +{ + if(reg <= WCD937X_BASE_ADDRESS) + return 0; + if ((wcd937x_reg_access[WCD937X_REG(reg)] & RD_REG) + && !(wcd937x_reg_access[WCD937X_REG(reg)] & WR_REG)) + return true; + return false; +} + +struct regmap_config wcd937x_regmap_config = { + .name = "wcd937x_csr", + .reg_bits = 16, + .val_bits = 8, + .cache_type = REGCACHE_RBTREE, + .reg_defaults = wcd937x_defaults, + .num_reg_defaults = ARRAY_SIZE(wcd937x_defaults), + .max_register = WCD937X_MAX_REGISTER, + .readable_reg = wcd937x_readable_register, + .writeable_reg = wcd937x_writeable_register, + .volatile_reg = wcd937x_volatile_register, + .can_multi_write = true, +}; diff --git a/audio-kernel/asoc/codecs/wcd937x/wcd937x-tables.c b/audio-kernel/asoc/codecs/wcd937x/wcd937x-tables.c new file mode 100644 index 000000000..5baf77335 --- /dev/null +++ b/audio-kernel/asoc/codecs/wcd937x/wcd937x-tables.c @@ -0,0 +1,431 @@ +/* + * 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 +#include "wcd937x-registers.h" + +const u8 wcd937x_reg_access[WCD937X_REG(WCD937X_REGISTERS_MAX_SIZE)] = { + [WCD937X_REG(WCD937X_ANA_BIAS)] = RD_WR_REG, + [WCD937X_REG(WCD937X_ANA_RX_SUPPLIES)] = RD_WR_REG, + [WCD937X_REG(WCD937X_ANA_HPH)] = RD_WR_REG, + [WCD937X_REG(WCD937X_ANA_EAR)] = RD_WR_REG, + [WCD937X_REG(WCD937X_ANA_EAR_COMPANDER_CTL)] = RD_WR_REG, + [WCD937X_REG(WCD937X_ANA_TX_CH1)] = RD_WR_REG, + [WCD937X_REG(WCD937X_ANA_TX_CH2)] = RD_WR_REG, + [WCD937X_REG(WCD937X_ANA_TX_CH3)] = RD_WR_REG, + [WCD937X_REG(WCD937X_ANA_TX_CH3_HPF)] = RD_WR_REG, + [WCD937X_REG(WCD937X_ANA_MICB1_MICB2_DSP_EN_LOGIC)] = RD_WR_REG, + [WCD937X_REG(WCD937X_ANA_MICB3_DSP_EN_LOGIC)] = RD_WR_REG, + [WCD937X_REG(WCD937X_ANA_MBHC_MECH)] = RD_WR_REG, + [WCD937X_REG(WCD937X_ANA_MBHC_ELECT)] = RD_WR_REG, + [WCD937X_REG(WCD937X_ANA_MBHC_ZDET)] = RD_WR_REG, + [WCD937X_REG(WCD937X_ANA_MBHC_RESULT_1)] = RD_REG, + [WCD937X_REG(WCD937X_ANA_MBHC_RESULT_2)] = RD_REG, + [WCD937X_REG(WCD937X_ANA_MBHC_RESULT_3)] = RD_REG, + [WCD937X_REG(WCD937X_ANA_MBHC_BTN0)] = RD_WR_REG, + [WCD937X_REG(WCD937X_ANA_MBHC_BTN1)] = RD_WR_REG, + [WCD937X_REG(WCD937X_ANA_MBHC_BTN2)] = RD_WR_REG, + [WCD937X_REG(WCD937X_ANA_MBHC_BTN3)] = RD_WR_REG, + [WCD937X_REG(WCD937X_ANA_MBHC_BTN4)] = RD_WR_REG, + [WCD937X_REG(WCD937X_ANA_MBHC_BTN5)] = RD_WR_REG, + [WCD937X_REG(WCD937X_ANA_MBHC_BTN6)] = RD_WR_REG, + [WCD937X_REG(WCD937X_ANA_MBHC_BTN7)] = RD_WR_REG, + [WCD937X_REG(WCD937X_ANA_MICB1)] = RD_WR_REG, + [WCD937X_REG(WCD937X_ANA_MICB2)] = RD_WR_REG, + [WCD937X_REG(WCD937X_ANA_MICB2_RAMP)] = RD_WR_REG, + [WCD937X_REG(WCD937X_ANA_MICB3)] = RD_WR_REG, + [WCD937X_REG(WCD937X_BIAS_CTL)] = RD_WR_REG, + [WCD937X_REG(WCD937X_BIAS_VBG_FINE_ADJ)] = RD_WR_REG, + [WCD937X_REG(WCD937X_LDOL_VDDCX_ADJUST)] = RD_WR_REG, + [WCD937X_REG(WCD937X_LDOL_DISABLE_LDOL)] = RD_WR_REG, + [WCD937X_REG(WCD937X_MBHC_CTL_CLK)] = RD_WR_REG, + [WCD937X_REG(WCD937X_MBHC_CTL_ANA)] = RD_WR_REG, + [WCD937X_REG(WCD937X_MBHC_CTL_SPARE_1)] = RD_WR_REG, + [WCD937X_REG(WCD937X_MBHC_CTL_SPARE_2)] = RD_WR_REG, + [WCD937X_REG(WCD937X_MBHC_CTL_BCS)] = RD_WR_REG, + [WCD937X_REG(WCD937X_MBHC_MOISTURE_DET_FSM_STATUS)] = RD_REG, + [WCD937X_REG(WCD937X_MBHC_TEST_CTL)] = RD_WR_REG, + [WCD937X_REG(WCD937X_LDOH_MODE)] = RD_WR_REG, + [WCD937X_REG(WCD937X_LDOH_BIAS)] = RD_WR_REG, + [WCD937X_REG(WCD937X_LDOH_STB_LOADS)] = RD_WR_REG, + [WCD937X_REG(WCD937X_LDOH_SLOWRAMP)] = RD_WR_REG, + [WCD937X_REG(WCD937X_MICB1_TEST_CTL_1)] = RD_WR_REG, + [WCD937X_REG(WCD937X_MICB1_TEST_CTL_2)] = RD_WR_REG, + [WCD937X_REG(WCD937X_MICB1_TEST_CTL_3)] = RD_WR_REG, + [WCD937X_REG(WCD937X_MICB2_TEST_CTL_1)] = RD_WR_REG, + [WCD937X_REG(WCD937X_MICB2_TEST_CTL_2)] = RD_WR_REG, + [WCD937X_REG(WCD937X_MICB2_TEST_CTL_3)] = RD_WR_REG, + [WCD937X_REG(WCD937X_MICB3_TEST_CTL_1)] = RD_WR_REG, + [WCD937X_REG(WCD937X_MICB3_TEST_CTL_2)] = RD_WR_REG, + [WCD937X_REG(WCD937X_MICB3_TEST_CTL_3)] = RD_WR_REG, + [WCD937X_REG(WCD937X_TX_COM_ADC_VCM)] = RD_WR_REG, + [WCD937X_REG(WCD937X_TX_COM_BIAS_ATEST)] = RD_WR_REG, + [WCD937X_REG(WCD937X_TX_COM_ADC_INT1_IB)] = RD_WR_REG, + [WCD937X_REG(WCD937X_TX_COM_ADC_INT2_IB)] = RD_WR_REG, + [WCD937X_REG(WCD937X_TX_COM_TXFE_DIV_CTL)] = RD_WR_REG, + [WCD937X_REG(WCD937X_TX_COM_TXFE_DIV_START)] = RD_WR_REG, + [WCD937X_REG(WCD937X_TX_COM_TXFE_DIV_STOP_9P6M)] = RD_WR_REG, + [WCD937X_REG(WCD937X_TX_COM_TXFE_DIV_STOP_12P288M)] = RD_WR_REG, + [WCD937X_REG(WCD937X_TX_1_2_TEST_EN)] = RD_WR_REG, + [WCD937X_REG(WCD937X_TX_1_2_ADC_IB)] = RD_WR_REG, + [WCD937X_REG(WCD937X_TX_1_2_ATEST_REFCTL)] = RD_WR_REG, + [WCD937X_REG(WCD937X_TX_1_2_TEST_CTL)] = RD_WR_REG, + [WCD937X_REG(WCD937X_TX_1_2_TEST_BLK_EN)] = RD_WR_REG, + [WCD937X_REG(WCD937X_TX_1_2_TXFE_CLKDIV)] = RD_WR_REG, + [WCD937X_REG(WCD937X_TX_1_2_SAR2_ERR)] = RD_REG, + [WCD937X_REG(WCD937X_TX_1_2_SAR1_ERR)] = RD_REG, + [WCD937X_REG(WCD937X_TX_3_TEST_EN)] = RD_WR_REG, + [WCD937X_REG(WCD937X_TX_3_ADC_IB)] = RD_WR_REG, + [WCD937X_REG(WCD937X_TX_3_ATEST_REFCTL)] = RD_WR_REG, + [WCD937X_REG(WCD937X_TX_3_TEST_CTL)] = RD_WR_REG, + [WCD937X_REG(WCD937X_TX_3_TEST_BLK_EN)] = RD_WR_REG, + [WCD937X_REG(WCD937X_TX_3_TXFE_CLKDIV)] = RD_WR_REG, + [WCD937X_REG(WCD937X_TX_3_SPARE_MONO)] = RD_REG, + [WCD937X_REG(WCD937X_TX_3_SAR1_ERR)] = RD_REG, + [WCD937X_REG(WCD937X_CLASSH_MODE_1)] = RD_WR_REG, + [WCD937X_REG(WCD937X_CLASSH_MODE_2)] = RD_WR_REG, + [WCD937X_REG(WCD937X_CLASSH_MODE_3)] = RD_WR_REG, + [WCD937X_REG(WCD937X_CLASSH_CTRL_VCL_1)] = RD_WR_REG, + [WCD937X_REG(WCD937X_CLASSH_CTRL_VCL_2)] = RD_WR_REG, + [WCD937X_REG(WCD937X_CLASSH_CTRL_CCL_1)] = RD_WR_REG, + [WCD937X_REG(WCD937X_CLASSH_CTRL_CCL_2)] = RD_WR_REG, + [WCD937X_REG(WCD937X_CLASSH_CTRL_CCL_3)] = RD_WR_REG, + [WCD937X_REG(WCD937X_CLASSH_CTRL_CCL_4)] = RD_WR_REG, + [WCD937X_REG(WCD937X_CLASSH_CTRL_CCL_5)] = RD_WR_REG, + [WCD937X_REG(WCD937X_CLASSH_BUCK_TMUX_A_D)] = RD_WR_REG, + [WCD937X_REG(WCD937X_CLASSH_BUCK_SW_DRV_CNTL)] = RD_WR_REG, + [WCD937X_REG(WCD937X_CLASSH_SPARE)] = RD_WR_REG, + [WCD937X_REG(WCD937X_FLYBACK_EN)] = RD_WR_REG, + [WCD937X_REG(WCD937X_FLYBACK_VNEG_CTRL_1)] = RD_WR_REG, + [WCD937X_REG(WCD937X_FLYBACK_VNEG_CTRL_2)] = RD_WR_REG, + [WCD937X_REG(WCD937X_FLYBACK_VNEG_CTRL_3)] = RD_WR_REG, + [WCD937X_REG(WCD937X_FLYBACK_VNEG_CTRL_4)] = RD_WR_REG, + [WCD937X_REG(WCD937X_FLYBACK_VNEG_CTRL_5)] = RD_WR_REG, + [WCD937X_REG(WCD937X_FLYBACK_VNEG_CTRL_6)] = RD_WR_REG, + [WCD937X_REG(WCD937X_FLYBACK_VNEG_CTRL_7)] = RD_WR_REG, + [WCD937X_REG(WCD937X_FLYBACK_VNEG_CTRL_8)] = RD_WR_REG, + [WCD937X_REG(WCD937X_FLYBACK_VNEG_CTRL_9)] = RD_WR_REG, + [WCD937X_REG(WCD937X_FLYBACK_VNEGDAC_CTRL_1)] = RD_WR_REG, + [WCD937X_REG(WCD937X_FLYBACK_VNEGDAC_CTRL_2)] = RD_WR_REG, + [WCD937X_REG(WCD937X_FLYBACK_VNEGDAC_CTRL_3)] = RD_WR_REG, + [WCD937X_REG(WCD937X_FLYBACK_CTRL_1)] = RD_WR_REG, + [WCD937X_REG(WCD937X_FLYBACK_TEST_CTL)] = RD_WR_REG, + [WCD937X_REG(WCD937X_RX_AUX_SW_CTL)] = RD_WR_REG, + [WCD937X_REG(WCD937X_RX_PA_AUX_IN_CONN)] = RD_WR_REG, + [WCD937X_REG(WCD937X_RX_TIMER_DIV)] = RD_WR_REG, + [WCD937X_REG(WCD937X_RX_OCP_CTL)] = RD_WR_REG, + [WCD937X_REG(WCD937X_RX_OCP_COUNT)] = RD_WR_REG, + [WCD937X_REG(WCD937X_RX_BIAS_EAR_DAC)] = RD_WR_REG, + [WCD937X_REG(WCD937X_RX_BIAS_EAR_AMP)] = RD_WR_REG, + [WCD937X_REG(WCD937X_RX_BIAS_HPH_LDO)] = RD_WR_REG, + [WCD937X_REG(WCD937X_RX_BIAS_HPH_PA)] = RD_WR_REG, + [WCD937X_REG(WCD937X_RX_BIAS_HPH_RDACBUFF_CNP2)] = RD_WR_REG, + [WCD937X_REG(WCD937X_RX_BIAS_HPH_RDAC_LDO)] = RD_WR_REG, + [WCD937X_REG(WCD937X_RX_BIAS_HPH_CNP1)] = RD_WR_REG, + [WCD937X_REG(WCD937X_RX_BIAS_HPH_LOWPOWER)] = RD_WR_REG, + [WCD937X_REG(WCD937X_RX_BIAS_AUX_DAC)] = RD_WR_REG, + [WCD937X_REG(WCD937X_RX_BIAS_AUX_AMP)] = RD_WR_REG, + [WCD937X_REG(WCD937X_RX_BIAS_VNEGDAC_BLEEDER)] = RD_WR_REG, + [WCD937X_REG(WCD937X_RX_BIAS_MISC)] = RD_WR_REG, + [WCD937X_REG(WCD937X_RX_BIAS_BUCK_RST)] = RD_WR_REG, + [WCD937X_REG(WCD937X_RX_BIAS_BUCK_VREF_ERRAMP)] = RD_WR_REG, + [WCD937X_REG(WCD937X_RX_BIAS_FLYB_ERRAMP)] = RD_WR_REG, + [WCD937X_REG(WCD937X_RX_BIAS_FLYB_BUFF)] = RD_WR_REG, + [WCD937X_REG(WCD937X_RX_BIAS_FLYB_MID_RST)] = RD_WR_REG, + [WCD937X_REG(WCD937X_HPH_L_STATUS)] = RD_REG, + [WCD937X_REG(WCD937X_HPH_R_STATUS)] = RD_REG, + [WCD937X_REG(WCD937X_HPH_CNP_EN)] = RD_WR_REG, + [WCD937X_REG(WCD937X_HPH_CNP_WG_CTL)] = RD_WR_REG, + [WCD937X_REG(WCD937X_HPH_CNP_WG_TIME)] = RD_WR_REG, + [WCD937X_REG(WCD937X_HPH_OCP_CTL)] = RD_WR_REG, + [WCD937X_REG(WCD937X_HPH_AUTO_CHOP)] = RD_WR_REG, + [WCD937X_REG(WCD937X_HPH_CHOP_CTL)] = RD_WR_REG, + [WCD937X_REG(WCD937X_HPH_PA_CTL1)] = RD_WR_REG, + [WCD937X_REG(WCD937X_HPH_PA_CTL2)] = RD_WR_REG, + [WCD937X_REG(WCD937X_HPH_L_EN)] = RD_WR_REG, + [WCD937X_REG(WCD937X_HPH_L_TEST)] = RD_WR_REG, + [WCD937X_REG(WCD937X_HPH_L_ATEST)] = RD_WR_REG, + [WCD937X_REG(WCD937X_HPH_R_EN)] = RD_WR_REG, + [WCD937X_REG(WCD937X_HPH_R_TEST)] = RD_WR_REG, + [WCD937X_REG(WCD937X_HPH_R_ATEST)] = RD_WR_REG, + [WCD937X_REG(WCD937X_HPH_RDAC_CLK_CTL1)] = RD_WR_REG, + [WCD937X_REG(WCD937X_HPH_RDAC_CLK_CTL2)] = RD_WR_REG, + [WCD937X_REG(WCD937X_HPH_RDAC_LDO_CTL)] = RD_WR_REG, + [WCD937X_REG(WCD937X_HPH_RDAC_CHOP_CLK_LP_CTL)] = RD_WR_REG, + [WCD937X_REG(WCD937X_HPH_REFBUFF_UHQA_CTL)] = RD_WR_REG, + [WCD937X_REG(WCD937X_HPH_REFBUFF_LP_CTL)] = RD_WR_REG, + [WCD937X_REG(WCD937X_HPH_L_DAC_CTL)] = RD_WR_REG, + [WCD937X_REG(WCD937X_HPH_R_DAC_CTL)] = RD_WR_REG, + [WCD937X_REG(WCD937X_HPH_SURGE_HPHLR_SURGE_COMP_SEL)] = RD_WR_REG, + [WCD937X_REG(WCD937X_HPH_SURGE_HPHLR_SURGE_EN)] = RD_WR_REG, + [WCD937X_REG(WCD937X_HPH_SURGE_HPHLR_SURGE_MISC1)] = RD_WR_REG, + [WCD937X_REG(WCD937X_HPH_SURGE_HPHLR_SURGE_STATUS)] = RD_REG, + [WCD937X_REG(WCD937X_EAR_EAR_EN_REG)] = RD_WR_REG, + [WCD937X_REG(WCD937X_EAR_EAR_PA_CON)] = RD_WR_REG, + [WCD937X_REG(WCD937X_EAR_EAR_SP_CON)] = RD_WR_REG, + [WCD937X_REG(WCD937X_EAR_EAR_DAC_CON)] = RD_WR_REG, + [WCD937X_REG(WCD937X_EAR_EAR_CNP_FSM_CON)] = RD_WR_REG, + [WCD937X_REG(WCD937X_EAR_TEST_CTL)] = RD_WR_REG, + [WCD937X_REG(WCD937X_EAR_STATUS_REG_1)] = RD_REG, + [WCD937X_REG(WCD937X_EAR_STATUS_REG_2)] = RD_REG, + [WCD937X_REG(WCD937X_HPH_NEW_ANA_HPH2)] = RD_WR_REG, + [WCD937X_REG(WCD937X_HPH_NEW_ANA_HPH3)] = RD_WR_REG, + [WCD937X_REG(WCD937X_SLEEP_CTL)] = RD_WR_REG, + [WCD937X_REG(WCD937X_SLEEP_WATCHDOG_CTL)] = RD_WR_REG, + [WCD937X_REG(WCD937X_MBHC_NEW_ELECT_REM_CLAMP_CTL)] = RD_WR_REG, + [WCD937X_REG(WCD937X_MBHC_NEW_CTL_1)] = RD_WR_REG, + [WCD937X_REG(WCD937X_MBHC_NEW_CTL_2)] = RD_WR_REG, + [WCD937X_REG(WCD937X_MBHC_NEW_PLUG_DETECT_CTL)] = RD_WR_REG, + [WCD937X_REG(WCD937X_MBHC_NEW_ZDET_ANA_CTL)] = RD_WR_REG, + [WCD937X_REG(WCD937X_MBHC_NEW_ZDET_RAMP_CTL)] = RD_WR_REG, + [WCD937X_REG(WCD937X_MBHC_NEW_FSM_STATUS)] = RD_REG, + [WCD937X_REG(WCD937X_MBHC_NEW_ADC_RESULT)] = RD_REG, + [WCD937X_REG(WCD937X_TX_NEW_TX_CH2_SEL)] = RD_WR_REG, + [WCD937X_REG(WCD937X_AUX_AUXPA)] = RD_WR_REG, + [WCD937X_REG(WCD937X_LDORXTX_MODE)] = RD_WR_REG, + [WCD937X_REG(WCD937X_LDORXTX_CONFIG)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIE_CRACK_DIE_CRK_DET_EN)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIE_CRACK_DIE_CRK_DET_OUT)] = RD_REG, + [WCD937X_REG(WCD937X_HPH_NEW_INT_RDAC_GAIN_CTL)] = RD_WR_REG, + [WCD937X_REG(WCD937X_HPH_NEW_INT_RDAC_HD2_CTL_L)] = RD_WR_REG, + [WCD937X_REG(WCD937X_HPH_NEW_INT_RDAC_VREF_CTL)] = RD_WR_REG, + [WCD937X_REG(WCD937X_HPH_NEW_INT_RDAC_OVERRIDE_CTL)] = RD_WR_REG, + [WCD937X_REG(WCD937X_HPH_NEW_INT_RDAC_HD2_CTL_R)] = RD_WR_REG, + [WCD937X_REG(WCD937X_HPH_NEW_INT_PA_MISC1)] = RD_WR_REG, + [WCD937X_REG(WCD937X_HPH_NEW_INT_PA_MISC2)] = RD_WR_REG, + [WCD937X_REG(WCD937X_HPH_NEW_INT_PA_RDAC_MISC)] = RD_WR_REG, + [WCD937X_REG(WCD937X_HPH_NEW_INT_HPH_TIMER1)] = RD_WR_REG, + [WCD937X_REG(WCD937X_HPH_NEW_INT_HPH_TIMER2)] = RD_WR_REG, + [WCD937X_REG(WCD937X_HPH_NEW_INT_HPH_TIMER3)] = RD_WR_REG, + [WCD937X_REG(WCD937X_HPH_NEW_INT_HPH_TIMER4)] = RD_WR_REG, + [WCD937X_REG(WCD937X_HPH_NEW_INT_PA_RDAC_MISC2)] = RD_WR_REG, + [WCD937X_REG(WCD937X_HPH_NEW_INT_PA_RDAC_MISC3)] = RD_WR_REG, + [WCD937X_REG(WCD937X_RX_NEW_INT_HPH_RDAC_BIAS_LOHIFI)] = RD_WR_REG, + [WCD937X_REG(WCD937X_RX_NEW_INT_HPH_RDAC_BIAS_ULP)] = RD_WR_REG, + [WCD937X_REG(WCD937X_RX_NEW_INT_HPH_RDAC_LDO_LP)] = RD_WR_REG, + [WCD937X_REG(WCD937X_MBHC_NEW_INT_MOISTURE_DET_DC_CTRL)] = RD_WR_REG, + [WCD937X_REG(WCD937X_MBHC_NEW_INT_MOISTURE_DET_POLLING_CTRL)] + = RD_WR_REG, + [WCD937X_REG(WCD937X_MBHC_NEW_INT_MECH_DET_CURRENT)] = RD_WR_REG, + [WCD937X_REG(WCD937X_MBHC_NEW_INT_SPARE_2)] = RD_WR_REG, + [WCD937X_REG(WCD937X_EAR_INT_NEW_EAR_CHOPPER_CON)] = RD_WR_REG, + [WCD937X_REG(WCD937X_EAR_INT_NEW_CNP_VCM_CON1)] = RD_WR_REG, + [WCD937X_REG(WCD937X_EAR_INT_NEW_CNP_VCM_CON2)] = RD_WR_REG, + [WCD937X_REG(WCD937X_EAR_INT_NEW_EAR_DYNAMIC_BIAS)] = RD_WR_REG, + [WCD937X_REG(WCD937X_AUX_INT_EN_REG)] = RD_WR_REG, + [WCD937X_REG(WCD937X_AUX_INT_PA_CTRL)] = RD_WR_REG, + [WCD937X_REG(WCD937X_AUX_INT_SP_CTRL)] = RD_WR_REG, + [WCD937X_REG(WCD937X_AUX_INT_DAC_CTRL)] = RD_WR_REG, + [WCD937X_REG(WCD937X_AUX_INT_CLK_CTRL)] = RD_WR_REG, + [WCD937X_REG(WCD937X_AUX_INT_TEST_CTRL)] = RD_WR_REG, + [WCD937X_REG(WCD937X_AUX_INT_STATUS_REG)] = RD_REG, + [WCD937X_REG(WCD937X_AUX_INT_MISC)] = RD_WR_REG, + [WCD937X_REG(WCD937X_LDORXTX_INT_BIAS)] = RD_WR_REG, + [WCD937X_REG(WCD937X_LDORXTX_INT_STB_LOADS_DTEST)] = RD_WR_REG, + [WCD937X_REG(WCD937X_LDORXTX_INT_TEST0)] = RD_WR_REG, + [WCD937X_REG(WCD937X_LDORXTX_INT_STARTUP_TIMER)] = RD_WR_REG, + [WCD937X_REG(WCD937X_LDORXTX_INT_TEST1)] = RD_WR_REG, + [WCD937X_REG(WCD937X_LDORXTX_INT_STATUS)] = RD_REG, + [WCD937X_REG(WCD937X_SLEEP_INT_WATCHDOG_CTL_1)] = RD_WR_REG, + [WCD937X_REG(WCD937X_SLEEP_INT_WATCHDOG_CTL_2)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIE_CRACK_INT_DIE_CRK_DET_INT1)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIE_CRACK_INT_DIE_CRK_DET_INT2)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_CHIP_ID0)] = RD_REG, + [WCD937X_REG(WCD937X_DIGITAL_CHIP_ID1)] = RD_REG, + [WCD937X_REG(WCD937X_DIGITAL_CHIP_ID2)] = RD_REG, + [WCD937X_REG(WCD937X_DIGITAL_CHIP_ID3)] = RD_REG, + [WCD937X_REG(WCD937X_DIGITAL_CDC_RST_CTL)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_TOP_CLK_CFG)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_CDC_ANA_CLK_CTL)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_CDC_DIG_CLK_CTL)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_SWR_RST_EN)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_CDC_PATH_MODE)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_CDC_RX_RST)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_CDC_RX0_CTL)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_CDC_RX1_CTL)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_CDC_RX2_CTL)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_DEM_BYPASS_DATA0)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_DEM_BYPASS_DATA1)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_DEM_BYPASS_DATA2)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_DEM_BYPASS_DATA3)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_CDC_COMP_CTL_0)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_CDC_RX_DELAY_CTL)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_CDC_HPH_DSM_A1_0)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_CDC_HPH_DSM_A1_1)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_CDC_HPH_DSM_A2_0)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_CDC_HPH_DSM_A2_1)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_CDC_HPH_DSM_A3_0)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_CDC_HPH_DSM_A3_1)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_CDC_HPH_DSM_A4_0)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_CDC_HPH_DSM_A4_1)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_CDC_HPH_DSM_A5_0)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_CDC_HPH_DSM_A5_1)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_CDC_HPH_DSM_A6_0)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_CDC_HPH_DSM_A7_0)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_CDC_HPH_DSM_C_0)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_CDC_HPH_DSM_C_1)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_CDC_HPH_DSM_C_2)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_CDC_HPH_DSM_C_3)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_CDC_HPH_DSM_R1)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_CDC_HPH_DSM_R2)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_CDC_HPH_DSM_R3)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_CDC_HPH_DSM_R4)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_CDC_HPH_DSM_R5)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_CDC_HPH_DSM_R6)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_CDC_HPH_DSM_R7)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_CDC_AUX_DSM_A1_0)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_CDC_AUX_DSM_A1_1)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_CDC_AUX_DSM_A2_0)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_CDC_AUX_DSM_A2_1)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_CDC_AUX_DSM_A3_0)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_CDC_AUX_DSM_A3_1)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_CDC_AUX_DSM_A4_0)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_CDC_AUX_DSM_A4_1)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_CDC_AUX_DSM_A5_0)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_CDC_AUX_DSM_A5_1)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_CDC_AUX_DSM_A6_0)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_CDC_AUX_DSM_A7_0)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_CDC_AUX_DSM_C_0)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_CDC_AUX_DSM_C_1)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_CDC_AUX_DSM_C_2)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_CDC_AUX_DSM_C_3)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_CDC_AUX_DSM_R1)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_CDC_AUX_DSM_R2)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_CDC_AUX_DSM_R3)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_CDC_AUX_DSM_R4)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_CDC_AUX_DSM_R5)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_CDC_AUX_DSM_R6)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_CDC_AUX_DSM_R7)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_CDC_HPH_GAIN_RX_0)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_CDC_HPH_GAIN_RX_1)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_CDC_HPH_GAIN_DSD_0)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_CDC_HPH_GAIN_DSD_1)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_CDC_HPH_GAIN_DSD_2)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_CDC_AUX_GAIN_DSD_0)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_CDC_AUX_GAIN_DSD_1)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_CDC_AUX_GAIN_DSD_2)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_CDC_HPH_GAIN_CTL)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_CDC_AUX_GAIN_CTL)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_CDC_EAR_PATH_CTL)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_CDC_SWR_CLH)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_SWR_CLH_BYP)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_CDC_TX0_CTL)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_CDC_TX1_CTL)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_CDC_TX2_CTL)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_CDC_TX_RST)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_CDC_REQ_CTL)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_CDC_AMIC_CTL)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_CDC_DMIC_CTL)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_CDC_DMIC0_CTL)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_CDC_DMIC1_CTL)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_CDC_DMIC2_CTL)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_EFUSE_CTL)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_EFUSE_PRG_CTL)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_EFUSE_TEST_CTL_0)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_EFUSE_TEST_CTL_1)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_EFUSE_T_DATA_0)] = RD_REG, + [WCD937X_REG(WCD937X_DIGITAL_EFUSE_T_DATA_1)] = RD_REG, + [WCD937X_REG(WCD937X_DIGITAL_PDM_WD_CTL0)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_PDM_WD_CTL1)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_PDM_WD_CTL2)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_INTR_MODE)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_INTR_MASK_0)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_INTR_MASK_1)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_INTR_MASK_2)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_INTR_STATUS_0)] = RD_REG, + [WCD937X_REG(WCD937X_DIGITAL_INTR_STATUS_1)] = RD_REG, + [WCD937X_REG(WCD937X_DIGITAL_INTR_STATUS_2)] = RD_REG, + [WCD937X_REG(WCD937X_DIGITAL_INTR_CLEAR_0)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_INTR_CLEAR_1)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_INTR_CLEAR_2)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_INTR_LEVEL_0)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_INTR_LEVEL_1)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_INTR_LEVEL_2)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_INTR_SET_0)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_INTR_SET_1)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_INTR_SET_2)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_INTR_TEST_0)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_INTR_TEST_1)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_INTR_TEST_2)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_CDC_CONN_RX0_CTL)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_CDC_CONN_RX1_CTL)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_CDC_CONN_RX2_CTL)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_CDC_CONN_TX_CTL)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_LOOP_BACK_MODE)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_SWR_DAC_TEST)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_SWR_HM_TEST_RX_0)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_SWR_HM_TEST_TX_0)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_SWR_HM_TEST_RX_1)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_SWR_HM_TEST_TX_1)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_SWR_HM_TEST)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_PAD_CTL_PDM_RX0)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_PAD_CTL_PDM_RX1)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_PAD_CTL_PDM_RX2)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_PAD_CTL_PDM_TX)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_PAD_CTL_PDM_TX)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_PAD_INP_DIS_0)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_PAD_INP_DIS_1)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_DRIVE_STRENGTH_0)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_DRIVE_STRENGTH_1)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_DRIVE_STRENGTH_2)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_RX_DATA_EDGE_CTL)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_TX_DATA_EDGE_CTL)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_GPIO_MODE)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_PIN_CTL_OE)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_PIN_CTL_DATA_0)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_PIN_CTL_DATA_1)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_PIN_STATUS_0)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_PIN_STATUS_1)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_DIG_DEBUG_CTL)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_DIG_DEBUG_EN)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_ANA_CSR_DBG_ADD)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_ANA_CSR_DBG_CTL)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_SSP_DBG)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_MODE_STATUS_0)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_MODE_STATUS_1)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_SPARE_0)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_SPARE_1)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_SPARE_2)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_EFUSE_REG_0)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_EFUSE_REG_1)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_EFUSE_REG_2)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_EFUSE_REG_3)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_EFUSE_REG_4)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_EFUSE_REG_5)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_EFUSE_REG_6)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_EFUSE_REG_7)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_EFUSE_REG_8)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_EFUSE_REG_9)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_EFUSE_REG_10)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_EFUSE_REG_11)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_EFUSE_REG_12)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_EFUSE_REG_13)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_EFUSE_REG_14)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_EFUSE_REG_15)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_EFUSE_REG_16)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_EFUSE_REG_17)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_EFUSE_REG_18)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_EFUSE_REG_19)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_EFUSE_REG_20)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_EFUSE_REG_21)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_EFUSE_REG_22)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_EFUSE_REG_23)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_EFUSE_REG_24)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_EFUSE_REG_25)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_EFUSE_REG_26)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_EFUSE_REG_27)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_EFUSE_REG_28)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_EFUSE_REG_29)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_EFUSE_REG_30)] = RD_WR_REG, + [WCD937X_REG(WCD937X_DIGITAL_EFUSE_REG_31)] = RD_WR_REG, +}; diff --git a/audio-kernel/asoc/codecs/wcd937x/wcd937x.c b/audio-kernel/asoc/codecs/wcd937x/wcd937x.c new file mode 100644 index 000000000..06acd4447 --- /dev/null +++ b/audio-kernel/asoc/codecs/wcd937x/wcd937x.c @@ -0,0 +1,2787 @@ +/* + * Copyright (c) 2018-2019, 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "internal.h" +#include "wcd937x.h" +#include "../wcdcal-hwdep.h" +#include "wcd937x-registers.h" +#include "../msm-cdc-pinctrl.h" +#include +#include "../msm-cdc-supply.h" + +#define WCD9370_VARIANT 0 +#define WCD9375_VARIANT 5 + +#define NUM_SWRS_DT_PARAMS 5 + +#define WCD937X_VERSION_1_0 1 +#define WCD937X_VERSION_ENTRY_SIZE 32 + +enum { + CODEC_TX = 0, + CODEC_RX, +}; + +enum { + ALLOW_BUCK_DISABLE, + HPH_COMP_DELAY, + HPH_PA_DELAY, +}; + +static const DECLARE_TLV_DB_SCALE(line_gain, 0, 7, 1); +static const DECLARE_TLV_DB_SCALE(analog_gain, 0, 25, 1); + +static int wcd937x_handle_post_irq(void *data); +static int wcd937x_reset(struct device *dev); +static int wcd937x_reset_low(struct device *dev); + +static const struct regmap_irq wcd937x_irqs[WCD937X_NUM_IRQS] = { + REGMAP_IRQ_REG(WCD937X_IRQ_MBHC_BUTTON_PRESS_DET, 0, 0x01), + REGMAP_IRQ_REG(WCD937X_IRQ_MBHC_BUTTON_RELEASE_DET, 0, 0x02), + REGMAP_IRQ_REG(WCD937X_IRQ_MBHC_ELECT_INS_REM_DET, 0, 0x04), + REGMAP_IRQ_REG(WCD937X_IRQ_MBHC_ELECT_INS_REM_LEG_DET, 0, 0x08), + REGMAP_IRQ_REG(WCD937X_IRQ_MBHC_SW_DET, 0, 0x10), + REGMAP_IRQ_REG(WCD937X_IRQ_HPHR_OCP_INT, 0, 0x20), + REGMAP_IRQ_REG(WCD937X_IRQ_HPHR_CNP_INT, 0, 0x40), + REGMAP_IRQ_REG(WCD937X_IRQ_HPHL_OCP_INT, 0, 0x80), + REGMAP_IRQ_REG(WCD937X_IRQ_HPHL_CNP_INT, 1, 0x01), + REGMAP_IRQ_REG(WCD937X_IRQ_EAR_CNP_INT, 1, 0x02), + REGMAP_IRQ_REG(WCD937X_IRQ_EAR_SCD_INT, 1, 0x04), + REGMAP_IRQ_REG(WCD937X_IRQ_AUX_CNP_INT, 1, 0x08), + REGMAP_IRQ_REG(WCD937X_IRQ_AUX_SCD_INT, 1, 0x10), + REGMAP_IRQ_REG(WCD937X_IRQ_HPHL_PDM_WD_INT, 1, 0x20), + REGMAP_IRQ_REG(WCD937X_IRQ_HPHR_PDM_WD_INT, 1, 0x40), + REGMAP_IRQ_REG(WCD937X_IRQ_AUX_PDM_WD_INT, 1, 0x80), + REGMAP_IRQ_REG(WCD937X_IRQ_LDORT_SCD_INT, 2, 0x01), + REGMAP_IRQ_REG(WCD937X_IRQ_MBHC_MOISTURE_INT, 2, 0x02), + REGMAP_IRQ_REG(WCD937X_IRQ_HPHL_SURGE_DET_INT, 2, 0x04), + REGMAP_IRQ_REG(WCD937X_IRQ_HPHR_SURGE_DET_INT, 2, 0x08), +}; + +static struct regmap_irq_chip wcd937x_regmap_irq_chip = { + .name = "wcd937x", + .irqs = wcd937x_irqs, + .num_irqs = ARRAY_SIZE(wcd937x_irqs), + .num_regs = 3, + .status_base = WCD937X_DIGITAL_INTR_STATUS_0, + .mask_base = WCD937X_DIGITAL_INTR_MASK_0, + .ack_base = WCD937X_DIGITAL_INTR_CLEAR_0, + .use_ack = 1, + .clear_ack = 1, + .type_base = WCD937X_DIGITAL_INTR_LEVEL_0, + .runtime_pm = false, + .handle_post_irq = wcd937x_handle_post_irq, + .irq_drv_data = NULL, +}; +static int wcd937x_handle_post_irq(void *data) +{ + struct wcd937x_priv *wcd937x = data; + u32 status1 = 0, status2 = 0, status3 = 0; + + regmap_read(wcd937x->regmap, WCD937X_DIGITAL_INTR_STATUS_0, &status1); + regmap_read(wcd937x->regmap, WCD937X_DIGITAL_INTR_STATUS_1, &status2); + regmap_read(wcd937x->regmap, WCD937X_DIGITAL_INTR_STATUS_2, &status3); + + wcd937x->tx_swr_dev->slave_irq_pending = + ((status1 || status2 || status3) ? true : false); + + return IRQ_HANDLED; +} + +static int wcd937x_init_reg(struct snd_soc_codec *codec) +{ + snd_soc_update_bits(codec, WCD937X_SLEEP_CTL, 0x0E, 0x0E); + snd_soc_update_bits(codec, WCD937X_SLEEP_CTL, 0x80, 0x80); + usleep_range(1000, 1010); + snd_soc_update_bits(codec, WCD937X_SLEEP_CTL, 0x40, 0x40); + usleep_range(1000, 1010); + snd_soc_update_bits(codec, WCD937X_LDORXTX_CONFIG, 0x10, 0x00); + snd_soc_update_bits(codec, WCD937X_BIAS_VBG_FINE_ADJ, 0xF0, 0x80); + snd_soc_update_bits(codec, WCD937X_ANA_BIAS, 0x80, 0x80); + snd_soc_update_bits(codec, WCD937X_ANA_BIAS, 0x40, 0x40); + usleep_range(10000, 10010); + snd_soc_update_bits(codec, WCD937X_ANA_BIAS, 0x40, 0x00); + snd_soc_update_bits(codec, WCD937X_HPH_OCP_CTL, 0xFF, 0x3A); + snd_soc_update_bits(codec, WCD937X_RX_OCP_CTL, 0x0F, 0x02); + snd_soc_update_bits(codec, WCD937X_HPH_SURGE_HPHLR_SURGE_EN, 0xFF, + 0xD9); + return 0; +} + +static int wcd937x_set_port_params(struct snd_soc_codec *codec, u8 slv_prt_type, + u8 *port_id, u8 *num_ch, u8 *ch_mask, u32 *ch_rate, + u8 *port_type, u8 path) +{ + int i, j; + u8 num_ports = 0; + struct codec_port_info (*map)[MAX_PORT][MAX_CH_PER_PORT] = NULL; + struct wcd937x_priv *wcd937x = snd_soc_codec_get_drvdata(codec); + + switch (path) { + case CODEC_RX: + map = &wcd937x->rx_port_mapping; + num_ports = wcd937x->num_rx_ports; + break; + case CODEC_TX: + map = &wcd937x->tx_port_mapping; + num_ports = wcd937x->num_tx_ports; + break; + } + + for (i = 0; i <= num_ports; i++) { + for (j = 0; j < MAX_CH_PER_PORT; j++) { + if ((*map)[i][j].slave_port_type == slv_prt_type) + goto found; + } + } +found: + if (i > num_ports || j == MAX_CH_PER_PORT) { + dev_err(codec->dev, "%s Failed to find slave port for type %u\n", + __func__, slv_prt_type); + return -EINVAL; + } + *port_id = i; + *num_ch = (*map)[i][j].num_ch; + *ch_mask = (*map)[i][j].ch_mask; + *ch_rate = (*map)[i][j].ch_rate; + *port_type = (*map)[i][j].master_port_type; + + return 0; +} + +static int wcd937x_parse_port_mapping(struct device *dev, + char *prop, u8 path) +{ + u32 *dt_array, map_size, map_length; + u32 port_num = 0, ch_mask, ch_rate, old_port_num = 0; + u32 slave_port_type, master_port_type; + u32 i, ch_iter = 0; + int ret = 0; + u8 *num_ports = NULL; + struct codec_port_info (*map)[MAX_PORT][MAX_CH_PER_PORT] = NULL; + struct wcd937x_priv *wcd937x = dev_get_drvdata(dev); + + switch (path) { + case CODEC_RX: + map = &wcd937x->rx_port_mapping; + num_ports = &wcd937x->num_rx_ports; + break; + case CODEC_TX: + map = &wcd937x->tx_port_mapping; + num_ports = &wcd937x->num_tx_ports; + break; + } + + if (!of_find_property(dev->of_node, prop, + &map_size)) { + dev_err(dev, "missing port mapping prop %s\n", prop); + ret = -EINVAL; + goto err; + } + + map_length = map_size / (NUM_SWRS_DT_PARAMS * sizeof(u32)); + + dt_array = kzalloc(map_size, GFP_KERNEL); + + if (!dt_array) { + ret = -ENOMEM; + goto err; + } + ret = of_property_read_u32_array(dev->of_node, prop, dt_array, + NUM_SWRS_DT_PARAMS * map_length); + if (ret) { + dev_err(dev, "%s: Failed to read port mapping from prop %s\n", + __func__, prop); + ret = -EINVAL; + goto err_pdata_fail; + } + + for (i = 0; i < map_length; i++) { + port_num = dt_array[NUM_SWRS_DT_PARAMS * i]; + slave_port_type = dt_array[NUM_SWRS_DT_PARAMS * i + 1]; + ch_mask = dt_array[NUM_SWRS_DT_PARAMS * i + 2]; + ch_rate = dt_array[NUM_SWRS_DT_PARAMS * i + 3]; + master_port_type = dt_array[NUM_SWRS_DT_PARAMS * i + 4]; + + if (port_num != old_port_num) + ch_iter = 0; + + (*map)[port_num][ch_iter].slave_port_type = slave_port_type; + (*map)[port_num][ch_iter].ch_mask = ch_mask; + (*map)[port_num][ch_iter].master_port_type = master_port_type; + (*map)[port_num][ch_iter].num_ch = __sw_hweight8(ch_mask); + (*map)[port_num][ch_iter++].ch_rate = ch_rate; + old_port_num = port_num; + } + *num_ports = port_num; + kfree(dt_array); + return 0; + +err_pdata_fail: + kfree(dt_array); +err: + return ret; +} + +static int wcd937x_tx_connect_port(struct snd_soc_codec *codec, + u8 slv_port_type, u8 enable) +{ + struct wcd937x_priv *wcd937x = snd_soc_codec_get_drvdata(codec); + u8 port_id; + u8 num_ch; + u8 ch_mask; + u32 ch_rate; + u8 port_type; + u8 num_port = 1; + int ret = 0; + + ret = wcd937x_set_port_params(codec, slv_port_type, &port_id, + &num_ch, &ch_mask, &ch_rate, + &port_type, CODEC_TX); + + if (ret) + return ret; + + if (enable) + ret = swr_connect_port(wcd937x->tx_swr_dev, &port_id, + num_port, &ch_mask, &ch_rate, + &num_ch, &port_type); + else + ret = swr_disconnect_port(wcd937x->tx_swr_dev, &port_id, + num_port, &ch_mask, &port_type); + return ret; + +} +static int wcd937x_rx_connect_port(struct snd_soc_codec *codec, + u8 slv_port_type, u8 enable) +{ + struct wcd937x_priv *wcd937x = snd_soc_codec_get_drvdata(codec); + u8 port_id; + u8 num_ch; + u8 ch_mask; + u32 ch_rate; + u8 port_type; + u8 num_port = 1; + int ret = 0; + + ret = wcd937x_set_port_params(codec, slv_port_type, &port_id, + &num_ch, &ch_mask, &ch_rate, + &port_type, CODEC_RX); + + if (ret) + return ret; + + if (enable) + ret = swr_connect_port(wcd937x->rx_swr_dev, &port_id, + num_port, &ch_mask, &ch_rate, + &num_ch, &port_type); + else + ret = swr_disconnect_port(wcd937x->rx_swr_dev, &port_id, + num_port, &ch_mask, &port_type); + return ret; +} + +static int wcd937x_rx_clk_enable(struct snd_soc_codec *codec) +{ + struct wcd937x_priv *wcd937x = snd_soc_codec_get_drvdata(codec); + + if (wcd937x->rx_clk_cnt == 0) { + snd_soc_update_bits(codec, WCD937X_DIGITAL_CDC_DIG_CLK_CTL, + 0x08, 0x08); + snd_soc_update_bits(codec, WCD937X_DIGITAL_CDC_ANA_CLK_CTL, + 0x01, 0x01); + snd_soc_update_bits(codec, WCD937X_ANA_RX_SUPPLIES, 0x01, 0x01); + snd_soc_update_bits(codec, WCD937X_DIGITAL_CDC_RX0_CTL, + 0x40, 0x00); + snd_soc_update_bits(codec, WCD937X_DIGITAL_CDC_RX1_CTL, + 0x40, 0x00); + snd_soc_update_bits(codec, WCD937X_DIGITAL_CDC_RX2_CTL, + 0x40, 0x00); + snd_soc_update_bits(codec, WCD937X_DIGITAL_CDC_ANA_CLK_CTL, + 0x02, 0x02); + } + wcd937x->rx_clk_cnt++; + + return 0; +} + +static int wcd937x_rx_clk_disable(struct snd_soc_codec *codec) +{ + struct wcd937x_priv *wcd937x = snd_soc_codec_get_drvdata(codec); + + if (wcd937x->rx_clk_cnt == 0) { + dev_dbg(wcd937x->dev, "%s:clk already disabled\n", __func__); + return 0; + } + wcd937x->rx_clk_cnt--; + if (wcd937x->rx_clk_cnt == 0) { + snd_soc_update_bits(codec, WCD937X_ANA_RX_SUPPLIES, 0x01, 0x00); + snd_soc_update_bits(codec, WCD937X_DIGITAL_CDC_ANA_CLK_CTL, + 0x02, 0x00); + snd_soc_update_bits(codec, WCD937X_DIGITAL_CDC_ANA_CLK_CTL, + 0x01, 0x00); + } + return 0; +} + +/* + * wcd937x_soc_get_mbhc: get wcd937x_mbhc handle of corresponding codec + * @codec: handle to snd_soc_codec * + * + * return wcd937x_mbhc handle or error code in case of failure + */ +struct wcd937x_mbhc *wcd937x_soc_get_mbhc(struct snd_soc_codec *codec) +{ + struct wcd937x_priv *wcd937x; + + if (!codec) { + pr_err("%s: Invalid params, NULL codec\n", __func__); + return NULL; + } + wcd937x = snd_soc_codec_get_drvdata(codec); + + if (!wcd937x) { + pr_err("%s: Invalid params, NULL tavil\n", __func__); + return NULL; + } + + return wcd937x->mbhc; +} +EXPORT_SYMBOL(wcd937x_soc_get_mbhc); + +static int wcd937x_codec_hphl_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct wcd937x_priv *wcd937x = snd_soc_codec_get_drvdata(codec); + int hph_mode = wcd937x->hph_mode; + + dev_dbg(codec->dev, "%s wname: %s event: %d\n", __func__, + w->name, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + wcd937x_rx_clk_enable(codec); + snd_soc_update_bits(codec, WCD937X_DIGITAL_CDC_DIG_CLK_CTL, + 0x01, 0x01); + snd_soc_update_bits(codec, WCD937X_DIGITAL_CDC_HPH_GAIN_CTL, + 0x04, 0x04); + snd_soc_update_bits(codec, WCD937X_HPH_RDAC_CLK_CTL1, + 0x80, 0x00); + set_bit(HPH_COMP_DELAY, &wcd937x->status_mask); + break; + case SND_SOC_DAPM_POST_PMU: + if (hph_mode == CLS_AB_HIFI || hph_mode == CLS_H_HIFI) + snd_soc_update_bits(codec, + WCD937X_HPH_NEW_INT_RDAC_HD2_CTL_L, + 0x0F, 0x02); + else if (hph_mode == CLS_H_LOHIFI) + snd_soc_update_bits(codec, + WCD937X_HPH_NEW_INT_RDAC_HD2_CTL_L, + 0x0F, 0x06); + if (wcd937x->comp1_enable) { + snd_soc_update_bits(codec, + WCD937X_DIGITAL_CDC_COMP_CTL_0, + 0x02, 0x02); + snd_soc_update_bits(codec, + WCD937X_HPH_L_EN, 0x20, 0x00); + if (wcd937x->comp2_enable) { + snd_soc_update_bits(codec, + WCD937X_DIGITAL_CDC_COMP_CTL_0, + 0x01, 0x01); + snd_soc_update_bits(codec, + WCD937X_HPH_R_EN, 0x20, 0x00); + } + /* + * 5ms sleep is required after COMP is enabled as per + * HW requirement + */ + if (test_bit(HPH_COMP_DELAY, &wcd937x->status_mask)) { + usleep_range(5000, 5100); + clear_bit(HPH_COMP_DELAY, + &wcd937x->status_mask); + } + } else { + snd_soc_update_bits(codec, + WCD937X_DIGITAL_CDC_COMP_CTL_0, + 0x02, 0x00); + snd_soc_update_bits(codec, + WCD937X_HPH_L_EN, 0x20, 0x20); + } + snd_soc_update_bits(codec, WCD937X_HPH_NEW_INT_HPH_TIMER1, + 0x02, 0x00); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_update_bits(codec, + WCD937X_HPH_NEW_INT_RDAC_HD2_CTL_L, + 0x0F, 0x01); + break; + } + + return 0; +} + +static int wcd937x_codec_hphr_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct wcd937x_priv *wcd937x = snd_soc_codec_get_drvdata(codec); + int hph_mode = wcd937x->hph_mode; + + dev_dbg(codec->dev, "%s wname: %s event: %d\n", __func__, + w->name, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + wcd937x_rx_clk_enable(codec); + snd_soc_update_bits(codec, WCD937X_DIGITAL_CDC_DIG_CLK_CTL, + 0x02, 0x02); + snd_soc_update_bits(codec, WCD937X_DIGITAL_CDC_HPH_GAIN_CTL, + 0x08, 0x08); + snd_soc_update_bits(codec, WCD937X_HPH_RDAC_CLK_CTL1, + 0x80, 0x00); + set_bit(HPH_COMP_DELAY, &wcd937x->status_mask); + break; + case SND_SOC_DAPM_POST_PMU: + if (hph_mode == CLS_AB_HIFI || hph_mode == CLS_H_HIFI) + snd_soc_update_bits(codec, + WCD937X_HPH_NEW_INT_RDAC_HD2_CTL_R, + 0x0F, 0x02); + else if (hph_mode == CLS_H_LOHIFI) + snd_soc_update_bits(codec, + WCD937X_HPH_NEW_INT_RDAC_HD2_CTL_R, + 0x0F, 0x06); + if (wcd937x->comp2_enable) { + snd_soc_update_bits(codec, + WCD937X_DIGITAL_CDC_COMP_CTL_0, + 0x01, 0x01); + snd_soc_update_bits(codec, + WCD937X_HPH_R_EN, 0x20, 0x00); + if (wcd937x->comp1_enable) { + snd_soc_update_bits(codec, + WCD937X_DIGITAL_CDC_COMP_CTL_0, + 0x02, 0x02); + snd_soc_update_bits(codec, + WCD937X_HPH_L_EN, 0x20, 0x00); + } + /* + * 5ms sleep is required after COMP is enabled as per + * HW requirement + */ + if (test_bit(HPH_COMP_DELAY, &wcd937x->status_mask)) { + usleep_range(5000, 5100); + clear_bit(HPH_COMP_DELAY, + &wcd937x->status_mask); + } + } else { + snd_soc_update_bits(codec, + WCD937X_DIGITAL_CDC_COMP_CTL_0, + 0x01, 0x00); + snd_soc_update_bits(codec, + WCD937X_HPH_R_EN, 0x20, 0x20); + } + snd_soc_update_bits(codec, WCD937X_HPH_NEW_INT_HPH_TIMER1, + 0x02, 0x00); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_update_bits(codec, + WCD937X_HPH_NEW_INT_RDAC_HD2_CTL_R, + 0x0F, 0x01); + break; + } + + return 0; +} + +static int wcd937x_codec_ear_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct wcd937x_priv *wcd937x = snd_soc_codec_get_drvdata(codec); + int hph_mode = wcd937x->hph_mode; + + dev_dbg(codec->dev, "%s wname: %s event: %d\n", __func__, + w->name, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + wcd937x_rx_clk_enable(codec); + snd_soc_update_bits(codec, WCD937X_DIGITAL_CDC_HPH_GAIN_CTL, + 0x04, 0x04); + snd_soc_update_bits(codec, WCD937X_DIGITAL_CDC_DIG_CLK_CTL, + 0x01, 0x01); + if (hph_mode == CLS_AB_HIFI || hph_mode == CLS_H_HIFI) + snd_soc_update_bits(codec, + WCD937X_HPH_NEW_INT_RDAC_HD2_CTL_L, + 0x0F, 0x02); + else if (hph_mode == CLS_H_LOHIFI) + snd_soc_update_bits(codec, + WCD937X_HPH_NEW_INT_RDAC_HD2_CTL_L, + 0x0F, 0x06); + snd_soc_update_bits(codec, WCD937X_DIGITAL_CDC_COMP_CTL_0, + 0x02, 0x02); + usleep_range(5000, 5010); + snd_soc_update_bits(codec, WCD937X_FLYBACK_EN, + 0x04, 0x00); + wcd_cls_h_fsm(codec, &wcd937x->clsh_info, + WCD_CLSH_EVENT_PRE_DAC, + WCD_CLSH_STATE_EAR, + hph_mode); + + break; + case SND_SOC_DAPM_POST_PMD: + if (hph_mode == CLS_AB_HIFI || hph_mode == CLS_H_LOHIFI || + hph_mode == CLS_H_HIFI) + snd_soc_update_bits(codec, + WCD937X_HPH_NEW_INT_RDAC_HD2_CTL_L, + 0x0F, 0x01); + break; + }; + return 0; + +} + +static int wcd937x_codec_aux_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct wcd937x_priv *wcd937x = snd_soc_codec_get_drvdata(codec); + int hph_mode = wcd937x->hph_mode; + + dev_dbg(codec->dev, "%s wname: %s event: %d\n", __func__, + w->name, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + wcd937x_rx_clk_enable(codec); + snd_soc_update_bits(codec, WCD937X_DIGITAL_CDC_ANA_CLK_CTL, + 0x04, 0x04); + snd_soc_update_bits(codec, WCD937X_DIGITAL_CDC_DIG_CLK_CTL, + 0x04, 0x04); + snd_soc_update_bits(codec, WCD937X_DIGITAL_CDC_AUX_GAIN_CTL, + 0x01, 0x01); + wcd_cls_h_fsm(codec, &wcd937x->clsh_info, + WCD_CLSH_EVENT_PRE_DAC, + WCD_CLSH_STATE_AUX, + hph_mode); + + break; + case SND_SOC_DAPM_POST_PMD: + wcd937x_rx_clk_disable(codec); + snd_soc_update_bits(codec, WCD937X_DIGITAL_CDC_ANA_CLK_CTL, + 0x04, 0x00); + break; + }; + return 0; + +} + +static int wcd937x_codec_enable_hphr_pa(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct wcd937x_priv *wcd937x = snd_soc_codec_get_drvdata(codec); + int ret = 0; + int hph_mode = wcd937x->hph_mode; + + dev_dbg(codec->dev, "%s wname: %s event: %d\n", __func__, + w->name, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + ret = swr_slvdev_datapath_control(wcd937x->rx_swr_dev, + wcd937x->rx_swr_dev->dev_num, + true); + wcd_cls_h_fsm(codec, &wcd937x->clsh_info, + WCD_CLSH_EVENT_PRE_DAC, + WCD_CLSH_STATE_HPHR, + hph_mode); + snd_soc_update_bits(codec, WCD937X_ANA_HPH, 0x10, 0x10); + usleep_range(100, 110); + set_bit(HPH_PA_DELAY, &wcd937x->status_mask); + break; + case SND_SOC_DAPM_POST_PMU: + /* + * 7ms sleep is required after PA is enabled as per + * HW requirement. If compander is disabled, then + * 20ms delay is required. + */ + if (test_bit(HPH_PA_DELAY, &wcd937x->status_mask)) { + if (!wcd937x->comp2_enable) + usleep_range(20000, 20100); + else + usleep_range(7000, 7100); + clear_bit(HPH_PA_DELAY, &wcd937x->status_mask); + } + + snd_soc_update_bits(codec, WCD937X_HPH_NEW_INT_HPH_TIMER1, + 0x02, 0x02); + snd_soc_update_bits(codec, WCD937X_HPH_R_TEST, 0x01, 0x01); + if (hph_mode == CLS_AB || hph_mode == CLS_AB_HIFI) + snd_soc_update_bits(codec, WCD937X_ANA_RX_SUPPLIES, + 0x02, 0x02); + if (wcd937x->update_wcd_event) + wcd937x->update_wcd_event(wcd937x->handle, + WCD_BOLERO_EVT_RX_MUTE, + (WCD_RX2 << 0x10)); + break; + case SND_SOC_DAPM_PRE_PMD: + snd_soc_update_bits(codec, WCD937X_HPH_R_TEST, 0x01, 0x00); + if (wcd937x->update_wcd_event) + wcd937x->update_wcd_event(wcd937x->handle, + WCD_BOLERO_EVT_RX_MUTE, + (WCD_RX2 << 0x10 | 0x1)); + blocking_notifier_call_chain(&wcd937x->mbhc->notifier, + WCD_EVENT_PRE_HPHR_PA_OFF, + &wcd937x->mbhc->wcd_mbhc); + set_bit(HPH_PA_DELAY, &wcd937x->status_mask); + break; + case SND_SOC_DAPM_POST_PMD: + /* + * 7ms sleep is required after PA is disabled as per + * HW requirement. If compander is disabled, then + * 20ms delay is required. + */ + if (test_bit(HPH_PA_DELAY, &wcd937x->status_mask)) { + if (!wcd937x->comp2_enable) + usleep_range(20000, 20100); + else + usleep_range(7000, 7100); + clear_bit(HPH_PA_DELAY, &wcd937x->status_mask); + } + + snd_soc_update_bits(codec, WCD937X_DIGITAL_PDM_WD_CTL1, + 0x17, 0x00); + blocking_notifier_call_chain(&wcd937x->mbhc->notifier, + WCD_EVENT_POST_HPHR_PA_OFF, + &wcd937x->mbhc->wcd_mbhc); + snd_soc_update_bits(codec, WCD937X_ANA_HPH, 0x10, 0x00); + wcd_cls_h_fsm(codec, &wcd937x->clsh_info, + WCD_CLSH_EVENT_POST_PA, + WCD_CLSH_STATE_HPHR, + hph_mode); + break; + }; + return ret; +} + +static int wcd937x_codec_enable_hphl_pa(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct wcd937x_priv *wcd937x = snd_soc_codec_get_drvdata(codec); + int ret = 0; + int hph_mode = wcd937x->hph_mode; + + dev_dbg(codec->dev, "%s wname: %s event: %d\n", __func__, + w->name, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + ret = swr_slvdev_datapath_control(wcd937x->rx_swr_dev, + wcd937x->rx_swr_dev->dev_num, + true); + wcd_cls_h_fsm(codec, &wcd937x->clsh_info, + WCD_CLSH_EVENT_PRE_DAC, + WCD_CLSH_STATE_HPHL, + hph_mode); + snd_soc_update_bits(codec, WCD937X_ANA_HPH, 0x20, 0x20); + usleep_range(100, 110); + set_bit(HPH_PA_DELAY, &wcd937x->status_mask); + break; + case SND_SOC_DAPM_POST_PMU: + /* + * 7ms sleep is required after PA is enabled as per + * HW requirement. If compander is disabled, then + * 20ms delay is required. + */ + if (test_bit(HPH_PA_DELAY, &wcd937x->status_mask)) { + if (!wcd937x->comp1_enable) + usleep_range(20000, 20100); + else + usleep_range(7000, 7100); + clear_bit(HPH_PA_DELAY, &wcd937x->status_mask); + } + + snd_soc_update_bits(codec, WCD937X_HPH_NEW_INT_HPH_TIMER1, + 0x02, 0x02); + snd_soc_update_bits(codec, WCD937X_HPH_L_TEST, 0x01, 0x01); + if (hph_mode == CLS_AB || hph_mode == CLS_AB_HIFI) + snd_soc_update_bits(codec, WCD937X_ANA_RX_SUPPLIES, + 0x02, 0x02); + if (wcd937x->update_wcd_event) + wcd937x->update_wcd_event(wcd937x->handle, + WCD_BOLERO_EVT_RX_MUTE, + (WCD_RX1 << 0x10)); + break; + case SND_SOC_DAPM_PRE_PMD: + snd_soc_update_bits(codec, WCD937X_HPH_L_TEST, 0x01, 0x00); + if (wcd937x->update_wcd_event) + wcd937x->update_wcd_event(wcd937x->handle, + WCD_BOLERO_EVT_RX_MUTE, + (WCD_RX1 << 0x10 | 0x1)); + blocking_notifier_call_chain(&wcd937x->mbhc->notifier, + WCD_EVENT_PRE_HPHL_PA_OFF, + &wcd937x->mbhc->wcd_mbhc); + set_bit(HPH_PA_DELAY, &wcd937x->status_mask); + break; + case SND_SOC_DAPM_POST_PMD: + /* + * 7ms sleep is required after PA is disabled as per + * HW requirement. If compander is disabled, then + * 20ms delay is required. + */ + if (test_bit(HPH_PA_DELAY, &wcd937x->status_mask)) { + if (!wcd937x->comp1_enable) + usleep_range(20000, 20100); + else + usleep_range(7000, 7100); + clear_bit(HPH_PA_DELAY, &wcd937x->status_mask); + } + + snd_soc_update_bits(codec, WCD937X_DIGITAL_PDM_WD_CTL0, + 0x17, 0x00); + blocking_notifier_call_chain(&wcd937x->mbhc->notifier, + WCD_EVENT_POST_HPHL_PA_OFF, + &wcd937x->mbhc->wcd_mbhc); + snd_soc_update_bits(codec, WCD937X_ANA_HPH, 0x20, 0x00); + wcd_cls_h_fsm(codec, &wcd937x->clsh_info, + WCD_CLSH_EVENT_POST_PA, + WCD_CLSH_STATE_HPHL, + hph_mode); + break; + }; + return ret; +} + +static int wcd937x_codec_enable_aux_pa(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct wcd937x_priv *wcd937x = snd_soc_codec_get_drvdata(codec); + int hph_mode = wcd937x->hph_mode; + int ret = 0; + + dev_dbg(codec->dev, "%s wname: %s event: %d\n", __func__, + w->name, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + ret = swr_slvdev_datapath_control(wcd937x->rx_swr_dev, + wcd937x->rx_swr_dev->dev_num, + true); + break; + case SND_SOC_DAPM_POST_PMU: + usleep_range(1000, 1010); + if (hph_mode == CLS_AB || hph_mode == CLS_AB_HIFI) + snd_soc_update_bits(codec, WCD937X_ANA_RX_SUPPLIES, + 0x02, 0x02); + if (wcd937x->update_wcd_event) + wcd937x->update_wcd_event(wcd937x->handle, + WCD_BOLERO_EVT_RX_MUTE, + (WCD_RX3 << 0x10)); + break; + case SND_SOC_DAPM_PRE_PMD: + if (wcd937x->update_wcd_event) + wcd937x->update_wcd_event(wcd937x->handle, + WCD_BOLERO_EVT_RX_MUTE, + (WCD_RX3 << 0x10 | 0x1)); + break; + case SND_SOC_DAPM_POST_PMD: + usleep_range(1000, 1010); + usleep_range(1000, 1010); + wcd_cls_h_fsm(codec, &wcd937x->clsh_info, + WCD_CLSH_EVENT_POST_PA, + WCD_CLSH_STATE_AUX, + hph_mode); + break; + }; + return ret; +} + +static int wcd937x_codec_enable_ear_pa(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct wcd937x_priv *wcd937x = snd_soc_codec_get_drvdata(codec); + int hph_mode = wcd937x->hph_mode; + int ret = 0; + + dev_dbg(codec->dev, "%s wname: %s event: %d\n", __func__, + w->name, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + ret = swr_slvdev_datapath_control(wcd937x->rx_swr_dev, + wcd937x->rx_swr_dev->dev_num, + true); + if (!wcd937x->comp1_enable) + snd_soc_update_bits(codec, + WCD937X_ANA_EAR_COMPANDER_CTL, 0x80, 0x80); + break; + case SND_SOC_DAPM_POST_PMU: + usleep_range(6000, 6010); + if (hph_mode == CLS_AB || hph_mode == CLS_AB_HIFI) + snd_soc_update_bits(codec, WCD937X_ANA_RX_SUPPLIES, + 0x02, 0x02); + if (wcd937x->update_wcd_event) + wcd937x->update_wcd_event(wcd937x->handle, + WCD_BOLERO_EVT_RX_MUTE, + (WCD_RX1 << 0x10)); + break; + case SND_SOC_DAPM_PRE_PMD: + if (wcd937x->update_wcd_event) + wcd937x->update_wcd_event(wcd937x->handle, + WCD_BOLERO_EVT_RX_MUTE, + (WCD_RX1 << 0x10 | 0x1)); + break; + case SND_SOC_DAPM_POST_PMD: + if (!wcd937x->comp1_enable) + snd_soc_update_bits(codec, + WCD937X_ANA_EAR_COMPANDER_CTL, 0x80, 0x00); + usleep_range(7000, 7010); + wcd_cls_h_fsm(codec, &wcd937x->clsh_info, + WCD_CLSH_EVENT_POST_PA, + WCD_CLSH_STATE_EAR, + hph_mode); + snd_soc_update_bits(codec, WCD937X_FLYBACK_EN, + 0x04, 0x04); + break; + }; + return ret; +} + +static int wcd937x_enable_clsh(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct wcd937x_priv *wcd937x = snd_soc_codec_get_drvdata(codec); + int mode = wcd937x->hph_mode; + int ret = 0; + + dev_dbg(codec->dev, "%s wname: %s event: %d\n", __func__, + w->name, event); + + if (mode == CLS_H_LOHIFI || mode == CLS_H_ULP || + mode == CLS_H_HIFI || mode == CLS_H_LP) { + wcd937x_rx_connect_port(codec, CLSH, + SND_SOC_DAPM_EVENT_ON(event)); + } + if (SND_SOC_DAPM_EVENT_OFF(event)) + ret = swr_slvdev_datapath_control( + wcd937x->rx_swr_dev, + wcd937x->rx_swr_dev->dev_num, + false); + return ret; +} + +static int wcd937x_enable_rx1(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct wcd937x_priv *wcd937x = snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, "%s wname: %s event: %d\n", __func__, + w->name, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + wcd937x_rx_connect_port(codec, HPH_L, true); + if (wcd937x->comp1_enable) + wcd937x_rx_connect_port(codec, COMP_L, true); + break; + case SND_SOC_DAPM_POST_PMD: + wcd937x_rx_connect_port(codec, HPH_L, false); + if (wcd937x->comp1_enable) + wcd937x_rx_connect_port(codec, COMP_L, false); + wcd937x_rx_clk_disable(codec); + snd_soc_update_bits(codec, WCD937X_DIGITAL_CDC_DIG_CLK_CTL, + 0x01, 0x00); + break; + }; + return 0; +} + +static int wcd937x_enable_rx2(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct wcd937x_priv *wcd937x = snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, "%s wname: %s event: %d\n", __func__, + w->name, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + wcd937x_rx_connect_port(codec, HPH_R, true); + if (wcd937x->comp2_enable) + wcd937x_rx_connect_port(codec, COMP_R, true); + break; + case SND_SOC_DAPM_POST_PMD: + wcd937x_rx_connect_port(codec, HPH_R, false); + if (wcd937x->comp2_enable) + wcd937x_rx_connect_port(codec, COMP_R, false); + wcd937x_rx_clk_disable(codec); + snd_soc_update_bits(codec, WCD937X_DIGITAL_CDC_DIG_CLK_CTL, + 0x02, 0x00); + break; + }; + + return 0; +} + +static int wcd937x_enable_rx3(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + + dev_dbg(codec->dev, "%s wname: %s event: %d\n", __func__, + w->name, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + wcd937x_rx_connect_port(codec, LO, true); + break; + case SND_SOC_DAPM_POST_PMD: + wcd937x_rx_connect_port(codec, LO, false); + usleep_range(6000, 6010); + wcd937x_rx_clk_disable(codec); + snd_soc_update_bits(codec, WCD937X_DIGITAL_CDC_DIG_CLK_CTL, + 0x04, 0x00); + break; + } + return 0; + +} + +static int wcd937x_codec_enable_dmic(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct wcd937x_priv *wcd937x = snd_soc_codec_get_drvdata(codec); + u16 dmic_clk_reg; + s32 *dmic_clk_cnt; + unsigned int dmic; + char *wname; + int ret = 0; + + wname = strpbrk(w->name, "012345"); + + if (!wname) { + dev_err(codec->dev, "%s: widget not found\n", __func__); + return -EINVAL; + } + + ret = kstrtouint(wname, 10, &dmic); + if (ret < 0) { + dev_err(codec->dev, "%s: Invalid DMIC line on the codec\n", + __func__); + return -EINVAL; + } + + dev_dbg(codec->dev, "%s wname: %s event: %d\n", __func__, + w->name, event); + + switch (dmic) { + case 0: + case 1: + dmic_clk_cnt = &(wcd937x->dmic_0_1_clk_cnt); + dmic_clk_reg = WCD937X_DIGITAL_CDC_DMIC0_CTL; + break; + case 2: + case 3: + dmic_clk_cnt = &(wcd937x->dmic_2_3_clk_cnt); + dmic_clk_reg = WCD937X_DIGITAL_CDC_DMIC1_CTL; + break; + case 4: + case 5: + dmic_clk_cnt = &(wcd937x->dmic_4_5_clk_cnt); + dmic_clk_reg = WCD937X_DIGITAL_CDC_DMIC2_CTL; + break; + default: + dev_err(codec->dev, "%s: Invalid DMIC Selection\n", + __func__); + return -EINVAL; + }; + dev_dbg(codec->dev, "%s: event %d DMIC%d dmic_clk_cnt %d\n", + __func__, event, dmic, *dmic_clk_cnt); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_update_bits(codec, WCD937X_DIGITAL_CDC_DIG_CLK_CTL, + 0x80, 0x80); + snd_soc_update_bits(codec, dmic_clk_reg, 0x07, 0x02); + snd_soc_update_bits(codec, dmic_clk_reg, 0x08, 0x08); + snd_soc_update_bits(codec, dmic_clk_reg, 0x70, 0x20); + wcd937x_tx_connect_port(codec, DMIC0 + (w->shift), true); + break; + case SND_SOC_DAPM_POST_PMD: + wcd937x_tx_connect_port(codec, DMIC0 + (w->shift), false); + break; + + }; + return 0; +} + +/* + * wcd937x_get_micb_vout_ctl_val: converts micbias from volts to register value + * @micb_mv: micbias in mv + * + * return register value converted + */ +int wcd937x_get_micb_vout_ctl_val(u32 micb_mv) +{ + /* min micbias voltage is 1V and maximum is 2.85V */ + if (micb_mv < 1000 || micb_mv > 2850) { + pr_err("%s: unsupported micbias voltage\n", __func__); + return -EINVAL; + } + + return (micb_mv - 1000) / 50; +} +EXPORT_SYMBOL(wcd937x_get_micb_vout_ctl_val); + +/* + * wcd937x_mbhc_micb_adjust_voltage: adjust specific micbias voltage + * @codec: handle to snd_soc_codec * + * @req_volt: micbias voltage to be set + * @micb_num: micbias to be set, e.g. micbias1 or micbias2 + * + * return 0 if adjustment is success or error code in case of failure + */ +int wcd937x_mbhc_micb_adjust_voltage(struct snd_soc_codec *codec, + int req_volt, int micb_num) +{ + struct wcd937x_priv *wcd937x = snd_soc_codec_get_drvdata(codec); + int cur_vout_ctl, req_vout_ctl; + int micb_reg, micb_val, micb_en; + int ret = 0; + + switch (micb_num) { + case MIC_BIAS_1: + micb_reg = WCD937X_ANA_MICB1; + break; + case MIC_BIAS_2: + micb_reg = WCD937X_ANA_MICB2; + break; + case MIC_BIAS_3: + micb_reg = WCD937X_ANA_MICB3; + break; + default: + return -EINVAL; + } + mutex_lock(&wcd937x->micb_lock); + + /* + * If requested micbias voltage is same as current micbias + * voltage, then just return. Otherwise, adjust voltage as + * per requested value. If micbias is already enabled, then + * to avoid slow micbias ramp-up or down enable pull-up + * momentarily, change the micbias value and then re-enable + * micbias. + */ + micb_val = snd_soc_read(codec, micb_reg); + micb_en = (micb_val & 0xC0) >> 6; + cur_vout_ctl = micb_val & 0x3F; + + req_vout_ctl = wcd937x_get_micb_vout_ctl_val(req_volt); + if (req_vout_ctl < 0) { + ret = -EINVAL; + goto exit; + } + if (cur_vout_ctl == req_vout_ctl) { + ret = 0; + goto exit; + } + + dev_dbg(codec->dev, "%s: micb_num: %d, cur_mv: %d, req_mv: %d, micb_en: %d\n", + __func__, micb_num, WCD_VOUT_CTL_TO_MICB(cur_vout_ctl), + req_volt, micb_en); + + if (micb_en == 0x1) + snd_soc_update_bits(codec, micb_reg, 0xC0, 0x80); + + snd_soc_update_bits(codec, micb_reg, 0x3F, req_vout_ctl); + + if (micb_en == 0x1) { + snd_soc_update_bits(codec, micb_reg, 0xC0, 0x40); + /* + * Add 2ms delay as per HW requirement after enabling + * micbias + */ + usleep_range(2000, 2100); + } +exit: + mutex_unlock(&wcd937x->micb_lock); + return ret; +} +EXPORT_SYMBOL(wcd937x_mbhc_micb_adjust_voltage); + +static int wcd937x_tx_swr_ctrl(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct wcd937x_priv *wcd937x = snd_soc_codec_get_drvdata(codec); + int ret = 0; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + ret = swr_slvdev_datapath_control(wcd937x->tx_swr_dev, + wcd937x->tx_swr_dev->dev_num, + true); + break; + case SND_SOC_DAPM_POST_PMD: + ret = swr_slvdev_datapath_control(wcd937x->tx_swr_dev, + wcd937x->tx_swr_dev->dev_num, + false); + break; + }; + + return ret; +} + +static int wcd937x_codec_enable_adc(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event){ + + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct wcd937x_priv *wcd937x = snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, "%s wname: %s event: %d\n", __func__, + w->name, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + mutex_lock(&wcd937x->ana_tx_clk_lock); + wcd937x->ana_clk_count++; + mutex_unlock(&wcd937x->ana_tx_clk_lock); + snd_soc_update_bits(codec, WCD937X_DIGITAL_CDC_DIG_CLK_CTL, + 0x80, 0x80); + snd_soc_update_bits(codec, WCD937X_DIGITAL_CDC_ANA_CLK_CTL, + 0x08, 0x08); + snd_soc_update_bits(codec, WCD937X_DIGITAL_CDC_ANA_CLK_CTL, + 0x10, 0x10); + wcd937x_tx_connect_port(codec, ADC1 + (w->shift), true); + break; + case SND_SOC_DAPM_POST_PMD: + wcd937x_tx_connect_port(codec, ADC1 + (w->shift), false); + snd_soc_update_bits(codec, WCD937X_DIGITAL_CDC_ANA_CLK_CTL, + 0x08, 0x00); + break; + }; + + return 0; +} + +static int wcd937x_enable_req(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct wcd937x_priv *wcd937x = snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, "%s wname: %s event: %d\n", __func__, + w->name, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_update_bits(codec, WCD937X_DIGITAL_CDC_REQ_CTL, + 0x02, 0x02); + snd_soc_update_bits(codec, WCD937X_DIGITAL_CDC_REQ_CTL, 0x01, + 0x00); + snd_soc_update_bits(codec, WCD937X_ANA_TX_CH2, 0x40, 0x40); + snd_soc_update_bits(codec, WCD937X_DIGITAL_CDC_DIG_CLK_CTL, + 0x30, 0x30); + snd_soc_update_bits(codec, WCD937X_ANA_TX_CH1, 0x80, 0x80); + snd_soc_update_bits(codec, WCD937X_ANA_TX_CH2, 0x40, 0x00); + snd_soc_update_bits(codec, WCD937X_ANA_TX_CH2, 0x80, 0x80); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_update_bits(codec, WCD937X_ANA_TX_CH1, 0x80, 0x00); + snd_soc_update_bits(codec, WCD937X_ANA_TX_CH2, 0x80, 0x00); + snd_soc_update_bits(codec, WCD937X_DIGITAL_CDC_DIG_CLK_CTL, + 0x10, 0x00); + mutex_lock(&wcd937x->ana_tx_clk_lock); + wcd937x->ana_clk_count--; + if (wcd937x->ana_clk_count <= 0) { + snd_soc_update_bits(codec, + WCD937X_DIGITAL_CDC_ANA_CLK_CTL, + 0x10, 0x00); + wcd937x->ana_clk_count = 0; + } + mutex_unlock(&wcd937x->ana_tx_clk_lock); + snd_soc_update_bits(codec, WCD937X_DIGITAL_CDC_DIG_CLK_CTL, + 0x80, 0x00); + break; + }; + return 0; +} + +int wcd937x_micbias_control(struct snd_soc_codec *codec, + int micb_num, int req, bool is_dapm) +{ + + struct wcd937x_priv *wcd937x = snd_soc_codec_get_drvdata(codec); + int micb_index = micb_num - 1; + u16 micb_reg; + int pre_off_event = 0, post_off_event = 0; + int post_on_event = 0, post_dapm_off = 0; + int post_dapm_on = 0; + + if ((micb_index < 0) || (micb_index > WCD937X_MAX_MICBIAS - 1)) { + dev_err(codec->dev, "%s: Invalid micbias index, micb_ind:%d\n", + __func__, micb_index); + return -EINVAL; + } + switch (micb_num) { + case MIC_BIAS_1: + micb_reg = WCD937X_ANA_MICB1; + break; + case MIC_BIAS_2: + micb_reg = WCD937X_ANA_MICB2; + pre_off_event = WCD_EVENT_PRE_MICBIAS_2_OFF; + post_off_event = WCD_EVENT_POST_MICBIAS_2_OFF; + post_on_event = WCD_EVENT_POST_MICBIAS_2_ON; + post_dapm_on = WCD_EVENT_POST_DAPM_MICBIAS_2_ON; + post_dapm_off = WCD_EVENT_POST_DAPM_MICBIAS_2_OFF; + break; + case MIC_BIAS_3: + micb_reg = WCD937X_ANA_MICB3; + break; + default: + dev_err(codec->dev, "%s: Invalid micbias number: %d\n", + __func__, micb_num); + return -EINVAL; + }; + mutex_lock(&wcd937x->micb_lock); + + switch (req) { + case MICB_PULLUP_ENABLE: + wcd937x->pullup_ref[micb_index]++; + if ((wcd937x->pullup_ref[micb_index] == 1) && + (wcd937x->micb_ref[micb_index] == 0)) + snd_soc_update_bits(codec, micb_reg, 0xC0, 0x80); + break; + case MICB_PULLUP_DISABLE: + if (wcd937x->pullup_ref[micb_index] > 0) + wcd937x->pullup_ref[micb_index]--; + if ((wcd937x->pullup_ref[micb_index] == 0) && + (wcd937x->micb_ref[micb_index] == 0)) + snd_soc_update_bits(codec, micb_reg, 0xC0, 0x00); + break; + case MICB_ENABLE: + wcd937x->micb_ref[micb_index]++; + mutex_lock(&wcd937x->ana_tx_clk_lock); + wcd937x->ana_clk_count++; + mutex_unlock(&wcd937x->ana_tx_clk_lock); + if (wcd937x->micb_ref[micb_index] == 1) { + snd_soc_update_bits(codec, WCD937X_DIGITAL_CDC_DIG_CLK_CTL, 0xE0, 0xE0); + snd_soc_update_bits(codec, WCD937X_DIGITAL_CDC_ANA_CLK_CTL, 0x10, 0x10); + snd_soc_update_bits(codec, WCD937X_MICB1_TEST_CTL_2, 0x01, 0x01); + snd_soc_update_bits(codec, WCD937X_MICB2_TEST_CTL_2, 0x01, 0x01); + snd_soc_update_bits(codec, WCD937X_MICB3_TEST_CTL_2, 0x01, 0x01); + snd_soc_update_bits(codec, micb_reg, 0xC0, 0x40); + if (post_on_event && wcd937x->mbhc) + blocking_notifier_call_chain( + &wcd937x->mbhc->notifier, post_on_event, + &wcd937x->mbhc->wcd_mbhc); + } + if (is_dapm && post_dapm_on && wcd937x->mbhc) + blocking_notifier_call_chain( + &wcd937x->mbhc->notifier, post_dapm_on, + &wcd937x->mbhc->wcd_mbhc); + break; + case MICB_DISABLE: + mutex_lock(&wcd937x->ana_tx_clk_lock); + wcd937x->ana_clk_count--; + mutex_unlock(&wcd937x->ana_tx_clk_lock); + if (wcd937x->micb_ref[micb_index] > 0) + wcd937x->micb_ref[micb_index]--; + if ((wcd937x->micb_ref[micb_index] == 0) && + (wcd937x->pullup_ref[micb_index] > 0)) + snd_soc_update_bits(codec, micb_reg, 0xC0, 0x80); + else if ((wcd937x->micb_ref[micb_index] == 0) && + (wcd937x->pullup_ref[micb_index] == 0)) { + if (pre_off_event && wcd937x->mbhc) + blocking_notifier_call_chain( + &wcd937x->mbhc->notifier, pre_off_event, + &wcd937x->mbhc->wcd_mbhc); + snd_soc_update_bits(codec, micb_reg, 0xC0, 0x00); + if (post_off_event && wcd937x->mbhc) + blocking_notifier_call_chain( + &wcd937x->mbhc->notifier, + post_off_event, + &wcd937x->mbhc->wcd_mbhc); + } + mutex_lock(&wcd937x->ana_tx_clk_lock); + if (wcd937x->ana_clk_count <= 0) { + snd_soc_update_bits(codec, + WCD937X_DIGITAL_CDC_ANA_CLK_CTL, + 0x10, 0x00); + wcd937x->ana_clk_count = 0; + } + mutex_unlock(&wcd937x->ana_tx_clk_lock); + if (is_dapm && post_dapm_off && wcd937x->mbhc) + blocking_notifier_call_chain( + &wcd937x->mbhc->notifier, post_dapm_off, + &wcd937x->mbhc->wcd_mbhc); + break; + }; + + dev_dbg(codec->dev, "%s: micb_num:%d, micb_ref: %d, pullup_ref: %d\n", + __func__, micb_num, wcd937x->micb_ref[micb_index], + wcd937x->pullup_ref[micb_index]); + mutex_unlock(&wcd937x->micb_lock); + + return 0; +} +EXPORT_SYMBOL(wcd937x_micbias_control); + +static int wcd937x_get_logical_addr(struct swr_device *swr_dev) +{ + int ret = 0; + uint8_t devnum = 0; + + ret = swr_get_logical_dev_num(swr_dev, swr_dev->addr, &devnum); + if (ret) { + dev_err(&swr_dev->dev, + "%s get devnum %d for dev addr %lx failed\n", + __func__, devnum, swr_dev->addr); + swr_remove_device(swr_dev); + return ret; + } + swr_dev->dev_num = devnum; + return 0; +} + +static int wcd937x_event_notify(struct notifier_block *block, + unsigned long val, + void *data) +{ + u16 event = (val & 0xffff); + u16 amic = (val >> 0x10); + u16 mask = 0x40, reg = 0x0; + int ret = 0; + struct wcd937x_priv *wcd937x = dev_get_drvdata((struct device *)data); + struct snd_soc_codec *codec = wcd937x->codec; + struct wcd_mbhc *mbhc; + + switch (event) { + case BOLERO_WCD_EVT_TX_CH_HOLD_CLEAR: + if (amic == 0x1 || amic == 0x2) + reg = WCD937X_ANA_TX_CH2; + else if (amic == 0x3) + reg = WCD937X_ANA_TX_CH3_HPF; + else + return 0; + if (amic == 0x2) + mask = 0x20; + snd_soc_update_bits(codec, reg, mask, 0x00); + break; + case BOLERO_WCD_EVT_PA_OFF_PRE_SSR: + snd_soc_update_bits(codec, WCD937X_ANA_HPH, 0xC0, 0x00); + snd_soc_update_bits(codec, WCD937X_ANA_EAR, 0x80, 0x00); + snd_soc_update_bits(codec, WCD937X_AUX_AUXPA, 0x80, 0x00); + break; + case BOLERO_WCD_EVT_SSR_DOWN: + mbhc = &wcd937x->mbhc->wcd_mbhc; + wcd937x_mbhc_ssr_down(wcd937x->mbhc, codec); + wcd937x_reset_low(wcd937x->dev); + break; + case BOLERO_WCD_EVT_SSR_UP: + wcd937x_reset(wcd937x->dev); + wcd937x_get_logical_addr(wcd937x->tx_swr_dev); + wcd937x_get_logical_addr(wcd937x->rx_swr_dev); + regcache_mark_dirty(wcd937x->regmap); + regcache_sync(wcd937x->regmap); + /* Enable surge protection */ + snd_soc_update_bits(codec, WCD937X_HPH_SURGE_HPHLR_SURGE_EN, + 0xFF, 0xD9); + /* Initialize MBHC module */ + mbhc = &wcd937x->mbhc->wcd_mbhc; + ret = wcd937x_mbhc_post_ssr_init(wcd937x->mbhc, codec); + if (ret) { + dev_err(codec->dev, "%s: mbhc initialization failed\n", + __func__); + } else { + wcd937x_mbhc_hs_detect(codec, mbhc->mbhc_cfg); + } + break; + default: + dev_err(codec->dev, "%s: invalid event %d\n", __func__, event); + break; + } + return 0; +} + +static int __wcd937x_codec_enable_micbias(struct snd_soc_dapm_widget *w, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + int micb_num; + + dev_dbg(codec->dev, "%s: wname: %s, event: %d\n", + __func__, w->name, event); + + if (strnstr(w->name, "MIC BIAS1", sizeof("MIC BIAS1"))) + micb_num = MIC_BIAS_1; + else if (strnstr(w->name, "MIC BIAS2", sizeof("MIC BIAS2"))) + micb_num = MIC_BIAS_2; + else if (strnstr(w->name, "MIC BIAS3", sizeof("MIC BIAS3"))) + micb_num = MIC_BIAS_3; + else + return -EINVAL; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + wcd937x_micbias_control(codec, micb_num, MICB_ENABLE, true); + break; + case SND_SOC_DAPM_POST_PMU: + usleep_range(1000, 1100); + break; + case SND_SOC_DAPM_POST_PMD: + wcd937x_micbias_control(codec, micb_num, MICB_DISABLE, true); + break; + }; + + return 0; + +} + +static int wcd937x_codec_enable_micbias(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + return __wcd937x_codec_enable_micbias(w, event); +} + +static int wcd937x_rx_hph_mode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct wcd937x_priv *wcd937x = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = wcd937x->hph_mode; + return 0; +} + +static int wcd937x_rx_hph_mode_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct wcd937x_priv *wcd937x = snd_soc_codec_get_drvdata(codec); + u32 mode_val; + + mode_val = ucontrol->value.enumerated.item[0]; + + dev_dbg(codec->dev, "%s: mode: %d\n", __func__, mode_val); + + if (mode_val == 0) { + dev_warn(codec->dev, "%s:Invalid HPH Mode, default to class_AB\n", + __func__); + mode_val = 3; /* enum will be updated later */ + } + wcd937x->hph_mode = mode_val; + return 0; +} + +static int wcd937x_ear_pa_gain_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u8 ear_pa_gain = 0; + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + + ear_pa_gain = snd_soc_read(codec, WCD937X_ANA_EAR_COMPANDER_CTL); + + ear_pa_gain = (ear_pa_gain & 0x7C) >> 2; + + ucontrol->value.integer.value[0] = ear_pa_gain; + + dev_dbg(codec->dev, "%s: ear_pa_gain = 0x%x\n", __func__, + ear_pa_gain); + + return 0; +} + +static int wcd937x_ear_pa_gain_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u8 ear_pa_gain = 0; + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct wcd937x_priv *wcd937x = snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n", + __func__, ucontrol->value.integer.value[0]); + + ear_pa_gain = ucontrol->value.integer.value[0] << 2; + + if (!wcd937x->comp1_enable) { + snd_soc_update_bits(codec, WCD937X_ANA_EAR_COMPANDER_CTL, + 0x7C, ear_pa_gain); + } + + return 0; +} + +static int wcd937x_get_compander(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct wcd937x_priv *wcd937x = snd_soc_codec_get_drvdata(codec); + bool hphr; + struct soc_multi_mixer_control *mc; + + mc = (struct soc_multi_mixer_control *)(kcontrol->private_value); + hphr = mc->shift; + + ucontrol->value.integer.value[0] = hphr ? wcd937x->comp2_enable : + wcd937x->comp1_enable; + return 0; +} + +static int wcd937x_set_compander(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct wcd937x_priv *wcd937x = snd_soc_codec_get_drvdata(codec); + int value = ucontrol->value.integer.value[0]; + bool hphr; + struct soc_multi_mixer_control *mc; + + mc = (struct soc_multi_mixer_control *)(kcontrol->private_value); + hphr = mc->shift; + if (hphr) + wcd937x->comp2_enable = value; + else + wcd937x->comp1_enable = value; + + return 0; +} + +static int wcd937x_codec_enable_vdd_buck(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct wcd937x_priv *wcd937x = snd_soc_codec_get_drvdata(codec); + struct wcd937x_pdata *pdata = NULL; + int ret = 0; + + pdata = dev_get_platdata(wcd937x->dev); + + if (!pdata) { + dev_err(codec->dev, "%s: pdata is NULL\n", __func__); + return -EINVAL; + } + + dev_dbg(codec->dev, "%s wname: %s event: %d\n", __func__, + w->name, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (test_bit(ALLOW_BUCK_DISABLE, &wcd937x->status_mask)) { + dev_dbg(codec->dev, + "%s: buck already in enabled state\n", + __func__); + clear_bit(ALLOW_BUCK_DISABLE, &wcd937x->status_mask); + return 0; + } + ret = msm_cdc_enable_ondemand_supply(wcd937x->dev, + wcd937x->supplies, + pdata->regulator, + pdata->num_supplies, + "cdc-vdd-buck"); + if (ret == -EINVAL) { + dev_err(codec->dev, "%s: vdd buck is not enabled\n", + __func__); + return ret; + } + clear_bit(ALLOW_BUCK_DISABLE, &wcd937x->status_mask); + /* + * 200us sleep is required after LDO15 is enabled as per + * HW requirement + */ + usleep_range(200, 250); + break; + case SND_SOC_DAPM_POST_PMD: + set_bit(ALLOW_BUCK_DISABLE, &wcd937x->status_mask); + break; + } + return 0; +} + +static const char * const rx_hph_mode_mux_text[] = { + "CLS_H_INVALID", "CLS_H_HIFI", "CLS_H_LP", "CLS_AB", "CLS_H_LOHIFI", + "CLS_H_ULP", "CLS_AB_HIFI", +}; + +static const char * const wcd937x_ear_pa_gain_text[] = { + "G_6_DB", "G_4P5_DB", "G_3_DB", "G_1P5_DB", "G_0_DB", + "G_M1P5_DB", "G_M3_DB", "G_M4P5_DB", + "G_M6_DB", "G_7P5_DB", "G_M9_DB", + "G_M10P5_DB", "G_M12_DB", "G_M13P5_DB", + "G_M15_DB", "G_M16P5_DB", "G_M18_DB", +}; + +static const struct soc_enum rx_hph_mode_mux_enum = + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(rx_hph_mode_mux_text), + rx_hph_mode_mux_text); + +static SOC_ENUM_SINGLE_EXT_DECL(wcd937x_ear_pa_gain_enum, + wcd937x_ear_pa_gain_text); + +static const struct snd_kcontrol_new wcd937x_snd_controls[] = { + SOC_ENUM_EXT("EAR PA GAIN", wcd937x_ear_pa_gain_enum, + wcd937x_ear_pa_gain_get, wcd937x_ear_pa_gain_put), + SOC_ENUM_EXT("RX HPH Mode", rx_hph_mode_mux_enum, + wcd937x_rx_hph_mode_get, wcd937x_rx_hph_mode_put), + SOC_SINGLE_EXT("HPHL_COMP Switch", SND_SOC_NOPM, 0, 1, 0, + wcd937x_get_compander, wcd937x_set_compander), + SOC_SINGLE_EXT("HPHR_COMP Switch", SND_SOC_NOPM, 1, 1, 0, + wcd937x_get_compander, wcd937x_set_compander), + + SOC_SINGLE_TLV("HPHL Volume", WCD937X_HPH_L_EN, 0, 20, 1, line_gain), + SOC_SINGLE_TLV("HPHR Volume", WCD937X_HPH_R_EN, 0, 20, 1, line_gain), + SOC_SINGLE_TLV("ADC1 Volume", WCD937X_ANA_TX_CH1, 0, 20, 0, analog_gain), + SOC_SINGLE_TLV("ADC2 Volume", WCD937X_ANA_TX_CH2, 0, 20, 0, analog_gain), + SOC_SINGLE_TLV("ADC3 Volume", WCD937X_ANA_TX_CH3, 0, 20, 0, analog_gain), +}; + +static const struct snd_kcontrol_new adc1_switch[] = { + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new adc2_switch[] = { + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new adc3_switch[] = { + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new dmic1_switch[] = { + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new dmic2_switch[] = { + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new dmic3_switch[] = { + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new dmic4_switch[] = { + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new dmic5_switch[] = { + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new dmic6_switch[] = { + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new ear_rdac_switch[] = { + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new aux_rdac_switch[] = { + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new hphl_rdac_switch[] = { + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new hphr_rdac_switch[] = { + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0) +}; + +static const char * const adc2_mux_text[] = { + "INP2", "INP3" +}; + +static const char * const rdac3_mux_text[] = { + "RX1", "RX3" +}; + +static const struct soc_enum adc2_enum = + SOC_ENUM_SINGLE(WCD937X_TX_NEW_TX_CH2_SEL, 7, + ARRAY_SIZE(adc2_mux_text), adc2_mux_text); + + +static const struct soc_enum rdac3_enum = + SOC_ENUM_SINGLE(WCD937X_DIGITAL_CDC_EAR_PATH_CTL, 0, + ARRAY_SIZE(rdac3_mux_text), rdac3_mux_text); + +static const struct snd_kcontrol_new tx_adc2_mux = + SOC_DAPM_ENUM("ADC2 MUX Mux", adc2_enum); + +static const struct snd_kcontrol_new rx_rdac3_mux = + SOC_DAPM_ENUM("RDAC3_MUX Mux", rdac3_enum); + +static const struct snd_soc_dapm_widget wcd937x_dapm_widgets[] = { + + /*input widgets*/ + + SND_SOC_DAPM_INPUT("AMIC1"), + SND_SOC_DAPM_INPUT("AMIC2"), + SND_SOC_DAPM_INPUT("AMIC3"), + SND_SOC_DAPM_INPUT("IN1_HPHL"), + SND_SOC_DAPM_INPUT("IN2_HPHR"), + SND_SOC_DAPM_INPUT("IN3_AUX"), + + /*tx widgets*/ + SND_SOC_DAPM_ADC_E("ADC1", NULL, SND_SOC_NOPM, 0, 0, + wcd937x_codec_enable_adc, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("ADC2", NULL, SND_SOC_NOPM, 1, 0, + wcd937x_codec_enable_adc, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MIXER_E("ADC1 REQ", SND_SOC_NOPM, 0, 0, + NULL, 0, wcd937x_enable_req, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("ADC2 REQ", SND_SOC_NOPM, 0, 0, + NULL, 0, wcd937x_enable_req, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX("ADC2 MUX", SND_SOC_NOPM, 0, 0, + &tx_adc2_mux), + + /*tx mixers*/ + SND_SOC_DAPM_MIXER_E("ADC1_MIXER", SND_SOC_NOPM, 0, 0, + adc1_switch, ARRAY_SIZE(adc1_switch), + wcd937x_tx_swr_ctrl, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("ADC2_MIXER", SND_SOC_NOPM, 0, 0, + adc2_switch, ARRAY_SIZE(adc2_switch), + wcd937x_tx_swr_ctrl, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + + /* micbias widgets*/ + SND_SOC_DAPM_MICBIAS_E("MIC BIAS1", SND_SOC_NOPM, 0, 0, + wcd937x_codec_enable_micbias, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MICBIAS_E("MIC BIAS2", SND_SOC_NOPM, 0, 0, + wcd937x_codec_enable_micbias, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MICBIAS_E("MIC BIAS3", SND_SOC_NOPM, 0, 0, + wcd937x_codec_enable_micbias, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_SUPPLY("VDD_BUCK", SND_SOC_NOPM, 0, 0, + wcd937x_codec_enable_vdd_buck, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_SUPPLY_S("CLS_H_PORT", 1, SND_SOC_NOPM, 0, 0, + wcd937x_enable_clsh, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + /*rx widgets*/ + SND_SOC_DAPM_PGA_E("EAR PGA", WCD937X_ANA_EAR, 7, 0, NULL, 0, + wcd937x_codec_enable_ear_pa, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("AUX PGA", WCD937X_AUX_AUXPA, 7, 0, NULL, 0, + wcd937x_codec_enable_aux_pa, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("HPHL PGA", WCD937X_ANA_HPH, 7, 0, NULL, 0, + wcd937x_codec_enable_hphl_pa, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("HPHR PGA", WCD937X_ANA_HPH, 6, 0, NULL, 0, + wcd937x_codec_enable_hphr_pa, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_DAC_E("RDAC1", NULL, SND_SOC_NOPM, 0, 0, + wcd937x_codec_hphl_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_DAC_E("RDAC2", NULL, SND_SOC_NOPM, 0, 0, + wcd937x_codec_hphr_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_DAC_E("RDAC3", NULL, SND_SOC_NOPM, 0, 0, + wcd937x_codec_ear_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_DAC_E("RDAC4", NULL, SND_SOC_NOPM, 0, 0, + wcd937x_codec_aux_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX("RDAC3_MUX", SND_SOC_NOPM, 0, 0, &rx_rdac3_mux), + + SND_SOC_DAPM_MIXER_E("RX1", SND_SOC_NOPM, 0, 0, NULL, 0, + wcd937x_enable_rx1, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("RX2", SND_SOC_NOPM, 0, 0, NULL, 0, + wcd937x_enable_rx2, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("RX3", SND_SOC_NOPM, 0, 0, NULL, 0, + wcd937x_enable_rx3, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + + /* rx mixer widgets*/ + + SND_SOC_DAPM_MIXER("EAR_RDAC", SND_SOC_NOPM, 0, 0, + ear_rdac_switch, ARRAY_SIZE(ear_rdac_switch)), + SND_SOC_DAPM_MIXER("AUX_RDAC", SND_SOC_NOPM, 0, 0, + aux_rdac_switch, ARRAY_SIZE(aux_rdac_switch)), + SND_SOC_DAPM_MIXER("HPHL_RDAC", SND_SOC_NOPM, 0, 0, + hphl_rdac_switch, ARRAY_SIZE(hphl_rdac_switch)), + SND_SOC_DAPM_MIXER("HPHR_RDAC", SND_SOC_NOPM, 0, 0, + hphr_rdac_switch, ARRAY_SIZE(hphr_rdac_switch)), + + /*output widgets tx*/ + + SND_SOC_DAPM_OUTPUT("ADC1_OUTPUT"), + SND_SOC_DAPM_OUTPUT("ADC2_OUTPUT"), + + /*output widgets rx*/ + SND_SOC_DAPM_OUTPUT("EAR"), + SND_SOC_DAPM_OUTPUT("AUX"), + SND_SOC_DAPM_OUTPUT("HPHL"), + SND_SOC_DAPM_OUTPUT("HPHR"), + +}; + +static const struct snd_soc_dapm_widget wcd9375_dapm_widgets[] = { + + /*input widgets*/ + SND_SOC_DAPM_INPUT("AMIC4"), + + /*tx widgets*/ + SND_SOC_DAPM_ADC_E("ADC3", NULL, SND_SOC_NOPM, 2, 0, + wcd937x_codec_enable_adc, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MIXER_E("ADC3 REQ", SND_SOC_NOPM, 0, 0, + NULL, 0, wcd937x_enable_req, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_ADC_E("DMIC1", NULL, SND_SOC_NOPM, 0, 0, + wcd937x_codec_enable_dmic, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("DMIC2", NULL, SND_SOC_NOPM, 1, 0, + wcd937x_codec_enable_dmic, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("DMIC3", NULL, SND_SOC_NOPM, 2, 0, + wcd937x_codec_enable_dmic, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("DMIC4", NULL, SND_SOC_NOPM, 3, 0, + wcd937x_codec_enable_dmic, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("DMIC5", NULL, SND_SOC_NOPM, 4, 0, + wcd937x_codec_enable_dmic, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("DMIC6", NULL, SND_SOC_NOPM, 5, 0, + wcd937x_codec_enable_dmic, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + /*tx mixer widgets*/ + SND_SOC_DAPM_MIXER_E("DMIC1_MIXER", SND_SOC_NOPM, 0, + 0, dmic1_switch, ARRAY_SIZE(dmic1_switch), + wcd937x_tx_swr_ctrl, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("DMIC2_MIXER", SND_SOC_NOPM, 0, + 0, dmic2_switch, ARRAY_SIZE(dmic2_switch), + wcd937x_tx_swr_ctrl, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("DMIC3_MIXER", SND_SOC_NOPM, 0, + 0, dmic3_switch, ARRAY_SIZE(dmic3_switch), + wcd937x_tx_swr_ctrl, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("DMIC4_MIXER", SND_SOC_NOPM, 0, + 0, dmic4_switch, ARRAY_SIZE(dmic4_switch), + wcd937x_tx_swr_ctrl, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("DMIC5_MIXER", SND_SOC_NOPM, 0, + 0, dmic5_switch, ARRAY_SIZE(dmic5_switch), + wcd937x_tx_swr_ctrl, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("DMIC6_MIXER", SND_SOC_NOPM, 0, + 0, dmic6_switch, ARRAY_SIZE(dmic6_switch), + wcd937x_tx_swr_ctrl, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("ADC3_MIXER", SND_SOC_NOPM, 0, 0, adc3_switch, + ARRAY_SIZE(adc3_switch), wcd937x_tx_swr_ctrl, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + /*output widgets*/ + SND_SOC_DAPM_OUTPUT("DMIC1_OUTPUT"), + SND_SOC_DAPM_OUTPUT("DMIC2_OUTPUT"), + SND_SOC_DAPM_OUTPUT("DMIC3_OUTPUT"), + SND_SOC_DAPM_OUTPUT("DMIC4_OUTPUT"), + SND_SOC_DAPM_OUTPUT("DMIC5_OUTPUT"), + SND_SOC_DAPM_OUTPUT("DMIC6_OUTPUT"), + SND_SOC_DAPM_OUTPUT("ADC3_OUTPUT"), + +}; + +static const struct snd_soc_dapm_route wcd937x_audio_map[] = { + {"ADC1_OUTPUT", NULL, "ADC1_MIXER"}, + {"ADC1_MIXER", "Switch", "ADC1 REQ"}, + {"ADC1 REQ", NULL, "ADC1"}, + {"ADC1", NULL, "AMIC1"}, + + {"ADC2_OUTPUT", NULL, "ADC2_MIXER"}, + {"ADC2_MIXER", "Switch", "ADC2 REQ"}, + {"ADC2 REQ", NULL, "ADC2"}, + {"ADC2", NULL, "ADC2 MUX"}, + {"ADC2 MUX", "INP3", "AMIC3"}, + {"ADC2 MUX", "INP2", "AMIC2"}, + + {"IN1_HPHL", NULL, "VDD_BUCK"}, + {"IN1_HPHL", NULL, "CLS_H_PORT"}, + {"RX1", NULL, "IN1_HPHL"}, + {"RDAC1", NULL, "RX1"}, + {"HPHL_RDAC", "Switch", "RDAC1"}, + {"HPHL PGA", NULL, "HPHL_RDAC"}, + {"HPHL", NULL, "HPHL PGA"}, + + {"IN2_HPHR", NULL, "VDD_BUCK"}, + {"IN2_HPHR", NULL, "CLS_H_PORT"}, + {"RX2", NULL, "IN2_HPHR"}, + {"RDAC2", NULL, "RX2"}, + {"HPHR_RDAC", "Switch", "RDAC2"}, + {"HPHR PGA", NULL, "HPHR_RDAC"}, + {"HPHR", NULL, "HPHR PGA"}, + + {"IN3_AUX", NULL, "VDD_BUCK"}, + {"IN3_AUX", NULL, "CLS_H_PORT"}, + {"RX3", NULL, "IN3_AUX"}, + {"RDAC4", NULL, "RX3"}, + {"AUX_RDAC", "Switch", "RDAC4"}, + {"AUX PGA", NULL, "AUX_RDAC"}, + {"AUX", NULL, "AUX PGA"}, + + {"RDAC3_MUX", "RX3", "RX3"}, + {"RDAC3_MUX", "RX1", "RX1"}, + {"RDAC3", NULL, "RDAC3_MUX"}, + {"EAR_RDAC", "Switch", "RDAC3"}, + {"EAR PGA", NULL, "EAR_RDAC"}, + {"EAR", NULL, "EAR PGA"}, +}; + +static const struct snd_soc_dapm_route wcd9375_audio_map[] = { + + {"ADC3_OUTPUT", NULL, "ADC3_MIXER"}, + {"ADC3_MIXER", "Switch", "ADC3 REQ"}, + {"ADC3 REQ", NULL, "ADC3"}, + {"ADC3", NULL, "AMIC4"}, + + {"DMIC1_OUTPUT", NULL, "DMIC1_MIXER"}, + {"DMIC1_MIXER", "Switch", "DMIC1"}, + + {"DMIC2_OUTPUT", NULL, "DMIC2_MIXER"}, + {"DMIC2_MIXER", "Switch", "DMIC2"}, + + {"DMIC3_OUTPUT", NULL, "DMIC3_MIXER"}, + {"DMIC3_MIXER", "Switch", "DMIC3"}, + + {"DMIC4_OUTPUT", NULL, "DMIC4_MIXER"}, + {"DMIC4_MIXER", "Switch", "DMIC4"}, + + {"DMIC5_OUTPUT", NULL, "DMIC5_MIXER"}, + {"DMIC5_MIXER", "Switch", "DMIC5"}, + + {"DMIC6_OUTPUT", NULL, "DMIC6_MIXER"}, + {"DMIC6_MIXER", "Switch", "DMIC6"}, + +}; + +static ssize_t wcd937x_version_read(struct snd_info_entry *entry, + void *file_private_data, + struct file *file, + char __user *buf, size_t count, + loff_t pos) +{ + struct wcd937x_priv *priv; + char buffer[WCD937X_VERSION_ENTRY_SIZE]; + int len = 0; + + priv = (struct wcd937x_priv *) entry->private_data; + if (!priv) { + pr_err("%s: wcd937x priv is null\n", __func__); + return -EINVAL; + } + + switch (priv->version) { + case WCD937X_VERSION_1_0: + len = snprintf(buffer, sizeof(buffer), "WCD937X_1_0\n"); + break; + default: + len = snprintf(buffer, sizeof(buffer), "VER_UNDEFINED\n"); + } + + return simple_read_from_buffer(buf, count, &pos, buffer, len); +} + +static struct snd_info_entry_ops wcd937x_info_ops = { + .read = wcd937x_version_read, +}; + +/* + * wcd937x_info_create_codec_entry - creates wcd937x module + * @codec_root: The parent directory + * @codec: Codec instance + * + * Creates wcd937x module and version entry under the given + * parent directory. + * + * Return: 0 on success or negative error code on failure. + */ +int wcd937x_info_create_codec_entry(struct snd_info_entry *codec_root, + struct snd_soc_codec *codec) +{ + struct snd_info_entry *version_entry; + struct wcd937x_priv *priv; + struct snd_soc_card *card; + + if (!codec_root || !codec) + return -EINVAL; + + priv = snd_soc_codec_get_drvdata(codec); + if (priv->entry) { + dev_dbg(priv->dev, + "%s:wcd937x module already created\n", __func__); + return 0; + } + card = codec->component.card; + priv->entry = snd_info_create_subdir(codec_root->module, + "wcd937x", codec_root); + if (!priv->entry) { + dev_dbg(codec->dev, "%s: failed to create wcd937x entry\n", + __func__); + return -ENOMEM; + } + version_entry = snd_info_create_card_entry(card->snd_card, + "version", + priv->entry); + if (!version_entry) { + dev_dbg(codec->dev, "%s: failed to create wcd937x version entry\n", + __func__); + return -ENOMEM; + } + + version_entry->private_data = priv; + version_entry->size = WCD937X_VERSION_ENTRY_SIZE; + version_entry->content = SNDRV_INFO_CONTENT_DATA; + version_entry->c.ops = &wcd937x_info_ops; + + if (snd_info_register(version_entry) < 0) { + snd_info_free_entry(version_entry); + return -ENOMEM; + } + priv->version_entry = version_entry; + + return 0; +} +EXPORT_SYMBOL(wcd937x_info_create_codec_entry); + +static int wcd937x_set_micbias_data(struct wcd937x_priv *wcd937x, + struct wcd937x_pdata *pdata) +{ + int vout_ctl_1 = 0, vout_ctl_2 = 0, vout_ctl_3 = 0; + int rc = 0; + + if (!pdata) { + dev_err(wcd937x->dev, "%s: NULL pdata\n", __func__); + return -ENODEV; + } + + /* set micbias voltage */ + vout_ctl_1 = wcd937x_get_micb_vout_ctl_val(pdata->micbias.micb1_mv); + vout_ctl_2 = wcd937x_get_micb_vout_ctl_val(pdata->micbias.micb2_mv); + vout_ctl_3 = wcd937x_get_micb_vout_ctl_val(pdata->micbias.micb3_mv); + if (vout_ctl_1 < 0 || vout_ctl_2 < 0 || vout_ctl_3 < 0) { + rc = -EINVAL; + goto done; + } + regmap_update_bits(wcd937x->regmap, WCD937X_ANA_MICB1, 0x3F, + vout_ctl_1); + regmap_update_bits(wcd937x->regmap, WCD937X_ANA_MICB2, 0x3F, + vout_ctl_2); + regmap_update_bits(wcd937x->regmap, WCD937X_ANA_MICB3, 0x3F, + vout_ctl_3); + +done: + return rc; +} + +static int wcd937x_soc_codec_probe(struct snd_soc_codec *codec) +{ + struct wcd937x_priv *wcd937x = snd_soc_codec_get_drvdata(codec); + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + int variant; + int ret = -EINVAL; + + dev_info(codec->dev, "%s()\n", __func__); + wcd937x = snd_soc_codec_get_drvdata(codec); + + if (!wcd937x) + return -EINVAL; + + wcd937x->codec = codec; + variant = (snd_soc_read(codec, WCD937X_DIGITAL_EFUSE_REG_0) & 0x0E) >> 1; + wcd937x->variant = variant; + + wcd937x->fw_data = devm_kzalloc(codec->dev, + sizeof(*(wcd937x->fw_data)), + GFP_KERNEL); + if (!wcd937x->fw_data) { + dev_err(codec->dev, "Failed to allocate fw_data\n"); + ret = -ENOMEM; + goto err; + } + + set_bit(WCD9XXX_MBHC_CAL, wcd937x->fw_data->cal_bit); + ret = wcd_cal_create_hwdep(wcd937x->fw_data, + WCD9XXX_CODEC_HWDEP_NODE, codec); + + if (ret < 0) { + dev_err(codec->dev, "%s hwdep failed %d\n", __func__, ret); + goto err_hwdep; + } + + ret = wcd937x_mbhc_init(&wcd937x->mbhc, codec, wcd937x->fw_data); + if (ret) { + pr_err("%s: mbhc initialization failed\n", __func__); + goto err_hwdep; + } + snd_soc_dapm_ignore_suspend(dapm, "AMIC1"); + snd_soc_dapm_ignore_suspend(dapm, "AMIC2"); + snd_soc_dapm_ignore_suspend(dapm, "AMIC3"); + snd_soc_dapm_ignore_suspend(dapm, "IN1_HPHL"); + snd_soc_dapm_ignore_suspend(dapm, "IN2_HPHR"); + snd_soc_dapm_ignore_suspend(dapm, "IN3_AUX"); + snd_soc_dapm_ignore_suspend(dapm, "ADC1_OUTPUT"); + snd_soc_dapm_ignore_suspend(dapm, "ADC2_OUTPUT"); + snd_soc_dapm_ignore_suspend(dapm, "EAR"); + snd_soc_dapm_ignore_suspend(dapm, "AUX"); + snd_soc_dapm_ignore_suspend(dapm, "HPHL"); + snd_soc_dapm_ignore_suspend(dapm, "HPHR"); + snd_soc_dapm_sync(dapm); + + wcd_cls_h_init(&wcd937x->clsh_info); + wcd937x_init_reg(codec); + + if (wcd937x->variant == WCD9375_VARIANT) { + ret = snd_soc_dapm_new_controls(dapm, wcd9375_dapm_widgets, + ARRAY_SIZE(wcd9375_dapm_widgets)); + if (ret < 0) { + dev_err(codec->dev, "%s: Failed to add snd_ctls\n", + __func__); + goto err_hwdep; + } + ret = snd_soc_dapm_add_routes(dapm, wcd9375_audio_map, + ARRAY_SIZE(wcd9375_audio_map)); + if (ret < 0) { + dev_err(codec->dev, "%s: Failed to add routes\n", + __func__); + goto err_hwdep; + } + snd_soc_dapm_ignore_suspend(dapm, "AMIC4"); + snd_soc_dapm_ignore_suspend(dapm, "DMIC1_OUTPUT"); + snd_soc_dapm_ignore_suspend(dapm, "DMIC2_OUTPUT"); + snd_soc_dapm_ignore_suspend(dapm, "DMIC3_OUTPUT"); + snd_soc_dapm_ignore_suspend(dapm, "DMIC4_OUTPUT"); + snd_soc_dapm_ignore_suspend(dapm, "DMIC5_OUTPUT"); + snd_soc_dapm_ignore_suspend(dapm, "DMIC6_OUTPUT"); + snd_soc_dapm_ignore_suspend(dapm, "ADC3_OUTPUT"); + snd_soc_dapm_sync(dapm); + } + wcd937x->version = WCD937X_VERSION_1_0; + /* Register event notifier */ + wcd937x->nblock.notifier_call = wcd937x_event_notify; + if (wcd937x->register_notifier) { + ret = wcd937x->register_notifier(wcd937x->handle, + &wcd937x->nblock, + true); + if (ret) { + dev_err(codec->dev, + "%s: Failed to register notifier %d\n", + __func__, ret); + return ret; + } + } + return ret; + +err_hwdep: + wcd937x->fw_data = NULL; + +err: + return ret; +} + +static int wcd937x_soc_codec_remove(struct snd_soc_codec *codec) +{ + struct wcd937x_priv *wcd937x = snd_soc_codec_get_drvdata(codec); + + if (!wcd937x) + return -EINVAL; + + if (wcd937x->register_notifier) + return wcd937x->register_notifier(wcd937x->handle, + &wcd937x->nblock, + false); + return 0; +} + +static struct regmap *wcd937x_get_regmap(struct device *dev) +{ + struct wcd937x_priv *wcd937x = dev_get_drvdata(dev); + + return wcd937x->regmap; +} + +static struct snd_soc_codec_driver soc_codec_dev_wcd937x = { + .probe = wcd937x_soc_codec_probe, + .remove = wcd937x_soc_codec_remove, + .get_regmap = wcd937x_get_regmap, + .component_driver = { + .controls = wcd937x_snd_controls, + .num_controls = ARRAY_SIZE(wcd937x_snd_controls), + .dapm_widgets = wcd937x_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wcd937x_dapm_widgets), + .dapm_routes = wcd937x_audio_map, + .num_dapm_routes = ARRAY_SIZE(wcd937x_audio_map), + }, +}; + +#ifdef CONFIG_PM_SLEEP +static int wcd937x_suspend(struct device *dev) +{ + struct wcd937x_priv *wcd937x = NULL; + int ret = 0; + struct wcd937x_pdata *pdata = NULL; + + if (!dev) + return -ENODEV; + + wcd937x = dev_get_drvdata(dev); + if (!wcd937x) + return -EINVAL; + + pdata = dev_get_platdata(wcd937x->dev); + + if (!pdata) { + dev_err(dev, "%s: pdata is NULL\n", __func__); + return -EINVAL; + } + + if (test_bit(ALLOW_BUCK_DISABLE, &wcd937x->status_mask)) { + ret = msm_cdc_disable_ondemand_supply(wcd937x->dev, + wcd937x->supplies, + pdata->regulator, + pdata->num_supplies, + "cdc-vdd-buck"); + if (ret == -EINVAL) { + dev_err(dev, "%s: vdd buck is not disabled\n", + __func__); + return 0; + } + clear_bit(ALLOW_BUCK_DISABLE, &wcd937x->status_mask); + } + return 0; +} + +static int wcd937x_resume(struct device *dev) +{ + return 0; +} +#endif + +static int wcd937x_reset(struct device *dev) +{ + struct wcd937x_priv *wcd937x = NULL; + int rc = 0; + int value = 0; + + if (!dev) + return -ENODEV; + + wcd937x = dev_get_drvdata(dev); + if (!wcd937x) + return -EINVAL; + + if (!wcd937x->rst_np) { + dev_err(dev, "%s: reset gpio device node not specified\n", + __func__); + return -EINVAL; + } + + value = msm_cdc_pinctrl_get_state(wcd937x->rst_np); + if (value > 0) + return 0; + + rc = msm_cdc_pinctrl_select_sleep_state(wcd937x->rst_np); + if (rc) { + dev_err(dev, "%s: wcd sleep state request fail!\n", + __func__); + return rc; + } + /* 20ms sleep required after pulling the reset gpio to LOW */ + usleep_range(20, 30); + + rc = msm_cdc_pinctrl_select_active_state(wcd937x->rst_np); + if (rc) { + dev_err(dev, "%s: wcd active state request fail!\n", + __func__); + return rc; + } + /* 20ms sleep required after pulling the reset gpio to HIGH */ + usleep_range(20, 30); + + return rc; +} + +static int wcd937x_read_of_property_u32(struct device *dev, const char *name, + u32 *val) +{ + int rc = 0; + + rc = of_property_read_u32(dev->of_node, name, val); + if (rc) + dev_err(dev, "%s: Looking up %s property in node %s failed\n", + __func__, name, dev->of_node->full_name); + + return rc; +} + +static void wcd937x_dt_parse_micbias_info(struct device *dev, + struct wcd937x_micbias_setting *mb) +{ + u32 prop_val = 0; + int rc = 0; + + /* MB1 */ + if (of_find_property(dev->of_node, "qcom,cdc-micbias1-mv", + NULL)) { + rc = wcd937x_read_of_property_u32(dev, + "qcom,cdc-micbias1-mv", + &prop_val); + if (!rc) + mb->micb1_mv = prop_val; + } else { + dev_info(dev, "%s: Micbias1 DT property not found\n", + __func__); + } + + /* MB2 */ + if (of_find_property(dev->of_node, "qcom,cdc-micbias2-mv", + NULL)) { + rc = wcd937x_read_of_property_u32(dev, + "qcom,cdc-micbias2-mv", + &prop_val); + if (!rc) + mb->micb2_mv = prop_val; + } else { + dev_info(dev, "%s: Micbias2 DT property not found\n", + __func__); + } + + /* MB3 */ + if (of_find_property(dev->of_node, "qcom,cdc-micbias3-mv", + NULL)) { + rc = wcd937x_read_of_property_u32(dev, + "qcom,cdc-micbias3-mv", + &prop_val); + if (!rc) + mb->micb3_mv = prop_val; + } else { + dev_info(dev, "%s: Micbias3 DT property not found\n", + __func__); + } +} + +static int wcd937x_reset_low(struct device *dev) +{ + struct wcd937x_priv *wcd937x = NULL; + int rc = 0; + + if (!dev) + return -ENODEV; + + wcd937x = dev_get_drvdata(dev); + if (!wcd937x) + return -EINVAL; + + if (!wcd937x->rst_np) { + dev_err(dev, "%s: reset gpio device node not specified\n", + __func__); + return -EINVAL; + } + + rc = msm_cdc_pinctrl_select_sleep_state(wcd937x->rst_np); + if (rc) { + dev_err(dev, "%s: wcd sleep state request fail!\n", + __func__); + return rc; + } + /* 20ms sleep required after pulling the reset gpio to LOW */ + usleep_range(20, 30); + + return rc; +} + +struct wcd937x_pdata *wcd937x_populate_dt_data(struct device *dev) +{ + struct wcd937x_pdata *pdata = NULL; + + pdata = devm_kzalloc(dev, sizeof(struct wcd937x_pdata), + GFP_KERNEL); + if (!pdata) + return NULL; + + pdata->rst_np = of_parse_phandle(dev->of_node, + "qcom,wcd-rst-gpio-node", 0); + + if (!pdata->rst_np) { + dev_err(dev, "%s: Looking up %s property in node %s failed\n", + __func__, "qcom,wcd-rst-gpio-node", + dev->of_node->full_name); + return NULL; + } + + /* Parse power supplies */ + msm_cdc_get_power_supplies(dev, &pdata->regulator, + &pdata->num_supplies); + if (!pdata->regulator || (pdata->num_supplies <= 0)) { + dev_err(dev, "%s: no power supplies defined for codec\n", + __func__); + return NULL; + } + + pdata->rx_slave = of_parse_phandle(dev->of_node, "qcom,rx-slave", 0); + pdata->tx_slave = of_parse_phandle(dev->of_node, "qcom,tx-slave", 0); + wcd937x_dt_parse_micbias_info(dev, &pdata->micbias); + + return pdata; +} + +static int wcd937x_wakeup(void *handle, bool enable) +{ + struct wcd937x_priv *priv; + + if (!handle) { + pr_err("%s: NULL handle\n", __func__); + return -EINVAL; + } + priv = (struct wcd937x_priv *)handle; + if (!priv->tx_swr_dev) { + pr_err("%s: tx swr dev is NULL\n", __func__); + return -EINVAL; + } + if (enable) + return swr_device_wakeup_vote(priv->tx_swr_dev); + else + return swr_device_wakeup_unvote(priv->tx_swr_dev); +} + +static int wcd937x_bind(struct device *dev) +{ + int ret = 0, i = 0; + struct wcd937x_priv *wcd937x = NULL; + struct wcd937x_pdata *pdata = NULL; + struct wcd_ctrl_platform_data *plat_data = NULL; + + wcd937x = devm_kzalloc(dev, sizeof(struct wcd937x_priv), GFP_KERNEL); + if (!wcd937x) + return -ENOMEM; + + dev_set_drvdata(dev, wcd937x); + + pdata = wcd937x_populate_dt_data(dev); + if (!pdata) { + dev_err(dev, "%s: Fail to obtain platform data\n", __func__); + return -EINVAL; + } + wcd937x->dev = dev; + wcd937x->dev->platform_data = pdata; + wcd937x->rst_np = pdata->rst_np; + ret = msm_cdc_init_supplies(dev, &wcd937x->supplies, + pdata->regulator, pdata->num_supplies); + if (!wcd937x->supplies) { + dev_err(dev, "%s: Cannot init wcd supplies\n", + __func__); + return ret; + } + + plat_data = dev_get_platdata(dev->parent); + if (!plat_data) { + dev_err(dev, "%s: platform data from parent is NULL\n", + __func__); + return -EINVAL; + } + wcd937x->handle = (void *)plat_data->handle; + if (!wcd937x->handle) { + dev_err(dev, "%s: handle is NULL\n", __func__); + return -EINVAL; + } + wcd937x->update_wcd_event = plat_data->update_wcd_event; + if (!wcd937x->update_wcd_event) { + dev_err(dev, "%s: update_wcd_event api is null!\n", + __func__); + return -EINVAL; + } + wcd937x->register_notifier = plat_data->register_notifier; + if (!wcd937x->register_notifier) { + dev_err(dev, "%s: register_notifier api is null!\n", + __func__); + return -EINVAL; + } + + ret = msm_cdc_enable_static_supplies(dev, wcd937x->supplies, + pdata->regulator, + pdata->num_supplies); + if (ret) { + dev_err(dev, "%s: wcd static supply enable failed!\n", + __func__); + return ret; + } + + wcd937x_reset(dev); + /* + * Add 5msec delay to provide sufficient time for + * soundwire auto enumeration of slave devices as + * as per HW requirement. + */ + usleep_range(5000, 5010); + wcd937x->wakeup = wcd937x_wakeup; + + ret = component_bind_all(dev, wcd937x); + if (ret) { + dev_err(dev, "%s: Slave bind failed, ret = %d\n", + __func__, ret); + return ret; + } + + ret = wcd937x_parse_port_mapping(dev, "qcom,rx_swr_ch_map", CODEC_RX); + ret |= wcd937x_parse_port_mapping(dev, "qcom,tx_swr_ch_map", CODEC_TX); + + if (ret) { + dev_err(dev, "Failed to read port mapping\n"); + goto err; + } + + wcd937x->rx_swr_dev = get_matching_swr_slave_device(pdata->rx_slave); + if (!wcd937x->rx_swr_dev) { + dev_err(dev, "%s: Could not find RX swr slave device\n", + __func__); + ret = -ENODEV; + goto err; + } + + wcd937x->tx_swr_dev = get_matching_swr_slave_device(pdata->tx_slave); + if (!wcd937x->tx_swr_dev) { + dev_err(dev, "%s: Could not find TX swr slave device\n", + __func__); + ret = -ENODEV; + goto err; + } + + wcd937x->regmap = devm_regmap_init_swr(wcd937x->tx_swr_dev, + &wcd937x_regmap_config); + if (!wcd937x->regmap) { + dev_err(dev, "%s: Regmap init failed\n", + __func__); + goto err; + } + + /* Set all interupts as edge triggered */ + for (i = 0; i < wcd937x_regmap_irq_chip.num_regs; i++) + regmap_write(wcd937x->regmap, + (WCD937X_DIGITAL_INTR_LEVEL_0 + i), 0); + + wcd937x_regmap_irq_chip.irq_drv_data = wcd937x; + wcd937x->irq_info.wcd_regmap_irq_chip = &wcd937x_regmap_irq_chip; + wcd937x->irq_info.codec_name = "WCD937X"; + wcd937x->irq_info.regmap = wcd937x->regmap; + wcd937x->irq_info.dev = dev; + ret = wcd_irq_init(&wcd937x->irq_info, &wcd937x->virq); + + if (ret) { + dev_err(dev, "%s: IRQ init failed: %d\n", + __func__, ret); + goto err; + } + wcd937x->tx_swr_dev->slave_irq = wcd937x->virq; + + ret = wcd937x_set_micbias_data(wcd937x, pdata); + if (ret < 0) { + dev_err(dev, "%s: bad micbias pdata\n", __func__); + goto err_irq; + } + + mutex_init(&wcd937x->micb_lock); + mutex_init(&wcd937x->ana_tx_clk_lock); + ret = snd_soc_register_codec(dev, &soc_codec_dev_wcd937x, + NULL, 0); + if (ret) { + dev_err(dev, "%s: Codec registration failed\n", + __func__); + goto err_irq; + } + + return ret; +err_irq: + wcd_irq_exit(&wcd937x->irq_info, wcd937x->virq); +err: + component_unbind_all(dev, wcd937x); + return ret; +} + +static void wcd937x_unbind(struct device *dev) +{ + struct wcd937x_priv *wcd937x = dev_get_drvdata(dev); + + wcd_irq_exit(&wcd937x->irq_info, wcd937x->virq); + snd_soc_unregister_codec(dev); + component_unbind_all(dev, wcd937x); + mutex_destroy(&wcd937x->micb_lock); + mutex_destroy(&wcd937x->ana_tx_clk_lock); +} + +static const struct of_device_id wcd937x_dt_match[] = { + { .compatible = "qcom,wcd937x-codec" }, + {} +}; + +static const struct component_master_ops wcd937x_comp_ops = { + .bind = wcd937x_bind, + .unbind = wcd937x_unbind, +}; + +static int wcd937x_compare_of(struct device *dev, void *data) +{ + return dev->of_node == data; +} + +static void wcd937x_release_of(struct device *dev, void *data) +{ + of_node_put(data); +} + +static int wcd937x_add_slave_components(struct device *dev, + struct component_match **matchptr) +{ + struct device_node *np, *rx_node, *tx_node; + + np = dev->of_node; + + rx_node = of_parse_phandle(np, "qcom,rx-slave", 0); + if (!rx_node) { + dev_err(dev, "%s: Rx-slave node not defined\n", __func__); + return -ENODEV; + } + of_node_get(rx_node); + component_match_add_release(dev, matchptr, + wcd937x_release_of, + wcd937x_compare_of, + rx_node); + + tx_node = of_parse_phandle(np, "qcom,tx-slave", 0); + if (!tx_node) { + dev_err(dev, "%s: Tx-slave node not defined\n", __func__); + return -ENODEV; + } + of_node_get(tx_node); + component_match_add_release(dev, matchptr, + wcd937x_release_of, + wcd937x_compare_of, + tx_node); + return 0; +} + +static int wcd937x_probe(struct platform_device *pdev) +{ + struct component_match *match = NULL; + int ret; + + ret = wcd937x_add_slave_components(&pdev->dev, &match); + if (ret) + return ret; + + return component_master_add_with_match(&pdev->dev, + &wcd937x_comp_ops, match); +} + +static int wcd937x_remove(struct platform_device *pdev) +{ + component_master_del(&pdev->dev, &wcd937x_comp_ops); + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static const struct dev_pm_ops wcd937x_dev_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS( + wcd937x_suspend, + wcd937x_resume + ) +}; +#endif + +static struct platform_driver wcd937x_codec_driver = { + .probe = wcd937x_probe, + .remove = wcd937x_remove, + .driver = { + .name = "wcd937x_codec", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(wcd937x_dt_match), +#ifdef CONFIG_PM_SLEEP + .pm = &wcd937x_dev_pm_ops, +#endif + }, +}; + +module_platform_driver(wcd937x_codec_driver); +MODULE_DESCRIPTION("WCD937X Codec driver"); +MODULE_LICENSE("GPL v2"); diff --git a/audio-kernel/asoc/codecs/wcd937x/wcd937x.h b/audio-kernel/asoc/codecs/wcd937x/wcd937x.h new file mode 100644 index 000000000..49905d77f --- /dev/null +++ b/audio-kernel/asoc/codecs/wcd937x/wcd937x.h @@ -0,0 +1,27 @@ +/* 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 _WCD937X_H +#define _WCD937X_H + +#ifdef CONFIG_SND_SOC_WCD937X +extern int wcd937x_info_create_codec_entry(struct snd_info_entry *codec_root, + struct snd_soc_codec *codec); +#else +extern int wcd937x_info_create_codec_entry(struct snd_info_entry *codec_root, + struct snd_soc_codec *codec) +{ + return 0; +} +#endif /* CONFIG_SND_SOC_WCD937X */ + +#endif diff --git a/audio-kernel/asoc/codecs/wcd937x/wcd937x_slave.c b/audio-kernel/asoc/codecs/wcd937x/wcd937x_slave.c new file mode 100644 index 000000000..baab26fe6 --- /dev/null +++ b/audio-kernel/asoc/codecs/wcd937x/wcd937x_slave.c @@ -0,0 +1,152 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include + +struct wcd937x_slave_priv { + struct swr_device *swr_slave; +}; + +static int wcd937x_slave_bind(struct device *dev, + struct device *master, void *data) +{ + int ret = 0; + struct wcd937x_slave_priv *wcd937x_slave = NULL; + uint8_t devnum = 0; + struct swr_device *pdev = to_swr_device(dev); + + if (pdev == NULL) { + dev_err(dev, "%s: pdev is NULL\n", __func__); + return -EINVAL; + } + + wcd937x_slave = devm_kzalloc(&pdev->dev, + sizeof(struct wcd937x_slave_priv), GFP_KERNEL); + if (!wcd937x_slave) + return -ENOMEM; + + swr_set_dev_data(pdev, wcd937x_slave); + + wcd937x_slave->swr_slave = pdev; + + ret = swr_get_logical_dev_num(pdev, pdev->addr, &devnum); + if (ret) { + dev_dbg(&pdev->dev, + "%s get devnum %d for dev addr %lx failed\n", + __func__, devnum, pdev->addr); + swr_remove_device(pdev); + return ret; + } + pdev->dev_num = devnum; + + return ret; +} + +static void wcd937x_slave_unbind(struct device *dev, + struct device *master, void *data) +{ + struct wcd937x_slave_priv *wcd937x_slave = NULL; + struct swr_device *pdev = to_swr_device(dev); + + if (pdev == NULL) { + dev_err(dev, "%s: pdev is NULL\n", __func__); + return; + } + + wcd937x_slave = swr_get_dev_data(pdev); + if (!wcd937x_slave) { + dev_err(&pdev->dev, "%s: wcd937x_slave is NULL\n", __func__); + return; + } + + swr_set_dev_data(pdev, NULL); +} + +static const struct swr_device_id wcd937x_swr_id[] = { + {"wcd937x-slave", 0}, + {} +}; + +static const struct of_device_id wcd937x_swr_dt_match[] = { + { + .compatible = "qcom,wcd937x-slave", + }, + {} +}; + +static const struct component_ops wcd937x_slave_comp_ops = { + .bind = wcd937x_slave_bind, + .unbind = wcd937x_slave_unbind, +}; + +static int wcd937x_swr_up(struct swr_device *pdev) +{ + return 0; +} + +static int wcd937x_swr_down(struct swr_device *pdev) +{ + return 0; +} + +static int wcd937x_swr_reset(struct swr_device *pdev) +{ + return 0; +} + +static int wcd937x_swr_probe(struct swr_device *pdev) +{ + return component_add(&pdev->dev, &wcd937x_slave_comp_ops); +} + +static int wcd937x_swr_remove(struct swr_device *pdev) +{ + component_del(&pdev->dev, &wcd937x_slave_comp_ops); + return 0; +} + +static struct swr_driver wcd937x_slave_driver = { + .driver = { + .name = "wcd937x-slave", + .owner = THIS_MODULE, + .of_match_table = wcd937x_swr_dt_match, + }, + .probe = wcd937x_swr_probe, + .remove = wcd937x_swr_remove, + .id_table = wcd937x_swr_id, + .device_up = wcd937x_swr_up, + .device_down = wcd937x_swr_down, + .reset_device = wcd937x_swr_reset, +}; + +static int __init wcd937x_slave_init(void) +{ + return swr_driver_register(&wcd937x_slave_driver); +} + +static void __exit wcd937x_slave_exit(void) +{ + swr_driver_unregister(&wcd937x_slave_driver); +} + +module_init(wcd937x_slave_init); +module_exit(wcd937x_slave_exit); + +MODULE_DESCRIPTION("WCD937X Swr Slave driver"); +MODULE_LICENSE("GPL v2"); diff --git a/audio-kernel/asoc/codecs/wcd9xxx-common-v2.c b/audio-kernel/asoc/codecs/wcd9xxx-common-v2.c new file mode 100644 index 000000000..478c0c6d7 --- /dev/null +++ b/audio-kernel/asoc/codecs/wcd9xxx-common-v2.c @@ -0,0 +1,1367 @@ +/* + * 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 + * 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 +#include +#include +#include +#include +#include +#include "core.h" +#include "wcd9xxx-common-v2.h" + +#define WCD_USLEEP_RANGE 50 +#define MAX_IMPED_PARAMS 6 + +enum { + DAC_GAIN_0DB = 0, + DAC_GAIN_0P2DB, + DAC_GAIN_0P4DB, + DAC_GAIN_0P6DB, + DAC_GAIN_0P8DB, + DAC_GAIN_M0P2DB, + DAC_GAIN_M0P4DB, + DAC_GAIN_M0P6DB, +}; + +enum { + VREF_FILT_R_0OHM = 0, + VREF_FILT_R_25KOHM, + VREF_FILT_R_50KOHM, + VREF_FILT_R_100KOHM, +}; + +enum { + DELTA_I_0MA, + DELTA_I_10MA, + DELTA_I_20MA, + DELTA_I_30MA, + DELTA_I_40MA, + DELTA_I_50MA, +}; + +struct wcd_imped_val { + u32 imped_val; + u8 index; +}; + +static const struct wcd_reg_mask_val imped_table[][MAX_IMPED_PARAMS] = { + { + {WCD9XXX_CDC_RX1_RX_VOL_CTL, 0xff, 0xf5}, + {WCD9XXX_CDC_RX1_RX_VOL_MIX_CTL, 0xff, 0xf5}, + {WCD9XXX_CDC_RX1_RX_PATH_SEC1, 0x01, 0x01}, + {WCD9XXX_CDC_RX2_RX_VOL_CTL, 0xff, 0xf5}, + {WCD9XXX_CDC_RX2_RX_VOL_MIX_CTL, 0xff, 0xf5}, + {WCD9XXX_CDC_RX2_RX_PATH_SEC1, 0x01, 0x01}, + }, + { + {WCD9XXX_CDC_RX1_RX_VOL_CTL, 0xff, 0xf7}, + {WCD9XXX_CDC_RX1_RX_VOL_MIX_CTL, 0xff, 0xf7}, + {WCD9XXX_CDC_RX1_RX_PATH_SEC1, 0x01, 0x01}, + {WCD9XXX_CDC_RX2_RX_VOL_CTL, 0xff, 0xf7}, + {WCD9XXX_CDC_RX2_RX_VOL_MIX_CTL, 0xff, 0xf7}, + {WCD9XXX_CDC_RX2_RX_PATH_SEC1, 0x01, 0x01}, + }, + { + {WCD9XXX_CDC_RX1_RX_VOL_CTL, 0xff, 0xf9}, + {WCD9XXX_CDC_RX1_RX_VOL_MIX_CTL, 0xff, 0xf9}, + {WCD9XXX_CDC_RX1_RX_PATH_SEC1, 0x01, 0x0}, + {WCD9XXX_CDC_RX2_RX_VOL_CTL, 0xff, 0xf9}, + {WCD9XXX_CDC_RX2_RX_VOL_MIX_CTL, 0xff, 0xf9}, + {WCD9XXX_CDC_RX2_RX_PATH_SEC1, 0x01, 0x0}, + }, + { + {WCD9XXX_CDC_RX1_RX_VOL_CTL, 0xff, 0xfa}, + {WCD9XXX_CDC_RX1_RX_VOL_MIX_CTL, 0xff, 0xfa}, + {WCD9XXX_CDC_RX1_RX_PATH_SEC1, 0x01, 0x01}, + {WCD9XXX_CDC_RX2_RX_VOL_CTL, 0xff, 0xfa}, + {WCD9XXX_CDC_RX2_RX_VOL_MIX_CTL, 0xff, 0xfa}, + {WCD9XXX_CDC_RX2_RX_PATH_SEC1, 0x01, 0x01}, + }, + { + {WCD9XXX_CDC_RX1_RX_VOL_CTL, 0xff, 0xfb}, + {WCD9XXX_CDC_RX1_RX_VOL_MIX_CTL, 0xff, 0xfb}, + {WCD9XXX_CDC_RX1_RX_PATH_SEC1, 0x01, 0x01}, + {WCD9XXX_CDC_RX2_RX_VOL_CTL, 0xff, 0xfb}, + {WCD9XXX_CDC_RX2_RX_VOL_MIX_CTL, 0xff, 0xfb}, + {WCD9XXX_CDC_RX2_RX_PATH_SEC1, 0x01, 0x01}, + }, + { + {WCD9XXX_CDC_RX1_RX_VOL_CTL, 0xff, 0xfc}, + {WCD9XXX_CDC_RX1_RX_VOL_MIX_CTL, 0xff, 0xfc}, + {WCD9XXX_CDC_RX1_RX_PATH_SEC1, 0x01, 0x01}, + {WCD9XXX_CDC_RX2_RX_VOL_CTL, 0xff, 0xfc}, + {WCD9XXX_CDC_RX2_RX_VOL_MIX_CTL, 0xff, 0xfc}, + {WCD9XXX_CDC_RX2_RX_PATH_SEC1, 0x01, 0x01}, + }, + { + {WCD9XXX_CDC_RX1_RX_VOL_CTL, 0xff, 0xfd}, + {WCD9XXX_CDC_RX1_RX_VOL_MIX_CTL, 0xff, 0xfd}, + {WCD9XXX_CDC_RX1_RX_PATH_SEC1, 0x01, 0x01}, + {WCD9XXX_CDC_RX2_RX_VOL_CTL, 0xff, 0xfd}, + {WCD9XXX_CDC_RX2_RX_VOL_MIX_CTL, 0xff, 0xfd}, + {WCD9XXX_CDC_RX2_RX_PATH_SEC1, 0x01, 0x01}, + }, + { + {WCD9XXX_CDC_RX1_RX_VOL_CTL, 0xff, 0xfe}, + {WCD9XXX_CDC_RX1_RX_VOL_MIX_CTL, 0xff, 0xfe}, + {WCD9XXX_CDC_RX1_RX_PATH_SEC1, 0x01, 0x01}, + {WCD9XXX_CDC_RX2_RX_VOL_CTL, 0xff, 0xfe}, + {WCD9XXX_CDC_RX2_RX_VOL_MIX_CTL, 0xff, 0xfe}, + {WCD9XXX_CDC_RX2_RX_PATH_SEC1, 0x01, 0x01}, + }, + { + {WCD9XXX_CDC_RX1_RX_VOL_CTL, 0xff, 0xff}, + {WCD9XXX_CDC_RX1_RX_VOL_MIX_CTL, 0xff, 0xff}, + {WCD9XXX_CDC_RX1_RX_PATH_SEC1, 0x01, 0x00}, + {WCD9XXX_CDC_RX2_RX_VOL_CTL, 0xff, 0xff}, + {WCD9XXX_CDC_RX2_RX_VOL_MIX_CTL, 0xff, 0xff}, + {WCD9XXX_CDC_RX2_RX_PATH_SEC1, 0x01, 0x00}, + }, +}; + +static const struct wcd_reg_mask_val imped_table_tavil[][MAX_IMPED_PARAMS] = { + { + {WCD9XXX_CDC_RX1_RX_VOL_CTL, 0xff, 0xf2}, + {WCD9XXX_CDC_RX1_RX_VOL_MIX_CTL, 0xff, 0xf2}, + {WCD9XXX_CDC_RX1_RX_PATH_SEC1, 0x01, 0x00}, + {WCD9XXX_CDC_RX2_RX_VOL_CTL, 0xff, 0xf2}, + {WCD9XXX_CDC_RX2_RX_VOL_MIX_CTL, 0xff, 0xf2}, + {WCD9XXX_CDC_RX2_RX_PATH_SEC1, 0x01, 0x00}, + }, + { + {WCD9XXX_CDC_RX1_RX_VOL_CTL, 0xff, 0xf4}, + {WCD9XXX_CDC_RX1_RX_VOL_MIX_CTL, 0xff, 0xf4}, + {WCD9XXX_CDC_RX1_RX_PATH_SEC1, 0x01, 0x00}, + {WCD9XXX_CDC_RX2_RX_VOL_CTL, 0xff, 0xf4}, + {WCD9XXX_CDC_RX2_RX_VOL_MIX_CTL, 0xff, 0xf4}, + {WCD9XXX_CDC_RX2_RX_PATH_SEC1, 0x01, 0x00}, + }, + { + {WCD9XXX_CDC_RX1_RX_VOL_CTL, 0xff, 0xf7}, + {WCD9XXX_CDC_RX1_RX_VOL_MIX_CTL, 0xff, 0xf7}, + {WCD9XXX_CDC_RX1_RX_PATH_SEC1, 0x01, 0x01}, + {WCD9XXX_CDC_RX2_RX_VOL_CTL, 0xff, 0xf7}, + {WCD9XXX_CDC_RX2_RX_VOL_MIX_CTL, 0xff, 0xf7}, + {WCD9XXX_CDC_RX2_RX_PATH_SEC1, 0x01, 0x01}, + }, + { + {WCD9XXX_CDC_RX1_RX_VOL_CTL, 0xff, 0xf9}, + {WCD9XXX_CDC_RX1_RX_VOL_MIX_CTL, 0xff, 0xf9}, + {WCD9XXX_CDC_RX1_RX_PATH_SEC1, 0x01, 0x00}, + {WCD9XXX_CDC_RX2_RX_VOL_CTL, 0xff, 0xf9}, + {WCD9XXX_CDC_RX2_RX_VOL_MIX_CTL, 0xff, 0xf9}, + {WCD9XXX_CDC_RX2_RX_PATH_SEC1, 0x01, 0x00}, + }, + { + {WCD9XXX_CDC_RX1_RX_VOL_CTL, 0xff, 0xfa}, + {WCD9XXX_CDC_RX1_RX_VOL_MIX_CTL, 0xff, 0xfa}, + {WCD9XXX_CDC_RX1_RX_PATH_SEC1, 0x01, 0x00}, + {WCD9XXX_CDC_RX2_RX_VOL_CTL, 0xff, 0xfa}, + {WCD9XXX_CDC_RX2_RX_VOL_MIX_CTL, 0xff, 0xfa}, + {WCD9XXX_CDC_RX2_RX_PATH_SEC1, 0x01, 0x00}, + }, + { + {WCD9XXX_CDC_RX1_RX_VOL_CTL, 0xff, 0xfb}, + {WCD9XXX_CDC_RX1_RX_VOL_MIX_CTL, 0xff, 0xfb}, + {WCD9XXX_CDC_RX1_RX_PATH_SEC1, 0x01, 0x00}, + {WCD9XXX_CDC_RX2_RX_VOL_CTL, 0xff, 0xfb}, + {WCD9XXX_CDC_RX2_RX_VOL_MIX_CTL, 0xff, 0xfb}, + {WCD9XXX_CDC_RX2_RX_PATH_SEC1, 0x01, 0x00}, + }, + { + {WCD9XXX_CDC_RX1_RX_VOL_CTL, 0xff, 0xfc}, + {WCD9XXX_CDC_RX1_RX_VOL_MIX_CTL, 0xff, 0xfc}, + {WCD9XXX_CDC_RX1_RX_PATH_SEC1, 0x01, 0x00}, + {WCD9XXX_CDC_RX2_RX_VOL_CTL, 0xff, 0xfc}, + {WCD9XXX_CDC_RX2_RX_VOL_MIX_CTL, 0xff, 0xfc}, + {WCD9XXX_CDC_RX2_RX_PATH_SEC1, 0x01, 0x00}, + }, + { + {WCD9XXX_CDC_RX1_RX_VOL_CTL, 0xff, 0xfd}, + {WCD9XXX_CDC_RX1_RX_VOL_MIX_CTL, 0xff, 0xfd}, + {WCD9XXX_CDC_RX1_RX_PATH_SEC1, 0x01, 0x00}, + {WCD9XXX_CDC_RX2_RX_VOL_CTL, 0xff, 0xfd}, + {WCD9XXX_CDC_RX2_RX_VOL_MIX_CTL, 0xff, 0xfd}, + {WCD9XXX_CDC_RX2_RX_PATH_SEC1, 0x01, 0x00}, + }, + { + {WCD9XXX_CDC_RX1_RX_VOL_CTL, 0xff, 0xfd}, + {WCD9XXX_CDC_RX1_RX_VOL_MIX_CTL, 0xff, 0xfd}, + {WCD9XXX_CDC_RX1_RX_PATH_SEC1, 0x01, 0x01}, + {WCD9XXX_CDC_RX2_RX_VOL_CTL, 0xff, 0xfd}, + {WCD9XXX_CDC_RX2_RX_VOL_MIX_CTL, 0xff, 0xfd}, + {WCD9XXX_CDC_RX2_RX_PATH_SEC1, 0x01, 0x01}, + }, +}; + +static const struct wcd_imped_val imped_index[] = { + {4, 0}, + {5, 1}, + {6, 2}, + {7, 3}, + {8, 4}, + {9, 5}, + {10, 6}, + {11, 7}, + {12, 8}, + {13, 9}, +}; + +static void (*clsh_state_fp[NUM_CLSH_STATES_V2])(struct snd_soc_codec *, + struct wcd_clsh_cdc_data *, + u8 req_state, bool en, int mode); + +static int get_impedance_index(int imped) +{ + int i = 0; + + if (imped < imped_index[i].imped_val) { + pr_debug("%s, detected impedance is less than 4 Ohm\n", + __func__); + i = 0; + goto ret; + } + if (imped >= imped_index[ARRAY_SIZE(imped_index) - 1].imped_val) { + pr_debug("%s, detected impedance is greater than 12 Ohm\n", + __func__); + i = ARRAY_SIZE(imped_index) - 1; + goto ret; + } + for (i = 0; i < ARRAY_SIZE(imped_index) - 1; i++) { + if (imped >= imped_index[i].imped_val && + imped < imped_index[i + 1].imped_val) + break; + } +ret: + pr_debug("%s: selected impedance index = %d\n", + __func__, imped_index[i].index); + return imped_index[i].index; +} + +/* + * Function: wcd_clsh_imped_config + * Params: codec, imped, reset + * Description: + * This function updates HPHL and HPHR gain settings + * according to the impedance value. + */ +void wcd_clsh_imped_config(struct snd_soc_codec *codec, int imped, bool reset) +{ + int i; + int index = 0; + int table_size; + + static const struct wcd_reg_mask_val + (*imped_table_ptr)[MAX_IMPED_PARAMS]; + struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent); + + if (IS_CODEC_TYPE(wcd9xxx, WCD934X)) { + table_size = ARRAY_SIZE(imped_table_tavil); + imped_table_ptr = imped_table_tavil; + } else { + table_size = ARRAY_SIZE(imped_table); + imped_table_ptr = imped_table; + } + + /* reset = 1, which means request is to reset the register values */ + if (reset) { + for (i = 0; i < MAX_IMPED_PARAMS; i++) + snd_soc_update_bits(codec, + imped_table_ptr[index][i].reg, + imped_table_ptr[index][i].mask, 0); + return; + } + index = get_impedance_index(imped); + if (index >= (ARRAY_SIZE(imped_index) - 1)) { + pr_debug("%s, impedance not in range = %d\n", __func__, imped); + return; + } + if (index >= table_size) { + pr_debug("%s, impedance index not in range = %d\n", __func__, + index); + return; + } + for (i = 0; i < MAX_IMPED_PARAMS; i++) + snd_soc_update_bits(codec, + imped_table_ptr[index][i].reg, + imped_table_ptr[index][i].mask, + imped_table_ptr[index][i].val); +} +EXPORT_SYMBOL(wcd_clsh_imped_config); + +static bool is_native_44_1_active(struct snd_soc_codec *codec) +{ + bool native_active = false; + u8 native_clk, rx1_rate, rx2_rate; + + native_clk = snd_soc_read(codec, + WCD9XXX_CDC_CLK_RST_CTRL_MCLK_CONTROL); + rx1_rate = snd_soc_read(codec, WCD9XXX_CDC_RX1_RX_PATH_CTL); + rx2_rate = snd_soc_read(codec, WCD9XXX_CDC_RX2_RX_PATH_CTL); + + dev_dbg(codec->dev, "%s: native_clk %x rx1_rate= %x rx2_rate= %x", + __func__, native_clk, rx1_rate, rx2_rate); + + if ((native_clk & 0x2) && + ((rx1_rate & 0x0F) == 0x9 || (rx2_rate & 0x0F) == 0x9)) + native_active = true; + + return native_active; +} + +static const char *mode_to_str(int mode) +{ + switch (mode) { + case CLS_H_NORMAL: + return "CLS_H_NORMAL"; + case CLS_H_HIFI: + return "CLS_H_HIFI"; + case CLS_H_LOHIFI: + return "CLS_H_LOHIFI"; + case CLS_H_LP: + return "CLS_H_LP"; + case CLS_H_ULP: + return "CLS_H_ULP"; + case CLS_AB: + return "CLS_AB"; + case CLS_AB_HIFI: + return "CLS_AB_HIFI"; + default: + return "CLS_H_INVALID"; + }; +} + +static const char *state_to_str(u8 state, char *buf, size_t buflen) +{ + int i; + int cnt = 0; + /* + * This array of strings should match with enum wcd_clsh_state_bit. + */ + static const char *const states[] = { + "STATE_EAR", + "STATE_HPH_L", + "STATE_HPH_R", + "STATE_LO", + }; + + if (state == WCD_CLSH_STATE_IDLE) { + snprintf(buf, buflen, "[STATE_IDLE]"); + goto done; + } + + buf[0] = '\0'; + for (i = 0; i < ARRAY_SIZE(states); i++) { + if (!(state & (1 << i))) + continue; + cnt = snprintf(buf, buflen - cnt - 1, "%s%s%s", buf, + buf[0] == '\0' ? "[" : "|", + states[i]); + } + if (cnt > 0) + strlcat(buf + cnt, "]", buflen); + +done: + if (buf[0] == '\0') + snprintf(buf, buflen, "[STATE_UNKNOWN]"); + return buf; +} + +static inline void +wcd_enable_clsh_block(struct snd_soc_codec *codec, + struct wcd_clsh_cdc_data *clsh_d, bool enable) +{ + if ((enable && ++clsh_d->clsh_users == 1) || + (!enable && --clsh_d->clsh_users == 0)) + snd_soc_update_bits(codec, WCD9XXX_A_CDC_CLSH_CRC, 0x01, + (u8) enable); + if (clsh_d->clsh_users < 0) + clsh_d->clsh_users = 0; + dev_dbg(codec->dev, "%s: clsh_users %d, enable %d", __func__, + clsh_d->clsh_users, enable); +} + +static inline bool wcd_clsh_enable_status(struct snd_soc_codec *codec) +{ + return snd_soc_read(codec, WCD9XXX_A_CDC_CLSH_CRC) & 0x01; +} + +static inline int wcd_clsh_get_int_mode(struct wcd_clsh_cdc_data *clsh_d, + int clsh_state) +{ + int mode; + + if ((clsh_state != WCD_CLSH_STATE_EAR) && + (clsh_state != WCD_CLSH_STATE_HPHL) && + (clsh_state != WCD_CLSH_STATE_HPHR) && + (clsh_state != WCD_CLSH_STATE_LO)) + mode = CLS_NONE; + else + mode = clsh_d->interpolator_modes[ffs(clsh_state)]; + + return mode; +} + +static inline void wcd_clsh_set_int_mode(struct wcd_clsh_cdc_data *clsh_d, + int clsh_state, int mode) +{ + if ((clsh_state != WCD_CLSH_STATE_EAR) && + (clsh_state != WCD_CLSH_STATE_HPHL) && + (clsh_state != WCD_CLSH_STATE_HPHR) && + (clsh_state != WCD_CLSH_STATE_LO)) + return; + + clsh_d->interpolator_modes[ffs(clsh_state)] = mode; +} + +static inline void wcd_clsh_set_buck_mode(struct snd_soc_codec *codec, + int mode) +{ + if (mode == CLS_H_HIFI || mode == CLS_H_LOHIFI || + mode == CLS_AB_HIFI || mode == CLS_AB) + snd_soc_update_bits(codec, WCD9XXX_A_ANA_RX_SUPPLIES, + 0x08, 0x08); /* set to HIFI */ + else + snd_soc_update_bits(codec, WCD9XXX_A_ANA_RX_SUPPLIES, + 0x08, 0x00); /* set to default */ +} + +static inline void wcd_clsh_set_flyback_mode(struct snd_soc_codec *codec, + int mode) +{ + if (mode == CLS_H_HIFI || mode == CLS_H_LOHIFI || + mode == CLS_AB_HIFI || mode == CLS_AB) + snd_soc_update_bits(codec, WCD9XXX_A_ANA_RX_SUPPLIES, + 0x04, 0x04); /* set to HIFI */ + else + snd_soc_update_bits(codec, WCD9XXX_A_ANA_RX_SUPPLIES, + 0x04, 0x00); /* set to Default */ +} + +static inline void wcd_clsh_gm3_boost_disable(struct snd_soc_codec *codec, + int mode) +{ + struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent); + + if (!IS_CODEC_TYPE(wcd9xxx, WCD934X)) + return; + + if (mode == CLS_H_HIFI || mode == CLS_H_LOHIFI || + mode == CLS_AB_HIFI || mode == CLS_AB) { + if (TAVIL_IS_1_0(wcd9xxx)) + snd_soc_update_bits(codec, WCD9XXX_HPH_CNP_WG_CTL, + 0x80, 0x0); /* disable GM3 Boost */ + snd_soc_update_bits(codec, WCD9XXX_FLYBACK_VNEG_CTRL_4, + 0xF0, 0x80); + } else { + snd_soc_update_bits(codec, WCD9XXX_HPH_CNP_WG_CTL, + 0x80, 0x80); /* set to Default */ + snd_soc_update_bits(codec, WCD9XXX_FLYBACK_VNEG_CTRL_4, + 0xF0, 0x70); + } +} + + +static inline void wcd_clsh_force_iq_ctl(struct snd_soc_codec *codec, + int mode) +{ + struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent); + + if (!IS_CODEC_TYPE(wcd9xxx, WCD934X)) + return; + + if (mode == CLS_H_LOHIFI || mode == CLS_AB) { + snd_soc_update_bits(codec, WCD9XXX_HPH_NEW_INT_PA_MISC2, + 0x20, 0x20); + snd_soc_update_bits(codec, WCD9XXX_RX_BIAS_HPH_LOWPOWER, + 0xF0, 0xC0); + snd_soc_update_bits(codec, WCD9XXX_HPH_PA_CTL1, + 0x0E, 0x02); + } else { + + snd_soc_update_bits(codec, WCD9XXX_HPH_NEW_INT_PA_MISC2, + 0x20, 0x0); + snd_soc_update_bits(codec, WCD9XXX_RX_BIAS_HPH_LOWPOWER, + 0xF0, 0x80); + snd_soc_update_bits(codec, WCD9XXX_HPH_PA_CTL1, + 0x0E, 0x06); + } +} + +static void wcd_clsh_buck_ctrl(struct snd_soc_codec *codec, + struct wcd_clsh_cdc_data *clsh_d, + int mode, + bool enable) +{ + /* enable/disable buck */ + if ((enable && (++clsh_d->buck_users == 1)) || + (!enable && (--clsh_d->buck_users == 0))) + snd_soc_update_bits(codec, WCD9XXX_A_ANA_RX_SUPPLIES, + (1 << 7), (enable << 7)); + dev_dbg(codec->dev, "%s: buck_users %d, enable %d, mode: %s", + __func__, clsh_d->buck_users, enable, mode_to_str(mode)); + /* + * 500us sleep is required after buck enable/disable + * as per HW requirement + */ + usleep_range(500, 500 + WCD_USLEEP_RANGE); +} + +static void wcd_clsh_flyback_ctrl(struct snd_soc_codec *codec, + struct wcd_clsh_cdc_data *clsh_d, + int mode, + bool enable) +{ + struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent); + struct wcd9xxx_reg_val bulk_reg[2]; + u8 vneg[] = {0x00, 0x40}; + + /* enable/disable flyback */ + if ((enable && (++clsh_d->flyback_users == 1)) || + (!enable && (--clsh_d->flyback_users == 0))) { + snd_soc_update_bits(codec, WCD9XXX_A_ANA_RX_SUPPLIES, + (1 << 6), (enable << 6)); + /* 100usec delay is needed as per HW requirement */ + usleep_range(100, 110); + if (enable && (TASHA_IS_1_1(wcd9xxx))) { + wcd_clsh_set_flyback_mode(codec, CLS_H_HIFI); + snd_soc_update_bits(codec, WCD9XXX_FLYBACK_EN, + 0x60, 0x40); + snd_soc_update_bits(codec, WCD9XXX_FLYBACK_EN, + 0x10, 0x10); + vneg[0] = snd_soc_read(codec, + WCD9XXX_A_ANA_RX_SUPPLIES); + vneg[0] &= ~(0x40); + vneg[1] = vneg[0] | 0x40; + bulk_reg[0].reg = WCD9XXX_A_ANA_RX_SUPPLIES; + bulk_reg[0].buf = &vneg[0]; + bulk_reg[0].bytes = 1; + bulk_reg[1].reg = WCD9XXX_A_ANA_RX_SUPPLIES; + bulk_reg[1].buf = &vneg[1]; + bulk_reg[1].bytes = 1; + /* 500usec delay is needed as per HW requirement */ + usleep_range(500, 510); + wcd9xxx_slim_bulk_write(wcd9xxx, bulk_reg, 2, + false); + snd_soc_update_bits(codec, WCD9XXX_FLYBACK_EN, + 0x10, 0x00); + wcd_clsh_set_flyback_mode(codec, mode); + } + + } + dev_dbg(codec->dev, "%s: flyback_users %d, enable %d, mode: %s", + __func__, clsh_d->flyback_users, enable, mode_to_str(mode)); + /* + * 500us sleep is required after flyback enable/disable + * as per HW requirement + */ + usleep_range(500, 500 + WCD_USLEEP_RANGE); +} + +static void wcd_clsh_set_gain_path(struct snd_soc_codec *codec, + int mode) +{ + u8 val = 0; + struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent); + + if (!TASHA_IS_2_0(wcd9xxx)) + return; + + switch (mode) { + case CLS_H_NORMAL: + case CLS_AB: + val = 0x00; + break; + case CLS_H_HIFI: + val = 0x02; + break; + case CLS_H_LP: + val = 0x01; + break; + default: + return; + }; + snd_soc_update_bits(codec, WCD9XXX_HPH_L_EN, 0xC0, (val << 6)); + snd_soc_update_bits(codec, WCD9XXX_HPH_R_EN, 0xC0, (val << 6)); +} + +static void wcd_clsh_set_hph_mode(struct snd_soc_codec *codec, + int mode) +{ + u8 val = 0; + u8 gain = 0; + u8 res_val = VREF_FILT_R_0OHM; + u8 ipeak = DELTA_I_50MA; + + struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent); + + switch (mode) { + case CLS_H_NORMAL: + res_val = VREF_FILT_R_50KOHM; + val = 0x00; + gain = DAC_GAIN_0DB; + ipeak = DELTA_I_50MA; + break; + case CLS_AB: + val = 0x00; + gain = DAC_GAIN_0DB; + ipeak = DELTA_I_50MA; + break; + case CLS_AB_HIFI: + val = 0x08; + break; + case CLS_H_HIFI: + val = 0x08; + gain = DAC_GAIN_M0P2DB; + ipeak = DELTA_I_50MA; + break; + case CLS_H_LOHIFI: + val = 0x00; + if ((IS_CODEC_TYPE(wcd9xxx, WCD9335)) || + (IS_CODEC_TYPE(wcd9xxx, WCD9326))) { + val = 0x08; + gain = DAC_GAIN_M0P2DB; + ipeak = DELTA_I_50MA; + } + break; + case CLS_H_ULP: + val = 0x0C; + break; + case CLS_H_LP: + val = 0x04; + ipeak = DELTA_I_30MA; + break; + default: + return; + }; + + /* + * For tavil set mode to Lower_power for + * CLS_H_LOHIFI and CLS_AB + */ + if ((IS_CODEC_TYPE(wcd9xxx, WCD934X)) && + (mode == CLS_H_LOHIFI || mode == CLS_AB)) + val = 0x04; + + snd_soc_update_bits(codec, WCD9XXX_A_ANA_HPH, 0x0C, val); + if (TASHA_IS_2_0(wcd9xxx)) { + snd_soc_update_bits(codec, WCD9XXX_CLASSH_CTRL_VCL_2, + 0x30, (res_val << 4)); + if (mode != CLS_H_LP) + snd_soc_update_bits(codec, WCD9XXX_HPH_REFBUFF_UHQA_CTL, + 0x07, gain); + snd_soc_update_bits(codec, WCD9XXX_CLASSH_CTRL_CCL_1, + 0xF0, (ipeak << 4)); + } +} + +static void wcd_clsh_set_flyback_vneg_ctl(struct snd_soc_codec *codec, + bool enable) +{ + struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent); + + if (!TASHA_IS_2_0(wcd9xxx)) + return; + + if (enable) { + snd_soc_update_bits(codec, WCD9XXX_FLYBACK_VNEG_CTRL_1, 0xE0, + 0x00); + snd_soc_update_bits(codec, WCD9XXX_FLYBACK_VNEGDAC_CTRL_2, + 0xE0, (0x07 << 5)); + } else { + snd_soc_update_bits(codec, WCD9XXX_FLYBACK_VNEG_CTRL_1, 0xE0, + (0x07 << 5)); + snd_soc_update_bits(codec, WCD9XXX_FLYBACK_VNEGDAC_CTRL_2, + 0xE0, (0x02 << 5)); + } +} + +static void wcd_clsh_set_flyback_current(struct snd_soc_codec *codec, int mode) +{ + struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent); + + if (!TASHA_IS_2_0(wcd9xxx)) + return; + + snd_soc_update_bits(codec, WCD9XXX_RX_BIAS_FLYB_BUFF, 0x0F, 0x0A); + snd_soc_update_bits(codec, WCD9XXX_RX_BIAS_FLYB_BUFF, 0xF0, 0xA0); + /* Sleep needed to avoid click and pop as per HW requirement */ + usleep_range(100, 110); +} + +static void wcd_clsh_set_buck_regulator_mode(struct snd_soc_codec *codec, + int mode) +{ + snd_soc_update_bits(codec, WCD9XXX_A_ANA_RX_SUPPLIES, + 0x02, 0x00); +} + +static void wcd_clsh_state_lo(struct snd_soc_codec *codec, + struct wcd_clsh_cdc_data *clsh_d, + u8 req_state, bool is_enable, int mode) +{ + dev_dbg(codec->dev, "%s: mode: %s, %s\n", __func__, mode_to_str(mode), + is_enable ? "enable" : "disable"); + + if (mode != CLS_AB && mode != CLS_AB_HIFI) { + dev_err(codec->dev, "%s: LO cannot be in this mode: %d\n", + __func__, mode); + return; + } + + if (is_enable) { + wcd_clsh_set_buck_regulator_mode(codec, mode); + wcd_clsh_set_flyback_vneg_ctl(codec, true); + wcd_clsh_set_buck_mode(codec, mode); + wcd_clsh_set_flyback_mode(codec, mode); + wcd_clsh_flyback_ctrl(codec, clsh_d, mode, true); + wcd_clsh_set_flyback_current(codec, mode); + wcd_clsh_buck_ctrl(codec, clsh_d, mode, true); + } else { + wcd_clsh_buck_ctrl(codec, clsh_d, mode, false); + wcd_clsh_flyback_ctrl(codec, clsh_d, mode, false); + wcd_clsh_set_flyback_mode(codec, CLS_H_NORMAL); + wcd_clsh_set_buck_mode(codec, CLS_H_NORMAL); + wcd_clsh_set_flyback_vneg_ctl(codec, false); + wcd_clsh_set_buck_regulator_mode(codec, CLS_H_NORMAL); + } +} + +static void wcd_clsh_state_hph_ear(struct snd_soc_codec *codec, + struct wcd_clsh_cdc_data *clsh_d, + u8 req_state, bool is_enable, int mode) +{ + int hph_mode = 0; + + dev_dbg(codec->dev, "%s: mode: %s, %s\n", __func__, mode_to_str(mode), + is_enable ? "enable" : "disable"); + + if (is_enable) { + if (req_state == WCD_CLSH_STATE_EAR) { + /* If HPH is running in CLS-AB when + * EAR comes, let it continue to run + * in Class-AB, no need to enable Class-H + * for EAR. + */ + if (clsh_d->state & WCD_CLSH_STATE_HPHL) + hph_mode = wcd_clsh_get_int_mode(clsh_d, + WCD_CLSH_STATE_HPHL); + else if (clsh_d->state & WCD_CLSH_STATE_HPHR) + hph_mode = wcd_clsh_get_int_mode(clsh_d, + WCD_CLSH_STATE_HPHR); + else + return; + if (hph_mode != CLS_AB && hph_mode != CLS_AB_HIFI + && !is_native_44_1_active(codec)) + snd_soc_update_bits(codec, + WCD9XXX_A_CDC_RX0_RX_PATH_CFG0, + 0x40, 0x40); + } + + if (is_native_44_1_active(codec)) { + snd_soc_write(codec, WCD9XXX_CDC_CLSH_HPH_V_PA, 0x39); + snd_soc_update_bits(codec, + WCD9XXX_CDC_RX0_RX_PATH_SEC0, + 0x03, 0x00); + if ((req_state == WCD_CLSH_STATE_HPHL) || + (req_state == WCD_CLSH_STATE_HPHR)) + snd_soc_update_bits(codec, + WCD9XXX_A_CDC_RX0_RX_PATH_CFG0, + 0x40, 0x00); + } + + if (req_state == WCD_CLSH_STATE_HPHL) + snd_soc_update_bits(codec, + WCD9XXX_A_CDC_RX1_RX_PATH_CFG0, + 0x40, 0x40); + if (req_state == WCD_CLSH_STATE_HPHR) + snd_soc_update_bits(codec, + WCD9XXX_A_CDC_RX2_RX_PATH_CFG0, + 0x40, 0x40); + if ((req_state == WCD_CLSH_STATE_HPHL) || + (req_state == WCD_CLSH_STATE_HPHR)) { + wcd_clsh_set_gain_path(codec, mode); + wcd_clsh_set_flyback_mode(codec, mode); + wcd_clsh_set_buck_mode(codec, mode); + } + } else { + if (req_state == WCD_CLSH_STATE_EAR) { + /* + * If EAR goes away, disable EAR Channel Enable + * if HPH running in Class-H otherwise + * and if HPH requested mode is CLS_AB then + * no need to disable EAR channel enable bit. + */ + if (wcd_clsh_enable_status(codec)) + snd_soc_update_bits(codec, + WCD9XXX_A_CDC_RX0_RX_PATH_CFG0, + 0x40, 0x00); + } + + if (is_native_44_1_active(codec)) { + snd_soc_write(codec, WCD9XXX_CDC_CLSH_HPH_V_PA, 0x1C); + snd_soc_update_bits(codec, + WCD9XXX_CDC_RX0_RX_PATH_SEC0, + 0x03, 0x01); + if (((clsh_d->state & WCD_CLSH_STATE_HPH_ST) + != WCD_CLSH_STATE_HPH_ST) && + ((req_state == WCD_CLSH_STATE_HPHL) || + (req_state == WCD_CLSH_STATE_HPHR))) + snd_soc_update_bits(codec, + WCD9XXX_A_CDC_RX0_RX_PATH_CFG0, + 0x40, 0x40); + } + + if (req_state == WCD_CLSH_STATE_HPHL) + snd_soc_update_bits(codec, + WCD9XXX_A_CDC_RX1_RX_PATH_CFG0, + 0x40, 0x00); + if (req_state == WCD_CLSH_STATE_HPHR) + snd_soc_update_bits(codec, + WCD9XXX_A_CDC_RX2_RX_PATH_CFG0, + 0x40, 0x00); + if ((req_state & WCD_CLSH_STATE_HPH_ST) && + !wcd_clsh_enable_status(codec)) { + /* If Class-H is not enabled when HPH is turned + * off, enable it as EAR is in progress + */ + wcd_enable_clsh_block(codec, clsh_d, true); + snd_soc_update_bits(codec, + WCD9XXX_A_CDC_RX0_RX_PATH_CFG0, + 0x40, 0x40); + wcd_clsh_set_flyback_mode(codec, CLS_H_NORMAL); + wcd_clsh_set_buck_mode(codec, CLS_H_NORMAL); + } + } +} + +static void wcd_clsh_state_ear_lo(struct snd_soc_codec *codec, + struct wcd_clsh_cdc_data *clsh_d, + u8 req_state, bool is_enable, int mode) +{ + dev_dbg(codec->dev, "%s: mode: %s, %s\n", __func__, mode_to_str(mode), + is_enable ? "enable" : "disable"); + + if (is_enable) { + /* LO powerup is taken care in PA sequence. + * No need to change to class AB here. + */ + if (req_state == WCD_CLSH_STATE_EAR) { + /* EAR powerup.*/ + if (!wcd_clsh_enable_status(codec)) { + wcd_enable_clsh_block(codec, clsh_d, true); + wcd_clsh_set_buck_mode(codec, mode); + wcd_clsh_set_flyback_mode(codec, mode); + } + snd_soc_update_bits(codec, + WCD9XXX_A_CDC_RX0_RX_PATH_CFG0, + 0x40, 0x40); + } + } else { + if (req_state == WCD_CLSH_STATE_EAR) { + /* EAR powerdown.*/ + wcd_enable_clsh_block(codec, clsh_d, false); + wcd_clsh_set_buck_mode(codec, CLS_H_NORMAL); + wcd_clsh_set_flyback_mode(codec, CLS_H_NORMAL); + snd_soc_update_bits(codec, + WCD9XXX_A_CDC_RX0_RX_PATH_CFG0, + 0x40, 0x00); + } + /* LO powerdown is taken care in PA sequence. + * No need to change to class H here. + */ + } +} + +static void wcd_clsh_state_hph_lo(struct snd_soc_codec *codec, + struct wcd_clsh_cdc_data *clsh_d, + u8 req_state, bool is_enable, int mode) +{ + int hph_mode = 0; + + dev_dbg(codec->dev, "%s: mode: %s, %s\n", __func__, mode_to_str(mode), + is_enable ? "enable" : "disable"); + + if (is_enable) { + /* + * If requested state is LO, put regulator + * in class-AB or if requested state is HPH, + * which means LO is already enabled, keep + * the regulator config the same at class-AB + * and just set the power modes for flyback + * and buck. + */ + if (req_state == WCD_CLSH_STATE_LO) + wcd_clsh_set_buck_regulator_mode(codec, CLS_AB); + else { + if (!wcd_clsh_enable_status(codec)) { + wcd_enable_clsh_block(codec, clsh_d, true); + snd_soc_update_bits(codec, + WCD9XXX_A_CDC_CLSH_K1_MSB, + 0x0F, 0x00); + snd_soc_update_bits(codec, + WCD9XXX_A_CDC_CLSH_K1_LSB, + 0xFF, 0xC0); + wcd_clsh_set_flyback_mode(codec, mode); + wcd_clsh_set_flyback_vneg_ctl(codec, false); + wcd_clsh_set_buck_mode(codec, mode); + wcd_clsh_set_hph_mode(codec, mode); + wcd_clsh_set_gain_path(codec, mode); + } else { + dev_dbg(codec->dev, "%s:clsh is already enabled\n", + __func__); + } + if (req_state == WCD_CLSH_STATE_HPHL) + snd_soc_update_bits(codec, + WCD9XXX_A_CDC_RX1_RX_PATH_CFG0, + 0x40, 0x40); + if (req_state == WCD_CLSH_STATE_HPHR) + snd_soc_update_bits(codec, + WCD9XXX_A_CDC_RX2_RX_PATH_CFG0, + 0x40, 0x40); + } + } else { + if ((req_state == WCD_CLSH_STATE_HPHL) || + (req_state == WCD_CLSH_STATE_HPHR)) { + if (req_state == WCD_CLSH_STATE_HPHL) + snd_soc_update_bits(codec, + WCD9XXX_A_CDC_RX1_RX_PATH_CFG0, + 0x40, 0x00); + if (req_state == WCD_CLSH_STATE_HPHR) + snd_soc_update_bits(codec, + WCD9XXX_A_CDC_RX2_RX_PATH_CFG0, + 0x40, 0x00); + /* + * If HPH is powering down first, then disable clsh, + * set the buck/flyback mode to default and keep the + * regulator at Class-AB + */ + if ((clsh_d->state & WCD_CLSH_STATE_HPH_ST) + != WCD_CLSH_STATE_HPH_ST) { + wcd_enable_clsh_block(codec, clsh_d, false); + wcd_clsh_set_flyback_vneg_ctl(codec, true); + wcd_clsh_set_flyback_mode(codec, CLS_H_NORMAL); + wcd_clsh_set_buck_mode(codec, CLS_H_NORMAL); + } + } else { + /* LO powerdown. + * If HPH mode also is CLS-AB, no need + * to turn-on class-H, otherwise enable + * Class-H configuration. + */ + if (clsh_d->state & WCD_CLSH_STATE_HPHL) + hph_mode = wcd_clsh_get_int_mode(clsh_d, + WCD_CLSH_STATE_HPHL); + else if (clsh_d->state & WCD_CLSH_STATE_HPHR) + hph_mode = wcd_clsh_get_int_mode(clsh_d, + WCD_CLSH_STATE_HPHR); + else + return; + dev_dbg(codec->dev, "%s: hph_mode = %d\n", __func__, + hph_mode); + + if ((hph_mode == CLS_AB) || + (hph_mode == CLS_AB_HIFI) || + (hph_mode == CLS_NONE)) + goto end; + + /* + * If Class-H is already enabled (HPH ON and then + * LO ON), no need to turn on again, just set the + * regulator mode. + */ + if (wcd_clsh_enable_status(codec)) { + wcd_clsh_set_buck_regulator_mode(codec, + hph_mode); + goto end; + } else { + dev_dbg(codec->dev, "%s: clsh is not enabled\n", + __func__); + } + + wcd_enable_clsh_block(codec, clsh_d, true); + snd_soc_update_bits(codec, + WCD9XXX_A_CDC_CLSH_K1_MSB, + 0x0F, 0x00); + snd_soc_update_bits(codec, + WCD9XXX_A_CDC_CLSH_K1_LSB, + 0xFF, 0xC0); + wcd_clsh_set_buck_regulator_mode(codec, + hph_mode); + if (clsh_d->state & WCD_CLSH_STATE_HPHL) + snd_soc_update_bits(codec, + WCD9XXX_A_CDC_RX1_RX_PATH_CFG0, + 0x40, 0x40); + if (clsh_d->state & WCD_CLSH_STATE_HPHR) + snd_soc_update_bits(codec, + WCD9XXX_A_CDC_RX2_RX_PATH_CFG0, + 0x40, 0x40); + wcd_clsh_set_hph_mode(codec, hph_mode); + } + } +end: + return; +} + +static void wcd_clsh_state_hph_st(struct snd_soc_codec *codec, + struct wcd_clsh_cdc_data *clsh_d, + u8 req_state, bool is_enable, int mode) +{ + dev_dbg(codec->dev, "%s: mode: %s, %s\n", __func__, mode_to_str(mode), + is_enable ? "enable" : "disable"); + + if (mode == CLS_AB || mode == CLS_AB_HIFI) + return; + + if (is_enable) { + if (req_state == WCD_CLSH_STATE_HPHL) + snd_soc_update_bits(codec, + WCD9XXX_A_CDC_RX1_RX_PATH_CFG0, + 0x40, 0x40); + if (req_state == WCD_CLSH_STATE_HPHR) + snd_soc_update_bits(codec, + WCD9XXX_A_CDC_RX2_RX_PATH_CFG0, + 0x40, 0x40); + } else { + if (req_state == WCD_CLSH_STATE_HPHL) + snd_soc_update_bits(codec, + WCD9XXX_A_CDC_RX1_RX_PATH_CFG0, + 0x40, 0x00); + if (req_state == WCD_CLSH_STATE_HPHR) + snd_soc_update_bits(codec, + WCD9XXX_A_CDC_RX2_RX_PATH_CFG0, + 0x40, 0x00); + } +} + +static void wcd_clsh_state_hph_r(struct snd_soc_codec *codec, + struct wcd_clsh_cdc_data *clsh_d, + u8 req_state, bool is_enable, int mode) +{ + dev_dbg(codec->dev, "%s: mode: %s, %s\n", __func__, mode_to_str(mode), + is_enable ? "enable" : "disable"); + + if (mode == CLS_H_NORMAL) { + dev_err(codec->dev, "%s: Normal mode not applicable for hph_r\n", + __func__); + return; + } + + if (is_enable) { + if (mode != CLS_AB && mode != CLS_AB_HIFI) { + wcd_enable_clsh_block(codec, clsh_d, true); + /* + * These K1 values depend on the Headphone Impedance + * For now it is assumed to be 16 ohm + */ + snd_soc_update_bits(codec, WCD9XXX_A_CDC_CLSH_K1_MSB, + 0x0F, 0x00); + snd_soc_update_bits(codec, WCD9XXX_A_CDC_CLSH_K1_LSB, + 0xFF, 0xC0); + snd_soc_update_bits(codec, + WCD9XXX_A_CDC_RX2_RX_PATH_CFG0, + 0x40, 0x40); + } + wcd_clsh_set_buck_regulator_mode(codec, mode); + wcd_clsh_set_flyback_mode(codec, mode); + wcd_clsh_gm3_boost_disable(codec, mode); + wcd_clsh_force_iq_ctl(codec, mode); + wcd_clsh_flyback_ctrl(codec, clsh_d, mode, true); + wcd_clsh_set_flyback_current(codec, mode); + wcd_clsh_set_buck_mode(codec, mode); + wcd_clsh_buck_ctrl(codec, clsh_d, mode, true); + wcd_clsh_set_hph_mode(codec, mode); + wcd_clsh_set_gain_path(codec, mode); + } else { + wcd_clsh_set_hph_mode(codec, CLS_H_NORMAL); + + if (mode != CLS_AB && mode != CLS_AB_HIFI) { + snd_soc_update_bits(codec, + WCD9XXX_A_CDC_RX2_RX_PATH_CFG0, + 0x40, 0x00); + wcd_enable_clsh_block(codec, clsh_d, false); + } + /* buck and flyback set to default mode and disable */ + wcd_clsh_buck_ctrl(codec, clsh_d, CLS_H_NORMAL, false); + wcd_clsh_flyback_ctrl(codec, clsh_d, CLS_H_NORMAL, false); + wcd_clsh_force_iq_ctl(codec, CLS_H_NORMAL); + wcd_clsh_gm3_boost_disable(codec, CLS_H_NORMAL); + wcd_clsh_set_flyback_mode(codec, CLS_H_NORMAL); + wcd_clsh_set_buck_mode(codec, CLS_H_NORMAL); + wcd_clsh_set_buck_regulator_mode(codec, CLS_H_NORMAL); + } +} + +static void wcd_clsh_state_hph_l(struct snd_soc_codec *codec, + struct wcd_clsh_cdc_data *clsh_d, + u8 req_state, bool is_enable, int mode) +{ + dev_dbg(codec->dev, "%s: mode: %s, %s\n", __func__, mode_to_str(mode), + is_enable ? "enable" : "disable"); + + if (mode == CLS_H_NORMAL) { + dev_err(codec->dev, "%s: Normal mode not applicable for hph_l\n", + __func__); + return; + } + + if (is_enable) { + if (mode != CLS_AB && mode != CLS_AB_HIFI) { + wcd_enable_clsh_block(codec, clsh_d, true); + /* + * These K1 values depend on the Headphone Impedance + * For now it is assumed to be 16 ohm + */ + snd_soc_update_bits(codec, WCD9XXX_A_CDC_CLSH_K1_MSB, + 0x0F, 0x00); + snd_soc_update_bits(codec, WCD9XXX_A_CDC_CLSH_K1_LSB, + 0xFF, 0xC0); + snd_soc_update_bits(codec, + WCD9XXX_A_CDC_RX1_RX_PATH_CFG0, + 0x40, 0x40); + } + wcd_clsh_set_buck_regulator_mode(codec, mode); + wcd_clsh_set_flyback_mode(codec, mode); + wcd_clsh_gm3_boost_disable(codec, mode); + wcd_clsh_force_iq_ctl(codec, mode); + wcd_clsh_flyback_ctrl(codec, clsh_d, mode, true); + wcd_clsh_set_flyback_current(codec, mode); + wcd_clsh_set_buck_mode(codec, mode); + wcd_clsh_buck_ctrl(codec, clsh_d, mode, true); + wcd_clsh_set_hph_mode(codec, mode); + wcd_clsh_set_gain_path(codec, mode); + } else { + wcd_clsh_set_hph_mode(codec, CLS_H_NORMAL); + + if (mode != CLS_AB && mode != CLS_AB_HIFI) { + snd_soc_update_bits(codec, + WCD9XXX_A_CDC_RX1_RX_PATH_CFG0, + 0x40, 0x00); + wcd_enable_clsh_block(codec, clsh_d, false); + } + /* set buck and flyback to Default Mode */ + wcd_clsh_buck_ctrl(codec, clsh_d, CLS_H_NORMAL, false); + wcd_clsh_flyback_ctrl(codec, clsh_d, CLS_H_NORMAL, false); + wcd_clsh_force_iq_ctl(codec, CLS_H_NORMAL); + wcd_clsh_gm3_boost_disable(codec, CLS_H_NORMAL); + wcd_clsh_set_flyback_mode(codec, CLS_H_NORMAL); + wcd_clsh_set_buck_mode(codec, CLS_H_NORMAL); + wcd_clsh_set_buck_regulator_mode(codec, CLS_H_NORMAL); + } +} + +static void wcd_clsh_state_ear(struct snd_soc_codec *codec, + struct wcd_clsh_cdc_data *clsh_d, + u8 req_state, bool is_enable, int mode) +{ + dev_dbg(codec->dev, "%s: mode: %s, %s\n", __func__, mode_to_str(mode), + is_enable ? "enable" : "disable"); + + if (mode != CLS_H_NORMAL) { + dev_err(codec->dev, "%s: mode: %s cannot be used for EAR\n", + __func__, mode_to_str(mode)); + return; + } + + if (is_enable) { + wcd_enable_clsh_block(codec, clsh_d, true); + snd_soc_update_bits(codec, + WCD9XXX_A_CDC_RX0_RX_PATH_CFG0, + 0x40, 0x40); + wcd_clsh_set_buck_mode(codec, mode); + wcd_clsh_set_flyback_mode(codec, mode); + wcd_clsh_flyback_ctrl(codec, clsh_d, mode, true); + wcd_clsh_set_flyback_current(codec, mode); + wcd_clsh_buck_ctrl(codec, clsh_d, mode, true); + } else { + snd_soc_update_bits(codec, + WCD9XXX_A_CDC_RX0_RX_PATH_CFG0, + 0x40, 0x00); + wcd_enable_clsh_block(codec, clsh_d, false); + wcd_clsh_buck_ctrl(codec, clsh_d, mode, false); + wcd_clsh_flyback_ctrl(codec, clsh_d, mode, false); + wcd_clsh_set_flyback_mode(codec, CLS_H_NORMAL); + wcd_clsh_set_buck_mode(codec, CLS_H_NORMAL); + } +} + +static void wcd_clsh_state_err(struct snd_soc_codec *codec, + struct wcd_clsh_cdc_data *clsh_d, + u8 req_state, bool is_enable, int mode) +{ + char msg[128]; + + dev_err(codec->dev, + "%s Wrong request for class H state machine requested to %s %s", + __func__, is_enable ? "enable" : "disable", + state_to_str(req_state, msg, sizeof(msg))); + WARN_ON(1); +} + +/* + * Function: wcd_clsh_is_state_valid + * Params: state + * Description: + * Provides information on valid states of Class H configuration + */ +static bool wcd_clsh_is_state_valid(u8 state) +{ + switch (state) { + case WCD_CLSH_STATE_IDLE: + case WCD_CLSH_STATE_EAR: + case WCD_CLSH_STATE_HPHL: + case WCD_CLSH_STATE_HPHR: + case WCD_CLSH_STATE_HPH_ST: + case WCD_CLSH_STATE_LO: + case WCD_CLSH_STATE_HPHL_EAR: + case WCD_CLSH_STATE_HPHR_EAR: + case WCD_CLSH_STATE_HPH_ST_EAR: + case WCD_CLSH_STATE_HPHL_LO: + case WCD_CLSH_STATE_HPHR_LO: + case WCD_CLSH_STATE_HPH_ST_LO: + case WCD_CLSH_STATE_EAR_LO: + return true; + default: + return false; + }; +} + +/* + * Function: wcd_clsh_fsm + * Params: codec, cdc_clsh_d, req_state, req_type, clsh_event + * Description: + * This function handles PRE DAC and POST DAC conditions of different devices + * and updates class H configuration of different combination of devices + * based on validity of their states. cdc_clsh_d will contain current + * class h state information + */ +void wcd_clsh_fsm(struct snd_soc_codec *codec, + struct wcd_clsh_cdc_data *cdc_clsh_d, + u8 clsh_event, u8 req_state, + int int_mode) +{ + u8 old_state, new_state; + char msg0[128], msg1[128]; + + switch (clsh_event) { + case WCD_CLSH_EVENT_PRE_DAC: + old_state = cdc_clsh_d->state; + new_state = old_state | req_state; + + if (!wcd_clsh_is_state_valid(new_state)) { + dev_err(codec->dev, + "%s: Class-H not a valid new state: %s\n", + __func__, + state_to_str(new_state, msg0, sizeof(msg0))); + return; + } + if (new_state == old_state) { + dev_err(codec->dev, + "%s: Class-H already in requested state: %s\n", + __func__, + state_to_str(new_state, msg0, sizeof(msg0))); + return; + } + cdc_clsh_d->state = new_state; + wcd_clsh_set_int_mode(cdc_clsh_d, req_state, int_mode); + (*clsh_state_fp[new_state]) (codec, cdc_clsh_d, req_state, + CLSH_REQ_ENABLE, int_mode); + dev_dbg(codec->dev, + "%s: ClassH state transition from %s to %s\n", + __func__, state_to_str(old_state, msg0, sizeof(msg0)), + state_to_str(cdc_clsh_d->state, msg1, sizeof(msg1))); + break; + case WCD_CLSH_EVENT_POST_PA: + old_state = cdc_clsh_d->state; + new_state = old_state & (~req_state); + if (new_state < NUM_CLSH_STATES_V2) { + if (!wcd_clsh_is_state_valid(old_state)) { + dev_err(codec->dev, + "%s:Invalid old state:%s\n", + __func__, + state_to_str(old_state, msg0, + sizeof(msg0))); + return; + } + if (new_state == old_state) { + dev_err(codec->dev, + "%s: Class-H already in requested state: %s\n", + __func__, + state_to_str(new_state, msg0, + sizeof(msg0))); + return; + } + (*clsh_state_fp[old_state]) (codec, cdc_clsh_d, + req_state, CLSH_REQ_DISABLE, + int_mode); + cdc_clsh_d->state = new_state; + wcd_clsh_set_int_mode(cdc_clsh_d, req_state, CLS_NONE); + dev_dbg(codec->dev, "%s: ClassH state transition from %s to %s\n", + __func__, state_to_str(old_state, msg0, + sizeof(msg0)), + state_to_str(cdc_clsh_d->state, msg1, + sizeof(msg1))); + } + break; + }; +} +EXPORT_SYMBOL(wcd_clsh_fsm); + +int wcd_clsh_get_clsh_state(struct wcd_clsh_cdc_data *clsh) +{ + return clsh->state; +} +EXPORT_SYMBOL(wcd_clsh_get_clsh_state); + +void wcd_clsh_init(struct wcd_clsh_cdc_data *clsh) +{ + int i; + + clsh->state = WCD_CLSH_STATE_IDLE; + + for (i = 0; i < NUM_CLSH_STATES_V2; i++) + clsh_state_fp[i] = wcd_clsh_state_err; + + clsh_state_fp[WCD_CLSH_STATE_EAR] = wcd_clsh_state_ear; + clsh_state_fp[WCD_CLSH_STATE_HPHL] = + wcd_clsh_state_hph_l; + clsh_state_fp[WCD_CLSH_STATE_HPHR] = + wcd_clsh_state_hph_r; + clsh_state_fp[WCD_CLSH_STATE_HPH_ST] = + wcd_clsh_state_hph_st; + clsh_state_fp[WCD_CLSH_STATE_LO] = wcd_clsh_state_lo; + clsh_state_fp[WCD_CLSH_STATE_HPHL_EAR] = + wcd_clsh_state_hph_ear; + clsh_state_fp[WCD_CLSH_STATE_HPHR_EAR] = + wcd_clsh_state_hph_ear; + clsh_state_fp[WCD_CLSH_STATE_HPH_ST_EAR] = + wcd_clsh_state_hph_ear; + clsh_state_fp[WCD_CLSH_STATE_HPHL_LO] = wcd_clsh_state_hph_lo; + clsh_state_fp[WCD_CLSH_STATE_HPHR_LO] = wcd_clsh_state_hph_lo; + clsh_state_fp[WCD_CLSH_STATE_HPH_ST_LO] = + wcd_clsh_state_hph_lo; + clsh_state_fp[WCD_CLSH_STATE_EAR_LO] = wcd_clsh_state_ear_lo; + /* Set interpolaotr modes to NONE */ + wcd_clsh_set_int_mode(clsh, WCD_CLSH_STATE_EAR, CLS_NONE); + wcd_clsh_set_int_mode(clsh, WCD_CLSH_STATE_HPHL, CLS_NONE); + wcd_clsh_set_int_mode(clsh, WCD_CLSH_STATE_HPHR, CLS_NONE); + wcd_clsh_set_int_mode(clsh, WCD_CLSH_STATE_LO, CLS_NONE); + clsh->flyback_users = 0; + clsh->buck_users = 0; + clsh->clsh_users = 0; +} +EXPORT_SYMBOL(wcd_clsh_init); + +MODULE_DESCRIPTION("WCD9XXX Common Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/audio-kernel/asoc/codecs/wcd9xxx-common-v2.h b/audio-kernel/asoc/codecs/wcd9xxx-common-v2.h new file mode 100644 index 000000000..53c9a84b5 --- /dev/null +++ b/audio-kernel/asoc/codecs/wcd9xxx-common-v2.h @@ -0,0 +1,236 @@ +/* + * 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 + * 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 _WCD9XXX_COMMON_V2 + +#define _WCD9XXX_COMMON_V2 + +#define CLSH_REQ_ENABLE true +#define CLSH_REQ_DISABLE false + +#define WCD_CLSH_EVENT_PRE_DAC 0x01 +#define WCD_CLSH_EVENT_POST_PA 0x02 +#define MAX_VBAT_MONITOR_WRITES 17 +/* + * Basic states for Class H state machine. + * represented as a bit mask within a u8 data type + * bit 0: EAR mode + * bit 1: HPH Left mode + * bit 2: HPH Right mode + * bit 3: Lineout mode + */ +#define WCD_CLSH_STATE_IDLE 0x00 +#define WCD_CLSH_STATE_EAR (0x01 << 0) +#define WCD_CLSH_STATE_HPHL (0x01 << 1) +#define WCD_CLSH_STATE_HPHR (0x01 << 2) +#define WCD_CLSH_STATE_LO (0x01 << 3) + +/* + * Though number of CLSH states are 4, max state shoulbe be 5 + * because state array index starts from 1. + */ +#define WCD_CLSH_STATE_MAX 5 +#define NUM_CLSH_STATES_V2 (0x01 << WCD_CLSH_STATE_MAX) + + +/* Derived State: Bits 1 and 2 should be set for Headphone stereo */ +#define WCD_CLSH_STATE_HPH_ST (WCD_CLSH_STATE_HPHL | \ + WCD_CLSH_STATE_HPHR) + +#define WCD_CLSH_STATE_HPHL_LO (WCD_CLSH_STATE_HPHL | \ + WCD_CLSH_STATE_LO) +#define WCD_CLSH_STATE_HPHR_LO (WCD_CLSH_STATE_HPHR | \ + WCD_CLSH_STATE_LO) +#define WCD_CLSH_STATE_HPH_ST_LO (WCD_CLSH_STATE_HPH_ST | \ + WCD_CLSH_STATE_LO) +#define WCD_CLSH_STATE_EAR_LO (WCD_CLSH_STATE_EAR | \ + WCD_CLSH_STATE_LO) +#define WCD_CLSH_STATE_HPHL_EAR (WCD_CLSH_STATE_HPHL | \ + WCD_CLSH_STATE_EAR) +#define WCD_CLSH_STATE_HPHR_EAR (WCD_CLSH_STATE_HPHR | \ + WCD_CLSH_STATE_EAR) +#define WCD_CLSH_STATE_HPH_ST_EAR (WCD_CLSH_STATE_HPH_ST | \ + WCD_CLSH_STATE_EAR) + +enum { + CLS_H_NORMAL = 0, /* Class-H Default */ + CLS_H_HIFI, /* Class-H HiFi */ + CLS_H_LP, /* Class-H Low Power */ + CLS_AB, /* Class-AB Low HIFI*/ + CLS_H_LOHIFI, /* LoHIFI */ + CLS_H_ULP, /* Ultra Low power */ + CLS_AB_HIFI, /* Class-AB */ + CLS_NONE, /* None of the above modes */ +}; + +/* Class H data that the codec driver will maintain */ +struct wcd_clsh_cdc_data { + u8 state; + int flyback_users; + int buck_users; + int clsh_users; + int interpolator_modes[WCD_CLSH_STATE_MAX]; +}; + +struct wcd_mad_audio_header { + u32 reserved[3]; + u32 num_reg_cfg; +}; + +struct wcd_mad_microphone_info { + uint8_t input_microphone; + uint8_t cycle_time; + uint8_t settle_time; + uint8_t padding; +} __packed; + +struct wcd_mad_micbias_info { + uint8_t micbias; + uint8_t k_factor; + uint8_t external_bypass_capacitor; + uint8_t internal_biasing; + uint8_t cfilter; + uint8_t padding[3]; +} __packed; + +struct wcd_mad_rms_audio_beacon_info { + uint8_t rms_omit_samples; + uint8_t rms_comp_time; + uint8_t detection_mechanism; + uint8_t rms_diff_threshold; + uint8_t rms_threshold_lsb; + uint8_t rms_threshold_msb; + uint8_t padding[2]; + uint8_t iir_coefficients[36]; +} __packed; + +struct wcd_mad_rms_ultrasound_info { + uint8_t rms_comp_time; + uint8_t detection_mechanism; + uint8_t rms_diff_threshold; + uint8_t rms_threshold_lsb; + uint8_t rms_threshold_msb; + uint8_t padding[3]; + uint8_t iir_coefficients[36]; +} __packed; + +struct wcd_mad_audio_cal { + uint32_t version; + struct wcd_mad_microphone_info microphone_info; + struct wcd_mad_micbias_info micbias_info; + struct wcd_mad_rms_audio_beacon_info audio_info; + struct wcd_mad_rms_audio_beacon_info beacon_info; + struct wcd_mad_rms_ultrasound_info ultrasound_info; +} __packed; + +struct wcd9xxx_anc_header { + u32 reserved[3]; + u32 num_anc_slots; +}; + +struct vbat_monitor_reg { + u32 size; + u32 writes[MAX_VBAT_MONITOR_WRITES]; +} __packed; + +struct wcd_reg_mask_val { + u16 reg; + u8 mask; + u8 val; +}; + +extern void wcd_clsh_fsm(struct snd_soc_codec *codec, + struct wcd_clsh_cdc_data *cdc_clsh_d, + u8 clsh_event, u8 req_state, + int int_mode); + +extern void wcd_clsh_init(struct wcd_clsh_cdc_data *clsh); +extern int wcd_clsh_get_clsh_state(struct wcd_clsh_cdc_data *clsh); +extern void wcd_clsh_imped_config(struct snd_soc_codec *codec, int imped, + bool reset); + +enum { + RESERVED = 0, + AANC_LPF_FF_FB = 1, + AANC_LPF_COEFF_MSB, + AANC_LPF_COEFF_LSB, + HW_MAD_AUDIO_ENABLE, + HW_MAD_ULTR_ENABLE, + HW_MAD_BEACON_ENABLE, + HW_MAD_AUDIO_SLEEP_TIME, + HW_MAD_ULTR_SLEEP_TIME, + HW_MAD_BEACON_SLEEP_TIME, + HW_MAD_TX_AUDIO_SWITCH_OFF, + HW_MAD_TX_ULTR_SWITCH_OFF, + HW_MAD_TX_BEACON_SWITCH_OFF, + MAD_AUDIO_INT_DEST_SELECT_REG, + MAD_ULT_INT_DEST_SELECT_REG, + MAD_BEACON_INT_DEST_SELECT_REG, + MAD_CLIP_INT_DEST_SELECT_REG, + VBAT_INT_DEST_SELECT_REG, + MAD_AUDIO_INT_MASK_REG, + MAD_ULT_INT_MASK_REG, + MAD_BEACON_INT_MASK_REG, + MAD_CLIP_INT_MASK_REG, + VBAT_INT_MASK_REG, + MAD_AUDIO_INT_STATUS_REG, + MAD_ULT_INT_STATUS_REG, + MAD_BEACON_INT_STATUS_REG, + MAD_CLIP_INT_STATUS_REG, + VBAT_INT_STATUS_REG, + MAD_AUDIO_INT_CLEAR_REG, + MAD_ULT_INT_CLEAR_REG, + MAD_BEACON_INT_CLEAR_REG, + MAD_CLIP_INT_CLEAR_REG, + VBAT_INT_CLEAR_REG, + SB_PGD_PORT_TX_WATERMARK_N, + SB_PGD_PORT_TX_ENABLE_N, + SB_PGD_PORT_RX_WATERMARK_N, + SB_PGD_PORT_RX_ENABLE_N, + SB_PGD_TX_PORTn_MULTI_CHNL_0, + SB_PGD_TX_PORTn_MULTI_CHNL_1, + SB_PGD_RX_PORTn_MULTI_CHNL_0, + SB_PGD_RX_PORTn_MULTI_CHNL_1, + AANC_FF_GAIN_ADAPTIVE, + AANC_FFGAIN_ADAPTIVE_EN, + AANC_GAIN_CONTROL, + SPKR_CLIP_PIPE_BANK_SEL, + SPKR_CLIPDET_VAL0, + SPKR_CLIPDET_VAL1, + SPKR_CLIPDET_VAL2, + SPKR_CLIPDET_VAL3, + SPKR_CLIPDET_VAL4, + SPKR_CLIPDET_VAL5, + SPKR_CLIPDET_VAL6, + SPKR_CLIPDET_VAL7, + VBAT_RELEASE_INT_DEST_SELECT_REG, + VBAT_RELEASE_INT_MASK_REG, + VBAT_RELEASE_INT_STATUS_REG, + VBAT_RELEASE_INT_CLEAR_REG, + MAD2_CLIP_INT_DEST_SELECT_REG, + MAD2_CLIP_INT_MASK_REG, + MAD2_CLIP_INT_STATUS_REG, + MAD2_CLIP_INT_CLEAR_REG, + SPKR2_CLIP_PIPE_BANK_SEL, + SPKR2_CLIPDET_VAL0, + SPKR2_CLIPDET_VAL1, + SPKR2_CLIPDET_VAL2, + SPKR2_CLIPDET_VAL3, + SPKR2_CLIPDET_VAL4, + SPKR2_CLIPDET_VAL5, + SPKR2_CLIPDET_VAL6, + SPKR2_CLIPDET_VAL7, + MAX_CFG_REGISTERS, +}; + +#endif diff --git a/audio-kernel/asoc/codecs/wcd9xxx-core-init.c b/audio-kernel/asoc/codecs/wcd9xxx-core-init.c new file mode 100644 index 000000000..e575e0d34 --- /dev/null +++ b/audio-kernel/asoc/codecs/wcd9xxx-core-init.c @@ -0,0 +1,55 @@ +/* Copyright (c) 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 + * 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 +#include "msm-cdc-pinctrl.h" +#include "wcd9xxx-irq.h" +#include "core.h" + +#define NUM_DRIVERS_REG_RET 3 + +static int __init wcd9xxx_core_init(void) +{ + int ret[NUM_DRIVERS_REG_RET] = {0}; + int i = 0; + + ret[0] = msm_cdc_pinctrl_drv_init(); + if (ret[0]) + pr_err("%s: Failed init pinctrl drv: %d\n", __func__, ret[0]); + + ret[1] = wcd9xxx_irq_drv_init(); + if (ret[1]) + pr_err("%s: Failed init irq drv: %d\n", __func__, ret[1]); + + ret[2] = wcd9xxx_init(); + if (ret[2]) + pr_err("%s: Failed wcd core drv: %d\n", __func__, ret[2]); + + for (i = 0; i < NUM_DRIVERS_REG_RET; i++) { + if (ret[i]) + return ret[i]; + } + + return 0; +} +module_init(wcd9xxx_core_init); + +static void __exit wcd9xxx_core_exit(void) +{ + wcd9xxx_exit(); + wcd9xxx_irq_drv_exit(); + msm_cdc_pinctrl_drv_exit(); +} +module_exit(wcd9xxx_core_exit); + +MODULE_DESCRIPTION("WCD9XXX CODEC core init driver"); +MODULE_LICENSE("GPL v2"); diff --git a/audio-kernel/asoc/codecs/wcd9xxx-core.c b/audio-kernel/asoc/codecs/wcd9xxx-core.c new file mode 100644 index 000000000..76aea1a62 --- /dev/null +++ b/audio-kernel/asoc/codecs/wcd9xxx-core.c @@ -0,0 +1,1750 @@ +/* Copyright (c) 2011-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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "core.h" +#include "pdata.h" +#include "msm-cdc-pinctrl.h" +#include "msm-cdc-supply.h" +#include "wcd9xxx-irq.h" +#include "wcd9xxx-utils.h" +#include "wcd9xxx-regmap.h" +#include "wcd9xxx-slimslave.h" + +#define WCD9XXX_REGISTER_START_OFFSET 0x800 +#define WCD9XXX_SLIM_RW_MAX_TRIES 3 +#define SLIMBUS_PRESENT_TIMEOUT 100 + +#define MAX_WCD9XXX_DEVICE 4 +#define WCD9XXX_I2C_GSBI_SLAVE_ID "3-000d" +#define WCD9XXX_I2C_TOP_SLAVE_ADDR 0x0d +#define WCD9XXX_ANALOG_I2C_SLAVE_ADDR 0x77 +#define WCD9XXX_DIGITAL1_I2C_SLAVE_ADDR 0x66 +#define WCD9XXX_DIGITAL2_I2C_SLAVE_ADDR 0x55 +#define WCD9XXX_I2C_TOP_LEVEL 0 +#define WCD9XXX_I2C_ANALOG 1 +#define WCD9XXX_I2C_DIGITAL_1 2 +#define WCD9XXX_I2C_DIGITAL_2 3 + +/* + * Number of return values needs to be checked for each + * registration of Slimbus of I2C bus for each codec + */ +#define NUM_WCD9XXX_REG_RET 5 + +#define SLIM_USR_MC_REPEAT_CHANGE_VALUE 0x0 +#define SLIM_REPEAT_WRITE_MAX_SLICE 16 +#define REG_BYTES 2 +#define VAL_BYTES 1 +#define WCD9XXX_PAGE_NUM(reg) (((reg) >> 8) & 0xff) +#define WCD9XXX_PAGE_SIZE 256 + +struct wcd9xxx_i2c { + struct i2c_client *client; + struct i2c_msg xfer_msg[2]; + struct mutex xfer_lock; + int mod_id; +}; + +static struct regmap_config wcd9xxx_base_regmap_config = { + .reg_bits = 16, + .val_bits = 8, + .can_multi_write = true, +}; + +static struct regmap_config wcd9xxx_i2c_base_regmap_config = { + .reg_bits = 16, + .val_bits = 8, + .can_multi_write = false, + .use_single_rw = true, +}; + +static u8 wcd9xxx_pgd_la; +static u8 wcd9xxx_inf_la; + +static const int wcd9xxx_cdc_types[] = { + [WCD9XXX] = WCD9XXX, + [WCD9330] = WCD9330, + [WCD9335] = WCD9335, + [WCD934X] = WCD934X, + [WCD9360] = WCD9360, +}; + +static const struct of_device_id wcd9xxx_of_match[] = { + { .compatible = "qcom,tavil-i2c", + .data = (void *)&wcd9xxx_cdc_types[WCD934X]}, + { .compatible = "qcom,tasha-i2c-pgd", + .data = (void *)&wcd9xxx_cdc_types[WCD9335]}, + { .compatible = "qcom,wcd9xxx-i2c", + .data = (void *)&wcd9xxx_cdc_types[WCD9330]}, + { } +}; +MODULE_DEVICE_TABLE(of, wcd9xxx_of_match); + +static int wcd9xxx_slim_device_up(struct slim_device *sldev); +static int wcd9xxx_slim_device_down(struct slim_device *sldev); + +struct wcd9xxx_i2c wcd9xxx_modules[MAX_WCD9XXX_DEVICE]; + +static int wcd9xxx_slim_multi_reg_write(struct wcd9xxx *wcd9xxx, + const void *data, size_t count) +{ + unsigned int reg; + struct device *dev; + u8 val[WCD9XXX_PAGE_SIZE]; + int ret = 0; + int i = 0; + int n = 0; + unsigned int page_num; + size_t num_regs = (count / (REG_BYTES + VAL_BYTES)); + struct wcd9xxx_reg_val *bulk_reg; + u8 *buf; + + dev = wcd9xxx->dev; + if (!data) { + dev_err(dev, "%s: data is NULL\n", __func__); + return -EINVAL; + } + if (num_regs == 0) + return -EINVAL; + + bulk_reg = kzalloc(num_regs * (sizeof(struct wcd9xxx_reg_val)), + GFP_KERNEL); + if (!bulk_reg) + return -ENOMEM; + + buf = (u8 *)data; + reg = *(u16 *)buf; + page_num = WCD9XXX_PAGE_NUM(reg); + for (i = 0, n = 0; n < num_regs; i++, n++) { + reg = *(u16 *)buf; + if (page_num != WCD9XXX_PAGE_NUM(reg)) { + ret = wcd9xxx_slim_bulk_write(wcd9xxx, bulk_reg, + i, false); + page_num = WCD9XXX_PAGE_NUM(reg); + i = 0; + } + buf += REG_BYTES; + val[i] = *buf; + buf += VAL_BYTES; + bulk_reg[i].reg = reg; + bulk_reg[i].buf = &val[i]; + bulk_reg[i].bytes = 1; + } + ret = wcd9xxx_slim_bulk_write(wcd9xxx, bulk_reg, + i, false); + if (ret) + dev_err(dev, "%s: error writing bulk regs\n", + __func__); + + kfree(bulk_reg); + return ret; +} + +/* + * wcd9xxx_interface_reg_read: Read slim interface registers + * + * @wcd9xxx: Pointer to wcd9xxx structure + * @reg: register adderss + * + * Returns register value in success and negative error code in case of failure + */ +int wcd9xxx_interface_reg_read(struct wcd9xxx *wcd9xxx, unsigned short reg) +{ + u8 val; + int ret; + + mutex_lock(&wcd9xxx->io_lock); + ret = wcd9xxx->read_dev(wcd9xxx, reg, 1, (void *)&val, + true); + if (ret < 0) + dev_err(wcd9xxx->dev, "%s: Codec read 0x%x failed\n", + __func__, reg); + else + dev_dbg(wcd9xxx->dev, "%s: Read 0x%02x from 0x%x\n", + __func__, val, reg); + + mutex_unlock(&wcd9xxx->io_lock); + + if (ret < 0) + return ret; + else + return val; +} +EXPORT_SYMBOL(wcd9xxx_interface_reg_read); + +/* + * wcd9xxx_interface_reg_write: Write slim interface registers + * + * @wcd9xxx: Pointer to wcd9xxx structure + * @reg: register adderss + * @val: value of the register to be written + * + * Returns 0 for success and negative error code in case of failure + */ +int wcd9xxx_interface_reg_write(struct wcd9xxx *wcd9xxx, unsigned short reg, + u8 val) +{ + int ret; + + mutex_lock(&wcd9xxx->io_lock); + ret = wcd9xxx->write_dev(wcd9xxx, reg, 1, (void *)&val, true); + dev_dbg(wcd9xxx->dev, "%s: Write %02x to 0x%x ret(%d)\n", + __func__, val, reg, ret); + mutex_unlock(&wcd9xxx->io_lock); + + return ret; +} +EXPORT_SYMBOL(wcd9xxx_interface_reg_write); + +static int wcd9xxx_slim_read_device(struct wcd9xxx *wcd9xxx, unsigned short reg, + int bytes, void *dest, bool interface) +{ + int ret; + struct slim_ele_access msg; + int slim_read_tries = WCD9XXX_SLIM_RW_MAX_TRIES; + + msg.start_offset = WCD9XXX_REGISTER_START_OFFSET + reg; + msg.num_bytes = bytes; + msg.comp = NULL; + + if (!wcd9xxx->dev_up) { + dev_dbg_ratelimited( + wcd9xxx->dev, "%s: No read allowed. dev_up = %d\n", + __func__, wcd9xxx->dev_up); + return 0; + } + + while (1) { + mutex_lock(&wcd9xxx->xfer_lock); + ret = slim_request_val_element(interface ? + wcd9xxx->slim_slave : wcd9xxx->slim, + &msg, dest, bytes); + mutex_unlock(&wcd9xxx->xfer_lock); + if (likely(ret == 0) || (--slim_read_tries == 0)) + break; + usleep_range(5000, 5100); + } + + if (ret) + dev_err(wcd9xxx->dev, "%s: Error, Codec read failed (%d)\n", + __func__, ret); + + return ret; +} + +/* + * Interface specifies whether the write is to the interface or general + * registers. + */ +static int wcd9xxx_slim_write_device(struct wcd9xxx *wcd9xxx, + unsigned short reg, int bytes, void *src, bool interface) +{ + int ret; + struct slim_ele_access msg; + int slim_write_tries = WCD9XXX_SLIM_RW_MAX_TRIES; + + msg.start_offset = WCD9XXX_REGISTER_START_OFFSET + reg; + msg.num_bytes = bytes; + msg.comp = NULL; + + if (!wcd9xxx->dev_up) { + dev_dbg_ratelimited( + wcd9xxx->dev, "%s: No write allowed. dev_up = %d\n", + __func__, wcd9xxx->dev_up); + return 0; + } + + while (1) { + mutex_lock(&wcd9xxx->xfer_lock); + ret = slim_change_val_element(interface ? + wcd9xxx->slim_slave : wcd9xxx->slim, + &msg, src, bytes); + mutex_unlock(&wcd9xxx->xfer_lock); + if (likely(ret == 0) || (--slim_write_tries == 0)) + break; + usleep_range(5000, 5100); + } + + if (ret) + pr_err("%s: Error, Codec write failed (%d)\n", __func__, ret); + + return ret; +} + +static int wcd9xxx_slim_get_allowed_slice(struct wcd9xxx *wcd9xxx, + int bytes) +{ + int allowed_sz = bytes; + + if (likely(bytes == SLIM_REPEAT_WRITE_MAX_SLICE)) + allowed_sz = 16; + else if (bytes >= 12) + allowed_sz = 12; + else if (bytes >= 8) + allowed_sz = 8; + else if (bytes >= 6) + allowed_sz = 6; + else if (bytes >= 4) + allowed_sz = 4; + else + allowed_sz = bytes; + + return allowed_sz; +} + +/* + * wcd9xxx_slim_write_repeat: Write the same register with multiple values + * @wcd9xxx: handle to wcd core + * @reg: register to be written + * @bytes: number of bytes to be written to reg + * @src: buffer with data content to be written to reg + * This API will write reg with bytes from src in a single slimbus + * transaction. All values from 1 to 16 are supported by this API. + */ +int wcd9xxx_slim_write_repeat(struct wcd9xxx *wcd9xxx, unsigned short reg, + int bytes, void *src) +{ + int ret = 0, bytes_to_write = bytes, bytes_allowed; + struct slim_ele_access slim_msg; + + mutex_lock(&wcd9xxx->io_lock); + if (wcd9xxx->type == WCD9335 || wcd9xxx->type == WCD934X || + wcd9xxx->type == WCD9360) { + ret = wcd9xxx_page_write(wcd9xxx, ®); + if (ret) + goto done; + } + + slim_msg.start_offset = WCD9XXX_REGISTER_START_OFFSET + reg; + slim_msg.comp = NULL; + + if (unlikely(bytes > SLIM_REPEAT_WRITE_MAX_SLICE)) { + dev_err(wcd9xxx->dev, "%s: size %d not supported\n", + __func__, bytes); + ret = -EINVAL; + goto done; + } + + if (!wcd9xxx->dev_up) { + dev_dbg_ratelimited( + wcd9xxx->dev, "%s: No write allowed. dev_up = %d\n", + __func__, wcd9xxx->dev_up); + ret = 0; + goto done; + } + + while (bytes_to_write > 0) { + bytes_allowed = wcd9xxx_slim_get_allowed_slice(wcd9xxx, + bytes_to_write); + + slim_msg.num_bytes = bytes_allowed; + mutex_lock(&wcd9xxx->xfer_lock); + ret = slim_user_msg(wcd9xxx->slim, wcd9xxx->slim->laddr, + SLIM_MSG_MT_DEST_REFERRED_USER, + SLIM_USR_MC_REPEAT_CHANGE_VALUE, + &slim_msg, src, bytes_allowed); + mutex_unlock(&wcd9xxx->xfer_lock); + + if (ret) { + dev_err(wcd9xxx->dev, "%s: failed, ret = %d\n", + __func__, ret); + break; + } + + bytes_to_write = bytes_to_write - bytes_allowed; + src = ((u8 *)src) + bytes_allowed; + } + +done: + mutex_unlock(&wcd9xxx->io_lock); + + return ret; +} +EXPORT_SYMBOL(wcd9xxx_slim_write_repeat); + +/* + * wcd9xxx_slim_reserve_bw: API to reserve the slimbus bandwidth + * @wcd9xxx: Handle to the wcd9xxx core + * @bw_ops: value of the bandwidth that is requested + * @commit: Flag to indicate if bandwidth change is to be committed + * right away + */ +int wcd9xxx_slim_reserve_bw(struct wcd9xxx *wcd9xxx, + u32 bw_ops, bool commit) +{ + if (!wcd9xxx || !wcd9xxx->slim) { + pr_err("%s: Invalid handle to %s\n", + __func__, + (!wcd9xxx) ? "wcd9xxx" : "slim_device"); + return -EINVAL; + } + + return slim_reservemsg_bw(wcd9xxx->slim, bw_ops, commit); +} +EXPORT_SYMBOL(wcd9xxx_slim_reserve_bw); + +/* + * wcd9xxx_slim_bulk_write: API to write multiple registers with one descriptor + * @wcd9xxx: Handle to the wcd9xxx core + * @wcd9xxx_reg_val: structure holding register and values to be written + * @size: Indicates number of messages to be written with one descriptor + * @is_interface: Indicates whether the register is for slim interface or for + * general registers. + * @return: returns 0 if success or error information to the caller in case + * of failure. + */ +int wcd9xxx_slim_bulk_write(struct wcd9xxx *wcd9xxx, + struct wcd9xxx_reg_val *bulk_reg, + unsigned int size, bool is_interface) +{ + int ret, i; + struct slim_val_inf *msgs; + unsigned short reg; + + if (!bulk_reg || !size || !wcd9xxx) { + pr_err("%s: Invalid parameters\n", __func__); + return -EINVAL; + } + + if (!wcd9xxx->dev_up) { + dev_dbg_ratelimited( + wcd9xxx->dev, "%s: No write allowed. dev_up = %d\n", + __func__, wcd9xxx->dev_up); + return 0; + } + + msgs = kzalloc(size * (sizeof(struct slim_val_inf)), GFP_KERNEL); + if (!msgs) { + ret = -ENOMEM; + goto mem_fail; + } + + mutex_lock(&wcd9xxx->io_lock); + reg = bulk_reg->reg; + for (i = 0; i < size; i++) { + msgs[i].start_offset = WCD9XXX_REGISTER_START_OFFSET + + (bulk_reg->reg & 0xFF); + msgs[i].num_bytes = bulk_reg->bytes; + msgs[i].wbuf = bulk_reg->buf; + bulk_reg++; + } + ret = wcd9xxx_page_write(wcd9xxx, ®); + if (ret) { + pr_err("%s: Page write error for reg: 0x%x\n", + __func__, reg); + goto err; + } + + ret = slim_bulk_msg_write(is_interface ? + wcd9xxx->slim_slave : wcd9xxx->slim, + SLIM_MSG_MT_CORE, + SLIM_MSG_MC_CHANGE_VALUE, msgs, size, + NULL, NULL); + if (ret) + pr_err("%s: Error, Codec bulk write failed (%d)\n", + __func__, ret); + /* 100 usec sleep is needed as per HW requirement */ + usleep_range(100, 110); +err: + mutex_unlock(&wcd9xxx->io_lock); + kfree(msgs); +mem_fail: + return ret; +} +EXPORT_SYMBOL(wcd9xxx_slim_bulk_write); + +static int wcd9xxx_num_irq_regs(const struct wcd9xxx *wcd9xxx) +{ + return (wcd9xxx->codec_type->num_irqs / 8) + + ((wcd9xxx->codec_type->num_irqs % 8) ? 1 : 0); +} + +static int wcd9xxx_regmap_init_cache(struct wcd9xxx *wcd9xxx) +{ + struct regmap_config *regmap_config; + int rc; + + regmap_config = wcd9xxx_get_regmap_config(wcd9xxx->type); + if (!regmap_config) { + dev_err(wcd9xxx->dev, "regmap config is not defined\n"); + return -EINVAL; + } + + rc = regmap_reinit_cache(wcd9xxx->regmap, regmap_config); + if (rc != 0) { + dev_err(wcd9xxx->dev, "%s:Failed to reinit register cache: %d\n", + __func__, rc); + } + + return rc; +} + +static int wcd9xxx_device_init(struct wcd9xxx *wcd9xxx) +{ + int ret = 0, i; + struct wcd9xxx_core_resource *core_res = &wcd9xxx->core_res; + regmap_patch_fptr regmap_apply_patch = NULL; + + mutex_init(&wcd9xxx->io_lock); + mutex_init(&wcd9xxx->xfer_lock); + mutex_init(&wcd9xxx->reset_lock); + + ret = wcd9xxx_bringup(wcd9xxx->dev); + if (ret) { + ret = -EPROBE_DEFER; + goto err_bring_up; + } + + wcd9xxx->codec_type = devm_kzalloc(wcd9xxx->dev, + sizeof(struct wcd9xxx_codec_type), GFP_KERNEL); + if (!wcd9xxx->codec_type) { + ret = -ENOMEM; + goto err_bring_up; + } + ret = wcd9xxx_get_codec_info(wcd9xxx->dev); + if (ret) { + ret = -EPROBE_DEFER; + goto fail_cdc_fill; + } + wcd9xxx->version = wcd9xxx->codec_type->version; + if (!wcd9xxx->codec_type->dev || !wcd9xxx->codec_type->size) + goto fail_cdc_fill; + + core_res->parent = wcd9xxx; + core_res->dev = wcd9xxx->dev; + core_res->intr_table = wcd9xxx->codec_type->intr_tbl; + core_res->intr_table_size = wcd9xxx->codec_type->intr_tbl_size; + + for (i = 0; i < WCD9XXX_INTR_REG_MAX; i++) + wcd9xxx->core_res.intr_reg[i] = + wcd9xxx->codec_type->intr_reg[i]; + + wcd9xxx_core_res_init(&wcd9xxx->core_res, + wcd9xxx->codec_type->num_irqs, + wcd9xxx_num_irq_regs(wcd9xxx), + wcd9xxx->regmap); + + if (wcd9xxx_core_irq_init(&wcd9xxx->core_res)) + goto err; + + ret = wcd9xxx_regmap_init_cache(wcd9xxx); + if (ret) + goto err_irq; + + regmap_apply_patch = wcd9xxx_get_regmap_reg_patch( + wcd9xxx->type); + if (regmap_apply_patch) { + ret = regmap_apply_patch(wcd9xxx->regmap, + wcd9xxx->version); + if (ret) + dev_err(wcd9xxx->dev, + "Failed to register patch: %d\n", ret); + } + + ret = mfd_add_devices(wcd9xxx->dev, -1, wcd9xxx->codec_type->dev, + wcd9xxx->codec_type->size, NULL, 0, NULL); + if (ret != 0) { + dev_err(wcd9xxx->dev, "Failed to add children: %d\n", ret); + goto err_irq; + } + + ret = device_init_wakeup(wcd9xxx->dev, true); + if (ret) { + dev_err(wcd9xxx->dev, "Device wakeup init failed: %d\n", ret); + goto err_irq; + } + + return ret; +err_irq: + wcd9xxx_irq_exit(&wcd9xxx->core_res); +fail_cdc_fill: + devm_kfree(wcd9xxx->dev, wcd9xxx->codec_type); + wcd9xxx->codec_type = NULL; +err: + wcd9xxx_bringdown(wcd9xxx->dev); + wcd9xxx_core_res_deinit(&wcd9xxx->core_res); +err_bring_up: + mutex_destroy(&wcd9xxx->io_lock); + mutex_destroy(&wcd9xxx->xfer_lock); + mutex_destroy(&wcd9xxx->reset_lock); + return ret; +} + +static void wcd9xxx_device_exit(struct wcd9xxx *wcd9xxx) +{ + device_init_wakeup(wcd9xxx->dev, false); + wcd9xxx_irq_exit(&wcd9xxx->core_res); + mfd_remove_devices(wcd9xxx->dev); + wcd9xxx_bringdown(wcd9xxx->dev); + wcd9xxx_reset_low(wcd9xxx->dev); + wcd9xxx_core_res_deinit(&wcd9xxx->core_res); + mutex_destroy(&wcd9xxx->io_lock); + mutex_destroy(&wcd9xxx->xfer_lock); + mutex_destroy(&wcd9xxx->reset_lock); + if (wcd9xxx_get_intf_type() == WCD9XXX_INTERFACE_TYPE_SLIMBUS) + slim_remove_device(wcd9xxx->slim_slave); +} + + +#ifdef CONFIG_DEBUG_FS +struct wcd9xxx *debugCodec; + +static struct dentry *debugfs_wcd9xxx_dent; +static struct dentry *debugfs_peek; +static struct dentry *debugfs_poke; +static struct dentry *debugfs_power_state; +static struct dentry *debugfs_reg_dump; + +static unsigned char read_data; + +static int codec_debug_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static int get_parameters(char *buf, long int *param1, int num_of_par) +{ + char *token; + int base, cnt; + + token = strsep(&buf, " "); + + for (cnt = 0; cnt < num_of_par; cnt++) { + if (token != NULL) { + if ((token[1] == 'x') || (token[1] == 'X')) + base = 16; + else + base = 10; + + if (kstrtoul(token, base, ¶m1[cnt]) != 0) + return -EINVAL; + + token = strsep(&buf, " "); + } else + return -EINVAL; + } + return 0; +} + +static ssize_t wcd9xxx_slimslave_reg_show(char __user *ubuf, size_t count, + loff_t *ppos) +{ + int i, reg_val, len; + ssize_t total = 0; + char tmp_buf[25]; /* each line is 12 bytes but 25 for margin of error */ + + for (i = (int) *ppos / 12; i <= SLIM_MAX_REG_ADDR; i++) { + reg_val = wcd9xxx_interface_reg_read(debugCodec, i); + len = snprintf(tmp_buf, sizeof(tmp_buf), + "0x%.3x: 0x%.2x\n", i, reg_val); + + if ((total + len) >= count - 1) + break; + if (copy_to_user((ubuf + total), tmp_buf, len)) { + pr_err("%s: fail to copy reg dump\n", __func__); + total = -EFAULT; + goto copy_err; + } + *ppos += len; + total += len; + } + +copy_err: + return total; +} + +static ssize_t codec_debug_read(struct file *file, char __user *ubuf, + size_t count, loff_t *ppos) +{ + char lbuf[8]; + char *access_str = file->private_data; + ssize_t ret_cnt; + + if (*ppos < 0 || !count) + return -EINVAL; + + if (!strcmp(access_str, "slimslave_peek")) { + snprintf(lbuf, sizeof(lbuf), "0x%x\n", read_data); + ret_cnt = simple_read_from_buffer(ubuf, count, ppos, lbuf, + strnlen(lbuf, 7)); + } else if (!strcmp(access_str, "slimslave_reg_dump")) { + ret_cnt = wcd9xxx_slimslave_reg_show(ubuf, count, ppos); + } else { + pr_err("%s: %s not permitted to read\n", __func__, access_str); + ret_cnt = -EPERM; + } + + return ret_cnt; +} + +static void wcd9xxx_set_reset_pin_state(struct wcd9xxx *wcd9xxx, + struct wcd9xxx_pdata *pdata, + bool active) +{ + if (wcd9xxx->wcd_rst_np) { + if (active) + msm_cdc_pinctrl_select_active_state( + wcd9xxx->wcd_rst_np); + else + msm_cdc_pinctrl_select_sleep_state( + wcd9xxx->wcd_rst_np); + + return; + } else if (gpio_is_valid(wcd9xxx->reset_gpio)) { + gpio_direction_output(wcd9xxx->reset_gpio, + (active == true ? 1 : 0)); + } +} + +static int codec_debug_process_cdc_power(char *lbuf) +{ + long int param; + int rc; + struct wcd9xxx_pdata *pdata; + + if (wcd9xxx_get_intf_type() != WCD9XXX_INTERFACE_TYPE_SLIMBUS) { + pr_err("%s: CODEC is not in SLIMBUS mode\n", __func__); + rc = -EPERM; + goto error_intf; + } + + rc = get_parameters(lbuf, ¶m, 1); + + if (likely(!rc)) { + pdata = debugCodec->slim->dev.platform_data; + if (param == 0) { + wcd9xxx_slim_device_down(debugCodec->slim); + msm_cdc_disable_static_supplies(debugCodec->dev, + debugCodec->supplies, + pdata->regulator, + pdata->num_supplies); + wcd9xxx_set_reset_pin_state(debugCodec, pdata, false); + } else if (param == 1) { + msm_cdc_enable_static_supplies(debugCodec->dev, + debugCodec->supplies, + pdata->regulator, + pdata->num_supplies); + usleep_range(1000, 2000); + wcd9xxx_set_reset_pin_state(debugCodec, pdata, false); + usleep_range(1000, 2000); + wcd9xxx_set_reset_pin_state(debugCodec, pdata, true); + usleep_range(1000, 2000); + wcd9xxx_slim_device_up(debugCodec->slim); + } else { + pr_err("%s: invalid command %ld\n", __func__, param); + } + } + +error_intf: + return rc; +} + +static ssize_t codec_debug_write(struct file *filp, + const char __user *ubuf, size_t cnt, loff_t *ppos) +{ + char *access_str = filp->private_data; + char lbuf[32]; + int rc; + long int param[5]; + + if (cnt > sizeof(lbuf) - 1) + return -EINVAL; + + rc = copy_from_user(lbuf, ubuf, cnt); + if (rc) + return -EFAULT; + + lbuf[cnt] = '\0'; + + if (!strcmp(access_str, "slimslave_poke")) { + /* write */ + rc = get_parameters(lbuf, param, 2); + if ((param[0] <= 0x3FF) && (param[1] <= 0xFF) && + (rc == 0)) + wcd9xxx_interface_reg_write(debugCodec, param[0], + param[1]); + else + rc = -EINVAL; + } else if (!strcmp(access_str, "slimslave_peek")) { + /* read */ + rc = get_parameters(lbuf, param, 1); + if ((param[0] <= 0x3FF) && (rc == 0)) + read_data = wcd9xxx_interface_reg_read(debugCodec, + param[0]); + else + rc = -EINVAL; + } else if (!strcmp(access_str, "power_state")) { + rc = codec_debug_process_cdc_power(lbuf); + } + + if (rc == 0) + rc = cnt; + else + pr_err("%s: rc = %d\n", __func__, rc); + + return rc; +} + +static const struct file_operations codec_debug_ops = { + .open = codec_debug_open, + .write = codec_debug_write, + .read = codec_debug_read +}; +#endif + +static struct wcd9xxx_i2c *wcd9xxx_i2c_get_device_info(struct wcd9xxx *wcd9xxx, + u16 reg) +{ + u16 mask = 0x0f00; + int value = 0; + struct wcd9xxx_i2c *wcd9xxx_i2c = NULL; + + if (wcd9xxx->type == WCD9335) { + wcd9xxx_i2c = &wcd9xxx_modules[0]; + } else { + value = ((reg & mask) >> 8) & 0x000f; + switch (value) { + case 0: + wcd9xxx_i2c = &wcd9xxx_modules[0]; + break; + case 1: + wcd9xxx_i2c = &wcd9xxx_modules[1]; + break; + case 2: + wcd9xxx_i2c = &wcd9xxx_modules[2]; + break; + case 3: + wcd9xxx_i2c = &wcd9xxx_modules[3]; + break; + + default: + break; + } + } + return wcd9xxx_i2c; +} + +static int wcd9xxx_i2c_write_device(struct wcd9xxx *wcd9xxx, u16 reg, u8 *value, + u32 bytes) +{ + + struct i2c_msg *msg; + int ret = 0; + u8 reg_addr = 0; + u8 data[bytes + 1]; + struct wcd9xxx_i2c *wcd9xxx_i2c; + + wcd9xxx_i2c = wcd9xxx_i2c_get_device_info(wcd9xxx, reg); + if (wcd9xxx_i2c == NULL || wcd9xxx_i2c->client == NULL) { + pr_err("failed to get device info\n"); + return -ENODEV; + } + reg_addr = (u8)reg; + msg = &wcd9xxx_i2c->xfer_msg[0]; + msg->addr = wcd9xxx_i2c->client->addr; + msg->len = bytes + 1; + msg->flags = 0; + data[0] = reg; + data[1] = *value; + msg->buf = data; + ret = i2c_transfer(wcd9xxx_i2c->client->adapter, + wcd9xxx_i2c->xfer_msg, 1); + /* Try again if the write fails */ + if (ret != 1) { + ret = i2c_transfer(wcd9xxx_i2c->client->adapter, + wcd9xxx_i2c->xfer_msg, 1); + if (ret != 1) { + pr_err("failed to write the device\n"); + return ret; + } + } + pr_debug("write success register = %x val = %x\n", reg, data[1]); + return 0; +} + + +static int wcd9xxx_i2c_read_device(struct wcd9xxx *wcd9xxx, unsigned short reg, + int bytes, unsigned char *dest) +{ + struct i2c_msg *msg; + int ret = 0; + u8 reg_addr = 0; + struct wcd9xxx_i2c *wcd9xxx_i2c; + u8 i = 0; + + wcd9xxx_i2c = wcd9xxx_i2c_get_device_info(wcd9xxx, reg); + if (wcd9xxx_i2c == NULL || wcd9xxx_i2c->client == NULL) { + pr_err("failed to get device info\n"); + return -ENODEV; + } + for (i = 0; i < bytes; i++) { + reg_addr = (u8)reg++; + msg = &wcd9xxx_i2c->xfer_msg[0]; + msg->addr = wcd9xxx_i2c->client->addr; + msg->len = 1; + msg->flags = 0; + msg->buf = ®_addr; + + msg = &wcd9xxx_i2c->xfer_msg[1]; + msg->addr = wcd9xxx_i2c->client->addr; + msg->len = 1; + msg->flags = I2C_M_RD; + msg->buf = dest++; + ret = i2c_transfer(wcd9xxx_i2c->client->adapter, + wcd9xxx_i2c->xfer_msg, 2); + + /* Try again if read fails first time */ + if (ret != 2) { + ret = i2c_transfer(wcd9xxx_i2c->client->adapter, + wcd9xxx_i2c->xfer_msg, 2); + if (ret != 2) { + pr_err("failed to read wcd9xxx register\n"); + return ret; + } + } + } + return 0; +} + +int wcd9xxx_i2c_read(struct wcd9xxx *wcd9xxx, unsigned short reg, + int bytes, void *dest, bool interface_reg) +{ + return wcd9xxx_i2c_read_device(wcd9xxx, reg, bytes, dest); +} + +int wcd9xxx_i2c_write(struct wcd9xxx *wcd9xxx, unsigned short reg, + int bytes, void *src, bool interface_reg) +{ + return wcd9xxx_i2c_write_device(wcd9xxx, reg, src, bytes); +} + +static int wcd9xxx_i2c_get_client_index(struct i2c_client *client, + int *wcd9xx_index) +{ + int ret = 0; + + switch (client->addr) { + case WCD9XXX_I2C_TOP_SLAVE_ADDR: + *wcd9xx_index = WCD9XXX_I2C_TOP_LEVEL; + break; + case WCD9XXX_ANALOG_I2C_SLAVE_ADDR: + *wcd9xx_index = WCD9XXX_I2C_ANALOG; + break; + case WCD9XXX_DIGITAL1_I2C_SLAVE_ADDR: + *wcd9xx_index = WCD9XXX_I2C_DIGITAL_1; + break; + case WCD9XXX_DIGITAL2_I2C_SLAVE_ADDR: + *wcd9xx_index = WCD9XXX_I2C_DIGITAL_2; + break; + default: + ret = -EINVAL; + break; + } + return ret; +} + +static int wcd9xxx_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct wcd9xxx *wcd9xxx = NULL; + struct wcd9xxx_pdata *pdata = NULL; + int val = 0; + int ret = 0; + int wcd9xx_index = 0; + struct device *dev; + int intf_type; + const struct of_device_id *of_id; + + intf_type = wcd9xxx_get_intf_type(); + + pr_debug("%s: interface status %d\n", __func__, intf_type); + if (intf_type == WCD9XXX_INTERFACE_TYPE_SLIMBUS) { + dev_dbg(&client->dev, "%s:Codec is detected in slimbus mode\n", + __func__); + return -ENODEV; + } else if (intf_type == WCD9XXX_INTERFACE_TYPE_I2C) { + ret = wcd9xxx_i2c_get_client_index(client, &wcd9xx_index); + if (ret != 0) + dev_err(&client->dev, "%s: I2C set codec I2C\n" + "client failed\n", __func__); + else { + dev_err(&client->dev, "%s:probe for other slaves\n" + "devices of codec I2C slave Addr = %x\n", + __func__, client->addr); + wcd9xxx_modules[wcd9xx_index].client = client; + } + return ret; + } else if (intf_type == WCD9XXX_INTERFACE_TYPE_PROBING) { + dev = &client->dev; + if (client->dev.of_node) { + dev_dbg(&client->dev, "%s:Platform data\n" + "from device tree\n", __func__); + pdata = wcd9xxx_populate_dt_data(&client->dev); + if (!pdata) { + dev_err(&client->dev, + "%s: Fail to obtain pdata from device tree\n", + __func__); + ret = -EINVAL; + goto fail; + } + client->dev.platform_data = pdata; + } else { + dev_dbg(&client->dev, "%s:Platform data from\n" + "board file\n", __func__); + pdata = client->dev.platform_data; + } + wcd9xxx = devm_kzalloc(&client->dev, sizeof(struct wcd9xxx), + GFP_KERNEL); + if (!wcd9xxx) { + ret = -ENOMEM; + goto fail; + } + + if (!pdata) { + dev_dbg(&client->dev, "no platform data?\n"); + ret = -EINVAL; + goto fail; + } + wcd9xxx->type = WCD9XXX; + if (client->dev.of_node) { + of_id = of_match_device(wcd9xxx_of_match, &client->dev); + if (of_id) { + wcd9xxx->type = *((int *)of_id->data); + dev_info(&client->dev, "%s: codec type is %d\n", + __func__, wcd9xxx->type); + } + } else { + dev_info(&client->dev, "%s: dev.of_node is NULL, default to WCD9XXX\n", + __func__); + wcd9xxx->type = WCD9XXX; + } + wcd9xxx->regmap = wcd9xxx_regmap_init(&client->dev, + &wcd9xxx_i2c_base_regmap_config); + if (IS_ERR(wcd9xxx->regmap)) { + ret = PTR_ERR(wcd9xxx->regmap); + dev_err(&client->dev, "%s: Failed to allocate register map: %d\n", + __func__, ret); + goto err_codec; + } + wcd9xxx->reset_gpio = pdata->reset_gpio; + wcd9xxx->wcd_rst_np = pdata->wcd_rst_np; + + if (!wcd9xxx->wcd_rst_np) { + pdata->use_pinctrl = false; + dev_err(&client->dev, "%s: pinctrl not used for rst_n\n", + __func__); + goto err_codec; + } + + if (i2c_check_functionality(client->adapter, + I2C_FUNC_I2C) == 0) { + dev_dbg(&client->dev, "can't talk I2C?\n"); + ret = -EIO; + goto fail; + } + dev_set_drvdata(&client->dev, wcd9xxx); + wcd9xxx->dev = &client->dev; + wcd9xxx->dev_up = true; + if (client->dev.of_node) + wcd9xxx->mclk_rate = pdata->mclk_rate; + + wcd9xxx->num_of_supplies = pdata->num_supplies; + ret = msm_cdc_init_supplies(wcd9xxx->dev, &wcd9xxx->supplies, + pdata->regulator, + pdata->num_supplies); + if (!wcd9xxx->supplies) { + dev_err(wcd9xxx->dev, "%s: Cannot init wcd supplies\n", + __func__); + goto err_codec; + } + ret = msm_cdc_enable_static_supplies(wcd9xxx->dev, + wcd9xxx->supplies, + pdata->regulator, + pdata->num_supplies); + if (ret) { + dev_err(wcd9xxx->dev, "%s: wcd static supply enable failed!\n", + __func__); + goto err_codec; + } + /* For WCD9335, it takes about 600us for the Vout_A and + * Vout_D to be ready after BUCK_SIDO is powered up\ + * SYS_RST_N shouldn't be pulled high during this time + */ + if (wcd9xxx->type == WCD9335) + usleep_range(600, 650); + else + usleep_range(5, 10); + + ret = wcd9xxx_reset(wcd9xxx->dev); + if (ret) { + pr_err("%s: Resetting Codec failed\n", __func__); + goto err_supplies; + } + + ret = wcd9xxx_i2c_get_client_index(client, &wcd9xx_index); + if (ret != 0) { + pr_err("%s:Set codec I2C client failed\n", __func__); + goto err_supplies; + } + + wcd9xxx_modules[wcd9xx_index].client = client; + wcd9xxx->read_dev = wcd9xxx_i2c_read; + wcd9xxx->write_dev = wcd9xxx_i2c_write; + if (!wcd9xxx->dev->of_node) + wcd9xxx_assign_irq(&wcd9xxx->core_res, + pdata->irq, pdata->irq_base); + + ret = wcd9xxx_device_init(wcd9xxx); + if (ret) { + pr_err("%s: error, initializing device failed (%d)\n", + __func__, ret); + goto err_device_init; + } + + ret = wcd9xxx_i2c_read(wcd9xxx, WCD9XXX_A_CHIP_STATUS, 1, + &val, 0); + if (ret < 0) + pr_err("%s: failed to read the wcd9xxx status (%d)\n", + __func__, ret); + if (val != wcd9xxx->codec_type->i2c_chip_status) + pr_err("%s: unknown chip status 0x%x\n", __func__, val); + + wcd9xxx_set_intf_type(WCD9XXX_INTERFACE_TYPE_I2C); + + return ret; + } + + pr_err("%s: I2C probe in wrong state\n", __func__); + + +err_device_init: + wcd9xxx_reset_low(wcd9xxx->dev); +err_supplies: + msm_cdc_release_supplies(wcd9xxx->dev, wcd9xxx->supplies, + pdata->regulator, + pdata->num_supplies); + pdata->regulator = NULL; + pdata->num_supplies = 0; +err_codec: + devm_kfree(&client->dev, wcd9xxx); + dev_set_drvdata(&client->dev, NULL); +fail: + return ret; +} + +static int wcd9xxx_i2c_remove(struct i2c_client *client) +{ + struct wcd9xxx *wcd9xxx; + struct wcd9xxx_pdata *pdata = client->dev.platform_data; + + wcd9xxx = dev_get_drvdata(&client->dev); + msm_cdc_release_supplies(wcd9xxx->dev, wcd9xxx->supplies, + pdata->regulator, + pdata->num_supplies); + wcd9xxx_device_exit(wcd9xxx); + dev_set_drvdata(&client->dev, NULL); + return 0; +} + +static int wcd9xxx_dt_parse_slim_interface_dev_info(struct device *dev, + struct slim_device *slim_ifd) +{ + int ret = 0; + struct property *prop; + + ret = of_property_read_string(dev->of_node, "qcom,cdc-slim-ifd", + &slim_ifd->name); + if (ret) { + dev_err(dev, "Looking up %s property in node %s failed", + "qcom,cdc-slim-ifd-dev", dev->of_node->full_name); + return -ENODEV; + } + prop = of_find_property(dev->of_node, + "qcom,cdc-slim-ifd-elemental-addr", NULL); + if (!prop) { + dev_err(dev, "Looking up %s property in node %s failed", + "qcom,cdc-slim-ifd-elemental-addr", + dev->of_node->full_name); + return -ENODEV; + } else if (prop->length != 6) { + dev_err(dev, "invalid codec slim ifd addr. addr length = %d\n", + prop->length); + return -ENODEV; + } + memcpy(slim_ifd->e_addr, prop->value, 6); + + return 0; +} + +static int wcd9xxx_slim_get_laddr(struct slim_device *sb, + const u8 *e_addr, u8 e_len, u8 *laddr) +{ + int ret; + const unsigned long timeout = jiffies + + msecs_to_jiffies(SLIMBUS_PRESENT_TIMEOUT); + + do { + ret = slim_get_logical_addr(sb, e_addr, e_len, laddr); + if (!ret) + break; + /* Give SLIMBUS time to report present and be ready. */ + usleep_range(1000, 1100); + pr_debug_ratelimited("%s: retyring get logical addr\n", + __func__); + } while time_before(jiffies, timeout); + + return ret; +} + +static int wcd9xxx_slim_probe(struct slim_device *slim) +{ + struct wcd9xxx *wcd9xxx; + struct wcd9xxx_pdata *pdata; + const struct slim_device_id *device_id; + int ret = 0; + int intf_type; + + intf_type = wcd9xxx_get_intf_type(); + + wcd9xxx = devm_kzalloc(&slim->dev, sizeof(struct wcd9xxx), + GFP_KERNEL); + if (!wcd9xxx) { + ret = -ENOMEM; + goto err; + } + + if (!slim) { + ret = -EINVAL; + goto err; + } + + if (intf_type == WCD9XXX_INTERFACE_TYPE_I2C) { + dev_dbg(&slim->dev, "%s:Codec is detected in I2C mode\n", + __func__); + ret = -ENODEV; + goto err; + } + if (slim->dev.of_node) { + dev_dbg(&slim->dev, "Platform data from device tree\n"); + pdata = wcd9xxx_populate_dt_data(&slim->dev); + if (!pdata) { + dev_err(&slim->dev, + "%s: Fail to obtain pdata from device tree\n", + __func__); + ret = -EINVAL; + goto err; + } + + ret = wcd9xxx_dt_parse_slim_interface_dev_info(&slim->dev, + &pdata->slimbus_slave_device); + if (ret) { + dev_err(&slim->dev, "Error, parsing slim interface\n"); + devm_kfree(&slim->dev, pdata); + ret = -EINVAL; + goto err; + } + slim->dev.platform_data = pdata; + + } else { + dev_info(&slim->dev, "Platform data from board file\n"); + pdata = slim->dev.platform_data; + } + + if (!pdata) { + dev_err(&slim->dev, "Error, no platform data\n"); + ret = -EINVAL; + goto err; + } + + if (!slim->ctrl) { + dev_err(&slim->dev, "%s: Error, no SLIMBUS control data\n", + __func__); + ret = -EINVAL; + goto err_codec; + } + + if (pdata->has_buck_vsel_gpio) + msm_cdc_pinctrl_select_active_state(pdata->buck_vsel_ctl_np); + + if (pdata->has_micb_supply_en_gpio) + msm_cdc_pinctrl_select_active_state(pdata->micb_en_ctl); + + device_id = slim_get_device_id(slim); + if (!device_id) { + dev_err(&slim->dev, "%s: Error, no device id\n", __func__); + ret = -EINVAL; + goto err; + } + + wcd9xxx->type = device_id->driver_data; + dev_info(&slim->dev, "%s: probing for wcd type: %d, name: %s\n", + __func__, wcd9xxx->type, device_id->name); + + /* wcd9xxx members init */ + wcd9xxx->multi_reg_write = wcd9xxx_slim_multi_reg_write; + wcd9xxx->slim = slim; + slim_set_clientdata(slim, wcd9xxx); + wcd9xxx->reset_gpio = pdata->reset_gpio; + wcd9xxx->dev = &slim->dev; + wcd9xxx->mclk_rate = pdata->mclk_rate; + wcd9xxx->dev_up = true; + wcd9xxx->wcd_rst_np = pdata->wcd_rst_np; + + wcd9xxx->regmap = wcd9xxx_regmap_init(&slim->dev, + &wcd9xxx_base_regmap_config); + if (IS_ERR(wcd9xxx->regmap)) { + ret = PTR_ERR(wcd9xxx->regmap); + dev_err(&slim->dev, "%s: Failed to allocate register map: %d\n", + __func__, ret); + goto err_codec; + } + + if (!wcd9xxx->wcd_rst_np) { + pdata->use_pinctrl = false; + dev_err(&slim->dev, "%s: pinctrl not used for rst_n\n", + __func__); + goto err_codec; + } + + wcd9xxx->num_of_supplies = pdata->num_supplies; + ret = msm_cdc_init_supplies(&slim->dev, &wcd9xxx->supplies, + pdata->regulator, + pdata->num_supplies); + if (!wcd9xxx->supplies) { + dev_err(wcd9xxx->dev, "%s: Cannot init wcd supplies\n", + __func__); + goto err_codec; + } + ret = msm_cdc_enable_static_supplies(wcd9xxx->dev, + wcd9xxx->supplies, + pdata->regulator, + pdata->num_supplies); + if (ret) { + dev_err(wcd9xxx->dev, "%s: wcd static supply enable failed!\n", + __func__); + goto err_codec; + } + + /* + * For WCD9335, it takes about 600us for the Vout_A and + * Vout_D to be ready after BUCK_SIDO is powered up. + * SYS_RST_N shouldn't be pulled high during this time + */ + if (wcd9xxx->type == WCD9335 || wcd9xxx->type == WCD934X || + wcd9xxx->type == WCD9360) + usleep_range(600, 650); + else + usleep_range(5, 10); + + ret = wcd9xxx_reset(&slim->dev); + if (ret) { + dev_err(&slim->dev, "%s: Resetting Codec failed\n", __func__); + goto err_supplies; + } + + ret = wcd9xxx_slim_get_laddr(wcd9xxx->slim, wcd9xxx->slim->e_addr, + ARRAY_SIZE(wcd9xxx->slim->e_addr), + &wcd9xxx->slim->laddr); + if (ret) { + dev_err(&slim->dev, "%s: failed to get slimbus %s logical address: %d\n", + __func__, wcd9xxx->slim->name, ret); + goto err_reset; + } + wcd9xxx->read_dev = wcd9xxx_slim_read_device; + wcd9xxx->write_dev = wcd9xxx_slim_write_device; + wcd9xxx_pgd_la = wcd9xxx->slim->laddr; + wcd9xxx->slim_slave = &pdata->slimbus_slave_device; + if (!wcd9xxx->dev->of_node) + wcd9xxx_assign_irq(&wcd9xxx->core_res, + pdata->irq, pdata->irq_base); + + ret = slim_add_device(slim->ctrl, wcd9xxx->slim_slave); + if (ret) { + dev_err(&slim->dev, "%s: error, adding SLIMBUS device failed\n", + __func__); + goto err_reset; + } + + ret = wcd9xxx_slim_get_laddr(wcd9xxx->slim_slave, + wcd9xxx->slim_slave->e_addr, + ARRAY_SIZE(wcd9xxx->slim_slave->e_addr), + &wcd9xxx->slim_slave->laddr); + if (ret) { + dev_err(&slim->dev, "%s: failed to get slimbus %s logical address: %d\n", + __func__, wcd9xxx->slim->name, ret); + goto err_slim_add; + } + wcd9xxx_inf_la = wcd9xxx->slim_slave->laddr; + wcd9xxx_set_intf_type(WCD9XXX_INTERFACE_TYPE_SLIMBUS); + + ret = wcd9xxx_device_init(wcd9xxx); + if (ret) { + dev_err(&slim->dev, "%s: error, initializing device failed (%d)\n", + __func__, ret); + goto err_slim_add; + } +#ifdef CONFIG_DEBUG_FS + debugCodec = wcd9xxx; + + debugfs_wcd9xxx_dent = debugfs_create_dir + ("wcd9xxx_core", 0); + if (!IS_ERR(debugfs_wcd9xxx_dent)) { + debugfs_peek = debugfs_create_file("slimslave_peek", + S_IFREG | 0444, debugfs_wcd9xxx_dent, + (void *) "slimslave_peek", &codec_debug_ops); + + debugfs_poke = debugfs_create_file("slimslave_poke", + S_IFREG | 0444, debugfs_wcd9xxx_dent, + (void *) "slimslave_poke", &codec_debug_ops); + + debugfs_power_state = debugfs_create_file("power_state", + S_IFREG | 0444, debugfs_wcd9xxx_dent, + (void *) "power_state", &codec_debug_ops); + + debugfs_reg_dump = debugfs_create_file("slimslave_reg_dump", + S_IFREG | 0444, debugfs_wcd9xxx_dent, + (void *) "slimslave_reg_dump", &codec_debug_ops); + } +#endif + + return ret; + +err_slim_add: + slim_remove_device(wcd9xxx->slim_slave); +err_reset: + wcd9xxx_reset_low(wcd9xxx->dev); +err_supplies: + msm_cdc_release_supplies(wcd9xxx->dev, wcd9xxx->supplies, + pdata->regulator, + pdata->num_supplies); +err_codec: + slim_set_clientdata(slim, NULL); +err: + devm_kfree(&slim->dev, wcd9xxx); + return ret; +} +static int wcd9xxx_slim_remove(struct slim_device *pdev) +{ + struct wcd9xxx *wcd9xxx; + struct wcd9xxx_pdata *pdata = pdev->dev.platform_data; + +#ifdef CONFIG_DEBUG_FS + debugfs_remove_recursive(debugfs_wcd9xxx_dent); +#endif + wcd9xxx = slim_get_devicedata(pdev); + wcd9xxx_deinit_slimslave(wcd9xxx); + slim_remove_device(wcd9xxx->slim_slave); + msm_cdc_release_supplies(wcd9xxx->dev, wcd9xxx->supplies, + pdata->regulator, + pdata->num_supplies); + wcd9xxx_device_exit(wcd9xxx); + slim_set_clientdata(pdev, NULL); + return 0; +} + +static int wcd9xxx_device_up(struct wcd9xxx *wcd9xxx) +{ + int ret = 0; + struct wcd9xxx_core_resource *wcd9xxx_res = &wcd9xxx->core_res; + + dev_info(wcd9xxx->dev, "%s: codec bring up\n", __func__); + wcd9xxx_bringup(wcd9xxx->dev); + ret = wcd9xxx_irq_init(wcd9xxx_res); + if (ret) { + pr_err("%s: wcd9xx_irq_init failed : %d\n", __func__, ret); + } else { + if (wcd9xxx->post_reset) + ret = wcd9xxx->post_reset(wcd9xxx); + } + return ret; +} + +static int wcd9xxx_slim_device_reset(struct slim_device *sldev) +{ + int ret; + struct wcd9xxx *wcd9xxx = slim_get_devicedata(sldev); + + if (!wcd9xxx) { + pr_err("%s: wcd9xxx is NULL\n", __func__); + return -EINVAL; + } + + dev_info(wcd9xxx->dev, "%s: device reset, dev_up = %d\n", + __func__, wcd9xxx->dev_up); + if (wcd9xxx->dev_up) + return 0; + + mutex_lock(&wcd9xxx->reset_lock); + ret = wcd9xxx_reset(wcd9xxx->dev); + if (ret) + dev_err(wcd9xxx->dev, "%s: Resetting Codec failed\n", __func__); + mutex_unlock(&wcd9xxx->reset_lock); + + return ret; +} + +static int wcd9xxx_slim_device_up(struct slim_device *sldev) +{ + struct wcd9xxx *wcd9xxx = slim_get_devicedata(sldev); + int ret = 0; + + if (!wcd9xxx) { + pr_err("%s: wcd9xxx is NULL\n", __func__); + return -EINVAL; + } + dev_info(wcd9xxx->dev, "%s: slim device up, dev_up = %d\n", + __func__, wcd9xxx->dev_up); + if (wcd9xxx->dev_up) + return 0; + + wcd9xxx->dev_up = true; + + mutex_lock(&wcd9xxx->reset_lock); + ret = wcd9xxx_device_up(wcd9xxx); + mutex_unlock(&wcd9xxx->reset_lock); + + return ret; +} + +static int wcd9xxx_slim_device_down(struct slim_device *sldev) +{ + struct wcd9xxx *wcd9xxx = slim_get_devicedata(sldev); + + if (!wcd9xxx) { + pr_err("%s: wcd9xxx is NULL\n", __func__); + return -EINVAL; + } + + dev_info(wcd9xxx->dev, "%s: device down, dev_up = %d\n", + __func__, wcd9xxx->dev_up); + if (!wcd9xxx->dev_up) + return 0; + + wcd9xxx->dev_up = false; + + mutex_lock(&wcd9xxx->reset_lock); + if (wcd9xxx->dev_down) + wcd9xxx->dev_down(wcd9xxx); + wcd9xxx_irq_exit(&wcd9xxx->core_res); + wcd9xxx_reset_low(wcd9xxx->dev); + mutex_unlock(&wcd9xxx->reset_lock); + + return 0; +} + +static int wcd9xxx_slim_resume(struct slim_device *sldev) +{ + struct wcd9xxx *wcd9xxx = slim_get_devicedata(sldev); + + return wcd9xxx_core_res_resume(&wcd9xxx->core_res); +} + +static int wcd9xxx_i2c_resume(struct device *dev) +{ + struct wcd9xxx *wcd9xxx = dev_get_drvdata(dev); + + if (wcd9xxx) + return wcd9xxx_core_res_resume(&wcd9xxx->core_res); + else + return 0; +} + +static int wcd9xxx_slim_suspend(struct slim_device *sldev, pm_message_t pmesg) +{ + struct wcd9xxx *wcd9xxx = slim_get_devicedata(sldev); + + return wcd9xxx_core_res_suspend(&wcd9xxx->core_res, pmesg); +} + +static int wcd9xxx_i2c_suspend(struct device *dev) +{ + struct wcd9xxx *wcd9xxx = dev_get_drvdata(dev); + pm_message_t pmesg = {0}; + + if (wcd9xxx) + return wcd9xxx_core_res_suspend(&wcd9xxx->core_res, pmesg); + else + return 0; +} + +static const struct slim_device_id wcd_slim_device_id[] = { + {"sitar-slim", 0}, + {"sitar1p1-slim", 0}, + {"tabla-slim", 0}, + {"tabla2x-slim", 0}, + {"taiko-slim-pgd", 0}, + {"tapan-slim-pgd", 0}, + {"tomtom-slim-pgd", WCD9330}, + {"tasha-slim-pgd", WCD9335}, + {"tavil-slim-pgd", WCD934X}, + {"pahu-slim-pgd", WCD9360}, + {} +}; + +static struct slim_driver wcd_slim_driver = { + .driver = { + .name = "wcd-slim", + .owner = THIS_MODULE, + }, + .probe = wcd9xxx_slim_probe, + .remove = wcd9xxx_slim_remove, + .id_table = wcd_slim_device_id, + .resume = wcd9xxx_slim_resume, + .suspend = wcd9xxx_slim_suspend, + .device_up = wcd9xxx_slim_device_up, + .reset_device = wcd9xxx_slim_device_reset, + .device_down = wcd9xxx_slim_device_down, +}; + +static struct i2c_device_id wcd9xxx_id_table[] = { + {"wcd9xxx-i2c", WCD9XXX_I2C_TOP_LEVEL}, + {"wcd9xxx-i2c", WCD9XXX_I2C_ANALOG}, + {"wcd9xxx-i2c", WCD9XXX_I2C_DIGITAL_1}, + {"wcd9xxx-i2c", WCD9XXX_I2C_DIGITAL_2}, + {} +}; + +static struct i2c_device_id tasha_id_table[] = { + {"tasha-i2c-pgd", WCD9XXX_I2C_TOP_LEVEL}, + {} +}; + +static struct i2c_device_id tavil_id_table[] = { + {"tavil-i2c", WCD9XXX_I2C_TOP_LEVEL}, + {} +}; + +static struct i2c_device_id tabla_id_table[] = { + {"tabla top level", WCD9XXX_I2C_TOP_LEVEL}, + {"tabla analog", WCD9XXX_I2C_ANALOG}, + {"tabla digital1", WCD9XXX_I2C_DIGITAL_1}, + {"tabla digital2", WCD9XXX_I2C_DIGITAL_2}, + {} +}; +MODULE_DEVICE_TABLE(i2c, tabla_id_table); + +static const struct dev_pm_ops wcd9xxx_i2c_pm_ops = { + .suspend = wcd9xxx_i2c_suspend, + .resume = wcd9xxx_i2c_resume, +}; + +static struct i2c_driver tabla_i2c_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "tabla-i2c-core", + .pm = &wcd9xxx_i2c_pm_ops, + }, + .id_table = tabla_id_table, + .probe = wcd9xxx_i2c_probe, + .remove = wcd9xxx_i2c_remove, +}; + +static struct i2c_driver wcd9xxx_i2c_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "wcd9xxx-i2c-core", + .pm = &wcd9xxx_i2c_pm_ops, + }, + .id_table = wcd9xxx_id_table, + .probe = wcd9xxx_i2c_probe, + .remove = wcd9xxx_i2c_remove, +}; + +static struct i2c_driver wcd9335_i2c_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "tasha-i2c-core", + .pm = &wcd9xxx_i2c_pm_ops, + }, + .id_table = tasha_id_table, + .probe = wcd9xxx_i2c_probe, + .remove = wcd9xxx_i2c_remove, +}; + +static struct i2c_driver wcd934x_i2c_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "tavil-i2c-core", + .pm = &wcd9xxx_i2c_pm_ops, + }, + .id_table = tavil_id_table, + .probe = wcd9xxx_i2c_probe, + .remove = wcd9xxx_i2c_remove, +}; + +int wcd9xxx_init(void) +{ + int ret[NUM_WCD9XXX_REG_RET] = {0}; + int i = 0; + + wcd9xxx_set_intf_type(WCD9XXX_INTERFACE_TYPE_PROBING); + + ret[0] = i2c_add_driver(&tabla_i2c_driver); + if (ret[0]) + pr_err("%s: Failed to add the tabla2x I2C driver: %d\n", + __func__, ret[0]); + + ret[1] = i2c_add_driver(&wcd9xxx_i2c_driver); + if (ret[1]) + pr_err("%s: Failed to add the wcd9xxx I2C driver: %d\n", + __func__, ret[1]); + + ret[2] = i2c_add_driver(&wcd9335_i2c_driver); + if (ret[2]) + pr_err("%s: Failed to add the wcd9335 I2C driver: %d\n", + __func__, ret[2]); + + ret[3] = slim_driver_register(&wcd_slim_driver); + if (ret[3]) + pr_err("%s: Failed to register wcd SB driver: %d\n", + __func__, ret[3]); + + ret[4] = i2c_add_driver(&wcd934x_i2c_driver); + if (ret[4]) + pr_err("%s: Failed to add the wcd934x I2C driver: %d\n", + __func__, ret[4]); + + for (i = 0; i < NUM_WCD9XXX_REG_RET; i++) { + if (ret[i]) + return ret[i]; + } + + return 0; +} + +void wcd9xxx_exit(void) +{ + wcd9xxx_set_intf_type(WCD9XXX_INTERFACE_TYPE_PROBING); + + i2c_del_driver(&tabla_i2c_driver); + i2c_del_driver(&wcd9xxx_i2c_driver); + i2c_del_driver(&wcd9335_i2c_driver); + i2c_del_driver(&wcd934x_i2c_driver); + slim_driver_unregister(&wcd_slim_driver); +} + +MODULE_DESCRIPTION("Codec core driver"); +MODULE_LICENSE("GPL v2"); diff --git a/audio-kernel/asoc/codecs/wcd9xxx-irq.c b/audio-kernel/asoc/codecs/wcd9xxx-irq.c new file mode 100644 index 000000000..ad15470a3 --- /dev/null +++ b/audio-kernel/asoc/codecs/wcd9xxx-irq.c @@ -0,0 +1,896 @@ +/* Copyright (c) 2011-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 + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "core.h" +#include "wcd9xxx-irq.h" + +#define BYTE_BIT_MASK(nr) (1UL << ((nr) % BITS_PER_BYTE)) +#define BIT_BYTE(nr) ((nr) / BITS_PER_BYTE) + +#define WCD9XXX_SYSTEM_RESUME_TIMEOUT_MS 100 + +#ifndef NO_IRQ +#define NO_IRQ (-1) +#endif + +#ifdef CONFIG_OF +struct wcd9xxx_irq_drv_data { + struct irq_domain *domain; + int irq; +}; +#endif + +static int virq_to_phyirq( + struct wcd9xxx_core_resource *wcd9xxx_res, int virq); +static int phyirq_to_virq( + struct wcd9xxx_core_resource *wcd9xxx_res, int irq); +static unsigned int wcd9xxx_irq_get_upstream_irq( + struct wcd9xxx_core_resource *wcd9xxx_res); +static void wcd9xxx_irq_put_downstream_irq( + struct wcd9xxx_core_resource *wcd9xxx_res); +static void wcd9xxx_irq_put_upstream_irq( + struct wcd9xxx_core_resource *wcd9xxx_res); +static int wcd9xxx_map_irq( + struct wcd9xxx_core_resource *wcd9xxx_res, int irq); + +static void wcd9xxx_irq_lock(struct irq_data *data) +{ + struct wcd9xxx_core_resource *wcd9xxx_res = + irq_data_get_irq_chip_data(data); + mutex_lock(&wcd9xxx_res->irq_lock); +} + +static void wcd9xxx_irq_sync_unlock(struct irq_data *data) +{ + struct wcd9xxx_core_resource *wcd9xxx_res = + irq_data_get_irq_chip_data(data); + int i; + + if ((ARRAY_SIZE(wcd9xxx_res->irq_masks_cur) > + WCD9XXX_MAX_IRQ_REGS) || + (ARRAY_SIZE(wcd9xxx_res->irq_masks_cache) > + WCD9XXX_MAX_IRQ_REGS)) { + pr_err("%s: Array Size out of bound\n", __func__); + return; + } + if (!wcd9xxx_res->wcd_core_regmap) { + pr_err("%s: Codec core regmap not defined\n", + __func__); + return; + } + + for (i = 0; i < ARRAY_SIZE(wcd9xxx_res->irq_masks_cur); i++) { + /* If there's been a change in the mask write it back + * to the hardware. + */ + if (wcd9xxx_res->irq_masks_cur[i] != + wcd9xxx_res->irq_masks_cache[i]) { + + wcd9xxx_res->irq_masks_cache[i] = + wcd9xxx_res->irq_masks_cur[i]; + regmap_write(wcd9xxx_res->wcd_core_regmap, + wcd9xxx_res->intr_reg[WCD9XXX_INTR_MASK_BASE] + i, + wcd9xxx_res->irq_masks_cur[i]); + } + } + + mutex_unlock(&wcd9xxx_res->irq_lock); +} + +static void wcd9xxx_irq_enable(struct irq_data *data) +{ + struct wcd9xxx_core_resource *wcd9xxx_res = + irq_data_get_irq_chip_data(data); + int wcd9xxx_irq = virq_to_phyirq(wcd9xxx_res, data->irq); + int byte = BIT_BYTE(wcd9xxx_irq); + int size = ARRAY_SIZE(wcd9xxx_res->irq_masks_cur); + + if ((byte < size) && (byte >= 0)) { + wcd9xxx_res->irq_masks_cur[byte] &= + ~(BYTE_BIT_MASK(wcd9xxx_irq)); + } else { + pr_err("%s: Array size is %d but index is %d: Out of range\n", + __func__, size, byte); + } +} + +static void wcd9xxx_irq_disable(struct irq_data *data) +{ + struct wcd9xxx_core_resource *wcd9xxx_res = + irq_data_get_irq_chip_data(data); + int wcd9xxx_irq = virq_to_phyirq(wcd9xxx_res, data->irq); + int byte = BIT_BYTE(wcd9xxx_irq); + int size = ARRAY_SIZE(wcd9xxx_res->irq_masks_cur); + + if ((byte < size) && (byte >= 0)) { + wcd9xxx_res->irq_masks_cur[byte] + |= BYTE_BIT_MASK(wcd9xxx_irq); + } else { + pr_err("%s: Array size is %d but index is %d: Out of range\n", + __func__, size, byte); + } +} + +static void wcd9xxx_irq_ack(struct irq_data *data) +{ + int wcd9xxx_irq = 0; + struct wcd9xxx_core_resource *wcd9xxx_res = + irq_data_get_irq_chip_data(data); + + if (wcd9xxx_res == NULL) { + pr_err("%s: wcd9xxx_res is NULL\n", __func__); + return; + } + wcd9xxx_irq = virq_to_phyirq(wcd9xxx_res, data->irq); + pr_debug("%s: IRQ_ACK called for WCD9XXX IRQ: %d\n", + __func__, wcd9xxx_irq); +} + +static void wcd9xxx_irq_mask(struct irq_data *d) +{ + /* do nothing but required as linux calls irq_mask without NULL check */ +} + +static struct irq_chip wcd9xxx_irq_chip = { + .name = "wcd9xxx", + .irq_bus_lock = wcd9xxx_irq_lock, + .irq_bus_sync_unlock = wcd9xxx_irq_sync_unlock, + .irq_disable = wcd9xxx_irq_disable, + .irq_enable = wcd9xxx_irq_enable, + .irq_mask = wcd9xxx_irq_mask, + .irq_ack = wcd9xxx_irq_ack, +}; + +bool wcd9xxx_lock_sleep( + struct wcd9xxx_core_resource *wcd9xxx_res) +{ + enum wcd9xxx_pm_state os; + + /* + * wcd9xxx_{lock/unlock}_sleep will be called by wcd9xxx_irq_thread + * and its subroutines only motly. + * but btn0_lpress_fn is not wcd9xxx_irq_thread's subroutine and + * It can race with wcd9xxx_irq_thread. + * So need to embrace wlock_holders with mutex. + * + * If system didn't resume, we can simply return false so codec driver's + * IRQ handler can return without handling IRQ. + * As interrupt line is still active, codec will have another IRQ to + * retry shortly. + */ + mutex_lock(&wcd9xxx_res->pm_lock); + if (wcd9xxx_res->wlock_holders++ == 0) { + pr_debug("%s: holding wake lock\n", __func__); + pm_qos_update_request(&wcd9xxx_res->pm_qos_req, + msm_cpuidle_get_deep_idle_latency()); + pm_stay_awake(wcd9xxx_res->dev); + } + mutex_unlock(&wcd9xxx_res->pm_lock); + + if (!wait_event_timeout(wcd9xxx_res->pm_wq, + ((os = wcd9xxx_pm_cmpxchg(wcd9xxx_res, + WCD9XXX_PM_SLEEPABLE, + WCD9XXX_PM_AWAKE)) == + WCD9XXX_PM_SLEEPABLE || + (os == WCD9XXX_PM_AWAKE)), + msecs_to_jiffies( + WCD9XXX_SYSTEM_RESUME_TIMEOUT_MS))) { + pr_warn("%s: system didn't resume within %dms, s %d, w %d\n", + __func__, + WCD9XXX_SYSTEM_RESUME_TIMEOUT_MS, wcd9xxx_res->pm_state, + wcd9xxx_res->wlock_holders); + wcd9xxx_unlock_sleep(wcd9xxx_res); + return false; + } + wake_up_all(&wcd9xxx_res->pm_wq); + return true; +} +EXPORT_SYMBOL(wcd9xxx_lock_sleep); + +void wcd9xxx_unlock_sleep( + struct wcd9xxx_core_resource *wcd9xxx_res) +{ + mutex_lock(&wcd9xxx_res->pm_lock); + if (--wcd9xxx_res->wlock_holders == 0) { + pr_debug("%s: releasing wake lock pm_state %d -> %d\n", + __func__, wcd9xxx_res->pm_state, WCD9XXX_PM_SLEEPABLE); + /* + * if wcd9xxx_lock_sleep failed, pm_state would be still + * WCD9XXX_PM_ASLEEP, don't overwrite + */ + if (likely(wcd9xxx_res->pm_state == WCD9XXX_PM_AWAKE)) + wcd9xxx_res->pm_state = WCD9XXX_PM_SLEEPABLE; + pm_qos_update_request(&wcd9xxx_res->pm_qos_req, + PM_QOS_DEFAULT_VALUE); + pm_relax(wcd9xxx_res->dev); + } + mutex_unlock(&wcd9xxx_res->pm_lock); + wake_up_all(&wcd9xxx_res->pm_wq); +} +EXPORT_SYMBOL(wcd9xxx_unlock_sleep); + +void wcd9xxx_nested_irq_lock(struct wcd9xxx_core_resource *wcd9xxx_res) +{ + mutex_lock(&wcd9xxx_res->nested_irq_lock); +} + +void wcd9xxx_nested_irq_unlock(struct wcd9xxx_core_resource *wcd9xxx_res) +{ + mutex_unlock(&wcd9xxx_res->nested_irq_lock); +} + + +static void wcd9xxx_irq_dispatch(struct wcd9xxx_core_resource *wcd9xxx_res, + struct intr_data *irqdata) +{ + int irqbit = irqdata->intr_num; + + if (!wcd9xxx_res->wcd_core_regmap) { + pr_err("%s: codec core regmap not defined\n", + __func__); + return; + } + + if (irqdata->clear_first) { + wcd9xxx_nested_irq_lock(wcd9xxx_res); + regmap_write(wcd9xxx_res->wcd_core_regmap, + wcd9xxx_res->intr_reg[WCD9XXX_INTR_CLEAR_BASE] + + BIT_BYTE(irqbit), + BYTE_BIT_MASK(irqbit)); + + if (wcd9xxx_get_intf_type() == WCD9XXX_INTERFACE_TYPE_I2C) + regmap_write(wcd9xxx_res->wcd_core_regmap, + wcd9xxx_res->intr_reg[WCD9XXX_INTR_CLR_COMMIT], + 0x02); + handle_nested_irq(phyirq_to_virq(wcd9xxx_res, irqbit)); + wcd9xxx_nested_irq_unlock(wcd9xxx_res); + } else { + wcd9xxx_nested_irq_lock(wcd9xxx_res); + handle_nested_irq(phyirq_to_virq(wcd9xxx_res, irqbit)); + regmap_write(wcd9xxx_res->wcd_core_regmap, + wcd9xxx_res->intr_reg[WCD9XXX_INTR_CLEAR_BASE] + + BIT_BYTE(irqbit), + BYTE_BIT_MASK(irqbit)); + if (wcd9xxx_get_intf_type() == WCD9XXX_INTERFACE_TYPE_I2C) + regmap_write(wcd9xxx_res->wcd_core_regmap, + wcd9xxx_res->intr_reg[WCD9XXX_INTR_CLR_COMMIT], + 0x02); + + wcd9xxx_nested_irq_unlock(wcd9xxx_res); + } +} + +static irqreturn_t wcd9xxx_irq_thread(int irq, void *data) +{ + int ret; + int i; + struct intr_data irqdata; + char linebuf[128]; + static DEFINE_RATELIMIT_STATE(ratelimit, 5 * HZ, 1); + struct wcd9xxx_core_resource *wcd9xxx_res = data; + int num_irq_regs = wcd9xxx_res->num_irq_regs; + struct wcd9xxx *wcd9xxx; + u8 status[4], status1[4] = {0}, unmask_status[4] = {0}; + + if (unlikely(wcd9xxx_lock_sleep(wcd9xxx_res) == false)) { + dev_err(wcd9xxx_res->dev, "Failed to hold suspend\n"); + return IRQ_NONE; + } + + if (!wcd9xxx_res->wcd_core_regmap) { + dev_err(wcd9xxx_res->dev, + "%s: Codec core regmap not supplied\n", + __func__); + goto err_disable_irq; + } + + wcd9xxx = (struct wcd9xxx *)wcd9xxx_res->parent; + if (!wcd9xxx) { + dev_err(wcd9xxx_res->dev, + "%s: Codec core not supplied\n", __func__); + goto err_disable_irq; + } + + if (!wcd9xxx->dev_up) { + dev_info_ratelimited(wcd9xxx_res->dev, "wcd9xxx dev not up\n"); + /* + * sleep to not block the core when device is + * not up (slimbus will not be available) to + * process interrupts. + */ + msleep(10); + } + + memset(status, 0, sizeof(status)); + ret = regmap_bulk_read(wcd9xxx_res->wcd_core_regmap, + wcd9xxx_res->intr_reg[WCD9XXX_INTR_STATUS_BASE], + status, num_irq_regs); + + if (ret < 0) { + dev_err(wcd9xxx_res->dev, + "Failed to read interrupt status: %d\n", ret); + goto err_disable_irq; + } + /* + * If status is 0 return without clearing. + * status contains: HW status - masked interrupts + * status1 contains: unhandled interrupts - masked interrupts + * unmasked_status contains: unhandled interrupts + */ + if (unlikely(!memcmp(status, status1, sizeof(status)))) { + pr_debug("%s: status is 0\n", __func__); + wcd9xxx_unlock_sleep(wcd9xxx_res); + return IRQ_HANDLED; + } + + /* + * Copy status to unmask_status before masking, otherwise SW may miss + * to clear masked interrupt in corner case. + */ + memcpy(unmask_status, status, sizeof(unmask_status)); + + /* Apply masking */ + for (i = 0; i < num_irq_regs; i++) + status[i] &= ~wcd9xxx_res->irq_masks_cur[i]; + + memcpy(status1, status, sizeof(status1)); + + /* Find out which interrupt was triggered and call that interrupt's + * handler function + * + * Since codec has only one hardware irq line which is shared by + * codec's different internal interrupts, so it's possible master irq + * handler dispatches multiple nested irq handlers after breaking + * order. Dispatch interrupts in the order that is maintained by + * the interrupt table. + */ + for (i = 0; i < wcd9xxx_res->intr_table_size; i++) { + irqdata = wcd9xxx_res->intr_table[i]; + if (status[BIT_BYTE(irqdata.intr_num)] & + BYTE_BIT_MASK(irqdata.intr_num)) { + wcd9xxx_irq_dispatch(wcd9xxx_res, &irqdata); + status1[BIT_BYTE(irqdata.intr_num)] &= + ~BYTE_BIT_MASK(irqdata.intr_num); + unmask_status[BIT_BYTE(irqdata.intr_num)] &= + ~BYTE_BIT_MASK(irqdata.intr_num); + } + } + + /* + * As a failsafe if unhandled irq is found, clear it to prevent + * interrupt storm. + * Note that we can say there was an unhandled irq only when no irq + * handled by nested irq handler since Taiko supports qdsp as irqs' + * destination for few irqs. Therefore driver shouldn't clear pending + * irqs when few handled while few others not. + */ + if (unlikely(!memcmp(status, status1, sizeof(status)))) { + if (__ratelimit(&ratelimit)) { + pr_warn("%s: Unhandled irq found\n", __func__); + hex_dump_to_buffer(status, sizeof(status), 16, 1, + linebuf, sizeof(linebuf), false); + pr_warn("%s: status0 : %s\n", __func__, linebuf); + hex_dump_to_buffer(status1, sizeof(status1), 16, 1, + linebuf, sizeof(linebuf), false); + pr_warn("%s: status1 : %s\n", __func__, linebuf); + } + /* + * unmask_status contains unhandled interrupts, hence clear all + * unhandled interrupts. + */ + ret = regmap_bulk_write(wcd9xxx_res->wcd_core_regmap, + wcd9xxx_res->intr_reg[WCD9XXX_INTR_CLEAR_BASE], + unmask_status, num_irq_regs); + if (wcd9xxx_get_intf_type() == WCD9XXX_INTERFACE_TYPE_I2C) + regmap_write(wcd9xxx_res->wcd_core_regmap, + wcd9xxx_res->intr_reg[WCD9XXX_INTR_CLR_COMMIT], + 0x02); + } + wcd9xxx_unlock_sleep(wcd9xxx_res); + + return IRQ_HANDLED; + +err_disable_irq: + dev_err(wcd9xxx_res->dev, + "Disable irq %d\n", wcd9xxx_res->irq); + + disable_irq_wake(wcd9xxx_res->irq); + disable_irq_nosync(wcd9xxx_res->irq); + wcd9xxx_unlock_sleep(wcd9xxx_res); + return IRQ_NONE; +} + +/** + * wcd9xxx_free_irq + * + * @wcd9xxx_res: pointer to core resource + * irq: irq number + * @data: data pointer + * + */ +void wcd9xxx_free_irq(struct wcd9xxx_core_resource *wcd9xxx_res, + int irq, void *data) +{ + free_irq(phyirq_to_virq(wcd9xxx_res, irq), data); +} +EXPORT_SYMBOL(wcd9xxx_free_irq); + +/** + * wcd9xxx_enable_irq + * + * @wcd9xxx_res: pointer to core resource + * irq: irq number + * + */ +void wcd9xxx_enable_irq(struct wcd9xxx_core_resource *wcd9xxx_res, int irq) +{ + if (wcd9xxx_res->irq) + enable_irq(phyirq_to_virq(wcd9xxx_res, irq)); +} +EXPORT_SYMBOL(wcd9xxx_enable_irq); + +/** + * wcd9xxx_disable_irq + * + * @wcd9xxx_res: pointer to core resource + * irq: irq number + * + */ +void wcd9xxx_disable_irq(struct wcd9xxx_core_resource *wcd9xxx_res, int irq) +{ + if (wcd9xxx_res->irq) + disable_irq_nosync(phyirq_to_virq(wcd9xxx_res, irq)); +} +EXPORT_SYMBOL(wcd9xxx_disable_irq); + +/** + * wcd9xxx_disable_irq_sync + * + * @wcd9xxx_res: pointer to core resource + * irq: irq number + * + */ +void wcd9xxx_disable_irq_sync( + struct wcd9xxx_core_resource *wcd9xxx_res, int irq) +{ + if (wcd9xxx_res->irq) + disable_irq(phyirq_to_virq(wcd9xxx_res, irq)); +} +EXPORT_SYMBOL(wcd9xxx_disable_irq_sync); + +static int wcd9xxx_irq_setup_downstream_irq( + struct wcd9xxx_core_resource *wcd9xxx_res) +{ + int irq, virq, ret; + + pr_debug("%s: enter\n", __func__); + + for (irq = 0; irq < wcd9xxx_res->num_irqs; irq++) { + /* Map OF irq */ + virq = wcd9xxx_map_irq(wcd9xxx_res, irq); + pr_debug("%s: irq %d -> %d\n", __func__, irq, virq); + if (virq == NO_IRQ) { + pr_err("%s, No interrupt specifier for irq %d\n", + __func__, irq); + return NO_IRQ; + } + + ret = irq_set_chip_data(virq, wcd9xxx_res); + if (ret) { + pr_err("%s: Failed to configure irq %d (%d)\n", + __func__, irq, ret); + return ret; + } + + if (wcd9xxx_res->irq_level_high[irq]) + irq_set_chip_and_handler(virq, &wcd9xxx_irq_chip, + handle_level_irq); + else + irq_set_chip_and_handler(virq, &wcd9xxx_irq_chip, + handle_edge_irq); + + irq_set_nested_thread(virq, 1); + } + + pr_debug("%s: leave\n", __func__); + + return 0; +} + +/** + * wcd9xxx_irq_init + * + * @wcd9xxx_res: pointer to core resource + * + * Returns 0 on success, appropriate error code otherwise + */ +int wcd9xxx_irq_init(struct wcd9xxx_core_resource *wcd9xxx_res) +{ + int i, ret; + u8 irq_level[wcd9xxx_res->num_irq_regs]; + struct irq_domain *domain; + struct device_node *pnode; + + mutex_init(&wcd9xxx_res->irq_lock); + mutex_init(&wcd9xxx_res->nested_irq_lock); + + pnode = of_irq_find_parent(wcd9xxx_res->dev->of_node); + if (unlikely(!pnode)) + return -EINVAL; + + domain = irq_find_host(pnode); + if (unlikely(!domain)) + return -EINVAL; + + wcd9xxx_res->domain = domain; + + wcd9xxx_res->irq = wcd9xxx_irq_get_upstream_irq(wcd9xxx_res); + if (!wcd9xxx_res->irq) { + pr_warn("%s: irq driver is not yet initialized\n", __func__); + mutex_destroy(&wcd9xxx_res->irq_lock); + mutex_destroy(&wcd9xxx_res->nested_irq_lock); + return -EPROBE_DEFER; + } + pr_debug("%s: probed irq %d\n", __func__, wcd9xxx_res->irq); + + /* Setup downstream IRQs */ + ret = wcd9xxx_irq_setup_downstream_irq(wcd9xxx_res); + if (ret) { + pr_err("%s: Failed to setup downstream IRQ\n", __func__); + wcd9xxx_irq_put_upstream_irq(wcd9xxx_res); + mutex_destroy(&wcd9xxx_res->irq_lock); + mutex_destroy(&wcd9xxx_res->nested_irq_lock); + return ret; + } + + /* All other wcd9xxx interrupts are edge triggered */ + wcd9xxx_res->irq_level_high[0] = true; + + /* mask all the interrupts */ + memset(irq_level, 0, wcd9xxx_res->num_irq_regs); + for (i = 0; i < wcd9xxx_res->num_irqs; i++) { + wcd9xxx_res->irq_masks_cur[BIT_BYTE(i)] |= BYTE_BIT_MASK(i); + wcd9xxx_res->irq_masks_cache[BIT_BYTE(i)] |= BYTE_BIT_MASK(i); + irq_level[BIT_BYTE(i)] |= + wcd9xxx_res->irq_level_high[i] << (i % BITS_PER_BYTE); + } + + if (!wcd9xxx_res->wcd_core_regmap) { + dev_err(wcd9xxx_res->dev, + "%s: Codec core regmap not defined\n", + __func__); + ret = -EINVAL; + goto fail_irq_init; + } + + for (i = 0; i < wcd9xxx_res->num_irq_regs; i++) { + /* Initialize interrupt mask and level registers */ + regmap_write(wcd9xxx_res->wcd_core_regmap, + wcd9xxx_res->intr_reg[WCD9XXX_INTR_LEVEL_BASE] + i, + irq_level[i]); + regmap_write(wcd9xxx_res->wcd_core_regmap, + wcd9xxx_res->intr_reg[WCD9XXX_INTR_MASK_BASE] + i, + wcd9xxx_res->irq_masks_cur[i]); + } + + ret = request_threaded_irq(wcd9xxx_res->irq, NULL, wcd9xxx_irq_thread, + IRQF_TRIGGER_HIGH | IRQF_ONESHOT, + "wcd9xxx", wcd9xxx_res); + if (ret != 0) + dev_err(wcd9xxx_res->dev, "Failed to request IRQ %d: %d\n", + wcd9xxx_res->irq, ret); + else { + ret = enable_irq_wake(wcd9xxx_res->irq); + if (ret) + dev_err(wcd9xxx_res->dev, + "Failed to set wake interrupt on IRQ %d: %d\n", + wcd9xxx_res->irq, ret); + if (ret) + free_irq(wcd9xxx_res->irq, wcd9xxx_res); + } + + if (ret) + goto fail_irq_init; + + return ret; + +fail_irq_init: + dev_err(wcd9xxx_res->dev, + "%s: Failed to init wcd9xxx irq\n", __func__); + wcd9xxx_irq_put_upstream_irq(wcd9xxx_res); + mutex_destroy(&wcd9xxx_res->irq_lock); + mutex_destroy(&wcd9xxx_res->nested_irq_lock); + return ret; +} +EXPORT_SYMBOL(wcd9xxx_irq_init); + +int wcd9xxx_request_irq(struct wcd9xxx_core_resource *wcd9xxx_res, + int irq, irq_handler_t handler, + const char *name, void *data) +{ + int virq; + + virq = phyirq_to_virq(wcd9xxx_res, irq); + + return request_threaded_irq(virq, NULL, handler, IRQF_TRIGGER_RISING, + name, data); +} +EXPORT_SYMBOL(wcd9xxx_request_irq); + +void wcd9xxx_irq_exit(struct wcd9xxx_core_resource *wcd9xxx_res) +{ + dev_dbg(wcd9xxx_res->dev, "%s: Cleaning up irq %d\n", __func__, + wcd9xxx_res->irq); + + if (wcd9xxx_res->irq) { + disable_irq_wake(wcd9xxx_res->irq); + free_irq(wcd9xxx_res->irq, wcd9xxx_res); + wcd9xxx_res->irq = 0; + wcd9xxx_irq_put_downstream_irq(wcd9xxx_res); + wcd9xxx_irq_put_upstream_irq(wcd9xxx_res); + } + mutex_destroy(&wcd9xxx_res->irq_lock); + mutex_destroy(&wcd9xxx_res->nested_irq_lock); +} + +#ifndef CONFIG_OF +static int phyirq_to_virq( + struct wcd9xxx_core_resource *wcd9xxx_res, + int offset) +{ + return wcd9xxx_res->irq_base + offset; +} + +static int virq_to_phyirq( + struct wcd9xxx_core_resource *wcd9xxx_res, + int virq) +{ + return virq - wcd9xxx_res->irq_base; +} + +static unsigned int wcd9xxx_irq_get_upstream_irq( + struct wcd9xxx_core_resource *wcd9xxx_res) +{ + return wcd9xxx_res->irq; +} + +static void wcd9xxx_irq_put_upstream_irq( + struct wcd9xxx_core_resource *wcd9xxx_res) +{ + /* Do nothing */ +} + +static int wcd9xxx_map_irq( + struct wcd9xxx_core_resource *wcd9xxx_core_res, int irq) +{ + return phyirq_to_virq(wcd9xxx_core_res, irq); +} +#else +static struct wcd9xxx_irq_drv_data * +wcd9xxx_irq_add_domain(struct device_node *node, + struct device_node *parent) +{ + struct wcd9xxx_irq_drv_data *data = NULL; + + pr_debug("%s: node %s, node parent %s\n", __func__, + node->name, node->parent->name); + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) + return NULL; + + /* + * wcd9xxx_intc interrupt controller supports N to N irq mapping with + * single cell binding with irq numbers(offsets) only. + * Use irq_domain_simple_ops that has irq_domain_simple_map and + * irq_domain_xlate_onetwocell. + */ + data->domain = irq_domain_add_linear(node, WCD9XXX_MAX_NUM_IRQS, + &irq_domain_simple_ops, data); + if (!data->domain) { + kfree(data); + return NULL; + } + + return data; +} + +static struct wcd9xxx_irq_drv_data * +wcd9xxx_get_irq_drv_d(const struct wcd9xxx_core_resource *wcd9xxx_res) +{ + struct irq_domain *domain; + + domain = wcd9xxx_res->domain; + + if (domain) + return domain->host_data; + else + return NULL; +} + +static int phyirq_to_virq(struct wcd9xxx_core_resource *wcd9xxx_res, int offset) +{ + struct wcd9xxx_irq_drv_data *data; + + data = wcd9xxx_get_irq_drv_d(wcd9xxx_res); + if (!data) { + pr_warn("%s: not registered to interrupt controller\n", + __func__); + return -EINVAL; + } + return irq_linear_revmap(data->domain, offset); +} + +static int virq_to_phyirq(struct wcd9xxx_core_resource *wcd9xxx_res, int virq) +{ + struct irq_data *irq_data = irq_get_irq_data(virq); + + if (unlikely(!irq_data)) { + pr_err("%s: irq_data is NULL", __func__); + return -EINVAL; + } + return irq_data->hwirq; +} + +static unsigned int wcd9xxx_irq_get_upstream_irq( + struct wcd9xxx_core_resource *wcd9xxx_res) +{ + struct wcd9xxx_irq_drv_data *data; + + data = wcd9xxx_get_irq_drv_d(wcd9xxx_res); + if (!data) { + pr_err("%s: interrupt controller is not registered\n", + __func__); + return 0; + } + + /* Make sure data is updated before return. */ + rmb(); + return data->irq; +} + +static void wcd9xxx_irq_put_downstream_irq( + struct wcd9xxx_core_resource *wcd9xxx_res) +{ + int irq, virq, ret; + + /* + * IRQ migration hits error if the chip data and handles + * are not made NULL. make associated data and handles + * to NULL at irq_exit + */ + for (irq = 0; irq < wcd9xxx_res->num_irqs; irq++) { + virq = wcd9xxx_map_irq(wcd9xxx_res, irq); + pr_debug("%s: irq %d -> %d\n", __func__, irq, virq); + ret = irq_set_chip_data(virq, NULL); + if (ret) { + pr_err("%s: Failed to configure irq %d (%d)\n", + __func__, irq, ret); + return; + } + irq_set_chip_and_handler(virq, NULL, NULL); + } +} + +static void wcd9xxx_irq_put_upstream_irq( + struct wcd9xxx_core_resource *wcd9xxx_res) +{ + wcd9xxx_res->domain = NULL; +} + +static int wcd9xxx_map_irq(struct wcd9xxx_core_resource *wcd9xxx_res, int irq) +{ + return of_irq_to_resource(wcd9xxx_res->dev->of_node, irq, NULL); +} + +static int wcd9xxx_irq_probe(struct platform_device *pdev) +{ + int irq, dir_apps_irq = -EINVAL; + struct wcd9xxx_irq_drv_data *data; + struct device_node *node = pdev->dev.of_node; + int ret = -EINVAL; + + irq = of_get_named_gpio(node, "qcom,gpio-connect", 0); + if (!gpio_is_valid(irq)) + dir_apps_irq = platform_get_irq_byname(pdev, "wcd_irq"); + + if (!gpio_is_valid(irq) && dir_apps_irq < 0) { + dev_err(&pdev->dev, "TLMM connect gpio not found\n"); + return -EPROBE_DEFER; + } + if (dir_apps_irq > 0) { + irq = dir_apps_irq; + } else { + irq = gpio_to_irq(irq); + if (irq < 0) { + dev_err(&pdev->dev, "Unable to configure irq\n"); + return irq; + } + } + dev_dbg(&pdev->dev, "%s: virq = %d\n", __func__, irq); + data = wcd9xxx_irq_add_domain(node, node->parent); + if (!data) { + pr_err("%s: irq_add_domain failed\n", __func__); + return -EINVAL; + } + data->irq = irq; + + /* Make sure irq is saved before return. */ + wmb(); + ret = 0; + + return ret; +} + +static int wcd9xxx_irq_remove(struct platform_device *pdev) +{ + struct irq_domain *domain; + struct wcd9xxx_irq_drv_data *data; + + domain = irq_find_host(pdev->dev.of_node); + if (unlikely(!domain)) { + pr_err("%s: domain is NULL", __func__); + return -EINVAL; + } + data = (struct wcd9xxx_irq_drv_data *)domain->host_data; + data->irq = 0; + + /* Make sure irq variable is updated in data, before irq removal. */ + wmb(); + irq_domain_remove(data->domain); + kfree(data); + + return 0; +} + +static const struct of_device_id of_match[] = { + { .compatible = "qcom,wcd9xxx-irq" }, + { } +}; + +static struct platform_driver wcd9xxx_irq_driver = { + .probe = wcd9xxx_irq_probe, + .remove = wcd9xxx_irq_remove, + .driver = { + .name = "wcd9xxx_intc", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(of_match), + }, +}; + +int wcd9xxx_irq_drv_init(void) +{ + return platform_driver_register(&wcd9xxx_irq_driver); +} + +void wcd9xxx_irq_drv_exit(void) +{ + platform_driver_unregister(&wcd9xxx_irq_driver); +} +#endif /* CONFIG_OF */ diff --git a/audio-kernel/asoc/codecs/wcd9xxx-irq.h b/audio-kernel/asoc/codecs/wcd9xxx-irq.h new file mode 100644 index 000000000..dfe0b929a --- /dev/null +++ b/audio-kernel/asoc/codecs/wcd9xxx-irq.h @@ -0,0 +1,37 @@ +/* Copyright (c) 2016-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 + * 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 +#include "core.h" + +#ifndef __MFD_WCD9XXX_IRQ_H +#define __MFD_WCD9XXX_IRQ_H +bool wcd9xxx_lock_sleep(struct wcd9xxx_core_resource *wcd9xxx_res); +void wcd9xxx_unlock_sleep(struct wcd9xxx_core_resource *wcd9xxx_res); +void wcd9xxx_nested_irq_lock(struct wcd9xxx_core_resource *wcd9xxx_res); +void wcd9xxx_nested_irq_unlock(struct wcd9xxx_core_resource *wcd9xxx_res); +int wcd9xxx_request_irq(struct wcd9xxx_core_resource *wcd9xxx_res, int irq, + irq_handler_t handler, const char *name, void *data); + +void wcd9xxx_free_irq(struct wcd9xxx_core_resource *wcd9xxx_res, + int irq, void *data); +void wcd9xxx_enable_irq(struct wcd9xxx_core_resource *wcd9xxx_res, int irq); +void wcd9xxx_disable_irq(struct wcd9xxx_core_resource *wcd9xxx_res, + int irq); +void wcd9xxx_disable_irq_sync(struct wcd9xxx_core_resource *wcd9xxx_res, + int irq); + +int wcd9xxx_irq_init(struct wcd9xxx_core_resource *wcd9xxx_res); +void wcd9xxx_irq_exit(struct wcd9xxx_core_resource *wcd9xxx_res); +int wcd9xxx_irq_drv_init(void); +void wcd9xxx_irq_drv_exit(void); +#endif diff --git a/audio-kernel/asoc/codecs/wcd9xxx-regmap.h b/audio-kernel/asoc/codecs/wcd9xxx-regmap.h new file mode 100644 index 000000000..18e0e58b5 --- /dev/null +++ b/audio-kernel/asoc/codecs/wcd9xxx-regmap.h @@ -0,0 +1,73 @@ +/* + * 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 + * 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 _WCD9XXX_REGMAP_ +#define _WCD9XXX_REGMAP_ + +#include +#include "core.h" + +typedef int (*regmap_patch_fptr)(struct regmap *, int); + +extern struct regmap_config wcd9360_regmap_config; + +extern struct regmap_config wcd934x_regmap_config; +extern int wcd934x_regmap_register_patch(struct regmap *regmap, + int version); + +extern struct regmap_config wcd9335_regmap_config; +extern int wcd9335_regmap_register_patch(struct regmap *regmap, + int version); + +static inline struct regmap_config *wcd9xxx_get_regmap_config(int type) +{ + struct regmap_config *regmap_config; + + switch (type) { + case WCD9360: + regmap_config = &wcd9360_regmap_config; + break; + case WCD934X: + regmap_config = &wcd934x_regmap_config; + break; + case WCD9335: + regmap_config = &wcd9335_regmap_config; + break; + default: + regmap_config = NULL; + break; + }; + + return regmap_config; +} + +static inline regmap_patch_fptr wcd9xxx_get_regmap_reg_patch(int type) +{ + regmap_patch_fptr apply_patch; + + switch (type) { + case WCD9335: + apply_patch = wcd9335_regmap_register_patch; + break; + case WCD934X: + apply_patch = wcd934x_regmap_register_patch; + break; + default: + apply_patch = NULL; + break; + } + + return apply_patch; +} + +#endif diff --git a/audio-kernel/asoc/codecs/wcd9xxx-resmgr-v2.c b/audio-kernel/asoc/codecs/wcd9xxx-resmgr-v2.c new file mode 100644 index 000000000..d390558ce --- /dev/null +++ b/audio-kernel/asoc/codecs/wcd9xxx-resmgr-v2.c @@ -0,0 +1,694 @@ +/* + * 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 + * 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 +#include +#include +#include +#include +#include "wcd9xxx-resmgr-v2.h" +#include "core.h" + +#define WCD9XXX_RCO_CALIBRATION_DELAY_INC_US 5000 +/* This register is valid only for WCD9335 */ +#define WCD93XX_ANA_CLK_TOP 0x0602 + +#define WCD93XX_ANA_BIAS 0x0601 +#define WCD93XX_CDC_CLK_RST_CTRL_MCLK_CONTROL 0x0d41 +#define WCD93XX_CDC_CLK_RST_CTRL_FS_CNT_CONTROL 0x0d42 +#define WCD93XX_CLK_SYS_MCLK_PRG 0x711 +#define WCD93XX_CODEC_RPM_CLK_GATE 0x002 +#define WCD93XX_ANA_RCO 0x603 +#define WCD93XX_ANA_BUCK_CTL 0x606 + +static const char *wcd_resmgr_clk_type_to_str(enum wcd_clock_type clk_type) +{ + if (clk_type == WCD_CLK_OFF) + return "WCD_CLK_OFF"; + else if (clk_type == WCD_CLK_RCO) + return "WCD_CLK_RCO"; + else if (clk_type == WCD_CLK_MCLK) + return "WCD_CLK_MCLK"; + else + return "WCD_CLK_UNDEFINED"; +} + +static int wcd_resmgr_codec_reg_update_bits(struct wcd9xxx_resmgr_v2 *resmgr, + u16 reg, u8 mask, u8 val) +{ + bool change; + int ret; + + if (resmgr->codec_type != WCD9335) { + /* Tavil and Pahu does not support ANA_CLK_TOP register */ + if (reg == WCD93XX_ANA_CLK_TOP) + return 0; + } else { + /* Tasha does not support CLK_SYS_MCLK_PRG register */ + if (reg == WCD93XX_CLK_SYS_MCLK_PRG) + return 0; + } + if (resmgr->codec) { + ret = snd_soc_update_bits(resmgr->codec, reg, mask, val); + } else if (resmgr->core_res->wcd_core_regmap) { + ret = regmap_update_bits_check( + resmgr->core_res->wcd_core_regmap, + reg, mask, val, &change); + if (!ret) + ret = change; + } else { + pr_err("%s: codec/regmap not defined\n", __func__); + ret = -EINVAL; + } + + return ret; +} + +static int wcd_resmgr_codec_reg_read(struct wcd9xxx_resmgr_v2 *resmgr, + unsigned int reg) +{ + int val, ret; + + if (resmgr->codec_type != WCD9335) { + if (reg == WCD93XX_ANA_CLK_TOP) + return 0; + } else { + if (reg == WCD93XX_CLK_SYS_MCLK_PRG) + return 0; + } + if (resmgr->codec) { + val = snd_soc_read(resmgr->codec, reg); + } else if (resmgr->core_res->wcd_core_regmap) { + ret = regmap_read(resmgr->core_res->wcd_core_regmap, + reg, &val); + if (ret) + val = ret; + } else { + pr_err("%s: wcd regmap is null\n", __func__); + return -EINVAL; + } + + return val; +} + +/* + * wcd_resmgr_get_clk_type() + * Returns clk type that is currently enabled + */ +int wcd_resmgr_get_clk_type(struct wcd9xxx_resmgr_v2 *resmgr) +{ + if (!resmgr) { + pr_err("%s: resmgr not initialized\n", __func__); + return -EINVAL; + } + return resmgr->clk_type; +} +EXPORT_SYMBOL(wcd_resmgr_get_clk_type); + +static void wcd_resmgr_cdc_specific_get_clk(struct wcd9xxx_resmgr_v2 *resmgr, + int clk_users) +{ + /* Caller of this function should have acquired BG_CLK lock */ + if (clk_users) { + if (resmgr->resmgr_cb && + resmgr->resmgr_cb->cdc_rco_ctrl) { + while (clk_users--) + resmgr->resmgr_cb->cdc_rco_ctrl(resmgr->codec, + true); + } + } +} + +/* + * wcd_resmgr_post_ssr_v2 + * @resmgr: handle to struct wcd9xxx_resmgr_v2 + */ +void wcd_resmgr_post_ssr_v2(struct wcd9xxx_resmgr_v2 *resmgr) +{ + int old_bg_audio_users; + int old_clk_rco_users, old_clk_mclk_users; + + WCD9XXX_V2_BG_CLK_LOCK(resmgr); + + old_bg_audio_users = resmgr->master_bias_users; + old_clk_mclk_users = resmgr->clk_mclk_users; + old_clk_rco_users = resmgr->clk_rco_users; + resmgr->master_bias_users = 0; + resmgr->clk_mclk_users = 0; + resmgr->clk_rco_users = 0; + resmgr->clk_type = WCD_CLK_OFF; + + pr_debug("%s: old_bg_audio_users=%d old_clk_mclk_users=%d old_clk_rco_users=%d\n", + __func__, old_bg_audio_users, + old_clk_mclk_users, old_clk_rco_users); + + if (old_bg_audio_users) { + while (old_bg_audio_users--) + wcd_resmgr_enable_master_bias(resmgr); + } + + if (old_clk_mclk_users) { + while (old_clk_mclk_users--) + wcd_resmgr_enable_clk_block(resmgr, WCD_CLK_MCLK); + } + + if (old_clk_rco_users) + wcd_resmgr_cdc_specific_get_clk(resmgr, old_clk_rco_users); + + WCD9XXX_V2_BG_CLK_UNLOCK(resmgr); +} +EXPORT_SYMBOL(wcd_resmgr_post_ssr_v2); + +/* + * wcd_resmgr_enable_master_bias: enable codec master bias + * @resmgr: handle to struct wcd9xxx_resmgr_v2 + */ +int wcd_resmgr_enable_master_bias(struct wcd9xxx_resmgr_v2 *resmgr) +{ + mutex_lock(&resmgr->master_bias_lock); + + resmgr->master_bias_users++; + if (resmgr->master_bias_users == 1) { + wcd_resmgr_codec_reg_update_bits(resmgr, WCD93XX_ANA_BIAS, + 0x80, 0x80); + wcd_resmgr_codec_reg_update_bits(resmgr, WCD93XX_ANA_BIAS, + 0x40, 0x40); + /* + * 1ms delay is required after pre-charge is enabled + * as per HW requirement + */ + usleep_range(1000, 1100); + wcd_resmgr_codec_reg_update_bits(resmgr, WCD93XX_ANA_BIAS, + 0x40, 0x00); + wcd_resmgr_codec_reg_update_bits(resmgr, + WCD93XX_ANA_BIAS, 0x20, 0x00); + } + + pr_debug("%s: current master bias users: %d\n", __func__, + resmgr->master_bias_users); + + mutex_unlock(&resmgr->master_bias_lock); + return 0; +} +EXPORT_SYMBOL(wcd_resmgr_enable_master_bias); + +/* + * wcd_resmgr_disable_master_bias: disable codec master bias + * @resmgr: handle to struct wcd9xxx_resmgr_v2 + */ +int wcd_resmgr_disable_master_bias(struct wcd9xxx_resmgr_v2 *resmgr) +{ + mutex_lock(&resmgr->master_bias_lock); + if (resmgr->master_bias_users <= 0) { + mutex_unlock(&resmgr->master_bias_lock); + return -EINVAL; + } + + resmgr->master_bias_users--; + if (resmgr->master_bias_users == 0) { + wcd_resmgr_codec_reg_update_bits(resmgr, WCD93XX_ANA_BIAS, + 0x80, 0x00); + wcd_resmgr_codec_reg_update_bits(resmgr, + WCD93XX_ANA_BIAS, 0x20, 0x00); + } + mutex_unlock(&resmgr->master_bias_lock); + return 0; +} +EXPORT_SYMBOL(wcd_resmgr_disable_master_bias); + +static int wcd_resmgr_enable_clk_mclk(struct wcd9xxx_resmgr_v2 *resmgr) +{ + /* Enable mclk requires master bias to be enabled first */ + if (resmgr->master_bias_users <= 0) { + pr_err("%s: Cannot turn on MCLK, BG is not enabled\n", + __func__); + return -EINVAL; + } + + if (((resmgr->clk_mclk_users == 0) && + (resmgr->clk_type == WCD_CLK_MCLK)) || + ((resmgr->clk_mclk_users > 0) && + (resmgr->clk_type != WCD_CLK_MCLK))) { + pr_err("%s: Error enabling MCLK, clk_type: %s\n", + __func__, + wcd_resmgr_clk_type_to_str(resmgr->clk_type)); + return -EINVAL; + } + + if (++resmgr->clk_mclk_users == 1) { + wcd_resmgr_codec_reg_update_bits(resmgr, + WCD93XX_ANA_CLK_TOP, 0x80, 0x80); + wcd_resmgr_codec_reg_update_bits(resmgr, + WCD93XX_ANA_CLK_TOP, 0x08, 0x00); + wcd_resmgr_codec_reg_update_bits(resmgr, + WCD93XX_ANA_CLK_TOP, 0x04, 0x04); + if (resmgr->codec_type != WCD9335) { + /* + * In tavil clock contrl register is changed + * to CLK_SYS_MCLK_PRG + */ + wcd_resmgr_codec_reg_update_bits(resmgr, + WCD93XX_CLK_SYS_MCLK_PRG, 0x80, 0x80); + wcd_resmgr_codec_reg_update_bits(resmgr, + WCD93XX_CLK_SYS_MCLK_PRG, 0x30, 0x10); + wcd_resmgr_codec_reg_update_bits(resmgr, + WCD93XX_CLK_SYS_MCLK_PRG, 0x02, 0x00); + wcd_resmgr_codec_reg_update_bits(resmgr, + WCD93XX_CLK_SYS_MCLK_PRG, 0x01, 0x01); + wcd_resmgr_codec_reg_update_bits(resmgr, + WCD93XX_CLK_SYS_MCLK_PRG, 0x02, 0x00); + wcd_resmgr_codec_reg_update_bits(resmgr, + WCD93XX_CDC_CLK_RST_CTRL_FS_CNT_CONTROL, + 0x01, 0x01); + wcd_resmgr_codec_reg_update_bits(resmgr, + WCD93XX_CDC_CLK_RST_CTRL_MCLK_CONTROL, + 0x01, 0x01); + wcd_resmgr_codec_reg_update_bits(resmgr, + WCD93XX_CODEC_RPM_CLK_GATE, 0x03, 0x00); + } else { + wcd_resmgr_codec_reg_update_bits(resmgr, + WCD93XX_CDC_CLK_RST_CTRL_FS_CNT_CONTROL, + 0x01, 0x01); + wcd_resmgr_codec_reg_update_bits(resmgr, + WCD93XX_CDC_CLK_RST_CTRL_MCLK_CONTROL, + 0x01, 0x01); + } + /* + * 10us sleep is required after clock is enabled + * as per HW requirement + */ + usleep_range(10, 15); + } + + resmgr->clk_type = WCD_CLK_MCLK; + + pr_debug("%s: mclk_users: %d, clk_type: %s\n", __func__, + resmgr->clk_mclk_users, + wcd_resmgr_clk_type_to_str(resmgr->clk_type)); + + return 0; +} + +static int wcd_resmgr_disable_clk_mclk(struct wcd9xxx_resmgr_v2 *resmgr) +{ + if (resmgr->clk_mclk_users <= 0) { + pr_err("%s: No mclk users, cannot disable mclk\n", __func__); + return -EINVAL; + } + + if (--resmgr->clk_mclk_users == 0) { + if (resmgr->clk_rco_users > 0) { + /* MCLK to RCO switch */ + wcd_resmgr_codec_reg_update_bits(resmgr, + WCD93XX_ANA_CLK_TOP, + 0x08, 0x08); + wcd_resmgr_codec_reg_update_bits(resmgr, + WCD93XX_CLK_SYS_MCLK_PRG, 0x02, 0x02); + /* Disable clock buffer */ + wcd_resmgr_codec_reg_update_bits(resmgr, + WCD93XX_CLK_SYS_MCLK_PRG, 0x80, 0x00); + resmgr->clk_type = WCD_CLK_RCO; + } else { + wcd_resmgr_codec_reg_update_bits(resmgr, + WCD93XX_ANA_CLK_TOP, + 0x04, 0x00); + wcd_resmgr_codec_reg_update_bits(resmgr, + WCD93XX_CLK_SYS_MCLK_PRG, 0x81, 0x00); + resmgr->clk_type = WCD_CLK_OFF; + } + + wcd_resmgr_codec_reg_update_bits(resmgr, WCD93XX_ANA_CLK_TOP, + 0x80, 0x00); + } + + if ((resmgr->codec_type != WCD9335) && + (resmgr->clk_type == WCD_CLK_OFF)) + wcd_resmgr_set_sido_input_src(resmgr, SIDO_SOURCE_INTERNAL); + + pr_debug("%s: mclk_users: %d, clk_type: %s\n", __func__, + resmgr->clk_mclk_users, + wcd_resmgr_clk_type_to_str(resmgr->clk_type)); + + return 0; +} + +static void wcd_resmgr_set_buck_accuracy(struct wcd9xxx_resmgr_v2 *resmgr) +{ + wcd_resmgr_codec_reg_update_bits(resmgr, WCD93XX_ANA_BUCK_CTL, + 0x02, 0x02); + /* 100us sleep needed after HIGH_ACCURACY_PRE_EN1 */ + usleep_range(100, 110); + wcd_resmgr_codec_reg_update_bits(resmgr, WCD93XX_ANA_BUCK_CTL, + 0x01, 0x01); + /* 100us sleep needed after HIGH_ACCURACY_PRE_EN2 */ + usleep_range(100, 110); + wcd_resmgr_codec_reg_update_bits(resmgr, WCD93XX_ANA_BUCK_CTL, + 0x04, 0x04); + /* 100us sleep needed after HIGH_ACCURACY_EN */ + usleep_range(100, 110); +} + +static int wcd_resmgr_enable_clk_rco(struct wcd9xxx_resmgr_v2 *resmgr) +{ + bool rco_cal_done = true; + + resmgr->clk_rco_users++; + if ((resmgr->clk_rco_users == 1) && + ((resmgr->clk_type == WCD_CLK_OFF) || + (resmgr->clk_mclk_users == 0))) { + pr_warn("%s: RCO enable requires MCLK to be ON first\n", + __func__); + resmgr->clk_rco_users--; + return -EINVAL; + } else if ((resmgr->clk_rco_users == 1) && + (resmgr->clk_mclk_users)) { + /* RCO Enable */ + if (resmgr->sido_input_src == SIDO_SOURCE_INTERNAL) { + wcd_resmgr_codec_reg_update_bits(resmgr, + WCD93XX_ANA_RCO, + 0x80, 0x80); + if (resmgr->codec_type != WCD9335) + wcd_resmgr_set_buck_accuracy(resmgr); + } + + /* + * 20us required after RCO BG is enabled as per HW + * requirements + */ + usleep_range(20, 25); + wcd_resmgr_codec_reg_update_bits(resmgr, WCD93XX_ANA_RCO, + 0x40, 0x40); + /* + * 20us required after RCO is enabled as per HW + * requirements + */ + usleep_range(20, 25); + /* RCO Calibration */ + wcd_resmgr_codec_reg_update_bits(resmgr, WCD93XX_ANA_RCO, + 0x04, 0x04); + if (resmgr->codec_type != WCD9335) + /* + * For wcd934x and wcd936x codecs, 20us sleep is needed + * after enabling RCO calibration + */ + usleep_range(20, 25); + + wcd_resmgr_codec_reg_update_bits(resmgr, WCD93XX_ANA_RCO, + 0x04, 0x00); + if (resmgr->codec_type != WCD9335) + /* + * For wcd934x and wcd936x codecs, 20us sleep is needed + * after disabling RCO calibration + */ + usleep_range(20, 25); + + /* RCO calibration takes app. 5ms to complete */ + usleep_range(WCD9XXX_RCO_CALIBRATION_DELAY_INC_US, + WCD9XXX_RCO_CALIBRATION_DELAY_INC_US + 100); + if (wcd_resmgr_codec_reg_read(resmgr, WCD93XX_ANA_RCO) & 0x02) + rco_cal_done = false; + + WARN((!rco_cal_done), "RCO Calibration failed\n"); + + /* Switch MUX to RCO */ + if (resmgr->clk_mclk_users == 1) { + wcd_resmgr_codec_reg_update_bits(resmgr, + WCD93XX_ANA_CLK_TOP, + 0x08, 0x08); + wcd_resmgr_codec_reg_update_bits(resmgr, + WCD93XX_CLK_SYS_MCLK_PRG, + 0x02, 0x02); + resmgr->clk_type = WCD_CLK_RCO; + } + } + pr_debug("%s: rco clk users: %d, clk_type: %s\n", __func__, + resmgr->clk_rco_users, + wcd_resmgr_clk_type_to_str(resmgr->clk_type)); + + return 0; +} + +static int wcd_resmgr_disable_clk_rco(struct wcd9xxx_resmgr_v2 *resmgr) +{ + if ((resmgr->clk_rco_users <= 0) || + (resmgr->clk_type == WCD_CLK_OFF)) { + pr_err("%s: rco_clk_users = %d, clk_type = %d, cannot disable\n", + __func__, resmgr->clk_rco_users, resmgr->clk_type); + return -EINVAL; + } + + resmgr->clk_rco_users--; + + if ((resmgr->clk_rco_users == 0) && + (resmgr->clk_type == WCD_CLK_RCO)) { + wcd_resmgr_codec_reg_update_bits(resmgr, WCD93XX_ANA_CLK_TOP, + 0x08, 0x00); + wcd_resmgr_codec_reg_update_bits(resmgr, + WCD93XX_CLK_SYS_MCLK_PRG, + 0x02, 0x00); + wcd_resmgr_codec_reg_update_bits(resmgr, WCD93XX_ANA_CLK_TOP, + 0x04, 0x00); + wcd_resmgr_codec_reg_update_bits(resmgr, WCD93XX_ANA_RCO, + 0x40, 0x00); + if (resmgr->sido_input_src == SIDO_SOURCE_INTERNAL) + wcd_resmgr_codec_reg_update_bits(resmgr, + WCD93XX_ANA_RCO, + 0x80, 0x00); + wcd_resmgr_codec_reg_update_bits(resmgr, + WCD93XX_CLK_SYS_MCLK_PRG, + 0x01, 0x00); + resmgr->clk_type = WCD_CLK_OFF; + } else if ((resmgr->clk_rco_users == 0) && + (resmgr->clk_mclk_users)) { + /* Disable RCO while MCLK is ON */ + wcd_resmgr_codec_reg_update_bits(resmgr, WCD93XX_ANA_RCO, + 0x40, 0x00); + if (resmgr->sido_input_src == SIDO_SOURCE_INTERNAL) + wcd_resmgr_codec_reg_update_bits(resmgr, + WCD93XX_ANA_RCO, + 0x80, 0x00); + } + + if ((resmgr->codec_type != WCD9335) && + (resmgr->clk_type == WCD_CLK_OFF)) + wcd_resmgr_set_sido_input_src(resmgr, SIDO_SOURCE_INTERNAL); + + pr_debug("%s: rco clk users: %d, clk_type: %s\n", __func__, + resmgr->clk_rco_users, + wcd_resmgr_clk_type_to_str(resmgr->clk_type)); + + return 0; +} + +/* + * wcd_resmgr_enable_clk_block: enable MCLK or RCO + * @resmgr: handle to struct wcd9xxx_resmgr_v2 + * @type: Clock type to enable + */ +int wcd_resmgr_enable_clk_block(struct wcd9xxx_resmgr_v2 *resmgr, + enum wcd_clock_type type) +{ + int ret; + + switch (type) { + case WCD_CLK_MCLK: + ret = wcd_resmgr_enable_clk_mclk(resmgr); + break; + case WCD_CLK_RCO: + ret = wcd_resmgr_enable_clk_rco(resmgr); + break; + default: + pr_err("%s: Unknown Clock type: %s\n", __func__, + wcd_resmgr_clk_type_to_str(type)); + ret = -EINVAL; + break; + }; + + if (ret) + pr_err("%s: Enable clock %s failed\n", __func__, + wcd_resmgr_clk_type_to_str(type)); + + return ret; +} +EXPORT_SYMBOL(wcd_resmgr_enable_clk_block); + +void wcd_resmgr_set_sido_input_src(struct wcd9xxx_resmgr_v2 *resmgr, + int sido_src) +{ + if (!resmgr) + return; + + if (sido_src == resmgr->sido_input_src) + return; + + if (sido_src == SIDO_SOURCE_INTERNAL) { + wcd_resmgr_codec_reg_update_bits(resmgr, WCD93XX_ANA_BUCK_CTL, + 0x04, 0x00); + usleep_range(100, 110); + wcd_resmgr_codec_reg_update_bits(resmgr, WCD93XX_ANA_BUCK_CTL, + 0x03, 0x00); + usleep_range(100, 110); + wcd_resmgr_codec_reg_update_bits(resmgr, WCD93XX_ANA_RCO, + 0x80, 0x00); + usleep_range(100, 110); + resmgr->sido_input_src = SIDO_SOURCE_INTERNAL; + pr_debug("%s: sido input src to internal\n", __func__); + } else if (sido_src == SIDO_SOURCE_RCO_BG) { + wcd_resmgr_codec_reg_update_bits(resmgr, WCD93XX_ANA_RCO, + 0x80, 0x80); + usleep_range(100, 110); + wcd_resmgr_codec_reg_update_bits(resmgr, WCD93XX_ANA_BUCK_CTL, + 0x02, 0x02); + usleep_range(100, 110); + wcd_resmgr_codec_reg_update_bits(resmgr, WCD93XX_ANA_BUCK_CTL, + 0x01, 0x01); + usleep_range(100, 110); + wcd_resmgr_codec_reg_update_bits(resmgr, WCD93XX_ANA_BUCK_CTL, + 0x04, 0x04); + usleep_range(100, 110); + resmgr->sido_input_src = SIDO_SOURCE_RCO_BG; + pr_debug("%s: sido input src to external\n", __func__); + } +} +EXPORT_SYMBOL(wcd_resmgr_set_sido_input_src); + +/* + * wcd_resmgr_set_sido_input_src_locked: + * Set SIDO input in BG_CLK locked context + * + * @resmgr: handle to struct wcd9xxx_resmgr_v2 + * @sido_src: Select the SIDO input source + */ +void wcd_resmgr_set_sido_input_src_locked(struct wcd9xxx_resmgr_v2 *resmgr, + int sido_src) +{ + if (!resmgr) + return; + + WCD9XXX_V2_BG_CLK_LOCK(resmgr); + wcd_resmgr_set_sido_input_src(resmgr, sido_src); + WCD9XXX_V2_BG_CLK_UNLOCK(resmgr); +} +EXPORT_SYMBOL(wcd_resmgr_set_sido_input_src_locked); + +/* + * wcd_resmgr_disable_clk_block: disable MCLK or RCO + * @resmgr: handle to struct wcd9xxx_resmgr_v2 + * @type: Clock type to disable + */ +int wcd_resmgr_disable_clk_block(struct wcd9xxx_resmgr_v2 *resmgr, + enum wcd_clock_type type) +{ + int ret; + + switch (type) { + case WCD_CLK_MCLK: + ret = wcd_resmgr_disable_clk_mclk(resmgr); + break; + case WCD_CLK_RCO: + ret = wcd_resmgr_disable_clk_rco(resmgr); + break; + default: + pr_err("%s: Unknown Clock type: %s\n", __func__, + wcd_resmgr_clk_type_to_str(type)); + ret = -EINVAL; + break; + }; + + if (ret) + pr_err("%s: Disable clock %s failed\n", __func__, + wcd_resmgr_clk_type_to_str(type)); + + return ret; +} +EXPORT_SYMBOL(wcd_resmgr_disable_clk_block); + +/* + * wcd_resmgr_init: initialize wcd resource manager + * @core_res: handle to struct wcd9xxx_core_resource + * + * Early init call without a handle to snd_soc_codec * + */ +struct wcd9xxx_resmgr_v2 *wcd_resmgr_init( + struct wcd9xxx_core_resource *core_res, + struct snd_soc_codec *codec) +{ + struct wcd9xxx_resmgr_v2 *resmgr; + struct wcd9xxx *wcd9xxx; + + resmgr = kzalloc(sizeof(struct wcd9xxx_resmgr_v2), GFP_KERNEL); + if (!resmgr) + return ERR_PTR(-ENOMEM); + + wcd9xxx = container_of(core_res, struct wcd9xxx, core_res); + if (!wcd9xxx) { + kfree(resmgr); + pr_err("%s: Cannot get wcd9xx pointer\n", __func__); + return ERR_PTR(-EINVAL); + } + + mutex_init(&resmgr->codec_bg_clk_lock); + mutex_init(&resmgr->master_bias_lock); + resmgr->master_bias_users = 0; + resmgr->clk_mclk_users = 0; + resmgr->clk_rco_users = 0; + resmgr->master_bias_users = 0; + resmgr->codec = codec; + resmgr->core_res = core_res; + resmgr->sido_input_src = SIDO_SOURCE_INTERNAL; + resmgr->codec_type = wcd9xxx->type; + + return resmgr; +} +EXPORT_SYMBOL(wcd_resmgr_init); + +/* + * wcd_resmgr_remove: Clean-up wcd resource manager + * @resmgr: handle to struct wcd9xxx_resmgr_v2 + */ +void wcd_resmgr_remove(struct wcd9xxx_resmgr_v2 *resmgr) +{ + mutex_destroy(&resmgr->master_bias_lock); + kfree(resmgr); +} +EXPORT_SYMBOL(wcd_resmgr_remove); + +/* + * wcd_resmgr_post_init: post init call to assign codec handle + * @resmgr: handle to struct wcd9xxx_resmgr_v2 created during early init + * @resmgr_cb: codec callback function for resmgr + * @codec: handle to struct snd_soc_codec + */ +int wcd_resmgr_post_init(struct wcd9xxx_resmgr_v2 *resmgr, + const struct wcd_resmgr_cb *resmgr_cb, + struct snd_soc_codec *codec) +{ + if (!resmgr) { + pr_err("%s: resmgr not allocated\n", __func__); + return -EINVAL; + } + + if (!codec) { + pr_err("%s: Codec memory is NULL, nothing to post init\n", + __func__); + return -EINVAL; + } + + resmgr->codec = codec; + resmgr->resmgr_cb = resmgr_cb; + + return 0; +} +EXPORT_SYMBOL(wcd_resmgr_post_init); + +MODULE_DESCRIPTION("wcd9xxx resmgr v2 module"); +MODULE_LICENSE("GPL v2"); diff --git a/audio-kernel/asoc/codecs/wcd9xxx-resmgr-v2.h b/audio-kernel/asoc/codecs/wcd9xxx-resmgr-v2.h new file mode 100644 index 000000000..e9d3531e2 --- /dev/null +++ b/audio-kernel/asoc/codecs/wcd9xxx-resmgr-v2.h @@ -0,0 +1,93 @@ +/* + * 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 + * 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 __WCD9XXX_COMMON_V2_H__ +#define __WCD9XXX_COMMON_V2_H__ + +#include +#include "core.h" + +enum wcd_clock_type { + WCD_CLK_OFF, + WCD_CLK_RCO, + WCD_CLK_MCLK, +}; + +enum { + SIDO_SOURCE_INTERNAL, + SIDO_SOURCE_RCO_BG, +}; + +struct wcd_resmgr_cb { + int (*cdc_rco_ctrl)(struct snd_soc_codec *, bool); +}; + +struct wcd9xxx_resmgr_v2 { + struct snd_soc_codec *codec; + struct wcd9xxx_core_resource *core_res; + + int master_bias_users; + int clk_mclk_users; + int clk_rco_users; + + struct mutex codec_bg_clk_lock; + struct mutex master_bias_lock; + + enum codec_variant codec_type; + enum wcd_clock_type clk_type; + + const struct wcd_resmgr_cb *resmgr_cb; + int sido_input_src; +}; + +#define WCD9XXX_V2_BG_CLK_LOCK(resmgr) \ +{ \ + struct wcd9xxx_resmgr_v2 *__resmgr = resmgr; \ + pr_debug("%s: Acquiring BG_CLK\n", __func__); \ + mutex_lock(&__resmgr->codec_bg_clk_lock); \ + pr_debug("%s: Acquiring BG_CLK done\n", __func__); \ +} + +#define WCD9XXX_V2_BG_CLK_UNLOCK(resmgr) \ +{ \ + struct wcd9xxx_resmgr_v2 *__resmgr = resmgr; \ + pr_debug("%s: Releasing BG_CLK\n", __func__); \ + mutex_unlock(&__resmgr->codec_bg_clk_lock); \ +} + +#define WCD9XXX_V2_BG_CLK_ASSERT_LOCKED(resmgr) \ +{ \ + WARN_ONCE(!mutex_is_locked(&resmgr->codec_bg_clk_lock), \ + "%s: BG_CLK lock should have acquired\n", __func__); \ +} + +int wcd_resmgr_enable_master_bias(struct wcd9xxx_resmgr_v2 *resmgr); +int wcd_resmgr_disable_master_bias(struct wcd9xxx_resmgr_v2 *resmgr); +struct wcd9xxx_resmgr_v2 *wcd_resmgr_init( + struct wcd9xxx_core_resource *core_res, + struct snd_soc_codec *codec); +void wcd_resmgr_remove(struct wcd9xxx_resmgr_v2 *resmgr); +int wcd_resmgr_post_init(struct wcd9xxx_resmgr_v2 *resmgr, + const struct wcd_resmgr_cb *resmgr_cb, + struct snd_soc_codec *codec); +int wcd_resmgr_enable_clk_block(struct wcd9xxx_resmgr_v2 *resmgr, + enum wcd_clock_type type); +int wcd_resmgr_disable_clk_block(struct wcd9xxx_resmgr_v2 *resmgr, + enum wcd_clock_type type); +int wcd_resmgr_get_clk_type(struct wcd9xxx_resmgr_v2 *resmgr); +void wcd_resmgr_post_ssr_v2(struct wcd9xxx_resmgr_v2 *resmgr); +void wcd_resmgr_set_sido_input_src_locked(struct wcd9xxx_resmgr_v2 *resmgr, + int sido_src); +void wcd_resmgr_set_sido_input_src(struct wcd9xxx_resmgr_v2 *resmgr, + int sido_src); + +#endif diff --git a/audio-kernel/asoc/codecs/wcd9xxx-rst.c b/audio-kernel/asoc/codecs/wcd9xxx-rst.c new file mode 100644 index 000000000..563dd7850 --- /dev/null +++ b/audio-kernel/asoc/codecs/wcd9xxx-rst.c @@ -0,0 +1,602 @@ +/* Copyright (c) 2017-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 +#include +#include +#include +#include "core.h" +#include "pdata.h" +#include "wcd9xxx-utils.h" +#include "wcd9335_registers.h" +#include "wcd9335_irq.h" +#include +#include "wcd934x/wcd934x_irq.h" +#include +#include "wcd9360/wcd9360-irq.h" + +/* wcd9335 interrupt table */ +static const struct intr_data wcd9335_intr_table[] = { + {WCD9XXX_IRQ_SLIMBUS, false}, + {WCD9335_IRQ_MBHC_SW_DET, true}, + {WCD9335_IRQ_MBHC_BUTTON_PRESS_DET, true}, + {WCD9335_IRQ_MBHC_BUTTON_RELEASE_DET, true}, + {WCD9335_IRQ_MBHC_ELECT_INS_REM_DET, true}, + {WCD9335_IRQ_MBHC_ELECT_INS_REM_LEG_DET, true}, + {WCD9335_IRQ_FLL_LOCK_LOSS, false}, + {WCD9335_IRQ_HPH_PA_CNPL_COMPLETE, false}, + {WCD9335_IRQ_HPH_PA_CNPR_COMPLETE, false}, + {WCD9335_IRQ_EAR_PA_CNP_COMPLETE, false}, + {WCD9335_IRQ_LINE_PA1_CNP_COMPLETE, false}, + {WCD9335_IRQ_LINE_PA2_CNP_COMPLETE, false}, + {WCD9335_IRQ_LINE_PA3_CNP_COMPLETE, false}, + {WCD9335_IRQ_LINE_PA4_CNP_COMPLETE, false}, + {WCD9335_IRQ_HPH_PA_OCPL_FAULT, false}, + {WCD9335_IRQ_HPH_PA_OCPR_FAULT, false}, + {WCD9335_IRQ_EAR_PA_OCP_FAULT, false}, + {WCD9335_IRQ_SOUNDWIRE, false}, + {WCD9335_IRQ_VDD_DIG_RAMP_COMPLETE, false}, + {WCD9335_IRQ_RCO_ERROR, false}, + {WCD9335_IRQ_SVA_ERROR, false}, + {WCD9335_IRQ_MAD_AUDIO, false}, + {WCD9335_IRQ_MAD_BEACON, false}, + {WCD9335_IRQ_SVA_OUTBOX1, true}, + {WCD9335_IRQ_SVA_OUTBOX2, true}, + {WCD9335_IRQ_MAD_ULTRASOUND, false}, + {WCD9335_IRQ_VBAT_ATTACK, false}, + {WCD9335_IRQ_VBAT_RESTORE, false}, +}; + +static const struct intr_data wcd934x_intr_table[] = { + {WCD9XXX_IRQ_SLIMBUS, false}, + {WCD934X_IRQ_MBHC_SW_DET, true}, + {WCD934X_IRQ_MBHC_BUTTON_PRESS_DET, true}, + {WCD934X_IRQ_MBHC_BUTTON_RELEASE_DET, true}, + {WCD934X_IRQ_MBHC_ELECT_INS_REM_DET, true}, + {WCD934X_IRQ_MBHC_ELECT_INS_REM_LEG_DET, true}, + {WCD934X_IRQ_MISC, false}, + {WCD934X_IRQ_HPH_PA_CNPL_COMPLETE, false}, + {WCD934X_IRQ_HPH_PA_CNPR_COMPLETE, false}, + {WCD934X_IRQ_EAR_PA_CNP_COMPLETE, false}, + {WCD934X_IRQ_LINE_PA1_CNP_COMPLETE, false}, + {WCD934X_IRQ_LINE_PA2_CNP_COMPLETE, false}, + {WCD934X_IRQ_SLNQ_ANALOG_ERROR, false}, + {WCD934X_IRQ_RESERVED_3, false}, + {WCD934X_IRQ_HPH_PA_OCPL_FAULT, false}, + {WCD934X_IRQ_HPH_PA_OCPR_FAULT, false}, + {WCD934X_IRQ_EAR_PA_OCP_FAULT, false}, + {WCD934X_IRQ_SOUNDWIRE, false}, + {WCD934X_IRQ_VDD_DIG_RAMP_COMPLETE, false}, + {WCD934X_IRQ_RCO_ERROR, false}, + {WCD934X_IRQ_CPE_ERROR, false}, + {WCD934X_IRQ_MAD_AUDIO, false}, + {WCD934X_IRQ_MAD_BEACON, false}, + {WCD934X_IRQ_CPE1_INTR, true}, + {WCD934X_IRQ_RESERVED_4, false}, + {WCD934X_IRQ_MAD_ULTRASOUND, false}, + {WCD934X_IRQ_VBAT_ATTACK, false}, + {WCD934X_IRQ_VBAT_RESTORE, false}, +}; + +static const struct intr_data wcd9360_intr_table[] = { + {WCD9XXX_IRQ_SLIMBUS, false}, + {WCD9360_IRQ_MISC, false}, + {WCD9360_IRQ_LDO_RXTX_SCD, false}, + {WCD9360_IRQ_EAR_PA_SCD, false}, + {WCD9360_IRQ_AUX_PA_SCD, false}, + {WCD9360_IRQ_EAR_PA_CNP_COMPLETE, false}, + {WCD9360_IRQ_AUX_PA_CNP_COMPLETE, false}, + {WCD9360_IRQ_RESERVED_3, false}, + {WCD9360_IRQ_SOUNDWIRE, false}, + {WCD9360_IRQ_RCO_ERROR, false}, + {WCD9360_IRQ_CPE_ERROR, false}, + {WCD9360_IRQ_MAD_AUDIO, false}, + {WCD9360_IRQ_MAD_BEACON, false}, + {WCD9360_IRQ_CPE1_INTR, true}, + {WCD9360_IRQ_RESERVED_4, false}, + {WCD9360_IRQ_MAD_ULTRASOUND, false}, +}; + +/* + * wcd9335_bring_down: Bringdown WCD Codec + * + * @wcd9xxx: Pointer to wcd9xxx structure + * + * Returns 0 for success or negative error code for failure + */ +static int wcd9335_bring_down(struct wcd9xxx *wcd9xxx) +{ + if (!wcd9xxx || !wcd9xxx->regmap) + return -EINVAL; + + regmap_write(wcd9xxx->regmap, WCD9335_CODEC_RPM_PWR_CDC_DIG_HM_CTL, + 0x04); + + return 0; +} + +/* + * wcd9335_bring_up: Bringup WCD Codec + * + * @wcd9xxx: Pointer to the wcd9xxx structure + * + * Returns 0 for success or negative error code for failure + */ +static int wcd9335_bring_up(struct wcd9xxx *wcd9xxx) +{ + int ret = 0; + int val, byte0; + struct regmap *wcd_regmap; + + if (!wcd9xxx) + return -EINVAL; + + if (!wcd9xxx->regmap) { + dev_err(wcd9xxx->dev, "%s: wcd9xxx regmap is null!\n", + __func__); + return -EINVAL; + } + wcd_regmap = wcd9xxx->regmap; + + regmap_read(wcd_regmap, WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT0, &val); + regmap_read(wcd_regmap, WCD9335_CHIP_TIER_CTRL_CHIP_ID_BYTE0, &byte0); + + if ((val < 0) || (byte0 < 0)) { + dev_err(wcd9xxx->dev, "%s: tasha codec version detection fail!\n", + __func__); + return -EINVAL; + } + if ((val & 0x80) && (byte0 == 0x0)) { + dev_info(wcd9xxx->dev, "%s: wcd9335 codec version is v1.1\n", + __func__); + regmap_write(wcd_regmap, WCD9335_CODEC_RPM_RST_CTL, 0x01); + regmap_write(wcd_regmap, WCD9335_SIDO_SIDO_CCL_2, 0xFC); + regmap_write(wcd_regmap, WCD9335_SIDO_SIDO_CCL_4, 0x21); + regmap_write(wcd_regmap, WCD9335_CODEC_RPM_PWR_CDC_DIG_HM_CTL, + 0x5); + regmap_write(wcd_regmap, WCD9335_CODEC_RPM_PWR_CDC_DIG_HM_CTL, + 0x7); + regmap_write(wcd_regmap, WCD9335_CODEC_RPM_PWR_CDC_DIG_HM_CTL, + 0x3); + regmap_write(wcd_regmap, WCD9335_CODEC_RPM_RST_CTL, 0x3); + } else if (byte0 == 0x1) { + dev_info(wcd9xxx->dev, "%s: wcd9335 codec version is v2.0\n", + __func__); + regmap_write(wcd_regmap, WCD9335_CODEC_RPM_RST_CTL, 0x01); + regmap_write(wcd_regmap, WCD9335_SIDO_SIDO_TEST_2, 0x00); + regmap_write(wcd_regmap, WCD9335_SIDO_SIDO_CCL_8, 0x6F); + regmap_write(wcd_regmap, WCD9335_BIAS_VBG_FINE_ADJ, 0x65); + regmap_write(wcd_regmap, WCD9335_CODEC_RPM_PWR_CDC_DIG_HM_CTL, + 0x5); + regmap_write(wcd_regmap, WCD9335_CODEC_RPM_PWR_CDC_DIG_HM_CTL, + 0x7); + regmap_write(wcd_regmap, WCD9335_CODEC_RPM_PWR_CDC_DIG_HM_CTL, + 0x3); + regmap_write(wcd_regmap, WCD9335_CODEC_RPM_RST_CTL, 0x3); + } else if ((byte0 == 0) && (!(val & 0x80))) { + dev_info(wcd9xxx->dev, "%s: wcd9335 codec version is v1.0\n", + __func__); + regmap_write(wcd_regmap, WCD9335_CODEC_RPM_RST_CTL, 0x01); + regmap_write(wcd_regmap, WCD9335_SIDO_SIDO_CCL_2, 0xFC); + regmap_write(wcd_regmap, WCD9335_SIDO_SIDO_CCL_4, 0x21); + regmap_write(wcd_regmap, WCD9335_CODEC_RPM_PWR_CDC_DIG_HM_CTL, + 0x3); + regmap_write(wcd_regmap, WCD9335_CODEC_RPM_RST_CTL, 0x3); + } else { + dev_err(wcd9xxx->dev, "%s: tasha codec version unknown\n", + __func__); + ret = -EINVAL; + } + + return ret; +} + +/* + * wcd9335_get_cdc_info: Get codec specific information + * + * @wcd9xxx: pointer to wcd9xxx structure + * @wcd_type: pointer to wcd9xxx_codec_type structure + * + * Returns 0 for success or negative error code for failure + */ +static int wcd9335_get_cdc_info(struct wcd9xxx *wcd9xxx, + struct wcd9xxx_codec_type *wcd_type) +{ + u16 id_minor, id_major; + struct regmap *wcd_regmap; + int rc, val, version = 0; + + if (!wcd9xxx || !wcd_type) + return -EINVAL; + + if (!wcd9xxx->regmap) { + dev_err(wcd9xxx->dev, "%s: wcd9xxx regmap is null!\n", + __func__); + return -EINVAL; + } + wcd_regmap = wcd9xxx->regmap; + + rc = regmap_bulk_read(wcd_regmap, WCD9335_CHIP_TIER_CTRL_CHIP_ID_BYTE0, + (u8 *)&id_minor, sizeof(u16)); + if (rc) + return -EINVAL; + + rc = regmap_bulk_read(wcd_regmap, WCD9335_CHIP_TIER_CTRL_CHIP_ID_BYTE2, + (u8 *)&id_major, sizeof(u16)); + if (rc) + return -EINVAL; + + dev_info(wcd9xxx->dev, "%s: wcd9xxx chip id major 0x%x, minor 0x%x\n", + __func__, id_major, id_minor); + + /* Version detection */ + if (id_major == TASHA_MAJOR) { + regmap_read(wcd_regmap, WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT0, + &val); + version = ((u8)val & 0x80) >> 7; + } else if (id_major == TASHA2P0_MAJOR) + version = 2; + else + dev_err(wcd9xxx->dev, "%s: wcd9335 version unknown (major 0x%x, minor 0x%x)\n", + __func__, id_major, id_minor); + + /* Fill codec type info */ + wcd_type->id_major = id_major; + wcd_type->id_minor = id_minor; + wcd_type->num_irqs = WCD9335_NUM_IRQS; + wcd_type->version = version; + wcd_type->slim_slave_type = WCD9XXX_SLIM_SLAVE_ADDR_TYPE_1; + wcd_type->i2c_chip_status = 0x01; + wcd_type->intr_tbl = wcd9335_intr_table; + wcd_type->intr_tbl_size = ARRAY_SIZE(wcd9335_intr_table); + + wcd_type->intr_reg[WCD9XXX_INTR_STATUS_BASE] = + WCD9335_INTR_PIN1_STATUS0; + wcd_type->intr_reg[WCD9XXX_INTR_CLEAR_BASE] = + WCD9335_INTR_PIN1_CLEAR0; + wcd_type->intr_reg[WCD9XXX_INTR_MASK_BASE] = + WCD9335_INTR_PIN1_MASK0; + wcd_type->intr_reg[WCD9XXX_INTR_LEVEL_BASE] = + WCD9335_INTR_LEVEL0; + wcd_type->intr_reg[WCD9XXX_INTR_CLR_COMMIT] = + WCD9335_INTR_CLR_COMMIT; + + return rc; +} + +/* + * wcd934x_bring_down: Bringdown WCD Codec + * + * @wcd9xxx: Pointer to wcd9xxx structure + * + * Returns 0 for success or negative error code for failure + */ +static int wcd934x_bring_down(struct wcd9xxx *wcd9xxx) +{ + if (!wcd9xxx || !wcd9xxx->regmap) + return -EINVAL; + + regmap_write(wcd9xxx->regmap, WCD934X_CODEC_RPM_PWR_CDC_DIG_HM_CTL, + 0x04); + + return 0; +} + +/* + * wcd934x_bring_up: Bringup WCD Codec + * + * @wcd9xxx: Pointer to the wcd9xxx structure + * + * Returns 0 for success or negative error code for failure + */ +static int wcd934x_bring_up(struct wcd9xxx *wcd9xxx) +{ + struct regmap *wcd_regmap; + + if (!wcd9xxx) + return -EINVAL; + + if (!wcd9xxx->regmap) { + dev_err(wcd9xxx->dev, "%s: wcd9xxx regmap is null!\n", + __func__); + return -EINVAL; + } + wcd_regmap = wcd9xxx->regmap; + + regmap_write(wcd_regmap, WCD934X_CODEC_RPM_RST_CTL, 0x01); + regmap_write(wcd_regmap, WCD934X_SIDO_NEW_VOUT_A_STARTUP, 0x19); + regmap_write(wcd_regmap, WCD934X_SIDO_NEW_VOUT_D_STARTUP, 0x15); + /* Add 1msec delay for VOUT to settle */ + usleep_range(1000, 1100); + regmap_write(wcd_regmap, WCD934X_CODEC_RPM_PWR_CDC_DIG_HM_CTL, 0x5); + regmap_write(wcd_regmap, WCD934X_CODEC_RPM_PWR_CDC_DIG_HM_CTL, 0x7); + regmap_write(wcd_regmap, WCD934X_CODEC_RPM_RST_CTL, 0x3); + regmap_write(wcd_regmap, WCD934X_CODEC_RPM_RST_CTL, 0x7); + regmap_write(wcd_regmap, WCD934X_CODEC_RPM_PWR_CDC_DIG_HM_CTL, 0x3); + + return 0; +} + +/* + * wcd934x_get_cdc_info: Get codec specific information + * + * @wcd9xxx: pointer to wcd9xxx structure + * @wcd_type: pointer to wcd9xxx_codec_type structure + * + * Returns 0 for success or negative error code for failure + */ +static int wcd934x_get_cdc_info(struct wcd9xxx *wcd9xxx, + struct wcd9xxx_codec_type *wcd_type) +{ + u16 id_minor, id_major; + struct regmap *wcd_regmap; + int rc, version = -1; + + if (!wcd9xxx || !wcd_type) + return -EINVAL; + + if (!wcd9xxx->regmap) { + dev_err(wcd9xxx->dev, "%s: wcd9xxx regmap is null\n", __func__); + return -EINVAL; + } + wcd_regmap = wcd9xxx->regmap; + + rc = regmap_bulk_read(wcd_regmap, WCD934X_CHIP_TIER_CTRL_CHIP_ID_BYTE0, + (u8 *)&id_minor, sizeof(u16)); + if (rc) + return -EINVAL; + + rc = regmap_bulk_read(wcd_regmap, WCD934X_CHIP_TIER_CTRL_CHIP_ID_BYTE2, + (u8 *)&id_major, sizeof(u16)); + if (rc) + return -EINVAL; + + dev_info(wcd9xxx->dev, "%s: wcd9xxx chip id major 0x%x, minor 0x%x\n", + __func__, id_major, id_minor); + + if (id_major != TAVIL_MAJOR) + goto version_unknown; + + /* + * As fine version info cannot be retrieved before tavil probe. + * Assign coarse versions for possible future use before tavil probe. + */ + if (id_minor == cpu_to_le16(0)) + version = TAVIL_VERSION_1_0; + else if (id_minor == cpu_to_le16(0x01)) + version = TAVIL_VERSION_1_1; + +version_unknown: + if (version < 0) + dev_err(wcd9xxx->dev, "%s: wcd934x version unknown\n", + __func__); + + /* Fill codec type info */ + wcd_type->id_major = id_major; + wcd_type->id_minor = id_minor; + wcd_type->num_irqs = WCD934X_NUM_IRQS; + wcd_type->version = version; + wcd_type->slim_slave_type = WCD9XXX_SLIM_SLAVE_ADDR_TYPE_1; + wcd_type->i2c_chip_status = 0x01; + wcd_type->intr_tbl = wcd934x_intr_table; + wcd_type->intr_tbl_size = ARRAY_SIZE(wcd934x_intr_table); + + wcd_type->intr_reg[WCD9XXX_INTR_STATUS_BASE] = + WCD934X_INTR_PIN1_STATUS0; + wcd_type->intr_reg[WCD9XXX_INTR_CLEAR_BASE] = + WCD934X_INTR_PIN1_CLEAR0; + wcd_type->intr_reg[WCD9XXX_INTR_MASK_BASE] = + WCD934X_INTR_PIN1_MASK0; + wcd_type->intr_reg[WCD9XXX_INTR_LEVEL_BASE] = + WCD934X_INTR_LEVEL0; + wcd_type->intr_reg[WCD9XXX_INTR_CLR_COMMIT] = + WCD934X_INTR_CLR_COMMIT; + + return rc; +} + +/* + * wcd9360_bring_down: Bringdown WCD Codec + * + * @wcd9xxx: Pointer to wcd9xxx structure + * + * Returns 0 for success or negative error code for failure + */ +static int wcd9360_bring_down(struct wcd9xxx *wcd9xxx) +{ + if (!wcd9xxx || !wcd9xxx->regmap) + return -EINVAL; + + regmap_write(wcd9xxx->regmap, WCD9360_CODEC_RPM_PWR_CDC_DIG_HM_CTL, + 0x04); + + return 0; +} + +/* + * wcd9360_bring_up: Bringup WCD Codec + * + * @wcd9xxx: Pointer to the wcd9xxx structure + * + * Returns 0 for success or negative error code for failure + */ +static int wcd9360_bring_up(struct wcd9xxx *wcd9xxx) +{ + struct regmap *wcd_regmap; + + if (!wcd9xxx) + return -EINVAL; + + if (!wcd9xxx->regmap) { + dev_err(wcd9xxx->dev, "%s: wcd9xxx regmap is null!\n", + __func__); + return -EINVAL; + } + wcd_regmap = wcd9xxx->regmap; + + regmap_write(wcd_regmap, WCD9360_CODEC_RPM_RST_CTL, 0x01); + regmap_write(wcd_regmap, WCD9360_SIDO_NEW_VOUT_A_STARTUP, 0x19); + regmap_write(wcd_regmap, WCD9360_SIDO_NEW_VOUT_D_STARTUP, 0x15); + /* Add 1msec delay for VOUT to settle */ + usleep_range(1000, 1100); + regmap_write(wcd_regmap, WCD9360_CODEC_RPM_PWR_CDC_DIG_HM_CTL, 0x5); + regmap_write(wcd_regmap, WCD9360_CODEC_RPM_PWR_CDC_DIG_HM_CTL, 0x7); + regmap_write(wcd_regmap, WCD9360_CODEC_RPM_RST_CTL, 0x3); + regmap_write(wcd_regmap, WCD9360_CODEC_RPM_RST_CTL, 0x7); + regmap_write(wcd_regmap, WCD9360_CODEC_RPM_PWR_CDC_DIG_HM_CTL, 0x3); + + return 0; +} + +/* + * wcd9360_get_cdc_info: Get codec specific information + * + * @wcd9xxx: pointer to wcd9xxx structure + * @wcd_type: pointer to wcd9xxx_codec_type structure + * + * Returns 0 for success or negative error code for failure + */ +static int wcd9360_get_cdc_info(struct wcd9xxx *wcd9xxx, + struct wcd9xxx_codec_type *wcd_type) +{ + u16 id_minor, id_major; + struct regmap *wcd_regmap; + int rc, version = -1; + + if (!wcd9xxx || !wcd_type) + return -EINVAL; + + if (!wcd9xxx->regmap) { + dev_err(wcd9xxx->dev, "%s: wcd9xxx regmap is null\n", __func__); + return -EINVAL; + } + wcd_regmap = wcd9xxx->regmap; + + rc = regmap_bulk_read(wcd_regmap, WCD9360_CHIP_TIER_CTRL_CHIP_ID_BYTE0, + (u8 *)&id_minor, sizeof(u16)); + if (rc) + return -EINVAL; + + rc = regmap_bulk_read(wcd_regmap, WCD9360_CHIP_TIER_CTRL_CHIP_ID_BYTE2, + (u8 *)&id_major, sizeof(u16)); + if (rc) + return -EINVAL; + + dev_info(wcd9xxx->dev, "%s: wcd9xxx chip id major 0x%x, minor 0x%x\n", + __func__, id_major, id_minor); + + if (id_major != PAHU_MAJOR) + goto version_unknown; + + /* + * As fine version info cannot be retrieved before pahu probe. + * Assign coarse versions for possible future use before Pahu probe. + */ + if (id_minor == cpu_to_le16(0)) + version = PAHU_VERSION_1_0; + +version_unknown: + if (version < 0) + dev_err(wcd9xxx->dev, "%s: wcd934x version unknown\n", + __func__); + + /* Fill codec type info */ + wcd_type->id_major = id_major; + wcd_type->id_minor = id_minor; + wcd_type->num_irqs = WCD9360_NUM_IRQS; + wcd_type->version = version; + wcd_type->slim_slave_type = WCD9XXX_SLIM_SLAVE_ADDR_TYPE_1; + wcd_type->i2c_chip_status = 0x01; + wcd_type->intr_tbl = wcd9360_intr_table; + wcd_type->intr_tbl_size = ARRAY_SIZE(wcd9360_intr_table); + + wcd_type->intr_reg[WCD9XXX_INTR_STATUS_BASE] = + WCD9360_INTR_PIN1_STATUS0; + wcd_type->intr_reg[WCD9XXX_INTR_CLEAR_BASE] = + WCD9360_INTR_PIN1_CLEAR0; + wcd_type->intr_reg[WCD9XXX_INTR_MASK_BASE] = + WCD9360_INTR_PIN1_MASK0; + wcd_type->intr_reg[WCD9XXX_INTR_LEVEL_BASE] = + WCD9360_INTR_LEVEL0; + wcd_type->intr_reg[WCD9XXX_INTR_CLR_COMMIT] = + WCD9360_INTR_CLR_COMMIT; + + return rc; +} + +codec_bringdown_fn wcd9xxx_bringdown_fn(int type) +{ + codec_bringdown_fn cdc_bdown_fn; + + switch (type) { + case WCD9360: + cdc_bdown_fn = wcd9360_bring_down; + break; + case WCD934X: + cdc_bdown_fn = wcd934x_bring_down; + break; + case WCD9335: + cdc_bdown_fn = wcd9335_bring_down; + break; + default: + cdc_bdown_fn = NULL; + break; + } + + return cdc_bdown_fn; +} + +codec_bringup_fn wcd9xxx_bringup_fn(int type) +{ + codec_bringup_fn cdc_bup_fn; + + switch (type) { + case WCD9360: + cdc_bup_fn = wcd9360_bring_up; + break; + case WCD934X: + cdc_bup_fn = wcd934x_bring_up; + break; + case WCD9335: + cdc_bup_fn = wcd9335_bring_up; + break; + default: + cdc_bup_fn = NULL; + break; + } + + return cdc_bup_fn; +} + +codec_type_fn wcd9xxx_get_codec_info_fn(int type) +{ + codec_type_fn cdc_type_fn; + + switch (type) { + case WCD9360: + cdc_type_fn = wcd9360_get_cdc_info; + break; + case WCD934X: + cdc_type_fn = wcd934x_get_cdc_info; + break; + case WCD9335: + cdc_type_fn = wcd9335_get_cdc_info; + break; + default: + cdc_type_fn = NULL; + break; + } + + return cdc_type_fn; +} + diff --git a/audio-kernel/asoc/codecs/wcd9xxx-slimslave.c b/audio-kernel/asoc/codecs/wcd9xxx-slimslave.c new file mode 100644 index 000000000..560e80b17 --- /dev/null +++ b/audio-kernel/asoc/codecs/wcd9xxx-slimslave.c @@ -0,0 +1,607 @@ +/* Copyright (c) 2012-2017, 2019, 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 +#include +#include +#include "wcd9xxx-slimslave.h" + +struct wcd9xxx_slim_sch { + u16 rx_port_ch_reg_base; + u16 port_tx_cfg_reg_base; + u16 port_rx_cfg_reg_base; +}; + +static struct wcd9xxx_slim_sch sh_ch; + +static int wcd9xxx_alloc_slim_sh_ch(struct wcd9xxx *wcd9xxx, + u8 wcd9xxx_pgd_la, u32 cnt, + struct wcd9xxx_ch *channels, u32 path); + +static int wcd9xxx_dealloc_slim_sh_ch(struct slim_device *slim, + u32 cnt, struct wcd9xxx_ch *channels); + +static int wcd9xxx_configure_ports(struct wcd9xxx *wcd9xxx) +{ + if (wcd9xxx->codec_type->slim_slave_type == + WCD9XXX_SLIM_SLAVE_ADDR_TYPE_0) { + sh_ch.rx_port_ch_reg_base = 0x180; + sh_ch.port_rx_cfg_reg_base = 0x040; + sh_ch.port_tx_cfg_reg_base = 0x040; + } else { + sh_ch.rx_port_ch_reg_base = + 0x180 - (TAIKO_SB_PGD_OFFSET_OF_RX_SLAVE_DEV_PORTS * 4); + sh_ch.port_rx_cfg_reg_base = + 0x040 - TAIKO_SB_PGD_OFFSET_OF_RX_SLAVE_DEV_PORTS; + sh_ch.port_tx_cfg_reg_base = 0x050; + } + + return 0; +} + +/** + * wcd9xxx_init_slimslave + * + * @wcd9xxx: pointer to wcd9xxx struct + * @wcd9xxx_pgd_la: pgd_la value + * @tx_num: tx number + * @rx_num: rx number + * @tx_slot: pointer to tx slot + * @rx_slot: pointer to rx slot + * + * Returns 0 on success, appropriate error code otherwise + */ +int wcd9xxx_init_slimslave(struct wcd9xxx *wcd9xxx, u8 wcd9xxx_pgd_la, + unsigned int tx_num, unsigned int *tx_slot, + unsigned int rx_num, unsigned int *rx_slot) +{ + int ret = 0; + int i; + + ret = wcd9xxx_configure_ports(wcd9xxx); + if (ret) { + pr_err("%s: Failed to configure register address offset\n", + __func__); + goto err; + } + + if (!rx_num || rx_num > wcd9xxx->num_rx_port) { + pr_err("%s: invalid rx num %d\n", __func__, rx_num); + return -EINVAL; + } + if (wcd9xxx->rx_chs) { + wcd9xxx->num_rx_port = rx_num; + for (i = 0; i < rx_num; i++) { + wcd9xxx->rx_chs[i].ch_num = rx_slot[i]; + INIT_LIST_HEAD(&wcd9xxx->rx_chs[i].list); + } + ret = wcd9xxx_alloc_slim_sh_ch(wcd9xxx, wcd9xxx_pgd_la, + wcd9xxx->num_rx_port, + wcd9xxx->rx_chs, + SLIM_SINK); + if (ret) { + pr_err("%s: Failed to alloc %d rx slimbus channels\n", + __func__, wcd9xxx->num_rx_port); + kfree(wcd9xxx->rx_chs); + wcd9xxx->rx_chs = NULL; + wcd9xxx->num_rx_port = 0; + } + } else { + pr_err("Not able to allocate memory for %d slimbus rx ports\n", + wcd9xxx->num_rx_port); + } + + if (!tx_num || tx_num > wcd9xxx->num_tx_port) { + pr_err("%s: invalid tx num %d\n", __func__, tx_num); + return -EINVAL; + } + if (wcd9xxx->tx_chs) { + wcd9xxx->num_tx_port = tx_num; + for (i = 0; i < tx_num; i++) { + wcd9xxx->tx_chs[i].ch_num = tx_slot[i]; + INIT_LIST_HEAD(&wcd9xxx->tx_chs[i].list); + } + ret = wcd9xxx_alloc_slim_sh_ch(wcd9xxx, wcd9xxx_pgd_la, + wcd9xxx->num_tx_port, + wcd9xxx->tx_chs, + SLIM_SRC); + if (ret) { + pr_err("%s: Failed to alloc %d tx slimbus channels\n", + __func__, wcd9xxx->num_tx_port); + kfree(wcd9xxx->tx_chs); + wcd9xxx->tx_chs = NULL; + wcd9xxx->num_tx_port = 0; + } + } else { + pr_err("Not able to allocate memory for %d slimbus tx ports\n", + wcd9xxx->num_tx_port); + } + return 0; +err: + return ret; +} +EXPORT_SYMBOL(wcd9xxx_init_slimslave); + +int wcd9xxx_deinit_slimslave(struct wcd9xxx *wcd9xxx) +{ + if (wcd9xxx->num_rx_port) { + wcd9xxx_dealloc_slim_sh_ch(wcd9xxx->slim, + wcd9xxx->num_rx_port, + wcd9xxx->rx_chs); + wcd9xxx->num_rx_port = 0; + } + if (wcd9xxx->num_tx_port) { + wcd9xxx_dealloc_slim_sh_ch(wcd9xxx->slim, + wcd9xxx->num_tx_port, + wcd9xxx->tx_chs); + wcd9xxx->num_tx_port = 0; + } + return 0; +} + + +static int wcd9xxx_alloc_slim_sh_ch(struct wcd9xxx *wcd9xxx, + u8 wcd9xxx_pgd_la, u32 cnt, + struct wcd9xxx_ch *channels, u32 path) +{ + int ret = 0; + u32 ch_idx; + + /* The slimbus channel allocation seem take longer time + * so do the allocation up front to avoid delay in start of + * playback + */ + pr_debug("%s: pgd_la[%d]\n", __func__, wcd9xxx_pgd_la); + for (ch_idx = 0; ch_idx < cnt; ch_idx++) { + ret = slim_get_slaveport(wcd9xxx_pgd_la, + channels[ch_idx].port, + &channels[ch_idx].sph, path); + pr_debug("%s: pgd_la[%d] channels[%d].port[%d]\n" + "channels[%d].sph[%d] path[%d]\n", + __func__, wcd9xxx_pgd_la, ch_idx, + channels[ch_idx].port, + ch_idx, channels[ch_idx].sph, path); + if (ret < 0) { + pr_err("%s: slave port failure id[%d] ret[%d]\n", + __func__, channels[ch_idx].ch_num, ret); + goto err; + } + + ret = slim_query_ch(wcd9xxx->slim, + channels[ch_idx].ch_num, + &channels[ch_idx].ch_h); + if (ret < 0) { + pr_err("%s: slim_query_ch failed ch-num[%d] ret[%d]\n", + __func__, channels[ch_idx].ch_num, ret); + goto err; + } + } +err: + return ret; +} + +static int wcd9xxx_dealloc_slim_sh_ch(struct slim_device *slim, + u32 cnt, struct wcd9xxx_ch *channels) +{ + int idx = 0; + int ret = 0; + /* slim_dealloc_ch */ + for (idx = 0; idx < cnt; idx++) { + ret = slim_dealloc_ch(slim, channels[idx].ch_h); + if (ret < 0) { + pr_err("%s: slim_dealloc_ch fail ret[%d] ch_h[%d]\n", + __func__, ret, channels[idx].ch_h); + } + } + return ret; +} + +/* Enable slimbus slave device for RX path */ +int wcd9xxx_cfg_slim_sch_rx(struct wcd9xxx *wcd9xxx, + struct list_head *wcd9xxx_ch_list, + unsigned int rate, unsigned int bit_width, + u16 *grph) +{ + u8 ch_cnt = 0; + u16 ch_h[SLIM_MAX_RX_PORTS] = {0}; + u8 payload = 0; + u16 codec_port = 0; + int ret; + struct slim_ch prop; + struct wcd9xxx_ch *rx; + int size = ARRAY_SIZE(ch_h); + + /* Configure slave interface device */ + + list_for_each_entry(rx, wcd9xxx_ch_list, list) { + payload |= 1 << rx->shift; + if (ch_cnt < size) { + ch_h[ch_cnt] = rx->ch_h; + ch_cnt++; + pr_debug("list ch->ch_h %d ch->sph %d\n", + rx->ch_h, rx->sph); + } else { + pr_err("%s: allocated channel number %u is out of max rangae %d\n", + __func__, ch_cnt, + size); + ret = EINVAL; + goto err; + } + } + pr_debug("%s: ch_cnt[%d] rate=%d WATER_MARK_VAL %d\n", + __func__, ch_cnt, rate, WATER_MARK_VAL); + /* slim_define_ch api */ + prop.prot = SLIM_AUTO_ISO; + if ((rate == 44100) || (rate == 88200) || (rate == 176400) || + (rate == 352800)) { + prop.baser = SLIM_RATE_11025HZ; + prop.ratem = (rate/11025); + } else { + prop.baser = SLIM_RATE_4000HZ; + prop.ratem = (rate/4000); + } + prop.dataf = SLIM_CH_DATAF_NOT_DEFINED; + prop.auxf = SLIM_CH_AUXF_NOT_APPLICABLE; + prop.sampleszbits = bit_width; + + pr_debug("Before slim_define_ch:\n" + "ch_cnt %d,ch_h[0] %d ch_h[1] %d, grph %d\n", + ch_cnt, ch_h[0], ch_h[1], *grph); + ret = slim_define_ch(wcd9xxx->slim, &prop, ch_h, ch_cnt, + true, grph); + if (ret < 0) { + pr_err("%s: slim_define_ch failed ret[%d]\n", + __func__, ret); + goto err; + } + + list_for_each_entry(rx, wcd9xxx_ch_list, list) { + codec_port = rx->port; + pr_debug("%s: codec_port %d rx 0x%p, payload %d\n" + "sh_ch.rx_port_ch_reg_base0 0x%x\n" + "sh_ch.port_rx_cfg_reg_base 0x%x\n", + __func__, codec_port, rx, payload, + sh_ch.rx_port_ch_reg_base, + sh_ch.port_rx_cfg_reg_base); + + /* look for the valid port range and chose the + * payload accordingly + */ + /* write to interface device */ + ret = wcd9xxx_interface_reg_write(wcd9xxx, + SB_PGD_RX_PORT_MULTI_CHANNEL_0( + sh_ch.rx_port_ch_reg_base, codec_port), + payload); + + if (ret < 0) { + pr_err("%s:Intf-dev fail reg[%d] payload[%d] ret[%d]\n", + __func__, + SB_PGD_RX_PORT_MULTI_CHANNEL_0( + sh_ch.rx_port_ch_reg_base, codec_port), + payload, ret); + goto err; + } + /* configure the slave port for water mark and enable*/ + ret = wcd9xxx_interface_reg_write(wcd9xxx, + SB_PGD_PORT_CFG_BYTE_ADDR( + sh_ch.port_rx_cfg_reg_base, codec_port), + WATER_MARK_VAL); + if (ret < 0) { + pr_err("%s:watermark set failure for port[%d] ret[%d]", + __func__, codec_port, ret); + } + + ret = slim_connect_sink(wcd9xxx->slim, &rx->sph, 1, rx->ch_h); + if (ret < 0) { + pr_err("%s: slim_connect_sink failed ret[%d]\n", + __func__, ret); + goto err_close_slim_sch; + } + } + /* slim_control_ch */ + ret = slim_control_ch(wcd9xxx->slim, *grph, SLIM_CH_ACTIVATE, + true); + if (ret < 0) { + pr_err("%s: slim_control_ch failed ret[%d]\n", + __func__, ret); + goto err_close_slim_sch; + } + return 0; + +err_close_slim_sch: + /* release all acquired handles */ + wcd9xxx_close_slim_sch_rx(wcd9xxx, wcd9xxx_ch_list, *grph); +err: + return ret; +} +EXPORT_SYMBOL(wcd9xxx_cfg_slim_sch_rx); + +static void wcd9xxx_slim_tx_auto_recovery_cfg(struct wcd9xxx *wcd9xxx, + u16 codec_port) +{ + int ret; + + if (wcd9xxx->codec_type->id_major != TAVIL_MAJOR) + return; + + ret = wcd9xxx_interface_reg_write(wcd9xxx, + SB_PGD_PORT_TX_OR_UR_CFG(codec_port), + 0x02); + if (ret < 0) + pr_err("%s:auto_recovery set failure for port[%d] ret[%d]", + __func__, codec_port, ret); + else + pr_debug("%s: auto recovery register 0x%x value: 0x%x\n", + __func__, SB_PGD_PORT_TX_OR_UR_CFG(codec_port), + wcd9xxx_interface_reg_read(wcd9xxx, + SB_PGD_PORT_TX_OR_UR_CFG(codec_port))); +} + +/* Enable slimbus slave device for RX path */ +int wcd9xxx_cfg_slim_sch_tx(struct wcd9xxx *wcd9xxx, + struct list_head *wcd9xxx_ch_list, + unsigned int rate, unsigned int bit_width, + u16 *grph) +{ + u16 ch_cnt = 0; + u16 payload = 0; + u16 ch_h[SLIM_MAX_TX_PORTS] = {0}; + u16 codec_port; + int ret = 0; + struct wcd9xxx_ch *tx; + int size = ARRAY_SIZE(ch_h); + + struct slim_ch prop; + + list_for_each_entry(tx, wcd9xxx_ch_list, list) { + payload |= 1 << tx->shift; + if (ch_cnt < size) { + ch_h[ch_cnt] = tx->ch_h; + ch_cnt++; + } else { + pr_err("%s: allocated channel number %u is out of max rangae %d\n", + __func__, ch_cnt, + size); + ret = EINVAL; + goto err; + } + } + + /* slim_define_ch api */ + prop.prot = SLIM_AUTO_ISO; + prop.baser = SLIM_RATE_4000HZ; + prop.dataf = SLIM_CH_DATAF_NOT_DEFINED; + prop.auxf = SLIM_CH_AUXF_NOT_APPLICABLE; + prop.ratem = (rate/4000); + prop.sampleszbits = bit_width; + ret = slim_define_ch(wcd9xxx->slim, &prop, ch_h, ch_cnt, + true, grph); + if (ret < 0) { + pr_err("%s: slim_define_ch failed ret[%d]\n", + __func__, ret); + goto err; + } + + pr_debug("%s: ch_cnt[%d] rate[%d] bitwidth[%u]\n", __func__, ch_cnt, + rate, bit_width); + list_for_each_entry(tx, wcd9xxx_ch_list, list) { + codec_port = tx->port; + pr_debug("%s: codec_port %d tx 0x%p, payload 0x%x\n", + __func__, codec_port, tx, payload); + + wcd9xxx_slim_tx_auto_recovery_cfg(wcd9xxx, codec_port); + /* write to interface device */ + ret = wcd9xxx_interface_reg_write(wcd9xxx, + SB_PGD_TX_PORT_MULTI_CHANNEL_0(codec_port), + payload & 0x00FF); + if (ret < 0) { + pr_err("%s:Intf-dev fail reg[%d] payload[%d] ret[%d]\n", + __func__, + SB_PGD_TX_PORT_MULTI_CHANNEL_0(codec_port), + payload, ret); + goto err; + } + /* ports 8,9 */ + ret = wcd9xxx_interface_reg_write(wcd9xxx, + SB_PGD_TX_PORT_MULTI_CHANNEL_1(codec_port), + (payload & 0xFF00)>>8); + if (ret < 0) { + pr_err("%s:Intf-dev fail reg[%d] payload[%d] ret[%d]\n", + __func__, + SB_PGD_TX_PORT_MULTI_CHANNEL_1(codec_port), + payload, ret); + goto err; + } + /* configure the slave port for water mark and enable*/ + ret = wcd9xxx_interface_reg_write(wcd9xxx, + SB_PGD_PORT_CFG_BYTE_ADDR( + sh_ch.port_tx_cfg_reg_base, codec_port), + WATER_MARK_VAL); + if (ret < 0) { + pr_err("%s:watermark set failure for port[%d] ret[%d]", + __func__, codec_port, ret); + } + + ret = slim_connect_src(wcd9xxx->slim, tx->sph, tx->ch_h); + + if (ret < 0) { + pr_err("%s: slim_connect_src failed ret[%d]\n", + __func__, ret); + goto err; + } + } + /* slim_control_ch */ + ret = slim_control_ch(wcd9xxx->slim, *grph, SLIM_CH_ACTIVATE, + true); + if (ret < 0) { + pr_err("%s: slim_control_ch failed ret[%d]\n", + __func__, ret); + goto err; + } + return 0; +err: + /* release all acquired handles */ + wcd9xxx_close_slim_sch_tx(wcd9xxx, wcd9xxx_ch_list, *grph); + return ret; +} +EXPORT_SYMBOL(wcd9xxx_cfg_slim_sch_tx); + +int wcd9xxx_close_slim_sch_rx(struct wcd9xxx *wcd9xxx, + struct list_head *wcd9xxx_ch_list, u16 grph) +{ + u32 sph[SLIM_MAX_RX_PORTS] = {0}; + int ch_cnt = 0; + int ret = 0; + struct wcd9xxx_ch *rx; + + list_for_each_entry(rx, wcd9xxx_ch_list, list) + sph[ch_cnt++] = rx->sph; + + pr_debug("%s ch_cht %d, sph[0] %d sph[1] %d\n", __func__, ch_cnt, + sph[0], sph[1]); + + /* slim_control_ch (REMOVE) */ + pr_debug("%s before slim_control_ch grph %d\n", __func__, grph); + ret = slim_control_ch(wcd9xxx->slim, grph, SLIM_CH_REMOVE, true); + if (ret < 0) { + pr_err("%s: slim_control_ch failed ret[%d]\n", __func__, ret); + goto err; + } +err: + return ret; +} +EXPORT_SYMBOL(wcd9xxx_close_slim_sch_rx); + +int wcd9xxx_close_slim_sch_tx(struct wcd9xxx *wcd9xxx, + struct list_head *wcd9xxx_ch_list, + u16 grph) +{ + u32 sph[SLIM_MAX_TX_PORTS] = {0}; + int ret = 0; + int ch_cnt = 0; + struct wcd9xxx_ch *tx; + + pr_debug("%s\n", __func__); + list_for_each_entry(tx, wcd9xxx_ch_list, list) + sph[ch_cnt++] = tx->sph; + + pr_debug("%s ch_cht %d, sph[0] %d sph[1] %d\n", + __func__, ch_cnt, sph[0], sph[1]); + /* slim_control_ch (REMOVE) */ + ret = slim_control_ch(wcd9xxx->slim, grph, SLIM_CH_REMOVE, true); + if (ret < 0) { + pr_err("%s: slim_control_ch failed ret[%d]\n", + __func__, ret); + goto err; + } +err: + return ret; +} +EXPORT_SYMBOL(wcd9xxx_close_slim_sch_tx); + +int wcd9xxx_get_slave_port(unsigned int ch_num) +{ + int ret = 0; + + ret = (ch_num - BASE_CH_NUM); + pr_debug("%s: ch_num[%d] slave port[%d]\n", __func__, ch_num, ret); + if (ret < 0) { + pr_err("%s: Error:- Invalid slave port found = %d\n", + __func__, ret); + return -EINVAL; + } + return ret; +} +EXPORT_SYMBOL(wcd9xxx_get_slave_port); + +int wcd9xxx_disconnect_port(struct wcd9xxx *wcd9xxx, + struct list_head *wcd9xxx_ch_list, u16 grph) +{ + u32 sph[SLIM_MAX_TX_PORTS + SLIM_MAX_RX_PORTS] = {0}; + int ch_cnt = 0; + int ret = 0; + struct wcd9xxx_ch *slim_ch; + + list_for_each_entry(slim_ch, wcd9xxx_ch_list, list) + sph[ch_cnt++] = slim_ch->sph; + + /* slim_disconnect_port */ + ret = slim_disconnect_ports(wcd9xxx->slim, sph, ch_cnt); + if (ret < 0) { + pr_err("%s: slim_disconnect_ports failed ret[%d]\n", + __func__, ret); + } + return ret; +} +EXPORT_SYMBOL(wcd9xxx_disconnect_port); + +/* This function is called with mutex acquired */ +int wcd9xxx_rx_vport_validation(u32 port_id, + struct list_head *codec_dai_list) +{ + struct wcd9xxx_ch *ch; + int ret = 0; + + pr_debug("%s: port_id %u\n", __func__, port_id); + + list_for_each_entry(ch, + codec_dai_list, list) { + pr_debug("%s: ch->port %u\n", __func__, ch->port); + if (ch->port == port_id) { + ret = -EINVAL; + break; + } + } + return ret; +} +EXPORT_SYMBOL(wcd9xxx_rx_vport_validation); + + +/* This function is called with mutex acquired */ +int wcd9xxx_tx_vport_validation(u32 table, u32 port_id, + struct wcd9xxx_codec_dai_data *codec_dai, + u32 num_codec_dais) +{ + struct wcd9xxx_ch *ch; + int ret = 0; + u32 index; + unsigned long vtable = table; + u32 size = sizeof(table) * BITS_PER_BYTE; + + pr_debug("%s: vtable 0x%lx port_id %u size %d\n", __func__, + vtable, port_id, size); + for_each_set_bit(index, &vtable, size) { + if (index < num_codec_dais) { + list_for_each_entry(ch, + &codec_dai[index].wcd9xxx_ch_list, + list) { + pr_debug("%s: index %u ch->port %u vtable 0x%lx\n", + __func__, index, ch->port, + vtable); + if (ch->port == port_id) { + pr_err("%s: TX%u is used by AIF%u_CAP Mixer\n", + __func__, port_id + 1, + (index + 1)/2); + ret = -EINVAL; + break; + } + } + } else { + pr_err("%s: Invalid index %d of codec dai", + __func__, index); + ret = -EINVAL; + } + if (ret) + break; + } + return ret; +} +EXPORT_SYMBOL(wcd9xxx_tx_vport_validation); diff --git a/audio-kernel/asoc/codecs/wcd9xxx-slimslave.h b/audio-kernel/asoc/codecs/wcd9xxx-slimslave.h new file mode 100644 index 000000000..fdf98e123 --- /dev/null +++ b/audio-kernel/asoc/codecs/wcd9xxx-slimslave.h @@ -0,0 +1,121 @@ +/* Copyright (c) 2012-2015, 2019, 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 __WCD9XXX_SLIMSLAVE_H_ +#define __WCD9XXX_SLIMSLAVE_H_ + +#include +#include "core.h" + + +/* + * client is expected to give port ids in the range of + * 1-10 for pre Taiko Tx ports and 1-16 for Taiko + * 1-7 for pre Taiko Rx ports and 1-16 for Tako, + * we need to add offset for getting the absolute slave + * port id before configuring the HW + */ +#define TABLA_SB_PGD_MAX_NUMBER_OF_TX_SLAVE_DEV_PORTS 10 +#define TAIKO_SB_PGD_MAX_NUMBER_OF_TX_SLAVE_DEV_PORTS 16 + +#define SLIM_MAX_TX_PORTS TAIKO_SB_PGD_MAX_NUMBER_OF_TX_SLAVE_DEV_PORTS + +#define TABLA_SB_PGD_OFFSET_OF_RX_SLAVE_DEV_PORTS \ + TABLA_SB_PGD_MAX_NUMBER_OF_TX_SLAVE_DEV_PORTS +#define TAIKO_SB_PGD_OFFSET_OF_RX_SLAVE_DEV_PORTS \ + TAIKO_SB_PGD_MAX_NUMBER_OF_TX_SLAVE_DEV_PORTS + +#define TABLA_SB_PGD_MAX_NUMBER_OF_RX_SLAVE_DEV_PORTS 7 +#define TAIKO_SB_PGD_MAX_NUMBER_OF_RX_SLAVE_DEV_PORTS 13 + +#define SLIM_MAX_RX_PORTS TAIKO_SB_PGD_MAX_NUMBER_OF_RX_SLAVE_DEV_PORTS + +#define SLIM_MAX_REG_ADDR (0x180 + 4 * (SLIM_MAX_RX_PORTS)) + +#define TABLA_SB_PGD_RX_PORT_MULTI_CHANNEL_0_START_PORT_ID \ + TABLA_SB_PGD_OFFSET_OF_RX_SLAVE_DEV_PORTS +#define TAIKO_SB_PGD_RX_PORT_MULTI_CHANNEL_0_START_PORT_ID \ + TAIKO_SB_PGD_OFFSET_OF_RX_SLAVE_DEV_PORTS + +#define TABLA_SB_PGD_RX_PORT_MULTI_CHANNEL_0_END_PORT_ID 16 +#define TAIKO_SB_PGD_RX_PORT_MULTI_CHANNEL_0_END_PORT_ID 31 + +#define TABLA_SB_PGD_TX_PORT_MULTI_CHANNEL_1_END_PORT_ID 9 +#define TAIKO_SB_PGD_TX_PORT_MULTI_CHANNEL_1_END_PORT_ID 15 + +/* below details are taken from SLIMBUS slave SWI */ +#define SB_PGD_PORT_BASE 0x000 + +#define SB_PGD_PORT_CFG_BYTE_ADDR(offset, port_num) \ + (SB_PGD_PORT_BASE + offset + (1 * port_num)) + +#define SB_PGD_TX_PORT_MULTI_CHANNEL_0(port_num) \ + (SB_PGD_PORT_BASE + 0x100 + 4*port_num) +#define SB_PGD_TX_PORT_MULTI_CHANNEL_0_START_PORT_ID 0 +#define SB_PGD_TX_PORT_MULTI_CHANNEL_0_END_PORT_ID 7 + +#define SB_PGD_TX_PORT_MULTI_CHANNEL_1(port_num) \ + (SB_PGD_PORT_BASE + 0x101 + 4*port_num) +#define SB_PGD_TX_PORT_MULTI_CHANNEL_1_START_PORT_ID 8 + +#define SB_PGD_RX_PORT_MULTI_CHANNEL_0(offset, port_num) \ + (SB_PGD_PORT_BASE + offset + (4 * port_num)) + +#define SB_PGD_PORT_TX_OR_UR_CFG(port) (0x1F0 + port) + +/* slave port water mark level + * (0: 6bytes, 1: 9bytes, 2: 12 bytes, 3: 15 bytes) + */ +#define SLAVE_PORT_WATER_MARK_6BYTES 0 +#define SLAVE_PORT_WATER_MARK_9BYTES 1 +#define SLAVE_PORT_WATER_MARK_12BYTES 2 +#define SLAVE_PORT_WATER_MARK_15BYTES 3 +#define SLAVE_PORT_WATER_MARK_SHIFT 1 +#define SLAVE_PORT_ENABLE 1 +#define SLAVE_PORT_DISABLE 0 +#define WATER_MARK_VAL \ + ((SLAVE_PORT_WATER_MARK_12BYTES << SLAVE_PORT_WATER_MARK_SHIFT) | \ + (SLAVE_PORT_ENABLE)) +#define BASE_CH_NUM 128 + + +int wcd9xxx_init_slimslave(struct wcd9xxx *wcd9xxx, + u8 wcd9xxx_pgd_la, + unsigned int tx_num, unsigned int *tx_slot, + unsigned int rx_num, unsigned int *rx_slot); + +int wcd9xxx_deinit_slimslave(struct wcd9xxx *wcd9xxx); + +int wcd9xxx_cfg_slim_sch_rx(struct wcd9xxx *wcd9xxx, + struct list_head *wcd9xxx_ch_list, + unsigned int rate, unsigned int bit_width, + u16 *grph); +int wcd9xxx_cfg_slim_sch_tx(struct wcd9xxx *wcd9xxx, + struct list_head *wcd9xxx_ch_list, + unsigned int rate, unsigned int bit_width, + u16 *grph); +int wcd9xxx_close_slim_sch_rx(struct wcd9xxx *wcd9xxx, + struct list_head *wcd9xxx_ch_list, u16 grph); +int wcd9xxx_close_slim_sch_tx(struct wcd9xxx *wcd9xxx, + struct list_head *wcd9xxx_ch_list, u16 grph); +int wcd9xxx_get_channel(struct wcd9xxx *wcd9xxx, + unsigned int *rx_ch, + unsigned int *tx_ch); +int wcd9xxx_get_slave_port(unsigned int ch_num); +int wcd9xxx_disconnect_port(struct wcd9xxx *wcd9xxx, + struct list_head *wcd9xxx_ch_list, u16 grph); +int wcd9xxx_rx_vport_validation(u32 port_id, + struct list_head *codec_dai_list); +int wcd9xxx_tx_vport_validation(u32 vtable, u32 port_id, + struct wcd9xxx_codec_dai_data *codec_dai, + u32 num_codec_dais); +#endif /* __WCD9XXX_SLIMSLAVE_H_ */ diff --git a/audio-kernel/asoc/codecs/wcd9xxx-soc-init.c b/audio-kernel/asoc/codecs/wcd9xxx-soc-init.c new file mode 100644 index 000000000..fa8abb7de --- /dev/null +++ b/audio-kernel/asoc/codecs/wcd9xxx-soc-init.c @@ -0,0 +1,45 @@ +/* + * Copyright (c) 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 + * 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 +#include +#include "audio-ext-clk-up.h" + +static int __init wcd9xxx_soc_init(void) +{ + int ret = 0; + + ret = wcd_dsp_mgr_init(); + if (!ret) { + ret = audio_ref_clk_platform_init(); + if (ret) { + pr_err("%s: init extclk fail: %d\n", __func__, ret); + wcd_dsp_mgr_exit(); + } + } else { + pr_err("%s: init dsp mgr fail: %d\n", __func__, ret); + } + + return ret; +} +module_init(wcd9xxx_soc_init); + +static void __exit wcd9xxx_soc_exit(void) +{ + audio_ref_clk_platform_exit(); + wcd_dsp_mgr_exit(); +} +module_exit(wcd9xxx_soc_exit); + +MODULE_DESCRIPTION("WCD9XXX CODEC soc init driver"); +MODULE_LICENSE("GPL v2"); diff --git a/audio-kernel/asoc/codecs/wcd9xxx-utils.c b/audio-kernel/asoc/codecs/wcd9xxx-utils.c new file mode 100644 index 000000000..7dc18f68b --- /dev/null +++ b/audio-kernel/asoc/codecs/wcd9xxx-utils.c @@ -0,0 +1,1239 @@ +/* Copyright (c) 2016-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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "core.h" +#include "msm-cdc-supply.h" +#include "msm-cdc-pinctrl.h" +#include "pdata.h" +#include "wcd9xxx-irq.h" +#include "wcd9xxx-utils.h" + +#define REG_BYTES 2 +#define VAL_BYTES 1 +/* + * Page Register Address that APP Proc uses to + * access WCD9335 Codec registers is identified + * as 0x00 + */ +#define PAGE_REG_ADDR 0x00 + +static enum wcd9xxx_intf_status wcd9xxx_intf = -1; + +static struct mfd_cell pahu_devs[] = { + { + .name = "qcom-wcd-pinctrl", + .of_compatible = "qcom,wcd-pinctrl", + }, + { + .name = "pahu_codec", + }, +}; + +static struct mfd_cell tavil_devs[] = { + { + .name = "qcom-wcd-pinctrl", + .of_compatible = "qcom,wcd-pinctrl", + }, + { + .name = "tavil_codec", + }, +}; + +static struct mfd_cell tasha_devs[] = { + { + .name = "tasha_codec", + }, +}; + +static struct mfd_cell tomtom_devs[] = { + { + .name = "tomtom_codec", + }, +}; + +static int wcd9xxx_read_of_property_u32(struct device *dev, const char *name, + u32 *val) +{ + int rc = 0; + + rc = of_property_read_u32(dev->of_node, name, val); + if (rc) + dev_err(dev, "%s: Looking up %s property in node %s failed", + __func__, name, dev->of_node->full_name); + + return rc; +} + +static void wcd9xxx_dt_parse_micbias_info(struct device *dev, + struct wcd9xxx_micbias_setting *mb) +{ + u32 prop_val; + int rc; + + if (of_find_property(dev->of_node, "qcom,cdc-micbias-ldoh-v", NULL)) { + rc = wcd9xxx_read_of_property_u32(dev, + "qcom,cdc-micbias-ldoh-v", + &prop_val); + if (!rc) + mb->ldoh_v = (u8)prop_val; + } + + /* MB1 */ + if (of_find_property(dev->of_node, "qcom,cdc-micbias-cfilt1-mv", + NULL)) { + rc = wcd9xxx_read_of_property_u32(dev, + "qcom,cdc-micbias-cfilt1-mv", + &prop_val); + if (!rc) + mb->cfilt1_mv = prop_val; + + rc = wcd9xxx_read_of_property_u32(dev, + "qcom,cdc-micbias1-cfilt-sel", + &prop_val); + if (!rc) + mb->bias1_cfilt_sel = (u8)prop_val; + + } else if (of_find_property(dev->of_node, "qcom,cdc-micbias1-mv", + NULL)) { + rc = wcd9xxx_read_of_property_u32(dev, + "qcom,cdc-micbias1-mv", + &prop_val); + if (!rc) + mb->micb1_mv = prop_val; + } else { + dev_info(dev, "%s: Micbias1 DT property not found\n", + __func__); + } + + /* MB2 */ + if (of_find_property(dev->of_node, "qcom,cdc-micbias-cfilt2-mv", + NULL)) { + rc = wcd9xxx_read_of_property_u32(dev, + "qcom,cdc-micbias-cfilt2-mv", + &prop_val); + if (!rc) + mb->cfilt2_mv = prop_val; + + rc = wcd9xxx_read_of_property_u32(dev, + "qcom,cdc-micbias2-cfilt-sel", + &prop_val); + if (!rc) + mb->bias2_cfilt_sel = (u8)prop_val; + + } else if (of_find_property(dev->of_node, "qcom,cdc-micbias2-mv", + NULL)) { + rc = wcd9xxx_read_of_property_u32(dev, + "qcom,cdc-micbias2-mv", + &prop_val); + if (!rc) + mb->micb2_mv = prop_val; + } else { + dev_info(dev, "%s: Micbias2 DT property not found\n", + __func__); + } + + /* MB3 */ + if (of_find_property(dev->of_node, "qcom,cdc-micbias-cfilt3-mv", + NULL)) { + rc = wcd9xxx_read_of_property_u32(dev, + "qcom,cdc-micbias-cfilt3-mv", + &prop_val); + if (!rc) + mb->cfilt3_mv = prop_val; + + rc = wcd9xxx_read_of_property_u32(dev, + "qcom,cdc-micbias3-cfilt-sel", + &prop_val); + if (!rc) + mb->bias3_cfilt_sel = (u8)prop_val; + + } else if (of_find_property(dev->of_node, "qcom,cdc-micbias3-mv", + NULL)) { + rc = wcd9xxx_read_of_property_u32(dev, + "qcom,cdc-micbias3-mv", + &prop_val); + if (!rc) + mb->micb3_mv = prop_val; + } else { + dev_info(dev, "%s: Micbias3 DT property not found\n", + __func__); + } + + /* MB4 */ + if (of_find_property(dev->of_node, "qcom,cdc-micbias4-cfilt-sel", + NULL)) { + rc = wcd9xxx_read_of_property_u32(dev, + "qcom,cdc-micbias4-cfilt-sel", + &prop_val); + if (!rc) + mb->bias4_cfilt_sel = (u8)prop_val; + + } else if (of_find_property(dev->of_node, "qcom,cdc-micbias4-mv", + NULL)) { + rc = wcd9xxx_read_of_property_u32(dev, + "qcom,cdc-micbias4-mv", + &prop_val); + if (!rc) + mb->micb4_mv = prop_val; + } else { + dev_info(dev, "%s: Micbias4 DT property not found\n", + __func__); + } + + mb->bias1_cap_mode = + (of_property_read_bool(dev->of_node, "qcom,cdc-micbias1-ext-cap") ? + MICBIAS_EXT_BYP_CAP : MICBIAS_NO_EXT_BYP_CAP); + mb->bias2_cap_mode = + (of_property_read_bool(dev->of_node, "qcom,cdc-micbias2-ext-cap") ? + MICBIAS_EXT_BYP_CAP : MICBIAS_NO_EXT_BYP_CAP); + mb->bias3_cap_mode = + (of_property_read_bool(dev->of_node, "qcom,cdc-micbias3-ext-cap") ? + MICBIAS_EXT_BYP_CAP : MICBIAS_NO_EXT_BYP_CAP); + mb->bias4_cap_mode = + (of_property_read_bool(dev->of_node, "qcom,cdc-micbias4-ext-cap") ? + MICBIAS_EXT_BYP_CAP : MICBIAS_NO_EXT_BYP_CAP); + + mb->bias2_is_headset_only = + of_property_read_bool(dev->of_node, + "qcom,cdc-micbias2-headset-only"); + + /* Print micbias info */ + dev_dbg(dev, "%s: ldoh_v %u cfilt1_mv %u cfilt2_mv %u cfilt3_mv %u", + __func__, (u32)mb->ldoh_v, (u32)mb->cfilt1_mv, + (u32)mb->cfilt2_mv, (u32)mb->cfilt3_mv); + + dev_dbg(dev, "%s: micb1_mv %u micb2_mv %u micb3_mv %u micb4_mv %u", + __func__, mb->micb1_mv, mb->micb2_mv, + mb->micb3_mv, mb->micb4_mv); + + dev_dbg(dev, "%s: bias1_cfilt_sel %u bias2_cfilt_sel %u\n", + __func__, (u32)mb->bias1_cfilt_sel, (u32)mb->bias2_cfilt_sel); + + dev_dbg(dev, "%s: bias3_cfilt_sel %u bias4_cfilt_sel %u\n", + __func__, (u32)mb->bias3_cfilt_sel, (u32)mb->bias4_cfilt_sel); + + dev_dbg(dev, "%s: bias1_ext_cap %d bias2_ext_cap %d\n", + __func__, mb->bias1_cap_mode, mb->bias2_cap_mode); + + dev_dbg(dev, "%s: bias3_ext_cap %d bias4_ext_cap %d\n", + __func__, mb->bias3_cap_mode, mb->bias4_cap_mode); + + dev_dbg(dev, "%s: bias2_is_headset_only %d\n", + __func__, mb->bias2_is_headset_only); +} + +/* + * wcd9xxx_validate_dmic_sample_rate: + * Given the dmic_sample_rate and mclk rate, validate the + * dmic_sample_rate. If dmic rate is found to be invalid, + * assign the dmic rate as undefined, so individual codec + * drivers can use their own defaults + * @dev: the device for which the dmic is to be configured + * @dmic_sample_rate: The input dmic_sample_rate + * @mclk_rate: The input codec mclk rate + * @dmic_rate_type: String to indicate the type of dmic sample + * rate, used for debug/error logging. + */ +static u32 wcd9xxx_validate_dmic_sample_rate(struct device *dev, + u32 dmic_sample_rate, u32 mclk_rate, + const char *dmic_rate_type) +{ + u32 div_factor; + + if (dmic_sample_rate == WCD9XXX_DMIC_SAMPLE_RATE_UNDEFINED || + mclk_rate % dmic_sample_rate != 0) + goto undefined_rate; + + div_factor = mclk_rate / dmic_sample_rate; + + switch (div_factor) { + case 2: + case 3: + case 4: + case 8: + case 16: + /* Valid dmic DIV factors */ + dev_dbg(dev, "%s: DMIC_DIV = %u, mclk_rate = %u\n", + __func__, div_factor, mclk_rate); + break; + case 6: + /* + * DIV 6 is valid for both 9.6MHz and 12.288MHz + * MCLK on Tavil. Older codecs support DIV6 only + * for 12.288MHz MCLK. + */ + if ((mclk_rate == WCD9XXX_MCLK_CLK_9P6HZ) && + (of_device_is_compatible(dev->of_node, + "qcom,tavil-slim-pgd"))) + dev_dbg(dev, "%s: DMIC_DIV = %u, mclk_rate = %u\n", + __func__, div_factor, mclk_rate); + else if (mclk_rate != WCD9XXX_MCLK_CLK_12P288MHZ) + goto undefined_rate; + break; + default: + /* Any other DIV factor is invalid */ + goto undefined_rate; + } + + return dmic_sample_rate; + +undefined_rate: + dev_dbg(dev, "%s: Invalid %s = %d, for mclk %d\n", + __func__, dmic_rate_type, dmic_sample_rate, mclk_rate); + dmic_sample_rate = WCD9XXX_DMIC_SAMPLE_RATE_UNDEFINED; + + return dmic_sample_rate; +} + +/* + * wcd9xxx_populate_dt_data: + * Parse device tree properties for the given codec device + * + * @dev: pointer to codec device + * + * Returns pointer to the platform data resulting from parsing + * device tree. + */ +struct wcd9xxx_pdata *wcd9xxx_populate_dt_data(struct device *dev) +{ + struct wcd9xxx_pdata *pdata; + u32 dmic_sample_rate = WCD9XXX_DMIC_SAMPLE_RATE_UNDEFINED; + u32 mad_dmic_sample_rate = WCD9XXX_DMIC_SAMPLE_RATE_UNDEFINED; + u32 ecpp_dmic_sample_rate = WCD9XXX_DMIC_SAMPLE_RATE_UNDEFINED; + u32 dmic_clk_drive = WCD9XXX_DMIC_CLK_DRIVE_UNDEFINED; + u32 prop_val; + int rc = 0; + + if (!dev || !dev->of_node) + return NULL; + + pdata = devm_kzalloc(dev, sizeof(struct wcd9xxx_pdata), + GFP_KERNEL); + if (!pdata) + return NULL; + + /* Parse power supplies */ + msm_cdc_get_power_supplies(dev, &pdata->regulator, + &pdata->num_supplies); + if (!pdata->regulator || (pdata->num_supplies <= 0)) { + dev_err(dev, "%s: no power supplies defined for codec\n", + __func__); + goto err_power_sup; + } + + /* Parse micbias info */ + wcd9xxx_dt_parse_micbias_info(dev, &pdata->micbias); + + pdata->wcd_rst_np = of_parse_phandle(dev->of_node, + "qcom,wcd-rst-gpio-node", 0); + if (!pdata->wcd_rst_np) { + dev_err(dev, "%s: Looking up %s property in node %s failed\n", + __func__, "qcom,wcd-rst-gpio-node", + dev->of_node->full_name); + goto err_parse_dt_prop; + } + + pdata->has_buck_vsel_gpio = of_property_read_bool(dev->of_node, + "qcom,has-buck-vsel-gpio"); + if (pdata->has_buck_vsel_gpio) { + pdata->buck_vsel_ctl_np = of_parse_phandle(dev->of_node, + "qcom,buck-vsel-gpio-node", 0); + if (!pdata->buck_vsel_ctl_np) { + dev_err(dev, "%s No entry for %s property in node %s\n", + __func__, "qcom,buck-vsel-gpio-node", + dev->of_node->full_name); + goto err_parse_dt_prop; + } + } + + pdata->has_micb_supply_en_gpio = of_property_read_bool(dev->of_node, + "qcom,has-micbias-supply-en-gpio"); + if (pdata->has_micb_supply_en_gpio) { + pdata->micb_en_ctl = of_parse_phandle(dev->of_node, + "qcom,micbias-supply-en-gpio-node", 0); + if (!pdata->micb_en_ctl) { + dev_err(dev, "%s No entry for %s property in node %s\n", + __func__, "qcom,micbias-supply-en-gpio-node", + dev->of_node->full_name); + goto err_parse_dt_prop; + } + } + + if (!(wcd9xxx_read_of_property_u32(dev, "qcom,cdc-mclk-clk-rate", + &prop_val))) + pdata->mclk_rate = prop_val; + + if (pdata->mclk_rate != WCD9XXX_MCLK_CLK_9P6HZ && + pdata->mclk_rate != WCD9XXX_MCLK_CLK_12P288MHZ) { + dev_err(dev, "%s: Invalid mclk_rate = %u\n", __func__, + pdata->mclk_rate); + goto err_parse_dt_prop; + } + + if (!(wcd9xxx_read_of_property_u32(dev, "qcom,cdc-dmic-sample-rate", + &prop_val))) + dmic_sample_rate = prop_val; + + pdata->dmic_sample_rate = wcd9xxx_validate_dmic_sample_rate(dev, + dmic_sample_rate, + pdata->mclk_rate, + "audio_dmic_rate"); + if (!(wcd9xxx_read_of_property_u32(dev, "qcom,cdc-mad-dmic-rate", + &prop_val))) + mad_dmic_sample_rate = prop_val; + + pdata->mad_dmic_sample_rate = wcd9xxx_validate_dmic_sample_rate(dev, + mad_dmic_sample_rate, + pdata->mclk_rate, + "mad_dmic_rate"); + + if (of_find_property(dev->of_node, "qcom,cdc-ecpp-dmic-rate", NULL)) { + rc = wcd9xxx_read_of_property_u32(dev, + "qcom,cdc-ecpp-dmic-rate", + &prop_val); + if (!rc) + ecpp_dmic_sample_rate = prop_val; + } + + pdata->ecpp_dmic_sample_rate = wcd9xxx_validate_dmic_sample_rate(dev, + ecpp_dmic_sample_rate, + pdata->mclk_rate, + "ecpp_dmic_rate"); + + if (!(of_property_read_u32(dev->of_node, + "qcom,cdc-dmic-clk-drv-strength", + &prop_val))) { + dmic_clk_drive = prop_val; + + if (dmic_clk_drive != 2 && dmic_clk_drive != 4 && + dmic_clk_drive != 8 && dmic_clk_drive != 16) + dev_err(dev, "Invalid cdc-dmic-clk-drv-strength %d\n", + dmic_clk_drive); + } + + pdata->dmic_clk_drv = dmic_clk_drive; + + return pdata; + +err_parse_dt_prop: + devm_kfree(dev, pdata->regulator); + pdata->regulator = NULL; + pdata->num_supplies = 0; +err_power_sup: + devm_kfree(dev, pdata); + return NULL; +} +EXPORT_SYMBOL(wcd9xxx_populate_dt_data); + +static bool is_wcd9xxx_reg_power_down(struct wcd9xxx *wcd9xxx, u16 rreg) +{ + bool ret = false; + int i; + struct wcd9xxx_power_region *wcd9xxx_pwr; + + if (!wcd9xxx) + return ret; + + for (i = 0; i < WCD9XXX_MAX_PWR_REGIONS; i++) { + wcd9xxx_pwr = wcd9xxx->wcd9xxx_pwr[i]; + if (!wcd9xxx_pwr) + continue; + if (((wcd9xxx_pwr->pwr_collapse_reg_min == 0) && + (wcd9xxx_pwr->pwr_collapse_reg_max == 0)) || + (wcd9xxx_pwr->power_state == + WCD_REGION_POWER_COLLAPSE_REMOVE)) + ret = false; + else if (((wcd9xxx_pwr->power_state == + WCD_REGION_POWER_DOWN) || + (wcd9xxx_pwr->power_state == + WCD_REGION_POWER_COLLAPSE_BEGIN)) && + (rreg >= wcd9xxx_pwr->pwr_collapse_reg_min) && + (rreg <= wcd9xxx_pwr->pwr_collapse_reg_max)) + ret = true; + } + return ret; +} + +/* + * wcd9xxx_page_write: + * Retrieve page number from register and + * write that page number to the page address. + * Called under io_lock acquisition. + * + * @wcd9xxx: pointer to wcd9xxx + * @reg: Register address from which page number is retrieved + * + * Returns 0 for success and negative error code for failure. + */ +int wcd9xxx_page_write(struct wcd9xxx *wcd9xxx, unsigned short *reg) +{ + int ret = 0; + unsigned short c_reg, reg_addr; + u8 pg_num, prev_pg_num; + + if (wcd9xxx->type != WCD9335 && wcd9xxx->type != WCD934X && + wcd9xxx->type != WCD9360) + return ret; + + c_reg = *reg; + pg_num = c_reg >> 8; + reg_addr = c_reg & 0xff; + if (wcd9xxx->prev_pg_valid) { + prev_pg_num = wcd9xxx->prev_pg; + if (prev_pg_num != pg_num) { + ret = wcd9xxx->write_dev( + wcd9xxx, PAGE_REG_ADDR, 1, + (void *) &pg_num, false); + if (ret < 0) + pr_err("page write error, pg_num: 0x%x\n", + pg_num); + else { + wcd9xxx->prev_pg = pg_num; + dev_dbg(wcd9xxx->dev, "%s: Page 0x%x Write to 0x00\n", + __func__, pg_num); + } + } + } else { + ret = wcd9xxx->write_dev( + wcd9xxx, PAGE_REG_ADDR, 1, (void *) &pg_num, + false); + if (ret < 0) + pr_err("page write error, pg_num: 0x%x\n", pg_num); + else { + wcd9xxx->prev_pg = pg_num; + wcd9xxx->prev_pg_valid = true; + dev_dbg(wcd9xxx->dev, "%s: Page 0x%x Write to 0x00\n", + __func__, pg_num); + } + } + *reg = reg_addr; + return ret; +} +EXPORT_SYMBOL(wcd9xxx_page_write); + +static int regmap_bus_read(void *context, const void *reg, size_t reg_size, + void *val, size_t val_size) +{ + struct device *dev = context; + struct wcd9xxx *wcd9xxx = dev_get_drvdata(dev); + unsigned short c_reg, rreg; + int ret, i; + + if (!wcd9xxx) { + dev_err(dev, "%s: wcd9xxx is NULL\n", __func__); + return -EINVAL; + } + if (!reg || !val) { + dev_err(dev, "%s: reg or val is NULL\n", __func__); + return -EINVAL; + } + + if (reg_size != REG_BYTES) { + dev_err(dev, "%s: register size %zd bytes, not supported\n", + __func__, reg_size); + return -EINVAL; + } + + mutex_lock(&wcd9xxx->io_lock); + c_reg = *(u16 *)reg; + rreg = c_reg; + + if (is_wcd9xxx_reg_power_down(wcd9xxx, rreg)) { + ret = 0; + for (i = 0; i < val_size; i++) + ((u8 *)val)[i] = 0; + goto err; + } + ret = wcd9xxx_page_write(wcd9xxx, &c_reg); + if (ret) + goto err; + ret = wcd9xxx->read_dev(wcd9xxx, c_reg, val_size, val, false); + if (ret < 0) + dev_err(dev, "%s: Codec read failed (%d), reg: 0x%x, size:%zd\n", + __func__, ret, rreg, val_size); + else { + for (i = 0; i < val_size; i++) + dev_dbg(dev, "%s: Read 0x%02x from 0x%x\n", + __func__, ((u8 *)val)[i], rreg + i); + } +err: + mutex_unlock(&wcd9xxx->io_lock); + + return ret; +} + +static int regmap_bus_gather_write(void *context, + const void *reg, size_t reg_size, + const void *val, size_t val_size) +{ + struct device *dev = context; + struct wcd9xxx *wcd9xxx = dev_get_drvdata(dev); + unsigned short c_reg, rreg; + int ret, i; + + if (!wcd9xxx) { + dev_err(dev, "%s: wcd9xxx is NULL\n", __func__); + return -EINVAL; + } + if (!reg || !val) { + dev_err(dev, "%s: reg or val is NULL\n", __func__); + return -EINVAL; + } + if (reg_size != REG_BYTES) { + dev_err(dev, "%s: register size %zd bytes, not supported\n", + __func__, reg_size); + return -EINVAL; + } + mutex_lock(&wcd9xxx->io_lock); + c_reg = *(u16 *)reg; + rreg = c_reg; + + if (is_wcd9xxx_reg_power_down(wcd9xxx, rreg)) { + ret = 0; + goto err; + } + ret = wcd9xxx_page_write(wcd9xxx, &c_reg); + if (ret) + goto err; + + for (i = 0; i < val_size; i++) + dev_dbg(dev, "Write %02x to 0x%x\n", ((u8 *)val)[i], + rreg + i); + + ret = wcd9xxx->write_dev(wcd9xxx, c_reg, val_size, (void *) val, + false); + if (ret < 0) + dev_err(dev, "%s: Codec write failed (%d), reg:0x%x, size:%zd\n", + __func__, ret, rreg, val_size); + +err: + mutex_unlock(&wcd9xxx->io_lock); + return ret; +} + +static int regmap_bus_write(void *context, const void *data, size_t count) +{ + struct device *dev = context; + struct wcd9xxx *wcd9xxx = dev_get_drvdata(dev); + + if (!wcd9xxx) + return -EINVAL; + + WARN_ON(count < REG_BYTES); + + if (count > (REG_BYTES + VAL_BYTES)) { + if (wcd9xxx->multi_reg_write) + return wcd9xxx->multi_reg_write(wcd9xxx, + data, count); + } else + return regmap_bus_gather_write(context, data, REG_BYTES, + data + REG_BYTES, + count - REG_BYTES); + + dev_err(dev, "%s: bus multi reg write failure\n", __func__); + + return -EINVAL; +} + +static struct regmap_bus regmap_bus_config = { + .write = regmap_bus_write, + .gather_write = regmap_bus_gather_write, + .read = regmap_bus_read, + .reg_format_endian_default = REGMAP_ENDIAN_NATIVE, + .val_format_endian_default = REGMAP_ENDIAN_NATIVE, +}; + +/* + * wcd9xxx_regmap_init: + * Initialize wcd9xxx register map + * + * @dev: pointer to wcd device + * @config: pointer to register map config + * + * Returns pointer to regmap structure for success + * or NULL in case of failure. + */ +struct regmap *wcd9xxx_regmap_init(struct device *dev, + const struct regmap_config *config) +{ + return devm_regmap_init(dev, ®map_bus_config, dev, config); +} +EXPORT_SYMBOL(wcd9xxx_regmap_init); + +/* + * wcd9xxx_reset: + * Reset wcd9xxx codec + * + * @dev: pointer to wcd device + * + * Returns 0 for success or negative error code in case of failure + */ +int wcd9xxx_reset(struct device *dev) +{ + struct wcd9xxx *wcd9xxx; + int rc; + int value; + + if (!dev) + return -ENODEV; + + wcd9xxx = dev_get_drvdata(dev); + if (!wcd9xxx) + return -EINVAL; + + if (!wcd9xxx->wcd_rst_np) { + dev_err(dev, "%s: reset gpio device node not specified\n", + __func__); + return -EINVAL; + } + + value = msm_cdc_pinctrl_get_state(wcd9xxx->wcd_rst_np); + if (value > 0) { + wcd9xxx->avoid_cdc_rstlow = 1; + return 0; + } + + rc = msm_cdc_pinctrl_select_sleep_state(wcd9xxx->wcd_rst_np); + if (rc) { + dev_err(dev, "%s: wcd sleep state request fail!\n", + __func__); + return rc; + } + + /* 20ms sleep required after pulling the reset gpio to LOW */ + msleep(20); + + rc = msm_cdc_pinctrl_select_active_state(wcd9xxx->wcd_rst_np); + if (rc) { + dev_err(dev, "%s: wcd active state request fail!\n", + __func__); + return rc; + } + msleep(20); + + return rc; +} +EXPORT_SYMBOL(wcd9xxx_reset); + +/* + * wcd9xxx_reset_low: + * Pull the wcd9xxx codec reset_n to low + * + * @dev: pointer to wcd device + * + * Returns 0 for success or negative error code in case of failure + */ +int wcd9xxx_reset_low(struct device *dev) +{ + struct wcd9xxx *wcd9xxx; + int rc; + + if (!dev) + return -ENODEV; + + wcd9xxx = dev_get_drvdata(dev); + if (!wcd9xxx) + return -EINVAL; + + if (!wcd9xxx->wcd_rst_np) { + dev_err(dev, "%s: reset gpio device node not specified\n", + __func__); + return -EINVAL; + } + if (wcd9xxx->avoid_cdc_rstlow) { + wcd9xxx->avoid_cdc_rstlow = 0; + dev_dbg(dev, "%s: avoid pull down of reset GPIO\n", __func__); + return 0; + } + + rc = msm_cdc_pinctrl_select_sleep_state(wcd9xxx->wcd_rst_np); + if (rc) + dev_err(dev, "%s: wcd sleep state request fail!\n", + __func__); + + return rc; +} +EXPORT_SYMBOL(wcd9xxx_reset_low); + +/* + * wcd9xxx_bringup: + * Toggle reset analog and digital cores of wcd9xxx codec + * + * @dev: pointer to wcd device + * + * Returns 0 for success or negative error code in case of failure + */ +int wcd9xxx_bringup(struct device *dev) +{ + struct wcd9xxx *wcd9xxx; + int rc; + codec_bringup_fn cdc_bup_fn; + + if (!dev) + return -ENODEV; + + wcd9xxx = dev_get_drvdata(dev); + if (!wcd9xxx) + return -EINVAL; + + cdc_bup_fn = wcd9xxx_bringup_fn(wcd9xxx->type); + if (!cdc_bup_fn) { + dev_err(dev, "%s: Codec bringup fn NULL!\n", + __func__); + return -EINVAL; + } + rc = cdc_bup_fn(wcd9xxx); + if (rc) + dev_err(dev, "%s: Codec bringup error, rc: %d\n", + __func__, rc); + + return rc; +} +EXPORT_SYMBOL(wcd9xxx_bringup); + +/* + * wcd9xxx_bringup: + * Set analog and digital cores of wcd9xxx codec in reset state + * + * @dev: pointer to wcd device + * + * Returns 0 for success or negative error code in case of failure + */ +int wcd9xxx_bringdown(struct device *dev) +{ + struct wcd9xxx *wcd9xxx; + int rc; + codec_bringdown_fn cdc_bdown_fn; + + if (!dev) + return -ENODEV; + + wcd9xxx = dev_get_drvdata(dev); + if (!wcd9xxx) + return -EINVAL; + + cdc_bdown_fn = wcd9xxx_bringdown_fn(wcd9xxx->type); + if (!cdc_bdown_fn) { + dev_err(dev, "%s: Codec bring down fn NULL!\n", + __func__); + return -EINVAL; + } + rc = cdc_bdown_fn(wcd9xxx); + if (rc) + dev_err(dev, "%s: Codec bring down error, rc: %d\n", + __func__, rc); + + return rc; +} +EXPORT_SYMBOL(wcd9xxx_bringdown); + +/* + * wcd9xxx_get_codec_info: + * Fill codec specific information like interrupts, version + * + * @dev: pointer to wcd device + * + * Returns 0 for success or negative error code in case of failure + */ +int wcd9xxx_get_codec_info(struct device *dev) +{ + struct wcd9xxx *wcd9xxx; + int rc; + codec_type_fn cdc_type_fn; + struct wcd9xxx_codec_type *cinfo; + + if (!dev) + return -ENODEV; + + wcd9xxx = dev_get_drvdata(dev); + if (!wcd9xxx) + return -EINVAL; + + cdc_type_fn = wcd9xxx_get_codec_info_fn(wcd9xxx->type); + if (!cdc_type_fn) { + dev_err(dev, "%s: Codec fill type fn NULL!\n", + __func__); + return -EINVAL; + } + + cinfo = wcd9xxx->codec_type; + if (!cinfo) + return -EINVAL; + + rc = cdc_type_fn(wcd9xxx, cinfo); + if (rc) { + dev_err(dev, "%s: Codec type fill failed, rc:%d\n", + __func__, rc); + return rc; + + } + + switch (wcd9xxx->type) { + case WCD9360: + cinfo->dev = pahu_devs; + cinfo->size = ARRAY_SIZE(pahu_devs); + break; + case WCD934X: + cinfo->dev = tavil_devs; + cinfo->size = ARRAY_SIZE(tavil_devs); + break; + case WCD9335: + cinfo->dev = tasha_devs; + cinfo->size = ARRAY_SIZE(tasha_devs); + break; + case WCD9330: + cinfo->dev = tomtom_devs; + cinfo->size = ARRAY_SIZE(tomtom_devs); + break; + default: + cinfo->dev = NULL; + cinfo->size = 0; + break; + } + + return rc; +} +EXPORT_SYMBOL(wcd9xxx_get_codec_info); + +/* + * wcd9xxx_core_irq_init: + * Initialize wcd9xxx codec irq instance + * + * @wcd9xxx_core_res: pointer to wcd core resource + * + * Returns 0 for success or negative error code in case of failure + */ +int wcd9xxx_core_irq_init( + struct wcd9xxx_core_resource *wcd9xxx_core_res) +{ + int ret = 0; + + if (!wcd9xxx_core_res) + return -EINVAL; + + if (wcd9xxx_core_res->irq != 1) { + ret = wcd9xxx_irq_init(wcd9xxx_core_res); + if (ret) + pr_err("IRQ initialization failed\n"); + } + + return ret; +} +EXPORT_SYMBOL(wcd9xxx_core_irq_init); + +/* + * wcd9xxx_assign_irq: + * Assign irq and irq_base to wcd9xxx core resource + * + * @wcd9xxx_core_res: pointer to wcd core resource + * @irq: irq number + * @irq_base: base irq number + * + * Returns 0 for success or negative error code in case of failure + */ +int wcd9xxx_assign_irq( + struct wcd9xxx_core_resource *wcd9xxx_core_res, + unsigned int irq, + unsigned int irq_base) +{ + if (!wcd9xxx_core_res) + return -EINVAL; + + wcd9xxx_core_res->irq = irq; + wcd9xxx_core_res->irq_base = irq_base; + + return 0; +} +EXPORT_SYMBOL(wcd9xxx_assign_irq); + +/* + * wcd9xxx_core_res_init: + * Initialize wcd core resource instance + * + * @wcd9xxx_core_res: pointer to wcd core resource + * @num_irqs: number of irqs for wcd9xxx core + * @num_irq_regs: number of irq registers + * @wcd_regmap: pointer to the wcd register map + * + * Returns 0 for success or negative error code in case of failure + */ +int wcd9xxx_core_res_init( + struct wcd9xxx_core_resource *wcd9xxx_core_res, + int num_irqs, int num_irq_regs, struct regmap *wcd_regmap) +{ + if (!wcd9xxx_core_res || !wcd_regmap) + return -EINVAL; + + mutex_init(&wcd9xxx_core_res->pm_lock); + wcd9xxx_core_res->wlock_holders = 0; + wcd9xxx_core_res->pm_state = WCD9XXX_PM_SLEEPABLE; + init_waitqueue_head(&wcd9xxx_core_res->pm_wq); + pm_qos_add_request(&wcd9xxx_core_res->pm_qos_req, + PM_QOS_CPU_DMA_LATENCY, + PM_QOS_DEFAULT_VALUE); + + wcd9xxx_core_res->num_irqs = num_irqs; + wcd9xxx_core_res->num_irq_regs = num_irq_regs; + wcd9xxx_core_res->wcd_core_regmap = wcd_regmap; + + pr_debug("%s: num_irqs = %d, num_irq_regs = %d\n", + __func__, wcd9xxx_core_res->num_irqs, + wcd9xxx_core_res->num_irq_regs); + + return 0; +} +EXPORT_SYMBOL(wcd9xxx_core_res_init); + +/* + * wcd9xxx_core_res_deinit: + * Deinit wcd core resource instance + * + * @wcd9xxx_core_res: pointer to wcd core resource + */ +void wcd9xxx_core_res_deinit(struct wcd9xxx_core_resource *wcd9xxx_core_res) +{ + if (!wcd9xxx_core_res) + return; + + pm_qos_remove_request(&wcd9xxx_core_res->pm_qos_req); + mutex_destroy(&wcd9xxx_core_res->pm_lock); +} +EXPORT_SYMBOL(wcd9xxx_core_res_deinit); + +/* + * wcd9xxx_pm_cmpxchg: + * Check old state and exchange with pm new state + * if old state matches with current state + * + * @wcd9xxx_core_res: pointer to wcd core resource + * @o: pm old state + * @n: pm new state + * + * Returns old state + */ +enum wcd9xxx_pm_state wcd9xxx_pm_cmpxchg( + struct wcd9xxx_core_resource *wcd9xxx_core_res, + enum wcd9xxx_pm_state o, + enum wcd9xxx_pm_state n) +{ + enum wcd9xxx_pm_state old; + + if (!wcd9xxx_core_res) + return o; + + mutex_lock(&wcd9xxx_core_res->pm_lock); + old = wcd9xxx_core_res->pm_state; + if (old == o) + wcd9xxx_core_res->pm_state = n; + mutex_unlock(&wcd9xxx_core_res->pm_lock); + + return old; +} +EXPORT_SYMBOL(wcd9xxx_pm_cmpxchg); + +/* + * wcd9xxx_core_res_suspend: + * Suspend callback function for wcd9xxx core + * + * @wcd9xxx_core_res: pointer to wcd core resource + * @pm_message_t: pm message + * + * Returns 0 for success or negative error code for failure/busy + */ +int wcd9xxx_core_res_suspend( + struct wcd9xxx_core_resource *wcd9xxx_core_res, + pm_message_t pmesg) +{ + int ret = 0; + + pr_debug("%s: enter\n", __func__); + /* + * pm_qos_update_request() can be called after this suspend chain call + * started. thus suspend can be called while lock is being held + */ + mutex_lock(&wcd9xxx_core_res->pm_lock); + if (wcd9xxx_core_res->pm_state == WCD9XXX_PM_SLEEPABLE) { + pr_debug("%s: suspending system, state %d, wlock %d\n", + __func__, wcd9xxx_core_res->pm_state, + wcd9xxx_core_res->wlock_holders); + wcd9xxx_core_res->pm_state = WCD9XXX_PM_ASLEEP; + } else if (wcd9xxx_core_res->pm_state == WCD9XXX_PM_AWAKE) { + /* + * unlock to wait for pm_state == WCD9XXX_PM_SLEEPABLE + * then set to WCD9XXX_PM_ASLEEP + */ + pr_debug("%s: waiting to suspend system, state %d, wlock %d\n", + __func__, wcd9xxx_core_res->pm_state, + wcd9xxx_core_res->wlock_holders); + mutex_unlock(&wcd9xxx_core_res->pm_lock); + if (!(wait_event_timeout(wcd9xxx_core_res->pm_wq, + wcd9xxx_pm_cmpxchg(wcd9xxx_core_res, + WCD9XXX_PM_SLEEPABLE, + WCD9XXX_PM_ASLEEP) == + WCD9XXX_PM_SLEEPABLE, + HZ))) { + pr_debug("%s: suspend failed state %d, wlock %d\n", + __func__, wcd9xxx_core_res->pm_state, + wcd9xxx_core_res->wlock_holders); + ret = -EBUSY; + } else { + pr_debug("%s: done, state %d, wlock %d\n", __func__, + wcd9xxx_core_res->pm_state, + wcd9xxx_core_res->wlock_holders); + } + mutex_lock(&wcd9xxx_core_res->pm_lock); + } else if (wcd9xxx_core_res->pm_state == WCD9XXX_PM_ASLEEP) { + pr_warn("%s: system is already suspended, state %d, wlock %dn", + __func__, wcd9xxx_core_res->pm_state, + wcd9xxx_core_res->wlock_holders); + } + mutex_unlock(&wcd9xxx_core_res->pm_lock); + + return ret; +} +EXPORT_SYMBOL(wcd9xxx_core_res_suspend); + +/* + * wcd9xxx_core_res_resume: + * Resume callback function for wcd9xxx core + * + * @wcd9xxx_core_res: pointer to wcd core resource + * + * Returns 0 for success or negative error code for failure/busy + */ +int wcd9xxx_core_res_resume( + struct wcd9xxx_core_resource *wcd9xxx_core_res) +{ + int ret = 0; + + pr_debug("%s: enter\n", __func__); + mutex_lock(&wcd9xxx_core_res->pm_lock); + if (wcd9xxx_core_res->pm_state == WCD9XXX_PM_ASLEEP) { + pr_debug("%s: resuming system, state %d, wlock %d\n", __func__, + wcd9xxx_core_res->pm_state, + wcd9xxx_core_res->wlock_holders); + wcd9xxx_core_res->pm_state = WCD9XXX_PM_SLEEPABLE; + } else { + pr_warn("%s: system is already awake, state %d wlock %d\n", + __func__, wcd9xxx_core_res->pm_state, + wcd9xxx_core_res->wlock_holders); + } + mutex_unlock(&wcd9xxx_core_res->pm_lock); + wake_up_all(&wcd9xxx_core_res->pm_wq); + + return ret; +} +EXPORT_SYMBOL(wcd9xxx_core_res_resume); + +/* + * wcd9xxx_get_intf_type: + * Get interface type of wcd9xxx core + * + * Returns interface type + */ +enum wcd9xxx_intf_status wcd9xxx_get_intf_type(void) +{ + return wcd9xxx_intf; +} +EXPORT_SYMBOL(wcd9xxx_get_intf_type); + +/* + * wcd9xxx_set_intf_type: + * Set interface type of wcd9xxx core + * + */ +void wcd9xxx_set_intf_type(enum wcd9xxx_intf_status intf_status) +{ + wcd9xxx_intf = intf_status; +} +EXPORT_SYMBOL(wcd9xxx_set_intf_type); + +/* + * wcd9xxx_set_power_state: set power state for the region + * @wcd9xxx: handle to wcd core + * @state: power state to be set + * @region: region index + * + * Returns error code in case of failure or 0 for success + */ +int wcd9xxx_set_power_state(struct wcd9xxx *wcd9xxx, + enum codec_power_states state, + enum wcd_power_regions region) +{ + if (!wcd9xxx) { + pr_err("%s: wcd9xxx is NULL\n", __func__); + return -EINVAL; + } + + if ((region < 0) || (region >= WCD9XXX_MAX_PWR_REGIONS)) { + dev_err(wcd9xxx->dev, "%s: region index %d out of bounds\n", + __func__, region); + return -EINVAL; + } + if (!wcd9xxx->wcd9xxx_pwr[region]) { + dev_err(wcd9xxx->dev, "%s: memory not created for region: %d\n", + __func__, region); + return -EINVAL; + } + mutex_lock(&wcd9xxx->io_lock); + wcd9xxx->wcd9xxx_pwr[region]->power_state = state; + mutex_unlock(&wcd9xxx->io_lock); + + return 0; +} +EXPORT_SYMBOL(wcd9xxx_set_power_state); + +/* + * wcd9xxx_get_current_power_state: Get power state of the region + * @wcd9xxx: handle to wcd core + * @region: region index + * + * Returns current power state of the region or error code for failure + */ +int wcd9xxx_get_current_power_state(struct wcd9xxx *wcd9xxx, + enum wcd_power_regions region) +{ + int state; + + if (!wcd9xxx) { + pr_err("%s: wcd9xxx is NULL\n", __func__); + return -EINVAL; + } + + if ((region < 0) || (region >= WCD9XXX_MAX_PWR_REGIONS)) { + dev_err(wcd9xxx->dev, "%s: region index %d out of bounds\n", + __func__, region); + return -EINVAL; + } + if (!wcd9xxx->wcd9xxx_pwr[region]) { + dev_err(wcd9xxx->dev, "%s: memory not created for region: %d\n", + __func__, region); + return -EINVAL; + } + + mutex_lock(&wcd9xxx->io_lock); + state = wcd9xxx->wcd9xxx_pwr[region]->power_state; + mutex_unlock(&wcd9xxx->io_lock); + + return state; +} +EXPORT_SYMBOL(wcd9xxx_get_current_power_state); diff --git a/audio-kernel/asoc/codecs/wcd9xxx-utils.h b/audio-kernel/asoc/codecs/wcd9xxx-utils.h new file mode 100644 index 000000000..a7ec56c2d --- /dev/null +++ b/audio-kernel/asoc/codecs/wcd9xxx-utils.h @@ -0,0 +1,40 @@ +/* Copyright (c) 2016-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 + * 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 __WCD9XXX_UTILS_H__ +#define __WCD9XXX_UTILS_H__ + +#include +#include +#include +#include "pdata.h" +#include "core.h" + +struct wcd9xxx_pdata *wcd9xxx_populate_dt_data(struct device *dev); +int wcd9xxx_bringup(struct device *dev); +int wcd9xxx_bringdown(struct device *dev); +struct regmap *wcd9xxx_regmap_init(struct device *dev, + const struct regmap_config *config); +int wcd9xxx_reset(struct device *dev); +int wcd9xxx_reset_low(struct device *dev); +int wcd9xxx_get_codec_info(struct device *dev); + +typedef int (*codec_bringup_fn)(struct wcd9xxx *); +typedef int (*codec_bringdown_fn)(struct wcd9xxx *); +typedef int (*codec_type_fn)(struct wcd9xxx *, + struct wcd9xxx_codec_type *); + +codec_bringdown_fn wcd9xxx_bringdown_fn(int type); +codec_bringup_fn wcd9xxx_bringup_fn(int type); +codec_type_fn wcd9xxx_get_codec_info_fn(int type); + +#endif diff --git a/audio-kernel/asoc/codecs/wcd_cmi_api.h b/audio-kernel/asoc/codecs/wcd_cmi_api.h new file mode 100644 index 000000000..39be6417e --- /dev/null +++ b/audio-kernel/asoc/codecs/wcd_cmi_api.h @@ -0,0 +1,43 @@ +/* Copyright (c) 2014, 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 __CMI_API__ +#define __CMI_API__ + +enum cmi_api_result { + CMI_API_FAILED = 1, + CMI_API_BUSY, + CMI_API_NO_MEMORY, + CMI_API_NOT_READY, +}; + +enum cmi_api_event { + CMI_API_MSG = 1, + CMI_API_OFFLINE, + CMI_API_ONLINE, + CMI_API_DEINITIALIZED, +}; + +struct cmi_api_notification { + enum cmi_api_event event; + enum cmi_api_result result; + void *message; +}; + +void *cmi_register( + void notification_callback + (const struct cmi_api_notification *parameter), + u32 service); +enum cmi_api_result cmi_deregister(void *reg_handle); +enum cmi_api_result cmi_send_msg(void *message); + +#endif /*__CMI_API__*/ diff --git a/audio-kernel/asoc/codecs/wcd_cpe_core.c b/audio-kernel/asoc/codecs/wcd_cpe_core.c new file mode 100644 index 000000000..2e154e701 --- /dev/null +++ b/audio-kernel/asoc/codecs/wcd_cpe_core.c @@ -0,0 +1,4598 @@ +/* Copyright (c) 2014-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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "core.h" +#include "cpe_core.h" +#include "cpe_err.h" +#include "cpe_cmi.h" +#include "wcd_cpe_core.h" +#include "wcd_cpe_services.h" +#include "wcd_cmi_api.h" +#include "wcd9xxx-irq.h" + +#define CMI_CMD_TIMEOUT (10 * HZ) +#define WCD_CPE_LSM_MAX_SESSIONS 2 +#define WCD_CPE_AFE_MAX_PORTS 4 +#define AFE_SVC_EXPLICIT_PORT_START 1 +#define WCD_CPE_EC_PP_BUF_SIZE 480 /* 5 msec buffer */ + +#define ELF_FLAG_EXECUTE (1 << 0) +#define ELF_FLAG_WRITE (1 << 1) +#define ELF_FLAG_READ (1 << 2) + +#define ELF_FLAG_RW (ELF_FLAG_READ | ELF_FLAG_WRITE) + +#define WCD_CPE_GRAB_LOCK(lock, name) \ +{ \ + pr_debug("%s: %s lock acquire\n", \ + __func__, name); \ + mutex_lock(lock); \ +} + +#define WCD_CPE_REL_LOCK(lock, name) \ +{ \ + pr_debug("%s: %s lock release\n", \ + __func__, name); \ + mutex_unlock(lock); \ +} + +#define WCD_CPE_STATE_MAX_LEN 11 +#define CPE_OFFLINE_WAIT_TIMEOUT (2 * HZ) +#define CPE_READY_WAIT_TIMEOUT (3 * HZ) +#define WCD_CPE_SYSFS_DIR_MAX_LENGTH 32 + +#define CPE_ERR_IRQ_CB(core) \ + (core->cpe_cdc_cb->cpe_err_irq_control) + +/* + * AFE output buffer size is always + * (sample_rate * number of bytes per sample/2*1000) + */ +#define AFE_OUT_BUF_SIZE(bit_width, sample_rate) \ + (((sample_rate) * (bit_width / BITS_PER_BYTE))/(2*1000)) + +enum afe_port_state { + AFE_PORT_STATE_DEINIT = 0, + AFE_PORT_STATE_INIT, + AFE_PORT_STATE_CONFIG, + AFE_PORT_STATE_STARTED, + AFE_PORT_STATE_SUSPENDED, +}; + +struct wcd_cmi_afe_port_data { + u8 port_id; + struct mutex afe_lock; + struct completion afe_cmd_complete; + enum afe_port_state port_state; + u8 cmd_result; + u32 mem_handle; +}; + +struct cpe_lsm_ids { + u32 module_id; + u32 param_id; +}; + +static struct wcd_cpe_core *core_d; +static struct cpe_lsm_session + *lsm_sessions[WCD_CPE_LSM_MAX_SESSIONS + 1]; +struct wcd_cpe_core * (*wcd_get_cpe_core)(struct snd_soc_codec *); +static struct wcd_cmi_afe_port_data afe_ports[WCD_CPE_AFE_MAX_PORTS + 1]; +static void wcd_cpe_svc_event_cb(const struct cpe_svc_notification *param); +static int wcd_cpe_setup_irqs(struct wcd_cpe_core *core); +static void wcd_cpe_cleanup_irqs(struct wcd_cpe_core *core); +static ssize_t cpe_ftm_test_trigger(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos); +static u32 ramdump_enable; +static u32 cpe_ftm_test_status; +static const struct file_operations cpe_ftm_test_trigger_fops = { + .open = simple_open, + .write = cpe_ftm_test_trigger, +}; + +static int wcd_cpe_afe_svc_cmd_mode(void *core_handle, + u8 mode); +struct wcd_cpe_attribute { + struct attribute attr; + ssize_t (*show)(struct wcd_cpe_core *core, char *buf); + ssize_t (*store)(struct wcd_cpe_core *core, const char *buf, + ssize_t count); +}; + +#define WCD_CPE_ATTR(_name, _mode, _show, _store) \ +static struct wcd_cpe_attribute cpe_attr_##_name = { \ + .attr = {.name = __stringify(_name), .mode = _mode}, \ + .show = _show, \ + .store = _store, \ +} + +#define to_wcd_cpe_attr(a) \ + container_of((a), struct wcd_cpe_attribute, attr) + +#define kobj_to_cpe_core(kobj) \ + container_of((kobj), struct wcd_cpe_core, cpe_kobj) + +/* wcd_cpe_lsm_session_active: check if any session is active + * return true if any session is active. + */ +static bool wcd_cpe_lsm_session_active(void) +{ + int index = 1; + bool lsm_active = false; + + /* session starts from index 1 */ + for (; index <= WCD_CPE_LSM_MAX_SESSIONS; index++) { + if (lsm_sessions[index] != NULL) { + lsm_active = true; + break; + } else { + lsm_active = false; + } + } + return lsm_active; +} + +static int wcd_cpe_get_sfr_dump(struct wcd_cpe_core *core) +{ + struct cpe_svc_mem_segment dump_seg; + int rc; + u8 *sfr_dump; + + sfr_dump = kzalloc(core->sfr_buf_size, GFP_KERNEL); + if (!sfr_dump) + goto done; + + dump_seg.type = CPE_SVC_DATA_MEM; + dump_seg.cpe_addr = core->sfr_buf_addr; + dump_seg.size = core->sfr_buf_size; + dump_seg.data = sfr_dump; + dev_dbg(core->dev, + "%s: reading SFR from CPE, size = %zu\n", + __func__, core->sfr_buf_size); + + rc = cpe_svc_ramdump(core->cpe_handle, &dump_seg); + if (rc < 0) { + dev_err(core->dev, + "%s: Failed to read cpe sfr_dump, err = %d\n", + __func__, rc); + goto free_sfr_dump; + } + + dev_info(core->dev, + "%s: cpe_sfr = %s\n", __func__, sfr_dump); + +free_sfr_dump: + kfree(sfr_dump); +done: + /* Even if SFR dump failed, do not return error */ + return 0; +} + +static int wcd_cpe_collect_ramdump(struct wcd_cpe_core *core) +{ + struct cpe_svc_mem_segment dump_seg; + int rc; + + if (!core->cpe_ramdump_dev || !core->cpe_dump_v_addr || + core->hw_info.dram_size == 0) { + dev_err(core->dev, + "%s: Ramdump devices not set up, size = %zu\n", + __func__, core->hw_info.dram_size); + return -EINVAL; + } + + dump_seg.type = CPE_SVC_DATA_MEM; + dump_seg.cpe_addr = core->hw_info.dram_offset; + dump_seg.size = core->hw_info.dram_size; + dump_seg.data = core->cpe_dump_v_addr; + + dev_dbg(core->dev, + "%s: Reading ramdump from CPE\n", + __func__); + + rc = cpe_svc_ramdump(core->cpe_handle, &dump_seg); + if (rc < 0) { + dev_err(core->dev, + "%s: Failed to read CPE ramdump, err = %d\n", + __func__, rc); + return rc; + } + + dev_dbg(core->dev, + "%s: completed reading ramdump from CPE\n", + __func__); + + core->cpe_ramdump_seg.address = (unsigned long) core->cpe_dump_addr; + core->cpe_ramdump_seg.size = core->hw_info.dram_size; + core->cpe_ramdump_seg.v_address = core->cpe_dump_v_addr; + + rc = do_ramdump(core->cpe_ramdump_dev, + &core->cpe_ramdump_seg, 1); + if (rc) + dev_err(core->dev, + "%s: fail to dump cpe ram to device, err = %d\n", + __func__, rc); + return rc; +} + +/* wcd_cpe_is_valid_elf_hdr: check if the ELF header is valid + * @core: handle to wcd_cpe_core + * @fw_size: size of firmware from request_firmware + * @ehdr: the elf header to be checked for + * return true if all checks pass, true if any elf check fails + */ +static bool wcd_cpe_is_valid_elf_hdr(struct wcd_cpe_core *core, size_t fw_size, + const struct elf32_hdr *ehdr) +{ + if (fw_size < sizeof(*ehdr)) { + dev_err(core->dev, "%s:Firmware too small\n", __func__); + goto elf_check_fail; + } + + if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG) != 0) { + dev_err(core->dev, "%s: Not an ELF file\n", __func__); + goto elf_check_fail; + } + + if (ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN) { + dev_err(core->dev, "%s: Not a executable image\n", __func__); + goto elf_check_fail; + } + + if (ehdr->e_phnum == 0) { + dev_err(core->dev, "%s: no segments to load\n", __func__); + goto elf_check_fail; + } + + if (sizeof(struct elf32_phdr) * ehdr->e_phnum + + sizeof(struct elf32_hdr) > fw_size) { + dev_err(core->dev, "%s: Too small MDT file\n", __func__); + goto elf_check_fail; + } + + return true; + +elf_check_fail: + return false; +} + +/* + * wcd_cpe_load_each_segment: download segment to CPE + * @core: handle to struct wcd_cpe_core + * @file_idx: index of split firmware image file name + * @phdr: program header from metadata + */ +static int wcd_cpe_load_each_segment(struct wcd_cpe_core *core, + int file_idx, const struct elf32_phdr *phdr) +{ + const struct firmware *split_fw; + char split_fname[32]; + int ret = 0; + struct cpe_svc_mem_segment *segment; + + if (!core || !phdr) { + pr_err("%s: Invalid params\n", __func__); + return -EINVAL; + } + + /* file size can be 0 for bss segments */ + if (phdr->p_filesz == 0 || phdr->p_memsz == 0) + return 0; + + segment = kzalloc(sizeof(struct cpe_svc_mem_segment), GFP_KERNEL); + if (!segment) + return -ENOMEM; + + snprintf(split_fname, sizeof(split_fname), "%s.b%02d", + core->fname, file_idx); + + ret = request_firmware(&split_fw, split_fname, core->dev); + if (ret) { + dev_err(core->dev, "firmware %s not found\n", + split_fname); + ret = -EIO; + goto fw_req_fail; + } + + if (phdr->p_flags & ELF_FLAG_EXECUTE) + segment->type = CPE_SVC_INSTRUCTION_MEM; + else if (phdr->p_flags & ELF_FLAG_RW) + segment->type = CPE_SVC_DATA_MEM; + else { + dev_err(core->dev, "%s invalid flags 0x%x\n", + __func__, phdr->p_flags); + goto done; + } + + if (phdr->p_filesz != split_fw->size) { + dev_err(core->dev, + "%s: %s size mismatch, phdr_size: 0x%x fw_size: 0x%zx", + __func__, split_fname, phdr->p_filesz, split_fw->size); + ret = -EINVAL; + goto done; + } + + segment->cpe_addr = phdr->p_paddr; + segment->size = phdr->p_filesz; + segment->data = (u8 *) split_fw->data; + + dev_dbg(core->dev, + "%s: cpe segment type %s read from firmware\n", __func__, + (segment->type == CPE_SVC_INSTRUCTION_MEM) ? + "INSTRUCTION" : "DATA"); + + ret = cpe_svc_download_segment(core->cpe_handle, segment); + if (ret) { + dev_err(core->dev, + "%s: Failed to download %s, error = %d\n", + __func__, split_fname, ret); + goto done; + } + +done: + release_firmware(split_fw); + +fw_req_fail: + kfree(segment); + return ret; +} + +/* + * wcd_cpe_enable_cpe_clks: enable the clocks for CPE + * @core: handle to wcd_cpe_core + * @enable: flag indicating whether to enable/disable cpe clocks + */ +static int wcd_cpe_enable_cpe_clks(struct wcd_cpe_core *core, bool enable) +{ + int ret, ret1; + + if (!core || !core->cpe_cdc_cb || + !core->cpe_cdc_cb->cpe_clk_en) { + pr_err("%s: invalid handle\n", + __func__); + return -EINVAL; + } + + ret = core->cpe_cdc_cb->cdc_clk_en(core->codec, enable); + if (ret) { + dev_err(core->dev, "%s: Failed to enable RCO\n", + __func__); + return ret; + } + + if (!enable && core->cpe_clk_ref > 0) + core->cpe_clk_ref--; + + /* + * CPE clk will be enabled at the first time + * and be disabled at the last time. + */ + if (core->cpe_clk_ref == 0) { + ret = core->cpe_cdc_cb->cpe_clk_en(core->codec, enable); + if (ret) { + dev_err(core->dev, + "%s: cpe_clk_en() failed, err = %d\n", + __func__, ret); + goto cpe_clk_fail; + } + } + + if (enable) + core->cpe_clk_ref++; + + return 0; + +cpe_clk_fail: + /* Release the codec clk if CPE clk enable failed */ + if (enable) { + ret1 = core->cpe_cdc_cb->cdc_clk_en(core->codec, !enable); + if (ret1) + dev_err(core->dev, + "%s: Fail to release codec clk, err = %d\n", + __func__, ret1); + } + + return ret; +} + +/* + * wcd_cpe_bus_vote_max_bw: Function to vote for max bandwidth on codec bus + * @core: handle to core for cpe + * @vote: flag to indicate enable/disable of vote + * + * This function will try to use the codec provided callback to + * vote/unvote for the max bandwidth of the bus that is used by + * the codec for register reads/writes. + */ +static int wcd_cpe_bus_vote_max_bw(struct wcd_cpe_core *core, + bool vote) +{ + if (!core || !core->cpe_cdc_cb) { + pr_err("%s: Invalid handle to %s\n", + __func__, + (!core) ? "core" : "codec callbacks"); + return -EINVAL; + } + + if (core->cpe_cdc_cb->bus_vote_bw) { + dev_dbg(core->dev, "%s: %s cdc bus max bandwidth\n", + __func__, vote ? "Vote" : "Unvote"); + core->cpe_cdc_cb->bus_vote_bw(core->codec, vote); + } + + return 0; +} + +/* + * wcd_cpe_load_fw: Function to load the fw image + * @core: cpe core pointer + * @load_type: indicates whether to load to data section + * or the instruction section + * + * Parse the mdt file to look for program headers, load each + * split file corresponding to the program headers. + */ +static int wcd_cpe_load_fw(struct wcd_cpe_core *core, + unsigned int load_type) +{ + + int ret, phdr_idx; + struct snd_soc_codec *codec = NULL; + struct wcd9xxx *wcd9xxx = NULL; + const struct elf32_hdr *ehdr; + const struct elf32_phdr *phdr; + const struct firmware *fw; + const u8 *elf_ptr; + char mdt_name[64]; + bool img_dload_fail = false; + bool load_segment; + + if (!core || !core->cpe_handle) { + pr_err("%s: Error CPE core %pK\n", __func__, + core); + return -EINVAL; + } + codec = core->codec; + wcd9xxx = dev_get_drvdata(codec->dev->parent); + snprintf(mdt_name, sizeof(mdt_name), "%s.mdt", core->fname); + ret = request_firmware(&fw, mdt_name, core->dev); + if (ret < 0) { + dev_err(core->dev, "firmware %s not found\n", mdt_name); + return ret; + } + + ehdr = (struct elf32_hdr *) fw->data; + if (!wcd_cpe_is_valid_elf_hdr(core, fw->size, ehdr)) { + dev_err(core->dev, "%s: fw mdt %s is invalid\n", + __func__, mdt_name); + ret = -EINVAL; + goto done; + } + + elf_ptr = fw->data + sizeof(*ehdr); + + if (load_type == ELF_FLAG_EXECUTE) { + /* Reset CPE first */ + ret = cpe_svc_reset(core->cpe_handle); + if (ret < 0) { + dev_err(core->dev, + "%s: Failed to reset CPE with error %d\n", + __func__, ret); + goto done; + } + } + + dev_dbg(core->dev, "%s: start image dload, name = %s, load_type = 0x%x\n", + __func__, core->fname, load_type); + + wcd_cpe_bus_vote_max_bw(core, true); + + /* parse every program header and request corresponding firmware */ + for (phdr_idx = 0; phdr_idx < ehdr->e_phnum; phdr_idx++) { + phdr = (struct elf32_phdr *)elf_ptr; + load_segment = false; + + dev_dbg(core->dev, + "index = %d, vaddr = 0x%x, paddr = 0x%x, filesz = 0x%x, memsz = 0x%x, flags = 0x%x\n" + , phdr_idx, phdr->p_vaddr, phdr->p_paddr, + phdr->p_filesz, phdr->p_memsz, phdr->p_flags); + + switch (load_type) { + case ELF_FLAG_EXECUTE: + if (phdr->p_flags & load_type) + load_segment = true; + break; + case ELF_FLAG_RW: + if (!(phdr->p_flags & ELF_FLAG_EXECUTE) && + (phdr->p_flags & load_type)) + load_segment = true; + break; + default: + pr_err("%s: Invalid load_type 0x%x\n", + __func__, load_type); + ret = -EINVAL; + goto rel_bus_vote; + } + + if (load_segment) { + ret = wcd_cpe_load_each_segment(core, + phdr_idx, phdr); + if (ret < 0) { + dev_err(core->dev, + "Failed to load segment %d, aborting img dload\n", + phdr_idx); + img_dload_fail = true; + goto rel_bus_vote; + } + } else { + dev_dbg(core->dev, + "%s: skipped segment with index %d\n", + __func__, phdr_idx); + } + + elf_ptr = elf_ptr + sizeof(*phdr); + } + if (load_type == ELF_FLAG_EXECUTE) + core->ssr_type = WCD_CPE_IMEM_DOWNLOADED; + +rel_bus_vote: + wcd_cpe_bus_vote_max_bw(core, false); + +done: + release_firmware(fw); + return ret; +} + +/* + * wcd_cpe_change_online_state - mark cpe online/offline state + * @core: core session to mark + * @online: whether online of offline + * + */ +static void wcd_cpe_change_online_state(struct wcd_cpe_core *core, + int online) +{ + struct wcd_cpe_ssr_entry *ssr_entry = NULL; + unsigned long ret; + + if (!core) { + pr_err("%s: Invalid core handle\n", + __func__); + return; + } + + ssr_entry = &core->ssr_entry; + WCD_CPE_GRAB_LOCK(&core->ssr_lock, "SSR"); + ssr_entry->offline = !online; + + /* Make sure write to offline state is completed. */ + wmb(); + ret = xchg(&ssr_entry->offline_change, 1); + wake_up_interruptible(&ssr_entry->offline_poll_wait); + WCD_CPE_REL_LOCK(&core->ssr_lock, "SSR"); + pr_debug("%s: change state 0x%x offline_change 0x%x\n" + " core->offline 0x%x, ret = %ld\n", + __func__, online, + ssr_entry->offline_change, + core->ssr_entry.offline, ret); +} + +/* + * wcd_cpe_load_fw_image: work function to load the fw image + * @work: work that is scheduled to perform the image loading + * + * Parse the mdt file to look for program headers, load each + * split file corresponding to the program headers. + */ +static void wcd_cpe_load_fw_image(struct work_struct *work) +{ + struct wcd_cpe_core *core; + int ret = 0; + + core = container_of(work, struct wcd_cpe_core, load_fw_work); + ret = wcd_cpe_load_fw(core, ELF_FLAG_EXECUTE); + if (!ret) + wcd_cpe_change_online_state(core, 1); + else + pr_err("%s: failed to load instruction section, err = %d\n", + __func__, ret); +} + +/* + * wcd_cpe_get_core_handle: get the handle to wcd_cpe_core + * @codec: codec from which this handle is to be obtained + * Codec driver should provide a callback function to obtain + * handle to wcd_cpe_core during initialization of wcd_cpe_core + */ +void *wcd_cpe_get_core_handle( + struct snd_soc_codec *codec) +{ + struct wcd_cpe_core *core = NULL; + + if (!codec) { + pr_err("%s: Invalid codec handle\n", + __func__); + goto done; + } + + if (!wcd_get_cpe_core) { + dev_err(codec->dev, + "%s: codec callback not available\n", + __func__); + goto done; + } + + core = wcd_get_cpe_core(codec); + + if (!core) + dev_err(codec->dev, + "%s: handle to core not available\n", + __func__); +done: + return core; +} +EXPORT_SYMBOL(wcd_cpe_get_core_handle); + +/* + * svass_engine_irq: threaded interrupt handler for svass engine irq + * @irq: interrupt number + * @data: data pointer passed during irq registration + */ +static irqreturn_t svass_engine_irq(int irq, void *data) +{ + struct wcd_cpe_core *core = data; + int ret = 0; + + if (!core) { + pr_err("%s: Invalid data for interrupt handler\n", + __func__); + goto done; + } + + ret = cpe_svc_process_irq(core->cpe_handle, CPE_IRQ_OUTBOX_IRQ); + if (ret < 0) + dev_err(core->dev, + "%s: Error processing irq from cpe_Services\n", + __func__); +done: + return IRQ_HANDLED; +} + +/* + * wcd_cpe_state_read - update read status in procfs + * @entry: snd_info_entry + * @buf: buffer where the read status is updated. + * + */ +static ssize_t wcd_cpe_state_read(struct snd_info_entry *entry, + void *file_private_data, struct file *file, + char __user *buf, size_t count, loff_t pos) +{ + int len = 0; + char buffer[WCD_CPE_STATE_MAX_LEN]; + struct wcd_cpe_core *core = NULL; + struct wcd_cpe_ssr_entry *ssr_entry = NULL; + + core = (struct wcd_cpe_core *) entry->private_data; + if (!core) { + pr_err("%s: CPE core NULL\n", __func__); + return -EINVAL; + } + ssr_entry = &core->ssr_entry; + + /* Make sure read from ssr_entry is completed. */ + rmb(); + dev_dbg(core->dev, + "%s: Offline 0x%x\n", __func__, + ssr_entry->offline); + + WCD_CPE_GRAB_LOCK(&core->ssr_lock, "SSR"); + len = snprintf(buffer, sizeof(buffer), "%s\n", + ssr_entry->offline ? "OFFLINE" : "ONLINE"); + WCD_CPE_REL_LOCK(&core->ssr_lock, "SSR"); + + return simple_read_from_buffer(buf, count, &pos, buffer, len); +} + +/* + * wcd_cpe_state_poll - polls for change state + * @entry: snd_info_entry + * @wait: wait for duration for poll wait + * + */ +static unsigned int wcd_cpe_state_poll(struct snd_info_entry *entry, + void *private_data, struct file *file, + poll_table *wait) +{ + struct wcd_cpe_core *core = NULL; + struct wcd_cpe_ssr_entry *ssr_entry = NULL; + int ret = 0; + + core = (struct wcd_cpe_core *) entry->private_data; + if (!core) { + pr_err("%s: CPE core NULL\n", __func__); + return -EINVAL; + } + + ssr_entry = &core->ssr_entry; + + dev_dbg(core->dev, "%s: CPE Poll wait\n", + __func__); + poll_wait(file, &ssr_entry->offline_poll_wait, wait); + dev_dbg(core->dev, "%s: Wake-up Poll wait\n", + __func__); + WCD_CPE_GRAB_LOCK(&core->ssr_lock, "SSR"); + + if (xchg(&ssr_entry->offline_change, 0)) + ret = POLLIN | POLLPRI | POLLRDNORM; + + WCD_CPE_REL_LOCK(&core->ssr_lock, "SSR"); + + dev_dbg(core->dev, "%s: ret (%d) from poll_wait\n", + __func__, ret); + return ret; +} + +/* + * wcd_cpe_is_online_state - return true if card is online state + * @core: core offline to query + */ +static bool wcd_cpe_is_online_state(void *core_handle) +{ + struct wcd_cpe_core *core = core_handle; + + if (core_handle) { + return !core->ssr_entry.offline; + } else { + pr_err("%s: Core handle NULL\n", __func__); + /* still return 1- offline if core ptr null */ + return false; + } +} + +static struct snd_info_entry_ops wcd_cpe_state_proc_ops = { + .read = wcd_cpe_state_read, + .poll = wcd_cpe_state_poll, +}; + +static int wcd_cpe_check_new_image(struct wcd_cpe_core *core) +{ + int rc = 0; + char temp_img_name[WCD_CPE_IMAGE_FNAME_MAX]; + + if (!strcmp(core->fname, core->dyn_fname) && + core->ssr_type != WCD_CPE_INITIALIZED) { + dev_dbg(core->dev, + "%s: Firmware unchanged, fname = %s, ssr_type 0x%x\n", + __func__, core->fname, core->ssr_type); + goto done; + } + + /* + * Different firmware name requested, + * Re-load the instruction section + */ + strlcpy(temp_img_name, core->fname, + WCD_CPE_IMAGE_FNAME_MAX); + strlcpy(core->fname, core->dyn_fname, + WCD_CPE_IMAGE_FNAME_MAX); + + rc = wcd_cpe_load_fw(core, ELF_FLAG_EXECUTE); + if (rc) { + dev_err(core->dev, + "%s: Failed to dload new image %s, err = %d\n", + __func__, core->fname, rc); + /* If new image download failed, revert back to old image */ + strlcpy(core->fname, temp_img_name, + WCD_CPE_IMAGE_FNAME_MAX); + rc = wcd_cpe_load_fw(core, ELF_FLAG_EXECUTE); + if (rc) + dev_err(core->dev, + "%s: Failed to re-dload image %s, err = %d\n", + __func__, core->fname, rc); + } else { + dev_info(core->dev, "%s: fw changed to %s\n", + __func__, core->fname); + } +done: + return rc; +} + +static int wcd_cpe_enable(struct wcd_cpe_core *core, + bool enable) +{ + int ret = 0; + + if (enable) { + /* Reset CPE first */ + ret = cpe_svc_reset(core->cpe_handle); + if (ret < 0) { + dev_err(core->dev, + "%s: CPE Reset failed, error = %d\n", + __func__, ret); + goto done; + } + + ret = wcd_cpe_setup_irqs(core); + if (ret) { + dev_err(core->dev, + "%s: CPE IRQs setup failed, error = %d\n", + __func__, ret); + goto done; + } + ret = wcd_cpe_check_new_image(core); + if (ret) + goto fail_boot; + + /* Dload data section */ + ret = wcd_cpe_load_fw(core, ELF_FLAG_RW); + if (ret) { + dev_err(core->dev, + "%s: Failed to dload data section, err = %d\n", + __func__, ret); + goto fail_boot; + } + + ret = wcd_cpe_enable_cpe_clks(core, true); + if (ret < 0) { + dev_err(core->dev, + "%s: CPE clk enable failed, err = %d\n", + __func__, ret); + goto fail_boot; + } + + ret = cpe_svc_boot(core->cpe_handle, + core->cpe_debug_mode); + if (ret < 0) { + dev_err(core->dev, + "%s: Failed to boot CPE\n", + __func__); + goto fail_boot; + } + + /* wait for CPE to be online */ + dev_dbg(core->dev, + "%s: waiting for CPE bootup\n", + __func__); + + wait_for_completion(&core->online_compl); + + dev_dbg(core->dev, + "%s: CPE bootup done\n", + __func__); + + core->ssr_type = WCD_CPE_ENABLED; + } else { + if (core->ssr_type == WCD_CPE_BUS_DOWN_EVENT || + core->ssr_type == WCD_CPE_SSR_EVENT) { + /* + * If this disable vote is when + * SSR is in progress, do not disable CPE here, + * instead SSR handler will control CPE. + */ + wcd_cpe_enable_cpe_clks(core, false); + wcd_cpe_cleanup_irqs(core); + goto done; + } + + ret = cpe_svc_shutdown(core->cpe_handle); + if (ret < 0) { + dev_err(core->dev, + "%s: CPE shutdown failed, error %d\n", + __func__, ret); + goto done; + } + + wcd_cpe_enable_cpe_clks(core, false); + wcd_cpe_cleanup_irqs(core); + core->ssr_type = WCD_CPE_IMEM_DOWNLOADED; + } + + return ret; + +fail_boot: + wcd_cpe_cleanup_irqs(core); + +done: + return ret; +} + +/* + * wcd_cpe_boot_ssr: Load the images to CPE after ssr and bootup cpe + * @core: handle to the core + */ +static int wcd_cpe_boot_ssr(struct wcd_cpe_core *core) +{ + int rc = 0; + + if (!core || !core->cpe_handle) { + pr_err("%s: Invalid handle\n", __func__); + rc = -EINVAL; + goto fail; + } + /* Load the instruction section and mark CPE as online */ + rc = wcd_cpe_load_fw(core, ELF_FLAG_EXECUTE); + if (rc) { + dev_err(core->dev, + "%s: Failed to load instruction, err = %d\n", + __func__, rc); + goto fail; + } else { + wcd_cpe_change_online_state(core, 1); + } + +fail: + return rc; +} + +/* + * wcd_cpe_clr_ready_status: + * Clear the value from the ready status for CPE + * @core: handle to the core + * @value: flag/bitmask that is to be cleared + * + * This function should not be invoked with ssr_lock acquired + */ +static void wcd_cpe_clr_ready_status(struct wcd_cpe_core *core, + u8 value) +{ + WCD_CPE_GRAB_LOCK(&core->ssr_lock, "SSR"); + core->ready_status &= ~(value); + dev_dbg(core->dev, + "%s: ready_status = 0x%x\n", + __func__, core->ready_status); + WCD_CPE_REL_LOCK(&core->ssr_lock, "SSR"); +} + +/* + * wcd_cpe_set_and_complete: + * Set the ready status with the provided value and + * flag the completion object if ready status moves + * to ready to download + * @core: handle to the core + * @value: flag/bitmask that is to be set + */ +static void wcd_cpe_set_and_complete(struct wcd_cpe_core *core, + u8 value) +{ + WCD_CPE_GRAB_LOCK(&core->ssr_lock, "SSR"); + core->ready_status |= value; + if ((core->ready_status & WCD_CPE_READY_TO_DLOAD) == + WCD_CPE_READY_TO_DLOAD) { + dev_dbg(core->dev, + "%s: marking ready, status = 0x%x\n", + __func__, core->ready_status); + complete(&core->ready_compl); + } + WCD_CPE_REL_LOCK(&core->ssr_lock, "SSR"); +} + + +/* + * wcd_cpe_ssr_work: work function to handle CPE SSR + * @work: work that is scheduled to perform CPE shutdown + * and restart + */ +static void wcd_cpe_ssr_work(struct work_struct *work) +{ + + int rc = 0; + u32 irq = 0; + struct wcd_cpe_core *core = NULL; + u8 status = 0; + + core = container_of(work, struct wcd_cpe_core, ssr_work); + if (!core) { + pr_err("%s: Core handle NULL\n", __func__); + return; + } + + /* Obtain pm request up in case of suspend mode */ + pm_qos_add_request(&core->pm_qos_req, + PM_QOS_CPU_DMA_LATENCY, + PM_QOS_DEFAULT_VALUE); + pm_qos_update_request(&core->pm_qos_req, + msm_cpuidle_get_deep_idle_latency()); + + dev_dbg(core->dev, + "%s: CPE SSR with event %d\n", + __func__, core->ssr_type); + + if (core->ssr_type == WCD_CPE_SSR_EVENT) { + if (CPE_ERR_IRQ_CB(core)) + core->cpe_cdc_cb->cpe_err_irq_control( + core->codec, + CPE_ERR_IRQ_STATUS, + &status); + if (status & core->irq_info.cpe_fatal_irqs) + irq = CPE_IRQ_WDOG_BITE; + } else { + /* If bus is down, cdc reg cannot be read */ + irq = CPE_IRQ_WDOG_BITE; + } + + if (core->cpe_users > 0) { + rc = cpe_svc_process_irq(core->cpe_handle, irq); + if (rc < 0) + /* + * Even if process_irq fails, + * wait for cpe to move to offline state + */ + dev_err(core->dev, + "%s: irq processing failed, error = %d\n", + __func__, rc); + + rc = wait_for_completion_timeout(&core->offline_compl, + CPE_OFFLINE_WAIT_TIMEOUT); + if (!rc) { + dev_err(core->dev, + "%s: wait for cpe offline timed out\n", + __func__); + goto err_ret; + } + if (core->ssr_type != WCD_CPE_BUS_DOWN_EVENT) { + wcd_cpe_get_sfr_dump(core); + + /* + * Ramdump has to be explicitly enabled + * through debugfs and cannot be collected + * when bus is down. + */ + if (ramdump_enable) + wcd_cpe_collect_ramdump(core); + } + } else { + pr_err("%s: no cpe users, mark as offline\n", __func__); + wcd_cpe_change_online_state(core, 0); + wcd_cpe_set_and_complete(core, + WCD_CPE_BLK_READY); + } + + rc = wait_for_completion_timeout(&core->ready_compl, + CPE_READY_WAIT_TIMEOUT); + if (!rc) { + dev_err(core->dev, + "%s: ready to online timed out, status = %u\n", + __func__, core->ready_status); + goto err_ret; + } + + rc = wcd_cpe_boot_ssr(core); + + /* Once image are downloaded make sure all + * error interrupts are cleared + */ + if (CPE_ERR_IRQ_CB(core)) + core->cpe_cdc_cb->cpe_err_irq_control(core->codec, + CPE_ERR_IRQ_CLEAR, NULL); + +err_ret: + /* remove after default pm qos */ + pm_qos_update_request(&core->pm_qos_req, + PM_QOS_DEFAULT_VALUE); + pm_qos_remove_request(&core->pm_qos_req); +} + +/* + * wcd_cpe_ssr_handle: handle SSR events here. + * @core_handle: handle to the cpe core + * @event: indicates ADSP or CDSP SSR. + */ +int wcd_cpe_ssr_event(void *core_handle, + enum wcd_cpe_ssr_state_event event) +{ + struct wcd_cpe_core *core = core_handle; + + if (!core) { + pr_err("%s: Invalid handle to core\n", + __func__); + return -EINVAL; + } + + /* + * If CPE is not even enabled, the SSR event for + * CPE needs to be ignored + */ + if (core->ssr_type == WCD_CPE_INITIALIZED) { + dev_info(core->dev, + "%s: CPE initialized but not enabled, skip CPE ssr\n", + __func__); + return 0; + } + + dev_dbg(core->dev, + "%s: Schedule ssr work, event = %d\n", + __func__, core->ssr_type); + + switch (event) { + case WCD_CPE_BUS_DOWN_EVENT: + /* + * If bus down, then CPE block is also + * treated to be down + */ + wcd_cpe_clr_ready_status(core, WCD_CPE_READY_TO_DLOAD); + core->ssr_type = event; + schedule_work(&core->ssr_work); + break; + + case WCD_CPE_SSR_EVENT: + wcd_cpe_clr_ready_status(core, WCD_CPE_BLK_READY); + core->ssr_type = event; + schedule_work(&core->ssr_work); + break; + + case WCD_CPE_BUS_UP_EVENT: + wcd_cpe_set_and_complete(core, WCD_CPE_BUS_READY); + /* + * In case of bus up event ssr_type will be changed + * to WCD_CPE_ACTIVE once CPE is online + */ + break; + + default: + dev_err(core->dev, + "%s: unhandled SSR event %d\n", + __func__, event); + return -EINVAL; + } + + return 0; +} +EXPORT_SYMBOL(wcd_cpe_ssr_event); + +/* + * svass_exception_irq: threaded irq handler for sva error interrupts + * @irq: interrupt number + * @data: data pointer passed during irq registration + * + * Once a error interrupt is received, it is not cleared, since + * clearing this interrupt will raise spurious interrupts unless + * CPE is reset. + */ +static irqreturn_t svass_exception_irq(int irq, void *data) +{ + struct wcd_cpe_core *core = data; + u8 status = 0; + + if (!core || !CPE_ERR_IRQ_CB(core)) { + pr_err("%s: Invalid %s\n", + __func__, + (!core) ? "core" : "cdc control"); + return IRQ_HANDLED; + } + + core->cpe_cdc_cb->cpe_err_irq_control(core->codec, + CPE_ERR_IRQ_STATUS, &status); + + while (status != 0) { + if (status & core->irq_info.cpe_fatal_irqs) { + dev_err(core->dev, + "%s: CPE SSR event,err_status = 0x%02x\n", + __func__, status); + wcd_cpe_ssr_event(core, WCD_CPE_SSR_EVENT); + /* + * If fatal interrupt is received, + * trigger SSR and stop processing + * further interrupts + */ + break; + } + /* + * Mask the interrupt that was raised to + * avoid spurious interrupts + */ + core->cpe_cdc_cb->cpe_err_irq_control(core->codec, + CPE_ERR_IRQ_MASK, &status); + + /* Clear only the interrupt that was raised */ + core->cpe_cdc_cb->cpe_err_irq_control(core->codec, + CPE_ERR_IRQ_CLEAR, &status); + dev_err(core->dev, + "%s: err_interrupt status = 0x%x\n", + __func__, status); + + /* Read status for pending interrupts */ + core->cpe_cdc_cb->cpe_err_irq_control(core->codec, + CPE_ERR_IRQ_STATUS, &status); + } + + return IRQ_HANDLED; +} + +/* + * wcd_cpe_cmi_afe_cb: callback called on response to afe commands + * @param: parameter containing the response code, etc + * + * Process the request to the command sent to CPE and wakeup the + * command send wait. + */ +static void wcd_cpe_cmi_afe_cb(const struct cmi_api_notification *param) +{ + struct cmi_hdr *hdr; + struct wcd_cmi_afe_port_data *afe_port_d; + u8 port_id; + + if (!param) { + pr_err("%s: param is null\n", __func__); + return; + } + + if (param->event != CMI_API_MSG) { + pr_err("%s: unhandled event 0x%x\n", + __func__, param->event); + return; + } + + pr_debug("%s: param->result = %d\n", + __func__, param->result); + + hdr = (struct cmi_hdr *) param->message; + + /* + * for AFE cmd response, port id is + * stored at session id field of header + */ + port_id = CMI_HDR_GET_SESSION_ID(hdr); + if (port_id > WCD_CPE_AFE_MAX_PORTS) { + pr_err("%s: invalid port_id %d\n", + __func__, port_id); + return; + } + + afe_port_d = &(afe_ports[port_id]); + + if (hdr->opcode == CPE_CMI_BASIC_RSP_OPCODE) { + + u8 *payload = ((u8 *)param->message) + (sizeof(struct cmi_hdr)); + u8 result = payload[0]; + + afe_port_d->cmd_result = result; + complete(&afe_port_d->afe_cmd_complete); + + } else if (hdr->opcode == CPE_AFE_PORT_CMDRSP_SHARED_MEM_ALLOC) { + + struct cpe_cmdrsp_shmem_alloc *cmdrsp_shmem_alloc = + (struct cpe_cmdrsp_shmem_alloc *) param->message; + + if (cmdrsp_shmem_alloc->addr == 0) { + pr_err("%s: Failed AFE shared mem alloc\n", __func__); + afe_port_d->cmd_result = CMI_SHMEM_ALLOC_FAILED; + } else { + pr_debug("%s AFE shared mem addr = 0x%x\n", + __func__, cmdrsp_shmem_alloc->addr); + afe_port_d->mem_handle = cmdrsp_shmem_alloc->addr; + afe_port_d->cmd_result = 0; + } + complete(&afe_port_d->afe_cmd_complete); + } +} + +/* + * wcd_cpe_initialize_afe_port_data: Initialize all AFE ports + * + * Initialize the data for all the afe ports. Assign the + * afe port state to INIT state. + */ +static void wcd_cpe_initialize_afe_port_data(void) +{ + struct wcd_cmi_afe_port_data *afe_port_d; + int i; + + for (i = 0; i <= WCD_CPE_AFE_MAX_PORTS; i++) { + afe_port_d = &afe_ports[i]; + afe_port_d->port_id = i; + init_completion(&afe_port_d->afe_cmd_complete); + afe_port_d->port_state = AFE_PORT_STATE_INIT; + mutex_init(&afe_port_d->afe_lock); + } +} + +/* + * wcd_cpe_deinitialize_afe_port_data: De-initialize all AFE ports + * + * De-Initialize the data for all the afe ports. Assign the + * afe port state to DEINIT state. + */ +static void wcd_cpe_deinitialize_afe_port_data(void) +{ + struct wcd_cmi_afe_port_data *afe_port_d; + int i; + + for (i = 0; i <= WCD_CPE_AFE_MAX_PORTS; i++) { + afe_port_d = &afe_ports[i]; + afe_port_d->port_state = AFE_PORT_STATE_DEINIT; + mutex_destroy(&afe_port_d->afe_lock); + } +} + +/* + * wcd_cpe_svc_event_cb: callback from cpe services, indicating + * CPE is online or offline. + * @param: parameter / payload for event to be notified + */ +static void wcd_cpe_svc_event_cb(const struct cpe_svc_notification *param) +{ + struct snd_soc_codec *codec; + struct wcd_cpe_core *core; + struct cpe_svc_boot_event *boot_data; + bool active_sessions; + + if (!param) { + pr_err("%s: Invalid event\n", __func__); + return; + } + + codec = param->private_data; + if (!codec) { + pr_err("%s: Invalid handle to codec\n", + __func__); + return; + } + + core = wcd_cpe_get_core_handle(codec); + if (!core) { + pr_err("%s: Invalid handle to core\n", + __func__); + return; + } + + dev_dbg(core->dev, + "%s: event = 0x%x, ssr_type = 0x%x\n", + __func__, param->event, core->ssr_type); + + switch (param->event) { + case CPE_SVC_BOOT: + boot_data = (struct cpe_svc_boot_event *) + param->payload; + core->sfr_buf_addr = boot_data->debug_address; + core->sfr_buf_size = boot_data->debug_buffer_size; + dev_dbg(core->dev, + "%s: CPE booted, sfr_addr = %d, sfr_size = %zu\n", + __func__, core->sfr_buf_addr, + core->sfr_buf_size); + break; + case CPE_SVC_ONLINE: + core->ssr_type = WCD_CPE_ACTIVE; + dev_dbg(core->dev, "%s CPE is now online\n", + __func__); + complete(&core->online_compl); + break; + case CPE_SVC_OFFLINE: + /* + * offline can happen during normal shutdown, + * but we are interested in offline only during + * SSR. + */ + if (core->ssr_type != WCD_CPE_SSR_EVENT && + core->ssr_type != WCD_CPE_BUS_DOWN_EVENT) + break; + + active_sessions = wcd_cpe_lsm_session_active(); + wcd_cpe_change_online_state(core, 0); + complete(&core->offline_compl); + dev_err(core->dev, "%s: CPE is now offline\n", + __func__); + break; + case CPE_SVC_CMI_CLIENTS_DEREG: + + /* + * Only when either CPE SSR is in progress, + * or the bus is down, we need to mark the CPE + * as ready. In all other cases, this event is + * ignored + */ + if (core->ssr_type == WCD_CPE_SSR_EVENT || + core->ssr_type == WCD_CPE_BUS_DOWN_EVENT) + wcd_cpe_set_and_complete(core, + WCD_CPE_BLK_READY); + break; + default: + dev_err(core->dev, + "%s: unhandled notification\n", + __func__); + break; + } +} + +/* + * wcd_cpe_cleanup_irqs: free the irq resources required by cpe + * @core: handle the cpe core + * + * This API will free the IRQs for CPE but does not mask the + * CPE interrupts. If masking is needed, it has to be done + * explicity by caller. + */ +static void wcd_cpe_cleanup_irqs(struct wcd_cpe_core *core) +{ + + struct snd_soc_codec *codec = core->codec; + struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent); + struct wcd9xxx_core_resource *core_res = &wcd9xxx->core_res; + + wcd9xxx_free_irq(core_res, + core->irq_info.cpe_engine_irq, + core); + wcd9xxx_free_irq(core_res, + core->irq_info.cpe_err_irq, + core); + +} + +/* + * wcd_cpe_setup_sva_err_intr: setup the irqs for CPE + * @core: handle to wcd_cpe_core + * All interrupts needed for CPE are acquired. If any + * request_irq fails, then all irqs are free'd + */ +static int wcd_cpe_setup_irqs(struct wcd_cpe_core *core) +{ + int ret; + struct snd_soc_codec *codec = core->codec; + struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent); + struct wcd9xxx_core_resource *core_res = &wcd9xxx->core_res; + + ret = wcd9xxx_request_irq(core_res, + core->irq_info.cpe_engine_irq, + svass_engine_irq, "SVASS_Engine", core); + if (ret) { + dev_err(core->dev, + "%s: Failed to request svass engine irq\n", + __func__); + goto fail_engine_irq; + } + + /* Make sure all error interrupts are cleared */ + if (CPE_ERR_IRQ_CB(core)) + core->cpe_cdc_cb->cpe_err_irq_control( + core->codec, + CPE_ERR_IRQ_CLEAR, + NULL); + + /* Enable required error interrupts */ + if (CPE_ERR_IRQ_CB(core)) + core->cpe_cdc_cb->cpe_err_irq_control( + core->codec, + CPE_ERR_IRQ_UNMASK, + NULL); + + ret = wcd9xxx_request_irq(core_res, + core->irq_info.cpe_err_irq, + svass_exception_irq, "SVASS_Exception", core); + if (ret) { + dev_err(core->dev, + "%s: Failed to request svass err irq\n", + __func__); + goto fail_exception_irq; + } + + return 0; + +fail_exception_irq: + wcd9xxx_free_irq(core_res, + core->irq_info.cpe_engine_irq, core); + +fail_engine_irq: + return ret; +} + +static int wcd_cpe_get_cal_index(int32_t cal_type) +{ + int cal_index = -EINVAL; + + if (cal_type == ULP_AFE_CAL_TYPE) + cal_index = WCD_CPE_LSM_CAL_AFE; + else if (cal_type == ULP_LSM_CAL_TYPE) + cal_index = WCD_CPE_LSM_CAL_LSM; + else if (cal_type == ULP_LSM_TOPOLOGY_ID_CAL_TYPE) + cal_index = WCD_CPE_LSM_CAL_TOPOLOGY_ID; + else + pr_err("%s: invalid cal_type %d\n", + __func__, cal_type); + + return cal_index; +} + +static int wcd_cpe_alloc_cal(int32_t cal_type, size_t data_size, void *data) +{ + int ret = 0; + int cal_index; + + cal_index = wcd_cpe_get_cal_index(cal_type); + if (cal_index < 0) { + pr_err("%s: invalid caltype %d\n", + __func__, cal_type); + return -EINVAL; + } + + ret = cal_utils_alloc_cal(data_size, data, + core_d->cal_data[cal_index], + 0, NULL); + if (ret < 0) + pr_err("%s: cal_utils_alloc_block failed, ret = %d, cal type = %d!\n", + __func__, ret, cal_type); + return ret; +} + +static int wcd_cpe_dealloc_cal(int32_t cal_type, size_t data_size, + void *data) +{ + int ret = 0; + int cal_index; + + cal_index = wcd_cpe_get_cal_index(cal_type); + if (cal_index < 0) { + pr_err("%s: invalid caltype %d\n", + __func__, cal_type); + return -EINVAL; + } + + ret = cal_utils_dealloc_cal(data_size, data, + core_d->cal_data[cal_index]); + if (ret < 0) + pr_err("%s: cal_utils_dealloc_block failed, ret = %d, cal type = %d!\n", + __func__, ret, cal_type); + return ret; +} + +static int wcd_cpe_set_cal(int32_t cal_type, size_t data_size, void *data) +{ + int ret = 0; + int cal_index; + + cal_index = wcd_cpe_get_cal_index(cal_type); + if (cal_index < 0) { + pr_err("%s: invalid caltype %d\n", + __func__, cal_type); + return -EINVAL; + } + + ret = cal_utils_set_cal(data_size, data, + core_d->cal_data[cal_index], + 0, NULL); + if (ret < 0) + pr_err("%s: cal_utils_set_cal failed, ret = %d, cal type = %d!\n", + __func__, ret, cal_type); + return ret; +} + +static int wcd_cpe_cal_init(struct wcd_cpe_core *core) +{ + int ret = 0; + + struct cal_type_info cal_type_info[] = { + {{ULP_AFE_CAL_TYPE, + {wcd_cpe_alloc_cal, wcd_cpe_dealloc_cal, NULL, + wcd_cpe_set_cal, NULL, NULL} }, + {NULL, NULL, cal_utils_match_buf_num} }, + + {{ULP_LSM_CAL_TYPE, + {wcd_cpe_alloc_cal, wcd_cpe_dealloc_cal, NULL, + wcd_cpe_set_cal, NULL, NULL} }, + {NULL, NULL, cal_utils_match_buf_num} }, + + {{ULP_LSM_TOPOLOGY_ID_CAL_TYPE, + {wcd_cpe_alloc_cal, wcd_cpe_dealloc_cal, NULL, + wcd_cpe_set_cal, NULL, NULL} }, + {NULL, NULL, cal_utils_match_buf_num} }, + }; + + ret = cal_utils_create_cal_types(WCD_CPE_LSM_CAL_MAX, + core->cal_data, + cal_type_info); + if (ret < 0) + pr_err("%s: could not create cal type!\n", + __func__); + return ret; +} + +/* + * wcd_cpe_enable: setup the cpe interrupts and schedule + * the work to download image and bootup the CPE. + * core: handle to cpe core structure + */ +static int wcd_cpe_vote(struct wcd_cpe_core *core, + bool enable) +{ + int ret = 0; + + if (!core) { + pr_err("%s: Invalid handle to core\n", + __func__); + ret = -EINVAL; + goto done; + } + + dev_dbg(core->dev, + "%s: enter, enable = %s, cpe_users = %u\n", + __func__, (enable ? "true" : "false"), + core->cpe_users); + + if (enable) { + core->cpe_users++; + if (core->cpe_users == 1) { + ret = wcd_cpe_enable(core, enable); + if (ret) { + dev_err(core->dev, + "%s: CPE enable failed, err = %d\n", + __func__, ret); + goto done; + } + } else { + dev_dbg(core->dev, + "%s: cpe already enabled, users = %u\n", + __func__, core->cpe_users); + goto done; + } + } else { + core->cpe_users--; + if (core->cpe_users == 0) { + ret = wcd_cpe_enable(core, enable); + if (ret) { + dev_err(core->dev, + "%s: CPE disable failed, err = %d\n", + __func__, ret); + goto done; + } + } else { + dev_dbg(core->dev, + "%s: %u valid users on cpe\n", + __func__, core->cpe_users); + goto done; + } + } + + dev_dbg(core->dev, + "%s: leave, enable = %s, cpe_users = %u\n", + __func__, (enable ? "true" : "false"), + core->cpe_users); + +done: + return ret; +} + +static int wcd_cpe_debugfs_init(struct wcd_cpe_core *core) +{ + int rc = 0; + + struct dentry *dir = debugfs_create_dir("wcd_cpe", NULL); + + if (IS_ERR_OR_NULL(dir)) { + dir = NULL; + rc = -ENODEV; + goto err_create_dir; + } + + if (!debugfs_create_u32("ramdump_enable", 0644, + dir, &ramdump_enable)) { + dev_err(core->dev, "%s: Failed to create debugfs node %s\n", + __func__, "ramdump_enable"); + rc = -ENODEV; + goto err_create_entry; + } + + if (!debugfs_create_file("cpe_ftm_test_trigger", 0200, + dir, core, &cpe_ftm_test_trigger_fops)) { + dev_err(core->dev, "%s: Failed to create debugfs node %s\n", + __func__, "cpe_ftm_test_trigger"); + rc = -ENODEV; + goto err_create_entry; + } + + if (!debugfs_create_u32("cpe_ftm_test_status", 0444, + dir, &cpe_ftm_test_status)) { + dev_err(core->dev, "%s: Failed to create debugfs node %s\n", + __func__, "cpe_ftm_test_status"); + rc = -ENODEV; + goto err_create_entry; + } + +err_create_entry: + debugfs_remove(dir); + +err_create_dir: + return rc; +} + +static ssize_t fw_name_show(struct wcd_cpe_core *core, char *buf) +{ + return snprintf(buf, WCD_CPE_IMAGE_FNAME_MAX, "%s", + core->dyn_fname); +} + +static ssize_t fw_name_store(struct wcd_cpe_core *core, + const char *buf, ssize_t count) +{ + int copy_count = count; + const char *pos; + + pos = memchr(buf, '\n', count); + if (pos) + copy_count = pos - buf; + + if (copy_count > (WCD_CPE_IMAGE_FNAME_MAX - 1)) { + dev_err(core->dev, + "%s: Invalid length %d, max allowed %d\n", + __func__, copy_count, WCD_CPE_IMAGE_FNAME_MAX - 1); + return -EINVAL; + } + + strlcpy(core->dyn_fname, buf, copy_count + 1); + + return count; +} + +WCD_CPE_ATTR(fw_name, 0660, fw_name_show, fw_name_store); + +static ssize_t wcd_cpe_sysfs_show(struct kobject *kobj, + struct attribute *attr, char *buf) +{ + struct wcd_cpe_attribute *cpe_attr = to_wcd_cpe_attr(attr); + struct wcd_cpe_core *core = kobj_to_cpe_core(kobj); + ssize_t ret = -EINVAL; + + if (core && cpe_attr->show) + ret = cpe_attr->show(core, buf); + + return ret; +} + +static ssize_t wcd_cpe_sysfs_store(struct kobject *kobj, + struct attribute *attr, const char *buf, + size_t count) +{ + struct wcd_cpe_attribute *cpe_attr = to_wcd_cpe_attr(attr); + struct wcd_cpe_core *core = kobj_to_cpe_core(kobj); + ssize_t ret = -EINVAL; + + if (core && cpe_attr->store) + ret = cpe_attr->store(core, buf, count); + + return ret; +} + +static const struct sysfs_ops wcd_cpe_sysfs_ops = { + .show = wcd_cpe_sysfs_show, + .store = wcd_cpe_sysfs_store, +}; + +static struct kobj_type wcd_cpe_ktype = { + .sysfs_ops = &wcd_cpe_sysfs_ops, +}; + +static int wcd_cpe_sysfs_init(struct wcd_cpe_core *core, int id) +{ + char sysfs_dir_name[WCD_CPE_SYSFS_DIR_MAX_LENGTH]; + int rc = 0; + + snprintf(sysfs_dir_name, WCD_CPE_SYSFS_DIR_MAX_LENGTH, + "%s%d", "wcd_cpe", id); + + rc = kobject_init_and_add(&core->cpe_kobj, &wcd_cpe_ktype, + kernel_kobj, + sysfs_dir_name); + if (unlikely(rc)) { + dev_err(core->dev, + "%s: Failed to add kobject %s, err = %d\n", + __func__, sysfs_dir_name, rc); + goto done; + } + + rc = sysfs_create_file(&core->cpe_kobj, &cpe_attr_fw_name.attr); + if (rc) { + dev_err(core->dev, + "%s: Failed to fw_name sysfs entry to %s\n", + __func__, sysfs_dir_name); + goto fail_create_file; + } + + return 0; + +fail_create_file: + kobject_put(&core->cpe_kobj); +done: + return rc; +} + +static ssize_t cpe_ftm_test_trigger(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct wcd_cpe_core *core = file->private_data; + int ret = 0; + + /* Enable the clks for cpe */ + ret = wcd_cpe_enable_cpe_clks(core, true); + if (ret < 0) { + dev_err(core->dev, + "%s: CPE clk enable failed, err = %d\n", + __func__, ret); + goto done; + } + + /* Get the CPE_STATUS */ + ret = cpe_svc_ftm_test(core->cpe_handle, &cpe_ftm_test_status); + if (ret < 0) { + dev_err(core->dev, + "%s: CPE FTM test failed, err = %d\n", + __func__, ret); + if (ret == CPE_SVC_BUSY) { + cpe_ftm_test_status = 1; + ret = 0; + } + } + + /* Disable the clks for cpe */ + ret = wcd_cpe_enable_cpe_clks(core, false); + if (ret < 0) { + dev_err(core->dev, + "%s: CPE clk disable failed, err = %d\n", + __func__, ret); + } + +done: + if (ret < 0) + return ret; + else + return count; +} + +static int wcd_cpe_validate_params( + struct snd_soc_codec *codec, + struct wcd_cpe_params *params) +{ + + if (!codec) { + pr_err("%s: Invalid codec\n", __func__); + return -EINVAL; + } + + if (!params) { + dev_err(codec->dev, + "%s: No params supplied for codec %s\n", + __func__, codec->component.name); + return -EINVAL; + } + + if (!params->codec || !params->get_cpe_core || + !params->cdc_cb) { + dev_err(codec->dev, + "%s: Invalid params for codec %s\n", + __func__, codec->component.name); + return -EINVAL; + } + + return 0; +} + +/* + * wcd_cpe_init: Initialize CPE related structures + * @img_fname: filename for firmware image + * @codec: handle to codec requesting for image download + * @params: parameter structure passed from caller + * + * This API will initialize the cpe core but will not + * download the image or boot the cpe core. + */ +struct wcd_cpe_core *wcd_cpe_init(const char *img_fname, + struct snd_soc_codec *codec, + struct wcd_cpe_params *params) +{ + struct wcd_cpe_core *core; + int ret = 0; + struct snd_card *card = NULL; + struct snd_info_entry *entry = NULL; + char proc_name[WCD_CPE_STATE_MAX_LEN]; + const char *cpe_name = "cpe"; + const char *state_name = "_state"; + const struct cpe_svc_hw_cfg *hw_info; + int id = 0; + + if (wcd_cpe_validate_params(codec, params)) + return NULL; + + core = kzalloc(sizeof(struct wcd_cpe_core), GFP_KERNEL); + if (!core) + return NULL; + + snprintf(core->fname, sizeof(core->fname), "%s", img_fname); + strlcpy(core->dyn_fname, core->fname, WCD_CPE_IMAGE_FNAME_MAX); + + wcd_get_cpe_core = params->get_cpe_core; + + core->codec = params->codec; + core->dev = params->codec->dev; + core->cpe_debug_mode = params->dbg_mode; + + core->cdc_info.major_version = params->cdc_major_ver; + core->cdc_info.minor_version = params->cdc_minor_ver; + core->cdc_info.id = params->cdc_id; + + core->cpe_cdc_cb = params->cdc_cb; + + memcpy(&core->irq_info, ¶ms->cdc_irq_info, + sizeof(core->irq_info)); + + INIT_WORK(&core->load_fw_work, wcd_cpe_load_fw_image); + INIT_WORK(&core->ssr_work, wcd_cpe_ssr_work); + init_completion(&core->offline_compl); + init_completion(&core->ready_compl); + init_completion(&core->online_compl); + init_waitqueue_head(&core->ssr_entry.offline_poll_wait); + mutex_init(&core->ssr_lock); + mutex_init(&core->session_lock); + core->cpe_users = 0; + core->cpe_clk_ref = 0; + + /* + * By default, during probe, it is assumed that + * both CPE hardware block and underlying bus to codec + * are ready + */ + core->ready_status = WCD_CPE_READY_TO_DLOAD; + + core->cpe_handle = cpe_svc_initialize(NULL, &core->cdc_info, + params->cpe_svc_params); + if (!core->cpe_handle) { + dev_err(core->dev, + "%s: failed to initialize cpe services\n", + __func__); + goto fail_cpe_initialize; + } + + core->cpe_reg_handle = cpe_svc_register(core->cpe_handle, + wcd_cpe_svc_event_cb, + CPE_SVC_ONLINE | CPE_SVC_OFFLINE | + CPE_SVC_BOOT | + CPE_SVC_CMI_CLIENTS_DEREG, + "codec cpe handler"); + if (!core->cpe_reg_handle) { + dev_err(core->dev, + "%s: failed to register cpe service\n", + __func__); + goto fail_cpe_register; + } + + card = codec->component.card->snd_card; + snprintf(proc_name, (sizeof("cpe") + sizeof("_state") + + sizeof(id) - 2), "%s%d%s", cpe_name, id, state_name); + entry = snd_info_create_card_entry(card, proc_name, + card->proc_root); + if (entry) { + core->ssr_entry.entry = entry; + core->ssr_entry.offline = 1; + entry->size = WCD_CPE_STATE_MAX_LEN; + entry->content = SNDRV_INFO_CONTENT_DATA; + entry->c.ops = &wcd_cpe_state_proc_ops; + entry->private_data = core; + ret = snd_info_register(entry); + if (ret < 0) { + dev_err(core->dev, + "%s: snd_info_register failed (%d)\n", + __func__, ret); + snd_info_free_entry(entry); + entry = NULL; + } + } else { + dev_err(core->dev, + "%s: Failed to create CPE SSR status entry\n", + __func__); + /* + * Even if SSR entry creation fails, continue + * with image download + */ + } + + core_d = core; + ret = wcd_cpe_cal_init(core); + if (ret < 0) { + dev_err(core->dev, + "%s: CPE calibration init failed, err = %d\n", + __func__, ret); + goto fail_cpe_reset; + } + + wcd_cpe_debugfs_init(core); + + wcd_cpe_sysfs_init(core, id); + + hw_info = cpe_svc_get_hw_cfg(core->cpe_handle); + if (!hw_info) { + dev_err(core->dev, + "%s: hw info not available\n", + __func__); + goto schedule_dload_work; + } else { + core->hw_info.dram_offset = hw_info->DRAM_offset; + core->hw_info.dram_size = hw_info->DRAM_size; + core->hw_info.iram_offset = hw_info->IRAM_offset; + core->hw_info.iram_size = hw_info->IRAM_size; + } + + /* Setup the ramdump device and buffer */ + core->cpe_ramdump_dev = create_ramdump_device("cpe", + core->dev); + if (!core->cpe_ramdump_dev) { + dev_err(core->dev, + "%s: Failed to create ramdump device\n", + __func__); + goto schedule_dload_work; + } + + arch_setup_dma_ops(core->dev, 0, 0, NULL, 0); + core->cpe_dump_v_addr = dma_alloc_coherent(core->dev, + core->hw_info.dram_size, + &core->cpe_dump_addr, + GFP_KERNEL); + if (!core->cpe_dump_v_addr) { + dev_err(core->dev, + "%s: Failed to alloc memory for cpe dump, size = %zd\n", + __func__, core->hw_info.dram_size); + goto schedule_dload_work; + } else { + memset(core->cpe_dump_v_addr, 0, core->hw_info.dram_size); + } + +schedule_dload_work: + core->ssr_type = WCD_CPE_INITIALIZED; + schedule_work(&core->load_fw_work); + return core; + +fail_cpe_reset: + cpe_svc_deregister(core->cpe_handle, core->cpe_reg_handle); + +fail_cpe_register: + cpe_svc_deinitialize(core->cpe_handle); + +fail_cpe_initialize: + kfree(core); + return NULL; +} +EXPORT_SYMBOL(wcd_cpe_init); + +/* + * wcd_cpe_cmi_lsm_callback: callback called from cpe services + * to notify command response for lsm + * service + * @param: param containing the response code and status + * + * This callback is registered with cpe services while registering + * the LSM service + */ +static void wcd_cpe_cmi_lsm_callback(const struct cmi_api_notification *param) +{ + struct cmi_hdr *hdr; + struct cpe_lsm_session *lsm_session; + u8 session_id; + + if (!param) { + pr_err("%s: param is null\n", __func__); + return; + } + + if (param->event != CMI_API_MSG) { + pr_err("%s: unhandled event 0x%x\n", __func__, param->event); + return; + } + + hdr = (struct cmi_hdr *) param->message; + session_id = CMI_HDR_GET_SESSION_ID(hdr); + + if (session_id > WCD_CPE_LSM_MAX_SESSIONS) { + pr_err("%s: invalid lsm session id = %d\n", + __func__, session_id); + return; + } + + lsm_session = lsm_sessions[session_id]; + + if (hdr->opcode == CPE_CMI_BASIC_RSP_OPCODE) { + + u8 *payload = ((u8 *)param->message) + (sizeof(struct cmi_hdr)); + u8 result = payload[0]; + + lsm_session->cmd_err_code = result; + complete(&lsm_session->cmd_comp); + + } else if (hdr->opcode == CPE_LSM_SESSION_CMDRSP_SHARED_MEM_ALLOC) { + + struct cpe_cmdrsp_shmem_alloc *cmdrsp_shmem_alloc = + (struct cpe_cmdrsp_shmem_alloc *) param->message; + + if (cmdrsp_shmem_alloc->addr == 0) { + pr_err("%s: Failed LSM shared mem alloc\n", __func__); + lsm_session->cmd_err_code = CMI_SHMEM_ALLOC_FAILED; + + } else { + + pr_debug("%s LSM shared mem addr = 0x%x\n", + __func__, cmdrsp_shmem_alloc->addr); + lsm_session->lsm_mem_handle = cmdrsp_shmem_alloc->addr; + lsm_session->cmd_err_code = 0; + } + + complete(&lsm_session->cmd_comp); + + } else if (hdr->opcode == CPE_LSM_SESSION_EVENT_DETECTION_STATUS_V2) { + + struct cpe_lsm_event_detect_v2 *event_detect_v2 = + (struct cpe_lsm_event_detect_v2 *) param->message; + + if (!lsm_session->priv_d) { + pr_err("%s: private data is not present\n", + __func__); + return; + } + + pr_debug("%s: event payload, status = %u, size = %u\n", + __func__, event_detect_v2->detection_status, + event_detect_v2->size); + + if (lsm_session->event_cb) + lsm_session->event_cb( + lsm_session->priv_d, + event_detect_v2->detection_status, + event_detect_v2->size, + event_detect_v2->payload); + } +} + +/* + * wcd_cpe_cmi_send_lsm_msg: send a message to lsm service + * @core: handle to cpe core + * @session: session on which to send the message + * @message: actual message containing header and payload + * + * Sends message to lsm service for specified session and wait + * for response back on the message. + * should be called after acquiring session specific mutex + */ +static int wcd_cpe_cmi_send_lsm_msg( + struct wcd_cpe_core *core, + struct cpe_lsm_session *session, + void *message) +{ + int ret = 0; + struct cmi_hdr *hdr = message; + + pr_debug("%s: sending message with opcode 0x%x\n", + __func__, hdr->opcode); + + if (unlikely(!wcd_cpe_is_online_state(core))) { + dev_err(core->dev, + "%s: MSG not sent, CPE offline\n", + __func__); + goto done; + } + + if (CMI_HDR_GET_OBM_FLAG(hdr)) + wcd_cpe_bus_vote_max_bw(core, true); + + reinit_completion(&session->cmd_comp); + ret = cmi_send_msg(message); + if (ret) { + pr_err("%s: msg opcode (0x%x) send failed (%d)\n", + __func__, hdr->opcode, ret); + goto rel_bus_vote; + } + + ret = wait_for_completion_timeout(&session->cmd_comp, + CMI_CMD_TIMEOUT); + if (ret > 0) { + pr_debug("%s: command 0x%x, received response 0x%x\n", + __func__, hdr->opcode, session->cmd_err_code); + if (session->cmd_err_code == CMI_SHMEM_ALLOC_FAILED) + session->cmd_err_code = CPE_ENOMEMORY; + if (session->cmd_err_code > 0) + pr_err("%s: CPE returned error[%s]\n", + __func__, cpe_err_get_err_str( + session->cmd_err_code)); + ret = cpe_err_get_lnx_err_code(session->cmd_err_code); + goto rel_bus_vote; + } else { + pr_err("%s: command (0x%x) send timed out\n", + __func__, hdr->opcode); + ret = -ETIMEDOUT; + goto rel_bus_vote; + } + + +rel_bus_vote: + + if (CMI_HDR_GET_OBM_FLAG(hdr)) + wcd_cpe_bus_vote_max_bw(core, false); + +done: + return ret; +} + + +/* + * fill_cmi_header: fill the cmi header with specified values + * + * @hdr: header to be updated with values + * @session_id: session id of the header, + * in case of AFE service it is port_id + * @service_id: afe/lsm, etc + * @version: update the version field in header + * @payload_size: size of the payload following after header + * @opcode: opcode of the message + * @obm_flag: indicates if this header is for obm message + * + */ +static int fill_cmi_header(struct cmi_hdr *hdr, + u8 session_id, u8 service_id, + bool version, u8 payload_size, + u16 opcode, bool obm_flag) +{ + /* sanitize the data */ + if (!IS_VALID_SESSION_ID(session_id) || + !IS_VALID_SERVICE_ID(service_id) || + !IS_VALID_PLD_SIZE(payload_size)) { + pr_err("Invalid header creation request\n"); + return -EINVAL; + } + + CMI_HDR_SET_SESSION(hdr, session_id); + CMI_HDR_SET_SERVICE(hdr, service_id); + if (version) + CMI_HDR_SET_VERSION(hdr, 1); + else + CMI_HDR_SET_VERSION(hdr, 0); + + CMI_HDR_SET_PAYLOAD_SIZE(hdr, payload_size); + + hdr->opcode = opcode; + + if (obm_flag) + CMI_HDR_SET_OBM(hdr, CMI_OBM_FLAG_OUT_BAND); + else + CMI_HDR_SET_OBM(hdr, CMI_OBM_FLAG_IN_BAND); + + return 0; +} + +/* + * fill_lsm_cmd_header_v0_inband: + * Given the header, fill the header with information + * for lsm service, version 0 and inband message + * @hdr: the cmi header to be filled. + * @session_id: ID for the lsm session + * @payload_size: size for cmi message payload + * @opcode: opcode for cmi message + */ +static int fill_lsm_cmd_header_v0_inband(struct cmi_hdr *hdr, + u8 session_id, u8 payload_size, u16 opcode) +{ + return fill_cmi_header(hdr, session_id, + CMI_CPE_LSM_SERVICE_ID, false, + payload_size, opcode, false); +} + +/* + * wcd_cpe_is_valid_lsm_session: + * Check session parameters to identify validity for the sesion + * @core: handle to cpe core + * @session: handle to the lsm session + * @func: invoking function to be printed in error logs + */ +static int wcd_cpe_is_valid_lsm_session(struct wcd_cpe_core *core, + struct cpe_lsm_session *session, + const char *func) +{ + if (unlikely(IS_ERR_OR_NULL(core))) { + pr_err("%s: invalid handle to core\n", + func); + return -EINVAL; + } + + if (unlikely(IS_ERR_OR_NULL(session))) { + dev_err(core->dev, "%s: invalid session\n", + func); + return -EINVAL; + } + + if (session->id > WCD_CPE_LSM_MAX_SESSIONS) { + dev_err(core->dev, "%s: invalid session id (%u)\n", + func, session->id); + return -EINVAL; + } + + dev_dbg(core->dev, "%s: session_id = %u\n", + func, session->id); + return 0; +} + +static int wcd_cpe_cmd_lsm_open_tx_v2( + struct wcd_cpe_core *core, + struct cpe_lsm_session *session) +{ + struct cpe_lsm_cmd_open_tx_v2 cmd_open_tx_v2; + struct cal_block_data *top_cal = NULL; + struct audio_cal_info_lsm_top *lsm_top; + int ret = 0; + + ret = wcd_cpe_is_valid_lsm_session(core, session, + __func__); + if (ret) + return ret; + + if (core->cal_data[WCD_CPE_LSM_CAL_TOPOLOGY_ID] == NULL) { + dev_err(core->dev, + "%s: LSM_TOPOLOGY cal not allocated!\n", + __func__); + return -EINVAL; + } + + mutex_lock(&core->cal_data[WCD_CPE_LSM_CAL_TOPOLOGY_ID]->lock); + top_cal = cal_utils_get_only_cal_block( + core->cal_data[WCD_CPE_LSM_CAL_TOPOLOGY_ID]); + if (!top_cal) { + dev_err(core->dev, + "%s: Failed to get LSM TOPOLOGY cal block\n", + __func__); + ret = -EINVAL; + goto unlock_cal_mutex; + } + + lsm_top = (struct audio_cal_info_lsm_top *) + top_cal->cal_info; + + if (!lsm_top) { + dev_err(core->dev, + "%s: cal_info for LSM_TOPOLOGY not found\n", + __func__); + ret = -EINVAL; + goto unlock_cal_mutex; + } + + dev_dbg(core->dev, + "%s: topology_id = 0x%x, acdb_id = 0x%x, app_type = 0x%x\n", + __func__, lsm_top->topology, lsm_top->acdb_id, + lsm_top->app_type); + + if (lsm_top->topology == 0) { + dev_err(core->dev, + "%s: topology id not sent for app_type 0x%x\n", + __func__, lsm_top->app_type); + ret = -EINVAL; + goto unlock_cal_mutex; + } + + WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm"); + + memset(&cmd_open_tx_v2, 0, sizeof(struct cpe_lsm_cmd_open_tx_v2)); + if (fill_lsm_cmd_header_v0_inband(&cmd_open_tx_v2.hdr, + session->id, OPEN_V2_CMD_PAYLOAD_SIZE, + CPE_LSM_SESSION_CMD_OPEN_TX_V2)) { + ret = -EINVAL; + goto end_ret; + } + + cmd_open_tx_v2.topology_id = lsm_top->topology; + ret = wcd_cpe_cmi_send_lsm_msg(core, session, &cmd_open_tx_v2); + if (ret) + dev_err(core->dev, + "%s: failed to send open_tx_v2 cmd, err = %d\n", + __func__, ret); + else + session->is_topology_used = true; +end_ret: + WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm"); + +unlock_cal_mutex: + mutex_unlock(&core->cal_data[WCD_CPE_LSM_CAL_TOPOLOGY_ID]->lock); + return ret; +} + +/* + * wcd_cpe_cmd_lsm_open_tx: compose and send lsm open command + * @core_handle: handle to cpe core + * @session: session for which the command needs to be sent + * @app_id: application id part of the command + * @sample_rate: sample rate for this session + */ +static int wcd_cpe_cmd_lsm_open_tx(void *core_handle, + struct cpe_lsm_session *session, + u16 app_id, u16 sample_rate) +{ + struct cpe_lsm_cmd_open_tx cmd_open_tx; + struct wcd_cpe_core *core = core_handle; + int ret = 0; + + ret = wcd_cpe_is_valid_lsm_session(core, session, + __func__); + if (ret) + return ret; + + /* Try to open with topology first */ + ret = wcd_cpe_cmd_lsm_open_tx_v2(core, session); + if (!ret) + goto done; + + dev_dbg(core->dev, "%s: Try open_tx without topology\n", + __func__); + + WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm"); + + memset(&cmd_open_tx, 0, sizeof(struct cpe_lsm_cmd_open_tx)); + if (fill_lsm_cmd_header_v0_inband(&cmd_open_tx.hdr, + session->id, OPEN_CMD_PAYLOAD_SIZE, + CPE_LSM_SESSION_CMD_OPEN_TX)) { + ret = -EINVAL; + goto end_ret; + } + + cmd_open_tx.app_id = app_id; + cmd_open_tx.sampling_rate = sample_rate; + + ret = wcd_cpe_cmi_send_lsm_msg(core, session, &cmd_open_tx); + if (ret) + dev_err(core->dev, + "%s: failed to send open_tx cmd, err = %d\n", + __func__, ret); +end_ret: + WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm"); +done: + return ret; +} + +/* + * wcd_cpe_cmd_close_tx: compose and send lsm close command + * @core_handle: handle to cpe core + * @session: session for which the command needs to be sent + */ +static int wcd_cpe_cmd_lsm_close_tx(void *core_handle, + struct cpe_lsm_session *session) +{ + struct cmi_hdr cmd_close_tx; + struct wcd_cpe_core *core = core_handle; + int ret = 0; + + ret = wcd_cpe_is_valid_lsm_session(core, session, + __func__); + if (ret) + return ret; + + WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm"); + + memset(&cmd_close_tx, 0, sizeof(cmd_close_tx)); + if (fill_lsm_cmd_header_v0_inband(&cmd_close_tx, session->id, + 0, CPE_LSM_SESSION_CMD_CLOSE_TX)) { + ret = -EINVAL; + goto end_ret; + } + + ret = wcd_cpe_cmi_send_lsm_msg(core, session, &cmd_close_tx); + if (ret) + dev_err(core->dev, + "%s: lsm close_tx cmd failed, err = %d\n", + __func__, ret); + else + session->is_topology_used = false; +end_ret: + WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm"); + return ret; +} + +/* + * wcd_cpe_cmd_shmem_alloc: compose and send lsm shared + * memory allocation command + * @core_handle: handle to cpe core + * @session: session for which the command needs to be sent + * @size: size of memory to be allocated + */ +static int wcd_cpe_cmd_lsm_shmem_alloc(void *core_handle, + struct cpe_lsm_session *session, + u32 size) +{ + struct cpe_cmd_shmem_alloc cmd_shmem_alloc; + struct wcd_cpe_core *core = core_handle; + int ret = 0; + + ret = wcd_cpe_is_valid_lsm_session(core, session, + __func__); + if (ret) + return ret; + + WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm"); + + memset(&cmd_shmem_alloc, 0, sizeof(cmd_shmem_alloc)); + if (fill_lsm_cmd_header_v0_inband(&cmd_shmem_alloc.hdr, session->id, + SHMEM_ALLOC_CMD_PLD_SIZE, + CPE_LSM_SESSION_CMD_SHARED_MEM_ALLOC)) { + ret = -EINVAL; + goto end_ret; + } + + cmd_shmem_alloc.size = size; + ret = wcd_cpe_cmi_send_lsm_msg(core, session, &cmd_shmem_alloc); + if (ret) + dev_err(core->dev, + "%s: lsm_shmem_alloc cmd send fail, %d\n", + __func__, ret); +end_ret: + WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm"); + return ret; +} + +/* + * wcd_cpe_cmd_lsm_shmem_dealloc: deallocate the shared memory + * for the specified session + * @core_handle: handle to cpe core + * @session: session for which memory needs to be deallocated. + */ +static int wcd_cpe_cmd_lsm_shmem_dealloc(void *core_handle, + struct cpe_lsm_session *session) +{ + struct cpe_cmd_shmem_dealloc cmd_dealloc; + struct wcd_cpe_core *core = core_handle; + int ret = 0; + + ret = wcd_cpe_is_valid_lsm_session(core, session, + __func__); + if (ret) + return ret; + + WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm"); + + memset(&cmd_dealloc, 0, sizeof(cmd_dealloc)); + if (fill_lsm_cmd_header_v0_inband(&cmd_dealloc.hdr, session->id, + SHMEM_DEALLOC_CMD_PLD_SIZE, + CPE_LSM_SESSION_CMD_SHARED_MEM_DEALLOC)) { + ret = -EINVAL; + goto end_ret; + } + + cmd_dealloc.addr = session->lsm_mem_handle; + ret = wcd_cpe_cmi_send_lsm_msg(core, session, &cmd_dealloc); + if (ret) { + dev_err(core->dev, + "%s: lsm_shmem_dealloc cmd failed, rc %d\n", + __func__, ret); + goto end_ret; + } + + memset(&session->lsm_mem_handle, 0, + sizeof(session->lsm_mem_handle)); + +end_ret: + WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm"); + return ret; +} + +/* + * wcd_cpe_send_lsm_cal: send the calibration for lsm service + * from acdb to the cpe + * @core: handle to cpe core + * @session: session for which the calibration needs to be set. + */ +static int wcd_cpe_send_lsm_cal( + struct wcd_cpe_core *core, + struct cpe_lsm_session *session) +{ + + u8 *msg_pld; + struct cmi_hdr *hdr; + struct cal_block_data *lsm_cal = NULL; + void *inb_msg; + int rc = 0; + + if (core->cal_data[WCD_CPE_LSM_CAL_LSM] == NULL) { + pr_err("%s: LSM cal not allocated!\n", __func__); + return -EINVAL; + } + + mutex_lock(&core->cal_data[WCD_CPE_LSM_CAL_LSM]->lock); + lsm_cal = cal_utils_get_only_cal_block( + core->cal_data[WCD_CPE_LSM_CAL_LSM]); + if (!lsm_cal) { + pr_err("%s: failed to get lsm cal block\n", __func__); + rc = -EINVAL; + goto unlock_cal_mutex; + } + + if (lsm_cal->cal_data.size == 0) { + dev_dbg(core->dev, "%s: No LSM cal to send\n", + __func__); + rc = 0; + goto unlock_cal_mutex; + } + + inb_msg = kzalloc(sizeof(struct cmi_hdr) + lsm_cal->cal_data.size, + GFP_KERNEL); + if (!inb_msg) { + rc = -ENOMEM; + goto unlock_cal_mutex; + } + + hdr = (struct cmi_hdr *) inb_msg; + + rc = fill_lsm_cmd_header_v0_inband(hdr, session->id, + lsm_cal->cal_data.size, + CPE_LSM_SESSION_CMD_SET_PARAMS); + if (rc) { + pr_err("%s: invalid params for header, err = %d\n", + __func__, rc); + goto free_msg; + } + + msg_pld = ((u8 *) inb_msg) + sizeof(struct cmi_hdr); + memcpy(msg_pld, lsm_cal->cal_data.kvaddr, + lsm_cal->cal_data.size); + + rc = wcd_cpe_cmi_send_lsm_msg(core, session, inb_msg); + if (rc) + pr_err("%s: acdb lsm_params send failed, err = %d\n", + __func__, rc); + +free_msg: + kfree(inb_msg); + +unlock_cal_mutex: + mutex_unlock(&core->cal_data[WCD_CPE_LSM_CAL_LSM]->lock); + return rc; + +} + +static void wcd_cpe_set_param_data(struct cpe_param_data *param_d, + struct cpe_lsm_ids *ids, u32 p_size, + u32 set_param_cmd) +{ + param_d->module_id = ids->module_id; + param_d->param_id = ids->param_id; + + switch (set_param_cmd) { + case CPE_LSM_SESSION_CMD_SET_PARAMS_V2: + param_d->p_size.param_size = p_size; + break; + case CPE_LSM_SESSION_CMD_SET_PARAMS: + default: + param_d->p_size.sr.param_size = + (u16) p_size; + param_d->p_size.sr.reserved = 0; + break; + } +} + +static int wcd_cpe_send_param_epd_thres(struct wcd_cpe_core *core, + struct cpe_lsm_session *session, + void *data, struct cpe_lsm_ids *ids) +{ + struct snd_lsm_ep_det_thres *ep_det_data; + struct cpe_lsm_param_epd_thres epd_cmd; + struct cmi_hdr *msg_hdr = &epd_cmd.hdr; + struct cpe_param_data *param_d = + &epd_cmd.param; + int rc; + + memset(&epd_cmd, 0, sizeof(epd_cmd)); + ep_det_data = (struct snd_lsm_ep_det_thres *) data; + if (fill_lsm_cmd_header_v0_inband(msg_hdr, + session->id, + CPE_CMD_EPD_THRES_PLD_SIZE, + CPE_LSM_SESSION_CMD_SET_PARAMS_V2)) { + rc = -EINVAL; + goto err_ret; + } + + wcd_cpe_set_param_data(param_d, ids, + CPE_EPD_THRES_PARAM_SIZE, + CPE_LSM_SESSION_CMD_SET_PARAMS_V2); + + epd_cmd.minor_version = 1; + epd_cmd.epd_begin = ep_det_data->epd_begin; + epd_cmd.epd_end = ep_det_data->epd_end; + + WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm"); + rc = wcd_cpe_cmi_send_lsm_msg(core, session, &epd_cmd); + if (unlikely(rc)) + dev_err(core->dev, + "%s: set_param(EPD Threshold) failed, rc %dn", + __func__, rc); + WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm"); +err_ret: + return rc; +} + +static int wcd_cpe_send_param_opmode(struct wcd_cpe_core *core, + struct cpe_lsm_session *session, + void *data, struct cpe_lsm_ids *ids) +{ + struct snd_lsm_detect_mode *opmode_d; + struct cpe_lsm_param_opmode opmode_cmd; + struct cmi_hdr *msg_hdr = &opmode_cmd.hdr; + struct cpe_param_data *param_d = + &opmode_cmd.param; + int rc; + + memset(&opmode_cmd, 0, sizeof(opmode_cmd)); + opmode_d = (struct snd_lsm_detect_mode *) data; + if (fill_lsm_cmd_header_v0_inband(msg_hdr, + session->id, + CPE_CMD_OPMODE_PLD_SIZE, + CPE_LSM_SESSION_CMD_SET_PARAMS_V2)) { + rc = -EINVAL; + goto err_ret; + } + + wcd_cpe_set_param_data(param_d, ids, + CPE_OPMODE_PARAM_SIZE, + CPE_LSM_SESSION_CMD_SET_PARAMS_V2); + + opmode_cmd.minor_version = 1; + if (opmode_d->mode == LSM_MODE_KEYWORD_ONLY_DETECTION) + opmode_cmd.mode = 1; + else + opmode_cmd.mode = 3; + + if (opmode_d->detect_failure) + opmode_cmd.mode |= 0x04; + + opmode_cmd.reserved = 0; + + WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm"); + rc = wcd_cpe_cmi_send_lsm_msg(core, session, &opmode_cmd); + if (unlikely(rc)) + dev_err(core->dev, + "%s: set_param(operation_mode) failed, rc %dn", + __func__, rc); + WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm"); +err_ret: + return rc; +} + +static int wcd_cpe_send_param_gain(struct wcd_cpe_core *core, + struct cpe_lsm_session *session, + void *data, struct cpe_lsm_ids *ids) +{ + struct snd_lsm_gain *gain_d; + struct cpe_lsm_param_gain gain_cmd; + struct cmi_hdr *msg_hdr = &gain_cmd.hdr; + struct cpe_param_data *param_d = + &gain_cmd.param; + int rc; + + memset(&gain_cmd, 0, sizeof(gain_cmd)); + gain_d = (struct snd_lsm_gain *) data; + if (fill_lsm_cmd_header_v0_inband(msg_hdr, + session->id, + CPE_CMD_GAIN_PLD_SIZE, + CPE_LSM_SESSION_CMD_SET_PARAMS_V2)) { + rc = -EINVAL; + goto err_ret; + } + + wcd_cpe_set_param_data(param_d, ids, + CPE_GAIN_PARAM_SIZE, + CPE_LSM_SESSION_CMD_SET_PARAMS_V2); + + gain_cmd.minor_version = 1; + gain_cmd.gain = gain_d->gain; + gain_cmd.reserved = 0; + + WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm"); + rc = wcd_cpe_cmi_send_lsm_msg(core, session, &gain_cmd); + if (unlikely(rc)) + dev_err(core->dev, + "%s: set_param(lsm_gain) failed, rc %dn", + __func__, rc); + WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm"); +err_ret: + return rc; +} + +static int wcd_cpe_send_param_connectport(struct wcd_cpe_core *core, + struct cpe_lsm_session *session, + void *data, struct cpe_lsm_ids *ids, u16 port_id) +{ + struct cpe_lsm_param_connectport con_port_cmd; + struct cmi_hdr *msg_hdr = &con_port_cmd.hdr; + struct cpe_param_data *param_d = + &con_port_cmd.param; + int rc; + + memset(&con_port_cmd, 0, sizeof(con_port_cmd)); + if (fill_lsm_cmd_header_v0_inband(msg_hdr, + session->id, + CPE_CMD_CONNECTPORT_PLD_SIZE, + CPE_LSM_SESSION_CMD_SET_PARAMS_V2)) { + rc = -EINVAL; + goto err_ret; + } + + wcd_cpe_set_param_data(param_d, ids, + CPE_CONNECTPORT_PARAM_SIZE, + CPE_LSM_SESSION_CMD_SET_PARAMS_V2); + + con_port_cmd.minor_version = 1; + con_port_cmd.afe_port_id = port_id; + con_port_cmd.reserved = 0; + + WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm"); + rc = wcd_cpe_cmi_send_lsm_msg(core, session, &con_port_cmd); + if (unlikely(rc)) + dev_err(core->dev, + "%s: set_param(connect_port) failed, rc %dn", + __func__, rc); + WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm"); +err_ret: + return rc; +} + +static int wcd_cpe_send_param_conf_levels( + struct wcd_cpe_core *core, + struct cpe_lsm_session *session, + struct cpe_lsm_ids *ids) +{ + struct cpe_lsm_conf_level conf_level_data; + struct cmi_hdr *hdr = &(conf_level_data.hdr); + struct cpe_param_data *param_d = &(conf_level_data.param); + u8 pld_size = 0; + u8 pad_bytes = 0; + void *message; + int ret = 0; + + memset(&conf_level_data, 0, sizeof(conf_level_data)); + + pld_size = (sizeof(struct cpe_lsm_conf_level) - sizeof(struct cmi_hdr)); + pld_size += session->num_confidence_levels; + pad_bytes = ((4 - (pld_size % 4)) % 4); + pld_size += pad_bytes; + + fill_cmi_header(hdr, session->id, CMI_CPE_LSM_SERVICE_ID, + false, pld_size, + CPE_LSM_SESSION_CMD_SET_PARAMS_V2, false); + + wcd_cpe_set_param_data(param_d, ids, + pld_size - sizeof(struct cpe_param_data), + CPE_LSM_SESSION_CMD_SET_PARAMS_V2); + + conf_level_data.num_active_models = session->num_confidence_levels; + + message = kzalloc(sizeof(struct cpe_lsm_conf_level) + + conf_level_data.num_active_models + pad_bytes, + GFP_KERNEL); + if (!message) { + pr_err("%s: no memory for conf_level\n", __func__); + return -ENOMEM; + } + + memcpy(message, &conf_level_data, + sizeof(struct cpe_lsm_conf_level)); + memcpy(((u8 *) message) + sizeof(struct cpe_lsm_conf_level), + session->conf_levels, conf_level_data.num_active_models); + + WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm"); + ret = wcd_cpe_cmi_send_lsm_msg(core, session, message); + if (ret) + pr_err("%s: lsm_set_conf_levels failed, err = %d\n", + __func__, ret); + kfree(message); + WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm"); + return ret; +} + +static int wcd_cpe_send_param_snd_model(struct wcd_cpe_core *core, + struct cpe_lsm_session *session, struct cpe_lsm_ids *ids) +{ + int ret = 0; + struct cmi_obm_msg obm_msg; + struct cpe_param_data *param_d; + + + ret = fill_cmi_header(&obm_msg.hdr, session->id, + CMI_CPE_LSM_SERVICE_ID, 0, 20, + CPE_LSM_SESSION_CMD_SET_PARAMS_V2, true); + if (ret) { + dev_err(core->dev, + "%s: Invalid parameters, rc = %d\n", + __func__, ret); + goto err_ret; + } + + obm_msg.pld.version = 0; + obm_msg.pld.size = session->snd_model_size; + obm_msg.pld.data_ptr.kvaddr = session->snd_model_data; + obm_msg.pld.mem_handle = session->lsm_mem_handle; + + param_d = (struct cpe_param_data *) session->snd_model_data; + wcd_cpe_set_param_data(param_d, ids, + (session->snd_model_size - sizeof(*param_d)), + CPE_LSM_SESSION_CMD_SET_PARAMS_V2); + + WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm"); + ret = wcd_cpe_cmi_send_lsm_msg(core, session, &obm_msg); + if (ret) + dev_err(core->dev, + "%s: snd_model_register failed, %d\n", + __func__, ret); + WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm"); + +err_ret: + return ret; +} + +static int wcd_cpe_send_param_dereg_model( + struct wcd_cpe_core *core, + struct cpe_lsm_session *session, + struct cpe_lsm_ids *ids) +{ + struct cmi_hdr *hdr; + struct cpe_param_data *param_d; + u8 *message; + u32 pld_size; + int rc = 0; + + pld_size = sizeof(*hdr) + sizeof(*param_d); + + message = kzalloc(pld_size, GFP_KERNEL); + if (!message) + return -ENOMEM; + + hdr = (struct cmi_hdr *) message; + param_d = (struct cpe_param_data *) + (((u8 *) message) + sizeof(*hdr)); + + if (fill_lsm_cmd_header_v0_inband(hdr, + session->id, + sizeof(*param_d), + CPE_LSM_SESSION_CMD_SET_PARAMS_V2)) { + rc = -EINVAL; + goto err_ret; + } + wcd_cpe_set_param_data(param_d, ids, 0, + CPE_LSM_SESSION_CMD_SET_PARAMS_V2); + WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm"); + rc = wcd_cpe_cmi_send_lsm_msg(core, session, message); + if (rc) + dev_err(core->dev, + "%s: snd_model_deregister failed, %d\n", + __func__, rc); + WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm"); +err_ret: + kfree(message); + return rc; +} + +static int wcd_cpe_send_custom_param( + struct wcd_cpe_core *core, + struct cpe_lsm_session *session, + void *data, u32 msg_size) +{ + u8 *msg; + struct cmi_hdr *hdr; + u8 *msg_pld; + int rc; + + if (msg_size > CMI_INBAND_MESSAGE_SIZE) { + dev_err(core->dev, + "%s: out of band custom params not supported\n", + __func__); + return -EINVAL; + } + + msg = kzalloc(sizeof(*hdr) + msg_size, GFP_KERNEL); + if (!msg) + return -ENOMEM; + + hdr = (struct cmi_hdr *) msg; + msg_pld = msg + sizeof(struct cmi_hdr); + + if (fill_lsm_cmd_header_v0_inband(hdr, + session->id, + msg_size, + CPE_LSM_SESSION_CMD_SET_PARAMS_V2)) { + rc = -EINVAL; + goto err_ret; + } + + memcpy(msg_pld, data, msg_size); + WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm"); + rc = wcd_cpe_cmi_send_lsm_msg(core, session, msg); + if (rc) + dev_err(core->dev, + "%s: custom params send failed, err = %d\n", + __func__, rc); + WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm"); +err_ret: + kfree(msg); + return rc; +} + +static int wcd_cpe_set_one_param(void *core_handle, + struct cpe_lsm_session *session, struct lsm_params_info *p_info, + void *data, uint32_t param_type) +{ + struct wcd_cpe_core *core = core_handle; + int rc = 0; + struct cpe_lsm_ids ids; + + memset(&ids, 0, sizeof(ids)); + ids.module_id = p_info->module_id; + ids.param_id = p_info->param_id; + + switch (param_type) { + case LSM_ENDPOINT_DETECT_THRESHOLD: + rc = wcd_cpe_send_param_epd_thres(core, session, + data, &ids); + break; + case LSM_OPERATION_MODE: + rc = wcd_cpe_send_param_opmode(core, session, data, &ids); + break; + case LSM_GAIN: + rc = wcd_cpe_send_param_gain(core, session, data, &ids); + break; + case LSM_MIN_CONFIDENCE_LEVELS: + rc = wcd_cpe_send_param_conf_levels(core, session, &ids); + break; + case LSM_REG_SND_MODEL: + rc = wcd_cpe_send_param_snd_model(core, session, &ids); + break; + case LSM_DEREG_SND_MODEL: + rc = wcd_cpe_send_param_dereg_model(core, session, &ids); + break; + case LSM_CUSTOM_PARAMS: + rc = wcd_cpe_send_custom_param(core, session, + data, p_info->param_size); + break; + default: + pr_err("%s: wrong param_type 0x%x\n", + __func__, param_type); + } + + if (rc) + dev_err(core->dev, + "%s: send_param(%d) failed, err %d\n", + __func__, param_type, rc); + return rc; +} + +/* + * wcd_cpe_lsm_set_params: set the parameters for lsm service + * @core: handle to cpe core + * @session: session for which the parameters are to be set + * @detect_mode: mode for detection + * @detect_failure: flag indicating failure detection enabled/disabled + * + */ +static int wcd_cpe_lsm_set_params(struct wcd_cpe_core *core, + struct cpe_lsm_session *session, + enum lsm_detection_mode detect_mode, bool detect_failure) +{ + struct cpe_lsm_ids ids; + struct snd_lsm_detect_mode det_mode; + + int ret = 0; + + /* Send lsm calibration */ + ret = wcd_cpe_send_lsm_cal(core, session); + if (ret) { + pr_err("%s: fail to sent acdb cal, err = %d", + __func__, ret); + goto err_ret; + } + + /* Send operation mode */ + ids.module_id = CPE_LSM_MODULE_ID_VOICE_WAKEUP; + ids.param_id = CPE_LSM_PARAM_ID_OPERATION_MODE; + det_mode.mode = detect_mode; + det_mode.detect_failure = detect_failure; + ret = wcd_cpe_send_param_opmode(core, session, + &det_mode, &ids); + if (ret) + dev_err(core->dev, + "%s: Failed to set opmode, err=%d\n", + __func__, ret); + +err_ret: + return ret; +} + +static int wcd_cpe_lsm_set_data(void *core_handle, + struct cpe_lsm_session *session, + enum lsm_detection_mode detect_mode, + bool detect_failure) +{ + struct wcd_cpe_core *core = core_handle; + struct cpe_lsm_ids ids; + int ret = 0; + + if (session->num_confidence_levels > 0) { + ret = wcd_cpe_lsm_set_params(core, session, detect_mode, + detect_failure); + if (ret) { + dev_err(core->dev, + "%s: lsm set params failed, rc = %d\n", + __func__, ret); + goto err_ret; + } + + ids.module_id = CPE_LSM_MODULE_ID_VOICE_WAKEUP; + ids.param_id = CPE_LSM_PARAM_ID_MIN_CONFIDENCE_LEVELS; + ret = wcd_cpe_send_param_conf_levels(core, session, &ids); + if (ret) { + dev_err(core->dev, + "%s: lsm confidence levels failed, rc = %d\n", + __func__, ret); + goto err_ret; + } + } else { + dev_dbg(core->dev, + "%s: no conf levels to set\n", + __func__); + } + +err_ret: + return ret; +} + +/* + * wcd_cpe_lsm_reg_snd_model: register the sound model for listen + * @session: session for which to register the sound model + * @detect_mode: detection mode, user dependent/independent + * @detect_failure: flag to indicate if failure detection is enabled + * + * The memory required for sound model should be pre-allocated on CPE + * before this function is invoked. + */ +static int wcd_cpe_lsm_reg_snd_model(void *core_handle, + struct cpe_lsm_session *session, + enum lsm_detection_mode detect_mode, + bool detect_failure) +{ + int ret = 0; + struct cmi_obm_msg obm_msg; + struct wcd_cpe_core *core = core_handle; + + ret = wcd_cpe_is_valid_lsm_session(core, session, + __func__); + if (ret) + return ret; + + ret = wcd_cpe_lsm_set_data(core_handle, session, + detect_mode, detect_failure); + if (ret) { + dev_err(core->dev, + "%s: fail to set lsm data, err = %d\n", + __func__, ret); + return ret; + } + + WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm"); + + ret = fill_cmi_header(&obm_msg.hdr, session->id, + CMI_CPE_LSM_SERVICE_ID, 0, 20, + CPE_LSM_SESSION_CMD_REGISTER_SOUND_MODEL, true); + if (ret) { + dev_err(core->dev, + "%s: Invalid parameters, rc = %d\n", + __func__, ret); + goto err_ret; + } + + obm_msg.pld.version = 0; + obm_msg.pld.size = session->snd_model_size; + obm_msg.pld.data_ptr.kvaddr = session->snd_model_data; + obm_msg.pld.mem_handle = session->lsm_mem_handle; + + ret = wcd_cpe_cmi_send_lsm_msg(core, session, &obm_msg); + if (ret) + dev_err(core->dev, + "%s: snd_model_register failed, %d\n", + __func__, ret); +err_ret: + WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm"); + return ret; +} + +/* + * wcd_cpe_lsm_dereg_snd_model: deregister the sound model for listen + * @core_handle: handle to cpe core + * @session: session for which to deregister the sound model + * + */ +static int wcd_cpe_lsm_dereg_snd_model(void *core_handle, + struct cpe_lsm_session *session) +{ + struct cmi_hdr cmd_dereg_snd_model; + struct wcd_cpe_core *core = core_handle; + int ret = 0; + + ret = wcd_cpe_is_valid_lsm_session(core, session, + __func__); + if (ret) + return ret; + + WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm"); + + memset(&cmd_dereg_snd_model, 0, sizeof(cmd_dereg_snd_model)); + if (fill_lsm_cmd_header_v0_inband(&cmd_dereg_snd_model, session->id, + 0, CPE_LSM_SESSION_CMD_DEREGISTER_SOUND_MODEL)) { + ret = -EINVAL; + goto end_ret; + } + + ret = wcd_cpe_cmi_send_lsm_msg(core, session, &cmd_dereg_snd_model); + if (ret) + dev_err(core->dev, + "%s: failed to send dereg_snd_model cmd\n", + __func__); +end_ret: + WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm"); + return ret; +} + +/* + * wcd_cpe_lsm_get_afe_out_port_id: get afe output port id + * @core_handle: handle to the CPE core + * @session: session for which port id needs to get + */ +static int wcd_cpe_lsm_get_afe_out_port_id(void *core_handle, + struct cpe_lsm_session *session) +{ + struct wcd_cpe_core *core = core_handle; + struct snd_soc_codec *codec; + int rc = 0; + + if (!core || !core->codec) { + pr_err("%s: Invalid handle to %s\n", + __func__, + (!core) ? "core" : "codec"); + rc = -EINVAL; + goto done; + } + + if (!session) { + dev_err(core->dev, "%s: Invalid session\n", + __func__); + rc = -EINVAL; + goto done; + } + + if (!core->cpe_cdc_cb || + !core->cpe_cdc_cb->get_afe_out_port_id) { + session->afe_out_port_id = WCD_CPE_AFE_OUT_PORT_2; + dev_dbg(core->dev, + "%s: callback not defined, default port_id = %d\n", + __func__, session->afe_out_port_id); + goto done; + } + + codec = core->codec; + rc = core->cpe_cdc_cb->get_afe_out_port_id(codec, + &session->afe_out_port_id); + if (rc) { + dev_err(core->dev, + "%s: failed to get port id, err = %d\n", + __func__, rc); + goto done; + } + dev_dbg(core->dev, "%s: port_id: %d\n", __func__, + session->afe_out_port_id); + +done: + return rc; +} + +/* + * wcd_cpe_cmd_lsm_start: send the start command to lsm + * @core_handle: handle to the CPE core + * @session: session for which start command to be sent + * + */ +static int wcd_cpe_cmd_lsm_start(void *core_handle, + struct cpe_lsm_session *session) +{ + struct cmi_hdr cmd_lsm_start; + struct wcd_cpe_core *core = core_handle; + int ret = 0; + + ret = wcd_cpe_is_valid_lsm_session(core, session, + __func__); + if (ret) + return ret; + + WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm"); + + memset(&cmd_lsm_start, 0, sizeof(struct cmi_hdr)); + if (fill_lsm_cmd_header_v0_inband(&cmd_lsm_start, session->id, 0, + CPE_LSM_SESSION_CMD_START)) { + ret = -EINVAL; + goto end_ret; + } + + ret = wcd_cpe_cmi_send_lsm_msg(core, session, &cmd_lsm_start); + if (ret) + dev_err(core->dev, "failed to send lsm_start cmd\n"); +end_ret: + WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm"); + return ret; +} + +/* + * wcd_cpe_cmd_lsm_stop: send the stop command for LSM service + * @core_handle: handle to the cpe core + * @session: session for which stop command to be sent + * + */ +static int wcd_cpe_cmd_lsm_stop(void *core_handle, + struct cpe_lsm_session *session) +{ + struct cmi_hdr cmd_lsm_stop; + struct wcd_cpe_core *core = core_handle; + int ret = 0; + + ret = wcd_cpe_is_valid_lsm_session(core, session, + __func__); + if (ret) + return ret; + + WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm"); + + memset(&cmd_lsm_stop, 0, sizeof(struct cmi_hdr)); + if (fill_lsm_cmd_header_v0_inband(&cmd_lsm_stop, session->id, 0, + CPE_LSM_SESSION_CMD_STOP)) { + ret = -EINVAL; + goto end_ret; + } + + ret = wcd_cpe_cmi_send_lsm_msg(core, session, &cmd_lsm_stop); + if (ret) + dev_err(core->dev, + "%s: failed to send lsm_stop cmd\n", + __func__); +end_ret: + WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm"); + return ret; + +} + +/* + * wcd_cpe_alloc_lsm_session: allocate a lsm session + * @core: handle to wcd_cpe_core + * @lsm_priv_d: lsm private data + */ +static struct cpe_lsm_session *wcd_cpe_alloc_lsm_session( + void *core_handle, void *client_data, + void (*event_cb)(void *, u8, u8, u8 *)) +{ + struct cpe_lsm_session *session; + int i, session_id = -1; + struct wcd_cpe_core *core = core_handle; + bool afe_register_service = false; + int ret = 0; + + /* + * Even if multiple listen sessions can be + * allocated, the AFE service registration + * should be done only once as CPE can only + * have one instance of AFE service. + * + * If this is the first session to be allocated, + * only then register the afe service. + */ + WCD_CPE_GRAB_LOCK(&core->session_lock, "session_lock"); + if (!wcd_cpe_lsm_session_active()) + afe_register_service = true; + + for (i = 1; i <= WCD_CPE_LSM_MAX_SESSIONS; i++) { + if (!lsm_sessions[i]) { + session_id = i; + break; + } + } + + if (session_id < 0) { + dev_err(core->dev, + "%s: max allowed sessions already allocated\n", + __func__); + WCD_CPE_REL_LOCK(&core->session_lock, "session_lock"); + return NULL; + } + + ret = wcd_cpe_vote(core, true); + if (ret) { + dev_err(core->dev, + "%s: Failed to enable cpe, err = %d\n", + __func__, ret); + WCD_CPE_REL_LOCK(&core->session_lock, "session_lock"); + return NULL; + } + + session = kzalloc(sizeof(struct cpe_lsm_session), GFP_KERNEL); + if (!session) + goto err_session_alloc; + + session->id = session_id; + session->event_cb = event_cb; + session->cmi_reg_handle = cmi_register(wcd_cpe_cmi_lsm_callback, + CMI_CPE_LSM_SERVICE_ID); + if (!session->cmi_reg_handle) { + dev_err(core->dev, + "%s: Failed to register LSM service with CMI\n", + __func__); + goto err_ret; + } + session->priv_d = client_data; + mutex_init(&session->lsm_lock); + if (afe_register_service) { + /* Register for AFE Service */ + core->cmi_afe_handle = cmi_register(wcd_cpe_cmi_afe_cb, + CMI_CPE_AFE_SERVICE_ID); + wcd_cpe_initialize_afe_port_data(); + if (!core->cmi_afe_handle) { + dev_err(core->dev, + "%s: Failed to register AFE service with CMI\n", + __func__); + goto err_afe_svc_reg; + } + + /* Once AFE service is registered, send the mode command */ + ret = wcd_cpe_afe_svc_cmd_mode(core, + AFE_SVC_EXPLICIT_PORT_START); + if (ret) + goto err_afe_mode_cmd; + } + + session->lsm_mem_handle = 0; + init_completion(&session->cmd_comp); + + lsm_sessions[session_id] = session; + + WCD_CPE_REL_LOCK(&core->session_lock, "session_lock"); + return session; + +err_afe_mode_cmd: + cmi_deregister(core->cmi_afe_handle); + +err_afe_svc_reg: + cmi_deregister(session->cmi_reg_handle); + mutex_destroy(&session->lsm_lock); + +err_ret: + kfree(session); + +err_session_alloc: + wcd_cpe_vote(core, false); + WCD_CPE_REL_LOCK(&core->session_lock, "session_lock"); + return NULL; +} + +/* + * wcd_cpe_lsm_config_lab_latency: send lab latency value + * @core: handle to wcd_cpe_core + * @session: lsm session + * @latency: the value of latency for lab setup in msec + */ +static int wcd_cpe_lsm_config_lab_latency( + struct wcd_cpe_core *core, + struct cpe_lsm_session *session, + u32 latency) +{ + int ret = 0, pld_size = CPE_PARAM_LSM_LAB_LATENCY_SIZE; + struct cpe_lsm_lab_latency_config cpe_lab_latency; + struct cpe_lsm_lab_config *lab_lat = &cpe_lab_latency.latency_cfg; + struct cpe_param_data *param_d = &lab_lat->param; + struct cpe_lsm_ids ids; + + if (fill_lsm_cmd_header_v0_inband(&cpe_lab_latency.hdr, session->id, + (u8) pld_size, CPE_LSM_SESSION_CMD_SET_PARAMS_V2)) { + pr_err("%s: Failed to create header\n", __func__); + return -EINVAL; + } + if (latency == 0x00 || latency > WCD_CPE_LAB_MAX_LATENCY) { + pr_err("%s: Invalid latency %u\n", + __func__, latency); + return -EINVAL; + } + + lab_lat->latency = latency; + lab_lat->minor_ver = 1; + ids.module_id = CPE_LSM_MODULE_ID_LAB; + ids.param_id = CPE_LSM_PARAM_ID_LAB_CONFIG; + wcd_cpe_set_param_data(param_d, &ids, + PARAM_SIZE_LSM_LATENCY_SIZE, + CPE_LSM_SESSION_CMD_SET_PARAMS_V2); + + pr_debug("%s: Module 0x%x Param 0x%x size %zu pld_size 0x%x\n", + __func__, lab_lat->param.module_id, + lab_lat->param.param_id, PARAM_SIZE_LSM_LATENCY_SIZE, + pld_size); + + WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm"); + ret = wcd_cpe_cmi_send_lsm_msg(core, session, &cpe_lab_latency); + if (ret != 0) + pr_err("%s: lsm_set_params failed, error = %d\n", + __func__, ret); + WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm"); + return ret; +} + +/* + * wcd_cpe_lsm_lab_control: enable/disable lab + * @core: handle to wcd_cpe_core + * @session: lsm session + * @enable: Indicates whether to enable / disable lab + */ +static int wcd_cpe_lsm_lab_control( + void *core_handle, + struct cpe_lsm_session *session, + bool enable) +{ + struct wcd_cpe_core *core = core_handle; + int ret = 0, pld_size = CPE_PARAM_SIZE_LSM_LAB_CONTROL; + struct cpe_lsm_control_lab cpe_lab_enable; + struct cpe_lsm_lab_enable *lab_enable = &cpe_lab_enable.lab_enable; + struct cpe_param_data *param_d = &lab_enable->param; + struct cpe_lsm_ids ids; + + pr_debug("%s: enter payload_size = %d Enable %d\n", + __func__, pld_size, enable); + + memset(&cpe_lab_enable, 0, sizeof(cpe_lab_enable)); + + if (fill_lsm_cmd_header_v0_inband(&cpe_lab_enable.hdr, session->id, + (u8) pld_size, CPE_LSM_SESSION_CMD_SET_PARAMS_V2)) { + return -EINVAL; + } + if (enable == true) + lab_enable->enable = 1; + else + lab_enable->enable = 0; + + ids.module_id = CPE_LSM_MODULE_ID_LAB; + ids.param_id = CPE_LSM_PARAM_ID_LAB_ENABLE; + wcd_cpe_set_param_data(param_d, &ids, + PARAM_SIZE_LSM_CONTROL_SIZE, + CPE_LSM_SESSION_CMD_SET_PARAMS_V2); + + pr_debug("%s: Module 0x%x, Param 0x%x size %zu pld_size 0x%x\n", + __func__, lab_enable->param.module_id, + lab_enable->param.param_id, PARAM_SIZE_LSM_CONTROL_SIZE, + pld_size); + + WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm"); + ret = wcd_cpe_cmi_send_lsm_msg(core, session, &cpe_lab_enable); + if (ret != 0) { + pr_err("%s: lsm_set_params failed, error = %d\n", + __func__, ret); + WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm"); + goto done; + } + WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm"); + + if (lab_enable->enable) + ret = wcd_cpe_lsm_config_lab_latency(core, session, + WCD_CPE_LAB_MAX_LATENCY); +done: + return ret; +} + +/* + * wcd_cpe_lsm_eob: stop lab + * @core: handle to wcd_cpe_core + * @session: lsm session to be deallocated + */ +static int wcd_cpe_lsm_eob( + struct wcd_cpe_core *core, + struct cpe_lsm_session *session) +{ + int ret = 0; + struct cmi_hdr lab_eob; + + if (fill_lsm_cmd_header_v0_inband(&lab_eob, session->id, + 0, CPE_LSM_SESSION_CMD_EOB)) { + return -EINVAL; + } + + WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm"); + ret = wcd_cpe_cmi_send_lsm_msg(core, session, &lab_eob); + if (ret != 0) + pr_err("%s: lsm_set_params failed\n", __func__); + WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm"); + + return ret; +} + +/* + * wcd_cpe_dealloc_lsm_session: deallocate lsm session + * @core: handle to wcd_cpe_core + * @session: lsm session to be deallocated + */ +static int wcd_cpe_dealloc_lsm_session(void *core_handle, + struct cpe_lsm_session *session) +{ + struct wcd_cpe_core *core = core_handle; + int ret = 0; + + WCD_CPE_GRAB_LOCK(&core->session_lock, "session_lock"); + if (!session) { + dev_err(core->dev, + "%s: Invalid lsm session\n", __func__); + WCD_CPE_REL_LOCK(&core->session_lock, "session_lock"); + return -EINVAL; + } + + dev_dbg(core->dev, "%s: session %d being deallocated\n", + __func__, session->id); + if (session->id > WCD_CPE_LSM_MAX_SESSIONS) { + dev_err(core->dev, + "%s: Wrong session id %d max allowed = %d\n", + __func__, session->id, + WCD_CPE_LSM_MAX_SESSIONS); + WCD_CPE_REL_LOCK(&core->session_lock, "session_lock"); + return -EINVAL; + } + + cmi_deregister(session->cmi_reg_handle); + mutex_destroy(&session->lsm_lock); + lsm_sessions[session->id] = NULL; + kfree(session); + + if (!wcd_cpe_lsm_session_active()) { + cmi_deregister(core->cmi_afe_handle); + core->cmi_afe_handle = NULL; + wcd_cpe_deinitialize_afe_port_data(); + } + + ret = wcd_cpe_vote(core, false); + if (ret) + dev_dbg(core->dev, + "%s: Failed to un-vote cpe, err = %d\n", + __func__, ret); + + WCD_CPE_REL_LOCK(&core->session_lock, "session_lock"); + return ret; +} + +static int wcd_cpe_lab_ch_setup(void *core_handle, + struct cpe_lsm_session *session, + enum wcd_cpe_event event) +{ + struct wcd_cpe_core *core = core_handle; + struct snd_soc_codec *codec; + int rc = 0; + u8 cpe_intr_bits; + + if (!core || !core->codec) { + pr_err("%s: Invalid handle to %s\n", + __func__, + (!core) ? "core" : "codec"); + rc = EINVAL; + goto done; + } + + if (!core->cpe_cdc_cb || + !core->cpe_cdc_cb->cdc_ext_clk || + !core->cpe_cdc_cb->lab_cdc_ch_ctl) { + dev_err(core->dev, + "%s: Invalid codec callbacks\n", + __func__); + rc = -EINVAL; + goto done; + } + + codec = core->codec; + dev_dbg(core->dev, + "%s: event = 0x%x\n", + __func__, event); + + switch (event) { + case WCD_CPE_PRE_ENABLE: + rc = core->cpe_cdc_cb->cdc_ext_clk(codec, true, false); + if (rc) { + dev_err(core->dev, + "%s: failed to enable cdc clk, err = %d\n", + __func__, rc); + goto done; + } + + rc = core->cpe_cdc_cb->lab_cdc_ch_ctl(codec, + true); + if (rc) { + dev_err(core->dev, + "%s: failed to enable cdc port, err = %d\n", + __func__, rc); + rc = core->cpe_cdc_cb->cdc_ext_clk(codec, false, false); + goto done; + } + + break; + + case WCD_CPE_POST_ENABLE: + rc = cpe_svc_toggle_lab(core->cpe_handle, true); + if (rc) + dev_err(core->dev, + "%s: Failed to enable lab\n", __func__); + break; + + case WCD_CPE_PRE_DISABLE: + /* + * Mask the non-fatal interrupts in CPE as they will + * be generated during lab teardown and may flood. + */ + cpe_intr_bits = ~(core->irq_info.cpe_fatal_irqs & 0xFF); + if (CPE_ERR_IRQ_CB(core)) + core->cpe_cdc_cb->cpe_err_irq_control( + core->codec, + CPE_ERR_IRQ_MASK, + &cpe_intr_bits); + + rc = core->cpe_cdc_cb->lab_cdc_ch_ctl(codec, + false); + if (rc) + dev_err(core->dev, + "%s: failed to disable cdc port, err = %d\n", + __func__, rc); + break; + + case WCD_CPE_POST_DISABLE: + rc = wcd_cpe_lsm_eob(core, session); + if (rc) + dev_err(core->dev, + "%s: eob send failed, err = %d\n", + __func__, rc); + + /* Continue teardown even if eob failed */ + rc = cpe_svc_toggle_lab(core->cpe_handle, false); + if (rc) + dev_err(core->dev, + "%s: Failed to disable lab\n", __func__); + + /* Continue with disabling even if toggle lab fails */ + rc = core->cpe_cdc_cb->cdc_ext_clk(codec, false, false); + if (rc) + dev_err(core->dev, + "%s: failed to disable cdc clk, err = %d\n", + __func__, rc); + + /* Unmask non-fatal CPE interrupts */ + cpe_intr_bits = ~(core->irq_info.cpe_fatal_irqs & 0xFF); + if (CPE_ERR_IRQ_CB(core)) + core->cpe_cdc_cb->cpe_err_irq_control( + core->codec, + CPE_ERR_IRQ_UNMASK, + &cpe_intr_bits); + break; + + default: + dev_err(core->dev, + "%s: Invalid event 0x%x\n", + __func__, event); + rc = -EINVAL; + break; + } + +done: + return rc; +} + +static int wcd_cpe_lsm_set_fmt_cfg(void *core_handle, + struct cpe_lsm_session *session) +{ + int ret; + struct cpe_lsm_output_format_cfg out_fmt_cfg; + struct wcd_cpe_core *core = core_handle; + + ret = wcd_cpe_is_valid_lsm_session(core, session, __func__); + if (ret) + goto done; + + WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm"); + + memset(&out_fmt_cfg, 0, sizeof(out_fmt_cfg)); + if (fill_lsm_cmd_header_v0_inband(&out_fmt_cfg.hdr, + session->id, OUT_FMT_CFG_CMD_PAYLOAD_SIZE, + CPE_LSM_SESSION_CMD_TX_BUFF_OUTPUT_CONFIG)) { + ret = -EINVAL; + goto err_ret; + } + + out_fmt_cfg.format = session->out_fmt_cfg.format; + out_fmt_cfg.packing = session->out_fmt_cfg.pack_mode; + out_fmt_cfg.data_path_events = session->out_fmt_cfg.data_path_events; + + ret = wcd_cpe_cmi_send_lsm_msg(core, session, &out_fmt_cfg); + if (ret) + dev_err(core->dev, + "%s: lsm_set_output_format_cfg failed, err = %d\n", + __func__, ret); + +err_ret: + WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm"); +done: + return ret; +} + +static void wcd_cpe_snd_model_offset(void *core_handle, + struct cpe_lsm_session *session, size_t *offset) +{ + *offset = sizeof(struct cpe_param_data); +} + +static int wcd_cpe_lsm_set_media_fmt_params(void *core_handle, + struct cpe_lsm_session *session, + struct lsm_hw_params *param) +{ + struct cpe_lsm_media_fmt_param media_fmt; + struct cmi_hdr *msg_hdr = &media_fmt.hdr; + struct wcd_cpe_core *core = core_handle; + struct cpe_param_data *param_d = &media_fmt.param; + struct cpe_lsm_ids ids; + int ret; + + memset(&media_fmt, 0, sizeof(media_fmt)); + if (fill_lsm_cmd_header_v0_inband(msg_hdr, + session->id, + CPE_MEDIA_FMT_PLD_SIZE, + CPE_LSM_SESSION_CMD_SET_PARAMS_V2)) { + ret = -EINVAL; + goto done; + } + + memset(&ids, 0, sizeof(ids)); + ids.module_id = CPE_LSM_MODULE_FRAMEWORK; + ids.param_id = CPE_LSM_PARAM_ID_MEDIA_FMT; + + wcd_cpe_set_param_data(param_d, &ids, CPE_MEDIA_FMT_PARAM_SIZE, + CPE_LSM_SESSION_CMD_SET_PARAMS_V2); + + media_fmt.minor_version = 1; + media_fmt.sample_rate = param->sample_rate; + media_fmt.num_channels = param->num_chs; + media_fmt.bit_width = param->bit_width; + + WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm"); + ret = wcd_cpe_cmi_send_lsm_msg(core, session, &media_fmt); + if (ret) + dev_err(core->dev, + "%s: Set_param(media_format) failed, err=%d\n", + __func__, ret); + WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm"); +done: + return ret; +} + +static int wcd_cpe_lsm_set_port(void *core_handle, + struct cpe_lsm_session *session, void *data) +{ + u32 port_id; + int ret; + struct cpe_lsm_ids ids; + struct wcd_cpe_core *core = core_handle; + + ret = wcd_cpe_is_valid_lsm_session(core, session, __func__); + if (ret) + goto done; + + if (!data) { + dev_err(core->dev, "%s: data is NULL\n", __func__); + ret = -EINVAL; + goto done; + } + port_id = *(u32 *)data; + dev_dbg(core->dev, "%s: port_id: %d\n", __func__, port_id); + + memset(&ids, 0, sizeof(ids)); + ids.module_id = LSM_MODULE_ID_FRAMEWORK; + ids.param_id = LSM_PARAM_ID_CONNECT_TO_PORT; + + ret = wcd_cpe_send_param_connectport(core, session, NULL, + &ids, port_id); + if (ret) + dev_err(core->dev, + "%s: send_param_connectport failed, err %d\n", + __func__, ret); +done: + return ret; +} + +/* + * wcd_cpe_get_lsm_ops: register lsm driver to codec + * @lsm_ops: structure with lsm callbacks + * @codec: codec to which this lsm driver is registered to + */ +int wcd_cpe_get_lsm_ops(struct wcd_cpe_lsm_ops *lsm_ops) +{ + lsm_ops->lsm_alloc_session = wcd_cpe_alloc_lsm_session; + lsm_ops->lsm_dealloc_session = wcd_cpe_dealloc_lsm_session; + lsm_ops->lsm_open_tx = wcd_cpe_cmd_lsm_open_tx; + lsm_ops->lsm_close_tx = wcd_cpe_cmd_lsm_close_tx; + lsm_ops->lsm_shmem_alloc = wcd_cpe_cmd_lsm_shmem_alloc; + lsm_ops->lsm_shmem_dealloc = wcd_cpe_cmd_lsm_shmem_dealloc; + lsm_ops->lsm_register_snd_model = wcd_cpe_lsm_reg_snd_model; + lsm_ops->lsm_deregister_snd_model = wcd_cpe_lsm_dereg_snd_model; + lsm_ops->lsm_get_afe_out_port_id = wcd_cpe_lsm_get_afe_out_port_id; + lsm_ops->lsm_start = wcd_cpe_cmd_lsm_start; + lsm_ops->lsm_stop = wcd_cpe_cmd_lsm_stop; + lsm_ops->lsm_lab_control = wcd_cpe_lsm_lab_control; + lsm_ops->lab_ch_setup = wcd_cpe_lab_ch_setup; + lsm_ops->lsm_set_data = wcd_cpe_lsm_set_data; + lsm_ops->lsm_set_fmt_cfg = wcd_cpe_lsm_set_fmt_cfg; + lsm_ops->lsm_set_one_param = wcd_cpe_set_one_param; + lsm_ops->lsm_get_snd_model_offset = wcd_cpe_snd_model_offset; + lsm_ops->lsm_set_media_fmt_params = wcd_cpe_lsm_set_media_fmt_params; + lsm_ops->lsm_set_port = wcd_cpe_lsm_set_port; + + return 0; +} +EXPORT_SYMBOL(wcd_cpe_get_lsm_ops); + +static int fill_afe_cmd_header(struct cmi_hdr *hdr, u8 port_id, + u16 opcode, u8 pld_size, + bool obm_flag) +{ + CMI_HDR_SET_SESSION(hdr, port_id); + CMI_HDR_SET_SERVICE(hdr, CMI_CPE_AFE_SERVICE_ID); + + CMI_HDR_SET_PAYLOAD_SIZE(hdr, pld_size); + + hdr->opcode = opcode; + + if (obm_flag) + CMI_HDR_SET_OBM(hdr, CMI_OBM_FLAG_OUT_BAND); + else + CMI_HDR_SET_OBM(hdr, CMI_OBM_FLAG_IN_BAND); + + return 0; +} + +/* + * wcd_cpe_cmi_send_afe_msg: send message to AFE service + * @core: wcd cpe core handle + * @port_cfg: configuration data for the afe port + * for which this message is to be sent + * @message: actual message with header and payload + * + * Port specific lock needs to be acquired before this + * function can be invoked + */ +static int wcd_cpe_cmi_send_afe_msg( + struct wcd_cpe_core *core, + struct wcd_cmi_afe_port_data *port_d, + void *message) +{ + int ret = 0; + struct cmi_hdr *hdr = message; + + pr_debug("%s: sending message with opcode 0x%x\n", + __func__, hdr->opcode); + + if (unlikely(!wcd_cpe_is_online_state(core))) { + dev_err(core->dev, "%s: CPE offline\n", __func__); + return 0; + } + + if (CMI_HDR_GET_OBM_FLAG(hdr)) + wcd_cpe_bus_vote_max_bw(core, true); + + ret = cmi_send_msg(message); + if (ret) { + pr_err("%s: cmd 0x%x send failed, err = %d\n", + __func__, hdr->opcode, ret); + goto rel_bus_vote; + } + + ret = wait_for_completion_timeout(&port_d->afe_cmd_complete, + CMI_CMD_TIMEOUT); + if (ret > 0) { + pr_debug("%s: command 0x%x, received response 0x%x\n", + __func__, hdr->opcode, port_d->cmd_result); + if (port_d->cmd_result == CMI_SHMEM_ALLOC_FAILED) + port_d->cmd_result = CPE_ENOMEMORY; + if (port_d->cmd_result > 0) + pr_err("%s: CPE returned error[%s]\n", + __func__, cpe_err_get_err_str( + port_d->cmd_result)); + ret = cpe_err_get_lnx_err_code(port_d->cmd_result); + goto rel_bus_vote; + } else { + pr_err("%s: command 0x%x send timed out\n", + __func__, hdr->opcode); + ret = -ETIMEDOUT; + goto rel_bus_vote; + } + +rel_bus_vote: + reinit_completion(&port_d->afe_cmd_complete); + + if (CMI_HDR_GET_OBM_FLAG(hdr)) + wcd_cpe_bus_vote_max_bw(core, false); + + return ret; +} + + + +/* + * wcd_cpe_afe_shmem_alloc: allocate the cpe memory for afe service + * @core: handle to cpe core + * @port_cfg: configuration data for the port which needs + * memory to be allocated on CPE + * @size: size of the memory to be allocated + */ +static int wcd_cpe_afe_shmem_alloc( + struct wcd_cpe_core *core, + struct wcd_cmi_afe_port_data *port_d, + u32 size) +{ + struct cpe_cmd_shmem_alloc cmd_shmem_alloc; + int ret = 0; + + pr_debug("%s: enter: size = %d\n", __func__, size); + + memset(&cmd_shmem_alloc, 0, sizeof(cmd_shmem_alloc)); + if (fill_afe_cmd_header(&cmd_shmem_alloc.hdr, port_d->port_id, + CPE_AFE_PORT_CMD_SHARED_MEM_ALLOC, + SHMEM_ALLOC_CMD_PLD_SIZE, false)) { + ret = -EINVAL; + goto end_ret; + } + + cmd_shmem_alloc.size = size; + + ret = wcd_cpe_cmi_send_afe_msg(core, port_d, &cmd_shmem_alloc); + if (ret) { + pr_err("%s: afe_shmem_alloc fail,ret = %d\n", + __func__, ret); + goto end_ret; + } + + pr_debug("%s: completed %s, mem_handle = 0x%x\n", + __func__, "CPE_AFE_CMD_SHARED_MEM_ALLOC", + port_d->mem_handle); + +end_ret: + return ret; +} + +/* + * wcd_cpe_afe_shmem_dealloc: deallocate the cpe memory for + * afe service + * @core: handle to cpe core + * @port_d: configuration data for the port which needs + * memory to be deallocated on CPE + * The memory handle to be de-allocated is saved in the + * port configuration data + */ +static int wcd_cpe_afe_shmem_dealloc( + struct wcd_cpe_core *core, + struct wcd_cmi_afe_port_data *port_d) +{ + struct cpe_cmd_shmem_dealloc cmd_dealloc; + int ret = 0; + + pr_debug("%s: enter, port_id = %d\n", + __func__, port_d->port_id); + + memset(&cmd_dealloc, 0, sizeof(cmd_dealloc)); + if (fill_afe_cmd_header(&cmd_dealloc.hdr, port_d->port_id, + CPE_AFE_PORT_CMD_SHARED_MEM_DEALLOC, + SHMEM_DEALLOC_CMD_PLD_SIZE, false)) { + ret = -EINVAL; + goto end_ret; + } + + cmd_dealloc.addr = port_d->mem_handle; + ret = wcd_cpe_cmi_send_afe_msg(core, port_d, &cmd_dealloc); + if (ret) { + pr_err("failed to send shmem_dealloc cmd\n"); + goto end_ret; + } + memset(&port_d->mem_handle, 0, + sizeof(port_d->mem_handle)); + +end_ret: + return ret; +} + +/* + * wcd_cpe_send_afe_cal: send the acdb calibration to AFE port + * @core: handle to cpe core + * @port_d: configuration data for the port for which the + * calibration needs to be appplied + */ +static int wcd_cpe_send_afe_cal(void *core_handle, + struct wcd_cmi_afe_port_data *port_d) +{ + + struct cal_block_data *afe_cal = NULL; + struct wcd_cpe_core *core = core_handle; + struct cmi_obm_msg obm_msg; + void *inb_msg = NULL; + void *msg; + int rc = 0; + bool is_obm_msg; + + if (core->cal_data[WCD_CPE_LSM_CAL_AFE] == NULL) { + pr_err("%s: LSM cal not allocated!\n", + __func__); + rc = -EINVAL; + goto rel_cal_mutex; + } + + mutex_lock(&core->cal_data[WCD_CPE_LSM_CAL_AFE]->lock); + afe_cal = cal_utils_get_only_cal_block( + core->cal_data[WCD_CPE_LSM_CAL_AFE]); + if (!afe_cal) { + pr_err("%s: failed to get afe cal block\n", + __func__); + rc = -EINVAL; + goto rel_cal_mutex; + } + + if (afe_cal->cal_data.size == 0) { + dev_dbg(core->dev, "%s: No AFE cal to send\n", + __func__); + rc = 0; + goto rel_cal_mutex; + } + + is_obm_msg = (afe_cal->cal_data.size > + CMI_INBAND_MESSAGE_SIZE) ? true : false; + + if (is_obm_msg) { + struct cmi_hdr *hdr = &(obm_msg.hdr); + struct cmi_obm *pld = &(obm_msg.pld); + + rc = wcd_cpe_afe_shmem_alloc(core, port_d, + afe_cal->cal_data.size); + if (rc) { + dev_err(core->dev, + "%s: AFE shmem alloc fail %d\n", + __func__, rc); + goto rel_cal_mutex; + } + + rc = fill_afe_cmd_header(hdr, port_d->port_id, + CPE_AFE_CMD_SET_PARAM, + CPE_AFE_PARAM_PAYLOAD_SIZE, + true); + if (rc) { + dev_err(core->dev, + "%s: invalid params for header, err = %d\n", + __func__, rc); + wcd_cpe_afe_shmem_dealloc(core, port_d); + goto rel_cal_mutex; + } + + pld->version = 0; + pld->size = afe_cal->cal_data.size; + pld->data_ptr.kvaddr = afe_cal->cal_data.kvaddr; + pld->mem_handle = port_d->mem_handle; + msg = &obm_msg; + + } else { + u8 *msg_pld; + struct cmi_hdr *hdr; + + inb_msg = kzalloc(sizeof(struct cmi_hdr) + + afe_cal->cal_data.size, + GFP_KERNEL); + if (!inb_msg) { + dev_err(core->dev, + "%s: no memory for afe cal inband\n", + __func__); + rc = -ENOMEM; + goto rel_cal_mutex; + } + + hdr = (struct cmi_hdr *) inb_msg; + + rc = fill_afe_cmd_header(hdr, port_d->port_id, + CPE_AFE_CMD_SET_PARAM, + CPE_AFE_PARAM_PAYLOAD_SIZE, + false); + if (rc) { + dev_err(core->dev, + "%s: invalid params for header, err = %d\n", + __func__, rc); + kfree(inb_msg); + inb_msg = NULL; + goto rel_cal_mutex; + } + + msg_pld = ((u8 *) inb_msg) + sizeof(struct cmi_hdr); + memcpy(msg_pld, afe_cal->cal_data.kvaddr, + afe_cal->cal_data.size); + + msg = inb_msg; + } + + rc = wcd_cpe_cmi_send_afe_msg(core, port_d, msg); + if (rc) + pr_err("%s: afe cal for listen failed, rc = %d\n", + __func__, rc); + + if (is_obm_msg) { + wcd_cpe_afe_shmem_dealloc(core, port_d); + port_d->mem_handle = 0; + } else { + kfree(inb_msg); + inb_msg = NULL; + } + +rel_cal_mutex: + mutex_unlock(&core->cal_data[WCD_CPE_LSM_CAL_AFE]->lock); + return rc; +} + +/* + * wcd_cpe_is_valid_port: check validity of afe port id + * @core: handle to core to check for validity + * @afe_cfg: client provided afe configuration + * @func: function name invoking this validity check, + * used for logging purpose only. + */ +static int wcd_cpe_is_valid_port(struct wcd_cpe_core *core, + struct wcd_cpe_afe_port_cfg *afe_cfg, + const char *func) +{ + if (unlikely(IS_ERR_OR_NULL(core))) { + pr_err("%s: Invalid core handle\n", func); + return -EINVAL; + } + + if (afe_cfg->port_id > WCD_CPE_AFE_MAX_PORTS) { + dev_err(core->dev, + "%s: invalid afe port (%u)\n", + func, afe_cfg->port_id); + return -EINVAL; + } + + dev_dbg(core->dev, + "%s: port_id = %u\n", + func, afe_cfg->port_id); + + return 0; +} + +static int wcd_cpe_afe_svc_cmd_mode(void *core_handle, + u8 mode) +{ + struct cpe_afe_svc_cmd_mode afe_mode; + struct wcd_cpe_core *core = core_handle; + struct wcd_cmi_afe_port_data *afe_port_d; + int ret; + + afe_port_d = &afe_ports[0]; + /* + * AFE SVC mode command is for the service and not port + * specific, hence use AFE port as 0 so the command will + * be applied to all AFE ports on CPE. + */ + afe_port_d->port_id = 0; + + WCD_CPE_GRAB_LOCK(&afe_port_d->afe_lock, "afe"); + memset(&afe_mode, 0, sizeof(afe_mode)); + if (fill_afe_cmd_header(&afe_mode.hdr, afe_port_d->port_id, + CPE_AFE_SVC_CMD_LAB_MODE, + CPE_AFE_CMD_MODE_PAYLOAD_SIZE, + false)) { + ret = -EINVAL; + goto err_ret; + } + + afe_mode.mode = mode; + + ret = wcd_cpe_cmi_send_afe_msg(core, afe_port_d, &afe_mode); + if (ret) + dev_err(core->dev, + "%s: afe_svc_mode cmd failed, err = %d\n", + __func__, ret); + +err_ret: + WCD_CPE_REL_LOCK(&afe_port_d->afe_lock, "afe"); + return ret; +} + +static int wcd_cpe_afe_cmd_port_cfg(void *core_handle, + struct wcd_cpe_afe_port_cfg *afe_cfg) +{ + struct cpe_afe_cmd_port_cfg port_cfg_cmd; + struct wcd_cpe_core *core = core_handle; + struct wcd_cmi_afe_port_data *afe_port_d; + int ret; + + ret = wcd_cpe_is_valid_port(core, afe_cfg, __func__); + if (ret) + goto done; + + afe_port_d = &afe_ports[afe_cfg->port_id]; + afe_port_d->port_id = afe_cfg->port_id; + + WCD_CPE_GRAB_LOCK(&afe_port_d->afe_lock, "afe"); + memset(&port_cfg_cmd, 0, sizeof(port_cfg_cmd)); + if (fill_afe_cmd_header(&port_cfg_cmd.hdr, + afe_cfg->port_id, + CPE_AFE_PORT_CMD_GENERIC_CONFIG, + CPE_AFE_CMD_PORT_CFG_PAYLOAD_SIZE, + false)) { + ret = -EINVAL; + goto err_ret; + } + + port_cfg_cmd.bit_width = afe_cfg->bit_width; + port_cfg_cmd.num_channels = afe_cfg->num_channels; + port_cfg_cmd.sample_rate = afe_cfg->sample_rate; + + if (afe_port_d->port_id == CPE_AFE_PORT_3_TX) + port_cfg_cmd.buffer_size = WCD_CPE_EC_PP_BUF_SIZE; + else + port_cfg_cmd.buffer_size = AFE_OUT_BUF_SIZE(afe_cfg->bit_width, + afe_cfg->sample_rate); + + ret = wcd_cpe_cmi_send_afe_msg(core, afe_port_d, &port_cfg_cmd); + if (ret) + dev_err(core->dev, + "%s: afe_port_config failed, err = %d\n", + __func__, ret); + +err_ret: + WCD_CPE_REL_LOCK(&afe_port_d->afe_lock, "afe"); +done: + return ret; +} + +/* + * wcd_cpe_afe_set_params: set the parameters for afe port + * @afe_cfg: configuration data for the port for which the + * parameters are to be set + */ +static int wcd_cpe_afe_set_params(void *core_handle, + struct wcd_cpe_afe_port_cfg *afe_cfg, bool afe_mad_ctl) +{ + struct cpe_afe_params afe_params; + struct cpe_afe_hw_mad_ctrl *hw_mad_ctrl = &afe_params.hw_mad_ctrl; + struct cpe_afe_port_cfg *port_cfg = &afe_params.port_cfg; + struct wcd_cpe_core *core = core_handle; + struct wcd_cmi_afe_port_data *afe_port_d; + int ret = 0, pld_size = 0; + + ret = wcd_cpe_is_valid_port(core, afe_cfg, __func__); + if (ret) + return ret; + + afe_port_d = &afe_ports[afe_cfg->port_id]; + afe_port_d->port_id = afe_cfg->port_id; + + WCD_CPE_GRAB_LOCK(&afe_port_d->afe_lock, "afe"); + + ret = wcd_cpe_send_afe_cal(core, afe_port_d); + if (ret) { + dev_err(core->dev, + "%s: afe acdb cal send failed, err = %d\n", + __func__, ret); + goto err_ret; + } + + pld_size = CPE_AFE_PARAM_PAYLOAD_SIZE; + memset(&afe_params, 0, sizeof(afe_params)); + + if (fill_afe_cmd_header(&afe_params.hdr, + afe_cfg->port_id, + CPE_AFE_CMD_SET_PARAM, + (u8) pld_size, false)) { + ret = -EINVAL; + goto err_ret; + } + + hw_mad_ctrl->param.module_id = CPE_AFE_MODULE_HW_MAD; + hw_mad_ctrl->param.param_id = CPE_AFE_PARAM_ID_HW_MAD_CTL; + hw_mad_ctrl->param.p_size.sr.param_size = PARAM_SIZE_AFE_HW_MAD_CTRL; + hw_mad_ctrl->param.p_size.sr.reserved = 0; + hw_mad_ctrl->minor_version = 1; + hw_mad_ctrl->mad_type = MAD_TYPE_AUDIO; + hw_mad_ctrl->mad_enable = afe_mad_ctl; + + port_cfg->param.module_id = CPE_AFE_MODULE_AUDIO_DEV_INTERFACE; + port_cfg->param.param_id = CPE_AFE_PARAM_ID_GENERIC_PORT_CONFIG; + port_cfg->param.p_size.sr.param_size = PARAM_SIZE_AFE_PORT_CFG; + port_cfg->param.p_size.sr.reserved = 0; + port_cfg->minor_version = 1; + port_cfg->bit_width = afe_cfg->bit_width; + port_cfg->num_channels = afe_cfg->num_channels; + port_cfg->sample_rate = afe_cfg->sample_rate; + + ret = wcd_cpe_cmi_send_afe_msg(core, afe_port_d, &afe_params); + if (ret) + dev_err(core->dev, + "%s: afe_port_config failed, err = %d\n", + __func__, ret); +err_ret: + WCD_CPE_REL_LOCK(&afe_port_d->afe_lock, "afe"); + return ret; +} + +/* + * wcd_cpe_afe_port_start: send the start command to afe service + * @core_handle: handle to the cpe core + * @port_cfg: configuration data for the afe port which needs + * to be started. + */ +static int wcd_cpe_afe_port_start(void *core_handle, + struct wcd_cpe_afe_port_cfg *port_cfg) +{ + + struct cmi_hdr hdr; + struct wcd_cpe_core *core = core_handle; + struct wcd_cmi_afe_port_data *afe_port_d; + int ret = 0; + + ret = wcd_cpe_is_valid_port(core, port_cfg, __func__); + if (ret) + return ret; + + afe_port_d = &afe_ports[port_cfg->port_id]; + afe_port_d->port_id = port_cfg->port_id; + + WCD_CPE_GRAB_LOCK(&afe_port_d->afe_lock, "afe"); + + memset(&hdr, 0, sizeof(struct cmi_hdr)); + fill_afe_cmd_header(&hdr, port_cfg->port_id, + CPE_AFE_PORT_CMD_START, + 0, false); + ret = wcd_cpe_cmi_send_afe_msg(core, afe_port_d, &hdr); + if (ret) + dev_err(core->dev, + "%s: afe_port_start cmd failed, err = %d\n", + __func__, ret); + WCD_CPE_REL_LOCK(&afe_port_d->afe_lock, "afe"); + return ret; +} + +/* + * wcd_cpe_afe_port_stop: send stop command to afe service + * @core_handle: handle to the cpe core + * @port_cfg: configuration data for the afe port which needs + * to be stopped. + */ +static int wcd_cpe_afe_port_stop(void *core_handle, + struct wcd_cpe_afe_port_cfg *port_cfg) +{ + struct cmi_hdr hdr; + struct wcd_cpe_core *core = core_handle; + struct wcd_cmi_afe_port_data *afe_port_d; + int ret = 0; + + ret = wcd_cpe_is_valid_port(core, port_cfg, __func__); + if (ret) + return ret; + + afe_port_d = &afe_ports[port_cfg->port_id]; + afe_port_d->port_id = port_cfg->port_id; + + WCD_CPE_GRAB_LOCK(&afe_port_d->afe_lock, "afe"); + + memset(&hdr, 0, sizeof(hdr)); + fill_afe_cmd_header(&hdr, port_cfg->port_id, + CPE_AFE_PORT_CMD_STOP, + 0, false); + ret = wcd_cpe_cmi_send_afe_msg(core, afe_port_d, &hdr); + if (ret) + dev_err(core->dev, + "%s: afe_stop cmd failed, err = %d\n", + __func__, ret); + + WCD_CPE_REL_LOCK(&afe_port_d->afe_lock, "afe"); + return ret; +} + +/* + * wcd_cpe_afe_port_suspend: send suspend command to afe service + * @core_handle: handle to the cpe core + * @port_cfg: configuration data for the afe port which needs + * to be suspended. + */ +static int wcd_cpe_afe_port_suspend(void *core_handle, + struct wcd_cpe_afe_port_cfg *port_cfg) +{ + struct cmi_hdr hdr; + struct wcd_cpe_core *core = core_handle; + struct wcd_cmi_afe_port_data *afe_port_d; + int ret = 0; + + ret = wcd_cpe_is_valid_port(core, port_cfg, __func__); + if (ret) + return ret; + + afe_port_d = &afe_ports[port_cfg->port_id]; + afe_port_d->port_id = port_cfg->port_id; + + WCD_CPE_GRAB_LOCK(&afe_port_d->afe_lock, "afe"); + + memset(&hdr, 0, sizeof(struct cmi_hdr)); + fill_afe_cmd_header(&hdr, port_cfg->port_id, + CPE_AFE_PORT_CMD_SUSPEND, + 0, false); + ret = wcd_cpe_cmi_send_afe_msg(core, afe_port_d, &hdr); + if (ret) + dev_err(core->dev, + "%s: afe_suspend cmd failed, err = %d\n", + __func__, ret); + WCD_CPE_REL_LOCK(&afe_port_d->afe_lock, "afe"); + return ret; +} + +/* + * wcd_cpe_afe_port_resume: send the resume command to afe service + * @core_handle: handle to the cpe core + * @port_cfg: configuration data for the afe port which needs + * to be resumed. + */ +static int wcd_cpe_afe_port_resume(void *core_handle, + struct wcd_cpe_afe_port_cfg *port_cfg) +{ + struct cmi_hdr hdr; + struct wcd_cpe_core *core = core_handle; + struct wcd_cmi_afe_port_data *afe_port_d; + int ret = 0; + + ret = wcd_cpe_is_valid_port(core, port_cfg, __func__); + if (ret) + return ret; + + afe_port_d = &afe_ports[port_cfg->port_id]; + afe_port_d->port_id = port_cfg->port_id; + + WCD_CPE_GRAB_LOCK(&afe_port_d->afe_lock, "afe"); + + memset(&hdr, 0, sizeof(hdr)); + fill_afe_cmd_header(&hdr, port_cfg->port_id, + CPE_AFE_PORT_CMD_RESUME, + 0, false); + ret = wcd_cpe_cmi_send_afe_msg(core, afe_port_d, &hdr); + if (ret) + dev_err(core->dev, + "%s: afe_resume cmd failed, err = %d\n", + __func__, ret); + WCD_CPE_REL_LOCK(&afe_port_d->afe_lock, "afe"); + return ret; + +} + +/* + * wcd_cpe_register_afe_driver: register lsm driver to codec + * @cpe_ops: structure with lsm callbacks + * @codec: codec to which this lsm driver is registered to + */ +int wcd_cpe_get_afe_ops(struct wcd_cpe_afe_ops *afe_ops) +{ + afe_ops->afe_set_params = wcd_cpe_afe_set_params; + afe_ops->afe_port_start = wcd_cpe_afe_port_start; + afe_ops->afe_port_stop = wcd_cpe_afe_port_stop; + afe_ops->afe_port_suspend = wcd_cpe_afe_port_suspend; + afe_ops->afe_port_resume = wcd_cpe_afe_port_resume; + afe_ops->afe_port_cmd_cfg = wcd_cpe_afe_cmd_port_cfg; + + return 0; +} +EXPORT_SYMBOL(wcd_cpe_get_afe_ops); + +MODULE_DESCRIPTION("WCD CPE Core"); +MODULE_LICENSE("GPL v2"); diff --git a/audio-kernel/asoc/codecs/wcd_cpe_core.h b/audio-kernel/asoc/codecs/wcd_cpe_core.h new file mode 100644 index 000000000..269c5f2a5 --- /dev/null +++ b/audio-kernel/asoc/codecs/wcd_cpe_core.h @@ -0,0 +1,243 @@ +/* Copyright (c) 2013-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 WCD_CPE_CORE_H +#define WCD_CPE_CORE_H + +#include +#include +#include "wcd_cpe_services.h" + +#define WCD_CPE_LAB_MAX_LATENCY 250 +#define WCD_CPE_MAD_SLIM_CHANNEL 140 + +/* Indicates CPE block is ready for image re-download */ +#define WCD_CPE_BLK_READY (1 << 0) +/* Indicates the underlying bus is ready */ +#define WCD_CPE_BUS_READY (1 << 1) + +/* + * only when the underlying bus and CPE block both are ready, + * the state will be ready to download + */ +#define WCD_CPE_READY_TO_DLOAD \ + (WCD_CPE_BLK_READY | WCD_CPE_BUS_READY) + +#define WCD_CPE_LOAD_IMEM (1 << 0) +#define WCD_CPE_LOAD_DATA (1 << 1) +#define WCD_CPE_LOAD_ALL \ + (WCD_CPE_LOAD_IMEM | WCD_CPE_LOAD_DATA) + +#define WCD_CPE_IMAGE_FNAME_MAX 64 + +#define WCD_CPE_AFE_OUT_PORT_2 2 +#define WCD_CPE_AFE_OUT_PORT_4 4 + +enum { + WCD_CPE_LSM_CAL_AFE = 0, + WCD_CPE_LSM_CAL_LSM, + WCD_CPE_LSM_CAL_TOPOLOGY_ID, + WCD_CPE_LSM_CAL_MAX, +}; + +enum cpe_err_irq_cntl_type { + CPE_ERR_IRQ_MASK = 0, + CPE_ERR_IRQ_UNMASK, + CPE_ERR_IRQ_CLEAR, + CPE_ERR_IRQ_STATUS, +}; + +struct wcd_cpe_cdc_cb { + /* codec provided callback to enable RCO */ + int (*cdc_clk_en)(struct snd_soc_codec *, bool); + + /* callback for FLL setup for codec */ + int (*cpe_clk_en)(struct snd_soc_codec *, bool); + int (*cdc_ext_clk)(struct snd_soc_codec *codec, int enable, bool dapm); + int (*lab_cdc_ch_ctl)(struct snd_soc_codec *codec, u8 event); + int (*get_afe_out_port_id)(struct snd_soc_codec *codec, u16 *port_id); + int (*bus_vote_bw)(struct snd_soc_codec *codec, + bool vote); + + /* Callback to control the cpe error interrupt mask/status/clear */ + int (*cpe_err_irq_control)(struct snd_soc_codec *codec, + enum cpe_err_irq_cntl_type cntl_type, + u8 *status); +}; + +enum wcd_cpe_ssr_state_event { + /* Indicates CPE is initialized */ + WCD_CPE_INITIALIZED = 0, + /* Indicates that IMEM is downloaded to CPE */ + WCD_CPE_IMEM_DOWNLOADED, + /* Indicates CPE is enabled */ + WCD_CPE_ENABLED, + /* Indicates that CPE is currently active */ + WCD_CPE_ACTIVE, + /* Event from underlying bus notifying bus is down */ + WCD_CPE_BUS_DOWN_EVENT, + /* Event from CPE block, notifying CPE is down */ + WCD_CPE_SSR_EVENT, + /* Event from underlying bus notifying bus is up */ + WCD_CPE_BUS_UP_EVENT, +}; + +struct wcd_cpe_ssr_entry { + int offline; + u32 offline_change; + wait_queue_head_t offline_poll_wait; + struct snd_info_entry *entry; +}; + +struct wcd_cpe_irq_info { + int cpe_engine_irq; + int cpe_err_irq; + u8 cpe_fatal_irqs; +}; + +struct wcd_cpe_hw_info { + u32 dram_offset; + size_t dram_size; + u32 iram_offset; + size_t iram_size; +}; + +struct wcd_cpe_core { + /* handle to cpe services */ + void *cpe_handle; + + /* registration handle to cpe services */ + void *cpe_reg_handle; + + /* cmi registration handle for afe service */ + void *cmi_afe_handle; + + /* handle to codec */ + struct snd_soc_codec *codec; + + /* codec device */ + struct device *dev; + + /* firmware image file name */ + char fname[WCD_CPE_IMAGE_FNAME_MAX]; + + /* firmware image file name from sysfs */ + char dyn_fname[WCD_CPE_IMAGE_FNAME_MAX]; + + /* codec information needed by cpe services */ + struct cpe_svc_codec_info_v1 cdc_info; + + /* work to perform image download */ + struct work_struct load_fw_work; + + /* flag to indicate mode in which cpe needs to be booted */ + int cpe_debug_mode; + + /* callbacks for codec specific implementation */ + const struct wcd_cpe_cdc_cb *cpe_cdc_cb; + + /* work to handle CPE SSR*/ + struct work_struct ssr_work; + + /* PM handle for suspend mode during SSR */ + struct pm_qos_request pm_qos_req; + + /* completion event indicating CPE OFFLINE */ + struct completion offline_compl; + + /* entry into snd card procfs indicating cpe status */ + struct wcd_cpe_ssr_entry ssr_entry; + + /* + * completion event to signal CPE is + * ready for image re-download + */ + struct completion ready_compl; + + /* maintains the status for cpe ssr */ + u8 ready_status; + + /* Indicate SSR type */ + enum wcd_cpe_ssr_state_event ssr_type; + + /* mutex to protect cpe ssr status variables */ + struct mutex ssr_lock; + + /* mutex to protect cpe session status variables */ + struct mutex session_lock; + + /* Store the calibration data needed for cpe */ + struct cal_type_data *cal_data[WCD_CPE_LSM_CAL_MAX]; + + /* completion event to signal CPE is online */ + struct completion online_compl; + + /* reference counter for cpe usage */ + u8 cpe_users; + + /* Ramdump support */ + void *cpe_ramdump_dev; + struct ramdump_segment cpe_ramdump_seg; + dma_addr_t cpe_dump_addr; + void *cpe_dump_v_addr; + + /* SFR support */ + u32 sfr_buf_addr; + size_t sfr_buf_size; + + /* IRQ information for CPE interrupts */ + struct wcd_cpe_irq_info irq_info; + + /* Kobject for sysfs entry */ + struct kobject cpe_kobj; + + /* Reference count for cpe clk*/ + int cpe_clk_ref; + + /* codec based hardware info */ + struct wcd_cpe_hw_info hw_info; +}; + +struct wcd_cpe_params { + struct snd_soc_codec *codec; + struct wcd_cpe_core * (*get_cpe_core)( + struct snd_soc_codec *); + const struct wcd_cpe_cdc_cb *cdc_cb; + int dbg_mode; + u16 cdc_major_ver; + u16 cdc_minor_ver; + u32 cdc_id; + + struct wcd_cpe_irq_info cdc_irq_info; + + struct cpe_svc_init_param *cpe_svc_params; +}; + +#if IS_ENABLED(CONFIG_SND_SOC_WCD_CPE) +int wcd_cpe_ssr_event(void *core_handle, + enum wcd_cpe_ssr_state_event event); +struct wcd_cpe_core *wcd_cpe_init(const char *img_fname, +struct snd_soc_codec *codec, struct wcd_cpe_params *params); +#else /* CONFIG_SND_SOC_WCD_CPE */ +static inline int wcd_cpe_ssr_event(void *core_handle, + enum wcd_cpe_ssr_state_event event) +{ + return 0; +} +static inline struct wcd_cpe_core *wcd_cpe_init(const char *img_fname, + struct snd_soc_codec *codec, + struct wcd_cpe_params *params) +{ + return NULL; +} +#endif /* CONFIG_SND_SOC_WCD_CPE */ +#endif diff --git a/audio-kernel/asoc/codecs/wcd_cpe_services.c b/audio-kernel/asoc/codecs/wcd_cpe_services.c new file mode 100644 index 000000000..e584cf05a --- /dev/null +++ b/audio-kernel/asoc/codecs/wcd_cpe_services.c @@ -0,0 +1,2727 @@ +/* Copyright (c) 2014-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 +#include +#include +#include +#include +#include +#include +#include +#include "wcd9335_registers.h" +#include "core.h" +#include "cpe_cmi.h" +#include "wcd_cpe_services.h" +#include "wcd_cmi_api.h" + +#define CPE_MSG_BUFFER_SIZE 132 +#define CPE_NO_SERVICE 0 + +#define CMI_DRIVER_SUPPORTED_VERSION 0 +#define CMI_API_SUCCESS 0 +#define CMI_MSG_TRANSPORT (0x0002) +#define CPE_SVC_INACTIVE_STATE_RETRIES_MAX 10 + +#define TOMTOM_A_SVASS_SPE_DRAM_OFFSET 0x50000 +#define TOMTOM_A_SVASS_SPE_DRAM_SIZE 0x30000 +#define TOMTOM_A_SVASS_SPE_IRAM_OFFSET 0x80000 +#define TOMTOM_A_SVASS_SPE_IRAM_SIZE 0xC000 +#define TOMTOM_A_SVASS_SPE_INBOX_SIZE 12 +#define TOMTOM_A_SVASS_SPE_OUTBOX_SIZE 12 + +#define MEM_ACCESS_NONE_VAL 0x0 +#define MEM_ACCESS_IRAM_VAL 0x1 +#define MEM_ACCESS_DRAM_VAL 0x2 +#define LISTEN_CTL_SPE_VAL 0x0 +#define LISTEN_CTL_MSM_VAL 0x1 + +#define WCD9335_CPE_SS_SPE_DRAM_OFFSET 0x48000 +#define WCD9335_CPE_SS_SPE_DRAM_SIZE 0x34000 +#define WCD9335_CPE_SS_SPE_IRAM_OFFSET 0x80000 +#define WCD9335_CPE_SS_SPE_IRAM_SIZE 0x20000 + +#define WCD9335_CPE_SS_SPE_INBOX_SIZE 16 +#define WCD9335_CPE_SS_SPE_OUTBOX_SIZE 16 +#define WCD9335_CPE_SS_SPE_MEM_BANK_SIZ 16 + +#define WCD9335_CPE_SS_SPE_INBOX1(N) (WCD9335_CPE_SS_INBOX1_0 + (N)) +#define WCD9335_CPE_SS_SPE_OUTBOX1(N) (WCD9335_CPE_SS_OUTBOX1_0 + (N)) +#define WCD9335_CPE_SS_MEM_BANK(N) (WCD9335_CPE_SS_MEM_BANK_0 + (N)) + +#define CHUNK_SIZE 16 + +#define CPE_SVC_GRAB_LOCK(lock, name) \ +{ \ + pr_debug("%s: %s lock acquire\n", \ + __func__, name); \ + mutex_lock(lock); \ +} + +#define CPE_SVC_REL_LOCK(lock, name) \ +{ \ + pr_debug("%s: %s lock release\n", \ + __func__, name); \ + mutex_unlock(lock); \ +} + +static const struct cpe_svc_hw_cfg cpe_svc_tomtom_info = { + TOMTOM_A_SVASS_SPE_DRAM_SIZE, + TOMTOM_A_SVASS_SPE_DRAM_OFFSET, + TOMTOM_A_SVASS_SPE_IRAM_SIZE, + TOMTOM_A_SVASS_SPE_IRAM_OFFSET, + TOMTOM_A_SVASS_SPE_INBOX_SIZE, + TOMTOM_A_SVASS_SPE_OUTBOX_SIZE +}; + +static const struct cpe_svc_hw_cfg cpe_svc_wcd9335_info = { + WCD9335_CPE_SS_SPE_DRAM_SIZE, + WCD9335_CPE_SS_SPE_DRAM_OFFSET, + WCD9335_CPE_SS_SPE_IRAM_SIZE, + WCD9335_CPE_SS_SPE_IRAM_OFFSET, + WCD9335_CPE_SS_SPE_INBOX_SIZE, + WCD9335_CPE_SS_SPE_OUTBOX_SIZE +}; + +enum cpe_state { + CPE_STATE_UNINITIALIZED = 0, + CPE_STATE_INITIALIZED, + CPE_STATE_IDLE, + CPE_STATE_DOWNLOADING, + CPE_STATE_BOOTING, + CPE_STATE_SENDING_MSG, + CPE_STATE_OFFLINE, + CPE_STATE_BUFFERING, + CPE_STATE_BUFFERING_CANCELLED +}; + +enum cpe_substate { + CPE_SS_IDLE = 0, + CPE_SS_MSG_REQUEST_ACCESS, + CPE_SS_MSG_SEND_INBOX, + CPE_SS_MSG_SENT, + CPE_SS_DL_DOWNLOADING, + CPE_SS_DL_COMPLETED, + CPE_SS_BOOT, + CPE_SS_BOOT_INIT, + CPE_SS_ONLINE +}; + +enum cpe_command { + CPE_CMD_KILL_THREAD = 0, + CPE_CMD_BOOT, + CPE_CMD_BOOT_INITIALIZE, + CPE_CMD_BOOT_COMPLETE, + CPE_CMD_SEND_MSG, + CPE_CMD_SEND_TRANS_MSG, + CPE_CMD_SEND_MSG_COMPLETE, + CPE_CMD_PROCESS_IRQ, + CPE_CMD_RAMDUMP, + CPE_CMD_DL_SEGMENT, + CPE_CMD_SHUTDOWN, + CPE_CMD_RESET, + CPE_CMD_DEINITIALIZE, + CPE_CMD_READ, + CPE_CMD_ENABLE_LAB, + CPE_CMD_DISABLE_LAB, + CPE_CMD_SWAP_BUFFER, + CPE_LAB_CFG_SB, + CPE_CMD_CANCEL_MEMACCESS, + CPE_CMD_PROC_INCOMING_MSG, + CPE_CMD_FTM_TEST, +}; + +enum cpe_process_result { + CPE_PROC_SUCCESS = 0, + CPE_PROC_FAILED, + CPE_PROC_KILLED, + CPE_PROC_QUEUED, +}; + +struct cpe_command_node { + enum cpe_command command; + enum cpe_svc_result result; + void *data; + struct list_head list; +}; + +struct cpe_info { + struct list_head main_queue; + struct completion cmd_complete; + struct completion thread_comp; + void *thread_handler; + bool stop_thread; + struct mutex msg_lock; + enum cpe_state state; + enum cpe_substate substate; + struct list_head client_list; + enum cpe_process_result (*cpe_process_command) + (struct cpe_command_node *command_node); + enum cpe_svc_result (*cpe_cmd_validate) + (const struct cpe_info *i, + enum cpe_command command); + enum cpe_svc_result (*cpe_start_notification) + (struct cpe_info *i); + u32 initialized; + struct cpe_svc_tgt_abstraction *tgt; + void *pending; + void *data; + void *client_context; + u32 codec_id; + struct work_struct clk_plan_work; + struct completion core_svc_cmd_compl; +}; + +struct cpe_tgt_waiti_info { + u8 tgt_waiti_size; + u8 *tgt_waiti_data; +}; + +struct cpe_svc_tgt_abstraction { + enum cpe_svc_result (*tgt_boot)(int debug_mode); + + u32 (*tgt_cpar_init_done)(void); + + u32 (*tgt_is_active)(void); + + enum cpe_svc_result (*tgt_reset)(void); + + enum cpe_svc_result (*tgt_stop)(void); + + enum cpe_svc_result (*tgt_read_mailbox) + (u8 *buffer, size_t size); + + enum cpe_svc_result (*tgt_write_mailbox) + (u8 *buffer, size_t size); + + enum cpe_svc_result (*tgt_read_ram) + (struct cpe_info *c, + struct cpe_svc_mem_segment *data); + + enum cpe_svc_result (*tgt_write_ram) + (struct cpe_info *c, + const struct cpe_svc_mem_segment *data); + + enum cpe_svc_result (*tgt_route_notification) + (enum cpe_svc_module module, + enum cpe_svc_route_dest dest); + + enum cpe_svc_result (*tgt_set_debug_mode)(u32 enable); + const struct cpe_svc_hw_cfg *(*tgt_get_cpe_info)(void); + enum cpe_svc_result (*tgt_deinit) + (struct cpe_svc_tgt_abstraction *param); + enum cpe_svc_result (*tgt_voice_tx_lab) + (bool); + u8 *inbox; + u8 *outbox; + struct cpe_tgt_waiti_info *tgt_waiti_info; +}; + +static enum cpe_svc_result cpe_tgt_tomtom_init( + struct cpe_svc_codec_info_v1 *codec_info, + struct cpe_svc_tgt_abstraction *param); + +static enum cpe_svc_result cpe_tgt_wcd9335_init( + struct cpe_svc_codec_info_v1 *codec_info, + struct cpe_svc_tgt_abstraction *param); + +struct cpe_send_msg { + u8 *payload; + u32 isobm; + u32 address; + size_t size; +}; + +struct cpe_read_handle { + void *registration; + struct cpe_info t_info; + struct list_head buffers; + void *config; +}; + +struct generic_notification { + void (*notification) + (const struct cpe_svc_notification *parameter); + void (*cmi_notification) + (const struct cmi_api_notification *parameter); +}; + +struct cpe_notif_node { + struct generic_notification notif; + u32 mask; + u32 service; + const struct cpe_info *context; + const char *name; + u32 disabled; + struct list_head list; +}; + +struct cpe_priv { + struct cpe_info *cpe_default_handle; + void (*cpe_irq_control_callback)(u32 enable); + void (*cpe_query_freq_plans_cb) + (void *cdc_priv, + struct cpe_svc_cfg_clk_plan *clk_freq); + void (*cpe_change_freq_plan_cb)(void *cdc_priv, + u32 clk_freq); + u32 cpe_msg_buffer; + void *cpe_cmi_handle; + struct mutex cpe_api_mutex; + struct mutex cpe_svc_lock; + struct cpe_svc_boot_event cpe_debug_vector; + void *cdc_priv; +}; + +static struct cpe_priv cpe_d; + +static enum cpe_svc_result __cpe_svc_shutdown(void *cpe_handle); + +static enum cpe_svc_result cpe_is_command_valid( + const struct cpe_info *t_info, + enum cpe_command command); + +static int cpe_register_read(u32 reg, u8 *val) +{ + *(val) = snd_soc_read(cpe_d.cdc_priv, reg); + return 0; +} + +static enum cpe_svc_result cpe_update_bits(u32 reg, + u32 mask, u32 value) +{ + int ret = 0; + + ret = snd_soc_update_bits(cpe_d.cdc_priv, reg, + mask, value); + if (ret < 0) + return CPE_SVC_FAILED; + + return CPE_SVC_SUCCESS; +} + +static int cpe_register_write(u32 reg, u32 val) +{ + int ret = 0; + + if (reg != WCD9335_CPE_SS_MEM_BANK_0) + pr_debug("%s: reg = 0x%x, value = 0x%x\n", + __func__, reg, val); + + ret = snd_soc_write(cpe_d.cdc_priv, reg, val); + if (ret < 0) + return CPE_SVC_FAILED; + + return CPE_SVC_SUCCESS; +} + +static int cpe_register_write_repeat(u32 reg, u8 *ptr, u32 to_write) +{ + struct snd_soc_codec *codec = cpe_d.cdc_priv; + struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent); + int ret = 0; + + ret = wcd9xxx_slim_write_repeat(wcd9xxx, reg, to_write, ptr); + if (ret != 0) + pr_err("%s: slim_write_repeat failed\n", __func__); + + if (ret < 0) + return CPE_SVC_FAILED; + + return CPE_SVC_SUCCESS; +} + +static bool cpe_register_read_autoinc_supported(void) +{ + return true; +} + + +/* Called under msgq locked context */ +static void cpe_cmd_received(struct cpe_info *t_info) +{ + struct cpe_command_node *node = NULL; + enum cpe_process_result proc_rc = CPE_PROC_SUCCESS; + + if (!t_info) { + pr_err("%s: Invalid thread info\n", + __func__); + return; + } + + while (!list_empty(&t_info->main_queue)) { + if (proc_rc != CPE_PROC_SUCCESS) + break; + node = list_first_entry(&t_info->main_queue, + struct cpe_command_node, list); + if (!node) + break; + list_del(&node->list); + proc_rc = t_info->cpe_process_command(node); + pr_debug("%s: process command return %d\n", + __func__, proc_rc); + + switch (proc_rc) { + case CPE_PROC_SUCCESS: + kfree(node); + break; + case CPE_PROC_FAILED: + kfree(node); + pr_err("%s: cmd failed\n", __func__); + break; + case CPE_PROC_KILLED: + break; + default: + list_add(&node->list, &(t_info->main_queue)); + + } + } +} + +static int cpe_worker_thread(void *context) +{ + struct cpe_info *t_info = (struct cpe_info *)context; + + /* + * Thread will run until requested to stop explicitly + * by setting the t_info->stop_thread flag + */ + while (1) { + /* Wait for command to be processed */ + wait_for_completion(&t_info->cmd_complete); + + CPE_SVC_GRAB_LOCK(&t_info->msg_lock, "msg_lock"); + cpe_cmd_received(t_info); + reinit_completion(&t_info->cmd_complete); + /* Check if thread needs to be stopped */ + if (t_info->stop_thread) + goto unlock_and_exit; + CPE_SVC_REL_LOCK(&t_info->msg_lock, "msg_lock"); + }; + +unlock_and_exit: + pr_debug("%s: thread stopped\n", __func__); + CPE_SVC_REL_LOCK(&t_info->msg_lock, "msg_lock"); + complete_and_exit(&t_info->thread_comp, 0); +} + +static void cpe_create_worker_thread(struct cpe_info *t_info) +{ + INIT_LIST_HEAD(&t_info->main_queue); + init_completion(&t_info->cmd_complete); + init_completion(&t_info->thread_comp); + t_info->stop_thread = false; + t_info->thread_handler = kthread_run(cpe_worker_thread, + (void *)t_info, "cpe-worker-thread"); + pr_debug("%s: Created new worker thread\n", + __func__); +} + +static void cpe_cleanup_worker_thread(struct cpe_info *t_info) +{ + if (!t_info->thread_handler) { + pr_err("%s: thread not created\n", __func__); + return; + } + + /* + * Wake up the command handler in case + * it is waiting for an command to be processed. + */ + CPE_SVC_GRAB_LOCK(&t_info->msg_lock, "msg_lock"); + t_info->stop_thread = true; + complete(&t_info->cmd_complete); + CPE_SVC_REL_LOCK(&t_info->msg_lock, "msg_lock"); + + /* Wait for the thread to exit */ + wait_for_completion(&t_info->thread_comp); + t_info->thread_handler = NULL; + + pr_debug("%s: Thread cleaned up successfully\n", + __func__); +} + +static enum cpe_svc_result +cpe_send_cmd_to_thread(struct cpe_info *t_info, + enum cpe_command command, void *data, + bool high_prio) +{ + enum cpe_svc_result rc = CPE_SVC_SUCCESS; + struct cpe_command_node *cmd = NULL; + + rc = cpe_is_command_valid(t_info, command); + if (rc != CPE_SVC_SUCCESS) { + pr_err("%s: Invalid command %d\n", + __func__, command); + return rc; + } + + cmd = kzalloc(sizeof(struct cpe_command_node), + GFP_ATOMIC); + if (!cmd) + return CPE_SVC_NO_MEMORY; + + cmd->command = command; + cmd->data = data; + + CPE_SVC_GRAB_LOCK(&t_info->msg_lock, "msg_lock"); + if (high_prio) + list_add(&(cmd->list), + &(t_info->main_queue)); + else + list_add_tail(&(cmd->list), + &(t_info->main_queue)); + complete(&t_info->cmd_complete); + CPE_SVC_REL_LOCK(&t_info->msg_lock, "msg_lock"); + + return rc; +} + +static enum cpe_svc_result cpe_change_state( + struct cpe_info *t_info, + enum cpe_state state, enum cpe_substate ss) +{ + enum cpe_svc_result rc = CPE_SVC_SUCCESS; + + if (!t_info) + t_info = cpe_d.cpe_default_handle; + + t_info->state = state; + t_info->substate = ss; + + pr_debug("%s: current state: %d,%d, new_state: %d,%d\n", + __func__, t_info->state, t_info->substate, + state, ss); + + return rc; +} + +static enum cpe_svc_result +cpe_is_command_valid(const struct cpe_info *t_info, + enum cpe_command command) +{ + enum cpe_svc_result rc = CPE_SVC_INVALID_HANDLE; + + if (t_info && t_info->cpe_cmd_validate) + rc = t_info->cpe_cmd_validate(t_info, command); + else + pr_err("%s: invalid handle or callback\n", + __func__); + return rc; +} + +static void cpe_notify_client(struct cpe_notif_node *client, + struct cpe_svc_notification *payload) +{ + if (!client || !payload) { + pr_err("%s: invalid client or payload\n", + __func__); + return; + } + + if (!(client->mask & payload->event)) { + pr_debug("%s: client mask 0x%x not registered for event 0x%x\n", + __func__, client->mask, payload->event); + return; + } + + if (client->notif.notification && !client->disabled) + client->notif.notification(payload); + + if ((client->mask & CPE_SVC_CMI_MSG) && + client->notif.cmi_notification) + client->notif.cmi_notification( + (const struct cmi_api_notification *)payload); +} + +static void cpe_broadcast_notification(const struct cpe_info *t_info, + struct cpe_svc_notification *payload) +{ + struct cpe_notif_node *n = NULL; + + if (!t_info || !payload) { + pr_err("%s: invalid handle\n", __func__); + return; + } + + pr_debug("%s: notify clients, event = %d\n", + __func__, payload->event); + payload->private_data = cpe_d.cdc_priv; + + CPE_SVC_GRAB_LOCK(&cpe_d.cpe_svc_lock, "cpe_svc"); + list_for_each_entry(n, &t_info->client_list, list) { + if (!(n->mask & CPE_SVC_CMI_MSG)) + cpe_notify_client(n, payload); + } + CPE_SVC_REL_LOCK(&cpe_d.cpe_svc_lock, "cpe_svc"); +} + +static void *cpe_register_generic(struct cpe_info *t_info, + void notification_callback( + const struct cpe_svc_notification *parameter), + void cmi_callback( + const struct cmi_api_notification *parameter), + u32 mask, u32 service, const char *name) +{ + struct cpe_notif_node *n = NULL; + + n = kzalloc(sizeof(struct cpe_notif_node), + GFP_KERNEL); + if (!n) + return NULL; + n->mask = mask; + n->service = service; + n->notif.notification = notification_callback; + n->notif.cmi_notification = cmi_callback; + n->context = t_info; + n->disabled = false; + n->name = name; + + CPE_SVC_GRAB_LOCK(&cpe_d.cpe_svc_lock, "cpe_svc"); + /* Make sure CPE core service is first */ + if (service == CMI_CPE_CORE_SERVICE_ID) + list_add(&n->list, &t_info->client_list); + else + list_add_tail(&n->list, &t_info->client_list); + CPE_SVC_REL_LOCK(&cpe_d.cpe_svc_lock, "cpe_svc"); + + return n; +} + +static enum cpe_svc_result cpe_deregister_generic(struct cpe_info *t_info, + void *reg_handle) +{ + struct cpe_notif_node *n = (struct cpe_notif_node *)reg_handle; + + if (!t_info || !reg_handle) { + pr_err("%s: invalid handle\n", __func__); + return CPE_SVC_INVALID_HANDLE; + } + + CPE_SVC_GRAB_LOCK(&cpe_d.cpe_svc_lock, "cpe_svc"); + list_del(&(n->list)); + kfree(reg_handle); + CPE_SVC_REL_LOCK(&cpe_d.cpe_svc_lock, "cpe_svc"); + + return CPE_SVC_SUCCESS; +} + +static enum cpe_svc_result cpe_svc_tgt_init(struct cpe_svc_codec_info_v1 *i, + struct cpe_svc_tgt_abstraction *abs) +{ + if (!i || !abs) { + pr_err("%s: Incorrect information provided\n", + __func__); + return CPE_SVC_FAILED; + } + + switch (i->id) { + case CPE_SVC_CODEC_TOMTOM: + return cpe_tgt_tomtom_init(i, abs); + case CPE_SVC_CODEC_WCD9335: + return cpe_tgt_wcd9335_init(i, abs); + default: + pr_err("%s: Codec type %d not supported\n", + __func__, i->id); + return CPE_SVC_FAILED; + } + + return CPE_SVC_SUCCESS; +} + +static void cpe_notify_cmi_client(struct cpe_info *t_info, u8 *payload, + enum cpe_svc_result result) +{ + struct cpe_notif_node *n = NULL; + struct cmi_api_notification notif; + struct cmi_hdr *hdr; + u8 service = 0; + + if (!t_info || !payload) { + pr_err("%s: invalid payload/handle\n", + __func__); + return; + } + + hdr = CMI_GET_HEADER(payload); + service = CMI_HDR_GET_SERVICE(hdr); + + notif.event = CMI_API_MSG; + notif.result = result; + notif.message = payload; + + CPE_SVC_GRAB_LOCK(&cpe_d.cpe_svc_lock, "cpe_svc"); + list_for_each_entry(n, &t_info->client_list, list) { + + if ((n->mask & CPE_SVC_CMI_MSG) && + n->service == service && + n->notif.cmi_notification) { + n->notif.cmi_notification(¬if); + break; + } + } + CPE_SVC_REL_LOCK(&cpe_d.cpe_svc_lock, "cpe_svc"); +} + +static void cpe_toggle_irq_notification(struct cpe_info *t_info, u32 value) +{ + if (cpe_d.cpe_irq_control_callback) + cpe_d.cpe_irq_control_callback(value); +} + +static void cpe_command_cleanup(struct cpe_command_node *command_node) +{ + switch (command_node->command) { + case CPE_CMD_SEND_MSG: + case CPE_CMD_SEND_TRANS_MSG: + case CPE_CMD_SEND_MSG_COMPLETE: + case CPE_CMD_SHUTDOWN: + case CPE_CMD_READ: + kfree(command_node->data); + command_node->data = NULL; + break; + default: + pr_err("%s: unhandled command\n", + __func__); + break; + } +} + +static enum cpe_svc_result cpe_send_msg_to_inbox( + struct cpe_info *t_info, u32 opcode, + struct cpe_send_msg *msg) +{ + size_t bytes = 0; + size_t inbox_size = + t_info->tgt->tgt_get_cpe_info()->inbox_size; + struct cmi_hdr *hdr; + enum cpe_svc_result rc = CPE_SVC_SUCCESS; + + memset(t_info->tgt->inbox, 0, inbox_size); + hdr = CMI_GET_HEADER(t_info->tgt->inbox); + CMI_HDR_SET_SESSION(hdr, 1); + CMI_HDR_SET_SERVICE(hdr, CMI_CPE_CORE_SERVICE_ID); + CMI_HDR_SET_VERSION(hdr, CMI_DRIVER_SUPPORTED_VERSION); + CMI_HDR_SET_OBM(hdr, CMI_OBM_FLAG_IN_BAND); + + switch (opcode) { + case CPE_CORE_SVC_CMD_SHARED_MEM_ALLOC: { + struct cmi_core_svc_cmd_shared_mem_alloc *m; + + CMI_HDR_SET_OPCODE(hdr, + CPE_CORE_SVC_CMD_SHARED_MEM_ALLOC); + CMI_HDR_SET_PAYLOAD_SIZE(hdr, + sizeof(struct cmi_core_svc_cmd_shared_mem_alloc)); + m = (struct cmi_core_svc_cmd_shared_mem_alloc *) + CMI_GET_PAYLOAD(t_info->tgt->inbox); + m->size = CPE_MSG_BUFFER_SIZE; + pr_debug("send shared mem alloc msg to cpe inbox\n"); + } + break; + case CPE_CORE_SVC_CMD_DRAM_ACCESS_REQ: + CMI_HDR_SET_OPCODE(hdr, + CPE_CORE_SVC_CMD_DRAM_ACCESS_REQ); + CMI_HDR_SET_PAYLOAD_SIZE(hdr, 0); + pr_debug("%s: Creating DRAM acces request msg\n", + __func__); + break; + + case CPE_CMI_BASIC_RSP_OPCODE: { + struct cmi_basic_rsp_result *rsp; + + CMI_HDR_SET_OPCODE(hdr, + CPE_CMI_BASIC_RSP_OPCODE); + CMI_HDR_SET_PAYLOAD_SIZE(hdr, + sizeof(struct cmi_basic_rsp_result)); + rsp = (struct cmi_basic_rsp_result *) + CMI_GET_PAYLOAD(t_info->tgt->inbox); + rsp->status = 0; + pr_debug("%s: send basic response\n", __func__); + } + break; + + default: + if (msg->address != 0) { + struct cmi_msg_transport *m = NULL; + struct cpe_svc_mem_segment mem_seg; + + mem_seg.type = CPE_SVC_DATA_MEM; + if (msg->isobm) { + struct cmi_obm *obm = (struct cmi_obm *) + + CMI_GET_PAYLOAD(msg->payload); + mem_seg.cpe_addr = obm->mem_handle; + mem_seg.data = (u8 *)obm->data_ptr.kvaddr; + mem_seg.size = obm->size; + t_info->tgt->tgt_write_ram(t_info, &mem_seg); + } + + mem_seg.cpe_addr = msg->address; + mem_seg.data = msg->payload; + mem_seg.size = msg->size; + t_info->tgt->tgt_write_ram(t_info, &mem_seg); + + hdr = CMI_GET_HEADER(t_info->tgt->inbox); + CMI_HDR_SET_OPCODE(hdr, CMI_MSG_TRANSPORT); + m = (struct cmi_msg_transport *) + CMI_GET_PAYLOAD(t_info->tgt->inbox); + m->addr = msg->address; + m->size = msg->size; + CMI_HDR_SET_PAYLOAD_SIZE(hdr, + sizeof(struct cmi_msg_transport)); + } else { + memcpy(t_info->tgt->inbox, msg->payload, + msg->size); + } + + break; + } + + pr_debug("%s: sending message to cpe inbox\n", + __func__); + bytes = sizeof(struct cmi_hdr); + hdr = CMI_GET_HEADER(t_info->tgt->inbox); + bytes += CMI_HDR_GET_PAYLOAD_SIZE(hdr); + rc = t_info->tgt->tgt_write_mailbox(t_info->tgt->inbox, bytes); + + return rc; +} + +static bool cpe_is_cmd_clk_req(void *cmd) +{ + struct cmi_hdr *hdr; + + hdr = CMI_GET_HEADER(cmd); + + if ((CMI_HDR_GET_SERVICE(hdr) == + CMI_CPE_CORE_SERVICE_ID)) { + if (CMI_GET_OPCODE(cmd) == + CPE_CORE_SVC_CMD_CLK_FREQ_REQUEST) + return true; + } + + return false; +} + +static enum cpe_svc_result cpe_process_clk_change_req( + struct cpe_info *t_info) +{ + struct cmi_core_svc_cmd_clk_freq_request *req; + + req = (struct cmi_core_svc_cmd_clk_freq_request *) + CMI_GET_PAYLOAD(t_info->tgt->outbox); + + if (!cpe_d.cpe_change_freq_plan_cb) { + pr_err("%s: No support for clk freq change\n", + __func__); + return CPE_SVC_FAILED; + } + + cpe_d.cpe_change_freq_plan_cb(cpe_d.cdc_priv, + req->clk_freq); + + /*send a basic response*/ + cpe_send_msg_to_inbox(t_info, + CPE_CMI_BASIC_RSP_OPCODE, NULL); + + return CPE_SVC_SUCCESS; +} + +static void cpe_process_irq_int(u32 irq, + struct cpe_info *t_info) +{ + struct cpe_command_node temp_node; + struct cpe_send_msg *m; + u8 size = 0; + bool err_irq = false; + struct cmi_hdr *hdr; + + pr_debug("%s: irq = %u\n", __func__, irq); + + if (!t_info) { + pr_err("%s: Invalid handle\n", + __func__); + return; + } + + CPE_SVC_GRAB_LOCK(&cpe_d.cpe_api_mutex, "cpe_api"); + switch (irq) { + case CPE_IRQ_OUTBOX_IRQ: + size = t_info->tgt->tgt_get_cpe_info()->outbox_size; + t_info->tgt->tgt_read_mailbox(t_info->tgt->outbox, size); + break; + + case CPE_IRQ_MEM_ACCESS_ERROR: + err_irq = true; + cpe_change_state(t_info, CPE_STATE_OFFLINE, CPE_SS_IDLE); + break; + + case CPE_IRQ_WDOG_BITE: + case CPE_IRQ_RCO_WDOG_INT: + err_irq = true; + __cpe_svc_shutdown(t_info); + break; + + case CPE_IRQ_FLL_LOCK_LOST: + default: + err_irq = true; + break; + } + + if (err_irq) { + pr_err("%s: CPE error IRQ %u occurred\n", + __func__, irq); + CPE_SVC_REL_LOCK(&cpe_d.cpe_api_mutex, "cpe_api"); + return; + } + + switch (t_info->state) { + case CPE_STATE_BOOTING: + + switch (t_info->substate) { + case CPE_SS_BOOT: + temp_node.command = CPE_CMD_BOOT_INITIALIZE; + temp_node.result = CPE_SVC_SUCCESS; + t_info->substate = CPE_SS_BOOT_INIT; + t_info->cpe_process_command(&temp_node); + break; + + case CPE_SS_BOOT_INIT: + temp_node.command = CPE_CMD_BOOT_COMPLETE; + temp_node.result = CPE_SVC_SUCCESS; + t_info->substate = CPE_SS_ONLINE; + t_info->cpe_process_command(&temp_node); + break; + + default: + pr_debug("%s: unhandled substate %d for state %d\n", + __func__, t_info->state, t_info->substate); + break; + } + break; + + case CPE_STATE_SENDING_MSG: + hdr = CMI_GET_HEADER(t_info->tgt->outbox); + if (CMI_GET_OPCODE(t_info->tgt->outbox) == + CPE_LSM_SESSION_EVENT_DETECTION_STATUS_V2) { + pr_debug("%s: session_id: %u, state: %d,%d, event received\n", + __func__, CMI_HDR_GET_SESSION_ID(hdr), + t_info->state, t_info->substate); + temp_node.command = CPE_CMD_PROC_INCOMING_MSG; + temp_node.data = NULL; + t_info->cpe_process_command(&temp_node); + break; + } + + m = (struct cpe_send_msg *)t_info->pending; + + switch (t_info->substate) { + case CPE_SS_MSG_REQUEST_ACCESS: + cpe_send_cmd_to_thread(t_info, + CPE_CMD_SEND_TRANS_MSG, m, true); + break; + + case CPE_SS_MSG_SEND_INBOX: + if (cpe_is_cmd_clk_req(t_info->tgt->outbox)) + cpe_process_clk_change_req(t_info); + else + cpe_send_cmd_to_thread(t_info, + CPE_CMD_SEND_MSG_COMPLETE, m, true); + break; + + default: + pr_debug("%s: unhandled substate %d for state %d\n", + __func__, t_info->state, t_info->substate); + break; + } + break; + + case CPE_STATE_IDLE: + pr_debug("%s: Message received, notifying client\n", + __func__); + temp_node.command = CPE_CMD_PROC_INCOMING_MSG; + temp_node.data = NULL; + t_info->cpe_process_command(&temp_node); + break; + + default: + pr_debug("%s: unhandled state %d\n", + __func__, t_info->state); + break; + } + + CPE_SVC_REL_LOCK(&cpe_d.cpe_api_mutex, "cpe_api"); +} + + +static void broacast_boot_failed(void) +{ + struct cpe_info *t_info = cpe_d.cpe_default_handle; + struct cpe_svc_notification payload; + + payload.event = CPE_SVC_BOOT_FAILED; + payload.result = CPE_SVC_FAILED; + payload.payload = NULL; + if (t_info) + payload.private_data = + t_info->client_context; + cpe_broadcast_notification(t_info, &payload); +} + +static enum cpe_svc_result broadcast_boot_event( + struct cpe_info *t_info) +{ + struct cpe_svc_notification payload; + + payload.event = CPE_SVC_ONLINE; + payload.result = CPE_SVC_SUCCESS; + payload.payload = NULL; + if (t_info) + payload.private_data = + t_info->client_context; + cpe_broadcast_notification(t_info, &payload); + + return CPE_SVC_SUCCESS; +} + +static enum cpe_process_result cpe_boot_initialize(struct cpe_info *t_info, + enum cpe_svc_result *cpe_rc) +{ + enum cpe_process_result rc = CPE_SVC_FAILED; + struct cpe_svc_notification payload; + struct cmi_core_svc_event_system_boot *p = NULL; + + if (CMI_GET_OPCODE(t_info->tgt->outbox) != + CPE_CORE_SVC_EVENT_SYSTEM_BOOT) { + broacast_boot_failed(); + return rc; + } + + p = (struct cmi_core_svc_event_system_boot *) + CMI_GET_PAYLOAD(t_info->tgt->outbox); + if (p->status != CPE_BOOT_SUCCESS) { + pr_err("%s: cpe boot failed, status = %d\n", + __func__, p->status); + broacast_boot_failed(); + return rc; + } + + /* boot was successful */ + if (p->version == + CPE_CORE_VERSION_SYSTEM_BOOT_EVENT) { + cpe_d.cpe_debug_vector.debug_address = + p->sfr_buff_address; + cpe_d.cpe_debug_vector.debug_buffer_size = + p->sfr_buff_size; + cpe_d.cpe_debug_vector.status = p->status; + payload.event = CPE_SVC_BOOT; + payload.result = CPE_SVC_SUCCESS; + payload.payload = (void *)&cpe_d.cpe_debug_vector; + payload.private_data = t_info->client_context; + cpe_broadcast_notification(t_info, &payload); + } + cpe_change_state(t_info, CPE_STATE_BOOTING, + CPE_SS_BOOT_INIT); + (*cpe_rc) = cpe_send_msg_to_inbox(t_info, + CPE_CORE_SVC_CMD_SHARED_MEM_ALLOC, NULL); + rc = CPE_PROC_SUCCESS; + return rc; +} + +static void cpe_svc_core_cmi_handler( + const struct cmi_api_notification *parameter) +{ + struct cmi_hdr *hdr; + + if (!parameter) + return; + + pr_debug("%s: event = %d\n", + __func__, parameter->event); + + if (parameter->event != CMI_API_MSG) + return; + + hdr = (struct cmi_hdr *) parameter->message; + + if (hdr->opcode == CPE_CMI_BASIC_RSP_OPCODE) { + struct cmi_basic_rsp_result *result; + + result = (struct cmi_basic_rsp_result *) + ((u8 *)parameter->message) + (sizeof(*hdr)); + if (result->status) + pr_err("%s: error response, error code = %u\n", + __func__, result->status); + complete(&cpe_d.cpe_default_handle->core_svc_cmd_compl); + } +} + +static void cpe_clk_plan_work(struct work_struct *work) +{ + struct cpe_info *t_info = NULL; + size_t size = 0; + struct cpe_svc_cfg_clk_plan plan; + u8 *cmi_msg; + struct cmi_hdr *hdr; + int rc; + + t_info = container_of(work, struct cpe_info, clk_plan_work); + if (!t_info) { + pr_err("%s: Invalid handle for cpe_info\n", + __func__); + return; + } + + /* Register the core service */ + cpe_d.cpe_cmi_handle = cmi_register( + cpe_svc_core_cmi_handler, + CMI_CPE_CORE_SERVICE_ID); + + /* send the clk plan command */ + if (!cpe_d.cpe_query_freq_plans_cb) { + pr_err("%s: No support for querying clk plans\n", + __func__); + return; + } + + cpe_d.cpe_query_freq_plans_cb(cpe_d.cdc_priv, &plan); + size = sizeof(plan.current_clk_feq) + + sizeof(plan.num_clk_freqs); + size += plan.num_clk_freqs * + sizeof(plan.clk_freqs[0]); + cmi_msg = kzalloc(size + sizeof(struct cmi_hdr), + GFP_KERNEL); + if (!cmi_msg) + return; + + hdr = (struct cmi_hdr *) cmi_msg; + CMI_HDR_SET_OPCODE(hdr, + CPE_CORE_SVC_CMD_CFG_CLK_PLAN); + CMI_HDR_SET_SERVICE(hdr, CMI_CPE_CORE_SERVICE_ID); + CMI_HDR_SET_SESSION(hdr, 1); + CMI_HDR_SET_VERSION(hdr, CMI_DRIVER_SUPPORTED_VERSION); + CMI_HDR_SET_PAYLOAD_SIZE(hdr, size); + memcpy(CMI_GET_PAYLOAD(cmi_msg), &plan, + size); + cmi_send_msg(cmi_msg); + + /* Wait for clk plan command to complete */ + rc = wait_for_completion_timeout(&t_info->core_svc_cmd_compl, + (10 * HZ)); + if (!rc) { + pr_err("%s: clk plan cmd timed out\n", + __func__); + goto cmd_fail; + } + + /* clk plan cmd is successful, send start notification */ + if (t_info->cpe_start_notification) + t_info->cpe_start_notification(t_info); + else + pr_err("%s: no start notification\n", + __func__); + +cmd_fail: + kfree(cmi_msg); + cmi_deregister(cpe_d.cpe_cmi_handle); +} + +static enum cpe_process_result cpe_boot_complete( + struct cpe_info *t_info) +{ + struct cmi_core_svc_cmdrsp_shared_mem_alloc *p = NULL; + + if (CMI_GET_OPCODE(t_info->tgt->outbox) != + CPE_CORE_SVC_CMDRSP_SHARED_MEM_ALLOC) { + broacast_boot_failed(); + return CPE_PROC_FAILED; + } + + p = (struct cmi_core_svc_cmdrsp_shared_mem_alloc *) + CMI_GET_PAYLOAD(t_info->tgt->outbox); + cpe_d.cpe_msg_buffer = p->addr; + + if (cpe_d.cpe_msg_buffer == 0) { + pr_err("%s: Invalid cpe buffer for message\n", + __func__); + broacast_boot_failed(); + return CPE_PROC_FAILED; + } + + cpe_change_state(t_info, CPE_STATE_IDLE, CPE_SS_IDLE); + cpe_create_worker_thread(t_info); + + if (t_info->codec_id != CPE_SVC_CODEC_TOMTOM) { + schedule_work(&t_info->clk_plan_work); + } else { + if (t_info->cpe_start_notification) + t_info->cpe_start_notification(t_info); + else + pr_err("%s: no start notification\n", + __func__); + } + + pr_debug("%s: boot complete\n", __func__); + return CPE_PROC_SUCCESS; +} + +static enum cpe_process_result cpe_process_send_msg( + struct cpe_info *t_info, + enum cpe_svc_result *cpe_rc, + struct cpe_command_node *command_node) +{ + enum cpe_process_result rc = CPE_PROC_SUCCESS; + struct cpe_send_msg *m = + (struct cpe_send_msg *)command_node->data; + u32 size = m->size; + + if (t_info->pending) { + pr_debug("%s: message queued\n", __func__); + *cpe_rc = CPE_SVC_SUCCESS; + return CPE_PROC_QUEUED; + } + + pr_debug("%s: Send CMI message, size = %u\n", + __func__, size); + + if (size <= t_info->tgt->tgt_get_cpe_info()->inbox_size) { + pr_debug("%s: Msg fits mailbox, size %u\n", + __func__, size); + cpe_change_state(t_info, CPE_STATE_SENDING_MSG, + CPE_SS_MSG_SEND_INBOX); + t_info->pending = m; + *cpe_rc = cpe_send_msg_to_inbox(t_info, 0, m); + } else if (size < CPE_MSG_BUFFER_SIZE) { + m->address = cpe_d.cpe_msg_buffer; + pr_debug("%s: Message req CMI mem access\n", + __func__); + t_info->pending = m; + cpe_change_state(t_info, CPE_STATE_SENDING_MSG, + CPE_SS_MSG_REQUEST_ACCESS); + *cpe_rc = cpe_send_msg_to_inbox(t_info, + CPE_CORE_SVC_CMD_DRAM_ACCESS_REQ, m); + } else { + pr_debug("%s: Invalid msg size %u\n", + __func__, size); + cpe_command_cleanup(command_node); + rc = CPE_PROC_FAILED; + cpe_change_state(t_info, CPE_STATE_IDLE, + CPE_SS_IDLE); + } + + return rc; +} + +static enum cpe_process_result cpe_process_incoming( + struct cpe_info *t_info) +{ + enum cpe_process_result rc = CPE_PROC_FAILED; + struct cmi_hdr *hdr; + + hdr = CMI_GET_HEADER(t_info->tgt->outbox); + + if (CMI_HDR_GET_SERVICE(hdr) == + CMI_CPE_CORE_SERVICE_ID) { + pr_debug("%s: core service message received\n", + __func__); + + switch (CMI_GET_OPCODE(t_info->tgt->outbox)) { + case CPE_CORE_SVC_CMD_CLK_FREQ_REQUEST: + cpe_process_clk_change_req(t_info); + rc = CPE_PROC_SUCCESS; + break; + case CMI_MSG_TRANSPORT: + pr_debug("%s: transport msg received\n", + __func__); + rc = CPE_PROC_SUCCESS; + break; + case CPE_CMI_BASIC_RSP_OPCODE: + pr_debug("%s: received basic rsp\n", + __func__); + rc = CPE_PROC_SUCCESS; + break; + default: + pr_debug("%s: unknown message received\n", + __func__); + break; + } + } else { + /* if service id if for a CMI client, notify client */ + pr_debug("%s: Message received, notifying client\n", + __func__); + cpe_notify_cmi_client(t_info, + t_info->tgt->outbox, CPE_SVC_SUCCESS); + rc = CPE_PROC_SUCCESS; + } + + return rc; +} + +static enum cpe_process_result cpe_process_kill_thread( + struct cpe_info *t_info, + struct cpe_command_node *command_node) +{ + struct cpe_svc_notification payload; + + cpe_d.cpe_msg_buffer = 0; + payload.result = CPE_SVC_SHUTTING_DOWN; + payload.event = CPE_SVC_OFFLINE; + payload.payload = NULL; + payload.private_data = t_info->client_context; + /* + * Make state as offline before broadcasting + * the message to clients. + */ + cpe_change_state(t_info, CPE_STATE_OFFLINE, + CPE_SS_IDLE); + cpe_broadcast_notification(t_info, &payload); + + return CPE_PROC_KILLED; +} + +static enum cpe_process_result cpe_mt_process_cmd( + struct cpe_command_node *command_node) +{ + struct cpe_info *t_info = cpe_d.cpe_default_handle; + enum cpe_svc_result cpe_rc = CPE_SVC_SUCCESS; + enum cpe_process_result rc = CPE_PROC_SUCCESS; + struct cpe_send_msg *m; + struct cmi_hdr *hdr; + u8 service = 0; + u8 retries = 0; + + if (!t_info || !command_node) { + pr_err("%s: Invalid handle/command node\n", + __func__); + return CPE_PROC_FAILED; + } + + pr_debug("%s: cmd = %u\n", __func__, command_node->command); + + cpe_rc = cpe_is_command_valid(t_info, command_node->command); + + if (cpe_rc != CPE_SVC_SUCCESS) { + pr_err("%s: Invalid command %d, err = %d\n", + __func__, command_node->command, cpe_rc); + return CPE_PROC_FAILED; + } + + switch (command_node->command) { + + case CPE_CMD_BOOT_INITIALIZE: + rc = cpe_boot_initialize(t_info, &cpe_rc); + break; + + case CPE_CMD_BOOT_COMPLETE: + rc = cpe_boot_complete(t_info); + break; + + case CPE_CMD_SEND_MSG: + rc = cpe_process_send_msg(t_info, &cpe_rc, + command_node); + break; + + case CPE_CMD_SEND_TRANS_MSG: + m = (struct cpe_send_msg *)command_node->data; + + while (retries < CPE_SVC_INACTIVE_STATE_RETRIES_MAX) { + if (t_info->tgt->tgt_is_active()) { + ++retries; + /* Wait for CPE to be inactive */ + usleep_range(5000, 5100); + } else { + break; + } + } + + pr_debug("%s: cpe inactive after %d attempts\n", + __func__, retries); + + cpe_change_state(t_info, CPE_STATE_SENDING_MSG, + CPE_SS_MSG_SEND_INBOX); + rc = cpe_send_msg_to_inbox(t_info, 0, m); + break; + + case CPE_CMD_SEND_MSG_COMPLETE: + hdr = CMI_GET_HEADER(t_info->tgt->outbox); + service = CMI_HDR_GET_SERVICE(hdr); + pr_debug("%s: msg send success, notifying clients\n", + __func__); + cpe_command_cleanup(command_node); + t_info->pending = NULL; + cpe_change_state(t_info, + CPE_STATE_IDLE, CPE_SS_IDLE); + cpe_notify_cmi_client(t_info, + t_info->tgt->outbox, CPE_SVC_SUCCESS); + break; + + case CPE_CMD_PROC_INCOMING_MSG: + rc = cpe_process_incoming(t_info); + break; + + case CPE_CMD_KILL_THREAD: + rc = cpe_process_kill_thread(t_info, command_node); + break; + + default: + pr_err("%s: unhandled cpe cmd = %d\n", + __func__, command_node->command); + break; + } + + if (cpe_rc != CPE_SVC_SUCCESS) { + pr_err("%s: failed to execute command\n", __func__); + if (t_info->pending) { + m = (struct cpe_send_msg *)t_info->pending; + cpe_notify_cmi_client(t_info, m->payload, + CPE_SVC_FAILED); + t_info->pending = NULL; + } + + cpe_command_cleanup(command_node); + rc = CPE_PROC_FAILED; + cpe_change_state(t_info, CPE_STATE_IDLE, + CPE_SS_IDLE); + } + + return rc; +} + +static enum cpe_svc_result cpe_mt_validate_cmd( + const struct cpe_info *t_info, + enum cpe_command command) +{ + enum cpe_svc_result rc = CPE_SVC_SUCCESS; + + if ((t_info == NULL) || t_info->initialized == false) { + pr_err("%s: cpe service is not ready\n", + __func__); + return CPE_SVC_NOT_READY; + } + + switch (t_info->state) { + case CPE_STATE_UNINITIALIZED: + case CPE_STATE_INITIALIZED: + switch (command) { + case CPE_CMD_RESET: + case CPE_CMD_DL_SEGMENT: + case CPE_CMD_RAMDUMP: + case CPE_CMD_PROCESS_IRQ: + case CPE_CMD_KILL_THREAD: + case CPE_CMD_DEINITIALIZE: + case CPE_CMD_FTM_TEST: + rc = CPE_SVC_SUCCESS; + break; + default: + rc = CPE_SVC_NOT_READY; + break; + } + break; + + case CPE_STATE_DOWNLOADING: + switch (command) { + case CPE_CMD_RESET: + case CPE_CMD_DL_SEGMENT: + case CPE_CMD_BOOT: + case CPE_CMD_FTM_TEST: + rc = CPE_SVC_SUCCESS; + break; + default: + rc = CPE_SVC_NOT_READY; + break; + } + break; + + case CPE_STATE_BOOTING: + switch (command) { + case CPE_CMD_PROCESS_IRQ: + case CPE_CMD_BOOT_INITIALIZE: + case CPE_CMD_BOOT_COMPLETE: + case CPE_CMD_SHUTDOWN: + rc = CPE_SVC_SUCCESS; + break; + case CPE_CMD_FTM_TEST: + rc = CPE_SVC_BUSY; + break; + default: + rc = CPE_SVC_NOT_READY; + break; + } + break; + + case CPE_STATE_IDLE: + switch (command) { + case CPE_CMD_SEND_MSG: + case CPE_CMD_SEND_TRANS_MSG: + case CPE_CMD_SEND_MSG_COMPLETE: + case CPE_CMD_PROCESS_IRQ: + case CPE_CMD_RESET: + case CPE_CMD_SHUTDOWN: + case CPE_CMD_KILL_THREAD: + case CPE_CMD_PROC_INCOMING_MSG: + rc = CPE_SVC_SUCCESS; + break; + case CPE_CMD_FTM_TEST: + rc = CPE_SVC_BUSY; + break; + default: + rc = CPE_SVC_FAILED; + break; + } + break; + + case CPE_STATE_SENDING_MSG: + switch (command) { + case CPE_CMD_SEND_MSG: + case CPE_CMD_SEND_TRANS_MSG: + case CPE_CMD_SEND_MSG_COMPLETE: + case CPE_CMD_PROCESS_IRQ: + case CPE_CMD_SHUTDOWN: + case CPE_CMD_KILL_THREAD: + case CPE_CMD_PROC_INCOMING_MSG: + rc = CPE_SVC_SUCCESS; + break; + case CPE_CMD_FTM_TEST: + rc = CPE_SVC_BUSY; + break; + default: + rc = CPE_SVC_FAILED; + break; + } + break; + + case CPE_STATE_OFFLINE: + switch (command) { + case CPE_CMD_RESET: + case CPE_CMD_RAMDUMP: + case CPE_CMD_KILL_THREAD: + rc = CPE_SVC_SUCCESS; + break; + default: + rc = CPE_SVC_NOT_READY; + break; + } + break; + + default: + pr_debug("%s: unhandled state %d\n", + __func__, t_info->state); + break; + } + + if (rc != CPE_SVC_SUCCESS) + pr_err("%s: invalid command %d, state = %d\n", + __func__, command, t_info->state); + return rc; +} + +void *cpe_svc_initialize( + void irq_control_callback(u32 enable), + const void *codec_info, void *context) +{ + struct cpe_info *t_info = NULL; + const struct cpe_svc_hw_cfg *cap = NULL; + enum cpe_svc_result rc = CPE_SVC_SUCCESS; + struct cpe_svc_init_param *init_context = + (struct cpe_svc_init_param *) context; + void *client_context = NULL; + + if (cpe_d.cpe_default_handle && + cpe_d.cpe_default_handle->initialized == true) + return (void *)cpe_d.cpe_default_handle; + cpe_d.cpe_query_freq_plans_cb = NULL; + cpe_d.cpe_change_freq_plan_cb = NULL; + + if (context) { + client_context = init_context->context; + switch (init_context->version) { + case CPE_SVC_INIT_PARAM_V1: + cpe_d.cpe_query_freq_plans_cb = + init_context->query_freq_plans_cb; + cpe_d.cpe_change_freq_plan_cb = + init_context->change_freq_plan_cb; + break; + default: + break; + } + } + + if (!cpe_d.cpe_default_handle) { + cpe_d.cpe_default_handle = kzalloc(sizeof(struct cpe_info), + GFP_KERNEL); + if (!cpe_d.cpe_default_handle) + goto err_register; + + memset(cpe_d.cpe_default_handle, 0, + sizeof(struct cpe_info)); + } + + t_info = cpe_d.cpe_default_handle; + t_info->client_context = client_context; + + INIT_LIST_HEAD(&t_info->client_list); + cpe_d.cdc_priv = client_context; + INIT_WORK(&t_info->clk_plan_work, cpe_clk_plan_work); + init_completion(&t_info->core_svc_cmd_compl); + + t_info->tgt = kzalloc(sizeof(struct cpe_svc_tgt_abstraction), + GFP_KERNEL); + if (!t_info->tgt) + goto err_tgt_alloc; + t_info->codec_id = + ((struct cpe_svc_codec_info_v1 *) codec_info)->id; + + rc = cpe_svc_tgt_init((struct cpe_svc_codec_info_v1 *)codec_info, + t_info->tgt); + + if (rc != CPE_SVC_SUCCESS) + goto err_tgt_init; + + cap = t_info->tgt->tgt_get_cpe_info(); + + memset(t_info->tgt->outbox, 0, cap->outbox_size); + memset(t_info->tgt->inbox, 0, cap->inbox_size); + mutex_init(&t_info->msg_lock); + cpe_d.cpe_irq_control_callback = irq_control_callback; + t_info->cpe_process_command = cpe_mt_process_cmd; + t_info->cpe_cmd_validate = cpe_mt_validate_cmd; + t_info->cpe_start_notification = broadcast_boot_event; + mutex_init(&cpe_d.cpe_api_mutex); + mutex_init(&cpe_d.cpe_svc_lock); + pr_debug("%s: cpe services initialized\n", __func__); + t_info->state = CPE_STATE_INITIALIZED; + t_info->initialized = true; + + return t_info; + +err_tgt_init: + kfree(t_info->tgt); + +err_tgt_alloc: + kfree(cpe_d.cpe_default_handle); + cpe_d.cpe_default_handle = NULL; + +err_register: + return NULL; +} + +enum cpe_svc_result cpe_svc_deinitialize(void *cpe_handle) +{ + enum cpe_svc_result rc = CPE_SVC_SUCCESS; + struct cpe_info *t_info = (struct cpe_info *)cpe_handle; + + if (!t_info) + t_info = cpe_d.cpe_default_handle; + + rc = cpe_is_command_valid(t_info, CPE_CMD_DEINITIALIZE); + + if (rc != CPE_SVC_SUCCESS) { + pr_err("%s: Invalid command %d\n", + __func__, CPE_CMD_DEINITIALIZE); + return rc; + } + + if (cpe_d.cpe_default_handle == t_info) + cpe_d.cpe_default_handle = NULL; + + t_info->tgt->tgt_deinit(t_info->tgt); + cpe_change_state(t_info, CPE_STATE_UNINITIALIZED, + CPE_SS_IDLE); + mutex_destroy(&t_info->msg_lock); + kfree(t_info->tgt); + kfree(t_info); + mutex_destroy(&cpe_d.cpe_api_mutex); + mutex_destroy(&cpe_d.cpe_svc_lock); + + return rc; +} + +void *cpe_svc_register(void *cpe_handle, + void (*notification_callback) + (const struct cpe_svc_notification *parameter), + u32 mask, const char *name) +{ + void *reg_handle; + + CPE_SVC_GRAB_LOCK(&cpe_d.cpe_api_mutex, "cpe_api"); + if (!cpe_d.cpe_default_handle) { + cpe_d.cpe_default_handle = kzalloc(sizeof(struct cpe_info), + GFP_KERNEL); + if (!cpe_d.cpe_default_handle) { + CPE_SVC_REL_LOCK(&cpe_d.cpe_api_mutex, "cpe_api"); + return NULL; + } + + memset(cpe_d.cpe_default_handle, 0, + sizeof(struct cpe_info)); + } + + if (!cpe_handle) + cpe_handle = cpe_d.cpe_default_handle; + + reg_handle = cpe_register_generic((struct cpe_info *)cpe_handle, + notification_callback, + NULL, + mask, CPE_NO_SERVICE, name); + CPE_SVC_REL_LOCK(&cpe_d.cpe_api_mutex, "cpe_api"); + + return reg_handle; +} + +enum cpe_svc_result cpe_svc_deregister(void *cpe_handle, void *reg_handle) +{ + enum cpe_svc_result rc; + + CPE_SVC_GRAB_LOCK(&cpe_d.cpe_api_mutex, "cpe_api"); + if (!cpe_handle) + cpe_handle = cpe_d.cpe_default_handle; + + rc = cpe_deregister_generic((struct cpe_info *)cpe_handle, + reg_handle); + CPE_SVC_REL_LOCK(&cpe_d.cpe_api_mutex, "cpe_api"); + + return rc; +} + +enum cpe_svc_result cpe_svc_download_segment(void *cpe_handle, + const struct cpe_svc_mem_segment *segment) +{ + enum cpe_svc_result rc = CPE_SVC_SUCCESS; + struct cpe_info *t_info = (struct cpe_info *)cpe_handle; + + CPE_SVC_GRAB_LOCK(&cpe_d.cpe_api_mutex, "cpe_api"); + if (!t_info) + t_info = cpe_d.cpe_default_handle; + + rc = cpe_is_command_valid(t_info, CPE_CMD_DL_SEGMENT); + + if (rc != CPE_SVC_SUCCESS) { + pr_err("%s: cmd validation fail, cmd = %d\n", + __func__, CPE_CMD_DL_SEGMENT); + CPE_SVC_REL_LOCK(&cpe_d.cpe_api_mutex, "cpe_api"); + return rc; + } + + cpe_toggle_irq_notification(t_info, false); + t_info->state = CPE_STATE_DOWNLOADING; + t_info->substate = CPE_SS_DL_DOWNLOADING; + rc = t_info->tgt->tgt_write_ram(t_info, segment); + cpe_toggle_irq_notification(t_info, true); + CPE_SVC_REL_LOCK(&cpe_d.cpe_api_mutex, "cpe_api"); + + return rc; +} + +enum cpe_svc_result cpe_svc_boot(void *cpe_handle, int debug_mode) +{ + enum cpe_svc_result rc = CPE_SVC_SUCCESS; + struct cpe_info *t_info = (struct cpe_info *)cpe_handle; + + CPE_SVC_GRAB_LOCK(&cpe_d.cpe_api_mutex, "cpe_api"); + if (!t_info) + t_info = cpe_d.cpe_default_handle; + + rc = cpe_is_command_valid(t_info, CPE_CMD_BOOT); + + if (rc != CPE_SVC_SUCCESS) { + pr_err("%s: cmd validation fail, cmd = %d\n", + __func__, CPE_CMD_BOOT); + CPE_SVC_REL_LOCK(&cpe_d.cpe_api_mutex, "cpe_api"); + return rc; + } + + if (rc == CPE_SVC_SUCCESS) { + t_info->tgt->tgt_boot(debug_mode); + t_info->state = CPE_STATE_BOOTING; + t_info->substate = CPE_SS_BOOT; + pr_debug("%s: cpe service booting\n", + __func__); + } + + CPE_SVC_REL_LOCK(&cpe_d.cpe_api_mutex, "cpe_api"); + return rc; +} + +enum cpe_svc_result cpe_svc_process_irq(void *cpe_handle, u32 cpe_irq) +{ + enum cpe_svc_result rc = CPE_SVC_SUCCESS; + struct cpe_info *t_info = (struct cpe_info *)cpe_handle; + + if (!t_info) + t_info = cpe_d.cpe_default_handle; + + cpe_toggle_irq_notification(t_info, false); + cpe_process_irq_int(cpe_irq, t_info); + cpe_toggle_irq_notification(t_info, true); + + return rc; +} + +enum cpe_svc_result cpe_svc_route_notification(void *cpe_handle, + enum cpe_svc_module module, enum cpe_svc_route_dest dest) +{ + struct cpe_info *t_info = (struct cpe_info *)cpe_handle; + enum cpe_svc_result rc = CPE_SVC_NOT_READY; + + CPE_SVC_GRAB_LOCK(&cpe_d.cpe_api_mutex, "cpe_api"); + if (!t_info) + t_info = cpe_d.cpe_default_handle; + + if (t_info->tgt) + rc = t_info->tgt->tgt_route_notification(module, dest); + + CPE_SVC_REL_LOCK(&cpe_d.cpe_api_mutex, "cpe_api"); + return rc; +} + +static enum cpe_svc_result __cpe_svc_shutdown(void *cpe_handle) +{ + enum cpe_svc_result rc = CPE_SVC_SUCCESS; + struct cpe_info *t_info = (struct cpe_info *)cpe_handle; + struct cpe_command_node *n = NULL; + struct cpe_command_node kill_cmd; + + if (!t_info) + t_info = cpe_d.cpe_default_handle; + + rc = cpe_is_command_valid(t_info, CPE_CMD_SHUTDOWN); + + if (rc != CPE_SVC_SUCCESS) { + pr_err("%s: cmd validation fail, cmd = %d\n", + __func__, CPE_CMD_SHUTDOWN); + return rc; + } + + while (!list_empty(&t_info->main_queue)) { + n = list_first_entry(&t_info->main_queue, + struct cpe_command_node, list); + + if (n->command == CPE_CMD_SEND_MSG) { + cpe_notify_cmi_client(t_info, (u8 *)n->data, + CPE_SVC_SHUTTING_DOWN); + } + /* + * Since command cannot be processed, + * delete it from the list and perform cleanup + */ + list_del(&n->list); + cpe_command_cleanup(n); + kfree(n); + } + + pr_debug("%s: cpe service OFFLINE state\n", __func__); + + t_info->state = CPE_STATE_OFFLINE; + t_info->substate = CPE_SS_IDLE; + + memset(&kill_cmd, 0, sizeof(kill_cmd)); + kill_cmd.command = CPE_CMD_KILL_THREAD; + + if (t_info->pending) { + struct cpe_send_msg *m = + (struct cpe_send_msg *)t_info->pending; + cpe_notify_cmi_client(t_info, m->payload, + CPE_SVC_SHUTTING_DOWN); + kfree(t_info->pending); + t_info->pending = NULL; + } + + cpe_cleanup_worker_thread(t_info); + t_info->cpe_process_command(&kill_cmd); + + return rc; +} + +enum cpe_svc_result cpe_svc_shutdown(void *cpe_handle) +{ + enum cpe_svc_result rc = CPE_SVC_SUCCESS; + + CPE_SVC_GRAB_LOCK(&cpe_d.cpe_api_mutex, "cpe_api"); + rc = __cpe_svc_shutdown(cpe_handle); + CPE_SVC_REL_LOCK(&cpe_d.cpe_api_mutex, "cpe_api"); + return rc; +} + +enum cpe_svc_result cpe_svc_reset(void *cpe_handle) +{ + enum cpe_svc_result rc = CPE_SVC_SUCCESS; + struct cpe_info *t_info = (struct cpe_info *)cpe_handle; + + CPE_SVC_GRAB_LOCK(&cpe_d.cpe_api_mutex, "cpe_api"); + if (!t_info) + t_info = cpe_d.cpe_default_handle; + + rc = cpe_is_command_valid(t_info, CPE_CMD_RESET); + + if (rc != CPE_SVC_SUCCESS) { + pr_err("%s: cmd validation fail, cmd = %d\n", + __func__, CPE_CMD_RESET); + CPE_SVC_REL_LOCK(&cpe_d.cpe_api_mutex, "cpe_api"); + return rc; + } + + if (t_info && t_info->tgt) { + rc = t_info->tgt->tgt_reset(); + pr_debug("%s: cpe services in INITIALIZED state\n", + __func__); + t_info->state = CPE_STATE_INITIALIZED; + t_info->substate = CPE_SS_IDLE; + } + CPE_SVC_REL_LOCK(&cpe_d.cpe_api_mutex, "cpe_api"); + + return rc; +} + +enum cpe_svc_result cpe_svc_ramdump(void *cpe_handle, + struct cpe_svc_mem_segment *buffer) +{ + enum cpe_svc_result rc = CPE_SVC_SUCCESS; + struct cpe_info *t_info = (struct cpe_info *)cpe_handle; + + CPE_SVC_GRAB_LOCK(&cpe_d.cpe_api_mutex, "cpe_api"); + if (!t_info) + t_info = cpe_d.cpe_default_handle; + + rc = cpe_is_command_valid(t_info, CPE_CMD_RAMDUMP); + if (rc != CPE_SVC_SUCCESS) { + pr_err("%s: cmd validation fail, cmd = %d\n", + __func__, CPE_CMD_RAMDUMP); + CPE_SVC_REL_LOCK(&cpe_d.cpe_api_mutex, "cpe_api"); + return rc; + } + + if (t_info->tgt) { + rc = t_info->tgt->tgt_read_ram(t_info, buffer); + } else { + pr_err("%s: cpe service not ready\n", __func__); + rc = CPE_SVC_NOT_READY; + } + CPE_SVC_REL_LOCK(&cpe_d.cpe_api_mutex, "cpe_api"); + + return rc; +} + +enum cpe_svc_result cpe_svc_set_debug_mode(void *cpe_handle, u32 mode) +{ + struct cpe_info *t_info = (struct cpe_info *)cpe_handle; + enum cpe_svc_result rc = CPE_SVC_INVALID_HANDLE; + + CPE_SVC_GRAB_LOCK(&cpe_d.cpe_api_mutex, "cpe_api"); + if (!t_info) + t_info = cpe_d.cpe_default_handle; + + if (t_info->tgt) + rc = t_info->tgt->tgt_set_debug_mode(mode); + CPE_SVC_REL_LOCK(&cpe_d.cpe_api_mutex, "cpe_api"); + + return rc; +} + +const struct cpe_svc_hw_cfg *cpe_svc_get_hw_cfg(void *cpe_handle) +{ + struct cpe_info *t_info = (struct cpe_info *)cpe_handle; + + if (!t_info) + t_info = cpe_d.cpe_default_handle; + + if (t_info->tgt) + return t_info->tgt->tgt_get_cpe_info(); + + return NULL; +} + +void *cmi_register( + void notification_callback( + const struct cmi_api_notification *parameter), + u32 service) +{ + void *reg_handle = NULL; + + CPE_SVC_GRAB_LOCK(&cpe_d.cpe_api_mutex, "cpe_api"); + reg_handle = cpe_register_generic(cpe_d.cpe_default_handle, + NULL, + notification_callback, + (CPE_SVC_CMI_MSG | CPE_SVC_OFFLINE | + CPE_SVC_ONLINE), + service, + "CMI_CLIENT"); + CPE_SVC_REL_LOCK(&cpe_d.cpe_api_mutex, "cpe_api"); + + return reg_handle; +} + +enum cmi_api_result cmi_deregister(void *reg_handle) +{ + u32 clients = 0; + struct cpe_notif_node *n = NULL; + enum cmi_api_result rc = CMI_API_SUCCESS; + struct cpe_svc_notification payload; + + CPE_SVC_GRAB_LOCK(&cpe_d.cpe_api_mutex, "cpe_api"); + rc = (enum cmi_api_result) cpe_deregister_generic( + cpe_d.cpe_default_handle, reg_handle); + + CPE_SVC_GRAB_LOCK(&cpe_d.cpe_svc_lock, "cpe_svc"); + list_for_each_entry(n, &cpe_d.cpe_default_handle->client_list, list) { + if (n->mask & CPE_SVC_CMI_MSG) + clients++; + } + CPE_SVC_REL_LOCK(&cpe_d.cpe_svc_lock, "cpe_svc"); + + if (clients == 0) { + payload.event = CPE_SVC_CMI_CLIENTS_DEREG; + payload.payload = NULL; + payload.result = CPE_SVC_SUCCESS; + cpe_broadcast_notification(cpe_d.cpe_default_handle, &payload); + } + + CPE_SVC_REL_LOCK(&cpe_d.cpe_api_mutex, "cpe_api"); + return rc; +} + +enum cmi_api_result cmi_send_msg(void *message) +{ + enum cmi_api_result rc = CMI_API_SUCCESS; + struct cpe_send_msg *msg = NULL; + struct cmi_hdr *hdr; + + CPE_SVC_GRAB_LOCK(&cpe_d.cpe_api_mutex, "cpe_api"); + hdr = CMI_GET_HEADER(message); + msg = kzalloc(sizeof(struct cpe_send_msg), + GFP_ATOMIC); + if (!msg) { + CPE_SVC_REL_LOCK(&cpe_d.cpe_api_mutex, "cpe_api"); + return CPE_SVC_NO_MEMORY; + } + + if (CMI_HDR_GET_OBM_FLAG(hdr) == CMI_OBM_FLAG_OUT_BAND) + msg->isobm = 1; + else + msg->isobm = 0; + + msg->size = sizeof(struct cmi_hdr) + + CMI_HDR_GET_PAYLOAD_SIZE(hdr); + + msg->payload = kzalloc(msg->size, GFP_ATOMIC); + if (!msg->payload) { + kfree(msg); + CPE_SVC_REL_LOCK(&cpe_d.cpe_api_mutex, "cpe_api"); + return CPE_SVC_NO_MEMORY; + } + + msg->address = 0; + memcpy((void *)msg->payload, message, msg->size); + + rc = (enum cmi_api_result) cpe_send_cmd_to_thread( + cpe_d.cpe_default_handle, + CPE_CMD_SEND_MSG, + (void *)msg, false); + + if (rc != 0) { + pr_err("%s: Failed to queue message\n", __func__); + kfree(msg->payload); + kfree(msg); + } + + CPE_SVC_REL_LOCK(&cpe_d.cpe_api_mutex, "cpe_api"); + return rc; +} + +enum cpe_svc_result cpe_svc_ftm_test(void *cpe_handle, u32 *status) +{ + enum cpe_svc_result rc = CPE_SVC_SUCCESS; + struct cpe_info *t_info = (struct cpe_info *)cpe_handle; + struct cpe_svc_mem_segment backup_seg; + struct cpe_svc_mem_segment waiti_seg; + u8 *backup_data = NULL; + + CPE_SVC_GRAB_LOCK(&cpe_d.cpe_api_mutex, "cpe_api"); + if (!t_info) + t_info = cpe_d.cpe_default_handle; + + rc = cpe_is_command_valid(t_info, CPE_CMD_FTM_TEST); + if (rc != CPE_SVC_SUCCESS) { + pr_err("%s: cmd validation fail, cmd = %d\n", + __func__, CPE_CMD_FTM_TEST); + goto fail_cmd; + } + + if (t_info && t_info->tgt) { + backup_data = kzalloc( + t_info->tgt->tgt_waiti_info->tgt_waiti_size, + GFP_KERNEL); + + /* CPE reset */ + rc = t_info->tgt->tgt_reset(); + if (rc != CPE_SVC_SUCCESS) { + pr_err("%s: CPE reset fail! err = %d\n", + __func__, rc); + goto err_return; + } + + /* Back up the 4 byte IRAM data first */ + backup_seg.type = CPE_SVC_INSTRUCTION_MEM; + backup_seg.cpe_addr = + t_info->tgt->tgt_get_cpe_info()->IRAM_offset; + backup_seg.size = t_info->tgt->tgt_waiti_info->tgt_waiti_size; + backup_seg.data = backup_data; + + pr_debug("%s: Backing up IRAM data from CPE\n", + __func__); + + rc = t_info->tgt->tgt_read_ram(t_info, &backup_seg); + if (rc != CPE_SVC_SUCCESS) { + pr_err("%s: Fail to backup CPE IRAM data, err = %d\n", + __func__, rc); + goto err_return; + } + + pr_debug("%s: Complete backing up IRAM data from CPE\n", + __func__); + + /* Write the WAITI instruction data */ + waiti_seg.type = CPE_SVC_INSTRUCTION_MEM; + waiti_seg.cpe_addr = + t_info->tgt->tgt_get_cpe_info()->IRAM_offset; + waiti_seg.size = t_info->tgt->tgt_waiti_info->tgt_waiti_size; + waiti_seg.data = t_info->tgt->tgt_waiti_info->tgt_waiti_data; + + rc = t_info->tgt->tgt_write_ram(t_info, &waiti_seg); + if (rc != CPE_SVC_SUCCESS) { + pr_err("%s: Fail to write the WAITI data, err = %d\n", + __func__, rc); + goto restore_iram; + } + + /* Boot up cpe to execute the WAITI instructions */ + rc = t_info->tgt->tgt_boot(1); + if (rc != CPE_SVC_SUCCESS) { + pr_err("%s: Fail to boot CPE, err = %d\n", + __func__, rc); + goto reset; + } + + /* + * 1ms delay is suggested by the hw team to + * wait for cpe to boot up. + */ + usleep_range(1000, 1100); + + /* Check if the cpe init is done after executing the WAITI */ + *status = t_info->tgt->tgt_cpar_init_done(); + +reset: + /* Set the cpe back to reset state */ + rc = t_info->tgt->tgt_reset(); + if (rc != CPE_SVC_SUCCESS) { + pr_err("%s: CPE reset fail! err = %d\n", + __func__, rc); + goto restore_iram; + } + +restore_iram: + /* Restore the IRAM 4 bytes data */ + rc = t_info->tgt->tgt_write_ram(t_info, &backup_seg); + if (rc != CPE_SVC_SUCCESS) { + pr_err("%s: Fail to restore the IRAM data, err = %d\n", + __func__, rc); + goto err_return; + } + } + +err_return: + kfree(backup_data); +fail_cmd: + CPE_SVC_REL_LOCK(&cpe_d.cpe_api_mutex, "cpe_api"); + return rc; +} + +static enum cpe_svc_result cpe_tgt_tomtom_boot(int debug_mode) +{ + return CPE_SVC_SUCCESS; +} + +static u32 cpe_tgt_tomtom_is_cpar_init_done(void) +{ + return 0; +} + +static u32 cpe_tgt_tomtom_is_active(void) +{ + return 0; +} + +static enum cpe_svc_result cpe_tgt_tomtom_reset(void) +{ + return CPE_SVC_SUCCESS; +} + +enum cpe_svc_result cpe_tgt_tomtom_voicetx(bool enable) +{ + return CPE_SVC_SUCCESS; +} + +enum cpe_svc_result cpe_svc_toggle_lab(void *cpe_handle, bool enable) +{ + + struct cpe_info *t_info = (struct cpe_info *)cpe_handle; + + if (!t_info) + t_info = cpe_d.cpe_default_handle; + + if (t_info->tgt) + return t_info->tgt->tgt_voice_tx_lab(enable); + else + return CPE_SVC_INVALID_HANDLE; +} + +static enum cpe_svc_result cpe_tgt_tomtom_read_mailbox(u8 *buffer, + size_t size) +{ + return CPE_SVC_SUCCESS; +} + +static enum cpe_svc_result cpe_tgt_tomtom_write_mailbox(u8 *buffer, + size_t size) +{ + return CPE_SVC_SUCCESS; +} + +static enum cpe_svc_result cpe_tgt_tomtom_read_RAM(struct cpe_info *t_info, + struct cpe_svc_mem_segment *mem_seg) +{ + return CPE_SVC_SUCCESS; +} + +static enum cpe_svc_result cpe_tgt_tomtom_write_RAM(struct cpe_info *t_info, + const struct cpe_svc_mem_segment *mem_seg) +{ + return CPE_SVC_SUCCESS; +} + +static enum cpe_svc_result cpe_tgt_tomtom_route_notification( + enum cpe_svc_module module, + enum cpe_svc_route_dest dest) +{ + return CPE_SVC_SUCCESS; +} + +static enum cpe_svc_result cpe_tgt_tomtom_set_debug_mode(u32 enable) +{ + return CPE_SVC_SUCCESS; +} + +static const struct cpe_svc_hw_cfg *cpe_tgt_tomtom_get_cpe_info(void) +{ + return &cpe_svc_tomtom_info; +} + +static enum cpe_svc_result cpe_tgt_tomtom_deinit( + struct cpe_svc_tgt_abstraction *param) +{ + kfree(param->inbox); + param->inbox = NULL; + kfree(param->outbox); + param->outbox = NULL; + memset(param, 0, sizeof(struct cpe_svc_tgt_abstraction)); + return CPE_SVC_SUCCESS; +} + +static u8 cpe_tgt_tomtom_waiti_data[] = {0x00, 0x70, 0x00, 0x00}; + +static struct cpe_tgt_waiti_info cpe_tgt_tomtom_waiti_info = { + .tgt_waiti_size = ARRAY_SIZE(cpe_tgt_tomtom_waiti_data), + .tgt_waiti_data = cpe_tgt_tomtom_waiti_data, +}; + +static enum cpe_svc_result cpe_tgt_tomtom_init( + struct cpe_svc_codec_info_v1 *codec_info, + struct cpe_svc_tgt_abstraction *param) +{ + if (!codec_info) + return CPE_SVC_INVALID_HANDLE; + if (!param) + return CPE_SVC_INVALID_HANDLE; + + if (codec_info->id == CPE_SVC_CODEC_TOMTOM) { + param->tgt_boot = cpe_tgt_tomtom_boot; + param->tgt_cpar_init_done = cpe_tgt_tomtom_is_cpar_init_done; + param->tgt_is_active = cpe_tgt_tomtom_is_active; + param->tgt_reset = cpe_tgt_tomtom_reset; + param->tgt_read_mailbox = cpe_tgt_tomtom_read_mailbox; + param->tgt_write_mailbox = cpe_tgt_tomtom_write_mailbox; + param->tgt_read_ram = cpe_tgt_tomtom_read_RAM; + param->tgt_write_ram = cpe_tgt_tomtom_write_RAM; + param->tgt_route_notification = + cpe_tgt_tomtom_route_notification; + param->tgt_set_debug_mode = cpe_tgt_tomtom_set_debug_mode; + param->tgt_get_cpe_info = cpe_tgt_tomtom_get_cpe_info; + param->tgt_deinit = cpe_tgt_tomtom_deinit; + param->tgt_voice_tx_lab = cpe_tgt_tomtom_voicetx; + param->tgt_waiti_info = &cpe_tgt_tomtom_waiti_info; + + param->inbox = kzalloc(TOMTOM_A_SVASS_SPE_INBOX_SIZE, + GFP_KERNEL); + if (!param->inbox) + return CPE_SVC_NO_MEMORY; + + param->outbox = kzalloc(TOMTOM_A_SVASS_SPE_OUTBOX_SIZE, + GFP_KERNEL); + if (!param->outbox) { + kfree(param->inbox); + return CPE_SVC_NO_MEMORY; + } + } + + return CPE_SVC_SUCCESS; +} + +static enum cpe_svc_result cpe_tgt_wcd9335_boot(int debug_mode) +{ + enum cpe_svc_result rc = CPE_SVC_SUCCESS; + + if (!debug_mode) + rc |= cpe_update_bits( + WCD9335_CPE_SS_WDOG_CFG, + 0x3f, 0x31); + else + pr_info("%s: CPE in debug mode, WDOG disabled\n", + __func__); + + rc |= cpe_register_write(WCD9335_CPE_SS_CPARMAD_BUFRDY_INT_PERIOD, 19); + rc |= cpe_update_bits(WCD9335_CPE_SS_CPAR_CTL, 0x04, 0x00); + rc |= cpe_update_bits(WCD9335_CPE_SS_CPAR_CTL, 0x02, 0x02); + rc |= cpe_update_bits(WCD9335_CPE_SS_CPAR_CTL, 0x01, 0x01); + + if (unlikely(rc)) { + pr_err("%s: Failed to boot, err = %d\n", + __func__, rc); + rc = CPE_SVC_FAILED; + } + + return rc; +} + +static u32 cpe_tgt_wcd9335_is_cpar_init_done(void) +{ + u8 temp = 0; + + cpe_register_read(WCD9335_CPE_SS_STATUS, &temp); + return temp & 0x1; +} + +static u32 cpe_tgt_wcd9335_is_active(void) +{ + u8 temp = 0; + + cpe_register_read(WCD9335_CPE_SS_STATUS, &temp); + return temp & 0x4; +} + +static enum cpe_svc_result cpe_tgt_wcd9335_reset(void) +{ + enum cpe_svc_result rc = CPE_SVC_SUCCESS; + + rc |= cpe_update_bits(WCD9335_CPE_SS_CPAR_CFG, 0x01, 0x00); + + rc |= cpe_register_write( + WCD9335_CODEC_RPM_PWR_CPE_IRAM_SHUTDOWN, 0x00); + rc |= cpe_register_write( + WCD9335_CODEC_RPM_PWR_CPE_DRAM1_SHUTDOWN, 0x00); + rc |= cpe_register_write( + WCD9335_CODEC_RPM_PWR_CPE_DRAM0_SHUTDOWN_1, 0x00); + rc |= cpe_register_write( + WCD9335_CODEC_RPM_PWR_CPE_DRAM0_SHUTDOWN_2, 0x00); + + rc |= cpe_update_bits(WCD9335_CPE_SS_CPAR_CTL, 0x04, 0x04); + + if (unlikely(rc)) { + pr_err("%s: failed to reset cpe, err = %d\n", + __func__, rc); + rc = CPE_SVC_FAILED; + } + + return rc; +} + +static enum cpe_svc_result cpe_tgt_wcd9335_read_mailbox(u8 *buffer, + size_t size) +{ + enum cpe_svc_result rc = CPE_SVC_SUCCESS; + u32 cnt = 0; + + pr_debug("%s: size=%zd\n", __func__, size); + + if (size > WCD9335_CPE_SS_SPE_OUTBOX_SIZE) + size = WCD9335_CPE_SS_SPE_OUTBOX_SIZE; + + for (cnt = 0; (cnt < size) && (rc == CPE_SVC_SUCCESS); cnt++) + rc = cpe_register_read(WCD9335_CPE_SS_SPE_OUTBOX1(cnt), + &buffer[cnt]); + + rc = cpe_register_write(WCD9335_CPE_SS_OUTBOX1_ACK, 0x01); + + if (unlikely(rc)) { + pr_err("%s: failed to ACK outbox, err = %d\n", + __func__, rc); + rc = CPE_SVC_FAILED; + } + + return rc; +} + +static enum cpe_svc_result cpe_tgt_wcd9335_write_mailbox(u8 *buffer, + size_t size) +{ + enum cpe_svc_result rc = CPE_SVC_SUCCESS; + u32 cnt = 0; + + pr_debug("%s: size = %zd\n", __func__, size); + if (size > WCD9335_CPE_SS_SPE_INBOX_SIZE) + size = WCD9335_CPE_SS_SPE_INBOX_SIZE; + for (cnt = 0; (cnt < size) && (rc == CPE_SVC_SUCCESS); cnt++) { + rc |= cpe_register_write(WCD9335_CPE_SS_SPE_INBOX1(cnt), + buffer[cnt]); + } + + if (unlikely(rc)) { + pr_err("%s: Error %d writing mailbox registers\n", + __func__, rc); + return rc; + } + + rc = cpe_register_write(WCD9335_CPE_SS_INBOX1_TRG, 1); + return rc; +} + +static enum cpe_svc_result cpe_wcd9335_get_mem_addr(struct cpe_info *t_info, + const struct cpe_svc_mem_segment *mem_seg, + u32 *addr, u8 *mem) +{ + enum cpe_svc_result rc = CPE_SVC_SUCCESS; + u32 offset, mem_sz, address; + u8 mem_type; + + switch (mem_seg->type) { + case CPE_SVC_DATA_MEM: + mem_type = MEM_ACCESS_DRAM_VAL; + offset = WCD9335_CPE_SS_SPE_DRAM_OFFSET; + mem_sz = WCD9335_CPE_SS_SPE_DRAM_SIZE; + break; + + case CPE_SVC_INSTRUCTION_MEM: + mem_type = MEM_ACCESS_IRAM_VAL; + offset = WCD9335_CPE_SS_SPE_IRAM_OFFSET; + mem_sz = WCD9335_CPE_SS_SPE_IRAM_SIZE; + break; + + default: + pr_err("%s: Invalid mem type = %u\n", + __func__, mem_seg->type); + return CPE_SVC_INVALID_HANDLE; + } + + if (mem_seg->cpe_addr < offset) { + pr_err("%s: Invalid addr %x for mem type %u\n", + __func__, mem_seg->cpe_addr, mem_type); + return CPE_SVC_INVALID_HANDLE; + } + + address = mem_seg->cpe_addr - offset; + if (address + mem_seg->size > mem_sz) { + pr_err("%s: wrong size %zu, start address %x, mem_type %u\n", + __func__, mem_seg->size, address, mem_type); + return CPE_SVC_INVALID_HANDLE; + } + + (*addr) = address; + (*mem) = mem_type; + + return rc; +} + +static enum cpe_svc_result cpe_tgt_wcd9335_read_RAM(struct cpe_info *t_info, + struct cpe_svc_mem_segment *mem_seg) +{ + enum cpe_svc_result rc = CPE_SVC_SUCCESS; + u8 temp = 0; + u32 cnt = 0; + u8 mem = 0x0; + u32 addr = 0; + u32 lastaddr = 0; + u32 ptr_update = true; + bool autoinc; + + if (!mem_seg) { + pr_err("%s: Invalid buffer\n", __func__); + return CPE_SVC_INVALID_HANDLE; + } + + rc = cpe_wcd9335_get_mem_addr(t_info, mem_seg, &addr, &mem); + + if (rc != CPE_SVC_SUCCESS) { + pr_err("%s: Cannot obtain address, mem_type %u\n", + __func__, mem_seg->type); + return rc; + } + + rc |= cpe_register_write(WCD9335_CPE_SS_MEM_CTRL, 0); + autoinc = cpe_register_read_autoinc_supported(); + + if (autoinc) + temp = 0x18; + else + temp = 0x10; + + temp |= mem; + + lastaddr = ~addr; + do { + if (!autoinc || (ptr_update)) { + /* write LSB only if modified */ + if ((lastaddr & 0xFF) != (addr & 0xFF)) + rc |= cpe_register_write( + WCD9335_CPE_SS_MEM_PTR_0, + (addr & 0xFF)); + /* write middle byte only if modified */ + if (((lastaddr >> 8) & 0xFF) != ((addr >> 8) & 0xFF)) + rc |= cpe_register_write( + WCD9335_CPE_SS_MEM_PTR_1, + ((addr>>8) & 0xFF)); + /* write MSB only if modified */ + if (((lastaddr >> 16) & 0xFF) != ((addr >> 16) & 0xFF)) + rc |= cpe_register_write( + WCD9335_CPE_SS_MEM_PTR_2, + ((addr>>16) & 0xFF)); + + rc |= cpe_register_write(WCD9335_CPE_SS_MEM_CTRL, temp); + lastaddr = addr; + addr++; + ptr_update = false; + } + + rc |= cpe_register_read(WCD9335_CPE_SS_MEM_BANK_0, + &mem_seg->data[cnt]); + + if (!autoinc) + rc |= cpe_register_write(WCD9335_CPE_SS_MEM_CTRL, 0); + } while ((++cnt < mem_seg->size) || + (rc != CPE_SVC_SUCCESS)); + + rc |= cpe_register_write(WCD9335_CPE_SS_MEM_CTRL, 0); + + if (rc) + pr_err("%s: Failed to read registers, err = %d\n", + __func__, rc); + + return rc; +} + +static enum cpe_svc_result cpe_tgt_wcd9335_write_RAM(struct cpe_info *t_info, + const struct cpe_svc_mem_segment *mem_seg) +{ + enum cpe_svc_result rc = CPE_SVC_SUCCESS; + u8 mem_reg_val = 0; + u8 mem = MEM_ACCESS_NONE_VAL; + u32 addr = 0; + u8 *temp_ptr = NULL; + u32 temp_size = 0; + bool autoinc; + + if (!mem_seg) { + pr_err("%s: Invalid mem segment\n", + __func__); + return CPE_SVC_INVALID_HANDLE; + } + + rc = cpe_wcd9335_get_mem_addr(t_info, mem_seg, &addr, &mem); + + if (rc != CPE_SVC_SUCCESS) { + pr_err("%s: Cannot obtain address, mem_type %u\n", + __func__, mem_seg->type); + return rc; + } + + autoinc = cpe_register_read_autoinc_supported(); + if (autoinc) + mem_reg_val = 0x18; + else + mem_reg_val = 0x10; + + mem_reg_val |= mem; + + rc = cpe_update_bits(WCD9335_CPE_SS_MEM_CTRL, + 0x0F, mem_reg_val); + + rc = cpe_register_write(WCD9335_CPE_SS_MEM_PTR_0, + (addr & 0xFF)); + rc = cpe_register_write(WCD9335_CPE_SS_MEM_PTR_1, + ((addr >> 8) & 0xFF)); + + rc = cpe_register_write(WCD9335_CPE_SS_MEM_PTR_2, + ((addr >> 16) & 0xFF)); + + temp_size = 0; + temp_ptr = mem_seg->data; + + while (temp_size <= mem_seg->size) { + u32 to_write = (mem_seg->size >= temp_size+CHUNK_SIZE) + ? CHUNK_SIZE : (mem_seg->size - temp_size); + + if (t_info->state == CPE_STATE_OFFLINE) { + pr_err("%s: CPE is offline\n", __func__); + return CPE_SVC_FAILED; + } + + cpe_register_write_repeat(WCD9335_CPE_SS_MEM_BANK_0, + temp_ptr, to_write); + temp_size += CHUNK_SIZE; + temp_ptr += CHUNK_SIZE; + } + + rc = cpe_register_write(WCD9335_CPE_SS_MEM_CTRL, 0); + + if (rc) + pr_err("%s: Failed to write registers, err = %d\n", + __func__, rc); + return rc; +} + +static enum cpe_svc_result cpe_tgt_wcd9335_route_notification( + enum cpe_svc_module module, + enum cpe_svc_route_dest dest) +{ + enum cpe_svc_result rc = CPE_SVC_SUCCESS; + + pr_debug("%s: Module = %d, Destination = %d\n", + __func__, module, dest); + + switch (module) { + case CPE_SVC_LISTEN_PROC: + switch (dest) { + case CPE_SVC_EXTERNAL: + rc = cpe_update_bits(WCD9335_CPE_SS_CFG, 0x01, 0x01); + break; + case CPE_SVC_INTERNAL: + rc = cpe_update_bits(WCD9335_CPE_SS_CFG, 0x01, 0x00); + break; + default: + pr_err("%s: Invalid destination %d\n", + __func__, dest); + return CPE_SVC_FAILED; + } + break; + default: + pr_err("%s: Invalid module %d\n", + __func__, module); + rc = CPE_SVC_FAILED; + break; + } + return rc; +} + +static enum cpe_svc_result cpe_tgt_wcd9335_set_debug_mode(u32 enable) +{ + enum cpe_svc_result rc = CPE_SVC_SUCCESS; + + pr_debug("%s: enable = %s\n", __func__, + (enable) ? "true" : "false"); + + return rc; +} + +static const struct cpe_svc_hw_cfg *cpe_tgt_wcd9335_get_cpe_info(void) +{ + return &cpe_svc_wcd9335_info; +} + +static enum cpe_svc_result +cpe_tgt_wcd9335_deinit(struct cpe_svc_tgt_abstraction *param) +{ + kfree(param->inbox); + param->inbox = NULL; + kfree(param->outbox); + param->outbox = NULL; + memset(param, 0, sizeof(struct cpe_svc_tgt_abstraction)); + + return CPE_SVC_SUCCESS; +} + +static enum cpe_svc_result + cpe_tgt_wcd9335_voicetx(bool enable) +{ + enum cpe_svc_result rc = CPE_SVC_SUCCESS; + u8 val = 0; + + pr_debug("%s: enable = %u\n", __func__, enable); + if (enable) + val = 0x02; + else + val = 0x00; + + rc = cpe_update_bits(WCD9335_CPE_SS_CFG, 0x02, val); + val = 0; + cpe_register_read(WCD9335_CPE_SS_CFG, &val); + + return rc; +} + +static u8 cpe_tgt_wcd9335_waiti_data[] = {0x00, 0x70, 0x00, 0x00}; + +static struct cpe_tgt_waiti_info cpe_tgt_wcd9335_waiti_info = { + .tgt_waiti_size = ARRAY_SIZE(cpe_tgt_wcd9335_waiti_data), + .tgt_waiti_data = cpe_tgt_wcd9335_waiti_data, +}; + +static enum cpe_svc_result cpe_tgt_wcd9335_init( + struct cpe_svc_codec_info_v1 *codec_info, + struct cpe_svc_tgt_abstraction *param) +{ + if (!codec_info) + return CPE_SVC_INVALID_HANDLE; + if (!param) + return CPE_SVC_INVALID_HANDLE; + + if (codec_info->id == CPE_SVC_CODEC_WCD9335) { + param->tgt_boot = cpe_tgt_wcd9335_boot; + param->tgt_cpar_init_done = cpe_tgt_wcd9335_is_cpar_init_done; + param->tgt_is_active = cpe_tgt_wcd9335_is_active; + param->tgt_reset = cpe_tgt_wcd9335_reset; + param->tgt_read_mailbox = cpe_tgt_wcd9335_read_mailbox; + param->tgt_write_mailbox = cpe_tgt_wcd9335_write_mailbox; + param->tgt_read_ram = cpe_tgt_wcd9335_read_RAM; + param->tgt_write_ram = cpe_tgt_wcd9335_write_RAM; + param->tgt_route_notification = + cpe_tgt_wcd9335_route_notification; + param->tgt_set_debug_mode = cpe_tgt_wcd9335_set_debug_mode; + param->tgt_get_cpe_info = cpe_tgt_wcd9335_get_cpe_info; + param->tgt_deinit = cpe_tgt_wcd9335_deinit; + param->tgt_voice_tx_lab = cpe_tgt_wcd9335_voicetx; + param->tgt_waiti_info = &cpe_tgt_wcd9335_waiti_info; + + param->inbox = kzalloc(WCD9335_CPE_SS_SPE_INBOX_SIZE, + GFP_KERNEL); + if (!param->inbox) + return CPE_SVC_NO_MEMORY; + + param->outbox = kzalloc(WCD9335_CPE_SS_SPE_OUTBOX_SIZE, + GFP_KERNEL); + if (!param->outbox) { + kfree(param->inbox); + return CPE_SVC_NO_MEMORY; + } + } + + return CPE_SVC_SUCCESS; +} + +MODULE_DESCRIPTION("WCD CPE Services"); +MODULE_LICENSE("GPL v2"); diff --git a/audio-kernel/asoc/codecs/wcd_cpe_services.h b/audio-kernel/asoc/codecs/wcd_cpe_services.h new file mode 100644 index 000000000..68eb61996 --- /dev/null +++ b/audio-kernel/asoc/codecs/wcd_cpe_services.h @@ -0,0 +1,179 @@ +/* Copyright (c) 2014-2015, 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 __CPE_SERVICES__ +#define __CPE_SERVICES__ + +#define CPE_IRQ_OUTBOX_IRQ 0x01 +#define CPE_IRQ_MEM_ACCESS_ERROR 0x02 +#define CPE_IRQ_WDOG_BITE 0x04 +#define CPE_IRQ_BUFFER_OVERFLOW 0x08 +#define CPE_IRQ_LAB_OVFUNF 0x10 +#define CPE_IRQ_FLL_LOCK_LOST 0x20 +#define CPE_IRQ_RCO_WDOG_INT 0x40 + +#define EFAILED (MAX_ERRNO - 1) +#define ENOTREADY (MAX_ERRNO - 2) + +#define MAX_SUPPORTED_CLKFREQ 8 +#define CPE_SVC_INIT_PARAM_V1 1 + +enum cpe_svc_result { + CPE_SVC_SUCCESS = 0, + CPE_SVC_FAILED = -EFAILED, + CPE_SVC_NO_MEMORY = -ENOMEM, + CPE_SVC_INVALID_HANDLE = -EINVAL, + CPE_SVC_NOT_READY = -ENOTREADY, + CPE_SVC_SHUTTING_DOWN = -ESHUTDOWN, + CPE_SVC_BUSY = -EBUSY, +}; + +enum cpe_svc_event { + CPE_SVC_CMI_MSG = 0x01, + CPE_SVC_OFFLINE = 0x02, + CPE_SVC_ONLINE = 0x04, + CPE_SVC_BOOT_FAILED = 0x08, + CPE_SVC_READ_COMPLETE = 0x10, + CPE_SVC_READ_ERROR = 0x20, + CPE_SVC_BOOT = 0x40, + CPE_SVC_CMI_CLIENTS_DEREG = 0x100, + CPE_SVC_EVENT_ANCHOR = 0x7FFF +}; + +enum cpe_svc_module { + CPE_SVC_LISTEN_PROC = 1, + CPE_SVC_MODULE_ANCHOR = 0x7F +}; + +enum cpe_svc_route_dest { + CPE_SVC_EXTERNAL = 1, + CPE_SVC_INTERNAL = 2, + CPE_SVC_ROUTE_ANCHOR = 0x7F +}; + +enum cpe_svc_mem_type { + CPE_SVC_DATA_MEM = 1, + CPE_SVC_INSTRUCTION_MEM = 2, + CPE_SVC_IPC_MEM = 3, + CPE_SVC_MEM_TYPE_ANCHOR = 0x7F +}; + +enum cpe_svc_codec_id { + CPE_SVC_CODEC_TOMTOM = 5, + CPE_SVC_CODEC_WCD9335 = 7, + CPE_SVC_CODEC_WCD9326 = 8, + CPE_SVC_CODEC_ID_ANCHOR = 0x7ffffff +}; + +enum cpe_svc_codec_version { + CPE_SVC_CODEC_V1P0 = 1, + CPE_SVC_CODEC_VERSION_ANCHOR = 0x7fffffff +}; + +struct cpe_svc_codec_info_v1 { + u16 major_version;/*must be 1*/ + u16 minor_version;/*must be 0*/ + u32 id; + u32 version; + /*Add 1.1 version fields after this line*/ +}; + +struct cpe_svc_notification { + enum cpe_svc_event event; + enum cpe_svc_result result; + void *payload; + void *private_data; +}; + +struct cpe_svc_msg_payload { + u8 *cmi_msg; +}; + +struct cpe_svc_read_complete { + u8 *buffer; + size_t size; +}; + +struct cpe_svc_boot_event { + u32 debug_address; + size_t debug_buffer_size; + u32 status; +}; + +struct cpe_svc_mem_segment { + enum cpe_svc_mem_type type; + u32 cpe_addr; + size_t size; + u8 *data; +}; + +struct cpe_svc_hw_cfg { + size_t DRAM_size; + u32 DRAM_offset; + size_t IRAM_size; + u32 IRAM_offset; + u8 inbox_size; + u8 outbox_size; +}; + +struct cpe_svc_cfg_clk_plan { + u32 current_clk_feq; + u32 num_clk_freqs; + u32 clk_freqs[MAX_SUPPORTED_CLKFREQ]; +}; + +struct cpe_svc_init_param { + void *context; + u32 version; + void (*query_freq_plans_cb)(void *cdc_priv, + struct cpe_svc_cfg_clk_plan *clk_freq); + void (*change_freq_plan_cb)(void *cdc_priv, + u32 clk_freq); +}; + + +void *cpe_svc_initialize( + void irq_control_callback(u32 enable), + const void *codec_info, void *context); +enum cpe_svc_result cpe_svc_deinitialize(void *cpe_handle); + +void *cpe_svc_register(void *cpe_handle, + void (*notification_callback)( + const struct cpe_svc_notification *parameter), + u32 mask, const char *name); + +enum cpe_svc_result cpe_svc_deregister(void *cpe_handle, void *reg_handle); + +enum cpe_svc_result cpe_svc_download_segment(void *cpe_handle, + const struct cpe_svc_mem_segment *segment); + +enum cpe_svc_result cpe_svc_boot(void *cpe_handle, int debug_mode); + +enum cpe_svc_result cpe_svc_shutdown(void *cpe_handle); + +enum cpe_svc_result cpe_svc_reset(void *cpe_handle); + +enum cpe_svc_result cpe_svc_process_irq(void *cpe_handle, u32 cpe_irq); + +enum cpe_svc_result +cpe_svc_route_notification(void *cpe_handle, enum cpe_svc_module module, + enum cpe_svc_route_dest dest); + +enum cpe_svc_result cpe_svc_ramdump(void *cpe_handle, + struct cpe_svc_mem_segment *buffer); + +enum cpe_svc_result cpe_svc_set_debug_mode(void *cpe_handle, u32 mode); + +const struct cpe_svc_hw_cfg *cpe_svc_get_hw_cfg(void *cpe_handle); +enum cpe_svc_result cpe_svc_toggle_lab(void *cpe_handle, bool enable); +enum cpe_svc_result cpe_svc_ftm_test(void *cpe_handle, u32 *status); +#endif /*__CPE_SERVICES__*/ diff --git a/audio-kernel/asoc/codecs/wcdcal-hwdep.c b/audio-kernel/asoc/codecs/wcdcal-hwdep.c new file mode 100644 index 000000000..31eae69b1 --- /dev/null +++ b/audio-kernel/asoc/codecs/wcdcal-hwdep.c @@ -0,0 +1,226 @@ +/* + * 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 + * 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 +#include +#include +#include +#include +#include +#include +#include +#include "wcdcal-hwdep.h" + +const int cal_size_info[WCD9XXX_MAX_CAL] = { + [WCD9XXX_ANC_CAL] = 16384, + [WCD9XXX_MBHC_CAL] = 4096, + [WCD9XXX_MAD_CAL] = 4096, + [WCD9XXX_VBAT_CAL] = 72, +}; + +const char *cal_name_info[WCD9XXX_MAX_CAL] = { + [WCD9XXX_ANC_CAL] = "anc", + [WCD9XXX_MBHC_CAL] = "mbhc", + [WCD9XXX_MAD_CAL] = "mad", + [WCD9XXX_VBAT_CAL] = "vbat", +}; + +struct firmware_cal *wcdcal_get_fw_cal(struct fw_info *fw_data, + enum wcd_cal_type type) +{ + if (!fw_data) { + pr_err("%s: fw_data is NULL\n", __func__); + return NULL; + } + if (type >= WCD9XXX_MAX_CAL || + type < WCD9XXX_MIN_CAL) { + pr_err("%s: wrong cal type sent %d\n", __func__, type); + return NULL; + } + mutex_lock(&fw_data->lock); + if (!test_bit(WCDCAL_RECIEVED, + &fw_data->wcdcal_state[type])) { + pr_err("%s: cal not sent by userspace %d\n", + __func__, type); + mutex_unlock(&fw_data->lock); + return NULL; + } + mutex_unlock(&fw_data->lock); + return fw_data->fw[type]; +} +EXPORT_SYMBOL(wcdcal_get_fw_cal); + +static int wcdcal_hwdep_ioctl_shared(struct snd_hwdep *hw, + struct wcdcal_ioctl_buffer fw_user) +{ + struct fw_info *fw_data = hw->private_data; + struct firmware_cal **fw = fw_data->fw; + void *data; + + if (!test_bit(fw_user.cal_type, fw_data->cal_bit)) { + pr_err("%s: codec didn't set this %d!!\n", + __func__, fw_user.cal_type); + return -EFAULT; + } + if (fw_user.cal_type >= WCD9XXX_MAX_CAL || + fw_user.cal_type < WCD9XXX_MIN_CAL) { + pr_err("%s: wrong cal type sent %d\n", + __func__, fw_user.cal_type); + return -EFAULT; + } + if (fw_user.size > cal_size_info[fw_user.cal_type] || + fw_user.size <= 0) { + pr_err("%s: incorrect firmware size %d for %s\n", + __func__, fw_user.size, + cal_name_info[fw_user.cal_type]); + return -EFAULT; + } + data = fw[fw_user.cal_type]->data; + if (copy_from_user(data, fw_user.buffer, fw_user.size)) + return -EFAULT; + fw[fw_user.cal_type]->size = fw_user.size; + mutex_lock(&fw_data->lock); + set_bit(WCDCAL_RECIEVED, &fw_data->wcdcal_state[fw_user.cal_type]); + mutex_unlock(&fw_data->lock); + return 0; +} + +#ifdef CONFIG_COMPAT +struct wcdcal_ioctl_buffer32 { + u32 size; + compat_uptr_t buffer; + enum wcd_cal_type cal_type; +}; + +enum { + SNDRV_CTL_IOCTL_HWDEP_CAL_TYPE32 = + _IOW('U', 0x1, struct wcdcal_ioctl_buffer32), +}; + +static int wcdcal_hwdep_ioctl_compat(struct snd_hwdep *hw, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct wcdcal_ioctl_buffer __user *argp = (void __user *)arg; + struct wcdcal_ioctl_buffer32 fw_user32; + struct wcdcal_ioctl_buffer fw_user_compat; + + if (cmd != SNDRV_CTL_IOCTL_HWDEP_CAL_TYPE32) { + pr_err("%s: wrong ioctl command sent %u!\n", __func__, cmd); + return -ENOIOCTLCMD; + } + if (copy_from_user(&fw_user32, argp, sizeof(fw_user32))) { + pr_err("%s: failed to copy\n", __func__); + return -EFAULT; + } + fw_user_compat.size = fw_user32.size; + fw_user_compat.buffer = compat_ptr(fw_user32.buffer); + fw_user_compat.cal_type = fw_user32.cal_type; + return wcdcal_hwdep_ioctl_shared(hw, fw_user_compat); +} +#else +#define wcdcal_hwdep_ioctl_compat NULL +#endif + +static int wcdcal_hwdep_ioctl(struct snd_hwdep *hw, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct wcdcal_ioctl_buffer __user *argp = (void __user *)arg; + struct wcdcal_ioctl_buffer fw_user; + + if (cmd != SNDRV_CTL_IOCTL_HWDEP_CAL_TYPE) { + pr_err("%s: wrong ioctl command sent %d!\n", __func__, cmd); + return -ENOIOCTLCMD; + } + if (copy_from_user(&fw_user, argp, sizeof(fw_user))) { + pr_err("%s: failed to copy\n", __func__); + return -EFAULT; + } + return wcdcal_hwdep_ioctl_shared(hw, fw_user); +} + +static int wcdcal_hwdep_release(struct snd_hwdep *hw, struct file *file) +{ + struct fw_info *fw_data = hw->private_data; + + mutex_lock(&fw_data->lock); + /* clear all the calibrations */ + memset(fw_data->wcdcal_state, 0, + sizeof(fw_data->wcdcal_state)); + mutex_unlock(&fw_data->lock); + return 0; +} + +int wcd_cal_create_hwdep(void *data, int node, struct snd_soc_codec *codec) +{ + char hwname[40]; + struct snd_hwdep *hwdep; + struct firmware_cal **fw; + struct fw_info *fw_data = data; + int err, cal_bit; + + if (!fw_data || !codec) { + pr_err("%s: wrong arguments passed\n", __func__); + return -EINVAL; + } + + fw = fw_data->fw; + snprintf(hwname, strlen("Codec %s"), "Codec %s", + codec->component.name); + err = snd_hwdep_new(codec->component.card->snd_card, + hwname, node, &hwdep); + if (err < 0) { + dev_err(codec->dev, "%s: new hwdep failed %d\n", + __func__, err); + return err; + } + snprintf(hwdep->name, strlen("Codec %s"), "Codec %s", + codec->component.name); + hwdep->iface = SNDRV_HWDEP_IFACE_AUDIO_CODEC; + hwdep->private_data = fw_data; + hwdep->ops.ioctl_compat = wcdcal_hwdep_ioctl_compat; + hwdep->ops.ioctl = wcdcal_hwdep_ioctl; + hwdep->ops.release = wcdcal_hwdep_release; + mutex_init(&fw_data->lock); + + for_each_set_bit(cal_bit, fw_data->cal_bit, WCD9XXX_MAX_CAL) { + set_bit(WCDCAL_UNINITIALISED, + &fw_data->wcdcal_state[cal_bit]); + fw[cal_bit] = kzalloc(sizeof *(fw[cal_bit]), GFP_KERNEL); + if (!fw[cal_bit]) { + dev_err(codec->dev, "%s: no memory for %s cal\n", + __func__, cal_name_info[cal_bit]); + goto end; + } + } + for_each_set_bit(cal_bit, fw_data->cal_bit, WCD9XXX_MAX_CAL) { + fw[cal_bit]->data = kzalloc(cal_size_info[cal_bit], + GFP_KERNEL); + if (!fw[cal_bit]->data) + goto exit; + set_bit(WCDCAL_INITIALISED, + &fw_data->wcdcal_state[cal_bit]); + } + return 0; +exit: + for_each_set_bit(cal_bit, fw_data->cal_bit, WCD9XXX_MAX_CAL) { + kfree(fw[cal_bit]->data); + fw[cal_bit]->data = NULL; + } +end: + for_each_set_bit(cal_bit, fw_data->cal_bit, WCD9XXX_MAX_CAL) { + kfree(fw[cal_bit]); + fw[cal_bit] = NULL; + } + return -ENOMEM; +} +EXPORT_SYMBOL(wcd_cal_create_hwdep); diff --git a/audio-kernel/asoc/codecs/wcdcal-hwdep.h b/audio-kernel/asoc/codecs/wcdcal-hwdep.h new file mode 100644 index 000000000..632e2f11f --- /dev/null +++ b/audio-kernel/asoc/codecs/wcdcal-hwdep.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2014, 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 __WCD9XXX_HWDEP_H__ +#define __WCD9XXX_HWDEP_H__ +#include + +enum wcd_cal_states { + WCDCAL_UNINITIALISED, + WCDCAL_INITIALISED, + WCDCAL_RECIEVED +}; + +struct fw_info { + struct firmware_cal *fw[WCD9XXX_MAX_CAL]; + DECLARE_BITMAP(cal_bit, WCD9XXX_MAX_CAL); + /* for calibration tracking */ + unsigned long wcdcal_state[WCD9XXX_MAX_CAL]; + struct mutex lock; +}; + +struct firmware_cal { + u8 *data; + size_t size; +}; + +struct snd_soc_codec; +int wcd_cal_create_hwdep(void *fw, int node, struct snd_soc_codec *codec); +struct firmware_cal *wcdcal_get_fw_cal(struct fw_info *fw_data, + enum wcd_cal_type type); +#endif /* __WCD9XXX_HWDEP_H__ */ diff --git a/audio-kernel/asoc/codecs/wsa881x-registers.h b/audio-kernel/asoc/codecs/wsa881x-registers.h new file mode 100644 index 000000000..825a5f034 --- /dev/null +++ b/audio-kernel/asoc/codecs/wsa881x-registers.h @@ -0,0 +1,178 @@ +/* Copyright (c) 2015, 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 WSA881X_REGISTERS_H +#define WSA881X_REGISTERS_H + +#define WSA881X_DIGITAL_BASE 0x3000 +#define WSA881X_ANALOG_BASE 0x3100 + +/* Digital register address space */ +#define WSA881X_CHIP_ID0 (WSA881X_DIGITAL_BASE+0x0000) +#define WSA881X_CHIP_ID1 (WSA881X_DIGITAL_BASE+0x0001) +#define WSA881X_CHIP_ID2 (WSA881X_DIGITAL_BASE+0x0002) +#define WSA881X_CHIP_ID3 (WSA881X_DIGITAL_BASE+0x0003) +#define WSA881X_BUS_ID (WSA881X_DIGITAL_BASE+0x0004) +#define WSA881X_CDC_RST_CTL (WSA881X_DIGITAL_BASE+0x0005) +#define WSA881X_CDC_TOP_CLK_CTL (WSA881X_DIGITAL_BASE+0x0006) +#define WSA881X_CDC_ANA_CLK_CTL (WSA881X_DIGITAL_BASE+0x0007) +#define WSA881X_CDC_DIG_CLK_CTL (WSA881X_DIGITAL_BASE+0x0008) +#define WSA881X_CLOCK_CONFIG (WSA881X_DIGITAL_BASE+0x0009) +#define WSA881X_ANA_CTL (WSA881X_DIGITAL_BASE+0x000A) +#define WSA881X_SWR_RESET_EN (WSA881X_DIGITAL_BASE+0x000B) +#define WSA881X_RESET_CTL (WSA881X_DIGITAL_BASE+0x000C) +#define WSA881X_TADC_VALUE_CTL (WSA881X_DIGITAL_BASE+0x000F) +#define WSA881X_TEMP_DETECT_CTL (WSA881X_DIGITAL_BASE+0x0010) +#define WSA881X_TEMP_MSB (WSA881X_DIGITAL_BASE+0x0011) +#define WSA881X_TEMP_LSB (WSA881X_DIGITAL_BASE+0x0012) +#define WSA881X_TEMP_CONFIG0 (WSA881X_DIGITAL_BASE+0x0013) +#define WSA881X_TEMP_CONFIG1 (WSA881X_DIGITAL_BASE+0x0014) +#define WSA881X_CDC_CLIP_CTL (WSA881X_DIGITAL_BASE+0x0015) +#define WSA881X_SDM_PDM9_LSB (WSA881X_DIGITAL_BASE+0x0016) +#define WSA881X_SDM_PDM9_MSB (WSA881X_DIGITAL_BASE+0x0017) +#define WSA881X_CDC_RX_CTL (WSA881X_DIGITAL_BASE+0x0018) +#define WSA881X_DEM_BYPASS_DATA0 (WSA881X_DIGITAL_BASE+0x0019) +#define WSA881X_DEM_BYPASS_DATA1 (WSA881X_DIGITAL_BASE+0x001A) +#define WSA881X_DEM_BYPASS_DATA2 (WSA881X_DIGITAL_BASE+0x001B) +#define WSA881X_DEM_BYPASS_DATA3 (WSA881X_DIGITAL_BASE+0x001C) +#define WSA881X_OTP_CTRL0 (WSA881X_DIGITAL_BASE+0x001D) +#define WSA881X_OTP_CTRL1 (WSA881X_DIGITAL_BASE+0x001E) +#define WSA881X_HDRIVE_CTL_GROUP1 (WSA881X_DIGITAL_BASE+0x001F) +#define WSA881X_INTR_MODE (WSA881X_DIGITAL_BASE+0x0020) +#define WSA881X_INTR_MASK (WSA881X_DIGITAL_BASE+0x0021) +#define WSA881X_INTR_STATUS (WSA881X_DIGITAL_BASE+0x0022) +#define WSA881X_INTR_CLEAR (WSA881X_DIGITAL_BASE+0x0023) +#define WSA881X_INTR_LEVEL (WSA881X_DIGITAL_BASE+0x0024) +#define WSA881X_INTR_SET (WSA881X_DIGITAL_BASE+0x0025) +#define WSA881X_INTR_TEST (WSA881X_DIGITAL_BASE+0x0026) +#define WSA881X_PDM_TEST_MODE (WSA881X_DIGITAL_BASE+0x0030) +#define WSA881X_ATE_TEST_MODE (WSA881X_DIGITAL_BASE+0x0031) +#define WSA881X_PIN_CTL_MODE (WSA881X_DIGITAL_BASE+0x0032) +#define WSA881X_PIN_CTL_OE (WSA881X_DIGITAL_BASE+0x0033) +#define WSA881X_PIN_WDATA_IOPAD (WSA881X_DIGITAL_BASE+0x0034) +#define WSA881X_PIN_STATUS (WSA881X_DIGITAL_BASE+0x0035) +#define WSA881X_DIG_DEBUG_MODE (WSA881X_DIGITAL_BASE+0x0037) +#define WSA881X_DIG_DEBUG_SEL (WSA881X_DIGITAL_BASE+0x0038) +#define WSA881X_DIG_DEBUG_EN (WSA881X_DIGITAL_BASE+0x0039) +#define WSA881X_SWR_HM_TEST1 (WSA881X_DIGITAL_BASE+0x003B) +#define WSA881X_SWR_HM_TEST2 (WSA881X_DIGITAL_BASE+0x003C) +#define WSA881X_TEMP_DETECT_DBG_CTL (WSA881X_DIGITAL_BASE+0x003D) +#define WSA881X_TEMP_DEBUG_MSB (WSA881X_DIGITAL_BASE+0x003E) +#define WSA881X_TEMP_DEBUG_LSB (WSA881X_DIGITAL_BASE+0x003F) +#define WSA881X_SAMPLE_EDGE_SEL (WSA881X_DIGITAL_BASE+0x0044) +#define WSA881X_IOPAD_CTL (WSA881X_DIGITAL_BASE+0x0045) +#define WSA881X_SPARE_0 (WSA881X_DIGITAL_BASE+0x0050) +#define WSA881X_SPARE_1 (WSA881X_DIGITAL_BASE+0x0051) +#define WSA881X_SPARE_2 (WSA881X_DIGITAL_BASE+0x0052) +#define WSA881X_OTP_REG_0 (WSA881X_DIGITAL_BASE+0x0080) +#define WSA881X_OTP_REG_1 (WSA881X_DIGITAL_BASE+0x0081) +#define WSA881X_OTP_REG_2 (WSA881X_DIGITAL_BASE+0x0082) +#define WSA881X_OTP_REG_3 (WSA881X_DIGITAL_BASE+0x0083) +#define WSA881X_OTP_REG_4 (WSA881X_DIGITAL_BASE+0x0084) +#define WSA881X_OTP_REG_5 (WSA881X_DIGITAL_BASE+0x0085) +#define WSA881X_OTP_REG_6 (WSA881X_DIGITAL_BASE+0x0086) +#define WSA881X_OTP_REG_7 (WSA881X_DIGITAL_BASE+0x0087) +#define WSA881X_OTP_REG_8 (WSA881X_DIGITAL_BASE+0x0088) +#define WSA881X_OTP_REG_9 (WSA881X_DIGITAL_BASE+0x0089) +#define WSA881X_OTP_REG_10 (WSA881X_DIGITAL_BASE+0x008A) +#define WSA881X_OTP_REG_11 (WSA881X_DIGITAL_BASE+0x008B) +#define WSA881X_OTP_REG_12 (WSA881X_DIGITAL_BASE+0x008C) +#define WSA881X_OTP_REG_13 (WSA881X_DIGITAL_BASE+0x008D) +#define WSA881X_OTP_REG_14 (WSA881X_DIGITAL_BASE+0x008E) +#define WSA881X_OTP_REG_15 (WSA881X_DIGITAL_BASE+0x008F) +#define WSA881X_OTP_REG_16 (WSA881X_DIGITAL_BASE+0x0090) +#define WSA881X_OTP_REG_17 (WSA881X_DIGITAL_BASE+0x0091) +#define WSA881X_OTP_REG_18 (WSA881X_DIGITAL_BASE+0x0092) +#define WSA881X_OTP_REG_19 (WSA881X_DIGITAL_BASE+0x0093) +#define WSA881X_OTP_REG_20 (WSA881X_DIGITAL_BASE+0x0094) +#define WSA881X_OTP_REG_21 (WSA881X_DIGITAL_BASE+0x0095) +#define WSA881X_OTP_REG_22 (WSA881X_DIGITAL_BASE+0x0096) +#define WSA881X_OTP_REG_23 (WSA881X_DIGITAL_BASE+0x0097) +#define WSA881X_OTP_REG_24 (WSA881X_DIGITAL_BASE+0x0098) +#define WSA881X_OTP_REG_25 (WSA881X_DIGITAL_BASE+0x0099) +#define WSA881X_OTP_REG_26 (WSA881X_DIGITAL_BASE+0x009A) +#define WSA881X_OTP_REG_27 (WSA881X_DIGITAL_BASE+0x009B) +#define WSA881X_OTP_REG_28 (WSA881X_DIGITAL_BASE+0x009C) +#define WSA881X_OTP_REG_29 (WSA881X_DIGITAL_BASE+0x009D) +#define WSA881X_OTP_REG_30 (WSA881X_DIGITAL_BASE+0x009E) +#define WSA881X_OTP_REG_31 (WSA881X_DIGITAL_BASE+0x009F) +#define WSA881X_OTP_REG_63 (WSA881X_DIGITAL_BASE+0x00BF) + +/* Analog Register address space */ +#define WSA881X_BIAS_REF_CTRL (WSA881X_ANALOG_BASE+0x0000) +#define WSA881X_BIAS_TEST (WSA881X_ANALOG_BASE+0x0001) +#define WSA881X_BIAS_BIAS (WSA881X_ANALOG_BASE+0x0002) +#define WSA881X_TEMP_OP (WSA881X_ANALOG_BASE+0x0003) +#define WSA881X_TEMP_IREF_CTRL (WSA881X_ANALOG_BASE+0x0004) +#define WSA881X_TEMP_ISENS_CTRL (WSA881X_ANALOG_BASE+0x0005) +#define WSA881X_TEMP_CLK_CTRL (WSA881X_ANALOG_BASE+0x0006) +#define WSA881X_TEMP_TEST (WSA881X_ANALOG_BASE+0x0007) +#define WSA881X_TEMP_BIAS (WSA881X_ANALOG_BASE+0x0008) +#define WSA881X_TEMP_ADC_CTRL (WSA881X_ANALOG_BASE+0x0009) +#define WSA881X_TEMP_DOUT_MSB (WSA881X_ANALOG_BASE+0x000A) +#define WSA881X_TEMP_DOUT_LSB (WSA881X_ANALOG_BASE+0x000B) +#define WSA881X_ADC_EN_MODU_V (WSA881X_ANALOG_BASE+0x0010) +#define WSA881X_ADC_EN_MODU_I (WSA881X_ANALOG_BASE+0x0011) +#define WSA881X_ADC_EN_DET_TEST_V (WSA881X_ANALOG_BASE+0x0012) +#define WSA881X_ADC_EN_DET_TEST_I (WSA881X_ANALOG_BASE+0x0013) +#define WSA881X_ADC_SEL_IBIAS (WSA881X_ANALOG_BASE+0x0014) +#define WSA881X_ADC_EN_SEL_IBAIS (WSA881X_ANALOG_BASE+0x0015) +#define WSA881X_SPKR_DRV_EN (WSA881X_ANALOG_BASE+0x001A) +#define WSA881X_SPKR_DRV_GAIN (WSA881X_ANALOG_BASE+0x001B) +#define WSA881X_SPKR_DAC_CTL (WSA881X_ANALOG_BASE+0x001C) +#define WSA881X_SPKR_DRV_DBG (WSA881X_ANALOG_BASE+0x001D) +#define WSA881X_SPKR_PWRSTG_DBG (WSA881X_ANALOG_BASE+0x001E) +#define WSA881X_SPKR_OCP_CTL (WSA881X_ANALOG_BASE+0x001F) +#define WSA881X_SPKR_CLIP_CTL (WSA881X_ANALOG_BASE+0x0020) +#define WSA881X_SPKR_BBM_CTL (WSA881X_ANALOG_BASE+0x0021) +#define WSA881X_SPKR_MISC_CTL1 (WSA881X_ANALOG_BASE+0x0022) +#define WSA881X_SPKR_MISC_CTL2 (WSA881X_ANALOG_BASE+0x0023) +#define WSA881X_SPKR_BIAS_INT (WSA881X_ANALOG_BASE+0x0024) +#define WSA881X_SPKR_PA_INT (WSA881X_ANALOG_BASE+0x0025) +#define WSA881X_SPKR_BIAS_CAL (WSA881X_ANALOG_BASE+0x0026) +#define WSA881X_SPKR_BIAS_PSRR (WSA881X_ANALOG_BASE+0x0027) +#define WSA881X_SPKR_STATUS1 (WSA881X_ANALOG_BASE+0x0028) +#define WSA881X_SPKR_STATUS2 (WSA881X_ANALOG_BASE+0x0029) +#define WSA881X_BOOST_EN_CTL (WSA881X_ANALOG_BASE+0x002A) +#define WSA881X_BOOST_CURRENT_LIMIT (WSA881X_ANALOG_BASE+0x002B) +#define WSA881X_BOOST_PS_CTL (WSA881X_ANALOG_BASE+0x002C) +#define WSA881X_BOOST_PRESET_OUT1 (WSA881X_ANALOG_BASE+0x002D) +#define WSA881X_BOOST_PRESET_OUT2 (WSA881X_ANALOG_BASE+0x002E) +#define WSA881X_BOOST_FORCE_OUT (WSA881X_ANALOG_BASE+0x002F) +#define WSA881X_BOOST_LDO_PROG (WSA881X_ANALOG_BASE+0x0030) +#define WSA881X_BOOST_SLOPE_COMP_ISENSE_FB (WSA881X_ANALOG_BASE+0x0031) +#define WSA881X_BOOST_RON_CTL (WSA881X_ANALOG_BASE+0x0032) +#define WSA881X_BOOST_LOOP_STABILITY (WSA881X_ANALOG_BASE+0x0033) +#define WSA881X_BOOST_ZX_CTL (WSA881X_ANALOG_BASE+0x0034) +#define WSA881X_BOOST_START_CTL (WSA881X_ANALOG_BASE+0x0035) +#define WSA881X_BOOST_MISC1_CTL (WSA881X_ANALOG_BASE+0x0036) +#define WSA881X_BOOST_MISC2_CTL (WSA881X_ANALOG_BASE+0x0037) +#define WSA881X_BOOST_MISC3_CTL (WSA881X_ANALOG_BASE+0x0038) +#define WSA881X_BOOST_ATEST_CTL (WSA881X_ANALOG_BASE+0x0039) +#define WSA881X_SPKR_PROT_FE_GAIN (WSA881X_ANALOG_BASE+0x003A) +#define WSA881X_SPKR_PROT_FE_CM_LDO_SET (WSA881X_ANALOG_BASE+0x003B) +#define WSA881X_SPKR_PROT_FE_ISENSE_BIAS_SET1 (WSA881X_ANALOG_BASE+0x003C) +#define WSA881X_SPKR_PROT_FE_ISENSE_BIAS_SET2 (WSA881X_ANALOG_BASE+0x003D) +#define WSA881X_SPKR_PROT_ATEST1 (WSA881X_ANALOG_BASE+0x003E) +#define WSA881X_SPKR_PROT_ATEST2 (WSA881X_ANALOG_BASE+0x003F) +#define WSA881X_SPKR_PROT_FE_VSENSE_VCM (WSA881X_ANALOG_BASE+0x0040) +#define WSA881X_SPKR_PROT_FE_VSENSE_BIAS_SET1 (WSA881X_ANALOG_BASE+0x0041) +#define WSA881X_BONGO_RESRV_REG1 (WSA881X_ANALOG_BASE+0x0042) +#define WSA881X_BONGO_RESRV_REG2 (WSA881X_ANALOG_BASE+0x0043) +#define WSA881X_SPKR_PROT_SAR (WSA881X_ANALOG_BASE+0x0044) +#define WSA881X_SPKR_STATUS3 (WSA881X_ANALOG_BASE+0x0045) + +#define WSA881X_NUM_REGISTERS (WSA881X_SPKR_STATUS3+1) +#define WSA881X_MAX_REGISTER (WSA881X_NUM_REGISTERS-1) +#define WSA881X_CACHE_SIZE WSA881X_NUM_REGISTERS + +#endif /* WSA881X_REGISTERS_H */ diff --git a/audio-kernel/asoc/codecs/wsa881x-regmap.c b/audio-kernel/asoc/codecs/wsa881x-regmap.c new file mode 100644 index 000000000..63bbbfa6b --- /dev/null +++ b/audio-kernel/asoc/codecs/wsa881x-regmap.c @@ -0,0 +1,266 @@ +/* + * Copyright (c) 2015-2016, 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 +#include +#include "wsa881x-registers.h" +#include "wsa881x.h" + +/* + * Default register reset values that are common across different versions + * are defined here. If a register reset value is changed based on version + * then remove it from this structure and add it in version specific + * structures. + */ +static struct reg_default wsa881x_defaults[] = { + {WSA881X_CHIP_ID0, 0x00}, + {WSA881X_CHIP_ID1, 0x00}, + {WSA881X_CHIP_ID2, 0x00}, + {WSA881X_CHIP_ID3, 0x02}, + {WSA881X_BUS_ID, 0x00}, + {WSA881X_CDC_RST_CTL, 0x00}, + {WSA881X_CDC_TOP_CLK_CTL, 0x03}, + {WSA881X_CDC_ANA_CLK_CTL, 0x00}, + {WSA881X_CDC_DIG_CLK_CTL, 0x00}, + {WSA881X_CLOCK_CONFIG, 0x00}, + {WSA881X_ANA_CTL, 0x08}, + {WSA881X_SWR_RESET_EN, 0x00}, + {WSA881X_TEMP_DETECT_CTL, 0x01}, + {WSA881X_TEMP_MSB, 0x00}, + {WSA881X_TEMP_LSB, 0x00}, + {WSA881X_TEMP_CONFIG0, 0x00}, + {WSA881X_TEMP_CONFIG1, 0x00}, + {WSA881X_CDC_CLIP_CTL, 0x03}, + {WSA881X_SDM_PDM9_LSB, 0x00}, + {WSA881X_SDM_PDM9_MSB, 0x00}, + {WSA881X_CDC_RX_CTL, 0x7E}, + {WSA881X_DEM_BYPASS_DATA0, 0x00}, + {WSA881X_DEM_BYPASS_DATA1, 0x00}, + {WSA881X_DEM_BYPASS_DATA2, 0x00}, + {WSA881X_DEM_BYPASS_DATA3, 0x00}, + {WSA881X_OTP_CTRL0, 0x00}, + {WSA881X_OTP_CTRL1, 0x00}, + {WSA881X_HDRIVE_CTL_GROUP1, 0x00}, + {WSA881X_INTR_MODE, 0x00}, + {WSA881X_INTR_STATUS, 0x00}, + {WSA881X_INTR_CLEAR, 0x00}, + {WSA881X_INTR_LEVEL, 0x00}, + {WSA881X_INTR_SET, 0x00}, + {WSA881X_INTR_TEST, 0x00}, + {WSA881X_PDM_TEST_MODE, 0x00}, + {WSA881X_ATE_TEST_MODE, 0x00}, + {WSA881X_PIN_CTL_MODE, 0x00}, + {WSA881X_PIN_CTL_OE, 0x00}, + {WSA881X_PIN_WDATA_IOPAD, 0x00}, + {WSA881X_PIN_STATUS, 0x00}, + {WSA881X_DIG_DEBUG_MODE, 0x00}, + {WSA881X_DIG_DEBUG_SEL, 0x00}, + {WSA881X_DIG_DEBUG_EN, 0x00}, + {WSA881X_SWR_HM_TEST1, 0x08}, + {WSA881X_SWR_HM_TEST2, 0x00}, + {WSA881X_TEMP_DETECT_DBG_CTL, 0x00}, + {WSA881X_TEMP_DEBUG_MSB, 0x00}, + {WSA881X_TEMP_DEBUG_LSB, 0x00}, + {WSA881X_SAMPLE_EDGE_SEL, 0x0C}, + {WSA881X_SPARE_0, 0x00}, + {WSA881X_SPARE_1, 0x00}, + {WSA881X_SPARE_2, 0x00}, + {WSA881X_OTP_REG_0, 0x01}, + {WSA881X_OTP_REG_1, 0xFF}, + {WSA881X_OTP_REG_2, 0xC0}, + {WSA881X_OTP_REG_3, 0xFF}, + {WSA881X_OTP_REG_4, 0xC0}, + {WSA881X_OTP_REG_5, 0xFF}, + {WSA881X_OTP_REG_6, 0xFF}, + {WSA881X_OTP_REG_7, 0xFF}, + {WSA881X_OTP_REG_8, 0xFF}, + {WSA881X_OTP_REG_9, 0xFF}, + {WSA881X_OTP_REG_10, 0xFF}, + {WSA881X_OTP_REG_11, 0xFF}, + {WSA881X_OTP_REG_12, 0xFF}, + {WSA881X_OTP_REG_13, 0xFF}, + {WSA881X_OTP_REG_14, 0xFF}, + {WSA881X_OTP_REG_15, 0xFF}, + {WSA881X_OTP_REG_16, 0xFF}, + {WSA881X_OTP_REG_17, 0xFF}, + {WSA881X_OTP_REG_18, 0xFF}, + {WSA881X_OTP_REG_19, 0xFF}, + {WSA881X_OTP_REG_20, 0xFF}, + {WSA881X_OTP_REG_21, 0xFF}, + {WSA881X_OTP_REG_22, 0xFF}, + {WSA881X_OTP_REG_23, 0xFF}, + {WSA881X_OTP_REG_24, 0x03}, + {WSA881X_OTP_REG_25, 0x01}, + {WSA881X_OTP_REG_26, 0x03}, + {WSA881X_OTP_REG_27, 0x11}, + {WSA881X_OTP_REG_63, 0x40}, + /* WSA881x Analog registers */ + {WSA881X_BIAS_REF_CTRL, 0x6C}, + {WSA881X_BIAS_TEST, 0x16}, + {WSA881X_BIAS_BIAS, 0xF0}, + {WSA881X_TEMP_OP, 0x00}, + {WSA881X_TEMP_IREF_CTRL, 0x56}, + {WSA881X_TEMP_ISENS_CTRL, 0x47}, + {WSA881X_TEMP_CLK_CTRL, 0x87}, + {WSA881X_TEMP_TEST, 0x00}, + {WSA881X_TEMP_BIAS, 0x51}, + {WSA881X_TEMP_DOUT_MSB, 0x00}, + {WSA881X_TEMP_DOUT_LSB, 0x00}, + {WSA881X_ADC_EN_MODU_V, 0x00}, + {WSA881X_ADC_EN_MODU_I, 0x00}, + {WSA881X_ADC_EN_DET_TEST_V, 0x00}, + {WSA881X_ADC_EN_DET_TEST_I, 0x00}, + {WSA881X_ADC_EN_SEL_IBAIS, 0x10}, + {WSA881X_SPKR_DRV_EN, 0x74}, + {WSA881X_SPKR_DRV_DBG, 0x15}, + {WSA881X_SPKR_PWRSTG_DBG, 0x00}, + {WSA881X_SPKR_OCP_CTL, 0xD4}, + {WSA881X_SPKR_CLIP_CTL, 0x90}, + {WSA881X_SPKR_PA_INT, 0x54}, + {WSA881X_SPKR_BIAS_CAL, 0xAC}, + {WSA881X_SPKR_STATUS1, 0x00}, + {WSA881X_SPKR_STATUS2, 0x00}, + {WSA881X_BOOST_EN_CTL, 0x18}, + {WSA881X_BOOST_CURRENT_LIMIT, 0x7A}, + {WSA881X_BOOST_PRESET_OUT2, 0x70}, + {WSA881X_BOOST_FORCE_OUT, 0x0E}, + {WSA881X_BOOST_LDO_PROG, 0x16}, + {WSA881X_BOOST_SLOPE_COMP_ISENSE_FB, 0x71}, + {WSA881X_BOOST_RON_CTL, 0x0F}, + {WSA881X_BOOST_ZX_CTL, 0x34}, + {WSA881X_BOOST_START_CTL, 0x23}, + {WSA881X_BOOST_MISC1_CTL, 0x80}, + {WSA881X_BOOST_MISC2_CTL, 0x00}, + {WSA881X_BOOST_MISC3_CTL, 0x00}, + {WSA881X_BOOST_ATEST_CTL, 0x00}, + {WSA881X_SPKR_PROT_FE_GAIN, 0x46}, + {WSA881X_SPKR_PROT_FE_CM_LDO_SET, 0x3B}, + {WSA881X_SPKR_PROT_FE_ISENSE_BIAS_SET1, 0x8D}, + {WSA881X_SPKR_PROT_FE_ISENSE_BIAS_SET2, 0x8D}, + {WSA881X_SPKR_PROT_ATEST1, 0x01}, + {WSA881X_SPKR_PROT_FE_VSENSE_VCM, 0x8D}, + {WSA881X_SPKR_PROT_FE_VSENSE_BIAS_SET1, 0x4D}, + {WSA881X_SPKR_PROT_SAR, 0x00}, + {WSA881X_SPKR_STATUS3, 0x00}, +}; + +/* Default register reset values for WSA881x rev 2.0 */ +static struct reg_sequence wsa881x_rev_2_0[] = { + {WSA881X_RESET_CTL, 0x00, 0x00}, + {WSA881X_TADC_VALUE_CTL, 0x01, 0x00}, + {WSA881X_INTR_MASK, 0x1B, 0x00}, + {WSA881X_IOPAD_CTL, 0x00, 0x00}, + {WSA881X_OTP_REG_28, 0x3F, 0x00}, + {WSA881X_OTP_REG_29, 0x3F, 0x00}, + {WSA881X_OTP_REG_30, 0x01, 0x00}, + {WSA881X_OTP_REG_31, 0x01, 0x00}, + {WSA881X_TEMP_ADC_CTRL, 0x03, 0x00}, + {WSA881X_ADC_SEL_IBIAS, 0x45, 0x00}, + {WSA881X_SPKR_DRV_GAIN, 0xC1, 0x00}, + {WSA881X_SPKR_DAC_CTL, 0x42, 0x00}, + {WSA881X_SPKR_BBM_CTL, 0x02, 0x00}, + {WSA881X_SPKR_MISC_CTL1, 0x40, 0x00}, + {WSA881X_SPKR_MISC_CTL2, 0x07, 0x00}, + {WSA881X_SPKR_BIAS_INT, 0x5F, 0x00}, + {WSA881X_SPKR_BIAS_PSRR, 0x44, 0x00}, + {WSA881X_BOOST_PS_CTL, 0xA0, 0x00}, + {WSA881X_BOOST_PRESET_OUT1, 0xB7, 0x00}, + {WSA881X_BOOST_LOOP_STABILITY, 0x8D, 0x00}, + {WSA881X_SPKR_PROT_ATEST2, 0x02, 0x00}, + {WSA881X_BONGO_RESRV_REG1, 0x5E, 0x00}, + {WSA881X_BONGO_RESRV_REG2, 0x07, 0x00}, +}; + +/* + * wsa881x_regmap_defaults - update regmap default register values + * @regmap: pointer to regmap structure + * @version: wsa881x version id + * + * Update regmap default register values based on version id + * + */ +void wsa881x_regmap_defaults(struct regmap *regmap, u8 version) +{ + u16 ret = 0; + + if (!regmap) { + pr_debug("%s: regmap structure is NULL\n", __func__); + return; + } + + regcache_cache_only(regmap, true); + ret = regmap_multi_reg_write(regmap, wsa881x_rev_2_0, + ARRAY_SIZE(wsa881x_rev_2_0)); + regcache_cache_only(regmap, false); + + if (ret) + pr_debug("%s: Failed to update regmap defaults ret= %d\n", + __func__, ret); +} +EXPORT_SYMBOL(wsa881x_regmap_defaults); + +static bool wsa881x_readable_register(struct device *dev, unsigned int reg) +{ + return wsa881x_reg_readable[reg]; +} + +static bool wsa881x_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case WSA881X_CHIP_ID0: + case WSA881X_CHIP_ID1: + case WSA881X_CHIP_ID2: + case WSA881X_CHIP_ID3: + case WSA881X_BUS_ID: + case WSA881X_TEMP_MSB: + case WSA881X_TEMP_LSB: + case WSA881X_SDM_PDM9_LSB: + case WSA881X_SDM_PDM9_MSB: + case WSA881X_OTP_CTRL1: + case WSA881X_INTR_STATUS: + case WSA881X_ATE_TEST_MODE: + case WSA881X_PIN_STATUS: + case WSA881X_SWR_HM_TEST2: + case WSA881X_SPKR_STATUS1: + case WSA881X_SPKR_STATUS2: + case WSA881X_SPKR_STATUS3: + case WSA881X_OTP_REG_0: + case WSA881X_OTP_REG_1: + case WSA881X_OTP_REG_2: + case WSA881X_OTP_REG_3: + case WSA881X_OTP_REG_4: + case WSA881X_OTP_REG_5: + case WSA881X_OTP_REG_31: + case WSA881X_TEMP_DOUT_MSB: + case WSA881X_TEMP_DOUT_LSB: + case WSA881X_TEMP_OP: + case WSA881X_SPKR_PROT_SAR: + return true; + default: + return false; + } +} + +struct regmap_config wsa881x_regmap_config = { + .reg_bits = 16, + .val_bits = 8, + .cache_type = REGCACHE_RBTREE, + .reg_defaults = wsa881x_defaults, + .num_reg_defaults = ARRAY_SIZE(wsa881x_defaults), + .max_register = WSA881X_MAX_REGISTER, + .volatile_reg = wsa881x_volatile_register, + .readable_reg = wsa881x_readable_register, + .reg_format_endian = REGMAP_ENDIAN_NATIVE, + .val_format_endian = REGMAP_ENDIAN_NATIVE, + .can_multi_write = true, +}; diff --git a/audio-kernel/asoc/codecs/wsa881x-tables.c b/audio-kernel/asoc/codecs/wsa881x-tables.c new file mode 100644 index 000000000..4f1212be9 --- /dev/null +++ b/audio-kernel/asoc/codecs/wsa881x-tables.c @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2015, 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 +#include +#include "wsa881x-registers.h" + +const u8 wsa881x_reg_readable[WSA881X_CACHE_SIZE] = { + [WSA881X_CHIP_ID0] = 1, + [WSA881X_CHIP_ID1] = 1, + [WSA881X_CHIP_ID2] = 1, + [WSA881X_CHIP_ID3] = 1, + [WSA881X_BUS_ID] = 1, + [WSA881X_CDC_RST_CTL] = 1, + [WSA881X_CDC_TOP_CLK_CTL] = 1, + [WSA881X_CDC_ANA_CLK_CTL] = 1, + [WSA881X_CDC_DIG_CLK_CTL] = 1, + [WSA881X_CLOCK_CONFIG] = 1, + [WSA881X_ANA_CTL] = 1, + [WSA881X_SWR_RESET_EN] = 1, + [WSA881X_RESET_CTL] = 1, + [WSA881X_TADC_VALUE_CTL] = 1, + [WSA881X_TEMP_DETECT_CTL] = 1, + [WSA881X_TEMP_MSB] = 1, + [WSA881X_TEMP_LSB] = 1, + [WSA881X_TEMP_CONFIG0] = 1, + [WSA881X_TEMP_CONFIG1] = 1, + [WSA881X_CDC_CLIP_CTL] = 1, + [WSA881X_SDM_PDM9_LSB] = 1, + [WSA881X_SDM_PDM9_MSB] = 1, + [WSA881X_CDC_RX_CTL] = 1, + [WSA881X_DEM_BYPASS_DATA0] = 1, + [WSA881X_DEM_BYPASS_DATA1] = 1, + [WSA881X_DEM_BYPASS_DATA2] = 1, + [WSA881X_DEM_BYPASS_DATA3] = 1, + [WSA881X_OTP_CTRL0] = 1, + [WSA881X_OTP_CTRL1] = 1, + [WSA881X_HDRIVE_CTL_GROUP1] = 1, + [WSA881X_INTR_MODE] = 1, + [WSA881X_INTR_MASK] = 1, + [WSA881X_INTR_STATUS] = 1, + [WSA881X_INTR_CLEAR] = 1, + [WSA881X_INTR_LEVEL] = 1, + [WSA881X_INTR_SET] = 1, + [WSA881X_INTR_TEST] = 1, + [WSA881X_PDM_TEST_MODE] = 1, + [WSA881X_ATE_TEST_MODE] = 1, + [WSA881X_PIN_CTL_MODE] = 1, + [WSA881X_PIN_CTL_OE] = 1, + [WSA881X_PIN_WDATA_IOPAD] = 1, + [WSA881X_PIN_STATUS] = 1, + [WSA881X_DIG_DEBUG_MODE] = 1, + [WSA881X_DIG_DEBUG_SEL] = 1, + [WSA881X_DIG_DEBUG_EN] = 1, + [WSA881X_SWR_HM_TEST1] = 1, + [WSA881X_SWR_HM_TEST2] = 1, + [WSA881X_TEMP_DETECT_DBG_CTL] = 1, + [WSA881X_TEMP_DEBUG_MSB] = 1, + [WSA881X_TEMP_DEBUG_LSB] = 1, + [WSA881X_SAMPLE_EDGE_SEL] = 1, + [WSA881X_IOPAD_CTL] = 1, + [WSA881X_SPARE_0] = 1, + [WSA881X_SPARE_1] = 1, + [WSA881X_SPARE_2] = 1, + [WSA881X_OTP_REG_0] = 1, + [WSA881X_OTP_REG_1] = 1, + [WSA881X_OTP_REG_2] = 1, + [WSA881X_OTP_REG_3] = 1, + [WSA881X_OTP_REG_4] = 1, + [WSA881X_OTP_REG_5] = 1, + [WSA881X_OTP_REG_6] = 1, + [WSA881X_OTP_REG_7] = 1, + [WSA881X_OTP_REG_8] = 1, + [WSA881X_OTP_REG_9] = 1, + [WSA881X_OTP_REG_10] = 1, + [WSA881X_OTP_REG_11] = 1, + [WSA881X_OTP_REG_12] = 1, + [WSA881X_OTP_REG_13] = 1, + [WSA881X_OTP_REG_14] = 1, + [WSA881X_OTP_REG_15] = 1, + [WSA881X_OTP_REG_16] = 1, + [WSA881X_OTP_REG_17] = 1, + [WSA881X_OTP_REG_18] = 1, + [WSA881X_OTP_REG_19] = 1, + [WSA881X_OTP_REG_20] = 1, + [WSA881X_OTP_REG_21] = 1, + [WSA881X_OTP_REG_22] = 1, + [WSA881X_OTP_REG_23] = 1, + [WSA881X_OTP_REG_24] = 1, + [WSA881X_OTP_REG_25] = 1, + [WSA881X_OTP_REG_26] = 1, + [WSA881X_OTP_REG_27] = 1, + [WSA881X_OTP_REG_28] = 1, + [WSA881X_OTP_REG_29] = 1, + [WSA881X_OTP_REG_30] = 1, + [WSA881X_OTP_REG_31] = 1, + [WSA881X_OTP_REG_63] = 1, + /* Analog Registers */ + [WSA881X_BIAS_REF_CTRL] = 1, + [WSA881X_BIAS_TEST] = 1, + [WSA881X_BIAS_BIAS] = 1, + [WSA881X_TEMP_OP] = 1, + [WSA881X_TEMP_IREF_CTRL] = 1, + [WSA881X_TEMP_ISENS_CTRL] = 1, + [WSA881X_TEMP_CLK_CTRL] = 1, + [WSA881X_TEMP_TEST] = 1, + [WSA881X_TEMP_BIAS] = 1, + [WSA881X_TEMP_ADC_CTRL] = 1, + [WSA881X_TEMP_DOUT_MSB] = 1, + [WSA881X_TEMP_DOUT_LSB] = 1, + [WSA881X_ADC_EN_MODU_V] = 1, + [WSA881X_ADC_EN_MODU_I] = 1, + [WSA881X_ADC_EN_DET_TEST_V] = 1, + [WSA881X_ADC_EN_DET_TEST_I] = 1, + [WSA881X_ADC_SEL_IBIAS] = 1, + [WSA881X_ADC_EN_SEL_IBAIS] = 1, + [WSA881X_SPKR_DRV_EN] = 1, + [WSA881X_SPKR_DRV_GAIN] = 1, + [WSA881X_SPKR_DAC_CTL] = 1, + [WSA881X_SPKR_DRV_DBG] = 1, + [WSA881X_SPKR_PWRSTG_DBG] = 1, + [WSA881X_SPKR_OCP_CTL] = 1, + [WSA881X_SPKR_CLIP_CTL] = 1, + [WSA881X_SPKR_BBM_CTL] = 1, + [WSA881X_SPKR_MISC_CTL1] = 1, + [WSA881X_SPKR_MISC_CTL2] = 1, + [WSA881X_SPKR_BIAS_INT] = 1, + [WSA881X_SPKR_PA_INT] = 1, + [WSA881X_SPKR_BIAS_CAL] = 1, + [WSA881X_SPKR_BIAS_PSRR] = 1, + [WSA881X_SPKR_STATUS1] = 1, + [WSA881X_SPKR_STATUS2] = 1, + [WSA881X_BOOST_EN_CTL] = 1, + [WSA881X_BOOST_CURRENT_LIMIT] = 1, + [WSA881X_BOOST_PS_CTL] = 1, + [WSA881X_BOOST_PRESET_OUT1] = 1, + [WSA881X_BOOST_PRESET_OUT2] = 1, + [WSA881X_BOOST_FORCE_OUT] = 1, + [WSA881X_BOOST_LDO_PROG] = 1, + [WSA881X_BOOST_SLOPE_COMP_ISENSE_FB] = 1, + [WSA881X_BOOST_RON_CTL] = 1, + [WSA881X_BOOST_LOOP_STABILITY] = 1, + [WSA881X_BOOST_ZX_CTL] = 1, + [WSA881X_BOOST_START_CTL] = 1, + [WSA881X_BOOST_MISC1_CTL] = 1, + [WSA881X_BOOST_MISC2_CTL] = 1, + [WSA881X_BOOST_MISC3_CTL] = 1, + [WSA881X_BOOST_ATEST_CTL] = 1, + [WSA881X_SPKR_PROT_FE_GAIN] = 1, + [WSA881X_SPKR_PROT_FE_CM_LDO_SET] = 1, + [WSA881X_SPKR_PROT_FE_ISENSE_BIAS_SET1] = 1, + [WSA881X_SPKR_PROT_FE_ISENSE_BIAS_SET2] = 1, + [WSA881X_SPKR_PROT_ATEST1] = 1, + [WSA881X_SPKR_PROT_ATEST2] = 1, + [WSA881X_SPKR_PROT_FE_VSENSE_VCM] = 1, + [WSA881X_SPKR_PROT_FE_VSENSE_BIAS_SET1] = 1, + [WSA881X_BONGO_RESRV_REG1] = 1, + [WSA881X_BONGO_RESRV_REG2] = 1, + [WSA881X_SPKR_PROT_SAR] = 1, + [WSA881X_SPKR_STATUS3] = 1, +}; diff --git a/audio-kernel/asoc/codecs/wsa881x-temp-sensor.c b/audio-kernel/asoc/codecs/wsa881x-temp-sensor.c new file mode 100644 index 000000000..2a1291f34 --- /dev/null +++ b/audio-kernel/asoc/codecs/wsa881x-temp-sensor.c @@ -0,0 +1,196 @@ +/* Copyright (c) 2015, 2017-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 +#include +#include +#include +#include +#include +#include +#include "wsa881x-temp-sensor.h" + +#define T1_TEMP -10 +#define T2_TEMP 150 +#define LOW_TEMP_THRESHOLD 5 +#define HIGH_TEMP_THRESHOLD 45 +#define TEMP_INVALID 0xFFFF +#define WSA881X_TEMP_RETRY 3 +/* + * wsa881x_get_temp - get wsa temperature + * @thermal: thermal zone device + * @temp: temperature value + * + * Get the temperature of wsa881x. + * + * Return: 0 on success or negative error code on failure. + */ +int wsa881x_get_temp(struct thermal_zone_device *thermal, + int *temp) +{ + struct wsa881x_tz_priv *pdata; + struct snd_soc_codec *codec; + struct wsa_temp_register reg; + int dmeas, d1, d2; + int ret = 0; + int temp_val; + int t1 = T1_TEMP; + int t2 = T2_TEMP; + u8 retry = WSA881X_TEMP_RETRY; + + if (!thermal) + return -EINVAL; + + if (thermal->devdata) { + pdata = thermal->devdata; + if (pdata->codec) { + codec = pdata->codec; + } else { + pr_err("%s: codec is NULL\n", __func__); + return -EINVAL; + } + } else { + pr_err("%s: pdata is NULL\n", __func__); + return -EINVAL; + } + if (atomic_cmpxchg(&pdata->is_suspend_spk, 1, 0)) { + /* + * get_temp query happens as part of POST_PM_SUSPEND + * from thermal core. To avoid calls to slimbus + * as part of this thermal query, return default temp + * and reset the suspend flag. + */ + if (!pdata->t0_init) { + if (temp) + *temp = pdata->curr_temp; + return 0; + } + } + +temp_retry: + if (pdata->wsa_temp_reg_read) { + ret = pdata->wsa_temp_reg_read(codec, ®); + if (ret) { + pr_err("%s: temp read failed: %d, current temp: %d\n", + __func__, ret, pdata->curr_temp); + if (temp) + *temp = pdata->curr_temp; + return 0; + } + } else { + pr_err("%s: wsa_temp_reg_read is NULL\n", __func__); + return -EINVAL; + } + /* + * Temperature register values are expected to be in the + * following range. + * d1_msb = 68 - 92 and d1_lsb = 0, 64, 128, 192 + * d2_msb = 185 -218 and d2_lsb = 0, 64, 128, 192 + */ + if ((reg.d1_msb < 68 || reg.d1_msb > 92) || + (!(reg.d1_lsb == 0 || reg.d1_lsb == 64 || reg.d1_lsb == 128 || + reg.d1_lsb == 192)) || + (reg.d2_msb < 185 || reg.d2_msb > 218) || + (!(reg.d2_lsb == 0 || reg.d2_lsb == 64 || reg.d2_lsb == 128 || + reg.d2_lsb == 192))) { + printk_ratelimited("%s: Temperature registers[%d %d %d %d] are out of range\n", + __func__, reg.d1_msb, reg.d1_lsb, reg.d2_msb, + reg.d2_lsb); + } + dmeas = ((reg.dmeas_msb << 0x8) | reg.dmeas_lsb) >> 0x6; + d1 = ((reg.d1_msb << 0x8) | reg.d1_lsb) >> 0x6; + d2 = ((reg.d2_msb << 0x8) | reg.d2_lsb) >> 0x6; + + if (d1 == d2) + temp_val = TEMP_INVALID; + else + temp_val = t1 + (((dmeas - d1) * (t2 - t1))/(d2 - d1)); + + if (temp_val <= LOW_TEMP_THRESHOLD || + temp_val >= HIGH_TEMP_THRESHOLD) { + printk_ratelimited("%s: T0: %d is out of range[%d, %d]\n", + __func__, temp_val, LOW_TEMP_THRESHOLD, + HIGH_TEMP_THRESHOLD); + if (retry--) { + msleep(20); + goto temp_retry; + } + } + pdata->curr_temp = temp_val; + + if (temp) + *temp = temp_val; + pr_debug("%s: t0 measured: %d dmeas = %d, d1 = %d, d2 = %d\n", + __func__, temp_val, dmeas, d1, d2); + return ret; +} +EXPORT_SYMBOL(wsa881x_get_temp); + +static struct thermal_zone_device_ops wsa881x_thermal_ops = { + .get_temp = wsa881x_get_temp, +}; + + +static int wsa881x_pm_notify(struct notifier_block *nb, + unsigned long mode, void *_unused) +{ + struct wsa881x_tz_priv *pdata = + container_of(nb, struct wsa881x_tz_priv, pm_nb); + + switch (mode) { + case PM_SUSPEND_PREPARE: + atomic_set(&pdata->is_suspend_spk, 1); + break; + default: + break; + } + return 0; +} + +int wsa881x_init_thermal(struct wsa881x_tz_priv *tz_pdata) +{ + struct thermal_zone_device *tz_dev; + + if (tz_pdata == NULL) { + pr_err("%s: thermal pdata is NULL\n", __func__); + return -EINVAL; + } + /* Register with the thermal zone */ + tz_dev = thermal_zone_device_register(tz_pdata->name, + 0, 0, tz_pdata, + &wsa881x_thermal_ops, NULL, 0, 0); + if (IS_ERR(tz_dev)) { + pr_err("%s: thermal device register failed.\n", __func__); + return -EINVAL; + } + tz_pdata->tz_dev = tz_dev; + tz_pdata->pm_nb.notifier_call = wsa881x_pm_notify; + register_pm_notifier(&tz_pdata->pm_nb); + atomic_set(&tz_pdata->is_suspend_spk, 0); + + return 0; +} +EXPORT_SYMBOL(wsa881x_init_thermal); + +void wsa881x_deinit_thermal(struct thermal_zone_device *tz_dev) +{ + struct wsa881x_tz_priv *pdata; + + if (tz_dev && tz_dev->devdata) { + pdata = tz_dev->devdata; + if (pdata) + unregister_pm_notifier(&pdata->pm_nb); + } + if (tz_dev) + thermal_zone_device_unregister(tz_dev); +} +EXPORT_SYMBOL(wsa881x_deinit_thermal); diff --git a/audio-kernel/asoc/codecs/wsa881x-temp-sensor.h b/audio-kernel/asoc/codecs/wsa881x-temp-sensor.h new file mode 100644 index 000000000..26828b716 --- /dev/null +++ b/audio-kernel/asoc/codecs/wsa881x-temp-sensor.h @@ -0,0 +1,43 @@ +/* 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 + * 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 WSA881X_TEMP_SENSOR_H +#define WSA881X_TEMP_SENSOR_H + +#include +#include + +struct wsa_temp_register { + u8 d1_msb; + u8 d1_lsb; + u8 d2_msb; + u8 d2_lsb; + u8 dmeas_msb; + u8 dmeas_lsb; +}; +typedef int32_t (*wsa_temp_register_read)(struct snd_soc_codec *codec, + struct wsa_temp_register *wsa_temp_reg); +struct wsa881x_tz_priv { + struct thermal_zone_device *tz_dev; + struct snd_soc_codec *codec; + struct wsa_temp_register *wsa_temp_reg; + char name[80]; + wsa_temp_register_read wsa_temp_reg_read; + struct notifier_block pm_nb; + atomic_t is_suspend_spk; + int t0_init; + int curr_temp; +}; + +int wsa881x_get_temp(struct thermal_zone_device *tz_dev, int *temp); +int wsa881x_init_thermal(struct wsa881x_tz_priv *tz_pdata); +void wsa881x_deinit_thermal(struct thermal_zone_device *tz_dev); +#endif diff --git a/audio-kernel/asoc/codecs/wsa881x.c b/audio-kernel/asoc/codecs/wsa881x.c new file mode 100644 index 000000000..c8ae53d27 --- /dev/null +++ b/audio-kernel/asoc/codecs/wsa881x.c @@ -0,0 +1,1545 @@ +/* + * 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 + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "msm-cdc-pinctrl.h" +#include "wsa881x.h" +#include "wsa881x-temp-sensor.h" + +#define WSA881X_NUM_RETRY 5 + +enum { + G_18DB = 0, + G_16P5DB, + G_15DB, + G_13P5DB, + G_12DB, + G_10P5DB, + G_9DB, + G_7P5DB, + G_6DB, + G_4P5DB, + G_3DB, + G_1P5DB, + G_0DB, +}; + +enum { + DISABLE = 0, + ENABLE, +}; + +enum { + SWR_DAC_PORT, + SWR_COMP_PORT, + SWR_BOOST_PORT, + SWR_VISENSE_PORT, +}; + +struct swr_port { + u8 port_id; + u8 ch_mask; + u32 ch_rate; + u8 num_ch; + u8 port_type; +}; + +enum { + WSA881X_DEV_DOWN, + WSA881X_DEV_UP, + WSA881X_DEV_READY, +}; + +/* + * Private data Structure for wsa881x. All parameters related to + * WSA881X codec needs to be defined here. + */ +struct wsa881x_priv { + struct regmap *regmap; + struct device *dev; + struct swr_device *swr_slave; + struct snd_soc_codec *codec; + bool comp_enable; + bool boost_enable; + bool visense_enable; + u8 pa_gain; + struct swr_port port[WSA881X_MAX_SWR_PORTS]; + int pd_gpio; + struct wsa881x_tz_priv tz_pdata; + int bg_cnt; + int clk_cnt; + int version; + struct mutex bg_lock; + struct mutex res_lock; + struct mutex temp_lock; + struct snd_info_entry *entry; + struct snd_info_entry *version_entry; + int state; + struct delayed_work ocp_ctl_work; + struct device_node *wsa_rst_np; + int pa_mute; +}; + +#define SWR_SLV_MAX_REG_ADDR 0x390 +#define SWR_SLV_START_REG_ADDR 0x40 +#define SWR_SLV_MAX_BUF_LEN 20 +#define BYTES_PER_LINE 12 +#define SWR_SLV_RD_BUF_LEN 8 +#define SWR_SLV_WR_BUF_LEN 32 +#define SWR_SLV_MAX_DEVICES 2 + +#define WSA881X_VERSION_ENTRY_SIZE 27 +#define WSA881X_OCP_CTL_TIMER_SEC 2 +#define WSA881X_OCP_CTL_TEMP_CELSIUS 25 +#define WSA881X_OCP_CTL_POLL_TIMER_SEC 60 + +static int wsa881x_ocp_poll_timer_sec = WSA881X_OCP_CTL_POLL_TIMER_SEC; +module_param(wsa881x_ocp_poll_timer_sec, int, 0664); +MODULE_PARM_DESC(wsa881x_ocp_poll_timer_sec, "timer for ocp ctl polling"); + +static struct wsa881x_priv *dbgwsa881x; +static struct dentry *debugfs_wsa881x_dent; +static struct dentry *debugfs_peek; +static struct dentry *debugfs_poke; +static struct dentry *debugfs_reg_dump; +static unsigned int read_data; +static unsigned int devnum; + +static int32_t wsa881x_resource_acquire(struct snd_soc_codec *codec, + bool enable); + +static const char * const wsa_pa_gain_text[] = { + "G_18_DB", "G_16P5_DB", "G_15_DB", "G_13P5_DB", "G_12_DB", "G_10P5_DB", + "G_9_DB", "G_7P5_DB", "G_6_DB", "G_4P5_DB", "G_3_DB", "G_1P5_DB", + "G_0_DB" +}; + +static const struct soc_enum wsa_pa_gain_enum = + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(wsa_pa_gain_text), wsa_pa_gain_text); + +static int wsa_pa_gain_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = wsa881x->pa_gain; + + dev_dbg(codec->dev, "%s: PA gain = 0x%x\n", __func__, wsa881x->pa_gain); + + return 0; +} + +static int wsa_pa_gain_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n", + __func__, ucontrol->value.integer.value[0]); + + wsa881x->pa_gain = ucontrol->value.integer.value[0]; + + return 0; +} + +static int wsa881x_get_mute(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = wsa881x->pa_mute; + + return 0; +} + +static int wsa881x_set_mute(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec); + int value = ucontrol->value.integer.value[0]; + + dev_dbg(codec->dev, "%s: mute current %d, new %d\n", + __func__, wsa881x->pa_mute, value); + + if (value) + snd_soc_update_bits(codec, WSA881X_SPKR_DRV_EN, 0x80, 0x00); + wsa881x->pa_mute = value; + + return 0; +} + +static int wsa881x_get_t0_init(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec); + struct wsa881x_tz_priv *pdata = &wsa881x->tz_pdata; + + ucontrol->value.integer.value[0] = pdata->t0_init; + dev_dbg(codec->dev, "%s: t0 init %d\n", __func__, pdata->t0_init); + + return 0; +} + +static int wsa881x_set_t0_init(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec); + struct wsa881x_tz_priv *pdata = &wsa881x->tz_pdata; + + pdata->t0_init = ucontrol->value.integer.value[0]; + dev_dbg(codec->dev, "%s: t0 init %d\n", __func__, pdata->t0_init); + + return 0; +} + +static const struct snd_kcontrol_new wsa_snd_controls[] = { + SOC_ENUM_EXT("WSA PA Gain", wsa_pa_gain_enum, + wsa_pa_gain_get, wsa_pa_gain_put), + SOC_SINGLE_EXT("WSA PA Mute", SND_SOC_NOPM, 0, 1, 0, + wsa881x_get_mute, wsa881x_set_mute), + SOC_SINGLE_EXT("WSA T0 Init", SND_SOC_NOPM, 0, 1, 0, + wsa881x_get_t0_init, wsa881x_set_t0_init), +}; + +static int codec_debug_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static int get_parameters(char *buf, u32 *param1, int num_of_par) +{ + char *token; + int base, cnt; + + token = strsep(&buf, " "); + for (cnt = 0; cnt < num_of_par; cnt++) { + if (token) { + if ((token[1] == 'x') || (token[1] == 'X')) + base = 16; + else + base = 10; + + if (kstrtou32(token, base, ¶m1[cnt]) != 0) + return -EINVAL; + + token = strsep(&buf, " "); + } else + return -EINVAL; + } + return 0; +} + +static ssize_t wsa881x_codec_version_read(struct snd_info_entry *entry, + void *file_private_data, struct file *file, + char __user *buf, size_t count, loff_t pos) +{ + struct wsa881x_priv *wsa881x; + char buffer[WSA881X_VERSION_ENTRY_SIZE]; + int len; + + wsa881x = (struct wsa881x_priv *) entry->private_data; + if (!wsa881x) { + pr_err("%s: wsa881x priv is null\n", __func__); + return -EINVAL; + } + + len = snprintf(buffer, sizeof(buffer), "WSA881X-SOUNDWIRE_2_0\n"); + + return simple_read_from_buffer(buf, count, &pos, buffer, len); +} + +static struct snd_info_entry_ops wsa881x_codec_info_ops = { + .read = wsa881x_codec_version_read, +}; + +/* + * wsa881x_codec_info_create_codec_entry - creates wsa881x module + * @codec_root: The parent directory + * @codec: Codec instance + * + * Creates wsa881x module and version entry under the given + * parent directory. + * + * Return: 0 on success or negative error code on failure. + */ +int wsa881x_codec_info_create_codec_entry(struct snd_info_entry *codec_root, + struct snd_soc_codec *codec) +{ + struct snd_info_entry *version_entry; + struct wsa881x_priv *wsa881x; + struct snd_soc_card *card; + char name[80]; + + if (!codec_root || !codec) + return -EINVAL; + + wsa881x = snd_soc_codec_get_drvdata(codec); + card = codec->component.card; + snprintf(name, sizeof(name), "%s.%x", "wsa881x", + (u32)wsa881x->swr_slave->addr); + + wsa881x->entry = snd_info_create_subdir(codec_root->module, + (const char *)name, + codec_root); + if (!wsa881x->entry) { + dev_dbg(codec->dev, "%s: failed to create wsa881x entry\n", + __func__); + return -ENOMEM; + } + + version_entry = snd_info_create_card_entry(card->snd_card, + "version", + wsa881x->entry); + if (!version_entry) { + dev_dbg(codec->dev, "%s: failed to create wsa881x version entry\n", + __func__); + return -ENOMEM; + } + + version_entry->private_data = wsa881x; + version_entry->size = WSA881X_VERSION_ENTRY_SIZE; + version_entry->content = SNDRV_INFO_CONTENT_DATA; + version_entry->c.ops = &wsa881x_codec_info_ops; + + if (snd_info_register(version_entry) < 0) { + snd_info_free_entry(version_entry); + return -ENOMEM; + } + wsa881x->version_entry = version_entry; + + return 0; +} +EXPORT_SYMBOL(wsa881x_codec_info_create_codec_entry); + +static bool is_swr_slv_reg_readable(int reg) +{ + bool ret = true; + + if (((reg > 0x46) && (reg < 0x4A)) || + ((reg > 0x4A) && (reg < 0x50)) || + ((reg > 0x55) && (reg < 0xE0)) || + ((reg > 0xE0) && (reg < 0xF0)) || + ((reg > 0xF0) && (reg < 0x100)) || + ((reg > 0x105) && (reg < 0x120)) || + ((reg > 0x128) && (reg < 0x130)) || + ((reg > 0x138) && (reg < 0x200)) || + ((reg > 0x205) && (reg < 0x220)) || + ((reg > 0x228) && (reg < 0x230)) || + ((reg > 0x238) && (reg < 0x300)) || + ((reg > 0x305) && (reg < 0x320)) || + ((reg > 0x328) && (reg < 0x330)) || + ((reg > 0x338) && (reg < 0x400)) || + ((reg > 0x405) && (reg < 0x420))) + ret = false; + + return ret; +} + +static ssize_t wsa881x_swrslave_reg_show(char __user *ubuf, size_t count, + loff_t *ppos) +{ + int i, reg_val, len; + ssize_t total = 0; + char tmp_buf[SWR_SLV_MAX_BUF_LEN]; + + if (!ubuf || !ppos || (devnum == 0)) + return 0; + + for (i = (((int) *ppos / BYTES_PER_LINE) + SWR_SLV_START_REG_ADDR); + i <= SWR_SLV_MAX_REG_ADDR; i++) { + if (!is_swr_slv_reg_readable(i)) + continue; + swr_read(dbgwsa881x->swr_slave, devnum, + i, ®_val, 1); + len = snprintf(tmp_buf, 25, "0x%.3x: 0x%.2x\n", i, + (reg_val & 0xFF)); + if ((total + len) >= count - 1) + break; + if (copy_to_user((ubuf + total), tmp_buf, len)) { + pr_err("%s: fail to copy reg dump\n", __func__); + total = -EFAULT; + goto copy_err; + } + *ppos += len; + total += len; + } + +copy_err: + return total; +} + +static ssize_t codec_debug_read(struct file *file, char __user *ubuf, + size_t count, loff_t *ppos) +{ + char lbuf[SWR_SLV_RD_BUF_LEN]; + char *access_str; + ssize_t ret_cnt; + + if (!count || !file || !ppos || !ubuf) + return -EINVAL; + + access_str = file->private_data; + if (*ppos < 0) + return -EINVAL; + + if (!strcmp(access_str, "swrslave_peek")) { + snprintf(lbuf, sizeof(lbuf), "0x%x\n", (read_data & 0xFF)); + ret_cnt = simple_read_from_buffer(ubuf, count, ppos, lbuf, + strnlen(lbuf, 7)); + } else if (!strcmp(access_str, "swrslave_reg_dump")) { + ret_cnt = wsa881x_swrslave_reg_show(ubuf, count, ppos); + } else { + pr_err("%s: %s not permitted to read\n", __func__, access_str); + ret_cnt = -EPERM; + } + return ret_cnt; +} + +static ssize_t codec_debug_write(struct file *filp, + const char __user *ubuf, size_t cnt, loff_t *ppos) +{ + char lbuf[SWR_SLV_WR_BUF_LEN]; + int rc; + u32 param[5]; + char *access_str; + + if (!filp || !ppos || !ubuf) + return -EINVAL; + + access_str = filp->private_data; + if (cnt > sizeof(lbuf) - 1) + return -EINVAL; + + rc = copy_from_user(lbuf, ubuf, cnt); + if (rc) + return -EFAULT; + + lbuf[cnt] = '\0'; + if (!strcmp(access_str, "swrslave_poke")) { + /* write */ + rc = get_parameters(lbuf, param, 3); + if ((param[0] <= SWR_SLV_MAX_REG_ADDR) && (param[1] <= 0xFF) && + (rc == 0)) + swr_write(dbgwsa881x->swr_slave, param[2], + param[0], ¶m[1]); + else + rc = -EINVAL; + } else if (!strcmp(access_str, "swrslave_peek")) { + /* read */ + rc = get_parameters(lbuf, param, 2); + if ((param[0] <= SWR_SLV_MAX_REG_ADDR) && (rc == 0)) + swr_read(dbgwsa881x->swr_slave, param[1], + param[0], &read_data, 1); + else + rc = -EINVAL; + } else if (!strcmp(access_str, "swrslave_reg_dump")) { + /* reg dump */ + rc = get_parameters(lbuf, param, 1); + if ((rc == 0) && (param[0] > 0) && + (param[0] <= SWR_SLV_MAX_DEVICES)) + devnum = param[0]; + else + rc = -EINVAL; + } + if (rc == 0) + rc = cnt; + else + pr_err("%s: rc = %d\n", __func__, rc); + + return rc; +} + +static const struct file_operations codec_debug_ops = { + .open = codec_debug_open, + .write = codec_debug_write, + .read = codec_debug_read, +}; + +static void wsa881x_regcache_sync(struct wsa881x_priv *wsa881x) +{ + mutex_lock(&wsa881x->res_lock); + if (wsa881x->state != WSA881X_DEV_READY) { + regcache_mark_dirty(wsa881x->regmap); + regcache_sync(wsa881x->regmap); + wsa881x->state = WSA881X_DEV_READY; + } + mutex_unlock(&wsa881x->res_lock); +} + +static const struct reg_sequence wsa881x_pre_pmu_pa[] = { + {WSA881X_SPKR_DRV_GAIN, 0x41, 0}, + {WSA881X_SPKR_MISC_CTL1, 0x01, 0}, + {WSA881X_ADC_EN_DET_TEST_I, 0x01, 0}, + {WSA881X_ADC_EN_MODU_V, 0x02, 0}, + {WSA881X_ADC_EN_DET_TEST_V, 0x10, 0}, + {WSA881X_SPKR_PWRSTG_DBG, 0xA0, 0}, +}; + +static const struct reg_sequence wsa881x_pre_pmu_pa_2_0[] = { + {WSA881X_SPKR_DRV_GAIN, 0x41, 0}, + {WSA881X_SPKR_MISC_CTL1, 0x87, 0}, +}; + +static const struct reg_sequence wsa881x_post_pmu_pa[] = { + {WSA881X_SPKR_PWRSTG_DBG, 0x00, 0}, + {WSA881X_ADC_EN_DET_TEST_V, 0x00, 0}, + {WSA881X_ADC_EN_MODU_V, 0x00, 0}, + {WSA881X_ADC_EN_DET_TEST_I, 0x00, 0}, +}; + +static const struct reg_sequence wsa881x_vi_txfe_en[] = { + {WSA881X_SPKR_PROT_FE_VSENSE_VCM, 0x85, 0}, + {WSA881X_SPKR_PROT_ATEST2, 0x0A, 0}, + {WSA881X_SPKR_PROT_FE_GAIN, 0xCF, 0}, +}; + +static const struct reg_sequence wsa881x_vi_txfe_en_2_0[] = { + {WSA881X_SPKR_PROT_FE_VSENSE_VCM, 0x85, 0}, + {WSA881X_SPKR_PROT_ATEST2, 0x0A, 0}, + {WSA881X_SPKR_PROT_FE_GAIN, 0x47, 0}, +}; + +static int wsa881x_boost_ctrl(struct snd_soc_codec *codec, bool enable) +{ + dev_dbg(codec->dev, "%s: enable:%d\n", __func__, enable); + if (enable) + snd_soc_update_bits(codec, WSA881X_BOOST_EN_CTL, 0x80, 0x80); + else + snd_soc_update_bits(codec, WSA881X_BOOST_EN_CTL, 0x80, 0x00); + /* + * 1.5ms sleep is needed after boost enable/disable as per + * HW requirement + */ + usleep_range(1500, 1510); + return 0; +} + +static int wsa881x_visense_txfe_ctrl(struct snd_soc_codec *codec, bool enable, + u8 isense1_gain, u8 isense2_gain, + u8 vsense_gain) +{ + struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, + "%s: enable:%d, isense1 gain: %d, isense2 gain: %d, vsense_gain %d\n", + __func__, enable, isense1_gain, isense2_gain, vsense_gain); + + if (enable) { + regmap_multi_reg_write(wsa881x->regmap, + wsa881x_vi_txfe_en_2_0, + ARRAY_SIZE(wsa881x_vi_txfe_en_2_0)); + } else { + snd_soc_update_bits(codec, WSA881X_SPKR_PROT_FE_VSENSE_VCM, + 0x08, 0x08); + /* + * 200us sleep is needed after visense txfe disable as per + * HW requirement. + */ + usleep_range(200, 210); + snd_soc_update_bits(codec, WSA881X_SPKR_PROT_FE_GAIN, + 0x01, 0x00); + } + return 0; +} + +static int wsa881x_visense_adc_ctrl(struct snd_soc_codec *codec, bool enable) +{ + + dev_dbg(codec->dev, "%s: enable:%d\n", __func__, enable); + snd_soc_update_bits(codec, WSA881X_ADC_EN_MODU_V, (0x01 << 7), + (enable << 7)); + snd_soc_update_bits(codec, WSA881X_ADC_EN_MODU_I, (0x01 << 7), + (enable << 7)); + return 0; +} + +static void wsa881x_bandgap_ctrl(struct snd_soc_codec *codec, bool enable) +{ + struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, "%s: enable:%d, bg_count:%d\n", __func__, + enable, wsa881x->bg_cnt); + mutex_lock(&wsa881x->bg_lock); + if (enable) { + ++wsa881x->bg_cnt; + if (wsa881x->bg_cnt == 1) { + snd_soc_update_bits(codec, WSA881X_TEMP_OP, + 0x08, 0x08); + /* 400usec sleep is needed as per HW requirement */ + usleep_range(400, 410); + snd_soc_update_bits(codec, WSA881X_TEMP_OP, + 0x04, 0x04); + } + } else { + --wsa881x->bg_cnt; + if (wsa881x->bg_cnt <= 0) { + WARN_ON(wsa881x->bg_cnt < 0); + wsa881x->bg_cnt = 0; + snd_soc_update_bits(codec, WSA881X_TEMP_OP, 0x04, 0x00); + snd_soc_update_bits(codec, WSA881X_TEMP_OP, 0x08, 0x00); + } + } + mutex_unlock(&wsa881x->bg_lock); +} + +static void wsa881x_clk_ctrl(struct snd_soc_codec *codec, bool enable) +{ + struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, "%s: enable:%d, clk_count:%d\n", __func__, + enable, wsa881x->clk_cnt); + mutex_lock(&wsa881x->res_lock); + if (enable) { + ++wsa881x->clk_cnt; + if (wsa881x->clk_cnt == 1) { + snd_soc_write(codec, WSA881X_CDC_DIG_CLK_CTL, 0x01); + snd_soc_write(codec, WSA881X_CDC_ANA_CLK_CTL, 0x01); + } + } else { + --wsa881x->clk_cnt; + if (wsa881x->clk_cnt <= 0) { + WARN_ON(wsa881x->clk_cnt < 0); + wsa881x->clk_cnt = 0; + snd_soc_write(codec, WSA881X_CDC_DIG_CLK_CTL, 0x00); + snd_soc_write(codec, WSA881X_CDC_ANA_CLK_CTL, 0x00); + } + } + mutex_unlock(&wsa881x->res_lock); +} + +static int wsa881x_get_compander(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = wsa881x->comp_enable; + return 0; +} + +static int wsa881x_set_compander(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec); + int value = ucontrol->value.integer.value[0]; + + dev_dbg(codec->dev, "%s: Compander enable current %d, new %d\n", + __func__, wsa881x->comp_enable, value); + wsa881x->comp_enable = value; + return 0; +} + +static int wsa881x_get_boost(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = wsa881x->boost_enable; + return 0; +} + +static int wsa881x_set_boost(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec); + int value = ucontrol->value.integer.value[0]; + + dev_dbg(codec->dev, "%s: Boost enable current %d, new %d\n", + __func__, wsa881x->boost_enable, value); + wsa881x->boost_enable = value; + return 0; +} + +static int wsa881x_get_visense(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = wsa881x->visense_enable; + return 0; +} + +static int wsa881x_set_visense(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec); + int value = ucontrol->value.integer.value[0]; + + dev_dbg(codec->dev, "%s: VIsense enable current %d, new %d\n", + __func__, wsa881x->visense_enable, value); + wsa881x->visense_enable = value; + return 0; +} + +static int wsa881x_set_boost_level(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + u8 wsa_boost_level = 0; + + dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n", + __func__, ucontrol->value.integer.value[0]); + + wsa_boost_level = ucontrol->value.integer.value[0]; + snd_soc_update_bits(codec, WSA881X_BOOST_PRESET_OUT1, + 0xff, wsa_boost_level); + + return 0; +} + +static int wsa881x_get_boost_level(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + u8 wsa_boost_level = 0; + + wsa_boost_level = snd_soc_read(codec, WSA881X_BOOST_PRESET_OUT1); + ucontrol->value.integer.value[0] = wsa_boost_level; + dev_dbg(codec->dev, "%s: boost level = 0x%x\n", __func__, + wsa_boost_level); + + return 0; +} + +static const struct snd_kcontrol_new wsa881x_snd_controls[] = { + SOC_SINGLE_EXT("COMP Switch", SND_SOC_NOPM, 0, 1, 0, + wsa881x_get_compander, wsa881x_set_compander), + + SOC_SINGLE_EXT("BOOST Switch", SND_SOC_NOPM, 0, 1, 0, + wsa881x_get_boost, wsa881x_set_boost), + + SOC_SINGLE_EXT("VISENSE Switch", SND_SOC_NOPM, 0, 1, 0, + wsa881x_get_visense, wsa881x_set_visense), + + SOC_SINGLE_EXT("Boost Level", SND_SOC_NOPM, 0, 0xff, 0, + wsa881x_get_boost_level, wsa881x_set_boost_level), +}; + +static const struct snd_kcontrol_new swr_dac_port[] = { + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0) +}; + +static int wsa881x_set_port(struct snd_soc_codec *codec, int port_idx, + u8 *port_id, u8 *num_ch, u8 *ch_mask, u32 *ch_rate, + u8 *port_type) +{ + struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec); + + *port_id = wsa881x->port[port_idx].port_id; + *num_ch = wsa881x->port[port_idx].num_ch; + *ch_mask = wsa881x->port[port_idx].ch_mask; + *ch_rate = wsa881x->port[port_idx].ch_rate; + *port_type = wsa881x->port[port_idx].port_type; + return 0; +} + +static int wsa881x_enable_swr_dac_port(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec); + u8 port_id[WSA881X_MAX_SWR_PORTS]; + u8 num_ch[WSA881X_MAX_SWR_PORTS]; + u8 ch_mask[WSA881X_MAX_SWR_PORTS]; + u32 ch_rate[WSA881X_MAX_SWR_PORTS]; + u8 port_type[WSA881X_MAX_SWR_PORTS]; + u8 num_port = 0; + + dev_dbg(codec->dev, "%s: event %d name %s\n", __func__, + event, w->name); + if (wsa881x == NULL) + return -EINVAL; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + wsa881x_set_port(codec, SWR_DAC_PORT, + &port_id[num_port], &num_ch[num_port], + &ch_mask[num_port], &ch_rate[num_port], + &port_type[num_port]); + ++num_port; + + if (wsa881x->comp_enable) { + wsa881x_set_port(codec, SWR_COMP_PORT, + &port_id[num_port], &num_ch[num_port], + &ch_mask[num_port], &ch_rate[num_port], + &port_type[num_port]); + ++num_port; + } + if (wsa881x->boost_enable) { + wsa881x_set_port(codec, SWR_BOOST_PORT, + &port_id[num_port], &num_ch[num_port], + &ch_mask[num_port], &ch_rate[num_port], + &port_type[num_port]); + ++num_port; + } + if (wsa881x->visense_enable) { + wsa881x_set_port(codec, SWR_VISENSE_PORT, + &port_id[num_port], &num_ch[num_port], + &ch_mask[num_port], &ch_rate[num_port], + &port_type[num_port]); + ++num_port; + } + swr_connect_port(wsa881x->swr_slave, &port_id[0], num_port, + &ch_mask[0], &ch_rate[0], &num_ch[0], + &port_type[0]); + break; + case SND_SOC_DAPM_POST_PMU: + break; + case SND_SOC_DAPM_PRE_PMD: + break; + case SND_SOC_DAPM_POST_PMD: + wsa881x_set_port(codec, SWR_DAC_PORT, + &port_id[num_port], &num_ch[num_port], + &ch_mask[num_port], &ch_rate[num_port], + &port_type[num_port]); + ++num_port; + + if (wsa881x->comp_enable) { + wsa881x_set_port(codec, SWR_COMP_PORT, + &port_id[num_port], &num_ch[num_port], + &ch_mask[num_port], &ch_rate[num_port], + &port_type[num_port]); + ++num_port; + } + if (wsa881x->boost_enable) { + wsa881x_set_port(codec, SWR_BOOST_PORT, + &port_id[num_port], &num_ch[num_port], + &ch_mask[num_port], &ch_rate[num_port], + &port_type[num_port]); + ++num_port; + } + if (wsa881x->visense_enable) { + wsa881x_set_port(codec, SWR_VISENSE_PORT, + &port_id[num_port], &num_ch[num_port], + &ch_mask[num_port], &ch_rate[num_port], + &port_type[num_port]); + ++num_port; + } + swr_disconnect_port(wsa881x->swr_slave, &port_id[0], num_port, + &ch_mask[0], &port_type[0]); + break; + default: + break; + } + return 0; +} + +static int wsa881x_rdac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, "%s: %s %d boost %d visense %d\n", __func__, + w->name, event, wsa881x->boost_enable, + wsa881x->visense_enable); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + mutex_lock(&wsa881x->temp_lock); + wsa881x_resource_acquire(codec, ENABLE); + mutex_unlock(&wsa881x->temp_lock); + wsa881x_boost_ctrl(codec, ENABLE); + break; + case SND_SOC_DAPM_POST_PMD: + swr_slvdev_datapath_control(wsa881x->swr_slave, + wsa881x->swr_slave->dev_num, + false); + wsa881x_boost_ctrl(codec, DISABLE); + mutex_lock(&wsa881x->temp_lock); + wsa881x_resource_acquire(codec, DISABLE); + mutex_unlock(&wsa881x->temp_lock); + break; + } + return 0; +} + +static int wsa881x_ramp_pa_gain(struct snd_soc_codec *codec, + int min_gain, int max_gain, int udelay) +{ + int val; + + for (val = min_gain; max_gain <= val; val--) { + snd_soc_update_bits(codec, WSA881X_SPKR_DRV_GAIN, + 0xF0, val << 4); + /* + * 1ms delay is needed for every step change in gain as per + * HW requirement. + */ + usleep_range(udelay, udelay+10); + } + return 0; +} + +static void wsa881x_ocp_ctl_work(struct work_struct *work) +{ + struct wsa881x_priv *wsa881x; + struct delayed_work *dwork; + struct snd_soc_codec *codec; + int temp_val; + + dwork = to_delayed_work(work); + wsa881x = container_of(dwork, struct wsa881x_priv, ocp_ctl_work); + + codec = wsa881x->codec; + wsa881x_get_temp(wsa881x->tz_pdata.tz_dev, &temp_val); + dev_dbg(codec->dev, " temp = %d\n", temp_val); + + if (temp_val <= WSA881X_OCP_CTL_TEMP_CELSIUS) + snd_soc_update_bits(codec, WSA881X_SPKR_OCP_CTL, 0xC0, 0x00); + else + snd_soc_update_bits(codec, WSA881X_SPKR_OCP_CTL, 0xC0, 0xC0); + + schedule_delayed_work(&wsa881x->ocp_ctl_work, + msecs_to_jiffies(wsa881x_ocp_poll_timer_sec * 1000)); +} + +static int wsa881x_spkr_pa_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec); + int min_gain, max_gain; + + dev_dbg(codec->dev, "%s: %s %d\n", __func__, w->name, event); + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_update_bits(codec, WSA881X_SPKR_OCP_CTL, 0xC0, 0x80); + regmap_multi_reg_write(wsa881x->regmap, + wsa881x_pre_pmu_pa_2_0, + ARRAY_SIZE(wsa881x_pre_pmu_pa_2_0)); + swr_slvdev_datapath_control(wsa881x->swr_slave, + wsa881x->swr_slave->dev_num, + true); + /* Set register mode if compander is not enabled */ + if (!wsa881x->comp_enable) + snd_soc_update_bits(codec, WSA881X_SPKR_DRV_GAIN, + 0x08, 0x08); + else + snd_soc_update_bits(codec, WSA881X_SPKR_DRV_GAIN, + 0x08, 0x00); + + break; + case SND_SOC_DAPM_POST_PMU: + if (!wsa881x->comp_enable) { + max_gain = wsa881x->pa_gain; + /* + * Gain has to set incrementally in 4 steps + * as per HW sequence + */ + if (max_gain > G_4P5DB) + min_gain = G_0DB; + else + min_gain = max_gain + 3; + /* + * 1ms delay is needed before change in gain + * as per HW requirement. + */ + usleep_range(1000, 1010); + wsa881x_ramp_pa_gain(codec, min_gain, max_gain, 1000); + } + if (wsa881x->visense_enable) { + wsa881x_visense_txfe_ctrl(codec, ENABLE, + 0x00, 0x03, 0x01); + snd_soc_update_bits(codec, WSA881X_ADC_EN_SEL_IBAIS, + 0x07, 0x01); + wsa881x_visense_adc_ctrl(codec, ENABLE); + } + schedule_delayed_work(&wsa881x->ocp_ctl_work, + msecs_to_jiffies(WSA881X_OCP_CTL_TIMER_SEC * 1000)); + /* Force remove group */ + swr_remove_from_group(wsa881x->swr_slave, + wsa881x->swr_slave->dev_num); + break; + case SND_SOC_DAPM_POST_PMD: + if (wsa881x->visense_enable) { + wsa881x_visense_adc_ctrl(codec, DISABLE); + wsa881x_visense_txfe_ctrl(codec, DISABLE, + 0x00, 0x01, 0x01); + } + cancel_delayed_work_sync(&wsa881x->ocp_ctl_work); + snd_soc_update_bits(codec, WSA881X_SPKR_OCP_CTL, 0xC0, 0xC0); + break; + } + return 0; +} + +static const struct snd_soc_dapm_widget wsa881x_dapm_widgets[] = { + SND_SOC_DAPM_INPUT("IN"), + + SND_SOC_DAPM_MIXER_E("SWR DAC_Port", SND_SOC_NOPM, 0, 0, swr_dac_port, + ARRAY_SIZE(swr_dac_port), wsa881x_enable_swr_dac_port, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_DAC_E("RDAC", NULL, WSA881X_SPKR_DAC_CTL, 7, 0, + wsa881x_rdac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_PGA_E("SPKR PGA", WSA881X_SPKR_DRV_EN, 7, 0, NULL, 0, + wsa881x_spkr_pa_event, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_OUTPUT("SPKR"), +}; + +static const struct snd_soc_dapm_route wsa881x_audio_map[] = { + {"SWR DAC_Port", "Switch", "IN"}, + {"RDAC", NULL, "SWR DAC_Port"}, + {"SPKR PGA", NULL, "RDAC"}, + {"SPKR", NULL, "SPKR PGA"}, +}; + +int wsa881x_set_channel_map(struct snd_soc_codec *codec, u8 *port, u8 num_port, + unsigned int *ch_mask, unsigned int *ch_rate, + u8 *port_type) +{ + struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec); + int i; + + if (!port || !ch_mask || !ch_rate || + (num_port > WSA881X_MAX_SWR_PORTS)) { + dev_err(codec->dev, + "%s: Invalid port=%pK, ch_mask=%pK, ch_rate=%pK\n", + __func__, port, ch_mask, ch_rate); + return -EINVAL; + } + for (i = 0; i < num_port; i++) { + wsa881x->port[i].port_id = port[i]; + wsa881x->port[i].ch_mask = ch_mask[i]; + wsa881x->port[i].ch_rate = ch_rate[i]; + wsa881x->port[i].num_ch = __sw_hweight8(ch_mask[i]); + if (port_type) + wsa881x->port[i].port_type = port_type[i]; + } + return 0; +} +EXPORT_SYMBOL(wsa881x_set_channel_map); + +static void wsa881x_init(struct snd_soc_codec *codec) +{ + struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec); + + wsa881x->version = snd_soc_read(codec, WSA881X_CHIP_ID1); + wsa881x_regmap_defaults(wsa881x->regmap, wsa881x->version); + /* Enable software reset output from soundwire slave */ + snd_soc_update_bits(codec, WSA881X_SWR_RESET_EN, 0x07, 0x07); + /* Bring out of analog reset */ + snd_soc_update_bits(codec, WSA881X_CDC_RST_CTL, 0x02, 0x02); + /* Bring out of digital reset */ + snd_soc_update_bits(codec, WSA881X_CDC_RST_CTL, 0x01, 0x01); + + snd_soc_update_bits(codec, WSA881X_CLOCK_CONFIG, 0x10, 0x10); + snd_soc_update_bits(codec, WSA881X_SPKR_OCP_CTL, 0x02, 0x02); + snd_soc_update_bits(codec, WSA881X_SPKR_MISC_CTL1, 0xC0, 0x80); + snd_soc_update_bits(codec, WSA881X_SPKR_MISC_CTL1, 0x06, 0x06); + snd_soc_update_bits(codec, WSA881X_SPKR_BIAS_INT, 0xFF, 0x00); + snd_soc_update_bits(codec, WSA881X_SPKR_PA_INT, 0xF0, 0x40); + snd_soc_update_bits(codec, WSA881X_SPKR_PA_INT, 0x0E, 0x0E); + snd_soc_update_bits(codec, WSA881X_BOOST_LOOP_STABILITY, + 0x03, 0x03); + snd_soc_update_bits(codec, WSA881X_BOOST_MISC2_CTL, 0xFF, 0x14); + snd_soc_update_bits(codec, WSA881X_BOOST_START_CTL, 0x80, 0x80); + snd_soc_update_bits(codec, WSA881X_BOOST_START_CTL, 0x03, 0x00); + snd_soc_update_bits(codec, WSA881X_BOOST_SLOPE_COMP_ISENSE_FB, + 0x0C, 0x04); + snd_soc_update_bits(codec, WSA881X_BOOST_SLOPE_COMP_ISENSE_FB, + 0x03, 0x00); + if (snd_soc_read(codec, WSA881X_OTP_REG_0)) + snd_soc_update_bits(codec, WSA881X_BOOST_PRESET_OUT1, + 0xF0, 0x70); + snd_soc_update_bits(codec, WSA881X_BOOST_PRESET_OUT2, + 0xF0, 0x30); + snd_soc_update_bits(codec, WSA881X_SPKR_DRV_EN, 0x08, 0x08); + snd_soc_update_bits(codec, WSA881X_BOOST_CURRENT_LIMIT, + 0x0F, 0x08); + snd_soc_update_bits(codec, WSA881X_SPKR_OCP_CTL, 0x30, 0x30); + snd_soc_update_bits(codec, WSA881X_SPKR_OCP_CTL, 0x0C, 0x00); + snd_soc_update_bits(codec, WSA881X_OTP_REG_28, 0x3F, 0x3A); + snd_soc_update_bits(codec, WSA881X_BONGO_RESRV_REG1, + 0xFF, 0xB2); + snd_soc_update_bits(codec, WSA881X_BONGO_RESRV_REG2, + 0xFF, 0x05); +} + +static int32_t wsa881x_resource_acquire(struct snd_soc_codec *codec, + bool enable) +{ + wsa881x_clk_ctrl(codec, enable); + wsa881x_bandgap_ctrl(codec, enable); + return 0; +} + +static int32_t wsa881x_temp_reg_read(struct snd_soc_codec *codec, + struct wsa_temp_register *wsa_temp_reg) +{ + struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec); + struct swr_device *dev; + u8 retry = WSA881X_NUM_RETRY; + u8 devnum = 0; + + if (!wsa881x) { + dev_err(codec->dev, "%s: wsa881x is NULL\n", __func__); + return -EINVAL; + } + dev = wsa881x->swr_slave; + if (dev && (wsa881x->state == WSA881X_DEV_DOWN)) { + while (swr_get_logical_dev_num(dev, dev->addr, &devnum) && + retry--) { + /* Retry after 1 msec delay */ + usleep_range(1000, 1100); + } + if (retry == 0) { + dev_err(codec->dev, + "%s get devnum %d for dev addr %lx failed\n", + __func__, devnum, dev->addr); + return -EINVAL; + } + } + wsa881x_regcache_sync(wsa881x); + mutex_lock(&wsa881x->temp_lock); + wsa881x_resource_acquire(codec, ENABLE); + + snd_soc_update_bits(codec, WSA881X_TADC_VALUE_CTL, 0x01, 0x00); + wsa_temp_reg->dmeas_msb = snd_soc_read(codec, WSA881X_TEMP_MSB); + wsa_temp_reg->dmeas_lsb = snd_soc_read(codec, WSA881X_TEMP_LSB); + snd_soc_update_bits(codec, WSA881X_TADC_VALUE_CTL, 0x01, 0x01); + wsa_temp_reg->d1_msb = snd_soc_read(codec, WSA881X_OTP_REG_1); + wsa_temp_reg->d1_lsb = snd_soc_read(codec, WSA881X_OTP_REG_2); + wsa_temp_reg->d2_msb = snd_soc_read(codec, WSA881X_OTP_REG_3); + wsa_temp_reg->d2_lsb = snd_soc_read(codec, WSA881X_OTP_REG_4); + + wsa881x_resource_acquire(codec, DISABLE); + mutex_unlock(&wsa881x->temp_lock); + + return 0; +} + +static int wsa881x_probe(struct snd_soc_codec *codec) +{ + struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec); + struct swr_device *dev; + + if (!wsa881x) + return -EINVAL; + + dev = wsa881x->swr_slave; + wsa881x->codec = codec; + mutex_init(&wsa881x->bg_lock); + wsa881x_init(codec); + snprintf(wsa881x->tz_pdata.name, sizeof(wsa881x->tz_pdata.name), + "%s.%x", "wsatz", (u8)dev->addr); + wsa881x->bg_cnt = 0; + wsa881x->clk_cnt = 0; + wsa881x->tz_pdata.codec = codec; + wsa881x->tz_pdata.wsa_temp_reg_read = wsa881x_temp_reg_read; + wsa881x_init_thermal(&wsa881x->tz_pdata); + snd_soc_add_codec_controls(codec, wsa_snd_controls, + ARRAY_SIZE(wsa_snd_controls)); + INIT_DELAYED_WORK(&wsa881x->ocp_ctl_work, wsa881x_ocp_ctl_work); + return 0; +} + +static int wsa881x_remove(struct snd_soc_codec *codec) +{ + struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec); + + if (wsa881x->tz_pdata.tz_dev) + wsa881x_deinit_thermal(wsa881x->tz_pdata.tz_dev); + mutex_destroy(&wsa881x->bg_lock); + + return 0; +} + +static struct regmap *wsa881x_get_regmap(struct device *dev) +{ + struct wsa881x_priv *control = swr_get_dev_data(to_swr_device(dev)); + + if (!control) + return NULL; + + return control->regmap; +} + +static struct snd_soc_codec_driver soc_codec_dev_wsa881x = { + .probe = wsa881x_probe, + .remove = wsa881x_remove, + .get_regmap = wsa881x_get_regmap, + .component_driver = { + .controls = wsa881x_snd_controls, + .num_controls = ARRAY_SIZE(wsa881x_snd_controls), + .dapm_widgets = wsa881x_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wsa881x_dapm_widgets), + .dapm_routes = wsa881x_audio_map, + .num_dapm_routes = ARRAY_SIZE(wsa881x_audio_map), + }, +}; + +static int wsa881x_gpio_ctrl(struct wsa881x_priv *wsa881x, bool enable) +{ + int ret = 0; + + if (wsa881x->pd_gpio < 0) { + dev_err(wsa881x->dev, "%s: gpio is not valid %d\n", + __func__, wsa881x->pd_gpio); + return -EINVAL; + } + + if (wsa881x->wsa_rst_np) { + if (enable) + ret = msm_cdc_pinctrl_select_active_state( + wsa881x->wsa_rst_np); + else + ret = msm_cdc_pinctrl_select_sleep_state( + wsa881x->wsa_rst_np); + if (ret != 0) + dev_err(wsa881x->dev, + "%s: Failed to turn state %d; ret=%d\n", + __func__, enable, ret); + } else { + if (gpio_is_valid(wsa881x->pd_gpio)) + gpio_direction_output(wsa881x->pd_gpio, enable); + } + + return ret; +} + +static int wsa881x_gpio_init(struct swr_device *pdev) +{ + int ret = 0; + struct wsa881x_priv *wsa881x; + + wsa881x = swr_get_dev_data(pdev); + if (!wsa881x) { + dev_err(&pdev->dev, "%s: wsa881x is NULL\n", __func__); + return -EINVAL; + } + dev_dbg(&pdev->dev, "%s: gpio %d request with name %s\n", + __func__, wsa881x->pd_gpio, dev_name(&pdev->dev)); + ret = gpio_request(wsa881x->pd_gpio, dev_name(&pdev->dev)); + if (ret) { + if (ret == -EBUSY) { + /* GPIO was already requested */ + dev_dbg(&pdev->dev, + "%s: gpio %d is already set to high\n", + __func__, wsa881x->pd_gpio); + ret = 0; + } else { + dev_err(&pdev->dev, "%s: Failed to request gpio %d, err: %d\n", + __func__, wsa881x->pd_gpio, ret); + } + } + return ret; +} + +static int wsa881x_swr_probe(struct swr_device *pdev) +{ + int ret = 0; + struct wsa881x_priv *wsa881x; + u8 devnum = 0; + bool pin_state_current = false; + + wsa881x = devm_kzalloc(&pdev->dev, sizeof(struct wsa881x_priv), + GFP_KERNEL); + if (!wsa881x) + return -ENOMEM; + wsa881x->wsa_rst_np = of_parse_phandle(pdev->dev.of_node, + "qcom,spkr-sd-n-node", 0); + if (!wsa881x->wsa_rst_np) { + dev_dbg(&pdev->dev, "%s: Not using pinctrl, fallback to gpio\n", + __func__); + wsa881x->pd_gpio = of_get_named_gpio(pdev->dev.of_node, + "qcom,spkr-sd-n-gpio", 0); + if (wsa881x->pd_gpio < 0) { + dev_err(&pdev->dev, "%s: %s property is not found %d\n", + __func__, "qcom,spkr-sd-n-gpio", + wsa881x->pd_gpio); + goto err; + } + dev_dbg(&pdev->dev, "%s: reset gpio %d\n", __func__, + wsa881x->pd_gpio); + } + swr_set_dev_data(pdev, wsa881x); + + wsa881x->swr_slave = pdev; + + if (!wsa881x->wsa_rst_np) { + ret = wsa881x_gpio_init(pdev); + if (ret) + goto err; + } + if (wsa881x->wsa_rst_np) + pin_state_current = msm_cdc_pinctrl_get_state( + wsa881x->wsa_rst_np); + wsa881x_gpio_ctrl(wsa881x, true); + wsa881x->state = WSA881X_DEV_UP; + + if (!debugfs_wsa881x_dent) { + dbgwsa881x = wsa881x; + debugfs_wsa881x_dent = debugfs_create_dir( + "wsa881x_swr_slave", 0); + if (!IS_ERR(debugfs_wsa881x_dent)) { + debugfs_peek = debugfs_create_file("swrslave_peek", + S_IFREG | 0444, debugfs_wsa881x_dent, + (void *) "swrslave_peek", + &codec_debug_ops); + + debugfs_poke = debugfs_create_file("swrslave_poke", + S_IFREG | 0444, debugfs_wsa881x_dent, + (void *) "swrslave_poke", + &codec_debug_ops); + + debugfs_reg_dump = debugfs_create_file( + "swrslave_reg_dump", + S_IFREG | 0444, + debugfs_wsa881x_dent, + (void *) "swrslave_reg_dump", + &codec_debug_ops); + } + } + + /* + * Add 5msec delay to provide sufficient time for + * soundwire auto enumeration of slave devices as + * as per HW requirement. + */ + usleep_range(5000, 5010); + ret = swr_get_logical_dev_num(pdev, pdev->addr, &devnum); + if (ret) { + dev_dbg(&pdev->dev, + "%s get devnum %d for dev addr %lx failed\n", + __func__, devnum, pdev->addr); + goto dev_err; + } + pdev->dev_num = devnum; + + wsa881x->regmap = devm_regmap_init_swr(pdev, + &wsa881x_regmap_config); + if (IS_ERR(wsa881x->regmap)) { + ret = PTR_ERR(wsa881x->regmap); + dev_err(&pdev->dev, "%s: regmap_init failed %d\n", + __func__, ret); + goto dev_err; + } + + ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_wsa881x, + NULL, 0); + if (ret) { + dev_err(&pdev->dev, "%s: Codec registration failed\n", + __func__); + goto dev_err; + } + mutex_init(&wsa881x->res_lock); + mutex_init(&wsa881x->temp_lock); + + return 0; + +dev_err: + if (pin_state_current == false) + wsa881x_gpio_ctrl(wsa881x, false); + swr_remove_device(pdev); +err: + return ret; +} + +static int wsa881x_swr_remove(struct swr_device *pdev) +{ + struct wsa881x_priv *wsa881x; + + wsa881x = swr_get_dev_data(pdev); + if (!wsa881x) { + dev_err(&pdev->dev, "%s: wsa881x is NULL\n", __func__); + return -EINVAL; + } + debugfs_remove_recursive(debugfs_wsa881x_dent); + debugfs_wsa881x_dent = NULL; + mutex_destroy(&wsa881x->res_lock); + mutex_destroy(&wsa881x->temp_lock); + snd_soc_unregister_codec(&pdev->dev); + if (wsa881x->pd_gpio) + gpio_free(wsa881x->pd_gpio); + swr_set_dev_data(pdev, NULL); + return 0; +} + +static int wsa881x_swr_up(struct swr_device *pdev) +{ + int ret; + struct wsa881x_priv *wsa881x; + + wsa881x = swr_get_dev_data(pdev); + if (!wsa881x) { + dev_err(&pdev->dev, "%s: wsa881x is NULL\n", __func__); + return -EINVAL; + } + ret = wsa881x_gpio_ctrl(wsa881x, true); + if (ret) + dev_err(&pdev->dev, "%s: Failed to enable gpio\n", __func__); + else + wsa881x->state = WSA881X_DEV_UP; + + return ret; +} + +static int wsa881x_swr_down(struct swr_device *pdev) +{ + struct wsa881x_priv *wsa881x; + int ret; + + wsa881x = swr_get_dev_data(pdev); + if (!wsa881x) { + dev_err(&pdev->dev, "%s: wsa881x is NULL\n", __func__); + return -EINVAL; + } + if (delayed_work_pending(&wsa881x->ocp_ctl_work)) + cancel_delayed_work_sync(&wsa881x->ocp_ctl_work); + ret = wsa881x_gpio_ctrl(wsa881x, false); + if (ret) + dev_err(&pdev->dev, "%s: Failed to disable gpio\n", __func__); + else + wsa881x->state = WSA881X_DEV_DOWN; + + return ret; +} + +static int wsa881x_swr_reset(struct swr_device *pdev) +{ + struct wsa881x_priv *wsa881x; + u8 retry = WSA881X_NUM_RETRY; + u8 devnum = 0; + + wsa881x = swr_get_dev_data(pdev); + if (!wsa881x) { + dev_err(&pdev->dev, "%s: wsa881x is NULL\n", __func__); + return -EINVAL; + } + if (wsa881x->state == WSA881X_DEV_READY) { + dev_dbg(&pdev->dev, "%s: device already active\n", __func__); + return 0; + } + + wsa881x->bg_cnt = 0; + wsa881x->clk_cnt = 0; + while (swr_get_logical_dev_num(pdev, pdev->addr, &devnum) && retry--) { + /* Retry after 1 msec delay */ + usleep_range(1000, 1100); + } + pdev->dev_num = devnum; + wsa881x_regcache_sync(wsa881x); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int wsa881x_swr_suspend(struct device *dev) +{ + dev_dbg(dev, "%s: system suspend\n", __func__); + return 0; +} + +static int wsa881x_swr_resume(struct device *dev) +{ + struct wsa881x_priv *wsa881x = swr_get_dev_data(to_swr_device(dev)); + + if (!wsa881x) { + dev_err(dev, "%s: wsa881x private data is NULL\n", __func__); + return -EINVAL; + } + dev_dbg(dev, "%s: system resume\n", __func__); + return 0; +} +#endif /* CONFIG_PM_SLEEP */ + +static const struct dev_pm_ops wsa881x_swr_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(wsa881x_swr_suspend, wsa881x_swr_resume) +}; + +static const struct swr_device_id wsa881x_swr_id[] = { + {"wsa881x", 0}, + {} +}; + +static const struct of_device_id wsa881x_swr_dt_match[] = { + { + .compatible = "qcom,wsa881x", + }, + {} +}; + +static struct swr_driver wsa881x_codec_driver = { + .driver = { + .name = "wsa881x", + .owner = THIS_MODULE, + .pm = &wsa881x_swr_pm_ops, + .of_match_table = wsa881x_swr_dt_match, + }, + .probe = wsa881x_swr_probe, + .remove = wsa881x_swr_remove, + .id_table = wsa881x_swr_id, + .device_up = wsa881x_swr_up, + .device_down = wsa881x_swr_down, + .reset_device = wsa881x_swr_reset, +}; + +static int __init wsa881x_codec_init(void) +{ + return swr_driver_register(&wsa881x_codec_driver); +} + +static void __exit wsa881x_codec_exit(void) +{ + swr_driver_unregister(&wsa881x_codec_driver); +} + +module_init(wsa881x_codec_init); +module_exit(wsa881x_codec_exit); + +MODULE_DESCRIPTION("WSA881x Codec driver"); +MODULE_LICENSE("GPL v2"); diff --git a/audio-kernel/asoc/codecs/wsa881x.h b/audio-kernel/asoc/codecs/wsa881x.h new file mode 100644 index 000000000..9f998e37f --- /dev/null +++ b/audio-kernel/asoc/codecs/wsa881x.h @@ -0,0 +1,56 @@ +/* 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 + * 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 _WSA881X_H +#define _WSA881X_H + +#include +#include +#include +#include "wsa881x-registers.h" + +#define WSA881X_MAX_SWR_PORTS 4 + +#if IS_ENABLED(CONFIG_SND_SOC_WSA881X) +extern int wsa881x_set_channel_map(struct snd_soc_codec *codec, u8 *port, + u8 num_port, unsigned int *ch_mask, + unsigned int *ch_rate, u8 *port_type); + +extern const u8 wsa881x_reg_readable[WSA881X_CACHE_SIZE]; +extern struct regmap_config wsa881x_regmap_config; +extern int wsa881x_codec_info_create_codec_entry( + struct snd_info_entry *codec_root, + struct snd_soc_codec *codec); +void wsa881x_regmap_defaults(struct regmap *regmap, u8 version); + +#else +extern int wsa881x_set_channel_map(struct snd_soc_codec *codec, u8 *port, + u8 num_port, unsigned int *ch_mask, + unsigned int *ch_rate, u8 *port_type); +{ + return 0; +} + +extern int wsa881x_codec_info_create_codec_entry( + struct snd_info_entry *codec_root, + struct snd_soc_codec *codec) +{ + return 0; +} + +void wsa881x_regmap_defaults(struct regmap *regmap, u8 version) +{ +} + +#endif + +#endif /* _WSA881X_H */ diff --git a/audio-kernel/asoc/device_event.h b/audio-kernel/asoc/device_event.h new file mode 100644 index 000000000..408d114e3 --- /dev/null +++ b/audio-kernel/asoc/device_event.h @@ -0,0 +1,20 @@ +/* Copyright (c) 2014, 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 __DEVICE_EVENT_H +#define __DEVICE_EVENT_H + +#define QC_AUDIO_EXTERNAL_SPK_1_EVENT "qc_ext_spk_1" +#define QC_AUDIO_EXTERNAL_SPK_2_EVENT "qc_ext_spk_2" +#define QC_AUDIO_EXTERNAL_MIC_EVENT "qc_ext_mic" + +#endif /* __DEVICE_EVENT_H */ diff --git a/audio-kernel/asoc/machine_815x_init.c b/audio-kernel/asoc/machine_815x_init.c new file mode 100644 index 000000000..ab1bdc1f6 --- /dev/null +++ b/audio-kernel/asoc/machine_815x_init.c @@ -0,0 +1,36 @@ +/* +Copyright (c) 2017-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 +#include +#include "machine_815x_init.h" + +static int __init audio_machine_815x_init(void) +{ + sm8150_init(); + sa8155_init(); + return 0; +} + +static void audio_machine_815x_exit(void) +{ + sm8150_exit(); + sa8155_exit(); +} + +module_init(audio_machine_815x_init); +module_exit(audio_machine_815x_exit); + +MODULE_DESCRIPTION("Audio Machine 815X Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/audio-kernel/asoc/machine_815x_init.h b/audio-kernel/asoc/machine_815x_init.h new file mode 100644 index 000000000..4ee6d4f96 --- /dev/null +++ b/audio-kernel/asoc/machine_815x_init.h @@ -0,0 +1,23 @@ +/* +Copyright (c) 2017-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 __MACHINE_815X_INIT_H__ +#define __MACHINE_815X_INIT_H__ +int sm8150_init(void); +int sa8155_init(void); + +void sm8150_exit(void); +void sa8155_exit(void); +#endif + diff --git a/audio-kernel/asoc/msm-audio-effects-q6-v2.c b/audio-kernel/asoc/msm-audio-effects-q6-v2.c new file mode 100644 index 000000000..b4dc30ab5 --- /dev/null +++ b/audio-kernel/asoc/msm-audio-effects-q6-v2.c @@ -0,0 +1,1416 @@ +/* Copyright (c) 2013-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 +#include +#include +#include +#include +#include +#include + +#define MAX_ENABLE_CMD_SIZE 32 + +#define GET_NEXT(ptr, upper_limit, rc) \ +({ \ + if (((ptr) + 1) > (upper_limit)) { \ + pr_err("%s: param list out of boundary\n", __func__); \ + (rc) = -EINVAL; \ + } \ + ((rc) == 0) ? *(ptr)++ : -EINVAL; \ +}) + +#define CHECK_PARAM_LEN(len, max_len, tag, rc) \ +do { \ + if ((len) > (max_len)) { \ + pr_err("%s: params length overflows\n", (tag)); \ + (rc) = -EINVAL; \ + } \ +} while (0) + + +/** + * msm_audio_effects_is_effmodule_supp_in_top - + * Checks if given topology and module in effects + * + */ +bool msm_audio_effects_is_effmodule_supp_in_top(int effect_module, + int topology) +{ + switch (effect_module) { + case VIRTUALIZER_MODULE: + case REVERB_MODULE: + case BASS_BOOST_MODULE: + case PBE_MODULE: + case EQ_MODULE: + switch (topology) { + case ASM_STREAM_POSTPROC_TOPO_ID_SA_PLUS: + return true; + default: + return false; + } + default: + return false; + } +} +EXPORT_SYMBOL(msm_audio_effects_is_effmodule_supp_in_top); + +int msm_audio_effects_enable_extn(struct audio_client *ac, + struct msm_nt_eff_all_config *effects, + bool flag) +{ + u32 flag_param = flag ? 1 : 0; + struct param_hdr_v3 param_hdr; + int rc = 0; + + pr_debug("%s\n", __func__); + memset(¶m_hdr, 0, sizeof(param_hdr)); + param_hdr.module_id = AUDPROC_MODULE_ID_VIRTUALIZER; + param_hdr.instance_id = INSTANCE_ID_0; + param_hdr.param_id = AUDPROC_PARAM_ID_ENABLE; + param_hdr.param_size = VIRTUALIZER_ENABLE_PARAM_SZ; + if (effects->virtualizer.enable_flag) + rc = q6asm_pack_and_set_pp_param_in_band(ac, param_hdr, + (u8 *) &flag_param); + + param_hdr.module_id = AUDPROC_MODULE_ID_BASS_BOOST; + param_hdr.instance_id = INSTANCE_ID_0; + param_hdr.param_id = AUDPROC_PARAM_ID_ENABLE; + param_hdr.param_size = BASS_BOOST_ENABLE_PARAM_SZ; + if (effects->bass_boost.enable_flag) + rc = q6asm_pack_and_set_pp_param_in_band(ac, param_hdr, + (u8 *) &flag_param); + + param_hdr.module_id = AUDPROC_MODULE_ID_POPLESS_EQUALIZER; + param_hdr.instance_id = INSTANCE_ID_0; + param_hdr.param_id = AUDPROC_PARAM_ID_ENABLE; + param_hdr.param_size = EQ_ENABLE_PARAM_SZ; + if (effects->equalizer.enable_flag) + rc = q6asm_pack_and_set_pp_param_in_band(ac, param_hdr, + (u8 *) &flag_param); + + return rc; +} + +/** + * msm_audio_effects_virtualizer_handler - + * Audio effects handler for virtualizer + * + * @ac: audio client handle + * @pbe: virtualizer params + * @values: values to be updated + * + * Return 0 on success or error on failure + */ +int msm_audio_effects_virtualizer_handler(struct audio_client *ac, + struct virtualizer_params *virtualizer, + long *values) +{ + long *param_max_offset = values + MAX_PP_PARAMS_SZ - 1; + char *params = NULL; + u8 *updt_params; + int rc = 0; + int devices = GET_NEXT(values, param_max_offset, rc); + int num_commands = GET_NEXT(values, param_max_offset, rc); + int i, prev_enable_flag; + uint32_t max_params_length = 0; + uint32_t params_length = 0; + struct param_hdr_v3 param_hdr; + u8 *param_data = NULL; + u32 packed_data_size = 0; + + pr_debug("%s\n", __func__); + if (!ac || (devices == -EINVAL) || (num_commands == -EINVAL)) { + pr_err("%s: cannot set audio effects\n", __func__); + return -EINVAL; + } + params = kzalloc(MAX_INBAND_PARAM_SZ, GFP_KERNEL); + if (!params) { + pr_err("%s, params memory alloc failed\n", __func__); + return -ENOMEM; + } + pr_debug("%s: device: %d\n", __func__, devices); + updt_params = (u8 *) params; + /* Set MID and IID once at top and only update param specific fields*/ + memset(¶m_hdr, 0, sizeof(param_hdr)); + param_hdr.module_id = AUDPROC_MODULE_ID_VIRTUALIZER; + param_hdr.instance_id = INSTANCE_ID_0; + for (i = 0; i < num_commands; i++) { + uint32_t command_id = + GET_NEXT(values, param_max_offset, rc); + uint32_t command_config_state = + GET_NEXT(values, param_max_offset, rc); + uint32_t index_offset = + GET_NEXT(values, param_max_offset, rc); + uint32_t length = + GET_NEXT(values, param_max_offset, rc); + switch (command_id) { + case VIRTUALIZER_ENABLE: + if (length != 1 || index_offset != 0) { + pr_err("VIRT ENABLE:invalid params\n"); + rc = -EINVAL; + goto invalid_config; + } + prev_enable_flag = virtualizer->enable_flag; + virtualizer->enable_flag = + GET_NEXT(values, param_max_offset, rc); + pr_debug("%s:VIRT ENABLE prev:%d, new:%d\n", __func__, + prev_enable_flag, virtualizer->enable_flag); + if (prev_enable_flag == virtualizer->enable_flag) + break; + max_params_length = params_length + + COMMAND_IID_PAYLOAD_SZ + + VIRTUALIZER_ENABLE_PARAM_SZ; + CHECK_PARAM_LEN(max_params_length, MAX_INBAND_PARAM_SZ, + "VIRT ENABLE", rc); + if (rc != 0) + break; + param_hdr.param_id = + AUDPROC_PARAM_ID_VIRTUALIZER_ENABLE; + param_hdr.param_size = VIRTUALIZER_ENABLE_PARAM_SZ; + param_data = (u8 *) &virtualizer->enable_flag; + break; + case VIRTUALIZER_STRENGTH: + if (length != 1 || index_offset != 0) { + pr_err("VIRT STRENGTH:invalid params\n"); + rc = -EINVAL; + goto invalid_config; + } + virtualizer->strength = + GET_NEXT(values, param_max_offset, rc); + pr_debug("%s: VIRT STRENGTH val: %d\n", + __func__, virtualizer->strength); + if (command_config_state != CONFIG_SET) + break; + max_params_length = params_length + + COMMAND_IID_PAYLOAD_SZ + + VIRTUALIZER_STRENGTH_PARAM_SZ; + CHECK_PARAM_LEN(max_params_length, MAX_INBAND_PARAM_SZ, + "VIRT STRENGTH", rc); + if (rc != 0) + break; + param_hdr.param_id = + AUDPROC_PARAM_ID_VIRTUALIZER_STRENGTH; + param_hdr.param_size = VIRTUALIZER_STRENGTH_PARAM_SZ; + param_data = (u8 *) &virtualizer->strength; + break; + case VIRTUALIZER_OUT_TYPE: + if (length != 1 || index_offset != 0) { + pr_err("VIRT OUT_TYPE:invalid params\n"); + rc = -EINVAL; + goto invalid_config; + } + virtualizer->out_type = + GET_NEXT(values, param_max_offset, rc); + pr_debug("%s: VIRT OUT_TYPE val:%d\n", + __func__, virtualizer->out_type); + if (command_config_state != CONFIG_SET) + break; + max_params_length = params_length + + COMMAND_IID_PAYLOAD_SZ + + VIRTUALIZER_OUT_TYPE_PARAM_SZ; + CHECK_PARAM_LEN(max_params_length, MAX_INBAND_PARAM_SZ, + "VIRT OUT_TYPE", rc); + if (rc != 0) + break; + param_hdr.param_id = + AUDPROC_PARAM_ID_VIRTUALIZER_OUT_TYPE; + param_hdr.param_size = VIRTUALIZER_OUT_TYPE_PARAM_SZ; + param_data = (u8 *) &virtualizer->out_type; + break; + case VIRTUALIZER_GAIN_ADJUST: + if (length != 1 || index_offset != 0) { + pr_err("VIRT GAIN_ADJUST: invalid params\n"); + rc = -EINVAL; + goto invalid_config; + } + virtualizer->gain_adjust = + GET_NEXT(values, param_max_offset, rc); + pr_debug("%s: VIRT GAIN_ADJUST val:%d\n", + __func__, virtualizer->gain_adjust); + if (command_config_state != CONFIG_SET) + break; + max_params_length = params_length + + COMMAND_IID_PAYLOAD_SZ + + VIRTUALIZER_GAIN_ADJUST_PARAM_SZ; + CHECK_PARAM_LEN(max_params_length, MAX_INBAND_PARAM_SZ, + "VIRT GAIN_ADJUST", rc); + if (rc != 0) + break; + param_hdr.param_id = + AUDPROC_PARAM_ID_VIRTUALIZER_GAIN_ADJUST; + param_hdr.param_size = VIRTUALIZER_GAIN_ADJUST_PARAM_SZ; + param_data = (u8 *) &virtualizer->gain_adjust; + break; + default: + pr_err("%s: Invalid command to set config\n", __func__); + continue; + } + if (rc) + goto invalid_config; + + rc = q6common_pack_pp_params(updt_params, ¶m_hdr, + param_data, &packed_data_size); + if (rc) { + pr_err("%s: Failed to pack params, error %d\n", + __func__, rc); + goto invalid_config; + } + + updt_params += packed_data_size; + params_length += packed_data_size; + } + if (params_length && (rc == 0)) + q6asm_set_pp_params(ac, NULL, params, params_length); + else + pr_debug("%s: did not send pp params\n", __func__); +invalid_config: + kfree(params); + return rc; +} +EXPORT_SYMBOL(msm_audio_effects_virtualizer_handler); + +/** + * msm_audio_effects_reverb_handler - + * Audio effects handler for reverb + * + * @ac: audio client handle + * @pbe: reverb params + * @values: values to be updated + * + * Return 0 on success or error on failure + */ +int msm_audio_effects_reverb_handler(struct audio_client *ac, + struct reverb_params *reverb, + long *values) +{ + long *param_max_offset = values + MAX_PP_PARAMS_SZ - 1; + char *params = NULL; + u8 *updt_params; + int rc = 0; + int devices = GET_NEXT(values, param_max_offset, rc); + int num_commands = GET_NEXT(values, param_max_offset, rc); + int i, prev_enable_flag; + uint32_t max_params_length = 0; + uint32_t params_length = 0; + struct param_hdr_v3 param_hdr; + u8 *param_data = NULL; + u32 packed_data_size = 0; + + pr_debug("%s\n", __func__); + if (!ac || (devices == -EINVAL) || (num_commands == -EINVAL)) { + pr_err("%s: cannot set audio effects\n", __func__); + return -EINVAL; + } + params = kzalloc(MAX_INBAND_PARAM_SZ, GFP_KERNEL); + if (!params) { + pr_err("%s, params memory alloc failed\n", __func__); + return -ENOMEM; + } + pr_debug("%s: device: %d\n", __func__, devices); + updt_params = (u8 *) params; + /* Set MID and IID once at top and only update param specific fields*/ + memset(¶m_hdr, 0, sizeof(param_hdr)); + param_hdr.module_id = AUDPROC_MODULE_ID_REVERB; + param_hdr.instance_id = INSTANCE_ID_0; + for (i = 0; i < num_commands; i++) { + uint32_t command_id = + GET_NEXT(values, param_max_offset, rc); + uint32_t command_config_state = + GET_NEXT(values, param_max_offset, rc); + uint32_t index_offset = + GET_NEXT(values, param_max_offset, rc); + uint32_t length = + GET_NEXT(values, param_max_offset, rc); + switch (command_id) { + case REVERB_ENABLE: + if (length != 1 || index_offset != 0) { + pr_err("REVERB_ENABLE:invalid params\n"); + rc = -EINVAL; + goto invalid_config; + } + prev_enable_flag = reverb->enable_flag; + reverb->enable_flag = + GET_NEXT(values, param_max_offset, rc); + pr_debug("%s:REVERB_ENABLE prev:%d,new:%d\n", __func__, + prev_enable_flag, reverb->enable_flag); + if (prev_enable_flag == reverb->enable_flag) + break; + max_params_length = params_length + + COMMAND_IID_PAYLOAD_SZ + + REVERB_ENABLE_PARAM_SZ; + CHECK_PARAM_LEN(max_params_length, MAX_INBAND_PARAM_SZ, + "REVERB_ENABLE", rc); + if (rc != 0) + break; + param_hdr.param_id = AUDPROC_PARAM_ID_REVERB_ENABLE; + param_hdr.param_size = REVERB_ENABLE_PARAM_SZ; + param_data = (u8 *) &reverb->enable_flag; + break; + case REVERB_MODE: + if (length != 1 || index_offset != 0) { + pr_err("REVERB_MODE:invalid params\n"); + rc = -EINVAL; + goto invalid_config; + } + reverb->mode = + GET_NEXT(values, param_max_offset, rc); + pr_debug("%s: REVERB_MODE val:%d\n", + __func__, reverb->mode); + if (command_config_state != CONFIG_SET) + break; + max_params_length = params_length + + COMMAND_IID_PAYLOAD_SZ + + REVERB_MODE_PARAM_SZ; + CHECK_PARAM_LEN(max_params_length, MAX_INBAND_PARAM_SZ, + "REVERB_MODE", rc); + if (rc != 0) + break; + param_hdr.param_id = AUDPROC_PARAM_ID_REVERB_MODE; + param_hdr.param_size = REVERB_MODE_PARAM_SZ; + param_data = (u8 *) &reverb->mode; + break; + case REVERB_PRESET: + if (length != 1 || index_offset != 0) { + pr_err("REVERB_PRESET:invalid params\n"); + rc = -EINVAL; + goto invalid_config; + } + reverb->preset = + GET_NEXT(values, param_max_offset, rc); + pr_debug("%s: REVERB_PRESET val:%d\n", + __func__, reverb->preset); + if (command_config_state != CONFIG_SET) + break; + max_params_length = params_length + + COMMAND_IID_PAYLOAD_SZ + + REVERB_PRESET_PARAM_SZ; + CHECK_PARAM_LEN(max_params_length, MAX_INBAND_PARAM_SZ, + "REVERB_PRESET", rc); + if (rc != 0) + break; + param_hdr.param_id = AUDPROC_PARAM_ID_REVERB_PRESET; + param_hdr.param_size = REVERB_PRESET_PARAM_SZ; + param_data = (u8 *) &reverb->preset; + break; + case REVERB_WET_MIX: + if (length != 1 || index_offset != 0) { + pr_err("REVERB_WET_MIX:invalid params\n"); + rc = -EINVAL; + goto invalid_config; + } + reverb->wet_mix = + GET_NEXT(values, param_max_offset, rc); + pr_debug("%s: REVERB_WET_MIX val:%d\n", + __func__, reverb->wet_mix); + if (command_config_state != CONFIG_SET) + break; + max_params_length = params_length + + COMMAND_IID_PAYLOAD_SZ + + REVERB_WET_MIX_PARAM_SZ; + CHECK_PARAM_LEN(max_params_length, MAX_INBAND_PARAM_SZ, + "REVERB_WET_MIX", rc); + if (rc != 0) + break; + param_hdr.param_id = AUDPROC_PARAM_ID_REVERB_WET_MIX; + param_hdr.param_size = REVERB_WET_MIX_PARAM_SZ; + param_data = (u8 *) &reverb->wet_mix; + break; + case REVERB_GAIN_ADJUST: + if (length != 1 || index_offset != 0) { + pr_err("REVERB_GAIN_ADJUST:invalid params\n"); + rc = -EINVAL; + goto invalid_config; + } + reverb->gain_adjust = + GET_NEXT(values, param_max_offset, rc); + pr_debug("%s: REVERB_GAIN_ADJUST val:%d\n", + __func__, reverb->gain_adjust); + if (command_config_state != CONFIG_SET) + break; + max_params_length = params_length + + COMMAND_IID_PAYLOAD_SZ + + REVERB_GAIN_ADJUST_PARAM_SZ; + CHECK_PARAM_LEN(max_params_length, MAX_INBAND_PARAM_SZ, + "REVERB_GAIN_ADJUST", rc); + if (rc != 0) + break; + param_hdr.param_id = + AUDPROC_PARAM_ID_REVERB_GAIN_ADJUST; + param_hdr.param_size = REVERB_GAIN_ADJUST_PARAM_SZ; + param_data = (u8 *) &reverb->gain_adjust; + break; + case REVERB_ROOM_LEVEL: + if (length != 1 || index_offset != 0) { + pr_err("REVERB_ROOM_LEVEL:invalid params\n"); + rc = -EINVAL; + goto invalid_config; + } + reverb->room_level = + GET_NEXT(values, param_max_offset, rc); + pr_debug("%s: REVERB_ROOM_LEVEL val:%d\n", + __func__, reverb->room_level); + if (command_config_state != CONFIG_SET) + break; + max_params_length = params_length + + COMMAND_IID_PAYLOAD_SZ + + REVERB_ROOM_LEVEL_PARAM_SZ; + CHECK_PARAM_LEN(max_params_length, MAX_INBAND_PARAM_SZ, + "REVERB_ROOM_LEVEL", rc); + if (rc != 0) + break; + param_hdr.param_id = AUDPROC_PARAM_ID_REVERB_ROOM_LEVEL; + param_hdr.param_size = REVERB_ROOM_LEVEL_PARAM_SZ; + param_data = (u8 *) &reverb->room_level; + break; + case REVERB_ROOM_HF_LEVEL: + if (length != 1 || index_offset != 0) { + pr_err("REVERB_ROOM_HF_LEVEL:invalid params\n"); + rc = -EINVAL; + goto invalid_config; + } + reverb->room_hf_level = + GET_NEXT(values, param_max_offset, rc); + pr_debug("%s: REVERB_ROOM_HF_LEVEL val%d\n", + __func__, reverb->room_hf_level); + if (command_config_state != CONFIG_SET) + break; + max_params_length = params_length + + COMMAND_IID_PAYLOAD_SZ + + REVERB_ROOM_HF_LEVEL_PARAM_SZ; + CHECK_PARAM_LEN(max_params_length, MAX_INBAND_PARAM_SZ, + "REVERB_ROOM_HF_LEVEL", rc); + if (rc != 0) + break; + param_hdr.param_id = + AUDPROC_PARAM_ID_REVERB_ROOM_HF_LEVEL; + param_hdr.param_size = REVERB_ROOM_HF_LEVEL_PARAM_SZ; + param_data = (u8 *) &reverb->room_hf_level; + break; + case REVERB_DECAY_TIME: + if (length != 1 || index_offset != 0) { + pr_err("REVERB_DECAY_TIME:invalid params\n"); + rc = -EINVAL; + goto invalid_config; + } + reverb->decay_time = + GET_NEXT(values, param_max_offset, rc); + pr_debug("%s: REVERB_DECAY_TIME val:%d\n", + __func__, reverb->decay_time); + if (command_config_state != CONFIG_SET) + break; + max_params_length = params_length + + COMMAND_IID_PAYLOAD_SZ + + REVERB_DECAY_TIME_PARAM_SZ; + CHECK_PARAM_LEN(max_params_length, MAX_INBAND_PARAM_SZ, + "REVERB_DECAY_TIME", rc); + if (rc != 0) + break; + param_hdr.param_id = AUDPROC_PARAM_ID_REVERB_DECAY_TIME; + param_hdr.param_size = REVERB_DECAY_TIME_PARAM_SZ; + param_data = (u8 *) &reverb->decay_time; + break; + case REVERB_DECAY_HF_RATIO: + if (length != 1 || index_offset != 0) { + pr_err("REVERB_DECAY_HF_RATIOinvalid params\n"); + rc = -EINVAL; + goto invalid_config; + } + reverb->decay_hf_ratio = + GET_NEXT(values, param_max_offset, rc); + pr_debug("%s: REVERB_DECAY_HF_RATIO val%d\n", + __func__, reverb->decay_hf_ratio); + if (command_config_state != CONFIG_SET) + break; + max_params_length = params_length + + COMMAND_IID_PAYLOAD_SZ + + REVERB_DECAY_HF_RATIO_PARAM_SZ; + CHECK_PARAM_LEN(max_params_length, MAX_INBAND_PARAM_SZ, + "REVERB_DECAY_HF_RATIO", rc); + if (rc != 0) + break; + param_hdr.param_id = + AUDPROC_PARAM_ID_REVERB_DECAY_HF_RATIO; + param_hdr.param_size = REVERB_DECAY_HF_RATIO_PARAM_SZ; + param_data = (u8 *) &reverb->decay_hf_ratio; + break; + case REVERB_REFLECTIONS_LEVEL: + if (length != 1 || index_offset != 0) { + pr_err("REVERB_REFLECTION_LVLinvalid params\n"); + rc = -EINVAL; + goto invalid_config; + } + reverb->reflections_level = + GET_NEXT(values, param_max_offset, rc); + pr_debug("%s: REVERB_REFLECTIONS_LEVEL val:%d\n", + __func__, reverb->reflections_level); + if (command_config_state != CONFIG_SET) + break; + max_params_length = params_length + + COMMAND_IID_PAYLOAD_SZ + + REVERB_REFLECTIONS_LEVEL_PARAM_SZ; + CHECK_PARAM_LEN(max_params_length, MAX_INBAND_PARAM_SZ, + "REVERB_REFLECTIONS_LEVEL", rc); + if (rc != 0) + break; + param_hdr.param_id = + AUDPROC_PARAM_ID_REVERB_REFLECTIONS_LEVEL; + param_hdr.param_size = + REVERB_REFLECTIONS_LEVEL_PARAM_SZ; + param_data = (u8 *) &reverb->reflections_level; + break; + case REVERB_REFLECTIONS_DELAY: + if (length != 1 || index_offset != 0) { + pr_err("REVERB_REFLECTION_DLYinvalid params\n"); + rc = -EINVAL; + goto invalid_config; + } + reverb->reflections_delay = + GET_NEXT(values, param_max_offset, rc); + pr_debug("%s: REVERB_REFLECTIONS_DELAY val:%d\n", + __func__, reverb->reflections_delay); + if (command_config_state != CONFIG_SET) + break; + max_params_length = params_length + + COMMAND_IID_PAYLOAD_SZ + + REVERB_REFLECTIONS_DELAY_PARAM_SZ; + CHECK_PARAM_LEN(max_params_length, MAX_INBAND_PARAM_SZ, + "REVERB_REFLECTIONS_DELAY", rc); + if (rc != 0) + break; + param_hdr.param_id = + AUDPROC_PARAM_ID_REVERB_REFLECTIONS_DELAY; + param_hdr.param_size = + REVERB_REFLECTIONS_DELAY_PARAM_SZ; + param_data = (u8 *) &reverb->reflections_delay; + break; + case REVERB_LEVEL: + if (length != 1 || index_offset != 0) { + pr_err("REVERB_LEVEL:invalid params\n"); + rc = -EINVAL; + goto invalid_config; + } + reverb->level = + GET_NEXT(values, param_max_offset, rc); + pr_debug("%s: REVERB_LEVEL val:%d\n", + __func__, reverb->level); + if (command_config_state != CONFIG_SET) + break; + max_params_length = params_length + + COMMAND_IID_PAYLOAD_SZ + + REVERB_LEVEL_PARAM_SZ; + CHECK_PARAM_LEN(max_params_length, MAX_INBAND_PARAM_SZ, + "REVERB_LEVEL", rc); + if (rc != 0) + break; + param_hdr.param_id = AUDPROC_PARAM_ID_REVERB_LEVEL; + param_hdr.param_size = REVERB_LEVEL_PARAM_SZ; + param_data = (u8 *) &reverb->level; + break; + case REVERB_DELAY: + if (length != 1 || index_offset != 0) { + pr_err("REVERB_DELAY:invalid params\n"); + rc = -EINVAL; + goto invalid_config; + } + reverb->delay = + GET_NEXT(values, param_max_offset, rc); + pr_debug("%s:REVERB_DELAY val:%d\n", + __func__, reverb->delay); + if (command_config_state != CONFIG_SET) + break; + max_params_length = params_length + + COMMAND_IID_PAYLOAD_SZ + + REVERB_DELAY_PARAM_SZ; + CHECK_PARAM_LEN(max_params_length, MAX_INBAND_PARAM_SZ, + "REVERB_DELAY", rc); + if (rc != 0) + break; + param_hdr.param_id = AUDPROC_PARAM_ID_REVERB_DELAY; + param_hdr.param_size = REVERB_DELAY_PARAM_SZ; + param_data = (u8 *) &reverb->delay; + break; + case REVERB_DIFFUSION: + if (length != 1 || index_offset != 0) { + pr_err("REVERB_DIFFUSION:invalid params\n"); + rc = -EINVAL; + goto invalid_config; + } + reverb->diffusion = + GET_NEXT(values, param_max_offset, rc); + pr_debug("%s: REVERB_DIFFUSION val:%d\n", + __func__, reverb->diffusion); + if (command_config_state != CONFIG_SET) + break; + max_params_length = params_length + + COMMAND_IID_PAYLOAD_SZ + + REVERB_DIFFUSION_PARAM_SZ; + CHECK_PARAM_LEN(max_params_length, MAX_INBAND_PARAM_SZ, + "REVERB_DIFFUSION", rc); + if (rc != 0) + break; + param_hdr.param_id = AUDPROC_PARAM_ID_REVERB_DIFFUSION; + param_hdr.param_size = REVERB_DIFFUSION_PARAM_SZ; + param_data = (u8 *) &reverb->diffusion; + break; + case REVERB_DENSITY: + if (length != 1 || index_offset != 0) { + pr_err("REVERB_DENSITY:invalid params\n"); + rc = -EINVAL; + goto invalid_config; + } + reverb->density = + GET_NEXT(values, param_max_offset, rc); + pr_debug("%s: REVERB_DENSITY val:%d\n", + __func__, reverb->density); + if (command_config_state != CONFIG_SET) + break; + max_params_length = params_length + + COMMAND_IID_PAYLOAD_SZ + + REVERB_DENSITY_PARAM_SZ; + CHECK_PARAM_LEN(max_params_length, MAX_INBAND_PARAM_SZ, + "REVERB_DENSITY", rc); + if (rc != 0) + break; + param_hdr.param_id = AUDPROC_PARAM_ID_REVERB_DENSITY; + param_hdr.param_size = REVERB_DENSITY_PARAM_SZ; + param_data = (u8 *) &reverb->density; + break; + default: + pr_err("%s: Invalid command to set config\n", __func__); + continue; + } + if (rc) + goto invalid_config; + + rc = q6common_pack_pp_params(updt_params, ¶m_hdr, + param_data, &packed_data_size); + if (rc) { + pr_err("%s: Failed to pack params, error %d\n", + __func__, rc); + goto invalid_config; + } + + updt_params += packed_data_size; + params_length += packed_data_size; + } + if (params_length && (rc == 0)) + q6asm_set_pp_params(ac, NULL, params, params_length); + else + pr_debug("%s: did not send pp params\n", __func__); +invalid_config: + kfree(params); + return rc; +} +EXPORT_SYMBOL(msm_audio_effects_reverb_handler); + +/** + * msm_audio_effects_bass_boost_handler - + * Audio effects handler for bass_boost + * + * @ac: audio client handle + * @bass_boost: bass_boost params + * @values: values to be updated + * + * Return 0 on success or error on failure + */ +int msm_audio_effects_bass_boost_handler(struct audio_client *ac, + struct bass_boost_params *bass_boost, + long *values) +{ + long *param_max_offset = values + MAX_PP_PARAMS_SZ - 1; + char *params = NULL; + u8 *updt_params; + int rc = 0; + int devices = GET_NEXT(values, param_max_offset, rc); + int num_commands = GET_NEXT(values, param_max_offset, rc); + int i, prev_enable_flag; + uint32_t max_params_length = 0; + uint32_t params_length = 0; + struct param_hdr_v3 param_hdr; + u8 *param_data = NULL; + u32 packed_data_size = 0; + + pr_debug("%s\n", __func__); + if (!ac || (devices == -EINVAL) || (num_commands == -EINVAL)) { + pr_err("%s: cannot set audio effects\n", __func__); + return -EINVAL; + } + params = kzalloc(MAX_INBAND_PARAM_SZ, GFP_KERNEL); + if (!params) { + pr_err("%s, params memory alloc failed\n", __func__); + return -ENOMEM; + } + pr_debug("%s: device: %d\n", __func__, devices); + updt_params = (u8 *) params; + /* Set MID and IID once at top and only update param specific fields*/ + memset(¶m_hdr, 0, sizeof(param_hdr)); + param_hdr.module_id = AUDPROC_MODULE_ID_BASS_BOOST; + param_hdr.instance_id = INSTANCE_ID_0; + for (i = 0; i < num_commands; i++) { + uint32_t command_id = + GET_NEXT(values, param_max_offset, rc); + uint32_t command_config_state = + GET_NEXT(values, param_max_offset, rc); + uint32_t index_offset = + GET_NEXT(values, param_max_offset, rc); + uint32_t length = + GET_NEXT(values, param_max_offset, rc); + switch (command_id) { + case BASS_BOOST_ENABLE: + if (length != 1 || index_offset != 0) { + pr_err("BASS_BOOST_ENABLE:invalid params\n"); + rc = -EINVAL; + goto invalid_config; + } + prev_enable_flag = bass_boost->enable_flag; + bass_boost->enable_flag = + GET_NEXT(values, param_max_offset, rc); + pr_debug("%s: BASS_BOOST_ENABLE prev:%d new:%d\n", + __func__, prev_enable_flag, + bass_boost->enable_flag); + if (prev_enable_flag == bass_boost->enable_flag) + break; + max_params_length = params_length + + COMMAND_IID_PAYLOAD_SZ + + BASS_BOOST_ENABLE_PARAM_SZ; + CHECK_PARAM_LEN(max_params_length, MAX_INBAND_PARAM_SZ, + "BASS_BOOST_ENABLE", rc); + if (rc != 0) + break; + param_hdr.param_id = AUDPROC_PARAM_ID_BASS_BOOST_ENABLE; + param_hdr.param_size = BASS_BOOST_ENABLE_PARAM_SZ; + param_data = (u8 *) &bass_boost->enable_flag; + break; + case BASS_BOOST_MODE: + if (length != 1 || index_offset != 0) { + pr_err("BASS_BOOST_MODE:invalid params\n"); + rc = -EINVAL; + goto invalid_config; + } + bass_boost->mode = + GET_NEXT(values, param_max_offset, rc); + pr_debug("%s: BASS_BOOST_MODE val:%d\n", + __func__, bass_boost->mode); + if (command_config_state != CONFIG_SET) + break; + max_params_length = params_length + + COMMAND_IID_PAYLOAD_SZ + + BASS_BOOST_MODE_PARAM_SZ; + CHECK_PARAM_LEN(max_params_length, MAX_INBAND_PARAM_SZ, + "BASS_BOOST_MODE", rc); + if (rc != 0) + break; + param_hdr.param_id = AUDPROC_PARAM_ID_BASS_BOOST_MODE; + param_hdr.param_size = BASS_BOOST_MODE_PARAM_SZ; + param_data = (u8 *) &bass_boost->mode; + break; + case BASS_BOOST_STRENGTH: + if (length != 1 || index_offset != 0) { + pr_err("BASS_BOOST_STRENGTH:invalid params\n"); + rc = -EINVAL; + goto invalid_config; + } + bass_boost->strength = + GET_NEXT(values, param_max_offset, rc); + pr_debug("%s: BASS_BOOST_STRENGTH val:%d\n", + __func__, bass_boost->strength); + if (command_config_state != CONFIG_SET) + break; + max_params_length = params_length + + COMMAND_IID_PAYLOAD_SZ + + BASS_BOOST_STRENGTH_PARAM_SZ; + CHECK_PARAM_LEN(max_params_length, MAX_INBAND_PARAM_SZ, + "BASS_BOOST_STRENGTH", rc); + if (rc != 0) + break; + param_hdr.param_id = + AUDPROC_PARAM_ID_BASS_BOOST_STRENGTH; + param_hdr.param_size = BASS_BOOST_STRENGTH_PARAM_SZ; + param_data = (u8 *) &bass_boost->strength; + break; + default: + pr_err("%s: Invalid command to set config\n", __func__); + continue; + } + if (rc) + goto invalid_config; + + rc = q6common_pack_pp_params(updt_params, ¶m_hdr, + param_data, &packed_data_size); + if (rc) { + pr_err("%s: Failed to pack params, error %d\n", + __func__, rc); + goto invalid_config; + } + + updt_params += packed_data_size; + params_length += packed_data_size; + } + if (params_length && (rc == 0)) + q6asm_set_pp_params(ac, NULL, params, params_length); + else + pr_debug("%s: did not send pp params\n", __func__); +invalid_config: + kfree(params); + return rc; +} +EXPORT_SYMBOL(msm_audio_effects_bass_boost_handler); + +/** + * msm_audio_effects_pbe_handler - + * Audio effects handler for pbe + * + * @ac: audio client handle + * @pbe: pbe params + * @values: values to be updated + * + * Return 0 on success or error on failure + */ +int msm_audio_effects_pbe_handler(struct audio_client *ac, + struct pbe_params *pbe, + long *values) +{ + long *param_max_offset = values + MAX_PP_PARAMS_SZ - 1; + char *params = NULL; + u8 *updt_params; + int rc = 0; + int devices = GET_NEXT(values, param_max_offset, rc); + int num_commands = GET_NEXT(values, param_max_offset, rc); + int i, prev_enable_flag; + uint32_t max_params_length = 0; + uint32_t params_length = 0; + struct param_hdr_v3 param_hdr; + u8 *param_data = NULL; + u32 packed_data_size = 0; + + pr_debug("%s\n", __func__); + if (!ac || (devices == -EINVAL) || (num_commands == -EINVAL)) { + pr_err("%s: cannot set audio effects\n", __func__); + return -EINVAL; + } + params = kzalloc(MAX_INBAND_PARAM_SZ, GFP_KERNEL); + if (!params) { + pr_err("%s, params memory alloc failed\n", __func__); + return -ENOMEM; + } + pr_debug("%s: device: %d\n", __func__, devices); + updt_params = (u8 *) params; + /* Set MID and IID once at top and only update param specific fields*/ + memset(¶m_hdr, 0, sizeof(param_hdr)); + param_hdr.module_id = AUDPROC_MODULE_ID_PBE; + param_hdr.instance_id = INSTANCE_ID_0; + for (i = 0; i < num_commands; i++) { + uint32_t command_id = + GET_NEXT(values, param_max_offset, rc); + uint32_t command_config_state = + GET_NEXT(values, param_max_offset, rc); + uint32_t index_offset = + GET_NEXT(values, param_max_offset, rc); + uint32_t length = + GET_NEXT(values, param_max_offset, rc); + switch (command_id) { + case PBE_ENABLE: + pr_debug("%s: PBE_ENABLE\n", __func__); + if (length != 1 || index_offset != 0) { + pr_err("no valid params\n"); + rc = -EINVAL; + goto invalid_config; + } + prev_enable_flag = pbe->enable_flag; + pbe->enable_flag = + GET_NEXT(values, param_max_offset, rc); + if (prev_enable_flag == pbe->enable_flag) + break; + max_params_length = params_length + + COMMAND_IID_PAYLOAD_SZ + + PBE_ENABLE_PARAM_SZ; + CHECK_PARAM_LEN(max_params_length, MAX_INBAND_PARAM_SZ, + "PBE_ENABLE", rc); + if (rc != 0) + break; + param_hdr.param_id = AUDPROC_PARAM_ID_PBE_ENABLE; + param_hdr.param_size = PBE_ENABLE_PARAM_SZ; + param_data = (u8 *) &pbe->enable_flag; + break; + case PBE_CONFIG: + pr_debug("%s: PBE_PARAM length %u\n", __func__, length); + if (length > sizeof(struct pbe_config_t) || + length < PBE_CONFIG_PARAM_LEN || + index_offset != 0) { + pr_err("no valid params, len %d\n", length); + rc = -EINVAL; + goto invalid_config; + } + if (command_config_state != CONFIG_SET) + break; + max_params_length = + params_length + COMMAND_IID_PAYLOAD_SZ + length; + CHECK_PARAM_LEN(max_params_length, MAX_INBAND_PARAM_SZ, + "PBE_PARAM", rc); + if (rc != 0) + break; + param_hdr.param_id = AUDPROC_PARAM_ID_PBE_PARAM_CONFIG; + param_hdr.param_size = length; + param_data = (u8 *) values; + break; + default: + pr_err("%s: Invalid command to set config\n", __func__); + continue; + } + if (rc) + goto invalid_config; + + rc = q6common_pack_pp_params(updt_params, ¶m_hdr, + param_data, &packed_data_size); + if (rc) { + pr_err("%s: Failed to pack params, error %d\n", + __func__, rc); + goto invalid_config; + } + + updt_params += packed_data_size; + params_length += packed_data_size; + } + if (params_length && (rc == 0)) + q6asm_set_pp_params(ac, NULL, params, params_length); +invalid_config: + kfree(params); + return rc; +} +EXPORT_SYMBOL(msm_audio_effects_pbe_handler); + +/** + * msm_audio_effects_popless_eq_handler - + * Audio effects handler for popless equalizer + * + * @ac: audio client handle + * @eq: equalizer params + * @values: values to be updated + * + * Return 0 on success or error on failure + */ +int msm_audio_effects_popless_eq_handler(struct audio_client *ac, + struct eq_params *eq, + long *values) +{ + long *param_max_offset = values + MAX_PP_PARAMS_SZ - 1; + char *params = NULL; + u8 *updt_params = NULL; + int rc = 0; + int devices = GET_NEXT(values, param_max_offset, rc); + int num_commands = GET_NEXT(values, param_max_offset, rc); + int i, prev_enable_flag; + uint32_t max_params_length = 0; + uint32_t params_length = 0; + struct param_hdr_v3 param_hdr; + u8 *param_data = NULL; + u32 packed_data_size = 0; + u8 *eq_config_data = NULL; + u32 *updt_config_data = NULL; + int config_param_length; + + pr_debug("%s\n", __func__); + if (!ac || (devices == -EINVAL) || (num_commands == -EINVAL)) { + pr_err("%s: cannot set audio effects\n", __func__); + return -EINVAL; + } + params = kzalloc(MAX_INBAND_PARAM_SZ, GFP_KERNEL); + if (!params) { + pr_err("%s, params memory alloc failed\n", __func__); + return -ENOMEM; + } + pr_debug("%s: device: %d\n", __func__, devices); + updt_params = (u8 *) params; + /* Set MID and IID once at top and only update param specific fields*/ + memset(¶m_hdr, 0, sizeof(param_hdr)); + param_hdr.module_id = AUDPROC_MODULE_ID_POPLESS_EQUALIZER; + param_hdr.instance_id = INSTANCE_ID_0; + for (i = 0; i < num_commands; i++) { + uint32_t command_id = + GET_NEXT(values, param_max_offset, rc); + uint32_t command_config_state = + GET_NEXT(values, param_max_offset, rc); + uint32_t index_offset = + GET_NEXT(values, param_max_offset, rc); + uint32_t length = + GET_NEXT(values, param_max_offset, rc); + uint32_t idx; + int j; + + switch (command_id) { + case EQ_ENABLE: + if (length != 1 || index_offset != 0) { + pr_err("EQ_ENABLE:invalid params\n"); + rc = -EINVAL; + goto invalid_config; + } + prev_enable_flag = eq->enable_flag; + eq->enable_flag = + GET_NEXT(values, param_max_offset, rc); + pr_debug("%s: EQ_ENABLE prev:%d new:%d\n", __func__, + prev_enable_flag, eq->enable_flag); + if (prev_enable_flag == eq->enable_flag) + break; + max_params_length = params_length + + COMMAND_IID_PAYLOAD_SZ + + EQ_ENABLE_PARAM_SZ; + CHECK_PARAM_LEN(max_params_length, MAX_INBAND_PARAM_SZ, + "EQ_ENABLE", rc); + if (rc != 0) + break; + param_hdr.param_id = AUDPROC_PARAM_ID_EQ_ENABLE; + param_hdr.param_size = EQ_ENABLE_PARAM_SZ; + param_data = (u8 *) &eq->enable_flag; + break; + case EQ_CONFIG: + if (length < EQ_CONFIG_PARAM_LEN || index_offset != 0) { + pr_err("EQ_CONFIG:invalid params\n"); + rc = -EINVAL; + goto invalid_config; + } + pr_debug("%s: EQ_CONFIG bands:%d, pgain:%d, pset:%d\n", + __func__, eq->config.num_bands, + eq->config.eq_pregain, eq->config.preset_id); + for (idx = 0; idx < MAX_EQ_BANDS; idx++) + eq->per_band_cfg[idx].band_idx = -1; + eq->config.eq_pregain = + GET_NEXT(values, param_max_offset, rc); + eq->config.preset_id = + GET_NEXT(values, param_max_offset, rc); + eq->config.num_bands = + GET_NEXT(values, param_max_offset, rc); + if (eq->config.num_bands > MAX_EQ_BANDS) { + pr_err("EQ_CONFIG:invalid num of bands\n"); + rc = -EINVAL; + goto invalid_config; + } + if (eq->config.num_bands && + (((length - EQ_CONFIG_PARAM_LEN)/ + EQ_CONFIG_PER_BAND_PARAM_LEN) + != eq->config.num_bands)) { + pr_err("EQ_CONFIG:invalid length per band\n"); + rc = -EINVAL; + goto invalid_config; + } + for (j = 0; j < eq->config.num_bands; j++) { + idx = GET_NEXT(values, param_max_offset, rc); + if (idx >= MAX_EQ_BANDS) { + pr_err("EQ_CONFIG:invalid band index\n"); + rc = -EINVAL; + goto invalid_config; + } + eq->per_band_cfg[idx].band_idx = idx; + eq->per_band_cfg[idx].filter_type = + GET_NEXT(values, param_max_offset, rc); + eq->per_band_cfg[idx].freq_millihertz = + GET_NEXT(values, param_max_offset, rc); + eq->per_band_cfg[idx].gain_millibels = + GET_NEXT(values, param_max_offset, rc); + eq->per_band_cfg[idx].quality_factor = + GET_NEXT(values, param_max_offset, rc); + } + if (command_config_state != CONFIG_SET) + break; + config_param_length = EQ_CONFIG_PARAM_SZ + + (EQ_CONFIG_PER_BAND_PARAM_SZ * + eq->config.num_bands); + max_params_length = params_length + + COMMAND_IID_PAYLOAD_SZ + + config_param_length; + CHECK_PARAM_LEN(max_params_length, MAX_INBAND_PARAM_SZ, + "EQ_CONFIG", rc); + if (rc != 0) + break; + param_hdr.param_id = AUDPROC_PARAM_ID_EQ_CONFIG; + param_hdr.param_size = config_param_length; + + if (!eq_config_data) + eq_config_data = kzalloc(config_param_length, + GFP_KERNEL); + else + memset(eq_config_data, 0, config_param_length); + if (!eq_config_data) { + pr_err("%s, EQ_CONFIG:memory alloc failed\n", + __func__); + rc = -ENOMEM; + goto invalid_config; + } + param_data = eq_config_data; + updt_config_data = (u32 *) eq_config_data; + *updt_config_data++ = eq->config.eq_pregain; + *updt_config_data++ = eq->config.preset_id; + *updt_config_data++ = eq->config.num_bands; + for (idx = 0; idx < MAX_EQ_BANDS; idx++) { + if (eq->per_band_cfg[idx].band_idx < 0) + continue; + *updt_config_data++ = + eq->per_band_cfg[idx].filter_type; + *updt_config_data++ = + eq->per_band_cfg[idx].freq_millihertz; + *updt_config_data++ = + eq->per_band_cfg[idx].gain_millibels; + *updt_config_data++ = + eq->per_band_cfg[idx].quality_factor; + *updt_config_data++ = + eq->per_band_cfg[idx].band_idx; + } + break; + case EQ_BAND_INDEX: + if (length != 1 || index_offset != 0) { + pr_err("EQ_BAND_INDEX:invalid params\n"); + rc = -EINVAL; + goto invalid_config; + } + idx = GET_NEXT(values, param_max_offset, rc); + if (idx > MAX_EQ_BANDS) { + pr_err("EQ_BAND_INDEX:invalid band index\n"); + rc = -EINVAL; + goto invalid_config; + } + eq->band_index = idx; + pr_debug("%s: EQ_BAND_INDEX val:%d\n", + __func__, eq->band_index); + if (command_config_state != CONFIG_SET) + break; + max_params_length = params_length + + COMMAND_IID_PAYLOAD_SZ + + EQ_BAND_INDEX_PARAM_SZ; + CHECK_PARAM_LEN(max_params_length, MAX_INBAND_PARAM_SZ, + "EQ_BAND_INDEX", rc); + if (rc != 0) + break; + param_hdr.param_id = AUDPROC_PARAM_ID_EQ_BAND_INDEX; + param_hdr.param_size = EQ_BAND_INDEX_PARAM_SZ; + param_data = (u8 *) &eq->band_index; + break; + case EQ_SINGLE_BAND_FREQ: + if (length != 1 || index_offset != 0) { + pr_err("EQ_SINGLE_BAND_FREQ:invalid params\n"); + rc = -EINVAL; + goto invalid_config; + } + if (eq->band_index > MAX_EQ_BANDS) { + pr_err("EQ_SINGLE_BAND_FREQ:invalid index\n"); + break; + } + eq->freq_millihertz = + GET_NEXT(values, param_max_offset, rc); + pr_debug("%s: EQ_SINGLE_BAND_FREQ idx:%d, val:%d\n", + __func__, eq->band_index, eq->freq_millihertz); + if (command_config_state != CONFIG_SET) + break; + max_params_length = params_length + + COMMAND_IID_PAYLOAD_SZ + + EQ_SINGLE_BAND_FREQ_PARAM_SZ; + CHECK_PARAM_LEN(max_params_length, MAX_INBAND_PARAM_SZ, + "EQ_SINGLE_BAND_FREQ", rc); + if (rc != 0) + break; + param_hdr.param_id = + AUDPROC_PARAM_ID_EQ_SINGLE_BAND_FREQ; + param_hdr.param_size = EQ_SINGLE_BAND_FREQ_PARAM_SZ; + param_data = (u8 *) &eq->freq_millihertz; + break; + default: + pr_err("%s: Invalid command to set config\n", __func__); + continue; + } + if (rc) + goto invalid_config; + + rc = q6common_pack_pp_params(updt_params, ¶m_hdr, + param_data, &packed_data_size); + if (rc) { + pr_err("%s: Failed to pack params, error %d\n", + __func__, rc); + goto invalid_config; + } + + updt_params += packed_data_size; + params_length += packed_data_size; + } + if (params_length && (rc == 0)) + q6asm_set_pp_params(ac, NULL, params, params_length); + else + pr_debug("%s: did not send pp params\n", __func__); +invalid_config: + kfree(params); + kfree(eq_config_data); + return rc; +} +EXPORT_SYMBOL(msm_audio_effects_popless_eq_handler); + +static int __msm_audio_effects_volume_handler(struct audio_client *ac, + struct soft_volume_params *vol, + long *values, + int instance) +{ + int devices; + int num_commands; + char *params = NULL; + u8 *updt_params; + int i; + uint32_t vol_gain_2ch = 0; + uint32_t max_params_length = 0; + uint32_t params_length = 0; + struct param_hdr_v3 param_hdr; + u32 packed_data_size = 0; + long *param_max_offset; + int rc = 0; + + pr_debug("%s: instance: %d\n", __func__, instance); + if (!values) { + pr_err("%s: set audio effects failed, no valid data\n", + __func__); + return -EINVAL; + } + param_max_offset = values + MAX_PP_PARAMS_SZ - 1; + devices = GET_NEXT(values, param_max_offset, rc); + num_commands = GET_NEXT(values, param_max_offset, rc); + if (!ac || (devices == -EINVAL) || (num_commands == -EINVAL)) { + pr_err("%s: cannot set audio effects\n", __func__); + return -EINVAL; + } + params = kzalloc(MAX_INBAND_PARAM_SZ, GFP_KERNEL); + if (!params) { + pr_err("%s, params memory alloc failed\n", __func__); + return -ENOMEM; + } + updt_params = (u8 *) params; + /* Set MID and IID once at top and only update param specific fields*/ + memset(¶m_hdr, 0, sizeof(param_hdr)); + q6asm_set_soft_volume_module_instance_ids(instance, ¶m_hdr); + for (i = 0; i < num_commands; i++) { + uint32_t command_id = + GET_NEXT(values, param_max_offset, rc); + uint32_t command_config_state = + GET_NEXT(values, param_max_offset, rc); + uint32_t index_offset = + GET_NEXT(values, param_max_offset, rc); + uint32_t length = + GET_NEXT(values, param_max_offset, rc); + switch (command_id) { + case SOFT_VOLUME_GAIN_2CH: + case SOFT_VOLUME2_GAIN_2CH: + if (length != 2 || index_offset != 0) { + pr_err("VOLUME_GAIN_2CH: invalid params\n"); + rc = -EINVAL; + goto invalid_config; + } + vol->left_gain = GET_NEXT(values, param_max_offset, rc); + vol->right_gain = + GET_NEXT(values, param_max_offset, rc); + vol->master_gain = 0x2000; + if (command_config_state != CONFIG_SET) + break; + max_params_length = params_length + + COMMAND_IID_PAYLOAD_SZ + + SOFT_VOLUME_GAIN_2CH_PARAM_SZ + + COMMAND_IID_PAYLOAD_SZ + + SOFT_VOLUME_GAIN_MASTER_PARAM_SZ; + CHECK_PARAM_LEN(max_params_length, MAX_INBAND_PARAM_SZ, + "VOLUME/VOLUME2_GAIN_2CH", rc); + break; + case SOFT_VOLUME_GAIN_MASTER: + case SOFT_VOLUME2_GAIN_MASTER: + if (length != 1 || index_offset != 0) { + pr_err("VOLUME_GAIN_MASTER: invalid params\n"); + rc = -EINVAL; + goto invalid_config; + } + vol->left_gain = 0x2000; + vol->right_gain = 0x2000; + vol->master_gain = + GET_NEXT(values, param_max_offset, rc); + if (command_config_state != CONFIG_SET) + break; + max_params_length = params_length + + COMMAND_IID_PAYLOAD_SZ + + SOFT_VOLUME_GAIN_2CH_PARAM_SZ + + COMMAND_IID_PAYLOAD_SZ + + SOFT_VOLUME_GAIN_MASTER_PARAM_SZ; + CHECK_PARAM_LEN(max_params_length, MAX_INBAND_PARAM_SZ, + "VOLUME/VOLUME2_GAIN_MASTER", rc); + break; + default: + pr_err("%s: Invalid command id: %d to set config\n", + __func__, command_id); + continue; + } + if (rc) + continue; + + /* Set Volume Control for Left/Right */ + param_hdr.param_id = ASM_PARAM_ID_VOL_CTRL_LR_CHANNEL_GAIN; + param_hdr.param_size = SOFT_VOLUME_GAIN_2CH_PARAM_SZ; + vol_gain_2ch = (vol->left_gain << 16) | vol->right_gain; + rc = q6common_pack_pp_params(updt_params, ¶m_hdr, + (u8 *) &vol_gain_2ch, + &packed_data_size); + if (rc) { + pr_err("%s: Failed to pack params, error %d\n", + __func__, rc); + goto invalid_config; + } + + updt_params += packed_data_size; + params_length += packed_data_size; + + /* Set Master Volume Control */ + param_hdr.param_id = ASM_PARAM_ID_VOL_CTRL_MASTER_GAIN; + param_hdr.param_size = SOFT_VOLUME_GAIN_MASTER_PARAM_SZ; + rc = q6common_pack_pp_params(updt_params, ¶m_hdr, + (u8 *) &vol->master_gain, + &packed_data_size); + if (rc) { + pr_err("%s: Failed to pack params, error %d\n", + __func__, rc); + goto invalid_config; + } + + updt_params += packed_data_size; + params_length += packed_data_size; + } + if (params_length && (rc == 0)) + q6asm_set_pp_params(ac, NULL, params, params_length); +invalid_config: + kfree(params); + return rc; +} + +int msm_audio_effects_volume_handler(struct audio_client *ac, + struct soft_volume_params *vol, + long *values) +{ + return __msm_audio_effects_volume_handler(ac, vol, values, + SOFT_VOLUME_INSTANCE_1); +} + +/** + * msm_audio_effects_volume_handler_v2 - + * Audio effects handler for volume + * + * @ac: audio client handle + * @vol: volume params + * @values: values to be updated + * @instance: instance to update + * + * Return 0 on success or error on failure + */ +int msm_audio_effects_volume_handler_v2(struct audio_client *ac, + struct soft_volume_params *vol, + long *values, int instance) +{ + return __msm_audio_effects_volume_handler(ac, vol, values, instance); +} +EXPORT_SYMBOL(msm_audio_effects_volume_handler_v2); diff --git a/audio-kernel/asoc/msm-compress-q6-v2.c b/audio-kernel/asoc/msm-compress-q6-v2.c new file mode 100644 index 000000000..c9d452362 --- /dev/null +++ b/audio-kernel/asoc/msm-compress-q6-v2.c @@ -0,0 +1,4646 @@ +/* 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 + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include "msm-pcm-routing-v2.h" +#include "msm-qti-pp-config.h" + +#define TIMEOUT_MS 1000 +#define DSP_PP_BUFFERING_IN_MSEC 25 +#define PARTIAL_DRAIN_ACK_EARLY_BY_MSEC 150 +#define MP3_OUTPUT_FRAME_SZ 1152 +#define AAC_OUTPUT_FRAME_SZ 1024 +#define AC3_OUTPUT_FRAME_SZ 1536 +#define EAC3_OUTPUT_FRAME_SZ 1536 +#define DSP_NUM_OUTPUT_FRAME_BUFFERED 2 +#define FLAC_BLK_SIZE_LIMIT 65535 + +/* Timestamp mode payload offsets */ +#define CAPTURE_META_DATA_TS_OFFSET_LSW 6 +#define CAPTURE_META_DATA_TS_OFFSET_MSW 7 + +/* decoder parameter length */ +#define DDP_DEC_MAX_NUM_PARAM 18 + +/* Default values used if user space does not set */ +#define COMPR_PLAYBACK_MIN_FRAGMENT_SIZE (8 * 1024) +#define COMPR_PLAYBACK_MAX_FRAGMENT_SIZE (128 * 1024) +#define COMPR_PLAYBACK_MIN_NUM_FRAGMENTS (4) +#define COMPR_PLAYBACK_MAX_NUM_FRAGMENTS (16 * 4) + +#define COMPRESSED_LR_VOL_MAX_STEPS 0x2000 +const DECLARE_TLV_DB_LINEAR(msm_compr_vol_gain, 0, + COMPRESSED_LR_VOL_MAX_STEPS); + +/* Stream id switches between 1 and 2 */ +#define NEXT_STREAM_ID(stream_id) ((stream_id & 1) + 1) + +#define STREAM_ARRAY_INDEX(stream_id) (stream_id - 1) + +#define MAX_NUMBER_OF_STREAMS 2 + +struct msm_compr_gapless_state { + bool set_next_stream_id; + int32_t stream_opened[MAX_NUMBER_OF_STREAMS]; + uint32_t initial_samples_drop; + uint32_t trailing_samples_drop; + uint32_t gapless_transition; + bool use_dsp_gapless_mode; + union snd_codec_options codec_options; +}; + +static unsigned int supported_sample_rates[] = { + 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000, 64000, + 88200, 96000, 128000, 144000, 176400, 192000, 352800, 384000, 2822400, + 5644800 +}; + +struct msm_compr_pdata { + struct snd_compr_stream *cstream[MSM_FRONTEND_DAI_MAX]; + uint32_t volume[MSM_FRONTEND_DAI_MAX][2]; /* For both L & R */ + struct msm_compr_audio_effects *audio_effects[MSM_FRONTEND_DAI_MAX]; + bool use_dsp_gapless_mode; + bool use_legacy_api; /* indicates use older asm apis*/ + struct msm_compr_dec_params *dec_params[MSM_FRONTEND_DAI_MAX]; + struct msm_compr_ch_map *ch_map[MSM_FRONTEND_DAI_MAX]; + bool is_in_use[MSM_FRONTEND_DAI_MAX]; +}; + +struct msm_compr_audio { + struct snd_compr_stream *cstream; + struct snd_compr_caps compr_cap; + struct snd_compr_codec_caps codec_caps; + struct snd_compr_params codec_param; + struct audio_client *audio_client; + + uint32_t codec; + uint32_t compr_passthr; + void *buffer; /* virtual address */ + phys_addr_t buffer_paddr; /* physical address */ + uint32_t app_pointer; + uint32_t buffer_size; + uint32_t byte_offset; + uint64_t copied_total; /* bytes consumed by DSP */ + uint64_t bytes_received; /* from userspace */ + uint64_t bytes_sent; /* to DSP */ + + uint64_t received_total; /* bytes received from DSP */ + uint64_t bytes_copied; /* to userspace */ + uint64_t bytes_read; /* from DSP */ + uint32_t bytes_read_offset; /* bytes read offset */ + + uint32_t ts_header_offset; /* holds the timestamp header offset */ + + int32_t first_buffer; + int32_t last_buffer; + int32_t partial_drain_delay; + + uint16_t session_id; + + uint32_t sample_rate; + uint32_t num_channels; + + /* + * convention - commands coming from the same thread + * can use the common cmd_ack var. Others (e.g drain/EOS) + * must use separate vars to track command status. + */ + uint32_t cmd_ack; + uint32_t cmd_interrupt; + uint32_t drain_ready; + uint32_t eos_ack; + + uint32_t stream_available; + uint32_t next_stream; + + uint32_t run_mode; + uint32_t start_delay_lsw; + uint32_t start_delay_msw; + + uint64_t marker_timestamp; + + struct msm_compr_gapless_state gapless_state; + + atomic_t start; + atomic_t eos; + atomic_t drain; + atomic_t xrun; + atomic_t close; + atomic_t wait_on_close; + atomic_t error; + + wait_queue_head_t eos_wait; + wait_queue_head_t drain_wait; + wait_queue_head_t close_wait; + wait_queue_head_t wait_for_stream_avail; + + spinlock_t lock; +}; + +const u32 compr_codecs[] = { + SND_AUDIOCODEC_AC3, SND_AUDIOCODEC_EAC3, SND_AUDIOCODEC_DTS, + SND_AUDIOCODEC_DSD, SND_AUDIOCODEC_TRUEHD, SND_AUDIOCODEC_IEC61937}; + +struct query_audio_effect { + uint32_t mod_id; + uint32_t parm_id; + uint32_t size; + uint32_t offset; + uint32_t device; +}; + +struct msm_compr_audio_effects { + struct bass_boost_params bass_boost; + struct pbe_params pbe; + struct virtualizer_params virtualizer; + struct reverb_params reverb; + struct eq_params equalizer; + struct soft_volume_params volume; + struct query_audio_effect query; +}; + +struct msm_compr_dec_params { + struct snd_dec_ddp ddp_params; +}; + +struct msm_compr_ch_map { + bool set_ch_map; + char channel_map[PCM_FORMAT_MAX_NUM_CHANNEL_V8]; +}; + +static int msm_compr_send_dec_params(struct snd_compr_stream *cstream, + struct msm_compr_dec_params *dec_params, + int stream_id); + +static int msm_compr_set_render_mode(struct msm_compr_audio *prtd, + uint32_t render_mode) { + int ret = -EINVAL; + struct audio_client *ac = prtd->audio_client; + + pr_debug("%s, got render mode %u\n", __func__, render_mode); + + if (render_mode == SNDRV_COMPRESS_RENDER_MODE_AUDIO_MASTER) { + render_mode = ASM_SESSION_MTMX_STRTR_PARAM_RENDER_DEFAULT; + } else if (render_mode == SNDRV_COMPRESS_RENDER_MODE_STC_MASTER) { + render_mode = ASM_SESSION_MTMX_STRTR_PARAM_RENDER_LOCAL_STC; + prtd->run_mode = ASM_SESSION_CMD_RUN_STARTIME_RUN_WITH_DELAY; + } else { + pr_err("%s, Invalid render mode %u\n", __func__, + render_mode); + ret = -EINVAL; + goto exit; + } + + ret = q6asm_send_mtmx_strtr_render_mode(ac, render_mode); + if (ret) { + pr_err("%s, Render mode can't be set error %d\n", __func__, + ret); + } +exit: + return ret; +} + +static int msm_compr_set_clk_rec_mode(struct audio_client *ac, + uint32_t clk_rec_mode) { + int ret = -EINVAL; + + pr_debug("%s, got clk rec mode %u\n", __func__, clk_rec_mode); + + if (clk_rec_mode == SNDRV_COMPRESS_CLK_REC_MODE_NONE) { + clk_rec_mode = ASM_SESSION_MTMX_STRTR_PARAM_CLK_REC_NONE; + } else if (clk_rec_mode == SNDRV_COMPRESS_CLK_REC_MODE_AUTO) { + clk_rec_mode = ASM_SESSION_MTMX_STRTR_PARAM_CLK_REC_AUTO; + } else { + pr_err("%s, Invalid clk rec_mode mode %u\n", __func__, + clk_rec_mode); + ret = -EINVAL; + goto exit; + } + + ret = q6asm_send_mtmx_strtr_clk_rec_mode(ac, clk_rec_mode); + if (ret) { + pr_err("%s, clk rec mode can't be set, error %d\n", __func__, + ret); + } + +exit: + return ret; +} + +static int msm_compr_set_render_window(struct audio_client *ac, + uint32_t ws_lsw, uint32_t ws_msw, + uint32_t we_lsw, uint32_t we_msw) +{ + int ret = -EINVAL; + struct asm_session_mtmx_strtr_param_window_v2_t asm_mtmx_strtr_window; + uint32_t param_id; + + pr_debug("%s, ws_lsw 0x%x ws_msw 0x%x we_lsw 0x%x we_ms 0x%x\n", + __func__, ws_lsw, ws_msw, we_lsw, we_msw); + + memset(&asm_mtmx_strtr_window, 0, + sizeof(struct asm_session_mtmx_strtr_param_window_v2_t)); + asm_mtmx_strtr_window.window_lsw = ws_lsw; + asm_mtmx_strtr_window.window_msw = ws_msw; + param_id = ASM_SESSION_MTMX_STRTR_PARAM_RENDER_WINDOW_START_V2; + ret = q6asm_send_mtmx_strtr_window(ac, &asm_mtmx_strtr_window, + param_id); + if (ret) { + pr_err("%s, start window can't be set error %d\n", __func__, + ret); + goto exit; + } + + asm_mtmx_strtr_window.window_lsw = we_lsw; + asm_mtmx_strtr_window.window_msw = we_msw; + param_id = ASM_SESSION_MTMX_STRTR_PARAM_RENDER_WINDOW_END_V2; + ret = q6asm_send_mtmx_strtr_window(ac, &asm_mtmx_strtr_window, + param_id); + if (ret) { + pr_err("%s, end window can't be set error %d\n", __func__, + ret); + } + +exit: + return ret; +} + +static int msm_compr_enable_adjust_session_clock(struct audio_client *ac, + bool enable) +{ + int ret; + + pr_debug("%s, enable adjust_session %d\n", __func__, enable); + + ret = q6asm_send_mtmx_strtr_enable_adjust_session_clock(ac, enable); + if (ret) + pr_err("%s, adjust session clock can't be set error %d\n", + __func__, ret); + + return ret; +} + +static int msm_compr_adjust_session_clock(struct audio_client *ac, + uint32_t adjust_session_lsw, uint32_t adjust_session_msw) +{ + int ret; + + pr_debug("%s, adjust_session_time_msw 0x%x adjust_session_time_lsw 0x%x\n", + __func__, adjust_session_msw, adjust_session_lsw); + + ret = q6asm_adjust_session_clock(ac, + adjust_session_lsw, + adjust_session_msw); + if (ret) + pr_err("%s, adjust session clock can't be set error %d\n", + __func__, ret); + + return ret; +} + +static int msm_compr_set_volume(struct snd_compr_stream *cstream, + uint32_t volume_l, uint32_t volume_r) +{ + struct msm_compr_audio *prtd; + int rc = 0; + uint32_t avg_vol, gain_list[VOLUME_CONTROL_MAX_CHANNELS]; + uint32_t num_channels; + struct snd_soc_pcm_runtime *rtd; + struct msm_compr_pdata *pdata; + bool use_default = true; + u8 *chmap = NULL; + + pr_debug("%s: volume_l %d volume_r %d\n", + __func__, volume_l, volume_r); + if (!cstream || !cstream->runtime) { + pr_err("%s: session not active\n", __func__); + return -EPERM; + } + rtd = cstream->private_data; + prtd = cstream->runtime->private_data; + + if (!rtd || !rtd->platform || !prtd || !prtd->audio_client) { + pr_err("%s: invalid rtd, prtd or audio client", __func__); + return rc; + } + pdata = snd_soc_platform_get_drvdata(rtd->platform); + + if (prtd->compr_passthr != LEGACY_PCM) { + pr_debug("%s: No volume config for passthrough %d\n", + __func__, prtd->compr_passthr); + return rc; + } + + use_default = !(pdata->ch_map[rtd->dai_link->id]->set_ch_map); + chmap = pdata->ch_map[rtd->dai_link->id]->channel_map; + num_channels = prtd->num_channels; + + if (prtd->num_channels > 2) { + /* + * Currently the left and right gains are averaged an applied + * to all channels. This might not be desirable. But currently, + * there exists no API in userspace to send a list of gains for + * each channel either. If such an API does become available, + * the mixer control must be updated to accept more than 2 + * channel gains. + * + */ + avg_vol = (volume_l + volume_r) / 2; + rc = q6asm_set_volume(prtd->audio_client, avg_vol); + } else { + gain_list[0] = volume_l; + gain_list[1] = volume_r; + gain_list[2] = volume_l; + num_channels = 3; + use_default = true; + rc = q6asm_set_multich_gain(prtd->audio_client, num_channels, + gain_list, chmap, use_default); + } + + if (rc < 0) + pr_err("%s: Send vol gain command failed rc=%d\n", + __func__, rc); + + return rc; +} + +static int msm_compr_send_ddp_cfg(struct audio_client *ac, + struct snd_dec_ddp *ddp, + int stream_id) +{ + int i, rc; + + pr_debug("%s\n", __func__); + for (i = 0; i < ddp->params_length; i++) { + rc = q6asm_ds1_set_stream_endp_params(ac, ddp->params_id[i], + ddp->params_value[i], + stream_id); + if (rc) { + pr_err("sending params_id: %d failed\n", + ddp->params_id[i]); + return rc; + } + } + return 0; +} + +static int msm_compr_send_buffer(struct msm_compr_audio *prtd) +{ + int buffer_length; + uint64_t bytes_available; + struct audio_aio_write_param param; + struct snd_codec_metadata *buff_addr; + + if (!atomic_read(&prtd->start)) { + pr_err("%s: stream is not in started state\n", __func__); + return -EINVAL; + } + + + if (atomic_read(&prtd->xrun)) { + WARN(1, "%s called while xrun is true", __func__); + return -EPERM; + } + + pr_debug("%s: bytes_received = %llu copied_total = %llu\n", + __func__, prtd->bytes_received, prtd->copied_total); + if (prtd->first_buffer && prtd->gapless_state.use_dsp_gapless_mode && + prtd->compr_passthr == LEGACY_PCM) + q6asm_stream_send_meta_data(prtd->audio_client, + prtd->audio_client->stream_id, + prtd->gapless_state.initial_samples_drop, + prtd->gapless_state.trailing_samples_drop); + + buffer_length = prtd->codec_param.buffer.fragment_size; + bytes_available = prtd->bytes_received - prtd->copied_total; + if (bytes_available < prtd->codec_param.buffer.fragment_size) + buffer_length = bytes_available; + + if (prtd->byte_offset + buffer_length > prtd->buffer_size) { + buffer_length = (prtd->buffer_size - prtd->byte_offset); + pr_debug("%s: wrap around situation, send partial data %d now", + __func__, buffer_length); + } + + if (buffer_length) { + param.paddr = prtd->buffer_paddr + prtd->byte_offset; + WARN(prtd->byte_offset % 32 != 0, "offset %x not multiple of 32\n", + prtd->byte_offset); + } else { + param.paddr = prtd->buffer_paddr; + } + param.len = buffer_length; + if (prtd->ts_header_offset) { + buff_addr = (struct snd_codec_metadata *) + (prtd->buffer + prtd->byte_offset); + param.len = buff_addr->length; + param.msw_ts = (uint32_t) + ((buff_addr->timestamp & 0xFFFFFFFF00000000LL) >> 32); + param.lsw_ts = (uint32_t) (buff_addr->timestamp & 0xFFFFFFFFLL); + param.paddr += prtd->ts_header_offset; + param.flags = SET_TIMESTAMP; + param.metadata_len = prtd->ts_header_offset; + } else { + param.msw_ts = 0; + param.lsw_ts = 0; + param.flags = NO_TIMESTAMP; + param.metadata_len = 0; + } + param.uid = buffer_length; + param.last_buffer = prtd->last_buffer; + + pr_debug("%s: sending %d bytes to DSP byte_offset = %d\n", + __func__, param.len, prtd->byte_offset); + if (q6asm_async_write(prtd->audio_client, ¶m) < 0) { + pr_err("%s:q6asm_async_write failed\n", __func__); + } else { + prtd->bytes_sent += buffer_length; + if (prtd->first_buffer) + prtd->first_buffer = 0; + } + + return 0; +} + +static int msm_compr_read_buffer(struct msm_compr_audio *prtd) +{ + int buffer_length; + uint64_t bytes_available; + uint64_t buffer_sent; + struct audio_aio_read_param param; + int ret; + + if (!atomic_read(&prtd->start)) { + pr_err("%s: stream is not in started state\n", __func__); + return -EINVAL; + } + + buffer_length = prtd->codec_param.buffer.fragment_size - + prtd->ts_header_offset; + bytes_available = prtd->received_total - prtd->bytes_copied; + buffer_sent = prtd->bytes_read - prtd->bytes_copied; + if (buffer_sent + buffer_length + prtd->ts_header_offset + > prtd->buffer_size) { + pr_debug(" %s : Buffer is Full bytes_available: %llu\n", + __func__, bytes_available); + return 0; + } + + memset(¶m, 0x0, sizeof(struct audio_aio_read_param)); + param.paddr = prtd->buffer_paddr + prtd->bytes_read_offset + + prtd->ts_header_offset; + param.len = buffer_length; + param.uid = buffer_length; + param.flags = prtd->codec_param.codec.flags; + + pr_debug("%s: reading %d bytes from DSP byte_offset = %llu\n", + __func__, buffer_length, prtd->bytes_read); + ret = q6asm_async_read(prtd->audio_client, ¶m); + if (ret < 0) { + pr_err("%s: q6asm_async_read failed - %d\n", + __func__, ret); + return ret; + } + prtd->bytes_read += buffer_length + prtd->ts_header_offset; + prtd->bytes_read_offset += buffer_length + prtd->ts_header_offset; + if (prtd->bytes_read_offset >= prtd->buffer_size) + prtd->bytes_read_offset -= prtd->buffer_size; + + return 0; +} + +static void compr_event_handler(uint32_t opcode, + uint32_t token, uint32_t *payload, void *priv) +{ + struct msm_compr_audio *prtd = priv; + struct snd_compr_stream *cstream; + struct audio_client *ac; + uint32_t chan_mode = 0; + uint32_t sample_rate = 0; + uint64_t bytes_available; + int stream_id; + uint32_t stream_index; + unsigned long flags; + uint64_t read_size; + uint32_t *buff_addr; + struct snd_soc_pcm_runtime *rtd; + int ret = 0; + + if (!prtd) { + pr_err("%s: prtd is NULL\n", __func__); + return; + } + cstream = prtd->cstream; + if (!cstream) { + pr_err("%s: cstream is NULL\n", __func__); + return; + } + + ac = prtd->audio_client; + + /* + * Token for rest of the compressed commands use to set + * session id, stream id, dir etc. + */ + stream_id = q6asm_get_stream_id_from_token(token); + + pr_debug("%s opcode =%08x\n", __func__, opcode); + switch (opcode) { + case ASM_DATA_EVENT_WRITE_DONE_V2: + spin_lock_irqsave(&prtd->lock, flags); + + if (payload[3]) { + pr_err("%s: WRITE FAILED w/ err 0x%x !, paddr 0x%x, byte_offset=%d,copied_total=%llu,token=%d\n", + __func__, + payload[3], + payload[0], + prtd->byte_offset, + prtd->copied_total, token); + + if (atomic_cmpxchg(&prtd->drain, 1, 0) && + prtd->last_buffer) { + pr_debug("%s: wake up on drain\n", __func__); + prtd->drain_ready = 1; + wake_up(&prtd->drain_wait); + prtd->last_buffer = 0; + } else { + atomic_set(&prtd->start, 0); + } + } else { + pr_debug("ASM_DATA_EVENT_WRITE_DONE_V2 offset %d, length %d\n", + prtd->byte_offset, token); + } + + /* + * Token for WRITE command represents the amount of data + * written to ADSP in the last write, update offset and + * total copied data accordingly. + */ + if (prtd->ts_header_offset) { + /* Always assume that the data will be sent to DSP on + * frame boundary. + * i.e, one frame of userspace write will result in + * one kernel write to DSP. This is needed as + * timestamp will be sent per frame. + */ + prtd->byte_offset += + prtd->codec_param.buffer.fragment_size; + prtd->copied_total += + prtd->codec_param.buffer.fragment_size; + } else { + prtd->byte_offset += token; + prtd->copied_total += token; + } + if (prtd->byte_offset >= prtd->buffer_size) + prtd->byte_offset -= prtd->buffer_size; + + snd_compr_fragment_elapsed(cstream); + + if (!atomic_read(&prtd->start)) { + /* Writes must be restarted from _copy() */ + pr_debug("write_done received while not started, treat as xrun"); + atomic_set(&prtd->xrun, 1); + spin_unlock_irqrestore(&prtd->lock, flags); + break; + } + + bytes_available = prtd->bytes_received - prtd->copied_total; + if (bytes_available < cstream->runtime->fragment_size) { + pr_debug("WRITE_DONE Insufficient data to send. break out\n"); + atomic_set(&prtd->xrun, 1); + + if (prtd->last_buffer) + prtd->last_buffer = 0; + if (atomic_read(&prtd->drain)) { + pr_debug("wake up on drain\n"); + prtd->drain_ready = 1; + wake_up(&prtd->drain_wait); + atomic_set(&prtd->drain, 0); + } + } else if ((bytes_available == cstream->runtime->fragment_size) + && atomic_read(&prtd->drain)) { + prtd->last_buffer = 1; + msm_compr_send_buffer(prtd); + prtd->last_buffer = 0; + } else + msm_compr_send_buffer(prtd); + + spin_unlock_irqrestore(&prtd->lock, flags); + break; + + case ASM_DATA_EVENT_READ_DONE_V2: + spin_lock_irqsave(&prtd->lock, flags); + + pr_debug("ASM_DATA_EVENT_READ_DONE_V2 offset %d, length %d\n", + prtd->byte_offset, payload[4]); + + if (prtd->ts_header_offset) { + /* Update the header for received buffer */ + buff_addr = prtd->buffer + prtd->byte_offset; + /* Write the actual length of the received buffer */ + *buff_addr = payload[4]; + buff_addr++; + /* Write the offset */ + *buff_addr = prtd->ts_header_offset; + buff_addr++; + /* Write the TS LSW */ + *buff_addr = payload[CAPTURE_META_DATA_TS_OFFSET_LSW]; + buff_addr++; + /* Write the TS MSW */ + *buff_addr = payload[CAPTURE_META_DATA_TS_OFFSET_MSW]; + } + /* Always assume read_size is same as fragment_size */ + read_size = prtd->codec_param.buffer.fragment_size; + prtd->byte_offset += read_size; + prtd->received_total += read_size; + if (prtd->byte_offset >= prtd->buffer_size) + prtd->byte_offset -= prtd->buffer_size; + + snd_compr_fragment_elapsed(cstream); + + if (!atomic_read(&prtd->start)) { + pr_debug("read_done received while not started, treat as xrun"); + atomic_set(&prtd->xrun, 1); + spin_unlock_irqrestore(&prtd->lock, flags); + break; + } + msm_compr_read_buffer(prtd); + + spin_unlock_irqrestore(&prtd->lock, flags); + break; + + case ASM_DATA_EVENT_RENDERED_EOS: + spin_lock_irqsave(&prtd->lock, flags); + pr_debug("%s: ASM_DATA_CMDRSP_EOS token 0x%x,stream id %d\n", + __func__, token, stream_id); + if (atomic_read(&prtd->eos) && + !prtd->gapless_state.set_next_stream_id) { + pr_debug("ASM_DATA_CMDRSP_EOS wake up\n"); + prtd->eos_ack = 1; + wake_up(&prtd->eos_wait); + } + atomic_set(&prtd->eos, 0); + stream_index = STREAM_ARRAY_INDEX(stream_id); + if (stream_index >= MAX_NUMBER_OF_STREAMS || + stream_index < 0) { + pr_err("%s: Invalid stream index %d", __func__, + stream_index); + spin_unlock_irqrestore(&prtd->lock, flags); + break; + } + + if (prtd->gapless_state.set_next_stream_id && + prtd->gapless_state.stream_opened[stream_index]) { + pr_debug("%s: CMD_CLOSE stream_id %d\n", + __func__, stream_id); + q6asm_stream_cmd_nowait(ac, CMD_CLOSE, stream_id); + atomic_set(&prtd->close, 1); + prtd->gapless_state.stream_opened[stream_index] = 0; + prtd->gapless_state.set_next_stream_id = false; + } + if (prtd->gapless_state.gapless_transition) + prtd->gapless_state.gapless_transition = 0; + spin_unlock_irqrestore(&prtd->lock, flags); + break; + case ASM_STREAM_PP_EVENT: + case ASM_STREAM_CMD_ENCDEC_EVENTS: + pr_debug("%s: ASM_STREAM_EVENT(0x%x)\n", __func__, opcode); + rtd = cstream->private_data; + if (!rtd) { + pr_err("%s: rtd is NULL\n", __func__); + return; + } + + ret = msm_adsp_inform_mixer_ctl(rtd, payload); + if (ret) { + pr_err("%s: failed to inform mixer ctrl. err = %d\n", + __func__, ret); + return; + } + break; + case ASM_DATA_EVENT_SR_CM_CHANGE_NOTIFY: + case ASM_DATA_EVENT_ENC_SR_CM_CHANGE_NOTIFY: { + pr_debug("ASM_DATA_EVENT_SR_CM_CHANGE_NOTIFY\n"); + chan_mode = payload[1] >> 16; + sample_rate = payload[2] >> 16; + if (prtd && (chan_mode != prtd->num_channels || + sample_rate != prtd->sample_rate)) { + prtd->num_channels = chan_mode; + prtd->sample_rate = sample_rate; + } + } + /* Fallthrough here */ + case APR_BASIC_RSP_RESULT: { + switch (payload[0]) { + case ASM_SESSION_CMD_RUN_V2: + /* check if the first buffer need to be sent to DSP */ + pr_debug("ASM_SESSION_CMD_RUN_V2\n"); + + /* FIXME: A state is a better way, dealing with this */ + spin_lock_irqsave(&prtd->lock, flags); + + if (cstream->direction == SND_COMPRESS_CAPTURE) { + atomic_set(&prtd->start, 1); + msm_compr_read_buffer(prtd); + spin_unlock_irqrestore(&prtd->lock, flags); + break; + } + + if (!prtd->bytes_sent) { + bytes_available = prtd->bytes_received - + prtd->copied_total; + if (bytes_available < + cstream->runtime->fragment_size) { + pr_debug("CMD_RUN_V2 Insufficient data to send. break out\n"); + atomic_set(&prtd->xrun, 1); + } else { + msm_compr_send_buffer(prtd); + } + } + + /* + * The condition below ensures playback finishes in the + * follow cornercase + * WRITE(last buffer) + * WAIT_FOR_DRAIN + * PAUSE + * WRITE_DONE(X) + * RESUME + */ + if ((prtd->copied_total == prtd->bytes_sent) && + atomic_read(&prtd->drain)) { + pr_debug("RUN ack, wake up & continue pending drain\n"); + + if (prtd->last_buffer) + prtd->last_buffer = 0; + + prtd->drain_ready = 1; + wake_up(&prtd->drain_wait); + atomic_set(&prtd->drain, 0); + } + + spin_unlock_irqrestore(&prtd->lock, flags); + break; + case ASM_STREAM_CMD_FLUSH: + pr_debug("%s: ASM_STREAM_CMD_FLUSH:", __func__); + pr_debug("token 0x%x, stream id %d\n", token, + stream_id); + prtd->cmd_ack = 1; + break; + case ASM_DATA_CMD_REMOVE_INITIAL_SILENCE: + pr_debug("%s: ASM_DATA_CMD_REMOVE_INITIAL_SILENCE:", + __func__); + pr_debug("token 0x%x, stream id = %d\n", token, + stream_id); + break; + case ASM_DATA_CMD_REMOVE_TRAILING_SILENCE: + pr_debug("%s: ASM_DATA_CMD_REMOVE_TRAILING_SILENCE:", + __func__); + pr_debug("token = 0x%x, stream id = %d\n", token, + stream_id); + break; + case ASM_STREAM_CMD_CLOSE: + pr_debug("%s: ASM_DATA_CMD_CLOSE:", __func__); + pr_debug("token 0x%x, stream id %d\n", token, + stream_id); + /* + * wakeup wait for stream avail on stream 3 + * after stream 1 ends. + */ + if (prtd->next_stream) { + pr_debug("%s:CLOSE:wakeup wait for stream\n", + __func__); + prtd->stream_available = 1; + wake_up(&prtd->wait_for_stream_avail); + prtd->next_stream = 0; + } + if (atomic_read(&prtd->close) && + atomic_read(&prtd->wait_on_close)) { + prtd->cmd_ack = 1; + wake_up(&prtd->close_wait); + } + atomic_set(&prtd->close, 0); + break; + case ASM_STREAM_CMD_REGISTER_PP_EVENTS: + pr_debug("%s: ASM_STREAM_CMD_REGISTER_PP_EVENTS:", + __func__); + break; + default: + break; + } + break; + } + case ASM_SESSION_CMDRSP_GET_SESSIONTIME_V3: + pr_debug("%s: ASM_SESSION_CMDRSP_GET_SESSIONTIME_V3\n", + __func__); + break; + case RESET_EVENTS: + pr_err("%s: Received reset events CB, move to error state", + __func__); + spin_lock_irqsave(&prtd->lock, flags); + /* + * Since ADSP is down, let this driver pretend that it copied + * all the bytes received, so that next write will be triggered + */ + prtd->copied_total = prtd->bytes_received; + snd_compr_fragment_elapsed(cstream); + atomic_set(&prtd->error, 1); + wake_up(&prtd->drain_wait); + if (atomic_cmpxchg(&prtd->eos, 1, 0)) { + pr_debug("%s:unblock eos wait queues", __func__); + wake_up(&prtd->eos_wait); + } + spin_unlock_irqrestore(&prtd->lock, flags); + break; + default: + pr_debug("%s: Not Supported Event opcode[0x%x]\n", + __func__, opcode); + break; + } +} + +static int msm_compr_get_partial_drain_delay(int frame_sz, int sample_rate) +{ + int delay_time_ms = 0; + + delay_time_ms = ((DSP_NUM_OUTPUT_FRAME_BUFFERED * frame_sz * 1000) / + sample_rate) + DSP_PP_BUFFERING_IN_MSEC; + delay_time_ms = delay_time_ms > PARTIAL_DRAIN_ACK_EARLY_BY_MSEC ? + delay_time_ms - PARTIAL_DRAIN_ACK_EARLY_BY_MSEC : 0; + + pr_debug("%s: frame_sz %d, sample_rate %d, partial drain delay %d\n", + __func__, frame_sz, sample_rate, delay_time_ms); + return delay_time_ms; +} + +static void populate_codec_list(struct msm_compr_audio *prtd) +{ + pr_debug("%s\n", __func__); + prtd->compr_cap.direction = SND_COMPRESS_PLAYBACK; + prtd->compr_cap.min_fragment_size = + COMPR_PLAYBACK_MIN_FRAGMENT_SIZE; + prtd->compr_cap.max_fragment_size = + COMPR_PLAYBACK_MAX_FRAGMENT_SIZE; + prtd->compr_cap.min_fragments = + COMPR_PLAYBACK_MIN_NUM_FRAGMENTS; + prtd->compr_cap.max_fragments = + COMPR_PLAYBACK_MAX_NUM_FRAGMENTS; + prtd->compr_cap.num_codecs = 17; + prtd->compr_cap.codecs[0] = SND_AUDIOCODEC_MP3; + prtd->compr_cap.codecs[1] = SND_AUDIOCODEC_AAC; + prtd->compr_cap.codecs[2] = SND_AUDIOCODEC_AC3; + prtd->compr_cap.codecs[3] = SND_AUDIOCODEC_EAC3; + prtd->compr_cap.codecs[4] = SND_AUDIOCODEC_MP2; + prtd->compr_cap.codecs[5] = SND_AUDIOCODEC_PCM; + prtd->compr_cap.codecs[6] = SND_AUDIOCODEC_WMA; + prtd->compr_cap.codecs[7] = SND_AUDIOCODEC_WMA_PRO; + prtd->compr_cap.codecs[8] = SND_AUDIOCODEC_FLAC; + prtd->compr_cap.codecs[9] = SND_AUDIOCODEC_VORBIS; + prtd->compr_cap.codecs[10] = SND_AUDIOCODEC_ALAC; + prtd->compr_cap.codecs[11] = SND_AUDIOCODEC_APE; + prtd->compr_cap.codecs[12] = SND_AUDIOCODEC_DTS; + prtd->compr_cap.codecs[13] = SND_AUDIOCODEC_DSD; + prtd->compr_cap.codecs[14] = SND_AUDIOCODEC_APTX; + prtd->compr_cap.codecs[15] = SND_AUDIOCODEC_TRUEHD; + prtd->compr_cap.codecs[16] = SND_AUDIOCODEC_IEC61937; +} + +static int msm_compr_send_media_format_block(struct snd_compr_stream *cstream, + int stream_id, + bool use_gapless_codec_options) +{ + struct snd_compr_runtime *runtime = cstream->runtime; + struct msm_compr_audio *prtd = runtime->private_data; + struct snd_soc_pcm_runtime *rtd = cstream->private_data; + struct msm_compr_pdata *pdata = + snd_soc_platform_get_drvdata(rtd->platform); + struct asm_aac_cfg aac_cfg; + struct asm_wma_cfg wma_cfg; + struct asm_wmapro_cfg wma_pro_cfg; + struct asm_flac_cfg flac_cfg; + struct asm_vorbis_cfg vorbis_cfg; + struct asm_alac_cfg alac_cfg; + struct asm_ape_cfg ape_cfg; + struct asm_dsd_cfg dsd_cfg; + struct aptx_dec_bt_addr_cfg aptx_cfg; + union snd_codec_options *codec_options; + + int ret = 0; + uint16_t bit_width; + bool use_default_chmap = true; + char *chmap = NULL; + uint16_t sample_word_size; + + pr_debug("%s: use_gapless_codec_options %d\n", + __func__, use_gapless_codec_options); + + if (use_gapless_codec_options) + codec_options = &(prtd->gapless_state.codec_options); + else + codec_options = &(prtd->codec_param.codec.options); + + if (!codec_options) { + pr_err("%s: codec_options is NULL\n", __func__); + return -EINVAL; + } + + switch (prtd->codec) { + case FORMAT_LINEAR_PCM: + pr_debug("SND_AUDIOCODEC_PCM\n"); + if (pdata->ch_map[rtd->dai_link->id]) { + use_default_chmap = + !(pdata->ch_map[rtd->dai_link->id]->set_ch_map); + chmap = + pdata->ch_map[rtd->dai_link->id]->channel_map; + } + + switch (prtd->codec_param.codec.format) { + case SNDRV_PCM_FORMAT_S32_LE: + bit_width = 32; + sample_word_size = 32; + break; + case SNDRV_PCM_FORMAT_S24_LE: + bit_width = 24; + sample_word_size = 32; + break; + case SNDRV_PCM_FORMAT_S24_3LE: + bit_width = 24; + sample_word_size = 24; + break; + case SNDRV_PCM_FORMAT_S16_LE: + default: + bit_width = 16; + sample_word_size = 16; + break; + } + + if (q6core_get_avcs_api_version_per_service( + APRV2_IDS_SERVICE_ID_ADSP_ASM_V) >= + ADSP_ASM_API_VERSION_V2) { + ret = q6asm_media_format_block_pcm_format_support_v5( + prtd->audio_client, + prtd->sample_rate, + prtd->num_channels, + bit_width, stream_id, + use_default_chmap, + chmap, + sample_word_size, + ASM_LITTLE_ENDIAN, + DEFAULT_QF); + } else { + ret = q6asm_media_format_block_pcm_format_support_v4( + prtd->audio_client, + prtd->sample_rate, + prtd->num_channels, + bit_width, stream_id, + use_default_chmap, + chmap, + sample_word_size, + ASM_LITTLE_ENDIAN, + DEFAULT_QF); + } + if (ret < 0) + pr_err("%s: CMD Format block failed\n", __func__); + + break; + case FORMAT_MP3: + pr_debug("SND_AUDIOCODEC_MP3\n"); + /* no media format block needed */ + break; + case FORMAT_MPEG4_AAC: + pr_debug("SND_AUDIOCODEC_AAC\n"); + memset(&aac_cfg, 0x0, sizeof(struct asm_aac_cfg)); + aac_cfg.aot = AAC_ENC_MODE_EAAC_P; + if (prtd->codec_param.codec.format == + SND_AUDIOSTREAMFORMAT_MP4ADTS) + aac_cfg.format = 0x0; + else if (prtd->codec_param.codec.format == + SND_AUDIOSTREAMFORMAT_MP4LATM) + aac_cfg.format = 0x04; + else + aac_cfg.format = 0x03; + aac_cfg.ch_cfg = prtd->num_channels; + aac_cfg.sample_rate = prtd->sample_rate; + ret = q6asm_stream_media_format_block_aac(prtd->audio_client, + &aac_cfg, stream_id); + if (ret < 0) + pr_err("%s: CMD Format block failed\n", __func__); + break; + case FORMAT_AC3: + pr_debug("SND_AUDIOCODEC_AC3\n"); + break; + case FORMAT_EAC3: + pr_debug("SND_AUDIOCODEC_EAC3\n"); + break; + case FORMAT_WMA_V9: + pr_debug("SND_AUDIOCODEC_WMA\n"); + memset(&wma_cfg, 0x0, sizeof(struct asm_wma_cfg)); + wma_cfg.format_tag = prtd->codec_param.codec.format; + wma_cfg.ch_cfg = prtd->codec_param.codec.ch_in; + wma_cfg.sample_rate = prtd->sample_rate; + wma_cfg.avg_bytes_per_sec = codec_options->wma.avg_bit_rate/8; + wma_cfg.block_align = codec_options->wma.super_block_align; + wma_cfg.valid_bits_per_sample = + codec_options->wma.bits_per_sample; + wma_cfg.ch_mask = codec_options->wma.channelmask; + wma_cfg.encode_opt = codec_options->wma.encodeopt; + ret = q6asm_media_format_block_wma(prtd->audio_client, + &wma_cfg, stream_id); + if (ret < 0) + pr_err("%s: CMD Format block failed\n", __func__); + break; + case FORMAT_WMA_V10PRO: + pr_debug("SND_AUDIOCODEC_WMA_PRO\n"); + memset(&wma_pro_cfg, 0x0, sizeof(struct asm_wmapro_cfg)); + wma_pro_cfg.format_tag = prtd->codec_param.codec.format; + wma_pro_cfg.ch_cfg = prtd->codec_param.codec.ch_in; + wma_pro_cfg.sample_rate = prtd->sample_rate; + wma_cfg.avg_bytes_per_sec = codec_options->wma.avg_bit_rate/8; + wma_pro_cfg.block_align = codec_options->wma.super_block_align; + wma_pro_cfg.valid_bits_per_sample = + codec_options->wma.bits_per_sample; + wma_pro_cfg.ch_mask = codec_options->wma.channelmask; + wma_pro_cfg.encode_opt = codec_options->wma.encodeopt; + wma_pro_cfg.adv_encode_opt = codec_options->wma.encodeopt1; + wma_pro_cfg.adv_encode_opt2 = codec_options->wma.encodeopt2; + ret = q6asm_media_format_block_wmapro(prtd->audio_client, + &wma_pro_cfg, stream_id); + if (ret < 0) + pr_err("%s: CMD Format block failed\n", __func__); + break; + case FORMAT_MP2: + pr_debug("%s: SND_AUDIOCODEC_MP2\n", __func__); + break; + case FORMAT_FLAC: + pr_debug("%s: SND_AUDIOCODEC_FLAC\n", __func__); + memset(&flac_cfg, 0x0, sizeof(struct asm_flac_cfg)); + flac_cfg.ch_cfg = prtd->num_channels; + flac_cfg.sample_rate = prtd->sample_rate; + flac_cfg.stream_info_present = 1; + flac_cfg.sample_size = codec_options->flac_dec.sample_size; + flac_cfg.min_blk_size = codec_options->flac_dec.min_blk_size; + flac_cfg.max_blk_size = codec_options->flac_dec.max_blk_size; + flac_cfg.max_frame_size = + codec_options->flac_dec.max_frame_size; + flac_cfg.min_frame_size = + codec_options->flac_dec.min_frame_size; + + ret = q6asm_stream_media_format_block_flac(prtd->audio_client, + &flac_cfg, stream_id); + if (ret < 0) + pr_err("%s: CMD Format block failed ret %d\n", + __func__, ret); + + break; + case FORMAT_VORBIS: + pr_debug("%s: SND_AUDIOCODEC_VORBIS\n", __func__); + memset(&vorbis_cfg, 0x0, sizeof(struct asm_vorbis_cfg)); + vorbis_cfg.bit_stream_fmt = + codec_options->vorbis_dec.bit_stream_fmt; + + ret = q6asm_stream_media_format_block_vorbis( + prtd->audio_client, &vorbis_cfg, + stream_id); + if (ret < 0) + pr_err("%s: CMD Format block failed ret %d\n", + __func__, ret); + + break; + case FORMAT_ALAC: + pr_debug("%s: SND_AUDIOCODEC_ALAC\n", __func__); + memset(&alac_cfg, 0x0, sizeof(struct asm_alac_cfg)); + alac_cfg.num_channels = prtd->num_channels; + alac_cfg.sample_rate = prtd->sample_rate; + alac_cfg.frame_length = codec_options->alac.frame_length; + alac_cfg.compatible_version = + codec_options->alac.compatible_version; + alac_cfg.bit_depth = codec_options->alac.bit_depth; + alac_cfg.pb = codec_options->alac.pb; + alac_cfg.mb = codec_options->alac.mb; + alac_cfg.kb = codec_options->alac.kb; + alac_cfg.max_run = codec_options->alac.max_run; + alac_cfg.max_frame_bytes = codec_options->alac.max_frame_bytes; + alac_cfg.avg_bit_rate = codec_options->alac.avg_bit_rate; + alac_cfg.channel_layout_tag = + codec_options->alac.channel_layout_tag; + + ret = q6asm_media_format_block_alac(prtd->audio_client, + &alac_cfg, stream_id); + if (ret < 0) + pr_err("%s: CMD Format block failed ret %d\n", + __func__, ret); + break; + case FORMAT_APE: + pr_debug("%s: SND_AUDIOCODEC_APE\n", __func__); + memset(&ape_cfg, 0x0, sizeof(struct asm_ape_cfg)); + ape_cfg.num_channels = prtd->num_channels; + ape_cfg.sample_rate = prtd->sample_rate; + ape_cfg.compatible_version = + codec_options->ape.compatible_version; + ape_cfg.compression_level = + codec_options->ape.compression_level; + ape_cfg.format_flags = codec_options->ape.format_flags; + ape_cfg.blocks_per_frame = codec_options->ape.blocks_per_frame; + ape_cfg.final_frame_blocks = + codec_options->ape.final_frame_blocks; + ape_cfg.total_frames = codec_options->ape.total_frames; + ape_cfg.bits_per_sample = codec_options->ape.bits_per_sample; + ape_cfg.seek_table_present = + codec_options->ape.seek_table_present; + + ret = q6asm_media_format_block_ape(prtd->audio_client, + &ape_cfg, stream_id); + + if (ret < 0) + pr_err("%s: CMD Format block failed ret %d\n", + __func__, ret); + break; + case FORMAT_DTS: + pr_debug("SND_AUDIOCODEC_DTS\n"); + /* no media format block needed */ + break; + case FORMAT_DSD: + pr_debug("%s: SND_AUDIOCODEC_DSD\n", __func__); + memset(&dsd_cfg, 0x0, sizeof(struct asm_dsd_cfg)); + dsd_cfg.num_channels = prtd->num_channels; + dsd_cfg.dsd_data_rate = prtd->sample_rate; + dsd_cfg.num_version = 0; + dsd_cfg.is_bitwise_big_endian = 1; + dsd_cfg.dsd_channel_block_size = 1; + ret = q6asm_media_format_block_dsd(prtd->audio_client, + &dsd_cfg, stream_id); + if (ret < 0) + pr_err("%s: CMD DSD Format block failed ret %d\n", + __func__, ret); + break; + case FORMAT_TRUEHD: + pr_debug("SND_AUDIOCODEC_TRUEHD\n"); + /* no media format block needed */ + break; + case FORMAT_IEC61937: + pr_debug("SND_AUDIOCODEC_IEC61937\n"); + ret = q6asm_media_format_block_iec(prtd->audio_client, + prtd->sample_rate, + prtd->num_channels); + if (ret < 0) + pr_err("%s: CMD IEC61937 Format block failed ret %d\n", + __func__, ret); + break; + case FORMAT_APTX: + pr_debug("SND_AUDIOCODEC_APTX\n"); + memset(&aptx_cfg, 0x0, sizeof(struct aptx_dec_bt_addr_cfg)); + ret = q6asm_stream_media_format_block_aptx_dec( + prtd->audio_client, + prtd->sample_rate, + stream_id); + if (ret >= 0) { + aptx_cfg.nap = codec_options->aptx_dec.nap; + aptx_cfg.uap = codec_options->aptx_dec.uap; + aptx_cfg.lap = codec_options->aptx_dec.lap; + q6asm_set_aptx_dec_bt_addr(prtd->audio_client, + &aptx_cfg); + } else { + pr_err("%s: CMD Format block failed ret %d\n", + __func__, ret); + } + break; + default: + pr_debug("%s, unsupported format, skip", __func__); + break; + } + return ret; +} + +static int msm_compr_init_pp_params(struct snd_compr_stream *cstream, + struct audio_client *ac) +{ + int ret = 0; + struct asm_softvolume_params softvol = { + .period = SOFT_VOLUME_PERIOD, + .step = SOFT_VOLUME_STEP, + .rampingcurve = SOFT_VOLUME_CURVE_LINEAR, + }; + + switch (ac->topology) { + default: + ret = q6asm_set_softvolume_v2(ac, &softvol, + SOFT_VOLUME_INSTANCE_1); + if (ret < 0) + pr_err("%s: Send SoftVolume Param failed ret=%d\n", + __func__, ret); + + break; + } + return ret; +} + +static int msm_compr_configure_dsp_for_playback + (struct snd_compr_stream *cstream) +{ + struct snd_compr_runtime *runtime = cstream->runtime; + struct msm_compr_audio *prtd = runtime->private_data; + struct snd_soc_pcm_runtime *soc_prtd = cstream->private_data; + uint16_t bits_per_sample = 16; + int dir = IN, ret = 0; + struct audio_client *ac = prtd->audio_client; + uint32_t stream_index; + struct asm_softpause_params softpause = { + .enable = SOFT_PAUSE_ENABLE, + .period = SOFT_PAUSE_PERIOD, + .step = SOFT_PAUSE_STEP, + .rampingcurve = SOFT_PAUSE_CURVE_LINEAR, + }; + struct asm_softvolume_params softvol = { + .period = SOFT_VOLUME_PERIOD, + .step = SOFT_VOLUME_STEP, + .rampingcurve = SOFT_VOLUME_CURVE_LINEAR, + }; + struct snd_kcontrol *kctl; + struct snd_ctl_elem_value kctl_elem_value; + uint16_t target_asm_bit_width = 0; + + pr_debug("%s: stream_id %d\n", __func__, ac->stream_id); + stream_index = STREAM_ARRAY_INDEX(ac->stream_id); + if (stream_index >= MAX_NUMBER_OF_STREAMS || stream_index < 0) { + pr_err("%s: Invalid stream index:%d", __func__, stream_index); + return -EINVAL; + } + + kctl = snd_soc_card_get_kcontrol(soc_prtd->card, + DSP_BIT_WIDTH_MIXER_CTL); + if (kctl) { + kctl->get(kctl, &kctl_elem_value); + target_asm_bit_width = kctl_elem_value.value.integer.value[0]; + if (target_asm_bit_width > 0) { + pr_debug("%s enforce ASM bitwidth to %d from %d\n", + __func__, + target_asm_bit_width, + bits_per_sample); + bits_per_sample = target_asm_bit_width; + } + } else { + pr_info("%s: failed to get mixer ctl for %s.\n", + __func__, DSP_BIT_WIDTH_MIXER_CTL); + } + + if ((prtd->codec_param.codec.format == SNDRV_PCM_FORMAT_S24_LE) || + (prtd->codec_param.codec.format == SNDRV_PCM_FORMAT_S24_3LE)) + bits_per_sample = 24; + else if (prtd->codec_param.codec.format == SNDRV_PCM_FORMAT_S32_LE) + bits_per_sample = 32; + + if (prtd->compr_passthr != LEGACY_PCM) { + ret = q6asm_open_write_compressed(ac, prtd->codec, + prtd->compr_passthr); + if (ret < 0) { + pr_err("%s:ASM open write err[%d] for compr_type[%d]\n", + __func__, ret, prtd->compr_passthr); + return ret; + } + prtd->gapless_state.stream_opened[stream_index] = 1; + + ret = msm_pcm_routing_reg_phy_compr_stream( + soc_prtd->dai_link->id, + ac->perf_mode, + prtd->session_id, + SNDRV_PCM_STREAM_PLAYBACK, + prtd->compr_passthr); + if (ret) { + pr_err("%s: compr stream reg failed:%d\n", __func__, + ret); + return ret; + } + } else { + pr_debug("%s: stream_id %d bits_per_sample %d\n", + __func__, ac->stream_id, bits_per_sample); + + if (q6core_get_avcs_api_version_per_service( + APRV2_IDS_SERVICE_ID_ADSP_ASM_V) >= + ADSP_ASM_API_VERSION_V2) + ret = q6asm_stream_open_write_v5(ac, + prtd->codec, bits_per_sample, + ac->stream_id, + prtd->gapless_state.use_dsp_gapless_mode); + else + ret = q6asm_stream_open_write_v4(ac, + prtd->codec, bits_per_sample, + ac->stream_id, + prtd->gapless_state.use_dsp_gapless_mode); + if (ret < 0) { + pr_err("%s:ASM open write err[%d] for compr type[%d]\n", + __func__, ret, prtd->compr_passthr); + return -ENOMEM; + } + prtd->gapless_state.stream_opened[stream_index] = 1; + + pr_debug("%s: BE id %d\n", __func__, soc_prtd->dai_link->id); + ret = msm_pcm_routing_reg_phy_stream(soc_prtd->dai_link->id, + ac->perf_mode, + prtd->session_id, + SNDRV_PCM_STREAM_PLAYBACK); + if (ret) { + pr_err("%s: stream reg failed:%d\n", __func__, ret); + return ret; + } + } + + ret = msm_compr_set_volume(cstream, 0, 0); + if (ret < 0) + pr_err("%s : Set Volume failed : %d", __func__, ret); + + if (prtd->compr_passthr != LEGACY_PCM) { + pr_debug("%s : Don't send cal and PP params for compress path", + __func__); + } else { + ret = q6asm_send_cal(ac); + if (ret < 0) + pr_debug("%s : Send cal failed : %d", __func__, ret); + + ret = q6asm_set_softpause(ac, &softpause); + if (ret < 0) + pr_err("%s: Send SoftPause Param failed ret=%d\n", + __func__, ret); + + ret = q6asm_set_softvolume(ac, &softvol); + if (ret < 0) + pr_err("%s: Send SoftVolume Param failed ret=%d\n", + __func__, ret); + } + ret = q6asm_set_io_mode(ac, (COMPRESSED_STREAM_IO | ASYNC_IO_MODE)); + if (ret < 0) { + pr_err("%s: Set IO mode failed\n", __func__); + return -EINVAL; + } + + runtime->fragments = prtd->codec_param.buffer.fragments; + runtime->fragment_size = prtd->codec_param.buffer.fragment_size; + pr_debug("allocate %d buffers each of size %d\n", + runtime->fragments, + runtime->fragment_size); + ret = q6asm_audio_client_buf_alloc_contiguous(dir, ac, + runtime->fragment_size, + runtime->fragments); + if (ret < 0) { + pr_err("Audio Start: Buffer Allocation failed rc = %d\n", ret); + return -ENOMEM; + } + + prtd->byte_offset = 0; + prtd->copied_total = 0; + prtd->app_pointer = 0; + prtd->bytes_received = 0; + prtd->bytes_sent = 0; + prtd->buffer = ac->port[dir].buf[0].data; + prtd->buffer_paddr = ac->port[dir].buf[0].phys; + prtd->buffer_size = runtime->fragments * runtime->fragment_size; + + /* Bit-0 of flags represent timestamp mode */ + if (prtd->codec_param.codec.flags & COMPRESSED_TIMESTAMP_FLAG) + prtd->ts_header_offset = sizeof(struct snd_codec_metadata); + else + prtd->ts_header_offset = 0; + + ret = msm_compr_send_media_format_block(cstream, ac->stream_id, false); + if (ret < 0) + pr_err("%s, failed to send media format block\n", __func__); + + return ret; +} + +static int msm_compr_configure_dsp_for_capture(struct snd_compr_stream *cstream) +{ + struct snd_compr_runtime *runtime = cstream->runtime; + struct msm_compr_audio *prtd = runtime->private_data; + struct snd_soc_pcm_runtime *soc_prtd = cstream->private_data; + uint16_t bits_per_sample; + uint16_t sample_word_size; + int dir = OUT, ret = 0; + struct audio_client *ac = prtd->audio_client; + uint32_t stream_index; + uint32_t enc_cfg_id = ENC_CFG_ID_NONE; + + switch (prtd->codec_param.codec.format) { + case SNDRV_PCM_FORMAT_S24_LE: + bits_per_sample = 24; + sample_word_size = 32; + break; + case SNDRV_PCM_FORMAT_S24_3LE: + bits_per_sample = 24; + sample_word_size = 24; + break; + case SNDRV_PCM_FORMAT_S32_LE: + bits_per_sample = 32; + sample_word_size = 32; + break; + case SNDRV_PCM_FORMAT_S16_LE: + default: + bits_per_sample = 16; + sample_word_size = 16; + if (prtd->codec == FORMAT_BESPOKE) + enc_cfg_id = + prtd->codec_param.codec.options.generic.reserved[0]; + break; + } + + pr_debug("%s: stream_id %d bits_per_sample %d compr_passthr %d\n", + __func__, ac->stream_id, bits_per_sample, + prtd->compr_passthr); + + if (prtd->compr_passthr != LEGACY_PCM) { + ret = q6asm_open_read_compressed(prtd->audio_client, + prtd->codec, prtd->compr_passthr); + if (ret < 0) { + pr_err("%s:ASM open read err[%d] for compr_type[%d]\n", + __func__, ret, prtd->compr_passthr); + return ret; + } + + ret = msm_pcm_routing_reg_phy_compr_stream( + soc_prtd->dai_link->id, + ac->perf_mode, + prtd->session_id, + SNDRV_PCM_STREAM_CAPTURE, + prtd->compr_passthr); + if (ret) { + pr_err("%s: compr stream reg failed:%d\n", + __func__, ret); + return ret; + } + } else { + if (prtd->codec_param.codec.flags & COMPRESSED_TIMESTAMP_FLAG) { + ret = q6asm_open_read_v4(prtd->audio_client, + prtd->codec, + bits_per_sample, true, enc_cfg_id); + } else { + ret = q6asm_open_read_v4(prtd->audio_client, + prtd->codec, + bits_per_sample, false, enc_cfg_id); + } + if (ret < 0) { + pr_err("%s: q6asm_open_read failed:%d\n", + __func__, ret); + return ret; + } + + ret = msm_pcm_routing_reg_phy_stream(soc_prtd->dai_link->id, + ac->perf_mode, + prtd->session_id, + SNDRV_PCM_STREAM_CAPTURE); + if (ret) { + pr_err("%s: stream reg failed:%d\n", __func__, ret); + return ret; + } + } + + ret = q6asm_set_io_mode(ac, (COMPRESSED_STREAM_IO | ASYNC_IO_MODE)); + if (ret < 0) { + pr_err("%s: Set IO mode failed\n", __func__); + return -EINVAL; + } + + stream_index = STREAM_ARRAY_INDEX(ac->stream_id); + if (stream_index >= MAX_NUMBER_OF_STREAMS || stream_index < 0) { + pr_err("%s: Invalid stream index:%d", __func__, stream_index); + return -EINVAL; + } + + runtime->fragments = prtd->codec_param.buffer.fragments; + runtime->fragment_size = prtd->codec_param.buffer.fragment_size; + pr_debug("%s: allocate %d buffers each of size %d\n", + __func__, runtime->fragments, + runtime->fragment_size); + ret = q6asm_audio_client_buf_alloc_contiguous(dir, ac, + runtime->fragment_size, + runtime->fragments); + if (ret < 0) { + pr_err("Audio Start: Buffer Allocation failed rc = %d\n", ret); + return -ENOMEM; + } + + prtd->byte_offset = 0; + prtd->received_total = 0; + prtd->app_pointer = 0; + prtd->bytes_copied = 0; + prtd->bytes_read = 0; + prtd->bytes_read_offset = 0; + prtd->buffer = ac->port[dir].buf[0].data; + prtd->buffer_paddr = ac->port[dir].buf[0].phys; + prtd->buffer_size = runtime->fragments * runtime->fragment_size; + + /* Bit-0 of flags represent timestamp mode */ + if (prtd->codec_param.codec.flags & COMPRESSED_TIMESTAMP_FLAG) + prtd->ts_header_offset = sizeof(struct snd_codec_metadata); + else + prtd->ts_header_offset = 0; + + pr_debug("%s: sample_rate = %d channels = %d bps = %d sample_word_size = %d\n", + __func__, prtd->sample_rate, prtd->num_channels, + bits_per_sample, sample_word_size); + if (prtd->codec == FORMAT_BESPOKE) { + /* + * For BESPOKE codec, encoder specific config params are + * included as part of generic. + */ + ret = q6asm_enc_cfg_blk_custom(prtd->audio_client, prtd->sample_rate, + prtd->num_channels, prtd->codec, + (void *)&prtd->codec_param.codec.options.generic); + } else if (prtd->compr_passthr == LEGACY_PCM) { + ret = q6asm_enc_cfg_blk_pcm_format_support_v4(prtd->audio_client, + prtd->sample_rate, prtd->num_channels, + bits_per_sample, sample_word_size, + ASM_LITTLE_ENDIAN, DEFAULT_QF); + } + + return ret; +} + +static int msm_compr_playback_open(struct snd_compr_stream *cstream) +{ + struct snd_compr_runtime *runtime = cstream->runtime; + struct snd_soc_pcm_runtime *rtd = cstream->private_data; + struct msm_compr_audio *prtd = NULL; + struct msm_compr_pdata *pdata = + snd_soc_platform_get_drvdata(rtd->platform); + + pr_debug("%s\n", __func__); + if (pdata->is_in_use[rtd->dai_link->id] == true) { + pr_err("%s: %s is already in use, err: %d\n", + __func__, rtd->dai_link->cpu_dai_name, -EBUSY); + return -EBUSY; + } + prtd = kzalloc(sizeof(struct msm_compr_audio), GFP_KERNEL); + if (prtd == NULL) { + pr_err("Failed to allocate memory for msm_compr_audio\n"); + return -ENOMEM; + } + + runtime->private_data = NULL; + prtd->cstream = cstream; + pdata->cstream[rtd->dai_link->id] = cstream; + pdata->audio_effects[rtd->dai_link->id] = + kzalloc(sizeof(struct msm_compr_audio_effects), GFP_KERNEL); + if (pdata->audio_effects[rtd->dai_link->id] == NULL) { + pr_err("%s: Could not allocate memory for effects\n", __func__); + pdata->cstream[rtd->dai_link->id] = NULL; + kfree(prtd); + return -ENOMEM; + } + pdata->dec_params[rtd->dai_link->id] = + kzalloc(sizeof(struct msm_compr_dec_params), GFP_KERNEL); + if (pdata->dec_params[rtd->dai_link->id] == NULL) { + pr_err("%s: Could not allocate memory for dec params\n", + __func__); + kfree(pdata->audio_effects[rtd->dai_link->id]); + pdata->audio_effects[rtd->dai_link->id] = NULL; + pdata->cstream[rtd->dai_link->id] = NULL; + kfree(prtd); + return -ENOMEM; + } + prtd->codec = FORMAT_MP3; + prtd->bytes_received = 0; + prtd->bytes_sent = 0; + prtd->copied_total = 0; + prtd->byte_offset = 0; + prtd->sample_rate = 44100; + prtd->num_channels = 2; + prtd->drain_ready = 0; + prtd->last_buffer = 0; + prtd->first_buffer = 1; + prtd->partial_drain_delay = 0; + prtd->next_stream = 0; + memset(&prtd->gapless_state, 0, sizeof(struct msm_compr_gapless_state)); + /* + * Update the use_dsp_gapless_mode from gapless struture with the value + * part of platform data. + */ + prtd->gapless_state.use_dsp_gapless_mode = pdata->use_dsp_gapless_mode; + + pr_debug("%s: gapless mode %d", __func__, pdata->use_dsp_gapless_mode); + + spin_lock_init(&prtd->lock); + + atomic_set(&prtd->eos, 0); + atomic_set(&prtd->start, 0); + atomic_set(&prtd->drain, 0); + atomic_set(&prtd->xrun, 0); + atomic_set(&prtd->close, 0); + atomic_set(&prtd->wait_on_close, 0); + atomic_set(&prtd->error, 0); + + init_waitqueue_head(&prtd->eos_wait); + init_waitqueue_head(&prtd->drain_wait); + init_waitqueue_head(&prtd->close_wait); + init_waitqueue_head(&prtd->wait_for_stream_avail); + + runtime->private_data = prtd; + populate_codec_list(prtd); + prtd->audio_client = q6asm_audio_client_alloc( + (app_cb)compr_event_handler, prtd); + if (prtd->audio_client == NULL) { + pr_err("%s: Could not allocate memory for client\n", __func__); + kfree(pdata->audio_effects[rtd->dai_link->id]); + pdata->audio_effects[rtd->dai_link->id] = NULL; + kfree(pdata->dec_params[rtd->dai_link->id]); + pdata->dec_params[rtd->dai_link->id] = NULL; + pdata->cstream[rtd->dai_link->id] = NULL; + kfree(prtd); + runtime->private_data = NULL; + return -ENOMEM; + } + pr_debug("%s: session ID %d\n", __func__, prtd->audio_client->session); + prtd->audio_client->perf_mode = false; + prtd->session_id = prtd->audio_client->session; + msm_adsp_init_mixer_ctl_pp_event_queue(rtd); + pdata->is_in_use[rtd->dai_link->id] = true; + return 0; +} + +static int msm_compr_capture_open(struct snd_compr_stream *cstream) +{ + struct snd_compr_runtime *runtime = cstream->runtime; + struct snd_soc_pcm_runtime *rtd = cstream->private_data; + struct msm_compr_audio *prtd; + struct msm_compr_pdata *pdata = + snd_soc_platform_get_drvdata(rtd->platform); + + pr_debug("%s\n", __func__); + prtd = kzalloc(sizeof(struct msm_compr_audio), GFP_KERNEL); + if (prtd == NULL) { + pr_err("Failed to allocate memory for msm_compr_audio\n"); + return -ENOMEM; + } + + runtime->private_data = NULL; + prtd->cstream = cstream; + pdata->cstream[rtd->dai_link->id] = cstream; + + prtd->audio_client = q6asm_audio_client_alloc( + (app_cb)compr_event_handler, prtd); + if (!prtd->audio_client) { + pr_err("%s: Could not allocate memory for client\n", __func__); + pdata->cstream[rtd->dai_link->id] = NULL; + kfree(prtd); + return -ENOMEM; + } + pr_debug("%s: session ID %d\n", __func__, prtd->audio_client->session); + prtd->audio_client->perf_mode = false; + prtd->session_id = prtd->audio_client->session; + prtd->codec = FORMAT_LINEAR_PCM; + prtd->bytes_copied = 0; + prtd->bytes_read = 0; + prtd->bytes_read_offset = 0; + prtd->received_total = 0; + prtd->byte_offset = 0; + prtd->sample_rate = 48000; + prtd->num_channels = 2; + prtd->first_buffer = 0; + + spin_lock_init(&prtd->lock); + + atomic_set(&prtd->eos, 0); + atomic_set(&prtd->start, 0); + atomic_set(&prtd->drain, 0); + atomic_set(&prtd->xrun, 0); + atomic_set(&prtd->close, 0); + atomic_set(&prtd->wait_on_close, 0); + atomic_set(&prtd->error, 0); + + runtime->private_data = prtd; + + return 0; +} + +static int msm_compr_open(struct snd_compr_stream *cstream) +{ + int ret = 0; + + if (cstream->direction == SND_COMPRESS_PLAYBACK) + ret = msm_compr_playback_open(cstream); + else if (cstream->direction == SND_COMPRESS_CAPTURE) + ret = msm_compr_capture_open(cstream); + return ret; +} + +static int msm_compr_playback_free(struct snd_compr_stream *cstream) +{ + struct snd_compr_runtime *runtime; + struct msm_compr_audio *prtd; + struct snd_soc_pcm_runtime *soc_prtd; + struct msm_compr_pdata *pdata; + struct audio_client *ac; + int dir = IN, ret = 0, stream_id; + unsigned long flags; + uint32_t stream_index; + + pr_debug("%s\n", __func__); + + if (!cstream) { + pr_err("%s cstream is null\n", __func__); + return 0; + } + runtime = cstream->runtime; + soc_prtd = cstream->private_data; + if (!runtime || !soc_prtd || !(soc_prtd->platform)) { + pr_err("%s runtime or soc_prtd or platform is null\n", + __func__); + return 0; + } + prtd = runtime->private_data; + if (!prtd) { + pr_err("%s prtd is null\n", __func__); + return 0; + } + prtd->cmd_interrupt = 1; + wake_up(&prtd->drain_wait); + pdata = snd_soc_platform_get_drvdata(soc_prtd->platform); + ac = prtd->audio_client; + if (!pdata || !ac) { + pr_err("%s pdata or ac is null\n", __func__); + return 0; + } + if (atomic_read(&prtd->eos)) { + ret = wait_event_timeout(prtd->eos_wait, + prtd->eos_ack, + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) + pr_err("%s: CMD_EOS failed\n", __func__); + } + if (atomic_read(&prtd->close)) { + prtd->cmd_ack = 0; + atomic_set(&prtd->wait_on_close, 1); + ret = wait_event_timeout(prtd->close_wait, + prtd->cmd_ack, + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) + pr_err("%s: CMD_CLOSE failed\n", __func__); + } + + spin_lock_irqsave(&prtd->lock, flags); + stream_id = ac->stream_id; + stream_index = STREAM_ARRAY_INDEX(NEXT_STREAM_ID(stream_id)); + + if ((stream_index < MAX_NUMBER_OF_STREAMS && stream_index >= 0) && + (prtd->gapless_state.stream_opened[stream_index])) { + prtd->gapless_state.stream_opened[stream_index] = 0; + spin_unlock_irqrestore(&prtd->lock, flags); + pr_debug(" close stream %d", NEXT_STREAM_ID(stream_id)); + q6asm_stream_cmd(ac, CMD_CLOSE, NEXT_STREAM_ID(stream_id)); + spin_lock_irqsave(&prtd->lock, flags); + } + + stream_index = STREAM_ARRAY_INDEX(stream_id); + if ((stream_index < MAX_NUMBER_OF_STREAMS && stream_index >= 0) && + (prtd->gapless_state.stream_opened[stream_index])) { + prtd->gapless_state.stream_opened[stream_index] = 0; + spin_unlock_irqrestore(&prtd->lock, flags); + pr_debug("close stream %d", stream_id); + q6asm_stream_cmd(ac, CMD_CLOSE, stream_id); + spin_lock_irqsave(&prtd->lock, flags); + } + spin_unlock_irqrestore(&prtd->lock, flags); + + pdata->cstream[soc_prtd->dai_link->id] = NULL; + if (cstream->direction == SND_COMPRESS_PLAYBACK) { + msm_pcm_routing_dereg_phy_stream(soc_prtd->dai_link->id, + SNDRV_PCM_STREAM_PLAYBACK); + } + + q6asm_audio_client_buf_free_contiguous(dir, ac); + + q6asm_audio_client_free(ac); + msm_adsp_clean_mixer_ctl_pp_event_queue(soc_prtd); + if (pdata->audio_effects[soc_prtd->dai_link->id] != NULL) { + kfree(pdata->audio_effects[soc_prtd->dai_link->id]); + pdata->audio_effects[soc_prtd->dai_link->id] = NULL; + } + if (pdata->dec_params[soc_prtd->dai_link->id] != NULL) { + kfree(pdata->dec_params[soc_prtd->dai_link->id]); + pdata->dec_params[soc_prtd->dai_link->id] = NULL; + } + pdata->is_in_use[soc_prtd->dai_link->id] = false; + kfree(prtd); + runtime->private_data = NULL; + + return 0; +} + +static int msm_compr_capture_free(struct snd_compr_stream *cstream) +{ + struct snd_compr_runtime *runtime; + struct msm_compr_audio *prtd; + struct snd_soc_pcm_runtime *soc_prtd; + struct msm_compr_pdata *pdata; + struct audio_client *ac; + int dir = OUT, stream_id; + unsigned long flags; + uint32_t stream_index; + + if (!cstream) { + pr_err("%s cstream is null\n", __func__); + return 0; + } + runtime = cstream->runtime; + soc_prtd = cstream->private_data; + if (!runtime || !soc_prtd || !(soc_prtd->platform)) { + pr_err("%s runtime or soc_prtd or platform is null\n", + __func__); + return 0; + } + prtd = runtime->private_data; + if (!prtd) { + pr_err("%s prtd is null\n", __func__); + return 0; + } + pdata = snd_soc_platform_get_drvdata(soc_prtd->platform); + ac = prtd->audio_client; + if (!pdata || !ac) { + pr_err("%s pdata or ac is null\n", __func__); + return 0; + } + + spin_lock_irqsave(&prtd->lock, flags); + stream_id = ac->stream_id; + + stream_index = STREAM_ARRAY_INDEX(stream_id); + if ((stream_index < MAX_NUMBER_OF_STREAMS && stream_index >= 0)) { + spin_unlock_irqrestore(&prtd->lock, flags); + pr_debug("close stream %d", stream_id); + q6asm_stream_cmd(ac, CMD_CLOSE, stream_id); + spin_lock_irqsave(&prtd->lock, flags); + } + spin_unlock_irqrestore(&prtd->lock, flags); + + pdata->cstream[soc_prtd->dai_link->id] = NULL; + msm_pcm_routing_dereg_phy_stream(soc_prtd->dai_link->id, + SNDRV_PCM_STREAM_CAPTURE); + + q6asm_audio_client_buf_free_contiguous(dir, ac); + + q6asm_audio_client_free(ac); + + kfree(prtd); + runtime->private_data = NULL; + + return 0; +} + +static int msm_compr_free(struct snd_compr_stream *cstream) +{ + int ret = 0; + + if (cstream->direction == SND_COMPRESS_PLAYBACK) + ret = msm_compr_playback_free(cstream); + else if (cstream->direction == SND_COMPRESS_CAPTURE) + ret = msm_compr_capture_free(cstream); + return ret; +} + +static bool msm_compr_validate_codec_compr(__u32 codec_id) +{ + int32_t i; + + for (i = 0; i < ARRAY_SIZE(compr_codecs); i++) { + if (compr_codecs[i] == codec_id) + return true; + } + return false; +} + +/* compress stream operations */ +static int msm_compr_set_params(struct snd_compr_stream *cstream, + struct snd_compr_params *params) +{ + struct snd_compr_runtime *runtime = cstream->runtime; + struct msm_compr_audio *prtd = runtime->private_data; + int ret = 0, frame_sz = 0; + int i, num_rates; + bool is_format_gapless = false; + + pr_debug("%s\n", __func__); + + num_rates = sizeof(supported_sample_rates)/sizeof(unsigned int); + for (i = 0; i < num_rates; i++) + if (params->codec.sample_rate == supported_sample_rates[i]) + break; + if (i == num_rates) + return -EINVAL; + + memcpy(&prtd->codec_param, params, sizeof(struct snd_compr_params)); + /* ToDo: remove duplicates */ + prtd->num_channels = prtd->codec_param.codec.ch_in; + prtd->sample_rate = prtd->codec_param.codec.sample_rate; + pr_debug("%s: sample_rate %d\n", __func__, prtd->sample_rate); + + if ((prtd->codec_param.codec.compr_passthr >= LEGACY_PCM && + prtd->codec_param. + codec.compr_passthr <= COMPRESSED_PASSTHROUGH_DSD) || + (prtd->codec_param. + codec.compr_passthr == COMPRESSED_PASSTHROUGH_IEC61937)) + prtd->compr_passthr = prtd->codec_param.codec.compr_passthr; + else + prtd->compr_passthr = LEGACY_PCM; + pr_debug("%s: compr_passthr = %d", __func__, prtd->compr_passthr); + if (prtd->compr_passthr != LEGACY_PCM) { + pr_debug("%s: Reset gapless mode playback for compr_type[%d]\n", + __func__, prtd->compr_passthr); + prtd->gapless_state.use_dsp_gapless_mode = 0; + if (!msm_compr_validate_codec_compr(params->codec.id)) { + pr_err("%s codec not supported in passthrough,id =%d\n", + __func__, params->codec.id); + return -EINVAL; + } + } + + switch (params->codec.id) { + case SND_AUDIOCODEC_PCM: { + pr_debug("SND_AUDIOCODEC_PCM\n"); + prtd->codec = FORMAT_LINEAR_PCM; + is_format_gapless = true; + break; + } + + case SND_AUDIOCODEC_MP3: { + pr_debug("SND_AUDIOCODEC_MP3\n"); + prtd->codec = FORMAT_MP3; + frame_sz = MP3_OUTPUT_FRAME_SZ; + is_format_gapless = true; + break; + } + + case SND_AUDIOCODEC_AAC: { + pr_debug("SND_AUDIOCODEC_AAC\n"); + prtd->codec = FORMAT_MPEG4_AAC; + frame_sz = AAC_OUTPUT_FRAME_SZ; + is_format_gapless = true; + break; + } + + case SND_AUDIOCODEC_AC3: { + pr_debug("SND_AUDIOCODEC_AC3\n"); + prtd->codec = FORMAT_AC3; + frame_sz = AC3_OUTPUT_FRAME_SZ; + is_format_gapless = true; + break; + } + + case SND_AUDIOCODEC_EAC3: { + pr_debug("SND_AUDIOCODEC_EAC3\n"); + prtd->codec = FORMAT_EAC3; + frame_sz = EAC3_OUTPUT_FRAME_SZ; + is_format_gapless = true; + break; + } + + case SND_AUDIOCODEC_MP2: { + pr_debug("SND_AUDIOCODEC_MP2\n"); + prtd->codec = FORMAT_MP2; + break; + } + + case SND_AUDIOCODEC_WMA: { + pr_debug("SND_AUDIOCODEC_WMA\n"); + prtd->codec = FORMAT_WMA_V9; + break; + } + + case SND_AUDIOCODEC_WMA_PRO: { + pr_debug("SND_AUDIOCODEC_WMA_PRO\n"); + prtd->codec = FORMAT_WMA_V10PRO; + break; + } + + case SND_AUDIOCODEC_FLAC: { + pr_debug("%s: SND_AUDIOCODEC_FLAC\n", __func__); + prtd->codec = FORMAT_FLAC; + /* + * DSP bufferring is based on blk size, + * consider mininum buffering to rule out any false wait + */ + frame_sz = + prtd->codec_param.codec.options.flac_dec.min_blk_size; + is_format_gapless = true; + break; + } + + case SND_AUDIOCODEC_VORBIS: { + pr_debug("%s: SND_AUDIOCODEC_VORBIS\n", __func__); + prtd->codec = FORMAT_VORBIS; + break; + } + + case SND_AUDIOCODEC_ALAC: { + pr_debug("%s: SND_AUDIOCODEC_ALAC\n", __func__); + prtd->codec = FORMAT_ALAC; + break; + } + + case SND_AUDIOCODEC_APE: { + pr_debug("%s: SND_AUDIOCODEC_APE\n", __func__); + prtd->codec = FORMAT_APE; + break; + } + + case SND_AUDIOCODEC_DTS: { + pr_debug("%s: SND_AUDIOCODEC_DTS\n", __func__); + prtd->codec = FORMAT_DTS; + break; + } + + case SND_AUDIOCODEC_DSD: { + pr_debug("%s: SND_AUDIOCODEC_DSD\n", __func__); + prtd->codec = FORMAT_DSD; + break; + } + + case SND_AUDIOCODEC_TRUEHD: { + pr_debug("%s: SND_AUDIOCODEC_TRUEHD\n", __func__); + prtd->codec = FORMAT_TRUEHD; + break; + } + + case SND_AUDIOCODEC_IEC61937: { + pr_debug("%s: SND_AUDIOCODEC_IEC61937\n", __func__); + prtd->codec = FORMAT_IEC61937; + break; + } + + case SND_AUDIOCODEC_APTX: { + pr_debug("%s: SND_AUDIOCODEC_APTX\n", __func__); + prtd->codec = FORMAT_APTX; + break; + } + + case SND_AUDIOCODEC_BESPOKE: { + pr_debug("%s: SND_AUDIOCODEC_BESPOKE\n", __func__); + prtd->codec = FORMAT_BESPOKE; + break; + } + + default: + pr_err("codec not supported, id =%d\n", params->codec.id); + return -EINVAL; + } + + if (!is_format_gapless) + prtd->gapless_state.use_dsp_gapless_mode = false; + + prtd->partial_drain_delay = + msm_compr_get_partial_drain_delay(frame_sz, prtd->sample_rate); + + if (cstream->direction == SND_COMPRESS_PLAYBACK) + ret = msm_compr_configure_dsp_for_playback(cstream); + else if (cstream->direction == SND_COMPRESS_CAPTURE) + ret = msm_compr_configure_dsp_for_capture(cstream); + + return ret; +} + +static int msm_compr_drain_buffer(struct msm_compr_audio *prtd, + unsigned long *flags) +{ + int rc = 0; + + atomic_set(&prtd->drain, 1); + prtd->drain_ready = 0; + spin_unlock_irqrestore(&prtd->lock, *flags); + pr_debug("%s: wait for buffer to be drained\n", __func__); + rc = wait_event_interruptible(prtd->drain_wait, + prtd->drain_ready || + prtd->cmd_interrupt || + atomic_read(&prtd->xrun) || + atomic_read(&prtd->error)); + pr_debug("%s: out of buffer drain wait with ret %d\n", __func__, rc); + spin_lock_irqsave(&prtd->lock, *flags); + if (prtd->cmd_interrupt) { + pr_debug("%s: buffer drain interrupted by flush)\n", __func__); + rc = -EINTR; + prtd->cmd_interrupt = 0; + } + if (atomic_read(&prtd->error)) { + pr_err("%s: Got RESET EVENTS notification, return\n", + __func__); + rc = -ENETRESET; + } + return rc; +} + +static int msm_compr_wait_for_stream_avail(struct msm_compr_audio *prtd, + unsigned long *flags) +{ + int rc = 0; + + pr_debug("next session is already in opened state\n"); + prtd->next_stream = 1; + prtd->cmd_interrupt = 0; + spin_unlock_irqrestore(&prtd->lock, *flags); + /* + * Wait for stream to be available, or the wait to be interrupted by + * commands like flush or till a timeout of one second. + */ + rc = wait_event_timeout(prtd->wait_for_stream_avail, + prtd->stream_available || prtd->cmd_interrupt, 1 * HZ); + pr_err("%s:prtd->stream_available %d, prtd->cmd_interrupt %d rc %d\n", + __func__, prtd->stream_available, prtd->cmd_interrupt, rc); + + spin_lock_irqsave(&prtd->lock, *flags); + if (rc == 0) { + pr_err("%s: wait_for_stream_avail timed out\n", + __func__); + rc = -ETIMEDOUT; + } else if (prtd->cmd_interrupt == 1) { + /* + * This scenario might not happen as we do not allow + * flush in transition state. + */ + pr_debug("%s: wait_for_stream_avail interrupted\n", __func__); + prtd->cmd_interrupt = 0; + prtd->stream_available = 0; + rc = -EINTR; + } else { + prtd->stream_available = 0; + rc = 0; + } + pr_debug("%s : rc = %d", __func__, rc); + return rc; +} + +static int msm_compr_trigger(struct snd_compr_stream *cstream, int cmd) +{ + struct snd_compr_runtime *runtime = cstream->runtime; + struct msm_compr_audio *prtd = runtime->private_data; + struct snd_soc_pcm_runtime *rtd = cstream->private_data; + struct msm_compr_pdata *pdata = + snd_soc_platform_get_drvdata(rtd->platform); + uint32_t *volume = pdata->volume[rtd->dai_link->id]; + struct audio_client *ac = prtd->audio_client; + unsigned long fe_id = rtd->dai_link->id; + int rc = 0; + int bytes_to_write; + unsigned long flags; + int stream_id; + uint32_t stream_index; + uint16_t bits_per_sample = 16; + + spin_lock_irqsave(&prtd->lock, flags); + if (atomic_read(&prtd->error)) { + pr_err("%s Got RESET EVENTS notification, return immediately", + __func__); + spin_unlock_irqrestore(&prtd->lock, flags); + return 0; + } + spin_unlock_irqrestore(&prtd->lock, flags); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + pr_debug("%s: SNDRV_PCM_TRIGGER_START\n", __func__); + atomic_set(&prtd->start, 1); + + /* + * compr_set_volume and compr_init_pp_params + * are used to configure ASM volume hence not + * needed for compress passthrough playback. + * + * compress passthrough volume is controlled in + * ADM by adm_send_compressed_device_mute() + */ + if (prtd->compr_passthr == LEGACY_PCM && + cstream->direction == SND_COMPRESS_PLAYBACK) { + /* set volume for the stream before RUN */ + rc = msm_compr_set_volume(cstream, + volume[0], volume[1]); + if (rc) + pr_err("%s : Set Volume failed : %d\n", + __func__, rc); + + rc = msm_compr_init_pp_params(cstream, ac); + if (rc) + pr_err("%s : init PP params failed : %d\n", + __func__, rc); + } else { + msm_compr_read_buffer(prtd); + } + /* issue RUN command for the stream */ + q6asm_run_nowait(prtd->audio_client, prtd->run_mode, + prtd->start_delay_msw, prtd->start_delay_lsw); + break; + case SNDRV_PCM_TRIGGER_STOP: + spin_lock_irqsave(&prtd->lock, flags); + pr_debug("%s: SNDRV_PCM_TRIGGER_STOP transition %d\n", __func__, + prtd->gapless_state.gapless_transition); + stream_id = ac->stream_id; + atomic_set(&prtd->start, 0); + if (cstream->direction == SND_COMPRESS_CAPTURE) { + q6asm_cmd_nowait(prtd->audio_client, CMD_PAUSE); + atomic_set(&prtd->xrun, 0); + prtd->received_total = 0; + prtd->bytes_copied = 0; + prtd->bytes_read = 0; + prtd->bytes_read_offset = 0; + prtd->byte_offset = 0; + prtd->app_pointer = 0; + spin_unlock_irqrestore(&prtd->lock, flags); + break; + } + if (prtd->next_stream) { + pr_debug("%s: interrupt next track wait queues\n", + __func__); + prtd->cmd_interrupt = 1; + wake_up(&prtd->wait_for_stream_avail); + prtd->next_stream = 0; + } + if (atomic_read(&prtd->eos)) { + pr_debug("%s: interrupt eos wait queues", __func__); + /* + * Gapless playback does not wait for eos, do not set + * cmd_int and do not wake up eos_wait during gapless + * transition + */ + if (!prtd->gapless_state.gapless_transition) { + prtd->cmd_interrupt = 1; + wake_up(&prtd->eos_wait); + } + atomic_set(&prtd->eos, 0); + } + if (atomic_read(&prtd->drain)) { + pr_debug("%s: interrupt drain wait queues", __func__); + prtd->cmd_interrupt = 1; + prtd->drain_ready = 1; + wake_up(&prtd->drain_wait); + atomic_set(&prtd->drain, 0); + } + prtd->last_buffer = 0; + prtd->cmd_ack = 0; + if (!prtd->gapless_state.gapless_transition) { + pr_debug("issue CMD_FLUSH stream_id %d\n", stream_id); + spin_unlock_irqrestore(&prtd->lock, flags); + q6asm_stream_cmd( + prtd->audio_client, CMD_FLUSH, stream_id); + spin_lock_irqsave(&prtd->lock, flags); + } else { + prtd->first_buffer = 0; + } + /* FIXME. only reset if flush was successful */ + prtd->byte_offset = 0; + prtd->copied_total = 0; + prtd->app_pointer = 0; + prtd->bytes_received = 0; + prtd->bytes_sent = 0; + prtd->marker_timestamp = 0; + + atomic_set(&prtd->xrun, 0); + spin_unlock_irqrestore(&prtd->lock, flags); + break; + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + pr_debug("SNDRV_PCM_TRIGGER_PAUSE_PUSH transition %d\n", + prtd->gapless_state.gapless_transition); + if (!prtd->gapless_state.gapless_transition) { + pr_debug("issue CMD_PAUSE stream_id %d\n", + ac->stream_id); + q6asm_stream_cmd_nowait(ac, CMD_PAUSE, ac->stream_id); + atomic_set(&prtd->start, 0); + } + break; + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + pr_debug("SNDRV_PCM_TRIGGER_PAUSE_RELEASE transition %d\n", + prtd->gapless_state.gapless_transition); + if (!prtd->gapless_state.gapless_transition) { + atomic_set(&prtd->start, 1); + q6asm_run_nowait(prtd->audio_client, prtd->run_mode, + 0, 0); + } + break; + case SND_COMPR_TRIGGER_PARTIAL_DRAIN: + pr_debug("%s: SND_COMPR_TRIGGER_PARTIAL_DRAIN\n", __func__); + if (!prtd->gapless_state.use_dsp_gapless_mode) { + pr_debug("%s: set partial drain as drain\n", __func__); + cmd = SND_COMPR_TRIGGER_DRAIN; + } + case SND_COMPR_TRIGGER_DRAIN: + pr_debug("%s: SNDRV_COMPRESS_DRAIN\n", __func__); + /* Make sure all the data is sent to DSP before sending EOS */ + spin_lock_irqsave(&prtd->lock, flags); + + if (!atomic_read(&prtd->start)) { + pr_err("%s: stream is not in started state\n", + __func__); + rc = -EPERM; + spin_unlock_irqrestore(&prtd->lock, flags); + break; + } + if (prtd->bytes_received > prtd->copied_total) { + pr_debug("%s: wait till all the data is sent to dsp\n", + __func__); + rc = msm_compr_drain_buffer(prtd, &flags); + if (rc || !atomic_read(&prtd->start)) { + if (rc != -ENETRESET) + rc = -EINTR; + spin_unlock_irqrestore(&prtd->lock, flags); + break; + } + /* + * FIXME: Bug. + * Write(32767) + * Start + * Drain <- Indefinite wait + * sol1 : if (prtd->copied_total) then wait? + * sol2 : (prtd->cmd_interrupt || prtd->drain_ready || + * atomic_read(xrun) + */ + bytes_to_write = prtd->bytes_received + - prtd->copied_total; + WARN(bytes_to_write > runtime->fragment_size, + "last write %d cannot be > than fragment_size", + bytes_to_write); + + if (bytes_to_write > 0) { + pr_debug("%s: send %d partial bytes at the end", + __func__, bytes_to_write); + atomic_set(&prtd->xrun, 0); + prtd->last_buffer = 1; + msm_compr_send_buffer(prtd); + } + } + + if ((cmd == SND_COMPR_TRIGGER_PARTIAL_DRAIN) && + (prtd->gapless_state.set_next_stream_id)) { + /* wait for the last buffer to be returned */ + + if (prtd->last_buffer) { + pr_debug("%s: last buffer drain\n", __func__); + rc = msm_compr_drain_buffer(prtd, &flags); + if (rc || !atomic_read(&prtd->start)) { + spin_unlock_irqrestore(&prtd->lock, + flags); + break; + } + } + /* send EOS */ + prtd->eos_ack = 0; + atomic_set(&prtd->eos, 1); + pr_debug("issue CMD_EOS stream_id %d\n", ac->stream_id); + q6asm_stream_cmd_nowait(ac, CMD_EOS, ac->stream_id); + pr_info("PARTIAL DRAIN, do not wait for EOS ack\n"); + + /* send a zero length buffer */ + atomic_set(&prtd->xrun, 0); + msm_compr_send_buffer(prtd); + + /* wait for the zero length buffer to be returned */ + pr_debug("%s: zero length buffer drain\n", __func__); + rc = msm_compr_drain_buffer(prtd, &flags); + if (rc || !atomic_read(&prtd->start)) { + spin_unlock_irqrestore(&prtd->lock, flags); + break; + } + + /* sleep for additional duration partial drain */ + atomic_set(&prtd->drain, 1); + prtd->drain_ready = 0; + pr_debug("%s, additional sleep: %d\n", __func__, + prtd->partial_drain_delay); + spin_unlock_irqrestore(&prtd->lock, flags); + rc = wait_event_timeout(prtd->drain_wait, + prtd->drain_ready || prtd->cmd_interrupt, + msecs_to_jiffies(prtd->partial_drain_delay)); + pr_debug("%s: out of additional wait for low sample rate\n", + __func__); + spin_lock_irqsave(&prtd->lock, flags); + if (prtd->cmd_interrupt) { + pr_debug("%s: additional wait interrupted by flush)\n", + __func__); + rc = -EINTR; + prtd->cmd_interrupt = 0; + spin_unlock_irqrestore(&prtd->lock, flags); + break; + } + + /* move to next stream and reset vars */ + pr_debug("%s: Moving to next stream in gapless\n", + __func__); + ac->stream_id = NEXT_STREAM_ID(ac->stream_id); + prtd->byte_offset = 0; + prtd->app_pointer = 0; + prtd->first_buffer = 1; + prtd->last_buffer = 0; + /* + * Set gapless transition flag only if EOS hasn't been + * acknowledged already. + */ + if (atomic_read(&prtd->eos)) + prtd->gapless_state.gapless_transition = 1; + prtd->marker_timestamp = 0; + + /* + * Don't reset these as these vars map to + * total_bytes_transferred and total_bytes_available + * directly, only total_bytes_transferred will be + * updated in the next avail() ioctl + * prtd->copied_total = 0; + * prtd->bytes_received = 0; + */ + atomic_set(&prtd->drain, 0); + atomic_set(&prtd->xrun, 1); + pr_debug("%s: issue CMD_RUN", __func__); + q6asm_run_nowait(prtd->audio_client, 0, 0, 0); + spin_unlock_irqrestore(&prtd->lock, flags); + break; + } + /* + * moving to next stream failed, so reset the gapless state + * set next stream id for the same session so that the same + * stream can be used for gapless playback + */ + prtd->gapless_state.set_next_stream_id = false; + prtd->gapless_state.gapless_transition = 0; + pr_debug("%s:CMD_EOS stream_id %d\n", __func__, ac->stream_id); + + prtd->eos_ack = 0; + atomic_set(&prtd->eos, 1); + q6asm_stream_cmd_nowait(ac, CMD_EOS, ac->stream_id); + + spin_unlock_irqrestore(&prtd->lock, flags); + + + /* Wait indefinitely for DRAIN. Flush can also signal this*/ + rc = wait_event_interruptible(prtd->eos_wait, + (prtd->eos_ack || + prtd->cmd_interrupt || + atomic_read(&prtd->error))); + + if (rc < 0) + pr_err("%s: EOS wait failed\n", __func__); + + pr_debug("%s: SNDRV_COMPRESS_DRAIN out of wait for EOS\n", + __func__); + + if (prtd->cmd_interrupt) + rc = -EINTR; + + if (atomic_read(&prtd->error)) { + pr_err("%s: Got RESET EVENTS notification, return\n", + __func__); + rc = -ENETRESET; + } + + /*FIXME : what if a flush comes while PC is here */ + if (rc == 0) { + /* + * Failed to open second stream in DSP for gapless + * so prepare the current stream in session + * for gapless playback + */ + spin_lock_irqsave(&prtd->lock, flags); + pr_debug("%s:issue CMD_PAUSE stream_id %d", + __func__, ac->stream_id); + q6asm_stream_cmd_nowait(ac, CMD_PAUSE, ac->stream_id); + prtd->cmd_ack = 0; + spin_unlock_irqrestore(&prtd->lock, flags); + + /* + * Cache this time as last known time + */ + if (pdata->use_legacy_api) + q6asm_get_session_time_legacy( + prtd->audio_client, + &prtd->marker_timestamp); + else + q6asm_get_session_time(prtd->audio_client, + &prtd->marker_timestamp); + + spin_lock_irqsave(&prtd->lock, flags); + /* + * Don't reset these as these vars map to + * total_bytes_transferred and total_bytes_available. + * Just total_bytes_transferred will be updated + * in the next avail() ioctl. + * prtd->copied_total = 0; + * prtd->bytes_received = 0; + * do not reset prtd->bytes_sent as well as the same + * session is used for gapless playback + */ + prtd->byte_offset = 0; + + prtd->app_pointer = 0; + prtd->first_buffer = 1; + prtd->last_buffer = 0; + atomic_set(&prtd->drain, 0); + atomic_set(&prtd->xrun, 1); + spin_unlock_irqrestore(&prtd->lock, flags); + + pr_debug("%s:issue CMD_FLUSH ac->stream_id %d", + __func__, ac->stream_id); + q6asm_stream_cmd(ac, CMD_FLUSH, ac->stream_id); + + q6asm_run_nowait(prtd->audio_client, 0, 0, 0); + } + prtd->cmd_interrupt = 0; + break; + case SND_COMPR_TRIGGER_NEXT_TRACK: + if (!prtd->gapless_state.use_dsp_gapless_mode) { + pr_debug("%s: ignore trigger next track\n", __func__); + rc = 0; + break; + } + pr_debug("%s: SND_COMPR_TRIGGER_NEXT_TRACK\n", __func__); + spin_lock_irqsave(&prtd->lock, flags); + rc = 0; + /* next stream in gapless */ + stream_id = NEXT_STREAM_ID(ac->stream_id); + /* + * Wait if stream 1 has not completed before honoring next + * track for stream 3. Scenario happens if second clip is + * small and fills in one buffer so next track will be + * called immediately. + */ + stream_index = STREAM_ARRAY_INDEX(stream_id); + if (stream_index >= MAX_NUMBER_OF_STREAMS || + stream_index < 0) { + pr_err("%s: Invalid stream index: %d", __func__, + stream_index); + spin_unlock_irqrestore(&prtd->lock, flags); + rc = -EINVAL; + break; + } + + if (prtd->gapless_state.stream_opened[stream_index]) { + if (prtd->gapless_state.gapless_transition) { + rc = msm_compr_wait_for_stream_avail(prtd, + &flags); + } else { + /* + * If session is already opened break out if + * the state is not gapless transition. This + * is when seek happens after the last buffer + * is sent to the driver. Next track would be + * called again after last buffer is sent. + */ + pr_debug("next session is in opened state\n"); + spin_unlock_irqrestore(&prtd->lock, flags); + break; + } + } + spin_unlock_irqrestore(&prtd->lock, flags); + if (rc < 0) { + /* + * if return type EINTR then reset to zero. Tiny + * compress treats EINTR as error and prevents PARTIAL + * DRAIN. EINTR is not an error. wait for stream avail + * is interrupted by some other command like FLUSH. + */ + if (rc == -EINTR) { + pr_debug("%s: EINTR reset rc to 0\n", __func__); + rc = 0; + } + break; + } + + if (prtd->codec_param.codec.format == SNDRV_PCM_FORMAT_S24_LE) + bits_per_sample = 24; + else if (prtd->codec_param.codec.format == + SNDRV_PCM_FORMAT_S32_LE) + bits_per_sample = 32; + + pr_debug("%s: open_write stream_id %d bits_per_sample %d", + __func__, stream_id, bits_per_sample); + rc = q6asm_stream_open_write_v4(prtd->audio_client, + prtd->codec, bits_per_sample, + stream_id, + prtd->gapless_state.use_dsp_gapless_mode); + if (rc < 0) { + pr_err("%s: Session out open failed for gapless\n", + __func__); + break; + } + + spin_lock_irqsave(&prtd->lock, flags); + prtd->gapless_state.stream_opened[stream_index] = 1; + prtd->gapless_state.set_next_stream_id = true; + spin_unlock_irqrestore(&prtd->lock, flags); + + rc = msm_compr_send_media_format_block(cstream, + stream_id, false); + if (rc < 0) { + pr_err("%s, failed to send media format block\n", + __func__); + break; + } + msm_compr_send_dec_params(cstream, pdata->dec_params[fe_id], + stream_id); + break; + } + + return rc; +} + +static int msm_compr_pointer(struct snd_compr_stream *cstream, + struct snd_compr_tstamp *arg) +{ + struct snd_compr_runtime *runtime = cstream->runtime; + struct snd_soc_pcm_runtime *rtd = cstream->private_data; + struct msm_compr_audio *prtd = runtime->private_data; + struct msm_compr_pdata *pdata = NULL; + struct snd_compr_tstamp tstamp; + uint64_t timestamp = 0; + int rc = 0, first_buffer; + unsigned long flags; + uint32_t gapless_transition; + + pdata = snd_soc_platform_get_drvdata(rtd->platform); + pr_debug("%s\n", __func__); + memset(&tstamp, 0x0, sizeof(struct snd_compr_tstamp)); + + spin_lock_irqsave(&prtd->lock, flags); + tstamp.sampling_rate = prtd->sample_rate; + tstamp.byte_offset = prtd->byte_offset; + if (cstream->direction == SND_COMPRESS_PLAYBACK) + tstamp.copied_total = prtd->copied_total; + else if (cstream->direction == SND_COMPRESS_CAPTURE) + tstamp.copied_total = prtd->received_total; + first_buffer = prtd->first_buffer; + if (atomic_read(&prtd->error)) { + pr_err_ratelimited("%s Got RESET EVENTS notification, return error\n", + __func__); + if (cstream->direction == SND_COMPRESS_PLAYBACK) + runtime->total_bytes_transferred = tstamp.copied_total; + else + runtime->total_bytes_available = tstamp.copied_total; + tstamp.pcm_io_frames = 0; + memcpy(arg, &tstamp, sizeof(struct snd_compr_tstamp)); + spin_unlock_irqrestore(&prtd->lock, flags); + return -ENETRESET; + } + if (cstream->direction == SND_COMPRESS_PLAYBACK) { + + gapless_transition = prtd->gapless_state.gapless_transition; + spin_unlock_irqrestore(&prtd->lock, flags); + if (gapless_transition) + pr_debug("%s session time in gapless transition", + __func__); + /* + *- Do not query if no buffer has been given. + *- Do not query on a gapless transition. + * Playback for the 2nd stream can start (thus returning time + * starting from 0) before the driver knows about EOS of first + * stream. + */ + if (!first_buffer || gapless_transition) { + + if (pdata->use_legacy_api) + rc = q6asm_get_session_time_legacy( + prtd->audio_client, &prtd->marker_timestamp); + else + rc = q6asm_get_session_time( + prtd->audio_client, &prtd->marker_timestamp); + if (rc < 0) { + pr_err("%s: Get Session Time return =%lld\n", + __func__, timestamp); + if (atomic_read(&prtd->error)) + return -ENETRESET; + else + return -EAGAIN; + } + } + } else { + spin_unlock_irqrestore(&prtd->lock, flags); + } + timestamp = prtd->marker_timestamp; + + /* DSP returns timestamp in usec */ + pr_debug("%s: timestamp = %lld usec\n", __func__, timestamp); + timestamp *= prtd->sample_rate; + tstamp.pcm_io_frames = (snd_pcm_uframes_t)div64_u64(timestamp, 1000000); + memcpy(arg, &tstamp, sizeof(struct snd_compr_tstamp)); + + return 0; +} + +static int msm_compr_ack(struct snd_compr_stream *cstream, + size_t count) +{ + struct snd_compr_runtime *runtime = cstream->runtime; + struct msm_compr_audio *prtd = runtime->private_data; + void *src, *dstn; + size_t copy; + unsigned long flags; + + WARN(1, "This path is untested"); + return -EINVAL; + + pr_debug("%s: count = %zd\n", __func__, count); + if (!prtd->buffer) { + pr_err("%s: Buffer is not allocated yet ??\n", __func__); + return -EINVAL; + } + src = runtime->buffer + prtd->app_pointer; + dstn = prtd->buffer + prtd->app_pointer; + if (count < prtd->buffer_size - prtd->app_pointer) { + memcpy(dstn, src, count); + prtd->app_pointer += count; + } else { + copy = prtd->buffer_size - prtd->app_pointer; + memcpy(dstn, src, copy); + memcpy(prtd->buffer, runtime->buffer, count - copy); + prtd->app_pointer = count - copy; + } + + /* + * If the stream is started and all the bytes received were + * copied to DSP, the newly received bytes should be + * sent right away + */ + spin_lock_irqsave(&prtd->lock, flags); + + if (atomic_read(&prtd->start) && + prtd->bytes_received == prtd->copied_total) { + prtd->bytes_received += count; + msm_compr_send_buffer(prtd); + } else + prtd->bytes_received += count; + + spin_unlock_irqrestore(&prtd->lock, flags); + + return 0; +} + +static int msm_compr_playback_copy(struct snd_compr_stream *cstream, + char __user *buf, size_t count) +{ + struct snd_compr_runtime *runtime = cstream->runtime; + struct msm_compr_audio *prtd = runtime->private_data; + void *dstn; + size_t copy; + uint64_t bytes_available = 0; + unsigned long flags; + + pr_debug("%s: count = %zd\n", __func__, count); + if (!prtd->buffer) { + pr_err("%s: Buffer is not allocated yet ??", __func__); + return 0; + } + + spin_lock_irqsave(&prtd->lock, flags); + if (atomic_read(&prtd->error)) { + pr_err("%s Got RESET EVENTS notification", __func__); + spin_unlock_irqrestore(&prtd->lock, flags); + return -ENETRESET; + } + spin_unlock_irqrestore(&prtd->lock, flags); + + dstn = prtd->buffer + prtd->app_pointer; + if (count < prtd->buffer_size - prtd->app_pointer) { + if (copy_from_user(dstn, buf, count)) + return -EFAULT; + prtd->app_pointer += count; + } else { + copy = prtd->buffer_size - prtd->app_pointer; + if (copy_from_user(dstn, buf, copy)) + return -EFAULT; + if (copy_from_user(prtd->buffer, buf + copy, count - copy)) + return -EFAULT; + prtd->app_pointer = count - copy; + } + + /* + * If stream is started and there has been an xrun, + * since the available bytes fits fragment_size, copy the data + * right away. + */ + spin_lock_irqsave(&prtd->lock, flags); + prtd->bytes_received += count; + if (atomic_read(&prtd->start)) { + if (atomic_read(&prtd->xrun)) { + pr_debug("%s: in xrun, count = %zd\n", __func__, count); + bytes_available = prtd->bytes_received - + prtd->copied_total; + if (bytes_available >= runtime->fragment_size) { + pr_debug("%s: handle xrun, bytes_to_write = %llu\n", + __func__, bytes_available); + atomic_set(&prtd->xrun, 0); + msm_compr_send_buffer(prtd); + } /* else not sufficient data */ + } /* writes will continue on the next write_done */ + } + + spin_unlock_irqrestore(&prtd->lock, flags); + + return count; +} + +static int msm_compr_capture_copy(struct snd_compr_stream *cstream, + char __user *buf, size_t count) +{ + struct snd_compr_runtime *runtime = cstream->runtime; + struct msm_compr_audio *prtd = runtime->private_data; + void *source; + unsigned long flags; + + pr_debug("%s: count = %zd\n", __func__, count); + if (!prtd->buffer) { + pr_err("%s: Buffer is not allocated yet ??", __func__); + return 0; + } + + spin_lock_irqsave(&prtd->lock, flags); + if (atomic_read(&prtd->error)) { + pr_err("%s Got RESET EVENTS notification", __func__); + spin_unlock_irqrestore(&prtd->lock, flags); + return -ENETRESET; + } + + source = prtd->buffer + prtd->app_pointer; + /* check if we have requested amount of data to copy to user*/ + if (count <= prtd->received_total - prtd->bytes_copied) { + spin_unlock_irqrestore(&prtd->lock, flags); + if (copy_to_user(buf, source, count)) { + pr_err("copy_to_user failed"); + return -EFAULT; + } + spin_lock_irqsave(&prtd->lock, flags); + prtd->app_pointer += count; + if (prtd->app_pointer >= prtd->buffer_size) + prtd->app_pointer -= prtd->buffer_size; + prtd->bytes_copied += count; + } + msm_compr_read_buffer(prtd); + + spin_unlock_irqrestore(&prtd->lock, flags); + return count; +} + +static int msm_compr_copy(struct snd_compr_stream *cstream, + char __user *buf, size_t count) +{ + int ret = 0; + + pr_debug(" In %s\n", __func__); + if (cstream->direction == SND_COMPRESS_PLAYBACK) + ret = msm_compr_playback_copy(cstream, buf, count); + else if (cstream->direction == SND_COMPRESS_CAPTURE) + ret = msm_compr_capture_copy(cstream, buf, count); + return ret; +} + +static int msm_compr_get_caps(struct snd_compr_stream *cstream, + struct snd_compr_caps *arg) +{ + struct snd_compr_runtime *runtime = cstream->runtime; + struct msm_compr_audio *prtd = runtime->private_data; + int ret = 0; + + pr_debug("%s\n", __func__); + if ((arg != NULL) && (prtd != NULL)) { + memcpy(arg, &prtd->compr_cap, sizeof(struct snd_compr_caps)); + } else { + ret = -EINVAL; + pr_err("%s: arg (0x%pK), prtd (0x%pK)\n", __func__, arg, prtd); + } + + return ret; +} + +static int msm_compr_get_codec_caps(struct snd_compr_stream *cstream, + struct snd_compr_codec_caps *codec) +{ + pr_debug("%s\n", __func__); + + switch (codec->codec) { + case SND_AUDIOCODEC_MP3: + codec->num_descriptors = 2; + codec->descriptor[0].max_ch = 2; + memcpy(codec->descriptor[0].sample_rates, + supported_sample_rates, + sizeof(supported_sample_rates)); + codec->descriptor[0].num_sample_rates = + sizeof(supported_sample_rates)/sizeof(unsigned int); + codec->descriptor[0].bit_rate[0] = 320; /* 320kbps */ + codec->descriptor[0].bit_rate[1] = 128; + codec->descriptor[0].num_bitrates = 2; + codec->descriptor[0].profiles = 0; + codec->descriptor[0].modes = SND_AUDIOCHANMODE_MP3_STEREO; + codec->descriptor[0].formats = 0; + break; + case SND_AUDIOCODEC_AAC: + codec->num_descriptors = 2; + codec->descriptor[1].max_ch = 2; + memcpy(codec->descriptor[1].sample_rates, + supported_sample_rates, + sizeof(supported_sample_rates)); + codec->descriptor[1].num_sample_rates = + sizeof(supported_sample_rates)/sizeof(unsigned int); + codec->descriptor[1].bit_rate[0] = 320; /* 320kbps */ + codec->descriptor[1].bit_rate[1] = 128; + codec->descriptor[1].num_bitrates = 2; + codec->descriptor[1].profiles = 0; + codec->descriptor[1].modes = 0; + codec->descriptor[1].formats = + (SND_AUDIOSTREAMFORMAT_MP4ADTS | + SND_AUDIOSTREAMFORMAT_RAW); + break; + case SND_AUDIOCODEC_AC3: + case SND_AUDIOCODEC_EAC3: + case SND_AUDIOCODEC_FLAC: + case SND_AUDIOCODEC_VORBIS: + case SND_AUDIOCODEC_ALAC: + case SND_AUDIOCODEC_APE: + case SND_AUDIOCODEC_DTS: + case SND_AUDIOCODEC_DSD: + case SND_AUDIOCODEC_TRUEHD: + case SND_AUDIOCODEC_IEC61937: + case SND_AUDIOCODEC_APTX: + break; + default: + pr_err("%s: Unsupported audio codec %d\n", + __func__, codec->codec); + return -EINVAL; + } + + return 0; +} + +static int msm_compr_set_metadata(struct snd_compr_stream *cstream, + struct snd_compr_metadata *metadata) +{ + struct msm_compr_audio *prtd; + struct audio_client *ac; + pr_debug("%s\n", __func__); + + if (!metadata || !cstream) + return -EINVAL; + + prtd = cstream->runtime->private_data; + if (!prtd || !prtd->audio_client) { + pr_err("%s: prtd or audio client is NULL\n", __func__); + return -EINVAL; + } + + if (((metadata->key == SNDRV_COMPRESS_ENCODER_PADDING) || + (metadata->key == SNDRV_COMPRESS_ENCODER_DELAY)) && + (prtd->compr_passthr != LEGACY_PCM)) { + pr_debug("%s: No trailing silence for compress_type[%d]\n", + __func__, prtd->compr_passthr); + return 0; + } + + ac = prtd->audio_client; + if (metadata->key == SNDRV_COMPRESS_ENCODER_PADDING) { + pr_debug("%s, got encoder padding %u", + __func__, metadata->value[0]); + prtd->gapless_state.trailing_samples_drop = metadata->value[0]; + } else if (metadata->key == SNDRV_COMPRESS_ENCODER_DELAY) { + pr_debug("%s, got encoder delay %u", + __func__, metadata->value[0]); + prtd->gapless_state.initial_samples_drop = metadata->value[0]; + } else if (metadata->key == SNDRV_COMPRESS_RENDER_MODE) { + return msm_compr_set_render_mode(prtd, metadata->value[0]); + } else if (metadata->key == SNDRV_COMPRESS_CLK_REC_MODE) { + return msm_compr_set_clk_rec_mode(ac, metadata->value[0]); + } else if (metadata->key == SNDRV_COMPRESS_RENDER_WINDOW) { + return msm_compr_set_render_window( + ac, + metadata->value[0], + metadata->value[1], + metadata->value[2], + metadata->value[3]); + } else if (metadata->key == SNDRV_COMPRESS_START_DELAY) { + prtd->start_delay_lsw = metadata->value[0]; + prtd->start_delay_msw = metadata->value[1]; + } else if (metadata->key == + SNDRV_COMPRESS_ENABLE_ADJUST_SESSION_CLOCK) { + return msm_compr_enable_adjust_session_clock(ac, + metadata->value[0]); + } else if (metadata->key == SNDRV_COMPRESS_ADJUST_SESSION_CLOCK) { + return msm_compr_adjust_session_clock(ac, + metadata->value[0], + metadata->value[1]); + } + + return 0; +} + +static int msm_compr_get_metadata(struct snd_compr_stream *cstream, + struct snd_compr_metadata *metadata) +{ + struct msm_compr_audio *prtd; + struct audio_client *ac; + int ret = -EINVAL; + + pr_debug("%s\n", __func__); + + if (!metadata || !cstream || !cstream->runtime) + return ret; + + if (metadata->key != SNDRV_COMPRESS_PATH_DELAY) { + pr_err("%s, unsupported key %d\n", __func__, metadata->key); + return ret; + } + + prtd = cstream->runtime->private_data; + if (!prtd || !prtd->audio_client) { + pr_err("%s: prtd or audio client is NULL\n", __func__); + return ret; + } + + ac = prtd->audio_client; + ret = q6asm_get_path_delay(prtd->audio_client); + if (ret) { + pr_err("%s: get_path_delay failed, ret=%d\n", __func__, ret); + return ret; + } + + pr_debug("%s, path delay(in us) %u\n", __func__, ac->path_delay); + + metadata->value[0] = ac->path_delay; + + return ret; +} + + +static int msm_compr_set_next_track_param(struct snd_compr_stream *cstream, + union snd_codec_options *codec_options) +{ + struct msm_compr_audio *prtd; + struct audio_client *ac; + int ret = 0; + + if (!codec_options || !cstream) + return -EINVAL; + + prtd = cstream->runtime->private_data; + if (!prtd || !prtd->audio_client) { + pr_err("%s: prtd or audio client is NULL\n", __func__); + return -EINVAL; + } + + ac = prtd->audio_client; + + pr_debug("%s: got codec options for codec type %u", + __func__, prtd->codec); + switch (prtd->codec) { + case FORMAT_WMA_V9: + case FORMAT_WMA_V10PRO: + case FORMAT_FLAC: + case FORMAT_VORBIS: + case FORMAT_ALAC: + case FORMAT_APE: + memcpy(&(prtd->gapless_state.codec_options), + codec_options, + sizeof(union snd_codec_options)); + ret = msm_compr_send_media_format_block(cstream, + ac->stream_id, true); + if (ret < 0) { + pr_err("%s: failed to send media format block\n", + __func__); + } + break; + + default: + pr_debug("%s: Ignore sending CMD Format block\n", + __func__); + break; + } + + return ret; +} + +static int msm_compr_volume_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol); + unsigned long fe_id = kcontrol->private_value; + struct msm_compr_pdata *pdata = (struct msm_compr_pdata *) + snd_soc_component_get_drvdata(comp); + struct snd_compr_stream *cstream = NULL; + uint32_t *volume = NULL; + + if (fe_id >= MSM_FRONTEND_DAI_MAX) { + pr_err("%s Received out of bounds fe_id %lu\n", + __func__, fe_id); + return -EINVAL; + } + + cstream = pdata->cstream[fe_id]; + volume = pdata->volume[fe_id]; + + volume[0] = ucontrol->value.integer.value[0]; + volume[1] = ucontrol->value.integer.value[1]; + pr_debug("%s: fe_id %lu left_vol %d right_vol %d\n", + __func__, fe_id, volume[0], volume[1]); + if (cstream) + msm_compr_set_volume(cstream, volume[0], volume[1]); + return 0; +} + +static int msm_compr_volume_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol); + unsigned long fe_id = kcontrol->private_value; + + struct msm_compr_pdata *pdata = + snd_soc_component_get_drvdata(comp); + uint32_t *volume = NULL; + + if (fe_id >= MSM_FRONTEND_DAI_MAX) { + pr_err("%s Received out of bound fe_id %lu\n", __func__, fe_id); + return -EINVAL; + } + + volume = pdata->volume[fe_id]; + pr_debug("%s: fe_id %lu\n", __func__, fe_id); + ucontrol->value.integer.value[0] = volume[0]; + ucontrol->value.integer.value[1] = volume[1]; + + return 0; +} + +static int msm_compr_audio_effects_config_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol); + unsigned long fe_id = kcontrol->private_value; + struct msm_compr_pdata *pdata = (struct msm_compr_pdata *) + snd_soc_component_get_drvdata(comp); + struct msm_compr_audio_effects *audio_effects = NULL; + struct snd_compr_stream *cstream = NULL; + struct msm_compr_audio *prtd = NULL; + long *values = &(ucontrol->value.integer.value[0]); + int effects_module; + + pr_debug("%s\n", __func__); + if (fe_id >= MSM_FRONTEND_DAI_MAX) { + pr_err("%s Received out of bounds fe_id %lu\n", + __func__, fe_id); + return -EINVAL; + } + cstream = pdata->cstream[fe_id]; + audio_effects = pdata->audio_effects[fe_id]; + if (!cstream || !audio_effects) { + pr_err("%s: stream or effects inactive\n", __func__); + return -EINVAL; + } + prtd = cstream->runtime->private_data; + if (!prtd) { + pr_err("%s: cannot set audio effects\n", __func__); + return -EINVAL; + } + if (prtd->compr_passthr != LEGACY_PCM) { + pr_debug("%s: No effects for compr_type[%d]\n", + __func__, prtd->compr_passthr); + return 0; + } + pr_debug("%s: Effects supported for compr_type[%d]\n", + __func__, prtd->compr_passthr); + + effects_module = *values++; + switch (effects_module) { + case VIRTUALIZER_MODULE: + pr_debug("%s: VIRTUALIZER_MODULE\n", __func__); + if (msm_audio_effects_is_effmodule_supp_in_top(effects_module, + prtd->audio_client->topology)) + msm_audio_effects_virtualizer_handler( + prtd->audio_client, + &(audio_effects->virtualizer), + values); + break; + case REVERB_MODULE: + pr_debug("%s: REVERB_MODULE\n", __func__); + if (msm_audio_effects_is_effmodule_supp_in_top(effects_module, + prtd->audio_client->topology)) + msm_audio_effects_reverb_handler(prtd->audio_client, + &(audio_effects->reverb), + values); + break; + case BASS_BOOST_MODULE: + pr_debug("%s: BASS_BOOST_MODULE\n", __func__); + if (msm_audio_effects_is_effmodule_supp_in_top(effects_module, + prtd->audio_client->topology)) + msm_audio_effects_bass_boost_handler(prtd->audio_client, + &(audio_effects->bass_boost), + values); + break; + case PBE_MODULE: + pr_debug("%s: PBE_MODULE\n", __func__); + if (msm_audio_effects_is_effmodule_supp_in_top(effects_module, + prtd->audio_client->topology)) + msm_audio_effects_pbe_handler(prtd->audio_client, + &(audio_effects->pbe), + values); + break; + case EQ_MODULE: + pr_debug("%s: EQ_MODULE\n", __func__); + if (msm_audio_effects_is_effmodule_supp_in_top(effects_module, + prtd->audio_client->topology)) + msm_audio_effects_popless_eq_handler(prtd->audio_client, + &(audio_effects->equalizer), + values); + break; + case SOFT_VOLUME_MODULE: + pr_debug("%s: SOFT_VOLUME_MODULE\n", __func__); + break; + case SOFT_VOLUME2_MODULE: + pr_debug("%s: SOFT_VOLUME2_MODULE\n", __func__); + if (msm_audio_effects_is_effmodule_supp_in_top(effects_module, + prtd->audio_client->topology)) + msm_audio_effects_volume_handler_v2(prtd->audio_client, + &(audio_effects->volume), + values, SOFT_VOLUME_INSTANCE_2); + break; + default: + pr_err("%s Invalid effects config module\n", __func__); + return -EINVAL; + } + return 0; +} + +static int msm_compr_audio_effects_config_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol); + unsigned long fe_id = kcontrol->private_value; + struct msm_compr_pdata *pdata = (struct msm_compr_pdata *) + snd_soc_component_get_drvdata(comp); + struct msm_compr_audio_effects *audio_effects = NULL; + struct snd_compr_stream *cstream = NULL; + struct msm_compr_audio *prtd = NULL; + + pr_debug("%s\n", __func__); + if (fe_id >= MSM_FRONTEND_DAI_MAX) { + pr_err("%s Received out of bounds fe_id %lu\n", + __func__, fe_id); + return -EINVAL; + } + cstream = pdata->cstream[fe_id]; + audio_effects = pdata->audio_effects[fe_id]; + if (!cstream || !audio_effects) { + pr_debug("%s: stream or effects inactive\n", __func__); + return -EINVAL; + } + prtd = cstream->runtime->private_data; + if (!prtd) { + pr_err("%s: cannot set audio effects\n", __func__); + return -EINVAL; + } + + return 0; +} + +static int msm_compr_query_audio_effect_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol); + unsigned long fe_id = kcontrol->private_value; + struct msm_compr_pdata *pdata = (struct msm_compr_pdata *) + snd_soc_component_get_drvdata(comp); + struct msm_compr_audio_effects *audio_effects = NULL; + struct snd_compr_stream *cstream = NULL; + struct msm_compr_audio *prtd = NULL; + long *values = &(ucontrol->value.integer.value[0]); + + if (fe_id >= MSM_FRONTEND_DAI_MAX) { + pr_err("%s Received out of bounds fe_id %lu\n", + __func__, fe_id); + return -EINVAL; + } + cstream = pdata->cstream[fe_id]; + audio_effects = pdata->audio_effects[fe_id]; + if (!cstream || !audio_effects) { + pr_err("%s: stream or effects inactive\n", __func__); + return -EINVAL; + } + prtd = cstream->runtime->private_data; + if (!prtd) { + pr_err("%s: cannot set audio effects\n", __func__); + return -EINVAL; + } + if (prtd->compr_passthr != LEGACY_PCM) { + pr_err("%s: No effects for compr_type[%d]\n", + __func__, prtd->compr_passthr); + return -EPERM; + } + audio_effects->query.mod_id = (u32)*values++; + audio_effects->query.parm_id = (u32)*values++; + audio_effects->query.size = (u32)*values++; + audio_effects->query.offset = (u32)*values++; + audio_effects->query.device = (u32)*values++; + return 0; +} + +static int msm_compr_query_audio_effect_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol); + unsigned long fe_id = kcontrol->private_value; + struct msm_compr_pdata *pdata = (struct msm_compr_pdata *) + snd_soc_component_get_drvdata(comp); + struct msm_compr_audio_effects *audio_effects = NULL; + struct snd_compr_stream *cstream = NULL; + struct msm_compr_audio *prtd = NULL; + long *values = &(ucontrol->value.integer.value[0]); + + if (fe_id >= MSM_FRONTEND_DAI_MAX) { + pr_err("%s Received out of bounds fe_id %lu\n", + __func__, fe_id); + return -EINVAL; + } + cstream = pdata->cstream[fe_id]; + audio_effects = pdata->audio_effects[fe_id]; + if (!cstream || !audio_effects) { + pr_debug("%s: stream or effects inactive\n", __func__); + return -EINVAL; + } + prtd = cstream->runtime->private_data; + if (!prtd) { + pr_err("%s: cannot set audio effects\n", __func__); + return -EINVAL; + } + values[0] = (long)audio_effects->query.mod_id; + values[1] = (long)audio_effects->query.parm_id; + values[2] = (long)audio_effects->query.size; + values[3] = (long)audio_effects->query.offset; + values[4] = (long)audio_effects->query.device; + return 0; +} + +static int msm_compr_send_dec_params(struct snd_compr_stream *cstream, + struct msm_compr_dec_params *dec_params, + int stream_id) +{ + + int rc = 0; + struct msm_compr_audio *prtd = NULL; + struct snd_dec_ddp *ddp = &dec_params->ddp_params; + + if (!cstream || !dec_params) { + pr_err("%s: stream or dec_params inactive\n", __func__); + rc = -EINVAL; + goto end; + } + prtd = cstream->runtime->private_data; + if (!prtd) { + pr_err("%s: cannot set dec_params\n", __func__); + rc = -EINVAL; + goto end; + } + switch (prtd->codec) { + case FORMAT_MP3: + case FORMAT_MPEG4_AAC: + case FORMAT_TRUEHD: + case FORMAT_IEC61937: + case FORMAT_APTX: + pr_debug("%s: no runtime parameters for codec: %d\n", __func__, + prtd->codec); + break; + case FORMAT_AC3: + case FORMAT_EAC3: + if (prtd->compr_passthr != LEGACY_PCM) { + pr_debug("%s: No DDP param for compr_type[%d]\n", + __func__, prtd->compr_passthr); + break; + } + rc = msm_compr_send_ddp_cfg(prtd->audio_client, ddp, stream_id); + if (rc < 0) + pr_err("%s: DDP CMD CFG failed %d\n", __func__, rc); + break; + default: + break; + } +end: + return rc; + +} +static int msm_compr_dec_params_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol); + unsigned long fe_id = kcontrol->private_value; + struct msm_compr_pdata *pdata = (struct msm_compr_pdata *) + snd_soc_component_get_drvdata(comp); + struct msm_compr_dec_params *dec_params = NULL; + struct snd_compr_stream *cstream = NULL; + struct msm_compr_audio *prtd = NULL; + long *values = &(ucontrol->value.integer.value[0]); + int rc = 0; + + pr_debug("%s\n", __func__); + if (fe_id >= MSM_FRONTEND_DAI_MAX) { + pr_err("%s Received out of bounds fe_id %lu\n", + __func__, fe_id); + rc = -EINVAL; + goto end; + } + + cstream = pdata->cstream[fe_id]; + dec_params = pdata->dec_params[fe_id]; + + if (!cstream || !dec_params) { + pr_err("%s: stream or dec_params inactive\n", __func__); + rc = -EINVAL; + goto end; + } + prtd = cstream->runtime->private_data; + if (!prtd) { + pr_err("%s: cannot set dec_params\n", __func__); + rc = -EINVAL; + goto end; + } + + switch (prtd->codec) { + case FORMAT_MP3: + case FORMAT_MPEG4_AAC: + case FORMAT_FLAC: + case FORMAT_VORBIS: + case FORMAT_ALAC: + case FORMAT_APE: + case FORMAT_DTS: + case FORMAT_DSD: + case FORMAT_TRUEHD: + case FORMAT_IEC61937: + case FORMAT_APTX: + pr_debug("%s: no runtime parameters for codec: %d\n", __func__, + prtd->codec); + break; + case FORMAT_AC3: + case FORMAT_EAC3: { + struct snd_dec_ddp *ddp = &dec_params->ddp_params; + int cnt; + + if (prtd->compr_passthr != LEGACY_PCM) { + pr_debug("%s: No DDP param for compr_type[%d]\n", + __func__, prtd->compr_passthr); + break; + } + + ddp->params_length = (*values++); + if (ddp->params_length > DDP_DEC_MAX_NUM_PARAM) { + pr_err("%s: invalid num of params:: %d\n", __func__, + ddp->params_length); + rc = -EINVAL; + goto end; + } + for (cnt = 0; cnt < ddp->params_length; cnt++) { + ddp->params_id[cnt] = *values++; + ddp->params_value[cnt] = *values++; + } + prtd = cstream->runtime->private_data; + if (prtd && prtd->audio_client) + rc = msm_compr_send_dec_params(cstream, dec_params, + prtd->audio_client->stream_id); + break; + } + default: + break; + } +end: + pr_debug("%s: ret %d\n", __func__, rc); + return rc; +} + +static int msm_compr_dec_params_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + /* dummy function */ + return 0; +} + +static int msm_compr_playback_app_type_cfg_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u64 fe_id = kcontrol->private_value; + int session_type = SESSION_TYPE_RX; + int be_id = ucontrol->value.integer.value[3]; + struct msm_pcm_stream_app_type_cfg cfg_data = {0, 0, 48000}; + int ret = 0; + + cfg_data.app_type = ucontrol->value.integer.value[0]; + cfg_data.acdb_dev_id = ucontrol->value.integer.value[1]; + if (ucontrol->value.integer.value[2] != 0) + cfg_data.sample_rate = ucontrol->value.integer.value[2]; + pr_debug("%s: fe_id- %llu session_type- %d be_id- %d app_type- %d acdb_dev_id- %d sample_rate- %d\n", + __func__, fe_id, session_type, be_id, + cfg_data.app_type, cfg_data.acdb_dev_id, cfg_data.sample_rate); + ret = msm_pcm_routing_reg_stream_app_type_cfg(fe_id, session_type, + be_id, &cfg_data); + if (ret < 0) + pr_err("%s: msm_pcm_routing_reg_stream_app_type_cfg failed returned %d\n", + __func__, ret); + + return ret; +} + +static int msm_compr_playback_app_type_cfg_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u64 fe_id = kcontrol->private_value; + int session_type = SESSION_TYPE_RX; + int be_id = 0; + struct msm_pcm_stream_app_type_cfg cfg_data = {0}; + int ret = 0; + + ret = msm_pcm_routing_get_stream_app_type_cfg(fe_id, session_type, + &be_id, &cfg_data); + if (ret < 0) { + pr_err("%s: msm_pcm_routing_get_stream_app_type_cfg failed returned %d\n", + __func__, ret); + goto done; + } + + ucontrol->value.integer.value[0] = cfg_data.app_type; + ucontrol->value.integer.value[1] = cfg_data.acdb_dev_id; + ucontrol->value.integer.value[2] = cfg_data.sample_rate; + ucontrol->value.integer.value[3] = be_id; + pr_debug("%s: fedai_id %llu, session_type %d, be_id %d, app_type %d, acdb_dev_id %d, sample_rate %d\n", + __func__, fe_id, session_type, be_id, + cfg_data.app_type, cfg_data.acdb_dev_id, cfg_data.sample_rate); +done: + return ret; +} + +static int msm_compr_capture_app_type_cfg_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u64 fe_id = kcontrol->private_value; + int session_type = SESSION_TYPE_TX; + int be_id = ucontrol->value.integer.value[3]; + struct msm_pcm_stream_app_type_cfg cfg_data = {0, 0, 48000}; + int ret = 0; + + cfg_data.app_type = ucontrol->value.integer.value[0]; + cfg_data.acdb_dev_id = ucontrol->value.integer.value[1]; + if (ucontrol->value.integer.value[2] != 0) + cfg_data.sample_rate = ucontrol->value.integer.value[2]; + pr_debug("%s: fe_id- %llu session_type- %d be_id- %d app_type- %d acdb_dev_id- %d sample_rate- %d\n", + __func__, fe_id, session_type, be_id, + cfg_data.app_type, cfg_data.acdb_dev_id, cfg_data.sample_rate); + ret = msm_pcm_routing_reg_stream_app_type_cfg(fe_id, session_type, + be_id, &cfg_data); + if (ret < 0) + pr_err("%s: msm_pcm_routing_reg_stream_app_type_cfg failed returned %d\n", + __func__, ret); + + return ret; +} + +static int msm_compr_capture_app_type_cfg_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u64 fe_id = kcontrol->private_value; + int session_type = SESSION_TYPE_TX; + int be_id = 0; + struct msm_pcm_stream_app_type_cfg cfg_data = {0}; + int ret = 0; + + ret = msm_pcm_routing_get_stream_app_type_cfg(fe_id, session_type, + &be_id, &cfg_data); + if (ret < 0) { + pr_err("%s: msm_pcm_routing_get_stream_app_type_cfg failed returned %d\n", + __func__, ret); + goto done; + } + + ucontrol->value.integer.value[0] = cfg_data.app_type; + ucontrol->value.integer.value[1] = cfg_data.acdb_dev_id; + ucontrol->value.integer.value[2] = cfg_data.sample_rate; + ucontrol->value.integer.value[3] = be_id; + pr_debug("%s: fedai_id %llu, session_type %d, be_id %d, app_type %d, acdb_dev_id %d, sample_rate %d\n", + __func__, fe_id, session_type, be_id, + cfg_data.app_type, cfg_data.acdb_dev_id, cfg_data.sample_rate); +done: + return ret; +} + +static int msm_compr_channel_map_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol); + u64 fe_id = kcontrol->private_value; + struct msm_compr_pdata *pdata = (struct msm_compr_pdata *) + snd_soc_component_get_drvdata(comp); + int rc = 0, i; + + pr_debug("%s: fe_id- %llu\n", __func__, fe_id); + + if (fe_id >= MSM_FRONTEND_DAI_MAX) { + pr_err("%s Received out of bounds fe_id %llu\n", + __func__, fe_id); + rc = -EINVAL; + goto end; + } + + if (pdata->ch_map[fe_id]) { + pdata->ch_map[fe_id]->set_ch_map = true; + for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++) + pdata->ch_map[fe_id]->channel_map[i] = + (char)(ucontrol->value.integer.value[i]); + } else { + pr_debug("%s: no memory for ch_map, default will be set\n", + __func__); + } +end: + pr_debug("%s: ret %d\n", __func__, rc); + return rc; +} + +static int msm_compr_channel_map_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol); + u64 fe_id = kcontrol->private_value; + struct msm_compr_pdata *pdata = (struct msm_compr_pdata *) + snd_soc_component_get_drvdata(comp); + int rc = 0, i; + + pr_debug("%s: fe_id- %llu\n", __func__, fe_id); + if (fe_id >= MSM_FRONTEND_DAI_MAX) { + pr_err("%s: Received out of bounds fe_id %llu\n", + __func__, fe_id); + rc = -EINVAL; + goto end; + } + if (pdata->ch_map[fe_id]) { + for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++) + ucontrol->value.integer.value[i] = + pdata->ch_map[fe_id]->channel_map[i]; + } +end: + pr_debug("%s: ret %d\n", __func__, rc); + return rc; +} + +static int msm_compr_adsp_stream_cmd_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol); + unsigned long fe_id = kcontrol->private_value; + struct msm_compr_pdata *pdata = (struct msm_compr_pdata *) + snd_soc_component_get_drvdata(comp); + struct snd_compr_stream *cstream = NULL; + struct msm_compr_audio *prtd; + int ret = 0; + struct msm_adsp_event_data *event_data = NULL; + + if (fe_id >= MSM_FRONTEND_DAI_MAX) { + pr_err("%s Received invalid fe_id %lu\n", + __func__, fe_id); + ret = -EINVAL; + goto done; + } + + cstream = pdata->cstream[fe_id]; + if (cstream == NULL) { + pr_err("%s cstream is null\n", __func__); + ret = -EINVAL; + goto done; + } + + prtd = cstream->runtime->private_data; + if (!prtd) { + pr_err("%s: prtd is null\n", __func__); + ret = -EINVAL; + goto done; + } + + if (prtd->audio_client == NULL) { + pr_err("%s: audio_client is null\n", __func__); + ret = -EINVAL; + goto done; + } + + event_data = (struct msm_adsp_event_data *)ucontrol->value.bytes.data; + if ((event_data->event_type < ADSP_STREAM_PP_EVENT) || + (event_data->event_type >= ADSP_STREAM_EVENT_MAX)) { + pr_err("%s: invalid event_type=%d", + __func__, event_data->event_type); + ret = -EINVAL; + goto done; + } + + if (event_data->payload_len > sizeof(ucontrol->value.bytes.data) + - sizeof(struct msm_adsp_event_data)) { + pr_err("%s param length=%d exceeds limit", + __func__, event_data->payload_len); + ret = -EINVAL; + goto done; + } + + ret = q6asm_send_stream_cmd(prtd->audio_client, event_data); + if (ret < 0) + pr_err("%s: failed to send stream event cmd, err = %d\n", + __func__, ret); +done: + return ret; +} + +static int msm_compr_ion_fd_map_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol); + unsigned long fe_id = kcontrol->private_value; + struct msm_compr_pdata *pdata = (struct msm_compr_pdata *) + snd_soc_component_get_drvdata(comp); + struct snd_compr_stream *cstream = NULL; + struct msm_compr_audio *prtd; + int fd; + int ret = 0; + + if (fe_id >= MSM_FRONTEND_DAI_MAX) { + pr_err("%s Received out of bounds invalid fe_id %lu\n", + __func__, fe_id); + ret = -EINVAL; + goto done; + } + + cstream = pdata->cstream[fe_id]; + if (cstream == NULL) { + pr_err("%s cstream is null\n", __func__); + ret = -EINVAL; + goto done; + } + + prtd = cstream->runtime->private_data; + if (!prtd) { + pr_err("%s: prtd is null\n", __func__); + ret = -EINVAL; + goto done; + } + + if (prtd->audio_client == NULL) { + pr_err("%s: audio_client is null\n", __func__); + ret = -EINVAL; + goto done; + } + + memcpy(&fd, ucontrol->value.bytes.data, sizeof(fd)); + ret = q6asm_send_ion_fd(prtd->audio_client, fd); + if (ret < 0) + pr_err("%s: failed to register ion fd\n", __func__); +done: + return ret; +} + +static int msm_compr_rtic_event_ack_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol); + unsigned long fe_id = kcontrol->private_value; + struct msm_compr_pdata *pdata = (struct msm_compr_pdata *) + snd_soc_component_get_drvdata(comp); + struct snd_compr_stream *cstream = NULL; + struct msm_compr_audio *prtd; + int ret = 0; + int param_length = 0; + + if (fe_id >= MSM_FRONTEND_DAI_MAX) { + pr_err("%s Received invalid fe_id %lu\n", + __func__, fe_id); + ret = -EINVAL; + goto done; + } + + cstream = pdata->cstream[fe_id]; + if (cstream == NULL) { + pr_err("%s cstream is null\n", __func__); + ret = -EINVAL; + goto done; + } + + prtd = cstream->runtime->private_data; + if (!prtd) { + pr_err("%s: prtd is null\n", __func__); + ret = -EINVAL; + goto done; + } + + if (prtd->audio_client == NULL) { + pr_err("%s: audio_client is null\n", __func__); + ret = -EINVAL; + goto done; + } + + memcpy(¶m_length, ucontrol->value.bytes.data, + sizeof(param_length)); + if ((param_length + sizeof(param_length)) + >= sizeof(ucontrol->value.bytes.data)) { + pr_err("%s param length=%d exceeds limit", + __func__, param_length); + ret = -EINVAL; + goto done; + } + + ret = q6asm_send_rtic_event_ack(prtd->audio_client, + ucontrol->value.bytes.data + sizeof(param_length), + param_length); + if (ret < 0) + pr_err("%s: failed to send rtic event ack, err = %d\n", + __func__, ret); +done: + return ret; +} + +static int msm_compr_gapless_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol); + struct msm_compr_pdata *pdata = (struct msm_compr_pdata *) + snd_soc_component_get_drvdata(comp); + pdata->use_dsp_gapless_mode = ucontrol->value.integer.value[0]; + pr_debug("%s: value: %ld\n", __func__, + ucontrol->value.integer.value[0]); + + return 0; +} + +static int msm_compr_gapless_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol); + struct msm_compr_pdata *pdata = + snd_soc_component_get_drvdata(comp); + pr_debug("%s:gapless mode %d\n", __func__, pdata->use_dsp_gapless_mode); + ucontrol->value.integer.value[0] = pdata->use_dsp_gapless_mode; + + return 0; +} + +static const struct snd_kcontrol_new msm_compr_gapless_controls[] = { + SOC_SINGLE_EXT("Compress Gapless Playback", + 0, 0, 1, 0, + msm_compr_gapless_get, + msm_compr_gapless_put), +}; + +static int msm_compr_probe(struct snd_soc_platform *platform) +{ + struct msm_compr_pdata *pdata; + int i; + int rc; + const char *qdsp_version; + + pr_debug("%s\n", __func__); + pdata = (struct msm_compr_pdata *) + kzalloc(sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + snd_soc_platform_set_drvdata(platform, pdata); + + for (i = 0; i < MSM_FRONTEND_DAI_MAX; i++) { + pdata->volume[i][0] = COMPRESSED_LR_VOL_MAX_STEPS; + pdata->volume[i][1] = COMPRESSED_LR_VOL_MAX_STEPS; + pdata->audio_effects[i] = NULL; + pdata->dec_params[i] = NULL; + pdata->cstream[i] = NULL; + pdata->ch_map[i] = NULL; + pdata->is_in_use[i] = false; + } + + snd_soc_add_platform_controls(platform, msm_compr_gapless_controls, + ARRAY_SIZE(msm_compr_gapless_controls)); + + rc = of_property_read_string(platform->dev->of_node, + "qcom,adsp-version", &qdsp_version); + if (!rc) { + if (!strcmp(qdsp_version, "MDSP 1.2")) + pdata->use_legacy_api = true; + else + pdata->use_legacy_api = false; + } else + pdata->use_legacy_api = false; + + pr_debug("%s: use legacy api %d\n", __func__, pdata->use_legacy_api); + /* + * use_dsp_gapless_mode part of platform data(pdata) is updated from HAL + * through a mixer control before compress driver is opened. The mixer + * control is used to decide if dsp gapless mode needs to be enabled. + * Gapless is disabled by default. + */ + pdata->use_dsp_gapless_mode = false; + return 0; +} + +static int msm_compr_volume_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = COMPRESSED_LR_VOL_MAX_STEPS; + return 0; +} + +static int msm_compr_audio_effects_config_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = MAX_PP_PARAMS_SZ; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 0xFFFFFFFF; + return 0; +} + +static int msm_compr_query_audio_effect_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 128; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 0xFFFFFFFF; + return 0; +} + +static int msm_compr_dec_params_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 128; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 0xFFFFFFFF; + return 0; +} + +static int msm_compr_app_type_cfg_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 5; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 0xFFFFFFFF; + return 0; +} + +static int msm_compr_channel_map_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = PCM_FORMAT_MAX_NUM_CHANNEL_V8; + uinfo->value.integer.min = 0; + /* See PCM_CHANNEL_RSD=34 in apr_audio-v2.h */ + uinfo->value.integer.max = 34; + return 0; +} + +static int msm_compr_add_volume_control(struct snd_soc_pcm_runtime *rtd) +{ + const char *mixer_ctl_name = "Compress Playback"; + const char *deviceNo = "NN"; + const char *suffix = "Volume"; + char *mixer_str = NULL; + int ctl_len; + struct snd_kcontrol_new fe_volume_control[1] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "?", + .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | + SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = msm_compr_volume_info, + .tlv.p = msm_compr_vol_gain, + .get = msm_compr_volume_get, + .put = msm_compr_volume_put, + .private_value = 0, + } + }; + + if (!rtd) { + pr_err("%s NULL rtd\n", __func__); + return 0; + } + pr_debug("%s: added new compr FE with name %s, id %d, cpu dai %s, device no %d\n", + __func__, rtd->dai_link->name, rtd->dai_link->id, + rtd->dai_link->cpu_dai_name, rtd->pcm->device); + ctl_len = strlen(mixer_ctl_name) + 1 + strlen(deviceNo) + 1 + + strlen(suffix) + 1; + mixer_str = kzalloc(ctl_len, GFP_KERNEL); + if (!mixer_str) { + pr_err("failed to allocate mixer ctrl str of len %d", ctl_len); + return 0; + } + snprintf(mixer_str, ctl_len, "%s %d %s", mixer_ctl_name, + rtd->pcm->device, suffix); + fe_volume_control[0].name = mixer_str; + fe_volume_control[0].private_value = rtd->dai_link->id; + pr_debug("Registering new mixer ctl %s", mixer_str); + snd_soc_add_platform_controls(rtd->platform, fe_volume_control, + ARRAY_SIZE(fe_volume_control)); + kfree(mixer_str); + return 0; +} + +static int msm_compr_add_audio_effects_control(struct snd_soc_pcm_runtime *rtd) +{ + const char *mixer_ctl_name = "Audio Effects Config"; + const char *deviceNo = "NN"; + char *mixer_str = NULL; + int ctl_len; + struct snd_kcontrol_new fe_audio_effects_config_control[1] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "?", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = msm_compr_audio_effects_config_info, + .get = msm_compr_audio_effects_config_get, + .put = msm_compr_audio_effects_config_put, + .private_value = 0, + } + }; + + + if (!rtd) { + pr_err("%s NULL rtd\n", __func__); + return 0; + } + + pr_debug("%s: added new compr FE with name %s, id %d, cpu dai %s, device no %d\n", + __func__, rtd->dai_link->name, rtd->dai_link->id, + rtd->dai_link->cpu_dai_name, rtd->pcm->device); + + ctl_len = strlen(mixer_ctl_name) + 1 + strlen(deviceNo) + 1; + mixer_str = kzalloc(ctl_len, GFP_KERNEL); + + if (!mixer_str) + return 0; + + snprintf(mixer_str, ctl_len, "%s %d", mixer_ctl_name, rtd->pcm->device); + + fe_audio_effects_config_control[0].name = mixer_str; + fe_audio_effects_config_control[0].private_value = rtd->dai_link->id; + pr_debug("Registering new mixer ctl %s\n", mixer_str); + snd_soc_add_platform_controls(rtd->platform, + fe_audio_effects_config_control, + ARRAY_SIZE(fe_audio_effects_config_control)); + kfree(mixer_str); + return 0; +} + +static int msm_compr_add_query_audio_effect_control( + struct snd_soc_pcm_runtime *rtd) +{ + const char *mixer_ctl_name = "Query Audio Effect Param"; + const char *deviceNo = "NN"; + char *mixer_str = NULL; + int ctl_len; + struct snd_kcontrol_new fe_query_audio_effect_control[1] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "?", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = msm_compr_query_audio_effect_info, + .get = msm_compr_query_audio_effect_get, + .put = msm_compr_query_audio_effect_put, + .private_value = 0, + } + }; + if (!rtd) { + pr_err("%s NULL rtd\n", __func__); + return 0; + } + pr_debug("%s: added new compr FE with name %s, id %d, cpu dai %s, device no %d\n", + __func__, rtd->dai_link->name, rtd->dai_link->id, + rtd->dai_link->cpu_dai_name, rtd->pcm->device); + ctl_len = strlen(mixer_ctl_name) + 1 + strlen(deviceNo) + 1; + mixer_str = kzalloc(ctl_len, GFP_KERNEL); + if (!mixer_str) { + pr_err("failed to allocate mixer ctrl str of len %d", ctl_len); + return 0; + } + snprintf(mixer_str, ctl_len, "%s %d", mixer_ctl_name, rtd->pcm->device); + fe_query_audio_effect_control[0].name = mixer_str; + fe_query_audio_effect_control[0].private_value = rtd->dai_link->id; + pr_debug("%s: registering new mixer ctl %s\n", __func__, mixer_str); + snd_soc_add_platform_controls(rtd->platform, + fe_query_audio_effect_control, + ARRAY_SIZE(fe_query_audio_effect_control)); + kfree(mixer_str); + return 0; +} + +static int msm_compr_add_audio_adsp_stream_cmd_control( + struct snd_soc_pcm_runtime *rtd) +{ + const char *mixer_ctl_name = DSP_STREAM_CMD; + const char *deviceNo = "NN"; + char *mixer_str = NULL; + int ctl_len = 0, ret = 0; + struct snd_kcontrol_new fe_audio_adsp_stream_cmd_config_control[1] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "?", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = msm_adsp_stream_cmd_info, + .put = msm_compr_adsp_stream_cmd_put, + .private_value = 0, + } + }; + + if (!rtd) { + pr_err("%s NULL rtd\n", __func__); + ret = -EINVAL; + goto done; + } + + ctl_len = strlen(mixer_ctl_name) + 1 + strlen(deviceNo) + 1; + mixer_str = kzalloc(ctl_len, GFP_KERNEL); + if (!mixer_str) { + ret = -ENOMEM; + goto done; + } + + snprintf(mixer_str, ctl_len, "%s %d", mixer_ctl_name, rtd->pcm->device); + fe_audio_adsp_stream_cmd_config_control[0].name = mixer_str; + fe_audio_adsp_stream_cmd_config_control[0].private_value = + rtd->dai_link->id; + pr_debug("%s: Registering new mixer ctl %s\n", __func__, mixer_str); + ret = snd_soc_add_platform_controls(rtd->platform, + fe_audio_adsp_stream_cmd_config_control, + ARRAY_SIZE(fe_audio_adsp_stream_cmd_config_control)); + if (ret < 0) + pr_err("%s: failed to add ctl %s. err = %d\n", + __func__, mixer_str, ret); + + kfree(mixer_str); +done: + return ret; +} + +static int msm_compr_add_audio_adsp_stream_callback_control( + struct snd_soc_pcm_runtime *rtd) +{ + const char *mixer_ctl_name = DSP_STREAM_CALLBACK; + const char *deviceNo = "NN"; + char *mixer_str = NULL; + int ctl_len = 0, ret = 0; + struct snd_kcontrol *kctl; + + struct snd_kcontrol_new fe_audio_adsp_callback_config_control[1] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "?", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = msm_adsp_stream_callback_info, + .get = msm_adsp_stream_callback_get, + .private_value = 0, + } + }; + + if (!rtd) { + pr_err("%s: rtd is NULL\n", __func__); + ret = -EINVAL; + goto done; + } + + ctl_len = strlen(mixer_ctl_name) + 1 + strlen(deviceNo) + 1; + mixer_str = kzalloc(ctl_len, GFP_KERNEL); + if (!mixer_str) { + ret = -ENOMEM; + goto done; + } + + snprintf(mixer_str, ctl_len, "%s %d", mixer_ctl_name, rtd->pcm->device); + fe_audio_adsp_callback_config_control[0].name = mixer_str; + fe_audio_adsp_callback_config_control[0].private_value = + rtd->dai_link->id; + pr_debug("%s: Registering new mixer ctl %s\n", __func__, mixer_str); + ret = snd_soc_add_platform_controls(rtd->platform, + fe_audio_adsp_callback_config_control, + ARRAY_SIZE(fe_audio_adsp_callback_config_control)); + if (ret < 0) { + pr_err("%s: failed to add ctl %s. err = %d\n", + __func__, mixer_str, ret); + ret = -EINVAL; + goto free_mixer_str; + } + + kctl = snd_soc_card_get_kcontrol(rtd->card, mixer_str); + if (!kctl) { + pr_err("%s: failed to get kctl %s.\n", __func__, mixer_str); + ret = -EINVAL; + goto free_mixer_str; + } + + kctl->private_data = NULL; + +free_mixer_str: + kfree(mixer_str); +done: + return ret; +} + +static int msm_compr_add_dec_runtime_params_control( + struct snd_soc_pcm_runtime *rtd) +{ + const char *mixer_ctl_name = "Audio Stream"; + const char *deviceNo = "NN"; + const char *suffix = "Dec Params"; + char *mixer_str = NULL; + int ctl_len; + struct snd_kcontrol_new fe_dec_params_control[1] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "?", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = msm_compr_dec_params_info, + .get = msm_compr_dec_params_get, + .put = msm_compr_dec_params_put, + .private_value = 0, + } + }; + + if (!rtd) { + pr_err("%s NULL rtd\n", __func__); + return 0; + } + + pr_debug("%s: added new compr FE with name %s, id %d, cpu dai %s, device no %d\n", + __func__, rtd->dai_link->name, rtd->dai_link->id, + rtd->dai_link->cpu_dai_name, rtd->pcm->device); + + ctl_len = strlen(mixer_ctl_name) + 1 + strlen(deviceNo) + 1 + + strlen(suffix) + 1; + mixer_str = kzalloc(ctl_len, GFP_KERNEL); + + if (!mixer_str) + return 0; + + snprintf(mixer_str, ctl_len, "%s %d %s", mixer_ctl_name, + rtd->pcm->device, suffix); + + fe_dec_params_control[0].name = mixer_str; + fe_dec_params_control[0].private_value = rtd->dai_link->id; + pr_debug("Registering new mixer ctl %s", mixer_str); + snd_soc_add_platform_controls(rtd->platform, + fe_dec_params_control, + ARRAY_SIZE(fe_dec_params_control)); + kfree(mixer_str); + return 0; +} + +static int msm_compr_add_app_type_cfg_control(struct snd_soc_pcm_runtime *rtd) +{ + const char *playback_mixer_ctl_name = "Audio Stream"; + const char *capture_mixer_ctl_name = "Audio Stream Capture"; + const char *deviceNo = "NN"; + const char *suffix = "App Type Cfg"; + char *mixer_str = NULL; + int ctl_len; + struct snd_kcontrol_new fe_app_type_cfg_control[1] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "?", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = msm_compr_app_type_cfg_info, + .put = msm_compr_playback_app_type_cfg_put, + .get = msm_compr_playback_app_type_cfg_get, + .private_value = 0, + } + }; + + if (!rtd) { + pr_err("%s NULL rtd\n", __func__); + return 0; + } + + pr_debug("%s: added new compr FE ctl with name %s, id %d, cpu dai %s, device no %d\n", + __func__, rtd->dai_link->name, rtd->dai_link->id, + rtd->dai_link->cpu_dai_name, rtd->pcm->device); + if (rtd->compr->direction == SND_COMPRESS_PLAYBACK) + ctl_len = strlen(playback_mixer_ctl_name) + 1 + strlen(deviceNo) + + 1 + strlen(suffix) + 1; + else + ctl_len = strlen(capture_mixer_ctl_name) + 1 + strlen(deviceNo) + + 1 + strlen(suffix) + 1; + + mixer_str = kzalloc(ctl_len, GFP_KERNEL); + + if (!mixer_str) + return 0; + + if (rtd->compr->direction == SND_COMPRESS_PLAYBACK) + snprintf(mixer_str, ctl_len, "%s %d %s", + playback_mixer_ctl_name, rtd->pcm->device, suffix); + else + snprintf(mixer_str, ctl_len, "%s %d %s", + capture_mixer_ctl_name, rtd->pcm->device, suffix); + + fe_app_type_cfg_control[0].name = mixer_str; + fe_app_type_cfg_control[0].private_value = rtd->dai_link->id; + + if (rtd->compr->direction == SND_COMPRESS_PLAYBACK) { + fe_app_type_cfg_control[0].put = + msm_compr_playback_app_type_cfg_put; + fe_app_type_cfg_control[0].get = + msm_compr_playback_app_type_cfg_get; + } else { + fe_app_type_cfg_control[0].put = + msm_compr_capture_app_type_cfg_put; + fe_app_type_cfg_control[0].get = + msm_compr_capture_app_type_cfg_get; + } + pr_debug("Registering new mixer ctl %s", mixer_str); + snd_soc_add_platform_controls(rtd->platform, + fe_app_type_cfg_control, + ARRAY_SIZE(fe_app_type_cfg_control)); + kfree(mixer_str); + return 0; +} + +static int msm_compr_add_channel_map_control(struct snd_soc_pcm_runtime *rtd) +{ + const char *mixer_ctl_name = "Playback Channel Map"; + const char *deviceNo = "NN"; + char *mixer_str = NULL; + struct msm_compr_pdata *pdata = NULL; + int ctl_len; + struct snd_kcontrol_new fe_channel_map_control[1] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "?", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = msm_compr_channel_map_info, + .get = msm_compr_channel_map_get, + .put = msm_compr_channel_map_put, + .private_value = 0, + } + }; + + if (!rtd) { + pr_err("%s: NULL rtd\n", __func__); + return -EINVAL; + } + + pr_debug("%s: added new compr FE with name %s, id %d, cpu dai %s, device no %d\n", + __func__, rtd->dai_link->name, rtd->dai_link->id, + rtd->dai_link->cpu_dai_name, rtd->pcm->device); + + ctl_len = strlen(mixer_ctl_name) + strlen(deviceNo) + 1; + mixer_str = kzalloc(ctl_len, GFP_KERNEL); + + if (!mixer_str) + return -ENOMEM; + + snprintf(mixer_str, ctl_len, "%s%d", mixer_ctl_name, rtd->pcm->device); + + fe_channel_map_control[0].name = mixer_str; + fe_channel_map_control[0].private_value = rtd->dai_link->id; + pr_debug("%s: Registering new mixer ctl %s\n", __func__, mixer_str); + snd_soc_add_platform_controls(rtd->platform, + fe_channel_map_control, + ARRAY_SIZE(fe_channel_map_control)); + + pdata = snd_soc_platform_get_drvdata(rtd->platform); + pdata->ch_map[rtd->dai_link->id] = + kzalloc(sizeof(struct msm_compr_ch_map), GFP_KERNEL); + if (!pdata->ch_map[rtd->dai_link->id]) { + pr_err("%s: Could not allocate memory for channel map\n", + __func__); + kfree(mixer_str); + return -ENOMEM; + } + kfree(mixer_str); + return 0; +} + +static int msm_compr_add_io_fd_cmd_control(struct snd_soc_pcm_runtime *rtd) +{ + const char *mixer_ctl_name = "Playback ION FD"; + const char *deviceNo = "NN"; + char *mixer_str = NULL; + int ctl_len = 0, ret = 0; + struct snd_kcontrol_new fe_ion_fd_config_control[1] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "?", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = msm_adsp_stream_cmd_info, + .put = msm_compr_ion_fd_map_put, + .private_value = 0, + } + }; + + if (!rtd) { + pr_err("%s NULL rtd\n", __func__); + ret = -EINVAL; + goto done; + } + + ctl_len = strlen(mixer_ctl_name) + 1 + strlen(deviceNo) + 1; + mixer_str = kzalloc(ctl_len, GFP_KERNEL); + if (!mixer_str) { + ret = -ENOMEM; + goto done; + } + + snprintf(mixer_str, ctl_len, "%s %d", mixer_ctl_name, rtd->pcm->device); + fe_ion_fd_config_control[0].name = mixer_str; + fe_ion_fd_config_control[0].private_value = rtd->dai_link->id; + pr_debug("%s: Registering new mixer ctl %s\n", __func__, mixer_str); + ret = snd_soc_add_platform_controls(rtd->platform, + fe_ion_fd_config_control, + ARRAY_SIZE(fe_ion_fd_config_control)); + if (ret < 0) + pr_err("%s: failed to add ctl %s\n", __func__, mixer_str); + + kfree(mixer_str); +done: + return ret; +} + +static int msm_compr_add_event_ack_cmd_control(struct snd_soc_pcm_runtime *rtd) +{ + const char *mixer_ctl_name = "Playback Event Ack"; + const char *deviceNo = "NN"; + char *mixer_str = NULL; + int ctl_len = 0, ret = 0; + struct snd_kcontrol_new fe_event_ack_config_control[1] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "?", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = msm_adsp_stream_cmd_info, + .put = msm_compr_rtic_event_ack_put, + .private_value = 0, + } + }; + + if (!rtd) { + pr_err("%s NULL rtd\n", __func__); + ret = -EINVAL; + goto done; + } + + ctl_len = strlen(mixer_ctl_name) + 1 + strlen(deviceNo) + 1; + mixer_str = kzalloc(ctl_len, GFP_KERNEL); + if (!mixer_str) { + ret = -ENOMEM; + goto done; + } + + snprintf(mixer_str, ctl_len, "%s %d", mixer_ctl_name, rtd->pcm->device); + fe_event_ack_config_control[0].name = mixer_str; + fe_event_ack_config_control[0].private_value = rtd->dai_link->id; + pr_debug("%s: Registering new mixer ctl %s\n", __func__, mixer_str); + ret = snd_soc_add_platform_controls(rtd->platform, + fe_event_ack_config_control, + ARRAY_SIZE(fe_event_ack_config_control)); + if (ret < 0) + pr_err("%s: failed to add ctl %s\n", __func__, mixer_str); + + kfree(mixer_str); +done: + return ret; +} + +static int msm_compr_new(struct snd_soc_pcm_runtime *rtd) +{ + int rc; + + rc = msm_compr_add_volume_control(rtd); + if (rc) + pr_err("%s: Could not add Compr Volume Control\n", __func__); + + rc = msm_compr_add_audio_effects_control(rtd); + if (rc) + pr_err("%s: Could not add Compr Audio Effects Control\n", + __func__); + + rc = msm_compr_add_audio_adsp_stream_cmd_control(rtd); + if (rc) + pr_err("%s: Could not add Compr ADSP Stream Cmd Control\n", + __func__); + + rc = msm_compr_add_audio_adsp_stream_callback_control(rtd); + if (rc) + pr_err("%s: Could not add Compr ADSP Stream Callback Control\n", + __func__); + + rc = msm_compr_add_io_fd_cmd_control(rtd); + if (rc) + pr_err("%s: Could not add Compr ion fd Control\n", + __func__); + + rc = msm_compr_add_event_ack_cmd_control(rtd); + if (rc) + pr_err("%s: Could not add Compr event ack Control\n", + __func__); + + rc = msm_compr_add_query_audio_effect_control(rtd); + if (rc) + pr_err("%s: Could not add Compr Query Audio Effect Control\n", + __func__); + + rc = msm_compr_add_dec_runtime_params_control(rtd); + if (rc) + pr_err("%s: Could not add Compr Dec runtime params Control\n", + __func__); + rc = msm_compr_add_app_type_cfg_control(rtd); + if (rc) + pr_err("%s: Could not add Compr App Type Cfg Control\n", + __func__); + rc = msm_compr_add_channel_map_control(rtd); + if (rc) + pr_err("%s: Could not add Compr Channel Map Control\n", + __func__); + return 0; +} + +static struct snd_compr_ops msm_compr_ops = { + .open = msm_compr_open, + .free = msm_compr_free, + .trigger = msm_compr_trigger, + .pointer = msm_compr_pointer, + .set_params = msm_compr_set_params, + .set_metadata = msm_compr_set_metadata, + .get_metadata = msm_compr_get_metadata, + .set_next_track_param = msm_compr_set_next_track_param, + .ack = msm_compr_ack, + .copy = msm_compr_copy, + .get_caps = msm_compr_get_caps, + .get_codec_caps = msm_compr_get_codec_caps, +}; + +static struct snd_soc_platform_driver msm_soc_platform = { + .probe = msm_compr_probe, + .compr_ops = &msm_compr_ops, + .pcm_new = msm_compr_new, +}; + +static int msm_compr_dev_probe(struct platform_device *pdev) +{ + + pr_debug("%s: dev name %s\n", __func__, dev_name(&pdev->dev)); + return snd_soc_register_platform(&pdev->dev, + &msm_soc_platform); +} + +static int msm_compr_remove(struct platform_device *pdev) +{ + snd_soc_unregister_platform(&pdev->dev); + return 0; +} + +static const struct of_device_id msm_compr_dt_match[] = { + {.compatible = "qcom,msm-compress-dsp"}, + {} +}; +MODULE_DEVICE_TABLE(of, msm_compr_dt_match); + +static struct platform_driver msm_compr_driver = { + .driver = { + .name = "msm-compress-dsp", + .owner = THIS_MODULE, + .of_match_table = msm_compr_dt_match, + }, + .probe = msm_compr_dev_probe, + .remove = msm_compr_remove, +}; + +int __init msm_compress_dsp_init(void) +{ + return platform_driver_register(&msm_compr_driver); +} + +void msm_compress_dsp_exit(void) +{ + platform_driver_unregister(&msm_compr_driver); +} + +MODULE_DESCRIPTION("Compress Offload platform driver"); +MODULE_LICENSE("GPL v2"); diff --git a/audio-kernel/asoc/msm-cpe-lsm.c b/audio-kernel/asoc/msm-cpe-lsm.c new file mode 100644 index 000000000..ea91e0e08 --- /dev/null +++ b/audio-kernel/asoc/msm-cpe-lsm.c @@ -0,0 +1,3356 @@ +/* + * Copyright (c) 2013-2019, 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "msm-slim-dma.h" +#include "codecs/cpe_core.h" + +#define SAMPLE_RATE_48KHZ 48000 +#define SAMPLE_RATE_16KHZ 16000 +#define LSM_VOICE_WAKEUP_APP_V2 2 +#define AFE_PORT_ID_1 1 +#define AFE_PORT_ID_3 3 +#define AFE_OUT_PORT_2 2 +#define LISTEN_MIN_NUM_PERIODS 2 +#define LISTEN_MAX_NUM_PERIODS 12 +#define LISTEN_MAX_PERIOD_SIZE 61440 +#define LISTEN_MIN_PERIOD_SIZE 320 +#define LISTEN_MAX_STATUS_PAYLOAD_SIZE 256 +#define MSM_CPE_MAX_CUSTOM_PARAM_SIZE 2048 + +#define MSM_CPE_LAB_THREAD_TIMEOUT (3 * (HZ/10)) + +#define MSM_CPE_LSM_GRAB_LOCK(lock, name) \ +{ \ + pr_debug("%s: %s lock acquire\n", \ + __func__, name); \ + mutex_lock(lock); \ +} + +#define MSM_CPE_LSM_REL_LOCK(lock, name) \ +{ \ + pr_debug("%s: %s lock release\n", \ + __func__, name); \ + mutex_unlock(lock); \ +} + +/* Conventional and unconventional sample rate supported */ +static unsigned int supported_sample_rates[] = { + 8000, 16000, 48000, 192000, 384000 +}; + +static struct snd_pcm_hw_constraint_list constraints_sample_rates = { + .count = ARRAY_SIZE(supported_sample_rates), + .list = supported_sample_rates, + .mask = 0, +}; + + +static struct snd_pcm_hardware msm_pcm_hardware_listen = { + .info = (SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_RESUME), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE), + .rates = (SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_192000 | SNDRV_PCM_RATE_384000), + .rate_min = 16000, + .rate_max = 384000, + .channels_min = 1, + .channels_max = 1, + .buffer_bytes_max = LISTEN_MAX_NUM_PERIODS * + LISTEN_MAX_PERIOD_SIZE, + .period_bytes_min = LISTEN_MIN_PERIOD_SIZE, + .period_bytes_max = LISTEN_MAX_PERIOD_SIZE, + .periods_min = LISTEN_MIN_NUM_PERIODS, + .periods_max = LISTEN_MAX_NUM_PERIODS, + .fifo_size = 0, +}; + +enum { + AFE_CMD_INVALID = 0, + AFE_CMD_PORT_START, + AFE_CMD_PORT_SUSPEND, + AFE_CMD_PORT_RESUME, + AFE_CMD_PORT_STOP, +}; + +enum cpe_lab_thread_status { + MSM_LSM_LAB_THREAD_STOP, + MSM_LSM_LAB_THREAD_RUNNING, + MSM_LSM_LAB_THREAD_ERROR, +}; + +struct cpe_hw_params { + u32 sample_rate; + u16 sample_size; + u32 buf_sz; + u32 period_count; + u16 channels; +}; + +struct cpe_data_pcm_buf { + u8 *mem; + phys_addr_t phys; +}; + +struct cpe_lsm_lab { + atomic_t in_count; + atomic_t abort_read; + u32 dma_write; + u32 buf_idx; + u32 pcm_size; + enum cpe_lab_thread_status thread_status; + struct cpe_data_pcm_buf *pcm_buf; + wait_queue_head_t period_wait; + struct completion comp; + struct completion thread_complete; +}; + +struct cpe_priv { + void *core_handle; + struct snd_soc_codec *codec; + struct wcd_cpe_lsm_ops lsm_ops; + struct wcd_cpe_afe_ops afe_ops; + bool afe_mad_ctl; + u32 input_port_id; +}; + +struct cpe_lsm_data { + struct device *dev; + struct cpe_lsm_session *lsm_session; + struct mutex lsm_api_lock; + struct cpe_lsm_lab lab; + struct cpe_hw_params hw_params; + struct snd_pcm_substream *substream; + + wait_queue_head_t event_wait; + atomic_t event_avail; + atomic_t event_stop; + + u8 ev_det_status; + u8 ev_det_pld_size; + u8 *ev_det_payload; + + bool cpe_prepared; +}; + +static int msm_cpe_afe_mad_ctl_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct cpe_priv *cpe = kcontrol->private_data; + + ucontrol->value.integer.value[0] = cpe->afe_mad_ctl; + return 0; +} + +static int msm_cpe_afe_mad_ctl_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct cpe_priv *cpe = kcontrol->private_data; + + cpe->afe_mad_ctl = ucontrol->value.integer.value[0]; + return 0; +} + +static struct snd_kcontrol_new msm_cpe_kcontrols[] = { + SOC_SINGLE_EXT("CPE AFE MAD Enable", SND_SOC_NOPM, 0, 1, 0, + msm_cpe_afe_mad_ctl_get, msm_cpe_afe_mad_ctl_put), +}; + +/* + * cpe_get_private_data: obtain ASoC platform driver private data + * @substream: ASoC substream for which private data to be obtained + */ +static struct cpe_priv *cpe_get_private_data( + struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd; + + if (!substream || !substream->private_data) { + pr_err("%s: %s is invalid\n", + __func__, + (!substream) ? "substream" : "private_data"); + goto err_ret; + } + + rtd = substream->private_data; + + if (!rtd || !rtd->platform) { + pr_err("%s: %s is invalid\n", + __func__, + (!rtd) ? "runtime" : "platform"); + goto err_ret; + } + + return snd_soc_platform_get_drvdata(rtd->platform); + +err_ret: + return NULL; +} + +/* + * cpe_get_lsm_data: obtain the lsm session data given the substream + * @substream: ASoC substream for which lsm session data to be obtained + */ +static struct cpe_lsm_data *cpe_get_lsm_data( + struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + + return runtime->private_data; +} + +static void msm_cpe_process_event_status(void *data, + u8 detect_status, u8 size, u8 *payload) +{ + struct cpe_lsm_data *lsm_d = data; + + lsm_d->ev_det_status = detect_status; + lsm_d->ev_det_pld_size = size; + + lsm_d->ev_det_payload = kzalloc(size, GFP_KERNEL); + if (!lsm_d->ev_det_payload) + return; + + memcpy(lsm_d->ev_det_payload, payload, size); + + atomic_set(&lsm_d->event_avail, 1); + wake_up(&lsm_d->event_wait); +} + +static void msm_cpe_process_event_status_done(struct cpe_lsm_data *lsm_data) +{ + kfree(lsm_data->ev_det_payload); + lsm_data->ev_det_payload = NULL; + + lsm_data->ev_det_status = 0; + lsm_data->ev_det_pld_size = 0; +} + +/* + * msm_cpe_afe_port_cntl: Perform the afe port control + * @substream: substream for which afe port command to be performed + * @core_handle: handle to core + * @afe_ops: handle to the afe operations + * @afe_cfg: afe port configuration data + * @cmd: command to be sent to AFE + * + */ +static int msm_cpe_afe_port_cntl( + struct snd_pcm_substream *substream, + void *core_handle, + struct wcd_cpe_afe_ops *afe_ops, + struct wcd_cpe_afe_port_cfg *afe_cfg, + int cmd) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + int rc = 0; + + if (!afe_cfg->port_id) { + /* + * It is possible driver can get closed without prepare, + * in which case afe ports will not be initialized. + */ + dev_dbg(rtd->dev, + "%s: Invalid afe port id\n", + __func__); + return 0; + } + + switch (cmd) { + case AFE_CMD_PORT_START: + rc = afe_ops->afe_port_start(core_handle, afe_cfg); + if (rc != 0) + dev_err(rtd->dev, + "%s: AFE port start failed\n", + __func__); + break; + case AFE_CMD_PORT_SUSPEND: + rc = afe_ops->afe_port_suspend(core_handle, afe_cfg); + if (rc != 0) + dev_err(rtd->dev, + "%s: afe_suspend failed, err = %d\n", + __func__, rc); + break; + case AFE_CMD_PORT_RESUME: + rc = afe_ops->afe_port_resume(core_handle, afe_cfg); + if (rc != 0) + dev_err(rtd->dev, + "%s: afe_resume failed, err = %d\n", + __func__, rc); + break; + case AFE_CMD_PORT_STOP: + rc = afe_ops->afe_port_stop(core_handle, afe_cfg); + if (rc != 0) + dev_err(rtd->dev, + "%s: afe_stopfailed, err = %d\n", + __func__, rc); + break; + } + + return rc; +} + +static int msm_cpe_lsm_lab_stop(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct cpe_lsm_data *lsm_d = cpe_get_lsm_data(substream); + struct cpe_priv *cpe = cpe_get_private_data(substream); + struct wcd_cpe_lsm_ops *lsm_ops; + struct wcd_cpe_afe_ops *afe_ops; + struct cpe_lsm_session *session; + struct cpe_lsm_lab *lab_d = &lsm_d->lab; + struct msm_slim_dma_data *dma_data = NULL; + int rc; + + /* + * the caller is not aware of LAB status and will + * try to stop lab even if it is already stopped. + * return success right away is LAB is already stopped + */ + if (lab_d->thread_status == MSM_LSM_LAB_THREAD_STOP) { + dev_dbg(rtd->dev, + "%s: lab already stopped\n", + __func__); + return 0; + } + + if (!cpe || !cpe->core_handle) { + dev_err(rtd->dev, + "%s: Invalid private data\n", + __func__); + return -EINVAL; + } + + if (!lsm_d->lsm_session) { + dev_err(rtd->dev, + "%s: Invalid session data\n", + __func__); + return -EINVAL; + } + + lsm_ops = &cpe->lsm_ops; + afe_ops = &cpe->afe_ops; + session = lsm_d->lsm_session; + if (rtd->cpu_dai) + dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, + substream); + if (!dma_data || !dma_data->dai_channel_ctl) { + dev_err(rtd->dev, + "%s: dma_data is not set\n", + __func__); + return -EINVAL; + } + + if (lab_d->thread_status == MSM_LSM_LAB_THREAD_RUNNING) { + dev_dbg(rtd->dev, "%s: stopping lab thread\n", + __func__); + rc = kthread_stop(session->lsm_lab_thread); + + /* + * kthread_stop returns EINTR if the thread_fn + * was not scheduled before calling kthread_stop. + * In this case, we dont need to wait for lab + * thread to complete as lab thread will not be + * scheduled at all. + */ + if (rc == -EINTR) + goto done; + + /* Wait for the lab thread to exit */ + rc = wait_for_completion_timeout( + &lab_d->thread_complete, + MSM_CPE_LAB_THREAD_TIMEOUT); + if (!rc) { + dev_err(rtd->dev, + "%s: Wait for lab thread timedout\n", + __func__); + return -ETIMEDOUT; + } + } + + rc = lsm_ops->lab_ch_setup(cpe->core_handle, + session, + WCD_CPE_PRE_DISABLE); + if (rc) + dev_err(rtd->dev, + "%s: PRE ch teardown failed, err = %d\n", + __func__, rc); + /* continue with teardown even if any intermediate step fails */ + rc = dma_data->dai_channel_ctl(dma_data, rtd->cpu_dai, false); + if (rc) + dev_err(rtd->dev, + "%s: open data failed %d\n", __func__, rc); + dma_data->ph = 0; + + /* + * Even though LAB stop failed, + * output AFE port needs to be stopped + */ + rc = afe_ops->afe_port_stop(cpe->core_handle, + &session->afe_out_port_cfg); + if (rc) + dev_err(rtd->dev, + "%s: AFE out port stop failed, err = %d\n", + __func__, rc); + + rc = lsm_ops->lab_ch_setup(cpe->core_handle, + session, + WCD_CPE_POST_DISABLE); + if (rc) + dev_err(rtd->dev, + "%s: POST ch teardown failed, err = %d\n", + __func__, rc); + +done: + lab_d->thread_status = MSM_LSM_LAB_THREAD_STOP; + lab_d->buf_idx = 0; + atomic_set(&lab_d->in_count, 0); + lab_d->dma_write = 0; + + return 0; +} + +static int msm_cpe_lab_buf_alloc(struct snd_pcm_substream *substream, + struct cpe_lsm_session *session, + struct msm_slim_dma_data *dma_data) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct cpe_lsm_data *lsm_d = cpe_get_lsm_data(substream); + struct cpe_lsm_lab *lab_d = &lsm_d->lab; + struct cpe_hw_params *hw_params = &lsm_d->hw_params; + struct cpe_data_pcm_buf *pcm_buf = NULL; + int rc = 0; + int dma_alloc = 0; + u32 count = 0; + u32 bufsz, bufcnt; + + if (lab_d->pcm_buf && + lab_d->pcm_buf->mem) { + dev_dbg(rtd->dev, + "%s: LAB buf already allocated\n", + __func__); + goto exit; + } + + bufsz = hw_params->buf_sz; + bufcnt = hw_params->period_count; + + dev_dbg(rtd->dev, + "%s:Buf Size %d Buf count %d\n", + __func__, + bufsz, bufcnt); + + pcm_buf = kzalloc(((sizeof(struct cpe_data_pcm_buf)) * bufcnt), + GFP_KERNEL); + if (!pcm_buf) { + rc = -ENOMEM; + goto exit; + } + + lab_d->pcm_buf = pcm_buf; + dma_alloc = bufsz * bufcnt; + pcm_buf->mem = NULL; + pcm_buf->mem = kzalloc(dma_alloc, GFP_DMA); + if (!pcm_buf->mem) { + rc = -ENOMEM; + goto fail; + } + + pcm_buf->phys = dma_map_single(dma_data->sdev->dev.parent, + pcm_buf->mem, dma_alloc, DMA_BIDIRECTIONAL); + if (dma_mapping_error(dma_data->sdev->dev.parent, pcm_buf->phys)) { + dev_err(rtd->dev, "%s Error mapping DMA buffers\n", __func__); + pcm_buf->phys = (phys_addr_t)NULL; + rc = -EFAULT; + goto fail; + } + + count = 0; + while (count < bufcnt) { + pcm_buf[count].mem = pcm_buf[0].mem + (count * bufsz); + pcm_buf[count].phys = pcm_buf[0].phys + (count * bufsz); + dev_dbg(rtd->dev, + "%s: pcm_buf[%d].mem %pK pcm_buf[%d].phys %pK\n", + __func__, count, + (void *)pcm_buf[count].mem, + count, &(pcm_buf[count].phys)); + count++; + } + + return 0; +fail: + if (pcm_buf && pcm_buf->mem) + kfree(pcm_buf->mem); + kfree(pcm_buf); + lab_d->pcm_buf = NULL; +exit: + return rc; +} + +static int msm_cpe_lab_buf_dealloc(struct snd_pcm_substream *substream, + struct cpe_lsm_session *session, struct msm_slim_dma_data *dma_data) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct cpe_lsm_data *lsm_d = cpe_get_lsm_data(substream); + struct cpe_lsm_lab *lab_d = &lsm_d->lab; + struct cpe_hw_params *hw_params = &lsm_d->hw_params; + int rc = 0; + int dma_alloc = 0; + struct cpe_data_pcm_buf *pcm_buf = NULL; + int bufsz, bufcnt; + + bufsz = hw_params->buf_sz; + bufcnt = hw_params->period_count; + + dev_dbg(rtd->dev, + "%s:Buf Size %d Buf count %d\n", __func__, + bufsz, bufcnt); + + if (bufcnt <= 0 || bufsz <= 0) { + dev_err(rtd->dev, + "%s: Invalid params, bufsz = %u, bufcnt = %u\n", + __func__, bufsz, bufcnt); + return -EINVAL; + } + + pcm_buf = lab_d->pcm_buf; + dma_alloc = bufsz * bufcnt; + if (dma_data && pcm_buf) + if (pcm_buf->phys) + dma_unmap_single(dma_data->sdev->dev.parent, + pcm_buf->phys, dma_alloc, DMA_BIDIRECTIONAL); + if (pcm_buf) + kfree(pcm_buf->mem); + + kfree(pcm_buf); + lab_d->pcm_buf = NULL; + return rc; +} + +/* + * msm_cpe_lab_thread: Initiated on KW detection + * @data: lab data + * + * Start lab thread and call CPE core API for SLIM + * read operations. + */ +static int msm_cpe_lab_thread(void *data) +{ + struct cpe_lsm_data *lsm_d = data; + struct cpe_lsm_session *session = lsm_d->lsm_session; + struct snd_pcm_substream *substream = lsm_d->substream; + struct cpe_lsm_lab *lab_d = &lsm_d->lab; + struct cpe_hw_params *hw_params = &lsm_d->hw_params; + struct cpe_priv *cpe = cpe_get_private_data(substream); + struct wcd_cpe_lsm_ops *lsm_ops; + struct wcd_cpe_afe_ops *afe_ops; + struct cpe_data_pcm_buf *cur_buf, *next_buf; + struct msm_slim_dma_data *dma_data = NULL; + struct snd_soc_pcm_runtime *rtd = NULL; + bool wait_timedout = false; + int rc = 0; + u32 done_len = 0; + u32 buf_count = 0; + u32 prd_cnt; + + allow_signal(SIGKILL); + set_current_state(TASK_INTERRUPTIBLE); + + pr_debug("%s: Lab thread start\n", __func__); + init_completion(&lab_d->comp); + + if (PCM_RUNTIME_CHECK(substream)) { + rc = -EINVAL; + goto done; + } + + if (!cpe || !cpe->core_handle) { + pr_err("%s: Handle to %s is invalid\n", + __func__, + (!cpe) ? "cpe" : "core"); + rc = -EINVAL; + goto done; + } + + rtd = substream->private_data; + if (rtd->cpu_dai) + dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, + substream); + if (!dma_data || !dma_data->dai_channel_ctl) { + pr_err("%s: dma_data is not set\n", __func__); + rc = -EINVAL; + goto done; + } + + lsm_ops = &cpe->lsm_ops; + afe_ops = &cpe->afe_ops; + + rc = lsm_ops->lab_ch_setup(cpe->core_handle, + session, + WCD_CPE_PRE_ENABLE); + if (rc) { + dev_err(rtd->dev, + "%s: PRE ch setup failed, err = %d\n", + __func__, rc); + goto done; + } + + rc = dma_data->dai_channel_ctl(dma_data, rtd->cpu_dai, true); + if (rc) { + dev_err(rtd->dev, + "%s: open data failed %d\n", __func__, rc); + goto done; + } + + dev_dbg(rtd->dev, "%s: Established data channel\n", + __func__); + + init_waitqueue_head(&lab_d->period_wait); + memset(lab_d->pcm_buf[0].mem, 0, lab_d->pcm_size); + + rc = slim_port_xfer(dma_data->sdev, dma_data->ph, + lab_d->pcm_buf[0].mem, + hw_params->buf_sz, &lab_d->comp); + if (rc) { + dev_err(rtd->dev, + "%s: buf[0] slim_port_xfer failed, err = %d\n", + __func__, rc); + goto done; + } + + rc = slim_port_xfer(dma_data->sdev, dma_data->ph, + lab_d->pcm_buf[1].mem, + hw_params->buf_sz, &lab_d->comp); + if (rc) { + dev_err(rtd->dev, + "%s: buf[0] slim_port_xfer failed, err = %d\n", + __func__, rc); + goto done; + } + + cur_buf = &lab_d->pcm_buf[0]; + next_buf = &lab_d->pcm_buf[2]; + prd_cnt = hw_params->period_count; + rc = lsm_ops->lab_ch_setup(cpe->core_handle, + session, + WCD_CPE_POST_ENABLE); + if (rc) { + dev_err(rtd->dev, + "%s: POST ch setup failed, err = %d\n", + __func__, rc); + goto done; + } + + rc = afe_ops->afe_port_start(cpe->core_handle, + &session->afe_out_port_cfg); + if (rc) { + dev_err(rtd->dev, + "%s: AFE out port start failed, err = %d\n", + __func__, rc); + goto done; + } + + while (!kthread_should_stop() && + lab_d->thread_status != MSM_LSM_LAB_THREAD_ERROR) { + + rc = slim_port_xfer(dma_data->sdev, dma_data->ph, + next_buf->mem, + hw_params->buf_sz, &lab_d->comp); + if (rc) { + dev_err(rtd->dev, + "%s: slim_port_xfer failed, err = %d\n", + __func__, rc); + lab_d->thread_status = MSM_LSM_LAB_THREAD_ERROR; + } + + rc = wait_for_completion_timeout(&lab_d->comp, (2 * HZ/10)); + if (!rc) { + dev_err(rtd->dev, + "%s: wait timedout for slim buffer\n", + __func__); + wait_timedout = true; + } else { + wait_timedout = false; + } + + rc = slim_port_get_xfer_status(dma_data->sdev, + dma_data->ph, + &cur_buf->phys, &done_len); + if (rc || + (!rc && wait_timedout)) { + dev_err(rtd->dev, + "%s: xfer_status failure, rc = %d, wait_timedout = %s\n", + __func__, rc, + (wait_timedout ? "true" : "false")); + lab_d->thread_status = MSM_LSM_LAB_THREAD_ERROR; + } + + if (done_len || + ((!done_len) && + lab_d->thread_status == MSM_LSM_LAB_THREAD_ERROR)) { + atomic_inc(&lab_d->in_count); + lab_d->dma_write += snd_pcm_lib_period_bytes(substream); + snd_pcm_period_elapsed(substream); + wake_up(&lab_d->period_wait); + buf_count++; + + cur_buf = &lab_d->pcm_buf[buf_count % prd_cnt]; + next_buf = &lab_d->pcm_buf[(buf_count + 2) % prd_cnt]; + dev_dbg(rtd->dev, + "%s: Cur buf.mem = %pK Next Buf.mem = %pK\n" + " buf count = 0x%x\n", __func__, + cur_buf->mem, next_buf->mem, buf_count); + } else { + dev_err(rtd->dev, + "%s: SB get status, invalid len = 0x%x\n", + __func__, done_len); + } + done_len = 0; + } + +done: + if (rc) + lab_d->thread_status = MSM_LSM_LAB_THREAD_ERROR; + pr_debug("%s: Exit lab_thread, exit_status=%d, thread_status=%d\n", + __func__, rc, lab_d->thread_status); + complete(&lab_d->thread_complete); + + return 0; +} + +/* + * msm_cpe_lsm_open: ASoC call to open the stream + * @substream: substream that is to be opened + * + * Create session data for lsm session and open the lsm session + * on CPE. + */ +static int msm_cpe_lsm_open(struct snd_pcm_substream *substream) +{ + struct cpe_lsm_data *lsm_d; + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct cpe_priv *cpe = cpe_get_private_data(substream); + struct wcd_cpe_lsm_ops *lsm_ops; + int rc = 0; + + if (!cpe || !cpe->codec) { + dev_err(rtd->dev, + "%s: Invalid private data\n", + __func__); + return -EINVAL; + } + + runtime->hw = msm_pcm_hardware_listen; + + rc = snd_pcm_hw_constraint_list(runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + &constraints_sample_rates); + if (rc < 0) { + pr_err("snd_pcm_hw_constraint_list failed rc %d\n", rc); + return -EINVAL; + } + + /* Ensure that buffer size is a multiple of period size */ + rc = snd_pcm_hw_constraint_integer(runtime, + SNDRV_PCM_HW_PARAM_PERIODS); + if (rc < 0) { + pr_err("%s: Unable to set pcm_param_periods, rc %d\n", + __func__, rc); + return -EINVAL; + } + + rc = snd_pcm_hw_constraint_minmax(runtime, + SNDRV_PCM_HW_PARAM_BUFFER_BYTES, + LISTEN_MIN_NUM_PERIODS * LISTEN_MIN_PERIOD_SIZE, + LISTEN_MAX_NUM_PERIODS * LISTEN_MAX_PERIOD_SIZE); + if (rc < 0) { + pr_err("%s: Unable to set pcm constraints, rc %d\n", + __func__, rc); + return -EINVAL; + } + + cpe->core_handle = wcd_cpe_get_core_handle(cpe->codec); + + if (!cpe->core_handle) { + dev_err(rtd->dev, + "%s: Invalid handle to codec core\n", + __func__); + return -EINVAL; + } + + lsm_ops = &cpe->lsm_ops; + lsm_d = kzalloc(sizeof(struct cpe_lsm_data), GFP_KERNEL); + if (!lsm_d) { + dev_err(rtd->dev, + "%s: ENOMEM for lsm session, size = %zd\n", + __func__, sizeof(struct cpe_lsm_data)); + rc = -ENOMEM; + goto fail_return; + } + mutex_init(&lsm_d->lsm_api_lock); + + lsm_d->lsm_session = lsm_ops->lsm_alloc_session(cpe->core_handle, + lsm_d, msm_cpe_process_event_status); + if (!lsm_d->lsm_session) { + dev_err(rtd->dev, + "%s: session allocation failed", + __func__); + rc = -EINVAL; + goto fail_session_alloc; + } + /* Explicitly Assign the LAB thread to STOP state */ + lsm_d->lab.thread_status = MSM_LSM_LAB_THREAD_STOP; + lsm_d->lsm_session->started = false; + lsm_d->substream = substream; + init_waitqueue_head(&lsm_d->lab.period_wait); + lsm_d->cpe_prepared = false; + + dev_dbg(rtd->dev, "%s: allocated session with id = %d\n", + __func__, lsm_d->lsm_session->id); + + + rc = lsm_ops->lsm_open_tx(cpe->core_handle, lsm_d->lsm_session, + LSM_VOICE_WAKEUP_APP_V2, 16000); + if (rc < 0) { + dev_err(rtd->dev, + "%s: OPEN_TX cmd failed, err = %d\n", + __func__, rc); + goto fail_open_tx; + } + + init_waitqueue_head(&lsm_d->event_wait); + atomic_set(&lsm_d->event_avail, 0); + atomic_set(&lsm_d->event_stop, 0); + runtime->private_data = lsm_d; + + return 0; + +fail_open_tx: + lsm_ops->lsm_dealloc_session(cpe->core_handle, lsm_d->lsm_session); + +fail_session_alloc: + mutex_destroy(&lsm_d->lsm_api_lock); + kfree(lsm_d); +fail_return: + return rc; +} + +/* + * msm_cpe_lsm_close: ASoC call to close/cleanup the stream + * @substream: substream that is to be closed + * + * Deallocate the session and release the AFE port. It is not + * required to deregister the sound model as long as we close + * the lsm session on CPE. + */ +static int msm_cpe_lsm_close(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct cpe_lsm_data *lsm_d = cpe_get_lsm_data(substream); + struct cpe_priv *cpe = cpe_get_private_data(substream); + struct wcd_cpe_lsm_ops *lsm_ops; + struct cpe_lsm_session *session; + struct wcd_cpe_afe_ops *afe_ops; + struct wcd_cpe_afe_port_cfg *afe_cfg; + int rc = 0; + + if (!cpe || !cpe->core_handle) { + dev_err(rtd->dev, + "%s: Invalid private data\n", + __func__); + return -EINVAL; + } + + if (!lsm_d || !lsm_d->lsm_session) { + dev_err(rtd->dev, + "%s: Invalid session data\n", + __func__); + return -EINVAL; + } + + lsm_ops = &cpe->lsm_ops; + session = lsm_d->lsm_session; + afe_ops = &cpe->afe_ops; + afe_cfg = &(lsm_d->lsm_session->afe_port_cfg); + + /* + * If driver is closed without stopping LAB, + * explicitly stop LAB before cleaning up the + * driver resources. + */ + rc = msm_cpe_lsm_lab_stop(substream); + if (rc) { + dev_err(rtd->dev, + "%s: Failed to stop lab, error = %d\n", + __func__, rc); + return rc; + } + + rc = msm_cpe_afe_port_cntl(substream, + cpe->core_handle, + afe_ops, afe_cfg, + AFE_CMD_PORT_STOP); + + lsm_d->cpe_prepared = false; + + rc = lsm_ops->lsm_close_tx(cpe->core_handle, session); + if (rc != 0) { + dev_err(rtd->dev, + "%s: lsm_close fail, err = %d\n", + __func__, rc); + return rc; + } + + lsm_ops->lsm_dealloc_session(cpe->core_handle, session); + runtime->private_data = NULL; + mutex_destroy(&lsm_d->lsm_api_lock); + kfree(lsm_d); + + return rc; +} + +static int msm_cpe_lsm_get_conf_levels( + struct cpe_lsm_session *session, + u8 *conf_levels_ptr) +{ + int rc = 0; + + if (session->num_confidence_levels <= 0) { + pr_debug("%s: conf_levels (%u), skip set params\n", + __func__, + session->num_confidence_levels); + goto done; + } + + session->conf_levels = kzalloc(session->num_confidence_levels, + GFP_KERNEL); + if (!session->conf_levels) { + rc = -ENOMEM; + goto done; + } + + if (copy_from_user(session->conf_levels, + conf_levels_ptr, + session->num_confidence_levels)) { + pr_err("%s: copy_from_user failed for confidence levels %u\n", + __func__, session->num_confidence_levels); + kfree(session->conf_levels); + session->conf_levels = NULL; + rc = -EFAULT; + goto done; + } + +done: + return rc; +} + +static int msm_cpe_lsm_validate_out_format( + struct snd_pcm_substream *substream, + struct snd_lsm_output_format_cfg *cfg) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + int rc = 0; + + if (!cfg) { + dev_err(rtd->dev, + "%s: Invalid lsm out cfg\n", __func__); + rc = -EINVAL; + goto done; + } + + if (cfg->format != LSM_OUT_FORMAT_PCM && + cfg->format != LSM_OUT_FORMAT_ADPCM) { + dev_err(rtd->dev, + "%s: Invalid format %u\n", + __func__, cfg->format); + rc = -EINVAL; + goto done; + } + + if (cfg->packing != LSM_OUT_DATA_RAW && + cfg->packing != LSM_OUT_DATA_PACKED) { + dev_err(rtd->dev, + "%s: Invalid packing method %u\n", + __func__, cfg->packing); + rc = -EINVAL; + goto done; + } + + if (cfg->events != LSM_OUT_DATA_EVENTS_DISABLED && + cfg->events != LSM_OUT_DATA_EVENTS_ENABLED) { + dev_err(rtd->dev, + "%s: Invalid events provided %u\n", + __func__, cfg->events); + rc = -EINVAL; + goto done; + } + + if (cfg->mode != LSM_OUT_TRANSFER_MODE_RT && + cfg->mode != LSM_OUT_TRANSFER_MODE_FTRT) { + dev_err(rtd->dev, + "%s: Invalid transfer mode %u\n", + __func__, cfg->mode); + rc = -EINVAL; + goto done; + } + +done: + return rc; +} + +/* + * msm_cpe_lsm_ioctl_shared: Shared IOCTL for this platform driver + * @substream: ASoC substream for which the operation is invoked + * @cmd: command for the ioctl + * @arg: argument for the ioctl + * + * Perform dedicated listen functions like register sound model, + * deregister sound model, etc + * Called with lsm_api_lock acquired. + */ +static int msm_cpe_lsm_ioctl_shared(struct snd_pcm_substream *substream, + unsigned int cmd, void *arg) +{ + struct snd_lsm_sound_model_v2 snd_model; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct cpe_lsm_data *lsm_d = cpe_get_lsm_data(substream); + struct cpe_priv *cpe = cpe_get_private_data(substream); + struct cpe_lsm_session *session; + struct wcd_cpe_lsm_ops *lsm_ops; + struct cpe_lsm_lab *lab_d = &lsm_d->lab; + struct snd_dma_buffer *dma_buf = &substream->dma_buffer; + struct msm_slim_dma_data *dma_data = NULL; + struct snd_lsm_detection_params det_params; + int rc = 0; + + if (!cpe || !cpe->core_handle) { + dev_err(rtd->dev, + "%s: Invalid private data\n", + __func__); + return -EINVAL; + } + + if (!lsm_d || !lsm_d->lsm_session) { + dev_err(rtd->dev, + "%s: Invalid session data\n", + __func__); + return -EINVAL; + } + + session = lsm_d->lsm_session; + lsm_ops = &cpe->lsm_ops; + + switch (cmd) { + case SNDRV_LSM_STOP_LAB: + dev_dbg(rtd->dev, + "%s: %s, lab_enable = %d, lab_thread_ststus = %d\n", + __func__, "SNDRV_LSM_STOP_LAB", + session->lab_enable, + lab_d->thread_status); + + if (session->lab_enable && + lab_d->thread_status != MSM_LSM_LAB_THREAD_STOP) { + atomic_inc(&lab_d->abort_read); + wake_up(&lab_d->period_wait); + rc = msm_cpe_lsm_lab_stop(substream); + if (rc) { + dev_err(rtd->dev, + "%s: stop LAB failed, error = %d\n", + __func__, rc); + return rc; + } + } else if (!session->lab_enable) { + dev_dbg(rtd->dev, + "%s: LAB already stopped\n", + __func__); + } + + break; + + case SNDRV_LSM_LAB_CONTROL: + if (copy_from_user(&session->lab_enable, (void *)arg, + sizeof(u32))) { + dev_err(rtd->dev, + "%s: copy_from_user failed, size %zd\n", + __func__, sizeof(u32)); + return -EFAULT; + } + + dev_dbg(rtd->dev, + "%s: %s, lab_enable = %d\n", + __func__, "SNDRV_LSM_LAB_CONTROL", + session->lab_enable); + if (rtd->cpu_dai) + dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, + substream); + if (!dma_data || !dma_data->dai_channel_ctl) { + dev_err(rtd->dev, + "%s: dma_data is not set\n", __func__); + return -EINVAL; + } + + if (session->lab_enable) { + rc = msm_cpe_lab_buf_alloc(substream, + session, dma_data); + if (rc < 0) { + dev_err(rtd->dev, + "%s: lab buffer alloc failed, err = %d\n", + __func__, rc); + return rc; + } + + dma_buf->dev.type = SNDRV_DMA_TYPE_DEV; + dma_buf->dev.dev = substream->pcm->card->dev; + dma_buf->private_data = NULL; + dma_buf->area = lab_d->pcm_buf[0].mem; + dma_buf->addr = lab_d->pcm_buf[0].phys; + dma_buf->bytes = (lsm_d->hw_params.buf_sz * + lsm_d->hw_params.period_count); + init_completion(&lab_d->thread_complete); + snd_pcm_set_runtime_buffer(substream, + &substream->dma_buffer); + rc = lsm_ops->lsm_lab_control(cpe->core_handle, + session, true); + if (rc < 0) { + dev_err(rtd->dev, + "%s: Lab Enable Failed rc %d\n", + __func__, rc); + return rc; + } + } else { + /* + * It is possible that lab is still enabled + * when trying to de-allocate the lab buffer. + * Make sure to disable lab before de-allocating + * the lab buffer. + */ + rc = msm_cpe_lsm_lab_stop(substream); + if (rc < 0) { + dev_err(rtd->dev, + "%s: LAB stop failed, error = %d\n", + __func__, rc); + return rc; + } + /* + * Buffer has to be de-allocated even if + * lab_control failed. + */ + rc = msm_cpe_lab_buf_dealloc(substream, + session, dma_data); + if (rc < 0) { + dev_err(rtd->dev, + "%s: lab buffer free failed, err = %d\n", + __func__, rc); + return rc; + } + } + break; + case SNDRV_LSM_REG_SND_MODEL_V2: + dev_dbg(rtd->dev, + "%s: %s\n", + __func__, "SNDRV_LSM_REG_SND_MODEL_V2"); + + memcpy(&snd_model, arg, + sizeof(struct snd_lsm_sound_model_v2)); + + session->num_confidence_levels = + snd_model.num_confidence_levels; + rc = msm_cpe_lsm_get_conf_levels(session, + snd_model.confidence_level); + if (rc) { + dev_err(rtd->dev, + "%s: %s get_conf_levels fail, err = %d\n", + __func__, "SNDRV_LSM_REG_SND_MODEL_V2", + rc); + break; + } + + session->snd_model_data = kzalloc(snd_model.data_size, + GFP_KERNEL); + if (!session->snd_model_data) { + kfree(session->conf_levels); + session->conf_levels = NULL; + return -ENOMEM; + } + session->snd_model_size = snd_model.data_size; + + if (copy_from_user(session->snd_model_data, + snd_model.data, snd_model.data_size)) { + dev_err(rtd->dev, + "%s: copy_from_user failed for snd_model\n", + __func__); + kfree(session->conf_levels); + kfree(session->snd_model_data); + session->conf_levels = NULL; + session->snd_model_data = NULL; + return -EFAULT; + } + + rc = lsm_ops->lsm_shmem_alloc(cpe->core_handle, session, + session->snd_model_size); + if (rc != 0) { + dev_err(rtd->dev, + "%s: shared memory allocation failed, err = %d\n", + __func__, rc); + kfree(session->snd_model_data); + kfree(session->conf_levels); + session->snd_model_data = NULL; + session->conf_levels = NULL; + return rc; + } + + rc = lsm_ops->lsm_register_snd_model(cpe->core_handle, session, + snd_model.detection_mode, + snd_model.detect_failure); + if (rc != 0) { + dev_err(rtd->dev, + "%s: snd_model_reg failed, err = %d\n", + __func__, rc); + lsm_ops->lsm_shmem_dealloc(cpe->core_handle, session); + kfree(session->snd_model_data); + kfree(session->conf_levels); + session->snd_model_data = NULL; + session->conf_levels = NULL; + return rc; + } + + break; + + case SNDRV_LSM_DEREG_SND_MODEL: + dev_dbg(rtd->dev, + "%s: %s\n", + __func__, "SNDRV_LSM_DEREG_SND_MODEL"); + + if (session->lab_enable) { + /* + * It is possible that lab is still enabled + * when trying to deregister sound model. + * Make sure to disable lab before de-allocating + * the lab buffer. + */ + rc = msm_cpe_lsm_lab_stop(substream); + if (rc) { + dev_err(rtd->dev, + "%s: LAB stop failed, error = %d\n", + __func__, rc); + return rc; + } + + rc = lsm_ops->lsm_lab_control(cpe->core_handle, + session, false); + if (rc) + dev_err(rtd->dev, + "%s: Lab Disable Failed rc %d\n", + __func__, rc); + + dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, + substream); + if (!dma_data || !dma_data->dai_channel_ctl) + dev_err(rtd->dev, + "%s: dma_data is not set\n", __func__); + + /* + * Buffer has to be de-allocated even if + * lab_control failed and/or dma data is invalid. + */ + rc = msm_cpe_lab_buf_dealloc(substream, + session, dma_data); + if (rc < 0) + dev_err(rtd->dev, + "%s: lab buffer free failed, err = %d\n", + __func__, rc); + } + + rc = lsm_ops->lsm_deregister_snd_model( + cpe->core_handle, session); + if (rc != 0) { + dev_err(rtd->dev, + "%s: snd_model de-reg failed, err = %d\n", + __func__, rc); + return rc; + } + + kfree(session->snd_model_data); + kfree(session->conf_levels); + session->snd_model_data = NULL; + session->conf_levels = NULL; + + rc = lsm_ops->lsm_shmem_dealloc(cpe->core_handle, session); + if (rc != 0) { + dev_err(rtd->dev, + "%s: LSM shared memory dealloc failed, err = %d\n", + __func__, rc); + return rc; + } + + break; + + case SNDRV_LSM_EVENT_STATUS: + case SNDRV_LSM_EVENT_STATUS_V3: { + struct snd_lsm_event_status *user; + struct snd_lsm_event_status_v3 *user_v3; + + dev_dbg(rtd->dev, + "%s: %s\n", + __func__, "SNDRV_LSM_EVENT_STATUS(_V3)"); + if (!arg) { + dev_err(rtd->dev, + "%s: Invalid argument to ioctl %s\n", + __func__, + "SNDRV_LSM_EVENT_STATUS(_V3)"); + return -EINVAL; + } + + /* + * Release the api lock before wait to allow + * other IOCTLs to be invoked while waiting + * for event + */ + MSM_CPE_LSM_REL_LOCK(&lsm_d->lsm_api_lock, + "lsm_api_lock"); + + rc = wait_event_freezable(lsm_d->event_wait, + (atomic_read(&lsm_d->event_avail) == 1) || + (atomic_read(&lsm_d->event_stop) == 1)); + + MSM_CPE_LSM_GRAB_LOCK(&lsm_d->lsm_api_lock, + "lsm_api_lock"); + + if (!rc) { + if (atomic_read(&lsm_d->event_avail) == 1) { + rc = 0; + atomic_set(&lsm_d->event_avail, 0); + + if (cmd == SNDRV_LSM_EVENT_STATUS) { + user = arg; + if (lsm_d->ev_det_pld_size > + user->payload_size) { + dev_err(rtd->dev, + "%s: avail pld_bytes = %u, needed = %u\n", + __func__, + user->payload_size, + lsm_d->ev_det_pld_size); + return -EINVAL; + } + + user->status = lsm_d->ev_det_status; + user->payload_size = + lsm_d->ev_det_pld_size; + memcpy(user->payload, + lsm_d->ev_det_payload, + lsm_d->ev_det_pld_size); + } else { + user_v3 = arg; + if (lsm_d->ev_det_pld_size > + user_v3->payload_size) { + dev_err(rtd->dev, + "%s: avail pld_bytes = %u, needed = %u\n", + __func__, + user_v3->payload_size, + lsm_d->ev_det_pld_size); + return -EINVAL; + } + /* event status timestamp not supported + * on CPE mode. Set msw and lsw to 0. + */ + user_v3->timestamp_lsw = 0; + user_v3->timestamp_msw = 0; + user_v3->status = lsm_d->ev_det_status; + user_v3->payload_size = + lsm_d->ev_det_pld_size; + memcpy(user_v3->payload, + lsm_d->ev_det_payload, + lsm_d->ev_det_pld_size); + } + } else if (atomic_read(&lsm_d->event_stop) == 1) { + dev_dbg(rtd->dev, + "%s: wait_aborted\n", __func__); + if (cmd == SNDRV_LSM_EVENT_STATUS) { + user = arg; + user->payload_size = 0; + } else { + user_v3 = arg; + user_v3->payload_size = 0; + } + rc = 0; + } + } + } + break; + + case SNDRV_LSM_ABORT_EVENT: + dev_dbg(rtd->dev, + "%s: %s\n", + __func__, "SNDRV_LSM_ABORT_EVENT"); + atomic_set(&lsm_d->event_stop, 1); + wake_up(&lsm_d->event_wait); + break; + + case SNDRV_LSM_START: + dev_dbg(rtd->dev, + "%s: %s\n", + __func__, "SNDRV_LSM_START"); + rc = lsm_ops->lsm_start(cpe->core_handle, session); + if (rc != 0) { + dev_err(rtd->dev, + "%s: lsm_start fail, err = %d\n", + __func__, rc); + return rc; + } + session->started = true; + break; + + case SNDRV_LSM_STOP: + dev_dbg(rtd->dev, + "%s: %s, lab_enable = %d, lab_thread_status = %d\n", + __func__, "SNDRV_LSM_STOP", + session->lab_enable, + lab_d->thread_status); + if ((session->lab_enable && + lab_d->thread_status == + MSM_LSM_LAB_THREAD_RUNNING)) { + /* Explicitly stop LAB */ + rc = msm_cpe_lsm_lab_stop(substream); + if (rc) { + dev_err(rtd->dev, + "%s: lab_stop failed, err = %d\n", + __func__, rc); + return rc; + } + } + + rc = lsm_ops->lsm_stop(cpe->core_handle, session); + if (rc != 0) { + dev_err(rtd->dev, + "%s: lsm_stop fail err = %d\n", + __func__, rc); + + return rc; + } + session->started = false; + break; + + case SNDRV_LSM_SET_PARAMS: + memcpy(&det_params, arg, + sizeof(det_params)); + if (det_params.num_confidence_levels <= 0) { + dev_err(rtd->dev, + "%s: %s: Invalid confidence levels %u\n", + __func__, "SNDRV_LSM_SET_PARAMS", + det_params.num_confidence_levels); + return -EINVAL; + } + + session->num_confidence_levels = + det_params.num_confidence_levels; + rc = msm_cpe_lsm_get_conf_levels(session, + det_params.conf_level); + if (rc) { + dev_err(rtd->dev, + "%s: %s get_conf_levels fail, err = %d\n", + __func__, "SNDRV_LSM_SET_PARAMS", + rc); + break; + } + + rc = lsm_ops->lsm_set_data(cpe->core_handle, session, + det_params.detect_mode, + det_params.detect_failure); + if (rc) { + dev_err(rtd->dev, + "%s: lsm_set_data failed, err = %d\n", + __func__, rc); + return rc; + } + + kfree(session->conf_levels); + session->conf_levels = NULL; + + break; + + case SNDRV_LSM_OUT_FORMAT_CFG: { + struct snd_lsm_output_format_cfg u_fmt_cfg; + + if (!arg) { + dev_err(rtd->dev, + "%s: Invalid argument to ioctl %s\n", + __func__, "SNDRV_LSM_OUT_FORMAT_CFG"); + return -EINVAL; + } + + if (copy_from_user(&u_fmt_cfg, arg, + sizeof(u_fmt_cfg))) { + dev_err(rtd->dev, + "%s: copy_from_user failed for out_fmt_cfg\n", + __func__); + return -EFAULT; + } + + if (msm_cpe_lsm_validate_out_format(substream, + &u_fmt_cfg)) + return -EINVAL; + + session->out_fmt_cfg.format = u_fmt_cfg.format; + session->out_fmt_cfg.pack_mode = u_fmt_cfg.packing; + session->out_fmt_cfg.data_path_events = + u_fmt_cfg.events; + session->out_fmt_cfg.transfer_mode = u_fmt_cfg.mode; + + rc = lsm_ops->lsm_set_fmt_cfg(cpe->core_handle, + session); + if (rc) { + dev_err(rtd->dev, + "%s: lsm_set_fmt_cfg failed, err = %d\n", + __func__, rc); + return rc; + } + } + break; + + case SNDRV_LSM_SET_PORT: { + u32 port_id = cpe->input_port_id; + + dev_dbg(rtd->dev, "%s: %s\n", __func__, "SNDRV_LSM_SET_PORT"); + rc = lsm_ops->lsm_set_port(cpe->core_handle, session, &port_id); + if (rc) { + dev_err(rtd->dev, + "%s: lsm_set_port failed, err = %d\n", + __func__, rc); + return rc; + } + } + break; + + default: + dev_dbg(rtd->dev, + "%s: Default snd_lib_ioctl cmd 0x%x\n", + __func__, cmd); + rc = snd_pcm_lib_ioctl(substream, cmd, arg); + } + + return rc; +} + +static int msm_cpe_lsm_lab_start(struct snd_pcm_substream *substream, + u16 event_det_status) +{ + struct snd_soc_pcm_runtime *rtd; + struct cpe_lsm_data *lsm_d = NULL; + struct cpe_priv *cpe = NULL; + struct cpe_lsm_session *session = NULL; + struct cpe_lsm_lab *lab_d = NULL; + struct cpe_hw_params *hw_params; + struct wcd_cpe_lsm_ops *lsm_ops; + struct wcd_cpe_afe_ops *afe_ops; + struct wcd_cpe_afe_port_cfg *out_port; + int rc; + + if (!substream || !substream->private_data) { + pr_err("%s: invalid substream (%pK)\n", + __func__, substream); + return -EINVAL; + } + + rtd = substream->private_data; + lsm_d = cpe_get_lsm_data(substream); + cpe = cpe_get_private_data(substream); + + if (!cpe || !cpe->core_handle) { + dev_err(rtd->dev, + "%s: Invalid private data\n", + __func__); + return -EINVAL; + } + + if (!lsm_d || !lsm_d->lsm_session) { + dev_err(rtd->dev, + "%s: Invalid session data\n", + __func__); + return -EINVAL; + } + + session = lsm_d->lsm_session; + lsm_ops = &cpe->lsm_ops; + lab_d = &lsm_d->lab; + afe_ops = &cpe->afe_ops; + hw_params = &lsm_d->hw_params; + + if (!session->started) { + dev_dbg(rtd->dev, + "%s: Session is stopped, cannot start LAB\n", + __func__); + return 0; + } + + reinit_completion(&lab_d->thread_complete); + + if (session->lab_enable && + event_det_status == + LSM_VOICE_WAKEUP_STATUS_DETECTED) { + out_port = &session->afe_out_port_cfg; + out_port->port_id = session->afe_out_port_id; + out_port->bit_width = hw_params->sample_size; + out_port->num_channels = hw_params->channels; + out_port->sample_rate = hw_params->sample_rate; + dev_dbg(rtd->dev, "%s: port_id= %u, bit_width= %u, rate= %u\n", + __func__, out_port->port_id, out_port->bit_width, + out_port->sample_rate); + + rc = afe_ops->afe_port_cmd_cfg(cpe->core_handle, + out_port); + if (rc) { + dev_err(rtd->dev, + "%s: Failed afe generic config v2, err = %d\n", + __func__, rc); + return rc; + } + + atomic_set(&lab_d->abort_read, 0); + dev_dbg(rtd->dev, + "%s: KW detected, scheduling LAB thread\n", + __func__); + + /* + * Even though thread might be only scheduled and + * not currently running, mark the internal driver + * status to running so driver can cancel this thread + * if it needs to before the thread gets chance to run. + */ + lab_d->thread_status = MSM_LSM_LAB_THREAD_RUNNING; + session->lsm_lab_thread = kthread_run( + msm_cpe_lab_thread, + lsm_d, + "lab_thread"); + } + + return 0; +} + +static bool msm_cpe_lsm_is_valid_stream(struct snd_pcm_substream *substream, + const char *func) +{ + struct snd_soc_pcm_runtime *rtd; + struct cpe_lsm_data *lsm_d = NULL; + struct cpe_priv *cpe = NULL; + struct cpe_lsm_session *session = NULL; + struct wcd_cpe_lsm_ops *lsm_ops; + + if (!substream || !substream->private_data) { + pr_err("%s: invalid substream (%pK)\n", + func, substream); + return false; + } + + rtd = substream->private_data; + lsm_d = cpe_get_lsm_data(substream); + cpe = cpe_get_private_data(substream); + + if (!cpe || !cpe->core_handle) { + dev_err(rtd->dev, + "%s: Invalid private data\n", + func); + return false; + } + + if (!lsm_d || !lsm_d->lsm_session) { + dev_err(rtd->dev, + "%s: Invalid session data\n", + func); + return false; + } + + session = lsm_d->lsm_session; + lsm_ops = &cpe->lsm_ops; + + if (!lsm_ops) { + dev_err(rtd->dev, + "%s: Invalid lsm_ops\n", func); + return false; + } + + return true; +} + +static int msm_cpe_lsm_set_epd(struct snd_pcm_substream *substream, + struct lsm_params_info *p_info) +{ + struct snd_soc_pcm_runtime *rtd; + struct cpe_lsm_data *lsm_d = NULL; + struct cpe_priv *cpe = NULL; + struct cpe_lsm_session *session = NULL; + struct wcd_cpe_lsm_ops *lsm_ops; + struct snd_lsm_ep_det_thres epd_thres; + int rc; + + if (!msm_cpe_lsm_is_valid_stream(substream, __func__)) + return -EINVAL; + + rtd = substream->private_data; + lsm_d = cpe_get_lsm_data(substream); + cpe = cpe_get_private_data(substream); + session = lsm_d->lsm_session; + lsm_ops = &cpe->lsm_ops; + + if (p_info->param_size != sizeof(epd_thres)) { + dev_err(rtd->dev, + "%s: Invalid param_size %d\n", + __func__, p_info->param_size); + rc = -EINVAL; + goto done; + } + + if (copy_from_user(&epd_thres, p_info->param_data, + p_info->param_size)) { + dev_err(rtd->dev, + "%s: copy_from_user failed, size = %d\n", + __func__, p_info->param_size); + rc = -EFAULT; + goto done; + } + + rc = lsm_ops->lsm_set_one_param(cpe->core_handle, + session, p_info, &epd_thres, + LSM_ENDPOINT_DETECT_THRESHOLD); + if (unlikely(rc)) + dev_err(rtd->dev, + "%s: set_one_param(epd_threshold) failed, rc %d\n", + __func__, rc); +done: + return rc; +} + +static int msm_cpe_lsm_set_mode(struct snd_pcm_substream *substream, + struct lsm_params_info *p_info) +{ + struct snd_soc_pcm_runtime *rtd; + struct cpe_lsm_data *lsm_d = NULL; + struct cpe_priv *cpe = NULL; + struct cpe_lsm_session *session = NULL; + struct wcd_cpe_lsm_ops *lsm_ops; + struct snd_lsm_detect_mode det_mode; + int rc; + + if (!msm_cpe_lsm_is_valid_stream(substream, __func__)) + return -EINVAL; + + rtd = substream->private_data; + lsm_d = cpe_get_lsm_data(substream); + cpe = cpe_get_private_data(substream); + session = lsm_d->lsm_session; + lsm_ops = &cpe->lsm_ops; + + if (p_info->param_size != sizeof(det_mode)) { + dev_err(rtd->dev, + "%s: Invalid param_size %d\n", + __func__, p_info->param_size); + rc = -EINVAL; + goto done; + } + + if (copy_from_user(&det_mode, p_info->param_data, + p_info->param_size)) { + dev_err(rtd->dev, + "%s: copy_from_user failed, size = %d\n", + __func__, p_info->param_size); + rc = -EFAULT; + goto done; + } + + rc = lsm_ops->lsm_set_one_param(cpe->core_handle, + session, p_info, &det_mode, + LSM_OPERATION_MODE); + if (unlikely(rc)) + dev_err(rtd->dev, + "%s: set_one_param(epd_threshold) failed, rc %d\n", + __func__, rc); +done: + return rc; +} + +static int msm_cpe_lsm_set_gain(struct snd_pcm_substream *substream, + struct lsm_params_info *p_info) +{ + struct snd_soc_pcm_runtime *rtd; + struct cpe_lsm_data *lsm_d = NULL; + struct cpe_priv *cpe = NULL; + struct cpe_lsm_session *session = NULL; + struct wcd_cpe_lsm_ops *lsm_ops; + struct snd_lsm_gain gain; + int rc; + + if (!msm_cpe_lsm_is_valid_stream(substream, __func__)) + return -EINVAL; + + rtd = substream->private_data; + lsm_d = cpe_get_lsm_data(substream); + cpe = cpe_get_private_data(substream); + session = lsm_d->lsm_session; + lsm_ops = &cpe->lsm_ops; + + if (p_info->param_size != sizeof(gain)) { + dev_err(rtd->dev, + "%s: Invalid param_size %d\n", + __func__, p_info->param_size); + rc = -EINVAL; + goto done; + } + + if (copy_from_user(&gain, p_info->param_data, + p_info->param_size)) { + dev_err(rtd->dev, + "%s: copy_from_user failed, size = %d\n", + __func__, p_info->param_size); + rc = -EFAULT; + goto done; + } + + rc = lsm_ops->lsm_set_one_param(cpe->core_handle, + session, p_info, &gain, + LSM_GAIN); + if (unlikely(rc)) + dev_err(rtd->dev, + "%s: set_one_param(epd_threshold) failed, rc %d\n", + __func__, rc); +done: + return rc; + +} + +static int msm_cpe_lsm_set_conf(struct snd_pcm_substream *substream, + struct lsm_params_info *p_info) +{ + struct snd_soc_pcm_runtime *rtd; + struct cpe_lsm_data *lsm_d = NULL; + struct cpe_priv *cpe = NULL; + struct cpe_lsm_session *session = NULL; + struct wcd_cpe_lsm_ops *lsm_ops; + int rc; + + if (!msm_cpe_lsm_is_valid_stream(substream, __func__)) + return -EINVAL; + + rtd = substream->private_data; + lsm_d = cpe_get_lsm_data(substream); + cpe = cpe_get_private_data(substream); + session = lsm_d->lsm_session; + lsm_ops = &cpe->lsm_ops; + + session->num_confidence_levels = + p_info->param_size; + rc = msm_cpe_lsm_get_conf_levels(session, + p_info->param_data); + if (rc) { + dev_err(rtd->dev, + "%s: get_conf_levels failed, err = %d\n", + __func__, rc); + goto done; + } + + rc = lsm_ops->lsm_set_one_param(cpe->core_handle, + session, p_info, NULL, + LSM_MIN_CONFIDENCE_LEVELS); + if (unlikely(rc)) + dev_err(rtd->dev, + "%s: set_one_param(conf_levels) failed, rc %d\n", + __func__, rc); +done: + return rc; +} + +static int msm_cpe_lsm_reg_model(struct snd_pcm_substream *substream, + struct lsm_params_info *p_info) +{ + struct snd_soc_pcm_runtime *rtd; + struct cpe_lsm_data *lsm_d = NULL; + struct cpe_priv *cpe = NULL; + struct cpe_lsm_session *session = NULL; + struct wcd_cpe_lsm_ops *lsm_ops; + int rc; + size_t offset; + u8 *snd_model_ptr; + + if (!msm_cpe_lsm_is_valid_stream(substream, __func__)) + return -EINVAL; + + rtd = substream->private_data; + lsm_d = cpe_get_lsm_data(substream); + cpe = cpe_get_private_data(substream); + session = lsm_d->lsm_session; + lsm_ops = &cpe->lsm_ops; + + lsm_ops->lsm_get_snd_model_offset(cpe->core_handle, + session, &offset); + /* Check if 'p_info->param_size + offset' crosses U32_MAX. */ + if (p_info->param_size > U32_MAX - offset) { + dev_err(rtd->dev, + "%s: Invalid param_size %d\n", + __func__, p_info->param_size); + return -EINVAL; + } + session->snd_model_size = p_info->param_size + offset; + + session->snd_model_data = vzalloc(session->snd_model_size); + if (!session->snd_model_data) + return -ENOMEM; + + snd_model_ptr = ((u8 *) session->snd_model_data) + offset; + + if (copy_from_user(snd_model_ptr, + p_info->param_data, p_info->param_size)) { + dev_err(rtd->dev, + "%s: copy_from_user for snd_model failed\n", + __func__); + rc = -EFAULT; + goto free_snd_model_data; + } + + rc = lsm_ops->lsm_shmem_alloc(cpe->core_handle, session, + session->snd_model_size); + if (rc != 0) { + dev_err(rtd->dev, + "%s: shared memory allocation failed, err = %d\n", + __func__, rc); + rc = -EINVAL; + goto free_snd_model_data; + } + + rc = lsm_ops->lsm_set_one_param(cpe->core_handle, + session, p_info, NULL, + LSM_REG_SND_MODEL); + if (unlikely(rc)) { + dev_err(rtd->dev, + "%s: set_one_param(snd_model) failed, rc %d\n", + __func__, rc); + goto dealloc_shmem; + } + return 0; + +dealloc_shmem: + lsm_ops->lsm_shmem_dealloc(cpe->core_handle, session); + +free_snd_model_data: + vfree(session->snd_model_data); + return rc; +} + +static int msm_cpe_lsm_dereg_model(struct snd_pcm_substream *substream, + struct lsm_params_info *p_info) +{ + struct snd_soc_pcm_runtime *rtd; + struct cpe_lsm_data *lsm_d = NULL; + struct cpe_priv *cpe = NULL; + struct cpe_lsm_session *session = NULL; + struct wcd_cpe_lsm_ops *lsm_ops; + int rc; + + if (!msm_cpe_lsm_is_valid_stream(substream, __func__)) + return -EINVAL; + + rtd = substream->private_data; + lsm_d = cpe_get_lsm_data(substream); + cpe = cpe_get_private_data(substream); + session = lsm_d->lsm_session; + lsm_ops = &cpe->lsm_ops; + + rc = lsm_ops->lsm_set_one_param(cpe->core_handle, + session, p_info, NULL, + LSM_DEREG_SND_MODEL); + if (rc) + dev_err(rtd->dev, + "%s: dereg_snd_model failed\n", + __func__); + return lsm_ops->lsm_shmem_dealloc(cpe->core_handle, session); +} + +static int msm_cpe_lsm_set_custom(struct snd_pcm_substream *substream, + struct lsm_params_info *p_info) +{ + struct snd_soc_pcm_runtime *rtd; + struct cpe_lsm_data *lsm_d = NULL; + struct cpe_priv *cpe = NULL; + struct cpe_lsm_session *session = NULL; + struct wcd_cpe_lsm_ops *lsm_ops; + u8 *data; + int rc; + + if (!msm_cpe_lsm_is_valid_stream(substream, __func__)) + return -EINVAL; + + rtd = substream->private_data; + lsm_d = cpe_get_lsm_data(substream); + cpe = cpe_get_private_data(substream); + session = lsm_d->lsm_session; + lsm_ops = &cpe->lsm_ops; + + if (p_info->param_size > MSM_CPE_MAX_CUSTOM_PARAM_SIZE) { + dev_err(rtd->dev, + "%s: invalid size %d, max allowed %d\n", + __func__, p_info->param_size, + MSM_CPE_MAX_CUSTOM_PARAM_SIZE); + return -EINVAL; + } + + data = kzalloc(p_info->param_size, GFP_KERNEL); + if (!data) + return -ENOMEM; + + if (copy_from_user(data, p_info->param_data, + p_info->param_size)) { + dev_err(rtd->dev, + "%s: copy_from_user failed for custom params, size = %d\n", + __func__, p_info->param_size); + rc = -EFAULT; + goto err_ret; + } + + rc = lsm_ops->lsm_set_one_param(cpe->core_handle, + session, p_info, data, + LSM_CUSTOM_PARAMS); + if (rc) + dev_err(rtd->dev, + "%s: custom_params failed, err = %d\n", + __func__, rc); +err_ret: + kfree(data); + return rc; +} + +static int msm_cpe_lsm_process_params(struct snd_pcm_substream *substream, + struct snd_lsm_module_params *p_data, + void *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct lsm_params_info *p_info; + int i; + int rc = 0; + + p_info = (struct lsm_params_info *) params; + + for (i = 0; i < p_data->num_params; i++) { + dev_dbg(rtd->dev, + "%s: param (%d), module_id = 0x%x, param_id = 0x%x, param_size = 0x%x, param_type = 0x%x\n", + __func__, i, p_info->module_id, + p_info->param_id, p_info->param_size, + p_info->param_type); + + switch (p_info->param_type) { + case LSM_ENDPOINT_DETECT_THRESHOLD: + rc = msm_cpe_lsm_set_epd(substream, p_info); + break; + case LSM_OPERATION_MODE: + rc = msm_cpe_lsm_set_mode(substream, p_info); + break; + case LSM_GAIN: + rc = msm_cpe_lsm_set_gain(substream, p_info); + break; + case LSM_MIN_CONFIDENCE_LEVELS: + rc = msm_cpe_lsm_set_conf(substream, p_info); + break; + case LSM_REG_SND_MODEL: + rc = msm_cpe_lsm_reg_model(substream, p_info); + break; + case LSM_DEREG_SND_MODEL: + rc = msm_cpe_lsm_dereg_model(substream, p_info); + break; + case LSM_CUSTOM_PARAMS: + rc = msm_cpe_lsm_set_custom(substream, p_info); + break; + default: + dev_err(rtd->dev, + "%s: Invalid param_type %d\n", + __func__, p_info->param_type); + rc = -EINVAL; + break; + } + if (rc) { + pr_err("%s: set_param fail for param_type %d\n", + __func__, p_info->param_type); + return rc; + } + + p_info++; + } + + return rc; +} + +static int msm_cpe_lsm_ioctl(struct snd_pcm_substream *substream, + unsigned int cmd, void *arg) +{ + int err = 0; + struct snd_soc_pcm_runtime *rtd; + struct cpe_priv *cpe = NULL; + struct cpe_lsm_data *lsm_d = NULL; + struct cpe_lsm_session *session = NULL; + struct wcd_cpe_lsm_ops *lsm_ops; + + if (!substream || !substream->private_data) { + pr_err("%s: invalid substream (%pK)\n", + __func__, substream); + return -EINVAL; + } + + rtd = substream->private_data; + lsm_d = cpe_get_lsm_data(substream); + cpe = cpe_get_private_data(substream); + + if (!cpe || !cpe->core_handle) { + dev_err(rtd->dev, + "%s: Invalid private data\n", + __func__); + return -EINVAL; + } + + if (!lsm_d || !lsm_d->lsm_session) { + dev_err(rtd->dev, + "%s: Invalid session data\n", + __func__); + return -EINVAL; + } + + MSM_CPE_LSM_GRAB_LOCK(&lsm_d->lsm_api_lock, + "lsm_api_lock"); + + session = lsm_d->lsm_session; + lsm_ops = &cpe->lsm_ops; + + switch (cmd) { + case SNDRV_LSM_REG_SND_MODEL_V2: { + struct snd_lsm_sound_model_v2 snd_model; + + if (session->is_topology_used) { + dev_err(rtd->dev, + "%s: %s: not supported if using topology\n", + __func__, "LSM_REG_SND_MODEL_V2"); + err = -EINVAL; + goto done; + } + + if (copy_from_user(&snd_model, (void *)arg, + sizeof(struct snd_lsm_sound_model_v2))) { + dev_err(rtd->dev, + "%s: copy from user failed, size %zd\n", + __func__, + sizeof(struct snd_lsm_sound_model_v2)); + err = -EFAULT; + goto done; + } + + err = msm_cpe_lsm_ioctl_shared(substream, cmd, + &snd_model); + } + break; + case SNDRV_LSM_EVENT_STATUS: { + struct snd_lsm_event_status u_event_status; + struct snd_lsm_event_status *event_status = NULL; + int u_pld_size = 0; + + if (copy_from_user(&u_event_status, (void *)arg, + sizeof(struct snd_lsm_event_status))) { + dev_err(rtd->dev, + "%s: event status copy from user failed, size %zd\n", + __func__, + sizeof(struct snd_lsm_event_status)); + err = -EFAULT; + goto done; + } + + if (u_event_status.payload_size > + LISTEN_MAX_STATUS_PAYLOAD_SIZE) { + dev_err(rtd->dev, + "%s: payload_size %d is invalid, max allowed = %d\n", + __func__, u_event_status.payload_size, + LISTEN_MAX_STATUS_PAYLOAD_SIZE); + err = -EINVAL; + goto done; + } + + u_pld_size = sizeof(struct snd_lsm_event_status) + + u_event_status.payload_size; + + event_status = kzalloc(u_pld_size, GFP_KERNEL); + if (!event_status) { + err = -ENOMEM; + goto done; + } else { + event_status->payload_size = + u_event_status.payload_size; + err = msm_cpe_lsm_ioctl_shared(substream, + cmd, event_status); + } + + if (!err && copy_to_user(arg, event_status, u_pld_size)) { + dev_err(rtd->dev, + "%s: copy to user failed\n", + __func__); + kfree(event_status); + err = -EFAULT; + goto done; + } + + msm_cpe_lsm_lab_start(substream, event_status->status); + msm_cpe_process_event_status_done(lsm_d); + kfree(event_status); + } + break; + case SNDRV_LSM_EVENT_STATUS_V3: { + struct snd_lsm_event_status_v3 u_event_status; + struct snd_lsm_event_status_v3 *event_status = NULL; + int u_pld_size = 0; + + if (copy_from_user(&u_event_status, (void *)arg, + sizeof(struct snd_lsm_event_status_v3))) { + dev_err(rtd->dev, + "%s: event status copy from user failed, size %zd\n", + __func__, + sizeof(struct snd_lsm_event_status_v3)); + err = -EFAULT; + goto done; + } + + if (u_event_status.payload_size > + LISTEN_MAX_STATUS_PAYLOAD_SIZE) { + dev_err(rtd->dev, + "%s: payload_size %d is invalid, max allowed = %d\n", + __func__, u_event_status.payload_size, + LISTEN_MAX_STATUS_PAYLOAD_SIZE); + err = -EINVAL; + goto done; + } + + u_pld_size = sizeof(struct snd_lsm_event_status_v3) + + u_event_status.payload_size; + + event_status = kzalloc(u_pld_size, GFP_KERNEL); + if (!event_status) { + err = -ENOMEM; + goto done; + } else { + event_status->payload_size = + u_event_status.payload_size; + err = msm_cpe_lsm_ioctl_shared(substream, + cmd, event_status); + } + + if (!err && copy_to_user(arg, event_status, u_pld_size)) { + dev_err(rtd->dev, + "%s: copy to user failed\n", + __func__); + kfree(event_status); + err = -EFAULT; + goto done; + } + + msm_cpe_lsm_lab_start(substream, event_status->status); + msm_cpe_process_event_status_done(lsm_d); + kfree(event_status); + } + break; + case SNDRV_LSM_SET_PARAMS: { + struct snd_lsm_detection_params det_params; + + if (session->is_topology_used) { + dev_err(rtd->dev, + "%s: %s: not supported if using topology\n", + __func__, "SNDRV_LSM_SET_PARAMS"); + err = -EINVAL; + goto done; + } + + if (copy_from_user(&det_params, (void *) arg, + sizeof(det_params))) { + dev_err(rtd->dev, + "%s: %s: copy_from_user failed, size = %zd\n", + __func__, "SNDRV_LSM_SET_PARAMS", + sizeof(det_params)); + err = -EFAULT; + goto done; + } + + err = msm_cpe_lsm_ioctl_shared(substream, cmd, + &det_params); + } + break; + + case SNDRV_LSM_SET_MODULE_PARAMS: { + struct snd_lsm_module_params p_data; + size_t p_size; + u8 *params; + + if (!session->is_topology_used) { + dev_err(rtd->dev, + "%s: %s: not supported if not using topology\n", + __func__, "SET_MODULE_PARAMS"); + err = -EINVAL; + goto done; + } + + if (!arg) { + dev_err(rtd->dev, + "%s: %s: No Param data to set\n", + __func__, "SET_MODULE_PARAMS"); + err = -EINVAL; + goto done; + } + + if (copy_from_user(&p_data, arg, + sizeof(p_data))) { + dev_err(rtd->dev, + "%s: %s: copy_from_user failed, size = %zd\n", + __func__, "p_data", sizeof(p_data)); + err = -EFAULT; + goto done; + } + + if (p_data.num_params > LSM_PARAMS_MAX) { + dev_err(rtd->dev, + "%s: %s: Invalid num_params %d\n", + __func__, "SET_MODULE_PARAMS", + p_data.num_params); + err = -EINVAL; + goto done; + } + + p_size = p_data.num_params * + sizeof(struct lsm_params_info); + + if (p_data.data_size != p_size) { + dev_err(rtd->dev, + "%s: %s: Invalid size %zd\n", + __func__, "SET_MODULE_PARAMS", p_size); + + err = -EFAULT; + goto done; + } + + params = kzalloc(p_size, GFP_KERNEL); + if (!params) { + err = -ENOMEM; + goto done; + } + + if (copy_from_user(params, p_data.params, + p_data.data_size)) { + dev_err(rtd->dev, + "%s: %s: copy_from_user failed, size = %d\n", + __func__, "params", p_data.data_size); + kfree(params); + err = -EFAULT; + goto done; + } + + err = msm_cpe_lsm_process_params(substream, &p_data, params); + if (err) + dev_err(rtd->dev, + "%s: %s: Failed to set params, err = %d\n", + __func__, "SET_MODULE_PARAMS", err); + kfree(params); + break; + } + default: + err = msm_cpe_lsm_ioctl_shared(substream, cmd, arg); + break; + } + +done: + MSM_CPE_LSM_REL_LOCK(&lsm_d->lsm_api_lock, + "lsm_api_lock"); + return err; +} + +#ifdef CONFIG_COMPAT +struct snd_lsm_sound_model_v2_32 { + compat_uptr_t data; + compat_uptr_t confidence_level; + u32 data_size; + enum lsm_detection_mode detection_mode; + u8 num_confidence_levels; + bool detect_failure; +}; + +struct snd_lsm_detection_params_32 { + compat_uptr_t conf_level; + enum lsm_detection_mode detect_mode; + u8 num_confidence_levels; + bool detect_failure; +}; + +struct lsm_params_info_32 { + u32 module_id; + u32 param_id; + u32 param_size; + compat_uptr_t param_data; + uint32_t param_type; +}; + +struct snd_lsm_module_params_32 { + compat_uptr_t params; + u32 num_params; + u32 data_size; +}; + +enum { + SNDRV_LSM_REG_SND_MODEL_V2_32 = + _IOW('U', 0x07, struct snd_lsm_sound_model_v2_32), + SNDRV_LSM_SET_PARAMS32 = + _IOW('U', 0x0A, struct snd_lsm_detection_params_32), + SNDRV_LSM_SET_MODULE_PARAMS_32 = + _IOW('U', 0x0B, struct snd_lsm_module_params_32), +}; + +static int msm_cpe_lsm_ioctl_compat(struct snd_pcm_substream *substream, + unsigned int cmd, void *arg) +{ + int err = 0; + struct snd_soc_pcm_runtime *rtd; + struct cpe_priv *cpe = NULL; + struct cpe_lsm_data *lsm_d = NULL; + struct cpe_lsm_session *session = NULL; + struct wcd_cpe_lsm_ops *lsm_ops; + + if (!substream || !substream->private_data) { + pr_err("%s: invalid substream (%pK)\n", + __func__, substream); + return -EINVAL; + } + + rtd = substream->private_data; + lsm_d = cpe_get_lsm_data(substream); + cpe = cpe_get_private_data(substream); + + if (!cpe || !cpe->core_handle) { + dev_err(rtd->dev, + "%s: Invalid private data\n", + __func__); + return -EINVAL; + } + + if (!lsm_d || !lsm_d->lsm_session) { + dev_err(rtd->dev, + "%s: Invalid session data\n", + __func__); + return -EINVAL; + } + + MSM_CPE_LSM_GRAB_LOCK(&lsm_d->lsm_api_lock, + "lsm_api_lock"); + + session = lsm_d->lsm_session; + lsm_ops = &cpe->lsm_ops; + + switch (cmd) { + case SNDRV_LSM_REG_SND_MODEL_V2_32: { + struct snd_lsm_sound_model_v2 snd_model; + struct snd_lsm_sound_model_v2_32 snd_model32; + + if (session->is_topology_used) { + dev_err(rtd->dev, + "%s: %s: not supported if using topology\n", + __func__, "LSM_REG_SND_MODEL_V2_32"); + err = -EINVAL; + goto done; + } + + dev_dbg(rtd->dev, + "%s: ioctl %s\n", __func__, + "SNDRV_LSM_REG_SND_MODEL_V2_32"); + + if (copy_from_user(&snd_model32, (void *)arg, + sizeof(snd_model32))) { + dev_err(rtd->dev, + "%s: copy from user failed, size %zd\n", + __func__, + sizeof(snd_model32)); + err = -EFAULT; + goto done; + } + + snd_model.data = compat_ptr(snd_model32.data); + snd_model.confidence_level = + compat_ptr(snd_model32.confidence_level); + snd_model.data_size = snd_model32.data_size; + snd_model.detect_failure = snd_model32.detect_failure; + snd_model.num_confidence_levels = + snd_model32.num_confidence_levels; + snd_model.detection_mode = snd_model32.detection_mode; + + cmd = SNDRV_LSM_REG_SND_MODEL_V2; + err = msm_cpe_lsm_ioctl_shared(substream, cmd, &snd_model); + if (err) + dev_err(rtd->dev, + "%s: %s failed, error = %d\n", + __func__, + "SNDRV_LSM_REG_SND_MODEL_V2_32", + err); + } + break; + case SNDRV_LSM_EVENT_STATUS: { + struct snd_lsm_event_status *event_status = NULL; + struct snd_lsm_event_status u_event_status32; + struct snd_lsm_event_status *udata_32 = NULL; + int u_pld_size = 0; + + dev_dbg(rtd->dev, + "%s: ioctl %s\n", __func__, + "SNDRV_LSM_EVENT_STATUS32"); + + if (copy_from_user(&u_event_status32, (void *)arg, + sizeof(struct snd_lsm_event_status))) { + dev_err(rtd->dev, + "%s: event status copy from user failed, size %zd\n", + __func__, + sizeof(struct snd_lsm_event_status)); + err = -EFAULT; + goto done; + } + + if (u_event_status32.payload_size > + LISTEN_MAX_STATUS_PAYLOAD_SIZE) { + dev_err(rtd->dev, + "%s: payload_size %d is invalid, max allowed = %d\n", + __func__, u_event_status32.payload_size, + LISTEN_MAX_STATUS_PAYLOAD_SIZE); + err = -EINVAL; + goto done; + } + + u_pld_size = sizeof(struct snd_lsm_event_status) + + u_event_status32.payload_size; + event_status = kzalloc(u_pld_size, GFP_KERNEL); + if (!event_status) { + dev_err(rtd->dev, + "%s: No memory for event status\n", + __func__); + err = -ENOMEM; + goto done; + } else { + event_status->payload_size = + u_event_status32.payload_size; + err = msm_cpe_lsm_ioctl_shared(substream, + cmd, event_status); + if (err) + dev_err(rtd->dev, + "%s: %s failed, error = %d\n", + __func__, + "SNDRV_LSM_EVENT_STATUS32", + err); + } + + if (!err) { + udata_32 = kzalloc(u_pld_size, GFP_KERNEL); + if (!udata_32) { + dev_err(rtd->dev, + "%s: nomem for udata\n", + __func__); + err = -EFAULT; + } else { + udata_32->status = event_status->status; + udata_32->payload_size = + event_status->payload_size; + memcpy(udata_32->payload, + event_status->payload, + u_pld_size); + } + } + + if (!err && copy_to_user(arg, udata_32, + u_pld_size)) { + dev_err(rtd->dev, + "%s: copy to user failed\n", + __func__); + kfree(event_status); + kfree(udata_32); + err = -EFAULT; + goto done; + } + + msm_cpe_lsm_lab_start(substream, event_status->status); + msm_cpe_process_event_status_done(lsm_d); + kfree(event_status); + kfree(udata_32); + } + break; + case SNDRV_LSM_EVENT_STATUS_V3: { + struct snd_lsm_event_status_v3 *event_status = NULL; + struct snd_lsm_event_status_v3 u_event_status32; + struct snd_lsm_event_status_v3 *udata_32 = NULL; + int u_pld_size = 0; + + dev_dbg(rtd->dev, + "%s: ioctl %s\n", __func__, + "SNDRV_LSM_EVENT_STATUS_V3_32"); + + if (copy_from_user(&u_event_status32, (void *)arg, + sizeof(struct snd_lsm_event_status_v3))) { + dev_err(rtd->dev, + "%s: event status copy from user failed, size %zd\n", + __func__, + sizeof(struct snd_lsm_event_status_v3)); + err = -EFAULT; + goto done; + } + + if (u_event_status32.payload_size > + LISTEN_MAX_STATUS_PAYLOAD_SIZE) { + dev_err(rtd->dev, + "%s: payload_size %d is invalid, max allowed = %d\n", + __func__, u_event_status32.payload_size, + LISTEN_MAX_STATUS_PAYLOAD_SIZE); + err = -EINVAL; + goto done; + } + + u_pld_size = sizeof(struct snd_lsm_event_status_v3) + + u_event_status32.payload_size; + event_status = kzalloc(u_pld_size, GFP_KERNEL); + if (!event_status) { + dev_err(rtd->dev, + "%s: No memory for event status\n", + __func__); + err = -ENOMEM; + goto done; + } else { + event_status->payload_size = + u_event_status32.payload_size; + err = msm_cpe_lsm_ioctl_shared(substream, + cmd, event_status); + if (err) + dev_err(rtd->dev, + "%s: %s failed, error = %d\n", + __func__, + "SNDRV_LSM_EVENT_STATUS_V3_32", + err); + } + + if (!err) { + udata_32 = kzalloc(u_pld_size, GFP_KERNEL); + if (!udata_32) { + dev_err(rtd->dev, + "%s: nomem for udata\n", + __func__); + err = -EFAULT; + } else { + udata_32->timestamp_lsw = + event_status->timestamp_lsw; + udata_32->timestamp_msw = + event_status->timestamp_msw; + udata_32->status = event_status->status; + udata_32->payload_size = + event_status->payload_size; + memcpy(udata_32->payload, + event_status->payload, + u_pld_size); + } + } + + if (!err && copy_to_user(arg, udata_32, + u_pld_size)) { + dev_err(rtd->dev, + "%s: copy to user failed\n", + __func__); + kfree(event_status); + kfree(udata_32); + err = -EFAULT; + goto done; + } + + msm_cpe_lsm_lab_start(substream, event_status->status); + msm_cpe_process_event_status_done(lsm_d); + kfree(event_status); + kfree(udata_32); + } + break; + case SNDRV_LSM_SET_PARAMS32: { + struct snd_lsm_detection_params_32 det_params32; + struct snd_lsm_detection_params det_params; + + if (session->is_topology_used) { + dev_err(rtd->dev, + "%s: %s: not supported if using topology\n", + __func__, "SNDRV_LSM_SET_PARAMS32"); + + err = -EINVAL; + goto done; + } + + if (copy_from_user(&det_params32, arg, + sizeof(det_params32))) { + err = -EFAULT; + dev_err(rtd->dev, + "%s: %s: copy_from_user failed, size = %zd\n", + __func__, "SNDRV_LSM_SET_PARAMS_32", + sizeof(det_params32)); + } else { + det_params.conf_level = + compat_ptr(det_params32.conf_level); + det_params.detect_mode = + det_params32.detect_mode; + det_params.num_confidence_levels = + det_params32.num_confidence_levels; + det_params.detect_failure = + det_params32.detect_failure; + cmd = SNDRV_LSM_SET_PARAMS; + err = msm_cpe_lsm_ioctl_shared(substream, cmd, + &det_params); + if (err) + dev_err(rtd->dev, + "%s: ioctl %s failed\n", __func__, + "SNDRV_LSM_SET_PARAMS"); + } + + break; + } + + case SNDRV_LSM_SET_MODULE_PARAMS_32: { + struct snd_lsm_module_params_32 p_data_32; + struct snd_lsm_module_params p_data; + u8 *params, *params32; + size_t p_size; + struct lsm_params_info_32 *p_info_32; + struct lsm_params_info *p_info; + int i; + + if (!session->is_topology_used) { + dev_err(rtd->dev, + "%s: %s: not supported if not using topology\n", + __func__, "SET_MODULE_PARAMS_32"); + err = -EINVAL; + goto done; + } + + if (copy_from_user(&p_data_32, arg, + sizeof(p_data_32))) { + dev_err(rtd->dev, + "%s: %s: copy_from_user failed, size = %zd\n", + __func__, "SET_MODULE_PARAMS_32", + sizeof(p_data_32)); + err = -EFAULT; + goto done; + } + + p_data.params = compat_ptr(p_data_32.params); + p_data.num_params = p_data_32.num_params; + p_data.data_size = p_data_32.data_size; + + if (p_data.num_params > LSM_PARAMS_MAX) { + dev_err(rtd->dev, + "%s: %s: Invalid num_params %d\n", + __func__, "SET_MODULE_PARAMS_32", + p_data.num_params); + err = -EINVAL; + goto done; + } + + if (p_data.data_size != + (p_data.num_params * sizeof(struct lsm_params_info_32))) { + dev_err(rtd->dev, + "%s: %s: Invalid size %d\n", + __func__, "SET_MODULE_PARAMS_32", + p_data.data_size); + err = -EINVAL; + goto done; + } + + p_size = sizeof(struct lsm_params_info_32) * + p_data.num_params; + + params32 = kzalloc(p_size, GFP_KERNEL); + if (!params32) { + err = -ENOMEM; + goto done; + } + + p_size = sizeof(struct lsm_params_info) * p_data.num_params; + params = kzalloc(p_size, GFP_KERNEL); + if (!params) { + kfree(params32); + err = -ENOMEM; + goto done; + } + + if (copy_from_user(params32, p_data.params, + p_data.data_size)) { + dev_err(rtd->dev, + "%s: %s: copy_from_user failed, size = %d\n", + __func__, "params32", p_data.data_size); + kfree(params32); + kfree(params); + err = -EFAULT; + goto done; + } + + p_info_32 = (struct lsm_params_info_32 *) params32; + p_info = (struct lsm_params_info *) params; + for (i = 0; i < p_data.num_params; i++) { + p_info->module_id = p_info_32->module_id; + p_info->param_id = p_info_32->param_id; + p_info->param_size = p_info_32->param_size; + p_info->param_data = compat_ptr(p_info_32->param_data); + p_info->param_type = p_info_32->param_type; + + p_info_32++; + p_info++; + } + + err = msm_cpe_lsm_process_params(substream, + &p_data, params); + if (err) + dev_err(rtd->dev, + "%s: Failed to process params, err = %d\n", + __func__, err); + kfree(params); + kfree(params32); + break; + } + case SNDRV_LSM_REG_SND_MODEL_V2: + case SNDRV_LSM_SET_PARAMS: + case SNDRV_LSM_SET_MODULE_PARAMS: + /* + * In ideal cases, the compat_ioctl should never be called + * with the above unlocked ioctl commands. Print error + * and return error if it does. + */ + dev_err(rtd->dev, + "%s: Invalid cmd for compat_ioctl\n", + __func__); + err = -EINVAL; + break; + default: + err = msm_cpe_lsm_ioctl_shared(substream, cmd, arg); + break; + } +done: + MSM_CPE_LSM_REL_LOCK(&lsm_d->lsm_api_lock, + "lsm_api_lock"); + return err; +} + +#else +#define msm_cpe_lsm_ioctl_compat NULL +#endif + +/* + * msm_cpe_lsm_prepare: prepare call from ASoC core for this platform + * @substream: ASoC substream for which the operation is invoked + * + * start the AFE port on CPE associated for this listen session + */ +static int msm_cpe_lsm_prepare(struct snd_pcm_substream *substream) +{ + int rc = 0; + struct cpe_priv *cpe = cpe_get_private_data(substream); + struct cpe_lsm_data *lsm_d = cpe_get_lsm_data(substream); + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct wcd_cpe_afe_ops *afe_ops; + struct wcd_cpe_afe_port_cfg *afe_cfg; + struct cpe_lsm_session *lsm_session; + struct cpe_lsm_lab *lab_d = &lsm_d->lab; + struct snd_pcm_runtime *runtime = substream->runtime; + struct lsm_hw_params lsm_param; + struct wcd_cpe_lsm_ops *lsm_ops; + + if (!cpe || !cpe->core_handle) { + dev_err(rtd->dev, + "%s: Invalid private data\n", + __func__); + return -EINVAL; + } + + if (!lsm_d || !lsm_d->lsm_session) { + dev_err(rtd->dev, + "%s: Invalid session data\n", + __func__); + return -EINVAL; + } + if (runtime->status->state == SNDRV_PCM_STATE_XRUN || + runtime->status->state == SNDRV_PCM_STATE_PREPARED) { + pr_err("%s: XRUN ignore for now\n", __func__); + return 0; + } + + lsm_session = lsm_d->lsm_session; + lab_d->pcm_size = snd_pcm_lib_buffer_bytes(substream); + + dev_dbg(rtd->dev, + "%s: pcm_size 0x%x", __func__, lab_d->pcm_size); + + if (lsm_d->cpe_prepared) { + dev_dbg(rtd->dev, "%s: CPE is alredy prepared\n", + __func__); + return 0; + } + + lsm_ops = &cpe->lsm_ops; + afe_ops = &cpe->afe_ops; + afe_cfg = &(lsm_d->lsm_session->afe_port_cfg); + + switch (cpe->input_port_id) { + case AFE_PORT_ID_3: + afe_cfg->port_id = AFE_PORT_ID_3; + afe_cfg->bit_width = 16; + afe_cfg->num_channels = 1; + afe_cfg->sample_rate = SAMPLE_RATE_48KHZ; + rc = afe_ops->afe_port_cmd_cfg(cpe->core_handle, afe_cfg); + break; + case AFE_PORT_ID_1: + default: + afe_cfg->port_id = AFE_PORT_ID_1; + afe_cfg->bit_width = 16; + afe_cfg->num_channels = 1; + afe_cfg->sample_rate = SAMPLE_RATE_16KHZ; + rc = afe_ops->afe_set_params(cpe->core_handle, + afe_cfg, cpe->afe_mad_ctl); + break; + } + + if (rc != 0) { + dev_err(rtd->dev, + "%s: cpe afe params failed for port = %d, err = %d\n", + __func__, afe_cfg->port_id, rc); + return rc; + } + lsm_param.sample_rate = afe_cfg->sample_rate; + lsm_param.num_chs = afe_cfg->num_channels; + lsm_param.bit_width = afe_cfg->bit_width; + rc = lsm_ops->lsm_set_media_fmt_params(cpe->core_handle, lsm_session, + &lsm_param); + if (rc) + dev_dbg(rtd->dev, + "%s: failed to set lsm media fmt params, err = %d\n", + __func__, rc); + + /* Send connect to port (input) */ + rc = lsm_ops->lsm_set_port(cpe->core_handle, lsm_session, + &cpe->input_port_id); + if (rc) { + dev_err(rtd->dev, + "%s: Failed to set connect input port, err=%d\n", + __func__, rc); + return rc; + } + + if (cpe->input_port_id != 3) { + rc = lsm_ops->lsm_get_afe_out_port_id(cpe->core_handle, + lsm_session); + if (rc != 0) { + dev_err(rtd->dev, + "%s: failed to get port id, err = %d\n", + __func__, rc); + return rc; + } + /* Send connect to port (output) */ + rc = lsm_ops->lsm_set_port(cpe->core_handle, lsm_session, + &lsm_session->afe_out_port_id); + if (rc) { + dev_err(rtd->dev, + "%s: Failed to set connect output port, err=%d\n", + __func__, rc); + return rc; + } + } + rc = msm_cpe_afe_port_cntl(substream, + cpe->core_handle, + afe_ops, afe_cfg, + AFE_CMD_PORT_START); + if (rc) + dev_err(rtd->dev, + "%s: cpe_afe_port start failed, err = %d\n", + __func__, rc); + else + lsm_d->cpe_prepared = true; + + return rc; +} + +/* + * msm_cpe_lsm_trigger: trigger call from ASoC core for this platform + * @substream: ASoC substream for which the operation is invoked + * @cmd: the trigger command from framework + * + * suspend/resume the AFE port on CPE associated with listen session + */ +static int msm_cpe_lsm_trigger(struct snd_pcm_substream *substream, + int cmd) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct cpe_priv *cpe = cpe_get_private_data(substream); + struct cpe_lsm_data *lsm_d = cpe_get_lsm_data(substream); + struct wcd_cpe_afe_ops *afe_ops; + struct wcd_cpe_afe_port_cfg *afe_cfg; + int afe_cmd = AFE_CMD_INVALID; + int rc = 0; + + if (!cpe || !cpe->core_handle) { + dev_err(rtd->dev, + "%s: Invalid private data\n", + __func__); + return -EINVAL; + } + + if (!lsm_d || !lsm_d->lsm_session) { + dev_err(rtd->dev, + "%s: Invalid session data\n", + __func__); + return -EINVAL; + } + + afe_ops = &cpe->afe_ops; + afe_cfg = &(lsm_d->lsm_session->afe_port_cfg); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + afe_cmd = AFE_CMD_PORT_SUSPEND; + break; + + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + afe_cmd = AFE_CMD_PORT_RESUME; + break; + + default: + afe_cmd = AFE_CMD_INVALID; + dev_dbg(rtd->dev, + "%s: unhandled trigger cmd %d\n", + __func__, cmd); + break; + } + + if (afe_cmd != AFE_CMD_INVALID) + rc = msm_cpe_afe_port_cntl(substream, + cpe->core_handle, + afe_ops, afe_cfg, + afe_cmd); + + return rc; +} + +static int msm_cpe_lsm_hwparams(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct cpe_lsm_data *lsm_d = cpe_get_lsm_data(substream); + struct cpe_priv *cpe = cpe_get_private_data(substream); + struct cpe_lsm_session *session = NULL; + struct cpe_hw_params *hw_params = NULL; + + if (!cpe || !cpe->core_handle) { + dev_err(rtd->dev, + "%s: Invalid %s\n", + __func__, + (!cpe) ? "cpe" : "core"); + return -EINVAL; + } + + if (!lsm_d || !lsm_d->lsm_session) { + dev_err(rtd->dev, + "%s: Invalid %s\n", + __func__, + (!lsm_d) ? "priv_data" : "session"); + return -EINVAL; + } + + session = lsm_d->lsm_session; + hw_params = &lsm_d->hw_params; + hw_params->buf_sz = (params_buffer_bytes(params) + / params_periods(params)); + hw_params->period_count = params_periods(params); + hw_params->channels = params_channels(params); + hw_params->sample_rate = params_rate(params); + + if (params_format(params) == SNDRV_PCM_FORMAT_S16_LE) + hw_params->sample_size = 16; + else if (params_format(params) == + SNDRV_PCM_FORMAT_S24_LE) + hw_params->sample_size = 24; + else if (params_format(params) == + SNDRV_PCM_FORMAT_S32_LE) + hw_params->sample_size = 32; + else { + dev_err(rtd->dev, + "%s: Invalid Format 0x%x\n", + __func__, params_format(params)); + return -EINVAL; + } + + dev_dbg(rtd->dev, + "%s: Format %d buffer size(bytes) %d period count %d\n" + " Channel %d period in bytes 0x%x Period Size 0x%x rate = %d\n", + __func__, params_format(params), params_buffer_bytes(params), + params_periods(params), params_channels(params), + params_period_bytes(params), params_period_size(params), + params_rate(params)); + + return 0; +} + +static snd_pcm_uframes_t msm_cpe_lsm_pointer( + struct snd_pcm_substream *substream) +{ + + struct cpe_lsm_data *lsm_d = cpe_get_lsm_data(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct cpe_lsm_session *session; + struct cpe_lsm_lab *lab_d = &lsm_d->lab; + + session = lsm_d->lsm_session; + if (lab_d->dma_write >= lab_d->pcm_size) + lab_d->dma_write = 0; + dev_dbg(rtd->dev, + "%s:pcm_dma_pos = %d\n", + __func__, lab_d->dma_write); + + return bytes_to_frames(runtime, (lab_d->dma_write)); +} + +static int msm_cpe_lsm_copy(struct snd_pcm_substream *substream, int a, + unsigned long hwoff, void __user *buf, unsigned long fbytes) +{ + struct cpe_lsm_data *lsm_d = cpe_get_lsm_data(substream); + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_pcm_runtime *runtime = substream->runtime; + struct cpe_lsm_session *session; + struct cpe_lsm_lab *lab_d = &lsm_d->lab; + char *pcm_buf; + int rc = 0; + + if (runtime->status->state == SNDRV_PCM_STATE_XRUN || + runtime->status->state == SNDRV_PCM_STATE_PREPARED) { + pr_err("%s: XRUN ignore for now\n", __func__); + return 0; + } + session = lsm_d->lsm_session; + + /* Check if buffer reading is already in error state */ + if (lab_d->thread_status == MSM_LSM_LAB_THREAD_ERROR) { + dev_err(rtd->dev, + "%s: Bufferring is in error state\n", + __func__); + /* + * Advance the period so there is no wait in case + * read is invoked even after error is propogated + */ + atomic_inc(&lab_d->in_count); + lab_d->dma_write += snd_pcm_lib_period_bytes(substream); + snd_pcm_period_elapsed(substream); + return -ENETRESET; + } else if (lab_d->thread_status == MSM_LSM_LAB_THREAD_STOP) { + dev_err(rtd->dev, + "%s: Buferring is in stopped\n", + __func__); + return -EIO; + } + + rc = wait_event_timeout(lab_d->period_wait, + (atomic_read(&lab_d->in_count) || + atomic_read(&lab_d->abort_read)), + (2 * HZ)); + if (atomic_read(&lab_d->abort_read)) { + pr_debug("%s: LSM LAB Abort read\n", __func__); + return -EIO; + } + if (lab_d->thread_status != MSM_LSM_LAB_THREAD_RUNNING) { + pr_err("%s: Lab stopped\n", __func__); + return -EIO; + } + if (!rc) { + pr_err("%s:LAB err wait_event_timeout\n", __func__); + rc = -EAGAIN; + goto fail; + } + if (lab_d->buf_idx >= (lsm_d->hw_params.period_count)) + lab_d->buf_idx = 0; + pcm_buf = (lab_d->pcm_buf[lab_d->buf_idx].mem); + pr_debug("%s: Buf IDX = 0x%x pcm_buf %pK\n", + __func__, lab_d->buf_idx, pcm_buf); + if (pcm_buf) { + if (copy_to_user(buf, pcm_buf, fbytes)) { + pr_err("Failed to copy buf to user\n"); + rc = -EFAULT; + goto fail; + } + } + lab_d->buf_idx++; + atomic_dec(&lab_d->in_count); + return 0; +fail: + return rc; +} + +/* + * msm_asoc_cpe_lsm_probe: ASoC framework for lsm platform driver + * @platform: platform registered with ASoC core + * + * Allocate the private data for this platform and obtain the ops for + * lsm and afe modules from underlying driver. Also find the codec + * for this platform as specified by machine driver for ASoC framework. + */ +static int msm_asoc_cpe_lsm_probe(struct snd_soc_platform *platform) +{ + struct snd_soc_card *card; + struct snd_soc_pcm_runtime *rtd; + struct snd_soc_codec *codec; + struct cpe_priv *cpe_priv; + const struct snd_kcontrol_new *kcontrol; + bool found_runtime = false; + const char *cpe_dev_id = "qcom,msm-cpe-lsm-id"; + u32 port_id = 0; + int ret = 0; + + if (!platform || !platform->component.card) { + pr_err("%s: Invalid platform or card\n", + __func__); + return -EINVAL; + } + + card = platform->component.card; + + /* Match platform to codec */ + list_for_each_entry(rtd, &card->rtd_list, list) { + if (!rtd->platform) + continue; + if (!strcmp(rtd->platform->component.name, + platform->component.name)) { + found_runtime = true; + break; + } + } + + if (!found_runtime) { + dev_err(platform->dev, + "%s: Failed to find runtime for platform\n", + __func__); + return -EINVAL; + } + + ret = of_property_read_u32(platform->dev->of_node, cpe_dev_id, + &port_id); + if (ret) { + dev_dbg(platform->dev, + "%s: missing 0x%x in dt node\n", __func__, port_id); + port_id = 1; + } + + codec = rtd->codec; + + cpe_priv = kzalloc(sizeof(struct cpe_priv), + GFP_KERNEL); + if (!cpe_priv) + return -ENOMEM; + + cpe_priv->codec = codec; + cpe_priv->input_port_id = port_id; + wcd_cpe_get_lsm_ops(&cpe_priv->lsm_ops); + wcd_cpe_get_afe_ops(&cpe_priv->afe_ops); + + snd_soc_platform_set_drvdata(platform, cpe_priv); + kcontrol = &msm_cpe_kcontrols[0]; + snd_ctl_add(card->snd_card, snd_ctl_new1(kcontrol, cpe_priv)); + return 0; +} + +static const struct snd_pcm_ops msm_cpe_lsm_ops = { + .open = msm_cpe_lsm_open, + .close = msm_cpe_lsm_close, + .ioctl = msm_cpe_lsm_ioctl, + .prepare = msm_cpe_lsm_prepare, + .trigger = msm_cpe_lsm_trigger, + .pointer = msm_cpe_lsm_pointer, + .copy_user = msm_cpe_lsm_copy, + .hw_params = msm_cpe_lsm_hwparams, + .compat_ioctl = msm_cpe_lsm_ioctl_compat, +}; + +static struct snd_soc_platform_driver msm_soc_cpe_platform = { + .ops = &msm_cpe_lsm_ops, + .probe = msm_asoc_cpe_lsm_probe, +}; + +/* + * msm_cpe_lsm_probe: platform driver probe + * @pdev: platform device + * + * Register the ASoC platform driver with ASoC core + */ +static int msm_cpe_lsm_probe(struct platform_device *pdev) +{ + + return snd_soc_register_platform(&pdev->dev, + &msm_soc_cpe_platform); +} + +/* + * msm_cpe_lsm_remove: platform driver remove + * @pdev: platform device + * + * Deregister the ASoC platform driver + */ +static int msm_cpe_lsm_remove(struct platform_device *pdev) +{ + snd_soc_unregister_platform(&pdev->dev); + return 0; +} + +static const struct of_device_id msm_cpe_lsm_dt_match[] = { + {.compatible = "qcom,msm-cpe-lsm" }, + { } +}; + +static struct platform_driver msm_cpe_lsm_driver = { + .driver = { + .name = "msm-cpe-lsm", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(msm_cpe_lsm_dt_match), + }, + .probe = msm_cpe_lsm_probe, + .remove = msm_cpe_lsm_remove, +}; + +int __init msm_cpe_lsm_init(void) +{ + return platform_driver_register(&msm_cpe_lsm_driver); +} + +void __exit msm_cpe_lsm_exit(void) +{ + platform_driver_unregister(&msm_cpe_lsm_driver); +} + +module_init(msm_cpe_lsm_init); +module_exit(msm_cpe_lsm_exit); +MODULE_DESCRIPTION("CPE LSM platform driver"); +MODULE_DEVICE_TABLE(of, msm_cpe_lsm_dt_match); +MODULE_LICENSE("GPL v2"); diff --git a/audio-kernel/asoc/msm-dai-fe.c b/audio-kernel/asoc/msm-dai-fe.c new file mode 100644 index 000000000..8020929f6 --- /dev/null +++ b/audio-kernel/asoc/msm-dai-fe.c @@ -0,0 +1,2829 @@ +/* Copyright (c) 2012-2019, 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 +#include +#include +#include +#include +#include +#include +#include + +static struct snd_soc_dai_ops msm_fe_dai_ops = {}; + +/* Conventional and unconventional sample rate supported */ +static unsigned int supported_sample_rates[] = { + 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000, + 88200, 96000, 176400, 192000, 352800, 384000 +}; + +static struct snd_pcm_hw_constraint_list constraints_sample_rates = { + .count = ARRAY_SIZE(supported_sample_rates), + .list = supported_sample_rates, + .mask = 0, +}; + +static int multimedia_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + &constraints_sample_rates); + return 0; +} + +static int fe_dai_probe(struct snd_soc_dai *dai) +{ + struct snd_soc_dapm_route intercon; + struct snd_soc_dapm_context *dapm; + + if (!dai || !dai->driver) { + pr_err("%s invalid params\n", __func__); + return -EINVAL; + } + dapm = snd_soc_component_get_dapm(dai->component); + memset(&intercon, 0, sizeof(intercon)); + if (dai->driver->playback.stream_name && + dai->driver->playback.aif_name) { + dev_dbg(dai->dev, "%s add route for widget %s", + __func__, dai->driver->playback.stream_name); + intercon.source = dai->driver->playback.stream_name; + intercon.sink = dai->driver->playback.aif_name; + dev_dbg(dai->dev, "%s src %s sink %s\n", + __func__, intercon.source, intercon.sink); + snd_soc_dapm_add_routes(dapm, &intercon, 1); + snd_soc_dapm_ignore_suspend(dapm, intercon.source); + } + if (dai->driver->capture.stream_name && + dai->driver->capture.aif_name) { + dev_dbg(dai->dev, "%s add route for widget %s", + __func__, dai->driver->capture.stream_name); + intercon.sink = dai->driver->capture.stream_name; + intercon.source = dai->driver->capture.aif_name; + dev_dbg(dai->dev, "%s src %s sink %s\n", + __func__, intercon.source, intercon.sink); + snd_soc_dapm_add_routes(dapm, &intercon, 1); + snd_soc_dapm_ignore_suspend(dapm, intercon.sink); + } + return 0; +} + +static struct snd_soc_dai_ops msm_fe_Multimedia_dai_ops = { + .startup = multimedia_startup, +}; + +static const struct snd_soc_component_driver msm_fe_dai_component = { + .name = "msm-dai-fe", +}; + +static struct snd_soc_dai_driver msm_fe_dais[] = { + { + .playback = { + .stream_name = "MultiMedia1 Playback", + .aif_name = "MM_DL1", + .rates = (SNDRV_PCM_RATE_8000_384000| + SNDRV_PCM_RATE_KNOT), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE), + .channels_min = 1, + .channels_max = 16, + .rate_min = 8000, + .rate_max = 384000, + }, + .capture = { + .stream_name = "MultiMedia1 Capture", + .aif_name = "MM_UL1", + .rates = (SNDRV_PCM_RATE_8000_384000| + SNDRV_PCM_RATE_KNOT), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE), + .channels_min = 1, + .channels_max = 16, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_Multimedia_dai_ops, + .name = "MultiMedia1", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "MultiMedia2 Playback", + .aif_name = "MM_DL2", + .rates = (SNDRV_PCM_RATE_8000_384000| + SNDRV_PCM_RATE_KNOT), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 384000, + }, + .capture = { + .stream_name = "MultiMedia2 Capture", + .aif_name = "MM_UL2", + .rates = (SNDRV_PCM_RATE_8000_384000| + SNDRV_PCM_RATE_KNOT), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_Multimedia_dai_ops, + .name = "MultiMedia2", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "VoIP Playback", + .aif_name = "VOIP_DL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_SPECIAL, + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + .capture = { + .stream_name = "VoIP Capture", + .aif_name = "VOIP_UL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_SPECIAL, + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "VoIP", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "MultiMedia3 Playback", + .aif_name = "MM_DL3", + .rates = (SNDRV_PCM_RATE_8000_384000 | + SNDRV_PCM_RATE_KNOT), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE), + .channels_min = 1, + .channels_max = 6, + .rate_min = 8000, + .rate_max = 384000, + }, + .capture = { + .stream_name = "MultiMedia3 Capture", + .aif_name = "MM_UL3", + .rates = (SNDRV_PCM_RATE_8000_384000| + SNDRV_PCM_RATE_KNOT), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_Multimedia_dai_ops, + .name = "MultiMedia3", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "MultiMedia4 Playback", + .aif_name = "MM_DL4", + .rates = (SNDRV_PCM_RATE_8000_384000 | + SNDRV_PCM_RATE_KNOT), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 384000, + }, + .ops = &msm_fe_Multimedia_dai_ops, + .compress_new = snd_soc_new_compress, + .name = "MultiMedia4", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "MultiMedia5 Playback", + .aif_name = "MM_DL5", + .rates = (SNDRV_PCM_RATE_8000_384000 | + SNDRV_PCM_RATE_KNOT), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 384000, + }, + .capture = { + .stream_name = "MultiMedia5 Capture", + .aif_name = "MM_UL5", + .rates = (SNDRV_PCM_RATE_8000_48000| + SNDRV_PCM_RATE_KNOT), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_Multimedia_dai_ops, + .name = "MultiMedia5", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "MultiMedia6 Playback", + .aif_name = "MM_DL6", + .rates = (SNDRV_PCM_RATE_8000_384000 | + SNDRV_PCM_RATE_KNOT), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 384000, + }, + .capture = { + .stream_name = "MultiMedia6 Capture", + .aif_name = "MM_UL6", + .rates = (SNDRV_PCM_RATE_8000_48000| + SNDRV_PCM_RATE_KNOT), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_Multimedia_dai_ops, + .name = "MultiMedia6", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "MultiMedia7 Playback", + .aif_name = "MM_DL7", + .rates = (SNDRV_PCM_RATE_8000_384000 | + SNDRV_PCM_RATE_KNOT), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE), + .channels_min = 1, + .channels_max = 16, + .rate_min = 8000, + .rate_max = 384000, + }, + .ops = &msm_fe_Multimedia_dai_ops, + .compress_new = snd_soc_new_compress, + .name = "MultiMedia7", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "MultiMedia8 Playback", + .aif_name = "MM_DL8", + .rates = (SNDRV_PCM_RATE_8000_384000 | + SNDRV_PCM_RATE_KNOT), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 384000, + }, + .capture = { + .stream_name = "MultiMedia8 Capture", + .aif_name = "MM_UL8", + .rates = (SNDRV_PCM_RATE_8000_48000| + SNDRV_PCM_RATE_KNOT), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_Multimedia_dai_ops, + .name = "MultiMedia8", + .probe = fe_dai_probe, + }, + /* FE DAIs created for hostless operation purpose */ + { + .playback = { + .stream_name = "SLIMBUS0_HOSTLESS Playback", + .aif_name = "SLIM0_DL_HL", + .rates = SNDRV_PCM_RATE_8000_384000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 384000, + }, + .capture = { + .stream_name = "SLIMBUS0_HOSTLESS Capture", + .aif_name = "SLIM0_UL_HL", + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 384000, + }, + .ops = &msm_fe_dai_ops, + .name = "SLIMBUS0_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "SLIMBUS1_HOSTLESS Playback", + .aif_name = "SLIM1_DL_HL", + .rates = SNDRV_PCM_RATE_8000_384000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 384000, + }, + .capture = { + .stream_name = "SLIMBUS1_HOSTLESS Capture", + .aif_name = "SLIM1_UL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "SLIMBUS1_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "SLIMBUS3_HOSTLESS Playback", + .aif_name = "SLIM3_DL_HL", + .rates = SNDRV_PCM_RATE_8000_384000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 384000, + }, + .capture = { + .stream_name = "SLIMBUS3_HOSTLESS Capture", + .aif_name = "SLIM3_UL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "SLIMBUS3_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "SLIMBUS4_HOSTLESS Playback", + .aif_name = "SLIM4_DL_HL", + .rates = SNDRV_PCM_RATE_8000_384000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 384000, + }, + .capture = { + .stream_name = "SLIMBUS4_HOSTLESS Capture", + .aif_name = "SLIM4_UL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "SLIMBUS4_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "SLIMBUS6_HOSTLESS Playback", + .aif_name = "SLIM6_DL_HL", + .rates = SNDRV_PCM_RATE_8000_384000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 384000, + }, + .ops = &msm_fe_dai_ops, + .name = "SLIMBUS6_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "SLIMBUS7_HOSTLESS Playback", + .aif_name = "SLIM7_DL_HL", + .rates = SNDRV_PCM_RATE_8000_384000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 384000, + }, + .capture = { + .stream_name = "SLIMBUS7_HOSTLESS Capture", + .aif_name = "SLIM7_UL_HL", + .rates = SNDRV_PCM_RATE_8000_384000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 384000, + }, + .ops = &msm_fe_dai_ops, + .name = "SLIMBUS7_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "SLIMBUS8_HOSTLESS Playback", + .aif_name = "SLIM8_DL_HL", + .rates = SNDRV_PCM_RATE_8000_384000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 384000, + }, + .capture = { + .stream_name = "SLIMBUS8_HOSTLESS Capture", + .aif_name = "SLIM8_UL_HL", + .rates = SNDRV_PCM_RATE_8000_384000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 384000, + }, + .ops = &msm_fe_dai_ops, + .name = "SLIMBUS8_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "CDC_DMA_HOSTLESS Playback", + .aif_name = "CDC_DMA_DL_HL", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 | + SNDRV_PCM_RATE_192000 | SNDRV_PCM_RATE_352800 | + SNDRV_PCM_RATE_384000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 4, + .rate_min = 8000, + .rate_max = 384000, + }, + .capture = { + .stream_name = "CDC_DMA_HOSTLESS Capture", + .aif_name = "CDC_DMA_UL_HL", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 | + SNDRV_PCM_RATE_192000 | SNDRV_PCM_RATE_352800 | + SNDRV_PCM_RATE_384000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 384000, + }, + .ops = &msm_fe_dai_ops, + .name = "CDC_DMA_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .capture = { + .stream_name = "TX3_CDC_DMA_HOSTLESS Capture", + .aif_name = "TX3_CDC_DMA_UL_HL", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 | + SNDRV_PCM_RATE_192000 | SNDRV_PCM_RATE_352800 | + SNDRV_PCM_RATE_384000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 384000, + }, + .ops = &msm_fe_dai_ops, + .name = "TX3_CDC_DMA_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "INT_FM_HOSTLESS Playback", + .aif_name = "INTFM_DL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + .capture = { + .stream_name = "INT_FM_HOSTLESS Capture", + .aif_name = "INTFM_UL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "INT_FM_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "INT_HFP_BT Hostless Playback", + .aif_name = "INTHFP_DL_HL", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 16000, + }, + .capture = { + .stream_name = "INT_HFP_BT Hostless Capture", + .aif_name = "INTHFP_UL_HL", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 16000, + }, + .ops = &msm_fe_dai_ops, + .name = "INT_HFP_BT_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "USBAUDIO_HOSTLESS Playback", + .aif_name = "USBAUDIO_DL_HL", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 | + SNDRV_PCM_RATE_192000 | SNDRV_PCM_RATE_352800 | + SNDRV_PCM_RATE_384000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 384000, + }, + .capture = { + .stream_name = "USBAUDIO_HOSTLESS Capture", + .aif_name = "USBAUDIO_UL_HL", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 | + SNDRV_PCM_RATE_192000 | SNDRV_PCM_RATE_352800 | + SNDRV_PCM_RATE_384000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 384000, + }, + .ops = &msm_fe_dai_ops, + .name = "USBAUDIO_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "AFE Playback", + .aif_name = "PCM_RX", + .rates = (SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_48000), + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + .capture = { + .stream_name = "AFE Capture", + .aif_name = "PCM_TX", + .rates = (SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_48000), + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "AFE-PROXY", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "HDMI_HOSTLESS Playback", + .aif_name = "HDMI_DL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "HDMI_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "AUXPCM_HOSTLESS Playback", + .aif_name = "AUXPCM_DL_HL", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 1, + .rate_min = 8000, + .rate_max = 16000, + }, + .capture = { + .stream_name = "AUXPCM_HOSTLESS Capture", + .aif_name = "AUXPCM_UL_HL", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 1, + .rate_min = 8000, + .rate_max = 16000, + }, + .ops = &msm_fe_dai_ops, + .name = "AUXPCM_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "SEC_AUXPCM_HOSTLESS Playback", + .aif_name = "SEC_AUXPCM_DL_HL", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 1, + .rate_min = 8000, + .rate_max = 16000, + }, + .ops = &msm_fe_dai_ops, + .name = "SEC_AUXPCM_RX_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .capture = { + .stream_name = "SEC_AUXPCM_HOSTLESS Capture", + .aif_name = "SEC_AUXPCM_UL_HL", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 1, + .rate_min = 8000, + .rate_max = 16000, + }, + .ops = &msm_fe_dai_ops, + .name = "SEC_AUXPCM_TX_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "VOICE_STUB Playback", + .aif_name = "VOICE_STUB_DL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + .capture = { + .stream_name = "VOICE_STUB Capture", + .aif_name = "VOICE_STUB_UL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "VOICE_STUB", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "MI2S_RX_HOSTLESS Playback", + .aif_name = "MI2S_DL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + .capture = { + .stream_name = "MI2S_TX_HOSTLESS Capture", + .aif_name = "MI2S_UL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "MI2S_TX_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "SEC_I2S_RX_HOSTLESS Playback", + .aif_name = "SEC_I2S_DL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "SEC_I2S_RX_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .capture = { + .stream_name = "Primary MI2S_TX Hostless Capture", + .aif_name = "PRI_MI2S_UL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "PRI_MI2S_TX_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "Primary MI2S_RX Hostless Playback", + .aif_name = "PRI_MI2S_DL_HL", + .rates = SNDRV_PCM_RATE_8000_384000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 384000, + }, + .ops = &msm_fe_dai_ops, + .name = "PRI_MI2S_RX_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .capture = { + .stream_name = "Secondary MI2S_TX Hostless Capture", + .aif_name = "SEC_MI2S_UL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "SEC_MI2S_TX_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "Secondary MI2S_RX Hostless Playback", + .aif_name = "SEC_MI2S_DL_HL", + .rates = SNDRV_PCM_RATE_8000_384000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 384000, + }, + .ops = &msm_fe_dai_ops, + .name = "SEC_MI2S_RX_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .capture = { + .stream_name = "Tertiary MI2S_TX Hostless Capture", + .aif_name = "TERT_MI2S_UL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "TERT_MI2S_TX_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "Tertiary MI2S_RX Hostless Playback", + .aif_name = "TERT_MI2S_DL_HL", + .rates = SNDRV_PCM_RATE_8000_384000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 384000, + }, + .ops = &msm_fe_dai_ops, + .name = "TERT_MI2S_RX_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .capture = { + .stream_name = "Quaternary MI2S_TX Hostless Capture", + .aif_name = "QUAT_MI2S_UL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "QUAT_MI2S_TX_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "Quaternary MI2S_RX Hostless Playback", + .aif_name = "QUAT_MI2S_DL_HL", + .rates = SNDRV_PCM_RATE_8000_384000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 384000, + }, + .ops = &msm_fe_dai_ops, + .name = "QUAT_MI2S_RX_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "INT0 MI2S_RX Hostless Playback", + .aif_name = "INT0_MI2S_DL_HL", + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 192000, + }, + .ops = &msm_fe_dai_ops, + .name = "INT0_MI2S_RX_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "INT4 MI2S_RX Hostless Playback", + .aif_name = "INT4_MI2S_DL_HL", + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 4, + .rate_min = 8000, + .rate_max = 192000, + }, + .ops = &msm_fe_dai_ops, + .name = "INT4_MI2S_RX_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .capture = { + .stream_name = "INT3 MI2S_TX Hostless Capture", + .aif_name = "INT3_MI2S_UL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "INT3_MI2S_TX_HOSTLESS", + .probe = fe_dai_probe, + }, + /* TDM Hostless */ + { + .capture = { + .stream_name = "Primary TDM0 Hostless Capture", + .aif_name = "PRI_TDM_TX_0_UL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "PRI_TDM_TX_0_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "Primary TDM0 Hostless Playback", + .aif_name = "PRI_TDM_RX_0_DL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "PRI_TDM_RX_0_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .capture = { + .stream_name = "Primary TDM1 Hostless Capture", + .aif_name = "PRI_TDM_TX_1_UL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "PRI_TDM_TX_1_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "Primary TDM1 Hostless Playback", + .aif_name = "PRI_TDM_RX_1_DL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "PRI_TDM_RX_1_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .capture = { + .stream_name = "Primary TDM2 Hostless Capture", + .aif_name = "PRI_TDM_TX_2_UL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "PRI_TDM_TX_2_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "Primary TDM2 Hostless Playback", + .aif_name = "PRI_TDM_RX_2_DL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "PRI_TDM_RX_2_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .capture = { + .stream_name = "Primary TDM3 Hostless Capture", + .aif_name = "PRI_TDM_TX_3_UL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "PRI_TDM_TX_3_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "Primary TDM3 Hostless Playback", + .aif_name = "PRI_TDM_RX_3_DL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "PRI_TDM_RX_3_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .capture = { + .stream_name = "Primary TDM4 Hostless Capture", + .aif_name = "PRI_TDM_TX_4_UL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "PRI_TDM_TX_4_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "Primary TDM4 Hostless Playback", + .aif_name = "PRI_TDM_RX_4_DL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "PRI_TDM_RX_4_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .capture = { + .stream_name = "Primary TDM5 Hostless Capture", + .aif_name = "PRI_TDM_TX_5_UL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "PRI_TDM_TX_5_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "Primary TDM5 Hostless Playback", + .aif_name = "PRI_TDM_RX_5_DL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "PRI_TDM_RX_5_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .capture = { + .stream_name = "Primary TDM6 Hostless Capture", + .aif_name = "PRI_TDM_TX_6_UL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "PRI_TDM_TX_6_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "Primary TDM6 Hostless Playback", + .aif_name = "PRI_TDM_RX_6_DL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "PRI_TDM_RX_6_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .capture = { + .stream_name = "Primary TDM7 Hostless Capture", + .aif_name = "PRI_TDM_TX_7_UL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "PRI_TDM_TX_7_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "Primary TDM7 Hostless Playback", + .aif_name = "PRI_TDM_RX_7_DL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "PRI_TDM_RX_7_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .capture = { + .stream_name = "Secondary TDM0 Hostless Capture", + .aif_name = "SEC_TDM_TX_0_UL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "SEC_TDM_TX_0_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "Secondary TDM0 Hostless Playback", + .aif_name = "SEC_TDM_RX_0_DL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "SEC_TDM_RX_0_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .capture = { + .stream_name = "Secondary TDM1 Hostless Capture", + .aif_name = "SEC_TDM_TX_1_UL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "SEC_TDM_TX_1_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "Secondary TDM1 Hostless Playback", + .aif_name = "SEC_TDM_RX_1_DL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "SEC_TDM_RX_1_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .capture = { + .stream_name = "Secondary TDM2 Hostless Capture", + .aif_name = "SEC_TDM_TX_2_UL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "SEC_TDM_TX_2_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "Secondary TDM2 Hostless Playback", + .aif_name = "SEC_TDM_RX_2_DL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "SEC_TDM_RX_2_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .capture = { + .stream_name = "Secondary TDM3 Hostless Capture", + .aif_name = "SEC_TDM_TX_3_UL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "SEC_TDM_TX_3_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "Secondary TDM3 Hostless Playback", + .aif_name = "SEC_TDM_RX_3_DL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "SEC_TDM_RX_3_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .capture = { + .stream_name = "Secondary TDM4 Hostless Capture", + .aif_name = "SEC_TDM_TX_4_UL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "SEC_TDM_TX_4_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "Secondary TDM4 Hostless Playback", + .aif_name = "SEC_TDM_RX_4_DL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "SEC_TDM_RX_4_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .capture = { + .stream_name = "Secondary TDM5 Hostless Capture", + .aif_name = "SEC_TDM_TX_5_UL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "SEC_TDM_TX_5_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "Secondary TDM5 Hostless Playback", + .aif_name = "SEC_TDM_RX_5_DL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "SEC_TDM_RX_5_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .capture = { + .stream_name = "Secondary TDM6 Hostless Capture", + .aif_name = "SEC_TDM_TX_6_UL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "SEC_TDM_TX_6_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "Secondary TDM6 Hostless Playback", + .aif_name = "SEC_TDM_RX_6_DL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "SEC_TDM_RX_6_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .capture = { + .stream_name = "Secondary TDM7 Hostless Capture", + .aif_name = "SEC_TDM_TX_7_UL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "SEC_TDM_TX_7_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "Secondary TDM7 Hostless Playback", + .aif_name = "SEC_TDM_RX_7_DL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "SEC_TDM_RX_7_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .capture = { + .stream_name = "Tertiary TDM0 Hostless Capture", + .aif_name = "TERT_TDM_TX_0_UL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "TERT_TDM_TX_0_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "Tertiary TDM0 Hostless Playback", + .aif_name = "TERT_TDM_RX_0_DL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "TERT_TDM_RX_0_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .capture = { + .stream_name = "Tertiary TDM1 Hostless Capture", + .aif_name = "TERT_TDM_TX_1_UL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "TERT_TDM_TX_1_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "Tertiary TDM1 Hostless Playback", + .aif_name = "TERT_TDM_RX_1_DL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "TERT_TDM_RX_1_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .capture = { + .stream_name = "Tertiary TDM2 Hostless Capture", + .aif_name = "TERT_TDM_TX_2_UL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "TERT_TDM_TX_2_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "Tertiary TDM2 Hostless Playback", + .aif_name = "TERT_TDM_RX_2_DL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "TERT_TDM_RX_2_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .capture = { + .stream_name = "Tertiary TDM3 Hostless Capture", + .aif_name = "TERT_TDM_TX_3_UL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "TERT_TDM_TX_3_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "Tertiary TDM3 Hostless Playback", + .aif_name = "TERT_TDM_RX_3_DL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "TERT_TDM_RX_3_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .capture = { + .stream_name = "Tertiary TDM4 Hostless Capture", + .aif_name = "TERT_TDM_TX_4_UL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "TERT_TDM_TX_4_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "Tertiary TDM4 Hostless Playback", + .aif_name = "TERT_TDM_RX_4_DL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "TERT_TDM_RX_4_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .capture = { + .stream_name = "Tertiary TDM5 Hostless Capture", + .aif_name = "TERT_TDM_TX_5_UL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "TERT_TDM_TX_5_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "Tertiary TDM5 Hostless Playback", + .aif_name = "TERT_TDM_RX_5_DL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "TERT_TDM_RX_5_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .capture = { + .stream_name = "Tertiary TDM6 Hostless Capture", + .aif_name = "TERT_TDM_TX_6_UL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "TERT_TDM_TX_6_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "Tertiary TDM6 Hostless Playback", + .aif_name = "TERT_TDM_RX_6_DL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "TERT_TDM_RX_6_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .capture = { + .stream_name = "Tertiary TDM7 Hostless Capture", + .aif_name = "TERT_TDM_TX_7_UL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "TERT_TDM_TX_7_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "Tertiary TDM7 Hostless Playback", + .aif_name = "TERT_TDM_RX_7_DL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "TERT_TDM_RX_7_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .capture = { + .stream_name = "Quaternary TDM0 Hostless Capture", + .aif_name = "QUAT_TDM_TX_0_UL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "QUAT_TDM_TX_0_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "Quaternary TDM0 Hostless Playback", + .aif_name = "QUAT_TDM_RX_0_DL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "QUAT_TDM_RX_0_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .capture = { + .stream_name = "Quaternary TDM1 Hostless Capture", + .aif_name = "QUAT_TDM_TX_1_UL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "QUAT_TDM_TX_1_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "Quaternary TDM1 Hostless Playback", + .aif_name = "QUAT_TDM_RX_1_DL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "QUAT_TDM_RX_1_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .capture = { + .stream_name = "Quaternary TDM2 Hostless Capture", + .aif_name = "QUAT_TDM_TX_2_UL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "QUAT_TDM_TX_2_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "Quaternary TDM2 Hostless Playback", + .aif_name = "QUAT_TDM_RX_2_DL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "QUAT_TDM_RX_2_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .capture = { + .stream_name = "Quaternary TDM3 Hostless Capture", + .aif_name = "QUAT_TDM_TX_3_UL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "QUAT_TDM_TX_3_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "Quaternary TDM3 Hostless Playback", + .aif_name = "QUAT_TDM_RX_3_DL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "QUAT_TDM_RX_3_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .capture = { + .stream_name = "Quaternary TDM4 Hostless Capture", + .aif_name = "QUAT_TDM_TX_4_UL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "QUAT_TDM_TX_4_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "Quaternary TDM4 Hostless Playback", + .aif_name = "QUAT_TDM_RX_4_DL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "QUAT_TDM_RX_4_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .capture = { + .stream_name = "Quaternary TDM5 Hostless Capture", + .aif_name = "QUAT_TDM_TX_5_UL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "QUAT_TDM_TX_5_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "Quaternary TDM5 Hostless Playback", + .aif_name = "QUAT_TDM_RX_5_DL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "QUAT_TDM_RX_5_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .capture = { + .stream_name = "Quaternary TDM6 Hostless Capture", + .aif_name = "QUAT_TDM_TX_6_UL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "QUAT_TDM_TX_6_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "Quaternary TDM6 Hostless Playback", + .aif_name = "QUAT_TDM_RX_6_DL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "QUAT_TDM_RX_6_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .capture = { + .stream_name = "Quaternary TDM7 Hostless Capture", + .aif_name = "QUAT_TDM_TX_7_UL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "QUAT_TDM_TX_7_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "Quaternary TDM7 Hostless Playback", + .aif_name = "QUAT_TDM_RX_7_DL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "QUAT_TDM_RX_7_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "DTMF_RX_HOSTLESS Playback", + .aif_name = "DTMF_DL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "DTMF_RX_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .capture = { + .stream_name = "CPE Listen Audio capture", + .aif_name = "CPE_LSM_UL_HL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 1, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "CPE_LSM_NOHOST", + }, + { + .playback = { + .stream_name = "VOLTE_STUB Playback", + .aif_name = "VOLTE_STUB_DL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + .capture = { + .stream_name = "VOLTE_STUB Capture", + .aif_name = "VOLTE_STUB_UL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "VOLTE_STUB", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "VOICE2_STUB Playback", + .aif_name = "VOICE2_STUB_DL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + .capture = { + .stream_name = "VOICE2_STUB Capture", + .aif_name = "VOICE2_STUB_UL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "VOICE2_STUB", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "MultiMedia9 Playback", + .aif_name = "MM_DL9", + .rates = (SNDRV_PCM_RATE_8000_384000| + SNDRV_PCM_RATE_KNOT), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 384000, + }, + .capture = { + .stream_name = "MultiMedia9 Capture", + .aif_name = "MM_UL9", + .rates = (SNDRV_PCM_RATE_8000_48000| + SNDRV_PCM_RATE_KNOT), + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_Multimedia_dai_ops, + .name = "MultiMedia9", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "QCHAT Playback", + .aif_name = "QCHAT_DL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + .capture = { + .stream_name = "QCHAT Capture", + .aif_name = "QCHAT_UL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "QCHAT", + .probe = fe_dai_probe, + }, + { + .capture = { + .stream_name = "Listen 1 Audio Service Capture", + .aif_name = "LSM1_UL_HL", + .rates = (SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_48000), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 4, + .rate_min = 16000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "LSM1", + .probe = fe_dai_probe, + }, + { + .capture = { + .stream_name = "Listen 2 Audio Service Capture", + .aif_name = "LSM2_UL_HL", + .rates = (SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_48000), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 4, + .rate_min = 16000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "LSM2", + .probe = fe_dai_probe, + }, + { + .capture = { + .stream_name = "Listen 3 Audio Service Capture", + .aif_name = "LSM3_UL_HL", + .rates = (SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_48000), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 4, + .rate_min = 16000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "LSM3", + .probe = fe_dai_probe, + }, + { + .capture = { + .stream_name = "Listen 4 Audio Service Capture", + .aif_name = "LSM4_UL_HL", + .rates = (SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_48000), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 4, + .rate_min = 16000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "LSM4", + .probe = fe_dai_probe, + }, + { + .capture = { + .stream_name = "Listen 5 Audio Service Capture", + .aif_name = "LSM5_UL_HL", + .rates = (SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_48000), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 4, + .rate_min = 16000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "LSM5", + .probe = fe_dai_probe, + }, + { + .capture = { + .stream_name = "Listen 6 Audio Service Capture", + .aif_name = "LSM6_UL_HL", + .rates = (SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_48000), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 4, + .rate_min = 16000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "LSM6", + .probe = fe_dai_probe, + }, + { + .capture = { + .stream_name = "Listen 7 Audio Service Capture", + .aif_name = "LSM7_UL_HL", + .rates = (SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_48000), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 4, + .rate_min = 16000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "LSM7", + .probe = fe_dai_probe, + }, + { + .capture = { + .stream_name = "Listen 8 Audio Service Capture", + .aif_name = "LSM8_UL_HL", + .rates = (SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_48000), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 4, + .rate_min = 16000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "LSM8", + .probe = fe_dai_probe, + }, + /* FE DAIs created for multiple instances of offload playback */ + { + .playback = { + .stream_name = "MultiMedia10 Playback", + .aif_name = "MM_DL10", + .rates = (SNDRV_PCM_RATE_8000_384000 | + SNDRV_PCM_RATE_KNOT), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 384000, + }, + .capture = { + .stream_name = "MultiMedia10 Capture", + .aif_name = "MM_UL10", + .rates = (SNDRV_PCM_RATE_8000_48000 | + SNDRV_PCM_RATE_KNOT), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_Multimedia_dai_ops, + .name = "MultiMedia10", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "MultiMedia11 Playback", + .aif_name = "MM_DL11", + .rates = (SNDRV_PCM_RATE_8000_384000 | + SNDRV_PCM_RATE_KNOT), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 384000, + }, + .ops = &msm_fe_Multimedia_dai_ops, + .compress_new = snd_soc_new_compress, + .name = "MultiMedia11", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "MultiMedia12 Playback", + .aif_name = "MM_DL12", + .rates = (SNDRV_PCM_RATE_8000_384000 | + SNDRV_PCM_RATE_KNOT), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 384000, + }, + .ops = &msm_fe_Multimedia_dai_ops, + .compress_new = snd_soc_new_compress, + .name = "MultiMedia12", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "MultiMedia13 Playback", + .aif_name = "MM_DL13", + .rates = (SNDRV_PCM_RATE_8000_384000 | + SNDRV_PCM_RATE_KNOT), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 384000, + }, + .ops = &msm_fe_Multimedia_dai_ops, + .compress_new = snd_soc_new_compress, + .name = "MultiMedia13", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "MultiMedia14 Playback", + .aif_name = "MM_DL14", + .rates = (SNDRV_PCM_RATE_8000_384000 | + SNDRV_PCM_RATE_KNOT), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 384000, + }, + .ops = &msm_fe_Multimedia_dai_ops, + .compress_new = snd_soc_new_compress, + .name = "MultiMedia14", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "MultiMedia15 Playback", + .aif_name = "MM_DL15", + .rates = (SNDRV_PCM_RATE_8000_384000 | + SNDRV_PCM_RATE_KNOT), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 384000, + }, + .ops = &msm_fe_Multimedia_dai_ops, + .compress_new = snd_soc_new_compress, + .name = "MultiMedia15", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "MultiMedia16 Playback", + .aif_name = "MM_DL16", + .rates = (SNDRV_PCM_RATE_8000_384000 | + SNDRV_PCM_RATE_KNOT), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 384000, + }, + .capture = { + .stream_name = "MultiMedia16 Capture", + .aif_name = "MM_UL16", + .rates = (SNDRV_PCM_RATE_8000_48000| + SNDRV_PCM_RATE_KNOT), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_Multimedia_dai_ops, + .name = "MultiMedia16", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "VoiceMMode1 Playback", + .aif_name = "VOICEMMODE1_DL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + .capture = { + .stream_name = "VoiceMMode1 Capture", + .aif_name = "VOICEMMODE1_UL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "VoiceMMode1", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "VoiceMMode2 Playback", + .aif_name = "VOICEMMODE2_DL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + .capture = { + .stream_name = "VoiceMMode2 Capture", + .aif_name = "VOICEMMODE2_UL", + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_dai_ops, + .name = "VoiceMMode2", + .probe = fe_dai_probe, + }, + { + .capture = { + .stream_name = "MultiMedia17 Capture", + .aif_name = "MM_UL17", + .rates = (SNDRV_PCM_RATE_8000_192000| + SNDRV_PCM_RATE_KNOT), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 192000, + }, + .ops = &msm_fe_Multimedia_dai_ops, + .compress_new = snd_soc_new_compress, + .name = "MultiMedia17", + .probe = fe_dai_probe, + }, + { + .capture = { + .stream_name = "MultiMedia18 Capture", + .aif_name = "MM_UL18", + .rates = (SNDRV_PCM_RATE_8000_192000| + SNDRV_PCM_RATE_KNOT), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 192000, + }, + .ops = &msm_fe_Multimedia_dai_ops, + .compress_new = snd_soc_new_compress, + .name = "MultiMedia18", + .probe = fe_dai_probe, + }, + { + .capture = { + .stream_name = "MultiMedia19 Capture", + .aif_name = "MM_UL19", + .rates = (SNDRV_PCM_RATE_8000_192000| + SNDRV_PCM_RATE_KNOT), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 192000, + }, + .ops = &msm_fe_Multimedia_dai_ops, + .compress_new = snd_soc_new_compress, + .name = "MultiMedia19", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "MultiMedia20 Playback", + .aif_name = "MM_DL20", + .rates = (SNDRV_PCM_RATE_8000_384000| + SNDRV_PCM_RATE_KNOT), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 384000, + }, + .capture = { + .stream_name = "MultiMedia20 Capture", + .aif_name = "MM_UL20", + .rates = (SNDRV_PCM_RATE_8000_48000| + SNDRV_PCM_RATE_KNOT), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_Multimedia_dai_ops, + .name = "MultiMedia20", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "MultiMedia21 Playback", + .aif_name = "MM_DL21", + .rates = (SNDRV_PCM_RATE_8000_384000| + SNDRV_PCM_RATE_KNOT), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 384000, + }, + .capture = { + .stream_name = "MultiMedia21 Capture", + .aif_name = "MM_UL21", + .rates = (SNDRV_PCM_RATE_8000_48000| + SNDRV_PCM_RATE_KNOT), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_fe_Multimedia_dai_ops, + .name = "MultiMedia21", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "MultiMedia26 Playback", + .aif_name = "MM_DL26", + .rates = (SNDRV_PCM_RATE_8000_384000| + SNDRV_PCM_RATE_KNOT), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE), + .channels_min = 1, + .channels_max = 32, + .rate_min = 8000, + .rate_max = 384000, + }, + .ops = &msm_fe_Multimedia_dai_ops, + .compress_new = snd_soc_new_compress, + .name = "MultiMedia26", + .probe = fe_dai_probe, + }, + { + .capture = { + .stream_name = "MultiMedia27 Capture", + .aif_name = "MM_UL27", + .rates = (SNDRV_PCM_RATE_8000_192000| + SNDRV_PCM_RATE_KNOT), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE), + .channels_min = 1, + .channels_max = 32, + .rate_min = 8000, + .rate_max = 192000, + }, + .ops = &msm_fe_Multimedia_dai_ops, + .compress_new = snd_soc_new_compress, + .name = "MultiMedia27", + .probe = fe_dai_probe, + }, + { + .capture = { + .stream_name = "MultiMedia28 Capture", + .aif_name = "MM_UL28", + .rates = (SNDRV_PCM_RATE_8000_192000| + SNDRV_PCM_RATE_KNOT), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 192000, + }, + .ops = &msm_fe_Multimedia_dai_ops, + .compress_new = snd_soc_new_compress, + .name = "MultiMedia28", + .probe = fe_dai_probe, + }, + { + .capture = { + .stream_name = "MultiMedia29 Capture", + .aif_name = "MM_UL29", + .rates = (SNDRV_PCM_RATE_8000_192000| + SNDRV_PCM_RATE_KNOT), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 192000, + }, + .ops = &msm_fe_Multimedia_dai_ops, + .compress_new = snd_soc_new_compress, + .name = "MultiMedia29", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "MultiMedia30 Playback", + .aif_name = "MM_DL30", + .rates = (SNDRV_PCM_RATE_8000_384000| + SNDRV_PCM_RATE_KNOT), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE), + .channels_min = 1, + .channels_max = 32, + .rate_min = 8000, + .rate_max = 384000, + }, + .ops = &msm_fe_Multimedia_dai_ops, + .name = "MultiMedia30", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "MultiMedia31 Playback", + .aif_name = "MM_DL31", + .rates = (SNDRV_PCM_RATE_8000_384000| + SNDRV_PCM_RATE_KNOT), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE), + .channels_min = 1, + .channels_max = 32, + .rate_min = 8000, + .rate_max = 384000, + }, + .ops = &msm_fe_Multimedia_dai_ops, + .name = "MultiMedia31", + .probe = fe_dai_probe, + }, +}; + +static int msm_fe_dai_dev_probe(struct platform_device *pdev) +{ + + dev_dbg(&pdev->dev, "%s: dev name %s\n", __func__, + dev_name(&pdev->dev)); + return snd_soc_register_component(&pdev->dev, &msm_fe_dai_component, + msm_fe_dais, ARRAY_SIZE(msm_fe_dais)); +} + +static int msm_fe_dai_dev_remove(struct platform_device *pdev) +{ + snd_soc_unregister_component(&pdev->dev); + return 0; +} + +static const struct of_device_id msm_dai_fe_dt_match[] = { + {.compatible = "qcom,msm-dai-fe"}, + {} +}; + +static struct platform_driver msm_fe_dai_driver = { + .probe = msm_fe_dai_dev_probe, + .remove = msm_fe_dai_dev_remove, + .driver = { + .name = "msm-dai-fe", + .owner = THIS_MODULE, + .of_match_table = msm_dai_fe_dt_match, + }, +}; + +int __init msm_fe_dai_init(void) +{ + return platform_driver_register(&msm_fe_dai_driver); +} + +void msm_fe_dai_exit(void) +{ + platform_driver_unregister(&msm_fe_dai_driver); +} + +/* Module information */ +MODULE_DESCRIPTION("MSM Frontend DAI driver"); +MODULE_LICENSE("GPL v2"); diff --git a/audio-kernel/asoc/msm-dai-q6-hdmi-v2.c b/audio-kernel/asoc/msm-dai-q6-hdmi-v2.c new file mode 100644 index 000000000..4291077fc --- /dev/null +++ b/audio-kernel/asoc/msm-dai-q6-hdmi-v2.c @@ -0,0 +1,612 @@ +/* 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 + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "msm-dai-q6-v2.h" + +#define HDMI_RX_CA_MAX 0x32 + +enum { + STATUS_PORT_STARTED, /* track if AFE port has started */ + STATUS_MAX +}; + +struct msm_ext_disp_ca { + bool set_ca; + u32 ca; +}; + +struct msm_dai_q6_hdmi_dai_data { + DECLARE_BITMAP(status_mask, STATUS_MAX); + u32 rate; + u32 channels; + u32 stream_idx; + u32 ctl_idx; + struct msm_ext_disp_ca ca; + union afe_port_config port_config; +}; + +static int msm_dai_q6_ext_disp_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct msm_dai_q6_hdmi_dai_data *dai_data = kcontrol->private_data; + int value = ucontrol->value.integer.value[0]; + + if (!dai_data) { + pr_err("%s: dai_data is NULL\n", __func__); + return -EINVAL; + } + + dai_data->port_config.hdmi_multi_ch.datatype = value; + pr_debug("%s: value = %d\n", __func__, value); + + return 0; +} + +static int msm_dai_q6_ext_disp_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct msm_dai_q6_hdmi_dai_data *dai_data = kcontrol->private_data; + + if (!dai_data) { + pr_err("%s: dai_data is NULL\n", __func__); + return -EINVAL; + } + + ucontrol->value.integer.value[0] = + dai_data->port_config.hdmi_multi_ch.datatype; + pr_debug("%s: value = %ld\n", + __func__, ucontrol->value.integer.value[0]); + + return 0; +} + +static int msm_dai_q6_ext_disp_device_idx_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct msm_dai_q6_hdmi_dai_data *dai_data = kcontrol->private_data; + + if (!dai_data) { + pr_err("%s: dai_data is NULL\n", __func__); + return -EINVAL; + } + + dai_data->ctl_idx = ucontrol->value.integer.value[0]; + dai_data->stream_idx = ucontrol->value.integer.value[1]; + pr_debug("%s: DP ctl id %d stream id %d\n", __func__, + dai_data->ctl_idx, dai_data->stream_idx); + + return 0; +} + +static int msm_dai_q6_ext_disp_device_idx_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct msm_dai_q6_hdmi_dai_data *dai_data = kcontrol->private_data; + + if (!dai_data) { + pr_err("%s: dai_data is NULL\n", __func__); + return -EINVAL; + } + + ucontrol->value.integer.value[0] = dai_data->ctl_idx; + ucontrol->value.integer.value[1] = dai_data->stream_idx; + pr_debug("%s: DP ctl id %d stream id %d\n", __func__, + dai_data->ctl_idx, dai_data->stream_idx); + + return 0; +} + +static int msm_dai_q6_ext_disp_ca_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct msm_dai_q6_hdmi_dai_data *dai_data = kcontrol->private_data; + + if (!dai_data) { + pr_err("%s: dai_data is NULL\n", __func__); + return -EINVAL; + } + + dai_data->ca.ca = ucontrol->value.integer.value[0]; + dai_data->ca.set_ca = true; + pr_debug("%s: ca = %d\n", __func__, dai_data->ca.ca); + return 0; +} + +static int msm_dai_q6_ext_disp_ca_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct msm_dai_q6_hdmi_dai_data *dai_data = kcontrol->private_data; + + if (!dai_data) { + pr_err("%s: dai_data is NULL\n", __func__); + return -EINVAL; + } + + ucontrol->value.integer.value[0] = dai_data->ca.ca; + pr_debug("%s: ca = %d\n", __func__, dai_data->ca.ca); + return 0; +} + +/* HDMI format field for AFE_PORT_MULTI_CHAN_HDMI_AUDIO_IF_CONFIG command + * 0: linear PCM + * 1: non-linear PCM + */ +static const char * const hdmi_format[] = { + "LPCM", + "Compr" +}; + +static const struct soc_enum hdmi_config_enum[] = { + SOC_ENUM_SINGLE_EXT(2, hdmi_format), +}; + +static int msm_dai_q6_ext_disp_drift_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; + uinfo->count = sizeof(struct afe_param_id_dev_timing_stats); + + return 0; +} + +static int msm_dai_q6_ext_disp_drift_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret = -EINVAL; + struct afe_param_id_dev_timing_stats timing_stats; + struct snd_soc_dai *dai = kcontrol->private_data; + struct msm_dai_q6_hdmi_dai_data *dai_data = dev_get_drvdata(dai->dev); + + if (!test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) { + pr_debug("%s: afe port not started. status_mask = %ld\n", + __func__, *dai_data->status_mask); + goto done; + } + + memset(&timing_stats, 0, sizeof(struct afe_param_id_dev_timing_stats)); + ret = afe_get_av_dev_drift(&timing_stats, dai->id); + if (ret) { + pr_err("%s: Error getting AFE Drift for port %d, err=%d\n", + __func__, dai->id, ret); + + ret = -EINVAL; + goto done; + } + + memcpy(ucontrol->value.bytes.data, (void *)&timing_stats, + sizeof(struct afe_param_id_dev_timing_stats)); +done: + return ret; +} + +static const struct snd_kcontrol_new hdmi_config_controls[] = { + SOC_ENUM_EXT("HDMI RX Format", hdmi_config_enum[0], + msm_dai_q6_ext_disp_format_get, + msm_dai_q6_ext_disp_format_put), + SOC_SINGLE_MULTI_EXT("HDMI RX CA", SND_SOC_NOPM, 0, + HDMI_RX_CA_MAX, 0, 1, + msm_dai_q6_ext_disp_ca_get, + msm_dai_q6_ext_disp_ca_put), + { + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "HDMI DRIFT", + .info = msm_dai_q6_ext_disp_drift_info, + .get = msm_dai_q6_ext_disp_drift_get, + }, +}; + +static const struct snd_kcontrol_new display_port_config_controls[] = { + SOC_ENUM_EXT("Display Port RX Format", hdmi_config_enum[0], + msm_dai_q6_ext_disp_format_get, + msm_dai_q6_ext_disp_format_put), + SOC_SINGLE_MULTI_EXT("Display Port RX CA", SND_SOC_NOPM, 0, + HDMI_RX_CA_MAX, 0, 1, + msm_dai_q6_ext_disp_ca_get, + msm_dai_q6_ext_disp_ca_put), + SOC_SINGLE_MULTI_EXT("Display Port RX DEVICE IDX", SND_SOC_NOPM, 0, + 1, 0, 1, + msm_dai_q6_ext_disp_device_idx_get, + msm_dai_q6_ext_disp_device_idx_put), + { + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "DISPLAY_PORT DRIFT", + .info = msm_dai_q6_ext_disp_drift_info, + .get = msm_dai_q6_ext_disp_drift_get, + }, +}; + +/* Current implementation assumes hw_param is called once + * This may not be the case but what to do when ADM and AFE + * port are already opened and parameter changes + */ +static int msm_dai_q6_hdmi_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct msm_dai_q6_hdmi_dai_data *dai_data = dev_get_drvdata(dai->dev); + + dai_data->channels = params_channels(params); + dai_data->rate = params_rate(params); + dai_data->port_config.hdmi_multi_ch.reserved = 0; + dai_data->port_config.hdmi_multi_ch.hdmi_cfg_minor_version = 1; + dai_data->port_config.hdmi_multi_ch.sample_rate = dai_data->rate; + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + dai_data->port_config.hdmi_multi_ch.bit_width = 16; + break; + case SNDRV_PCM_FORMAT_S24_LE: + case SNDRV_PCM_FORMAT_S24_3LE: + dai_data->port_config.hdmi_multi_ch.bit_width = 24; + break; + } + + /*refer to HDMI spec CEA-861-E: Table 28 Audio InfoFrame Data Byte 4*/ + switch (dai_data->channels) { + case 2: + dai_data->port_config.hdmi_multi_ch.channel_allocation = 0; + break; + case 3: + dai_data->port_config.hdmi_multi_ch.channel_allocation = 0x02; + break; + case 4: + dai_data->port_config.hdmi_multi_ch.channel_allocation = 0x06; + break; + case 5: + dai_data->port_config.hdmi_multi_ch.channel_allocation = 0x0A; + break; + case 6: + dai_data->port_config.hdmi_multi_ch.channel_allocation = 0x0B; + break; + case 7: + dai_data->port_config.hdmi_multi_ch.channel_allocation = 0x12; + break; + case 8: + dai_data->port_config.hdmi_multi_ch.channel_allocation = 0x13; + break; + default: + dev_err(dai->dev, "invalid Channels = %u\n", + dai_data->channels); + return -EINVAL; + } + dev_dbg(dai->dev, "%s() minor version: %u samplerate: %u bitwidth: %u\n" + "num_ch = %u channel_allocation = %u datatype = %d\n", __func__, + dai_data->port_config.hdmi_multi_ch.hdmi_cfg_minor_version, + dai_data->port_config.hdmi_multi_ch.sample_rate, + dai_data->port_config.hdmi_multi_ch.bit_width, + dai_data->channels, + dai_data->port_config.hdmi_multi_ch.channel_allocation, + dai_data->port_config.hdmi_multi_ch.datatype); + + return 0; +} + + +static void msm_dai_q6_hdmi_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct msm_dai_q6_hdmi_dai_data *dai_data = dev_get_drvdata(dai->dev); + int rc = 0; + + if (!test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) { + pr_info("%s: afe port not started. dai_data->status_mask = %ld\n", + __func__, *dai_data->status_mask); + return; + } + + rc = afe_close(dai->id); /* can block */ + if (rc < 0) + dev_err(dai->dev, "fail to close AFE port\n"); + + pr_debug("%s: dai_data->status_mask = %ld\n", __func__, + *dai_data->status_mask); + + clear_bit(STATUS_PORT_STARTED, dai_data->status_mask); +} + + +static int msm_dai_q6_hdmi_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct msm_dai_q6_hdmi_dai_data *dai_data = dev_get_drvdata(dai->dev); + int rc = 0; + + if (dai_data->ca.set_ca) + dai_data->port_config.hdmi_multi_ch.channel_allocation = + dai_data->ca.ca; + + if (!test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) { + + rc = afe_set_display_stream(dai->id, dai_data->stream_idx, + dai_data->ctl_idx); + if (rc < 0) { + dev_err(dai->dev, "fail to set AFE ctl, stream ID params %x\n", + dai->id); + if (rc != -EOPNOTSUPP) { + dev_err(dai->dev, "not starting AFE port\n"); + goto err; + } + } + + rc = afe_port_start(dai->id, &dai_data->port_config, + dai_data->rate); + if (rc < 0) + dev_err(dai->dev, "fail to open AFE port %x\n", + dai->id); + else + set_bit(STATUS_PORT_STARTED, + dai_data->status_mask); + } + +err: + return rc; +} + +static inline void msm_dai_q6_hdmi_set_dai_id(struct snd_soc_dai *dai) +{ + if (!dai->driver->id) { + dev_warn(dai->dev, "DAI driver id is not set\n"); + return; + } + dai->id = dai->driver->id; +} + +static int msm_dai_q6_hdmi_dai_probe(struct snd_soc_dai *dai) +{ + struct msm_dai_q6_hdmi_dai_data *dai_data; + const struct snd_kcontrol_new *kcontrol; + int rc = 0; + struct snd_soc_dapm_route intercon; + struct snd_soc_dapm_context *dapm; + + if (!dai || !dai->driver) { + pr_err("%s: dai or dai->driver is NULL\n", __func__); + return -EINVAL; + } + dai_data = kzalloc(sizeof(struct msm_dai_q6_hdmi_dai_data), + GFP_KERNEL); + + if (!dai_data) { + dev_err(dai->dev, "DAI-%d: fail to allocate dai data\n", + dai->id); + rc = -ENOMEM; + } else + dev_set_drvdata(dai->dev, dai_data); + + msm_dai_q6_hdmi_set_dai_id(dai); + + if (dai->driver->id == HDMI_RX) { + kcontrol = &hdmi_config_controls[0]; + rc = snd_ctl_add(dai->component->card->snd_card, + snd_ctl_new1(kcontrol, dai_data)); + + kcontrol = &hdmi_config_controls[1]; + rc = snd_ctl_add(dai->component->card->snd_card, + snd_ctl_new1(kcontrol, dai_data)); + + kcontrol = &hdmi_config_controls[2]; + rc = snd_ctl_add(dai->component->card->snd_card, + snd_ctl_new1(kcontrol, dai)); + } else if (dai->driver->id == DISPLAY_PORT_RX) { + kcontrol = &display_port_config_controls[0]; + rc = snd_ctl_add(dai->component->card->snd_card, + snd_ctl_new1(kcontrol, dai_data)); + + kcontrol = &display_port_config_controls[1]; + rc = snd_ctl_add(dai->component->card->snd_card, + snd_ctl_new1(kcontrol, dai_data)); + + kcontrol = &display_port_config_controls[2]; + rc = snd_ctl_add(dai->component->card->snd_card, + snd_ctl_new1(kcontrol, dai_data)); + + kcontrol = &display_port_config_controls[3]; + rc = snd_ctl_add(dai->component->card->snd_card, + snd_ctl_new1(kcontrol, dai)); + } else { + dev_err(dai->dev, "%s: Invalid id:%d\n", + __func__, dai->driver->id); + kfree(dai_data); + dev_set_drvdata(dai->dev, NULL); + return -EINVAL; + } + + dapm = snd_soc_component_get_dapm(dai->component); + memset(&intercon, 0, sizeof(intercon)); + if (!rc) { + if (dai->driver->playback.stream_name && + dai->driver->playback.aif_name) { + dev_dbg(dai->dev, "%s add route for widget %s", + __func__, dai->driver->playback.stream_name); + intercon.source = dai->driver->playback.aif_name; + intercon.sink = dai->driver->playback.stream_name; + dev_dbg(dai->dev, "%s src %s sink %s\n", + __func__, intercon.source, intercon.sink); + snd_soc_dapm_add_routes(dapm, &intercon, 1); + } + if (dai->driver->capture.stream_name && + dai->driver->capture.aif_name) { + dev_dbg(dai->dev, "%s add route for widget %s", + __func__, dai->driver->capture.stream_name); + intercon.sink = dai->driver->capture.aif_name; + intercon.source = dai->driver->capture.stream_name; + dev_dbg(dai->dev, "%s src %s sink %s\n", + __func__, intercon.source, intercon.sink); + snd_soc_dapm_add_routes(dapm, &intercon, 1); + } + } + return rc; +} + +static int msm_dai_q6_hdmi_dai_remove(struct snd_soc_dai *dai) +{ + struct msm_dai_q6_hdmi_dai_data *dai_data; + int rc; + + dai_data = dev_get_drvdata(dai->dev); + + /* If AFE port is still up, close it */ + if (test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) { + rc = afe_close(dai->id); /* can block */ + if (rc < 0) + dev_err(dai->dev, "fail to close AFE port\n"); + + clear_bit(STATUS_PORT_STARTED, dai_data->status_mask); + } + kfree(dai_data); + + return 0; +} + +static struct snd_soc_dai_ops msm_dai_q6_hdmi_ops = { + .prepare = msm_dai_q6_hdmi_prepare, + .hw_params = msm_dai_q6_hdmi_hw_params, + .shutdown = msm_dai_q6_hdmi_shutdown, +}; + +static struct snd_soc_dai_driver msm_dai_q6_hdmi_hdmi_rx_dai = { + .playback = { + .stream_name = "HDMI Playback", + .aif_name = "HDMI", + .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 | + SNDRV_PCM_RATE_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE, + .channels_min = 2, + .channels_max = 8, + .rate_max = 192000, + .rate_min = 48000, + }, + .ops = &msm_dai_q6_hdmi_ops, + .id = HDMI_RX, + .probe = msm_dai_q6_hdmi_dai_probe, + .remove = msm_dai_q6_hdmi_dai_remove, +}; + +static struct snd_soc_dai_driver msm_dai_q6_display_port_rx_dai[] = { + { + .playback = { + .stream_name = "Display Port Playback", + .aif_name = "DISPLAY_PORT", + .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 | + SNDRV_PCM_RATE_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE, + .channels_min = 2, + .channels_max = 8, + .rate_max = 192000, + .rate_min = 48000, + }, + .ops = &msm_dai_q6_hdmi_ops, + .id = DISPLAY_PORT_RX, + .probe = msm_dai_q6_hdmi_dai_probe, + .remove = msm_dai_q6_hdmi_dai_remove, + }, +}; + +static const struct snd_soc_component_driver msm_dai_hdmi_q6_component = { + .name = "msm-dai-q6-hdmi", +}; + +/* To do: change to register DAIs as batch */ +static int msm_dai_q6_hdmi_dev_probe(struct platform_device *pdev) +{ + int rc, id; + const char *q6_dev_id = "qcom,msm-dai-q6-dev-id"; + + rc = of_property_read_u32(pdev->dev.of_node, q6_dev_id, &id); + if (rc) { + dev_err(&pdev->dev, + "%s: missing %s in dt node\n", __func__, q6_dev_id); + return rc; + } + + pdev->id = id; + + pr_debug("%s: dev name %s, id:%d\n", __func__, + dev_name(&pdev->dev), pdev->id); + + switch (pdev->id) { + case HDMI_RX: + rc = snd_soc_register_component(&pdev->dev, + &msm_dai_hdmi_q6_component, + &msm_dai_q6_hdmi_hdmi_rx_dai, 1); + break; + case DISPLAY_PORT_RX: + rc = snd_soc_register_component(&pdev->dev, + &msm_dai_hdmi_q6_component, + &msm_dai_q6_display_port_rx_dai[0], + ARRAY_SIZE(msm_dai_q6_display_port_rx_dai)); + break; + default: + dev_err(&pdev->dev, "invalid device ID %d\n", pdev->id); + rc = -ENODEV; + break; + } + return rc; +} + +static int msm_dai_q6_hdmi_dev_remove(struct platform_device *pdev) +{ + snd_soc_unregister_component(&pdev->dev); + return 0; +} + +static const struct of_device_id msm_dai_q6_hdmi_dt_match[] = { + {.compatible = "qcom,msm-dai-q6-hdmi"}, + {} +}; +MODULE_DEVICE_TABLE(of, msm_dai_q6_hdmi_dt_match); + +static struct platform_driver msm_dai_q6_hdmi_driver = { + .probe = msm_dai_q6_hdmi_dev_probe, + .remove = msm_dai_q6_hdmi_dev_remove, + .driver = { + .name = "msm-dai-q6-hdmi", + .owner = THIS_MODULE, + .of_match_table = msm_dai_q6_hdmi_dt_match, + }, +}; + +int __init msm_dai_q6_hdmi_init(void) +{ + return platform_driver_register(&msm_dai_q6_hdmi_driver); +} + +void msm_dai_q6_hdmi_exit(void) +{ + platform_driver_unregister(&msm_dai_q6_hdmi_driver); +} + +/* Module information */ +MODULE_DESCRIPTION("MSM DSP HDMI DAI driver"); +MODULE_LICENSE("GPL v2"); diff --git a/audio-kernel/asoc/msm-dai-q6-v2.c b/audio-kernel/asoc/msm-dai-q6-v2.c new file mode 100644 index 000000000..49babfc29 --- /dev/null +++ b/audio-kernel/asoc/msm-dai-q6-v2.c @@ -0,0 +1,11247 @@ +/* Copyright (c) 2012-2019, 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "msm-dai-q6-v2.h" +#include "codecs/core.h" + +#define MSM_DAI_PRI_AUXPCM_DT_DEV_ID 1 +#define MSM_DAI_SEC_AUXPCM_DT_DEV_ID 2 +#define MSM_DAI_TERT_AUXPCM_DT_DEV_ID 3 +#define MSM_DAI_QUAT_AUXPCM_DT_DEV_ID 4 +#define MSM_DAI_QUIN_AUXPCM_DT_DEV_ID 5 + +#define MSM_DAI_TWS_CHANNEL_MODE_ONE 1 +#define MSM_DAI_TWS_CHANNEL_MODE_TWO 2 + +#define spdif_clock_value(rate) (2*rate*32*2) +#define CHANNEL_STATUS_SIZE 24 +#define CHANNEL_STATUS_MASK_INIT 0x0 +#define CHANNEL_STATUS_MASK 0x4 +#define AFE_API_VERSION_CLOCK_SET 1 +#define MSM_DAI_SYSFS_ENTRY_MAX_LEN 64 + +#define DAI_FORMATS_S16_S24_S32_LE (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) + +static int msm_mi2s_get_port_id(u32 mi2s_id, int stream, u16 *port_id); + +enum { + ENC_FMT_NONE, + DEC_FMT_NONE = ENC_FMT_NONE, + ENC_FMT_SBC = ASM_MEDIA_FMT_SBC, + DEC_FMT_SBC = ASM_MEDIA_FMT_SBC, + ENC_FMT_AAC_V2 = ASM_MEDIA_FMT_AAC_V2, + DEC_FMT_AAC_V2 = ASM_MEDIA_FMT_AAC_V2, + ENC_FMT_APTX = ASM_MEDIA_FMT_APTX, + ENC_FMT_APTX_HD = ASM_MEDIA_FMT_APTX_HD, + ENC_FMT_CELT = ASM_MEDIA_FMT_CELT, + ENC_FMT_LDAC = ASM_MEDIA_FMT_LDAC, + ENC_FMT_APTX_ADAPTIVE = ASM_MEDIA_FMT_APTX_ADAPTIVE, + DEC_FMT_APTX_ADAPTIVE = ASM_MEDIA_FMT_APTX_ADAPTIVE, + DEC_FMT_MP3 = ASM_MEDIA_FMT_MP3, +}; + +enum { + SPKR_1, + SPKR_2, +}; + +static const struct afe_clk_set lpass_clk_set_default = { + AFE_API_VERSION_CLOCK_SET, + Q6AFE_LPASS_CLK_ID_PRI_PCM_IBIT, + Q6AFE_LPASS_OSR_CLK_2_P048_MHZ, + Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO, + Q6AFE_LPASS_CLK_ROOT_DEFAULT, + 0, +}; + +static const struct afe_clk_cfg lpass_clk_cfg_default = { + AFE_API_VERSION_I2S_CONFIG, + Q6AFE_LPASS_OSR_CLK_2_P048_MHZ, + 0, + Q6AFE_LPASS_CLK_SRC_INTERNAL, + Q6AFE_LPASS_CLK_ROOT_DEFAULT, + Q6AFE_LPASS_MODE_CLK1_VALID, + 0, +}; +enum { + STATUS_PORT_STARTED, /* track if AFE port has started */ + /* track AFE Tx port status for bi-directional transfers */ + STATUS_TX_PORT, + /* track AFE Rx port status for bi-directional transfers */ + STATUS_RX_PORT, + STATUS_MAX +}; + +enum { + RATE_8KHZ, + RATE_16KHZ, + RATE_MAX_NUM_OF_AUX_PCM_RATES, +}; + +enum { + IDX_PRIMARY_TDM_RX_0, + IDX_PRIMARY_TDM_RX_1, + IDX_PRIMARY_TDM_RX_2, + IDX_PRIMARY_TDM_RX_3, + IDX_PRIMARY_TDM_RX_4, + IDX_PRIMARY_TDM_RX_5, + IDX_PRIMARY_TDM_RX_6, + IDX_PRIMARY_TDM_RX_7, + IDX_PRIMARY_TDM_TX_0, + IDX_PRIMARY_TDM_TX_1, + IDX_PRIMARY_TDM_TX_2, + IDX_PRIMARY_TDM_TX_3, + IDX_PRIMARY_TDM_TX_4, + IDX_PRIMARY_TDM_TX_5, + IDX_PRIMARY_TDM_TX_6, + IDX_PRIMARY_TDM_TX_7, + IDX_SECONDARY_TDM_RX_0, + IDX_SECONDARY_TDM_RX_1, + IDX_SECONDARY_TDM_RX_2, + IDX_SECONDARY_TDM_RX_3, + IDX_SECONDARY_TDM_RX_4, + IDX_SECONDARY_TDM_RX_5, + IDX_SECONDARY_TDM_RX_6, + IDX_SECONDARY_TDM_RX_7, + IDX_SECONDARY_TDM_TX_0, + IDX_SECONDARY_TDM_TX_1, + IDX_SECONDARY_TDM_TX_2, + IDX_SECONDARY_TDM_TX_3, + IDX_SECONDARY_TDM_TX_4, + IDX_SECONDARY_TDM_TX_5, + IDX_SECONDARY_TDM_TX_6, + IDX_SECONDARY_TDM_TX_7, + IDX_TERTIARY_TDM_RX_0, + IDX_TERTIARY_TDM_RX_1, + IDX_TERTIARY_TDM_RX_2, + IDX_TERTIARY_TDM_RX_3, + IDX_TERTIARY_TDM_RX_4, + IDX_TERTIARY_TDM_RX_5, + IDX_TERTIARY_TDM_RX_6, + IDX_TERTIARY_TDM_RX_7, + IDX_TERTIARY_TDM_TX_0, + IDX_TERTIARY_TDM_TX_1, + IDX_TERTIARY_TDM_TX_2, + IDX_TERTIARY_TDM_TX_3, + IDX_TERTIARY_TDM_TX_4, + IDX_TERTIARY_TDM_TX_5, + IDX_TERTIARY_TDM_TX_6, + IDX_TERTIARY_TDM_TX_7, + IDX_QUATERNARY_TDM_RX_0, + IDX_QUATERNARY_TDM_RX_1, + IDX_QUATERNARY_TDM_RX_2, + IDX_QUATERNARY_TDM_RX_3, + IDX_QUATERNARY_TDM_RX_4, + IDX_QUATERNARY_TDM_RX_5, + IDX_QUATERNARY_TDM_RX_6, + IDX_QUATERNARY_TDM_RX_7, + IDX_QUATERNARY_TDM_TX_0, + IDX_QUATERNARY_TDM_TX_1, + IDX_QUATERNARY_TDM_TX_2, + IDX_QUATERNARY_TDM_TX_3, + IDX_QUATERNARY_TDM_TX_4, + IDX_QUATERNARY_TDM_TX_5, + IDX_QUATERNARY_TDM_TX_6, + IDX_QUATERNARY_TDM_TX_7, + IDX_QUINARY_TDM_RX_0, + IDX_QUINARY_TDM_RX_1, + IDX_QUINARY_TDM_RX_2, + IDX_QUINARY_TDM_RX_3, + IDX_QUINARY_TDM_RX_4, + IDX_QUINARY_TDM_RX_5, + IDX_QUINARY_TDM_RX_6, + IDX_QUINARY_TDM_RX_7, + IDX_QUINARY_TDM_TX_0, + IDX_QUINARY_TDM_TX_1, + IDX_QUINARY_TDM_TX_2, + IDX_QUINARY_TDM_TX_3, + IDX_QUINARY_TDM_TX_4, + IDX_QUINARY_TDM_TX_5, + IDX_QUINARY_TDM_TX_6, + IDX_QUINARY_TDM_TX_7, + IDX_TDM_MAX, +}; + +enum { + IDX_GROUP_PRIMARY_TDM_RX, + IDX_GROUP_PRIMARY_TDM_TX, + IDX_GROUP_SECONDARY_TDM_RX, + IDX_GROUP_SECONDARY_TDM_TX, + IDX_GROUP_TERTIARY_TDM_RX, + IDX_GROUP_TERTIARY_TDM_TX, + IDX_GROUP_QUATERNARY_TDM_RX, + IDX_GROUP_QUATERNARY_TDM_TX, + IDX_GROUP_QUINARY_TDM_RX, + IDX_GROUP_QUINARY_TDM_TX, + IDX_GROUP_TDM_MAX, +}; + +struct msm_dai_q6_dai_data { + DECLARE_BITMAP(status_mask, STATUS_MAX); + DECLARE_BITMAP(hwfree_status, STATUS_MAX); + u32 rate; + u32 channels; + u32 bitwidth; + u32 cal_mode; + u32 afe_rx_in_channels; + u16 afe_rx_in_bitformat; + u32 afe_tx_out_channels; + u16 afe_tx_out_bitformat; + struct afe_enc_config enc_config; + struct afe_dec_config dec_config; + union afe_port_config port_config; + u16 vi_feed_mono; +}; + +struct msm_dai_q6_spdif_dai_data { + DECLARE_BITMAP(status_mask, STATUS_MAX); + u32 rate; + u32 channels; + u32 bitwidth; + u16 port_id; + struct afe_spdif_port_config spdif_port; + struct afe_event_fmt_update fmt_event; + struct kobject *kobj; +}; + +struct msm_dai_q6_spdif_event_msg { + struct afe_port_mod_evt_rsp_hdr evt_hdr; + struct afe_event_fmt_update fmt_event; +}; + +struct msm_dai_q6_mi2s_dai_config { + u16 pdata_mi2s_lines; + struct msm_dai_q6_dai_data mi2s_dai_data; +}; + +struct msm_dai_q6_mi2s_dai_data { + u32 is_island_dai; + struct msm_dai_q6_mi2s_dai_config tx_dai; + struct msm_dai_q6_mi2s_dai_config rx_dai; +}; + +struct msm_dai_q6_cdc_dma_dai_data { + DECLARE_BITMAP(status_mask, STATUS_MAX); + DECLARE_BITMAP(hwfree_status, STATUS_MAX); + u32 rate; + u32 channels; + u32 bitwidth; + u32 is_island_dai; + union afe_port_config port_config; +}; + +struct msm_dai_q6_auxpcm_dai_data { + /* BITMAP to track Rx and Tx port usage count */ + DECLARE_BITMAP(auxpcm_port_status, STATUS_MAX); + struct mutex rlock; /* auxpcm dev resource lock */ + u16 rx_pid; /* AUXPCM RX AFE port ID */ + u16 tx_pid; /* AUXPCM TX AFE port ID */ + u16 afe_clk_ver; + u32 is_island_dai; + struct afe_clk_cfg clk_cfg; /* hold LPASS clock configuration */ + struct afe_clk_set clk_set; /* hold LPASS clock configuration */ + struct msm_dai_q6_dai_data bdai_data; /* incoporate base DAI data */ +}; + +struct msm_dai_q6_tdm_dai_data { + DECLARE_BITMAP(status_mask, STATUS_MAX); + u32 rate; + u32 channels; + u32 bitwidth; + u32 num_group_ports; + u32 is_island_dai; + struct afe_clk_set clk_set; /* hold LPASS clock config. */ + union afe_port_group_config group_cfg; /* hold tdm group config */ + struct afe_tdm_port_config port_cfg; /* hold tdm config */ +}; + +/* MI2S format field for AFE_PORT_CMD_I2S_CONFIG command + * 0: linear PCM + * 1: non-linear PCM + * 2: PCM data in IEC 60968 container + * 3: compressed data in IEC 60958 container + */ +static const char *const mi2s_format[] = { + "LPCM", + "Compr", + "LPCM-60958", + "Compr-60958" +}; + +static const char *const mi2s_vi_feed_mono[] = { + "Left", + "Right", +}; + +static const struct soc_enum mi2s_config_enum[] = { + SOC_ENUM_SINGLE_EXT(4, mi2s_format), + SOC_ENUM_SINGLE_EXT(2, mi2s_vi_feed_mono), +}; + +static const char *const cdc_dma_format[] = { + "UNPACKED", + "PACKED_16B", +}; + +static const struct soc_enum cdc_dma_config_enum[] = { + SOC_ENUM_SINGLE_EXT(2, cdc_dma_format), +}; + +static const char *const sb_format[] = { + "UNPACKED", + "PACKED_16B", + "DSD_DOP", +}; + +static const struct soc_enum sb_config_enum[] = { + SOC_ENUM_SINGLE_EXT(3, sb_format), +}; + +static const char *const tdm_data_format[] = { + "LPCM", + "Compr", + "Gen Compr" +}; + +static const char *const tdm_header_type[] = { + "Invalid", + "Default", + "Entertainment", +}; + +static const struct soc_enum tdm_config_enum[] = { + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(tdm_data_format), tdm_data_format), + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(tdm_header_type), tdm_header_type), +}; + +static DEFINE_MUTEX(tdm_mutex); + +static atomic_t tdm_group_ref[IDX_GROUP_TDM_MAX]; + +/* cache of group cfg per parent node */ +static struct afe_param_id_group_device_tdm_cfg tdm_group_cfg = { + AFE_API_VERSION_GROUP_DEVICE_TDM_CONFIG, + AFE_GROUP_DEVICE_ID_QUATERNARY_TDM_RX, + 0, + {AFE_PORT_ID_QUATERNARY_TDM_RX, + AFE_PORT_ID_QUATERNARY_TDM_RX_1, + AFE_PORT_ID_QUATERNARY_TDM_RX_2, + AFE_PORT_ID_QUATERNARY_TDM_RX_3, + AFE_PORT_ID_QUATERNARY_TDM_RX_4, + AFE_PORT_ID_QUATERNARY_TDM_RX_5, + AFE_PORT_ID_QUATERNARY_TDM_RX_6, + AFE_PORT_ID_QUATERNARY_TDM_RX_7}, + 8, + 48000, + 32, + 8, + 32, + 0xFF, +}; + +static u32 num_tdm_group_ports; + +static struct afe_clk_set tdm_clk_set = { + AFE_API_VERSION_CLOCK_SET, + Q6AFE_LPASS_CLK_ID_QUAD_TDM_EBIT, + Q6AFE_LPASS_IBIT_CLK_DISABLE, + Q6AFE_LPASS_CLK_ATTRIBUTE_INVERT_COUPLE_NO, + Q6AFE_LPASS_CLK_ROOT_DEFAULT, + 0, +}; + +static int msm_dai_q6_get_tdm_clk_ref(u16 id) +{ + switch (id) { + case IDX_GROUP_PRIMARY_TDM_RX: + case IDX_GROUP_PRIMARY_TDM_TX: + return atomic_read(&tdm_group_ref[IDX_GROUP_PRIMARY_TDM_RX]) + + atomic_read(&tdm_group_ref[IDX_GROUP_PRIMARY_TDM_TX]); + case IDX_GROUP_SECONDARY_TDM_RX: + case IDX_GROUP_SECONDARY_TDM_TX: + return atomic_read(&tdm_group_ref[IDX_GROUP_SECONDARY_TDM_RX]) + + atomic_read(&tdm_group_ref[IDX_GROUP_SECONDARY_TDM_TX]); + case IDX_GROUP_TERTIARY_TDM_RX: + case IDX_GROUP_TERTIARY_TDM_TX: + return atomic_read(&tdm_group_ref[IDX_GROUP_TERTIARY_TDM_RX]) + + atomic_read(&tdm_group_ref[IDX_GROUP_TERTIARY_TDM_TX]); + case IDX_GROUP_QUATERNARY_TDM_RX: + case IDX_GROUP_QUATERNARY_TDM_TX: + return atomic_read(&tdm_group_ref[IDX_GROUP_QUATERNARY_TDM_RX]) + + atomic_read(&tdm_group_ref[IDX_GROUP_QUATERNARY_TDM_TX]); + case IDX_GROUP_QUINARY_TDM_RX: + case IDX_GROUP_QUINARY_TDM_TX: + return atomic_read(&tdm_group_ref[IDX_GROUP_QUINARY_TDM_RX]) + + atomic_read(&tdm_group_ref[IDX_GROUP_QUINARY_TDM_TX]); + default: return -EINVAL; + } +} + +int msm_dai_q6_get_group_idx(u16 id) +{ + switch (id) { + case AFE_GROUP_DEVICE_ID_PRIMARY_TDM_RX: + 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: + return IDX_GROUP_PRIMARY_TDM_RX; + case AFE_GROUP_DEVICE_ID_PRIMARY_TDM_TX: + case AFE_PORT_ID_PRIMARY_TDM_TX: + case AFE_PORT_ID_PRIMARY_TDM_TX_1: + case AFE_PORT_ID_PRIMARY_TDM_TX_2: + case AFE_PORT_ID_PRIMARY_TDM_TX_3: + case AFE_PORT_ID_PRIMARY_TDM_TX_4: + case AFE_PORT_ID_PRIMARY_TDM_TX_5: + case AFE_PORT_ID_PRIMARY_TDM_TX_6: + case AFE_PORT_ID_PRIMARY_TDM_TX_7: + return IDX_GROUP_PRIMARY_TDM_TX; + case AFE_GROUP_DEVICE_ID_SECONDARY_TDM_RX: + 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: + return IDX_GROUP_SECONDARY_TDM_RX; + case AFE_GROUP_DEVICE_ID_SECONDARY_TDM_TX: + case AFE_PORT_ID_SECONDARY_TDM_TX: + case AFE_PORT_ID_SECONDARY_TDM_TX_1: + case AFE_PORT_ID_SECONDARY_TDM_TX_2: + case AFE_PORT_ID_SECONDARY_TDM_TX_3: + case AFE_PORT_ID_SECONDARY_TDM_TX_4: + case AFE_PORT_ID_SECONDARY_TDM_TX_5: + case AFE_PORT_ID_SECONDARY_TDM_TX_6: + case AFE_PORT_ID_SECONDARY_TDM_TX_7: + return IDX_GROUP_SECONDARY_TDM_TX; + case AFE_GROUP_DEVICE_ID_TERTIARY_TDM_RX: + 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: + return IDX_GROUP_TERTIARY_TDM_RX; + case AFE_GROUP_DEVICE_ID_TERTIARY_TDM_TX: + case AFE_PORT_ID_TERTIARY_TDM_TX: + case AFE_PORT_ID_TERTIARY_TDM_TX_1: + case AFE_PORT_ID_TERTIARY_TDM_TX_2: + case AFE_PORT_ID_TERTIARY_TDM_TX_3: + case AFE_PORT_ID_TERTIARY_TDM_TX_4: + case AFE_PORT_ID_TERTIARY_TDM_TX_5: + case AFE_PORT_ID_TERTIARY_TDM_TX_6: + case AFE_PORT_ID_TERTIARY_TDM_TX_7: + return IDX_GROUP_TERTIARY_TDM_TX; + case AFE_GROUP_DEVICE_ID_QUATERNARY_TDM_RX: + 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: + return IDX_GROUP_QUATERNARY_TDM_RX; + case AFE_GROUP_DEVICE_ID_QUATERNARY_TDM_TX: + case AFE_PORT_ID_QUATERNARY_TDM_TX: + case AFE_PORT_ID_QUATERNARY_TDM_TX_1: + case AFE_PORT_ID_QUATERNARY_TDM_TX_2: + case AFE_PORT_ID_QUATERNARY_TDM_TX_3: + case AFE_PORT_ID_QUATERNARY_TDM_TX_4: + case AFE_PORT_ID_QUATERNARY_TDM_TX_5: + case AFE_PORT_ID_QUATERNARY_TDM_TX_6: + case AFE_PORT_ID_QUATERNARY_TDM_TX_7: + return IDX_GROUP_QUATERNARY_TDM_TX; + case AFE_GROUP_DEVICE_ID_QUINARY_TDM_RX: + case AFE_PORT_ID_QUINARY_TDM_RX: + case AFE_PORT_ID_QUINARY_TDM_RX_1: + case AFE_PORT_ID_QUINARY_TDM_RX_2: + case AFE_PORT_ID_QUINARY_TDM_RX_3: + case AFE_PORT_ID_QUINARY_TDM_RX_4: + case AFE_PORT_ID_QUINARY_TDM_RX_5: + case AFE_PORT_ID_QUINARY_TDM_RX_6: + case AFE_PORT_ID_QUINARY_TDM_RX_7: + return IDX_GROUP_QUINARY_TDM_RX; + case AFE_GROUP_DEVICE_ID_QUINARY_TDM_TX: + case AFE_PORT_ID_QUINARY_TDM_TX: + case AFE_PORT_ID_QUINARY_TDM_TX_1: + case AFE_PORT_ID_QUINARY_TDM_TX_2: + case AFE_PORT_ID_QUINARY_TDM_TX_3: + case AFE_PORT_ID_QUINARY_TDM_TX_4: + case AFE_PORT_ID_QUINARY_TDM_TX_5: + case AFE_PORT_ID_QUINARY_TDM_TX_6: + case AFE_PORT_ID_QUINARY_TDM_TX_7: + return IDX_GROUP_QUINARY_TDM_TX; + default: return -EINVAL; + } +} + +int msm_dai_q6_get_port_idx(u16 id) +{ + switch (id) { + case AFE_PORT_ID_PRIMARY_TDM_RX: + return IDX_PRIMARY_TDM_RX_0; + case AFE_PORT_ID_PRIMARY_TDM_TX: + return IDX_PRIMARY_TDM_TX_0; + case AFE_PORT_ID_PRIMARY_TDM_RX_1: + return IDX_PRIMARY_TDM_RX_1; + case AFE_PORT_ID_PRIMARY_TDM_TX_1: + return IDX_PRIMARY_TDM_TX_1; + case AFE_PORT_ID_PRIMARY_TDM_RX_2: + return IDX_PRIMARY_TDM_RX_2; + case AFE_PORT_ID_PRIMARY_TDM_TX_2: + return IDX_PRIMARY_TDM_TX_2; + case AFE_PORT_ID_PRIMARY_TDM_RX_3: + return IDX_PRIMARY_TDM_RX_3; + case AFE_PORT_ID_PRIMARY_TDM_TX_3: + return IDX_PRIMARY_TDM_TX_3; + case AFE_PORT_ID_PRIMARY_TDM_RX_4: + return IDX_PRIMARY_TDM_RX_4; + case AFE_PORT_ID_PRIMARY_TDM_TX_4: + return IDX_PRIMARY_TDM_TX_4; + case AFE_PORT_ID_PRIMARY_TDM_RX_5: + return IDX_PRIMARY_TDM_RX_5; + case AFE_PORT_ID_PRIMARY_TDM_TX_5: + return IDX_PRIMARY_TDM_TX_5; + case AFE_PORT_ID_PRIMARY_TDM_RX_6: + return IDX_PRIMARY_TDM_RX_6; + case AFE_PORT_ID_PRIMARY_TDM_TX_6: + return IDX_PRIMARY_TDM_TX_6; + case AFE_PORT_ID_PRIMARY_TDM_RX_7: + return IDX_PRIMARY_TDM_RX_7; + case AFE_PORT_ID_PRIMARY_TDM_TX_7: + return IDX_PRIMARY_TDM_TX_7; + case AFE_PORT_ID_SECONDARY_TDM_RX: + return IDX_SECONDARY_TDM_RX_0; + case AFE_PORT_ID_SECONDARY_TDM_TX: + return IDX_SECONDARY_TDM_TX_0; + case AFE_PORT_ID_SECONDARY_TDM_RX_1: + return IDX_SECONDARY_TDM_RX_1; + case AFE_PORT_ID_SECONDARY_TDM_TX_1: + return IDX_SECONDARY_TDM_TX_1; + case AFE_PORT_ID_SECONDARY_TDM_RX_2: + return IDX_SECONDARY_TDM_RX_2; + case AFE_PORT_ID_SECONDARY_TDM_TX_2: + return IDX_SECONDARY_TDM_TX_2; + case AFE_PORT_ID_SECONDARY_TDM_RX_3: + return IDX_SECONDARY_TDM_RX_3; + case AFE_PORT_ID_SECONDARY_TDM_TX_3: + return IDX_SECONDARY_TDM_TX_3; + case AFE_PORT_ID_SECONDARY_TDM_RX_4: + return IDX_SECONDARY_TDM_RX_4; + case AFE_PORT_ID_SECONDARY_TDM_TX_4: + return IDX_SECONDARY_TDM_TX_4; + case AFE_PORT_ID_SECONDARY_TDM_RX_5: + return IDX_SECONDARY_TDM_RX_5; + case AFE_PORT_ID_SECONDARY_TDM_TX_5: + return IDX_SECONDARY_TDM_TX_5; + case AFE_PORT_ID_SECONDARY_TDM_RX_6: + return IDX_SECONDARY_TDM_RX_6; + case AFE_PORT_ID_SECONDARY_TDM_TX_6: + return IDX_SECONDARY_TDM_TX_6; + case AFE_PORT_ID_SECONDARY_TDM_RX_7: + return IDX_SECONDARY_TDM_RX_7; + case AFE_PORT_ID_SECONDARY_TDM_TX_7: + return IDX_SECONDARY_TDM_TX_7; + case AFE_PORT_ID_TERTIARY_TDM_RX: + return IDX_TERTIARY_TDM_RX_0; + case AFE_PORT_ID_TERTIARY_TDM_TX: + return IDX_TERTIARY_TDM_TX_0; + case AFE_PORT_ID_TERTIARY_TDM_RX_1: + return IDX_TERTIARY_TDM_RX_1; + case AFE_PORT_ID_TERTIARY_TDM_TX_1: + return IDX_TERTIARY_TDM_TX_1; + case AFE_PORT_ID_TERTIARY_TDM_RX_2: + return IDX_TERTIARY_TDM_RX_2; + case AFE_PORT_ID_TERTIARY_TDM_TX_2: + return IDX_TERTIARY_TDM_TX_2; + case AFE_PORT_ID_TERTIARY_TDM_RX_3: + return IDX_TERTIARY_TDM_RX_3; + case AFE_PORT_ID_TERTIARY_TDM_TX_3: + return IDX_TERTIARY_TDM_TX_3; + case AFE_PORT_ID_TERTIARY_TDM_RX_4: + return IDX_TERTIARY_TDM_RX_4; + case AFE_PORT_ID_TERTIARY_TDM_TX_4: + return IDX_TERTIARY_TDM_TX_4; + case AFE_PORT_ID_TERTIARY_TDM_RX_5: + return IDX_TERTIARY_TDM_RX_5; + case AFE_PORT_ID_TERTIARY_TDM_TX_5: + return IDX_TERTIARY_TDM_TX_5; + case AFE_PORT_ID_TERTIARY_TDM_RX_6: + return IDX_TERTIARY_TDM_RX_6; + case AFE_PORT_ID_TERTIARY_TDM_TX_6: + return IDX_TERTIARY_TDM_TX_6; + case AFE_PORT_ID_TERTIARY_TDM_RX_7: + return IDX_TERTIARY_TDM_RX_7; + case AFE_PORT_ID_TERTIARY_TDM_TX_7: + return IDX_TERTIARY_TDM_TX_7; + case AFE_PORT_ID_QUATERNARY_TDM_RX: + return IDX_QUATERNARY_TDM_RX_0; + case AFE_PORT_ID_QUATERNARY_TDM_TX: + return IDX_QUATERNARY_TDM_TX_0; + case AFE_PORT_ID_QUATERNARY_TDM_RX_1: + return IDX_QUATERNARY_TDM_RX_1; + case AFE_PORT_ID_QUATERNARY_TDM_TX_1: + return IDX_QUATERNARY_TDM_TX_1; + case AFE_PORT_ID_QUATERNARY_TDM_RX_2: + return IDX_QUATERNARY_TDM_RX_2; + case AFE_PORT_ID_QUATERNARY_TDM_TX_2: + return IDX_QUATERNARY_TDM_TX_2; + case AFE_PORT_ID_QUATERNARY_TDM_RX_3: + return IDX_QUATERNARY_TDM_RX_3; + case AFE_PORT_ID_QUATERNARY_TDM_TX_3: + return IDX_QUATERNARY_TDM_TX_3; + case AFE_PORT_ID_QUATERNARY_TDM_RX_4: + return IDX_QUATERNARY_TDM_RX_4; + case AFE_PORT_ID_QUATERNARY_TDM_TX_4: + return IDX_QUATERNARY_TDM_TX_4; + case AFE_PORT_ID_QUATERNARY_TDM_RX_5: + return IDX_QUATERNARY_TDM_RX_5; + case AFE_PORT_ID_QUATERNARY_TDM_TX_5: + return IDX_QUATERNARY_TDM_TX_5; + case AFE_PORT_ID_QUATERNARY_TDM_RX_6: + return IDX_QUATERNARY_TDM_RX_6; + case AFE_PORT_ID_QUATERNARY_TDM_TX_6: + return IDX_QUATERNARY_TDM_TX_6; + case AFE_PORT_ID_QUATERNARY_TDM_RX_7: + return IDX_QUATERNARY_TDM_RX_7; + case AFE_PORT_ID_QUATERNARY_TDM_TX_7: + return IDX_QUATERNARY_TDM_TX_7; + case AFE_PORT_ID_QUINARY_TDM_RX: + return IDX_QUINARY_TDM_RX_0; + case AFE_PORT_ID_QUINARY_TDM_TX: + return IDX_QUINARY_TDM_TX_0; + case AFE_PORT_ID_QUINARY_TDM_RX_1: + return IDX_QUINARY_TDM_RX_1; + case AFE_PORT_ID_QUINARY_TDM_TX_1: + return IDX_QUINARY_TDM_TX_1; + case AFE_PORT_ID_QUINARY_TDM_RX_2: + return IDX_QUINARY_TDM_RX_2; + case AFE_PORT_ID_QUINARY_TDM_TX_2: + return IDX_QUINARY_TDM_TX_2; + case AFE_PORT_ID_QUINARY_TDM_RX_3: + return IDX_QUINARY_TDM_RX_3; + case AFE_PORT_ID_QUINARY_TDM_TX_3: + return IDX_QUINARY_TDM_TX_3; + case AFE_PORT_ID_QUINARY_TDM_RX_4: + return IDX_QUINARY_TDM_RX_4; + case AFE_PORT_ID_QUINARY_TDM_TX_4: + return IDX_QUINARY_TDM_TX_4; + case AFE_PORT_ID_QUINARY_TDM_RX_5: + return IDX_QUINARY_TDM_RX_5; + case AFE_PORT_ID_QUINARY_TDM_TX_5: + return IDX_QUINARY_TDM_TX_5; + case AFE_PORT_ID_QUINARY_TDM_RX_6: + return IDX_QUINARY_TDM_RX_6; + case AFE_PORT_ID_QUINARY_TDM_TX_6: + return IDX_QUINARY_TDM_TX_6; + case AFE_PORT_ID_QUINARY_TDM_RX_7: + return IDX_QUINARY_TDM_RX_7; + case AFE_PORT_ID_QUINARY_TDM_TX_7: + return IDX_QUINARY_TDM_TX_7; + default: return -EINVAL; + } +} + +static u16 msm_dai_q6_max_num_slot(int frame_rate) +{ + /* Max num of slots is bits per frame divided + * by bits per sample which is 16 + */ + switch (frame_rate) { + case AFE_PORT_PCM_BITS_PER_FRAME_8: + return 0; + case AFE_PORT_PCM_BITS_PER_FRAME_16: + return 1; + case AFE_PORT_PCM_BITS_PER_FRAME_32: + return 2; + case AFE_PORT_PCM_BITS_PER_FRAME_64: + return 4; + case AFE_PORT_PCM_BITS_PER_FRAME_128: + return 8; + case AFE_PORT_PCM_BITS_PER_FRAME_256: + return 16; + default: + pr_err("%s Invalid bits per frame %d\n", + __func__, frame_rate); + return 0; + } +} + +static int msm_dai_q6_dai_add_route(struct snd_soc_dai *dai) +{ + struct snd_soc_dapm_route intercon; + struct snd_soc_dapm_context *dapm; + + if (!dai) { + pr_err("%s: Invalid params dai\n", __func__); + return -EINVAL; + } + if (!dai->driver) { + pr_err("%s: Invalid params dai driver\n", __func__); + return -EINVAL; + } + dapm = snd_soc_component_get_dapm(dai->component); + memset(&intercon, 0, sizeof(intercon)); + if (dai->driver->playback.stream_name && + dai->driver->playback.aif_name) { + dev_dbg(dai->dev, "%s: add route for widget %s", + __func__, dai->driver->playback.stream_name); + intercon.source = dai->driver->playback.aif_name; + intercon.sink = dai->driver->playback.stream_name; + dev_dbg(dai->dev, "%s: src %s sink %s\n", + __func__, intercon.source, intercon.sink); + snd_soc_dapm_add_routes(dapm, &intercon, 1); + } + if (dai->driver->capture.stream_name && + dai->driver->capture.aif_name) { + dev_dbg(dai->dev, "%s: add route for widget %s", + __func__, dai->driver->capture.stream_name); + intercon.sink = dai->driver->capture.aif_name; + intercon.source = dai->driver->capture.stream_name; + dev_dbg(dai->dev, "%s: src %s sink %s\n", + __func__, intercon.source, intercon.sink); + snd_soc_dapm_add_routes(dapm, &intercon, 1); + } + return 0; +} + +static int msm_dai_q6_auxpcm_hw_params( + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct msm_dai_q6_auxpcm_dai_data *aux_dai_data = + dev_get_drvdata(dai->dev); + struct msm_dai_q6_dai_data *dai_data = &aux_dai_data->bdai_data; + struct msm_dai_auxpcm_pdata *auxpcm_pdata = + (struct msm_dai_auxpcm_pdata *) dai->dev->platform_data; + int rc = 0, slot_mapping_copy_len = 0; + + if (params_channels(params) != 1 || (params_rate(params) != 8000 && + params_rate(params) != 16000)) { + dev_err(dai->dev, "%s: invalid param chan %d rate %d\n", + __func__, params_channels(params), params_rate(params)); + return -EINVAL; + } + + mutex_lock(&aux_dai_data->rlock); + + if (test_bit(STATUS_TX_PORT, aux_dai_data->auxpcm_port_status) || + test_bit(STATUS_RX_PORT, aux_dai_data->auxpcm_port_status)) { + /* AUXPCM DAI in use */ + if (dai_data->rate != params_rate(params)) { + dev_err(dai->dev, "%s: rate mismatch of running DAI\n", + __func__); + rc = -EINVAL; + } + mutex_unlock(&aux_dai_data->rlock); + return rc; + } + + dai_data->channels = params_channels(params); + dai_data->rate = params_rate(params); + + if (dai_data->rate == 8000) { + dai_data->port_config.pcm.pcm_cfg_minor_version = + AFE_API_VERSION_PCM_CONFIG; + dai_data->port_config.pcm.aux_mode = auxpcm_pdata->mode_8k.mode; + dai_data->port_config.pcm.sync_src = auxpcm_pdata->mode_8k.sync; + dai_data->port_config.pcm.frame_setting = + auxpcm_pdata->mode_8k.frame; + dai_data->port_config.pcm.quantype = + auxpcm_pdata->mode_8k.quant; + dai_data->port_config.pcm.ctrl_data_out_enable = + auxpcm_pdata->mode_8k.data; + dai_data->port_config.pcm.sample_rate = dai_data->rate; + dai_data->port_config.pcm.num_channels = dai_data->channels; + dai_data->port_config.pcm.bit_width = 16; + if (ARRAY_SIZE(dai_data->port_config.pcm.slot_number_mapping) <= + auxpcm_pdata->mode_8k.num_slots) + slot_mapping_copy_len = + ARRAY_SIZE( + dai_data->port_config.pcm.slot_number_mapping) + * sizeof(uint16_t); + else + slot_mapping_copy_len = auxpcm_pdata->mode_8k.num_slots + * sizeof(uint16_t); + + if (auxpcm_pdata->mode_8k.slot_mapping) { + memcpy(dai_data->port_config.pcm.slot_number_mapping, + auxpcm_pdata->mode_8k.slot_mapping, + slot_mapping_copy_len); + } else { + dev_err(dai->dev, "%s 8khz slot mapping is NULL\n", + __func__); + mutex_unlock(&aux_dai_data->rlock); + return -EINVAL; + } + } else { + dai_data->port_config.pcm.pcm_cfg_minor_version = + AFE_API_VERSION_PCM_CONFIG; + dai_data->port_config.pcm.aux_mode = + auxpcm_pdata->mode_16k.mode; + dai_data->port_config.pcm.sync_src = + auxpcm_pdata->mode_16k.sync; + dai_data->port_config.pcm.frame_setting = + auxpcm_pdata->mode_16k.frame; + dai_data->port_config.pcm.quantype = + auxpcm_pdata->mode_16k.quant; + dai_data->port_config.pcm.ctrl_data_out_enable = + auxpcm_pdata->mode_16k.data; + dai_data->port_config.pcm.sample_rate = dai_data->rate; + dai_data->port_config.pcm.num_channels = dai_data->channels; + dai_data->port_config.pcm.bit_width = 16; + if (ARRAY_SIZE(dai_data->port_config.pcm.slot_number_mapping) <= + auxpcm_pdata->mode_16k.num_slots) + slot_mapping_copy_len = + ARRAY_SIZE( + dai_data->port_config.pcm.slot_number_mapping) + * sizeof(uint16_t); + else + slot_mapping_copy_len = auxpcm_pdata->mode_16k.num_slots + * sizeof(uint16_t); + + if (auxpcm_pdata->mode_16k.slot_mapping) { + memcpy(dai_data->port_config.pcm.slot_number_mapping, + auxpcm_pdata->mode_16k.slot_mapping, + slot_mapping_copy_len); + } else { + dev_err(dai->dev, "%s 16khz slot mapping is NULL\n", + __func__); + mutex_unlock(&aux_dai_data->rlock); + return -EINVAL; + } + } + + dev_dbg(dai->dev, "%s: aux_mode 0x%x sync_src 0x%x frame_setting 0x%x\n", + __func__, dai_data->port_config.pcm.aux_mode, + dai_data->port_config.pcm.sync_src, + dai_data->port_config.pcm.frame_setting); + dev_dbg(dai->dev, "%s: qtype 0x%x dout 0x%x num_map[0] 0x%x\n" + "num_map[1] 0x%x num_map[2] 0x%x num_map[3] 0x%x\n", + __func__, dai_data->port_config.pcm.quantype, + dai_data->port_config.pcm.ctrl_data_out_enable, + dai_data->port_config.pcm.slot_number_mapping[0], + dai_data->port_config.pcm.slot_number_mapping[1], + dai_data->port_config.pcm.slot_number_mapping[2], + dai_data->port_config.pcm.slot_number_mapping[3]); + + mutex_unlock(&aux_dai_data->rlock); + return rc; +} + +static int msm_dai_q6_auxpcm_set_clk( + struct msm_dai_q6_auxpcm_dai_data *aux_dai_data, + u16 port_id, bool enable) +{ + int rc; + + pr_debug("%s: afe_clk_ver: %d, port_id: %d, enable: %d\n", __func__, + aux_dai_data->afe_clk_ver, port_id, enable); + if (aux_dai_data->afe_clk_ver == AFE_CLK_VERSION_V2) { + aux_dai_data->clk_set.enable = enable; + rc = afe_set_lpass_clock_v2(port_id, + &aux_dai_data->clk_set); + } else { + if (!enable) + aux_dai_data->clk_cfg.clk_val1 = 0; + rc = afe_set_lpass_clock(port_id, + &aux_dai_data->clk_cfg); + } + return rc; +} + +static void msm_dai_q6_auxpcm_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + int rc = 0; + struct msm_dai_q6_auxpcm_dai_data *aux_dai_data = + dev_get_drvdata(dai->dev); + + mutex_lock(&aux_dai_data->rlock); + + if (!(test_bit(STATUS_TX_PORT, aux_dai_data->auxpcm_port_status) || + test_bit(STATUS_RX_PORT, aux_dai_data->auxpcm_port_status))) { + dev_dbg(dai->dev, "%s(): dai->id %d PCM ports already closed\n", + __func__, dai->id); + goto exit; + } + + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + if (test_bit(STATUS_TX_PORT, aux_dai_data->auxpcm_port_status)) + clear_bit(STATUS_TX_PORT, + aux_dai_data->auxpcm_port_status); + else { + dev_dbg(dai->dev, "%s: PCM_TX port already closed\n", + __func__); + goto exit; + } + } else if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + if (test_bit(STATUS_RX_PORT, aux_dai_data->auxpcm_port_status)) + clear_bit(STATUS_RX_PORT, + aux_dai_data->auxpcm_port_status); + else { + dev_dbg(dai->dev, "%s: PCM_RX port already closed\n", + __func__); + goto exit; + } + } + if (test_bit(STATUS_TX_PORT, aux_dai_data->auxpcm_port_status) || + test_bit(STATUS_RX_PORT, aux_dai_data->auxpcm_port_status)) { + dev_dbg(dai->dev, "%s: cannot shutdown PCM ports\n", + __func__); + goto exit; + } + + dev_dbg(dai->dev, "%s: dai->id = %d closing PCM AFE ports\n", + __func__, dai->id); + + rc = afe_close(aux_dai_data->rx_pid); /* can block */ + if (rc < 0) + dev_err(dai->dev, "fail to close PCM_RX AFE port\n"); + + rc = afe_close(aux_dai_data->tx_pid); + if (rc < 0) + dev_err(dai->dev, "fail to close AUX PCM TX port\n"); + + msm_dai_q6_auxpcm_set_clk(aux_dai_data, aux_dai_data->rx_pid, false); + msm_dai_q6_auxpcm_set_clk(aux_dai_data, aux_dai_data->tx_pid, false); +exit: + mutex_unlock(&aux_dai_data->rlock); +} + +static int msm_dai_q6_auxpcm_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct msm_dai_q6_auxpcm_dai_data *aux_dai_data = + dev_get_drvdata(dai->dev); + struct msm_dai_q6_dai_data *dai_data = &aux_dai_data->bdai_data; + struct msm_dai_auxpcm_pdata *auxpcm_pdata = NULL; + int rc = 0; + u32 pcm_clk_rate; + + auxpcm_pdata = dai->dev->platform_data; + mutex_lock(&aux_dai_data->rlock); + + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + if (test_bit(STATUS_TX_PORT, + aux_dai_data->auxpcm_port_status)) { + dev_dbg(dai->dev, "%s: PCM_TX port already ON\n", + __func__); + goto exit; + } else + set_bit(STATUS_TX_PORT, + aux_dai_data->auxpcm_port_status); + } else if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + if (test_bit(STATUS_RX_PORT, + aux_dai_data->auxpcm_port_status)) { + dev_dbg(dai->dev, "%s: PCM_RX port already ON\n", + __func__); + goto exit; + } else + set_bit(STATUS_RX_PORT, + aux_dai_data->auxpcm_port_status); + } + if (test_bit(STATUS_TX_PORT, aux_dai_data->auxpcm_port_status) && + test_bit(STATUS_RX_PORT, aux_dai_data->auxpcm_port_status)) { + dev_dbg(dai->dev, "%s: PCM ports already set\n", __func__); + goto exit; + } + + dev_dbg(dai->dev, "%s: dai->id:%d opening afe ports\n", + __func__, dai->id); + + rc = afe_q6_interface_prepare(); + if (rc < 0) { + dev_err(dai->dev, "fail to open AFE APR\n"); + goto fail; + } + + /* + * For AUX PCM Interface the below sequence of clk + * settings and afe_open is a strict requirement. + * + * Also using afe_open instead of afe_port_start_nowait + * to make sure the port is open before deasserting the + * clock line. This is required because pcm register is + * not written before clock deassert. Hence the hw does + * not get updated with new setting if the below clock + * assert/deasset and afe_open sequence is not followed. + */ + + if (dai_data->rate == 8000) { + pcm_clk_rate = auxpcm_pdata->mode_8k.pcm_clk_rate; + } else if (dai_data->rate == 16000) { + pcm_clk_rate = (auxpcm_pdata->mode_16k.pcm_clk_rate); + } else { + dev_err(dai->dev, "%s: Invalid AUX PCM rate %d\n", __func__, + dai_data->rate); + rc = -EINVAL; + goto fail; + } + if (aux_dai_data->afe_clk_ver == AFE_CLK_VERSION_V2) { + memcpy(&aux_dai_data->clk_set, &lpass_clk_set_default, + sizeof(struct afe_clk_set)); + aux_dai_data->clk_set.clk_freq_in_hz = pcm_clk_rate; + + switch (dai->id) { + case MSM_DAI_PRI_AUXPCM_DT_DEV_ID: + if (pcm_clk_rate) + aux_dai_data->clk_set.clk_id = + Q6AFE_LPASS_CLK_ID_PRI_PCM_IBIT; + else + aux_dai_data->clk_set.clk_id = + Q6AFE_LPASS_CLK_ID_PRI_PCM_EBIT; + break; + case MSM_DAI_SEC_AUXPCM_DT_DEV_ID: + if (pcm_clk_rate) + aux_dai_data->clk_set.clk_id = + Q6AFE_LPASS_CLK_ID_SEC_PCM_IBIT; + else + aux_dai_data->clk_set.clk_id = + Q6AFE_LPASS_CLK_ID_SEC_PCM_EBIT; + break; + case MSM_DAI_TERT_AUXPCM_DT_DEV_ID: + if (pcm_clk_rate) + aux_dai_data->clk_set.clk_id = + Q6AFE_LPASS_CLK_ID_TER_PCM_IBIT; + else + aux_dai_data->clk_set.clk_id = + Q6AFE_LPASS_CLK_ID_TER_PCM_EBIT; + break; + case MSM_DAI_QUAT_AUXPCM_DT_DEV_ID: + if (pcm_clk_rate) + aux_dai_data->clk_set.clk_id = + Q6AFE_LPASS_CLK_ID_QUAD_PCM_IBIT; + else + aux_dai_data->clk_set.clk_id = + Q6AFE_LPASS_CLK_ID_QUAD_PCM_EBIT; + break; + case MSM_DAI_QUIN_AUXPCM_DT_DEV_ID: + if (pcm_clk_rate) + aux_dai_data->clk_set.clk_id = + Q6AFE_LPASS_CLK_ID_QUIN_PCM_IBIT; + else + aux_dai_data->clk_set.clk_id = + Q6AFE_LPASS_CLK_ID_QUIN_PCM_EBIT; + break; + default: + dev_err(dai->dev, "%s: AUXPCM id: %d not supported\n", + __func__, dai->id); + break; + } + } else { + memcpy(&aux_dai_data->clk_cfg, &lpass_clk_cfg_default, + sizeof(struct afe_clk_cfg)); + aux_dai_data->clk_cfg.clk_val1 = pcm_clk_rate; + } + + rc = msm_dai_q6_auxpcm_set_clk(aux_dai_data, + aux_dai_data->rx_pid, true); + if (rc < 0) { + dev_err(dai->dev, + "%s:afe_set_lpass_clock on RX pcm_src_clk failed\n", + __func__); + goto fail; + } + + rc = msm_dai_q6_auxpcm_set_clk(aux_dai_data, + aux_dai_data->tx_pid, true); + if (rc < 0) { + dev_err(dai->dev, + "%s:afe_set_lpass_clock on TX pcm_src_clk failed\n", + __func__); + goto fail; + } + + afe_open(aux_dai_data->rx_pid, &dai_data->port_config, dai_data->rate); + if (q6core_get_avcs_api_version_per_service( + APRV2_IDS_SERVICE_ID_ADSP_AFE_V) >= AFE_API_VERSION_V4) { + /* + * send island mode config + * This should be the first configuration + */ + rc = afe_send_port_island_mode(aux_dai_data->tx_pid); + if (rc) + dev_err(dai->dev, "%s: afe send island mode failed %d\n", + __func__, rc); + } + afe_open(aux_dai_data->tx_pid, &dai_data->port_config, dai_data->rate); + goto exit; + +fail: + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + clear_bit(STATUS_TX_PORT, aux_dai_data->auxpcm_port_status); + else if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + clear_bit(STATUS_RX_PORT, aux_dai_data->auxpcm_port_status); + +exit: + mutex_unlock(&aux_dai_data->rlock); + return rc; +} + +static int msm_dai_q6_auxpcm_trigger(struct snd_pcm_substream *substream, + int cmd, struct snd_soc_dai *dai) +{ + int rc = 0; + + pr_debug("%s:port:%d cmd:%d\n", + __func__, dai->id, cmd); + + switch (cmd) { + + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + /* afe_open will be called from prepare */ + return 0; + + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + return 0; + + default: + pr_err("%s: cmd %d\n", __func__, cmd); + rc = -EINVAL; + } + + return rc; + +} + +static int msm_dai_q6_dai_auxpcm_remove(struct snd_soc_dai *dai) +{ + struct msm_dai_q6_auxpcm_dai_data *aux_dai_data; + int rc; + + aux_dai_data = dev_get_drvdata(dai->dev); + + dev_dbg(dai->dev, "%s: dai->id %d closing afe\n", + __func__, dai->id); + + if (test_bit(STATUS_TX_PORT, aux_dai_data->auxpcm_port_status) || + test_bit(STATUS_RX_PORT, aux_dai_data->auxpcm_port_status)) { + rc = afe_close(aux_dai_data->rx_pid); /* can block */ + if (rc < 0) + dev_err(dai->dev, "fail to close AUXPCM RX AFE port\n"); + rc = afe_close(aux_dai_data->tx_pid); + if (rc < 0) + dev_err(dai->dev, "fail to close AUXPCM TX AFE port\n"); + clear_bit(STATUS_TX_PORT, aux_dai_data->auxpcm_port_status); + clear_bit(STATUS_RX_PORT, aux_dai_data->auxpcm_port_status); + } + msm_dai_q6_auxpcm_set_clk(aux_dai_data, aux_dai_data->rx_pid, false); + msm_dai_q6_auxpcm_set_clk(aux_dai_data, aux_dai_data->tx_pid, false); + return 0; +} + +static int msm_dai_q6_island_mode_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int value = ucontrol->value.integer.value[0]; + u16 port_id = (u16)kcontrol->private_value; + + pr_debug("%s: island mode = %d\n", __func__, value); + + afe_set_island_mode_cfg(port_id, value); + return 0; +} + +static int msm_dai_q6_island_mode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int value; + u16 port_id = (u16)kcontrol->private_value; + + afe_get_island_mode_cfg(port_id, &value); + ucontrol->value.integer.value[0] = value; + return 0; +} + +static void island_mx_ctl_private_free(struct snd_kcontrol *kcontrol) +{ + struct snd_kcontrol_new *knew = snd_kcontrol_chip(kcontrol); + + kfree(knew); +} + +static int msm_dai_q6_add_island_mx_ctls(struct snd_card *card, + const char *dai_name, + int dai_id, void *dai_data) +{ + const char *mx_ctl_name = "TX island"; + char *mixer_str = NULL; + int dai_str_len = 0, ctl_len = 0; + int rc = 0; + struct snd_kcontrol_new *knew = NULL; + struct snd_kcontrol *kctl = NULL; + + dai_str_len = strlen(dai_name) + 1; + + /* Add island related mixer controls */ + ctl_len = dai_str_len + strlen(mx_ctl_name) + 1; + mixer_str = kzalloc(ctl_len, GFP_KERNEL); + if (!mixer_str) + return -ENOMEM; + + snprintf(mixer_str, ctl_len, "%s %s", dai_name, mx_ctl_name); + + knew = kzalloc(sizeof(struct snd_kcontrol_new), GFP_KERNEL); + if (!knew) { + kfree(mixer_str); + return -ENOMEM; + } + knew->iface = SNDRV_CTL_ELEM_IFACE_MIXER; + knew->info = snd_ctl_boolean_mono_info; + knew->get = msm_dai_q6_island_mode_get; + knew->put = msm_dai_q6_island_mode_put; + knew->name = mixer_str; + knew->private_value = dai_id; + kctl = snd_ctl_new1(knew, knew); + if (!kctl) { + kfree(knew); + kfree(mixer_str); + return -ENOMEM; + } + kctl->private_free = island_mx_ctl_private_free; + rc = snd_ctl_add(card, kctl); + if (rc < 0) + pr_err("%s: err add config ctl, DAI = %s\n", + __func__, dai_name); + kfree(mixer_str); + + return rc; +} + +/* + * For single CPU DAI registration, the dai id needs to be + * set explicitly in the dai probe as ASoC does not read + * the cpu->driver->id field rather it assigns the dai id + * from the device name that is in the form %s.%d. This dai + * id should be assigned to back-end AFE port id and used + * during dai prepare. For multiple dai registration, it + * is not required to call this function, however the dai-> + * driver->id field must be defined and set to corresponding + * AFE Port id. + */ +static inline void msm_dai_q6_set_dai_id(struct snd_soc_dai *dai) +{ + if (!dai->driver) { + dev_err(dai->dev, "DAI driver is not set\n"); + return; + } + if (!dai->driver->id) { + dev_dbg(dai->dev, "DAI driver id is not set\n"); + return; + } + dai->id = dai->driver->id; +} + +static int msm_dai_q6_aux_pcm_probe(struct snd_soc_dai *dai) +{ + int rc = 0; + struct msm_dai_q6_auxpcm_dai_data *dai_data = NULL; + + if (!dai) { + pr_err("%s: Invalid params dai\n", __func__); + return -EINVAL; + } + if (!dai->dev) { + pr_err("%s: Invalid params dai dev\n", __func__); + return -EINVAL; + } + + msm_dai_q6_set_dai_id(dai); + dai_data = dev_get_drvdata(dai->dev); + + if (dai_data->is_island_dai) + rc = msm_dai_q6_add_island_mx_ctls( + dai->component->card->snd_card, + dai->name, dai_data->tx_pid, + (void *)dai_data); + + rc = msm_dai_q6_dai_add_route(dai); + return rc; +} + +static struct snd_soc_dai_ops msm_dai_q6_auxpcm_ops = { + .prepare = msm_dai_q6_auxpcm_prepare, + .trigger = msm_dai_q6_auxpcm_trigger, + .hw_params = msm_dai_q6_auxpcm_hw_params, + .shutdown = msm_dai_q6_auxpcm_shutdown, +}; + +static const struct snd_soc_component_driver + msm_dai_q6_aux_pcm_dai_component = { + .name = "msm-auxpcm-dev", +}; + +static struct snd_soc_dai_driver msm_dai_q6_aux_pcm_dai[] = { + { + .playback = { + .stream_name = "AUX PCM Playback", + .aif_name = "AUX_PCM_RX", + .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000), + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 1, + .rate_max = 16000, + .rate_min = 8000, + }, + .capture = { + .stream_name = "AUX PCM Capture", + .aif_name = "AUX_PCM_TX", + .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000), + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 1, + .rate_max = 16000, + .rate_min = 8000, + }, + .id = MSM_DAI_PRI_AUXPCM_DT_DEV_ID, + .name = "Pri AUX PCM", + .ops = &msm_dai_q6_auxpcm_ops, + .probe = msm_dai_q6_aux_pcm_probe, + .remove = msm_dai_q6_dai_auxpcm_remove, + }, + { + .playback = { + .stream_name = "Sec AUX PCM Playback", + .aif_name = "SEC_AUX_PCM_RX", + .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000), + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 1, + .rate_max = 16000, + .rate_min = 8000, + }, + .capture = { + .stream_name = "Sec AUX PCM Capture", + .aif_name = "SEC_AUX_PCM_TX", + .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000), + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 1, + .rate_max = 16000, + .rate_min = 8000, + }, + .id = MSM_DAI_SEC_AUXPCM_DT_DEV_ID, + .name = "Sec AUX PCM", + .ops = &msm_dai_q6_auxpcm_ops, + .probe = msm_dai_q6_aux_pcm_probe, + .remove = msm_dai_q6_dai_auxpcm_remove, + }, + { + .playback = { + .stream_name = "Tert AUX PCM Playback", + .aif_name = "TERT_AUX_PCM_RX", + .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000), + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 1, + .rate_max = 16000, + .rate_min = 8000, + }, + .capture = { + .stream_name = "Tert AUX PCM Capture", + .aif_name = "TERT_AUX_PCM_TX", + .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000), + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 1, + .rate_max = 16000, + .rate_min = 8000, + }, + .id = MSM_DAI_TERT_AUXPCM_DT_DEV_ID, + .name = "Tert AUX PCM", + .ops = &msm_dai_q6_auxpcm_ops, + .probe = msm_dai_q6_aux_pcm_probe, + .remove = msm_dai_q6_dai_auxpcm_remove, + }, + { + .playback = { + .stream_name = "Quat AUX PCM Playback", + .aif_name = "QUAT_AUX_PCM_RX", + .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000), + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 1, + .rate_max = 16000, + .rate_min = 8000, + }, + .capture = { + .stream_name = "Quat AUX PCM Capture", + .aif_name = "QUAT_AUX_PCM_TX", + .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000), + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 1, + .rate_max = 16000, + .rate_min = 8000, + }, + .id = MSM_DAI_QUAT_AUXPCM_DT_DEV_ID, + .name = "Quat AUX PCM", + .ops = &msm_dai_q6_auxpcm_ops, + .probe = msm_dai_q6_aux_pcm_probe, + .remove = msm_dai_q6_dai_auxpcm_remove, + }, + { + .playback = { + .stream_name = "Quin AUX PCM Playback", + .aif_name = "QUIN_AUX_PCM_RX", + .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000), + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 1, + .rate_max = 16000, + .rate_min = 8000, + }, + .capture = { + .stream_name = "Quin AUX PCM Capture", + .aif_name = "QUIN_AUX_PCM_TX", + .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000), + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 1, + .rate_max = 16000, + .rate_min = 8000, + }, + .id = MSM_DAI_QUIN_AUXPCM_DT_DEV_ID, + .name = "Quin AUX PCM", + .ops = &msm_dai_q6_auxpcm_ops, + .probe = msm_dai_q6_aux_pcm_probe, + .remove = msm_dai_q6_dai_auxpcm_remove, + }, +}; + +static int msm_dai_q6_spdif_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + + struct msm_dai_q6_spdif_dai_data *dai_data = kcontrol->private_data; + int value = ucontrol->value.integer.value[0]; + + dai_data->spdif_port.cfg.data_format = value; + pr_debug("%s: value = %d\n", __func__, value); + return 0; +} + +static int msm_dai_q6_spdif_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + + struct msm_dai_q6_spdif_dai_data *dai_data = kcontrol->private_data; + + ucontrol->value.integer.value[0] = + dai_data->spdif_port.cfg.data_format; + return 0; +} + +static int msm_dai_q6_spdif_source_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + + struct msm_dai_q6_spdif_dai_data *dai_data = kcontrol->private_data; + int value = ucontrol->value.integer.value[0]; + + dai_data->spdif_port.cfg.src_sel = value; + pr_debug("%s: value = %d\n", __func__, value); + return 0; +} + +static int msm_dai_q6_spdif_source_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + + struct msm_dai_q6_spdif_dai_data *dai_data = kcontrol->private_data; + + ucontrol->value.integer.value[0] = + dai_data->spdif_port.cfg.src_sel; + return 0; +} + +static const char * const spdif_format[] = { + "LPCM", + "Compr" +}; + +static const char * const spdif_source[] = { + "Optical", "EXT-ARC", "Coaxial", "VT-ARC" +}; + +static const struct soc_enum spdif_rx_config_enum[] = { + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(spdif_format), spdif_format), +}; + +static const struct soc_enum spdif_tx_config_enum[] = { + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(spdif_source), spdif_source), + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(spdif_format), spdif_format), +}; + +static int msm_dai_q6_spdif_chstatus_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct msm_dai_q6_spdif_dai_data *dai_data = kcontrol->private_data; + int ret = 0; + + dai_data->spdif_port.ch_status.status_type = + AFE_API_VERSION_SPDIF_CH_STATUS_CONFIG; + memset(dai_data->spdif_port.ch_status.status_mask, + CHANNEL_STATUS_MASK_INIT, CHANNEL_STATUS_SIZE); + dai_data->spdif_port.ch_status.status_mask[0] = + CHANNEL_STATUS_MASK; + + memcpy(dai_data->spdif_port.ch_status.status_bits, + ucontrol->value.iec958.status, CHANNEL_STATUS_SIZE); + + if (test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) { + pr_debug("%s: Port already started. Dynamic update\n", + __func__); + ret = afe_send_spdif_ch_status_cfg( + &dai_data->spdif_port.ch_status, + dai_data->port_id); + } + return ret; +} + +static int msm_dai_q6_spdif_chstatus_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + + struct msm_dai_q6_spdif_dai_data *dai_data = kcontrol->private_data; + + memcpy(ucontrol->value.iec958.status, + dai_data->spdif_port.ch_status.status_bits, + CHANNEL_STATUS_SIZE); + return 0; +} + +static int msm_dai_q6_spdif_chstatus_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; + return 0; +} + +static const struct snd_kcontrol_new spdif_rx_config_controls[] = { + /* Primary SPDIF output */ + { + .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_INACTIVE), + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, PCM_STREAM), + .info = msm_dai_q6_spdif_chstatus_info, + .get = msm_dai_q6_spdif_chstatus_get, + .put = msm_dai_q6_spdif_chstatus_put, + }, + SOC_ENUM_EXT("PRI SPDIF RX Format", spdif_rx_config_enum[0], + msm_dai_q6_spdif_format_get, + msm_dai_q6_spdif_format_put), + /* Secondary SPDIF output */ + { + .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_INACTIVE), + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = SNDRV_CTL_NAME_IEC958("SEC", PLAYBACK, PCM_STREAM), + .info = msm_dai_q6_spdif_chstatus_info, + .get = msm_dai_q6_spdif_chstatus_get, + .put = msm_dai_q6_spdif_chstatus_put, + }, + SOC_ENUM_EXT("SEC SPDIF RX Format", spdif_rx_config_enum[0], + msm_dai_q6_spdif_format_get, + msm_dai_q6_spdif_format_put) +}; + +static const struct snd_kcontrol_new spdif_tx_config_controls[] = { + SOC_ENUM_EXT("PRI SPDIF TX Source", spdif_tx_config_enum[0], + msm_dai_q6_spdif_source_get, + msm_dai_q6_spdif_source_put), + SOC_ENUM_EXT("PRI SPDIF TX Format", spdif_tx_config_enum[1], + msm_dai_q6_spdif_format_get, + msm_dai_q6_spdif_format_put), + SOC_ENUM_EXT("SEC SPDIF TX Source", spdif_tx_config_enum[0], + msm_dai_q6_spdif_source_get, + msm_dai_q6_spdif_source_put), + SOC_ENUM_EXT("SEC SPDIF TX Format", spdif_tx_config_enum[1], + msm_dai_q6_spdif_format_get, + msm_dai_q6_spdif_format_put) +}; + +static void msm_dai_q6_spdif_process_event(uint32_t opcode, uint32_t token, + uint32_t *payload, void *private_data) +{ + struct msm_dai_q6_spdif_event_msg *evt; + struct msm_dai_q6_spdif_dai_data *dai_data; + + evt = (struct msm_dai_q6_spdif_event_msg *)payload; + dai_data = (struct msm_dai_q6_spdif_dai_data *)private_data; + + pr_debug("%s: old state %d, fmt %d, rate %d\n", + __func__, dai_data->fmt_event.status, + dai_data->fmt_event.data_format, + dai_data->fmt_event.sample_rate); + pr_debug("%s: new state %d, fmt %d, rate %d\n", + __func__, evt->fmt_event.status, + evt->fmt_event.data_format, + evt->fmt_event.sample_rate); + + dai_data->fmt_event.status = evt->fmt_event.status; + dai_data->fmt_event.data_format = evt->fmt_event.data_format; + dai_data->fmt_event.sample_rate = evt->fmt_event.sample_rate; +} + +static int msm_dai_q6_spdif_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct msm_dai_q6_spdif_dai_data *dai_data = dev_get_drvdata(dai->dev); + + dai_data->channels = params_channels(params); + dai_data->spdif_port.cfg.num_channels = dai_data->channels; + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + dai_data->spdif_port.cfg.bit_width = 16; + break; + case SNDRV_PCM_FORMAT_S24_LE: + case SNDRV_PCM_FORMAT_S24_3LE: + dai_data->spdif_port.cfg.bit_width = 24; + break; + default: + pr_err("%s: format %d\n", + __func__, params_format(params)); + return -EINVAL; + } + + dai_data->rate = params_rate(params); + dai_data->bitwidth = dai_data->spdif_port.cfg.bit_width; + dai_data->spdif_port.cfg.sample_rate = dai_data->rate; + dai_data->spdif_port.cfg.spdif_cfg_minor_version = + AFE_API_VERSION_SPDIF_CONFIG_V2; + dev_dbg(dai->dev, " channel %d sample rate %d bit width %d\n", + dai_data->channels, dai_data->rate, + dai_data->spdif_port.cfg.bit_width); + dai_data->spdif_port.cfg.reserved = 0; + return 0; +} + +static void msm_dai_q6_spdif_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct msm_dai_q6_spdif_dai_data *dai_data = dev_get_drvdata(dai->dev); + int rc = 0; + + if (!test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) { + pr_info("%s: afe port not started. dai_data->status_mask = %ld\n", + __func__, *dai_data->status_mask); + return; + } + + rc = afe_close(dai->id); + if (rc < 0) + dev_err(dai->dev, "fail to close AFE port\n"); + + dai_data->fmt_event.status = 0; /* report invalid line state */ + + pr_debug("%s: dai_data->status_mask = %ld\n", __func__, + *dai_data->status_mask); + + clear_bit(STATUS_PORT_STARTED, dai_data->status_mask); +} + +static int msm_dai_q6_spdif_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct msm_dai_q6_spdif_dai_data *dai_data = dev_get_drvdata(dai->dev); + int rc = 0; + + if (!test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) { + rc = afe_spdif_reg_event_cfg(dai->id, + AFE_MODULE_REGISTER_EVENT_FLAG, + msm_dai_q6_spdif_process_event, + dai_data); + if (rc < 0) + dev_err(dai->dev, + "fail to register event for port 0x%x\n", + dai->id); + + rc = afe_spdif_port_start(dai->id, &dai_data->spdif_port, + dai_data->rate); + if (rc < 0) + dev_err(dai->dev, "fail to open AFE port 0x%x\n", + dai->id); + else + set_bit(STATUS_PORT_STARTED, + dai_data->status_mask); + } + + return rc; +} + +static ssize_t msm_dai_q6_spdif_sysfs_rda_audio_state(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret; + struct msm_dai_q6_spdif_dai_data *dai_data = dev_get_drvdata(dev); + + if (!dai_data) { + pr_err("%s: invalid input\n", __func__); + return -EINVAL; + } + + ret = snprintf(buf, MSM_DAI_SYSFS_ENTRY_MAX_LEN, "%d\n", + dai_data->fmt_event.status); + pr_debug("%s: '%d'\n", __func__, dai_data->fmt_event.status); + + return ret; +} + +static ssize_t msm_dai_q6_spdif_sysfs_rda_audio_format(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret; + struct msm_dai_q6_spdif_dai_data *dai_data = dev_get_drvdata(dev); + + if (!dai_data) { + pr_err("%s: invalid input\n", __func__); + return -EINVAL; + } + + ret = snprintf(buf, MSM_DAI_SYSFS_ENTRY_MAX_LEN, "%d\n", + dai_data->fmt_event.data_format); + pr_debug("%s: '%d'\n", __func__, dai_data->fmt_event.data_format); + + return ret; +} + +static ssize_t msm_dai_q6_spdif_sysfs_rda_audio_rate(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret; + struct msm_dai_q6_spdif_dai_data *dai_data = dev_get_drvdata(dev); + + if (!dai_data) { + pr_err("%s: invalid input\n", __func__); + return -EINVAL; + } + + ret = snprintf(buf, MSM_DAI_SYSFS_ENTRY_MAX_LEN, "%d\n", + dai_data->fmt_event.sample_rate); + pr_debug("%s: '%d'\n", __func__, dai_data->fmt_event.sample_rate); + + return ret; +} + +static DEVICE_ATTR(audio_state, 0444, msm_dai_q6_spdif_sysfs_rda_audio_state, + NULL); +static DEVICE_ATTR(audio_format, 0444, msm_dai_q6_spdif_sysfs_rda_audio_format, + NULL); +static DEVICE_ATTR(audio_rate, 0444, msm_dai_q6_spdif_sysfs_rda_audio_rate, + NULL); + +static struct attribute *msm_dai_q6_spdif_fs_attrs[] = { + &dev_attr_audio_state.attr, + &dev_attr_audio_format.attr, + &dev_attr_audio_rate.attr, + NULL, +}; +static struct attribute_group msm_dai_q6_spdif_fs_attrs_group = { + .attrs = msm_dai_q6_spdif_fs_attrs, +}; + +static int msm_dai_q6_spdif_sysfs_create(struct snd_soc_dai *dai, + struct msm_dai_q6_spdif_dai_data *dai_data) +{ + int rc; + + rc = sysfs_create_group(&dai->dev->kobj, + &msm_dai_q6_spdif_fs_attrs_group); + if (rc) { + pr_err("%s: failed, rc=%d\n", __func__, rc); + return rc; + } + dai_data->kobj = &dai->dev->kobj; + + return 0; +} + +static void msm_dai_q6_spdif_sysfs_remove(struct snd_soc_dai *dai, + struct msm_dai_q6_spdif_dai_data *dai_data) +{ + if (dai_data->kobj) + sysfs_remove_group(dai_data->kobj, + &msm_dai_q6_spdif_fs_attrs_group); + dai_data->kobj = NULL; +} + +static int msm_dai_q6_spdif_dai_probe(struct snd_soc_dai *dai) +{ + struct msm_dai_q6_spdif_dai_data *dai_data; + int rc = 0; + struct snd_soc_dapm_route intercon; + struct snd_soc_dapm_context *dapm; + + if (!dai) { + pr_err("%s: dai not found!!\n", __func__); + return -EINVAL; + } + if (!dai->dev) { + pr_err("%s: Invalid params dai dev\n", __func__); + return -EINVAL; + } + + dai_data = kzalloc(sizeof(struct msm_dai_q6_spdif_dai_data), + GFP_KERNEL); + + if (!dai_data) + return -ENOMEM; + else + dev_set_drvdata(dai->dev, dai_data); + + msm_dai_q6_set_dai_id(dai); + dai_data->port_id = dai->id; + + switch (dai->id) { + case AFE_PORT_ID_PRIMARY_SPDIF_RX: + rc = snd_ctl_add(dai->component->card->snd_card, + snd_ctl_new1(&spdif_rx_config_controls[1], + dai_data)); + break; + case AFE_PORT_ID_SECONDARY_SPDIF_RX: + rc = snd_ctl_add(dai->component->card->snd_card, + snd_ctl_new1(&spdif_rx_config_controls[3], + dai_data)); + break; + case AFE_PORT_ID_PRIMARY_SPDIF_TX: + rc = msm_dai_q6_spdif_sysfs_create(dai, dai_data); + + rc = snd_ctl_add(dai->component->card->snd_card, + snd_ctl_new1(&spdif_tx_config_controls[0], + dai_data)); + rc = snd_ctl_add(dai->component->card->snd_card, + snd_ctl_new1(&spdif_tx_config_controls[1], + dai_data)); + break; + case AFE_PORT_ID_SECONDARY_SPDIF_TX: + rc = msm_dai_q6_spdif_sysfs_create(dai, dai_data); + + rc = snd_ctl_add(dai->component->card->snd_card, + snd_ctl_new1(&spdif_tx_config_controls[2], + dai_data)); + rc = snd_ctl_add(dai->component->card->snd_card, + snd_ctl_new1(&spdif_tx_config_controls[3], + dai_data)); + break; + } + if (rc < 0) + dev_err(dai->dev, + "%s: err add config ctl, DAI = %s\n", + __func__, dai->name); + + dapm = snd_soc_component_get_dapm(dai->component); + + memset(&intercon, 0, sizeof(intercon)); + if (!rc && dai && dai->driver) { + if (dai->driver->playback.stream_name && + dai->driver->playback.aif_name) { + dev_dbg(dai->dev, "%s: add route for widget %s", + __func__, dai->driver->playback.stream_name); + intercon.source = dai->driver->playback.aif_name; + intercon.sink = dai->driver->playback.stream_name; + dev_dbg(dai->dev, "%s: src %s sink %s\n", + __func__, intercon.source, intercon.sink); + snd_soc_dapm_add_routes(dapm, &intercon, 1); + } + if (dai->driver->capture.stream_name && + dai->driver->capture.aif_name) { + dev_dbg(dai->dev, "%s: add route for widget %s", + __func__, dai->driver->capture.stream_name); + intercon.sink = dai->driver->capture.aif_name; + intercon.source = dai->driver->capture.stream_name; + dev_dbg(dai->dev, "%s: src %s sink %s\n", + __func__, intercon.source, intercon.sink); + snd_soc_dapm_add_routes(dapm, &intercon, 1); + } + } + return rc; +} + +static int msm_dai_q6_spdif_dai_remove(struct snd_soc_dai *dai) +{ + struct msm_dai_q6_spdif_dai_data *dai_data; + int rc; + + dai_data = dev_get_drvdata(dai->dev); + + /* If AFE port is still up, close it */ + if (test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) { + rc = afe_spdif_reg_event_cfg(dai->id, + AFE_MODULE_DEREGISTER_EVENT_FLAG, + NULL, + dai_data); + if (rc < 0) + dev_err(dai->dev, + "fail to deregister event for port 0x%x\n", + dai->id); + + rc = afe_close(dai->id); /* can block */ + if (rc < 0) + dev_err(dai->dev, "fail to close AFE port\n"); + + clear_bit(STATUS_PORT_STARTED, dai_data->status_mask); + } + + msm_dai_q6_spdif_sysfs_remove(dai, dai_data); + + kfree(dai_data); + + return 0; +} + + +static struct snd_soc_dai_ops msm_dai_q6_spdif_ops = { + .prepare = msm_dai_q6_spdif_prepare, + .hw_params = msm_dai_q6_spdif_hw_params, + .shutdown = msm_dai_q6_spdif_shutdown, +}; + +static struct snd_soc_dai_driver msm_dai_q6_spdif_spdif_rx_dai[] = { + { + .playback = { + .stream_name = "Primary SPDIF Playback", + .aif_name = "PRI_SPDIF_RX", + .rates = SNDRV_PCM_RATE_32000 | + SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_176400 | + SNDRV_PCM_RATE_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .channels_min = 1, + .channels_max = 2, + .rate_min = 32000, + .rate_max = 192000, + }, + .name = "PRI_SPDIF_RX", + .ops = &msm_dai_q6_spdif_ops, + .id = AFE_PORT_ID_PRIMARY_SPDIF_RX, + .probe = msm_dai_q6_spdif_dai_probe, + .remove = msm_dai_q6_spdif_dai_remove, + }, + { + .playback = { + .stream_name = "Secondary SPDIF Playback", + .aif_name = "SEC_SPDIF_RX", + .rates = SNDRV_PCM_RATE_32000 | + SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_176400 | + SNDRV_PCM_RATE_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .channels_min = 1, + .channels_max = 2, + .rate_min = 32000, + .rate_max = 192000, + }, + .name = "SEC_SPDIF_RX", + .ops = &msm_dai_q6_spdif_ops, + .id = AFE_PORT_ID_SECONDARY_SPDIF_RX, + .probe = msm_dai_q6_spdif_dai_probe, + .remove = msm_dai_q6_spdif_dai_remove, + }, +}; + +static struct snd_soc_dai_driver msm_dai_q6_spdif_spdif_tx_dai[] = { + { + .capture = { + .stream_name = "Primary SPDIF Capture", + .aif_name = "PRI_SPDIF_TX", + .rates = SNDRV_PCM_RATE_32000 | + SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_176400 | + SNDRV_PCM_RATE_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .channels_min = 1, + .channels_max = 2, + .rate_min = 32000, + .rate_max = 192000, + }, + .name = "PRI_SPDIF_TX", + .ops = &msm_dai_q6_spdif_ops, + .id = AFE_PORT_ID_PRIMARY_SPDIF_TX, + .probe = msm_dai_q6_spdif_dai_probe, + .remove = msm_dai_q6_spdif_dai_remove, + }, + { + .capture = { + .stream_name = "Secondary SPDIF Capture", + .aif_name = "SEC_SPDIF_TX", + .rates = SNDRV_PCM_RATE_32000 | + SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_176400 | + SNDRV_PCM_RATE_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .channels_min = 1, + .channels_max = 2, + .rate_min = 32000, + .rate_max = 192000, + }, + .name = "SEC_SPDIF_TX", + .ops = &msm_dai_q6_spdif_ops, + .id = AFE_PORT_ID_SECONDARY_SPDIF_TX, + .probe = msm_dai_q6_spdif_dai_probe, + .remove = msm_dai_q6_spdif_dai_remove, + }, +}; + +static const struct snd_soc_component_driver msm_dai_spdif_q6_component = { + .name = "msm-dai-q6-spdif", +}; + +static int msm_dai_q6_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct msm_dai_q6_dai_data *dai_data = dev_get_drvdata(dai->dev); + int rc = 0; + + if (!test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) { + if (dai_data->enc_config.format != ENC_FMT_NONE) { + int bitwidth = 0; + + switch (dai_data->afe_rx_in_bitformat) { + case SNDRV_PCM_FORMAT_S32_LE: + bitwidth = 32; + break; + case SNDRV_PCM_FORMAT_S24_LE: + bitwidth = 24; + break; + case SNDRV_PCM_FORMAT_S16_LE: + default: + bitwidth = 16; + break; + } + pr_debug("%s: calling AFE_PORT_START_V2 with enc_format: %d\n", + __func__, dai_data->enc_config.format); + rc = afe_port_start_v2(dai->id, &dai_data->port_config, + dai_data->rate, + dai_data->afe_rx_in_channels, + bitwidth, + &dai_data->enc_config, NULL); + if (rc < 0) + pr_err("%s: afe_port_start_v2 failed error: %d\n", + __func__, rc); + } else if (dai_data->dec_config.format != DEC_FMT_NONE) { + int bitwidth = 0; + + /* + * If bitwidth is not configured set default value to + * zero, so that decoder port config uses slim device + * bit width value in afe decoder config. + */ + switch (dai_data->afe_tx_out_bitformat) { + case SNDRV_PCM_FORMAT_S32_LE: + bitwidth = 32; + break; + case SNDRV_PCM_FORMAT_S24_LE: + bitwidth = 24; + break; + case SNDRV_PCM_FORMAT_S16_LE: + bitwidth = 16; + break; + default: + bitwidth = 0; + break; + } + pr_debug("%s: calling AFE_PORT_START_V2 with dec format: %d\n", + __func__, dai_data->dec_config.format); + rc = afe_port_start_v2(dai->id, &dai_data->port_config, + dai_data->rate, + dai_data->afe_tx_out_channels, + bitwidth, + NULL, &dai_data->dec_config); + if (rc < 0) { + pr_err("%s: fail to open AFE port 0x%x\n", + __func__, dai->id); + } + } else { + rc = afe_port_start(dai->id, &dai_data->port_config, + dai_data->rate); + } + if (rc < 0) + dev_err(dai->dev, "fail to open AFE port 0x%x\n", + dai->id); + else + set_bit(STATUS_PORT_STARTED, + dai_data->status_mask); + } + return rc; +} + +static int msm_dai_q6_cdc_hw_params(struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai, int stream) +{ + struct msm_dai_q6_dai_data *dai_data = dev_get_drvdata(dai->dev); + + dai_data->channels = params_channels(params); + switch (dai_data->channels) { + case 2: + dai_data->port_config.i2s.mono_stereo = MSM_AFE_STEREO; + break; + case 1: + dai_data->port_config.i2s.mono_stereo = MSM_AFE_MONO; + break; + default: + return -EINVAL; + pr_err("%s: err channels %d\n", + __func__, dai_data->channels); + break; + } + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + case SNDRV_PCM_FORMAT_SPECIAL: + dai_data->port_config.i2s.bit_width = 16; + break; + case SNDRV_PCM_FORMAT_S24_LE: + case SNDRV_PCM_FORMAT_S24_3LE: + dai_data->port_config.i2s.bit_width = 24; + break; + default: + pr_err("%s: format %d\n", + __func__, params_format(params)); + return -EINVAL; + } + + dai_data->rate = params_rate(params); + dai_data->port_config.i2s.sample_rate = dai_data->rate; + dai_data->port_config.i2s.i2s_cfg_minor_version = + AFE_API_VERSION_I2S_CONFIG; + dai_data->port_config.i2s.data_format = AFE_LINEAR_PCM_DATA; + dev_dbg(dai->dev, " channel %d sample rate %d entered\n", + dai_data->channels, dai_data->rate); + + dai_data->port_config.i2s.channel_mode = 1; + return 0; +} + +static u16 num_of_bits_set(u16 sd_line_mask) +{ + u8 num_bits_set = 0; + + while (sd_line_mask) { + num_bits_set++; + sd_line_mask = sd_line_mask & (sd_line_mask - 1); + } + return num_bits_set; +} + +static int msm_dai_q6_i2s_hw_params(struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai, int stream) +{ + struct msm_dai_q6_dai_data *dai_data = dev_get_drvdata(dai->dev); + struct msm_i2s_data *i2s_pdata = + (struct msm_i2s_data *) dai->dev->platform_data; + + dai_data->channels = params_channels(params); + if (num_of_bits_set(i2s_pdata->sd_lines) == 1) { + switch (dai_data->channels) { + case 2: + dai_data->port_config.i2s.mono_stereo = MSM_AFE_STEREO; + break; + case 1: + dai_data->port_config.i2s.mono_stereo = MSM_AFE_MONO; + break; + default: + pr_warn("%s: greater than stereo has not been validated %d", + __func__, dai_data->channels); + break; + } + } + dai_data->rate = params_rate(params); + dai_data->port_config.i2s.sample_rate = dai_data->rate; + dai_data->port_config.i2s.i2s_cfg_minor_version = + AFE_API_VERSION_I2S_CONFIG; + dai_data->port_config.i2s.data_format = AFE_LINEAR_PCM_DATA; + /* Q6 only supports 16 as now */ + dai_data->port_config.i2s.bit_width = 16; + dai_data->port_config.i2s.channel_mode = 1; + + return 0; +} + +static int msm_dai_q6_slim_bus_hw_params(struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai, int stream) +{ + struct msm_dai_q6_dai_data *dai_data = dev_get_drvdata(dai->dev); + + dai_data->channels = params_channels(params); + dai_data->rate = params_rate(params); + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + case SNDRV_PCM_FORMAT_SPECIAL: + dai_data->port_config.slim_sch.bit_width = 16; + break; + case SNDRV_PCM_FORMAT_S24_LE: + case SNDRV_PCM_FORMAT_S24_3LE: + dai_data->port_config.slim_sch.bit_width = 24; + break; + case SNDRV_PCM_FORMAT_S32_LE: + dai_data->port_config.slim_sch.bit_width = 32; + break; + default: + pr_err("%s: format %d\n", + __func__, params_format(params)); + return -EINVAL; + } + + dai_data->port_config.slim_sch.sb_cfg_minor_version = + AFE_API_VERSION_SLIMBUS_CONFIG; + dai_data->port_config.slim_sch.sample_rate = dai_data->rate; + dai_data->port_config.slim_sch.num_channels = dai_data->channels; + + switch (dai->id) { + case SLIMBUS_7_RX: + case SLIMBUS_7_TX: + case SLIMBUS_8_RX: + case SLIMBUS_8_TX: + case SLIMBUS_9_RX: + case SLIMBUS_9_TX: + dai_data->port_config.slim_sch.slimbus_dev_id = + AFE_SLIMBUS_DEVICE_2; + break; + default: + dai_data->port_config.slim_sch.slimbus_dev_id = + AFE_SLIMBUS_DEVICE_1; + break; + } + + dev_dbg(dai->dev, "%s:slimbus_dev_id[%hu] bit_wd[%hu] format[%hu]\n" + "num_channel %hu shared_ch_mapping[0] %hu\n" + "slave_port_mapping[1] %hu slave_port_mapping[2] %hu\n" + "sample_rate %d\n", __func__, + dai_data->port_config.slim_sch.slimbus_dev_id, + dai_data->port_config.slim_sch.bit_width, + dai_data->port_config.slim_sch.data_format, + dai_data->port_config.slim_sch.num_channels, + dai_data->port_config.slim_sch.shared_ch_mapping[0], + dai_data->port_config.slim_sch.shared_ch_mapping[1], + dai_data->port_config.slim_sch.shared_ch_mapping[2], + dai_data->rate); + + return 0; +} + +static int msm_dai_q6_usb_audio_hw_params(struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai, int stream) +{ + struct msm_dai_q6_dai_data *dai_data = dev_get_drvdata(dai->dev); + + dai_data->channels = params_channels(params); + dai_data->rate = params_rate(params); + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + case SNDRV_PCM_FORMAT_SPECIAL: + dai_data->port_config.usb_audio.bit_width = 16; + break; + case SNDRV_PCM_FORMAT_S24_LE: + case SNDRV_PCM_FORMAT_S24_3LE: + dai_data->port_config.usb_audio.bit_width = 24; + break; + case SNDRV_PCM_FORMAT_S32_LE: + dai_data->port_config.usb_audio.bit_width = 32; + break; + + default: + dev_err(dai->dev, "%s: invalid format %d\n", + __func__, params_format(params)); + return -EINVAL; + } + dai_data->port_config.usb_audio.cfg_minor_version = + AFE_API_MINOR_VERSION_USB_AUDIO_CONFIG; + dai_data->port_config.usb_audio.num_channels = dai_data->channels; + dai_data->port_config.usb_audio.sample_rate = dai_data->rate; + + dev_dbg(dai->dev, "%s: dev_id[0x%x] bit_wd[%hu] format[%hu]\n" + "num_channel %hu sample_rate %d\n", __func__, + dai_data->port_config.usb_audio.dev_token, + dai_data->port_config.usb_audio.bit_width, + dai_data->port_config.usb_audio.data_format, + dai_data->port_config.usb_audio.num_channels, + dai_data->port_config.usb_audio.sample_rate); + + return 0; +} + +static int msm_dai_q6_bt_fm_hw_params(struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai, int stream) +{ + struct msm_dai_q6_dai_data *dai_data = dev_get_drvdata(dai->dev); + + dai_data->channels = params_channels(params); + dai_data->rate = params_rate(params); + + dev_dbg(dai->dev, "channels %d sample rate %d entered\n", + dai_data->channels, dai_data->rate); + + memset(&dai_data->port_config, 0, sizeof(dai_data->port_config)); + + pr_debug("%s: setting bt_fm parameters\n", __func__); + + dai_data->port_config.int_bt_fm.bt_fm_cfg_minor_version = + AFE_API_VERSION_INTERNAL_BT_FM_CONFIG; + dai_data->port_config.int_bt_fm.num_channels = dai_data->channels; + dai_data->port_config.int_bt_fm.sample_rate = dai_data->rate; + dai_data->port_config.int_bt_fm.bit_width = 16; + + return 0; +} + +static int msm_dai_q6_afe_rtproxy_hw_params(struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct msm_dai_q6_dai_data *dai_data = dev_get_drvdata(dai->dev); + + dai_data->rate = params_rate(params); + dai_data->port_config.rtproxy.num_channels = params_channels(params); + dai_data->port_config.rtproxy.sample_rate = params_rate(params); + + pr_debug("channel %d entered,dai_id: %d,rate: %d\n", + dai_data->port_config.rtproxy.num_channels, dai->id, dai_data->rate); + + dai_data->port_config.rtproxy.rt_proxy_cfg_minor_version = + AFE_API_VERSION_RT_PROXY_CONFIG; + dai_data->port_config.rtproxy.bit_width = 16; /* Q6 only supports 16 */ + dai_data->port_config.rtproxy.interleaved = 1; + dai_data->port_config.rtproxy.frame_size = params_period_bytes(params); + dai_data->port_config.rtproxy.jitter_allowance = + dai_data->port_config.rtproxy.frame_size/2; + dai_data->port_config.rtproxy.low_water_mark = 0; + dai_data->port_config.rtproxy.high_water_mark = 0; + + return 0; +} + +static int msm_dai_q6_pseudo_port_hw_params(struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai, int stream) +{ + struct msm_dai_q6_dai_data *dai_data = dev_get_drvdata(dai->dev); + + dai_data->channels = params_channels(params); + dai_data->rate = params_rate(params); + + /* Q6 only supports 16 as now */ + dai_data->port_config.pseudo_port.pseud_port_cfg_minor_version = + AFE_API_VERSION_PSEUDO_PORT_CONFIG; + dai_data->port_config.pseudo_port.num_channels = + params_channels(params); + dai_data->port_config.pseudo_port.bit_width = 16; + dai_data->port_config.pseudo_port.data_format = 0; + dai_data->port_config.pseudo_port.timing_mode = + AFE_PSEUDOPORT_TIMING_MODE_TIMER; + dai_data->port_config.pseudo_port.sample_rate = params_rate(params); + + dev_dbg(dai->dev, "%s: bit_wd[%hu] num_channels [%hu] format[%hu]\n" + "timing Mode %hu sample_rate %d\n", __func__, + dai_data->port_config.pseudo_port.bit_width, + dai_data->port_config.pseudo_port.num_channels, + dai_data->port_config.pseudo_port.data_format, + dai_data->port_config.pseudo_port.timing_mode, + dai_data->port_config.pseudo_port.sample_rate); + + return 0; +} + +/* Current implementation assumes hw_param is called once + * This may not be the case but what to do when ADM and AFE + * port are already opened and parameter changes + */ +static int msm_dai_q6_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + int rc = 0; + + switch (dai->id) { + case PRIMARY_I2S_TX: + case PRIMARY_I2S_RX: + case SECONDARY_I2S_RX: + rc = msm_dai_q6_cdc_hw_params(params, dai, substream->stream); + break; + case MI2S_RX: + rc = msm_dai_q6_i2s_hw_params(params, dai, substream->stream); + break; + case SLIMBUS_0_RX: + case SLIMBUS_1_RX: + case SLIMBUS_2_RX: + case SLIMBUS_3_RX: + case SLIMBUS_4_RX: + case SLIMBUS_5_RX: + case SLIMBUS_6_RX: + case SLIMBUS_7_RX: + case SLIMBUS_8_RX: + case SLIMBUS_9_RX: + case SLIMBUS_0_TX: + case SLIMBUS_1_TX: + case SLIMBUS_2_TX: + case SLIMBUS_3_TX: + case SLIMBUS_4_TX: + case SLIMBUS_5_TX: + case SLIMBUS_6_TX: + case SLIMBUS_7_TX: + case SLIMBUS_8_TX: + case SLIMBUS_9_TX: + rc = msm_dai_q6_slim_bus_hw_params(params, dai, + substream->stream); + break; + case INT_BT_SCO_RX: + case INT_BT_SCO_TX: + case INT_BT_A2DP_RX: + case INT_FM_RX: + case INT_FM_TX: + rc = msm_dai_q6_bt_fm_hw_params(params, dai, substream->stream); + break; + case AFE_PORT_ID_USB_RX: + case AFE_PORT_ID_USB_TX: + rc = msm_dai_q6_usb_audio_hw_params(params, dai, + substream->stream); + break; + case RT_PROXY_DAI_001_TX: + case RT_PROXY_DAI_001_RX: + case RT_PROXY_DAI_002_TX: + case RT_PROXY_DAI_002_RX: + rc = msm_dai_q6_afe_rtproxy_hw_params(params, dai); + break; + case VOICE_PLAYBACK_TX: + case VOICE2_PLAYBACK_TX: + case VOICE_RECORD_RX: + case VOICE_RECORD_TX: + rc = msm_dai_q6_pseudo_port_hw_params(params, + dai, substream->stream); + break; + default: + dev_err(dai->dev, "invalid AFE port ID 0x%x\n", dai->id); + rc = -EINVAL; + break; + } + + return rc; +} + +static void msm_dai_q6_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct msm_dai_q6_dai_data *dai_data = dev_get_drvdata(dai->dev); + int rc = 0; + + if (test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) { + pr_debug("%s: stop pseudo port:%d\n", __func__, dai->id); + rc = afe_close(dai->id); /* can block */ + if (rc < 0) + dev_err(dai->dev, "fail to close AFE port\n"); + pr_debug("%s: dai_data->status_mask = %ld\n", __func__, + *dai_data->status_mask); + clear_bit(STATUS_PORT_STARTED, dai_data->status_mask); + } +} + +static int msm_dai_q6_cdc_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct msm_dai_q6_dai_data *dai_data = dev_get_drvdata(dai->dev); + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + dai_data->port_config.i2s.ws_src = 1; /* CPU is master */ + break; + case SND_SOC_DAIFMT_CBM_CFM: + dai_data->port_config.i2s.ws_src = 0; /* CPU is slave */ + break; + default: + pr_err("%s: fmt 0x%x\n", + __func__, fmt & SND_SOC_DAIFMT_MASTER_MASK); + return -EINVAL; + } + + return 0; +} + +static int msm_dai_q6_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + int rc = 0; + + dev_dbg(dai->dev, "%s: id = %d fmt[%d]\n", __func__, + dai->id, fmt); + switch (dai->id) { + case PRIMARY_I2S_TX: + case PRIMARY_I2S_RX: + case MI2S_RX: + case SECONDARY_I2S_RX: + rc = msm_dai_q6_cdc_set_fmt(dai, fmt); + break; + default: + dev_err(dai->dev, "invalid cpu_dai id 0x%x\n", dai->id); + rc = -EINVAL; + break; + } + + return rc; +} + +static int msm_dai_q6_set_channel_map(struct snd_soc_dai *dai, + unsigned int tx_num, unsigned int *tx_slot, + unsigned int rx_num, unsigned int *rx_slot) + +{ + int rc = 0; + struct msm_dai_q6_dai_data *dai_data = dev_get_drvdata(dai->dev); + unsigned int i = 0; + + dev_dbg(dai->dev, "%s: id = %d\n", __func__, dai->id); + switch (dai->id) { + case SLIMBUS_0_RX: + case SLIMBUS_1_RX: + case SLIMBUS_2_RX: + case SLIMBUS_3_RX: + case SLIMBUS_4_RX: + case SLIMBUS_5_RX: + case SLIMBUS_6_RX: + case SLIMBUS_7_RX: + case SLIMBUS_8_RX: + case SLIMBUS_9_RX: + /* + * channel number to be between 128 and 255. + * For RX port use channel numbers + * from 138 to 144 for pre-Taiko + * from 144 to 159 for Taiko + */ + if (!rx_slot) { + pr_err("%s: rx slot not found\n", __func__); + return -EINVAL; + } + if (rx_num > AFE_PORT_MAX_AUDIO_CHAN_CNT) { + pr_err("%s: invalid rx num %d\n", __func__, rx_num); + return -EINVAL; + } + + for (i = 0; i < rx_num; i++) { + dai_data->port_config.slim_sch.shared_ch_mapping[i] = + rx_slot[i]; + pr_debug("%s: find number of channels[%d] ch[%d]\n", + __func__, i, rx_slot[i]); + } + dai_data->port_config.slim_sch.num_channels = rx_num; + pr_debug("%s: SLIMBUS_%d_RX cnt[%d] ch[%d %d]\n", __func__, + (dai->id - SLIMBUS_0_RX) / 2, rx_num, + dai_data->port_config.slim_sch.shared_ch_mapping[0], + dai_data->port_config.slim_sch.shared_ch_mapping[1]); + + break; + case SLIMBUS_0_TX: + case SLIMBUS_1_TX: + case SLIMBUS_2_TX: + case SLIMBUS_3_TX: + case SLIMBUS_4_TX: + case SLIMBUS_5_TX: + case SLIMBUS_6_TX: + case SLIMBUS_7_TX: + case SLIMBUS_8_TX: + case SLIMBUS_9_TX: + /* + * channel number to be between 128 and 255. + * For TX port use channel numbers + * from 128 to 137 for pre-Taiko + * from 128 to 143 for Taiko + */ + if (!tx_slot) { + pr_err("%s: tx slot not found\n", __func__); + return -EINVAL; + } + if (tx_num > AFE_PORT_MAX_AUDIO_CHAN_CNT) { + pr_err("%s: invalid tx num %d\n", __func__, tx_num); + return -EINVAL; + } + + for (i = 0; i < tx_num; i++) { + dai_data->port_config.slim_sch.shared_ch_mapping[i] = + tx_slot[i]; + pr_debug("%s: find number of channels[%d] ch[%d]\n", + __func__, i, tx_slot[i]); + } + dai_data->port_config.slim_sch.num_channels = tx_num; + pr_debug("%s:SLIMBUS_%d_TX cnt[%d] ch[%d %d]\n", __func__, + (dai->id - SLIMBUS_0_TX) / 2, tx_num, + dai_data->port_config.slim_sch.shared_ch_mapping[0], + dai_data->port_config.slim_sch.shared_ch_mapping[1]); + break; + default: + dev_err(dai->dev, "invalid cpu_dai id 0x%x\n", dai->id); + rc = -EINVAL; + break; + } + return rc; +} + +static struct snd_soc_dai_ops msm_dai_q6_ops = { + .prepare = msm_dai_q6_prepare, + .hw_params = msm_dai_q6_hw_params, + .shutdown = msm_dai_q6_shutdown, + .set_fmt = msm_dai_q6_set_fmt, + .set_channel_map = msm_dai_q6_set_channel_map, +}; + +static int msm_dai_q6_cal_info_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct msm_dai_q6_dai_data *dai_data = kcontrol->private_data; + u16 port_id = ((struct soc_enum *) + kcontrol->private_value)->reg; + + dai_data->cal_mode = ucontrol->value.integer.value[0]; + pr_debug("%s: setting cal_mode to %d\n", + __func__, dai_data->cal_mode); + afe_set_cal_mode(port_id, dai_data->cal_mode); + + return 0; +} + +static int msm_dai_q6_cal_info_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct msm_dai_q6_dai_data *dai_data = kcontrol->private_data; + + ucontrol->value.integer.value[0] = dai_data->cal_mode; + return 0; +} + +static int msm_dai_q6_sb_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct msm_dai_q6_dai_data *dai_data = kcontrol->private_data; + int value = ucontrol->value.integer.value[0]; + + if (dai_data) { + dai_data->port_config.slim_sch.data_format = value; + pr_debug("%s: format = %d\n", __func__, value); + } + + return 0; +} + +static int msm_dai_q6_sb_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct msm_dai_q6_dai_data *dai_data = kcontrol->private_data; + + if (dai_data) + ucontrol->value.integer.value[0] = + dai_data->port_config.slim_sch.data_format; + + return 0; +} + +static int msm_dai_q6_usb_audio_cfg_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct msm_dai_q6_dai_data *dai_data = kcontrol->private_data; + u32 val = ucontrol->value.integer.value[0]; + + if (dai_data) { + dai_data->port_config.usb_audio.dev_token = val; + pr_debug("%s: dev_token = 0x%x\n", __func__, + dai_data->port_config.usb_audio.dev_token); + } else { + pr_err("%s: dai_data is NULL\n", __func__); + } + + return 0; +} + +static int msm_dai_q6_usb_audio_cfg_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct msm_dai_q6_dai_data *dai_data = kcontrol->private_data; + + if (dai_data) { + ucontrol->value.integer.value[0] = + dai_data->port_config.usb_audio.dev_token; + pr_debug("%s: dev_token = 0x%x\n", __func__, + dai_data->port_config.usb_audio.dev_token); + } else { + pr_err("%s: dai_data is NULL\n", __func__); + } + + return 0; +} + +static int msm_dai_q6_usb_audio_endian_cfg_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct msm_dai_q6_dai_data *dai_data = kcontrol->private_data; + u32 val = ucontrol->value.integer.value[0]; + + if (dai_data) { + dai_data->port_config.usb_audio.endian = val; + pr_debug("%s: endian = 0x%x\n", __func__, + dai_data->port_config.usb_audio.endian); + } else { + pr_err("%s: dai_data is NULL\n", __func__); + return -EINVAL; + } + + return 0; +} + +static int msm_dai_q6_usb_audio_endian_cfg_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct msm_dai_q6_dai_data *dai_data = kcontrol->private_data; + + if (dai_data) { + ucontrol->value.integer.value[0] = + dai_data->port_config.usb_audio.endian; + pr_debug("%s: endian = 0x%x\n", __func__, + dai_data->port_config.usb_audio.endian); + } else { + pr_err("%s: dai_data is NULL\n", __func__); + return -EINVAL; + } + + return 0; +} + +static int msm_dai_q6_usb_audio_svc_interval_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct msm_dai_q6_dai_data *dai_data = kcontrol->private_data; + u32 val = ucontrol->value.integer.value[0]; + + if (!dai_data) { + pr_err("%s: dai_data is NULL\n", __func__); + return -EINVAL; + } + dai_data->port_config.usb_audio.service_interval = val; + pr_debug("%s: new service interval = %u\n", __func__, + dai_data->port_config.usb_audio.service_interval); + return 0; +} + +static int msm_dai_q6_usb_audio_svc_interval_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct msm_dai_q6_dai_data *dai_data = kcontrol->private_data; + + if (!dai_data) { + pr_err("%s: dai_data is NULL\n", __func__); + return -EINVAL; + } + ucontrol->value.integer.value[0] = + dai_data->port_config.usb_audio.service_interval; + pr_debug("%s: service interval = %d\n", __func__, + dai_data->port_config.usb_audio.service_interval); + return 0; +} + +static int msm_dai_q6_afe_enc_cfg_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; + uinfo->count = sizeof(struct afe_enc_config); + + return 0; +} + +static int msm_dai_q6_afe_enc_cfg_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret = 0; + struct msm_dai_q6_dai_data *dai_data = kcontrol->private_data; + + if (dai_data) { + int format_size = sizeof(dai_data->enc_config.format); + + pr_debug("%s: encoder config for %d format\n", + __func__, dai_data->enc_config.format); + memcpy(ucontrol->value.bytes.data, + &dai_data->enc_config.format, + format_size); + switch (dai_data->enc_config.format) { + case ENC_FMT_SBC: + memcpy(ucontrol->value.bytes.data + format_size, + &dai_data->enc_config.data, + sizeof(struct asm_sbc_enc_cfg_t)); + break; + case ENC_FMT_AAC_V2: + memcpy(ucontrol->value.bytes.data + format_size, + &dai_data->enc_config.data, + sizeof(struct asm_aac_enc_cfg_t)); + break; + case ENC_FMT_APTX: + memcpy(ucontrol->value.bytes.data + format_size, + &dai_data->enc_config.data, + sizeof(struct asm_aptx_enc_cfg_t)); + break; + case ENC_FMT_APTX_HD: + memcpy(ucontrol->value.bytes.data + format_size, + &dai_data->enc_config.data, + sizeof(struct asm_custom_enc_cfg_t)); + break; + case ENC_FMT_CELT: + memcpy(ucontrol->value.bytes.data + format_size, + &dai_data->enc_config.data, + sizeof(struct asm_celt_enc_cfg_t)); + break; + case ENC_FMT_LDAC: + memcpy(ucontrol->value.bytes.data + format_size, + &dai_data->enc_config.data, + sizeof(struct asm_ldac_enc_cfg_t)); + break; + case ENC_FMT_APTX_ADAPTIVE: + memcpy(ucontrol->value.bytes.data + format_size, + &dai_data->enc_config.data, + sizeof(struct asm_aptx_ad_enc_cfg_t)); + break; + default: + pr_debug("%s: unknown format = %d\n", + __func__, dai_data->enc_config.format); + ret = -EINVAL; + break; + } + } + + return ret; +} + +static int msm_dai_q6_afe_enc_cfg_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret = 0; + struct msm_dai_q6_dai_data *dai_data = kcontrol->private_data; + + if (dai_data) { + int format_size = sizeof(dai_data->enc_config.format); + + memset(&dai_data->enc_config, 0x0, + sizeof(struct afe_enc_config)); + memcpy(&dai_data->enc_config.format, + ucontrol->value.bytes.data, + format_size); + pr_debug("%s: Received encoder config for %d format\n", + __func__, dai_data->enc_config.format); + switch (dai_data->enc_config.format) { + case ENC_FMT_SBC: + memcpy(&dai_data->enc_config.data, + ucontrol->value.bytes.data + format_size, + sizeof(struct asm_sbc_enc_cfg_t)); + break; + case ENC_FMT_AAC_V2: + memcpy(&dai_data->enc_config.data, + ucontrol->value.bytes.data + format_size, + sizeof(struct asm_aac_enc_cfg_t)); + break; + case ENC_FMT_APTX: + memcpy(&dai_data->enc_config.data, + ucontrol->value.bytes.data + format_size, + sizeof(struct asm_aptx_enc_cfg_t)); + break; + case ENC_FMT_APTX_HD: + memcpy(&dai_data->enc_config.data, + ucontrol->value.bytes.data + format_size, + sizeof(struct asm_custom_enc_cfg_t)); + break; + case ENC_FMT_CELT: + memcpy(&dai_data->enc_config.data, + ucontrol->value.bytes.data + format_size, + sizeof(struct asm_celt_enc_cfg_t)); + break; + case ENC_FMT_LDAC: + memcpy(&dai_data->enc_config.data, + ucontrol->value.bytes.data + format_size, + sizeof(struct asm_ldac_enc_cfg_t)); + break; + case ENC_FMT_APTX_ADAPTIVE: + memcpy(&dai_data->enc_config.data, + ucontrol->value.bytes.data + format_size, + sizeof(struct asm_aptx_ad_enc_cfg_t)); + break; + default: + pr_debug("%s: Ignore enc config for unknown format = %d\n", + __func__, dai_data->enc_config.format); + ret = -EINVAL; + break; + } + } else + ret = -EINVAL; + + return ret; +} + +static const char *const afe_chs_text[] = {"Zero", "One", "Two"}; + +static const struct soc_enum afe_chs_enum[] = { + SOC_ENUM_SINGLE_EXT(3, afe_chs_text), +}; + +static const char *const afe_bit_format_text[] = {"S16_LE", "S24_LE", + "S32_LE"}; + +static const struct soc_enum afe_bit_format_enum[] = { + SOC_ENUM_SINGLE_EXT(3, afe_bit_format_text), +}; + +static const char *const tws_chs_mode_text[] = {"Zero", "One", "Two"}; + +static const struct soc_enum tws_chs_mode_enum[] = { + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(tws_chs_mode_text), tws_chs_mode_text), +}; + +static int msm_dai_q6_afe_input_channel_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct msm_dai_q6_dai_data *dai_data = kcontrol->private_data; + + if (dai_data) { + ucontrol->value.integer.value[0] = dai_data->afe_rx_in_channels; + pr_debug("%s:afe input channel = %d\n", + __func__, dai_data->afe_rx_in_channels); + } + + return 0; +} + +static int msm_dai_q6_afe_input_channel_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct msm_dai_q6_dai_data *dai_data = kcontrol->private_data; + + if (dai_data) { + dai_data->afe_rx_in_channels = ucontrol->value.integer.value[0]; + pr_debug("%s: updating afe input channel : %d\n", + __func__, dai_data->afe_rx_in_channels); + } + + return 0; +} + +static int msm_dai_q6_tws_channel_mode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dai *dai = kcontrol->private_data; + struct msm_dai_q6_dai_data *dai_data = NULL; + + if (dai) + dai_data = dev_get_drvdata(dai->dev); + + if (dai_data) { + ucontrol->value.integer.value[0] = + dai_data->enc_config.mono_mode; + pr_debug("%s:tws channel mode = %d\n", + __func__, dai_data->enc_config.mono_mode); + } + + return 0; +} + +static int msm_dai_q6_tws_channel_mode_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dai *dai = kcontrol->private_data; + struct msm_dai_q6_dai_data *dai_data = NULL; + int ret = 0; + + if (dai) + dai_data = dev_get_drvdata(dai->dev); + + if (dai_data && (dai_data->enc_config.format == ENC_FMT_APTX)) { + if (test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) { + ret = afe_set_tws_channel_mode(dai->id, + ucontrol->value.integer.value[0]); + if (ret < 0) { + pr_err("%s: channel mode setting failed for TWS\n", + __func__); + goto exit; + } else { + pr_debug("%s: updating tws channel mode : %d\n", + __func__, dai_data->enc_config.mono_mode); + } + } + if (ucontrol->value.integer.value[0] == + MSM_DAI_TWS_CHANNEL_MODE_ONE || + ucontrol->value.integer.value[0] == + MSM_DAI_TWS_CHANNEL_MODE_TWO) + dai_data->enc_config.mono_mode = + ucontrol->value.integer.value[0]; + else + return -EINVAL; + } +exit: + return ret; +} + +static int msm_dai_q6_afe_input_bit_format_get( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct msm_dai_q6_dai_data *dai_data = kcontrol->private_data; + + if (!dai_data) { + pr_err("%s: Invalid dai data\n", __func__); + return -EINVAL; + } + + switch (dai_data->afe_rx_in_bitformat) { + case SNDRV_PCM_FORMAT_S32_LE: + ucontrol->value.integer.value[0] = 2; + break; + case SNDRV_PCM_FORMAT_S24_LE: + ucontrol->value.integer.value[0] = 1; + break; + case SNDRV_PCM_FORMAT_S16_LE: + default: + ucontrol->value.integer.value[0] = 0; + break; + } + pr_debug("%s: afe input bit format : %ld\n", + __func__, ucontrol->value.integer.value[0]); + + return 0; +} + +static int msm_dai_q6_afe_input_bit_format_put( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct msm_dai_q6_dai_data *dai_data = kcontrol->private_data; + + if (!dai_data) { + pr_err("%s: Invalid dai data\n", __func__); + return -EINVAL; + } + switch (ucontrol->value.integer.value[0]) { + case 2: + dai_data->afe_rx_in_bitformat = SNDRV_PCM_FORMAT_S32_LE; + break; + case 1: + dai_data->afe_rx_in_bitformat = SNDRV_PCM_FORMAT_S24_LE; + break; + case 0: + default: + dai_data->afe_rx_in_bitformat = SNDRV_PCM_FORMAT_S16_LE; + break; + } + pr_debug("%s: updating afe input bit format : %d\n", + __func__, dai_data->afe_rx_in_bitformat); + + return 0; +} + +static int msm_dai_q6_afe_output_bit_format_get( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct msm_dai_q6_dai_data *dai_data = kcontrol->private_data; + + if (!dai_data) { + pr_err("%s: Invalid dai data\n", __func__); + return -EINVAL; + } + + switch (dai_data->afe_tx_out_bitformat) { + case SNDRV_PCM_FORMAT_S32_LE: + ucontrol->value.integer.value[0] = 2; + break; + case SNDRV_PCM_FORMAT_S24_LE: + ucontrol->value.integer.value[0] = 1; + break; + case SNDRV_PCM_FORMAT_S16_LE: + default: + ucontrol->value.integer.value[0] = 0; + break; + } + pr_debug("%s: afe output bit format : %ld\n", + __func__, ucontrol->value.integer.value[0]); + + return 0; +} + +static int msm_dai_q6_afe_output_bit_format_put( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct msm_dai_q6_dai_data *dai_data = kcontrol->private_data; + + if (!dai_data) { + pr_err("%s: Invalid dai data\n", __func__); + return -EINVAL; + } + switch (ucontrol->value.integer.value[0]) { + case 2: + dai_data->afe_tx_out_bitformat = SNDRV_PCM_FORMAT_S32_LE; + break; + case 1: + dai_data->afe_tx_out_bitformat = SNDRV_PCM_FORMAT_S24_LE; + break; + case 0: + default: + dai_data->afe_tx_out_bitformat = SNDRV_PCM_FORMAT_S16_LE; + break; + } + pr_debug("%s: updating afe output bit format : %d\n", + __func__, dai_data->afe_tx_out_bitformat); + + return 0; +} + +static int msm_dai_q6_afe_output_channel_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct msm_dai_q6_dai_data *dai_data = kcontrol->private_data; + + if (dai_data) { + ucontrol->value.integer.value[0] = + dai_data->afe_tx_out_channels; + pr_debug("%s:afe output channel = %d\n", + __func__, dai_data->afe_tx_out_channels); + } + return 0; +} + +static int msm_dai_q6_afe_output_channel_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct msm_dai_q6_dai_data *dai_data = kcontrol->private_data; + + if (dai_data) { + dai_data->afe_tx_out_channels = + ucontrol->value.integer.value[0]; + pr_debug("%s: updating afe output channel : %d\n", + __func__, dai_data->afe_tx_out_channels); + } + return 0; +} + +static int msm_dai_q6_afe_scrambler_mode_get( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct msm_dai_q6_dai_data *dai_data = kcontrol->private_data; + + if (!dai_data) { + pr_err("%s: Invalid dai data\n", __func__); + return -EINVAL; + } + ucontrol->value.integer.value[0] = dai_data->enc_config.scrambler_mode; + + return 0; +} + +static int msm_dai_q6_afe_scrambler_mode_put( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct msm_dai_q6_dai_data *dai_data = kcontrol->private_data; + + if (!dai_data) { + pr_err("%s: Invalid dai data\n", __func__); + return -EINVAL; + } + dai_data->enc_config.scrambler_mode = ucontrol->value.integer.value[0]; + pr_debug("%s: afe scrambler mode : %d\n", + __func__, dai_data->enc_config.scrambler_mode); + return 0; +} + +static const struct snd_kcontrol_new afe_enc_config_controls[] = { + { + .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_INACTIVE), + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "SLIM_7_RX Encoder Config", + .info = msm_dai_q6_afe_enc_cfg_info, + .get = msm_dai_q6_afe_enc_cfg_get, + .put = msm_dai_q6_afe_enc_cfg_put, + }, + SOC_ENUM_EXT("AFE Input Channels", afe_chs_enum[0], + msm_dai_q6_afe_input_channel_get, + msm_dai_q6_afe_input_channel_put), + SOC_ENUM_EXT("AFE Input Bit Format", afe_bit_format_enum[0], + msm_dai_q6_afe_input_bit_format_get, + msm_dai_q6_afe_input_bit_format_put), + SOC_SINGLE_EXT("AFE Scrambler Mode", + 0, 0, 1, 0, + msm_dai_q6_afe_scrambler_mode_get, + msm_dai_q6_afe_scrambler_mode_put), + SOC_ENUM_EXT("TWS Channel Mode", tws_chs_mode_enum[0], + msm_dai_q6_tws_channel_mode_get, + msm_dai_q6_tws_channel_mode_put) +}; + +static int msm_dai_q6_afe_dec_cfg_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; + uinfo->count = sizeof(struct afe_dec_config); + + return 0; +} + +static int msm_dai_q6_afe_dec_cfg_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct msm_dai_q6_dai_data *dai_data = kcontrol->private_data; + u32 format_size = 0; + + if (!dai_data) { + pr_err("%s: Invalid dai data\n", __func__); + return -EINVAL; + } + + format_size = sizeof(dai_data->dec_config.format); + memcpy(ucontrol->value.bytes.data, + &dai_data->dec_config.format, + format_size); + switch (dai_data->dec_config.format) { + case DEC_FMT_AAC_V2: + memcpy(ucontrol->value.bytes.data + format_size, + &dai_data->dec_config.data, + sizeof(struct asm_aac_dec_cfg_v2_t)); + break; + case DEC_FMT_SBC: + case DEC_FMT_MP3: + /* No decoder specific data available */ + break; + default: + pr_debug("%s: Default decoder config for %d format: Expect abr_dec_cfg\n", + __func__, dai_data->dec_config.format); + memcpy(ucontrol->value.bytes.data + format_size, + &dai_data->dec_config.abr_dec_cfg, + sizeof(struct afe_abr_dec_cfg_t)); + + break; + } + return 0; +} + +static int msm_dai_q6_afe_dec_cfg_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct msm_dai_q6_dai_data *dai_data = kcontrol->private_data; + u32 format_size = 0; + + if (!dai_data) { + pr_err("%s: Invalid dai data\n", __func__); + return -EINVAL; + } + + memset(&dai_data->dec_config, 0x0, + sizeof(struct afe_dec_config)); + format_size = sizeof(dai_data->dec_config.format); + memcpy(&dai_data->dec_config.format, + ucontrol->value.bytes.data, + format_size); + pr_debug("%s: Received decoder config for %d format\n", + __func__, dai_data->dec_config.format); + switch (dai_data->dec_config.format) { + case DEC_FMT_AAC_V2: + memcpy(&dai_data->dec_config.data, + ucontrol->value.bytes.data + format_size, + sizeof(struct asm_aac_dec_cfg_v2_t)); + break; + case DEC_FMT_SBC: + case DEC_FMT_MP3: + /* No decoder specific data available */ + break; + default: + pr_debug("%s: Default decoder config for %d format: Expect abr_dec_cfg\n", + __func__, dai_data->dec_config.format); + memcpy(&dai_data->dec_config.abr_dec_cfg, + ucontrol->value.bytes.data + format_size, + sizeof(struct afe_abr_dec_cfg_t)); + break; + } + return 0; +} + +static const struct snd_kcontrol_new afe_dec_config_controls[] = { + { + .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_INACTIVE), + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "SLIM_7_TX Decoder Config", + .info = msm_dai_q6_afe_dec_cfg_info, + .get = msm_dai_q6_afe_dec_cfg_get, + .put = msm_dai_q6_afe_dec_cfg_put, + }, + { + .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_INACTIVE), + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "SLIM_9_TX Decoder Config", + .info = msm_dai_q6_afe_dec_cfg_info, + .get = msm_dai_q6_afe_dec_cfg_get, + .put = msm_dai_q6_afe_dec_cfg_put, + }, + SOC_ENUM_EXT("AFE Output Channels", afe_chs_enum[0], + msm_dai_q6_afe_output_channel_get, + msm_dai_q6_afe_output_channel_put), + SOC_ENUM_EXT("AFE Output Bit Format", afe_bit_format_enum[0], + msm_dai_q6_afe_output_bit_format_get, + msm_dai_q6_afe_output_bit_format_put), +}; + +static int msm_dai_q6_slim_rx_drift_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; + uinfo->count = sizeof(struct afe_param_id_dev_timing_stats); + + return 0; +} + +static int msm_dai_q6_slim_rx_drift_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret = -EINVAL; + struct afe_param_id_dev_timing_stats timing_stats; + struct snd_soc_dai *dai = kcontrol->private_data; + struct msm_dai_q6_dai_data *dai_data = dev_get_drvdata(dai->dev); + + if (!test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) { + pr_debug("%s: afe port not started. dai_data->status_mask = %ld\n", + __func__, *dai_data->status_mask); + goto done; + } + + memset(&timing_stats, 0, sizeof(struct afe_param_id_dev_timing_stats)); + ret = afe_get_av_dev_drift(&timing_stats, dai->id); + if (ret) { + pr_err("%s: Error getting AFE Drift for port %d, err=%d\n", + __func__, dai->id, ret); + + goto done; + } + + memcpy(ucontrol->value.bytes.data, (void *)&timing_stats, + sizeof(struct afe_param_id_dev_timing_stats)); +done: + return ret; +} + +static const char * const afe_cal_mode_text[] = { + "CAL_MODE_DEFAULT", "CAL_MODE_NONE" +}; + +static const struct soc_enum slim_2_rx_enum = + SOC_ENUM_SINGLE(SLIMBUS_2_RX, 0, ARRAY_SIZE(afe_cal_mode_text), + afe_cal_mode_text); + +static const struct soc_enum rt_proxy_1_rx_enum = + SOC_ENUM_SINGLE(RT_PROXY_PORT_001_RX, 0, ARRAY_SIZE(afe_cal_mode_text), + afe_cal_mode_text); + +static const struct soc_enum rt_proxy_1_tx_enum = + SOC_ENUM_SINGLE(RT_PROXY_PORT_001_TX, 0, ARRAY_SIZE(afe_cal_mode_text), + afe_cal_mode_text); + +static const struct snd_kcontrol_new sb_config_controls[] = { + SOC_ENUM_EXT("SLIM_4_TX Format", sb_config_enum[0], + msm_dai_q6_sb_format_get, + msm_dai_q6_sb_format_put), + SOC_ENUM_EXT("SLIM_2_RX SetCalMode", slim_2_rx_enum, + msm_dai_q6_cal_info_get, + msm_dai_q6_cal_info_put), + SOC_ENUM_EXT("SLIM_2_RX Format", sb_config_enum[0], + msm_dai_q6_sb_format_get, + msm_dai_q6_sb_format_put) +}; + +static const struct snd_kcontrol_new rt_proxy_config_controls[] = { + SOC_ENUM_EXT("RT_PROXY_1_RX SetCalMode", rt_proxy_1_rx_enum, + msm_dai_q6_cal_info_get, + msm_dai_q6_cal_info_put), + SOC_ENUM_EXT("RT_PROXY_1_TX SetCalMode", rt_proxy_1_tx_enum, + msm_dai_q6_cal_info_get, + msm_dai_q6_cal_info_put), +}; + +static const struct snd_kcontrol_new usb_audio_cfg_controls[] = { + SOC_SINGLE_EXT("USB_AUDIO_RX dev_token", 0, 0, UINT_MAX, 0, + msm_dai_q6_usb_audio_cfg_get, + msm_dai_q6_usb_audio_cfg_put), + SOC_SINGLE_EXT("USB_AUDIO_RX endian", 0, 0, 1, 0, + msm_dai_q6_usb_audio_endian_cfg_get, + msm_dai_q6_usb_audio_endian_cfg_put), + SOC_SINGLE_EXT("USB_AUDIO_TX dev_token", 0, 0, UINT_MAX, 0, + msm_dai_q6_usb_audio_cfg_get, + msm_dai_q6_usb_audio_cfg_put), + SOC_SINGLE_EXT("USB_AUDIO_TX endian", 0, 0, 1, 0, + msm_dai_q6_usb_audio_endian_cfg_get, + msm_dai_q6_usb_audio_endian_cfg_put), + SOC_SINGLE_EXT("USB_AUDIO_RX service_interval", SND_SOC_NOPM, 0, + UINT_MAX, 0, + msm_dai_q6_usb_audio_svc_interval_get, + msm_dai_q6_usb_audio_svc_interval_put), +}; + +static const struct snd_kcontrol_new avd_drift_config_controls[] = { + { + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "SLIMBUS_0_RX DRIFT", + .info = msm_dai_q6_slim_rx_drift_info, + .get = msm_dai_q6_slim_rx_drift_get, + }, + { + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "SLIMBUS_6_RX DRIFT", + .info = msm_dai_q6_slim_rx_drift_info, + .get = msm_dai_q6_slim_rx_drift_get, + }, + { + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "SLIMBUS_7_RX DRIFT", + .info = msm_dai_q6_slim_rx_drift_info, + .get = msm_dai_q6_slim_rx_drift_get, + }, +}; +static int msm_dai_q6_dai_probe(struct snd_soc_dai *dai) +{ + struct msm_dai_q6_dai_data *dai_data; + int rc = 0; + + if (!dai) { + pr_err("%s: Invalid params dai\n", __func__); + return -EINVAL; + } + if (!dai->dev) { + pr_err("%s: Invalid params dai dev\n", __func__); + return -EINVAL; + } + + dai_data = kzalloc(sizeof(struct msm_dai_q6_dai_data), GFP_KERNEL); + + if (!dai_data) + return -ENOMEM; + else + dev_set_drvdata(dai->dev, dai_data); + + msm_dai_q6_set_dai_id(dai); + + switch (dai->id) { + case SLIMBUS_4_TX: + rc = snd_ctl_add(dai->component->card->snd_card, + snd_ctl_new1(&sb_config_controls[0], + dai_data)); + break; + case SLIMBUS_2_RX: + rc = snd_ctl_add(dai->component->card->snd_card, + snd_ctl_new1(&sb_config_controls[1], + dai_data)); + rc = snd_ctl_add(dai->component->card->snd_card, + snd_ctl_new1(&sb_config_controls[2], + dai_data)); + break; + case SLIMBUS_7_RX: + rc = snd_ctl_add(dai->component->card->snd_card, + snd_ctl_new1(&afe_enc_config_controls[0], + dai_data)); + rc = snd_ctl_add(dai->component->card->snd_card, + snd_ctl_new1(&afe_enc_config_controls[1], + dai_data)); + rc = snd_ctl_add(dai->component->card->snd_card, + snd_ctl_new1(&afe_enc_config_controls[2], + dai_data)); + rc = snd_ctl_add(dai->component->card->snd_card, + snd_ctl_new1(&afe_enc_config_controls[3], + dai_data)); + rc = snd_ctl_add(dai->component->card->snd_card, + snd_ctl_new1(&afe_enc_config_controls[4], + dai)); + rc = snd_ctl_add(dai->component->card->snd_card, + snd_ctl_new1(&avd_drift_config_controls[2], + dai)); + break; + case SLIMBUS_7_TX: + rc = snd_ctl_add(dai->component->card->snd_card, + snd_ctl_new1(&afe_dec_config_controls[0], + dai_data)); + break; + case SLIMBUS_9_TX: + rc = snd_ctl_add(dai->component->card->snd_card, + snd_ctl_new1(&afe_dec_config_controls[1], + dai_data)); + rc = snd_ctl_add(dai->component->card->snd_card, + snd_ctl_new1(&afe_dec_config_controls[2], + dai_data)); + rc = snd_ctl_add(dai->component->card->snd_card, + snd_ctl_new1(&afe_dec_config_controls[3], + dai_data)); + break; + case RT_PROXY_DAI_001_RX: + rc = snd_ctl_add(dai->component->card->snd_card, + snd_ctl_new1(&rt_proxy_config_controls[0], + dai_data)); + break; + case RT_PROXY_DAI_001_TX: + rc = snd_ctl_add(dai->component->card->snd_card, + snd_ctl_new1(&rt_proxy_config_controls[1], + dai_data)); + break; + case AFE_PORT_ID_USB_RX: + rc = snd_ctl_add(dai->component->card->snd_card, + snd_ctl_new1(&usb_audio_cfg_controls[0], + dai_data)); + rc = snd_ctl_add(dai->component->card->snd_card, + snd_ctl_new1(&usb_audio_cfg_controls[1], + dai_data)); + rc = snd_ctl_add(dai->component->card->snd_card, + snd_ctl_new1(&usb_audio_cfg_controls[4], + dai_data)); + break; + case AFE_PORT_ID_USB_TX: + rc = snd_ctl_add(dai->component->card->snd_card, + snd_ctl_new1(&usb_audio_cfg_controls[2], + dai_data)); + rc = snd_ctl_add(dai->component->card->snd_card, + snd_ctl_new1(&usb_audio_cfg_controls[3], + dai_data)); + break; + case SLIMBUS_0_RX: + rc = snd_ctl_add(dai->component->card->snd_card, + snd_ctl_new1(&avd_drift_config_controls[0], + dai)); + break; + case SLIMBUS_6_RX: + rc = snd_ctl_add(dai->component->card->snd_card, + snd_ctl_new1(&avd_drift_config_controls[1], + dai)); + break; + } + if (rc < 0) + dev_err(dai->dev, "%s: err add config ctl, DAI = %s\n", + __func__, dai->name); + + rc = msm_dai_q6_dai_add_route(dai); + return rc; +} + +static int msm_dai_q6_dai_remove(struct snd_soc_dai *dai) +{ + struct msm_dai_q6_dai_data *dai_data; + int rc; + + dai_data = dev_get_drvdata(dai->dev); + + /* If AFE port is still up, close it */ + if (test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) { + pr_debug("%s: stop pseudo port:%d\n", __func__, dai->id); + rc = afe_close(dai->id); /* can block */ + if (rc < 0) + dev_err(dai->dev, "fail to close AFE port\n"); + clear_bit(STATUS_PORT_STARTED, dai_data->status_mask); + } + kfree(dai_data); + + return 0; +} + +static struct snd_soc_dai_driver msm_dai_q6_afe_rx_dai[] = { + { + .playback = { + .stream_name = "AFE Playback", + .aif_name = "PCM_RX", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_dai_q6_ops, + .id = RT_PROXY_DAI_001_RX, + .probe = msm_dai_q6_dai_probe, + .remove = msm_dai_q6_dai_remove, + }, + { + .playback = { + .stream_name = "AFE-PROXY RX", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_dai_q6_ops, + .id = RT_PROXY_DAI_002_RX, + .probe = msm_dai_q6_dai_probe, + .remove = msm_dai_q6_dai_remove, + }, +}; + +static struct snd_soc_dai_driver msm_dai_q6_afe_tx_dai[] = { + { + .capture = { + .stream_name = "AFE Capture", + .aif_name = "PCM_TX", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_dai_q6_ops, + .id = RT_PROXY_DAI_002_TX, + .probe = msm_dai_q6_dai_probe, + .remove = msm_dai_q6_dai_remove, + }, + { + .capture = { + .stream_name = "AFE-PROXY TX", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_dai_q6_ops, + .id = RT_PROXY_DAI_001_TX, + .probe = msm_dai_q6_dai_probe, + .remove = msm_dai_q6_dai_remove, + }, +}; + +static struct snd_soc_dai_driver msm_dai_q6_bt_sco_rx_dai = { + .playback = { + .stream_name = "Internal BT-SCO Playback", + .aif_name = "INT_BT_SCO_RX", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 1, + .rate_max = 16000, + .rate_min = 8000, + }, + .ops = &msm_dai_q6_ops, + .id = INT_BT_SCO_RX, + .probe = msm_dai_q6_dai_probe, + .remove = msm_dai_q6_dai_remove, +}; + +static struct snd_soc_dai_driver msm_dai_q6_bt_a2dp_rx_dai = { + .playback = { + .stream_name = "Internal BT-A2DP Playback", + .aif_name = "INT_BT_A2DP_RX", + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 2, + .rate_max = 48000, + .rate_min = 48000, + }, + .ops = &msm_dai_q6_ops, + .id = INT_BT_A2DP_RX, + .probe = msm_dai_q6_dai_probe, + .remove = msm_dai_q6_dai_remove, +}; + +static struct snd_soc_dai_driver msm_dai_q6_bt_sco_tx_dai = { + .capture = { + .stream_name = "Internal BT-SCO Capture", + .aif_name = "INT_BT_SCO_TX", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 1, + .rate_max = 16000, + .rate_min = 8000, + }, + .ops = &msm_dai_q6_ops, + .id = INT_BT_SCO_TX, + .probe = msm_dai_q6_dai_probe, + .remove = msm_dai_q6_dai_remove, +}; + +static struct snd_soc_dai_driver msm_dai_q6_fm_rx_dai = { + .playback = { + .stream_name = "Internal FM Playback", + .aif_name = "INT_FM_RX", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 2, + .channels_max = 2, + .rate_max = 48000, + .rate_min = 8000, + }, + .ops = &msm_dai_q6_ops, + .id = INT_FM_RX, + .probe = msm_dai_q6_dai_probe, + .remove = msm_dai_q6_dai_remove, +}; + +static struct snd_soc_dai_driver msm_dai_q6_fm_tx_dai = { + .capture = { + .stream_name = "Internal FM Capture", + .aif_name = "INT_FM_TX", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 2, + .channels_max = 2, + .rate_max = 48000, + .rate_min = 8000, + }, + .ops = &msm_dai_q6_ops, + .id = INT_FM_TX, + .probe = msm_dai_q6_dai_probe, + .remove = msm_dai_q6_dai_remove, +}; + +static struct snd_soc_dai_driver msm_dai_q6_voc_playback_dai[] = { + { + .playback = { + .stream_name = "Voice Farend Playback", + .aif_name = "VOICE_PLAYBACK_TX", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_dai_q6_ops, + .id = VOICE_PLAYBACK_TX, + .probe = msm_dai_q6_dai_probe, + .remove = msm_dai_q6_dai_remove, + }, + { + .playback = { + .stream_name = "Voice2 Farend Playback", + .aif_name = "VOICE2_PLAYBACK_TX", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_dai_q6_ops, + .id = VOICE2_PLAYBACK_TX, + .probe = msm_dai_q6_dai_probe, + .remove = msm_dai_q6_dai_remove, + }, +}; + +static struct snd_soc_dai_driver msm_dai_q6_incall_record_dai[] = { + { + .capture = { + .stream_name = "Voice Uplink Capture", + .aif_name = "INCALL_RECORD_TX", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_dai_q6_ops, + .id = VOICE_RECORD_TX, + .probe = msm_dai_q6_dai_probe, + .remove = msm_dai_q6_dai_remove, + }, + { + .capture = { + .stream_name = "Voice Downlink Capture", + .aif_name = "INCALL_RECORD_RX", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_dai_q6_ops, + .id = VOICE_RECORD_RX, + .probe = msm_dai_q6_dai_probe, + .remove = msm_dai_q6_dai_remove, + }, +}; + +static struct snd_soc_dai_driver msm_dai_q6_usb_rx_dai = { + .playback = { + .stream_name = "USB Audio Playback", + .aif_name = "USB_AUDIO_RX", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 | + SNDRV_PCM_RATE_192000 | SNDRV_PCM_RATE_352800 | + SNDRV_PCM_RATE_384000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 8, + .rate_max = 384000, + .rate_min = 8000, + }, + .ops = &msm_dai_q6_ops, + .id = AFE_PORT_ID_USB_RX, + .probe = msm_dai_q6_dai_probe, + .remove = msm_dai_q6_dai_remove, +}; + +static struct snd_soc_dai_driver msm_dai_q6_usb_tx_dai = { + .capture = { + .stream_name = "USB Audio Capture", + .aif_name = "USB_AUDIO_TX", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 | + SNDRV_PCM_RATE_192000 | SNDRV_PCM_RATE_352800 | + SNDRV_PCM_RATE_384000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 8, + .rate_max = 384000, + .rate_min = 8000, + }, + .ops = &msm_dai_q6_ops, + .id = AFE_PORT_ID_USB_TX, + .probe = msm_dai_q6_dai_probe, + .remove = msm_dai_q6_dai_remove, +}; + +static int msm_auxpcm_dev_probe(struct platform_device *pdev) +{ + struct msm_dai_q6_auxpcm_dai_data *dai_data; + struct msm_dai_auxpcm_pdata *auxpcm_pdata; + uint32_t val_array[RATE_MAX_NUM_OF_AUX_PCM_RATES]; + uint32_t val = 0; + const char *intf_name; + int rc = 0, i = 0, len = 0; + const uint32_t *slot_mapping_array = NULL; + u32 array_length = 0; + + dai_data = kzalloc(sizeof(struct msm_dai_q6_auxpcm_dai_data), + GFP_KERNEL); + if (!dai_data) + return -ENOMEM; + + rc = of_property_read_u32(pdev->dev.of_node, + "qcom,msm-dai-is-island-supported", + &dai_data->is_island_dai); + if (rc) + dev_dbg(&pdev->dev, "island supported entry not found\n"); + + auxpcm_pdata = kzalloc(sizeof(struct msm_dai_auxpcm_pdata), + GFP_KERNEL); + + if (!auxpcm_pdata) { + dev_err(&pdev->dev, "Failed to allocate memory for platform data\n"); + goto fail_pdata_nomem; + } + + dev_dbg(&pdev->dev, "%s: dev %pK, dai_data %pK, auxpcm_pdata %pK\n", + __func__, &pdev->dev, dai_data, auxpcm_pdata); + + rc = of_property_read_u32_array(pdev->dev.of_node, + "qcom,msm-cpudai-auxpcm-mode", + val_array, RATE_MAX_NUM_OF_AUX_PCM_RATES); + if (rc) { + dev_err(&pdev->dev, "%s: qcom,msm-cpudai-auxpcm-mode missing in DT node\n", + __func__); + goto fail_invalid_dt; + } + auxpcm_pdata->mode_8k.mode = (u16)val_array[RATE_8KHZ]; + auxpcm_pdata->mode_16k.mode = (u16)val_array[RATE_16KHZ]; + + rc = of_property_read_u32_array(pdev->dev.of_node, + "qcom,msm-cpudai-auxpcm-sync", + val_array, RATE_MAX_NUM_OF_AUX_PCM_RATES); + if (rc) { + dev_err(&pdev->dev, "%s: qcom,msm-cpudai-auxpcm-sync missing in DT node\n", + __func__); + goto fail_invalid_dt; + } + auxpcm_pdata->mode_8k.sync = (u16)val_array[RATE_8KHZ]; + auxpcm_pdata->mode_16k.sync = (u16)val_array[RATE_16KHZ]; + + rc = of_property_read_u32_array(pdev->dev.of_node, + "qcom,msm-cpudai-auxpcm-frame", + val_array, RATE_MAX_NUM_OF_AUX_PCM_RATES); + + if (rc) { + dev_err(&pdev->dev, "%s: qcom,msm-cpudai-auxpcm-frame missing in DT node\n", + __func__); + goto fail_invalid_dt; + } + auxpcm_pdata->mode_8k.frame = (u16)val_array[RATE_8KHZ]; + auxpcm_pdata->mode_16k.frame = (u16)val_array[RATE_16KHZ]; + + rc = of_property_read_u32_array(pdev->dev.of_node, + "qcom,msm-cpudai-auxpcm-quant", + val_array, RATE_MAX_NUM_OF_AUX_PCM_RATES); + if (rc) { + dev_err(&pdev->dev, "%s: qcom,msm-cpudai-auxpcm-quant missing in DT node\n", + __func__); + goto fail_invalid_dt; + } + auxpcm_pdata->mode_8k.quant = (u16)val_array[RATE_8KHZ]; + auxpcm_pdata->mode_16k.quant = (u16)val_array[RATE_16KHZ]; + + rc = of_property_read_u32_array(pdev->dev.of_node, + "qcom,msm-cpudai-auxpcm-num-slots", + val_array, RATE_MAX_NUM_OF_AUX_PCM_RATES); + if (rc) { + dev_err(&pdev->dev, "%s: qcom,msm-cpudai-auxpcm-num-slots missing in DT node\n", + __func__); + goto fail_invalid_dt; + } + auxpcm_pdata->mode_8k.num_slots = (u16)val_array[RATE_8KHZ]; + + if (auxpcm_pdata->mode_8k.num_slots > + msm_dai_q6_max_num_slot(auxpcm_pdata->mode_8k.frame)) { + dev_err(&pdev->dev, "%s Max slots %d greater than DT node %d\n", + __func__, + msm_dai_q6_max_num_slot(auxpcm_pdata->mode_8k.frame), + auxpcm_pdata->mode_8k.num_slots); + rc = -EINVAL; + goto fail_invalid_dt; + } + auxpcm_pdata->mode_16k.num_slots = (u16)val_array[RATE_16KHZ]; + + if (auxpcm_pdata->mode_16k.num_slots > + msm_dai_q6_max_num_slot(auxpcm_pdata->mode_16k.frame)) { + dev_err(&pdev->dev, "%s Max slots %d greater than DT node %d\n", + __func__, + msm_dai_q6_max_num_slot(auxpcm_pdata->mode_16k.frame), + auxpcm_pdata->mode_16k.num_slots); + rc = -EINVAL; + goto fail_invalid_dt; + } + + slot_mapping_array = of_get_property(pdev->dev.of_node, + "qcom,msm-cpudai-auxpcm-slot-mapping", &len); + + if (slot_mapping_array == NULL) { + dev_err(&pdev->dev, "%s slot_mapping_array is not valid\n", + __func__); + rc = -EINVAL; + goto fail_invalid_dt; + } + + array_length = auxpcm_pdata->mode_8k.num_slots + + auxpcm_pdata->mode_16k.num_slots; + + if (len != sizeof(uint32_t) * array_length) { + dev_err(&pdev->dev, "%s Length is %d and expected is %zd\n", + __func__, len, sizeof(uint32_t) * array_length); + rc = -EINVAL; + goto fail_invalid_dt; + } + + auxpcm_pdata->mode_8k.slot_mapping = + kzalloc(sizeof(uint16_t) * + auxpcm_pdata->mode_8k.num_slots, + GFP_KERNEL); + if (!auxpcm_pdata->mode_8k.slot_mapping) { + dev_err(&pdev->dev, "%s No mem for mode_8k slot mapping\n", + __func__); + rc = -ENOMEM; + goto fail_invalid_dt; + } + + for (i = 0; i < auxpcm_pdata->mode_8k.num_slots; i++) + auxpcm_pdata->mode_8k.slot_mapping[i] = + (u16)be32_to_cpu(slot_mapping_array[i]); + + auxpcm_pdata->mode_16k.slot_mapping = + kzalloc(sizeof(uint16_t) * + auxpcm_pdata->mode_16k.num_slots, + GFP_KERNEL); + + if (!auxpcm_pdata->mode_16k.slot_mapping) { + dev_err(&pdev->dev, "%s No mem for mode_16k slot mapping\n", + __func__); + rc = -ENOMEM; + goto fail_invalid_16k_slot_mapping; + } + + for (i = 0; i < auxpcm_pdata->mode_16k.num_slots; i++) + auxpcm_pdata->mode_16k.slot_mapping[i] = + (u16)be32_to_cpu(slot_mapping_array[i + + auxpcm_pdata->mode_8k.num_slots]); + + rc = of_property_read_u32_array(pdev->dev.of_node, + "qcom,msm-cpudai-auxpcm-data", + val_array, RATE_MAX_NUM_OF_AUX_PCM_RATES); + if (rc) { + dev_err(&pdev->dev, "%s: qcom,msm-cpudai-auxpcm-data missing in DT node\n", + __func__); + goto fail_invalid_dt1; + } + auxpcm_pdata->mode_8k.data = (u16)val_array[RATE_8KHZ]; + auxpcm_pdata->mode_16k.data = (u16)val_array[RATE_16KHZ]; + + rc = of_property_read_u32_array(pdev->dev.of_node, + "qcom,msm-cpudai-auxpcm-pcm-clk-rate", + val_array, RATE_MAX_NUM_OF_AUX_PCM_RATES); + if (rc) { + dev_err(&pdev->dev, + "%s: qcom,msm-cpudai-auxpcm-pcm-clk-rate missing in DT\n", + __func__); + goto fail_invalid_dt1; + } + auxpcm_pdata->mode_8k.pcm_clk_rate = (int)val_array[RATE_8KHZ]; + auxpcm_pdata->mode_16k.pcm_clk_rate = (int)val_array[RATE_16KHZ]; + + rc = of_property_read_string(pdev->dev.of_node, + "qcom,msm-auxpcm-interface", &intf_name); + if (rc) { + dev_err(&pdev->dev, + "%s: qcom,msm-auxpcm-interface missing in DT node\n", + __func__); + goto fail_nodev_intf; + } + + if (!strcmp(intf_name, "primary")) { + dai_data->rx_pid = AFE_PORT_ID_PRIMARY_PCM_RX; + dai_data->tx_pid = AFE_PORT_ID_PRIMARY_PCM_TX; + pdev->id = MSM_DAI_PRI_AUXPCM_DT_DEV_ID; + i = 0; + } else if (!strcmp(intf_name, "secondary")) { + dai_data->rx_pid = AFE_PORT_ID_SECONDARY_PCM_RX; + dai_data->tx_pid = AFE_PORT_ID_SECONDARY_PCM_TX; + pdev->id = MSM_DAI_SEC_AUXPCM_DT_DEV_ID; + i = 1; + } else if (!strcmp(intf_name, "tertiary")) { + dai_data->rx_pid = AFE_PORT_ID_TERTIARY_PCM_RX; + dai_data->tx_pid = AFE_PORT_ID_TERTIARY_PCM_TX; + pdev->id = MSM_DAI_TERT_AUXPCM_DT_DEV_ID; + i = 2; + } else if (!strcmp(intf_name, "quaternary")) { + dai_data->rx_pid = AFE_PORT_ID_QUATERNARY_PCM_RX; + dai_data->tx_pid = AFE_PORT_ID_QUATERNARY_PCM_TX; + pdev->id = MSM_DAI_QUAT_AUXPCM_DT_DEV_ID; + i = 3; + } else if (!strcmp(intf_name, "quinary")) { + dai_data->rx_pid = AFE_PORT_ID_QUINARY_PCM_RX; + dai_data->tx_pid = AFE_PORT_ID_QUINARY_PCM_TX; + pdev->id = MSM_DAI_QUIN_AUXPCM_DT_DEV_ID; + i = 4; + } else { + dev_err(&pdev->dev, "%s: invalid DT intf name %s\n", + __func__, intf_name); + goto fail_invalid_intf; + } + rc = of_property_read_u32(pdev->dev.of_node, + "qcom,msm-cpudai-afe-clk-ver", &val); + if (rc) + dai_data->afe_clk_ver = AFE_CLK_VERSION_V1; + else + dai_data->afe_clk_ver = val; + + mutex_init(&dai_data->rlock); + dev_dbg(&pdev->dev, "dev name %s\n", dev_name(&pdev->dev)); + + dev_set_drvdata(&pdev->dev, dai_data); + pdev->dev.platform_data = (void *) auxpcm_pdata; + + rc = snd_soc_register_component(&pdev->dev, + &msm_dai_q6_aux_pcm_dai_component, + &msm_dai_q6_aux_pcm_dai[i], 1); + if (rc) { + dev_err(&pdev->dev, "%s: auxpcm dai reg failed, rc=%d\n", + __func__, rc); + goto fail_reg_dai; + } + + return rc; + +fail_reg_dai: +fail_invalid_intf: +fail_nodev_intf: +fail_invalid_dt1: + kfree(auxpcm_pdata->mode_16k.slot_mapping); +fail_invalid_16k_slot_mapping: + kfree(auxpcm_pdata->mode_8k.slot_mapping); +fail_invalid_dt: + kfree(auxpcm_pdata); +fail_pdata_nomem: + kfree(dai_data); + return rc; +} + +static int msm_auxpcm_dev_remove(struct platform_device *pdev) +{ + struct msm_dai_q6_auxpcm_dai_data *dai_data; + + dai_data = dev_get_drvdata(&pdev->dev); + + snd_soc_unregister_component(&pdev->dev); + + mutex_destroy(&dai_data->rlock); + kfree(dai_data); + kfree(pdev->dev.platform_data); + + return 0; +} + +static const struct of_device_id msm_auxpcm_dev_dt_match[] = { + { .compatible = "qcom,msm-auxpcm-dev", }, + {} +}; + + +static struct platform_driver msm_auxpcm_dev_driver = { + .probe = msm_auxpcm_dev_probe, + .remove = msm_auxpcm_dev_remove, + .driver = { + .name = "msm-auxpcm-dev", + .owner = THIS_MODULE, + .of_match_table = msm_auxpcm_dev_dt_match, + }, +}; + +static struct snd_soc_dai_driver msm_dai_q6_slimbus_rx_dai[] = { + { + .playback = { + .stream_name = "Slimbus Playback", + .aif_name = "SLIMBUS_0_RX", + .rates = SNDRV_PCM_RATE_8000_384000, + .formats = DAI_FORMATS_S16_S24_S32_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 384000, + }, + .ops = &msm_dai_q6_ops, + .id = SLIMBUS_0_RX, + .probe = msm_dai_q6_dai_probe, + .remove = msm_dai_q6_dai_remove, + }, + { + .playback = { + .stream_name = "Slimbus1 Playback", + .aif_name = "SLIMBUS_1_RX", + .rates = SNDRV_PCM_RATE_8000_384000, + .formats = DAI_FORMATS_S16_S24_S32_LE, + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 384000, + }, + .ops = &msm_dai_q6_ops, + .id = SLIMBUS_1_RX, + .probe = msm_dai_q6_dai_probe, + .remove = msm_dai_q6_dai_remove, + }, + { + .playback = { + .stream_name = "Slimbus2 Playback", + .aif_name = "SLIMBUS_2_RX", + .rates = SNDRV_PCM_RATE_8000_384000, + .formats = DAI_FORMATS_S16_S24_S32_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 384000, + }, + .ops = &msm_dai_q6_ops, + .id = SLIMBUS_2_RX, + .probe = msm_dai_q6_dai_probe, + .remove = msm_dai_q6_dai_remove, + }, + { + .playback = { + .stream_name = "Slimbus3 Playback", + .aif_name = "SLIMBUS_3_RX", + .rates = SNDRV_PCM_RATE_8000_384000, + .formats = DAI_FORMATS_S16_S24_S32_LE, + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 384000, + }, + .ops = &msm_dai_q6_ops, + .id = SLIMBUS_3_RX, + .probe = msm_dai_q6_dai_probe, + .remove = msm_dai_q6_dai_remove, + }, + { + .playback = { + .stream_name = "Slimbus4 Playback", + .aif_name = "SLIMBUS_4_RX", + .rates = SNDRV_PCM_RATE_8000_384000, + .formats = DAI_FORMATS_S16_S24_S32_LE, + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 384000, + }, + .ops = &msm_dai_q6_ops, + .id = SLIMBUS_4_RX, + .probe = msm_dai_q6_dai_probe, + .remove = msm_dai_q6_dai_remove, + }, + { + .playback = { + .stream_name = "Slimbus6 Playback", + .aif_name = "SLIMBUS_6_RX", + .rates = SNDRV_PCM_RATE_8000_384000, + .formats = DAI_FORMATS_S16_S24_S32_LE, + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 384000, + }, + .ops = &msm_dai_q6_ops, + .id = SLIMBUS_6_RX, + .probe = msm_dai_q6_dai_probe, + .remove = msm_dai_q6_dai_remove, + }, + { + .playback = { + .stream_name = "Slimbus5 Playback", + .aif_name = "SLIMBUS_5_RX", + .rates = SNDRV_PCM_RATE_8000_384000, + .formats = DAI_FORMATS_S16_S24_S32_LE, + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 384000, + }, + .ops = &msm_dai_q6_ops, + .id = SLIMBUS_5_RX, + .probe = msm_dai_q6_dai_probe, + .remove = msm_dai_q6_dai_remove, + }, + { + .playback = { + .stream_name = "Slimbus7 Playback", + .aif_name = "SLIMBUS_7_RX", + .rates = SNDRV_PCM_RATE_8000_384000, + .formats = DAI_FORMATS_S16_S24_S32_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 384000, + }, + .ops = &msm_dai_q6_ops, + .id = SLIMBUS_7_RX, + .probe = msm_dai_q6_dai_probe, + .remove = msm_dai_q6_dai_remove, + }, + { + .playback = { + .stream_name = "Slimbus8 Playback", + .aif_name = "SLIMBUS_8_RX", + .rates = SNDRV_PCM_RATE_8000_384000, + .formats = DAI_FORMATS_S16_S24_S32_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 384000, + }, + .ops = &msm_dai_q6_ops, + .id = SLIMBUS_8_RX, + .probe = msm_dai_q6_dai_probe, + .remove = msm_dai_q6_dai_remove, + }, + { + .playback = { + .stream_name = "Slimbus9 Playback", + .aif_name = "SLIMBUS_9_RX", + .rates = SNDRV_PCM_RATE_8000_384000, + .formats = DAI_FORMATS_S16_S24_S32_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 384000, + }, + .ops = &msm_dai_q6_ops, + .id = SLIMBUS_9_RX, + .probe = msm_dai_q6_dai_probe, + .remove = msm_dai_q6_dai_remove, + }, +}; + +static struct snd_soc_dai_driver msm_dai_q6_slimbus_tx_dai[] = { + { + .capture = { + .stream_name = "Slimbus Capture", + .aif_name = "SLIMBUS_0_TX", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 192000, + }, + .ops = &msm_dai_q6_ops, + .id = SLIMBUS_0_TX, + .probe = msm_dai_q6_dai_probe, + .remove = msm_dai_q6_dai_remove, + }, + { + .capture = { + .stream_name = "Slimbus1 Capture", + .aif_name = "SLIMBUS_1_TX", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE, + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 192000, + }, + .ops = &msm_dai_q6_ops, + .id = SLIMBUS_1_TX, + .probe = msm_dai_q6_dai_probe, + .remove = msm_dai_q6_dai_remove, + }, + { + .capture = { + .stream_name = "Slimbus2 Capture", + .aif_name = "SLIMBUS_2_TX", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 192000, + }, + .ops = &msm_dai_q6_ops, + .id = SLIMBUS_2_TX, + .probe = msm_dai_q6_dai_probe, + .remove = msm_dai_q6_dai_remove, + }, + { + .capture = { + .stream_name = "Slimbus3 Capture", + .aif_name = "SLIMBUS_3_TX", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .channels_min = 2, + .channels_max = 4, + .rate_min = 8000, + .rate_max = 192000, + }, + .ops = &msm_dai_q6_ops, + .id = SLIMBUS_3_TX, + .probe = msm_dai_q6_dai_probe, + .remove = msm_dai_q6_dai_remove, + }, + { + .capture = { + .stream_name = "Slimbus4 Capture", + .aif_name = "SLIMBUS_4_TX", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 2, + .channels_max = 4, + .rate_min = 8000, + .rate_max = 192000, + }, + .ops = &msm_dai_q6_ops, + .id = SLIMBUS_4_TX, + .probe = msm_dai_q6_dai_probe, + .remove = msm_dai_q6_dai_remove, + }, + { + .capture = { + .stream_name = "Slimbus5 Capture", + .aif_name = "SLIMBUS_5_TX", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 192000, + }, + .ops = &msm_dai_q6_ops, + .id = SLIMBUS_5_TX, + .probe = msm_dai_q6_dai_probe, + .remove = msm_dai_q6_dai_remove, + }, + { + .capture = { + .stream_name = "Slimbus6 Capture", + .aif_name = "SLIMBUS_6_TX", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 192000, + }, + .ops = &msm_dai_q6_ops, + .id = SLIMBUS_6_TX, + .probe = msm_dai_q6_dai_probe, + .remove = msm_dai_q6_dai_remove, + }, + { + .capture = { + .stream_name = "Slimbus7 Capture", + .aif_name = "SLIMBUS_7_TX", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 192000, + }, + .ops = &msm_dai_q6_ops, + .id = SLIMBUS_7_TX, + .probe = msm_dai_q6_dai_probe, + .remove = msm_dai_q6_dai_remove, + }, + { + .capture = { + .stream_name = "Slimbus8 Capture", + .aif_name = "SLIMBUS_8_TX", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 192000, + }, + .ops = &msm_dai_q6_ops, + .id = SLIMBUS_8_TX, + .probe = msm_dai_q6_dai_probe, + .remove = msm_dai_q6_dai_remove, + }, + { + .capture = { + .stream_name = "Slimbus9 Capture", + .aif_name = "SLIMBUS_9_TX", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 192000, + }, + .ops = &msm_dai_q6_ops, + .id = SLIMBUS_9_TX, + .probe = msm_dai_q6_dai_probe, + .remove = msm_dai_q6_dai_remove, + }, +}; + +static int msm_dai_q6_mi2s_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct msm_dai_q6_dai_data *dai_data = kcontrol->private_data; + int value = ucontrol->value.integer.value[0]; + + dai_data->port_config.i2s.data_format = value; + pr_debug("%s: value = %d, channel = %d, line = %d\n", + __func__, value, dai_data->port_config.i2s.mono_stereo, + dai_data->port_config.i2s.channel_mode); + return 0; +} + +static int msm_dai_q6_mi2s_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct msm_dai_q6_dai_data *dai_data = kcontrol->private_data; + + ucontrol->value.integer.value[0] = + dai_data->port_config.i2s.data_format; + return 0; +} + +static int msm_dai_q6_mi2s_vi_feed_mono_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct msm_dai_q6_dai_data *dai_data = kcontrol->private_data; + int value = ucontrol->value.integer.value[0]; + + dai_data->vi_feed_mono = value; + pr_debug("%s: value = %d\n", __func__, value); + return 0; +} + +static int msm_dai_q6_mi2s_vi_feed_mono_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct msm_dai_q6_dai_data *dai_data = kcontrol->private_data; + + ucontrol->value.integer.value[0] = dai_data->vi_feed_mono; + return 0; +} + +static const struct snd_kcontrol_new mi2s_config_controls[] = { + SOC_ENUM_EXT("PRI MI2S RX Format", mi2s_config_enum[0], + msm_dai_q6_mi2s_format_get, + msm_dai_q6_mi2s_format_put), + SOC_ENUM_EXT("SEC MI2S RX Format", mi2s_config_enum[0], + msm_dai_q6_mi2s_format_get, + msm_dai_q6_mi2s_format_put), + SOC_ENUM_EXT("TERT MI2S RX Format", mi2s_config_enum[0], + msm_dai_q6_mi2s_format_get, + msm_dai_q6_mi2s_format_put), + SOC_ENUM_EXT("QUAT MI2S RX Format", mi2s_config_enum[0], + msm_dai_q6_mi2s_format_get, + msm_dai_q6_mi2s_format_put), + SOC_ENUM_EXT("QUIN MI2S RX Format", mi2s_config_enum[0], + msm_dai_q6_mi2s_format_get, + msm_dai_q6_mi2s_format_put), + SOC_ENUM_EXT("PRI MI2S TX Format", mi2s_config_enum[0], + msm_dai_q6_mi2s_format_get, + msm_dai_q6_mi2s_format_put), + SOC_ENUM_EXT("SEC MI2S TX Format", mi2s_config_enum[0], + msm_dai_q6_mi2s_format_get, + msm_dai_q6_mi2s_format_put), + SOC_ENUM_EXT("TERT MI2S TX Format", mi2s_config_enum[0], + msm_dai_q6_mi2s_format_get, + msm_dai_q6_mi2s_format_put), + SOC_ENUM_EXT("QUAT MI2S TX Format", mi2s_config_enum[0], + msm_dai_q6_mi2s_format_get, + msm_dai_q6_mi2s_format_put), + SOC_ENUM_EXT("QUIN MI2S TX Format", mi2s_config_enum[0], + msm_dai_q6_mi2s_format_get, + msm_dai_q6_mi2s_format_put), + SOC_ENUM_EXT("SENARY MI2S TX Format", mi2s_config_enum[0], + msm_dai_q6_mi2s_format_get, + msm_dai_q6_mi2s_format_put), + SOC_ENUM_EXT("INT5 MI2S TX Format", mi2s_config_enum[0], + msm_dai_q6_mi2s_format_get, + msm_dai_q6_mi2s_format_put), +}; + +static const struct snd_kcontrol_new mi2s_vi_feed_controls[] = { + SOC_ENUM_EXT("INT5 MI2S VI MONO", mi2s_config_enum[1], + msm_dai_q6_mi2s_vi_feed_mono_get, + msm_dai_q6_mi2s_vi_feed_mono_put), +}; + +static int msm_dai_q6_dai_mi2s_probe(struct snd_soc_dai *dai) +{ + struct msm_dai_q6_mi2s_dai_data *mi2s_dai_data = + dev_get_drvdata(dai->dev); + struct msm_mi2s_pdata *mi2s_pdata = + (struct msm_mi2s_pdata *) dai->dev->platform_data; + struct snd_kcontrol *kcontrol = NULL; + int rc = 0; + const struct snd_kcontrol_new *ctrl = NULL; + const struct snd_kcontrol_new *vi_feed_ctrl = NULL; + u16 dai_id = 0; + + dai->id = mi2s_pdata->intf_id; + + if (mi2s_dai_data->rx_dai.mi2s_dai_data.port_config.i2s.channel_mode) { + if (dai->id == MSM_PRIM_MI2S) + ctrl = &mi2s_config_controls[0]; + if (dai->id == MSM_SEC_MI2S) + ctrl = &mi2s_config_controls[1]; + if (dai->id == MSM_TERT_MI2S) + ctrl = &mi2s_config_controls[2]; + if (dai->id == MSM_QUAT_MI2S) + ctrl = &mi2s_config_controls[3]; + if (dai->id == MSM_QUIN_MI2S) + ctrl = &mi2s_config_controls[4]; + } + + if (ctrl) { + kcontrol = snd_ctl_new1(ctrl, + &mi2s_dai_data->rx_dai.mi2s_dai_data); + rc = snd_ctl_add(dai->component->card->snd_card, kcontrol); + if (rc < 0) { + dev_err(dai->dev, "%s: err add RX fmt ctl DAI = %s\n", + __func__, dai->name); + goto rtn; + } + } + + ctrl = NULL; + if (mi2s_dai_data->tx_dai.mi2s_dai_data.port_config.i2s.channel_mode) { + if (dai->id == MSM_PRIM_MI2S) + ctrl = &mi2s_config_controls[5]; + if (dai->id == MSM_SEC_MI2S) + ctrl = &mi2s_config_controls[6]; + if (dai->id == MSM_TERT_MI2S) + ctrl = &mi2s_config_controls[7]; + if (dai->id == MSM_QUAT_MI2S) + ctrl = &mi2s_config_controls[8]; + if (dai->id == MSM_QUIN_MI2S) + ctrl = &mi2s_config_controls[9]; + if (dai->id == MSM_SENARY_MI2S) + ctrl = &mi2s_config_controls[10]; + if (dai->id == MSM_INT5_MI2S) + ctrl = &mi2s_config_controls[11]; + } + + if (ctrl) { + rc = snd_ctl_add(dai->component->card->snd_card, + snd_ctl_new1(ctrl, + &mi2s_dai_data->tx_dai.mi2s_dai_data)); + if (rc < 0) { + if (kcontrol) + snd_ctl_remove(dai->component->card->snd_card, + kcontrol); + dev_err(dai->dev, "%s: err add TX fmt ctl DAI = %s\n", + __func__, dai->name); + } + } + + if (dai->id == MSM_INT5_MI2S) + vi_feed_ctrl = &mi2s_vi_feed_controls[0]; + + if (vi_feed_ctrl) { + rc = snd_ctl_add(dai->component->card->snd_card, + snd_ctl_new1(vi_feed_ctrl, + &mi2s_dai_data->tx_dai.mi2s_dai_data)); + + if (rc < 0) { + dev_err(dai->dev, "%s: err add TX vi feed channel ctl DAI = %s\n", + __func__, dai->name); + } + } + + if (mi2s_dai_data->is_island_dai) { + msm_mi2s_get_port_id(dai->id, SNDRV_PCM_STREAM_CAPTURE, + &dai_id); + rc = msm_dai_q6_add_island_mx_ctls( + dai->component->card->snd_card, + dai->name, dai_id, + (void *)mi2s_dai_data); + } + + rc = msm_dai_q6_dai_add_route(dai); +rtn: + return rc; +} + + +static int msm_dai_q6_dai_mi2s_remove(struct snd_soc_dai *dai) +{ + struct msm_dai_q6_mi2s_dai_data *mi2s_dai_data = + dev_get_drvdata(dai->dev); + int rc; + + /* If AFE port is still up, close it */ + if (test_bit(STATUS_PORT_STARTED, + mi2s_dai_data->rx_dai.mi2s_dai_data.status_mask)) { + rc = afe_close(MI2S_RX); /* can block */ + if (rc < 0) + dev_err(dai->dev, "fail to close MI2S_RX port\n"); + clear_bit(STATUS_PORT_STARTED, + mi2s_dai_data->rx_dai.mi2s_dai_data.status_mask); + } + if (test_bit(STATUS_PORT_STARTED, + mi2s_dai_data->tx_dai.mi2s_dai_data.status_mask)) { + rc = afe_close(MI2S_TX); /* can block */ + if (rc < 0) + dev_err(dai->dev, "fail to close MI2S_TX port\n"); + clear_bit(STATUS_PORT_STARTED, + mi2s_dai_data->tx_dai.mi2s_dai_data.status_mask); + } + return 0; +} + +static int msm_dai_q6_mi2s_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + + return 0; +} + +static int msm_mi2s_get_port_id(u32 mi2s_id, int stream, u16 *port_id) +{ + int ret = 0; + + switch (stream) { + case SNDRV_PCM_STREAM_PLAYBACK: + switch (mi2s_id) { + case MSM_PRIM_MI2S: + *port_id = AFE_PORT_ID_PRIMARY_MI2S_RX; + break; + case MSM_SEC_MI2S: + *port_id = AFE_PORT_ID_SECONDARY_MI2S_RX; + break; + case MSM_TERT_MI2S: + *port_id = AFE_PORT_ID_TERTIARY_MI2S_RX; + break; + case MSM_QUAT_MI2S: + *port_id = AFE_PORT_ID_QUATERNARY_MI2S_RX; + break; + case MSM_SEC_MI2S_SD1: + *port_id = AFE_PORT_ID_SECONDARY_MI2S_RX_SD1; + break; + case MSM_QUIN_MI2S: + *port_id = AFE_PORT_ID_QUINARY_MI2S_RX; + break; + case MSM_INT0_MI2S: + *port_id = AFE_PORT_ID_INT0_MI2S_RX; + break; + case MSM_INT1_MI2S: + *port_id = AFE_PORT_ID_INT1_MI2S_RX; + break; + case MSM_INT2_MI2S: + *port_id = AFE_PORT_ID_INT2_MI2S_RX; + break; + case MSM_INT3_MI2S: + *port_id = AFE_PORT_ID_INT3_MI2S_RX; + break; + case MSM_INT4_MI2S: + *port_id = AFE_PORT_ID_INT4_MI2S_RX; + break; + case MSM_INT5_MI2S: + *port_id = AFE_PORT_ID_INT5_MI2S_RX; + break; + case MSM_INT6_MI2S: + *port_id = AFE_PORT_ID_INT6_MI2S_RX; + break; + default: + pr_err("%s: playback err id 0x%x\n", + __func__, mi2s_id); + ret = -1; + break; + } + break; + case SNDRV_PCM_STREAM_CAPTURE: + switch (mi2s_id) { + case MSM_PRIM_MI2S: + *port_id = AFE_PORT_ID_PRIMARY_MI2S_TX; + break; + case MSM_SEC_MI2S: + *port_id = AFE_PORT_ID_SECONDARY_MI2S_TX; + break; + case MSM_TERT_MI2S: + *port_id = AFE_PORT_ID_TERTIARY_MI2S_TX; + break; + case MSM_QUAT_MI2S: + *port_id = AFE_PORT_ID_QUATERNARY_MI2S_TX; + break; + case MSM_QUIN_MI2S: + *port_id = AFE_PORT_ID_QUINARY_MI2S_TX; + break; + case MSM_SENARY_MI2S: + *port_id = AFE_PORT_ID_SENARY_MI2S_TX; + break; + case MSM_INT0_MI2S: + *port_id = AFE_PORT_ID_INT0_MI2S_TX; + break; + case MSM_INT1_MI2S: + *port_id = AFE_PORT_ID_INT1_MI2S_TX; + break; + case MSM_INT2_MI2S: + *port_id = AFE_PORT_ID_INT2_MI2S_TX; + break; + case MSM_INT3_MI2S: + *port_id = AFE_PORT_ID_INT3_MI2S_TX; + break; + case MSM_INT4_MI2S: + *port_id = AFE_PORT_ID_INT4_MI2S_TX; + break; + case MSM_INT5_MI2S: + *port_id = AFE_PORT_ID_INT5_MI2S_TX; + break; + case MSM_INT6_MI2S: + *port_id = AFE_PORT_ID_INT6_MI2S_TX; + break; + default: + pr_err("%s: capture err id 0x%x\n", __func__, mi2s_id); + ret = -1; + break; + } + break; + default: + pr_err("%s: default err %d\n", __func__, stream); + ret = -1; + break; + } + pr_debug("%s: port_id = 0x%x\n", __func__, *port_id); + return ret; +} + +static int msm_dai_q6_mi2s_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct msm_dai_q6_mi2s_dai_data *mi2s_dai_data = + dev_get_drvdata(dai->dev); + struct msm_dai_q6_dai_data *dai_data = + (substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? + &mi2s_dai_data->rx_dai.mi2s_dai_data : + &mi2s_dai_data->tx_dai.mi2s_dai_data); + u16 port_id = 0; + int rc = 0; + + if (msm_mi2s_get_port_id(dai->id, substream->stream, + &port_id) != 0) { + dev_err(dai->dev, "%s: Invalid Port ID 0x%x\n", + __func__, port_id); + return -EINVAL; + } + + dev_dbg(dai->dev, "%s: dai id %d, afe port id = 0x%x\n" + "dai_data->channels = %u sample_rate = %u\n", __func__, + dai->id, port_id, dai_data->channels, dai_data->rate); + + if (!test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) { + if (q6core_get_avcs_api_version_per_service( + APRV2_IDS_SERVICE_ID_ADSP_AFE_V) >= AFE_API_VERSION_V4) { + /* + * send island mode config. + * This should be the first configuration + */ + rc = afe_send_port_island_mode(port_id); + if (rc) + dev_err(dai->dev, "%s: afe send island mode failed %d\n", + __func__, rc); + } + + /* PORT START should be set if prepare called + * in active state. + */ + rc = afe_port_start(port_id, &dai_data->port_config, + dai_data->rate); + if (rc < 0) + dev_err(dai->dev, "fail to open AFE port 0x%x\n", + dai->id); + else + set_bit(STATUS_PORT_STARTED, + dai_data->status_mask); + } + if (!test_bit(STATUS_PORT_STARTED, dai_data->hwfree_status)) { + set_bit(STATUS_PORT_STARTED, dai_data->hwfree_status); + dev_dbg(dai->dev, "%s: set hwfree_status to started\n", + __func__); + } + return rc; +} + +static int msm_dai_q6_mi2s_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct msm_dai_q6_mi2s_dai_data *mi2s_dai_data = + dev_get_drvdata(dai->dev); + struct msm_dai_q6_mi2s_dai_config *mi2s_dai_config = + (substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? + &mi2s_dai_data->rx_dai : &mi2s_dai_data->tx_dai); + struct msm_dai_q6_dai_data *dai_data = &mi2s_dai_config->mi2s_dai_data; + struct afe_param_id_i2s_cfg *i2s = &dai_data->port_config.i2s; + + dai_data->channels = params_channels(params); + switch (dai_data->channels) { + case 15: + case 16: + switch (mi2s_dai_config->pdata_mi2s_lines) { + case AFE_PORT_I2S_16CHS: + dai_data->port_config.i2s.channel_mode + = AFE_PORT_I2S_16CHS; + break; + default: + goto error_invalid_data; + }; + break; + case 13: + case 14: + switch (mi2s_dai_config->pdata_mi2s_lines) { + case AFE_PORT_I2S_14CHS: + case AFE_PORT_I2S_16CHS: + dai_data->port_config.i2s.channel_mode + = AFE_PORT_I2S_14CHS; + break; + default: + goto error_invalid_data; + }; + break; + case 11: + case 12: + switch (mi2s_dai_config->pdata_mi2s_lines) { + case AFE_PORT_I2S_12CHS: + case AFE_PORT_I2S_14CHS: + case AFE_PORT_I2S_16CHS: + dai_data->port_config.i2s.channel_mode + = AFE_PORT_I2S_12CHS; + break; + default: + goto error_invalid_data; + }; + break; + case 9: + case 10: + switch (mi2s_dai_config->pdata_mi2s_lines) { + case AFE_PORT_I2S_10CHS: + case AFE_PORT_I2S_12CHS: + case AFE_PORT_I2S_14CHS: + case AFE_PORT_I2S_16CHS: + dai_data->port_config.i2s.channel_mode + = AFE_PORT_I2S_10CHS; + break; + default: + goto error_invalid_data; + }; + break; + case 8: + case 7: + if (mi2s_dai_config->pdata_mi2s_lines < AFE_PORT_I2S_8CHS) + goto error_invalid_data; + else + if (mi2s_dai_config->pdata_mi2s_lines + == AFE_PORT_I2S_8CHS_2) + dai_data->port_config.i2s.channel_mode = + AFE_PORT_I2S_8CHS_2; + else + dai_data->port_config.i2s.channel_mode = + AFE_PORT_I2S_8CHS; + break; + case 6: + case 5: + if (mi2s_dai_config->pdata_mi2s_lines < AFE_PORT_I2S_6CHS) + goto error_invalid_data; + dai_data->port_config.i2s.channel_mode = AFE_PORT_I2S_6CHS; + break; + case 4: + case 3: + switch (mi2s_dai_config->pdata_mi2s_lines) { + case AFE_PORT_I2S_SD0: + case AFE_PORT_I2S_SD1: + case AFE_PORT_I2S_SD2: + case AFE_PORT_I2S_SD3: + case AFE_PORT_I2S_SD4: + case AFE_PORT_I2S_SD5: + case AFE_PORT_I2S_SD6: + case AFE_PORT_I2S_SD7: + goto error_invalid_data; + break; + case AFE_PORT_I2S_QUAD01: + case AFE_PORT_I2S_QUAD23: + case AFE_PORT_I2S_QUAD45: + case AFE_PORT_I2S_QUAD67: + dai_data->port_config.i2s.channel_mode = + mi2s_dai_config->pdata_mi2s_lines; + break; + case AFE_PORT_I2S_8CHS_2: + dai_data->port_config.i2s.channel_mode = + AFE_PORT_I2S_QUAD45; + break; + default: + dai_data->port_config.i2s.channel_mode = + AFE_PORT_I2S_QUAD01; + break; + }; + break; + case 2: + case 1: + if (mi2s_dai_config->pdata_mi2s_lines < AFE_PORT_I2S_SD0) + goto error_invalid_data; + switch (mi2s_dai_config->pdata_mi2s_lines) { + case AFE_PORT_I2S_SD0: + case AFE_PORT_I2S_SD1: + case AFE_PORT_I2S_SD2: + case AFE_PORT_I2S_SD3: + case AFE_PORT_I2S_SD4: + case AFE_PORT_I2S_SD5: + case AFE_PORT_I2S_SD6: + case AFE_PORT_I2S_SD7: + dai_data->port_config.i2s.channel_mode = + mi2s_dai_config->pdata_mi2s_lines; + break; + case AFE_PORT_I2S_QUAD01: + case AFE_PORT_I2S_6CHS: + case AFE_PORT_I2S_8CHS: + case AFE_PORT_I2S_10CHS: + case AFE_PORT_I2S_12CHS: + case AFE_PORT_I2S_14CHS: + case AFE_PORT_I2S_16CHS: + if (dai_data->vi_feed_mono == SPKR_1) + dai_data->port_config.i2s.channel_mode = + AFE_PORT_I2S_SD0; + else + dai_data->port_config.i2s.channel_mode = + AFE_PORT_I2S_SD1; + break; + case AFE_PORT_I2S_QUAD23: + dai_data->port_config.i2s.channel_mode = + AFE_PORT_I2S_SD2; + break; + case AFE_PORT_I2S_QUAD45: + dai_data->port_config.i2s.channel_mode = + AFE_PORT_I2S_SD4; + break; + case AFE_PORT_I2S_QUAD67: + dai_data->port_config.i2s.channel_mode = + AFE_PORT_I2S_SD6; + break; + } + if (dai_data->channels == 2) + dai_data->port_config.i2s.mono_stereo = + MSM_AFE_CH_STEREO; + else + dai_data->port_config.i2s.mono_stereo = MSM_AFE_MONO; + break; + default: + pr_err("%s: default err channels %d\n", + __func__, dai_data->channels); + goto error_invalid_data; + } + dai_data->rate = params_rate(params); + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + case SNDRV_PCM_FORMAT_SPECIAL: + dai_data->port_config.i2s.bit_width = 16; + dai_data->bitwidth = 16; + break; + case SNDRV_PCM_FORMAT_S24_LE: + case SNDRV_PCM_FORMAT_S24_3LE: + dai_data->port_config.i2s.bit_width = 24; + dai_data->bitwidth = 24; + break; + default: + pr_err("%s: format %d\n", + __func__, params_format(params)); + return -EINVAL; + } + + dai_data->port_config.i2s.i2s_cfg_minor_version = + AFE_API_VERSION_I2S_CONFIG; + dai_data->port_config.i2s.sample_rate = dai_data->rate; + if ((test_bit(STATUS_PORT_STARTED, + mi2s_dai_data->rx_dai.mi2s_dai_data.status_mask) && + test_bit(STATUS_PORT_STARTED, + mi2s_dai_data->rx_dai.mi2s_dai_data.hwfree_status)) || + (test_bit(STATUS_PORT_STARTED, + mi2s_dai_data->tx_dai.mi2s_dai_data.status_mask) && + test_bit(STATUS_PORT_STARTED, + mi2s_dai_data->tx_dai.mi2s_dai_data.hwfree_status))) { + if ((mi2s_dai_data->tx_dai.mi2s_dai_data.rate != + mi2s_dai_data->rx_dai.mi2s_dai_data.rate) || + (mi2s_dai_data->rx_dai.mi2s_dai_data.bitwidth != + mi2s_dai_data->tx_dai.mi2s_dai_data.bitwidth)) { + dev_err(dai->dev, "%s: Error mismatch in HW params\n" + "Tx sample_rate = %u bit_width = %hu\n" + "Rx sample_rate = %u bit_width = %hu\n" + , __func__, + mi2s_dai_data->tx_dai.mi2s_dai_data.rate, + mi2s_dai_data->tx_dai.mi2s_dai_data.bitwidth, + mi2s_dai_data->rx_dai.mi2s_dai_data.rate, + mi2s_dai_data->rx_dai.mi2s_dai_data.bitwidth); + return -EINVAL; + } + } + dev_dbg(dai->dev, "%s: dai id %d dai_data->channels = %d\n" + "sample_rate = %u i2s_cfg_minor_version = 0x%x\n" + "bit_width = %hu channel_mode = 0x%x mono_stereo = %#x\n" + "ws_src = 0x%x sample_rate = %u data_format = 0x%x\n" + "reserved = %u\n", __func__, dai->id, dai_data->channels, + dai_data->rate, i2s->i2s_cfg_minor_version, i2s->bit_width, + i2s->channel_mode, i2s->mono_stereo, i2s->ws_src, + i2s->sample_rate, i2s->data_format, i2s->reserved); + + return 0; + +error_invalid_data: + pr_err("%s: dai_data->channels = %d channel_mode = %d\n", __func__, + dai_data->channels, dai_data->port_config.i2s.channel_mode); + return -EINVAL; +} + + +static int msm_dai_q6_mi2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct msm_dai_q6_mi2s_dai_data *mi2s_dai_data = + dev_get_drvdata(dai->dev); + + if (test_bit(STATUS_PORT_STARTED, + mi2s_dai_data->rx_dai.mi2s_dai_data.status_mask) || + test_bit(STATUS_PORT_STARTED, + mi2s_dai_data->tx_dai.mi2s_dai_data.status_mask)) { + dev_err(dai->dev, "%s: err chg i2s mode while dai running", + __func__); + return -EPERM; + } + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + mi2s_dai_data->rx_dai.mi2s_dai_data.port_config.i2s.ws_src = 1; + mi2s_dai_data->tx_dai.mi2s_dai_data.port_config.i2s.ws_src = 1; + break; + case SND_SOC_DAIFMT_CBM_CFM: + mi2s_dai_data->rx_dai.mi2s_dai_data.port_config.i2s.ws_src = 0; + mi2s_dai_data->tx_dai.mi2s_dai_data.port_config.i2s.ws_src = 0; + break; + default: + pr_err("%s: fmt %d\n", + __func__, fmt & SND_SOC_DAIFMT_MASTER_MASK); + return -EINVAL; + } + + return 0; +} + +static int msm_dai_q6_mi2s_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct msm_dai_q6_mi2s_dai_data *mi2s_dai_data = + dev_get_drvdata(dai->dev); + struct msm_dai_q6_dai_data *dai_data = + (substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? + &mi2s_dai_data->rx_dai.mi2s_dai_data : + &mi2s_dai_data->tx_dai.mi2s_dai_data); + + if (test_bit(STATUS_PORT_STARTED, dai_data->hwfree_status)) { + clear_bit(STATUS_PORT_STARTED, dai_data->hwfree_status); + dev_dbg(dai->dev, "%s: clear hwfree_status\n", __func__); + } + return 0; +} + +static void msm_dai_q6_mi2s_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct msm_dai_q6_mi2s_dai_data *mi2s_dai_data = + dev_get_drvdata(dai->dev); + struct msm_dai_q6_dai_data *dai_data = + (substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? + &mi2s_dai_data->rx_dai.mi2s_dai_data : + &mi2s_dai_data->tx_dai.mi2s_dai_data); + u16 port_id = 0; + int rc = 0; + + if (msm_mi2s_get_port_id(dai->id, substream->stream, + &port_id) != 0) { + dev_err(dai->dev, "%s: Invalid Port ID 0x%x\n", + __func__, port_id); + } + + dev_dbg(dai->dev, "%s: closing afe port id = 0x%x\n", + __func__, port_id); + + if (test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) { + rc = afe_close(port_id); + if (rc < 0) + dev_err(dai->dev, "fail to close AFE port\n"); + clear_bit(STATUS_PORT_STARTED, dai_data->status_mask); + } + if (test_bit(STATUS_PORT_STARTED, dai_data->hwfree_status)) + clear_bit(STATUS_PORT_STARTED, dai_data->hwfree_status); +} + +static struct snd_soc_dai_ops msm_dai_q6_mi2s_ops = { + .startup = msm_dai_q6_mi2s_startup, + .prepare = msm_dai_q6_mi2s_prepare, + .hw_params = msm_dai_q6_mi2s_hw_params, + .hw_free = msm_dai_q6_mi2s_hw_free, + .set_fmt = msm_dai_q6_mi2s_set_fmt, + .shutdown = msm_dai_q6_mi2s_shutdown, +}; + +/* Channel min and max are initialized base on platform data */ +static struct snd_soc_dai_driver msm_dai_q6_mi2s_dai[] = { + { + .playback = { + .stream_name = "Primary MI2S Playback", + .aif_name = "PRI_MI2S_RX", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 | + SNDRV_PCM_RATE_192000 | SNDRV_PCM_RATE_352800 | + SNDRV_PCM_RATE_384000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE, + .rate_min = 8000, + .rate_max = 384000, + }, + .capture = { + .stream_name = "Primary MI2S Capture", + .aif_name = "PRI_MI2S_TX", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rate_min = 8000, + .rate_max = 192000, + }, + .ops = &msm_dai_q6_mi2s_ops, + .name = "Primary MI2S", + .id = MSM_PRIM_MI2S, + .probe = msm_dai_q6_dai_mi2s_probe, + .remove = msm_dai_q6_dai_mi2s_remove, + }, + { + .playback = { + .stream_name = "Secondary MI2S Playback", + .aif_name = "SEC_MI2S_RX", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rate_min = 8000, + .rate_max = 192000, + }, + .capture = { + .stream_name = "Secondary MI2S Capture", + .aif_name = "SEC_MI2S_TX", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rate_min = 8000, + .rate_max = 192000, + }, + .ops = &msm_dai_q6_mi2s_ops, + .name = "Secondary MI2S", + .id = MSM_SEC_MI2S, + .probe = msm_dai_q6_dai_mi2s_probe, + .remove = msm_dai_q6_dai_mi2s_remove, + }, + { + .playback = { + .stream_name = "Tertiary MI2S Playback", + .aif_name = "TERT_MI2S_RX", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rate_min = 8000, + .rate_max = 192000, + }, + .capture = { + .stream_name = "Tertiary MI2S Capture", + .aif_name = "TERT_MI2S_TX", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rate_min = 8000, + .rate_max = 192000, + }, + .ops = &msm_dai_q6_mi2s_ops, + .name = "Tertiary MI2S", + .id = MSM_TERT_MI2S, + .probe = msm_dai_q6_dai_mi2s_probe, + .remove = msm_dai_q6_dai_mi2s_remove, + }, + { + .playback = { + .stream_name = "Quaternary MI2S Playback", + .aif_name = "QUAT_MI2S_RX", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rate_min = 8000, + .rate_max = 192000, + }, + .capture = { + .stream_name = "Quaternary MI2S Capture", + .aif_name = "QUAT_MI2S_TX", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rate_min = 8000, + .rate_max = 192000, + }, + .ops = &msm_dai_q6_mi2s_ops, + .name = "Quaternary MI2S", + .id = MSM_QUAT_MI2S, + .probe = msm_dai_q6_dai_mi2s_probe, + .remove = msm_dai_q6_dai_mi2s_remove, + }, + { + .playback = { + .stream_name = "Quinary MI2S Playback", + .aif_name = "QUIN_MI2S_RX", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rate_min = 8000, + .rate_max = 192000, + }, + .capture = { + .stream_name = "Quinary MI2S Capture", + .aif_name = "QUIN_MI2S_TX", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_dai_q6_mi2s_ops, + .name = "Quinary MI2S", + .id = MSM_QUIN_MI2S, + .probe = msm_dai_q6_dai_mi2s_probe, + .remove = msm_dai_q6_dai_mi2s_remove, + }, + { + .playback = { + .stream_name = "Secondary MI2S Playback SD1", + .aif_name = "SEC_MI2S_RX_SD1", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rate_min = 8000, + .rate_max = 48000, + }, + .id = MSM_SEC_MI2S_SD1, + }, + { + .capture = { + .stream_name = "Senary_mi2s Capture", + .aif_name = "SENARY_TX", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_dai_q6_mi2s_ops, + .name = "Senary MI2S", + .id = MSM_SENARY_MI2S, + .probe = msm_dai_q6_dai_mi2s_probe, + .remove = msm_dai_q6_dai_mi2s_remove, + }, + { + .playback = { + .stream_name = "INT0 MI2S Playback", + .aif_name = "INT0_MI2S_RX", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE, + .rate_min = 8000, + .rate_max = 192000, + }, + .capture = { + .stream_name = "INT0 MI2S Capture", + .aif_name = "INT0_MI2S_TX", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_dai_q6_mi2s_ops, + .name = "INT0 MI2S", + .id = MSM_INT0_MI2S, + .probe = msm_dai_q6_dai_mi2s_probe, + .remove = msm_dai_q6_dai_mi2s_remove, + }, + { + .playback = { + .stream_name = "INT1 MI2S Playback", + .aif_name = "INT1_MI2S_RX", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE, + .rate_min = 8000, + .rate_max = 48000, + }, + .capture = { + .stream_name = "INT1 MI2S Capture", + .aif_name = "INT1_MI2S_TX", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_dai_q6_mi2s_ops, + .name = "INT1 MI2S", + .id = MSM_INT1_MI2S, + .probe = msm_dai_q6_dai_mi2s_probe, + .remove = msm_dai_q6_dai_mi2s_remove, + }, + { + .playback = { + .stream_name = "INT2 MI2S Playback", + .aif_name = "INT2_MI2S_RX", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE, + .rate_min = 8000, + .rate_max = 48000, + }, + .capture = { + .stream_name = "INT2 MI2S Capture", + .aif_name = "INT2_MI2S_TX", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_dai_q6_mi2s_ops, + .name = "INT2 MI2S", + .id = MSM_INT2_MI2S, + .probe = msm_dai_q6_dai_mi2s_probe, + .remove = msm_dai_q6_dai_mi2s_remove, + }, + { + .playback = { + .stream_name = "INT3 MI2S Playback", + .aif_name = "INT3_MI2S_RX", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE, + .rate_min = 8000, + .rate_max = 48000, + }, + .capture = { + .stream_name = "INT3 MI2S Capture", + .aif_name = "INT3_MI2S_TX", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_dai_q6_mi2s_ops, + .name = "INT3 MI2S", + .id = MSM_INT3_MI2S, + .probe = msm_dai_q6_dai_mi2s_probe, + .remove = msm_dai_q6_dai_mi2s_remove, + }, + { + .playback = { + .stream_name = "INT4 MI2S Playback", + .aif_name = "INT4_MI2S_RX", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE, + .rate_min = 8000, + .rate_max = 192000, + }, + .capture = { + .stream_name = "INT4 MI2S Capture", + .aif_name = "INT4_MI2S_TX", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_dai_q6_mi2s_ops, + .name = "INT4 MI2S", + .id = MSM_INT4_MI2S, + .probe = msm_dai_q6_dai_mi2s_probe, + .remove = msm_dai_q6_dai_mi2s_remove, + }, + { + .playback = { + .stream_name = "INT5 MI2S Playback", + .aif_name = "INT5_MI2S_RX", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE, + .rate_min = 8000, + .rate_max = 48000, + }, + .capture = { + .stream_name = "INT5 MI2S Capture", + .aif_name = "INT5_MI2S_TX", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_dai_q6_mi2s_ops, + .name = "INT5 MI2S", + .id = MSM_INT5_MI2S, + .probe = msm_dai_q6_dai_mi2s_probe, + .remove = msm_dai_q6_dai_mi2s_remove, + }, + { + .playback = { + .stream_name = "INT6 MI2S Playback", + .aif_name = "INT6_MI2S_RX", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE, + .rate_min = 8000, + .rate_max = 48000, + }, + .capture = { + .stream_name = "INT6 MI2S Capture", + .aif_name = "INT6_MI2S_TX", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_dai_q6_mi2s_ops, + .name = "INT6 MI2S", + .id = MSM_INT6_MI2S, + .probe = msm_dai_q6_dai_mi2s_probe, + .remove = msm_dai_q6_dai_mi2s_remove, + }, +}; + + +static int msm_dai_q6_mi2s_get_lineconfig(u16 sd_lines, u16 *config_ptr, + unsigned int *ch_cnt) +{ + u8 num_of_sd_lines; + + num_of_sd_lines = num_of_bits_set(sd_lines); + switch (num_of_sd_lines) { + case 0: + pr_debug("%s: no line is assigned\n", __func__); + break; + case 1: + switch (sd_lines) { + case MSM_MI2S_SD0: + *config_ptr = AFE_PORT_I2S_SD0; + break; + case MSM_MI2S_SD1: + *config_ptr = AFE_PORT_I2S_SD1; + break; + case MSM_MI2S_SD2: + *config_ptr = AFE_PORT_I2S_SD2; + break; + case MSM_MI2S_SD3: + *config_ptr = AFE_PORT_I2S_SD3; + break; + case MSM_MI2S_SD4: + *config_ptr = AFE_PORT_I2S_SD4; + break; + case MSM_MI2S_SD5: + *config_ptr = AFE_PORT_I2S_SD5; + break; + case MSM_MI2S_SD6: + *config_ptr = AFE_PORT_I2S_SD6; + break; + case MSM_MI2S_SD7: + *config_ptr = AFE_PORT_I2S_SD7; + break; + default: + pr_err("%s: invalid SD lines %d\n", + __func__, sd_lines); + goto error_invalid_data; + } + break; + case 2: + switch (sd_lines) { + case MSM_MI2S_SD0 | MSM_MI2S_SD1: + *config_ptr = AFE_PORT_I2S_QUAD01; + break; + case MSM_MI2S_SD2 | MSM_MI2S_SD3: + *config_ptr = AFE_PORT_I2S_QUAD23; + break; + case MSM_MI2S_SD4 | MSM_MI2S_SD5: + *config_ptr = AFE_PORT_I2S_QUAD45; + break; + case MSM_MI2S_SD6 | MSM_MI2S_SD7: + *config_ptr = AFE_PORT_I2S_QUAD67; + break; + default: + pr_err("%s: invalid SD lines %d\n", + __func__, sd_lines); + goto error_invalid_data; + } + break; + case 3: + switch (sd_lines) { + case MSM_MI2S_SD0 | MSM_MI2S_SD1 | MSM_MI2S_SD2: + *config_ptr = AFE_PORT_I2S_6CHS; + break; + default: + pr_err("%s: invalid SD lines %d\n", + __func__, sd_lines); + goto error_invalid_data; + } + break; + case 4: + switch (sd_lines) { + case MSM_MI2S_SD0 | MSM_MI2S_SD1 | MSM_MI2S_SD2 | MSM_MI2S_SD3: + *config_ptr = AFE_PORT_I2S_8CHS; + break; + case MSM_MI2S_SD4 | MSM_MI2S_SD5 | MSM_MI2S_SD6 | MSM_MI2S_SD7: + *config_ptr = AFE_PORT_I2S_8CHS_2; + break; + default: + pr_err("%s: invalid SD lines %d\n", + __func__, sd_lines); + goto error_invalid_data; + } + break; + case 5: + switch (sd_lines) { + case MSM_MI2S_SD0 | MSM_MI2S_SD1 | MSM_MI2S_SD2 + | MSM_MI2S_SD3 | MSM_MI2S_SD4: + *config_ptr = AFE_PORT_I2S_10CHS; + break; + default: + pr_err("%s: invalid SD lines %d\n", + __func__, sd_lines); + goto error_invalid_data; + } + break; + case 6: + switch (sd_lines) { + case MSM_MI2S_SD0 | MSM_MI2S_SD1 | MSM_MI2S_SD2 + | MSM_MI2S_SD3 | MSM_MI2S_SD4 | MSM_MI2S_SD5: + *config_ptr = AFE_PORT_I2S_12CHS; + break; + default: + pr_err("%s: invalid SD lines %d\n", + __func__, sd_lines); + goto error_invalid_data; + } + break; + case 7: + switch (sd_lines) { + case MSM_MI2S_SD0 | MSM_MI2S_SD1 | MSM_MI2S_SD2 | MSM_MI2S_SD3 + | MSM_MI2S_SD4 | MSM_MI2S_SD5 | MSM_MI2S_SD6: + *config_ptr = AFE_PORT_I2S_14CHS; + break; + default: + pr_err("%s: invalid SD lines %d\n", + __func__, sd_lines); + goto error_invalid_data; + } + break; + case 8: + switch (sd_lines) { + case MSM_MI2S_SD0 | MSM_MI2S_SD1 | MSM_MI2S_SD2 | MSM_MI2S_SD3 + | MSM_MI2S_SD4 | MSM_MI2S_SD5 | MSM_MI2S_SD6 | MSM_MI2S_SD7: + *config_ptr = AFE_PORT_I2S_16CHS; + break; + default: + pr_err("%s: invalid SD lines %d\n", + __func__, sd_lines); + goto error_invalid_data; + } + break; + default: + pr_err("%s: invalid SD lines %d\n", __func__, num_of_sd_lines); + goto error_invalid_data; + } + *ch_cnt = num_of_sd_lines; + return 0; + +error_invalid_data: + pr_err("%s: invalid data\n", __func__); + return -EINVAL; +} + +static int msm_dai_q6_mi2s_platform_data_validation( + struct platform_device *pdev, struct snd_soc_dai_driver *dai_driver) +{ + struct msm_dai_q6_mi2s_dai_data *dai_data = dev_get_drvdata(&pdev->dev); + struct msm_mi2s_pdata *mi2s_pdata = + (struct msm_mi2s_pdata *) pdev->dev.platform_data; + unsigned int ch_cnt; + int rc = 0; + u16 sd_line; + + if (mi2s_pdata == NULL) { + pr_err("%s: mi2s_pdata NULL", __func__); + return -EINVAL; + } + + rc = msm_dai_q6_mi2s_get_lineconfig(mi2s_pdata->rx_sd_lines, + &sd_line, &ch_cnt); + if (rc < 0) { + dev_err(&pdev->dev, "invalid MI2S RX sd line config\n"); + goto rtn; + } + + if (ch_cnt) { + dai_data->rx_dai.mi2s_dai_data.port_config.i2s.channel_mode = + sd_line; + dai_data->rx_dai.pdata_mi2s_lines = sd_line; + dai_driver->playback.channels_min = 1; + dai_driver->playback.channels_max = ch_cnt << 1; + } else { + dai_driver->playback.channels_min = 0; + dai_driver->playback.channels_max = 0; + } + rc = msm_dai_q6_mi2s_get_lineconfig(mi2s_pdata->tx_sd_lines, + &sd_line, &ch_cnt); + if (rc < 0) { + dev_err(&pdev->dev, "invalid MI2S TX sd line config\n"); + goto rtn; + } + + if (ch_cnt) { + dai_data->tx_dai.mi2s_dai_data.port_config.i2s.channel_mode = + sd_line; + dai_data->tx_dai.pdata_mi2s_lines = sd_line; + dai_driver->capture.channels_min = 1; + dai_driver->capture.channels_max = ch_cnt << 1; + } else { + dai_driver->capture.channels_min = 0; + dai_driver->capture.channels_max = 0; + } + + dev_dbg(&pdev->dev, "%s: playback sdline 0x%x capture sdline 0x%x\n", + __func__, dai_data->rx_dai.pdata_mi2s_lines, + dai_data->tx_dai.pdata_mi2s_lines); + dev_dbg(&pdev->dev, "%s: playback ch_max %d capture ch_mx %d\n", + __func__, dai_driver->playback.channels_max, + dai_driver->capture.channels_max); +rtn: + return rc; +} + +static const struct snd_soc_component_driver msm_q6_mi2s_dai_component = { + .name = "msm-dai-q6-mi2s", +}; +static int msm_dai_q6_mi2s_dev_probe(struct platform_device *pdev) +{ + struct msm_dai_q6_mi2s_dai_data *dai_data; + const char *q6_mi2s_dev_id = "qcom,msm-dai-q6-mi2s-dev-id"; + u32 tx_line = 0; + u32 rx_line = 0; + u32 mi2s_intf = 0; + struct msm_mi2s_pdata *mi2s_pdata; + int rc; + + rc = of_property_read_u32(pdev->dev.of_node, q6_mi2s_dev_id, + &mi2s_intf); + if (rc) { + dev_err(&pdev->dev, + "%s: missing 0x%x in dt node\n", __func__, mi2s_intf); + goto rtn; + } + + dev_dbg(&pdev->dev, "dev name %s dev id 0x%x\n", dev_name(&pdev->dev), + mi2s_intf); + + if ((mi2s_intf < MSM_MI2S_MIN || mi2s_intf > MSM_MI2S_MAX) + || (mi2s_intf >= ARRAY_SIZE(msm_dai_q6_mi2s_dai))) { + dev_err(&pdev->dev, + "%s: Invalid MI2S ID %u from Device Tree\n", + __func__, mi2s_intf); + rc = -ENXIO; + goto rtn; + } + + pdev->id = mi2s_intf; + + mi2s_pdata = kzalloc(sizeof(struct msm_mi2s_pdata), GFP_KERNEL); + if (!mi2s_pdata) { + rc = -ENOMEM; + goto rtn; + } + + rc = of_property_read_u32(pdev->dev.of_node, "qcom,msm-mi2s-rx-lines", + &rx_line); + if (rc) { + dev_err(&pdev->dev, "%s: Rx line from DT file %s\n", __func__, + "qcom,msm-mi2s-rx-lines"); + goto free_pdata; + } + + rc = of_property_read_u32(pdev->dev.of_node, "qcom,msm-mi2s-tx-lines", + &tx_line); + if (rc) { + dev_err(&pdev->dev, "%s: Tx line from DT file %s\n", __func__, + "qcom,msm-mi2s-tx-lines"); + goto free_pdata; + } + dev_dbg(&pdev->dev, "dev name %s Rx line 0x%x , Tx ine 0x%x\n", + dev_name(&pdev->dev), rx_line, tx_line); + mi2s_pdata->rx_sd_lines = rx_line; + mi2s_pdata->tx_sd_lines = tx_line; + mi2s_pdata->intf_id = mi2s_intf; + + dai_data = kzalloc(sizeof(struct msm_dai_q6_mi2s_dai_data), + GFP_KERNEL); + if (!dai_data) { + rc = -ENOMEM; + goto free_pdata; + } else + dev_set_drvdata(&pdev->dev, dai_data); + + rc = of_property_read_u32(pdev->dev.of_node, + "qcom,msm-dai-is-island-supported", + &dai_data->is_island_dai); + if (rc) + dev_dbg(&pdev->dev, "island supported entry not found\n"); + + pdev->dev.platform_data = mi2s_pdata; + + rc = msm_dai_q6_mi2s_platform_data_validation(pdev, + &msm_dai_q6_mi2s_dai[mi2s_intf]); + if (rc < 0) + goto free_dai_data; + + rc = snd_soc_register_component(&pdev->dev, &msm_q6_mi2s_dai_component, + &msm_dai_q6_mi2s_dai[mi2s_intf], 1); + if (rc < 0) + goto err_register; + return 0; + +err_register: + dev_err(&pdev->dev, "fail to msm_dai_q6_mi2s_dev_probe\n"); +free_dai_data: + kfree(dai_data); +free_pdata: + kfree(mi2s_pdata); +rtn: + return rc; +} + +static int msm_dai_q6_mi2s_dev_remove(struct platform_device *pdev) +{ + snd_soc_unregister_component(&pdev->dev); + return 0; +} + +static const struct snd_soc_component_driver msm_dai_q6_component = { + .name = "msm-dai-q6-dev", +}; + +static int msm_dai_q6_dev_probe(struct platform_device *pdev) +{ + int rc, id, i, len; + const char *q6_dev_id = "qcom,msm-dai-q6-dev-id"; + char stream_name[80]; + + rc = of_property_read_u32(pdev->dev.of_node, q6_dev_id, &id); + if (rc) { + dev_err(&pdev->dev, + "%s: missing %s in dt node\n", __func__, q6_dev_id); + return rc; + } + + pdev->id = id; + + pr_debug("%s: dev name %s, id:%d\n", __func__, + dev_name(&pdev->dev), pdev->id); + + switch (id) { + case SLIMBUS_0_RX: + strlcpy(stream_name, "Slimbus Playback", 80); + goto register_slim_playback; + case SLIMBUS_2_RX: + strlcpy(stream_name, "Slimbus2 Playback", 80); + goto register_slim_playback; + case SLIMBUS_1_RX: + strlcpy(stream_name, "Slimbus1 Playback", 80); + goto register_slim_playback; + case SLIMBUS_3_RX: + strlcpy(stream_name, "Slimbus3 Playback", 80); + goto register_slim_playback; + case SLIMBUS_4_RX: + strlcpy(stream_name, "Slimbus4 Playback", 80); + goto register_slim_playback; + case SLIMBUS_5_RX: + strlcpy(stream_name, "Slimbus5 Playback", 80); + goto register_slim_playback; + case SLIMBUS_6_RX: + strlcpy(stream_name, "Slimbus6 Playback", 80); + goto register_slim_playback; + case SLIMBUS_7_RX: + strlcpy(stream_name, "Slimbus7 Playback", sizeof(stream_name)); + goto register_slim_playback; + case SLIMBUS_8_RX: + strlcpy(stream_name, "Slimbus8 Playback", sizeof(stream_name)); + goto register_slim_playback; + case SLIMBUS_9_RX: + strlcpy(stream_name, "Slimbus9 Playback", sizeof(stream_name)); + goto register_slim_playback; +register_slim_playback: + rc = -ENODEV; + len = strnlen(stream_name, 80); + for (i = 0; i < ARRAY_SIZE(msm_dai_q6_slimbus_rx_dai); i++) { + if (msm_dai_q6_slimbus_rx_dai[i].playback.stream_name && + !strcmp(stream_name, + msm_dai_q6_slimbus_rx_dai[i] + .playback.stream_name)) { + rc = snd_soc_register_component(&pdev->dev, + &msm_dai_q6_component, + &msm_dai_q6_slimbus_rx_dai[i], 1); + break; + } + } + if (rc) + pr_err("%s: Device not found stream name %s\n", + __func__, stream_name); + break; + case SLIMBUS_0_TX: + strlcpy(stream_name, "Slimbus Capture", 80); + goto register_slim_capture; + case SLIMBUS_1_TX: + strlcpy(stream_name, "Slimbus1 Capture", 80); + goto register_slim_capture; + case SLIMBUS_2_TX: + strlcpy(stream_name, "Slimbus2 Capture", 80); + goto register_slim_capture; + case SLIMBUS_3_TX: + strlcpy(stream_name, "Slimbus3 Capture", 80); + goto register_slim_capture; + case SLIMBUS_4_TX: + strlcpy(stream_name, "Slimbus4 Capture", 80); + goto register_slim_capture; + case SLIMBUS_5_TX: + strlcpy(stream_name, "Slimbus5 Capture", 80); + goto register_slim_capture; + case SLIMBUS_6_TX: + strlcpy(stream_name, "Slimbus6 Capture", 80); + goto register_slim_capture; + case SLIMBUS_7_TX: + strlcpy(stream_name, "Slimbus7 Capture", sizeof(stream_name)); + goto register_slim_capture; + case SLIMBUS_8_TX: + strlcpy(stream_name, "Slimbus8 Capture", sizeof(stream_name)); + goto register_slim_capture; + case SLIMBUS_9_TX: + strlcpy(stream_name, "Slimbus9 Capture", sizeof(stream_name)); + goto register_slim_capture; +register_slim_capture: + rc = -ENODEV; + len = strnlen(stream_name, 80); + for (i = 0; i < ARRAY_SIZE(msm_dai_q6_slimbus_tx_dai); i++) { + if (msm_dai_q6_slimbus_tx_dai[i].capture.stream_name && + !strcmp(stream_name, + msm_dai_q6_slimbus_tx_dai[i] + .capture.stream_name)) { + rc = snd_soc_register_component(&pdev->dev, + &msm_dai_q6_component, + &msm_dai_q6_slimbus_tx_dai[i], 1); + break; + } + } + if (rc) + pr_err("%s: Device not found stream name %s\n", + __func__, stream_name); + break; + case INT_BT_SCO_RX: + rc = snd_soc_register_component(&pdev->dev, + &msm_dai_q6_component, &msm_dai_q6_bt_sco_rx_dai, 1); + break; + case INT_BT_SCO_TX: + rc = snd_soc_register_component(&pdev->dev, + &msm_dai_q6_component, &msm_dai_q6_bt_sco_tx_dai, 1); + break; + case INT_BT_A2DP_RX: + rc = snd_soc_register_component(&pdev->dev, + &msm_dai_q6_component, &msm_dai_q6_bt_a2dp_rx_dai, 1); + break; + case INT_FM_RX: + rc = snd_soc_register_component(&pdev->dev, + &msm_dai_q6_component, &msm_dai_q6_fm_rx_dai, 1); + break; + case INT_FM_TX: + rc = snd_soc_register_component(&pdev->dev, + &msm_dai_q6_component, &msm_dai_q6_fm_tx_dai, 1); + break; + case AFE_PORT_ID_USB_RX: + rc = snd_soc_register_component(&pdev->dev, + &msm_dai_q6_component, &msm_dai_q6_usb_rx_dai, 1); + break; + case AFE_PORT_ID_USB_TX: + rc = snd_soc_register_component(&pdev->dev, + &msm_dai_q6_component, &msm_dai_q6_usb_tx_dai, 1); + break; + case RT_PROXY_DAI_001_RX: + strlcpy(stream_name, "AFE Playback", 80); + goto register_afe_playback; + case RT_PROXY_DAI_002_RX: + strlcpy(stream_name, "AFE-PROXY RX", 80); +register_afe_playback: + rc = -ENODEV; + len = strnlen(stream_name, 80); + for (i = 0; i < ARRAY_SIZE(msm_dai_q6_afe_rx_dai); i++) { + if (msm_dai_q6_afe_rx_dai[i].playback.stream_name && + !strcmp(stream_name, + msm_dai_q6_afe_rx_dai[i].playback.stream_name)) { + rc = snd_soc_register_component(&pdev->dev, + &msm_dai_q6_component, + &msm_dai_q6_afe_rx_dai[i], 1); + break; + } + } + if (rc) + pr_err("%s: Device not found stream name %s\n", + __func__, stream_name); + break; + case RT_PROXY_DAI_001_TX: + strlcpy(stream_name, "AFE-PROXY TX", 80); + goto register_afe_capture; + case RT_PROXY_DAI_002_TX: + strlcpy(stream_name, "AFE Capture", 80); +register_afe_capture: + rc = -ENODEV; + len = strnlen(stream_name, 80); + for (i = 0; i < ARRAY_SIZE(msm_dai_q6_afe_tx_dai); i++) { + if (msm_dai_q6_afe_tx_dai[i].capture.stream_name && + !strcmp(stream_name, + msm_dai_q6_afe_tx_dai[i].capture.stream_name)) { + rc = snd_soc_register_component(&pdev->dev, + &msm_dai_q6_component, + &msm_dai_q6_afe_tx_dai[i], 1); + break; + } + } + if (rc) + pr_err("%s: Device not found stream name %s\n", + __func__, stream_name); + break; + case VOICE_PLAYBACK_TX: + strlcpy(stream_name, "Voice Farend Playback", 80); + goto register_voice_playback; + case VOICE2_PLAYBACK_TX: + strlcpy(stream_name, "Voice2 Farend Playback", 80); +register_voice_playback: + rc = -ENODEV; + len = strnlen(stream_name, 80); + for (i = 0; i < ARRAY_SIZE(msm_dai_q6_voc_playback_dai); i++) { + if (msm_dai_q6_voc_playback_dai[i].playback.stream_name + && !strcmp(stream_name, + msm_dai_q6_voc_playback_dai[i].playback.stream_name)) { + rc = snd_soc_register_component(&pdev->dev, + &msm_dai_q6_component, + &msm_dai_q6_voc_playback_dai[i], 1); + break; + } + } + if (rc) + pr_err("%s Device not found stream name %s\n", + __func__, stream_name); + break; + case VOICE_RECORD_RX: + strlcpy(stream_name, "Voice Downlink Capture", 80); + goto register_uplink_capture; + case VOICE_RECORD_TX: + strlcpy(stream_name, "Voice Uplink Capture", 80); +register_uplink_capture: + rc = -ENODEV; + len = strnlen(stream_name, 80); + for (i = 0; i < ARRAY_SIZE(msm_dai_q6_incall_record_dai); i++) { + if (msm_dai_q6_incall_record_dai[i].capture.stream_name + && !strcmp(stream_name, + msm_dai_q6_incall_record_dai[i]. + capture.stream_name)) { + rc = snd_soc_register_component(&pdev->dev, + &msm_dai_q6_component, + &msm_dai_q6_incall_record_dai[i], 1); + break; + } + } + if (rc) + pr_err("%s: Device not found stream name %s\n", + __func__, stream_name); + break; + + default: + rc = -ENODEV; + break; + } + + return rc; +} + +static int msm_dai_q6_dev_remove(struct platform_device *pdev) +{ + snd_soc_unregister_component(&pdev->dev); + return 0; +} + +static const struct of_device_id msm_dai_q6_dev_dt_match[] = { + { .compatible = "qcom,msm-dai-q6-dev", }, + { } +}; +MODULE_DEVICE_TABLE(of, msm_dai_q6_dev_dt_match); + +static struct platform_driver msm_dai_q6_dev = { + .probe = msm_dai_q6_dev_probe, + .remove = msm_dai_q6_dev_remove, + .driver = { + .name = "msm-dai-q6-dev", + .owner = THIS_MODULE, + .of_match_table = msm_dai_q6_dev_dt_match, + }, +}; + +static int msm_dai_q6_probe(struct platform_device *pdev) +{ + int rc; + + pr_debug("%s: dev name %s, id:%d\n", __func__, + dev_name(&pdev->dev), pdev->id); + rc = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev); + if (rc) { + dev_err(&pdev->dev, "%s: failed to add child nodes, rc=%d\n", + __func__, rc); + } else + dev_dbg(&pdev->dev, "%s: added child node\n", __func__); + + return rc; +} + +static int msm_dai_q6_remove(struct platform_device *pdev) +{ + of_platform_depopulate(&pdev->dev); + return 0; +} + +static const struct of_device_id msm_dai_q6_dt_match[] = { + { .compatible = "qcom,msm-dai-q6", }, + { } +}; +MODULE_DEVICE_TABLE(of, msm_dai_q6_dt_match); +static struct platform_driver msm_dai_q6 = { + .probe = msm_dai_q6_probe, + .remove = msm_dai_q6_remove, + .driver = { + .name = "msm-dai-q6", + .owner = THIS_MODULE, + .of_match_table = msm_dai_q6_dt_match, + }, +}; + +static int msm_dai_mi2s_q6_probe(struct platform_device *pdev) +{ + int rc; + + rc = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev); + if (rc) { + dev_err(&pdev->dev, "%s: failed to add child nodes, rc=%d\n", + __func__, rc); + } else + dev_dbg(&pdev->dev, "%s: added child node\n", __func__); + return rc; +} + +static int msm_dai_mi2s_q6_remove(struct platform_device *pdev) +{ + return 0; +} + +static const struct of_device_id msm_dai_mi2s_dt_match[] = { + { .compatible = "qcom,msm-dai-mi2s", }, + { } +}; + +MODULE_DEVICE_TABLE(of, msm_dai_mi2s_dt_match); + +static struct platform_driver msm_dai_mi2s_q6 = { + .probe = msm_dai_mi2s_q6_probe, + .remove = msm_dai_mi2s_q6_remove, + .driver = { + .name = "msm-dai-mi2s", + .owner = THIS_MODULE, + .of_match_table = msm_dai_mi2s_dt_match, + }, +}; + +static const struct of_device_id msm_dai_q6_mi2s_dev_dt_match[] = { + { .compatible = "qcom,msm-dai-q6-mi2s", }, + { } +}; + +MODULE_DEVICE_TABLE(of, msm_dai_q6_mi2s_dev_dt_match); + +static struct platform_driver msm_dai_q6_mi2s_driver = { + .probe = msm_dai_q6_mi2s_dev_probe, + .remove = msm_dai_q6_mi2s_dev_remove, + .driver = { + .name = "msm-dai-q6-mi2s", + .owner = THIS_MODULE, + .of_match_table = msm_dai_q6_mi2s_dev_dt_match, + }, +}; + +static int msm_dai_q6_spdif_dev_probe(struct platform_device *pdev) +{ + int rc, id; + const char *q6_dev_id = "qcom,msm-dai-q6-dev-id"; + + rc = of_property_read_u32(pdev->dev.of_node, q6_dev_id, &id); + if (rc) { + dev_err(&pdev->dev, + "%s: missing %s in dt node\n", __func__, q6_dev_id); + return rc; + } + + pdev->id = id; + + pr_debug("%s: dev name %s, id:%d\n", __func__, + dev_name(&pdev->dev), pdev->id); + + switch (pdev->id) { + case AFE_PORT_ID_PRIMARY_SPDIF_RX: + rc = snd_soc_register_component(&pdev->dev, + &msm_dai_spdif_q6_component, + &msm_dai_q6_spdif_spdif_rx_dai[0], 1); + break; + case AFE_PORT_ID_SECONDARY_SPDIF_RX: + rc = snd_soc_register_component(&pdev->dev, + &msm_dai_spdif_q6_component, + &msm_dai_q6_spdif_spdif_rx_dai[1], 1); + break; + case AFE_PORT_ID_PRIMARY_SPDIF_TX: + rc = snd_soc_register_component(&pdev->dev, + &msm_dai_spdif_q6_component, + &msm_dai_q6_spdif_spdif_tx_dai[0], 1); + break; + case AFE_PORT_ID_SECONDARY_SPDIF_TX: + rc = snd_soc_register_component(&pdev->dev, + &msm_dai_spdif_q6_component, + &msm_dai_q6_spdif_spdif_tx_dai[1], 1); + break; + default: + dev_err(&pdev->dev, "invalid device ID %d\n", pdev->id); + rc = -ENODEV; + break; + } + + return rc; +} + +static int msm_dai_q6_spdif_dev_remove(struct platform_device *pdev) +{ + snd_soc_unregister_component(&pdev->dev); + return 0; +} + +static const struct of_device_id msm_dai_q6_spdif_dt_match[] = { + {.compatible = "qcom,msm-dai-q6-spdif"}, + {} +}; +MODULE_DEVICE_TABLE(of, msm_dai_q6_spdif_dt_match); + +static struct platform_driver msm_dai_q6_spdif_driver = { + .probe = msm_dai_q6_spdif_dev_probe, + .remove = msm_dai_q6_spdif_dev_remove, + .driver = { + .name = "msm-dai-q6-spdif", + .owner = THIS_MODULE, + .of_match_table = msm_dai_q6_spdif_dt_match, + }, +}; + +static int msm_dai_q6_tdm_set_clk_param(u32 group_id, + struct afe_clk_set *clk_set, u32 mode) +{ + switch (group_id) { + case AFE_GROUP_DEVICE_ID_PRIMARY_TDM_RX: + case AFE_GROUP_DEVICE_ID_PRIMARY_TDM_TX: + if (mode) + clk_set->clk_id = Q6AFE_LPASS_CLK_ID_PRI_TDM_IBIT; + else + 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 (mode) + clk_set->clk_id = Q6AFE_LPASS_CLK_ID_SEC_TDM_IBIT; + else + 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 (mode) + clk_set->clk_id = Q6AFE_LPASS_CLK_ID_TER_TDM_IBIT; + else + 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 (mode) + clk_set->clk_id = Q6AFE_LPASS_CLK_ID_QUAD_TDM_IBIT; + else + clk_set->clk_id = Q6AFE_LPASS_CLK_ID_QUAD_TDM_EBIT; + break; + case AFE_GROUP_DEVICE_ID_QUINARY_TDM_RX: + case AFE_GROUP_DEVICE_ID_QUINARY_TDM_TX: + if (mode) + clk_set->clk_id = Q6AFE_LPASS_CLK_ID_QUIN_TDM_IBIT; + else + clk_set->clk_id = Q6AFE_LPASS_CLK_ID_QUIN_TDM_EBIT; + break; + default: + return -EINVAL; + } + return 0; +} + +static int msm_dai_tdm_q6_probe(struct platform_device *pdev) +{ + int rc = 0; + const uint32_t *port_id_array = NULL; + uint32_t array_length = 0; + int i = 0; + int group_idx = 0; + u32 clk_mode = 0; + + /* extract tdm group info into static */ + rc = of_property_read_u32(pdev->dev.of_node, + "qcom,msm-cpudai-tdm-group-id", + (u32 *)&tdm_group_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: Group ID from DT file 0x%x\n", + __func__, tdm_group_cfg.group_id); + + rc = of_property_read_u32(pdev->dev.of_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(pdev->dev.of_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++) + tdm_group_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++) + tdm_group_cfg.port_id[i] = + AFE_PORT_INVALID; + + /* extract tdm clk info into static */ + rc = of_property_read_u32(pdev->dev.of_node, + "qcom,msm-cpudai-tdm-clk-rate", + &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__, tdm_clk_set.clk_freq_in_hz); + + /* initialize static tdm clk attribute to default value */ + tdm_clk_set.clk_attri = Q6AFE_LPASS_CLK_ATTRIBUTE_INVERT_COUPLE_NO; + + /* extract tdm clk attribute into static */ + if (of_find_property(pdev->dev.of_node, + "qcom,msm-cpudai-tdm-clk-attribute", NULL)) { + rc = of_property_read_u16(pdev->dev.of_node, + "qcom,msm-cpudai-tdm-clk-attribute", + &tdm_clk_set.clk_attri); + if (rc) { + dev_err(&pdev->dev, "%s: value for clk attribute not found %s\n", + __func__, "qcom,msm-cpudai-tdm-clk-attribute"); + goto rtn; + } + dev_dbg(&pdev->dev, "%s: clk attribute from DT file %d\n", + __func__, tdm_clk_set.clk_attri); + } else + dev_dbg(&pdev->dev, "%s: clk attribute not found\n", __func__); + + /* extract tdm clk src master/slave info into static */ + rc = of_property_read_u32(pdev->dev.of_node, + "qcom,msm-cpudai-tdm-clk-internal", + &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__, clk_mode); + + rc = msm_dai_q6_tdm_set_clk_param(tdm_group_cfg.group_id, + &tdm_clk_set, clk_mode); + if (rc) { + dev_err(&pdev->dev, "%s: group id not supported 0x%x\n", + __func__, tdm_group_cfg.group_id); + goto rtn; + } + + /* other initializations within device group */ + group_idx = msm_dai_q6_get_group_idx(tdm_group_cfg.group_id); + if (group_idx < 0) { + dev_err(&pdev->dev, "%s: group id 0x%x not supported\n", + __func__, tdm_group_cfg.group_id); + rc = -EINVAL; + goto rtn; + } + atomic_set(&tdm_group_ref[group_idx], 0); + + /* probe child node info */ + rc = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev); + if (rc) { + dev_err(&pdev->dev, "%s: failed to add child nodes, rc=%d\n", + __func__, rc); + goto rtn; + } else + dev_dbg(&pdev->dev, "%s: added child node\n", __func__); + +rtn: + return rc; +} + +static int msm_dai_tdm_q6_remove(struct platform_device *pdev) +{ + return 0; +} + +static const struct of_device_id msm_dai_tdm_dt_match[] = { + { .compatible = "qcom,msm-dai-tdm", }, + {} +}; + +MODULE_DEVICE_TABLE(of, msm_dai_tdm_dt_match); + +static struct platform_driver msm_dai_tdm_q6 = { + .probe = msm_dai_tdm_q6_probe, + .remove = msm_dai_tdm_q6_remove, + .driver = { + .name = "msm-dai-tdm", + .owner = THIS_MODULE, + .of_match_table = msm_dai_tdm_dt_match, + }, +}; + +static int msm_dai_q6_tdm_data_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct msm_dai_q6_tdm_dai_data *dai_data = kcontrol->private_data; + int value = ucontrol->value.integer.value[0]; + + switch (value) { + case 0: + dai_data->port_cfg.tdm.data_format = AFE_LINEAR_PCM_DATA; + break; + case 1: + dai_data->port_cfg.tdm.data_format = AFE_NON_LINEAR_DATA; + break; + case 2: + dai_data->port_cfg.tdm.data_format = AFE_GENERIC_COMPRESSED; + break; + default: + pr_err("%s: data_format invalid\n", __func__); + break; + } + pr_debug("%s: data_format = %d\n", + __func__, dai_data->port_cfg.tdm.data_format); + return 0; +} + +static int msm_dai_q6_tdm_data_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct msm_dai_q6_tdm_dai_data *dai_data = kcontrol->private_data; + + ucontrol->value.integer.value[0] = + dai_data->port_cfg.tdm.data_format; + pr_debug("%s: data_format = %d\n", + __func__, dai_data->port_cfg.tdm.data_format); + return 0; +} + +static int msm_dai_q6_tdm_header_type_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct msm_dai_q6_tdm_dai_data *dai_data = kcontrol->private_data; + int value = ucontrol->value.integer.value[0]; + + dai_data->port_cfg.custom_tdm_header.header_type = value; + pr_debug("%s: header_type = %d\n", + __func__, + dai_data->port_cfg.custom_tdm_header.header_type); + return 0; +} + +static int msm_dai_q6_tdm_header_type_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct msm_dai_q6_tdm_dai_data *dai_data = kcontrol->private_data; + + ucontrol->value.integer.value[0] = + dai_data->port_cfg.custom_tdm_header.header_type; + pr_debug("%s: header_type = %d\n", + __func__, + dai_data->port_cfg.custom_tdm_header.header_type); + return 0; +} + +static int msm_dai_q6_tdm_header_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct msm_dai_q6_tdm_dai_data *dai_data = kcontrol->private_data; + int i = 0; + + for (i = 0; i < AFE_CUSTOM_TDM_HEADER_MAX_CNT; i++) { + dai_data->port_cfg.custom_tdm_header.header[i] = + (u16)ucontrol->value.integer.value[i]; + pr_debug("%s: header #%d = 0x%x\n", + __func__, i, + dai_data->port_cfg.custom_tdm_header.header[i]); + } + return 0; +} + +static int msm_dai_q6_tdm_header_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct msm_dai_q6_tdm_dai_data *dai_data = kcontrol->private_data; + int i = 0; + + for (i = 0; i < AFE_CUSTOM_TDM_HEADER_MAX_CNT; i++) { + ucontrol->value.integer.value[i] = + dai_data->port_cfg.custom_tdm_header.header[i]; + pr_debug("%s: header #%d = 0x%x\n", + __func__, i, + dai_data->port_cfg.custom_tdm_header.header[i]); + } + return 0; +} + +static const struct snd_kcontrol_new tdm_config_controls_data_format[] = { + SOC_ENUM_EXT("PRI_TDM_RX_0 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("PRI_TDM_RX_1 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("PRI_TDM_RX_2 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("PRI_TDM_RX_3 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("PRI_TDM_RX_4 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("PRI_TDM_RX_5 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("PRI_TDM_RX_6 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("PRI_TDM_RX_7 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("PRI_TDM_TX_0 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("PRI_TDM_TX_1 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("PRI_TDM_TX_2 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("PRI_TDM_TX_3 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("PRI_TDM_TX_4 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("PRI_TDM_TX_5 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("PRI_TDM_TX_6 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("PRI_TDM_TX_7 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("SEC_TDM_RX_0 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("SEC_TDM_RX_1 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("SEC_TDM_RX_2 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("SEC_TDM_RX_3 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("SEC_TDM_RX_4 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("SEC_TDM_RX_5 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("SEC_TDM_RX_6 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("SEC_TDM_RX_7 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("SEC_TDM_TX_0 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("SEC_TDM_TX_1 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("SEC_TDM_TX_2 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("SEC_TDM_TX_3 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("SEC_TDM_TX_4 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("SEC_TDM_TX_5 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("SEC_TDM_TX_6 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("SEC_TDM_TX_7 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("TERT_TDM_RX_0 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("TERT_TDM_RX_1 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("TERT_TDM_RX_2 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("TERT_TDM_RX_3 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("TERT_TDM_RX_4 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("TERT_TDM_RX_5 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("TERT_TDM_RX_6 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("TERT_TDM_RX_7 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("TERT_TDM_TX_0 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("TERT_TDM_TX_1 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("TERT_TDM_TX_2 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("TERT_TDM_TX_3 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("TERT_TDM_TX_4 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("TERT_TDM_TX_5 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("TERT_TDM_TX_6 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("TERT_TDM_TX_7 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("QUAT_TDM_RX_0 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("QUAT_TDM_RX_1 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("QUAT_TDM_RX_2 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("QUAT_TDM_RX_3 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("QUAT_TDM_RX_4 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("QUAT_TDM_RX_5 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("QUAT_TDM_RX_6 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("QUAT_TDM_RX_7 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("QUAT_TDM_TX_0 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("QUAT_TDM_TX_1 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("QUAT_TDM_TX_2 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("QUAT_TDM_TX_3 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("QUAT_TDM_TX_4 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("QUAT_TDM_TX_5 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("QUAT_TDM_TX_6 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("QUAT_TDM_TX_7 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("QUIN_TDM_RX_0 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("QUIN_TDM_RX_1 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("QUIN_TDM_RX_2 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("QUIN_TDM_RX_3 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("QUIN_TDM_RX_4 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("QUIN_TDM_RX_5 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("QUIN_TDM_RX_6 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("QUIN_TDM_RX_7 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("QUIN_TDM_TX_0 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("QUIN_TDM_TX_1 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("QUIN_TDM_TX_2 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("QUIN_TDM_TX_3 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("QUIN_TDM_TX_4 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("QUIN_TDM_TX_5 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("QUIN_TDM_TX_6 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), + SOC_ENUM_EXT("QUIN_TDM_TX_7 Data Format", tdm_config_enum[0], + msm_dai_q6_tdm_data_format_get, + msm_dai_q6_tdm_data_format_put), +}; + +static const struct snd_kcontrol_new tdm_config_controls_header_type[] = { + SOC_ENUM_EXT("PRI_TDM_RX_0 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("PRI_TDM_RX_1 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("PRI_TDM_RX_2 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("PRI_TDM_RX_3 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("PRI_TDM_RX_4 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("PRI_TDM_RX_5 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("PRI_TDM_RX_6 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("PRI_TDM_RX_7 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("PRI_TDM_TX_0 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("PRI_TDM_TX_1 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("PRI_TDM_TX_2 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("PRI_TDM_TX_3 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("PRI_TDM_TX_4 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("PRI_TDM_TX_5 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("PRI_TDM_TX_6 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("PRI_TDM_TX_7 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("SEC_TDM_RX_0 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("SEC_TDM_RX_1 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("SEC_TDM_RX_2 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("SEC_TDM_RX_3 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("SEC_TDM_RX_4 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("SEC_TDM_RX_5 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("SEC_TDM_RX_6 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("SEC_TDM_RX_7 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("SEC_TDM_TX_0 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("SEC_TDM_TX_1 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("SEC_TDM_TX_2 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("SEC_TDM_TX_3 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("SEC_TDM_TX_4 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("SEC_TDM_TX_5 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("SEC_TDM_TX_6 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("SEC_TDM_TX_7 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("TERT_TDM_RX_0 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("TERT_TDM_RX_1 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("TERT_TDM_RX_2 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("TERT_TDM_RX_3 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("TERT_TDM_RX_4 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("TERT_TDM_RX_5 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("TERT_TDM_RX_6 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("TERT_TDM_RX_7 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("TERT_TDM_TX_0 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("TERT_TDM_TX_1 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("TERT_TDM_TX_2 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("TERT_TDM_TX_3 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("TERT_TDM_TX_4 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("TERT_TDM_TX_5 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("TERT_TDM_TX_6 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("TERT_TDM_TX_7 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("QUAT_TDM_RX_0 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("QUAT_TDM_RX_1 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("QUAT_TDM_RX_2 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("QUAT_TDM_RX_3 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("QUAT_TDM_RX_4 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("QUAT_TDM_RX_5 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("QUAT_TDM_RX_6 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("QUAT_TDM_RX_7 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("QUAT_TDM_TX_0 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("QUAT_TDM_TX_1 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("QUAT_TDM_TX_2 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("QUAT_TDM_TX_3 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("QUAT_TDM_TX_4 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("QUAT_TDM_TX_5 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("QUAT_TDM_TX_6 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("QUAT_TDM_TX_7 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("QUIN_TDM_RX_0 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("QUIN_TDM_RX_1 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("QUIN_TDM_RX_2 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("QUIN_TDM_RX_3 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("QUIN_TDM_RX_4 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("QUIN_TDM_RX_5 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("QUIN_TDM_RX_6 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("QUIN_TDM_RX_7 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("QUIN_TDM_TX_0 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("QUIN_TDM_TX_1 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("QUIN_TDM_TX_2 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("QUIN_TDM_TX_3 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("QUIN_TDM_TX_4 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("QUIN_TDM_TX_5 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("QUIN_TDM_TX_6 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), + SOC_ENUM_EXT("QUIN_TDM_TX_7 Header Type", tdm_config_enum[1], + msm_dai_q6_tdm_header_type_get, + msm_dai_q6_tdm_header_type_put), +}; + +static const struct snd_kcontrol_new tdm_config_controls_header[] = { + SOC_SINGLE_MULTI_EXT("PRI_TDM_RX_0 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("PRI_TDM_RX_1 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("PRI_TDM_RX_2 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("PRI_TDM_RX_3 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("PRI_TDM_RX_4 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("PRI_TDM_RX_5 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("PRI_TDM_RX_6 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("PRI_TDM_RX_7 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("PRI_TDM_TX_0 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("PRI_TDM_TX_1 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("PRI_TDM_TX_2 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("PRI_TDM_TX_3 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("PRI_TDM_TX_4 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("PRI_TDM_TX_5 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("PRI_TDM_TX_6 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("PRI_TDM_TX_7 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("SEC_TDM_RX_0 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("SEC_TDM_RX_1 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("SEC_TDM_RX_2 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("SEC_TDM_RX_3 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("SEC_TDM_RX_4 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("SEC_TDM_RX_5 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("SEC_TDM_RX_6 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("SEC_TDM_RX_7 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("SEC_TDM_TX_0 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("SEC_TDM_TX_1 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("SEC_TDM_TX_2 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("SEC_TDM_TX_3 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("SEC_TDM_TX_4 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("SEC_TDM_TX_5 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("SEC_TDM_TX_6 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("SEC_TDM_TX_7 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("TERT_TDM_RX_0 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("TERT_TDM_RX_1 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("TERT_TDM_RX_2 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("TERT_TDM_RX_3 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("TERT_TDM_RX_4 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("TERT_TDM_RX_5 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("TERT_TDM_RX_6 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("TERT_TDM_RX_7 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("TERT_TDM_TX_0 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("TERT_TDM_TX_1 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("TERT_TDM_TX_2 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("TERT_TDM_TX_3 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("TERT_TDM_TX_4 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("TERT_TDM_TX_5 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("TERT_TDM_TX_6 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("TERT_TDM_TX_7 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("QUAT_TDM_RX_0 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("QUAT_TDM_RX_1 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("QUAT_TDM_RX_2 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("QUAT_TDM_RX_3 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("QUAT_TDM_RX_4 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("QUAT_TDM_RX_5 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("QUAT_TDM_RX_6 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("QUAT_TDM_RX_7 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("QUAT_TDM_TX_0 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("QUAT_TDM_TX_1 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("QUAT_TDM_TX_2 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("QUAT_TDM_TX_3 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("QUAT_TDM_TX_4 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("QUAT_TDM_TX_5 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("QUAT_TDM_TX_6 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("QUAT_TDM_TX_7 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("QUIN_TDM_RX_0 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("QUIN_TDM_RX_1 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("QUIN_TDM_RX_2 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("QUIN_TDM_RX_3 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("QUIN_TDM_RX_4 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("QUIN_TDM_RX_5 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("QUIN_TDM_RX_6 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("QUIN_TDM_RX_7 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("QUIN_TDM_TX_0 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("QUIN_TDM_TX_1 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("QUIN_TDM_TX_2 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("QUIN_TDM_TX_3 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("QUIN_TDM_TX_4 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("QUIN_TDM_TX_5 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("QUIN_TDM_TX_6 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("QUIN_TDM_TX_7 Header", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, + msm_dai_q6_tdm_header_get, + msm_dai_q6_tdm_header_put), +}; + +static int msm_dai_q6_tdm_set_clk( + struct msm_dai_q6_tdm_dai_data *dai_data, + u16 port_id, bool enable) +{ + int rc = 0; + + dai_data->clk_set.enable = enable; + + rc = afe_set_lpass_clock_v2(port_id, + &dai_data->clk_set); + if (rc < 0) + pr_err("%s: afe lpass clock failed, err:%d\n", + __func__, rc); + + return rc; +} + +static int msm_dai_q6_dai_tdm_probe(struct snd_soc_dai *dai) +{ + int rc = 0; + struct msm_dai_q6_tdm_dai_data *tdm_dai_data = NULL; + struct snd_kcontrol *data_format_kcontrol = NULL; + struct snd_kcontrol *header_type_kcontrol = NULL; + struct snd_kcontrol *header_kcontrol = NULL; + int port_idx = 0; + const struct snd_kcontrol_new *data_format_ctrl = NULL; + const struct snd_kcontrol_new *header_type_ctrl = NULL; + const struct snd_kcontrol_new *header_ctrl = NULL; + + tdm_dai_data = dev_get_drvdata(dai->dev); + + msm_dai_q6_set_dai_id(dai); + + port_idx = msm_dai_q6_get_port_idx(dai->id); + if (port_idx < 0) { + dev_err(dai->dev, "%s port id 0x%x not supported\n", + __func__, dai->id); + rc = -EINVAL; + goto rtn; + } + + data_format_ctrl = + &tdm_config_controls_data_format[port_idx]; + header_type_ctrl = + &tdm_config_controls_header_type[port_idx]; + header_ctrl = + &tdm_config_controls_header[port_idx]; + + if (data_format_ctrl) { + data_format_kcontrol = snd_ctl_new1(data_format_ctrl, + tdm_dai_data); + rc = snd_ctl_add(dai->component->card->snd_card, + data_format_kcontrol); + if (rc < 0) { + dev_err(dai->dev, "%s: err add data format ctrl DAI = %s\n", + __func__, dai->name); + goto rtn; + } + } + + if (header_type_ctrl) { + header_type_kcontrol = snd_ctl_new1(header_type_ctrl, + tdm_dai_data); + rc = snd_ctl_add(dai->component->card->snd_card, + header_type_kcontrol); + if (rc < 0) { + if (data_format_kcontrol) + snd_ctl_remove(dai->component->card->snd_card, + data_format_kcontrol); + dev_err(dai->dev, "%s: err add header type ctrl DAI = %s\n", + __func__, dai->name); + goto rtn; + } + } + + if (header_ctrl) { + header_kcontrol = snd_ctl_new1(header_ctrl, + tdm_dai_data); + rc = snd_ctl_add(dai->component->card->snd_card, + header_kcontrol); + if (rc < 0) { + if (header_type_kcontrol) + snd_ctl_remove(dai->component->card->snd_card, + header_type_kcontrol); + if (data_format_kcontrol) + snd_ctl_remove(dai->component->card->snd_card, + data_format_kcontrol); + dev_err(dai->dev, "%s: err add header ctrl DAI = %s\n", + __func__, dai->name); + goto rtn; + } + } + + if (tdm_dai_data->is_island_dai) + rc = msm_dai_q6_add_island_mx_ctls( + dai->component->card->snd_card, + dai->name, + dai->id, (void *)tdm_dai_data); + + rc = msm_dai_q6_dai_add_route(dai); + +rtn: + return rc; +} + + +static int msm_dai_q6_dai_tdm_remove(struct snd_soc_dai *dai) +{ + int rc = 0; + struct msm_dai_q6_tdm_dai_data *tdm_dai_data = + dev_get_drvdata(dai->dev); + u16 group_id = tdm_dai_data->group_cfg.tdm_cfg.group_id; + int group_idx = 0; + atomic_t *group_ref = NULL; + + group_idx = msm_dai_q6_get_group_idx(dai->id); + if (group_idx < 0) { + dev_err(dai->dev, "%s port id 0x%x not supported\n", + __func__, dai->id); + return -EINVAL; + } + + group_ref = &tdm_group_ref[group_idx]; + + /* If AFE port is still up, close it */ + if (test_bit(STATUS_PORT_STARTED, tdm_dai_data->status_mask)) { + rc = afe_close(dai->id); /* can block */ + if (rc < 0) { + dev_err(dai->dev, "%s: fail to close AFE port 0x%x\n", + __func__, dai->id); + } + atomic_dec(group_ref); + clear_bit(STATUS_PORT_STARTED, + tdm_dai_data->status_mask); + + if (atomic_read(group_ref) == 0) { + rc = afe_port_group_enable(group_id, + NULL, false); + if (rc < 0) { + dev_err(dai->dev, "fail to disable AFE group 0x%x\n", + group_id); + } + } + + if (msm_dai_q6_get_tdm_clk_ref(group_idx) == 0) { + rc = msm_dai_q6_tdm_set_clk(tdm_dai_data, + dai->id, false); + if (rc < 0) { + dev_err(dai->dev, "%s: fail to disable AFE clk 0x%x\n", + __func__, dai->id); + } + } + } + + return 0; +} + +static int msm_dai_q6_tdm_set_tdm_slot(struct snd_soc_dai *dai, + unsigned int tx_mask, + unsigned int rx_mask, + int slots, int slot_width) +{ + int rc = 0; + struct msm_dai_q6_tdm_dai_data *dai_data = + dev_get_drvdata(dai->dev); + struct afe_param_id_group_device_tdm_cfg *tdm_group = + &dai_data->group_cfg.tdm_cfg; + unsigned int cap_mask; + + dev_dbg(dai->dev, "%s: dai id = 0x%x\n", __func__, dai->id); + + /* HW only supports 16 and 32 bit slot width configuration */ + if ((slot_width != 16) && (slot_width != 32)) { + dev_err(dai->dev, "%s: invalid slot_width %d\n", + __func__, slot_width); + return -EINVAL; + } + + /* HW supports 1-32 slots configuration. Typical: 1, 2, 4, 8, 16, 32 */ + switch (slots) { + case 1: + cap_mask = 0x01; + break; + case 2: + cap_mask = 0x03; + break; + case 4: + cap_mask = 0x0F; + break; + case 8: + cap_mask = 0xFF; + break; + case 16: + cap_mask = 0xFFFF; + break; + default: + dev_err(dai->dev, "%s: invalid slots %d\n", + __func__, slots); + return -EINVAL; + } + + switch (dai->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: + 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: + 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: + 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: + case AFE_PORT_ID_QUINARY_TDM_RX: + case AFE_PORT_ID_QUINARY_TDM_RX_1: + case AFE_PORT_ID_QUINARY_TDM_RX_2: + case AFE_PORT_ID_QUINARY_TDM_RX_3: + case AFE_PORT_ID_QUINARY_TDM_RX_4: + case AFE_PORT_ID_QUINARY_TDM_RX_5: + case AFE_PORT_ID_QUINARY_TDM_RX_6: + case AFE_PORT_ID_QUINARY_TDM_RX_7: + tdm_group->nslots_per_frame = slots; + tdm_group->slot_width = slot_width; + tdm_group->slot_mask = rx_mask & cap_mask; + break; + case AFE_PORT_ID_PRIMARY_TDM_TX: + case AFE_PORT_ID_PRIMARY_TDM_TX_1: + case AFE_PORT_ID_PRIMARY_TDM_TX_2: + case AFE_PORT_ID_PRIMARY_TDM_TX_3: + case AFE_PORT_ID_PRIMARY_TDM_TX_4: + case AFE_PORT_ID_PRIMARY_TDM_TX_5: + case AFE_PORT_ID_PRIMARY_TDM_TX_6: + case AFE_PORT_ID_PRIMARY_TDM_TX_7: + case AFE_PORT_ID_SECONDARY_TDM_TX: + case AFE_PORT_ID_SECONDARY_TDM_TX_1: + case AFE_PORT_ID_SECONDARY_TDM_TX_2: + case AFE_PORT_ID_SECONDARY_TDM_TX_3: + case AFE_PORT_ID_SECONDARY_TDM_TX_4: + case AFE_PORT_ID_SECONDARY_TDM_TX_5: + case AFE_PORT_ID_SECONDARY_TDM_TX_6: + case AFE_PORT_ID_SECONDARY_TDM_TX_7: + case AFE_PORT_ID_TERTIARY_TDM_TX: + case AFE_PORT_ID_TERTIARY_TDM_TX_1: + case AFE_PORT_ID_TERTIARY_TDM_TX_2: + case AFE_PORT_ID_TERTIARY_TDM_TX_3: + case AFE_PORT_ID_TERTIARY_TDM_TX_4: + case AFE_PORT_ID_TERTIARY_TDM_TX_5: + case AFE_PORT_ID_TERTIARY_TDM_TX_6: + case AFE_PORT_ID_TERTIARY_TDM_TX_7: + case AFE_PORT_ID_QUATERNARY_TDM_TX: + case AFE_PORT_ID_QUATERNARY_TDM_TX_1: + case AFE_PORT_ID_QUATERNARY_TDM_TX_2: + case AFE_PORT_ID_QUATERNARY_TDM_TX_3: + case AFE_PORT_ID_QUATERNARY_TDM_TX_4: + case AFE_PORT_ID_QUATERNARY_TDM_TX_5: + case AFE_PORT_ID_QUATERNARY_TDM_TX_6: + case AFE_PORT_ID_QUATERNARY_TDM_TX_7: + case AFE_PORT_ID_QUINARY_TDM_TX: + case AFE_PORT_ID_QUINARY_TDM_TX_1: + case AFE_PORT_ID_QUINARY_TDM_TX_2: + case AFE_PORT_ID_QUINARY_TDM_TX_3: + case AFE_PORT_ID_QUINARY_TDM_TX_4: + case AFE_PORT_ID_QUINARY_TDM_TX_5: + case AFE_PORT_ID_QUINARY_TDM_TX_6: + case AFE_PORT_ID_QUINARY_TDM_TX_7: + tdm_group->nslots_per_frame = slots; + tdm_group->slot_width = slot_width; + tdm_group->slot_mask = tx_mask & cap_mask; + break; + default: + dev_err(dai->dev, "%s: invalid dai id 0x%x\n", + __func__, dai->id); + return -EINVAL; + } + + return rc; +} + +static int msm_dai_q6_tdm_set_sysclk(struct snd_soc_dai *dai, + int clk_id, unsigned int freq, int dir) +{ + struct msm_dai_q6_tdm_dai_data *dai_data = + dev_get_drvdata(dai->dev); + + if ((dai->id >= AFE_PORT_ID_PRIMARY_TDM_RX) && + (dai->id <= AFE_PORT_ID_QUINARY_TDM_TX_7)) { + dai_data->clk_set.clk_freq_in_hz = freq; + } else { + dev_err(dai->dev, "%s: invalid dai id 0x%x\n", + __func__, dai->id); + return -EINVAL; + } + + dev_dbg(dai->dev, "%s: dai id = 0x%x, group clk_freq = %d\n", + __func__, dai->id, freq); + return 0; +} + +static int msm_dai_q6_tdm_set_channel_map(struct snd_soc_dai *dai, + unsigned int tx_num, unsigned int *tx_slot, + unsigned int rx_num, unsigned int *rx_slot) +{ + int rc = 0; + struct msm_dai_q6_tdm_dai_data *dai_data = + dev_get_drvdata(dai->dev); + struct afe_param_id_slot_mapping_cfg *slot_mapping = + &dai_data->port_cfg.slot_mapping; + int i = 0; + + dev_dbg(dai->dev, "%s: dai id = 0x%x\n", __func__, dai->id); + + switch (dai->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: + 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: + 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: + 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: + case AFE_PORT_ID_QUINARY_TDM_RX: + case AFE_PORT_ID_QUINARY_TDM_RX_1: + case AFE_PORT_ID_QUINARY_TDM_RX_2: + case AFE_PORT_ID_QUINARY_TDM_RX_3: + case AFE_PORT_ID_QUINARY_TDM_RX_4: + case AFE_PORT_ID_QUINARY_TDM_RX_5: + case AFE_PORT_ID_QUINARY_TDM_RX_6: + case AFE_PORT_ID_QUINARY_TDM_RX_7: + if (!rx_slot) { + dev_err(dai->dev, "%s: rx slot not found\n", __func__); + return -EINVAL; + } + if (rx_num > AFE_PORT_MAX_AUDIO_CHAN_CNT) { + dev_err(dai->dev, "%s: invalid rx num %d\n", __func__, + rx_num); + return -EINVAL; + } + + for (i = 0; i < rx_num; i++) + slot_mapping->offset[i] = rx_slot[i]; + for (i = rx_num; i < AFE_PORT_MAX_AUDIO_CHAN_CNT; i++) + slot_mapping->offset[i] = + AFE_SLOT_MAPPING_OFFSET_INVALID; + + slot_mapping->num_channel = rx_num; + break; + case AFE_PORT_ID_PRIMARY_TDM_TX: + case AFE_PORT_ID_PRIMARY_TDM_TX_1: + case AFE_PORT_ID_PRIMARY_TDM_TX_2: + case AFE_PORT_ID_PRIMARY_TDM_TX_3: + case AFE_PORT_ID_PRIMARY_TDM_TX_4: + case AFE_PORT_ID_PRIMARY_TDM_TX_5: + case AFE_PORT_ID_PRIMARY_TDM_TX_6: + case AFE_PORT_ID_PRIMARY_TDM_TX_7: + case AFE_PORT_ID_SECONDARY_TDM_TX: + case AFE_PORT_ID_SECONDARY_TDM_TX_1: + case AFE_PORT_ID_SECONDARY_TDM_TX_2: + case AFE_PORT_ID_SECONDARY_TDM_TX_3: + case AFE_PORT_ID_SECONDARY_TDM_TX_4: + case AFE_PORT_ID_SECONDARY_TDM_TX_5: + case AFE_PORT_ID_SECONDARY_TDM_TX_6: + case AFE_PORT_ID_SECONDARY_TDM_TX_7: + case AFE_PORT_ID_TERTIARY_TDM_TX: + case AFE_PORT_ID_TERTIARY_TDM_TX_1: + case AFE_PORT_ID_TERTIARY_TDM_TX_2: + case AFE_PORT_ID_TERTIARY_TDM_TX_3: + case AFE_PORT_ID_TERTIARY_TDM_TX_4: + case AFE_PORT_ID_TERTIARY_TDM_TX_5: + case AFE_PORT_ID_TERTIARY_TDM_TX_6: + case AFE_PORT_ID_TERTIARY_TDM_TX_7: + case AFE_PORT_ID_QUATERNARY_TDM_TX: + case AFE_PORT_ID_QUATERNARY_TDM_TX_1: + case AFE_PORT_ID_QUATERNARY_TDM_TX_2: + case AFE_PORT_ID_QUATERNARY_TDM_TX_3: + case AFE_PORT_ID_QUATERNARY_TDM_TX_4: + case AFE_PORT_ID_QUATERNARY_TDM_TX_5: + case AFE_PORT_ID_QUATERNARY_TDM_TX_6: + case AFE_PORT_ID_QUATERNARY_TDM_TX_7: + case AFE_PORT_ID_QUINARY_TDM_TX: + case AFE_PORT_ID_QUINARY_TDM_TX_1: + case AFE_PORT_ID_QUINARY_TDM_TX_2: + case AFE_PORT_ID_QUINARY_TDM_TX_3: + case AFE_PORT_ID_QUINARY_TDM_TX_4: + case AFE_PORT_ID_QUINARY_TDM_TX_5: + case AFE_PORT_ID_QUINARY_TDM_TX_6: + case AFE_PORT_ID_QUINARY_TDM_TX_7: + if (!tx_slot) { + dev_err(dai->dev, "%s: tx slot not found\n", __func__); + return -EINVAL; + } + if (tx_num > AFE_PORT_MAX_AUDIO_CHAN_CNT) { + dev_err(dai->dev, "%s: invalid tx num %d\n", __func__, + tx_num); + return -EINVAL; + } + + for (i = 0; i < tx_num; i++) + slot_mapping->offset[i] = tx_slot[i]; + for (i = tx_num; i < AFE_PORT_MAX_AUDIO_CHAN_CNT; i++) + slot_mapping->offset[i] = + AFE_SLOT_MAPPING_OFFSET_INVALID; + + slot_mapping->num_channel = tx_num; + break; + default: + dev_err(dai->dev, "%s: invalid dai id 0x%x\n", + __func__, dai->id); + return -EINVAL; + } + + return rc; +} + +static unsigned int tdm_param_set_slot_mask(u16 *slot_offset, int slot_width, + int slots_per_frame) +{ + unsigned int i = 0; + unsigned int slot_index = 0; + unsigned long slot_mask = 0; + unsigned int slot_width_bytes = slot_width / 8; + + for (i = 0; i < AFE_PORT_MAX_AUDIO_CHAN_CNT; i++) { + if (slot_offset[i] != AFE_SLOT_MAPPING_OFFSET_INVALID) { + slot_index = slot_offset[i] / slot_width_bytes; + if (slot_index < slots_per_frame) + set_bit(slot_index, &slot_mask); + else { + pr_err("%s: invalid slot map setting\n", + __func__); + return 0; + } + } else + break; + } + + return slot_mask; +} + +static int msm_dai_q6_tdm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct msm_dai_q6_tdm_dai_data *dai_data = + dev_get_drvdata(dai->dev); + + struct afe_param_id_group_device_tdm_cfg *tdm_group = + &dai_data->group_cfg.tdm_cfg; + struct afe_param_id_tdm_cfg *tdm = + &dai_data->port_cfg.tdm; + struct afe_param_id_slot_mapping_cfg *slot_mapping = + &dai_data->port_cfg.slot_mapping; + struct afe_param_id_custom_tdm_header_cfg *custom_tdm_header = + &dai_data->port_cfg.custom_tdm_header; + + pr_debug("%s: dev_name: %s\n", + __func__, dev_name(dai->dev)); + + if ((params_channels(params) == 0) || + (params_channels(params) > 8)) { + dev_err(dai->dev, "%s: invalid param channels %d\n", + __func__, params_channels(params)); + return -EINVAL; + } + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + dai_data->bitwidth = 16; + break; + case SNDRV_PCM_FORMAT_S24_LE: + case SNDRV_PCM_FORMAT_S24_3LE: + dai_data->bitwidth = 24; + break; + case SNDRV_PCM_FORMAT_S32_LE: + dai_data->bitwidth = 32; + break; + default: + dev_err(dai->dev, "%s: invalid param format 0x%x\n", + __func__, params_format(params)); + return -EINVAL; + } + dai_data->channels = params_channels(params); + dai_data->rate = params_rate(params); + + /* + * update tdm group config param + * NOTE: group config is set to the same as slot config. + */ + tdm_group->bit_width = tdm_group->slot_width; + tdm_group->num_channels = tdm_group->nslots_per_frame; + tdm_group->sample_rate = dai_data->rate; + + pr_debug("%s: TDM GROUP:\n" + "num_channels=%d sample_rate=%d bit_width=%d\n" + "nslots_per_frame=%d slot_width=%d slot_mask=0x%x\n", + __func__, + tdm_group->num_channels, + tdm_group->sample_rate, + tdm_group->bit_width, + tdm_group->nslots_per_frame, + tdm_group->slot_width, + tdm_group->slot_mask); + pr_debug("%s: TDM GROUP:\n" + "port_id[0]=0x%x port_id[1]=0x%x port_id[2]=0x%x port_id[3]=0x%x\n" + "port_id[4]=0x%x port_id[5]=0x%x port_id[6]=0x%x port_id[7]=0x%x\n", + __func__, + tdm_group->port_id[0], + tdm_group->port_id[1], + tdm_group->port_id[2], + tdm_group->port_id[3], + tdm_group->port_id[4], + tdm_group->port_id[5], + tdm_group->port_id[6], + tdm_group->port_id[7]); + + /* + * update tdm config param + * NOTE: channels/rate/bitwidth are per stream property + */ + tdm->num_channels = dai_data->channels; + tdm->sample_rate = dai_data->rate; + tdm->bit_width = dai_data->bitwidth; + /* + * port slot config is the same as group slot config + * port slot mask should be set according to offset + */ + tdm->nslots_per_frame = tdm_group->nslots_per_frame; + tdm->slot_width = tdm_group->slot_width; + tdm->slot_mask = tdm_param_set_slot_mask(slot_mapping->offset, + tdm_group->slot_width, + tdm_group->nslots_per_frame); + + pr_debug("%s: TDM:\n" + "num_channels=%d sample_rate=%d bit_width=%d\n" + "nslots_per_frame=%d slot_width=%d slot_mask=0x%x\n" + "data_format=0x%x sync_mode=0x%x sync_src=0x%x\n" + "data_out=0x%x invert_sync=0x%x data_delay=0x%x\n", + __func__, + tdm->num_channels, + tdm->sample_rate, + tdm->bit_width, + tdm->nslots_per_frame, + tdm->slot_width, + tdm->slot_mask, + tdm->data_format, + tdm->sync_mode, + tdm->sync_src, + tdm->ctrl_data_out_enable, + tdm->ctrl_invert_sync_pulse, + tdm->ctrl_sync_data_delay); + + /* + * update slot mapping config param + * NOTE: channels/rate/bitwidth are per stream property + */ + slot_mapping->bitwidth = dai_data->bitwidth; + + pr_debug("%s: SLOT MAPPING:\n" + "num_channel=%d bitwidth=%d data_align=0x%x\n", + __func__, + slot_mapping->num_channel, + slot_mapping->bitwidth, + slot_mapping->data_align_type); + pr_debug("%s: SLOT MAPPING:\n" + "offset[0]=0x%x offset[1]=0x%x offset[2]=0x%x offset[3]=0x%x\n" + "offset[4]=0x%x offset[5]=0x%x offset[6]=0x%x offset[7]=0x%x\n", + __func__, + slot_mapping->offset[0], + slot_mapping->offset[1], + slot_mapping->offset[2], + slot_mapping->offset[3], + slot_mapping->offset[4], + slot_mapping->offset[5], + slot_mapping->offset[6], + slot_mapping->offset[7]); + + /* + * update custom header config param + * NOTE: channels/rate/bitwidth are per playback stream property. + * custom tdm header only applicable to playback stream. + */ + if (custom_tdm_header->header_type != + AFE_CUSTOM_TDM_HEADER_TYPE_INVALID) { + pr_debug("%s: CUSTOM TDM HEADER:\n" + "start_offset=0x%x header_width=%d\n" + "num_frame_repeat=%d header_type=0x%x\n", + __func__, + custom_tdm_header->start_offset, + custom_tdm_header->header_width, + custom_tdm_header->num_frame_repeat, + custom_tdm_header->header_type); + pr_debug("%s: CUSTOM TDM HEADER:\n" + "header[0]=0x%x header[1]=0x%x header[2]=0x%x header[3]=0x%x\n" + "header[4]=0x%x header[5]=0x%x header[6]=0x%x header[7]=0x%x\n", + __func__, + custom_tdm_header->header[0], + custom_tdm_header->header[1], + custom_tdm_header->header[2], + custom_tdm_header->header[3], + custom_tdm_header->header[4], + custom_tdm_header->header[5], + custom_tdm_header->header[6], + custom_tdm_header->header[7]); + } + + return 0; +} + +static int msm_dai_q6_tdm_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + int rc = 0; + struct msm_dai_q6_tdm_dai_data *dai_data = + dev_get_drvdata(dai->dev); + u16 group_id = dai_data->group_cfg.tdm_cfg.group_id; + int group_idx = 0; + atomic_t *group_ref = NULL; + + dev_dbg(dai->dev, "%s: dev_name: %s dev_id: 0x%x group_id: 0x%x\n", + __func__, dev_name(dai->dev), dai->dev->id, group_id); + + if (dai_data->port_cfg.custom_tdm_header.minor_version == 0) + dev_dbg(dai->dev, + "%s: Custom tdm header not supported\n", __func__); + + group_idx = msm_dai_q6_get_group_idx(dai->id); + if (group_idx < 0) { + dev_err(dai->dev, "%s port id 0x%x not supported\n", + __func__, dai->id); + return -EINVAL; + } + + mutex_lock(&tdm_mutex); + + group_ref = &tdm_group_ref[group_idx]; + + if (!test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) { + if (q6core_get_avcs_api_version_per_service( + APRV2_IDS_SERVICE_ID_ADSP_AFE_V) >= AFE_API_VERSION_V4) { + /* + * send island mode config. + * This should be the first configuration + */ + rc = afe_send_port_island_mode(dai->id); + if (rc) + dev_err(dai->dev, "%s: afe send island mode failed %d\n", + __func__, rc); + } + + if (msm_dai_q6_get_tdm_clk_ref(group_idx) == 0) { + /* TX and RX share the same clk. So enable the clk + * per TDM interface. */ + rc = msm_dai_q6_tdm_set_clk(dai_data, + dai->id, true); + if (rc < 0) { + dev_err(dai->dev, "%s: fail to enable AFE clk 0x%x\n", + __func__, dai->id); + goto rtn; + } + } + + /* PORT START should be set if prepare called + * in active state. + */ + if (atomic_read(group_ref) == 0) { + /* + * if only one port, don't do group enable as there + * is no group need for only one port + */ + if (dai_data->num_group_ports > 1) { + rc = afe_port_group_enable(group_id, + &dai_data->group_cfg, true); + if (rc < 0) { + dev_err(dai->dev, + "%s: fail to enable AFE group 0x%x\n", + __func__, group_id); + goto rtn; + } + } + } + + rc = afe_tdm_port_start(dai->id, &dai_data->port_cfg, + dai_data->rate, dai_data->num_group_ports); + if (rc < 0) { + if (atomic_read(group_ref) == 0) { + afe_port_group_enable(group_id, + NULL, false); + } + if (msm_dai_q6_get_tdm_clk_ref(group_idx) == 0) { + msm_dai_q6_tdm_set_clk(dai_data, + dai->id, false); + } + dev_err(dai->dev, "%s: fail to open AFE port 0x%x\n", + __func__, dai->id); + } else { + set_bit(STATUS_PORT_STARTED, + dai_data->status_mask); + atomic_inc(group_ref); + } + + /* TODO: need to monitor PCM/MI2S/TDM HW status */ + /* NOTE: AFE should error out if HW resource contention */ + + } + +rtn: + mutex_unlock(&tdm_mutex); + return rc; +} + +static void msm_dai_q6_tdm_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + int rc = 0; + struct msm_dai_q6_tdm_dai_data *dai_data = + dev_get_drvdata(dai->dev); + u16 group_id = dai_data->group_cfg.tdm_cfg.group_id; + int group_idx = 0; + atomic_t *group_ref = NULL; + + group_idx = msm_dai_q6_get_group_idx(dai->id); + if (group_idx < 0) { + dev_err(dai->dev, "%s port id 0x%x not supported\n", + __func__, dai->id); + return; + } + + mutex_lock(&tdm_mutex); + + group_ref = &tdm_group_ref[group_idx]; + + if (test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) { + rc = afe_close(dai->id); + if (rc < 0) { + dev_err(dai->dev, "%s: fail to close AFE port 0x%x\n", + __func__, dai->id); + } + atomic_dec(group_ref); + clear_bit(STATUS_PORT_STARTED, + dai_data->status_mask); + + if (atomic_read(group_ref) == 0) { + rc = afe_port_group_enable(group_id, + NULL, false); + if (rc < 0) { + dev_err(dai->dev, "%s: fail to disable AFE group 0x%x\n", + __func__, group_id); + } + } + + if (msm_dai_q6_get_tdm_clk_ref(group_idx) == 0) { + rc = msm_dai_q6_tdm_set_clk(dai_data, + dai->id, false); + if (rc < 0) { + dev_err(dai->dev, "%s: fail to disable AFE clk 0x%x\n", + __func__, dai->id); + } + } + + /* TODO: need to monitor PCM/MI2S/TDM HW status */ + /* NOTE: AFE should error out if HW resource contention */ + + } + + mutex_unlock(&tdm_mutex); +} + +static struct snd_soc_dai_ops msm_dai_q6_tdm_ops = { + .prepare = msm_dai_q6_tdm_prepare, + .hw_params = msm_dai_q6_tdm_hw_params, + .set_tdm_slot = msm_dai_q6_tdm_set_tdm_slot, + .set_channel_map = msm_dai_q6_tdm_set_channel_map, + .set_sysclk = msm_dai_q6_tdm_set_sysclk, + .shutdown = msm_dai_q6_tdm_shutdown, +}; + +static struct snd_soc_dai_driver msm_dai_q6_tdm_dai[] = { + { + .playback = { + .stream_name = "Primary TDM0 Playback", + .aif_name = "PRI_TDM_RX_0", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 352800, + }, + .name = "PRI_TDM_RX_0", + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_PRIMARY_TDM_RX, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .playback = { + .stream_name = "Primary TDM1 Playback", + .aif_name = "PRI_TDM_RX_1", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 352800, + }, + .name = "PRI_TDM_RX_1", + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_PRIMARY_TDM_RX_1, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .playback = { + .stream_name = "Primary TDM2 Playback", + .aif_name = "PRI_TDM_RX_2", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 352800, + }, + .name = "PRI_TDM_RX_2", + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_PRIMARY_TDM_RX_2, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .playback = { + .stream_name = "Primary TDM3 Playback", + .aif_name = "PRI_TDM_RX_3", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 352800, + }, + .name = "PRI_TDM_RX_3", + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_PRIMARY_TDM_RX_3, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .playback = { + .stream_name = "Primary TDM4 Playback", + .aif_name = "PRI_TDM_RX_4", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 352800, + }, + .name = "PRI_TDM_RX_4", + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_PRIMARY_TDM_RX_4, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .playback = { + .stream_name = "Primary TDM5 Playback", + .aif_name = "PRI_TDM_RX_5", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 352800, + }, + .name = "PRI_TDM_RX_5", + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_PRIMARY_TDM_RX_5, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .playback = { + .stream_name = "Primary TDM6 Playback", + .aif_name = "PRI_TDM_RX_6", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 352800, + }, + .name = "PRI_TDM_RX_6", + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_PRIMARY_TDM_RX_6, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .playback = { + .stream_name = "Primary TDM7 Playback", + .aif_name = "PRI_TDM_RX_7", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 352800, + }, + .name = "PRI_TDM_RX_7", + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_PRIMARY_TDM_RX_7, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .capture = { + .stream_name = "Primary TDM0 Capture", + .aif_name = "PRI_TDM_TX_0", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 352800, + }, + .name = "PRI_TDM_TX_0", + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_PRIMARY_TDM_TX, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .capture = { + .stream_name = "Primary TDM1 Capture", + .aif_name = "PRI_TDM_TX_1", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 352800, + }, + .name = "PRI_TDM_TX_1", + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_PRIMARY_TDM_TX_1, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .capture = { + .stream_name = "Primary TDM2 Capture", + .aif_name = "PRI_TDM_TX_2", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 352800, + }, + .name = "PRI_TDM_TX_2", + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_PRIMARY_TDM_TX_2, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .capture = { + .stream_name = "Primary TDM3 Capture", + .aif_name = "PRI_TDM_TX_3", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 352800, + }, + .name = "PRI_TDM_TX_3", + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_PRIMARY_TDM_TX_3, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .capture = { + .stream_name = "Primary TDM4 Capture", + .aif_name = "PRI_TDM_TX_4", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 352800, + }, + .name = "PRI_TDM_TX_4", + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_PRIMARY_TDM_TX_4, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .capture = { + .stream_name = "Primary TDM5 Capture", + .aif_name = "PRI_TDM_TX_5", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 352800, + }, + .name = "PRI_TDM_TX_5", + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_PRIMARY_TDM_TX_5, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .capture = { + .stream_name = "Primary TDM6 Capture", + .aif_name = "PRI_TDM_TX_6", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 352800, + }, + .name = "PRI_TDM_TX_6", + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_PRIMARY_TDM_TX_6, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .capture = { + .stream_name = "Primary TDM7 Capture", + .aif_name = "PRI_TDM_TX_7", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 352800, + }, + .name = "PRI_TDM_TX_7", + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_PRIMARY_TDM_TX_7, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .playback = { + .stream_name = "Secondary TDM0 Playback", + .aif_name = "SEC_TDM_RX_0", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 352800, + }, + .name = "SEC_TDM_RX_0", + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_SECONDARY_TDM_RX, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .playback = { + .stream_name = "Secondary TDM1 Playback", + .aif_name = "SEC_TDM_RX_1", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 352800, + }, + .name = "SEC_TDM_RX_1", + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_SECONDARY_TDM_RX_1, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .playback = { + .stream_name = "Secondary TDM2 Playback", + .aif_name = "SEC_TDM_RX_2", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 352800, + }, + .name = "SEC_TDM_RX_2", + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_SECONDARY_TDM_RX_2, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .playback = { + .stream_name = "Secondary TDM3 Playback", + .aif_name = "SEC_TDM_RX_3", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 352800, + }, + .name = "SEC_TDM_RX_3", + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_SECONDARY_TDM_RX_3, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .playback = { + .stream_name = "Secondary TDM4 Playback", + .aif_name = "SEC_TDM_RX_4", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 352800, + }, + .name = "SEC_TDM_RX_4", + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_SECONDARY_TDM_RX_4, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .playback = { + .stream_name = "Secondary TDM5 Playback", + .aif_name = "SEC_TDM_RX_5", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 352800, + }, + .name = "SEC_TDM_RX_5", + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_SECONDARY_TDM_RX_5, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .playback = { + .stream_name = "Secondary TDM6 Playback", + .aif_name = "SEC_TDM_RX_6", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 352800, + }, + .name = "SEC_TDM_RX_6", + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_SECONDARY_TDM_RX_6, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .playback = { + .stream_name = "Secondary TDM7 Playback", + .aif_name = "SEC_TDM_RX_7", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 352800, + }, + .name = "SEC_TDM_RX_7", + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_SECONDARY_TDM_RX_7, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .capture = { + .stream_name = "Secondary TDM0 Capture", + .aif_name = "SEC_TDM_TX_0", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 352800, + }, + .name = "SEC_TDM_TX_0", + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_SECONDARY_TDM_TX, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .capture = { + .stream_name = "Secondary TDM1 Capture", + .aif_name = "SEC_TDM_TX_1", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 352800, + }, + .name = "SEC_TDM_TX_1", + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_SECONDARY_TDM_TX_1, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .capture = { + .stream_name = "Secondary TDM2 Capture", + .aif_name = "SEC_TDM_TX_2", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 352800, + }, + .name = "SEC_TDM_TX_2", + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_SECONDARY_TDM_TX_2, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .capture = { + .stream_name = "Secondary TDM3 Capture", + .aif_name = "SEC_TDM_TX_3", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 352800, + }, + .name = "SEC_TDM_TX_3", + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_SECONDARY_TDM_TX_3, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .capture = { + .stream_name = "Secondary TDM4 Capture", + .aif_name = "SEC_TDM_TX_4", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 352800, + }, + .name = "SEC_TDM_TX_4", + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_SECONDARY_TDM_TX_4, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .capture = { + .stream_name = "Secondary TDM5 Capture", + .aif_name = "SEC_TDM_TX_5", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 352800, + }, + .name = "SEC_TDM_TX_5", + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_SECONDARY_TDM_TX_5, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .capture = { + .stream_name = "Secondary TDM6 Capture", + .aif_name = "SEC_TDM_TX_6", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 352800, + }, + .name = "SEC_TDM_TX_6", + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_SECONDARY_TDM_TX_6, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .capture = { + .stream_name = "Secondary TDM7 Capture", + .aif_name = "SEC_TDM_TX_7", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 352800, + }, + .name = "SEC_TDM_TX_7", + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_SECONDARY_TDM_TX_7, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .playback = { + .stream_name = "Tertiary TDM0 Playback", + .aif_name = "TERT_TDM_RX_0", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 352800, + }, + .name = "TERT_TDM_RX_0", + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_TERTIARY_TDM_RX, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .playback = { + .stream_name = "Tertiary TDM1 Playback", + .aif_name = "TERT_TDM_RX_1", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 352800, + }, + .name = "TERT_TDM_RX_1", + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_TERTIARY_TDM_RX_1, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .playback = { + .stream_name = "Tertiary TDM2 Playback", + .aif_name = "TERT_TDM_RX_2", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 352800, + }, + .name = "TERT_TDM_RX_2", + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_TERTIARY_TDM_RX_2, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .playback = { + .stream_name = "Tertiary TDM3 Playback", + .aif_name = "TERT_TDM_RX_3", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 352800, + }, + .name = "TERT_TDM_RX_3", + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_TERTIARY_TDM_RX_3, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .playback = { + .stream_name = "Tertiary TDM4 Playback", + .aif_name = "TERT_TDM_RX_4", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 352800, + }, + .name = "TERT_TDM_RX_4", + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_TERTIARY_TDM_RX_4, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .playback = { + .stream_name = "Tertiary TDM5 Playback", + .aif_name = "TERT_TDM_RX_5", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 352800, + }, + .name = "TERT_TDM_RX_5", + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_TERTIARY_TDM_RX_5, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .playback = { + .stream_name = "Tertiary TDM6 Playback", + .aif_name = "TERT_TDM_RX_6", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 352800, + }, + .name = "TERT_TDM_RX_6", + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_TERTIARY_TDM_RX_6, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .playback = { + .stream_name = "Tertiary TDM7 Playback", + .aif_name = "TERT_TDM_RX_7", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 352800, + }, + .name = "TERT_TDM_RX_7", + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_TERTIARY_TDM_RX_7, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .capture = { + .stream_name = "Tertiary TDM0 Capture", + .aif_name = "TERT_TDM_TX_0", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 352800, + }, + .name = "TERT_TDM_TX_0", + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_TERTIARY_TDM_TX, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .capture = { + .stream_name = "Tertiary TDM1 Capture", + .aif_name = "TERT_TDM_TX_1", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 352800, + }, + .name = "TERT_TDM_TX_1", + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_TERTIARY_TDM_TX_1, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .capture = { + .stream_name = "Tertiary TDM2 Capture", + .aif_name = "TERT_TDM_TX_2", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 352800, + }, + .name = "TERT_TDM_TX_2", + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_TERTIARY_TDM_TX_2, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .capture = { + .stream_name = "Tertiary TDM3 Capture", + .aif_name = "TERT_TDM_TX_3", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 352800, + }, + .name = "TERT_TDM_TX_3", + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_TERTIARY_TDM_TX_3, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .capture = { + .stream_name = "Tertiary TDM4 Capture", + .aif_name = "TERT_TDM_TX_4", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 352800, + }, + .name = "TERT_TDM_TX_4", + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_TERTIARY_TDM_TX_4, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .capture = { + .stream_name = "Tertiary TDM5 Capture", + .aif_name = "TERT_TDM_TX_5", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 352800, + }, + .name = "TERT_TDM_TX_5", + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_TERTIARY_TDM_TX_5, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .capture = { + .stream_name = "Tertiary TDM6 Capture", + .aif_name = "TERT_TDM_TX_6", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 352800, + }, + .name = "TERT_TDM_TX_6", + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_TERTIARY_TDM_TX_6, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .capture = { + .stream_name = "Tertiary TDM7 Capture", + .aif_name = "TERT_TDM_TX_7", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 352800, + }, + .name = "TERT_TDM_TX_7", + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_TERTIARY_TDM_TX_7, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .playback = { + .stream_name = "Quaternary TDM0 Playback", + .aif_name = "QUAT_TDM_RX_0", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 352800, + }, + .name = "QUAT_TDM_RX_0", + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_QUATERNARY_TDM_RX, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .playback = { + .stream_name = "Quaternary TDM1 Playback", + .aif_name = "QUAT_TDM_RX_1", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 352800, + }, + .name = "QUAT_TDM_RX_1", + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_QUATERNARY_TDM_RX_1, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .playback = { + .stream_name = "Quaternary TDM2 Playback", + .aif_name = "QUAT_TDM_RX_2", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 352800, + }, + .name = "QUAT_TDM_RX_2", + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_QUATERNARY_TDM_RX_2, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .playback = { + .stream_name = "Quaternary TDM3 Playback", + .aif_name = "QUAT_TDM_RX_3", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 352800, + }, + .name = "QUAT_TDM_RX_3", + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_QUATERNARY_TDM_RX_3, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .playback = { + .stream_name = "Quaternary TDM4 Playback", + .aif_name = "QUAT_TDM_RX_4", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 352800, + }, + .name = "QUAT_TDM_RX_4", + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_QUATERNARY_TDM_RX_4, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .playback = { + .stream_name = "Quaternary TDM5 Playback", + .aif_name = "QUAT_TDM_RX_5", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 352800, + }, + .name = "QUAT_TDM_RX_5", + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_QUATERNARY_TDM_RX_5, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .playback = { + .stream_name = "Quaternary TDM6 Playback", + .aif_name = "QUAT_TDM_RX_6", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 352800, + }, + .name = "QUAT_TDM_RX_6", + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_QUATERNARY_TDM_RX_6, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .playback = { + .stream_name = "Quaternary TDM7 Playback", + .aif_name = "QUAT_TDM_RX_7", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 352800, + }, + .name = "QUAT_TDM_RX_7", + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_QUATERNARY_TDM_RX_7, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .capture = { + .stream_name = "Quaternary TDM0 Capture", + .aif_name = "QUAT_TDM_TX_0", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 352800, + }, + .name = "QUAT_TDM_TX_0", + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_QUATERNARY_TDM_TX, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .capture = { + .stream_name = "Quaternary TDM1 Capture", + .aif_name = "QUAT_TDM_TX_1", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 352800, + }, + .name = "QUAT_TDM_TX_1", + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_QUATERNARY_TDM_TX_1, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .capture = { + .stream_name = "Quaternary TDM2 Capture", + .aif_name = "QUAT_TDM_TX_2", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 352800, + }, + .name = "QUAT_TDM_TX_2", + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_QUATERNARY_TDM_TX_2, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .capture = { + .stream_name = "Quaternary TDM3 Capture", + .aif_name = "QUAT_TDM_TX_3", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 352800, + }, + .name = "QUAT_TDM_TX_3", + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_QUATERNARY_TDM_TX_3, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .capture = { + .stream_name = "Quaternary TDM4 Capture", + .aif_name = "QUAT_TDM_TX_4", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 352800, + }, + .name = "QUAT_TDM_TX_4", + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_QUATERNARY_TDM_TX_4, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .capture = { + .stream_name = "Quaternary TDM5 Capture", + .aif_name = "QUAT_TDM_TX_5", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 352800, + }, + .name = "QUAT_TDM_TX_5", + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_QUATERNARY_TDM_TX_5, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .capture = { + .stream_name = "Quaternary TDM6 Capture", + .aif_name = "QUAT_TDM_TX_6", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 352800, + }, + .name = "QUAT_TDM_TX_6", + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_QUATERNARY_TDM_TX_6, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .capture = { + .stream_name = "Quaternary TDM7 Capture", + .aif_name = "QUAT_TDM_TX_7", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 352800, + }, + .name = "QUAT_TDM_TX_7", + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_QUATERNARY_TDM_TX_7, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .playback = { + .stream_name = "Quinary TDM0 Playback", + .aif_name = "QUIN_TDM_RX_0", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 352800, + }, + .name = "QUIN_TDM_RX_0", + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_QUINARY_TDM_RX, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .playback = { + .stream_name = "Quinary TDM1 Playback", + .aif_name = "QUIN_TDM_RX_1", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 352800, + }, + .name = "QUIN_TDM_RX_1", + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_QUINARY_TDM_RX_1, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .playback = { + .stream_name = "Quinary TDM2 Playback", + .aif_name = "QUIN_TDM_RX_2", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 352800, + }, + .name = "QUIN_TDM_RX_2", + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_QUINARY_TDM_RX_2, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .playback = { + .stream_name = "Quinary TDM3 Playback", + .aif_name = "QUIN_TDM_RX_3", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 352800, + }, + .name = "QUIN_TDM_RX_3", + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_QUINARY_TDM_RX_3, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .playback = { + .stream_name = "Quinary TDM4 Playback", + .aif_name = "QUIN_TDM_RX_4", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 352800, + }, + .name = "QUIN_TDM_RX_4", + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_QUINARY_TDM_RX_4, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .playback = { + .stream_name = "Quinary TDM5 Playback", + .aif_name = "QUIN_TDM_RX_5", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 352800, + }, + .name = "QUIN_TDM_RX_5", + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_QUINARY_TDM_RX_5, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .playback = { + .stream_name = "Quinary TDM6 Playback", + .aif_name = "QUIN_TDM_RX_6", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 352800, + }, + .name = "QUIN_TDM_RX_6", + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_QUINARY_TDM_RX_6, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .playback = { + .stream_name = "Quinary TDM7 Playback", + .aif_name = "QUIN_TDM_RX_7", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 352800, + }, + .name = "QUIN_TDM_RX_7", + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_QUINARY_TDM_RX_7, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .capture = { + .stream_name = "Quinary TDM0 Capture", + .aif_name = "QUIN_TDM_TX_0", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 352800, + }, + .name = "QUIN_TDM_TX_0", + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_QUINARY_TDM_TX, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .capture = { + .stream_name = "Quinary TDM1 Capture", + .aif_name = "QUIN_TDM_TX_1", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 352800, + }, + .name = "QUIN_TDM_TX_1", + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_QUINARY_TDM_TX_1, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .capture = { + .stream_name = "Quinary TDM2 Capture", + .aif_name = "QUIN_TDM_TX_2", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 352800, + }, + .name = "QUIN_TDM_TX_2", + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_QUINARY_TDM_TX_2, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .capture = { + .stream_name = "Quinary TDM3 Capture", + .aif_name = "QUIN_TDM_TX_3", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 352800, + }, + .name = "QUIN_TDM_TX_3", + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_QUINARY_TDM_TX_3, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .capture = { + .stream_name = "Quinary TDM4 Capture", + .aif_name = "QUIN_TDM_TX_4", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 352800, + }, + .name = "QUIN_TDM_TX_4", + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_QUINARY_TDM_TX_4, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .capture = { + .stream_name = "Quinary TDM5 Capture", + .aif_name = "QUIN_TDM_TX_5", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 352800, + }, + .name = "QUIN_TDM_TX_5", + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_QUINARY_TDM_TX_5, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .capture = { + .stream_name = "Quinary TDM6 Capture", + .aif_name = "QUIN_TDM_TX_6", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 352800, + }, + .name = "QUIN_TDM_TX_6", + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_QUINARY_TDM_TX_6, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .capture = { + .stream_name = "Quinary TDM7 Capture", + .aif_name = "QUIN_TDM_TX_7", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 352800, + }, + .name = "QUIN_TDM_TX_7", + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_QUINARY_TDM_TX_7, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, +}; + +static const struct snd_soc_component_driver msm_q6_tdm_dai_component = { + .name = "msm-dai-q6-tdm", +}; + +static int msm_dai_q6_tdm_dev_probe(struct platform_device *pdev) +{ + struct msm_dai_q6_tdm_dai_data *dai_data = NULL; + struct afe_param_id_custom_tdm_header_cfg *custom_tdm_header = NULL; + int rc = 0; + u32 tdm_dev_id = 0; + int port_idx = 0; + struct device_node *tdm_parent_node = NULL; + + /* retrieve device/afe id */ + rc = of_property_read_u32(pdev->dev.of_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; + } + pdev->id = tdm_dev_id; + + dai_data = kzalloc(sizeof(struct msm_dai_q6_tdm_dai_data), + GFP_KERNEL); + if (!dai_data) { + rc = -ENOMEM; + dev_err(&pdev->dev, + "%s Failed to allocate memory for tdm dai_data\n", + __func__); + goto rtn; + } + memset(dai_data, 0, sizeof(*dai_data)); + + rc = of_property_read_u32(pdev->dev.of_node, + "qcom,msm-dai-is-island-supported", + &dai_data->is_island_dai); + if (rc) + dev_dbg(&pdev->dev, "island supported entry not found\n"); + + /* TDM CFG */ + tdm_parent_node = of_get_parent(pdev->dev.of_node); + rc = of_property_read_u32(tdm_parent_node, + "qcom,msm-cpudai-tdm-sync-mode", + (u32 *)&dai_data->port_cfg.tdm.sync_mode); + if (rc) { + dev_err(&pdev->dev, "%s: Sync Mode from DT file %s\n", + __func__, "qcom,msm-cpudai-tdm-sync-mode"); + goto free_dai_data; + } + dev_dbg(&pdev->dev, "%s: Sync Mode from DT file 0x%x\n", + __func__, dai_data->port_cfg.tdm.sync_mode); + + rc = of_property_read_u32(tdm_parent_node, + "qcom,msm-cpudai-tdm-sync-src", + (u32 *)&dai_data->port_cfg.tdm.sync_src); + if (rc) { + dev_err(&pdev->dev, "%s: Sync Src from DT file %s\n", + __func__, "qcom,msm-cpudai-tdm-sync-src"); + goto free_dai_data; + } + dev_dbg(&pdev->dev, "%s: Sync Src from DT file 0x%x\n", + __func__, dai_data->port_cfg.tdm.sync_src); + + rc = of_property_read_u32(tdm_parent_node, + "qcom,msm-cpudai-tdm-data-out", + (u32 *)&dai_data->port_cfg.tdm.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 free_dai_data; + } + dev_dbg(&pdev->dev, "%s: Data Out from DT file 0x%x\n", + __func__, dai_data->port_cfg.tdm.ctrl_data_out_enable); + + rc = of_property_read_u32(tdm_parent_node, + "qcom,msm-cpudai-tdm-invert-sync", + (u32 *)&dai_data->port_cfg.tdm.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 free_dai_data; + } + dev_dbg(&pdev->dev, "%s: Invert Sync from DT file 0x%x\n", + __func__, dai_data->port_cfg.tdm.ctrl_invert_sync_pulse); + + rc = of_property_read_u32(tdm_parent_node, + "qcom,msm-cpudai-tdm-data-delay", + (u32 *)&dai_data->port_cfg.tdm.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 free_dai_data; + } + dev_dbg(&pdev->dev, "%s: Data Delay from DT file 0x%x\n", + __func__, dai_data->port_cfg.tdm.ctrl_sync_data_delay); + + /* TDM CFG -- set default */ + dai_data->port_cfg.tdm.data_format = AFE_LINEAR_PCM_DATA; + dai_data->port_cfg.tdm.tdm_cfg_minor_version = + AFE_API_VERSION_TDM_CONFIG; + + /* TDM SLOT MAPPING CFG */ + rc = of_property_read_u32(pdev->dev.of_node, + "qcom,msm-cpudai-tdm-data-align", + &dai_data->port_cfg.slot_mapping.data_align_type); + if (rc) { + dev_err(&pdev->dev, "%s: Data Align from DT file %s\n", + __func__, + "qcom,msm-cpudai-tdm-data-align"); + goto free_dai_data; + } + dev_dbg(&pdev->dev, "%s: Data Align from DT file 0x%x\n", + __func__, dai_data->port_cfg.slot_mapping.data_align_type); + + /* TDM SLOT MAPPING CFG -- set default */ + dai_data->port_cfg.slot_mapping.minor_version = + AFE_API_VERSION_SLOT_MAPPING_CONFIG; + + /* CUSTOM TDM HEADER CFG */ + custom_tdm_header = &dai_data->port_cfg.custom_tdm_header; + if (of_find_property(pdev->dev.of_node, + "qcom,msm-cpudai-tdm-header-start-offset", NULL) && + of_find_property(pdev->dev.of_node, + "qcom,msm-cpudai-tdm-header-width", NULL) && + of_find_property(pdev->dev.of_node, + "qcom,msm-cpudai-tdm-header-num-frame-repeat", NULL)) { + /* if the property exist */ + rc = of_property_read_u32(pdev->dev.of_node, + "qcom,msm-cpudai-tdm-header-start-offset", + (u32 *)&custom_tdm_header->start_offset); + if (rc) { + dev_err(&pdev->dev, "%s: Header Start Offset from DT file %s\n", + __func__, + "qcom,msm-cpudai-tdm-header-start-offset"); + goto free_dai_data; + } + dev_dbg(&pdev->dev, "%s: Header Start Offset from DT file 0x%x\n", + __func__, custom_tdm_header->start_offset); + + rc = of_property_read_u32(pdev->dev.of_node, + "qcom,msm-cpudai-tdm-header-width", + (u32 *)&custom_tdm_header->header_width); + if (rc) { + dev_err(&pdev->dev, "%s: Header Width from DT file %s\n", + __func__, "qcom,msm-cpudai-tdm-header-width"); + goto free_dai_data; + } + dev_dbg(&pdev->dev, "%s: Header Width from DT file 0x%x\n", + __func__, custom_tdm_header->header_width); + + rc = of_property_read_u32(pdev->dev.of_node, + "qcom,msm-cpudai-tdm-header-num-frame-repeat", + (u32 *)&custom_tdm_header->num_frame_repeat); + if (rc) { + dev_err(&pdev->dev, "%s: Header Num Frame Repeat from DT file %s\n", + __func__, + "qcom,msm-cpudai-tdm-header-num-frame-repeat"); + goto free_dai_data; + } + dev_dbg(&pdev->dev, "%s: Header Num Frame Repeat from DT file 0x%x\n", + __func__, custom_tdm_header->num_frame_repeat); + + /* CUSTOM TDM HEADER CFG -- set default */ + custom_tdm_header->minor_version = + AFE_API_VERSION_CUSTOM_TDM_HEADER_CONFIG; + custom_tdm_header->header_type = + AFE_CUSTOM_TDM_HEADER_TYPE_INVALID; + } else { + /* CUSTOM TDM HEADER CFG -- set default */ + custom_tdm_header->header_type = + AFE_CUSTOM_TDM_HEADER_TYPE_INVALID; + /* proceed with probe */ + } + + /* copy static clk per parent node */ + dai_data->clk_set = tdm_clk_set; + /* copy static group cfg per parent node */ + dai_data->group_cfg.tdm_cfg = tdm_group_cfg; + /* copy static num group ports per parent node */ + dai_data->num_group_ports = num_tdm_group_ports; + + + dev_set_drvdata(&pdev->dev, dai_data); + + port_idx = msm_dai_q6_get_port_idx(tdm_dev_id); + if (port_idx < 0) { + dev_err(&pdev->dev, "%s Port id 0x%x not supported\n", + __func__, tdm_dev_id); + rc = -EINVAL; + goto free_dai_data; + } + + rc = snd_soc_register_component(&pdev->dev, + &msm_q6_tdm_dai_component, + &msm_dai_q6_tdm_dai[port_idx], 1); + + if (rc) { + dev_err(&pdev->dev, "%s: TDM dai 0x%x register failed, rc=%d\n", + __func__, tdm_dev_id, rc); + goto err_register; + } + + return 0; + +err_register: +free_dai_data: + kfree(dai_data); +rtn: + return rc; +} + +static int msm_dai_q6_tdm_dev_remove(struct platform_device *pdev) +{ + struct msm_dai_q6_tdm_dai_data *dai_data = + dev_get_drvdata(&pdev->dev); + + snd_soc_unregister_component(&pdev->dev); + + kfree(dai_data); + + return 0; +} + +static const struct of_device_id msm_dai_q6_tdm_dev_dt_match[] = { + { .compatible = "qcom,msm-dai-q6-tdm", }, + {} +}; + +MODULE_DEVICE_TABLE(of, msm_dai_q6_tdm_dev_dt_match); + +static struct platform_driver msm_dai_q6_tdm_driver = { + .probe = msm_dai_q6_tdm_dev_probe, + .remove = msm_dai_q6_tdm_dev_remove, + .driver = { + .name = "msm-dai-q6-tdm", + .owner = THIS_MODULE, + .of_match_table = msm_dai_q6_tdm_dev_dt_match, + }, +}; + +static int msm_dai_q6_cdc_dma_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct msm_dai_q6_cdc_dma_dai_data *dai_data = kcontrol->private_data; + int value = ucontrol->value.integer.value[0]; + + dai_data->port_config.cdc_dma.data_format = value; + pr_debug("%s: format = %d\n", __func__, value); + return 0; +} + +static int msm_dai_q6_cdc_dma_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct msm_dai_q6_cdc_dma_dai_data *dai_data = kcontrol->private_data; + + ucontrol->value.integer.value[0] = + dai_data->port_config.cdc_dma.data_format; + return 0; +} +static const struct snd_kcontrol_new cdc_dma_config_controls[] = { + SOC_ENUM_EXT("WSA_CDC_DMA_0 TX Format", cdc_dma_config_enum[0], + msm_dai_q6_cdc_dma_format_get, + msm_dai_q6_cdc_dma_format_put), +}; + +/* SOC probe for codec DMA interface */ +static int msm_dai_q6_dai_cdc_dma_probe(struct snd_soc_dai *dai) +{ + struct msm_dai_q6_cdc_dma_dai_data *dai_data = NULL; + int rc = 0; + + if (!dai) { + pr_err("%s: Invalid params dai\n", __func__); + return -EINVAL; + } + if (!dai->dev) { + pr_err("%s: Invalid params dai dev\n", __func__); + return -EINVAL; + } + + msm_dai_q6_set_dai_id(dai); + dai_data = dev_get_drvdata(dai->dev); + + switch (dai->id) { + case AFE_PORT_ID_WSA_CODEC_DMA_TX_0: + rc = snd_ctl_add(dai->component->card->snd_card, + snd_ctl_new1(&cdc_dma_config_controls[0], + dai_data)); + break; + default: + break; + } + + if (rc < 0) + dev_err(dai->dev, "%s: err add config ctl, DAI = %s\n", + __func__, dai->name); + + if (dai_data->is_island_dai) + rc = msm_dai_q6_add_island_mx_ctls( + dai->component->card->snd_card, + dai->name, dai->id, + (void *)dai_data); + + rc = msm_dai_q6_dai_add_route(dai); + return rc; +} + +static int msm_dai_q6_dai_cdc_dma_remove(struct snd_soc_dai *dai) +{ + struct msm_dai_q6_cdc_dma_dai_data *dai_data = + dev_get_drvdata(dai->dev); + int rc = 0; + + /* If AFE port is still up, close it */ + if (test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) { + dev_dbg(dai->dev, "%s: stop codec dma port:%d\n", __func__, + dai->id); + rc = afe_close(dai->id); /* can block */ + if (rc < 0) + dev_err(dai->dev, "fail to close AFE port\n"); + clear_bit(STATUS_PORT_STARTED, dai_data->status_mask); + } + return rc; +} + +static int msm_dai_q6_cdc_dma_set_channel_map(struct snd_soc_dai *dai, + unsigned int tx_num_ch, unsigned int *tx_ch_mask, + unsigned int rx_num_ch, unsigned int *rx_ch_mask) + +{ + int rc = 0; + struct msm_dai_q6_cdc_dma_dai_data *dai_data = + dev_get_drvdata(dai->dev); + unsigned int ch_mask = 0, ch_num = 0; + + dev_dbg(dai->dev, "%s: id = %d\n", __func__, dai->id); + switch (dai->id) { + case AFE_PORT_ID_WSA_CODEC_DMA_RX_0: + case AFE_PORT_ID_WSA_CODEC_DMA_RX_1: + case AFE_PORT_ID_RX_CODEC_DMA_RX_0: + case AFE_PORT_ID_RX_CODEC_DMA_RX_1: + case AFE_PORT_ID_RX_CODEC_DMA_RX_2: + case AFE_PORT_ID_RX_CODEC_DMA_RX_3: + case AFE_PORT_ID_RX_CODEC_DMA_RX_4: + case AFE_PORT_ID_RX_CODEC_DMA_RX_5: + case AFE_PORT_ID_RX_CODEC_DMA_RX_6: + case AFE_PORT_ID_RX_CODEC_DMA_RX_7: + if (!rx_ch_mask) { + dev_err(dai->dev, "%s: invalid rx ch mask\n", __func__); + return -EINVAL; + } + if (rx_num_ch > AFE_PORT_MAX_AUDIO_CHAN_CNT) { + dev_err(dai->dev, "%s: invalid rx_num_ch %d\n", + __func__, rx_num_ch); + return -EINVAL; + } + ch_mask = *rx_ch_mask; + ch_num = rx_num_ch; + break; + case AFE_PORT_ID_WSA_CODEC_DMA_TX_0: + case AFE_PORT_ID_WSA_CODEC_DMA_TX_1: + case AFE_PORT_ID_WSA_CODEC_DMA_TX_2: + case AFE_PORT_ID_VA_CODEC_DMA_TX_0: + case AFE_PORT_ID_VA_CODEC_DMA_TX_1: + case AFE_PORT_ID_TX_CODEC_DMA_TX_0: + case AFE_PORT_ID_TX_CODEC_DMA_TX_1: + case AFE_PORT_ID_TX_CODEC_DMA_TX_2: + case AFE_PORT_ID_TX_CODEC_DMA_TX_3: + case AFE_PORT_ID_TX_CODEC_DMA_TX_4: + case AFE_PORT_ID_TX_CODEC_DMA_TX_5: + if (!tx_ch_mask) { + dev_err(dai->dev, "%s: invalid tx ch mask\n", __func__); + return -EINVAL; + } + if (tx_num_ch > AFE_PORT_MAX_AUDIO_CHAN_CNT) { + dev_err(dai->dev, "%s: invalid tx_num_ch %d\n", + __func__, tx_num_ch); + return -EINVAL; + } + ch_mask = *tx_ch_mask; + ch_num = tx_num_ch; + break; + default: + dev_err(dai->dev, "%s: invalid dai id %d\n", __func__, dai->id); + return -EINVAL; + } + + dai_data->port_config.cdc_dma.active_channels_mask = ch_mask; + dev_dbg(dai->dev, "%s: CDC_DMA_%d_ch cnt[%d] ch mask[0x%x]\n", __func__, + dai->id, ch_num, ch_mask); + return rc; +} + +static int msm_dai_q6_cdc_dma_hw_params( + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct msm_dai_q6_cdc_dma_dai_data *dai_data = + dev_get_drvdata(dai->dev); + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + case SNDRV_PCM_FORMAT_SPECIAL: + dai_data->port_config.cdc_dma.bit_width = 16; + break; + case SNDRV_PCM_FORMAT_S24_LE: + case SNDRV_PCM_FORMAT_S24_3LE: + dai_data->port_config.cdc_dma.bit_width = 24; + break; + case SNDRV_PCM_FORMAT_S32_LE: + dai_data->port_config.cdc_dma.bit_width = 32; + break; + default: + dev_err(dai->dev, "%s: format %d\n", + __func__, params_format(params)); + return -EINVAL; + } + + dai_data->rate = params_rate(params); + dai_data->channels = params_channels(params); + + dai_data->port_config.cdc_dma.cdc_dma_cfg_minor_version = + AFE_API_VERSION_CODEC_DMA_CONFIG; + dai_data->port_config.cdc_dma.sample_rate = dai_data->rate; + dai_data->port_config.cdc_dma.num_channels = dai_data->channels; + dev_dbg(dai->dev, "%s: bit_wd[%hu] format[%hu]\n" + "num_channel %hu sample_rate %d\n", __func__, + dai_data->port_config.cdc_dma.bit_width, + dai_data->port_config.cdc_dma.data_format, + dai_data->port_config.cdc_dma.num_channels, + dai_data->rate); + + return 0; +} + +static int msm_dai_q6_cdc_dma_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct msm_dai_q6_cdc_dma_dai_data *dai_data = + dev_get_drvdata(dai->dev); + int rc = 0; + + if (!test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) { + if (q6core_get_avcs_api_version_per_service( + APRV2_IDS_SERVICE_ID_ADSP_AFE_V) >= AFE_API_VERSION_V4) { + /* + * send island mode config. + * This should be the first configuration + */ + rc = afe_send_port_island_mode(dai->id); + if (rc) + pr_err("%s: afe send island mode failed %d\n", + __func__, rc); + } + if ((dai->id == AFE_PORT_ID_WSA_CODEC_DMA_TX_0) && + (dai_data->port_config.cdc_dma.data_format == 1)) + dai_data->port_config.cdc_dma.data_format = + AFE_LINEAR_PCM_DATA_PACKED_16BIT; + + rc = afe_port_start(dai->id, &dai_data->port_config, + dai_data->rate); + if (rc < 0) + dev_err(dai->dev, "fail to open AFE port 0x%x\n", + dai->id); + else + set_bit(STATUS_PORT_STARTED, + dai_data->status_mask); + } + return rc; +} + + +static void msm_dai_q6_cdc_dma_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct msm_dai_q6_dai_data *dai_data = dev_get_drvdata(dai->dev); + int rc = 0; + + if (test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) { + dev_dbg(dai->dev, "%s: stop AFE port:%d\n", __func__, + dai->id); + rc = afe_close(dai->id); /* can block */ + if (rc < 0) + dev_err(dai->dev, "fail to close AFE port\n"); + + dev_dbg(dai->dev, "%s: dai_data->status_mask = %ld\n", __func__, + *dai_data->status_mask); + clear_bit(STATUS_PORT_STARTED, dai_data->status_mask); + } + + if (test_bit(STATUS_PORT_STARTED, dai_data->hwfree_status)) + clear_bit(STATUS_PORT_STARTED, dai_data->hwfree_status); +} + +/* all ports with same WSA requirement can use this digital mute API */ +static int msm_dai_q6_spk_digital_mute(struct snd_soc_dai *dai, + int mute) +{ + int port_id = dai->id; + + if (mute) + afe_get_sp_xt_logging_data(port_id); + + return 0; +} + +static struct snd_soc_dai_ops msm_dai_q6_cdc_dma_ops = { + .prepare = msm_dai_q6_cdc_dma_prepare, + .hw_params = msm_dai_q6_cdc_dma_hw_params, + .shutdown = msm_dai_q6_cdc_dma_shutdown, + .set_channel_map = msm_dai_q6_cdc_dma_set_channel_map, +}; + +static struct snd_soc_dai_ops msm_dai_q6_cdc_wsa_dma_ops = { + .prepare = msm_dai_q6_cdc_dma_prepare, + .hw_params = msm_dai_q6_cdc_dma_hw_params, + .shutdown = msm_dai_q6_cdc_dma_shutdown, + .set_channel_map = msm_dai_q6_cdc_dma_set_channel_map, + .digital_mute = msm_dai_q6_spk_digital_mute, +}; + +static struct snd_soc_dai_driver msm_dai_q6_cdc_dma_dai[] = { + { + .playback = { + .stream_name = "WSA CDC DMA0 Playback", + .aif_name = "WSA_CDC_DMA_RX_0", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 | + SNDRV_PCM_RATE_192000 | SNDRV_PCM_RATE_352800 | + SNDRV_PCM_RATE_384000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 4, + .rate_min = 8000, + .rate_max = 384000, + }, + .name = "WSA_CDC_DMA_RX_0", + .ops = &msm_dai_q6_cdc_wsa_dma_ops, + .id = AFE_PORT_ID_WSA_CODEC_DMA_RX_0, + .probe = msm_dai_q6_dai_cdc_dma_probe, + .remove = msm_dai_q6_dai_cdc_dma_remove, + }, + { + .capture = { + .stream_name = "WSA CDC DMA0 Capture", + .aif_name = "WSA_CDC_DMA_TX_0", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 | + SNDRV_PCM_RATE_192000 | SNDRV_PCM_RATE_352800 | + SNDRV_PCM_RATE_384000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 4, + .rate_min = 8000, + .rate_max = 384000, + }, + .name = "WSA_CDC_DMA_TX_0", + .ops = &msm_dai_q6_cdc_dma_ops, + .id = AFE_PORT_ID_WSA_CODEC_DMA_TX_0, + .probe = msm_dai_q6_dai_cdc_dma_probe, + .remove = msm_dai_q6_dai_cdc_dma_remove, + }, + { + .playback = { + .stream_name = "WSA CDC DMA1 Playback", + .aif_name = "WSA_CDC_DMA_RX_1", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 | + SNDRV_PCM_RATE_192000 | SNDRV_PCM_RATE_352800 | + SNDRV_PCM_RATE_384000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 384000, + }, + .name = "WSA_CDC_DMA_RX_1", + .ops = &msm_dai_q6_cdc_wsa_dma_ops, + .id = AFE_PORT_ID_WSA_CODEC_DMA_RX_1, + .probe = msm_dai_q6_dai_cdc_dma_probe, + .remove = msm_dai_q6_dai_cdc_dma_remove, + }, + { + .capture = { + .stream_name = "WSA CDC DMA1 Capture", + .aif_name = "WSA_CDC_DMA_TX_1", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 | + SNDRV_PCM_RATE_192000 | SNDRV_PCM_RATE_352800 | + SNDRV_PCM_RATE_384000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 384000, + }, + .name = "WSA_CDC_DMA_TX_1", + .ops = &msm_dai_q6_cdc_dma_ops, + .id = AFE_PORT_ID_WSA_CODEC_DMA_TX_1, + .probe = msm_dai_q6_dai_cdc_dma_probe, + .remove = msm_dai_q6_dai_cdc_dma_remove, + }, + { + .capture = { + .stream_name = "WSA CDC DMA2 Capture", + .aif_name = "WSA_CDC_DMA_TX_2", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 | + SNDRV_PCM_RATE_192000 | SNDRV_PCM_RATE_352800 | + SNDRV_PCM_RATE_384000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 1, + .rate_min = 8000, + .rate_max = 384000, + }, + .name = "WSA_CDC_DMA_TX_2", + .ops = &msm_dai_q6_cdc_dma_ops, + .id = AFE_PORT_ID_WSA_CODEC_DMA_TX_2, + .probe = msm_dai_q6_dai_cdc_dma_probe, + .remove = msm_dai_q6_dai_cdc_dma_remove, + }, + { + .capture = { + .stream_name = "VA CDC DMA0 Capture", + .aif_name = "VA_CDC_DMA_TX_0", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 | + SNDRV_PCM_RATE_192000 | SNDRV_PCM_RATE_352800 | + SNDRV_PCM_RATE_384000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 384000, + }, + .name = "VA_CDC_DMA_TX_0", + .ops = &msm_dai_q6_cdc_dma_ops, + .id = AFE_PORT_ID_VA_CODEC_DMA_TX_0, + .probe = msm_dai_q6_dai_cdc_dma_probe, + .remove = msm_dai_q6_dai_cdc_dma_remove, + }, + { + .capture = { + .stream_name = "VA CDC DMA1 Capture", + .aif_name = "VA_CDC_DMA_TX_1", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 | + SNDRV_PCM_RATE_192000 | SNDRV_PCM_RATE_352800 | + SNDRV_PCM_RATE_384000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 384000, + }, + .name = "VA_CDC_DMA_TX_1", + .ops = &msm_dai_q6_cdc_dma_ops, + .id = AFE_PORT_ID_VA_CODEC_DMA_TX_1, + .probe = msm_dai_q6_dai_cdc_dma_probe, + .remove = msm_dai_q6_dai_cdc_dma_remove, + }, + { + .playback = { + .stream_name = "RX CDC DMA0 Playback", + .aif_name = "RX_CDC_DMA_RX_0", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 | + SNDRV_PCM_RATE_192000 | SNDRV_PCM_RATE_352800 | + SNDRV_PCM_RATE_384000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 384000, + }, + .ops = &msm_dai_q6_cdc_dma_ops, + .id = AFE_PORT_ID_RX_CODEC_DMA_RX_0, + .probe = msm_dai_q6_dai_cdc_dma_probe, + .remove = msm_dai_q6_dai_cdc_dma_remove, + }, + { + .capture = { + .stream_name = "TX CDC DMA0 Capture", + .aif_name = "TX_CDC_DMA_TX_0", + .rates = SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | + SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_192000 | + SNDRV_PCM_RATE_384000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 3, + .rate_min = 8000, + .rate_max = 384000, + }, + .ops = &msm_dai_q6_cdc_dma_ops, + .id = AFE_PORT_ID_TX_CODEC_DMA_TX_0, + .probe = msm_dai_q6_dai_cdc_dma_probe, + .remove = msm_dai_q6_dai_cdc_dma_remove, + }, + { + .playback = { + .stream_name = "RX CDC DMA1 Playback", + .aif_name = "RX_CDC_DMA_RX_1", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 | + SNDRV_PCM_RATE_192000 | SNDRV_PCM_RATE_352800 | + SNDRV_PCM_RATE_384000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 384000, + }, + .ops = &msm_dai_q6_cdc_dma_ops, + .id = AFE_PORT_ID_RX_CODEC_DMA_RX_1, + .probe = msm_dai_q6_dai_cdc_dma_probe, + .remove = msm_dai_q6_dai_cdc_dma_remove, + }, + { + .capture = { + .stream_name = "TX CDC DMA1 Capture", + .aif_name = "TX_CDC_DMA_TX_1", + .rates = SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | + SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_192000 | + SNDRV_PCM_RATE_384000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 3, + .rate_min = 8000, + .rate_max = 384000, + }, + .ops = &msm_dai_q6_cdc_dma_ops, + .id = AFE_PORT_ID_TX_CODEC_DMA_TX_1, + .probe = msm_dai_q6_dai_cdc_dma_probe, + .remove = msm_dai_q6_dai_cdc_dma_remove, + }, + { + .playback = { + .stream_name = "RX CDC DMA2 Playback", + .aif_name = "RX_CDC_DMA_RX_2", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 | + SNDRV_PCM_RATE_192000 | SNDRV_PCM_RATE_352800 | + SNDRV_PCM_RATE_384000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 1, + .rate_min = 8000, + .rate_max = 384000, + }, + .ops = &msm_dai_q6_cdc_dma_ops, + .id = AFE_PORT_ID_RX_CODEC_DMA_RX_2, + .probe = msm_dai_q6_dai_cdc_dma_probe, + .remove = msm_dai_q6_dai_cdc_dma_remove, + }, + { + .capture = { + .stream_name = "TX CDC DMA2 Capture", + .aif_name = "TX_CDC_DMA_TX_2", + .rates = SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | + SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_192000 | + SNDRV_PCM_RATE_384000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 4, + .rate_min = 8000, + .rate_max = 384000, + }, + .ops = &msm_dai_q6_cdc_dma_ops, + .id = AFE_PORT_ID_TX_CODEC_DMA_TX_2, + .probe = msm_dai_q6_dai_cdc_dma_probe, + .remove = msm_dai_q6_dai_cdc_dma_remove, + }, { + .playback = { + .stream_name = "RX CDC DMA3 Playback", + .aif_name = "RX_CDC_DMA_RX_3", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 | + SNDRV_PCM_RATE_192000 | SNDRV_PCM_RATE_352800 | + SNDRV_PCM_RATE_384000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 1, + .rate_min = 8000, + .rate_max = 384000, + }, + .ops = &msm_dai_q6_cdc_dma_ops, + .id = AFE_PORT_ID_RX_CODEC_DMA_RX_3, + .probe = msm_dai_q6_dai_cdc_dma_probe, + .remove = msm_dai_q6_dai_cdc_dma_remove, + }, + { + .capture = { + .stream_name = "TX CDC DMA3 Capture", + .aif_name = "TX_CDC_DMA_TX_3", + .rates = SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | + SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_192000 | + SNDRV_PCM_RATE_384000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 384000, + }, + .ops = &msm_dai_q6_cdc_dma_ops, + .id = AFE_PORT_ID_TX_CODEC_DMA_TX_3, + .probe = msm_dai_q6_dai_cdc_dma_probe, + .remove = msm_dai_q6_dai_cdc_dma_remove, + }, + { + .playback = { + .stream_name = "RX CDC DMA4 Playback", + .aif_name = "RX_CDC_DMA_RX_4", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 | + SNDRV_PCM_RATE_192000 | SNDRV_PCM_RATE_352800 | + SNDRV_PCM_RATE_384000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 6, + .rate_min = 8000, + .rate_max = 384000, + }, + .ops = &msm_dai_q6_cdc_dma_ops, + .id = AFE_PORT_ID_RX_CODEC_DMA_RX_4, + .probe = msm_dai_q6_dai_cdc_dma_probe, + .remove = msm_dai_q6_dai_cdc_dma_remove, + }, + { + .capture = { + .stream_name = "TX CDC DMA4 Capture", + .aif_name = "TX_CDC_DMA_TX_4", + .rates = SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | + SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_192000 | + SNDRV_PCM_RATE_384000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 384000, + }, + .ops = &msm_dai_q6_cdc_dma_ops, + .id = AFE_PORT_ID_TX_CODEC_DMA_TX_4, + .probe = msm_dai_q6_dai_cdc_dma_probe, + .remove = msm_dai_q6_dai_cdc_dma_remove, + }, + { + .playback = { + .stream_name = "RX CDC DMA5 Playback", + .aif_name = "RX_CDC_DMA_RX_5", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 | + SNDRV_PCM_RATE_192000 | SNDRV_PCM_RATE_352800 | + SNDRV_PCM_RATE_384000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 1, + .rate_min = 8000, + .rate_max = 384000, + }, + .ops = &msm_dai_q6_cdc_dma_ops, + .id = AFE_PORT_ID_RX_CODEC_DMA_RX_5, + .probe = msm_dai_q6_dai_cdc_dma_probe, + .remove = msm_dai_q6_dai_cdc_dma_remove, + }, + { + .capture = { + .stream_name = "TX CDC DMA5 Capture", + .aif_name = "TX_CDC_DMA_TX_5", + .rates = SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | + SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_192000 | + SNDRV_PCM_RATE_384000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 4, + .rate_min = 8000, + .rate_max = 384000, + }, + .ops = &msm_dai_q6_cdc_dma_ops, + .id = AFE_PORT_ID_TX_CODEC_DMA_TX_5, + .probe = msm_dai_q6_dai_cdc_dma_probe, + .remove = msm_dai_q6_dai_cdc_dma_remove, + }, + { + .playback = { + .stream_name = "RX CDC DMA6 Playback", + .aif_name = "RX_CDC_DMA_RX_6", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 | + SNDRV_PCM_RATE_192000 | SNDRV_PCM_RATE_352800 | + SNDRV_PCM_RATE_384000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 4, + .rate_min = 8000, + .rate_max = 384000, + }, + .ops = &msm_dai_q6_cdc_dma_ops, + .id = AFE_PORT_ID_RX_CODEC_DMA_RX_6, + .probe = msm_dai_q6_dai_cdc_dma_probe, + .remove = msm_dai_q6_dai_cdc_dma_remove, + }, + { + .playback = { + .stream_name = "RX CDC DMA7 Playback", + .aif_name = "RX_CDC_DMA_RX_7", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 | + SNDRV_PCM_RATE_192000 | SNDRV_PCM_RATE_352800 | + SNDRV_PCM_RATE_384000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 384000, + }, + .ops = &msm_dai_q6_cdc_dma_ops, + .id = AFE_PORT_ID_RX_CODEC_DMA_RX_7, + .probe = msm_dai_q6_dai_cdc_dma_probe, + .remove = msm_dai_q6_dai_cdc_dma_remove, + }, +}; + +static const struct snd_soc_component_driver msm_q6_cdc_dma_dai_component = { + .name = "msm-dai-cdc-dma-dev", +}; + +/* DT related probe for each codec DMA interface device */ +static int msm_dai_q6_cdc_dma_dev_probe(struct platform_device *pdev) +{ + const char *q6_cdc_dma_dev_id = "qcom,msm-dai-cdc-dma-dev-id"; + u32 cdc_dma_id = 0; + int i; + int rc = 0; + struct msm_dai_q6_cdc_dma_dai_data *dai_data = NULL; + + rc = of_property_read_u32(pdev->dev.of_node, q6_cdc_dma_dev_id, + &cdc_dma_id); + if (rc) { + dev_err(&pdev->dev, + "%s: missing 0x%x in dt node\n", __func__, cdc_dma_id); + return rc; + } + + dev_dbg(&pdev->dev, "%s: dev name %s dev id 0x%x\n", __func__, + dev_name(&pdev->dev), cdc_dma_id); + + pdev->id = cdc_dma_id; + + dai_data = devm_kzalloc(&pdev->dev, + sizeof(struct msm_dai_q6_cdc_dma_dai_data), + GFP_KERNEL); + + if (!dai_data) + return -ENOMEM; + + rc = of_property_read_u32(pdev->dev.of_node, + "qcom,msm-dai-is-island-supported", + &dai_data->is_island_dai); + if (rc) + dev_dbg(&pdev->dev, "island supported entry not found\n"); + + dev_set_drvdata(&pdev->dev, dai_data); + + for (i = 0; i < ARRAY_SIZE(msm_dai_q6_cdc_dma_dai); i++) { + if (msm_dai_q6_cdc_dma_dai[i].id == cdc_dma_id) { + return snd_soc_register_component(&pdev->dev, + &msm_q6_cdc_dma_dai_component, + &msm_dai_q6_cdc_dma_dai[i], 1); + } + } + return -ENODEV; +} + +static int msm_dai_q6_cdc_dma_dev_remove(struct platform_device *pdev) +{ + snd_soc_unregister_component(&pdev->dev); + return 0; +} + +static const struct of_device_id msm_dai_q6_cdc_dma_dev_dt_match[] = { + { .compatible = "qcom,msm-dai-cdc-dma-dev", }, + { } +}; + +MODULE_DEVICE_TABLE(of, msm_dai_q6_cdc_dma_dev_dt_match); + +static struct platform_driver msm_dai_q6_cdc_dma_driver = { + .probe = msm_dai_q6_cdc_dma_dev_probe, + .remove = msm_dai_q6_cdc_dma_dev_remove, + .driver = { + .name = "msm-dai-cdc-dma-dev", + .owner = THIS_MODULE, + .of_match_table = msm_dai_q6_cdc_dma_dev_dt_match, + }, +}; + +/* DT related probe for codec DMA interface device group */ +static int msm_dai_cdc_dma_q6_probe(struct platform_device *pdev) +{ + int rc; + + rc = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev); + if (rc) { + dev_err(&pdev->dev, "%s: failed to add child nodes, rc=%d\n", + __func__, rc); + } else + dev_dbg(&pdev->dev, "%s: added child node\n", __func__); + return rc; +} + +static int msm_dai_cdc_dma_q6_remove(struct platform_device *pdev) +{ + of_platform_depopulate(&pdev->dev); + return 0; +} + +static const struct of_device_id msm_dai_cdc_dma_dt_match[] = { + { .compatible = "qcom,msm-dai-cdc-dma", }, + { } +}; + +MODULE_DEVICE_TABLE(of, msm_dai_cdc_dma_dt_match); + +static struct platform_driver msm_dai_cdc_dma_q6 = { + .probe = msm_dai_cdc_dma_q6_probe, + .remove = msm_dai_cdc_dma_q6_remove, + .driver = { + .name = "msm-dai-cdc-dma", + .owner = THIS_MODULE, + .of_match_table = msm_dai_cdc_dma_dt_match, + }, +}; + +int __init msm_dai_q6_init(void) +{ + int rc; + + rc = platform_driver_register(&msm_auxpcm_dev_driver); + if (rc) { + pr_err("%s: fail to register auxpcm dev driver", __func__); + goto fail; + } + + rc = platform_driver_register(&msm_dai_q6); + if (rc) { + pr_err("%s: fail to register dai q6 driver", __func__); + goto dai_q6_fail; + } + + rc = platform_driver_register(&msm_dai_q6_dev); + if (rc) { + pr_err("%s: fail to register dai q6 dev driver", __func__); + goto dai_q6_dev_fail; + } + + rc = platform_driver_register(&msm_dai_q6_mi2s_driver); + if (rc) { + pr_err("%s: fail to register dai MI2S dev drv\n", __func__); + goto dai_q6_mi2s_drv_fail; + } + + rc = platform_driver_register(&msm_dai_mi2s_q6); + if (rc) { + pr_err("%s: fail to register dai MI2S\n", __func__); + goto dai_mi2s_q6_fail; + } + + rc = platform_driver_register(&msm_dai_q6_spdif_driver); + if (rc) { + pr_err("%s: fail to register dai SPDIF\n", __func__); + goto dai_spdif_q6_fail; + } + + rc = platform_driver_register(&msm_dai_q6_tdm_driver); + if (rc) { + pr_err("%s: fail to register dai TDM dev drv\n", __func__); + goto dai_q6_tdm_drv_fail; + } + + rc = platform_driver_register(&msm_dai_tdm_q6); + if (rc) { + pr_err("%s: fail to register dai TDM\n", __func__); + goto dai_tdm_q6_fail; + } + + rc = platform_driver_register(&msm_dai_q6_cdc_dma_driver); + if (rc) { + pr_err("%s: fail to register dai CDC DMA dev\n", __func__); + goto dai_cdc_dma_q6_dev_fail; + } + + + rc = platform_driver_register(&msm_dai_cdc_dma_q6); + if (rc) { + pr_err("%s: fail to register dai CDC DMA\n", __func__); + goto dai_cdc_dma_q6_fail; + } + return rc; + +dai_cdc_dma_q6_fail: + platform_driver_unregister(&msm_dai_q6_cdc_dma_driver); +dai_cdc_dma_q6_dev_fail: + platform_driver_unregister(&msm_dai_tdm_q6); +dai_tdm_q6_fail: + platform_driver_unregister(&msm_dai_q6_tdm_driver); +dai_q6_tdm_drv_fail: + platform_driver_unregister(&msm_dai_q6_spdif_driver); +dai_spdif_q6_fail: + platform_driver_unregister(&msm_dai_mi2s_q6); +dai_mi2s_q6_fail: + platform_driver_unregister(&msm_dai_q6_mi2s_driver); +dai_q6_mi2s_drv_fail: + platform_driver_unregister(&msm_dai_q6_dev); +dai_q6_dev_fail: + platform_driver_unregister(&msm_dai_q6); +dai_q6_fail: + platform_driver_unregister(&msm_auxpcm_dev_driver); +fail: + return rc; +} + +void msm_dai_q6_exit(void) +{ + platform_driver_unregister(&msm_dai_cdc_dma_q6); + platform_driver_unregister(&msm_dai_q6_cdc_dma_driver); + platform_driver_unregister(&msm_dai_tdm_q6); + platform_driver_unregister(&msm_dai_q6_tdm_driver); + platform_driver_unregister(&msm_dai_q6_spdif_driver); + platform_driver_unregister(&msm_dai_mi2s_q6); + platform_driver_unregister(&msm_dai_q6_mi2s_driver); + platform_driver_unregister(&msm_dai_q6_dev); + platform_driver_unregister(&msm_dai_q6); + platform_driver_unregister(&msm_auxpcm_dev_driver); +} + +/* Module information */ +MODULE_DESCRIPTION("MSM DSP DAI driver"); +MODULE_LICENSE("GPL v2"); diff --git a/audio-kernel/asoc/msm-dai-q6-v2.h b/audio-kernel/asoc/msm-dai-q6-v2.h new file mode 100644 index 000000000..2327cfd72 --- /dev/null +++ b/audio-kernel/asoc/msm-dai-q6-v2.h @@ -0,0 +1,97 @@ +/* Copyright (c) 2012-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 + * 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 __MSM_DAI_Q6_PDATA_H__ + +#define __MSM_DAI_Q6_PDATA_H__ + +#define MSM_MI2S_SD0 (1 << 0) +#define MSM_MI2S_SD1 (1 << 1) +#define MSM_MI2S_SD2 (1 << 2) +#define MSM_MI2S_SD3 (1 << 3) +#define MSM_MI2S_SD4 (1 << 4) +#define MSM_MI2S_SD5 (1 << 5) +#define MSM_MI2S_SD6 (1 << 6) +#define MSM_MI2S_SD7 (1 << 7) + +#define MSM_MI2S_CAP_RX 0 +#define MSM_MI2S_CAP_TX 1 + +#define MSM_PRIM_MI2S 0 +#define MSM_SEC_MI2S 1 +#define MSM_TERT_MI2S 2 +#define MSM_QUAT_MI2S 3 +#define MSM_QUIN_MI2S 4 +#define MSM_SEC_MI2S_SD1 5 +#define MSM_SENARY_MI2S 6 +#define MSM_INT0_MI2S 7 +#define MSM_INT1_MI2S 8 +#define MSM_INT2_MI2S 9 +#define MSM_INT3_MI2S 10 +#define MSM_INT4_MI2S 11 +#define MSM_INT5_MI2S 12 +#define MSM_INT6_MI2S 13 +#define MSM_MI2S_MIN MSM_PRIM_MI2S +#define MSM_MI2S_MAX MSM_INT6_MI2S + +struct msm_dai_auxpcm_config { + u16 mode; + u16 sync; + u16 frame; + u16 quant; + u16 num_slots; + u16 *slot_mapping; + u16 data; + u32 pcm_clk_rate; +}; + +struct msm_dai_auxpcm_pdata { + struct msm_dai_auxpcm_config mode_8k; + struct msm_dai_auxpcm_config mode_16k; +}; + +struct msm_mi2s_pdata { + u16 rx_sd_lines; + u16 tx_sd_lines; + u16 intf_id; +}; + +struct msm_i2s_data { + u32 capability; /* RX or TX */ + u16 sd_lines; +}; + +struct msm_dai_tdm_group_config { + u16 group_id; + u16 num_ports; + u16 *port_id; + u32 clk_rate; +}; + +struct msm_dai_tdm_config { + u16 sync_mode; + u16 sync_src; + u16 data_out; + u16 invert_sync; + u16 data_delay; + u32 data_align; + u16 header_start_offset; + u16 header_width; + u16 header_num_frame_repeat; +}; + +struct msm_dai_tdm_pdata { + struct msm_dai_tdm_group_config group_config; + struct msm_dai_tdm_config config; +}; + +#endif diff --git a/audio-kernel/asoc/msm-dai-slim.c b/audio-kernel/asoc/msm-dai-slim.c new file mode 100644 index 000000000..e8bdf13fd --- /dev/null +++ b/audio-kernel/asoc/msm-dai-slim.c @@ -0,0 +1,663 @@ +/* + * Copyright (c) 2014-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 + * 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 +#include +#include +#include +#include +#include +#include +#include +#include "msm-slim-dma.h" + +#define SLIM_DEV_NAME "msm-dai-slim" + +#define SLIM_DAI_RATES (SNDRV_PCM_RATE_48000 | \ + SNDRV_PCM_RATE_8000 | \ + SNDRV_PCM_RATE_16000 | \ + SNDRV_PCM_RATE_96000 | \ + SNDRV_PCM_RATE_192000 | \ + SNDRV_PCM_RATE_384000) + +#define SLIM_DAI_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) + +#define DAI_STATE_INITIALIZED (0x01 << 0) +#define DAI_STATE_PREPARED (0x01 << 1) +#define DAI_STATE_RUNNING (0x01 << 2) + +#define SET_DAI_STATE(status, state) \ + (status |= state) + +#define CLR_DAI_STATE(status, state) \ + (status = status & (~state)) + +enum { + MSM_DAI_SLIM0 = 0, + NUM_SLIM_DAIS, +}; + +struct msm_slim_dai_data { + unsigned int dai_id; + u16 *chan_h; + u16 *sh_ch; + u16 grph; + u32 rate; + u16 bits; + u16 ch_cnt; + u8 status; + struct snd_soc_dai_driver *dai_drv; + struct msm_slim_dma_data dma_data; + struct slim_port_cfg port_cfg; +}; + +struct msm_dai_slim_drv_data { + struct slim_device *sdev; + u16 num_dais; + struct msm_slim_dai_data slim_dai_data[NUM_SLIM_DAIS]; +}; + +struct msm_slim_dai_data *msm_slim_get_dai_data( + struct msm_dai_slim_drv_data *drv_data, + struct snd_soc_dai *dai) +{ + struct msm_slim_dai_data *dai_data_t; + int i; + + for (i = 0; i < drv_data->num_dais; i++) { + dai_data_t = &drv_data->slim_dai_data[i]; + if (dai_data_t->dai_id == dai->id) + return dai_data_t; + } + + dev_err(dai->dev, + "%s: no dai data found for dai_id %d\n", + __func__, dai->id); + return NULL; +} + +static int msm_dai_slim_ch_ctl(struct msm_slim_dma_data *dma_data, + struct snd_soc_dai *dai, bool enable) +{ + struct slim_device *sdev; + struct msm_dai_slim_drv_data *drv_data; + struct msm_slim_dai_data *dai_data; + int rc, rc1, i; + + if (!dma_data || !dma_data->sdev) { + pr_err("%s: Invalid %s\n", __func__, + (!dma_data) ? "dma_data" : "slim_device"); + return -EINVAL; + } + + sdev = dma_data->sdev; + drv_data = dev_get_drvdata(&sdev->dev); + dai_data = msm_slim_get_dai_data(drv_data, dai); + + if (!dai_data) { + dev_err(dai->dev, + "%s: Invalid dai_data for dai_id %d\n", + __func__, dai->id); + return -EINVAL; + } + + dev_dbg(&sdev->dev, + "%s: enable = %s, rate = %u\n", __func__, + enable ? "true" : "false", + dai_data->rate); + + if (enable) { + if (!(dai_data->status & DAI_STATE_PREPARED)) { + dev_err(&sdev->dev, + "%s: dai id (%d) has invalid state 0x%x\n", + __func__, dai->id, dai_data->status); + return -EINVAL; + } + + rc = slim_alloc_mgrports(sdev, + SLIM_REQ_DEFAULT, dai_data->ch_cnt, + &(dma_data->ph), + sizeof(dma_data->ph)); + if (rc < 0) { + dev_err(&sdev->dev, + "%s:alloc mgrport failed rc %d\n", + __func__, rc); + goto done; + } + + rc = slim_config_mgrports(sdev, &(dma_data->ph), + dai_data->ch_cnt, + &(dai_data->port_cfg)); + if (rc < 0) { + dev_err(&sdev->dev, + "%s: config mgrport failed rc %d\n", + __func__, rc); + goto err_done; + } + + for (i = 0; i < dai_data->ch_cnt; i++) { + rc = slim_connect_sink(sdev, + &dma_data->ph, 1, + dai_data->chan_h[i]); + if (rc < 0) { + dev_err(&sdev->dev, + "%s: slim_connect_sink failed, ch = %d, err = %d\n", + __func__, i, rc); + goto err_done; + } + } + + rc = slim_control_ch(sdev, + dai_data->grph, + SLIM_CH_ACTIVATE, true); + if (rc < 0) { + dev_err(&sdev->dev, + "%s: slim activate ch failed, err = %d\n", + __func__, rc); + goto err_done; + } + /* Mark dai status as running */ + SET_DAI_STATE(dai_data->status, DAI_STATE_RUNNING); + } else { + if (!(dai_data->status & DAI_STATE_RUNNING)) { + dev_err(&sdev->dev, + "%s: dai id (%d) has invalid state 0x%x\n", + __func__, dai->id, dai_data->status); + return -EINVAL; + } + + rc = slim_control_ch(sdev, + dai_data->grph, + SLIM_CH_REMOVE, true); + if (rc < 0) { + dev_err(&sdev->dev, + "%s: slim activate ch failed, err = %d\n", + __func__, rc); + goto done; + } + + rc = slim_dealloc_mgrports(sdev, + &dma_data->ph, 1); + if (rc < 0) { + dev_err(&sdev->dev, + "%s: dealloc mgrport failed, err = %d\n", + __func__, rc); + goto done; + } + /* clear running state for dai*/ + CLR_DAI_STATE(dai_data->status, DAI_STATE_RUNNING); + } + + return rc; + +err_done: + rc1 = slim_dealloc_mgrports(sdev, + &dma_data->ph, 1); + if (rc1 < 0) + dev_err(&sdev->dev, + "%s: dealloc mgrport failed, err = %d\n", + __func__, rc1); +done: + return rc; +} + +static int msm_dai_slim_hw_params( + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct msm_dai_slim_drv_data *drv_data = dev_get_drvdata(dai->dev); + struct msm_slim_dai_data *dai_data; + int rc = 0; + + dai_data = msm_slim_get_dai_data(drv_data, dai); + if (!dai_data) { + dev_err(dai->dev, + "%s: Invalid dai_data for dai_id %d\n", + __func__, dai->id); + rc = -EINVAL; + goto done; + } + + if (!dai_data->ch_cnt || dai_data->ch_cnt != params_channels(params)) { + dev_err(dai->dev, "%s: invalid ch_cnt %d %d\n", + __func__, dai_data->ch_cnt, params_channels(params)); + rc = -EINVAL; + goto done; + } + + dai_data->rate = params_rate(params); + dai_data->port_cfg.port_opts = SLIM_OPT_NONE; + if (dai_data->rate >= SNDRV_PCM_RATE_48000) + dai_data->port_cfg.watermark = 16; + else + dai_data->port_cfg.watermark = 8; + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + dai_data->bits = 16; + break; + case SNDRV_PCM_FORMAT_S24_LE: + dai_data->bits = 24; + break; + case SNDRV_PCM_FORMAT_S32_LE: + dai_data->bits = 32; + break; + default: + dev_err(dai->dev, "%s: invalid format %d\n", __func__, + params_format(params)); + rc = -EINVAL; + goto done; + } + + dev_dbg(dai->dev, "%s: ch_cnt=%u rate=%u, bit_width = %u\n", + __func__, dai_data->ch_cnt, dai_data->rate, + dai_data->bits); +done: + return rc; +} + +static int msm_dai_slim_set_channel_map(struct snd_soc_dai *dai, + unsigned int tx_num, unsigned int *tx_slot, + unsigned int rx_num, unsigned int *rx_slot) +{ + struct msm_dai_slim_drv_data *drv_data = dev_get_drvdata(dai->dev); + struct msm_slim_dai_data *dai_data; + struct snd_soc_dai_driver *dai_drv; + u8 i = 0; + + dev_dbg(dai->dev, + "%s: tx_num=%u, rx_num=%u\n", + __func__, tx_num, rx_num); + + dai_data = msm_slim_get_dai_data(drv_data, dai); + if (!dai_data) { + dev_err(dai->dev, + "%s: Invalid dai_data for dai_id %d\n", + __func__, dai->id); + return -EINVAL; + } + + dai_drv = dai_data->dai_drv; + + if (tx_num > dai_drv->capture.channels_max) { + dev_err(dai->dev, "%s: tx_num %u max out master port cnt\n", + __func__, tx_num); + return -EINVAL; + } + + for (i = 0; i < tx_num; i++) + dai_data->sh_ch[i] = tx_slot[i]; + + dai_data->ch_cnt = tx_num; + return 0; +} + +static int msm_dai_slim_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct msm_dai_slim_drv_data *drv_data = dev_get_drvdata(dai->dev); + struct msm_slim_dma_data *dma_data; + struct msm_slim_dai_data *dai_data = NULL; + struct slim_ch prop; + int rc; + u8 i, j; + + dai_data = msm_slim_get_dai_data(drv_data, dai); + if (!dai_data) { + dev_err(dai->dev, + "%s: Invalid dai_data for dai %d\n", + __func__, dai->id); + return -EINVAL; + } + + if (!(dai_data->status & DAI_STATE_INITIALIZED)) { + dev_err(dai->dev, + "%s: dai id (%d) has invalid state 0x%x\n", + __func__, dai->id, dai_data->status); + return -EINVAL; + } + + if (dai_data->status & DAI_STATE_PREPARED) { + dev_dbg(dai->dev, + "%s: dai id (%d) has already prepared.\n", + __func__, dai->id); + return 0; + } + + dma_data = &dai_data->dma_data; + snd_soc_dai_set_dma_data(dai, substream, dma_data); + + for (i = 0; i < dai_data->ch_cnt; i++) { + rc = slim_query_ch(drv_data->sdev, dai_data->sh_ch[i], + &dai_data->chan_h[i]); + if (rc) { + dev_err(dai->dev, "%s:query chan handle failed rc %d\n", + __func__, rc); + goto error_chan_query; + } + } + + prop.prot = SLIM_AUTO_ISO; + prop.baser = SLIM_RATE_4000HZ; + prop.dataf = SLIM_CH_DATAF_NOT_DEFINED; + prop.auxf = SLIM_CH_AUXF_NOT_APPLICABLE; + prop.ratem = (dai_data->rate/4000); + prop.sampleszbits = dai_data->bits; + + rc = slim_define_ch(drv_data->sdev, &prop, dai_data->chan_h, + dai_data->ch_cnt, true, &dai_data->grph); + + if (rc) { + dev_err(dai->dev, "%s:define chan failed rc %d\n", + __func__, rc); + goto error_define_chan; + } + + /* Mark stream status as prepared */ + SET_DAI_STATE(dai_data->status, DAI_STATE_PREPARED); + + return rc; + +error_define_chan: +error_chan_query: + for (j = 0; j < i; j++) + slim_dealloc_ch(drv_data->sdev, dai_data->chan_h[j]); + return rc; +} + +static void msm_dai_slim_shutdown(struct snd_pcm_substream *stream, + struct snd_soc_dai *dai) +{ + struct msm_dai_slim_drv_data *drv_data = dev_get_drvdata(dai->dev); + struct msm_slim_dma_data *dma_data = NULL; + struct msm_slim_dai_data *dai_data; + int i, rc = 0; + + dai_data = msm_slim_get_dai_data(drv_data, dai); + dma_data = snd_soc_dai_get_dma_data(dai, stream); + if (!dma_data || !dai_data) { + dev_err(dai->dev, + "%s: Invalid %s\n", __func__, + (!dma_data) ? "dma_data" : "dai_data"); + return; + } + + if ((!(dai_data->status & DAI_STATE_PREPARED)) || + dai_data->status & DAI_STATE_RUNNING) { + dev_err(dai->dev, + "%s: dai id (%d) has invalid state 0x%x\n", + __func__, dai->id, dai_data->status); + return; + } + + for (i = 0; i < dai_data->ch_cnt; i++) { + rc = slim_dealloc_ch(drv_data->sdev, dai_data->chan_h[i]); + if (rc) { + dev_err(dai->dev, + "%s: dealloc_ch failed, err = %d\n", + __func__, rc); + } + } + + snd_soc_dai_set_dma_data(dai, stream, NULL); + /* clear prepared state for the dai */ + CLR_DAI_STATE(dai_data->status, DAI_STATE_PREPARED); +} + +static const struct snd_soc_component_driver msm_dai_slim_component = { + .name = "msm-dai-slim-cmpnt", +}; + +static struct snd_soc_dai_ops msm_dai_slim_ops = { + .prepare = msm_dai_slim_prepare, + .hw_params = msm_dai_slim_hw_params, + .shutdown = msm_dai_slim_shutdown, + .set_channel_map = msm_dai_slim_set_channel_map, +}; + +static struct snd_soc_dai_driver msm_slim_dais[] = { + { + /* + * The first dai name should be same as device name + * to support registering single and multile dais. + */ + .name = SLIM_DEV_NAME, + .id = MSM_DAI_SLIM0, + .capture = { + .rates = SLIM_DAI_RATES, + .formats = SLIM_DAI_FORMATS, + .channels_min = 1, + /* + * max channels allowed is + * dependent on platform and + * will be updated before this + * dai driver is registered. + */ + .channels_max = 1, + .rate_min = 8000, + .rate_max = 384000, + .stream_name = "SLIM_DAI0 Capture", + }, + .ops = &msm_dai_slim_ops, + }, + /* + * If multiple dais are needed, + * add dais here and update the + * dai_id enum. + */ +}; + +static void msm_dai_slim_remove_dai_data( + struct device *dev, + struct msm_dai_slim_drv_data *drv_data) +{ + int i; + struct msm_slim_dai_data *dai_data_t; + + for (i = 0; i < drv_data->num_dais; i++) { + dai_data_t = &drv_data->slim_dai_data[i]; + + kfree(dai_data_t->chan_h); + dai_data_t->chan_h = NULL; + kfree(dai_data_t->sh_ch); + dai_data_t->sh_ch = NULL; + } +} + +static int msm_dai_slim_populate_dai_data(struct device *dev, + struct msm_dai_slim_drv_data *drv_data) +{ + struct snd_soc_dai_driver *dai_drv; + struct msm_slim_dai_data *dai_data_t; + u8 num_ch; + int i, j, rc; + + for (i = 0; i < drv_data->num_dais; i++) { + num_ch = 0; + dai_drv = &msm_slim_dais[i]; + num_ch += dai_drv->capture.channels_max; + num_ch += dai_drv->playback.channels_max; + + dai_data_t = &drv_data->slim_dai_data[i]; + dai_data_t->dai_drv = dai_drv; + dai_data_t->dai_id = dai_drv->id; + dai_data_t->dma_data.sdev = drv_data->sdev; + dai_data_t->dma_data.dai_channel_ctl = + msm_dai_slim_ch_ctl; + SET_DAI_STATE(dai_data_t->status, + DAI_STATE_INITIALIZED); + + dai_data_t->chan_h = devm_kzalloc(dev, + sizeof(u16) * num_ch, + GFP_KERNEL); + if (!dai_data_t->chan_h) { + dev_err(dev, + "%s: DAI ID %d, Failed to alloc channel handles\n", + __func__, i); + rc = -ENOMEM; + goto err_mem_alloc; + } + + dai_data_t->sh_ch = devm_kzalloc(dev, + sizeof(u16) * num_ch, + GFP_KERNEL); + if (!dai_data_t->sh_ch) { + dev_err(dev, + "%s: DAI ID %d, Failed to alloc sh_ch\n", + __func__, i); + rc = -ENOMEM; + goto err_mem_alloc; + } + } + return 0; + +err_mem_alloc: + for (j = 0; j < i; j++) { + dai_data_t = &drv_data->slim_dai_data[i]; + + devm_kfree(dev, dai_data_t->chan_h); + dai_data_t->chan_h = NULL; + + devm_kfree(dev, dai_data_t->sh_ch); + dai_data_t->sh_ch = NULL; + } + return rc; +} + +static int msm_dai_slim_dev_probe(struct slim_device *sdev) +{ + int rc, i; + u8 max_channels; + u32 apps_ch_pipes; + struct msm_dai_slim_drv_data *drv_data; + struct device *dev = &sdev->dev; + struct snd_soc_dai_driver *dai_drv; + + if (!dev->of_node || + !dev->of_node->parent) { + dev_err(dev, + "%s: Invalid %s\n", __func__, + (!dev->of_node) ? "of_node" : "parent_of_node"); + return -EINVAL; + } + + rc = of_property_read_u32(dev->of_node->parent, + "qcom,apps-ch-pipes", + &apps_ch_pipes); + if (rc) { + dev_err(dev, + "%s: Failed to lookup property %s in node %s, err = %d\n", + __func__, "qcom,apps-ch-pipes", + dev->of_node->parent->full_name, rc); + goto err_ret; + } + + max_channels = hweight_long(apps_ch_pipes); + if (max_channels <= 0) { + dev_err(dev, + "%s: Invalid apps owned ports %d\n", + __func__, max_channels); + goto err_ret; + } + + dev_dbg(dev, "%s: max channels = %u\n", + __func__, max_channels); + + for (i = 0; i < ARRAY_SIZE(msm_slim_dais); i++) { + dai_drv = &msm_slim_dais[i]; + dai_drv->capture.channels_max = max_channels; + dai_drv->playback.channels_max = max_channels; + } + + drv_data = devm_kzalloc(dev, sizeof(*drv_data), + GFP_KERNEL); + if (!drv_data) { + rc = -ENOMEM; + goto err_ret; + } + + drv_data->sdev = sdev; + drv_data->num_dais = NUM_SLIM_DAIS; + + rc = msm_dai_slim_populate_dai_data(dev, drv_data); + if (rc) { + dev_err(dev, + "%s: failed to setup dai_data, err = %d\n", + __func__, rc); + goto err_populate_dai; + } + + rc = snd_soc_register_component(&sdev->dev, &msm_dai_slim_component, + msm_slim_dais, NUM_SLIM_DAIS); + if (rc < 0) { + dev_err(dev, "%s: failed to register DAI, err = %d\n", + __func__, rc); + goto err_reg_comp; + } + + dev_set_drvdata(dev, drv_data); + return rc; + +err_reg_comp: + msm_dai_slim_remove_dai_data(dev, drv_data); + +err_populate_dai: + devm_kfree(dev, drv_data); + +err_ret: + return rc; +} + +static int msm_dai_slim_dev_remove(struct slim_device *sdev) +{ + snd_soc_unregister_component(&sdev->dev); + return 0; +} + +static const struct slim_device_id msm_dai_slim_dt_match[] = { + {SLIM_DEV_NAME, 0 }, + {} +}; + +static struct slim_driver msm_dai_slim_driver = { + .driver = { + .name = SLIM_DEV_NAME, + .owner = THIS_MODULE, + }, + .probe = msm_dai_slim_dev_probe, + .remove = msm_dai_slim_dev_remove, + .id_table = msm_dai_slim_dt_match, +}; + +int __init msm_dai_slim_init(void) +{ + int rc; + + rc = slim_driver_register(&msm_dai_slim_driver); + if (rc) + pr_err("%s: failed to register with slimbus driver rc = %d", + __func__, rc); + return rc; +} + +void msm_dai_slim_exit(void) +{ + slim_driver_unregister(&msm_dai_slim_driver); +} + +/* Module information */ +MODULE_DESCRIPTION("Slimbus apps-owned channel handling driver"); +MODULE_LICENSE("GPL v2"); diff --git a/audio-kernel/asoc/msm-dai-stub-v2.c b/audio-kernel/asoc/msm-dai-stub-v2.c new file mode 100644 index 000000000..c8b6b7736 --- /dev/null +++ b/audio-kernel/asoc/msm-dai-stub-v2.c @@ -0,0 +1,392 @@ +/* Copyright (c) 2013-2014, 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 + * 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 +#include +#include +#include +#include +#include +#include +#include + +enum { + STUB_RX, + STUB_TX, + STUB_1_RX, + STUB_1_TX, + STUB_DTMF_TX, + STUB_HOST_RX_CAPTURE_TX, + STUB_HOST_RX_PLAYBACK_RX, + STUB_HOST_TX_CAPTURE_TX, + STUB_HOST_TX_PLAYBACK_RX, +}; + +static int msm_dai_stub_set_channel_map(struct snd_soc_dai *dai, + unsigned int tx_num, unsigned int *tx_slot, + unsigned int rx_num, unsigned int *rx_slot) +{ + pr_debug("%s:\n", __func__); + + return 0; +} + +static struct snd_soc_dai_ops msm_dai_stub_ops = { + .set_channel_map = msm_dai_stub_set_channel_map, +}; + +static int msm_dai_stub_add_route(struct snd_soc_dai *dai) +{ + struct snd_soc_dapm_route intercon; + struct snd_soc_dapm_context *dapm; + + if (!dai || !dai->driver) { + pr_err("%s Invalid params\n", __func__); + return -EINVAL; + } + dapm = snd_soc_component_get_dapm(dai->component); + memset(&intercon, 0, sizeof(intercon)); + if (dai->driver->playback.stream_name && + dai->driver->playback.aif_name) { + dev_dbg(dai->dev, "%s add route for widget %s", + __func__, dai->driver->playback.stream_name); + intercon.source = dai->driver->playback.aif_name; + intercon.sink = dai->driver->playback.stream_name; + dev_dbg(dai->dev, "%s src %s sink %s\n", + __func__, intercon.source, intercon.sink); + snd_soc_dapm_add_routes(dapm, &intercon, 1); + } + if (dai->driver->capture.stream_name && + dai->driver->capture.aif_name) { + dev_dbg(dai->dev, "%s add route for widget %s", + __func__, dai->driver->capture.stream_name); + intercon.sink = dai->driver->capture.aif_name; + intercon.source = dai->driver->capture.stream_name; + dev_dbg(dai->dev, "%s src %s sink %s\n", + __func__, intercon.source, intercon.sink); + snd_soc_dapm_add_routes(dapm, &intercon, 1); + } + return 0; +} + +static int msm_dai_stub_dai_probe(struct snd_soc_dai *dai) +{ + return msm_dai_stub_add_route(dai); +} + +static int msm_dai_stub_dai_remove(struct snd_soc_dai *dai) +{ + pr_debug("%s:\n", __func__); + return 0; +} + +static struct snd_soc_dai_driver msm_dai_stub_dai_rx = { + .playback = { + .stream_name = "Stub Playback", + .aif_name = "STUB_RX", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_dai_stub_ops, + .probe = &msm_dai_stub_dai_probe, + .remove = &msm_dai_stub_dai_remove, +}; + +static struct snd_soc_dai_driver msm_dai_stub_dai_tx[] = { + { + .capture = { + .stream_name = "Stub Capture", + .aif_name = "STUB_TX", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_dai_stub_ops, + .probe = &msm_dai_stub_dai_probe, + .remove = &msm_dai_stub_dai_remove, + }, + { + .capture = { + .stream_name = "Stub1 Capture", + .aif_name = "STUB_1_TX", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_dai_stub_ops, + .probe = &msm_dai_stub_dai_probe, + .remove = &msm_dai_stub_dai_remove, + } +}; + +static struct snd_soc_dai_driver msm_dai_stub_dtmf_tx_dai = { + .capture = { + .stream_name = "DTMF TX", + .aif_name = "STUB_DTMF_TX", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_dai_stub_ops, + .probe = &msm_dai_stub_dai_probe, + .remove = &msm_dai_stub_dai_remove, +}; + +static struct snd_soc_dai_driver msm_dai_stub_host_capture_tx_dai[] = { + { + .capture = { + .stream_name = "CS-VOICE HOST RX CAPTURE", + .aif_name = "STUB_HOST_RX_CAPTURE_TX", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_dai_stub_ops, + .probe = &msm_dai_stub_dai_probe, + .remove = &msm_dai_stub_dai_remove, + }, + { + .capture = { + .stream_name = "CS-VOICE HOST TX CAPTURE", + .aif_name = "STUB_HOST_TX_CAPTURE_TX", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_dai_stub_ops, + .probe = &msm_dai_stub_dai_probe, + .remove = &msm_dai_stub_dai_remove, + }, +}; + +static struct snd_soc_dai_driver msm_dai_stub_host_playback_rx_dai[] = { + { + .playback = { + .stream_name = "CS-VOICE HOST RX PLAYBACK", + .aif_name = "STUB_HOST_RX_PLAYBACK_RX", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_dai_stub_ops, + .probe = &msm_dai_stub_dai_probe, + .remove = &msm_dai_stub_dai_remove, + }, + { + .playback = { + .stream_name = "CS-VOICE HOST TX PLAYBACK", + .aif_name = "STUB_HOST_TX_PLAYBACK_RX", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &msm_dai_stub_ops, + .probe = &msm_dai_stub_dai_probe, + .remove = &msm_dai_stub_dai_remove, + }, +}; + +static const struct snd_soc_component_driver msm_dai_stub_component = { + .name = "msm-dai-stub-dev", +}; + +static int msm_dai_stub_dev_probe(struct platform_device *pdev) +{ + int rc, id = -1; + const char *stub_dev_id = "qcom,msm-dai-stub-dev-id"; + + rc = of_property_read_u32(pdev->dev.of_node, stub_dev_id, &id); + if (rc) { + dev_err(&pdev->dev, + "%s: missing %s in dt node\n", __func__, stub_dev_id); + return rc; + } + + pdev->id = id; + + pr_debug("%s: dev name %s, id:%d\n", __func__, + dev_name(&pdev->dev), pdev->id); + + switch (id) { + case STUB_RX: + rc = snd_soc_register_component(&pdev->dev, + &msm_dai_stub_component, &msm_dai_stub_dai_rx, 1); + break; + case STUB_TX: + rc = snd_soc_register_component(&pdev->dev, + &msm_dai_stub_component, &msm_dai_stub_dai_tx[0], 1); + break; + case STUB_1_TX: + rc = snd_soc_register_component(&pdev->dev, + &msm_dai_stub_component, &msm_dai_stub_dai_tx[1], 1); + break; + case STUB_DTMF_TX: + rc = snd_soc_register_component(&pdev->dev, + &msm_dai_stub_component, + &msm_dai_stub_dtmf_tx_dai, 1); + break; + case STUB_HOST_RX_CAPTURE_TX: + rc = snd_soc_register_component(&pdev->dev, + &msm_dai_stub_component, + &msm_dai_stub_host_capture_tx_dai[0], 1); + break; + case STUB_HOST_TX_CAPTURE_TX: + rc = snd_soc_register_component(&pdev->dev, + &msm_dai_stub_component, + &msm_dai_stub_host_capture_tx_dai[1], 1); + break; + case STUB_HOST_RX_PLAYBACK_RX: + rc = snd_soc_register_component(&pdev->dev, + &msm_dai_stub_component, + &msm_dai_stub_host_playback_rx_dai[0], 1); + break; + case STUB_HOST_TX_PLAYBACK_RX: + rc = snd_soc_register_component(&pdev->dev, + &msm_dai_stub_component, + &msm_dai_stub_host_playback_rx_dai[1], 1); + break; + } + + return rc; +} + +static int msm_dai_stub_dev_remove(struct platform_device *pdev) +{ + snd_soc_unregister_component(&pdev->dev); + return 0; +} + +static const struct of_device_id msm_dai_stub_dev_dt_match[] = { + { .compatible = "qcom,msm-dai-stub-dev", }, + { } +}; +MODULE_DEVICE_TABLE(of, msm_dai_stub_dev_dt_match); + +static struct platform_driver msm_dai_stub_dev = { + .probe = msm_dai_stub_dev_probe, + .remove = msm_dai_stub_dev_remove, + .driver = { + .name = "msm-dai-stub-dev", + .owner = THIS_MODULE, + .of_match_table = msm_dai_stub_dev_dt_match, + }, +}; + +static int msm_dai_stub_probe(struct platform_device *pdev) +{ + int rc = 0; + + dev_dbg(&pdev->dev, "dev name %s\n", dev_name(&pdev->dev)); + + rc = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev); + if (rc) { + dev_err(&pdev->dev, "%s: failed to add child nodes, rc=%d\n", + __func__, rc); + } else + dev_dbg(&pdev->dev, "%s: added child node\n", __func__); + + return rc; +} + +static int msm_dai_stub_remove(struct platform_device *pdev) +{ + pr_debug("%s:\n", __func__); + + return 0; +} + +static const struct of_device_id msm_dai_stub_dt_match[] = { + {.compatible = "qcom,msm-dai-stub"}, + {} +}; + +MODULE_DEVICE_TABLE(of, msm_dai_stub_dt_match); + + +static struct platform_driver msm_dai_stub_driver = { + .probe = msm_dai_stub_probe, + .remove = msm_dai_stub_remove, + .driver = { + .name = "msm-dai-stub", + .owner = THIS_MODULE, + .of_match_table = msm_dai_stub_dt_match, + }, +}; + +int __init msm_dai_stub_init(void) +{ + int rc = 0; + + pr_debug("%s:\n", __func__); + + rc = platform_driver_register(&msm_dai_stub_driver); + if (rc) { + pr_err("%s: fail to register dai q6 driver", __func__); + goto fail; + } + + rc = platform_driver_register(&msm_dai_stub_dev); + if (rc) { + pr_err("%s: fail to register dai q6 dev driver", __func__); + goto dai_stub_dev_fail; + } + return rc; + +dai_stub_dev_fail: + platform_driver_unregister(&msm_dai_stub_driver); +fail: + return rc; +} + +void msm_dai_stub_exit(void) +{ + pr_debug("%s:\n", __func__); + + platform_driver_unregister(&msm_dai_stub_dev); + platform_driver_unregister(&msm_dai_stub_driver); +} + +/* Module information */ +MODULE_DESCRIPTION("MSM Stub DSP DAI driver"); +MODULE_LICENSE("GPL v2"); diff --git a/audio-kernel/asoc/msm-dolby-common.h b/audio-kernel/asoc/msm-dolby-common.h new file mode 100644 index 000000000..885d283ef --- /dev/null +++ b/audio-kernel/asoc/msm-dolby-common.h @@ -0,0 +1,266 @@ +/* + * Copyright (c) 2013-2014, 2016 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 _MSM_DOLBY_COMMON_H_ +#define _MSM_DOLBY_COMMON_H_ + +#include + + +#define DOLBY_BUNDLE_MODULE_ID 0x00010723 +#define DOLBY_VISUALIZER_MODULE_ID 0x0001072B + +#define DOLBY_PARAM_ID_VDHE 0x0001074D +#define DOLBY_PARAM_ID_VSPE 0x00010750 +#define DOLBY_PARAM_ID_DSSF 0x00010753 +#define DOLBY_PARAM_ID_DVLI 0x0001073E +#define DOLBY_PARAM_ID_DVLO 0x0001073F +#define DOLBY_PARAM_ID_DVLE 0x0001073C +#define DOLBY_PARAM_ID_DVMC 0x00010741 +#define DOLBY_PARAM_ID_DVME 0x00010740 +#define DOLBY_PARAM_ID_IENB 0x00010744 +#define DOLBY_PARAM_ID_IEBF 0x00010745 +#define DOLBY_PARAM_ID_IEON 0x00010743 +#define DOLBY_PARAM_ID_DEON 0x00010738 +#define DOLBY_PARAM_ID_NGON 0x00010736 +#define DOLBY_PARAM_ID_GEON 0x00010748 +#define DOLBY_PARAM_ID_GENB 0x00010749 +#define DOLBY_PARAM_ID_GEBF 0x0001074A +#define DOLBY_PARAM_ID_AONB 0x0001075B +#define DOLBY_PARAM_ID_AOBF 0x0001075C +#define DOLBY_PARAM_ID_AOBG 0x0001075D +#define DOLBY_PARAM_ID_AOON 0x00010759 +#define DOLBY_PARAM_ID_ARNB 0x0001075F +#define DOLBY_PARAM_ID_ARBF 0x00010760 +#define DOLBY_PARAM_ID_PLB 0x00010768 +#define DOLBY_PARAM_ID_PLMD 0x00010767 +#define DOLBY_PARAM_ID_DHSB 0x0001074E +#define DOLBY_PARAM_ID_DHRG 0x0001074F +#define DOLBY_PARAM_ID_DSSB 0x00010751 +#define DOLBY_PARAM_ID_DSSA 0x00010752 +#define DOLBY_PARAM_ID_DVLA 0x0001073D +#define DOLBY_PARAM_ID_IEBT 0x00010746 +#define DOLBY_PARAM_ID_IEA 0x0001076A +#define DOLBY_PARAM_ID_DEA 0x00010739 +#define DOLBY_PARAM_ID_DED 0x0001073A +#define DOLBY_PARAM_ID_GEBG 0x0001074B +#define DOLBY_PARAM_ID_AOCC 0x0001075A +#define DOLBY_PARAM_ID_ARBI 0x00010761 +#define DOLBY_PARAM_ID_ARBL 0x00010762 +#define DOLBY_PARAM_ID_ARBH 0x00010763 +#define DOLBY_PARAM_ID_AROD 0x00010764 +#define DOLBY_PARAM_ID_ARTP 0x00010765 +#define DOLBY_PARAM_ID_VMON 0x00010756 +#define DOLBY_PARAM_ID_VMB 0x00010757 +#define DOLBY_PARAM_ID_VCNB 0x00010733 +#define DOLBY_PARAM_ID_VCBF 0x00010734 +#define DOLBY_PARAM_ID_PREG 0x00010728 +#define DOLBY_PARAM_ID_VEN 0x00010732 +#define DOLBY_PARAM_ID_PSTG 0x00010729 +#define DOLBY_PARAM_ID_INIT_ENDP 0x00010727 + +/* Not Used with Set Param kcontrol, only to query using Get Param */ +#define DOLBY_PARAM_ID_VER 0x00010726 + +#define DOLBY_PARAM_ID_VCBG 0x00010730 +#define DOLBY_PARAM_ID_VCBE 0x00010731 + +/* DOLBY DAP control params */ +#define DOLBY_COMMIT_ALL_TO_DSP 0x70000001 +#define DOLBY_COMMIT_TO_DSP 0x70000002 +#define DOLBY_USE_CACHE 0x70000003 +#define DOLBY_AUTO_ENDP 0x70000004 +#define DOLBY_AUTO_ENDDEP_PARAMS 0x70000005 +#define DOLBY_DAP_BYPASS 0x70000006 + +#define DOLBY_ENABLE_CUSTOM_STEREO 0x000108c7 + +/* DOLBY DAP offsets start */ +#define DOLBY_PARAM_VDHE_LENGTH 1 +#define DOLBY_PARAM_VDHE_OFFSET 0 +#define DOLBY_PARAM_VSPE_LENGTH 1 +#define DOLBY_PARAM_VSPE_OFFSET (DOLBY_PARAM_VDHE_OFFSET + \ + DOLBY_PARAM_VDHE_LENGTH) +#define DOLBY_PARAM_DSSF_LENGTH 1 +#define DOLBY_PARAM_DSSF_OFFSET (DOLBY_PARAM_VSPE_OFFSET + \ + DOLBY_PARAM_VSPE_LENGTH) +#define DOLBY_PARAM_DVLI_LENGTH 1 +#define DOLBY_PARAM_DVLI_OFFSET (DOLBY_PARAM_DSSF_OFFSET + \ + DOLBY_PARAM_DSSF_LENGTH) +#define DOLBY_PARAM_DVLO_LENGTH 1 +#define DOLBY_PARAM_DVLO_OFFSET (DOLBY_PARAM_DVLI_OFFSET + \ + DOLBY_PARAM_DVLI_LENGTH) +#define DOLBY_PARAM_DVLE_LENGTH 1 +#define DOLBY_PARAM_DVLE_OFFSET (DOLBY_PARAM_DVLO_OFFSET + \ + DOLBY_PARAM_DVLO_LENGTH) +#define DOLBY_PARAM_DVMC_LENGTH 1 +#define DOLBY_PARAM_DVMC_OFFSET (DOLBY_PARAM_DVLE_OFFSET + \ + DOLBY_PARAM_DVLE_LENGTH) +#define DOLBY_PARAM_DVME_LENGTH 1 +#define DOLBY_PARAM_DVME_OFFSET (DOLBY_PARAM_DVMC_OFFSET + \ + DOLBY_PARAM_DVMC_LENGTH) +#define DOLBY_PARAM_IENB_LENGTH 1 +#define DOLBY_PARAM_IENB_OFFSET (DOLBY_PARAM_DVME_OFFSET + \ + DOLBY_PARAM_DVME_LENGTH) +#define DOLBY_PARAM_IEBF_LENGTH 40 +#define DOLBY_PARAM_IEBF_OFFSET (DOLBY_PARAM_IENB_OFFSET + \ + DOLBY_PARAM_IENB_LENGTH) +#define DOLBY_PARAM_IEON_LENGTH 1 +#define DOLBY_PARAM_IEON_OFFSET (DOLBY_PARAM_IEBF_OFFSET + \ + DOLBY_PARAM_IEBF_LENGTH) +#define DOLBY_PARAM_DEON_LENGTH 1 +#define DOLBY_PARAM_DEON_OFFSET (DOLBY_PARAM_IEON_OFFSET + \ + DOLBY_PARAM_IEON_LENGTH) +#define DOLBY_PARAM_NGON_LENGTH 1 +#define DOLBY_PARAM_NGON_OFFSET (DOLBY_PARAM_DEON_OFFSET + \ + DOLBY_PARAM_DEON_LENGTH) +#define DOLBY_PARAM_GEON_LENGTH 1 +#define DOLBY_PARAM_GEON_OFFSET (DOLBY_PARAM_NGON_OFFSET + \ + DOLBY_PARAM_NGON_LENGTH) +#define DOLBY_PARAM_GENB_LENGTH 1 +#define DOLBY_PARAM_GENB_OFFSET (DOLBY_PARAM_GEON_OFFSET + \ + DOLBY_PARAM_GEON_LENGTH) +#define DOLBY_PARAM_GEBF_LENGTH 40 +#define DOLBY_PARAM_GEBF_OFFSET (DOLBY_PARAM_GENB_OFFSET + \ + DOLBY_PARAM_GENB_LENGTH) +#define DOLBY_PARAM_AONB_LENGTH 1 +#define DOLBY_PARAM_AONB_OFFSET (DOLBY_PARAM_GEBF_OFFSET + \ + DOLBY_PARAM_GEBF_LENGTH) +#define DOLBY_PARAM_AOBF_LENGTH 40 +#define DOLBY_PARAM_AOBF_OFFSET (DOLBY_PARAM_AONB_OFFSET + \ + DOLBY_PARAM_AONB_LENGTH) +#define DOLBY_PARAM_AOBG_LENGTH 329 +#define DOLBY_PARAM_AOBG_OFFSET (DOLBY_PARAM_AOBF_OFFSET + \ + DOLBY_PARAM_AOBF_LENGTH) +#define DOLBY_PARAM_AOON_LENGTH 1 +#define DOLBY_PARAM_AOON_OFFSET (DOLBY_PARAM_AOBG_OFFSET + \ + DOLBY_PARAM_AOBG_LENGTH) +#define DOLBY_PARAM_ARNB_LENGTH 1 +#define DOLBY_PARAM_ARNB_OFFSET (DOLBY_PARAM_AOON_OFFSET + \ + DOLBY_PARAM_AOON_LENGTH) +#define DOLBY_PARAM_ARBF_LENGTH 40 +#define DOLBY_PARAM_ARBF_OFFSET (DOLBY_PARAM_ARNB_OFFSET + \ + DOLBY_PARAM_ARNB_LENGTH) +#define DOLBY_PARAM_PLB_LENGTH 1 +#define DOLBY_PARAM_PLB_OFFSET (DOLBY_PARAM_ARBF_OFFSET + \ + DOLBY_PARAM_ARBF_LENGTH) +#define DOLBY_PARAM_PLMD_LENGTH 1 +#define DOLBY_PARAM_PLMD_OFFSET (DOLBY_PARAM_PLB_OFFSET + \ + DOLBY_PARAM_PLB_LENGTH) +#define DOLBY_PARAM_DHSB_LENGTH 1 +#define DOLBY_PARAM_DHSB_OFFSET (DOLBY_PARAM_PLMD_OFFSET + \ + DOLBY_PARAM_PLMD_LENGTH) +#define DOLBY_PARAM_DHRG_LENGTH 1 +#define DOLBY_PARAM_DHRG_OFFSET (DOLBY_PARAM_DHSB_OFFSET + \ + DOLBY_PARAM_DHSB_LENGTH) +#define DOLBY_PARAM_DSSB_LENGTH 1 +#define DOLBY_PARAM_DSSB_OFFSET (DOLBY_PARAM_DHRG_OFFSET + \ + DOLBY_PARAM_DHRG_LENGTH) +#define DOLBY_PARAM_DSSA_LENGTH 1 +#define DOLBY_PARAM_DSSA_OFFSET (DOLBY_PARAM_DSSB_OFFSET + \ + DOLBY_PARAM_DSSB_LENGTH) +#define DOLBY_PARAM_DVLA_LENGTH 1 +#define DOLBY_PARAM_DVLA_OFFSET (DOLBY_PARAM_DSSA_OFFSET + \ + DOLBY_PARAM_DSSA_LENGTH) +#define DOLBY_PARAM_IEBT_LENGTH 40 +#define DOLBY_PARAM_IEBT_OFFSET (DOLBY_PARAM_DVLA_OFFSET + \ + DOLBY_PARAM_DVLA_LENGTH) +#define DOLBY_PARAM_IEA_LENGTH 1 +#define DOLBY_PARAM_IEA_OFFSET (DOLBY_PARAM_IEBT_OFFSET + \ + DOLBY_PARAM_IEBT_LENGTH) +#define DOLBY_PARAM_DEA_LENGTH 1 +#define DOLBY_PARAM_DEA_OFFSET (DOLBY_PARAM_IEA_OFFSET + \ + DOLBY_PARAM_IEA_LENGTH) +#define DOLBY_PARAM_DED_LENGTH 1 +#define DOLBY_PARAM_DED_OFFSET (DOLBY_PARAM_DEA_OFFSET + \ + DOLBY_PARAM_DEA_LENGTH) +#define DOLBY_PARAM_GEBG_LENGTH 40 +#define DOLBY_PARAM_GEBG_OFFSET (DOLBY_PARAM_DED_OFFSET + \ + DOLBY_PARAM_DED_LENGTH) +#define DOLBY_PARAM_AOCC_LENGTH 1 +#define DOLBY_PARAM_AOCC_OFFSET (DOLBY_PARAM_GEBG_OFFSET + \ + DOLBY_PARAM_GEBG_LENGTH) +#define DOLBY_PARAM_ARBI_LENGTH 40 +#define DOLBY_PARAM_ARBI_OFFSET (DOLBY_PARAM_AOCC_OFFSET + \ + DOLBY_PARAM_AOCC_LENGTH) +#define DOLBY_PARAM_ARBL_LENGTH 40 +#define DOLBY_PARAM_ARBL_OFFSET (DOLBY_PARAM_ARBI_OFFSET + \ + DOLBY_PARAM_ARBI_LENGTH) +#define DOLBY_PARAM_ARBH_LENGTH 40 +#define DOLBY_PARAM_ARBH_OFFSET (DOLBY_PARAM_ARBL_OFFSET + \ + DOLBY_PARAM_ARBL_LENGTH) +#define DOLBY_PARAM_AROD_LENGTH 1 +#define DOLBY_PARAM_AROD_OFFSET (DOLBY_PARAM_ARBH_OFFSET + \ + DOLBY_PARAM_ARBH_LENGTH) +#define DOLBY_PARAM_ARTP_LENGTH 1 +#define DOLBY_PARAM_ARTP_OFFSET (DOLBY_PARAM_AROD_OFFSET + \ + DOLBY_PARAM_AROD_LENGTH) +#define DOLBY_PARAM_VMON_LENGTH 1 +#define DOLBY_PARAM_VMON_OFFSET (DOLBY_PARAM_ARTP_OFFSET + \ + DOLBY_PARAM_ARTP_LENGTH) +#define DOLBY_PARAM_VMB_LENGTH 1 +#define DOLBY_PARAM_VMB_OFFSET (DOLBY_PARAM_VMON_OFFSET + \ + DOLBY_PARAM_VMON_LENGTH) +#define DOLBY_PARAM_VCNB_LENGTH 1 +#define DOLBY_PARAM_VCNB_OFFSET (DOLBY_PARAM_VMB_OFFSET + \ + DOLBY_PARAM_VMB_LENGTH) +#define DOLBY_PARAM_VCBF_LENGTH 20 +#define DOLBY_PARAM_VCBF_OFFSET (DOLBY_PARAM_VCNB_OFFSET + \ + DOLBY_PARAM_VCNB_LENGTH) +#define DOLBY_PARAM_PREG_LENGTH 1 +#define DOLBY_PARAM_PREG_OFFSET (DOLBY_PARAM_VCBF_OFFSET + \ + DOLBY_PARAM_VCBF_LENGTH) +#define DOLBY_PARAM_VEN_LENGTH 1 +#define DOLBY_PARAM_VEN_OFFSET (DOLBY_PARAM_PREG_OFFSET + \ + DOLBY_PARAM_PREG_LENGTH) +#define DOLBY_PARAM_PSTG_LENGTH 1 +#define DOLBY_PARAM_PSTG_OFFSET (DOLBY_PARAM_VEN_OFFSET + \ + DOLBY_PARAM_VEN_LENGTH) + +#define DOLBY_PARAM_INT_ENDP_LENGTH 1 +#define DOLBY_PARAM_PAYLOAD_SIZE 3 +#define DOLBY_MAX_LENGTH_INDIVIDUAL_PARAM 329 + +#define TOTAL_LENGTH_DOLBY_PARAM 745 +#define DOLBY_VIS_PARAM_HEADER_SIZE 25 +#define DOLBY_PARAM_VCNB_MAX_LENGTH 40 + +#define DOLBY_INVALID_PORT_ID -1 + +enum { + DEVICE_NONE = 0x0, + /* output devices */ + EARPIECE = 0x1, + SPEAKER = 0x2, + WIRED_HEADSET = 0x4, + WIRED_HEADPHONE = 0x8, + BLUETOOTH_SCO = 0x10, + BLUETOOTH_SCO_HEADSET = 0x20, + BLUETOOTH_SCO_CARKIT = 0x40, + BLUETOOTH_A2DP = 0x80, + BLUETOOTH_A2DP_HEADPHONES = 0x100, + BLUETOOTH_A2DP_SPEAKER = 0x200, + AUX_DIGITAL = 0x400, + ANLG_DOCK_HEADSET = 0x800, + DGTL_DOCK_HEADSET = 0x1000, + USB_ACCESSORY = 0x2000, + USB_DEVICE = 0x4000, + REMOTE_SUBMIX = 0x8000, + ANC_HEADSET = 0x10000, + ANC_HEADPHONE = 0x20000, + PROXY = 0x2000000, + FM = 0x100000, + FM_TX = 0x1000000, + DEVICE_OUT_DEFAULT = 0x40000000, + DEVICE_OUT_ALL = 0x403FFFFF, +}; +#endif diff --git a/audio-kernel/asoc/msm-dolby-dap-config.h b/audio-kernel/asoc/msm-dolby-dap-config.h new file mode 100644 index 000000000..b2b713907 --- /dev/null +++ b/audio-kernel/asoc/msm-dolby-dap-config.h @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2013-2014, 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 + * 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 _MSM_DOLBY_DAP_CONFIG_H_ +#define _MSM_DOLBY_DAP_CONFIG_H_ + +#include +#include "msm-dolby-common.h" + +#ifdef CONFIG_DOLBY_DAP +/* DOLBY DOLBY GUIDS */ +#define DOLBY_ADM_COPP_TOPOLOGY_ID 0x0001033B +#define NUM_DOLBY_ENDP_DEVICE 23 + +#define DOLBY_NUM_ENDP_DEPENDENT_PARAMS 3 +#define DOLBY_ENDDEP_PARAM_DVLO_OFFSET 0 +#define DOLBY_ENDDEP_PARAM_DVLO_LENGTH 1 +#define DOLBY_ENDDEP_PARAM_DVLI_OFFSET (DOLBY_ENDDEP_PARAM_DVLO_OFFSET + \ + DOLBY_ENDDEP_PARAM_DVLO_LENGTH) +#define DOLBY_ENDDEP_PARAM_DVLI_LENGTH 1 +#define DOLBY_ENDDEP_PARAM_VMB_OFFSET (DOLBY_ENDDEP_PARAM_DVLI_OFFSET + \ + DOLBY_ENDDEP_PARAM_DVLI_LENGTH) +#define DOLBY_ENDDEP_PARAM_VMB_LENGTH 1 +#define DOLBY_ENDDEP_PARAM_LENGTH (DOLBY_ENDDEP_PARAM_DVLO_LENGTH + \ + DOLBY_ENDDEP_PARAM_DVLI_LENGTH + DOLBY_ENDDEP_PARAM_VMB_LENGTH) + +#define MAX_DOLBY_PARAMS 47 +#define MAX_DOLBY_CTRL_PARAMS 5 +#define ALL_DOLBY_PARAMS (MAX_DOLBY_PARAMS + \ + MAX_DOLBY_CTRL_PARAMS) +#define DOLBY_COMMIT_ALL_IDX MAX_DOLBY_PARAMS +#define DOLBY_COMMIT_IDX (MAX_DOLBY_PARAMS+1) +#define DOLBY_USE_CACHE_IDX (MAX_DOLBY_PARAMS+2) +#define DOLBY_AUTO_ENDP_IDX (MAX_DOLBY_PARAMS+3) +#define DOLBY_AUTO_ENDDEP_IDX (MAX_DOLBY_PARAMS+4) + +/* DOLBY device definitions */ +enum { + DOLBY_ENDP_INT_SPEAKERS = 0, + DOLBY_ENDP_EXT_SPEAKERS, + DOLBY_ENDP_HEADPHONES, + DOLBY_ENDP_HDMI, + DOLBY_ENDP_SPDIF, + DOLBY_ENDP_DLNA, + DOLBY_ENDP_ANALOG, +}; + +/* DOLBY device definitions end */ + +struct dolby_dap_params { + uint32_t value[TOTAL_LENGTH_DOLBY_PARAM + MAX_DOLBY_PARAMS]; +} __packed; + +int msm_dolby_dap_init(int port_id, int copp_idx, int channels, + bool is_custom_stereo_on); +void msm_dolby_dap_deinit(int port_id); +void msm_dolby_dap_add_controls(struct snd_soc_platform *platform); +int dolby_dap_set_custom_stereo_onoff(int port_id, int copp_idx, + bool is_custom_stereo_enabled); +/* Dolby DOLBY end */ +#else +int msm_dolby_dap_init(int port_id, int copp_idx, int channels, + bool is_custom_stereo_on) +{ + return 0; +} +void msm_dolby_dap_deinit(int port_id) { } +void msm_dolby_dap_add_controls(struct snd_soc_platform *platform) { } +int dolby_dap_set_custom_stereo_onoff(int port_id, int copp_idx, + bool is_custom_stereo_enabled) +{ + return 0; +} +#endif + +#endif diff --git a/audio-kernel/asoc/msm-ds2-dap-config.c b/audio-kernel/asoc/msm-ds2-dap-config.c new file mode 100644 index 000000000..ad7a3c6a4 --- /dev/null +++ b/audio-kernel/asoc/msm-ds2-dap-config.c @@ -0,0 +1,2204 @@ +/* + * Copyright (c) 2013-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 +#include +#include +#include +#include +#include +#include + +#include "msm-ds2-dap-config.h" +#include "msm-pcm-routing-v2.h" + + +/* ramp up/down for 30ms */ +#define DOLBY_SOFT_VOLUME_PERIOD 40 +/* Step value 0ms or 0us */ +#define DOLBY_SOFT_VOLUME_STEP 1000 +#define DOLBY_ADDITIONAL_RAMP_WAIT 10 +#define SOFT_VOLUME_PARAM_SIZE 3 +#define PARAM_PAYLOAD_SIZE 3 + +enum { + DOLBY_SOFT_VOLUME_CURVE_LINEAR = 0, + DOLBY_SOFT_VOLUME_CURVE_EXP, + DOLBY_SOFT_VOLUME_CURVE_LOG, +}; + +#define VOLUME_ZERO_GAIN 0x0 +#define VOLUME_UNITY_GAIN 0x2000 +/* Wait time for module enable/disble */ +#define DOLBY_MODULE_ENABLE_PERIOD 50 + +/* DOLBY device definitions end */ +enum { + DOLBY_OFF_CACHE = 0, + DOLBY_SPEAKER_CACHE, + DOLBY_HEADPHONE_CACHE, + DOLBY_HDMI_CACHE, + DOLBY_WFD_CACHE, + DOLBY_FM_CACHE, + DOLBY_MAX_CACHE, +}; + +enum { + DAP_SOFT_BYPASS = 0, + DAP_HARD_BYPASS, +}; + +enum { + MODULE_DISABLE = 0, + MODULE_ENABLE, +}; +/* dolby param ids to/from dsp */ +static uint32_t ds2_dap_params_id[MAX_DS2_PARAMS] = { + DOLBY_PARAM_ID_VDHE, DOLBY_PARAM_ID_VSPE, DOLBY_PARAM_ID_DSSF, + DOLBY_PARAM_ID_DVLI, DOLBY_PARAM_ID_DVLO, DOLBY_PARAM_ID_DVLE, + DOLBY_PARAM_ID_DVMC, DOLBY_PARAM_ID_DVME, DOLBY_PARAM_ID_IENB, + DOLBY_PARAM_ID_IEBF, DOLBY_PARAM_ID_IEON, DOLBY_PARAM_ID_DEON, + DOLBY_PARAM_ID_NGON, DOLBY_PARAM_ID_GEON, DOLBY_PARAM_ID_GENB, + DOLBY_PARAM_ID_GEBF, DOLBY_PARAM_ID_AONB, DOLBY_PARAM_ID_AOBF, + DOLBY_PARAM_ID_AOBG, DOLBY_PARAM_ID_AOON, DOLBY_PARAM_ID_ARNB, + DOLBY_PARAM_ID_ARBF, DOLBY_PARAM_ID_PLB, DOLBY_PARAM_ID_PLMD, + DOLBY_PARAM_ID_DHSB, DOLBY_PARAM_ID_DHRG, DOLBY_PARAM_ID_DSSB, + DOLBY_PARAM_ID_DSSA, DOLBY_PARAM_ID_DVLA, DOLBY_PARAM_ID_IEBT, + DOLBY_PARAM_ID_IEA, DOLBY_PARAM_ID_DEA, DOLBY_PARAM_ID_DED, + DOLBY_PARAM_ID_GEBG, DOLBY_PARAM_ID_AOCC, DOLBY_PARAM_ID_ARBI, + DOLBY_PARAM_ID_ARBL, DOLBY_PARAM_ID_ARBH, DOLBY_PARAM_ID_AROD, + DOLBY_PARAM_ID_ARTP, DOLBY_PARAM_ID_VMON, DOLBY_PARAM_ID_VMB, + DOLBY_PARAM_ID_VCNB, DOLBY_PARAM_ID_VCBF, DOLBY_PARAM_ID_PREG, + DOLBY_PARAM_ID_VEN, DOLBY_PARAM_ID_PSTG, DOLBY_PARAM_ID_INIT_ENDP, +}; + +/* modifed state: 0x00000000 - Not updated + * > 0x00000000 && < 0x00010000 + * Updated and not committed to DSP + * 0x00010001 - Updated and committed to DSP + * > 0x00010001 - Modified the committed value + */ +/* param offset */ +static uint32_t ds2_dap_params_offset[MAX_DS2_PARAMS] = { + DOLBY_PARAM_VDHE_OFFSET, DOLBY_PARAM_VSPE_OFFSET, + DOLBY_PARAM_DSSF_OFFSET, DOLBY_PARAM_DVLI_OFFSET, + DOLBY_PARAM_DVLO_OFFSET, DOLBY_PARAM_DVLE_OFFSET, + DOLBY_PARAM_DVMC_OFFSET, DOLBY_PARAM_DVME_OFFSET, + DOLBY_PARAM_IENB_OFFSET, DOLBY_PARAM_IEBF_OFFSET, + DOLBY_PARAM_IEON_OFFSET, DOLBY_PARAM_DEON_OFFSET, + DOLBY_PARAM_NGON_OFFSET, DOLBY_PARAM_GEON_OFFSET, + DOLBY_PARAM_GENB_OFFSET, DOLBY_PARAM_GEBF_OFFSET, + DOLBY_PARAM_AONB_OFFSET, DOLBY_PARAM_AOBF_OFFSET, + DOLBY_PARAM_AOBG_OFFSET, DOLBY_PARAM_AOON_OFFSET, + DOLBY_PARAM_ARNB_OFFSET, DOLBY_PARAM_ARBF_OFFSET, + DOLBY_PARAM_PLB_OFFSET, DOLBY_PARAM_PLMD_OFFSET, + DOLBY_PARAM_DHSB_OFFSET, DOLBY_PARAM_DHRG_OFFSET, + DOLBY_PARAM_DSSB_OFFSET, DOLBY_PARAM_DSSA_OFFSET, + DOLBY_PARAM_DVLA_OFFSET, DOLBY_PARAM_IEBT_OFFSET, + DOLBY_PARAM_IEA_OFFSET, DOLBY_PARAM_DEA_OFFSET, + DOLBY_PARAM_DED_OFFSET, DOLBY_PARAM_GEBG_OFFSET, + DOLBY_PARAM_AOCC_OFFSET, DOLBY_PARAM_ARBI_OFFSET, + DOLBY_PARAM_ARBL_OFFSET, DOLBY_PARAM_ARBH_OFFSET, + DOLBY_PARAM_AROD_OFFSET, DOLBY_PARAM_ARTP_OFFSET, + DOLBY_PARAM_VMON_OFFSET, DOLBY_PARAM_VMB_OFFSET, + DOLBY_PARAM_VCNB_OFFSET, DOLBY_PARAM_VCBF_OFFSET, + DOLBY_PARAM_PREG_OFFSET, DOLBY_PARAM_VEN_OFFSET, + DOLBY_PARAM_PSTG_OFFSET, DOLBY_PARAM_INT_ENDP_OFFSET, +}; +/* param_length */ +static uint32_t ds2_dap_params_length[MAX_DS2_PARAMS] = { + DOLBY_PARAM_VDHE_LENGTH, DOLBY_PARAM_VSPE_LENGTH, + DOLBY_PARAM_DSSF_LENGTH, DOLBY_PARAM_DVLI_LENGTH, + DOLBY_PARAM_DVLO_LENGTH, DOLBY_PARAM_DVLE_LENGTH, + DOLBY_PARAM_DVMC_LENGTH, DOLBY_PARAM_DVME_LENGTH, + DOLBY_PARAM_IENB_LENGTH, DOLBY_PARAM_IEBF_LENGTH, + DOLBY_PARAM_IEON_LENGTH, DOLBY_PARAM_DEON_LENGTH, + DOLBY_PARAM_NGON_LENGTH, DOLBY_PARAM_GEON_LENGTH, + DOLBY_PARAM_GENB_LENGTH, DOLBY_PARAM_GEBF_LENGTH, + DOLBY_PARAM_AONB_LENGTH, DOLBY_PARAM_AOBF_LENGTH, + DOLBY_PARAM_AOBG_LENGTH, DOLBY_PARAM_AOON_LENGTH, + DOLBY_PARAM_ARNB_LENGTH, DOLBY_PARAM_ARBF_LENGTH, + DOLBY_PARAM_PLB_LENGTH, DOLBY_PARAM_PLMD_LENGTH, + DOLBY_PARAM_DHSB_LENGTH, DOLBY_PARAM_DHRG_LENGTH, + DOLBY_PARAM_DSSB_LENGTH, DOLBY_PARAM_DSSA_LENGTH, + DOLBY_PARAM_DVLA_LENGTH, DOLBY_PARAM_IEBT_LENGTH, + DOLBY_PARAM_IEA_LENGTH, DOLBY_PARAM_DEA_LENGTH, + DOLBY_PARAM_DED_LENGTH, DOLBY_PARAM_GEBG_LENGTH, + DOLBY_PARAM_AOCC_LENGTH, DOLBY_PARAM_ARBI_LENGTH, + DOLBY_PARAM_ARBL_LENGTH, DOLBY_PARAM_ARBH_LENGTH, + DOLBY_PARAM_AROD_LENGTH, DOLBY_PARAM_ARTP_LENGTH, + DOLBY_PARAM_VMON_LENGTH, DOLBY_PARAM_VMB_LENGTH, + DOLBY_PARAM_VCNB_LENGTH, DOLBY_PARAM_VCBF_LENGTH, + DOLBY_PARAM_PREG_LENGTH, DOLBY_PARAM_VEN_LENGTH, + DOLBY_PARAM_PSTG_LENGTH, DOLBY_PARAM_INT_ENDP_LENGTH, +}; + +struct ds2_dap_params_s { + int32_t params_val[TOTAL_LENGTH_DS2_PARAM]; + int32_t dap_params_modified[MAX_DS2_PARAMS]; +}; + +struct audio_rx_cal_data { + char aud_proc_data[AUD_PROC_BLOCK_SIZE]; + int32_t aud_proc_size; + char aud_vol_data[AUD_VOL_BLOCK_SIZE]; + int32_t aud_vol_size; +}; + +static struct ds2_dap_params_s ds2_dap_params[DOLBY_MAX_CACHE]; + +struct ds2_device_mapping { + int32_t device_id; /* audio_out_... */ + int port_id; /* afe port. constant for a target variant. routing-v2*/ + /*Only one Dolby COPP for a specific port*/ + int copp_idx; /* idx for the copp port on which ds2 is active */ + int cache_dev; /* idx to a shared parameter array dependent on device*/ + uint32_t stream_ref_count; + bool active; + void *cal_data; +}; + +static struct ds2_device_mapping dev_map[DS2_DEVICES_ALL]; + +struct ds2_dap_params_states_s { + bool use_cache; + bool dap_bypass; + bool dap_bypass_type; + bool node_opened; + int32_t device; + bool custom_stereo_onoff; +}; + +static struct ds2_dap_params_states_s ds2_dap_params_states = {true, false, + false, DEVICE_NONE}; + +static int all_supported_devices = EARPIECE|SPEAKER|WIRED_HEADSET| + WIRED_HEADPHONE|BLUETOOTH_SCO|AUX_DIGITAL| + ANLG_DOCK_HEADSET|DGTL_DOCK_HEADSET| + REMOTE_SUBMIX|ANC_HEADSET|ANC_HEADPHONE| + PROXY|FM|FM_TX|DEVICE_NONE| + BLUETOOTH_SCO_HEADSET|BLUETOOTH_SCO_CARKIT; + + +static void msm_ds2_dap_check_and_update_ramp_wait(int port_id, int copp_idx, + int *ramp_wait) +{ + + int32_t *update_params_value = NULL; + uint32_t params_length = SOFT_VOLUME_PARAM_SIZE * sizeof(uint32_t); + uint32_t param_payload_len = PARAM_PAYLOAD_SIZE * sizeof(uint32_t); + struct param_hdr_v3 param_hdr; + int rc = 0; + + update_params_value = kzalloc(params_length + param_payload_len, + GFP_KERNEL); + if (!update_params_value) { + pr_err("%s: params memory alloc failed\n", __func__); + goto end; + } + + memset(¶m_hdr, 0, sizeof(param_hdr)); + param_hdr.module_id = AUDPROC_MODULE_ID_VOL_CTRL; + param_hdr.instance_id = INSTANCE_ID_0; + param_hdr.param_id = AUDPROC_PARAM_ID_SOFT_VOL_STEPPING_PARAMETERS; + param_hdr.param_size = params_length + param_payload_len; + rc = adm_get_pp_params(port_id, copp_idx, ADM_CLIENT_ID_DEFAULT, NULL, + ¶m_hdr, (char *) update_params_value); + if (rc == 0) { + pr_debug("%s: params_value [0x%x, 0x%x, 0x%x]\n", + __func__, update_params_value[0], + update_params_value[1], + update_params_value[2]); + *ramp_wait = update_params_value[0]; + } +end: + kfree(update_params_value); + /* + * No error returned as we do not need to error out from dap on/dap + * bypass. The default ramp parameter will be used to wait during + * ramp down. + */ +} + +static int msm_ds2_dap_set_vspe_vdhe(int dev_map_idx, + bool is_custom_stereo_enabled) +{ + u8 *packed_param_data = NULL; + u8 *param_data = NULL; + struct param_hdr_v3 param_hdr; + u32 packed_param_size = 0; + u32 param_size = 0; + int cdev; + int rc = 0; + + if (dev_map_idx < 0 || dev_map_idx >= DS2_DEVICES_ALL) { + pr_err("%s: invalid dev map index %d\n", __func__, dev_map_idx); + rc = -EINVAL; + goto end; + } + + if (dev_map[dev_map_idx].port_id == DOLBY_INVALID_PORT_ID) { + pr_err("%s: Invalid port id\n", __func__); + rc = -EINVAL; + goto end; + } + + if ((dev_map[dev_map_idx].copp_idx < 0) || + (dev_map[dev_map_idx].copp_idx >= MAX_COPPS_PER_PORT)) { + pr_err("%s: Invalid copp_idx\n", __func__); + rc = -EINVAL; + goto end; + } + + if ((dev_map[dev_map_idx].port_id != SLIMBUS_0_RX) && + (dev_map[dev_map_idx].port_id != RT_PROXY_PORT_001_RX)) { + pr_debug("%s:No Custom stereo for port:0x%x\n", + __func__, dev_map[dev_map_idx].port_id); + goto end; + } + + /* Allocate the max space needed */ + packed_param_size = (TOTAL_LENGTH_DOLBY_PARAM * sizeof(uint32_t)) + + (2 * sizeof(union param_hdrs)); + packed_param_data = kzalloc(packed_param_size, GFP_KERNEL); + if (!packed_param_data) + return -ENOMEM; + + packed_param_size = 0; + memset(¶m_hdr, 0, sizeof(param_hdr)); + + /* Set common values */ + cdev = dev_map[dev_map_idx].cache_dev; + param_hdr.module_id = DOLBY_BUNDLE_MODULE_ID; + param_hdr.instance_id = INSTANCE_ID_0; + + /* Pack VDHE header + data */ + param_hdr.param_id = DOLBY_PARAM_ID_VDHE; + param_size = DOLBY_PARAM_VDHE_LENGTH * sizeof(uint32_t); + param_hdr.param_size = param_size; + + if (is_custom_stereo_enabled) + param_data = NULL; + else + param_data = (u8 *) &ds2_dap_params[cdev] + .params_val[DOLBY_PARAM_VDHE_OFFSET]; + + rc = q6common_pack_pp_params(packed_param_data, ¶m_hdr, param_data, + ¶m_size); + if (rc) { + pr_err("%s: Failed to pack params for dolby vdhe, error %d\n", + __func__, rc); + goto end; + } + packed_param_size += param_size; + + /* Pack VSPE header + data */ + param_hdr.param_id = DOLBY_PARAM_ID_VSPE; + param_size = DOLBY_PARAM_VSPE_LENGTH * sizeof(uint32_t); + param_hdr.param_size = param_size; + + if (is_custom_stereo_enabled) + param_data = NULL; + else + param_data = (u8 *) &ds2_dap_params[cdev] + .params_val[DOLBY_PARAM_VSPE_OFFSET]; + + rc = q6common_pack_pp_params(packed_param_data + packed_param_size, + ¶m_hdr, param_data, ¶m_size); + if (rc) { + pr_err("%s: Failed to pack params for dolby vspe, error %d\n", + __func__, rc); + goto end; + } + packed_param_size += param_size; + + rc = adm_set_pp_params(dev_map[dev_map_idx].port_id, + dev_map[dev_map_idx].copp_idx, NULL, + packed_param_data, packed_param_size); + if (rc) { + pr_err("%s: send vdhe/vspe params failed with rc=%d\n", + __func__, rc); + rc = -EINVAL; + goto end; + } +end: + kfree(packed_param_data); + return rc; +} + +int qti_set_custom_stereo_on(int port_id, int copp_idx, + bool is_custom_stereo_on) +{ + struct custom_stereo_param custom_stereo; + struct param_hdr_v3 param_hdr; + uint16_t op_FL_ip_FL_weight; + uint16_t op_FL_ip_FR_weight; + uint16_t op_FR_ip_FL_weight; + uint16_t op_FR_ip_FR_weight; + int rc = 0; + + if ((port_id != SLIMBUS_0_RX) && + (port_id != RT_PROXY_PORT_001_RX)) { + pr_debug("%s:No Custom stereo for port:0x%x\n", + __func__, port_id); + return 0; + } + + memset(&custom_stereo, 0, sizeof(custom_stereo)); + memset(¶m_hdr, 0, sizeof(param_hdr)); + pr_debug("%s: port 0x%x, copp_idx %d, is_custom_stereo_on %d\n", + __func__, port_id, copp_idx, is_custom_stereo_on); + if (is_custom_stereo_on) { + op_FL_ip_FL_weight = + Q14_GAIN_ZERO_POINT_FIVE; + op_FL_ip_FR_weight = + Q14_GAIN_ZERO_POINT_FIVE; + op_FR_ip_FL_weight = + Q14_GAIN_ZERO_POINT_FIVE; + op_FR_ip_FR_weight = + Q14_GAIN_ZERO_POINT_FIVE; + } else { + op_FL_ip_FL_weight = Q14_GAIN_UNITY; + op_FL_ip_FR_weight = 0; + op_FR_ip_FL_weight = 0; + op_FR_ip_FR_weight = Q14_GAIN_UNITY; + } + + param_hdr.module_id = MTMX_MODULE_ID_DEFAULT_CHMIXER; + param_hdr.instance_id = INSTANCE_ID_0; + param_hdr.param_id = DEFAULT_CHMIXER_PARAM_ID_COEFF; + param_hdr.param_size = sizeof(struct custom_stereo_param); + + /* index is 32-bit param in little endian*/ + custom_stereo.index = CUSTOM_STEREO_INDEX_PARAM; + custom_stereo.reserved = 0; + /* for stereo mixing num out ch*/ + custom_stereo.num_out_ch = CUSTOM_STEREO_NUM_OUT_CH; + /* for stereo mixing num in ch*/ + custom_stereo.num_in_ch = CUSTOM_STEREO_NUM_IN_CH; + + /* Out ch map FL/FR*/ + custom_stereo.out_fl = PCM_CHANNEL_FL; + custom_stereo.out_fr = PCM_CHANNEL_FR; + + /* In ch map FL/FR*/ + custom_stereo.in_fl = PCM_CHANNEL_FL; + custom_stereo.in_fr = PCM_CHANNEL_FR; + + /* weighting coefficients as name suggests, + * mixing will be done according to these coefficients + */ + custom_stereo.op_FL_ip_FL_weight = op_FL_ip_FL_weight; + custom_stereo.op_FL_ip_FR_weight = op_FL_ip_FR_weight; + custom_stereo.op_FR_ip_FL_weight = op_FR_ip_FL_weight; + custom_stereo.op_FR_ip_FR_weight = op_FR_ip_FR_weight; + rc = adm_pack_and_set_one_pp_param(port_id, copp_idx, param_hdr, + (u8 *) &custom_stereo); + if (rc) { + pr_err("%s: send params failed rc=%d\n", __func__, rc); + return -EINVAL; + } + + return 0; +} +static int dap_set_custom_stereo_onoff(int dev_map_idx, + bool is_custom_stereo_enabled) +{ + uint32_t enable = is_custom_stereo_enabled ? 1 : 0; + struct param_hdr_v3 param_hdr; + int rc = 0; + + if ((dev_map[dev_map_idx].port_id != SLIMBUS_0_RX) && + (dev_map[dev_map_idx].port_id != RT_PROXY_PORT_001_RX)) { + pr_debug("%s:No Custom stereo for port:0x%x\n", + __func__, dev_map[dev_map_idx].port_id); + goto end; + } + + if ((dev_map[dev_map_idx].copp_idx < 0) || + (dev_map[dev_map_idx].copp_idx >= MAX_COPPS_PER_PORT)) { + rc = -EINVAL; + goto end; + } + + memset(¶m_hdr, 0, sizeof(param_hdr)); + + /* DAP custom stereo */ + msm_ds2_dap_set_vspe_vdhe(dev_map_idx, + is_custom_stereo_enabled); + param_hdr.module_id = DOLBY_BUNDLE_MODULE_ID; + param_hdr.instance_id = INSTANCE_ID_0; + param_hdr.param_id = DOLBY_ENABLE_CUSTOM_STEREO; + param_hdr.param_size = sizeof(enable); + + rc = adm_pack_and_set_one_pp_param(dev_map[dev_map_idx].port_id, + dev_map[dev_map_idx].copp_idx, + param_hdr, (u8 *) &enable); + if (rc) { + pr_err("%s: set custom stereo enable failed with rc=%d\n", + __func__, rc); + rc = -EINVAL; + } +end: + return rc; + +} + + +static int set_custom_stereo_onoff(int dev_map_idx, + bool is_custom_stereo_enabled) +{ + int rc = 0; + + pr_debug("%s: map index %d, custom stereo %d\n", __func__, dev_map_idx, + is_custom_stereo_enabled); + + if (dev_map_idx < 0 || dev_map_idx >= DS2_DEVICES_ALL) { + pr_err("%s: invalid dev map index %d\n", __func__, dev_map_idx); + rc = -EINVAL; + goto end; + } + + if (dev_map[dev_map_idx].port_id == DOLBY_INVALID_PORT_ID) { + pr_err("%s: invalid port id\n", __func__); + rc = -EINVAL; + goto end; + } + + if ((dev_map[dev_map_idx].copp_idx < 0) || + (dev_map[dev_map_idx].copp_idx >= MAX_COPPS_PER_PORT)) { + pr_err("%s: invalid copp idx\n", __func__); + rc = -EINVAL; + goto end; + } + + if (ds2_dap_params_states.dap_bypass == true && + ds2_dap_params_states.dap_bypass_type == DAP_HARD_BYPASS) { + + rc = qti_set_custom_stereo_on(dev_map[dev_map_idx].port_id, + dev_map[dev_map_idx].copp_idx, + is_custom_stereo_enabled); + if (rc < 0) { + pr_err("%s:qti_set_custom_stereo_on_copp failed C.S %d", + __func__, is_custom_stereo_enabled); + } + goto end; + + } + + if (ds2_dap_params_states.dap_bypass == false) { + rc = dap_set_custom_stereo_onoff(dev_map_idx, + is_custom_stereo_enabled); + if (rc < 0) { + pr_err("%s:qti_set_custom_stereo_on_copp failed C.S %d", + __func__, is_custom_stereo_enabled); + } + goto end; + } +end: + return rc; +} + +static int msm_ds2_dap_alloc_and_store_cal_data(int dev_map_idx, int path, + int perf_mode) +{ + int rc = 0; + struct audio_rx_cal_data *aud_cal_data; + + pr_debug("%s: path %d, perf_mode %d, dev_map_idx %d\n", + __func__, path, perf_mode, dev_map_idx); + + if (dev_map_idx < 0 || dev_map_idx >= DS2_DEVICES_ALL) { + pr_err("%s: invalid dev map index %d\n", __func__, dev_map_idx); + rc = -EINVAL; + goto end; + } + + aud_cal_data = kzalloc(sizeof(struct audio_rx_cal_data), GFP_KERNEL); + if (!aud_cal_data) { + rc = -ENOMEM; + goto end; + } + + rc = adm_store_cal_data(dev_map[dev_map_idx].port_id, + dev_map[dev_map_idx].copp_idx, path, perf_mode, + ADM_AUDPROC_CAL, aud_cal_data->aud_proc_data, + &aud_cal_data->aud_proc_size); + if (rc < 0) { + pr_err("%s: store cal data err %d\n", __func__, rc); + kfree(aud_cal_data); + goto end; + } + + rc = adm_store_cal_data(dev_map[dev_map_idx].port_id, + dev_map[dev_map_idx].copp_idx, path, perf_mode, + ADM_AUDVOL_CAL, aud_cal_data->aud_vol_data, + &aud_cal_data->aud_vol_size); + if (rc < 0) { + pr_err("%s: store cal data err %d\n", __func__, rc); + kfree(aud_cal_data); + goto end; + } + + dev_map[dev_map_idx].cal_data = (void *)aud_cal_data; + +end: + pr_debug("%s: ret %d\n", __func__, rc); + return rc; +} + +static int msm_ds2_dap_free_cal_data(int dev_map_idx) +{ + int rc = 0; + struct audio_rx_cal_data *aud_cal_data; + + pr_debug("%s: dev_map_idx %d\n", __func__, dev_map_idx); + if (dev_map_idx < 0 || dev_map_idx >= DS2_DEVICES_ALL) { + pr_err("%s: invalid dev map index %d\n", __func__, dev_map_idx); + rc = -EINVAL; + goto end; + } + aud_cal_data = (struct audio_rx_cal_data *) + dev_map[dev_map_idx].cal_data; + kfree(aud_cal_data); + dev_map[dev_map_idx].cal_data = NULL; + +end: + return rc; +} + +static int msm_ds2_dap_send_cal_data(int dev_map_idx) +{ + int rc = 0; + struct audio_rx_cal_data *aud_cal_data = NULL; + + pr_debug("%s: devmap index %d\n", __func__, dev_map_idx); + if (dev_map_idx < 0 || dev_map_idx >= DS2_DEVICES_ALL) { + pr_err("%s: invalid dev map index %d\n", __func__, dev_map_idx); + rc = -EINVAL; + goto end; + } + + if (dev_map[dev_map_idx].cal_data == NULL) { + pr_err("%s: No valid calibration data stored for idx %d\n", + __func__, dev_map_idx); + rc = -EINVAL; + goto end; + } + + /* send aud proc cal */ + aud_cal_data = (struct audio_rx_cal_data *) + dev_map[dev_map_idx].cal_data; + rc = adm_send_calibration(dev_map[dev_map_idx].port_id, + dev_map[dev_map_idx].copp_idx, + ADM_PATH_PLAYBACK, 0, + ADM_AUDPROC_CAL, + aud_cal_data->aud_proc_data, + aud_cal_data->aud_proc_size); + if (rc < 0) { + pr_err("%s: adm_send_calibration failed %d\n", __func__, rc); + goto end; + } + + /* send aud volume cal*/ + rc = adm_send_calibration(dev_map[dev_map_idx].port_id, + dev_map[dev_map_idx].copp_idx, + ADM_PATH_PLAYBACK, 0, + ADM_AUDVOL_CAL, + aud_cal_data->aud_vol_data, + aud_cal_data->aud_vol_size); + if (rc < 0) + pr_err("%s: adm_send_calibration failed %d\n", __func__, rc); +end: + pr_debug("%s: return %d\n", __func__, rc); + return rc; +} + +static inline int msm_ds2_dap_can_enable_module(int32_t module_id) +{ + if (module_id == MTMX_MODULE_ID_DEFAULT_CHMIXER || + module_id == AUDPROC_MODULE_ID_RESAMPLER || + module_id == AUDPROC_MODULE_ID_VOL_CTRL) { + return false; + } + return true; +} + +static int msm_ds2_dap_init_modules_in_topology(int dev_map_idx) +{ + int rc = 0, i = 0, port_id, copp_idx; + /* Account for 32 bit integer allocation */ + int32_t param_sz = + (ADM_GET_TOPO_MODULE_INSTANCE_LIST_LENGTH / sizeof(uint32_t)); + int32_t *update_param_val = NULL; + struct module_instance_info mod_inst_info; + int mod_inst_info_sz = 0; + + if (dev_map_idx < 0 || dev_map_idx >= DS2_DEVICES_ALL) { + pr_err("%s: invalid dev map index %d\n", __func__, dev_map_idx); + rc = -EINVAL; + goto end; + } + + memset(&mod_inst_info, 0, sizeof(mod_inst_info)); + port_id = dev_map[dev_map_idx].port_id; + copp_idx = dev_map[dev_map_idx].copp_idx; + pr_debug("%s: port_id 0x%x copp_idx %d\n", __func__, port_id, copp_idx); + update_param_val = + kzalloc(ADM_GET_TOPO_MODULE_INSTANCE_LIST_LENGTH, GFP_KERNEL); + if (!update_param_val) { + pr_err("%s, param memory alloc failed\n", __func__); + rc = -ENOMEM; + goto end; + } + + if (!ds2_dap_params_states.dap_bypass) { + /* get modules from dsp */ + rc = adm_get_pp_topo_module_list_v2( + port_id, copp_idx, + ADM_GET_TOPO_MODULE_INSTANCE_LIST_LENGTH, + update_param_val); + if (rc < 0) { + pr_err("%s:topo list port %d, err %d,copp_idx %d\n", + __func__, port_id, copp_idx, rc); + goto end; + } + + if (update_param_val[0] > (param_sz - 1)) { + pr_err("%s:max modules exp/ret [%d: %d]\n", + __func__, (param_sz - 1), + update_param_val[0]); + rc = -EINVAL; + goto end; + } + + mod_inst_info_sz = sizeof(struct module_instance_info) / + sizeof(uint32_t); + /* Turn off modules */ + for (i = 1; i < update_param_val[0] * mod_inst_info_sz; + i += mod_inst_info_sz) { + if (!msm_ds2_dap_can_enable_module( + update_param_val[i]) || + (update_param_val[i] == DS2_MODULE_ID)) { + pr_debug("%s: Do not enable/disable %d\n", + __func__, update_param_val[i]); + continue; + } + + pr_debug("%s: param disable %d\n", + __func__, update_param_val[i]); + memcpy(&mod_inst_info, &update_param_val[i], + sizeof(mod_inst_info)); + adm_param_enable_v2(port_id, copp_idx, + mod_inst_info, + MODULE_DISABLE); + } + } else { + msm_ds2_dap_send_cal_data(dev_map_idx); + + } + + mod_inst_info.module_id = DS2_MODULE_ID; + mod_inst_info.instance_id = INSTANCE_ID_0; + adm_param_enable_v2(port_id, copp_idx, mod_inst_info, + !ds2_dap_params_states.dap_bypass); +end: + kfree(update_param_val); + return rc; +} + +static bool msm_ds2_dap_check_is_param_modified(int32_t *dap_params_modified, + int32_t idx, int32_t commit) +{ + if ((dap_params_modified[idx] == 0) || + (commit && + ((dap_params_modified[idx] & 0x00010000) && + ((dap_params_modified[idx] & 0x0000FFFF) <= 1)))) { + pr_debug("%s: not modified at idx %d\n", __func__, idx); + return false; + } + pr_debug("%s: modified at idx %d\n", __func__, idx); + return true; +} + +static int msm_ds2_dap_map_device_to_dolby_cache_devices(int32_t device_id) +{ + int32_t cache_dev = -1; + + switch (device_id) { + case DEVICE_NONE: + cache_dev = DOLBY_OFF_CACHE; + break; + case EARPIECE: + case SPEAKER: + cache_dev = DOLBY_SPEAKER_CACHE; + break; + case WIRED_HEADSET: + case WIRED_HEADPHONE: + case ANLG_DOCK_HEADSET: + case DGTL_DOCK_HEADSET: + case ANC_HEADSET: + case ANC_HEADPHONE: + case BLUETOOTH_SCO: + case BLUETOOTH_SCO_HEADSET: + case BLUETOOTH_SCO_CARKIT: + cache_dev = DOLBY_HEADPHONE_CACHE; + break; + case FM: + case FM_TX: + cache_dev = DOLBY_FM_CACHE; + break; + case AUX_DIGITAL: + cache_dev = DOLBY_HDMI_CACHE; + break; + case PROXY: + case REMOTE_SUBMIX: + cache_dev = DOLBY_WFD_CACHE; + break; + default: + pr_err("%s: invalid cache device\n", __func__); + } + pr_debug("%s: cache device %d\n", __func__, cache_dev); + return cache_dev; +} + +static int msm_ds2_dap_update_num_devices(struct dolby_param_data *dolby_data, + int32_t *num_device, int32_t *dev_arr, + int32_t array_size) +{ + int32_t idx = 0; + int supported_devices = 0; + + if (!array_size) { + pr_err("%s: array size zero\n", __func__); + return -EINVAL; + } + + if (dolby_data->device_id == DEVICE_OUT_ALL || + dolby_data->device_id == DEVICE_OUT_DEFAULT) + supported_devices = all_supported_devices; + else + supported_devices = dolby_data->device_id; + + if ((idx < array_size) && (supported_devices & EARPIECE)) + dev_arr[idx++] = EARPIECE; + if ((idx < array_size) && (supported_devices & SPEAKER)) + dev_arr[idx++] = SPEAKER; + if ((idx < array_size) && (supported_devices & WIRED_HEADSET)) + dev_arr[idx++] = WIRED_HEADSET; + if ((idx < array_size) && (supported_devices & WIRED_HEADPHONE)) + dev_arr[idx++] = WIRED_HEADPHONE; + if ((idx < array_size) && (supported_devices & BLUETOOTH_SCO)) + dev_arr[idx++] = BLUETOOTH_SCO; + if ((idx < array_size) && (supported_devices & BLUETOOTH_SCO_CARKIT)) + dev_arr[idx++] = BLUETOOTH_SCO_CARKIT; + if ((idx < array_size) && (supported_devices & BLUETOOTH_SCO_HEADSET)) + dev_arr[idx++] = BLUETOOTH_SCO_HEADSET; + if ((idx < array_size) && (supported_devices & AUX_DIGITAL)) + dev_arr[idx++] = AUX_DIGITAL; + if ((idx < array_size) && (supported_devices & ANLG_DOCK_HEADSET)) + dev_arr[idx++] = ANLG_DOCK_HEADSET; + if ((idx < array_size) && (supported_devices & DGTL_DOCK_HEADSET)) + dev_arr[idx++] = DGTL_DOCK_HEADSET; + if ((idx < array_size) && (supported_devices & REMOTE_SUBMIX)) + dev_arr[idx++] = REMOTE_SUBMIX; + if ((idx < array_size) && (supported_devices & ANC_HEADSET)) + dev_arr[idx++] = ANC_HEADSET; + if ((idx < array_size) && (supported_devices & ANC_HEADPHONE)) + dev_arr[idx++] = ANC_HEADPHONE; + if ((idx < array_size) && (supported_devices & PROXY)) + dev_arr[idx++] = PROXY; + if ((idx < array_size) && (supported_devices & FM)) + dev_arr[idx++] = FM; + if ((idx < array_size) && (supported_devices & FM_TX)) + dev_arr[idx++] = FM_TX; + /* CHECK device none separately */ + if ((idx < array_size) && (supported_devices == DEVICE_NONE)) + dev_arr[idx++] = DEVICE_NONE; + pr_debug("%s: dev id 0x%x, idx %d\n", __func__, + supported_devices, idx); + *num_device = idx; + return 0; +} + +static int msm_ds2_dap_get_port_id( + int32_t device_id, int32_t be_id) +{ + struct msm_pcm_routing_bdai_data bedais; + int port_id = DOLBY_INVALID_PORT_ID; + int port_type = 0; + + if (be_id < 0) { + port_id = -1; + goto end; + } + + msm_pcm_routing_get_bedai_info(be_id, &bedais); + pr_debug("%s: be port_id %d\n", __func__, bedais.port_id); + port_id = bedais.port_id; + port_type = afe_get_port_type(bedais.port_id); + if (port_type != MSM_AFE_PORT_TYPE_RX) + port_id = DOLBY_INVALID_PORT_ID; +end: + pr_debug("%s: device_id 0x%x, be_id %d, port_id %d\n", + __func__, device_id, be_id, port_id); + return port_id; +} + +static int msm_ds2_dap_update_dev_map_port_id(int32_t device_id, int port_id) +{ + int i; + + for (i = 0; i < DS2_DEVICES_ALL; i++) { + if (dev_map[i].device_id == device_id) + dev_map[i].port_id = port_id; + } + pr_debug("%s: port_id %d, device_id 0x%x\n", + __func__, port_id, device_id); + return 0; +} + +static int msm_ds2_dap_handle_bypass_wait(int port_id, int copp_idx, + int wait_time) +{ + int ret = 0; + + adm_set_wait_parameters(port_id, copp_idx); + msm_pcm_routing_release_lock(); + ret = adm_wait_timeout(port_id, copp_idx, wait_time); + msm_pcm_routing_acquire_lock(); + /* Reset the parameters if wait has timed out */ + if (ret == 0) + adm_reset_wait_parameters(port_id, copp_idx); + return ret; +} + +static int msm_ds2_dap_handle_bypass(struct dolby_param_data *dolby_data) +{ + int rc = 0, i = 0, j = 0; + /*Account for 32 bit integer allocation */ + int32_t param_sz = + (ADM_GET_TOPO_MODULE_INSTANCE_LIST_LENGTH / sizeof(uint32_t)); + int32_t *mod_list = NULL; + int port_id = 0, copp_idx = -1; + bool cs_onoff = ds2_dap_params_states.custom_stereo_onoff; + int ramp_wait = DOLBY_SOFT_VOLUME_PERIOD; + struct module_instance_info mod_inst_info; + int mod_inst_info_sz = 0; + + pr_debug("%s: bypass type %d bypass %d custom stereo %d\n", __func__, + ds2_dap_params_states.dap_bypass_type, + ds2_dap_params_states.dap_bypass, + ds2_dap_params_states.custom_stereo_onoff); + memset(&mod_inst_info, 0, sizeof(mod_inst_info)); + mod_list = + kzalloc(ADM_GET_TOPO_MODULE_INSTANCE_LIST_LENGTH, GFP_KERNEL); + if (!mod_list) { + pr_err("%s: param memory alloc failed\n", __func__); + rc = -ENOMEM; + goto end; + } + + for (i = 0; i < DS2_DEVICES_ALL; i++) { + pr_debug("%s: active dev %d\n", __func__, dev_map[i].active); + if (dev_map[i].active) { + port_id = dev_map[i].port_id; + copp_idx = dev_map[i].copp_idx; + + if (port_id == DOLBY_INVALID_PORT_ID) { + pr_err("%s: invalid port\n", __func__); + rc = 0; + goto end; + } + + if ((copp_idx < 0) || + (copp_idx >= MAX_COPPS_PER_PORT)) { + pr_err("%s: Invalid copp_idx\n", __func__); + rc = 0; + goto end; + } + + /* getmodules from dsp */ + rc = adm_get_pp_topo_module_list_v2( + port_id, copp_idx, + ADM_GET_TOPO_MODULE_INSTANCE_LIST_LENGTH, + mod_list); + if (rc < 0) { + pr_err("%s:adm get topo list port %d", + __func__, port_id); + pr_err("copp_idx %d, err %d\n", + copp_idx, rc); + goto end; + } + if (mod_list[0] > (param_sz - 1)) { + pr_err("%s:max modules exp/ret [%d: %d]\n", + __func__, (param_sz - 1), + mod_list[0]); + rc = -EINVAL; + goto end; + } + /* + * get ramp parameters + * check for change in ramp parameters + * update ramp wait + */ + msm_ds2_dap_check_and_update_ramp_wait(port_id, + copp_idx, + &ramp_wait); + + /* Mute before switching modules */ + rc = adm_set_volume(port_id, copp_idx, + VOLUME_ZERO_GAIN); + if (rc < 0) { + /* + * Not Fatal can continue bypass operations. + * Do not need to block playback + */ + pr_info("%s :Set volume port_id %d", + __func__, port_id); + pr_info("copp_idx %d, error %d\n", + copp_idx, rc); + } + + rc = msm_ds2_dap_handle_bypass_wait(port_id, copp_idx, + (ramp_wait + + DOLBY_ADDITIONAL_RAMP_WAIT)); + if (rc == -EINTR) { + pr_info("%s:bypass interrupted-ignore,port %d", + __func__, port_id); + pr_info("copp_idx %d\n", copp_idx); + rc = 0; + continue; + } + + /* if dap bypass is set */ + if (ds2_dap_params_states.dap_bypass) { + /* Turn off dap module */ + mod_inst_info.module_id = DS2_MODULE_ID; + mod_inst_info.instance_id = INSTANCE_ID_0; + adm_param_enable_v2(port_id, copp_idx, + mod_inst_info, + MODULE_DISABLE); + /* + * If custom stereo is on at the time of bypass, + * switch off custom stereo on dap and turn on + * custom stereo on qti channel mixer. + */ + if (cs_onoff) { + rc = dap_set_custom_stereo_onoff(i, + !cs_onoff); + if (rc < 0) { + pr_info("%s:D_CS i %d,rc %d\n", + __func__, i, rc); + } + rc = qti_set_custom_stereo_on(port_id, + copp_idx, + cs_onoff); + if (rc < 0) { + pr_info("%s:Q_CS port id 0x%x", + __func__, port_id); + pr_info("copp idx %d, rc %d\n", + copp_idx, rc); + } + } + + mod_inst_info_sz = + sizeof(struct module_instance_info) / + sizeof(uint32_t); + /* Turn on qti modules */ + for (j = 1; j < mod_list[0] * mod_inst_info_sz; + j += mod_inst_info_sz) { + if (!msm_ds2_dap_can_enable_module( + mod_list[j]) || + mod_list[j] == + DS2_MODULE_ID) + continue; + pr_debug("%s: param enable %d\n", + __func__, mod_list[j]); + memcpy(&mod_inst_info, &mod_list[j], + sizeof(mod_inst_info)); + adm_param_enable_v2(port_id, copp_idx, + mod_inst_info, + MODULE_ENABLE); + } + + /* Add adm api to resend calibration on port */ + rc = msm_ds2_dap_send_cal_data(i); + if (rc < 0) { + /* + * Not fatal,continue bypass operations. + * Do not need to block playback + */ + pr_info("%s:send cal err %d index %d\n", + __func__, rc, i); + } + } else { + /* Turn off qti modules */ + for (j = 1; j < mod_list[0] * mod_inst_info_sz; + j += mod_inst_info_sz) { + if (!msm_ds2_dap_can_enable_module( + mod_list[j]) || + mod_list[j] == + DS2_MODULE_ID) + continue; + pr_debug("%s: param disable %d\n", + __func__, mod_list[j]); + memcpy(&mod_inst_info, &mod_list[j], + sizeof(mod_inst_info)); + adm_param_enable_v2(port_id, copp_idx, + mod_inst_info, + MODULE_DISABLE); + } + + /* Enable DAP modules */ + pr_debug("%s:DS2 param enable\n", __func__); + mod_inst_info.module_id = DS2_MODULE_ID; + mod_inst_info.instance_id = INSTANCE_ID_0; + adm_param_enable_v2(port_id, copp_idx, + mod_inst_info, + MODULE_ENABLE); + /* + * If custom stereo is on at the time of dap on, + * switch off custom stereo on qti channel mixer + * and turn on custom stereo on DAP. + * mixer(qti). + */ + if (cs_onoff) { + rc = qti_set_custom_stereo_on(port_id, + copp_idx, + !cs_onoff); + if (rc < 0) { + pr_info("%s:Q_CS port_id 0x%x", + __func__, port_id); + pr_info("copp_idx %d rc %d\n", + copp_idx, rc); + } + rc = dap_set_custom_stereo_onoff(i, + cs_onoff); + if (rc < 0) { + pr_info("%s:D_CS i %d,rc %d\n", + __func__, i, rc); + } + } + } + + rc = msm_ds2_dap_handle_bypass_wait(port_id, copp_idx, + DOLBY_MODULE_ENABLE_PERIOD); + if (rc == -EINTR) { + pr_info("%s:bypass interrupted port_id %d copp_idx %d\n", + __func__, port_id, copp_idx); + /* Interrupted ignore bypass */ + rc = 0; + continue; + } + + /* set volume to unity gain after module on/off */ + rc = adm_set_volume(port_id, copp_idx, + VOLUME_UNITY_GAIN); + if (rc < 0) { + /* + * Not Fatal can continue bypass operations. + * Do not need to block playback + */ + pr_info("%s: Set vol port %d copp %d, rc %d\n", + __func__, port_id, copp_idx, rc); + rc = 0; + } + } + } + +end: + kfree(mod_list); + pr_debug("%s:return rc=%d\n", __func__, rc); + return rc; +} + +static int msm_ds2_dap_send_end_point(int dev_map_idx, int endp_idx) +{ + uint32_t offset = 0; + struct param_hdr_v3 param_hdr; + int cache_device = 0; + struct ds2_dap_params_s *ds2_ap_params_obj = NULL; + int32_t *modified_param = NULL; + int rc = 0; + + if (dev_map_idx < 0 || dev_map_idx >= DS2_DEVICES_ALL) { + pr_err("%s: invalid dev map index %d\n", __func__, dev_map_idx); + rc = -EINVAL; + goto end; + } + memset(¶m_hdr, 0, sizeof(param_hdr)); + cache_device = dev_map[dev_map_idx].cache_dev; + + ds2_ap_params_obj = &ds2_dap_params[cache_device]; + pr_debug("%s: cache dev %d, dev_map_idx %d\n", __func__, + cache_device, dev_map_idx); + pr_debug("%s: endp - %pK %pK\n", __func__, + &ds2_dap_params[cache_device], ds2_ap_params_obj); + + if (dev_map[dev_map_idx].port_id == DOLBY_INVALID_PORT_ID) { + pr_err("%s: invalid port\n", __func__); + rc = -EINVAL; + goto end; + } + + if ((dev_map[dev_map_idx].copp_idx < 0) || + (dev_map[dev_map_idx].copp_idx >= MAX_COPPS_PER_PORT)) { + pr_err("%s: Invalid copp_idx\n", __func__); + rc = -EINVAL; + goto end; + } + + param_hdr.module_id = DOLBY_BUNDLE_MODULE_ID; + param_hdr.instance_id = INSTANCE_ID_0; + param_hdr.param_id = DOLBY_PARAM_ID_INIT_ENDP; + param_hdr.param_size = sizeof(offset); + offset = ds2_ap_params_obj->params_val[ds2_dap_params_offset[endp_idx]]; + pr_debug("%s: off %d, length %d\n", __func__, + ds2_dap_params_offset[endp_idx], + ds2_dap_params_length[endp_idx]); + pr_debug("%s: param 0x%x, param val %d\n", __func__, + ds2_dap_params_id[endp_idx], ds2_ap_params_obj-> + params_val[ds2_dap_params_offset[endp_idx]]); + rc = adm_pack_and_set_one_pp_param(dev_map[dev_map_idx].port_id, + dev_map[dev_map_idx].copp_idx, + param_hdr, (u8 *) &offset); + if (rc) { + pr_err("%s: send dolby params failed rc %d\n", __func__, rc); + rc = -EINVAL; + } + modified_param = ds2_ap_params_obj->dap_params_modified; + if (modified_param == NULL) { + pr_err("%s: modified param structure invalid\n", + __func__); + rc = -EINVAL; + goto end; + } + + if (msm_ds2_dap_check_is_param_modified(modified_param, endp_idx, 0)) + ds2_ap_params_obj->dap_params_modified[endp_idx] = 0x00010001; + +end: + return rc; +} + +static int msm_ds2_dap_send_cached_params(int dev_map_idx, + int commit) +{ + uint8_t *packed_params = NULL; + uint32_t packed_params_size = 0; + uint32_t param_size = 0; + struct param_hdr_v3 param_hdr; + uint32_t idx, i, ret = 0; + int cache_device = 0; + struct ds2_dap_params_s *ds2_ap_params_obj = NULL; + int32_t *modified_param = NULL; + + if (dev_map_idx < 0 || dev_map_idx >= DS2_DEVICES_ALL) { + pr_err("%s: invalid dev map index %d\n", __func__, dev_map_idx); + ret = -EINVAL; + goto end; + } + memset(¶m_hdr, 0, sizeof(param_hdr)); + cache_device = dev_map[dev_map_idx].cache_dev; + + /* Use off profile cache in only for soft bypass */ + if (ds2_dap_params_states.dap_bypass_type == DAP_SOFT_BYPASS && + ds2_dap_params_states.dap_bypass == true) { + pr_debug("%s: use bypass cache 0\n", __func__); + cache_device = dev_map[0].cache_dev; + } + + ds2_ap_params_obj = &ds2_dap_params[cache_device]; + pr_debug("%s: cached param - %pK %pK, cache_device %d\n", __func__, + &ds2_dap_params[cache_device], ds2_ap_params_obj, + cache_device); + + /* + * Allocate the max space needed. This is enough space to hold the + * header for each param plus the total size of all the params. + */ + packed_params_size = (sizeof(param_hdr) * (MAX_DS2_PARAMS - 1)) + + (TOTAL_LENGTH_DOLBY_PARAM * sizeof(uint32_t)); + packed_params = kzalloc(packed_params_size, GFP_KERNEL); + if (!packed_params) + return -ENOMEM; + + if (dev_map[dev_map_idx].port_id == DOLBY_INVALID_PORT_ID) { + pr_err("%s: invalid port id\n", __func__); + ret = -EINVAL; + goto end; + } + + if ((dev_map[dev_map_idx].copp_idx < 0) || + (dev_map[dev_map_idx].copp_idx >= MAX_COPPS_PER_PORT)) { + pr_err("%s: Invalid copp_idx\n", __func__); + ret = -EINVAL; + goto end; + } + + packed_params_size = 0; + for (i = 0; i < (MAX_DS2_PARAMS-1); i++) { + /*get the pointer to the param modified array in the cache*/ + modified_param = ds2_ap_params_obj->dap_params_modified; + if (modified_param == NULL) { + pr_err("%s: modified param structure invalid\n", + __func__); + ret = -EINVAL; + goto end; + } + if (!msm_ds2_dap_check_is_param_modified(modified_param, i, + commit)) + continue; + + param_hdr.module_id = DOLBY_BUNDLE_MODULE_ID; + param_hdr.instance_id = INSTANCE_ID_0; + param_hdr.param_id = ds2_dap_params_id[i]; + param_hdr.param_size = + ds2_dap_params_length[i] * sizeof(uint32_t); + + idx = ds2_dap_params_offset[i]; + ret = q6common_pack_pp_params( + packed_params + packed_params_size, ¶m_hdr, + (u8 *) &ds2_ap_params_obj->params_val[idx], + ¶m_size); + if (ret) { + pr_err("%s: Failed to pack params, error %d\n", + __func__, ret); + goto end; + } + + packed_params_size += param_size; + } + + pr_debug("%s: total packed param length: %d\n", __func__, + packed_params_size); + if (packed_params_size) { + ret = adm_set_pp_params(dev_map[dev_map_idx].port_id, + dev_map[dev_map_idx].copp_idx, NULL, + packed_params, packed_params_size); + if (ret) { + pr_err("%s: send dolby params failed ret %d\n", + __func__, ret); + ret = -EINVAL; + goto end; + } + for (i = 0; i < MAX_DS2_PARAMS-1; i++) { + /*get pointer to the param modified array in the cache*/ + modified_param = ds2_ap_params_obj->dap_params_modified; + if (modified_param == NULL) { + pr_err("%s: modified param struct invalid\n", + __func__); + ret = -EINVAL; + goto end; + } + if (!msm_ds2_dap_check_is_param_modified( + modified_param, i, commit)) + continue; + ds2_ap_params_obj->dap_params_modified[i] = 0x00010001; + } + } +end: + kfree(packed_params); + return ret; +} + +static int msm_ds2_dap_commit_params(struct dolby_param_data *dolby_data, + int commit) +{ + int ret = 0, i, idx; + struct ds2_dap_params_s *ds2_ap_params_obj = NULL; + int32_t *modified_param = NULL; + + /* Do not commit params if in hard bypass */ + if (ds2_dap_params_states.dap_bypass_type == DAP_HARD_BYPASS && + ds2_dap_params_states.dap_bypass == true) { + pr_debug("%s: called in bypass", __func__); + ret = -EINVAL; + goto end; + } + for (idx = 0; idx < MAX_DS2_PARAMS; idx++) { + if (ds2_dap_params_id[idx] == DOLBY_PARAM_ID_INIT_ENDP) + break; + } + if (idx >= MAX_DS2_PARAMS || idx < 0) { + pr_err("%s: index of DS2 Param not found idx %d\n", + __func__, idx); + ret = -EINVAL; + goto end; + } + pr_debug("%s: found endp - idx %d 0x%x\n", __func__, idx, + ds2_dap_params_id[idx]); + for (i = 0; i < DS2_DEVICES_ALL; i++) { + pr_debug("%s:dev[0x%x,0x%x],i:%d,active:%d,bypass:%d,type:%d\n", + __func__, dolby_data->device_id, dev_map[i].device_id, + i, dev_map[i].active, ds2_dap_params_states.dap_bypass, + ds2_dap_params_states.dap_bypass_type); + + if (((dev_map[i].device_id & ds2_dap_params_states.device) || + ((ds2_dap_params_states.dap_bypass_type == + DAP_SOFT_BYPASS) && + (ds2_dap_params_states.dap_bypass == true))) && + (dev_map[i].active == true)) { + + /*get ptr to the cache storing the params for device*/ + if ((ds2_dap_params_states.dap_bypass_type == + DAP_SOFT_BYPASS) && + (ds2_dap_params_states.dap_bypass == true)) + ds2_ap_params_obj = + &ds2_dap_params[dev_map[0].cache_dev]; + else + ds2_ap_params_obj = + &ds2_dap_params[dev_map[i].cache_dev]; + + /*get the pointer to the param modified array in cache*/ + modified_param = ds2_ap_params_obj->dap_params_modified; + if (modified_param == NULL) { + pr_err("%s: modified_param NULL\n", __func__); + ret = -EINVAL; + goto end; + } + + /* + * Send the endp param if use cache is set + * or if param is modified + */ + if (!commit || msm_ds2_dap_check_is_param_modified( + modified_param, idx, commit)) { + msm_ds2_dap_send_end_point(i, idx); + commit = 0; + } + ret = msm_ds2_dap_send_cached_params(i, commit); + if (ret < 0) { + pr_err("%s: send cached param %d\n", + __func__, ret); + goto end; + } + } + } +end: + return ret; +} + +static int msm_ds2_dap_handle_commands(u32 cmd, void *arg) +{ + int ret = 0, port_id = 0; + int32_t data; + struct dolby_param_data *dolby_data = (struct dolby_param_data *)arg; + + if (get_user(data, &dolby_data->data[0])) { + pr_debug("%s error getting data\n", __func__); + ret = -EFAULT; + goto end; + } + + pr_debug("%s: param_id %d,be_id %d,device_id 0x%x,length %d,data %d\n", + __func__, dolby_data->param_id, dolby_data->be_id, + dolby_data->device_id, dolby_data->length, data); + + switch (dolby_data->param_id) { + case DAP_CMD_COMMIT_ALL: + msm_ds2_dap_commit_params(dolby_data, 0); + break; + + case DAP_CMD_COMMIT_CHANGED: + msm_ds2_dap_commit_params(dolby_data, 1); + break; + + case DAP_CMD_USE_CACHE_FOR_INIT: + ds2_dap_params_states.use_cache = data; + break; + + case DAP_CMD_SET_BYPASS: + pr_debug("%s: bypass %d bypass type %d, data %d\n", __func__, + ds2_dap_params_states.dap_bypass, + ds2_dap_params_states.dap_bypass_type, + data); + /* Do not perform bypass operation if bypass state is same*/ + if (ds2_dap_params_states.dap_bypass == data) + break; + ds2_dap_params_states.dap_bypass = data; + /* hard bypass */ + if (ds2_dap_params_states.dap_bypass_type == DAP_HARD_BYPASS) + msm_ds2_dap_handle_bypass(dolby_data); + /* soft bypass */ + msm_ds2_dap_commit_params(dolby_data, 0); + break; + + case DAP_CMD_SET_BYPASS_TYPE: + if (data == true) + ds2_dap_params_states.dap_bypass_type = + DAP_HARD_BYPASS; + else + ds2_dap_params_states.dap_bypass_type = + DAP_SOFT_BYPASS; + pr_debug("%s: bypass type %d", __func__, + ds2_dap_params_states.dap_bypass_type); + break; + + case DAP_CMD_SET_ACTIVE_DEVICE: + pr_debug("%s: DAP_CMD_SET_ACTIVE_DEVICE length %d\n", + __func__, dolby_data->length); + /* TODO: need to handle multiple instance*/ + ds2_dap_params_states.device |= dolby_data->device_id; + port_id = msm_ds2_dap_get_port_id( + dolby_data->device_id, + dolby_data->be_id); + pr_debug("%s: device id 0x%x all_dev 0x%x port_id %d\n", + __func__, dolby_data->device_id, + ds2_dap_params_states.device, port_id); + msm_ds2_dap_update_dev_map_port_id(dolby_data->device_id, + port_id); + if (port_id == DOLBY_INVALID_PORT_ID) { + pr_err("%s: invalid port id %d\n", __func__, port_id); + ret = -EINVAL; + goto end; + } + break; + } +end: + return ret; + +} + +static int msm_ds2_dap_set_param(u32 cmd, void *arg) +{ + int rc = 0, idx, i, j, off, port_id = 0, cdev = 0; + int32_t num_device = 0; + int32_t data = 0; + int32_t dev_arr[DS2_DSP_SUPPORTED_ENDP_DEVICE] = {0}; + struct dolby_param_data *dolby_data = (struct dolby_param_data *)arg; + + rc = msm_ds2_dap_update_num_devices(dolby_data, &num_device, dev_arr, + DS2_DSP_SUPPORTED_ENDP_DEVICE); + if (num_device == 0 || rc < 0) { + pr_err("%s: num devices 0\n", __func__); + rc = -EINVAL; + goto end; + } + for (i = 0; i < num_device; i++) { + port_id = msm_ds2_dap_get_port_id(dev_arr[i], + dolby_data->be_id); + if (port_id != DOLBY_INVALID_PORT_ID) + msm_ds2_dap_update_dev_map_port_id(dev_arr[i], port_id); + + cdev = msm_ds2_dap_map_device_to_dolby_cache_devices( + dev_arr[i]); + if (cdev < 0 || cdev >= DOLBY_MAX_CACHE) { + pr_err("%s: Invalid cache device %d for device 0x%x\n", + __func__, cdev, dev_arr[i]); + rc = -EINVAL; + goto end; + } + pr_debug("%s:port:%d,be:%d,dev:0x%x,cdev:%d,param:0x%x,len:%d\n" + , __func__, port_id, dolby_data->be_id, dev_arr[i], + cdev, dolby_data->param_id, dolby_data->length); + for (idx = 0; idx < MAX_DS2_PARAMS; idx++) { + /*paramid from user space*/ + if (dolby_data->param_id == ds2_dap_params_id[idx]) + break; + } + if (idx > MAX_DS2_PARAMS-1) { + pr_err("%s: invalid param id 0x%x at idx %d\n", + __func__, dolby_data->param_id, idx); + rc = -EINVAL; + goto end; + } + + off = ds2_dap_params_offset[idx]; + if ((dolby_data->length <= 0) || + (dolby_data->length > TOTAL_LENGTH_DS2_PARAM - off)) { + pr_err("%s: invalid length %d at idx %d\n", + __func__, dolby_data->length, idx); + rc = -EINVAL; + goto end; + } + + /* cache the parameters */ + ds2_dap_params[cdev].dap_params_modified[idx] += 1; + for (j = 0; j < dolby_data->length; j++) { + if (get_user(data, &dolby_data->data[j])) { + pr_debug("%s:error getting data\n", __func__); + rc = -EFAULT; + goto end; + } + ds2_dap_params[cdev].params_val[off + j] = data; + pr_debug("%s:off %d,val[i/p:o/p]-[%d / %d]\n", + __func__, off, data, + ds2_dap_params[cdev]. + params_val[off + j]); + } + } +end: + return rc; +} + +static int msm_ds2_dap_get_param(u32 cmd, void *arg) +{ + int rc = 0, i, port_id = 0, copp_idx = -1; + struct dolby_param_data *dolby_data = (struct dolby_param_data *)arg; + int32_t *params_value = NULL; + uint32_t params_length = DOLBY_MAX_LENGTH_INDIVIDUAL_PARAM * + sizeof(uint32_t); + uint32_t param_payload_len = + DOLBY_PARAM_PAYLOAD_SIZE * sizeof(uint32_t); + struct param_hdr_v3 param_hdr; + + /* Return error on get param in soft or hard bypass */ + if (ds2_dap_params_states.dap_bypass == true) { + pr_err("%s: called in bypass_type %d bypass %d\n", __func__, + ds2_dap_params_states.dap_bypass_type, + ds2_dap_params_states.dap_bypass); + rc = -EINVAL; + goto end; + } + + /* Return if invalid length */ + if ((dolby_data->length > + (DOLBY_MAX_LENGTH_INDIVIDUAL_PARAM - DOLBY_PARAM_PAYLOAD_SIZE)) || + (dolby_data->length <= 0)) { + pr_err("Invalid length %d", dolby_data->length); + rc = -EINVAL; + goto end; + } + + for (i = 0; i < DS2_DEVICES_ALL; i++) { + if ((dev_map[i].active) && + (dev_map[i].device_id & dolby_data->device_id)) { + port_id = dev_map[i].port_id; + copp_idx = dev_map[i].copp_idx; + break; + } + } + + if (port_id == DOLBY_INVALID_PORT_ID) { + pr_err("%s: Invalid port\n", __func__); + rc = -EINVAL; + goto end; + } + + if ((copp_idx < 0) || (copp_idx >= MAX_COPPS_PER_PORT)) { + pr_err("%s: Invalid copp_idx\n", __func__); + rc = -EINVAL; + goto end; + } + + pr_debug("%s: port_id 0x%x, copp_idx %d, dev_map[i].device_id %x\n", + __func__, port_id, copp_idx, dev_map[i].device_id); + + params_value = kzalloc(params_length + param_payload_len, + GFP_KERNEL); + if (!params_value) + return -ENOMEM; + + memset(¶m_hdr, 0, sizeof(param_hdr)); + if (dolby_data->param_id == DOLBY_PARAM_ID_VER) { + param_hdr.module_id = DOLBY_BUNDLE_MODULE_ID; + param_hdr.instance_id = INSTANCE_ID_0; + param_hdr.param_id = DOLBY_PARAM_ID_VER; + param_hdr.param_size = params_length + param_payload_len; + } else { + for (i = 0; i < MAX_DS2_PARAMS; i++) + if (ds2_dap_params_id[i] == + dolby_data->param_id) + break; + if (i > MAX_DS2_PARAMS-1) { + pr_err("%s: invalid param id 0x%x at id %d\n", __func__, + dolby_data->param_id, i); + rc = -EINVAL; + goto end; + } else { + params_length = + ds2_dap_params_length[i] * sizeof(uint32_t); + + param_hdr.module_id = DOLBY_BUNDLE_MODULE_ID; + param_hdr.instance_id = INSTANCE_ID_0; + param_hdr.param_id = ds2_dap_params_id[i]; + param_hdr.param_size = + params_length + param_payload_len; + } + } + rc = adm_get_pp_params(port_id, copp_idx, ADM_CLIENT_ID_DEFAULT, NULL, + ¶m_hdr, (u8 *) params_value); + if (rc) { + pr_err("%s: get parameters failed rc %d\n", __func__, rc); + rc = -EINVAL; + goto end; + } + if (copy_to_user((void __user *) dolby_data->data, + ¶ms_value[DOLBY_PARAM_PAYLOAD_SIZE], + (dolby_data->length * sizeof(uint32_t)))) { + pr_err("%s: error getting param\n", __func__); + rc = -EFAULT; + goto end; + } +end: + kfree(params_value); + return rc; +} + +static int msm_ds2_dap_param_visualizer_control_get(u32 cmd, void *arg) +{ + int32_t *visualizer_data = NULL; + int i = 0, ret = 0, port_id = -1, cache_dev = -1, copp_idx = -1; + int32_t *update_visualizer_data = NULL; + struct dolby_param_data *dolby_data = (struct dolby_param_data *)arg; + uint32_t offset, length, params_length; + uint32_t param_payload_len = + DOLBY_PARAM_PAYLOAD_SIZE * sizeof(uint32_t); + struct param_hdr_v3 param_hdr; + + for (i = 0; i < DS2_DEVICES_ALL; i++) { + if ((dev_map[i].active)) { + port_id = dev_map[i].port_id; + cache_dev = dev_map[i].cache_dev; + copp_idx = dev_map[i].copp_idx; + break; + } + } + + if (port_id == DOLBY_INVALID_PORT_ID || + (copp_idx < 0) || (copp_idx >= MAX_COPPS_PER_PORT)) { + ret = 0; + dolby_data->length = 0; + pr_err("%s: no device active\n", __func__); + goto end; + } + + memset(¶m_hdr, 0, sizeof(param_hdr)); + length = ds2_dap_params[cache_dev].params_val[DOLBY_PARAM_VCNB_OFFSET]; + + if (length > DOLBY_PARAM_VCNB_MAX_LENGTH || length <= 0) { + ret = 0; + dolby_data->length = 0; + pr_err("%s Incorrect VCNB length", __func__); + return -EINVAL; + } + + params_length = (2*length + DOLBY_VIS_PARAM_HEADER_SIZE) * + sizeof(uint32_t); + + visualizer_data = kzalloc(params_length, GFP_KERNEL); + if (!visualizer_data) { + pr_err("%s: params memory alloc failed\n", __func__); + ret = -ENOMEM; + dolby_data->length = 0; + goto end; + } + memset(visualizer_data, 0x0, params_length); + + /* Return error on get param in soft or hard bypass */ + if (ds2_dap_params_states.dap_bypass == true) { + pr_debug("%s: visualizer called in bypass, return 0\n", + __func__); + ret = 0; + dolby_data->length = 0; + goto end; + } + + offset = 0; + params_length = length * sizeof(uint32_t); + param_hdr.module_id = DOLBY_BUNDLE_MODULE_ID; + param_hdr.instance_id = INSTANCE_ID_0; + param_hdr.param_id = DOLBY_PARAM_ID_VCBG; + param_hdr.param_size = length * sizeof(uint32_t) + param_payload_len; + ret = adm_get_pp_params(port_id, copp_idx, ADM_CLIENT_ID_DEFAULT, NULL, + ¶m_hdr, + (((char *) (visualizer_data)) + offset)); + if (ret) { + pr_err("%s: get parameters failed ret %d\n", __func__, ret); + ret = -EINVAL; + dolby_data->length = 0; + goto end; + } + offset = length * sizeof(uint32_t); + param_hdr.module_id = DOLBY_BUNDLE_MODULE_ID; + param_hdr.instance_id = INSTANCE_ID_0; + param_hdr.param_id = DOLBY_PARAM_ID_VCBE; + param_hdr.param_size = length * sizeof(uint32_t) + param_payload_len; + ret = adm_get_pp_params(port_id, copp_idx, ADM_CLIENT_ID_DEFAULT, NULL, + ¶m_hdr, + (((char *) (visualizer_data)) + offset)); + if (ret) { + pr_err("%s: get parameters failed ret %d\n", __func__, ret); + ret = -EINVAL; + dolby_data->length = 0; + goto end; + } + update_visualizer_data = visualizer_data; + dolby_data->length = 2 * length; + + if (copy_to_user((void *)dolby_data->data, + (void *)update_visualizer_data, + (dolby_data->length * sizeof(uint32_t)))) { + pr_err("%s: copy to user failed for data\n", __func__); + dolby_data->length = 0; + ret = -EFAULT; + goto end; + } + +end: + kfree(visualizer_data); + return ret; +} + +int msm_ds2_dap_set_security_control(u32 cmd, void *arg) +{ + struct dolby_param_license *dolby_license = + ((struct dolby_param_license *)arg); + pr_debug("%s: dmid %d license key %d\n", __func__, + dolby_license->dmid, dolby_license->license_key); + core_set_dolby_manufacturer_id(dolby_license->dmid); + core_set_license(dolby_license->license_key, DOLBY_DS1_LICENSE_ID); + return 0; +} + +int msm_ds2_dap_update_port_parameters(struct snd_hwdep *hw, struct file *file, + bool open) +{ + int i = 0, dev_id = 0; + + pr_debug("%s: open %d\n", __func__, open); + ds2_dap_params_states.node_opened = open; + ds2_dap_params_states.dap_bypass = true; + ds2_dap_params_states.dap_bypass_type = 0; + ds2_dap_params_states.use_cache = 0; + ds2_dap_params_states.device = 0; + ds2_dap_params_states.custom_stereo_onoff = 0; + for (i = 0; i < DS2_DEVICES_ALL; i++) { + if (i == 0) + dev_map[i].device_id = 0; + else { + dev_id = (1 << (i-1)); + if (all_supported_devices & dev_id) + dev_map[i].device_id = dev_id; + else + continue; + } + dev_map[i].cache_dev = + msm_ds2_dap_map_device_to_dolby_cache_devices( + dev_map[i].device_id); + if (dev_map[i].cache_dev < 0 || + dev_map[i].cache_dev >= DOLBY_MAX_CACHE) + pr_err("%s: Invalid cache device %d for device 0x%x\n", + __func__, + dev_map[i].cache_dev, + dev_map[i].device_id); + dev_map[i].port_id = -1; + dev_map[i].active = false; + dev_map[i].stream_ref_count = 0; + dev_map[i].cal_data = NULL; + dev_map[i].copp_idx = -1; + pr_debug("%s: device_id 0x%x, cache_dev %d act %d\n", __func__, + dev_map[i].device_id, dev_map[i].cache_dev, + dev_map[i].active); + } + return 0; + +} + +int msm_ds2_dap_ioctl_shared(struct snd_hwdep *hw, struct file *file, + u32 cmd, void *arg) +{ + int ret = 0; + + pr_debug("%s: cmd: 0x%x\n", __func__, cmd); + switch (cmd) { + case SNDRV_DEVDEP_DAP_IOCTL_SET_PARAM: + ret = msm_ds2_dap_set_param(cmd, arg); + break; + case SNDRV_DEVDEP_DAP_IOCTL_GET_PARAM: + ret = msm_ds2_dap_get_param(cmd, arg); + break; + case SNDRV_DEVDEP_DAP_IOCTL_DAP_COMMAND: + ret = msm_ds2_dap_handle_commands(cmd, arg); + break; + case SNDRV_DEVDEP_DAP_IOCTL_DAP_LICENSE: + ret = msm_ds2_dap_set_security_control(cmd, arg); + break; + case SNDRV_DEVDEP_DAP_IOCTL_GET_VISUALIZER: + ret = msm_ds2_dap_param_visualizer_control_get(cmd, arg); + break; + default: + pr_err("%s: called with invalid control 0x%x\n", __func__, cmd); + ret = -EINVAL; + } + return ret; +} + +int msm_ds2_dap_ioctl(struct snd_hwdep *hw, struct file *file, + u32 cmd, void *arg) +{ + + int ret = 0; + + pr_debug("%s: cmd: 0x%x\n", __func__, cmd); + if (!arg) { + pr_err("%s: Invalid params event status\n", __func__); + ret = -EINVAL; + goto end; + } + switch (cmd) { + case SNDRV_DEVDEP_DAP_IOCTL_SET_PARAM: + case SNDRV_DEVDEP_DAP_IOCTL_DAP_COMMAND: { + struct dolby_param_data dolby_data; + + if (copy_from_user((void *)&dolby_data, (void *)arg, + sizeof(struct dolby_param_data))) { + pr_err("%s: Copy from user failed\n", __func__); + ret = -EFAULT; + goto end; + } + ret = msm_ds2_dap_ioctl_shared(hw, file, cmd, &dolby_data); + break; + } + case SNDRV_DEVDEP_DAP_IOCTL_DAP_LICENSE: { + struct dolby_param_license dolby_license; + + if (copy_from_user((void *)&dolby_license, (void *)arg, + sizeof(struct dolby_param_license))) { + pr_err("%s: Copy from user failed\n", __func__); + ret = -EFAULT; + goto end; + } + ret = msm_ds2_dap_ioctl_shared(hw, file, cmd, &dolby_license); + break; + } + case SNDRV_DEVDEP_DAP_IOCTL_GET_PARAM: + case SNDRV_DEVDEP_DAP_IOCTL_GET_VISUALIZER: { + struct dolby_param_data dolby_data; + + if (copy_from_user((void *)&dolby_data, (void *)arg, + sizeof(struct dolby_param_data))) { + pr_err("%s: Copy from user failed\n", __func__); + ret = -EFAULT; + goto end; + } + ret = msm_ds2_dap_ioctl_shared(hw, file, cmd, &dolby_data); + if (ret < 0) + pr_err("%s: ioctl cmd %d returned err %d\n", + __func__, cmd, ret); + if (copy_to_user((void *)arg, &dolby_data, + sizeof(struct dolby_param_data))) { + pr_err("%s: Copy to user failed\n", __func__); + ret = -EFAULT; + goto end; + } + break; + } + default: + pr_err("%s: called with invalid control 0x%x\n", __func__, cmd); + ret = -EINVAL; + } +end: + return ret; + +} +#ifdef CONFIG_COMPAT +int msm_ds2_dap_compat_ioctl(struct snd_hwdep *hw, struct file *file, + u32 cmd, void *arg) +{ + int ret = 0; + + pr_debug("%s: cmd: 0x%x\n", __func__, cmd); + switch (cmd) { + case SNDRV_DEVDEP_DAP_IOCTL_SET_PARAM32: + cmd = SNDRV_DEVDEP_DAP_IOCTL_SET_PARAM; + goto handle_set_ioctl; + case SNDRV_DEVDEP_DAP_IOCTL_DAP_COMMAND32: + cmd = SNDRV_DEVDEP_DAP_IOCTL_DAP_COMMAND; +handle_set_ioctl: + { + struct dolby_param_data32 dolby_data32; + struct dolby_param_data dolby_data; + + memset(&dolby_data32, 0, sizeof(dolby_data32)); + memset(&dolby_data, 0, sizeof(dolby_data)); + if (copy_from_user(&dolby_data32, (void *)arg, + sizeof(struct dolby_param_data32))) { + pr_err("%s: Copy from user failed\n", __func__); + ret = -EFAULT; + goto end; + } + dolby_data.version = dolby_data32.version; + dolby_data.device_id = dolby_data32.device_id; + dolby_data.be_id = dolby_data32.be_id; + dolby_data.param_id = dolby_data32.param_id; + dolby_data.length = dolby_data32.length; + dolby_data.data = compat_ptr(dolby_data32.data); + + ret = msm_ds2_dap_ioctl_shared(hw, file, cmd, &dolby_data); + break; + } + case SNDRV_DEVDEP_DAP_IOCTL_GET_PARAM32: + cmd = SNDRV_DEVDEP_DAP_IOCTL_GET_PARAM; + goto handle_get_ioctl; + case SNDRV_DEVDEP_DAP_IOCTL_GET_VISUALIZER32: + cmd = SNDRV_DEVDEP_DAP_IOCTL_GET_VISUALIZER; +handle_get_ioctl: + { + struct dolby_param_data32 dolby_data32; + struct dolby_param_data dolby_data; + + memset(&dolby_data32, 0, sizeof(dolby_data32)); + memset(&dolby_data, 0, sizeof(dolby_data)); + if (copy_from_user(&dolby_data32, (void *)arg, + sizeof(struct dolby_param_data32))) { + pr_err("%s: Copy from user failed\n", __func__); + ret = -EFAULT; + goto end; + } + dolby_data.version = dolby_data32.version; + dolby_data.device_id = dolby_data32.device_id; + dolby_data.be_id = dolby_data32.be_id; + dolby_data.param_id = dolby_data32.param_id; + dolby_data.length = dolby_data32.length; + dolby_data.data = compat_ptr(dolby_data32.data); + + ret = msm_ds2_dap_ioctl_shared(hw, file, cmd, &dolby_data); + if (ret < 0) + pr_err("%s: ioctl cmd %d, returned err %d\n", + __func__, cmd, ret); + dolby_data32.length = dolby_data.length; + if (copy_to_user((void *)arg, &dolby_data32, + sizeof(struct dolby_param_data32))) { + pr_err("%s: Copy to user failed\n", __func__); + ret = -EFAULT; + goto end; + } + break; + } + case SNDRV_DEVDEP_DAP_IOCTL_DAP_LICENSE32: { + struct dolby_param_license32 dolby_license32; + struct dolby_param_license dolby_license; + + cmd = SNDRV_DEVDEP_DAP_IOCTL_DAP_LICENSE; + if (copy_from_user((void *)&dolby_license32, (void *)arg, + sizeof(struct dolby_param_license32))) { + pr_err("%s: Copy from user failed\n", __func__); + ret = -EFAULT; + goto end; + } + dolby_license.dmid = dolby_license32.dmid; + dolby_license.license_key = dolby_license32.license_key; + ret = msm_ds2_dap_ioctl_shared(hw, file, cmd, &dolby_license); + break; + } + default: + pr_err("%s: called with invalid control 0x%x\n", + __func__, cmd); + ret = -EINVAL; + } +end: + return ret; + +} +#endif + +int msm_ds2_dap_init(int port_id, int copp_idx, int channels, + bool is_custom_stereo_on) +{ + int ret = 0, idx = -1, i; + struct dolby_param_data dolby_data; + + struct audproc_softvolume_params softvol = { + .period = DOLBY_SOFT_VOLUME_PERIOD, + .step = DOLBY_SOFT_VOLUME_STEP, + .rampingcurve = DOLBY_SOFT_VOLUME_CURVE_EXP, + }; + + pr_debug("%s: port id %d, copp_idx %d\n", __func__, port_id, copp_idx); + + if (port_id != DOLBY_INVALID_PORT_ID) { + for (i = 0; i < DS2_DEVICES_ALL; i++) { + if ((dev_map[i].port_id == port_id) && + /* device part of active device */ + (dev_map[i].device_id & + ds2_dap_params_states.device)) { + idx = i; + /* Give priority to headset in case of + * combo device + */ + if (dev_map[i].device_id == SPEAKER) + continue; + else + break; + } + } + if (idx < 0) { + pr_err("%s: invalid index for port %d\n", + __func__, port_id); + ret = -EINVAL; + goto end; + } + pr_debug("%s:index %d, dev[0x%x,0x%x]\n", __func__, idx, + dev_map[idx].device_id, ds2_dap_params_states.device); + dev_map[idx].active = true; + dev_map[idx].copp_idx = copp_idx; + dolby_data.param_id = DOLBY_COMMIT_ALL_TO_DSP; + dolby_data.length = 0; + dolby_data.data = NULL; + dolby_data.device_id = dev_map[idx].device_id; + pr_debug("%s: idx %d, active %d, dev id 0x%x, ref count %d\n", + __func__, idx, dev_map[idx].active, + dev_map[idx].device_id, + dev_map[idx].stream_ref_count); + if (dev_map[idx].stream_ref_count == 0) { + /*perform next 3 func only if hard bypass enabled*/ + if (ds2_dap_params_states.dap_bypass_type == + DAP_HARD_BYPASS) { + ret = msm_ds2_dap_alloc_and_store_cal_data(idx, + ADM_PATH_PLAYBACK, 0); + if (ret < 0) { + pr_err("%s: Failed to alloc and store cal data for idx %d, device %d, copp_idx %d", + __func__, + idx, dev_map[idx].device_id, + dev_map[idx].copp_idx); + dev_map[idx].active = false; + dev_map[idx].copp_idx = -1; + goto end; + } + + ret = adm_set_softvolume(port_id, copp_idx, + &softvol); + if (ret < 0) { + pr_err("%s: Soft volume ret error %d\n", + __func__, ret); + dev_map[idx].active = false; + dev_map[idx].copp_idx = -1; + goto end; + } + + ret = msm_ds2_dap_init_modules_in_topology( + idx); + if (ret < 0) { + pr_err("%s: Failed to init modules in topolofy for idx %d, device %d, copp_idx %d\n", + __func__, idx, + dev_map[idx].device_id, + dev_map[idx].copp_idx); + dev_map[idx].active = false; + dev_map[idx].copp_idx = -1; + goto end; + } + } + + ret = msm_ds2_dap_commit_params(&dolby_data, 0); + if (ret < 0) { + pr_debug("%s: commit params ret %d\n", + __func__, ret); + ret = 0; + } + } + dev_map[idx].stream_ref_count++; + if (is_custom_stereo_on) { + ds2_dap_params_states.custom_stereo_onoff = + is_custom_stereo_on; + set_custom_stereo_onoff(idx, + is_custom_stereo_on); + } + } + +end: + return ret; +} + +void msm_ds2_dap_deinit(int port_id) +{ + /* + * Get the active port corrresponding to the active device + * Check if this is same as incoming port + * Set it to invalid + */ + int idx = -1, i; + + pr_debug("%s: port_id %d\n", __func__, port_id); + if (port_id != DOLBY_INVALID_PORT_ID) { + for (i = 0; i < DS2_DEVICES_ALL; i++) { + /* Active port */ + if ((dev_map[i].port_id == port_id) && + /* device part of active device */ + (dev_map[i].device_id & + ds2_dap_params_states.device) && + /* + * Need this check to avoid race condition of + * active device being set and playback + * instance opened + */ + /* active device*/ + dev_map[i].active) { + idx = i; + if (dev_map[i].device_id == SPEAKER) + continue; + else + break; + } + } + if (idx < 0) { + pr_err("%s: invalid index for port %d\n", + __func__, port_id); + return; + } + pr_debug("%s:index %d, dev [0x%x, 0x%x]\n", __func__, idx, + dev_map[idx].device_id, ds2_dap_params_states.device); + dev_map[idx].stream_ref_count--; + if (dev_map[idx].stream_ref_count == 0) { + /*perform next func only if hard bypass enabled*/ + if (ds2_dap_params_states.dap_bypass_type == + DAP_HARD_BYPASS) { + msm_ds2_dap_free_cal_data(idx); + } + ds2_dap_params_states.device &= ~dev_map[idx].device_id; + dev_map[idx].active = false; + dev_map[idx].copp_idx = -1; + } + pr_debug("%s:idx %d, active %d, dev id 0x%x ref count %d\n", + __func__, idx, dev_map[idx].active, + dev_map[idx].device_id, dev_map[idx].stream_ref_count); + } +} + +int msm_ds2_dap_set_custom_stereo_onoff(int port_id, int copp_idx, + bool is_custom_stereo_enabled) +{ + int idx = -1, rc = 0, i; + + pr_debug("%s: port_id %d\n", __func__, port_id); + if (port_id != DOLBY_INVALID_PORT_ID) { + for (i = 0; i < DS2_DEVICES_ALL; i++) { + if ((dev_map[i].port_id == port_id) && + /* device part of active device */ + (dev_map[i].device_id & + ds2_dap_params_states.device)) { + idx = i; + if (dev_map[i].device_id == SPEAKER) + continue; + else + break; + } + } + if (idx < 0) { + pr_err("%s: invalid index for port %d\n", + __func__, port_id); + return rc; + } + ds2_dap_params_states.custom_stereo_onoff = + is_custom_stereo_enabled; + rc = set_custom_stereo_onoff(idx, + is_custom_stereo_enabled); + if (rc < 0) { + pr_err("%s: Custom stereo err %d on port %d\n", + __func__, rc, port_id); + } + } + return rc; +} diff --git a/audio-kernel/asoc/msm-ds2-dap-config.h b/audio-kernel/asoc/msm-ds2-dap-config.h new file mode 100644 index 000000000..4d6b5eb3d --- /dev/null +++ b/audio-kernel/asoc/msm-ds2-dap-config.h @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2013-2014, 2017-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 _MSM_DS2_DAP_CONFIG_H_ +#define _MSM_DS2_DAP_CONFIG_H_ + +#include +#include "msm-dolby-common.h" +#include +#include + +#ifdef CONFIG_COMPAT +struct dolby_param_data32 { + s32 version; + s32 device_id; + s32 be_id; + s32 param_id; + s32 length; + compat_uptr_t data; +}; + +struct dolby_param_license32 { + compat_uptr_t dmid; + compat_uptr_t license_key; +}; + +#define SNDRV_DEVDEP_DAP_IOCTL_SET_PARAM32\ + _IOWR('U', 0x10, struct dolby_param_data32) +#define SNDRV_DEVDEP_DAP_IOCTL_GET_PARAM32\ + _IOR('U', 0x11, struct dolby_param_data32) +#define SNDRV_DEVDEP_DAP_IOCTL_DAP_COMMAND32\ + _IOWR('U', 0x13, struct dolby_param_data32) +#define SNDRV_DEVDEP_DAP_IOCTL_DAP_LICENSE32\ + _IOWR('U', 0x14, struct dolby_param_license32) +#define SNDRV_DEVDEP_DAP_IOCTL_GET_VISUALIZER32\ + _IOR('U', 0x15, struct dolby_param_data32) +#endif + +#if defined(CONFIG_DOLBY_DS2) || defined(CONFIG_DOLBY_LICENSE) +/* DOLBY DOLBY GUIDS */ +#define DS2_MODULE_ID 0x00010775 + +#define DS2_DSP_SUPPORTED_ENDP_DEVICE 17 +#define DS2_DEVICES_ALL 32 /* enum val is 4 bytes */ + +enum { + + DAP_CMD_COMMIT_ALL = 0, + DAP_CMD_COMMIT_CHANGED = 1, + DAP_CMD_USE_CACHE_FOR_INIT = 2, + DAP_CMD_SET_BYPASS = 3, + DAP_CMD_SET_ACTIVE_DEVICE = 4, + DAP_CMD_SET_BYPASS_TYPE = 5, +}; + +struct custom_stereo_param { + /* Index is 32-bit param in little endian */ + u16 index; + u16 reserved; + + /* For stereo mixing, the number of out channels */ + u16 num_out_ch; + /* For stereo mixing, the number of in channels */ + u16 num_in_ch; + + /* Out channel map FL/FR*/ + u16 out_fl; + u16 out_fr; + + /* In channel map FL/FR*/ + u16 in_fl; + u16 in_fr; + + /* + * Weighting coefficients. Mixing will be done according to + * these coefficients. + */ + u16 op_FL_ip_FL_weight; + u16 op_FL_ip_FR_weight; + u16 op_FR_ip_FL_weight; + u16 op_FR_ip_FR_weight; +}; + +#define DOLBY_PARAM_INT_ENDP_LENGTH 1 +#define DOLBY_PARAM_INT_ENDP_OFFSET (DOLBY_PARAM_PSTG_OFFSET + \ + DOLBY_PARAM_PSTG_LENGTH) +#define MAX_DS2_PARAMS 48 +#define MAX_DS2_CTRL_PARAMS 4 +#define ALL_DS2_PARAMS (MAX_DS2_PARAMS + \ + MAX_DS2_CTRL_PARAMS) +#define TOTAL_LENGTH_DS2_PARAM (TOTAL_LENGTH_DOLBY_PARAM + 1) + +int msm_ds2_dap_update_port_parameters(struct snd_hwdep *hw, struct file *file, + bool open); +int msm_ds2_dap_ioctl(struct snd_hwdep *hw, struct file *file, + u32 cmd, void *arg); +int msm_ds2_dap_compat_ioctl(struct snd_hwdep *hw, + struct file *file, + u32 cmd, void *arg); +int msm_ds2_dap_init(int port_id, int copp_idx, int channels, + bool is_custom_stereo_on); +void msm_ds2_dap_deinit(int port_id); +int msm_ds2_dap_set_custom_stereo_onoff(int port_id, int copp_idx, + bool is_custom_stereo_enabled); +/* Dolby DOLBY end */ +#else + +static inline int msm_ds2_dap_update_port_parameters(struct snd_hwdep *hw, + struct file *file, + bool open) +{ + return 0; +} + +static inline int msm_ds2_dap_ioctl(struct snd_hwdep *hw, struct file *file, + u32 cmd, void *arg) +{ + return 0; +} + +static inline int msm_ds2_dap_compat_ioctl(struct snd_hwdep *hw, + struct file *file, + u32 cmd, void *arg) +{ + return 0; +} +static inline int msm_ds2_dap_init(int port_id, int copp_idx, int channels, + bool is_custom_stereo_on) +{ + return 0; +} + +static inline void msm_ds2_dap_deinit(int port_id) { } + +static inline int msm_ds2_dap_set_custom_stereo_onoff(int port_id, int copp_idx, + bool is_custom_stereo_enabled) +{ + return 0; +} +#endif +#endif diff --git a/audio-kernel/asoc/msm-lsm-client.c b/audio-kernel/asoc/msm-lsm-client.c new file mode 100644 index 000000000..391d71946 --- /dev/null +++ b/audio-kernel/asoc/msm-lsm-client.c @@ -0,0 +1,2991 @@ +/* + * Copyright (c) 2013-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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "msm-pcm-routing-v2.h" + +#define CAPTURE_MIN_NUM_PERIODS 2 +#define CAPTURE_MAX_NUM_PERIODS 8 +#define CAPTURE_MAX_PERIOD_SIZE 61440 +#define CAPTURE_MIN_PERIOD_SIZE 320 +#define LISTEN_MAX_STATUS_PAYLOAD_SIZE 256 + +#define LAB_BUFFER_ALLOC 1 +#define LAB_BUFFER_DEALLOC 0 + +#define LSM_IS_LAST_STAGE(client, stage_idx) \ + (client->num_stages == (stage_idx + 1)) + +static struct snd_pcm_hardware msm_pcm_hardware_capture = { + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .rates = (SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_48000), + .rate_min = 16000, + .rate_max = 48000, + .channels_min = LSM_INPUT_NUM_CHANNELS_MIN, + .channels_max = LSM_INPUT_NUM_CHANNELS_MAX, + .buffer_bytes_max = CAPTURE_MAX_NUM_PERIODS * + CAPTURE_MAX_PERIOD_SIZE, + .period_bytes_min = CAPTURE_MIN_PERIOD_SIZE, + .period_bytes_max = CAPTURE_MAX_PERIOD_SIZE, + .periods_min = CAPTURE_MIN_NUM_PERIODS, + .periods_max = CAPTURE_MAX_NUM_PERIODS, + .fifo_size = 0, +}; + +/* Conventional and unconventional sample rate supported */ +static unsigned int supported_sample_rates[] = { + 16000, 48000, +}; + +static struct snd_pcm_hw_constraint_list constraints_sample_rates = { + .count = ARRAY_SIZE(supported_sample_rates), + .list = supported_sample_rates, + .mask = 0, +}; + +struct lsm_priv { + struct snd_pcm_substream *substream; + struct lsm_client *lsm_client; + struct snd_lsm_event_status_v3 *event_status; + struct snd_lsm_event_status *det_event; + spinlock_t event_lock; + wait_queue_head_t event_wait; + unsigned long event_avail; + atomic_t event_wait_stop; + atomic_t buf_count; + atomic_t read_abort; + wait_queue_head_t period_wait; + struct mutex lsm_api_lock; + int appl_cnt; + int dma_write; + int xrun_count; + int xrun_index; + spinlock_t xrun_lock; +}; + +enum { /* lsm session states */ + IDLE = 0, + RUNNING, +}; + +static int msm_lsm_queue_lab_buffer(struct lsm_priv *prtd, int i) +{ + int rc = 0; + struct lsm_cmd_read cmd_read; + struct snd_soc_pcm_runtime *rtd; + + if (!prtd || !prtd->lsm_client) { + pr_err("%s: Invalid params prtd %pK lsm client %pK\n", + __func__, prtd, ((!prtd) ? NULL : prtd->lsm_client)); + return -EINVAL; + } + if (!prtd->substream || !prtd->substream->private_data) { + pr_err("%s: Invalid %s\n", __func__, + (!prtd->substream) ? "substream" : "private_data"); + return -EINVAL; + } + rtd = prtd->substream->private_data; + + if (!prtd->lsm_client->lab_buffer || + i >= prtd->lsm_client->out_hw_params.period_count) { + dev_err(rtd->dev, + "%s: Lab buffer not setup %pK incorrect index %d period count %d\n", + __func__, prtd->lsm_client->lab_buffer, i, + prtd->lsm_client->out_hw_params.period_count); + return -EINVAL; + } + cmd_read.buf_addr_lsw = + lower_32_bits(prtd->lsm_client->lab_buffer[i].phys); + cmd_read.buf_addr_msw = + msm_audio_populate_upper_32_bits( + prtd->lsm_client->lab_buffer[i].phys); + cmd_read.buf_size = prtd->lsm_client->lab_buffer[i].size; + cmd_read.mem_map_handle = + prtd->lsm_client->lab_buffer[i].mem_map_handle; + rc = q6lsm_read(prtd->lsm_client, &cmd_read); + if (rc) + dev_err(rtd->dev, + "%s: error in queuing the lab buffer rc %d\n", + __func__, rc); + return rc; +} + +static int lsm_lab_buffer_sanity(struct lsm_priv *prtd, + struct lsm_cmd_read_done *read_done, int *index) +{ + int i = 0, rc = -EINVAL; + struct snd_soc_pcm_runtime *rtd; + + if (!prtd || !read_done || !index) { + pr_err("%s: Invalid params prtd %pK read_done %pK index %pK\n", + __func__, prtd, read_done, index); + return -EINVAL; + } + + if (!prtd->substream || !prtd->substream->private_data) { + pr_err("%s: Invalid %s\n", __func__, + (!prtd->substream) ? "substream" : "private_data"); + return -EINVAL; + } + rtd = prtd->substream->private_data; + + if (!prtd->lsm_client->lab_enable || !prtd->lsm_client->lab_buffer) { + dev_err(rtd->dev, + "%s: Lab not enabled %d invalid lab buffer %pK\n", + __func__, prtd->lsm_client->lab_enable, + prtd->lsm_client->lab_buffer); + return -EINVAL; + } + for (i = 0; i < prtd->lsm_client->out_hw_params.period_count; i++) { + if ((lower_32_bits(prtd->lsm_client->lab_buffer[i].phys) == + read_done->buf_addr_lsw) && + (msm_audio_populate_upper_32_bits + (prtd->lsm_client->lab_buffer[i].phys) == + read_done->buf_addr_msw) && + (prtd->lsm_client->lab_buffer[i].mem_map_handle == + read_done->mem_map_handle)) { + dev_dbg(rtd->dev, + "%s: Buffer found %pK memmap handle %d\n", + __func__, &prtd->lsm_client->lab_buffer[i].phys, + prtd->lsm_client->lab_buffer[i].mem_map_handle); + if (read_done->total_size > + prtd->lsm_client->lab_buffer[i].size) { + dev_err(rtd->dev, + "%s: Size mismatch call back size %d actual size %zd\n", + __func__, read_done->total_size, + prtd->lsm_client->lab_buffer[i].size); + rc = -EINVAL; + break; + } else { + *index = i; + rc = 0; + break; + } + } + } + return rc; +} + +static void lsm_event_handler(uint32_t opcode, uint32_t token, + uint32_t *payload, void *priv) +{ + unsigned long flags; + struct lsm_priv *prtd = priv; + struct snd_pcm_substream *substream = prtd->substream; + struct snd_soc_pcm_runtime *rtd; + struct snd_lsm_event_status_v3 *temp; + uint16_t status = 0; + uint16_t payload_size = 0; + uint16_t index = 0; + uint32_t event_ts_lsw = 0; + uint32_t event_ts_msw = 0; + + if (!substream || !substream->private_data) { + pr_err("%s: Invalid %s\n", __func__, + (!substream) ? "substream" : "private_data"); + return; + } + rtd = substream->private_data; + + switch (opcode) { + case LSM_DATA_EVENT_READ_DONE: { + int rc; + struct lsm_cmd_read_done *read_done = (struct lsm_cmd_read_done *)payload; + int buf_index = 0; + unsigned long flags = 0; + + if (prtd->lsm_client->session != token || + !read_done) { + dev_err(rtd->dev, + "%s: EVENT_READ_DONE invalid callback, session %d callback %d payload %pK", + __func__, prtd->lsm_client->session, + token, read_done); + return; + } + if (atomic_read(&prtd->read_abort)) { + dev_dbg(rtd->dev, + "%s: read abort set skip data\n", __func__); + return; + } + if (!lsm_lab_buffer_sanity(prtd, read_done, &buf_index)) { + dev_dbg(rtd->dev, + "%s: process read done index %d\n", + __func__, buf_index); + if (buf_index >= + prtd->lsm_client->out_hw_params.period_count) { + dev_err(rtd->dev, + "%s: Invalid index %d buf_index max cnt %d\n", + __func__, buf_index, + prtd->lsm_client->out_hw_params.period_count); + return; + } + spin_lock_irqsave(&prtd->xrun_lock, flags); + prtd->dma_write += read_done->total_size; + atomic_inc(&prtd->buf_count); + snd_pcm_period_elapsed(substream); + wake_up(&prtd->period_wait); + if (atomic_read(&prtd->buf_count) < + prtd->lsm_client->out_hw_params.period_count) { + /* queue the next period buffer */ + buf_index = (buf_index + 1) % + prtd->lsm_client->out_hw_params.period_count; + rc = msm_lsm_queue_lab_buffer(prtd, buf_index); + if (rc) + dev_err(rtd->dev, + "%s: error in queuing the lab buffer rc %d\n", + __func__, rc); + } else { + dev_dbg(rtd->dev, + "%s: xrun: further lab to be queued after read from user\n", + __func__); + if (!prtd->xrun_count) + prtd->xrun_index = buf_index; + (prtd->xrun_count)++; + } + spin_unlock_irqrestore(&prtd->xrun_lock, flags); + } else + dev_err(rtd->dev, "%s: Invalid lab buffer returned by dsp\n", + __func__); + break; + } + + case LSM_SESSION_EVENT_DETECTION_STATUS: + status = (uint16_t)((uint8_t *)payload)[0]; + payload_size = (uint16_t)((uint8_t *)payload)[2]; + index = 4; + dev_dbg(rtd->dev, + "%s: event detect status = %d payload size = %d\n", + __func__, status, payload_size); + break; + + case LSM_SESSION_EVENT_DETECTION_STATUS_V2: + status = (uint16_t)((uint8_t *)payload)[0]; + payload_size = (uint16_t)((uint8_t *)payload)[1]; + index = 2; + dev_dbg(rtd->dev, + "%s: event detect status = %d payload size = %d\n", + __func__, status, payload_size); + break; + + case LSM_SESSION_EVENT_DETECTION_STATUS_V3: + event_ts_lsw = ((uint32_t *)payload)[0]; + event_ts_msw = ((uint32_t *)payload)[1]; + status = (uint16_t)((uint8_t *)payload)[8]; + payload_size = (uint16_t)((uint8_t *)payload)[9]; + index = 10; + dev_dbg(rtd->dev, + "%s: ts_msw = %u, ts_lsw = %u, event detect status = %d payload size = %d\n", + __func__, event_ts_msw, event_ts_lsw, status, + payload_size); + break; + + case LSM_SESSION_DETECTION_ENGINE_GENERIC_EVENT: { + struct snd_lsm_event_status *tmp; + + status = ((uint16_t *)payload)[0]; + payload_size = ((uint16_t *)payload)[1]; + + spin_lock_irqsave(&prtd->event_lock, flags); + tmp = krealloc(prtd->det_event, + sizeof(struct snd_lsm_event_status) + + payload_size, GFP_ATOMIC); + if (!tmp) { + spin_unlock_irqrestore(&prtd->event_lock, flags); + dev_err(rtd->dev, + "%s: Failed to allocate memory for %s, size = %zu\n", + __func__, + "LSM_SESSION_DETECTION_ENGINE_GENERIC_EVENT", + sizeof(struct snd_lsm_event_status) + + payload_size); + return; + } + + prtd->det_event = tmp; + prtd->det_event->status = status; + prtd->det_event->payload_size = payload_size; + memcpy(prtd->det_event->payload, &((uint8_t *)payload)[4], + payload_size); + prtd->event_avail = 1; + spin_unlock_irqrestore(&prtd->event_lock, flags); + wake_up(&prtd->event_wait); + + if (substream->timer_running) + snd_timer_interrupt(substream->timer, 1); + + dev_dbg(rtd->dev, + "%s: Generic det event status = %d payload size = %d\n", + __func__, prtd->det_event->status, + prtd->det_event->payload_size); + break; + } + + default: + break; + } + + if (opcode == LSM_SESSION_EVENT_DETECTION_STATUS || + opcode == LSM_SESSION_EVENT_DETECTION_STATUS_V2 || + opcode == LSM_SESSION_EVENT_DETECTION_STATUS_V3) { + spin_lock_irqsave(&prtd->event_lock, flags); + temp = krealloc(prtd->event_status, + sizeof(struct snd_lsm_event_status_v3) + + payload_size, GFP_ATOMIC); + if (!temp) { + dev_err(rtd->dev, "%s: no memory for event status\n", + __func__); + return; + } + /* + * event status timestamp will be non-zero and valid if + * opcode is LSM_SESSION_EVENT_DETECTION_STATUS_V3 + */ + prtd->event_status = temp; + prtd->event_status->timestamp_lsw = event_ts_lsw; + prtd->event_status->timestamp_msw = event_ts_msw; + prtd->event_status->status = status; + prtd->event_status->payload_size = payload_size; + + if (likely(prtd->event_status)) { + memcpy(prtd->event_status->payload, + &((uint8_t *)payload)[index], + payload_size); + prtd->event_avail = 1; + spin_unlock_irqrestore(&prtd->event_lock, flags); + wake_up(&prtd->event_wait); + } else { + spin_unlock_irqrestore(&prtd->event_lock, flags); + dev_err(rtd->dev, + "%s: Couldn't allocate %d bytes of memory\n", + __func__, payload_size); + } + if (substream->timer_running) + snd_timer_interrupt(substream->timer, 1); + } +} + +static int msm_lsm_lab_buffer_alloc(struct lsm_priv *lsm, int alloc) +{ + int ret = 0; + struct snd_dma_buffer *dma_buf = NULL; + + if (!lsm) { + pr_err("%s: Invalid param lsm %pK\n", __func__, lsm); + return -EINVAL; + } + if (alloc) { + if (!lsm->substream) { + pr_err("%s: substream is NULL\n", __func__); + return -EINVAL; + } + ret = q6lsm_lab_buffer_alloc(lsm->lsm_client, alloc); + if (ret) { + pr_err("%s: alloc lab buffer failed ret %d\n", + __func__, ret); + goto exit; + } + dma_buf = &lsm->substream->dma_buffer; + dma_buf->dev.type = SNDRV_DMA_TYPE_DEV; + dma_buf->dev.dev = lsm->substream->pcm->card->dev; + dma_buf->private_data = NULL; + dma_buf->area = lsm->lsm_client->lab_buffer[0].data; + dma_buf->addr = lsm->lsm_client->lab_buffer[0].phys; + dma_buf->bytes = lsm->lsm_client->out_hw_params.buf_sz * + lsm->lsm_client->out_hw_params.period_count; + snd_pcm_set_runtime_buffer(lsm->substream, dma_buf); + } else { + ret = q6lsm_lab_buffer_alloc(lsm->lsm_client, alloc); + if (ret) + pr_err("%s: free lab buffer failed ret %d\n", + __func__, ret); + kfree(lsm->lsm_client->lab_buffer); + lsm->lsm_client->lab_buffer = NULL; + } +exit: + return ret; +} + +static int msm_lsm_get_conf_levels(struct lsm_client *client, + u8 *conf_levels_ptr) +{ + int rc = 0; + + if (client->num_confidence_levels == 0) { + pr_debug("%s: no confidence levels provided\n", + __func__); + client->confidence_levels = NULL; + goto done; + } + + client->confidence_levels = + kzalloc((sizeof(uint8_t) * client->num_confidence_levels), + GFP_KERNEL); + if (!client->confidence_levels) { + pr_err("%s: No memory for confidence\n" + "levels num of level from user = %d\n", + __func__, client->num_confidence_levels); + rc = -ENOMEM; + goto done; + } + + if (copy_from_user(client->confidence_levels, + conf_levels_ptr, + client->num_confidence_levels)) { + pr_err("%s: copy from user failed, size = %d\n", + __func__, client->num_confidence_levels); + rc = -EFAULT; + goto copy_err; + } + + return rc; + +copy_err: + kfree(client->confidence_levels); + client->confidence_levels = NULL; +done: + return rc; + +} + +static int msm_lsm_set_epd(struct snd_pcm_substream *substream, + struct lsm_params_info_v2 *p_info) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct lsm_priv *prtd = runtime->private_data; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + int rc = 0; + struct snd_lsm_ep_det_thres epd_th; + + if (p_info->param_size != sizeof(epd_th)) { + dev_err(rtd->dev, + "%s: Invalid param_size %d\n", + __func__, p_info->param_size); + rc = -EINVAL; + goto done; + } + + if (copy_from_user(&epd_th, p_info->param_data, + p_info->param_size)) { + dev_err(rtd->dev, + "%s: copy_from_user failed, size = %d\n", + __func__, p_info->param_size); + rc = -EFAULT; + goto done; + } + + rc = q6lsm_set_one_param(prtd->lsm_client, p_info, + &epd_th, LSM_ENDPOINT_DETECT_THRESHOLD); + if (rc) + dev_err(rtd->dev, + "%s: Failed to set epd param, err = %d\n", + __func__, rc); +done: + return rc; +} + +static int msm_lsm_set_mode(struct snd_pcm_substream *substream, + struct lsm_params_info_v2 *p_info) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct lsm_priv *prtd = runtime->private_data; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_lsm_detect_mode mode; + int rc = 0; + + if (p_info->param_size != sizeof(mode)) { + dev_err(rtd->dev, + "%s: Invalid param_size %d\n", + __func__, p_info->param_size); + rc = -EINVAL; + goto done; + } + + if (copy_from_user(&mode, p_info->param_data, + sizeof(mode))) { + dev_err(rtd->dev, + "%s: copy_from_user failed, size = %zd\n", + __func__, sizeof(mode)); + rc = -EFAULT; + goto done; + } + + rc = q6lsm_set_one_param(prtd->lsm_client, p_info, + &mode, LSM_OPERATION_MODE); + if (rc) + dev_err(rtd->dev, + "%s: Failed to set det_mode param, err = %d\n", + __func__, rc); +done: + return rc; +} + +static int msm_lsm_set_gain(struct snd_pcm_substream *substream, + struct lsm_params_info_v2 *p_info) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct lsm_priv *prtd = runtime->private_data; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_lsm_gain gain; + int rc = 0; + + if (p_info->param_size != sizeof(gain)) { + dev_err(rtd->dev, + "%s: Invalid param_size %d\n", + __func__, p_info->param_size); + rc = -EINVAL; + goto done; + } + + if (copy_from_user(&gain, p_info->param_data, + sizeof(gain))) { + dev_err(rtd->dev, + "%s: copy_from_user failed, size = %zd\n", + __func__, sizeof(gain)); + rc = -EFAULT; + goto done; + } + + rc = q6lsm_set_one_param(prtd->lsm_client, p_info, + &gain, LSM_GAIN); + if (rc) + dev_err(rtd->dev, + "%s: Failed to set det_mode param, err = %d\n", + __func__, rc); +done: + return rc; +} + +static int msm_lsm_set_conf(struct snd_pcm_substream *substream, + struct lsm_params_info_v2 *p_info) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct lsm_priv *prtd = runtime->private_data; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + int rc = 0; + + if (p_info->param_size > MAX_NUM_CONFIDENCE) { + dev_err(rtd->dev, + "%s: invalid confidence levels %d\n", + __func__, p_info->param_size); + return -EINVAL; + } + + prtd->lsm_client->num_confidence_levels = + p_info->param_size; + rc = msm_lsm_get_conf_levels(prtd->lsm_client, + p_info->param_data); + if (rc) { + dev_err(rtd->dev, + "%s: get_conf_levels failed, err = %d\n", + __func__, rc); + return rc; + } + + rc = q6lsm_set_one_param(prtd->lsm_client, p_info, + prtd->lsm_client->confidence_levels, + LSM_MIN_CONFIDENCE_LEVELS); + if (rc) + dev_err(rtd->dev, + "%s: Failed to set min_conf_levels, err = %d\n", + __func__, rc); + + return rc; +} + +static int msm_lsm_reg_model(struct snd_pcm_substream *substream, + struct lsm_params_info_v2 *p_info) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct lsm_priv *prtd = runtime->private_data; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + int rc = 0; + struct lsm_sound_model *sm = NULL; + size_t offset = sizeof(union param_hdrs); + + rc = q6lsm_snd_model_buf_alloc(prtd->lsm_client, + p_info->param_size, p_info); + if (rc) { + dev_err(rtd->dev, + "%s: snd_model buf alloc failed, size = %d\n", + __func__, p_info->param_size); + return rc; + } + + q6lsm_sm_set_param_data(prtd->lsm_client, p_info, &offset); + + /* + * For set_param, advance the sound model data with the + * number of bytes required by param_data. + */ + + sm = &prtd->lsm_client->stage_cfg[p_info->stage_idx].sound_model; + if (copy_from_user((u8 *)sm->data + offset, + p_info->param_data, p_info->param_size)) { + dev_err(rtd->dev, + "%s: copy_from_user for snd_model failed, size = %d\n", + __func__, p_info->param_size); + rc = -EFAULT; + goto err_copy; + } + rc = q6lsm_set_one_param(prtd->lsm_client, p_info, NULL, + LSM_REG_SND_MODEL); + if (rc) { + dev_err(rtd->dev, + "%s: Failed to set sound_model, err = %d\n", + __func__, rc); + goto err_copy; + } + return rc; + +err_copy: + q6lsm_snd_model_buf_free(prtd->lsm_client, p_info); + return rc; +} + +static int msm_lsm_dereg_model(struct snd_pcm_substream *substream, + struct lsm_params_info_v2 *p_info) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct lsm_priv *prtd = runtime->private_data; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + int rc = 0; + + rc = q6lsm_set_one_param(prtd->lsm_client, p_info, + NULL, LSM_DEREG_SND_MODEL); + if (rc) + dev_err(rtd->dev, + "%s: Failed to set det_mode param, err = %d\n", + __func__, rc); + + q6lsm_snd_model_buf_free(prtd->lsm_client, p_info); + + return rc; +} + +static int msm_lsm_set_custom(struct snd_pcm_substream *substream, + struct lsm_params_info_v2 *p_info) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct lsm_priv *prtd = runtime->private_data; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + u8 *data; + int rc = 0; + + data = kzalloc(p_info->param_size, GFP_KERNEL); + if (!data) + return -ENOMEM; + + if (copy_from_user(data, p_info->param_data, + p_info->param_size)) { + dev_err(rtd->dev, + "%s: copy_from_user failed for custom params, size = %d\n", + __func__, p_info->param_size); + rc = -EFAULT; + goto err_ret; + } + + rc = q6lsm_set_one_param(prtd->lsm_client, p_info, + data, LSM_CUSTOM_PARAMS); + if (rc) + dev_err(rtd->dev, + "%s: Failed to set custom param, err = %d\n", + __func__, rc); + +err_ret: + kfree(data); + return rc; +} + +static int msm_lsm_check_and_set_lab_controls(struct snd_pcm_substream *substream, + u32 enable, struct lsm_params_info_v2 *p_info) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct lsm_priv *prtd = runtime->private_data; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct lsm_hw_params *out_hw_params = &prtd->lsm_client->out_hw_params; + u8 chmap[out_hw_params->num_chs]; + u32 ch_idx; + int rc = 0, stage_idx = p_info->stage_idx; + + if (prtd->lsm_client->stage_cfg[stage_idx].lab_enable == enable) { + dev_dbg(rtd->dev, "%s: Lab for session %d, stage %d already %s\n", + __func__, prtd->lsm_client->session, + stage_idx, enable ? "enabled" : "disabled"); + return rc; + } + + rc = q6lsm_lab_control(prtd->lsm_client, enable, p_info); + if (rc) { + dev_err(rtd->dev, "%s: Failed to set lab_control param, err = %d\n", + __func__, rc); + return rc; + } else { + if (LSM_IS_LAST_STAGE(prtd->lsm_client, stage_idx)) { + rc = msm_lsm_lab_buffer_alloc(prtd, + enable ? LAB_BUFFER_ALLOC : LAB_BUFFER_DEALLOC); + if (rc) { + dev_err(rtd->dev, + "%s: msm_lsm_lab_buffer_alloc failed rc %d for %s\n", + __func__, rc, enable ? "ALLOC" : "DEALLOC"); + return rc; + } else { + /* set client level flag based on last stage control */ + prtd->lsm_client->lab_enable = enable; + } + } + if (!rc) + prtd->lsm_client->stage_cfg[stage_idx].lab_enable = enable; + } + + memset(chmap, 0, out_hw_params->num_chs); + /* + * First channel to be read from lab is always the + * best channel (0xff). For second channel onwards, + * the channel indices are 0, 1, .. etc + */ + chmap[0] = 0xFF; + for (ch_idx = 1; ch_idx < out_hw_params->num_chs; ch_idx++) + chmap[ch_idx] = ch_idx - 1; + + rc = q6lsm_lab_out_ch_cfg(prtd->lsm_client, chmap, p_info); + if (rc) + dev_err(rtd->dev, "%s: Failed to set lab out ch cfg %d\n", + __func__, rc); + + return rc; +} + +static int msm_lsm_set_lab_control(struct snd_pcm_substream *substream, + struct lsm_params_info_v2 *p_info) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct lsm_priv *prtd = runtime->private_data; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_lsm_lab_control lab_ctrl; + int rc = 0; + + if (p_info->param_size != sizeof(lab_ctrl)) + return -EINVAL; + + if (prtd->lsm_client->started) { + dev_err(rtd->dev, "%s: lab control sent after start\n", __func__); + return -EAGAIN; + } + + if (copy_from_user(&lab_ctrl, p_info->param_data, + p_info->param_size)) { + dev_err(rtd->dev, + "%s: copy_from_user failed for lab_control params, size = %d\n", + __func__, p_info->param_size); + return -EFAULT; + } + + rc = msm_lsm_check_and_set_lab_controls(substream, lab_ctrl.enable, p_info); + return rc; +} + +static int msm_lsm_set_poll_enable(struct snd_pcm_substream *substream, + struct lsm_params_info_v2 *p_info) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct lsm_priv *prtd = runtime->private_data; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_lsm_poll_enable poll_enable; + int rc = 0; + + if (p_info->param_size != sizeof(poll_enable)) { + dev_err(rtd->dev, + "%s: Invalid param_size %d\n", + __func__, p_info->param_size); + rc = -EINVAL; + goto done; + } + + if (copy_from_user(&poll_enable, p_info->param_data, + sizeof(poll_enable))) { + dev_err(rtd->dev, + "%s: copy_from_user failed, size = %zd\n", + __func__, sizeof(poll_enable)); + rc = -EFAULT; + goto done; + } + + if (prtd->lsm_client->poll_enable == poll_enable.poll_en) { + dev_dbg(rtd->dev, + "%s: Polling for session %d already %s\n", + __func__, prtd->lsm_client->session, + (poll_enable.poll_en ? "enabled" : "disabled")); + rc = 0; + goto done; + } + + rc = q6lsm_set_one_param(prtd->lsm_client, p_info, + &poll_enable, LSM_POLLING_ENABLE); + if (!rc) { + prtd->lsm_client->poll_enable = poll_enable.poll_en; + } else { + dev_err(rtd->dev, + "%s: Failed to set poll enable, err = %d\n", + __func__, rc); + } +done: + return rc; +} + +static int msm_lsm_set_det_event_type(struct snd_pcm_substream *substream, + struct lsm_params_info_v2 *p_info) +{ + struct snd_lsm_det_event_type det_event_type; + struct snd_pcm_runtime *runtime = substream->runtime; + struct lsm_priv *prtd = runtime->private_data; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + int rc = 0; + + if (p_info->param_size != sizeof(det_event_type)) { + dev_err(rtd->dev, + "%s: Invalid param_size %d\n", + __func__, p_info->param_size); + rc = -EINVAL; + goto done; + } + + if (copy_from_user(&det_event_type, p_info->param_data, + sizeof(det_event_type))) { + dev_err(rtd->dev, + "%s: copy_from_user failed, size = %zd\n", + __func__, sizeof(det_event_type)); + rc = -EFAULT; + goto done; + } + + rc = q6lsm_set_one_param(prtd->lsm_client, p_info, + &det_event_type, LSM_DET_EVENT_TYPE); + if (!rc) + prtd->lsm_client->event_type = det_event_type.event_type; + else + dev_err(rtd->dev, + "%s: Failed to set detection event type %s, err = %d\n", + __func__, (det_event_type.event_type ? + "LSM_DET_EVENT_TYPE_GENERIC" : + "LSM_DET_EVENT_TYPE_LEGACY"), rc); +done: + return rc; +} + +static int msm_lsm_process_params(struct snd_pcm_substream *substream, + struct lsm_params_info_v2 *p_info) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct lsm_priv *prtd = runtime->private_data; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + int rc = 0; + + dev_dbg(rtd->dev, + "%s: mid=0x%x, pid=0x%x, iid=0x%x, stage_idx=%d, size=0x%x, type=%d\n", + __func__, p_info->module_id, p_info->param_id, p_info->instance_id, + p_info->stage_idx, p_info->param_size, p_info->param_type); + + if (!prtd->lsm_client || + prtd->lsm_client->num_stages <= p_info->stage_idx) { + dev_err(rtd->dev, + "%s: invalid stage_idx(%d) for client(%p) having num_stages(%d)\n", + __func__, p_info->stage_idx, prtd->lsm_client, + prtd->lsm_client ? prtd->lsm_client->num_stages : 0); + return -EINVAL; + } + + switch (p_info->param_type) { + case LSM_ENDPOINT_DETECT_THRESHOLD: + rc = msm_lsm_set_epd(substream, p_info); + break; + case LSM_OPERATION_MODE: + rc = msm_lsm_set_mode(substream, p_info); + break; + case LSM_GAIN: + rc = msm_lsm_set_gain(substream, p_info); + break; + case LSM_MIN_CONFIDENCE_LEVELS: + rc = msm_lsm_set_conf(substream, p_info); + break; + case LSM_REG_SND_MODEL: + rc = msm_lsm_reg_model(substream, p_info); + break; + case LSM_DEREG_SND_MODEL: + rc = msm_lsm_dereg_model(substream, p_info); + break; + case LSM_CUSTOM_PARAMS: + rc = msm_lsm_set_custom(substream, p_info); + break; + case LSM_POLLING_ENABLE: + rc = msm_lsm_set_poll_enable(substream, p_info); + break; + case LSM_DET_EVENT_TYPE: + rc = msm_lsm_set_det_event_type(substream, p_info); + break; + case LSM_LAB_CONTROL: + rc = msm_lsm_set_lab_control(substream, p_info); + break; + default: + dev_err(rtd->dev, + "%s: Invalid param_type %d\n", + __func__, p_info->param_type); + rc = -EINVAL; + break; + } + if (rc) { + pr_err("%s: set_param fail for param_type %d\n", + __func__, p_info->param_type); + } + + return rc; +} + +static int msm_lsm_start_lab_buffer(struct lsm_priv *prtd, uint16_t status) +{ + struct lsm_client *lsm_client = prtd->lsm_client; + int rc = 0; + + if (lsm_client && lsm_client->lab_enable && + !lsm_client->lab_started && + status == LSM_VOICE_WAKEUP_STATUS_DETECTED) { + atomic_set(&prtd->read_abort, 0); + atomic_set(&prtd->buf_count, 0); + prtd->appl_cnt = 0; + prtd->dma_write = 0; + prtd->xrun_count = 0; + prtd->xrun_index = 0; + + rc = msm_lsm_queue_lab_buffer(prtd, 0); + if (rc) + pr_err("%s: Queue buffer failed for lab rc = %d\n", + __func__, rc); + else + prtd->lsm_client->lab_started = true; + } + + return rc; +} + +static int msm_lsm_ioctl_shared(struct snd_pcm_substream *substream, + unsigned int cmd, void *arg) +{ + struct snd_soc_pcm_runtime *rtd; + unsigned long flags; + int ret; + struct snd_lsm_sound_model_v2 snd_model_v2; + struct snd_lsm_session_data session_data; + struct snd_lsm_session_data_v2 ses_data_v2 = {0}; + int rc = 0, stage_idx; + int xchg = 0; + struct snd_pcm_runtime *runtime; + struct lsm_priv *prtd; + struct snd_lsm_detection_params det_params; + uint8_t *confidence_level = NULL; + uint32_t max_detection_stages_supported = LSM_MAX_STAGES_PER_SESSION; + + if (!substream || !substream->private_data) { + pr_err("%s: Invalid %s\n", __func__, + (!substream) ? "substream" : "private_data"); + return -EINVAL; + } + + runtime = substream->runtime; + prtd = runtime->private_data; + rtd = substream->private_data; + + switch (cmd) { + case SNDRV_LSM_SET_SESSION_DATA: + case SNDRV_LSM_SET_SESSION_DATA_V2: + + if (cmd == SNDRV_LSM_SET_SESSION_DATA) { + dev_dbg(rtd->dev, "%s: set session data\n", __func__); + rc = copy_from_user(&session_data, arg, sizeof(session_data)); + if (!rc) { + ses_data_v2.app_id = session_data.app_id; + ses_data_v2.num_stages = 1; + } + } else { + dev_dbg(rtd->dev, "%s: set session data_v2\n", __func__); + rc = copy_from_user(&ses_data_v2, arg, sizeof(ses_data_v2)); + } + if (rc) { + dev_err(rtd->dev, "%s: %s: copy_from_user failed\n", + __func__, "LSM_SET_SESSION_DATA(_V2)"); + return -EFAULT; + } + + if (ses_data_v2.app_id != LSM_VOICE_WAKEUP_APP_ID_V2) { + dev_err(rtd->dev, + "%s:Invalid App id %d for Listen client\n", + __func__, session_data.app_id); + rc = -EINVAL; + break; + } + + /* + * Before validating num_stages from user argument. + * Check ADSP support for multi-stage session, + * and reset max_detection_stages_supported to "1" if required. + */ + if (!q6lsm_adsp_supports_multi_stage_detection()) { + dev_dbg(rtd->dev, + "%s: multi-stage session not supported by adsp\n", __func__); + max_detection_stages_supported = 1; + } + + if (ses_data_v2.num_stages <= 0 || + ses_data_v2.num_stages > max_detection_stages_supported) { + dev_err(rtd->dev, + "%s: Unsupported number of stages req(%d)/max(%d)\n", + __func__, ses_data_v2.num_stages, + max_detection_stages_supported); + rc = -EINVAL; + break; + } + + prtd->lsm_client->app_id = ses_data_v2.app_id; + prtd->lsm_client->num_stages = ses_data_v2.num_stages; + for (stage_idx = LSM_STAGE_INDEX_FIRST; + stage_idx < ses_data_v2.num_stages; stage_idx++) { + prtd->lsm_client->stage_cfg[stage_idx].app_type = + ses_data_v2.stage_info[stage_idx].app_type; + prtd->lsm_client->stage_cfg[stage_idx].lpi_enable = + ses_data_v2.stage_info[stage_idx].lpi_enable; + } + + ret = q6lsm_open(prtd->lsm_client, ses_data_v2.app_id); + if (ret < 0) { + dev_err(rtd->dev, + "%s: lsm open failed, %d\n", + __func__, ret); + return ret; + } + prtd->lsm_client->opened = true; + dev_dbg(rtd->dev, "%s: Session_ID = %d, APP ID = %d, Num stages %d\n", + __func__, + prtd->lsm_client->session, + prtd->lsm_client->app_id, + prtd->lsm_client->num_stages); + break; + case SNDRV_LSM_REG_SND_MODEL_V2: { + /* + * With multi-stage support sm buff allocation/free usage param info + * to check stage index for which this sound model is being set, and + * to check whether sm data is sent using set param command or not. + * Hence, set param ids to '0' to indicate allocation is for legacy + * reg_sm cmd, where buffer for param header need not be allocated, + * also set stage index to LSM_STAGE_INDEX_FIRST. + */ + struct lsm_params_info_v2 p_info = {0}; + p_info.stage_idx = LSM_STAGE_INDEX_FIRST; + + dev_dbg(rtd->dev, "%s: Registering sound model V2\n", + __func__); + memcpy(&snd_model_v2, arg, + sizeof(struct snd_lsm_sound_model_v2)); + if (snd_model_v2.num_confidence_levels > + MAX_NUM_CONFIDENCE) { + dev_err(rtd->dev, + "%s: Invalid conf_levels = %d, maximum allowed = %d\n", + __func__, snd_model_v2.num_confidence_levels, + MAX_NUM_CONFIDENCE); + rc = -EINVAL; + break; + } + rc = q6lsm_snd_model_buf_alloc(prtd->lsm_client, + snd_model_v2.data_size, &p_info); + if (rc) { + dev_err(rtd->dev, + "%s: q6lsm buffer alloc failed V2, size %d\n", + __func__, snd_model_v2.data_size); + break; + } + if (copy_from_user( + prtd->lsm_client->stage_cfg[p_info.stage_idx].sound_model.data, + snd_model_v2.data, snd_model_v2.data_size)) { + dev_err(rtd->dev, + "%s: copy from user data failed\n" + "data %pK size %d\n", __func__, + snd_model_v2.data, snd_model_v2.data_size); + q6lsm_snd_model_buf_free(prtd->lsm_client, &p_info); + rc = -EFAULT; + break; + } + + dev_dbg(rtd->dev, "SND Model Magic no byte[0] %x,\n" + "byte[1] %x, byte[2] %x byte[3] %x\n", + snd_model_v2.data[0], snd_model_v2.data[1], + snd_model_v2.data[2], snd_model_v2.data[3]); + prtd->lsm_client->num_confidence_levels = + snd_model_v2.num_confidence_levels; + + rc = msm_lsm_get_conf_levels(prtd->lsm_client, + snd_model_v2.confidence_level); + if (rc) { + dev_err(rtd->dev, + "%s: get_conf_levels failed, err = %d\n", + __func__, rc); + break; + } + + rc = q6lsm_register_sound_model(prtd->lsm_client, + snd_model_v2.detection_mode, + snd_model_v2.detect_failure); + if (rc < 0) { + dev_err(rtd->dev, + "%s: Register snd Model v2 failed =%d\n", + __func__, rc); + kfree(confidence_level); + q6lsm_snd_model_buf_free(prtd->lsm_client, &p_info); + } + + kfree(prtd->lsm_client->confidence_levels); + prtd->lsm_client->confidence_levels = NULL; + break; + } + case SNDRV_LSM_SET_PARAMS: + dev_dbg(rtd->dev, "%s: set_params\n", __func__); + memcpy(&det_params, arg, + sizeof(det_params)); + if (det_params.num_confidence_levels > + MAX_NUM_CONFIDENCE) { + rc = -EINVAL; + break; + } + + prtd->lsm_client->num_confidence_levels = + det_params.num_confidence_levels; + + rc = msm_lsm_get_conf_levels(prtd->lsm_client, + det_params.conf_level); + if (rc) { + dev_err(rtd->dev, + "%s: Failed to get conf_levels, err = %d\n", + __func__, rc); + break; + } + + rc = q6lsm_set_data(prtd->lsm_client, + det_params.detect_mode, + det_params.detect_failure); + if (rc) + dev_err(rtd->dev, + "%s: Failed to set params, err = %d\n", + __func__, rc); + + kfree(prtd->lsm_client->confidence_levels); + prtd->lsm_client->confidence_levels = NULL; + + break; + + case SNDRV_LSM_DEREG_SND_MODEL: + dev_dbg(rtd->dev, "%s: Deregistering sound model\n", + __func__); + rc = q6lsm_deregister_sound_model(prtd->lsm_client); + if (rc) + dev_err(rtd->dev, + "%s: Sound model de-register failed, err = %d\n", + __func__, rc); + break; + + case SNDRV_LSM_EVENT_STATUS: + case SNDRV_LSM_EVENT_STATUS_V3: { + uint32_t ts_lsw, ts_msw; + uint16_t status = 0, payload_size = 0; + + dev_dbg(rtd->dev, "%s: Get event status\n", __func__); + atomic_set(&prtd->event_wait_stop, 0); + + /* + * Release the api lock before wait to allow + * other IOCTLs to be invoked while waiting + * for event + */ + mutex_unlock(&prtd->lsm_api_lock); + rc = wait_event_freezable(prtd->event_wait, + (cmpxchg(&prtd->event_avail, 1, 0) || + (xchg = atomic_cmpxchg(&prtd->event_wait_stop, + 1, 0)))); + mutex_lock(&prtd->lsm_api_lock); + dev_dbg(rtd->dev, "%s: wait_event_freezable %d event_wait_stop %d\n", + __func__, rc, xchg); + if (!rc && !xchg) { + dev_dbg(rtd->dev, "%s: New event available %ld\n", + __func__, prtd->event_avail); + spin_lock_irqsave(&prtd->event_lock, flags); + + if (prtd->event_status) { + payload_size = prtd->event_status->payload_size; + ts_lsw = prtd->event_status->timestamp_lsw; + ts_msw = prtd->event_status->timestamp_msw; + status = prtd->event_status->status; + spin_unlock_irqrestore(&prtd->event_lock, + flags); + } else { + spin_unlock_irqrestore(&prtd->event_lock, + flags); + rc = -EINVAL; + dev_err(rtd->dev, + "%s: prtd->event_status is NULL\n", + __func__); + break; + } + + if (cmd == SNDRV_LSM_EVENT_STATUS) { + struct snd_lsm_event_status *user = arg; + + if (user->payload_size < payload_size) { + dev_dbg(rtd->dev, + "%s: provided %d bytes isn't enough, needs %d bytes\n", + __func__, user->payload_size, + payload_size); + rc = -ENOMEM; + } else { + user->status = status; + user->payload_size = payload_size; + memcpy(user->payload, + prtd->event_status->payload, + payload_size); + } + } else { + struct snd_lsm_event_status_v3 *user_v3 = arg; + + if (user_v3->payload_size < payload_size) { + dev_dbg(rtd->dev, + "%s: provided %d bytes isn't enough, needs %d bytes\n", + __func__, user_v3->payload_size, + payload_size); + rc = -ENOMEM; + } else { + user_v3->timestamp_lsw = ts_lsw; + user_v3->timestamp_msw = ts_msw; + user_v3->status = status; + user_v3->payload_size = payload_size; + memcpy(user_v3->payload, + prtd->event_status->payload, + payload_size); + } + } + + if (!rc) + rc = msm_lsm_start_lab_buffer(prtd, status); + } else if (xchg) { + dev_dbg(rtd->dev, "%s: Wait aborted\n", __func__); + rc = 0; + } + break; + } + + case SNDRV_LSM_GENERIC_DET_EVENT: { + struct snd_lsm_event_status *user = arg; + uint16_t status = 0; + uint16_t payload_size = 0; + + dev_dbg(rtd->dev, + "%s: SNDRV_LSM_GENERIC_DET_EVENT\n", __func__); + + atomic_set(&prtd->event_wait_stop, 0); + + /* + * Release the api lock before wait to allow + * other IOCTLs to be invoked while waiting + * for event + */ + mutex_unlock(&prtd->lsm_api_lock); + rc = wait_event_freezable(prtd->event_wait, + (cmpxchg(&prtd->event_avail, 1, 0) || + (xchg = atomic_cmpxchg(&prtd->event_wait_stop, + 1, 0)))); + mutex_lock(&prtd->lsm_api_lock); + + dev_dbg(rtd->dev, "%s: wait_event_freezable %d event_wait_stop %d\n", + __func__, rc, xchg); + + if (!rc && !xchg) { + dev_dbg(rtd->dev, "%s: %s: New event available %ld\n", + __func__, "SNDRV_LSM_GENERIC_DET_EVENT", + prtd->event_avail); + + spin_lock_irqsave(&prtd->event_lock, flags); + + if (prtd->det_event) { + payload_size = prtd->det_event->payload_size; + status = prtd->det_event->status; + spin_unlock_irqrestore(&prtd->event_lock, + flags); + } else { + spin_unlock_irqrestore(&prtd->event_lock, + flags); + dev_err(rtd->dev, + "%s: %s: prtd->event_status is NULL\n", + __func__, + "SNDRV_LSM_GENERIC_DET_EVENT"); + rc = -EINVAL; + break; + } + + if (user->payload_size < payload_size) { + dev_err(rtd->dev, + "%s: provided %d bytes isn't enough, needs %d bytes\n", + __func__, user->payload_size, + payload_size); + rc = -ENOMEM; + break; + } + user->status = status; + user->payload_size = payload_size; + memcpy(user->payload, prtd->det_event->payload, + payload_size); + + rc = msm_lsm_start_lab_buffer(prtd, status); + } else if (xchg) { + dev_dbg(rtd->dev, "%s: %s: Wait aborted\n", + __func__, "SNDRV_LSM_GENERIC_DET_EVENT"); + rc = 0; + } + break; + } + + case SNDRV_LSM_ABORT_EVENT: + dev_dbg(rtd->dev, "%s: Aborting event status wait\n", + __func__); + atomic_set(&prtd->event_wait_stop, 1); + wake_up(&prtd->event_wait); + break; + + case SNDRV_LSM_START: + dev_dbg(rtd->dev, "%s: Starting LSM client session\n", + __func__); + if (!prtd->lsm_client->started) { + ret = q6lsm_start(prtd->lsm_client, true); + if (!ret) { + prtd->lsm_client->started = true; + dev_dbg(rtd->dev, "%s: LSM client session started\n", + __func__); + } + } + break; + + case SNDRV_LSM_STOP: { + dev_dbg(rtd->dev, + "%s: Stopping LSM client session\n", + __func__); + if (prtd->lsm_client->started) { + if (prtd->lsm_client->lab_enable) { + atomic_set(&prtd->read_abort, 1); + if (prtd->lsm_client->lab_started) { + ret = q6lsm_stop_lab(prtd->lsm_client); + if (ret) + dev_err(rtd->dev, + "%s: stop lab failed ret %d\n", + __func__, ret); + prtd->lsm_client->lab_started = false; + } + } + ret = q6lsm_stop(prtd->lsm_client, true); + if (!ret) + dev_dbg(rtd->dev, + "%s: LSM client session stopped %d\n", + __func__, ret); + prtd->lsm_client->started = false; + } + break; + } + case SNDRV_LSM_LAB_CONTROL: { + u32 enable = 0; + struct lsm_params_info_v2 p_info = {0}; + + if (prtd->lsm_client->num_stages > 1) { + dev_err(rtd->dev, "%s: %s: not supported for multi stage session\n", + __func__, "LSM_LAB_CONTROL"); + return -EINVAL; + } + + if (copy_from_user(&enable, arg, sizeof(enable))) { + dev_err(rtd->dev, "%s: %s: copy_frm_user failed\n", + __func__, "LSM_LAB_CONTROL"); + return -EFAULT; + } + + dev_dbg(rtd->dev, "%s: ioctl %s, enable = %d\n", + __func__, "SNDRV_LSM_LAB_CONTROL", enable); + + if (prtd->lsm_client->started) { + dev_err(rtd->dev, "%s: ioctl %s issued after start", + __func__, "SNDRV_LSM_LAB_CONTROL"); + rc = -EINVAL; + break; + } + + /* + * With multi-stage support lab control needs to set param info + * specifying stage index for which this lab control is issued, + * along with values of module/instance ids applicable for the stage. + * Hence, set param info with default lab module/instance ids, and + * set stage index to LSM_STAGE_INDEX_FIRST. + */ + p_info.param_type = LSM_LAB_CONTROL; + p_info.module_id = LSM_MODULE_ID_LAB; + p_info.instance_id = INSTANCE_ID_0; + p_info.stage_idx = LSM_STAGE_INDEX_FIRST; + p_info.param_size = 0; + rc = msm_lsm_check_and_set_lab_controls(substream, enable, &p_info); + break; + } + case SNDRV_LSM_STOP_LAB: + dev_dbg(rtd->dev, "%s: stopping LAB\n", __func__); + if (prtd->lsm_client->lab_enable && + prtd->lsm_client->lab_started) { + atomic_set(&prtd->read_abort, 1); + rc = q6lsm_stop_lab(prtd->lsm_client); + if (rc) + dev_err(rtd->dev, + "%s: Lab stop failed for session %d rc %d\n", + __func__, + prtd->lsm_client->session, rc); + prtd->lsm_client->lab_started = false; + } + break; + + case SNDRV_LSM_SET_PORT: + dev_dbg(rtd->dev, "%s: set LSM port\n", __func__); + rc = q6lsm_set_port_connected(prtd->lsm_client); + break; + + case SNDRV_LSM_SET_FWK_MODE_CONFIG: { + u32 mode; + + if (copy_from_user(&mode, arg, sizeof(mode))) { + dev_err(rtd->dev, "%s: %s: copy_frm_user failed\n", + __func__, "LSM_SET_FWK_MODE_CONFIG"); + return -EFAULT; + } + + dev_dbg(rtd->dev, "%s: ioctl %s, enable = %d\n", + __func__, "SNDRV_LSM_SET_FWK_MODE_CONFIG", mode); + if (prtd->lsm_client->event_mode == mode) { + dev_dbg(rtd->dev, + "%s: mode for %d already set to %d\n", + __func__, prtd->lsm_client->session, mode); + rc = 0; + } else { + dev_dbg(rtd->dev, "%s: Event mode = %d\n", + __func__, mode); + rc = q6lsm_set_fwk_mode_cfg(prtd->lsm_client, mode); + if (!rc) + prtd->lsm_client->event_mode = mode; + else + dev_err(rtd->dev, + "%s: set event mode failed %d\n", + __func__, rc); + } + break; + } + case SNDRV_LSM_SET_INPUT_HW_PARAMS: { + struct lsm_hw_params *in_params; + struct snd_lsm_input_hw_params params; + + if (copy_from_user(¶ms, arg, sizeof(params))) { + dev_err(rtd->dev, "%s: %s: copy_from_user failed\n", + __func__, "LSM_SET_INPUT_HW_PARAMS"); + return -EFAULT; + } + + in_params = &prtd->lsm_client->in_hw_params; + in_params->sample_rate = params.sample_rate; + in_params->sample_size = params.bit_width; + in_params->num_chs = params.num_channels; + + break; + } + + default: + dev_dbg(rtd->dev, + "%s: Falling into default snd_lib_ioctl cmd 0x%x\n", + __func__, cmd); + rc = snd_pcm_lib_ioctl(substream, cmd, arg); + break; + } + + if (!rc) + dev_dbg(rtd->dev, "%s: leave (%d)\n", + __func__, rc); + else + dev_err(rtd->dev, "%s: cmd 0x%x failed %d\n", + __func__, cmd, rc); + + return rc; +} + +static int msm_lsm_check_event_type(struct lsm_client *lsm_client, + unsigned int cmd) +{ + int err = 0; + uint32_t event_type = lsm_client->event_type; + + if (cmd == SNDRV_LSM_EVENT_STATUS && + event_type != LSM_DET_EVENT_TYPE_LEGACY) { + pr_err("%s: %s: Invalid event request\n", + __func__, "SNDRV_LSM_EVENT_STATUS"); + err = -EINVAL; + } else if (cmd == SNDRV_LSM_GENERIC_DET_EVENT && + event_type != LSM_DET_EVENT_TYPE_GENERIC) { + pr_err("%s: %s: Invalid event request\n", + __func__, "SNDRV_LSM_GENERIC_DET_EVENT"); + err = -EINVAL; + } + + return err; +} + +#ifdef CONFIG_COMPAT + +struct snd_lsm_event_status32 { + u16 status; + u16 payload_size; + u8 payload[0]; +}; + +struct snd_lsm_event_status_v3_32 { + u32 timestamp_lsw; + u32 timestamp_msw; + u16 status; + u16 payload_size; + u8 payload[0]; +}; + +struct snd_lsm_sound_model_v2_32 { + compat_uptr_t data; + compat_uptr_t confidence_level; + u32 data_size; + enum lsm_detection_mode detection_mode; + u8 num_confidence_levels; + bool detect_failure; +}; + +struct snd_lsm_detection_params_32 { + compat_uptr_t conf_level; + enum lsm_detection_mode detect_mode; + u8 num_confidence_levels; + bool detect_failure; +}; + +struct lsm_params_info_32 { + u32 module_id; + u32 param_id; + u32 param_size; + compat_uptr_t param_data; + uint32_t param_type; +}; + +struct lsm_params_info_v2_32 { + u32 module_id; + u32 param_id; + u32 param_size; + compat_uptr_t param_data; + uint32_t param_type; + u16 instance_id; + u16 stage_idx; +}; + +struct snd_lsm_module_params_32 { + compat_uptr_t params; + u32 num_params; + u32 data_size; +}; + +enum { + SNDRV_LSM_REG_SND_MODEL_V2_32 = + _IOW('U', 0x07, struct snd_lsm_sound_model_v2_32), + SNDRV_LSM_SET_PARAMS_32 = + _IOW('U', 0x0A, struct snd_lsm_detection_params_32), + SNDRV_LSM_SET_MODULE_PARAMS_32 = + _IOW('U', 0x0B, struct snd_lsm_module_params_32), + SNDRV_LSM_EVENT_STATUS_V3_32 = + _IOW('U', 0x0F, struct snd_lsm_event_status_v3_32), + SNDRV_LSM_SET_MODULE_PARAMS_V2_32 = + _IOW('U', 0x13, struct snd_lsm_module_params_32), +}; + +static int msm_lsm_ioctl_compat(struct snd_pcm_substream *substream, + unsigned int cmd, void __user *arg) +{ + struct snd_pcm_runtime *runtime; + struct lsm_priv *prtd; + struct snd_soc_pcm_runtime *rtd; + int err = 0; + u32 size = 0; + + if (PCM_RUNTIME_CHECK(substream)) + return -ENXIO; + + if (!substream || !substream->private_data) { + pr_err("%s: Invalid %s\n", __func__, + (!substream) ? "substream" : "private_data"); + return -EINVAL; + } + runtime = substream->runtime; + rtd = substream->private_data; + prtd = runtime->private_data; + + mutex_lock(&prtd->lsm_api_lock); + + switch (cmd) { + case SNDRV_LSM_EVENT_STATUS: + case SNDRV_LSM_GENERIC_DET_EVENT: { + struct snd_lsm_event_status userarg32, *user32 = NULL; + struct snd_lsm_event_status *user = NULL; + + dev_dbg(rtd->dev, + "%s: %s\n", __func__, + (cmd == SNDRV_LSM_EVENT_STATUS) ? + "SNDRV_LSM_EVENT_STATUS" : + "SNDRV_LSM_GENERIC_DET_EVENT"); + + err = msm_lsm_check_event_type(prtd->lsm_client, cmd); + if (err) + goto done; + + if (copy_from_user(&userarg32, arg, sizeof(userarg32))) { + dev_err(rtd->dev, "%s: %s: Failed to copy from user\n", + __func__, (cmd == SNDRV_LSM_EVENT_STATUS) ? + "SNDRV_LSM_EVENT_STATUS" : + "SNDRV_LSM_GENERIC_DET_EVENT"); + err = -EFAULT; + goto done; + } + + if (userarg32.payload_size > + LISTEN_MAX_STATUS_PAYLOAD_SIZE) { + dev_err(rtd->dev, + "%s: payload_size %d is invalid, max allowed = %d\n", + __func__, userarg32.payload_size, + LISTEN_MAX_STATUS_PAYLOAD_SIZE); + err = -EINVAL; + goto done; + } + + size = sizeof(*user) + userarg32.payload_size; + user = kzalloc(size, GFP_KERNEL); + if (!user) { + err = -ENOMEM; + goto done; + } + + user->payload_size = userarg32.payload_size; + err = msm_lsm_ioctl_shared(substream, cmd, user); + if (err) { + dev_err(rtd->dev, + "%s: msm_lsm_ioctl_shared() failed, err = %d", + __func__, err); + kfree(user); + goto done; + } + + /* Update size with actual payload size */ + size = sizeof(userarg32) + user->payload_size; + if (!access_ok(VERIFY_WRITE, arg, size)) { + dev_err(rtd->dev, + "%s: Failed to verify write, size = %d\n", + __func__, size); + err = -EFAULT; + kfree(user); + goto done; + } + + user32 = kzalloc(size, GFP_KERNEL); + if (!user32) { + err = -ENOMEM; + kfree(user); + goto done; + } + user32->status = user->status; + user32->payload_size = user->payload_size; + memcpy(user32->payload, user->payload, + user32->payload_size); + + if (copy_to_user(arg, user32, size)) { + dev_err(rtd->dev, + "%s: Failed to copy payload to user, size = %d", + __func__, size); + err = -EFAULT; + } + kfree(user); + kfree(user32); + break; + } + + case SNDRV_LSM_EVENT_STATUS_V3_32: { + struct snd_lsm_event_status_v3_32 userarg32, *user32 = NULL; + struct snd_lsm_event_status_v3 *user = NULL; + + if (prtd->lsm_client->event_type != + LSM_DET_EVENT_TYPE_LEGACY) { + dev_err(rtd->dev, + "%s: %s: Invalid event request\n", + __func__, "SNDRV_LSM_EVENT_STATUS_V3_32"); + err = -EINVAL; + goto done; + } + + if (copy_from_user(&userarg32, arg, sizeof(userarg32))) { + dev_err(rtd->dev, "%s: err copyuser ioctl %s\n", + __func__, "SNDRV_LSM_EVENT_STATUS_V3_32"); + err = -EFAULT; + goto done; + } + + if (userarg32.payload_size > + LISTEN_MAX_STATUS_PAYLOAD_SIZE) { + pr_err("%s: payload_size %d is invalid, max allowed = %d\n", + __func__, userarg32.payload_size, + LISTEN_MAX_STATUS_PAYLOAD_SIZE); + err = -EINVAL; + goto done; + } + + size = sizeof(*user) + userarg32.payload_size; + user = kzalloc(size, GFP_KERNEL); + if (!user) { + dev_err(rtd->dev, + "%s: Allocation failed event status size %d\n", + __func__, size); + err = -ENOMEM; + goto done; + } + cmd = SNDRV_LSM_EVENT_STATUS_V3; + user->payload_size = userarg32.payload_size; + err = msm_lsm_ioctl_shared(substream, cmd, user); + + /* Update size with actual payload size */ + size = sizeof(userarg32) + user->payload_size; + if (!err && !access_ok(VERIFY_WRITE, arg, size)) { + dev_err(rtd->dev, + "%s: write verify failed size %d\n", + __func__, size); + err = -EFAULT; + } + if (!err) { + user32 = kzalloc(size, GFP_KERNEL); + if (!user32) { + dev_err(rtd->dev, + "%s: Allocation event user status size %d\n", + __func__, size); + err = -EFAULT; + } else { + user32->timestamp_lsw = user->timestamp_lsw; + user32->timestamp_msw = user->timestamp_msw; + user32->status = user->status; + user32->payload_size = user->payload_size; + memcpy(user32->payload, + user->payload, user32->payload_size); + } + } + if (!err && (copy_to_user(arg, user32, size))) { + dev_err(rtd->dev, "%s: failed to copy payload %d", + __func__, size); + err = -EFAULT; + } + kfree(user); + kfree(user32); + if (err) + dev_err(rtd->dev, "%s: lsmevent failed %d", + __func__, err); + break; + } + + case SNDRV_LSM_REG_SND_MODEL_V2_32: { + struct snd_lsm_sound_model_v2_32 snd_modelv232; + struct snd_lsm_sound_model_v2 snd_modelv2; + + if (prtd->lsm_client->use_topology) { + dev_err(rtd->dev, + "%s: %s: not supported if using topology\n", + __func__, "REG_SND_MODEL_V2"); + err = -EINVAL; + goto done; + } + + if (copy_from_user(&snd_modelv232, arg, + sizeof(snd_modelv232))) { + err = -EFAULT; + dev_err(rtd->dev, + "%s: copy user failed, size %zd %s\n", + __func__, + sizeof(struct snd_lsm_sound_model_v2_32), + "SNDRV_LSM_REG_SND_MODEL_V2_32"); + } else { + snd_modelv2.confidence_level = + compat_ptr(snd_modelv232.confidence_level); + snd_modelv2.data = compat_ptr(snd_modelv232.data); + snd_modelv2.data_size = snd_modelv232.data_size; + snd_modelv2.detect_failure = + snd_modelv232.detect_failure; + snd_modelv2.detection_mode = + snd_modelv232.detection_mode; + snd_modelv2.num_confidence_levels = + snd_modelv232.num_confidence_levels; + cmd = SNDRV_LSM_REG_SND_MODEL_V2; + err = msm_lsm_ioctl_shared(substream, cmd, + &snd_modelv2); + if (err) + dev_err(rtd->dev, + "%s: ioctl %s failed\n", __func__, + "SNDDRV_LSM_REG_SND_MODEL_V2_32"); + } + break; + } + + case SNDRV_LSM_SET_PARAMS_32:{ + struct snd_lsm_detection_params_32 det_params32; + struct snd_lsm_detection_params det_params; + + if (prtd->lsm_client->use_topology) { + dev_err(rtd->dev, + "%s: %s: not supported if using topology\n", + __func__, "SET_PARAMS_32"); + err = -EINVAL; + } + + if (copy_from_user(&det_params32, arg, + sizeof(det_params32))) { + err = -EFAULT; + dev_err(rtd->dev, + "%s: %s: copy_from_user failed, size = %zd\n", + __func__, "SNDRV_LSM_SET_PARAMS_32", + sizeof(det_params32)); + } else { + det_params.conf_level = + compat_ptr(det_params32.conf_level); + det_params.detect_mode = + det_params32.detect_mode; + det_params.num_confidence_levels = + det_params32.num_confidence_levels; + det_params.detect_failure = + det_params32.detect_failure; + cmd = SNDRV_LSM_SET_PARAMS; + err = msm_lsm_ioctl_shared(substream, cmd, + &det_params); + if (err) + dev_err(rtd->dev, + "%s: ioctl %s failed\n", __func__, + "SNDRV_LSM_SET_PARAMS"); + } + break; + } + + case SNDRV_LSM_SET_MODULE_PARAMS_32: + case SNDRV_LSM_SET_MODULE_PARAMS_V2_32: { + struct snd_lsm_module_params_32 p_data_32; + struct snd_lsm_module_params p_data; + u8 *params32; + size_t expected_size = 0, count; + struct lsm_params_info_32 *p_info_32 = NULL; + struct lsm_params_info_v2_32 *p_info_v2_32 = NULL; + struct lsm_params_info_v2 p_info; + + if (!prtd->lsm_client->use_topology) { + dev_err(rtd->dev, + "%s: %s: not supported if not using topology\n", + __func__, "SET_MODULE_PARAMS(_V2)_32"); + err = -EINVAL; + goto done; + } + + if (copy_from_user(&p_data_32, arg, + sizeof(p_data_32))) { + dev_err(rtd->dev, + "%s: %s: copy_from_user failed, size = %zd\n", + __func__, "SET_MODULE_PARAMS(_V2)_32", + sizeof(p_data_32)); + err = -EFAULT; + goto done; + } + + p_data.params = compat_ptr(p_data_32.params); + p_data.num_params = p_data_32.num_params; + p_data.data_size = p_data_32.data_size; + + if (p_data.num_params > LSM_PARAMS_MAX) { + dev_err(rtd->dev, + "%s: %s: Invalid num_params %d\n", + __func__, "SET_MODULE_PARAMS(_V2)_32", + p_data.num_params); + err = -EINVAL; + goto done; + } + + expected_size = (cmd == SNDRV_LSM_SET_MODULE_PARAMS_32) ? + p_data.num_params * sizeof(struct lsm_params_info_32) : + p_data.num_params * sizeof(struct lsm_params_info_v2_32); + + if (p_data.data_size != expected_size) { + dev_err(rtd->dev, + "%s: %s: Invalid size %d\n", + __func__, "SET_MODULE_PARAMS(_V2)_32", + p_data.data_size); + err = -EINVAL; + goto done; + } + + params32 = kzalloc(p_data.data_size, GFP_KERNEL); + if (!params32) { + err = -ENOMEM; + goto done; + } + + if (copy_from_user(params32, p_data.params, + p_data.data_size)) { + dev_err(rtd->dev, + "%s: %s: copy_from_user failed, size = %d\n", + __func__, "params32", p_data.data_size); + kfree(params32); + err = -EFAULT; + goto done; + } + + if (cmd == SNDRV_LSM_SET_MODULE_PARAMS_32) + p_info_32 = (struct lsm_params_info_32 *) params32; + else + p_info_v2_32 = (struct lsm_params_info_v2_32 *) params32; + + for (count = 0; count < p_data.num_params; count++) { + if (cmd == SNDRV_LSM_SET_MODULE_PARAMS_32) { + p_info.module_id = p_info_32->module_id; + p_info.param_id = p_info_32->param_id; + p_info.param_size = p_info_32->param_size; + p_info.param_data = compat_ptr(p_info_32->param_data); + p_info.param_type = p_info_32->param_type; + + p_info.instance_id = INSTANCE_ID_0; + p_info.stage_idx = LSM_STAGE_INDEX_FIRST; + + p_info_32++; + } else { + p_info.module_id = p_info_v2_32->module_id; + p_info.param_id = p_info_v2_32->param_id; + p_info.param_size = p_info_v2_32->param_size; + p_info.param_data = compat_ptr(p_info_v2_32->param_data); + p_info.param_type = p_info_v2_32->param_type; + + p_info.instance_id = p_info_v2_32->instance_id; + p_info.stage_idx = p_info_v2_32->stage_idx; + + p_info_v2_32++; + } + + err = msm_lsm_process_params(substream, &p_info); + if (err) + dev_err(rtd->dev, + "%s: Failed to process param, type%d stage=%d err=%d\n", + __func__, p_info.param_type, p_info.stage_idx, err); + } + + kfree(params32); + break; + } + case SNDRV_LSM_REG_SND_MODEL_V2: + case SNDRV_LSM_SET_PARAMS: + case SNDRV_LSM_SET_MODULE_PARAMS: + case SNDRV_LSM_SET_MODULE_PARAMS_V2: + /* + * In ideal cases, the compat_ioctl should never be called + * with the above unlocked ioctl commands. Print error + * and return error if it does. + */ + dev_err(rtd->dev, + "%s: Invalid cmd for compat_ioctl\n", + __func__); + err = -EINVAL; + break; + default: + err = msm_lsm_ioctl_shared(substream, cmd, arg); + break; + } +done: + mutex_unlock(&prtd->lsm_api_lock); + return err; +} +#else +#define msm_lsm_ioctl_compat NULL +#endif + +static int msm_lsm_ioctl(struct snd_pcm_substream *substream, + unsigned int cmd, void __user *arg) +{ + int err = 0; + u32 size = 0; + struct snd_pcm_runtime *runtime; + struct snd_soc_pcm_runtime *rtd; + struct lsm_priv *prtd; + + if (!substream || !substream->private_data) { + pr_err("%s: Invalid %s\n", __func__, + (!substream) ? "substream" : "private_data"); + return -EINVAL; + } + runtime = substream->runtime; + prtd = runtime->private_data; + rtd = substream->private_data; + + mutex_lock(&prtd->lsm_api_lock); + switch (cmd) { + case SNDRV_LSM_REG_SND_MODEL_V2: { + struct snd_lsm_sound_model_v2 snd_model_v2; + + if (prtd->lsm_client->use_topology) { + dev_err(rtd->dev, + "%s: %s: not supported if using topology\n", + __func__, "REG_SND_MODEL_V2"); + err = -EINVAL; + goto done; + } + + if (copy_from_user(&snd_model_v2, arg, sizeof(snd_model_v2))) { + err = -EFAULT; + dev_err(rtd->dev, + "%s: copy from user failed, size %zd\n", + __func__, + sizeof(struct snd_lsm_sound_model_v2)); + } + if (!err) + err = msm_lsm_ioctl_shared(substream, cmd, + &snd_model_v2); + if (err) + dev_err(rtd->dev, + "%s REG_SND_MODEL failed err %d\n", + __func__, err); + goto done; + } + break; + case SNDRV_LSM_SET_PARAMS: { + struct snd_lsm_detection_params det_params; + + if (prtd->lsm_client->use_topology) { + dev_err(rtd->dev, + "%s: %s: not supported if using topology\n", + __func__, "SET_PARAMS"); + err = -EINVAL; + goto done; + } + + pr_debug("%s: SNDRV_LSM_SET_PARAMS\n", __func__); + + if (copy_from_user(&det_params, arg, + sizeof(det_params))) { + dev_err(rtd->dev, + "%s: %s: copy_from_user failed, size %zd\n", + __func__, "SNDRV_LSM_SET_PARAMS", + sizeof(det_params)); + err = -EFAULT; + } + + if (!err) + err = msm_lsm_ioctl_shared(substream, cmd, + &det_params); + else + dev_err(rtd->dev, + "%s: LSM_SET_PARAMS failed, err %d\n", + __func__, err); + + goto done; + } + + case SNDRV_LSM_SET_MODULE_PARAMS: + case SNDRV_LSM_SET_MODULE_PARAMS_V2: { + struct snd_lsm_module_params p_data; + struct lsm_params_info *temp_ptr_info = NULL; + struct lsm_params_info_v2 info_v2; + struct lsm_params_info_v2 *ptr_info_v2 = NULL, *temp_ptr_info_v2 = NULL; + size_t p_size = 0, count; + u8 *params; + + if (!prtd->lsm_client->use_topology) { + dev_err(rtd->dev, + "%s: %s: not supported if not using topology\n", + __func__, "SET_MODULE_PARAMS(_V2)"); + err = -EINVAL; + goto done; + } + + if (copy_from_user(&p_data, arg, + sizeof(p_data))) { + dev_err(rtd->dev, + "%s: %s: copy_from_user failed, size = %zd\n", + __func__, "p_data", sizeof(p_data)); + err = -EFAULT; + goto done; + } + + if (p_data.num_params > LSM_PARAMS_MAX) { + dev_err(rtd->dev, + "%s: %s: Invalid num_params %d\n", + __func__, "SET_MODULE_PARAMS(_V2)", + p_data.num_params); + err = -EINVAL; + goto done; + } + + if (cmd == SNDRV_LSM_SET_MODULE_PARAMS) + p_size = p_data.num_params * sizeof(struct lsm_params_info); + else + p_size = p_data.num_params * sizeof(struct lsm_params_info_v2); + + if (p_data.data_size != p_size) { + dev_err(rtd->dev, + "%s: %s: Invalid data_size(%u) against expected(%zd)\n", + __func__, "SET_MODULE_PARAMS(_V2)", + p_data.data_size, p_size); + err = -EFAULT; + goto done; + } + + params = kzalloc(p_size, GFP_KERNEL); + if (!params) { + err = -ENOMEM; + goto done; + } + + if (copy_from_user(params, p_data.params, + p_data.data_size)) { + dev_err(rtd->dev, + "%s: %s: copy_from_user failed, size = %d\n", + __func__, "set module params", p_data.data_size); + kfree(params); + err = -EFAULT; + goto done; + } + + if (cmd == SNDRV_LSM_SET_MODULE_PARAMS) + temp_ptr_info = (struct lsm_params_info *)params; + else + temp_ptr_info_v2 = (struct lsm_params_info_v2 *)params; + + for (count = 0; count < p_data.num_params; count++) { + if (cmd == SNDRV_LSM_SET_MODULE_PARAMS) { + /* convert to V2 param info struct from legacy param info */ + info_v2.module_id = temp_ptr_info->module_id; + info_v2.param_id = temp_ptr_info->param_id; + info_v2.param_size = temp_ptr_info->param_size; + info_v2.param_data = temp_ptr_info->param_data; + info_v2.param_type = temp_ptr_info->param_type; + + info_v2.instance_id = INSTANCE_ID_0; + info_v2.stage_idx = LSM_STAGE_INDEX_FIRST; + + ptr_info_v2 = &info_v2; + temp_ptr_info++; + } else { + /* Just copy the pointer as user already provided v2 params */ + ptr_info_v2 = temp_ptr_info_v2; + temp_ptr_info_v2++; + } + err = msm_lsm_process_params(substream, ptr_info_v2); + if (err) + dev_err(rtd->dev, + "%s: Failed to process param, type%d stage=%d err=%d\n", + __func__, ptr_info_v2->param_type, + ptr_info_v2->stage_idx, err); + } + kfree(params); + break; + } + + case SNDRV_LSM_EVENT_STATUS: + case SNDRV_LSM_GENERIC_DET_EVENT: { + struct snd_lsm_event_status *user = NULL; + struct snd_lsm_event_status userarg; + + dev_dbg(rtd->dev, + "%s: %s\n", __func__, + (cmd == SNDRV_LSM_EVENT_STATUS) ? + "SNDRV_LSM_EVENT_STATUS" : + "SNDRV_LSM_GENERIC_DET_EVENT"); + + err = msm_lsm_check_event_type(prtd->lsm_client, cmd); + if (err) + goto done; + + if (copy_from_user(&userarg, arg, sizeof(userarg))) { + dev_err(rtd->dev, + "%s: %s: Copy from user failed\n", __func__, + (cmd == SNDRV_LSM_EVENT_STATUS) ? + "SNDRV_LSM_EVENT_STATUS" : + "SNDRV_LSM_GENERIC_DET_EVENT"); + err = -EFAULT; + goto done; + } + + if (userarg.payload_size > + LISTEN_MAX_STATUS_PAYLOAD_SIZE) { + dev_err(rtd->dev, + "%s: payload_size %d is invalid, max allowed = %d\n", + __func__, userarg.payload_size, + LISTEN_MAX_STATUS_PAYLOAD_SIZE); + err = -EINVAL; + goto done; + } + + size = sizeof(struct snd_lsm_event_status) + + userarg.payload_size; + user = kzalloc(size, GFP_KERNEL); + if (!user) { + err = -ENOMEM; + goto done; + } + + user->payload_size = userarg.payload_size; + err = msm_lsm_ioctl_shared(substream, cmd, user); + if (err) { + dev_err(rtd->dev, + "%s: msm_lsm_ioctl_shared() failed, err = %d", + __func__, err); + kfree(user); + goto done; + } + + /* Update size with actual payload size */ + size = sizeof(*user) + user->payload_size; + if (!access_ok(VERIFY_WRITE, arg, size)) { + dev_err(rtd->dev, + "%s: Failed to verify write, size = %d\n", + __func__, size); + err = -EFAULT; + } + if (!err && copy_to_user(arg, user, size)) { + dev_err(rtd->dev, + "%s: Failed to copy payload to user, size = %d\n", + __func__, size); + err = -EFAULT; + } + + kfree(user); + break; + } + + case SNDRV_LSM_EVENT_STATUS_V3: { + struct snd_lsm_event_status_v3 *user = NULL; + struct snd_lsm_event_status_v3 userarg; + + dev_dbg(rtd->dev, + "%s: SNDRV_LSM_EVENT_STATUS_V3\n", __func__); + + if (prtd->lsm_client->event_type != + LSM_DET_EVENT_TYPE_LEGACY) { + dev_err(rtd->dev, + "%s: %s: Invalid event request\n", + __func__, "SNDRV_LSM_EVENT_STATUS_V3"); + err = -EINVAL; + goto done; + } + + if (!arg) { + dev_err(rtd->dev, + "%s: Invalid params event_status_v3\n", + __func__); + err = -EINVAL; + goto done; + } + if (copy_from_user(&userarg, arg, sizeof(userarg))) { + dev_err(rtd->dev, + "%s: err copyuser event_status_v3\n", + __func__); + err = -EFAULT; + goto done; + } + + if (userarg.payload_size > + LISTEN_MAX_STATUS_PAYLOAD_SIZE) { + pr_err("%s: payload_size %d is invalid, max allowed = %d\n", + __func__, userarg.payload_size, + LISTEN_MAX_STATUS_PAYLOAD_SIZE); + err = -EINVAL; + goto done; + } + + size = sizeof(struct snd_lsm_event_status_v3) + + userarg.payload_size; + user = kzalloc(size, GFP_KERNEL); + if (!user) { + dev_err(rtd->dev, + "%s: Allocation failed event status size %d\n", + __func__, size); + err = -EFAULT; + goto done; + } + user->payload_size = userarg.payload_size; + err = msm_lsm_ioctl_shared(substream, cmd, user); + + /* Update size with actual payload size */ + size = sizeof(*user) + user->payload_size; + if (!err && !access_ok(VERIFY_WRITE, arg, size)) { + dev_err(rtd->dev, + "%s: write verify failed size %d\n", + __func__, size); + err = -EFAULT; + } + if (!err && (copy_to_user(arg, user, size))) { + dev_err(rtd->dev, + "%s: failed to copy payload %d", + __func__, size); + err = -EFAULT; + } + kfree(user); + if (err) + dev_err(rtd->dev, + "%s: lsm_event_v3 failed %d", __func__, err); + break; + } + + default: + err = msm_lsm_ioctl_shared(substream, cmd, arg); + break; + } +done: + mutex_unlock(&prtd->lsm_api_lock); + return err; +} + +static int msm_lsm_open(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct lsm_priv *prtd; + int ret = 0; + + pr_debug("%s\n", __func__); + prtd = kzalloc(sizeof(struct lsm_priv), GFP_KERNEL); + if (!prtd) { + pr_err("%s: Failed to allocate memory for lsm_priv\n", + __func__); + return -ENOMEM; + } + mutex_init(&prtd->lsm_api_lock); + spin_lock_init(&prtd->event_lock); + spin_lock_init(&prtd->xrun_lock); + init_waitqueue_head(&prtd->event_wait); + init_waitqueue_head(&prtd->period_wait); + prtd->substream = substream; + runtime->private_data = prtd; + runtime->hw = msm_pcm_hardware_capture; + + ret = snd_pcm_hw_constraint_list(runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + &constraints_sample_rates); + if (ret < 0) + pr_info("%s: snd_pcm_hw_constraint_list failed ret %d\n", + __func__, ret); + /* Ensure that buffer size is a multiple of period size */ + ret = snd_pcm_hw_constraint_integer(runtime, + SNDRV_PCM_HW_PARAM_PERIODS); + if (ret < 0) + pr_info("%s: snd_pcm_hw_constraint_integer failed ret %d\n", + __func__, ret); + + ret = snd_pcm_hw_constraint_minmax(runtime, + SNDRV_PCM_HW_PARAM_BUFFER_BYTES, + CAPTURE_MIN_NUM_PERIODS * CAPTURE_MIN_PERIOD_SIZE, + CAPTURE_MAX_NUM_PERIODS * CAPTURE_MAX_PERIOD_SIZE); + if (ret < 0) + pr_info("%s: constraint for buffer bytes min max ret = %d\n", + __func__, ret); + ret = snd_pcm_hw_constraint_step(runtime, 0, + SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 32); + if (ret < 0) { + pr_info("%s: constraint for period bytes step ret = %d\n", + __func__, ret); + } + ret = snd_pcm_hw_constraint_step(runtime, 0, + SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 32); + if (ret < 0) + pr_info("%s: constraint for buffer bytes step ret = %d\n", + __func__, ret); + prtd->lsm_client = q6lsm_client_alloc( + lsm_event_handler, prtd); + if (!prtd->lsm_client) { + pr_err("%s: Could not allocate memory\n", __func__); + kfree(prtd); + runtime->private_data = NULL; + return -ENOMEM; + } + prtd->lsm_client->opened = false; + prtd->lsm_client->session_state = IDLE; + prtd->lsm_client->poll_enable = true; + prtd->lsm_client->perf_mode = 0; + prtd->lsm_client->event_mode = LSM_EVENT_NON_TIME_STAMP_MODE; + prtd->lsm_client->event_type = LSM_DET_EVENT_TYPE_LEGACY; + + return 0; +} + +static int msm_lsm_send_ch_mix_config(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct lsm_priv *prtd = runtime->private_data; + struct snd_soc_pcm_runtime *rtd; + struct lsm_hw_params *in_params; + int pp_ch_cnt; + int *ch_wght_coeff; + int ret = 0, i, idx; + + /* + * The output channels from channel mixer is the input to LSM (stream) + * side and is read from in_params->num_chs. + * + * The input channels to channel mixer are the output channels from + * the device side (routing) and is obtained by reading the + * pp_ch_cnt. + * + * For LSM to be functional, only unity channel mixing is allowed. + */ + + in_params = &prtd->lsm_client->in_hw_params; + rtd = prtd->substream->private_data; + pp_ch_cnt = msm_pcm_routing_get_pp_ch_cnt(rtd->dai_link->id, + SESSION_TYPE_TX); + if (pp_ch_cnt < 0 || + pp_ch_cnt > LSM_V3P0_MAX_NUM_CHANNELS || + in_params->num_chs > LSM_V3P0_MAX_NUM_CHANNELS) { + dev_err(rtd->dev, + "%s: invalid ch cnt, pp_ch_cnt %d in_ch_cnt %d\n", + __func__, pp_ch_cnt, in_params->num_chs); + return -EINVAL; + } + + if (!pp_ch_cnt || + (pp_ch_cnt == in_params->num_chs)) { + dev_dbg(rtd->dev, + "%s: Skip ch mixing, pp_ch_cnt %d in_ch_cnt %d\n", + __func__, pp_ch_cnt, in_params->num_chs); + return 0; + } + + ch_wght_coeff = kzalloc(in_params->num_chs * pp_ch_cnt * sizeof(int), + GFP_KERNEL); + if (!ch_wght_coeff) + return -ENOMEM; + + /* + * channel weight co-efficients is a m X n array, where + * m = number of input channels to ch mixer (pp_ch_cnt) + * n = number of output channels from ch mixer (in_params->num_chs) + */ + for (i = 0; i < in_params->num_chs; i++) { + idx = (i * pp_ch_cnt) + i; + ch_wght_coeff[idx] = 1; + } + + ret = msm_pcm_routing_send_chmix_cfg(rtd->dai_link->id, + pp_ch_cnt, in_params->num_chs, + ch_wght_coeff, + SESSION_TYPE_TX, STREAM_TYPE_LSM); + if (ret) + dev_err(rtd->dev, + "%s: Failed to configure channel mixer err %d\n", + __func__, ret); + + kfree(ch_wght_coeff); + + return ret; +} + +static int msm_lsm_prepare(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct lsm_priv *prtd = runtime->private_data; + struct snd_soc_pcm_runtime *rtd; + int ret = 0; + + if (!substream->private_data) { + pr_err("%s: Invalid private_data", __func__); + return -EINVAL; + } + + rtd = prtd->substream->private_data; + + if (!prtd->lsm_client) { + dev_err(rtd->dev, + "%s: LSM client data ptr is NULL\n", __func__); + return -EINVAL; + } + + if (q6lsm_set_media_fmt_v2_params(prtd->lsm_client)) + dev_dbg(rtd->dev, + "%s: failed to set lsm media fmt params\n", __func__); + + if (prtd->lsm_client->session_state == IDLE) { + ret = msm_pcm_routing_reg_phy_compr_stream( + rtd->dai_link->id, + prtd->lsm_client->perf_mode, + prtd->lsm_client->session, + SNDRV_PCM_STREAM_CAPTURE, + LISTEN); + if (ret) { + dev_err(rtd->dev, + "%s: register phy compr stream failed %d\n", + __func__, ret); + return ret; + } + + ret = msm_lsm_send_ch_mix_config(substream); + if (ret) { + msm_pcm_routing_dereg_phy_stream(rtd->dai_link->id, + SNDRV_PCM_STREAM_CAPTURE); + return ret; + } + } + + prtd->lsm_client->session_state = RUNNING; + prtd->lsm_client->started = false; + runtime->private_data = prtd; + return ret; +} + +static int msm_lsm_close(struct snd_pcm_substream *substream) +{ + unsigned long flags; + struct snd_pcm_runtime *runtime = substream->runtime; + struct lsm_priv *prtd = runtime->private_data; + struct snd_soc_pcm_runtime *rtd; + int ret = 0; + + if (!substream->private_data) { + pr_err("%s: Invalid private_data", __func__); + return -EINVAL; + } + if (!prtd || !prtd->lsm_client) { + pr_err("%s: No LSM session active\n", __func__); + return -EINVAL; + } + rtd = substream->private_data; + + dev_dbg(rtd->dev, "%s\n", __func__); + if (prtd->lsm_client->started) { + ret = q6lsm_stop(prtd->lsm_client, true); + if (ret) + dev_err(rtd->dev, + "%s: session stop failed, err = %d\n", + __func__, ret); + else + dev_dbg(rtd->dev, + "%s: LSM client session stopped %d\n", + __func__, ret); + + /* + * Go Ahead and try de-register sound model, + * even if stop failed + */ + prtd->lsm_client->started = false; + + ret = q6lsm_deregister_sound_model(prtd->lsm_client); + if (ret) + dev_err(rtd->dev, + "%s: dereg_snd_model failed, err = %d\n", + __func__, ret); + else + dev_dbg(rtd->dev, "%s: dereg_snd_model successful\n", + __func__); + } + + msm_pcm_routing_dereg_phy_stream(rtd->dai_link->id, + SNDRV_PCM_STREAM_CAPTURE); + + if (prtd->lsm_client->opened) { + q6lsm_close(prtd->lsm_client); + prtd->lsm_client->opened = false; + } + q6lsm_client_free(prtd->lsm_client); + + spin_lock_irqsave(&prtd->event_lock, flags); + kfree(prtd->event_status); + prtd->event_status = NULL; + kfree(prtd->det_event); + prtd->det_event = NULL; + spin_unlock_irqrestore(&prtd->event_lock, flags); + mutex_destroy(&prtd->lsm_api_lock); + kfree(prtd); + runtime->private_data = NULL; + + return 0; +} + +static int msm_lsm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct lsm_priv *prtd = runtime->private_data; + struct lsm_hw_params *out_hw_params = NULL; + struct lsm_hw_params *in_hw_params = NULL; + struct snd_soc_pcm_runtime *rtd; + + if (!substream->private_data) { + pr_err("%s: Invalid private_data", __func__); + return -EINVAL; + } + rtd = substream->private_data; + + if (!prtd || !params) { + dev_err(rtd->dev, + "%s: invalid params prtd %pK params %pK", + __func__, prtd, params); + return -EINVAL; + } + in_hw_params = &prtd->lsm_client->in_hw_params; + out_hw_params = &prtd->lsm_client->out_hw_params; + out_hw_params->num_chs = params_channels(params); + out_hw_params->period_count = params_periods(params); + out_hw_params->sample_rate = params_rate(params); + if (((out_hw_params->sample_rate != 16000) && + (out_hw_params->sample_rate != 48000)) || + (out_hw_params->period_count == 0)) { + dev_err(rtd->dev, + "%s: Invalid Params sample rate %d period count %d\n", + __func__, out_hw_params->sample_rate, + out_hw_params->period_count); + return -EINVAL; + } + + if (params_format(params) == SNDRV_PCM_FORMAT_S16_LE) { + out_hw_params->sample_size = 16; + } else if (params_format(params) == SNDRV_PCM_FORMAT_S24_LE) { + out_hw_params->sample_size = 24; + } else { + dev_err(rtd->dev, "%s: Invalid Format 0x%x\n", + __func__, params_format(params)); + return -EINVAL; + } + + out_hw_params->buf_sz = params_buffer_bytes(params) / + out_hw_params->period_count; + dev_dbg(rtd->dev, + "%s: channels %d sample rate %d sample size %d buffer size %d period count %d\n", + __func__, out_hw_params->num_chs, out_hw_params->sample_rate, + out_hw_params->sample_size, out_hw_params->buf_sz, + out_hw_params->period_count); + + /* + * copy the out_hw_params to in_hw_params. in_hw_params will be + * over-written with LSM_SET_INPUT_HW_PARAMS ioctl from userspace. + * If this ioctl is not set, then it is assumed that input and + * output hw params for LSM are the same. + * Currently the period_count and buf_sz are unused for input params. + */ + memcpy(in_hw_params, out_hw_params, + sizeof(struct lsm_hw_params)); + return 0; +} + +static snd_pcm_uframes_t msm_lsm_pcm_pointer( + struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct lsm_priv *prtd = runtime->private_data; + struct snd_soc_pcm_runtime *rtd; + + if (!substream->private_data) { + pr_err("%s: Invalid private_data", __func__); + return -EINVAL; + } + rtd = substream->private_data; + + if (!prtd) { + dev_err(rtd->dev, + "%s: Invalid param %pK\n", __func__, prtd); + return 0; + } + + if (prtd->dma_write >= snd_pcm_lib_buffer_bytes(substream)) + prtd->dma_write = 0; + dev_dbg(rtd->dev, + "%s: dma post = %d\n", __func__, prtd->dma_write); + return bytes_to_frames(runtime, prtd->dma_write); +} + +static int msm_lsm_pcm_copy(struct snd_pcm_substream *substream, int ch, + unsigned long hwoff, void __user *buf, unsigned long fbytes) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct lsm_priv *prtd = runtime->private_data; + char *pcm_buf = NULL; + int rc = 0, buf_index = 0; + unsigned long flags = 0; + struct snd_soc_pcm_runtime *rtd; + + if (!substream->private_data) { + pr_err("%s: Invalid private_data", __func__); + return -EINVAL; + } + rtd = substream->private_data; + + if (!prtd) { + dev_err(rtd->dev, + "%s: Invalid param %pK\n", __func__, prtd); + return -EINVAL; + } + + if (runtime->status->state == SNDRV_PCM_STATE_XRUN || + runtime->status->state == SNDRV_PCM_STATE_PREPARED) { + dev_err(rtd->dev, + "%s: runtime state incorrect %d", __func__, + runtime->status->state); + return 0; + } + rc = wait_event_timeout(prtd->period_wait, + (atomic_read(&prtd->buf_count) | + atomic_read(&prtd->read_abort)), (2 * HZ)); + if (!rc) { + dev_err(rtd->dev, + "%s: timeout for read retry\n", __func__); + return -EAGAIN; + } + if (atomic_read(&prtd->read_abort)) { + dev_err(rtd->dev, + "%s: Read abort received\n", __func__); + return -EIO; + } + prtd->appl_cnt = prtd->appl_cnt % + prtd->lsm_client->out_hw_params.period_count; + pcm_buf = prtd->lsm_client->lab_buffer[prtd->appl_cnt].data; + dev_dbg(rtd->dev, + "%s: copy the pcm data size %lu\n", + __func__, fbytes); + if (pcm_buf) { + if (copy_to_user(buf, pcm_buf, fbytes)) { + dev_err(rtd->dev, + "%s: failed to copy bytes %lu\n", + __func__, fbytes); + return -EINVAL; + } + } else { + dev_err(rtd->dev, + "%s: Invalid pcm buffer\n", __func__); + return -EINVAL; + } + prtd->appl_cnt = (prtd->appl_cnt + 1) % + prtd->lsm_client->out_hw_params.period_count; + + spin_lock_irqsave(&prtd->xrun_lock, flags); + /* Queue lab buffer here if in xrun */ + if (prtd->xrun_count > 0) { + (prtd->xrun_count)--; + buf_index = (prtd->xrun_index + 1) % + prtd->lsm_client->out_hw_params.period_count; + rc = msm_lsm_queue_lab_buffer(prtd, buf_index); + if (rc) + dev_err(rtd->dev, + "%s: error in queuing the lab buffer rc %d\n", + __func__, rc); + prtd->xrun_index = buf_index; + } + atomic_dec(&prtd->buf_count); + spin_unlock_irqrestore(&prtd->xrun_lock, flags); + + return 0; +} + +static int msm_lsm_app_type_cfg_ctl_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u64 fe_id = kcontrol->private_value; + int session_type = SESSION_TYPE_TX; + int be_id = ucontrol->value.integer.value[3]; + struct msm_pcm_stream_app_type_cfg cfg_data = {0}; + int ret = 0; + + cfg_data.app_type = ucontrol->value.integer.value[0]; + cfg_data.acdb_dev_id = ucontrol->value.integer.value[1]; + cfg_data.sample_rate = ucontrol->value.integer.value[2]; + + pr_debug("%s: fe_id- %llu session_type- %d be_id- %d app_type- %d acdb_dev_id- %d sample_rate- %d\n", + __func__, fe_id, session_type, be_id, + cfg_data.app_type, cfg_data.acdb_dev_id, cfg_data.sample_rate); + ret = msm_pcm_routing_reg_stream_app_type_cfg(fe_id, session_type, + be_id, &cfg_data); + if (ret < 0) + pr_err("%s: msm_pcm_routing_reg_stream_app_type_cfg failed returned %d\n", + __func__, ret); + + return 0; +} + +static int msm_lsm_app_type_cfg_ctl_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u64 fe_id = kcontrol->private_value; + int session_type = SESSION_TYPE_TX; + int be_id = 0; + struct msm_pcm_stream_app_type_cfg cfg_data = {0}; + int ret = 0; + + ret = msm_pcm_routing_get_stream_app_type_cfg(fe_id, session_type, + &be_id, &cfg_data); + if (ret < 0) { + pr_err("%s: msm_pcm_routing_get_stream_app_type_cfg failed returned %d\n", + __func__, ret); + goto done; + } + + ucontrol->value.integer.value[0] = cfg_data.app_type; + ucontrol->value.integer.value[1] = cfg_data.acdb_dev_id; + ucontrol->value.integer.value[2] = cfg_data.sample_rate; + ucontrol->value.integer.value[3] = be_id; + pr_debug("%s: fedai_id %llu, session_type %d, be_id %d, app_type %d, acdb_dev_id %d, sample_rate %d\n", + __func__, fe_id, session_type, be_id, + cfg_data.app_type, cfg_data.acdb_dev_id, cfg_data.sample_rate); +done: + return ret; +} + +static int msm_lsm_add_app_type_controls(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_pcm *pcm = rtd->pcm; + struct snd_pcm_usr *app_type_info; + struct snd_kcontrol *kctl; + const char *mixer_ctl_name = "Listen Stream"; + const char *deviceNo = "NN"; + const char *suffix = "App Type Cfg"; + int ctl_len, ret = 0; + + ctl_len = strlen(mixer_ctl_name) + 1 + + strlen(deviceNo) + 1 + strlen(suffix) + 1; + pr_debug("%s: Listen app type cntrl add\n", __func__); + ret = snd_pcm_add_usr_ctls(pcm, SNDRV_PCM_STREAM_CAPTURE, + NULL, 1, ctl_len, rtd->dai_link->id, + &app_type_info); + if (ret < 0) { + pr_err("%s: Listen app type cntrl add failed: %d\n", + __func__, ret); + return ret; + } + kctl = app_type_info->kctl; + snprintf(kctl->id.name, ctl_len, "%s %d %s", + mixer_ctl_name, rtd->pcm->device, suffix); + kctl->put = msm_lsm_app_type_cfg_ctl_put; + kctl->get = msm_lsm_app_type_cfg_ctl_get; + return 0; +} + +static int msm_lsm_add_controls(struct snd_soc_pcm_runtime *rtd) +{ + int ret = 0; + + ret = msm_lsm_add_app_type_controls(rtd); + if (ret) + pr_err("%s, add app type controls failed:%d\n", __func__, ret); + + return ret; +} + +static const struct snd_pcm_ops msm_lsm_ops = { + .open = msm_lsm_open, + .close = msm_lsm_close, + .ioctl = msm_lsm_ioctl, + .prepare = msm_lsm_prepare, + .compat_ioctl = msm_lsm_ioctl_compat, + .hw_params = msm_lsm_hw_params, + .copy_user = msm_lsm_pcm_copy, + .pointer = msm_lsm_pcm_pointer, +}; + +static int msm_asoc_lsm_new(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_card *card = rtd->card->snd_card; + int ret = 0; + + if (!card->dev->coherent_dma_mask) + card->dev->coherent_dma_mask = DMA_BIT_MASK(32); + + ret = msm_lsm_add_controls(rtd); + if (ret) + pr_err("%s, kctl add failed:%d\n", __func__, ret); + + return ret; +} + +static int msm_asoc_lsm_probe(struct snd_soc_platform *platform) +{ + pr_debug("enter %s\n", __func__); + + return 0; +} + +static struct snd_soc_platform_driver msm_soc_platform = { + .ops = &msm_lsm_ops, + .pcm_new = msm_asoc_lsm_new, + .probe = msm_asoc_lsm_probe, +}; + +static int msm_lsm_probe(struct platform_device *pdev) +{ + + return snd_soc_register_platform(&pdev->dev, &msm_soc_platform); +} + +static int msm_lsm_remove(struct platform_device *pdev) +{ + snd_soc_unregister_platform(&pdev->dev); + + return 0; +} + +static const struct of_device_id msm_lsm_client_dt_match[] = { + {.compatible = "qcom,msm-lsm-client" }, + { } +}; + +static struct platform_driver msm_lsm_driver = { + .driver = { + .name = "msm-lsm-client", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(msm_lsm_client_dt_match), + }, + .probe = msm_lsm_probe, + .remove = msm_lsm_remove, +}; + +int __init msm_lsm_client_init(void) +{ + return platform_driver_register(&msm_lsm_driver); +} + +void msm_lsm_client_exit(void) +{ + platform_driver_unregister(&msm_lsm_driver); +} + +MODULE_DESCRIPTION("LSM client platform driver"); +MODULE_DEVICE_TABLE(of, msm_lsm_client_dt_match); +MODULE_LICENSE("GPL v2"); diff --git a/audio-kernel/asoc/msm-pcm-afe-v2.c b/audio-kernel/asoc/msm-pcm-afe-v2.c new file mode 100644 index 000000000..d54bde62a --- /dev/null +++ b/audio-kernel/asoc/msm-pcm-afe-v2.c @@ -0,0 +1,922 @@ +/* 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 + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "msm-pcm-afe-v2.h" + +#define TIMEOUT_MS 1000 +#define MIN_PLAYBACK_PERIOD_SIZE (128 * 2) +#define MAX_PLAYBACK_PERIOD_SIZE (128 * 2 * 2 * 6) +#define MIN_PLAYBACK_NUM_PERIODS (4) +#define MAX_PLAYBACK_NUM_PERIODS (384) + +#define MIN_CAPTURE_PERIOD_SIZE (128 * 2) +#define MAX_CAPTURE_PERIOD_SIZE (192 * 2 * 2 * 8 * 4) +#define MIN_CAPTURE_NUM_PERIODS (4) +#define MAX_CAPTURE_NUM_PERIODS (384) + +static struct snd_pcm_hardware msm_afe_hardware_playback = { + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED), + .formats = SNDRV_PCM_FMTBIT_S16_LE| + SNDRV_PCM_FMTBIT_S24_LE, + .rates = (SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_48000), + .rate_min = 8000, + .rate_max = 48000, + .channels_min = 1, + .channels_max = 6, + .buffer_bytes_max = MAX_PLAYBACK_PERIOD_SIZE * + MAX_PLAYBACK_NUM_PERIODS, + .period_bytes_min = MIN_PLAYBACK_PERIOD_SIZE, + .period_bytes_max = MAX_PLAYBACK_PERIOD_SIZE, + .periods_min = MIN_PLAYBACK_NUM_PERIODS, + .periods_max = MAX_PLAYBACK_NUM_PERIODS, + .fifo_size = 0, +}; + +static struct snd_pcm_hardware msm_afe_hardware_capture = { + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED), + .formats = SNDRV_PCM_FMTBIT_S16_LE| + SNDRV_PCM_FMTBIT_S24_LE, + .rates = (SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_48000), + .rate_min = 8000, + .rate_max = 48000, + .channels_min = 1, + .channels_max = 6, + .buffer_bytes_max = MAX_CAPTURE_PERIOD_SIZE * + MAX_CAPTURE_NUM_PERIODS, + .period_bytes_min = MIN_CAPTURE_PERIOD_SIZE, + .period_bytes_max = MAX_CAPTURE_PERIOD_SIZE, + .periods_min = MIN_CAPTURE_NUM_PERIODS, + .periods_max = MAX_CAPTURE_NUM_PERIODS, + .fifo_size = 0, +}; + + +static enum hrtimer_restart afe_hrtimer_callback(struct hrtimer *hrt); +static enum hrtimer_restart afe_hrtimer_rec_callback(struct hrtimer *hrt); + +static enum hrtimer_restart afe_hrtimer_callback(struct hrtimer *hrt) +{ + struct pcm_afe_info *prtd = + container_of(hrt, struct pcm_afe_info, hrt); + struct snd_pcm_substream *substream = prtd->substream; + struct snd_pcm_runtime *runtime = substream->runtime; + u32 mem_map_handle = 0; + + mem_map_handle = afe_req_mmap_handle(prtd->audio_client); + if (!mem_map_handle) + pr_err("%s: mem_map_handle is NULL\n", __func__); + + if (prtd->start) { + pr_debug("sending frame to DSP: poll_time: %d\n", + prtd->poll_time); + if (prtd->dsp_cnt == runtime->periods) + prtd->dsp_cnt = 0; + pr_debug("%s: mem_map_handle 0x%x\n", __func__, mem_map_handle); + afe_rt_proxy_port_write( + (prtd->dma_addr + + (prtd->dsp_cnt * + snd_pcm_lib_period_bytes(prtd->substream))), mem_map_handle, + snd_pcm_lib_period_bytes(prtd->substream)); + prtd->dsp_cnt++; + hrtimer_forward_now(hrt, ns_to_ktime(prtd->poll_time + * 1000)); + + return HRTIMER_RESTART; + } else + return HRTIMER_NORESTART; +} +static enum hrtimer_restart afe_hrtimer_rec_callback(struct hrtimer *hrt) +{ + struct pcm_afe_info *prtd = + container_of(hrt, struct pcm_afe_info, hrt); + struct snd_pcm_substream *substream = prtd->substream; + struct snd_pcm_runtime *runtime = substream->runtime; + u32 mem_map_handle = 0; + int ret; + + mem_map_handle = afe_req_mmap_handle(prtd->audio_client); + if (!mem_map_handle) + pr_err("%s: mem_map_handle is NULL\n", __func__); + + if (prtd->start) { + if (prtd->dsp_cnt == runtime->periods) + prtd->dsp_cnt = 0; + pr_debug("%s: mem_map_handle 0x%x\n", __func__, mem_map_handle); + ret = afe_rt_proxy_port_read( + (prtd->dma_addr + (prtd->dsp_cnt + * snd_pcm_lib_period_bytes(prtd->substream))), mem_map_handle, + snd_pcm_lib_period_bytes(prtd->substream)); + if (ret < 0) { + pr_err("%s: AFE port read fails: %d\n", __func__, ret); + prtd->start = 0; + return HRTIMER_NORESTART; + } + prtd->dsp_cnt++; + pr_debug("sending frame rec to DSP: poll_time: %d\n", + prtd->poll_time); + hrtimer_forward_now(hrt, ns_to_ktime(prtd->poll_time + * 1000)); + + return HRTIMER_RESTART; + } else + return HRTIMER_NORESTART; +} +static void pcm_afe_process_tx_pkt(uint32_t opcode, + uint32_t token, uint32_t *payload, + void *priv) +{ + struct pcm_afe_info *prtd = priv; + unsigned long dsp_flags; + struct snd_pcm_substream *substream = NULL; + struct snd_pcm_runtime *runtime = NULL; + uint16_t event; + uint64_t period_bytes; + uint64_t bytes_one_sec; + + if (prtd == NULL) + return; + substream = prtd->substream; + runtime = substream->runtime; + pr_debug("%s\n", __func__); + spin_lock_irqsave(&prtd->dsp_lock, dsp_flags); + switch (opcode) { + case AFE_EVENT_RT_PROXY_PORT_STATUS: { + event = (uint16_t)((0xFFFF0000 & payload[0]) >> 0x10); + switch (event) { + case AFE_EVENT_RTPORT_START: { + prtd->dsp_cnt = 0; + /* Calculate poll time. + * Split steps to avoid overflow. + * Poll time-time corresponding to one period + * in bytes. + * (Samplerate * channelcount * format) = + * bytes in 1 sec. + * Poll time = + * (period bytes / bytes in one sec) * + * 1000000 micro seconds. + * Multiplication by 1000000 is done in two + * steps to keep the accuracy of poll time. + */ + if (prtd->mmap_flag) { + period_bytes = ((uint64_t)( + (snd_pcm_lib_period_bytes( + prtd->substream)) * + 1000)); + bytes_one_sec = (runtime->rate + * runtime->channels * 2); + bytes_one_sec = + div_u64(bytes_one_sec, 1000); + prtd->poll_time = + div_u64(period_bytes, + bytes_one_sec); + pr_debug("prtd->poll_time: %d", + prtd->poll_time); + } + break; + } + case AFE_EVENT_RTPORT_STOP: + pr_debug("%s: event!=0\n", __func__); + prtd->start = 0; + snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP); + break; + case AFE_EVENT_RTPORT_LOW_WM: + pr_debug("%s: Underrun\n", __func__); + break; + case AFE_EVENT_RTPORT_HI_WM: + pr_debug("%s: Overrun\n", __func__); + break; + default: + break; + } + break; + } + case APR_BASIC_RSP_RESULT: { + switch (payload[0]) { + case AFE_PORT_DATA_CMD_RT_PROXY_PORT_WRITE_V2: + pr_debug("write done\n"); + prtd->pcm_irq_pos += snd_pcm_lib_period_bytes + (prtd->substream); + snd_pcm_period_elapsed(prtd->substream); + break; + default: + break; + } + break; + } + case RESET_EVENTS: + prtd->pcm_irq_pos += snd_pcm_lib_period_bytes + (prtd->substream); + prtd->reset_event = true; + snd_pcm_period_elapsed(prtd->substream); + break; + default: + break; + } + spin_unlock_irqrestore(&prtd->dsp_lock, dsp_flags); +} + +static void pcm_afe_process_rx_pkt(uint32_t opcode, + uint32_t token, uint32_t *payload, + void *priv) +{ + struct pcm_afe_info *prtd = priv; + unsigned long dsp_flags; + struct snd_pcm_substream *substream = NULL; + struct snd_pcm_runtime *runtime = NULL; + uint16_t event; + uint64_t period_bytes; + uint64_t bytes_one_sec; + uint32_t mem_map_handle = 0; + + if (prtd == NULL) + return; + substream = prtd->substream; + runtime = substream->runtime; + pr_debug("%s\n", __func__); + spin_lock_irqsave(&prtd->dsp_lock, dsp_flags); + switch (opcode) { + case AFE_EVENT_RT_PROXY_PORT_STATUS: { + event = (uint16_t)((0xFFFF0000 & payload[0]) >> 0x10); + switch (event) { + case AFE_EVENT_RTPORT_START: { + prtd->dsp_cnt = 0; + /* Calculate poll time. Split steps to avoid overflow. + * Poll time-time corresponding to one period in bytes. + * (Samplerate * channelcount * format)=bytes in 1 sec. + * Poll time = (period bytes / bytes in one sec) * + * 1000000 micro seconds. + * Multiplication by 1000000 is done in two steps to + * keep the accuracy of poll time. + */ + if (prtd->mmap_flag) { + period_bytes = ((uint64_t)( + (snd_pcm_lib_period_bytes( + prtd->substream)) * 1000)); + bytes_one_sec = (runtime->rate * + runtime->channels * 2); + bytes_one_sec = div_u64(bytes_one_sec, 1000); + prtd->poll_time = + div_u64(period_bytes, bytes_one_sec); + pr_debug("prtd->poll_time : %d\n", + prtd->poll_time); + } else { + mem_map_handle = + afe_req_mmap_handle(prtd->audio_client); + if (!mem_map_handle) + pr_err("%s:mem_map_handle is NULL\n", + __func__); + /* Do initial read to start transfer */ + afe_rt_proxy_port_read((prtd->dma_addr + + (prtd->dsp_cnt * + snd_pcm_lib_period_bytes( + prtd->substream))), + mem_map_handle, + snd_pcm_lib_period_bytes( + prtd->substream)); + prtd->dsp_cnt++; + } + break; + } + case AFE_EVENT_RTPORT_STOP: + pr_debug("%s: event!=0\n", __func__); + prtd->start = 0; + snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP); + break; + case AFE_EVENT_RTPORT_LOW_WM: + pr_debug("%s: Underrun\n", __func__); + break; + case AFE_EVENT_RTPORT_HI_WM: + pr_debug("%s: Overrun\n", __func__); + break; + default: + break; + } + break; + } + case APR_BASIC_RSP_RESULT: { + switch (payload[0]) { + case AFE_PORT_DATA_CMD_RT_PROXY_PORT_READ_V2: + pr_debug("%s :Read done\n", __func__); + prtd->pcm_irq_pos += snd_pcm_lib_period_bytes + (prtd->substream); + if (!prtd->mmap_flag) { + atomic_set(&prtd->rec_bytes_avail, 1); + wake_up(&prtd->read_wait); + } + snd_pcm_period_elapsed(prtd->substream); + break; + default: + break; + } + break; + } + case RESET_EVENTS: + prtd->pcm_irq_pos += snd_pcm_lib_period_bytes + (prtd->substream); + prtd->reset_event = true; + if (!prtd->mmap_flag) { + atomic_set(&prtd->rec_bytes_avail, 1); + wake_up(&prtd->read_wait); + } + snd_pcm_period_elapsed(prtd->substream); + break; + default: + break; + } + spin_unlock_irqrestore(&prtd->dsp_lock, dsp_flags); +} + +static int msm_afe_playback_prepare(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct pcm_afe_info *prtd = runtime->private_data; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *dai = rtd->cpu_dai; + int ret = 0; + + pr_debug("%s: sample_rate=%d\n", __func__, runtime->rate); + + pr_debug("%s: dai->id =%x\n", __func__, dai->id); + ret = afe_register_get_events(dai->id, + pcm_afe_process_tx_pkt, prtd); + if (ret < 0) { + pr_err("afe-pcm:register for events failed\n"); + return ret; + } + pr_debug("%s:success\n", __func__); + prtd->prepared++; + return ret; +} + +static int msm_afe_capture_prepare(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct pcm_afe_info *prtd = runtime->private_data; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *dai = rtd->cpu_dai; + int ret = 0; + + pr_debug("%s\n", __func__); + + pr_debug("%s: dai->id =%x\n", __func__, dai->id); + ret = afe_register_get_events(dai->id, + pcm_afe_process_rx_pkt, prtd); + if (ret < 0) { + pr_err("afe-pcm:register for events failed\n"); + return ret; + } + pr_debug("%s:success\n", __func__); + prtd->prepared++; + return 0; +} + +/* Conventional and unconventional sample rate supported */ +static unsigned int supported_sample_rates[] = { + 8000, 16000, 48000 +}; + +static struct snd_pcm_hw_constraint_list constraints_sample_rates = { + .count = ARRAY_SIZE(supported_sample_rates), + .list = supported_sample_rates, + .mask = 0, +}; + +static int msm_afe_open(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct pcm_afe_info *prtd = NULL; + int ret = 0; + + prtd = kzalloc(sizeof(struct pcm_afe_info), GFP_KERNEL); + if (prtd == NULL) + return -ENOMEM; + pr_debug("prtd %pK\n", prtd); + + mutex_init(&prtd->lock); + spin_lock_init(&prtd->dsp_lock); + prtd->dsp_cnt = 0; + + mutex_lock(&prtd->lock); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + runtime->hw = msm_afe_hardware_playback; + else + runtime->hw = msm_afe_hardware_capture; + + prtd->substream = substream; + runtime->private_data = prtd; + prtd->audio_client = q6afe_audio_client_alloc(prtd); + if (!prtd->audio_client) { + pr_debug("%s: Could not allocate memory\n", __func__); + mutex_unlock(&prtd->lock); + kfree(prtd); + return -ENOMEM; + } + + atomic_set(&prtd->rec_bytes_avail, 0); + init_waitqueue_head(&prtd->read_wait); + + hrtimer_init(&prtd->hrt, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + prtd->hrt.function = afe_hrtimer_callback; + else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + prtd->hrt.function = afe_hrtimer_rec_callback; + + mutex_unlock(&prtd->lock); + ret = snd_pcm_hw_constraint_list(runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + &constraints_sample_rates); + if (ret < 0) + pr_err("snd_pcm_hw_constraint_list failed\n"); + /* Ensure that buffer size is a multiple of period size */ + ret = snd_pcm_hw_constraint_integer(runtime, + SNDRV_PCM_HW_PARAM_PERIODS); + if (ret < 0) + pr_err("snd_pcm_hw_constraint_integer failed\n"); + + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + ret = snd_pcm_hw_constraint_minmax(runtime, + SNDRV_PCM_HW_PARAM_BUFFER_BYTES, + MIN_CAPTURE_NUM_PERIODS * MIN_CAPTURE_PERIOD_SIZE, + MAX_CAPTURE_NUM_PERIODS * MAX_CAPTURE_PERIOD_SIZE); + + if (ret < 0) { + pr_err("constraint for buffer bytes min max ret = %d\n", + ret); + } + } + + prtd->reset_event = false; + return 0; +} + +static int msm_afe_playback_copy(struct snd_pcm_substream *substream, + int channel, unsigned long hwoff, + void __user *buf, unsigned long fbytes) +{ + int ret = 0; + struct snd_pcm_runtime *runtime = substream->runtime; + struct pcm_afe_info *prtd = runtime->private_data; + char *hwbuf = runtime->dma_area + hwoff; + u32 mem_map_handle = 0; + + pr_debug("%s : appl_ptr 0x%lx hw_ptr 0x%lx dest_to_copy 0x%pK\n", + __func__, + runtime->control->appl_ptr, runtime->status->hw_ptr, hwbuf); + + if (copy_from_user(hwbuf, buf, fbytes)) { + pr_err("%s :Failed to copy audio from user buffer\n", + __func__); + + ret = -EFAULT; + goto fail; + } + + if (!prtd->mmap_flag) { + mem_map_handle = afe_req_mmap_handle(prtd->audio_client); + if (!mem_map_handle) { + pr_err("%s: mem_map_handle is NULL\n", __func__); + ret = -EFAULT; + goto fail; + } + + pr_debug("%s : prtd-> dma_addr 0x%lx dsp_cnt %d\n", __func__, + prtd->dma_addr, prtd->dsp_cnt); + + if (prtd->dsp_cnt == runtime->periods) + prtd->dsp_cnt = 0; + + ret = afe_rt_proxy_port_write( + (prtd->dma_addr + (prtd->dsp_cnt * + snd_pcm_lib_period_bytes(prtd->substream))), + mem_map_handle, + snd_pcm_lib_period_bytes(prtd->substream)); + + if (ret) { + pr_err("%s: AFE proxy port write failed %d\n", + __func__, ret); + goto fail; + } + prtd->dsp_cnt++; + } +fail: + return ret; +} + +static int msm_afe_capture_copy(struct snd_pcm_substream *substream, + int channel, unsigned long hwoff, + void __user *buf, unsigned long fbytes) +{ + int ret = 0; + struct snd_pcm_runtime *runtime = substream->runtime; + struct pcm_afe_info *prtd = runtime->private_data; + char *hwbuf = runtime->dma_area + hwoff; + u32 mem_map_handle = 0; + + if (!prtd->mmap_flag) { + mem_map_handle = afe_req_mmap_handle(prtd->audio_client); + + if (!mem_map_handle) { + pr_err("%s: mem_map_handle is NULL\n", __func__); + ret = -EFAULT; + goto fail; + } + + if (prtd->dsp_cnt == runtime->periods) + prtd->dsp_cnt = 0; + + ret = afe_rt_proxy_port_read((prtd->dma_addr + + (prtd->dsp_cnt * + snd_pcm_lib_period_bytes(prtd->substream))), + mem_map_handle, + snd_pcm_lib_period_bytes(prtd->substream)); + + if (ret) { + pr_err("%s: AFE proxy port read failed %d\n", + __func__, ret); + goto fail; + } + + prtd->dsp_cnt++; + ret = wait_event_timeout(prtd->read_wait, + atomic_read(&prtd->rec_bytes_avail), + msecs_to_jiffies(TIMEOUT_MS)); + if (ret < 0) { + pr_err("%s: wait_event_timeout failed\n", __func__); + + ret = -ETIMEDOUT; + goto fail; + } + atomic_set(&prtd->rec_bytes_avail, 0); + } + pr_debug("%s:appl_ptr 0x%lx hw_ptr 0x%lx src_to_copy 0x%pK\n", + __func__, runtime->control->appl_ptr, + runtime->status->hw_ptr, hwbuf); + + if (copy_to_user(buf, hwbuf, fbytes)) { + pr_err("%s: copy to user failed\n", __func__); + + goto fail; + ret = -EFAULT; + } + +fail: + return ret; +} + +static int msm_afe_copy(struct snd_pcm_substream *substream, int channel, + unsigned long hwoff, void __user *buf, + unsigned long fbytes) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct pcm_afe_info *prtd = runtime->private_data; + + int ret = 0; + + if (prtd->reset_event) { + pr_debug("%s: reset events received from ADSP, return error\n", + __func__); + return -ENETRESET; + } + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + ret = msm_afe_playback_copy(substream, channel, hwoff, + buf, fbytes); + else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + ret = msm_afe_capture_copy(substream, channel, hwoff, + buf, fbytes); + return ret; +} + +static int msm_afe_close(struct snd_pcm_substream *substream) +{ + int rc = 0; + struct snd_dma_buffer *dma_buf; + struct snd_pcm_runtime *runtime; + struct pcm_afe_info *prtd; + struct snd_soc_pcm_runtime *rtd = NULL; + struct snd_soc_dai *dai = NULL; + int dir = IN; + int ret = 0; + + pr_debug("%s\n", __func__); + if (substream == NULL) { + pr_err("substream is NULL\n"); + return -EINVAL; + } + rtd = substream->private_data; + dai = rtd->cpu_dai; + runtime = substream->runtime; + prtd = runtime->private_data; + + mutex_lock(&prtd->lock); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + dir = IN; + ret = afe_unregister_get_events(dai->id); + if (ret < 0) + pr_err("AFE unregister for events failed\n"); + } else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + dir = OUT; + ret = afe_unregister_get_events(dai->id); + if (ret < 0) + pr_err("AFE unregister for events failed\n"); + } + if (prtd->mmap_flag) + hrtimer_cancel(&prtd->hrt); + + rc = afe_cmd_memory_unmap(afe_req_mmap_handle(prtd->audio_client)); + if (rc < 0) + pr_err("AFE memory unmap failed\n"); + + pr_debug("release all buffer\n"); + dma_buf = &substream->dma_buffer; + if (dma_buf == NULL) { + pr_debug("dma_buf is NULL\n"); + goto done; + } + + if (dma_buf->area) + dma_buf->area = NULL; + q6afe_audio_client_buf_free_contiguous(dir, prtd->audio_client); +done: + pr_debug("%s: dai->id =%x\n", __func__, dai->id); + q6afe_audio_client_free(prtd->audio_client); + mutex_unlock(&prtd->lock); + prtd->prepared--; + kfree(prtd); + runtime->private_data = NULL; + return 0; +} +static int msm_afe_prepare(struct snd_pcm_substream *substream) +{ + int ret = 0; + struct snd_pcm_runtime *runtime = substream->runtime; + struct pcm_afe_info *prtd = runtime->private_data; + + prtd->pcm_irq_pos = 0; + if (prtd->prepared) + return 0; + mutex_lock(&prtd->lock); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + ret = msm_afe_playback_prepare(substream); + else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + ret = msm_afe_capture_prepare(substream); + mutex_unlock(&prtd->lock); + return ret; +} +static int msm_afe_mmap(struct snd_pcm_substream *substream, + struct vm_area_struct *vma) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct pcm_afe_info *prtd = runtime->private_data; + struct afe_audio_client *ac = prtd->audio_client; + struct afe_audio_port_data *apd = ac->port; + struct afe_audio_buffer *ab; + int dir = -1; + + pr_debug("%s\n", __func__); + prtd->mmap_flag = 1; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + dir = IN; + else + dir = OUT; + ab = &(apd[dir].buf[0]); + + return msm_audio_ion_mmap((struct audio_buffer *)ab, vma); +} +static int msm_afe_trigger(struct snd_pcm_substream *substream, int cmd) +{ + int ret = 0; + struct snd_pcm_runtime *runtime = substream->runtime; + struct pcm_afe_info *prtd = runtime->private_data; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + pr_debug("%s: SNDRV_PCM_TRIGGER_START\n", __func__); + prtd->start = 1; + if (prtd->mmap_flag) + hrtimer_start(&prtd->hrt, ns_to_ktime(0), + HRTIMER_MODE_REL); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + pr_debug("%s: SNDRV_PCM_TRIGGER_STOP\n", __func__); + prtd->start = 0; + break; + default: + ret = -EINVAL; + break; + } + return ret; +} +static int msm_afe_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_dma_buffer *dma_buf = &substream->dma_buffer; + struct pcm_afe_info *prtd = runtime->private_data; + struct afe_audio_buffer *buf; + int dir, rc; + + pr_debug("%s:\n", __func__); + + mutex_lock(&prtd->lock); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + dir = IN; + else + dir = OUT; + + rc = q6afe_audio_client_buf_alloc_contiguous(dir, + prtd->audio_client, + (params_buffer_bytes(params) / params_periods(params)), + params_periods(params)); + pr_debug("params_buffer_bytes(params) = %d\n", + (params_buffer_bytes(params))); + pr_debug("params_periods(params) = %d\n", + (params_periods(params))); + pr_debug("params_periodsize(params) = %d\n", + (params_buffer_bytes(params) / params_periods(params))); + + if (rc < 0) { + pr_err("Audio Start: Buffer Allocation failed rc = %d\n", rc); + mutex_unlock(&prtd->lock); + return -ENOMEM; + } + buf = prtd->audio_client->port[dir].buf; + + if (buf == NULL || buf[0].data == NULL) { + mutex_unlock(&prtd->lock); + return -ENOMEM; + } + + pr_debug("%s:buf = %pK\n", __func__, buf); + dma_buf->dev.type = SNDRV_DMA_TYPE_DEV; + dma_buf->dev.dev = substream->pcm->card->dev; + dma_buf->private_data = NULL; + dma_buf->area = buf[0].data; + dma_buf->addr = buf[0].phys; + + dma_buf->bytes = params_buffer_bytes(params); + + if (!dma_buf->area) { + pr_err("%s:MSM AFE physical memory allocation failed\n", + __func__); + mutex_unlock(&prtd->lock); + return -ENOMEM; + } + + memset(dma_buf->area, 0, params_buffer_bytes(params)); + + prtd->dma_addr = (phys_addr_t) dma_buf->addr; + + mutex_unlock(&prtd->lock); + + snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); + + rc = afe_memory_map(dma_buf->addr, dma_buf->bytes, prtd->audio_client); + if (rc < 0) + pr_err("fail to map memory to DSP\n"); + + return rc; +} +static snd_pcm_uframes_t msm_afe_pointer(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct pcm_afe_info *prtd = runtime->private_data; + + if (prtd->pcm_irq_pos >= snd_pcm_lib_buffer_bytes(substream)) + prtd->pcm_irq_pos = 0; + + if (prtd->reset_event) { + pr_debug("%s: reset events received from ADSP, return XRUN\n", + __func__); + return SNDRV_PCM_POS_XRUN; + } + + pr_debug("pcm_irq_pos = %d\n", prtd->pcm_irq_pos); + return bytes_to_frames(runtime, (prtd->pcm_irq_pos)); +} + +static const struct snd_pcm_ops msm_afe_ops = { + .open = msm_afe_open, + .copy_user = msm_afe_copy, + .hw_params = msm_afe_hw_params, + .trigger = msm_afe_trigger, + .close = msm_afe_close, + .prepare = msm_afe_prepare, + .mmap = msm_afe_mmap, + .pointer = msm_afe_pointer, +}; + + +static int msm_asoc_pcm_new(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_card *card = rtd->card->snd_card; + int ret = 0; + + pr_debug("%s\n", __func__); + if (!card->dev->coherent_dma_mask) + card->dev->coherent_dma_mask = DMA_BIT_MASK(32); + return ret; +} + +static int msm_afe_afe_probe(struct snd_soc_platform *platform) +{ + pr_debug("%s\n", __func__); + return 0; +} + +static struct snd_soc_platform_driver msm_soc_platform = { + .ops = &msm_afe_ops, + .pcm_new = msm_asoc_pcm_new, + .probe = msm_afe_afe_probe, +}; + +static int msm_afe_probe(struct platform_device *pdev) +{ + + pr_debug("%s: dev name %s\n", __func__, dev_name(&pdev->dev)); + return snd_soc_register_platform(&pdev->dev, + &msm_soc_platform); +} + +static int msm_afe_remove(struct platform_device *pdev) +{ + pr_debug("%s\n", __func__); + snd_soc_unregister_platform(&pdev->dev); + return 0; +} +static const struct of_device_id msm_pcm_afe_dt_match[] = { + {.compatible = "qcom,msm-pcm-afe"}, + {} +}; +MODULE_DEVICE_TABLE(of, msm_pcm_afe_dt_match); + +static struct platform_driver msm_afe_driver = { + .driver = { + .name = "msm-pcm-afe", + .owner = THIS_MODULE, + .of_match_table = msm_pcm_afe_dt_match, + }, + .probe = msm_afe_probe, + .remove = msm_afe_remove, +}; + +int __init msm_pcm_afe_init(void) +{ + pr_debug("%s\n", __func__); + return platform_driver_register(&msm_afe_driver); +} + +void msm_pcm_afe_exit(void) +{ + pr_debug("%s\n", __func__); + platform_driver_unregister(&msm_afe_driver); +} + +MODULE_DESCRIPTION("AFE PCM module platform driver"); +MODULE_LICENSE("GPL v2"); diff --git a/audio-kernel/asoc/msm-pcm-afe-v2.h b/audio-kernel/asoc/msm-pcm-afe-v2.h new file mode 100644 index 000000000..926b626de --- /dev/null +++ b/audio-kernel/asoc/msm-pcm-afe-v2.h @@ -0,0 +1,49 @@ +/* Copyright (c) 2012,2015-2016 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 _MSM_PCM_AFE_H +#define _MSM_PCM_AFE_H +#include +#include + + +struct pcm_afe_info { + unsigned long dma_addr; + struct snd_pcm_substream *substream; + unsigned int pcm_irq_pos; /* IRQ position */ + struct mutex lock; + spinlock_t dsp_lock; + uint32_t samp_rate; + uint32_t channel_mode; + uint8_t start; + uint32_t dsp_cnt; + uint32_t buf_phys; + int32_t mmap_flag; + int prepared; + struct hrtimer hrt; + int poll_time; + struct afe_audio_client *audio_client; + wait_queue_head_t read_wait; + atomic_t rec_bytes_avail; + bool reset_event; +}; + + +#define MSM_EXT(xname, fp_info, fp_get, fp_put, addr) \ + {.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \ + .name = xname, \ + .info = fp_info,\ + .get = fp_get, .put = fp_put, \ + .private_value = addr, \ + } + +#endif /*_MSM_PCM_AFE_H*/ diff --git a/audio-kernel/asoc/msm-pcm-dtmf-v2.c b/audio-kernel/asoc/msm-pcm-dtmf-v2.c new file mode 100644 index 000000000..77dc81ce9 --- /dev/null +++ b/audio-kernel/asoc/msm-pcm-dtmf-v2.c @@ -0,0 +1,591 @@ +/* Copyright (c) 2013-2014, 2017-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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "msm-pcm-q6-v2.h" +#include "msm-pcm-routing-v2.h" + +enum { + DTMF_IN_RX, + DTMF_IN_TX, +}; + +enum format { + FORMAT_S16_LE = 2 +}; + +struct dtmf_det_info { + char session[MAX_SESSION_NAME_LEN]; + uint8_t dir; + uint16_t high_freq; + uint16_t low_freq; +}; + +struct dtmf_buf_node { + struct list_head list; + struct dtmf_det_info dtmf_det_pkt; +}; + +enum dtmf_state { + DTMF_GEN_RX_STOPPED, + DTMF_GEN_RX_STARTED, +}; + +#define DTMF_MAX_Q_LEN 10 +#define DTMF_PKT_SIZE sizeof(struct dtmf_det_info) + +struct dtmf_drv_info { + enum dtmf_state state; + struct snd_pcm_substream *capture_substream; + + struct list_head out_queue; + struct list_head free_out_queue; + + wait_queue_head_t out_wait; + + struct mutex lock; + spinlock_t dsp_lock; + + uint8_t capture_start; + uint8_t capture_instance; + + unsigned int pcm_capture_size; + unsigned int pcm_capture_count; + unsigned int pcm_capture_irq_pos; + unsigned int pcm_capture_buf_pos; +}; + +static struct snd_pcm_hardware msm_pcm_hardware = { + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED), + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 1, + .channels_max = 1, + .buffer_bytes_max = (sizeof(struct dtmf_buf_node) * DTMF_MAX_Q_LEN), + .period_bytes_min = DTMF_PKT_SIZE, + .period_bytes_max = DTMF_PKT_SIZE, + .periods_min = DTMF_MAX_Q_LEN, + .periods_max = DTMF_MAX_Q_LEN, + .fifo_size = 0, +}; + +static int msm_dtmf_rx_generate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + uint16_t low_freq = ucontrol->value.integer.value[0]; + uint16_t high_freq = ucontrol->value.integer.value[1]; + int64_t duration = ucontrol->value.integer.value[2]; + uint16_t gain = ucontrol->value.integer.value[3]; + + pr_debug("%s: low_freq=%d high_freq=%d duration=%d gain=%d\n", + __func__, low_freq, high_freq, (int)duration, gain); + afe_dtmf_generate_rx(duration, high_freq, low_freq, gain); + return 0; +} + +static int msm_dtmf_rx_generate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s:\n", __func__); + ucontrol->value.integer.value[0] = 0; + return 0; +} + +static int msm_dtmf_detect_voice_rx_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int enable = ucontrol->value.integer.value[0]; + + pr_debug("%s: enable=%d\n", __func__, enable); + voc_enable_dtmf_rx_detection(voc_get_session_id(VOICE_SESSION_NAME), + enable); + + return 0; +} + +static int msm_dtmf_detect_voice_rx_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = 0; + return 0; +} + +static int msm_dtmf_detect_volte_rx_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int enable = ucontrol->value.integer.value[0]; + + pr_debug("%s: enable=%d\n", __func__, enable); + voc_enable_dtmf_rx_detection(voc_get_session_id(VOLTE_SESSION_NAME), + enable); + + return 0; +} + +static int msm_dtmf_detect_volte_rx_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = 0; + return 0; +} + +static struct snd_kcontrol_new msm_dtmf_controls[] = { + SOC_SINGLE_MULTI_EXT("DTMF_Generate Rx Low High Duration Gain", + SND_SOC_NOPM, 0, 5000, 0, 4, + msm_dtmf_rx_generate_get, + msm_dtmf_rx_generate_put), + SOC_SINGLE_EXT("DTMF_Detect Rx Voice enable", SND_SOC_NOPM, 0, 1, 0, + msm_dtmf_detect_voice_rx_get, + msm_dtmf_detect_voice_rx_put), + SOC_SINGLE_EXT("DTMF_Detect Rx VoLTE enable", SND_SOC_NOPM, 0, 1, 0, + msm_dtmf_detect_volte_rx_get, + msm_dtmf_detect_volte_rx_put), +}; + +static int msm_pcm_dtmf_probe(struct snd_soc_platform *platform) +{ + snd_soc_add_platform_controls(platform, msm_dtmf_controls, + ARRAY_SIZE(msm_dtmf_controls)); + return 0; +} + +static void dtmf_rx_detected_cb(uint8_t *pkt, + char *session, + void *private_data) +{ + struct dtmf_buf_node *buf_node = NULL; + struct vss_istream_evt_rx_dtmf_detected *dtmf_det_pkt = + (struct vss_istream_evt_rx_dtmf_detected *)pkt; + struct dtmf_drv_info *prtd = private_data; + unsigned long dsp_flags; + + pr_debug("%s\n", __func__); + if (prtd->capture_substream == NULL) + return; + + /* Copy dtmf detected info into out_queue. */ + spin_lock_irqsave(&prtd->dsp_lock, dsp_flags); + /* discarding dtmf detection info till start is received */ + if (!list_empty(&prtd->free_out_queue) && prtd->capture_start) { + buf_node = list_first_entry(&prtd->free_out_queue, + struct dtmf_buf_node, list); + list_del(&buf_node->list); + buf_node->dtmf_det_pkt.high_freq = dtmf_det_pkt->high_freq; + buf_node->dtmf_det_pkt.low_freq = dtmf_det_pkt->low_freq; + if (session != NULL) + strlcpy(buf_node->dtmf_det_pkt.session, + session, MAX_SESSION_NAME_LEN); + + buf_node->dtmf_det_pkt.dir = DTMF_IN_RX; + pr_debug("high =%d, low=%d session=%s\n", + buf_node->dtmf_det_pkt.high_freq, + buf_node->dtmf_det_pkt.low_freq, + buf_node->dtmf_det_pkt.session); + list_add_tail(&buf_node->list, &prtd->out_queue); + prtd->pcm_capture_irq_pos += prtd->pcm_capture_count; + spin_unlock_irqrestore(&prtd->dsp_lock, dsp_flags); + snd_pcm_period_elapsed(prtd->capture_substream); + } else { + spin_unlock_irqrestore(&prtd->dsp_lock, dsp_flags); + pr_err("DTMF detection pkt in Rx dropped, no free node available\n"); + } + + wake_up(&prtd->out_wait); +} + +static int msm_pcm_capture_copy(struct snd_pcm_substream *substream, + int channel, unsigned long hwoff, + void __user *buf, unsigned long fbytes) +{ + int ret = 0; + struct dtmf_buf_node *buf_node = NULL; + struct snd_pcm_runtime *runtime = substream->runtime; + struct dtmf_drv_info *prtd = runtime->private_data; + unsigned long dsp_flags; + + ret = wait_event_interruptible_timeout(prtd->out_wait, + (!list_empty(&prtd->out_queue)), + 1 * HZ); + + if (ret > 0) { + if (fbytes <= DTMF_PKT_SIZE) { + spin_lock_irqsave(&prtd->dsp_lock, dsp_flags); + buf_node = list_first_entry(&prtd->out_queue, + struct dtmf_buf_node, list); + list_del(&buf_node->list); + spin_unlock_irqrestore(&prtd->dsp_lock, dsp_flags); + ret = copy_to_user(buf, + &buf_node->dtmf_det_pkt, + fbytes); + if (ret) { + pr_err("%s: Copy to user returned %d\n", + __func__, ret); + ret = -EFAULT; + } + spin_lock_irqsave(&prtd->dsp_lock, dsp_flags); + list_add_tail(&buf_node->list, + &prtd->free_out_queue); + spin_unlock_irqrestore(&prtd->dsp_lock, dsp_flags); + + } else { + pr_err("%s: Read count %lu > DTMF_PKT_SIZE\n", + __func__, fbytes); + ret = -ENOMEM; + } + } else if (ret == 0) { + pr_err("%s: No UL data available\n", __func__); + ret = -ETIMEDOUT; + } else { + pr_err("%s: Read was interrupted\n", __func__); + ret = -ERESTARTSYS; + } + return ret; +} + +static int msm_pcm_copy(struct snd_pcm_substream *substream, int a, + unsigned long hwoff, void __user *buf, unsigned long fbytes) +{ + int ret = 0; + + pr_debug("%s() DTMF\n", __func__); + + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + ret = msm_pcm_capture_copy(substream, a, hwoff, buf, fbytes); + + return ret; +} + +static int msm_pcm_open(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct dtmf_drv_info *prtd = NULL; + int ret = 0; + + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + prtd = kzalloc(sizeof(struct dtmf_drv_info), GFP_KERNEL); + + if (prtd == NULL) { + ret = -ENOMEM; + goto done; + } + + mutex_init(&prtd->lock); + spin_lock_init(&prtd->dsp_lock); + init_waitqueue_head(&prtd->out_wait); + INIT_LIST_HEAD(&prtd->out_queue); + INIT_LIST_HEAD(&prtd->free_out_queue); + + runtime->hw = msm_pcm_hardware; + + ret = snd_pcm_hw_constraint_integer(runtime, + SNDRV_PCM_HW_PARAM_PERIODS); + if (ret < 0) + pr_info("snd_pcm_hw_constraint_integer failed\n"); + + prtd->capture_substream = substream; + prtd->capture_instance++; + runtime->private_data = prtd; + } + +done: + return ret; +} + +static int msm_pcm_close(struct snd_pcm_substream *substream) +{ + int ret = 0; + struct list_head *ptr = NULL; + struct list_head *next = NULL; + struct dtmf_buf_node *buf_node = NULL; + struct snd_dma_buffer *c_dma_buf; + struct snd_pcm_substream *c_substream; + struct snd_pcm_runtime *runtime = substream->runtime; + struct dtmf_drv_info *prtd = runtime->private_data; + unsigned long dsp_flags; + + pr_debug("%s() DTMF\n", __func__); + + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + mutex_lock(&prtd->lock); + wake_up(&prtd->out_wait); + + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + prtd->capture_instance--; + + if (!prtd->capture_instance) { + if (prtd->state == DTMF_GEN_RX_STARTED) { + prtd->state = DTMF_GEN_RX_STOPPED; + voc_disable_dtmf_det_on_active_sessions(); + voc_register_dtmf_rx_detection_cb(NULL, NULL); + } + /* release all buffer */ + /* release out_queue and free_out_queue */ + pr_debug("release all buffer\n"); + c_substream = prtd->capture_substream; + if (c_substream == NULL) { + pr_debug("c_substream is NULL\n"); + mutex_unlock(&prtd->lock); + return -EINVAL; + } + + c_dma_buf = &c_substream->dma_buffer; + if (c_dma_buf == NULL) { + pr_debug("c_dma_buf is NULL.\n"); + mutex_unlock(&prtd->lock); + return -EINVAL; + } + + if (c_dma_buf->area != NULL) { + spin_lock_irqsave(&prtd->dsp_lock, dsp_flags); + list_for_each_safe(ptr, next, + &prtd->out_queue) { + buf_node = list_entry(ptr, + struct dtmf_buf_node, list); + list_del(&buf_node->list); + } + + list_for_each_safe(ptr, next, + &prtd->free_out_queue) { + buf_node = list_entry(ptr, + struct dtmf_buf_node, list); + list_del(&buf_node->list); + } + + spin_unlock_irqrestore(&prtd->dsp_lock, + dsp_flags); + dma_free_coherent(c_substream->pcm->card->dev, + runtime->hw.buffer_bytes_max, + c_dma_buf->area, + c_dma_buf->addr); + c_dma_buf->area = NULL; + } + } + prtd->capture_substream = NULL; + mutex_unlock(&prtd->lock); + } + + return ret; +} + +static int msm_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct dtmf_drv_info *prtd = runtime->private_data; + struct snd_dma_buffer *dma_buf = &substream->dma_buffer; + struct dtmf_buf_node *buf_node = NULL; + int i = 0, offset = 0; + int ret = 0; + + pr_debug("%s: DTMF\n", __func__); + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + mutex_lock(&prtd->lock); + dma_buf->dev.type = SNDRV_DMA_TYPE_DEV; + dma_buf->dev.dev = substream->pcm->card->dev; + dma_buf->private_data = NULL; + + dma_buf->area = dma_alloc_coherent(substream->pcm->card->dev, + runtime->hw.buffer_bytes_max, + &dma_buf->addr, GFP_KERNEL); + if (!dma_buf->area) { + pr_err("%s:MSM DTMF dma_alloc failed\n", __func__); + mutex_unlock(&prtd->lock); + return -ENOMEM; + } + + dma_buf->bytes = runtime->hw.buffer_bytes_max; + memset(dma_buf->area, 0, runtime->hw.buffer_bytes_max); + + for (i = 0; i < DTMF_MAX_Q_LEN; i++) { + pr_debug("node =%d\n", i); + buf_node = (void *) dma_buf->area + offset; + list_add_tail(&buf_node->list, + &prtd->free_out_queue); + offset = offset + sizeof(struct dtmf_buf_node); + } + + snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); + mutex_unlock(&prtd->lock); + } + + return ret; +} + +static int msm_pcm_capture_prepare(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct dtmf_drv_info *prtd = runtime->private_data; + + pr_debug("%s: DTMF\n", __func__); + prtd->pcm_capture_size = snd_pcm_lib_buffer_bytes(substream); + prtd->pcm_capture_count = snd_pcm_lib_period_bytes(substream); + prtd->pcm_capture_irq_pos = 0; + prtd->pcm_capture_buf_pos = 0; + return 0; +} + +static int msm_pcm_prepare(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct dtmf_drv_info *prtd = runtime->private_data; + + pr_debug("%s: DTMF\n", __func__); + + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + mutex_lock(&prtd->lock); + + msm_pcm_capture_prepare(substream); + + if (runtime->format != FORMAT_S16_LE) { + pr_err("format:%u doesn't match %d\n", + (uint32_t)runtime->format, FORMAT_S16_LE); + mutex_unlock(&prtd->lock); + return -EINVAL; + } + + if (prtd->capture_instance && + (prtd->state != DTMF_GEN_RX_STARTED)) { + voc_register_dtmf_rx_detection_cb(dtmf_rx_detected_cb, + prtd); + prtd->state = DTMF_GEN_RX_STARTED; + } + mutex_unlock(&prtd->lock); + } + + return 0; +} + +static int msm_pcm_trigger(struct snd_pcm_substream *substream, int cmd) +{ + int ret = 0; + struct snd_pcm_runtime *runtime = substream->runtime; + struct dtmf_drv_info *prtd = runtime->private_data; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + pr_debug("%s: Trigger start\n", __func__); + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + prtd->capture_start = 1; + break; + case SNDRV_PCM_TRIGGER_STOP: + pr_debug("SNDRV_PCM_TRIGGER_STOP\n"); + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + prtd->capture_start = 0; + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static snd_pcm_uframes_t msm_pcm_pointer(struct snd_pcm_substream *substream) +{ + snd_pcm_uframes_t ret = 0; + struct snd_pcm_runtime *runtime = substream->runtime; + struct dtmf_drv_info *prtd = runtime->private_data; + + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + if (prtd->pcm_capture_irq_pos >= prtd->pcm_capture_size) + prtd->pcm_capture_irq_pos = 0; + ret = bytes_to_frames(runtime, (prtd->pcm_capture_irq_pos)); + } + + return ret; +} + +static const struct snd_pcm_ops msm_pcm_ops = { + .open = msm_pcm_open, + .copy_user = msm_pcm_copy, + .hw_params = msm_pcm_hw_params, + .close = msm_pcm_close, + .prepare = msm_pcm_prepare, + .trigger = msm_pcm_trigger, + .pointer = msm_pcm_pointer, +}; + +static int msm_asoc_pcm_new(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_card *card = rtd->card->snd_card; + int ret = 0; + + if (!card->dev->coherent_dma_mask) + card->dev->coherent_dma_mask = DMA_BIT_MASK(32); + return ret; +} + +static struct snd_soc_platform_driver msm_soc_platform = { + .ops = &msm_pcm_ops, + .pcm_new = msm_asoc_pcm_new, + .probe = msm_pcm_dtmf_probe, +}; + +static int msm_pcm_probe(struct platform_device *pdev) +{ + pr_debug("%s: dev name %s\n", __func__, dev_name(&pdev->dev)); + + return snd_soc_register_platform(&pdev->dev, + &msm_soc_platform); +} + +static int msm_pcm_remove(struct platform_device *pdev) +{ + snd_soc_unregister_platform(&pdev->dev); + return 0; +} + +static const struct of_device_id msm_pcm_dtmf_dt_match[] = { + {.compatible = "qcom,msm-pcm-dtmf"}, + {} +}; + +MODULE_DEVICE_TABLE(of, msm_pcm_dtmf_dt_match); + + +static struct platform_driver msm_pcm_driver = { + .driver = { + .name = "msm-pcm-dtmf", + .owner = THIS_MODULE, + .of_match_table = msm_pcm_dtmf_dt_match, + }, + .probe = msm_pcm_probe, + .remove = msm_pcm_remove, +}; + +int __init msm_pcm_dtmf_init(void) +{ + return platform_driver_register(&msm_pcm_driver); +} + +void msm_pcm_dtmf_exit(void) +{ + platform_driver_unregister(&msm_pcm_driver); +} + +MODULE_DESCRIPTION("DTMF platform driver"); +MODULE_LICENSE("GPL v2"); diff --git a/audio-kernel/asoc/msm-pcm-host-voice-v2.c b/audio-kernel/asoc/msm-pcm-host-voice-v2.c new file mode 100644 index 000000000..7486ad7dc --- /dev/null +++ b/audio-kernel/asoc/msm-pcm-host-voice-v2.c @@ -0,0 +1,1533 @@ +/* Copyright (c) 2013-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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define HPCM_MAX_Q_LEN 2 +#define HPCM_MIN_VOC_PKT_SIZE 320 +#define HPCM_MAX_VOC_PKT_SIZE 640 +#define VHPCM_BLOCK_SIZE 4096 +#define CACHE_ALIGNMENT_SIZE 128 +#define CACHE_ALIGNMENT_MASK 0xFFFFFF80 + +#define VOICE_TX_CAPTURE_DAI_ID "CS-VOICE HOST TX CAPTURE" +#define VOICE_TX_PLAYBACK_DAI_ID "CS-VOICE HOST TX PLAYBACK" +#define VOICE_RX_CAPTURE_DAI_ID "CS-VOICE HOST RX CAPTURE" +#define VOICE_RX_PLAYBACK_DAI_ID "CS-VOICE HOST RX PLAYBACK" + +#define VOLTE_TX_CAPTURE_DAI_ID "VOLTE HOST TX CAPTURE" +#define VOLTE_TX_PLAYBACK_DAI_ID "VOLTE HOST TX PLAYBACK" +#define VOLTE_RX_CAPTURE_DAI_ID "VOLTE HOST RX CAPTURE" +#define VOLTE_RX_PLAYBACK_DAI_ID "VOLTE HOST RX PLAYBACK" + + +#define VoMMode1_TX_CAPTURE_DAI_ID "VoiceMMode1 HOST TX CAPTURE" +#define VoMMode1_TX_PLAYBACK_DAI_ID "VoiceMMode1 HOST TX PLAYBACK" +#define VoMMode1_RX_CAPTURE_DAI_ID "VoiceMMode1 HOST RX CAPTURE" +#define VoMMode1_RX_PLAYBACK_DAI_ID "VoiceMMode1 HOST RX PLAYBACK" + +#define VoMMode2_TX_CAPTURE_DAI_ID "VoiceMMode2 HOST TX CAPTURE" +#define VoMMode2_TX_PLAYBACK_DAI_ID "VoiceMMode2 HOST TX PLAYBACK" +#define VoMMode2_RX_CAPTURE_DAI_ID "VoiceMMode2 HOST RX CAPTURE" +#define VoMMode2_RX_PLAYBACK_DAI_ID "VoiceMMode2 HOST RX PLAYBACK" + +enum { + RX = 1, + TX, +}; + +enum { + VOICE_INDEX = 0, + VOLTE_INDEX, + VOMMODE1_INDEX, + VOMMODE2_INDEX, + MAX_SESSION +}; + +enum hpcm_state { + HPCM_STOPPED = 1, + HPCM_CLOSED, + HPCM_PREPARED, + HPCM_STARTED, +}; + +struct hpcm_frame { + uint32_t len; + uint8_t voc_pkt[HPCM_MAX_VOC_PKT_SIZE]; +}; + +struct hpcm_buf_node { + struct list_head list; + struct hpcm_frame frame; +}; + +struct vocpcm_ion_buffer { + /* Physical address */ + phys_addr_t paddr; + /* Kernel virtual address */ + void *kvaddr; +}; + +struct dai_data { + enum hpcm_state state; + struct snd_pcm_substream *substream; + struct list_head filled_queue; + struct list_head free_queue; + wait_queue_head_t queue_wait; + spinlock_t dsp_lock; + uint32_t pcm_size; + uint32_t pcm_count; + /* IRQ position */ + uint32_t pcm_irq_pos; + /* Position in buffer */ + uint32_t pcm_buf_pos; + struct vocpcm_ion_buffer vocpcm_ion_buffer; +}; + +struct tap_point { + struct dai_data playback_dai_data; + struct dai_data capture_dai_data; +}; + +struct session { + struct tap_point tx_tap_point; + struct tap_point rx_tap_point; + phys_addr_t sess_paddr; + void *sess_kvaddr; + struct dma_buf *dma_buf; + struct mem_map_table tp_mem_table; +}; + +struct tappnt_mxr_data { + bool enable; + uint16_t direction; + uint16_t sample_rate; +}; + +/* Values from mixer ctl are cached in this structure */ +struct mixer_conf { + int8_t sess_indx; + struct tappnt_mxr_data rx; + struct tappnt_mxr_data tx; +}; + +struct start_cmd { + struct vss_ivpcm_tap_point tap_pnt[2]; + uint32_t no_of_tapoints; +}; + +struct hpcm_drv { + struct mutex lock; + struct session session[MAX_SESSION]; + struct mixer_conf mixer_conf; + struct start_cmd start_cmd; +}; + +static struct hpcm_drv hpcm_drv; + +static struct snd_pcm_hardware msm_pcm_hardware = { + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED), + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_SPECIAL, + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000, + .rate_min = 8000, + .rate_max = 16000, + .channels_min = 1, + .channels_max = 1, + .buffer_bytes_max = sizeof(struct hpcm_buf_node) * HPCM_MAX_Q_LEN, + .period_bytes_min = HPCM_MIN_VOC_PKT_SIZE, + .period_bytes_max = HPCM_MAX_VOC_PKT_SIZE, + .periods_min = HPCM_MAX_Q_LEN, + .periods_max = HPCM_MAX_Q_LEN, + .fifo_size = 0, +}; + +static char *hpcm_get_sess_name(int sess_indx) +{ + char *sess_name = NULL; + + if (sess_indx == VOICE_INDEX) + sess_name = VOICE_SESSION_NAME; + else if (sess_indx == VOLTE_INDEX) + sess_name = VOLTE_SESSION_NAME; + else if (sess_indx == VOMMODE1_INDEX) + sess_name = VOICEMMODE1_NAME; + else if (sess_indx == VOMMODE2_INDEX) + sess_name = VOICEMMODE2_NAME; + else + pr_err("%s:, Invalid sess_index\n", __func__); + + return sess_name; +} + +static void hpcm_reset_mixer_config(struct hpcm_drv *prtd) +{ + prtd->mixer_conf.sess_indx = -1; + prtd->mixer_conf.rx.enable = false; + prtd->mixer_conf.rx.direction = -1; + prtd->mixer_conf.rx.sample_rate = 0; + + prtd->mixer_conf.tx.enable = false; + prtd->mixer_conf.tx.direction = -1; + prtd->mixer_conf.tx.sample_rate = 0; +} + +/* Check for valid mixer control values */ +static bool hpcm_is_valid_config(int sess_indx, int tap_point, + uint16_t direction, uint16_t samplerate) +{ + if (sess_indx < VOICE_INDEX || sess_indx > VOMMODE2_INDEX) { + pr_err("%s: invalid sess_indx :%d\n", __func__, sess_indx); + goto error; + } + + if (samplerate != VSS_IVPCM_SAMPLING_RATE_8K && + samplerate != VSS_IVPCM_SAMPLING_RATE_16K) { + pr_err("%s: invalid sample rate :%d\n", __func__, samplerate); + goto error; + } + + if ((tap_point != RX) && (tap_point != TX)) { + pr_err("%s: invalid tappoint :%d\n", __func__, tap_point); + goto error; + } + + if ((direction != VSS_IVPCM_TAP_POINT_DIR_IN) && + (direction != VSS_IVPCM_TAP_POINT_DIR_OUT) && + (direction != VSS_IVPCM_TAP_POINT_DIR_OUT_IN)) { + pr_err("%s: invalid direction :%d\n", __func__, direction); + goto error; + } + + return true; + +error: + return false; +} + + +static struct dai_data *hpcm_get_dai_data(char *pcm_id, struct hpcm_drv *prtd) +{ + struct dai_data *dai_data = NULL; + size_t size = 0; + + if (pcm_id) { + size = strlen(pcm_id); + /* Check for Voice DAI */ + if (strnstr(pcm_id, VOICE_TX_CAPTURE_DAI_ID, size)) { + dai_data = + &prtd->session[VOICE_INDEX].tx_tap_point.capture_dai_data; + } else if (strnstr(pcm_id, VOICE_TX_PLAYBACK_DAI_ID, size)) { + dai_data = + &prtd->session[VOICE_INDEX].tx_tap_point.playback_dai_data; + } else if (strnstr(pcm_id, VOICE_RX_CAPTURE_DAI_ID, size)) { + dai_data = + &prtd->session[VOICE_INDEX].rx_tap_point.capture_dai_data; + } else if (strnstr(pcm_id, VOICE_RX_PLAYBACK_DAI_ID, size)) { + dai_data = + &prtd->session[VOICE_INDEX].rx_tap_point.playback_dai_data; + /* Check for VoLTE DAI */ + } else if (strnstr(pcm_id, VOLTE_TX_CAPTURE_DAI_ID, size)) { + dai_data = + &prtd->session[VOLTE_INDEX].tx_tap_point.capture_dai_data; + } else if (strnstr(pcm_id, VOLTE_TX_PLAYBACK_DAI_ID, size)) { + dai_data = + &prtd->session[VOLTE_INDEX].tx_tap_point.playback_dai_data; + } else if (strnstr(pcm_id, VOLTE_RX_CAPTURE_DAI_ID, size)) { + dai_data = + &prtd->session[VOLTE_INDEX].rx_tap_point.capture_dai_data; + } else if (strnstr(pcm_id, VOLTE_RX_PLAYBACK_DAI_ID, size)) { + dai_data = + &prtd->session[VOLTE_INDEX].rx_tap_point.playback_dai_data; + /* check for VoiceMMode1 DAI */ + } else if (strnstr(pcm_id, VoMMode1_TX_CAPTURE_DAI_ID, size)) { + dai_data = + &prtd->session[VOMMODE1_INDEX].tx_tap_point.capture_dai_data; + } else if (strnstr(pcm_id, VoMMode1_TX_PLAYBACK_DAI_ID, size)) { + dai_data = + &prtd->session[VOMMODE1_INDEX].tx_tap_point.playback_dai_data; + } else if (strnstr(pcm_id, VoMMode1_RX_CAPTURE_DAI_ID, size)) { + dai_data = + &prtd->session[VOMMODE1_INDEX].rx_tap_point.capture_dai_data; + } else if (strnstr(pcm_id, VoMMode1_RX_PLAYBACK_DAI_ID, size)) { + dai_data = + &prtd->session[VOMMODE1_INDEX].rx_tap_point.playback_dai_data; + /* check for VOiceMMode2 DAI */ + } else if (strnstr(pcm_id, VoMMode2_TX_CAPTURE_DAI_ID, size)) { + dai_data = + &prtd->session[VOMMODE2_INDEX].tx_tap_point.capture_dai_data; + } else if (strnstr(pcm_id, VoMMode2_TX_PLAYBACK_DAI_ID, size)) { + dai_data = + &prtd->session[VOMMODE2_INDEX].tx_tap_point.playback_dai_data; + } else if (strnstr(pcm_id, VoMMode2_RX_CAPTURE_DAI_ID, size)) { + dai_data = + &prtd->session[VOMMODE2_INDEX].rx_tap_point.capture_dai_data; + } else if (strnstr(pcm_id, VoMMode2_RX_PLAYBACK_DAI_ID, size)) { + dai_data = + &prtd->session[VOMMODE2_INDEX].rx_tap_point.playback_dai_data; + + } else { + pr_err("%s: Wrong dai id\n", __func__); + } + } + + return dai_data; +} + +static struct tap_point *hpcm_get_tappoint_data(char *pcm_id, + struct hpcm_drv *prtd) +{ + struct tap_point *tp = NULL; + size_t size = 0; + + if (pcm_id) { + size = strlen(pcm_id); + /* Check for Voice DAI */ + if (strnstr(pcm_id, VOICE_TX_CAPTURE_DAI_ID, size)) { + tp = &prtd->session[VOICE_INDEX].tx_tap_point; + } else if (strnstr(pcm_id, VOICE_TX_PLAYBACK_DAI_ID, size)) { + tp = &prtd->session[VOICE_INDEX].tx_tap_point; + } else if (strnstr(pcm_id, VOICE_RX_CAPTURE_DAI_ID, size)) { + tp = &prtd->session[VOICE_INDEX].rx_tap_point; + } else if (strnstr(pcm_id, VOICE_RX_PLAYBACK_DAI_ID, size)) { + tp = &prtd->session[VOICE_INDEX].rx_tap_point; + /* Check for VoLTE DAI */ + } else if (strnstr(pcm_id, VOLTE_TX_CAPTURE_DAI_ID, size)) { + tp = &prtd->session[VOLTE_INDEX].tx_tap_point; + } else if (strnstr(pcm_id, VOLTE_TX_PLAYBACK_DAI_ID, size)) { + tp = &prtd->session[VOLTE_INDEX].tx_tap_point; + } else if (strnstr(pcm_id, VOLTE_RX_CAPTURE_DAI_ID, size)) { + tp = &prtd->session[VOLTE_INDEX].rx_tap_point; + } else if (strnstr(pcm_id, VOLTE_RX_PLAYBACK_DAI_ID, size)) { + tp = &prtd->session[VOLTE_INDEX].rx_tap_point; + /* check for VoiceMMode1 */ + } else if (strnstr(pcm_id, VoMMode1_TX_CAPTURE_DAI_ID, size)) { + tp = &prtd->session[VOMMODE1_INDEX].tx_tap_point; + } else if (strnstr(pcm_id, VoMMode1_TX_PLAYBACK_DAI_ID, size)) { + tp = &prtd->session[VOMMODE1_INDEX].tx_tap_point; + } else if (strnstr(pcm_id, VoMMode1_RX_CAPTURE_DAI_ID, size)) { + tp = &prtd->session[VOMMODE1_INDEX].rx_tap_point; + } else if (strnstr(pcm_id, VoMMode1_RX_PLAYBACK_DAI_ID, size)) { + tp = &prtd->session[VOMMODE1_INDEX].rx_tap_point; + /* check for VoiceMMode2 */ + } else if (strnstr(pcm_id, VoMMode2_TX_CAPTURE_DAI_ID, size)) { + tp = &prtd->session[VOMMODE2_INDEX].tx_tap_point; + } else if (strnstr(pcm_id, VoMMode2_TX_PLAYBACK_DAI_ID, size)) { + tp = &prtd->session[VOMMODE2_INDEX].tx_tap_point; + } else if (strnstr(pcm_id, VoMMode2_RX_CAPTURE_DAI_ID, size)) { + tp = &prtd->session[VOMMODE2_INDEX].rx_tap_point; + } else if (strnstr(pcm_id, VoMMode2_RX_PLAYBACK_DAI_ID, size)) { + tp = &prtd->session[VOMMODE2_INDEX].rx_tap_point; + } else { + pr_err("%s: wrong dai id\n", __func__); + } + } + + return tp; +} + +static struct tappnt_mxr_data *hpcm_get_tappnt_mixer_data(char *pcm_id, + struct hpcm_drv *prtd) +{ + + if (strnstr(pcm_id, VOICE_TX_CAPTURE_DAI_ID, strlen(pcm_id)) || + strnstr(pcm_id, VOICE_TX_PLAYBACK_DAI_ID, strlen(pcm_id)) || + strnstr(pcm_id, VOLTE_TX_CAPTURE_DAI_ID, strlen(pcm_id)) || + strnstr(pcm_id, VOLTE_TX_PLAYBACK_DAI_ID, strlen(pcm_id)) || + strnstr(pcm_id, VoMMode1_TX_CAPTURE_DAI_ID, strlen(pcm_id)) || + strnstr(pcm_id, VoMMode1_TX_PLAYBACK_DAI_ID, strlen(pcm_id)) || + strnstr(pcm_id, VoMMode2_TX_CAPTURE_DAI_ID, strlen(pcm_id)) || + strnstr(pcm_id, VoMMode2_TX_PLAYBACK_DAI_ID, strlen(pcm_id))) { + return &prtd->mixer_conf.tx; + } else { + return &prtd->mixer_conf.rx; + } +} + +static int get_tappnt_value(char *pcm_id) +{ + + if (strnstr(pcm_id, VOICE_TX_CAPTURE_DAI_ID, strlen(pcm_id)) || + strnstr(pcm_id, VOICE_TX_PLAYBACK_DAI_ID, strlen(pcm_id)) || + strnstr(pcm_id, VOLTE_TX_CAPTURE_DAI_ID, strlen(pcm_id)) || + strnstr(pcm_id, VOLTE_TX_PLAYBACK_DAI_ID, strlen(pcm_id)) || + strnstr(pcm_id, VoMMode1_TX_CAPTURE_DAI_ID, strlen(pcm_id)) || + strnstr(pcm_id, VoMMode1_TX_PLAYBACK_DAI_ID, strlen(pcm_id)) || + strnstr(pcm_id, VoMMode2_TX_CAPTURE_DAI_ID, strlen(pcm_id)) || + strnstr(pcm_id, VoMMode2_TX_PLAYBACK_DAI_ID, strlen(pcm_id))) { + return TX; + } else { + return RX; + } +} + +static bool hpcm_all_dais_are_ready(uint16_t direction, struct tap_point *tp, + enum hpcm_state state) +{ + bool dais_started = false; + + /* + * Based on the direction set per tap point in the mixer control, + * all the dais per tap point should meet the required state for the + * commands such as vpcm_map_memory/vpcm_start to be executed. + */ + switch (direction) { + case VSS_IVPCM_TAP_POINT_DIR_OUT_IN: + if ((tp->playback_dai_data.state >= state) && + (tp->capture_dai_data.state >= state)) { + dais_started = true; + } + break; + + case VSS_IVPCM_TAP_POINT_DIR_IN: + if (tp->playback_dai_data.state >= state) + dais_started = true; + break; + + case VSS_IVPCM_TAP_POINT_DIR_OUT: + if (tp->capture_dai_data.state >= state) + dais_started = true; + break; + + default: + pr_err("invalid direction\n"); + } + + return dais_started; +} + +static void hpcm_create_free_queue(struct snd_dma_buffer *dma_buf, + struct dai_data *dai_data) +{ + struct hpcm_buf_node *buf_node = NULL; + int i = 0, offset = 0; + + for (i = 0; i < HPCM_MAX_Q_LEN; i++) { + buf_node = (void *)dma_buf->area + offset; + list_add_tail(&buf_node->list, + &dai_data->free_queue); + offset = offset + sizeof(struct hpcm_buf_node); + } +} + +static void hpcm_free_allocated_mem(struct hpcm_drv *prtd) +{ + phys_addr_t paddr = 0; + struct tap_point *txtp = NULL; + struct tap_point *rxtp = NULL; + struct session *sess = NULL; + + sess = &prtd->session[prtd->mixer_conf.sess_indx]; + txtp = &sess->tx_tap_point; + rxtp = &sess->rx_tap_point; + paddr = sess->sess_paddr; + + if (paddr) { + msm_audio_ion_free(sess->dma_buf); + sess->dma_buf = NULL; + msm_audio_ion_free(sess->tp_mem_table.dma_buf); + sess->tp_mem_table.dma_buf = NULL; + sess->sess_paddr = 0; + sess->sess_kvaddr = 0; + + txtp->capture_dai_data.vocpcm_ion_buffer.paddr = 0; + txtp->capture_dai_data.vocpcm_ion_buffer.kvaddr = 0; + + txtp->playback_dai_data.vocpcm_ion_buffer.paddr = 0; + txtp->playback_dai_data.vocpcm_ion_buffer.kvaddr = 0; + + rxtp->capture_dai_data.vocpcm_ion_buffer.paddr = 0; + rxtp->capture_dai_data.vocpcm_ion_buffer.kvaddr = 0; + + rxtp->playback_dai_data.vocpcm_ion_buffer.paddr = 0; + rxtp->playback_dai_data.vocpcm_ion_buffer.kvaddr = 0; + } else { + pr_debug("%s, paddr = 0, nothing to free\n", __func__); + } +} + +static void hpcm_unmap_and_free_shared_memory(struct hpcm_drv *prtd) + +{ + phys_addr_t paddr = 0; + char *sess_name = hpcm_get_sess_name(prtd->mixer_conf.sess_indx); + + if (prtd->mixer_conf.sess_indx >= 0) + paddr = prtd->session[prtd->mixer_conf.sess_indx].sess_paddr; + else + paddr = 0; + + if (paddr) { + voc_send_cvp_unmap_vocpcm_memory(voc_get_session_id(sess_name)); + hpcm_free_allocated_mem(prtd); + } else { + pr_debug("%s, paddr = 0, nothing to unmap/free\n", __func__); + } +} + +static int hpcm_map_vocpcm_memory(struct hpcm_drv *prtd) +{ + int ret = 0; + char *sess_name = hpcm_get_sess_name(prtd->mixer_conf.sess_indx); + struct session *sess = NULL; + + sess = &prtd->session[prtd->mixer_conf.sess_indx]; + + ret = voc_send_cvp_map_vocpcm_memory(voc_get_session_id(sess_name), + &sess->tp_mem_table, + sess->sess_paddr, + VHPCM_BLOCK_SIZE); + + return ret; +} + +static int hpcm_allocate_shared_memory(struct hpcm_drv *prtd) +{ + int result; + int ret = 0; + size_t mem_len; + size_t len; + struct tap_point *txtp = NULL; + struct tap_point *rxtp = NULL; + struct session *sess = NULL; + + sess = &prtd->session[prtd->mixer_conf.sess_indx]; + txtp = &sess->tx_tap_point; + rxtp = &sess->rx_tap_point; + + result = msm_audio_ion_alloc(&sess->dma_buf, + VHPCM_BLOCK_SIZE, + &sess->sess_paddr, + &mem_len, + &sess->sess_kvaddr); + if (result) { + pr_err("%s: msm_audio_ion_alloc error, rc = %d\n", + __func__, result); + sess->sess_paddr = 0; + sess->sess_kvaddr = 0; + ret = -ENOMEM; + goto done; + } + pr_debug("%s: Host PCM memory block allocated\n", __func__); + + /* Allocate mem_map_table for tap point */ + result = msm_audio_ion_alloc(&sess->tp_mem_table.dma_buf, + sizeof(struct vss_imemory_table_t), + &sess->tp_mem_table.phys, + &len, + &sess->tp_mem_table.data); + + if (result) { + pr_err("%s: msm_audio_ion_alloc error, rc = %d\n", + __func__, result); + msm_audio_ion_free(sess->dma_buf); + sess->dma_buf = NULL; + sess->sess_paddr = 0; + sess->sess_kvaddr = 0; + ret = -ENOMEM; + goto done; + } + pr_debug("%s: Host PCM memory table allocated\n", __func__); + + memset(sess->tp_mem_table.data, 0, + sizeof(struct vss_imemory_table_t)); + + sess->tp_mem_table.size = sizeof(struct vss_imemory_table_t); + + pr_debug("%s: data %pK phys %pK\n", __func__, + sess->tp_mem_table.data, &sess->tp_mem_table.phys); + + /* Split 4096 block into four 1024 byte blocks for each dai */ + txtp->capture_dai_data.vocpcm_ion_buffer.paddr = + sess->sess_paddr; + txtp->capture_dai_data.vocpcm_ion_buffer.kvaddr = + sess->sess_kvaddr; + + txtp->playback_dai_data.vocpcm_ion_buffer.paddr = + sess->sess_paddr + VHPCM_BLOCK_SIZE/4; + txtp->playback_dai_data.vocpcm_ion_buffer.kvaddr = + sess->sess_kvaddr + VHPCM_BLOCK_SIZE/4; + + rxtp->capture_dai_data.vocpcm_ion_buffer.paddr = + sess->sess_paddr + (VHPCM_BLOCK_SIZE/4) * 2; + rxtp->capture_dai_data.vocpcm_ion_buffer.kvaddr = + sess->sess_kvaddr + (VHPCM_BLOCK_SIZE/4) * 2; + + rxtp->playback_dai_data.vocpcm_ion_buffer.paddr = + sess->sess_paddr + (VHPCM_BLOCK_SIZE/4) * 3; + rxtp->playback_dai_data.vocpcm_ion_buffer.kvaddr = + sess->sess_kvaddr + (VHPCM_BLOCK_SIZE/4) * 3; + +done: + return ret; +} + +static int hpcm_start_vocpcm(char *pcm_id, struct hpcm_drv *prtd, + struct tap_point *tp) +{ + int indx = prtd->mixer_conf.sess_indx; + uint32_t *no_of_tp = &prtd->start_cmd.no_of_tapoints; + struct vss_ivpcm_tap_point *tap_pnt = &prtd->start_cmd.tap_pnt[0]; + uint32_t no_of_tp_req = 0; + char *sess_name = hpcm_get_sess_name(indx); + + if (prtd->mixer_conf.rx.enable) + no_of_tp_req++; + if (prtd->mixer_conf.tx.enable) + no_of_tp_req++; + + if (prtd->mixer_conf.rx.enable && (get_tappnt_value(pcm_id) == RX)) { + if (hpcm_all_dais_are_ready(prtd->mixer_conf.rx.direction, + tp, HPCM_PREPARED)) { + pr_debug("%s: RX conditions met\n", __func__); + tap_pnt[*no_of_tp].tap_point = + VSS_IVPCM_TAP_POINT_RX_DEFAULT; + tap_pnt[*no_of_tp].direction = + prtd->mixer_conf.rx.direction; + tap_pnt[*no_of_tp].sampling_rate = + prtd->mixer_conf.rx.sample_rate; + (*no_of_tp)++; + } + } + + if (prtd->mixer_conf.tx.enable && (get_tappnt_value(pcm_id) == TX)) { + if (hpcm_all_dais_are_ready(prtd->mixer_conf.tx.direction, + tp, HPCM_PREPARED)) { + pr_debug("%s: TX conditions met\n", __func__); + tap_pnt[*no_of_tp].tap_point = + VSS_IVPCM_TAP_POINT_TX_DEFAULT; + tap_pnt[*no_of_tp].direction = + prtd->mixer_conf.tx.direction; + tap_pnt[*no_of_tp].sampling_rate = + prtd->mixer_conf.tx.sample_rate; + (*no_of_tp)++; + } + } + + if ((prtd->mixer_conf.tx.enable || prtd->mixer_conf.rx.enable) && + *no_of_tp == no_of_tp_req) { + voc_send_cvp_start_vocpcm(voc_get_session_id(sess_name), + tap_pnt, *no_of_tp); + /* Reset the start command so that it is not called twice */ + memset(&prtd->start_cmd, 0, sizeof(struct start_cmd)); + } else { + pr_debug("%s: required pcm handles not opened yet\n", __func__); + } + + return 0; +} + +/* Playback path*/ +static void hpcm_copy_playback_data_from_queue(struct dai_data *dai_data, + uint32_t *len) +{ + struct hpcm_buf_node *buf_node = NULL; + unsigned long dsp_flags; + + if (dai_data->substream == NULL) + return; + + spin_lock_irqsave(&dai_data->dsp_lock, dsp_flags); + + if (!list_empty(&dai_data->filled_queue)) { + buf_node = list_first_entry(&dai_data->filled_queue, + struct hpcm_buf_node, list); + list_del(&buf_node->list); + *len = buf_node->frame.len; + memcpy((u8 *)dai_data->vocpcm_ion_buffer.kvaddr, + &buf_node->frame.voc_pkt[0], + buf_node->frame.len); + + list_add_tail(&buf_node->list, &dai_data->free_queue); + dai_data->pcm_irq_pos += dai_data->pcm_count; + spin_unlock_irqrestore(&dai_data->dsp_lock, dsp_flags); + snd_pcm_period_elapsed(dai_data->substream); + } else { + *len = 0; + spin_unlock_irqrestore(&dai_data->dsp_lock, dsp_flags); + pr_err("IN data not available\n"); + } + + wake_up(&dai_data->queue_wait); +} + +/* Capture path*/ +static void hpcm_copy_capture_data_to_queue(struct dai_data *dai_data, + uint32_t len) +{ + struct hpcm_buf_node *buf_node = NULL; + unsigned long dsp_flags; + + if (dai_data->substream == NULL) + return; + + /* Copy out buffer packet into free_queue */ + spin_lock_irqsave(&dai_data->dsp_lock, dsp_flags); + + if (!list_empty(&dai_data->free_queue)) { + buf_node = list_first_entry(&dai_data->free_queue, + struct hpcm_buf_node, list); + list_del(&buf_node->list); + buf_node->frame.len = len; + memcpy(&buf_node->frame.voc_pkt[0], + (uint8_t *)dai_data->vocpcm_ion_buffer.kvaddr, + buf_node->frame.len); + list_add_tail(&buf_node->list, &dai_data->filled_queue); + dai_data->pcm_irq_pos += dai_data->pcm_count; + spin_unlock_irqrestore(&dai_data->dsp_lock, dsp_flags); + snd_pcm_period_elapsed(dai_data->substream); + } else { + spin_unlock_irqrestore(&dai_data->dsp_lock, dsp_flags); + pr_err("OUTPUT data dropped\n"); + } + + wake_up(&dai_data->queue_wait); +} + +void hpcm_notify_evt_processing(uint8_t *data, char *session, + void *private_data) +{ + struct hpcm_drv *prtd = (struct hpcm_drv *)private_data; + struct vss_ivpcm_evt_notify_v2_t *notify_evt = + (struct vss_ivpcm_evt_notify_v2_t *)data; + struct vss_ivpcm_evt_push_buffer_v2_t push_buff_event; + struct tap_point *tp = NULL; + int in_buf_len = 0; + struct tappnt_mxr_data *tmd = NULL; + char *sess_name = hpcm_get_sess_name(prtd->mixer_conf.sess_indx); + + /* If it's not a timetick, it's a error notification, drop the event */ + if ((notify_evt->notify_mask & VSS_IVPCM_NOTIFY_MASK_TIMETICK) == 0) { + pr_err("%s: Error notification. mask=%d\n", __func__, + notify_evt->notify_mask); + return; + } + + if (notify_evt->tap_point == VSS_IVPCM_TAP_POINT_TX_DEFAULT) { + tp = &prtd->session[prtd->mixer_conf.sess_indx].tx_tap_point; + tmd = &prtd->mixer_conf.tx; + } else if (notify_evt->tap_point == VSS_IVPCM_TAP_POINT_RX_DEFAULT) { + tp = &prtd->session[prtd->mixer_conf.sess_indx].rx_tap_point; + tmd = &prtd->mixer_conf.rx; + } + + if (tp == NULL || tmd == NULL) { + pr_err("%s: tp = %pK or tmd = %pK is null\n", __func__, + tp, tmd); + + return; + } + + if (notify_evt->notify_mask & VSS_IVPCM_NOTIFY_MASK_OUTPUT_BUFFER) { + hpcm_copy_capture_data_to_queue(&tp->capture_dai_data, + notify_evt->filled_out_size); + } + + if (notify_evt->notify_mask & VSS_IVPCM_NOTIFY_MASK_INPUT_BUFFER) { + hpcm_copy_playback_data_from_queue(&tp->playback_dai_data, + &in_buf_len); + } + + switch (tmd->direction) { + /* + * When the dir is OUT_IN, for the first notify mask, pushbuf mask + * should be set to VSS_IVPCM_PUSH_BUFFER_MASK_OUTPUT_BUFFER since we + * atleast need one buffer's worth data before we can send IN buffer. + * For the consecutive notify evts, the push buf mask will set for both + * VSS_IVPCM_PUSH_BUFFER_MASK_OUTPUT_BUFFER and + * VSS_IVPCM_PUSH_BUFFER_MASK_IN_BUFFER. + */ + case VSS_IVPCM_TAP_POINT_DIR_OUT_IN: + if (notify_evt->notify_mask == + VSS_IVPCM_NOTIFY_MASK_TIMETICK) { + push_buff_event.push_buf_mask = + VSS_IVPCM_PUSH_BUFFER_MASK_OUTPUT_BUFFER; + } else { + push_buff_event.push_buf_mask = + VSS_IVPCM_PUSH_BUFFER_MASK_OUTPUT_BUFFER | + VSS_IVPCM_PUSH_BUFFER_MASK_INPUT_BUFFER; + } + break; + + case VSS_IVPCM_TAP_POINT_DIR_IN: + push_buff_event.push_buf_mask = + VSS_IVPCM_PUSH_BUFFER_MASK_INPUT_BUFFER; + break; + + case VSS_IVPCM_TAP_POINT_DIR_OUT: + push_buff_event.push_buf_mask = + VSS_IVPCM_PUSH_BUFFER_MASK_OUTPUT_BUFFER; + break; + } + + push_buff_event.tap_point = notify_evt->tap_point; + push_buff_event.out_buf_mem_address = + tp->capture_dai_data.vocpcm_ion_buffer.paddr; + push_buff_event.in_buf_mem_address = + tp->playback_dai_data.vocpcm_ion_buffer.paddr; + push_buff_event.sampling_rate = notify_evt->sampling_rate; + push_buff_event.num_in_channels = 1; + + /* + * ADSP must read and write from a cache aligned (128 byte) location, + * and in blocks of the cache alignment size. The 128 byte cache + * alignment requirement is guaranteed due to 4096 byte memory + * alignment requirement during memory allocation/mapping. The output + * buffer (ADSP write) size mask ensures that a 128 byte multiple + * worth of will be written. Internally, the input buffer (ADSP read) + * size will also be a multiple of 128 bytes. However it is the + * application's responsibility to ensure no other data is written in + * the specified length of memory. + */ + push_buff_event.out_buf_mem_size = ((notify_evt->request_buf_size) + + CACHE_ALIGNMENT_SIZE) & CACHE_ALIGNMENT_MASK; + push_buff_event.in_buf_mem_size = in_buf_len; + + voc_send_cvp_vocpcm_push_buf_evt(voc_get_session_id(sess_name), + &push_buff_event); +} + +static int msm_hpcm_configure_voice_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + + int tap_point = ucontrol->value.integer.value[0]; + uint16_t direction = ucontrol->value.integer.value[1]; + uint16_t sample_rate = ucontrol->value.integer.value[2]; + struct tappnt_mxr_data *tmd = NULL; + int ret = 0; + + mutex_lock(&hpcm_drv.lock); + pr_debug("%s: tap_point = %d direction = %d sample_rate = %d\n", + __func__, tap_point, direction, sample_rate); + + if (!hpcm_is_valid_config(VOICE_INDEX, tap_point, direction, + sample_rate)) { + pr_err("Invalid vpcm mixer control voice values\n"); + ret = -EINVAL; + goto done; + } + + if (tap_point == RX) + tmd = &hpcm_drv.mixer_conf.rx; + else + tmd = &hpcm_drv.mixer_conf.tx; + + tmd->enable = true; + tmd->direction = direction; + tmd->sample_rate = sample_rate; + hpcm_drv.mixer_conf.sess_indx = VOICE_INDEX; + +done: + mutex_unlock(&hpcm_drv.lock); + return ret; +} + +static int msm_hpcm_configure_vmmode1_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + + int tap_point = ucontrol->value.integer.value[0]; + uint16_t direction = ucontrol->value.integer.value[1]; + uint16_t sample_rate = ucontrol->value.integer.value[2]; + struct tappnt_mxr_data *tmd = NULL; + int ret = 0; + + mutex_lock(&hpcm_drv.lock); + pr_debug("%s: tap_point = %d direction = %d sample_rate = %d\n", + __func__, tap_point, direction, sample_rate); + + if (!hpcm_is_valid_config(VOMMODE1_INDEX, tap_point, direction, + sample_rate)) { + pr_err("Invalid vpcm mixer control voice values\n"); + ret = -EINVAL; + goto done; + } + + if (tap_point == RX) + tmd = &hpcm_drv.mixer_conf.rx; + else + tmd = &hpcm_drv.mixer_conf.tx; + + tmd->enable = true; + tmd->direction = direction; + tmd->sample_rate = sample_rate; + hpcm_drv.mixer_conf.sess_indx = VOMMODE1_INDEX; + +done: + mutex_unlock(&hpcm_drv.lock); + return ret; +} + +static int msm_hpcm_configure_vmmode2_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + + int tap_point = ucontrol->value.integer.value[0]; + uint16_t direction = ucontrol->value.integer.value[1]; + uint16_t sample_rate = ucontrol->value.integer.value[2]; + struct tappnt_mxr_data *tmd = NULL; + int ret = 0; + + mutex_lock(&hpcm_drv.lock); + pr_debug("%s: tap_point = %d direction = %d sample_rate = %d\n", + __func__, tap_point, direction, sample_rate); + + if (!hpcm_is_valid_config(VOMMODE2_INDEX, tap_point, direction, + sample_rate)) { + pr_err("Invalid vpcm mixer control voice values\n"); + ret = -EINVAL; + goto done; + } + + if (tap_point == RX) + tmd = &hpcm_drv.mixer_conf.rx; + else + tmd = &hpcm_drv.mixer_conf.tx; + + tmd->enable = true; + tmd->direction = direction; + tmd->sample_rate = sample_rate; + hpcm_drv.mixer_conf.sess_indx = VOMMODE2_INDEX; + +done: + mutex_unlock(&hpcm_drv.lock); + return ret; +} + +static int msm_hpcm_configure_volte_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + + int tap_point = ucontrol->value.integer.value[0]; + uint16_t direction = ucontrol->value.integer.value[1]; + uint16_t sample_rate = ucontrol->value.integer.value[2]; + struct tappnt_mxr_data *tmd = NULL; + int ret = 0; + + mutex_lock(&hpcm_drv.lock); + pr_debug("%s: tap_point=%d direction=%d sample_rate=%d\n", + __func__, tap_point, direction, sample_rate); + + if (!hpcm_is_valid_config(VOLTE_INDEX, tap_point, direction, + sample_rate)) { + pr_err("Invalid vpcm mixer control volte values\n"); + ret = -EINVAL; + goto done; + } + + if (tap_point == RX) + tmd = &hpcm_drv.mixer_conf.rx; + else + tmd = &hpcm_drv.mixer_conf.tx; + + tmd->enable = true; + tmd->direction = direction; + tmd->sample_rate = sample_rate; + hpcm_drv.mixer_conf.sess_indx = VOLTE_INDEX; + +done: + mutex_unlock(&hpcm_drv.lock); + return ret; + +} + +static struct snd_kcontrol_new msm_hpcm_controls[] = { + SOC_SINGLE_MULTI_EXT("HPCM_Voice tappoint direction samplerate", + SND_SOC_NOPM, 0, 16000, 0, 3, + NULL, msm_hpcm_configure_voice_put), + SOC_SINGLE_MULTI_EXT("HPCM_VoLTE tappoint direction samplerate", + SND_SOC_NOPM, 0, 16000, 0, 3, + NULL, msm_hpcm_configure_volte_put), + SOC_SINGLE_MULTI_EXT("HPCM_VMMode1 tappoint direction samplerate", + SND_SOC_NOPM, 0, 16000, 0, 3, + NULL, msm_hpcm_configure_vmmode1_put), + SOC_SINGLE_MULTI_EXT("HPCM_VMMode2 tappoint direction samplerate", + SND_SOC_NOPM, 0, 16000, 0, 3, + NULL, msm_hpcm_configure_vmmode2_put), +}; + +/* Sample rates supported */ +static unsigned int supported_sample_rates[] = {8000, 16000}; + +static struct snd_pcm_hw_constraint_list constraints_sample_rates = { + .count = ARRAY_SIZE(supported_sample_rates), + .list = supported_sample_rates, + .mask = 0, +}; + +static int msm_pcm_close(struct snd_pcm_substream *substream) +{ + int ret = 0; + struct list_head *ptr = NULL; + struct list_head *next = NULL; + struct hpcm_buf_node *buf_node = NULL; + struct snd_dma_buffer *dma_buf; + struct snd_pcm_runtime *runtime; + struct hpcm_drv *prtd; + unsigned long dsp_flags; + struct dai_data *dai_data = NULL; + struct tap_point *tp = NULL; + struct tappnt_mxr_data *tmd = NULL; + char *sess_name = NULL; + + if (substream == NULL) { + pr_err("substream is NULL\n"); + return -EINVAL; + } + + pr_debug("%s, %s\n", __func__, substream->pcm->id); + runtime = substream->runtime; + prtd = runtime->private_data; + sess_name = hpcm_get_sess_name(prtd->mixer_conf.sess_indx); + dai_data = hpcm_get_dai_data(substream->pcm->id, prtd); + + if (dai_data == NULL) { + pr_err("%s, dai_data is NULL\n", __func__); + + ret = -EINVAL; + goto done; + } + + wake_up(&dai_data->queue_wait); + mutex_lock(&prtd->lock); + + tmd = hpcm_get_tappnt_mixer_data(substream->pcm->id, prtd); + + tp = hpcm_get_tappoint_data(substream->pcm->id, prtd); + /* Send stop command */ + voc_send_cvp_stop_vocpcm(voc_get_session_id(sess_name)); + /* Memory unmap/free takes place only when called the first time */ + hpcm_unmap_and_free_shared_memory(prtd); + /* Unregister host PCM event callback function */ + voc_deregister_hpcm_evt_cb(); + /* Reset the cached start cmd */ + memset(&prtd->start_cmd, 0, sizeof(struct start_cmd)); + /* Release all buffer */ + pr_debug("%s: Release all buffer\n", __func__); + substream = dai_data->substream; + if (substream == NULL) { + pr_debug("%s: substream is NULL\n", __func__); + goto done; + } + dma_buf = &substream->dma_buffer; + if (dma_buf == NULL) { + pr_debug("%s: dma_buf is NULL\n", __func__); + goto done; + } + if (dma_buf->area != NULL) { + spin_lock_irqsave(&dai_data->dsp_lock, dsp_flags); + list_for_each_safe(ptr, next, &dai_data->filled_queue) { + buf_node = list_entry(ptr, + struct hpcm_buf_node, list); + list_del(&buf_node->list); + } + list_for_each_safe(ptr, next, &dai_data->free_queue) { + buf_node = list_entry(ptr, + struct hpcm_buf_node, list); + list_del(&buf_node->list); + } + spin_unlock_irqrestore(&dai_data->dsp_lock, dsp_flags); + dma_free_coherent(substream->pcm->card->dev, + runtime->hw.buffer_bytes_max, dma_buf->area, + dma_buf->addr); + dma_buf->area = NULL; + } + dai_data->substream = NULL; + dai_data->pcm_buf_pos = 0; + dai_data->pcm_count = 0; + dai_data->pcm_irq_pos = 0; + dai_data->pcm_size = 0; + dai_data->state = HPCM_CLOSED; + hpcm_reset_mixer_config(prtd); + +done: + mutex_unlock(&prtd->lock); + return ret; +} + +static int msm_pcm_playback_copy(struct snd_pcm_substream *substream, int a, + unsigned long hwoff, void __user *buf, + unsigned long fbytes) +{ + int ret = 0; + struct hpcm_buf_node *buf_node = NULL; + struct snd_pcm_runtime *runtime = substream->runtime; + struct hpcm_drv *prtd = runtime->private_data; + struct dai_data *dai_data = hpcm_get_dai_data(substream->pcm->id, prtd); + unsigned long dsp_flags; + + if (dai_data == NULL) { + pr_err("%s, dai_data is null\n", __func__); + + ret = -EINVAL; + goto done; + } + + ret = wait_event_interruptible_timeout(dai_data->queue_wait, + (!list_empty(&dai_data->free_queue) || + dai_data->state == HPCM_STOPPED), + 1 * HZ); + if (ret > 0) { + if (fbytes <= HPCM_MAX_VOC_PKT_SIZE) { + spin_lock_irqsave(&dai_data->dsp_lock, dsp_flags); + buf_node = + list_first_entry(&dai_data->free_queue, + struct hpcm_buf_node, list); + list_del(&buf_node->list); + spin_unlock_irqrestore(&dai_data->dsp_lock, dsp_flags); + ret = copy_from_user(&buf_node->frame.voc_pkt, buf, + fbytes); + buf_node->frame.len = fbytes; + spin_lock_irqsave(&dai_data->dsp_lock, dsp_flags); + list_add_tail(&buf_node->list, &dai_data->filled_queue); + spin_unlock_irqrestore(&dai_data->dsp_lock, dsp_flags); + } else { + pr_err("%s: Write cnt %lu is > HPCM_MAX_VOC_PKT_SIZE\n", + __func__, fbytes); + ret = -ENOMEM; + } + } else if (ret == 0) { + pr_err("%s: No free Playback buffer\n", __func__); + ret = -ETIMEDOUT; + } else { + pr_err("%s: playback copy was interrupted\n", __func__); + } + +done: + return ret; +} + +static int msm_pcm_capture_copy(struct snd_pcm_substream *substream, + int channel, unsigned long hwoff, + void __user *buf, unsigned long fbytes) +{ + int ret = 0; + struct hpcm_buf_node *buf_node = NULL; + struct snd_pcm_runtime *runtime = substream->runtime; + struct hpcm_drv *prtd = runtime->private_data; + struct dai_data *dai_data = hpcm_get_dai_data(substream->pcm->id, prtd); + unsigned long dsp_flags; + + if (dai_data == NULL) { + pr_err("%s, dai_data is null\n", __func__); + + ret = -EINVAL; + goto done; + } + + ret = wait_event_interruptible_timeout(dai_data->queue_wait, + (!list_empty(&dai_data->filled_queue) || + dai_data->state == HPCM_STOPPED), + 1 * HZ); + + if (ret > 0) { + if (fbytes <= HPCM_MAX_VOC_PKT_SIZE) { + spin_lock_irqsave(&dai_data->dsp_lock, dsp_flags); + buf_node = list_first_entry(&dai_data->filled_queue, + struct hpcm_buf_node, list); + list_del(&buf_node->list); + spin_unlock_irqrestore(&dai_data->dsp_lock, dsp_flags); + ret = copy_to_user(buf, &buf_node->frame.voc_pkt, + buf_node->frame.len); + if (ret) { + pr_err("%s: Copy to user returned %d\n", + __func__, ret); + ret = -EFAULT; + } + spin_lock_irqsave(&dai_data->dsp_lock, dsp_flags); + list_add_tail(&buf_node->list, &dai_data->free_queue); + spin_unlock_irqrestore(&dai_data->dsp_lock, dsp_flags); + + } else { + pr_err("%s: Read count %lu > HPCM_MAX_VOC_PKT_SIZE\n", + __func__, fbytes); + ret = -ENOMEM; + } + + } else if (ret == 0) { + pr_err("%s: No Caputre data available\n", __func__); + ret = -ETIMEDOUT; + } else { + pr_err("%s: Read was interrupted\n", __func__); + ret = -ERESTARTSYS; + } + +done: + return ret; +} + +static int msm_pcm_copy(struct snd_pcm_substream *substream, int channel, + unsigned long hwoff, void __user *buf, + unsigned long fbytes) +{ + int ret = 0; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + ret = msm_pcm_playback_copy(substream, channel, + hwoff, buf, fbytes); + else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + ret = msm_pcm_capture_copy(substream, channel, + hwoff, buf, fbytes); + + return ret; +} + +static snd_pcm_uframes_t msm_pcm_pointer(struct snd_pcm_substream *substream) +{ + struct dai_data *dai_data = NULL; + struct snd_pcm_runtime *runtime = substream->runtime; + struct hpcm_drv *prtd = runtime->private_data; + snd_pcm_uframes_t ret; + + dai_data = hpcm_get_dai_data(substream->pcm->id, prtd); + + if (dai_data == NULL) { + pr_err("%s, dai_data is null\n", __func__); + + ret = 0; + goto done; + } + + if (dai_data->pcm_irq_pos >= dai_data->pcm_size) + dai_data->pcm_irq_pos = 0; + + ret = bytes_to_frames(runtime, (dai_data->pcm_irq_pos)); + +done: + return ret; +} + +static int msm_pcm_trigger(struct snd_pcm_substream *substream, int cmd) +{ + int ret = 0; + struct snd_pcm_runtime *runtime = substream->runtime; + struct hpcm_drv *prtd = runtime->private_data; + struct dai_data *dai_data = + hpcm_get_dai_data(substream->pcm->id, prtd); + + if (dai_data == NULL) { + pr_err("%s, dai_data is null\n", __func__); + + ret = -EINVAL; + goto done; + } + + pr_debug("%s, %s\n", __func__, substream->pcm->id); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + pr_debug("SNDRV_PCM_TRIGGER_START\n"); + dai_data->state = HPCM_STARTED; + break; + + case SNDRV_PCM_TRIGGER_STOP: + pr_debug("SNDRV_PCM_TRIGGER_STOP\n"); + dai_data->state = HPCM_STOPPED; + break; + + default: + ret = -EINVAL; + break; + } + +done: + return ret; +} + +static int msm_pcm_prepare(struct snd_pcm_substream *substream) +{ + int ret = 0; + struct snd_pcm_runtime *runtime = substream->runtime; + struct hpcm_drv *prtd = runtime->private_data; + struct dai_data *dai_data = NULL; + struct tap_point *tp = NULL; + + pr_debug("%s, %s\n", __func__, substream->pcm->id); + mutex_lock(&prtd->lock); + + dai_data = hpcm_get_dai_data(substream->pcm->id, prtd); + + if (dai_data == NULL) { + pr_err("%s, dai_data is null\n", __func__); + + ret = -EINVAL; + goto done; + } + + dai_data->pcm_size = snd_pcm_lib_buffer_bytes(substream); + dai_data->pcm_count = snd_pcm_lib_period_bytes(substream); + dai_data->pcm_irq_pos = 0; + dai_data->pcm_buf_pos = 0; + dai_data->state = HPCM_PREPARED; + + /* Register event notify processing callback in prepare instead of + * init() as q6voice module's init() can be called at a later point + */ + voc_register_hpcm_evt_cb(hpcm_notify_evt_processing, &hpcm_drv); + + tp = hpcm_get_tappoint_data(substream->pcm->id, prtd); + if (tp != NULL) { + ret = hpcm_start_vocpcm(substream->pcm->id, prtd, tp); + if (ret) { + pr_err("error sending start cmd err=%d\n", ret); + goto done; + } + } else { + pr_err("%s tp is NULL\n", __func__); + } +done: + mutex_unlock(&prtd->lock); + return ret; +} + +static int msm_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_dma_buffer *dma_buf = &substream->dma_buffer; + struct hpcm_drv *prtd = (struct hpcm_drv *)runtime->private_data; + int ret = 0; + + pr_debug("%s: %s\n", __func__, substream->pcm->id); + mutex_lock(&prtd->lock); + + /* Allocate and map voice host PCM ion buffer */ + if (prtd->session[prtd->mixer_conf.sess_indx].sess_paddr == 0) { + ret = hpcm_allocate_shared_memory(prtd); + if (ret) { + pr_err("error creating shared memory err=%d\n", ret); + goto done; + } + + ret = hpcm_map_vocpcm_memory(prtd); + if (ret) { + pr_err("error mapping shared memory err=%d\n", ret); + hpcm_free_allocated_mem(prtd); + goto done; + } + } else { + pr_debug("%s, VHPCM memory allocation/mapping not performed\n" + , __func__); + } + + dma_buf->dev.type = SNDRV_DMA_TYPE_DEV; + dma_buf->dev.dev = substream->pcm->card->dev; + dma_buf->private_data = NULL; + + dma_buf->area = dma_alloc_coherent(substream->pcm->card->dev, + runtime->hw.buffer_bytes_max, + &dma_buf->addr, GFP_KERNEL); + + if (!dma_buf->area) { + pr_err("%s:MSM dma_alloc failed\n", __func__); + ret = -ENOMEM; + goto done; + } + + dma_buf->bytes = runtime->hw.buffer_bytes_max; + memset(dma_buf->area, 0, runtime->hw.buffer_bytes_max); + + hpcm_create_free_queue(dma_buf, + hpcm_get_dai_data(substream->pcm->id, prtd)); + + snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); + +done: + mutex_unlock(&prtd->lock); + return ret; +} + +static int msm_pcm_open(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct hpcm_drv *prtd = &hpcm_drv; + struct tappnt_mxr_data *tmd = NULL; + struct dai_data *dai_data = NULL; + int ret = 0; + int tp_val = 0; + + pr_debug("%s, %s\n", __func__, substream->pcm->id); + mutex_lock(&prtd->lock); + + dai_data = hpcm_get_dai_data(substream->pcm->id, prtd); + + if (dai_data == NULL) { + pr_err("%s, dai_data is null\n", __func__); + + ret = -EINVAL; + goto done; + } + + runtime->hw = msm_pcm_hardware; + + ret = snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + &constraints_sample_rates); + if (ret < 0) + pr_debug("snd_pcm_hw_constraint_list failed\n"); + + ret = snd_pcm_hw_constraint_integer(runtime, + SNDRV_PCM_HW_PARAM_PERIODS); + if (ret < 0) { + pr_debug("snd_pcm_hw_constraint_integer failed\n"); + goto done; + } + + tp_val = get_tappnt_value(substream->pcm->id); + tmd = hpcm_get_tappnt_mixer_data(substream->pcm->id, prtd); + + /* Check wheather the kcontrol values set are valid */ + if (!tmd || + !(tmd->enable) || + !hpcm_is_valid_config(prtd->mixer_conf.sess_indx, + tp_val, tmd->direction, + tmd->sample_rate)) { + ret = -EINVAL; + goto done; + } + + dai_data->substream = substream; + runtime->private_data = prtd; + +done: + mutex_unlock(&prtd->lock); + return ret; +} + +static const struct snd_pcm_ops msm_pcm_ops = { + .open = msm_pcm_open, + .hw_params = msm_pcm_hw_params, + .prepare = msm_pcm_prepare, + .trigger = msm_pcm_trigger, + .pointer = msm_pcm_pointer, + .copy_user = msm_pcm_copy, + .close = msm_pcm_close, +}; + +static int msm_asoc_pcm_new(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_card *card = rtd->card->snd_card; + + pr_debug("%s:\n", __func__); + if (!card->dev->coherent_dma_mask) + card->dev->coherent_dma_mask = DMA_BIT_MASK(32); + + return 0; +} + +static int msm_pcm_hpcm_probe(struct snd_soc_platform *platform) +{ + snd_soc_add_platform_controls(platform, msm_hpcm_controls, + ARRAY_SIZE(msm_hpcm_controls)); + + return 0; +} + +static struct snd_soc_platform_driver msm_soc_platform = { + .ops = &msm_pcm_ops, + .pcm_new = msm_asoc_pcm_new, + .probe = msm_pcm_hpcm_probe, +}; + +static int msm_pcm_probe(struct platform_device *pdev) +{ + + pr_info("%s: dev name %s\n", __func__, dev_name(&pdev->dev)); + return snd_soc_register_platform(&pdev->dev, &msm_soc_platform); +} + +static int msm_pcm_remove(struct platform_device *pdev) +{ + snd_soc_unregister_platform(&pdev->dev); + return 0; +} + +static const struct of_device_id msm_voice_host_pcm_dt_match[] = { + {.compatible = "qcom,msm-voice-host-pcm"}, + {} +}; +MODULE_DEVICE_TABLE(of, msm_voice_host_pcm_dt_match); + +static struct platform_driver msm_pcm_driver = { + .driver = { + .name = "msm-voice-host-pcm", + .owner = THIS_MODULE, + .of_match_table = msm_voice_host_pcm_dt_match, + }, + .probe = msm_pcm_probe, + .remove = msm_pcm_remove, +}; + +int __init msm_voice_host_init(void) +{ + int i = 0; + struct session *s = NULL; + + memset(&hpcm_drv, 0, sizeof(hpcm_drv)); + mutex_init(&hpcm_drv.lock); + + for (i = 0; i < MAX_SESSION; i++) { + s = &hpcm_drv.session[i]; + spin_lock_init(&s->rx_tap_point.capture_dai_data.dsp_lock); + spin_lock_init(&s->rx_tap_point.playback_dai_data.dsp_lock); + spin_lock_init(&s->tx_tap_point.capture_dai_data.dsp_lock); + spin_lock_init(&s->tx_tap_point.playback_dai_data.dsp_lock); + + init_waitqueue_head( + &s->rx_tap_point.capture_dai_data.queue_wait); + init_waitqueue_head( + &s->rx_tap_point.playback_dai_data.queue_wait); + init_waitqueue_head( + &s->tx_tap_point.capture_dai_data.queue_wait); + init_waitqueue_head( + &s->tx_tap_point.playback_dai_data.queue_wait); + + INIT_LIST_HEAD(&s->rx_tap_point.capture_dai_data.filled_queue); + INIT_LIST_HEAD(&s->rx_tap_point.capture_dai_data.free_queue); + INIT_LIST_HEAD(&s->rx_tap_point.playback_dai_data.filled_queue); + INIT_LIST_HEAD(&s->rx_tap_point.playback_dai_data.free_queue); + + INIT_LIST_HEAD(&s->tx_tap_point.capture_dai_data.filled_queue); + INIT_LIST_HEAD(&s->tx_tap_point.capture_dai_data.free_queue); + INIT_LIST_HEAD(&s->tx_tap_point.playback_dai_data.filled_queue); + INIT_LIST_HEAD(&s->tx_tap_point.playback_dai_data.free_queue); + } + + return platform_driver_register(&msm_pcm_driver); +} + +void msm_voice_host_exit(void) +{ + platform_driver_unregister(&msm_pcm_driver); +} + +MODULE_DESCRIPTION("PCM module platform driver"); +MODULE_LICENSE("GPL v2"); diff --git a/audio-kernel/asoc/msm-pcm-hostless.c b/audio-kernel/asoc/msm-pcm-hostless.c new file mode 100644 index 000000000..e2850a421 --- /dev/null +++ b/audio-kernel/asoc/msm-pcm-hostless.c @@ -0,0 +1,80 @@ +/* Copyright (c) 2011-2014, 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 + * 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 +#include +#include +#include +#include +#include +#include + + +static int msm_pcm_hostless_prepare(struct snd_pcm_substream *substream) +{ + if (!substream) { + pr_err("%s: invalid params\n", __func__); + return -EINVAL; + } + pm_qos_remove_request(&substream->latency_pm_qos_req); + return 0; +} + +static const struct snd_pcm_ops msm_pcm_hostless_ops = { + .prepare = msm_pcm_hostless_prepare +}; + +static struct snd_soc_platform_driver msm_soc_hostless_platform = { + .ops = &msm_pcm_hostless_ops, +}; + +static int msm_pcm_hostless_probe(struct platform_device *pdev) +{ + + pr_debug("%s: dev name %s\n", __func__, dev_name(&pdev->dev)); + return snd_soc_register_platform(&pdev->dev, + &msm_soc_hostless_platform); +} + +static int msm_pcm_hostless_remove(struct platform_device *pdev) +{ + snd_soc_unregister_platform(&pdev->dev); + return 0; +} + +static const struct of_device_id msm_pcm_hostless_dt_match[] = { + {.compatible = "qcom,msm-pcm-hostless"}, + {} +}; + +static struct platform_driver msm_pcm_hostless_driver = { + .driver = { + .name = "msm-pcm-hostless", + .owner = THIS_MODULE, + .of_match_table = msm_pcm_hostless_dt_match, + }, + .probe = msm_pcm_hostless_probe, + .remove = msm_pcm_hostless_remove, +}; + +int __init msm_pcm_hostless_init(void) +{ + return platform_driver_register(&msm_pcm_hostless_driver); +} + +void msm_pcm_hostless_exit(void) +{ + platform_driver_unregister(&msm_pcm_hostless_driver); +} + +MODULE_DESCRIPTION("Hostless platform driver"); +MODULE_LICENSE("GPL v2"); diff --git a/audio-kernel/asoc/msm-pcm-loopback-v2.c b/audio-kernel/asoc/msm-pcm-loopback-v2.c new file mode 100644 index 000000000..32c727aca --- /dev/null +++ b/audio-kernel/asoc/msm-pcm-loopback-v2.c @@ -0,0 +1,799 @@ +/* Copyright (c) 2013-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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "msm-pcm-routing-v2.h" + +#define LOOPBACK_VOL_MAX_STEPS 0x2000 +#define LOOPBACK_SESSION_MAX 4 + +static DEFINE_MUTEX(loopback_session_lock); +static const DECLARE_TLV_DB_LINEAR(loopback_rx_vol_gain, 0, + LOOPBACK_VOL_MAX_STEPS); + +struct msm_pcm_loopback { + struct snd_pcm_substream *playback_substream; + struct snd_pcm_substream *capture_substream; + + int instance; + + struct mutex lock; + + uint32_t samp_rate; + uint32_t channel_mode; + + int playback_start; + int capture_start; + int session_id; + struct audio_client *audio_client; + uint32_t volume; +}; + +struct fe_dai_session_map { + char stream_name[32]; + struct msm_pcm_loopback *loopback_priv; +}; + +static struct fe_dai_session_map session_map[LOOPBACK_SESSION_MAX] = { + { {}, NULL}, + { {}, NULL}, + { {}, NULL}, + { {}, NULL}, +}; + +static u32 hfp_tx_mute; + +struct msm_pcm_pdata { + int perf_mode; +}; + +static void stop_pcm(struct msm_pcm_loopback *pcm); +static int msm_pcm_loopback_get_session(struct snd_soc_pcm_runtime *rtd, + struct msm_pcm_loopback **pcm); + +static void msm_pcm_route_event_handler(enum msm_pcm_routing_event event, + void *priv_data) +{ + struct msm_pcm_loopback *pcm = priv_data; + + WARN_ON(!pcm); + + pr_debug("%s: event 0x%x\n", __func__, event); + + switch (event) { + case MSM_PCM_RT_EVT_DEVSWITCH: + q6asm_cmd(pcm->audio_client, CMD_PAUSE); + q6asm_cmd(pcm->audio_client, CMD_FLUSH); + q6asm_run(pcm->audio_client, 0, 0, 0); + /* fallthrough */ + default: + pr_err("%s: default event 0x%x\n", __func__, event); + break; + } +} + +static void msm_pcm_loopback_event_handler(uint32_t opcode, uint32_t token, + uint32_t *payload, void *priv) +{ + pr_debug("%s:\n", __func__); + switch (opcode) { + case APR_BASIC_RSP_RESULT: { + switch (payload[0]) { + break; + default: + break; + } + } + break; + default: + pr_err("%s: Not Supported Event opcode[0x%x]\n", + __func__, opcode); + break; + } +} + +static int msm_loopback_session_mute_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = hfp_tx_mute; + return 0; +} + +static int msm_loopback_session_mute_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret = 0, n = 0; + int mute = ucontrol->value.integer.value[0]; + struct msm_pcm_loopback *pcm = NULL; + + if ((mute < 0) || (mute > 1)) { + pr_err(" %s Invalid arguments", __func__); + ret = -EINVAL; + goto done; + } + + pr_debug("%s: mute=%d\n", __func__, mute); + hfp_tx_mute = mute; + for (n = 0; n < LOOPBACK_SESSION_MAX; n++) { + if (!strcmp(session_map[n].stream_name, "MultiMedia6")) + pcm = session_map[n].loopback_priv; + } + if (pcm && pcm->audio_client) { + ret = q6asm_set_mute(pcm->audio_client, mute); + if (ret < 0) + pr_err("%s: Send mute command failed rc=%d\n", + __func__, ret); + } +done: + return ret; +} + +static struct snd_kcontrol_new msm_loopback_controls[] = { + SOC_SINGLE_EXT("HFP TX Mute", SND_SOC_NOPM, 0, 1, 0, + msm_loopback_session_mute_get, + msm_loopback_session_mute_put), +}; + +static int msm_pcm_loopback_probe(struct snd_soc_platform *platform) +{ + snd_soc_add_platform_controls(platform, msm_loopback_controls, + ARRAY_SIZE(msm_loopback_controls)); + + return 0; +} +static int pcm_loopback_set_volume(struct msm_pcm_loopback *prtd, + uint32_t volume) +{ + int rc = -EINVAL; + + pr_debug("%s: Setting volume 0x%x\n", __func__, volume); + + if (prtd && prtd->audio_client) { + rc = q6asm_set_volume(prtd->audio_client, volume); + if (rc < 0) { + pr_err("%s: Send Volume command failed rc = %d\n", + __func__, rc); + return rc; + } + prtd->volume = volume; + } + return rc; +} + +static int msm_pcm_loopback_get_session(struct snd_soc_pcm_runtime *rtd, + struct msm_pcm_loopback **pcm) +{ + int ret = 0; + int n, index = -1; + + dev_dbg(rtd->platform->dev, "%s: stream %s\n", __func__, + rtd->dai_link->stream_name); + + mutex_lock(&loopback_session_lock); + for (n = 0; n < LOOPBACK_SESSION_MAX; n++) { + if (!strcmp(rtd->dai_link->stream_name, + session_map[n].stream_name)) { + *pcm = session_map[n].loopback_priv; + goto exit; + } + /* + * Store the min index value for allocating a new session. + * Here, if session stream name is not found in the + * existing entries after the loop iteration, then this + * index will be used to allocate the new session. + * This index variable is expected to point to the topmost + * available free session. + */ + if (!(session_map[n].stream_name[0]) && (index < 0)) + index = n; + } + + if (index < 0) { + dev_err(rtd->platform->dev, "%s: Max Sessions allocated\n", + __func__); + ret = -EAGAIN; + goto exit; + } + + session_map[index].loopback_priv = kzalloc( + sizeof(struct msm_pcm_loopback), GFP_KERNEL); + if (!session_map[index].loopback_priv) { + ret = -ENOMEM; + goto exit; + } + + strlcpy(session_map[index].stream_name, + rtd->dai_link->stream_name, + sizeof(session_map[index].stream_name)); + dev_dbg(rtd->platform->dev, "%s: stream %s index %d\n", + __func__, session_map[index].stream_name, index); + + mutex_init(&session_map[index].loopback_priv->lock); + *pcm = session_map[index].loopback_priv; +exit: + mutex_unlock(&loopback_session_lock); + return ret; +} + +static int msm_pcm_open(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); + struct msm_pcm_loopback *pcm = NULL; + int ret = 0; + uint16_t bits_per_sample = 16; + struct msm_pcm_routing_evt event; + struct asm_session_mtmx_strtr_param_window_v2_t asm_mtmx_strtr_window; + uint32_t param_id; + struct msm_pcm_pdata *pdata; + + ret = msm_pcm_loopback_get_session(rtd, &pcm); + if (ret) + return ret; + + mutex_lock(&pcm->lock); + + pcm->volume = 0x2000; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + pcm->playback_substream = substream; + else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + pcm->capture_substream = substream; + + pcm->instance++; + dev_dbg(rtd->platform->dev, "%s: pcm out open: %d,%d\n", __func__, + pcm->instance, substream->stream); + if (pcm->instance == 2) { + struct snd_soc_pcm_runtime *soc_pcm_rx = + pcm->playback_substream->private_data; + struct snd_soc_pcm_runtime *soc_pcm_tx = + pcm->capture_substream->private_data; + if (pcm->audio_client != NULL) + stop_pcm(pcm); + + pdata = (struct msm_pcm_pdata *) + dev_get_drvdata(rtd->platform->dev); + if (!pdata) { + dev_err(rtd->platform->dev, + "%s: platform data not populated\n", __func__); + mutex_unlock(&pcm->lock); + return -EINVAL; + } + + pcm->audio_client = q6asm_audio_client_alloc( + (app_cb)msm_pcm_loopback_event_handler, pcm); + if (!pcm->audio_client) { + dev_err(rtd->platform->dev, + "%s: Could not allocate memory\n", __func__); + mutex_unlock(&pcm->lock); + return -ENOMEM; + } + pcm->session_id = pcm->audio_client->session; + pcm->audio_client->perf_mode = pdata->perf_mode; + ret = q6asm_open_loopback_v2(pcm->audio_client, + bits_per_sample); + if (ret < 0) { + dev_err(rtd->platform->dev, + "%s: pcm out open failed\n", __func__); + q6asm_audio_client_free(pcm->audio_client); + mutex_unlock(&pcm->lock); + return -ENOMEM; + } + event.event_func = msm_pcm_route_event_handler; + event.priv_data = (void *) pcm; + msm_pcm_routing_reg_phy_stream(soc_pcm_tx->dai_link->id, + pcm->audio_client->perf_mode, + pcm->session_id, pcm->capture_substream->stream); + msm_pcm_routing_reg_phy_stream_v2(soc_pcm_rx->dai_link->id, + pcm->audio_client->perf_mode, + pcm->session_id, pcm->playback_substream->stream, + event); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + pcm->playback_substream = substream; + ret = pcm_loopback_set_volume(pcm, pcm->volume); + if (ret < 0) + dev_err(rtd->platform->dev, + "Error %d setting volume", ret); + } + /* Set to largest negative value */ + asm_mtmx_strtr_window.window_lsw = 0x00000000; + asm_mtmx_strtr_window.window_msw = 0x80000000; + param_id = ASM_SESSION_MTMX_STRTR_PARAM_RENDER_WINDOW_START_V2; + q6asm_send_mtmx_strtr_window(pcm->audio_client, + &asm_mtmx_strtr_window, + param_id); + /* Set to largest positive value */ + asm_mtmx_strtr_window.window_lsw = 0xffffffff; + asm_mtmx_strtr_window.window_msw = 0x7fffffff; + param_id = ASM_SESSION_MTMX_STRTR_PARAM_RENDER_WINDOW_END_V2; + q6asm_send_mtmx_strtr_window(pcm->audio_client, + &asm_mtmx_strtr_window, + param_id); + } + dev_info(rtd->platform->dev, "%s: Instance = %d, Stream ID = %s\n", + __func__, pcm->instance, substream->pcm->id); + runtime->private_data = pcm; + + mutex_unlock(&pcm->lock); + + return 0; +} + +static void stop_pcm(struct msm_pcm_loopback *pcm) +{ + struct snd_soc_pcm_runtime *soc_pcm_rx; + struct snd_soc_pcm_runtime *soc_pcm_tx; + + if (pcm->audio_client == NULL) + return; + q6asm_cmd(pcm->audio_client, CMD_CLOSE); + + if (pcm->playback_substream != NULL) { + soc_pcm_rx = pcm->playback_substream->private_data; + msm_pcm_routing_dereg_phy_stream(soc_pcm_rx->dai_link->id, + SNDRV_PCM_STREAM_PLAYBACK); + } + if (pcm->capture_substream != NULL) { + soc_pcm_tx = pcm->capture_substream->private_data; + msm_pcm_routing_dereg_phy_stream(soc_pcm_tx->dai_link->id, + SNDRV_PCM_STREAM_CAPTURE); + } + q6asm_audio_client_free(pcm->audio_client); + pcm->audio_client = NULL; +} + +static int msm_pcm_close(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct msm_pcm_loopback *pcm = runtime->private_data; + struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); + int ret = 0, n; + bool found = false; + + mutex_lock(&pcm->lock); + + dev_dbg(rtd->platform->dev, "%s: end pcm call:%d\n", + __func__, substream->stream); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + pcm->playback_start = 0; + else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + pcm->capture_start = 0; + + pcm->instance--; + if (!pcm->playback_start || !pcm->capture_start) { + dev_dbg(rtd->platform->dev, "%s: end pcm call\n", __func__); + stop_pcm(pcm); + } + + if (!pcm->instance) { + mutex_lock(&loopback_session_lock); + for (n = 0; n < LOOPBACK_SESSION_MAX; n++) { + if (!strcmp(rtd->dai_link->stream_name, + session_map[n].stream_name)) { + found = true; + break; + } + } + if (found) { + memset(session_map[n].stream_name, 0, + sizeof(session_map[n].stream_name)); + mutex_unlock(&pcm->lock); + mutex_destroy(&session_map[n].loopback_priv->lock); + session_map[n].loopback_priv = NULL; + kfree(pcm); + dev_dbg(rtd->platform->dev, "%s: stream freed %s\n", + __func__, rtd->dai_link->stream_name); + mutex_unlock(&loopback_session_lock); + return 0; + } + mutex_unlock(&loopback_session_lock); + } + mutex_unlock(&pcm->lock); + return ret; +} + +static int msm_pcm_prepare(struct snd_pcm_substream *substream) +{ + int ret = 0; + struct snd_pcm_runtime *runtime = substream->runtime; + struct msm_pcm_loopback *pcm = runtime->private_data; + struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); + + mutex_lock(&pcm->lock); + + dev_dbg(rtd->platform->dev, "%s: ASM loopback stream:%d\n", + __func__, substream->stream); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + if (!pcm->playback_start) + pcm->playback_start = 1; + } else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + if (!pcm->capture_start) + pcm->capture_start = 1; + } + mutex_unlock(&pcm->lock); + + return ret; +} + +static int msm_pcm_trigger(struct snd_pcm_substream *substream, int cmd) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct msm_pcm_loopback *pcm = runtime->private_data; + struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + dev_dbg(rtd->platform->dev, + "%s: playback_start:%d,capture_start:%d\n", __func__, + pcm->playback_start, pcm->capture_start); + if (pcm->playback_start && pcm->capture_start) + q6asm_run_nowait(pcm->audio_client, 0, 0, 0); + break; + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_STOP: + dev_dbg(rtd->platform->dev, + "%s:Pause/Stop - playback_start:%d,capture_start:%d\n", + __func__, pcm->playback_start, pcm->capture_start); + if (pcm->playback_start && pcm->capture_start) + q6asm_cmd_nowait(pcm->audio_client, CMD_PAUSE); + break; + default: + pr_err("%s: default cmd %d\n", __func__, cmd); + break; + } + + return 0; +} + +static const struct snd_pcm_ops msm_pcm_ops = { + .open = msm_pcm_open, + .close = msm_pcm_close, + .prepare = msm_pcm_prepare, + .trigger = msm_pcm_trigger, +}; + +static int msm_pcm_volume_ctl_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int rc = 0; + struct snd_pcm_volume *vol = kcontrol->private_data; + struct snd_pcm_substream *substream = vol->pcm->streams[0].substream; + struct msm_pcm_loopback *prtd; + int volume = ucontrol->value.integer.value[0]; + + pr_debug("%s: volume : 0x%x\n", __func__, volume); + if ((!substream) || (!substream->runtime)) { + pr_err("%s substream or runtime not found\n", __func__); + rc = -ENODEV; + goto exit; + } + prtd = substream->runtime->private_data; + if (!prtd) { + rc = -ENODEV; + goto exit; + } + rc = pcm_loopback_set_volume(prtd, volume); + +exit: + return rc; +} + +static int msm_pcm_volume_ctl_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int rc = 0; + struct snd_pcm_volume *vol = snd_kcontrol_chip(kcontrol); + struct snd_pcm_substream *substream = + vol->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; + struct msm_pcm_loopback *prtd; + + pr_debug("%s\n", __func__); + if ((!substream) || (!substream->runtime)) { + pr_debug("%s substream or runtime not found\n", __func__); + rc = -ENODEV; + goto exit; + } + prtd = substream->runtime->private_data; + if (!prtd) { + rc = -ENODEV; + goto exit; + } + ucontrol->value.integer.value[0] = prtd->volume; + +exit: + return rc; +} + +static int msm_pcm_add_volume_controls(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_pcm *pcm = rtd->pcm->streams[0].pcm; + struct snd_pcm_volume *volume_info; + struct snd_kcontrol *kctl; + int ret = 0; + + dev_dbg(rtd->dev, "%s, Volume cntrl add\n", __func__); + ret = snd_pcm_add_volume_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK, + NULL, 1, + rtd->dai_link->id, + &volume_info); + if (ret < 0) + return ret; + kctl = volume_info->kctl; + kctl->put = msm_pcm_volume_ctl_put; + kctl->get = msm_pcm_volume_ctl_get; + kctl->tlv.p = loopback_rx_vol_gain; + return 0; +} + +static int msm_pcm_playback_app_type_cfg_ctl_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u64 fe_id = kcontrol->private_value; + int session_type = SESSION_TYPE_RX; + int be_id = ucontrol->value.integer.value[3]; + struct msm_pcm_stream_app_type_cfg cfg_data = {0, 0, 48000}; + int ret = 0; + + cfg_data.app_type = ucontrol->value.integer.value[0]; + cfg_data.acdb_dev_id = ucontrol->value.integer.value[1]; + if (ucontrol->value.integer.value[2] != 0) + cfg_data.sample_rate = ucontrol->value.integer.value[2]; + pr_debug("%s: fe_id- %llu session_type- %d be_id- %d app_type- %d acdb_dev_id- %d sample_rate- %d\n", + __func__, fe_id, session_type, be_id, + cfg_data.app_type, cfg_data.acdb_dev_id, cfg_data.sample_rate); + ret = msm_pcm_routing_reg_stream_app_type_cfg(fe_id, session_type, + be_id, &cfg_data); + if (ret < 0) + pr_err("%s: msm_pcm_routing_reg_stream_app_type_cfg failed returned %d\n", + __func__, ret); + + return ret; +} + +static int msm_pcm_playback_app_type_cfg_ctl_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u64 fe_id = kcontrol->private_value; + int session_type = SESSION_TYPE_RX; + int be_id = 0; + struct msm_pcm_stream_app_type_cfg cfg_data = {0}; + int ret = 0; + + ret = msm_pcm_routing_get_stream_app_type_cfg(fe_id, session_type, + &be_id, &cfg_data); + if (ret < 0) { + pr_err("%s: msm_pcm_routing_get_stream_app_type_cfg failed returned %d\n", + __func__, ret); + goto done; + } + + ucontrol->value.integer.value[0] = cfg_data.app_type; + ucontrol->value.integer.value[1] = cfg_data.acdb_dev_id; + ucontrol->value.integer.value[2] = cfg_data.sample_rate; + ucontrol->value.integer.value[3] = be_id; + pr_debug("%s: fedai_id %llu, session_type %d, be_id %d, app_type %d, acdb_dev_id %d, sample_rate %d\n", + __func__, fe_id, session_type, be_id, + cfg_data.app_type, cfg_data.acdb_dev_id, cfg_data.sample_rate); +done: + return ret; +} + +static int msm_pcm_capture_app_type_cfg_ctl_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u64 fe_id = kcontrol->private_value; + int session_type = SESSION_TYPE_TX; + int be_id = ucontrol->value.integer.value[3]; + struct msm_pcm_stream_app_type_cfg cfg_data = {0, 0, 48000}; + int ret = 0; + + cfg_data.app_type = ucontrol->value.integer.value[0]; + cfg_data.acdb_dev_id = ucontrol->value.integer.value[1]; + if (ucontrol->value.integer.value[2] != 0) + cfg_data.sample_rate = ucontrol->value.integer.value[2]; + pr_debug("%s: fe_id- %llu session_type- %d be_id- %d app_type- %d acdb_dev_id- %d sample_rate- %d\n", + __func__, fe_id, session_type, be_id, + cfg_data.app_type, cfg_data.acdb_dev_id, cfg_data.sample_rate); + ret = msm_pcm_routing_reg_stream_app_type_cfg(fe_id, session_type, + be_id, &cfg_data); + if (ret < 0) + pr_err("%s: msm_pcm_routing_reg_stream_app_type_cfg failed returned %d\n", + __func__, ret); + + return ret; +} + +static int msm_pcm_capture_app_type_cfg_ctl_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u64 fe_id = kcontrol->private_value; + int session_type = SESSION_TYPE_TX; + int be_id = 0; + struct msm_pcm_stream_app_type_cfg cfg_data = {0}; + int ret = 0; + + ret = msm_pcm_routing_get_stream_app_type_cfg(fe_id, session_type, + &be_id, &cfg_data); + if (ret < 0) { + pr_err("%s: msm_pcm_routing_get_stream_app_type_cfg failed returned %d\n", + __func__, ret); + goto done; + } + + ucontrol->value.integer.value[0] = cfg_data.app_type; + ucontrol->value.integer.value[1] = cfg_data.acdb_dev_id; + ucontrol->value.integer.value[2] = cfg_data.sample_rate; + ucontrol->value.integer.value[3] = be_id; + pr_debug("%s: fedai_id %llu, session_type %d, be_id %d, app_type %d, acdb_dev_id %d, sample_rate %d\n", + __func__, fe_id, session_type, be_id, + cfg_data.app_type, cfg_data.acdb_dev_id, cfg_data.sample_rate); +done: + return ret; +} + +static int msm_pcm_add_app_type_controls(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_pcm *pcm = rtd->pcm->streams[0].pcm; + struct snd_pcm_usr *app_type_info; + struct snd_kcontrol *kctl; + const char *playback_mixer_ctl_name = "Audio Stream"; + const char *capture_mixer_ctl_name = "Audio Stream Capture"; + const char *deviceNo = "NN"; + const char *suffix = "App Type Cfg"; + int ctl_len, ret = 0; + + if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) { + ctl_len = strlen(playback_mixer_ctl_name) + 1 + + strlen(deviceNo) + 1 + strlen(suffix) + 1; + pr_debug("%s: Playback app type cntrl add\n", __func__); + ret = snd_pcm_add_usr_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK, + NULL, 1, ctl_len, rtd->dai_link->id, + &app_type_info); + if (ret < 0) + return ret; + kctl = app_type_info->kctl; + snprintf(kctl->id.name, ctl_len, "%s %d %s", + playback_mixer_ctl_name, rtd->pcm->device, suffix); + kctl->put = msm_pcm_playback_app_type_cfg_ctl_put; + kctl->get = msm_pcm_playback_app_type_cfg_ctl_get; + } + + if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) { + ctl_len = strlen(capture_mixer_ctl_name) + 1 + + strlen(deviceNo) + 1 + strlen(suffix) + 1; + pr_debug("%s: Capture app type cntrl add\n", __func__); + ret = snd_pcm_add_usr_ctls(pcm, SNDRV_PCM_STREAM_CAPTURE, + NULL, 1, ctl_len, rtd->dai_link->id, + &app_type_info); + if (ret < 0) + return ret; + kctl = app_type_info->kctl; + snprintf(kctl->id.name, ctl_len, "%s %d %s", + capture_mixer_ctl_name, rtd->pcm->device, suffix); + kctl->put = msm_pcm_capture_app_type_cfg_ctl_put; + kctl->get = msm_pcm_capture_app_type_cfg_ctl_get; + } + + return 0; +} + +static int msm_pcm_add_controls(struct snd_soc_pcm_runtime *rtd) +{ + int ret = 0; + + pr_debug("%s\n", __func__); + ret = msm_pcm_add_volume_controls(rtd); + if (ret) + pr_err("%s: pcm add volume controls failed:%d\n", + __func__, ret); + ret = msm_pcm_add_app_type_controls(rtd); + if (ret) + pr_err("%s: pcm add app type controls failed:%d\n", + __func__, ret); + return ret; +} + +static int msm_asoc_pcm_new(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_card *card = rtd->card->snd_card; + int ret = 0; + + if (!card->dev->coherent_dma_mask) + card->dev->coherent_dma_mask = DMA_BIT_MASK(32); + + ret = msm_pcm_add_controls(rtd); + if (ret) + dev_err(rtd->dev, "%s, kctl add failed\n", __func__); + return ret; +} + +static struct snd_soc_platform_driver msm_soc_platform = { + .ops = &msm_pcm_ops, + .pcm_new = msm_asoc_pcm_new, + .probe = msm_pcm_loopback_probe, +}; + +static int msm_pcm_probe(struct platform_device *pdev) +{ + struct msm_pcm_pdata *pdata; + + dev_dbg(&pdev->dev, "%s: dev name %s\n", + __func__, dev_name(&pdev->dev)); + + pdata = kzalloc(sizeof(struct msm_pcm_pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + if (of_property_read_bool(pdev->dev.of_node, + "qcom,msm-pcm-loopback-low-latency")) + pdata->perf_mode = LOW_LATENCY_PCM_MODE; + else + pdata->perf_mode = LEGACY_PCM_MODE; + + dev_set_drvdata(&pdev->dev, pdata); + + return snd_soc_register_platform(&pdev->dev, + &msm_soc_platform); +} + +static int msm_pcm_remove(struct platform_device *pdev) +{ + snd_soc_unregister_platform(&pdev->dev); + return 0; +} + +static const struct of_device_id msm_pcm_loopback_dt_match[] = { + {.compatible = "qcom,msm-pcm-loopback"}, + {} +}; + +static struct platform_driver msm_pcm_driver = { + .driver = { + .name = "msm-pcm-loopback", + .owner = THIS_MODULE, + .of_match_table = msm_pcm_loopback_dt_match, + }, + .probe = msm_pcm_probe, + .remove = msm_pcm_remove, +}; + +int __init msm_pcm_loopback_init(void) +{ + return platform_driver_register(&msm_pcm_driver); +} + +void msm_pcm_loopback_exit(void) +{ + platform_driver_unregister(&msm_pcm_driver); +} + +MODULE_DESCRIPTION("PCM loopback platform driver"); +MODULE_LICENSE("GPL v2"); diff --git a/audio-kernel/asoc/msm-pcm-q6-noirq.c b/audio-kernel/asoc/msm-pcm-q6-noirq.c new file mode 100644 index 000000000..be34903d9 --- /dev/null +++ b/audio-kernel/asoc/msm-pcm-q6-noirq.c @@ -0,0 +1,1336 @@ +/* Copyright (c) 2016-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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "msm-pcm-q6-v2.h" +#include "msm-pcm-routing-v2.h" + +#define PCM_MASTER_VOL_MAX_STEPS 0x2000 +static const DECLARE_TLV_DB_LINEAR(msm_pcm_vol_gain, 0, + PCM_MASTER_VOL_MAX_STEPS); + +struct snd_msm { + struct snd_card *card; + struct snd_pcm *pcm; +}; + +#define CMD_EOS_MIN_TIMEOUT_LENGTH 50 +#define CMD_EOS_TIMEOUT_MULTIPLIER (HZ * 50) + +#define ATRACE_END() \ + trace_printk("tracing_mark_write: E\n") +#define ATRACE_BEGIN(name) \ + trace_printk("tracing_mark_write: B|%d|%s\n", current->tgid, name) +#define ATRACE_FUNC() ATRACE_BEGIN(__func__) +#define ATRACE_INT(name, value) \ + trace_printk("tracing_mark_write: C|%d|%s|%d\n", \ + current->tgid, name, (int)(value)) + +#define SIO_PLAYBACK_MAX_PERIOD_SIZE PLAYBACK_MAX_PERIOD_SIZE +#define SIO_PLAYBACK_MIN_PERIOD_SIZE 48 +#define SIO_PLAYBACK_MAX_NUM_PERIODS 512 +#define SIO_PLAYBACK_MIN_NUM_PERIODS PLAYBACK_MIN_NUM_PERIODS +#define SIO_PLAYBACK_MIN_BYTES (SIO_PLAYBACK_MIN_NUM_PERIODS * \ + SIO_PLAYBACK_MIN_PERIOD_SIZE) + +#define SIO_PLAYBACK_MAX_BYTES ((SIO_PLAYBACK_MAX_NUM_PERIODS) * \ + (SIO_PLAYBACK_MAX_PERIOD_SIZE)) + +#define SIO_CAPTURE_MAX_PERIOD_SIZE CAPTURE_MAX_PERIOD_SIZE +#define SIO_CAPTURE_MIN_PERIOD_SIZE 48 +#define SIO_CAPTURE_MAX_NUM_PERIODS 512 +#define SIO_CAPTURE_MIN_NUM_PERIODS CAPTURE_MIN_NUM_PERIODS + +#define SIO_CAPTURE_MIN_BYTES (SIO_CAPTURE_MIN_NUM_PERIODS * \ + SIO_CAPTURE_MIN_PERIOD_SIZE) + +#define SIO_CAPTURE_MAX_BYTES (SIO_CAPTURE_MAX_NUM_PERIODS * \ + SIO_CAPTURE_MAX_PERIOD_SIZE) + +static struct snd_pcm_hardware msm_pcm_hardware_playback = { + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_NO_PERIOD_WAKEUP | + SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE), + .rates = SNDRV_PCM_RATE_8000_192000, + .rate_min = 8000, + .rate_max = 192000, + .channels_min = 1, + .channels_max = 8, + .buffer_bytes_max = SIO_PLAYBACK_MAX_NUM_PERIODS * + SIO_PLAYBACK_MAX_PERIOD_SIZE, + .period_bytes_min = SIO_PLAYBACK_MIN_PERIOD_SIZE, + .period_bytes_max = SIO_PLAYBACK_MAX_PERIOD_SIZE, + .periods_min = SIO_PLAYBACK_MIN_NUM_PERIODS, + .periods_max = SIO_PLAYBACK_MAX_NUM_PERIODS, + .fifo_size = 0, +}; + +static struct snd_pcm_hardware msm_pcm_hardware_capture = { + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_NO_PERIOD_WAKEUP | + SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE), + .rates = SNDRV_PCM_RATE_8000_48000, + .rate_min = 8000, + .rate_max = 48000, + .channels_min = 1, + .channels_max = 4, + .buffer_bytes_max = SIO_CAPTURE_MAX_NUM_PERIODS * + SIO_CAPTURE_MAX_PERIOD_SIZE, + .period_bytes_min = SIO_CAPTURE_MIN_PERIOD_SIZE, + .period_bytes_max = SIO_CAPTURE_MAX_PERIOD_SIZE, + .periods_min = SIO_CAPTURE_MIN_NUM_PERIODS, + .periods_max = SIO_CAPTURE_MAX_NUM_PERIODS, + .fifo_size = 0, +}; + +/* Conventional and unconventional sample rate supported */ +static unsigned int supported_sample_rates[] = { + 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000, + 88200, 96000, 176400, 192000 +}; + +static struct snd_pcm_hw_constraint_list constraints_sample_rates = { + .count = ARRAY_SIZE(supported_sample_rates), + .list = supported_sample_rates, + .mask = 0, +}; + +static unsigned long msm_pcm_fe_topology[MSM_FRONTEND_DAI_MAX]; + +/* default value is DTS (i.e read from device tree) */ +static char const *msm_pcm_fe_topology_text[] = { + "DTS", "ULL", "ULL_PP", "LL" }; + +static const struct soc_enum msm_pcm_fe_topology_enum[] = { + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(msm_pcm_fe_topology_text), + msm_pcm_fe_topology_text), +}; + +static void event_handler(uint32_t opcode, + uint32_t token, uint32_t *payload, void *priv) +{ + uint32_t *ptrmem = (uint32_t *)payload; + + switch (opcode) { + case ASM_DATA_EVENT_WATERMARK: + pr_debug("%s: Watermark level = 0x%08x\n", __func__, *ptrmem); + break; + case APR_BASIC_RSP_RESULT: + pr_debug("%s: Payload = [0x%x]stat[0x%x]\n", + __func__, payload[0], payload[1]); + switch (payload[0]) { + case ASM_SESSION_CMD_RUN_V2: + case ASM_SESSION_CMD_PAUSE: + case ASM_STREAM_CMD_FLUSH: + break; + default: + break; + } + break; + default: + pr_debug("Not Supported Event opcode[0x%x]\n", opcode); + break; + } +} + +static int msm_pcm_open(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct msm_audio *prtd; + int ret = 0; + + prtd = kzalloc(sizeof(struct msm_audio), GFP_KERNEL); + + if (prtd == NULL) + return -ENOMEM; + + prtd->substream = substream; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + runtime->hw = msm_pcm_hardware_playback; + else + runtime->hw = msm_pcm_hardware_capture; + + ret = snd_pcm_hw_constraint_list(runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + &constraints_sample_rates); + if (ret) + pr_info("snd_pcm_hw_constraint_list failed\n"); + + ret = snd_pcm_hw_constraint_integer(runtime, + SNDRV_PCM_HW_PARAM_PERIODS); + if (ret) + pr_info("snd_pcm_hw_constraint_integer failed\n"); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + ret = snd_pcm_hw_constraint_minmax(runtime, + SNDRV_PCM_HW_PARAM_BUFFER_BYTES, + SIO_PLAYBACK_MIN_BYTES, + SIO_PLAYBACK_MAX_BYTES); + if (ret) { + pr_info("%s: P buffer bytes minmax constraint ret %d\n", + __func__, ret); + } + } else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + ret = snd_pcm_hw_constraint_minmax(runtime, + SNDRV_PCM_HW_PARAM_BUFFER_BYTES, + SIO_CAPTURE_MIN_BYTES, + SIO_CAPTURE_MAX_BYTES); + if (ret) { + pr_info("%s: C buffer bytes minmax constraint ret %d\n", + __func__, ret); + } + } + + ret = snd_pcm_hw_constraint_step(runtime, 0, + SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 32); + if (ret) { + pr_err("%s: Constraint for period bytes step ret = %d\n", + __func__, ret); + } + ret = snd_pcm_hw_constraint_step(runtime, 0, + SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 32); + if (ret) { + pr_err("%s: Constraint for buffer bytes step ret = %d\n", + __func__, ret); + } + prtd->audio_client = q6asm_audio_client_alloc( + (app_cb)event_handler, prtd); + if (!prtd->audio_client) { + pr_err("%s: client alloc failed\n", __func__); + ret = -ENOMEM; + goto fail_cmd; + } + prtd->dsp_cnt = 0; + prtd->set_channel_map = false; + runtime->private_data = prtd; + return 0; + +fail_cmd: + kfree(prtd); + return ret; +} + +static int msm_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) + +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_soc_pcm_runtime *soc_prtd = substream->private_data; + struct msm_audio *prtd = runtime->private_data; + struct msm_plat_data *pdata; + struct snd_dma_buffer *dma_buf = &substream->dma_buffer; + struct audio_buffer *buf; + struct shared_io_config config; + uint16_t sample_word_size; + uint16_t bits_per_sample; + int ret; + int dir = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? IN : OUT; + unsigned long topology; + int perf_mode; + bool use_default_chmap = true; + char *chmap = NULL; + + pdata = (struct msm_plat_data *) + dev_get_drvdata(soc_prtd->platform->dev); + if (!pdata) { + ret = -EINVAL; + pr_err("%s: platform data not populated ret: %d\n", __func__, + ret); + return ret; + } + + topology = msm_pcm_fe_topology[soc_prtd->dai_link->id]; + + if (!strcmp(msm_pcm_fe_topology_text[topology], "ULL_PP")) + perf_mode = ULL_POST_PROCESSING_PCM_MODE; + else if (!strcmp(msm_pcm_fe_topology_text[topology], "ULL")) + perf_mode = ULTRA_LOW_LATENCY_PCM_MODE; + else if (!strcmp(msm_pcm_fe_topology_text[topology], "LL")) + perf_mode = LOW_LATENCY_PCM_MODE; + else + /* use the default from the device tree */ + perf_mode = pdata->perf_mode; + + + /* need to set LOW_LATENCY_PCM_MODE for capture since + * push mode does not support ULL + */ + prtd->audio_client->perf_mode = (dir == IN) ? + perf_mode : + LOW_LATENCY_PCM_MODE; + + /* rate and channels are sent to audio driver */ + prtd->samp_rate = params_rate(params); + prtd->channel_mode = params_channels(params); + if (prtd->enabled) + return 0; + + if (pdata->ch_map[soc_prtd->dai_link->id]) { + use_default_chmap = + !(pdata->ch_map[soc_prtd->dai_link->id]->set_ch_map); + chmap = + pdata->ch_map[soc_prtd->dai_link->id]->channel_map; + } + + switch (runtime->format) { + case SNDRV_PCM_FORMAT_S24_LE: + bits_per_sample = 24; + sample_word_size = 32; + break; + case SNDRV_PCM_FORMAT_S24_3LE: + bits_per_sample = 24; + sample_word_size = 24; + break; + case SNDRV_PCM_FORMAT_S16_LE: + default: + bits_per_sample = 16; + sample_word_size = 16; + break; + } + + config.format = FORMAT_LINEAR_PCM; + config.bits_per_sample = bits_per_sample; + config.rate = params_rate(params); + config.channels = params_channels(params); + config.sample_word_size = sample_word_size; + config.bufsz = params_buffer_bytes(params) / params_periods(params); + config.bufcnt = params_periods(params); + + ret = q6asm_open_shared_io(prtd->audio_client, &config, dir, + use_default_chmap, chmap); + if (ret) { + pr_err("%s: q6asm_open_write_shared_io failed ret: %d\n", + __func__, ret); + return ret; + } + + prtd->pcm_size = params_buffer_bytes(params); + prtd->pcm_count = params_buffer_bytes(params); + prtd->pcm_irq_pos = 0; + + buf = prtd->audio_client->port[dir].buf; + dma_buf->dev.type = SNDRV_DMA_TYPE_DEV; + dma_buf->dev.dev = substream->pcm->card->dev; + dma_buf->private_data = NULL; + dma_buf->area = buf->data; + dma_buf->addr = buf->phys; + dma_buf->bytes = prtd->pcm_size; + + snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); + + pr_debug("%s: session ID %d, perf %d\n", __func__, + prtd->audio_client->session, + prtd->audio_client->perf_mode); + prtd->session_id = prtd->audio_client->session; + + pr_debug("msm_pcm_routing_reg_phy_stream w/ id %d\n", + soc_prtd->dai_link->id); + ret = msm_pcm_routing_reg_phy_stream(soc_prtd->dai_link->id, + prtd->audio_client->perf_mode, + prtd->session_id, substream->stream); + + if (ret) { + pr_err("%s: stream reg failed ret:%d\n", __func__, ret); + return ret; + } + + atomic_set(&prtd->out_count, runtime->periods); + prtd->enabled = 1; + prtd->cmd_pending = 0; + prtd->cmd_interrupt = 0; + + return 0; +} + +static int msm_pcm_trigger(struct snd_pcm_substream *substream, int cmd) +{ + int ret = 0; + struct snd_pcm_runtime *runtime = substream->runtime; + struct msm_audio *prtd = runtime->private_data; + int dir = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? 0 : 1; + struct audio_buffer *buf; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + pr_debug("%s: %s Trigger start\n", __func__, + dir == 0 ? "P" : "C"); + ret = q6asm_run(prtd->audio_client, 0, 0, 0); + if (ret) + break; + atomic_set(&prtd->start, 1); + break; + case SNDRV_PCM_TRIGGER_STOP: + pr_debug("%s: SNDRV_PCM_TRIGGER_STOP\n", __func__); + atomic_set(&prtd->start, 0); + q6asm_cmd(prtd->audio_client, CMD_PAUSE); + q6asm_cmd(prtd->audio_client, CMD_FLUSH); + buf = q6asm_shared_io_buf(prtd->audio_client, dir); + if (buf == NULL) { + pr_err("%s: shared IO buffer is null\n", __func__); + ret = -EINVAL; + break; + } + memset(buf->data, 0, buf->actual_size); + break; + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + pr_debug("%s: SNDRV_PCM_TRIGGER_PAUSE\n", __func__); + ret = q6asm_cmd_nowait(prtd->audio_client, CMD_PAUSE); + atomic_set(&prtd->start, 0); + break; + default: + ret = -EINVAL; + break; + } + return ret; +} + + +static int msm_pcm_mmap_fd(struct snd_pcm_substream *substream, + struct snd_pcm_mmap_fd *mmap_fd) +{ + struct msm_audio *prtd; + struct audio_port_data *apd; + struct audio_buffer *ab; + int dir = -1; + + if (!substream->runtime) { + pr_err("%s substream runtime not found\n", __func__); + return -EFAULT; + } + + prtd = substream->runtime->private_data; + if (!prtd || !prtd->audio_client || !prtd->mmap_flag) { + pr_err("%s no audio client or not an mmap session\n", __func__); + return -EINVAL; + } + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + dir = IN; + else + dir = OUT; + + apd = prtd->audio_client->port; + ab = &(apd[dir].buf[0]); + /* + * Passing O_CLOEXEC as flag passed to fd, to be in sync with + * previous implimentation. + * This was the flag used by previous internal wrapper API, which + * used to call dma_buf_fd internally. + */ + mmap_fd->fd = dma_buf_fd(ab->dma_buf, O_CLOEXEC); + if (mmap_fd->fd >= 0) { + mmap_fd->dir = dir; + mmap_fd->actual_size = ab->actual_size; + mmap_fd->size = ab->size; + } + return mmap_fd->fd < 0 ? -EFAULT : 0; +} + +static int msm_pcm_ioctl(struct snd_pcm_substream *substream, + unsigned int cmd, void *arg) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct msm_audio *prtd = runtime->private_data; + int dir = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? 0 : 1; + struct audio_buffer *buf; + + switch (cmd) { + case SNDRV_PCM_IOCTL1_RESET: + pr_debug("%s: %s SNDRV_PCM_IOCTL1_RESET\n", __func__, + dir == 0 ? "P" : "C"); + buf = q6asm_shared_io_buf(prtd->audio_client, dir); + + if (buf && buf->data) + memset(buf->data, 0, buf->actual_size); + break; + default: + break; + } + + return snd_pcm_lib_ioctl(substream, cmd, arg); +} + +#ifdef CONFIG_COMPAT +static int msm_pcm_compat_ioctl(struct snd_pcm_substream *substream, + unsigned int cmd, void *arg) +{ + /* we only handle RESET which is common for both modes */ + return msm_pcm_ioctl(substream, cmd, arg); +} +#endif + +static snd_pcm_uframes_t msm_pcm_pointer(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + uint32_t read_index, wall_clk_msw, wall_clk_lsw; + /*these are offsets, unlike ASoC's full values*/ + snd_pcm_sframes_t hw_ptr; + snd_pcm_sframes_t period_size; + int ret; + int retries = 10; + struct msm_audio *prtd = runtime->private_data; + + period_size = runtime->period_size; + + do { + ret = q6asm_get_shared_pos(prtd->audio_client, + &read_index, &wall_clk_msw, + &wall_clk_lsw); + } while (ret == -EAGAIN && --retries); + + if (ret || !period_size) { + pr_err("get_shared_pos error or zero period size\n"); + return 0; + } + + hw_ptr = bytes_to_frames(substream->runtime, + read_index); + + if (runtime->control->appl_ptr == 0) { + pr_debug("ptr(%s): appl(0), hw = %lu read_index = %u\n", + prtd->substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? + "P" : "C", + hw_ptr, read_index); + } + return (hw_ptr/period_size) * period_size; +} + +static int msm_pcm_copy(struct snd_pcm_substream *substream, int a, + unsigned long hwoff, void __user *buf, unsigned long fbytes) +{ + return -EINVAL; +} + +static int msm_pcm_mmap(struct snd_pcm_substream *substream, + struct vm_area_struct *vma) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct msm_audio *prtd = runtime->private_data; + struct audio_client *ac = prtd->audio_client; + struct audio_port_data *apd = ac->port; + struct audio_buffer *ab; + int dir = -1; + int ret; + + pr_debug("%s: mmap begin\n", __func__); + prtd->mmap_flag = 1; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + dir = IN; + else + dir = OUT; + + ab = &(apd[dir].buf[0]); + + ret = msm_audio_ion_mmap(ab, vma); + + if (ret) + prtd->mmap_flag = 0; + + return ret; +} + +static int msm_pcm_prepare(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct msm_audio *prtd = runtime->private_data; + + if (!prtd || !prtd->mmap_flag) + return -EIO; + + return 0; +} + +static int msm_pcm_close(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_soc_pcm_runtime *soc_prtd = substream->private_data; + struct msm_audio *prtd = runtime->private_data; + struct audio_client *ac = prtd->audio_client; + uint32_t timeout; + int dir = 0; + int ret = 0; + + if (ac) { + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + dir = IN; + else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + dir = OUT; + + /* determine timeout length */ + if (runtime->frame_bits == 0 || runtime->rate == 0) { + timeout = CMD_EOS_MIN_TIMEOUT_LENGTH; + } else { + timeout = (runtime->period_size * + CMD_EOS_TIMEOUT_MULTIPLIER) / + ((runtime->frame_bits / 8) * + runtime->rate); + if (timeout < CMD_EOS_MIN_TIMEOUT_LENGTH) + timeout = CMD_EOS_MIN_TIMEOUT_LENGTH; + } + + q6asm_cmd(ac, CMD_CLOSE); + + ret = q6asm_shared_io_free(ac, dir); + + if (ret) { + pr_err("%s: Failed to close pull mode, ret %d\n", + __func__, ret); + } + q6asm_audio_client_free(ac); + } + msm_pcm_routing_dereg_phy_stream(soc_prtd->dai_link->id, + dir == IN ? + SNDRV_PCM_STREAM_PLAYBACK : + SNDRV_PCM_STREAM_CAPTURE); + kfree(prtd); + runtime->private_data = NULL; + + return 0; +} + +static int msm_pcm_set_volume(struct msm_audio *prtd, uint32_t volume) +{ + int rc = 0; + + if (prtd && prtd->audio_client) { + pr_debug("%s: channels %d volume 0x%x\n", __func__, + prtd->channel_mode, volume); + rc = q6asm_set_volume(prtd->audio_client, volume); + if (rc < 0) { + pr_err("%s: Send Volume command failed rc=%d\n", + __func__, rc); + } + } + return rc; +} + +static int msm_pcm_volume_ctl_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_pcm_volume *vol = snd_kcontrol_chip(kcontrol); + struct snd_pcm_substream *substream = + vol->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; + struct msm_audio *prtd; + + pr_debug("%s\n", __func__); + if (!substream) { + pr_err("%s substream not found\n", __func__); + return -ENODEV; + } + if (!substream->runtime) { + pr_debug("%s substream runtime not found\n", __func__); + return 0; + } + prtd = substream->runtime->private_data; + if (prtd) + ucontrol->value.integer.value[0] = prtd->volume; + return 0; +} + +static int msm_pcm_volume_ctl_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int rc = 0; + struct snd_pcm_volume *vol = snd_kcontrol_chip(kcontrol); + struct snd_pcm_substream *substream = + vol->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; + struct msm_audio *prtd; + int volume = ucontrol->value.integer.value[0]; + + pr_debug("%s: volume : 0x%x\n", __func__, volume); + if (!substream) { + pr_err("%s substream not found\n", __func__); + return -ENODEV; + } + if (!substream->runtime) { + pr_err("%s substream runtime not found\n", __func__); + return 0; + } + prtd = substream->runtime->private_data; + if (prtd) { + rc = msm_pcm_set_volume(prtd, volume); + prtd->volume = volume; + } + return rc; +} + +static int msm_pcm_add_volume_control(struct snd_soc_pcm_runtime *rtd) +{ + int ret = 0; + struct snd_pcm *pcm = rtd->pcm; + struct snd_pcm_volume *volume_info; + struct snd_kcontrol *kctl; + + dev_dbg(rtd->dev, "%s, Volume control add\n", __func__); + ret = snd_pcm_add_volume_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK, + NULL, 1, rtd->dai_link->id, + &volume_info); + if (ret < 0) { + pr_err("%s volume control failed ret %d\n", __func__, ret); + return ret; + } + kctl = volume_info->kctl; + kctl->put = msm_pcm_volume_ctl_put; + kctl->get = msm_pcm_volume_ctl_get; + kctl->tlv.p = msm_pcm_vol_gain; + return 0; +} + +static int msm_pcm_channel_map_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *pcm = snd_kcontrol_chip(kcontrol); + u64 fe_id = kcontrol->private_value; + struct msm_plat_data *pdata = (struct msm_plat_data *) + snd_soc_component_get_drvdata(pcm); + int rc = 0, i = 0; + + pr_debug("%s: fe_id- %llu\n", __func__, fe_id); + + if (fe_id >= MSM_FRONTEND_DAI_MAX) { + pr_err("%s Received out of bounds fe_id %llu\n", + __func__, fe_id); + rc = -EINVAL; + goto end; + } + + if (pdata->ch_map[fe_id]) { + pdata->ch_map[fe_id]->set_ch_map = true; + for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL; i++) + pdata->ch_map[fe_id]->channel_map[i] = + (char)(ucontrol->value.integer.value[i]); + } else { + pr_debug("%s: no memory for ch_map, default will be set\n", + __func__); + } +end: + pr_debug("%s: ret %d\n", __func__, rc); + return rc; +} + +static int msm_pcm_channel_map_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 8; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 0xFFFFFFFF; + return 0; +} + +static int msm_pcm_channel_map_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *pcm = snd_kcontrol_chip(kcontrol); + u64 fe_id = kcontrol->private_value; + struct msm_plat_data *pdata = (struct msm_plat_data *) + snd_soc_component_get_drvdata(pcm); + int rc = 0, i = 0; + + pr_debug("%s: fe_id- %llu\n", __func__, fe_id); + if (fe_id >= MSM_FRONTEND_DAI_MAX) { + pr_err("%s: Received out of bounds fe_id %llu\n", + __func__, fe_id); + rc = -EINVAL; + goto end; + } + if (pdata->ch_map[fe_id]) { + for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL; i++) + ucontrol->value.integer.value[i] = + pdata->ch_map[fe_id]->channel_map[i]; + } +end: + pr_debug("%s: ret %d\n", __func__, rc); + return rc; +} + +static int msm_pcm_add_channel_map_control(struct snd_soc_pcm_runtime *rtd) +{ + const char *mixer_ctl_name = "Playback Channel Map"; + const char *deviceNo = "NN"; + char *mixer_str = NULL; + struct msm_plat_data *pdata = NULL; + int ctl_len = 0; + struct snd_kcontrol_new fe_channel_map_control[1] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "?", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = msm_pcm_channel_map_info, + .get = msm_pcm_channel_map_get, + .put = msm_pcm_channel_map_put, + .private_value = 0, + } + }; + + if (!rtd) { + pr_err("%s: NULL rtd\n", __func__); + return -EINVAL; + } + + pr_debug("%s: added new pcm FE with name %s, id %d, cpu dai %s, device no %d\n", + __func__, rtd->dai_link->name, rtd->dai_link->id, + rtd->dai_link->cpu_dai_name, rtd->pcm->device); + + ctl_len = strlen(mixer_ctl_name) + strlen(deviceNo) + 1; + mixer_str = kzalloc(ctl_len, GFP_KERNEL); + if (!mixer_str) + return -ENOMEM; + + snprintf(mixer_str, ctl_len, "%s%d", mixer_ctl_name, rtd->pcm->device); + + fe_channel_map_control[0].name = mixer_str; + fe_channel_map_control[0].private_value = rtd->dai_link->id; + pr_debug("%s: Registering new mixer ctl %s\n", __func__, mixer_str); + snd_soc_add_platform_controls(rtd->platform, + fe_channel_map_control, + ARRAY_SIZE(fe_channel_map_control)); + + pdata = snd_soc_platform_get_drvdata(rtd->platform); + pdata->ch_map[rtd->dai_link->id] = + kzalloc(sizeof(struct msm_pcm_ch_map), GFP_KERNEL); + if (!pdata->ch_map[rtd->dai_link->id]) { + pr_err("%s: Could not allocate memory for channel map\n", + __func__); + kfree(mixer_str); + return -ENOMEM; + } + kfree(mixer_str); + return 0; +} + +static int msm_pcm_fe_topology_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + const struct soc_enum *e = &msm_pcm_fe_topology_enum[0]; + + return snd_ctl_enum_info(uinfo, 1, e->items, e->texts); +} + +static int msm_pcm_fe_topology_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + unsigned long fe_id = kcontrol->private_value; + + if (fe_id >= MSM_FRONTEND_DAI_MAX) { + pr_err("%s Received out of bound fe_id %lu\n", __func__, fe_id); + return -EINVAL; + } + + pr_debug("%s: %lu topology %s\n", __func__, fe_id, + msm_pcm_fe_topology_text[msm_pcm_fe_topology[fe_id]]); + ucontrol->value.enumerated.item[0] = msm_pcm_fe_topology[fe_id]; + return 0; +} + +static int msm_pcm_fe_topology_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + unsigned long fe_id = kcontrol->private_value; + unsigned int item; + + if (fe_id >= MSM_FRONTEND_DAI_MAX) { + pr_err("%s Received out of bound fe_id %lu\n", __func__, fe_id); + return -EINVAL; + } + + item = ucontrol->value.enumerated.item[0]; + if (item >= ARRAY_SIZE(msm_pcm_fe_topology_text)) { + pr_err("%s Received out of bound topology %lu\n", __func__, + fe_id); + return -EINVAL; + } + + pr_debug("%s: %lu new topology %s\n", __func__, fe_id, + msm_pcm_fe_topology_text[item]); + msm_pcm_fe_topology[fe_id] = item; + return 0; +} + +static int msm_pcm_add_fe_topology_control(struct snd_soc_pcm_runtime *rtd) +{ + const char *mixer_ctl_name = "PCM_Dev"; + const char *deviceNo = "NN"; + const char *topo_text = "Topology"; + char *mixer_str = NULL; + int ctl_len; + int ret; + struct snd_kcontrol_new topology_control[1] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .name = "?", + .info = msm_pcm_fe_topology_info, + .get = msm_pcm_fe_topology_get, + .put = msm_pcm_fe_topology_put, + .private_value = 0, + }, + }; + + ctl_len = strlen(mixer_ctl_name) + 1 + strlen(deviceNo) + 1 + + strlen(topo_text) + 1; + mixer_str = kzalloc(ctl_len, GFP_KERNEL); + + if (!mixer_str) + return -ENOMEM; + + snprintf(mixer_str, ctl_len, "%s %d %s", mixer_ctl_name, + rtd->pcm->device, topo_text); + + topology_control[0].name = mixer_str; + topology_control[0].private_value = rtd->dai_link->id; + ret = snd_soc_add_platform_controls(rtd->platform, topology_control, + ARRAY_SIZE(topology_control)); + msm_pcm_fe_topology[rtd->dai_link->id] = 0; + kfree(mixer_str); + return ret; +} + +static int msm_pcm_playback_app_type_cfg_ctl_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u64 fe_id = kcontrol->private_value; + int session_type = SESSION_TYPE_RX; + int be_id = ucontrol->value.integer.value[3]; + struct msm_pcm_stream_app_type_cfg cfg_data = {0, 0, 48000}; + int ret = 0; + + cfg_data.app_type = ucontrol->value.integer.value[0]; + cfg_data.acdb_dev_id = ucontrol->value.integer.value[1]; + if (ucontrol->value.integer.value[2] != 0) + cfg_data.sample_rate = ucontrol->value.integer.value[2]; + pr_debug("%s: fe_id- %llu session_type- %d be_id- %d app_type- %d acdb_dev_id- %d sample_rate- %d\n", + __func__, fe_id, session_type, be_id, + cfg_data.app_type, cfg_data.acdb_dev_id, cfg_data.sample_rate); + ret = msm_pcm_routing_reg_stream_app_type_cfg(fe_id, session_type, + be_id, &cfg_data); + if (ret < 0) + pr_err("%s: msm_pcm_routing_reg_stream_app_type_cfg failed returned %d\n", + __func__, ret); + return ret; +} + +static int msm_pcm_playback_app_type_cfg_ctl_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u64 fe_id = kcontrol->private_value; + int session_type = SESSION_TYPE_RX; + int be_id = 0; + struct msm_pcm_stream_app_type_cfg cfg_data = {0}; + int ret = 0; + + ret = msm_pcm_routing_get_stream_app_type_cfg(fe_id, session_type, + &be_id, &cfg_data); + if (ret < 0) { + pr_err("%s: msm_pcm_routing_get_stream_app_type_cfg failed returned %d\n", + __func__, ret); + goto done; + } + + ucontrol->value.integer.value[0] = cfg_data.app_type; + ucontrol->value.integer.value[1] = cfg_data.acdb_dev_id; + ucontrol->value.integer.value[2] = cfg_data.sample_rate; + ucontrol->value.integer.value[3] = be_id; + pr_debug("%s: fedai_id %llu, session_type %d, be_id %d, app_type %d, acdb_dev_id %d, sample_rate %d\n", + __func__, fe_id, session_type, be_id, + cfg_data.app_type, cfg_data.acdb_dev_id, cfg_data.sample_rate); +done: + return ret; +} + +static int msm_pcm_capture_app_type_cfg_ctl_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u64 fe_id = kcontrol->private_value; + int session_type = SESSION_TYPE_TX; + int be_id = ucontrol->value.integer.value[3]; + struct msm_pcm_stream_app_type_cfg cfg_data = {0, 0, 48000}; + int ret = 0; + + cfg_data.app_type = ucontrol->value.integer.value[0]; + cfg_data.acdb_dev_id = ucontrol->value.integer.value[1]; + if (ucontrol->value.integer.value[2] != 0) + cfg_data.sample_rate = ucontrol->value.integer.value[2]; + pr_debug("%s: fe_id- %llu session_type- %d be_id- %d app_type- %d acdb_dev_id- %d sample_rate- %d\n", + __func__, fe_id, session_type, be_id, + cfg_data.app_type, cfg_data.acdb_dev_id, cfg_data.sample_rate); + ret = msm_pcm_routing_reg_stream_app_type_cfg(fe_id, session_type, + be_id, &cfg_data); + if (ret < 0) + pr_err("%s: msm_pcm_routing_reg_stream_app_type_cfg failed returned %d\n", + __func__, ret); + + return ret; +} + +static int msm_pcm_capture_app_type_cfg_ctl_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u64 fe_id = kcontrol->private_value; + int session_type = SESSION_TYPE_TX; + int be_id = 0; + struct msm_pcm_stream_app_type_cfg cfg_data = {0}; + int ret = 0; + + ret = msm_pcm_routing_get_stream_app_type_cfg(fe_id, session_type, + &be_id, &cfg_data); + if (ret < 0) { + pr_err("%s: msm_pcm_routing_get_stream_app_type_cfg failed returned %d\n", + __func__, ret); + goto done; + } + + ucontrol->value.integer.value[0] = cfg_data.app_type; + ucontrol->value.integer.value[1] = cfg_data.acdb_dev_id; + ucontrol->value.integer.value[2] = cfg_data.sample_rate; + ucontrol->value.integer.value[3] = be_id; + pr_debug("%s: fedai_id %llu, session_type %d, be_id %d, app_type %d, acdb_dev_id %d, sample_rate %d\n", + __func__, fe_id, session_type, be_id, + cfg_data.app_type, cfg_data.acdb_dev_id, cfg_data.sample_rate); +done: + return ret; +} + +static int msm_pcm_add_app_type_controls(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_pcm *pcm = rtd->pcm; + struct snd_pcm_usr *app_type_info; + struct snd_kcontrol *kctl; + const char *playback_mixer_ctl_name = "Audio Stream"; + const char *capture_mixer_ctl_name = "Audio Stream Capture"; + const char *deviceNo = "NN"; + const char *suffix = "App Type Cfg"; + int ctl_len, ret = 0; + + if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) { + ctl_len = strlen(playback_mixer_ctl_name) + 1 + + strlen(deviceNo) + 1 + + strlen(suffix) + 1; + pr_debug("%s: Playback app type cntrl add\n", __func__); + ret = snd_pcm_add_usr_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK, + NULL, 1, ctl_len, rtd->dai_link->id, + &app_type_info); + if (ret < 0) { + pr_err("%s: playback app type cntrl add failed, err: %d\n", + __func__, ret); + return ret; + } + kctl = app_type_info->kctl; + snprintf(kctl->id.name, ctl_len, "%s %d %s", + playback_mixer_ctl_name, rtd->pcm->device, suffix); + kctl->put = msm_pcm_playback_app_type_cfg_ctl_put; + kctl->get = msm_pcm_playback_app_type_cfg_ctl_get; + } + + if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) { + ctl_len = strlen(capture_mixer_ctl_name) + 1 + + strlen(deviceNo) + 1 + strlen(suffix) + 1; + pr_debug("%s: Capture app type cntrl add\n", __func__); + ret = snd_pcm_add_usr_ctls(pcm, SNDRV_PCM_STREAM_CAPTURE, + NULL, 1, ctl_len, rtd->dai_link->id, + &app_type_info); + if (ret < 0) { + pr_err("%s: capture app type cntrl add failed, err: %d\n", + __func__, ret); + return ret; + } + kctl = app_type_info->kctl; + snprintf(kctl->id.name, ctl_len, "%s %d %s", + capture_mixer_ctl_name, rtd->pcm->device, suffix); + kctl->put = msm_pcm_capture_app_type_cfg_ctl_put; + kctl->get = msm_pcm_capture_app_type_cfg_ctl_get; + } + + return 0; +} + +static int msm_pcm_hwdep_ioctl(struct snd_hwdep *hw, struct file *file, + unsigned int cmd, unsigned long arg) +{ + int ret = 0; + struct snd_pcm *pcm = hw->private_data; + struct snd_pcm_mmap_fd __user *_mmap_fd = NULL; + struct snd_pcm_mmap_fd mmap_fd; + struct snd_pcm_substream *substream = NULL; + int32_t dir = -1; + + switch (cmd) { + case SNDRV_PCM_IOCTL_MMAP_DATA_FD: + _mmap_fd = (struct snd_pcm_mmap_fd __user *)arg; + if (get_user(dir, (int32_t __user *)&(_mmap_fd->dir))) { + pr_err("%s: error copying mmap_fd from user\n", + __func__); + ret = -EFAULT; + break; + } + if (dir != OUT && dir != IN) { + pr_err("%s invalid stream dir\n", __func__); + ret = -EINVAL; + break; + } + substream = pcm->streams[dir].substream; + if (!substream) { + pr_err("%s substream not found\n", __func__); + ret = -ENODEV; + break; + } + pr_debug("%s : %s MMAP Data fd\n", __func__, + dir == 0 ? "P" : "C"); + if (msm_pcm_mmap_fd(substream, &mmap_fd) < 0) { + pr_err("%s: error getting fd\n", + __func__); + ret = -EFAULT; + break; + } + if (put_user(mmap_fd.fd, &_mmap_fd->fd) || + put_user(mmap_fd.size, &_mmap_fd->size) || + put_user(mmap_fd.actual_size, &_mmap_fd->actual_size)) { + pr_err("%s: error copying fd\n", __func__); + return -EFAULT; + } + break; + default: + ret = -EINVAL; + break; + } + return ret; +} + +#ifdef CONFIG_COMPAT +static int msm_pcm_hwdep_compat_ioctl(struct snd_hwdep *hw, + struct file *file, + unsigned int cmd, + unsigned long arg) +{ + /* we only support mmap fd. Handling is common in both modes */ + return msm_pcm_hwdep_ioctl(hw, file, cmd, arg); +} +#else +static int msm_pcm_hwdep_compat_ioctl(struct snd_hwdep *hw, + struct file *file, + unsigned int cmd, + unsigned long arg) +{ + return -EINVAL; +} +#endif + +static int msm_pcm_add_hwdep_dev(struct snd_soc_pcm_runtime *runtime) +{ + struct snd_hwdep *hwdep; + int rc; + char id[] = "NOIRQ_NN"; + + snprintf(id, sizeof(id), "NOIRQ_%d", runtime->pcm->device); + pr_debug("%s: pcm dev %d\n", __func__, runtime->pcm->device); + rc = snd_hwdep_new(runtime->card->snd_card, + &id[0], + HWDEP_FE_BASE + runtime->pcm->device, + &hwdep); + if (!hwdep || rc < 0) { + pr_err("%s: hwdep intf failed to create %s - hwdep\n", __func__, + id); + return rc; + } + + hwdep->iface = SNDRV_HWDEP_IFACE_AUDIO_BE; /* for lack of a FE iface */ + hwdep->private_data = runtime->pcm; /* of type struct snd_pcm */ + hwdep->ops.ioctl = msm_pcm_hwdep_ioctl; + hwdep->ops.ioctl_compat = msm_pcm_hwdep_compat_ioctl; + return 0; +} + +static int msm_asoc_pcm_new(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_card *card = rtd->card->snd_card; + struct snd_pcm *pcm = rtd->pcm; + int ret; + + pr_debug("%s , register new control\n", __func__); + if (!card->dev->coherent_dma_mask) + card->dev->coherent_dma_mask = DMA_BIT_MASK(32); + + ret = msm_pcm_add_channel_map_control(rtd); + if (ret) + pr_err("%s: Could not add pcm Channel Map Control\n", + __func__); + + ret = msm_pcm_add_volume_control(rtd); + if (ret) { + pr_err("%s: Could not add pcm Volume Control %d\n", + __func__, ret); + } + + ret = msm_pcm_add_fe_topology_control(rtd); + if (ret) { + pr_err("%s: Could not add pcm topology control %d\n", + __func__, ret); + } + + ret = msm_pcm_add_app_type_controls(rtd); + if (ret) { + pr_err("%s: Could not add app type controls failed %d\n", + __func__, ret); + } + ret = msm_pcm_add_hwdep_dev(rtd); + if (ret) + pr_err("%s: Could not add hw dep node\n", __func__); + pcm->nonatomic = true; + + return ret; +} + + +static const struct snd_pcm_ops msm_pcm_ops = { + .open = msm_pcm_open, + .prepare = msm_pcm_prepare, + .copy_user = msm_pcm_copy, + .hw_params = msm_pcm_hw_params, + .ioctl = msm_pcm_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = msm_pcm_compat_ioctl, +#endif + .trigger = msm_pcm_trigger, + .pointer = msm_pcm_pointer, + .mmap = msm_pcm_mmap, + .close = msm_pcm_close, +}; + +static struct snd_soc_platform_driver msm_soc_platform = { + .ops = &msm_pcm_ops, + .pcm_new = msm_asoc_pcm_new, +}; + +static int msm_pcm_probe(struct platform_device *pdev) +{ + int rc; + struct msm_plat_data *pdata; + const char *latency_level; + int perf_mode = LOW_LATENCY_PCM_MODE; + + dev_dbg(&pdev->dev, "Pull mode driver probe\n"); + + if (of_property_read_bool(pdev->dev.of_node, + "qcom,msm-pcm-low-latency")) { + + rc = of_property_read_string(pdev->dev.of_node, + "qcom,latency-level", &latency_level); + if (!rc) { + if (!strcmp(latency_level, "ultra")) + perf_mode = ULTRA_LOW_LATENCY_PCM_MODE; + else if (!strcmp(latency_level, "ull-pp")) + perf_mode = ULL_POST_PROCESSING_PCM_MODE; + } + } + + pdata = devm_kzalloc(&pdev->dev, + sizeof(struct msm_plat_data), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + pdata->perf_mode = perf_mode; + + dev_set_drvdata(&pdev->dev, pdata); + + dev_dbg(&pdev->dev, "%s: dev name %s\n", + __func__, dev_name(&pdev->dev)); + dev_dbg(&pdev->dev, "Pull mode driver register\n"); + rc = snd_soc_register_platform(&pdev->dev, + &msm_soc_platform); + + if (rc) + dev_err(&pdev->dev, "Failed to register pull mode driver\n"); + + return rc; +} + +static int msm_pcm_remove(struct platform_device *pdev) +{ + struct msm_plat_data *pdata; + + dev_dbg(&pdev->dev, "Pull mode remove\n"); + pdata = dev_get_drvdata(&pdev->dev); + devm_kfree(&pdev->dev, pdata); + snd_soc_unregister_platform(&pdev->dev); + return 0; +} +static const struct of_device_id msm_pcm_noirq_dt_match[] = { + {.compatible = "qcom,msm-pcm-dsp-noirq"}, + {} +}; +MODULE_DEVICE_TABLE(of, msm_pcm_noirq_dt_match); + +static struct platform_driver msm_pcm_driver_noirq = { + .driver = { + .name = "msm-pcm-dsp-noirq", + .owner = THIS_MODULE, + .of_match_table = msm_pcm_noirq_dt_match, + }, + .probe = msm_pcm_probe, + .remove = msm_pcm_remove, +}; + +int __init msm_pcm_noirq_init(void) +{ + return platform_driver_register(&msm_pcm_driver_noirq); +} + +void msm_pcm_noirq_exit(void) +{ + platform_driver_unregister(&msm_pcm_driver_noirq); +} + +MODULE_DESCRIPTION("PCM NOIRQ module platform driver"); +MODULE_LICENSE("GPL v2"); diff --git a/audio-kernel/asoc/msm-pcm-q6-v2.c b/audio-kernel/asoc/msm-pcm-q6-v2.c new file mode 100644 index 000000000..9eb42c0be --- /dev/null +++ b/audio-kernel/asoc/msm-pcm-q6-v2.c @@ -0,0 +1,2060 @@ +/* Copyright (c) 2012-2019, 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "msm-pcm-q6-v2.h" +#include "msm-pcm-routing-v2.h" +#include "msm-qti-pp-config.h" + +#define TIMEOUT_MS 1000 + +enum stream_state { + IDLE = 0, + STOPPED, + RUNNING, +}; + +static struct audio_locks the_locks; + +#define PCM_MASTER_VOL_MAX_STEPS 0x2000 +static const DECLARE_TLV_DB_LINEAR(msm_pcm_vol_gain, 0, + PCM_MASTER_VOL_MAX_STEPS); + +struct snd_msm { + struct snd_card *card; + struct snd_pcm *pcm; +}; + +#define CMD_EOS_MIN_TIMEOUT_LENGTH 50 +#define CMD_EOS_TIMEOUT_MULTIPLIER (HZ * 50) +#define MAX_PB_COPY_RETRIES 3 + +static struct snd_pcm_hardware msm_pcm_hardware_capture = { + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE), + .rates = SNDRV_PCM_RATE_8000_384000, + .rate_min = 8000, + .rate_max = 384000, + .channels_min = 1, + .channels_max = 4, + .buffer_bytes_max = CAPTURE_MAX_NUM_PERIODS * + CAPTURE_MAX_PERIOD_SIZE, + .period_bytes_min = CAPTURE_MIN_PERIOD_SIZE, + .period_bytes_max = CAPTURE_MAX_PERIOD_SIZE, + .periods_min = CAPTURE_MIN_NUM_PERIODS, + .periods_max = CAPTURE_MAX_NUM_PERIODS, + .fifo_size = 0, +}; + +static struct snd_pcm_hardware msm_pcm_hardware_playback = { + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE), + .rates = SNDRV_PCM_RATE_8000_384000, + .rate_min = 8000, + .rate_max = 384000, + .channels_min = 1, + .channels_max = 8, + .buffer_bytes_max = PLAYBACK_MAX_NUM_PERIODS * + PLAYBACK_MAX_PERIOD_SIZE, + .period_bytes_min = PLAYBACK_MIN_PERIOD_SIZE, + .period_bytes_max = PLAYBACK_MAX_PERIOD_SIZE, + .periods_min = PLAYBACK_MIN_NUM_PERIODS, + .periods_max = PLAYBACK_MAX_NUM_PERIODS, + .fifo_size = 0, +}; + +/* Conventional and unconventional sample rate supported */ +static unsigned int supported_sample_rates[] = { + 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000, + 88200, 96000, 176400, 192000, 352800, 384000 +}; + +static struct snd_pcm_hw_constraint_list constraints_sample_rates = { + .count = ARRAY_SIZE(supported_sample_rates), + .list = supported_sample_rates, + .mask = 0, +}; + +static void msm_pcm_route_event_handler(enum msm_pcm_routing_event event, + void *priv_data) +{ + struct msm_audio *prtd = priv_data; + + WARN_ON(!prtd); + + pr_debug("%s: event %x\n", __func__, event); + + switch (event) { + case MSM_PCM_RT_EVT_BUF_RECFG: + q6asm_cmd(prtd->audio_client, CMD_PAUSE); + q6asm_cmd(prtd->audio_client, CMD_FLUSH); + q6asm_run(prtd->audio_client, 0, 0, 0); + /* fallthrough */ + default: + break; + } +} + +static void event_handler(uint32_t opcode, + uint32_t token, uint32_t *payload, void *priv) +{ + struct msm_audio *prtd = priv; + struct snd_pcm_substream *substream = prtd->substream; + uint32_t *ptrmem = (uint32_t *)payload; + uint32_t idx = 0; + uint32_t size = 0; + uint8_t buf_index; + struct snd_soc_pcm_runtime *rtd; + int ret = 0; + + switch (opcode) { + case ASM_DATA_EVENT_WRITE_DONE_V2: { + pr_debug("ASM_DATA_EVENT_WRITE_DONE_V2\n"); + pr_debug("Buffer Consumed = 0x%08x\n", *ptrmem); + prtd->pcm_irq_pos += prtd->pcm_count; + if (atomic_read(&prtd->start)) + snd_pcm_period_elapsed(substream); + atomic_inc(&prtd->out_count); + wake_up(&the_locks.write_wait); + if (!atomic_read(&prtd->start)) + break; + if (!prtd->mmap_flag || prtd->reset_event) + break; + if (q6asm_is_cpu_buf_avail_nolock(IN, + prtd->audio_client, + &size, &idx)) { + pr_debug("%s:writing %d bytes of buffer to dsp 2\n", + __func__, prtd->pcm_count); + q6asm_write_nolock(prtd->audio_client, + prtd->pcm_count, 0, 0, NO_TIMESTAMP); + } + break; + } + case ASM_DATA_EVENT_RENDERED_EOS: + pr_debug("ASM_DATA_EVENT_RENDERED_EOS\n"); + clear_bit(CMD_EOS, &prtd->cmd_pending); + wake_up(&the_locks.eos_wait); + break; + case ASM_DATA_EVENT_READ_DONE_V2: { + pr_debug("ASM_DATA_EVENT_READ_DONE_V2\n"); + buf_index = q6asm_get_buf_index_from_token(token); + if (buf_index >= CAPTURE_MAX_NUM_PERIODS) { + pr_err("%s: buffer index %u is out of range.\n", + __func__, buf_index); + return; + } + pr_debug("%s: token=0x%08x buf_index=0x%08x\n", + __func__, token, buf_index); + prtd->in_frame_info[buf_index].size = payload[4]; + prtd->in_frame_info[buf_index].offset = payload[5]; + /* assume data size = 0 during flushing */ + if (prtd->in_frame_info[buf_index].size) { + prtd->pcm_irq_pos += + prtd->in_frame_info[buf_index].size; + pr_debug("pcm_irq_pos=%d\n", prtd->pcm_irq_pos); + if (atomic_read(&prtd->start)) + snd_pcm_period_elapsed(substream); + if (atomic_read(&prtd->in_count) <= prtd->periods) + atomic_inc(&prtd->in_count); + wake_up(&the_locks.read_wait); + if (prtd->mmap_flag && + q6asm_is_cpu_buf_avail_nolock(OUT, + prtd->audio_client, + &size, &idx) && + (substream->runtime->status->state == + SNDRV_PCM_STATE_RUNNING)) + q6asm_read_nolock(prtd->audio_client); + } else { + pr_debug("%s: reclaim flushed buf in_count %x\n", + __func__, atomic_read(&prtd->in_count)); + prtd->pcm_irq_pos += prtd->pcm_count; + if (prtd->mmap_flag) { + if (q6asm_is_cpu_buf_avail_nolock(OUT, + prtd->audio_client, + &size, &idx) && + (substream->runtime->status->state == + SNDRV_PCM_STATE_RUNNING)) + q6asm_read_nolock(prtd->audio_client); + } else { + atomic_inc(&prtd->in_count); + } + if (atomic_read(&prtd->in_count) == prtd->periods) { + pr_info("%s: reclaimed all bufs\n", __func__); + if (atomic_read(&prtd->start)) + snd_pcm_period_elapsed(substream); + wake_up(&the_locks.read_wait); + } + } + break; + } + case ASM_STREAM_PP_EVENT: + case ASM_STREAM_CMD_ENCDEC_EVENTS: { + pr_debug("%s: ASM_STREAM_EVENT (0x%x)\n", __func__, opcode); + if (!substream) { + pr_err("%s: substream is NULL.\n", __func__); + return; + } + + rtd = substream->private_data; + if (!rtd) { + pr_err("%s: rtd is NULL\n", __func__); + return; + } + + ret = msm_adsp_inform_mixer_ctl(rtd, payload); + if (ret) { + pr_err("%s: failed to inform mixer ctl. err = %d\n", + __func__, ret); + return; + } + + break; + } + case APR_BASIC_RSP_RESULT: { + switch (payload[0]) { + case ASM_SESSION_CMD_RUN_V2: + if (substream->stream + != SNDRV_PCM_STREAM_PLAYBACK) { + atomic_set(&prtd->start, 1); + break; + } + if (prtd->mmap_flag) { + pr_debug("%s:writing %d bytes of buffer to dsp\n", + __func__, + prtd->pcm_count); + q6asm_write_nolock(prtd->audio_client, + prtd->pcm_count, + 0, 0, NO_TIMESTAMP); + } else { + while (atomic_read(&prtd->out_needed)) { + pr_debug("%s:writing %d bytes of buffer to dsp\n", + __func__, + prtd->pcm_count); + q6asm_write_nolock(prtd->audio_client, + prtd->pcm_count, + 0, 0, NO_TIMESTAMP); + atomic_dec(&prtd->out_needed); + wake_up(&the_locks.write_wait); + }; + } + atomic_set(&prtd->start, 1); + break; + case ASM_STREAM_CMD_REGISTER_PP_EVENTS: + pr_debug("%s: ASM_STREAM_CMD_REGISTER_PP_EVENTS:", + __func__); + break; + default: + pr_debug("%s:Payload = [0x%x]stat[0x%x]\n", + __func__, payload[0], payload[1]); + break; + } + } + break; + case RESET_EVENTS: + pr_debug("%s RESET_EVENTS\n", __func__); + prtd->pcm_irq_pos += prtd->pcm_count; + atomic_inc(&prtd->out_count); + atomic_inc(&prtd->in_count); + prtd->reset_event = true; + if (atomic_read(&prtd->start)) + snd_pcm_period_elapsed(substream); + wake_up(&the_locks.eos_wait); + wake_up(&the_locks.write_wait); + wake_up(&the_locks.read_wait); + break; + default: + pr_debug("Not Supported Event opcode[0x%x]\n", opcode); + break; + } +} + +static int msm_pcm_playback_prepare(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_soc_pcm_runtime *soc_prtd = substream->private_data; + struct msm_audio *prtd = runtime->private_data; + struct msm_plat_data *pdata; + struct snd_pcm_hw_params *params; + int ret; + uint32_t fmt_type = FORMAT_LINEAR_PCM; + uint16_t bits_per_sample; + uint16_t sample_word_size; + + pdata = (struct msm_plat_data *) + dev_get_drvdata(soc_prtd->platform->dev); + if (!pdata) { + pr_err("%s: platform data not populated\n", __func__); + return -EINVAL; + } + if (!prtd || !prtd->audio_client) { + pr_err("%s: private data null or audio client freed\n", + __func__); + return -EINVAL; + } + params = &soc_prtd->dpcm[substream->stream].hw_params; + + pr_debug("%s\n", __func__); + prtd->pcm_size = snd_pcm_lib_buffer_bytes(substream); + prtd->pcm_count = snd_pcm_lib_period_bytes(substream); + prtd->pcm_irq_pos = 0; + /* rate and channels are sent to audio driver */ + prtd->samp_rate = runtime->rate; + prtd->channel_mode = runtime->channels; + if (prtd->enabled) + return 0; + + prtd->audio_client->perf_mode = pdata->perf_mode; + pr_debug("%s: perf: %x\n", __func__, pdata->perf_mode); + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S32_LE: + bits_per_sample = 32; + sample_word_size = 32; + break; + case SNDRV_PCM_FORMAT_S24_LE: + bits_per_sample = 24; + sample_word_size = 32; + break; + case SNDRV_PCM_FORMAT_S24_3LE: + bits_per_sample = 24; + sample_word_size = 24; + break; + case SNDRV_PCM_FORMAT_S16_LE: + default: + bits_per_sample = 16; + sample_word_size = 16; + break; + } + if (prtd->compress_enable) { + fmt_type = FORMAT_GEN_COMPR; + pr_debug("%s: Compressed enabled!\n", __func__); + ret = q6asm_open_write_compressed(prtd->audio_client, fmt_type, + COMPRESSED_PASSTHROUGH_GEN); + if (ret < 0) { + pr_err("%s: q6asm_open_write_compressed failed (%d)\n", + __func__, ret); + q6asm_audio_client_free(prtd->audio_client); + prtd->audio_client = NULL; + return -ENOMEM; + } + } else { + if ((q6core_get_avcs_api_version_per_service( + APRV2_IDS_SERVICE_ID_ADSP_ASM_V) >= + ADSP_ASM_API_VERSION_V2) && + q6core_use_Q6_32ch_support()) + ret = q6asm_open_write_v5(prtd->audio_client, + fmt_type, bits_per_sample); + else + ret = q6asm_open_write_v4(prtd->audio_client, + fmt_type, bits_per_sample); + + if (ret < 0) { + pr_err("%s: q6asm_open_write failed (%d)\n", + __func__, ret); + q6asm_audio_client_free(prtd->audio_client); + prtd->audio_client = NULL; + return -ENOMEM; + } + + ret = q6asm_send_cal(prtd->audio_client); + if (ret < 0) + pr_debug("%s : Send cal failed : %d", __func__, ret); + } + pr_debug("%s: session ID %d\n", __func__, + prtd->audio_client->session); + prtd->session_id = prtd->audio_client->session; + + if (prtd->compress_enable) { + ret = msm_pcm_routing_reg_phy_compr_stream( + soc_prtd->dai_link->id, + prtd->audio_client->perf_mode, + prtd->session_id, + SNDRV_PCM_STREAM_PLAYBACK, + COMPRESSED_PASSTHROUGH_GEN); + } else { + ret = msm_pcm_routing_reg_phy_stream(soc_prtd->dai_link->id, + prtd->audio_client->perf_mode, + prtd->session_id, substream->stream); + } + if (ret) { + pr_err("%s: stream reg failed ret:%d\n", __func__, ret); + return ret; + } + if (prtd->compress_enable) { + ret = q6asm_media_format_block_gen_compr( + prtd->audio_client, runtime->rate, + runtime->channels, !prtd->set_channel_map, + prtd->channel_map, bits_per_sample); + } else { + + if ((q6core_get_avcs_api_version_per_service( + APRV2_IDS_SERVICE_ID_ADSP_ASM_V) >= + ADSP_ASM_API_VERSION_V2) && + q6core_use_Q6_32ch_support()) { + + ret = q6asm_media_format_block_multi_ch_pcm_v5( + prtd->audio_client, runtime->rate, + runtime->channels, !prtd->set_channel_map, + prtd->channel_map, bits_per_sample, + sample_word_size, ASM_LITTLE_ENDIAN, + DEFAULT_QF); + } else { + ret = q6asm_media_format_block_multi_ch_pcm_v4( + prtd->audio_client, runtime->rate, + runtime->channels, !prtd->set_channel_map, + prtd->channel_map, bits_per_sample, + sample_word_size, ASM_LITTLE_ENDIAN, + DEFAULT_QF); + } + } + if (ret < 0) + pr_info("%s: CMD Format block failed\n", __func__); + + atomic_set(&prtd->out_count, runtime->periods); + + prtd->enabled = 1; + prtd->cmd_pending = 0; + prtd->cmd_interrupt = 0; + + return 0; +} + +static int msm_pcm_capture_prepare(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct msm_audio *prtd = runtime->private_data; + struct snd_soc_pcm_runtime *soc_prtd = substream->private_data; + struct msm_plat_data *pdata; + struct snd_pcm_hw_params *params; + struct msm_pcm_routing_evt event; + int ret = 0; + int i = 0; + uint16_t bits_per_sample = 16; + uint16_t sample_word_size; + + pdata = (struct msm_plat_data *) + dev_get_drvdata(soc_prtd->platform->dev); + if (!pdata) { + pr_err("%s: platform data not populated\n", __func__); + return -EINVAL; + } + if (!prtd || !prtd->audio_client) { + pr_err("%s: private data null or audio client freed\n", + __func__); + return -EINVAL; + } + + if (prtd->enabled == IDLE) { + pr_debug("%s:perf_mode=%d periods=%d\n", __func__, + pdata->perf_mode, runtime->periods); + params = &soc_prtd->dpcm[substream->stream].hw_params; + if ((params_format(params) == SNDRV_PCM_FORMAT_S24_LE) || + (params_format(params) == SNDRV_PCM_FORMAT_S24_3LE)) + bits_per_sample = 24; + else if (params_format(params) == SNDRV_PCM_FORMAT_S32_LE) + bits_per_sample = 32; + + /* ULL mode is not supported in capture path */ + if (pdata->perf_mode == LEGACY_PCM_MODE) + prtd->audio_client->perf_mode = LEGACY_PCM_MODE; + else + prtd->audio_client->perf_mode = LOW_LATENCY_PCM_MODE; + + pr_debug("%s Opening %d-ch PCM read stream, perf_mode %d\n", + __func__, params_channels(params), + prtd->audio_client->perf_mode); + + if ((q6core_get_avcs_api_version_per_service( + APRV2_IDS_SERVICE_ID_ADSP_ASM_V) >= + ADSP_ASM_API_VERSION_V2) && + q6core_use_Q6_32ch_support()) + ret = q6asm_open_read_v5(prtd->audio_client, + FORMAT_LINEAR_PCM, + bits_per_sample, false, ENC_CFG_ID_NONE); + else + ret = q6asm_open_read_v4(prtd->audio_client, + FORMAT_LINEAR_PCM, + bits_per_sample, false, ENC_CFG_ID_NONE); + if (ret < 0) { + pr_err("%s: q6asm_open_read failed\n", __func__); + q6asm_audio_client_free(prtd->audio_client); + prtd->audio_client = NULL; + return -ENOMEM; + } + + ret = q6asm_send_cal(prtd->audio_client); + if (ret < 0) + pr_debug("%s : Send cal failed : %d", __func__, ret); + + pr_debug("%s: session ID %d\n", + __func__, prtd->audio_client->session); + prtd->session_id = prtd->audio_client->session; + event.event_func = msm_pcm_route_event_handler; + event.priv_data = (void *) prtd; + ret = msm_pcm_routing_reg_phy_stream_v2( + soc_prtd->dai_link->id, + prtd->audio_client->perf_mode, + prtd->session_id, substream->stream, + event); + if (ret) { + pr_err("%s: stream reg failed ret:%d\n", __func__, ret); + return ret; + } + } + + prtd->pcm_size = snd_pcm_lib_buffer_bytes(substream); + prtd->pcm_count = snd_pcm_lib_period_bytes(substream); + prtd->pcm_irq_pos = 0; + /* rate and channels are sent to audio driver */ + prtd->samp_rate = runtime->rate; + prtd->channel_mode = runtime->channels; + + if (prtd->enabled == IDLE || prtd->enabled == STOPPED) { + for (i = 0; i < runtime->periods; i++) + q6asm_read(prtd->audio_client); + prtd->periods = runtime->periods; + } + + if (prtd->enabled != IDLE) + return 0; + + switch (runtime->format) { + case SNDRV_PCM_FORMAT_S32_LE: + bits_per_sample = 32; + sample_word_size = 32; + break; + case SNDRV_PCM_FORMAT_S24_LE: + bits_per_sample = 24; + sample_word_size = 32; + break; + case SNDRV_PCM_FORMAT_S24_3LE: + bits_per_sample = 24; + sample_word_size = 24; + break; + case SNDRV_PCM_FORMAT_S16_LE: + default: + bits_per_sample = 16; + sample_word_size = 16; + break; + } + + pr_debug("%s: Samp_rate = %d Channel = %d bit width = %d, word size = %d\n", + __func__, prtd->samp_rate, prtd->channel_mode, + bits_per_sample, sample_word_size); + + if ((q6core_get_avcs_api_version_per_service( + APRV2_IDS_SERVICE_ID_ADSP_ASM_V) >= + ADSP_ASM_API_VERSION_V2) && + q6core_use_Q6_32ch_support()) + ret = q6asm_enc_cfg_blk_pcm_format_support_v5( + prtd->audio_client, + prtd->samp_rate, + prtd->channel_mode, + bits_per_sample, + sample_word_size, + ASM_LITTLE_ENDIAN, + DEFAULT_QF); + else + ret = q6asm_enc_cfg_blk_pcm_format_support_v4( + prtd->audio_client, + prtd->samp_rate, + prtd->channel_mode, + bits_per_sample, + sample_word_size, + ASM_LITTLE_ENDIAN, + DEFAULT_QF); + + if (ret < 0) + pr_debug("%s: cmd cfg pcm was block failed", __func__); + + prtd->enabled = RUNNING; + + return ret; +} + +static int msm_pcm_trigger(struct snd_pcm_substream *substream, int cmd) +{ + int ret = 0; + struct snd_pcm_runtime *runtime = substream->runtime; + struct msm_audio *prtd = runtime->private_data; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + if (prtd->qtime_lsw) { + pr_debug("%s: Trigger start at msw: %u, lsw: %u msec", + __func__, prtd->qtime_msw, prtd->qtime_lsw); + ret = q6asm_run_nowait(prtd->audio_client, 1, + prtd->qtime_msw, prtd->qtime_lsw); + prtd->qtime_lsw = 0; + prtd->qtime_msw = 0; + } else { + pr_debug("%s: Trigger start\n", __func__); + ret = q6asm_run_nowait(prtd->audio_client, 0, 0, 0); + } + break; + case SNDRV_PCM_TRIGGER_STOP: + pr_debug("SNDRV_PCM_TRIGGER_STOP\n"); + atomic_set(&prtd->start, 0); + if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK) { + prtd->enabled = STOPPED; + ret = q6asm_cmd_nowait(prtd->audio_client, CMD_PAUSE); + break; + } + /* pending CMD_EOS isn't expected */ + WARN_ON_ONCE(test_bit(CMD_EOS, &prtd->cmd_pending)); + set_bit(CMD_EOS, &prtd->cmd_pending); + ret = q6asm_cmd_nowait(prtd->audio_client, CMD_EOS); + if (ret) + clear_bit(CMD_EOS, &prtd->cmd_pending); + break; + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + pr_debug("SNDRV_PCM_TRIGGER_PAUSE\n"); + ret = q6asm_cmd_nowait(prtd->audio_client, CMD_PAUSE); + atomic_set(&prtd->start, 0); + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static int msm_pcm_open(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_soc_pcm_runtime *soc_prtd = substream->private_data; + struct msm_audio *prtd; + struct msm_plat_data *pdata; + int ret = 0; + + pdata = (struct msm_plat_data *) + dev_get_drvdata(soc_prtd->platform->dev); + if (!pdata) { + pr_err("%s: platform data not populated\n", __func__); + return -EINVAL; + } + prtd = kzalloc(sizeof(struct msm_audio), GFP_KERNEL); + if (prtd == NULL) + return -ENOMEM; + + prtd->substream = substream; + prtd->audio_client = q6asm_audio_client_alloc( + (app_cb)event_handler, prtd); + if (!prtd->audio_client) { + pr_info("%s: Could not allocate memory\n", __func__); + kfree(prtd); + return -ENOMEM; + } + + prtd->audio_client->dev = soc_prtd->platform->dev; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + runtime->hw = msm_pcm_hardware_playback; + + /* Capture path */ + else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + runtime->hw = msm_pcm_hardware_capture; + else { + pr_err("Invalid Stream type %d\n", substream->stream); + return -EINVAL; + } + + ret = snd_pcm_hw_constraint_list(runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + &constraints_sample_rates); + if (ret < 0) + pr_info("snd_pcm_hw_constraint_list failed\n"); + /* Ensure that buffer size is a multiple of period size */ + ret = snd_pcm_hw_constraint_integer(runtime, + SNDRV_PCM_HW_PARAM_PERIODS); + if (ret < 0) + pr_info("snd_pcm_hw_constraint_integer failed\n"); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + ret = snd_pcm_hw_constraint_minmax(runtime, + SNDRV_PCM_HW_PARAM_BUFFER_BYTES, + PLAYBACK_MIN_NUM_PERIODS * PLAYBACK_MIN_PERIOD_SIZE, + PLAYBACK_MAX_NUM_PERIODS * PLAYBACK_MAX_PERIOD_SIZE); + if (ret < 0) { + pr_err("constraint for buffer bytes min max ret = %d\n", + ret); + } + } + + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + ret = snd_pcm_hw_constraint_minmax(runtime, + SNDRV_PCM_HW_PARAM_BUFFER_BYTES, + CAPTURE_MIN_NUM_PERIODS * CAPTURE_MIN_PERIOD_SIZE, + CAPTURE_MAX_NUM_PERIODS * CAPTURE_MAX_PERIOD_SIZE); + if (ret < 0) { + pr_err("constraint for buffer bytes min max ret = %d\n", + ret); + } + } + ret = snd_pcm_hw_constraint_step(runtime, 0, + SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 32); + if (ret < 0) { + pr_err("constraint for period bytes step ret = %d\n", + ret); + } + ret = snd_pcm_hw_constraint_step(runtime, 0, + SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 32); + if (ret < 0) { + pr_err("constraint for buffer bytes step ret = %d\n", + ret); + } + + prtd->enabled = IDLE; + prtd->dsp_cnt = 0; + prtd->set_channel_map = false; + prtd->reset_event = false; + runtime->private_data = prtd; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + msm_adsp_init_mixer_ctl_pp_event_queue(soc_prtd); + + /* Vote to update the Rx thread priority to RT Thread for playback */ + if ((substream->stream == SNDRV_PCM_STREAM_PLAYBACK) && + (pdata->perf_mode == LOW_LATENCY_PCM_MODE)) + apr_start_rx_rt(prtd->audio_client->apr); + + return 0; +} + +static int msm_pcm_playback_copy(struct snd_pcm_substream *substream, int a, + unsigned long hwoff, void __user *buf, unsigned long fbytes) +{ + int ret = 0; + int xfer = 0; + char *bufptr = NULL; + void *data = NULL; + uint32_t idx = 0; + uint32_t size = 0; + uint32_t retries = 0; + + struct snd_pcm_runtime *runtime = substream->runtime; + struct msm_audio *prtd = runtime->private_data; + + pr_debug("%s: prtd->out_count = %d\n", + __func__, atomic_read(&prtd->out_count)); + + while ((fbytes > 0) && (retries < MAX_PB_COPY_RETRIES)) { + if (prtd->reset_event) { + pr_err("%s: In SSR return ENETRESET before wait\n", + __func__); + return -ENETRESET; + } + + ret = wait_event_timeout(the_locks.write_wait, + (atomic_read(&prtd->out_count)), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event_timeout failed\n", __func__); + ret = -ETIMEDOUT; + goto fail; + } + ret = 0; + + if (prtd->reset_event) { + pr_err("%s: In SSR return ENETRESET after wait\n", + __func__); + return -ENETRESET; + } + + if (!atomic_read(&prtd->out_count)) { + pr_err("%s: pcm stopped out_count 0\n", __func__); + return 0; + } + + data = q6asm_is_cpu_buf_avail(IN, prtd->audio_client, &size, + &idx); + if (data == NULL) { + retries++; + continue; + } else { + retries = 0; + } + + if (fbytes > size) + xfer = size; + else + xfer = fbytes; + + bufptr = data; + if (bufptr) { + pr_debug("%s:fbytes =%lu: xfer=%d size=%d\n", + __func__, fbytes, xfer, size); + if (copy_from_user(bufptr, buf, xfer)) { + ret = -EFAULT; + pr_err("%s: copy_from_user failed\n", + __func__); + q6asm_cpu_buf_release(IN, prtd->audio_client); + goto fail; + } + buf += xfer; + fbytes -= xfer; + pr_debug("%s:fbytes = %lu: xfer=%d\n", __func__, + fbytes, xfer); + if (atomic_read(&prtd->start)) { + pr_debug("%s:writing %d bytes of buffer to dsp\n", + __func__, xfer); + ret = q6asm_write(prtd->audio_client, xfer, + 0, 0, NO_TIMESTAMP); + if (ret < 0) { + ret = -EFAULT; + q6asm_cpu_buf_release(IN, + prtd->audio_client); + goto fail; + } + } else + atomic_inc(&prtd->out_needed); + atomic_dec(&prtd->out_count); + } + } +fail: + if (retries >= MAX_PB_COPY_RETRIES) + ret = -ENOMEM; + + return ret; +} + +static int msm_pcm_playback_close(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_soc_pcm_runtime *soc_prtd = substream->private_data; + struct msm_audio *prtd = runtime->private_data; + struct msm_plat_data *pdata; + uint32_t timeout; + int dir = 0; + int ret = 0; + + pr_debug("%s: cmd_pending 0x%lx\n", __func__, prtd->cmd_pending); + + if (prtd->audio_client) { + dir = IN; + + /* + * Unvote to downgrade the Rx thread priority from + * RT Thread for Low-Latency use case. + */ + pdata = (struct msm_plat_data *) + dev_get_drvdata(soc_prtd->platform->dev); + if (pdata) { + if (pdata->perf_mode == LOW_LATENCY_PCM_MODE) + apr_end_rx_rt(prtd->audio_client->apr); + } + /* determine timeout length */ + if (runtime->frame_bits == 0 || runtime->rate == 0) { + timeout = CMD_EOS_MIN_TIMEOUT_LENGTH; + } else { + timeout = (runtime->period_size * + CMD_EOS_TIMEOUT_MULTIPLIER) / + ((runtime->frame_bits / 8) * + runtime->rate); + if (timeout < CMD_EOS_MIN_TIMEOUT_LENGTH) + timeout = CMD_EOS_MIN_TIMEOUT_LENGTH; + } + pr_debug("%s: CMD_EOS timeout is %d\n", __func__, timeout); + + ret = wait_event_timeout(the_locks.eos_wait, + !test_bit(CMD_EOS, &prtd->cmd_pending), + timeout); + if (!ret) + pr_err("%s: CMD_EOS failed, cmd_pending 0x%lx\n", + __func__, prtd->cmd_pending); + q6asm_cmd(prtd->audio_client, CMD_CLOSE); + q6asm_audio_client_buf_free_contiguous(dir, + prtd->audio_client); + q6asm_audio_client_free(prtd->audio_client); + } + msm_pcm_routing_dereg_phy_stream(soc_prtd->dai_link->id, + SNDRV_PCM_STREAM_PLAYBACK); + msm_adsp_clean_mixer_ctl_pp_event_queue(soc_prtd); + kfree(prtd); + runtime->private_data = NULL; + + return 0; +} + +static int msm_pcm_capture_copy(struct snd_pcm_substream *substream, + int channel, unsigned long hwoff, void __user *buf, + unsigned long fbytes) +{ + int ret = 0; + int xfer; + char *bufptr; + void *data = NULL; + static uint32_t idx; + static uint32_t size; + uint32_t offset = 0; + struct snd_pcm_runtime *runtime = substream->runtime; + struct msm_audio *prtd = substream->runtime->private_data; + + + pr_debug("%s\n", __func__); + + pr_debug("appl_ptr %d\n", (int)runtime->control->appl_ptr); + pr_debug("hw_ptr %d\n", (int)runtime->status->hw_ptr); + pr_debug("avail_min %d\n", (int)runtime->control->avail_min); + + if (prtd->reset_event) { + pr_err("%s: In SSR return ENETRESET before wait\n", __func__); + return -ENETRESET; + } + ret = wait_event_timeout(the_locks.read_wait, + (atomic_read(&prtd->in_count)), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_debug("%s: wait_event_timeout failed\n", __func__); + goto fail; + } + if (prtd->reset_event) { + pr_err("%s: In SSR return ENETRESET after wait\n", __func__); + return -ENETRESET; + } + if (!atomic_read(&prtd->in_count)) { + pr_debug("%s: pcm stopped in_count 0\n", __func__); + return 0; + } + pr_debug("Checking if valid buffer is available...%pK\n", + data); + data = q6asm_is_cpu_buf_avail(OUT, prtd->audio_client, &size, &idx); + bufptr = data; + pr_debug("Size = %d\n", size); + pr_debug("fbytes = %lu\n", fbytes); + pr_debug("idx = %d\n", idx); + if (bufptr) { + xfer = fbytes; + if (xfer > size) + xfer = size; + offset = prtd->in_frame_info[idx].offset; + pr_debug("Offset value = %d\n", offset); + if (copy_to_user(buf, bufptr+offset, xfer)) { + pr_err("Failed to copy buf to user\n"); + ret = -EFAULT; + q6asm_cpu_buf_release(OUT, prtd->audio_client); + goto fail; + } + fbytes -= xfer; + size -= xfer; + prtd->in_frame_info[idx].offset += xfer; + pr_debug("%s:fbytes = %lu: size=%d: xfer=%d\n", + __func__, fbytes, size, xfer); + pr_debug(" Sending next buffer to dsp\n"); + memset(&prtd->in_frame_info[idx], 0, + sizeof(struct msm_audio_in_frame_info)); + atomic_dec(&prtd->in_count); + ret = q6asm_read(prtd->audio_client); + if (ret < 0) { + pr_err("q6asm read failed\n"); + ret = -EFAULT; + q6asm_cpu_buf_release(OUT, prtd->audio_client); + goto fail; + } + } else + pr_err("No valid buffer\n"); + + pr_debug("Returning from capture_copy... %d\n", ret); +fail: + return ret; +} + +static int msm_pcm_capture_close(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_soc_pcm_runtime *soc_prtd = substream->private_data; + struct msm_audio *prtd = runtime->private_data; + int dir = OUT; + + pr_debug("%s\n", __func__); + if (prtd->audio_client) { + q6asm_cmd(prtd->audio_client, CMD_CLOSE); + q6asm_audio_client_buf_free_contiguous(dir, + prtd->audio_client); + q6asm_audio_client_free(prtd->audio_client); + } + + msm_pcm_routing_dereg_phy_stream(soc_prtd->dai_link->id, + SNDRV_PCM_STREAM_CAPTURE); + kfree(prtd); + runtime->private_data = NULL; + + return 0; +} + +static int msm_pcm_copy(struct snd_pcm_substream *substream, int a, + unsigned long hwoff, void __user *buf, unsigned long fbytes) +{ + int ret = 0; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + ret = msm_pcm_playback_copy(substream, a, hwoff, buf, fbytes); + else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + ret = msm_pcm_capture_copy(substream, a, hwoff, buf, fbytes); + return ret; +} + +static int msm_pcm_close(struct snd_pcm_substream *substream) +{ + int ret = 0; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + ret = msm_pcm_playback_close(substream); + else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + ret = msm_pcm_capture_close(substream); + return ret; +} + +static int msm_pcm_prepare(struct snd_pcm_substream *substream) +{ + int ret = 0; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + ret = msm_pcm_playback_prepare(substream); + else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + ret = msm_pcm_capture_prepare(substream); + return ret; +} + +static snd_pcm_uframes_t msm_pcm_pointer(struct snd_pcm_substream *substream) +{ + + struct snd_pcm_runtime *runtime = substream->runtime; + struct msm_audio *prtd = runtime->private_data; + + if (prtd->pcm_irq_pos >= prtd->pcm_size) + prtd->pcm_irq_pos = 0; + + pr_debug("pcm_irq_pos = %d\n", prtd->pcm_irq_pos); + return bytes_to_frames(runtime, (prtd->pcm_irq_pos)); +} + +static int msm_pcm_mmap(struct snd_pcm_substream *substream, + struct vm_area_struct *vma) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct msm_audio *prtd = runtime->private_data; + struct audio_client *ac = prtd->audio_client; + struct audio_port_data *apd = ac->port; + struct audio_buffer *ab; + int dir = -1; + + prtd->mmap_flag = 1; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + dir = IN; + else + dir = OUT; + ab = &(apd[dir].buf[0]); + + return msm_audio_ion_mmap(ab, vma); +} + +static int msm_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct msm_audio *prtd = runtime->private_data; + struct snd_dma_buffer *dma_buf = &substream->dma_buffer; + struct audio_buffer *buf; + int dir, ret; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + dir = IN; + else + dir = OUT; + ret = q6asm_audio_client_buf_alloc_contiguous(dir, + prtd->audio_client, + (params_buffer_bytes(params) / params_periods(params)), + params_periods(params)); + if (ret < 0) { + pr_err("Audio Start: Buffer Allocation failed rc = %d\n", + ret); + return -ENOMEM; + } + buf = prtd->audio_client->port[dir].buf; + if (buf == NULL || buf[0].data == NULL) + return -ENOMEM; + + pr_debug("%s:buf = %pK\n", __func__, buf); + dma_buf->dev.type = SNDRV_DMA_TYPE_DEV; + dma_buf->dev.dev = substream->pcm->card->dev; + dma_buf->private_data = NULL; + dma_buf->area = buf[0].data; + dma_buf->addr = buf[0].phys; + dma_buf->bytes = params_buffer_bytes(params); + if (!dma_buf->area) + return -ENOMEM; + + snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); + return 0; +} + +static const struct snd_pcm_ops msm_pcm_ops = { + .open = msm_pcm_open, + .copy_user = msm_pcm_copy, + .hw_params = msm_pcm_hw_params, + .close = msm_pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .prepare = msm_pcm_prepare, + .trigger = msm_pcm_trigger, + .pointer = msm_pcm_pointer, + .mmap = msm_pcm_mmap, +}; + +static int msm_pcm_adsp_stream_cmd_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *pcm = snd_kcontrol_chip(kcontrol); + struct snd_soc_platform *platform = snd_soc_component_to_platform(pcm); + struct msm_plat_data *pdata = dev_get_drvdata(platform->dev); + struct snd_pcm_substream *substream; + struct msm_audio *prtd; + int ret = 0; + struct msm_adsp_event_data *event_data = NULL; + + if (!pdata) { + pr_err("%s pdata is NULL\n", __func__); + ret = -ENODEV; + goto done; + } + + substream = pdata->pcm[kcontrol->private_value]-> + streams[SNDRV_PCM_STREAM_PLAYBACK].substream; + if (!substream) { + pr_err("%s substream not found\n", __func__); + ret = -EINVAL; + goto done; + } + + if (!substream->runtime) { + pr_err("%s substream runtime not found\n", __func__); + ret = -EINVAL; + goto done; + } + + prtd = substream->runtime->private_data; + if (prtd->audio_client == NULL) { + pr_err("%s prtd is null.\n", __func__); + ret = -EINVAL; + goto done; + } + + event_data = (struct msm_adsp_event_data *)ucontrol->value.bytes.data; + if ((event_data->event_type < ADSP_STREAM_PP_EVENT) || + (event_data->event_type >= ADSP_STREAM_EVENT_MAX)) { + pr_err("%s: invalid event_type=%d", + __func__, event_data->event_type); + ret = -EINVAL; + goto done; + } + + if (event_data->payload_len > sizeof(ucontrol->value.bytes.data) + - sizeof(struct msm_adsp_event_data)) { + pr_err("%s param length=%d exceeds limit", + __func__, event_data->payload_len); + ret = -EINVAL; + goto done; + } + + ret = q6asm_send_stream_cmd(prtd->audio_client, event_data); + if (ret < 0) + pr_err("%s: failed to send stream event cmd, err = %d\n", + __func__, ret); +done: + return ret; +} + +static int msm_pcm_add_audio_adsp_stream_cmd_control( + struct snd_soc_pcm_runtime *rtd) +{ + const char *mixer_ctl_name = DSP_STREAM_CMD; + const char *deviceNo = "NN"; + char *mixer_str = NULL; + int ctl_len = 0, ret = 0; + struct snd_kcontrol_new fe_audio_adsp_stream_cmd_config_control[1] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "?", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = msm_adsp_stream_cmd_info, + .put = msm_pcm_adsp_stream_cmd_put, + .private_value = 0, + } + }; + + if (!rtd) { + pr_err("%s rtd is NULL\n", __func__); + ret = -EINVAL; + goto done; + } + + ctl_len = strlen(mixer_ctl_name) + 1 + strlen(deviceNo) + 1; + mixer_str = kzalloc(ctl_len, GFP_KERNEL); + if (!mixer_str) { + ret = -ENOMEM; + goto done; + } + + snprintf(mixer_str, ctl_len, "%s %d", mixer_ctl_name, rtd->pcm->device); + fe_audio_adsp_stream_cmd_config_control[0].name = mixer_str; + fe_audio_adsp_stream_cmd_config_control[0].private_value = + rtd->dai_link->id; + pr_debug("Registering new mixer ctl %s\n", mixer_str); + ret = snd_soc_add_platform_controls(rtd->platform, + fe_audio_adsp_stream_cmd_config_control, + ARRAY_SIZE(fe_audio_adsp_stream_cmd_config_control)); + if (ret < 0) + pr_err("%s: failed add ctl %s. err = %d\n", + __func__, mixer_str, ret); + + kfree(mixer_str); +done: + return ret; +} + +static int msm_pcm_add_audio_adsp_stream_callback_control( + struct snd_soc_pcm_runtime *rtd) +{ + const char *mixer_ctl_name = DSP_STREAM_CALLBACK; + const char *deviceNo = "NN"; + char *mixer_str = NULL; + int ctl_len = 0, ret = 0; + struct snd_kcontrol *kctl; + + struct snd_kcontrol_new fe_audio_adsp_callback_config_control[1] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "?", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = msm_adsp_stream_callback_info, + .get = msm_adsp_stream_callback_get, + .private_value = 0, + } + }; + + if (!rtd) { + pr_err("%s NULL rtd\n", __func__); + ret = -EINVAL; + goto done; + } + + pr_debug("%s: added new pcm FE with name %s, id %d, cpu dai %s, device no %d\n", + __func__, rtd->dai_link->name, rtd->dai_link->id, + rtd->dai_link->cpu_dai_name, rtd->pcm->device); + ctl_len = strlen(mixer_ctl_name) + 1 + strlen(deviceNo) + 1; + mixer_str = kzalloc(ctl_len, GFP_KERNEL); + if (!mixer_str) { + ret = -ENOMEM; + goto done; + } + + snprintf(mixer_str, ctl_len, "%s %d", mixer_ctl_name, rtd->pcm->device); + fe_audio_adsp_callback_config_control[0].name = mixer_str; + fe_audio_adsp_callback_config_control[0].private_value = + rtd->dai_link->id; + pr_debug("%s: Registering new mixer ctl %s\n", __func__, mixer_str); + ret = snd_soc_add_platform_controls(rtd->platform, + fe_audio_adsp_callback_config_control, + ARRAY_SIZE(fe_audio_adsp_callback_config_control)); + if (ret < 0) { + pr_err("%s: failed to add ctl %s. err = %d\n", + __func__, mixer_str, ret); + ret = -EINVAL; + goto free_mixer_str; + } + + kctl = snd_soc_card_get_kcontrol(rtd->card, mixer_str); + if (!kctl) { + pr_err("%s: failed to get kctl %s.\n", __func__, mixer_str); + ret = -EINVAL; + goto free_mixer_str; + } + + kctl->private_data = NULL; + +free_mixer_str: + kfree(mixer_str); +done: + return ret; +} + +static int msm_pcm_set_volume(struct msm_audio *prtd, uint32_t volume) +{ + int rc = 0; + + if (prtd && prtd->audio_client) { + pr_debug("%s: channels %d volume 0x%x\n", __func__, + prtd->channel_mode, volume); + rc = q6asm_set_volume(prtd->audio_client, volume); + if (rc < 0) { + pr_err("%s: Send Volume command failed rc=%d\n", + __func__, rc); + } + } + return rc; +} + +static int msm_pcm_volume_ctl_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_pcm_volume *vol = snd_kcontrol_chip(kcontrol); + struct snd_pcm_substream *substream = + vol->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; + struct msm_audio *prtd; + + pr_debug("%s\n", __func__); + if (!substream) { + pr_err("%s substream not found\n", __func__); + return -ENODEV; + } + if (!substream->runtime) { + pr_debug("%s substream runtime not found\n", __func__); + return 0; + } + prtd = substream->runtime->private_data; + if (prtd) + ucontrol->value.integer.value[0] = prtd->volume; + return 0; +} + +static int msm_pcm_volume_ctl_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int rc = 0; + struct snd_pcm_volume *vol = snd_kcontrol_chip(kcontrol); + struct snd_pcm_substream *substream = + vol->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; + struct msm_audio *prtd; + int volume = ucontrol->value.integer.value[0]; + + pr_debug("%s: volume : 0x%x\n", __func__, volume); + if (!substream) { + pr_err("%s substream not found\n", __func__); + return -ENODEV; + } + if (!substream->runtime) { + pr_err("%s substream runtime not found\n", __func__); + return 0; + } + prtd = substream->runtime->private_data; + if (prtd) { + rc = msm_pcm_set_volume(prtd, volume); + prtd->volume = volume; + } + return rc; +} + +static int msm_pcm_add_volume_control(struct snd_soc_pcm_runtime *rtd) +{ + int ret = 0; + struct snd_pcm *pcm = rtd->pcm; + struct snd_pcm_volume *volume_info; + struct snd_kcontrol *kctl; + + dev_dbg(rtd->dev, "%s, Volume control add\n", __func__); + ret = snd_pcm_add_volume_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK, + NULL, 1, rtd->dai_link->id, + &volume_info); + if (ret < 0) { + pr_err("%s volume control failed ret %d\n", __func__, ret); + return ret; + } + kctl = volume_info->kctl; + kctl->put = msm_pcm_volume_ctl_put; + kctl->get = msm_pcm_volume_ctl_get; + kctl->tlv.p = msm_pcm_vol_gain; + return 0; +} + +static int msm_pcm_compress_ctl_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 0x2000; + return 0; +} + +static int msm_pcm_compress_ctl_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol); + struct snd_soc_platform *platform = snd_soc_component_to_platform(comp); + struct msm_plat_data *pdata = dev_get_drvdata(platform->dev); + struct snd_pcm_substream *substream; + struct msm_audio *prtd; + + if (!pdata) { + pr_err("%s pdata is NULL\n", __func__); + return -ENODEV; + } + substream = pdata->pcm[kcontrol->private_value]-> + streams[SNDRV_PCM_STREAM_PLAYBACK].substream; + if (!substream) { + pr_err("%s substream not found\n", __func__); + return -EINVAL; + } + if (!substream->runtime) { + pr_debug("%s substream runtime not found\n", __func__); + return 0; + } + prtd = substream->runtime->private_data; + if (prtd) + ucontrol->value.integer.value[0] = prtd->compress_enable; + return 0; +} + +static int msm_pcm_compress_ctl_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int rc = 0; + struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol); + struct snd_soc_platform *platform = snd_soc_component_to_platform(comp); + struct msm_plat_data *pdata = dev_get_drvdata(platform->dev); + struct snd_pcm_substream *substream; + struct msm_audio *prtd; + int compress = ucontrol->value.integer.value[0]; + + if (!pdata) { + pr_err("%s pdata is NULL\n", __func__); + return -ENODEV; + } + substream = pdata->pcm[kcontrol->private_value]-> + streams[SNDRV_PCM_STREAM_PLAYBACK].substream; + pr_debug("%s: compress : 0x%x\n", __func__, compress); + if (!substream) { + pr_err("%s substream not found\n", __func__); + return -EINVAL; + } + if (!substream->runtime) { + pr_err("%s substream runtime not found\n", __func__); + return 0; + } + prtd = substream->runtime->private_data; + if (prtd) { + pr_debug("%s: setting compress flag to 0x%x\n", + __func__, compress); + prtd->compress_enable = compress; + } + return rc; +} + +static int msm_pcm_add_compress_control(struct snd_soc_pcm_runtime *rtd) +{ + const char *mixer_ctl_name = "Playback "; + const char *mixer_ctl_end_name = " Compress"; + const char *deviceNo = "NN"; + char *mixer_str = NULL; + int ctl_len; + int ret = 0; + struct msm_plat_data *pdata; + struct snd_kcontrol_new pcm_compress_control[1] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "?", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = msm_pcm_compress_ctl_info, + .get = msm_pcm_compress_ctl_get, + .put = msm_pcm_compress_ctl_put, + .private_value = 0, + } + }; + + if (!rtd) { + pr_err("%s: NULL rtd\n", __func__); + return -EINVAL; + } + + ctl_len = strlen(mixer_ctl_name) + strlen(deviceNo) + + strlen(mixer_ctl_end_name) + 1; + mixer_str = kzalloc(ctl_len, GFP_KERNEL); + + if (!mixer_str) + return -ENOMEM; + + snprintf(mixer_str, ctl_len, "%s%d%s", mixer_ctl_name, + rtd->pcm->device, mixer_ctl_end_name); + + pcm_compress_control[0].name = mixer_str; + pcm_compress_control[0].private_value = rtd->dai_link->id; + pr_debug("%s: Registering new mixer ctl %s\n", __func__, mixer_str); + pdata = dev_get_drvdata(rtd->platform->dev); + if (pdata) { + pdata->pcm[rtd->dai_link->id] = rtd->pcm; + snd_soc_add_platform_controls(rtd->platform, + pcm_compress_control, + ARRAY_SIZE + (pcm_compress_control)); + pr_debug("%s: add control success plt = %pK\n", + __func__, rtd->platform); + } else { + pr_err("%s: NULL pdata\n", __func__); + ret = -EINVAL; + } + kfree(mixer_str); + return ret; +} + +static int msm_pcm_chmap_ctl_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int i; + struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); + unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); + struct snd_pcm_substream *substream; + struct msm_audio *prtd; + + pr_debug("%s", __func__); + substream = snd_pcm_chmap_substream(info, idx); + if (!substream) + return -ENODEV; + if (!substream->runtime) + return 0; + + prtd = substream->runtime->private_data; + if (prtd) { + prtd->set_channel_map = true; + for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++) + prtd->channel_map[i] = + (char)(ucontrol->value.integer.value[i]); + } + return 0; +} + +static int msm_pcm_chmap_ctl_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int i; + struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); + unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); + struct snd_pcm_substream *substream; + struct msm_audio *prtd; + + pr_debug("%s", __func__); + substream = snd_pcm_chmap_substream(info, idx); + if (!substream) + return -ENODEV; + memset(ucontrol->value.integer.value, 0, + sizeof(ucontrol->value.integer.value)); + if (!substream->runtime) + return 0; /* no channels set */ + + prtd = substream->runtime->private_data; + + if (prtd && prtd->set_channel_map == true) { + for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++) + ucontrol->value.integer.value[i] = + (int)prtd->channel_map[i]; + } else { + for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++) + ucontrol->value.integer.value[i] = 0; + } + + return 0; +} + +static int msm_pcm_add_chmap_controls(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_pcm *pcm = rtd->pcm; + struct snd_pcm_chmap *chmap_info; + struct snd_kcontrol *kctl; + char device_num[12]; + int i, ret = 0; + + pr_debug("%s, Channel map cntrl add\n", __func__); + ret = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK, + snd_pcm_std_chmaps, + PCM_FORMAT_MAX_NUM_CHANNEL_V8, 0, + &chmap_info); + if (ret < 0) { + pr_err("%s, channel map cntrl add failed\n", __func__); + return ret; + } + kctl = chmap_info->kctl; + for (i = 0; i < kctl->count; i++) + kctl->vd[i].access |= SNDRV_CTL_ELEM_ACCESS_WRITE; + snprintf(device_num, sizeof(device_num), "%d", pcm->device); + strlcat(kctl->id.name, device_num, sizeof(kctl->id.name)); + pr_debug("%s, Overwriting channel map control name to: %s\n", + __func__, kctl->id.name); + kctl->put = msm_pcm_chmap_ctl_put; + kctl->get = msm_pcm_chmap_ctl_get; + return 0; +} + +static int msm_pcm_playback_app_type_cfg_ctl_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u64 fe_id = kcontrol->private_value; + int session_type = SESSION_TYPE_RX; + int be_id = ucontrol->value.integer.value[3]; + struct msm_pcm_stream_app_type_cfg cfg_data = {0, 0, 48000}; + int ret = 0; + + cfg_data.app_type = ucontrol->value.integer.value[0]; + cfg_data.acdb_dev_id = ucontrol->value.integer.value[1]; + if (ucontrol->value.integer.value[2] != 0) + cfg_data.sample_rate = ucontrol->value.integer.value[2]; + pr_debug("%s: fe_id- %llu session_type- %d be_id- %d app_type- %d acdb_dev_id- %d sample_rate- %d\n", + __func__, fe_id, session_type, be_id, + cfg_data.app_type, cfg_data.acdb_dev_id, cfg_data.sample_rate); + ret = msm_pcm_routing_reg_stream_app_type_cfg(fe_id, session_type, + be_id, &cfg_data); + if (ret < 0) + pr_err("%s: msm_pcm_routing_reg_stream_app_type_cfg failed returned %d\n", + __func__, ret); + + return ret; +} + +static int msm_pcm_playback_app_type_cfg_ctl_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u64 fe_id = kcontrol->private_value; + int session_type = SESSION_TYPE_RX; + int be_id = 0; + struct msm_pcm_stream_app_type_cfg cfg_data = {0}; + int ret = 0; + + ret = msm_pcm_routing_get_stream_app_type_cfg(fe_id, session_type, + &be_id, &cfg_data); + if (ret < 0) { + pr_err("%s: msm_pcm_routing_get_stream_app_type_cfg failed returned %d\n", + __func__, ret); + goto done; + } + + ucontrol->value.integer.value[0] = cfg_data.app_type; + ucontrol->value.integer.value[1] = cfg_data.acdb_dev_id; + ucontrol->value.integer.value[2] = cfg_data.sample_rate; + ucontrol->value.integer.value[3] = be_id; + pr_debug("%s: fedai_id %llu, session_type %d, be_id %d, app_type %d, acdb_dev_id %d, sample_rate %d\n", + __func__, fe_id, session_type, be_id, + cfg_data.app_type, cfg_data.acdb_dev_id, cfg_data.sample_rate); +done: + return ret; +} + +static int msm_pcm_capture_app_type_cfg_ctl_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u64 fe_id = kcontrol->private_value; + int session_type = SESSION_TYPE_TX; + int be_id = ucontrol->value.integer.value[3]; + struct msm_pcm_stream_app_type_cfg cfg_data = {0, 0, 48000}; + int ret = 0; + + cfg_data.app_type = ucontrol->value.integer.value[0]; + cfg_data.acdb_dev_id = ucontrol->value.integer.value[1]; + if (ucontrol->value.integer.value[2] != 0) + cfg_data.sample_rate = ucontrol->value.integer.value[2]; + pr_debug("%s: fe_id- %llu session_type- %d be_id- %d app_type- %d acdb_dev_id- %d sample_rate- %d\n", + __func__, fe_id, session_type, be_id, + cfg_data.app_type, cfg_data.acdb_dev_id, cfg_data.sample_rate); + ret = msm_pcm_routing_reg_stream_app_type_cfg(fe_id, session_type, + be_id, &cfg_data); + if (ret < 0) + pr_err("%s: msm_pcm_routing_reg_stream_app_type_cfg failed returned %d\n", + __func__, ret); + + return ret; +} + +static int msm_pcm_capture_app_type_cfg_ctl_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u64 fe_id = kcontrol->private_value; + int session_type = SESSION_TYPE_TX; + int be_id = 0; + struct msm_pcm_stream_app_type_cfg cfg_data = {0}; + int ret = 0; + + ret = msm_pcm_routing_get_stream_app_type_cfg(fe_id, session_type, + &be_id, &cfg_data); + if (ret < 0) { + pr_err("%s: msm_pcm_routing_get_stream_app_type_cfg failed returned %d\n", + __func__, ret); + goto done; + } + + ucontrol->value.integer.value[0] = cfg_data.app_type; + ucontrol->value.integer.value[1] = cfg_data.acdb_dev_id; + ucontrol->value.integer.value[2] = cfg_data.sample_rate; + ucontrol->value.integer.value[3] = be_id; + pr_debug("%s: fedai_id %llu, session_type %d, be_id %d, app_type %d, acdb_dev_id %d, sample_rate %d\n", + __func__, fe_id, session_type, be_id, + cfg_data.app_type, cfg_data.acdb_dev_id, cfg_data.sample_rate); +done: + return ret; +} + +static int msm_pcm_add_app_type_controls(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_pcm *pcm = rtd->pcm; + struct snd_pcm_usr *app_type_info; + struct snd_kcontrol *kctl; + const char *playback_mixer_ctl_name = "Audio Stream"; + const char *capture_mixer_ctl_name = "Audio Stream Capture"; + const char *deviceNo = "NN"; + const char *suffix = "App Type Cfg"; + int ctl_len, ret = 0; + + if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) { + ctl_len = strlen(playback_mixer_ctl_name) + 1 + + strlen(deviceNo) + 1 + strlen(suffix) + 1; + pr_debug("%s: Playback app type cntrl add\n", __func__); + ret = snd_pcm_add_usr_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK, + NULL, 1, ctl_len, rtd->dai_link->id, + &app_type_info); + if (ret < 0) { + pr_err("%s: playback app type cntrl add failed: %d\n", + __func__, ret); + return ret; + } + kctl = app_type_info->kctl; + snprintf(kctl->id.name, ctl_len, "%s %d %s", + playback_mixer_ctl_name, rtd->pcm->device, suffix); + kctl->put = msm_pcm_playback_app_type_cfg_ctl_put; + kctl->get = msm_pcm_playback_app_type_cfg_ctl_get; + } + + if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) { + ctl_len = strlen(capture_mixer_ctl_name) + 1 + + strlen(deviceNo) + 1 + strlen(suffix) + 1; + pr_debug("%s: Capture app type cntrl add\n", __func__); + ret = snd_pcm_add_usr_ctls(pcm, SNDRV_PCM_STREAM_CAPTURE, + NULL, 1, ctl_len, rtd->dai_link->id, + &app_type_info); + if (ret < 0) { + pr_err("%s: capture app type cntrl add failed: %d\n", + __func__, ret); + return ret; + } + kctl = app_type_info->kctl; + snprintf(kctl->id.name, ctl_len, "%s %d %s", + capture_mixer_ctl_name, rtd->pcm->device, suffix); + kctl->put = msm_pcm_capture_app_type_cfg_ctl_put; + kctl->get = msm_pcm_capture_app_type_cfg_ctl_get; + } + + return 0; +} + +static int msm_pcm_qtimer_ctl_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 0x200000; + return 0; +} + +static int msm_pcm_qtimer_ctl_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol); + struct snd_soc_platform *platform = snd_soc_component_to_platform(comp); + struct msm_plat_data *pdata = dev_get_drvdata(platform->dev); + struct snd_pcm_substream *substream; + struct msm_audio *prtd; + + if (!pdata) { + pr_err("%s pdata is NULL\n", __func__); + return -ENODEV; + } + + substream = pdata->pcm[kcontrol->private_value]-> + streams[SNDRV_PCM_STREAM_PLAYBACK].substream; + if (!substream) { + pr_err("%s substream not found\n", __func__); + return -EINVAL; + } + + if (!substream->runtime) { + pr_err("%s substream runtime not found\n", __func__); + return 0; + } + + prtd = substream->runtime->private_data; + if (prtd) { + prtd->qtime_lsw = ucontrol->value.integer.value[0]; + prtd->qtime_msw = ucontrol->value.integer.value[1]; + } + + return 0; +} + +static int msm_pcm_add_qtimer_control(struct snd_soc_pcm_runtime *rtd) +{ + const char *mixer_ctl_name = "QTimer"; + const char *deviceNo = "NN"; + char *mixer_str = NULL; + int ctl_len = 0, ret = 0; + struct snd_kcontrol_new qtimer_control[1] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "?", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = msm_pcm_qtimer_ctl_info, + .put = msm_pcm_qtimer_ctl_put, + .private_value = 0, + } + }; + + ctl_len = strlen(mixer_ctl_name) + 1 + strlen(deviceNo) + 1; + mixer_str = kzalloc(ctl_len, GFP_KERNEL); + if (!mixer_str) { + ret = -ENOMEM; + goto done; + } + + snprintf(mixer_str, ctl_len, "%s %d", mixer_ctl_name, rtd->pcm->device); + qtimer_control[0].name = mixer_str; + qtimer_control[0].private_value = rtd->dai_link->id; + ret = snd_soc_add_platform_controls(rtd->platform, + qtimer_control, + ARRAY_SIZE(qtimer_control)); + if (ret < 0) + pr_err("%s: failed add ctl %s. err = %d\n", + __func__, mixer_str, ret); + + kfree(mixer_str); +done: + return ret; +} + +static int msm_pcm_add_controls(struct snd_soc_pcm_runtime *rtd) +{ + int ret = 0; + + pr_debug("%s\n", __func__); + ret = msm_pcm_add_chmap_controls(rtd); + if (ret) + pr_err("%s: pcm add controls failed:%d\n", __func__, ret); + ret = msm_pcm_add_app_type_controls(rtd); + if (ret) + pr_err("%s: pcm add app type controls failed:%d\n", + __func__, ret); + + ret = msm_pcm_add_qtimer_control(rtd); + if (ret) + pr_err("%s: pcm add qtimer controls failed:%d\n", + __func__, ret); + + return ret; +} + +static int msm_asoc_pcm_new(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_card *card = rtd->card->snd_card; + int ret = 0; + + if (!card->dev->coherent_dma_mask) + card->dev->coherent_dma_mask = DMA_BIT_MASK(32); + + ret = msm_pcm_add_controls(rtd); + if (ret) { + pr_err("%s, kctl add failed:%d\n", __func__, ret); + return ret; + } + + ret = msm_pcm_add_volume_control(rtd); + if (ret) + pr_err("%s: Could not add pcm Volume Control %d\n", + __func__, ret); + + ret = msm_pcm_add_compress_control(rtd); + if (ret) + pr_err("%s: Could not add pcm Compress Control %d\n", + __func__, ret); + + ret = msm_pcm_add_audio_adsp_stream_cmd_control(rtd); + if (ret) + pr_err("%s: Could not add pcm ADSP Stream Cmd Control\n", + __func__); + + ret = msm_pcm_add_audio_adsp_stream_callback_control(rtd); + if (ret) + pr_err("%s: Could not add pcm ADSP Stream Callback Control\n", + __func__); + + return ret; +} + +static snd_pcm_sframes_t msm_pcm_delay_blk(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct msm_audio *prtd = runtime->private_data; + struct audio_client *ac = prtd->audio_client; + snd_pcm_sframes_t frames; + int ret; + + ret = q6asm_get_path_delay(prtd->audio_client); + if (ret) { + pr_err("%s: get_path_delay failed, ret=%d\n", __func__, ret); + return 0; + } + + /* convert microseconds to frames */ + frames = ac->path_delay / 1000 * runtime->rate / 1000; + + /* also convert the remainder from the initial division */ + frames += ac->path_delay % 1000 * runtime->rate / 1000000; + + /* overcompensate for the loss of precision (empirical) */ + frames += 2; + + return frames; +} + +static struct snd_soc_platform_driver msm_soc_platform = { + .ops = &msm_pcm_ops, + .pcm_new = msm_asoc_pcm_new, + .delay_blk = msm_pcm_delay_blk, +}; + +static int msm_pcm_probe(struct platform_device *pdev) +{ + int rc; + int id; + struct msm_plat_data *pdata; + const char *latency_level; + + rc = of_property_read_u32(pdev->dev.of_node, + "qcom,msm-pcm-dsp-id", &id); + if (rc) { + dev_err(&pdev->dev, "%s: qcom,msm-pcm-dsp-id missing in DT node\n", + __func__); + return rc; + } + + pdata = kzalloc(sizeof(struct msm_plat_data), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + if (of_property_read_bool(pdev->dev.of_node, + "qcom,msm-pcm-low-latency")) { + + pdata->perf_mode = LOW_LATENCY_PCM_MODE; + rc = of_property_read_string(pdev->dev.of_node, + "qcom,latency-level", &latency_level); + if (!rc) { + if (!strcmp(latency_level, "ultra")) + pdata->perf_mode = ULTRA_LOW_LATENCY_PCM_MODE; + else if (!strcmp(latency_level, "ull-pp")) + pdata->perf_mode = + ULL_POST_PROCESSING_PCM_MODE; + } + } else { + pdata->perf_mode = LEGACY_PCM_MODE; + } + + dev_set_drvdata(&pdev->dev, pdata); + + + dev_dbg(&pdev->dev, "%s: dev name %s\n", + __func__, dev_name(&pdev->dev)); + return snd_soc_register_platform(&pdev->dev, + &msm_soc_platform); +} + +static int msm_pcm_remove(struct platform_device *pdev) +{ + struct msm_plat_data *pdata; + + pdata = dev_get_drvdata(&pdev->dev); + kfree(pdata); + snd_soc_unregister_platform(&pdev->dev); + return 0; +} +static const struct of_device_id msm_pcm_dt_match[] = { + {.compatible = "qcom,msm-pcm-dsp"}, + {} +}; +MODULE_DEVICE_TABLE(of, msm_pcm_dt_match); + +static struct platform_driver msm_pcm_driver = { + .driver = { + .name = "msm-pcm-dsp", + .owner = THIS_MODULE, + .of_match_table = msm_pcm_dt_match, + }, + .probe = msm_pcm_probe, + .remove = msm_pcm_remove, +}; + +int __init msm_pcm_dsp_init(void) +{ + init_waitqueue_head(&the_locks.enable_wait); + init_waitqueue_head(&the_locks.eos_wait); + init_waitqueue_head(&the_locks.write_wait); + init_waitqueue_head(&the_locks.read_wait); + + return platform_driver_register(&msm_pcm_driver); +} + +void msm_pcm_dsp_exit(void) +{ + platform_driver_unregister(&msm_pcm_driver); +} + +MODULE_DESCRIPTION("PCM module platform driver"); +MODULE_LICENSE("GPL v2"); diff --git a/audio-kernel/asoc/msm-pcm-q6-v2.h b/audio-kernel/asoc/msm-pcm-q6-v2.h new file mode 100644 index 000000000..a4b423523 --- /dev/null +++ b/audio-kernel/asoc/msm-pcm-q6-v2.h @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2008 Google, Inc. + * Copyright (C) 2008 HTC Corporation + * Copyright (c) 2012-2017,2019 The Linux Foundation. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * You should have received a copy of the GNU General Public License + * along with this program; if not, you can find it at http://www.fsf.org. + */ + +#ifndef _MSM_PCM_H +#define _MSM_PCM_H +#include +#include +#include "msm-pcm-routing-v2.h" + + +/* Support unconventional sample rates 12000, 24000 as well */ +#define USE_RATE \ + (SNDRV_PCM_RATE_8000_48000 | SNDRV_PCM_RATE_KNOT) + +extern int copy_count; + +struct buffer { + void *data; + unsigned int size; + unsigned int used; + unsigned int addr; +}; + +struct buffer_rec { + void *data; + unsigned int size; + unsigned int read; + unsigned int addr; +}; + +struct audio_locks { + spinlock_t event_lock; + wait_queue_head_t read_wait; + wait_queue_head_t write_wait; + wait_queue_head_t eos_wait; + wait_queue_head_t enable_wait; + wait_queue_head_t flush_wait; +}; + +struct msm_audio_in_frame_info { + uint32_t size; + uint32_t offset; +}; + +#define PLAYBACK_MIN_NUM_PERIODS 2 +#define PLAYBACK_MAX_NUM_PERIODS 8 +#define PLAYBACK_MAX_PERIOD_SIZE 122880 +#define PLAYBACK_MIN_PERIOD_SIZE 128 +#define CAPTURE_MIN_NUM_PERIODS 2 +#define CAPTURE_MAX_NUM_PERIODS 8 +#define CAPTURE_MAX_PERIOD_SIZE 122880 +#define CAPTURE_MIN_PERIOD_SIZE 320 + +struct msm_audio { + struct snd_pcm_substream *substream; + unsigned int pcm_size; + unsigned int pcm_count; + unsigned int pcm_irq_pos; /* IRQ position */ + uint16_t source; /* Encoding source bit mask */ + + struct audio_client *audio_client; + + uint16_t session_id; + + uint32_t samp_rate; + uint32_t channel_mode; + uint32_t dsp_cnt; + + int abort; /* set when error, like sample rate mismatch */ + + bool reset_event; + int enabled; + int close_ack; + int cmd_ack; + /* + * cmd_ack doesn't tell if paticular command has been sent so can't + * determine if it needs to wait for completion. + * Use cmd_pending instead when checking whether a command is been + * sent or not. + */ + unsigned long cmd_pending; + atomic_t start; + atomic_t stop; + atomic_t out_count; + atomic_t in_count; + atomic_t out_needed; + atomic_t eos; + int out_head; + int periods; + int mmap_flag; + atomic_t pending_buffer; + bool set_channel_map; + char channel_map[PCM_FORMAT_MAX_NUM_CHANNEL_V8]; + int cmd_interrupt; + bool meta_data_mode; + uint32_t volume; + bool compress_enable; + uint32_t qtime_lsw; + uint32_t qtime_msw; + /* array of frame info */ + struct msm_audio_in_frame_info in_frame_info[CAPTURE_MAX_NUM_PERIODS]; +}; + +struct output_meta_data_st { + uint32_t meta_data_length; + uint32_t frame_size; + uint32_t timestamp_lsw; + uint32_t timestamp_msw; + uint32_t reserved[12]; +}; + +struct msm_plat_data { + int perf_mode; + struct snd_pcm *pcm[MSM_FRONTEND_DAI_MAX]; + struct msm_pcm_ch_map *ch_map[MSM_FRONTEND_DAI_MAX]; +}; + +struct msm_pcm_ch_map { + bool set_ch_map; + char channel_map[PCM_FORMAT_MAX_NUM_CHANNEL]; +}; + +#endif /*_MSM_PCM_H*/ diff --git a/audio-kernel/asoc/msm-pcm-routing-devdep.c b/audio-kernel/asoc/msm-pcm-routing-devdep.c new file mode 100644 index 000000000..733595152 --- /dev/null +++ b/audio-kernel/asoc/msm-pcm-routing-devdep.c @@ -0,0 +1,139 @@ +/* Copyright (c) 2014, 2016-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 + * 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 +#include +#include +#include +#include +#include +#include "msm-pcm-routing-devdep.h" +#include "msm-ds2-dap-config.h" + +#ifdef CONFIG_SND_HWDEP +static int msm_pcm_routing_hwdep_open(struct snd_hwdep *hw, struct file *file) +{ + pr_debug("%s\n", __func__); + msm_ds2_dap_update_port_parameters(hw, file, true); + return 0; +} + +static int msm_pcm_routing_hwdep_release(struct snd_hwdep *hw, + struct file *file) +{ + pr_debug("%s\n", __func__); + msm_ds2_dap_update_port_parameters(hw, file, false); + return 0; +} + +static int msm_pcm_routing_hwdep_ioctl(struct snd_hwdep *hw, struct file *file, + unsigned int cmd, unsigned long arg) +{ + int ret = 0; + void __user *argp = (void __user *)arg; + + pr_debug("%s:cmd %x\n", __func__, cmd); + switch (cmd) { + case SNDRV_DEVDEP_DAP_IOCTL_SET_PARAM: + case SNDRV_DEVDEP_DAP_IOCTL_GET_PARAM: + case SNDRV_DEVDEP_DAP_IOCTL_DAP_COMMAND: + case SNDRV_DEVDEP_DAP_IOCTL_DAP_LICENSE: + msm_pcm_routing_acquire_lock(); + ret = msm_ds2_dap_ioctl(hw, file, cmd, argp); + msm_pcm_routing_release_lock(); + break; + case SNDRV_DEVDEP_DAP_IOCTL_GET_VISUALIZER: + ret = msm_ds2_dap_ioctl(hw, file, cmd, argp); + break; + default: + pr_err("%s called with invalid control 0x%X\n", __func__, cmd); + ret = -EINVAL; + break; + } + return ret; +} + +void msm_pcm_routing_hwdep_free(struct snd_pcm *pcm) +{ + pr_debug("%s\n", __func__); +} + +#ifdef CONFIG_COMPAT +static int msm_pcm_routing_hwdep_compat_ioctl(struct snd_hwdep *hw, + struct file *file, + unsigned int cmd, + unsigned long arg) +{ + int ret = 0; + void __user *argp = (void __user *)arg; + + pr_debug("%s:cmd %x\n", __func__, cmd); + switch (cmd) { + case SNDRV_DEVDEP_DAP_IOCTL_SET_PARAM32: + case SNDRV_DEVDEP_DAP_IOCTL_GET_PARAM32: + case SNDRV_DEVDEP_DAP_IOCTL_DAP_COMMAND32: + case SNDRV_DEVDEP_DAP_IOCTL_DAP_LICENSE32: + msm_pcm_routing_acquire_lock(); + ret = msm_ds2_dap_compat_ioctl(hw, file, cmd, argp); + msm_pcm_routing_release_lock(); + break; + case SNDRV_DEVDEP_DAP_IOCTL_GET_VISUALIZER32: + ret = msm_ds2_dap_compat_ioctl(hw, file, cmd, argp); + break; + default: + pr_err("%s called with invalid control 0x%X\n", __func__, cmd); + ret = -EINVAL; + break; + } + return ret; +} +#endif + +int msm_pcm_routing_hwdep_new(struct snd_soc_pcm_runtime *runtime, + struct msm_pcm_routing_bdai_data *msm_bedais) +{ + struct snd_hwdep *hwdep; + struct snd_soc_dai_link *dai_link = runtime->dai_link; + int rc; + + if (dai_link->id < 0 || + dai_link->id >= MSM_BACKEND_DAI_MAX) { + pr_err("%s:BE id %d invalid index\n", + __func__, dai_link->id); + return -EINVAL; + } + pr_debug("%s BE id %d\n", __func__, dai_link->id); + rc = snd_hwdep_new(runtime->card->snd_card, + msm_bedais[dai_link->id].name, + dai_link->id, &hwdep); + if (hwdep == NULL) { + pr_err("%s: hwdep intf failed to create %s- hwdep NULL\n", + __func__, msm_bedais[dai_link->id].name); + return rc; + } + if (rc < 0) { + pr_err("%s: hwdep intf failed to create %s rc %d\n", __func__, + msm_bedais[dai_link->id].name, rc); + return rc; + } + + hwdep->iface = SNDRV_HWDEP_IFACE_AUDIO_BE; + hwdep->private_data = &msm_bedais[dai_link->id]; + hwdep->ops.open = msm_pcm_routing_hwdep_open; + hwdep->ops.ioctl = msm_pcm_routing_hwdep_ioctl; + hwdep->ops.release = msm_pcm_routing_hwdep_release; +#ifdef CONFIG_COMPAT + hwdep->ops.ioctl_compat = msm_pcm_routing_hwdep_compat_ioctl; +#endif + return rc; +} +#endif diff --git a/audio-kernel/asoc/msm-pcm-routing-devdep.h b/audio-kernel/asoc/msm-pcm-routing-devdep.h new file mode 100644 index 000000000..8a0b96700 --- /dev/null +++ b/audio-kernel/asoc/msm-pcm-routing-devdep.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2014-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 + * 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 _MSM_PCM_ROUTING_DEVDEP_H_ +#define _MSM_PCM_ROUTING_DEVDEP_H_ + +#include +#include "msm-pcm-routing-v2.h" + +#ifdef CONFIG_SND_HWDEP +int msm_pcm_routing_hwdep_new(struct snd_soc_pcm_runtime *runtime, + struct msm_pcm_routing_bdai_data *msm_bedais); +void msm_pcm_routing_hwdep_free(struct snd_pcm *pcm); +#else +static inline int msm_pcm_routing_hwdep_new(struct snd_soc_pcm_runtime *runtime, + struct msm_pcm_routing_bdai_data *msm_bedais) +{ + return 0; +} + +static inline void msm_pcm_routing_hwdep_free(struct snd_pcm *pcm) +{ +} +#endif +#endif diff --git a/audio-kernel/asoc/msm-pcm-routing-v2.c b/audio-kernel/asoc/msm-pcm-routing-v2.c new file mode 100644 index 000000000..bab0571c8 --- /dev/null +++ b/audio-kernel/asoc/msm-pcm-routing-v2.c @@ -0,0 +1,23875 @@ +/* Copyright (c) 2012-2019, 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "msm-pcm-routing-v2.h" +#include "msm-pcm-routing-devdep.h" +#include "msm-qti-pp-config.h" +#include "msm-dolby-dap-config.h" +#include "msm-ds2-dap-config.h" + +#ifndef CONFIG_DOLBY_DAP +#undef DOLBY_ADM_COPP_TOPOLOGY_ID +#define DOLBY_ADM_COPP_TOPOLOGY_ID 0xFFFFFFFE +#endif + +#ifndef CONFIG_DOLBY_DS2 +#undef DS2_ADM_COPP_TOPOLOGY_ID +#define DS2_ADM_COPP_TOPOLOGY_ID 0xFFFFFFFF +#endif + +static struct mutex routing_lock; + +static struct cal_type_data *cal_data[MAX_ROUTING_CAL_TYPES]; + +static int fm_switch_enable; +static int hfp_switch_enable; +static int a2dp_switch_enable; +static int int0_mi2s_switch_enable; +static int int4_mi2s_switch_enable; +static int pri_mi2s_switch_enable; +static int sec_mi2s_switch_enable; +static int tert_mi2s_switch_enable; +static int quat_mi2s_switch_enable; +static int quin_mi2s_switch_enable; +static int fm_pcmrx_switch_enable; +static int usb_switch_enable; +static int lsm_port_index; +static int slim0_rx_aanc_fb_port; +static int msm_route_ec_ref_rx; +static int msm_ec_ref_ch = 4; +static int msm_ec_ref_bit_format = SNDRV_PCM_FORMAT_S16_LE; +static int msm_ec_ref_sampling_rate = 48000; +static uint32_t voc_session_id = ALL_SESSION_VSID; +static int msm_route_ext_ec_ref; +static bool is_custom_stereo_on; +static bool is_ds2_on; +static bool swap_ch; +static int aanc_level; + +#define WEIGHT_0_DB 0x4000 +/* all the FEs which can support channel mixer */ +static struct msm_pcm_channel_mixer channel_mixer[MSM_FRONTEND_DAI_MM_SIZE]; +/* input BE for each FE */ +static int channel_input[MSM_FRONTEND_DAI_MM_SIZE][ADM_MAX_CHANNELS]; + +enum { + MADNONE, + MADAUDIO, + MADBEACON, + MADULTRASOUND, + MADSWAUDIO, +}; + +#define ADM_LSM_PORT_INDEX 9 + +#define SLIMBUS_0_TX_TEXT "SLIMBUS_0_TX" +#define SLIMBUS_1_TX_TEXT "SLIMBUS_1_TX" +#define SLIMBUS_2_TX_TEXT "SLIMBUS_2_TX" +#define SLIMBUS_3_TX_TEXT "SLIMBUS_3_TX" +#define SLIMBUS_4_TX_TEXT "SLIMBUS_4_TX" +#define SLIMBUS_5_TX_TEXT "SLIMBUS_5_TX" +#define TERT_MI2S_TX_TEXT "TERT_MI2S_TX" +#define QUAT_MI2S_TX_TEXT "QUAT_MI2S_TX" +#define ADM_LSM_TX_TEXT "ADM_LSM_TX" +#define INT3_MI2S_TX_TEXT "INT3_MI2S_TX" +#define VA_CDC_DMA_TX_0_TEXT "VA_CDC_DMA_TX_0" +#define VA_CDC_DMA_TX_1_TEXT "VA_CDC_DMA_TX_1" +#define TX_CDC_DMA_TX_3_TEXT "TX_CDC_DMA_TX_3" +#define QUIN_TDM_TX_TEXT "QUIN_TDM_TX_0" + +#define LSM_FUNCTION_TEXT "LSM Function" +static const char * const lsm_port_text[] = { + "None", + SLIMBUS_0_TX_TEXT, SLIMBUS_1_TX_TEXT, SLIMBUS_2_TX_TEXT, + SLIMBUS_3_TX_TEXT, SLIMBUS_4_TX_TEXT, SLIMBUS_5_TX_TEXT, + TERT_MI2S_TX_TEXT, QUAT_MI2S_TX_TEXT, ADM_LSM_TX_TEXT, + INT3_MI2S_TX_TEXT, VA_CDC_DMA_TX_0_TEXT, VA_CDC_DMA_TX_1_TEXT, + TX_CDC_DMA_TX_3_TEXT, QUIN_TDM_TX_TEXT +}; + +struct msm_pcm_route_bdai_pp_params { + u16 port_id; /* AFE port ID */ + unsigned long pp_params_config; + bool mute_on; + int latency; +}; + +static struct msm_pcm_route_bdai_pp_params + msm_bedais_pp_params[MSM_BACKEND_DAI_PP_PARAMS_REQ_MAX] = { + {HDMI_RX, 0, 0, 0}, + {DISPLAY_PORT_RX, 0, 0, 0}, +}; + +/* + * The be_dai_name_table is passed to HAL so that it can specify the + * BE ID for the BE it wants to enable based on the name. Thus there + * is a matching table and structure in HAL that need to be updated + * if any changes to these are made. + */ +struct msm_pcm_route_bdai_name { + unsigned int be_id; + char be_name[LPASS_BE_NAME_MAX_LENGTH]; +}; +static struct msm_pcm_route_bdai_name be_dai_name_table[MSM_BACKEND_DAI_MAX]; + +static int msm_routing_send_device_pp_params(int port_id, int copp_idx, + int fe_id); + +static void msm_routing_load_topology(size_t data_size, void *data); +static void msm_routing_unload_topology(uint32_t topology_id); + +static int msm_routing_get_bit_width(unsigned int format) +{ + int bit_width; + + switch (format) { + case SNDRV_PCM_FORMAT_S32_LE: + bit_width = 32; + break; + case SNDRV_PCM_FORMAT_S24_LE: + case SNDRV_PCM_FORMAT_S24_3LE: + bit_width = 24; + break; + case SNDRV_PCM_FORMAT_S16_LE: + default: + bit_width = 16; + } + return bit_width; +} + +static bool msm_is_resample_needed(int input_sr, int output_sr) +{ + bool rc = false; + + if (input_sr != output_sr) + rc = true; + + pr_debug("perform resampling (%s) for copp rate (%d)afe rate (%d)", + (rc ? "oh yes" : "not really"), + input_sr, output_sr); + + return rc; +} + +static void msm_pcm_routing_cfg_pp(int port_id, int copp_idx, int topology, + int channels) +{ + int rc = 0; + + switch (topology) { + case SRS_TRUMEDIA_TOPOLOGY_ID: + pr_debug("%s: SRS_TRUMEDIA_TOPOLOGY_ID\n", __func__); + msm_dts_srs_tm_init(port_id, copp_idx); + break; + case DS2_ADM_COPP_TOPOLOGY_ID: + pr_debug("%s: DS2_ADM_COPP_TOPOLOGY %d\n", + __func__, DS2_ADM_COPP_TOPOLOGY_ID); + rc = msm_ds2_dap_init(port_id, copp_idx, channels, + is_custom_stereo_on); + if (rc < 0) + pr_err("%s: DS2 topo_id 0x%x, port %d, CS %d rc %d\n", + __func__, topology, port_id, + is_custom_stereo_on, rc); + break; + case DOLBY_ADM_COPP_TOPOLOGY_ID: + if (is_ds2_on) { + pr_debug("%s: DS2_ADM_COPP_TOPOLOGY\n", __func__); + rc = msm_ds2_dap_init(port_id, copp_idx, channels, + is_custom_stereo_on); + if (rc < 0) + pr_err("%s:DS2 topo_id 0x%x, port %d, rc %d\n", + __func__, topology, port_id, rc); + } else { + pr_debug("%s: DOLBY_ADM_COPP_TOPOLOGY_ID\n", __func__); + rc = msm_dolby_dap_init(port_id, copp_idx, channels, + is_custom_stereo_on); + if (rc < 0) + pr_err("%s: DS1 topo_id 0x%x, port %d, rc %d\n", + __func__, topology, port_id, rc); + } + break; + case ADM_CMD_COPP_OPEN_TOPOLOGY_ID_AUDIOSPHERE: + pr_debug("%s: TOPOLOGY_ID_AUDIOSPHERE\n", __func__); + rc = msm_qti_pp_asphere_init(port_id, copp_idx); + if (rc < 0) + pr_err("%s: topo_id 0x%x, port %d, copp %d, rc %d\n", + __func__, topology, port_id, copp_idx, rc); + break; + default: + /* custom topology specific feature param handlers */ + break; + } +} + +static void msm_pcm_routing_deinit_pp(int port_id, int topology) +{ + switch (topology) { + case SRS_TRUMEDIA_TOPOLOGY_ID: + pr_debug("%s: SRS_TRUMEDIA_TOPOLOGY_ID\n", __func__); + msm_dts_srs_tm_deinit(port_id); + break; + case DS2_ADM_COPP_TOPOLOGY_ID: + pr_debug("%s: DS2_ADM_COPP_TOPOLOGY_ID %d\n", + __func__, DS2_ADM_COPP_TOPOLOGY_ID); + msm_ds2_dap_deinit(port_id); + break; + case DOLBY_ADM_COPP_TOPOLOGY_ID: + if (is_ds2_on) { + pr_debug("%s: DS2_ADM_COPP_TOPOLOGY_ID\n", __func__); + msm_ds2_dap_deinit(port_id); + } else { + pr_debug("%s: DOLBY_ADM_COPP_TOPOLOGY_ID\n", __func__); + msm_dolby_dap_deinit(port_id); + } + break; + case ADM_CMD_COPP_OPEN_TOPOLOGY_ID_AUDIOSPHERE: + pr_debug("%s: TOPOLOGY_ID_AUDIOSPHERE\n", __func__); + msm_qti_pp_asphere_deinit(port_id); + break; + default: + /* custom topology specific feature deinit handlers */ + break; + } +} + +static void msm_pcm_routng_cfg_matrix_map_pp(struct route_payload payload, + int path_type, int perf_mode) +{ + int itr = 0, rc = 0; + + if ((path_type == ADM_PATH_PLAYBACK) && + (perf_mode == LEGACY_PCM_MODE) && + is_custom_stereo_on) { + for (itr = 0; itr < payload.num_copps; itr++) { + if ((payload.port_id[itr] != SLIMBUS_0_RX) && + (payload.port_id[itr] != RT_PROXY_PORT_001_RX)) { + continue; + } + + rc = msm_qti_pp_send_stereo_to_custom_stereo_cmd( + payload.port_id[itr], + payload.copp_idx[itr], + payload.session_id, + Q14_GAIN_ZERO_POINT_FIVE, + Q14_GAIN_ZERO_POINT_FIVE, + Q14_GAIN_ZERO_POINT_FIVE, + Q14_GAIN_ZERO_POINT_FIVE); + if (rc < 0) + pr_err("%s: err setting custom stereo\n", + __func__); + } + } +} + +#define SLIMBUS_EXTPROC_RX AFE_PORT_INVALID +struct msm_pcm_routing_bdai_data msm_bedais[MSM_BACKEND_DAI_MAX] = { + { PRIMARY_I2S_RX, 0, {0}, {0}, 0, 0, 0, 0, LPASS_BE_PRI_I2S_RX}, + { PRIMARY_I2S_TX, 0, {0}, {0}, 0, 0, 0, 0, LPASS_BE_PRI_I2S_TX}, + { SLIMBUS_0_RX, 0, {0}, {0}, 0, 0, 0, 0, LPASS_BE_SLIMBUS_0_RX}, + { SLIMBUS_0_TX, 0, {0}, {0}, 0, 0, 0, 0, LPASS_BE_SLIMBUS_0_TX}, + { HDMI_RX, 0, {0}, {0}, 0, 0, 0, 0, LPASS_BE_HDMI}, + { INT_BT_SCO_RX, 0, {0}, {0}, 0, 0, 0, 0, LPASS_BE_INT_BT_SCO_RX}, + { INT_BT_SCO_TX, 0, {0}, {0}, 0, 0, 0, 0, LPASS_BE_INT_BT_SCO_TX}, + { INT_FM_RX, 0, {0}, {0}, 0, 0, 0, 0, LPASS_BE_INT_FM_RX}, + { INT_FM_TX, 0, {0}, {0}, 0, 0, 0, 0, LPASS_BE_INT_FM_TX}, + { RT_PROXY_PORT_001_RX, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_AFE_PCM_RX}, + { RT_PROXY_PORT_001_TX, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_AFE_PCM_TX}, + { AFE_PORT_ID_PRIMARY_PCM_RX, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_AUXPCM_RX}, + { AFE_PORT_ID_PRIMARY_PCM_TX, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_AUXPCM_TX}, + { VOICE_PLAYBACK_TX, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_VOICE_PLAYBACK_TX}, + { VOICE2_PLAYBACK_TX, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_VOICE2_PLAYBACK_TX}, + { VOICE_RECORD_RX, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_INCALL_RECORD_RX}, + { VOICE_RECORD_TX, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_INCALL_RECORD_TX}, + { MI2S_RX, 0, {0}, {0}, 0, 0, 0, 0, LPASS_BE_MI2S_RX}, + { MI2S_TX, 0, {0}, {0}, 0, 0, 0, 0, LPASS_BE_MI2S_TX}, + { SECONDARY_I2S_RX, 0, {0}, {0}, 0, 0, 0, 0, LPASS_BE_SEC_I2S_RX}, + { SLIMBUS_1_RX, 0, {0}, {0}, 0, 0, 0, 0, LPASS_BE_SLIMBUS_1_RX}, + { SLIMBUS_1_TX, 0, {0}, {0}, 0, 0, 0, 0, LPASS_BE_SLIMBUS_1_TX}, + { SLIMBUS_2_RX, 0, {0}, {0}, 0, 0, 0, 0, LPASS_BE_SLIMBUS_2_RX}, + { SLIMBUS_2_TX, 0, {0}, {0}, 0, 0, 0, 0, LPASS_BE_SLIMBUS_2_TX}, + { SLIMBUS_3_RX, 0, {0}, {0}, 0, 0, 0, 0, LPASS_BE_SLIMBUS_3_RX}, + { SLIMBUS_3_TX, 0, {0}, {0}, 0, 0, 0, 0, LPASS_BE_SLIMBUS_3_TX}, + { SLIMBUS_4_RX, 0, {0}, {0}, 0, 0, 0, 0, LPASS_BE_SLIMBUS_4_RX}, + { SLIMBUS_4_TX, 0, {0}, {0}, 0, 0, 0, 0, LPASS_BE_SLIMBUS_4_TX}, + { SLIMBUS_5_RX, 0, {0}, {0}, 0, 0, 0, 0, LPASS_BE_SLIMBUS_5_RX}, + { SLIMBUS_5_TX, 0, {0}, {0}, 0, 0, 0, 0, LPASS_BE_SLIMBUS_5_TX}, + { SLIMBUS_6_RX, 0, {0}, {0}, 0, 0, 0, 0, LPASS_BE_SLIMBUS_6_RX}, + { SLIMBUS_6_TX, 0, {0}, {0}, 0, 0, 0, 0, LPASS_BE_SLIMBUS_6_TX}, + { SLIMBUS_7_RX, 0, {0}, {0}, 0, 0, 0, 0, LPASS_BE_SLIMBUS_7_RX}, + { SLIMBUS_7_TX, 0, {0}, {0}, 0, 0, 0, 0, LPASS_BE_SLIMBUS_7_TX}, + { SLIMBUS_8_RX, 0, {0}, {0}, 0, 0, 0, 0, LPASS_BE_SLIMBUS_8_RX}, + { SLIMBUS_8_TX, 0, {0}, {0}, 0, 0, 0, 0, LPASS_BE_SLIMBUS_8_TX}, + { SLIMBUS_EXTPROC_RX, 0, {0}, {0}, 0, 0, 0, 0, LPASS_BE_STUB_RX}, + { SLIMBUS_EXTPROC_RX, 0, {0}, {0}, 0, 0, 0, 0, LPASS_BE_STUB_TX}, + { SLIMBUS_EXTPROC_RX, 0, {0}, {0}, 0, 0, 0, 0, LPASS_BE_STUB_1_TX}, + { AFE_PORT_ID_QUATERNARY_MI2S_RX, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_QUAT_MI2S_RX}, + { AFE_PORT_ID_QUATERNARY_MI2S_TX, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_QUAT_MI2S_TX}, + { AFE_PORT_ID_SECONDARY_MI2S_RX, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_SEC_MI2S_RX}, + { AFE_PORT_ID_SECONDARY_MI2S_TX, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_SEC_MI2S_TX}, + { AFE_PORT_ID_PRIMARY_MI2S_RX, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_PRI_MI2S_RX}, + { AFE_PORT_ID_PRIMARY_MI2S_TX, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_PRI_MI2S_TX}, + { AFE_PORT_ID_TERTIARY_MI2S_RX, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_TERT_MI2S_RX}, + { AFE_PORT_ID_TERTIARY_MI2S_TX, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_TERT_MI2S_TX}, + { AUDIO_PORT_ID_I2S_RX, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_AUDIO_I2S_RX}, + { AFE_PORT_ID_SECONDARY_PCM_RX, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_SEC_AUXPCM_RX}, + { AFE_PORT_ID_SECONDARY_PCM_TX, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_SEC_AUXPCM_TX}, + { AFE_PORT_ID_PRIMARY_SPDIF_RX, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_PRI_SPDIF_RX}, + { AFE_PORT_ID_SECONDARY_MI2S_RX_SD1, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_SEC_MI2S_RX_SD1}, + { AFE_PORT_ID_QUINARY_MI2S_RX, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_QUIN_MI2S_RX}, + { AFE_PORT_ID_QUINARY_MI2S_TX, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_QUIN_MI2S_TX}, + { AFE_PORT_ID_SENARY_MI2S_TX, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_SENARY_MI2S_TX}, + { AFE_PORT_ID_PRIMARY_TDM_RX, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_PRI_TDM_RX_0}, + { AFE_PORT_ID_PRIMARY_TDM_TX, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_PRI_TDM_TX_0}, + { AFE_PORT_ID_PRIMARY_TDM_RX_1, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_PRI_TDM_RX_1}, + { AFE_PORT_ID_PRIMARY_TDM_TX_1, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_PRI_TDM_TX_1}, + { AFE_PORT_ID_PRIMARY_TDM_RX_2, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_PRI_TDM_RX_2}, + { AFE_PORT_ID_PRIMARY_TDM_TX_2, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_PRI_TDM_TX_2}, + { AFE_PORT_ID_PRIMARY_TDM_RX_3, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_PRI_TDM_RX_3}, + { AFE_PORT_ID_PRIMARY_TDM_TX_3, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_PRI_TDM_TX_3}, + { AFE_PORT_ID_PRIMARY_TDM_RX_4, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_PRI_TDM_RX_4}, + { AFE_PORT_ID_PRIMARY_TDM_TX_4, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_PRI_TDM_TX_4}, + { AFE_PORT_ID_PRIMARY_TDM_RX_5, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_PRI_TDM_RX_5}, + { AFE_PORT_ID_PRIMARY_TDM_TX_5, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_PRI_TDM_TX_5}, + { AFE_PORT_ID_PRIMARY_TDM_RX_6, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_PRI_TDM_RX_6}, + { AFE_PORT_ID_PRIMARY_TDM_TX_6, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_PRI_TDM_TX_6}, + { AFE_PORT_ID_PRIMARY_TDM_RX_7, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_PRI_TDM_RX_7}, + { AFE_PORT_ID_PRIMARY_TDM_TX_7, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_PRI_TDM_TX_7}, + { AFE_PORT_ID_SECONDARY_TDM_RX, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_SEC_TDM_RX_0}, + { AFE_PORT_ID_SECONDARY_TDM_TX, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_SEC_TDM_TX_0}, + { AFE_PORT_ID_SECONDARY_TDM_RX_1, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_SEC_TDM_RX_1}, + { AFE_PORT_ID_SECONDARY_TDM_TX_1, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_SEC_TDM_TX_1}, + { AFE_PORT_ID_SECONDARY_TDM_RX_2, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_SEC_TDM_RX_2}, + { AFE_PORT_ID_SECONDARY_TDM_TX_2, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_SEC_TDM_TX_2}, + { AFE_PORT_ID_SECONDARY_TDM_RX_3, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_SEC_TDM_RX_3}, + { AFE_PORT_ID_SECONDARY_TDM_TX_3, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_SEC_TDM_TX_3}, + { AFE_PORT_ID_SECONDARY_TDM_RX_4, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_SEC_TDM_RX_4}, + { AFE_PORT_ID_SECONDARY_TDM_TX_4, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_SEC_TDM_TX_4}, + { AFE_PORT_ID_SECONDARY_TDM_RX_5, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_SEC_TDM_RX_5}, + { AFE_PORT_ID_SECONDARY_TDM_TX_5, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_SEC_TDM_TX_5}, + { AFE_PORT_ID_SECONDARY_TDM_RX_6, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_SEC_TDM_RX_6}, + { AFE_PORT_ID_SECONDARY_TDM_TX_6, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_SEC_TDM_TX_6}, + { AFE_PORT_ID_SECONDARY_TDM_RX_7, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_SEC_TDM_RX_7}, + { AFE_PORT_ID_SECONDARY_TDM_TX_7, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_SEC_TDM_TX_7}, + { AFE_PORT_ID_TERTIARY_TDM_RX, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_TERT_TDM_RX_0}, + { AFE_PORT_ID_TERTIARY_TDM_TX, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_TERT_TDM_TX_0}, + { AFE_PORT_ID_TERTIARY_TDM_RX_1, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_TERT_TDM_RX_1}, + { AFE_PORT_ID_TERTIARY_TDM_TX_1, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_TERT_TDM_TX_1}, + { AFE_PORT_ID_TERTIARY_TDM_RX_2, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_TERT_TDM_RX_2}, + { AFE_PORT_ID_TERTIARY_TDM_TX_2, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_TERT_TDM_TX_2}, + { AFE_PORT_ID_TERTIARY_TDM_RX_3, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_TERT_TDM_RX_3}, + { AFE_PORT_ID_TERTIARY_TDM_TX_3, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_TERT_TDM_TX_3}, + { AFE_PORT_ID_TERTIARY_TDM_RX_4, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_TERT_TDM_RX_4}, + { AFE_PORT_ID_TERTIARY_TDM_TX_4, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_TERT_TDM_TX_4}, + { AFE_PORT_ID_TERTIARY_TDM_RX_5, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_TERT_TDM_RX_5}, + { AFE_PORT_ID_TERTIARY_TDM_TX_5, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_TERT_TDM_TX_5}, + { AFE_PORT_ID_TERTIARY_TDM_RX_6, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_TERT_TDM_RX_6}, + { AFE_PORT_ID_TERTIARY_TDM_TX_6, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_TERT_TDM_TX_6}, + { AFE_PORT_ID_TERTIARY_TDM_RX_7, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_TERT_TDM_RX_7}, + { AFE_PORT_ID_TERTIARY_TDM_TX_7, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_TERT_TDM_TX_7}, + { AFE_PORT_ID_QUATERNARY_TDM_RX, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_QUAT_TDM_RX_0}, + { AFE_PORT_ID_QUATERNARY_TDM_TX, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_QUAT_TDM_TX_0}, + { AFE_PORT_ID_QUATERNARY_TDM_RX_1, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_QUAT_TDM_RX_1}, + { AFE_PORT_ID_QUATERNARY_TDM_TX_1, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_QUAT_TDM_TX_1}, + { AFE_PORT_ID_QUATERNARY_TDM_RX_2, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_QUAT_TDM_RX_2}, + { AFE_PORT_ID_QUATERNARY_TDM_TX_2, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_QUAT_TDM_TX_2}, + { AFE_PORT_ID_QUATERNARY_TDM_RX_3, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_QUAT_TDM_RX_3}, + { AFE_PORT_ID_QUATERNARY_TDM_TX_3, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_QUAT_TDM_TX_3}, + { AFE_PORT_ID_QUATERNARY_TDM_RX_4, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_QUAT_TDM_RX_4}, + { AFE_PORT_ID_QUATERNARY_TDM_TX_4, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_QUAT_TDM_TX_4}, + { AFE_PORT_ID_QUATERNARY_TDM_RX_5, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_QUAT_TDM_RX_5}, + { AFE_PORT_ID_QUATERNARY_TDM_TX_5, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_QUAT_TDM_TX_5}, + { AFE_PORT_ID_QUATERNARY_TDM_RX_6, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_QUAT_TDM_RX_6}, + { AFE_PORT_ID_QUATERNARY_TDM_TX_6, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_QUAT_TDM_TX_6}, + { AFE_PORT_ID_QUATERNARY_TDM_RX_7, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_QUAT_TDM_RX_7}, + { AFE_PORT_ID_QUATERNARY_TDM_TX_7, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_QUAT_TDM_TX_7}, + { AFE_PORT_ID_QUINARY_TDM_RX, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_QUIN_TDM_RX_0}, + { AFE_PORT_ID_QUINARY_TDM_TX, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_QUIN_TDM_TX_0}, + { AFE_PORT_ID_QUINARY_TDM_RX_1, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_QUIN_TDM_RX_1}, + { AFE_PORT_ID_QUINARY_TDM_TX_1, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_QUIN_TDM_TX_1}, + { AFE_PORT_ID_QUINARY_TDM_RX_2, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_QUIN_TDM_RX_2}, + { AFE_PORT_ID_QUINARY_TDM_TX_2, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_QUIN_TDM_TX_2}, + { AFE_PORT_ID_QUINARY_TDM_RX_3, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_QUIN_TDM_RX_3}, + { AFE_PORT_ID_QUINARY_TDM_TX_3, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_QUIN_TDM_TX_3}, + { AFE_PORT_ID_QUINARY_TDM_RX_4, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_QUIN_TDM_RX_4}, + { AFE_PORT_ID_QUINARY_TDM_TX_4, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_QUIN_TDM_TX_4}, + { AFE_PORT_ID_QUINARY_TDM_RX_5, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_QUIN_TDM_RX_5}, + { AFE_PORT_ID_QUINARY_TDM_TX_5, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_QUIN_TDM_TX_5}, + { AFE_PORT_ID_QUINARY_TDM_RX_6, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_QUIN_TDM_RX_6}, + { AFE_PORT_ID_QUINARY_TDM_TX_6, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_QUIN_TDM_TX_6}, + { AFE_PORT_ID_QUINARY_TDM_RX_7, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_QUIN_TDM_RX_7}, + { AFE_PORT_ID_QUINARY_TDM_TX_7, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_QUIN_TDM_TX_7}, + { INT_BT_A2DP_RX, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_INT_BT_A2DP_RX}, + { AFE_PORT_ID_USB_RX, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_USB_AUDIO_RX}, + { AFE_PORT_ID_USB_TX, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_USB_AUDIO_TX}, + { DISPLAY_PORT_RX, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_DISPLAY_PORT}, + { DISPLAY_PORT_RX, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_DISPLAY_PORT1}, + { AFE_PORT_ID_TERTIARY_PCM_RX, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_TERT_AUXPCM_RX}, + { AFE_PORT_ID_TERTIARY_PCM_TX, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_TERT_AUXPCM_TX}, + { AFE_PORT_ID_QUATERNARY_PCM_RX, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_QUAT_AUXPCM_RX}, + { AFE_PORT_ID_QUATERNARY_PCM_TX, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_QUAT_AUXPCM_TX}, + { AFE_PORT_ID_QUINARY_PCM_RX, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_QUIN_AUXPCM_RX}, + { AFE_PORT_ID_QUINARY_PCM_TX, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_QUIN_AUXPCM_TX}, + { AFE_PORT_ID_INT0_MI2S_RX, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_INT0_MI2S_RX}, + { AFE_PORT_ID_INT0_MI2S_TX, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_INT0_MI2S_TX}, + { AFE_PORT_ID_INT1_MI2S_RX, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_INT1_MI2S_RX}, + { AFE_PORT_ID_INT1_MI2S_TX, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_INT1_MI2S_TX}, + { AFE_PORT_ID_INT2_MI2S_RX, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_INT2_MI2S_RX}, + { AFE_PORT_ID_INT2_MI2S_TX, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_INT2_MI2S_TX}, + { AFE_PORT_ID_INT3_MI2S_RX, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_INT3_MI2S_RX}, + { AFE_PORT_ID_INT3_MI2S_TX, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_INT3_MI2S_TX}, + { AFE_PORT_ID_INT4_MI2S_RX, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_INT4_MI2S_RX}, + { AFE_PORT_ID_INT4_MI2S_TX, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_INT4_MI2S_TX}, + { AFE_PORT_ID_INT5_MI2S_RX, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_INT5_MI2S_RX}, + { AFE_PORT_ID_INT5_MI2S_TX, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_INT5_MI2S_TX}, + { AFE_PORT_ID_INT6_MI2S_RX, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_INT6_MI2S_RX}, + { AFE_PORT_ID_INT6_MI2S_TX, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_INT6_MI2S_TX}, + { AFE_PORT_ID_SENARY_PCM_RX, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_SEN_AUXPCM_RX}, + { AFE_PORT_ID_SENARY_PCM_TX, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_SEN_AUXPCM_TX}, + { AFE_PORT_ID_SENARY_MI2S_RX, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_SENARY_MI2S_RX}, + { AFE_PORT_ID_WSA_CODEC_DMA_RX_0, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_WSA_CDC_DMA_RX_0}, + { AFE_PORT_ID_WSA_CODEC_DMA_TX_0, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_WSA_CDC_DMA_TX_0}, + { AFE_PORT_ID_WSA_CODEC_DMA_RX_1, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_WSA_CDC_DMA_RX_1}, + { AFE_PORT_ID_WSA_CODEC_DMA_TX_1, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_WSA_CDC_DMA_TX_1}, + { AFE_PORT_ID_WSA_CODEC_DMA_TX_2, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_WSA_CDC_DMA_TX_2}, + { AFE_PORT_ID_VA_CODEC_DMA_TX_0, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_VA_CDC_DMA_TX_0}, + { AFE_PORT_ID_VA_CODEC_DMA_TX_1, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_VA_CDC_DMA_TX_1}, + { AFE_PORT_ID_RX_CODEC_DMA_RX_0, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_RX_CDC_DMA_RX_0}, + { AFE_PORT_ID_TX_CODEC_DMA_TX_0, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_TX_CDC_DMA_TX_0}, + { AFE_PORT_ID_RX_CODEC_DMA_RX_1, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_RX_CDC_DMA_RX_1}, + { AFE_PORT_ID_TX_CODEC_DMA_TX_1, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_TX_CDC_DMA_TX_1}, + { AFE_PORT_ID_RX_CODEC_DMA_RX_2, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_RX_CDC_DMA_RX_2}, + { AFE_PORT_ID_TX_CODEC_DMA_TX_2, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_TX_CDC_DMA_TX_2}, + { AFE_PORT_ID_RX_CODEC_DMA_RX_3, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_RX_CDC_DMA_RX_3}, + { AFE_PORT_ID_TX_CODEC_DMA_TX_3, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_TX_CDC_DMA_TX_3}, + { AFE_PORT_ID_RX_CODEC_DMA_RX_4, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_RX_CDC_DMA_RX_4}, + { AFE_PORT_ID_TX_CODEC_DMA_TX_4, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_TX_CDC_DMA_TX_4}, + { AFE_PORT_ID_RX_CODEC_DMA_RX_5, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_RX_CDC_DMA_RX_5}, + { AFE_PORT_ID_TX_CODEC_DMA_TX_5, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_TX_CDC_DMA_TX_5}, + { AFE_PORT_ID_RX_CODEC_DMA_RX_6, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_RX_CDC_DMA_RX_6}, + { AFE_PORT_ID_RX_CODEC_DMA_RX_7, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_RX_CDC_DMA_RX_7}, + { AFE_PORT_ID_PRIMARY_SPDIF_TX, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_PRI_SPDIF_TX}, + { AFE_PORT_ID_SECONDARY_SPDIF_RX, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_SEC_SPDIF_RX}, + { AFE_PORT_ID_SECONDARY_SPDIF_TX, 0, {0}, {0}, 0, 0, 0, 0, + LPASS_BE_SEC_SPDIF_TX}, + { SLIMBUS_9_RX, 0, {0}, {0}, 0, 0, 0, 0, LPASS_BE_SLIMBUS_9_RX}, + { SLIMBUS_9_TX, 0, {0}, {0}, 0, 0, 0, 0, LPASS_BE_SLIMBUS_9_TX}, +}; + +/* Track ASM playback & capture sessions of DAI + * Track LSM listen sessions + */ +static struct msm_pcm_routing_fdai_data + fe_dai_map[MSM_FRONTEND_DAI_MAX][2] = { + /* MULTIMEDIA1 */ + {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL}, LEGACY_PCM}, + {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL}, LEGACY_PCM} }, + /* MULTIMEDIA2 */ + {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL}, LEGACY_PCM}, + {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL}, LEGACY_PCM} }, + /* MULTIMEDIA3 */ + {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL}, LEGACY_PCM}, + {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL}, LEGACY_PCM} }, + /* MULTIMEDIA4 */ + {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL}, LEGACY_PCM}, + {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL}, LEGACY_PCM} }, + /* MULTIMEDIA5 */ + {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL}, LEGACY_PCM}, + {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL}, LEGACY_PCM} }, + /* MULTIMEDIA6 */ + {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL}, LEGACY_PCM}, + {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL}, LEGACY_PCM} }, + /* MULTIMEDIA7*/ + {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL}, LEGACY_PCM}, + {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL}, LEGACY_PCM} }, + /* MULTIMEDIA8 */ + {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL}, LEGACY_PCM}, + {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL}, LEGACY_PCM} }, + /* MULTIMEDIA9 */ + {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL}, LEGACY_PCM}, + {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL}, LEGACY_PCM} }, + /* MULTIMEDIA10 */ + {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL}, LEGACY_PCM}, + {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL}, LEGACY_PCM} }, + /* MULTIMEDIA11 */ + {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL}, LEGACY_PCM}, + {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL}, LEGACY_PCM} }, + /* MULTIMEDIA12 */ + {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL}, LEGACY_PCM}, + {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL}, LEGACY_PCM} }, + /* MULTIMEDIA13 */ + {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL}, LEGACY_PCM}, + {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL}, LEGACY_PCM} }, + /* MULTIMEDIA14 */ + {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL}, LEGACY_PCM}, + {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL}, LEGACY_PCM} }, + /* MULTIMEDIA15 */ + {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL}, LEGACY_PCM}, + {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL}, LEGACY_PCM} }, + /* MULTIMEDIA16 */ + {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL}, LEGACY_PCM}, + {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL}, LEGACY_PCM} }, + /* MULTIMEDIA17 */ + {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL}, LEGACY_PCM}, + {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL}, LEGACY_PCM} }, + /* MULTIMEDIA18 */ + {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL}, LEGACY_PCM}, + {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL}, LEGACY_PCM} }, + /* MULTIMEDIA19 */ + {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL}, LEGACY_PCM}, + {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL}, LEGACY_PCM} }, + /* MULTIMEDIA20 */ + {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL}, LEGACY_PCM}, + {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL}, LEGACY_PCM} }, + /* MULTIMEDIA21 */ + {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL}, LEGACY_PCM}, + {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL}, LEGACY_PCM} }, + /* MULTIMEDIA26 */ + {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL}, LEGACY_PCM}, + {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL}, LEGACY_PCM} }, + /* MULTIMEDIA27 */ + {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL}, LEGACY_PCM}, + {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL}, LEGACY_PCM} }, + /* MULTIMEDIA28 */ + {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL}, LEGACY_PCM}, + {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL}, LEGACY_PCM} }, + /* MULTIMEDIA29 */ + {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL}, LEGACY_PCM}, + {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL}, LEGACY_PCM} }, + /* VOIP */ + {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL}, LEGACY_PCM}, + {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL}, LEGACY_PCM} }, + /* AFE_RX */ + {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL}, LEGACY_PCM}, + {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL}, LEGACY_PCM} }, + /* AFE_TX */ + {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL}, LEGACY_PCM}, + {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL}, LEGACY_PCM} }, + /* VOICE_STUB */ + {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL}, LEGACY_PCM}, + {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL}, LEGACY_PCM} }, + /* DTMF_RX */ + {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL}, LEGACY_PCM}, + {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL}, LEGACY_PCM} }, + /* QCHAT */ + {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL}, LEGACY_PCM}, + {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL}, LEGACY_PCM} }, + /* VOLTE_STUB */ + {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL}, LEGACY_PCM}, + {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL}, LEGACY_PCM} }, + /* LSM1 */ + {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL}, LEGACY_PCM}, + {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL}, LEGACY_PCM} }, + /* LSM2 */ + {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL}, LEGACY_PCM}, + {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL}, LEGACY_PCM} }, + /* LSM3 */ + {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL}, LEGACY_PCM}, + {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL}, LEGACY_PCM} }, + /* LSM4 */ + {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL}, LEGACY_PCM}, + {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL}, LEGACY_PCM} }, + /* LSM5 */ + {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL}, LEGACY_PCM}, + {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL}, LEGACY_PCM} }, + /* LSM6 */ + {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL}, LEGACY_PCM}, + {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL}, LEGACY_PCM} }, + /* LSM7 */ + {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL}, LEGACY_PCM}, + {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL}, LEGACY_PCM} }, + /* LSM8 */ + {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL}, LEGACY_PCM}, + {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL}, LEGACY_PCM} }, + /* VOICE2_STUB */ + {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL}, LEGACY_PCM}, + {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL}, LEGACY_PCM} }, + /* VOICEMMODE1 */ + {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL}, LEGACY_PCM}, + {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL}, LEGACY_PCM} }, + /* VOICEMMODE2 */ + {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL}, LEGACY_PCM}, + {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL}, LEGACY_PCM} }, +}; + +static unsigned long session_copp_map[MSM_FRONTEND_DAI_MAX][2] + [MSM_BACKEND_DAI_MAX]; +static struct msm_pcm_routing_app_type_data app_type_cfg[MAX_APP_TYPES]; +static struct msm_pcm_routing_app_type_data lsm_app_type_cfg[MAX_APP_TYPES]; +static struct msm_pcm_stream_app_type_cfg + fe_dai_app_type_cfg[MSM_FRONTEND_DAI_MAX][2][MSM_BACKEND_DAI_MAX]; + +static int last_be_id_configured[MSM_FRONTEND_DAI_MAX][MAX_SESSION_TYPES]; + +/* The caller of this should acquire routing lock */ +void msm_pcm_routing_get_bedai_info(int be_idx, + struct msm_pcm_routing_bdai_data *be_dai) +{ + if (be_idx >= 0 && be_idx < MSM_BACKEND_DAI_MAX) + memcpy(be_dai, &msm_bedais[be_idx], + sizeof(struct msm_pcm_routing_bdai_data)); +} + +/* The caller of this should acquire routing lock */ +void msm_pcm_routing_get_fedai_info(int fe_idx, int sess_type, + struct msm_pcm_routing_fdai_data *fe_dai) +{ + if ((sess_type == SESSION_TYPE_TX) || (sess_type == SESSION_TYPE_RX)) + memcpy(fe_dai, &fe_dai_map[fe_idx][sess_type], + sizeof(struct msm_pcm_routing_fdai_data)); +} + +void msm_pcm_routing_acquire_lock(void) +{ + mutex_lock(&routing_lock); +} + +void msm_pcm_routing_release_lock(void) +{ + mutex_unlock(&routing_lock); +} + +static int msm_pcm_routing_get_app_type_idx(int app_type) +{ + int idx; + + pr_debug("%s: app_type: %d\n", __func__, app_type); + for (idx = 0; idx < MAX_APP_TYPES; idx++) { + if (app_type_cfg[idx].app_type == app_type) + return idx; + } + pr_info("%s: App type not available, fallback to default\n", __func__); + return 0; +} + +static int msm_pcm_routing_get_lsm_app_type_idx(int app_type) +{ + int idx; + + pr_debug("%s: app_type: %d\n", __func__, app_type); + for (idx = 0; idx < MAX_APP_TYPES; idx++) { + if (lsm_app_type_cfg[idx].app_type == app_type) + return idx; + } + pr_debug("%s: App type not available, fallback to default\n", __func__); + return 0; +} + +static bool is_mm_lsm_fe_id(int fe_id) +{ + bool rc = true; + + if (fe_id > MSM_FRONTEND_DAI_MM_MAX_ID && + ((fe_id < MSM_FRONTEND_DAI_LSM1) || + (fe_id > MSM_FRONTEND_DAI_LSM8))) { + rc = false; + } + return rc; +} + +/* + * msm_pcm_routing_send_chmix_cfg: + * send the channel mixer command to mix the input channels + * into output channels. + * + * @fe_id: front end id + * @ip_channel_cnt: input channel count + * @op_channel_cnt: output channel count + * @ch_wght_coeff: channel weight co-efficients for channel mixing + * @session_type: indicates session is of type TX or RX + * @stream_type: indicates either Audio or Listen stream type + */ +int msm_pcm_routing_send_chmix_cfg(int fe_id, int ip_channel_cnt, + int op_channel_cnt, int *ch_wght_coeff, + int session_type, int stream_type) +{ + + int rc = 0, idx = 0; + int be_index = 0, port_id; + unsigned int session_id = 0; + + pr_debug("%s:fe_id[%d] ip_ch[%d] op_ch[%d] sess_type [%d], stream_type[%d]", + __func__, fe_id, ip_channel_cnt, op_channel_cnt, session_type, + stream_type); + if (!is_mm_lsm_fe_id(fe_id)) { + /* bad ID assigned in machine driver */ + pr_err("%s: bad MM ID %d\n", __func__, fe_id); + return -EINVAL; + } + + if (ch_wght_coeff == NULL) { + pr_err("%s: Null channel weightage coefficients passed\n", + __func__); + return -EINVAL; + } + + for (be_index = 0; be_index < MSM_BACKEND_DAI_MAX; be_index++) { + port_id = msm_bedais[be_index].port_id; + if (!msm_bedais[be_index].active || + !test_bit(fe_id, &msm_bedais[be_index].fe_sessions[0])) + continue; + + session_id = fe_dai_map[fe_id][session_type].strm_id; + + for (idx = 0; idx < MAX_COPPS_PER_PORT; idx++) { + unsigned long copp = + session_copp_map[fe_id][session_type][be_index]; + if (!test_bit(idx, &copp)) + continue; + msm_qti_pp_send_chmix_cfg_cmd(port_id, idx, + session_id, ip_channel_cnt, + op_channel_cnt, ch_wght_coeff, + session_type, stream_type); + if (rc < 0) + pr_err("%s: err setting channel mix config\n", + __func__); + } + } + + return 0; +} +EXPORT_SYMBOL(msm_pcm_routing_send_chmix_cfg); + +int msm_pcm_routing_reg_stream_app_type_cfg( + int fedai_id, int session_type, int be_id, + struct msm_pcm_stream_app_type_cfg *cfg_data) +{ + int ret = 0; + + if (cfg_data == NULL) { + pr_err("%s: Received NULL pointer for cfg_data\n", __func__); + ret = -EINVAL; + goto done; + } + + pr_debug("%s: fedai_id %d, session_type %d, be_id %d, app_type %d, acdb_dev_id %d, sample_rate %d\n", + __func__, fedai_id, session_type, be_id, + cfg_data->app_type, cfg_data->acdb_dev_id, + cfg_data->sample_rate); + + if (!is_mm_lsm_fe_id(fedai_id)) { + pr_err("%s: Invalid machine driver ID %d\n", + __func__, fedai_id); + ret = -EINVAL; + goto done; + } + if (session_type != SESSION_TYPE_RX && + session_type != SESSION_TYPE_TX) { + pr_err("%s: Invalid session type %d\n", + __func__, session_type); + ret = -EINVAL; + goto done; + } + if (be_id < 0 || be_id >= MSM_BACKEND_DAI_MAX) { + pr_err("%s: Received out of bounds be_id %d\n", + __func__, be_id); + ret = -EINVAL; + goto done; + } + + fe_dai_app_type_cfg[fedai_id][session_type][be_id] = *cfg_data; + + /* + * Store the BE ID of the configuration information set as the latest so + * the get mixer control knows what to return. + */ + last_be_id_configured[fedai_id][session_type] = be_id; + +done: + return ret; +} +EXPORT_SYMBOL(msm_pcm_routing_reg_stream_app_type_cfg); + +/** + * msm_pcm_routing_get_stream_app_type_cfg + * + * Receives fedai_id, session_type, be_id, and populates app_type, + * acdb_dev_id, & sample rate. Returns 0 on success. On failure returns + * -EINVAL and does not alter passed values. + * + * fedai_id - Passed value, front end ID for which app type config is wanted + * session_type - Passed value, session type for which app type config + * is wanted + * be_id - Returned value, back end device id the app type config data is for + * cfg_data - Returned value, configuration data used by app type config + */ +int msm_pcm_routing_get_stream_app_type_cfg( + int fedai_id, int session_type, int *bedai_id, + struct msm_pcm_stream_app_type_cfg *cfg_data) +{ + int be_id; + int ret = 0; + + if (bedai_id == NULL) { + pr_err("%s: Received NULL pointer for backend ID\n", __func__); + ret = -EINVAL; + goto done; + } else if (cfg_data == NULL) { + pr_err("%s: NULL pointer sent for cfg_data\n", __func__); + ret = -EINVAL; + goto done; + } else if (!is_mm_lsm_fe_id(fedai_id)) { + pr_err("%s: Invalid FE ID %d\n", __func__, fedai_id); + ret = -EINVAL; + goto done; + } else if (session_type != SESSION_TYPE_RX && + session_type != SESSION_TYPE_TX) { + pr_err("%s: Invalid session type %d\n", __func__, session_type); + ret = -EINVAL; + goto done; + } + + be_id = last_be_id_configured[fedai_id][session_type]; + if (be_id < 0 || be_id >= MSM_BACKEND_DAI_MAX) { + pr_err("%s: Invalid BE ID %d\n", __func__, be_id); + ret = -EINVAL; + goto done; + } + + *bedai_id = be_id; + *cfg_data = fe_dai_app_type_cfg[fedai_id][session_type][be_id]; + pr_debug("%s: fedai_id %d, session_type %d, be_id %d, app_type %d, acdb_dev_id %d, sample_rate %d\n", + __func__, fedai_id, session_type, *bedai_id, + cfg_data->app_type, cfg_data->acdb_dev_id, + cfg_data->sample_rate); +done: + return ret; +} +EXPORT_SYMBOL(msm_pcm_routing_get_stream_app_type_cfg); + +static struct cal_block_data *msm_routing_find_topology_by_path(int path, + int cal_index) +{ + struct list_head *ptr, *next; + struct cal_block_data *cal_block = NULL; + pr_debug("%s\n", __func__); + + list_for_each_safe(ptr, next, + &cal_data[cal_index]->cal_blocks) { + + cal_block = list_entry(ptr, + struct cal_block_data, list); + + if (cal_utils_is_cal_stale(cal_block)) + continue; + + if (((struct audio_cal_info_adm_top *)cal_block + ->cal_info)->path == path) { + return cal_block; + } + } + pr_debug("%s: Can't find topology for path %d\n", __func__, path); + return NULL; +} + +static struct cal_block_data *msm_routing_find_topology(int path, + int app_type, + int acdb_id, + int cal_index) +{ + struct list_head *ptr, *next; + struct cal_block_data *cal_block = NULL; + struct audio_cal_info_adm_top *cal_info; + + pr_debug("%s\n", __func__); + + list_for_each_safe(ptr, next, + &cal_data[cal_index]->cal_blocks) { + + cal_block = list_entry(ptr, + struct cal_block_data, list); + + if (cal_utils_is_cal_stale(cal_block)) + continue; + + cal_info = (struct audio_cal_info_adm_top *) + cal_block->cal_info; + if ((cal_info->path == path) && + (cal_info->app_type == app_type) && + (cal_info->acdb_id == acdb_id)) { + return cal_block; + } + } + pr_debug("%s: Can't find topology for path %d, app %d, acdb_id %d defaulting to search by path\n", + __func__, path, app_type, acdb_id); + return msm_routing_find_topology_by_path(path, cal_index); +} + +/* + * Retrieving cal_block will mark cal_block as stale. + * Hence it cannot be reused or resent unless the flag + * is reset. + */ +static int msm_routing_get_adm_topology(int fedai_id, int session_type, + int be_id) +{ + int topology = NULL_COPP_TOPOLOGY; + struct cal_block_data *cal_block = NULL; + int app_type = 0, acdb_dev_id = 0; + + pr_debug("%s: fedai_id %d, session_type %d, be_id %d\n", + __func__, fedai_id, session_type, be_id); + + if (cal_data == NULL) + goto done; + + app_type = fe_dai_app_type_cfg[fedai_id][session_type][be_id].app_type; + acdb_dev_id = + fe_dai_app_type_cfg[fedai_id][session_type][be_id].acdb_dev_id; + + mutex_lock(&cal_data[ADM_TOPOLOGY_CAL_TYPE_IDX]->lock); + cal_block = msm_routing_find_topology(session_type, app_type, + acdb_dev_id, + ADM_TOPOLOGY_CAL_TYPE_IDX); + if (cal_block != NULL) { + topology = ((struct audio_cal_info_adm_top *) + cal_block->cal_info)->topology; + cal_utils_mark_cal_used(cal_block); + mutex_unlock(&cal_data[ADM_TOPOLOGY_CAL_TYPE_IDX]->lock); + } else { + mutex_unlock(&cal_data[ADM_TOPOLOGY_CAL_TYPE_IDX]->lock); + + pr_debug("%s: Check for LSM topology\n", __func__); + mutex_lock(&cal_data[ADM_LSM_TOPOLOGY_CAL_TYPE_IDX]->lock); + cal_block = msm_routing_find_topology(session_type, app_type, + acdb_dev_id, + ADM_LSM_TOPOLOGY_CAL_TYPE_IDX); + if (cal_block != NULL) { + topology = ((struct audio_cal_info_adm_top *) + cal_block->cal_info)->topology; + cal_utils_mark_cal_used(cal_block); + } + mutex_unlock(&cal_data[ADM_LSM_TOPOLOGY_CAL_TYPE_IDX]->lock); + } + +done: + pr_debug("%s: Using topology %d\n", __func__, topology); + return topology; +} + +static uint8_t is_be_dai_extproc(int be_dai) +{ + if (be_dai == MSM_BACKEND_DAI_EXTPROC_RX || + be_dai == MSM_BACKEND_DAI_EXTPROC_TX || + be_dai == MSM_BACKEND_DAI_EXTPROC_EC_TX) + return 1; + else + return 0; +} + +static void msm_pcm_routing_build_matrix(int fedai_id, int sess_type, + int path_type, int perf_mode, + uint32_t passthr_mode) +{ + int i, port_type, j, num_copps = 0; + struct route_payload payload; + + port_type = ((path_type == ADM_PATH_PLAYBACK || + path_type == ADM_PATH_COMPRESSED_RX) ? + MSM_AFE_PORT_TYPE_RX : MSM_AFE_PORT_TYPE_TX); + + for (i = 0; i < MSM_BACKEND_DAI_MAX; i++) { + if (!is_be_dai_extproc(i) && + (afe_get_port_type(msm_bedais[i].port_id) == port_type) && + (msm_bedais[i].active) && + (test_bit(fedai_id, &msm_bedais[i].fe_sessions[0]))) { + for (j = 0; j < MAX_COPPS_PER_PORT; j++) { + unsigned long copp = + session_copp_map[fedai_id][sess_type][i]; + if (test_bit(j, &copp)) { + payload.port_id[num_copps] = + msm_bedais[i].port_id; + payload.copp_idx[num_copps] = j; + payload.app_type[num_copps] = + fe_dai_app_type_cfg + [fedai_id][sess_type][i] + .app_type; + payload.acdb_dev_id[num_copps] = + fe_dai_app_type_cfg + [fedai_id][sess_type][i] + .acdb_dev_id; + payload.sample_rate[num_copps] = + fe_dai_app_type_cfg + [fedai_id][sess_type][i] + .sample_rate; + num_copps++; + } + } + } + } + + if (num_copps) { + payload.num_copps = num_copps; + payload.session_id = fe_dai_map[fedai_id][sess_type].strm_id; + adm_matrix_map(path_type, payload, perf_mode, passthr_mode); + msm_pcm_routng_cfg_matrix_map_pp(payload, path_type, perf_mode); + } +} + +void msm_pcm_routing_reg_psthr_stream(int fedai_id, int dspst_id, + int stream_type) +{ + int i, session_type, path_type, port_type; + u32 mode = 0; + + if (fedai_id > MSM_FRONTEND_DAI_MM_MAX_ID) { + /* bad ID assigned in machine driver */ + pr_err("%s: bad MM ID\n", __func__); + return; + } + + if (stream_type == SNDRV_PCM_STREAM_PLAYBACK) { + session_type = SESSION_TYPE_RX; + path_type = ADM_PATH_PLAYBACK; + port_type = MSM_AFE_PORT_TYPE_RX; + } else { + session_type = SESSION_TYPE_TX; + path_type = ADM_PATH_LIVE_REC; + port_type = MSM_AFE_PORT_TYPE_TX; + } + + mutex_lock(&routing_lock); + + fe_dai_map[fedai_id][session_type].strm_id = dspst_id; + for (i = 0; i < MSM_BACKEND_DAI_MAX; i++) { + if (!is_be_dai_extproc(i) && + (afe_get_port_type(msm_bedais[i].port_id) == port_type) && + (msm_bedais[i].active) && + (test_bit(fedai_id, &msm_bedais[i].fe_sessions[0]))) { + mode = afe_get_port_type(msm_bedais[i].port_id); + adm_connect_afe_port(mode, dspst_id, + msm_bedais[i].port_id); + break; + } + } + mutex_unlock(&routing_lock); +} + +static bool route_check_fe_id_adm_support(int fe_id) +{ + bool rc = true; + + if ((fe_id >= MSM_FRONTEND_DAI_LSM1) && + (fe_id <= MSM_FRONTEND_DAI_LSM8)) { + /* fe id is listen while port is set to afe */ + if (lsm_port_index != ADM_LSM_PORT_INDEX) { + pr_debug("%s: fe_id %d, lsm mux slim port %d\n", + __func__, fe_id, lsm_port_index); + rc = false; + } + } + + return rc; +} + +/* + * msm_pcm_routing_get_pp_ch_cnt: + * Read the processed channel count + * + * @fe_id: Front end ID + * @session_type: Inidicates RX or TX session type + */ +int msm_pcm_routing_get_pp_ch_cnt(int fe_id, int session_type) +{ + struct msm_pcm_stream_app_type_cfg cfg_data; + int be_id = 0, app_type_idx = 0, app_type = 0; + int ret = -EINVAL; + + memset(&cfg_data, 0, sizeof(cfg_data)); + + if (!is_mm_lsm_fe_id(fe_id)) { + pr_err("%s: bad MM ID\n", __func__); + return -EINVAL; + } + + ret = msm_pcm_routing_get_stream_app_type_cfg(fe_id, session_type, + &be_id, &cfg_data); + if (ret) { + pr_err("%s: cannot get stream app type cfg\n", __func__); + return ret; + } + + app_type = cfg_data.app_type; + app_type_idx = msm_pcm_routing_get_lsm_app_type_idx(app_type); + return lsm_app_type_cfg[app_type_idx].num_out_channels; +} +EXPORT_SYMBOL(msm_pcm_routing_get_pp_ch_cnt); + +int msm_pcm_routing_reg_phy_compr_stream(int fe_id, int perf_mode, + int dspst_id, int stream_type, + uint32_t passthr_mode) +{ + int i, j, session_type, path_type, port_type, topology; + int num_copps = 0; + struct route_payload payload; + u32 channels, sample_rate; + u16 bit_width = 16; + bool is_lsm; + + pr_debug("%s:fe_id[%d] perf_mode[%d] id[%d] stream_type[%d] passt[%d]", + __func__, fe_id, perf_mode, dspst_id, + stream_type, passthr_mode); + if (!is_mm_lsm_fe_id(fe_id)) { + /* bad ID assigned in machine driver */ + pr_err("%s: bad MM ID %d\n", __func__, fe_id); + return -EINVAL; + } + + if (stream_type == SNDRV_PCM_STREAM_PLAYBACK) { + session_type = SESSION_TYPE_RX; + if (passthr_mode != LEGACY_PCM) + path_type = ADM_PATH_COMPRESSED_RX; + else + path_type = ADM_PATH_PLAYBACK; + port_type = MSM_AFE_PORT_TYPE_RX; + } else if (stream_type == SNDRV_PCM_STREAM_CAPTURE) { + session_type = SESSION_TYPE_TX; + if ((passthr_mode != LEGACY_PCM) && (passthr_mode != LISTEN)) + path_type = ADM_PATH_COMPRESSED_TX; + else + path_type = ADM_PATH_LIVE_REC; + port_type = MSM_AFE_PORT_TYPE_TX; + } else { + pr_err("%s: invalid stream type %d\n", __func__, stream_type); + return -EINVAL; + } + + is_lsm = (fe_id >= MSM_FRONTEND_DAI_LSM1) && + (fe_id <= MSM_FRONTEND_DAI_LSM8); + mutex_lock(&routing_lock); + + fe_dai_map[fe_id][session_type].strm_id = dspst_id; + fe_dai_map[fe_id][session_type].perf_mode = perf_mode; + fe_dai_map[fe_id][session_type].passthr_mode = passthr_mode; + if (!route_check_fe_id_adm_support(fe_id)) { + /* ignore adm open if not supported for fe_id */ + pr_debug("%s: No ADM support for fe id %d\n", __func__, fe_id); + mutex_unlock(&routing_lock); + return 0; + } + + payload.num_copps = 0; /* only RX needs to use payload */ + /* re-enable EQ if active */ + msm_qti_pp_send_eq_values(fe_id); + for (i = 0; i < MSM_BACKEND_DAI_MAX; i++) { + if (!is_be_dai_extproc(i) && + (afe_get_port_type(msm_bedais[i].port_id) == + port_type) && + (msm_bedais[i].active) && + (test_bit(fe_id, &msm_bedais[i].fe_sessions[0]))) { + int app_type, app_type_idx, copp_idx, acdb_dev_id; + + /* + * check if ADM needs to be configured with different + * channel mapping than backend + */ + if (!msm_bedais[i].adm_override_ch) + channels = msm_bedais[i].channel; + else + channels = msm_bedais[i].adm_override_ch; + + bit_width = msm_routing_get_bit_width( + msm_bedais[i].format); + app_type = + fe_dai_app_type_cfg[fe_id][session_type][i].app_type; + if (app_type && is_lsm) { + app_type_idx = + msm_pcm_routing_get_lsm_app_type_idx(app_type); + sample_rate = + fe_dai_app_type_cfg[fe_id][session_type][i] + .sample_rate; + bit_width = + lsm_app_type_cfg[app_type_idx].bit_width; + } else if (app_type) { + app_type_idx = + msm_pcm_routing_get_app_type_idx( + app_type); + sample_rate = + fe_dai_app_type_cfg[fe_id][session_type][i].sample_rate; + bit_width = + app_type_cfg[app_type_idx].bit_width; + } else { + sample_rate = msm_bedais[i].sample_rate; + } + acdb_dev_id = + fe_dai_app_type_cfg[fe_id][session_type][i].acdb_dev_id; + topology = msm_routing_get_adm_topology(fe_id, + session_type, + i); + if ((passthr_mode == COMPRESSED_PASSTHROUGH_DSD) + || (passthr_mode == + COMPRESSED_PASSTHROUGH_GEN) + || (passthr_mode == + COMPRESSED_PASSTHROUGH_IEC61937)) + topology = COMPRESSED_PASSTHROUGH_NONE_TOPOLOGY; + pr_debug("%s: Before adm open topology %d\n", __func__, + topology); + + copp_idx = + adm_open(msm_bedais[i].port_id, + path_type, sample_rate, channels, + topology, perf_mode, bit_width, + app_type, acdb_dev_id); + if ((copp_idx < 0) || + (copp_idx >= MAX_COPPS_PER_PORT)) { + pr_err("%s:adm open failed coppid:%d\n", + __func__, copp_idx); + mutex_unlock(&routing_lock); + return -EINVAL; + } + pr_debug("%s: set idx bit of fe:%d, type: %d, be:%d\n", + __func__, fe_id, session_type, i); + set_bit(copp_idx, + &session_copp_map[fe_id][session_type][i]); + + if (msm_is_resample_needed( + sample_rate, + msm_bedais[i].sample_rate)) + adm_copp_mfc_cfg( + msm_bedais[i].port_id, copp_idx, + msm_bedais[i].sample_rate); + + for (j = 0; j < MAX_COPPS_PER_PORT; j++) { + unsigned long copp = + session_copp_map[fe_id][session_type][i]; + if (test_bit(j, &copp)) { + payload.port_id[num_copps] = + msm_bedais[i].port_id; + payload.copp_idx[num_copps] = j; + payload.app_type[num_copps] = + fe_dai_app_type_cfg + [fe_id][session_type][i] + .app_type; + payload.acdb_dev_id[num_copps] = + fe_dai_app_type_cfg + [fe_id][session_type][i] + .acdb_dev_id; + payload.sample_rate[num_copps] = + fe_dai_app_type_cfg + [fe_id][session_type][i] + .sample_rate; + num_copps++; + } + } + if (passthr_mode != COMPRESSED_PASSTHROUGH_DSD + && passthr_mode != COMPRESSED_PASSTHROUGH_GEN + && passthr_mode != COMPRESSED_PASSTHROUGH_IEC61937) + msm_routing_send_device_pp_params( + msm_bedais[i].port_id, + copp_idx, fe_id); + } + } + if (num_copps) { + payload.num_copps = num_copps; + payload.session_id = fe_dai_map[fe_id][session_type].strm_id; + adm_matrix_map(path_type, payload, perf_mode, passthr_mode); + msm_pcm_routng_cfg_matrix_map_pp(payload, path_type, perf_mode); + } + mutex_unlock(&routing_lock); + return 0; +} + +static u32 msm_pcm_routing_get_voc_sessionid(u16 val) +{ + u32 session_id; + + switch (val) { + case MSM_FRONTEND_DAI_QCHAT: + session_id = voc_get_session_id(QCHAT_SESSION_NAME); + break; + case MSM_FRONTEND_DAI_VOIP: + session_id = voc_get_session_id(VOIP_SESSION_NAME); + break; + case MSM_FRONTEND_DAI_VOICEMMODE1: + session_id = voc_get_session_id(VOICEMMODE1_NAME); + break; + case MSM_FRONTEND_DAI_VOICEMMODE2: + session_id = voc_get_session_id(VOICEMMODE2_NAME); + break; + default: + session_id = 0; + } + + pr_debug("%s session_id 0x%x", __func__, session_id); + return session_id; +} + +static int msm_pcm_routing_channel_mixer(int fe_id, bool perf_mode, + int dspst_id, int stream_type) +{ + int copp_idx = 0; + int sess_type = 0; + int i = 0, j = 0, be_id; + int ret = 0; + + if (fe_id >= MSM_FRONTEND_DAI_MM_SIZE) { + pr_err("%s: invalid FE %d\n", __func__, fe_id); + return 0; + } + + if (!(channel_mixer[fe_id].enable)) { + pr_debug("%s: channel mixer not enabled for FE %d\n", + __func__, fe_id); + return 0; + } + + if (stream_type == SNDRV_PCM_STREAM_PLAYBACK) + sess_type = SESSION_TYPE_RX; + else + sess_type = SESSION_TYPE_TX; + + for (i = 0; i < ADM_MAX_CHANNELS && channel_input[fe_id][i] > 0; + ++i) { + be_id = channel_input[fe_id][i] - 1; + channel_mixer[fe_id].input_channels[i] = + msm_bedais[be_id].channel; + + if ((msm_bedais[be_id].active) && + test_bit(fe_id, + &msm_bedais[be_id].fe_sessions[0])) { + unsigned long copp = + session_copp_map[fe_id][sess_type][be_id]; + for (j = 0; j < MAX_COPPS_PER_PORT; j++) { + if (test_bit(j, &copp)) { + copp_idx = j; + break; + } + } + + pr_debug("%s: fe %d, be %d, channel %d, copp %d\n", + __func__, + fe_id, be_id, msm_bedais[be_id].channel, + copp_idx); + ret = adm_programable_channel_mixer( + msm_bedais[be_id].port_id, + copp_idx, dspst_id, sess_type, + channel_mixer + fe_id, i); + } + } + + return ret; +} + +int msm_pcm_routing_reg_phy_stream(int fedai_id, int perf_mode, + int dspst_id, int stream_type) +{ + int i, j, session_type, path_type, port_type, topology, num_copps = 0; + struct route_payload payload; + u32 channels, sample_rate; + uint16_t bits_per_sample = 16; + uint32_t passthr_mode = LEGACY_PCM; + int ret = 0; + + if (fedai_id > MSM_FRONTEND_DAI_MM_MAX_ID) { + /* bad ID assigned in machine driver */ + pr_err("%s: bad MM ID %d\n", __func__, fedai_id); + return -EINVAL; + } + + if (stream_type == SNDRV_PCM_STREAM_PLAYBACK) { + session_type = SESSION_TYPE_RX; + path_type = ADM_PATH_PLAYBACK; + port_type = MSM_AFE_PORT_TYPE_RX; + } else { + session_type = SESSION_TYPE_TX; + path_type = ADM_PATH_LIVE_REC; + port_type = MSM_AFE_PORT_TYPE_TX; + } + + mutex_lock(&routing_lock); + + payload.num_copps = 0; /* only RX needs to use payload */ + fe_dai_map[fedai_id][session_type].strm_id = dspst_id; + fe_dai_map[fedai_id][session_type].perf_mode = perf_mode; + fe_dai_map[fedai_id][session_type].passthr_mode = LEGACY_PCM; + + /* re-enable EQ if active */ + msm_qti_pp_send_eq_values(fedai_id); + for (i = 0; i < MSM_BACKEND_DAI_MAX; i++) { + if (!is_be_dai_extproc(i) && + (afe_get_port_type(msm_bedais[i].port_id) == port_type) && + (msm_bedais[i].active) && + (test_bit(fedai_id, &msm_bedais[i].fe_sessions[0]))) { + int app_type, app_type_idx, copp_idx, acdb_dev_id; + /* + * check if ADM needs to be configured with different + * channel mapping than backend + */ + if (!msm_bedais[i].adm_override_ch) + channels = msm_bedais[i].channel; + else + channels = msm_bedais[i].adm_override_ch; + + bits_per_sample = msm_routing_get_bit_width( + msm_bedais[i].format); + + app_type = + fe_dai_app_type_cfg[fedai_id][session_type][i].app_type; + if (app_type) { + app_type_idx = + msm_pcm_routing_get_app_type_idx(app_type); + sample_rate = + fe_dai_app_type_cfg[fedai_id][session_type][i] + .sample_rate; + bits_per_sample = + app_type_cfg[app_type_idx].bit_width; + } else + sample_rate = msm_bedais[i].sample_rate; + + acdb_dev_id = + fe_dai_app_type_cfg[fedai_id][session_type][i] + .acdb_dev_id; + topology = msm_routing_get_adm_topology(fedai_id, + session_type, + i); + copp_idx = adm_open(msm_bedais[i].port_id, path_type, + sample_rate, channels, topology, + perf_mode, bits_per_sample, + app_type, acdb_dev_id); + if ((copp_idx < 0) || + (copp_idx >= MAX_COPPS_PER_PORT)) { + pr_err("%s: adm open failed copp_idx:%d\n", + __func__, copp_idx); + mutex_unlock(&routing_lock); + return -EINVAL; + } + pr_debug("%s: setting idx bit of fe:%d, type: %d, be:%d\n", + __func__, fedai_id, session_type, i); + set_bit(copp_idx, + &session_copp_map[fedai_id][session_type][i]); + + if (msm_is_resample_needed( + sample_rate, + msm_bedais[i].sample_rate)) + adm_copp_mfc_cfg( + msm_bedais[i].port_id, copp_idx, + msm_bedais[i].sample_rate); + + for (j = 0; j < MAX_COPPS_PER_PORT; j++) { + unsigned long copp = + session_copp_map[fedai_id][session_type][i]; + if (test_bit(j, &copp)) { + payload.port_id[num_copps] = + msm_bedais[i].port_id; + payload.copp_idx[num_copps] = j; + payload.app_type[num_copps] = + fe_dai_app_type_cfg + [fedai_id][session_type] + [i].app_type; + payload.acdb_dev_id[num_copps] = + fe_dai_app_type_cfg + [fedai_id][session_type] + [i].acdb_dev_id; + payload.sample_rate[num_copps] = + fe_dai_app_type_cfg + [fedai_id][session_type] + [i].sample_rate; + num_copps++; + } + } + if (perf_mode == LEGACY_PCM_MODE) + msm_pcm_routing_cfg_pp(msm_bedais[i].port_id, + copp_idx, topology, channels); + } + } + if (num_copps) { + payload.num_copps = num_copps; + payload.session_id = fe_dai_map[fedai_id][session_type].strm_id; + adm_matrix_map(path_type, payload, perf_mode, passthr_mode); + msm_pcm_routng_cfg_matrix_map_pp(payload, path_type, perf_mode); + } + + ret = msm_pcm_routing_channel_mixer(fedai_id, perf_mode, + dspst_id, stream_type); + mutex_unlock(&routing_lock); + return ret; +} + +int msm_pcm_routing_reg_phy_stream_v2(int fedai_id, int perf_mode, + int dspst_id, int stream_type, + struct msm_pcm_routing_evt event_info) +{ + if (msm_pcm_routing_reg_phy_stream(fedai_id, perf_mode, dspst_id, + stream_type)) { + pr_err("%s: failed to reg phy stream\n", __func__); + return -EINVAL; + } + + if (stream_type == SNDRV_PCM_STREAM_PLAYBACK) + fe_dai_map[fedai_id][SESSION_TYPE_RX].event_info = event_info; + else + fe_dai_map[fedai_id][SESSION_TYPE_TX].event_info = event_info; + return 0; +} + +void msm_pcm_routing_dereg_phy_stream(int fedai_id, int stream_type) +{ + int i, port_type, session_type, path_type, topology; + struct msm_pcm_routing_fdai_data *fdai; + + if (!is_mm_lsm_fe_id(fedai_id)) { + /* bad ID assigned in machine driver */ + pr_err("%s: bad MM ID\n", __func__); + return; + } + + if (stream_type == SNDRV_PCM_STREAM_PLAYBACK) { + port_type = MSM_AFE_PORT_TYPE_RX; + session_type = SESSION_TYPE_RX; + path_type = ADM_PATH_PLAYBACK; + } else { + port_type = MSM_AFE_PORT_TYPE_TX; + session_type = SESSION_TYPE_TX; + path_type = ADM_PATH_LIVE_REC; + } + + mutex_lock(&routing_lock); + for (i = 0; i < MSM_BACKEND_DAI_MAX; i++) { + if (!is_be_dai_extproc(i) && + (afe_get_port_type(msm_bedais[i].port_id) == port_type) && + (msm_bedais[i].active) && + (test_bit(fedai_id, &msm_bedais[i].fe_sessions[0]))) { + int idx; + unsigned long copp = + session_copp_map[fedai_id][session_type][i]; + fdai = &fe_dai_map[fedai_id][session_type]; + + for (idx = 0; idx < MAX_COPPS_PER_PORT; idx++) + if (test_bit(idx, &copp)) + break; + + if (idx >= MAX_COPPS_PER_PORT || idx < 0) { + pr_debug("%s: copp idx is invalid, exiting\n", + __func__); + continue; + } + topology = adm_get_topology_for_port_copp_idx( + msm_bedais[i].port_id, idx); + msm_routing_unload_topology(topology); + adm_close(msm_bedais[i].port_id, fdai->perf_mode, idx); + pr_debug("%s:copp:%ld,idx bit fe:%d,type:%d,be:%d\n", + __func__, copp, fedai_id, session_type, i); + clear_bit(idx, + &session_copp_map[fedai_id][session_type][i]); + if ((topology == DOLBY_ADM_COPP_TOPOLOGY_ID || + topology == DS2_ADM_COPP_TOPOLOGY_ID) && + (fdai->perf_mode == LEGACY_PCM_MODE) && + (fdai->passthr_mode == LEGACY_PCM)) + msm_pcm_routing_deinit_pp(msm_bedais[i].port_id, + topology); + } + } + + fe_dai_map[fedai_id][session_type].strm_id = INVALID_SESSION; + fe_dai_map[fedai_id][session_type].be_srate = 0; + mutex_unlock(&routing_lock); +} + +/* Check if FE/BE route is set */ +static bool msm_pcm_routing_route_is_set(u16 be_id, u16 fe_id) +{ + bool rc = false; + + if (!is_mm_lsm_fe_id(fe_id)) { + /* recheck FE ID in the mixer control defined in this file */ + pr_err("%s: bad MM ID\n", __func__); + return rc; + } + + if (test_bit(fe_id, &msm_bedais[be_id].fe_sessions[0])) + rc = true; + + return rc; +} + +static void msm_pcm_routing_process_audio(u16 reg, u16 val, int set) +{ + int session_type, path_type, topology; + u32 channels, sample_rate; + uint16_t bits_per_sample = 16; + struct msm_pcm_routing_fdai_data *fdai; + uint32_t passthr_mode; + bool is_lsm; + + pr_debug("%s: reg %x val %x set %x\n", __func__, reg, val, set); + + if (!is_mm_lsm_fe_id(val)) { + /* recheck FE ID in the mixer control defined in this file */ + pr_err("%s: bad MM ID\n", __func__); + return; + } + + if (!route_check_fe_id_adm_support(val)) { + /* ignore adm open if not supported for fe_id */ + pr_debug("%s: No ADM support for fe id %d\n", __func__, val); + return; + } + + session_type = + (afe_get_port_type(msm_bedais[reg].port_id) == MSM_AFE_PORT_TYPE_RX) ? + SESSION_TYPE_RX : SESSION_TYPE_TX; + fdai = &fe_dai_map[val][session_type]; + passthr_mode = fdai->passthr_mode; + if (session_type == SESSION_TYPE_RX) { + if (passthr_mode != LEGACY_PCM) + path_type = ADM_PATH_COMPRESSED_RX; + else + path_type = ADM_PATH_PLAYBACK; + } else { + if ((passthr_mode != LEGACY_PCM) && (passthr_mode != LISTEN)) + path_type = ADM_PATH_COMPRESSED_TX; + else + path_type = ADM_PATH_LIVE_REC; + } + is_lsm = (val >= MSM_FRONTEND_DAI_LSM1) && + (val <= MSM_FRONTEND_DAI_LSM8); + + mutex_lock(&routing_lock); + if (set) { + if (!test_bit(val, &msm_bedais[reg].fe_sessions[0]) && + ((msm_bedais[reg].port_id == VOICE_PLAYBACK_TX) || + (msm_bedais[reg].port_id == VOICE2_PLAYBACK_TX))) + voc_start_playback(set, msm_bedais[reg].port_id); + + set_bit(val, &msm_bedais[reg].fe_sessions[0]); + if (msm_bedais[reg].active && fdai->strm_id != + INVALID_SESSION) { + int app_type, app_type_idx, copp_idx, acdb_dev_id; + /* + * check if ADM needs to be configured with different + * channel mapping than backend + */ + if (!msm_bedais[reg].adm_override_ch) + channels = msm_bedais[reg].channel; + else + channels = msm_bedais[reg].adm_override_ch; + if (session_type == SESSION_TYPE_TX && + fdai->be_srate && + (fdai->be_srate != msm_bedais[reg].sample_rate)) { + pr_debug("%s: flush strm %d diff BE rates\n", + __func__, fdai->strm_id); + + if (fdai->event_info.event_func) + fdai->event_info.event_func( + MSM_PCM_RT_EVT_BUF_RECFG, + fdai->event_info.priv_data); + fdai->be_srate = 0; /* might not need it */ + } + + bits_per_sample = msm_routing_get_bit_width( + msm_bedais[reg].format); + + app_type = + fe_dai_app_type_cfg[val][session_type][reg].app_type; + if (app_type && is_lsm) { + app_type_idx = + msm_pcm_routing_get_lsm_app_type_idx(app_type); + sample_rate = + fe_dai_app_type_cfg[val][session_type][reg] + .sample_rate; + bits_per_sample = + lsm_app_type_cfg[app_type_idx].bit_width; + } else if (app_type) { + app_type_idx = + msm_pcm_routing_get_app_type_idx(app_type); + sample_rate = + fe_dai_app_type_cfg[val][session_type][reg] + .sample_rate; + bits_per_sample = + app_type_cfg[app_type_idx].bit_width; + } else + sample_rate = msm_bedais[reg].sample_rate; + + topology = msm_routing_get_adm_topology(val, + session_type, + reg); + acdb_dev_id = + fe_dai_app_type_cfg[val][session_type][reg].acdb_dev_id; + copp_idx = adm_open(msm_bedais[reg].port_id, path_type, + sample_rate, channels, topology, + fdai->perf_mode, bits_per_sample, + app_type, acdb_dev_id); + if ((copp_idx < 0) || + (copp_idx >= MAX_COPPS_PER_PORT)) { + pr_err("%s: adm open failed\n", __func__); + mutex_unlock(&routing_lock); + return; + } + pr_debug("%s: setting idx bit of fe:%d, type: %d, be:%d\n", + __func__, val, session_type, reg); + set_bit(copp_idx, + &session_copp_map[val][session_type][reg]); + + if (msm_is_resample_needed( + sample_rate, + msm_bedais[reg].sample_rate)) + adm_copp_mfc_cfg( + msm_bedais[reg].port_id, copp_idx, + msm_bedais[reg].sample_rate); + + if (session_type == SESSION_TYPE_RX && + fdai->event_info.event_func) + fdai->event_info.event_func( + MSM_PCM_RT_EVT_DEVSWITCH, + fdai->event_info.priv_data); + + msm_pcm_routing_build_matrix(val, session_type, + path_type, + fdai->perf_mode, + passthr_mode); + if ((fdai->perf_mode == LEGACY_PCM_MODE) && + (passthr_mode == LEGACY_PCM)) + msm_pcm_routing_cfg_pp(msm_bedais[reg].port_id, + copp_idx, topology, + channels); + } + } else { + if (test_bit(val, &msm_bedais[reg].fe_sessions[0]) && + ((msm_bedais[reg].port_id == VOICE_PLAYBACK_TX) || + (msm_bedais[reg].port_id == VOICE2_PLAYBACK_TX))) + voc_start_playback(set, msm_bedais[reg].port_id); + clear_bit(val, &msm_bedais[reg].fe_sessions[0]); + if (msm_bedais[reg].active && fdai->strm_id != + INVALID_SESSION) { + int idx; + int port_id; + unsigned long copp = + session_copp_map[val][session_type][reg]; + for (idx = 0; idx < MAX_COPPS_PER_PORT; idx++) + if (test_bit(idx, &copp)) + break; + + if (idx >= MAX_COPPS_PER_PORT) { + pr_debug("%s: copp idx is invalid, exiting\n", + __func__); + mutex_unlock(&routing_lock); + return; + } + port_id = msm_bedais[reg].port_id; + topology = adm_get_topology_for_port_copp_idx(port_id, + idx); + msm_routing_unload_topology(topology); + adm_close(msm_bedais[reg].port_id, fdai->perf_mode, + idx); + pr_debug("%s: copp: %ld, reset idx bit fe:%d, type: %d, be:%d topology=0x%x\n", + __func__, copp, val, session_type, reg, + topology); + clear_bit(idx, + &session_copp_map[val][session_type][reg]); + if ((topology == DOLBY_ADM_COPP_TOPOLOGY_ID || + topology == DS2_ADM_COPP_TOPOLOGY_ID) && + (fdai->perf_mode == LEGACY_PCM_MODE) && + (passthr_mode == LEGACY_PCM)) + msm_pcm_routing_deinit_pp( + msm_bedais[reg].port_id, + topology); + msm_pcm_routing_build_matrix(val, session_type, + path_type, + fdai->perf_mode, + passthr_mode); + } + } + if ((msm_bedais[reg].port_id == VOICE_RECORD_RX) + || (msm_bedais[reg].port_id == VOICE_RECORD_TX)) + voc_start_record(msm_bedais[reg].port_id, set, voc_session_id); + + mutex_unlock(&routing_lock); +} + +static int msm_routing_get_audio_mixer(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + + if (test_bit(mc->rshift, &msm_bedais[mc->shift].fe_sessions[0])) + ucontrol->value.integer.value[0] = 1; + else + ucontrol->value.integer.value[0] = 0; + + pr_debug("%s: shift %x rshift %x val %ld\n", __func__, mc->shift, mc->rshift, + ucontrol->value.integer.value[0]); + + return 0; +} + +static int msm_routing_put_audio_mixer(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct snd_soc_dapm_update *update = NULL; + + if (ucontrol->value.integer.value[0] && + msm_pcm_routing_route_is_set(mc->shift, mc->rshift) == false) { + msm_pcm_routing_process_audio(mc->shift, mc->rshift, 1); + snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, 1, + update); + } else if (!ucontrol->value.integer.value[0] && + msm_pcm_routing_route_is_set(mc->shift, mc->rshift) == true) { + msm_pcm_routing_process_audio(mc->shift, mc->rshift, 0); + snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, 0, + update); + } + + return 1; +} + +static int msm_routing_get_listen_mixer(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + + if (test_bit(mc->rshift, &msm_bedais[mc->shift].fe_sessions[0])) + ucontrol->value.integer.value[0] = 1; + else + ucontrol->value.integer.value[0] = 0; + + pr_debug("%s: shift %x rshift %x val %ld\n", __func__, mc->shift, mc->rshift, + ucontrol->value.integer.value[0]); + + return 0; +} + +static int msm_routing_put_listen_mixer(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct snd_soc_dapm_update *update = NULL; + + pr_debug("%s: shift %x rshift %x val %ld\n", __func__, mc->shift, mc->rshift, + ucontrol->value.integer.value[0]); + + if (ucontrol->value.integer.value[0]) { + if (msm_pcm_routing_route_is_set(mc->shift, mc->rshift) == false) + msm_pcm_routing_process_audio(mc->shift, mc->rshift, 1); + snd_soc_dapm_mixer_update_power(widget->dapm, + kcontrol, 1, update); + } else if (!ucontrol->value.integer.value[0]) { + if (msm_pcm_routing_route_is_set(mc->shift, mc->rshift) == true) + msm_pcm_routing_process_audio(mc->shift, mc->rshift, 0); + snd_soc_dapm_mixer_update_power(widget->dapm, + kcontrol, 0, update); + } + + return 1; +} + +static void msm_pcm_routing_process_voice(u16 reg, u16 val, int set) +{ + u32 session_id = 0; + u16 path_type; + struct media_format_info voc_be_media_format; + + pr_debug("%s: reg %x val %x set %x\n", __func__, reg, val, set); + + session_id = msm_pcm_routing_get_voc_sessionid(val); + + pr_debug("%s: FE DAI 0x%x session_id 0x%x\n", + __func__, val, session_id); + + mutex_lock(&routing_lock); + + if (set) + set_bit(val, &msm_bedais[reg].fe_sessions[0]); + else + clear_bit(val, &msm_bedais[reg].fe_sessions[0]); + + if (val == MSM_FRONTEND_DAI_DTMF_RX && + afe_get_port_type(msm_bedais[reg].port_id) == + MSM_AFE_PORT_TYPE_RX) { + pr_debug("%s(): set=%d port id=0x%x for dtmf generation\n", + __func__, set, msm_bedais[reg].port_id); + afe_set_dtmf_gen_rx_portid(msm_bedais[reg].port_id, set); + } + + if (afe_get_port_type(msm_bedais[reg].port_id) == + MSM_AFE_PORT_TYPE_RX) + path_type = RX_PATH; + else + path_type = TX_PATH; + + if (set) { + if (msm_bedais[reg].active) { + voc_set_route_flag(session_id, path_type, 1); + + memset(&voc_be_media_format, 0, + sizeof(struct media_format_info)); + + voc_be_media_format.port_id = msm_bedais[reg].port_id; + voc_be_media_format.num_channels = + msm_bedais[reg].channel; + voc_be_media_format.sample_rate = + msm_bedais[reg].sample_rate; + voc_be_media_format.bits_per_sample = + msm_bedais[reg].format; + /* Defaulting this to 1 for voice call usecases */ + voc_be_media_format.channel_mapping[0] = 1; + + voc_set_device_config(session_id, path_type, + &voc_be_media_format); + + if (voc_get_route_flag(session_id, TX_PATH) && + voc_get_route_flag(session_id, RX_PATH)) + voc_enable_device(session_id); + } else { + pr_debug("%s BE is not active\n", __func__); + } + } else { + voc_set_route_flag(session_id, path_type, 0); + voc_disable_device(session_id); + } + + mutex_unlock(&routing_lock); + +} + +static int msm_routing_get_voice_mixer(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + + mutex_lock(&routing_lock); + + if (test_bit(mc->rshift, &msm_bedais[mc->shift].fe_sessions[0])) + ucontrol->value.integer.value[0] = 1; + else + ucontrol->value.integer.value[0] = 0; + + mutex_unlock(&routing_lock); + + pr_debug("%s: shift %x rshift %x val %ld\n", __func__, mc->shift, mc->rshift, + ucontrol->value.integer.value[0]); + + return 0; +} + +static int msm_routing_put_voice_mixer(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct snd_soc_dapm_update *update = NULL; + + if (ucontrol->value.integer.value[0]) { + msm_pcm_routing_process_voice(mc->shift, mc->rshift, 1); + snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, 1, + update); + } else { + msm_pcm_routing_process_voice(mc->shift, mc->rshift, 0); + snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, 0, + update); + } + + return 1; +} + +static int msm_routing_get_voice_stub_mixer(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + + mutex_lock(&routing_lock); + + if (test_bit(mc->rshift, &msm_bedais[mc->shift].fe_sessions[0])) + ucontrol->value.integer.value[0] = 1; + else + ucontrol->value.integer.value[0] = 0; + + mutex_unlock(&routing_lock); + + pr_debug("%s: shift %x rshift %x val %ld\n", __func__, mc->shift, mc->rshift, + ucontrol->value.integer.value[0]); + + return 0; +} + +static int msm_routing_put_voice_stub_mixer(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct snd_soc_dapm_update *update = NULL; + + if (ucontrol->value.integer.value[0]) { + mutex_lock(&routing_lock); + set_bit(mc->rshift, &msm_bedais[mc->shift].fe_sessions[0]); + mutex_unlock(&routing_lock); + + snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, 1, + update); + } else { + mutex_lock(&routing_lock); + clear_bit(mc->rshift, &msm_bedais[mc->shift].fe_sessions[0]); + mutex_unlock(&routing_lock); + + snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, 0, + update); + } + + pr_debug("%s: shift %x rshift %x val %ld\n", __func__, mc->shift, mc->rshift, + ucontrol->value.integer.value[0]); + + return 1; +} + +/* + * Return the mapping between port ID and backend ID to enable the AFE callback + * to determine the acdb_dev_id from the port id + */ +int msm_pcm_get_be_id_from_port_id(int port_id) +{ + int i; + int be_id = -EINVAL; + + for (i = 0; i < MSM_BACKEND_DAI_MAX; i++) { + if (msm_bedais[i].port_id == port_id) { + be_id = i; + break; + } + } + + return be_id; +} + +/* + * Return the registered dev_acdb_id given a port ID to enable identifying the + * correct AFE calibration information by comparing the header information. + */ +static int msm_pcm_get_dev_acdb_id_by_port_id(int port_id) +{ + int acdb_id = -EINVAL; + int i = 0; + int session; + int port_type = afe_get_port_type(port_id); + int be_id = msm_pcm_get_be_id_from_port_id(port_id); + + pr_debug("%s:port_id %d be_id %d, port_type 0x%x\n", + __func__, port_id, be_id, port_type); + + if (port_type == MSM_AFE_PORT_TYPE_TX) { + session = SESSION_TYPE_TX; + } else if (port_type == MSM_AFE_PORT_TYPE_RX) { + session = SESSION_TYPE_RX; + } else { + pr_err("%s: Invalid port type %d\n", __func__, port_type); + acdb_id = -EINVAL; + goto exit; + } + + if (be_id < 0) { + pr_err("%s: Error getting backend id %d\n", __func__, be_id); + goto exit; + } + + mutex_lock(&routing_lock); + i = find_first_bit(&msm_bedais[be_id].fe_sessions[0], + MSM_FRONTEND_DAI_MAX); + if (i < MSM_FRONTEND_DAI_MAX) + acdb_id = fe_dai_app_type_cfg[i][session][be_id].acdb_dev_id; + + pr_debug("%s: FE[%d] session[%d] BE[%d] acdb_id(%d)\n", + __func__, i, session, be_id, acdb_id); + mutex_unlock(&routing_lock); +exit: + return acdb_id; +} + +static int msm_routing_get_switch_mixer(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = fm_switch_enable; + pr_debug("%s: FM Switch enable %ld\n", __func__, + ucontrol->value.integer.value[0]); + return 0; +} + +static int msm_routing_put_switch_mixer(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); + struct snd_soc_dapm_update *update = NULL; + + pr_debug("%s: FM Switch enable %ld\n", __func__, + ucontrol->value.integer.value[0]); + if (ucontrol->value.integer.value[0]) + snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, 1, + update); + else + snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, 0, + update); + fm_switch_enable = ucontrol->value.integer.value[0]; + return 1; +} + +static int msm_routing_get_hfp_switch_mixer(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = hfp_switch_enable; + pr_debug("%s: HFP Switch enable %ld\n", __func__, + ucontrol->value.integer.value[0]); + return 0; +} + +static int msm_routing_put_hfp_switch_mixer(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); + struct snd_soc_dapm_update *update = NULL; + + pr_debug("%s: HFP Switch enable %ld\n", __func__, + ucontrol->value.integer.value[0]); + if (ucontrol->value.integer.value[0]) + snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, + 1, update); + else + snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, + 0, update); + hfp_switch_enable = ucontrol->value.integer.value[0]; + return 1; +} + +static int msm_routing_a2dp_switch_mixer_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = a2dp_switch_enable; + pr_debug("%s: A2DP Switch enable %ld\n", __func__, + ucontrol->value.integer.value[0]); + return 0; +} + +static int msm_routing_a2dp_switch_mixer_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); + struct snd_soc_dapm_update *update = NULL; + + pr_debug("%s: A2DP Switch enable %ld\n", __func__, + ucontrol->value.integer.value[0]); + a2dp_switch_enable = ucontrol->value.integer.value[0]; + if (a2dp_switch_enable) + snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, + 1, update); + else + snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, + 0, update); + return 1; +} + +static int msm_routing_get_int0_mi2s_switch_mixer(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = int0_mi2s_switch_enable; + pr_debug("%s: INT0 MI2S Switch enable %ld\n", __func__, + ucontrol->value.integer.value[0]); + return 0; +} + +static int msm_routing_put_int0_mi2s_switch_mixer(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); + struct snd_soc_dapm_update *update = NULL; + + pr_debug("%s: INT0 MI2S Switch enable %ld\n", __func__, + ucontrol->value.integer.value[0]); + if (ucontrol->value.integer.value[0]) + snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, 1, + update); + else + snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, 0, + update); + int0_mi2s_switch_enable = ucontrol->value.integer.value[0]; + return 1; +} + +static int msm_routing_get_int4_mi2s_switch_mixer(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = int4_mi2s_switch_enable; + pr_debug("%s: INT4 MI2S Switch enable %ld\n", __func__, + ucontrol->value.integer.value[0]); + return 0; +} + +static int msm_routing_put_int4_mi2s_switch_mixer(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); + struct snd_soc_dapm_update *update = NULL; + + pr_debug("%s: INT4 MI2S Switch enable %ld\n", __func__, + ucontrol->value.integer.value[0]); + if (ucontrol->value.integer.value[0]) + snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, 1, + update); + else + snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, 0, + update); + int4_mi2s_switch_enable = ucontrol->value.integer.value[0]; + return 1; +} + +static int msm_routing_get_usb_switch_mixer(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = usb_switch_enable; + pr_debug("%s: HFP Switch enable %ld\n", __func__, + ucontrol->value.integer.value[0]); + return 0; +} + +static int msm_routing_put_usb_switch_mixer(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); + struct snd_soc_dapm_update *update = NULL; + + pr_debug("%s: USB Switch enable %ld\n", __func__, + ucontrol->value.integer.value[0]); + if (ucontrol->value.integer.value[0]) + snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, + 1, update); + else + snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, + 0, update); + usb_switch_enable = ucontrol->value.integer.value[0]; + return 1; +} + +static int msm_routing_get_pri_mi2s_switch_mixer(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = pri_mi2s_switch_enable; + pr_debug("%s: PRI MI2S Switch enable %ld\n", __func__, + ucontrol->value.integer.value[0]); + return 0; +} + +static int msm_routing_put_pri_mi2s_switch_mixer(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); + struct snd_soc_dapm_update *update = NULL; + + pr_debug("%s: PRI MI2S Switch enable %ld\n", __func__, + ucontrol->value.integer.value[0]); + if (ucontrol->value.integer.value[0]) + snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, 1, + update); + else + snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, 0, + update); + pri_mi2s_switch_enable = ucontrol->value.integer.value[0]; + return 1; +} + +static int msm_routing_get_sec_mi2s_switch_mixer(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = sec_mi2s_switch_enable; + pr_debug("%s: SEC MI2S Switch enable %ld\n", __func__, + ucontrol->value.integer.value[0]); + return 0; +} + +static int msm_routing_put_sec_mi2s_switch_mixer(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); + struct snd_soc_dapm_update *update = NULL; + + pr_debug("%s: SEC MI2S Switch enable %ld\n", __func__, + ucontrol->value.integer.value[0]); + if (ucontrol->value.integer.value[0]) + snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, 1, + update); + else + snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, 0, + update); + sec_mi2s_switch_enable = ucontrol->value.integer.value[0]; + return 1; +} + +static int msm_routing_get_tert_mi2s_switch_mixer( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = tert_mi2s_switch_enable; + pr_debug("%s: TERT MI2S Switch enable %ld\n", __func__, + ucontrol->value.integer.value[0]); + return 0; +} + +static int msm_routing_put_tert_mi2s_switch_mixer( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); + struct snd_soc_dapm_update *update = NULL; + + pr_debug("%s: TERT MI2S Switch enable %ld\n", __func__, + ucontrol->value.integer.value[0]); + if (ucontrol->value.integer.value[0]) + snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, 1, + update); + else + snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, 0, + update); + tert_mi2s_switch_enable = ucontrol->value.integer.value[0]; + return 1; +} + +static int msm_routing_get_quat_mi2s_switch_mixer( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = quat_mi2s_switch_enable; + pr_debug("%s: QUAT MI2S Switch enable %ld\n", __func__, + ucontrol->value.integer.value[0]); + return 0; +} + +static int msm_routing_put_quat_mi2s_switch_mixer( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); + struct snd_soc_dapm_update *update = NULL; + + pr_debug("%s: QUAT MI2S Switch enable %ld\n", __func__, + ucontrol->value.integer.value[0]); + if (ucontrol->value.integer.value[0]) + snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, 1, + update); + else + snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, 0, + update); + quat_mi2s_switch_enable = ucontrol->value.integer.value[0]; + return 1; +} + +static int msm_routing_get_quin_mi2s_switch_mixer( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = quin_mi2s_switch_enable; + pr_debug("%s: QUIN MI2S Switch enable %ld\n", __func__, + ucontrol->value.integer.value[0]); + return 0; +} + +static int msm_routing_put_quin_mi2s_switch_mixer( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); + struct snd_soc_dapm_update *update = NULL; + + pr_debug("%s: QUIN MI2S Switch enable %ld\n", __func__, + ucontrol->value.integer.value[0]); + if (ucontrol->value.integer.value[0]) + snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, 1, + update); + else + snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, 0, + update); + quin_mi2s_switch_enable = ucontrol->value.integer.value[0]; + return 1; +} + +static int msm_routing_get_fm_pcmrx_switch_mixer(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = fm_pcmrx_switch_enable; + pr_debug("%s: FM Switch enable %ld\n", __func__, + ucontrol->value.integer.value[0]); + return 0; +} + +static int msm_routing_put_fm_pcmrx_switch_mixer(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); + struct snd_soc_dapm_update *update = NULL; + + pr_debug("%s: FM Switch enable %ld\n", __func__, + ucontrol->value.integer.value[0]); + if (ucontrol->value.integer.value[0]) + snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, 1, + update); + else + snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, 0, + update); + fm_pcmrx_switch_enable = ucontrol->value.integer.value[0]; + return 1; +} + +static int msm_routing_lsm_port_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = lsm_port_index; + return 0; +} + +static int msm_routing_lsm_port_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + int mux = ucontrol->value.enumerated.item[0]; + int lsm_port = AFE_PORT_ID_SLIMBUS_MULTI_CHAN_5_TX; + + if (mux >= e->items) { + pr_err("%s: Invalid mux value %d\n", __func__, mux); + return -EINVAL; + } + + pr_debug("%s: LSM enable %ld\n", __func__, + ucontrol->value.integer.value[0]); + switch (ucontrol->value.integer.value[0]) { + case 1: + lsm_port = AFE_PORT_ID_SLIMBUS_MULTI_CHAN_0_TX; + break; + case 2: + lsm_port = AFE_PORT_ID_SLIMBUS_MULTI_CHAN_1_TX; + break; + case 3: + lsm_port = AFE_PORT_ID_SLIMBUS_MULTI_CHAN_2_TX; + break; + case 4: + lsm_port = AFE_PORT_ID_SLIMBUS_MULTI_CHAN_3_TX; + break; + case 5: + lsm_port = AFE_PORT_ID_SLIMBUS_MULTI_CHAN_4_TX; + break; + case 6: + lsm_port = AFE_PORT_ID_SLIMBUS_MULTI_CHAN_5_TX; + break; + case 7: + lsm_port = AFE_PORT_ID_TERTIARY_MI2S_TX; + break; + case 8: + lsm_port = AFE_PORT_ID_QUATERNARY_MI2S_TX; + break; + case 9: + lsm_port = ADM_LSM_PORT_ID; + break; + case 10: + lsm_port = AFE_PORT_ID_INT3_MI2S_TX; + break; + case 11: + lsm_port = AFE_PORT_ID_VA_CODEC_DMA_TX_0; + break; + case 12: + lsm_port = AFE_PORT_ID_VA_CODEC_DMA_TX_1; + break; + case 13: + lsm_port = AFE_PORT_ID_TX_CODEC_DMA_TX_3; + break; + case 14: + lsm_port = AFE_PORT_ID_QUINARY_TDM_TX; + break; + default: + pr_err("Default lsm port"); + break; + } + set_lsm_port(lsm_port); + lsm_port_index = ucontrol->value.integer.value[0]; + + return 0; +} + +static int msm_routing_lsm_func_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int i; + u16 port_id; + enum afe_mad_type mad_type; + + pr_debug("%s: enter\n", __func__); + for (i = 0; i < ARRAY_SIZE(lsm_port_text); i++) + if (!strnstr(kcontrol->id.name, lsm_port_text[i], + strlen(lsm_port_text[i]))) + break; + + if (i-- == ARRAY_SIZE(lsm_port_text)) { + pr_warn("%s: Invalid id name %s\n", __func__, + kcontrol->id.name); + return -EINVAL; + } + + port_id = i * 2 + 1 + SLIMBUS_0_RX; + + /*Check for Tertiary/Quaternary/INT3 TX port*/ + if (strnstr(kcontrol->id.name, lsm_port_text[7], + strlen(lsm_port_text[7]))) + port_id = AFE_PORT_ID_TERTIARY_MI2S_TX; + + if (strnstr(kcontrol->id.name, lsm_port_text[8], + strlen(lsm_port_text[8]))) + port_id = AFE_PORT_ID_QUATERNARY_MI2S_TX; + + if (strnstr(kcontrol->id.name, lsm_port_text[10], + strlen(lsm_port_text[10]))) + port_id = AFE_PORT_ID_INT3_MI2S_TX; + + if (strnstr(kcontrol->id.name, lsm_port_text[13], + strlen(lsm_port_text[13]))) + port_id = AFE_PORT_ID_TX_CODEC_DMA_TX_3; + + if (strnstr(kcontrol->id.name, lsm_port_text[14], + strlen(lsm_port_text[14]))) + port_id = AFE_PORT_ID_QUINARY_TDM_TX; + + mad_type = afe_port_get_mad_type(port_id); + pr_debug("%s: port_id 0x%x, mad_type %d\n", __func__, port_id, + mad_type); + switch (mad_type) { + case MAD_HW_NONE: + ucontrol->value.integer.value[0] = MADNONE; + break; + case MAD_HW_AUDIO: + ucontrol->value.integer.value[0] = MADAUDIO; + break; + case MAD_HW_BEACON: + ucontrol->value.integer.value[0] = MADBEACON; + break; + case MAD_HW_ULTRASOUND: + ucontrol->value.integer.value[0] = MADULTRASOUND; + break; + case MAD_SW_AUDIO: + ucontrol->value.integer.value[0] = MADSWAUDIO; + break; + default: + pr_warn("%s: Unknown\n", __func__); + return -EINVAL; + } + return 0; +} + +static int msm_routing_lsm_func_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int i; + u16 port_id; + enum afe_mad_type mad_type; + + pr_debug("%s: enter\n", __func__); + for (i = 0; i < ARRAY_SIZE(lsm_port_text); i++) + if (strnstr(kcontrol->id.name, lsm_port_text[i], + strlen(lsm_port_text[i]))) + break; + + if (i-- == ARRAY_SIZE(lsm_port_text)) { + pr_warn("%s: Invalid id name %s\n", __func__, + kcontrol->id.name); + return -EINVAL; + } + + port_id = i * 2 + 1 + SLIMBUS_0_RX; + switch (ucontrol->value.integer.value[0]) { + case MADNONE: + mad_type = MAD_HW_NONE; + break; + case MADAUDIO: + mad_type = MAD_HW_AUDIO; + break; + case MADBEACON: + mad_type = MAD_HW_BEACON; + break; + case MADULTRASOUND: + mad_type = MAD_HW_ULTRASOUND; + break; + case MADSWAUDIO: + mad_type = MAD_SW_AUDIO; + break; + default: + pr_warn("%s: Unknown\n", __func__); + return -EINVAL; + } + + /*Check for Tertiary/Quaternary/INT3 TX port*/ + if (strnstr(kcontrol->id.name, lsm_port_text[7], + strlen(lsm_port_text[7]))) + port_id = AFE_PORT_ID_TERTIARY_MI2S_TX; + + if (strnstr(kcontrol->id.name, lsm_port_text[8], + strlen(lsm_port_text[8]))) + port_id = AFE_PORT_ID_QUATERNARY_MI2S_TX; + + if (strnstr(kcontrol->id.name, lsm_port_text[10], + strlen(lsm_port_text[10]))) + port_id = AFE_PORT_ID_INT3_MI2S_TX; + + if (strnstr(kcontrol->id.name, lsm_port_text[13], + strlen(lsm_port_text[13]))) + port_id = AFE_PORT_ID_TX_CODEC_DMA_TX_3; + + if (strnstr(kcontrol->id.name, lsm_port_text[14], + strlen(lsm_port_text[14]))) + port_id = AFE_PORT_ID_QUINARY_TDM_TX; + + pr_debug("%s: port_id 0x%x, mad_type %d\n", __func__, port_id, + mad_type); + return afe_port_set_mad_type(port_id, mad_type); +} + +static const char *const adm_override_chs_text[] = {"Zero", "One", "Two"}; + +static SOC_ENUM_SINGLE_EXT_DECL(slim_7_rx_adm_override_chs, + adm_override_chs_text); + +static int msm_routing_adm_get_backend_idx(struct snd_kcontrol *kcontrol) +{ + int backend_id; + + if (strnstr(kcontrol->id.name, "SLIM7_RX", sizeof("SLIM7_RX"))) { + backend_id = MSM_BACKEND_DAI_SLIMBUS_7_RX; + } else { + pr_err("%s: unsupported backend id: %s", + __func__, kcontrol->id.name); + return -EINVAL; + } + + return backend_id; +} +static int msm_routing_adm_channel_config_get( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int backend_id = msm_routing_adm_get_backend_idx(kcontrol); + + if (backend_id >= 0) { + mutex_lock(&routing_lock); + ucontrol->value.integer.value[0] = + msm_bedais[backend_id].adm_override_ch; + pr_debug("%s: adm channel count %ld for BE:%d\n", __func__, + ucontrol->value.integer.value[0], backend_id); + mutex_unlock(&routing_lock); + } + + return 0; +} + +static int msm_routing_adm_channel_config_put( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int backend_id = msm_routing_adm_get_backend_idx(kcontrol); + + if (backend_id >= 0) { + mutex_lock(&routing_lock); + msm_bedais[backend_id].adm_override_ch = + ucontrol->value.integer.value[0]; + pr_debug("%s:updating BE :%d adm channels: %d\n", + __func__, backend_id, + msm_bedais[backend_id].adm_override_ch); + mutex_unlock(&routing_lock); + } + + return 0; +} + +static const struct snd_kcontrol_new adm_channel_config_controls[] = { + SOC_ENUM_EXT("SLIM7_RX ADM Channels", slim_7_rx_adm_override_chs, + msm_routing_adm_channel_config_get, + msm_routing_adm_channel_config_put), +}; + +static int msm_routing_slim_0_rx_aanc_mux_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + + mutex_lock(&routing_lock); + ucontrol->value.integer.value[0] = slim0_rx_aanc_fb_port; + mutex_unlock(&routing_lock); + pr_debug("%s: AANC Mux Port %ld\n", __func__, + ucontrol->value.integer.value[0]); + return 0; +}; + +static int msm_routing_slim_0_rx_aanc_mux_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct aanc_data aanc_info; + + mutex_lock(&routing_lock); + memset(&aanc_info, 0x00, sizeof(aanc_info)); + pr_debug("%s: AANC Mux Port %ld\n", __func__, + ucontrol->value.integer.value[0]); + slim0_rx_aanc_fb_port = ucontrol->value.integer.value[0]; + if (ucontrol->value.integer.value[0] == 0) { + aanc_info.aanc_active = false; + aanc_info.aanc_tx_port = 0; + aanc_info.aanc_rx_port = 0; + } else { + aanc_info.aanc_active = true; + aanc_info.aanc_rx_port = SLIMBUS_0_RX; + aanc_info.aanc_tx_port = + (SLIMBUS_0_RX - 1 + (slim0_rx_aanc_fb_port * 2)); + } + afe_set_aanc_info(&aanc_info); + mutex_unlock(&routing_lock); + return 0; +}; +static int msm_routing_get_port_mixer(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = 0, shift = 0; + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + + idx = mc->rshift/(sizeof(msm_bedais[mc->shift].port_sessions[0]) * 8); + shift = mc->rshift%(sizeof(msm_bedais[mc->shift].port_sessions[0]) * 8); + + if (idx >= BE_DAI_PORT_SESSIONS_IDX_MAX) { + pr_err("%s: Invalid idx = %d\n", __func__, idx); + return -EINVAL; + } + + if (test_bit(shift, + (unsigned long *)&msm_bedais[mc->shift].port_sessions[idx])) + ucontrol->value.integer.value[0] = 1; + else + ucontrol->value.integer.value[0] = 0; + + pr_debug("%s: shift %x rshift %x val %ld\n", __func__, mc->shift, mc->rshift, + ucontrol->value.integer.value[0]); + + return 0; +} + +static int msm_routing_put_port_mixer(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = 0, shift = 0; + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + + idx = mc->rshift/(sizeof(msm_bedais[mc->shift].port_sessions[0]) * 8); + shift = mc->rshift%(sizeof(msm_bedais[mc->shift].port_sessions[0]) * 8); + + if (idx >= BE_DAI_PORT_SESSIONS_IDX_MAX) { + pr_err("%s: Invalid idx = %d\n", __func__, idx); + return -EINVAL; + } + + pr_debug("%s: shift 0x%x rshift 0x%x val %ld idx %d reminder shift %d\n", + __func__, mc->shift, mc->rshift, + ucontrol->value.integer.value[0], idx, shift); + + if (ucontrol->value.integer.value[0]) { + afe_loopback(1, msm_bedais[mc->shift].port_id, + msm_bedais[mc->rshift].port_id); + set_bit(shift, + (unsigned long *)&msm_bedais[mc->shift].port_sessions[idx]); + } else { + afe_loopback(0, msm_bedais[mc->shift].port_id, + msm_bedais[mc->rshift].port_id); + clear_bit(shift, + (unsigned long *)&msm_bedais[mc->shift].port_sessions[idx]); + } + + return 1; +} + +static int msm_pcm_get_channel_rule_index(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u16 fe_id = 0; + + fe_id = ((struct soc_mixer_control *) + kcontrol->private_value)->shift; + if (fe_id >= MSM_FRONTEND_DAI_MM_SIZE) { + pr_err("%s: invalid FE %d\n", __func__, fe_id); + return -EINVAL; + } + + ucontrol->value.integer.value[0] = channel_mixer[fe_id].rule; + + return 0; +} + +static int msm_pcm_put_channel_rule_index(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u16 fe_id = 0; + + fe_id = ((struct soc_mixer_control *) + kcontrol->private_value)->shift; + if (fe_id >= MSM_FRONTEND_DAI_MM_SIZE) { + pr_err("%s: invalid FE %d\n", __func__, fe_id); + return -EINVAL; + } + + channel_mixer[fe_id].rule = ucontrol->value.integer.value[0]; + + return 1; +} + +static int msm_pcm_get_out_chs(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u16 fe_id = 0; + + fe_id = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + if (fe_id >= MSM_FRONTEND_DAI_MM_SIZE) { + pr_err("%s: invalid FE %d\n", __func__, fe_id); + return -EINVAL; + } + + ucontrol->value.integer.value[0] = + channel_mixer[fe_id].output_channel; + return 0; +} + +static int msm_pcm_put_out_chs(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u16 fe_id = 0; + + fe_id = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + if (fe_id >= MSM_FRONTEND_DAI_MM_SIZE) { + pr_err("%s: invalid FE %d\n", __func__, fe_id); + return -EINVAL; + } + + pr_debug("%s: fe_id is %d, output channels = %d\n", __func__, + fe_id, + (unsigned int)(ucontrol->value.integer.value[0])); + channel_mixer[fe_id].output_channel = + (unsigned int)(ucontrol->value.integer.value[0]); + + return 1; +} + +static const char *const ch_mixer[] = {"Disable", "Enable"}; + +/* If new backend is added, need update this array */ +static const char *const be_name[] = { +"ZERO", "PRI_I2S_RX", "PRI_I2S_TX", "SLIM_0_RX", +"SLIM_0_TX", "HDMI_RX", "INT_BT_SCO_RX", "INT_BT_SCO_TX", +"INT_FM_RX", "INT_FM_TX", "AFE_PCM_RX", "AFE_PCM_TX", +"AUXPCM_RX", "AUXPCM_TX", "VOICE_PLAYBACK_TX", "VOICE2_PLAYBACK_TX", +"INCALL_RECORD_RX", "INCALL_RECORD_TX", "MI2S_RX", "MI2S_TX", +"SEC_I2S_RX", "SLIM_1_RX", "SLIM_1_TX", "SLIM_2_RX", +"SLIM_2_TX", "SLIM_3_RX", "SLIM_3_TX", "SLIM_4_RX", +"SLIM_4_TX", "SLIM_5_RX", "SLIM_5_TX", "SLIM_6_RX", +"SLIM_6_TX", "SLIM_7_RX", "SLIM_7_TX", "SLIM_8_RX", +"SLIM_8_TX", "EXTPROC_RX", "EXTPROC_TX", "EXPROC_EC_TX", +"QUAT_MI2S_RX", "QUAT_MI2S_TX", "SECOND_MI2S_RX", "SECOND_MI2S_TX", +"PRI_MI2S_RX", "PRI_MI2S_TX", "TERT_MI2S_RX", "TERT_MI2S_TX", +"AUDIO_I2S_RX", "SEC_AUXPCM_RX", "SEC_AUXPCM_TX", "PRI_SPDIF_RX", +"SECOND_MI2S_RX_SD1", "QUIN_MI2S_RX", "QUIN_MI2S_TX", "SENARY_MI2S_TX", +"PRI_TDM_RX_0", "PRI_TDM_TX_0", "PRI_TDM_RX_1", "PRI_TDM_TX_1", +"PRI_TDM_RX_2", "PRI_TDM_TX_2", "PRI_TDM_RX_3", "PRI_TDM_TX_3", +"PRI_TDM_RX_4", "PRI_TDM_TX_4", "PRI_TDM_RX_5", "PRI_TDM_TX_5", +"PRI_TDM_RX_6", "PRI_TDM_TX_6", "PRI_TDM_RX_7", "PRI_TDM_TX_7", +"SEC_TDM_RX_0", "SEC_TDM_TX_0", "SEC_TDM_RX_1", "SEC_TDM_TX_1", +"SEC_TDM_RX_2", "SEC_TDM_TX_2", "SEC_TDM_RX_3", "SEC_TDM_TX_3", +"SEC_TDM_RX_4", "SEC_TDM_TX_4", "SEC_TDM_RX_5", "SEC_TDM_TX_5", +"SEC_TDM_RX_6", "SEC_TDM_TX_6", "SEC_TDM_RX_7", "SEC_TDM_TX_7", +"TERT_TDM_RX_0", "TERT_TDM_TX_0", "TERT_TDM_RX_1", "TERT_TDM_TX_1", +"TERT_TDM_RX_2", "TERT_TDM_TX_2", "TERT_TDM_RX_3", "TERT_TDM_TX_3", +"TERT_TDM_RX_4", "TERT_TDM_TX_4", "TERT_TDM_RX_5", "TERT_TDM_TX_5", +"TERT_TDM_RX_6", "TERT_TDM_TX_6", "TERT_TDM_RX_7", "TERT_TDM_TX_7", +"QUAT_TDM_RX_0", "QUAT_TDM_TX_0", "QUAT_TDM_RX_1", "QUAT_TDM_TX_1", +"QUAT_TDM_RX_2", "QUAT_TDM_TX_2", "QUAT_TDM_RX_3", "QUAT_TDM_TX_3", +"QUAT_TDM_RX_4", "QUAT_TDM_TX_4", "QUAT_TDM_RX_5", "QUAT_TDM_TX_5", +"QUAT_TDM_RX_6", "QUAT_TDM_TX_6", "QUAT_TDM_RX_7", "QUAT_TDM_TX_7", +"QUIN_TDM_RX_0", "QUIN_TDM_TX_0", "QUIN_TDM_RX_1", "QUIN_TDM_TX_1", +"QUIN_TDM_RX_2", "QUIN_TDM_TX_2", "QUIN_TDM_RX_3", "QUIN_TDM_TX_3", +"QUIN_TDM_RX_4", "QUIN_TDM_TX_4", "QUIN_TDM_RX_5", "QUIN_TDM_TX_5", +"QUIN_TDM_RX_6", "QUIN_TDM_TX_6", "QUIN_TDM_RX_7", "QUIN_TDM_TX_7", +"INT_BT_A2DP_RX", "USB_RX", "USB_TX", "DISPLAY_PORT_RX", "DISPLAY_PORT_RX1", +"TERT_AUXPCM_RX", "TERT_AUXPCM_TX", "QUAT_AUXPCM_RX", "QUAT_AUXPCM_TX", +"QUIN_AUXPCM_RX", "QUIN_AUXPCM_TX", +"INT0_MI2S_RX", "INT0_MI2S_TX", "INT1_MI2S_RX", "INT1_MI2S_TX", +"INT2_MI2S_RX", "INT2_MI2S_TX", "INT3_MI2S_RX", "INT3_MI2S_TX", +"INT4_MI2S_RX", "INT4_MI2S_TX", "INT5_MI2S_RX", "INT5_MI2S_TX", +"INT6_MI2S_RX", "INT6_MI2S_TX", "WSA_CDC_DMA_RX_0", +"WSA_CDC_DMA_TX_0", "WSA_CDC_DMA_RX_1", "WSA_CDC_DMA_TX_1", +"WSA_CDC_DMA_TX_2", "VA_CDC_DMA_TX_0", "VA_CDC_DMA_TX_1", +"RX_CDC_DMA_RX_0", "TX_CDC_DMA_TX_0", "RX_CDC_DMA_RX_1", "TX_CDC_DMA_TX_1", +"RX_CDC_DMA_RX_2", "TX_CDC_DMA_TX_2", "RX_CDC_DMA_RX_3", "TX_CDC_DMA_TX_3", +"RX_CDC_DMA_RX_4", "TX_CDC_DMA_TX_4", "RX_CDC_DMA_RX_5", "TX_CDC_DMA_TX_5", +"RX_CDC_DMA_RX_6", "RX_CDC_DMA_RX_7", +"PRI_SPDIF_TX", "SEC_SPDIF_RX", "SEC_SPDIF_TX", +}; + +static SOC_ENUM_SINGLE_DECL(mm1_channel_mux, + SND_SOC_NOPM, MSM_FRONTEND_DAI_MULTIMEDIA1, ch_mixer); +static SOC_ENUM_SINGLE_DECL(mm2_channel_mux, + SND_SOC_NOPM, MSM_FRONTEND_DAI_MULTIMEDIA2, ch_mixer); +static SOC_ENUM_SINGLE_DECL(mm3_channel_mux, + SND_SOC_NOPM, MSM_FRONTEND_DAI_MULTIMEDIA3, ch_mixer); +static SOC_ENUM_SINGLE_DECL(mm4_channel_mux, + SND_SOC_NOPM, MSM_FRONTEND_DAI_MULTIMEDIA4, ch_mixer); + +static SOC_ENUM_DOUBLE_DECL(mm1_ch1_enum, + SND_SOC_NOPM, MSM_FRONTEND_DAI_MULTIMEDIA1, 0, be_name); +static SOC_ENUM_DOUBLE_DECL(mm1_ch2_enum, + SND_SOC_NOPM, MSM_FRONTEND_DAI_MULTIMEDIA1, 1, be_name); +static SOC_ENUM_DOUBLE_DECL(mm1_ch3_enum, + SND_SOC_NOPM, MSM_FRONTEND_DAI_MULTIMEDIA1, 2, be_name); +static SOC_ENUM_DOUBLE_DECL(mm1_ch4_enum, + SND_SOC_NOPM, MSM_FRONTEND_DAI_MULTIMEDIA1, 3, be_name); +static SOC_ENUM_DOUBLE_DECL(mm1_ch5_enum, + SND_SOC_NOPM, MSM_FRONTEND_DAI_MULTIMEDIA1, 4, be_name); +static SOC_ENUM_DOUBLE_DECL(mm1_ch6_enum, + SND_SOC_NOPM, MSM_FRONTEND_DAI_MULTIMEDIA1, 5, be_name); +static SOC_ENUM_DOUBLE_DECL(mm1_ch7_enum, + SND_SOC_NOPM, MSM_FRONTEND_DAI_MULTIMEDIA1, 6, be_name); +static SOC_ENUM_DOUBLE_DECL(mm1_ch8_enum, + SND_SOC_NOPM, MSM_FRONTEND_DAI_MULTIMEDIA1, 7, be_name); + +static int msm_pcm_get_ctl_enum_info(struct snd_ctl_elem_info *uinfo, + unsigned int channels, + unsigned int items, const char *const names[]) +{ + if (uinfo->value.enumerated.item >= items) + uinfo->value.enumerated.item = items - 1; + + WARN(strlen(names[uinfo->value.enumerated.item]) >= + sizeof(uinfo->value.enumerated.name), + "ALSA: too long item name '%s'\n", + names[uinfo->value.enumerated.item]); + strlcpy(uinfo->value.enumerated.name, + names[uinfo->value.enumerated.item], + sizeof(uinfo->value.enumerated.name)); + return 0; +} + +static int msm_pcm_channel_mixer_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + + uinfo->value.enumerated.items = ARRAY_SIZE(ch_mixer); + msm_pcm_get_ctl_enum_info(uinfo, 1, e->items, e->texts); + + return 0; +} +static int msm_pcm_channel_mixer_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u16 fe_id = 0; + + fe_id = ((struct soc_enum *) + kcontrol->private_value)->shift_l; + if (fe_id >= MSM_FRONTEND_DAI_MM_SIZE) { + pr_err("%s: invalid FE %d\n", __func__, fe_id); + return -EINVAL; + } + + pr_debug("%s: FE %d %s\n", __func__, + fe_id, + channel_mixer[fe_id].enable ? "Enabled" : "Disabled"); + ucontrol->value.enumerated.item[0] = channel_mixer[fe_id].enable; + return 0; +} + +static int msm_pcm_channel_mixer_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u16 fe_id = 0; + + fe_id = ((struct soc_enum *) + kcontrol->private_value)->shift_l; + if (fe_id >= MSM_FRONTEND_DAI_MM_SIZE) { + pr_err("%s: invalid FE %d\n", __func__, fe_id); + return -EINVAL; + } + + channel_mixer[fe_id].enable = ucontrol->value.enumerated.item[0]; + pr_debug("%s: %s FE %d\n", __func__, + channel_mixer[fe_id].enable ? "Enable" : "Disable", + fe_id); + return 0; +} + +static int msm_pcm_channel_input_be_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + + uinfo->value.enumerated.items = ARRAY_SIZE(be_name); + msm_pcm_get_ctl_enum_info(uinfo, 1, e->items, e->texts); + + return 0; +} + +static int msm_pcm_channel_input_be_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + u16 fe_id = 0, in_ch = 0; + + fe_id = e->shift_l; + in_ch = e->shift_r; + if (fe_id >= MSM_FRONTEND_DAI_MM_SIZE) { + pr_err("%s: invalid FE %d\n", __func__, fe_id); + return -EINVAL; + } + if (in_ch >= ADM_MAX_CHANNELS) { + pr_err("%s: invalid input channel %d\n", __func__, in_ch); + return -EINVAL; + } + + channel_input[fe_id][in_ch] = ucontrol->value.enumerated.item[0]; + return 1; +} + +static int msm_pcm_channel_input_be_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + u16 fe_id = 0, in_ch = 0; + + fe_id = e->shift_l; + in_ch = e->shift_r; + if (fe_id >= MSM_FRONTEND_DAI_MM_SIZE) { + pr_err("%s: invalid FE %d\n", __func__, fe_id); + return -EINVAL; + } + if (in_ch >= ADM_MAX_CHANNELS) { + pr_err("%s: invalid input channel %d\n", __func__, in_ch); + return -EINVAL; + } + + ucontrol->value.enumerated.item[0] = channel_input[fe_id][in_ch]; + return 1; +} + + +static int msm_pcm_channel_weight_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = ADM_MAX_CHANNELS; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = WEIGHT_0_DB; + + return 0; +} + +static int msm_pcm_channel_weight_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u16 fe_id = 0, out_ch = 0; + int i, weight; + + fe_id = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + out_ch = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->rshift; + if (fe_id >= MSM_FRONTEND_DAI_MM_SIZE) { + pr_err("%s: invalid FE %d\n", __func__, fe_id); + return -EINVAL; + } + if (out_ch >= ADM_MAX_CHANNELS) { + pr_err("%s: invalid input channel %d\n", __func__, out_ch); + return -EINVAL; + } + + pr_debug("%s: FE_ID: %d, channel weight %ld, %ld, %ld, %ld, %ld, %ld, %ld, %ld\n", + __func__, fe_id, + ucontrol->value.integer.value[0], + ucontrol->value.integer.value[1], + ucontrol->value.integer.value[2], + ucontrol->value.integer.value[3], + ucontrol->value.integer.value[4], + ucontrol->value.integer.value[5], + ucontrol->value.integer.value[6], + ucontrol->value.integer.value[7]); + + for (i = 0; i < ADM_MAX_CHANNELS; ++i) { + weight = ucontrol->value.integer.value[i]; + channel_mixer[fe_id].channel_weight[out_ch][i] = weight; + pr_debug("%s: FE_ID %d, output %d input %d weight %d\n", + __func__, fe_id, out_ch, i, + channel_mixer[fe_id].channel_weight[out_ch][i]); + } + + return 0; +} + +static int msm_pcm_channel_weight_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u16 fe_id = 0, out_ch = 0; + int i; + + fe_id = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + out_ch = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->rshift; + if (fe_id >= MSM_FRONTEND_DAI_MM_SIZE) { + pr_err("%s: invalid FE %d\n", __func__, fe_id); + return -EINVAL; + } + if (out_ch >= ADM_MAX_CHANNELS) { + pr_err("%s: invalid input channel %d\n", __func__, out_ch); + return -EINVAL; + } + + for (i = 0; i < ADM_MAX_CHANNELS; ++i) + ucontrol->value.integer.value[i] = + channel_mixer[fe_id].channel_weight[out_ch][i]; + + pr_debug("%s: FE_ID: %d, weight %ld, %ld, %ld, %ld, %ld, %ld, %ld, %ld", + __func__, fe_id, + ucontrol->value.integer.value[0], + ucontrol->value.integer.value[1], + ucontrol->value.integer.value[2], + ucontrol->value.integer.value[3], + ucontrol->value.integer.value[4], + ucontrol->value.integer.value[5], + ucontrol->value.integer.value[6], + ucontrol->value.integer.value[7]); + + return 0; +} + +static const struct snd_kcontrol_new channel_mixer_controls[] = { + SOC_SINGLE_EXT("MultiMedia1 Channel Rule", SND_SOC_NOPM, + MSM_FRONTEND_DAI_MULTIMEDIA1, 8, 0, + msm_pcm_get_channel_rule_index, + msm_pcm_put_channel_rule_index), + SOC_SINGLE_EXT("MultiMedia2 Channel Rule", SND_SOC_NOPM, + MSM_FRONTEND_DAI_MULTIMEDIA2, 8, 0, + msm_pcm_get_channel_rule_index, + msm_pcm_put_channel_rule_index), + SOC_SINGLE_EXT("MultiMedia3 Channel Rule", SND_SOC_NOPM, + MSM_FRONTEND_DAI_MULTIMEDIA3, 8, 0, + msm_pcm_get_channel_rule_index, + msm_pcm_put_channel_rule_index), + SOC_SINGLE_EXT("MultiMedia4 Channel Rule", SND_SOC_NOPM, + MSM_FRONTEND_DAI_MULTIMEDIA4, 8, 0, + msm_pcm_get_channel_rule_index, + msm_pcm_put_channel_rule_index), + SOC_SINGLE_EXT("MultiMedia5 Channel Rule", SND_SOC_NOPM, + MSM_FRONTEND_DAI_MULTIMEDIA5, 8, 0, + msm_pcm_get_channel_rule_index, + msm_pcm_put_channel_rule_index), + SOC_SINGLE_EXT("MultiMedia6 Channel Rule", SND_SOC_NOPM, + MSM_FRONTEND_DAI_MULTIMEDIA6, 8, 0, + msm_pcm_get_channel_rule_index, + msm_pcm_put_channel_rule_index), + + SOC_SINGLE_EXT("MultiMedia1 Channels", SND_SOC_NOPM, + MSM_FRONTEND_DAI_MULTIMEDIA1, 8, 0, + msm_pcm_get_out_chs, + msm_pcm_put_out_chs), + SOC_SINGLE_EXT("MultiMedia2 Channels", SND_SOC_NOPM, + MSM_FRONTEND_DAI_MULTIMEDIA2, 8, 0, + msm_pcm_get_out_chs, + msm_pcm_put_out_chs), + SOC_SINGLE_EXT("MultiMedia3 Channels", SND_SOC_NOPM, + MSM_FRONTEND_DAI_MULTIMEDIA3, 8, 0, + msm_pcm_get_out_chs, + msm_pcm_put_out_chs), + SOC_SINGLE_EXT("MultiMedia4 Channels", SND_SOC_NOPM, + MSM_FRONTEND_DAI_MULTIMEDIA4, 8, 0, + msm_pcm_get_out_chs, + msm_pcm_put_out_chs), + SOC_SINGLE_EXT("MultiMedia5 Channels", SND_SOC_NOPM, + MSM_FRONTEND_DAI_MULTIMEDIA5, 8, 0, + msm_pcm_get_out_chs, + msm_pcm_put_out_chs), + SOC_SINGLE_EXT("MultiMedia6 Channels", SND_SOC_NOPM, + MSM_FRONTEND_DAI_MULTIMEDIA6, 8, 0, + msm_pcm_get_out_chs, + msm_pcm_put_out_chs), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .name = "MultiMedia1 Channel Mixer", + .info = msm_pcm_channel_mixer_info, + .get = msm_pcm_channel_mixer_get, + .put = msm_pcm_channel_mixer_put, + .private_value = (unsigned long)&(mm1_channel_mux) + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .name = "MultiMedia2 Channel Mixer", + .info = msm_pcm_channel_mixer_info, + .get = msm_pcm_channel_mixer_get, + .put = msm_pcm_channel_mixer_put, + .private_value = (unsigned long)&(mm2_channel_mux) + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .name = "MultiMedia3 Channel Mixer", + .info = msm_pcm_channel_mixer_info, + .get = msm_pcm_channel_mixer_get, + .put = msm_pcm_channel_mixer_put, + .private_value = (unsigned long)&(mm3_channel_mux) + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .name = "MultiMedia4 Channel Mixer", + .info = msm_pcm_channel_mixer_info, + .get = msm_pcm_channel_mixer_get, + .put = msm_pcm_channel_mixer_put, + .private_value = (unsigned long)&(mm4_channel_mux) + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .name = "MultiMedia1 Output Channel1", + .info = msm_pcm_channel_weight_info, + .get = msm_pcm_channel_weight_get, + .put = msm_pcm_channel_weight_put, + .private_value = (unsigned long)&(struct soc_multi_mixer_control) + { .shift = MSM_FRONTEND_DAI_MULTIMEDIA1, .rshift = 0,} + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .name = "MultiMedia1 Output Channel2", + .info = msm_pcm_channel_weight_info, + .get = msm_pcm_channel_weight_get, + .put = msm_pcm_channel_weight_put, + .private_value = (unsigned long)&(struct soc_multi_mixer_control) + { .shift = MSM_FRONTEND_DAI_MULTIMEDIA1, .rshift = 1, } + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .name = "MultiMedia1 Output Channel3", + .info = msm_pcm_channel_weight_info, + .get = msm_pcm_channel_weight_get, + .put = msm_pcm_channel_weight_put, + .private_value = (unsigned long)&(struct soc_multi_mixer_control) + { .shift = MSM_FRONTEND_DAI_MULTIMEDIA1, .rshift = 2,} + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .name = "MultiMedia1 Output Channel4", + .info = msm_pcm_channel_weight_info, + .get = msm_pcm_channel_weight_get, + .put = msm_pcm_channel_weight_put, + .private_value = (unsigned long)&(struct soc_multi_mixer_control) + { .shift = MSM_FRONTEND_DAI_MULTIMEDIA1, .rshift = 3,} + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .name = "MultiMedia1 Output Channel5", + .info = msm_pcm_channel_weight_info, + .get = msm_pcm_channel_weight_get, + .put = msm_pcm_channel_weight_put, + .private_value = (unsigned long)&(struct soc_multi_mixer_control) + { .shift = MSM_FRONTEND_DAI_MULTIMEDIA1, .rshift = 4,} + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .name = "MultiMedia1 Output Channel6", + .info = msm_pcm_channel_weight_info, + .get = msm_pcm_channel_weight_get, + .put = msm_pcm_channel_weight_put, + .private_value = (unsigned long)&(struct soc_multi_mixer_control) + { .shift = MSM_FRONTEND_DAI_MULTIMEDIA1, .rshift = 5,} + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .name = "MultiMedia1 Output Channel7", + .info = msm_pcm_channel_weight_info, + .get = msm_pcm_channel_weight_get, + .put = msm_pcm_channel_weight_put, + .private_value = (unsigned long)&(struct soc_multi_mixer_control) + { .shift = MSM_FRONTEND_DAI_MULTIMEDIA1, .rshift = 6,} + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .name = "MultiMedia1 Output Channel8", + .info = msm_pcm_channel_weight_info, + .get = msm_pcm_channel_weight_get, + .put = msm_pcm_channel_weight_put, + .private_value = (unsigned long)&(struct soc_multi_mixer_control) + { .shift = MSM_FRONTEND_DAI_MULTIMEDIA1, .rshift = 7,} + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .name = "MultiMedia2 Output Channel1", + .info = msm_pcm_channel_weight_info, + .get = msm_pcm_channel_weight_get, + .put = msm_pcm_channel_weight_put, + .private_value = (unsigned long)&(struct soc_multi_mixer_control) + {.shift = MSM_FRONTEND_DAI_MULTIMEDIA2, .rshift = 0,} + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .name = "MultiMedia2 Output Channel2", + .info = msm_pcm_channel_weight_info, + .get = msm_pcm_channel_weight_get, + .put = msm_pcm_channel_weight_put, + .private_value = (unsigned long)&(struct soc_multi_mixer_control) + {.shift = MSM_FRONTEND_DAI_MULTIMEDIA2, .rshift = 1,} + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .name = "MultiMedia2 Output Channel3", + .info = msm_pcm_channel_weight_info, + .get = msm_pcm_channel_weight_get, + .put = msm_pcm_channel_weight_put, + .private_value = (unsigned long)&(struct soc_multi_mixer_control) + {.shift = MSM_FRONTEND_DAI_MULTIMEDIA2, .rshift = 2,} + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .name = "MultiMedia3 Output Channel1", + .info = msm_pcm_channel_weight_info, + .get = msm_pcm_channel_weight_get, + .put = msm_pcm_channel_weight_put, + .private_value = (unsigned long)&(struct soc_multi_mixer_control) + {.shift = MSM_FRONTEND_DAI_MULTIMEDIA3, .rshift = 0,} + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .name = "MultiMedia3 Output Channel2", + .info = msm_pcm_channel_weight_info, + .get = msm_pcm_channel_weight_get, + .put = msm_pcm_channel_weight_put, + .private_value = (unsigned long)&(struct soc_multi_mixer_control) + {.shift = MSM_FRONTEND_DAI_MULTIMEDIA3, .rshift = 1,} + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .name = "MultiMedia1 Channel1", + .info = msm_pcm_channel_input_be_info, + .get = msm_pcm_channel_input_be_get, + .put = msm_pcm_channel_input_be_put, + .private_value = (unsigned long)&(mm1_ch1_enum) + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .name = "MultiMedia1 Channel2", + .info = msm_pcm_channel_input_be_info, + .get = msm_pcm_channel_input_be_get, + .put = msm_pcm_channel_input_be_put, + .private_value = (unsigned long)&(mm1_ch2_enum) + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .name = "MultiMedia1 Channel3", + .info = msm_pcm_channel_input_be_info, + .get = msm_pcm_channel_input_be_get, + .put = msm_pcm_channel_input_be_put, + .private_value = (unsigned long)&(mm1_ch3_enum) + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .name = "MultiMedia1 Channel4", + .info = msm_pcm_channel_input_be_info, + .get = msm_pcm_channel_input_be_get, + .put = msm_pcm_channel_input_be_put, + .private_value = (unsigned long)&(mm1_ch4_enum) + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .name = "MultiMedia1 Channel5", + .info = msm_pcm_channel_input_be_info, + .get = msm_pcm_channel_input_be_get, + .put = msm_pcm_channel_input_be_put, + .private_value = (unsigned long)&(mm1_ch5_enum) + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .name = "MultiMedia1 Channel6", + .info = msm_pcm_channel_input_be_info, + .get = msm_pcm_channel_input_be_get, + .put = msm_pcm_channel_input_be_put, + .private_value = (unsigned long)&(mm1_ch6_enum) + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .name = "MultiMedia1 Channel7", + .info = msm_pcm_channel_input_be_info, + .get = msm_pcm_channel_input_be_get, + .put = msm_pcm_channel_input_be_put, + .private_value = (unsigned long)&(mm1_ch7_enum) + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .name = "MultiMedia1 Channel8", + .info = msm_pcm_channel_input_be_info, + .get = msm_pcm_channel_input_be_get, + .put = msm_pcm_channel_input_be_put, + .private_value = (unsigned long)&(mm1_ch8_enum) + }, +}; +static int msm_ec_ref_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = msm_ec_ref_ch; + pr_debug("%s: msm_ec_ref_ch = %ld\n", __func__, + ucontrol->value.integer.value[0]); + return 0; +} + +static int msm_ec_ref_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + msm_ec_ref_ch = ucontrol->value.integer.value[0]; + pr_debug("%s: msm_ec_ref_ch = %d\n", __func__, msm_ec_ref_ch); + adm_num_ec_ref_rx_chans(msm_ec_ref_ch); + return 0; +} + +static const char *const ec_ref_ch_text[] = {"Zero", "One", "Two", "Three", + "Four", "Five", "Six", "Seven", "Eight"}; + +static int msm_ec_ref_bit_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (msm_ec_ref_bit_format) { + case SNDRV_PCM_FORMAT_S24_LE: + ucontrol->value.integer.value[0] = 2; + break; + case SNDRV_PCM_FORMAT_S16_LE: + ucontrol->value.integer.value[0] = 1; + break; + default: + ucontrol->value.integer.value[0] = 0; + break; + } + pr_debug("%s: msm_ec_ref_bit_format = %ld\n", + __func__, ucontrol->value.integer.value[0]); + return 0; +} + +static int msm_ec_ref_bit_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u16 bit_width = 0; + + switch (ucontrol->value.integer.value[0]) { + case 2: + msm_ec_ref_bit_format = SNDRV_PCM_FORMAT_S24_LE; + break; + case 1: + msm_ec_ref_bit_format = SNDRV_PCM_FORMAT_S16_LE; + break; + default: + msm_ec_ref_bit_format = 0; + break; + } + + if (msm_ec_ref_bit_format == SNDRV_PCM_FORMAT_S16_LE) + bit_width = 16; + else if (msm_ec_ref_bit_format == SNDRV_PCM_FORMAT_S24_LE) + bit_width = 24; + + pr_debug("%s: msm_ec_ref_bit_format = %d\n", + __func__, msm_ec_ref_bit_format); + adm_ec_ref_rx_bit_width(bit_width); + return 0; +} + +static char const *ec_ref_bit_format_text[] = {"0", "S16_LE", "S24_LE"}; + +static int msm_ec_ref_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = msm_ec_ref_sampling_rate; + pr_debug("%s: msm_ec_ref_sampling_rate = %ld\n", + __func__, ucontrol->value.integer.value[0]); + return 0; +} + +static int msm_ec_ref_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (ucontrol->value.integer.value[0]) { + case 0: + msm_ec_ref_sampling_rate = 0; + break; + case 1: + msm_ec_ref_sampling_rate = 8000; + break; + case 2: + msm_ec_ref_sampling_rate = 16000; + break; + case 3: + msm_ec_ref_sampling_rate = 32000; + break; + case 4: + msm_ec_ref_sampling_rate = 44100; + break; + case 5: + msm_ec_ref_sampling_rate = 48000; + break; + case 6: + msm_ec_ref_sampling_rate = 96000; + break; + case 7: + msm_ec_ref_sampling_rate = 192000; + break; + case 8: + msm_ec_ref_sampling_rate = 384000; + break; + default: + msm_ec_ref_sampling_rate = 48000; + break; + } + pr_debug("%s: msm_ec_ref_sampling_rate = %d\n", + __func__, msm_ec_ref_sampling_rate); + adm_ec_ref_rx_sampling_rate(msm_ec_ref_sampling_rate); + return 0; +} + +static const char *const ec_ref_rate_text[] = {"0", "8000", "16000", + "32000", "44100", "48000", "96000", "192000", "384000"}; + +static const struct soc_enum msm_route_ec_ref_params_enum[] = { + SOC_ENUM_SINGLE_EXT(9, ec_ref_ch_text), + SOC_ENUM_SINGLE_EXT(3, ec_ref_bit_format_text), + SOC_ENUM_SINGLE_EXT(9, ec_ref_rate_text), +}; + +static const struct snd_kcontrol_new ec_ref_param_controls[] = { + SOC_ENUM_EXT("EC Reference Channels", msm_route_ec_ref_params_enum[0], + msm_ec_ref_ch_get, msm_ec_ref_ch_put), + SOC_ENUM_EXT("EC Reference Bit Format", msm_route_ec_ref_params_enum[1], + msm_ec_ref_bit_format_get, msm_ec_ref_bit_format_put), + SOC_ENUM_EXT("EC Reference SampleRate", msm_route_ec_ref_params_enum[2], + msm_ec_ref_rate_get, msm_ec_ref_rate_put), +}; + +static int msm_routing_ec_ref_rx_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s: ec_ref_rx = %d", __func__, msm_route_ec_ref_rx); + mutex_lock(&routing_lock); + ucontrol->value.integer.value[0] = msm_route_ec_ref_rx; + mutex_unlock(&routing_lock); + return 0; +} + +static int msm_routing_ec_ref_rx_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ec_ref_port_id; + struct snd_soc_dapm_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + struct snd_soc_dapm_update *update = NULL; + + + mutex_lock(&routing_lock); + switch (ucontrol->value.integer.value[0]) { + case 0: + msm_route_ec_ref_rx = 0; + ec_ref_port_id = AFE_PORT_INVALID; + break; + case 1: + msm_route_ec_ref_rx = 1; + ec_ref_port_id = SLIMBUS_0_RX; + break; + case 2: + msm_route_ec_ref_rx = 2; + ec_ref_port_id = AFE_PORT_ID_PRIMARY_MI2S_RX; + break; + case 3: + msm_route_ec_ref_rx = 3; + ec_ref_port_id = AFE_PORT_ID_PRIMARY_MI2S_TX; + break; + case 4: + msm_route_ec_ref_rx = 4; + ec_ref_port_id = AFE_PORT_ID_SECONDARY_MI2S_TX; + break; + case 5: + msm_route_ec_ref_rx = 5; + ec_ref_port_id = AFE_PORT_ID_TERTIARY_MI2S_TX; + break; + case 6: + msm_route_ec_ref_rx = 6; + ec_ref_port_id = AFE_PORT_ID_QUATERNARY_MI2S_TX; + break; + case 7: + msm_route_ec_ref_rx = 7; + ec_ref_port_id = AFE_PORT_ID_SECONDARY_MI2S_RX; + break; + case 9: + msm_route_ec_ref_rx = 9; + ec_ref_port_id = SLIMBUS_5_RX; + break; + case 10: + msm_route_ec_ref_rx = 10; + ec_ref_port_id = SLIMBUS_1_TX; + break; + case 11: + msm_route_ec_ref_rx = 11; + ec_ref_port_id = AFE_PORT_ID_QUATERNARY_TDM_TX_1; + break; + case 12: + msm_route_ec_ref_rx = 12; + ec_ref_port_id = AFE_PORT_ID_QUATERNARY_TDM_RX; + break; + case 13: + msm_route_ec_ref_rx = 13; + ec_ref_port_id = AFE_PORT_ID_QUATERNARY_TDM_RX_1; + break; + case 14: + msm_route_ec_ref_rx = 14; + ec_ref_port_id = AFE_PORT_ID_QUATERNARY_TDM_RX_2; + break; + case 15: + msm_route_ec_ref_rx = 15; + ec_ref_port_id = SLIMBUS_6_RX; + break; + case 16: + msm_route_ec_ref_rx = 16; + ec_ref_port_id = AFE_PORT_ID_TERTIARY_MI2S_RX; + break; + case 17: + msm_route_ec_ref_rx = 17; + ec_ref_port_id = AFE_PORT_ID_QUATERNARY_MI2S_RX; + break; + case 18: + msm_route_ec_ref_rx = 18; + ec_ref_port_id = AFE_PORT_ID_TERTIARY_TDM_TX; + break; + case 19: + msm_route_ec_ref_rx = 19; + ec_ref_port_id = AFE_PORT_ID_USB_RX; + break; + case 20: + msm_route_ec_ref_rx = 20; + ec_ref_port_id = AFE_PORT_ID_INT0_MI2S_RX; + break; + case 21: + msm_route_ec_ref_rx = 21; + ec_ref_port_id = AFE_PORT_ID_INT4_MI2S_RX; + break; + case 22: + msm_route_ec_ref_rx = 22; + ec_ref_port_id = AFE_PORT_ID_INT3_MI2S_TX; + break; + case 23: + msm_route_ec_ref_rx = 23; + ec_ref_port_id = AFE_PORT_ID_HDMI_OVER_DP_RX; + break; + case 24: + msm_route_ec_ref_rx = 24; + ec_ref_port_id = AFE_PORT_ID_WSA_CODEC_DMA_RX_0; + break; + case 25: + msm_route_ec_ref_rx = 25; + ec_ref_port_id = AFE_PORT_ID_WSA_CODEC_DMA_RX_1; + break; + case 26: + msm_route_ec_ref_rx = 26; + ec_ref_port_id = AFE_PORT_ID_WSA_CODEC_DMA_TX_0; + break; + case 27: + msm_route_ec_ref_rx = 27; + ec_ref_port_id = AFE_PORT_ID_WSA_CODEC_DMA_TX_1; + break; + case 28: + msm_route_ec_ref_rx = 28; + ec_ref_port_id = AFE_PORT_ID_WSA_CODEC_DMA_TX_2; + break; + case 29: + msm_route_ec_ref_rx = 29; + ec_ref_port_id = SLIMBUS_7_RX; + break; + case 30: + msm_route_ec_ref_rx = 30; + ec_ref_port_id = AFE_PORT_ID_RX_CODEC_DMA_RX_0; + break; + case 31: + msm_route_ec_ref_rx = 31; + ec_ref_port_id = AFE_PORT_ID_RX_CODEC_DMA_RX_1; + break; + case 32: + msm_route_ec_ref_rx = 32; + ec_ref_port_id = AFE_PORT_ID_RX_CODEC_DMA_RX_2; + break; + case 33: + msm_route_ec_ref_rx = 33; + ec_ref_port_id = AFE_PORT_ID_RX_CODEC_DMA_RX_3; + break; + case 34: + msm_route_ec_ref_rx = 34; + ec_ref_port_id = AFE_PORT_ID_TX_CODEC_DMA_TX_0; + break; + case 35: + msm_route_ec_ref_rx = 35; + ec_ref_port_id = AFE_PORT_ID_TERTIARY_TDM_RX_2; + break; + case 36: + msm_route_ec_ref_rx = 36; + ec_ref_port_id = AFE_PORT_ID_SECONDARY_TDM_TX; + break; + default: + msm_route_ec_ref_rx = 0; /* NONE */ + pr_err("%s EC ref rx %ld not valid\n", + __func__, ucontrol->value.integer.value[0]); + ec_ref_port_id = AFE_PORT_INVALID; + break; + } + adm_ec_ref_rx_id(ec_ref_port_id); + pr_debug("%s: msm_route_ec_ref_rx = %d\n", + __func__, msm_route_ec_ref_rx); + mutex_unlock(&routing_lock); + snd_soc_dapm_mux_update_power(widget->dapm, kcontrol, + msm_route_ec_ref_rx, e, update); + return 0; +} + +static const char *const ec_ref_rx[] = { "None", "SLIM_RX", "I2S_RX", + "PRI_MI2S_TX", "SEC_MI2S_TX", + "TERT_MI2S_TX", "QUAT_MI2S_TX", "SEC_I2S_RX", "PROXY_RX", + "SLIM_5_RX", "SLIM_1_TX", "QUAT_TDM_TX_1", + "QUAT_TDM_RX_0", "QUAT_TDM_RX_1", "QUAT_TDM_RX_2", "SLIM_6_RX", + "TERT_MI2S_RX", "QUAT_MI2S_RX", "TERT_TDM_TX_0", "USB_AUDIO_RX", + "INT0_MI2S_RX", "INT4_MI2S_RX", "INT3_MI2S_TX", "DISPLAY_PORT", + "WSA_CDC_DMA_RX_0", "WSA_CDC_DMA_RX_1", + "WSA_CDC_DMA_TX_0", "WSA_CDC_DMA_TX_1", "WSA_CDC_DMA_TX_2", + "SLIM_7_RX", "RX_CDC_DMA_RX_0", "RX_CDC_DMA_RX_1", "RX_CDC_DMA_RX_2", + "RX_CDC_DMA_RX_3", "TX_CDC_DMA_TX_0", "TERT_TDM_RX_2", "SEC_TDM_TX_0", +}; + +static const struct soc_enum msm_route_ec_ref_rx_enum[] = { + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(ec_ref_rx), ec_ref_rx), +}; + +static const struct snd_kcontrol_new ext_ec_ref_mux_ul1 = + SOC_DAPM_ENUM_EXT("AUDIO_REF_EC_UL1 MUX Mux", + msm_route_ec_ref_rx_enum[0], + msm_routing_ec_ref_rx_get, msm_routing_ec_ref_rx_put); + +static const struct snd_kcontrol_new ext_ec_ref_mux_ul2 = + SOC_DAPM_ENUM_EXT("AUDIO_REF_EC_UL2 MUX Mux", + msm_route_ec_ref_rx_enum[0], + msm_routing_ec_ref_rx_get, msm_routing_ec_ref_rx_put); + +static const struct snd_kcontrol_new ext_ec_ref_mux_ul3 = + SOC_DAPM_ENUM_EXT("AUDIO_REF_EC_UL3 MUX Mux", + msm_route_ec_ref_rx_enum[0], + msm_routing_ec_ref_rx_get, msm_routing_ec_ref_rx_put); + +static const struct snd_kcontrol_new ext_ec_ref_mux_ul4 = + SOC_DAPM_ENUM_EXT("AUDIO_REF_EC_UL4 MUX Mux", + msm_route_ec_ref_rx_enum[0], + msm_routing_ec_ref_rx_get, msm_routing_ec_ref_rx_put); + +static const struct snd_kcontrol_new ext_ec_ref_mux_ul5 = + SOC_DAPM_ENUM_EXT("AUDIO_REF_EC_UL5 MUX Mux", + msm_route_ec_ref_rx_enum[0], + msm_routing_ec_ref_rx_get, msm_routing_ec_ref_rx_put); + +static const struct snd_kcontrol_new ext_ec_ref_mux_ul6 = + SOC_DAPM_ENUM_EXT("AUDIO_REF_EC_UL6 MUX Mux", + msm_route_ec_ref_rx_enum[0], + msm_routing_ec_ref_rx_get, msm_routing_ec_ref_rx_put); + +static const struct snd_kcontrol_new ext_ec_ref_mux_ul8 = + SOC_DAPM_ENUM_EXT("AUDIO_REF_EC_UL8 MUX Mux", + msm_route_ec_ref_rx_enum[0], + msm_routing_ec_ref_rx_get, msm_routing_ec_ref_rx_put); + +static const struct snd_kcontrol_new ext_ec_ref_mux_ul9 = + SOC_DAPM_ENUM_EXT("AUDIO_REF_EC_UL9 MUX Mux", + msm_route_ec_ref_rx_enum[0], + msm_routing_ec_ref_rx_get, msm_routing_ec_ref_rx_put); + +static const struct snd_kcontrol_new ext_ec_ref_mux_ul16 = + SOC_DAPM_ENUM_EXT("AUDIO_REF_EC_UL16 MUX Mux", + msm_route_ec_ref_rx_enum[0], + msm_routing_ec_ref_rx_get, msm_routing_ec_ref_rx_put); + +static const struct snd_kcontrol_new ext_ec_ref_mux_ul10 = + SOC_DAPM_ENUM_EXT("AUDIO_REF_EC_UL10 MUX Mux", + msm_route_ec_ref_rx_enum[0], + msm_routing_ec_ref_rx_get, msm_routing_ec_ref_rx_put); + +static const struct snd_kcontrol_new ext_ec_ref_mux_ul17 = + SOC_DAPM_ENUM_EXT("AUDIO_REF_EC_UL17 MUX Mux", + msm_route_ec_ref_rx_enum[0], + msm_routing_ec_ref_rx_get, msm_routing_ec_ref_rx_put); + +static const struct snd_kcontrol_new ext_ec_ref_mux_ul18 = + SOC_DAPM_ENUM_EXT("AUDIO_REF_EC_UL18 MUX Mux", + msm_route_ec_ref_rx_enum[0], + msm_routing_ec_ref_rx_get, msm_routing_ec_ref_rx_put); + +static const struct snd_kcontrol_new ext_ec_ref_mux_ul19 = + SOC_DAPM_ENUM_EXT("AUDIO_REF_EC_UL19 MUX Mux", + msm_route_ec_ref_rx_enum[0], + msm_routing_ec_ref_rx_get, msm_routing_ec_ref_rx_put); + +static const struct snd_kcontrol_new ext_ec_ref_mux_ul28 = + SOC_DAPM_ENUM_EXT("AUDIO_REF_EC_UL28 MUX Mux", + msm_route_ec_ref_rx_enum[0], + msm_routing_ec_ref_rx_get, msm_routing_ec_ref_rx_put); + +static const struct snd_kcontrol_new ext_ec_ref_mux_ul29 = + SOC_DAPM_ENUM_EXT("AUDIO_REF_EC_UL29 MUX Mux", + msm_route_ec_ref_rx_enum[0], + msm_routing_ec_ref_rx_get, msm_routing_ec_ref_rx_put); + +static int msm_routing_ext_ec_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s: ext_ec_ref_rx = %x\n", __func__, msm_route_ext_ec_ref); + + mutex_lock(&routing_lock); + ucontrol->value.integer.value[0] = msm_route_ext_ec_ref; + mutex_unlock(&routing_lock); + return 0; +} + +static int msm_routing_ext_ec_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); + int mux = ucontrol->value.enumerated.item[0]; + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + int ret = 1; + bool state = true; + uint16_t ext_ec_ref_port_id; + struct snd_soc_dapm_update *update = NULL; + + if (mux >= e->items) { + pr_err("%s: Invalid mux value %d\n", __func__, mux); + return -EINVAL; + } + + mutex_lock(&routing_lock); + msm_route_ext_ec_ref = ucontrol->value.integer.value[0]; + + switch (msm_route_ext_ec_ref) { + case EXT_EC_REF_PRI_MI2S_TX: + ext_ec_ref_port_id = AFE_PORT_ID_PRIMARY_MI2S_TX; + break; + case EXT_EC_REF_SEC_MI2S_TX: + ext_ec_ref_port_id = AFE_PORT_ID_SECONDARY_MI2S_TX; + break; + case EXT_EC_REF_TERT_MI2S_TX: + ext_ec_ref_port_id = AFE_PORT_ID_TERTIARY_MI2S_TX; + break; + case EXT_EC_REF_QUAT_MI2S_TX: + ext_ec_ref_port_id = AFE_PORT_ID_QUATERNARY_MI2S_TX; + break; + case EXT_EC_REF_QUIN_MI2S_TX: + ext_ec_ref_port_id = AFE_PORT_ID_QUINARY_MI2S_TX; + break; + case EXT_EC_REF_SLIM_1_TX: + ext_ec_ref_port_id = SLIMBUS_1_TX; + break; + case EXT_EC_REF_SEC_TDM_TX: + ext_ec_ref_port_id = AFE_PORT_ID_SECONDARY_TDM_TX; + break; + case EXT_EC_REF_NONE: + default: + ext_ec_ref_port_id = AFE_PORT_INVALID; + state = false; + break; + } + + pr_debug("%s: val = %d ext_ec_ref_port_id = 0x%0x state = %d\n", + __func__, msm_route_ext_ec_ref, ext_ec_ref_port_id, state); + + if (!voc_set_ext_ec_ref_port_id(ext_ec_ref_port_id, state)) { + mutex_unlock(&routing_lock); + snd_soc_dapm_mux_update_power(widget->dapm, kcontrol, mux, e, + update); + } else { + ret = -EINVAL; + mutex_unlock(&routing_lock); + } + return ret; +} + +static const char * const ext_ec_ref_rx[] = {"NONE", "PRI_MI2S_TX", + "SEC_MI2S_TX", "TERT_MI2S_TX", + "QUAT_MI2S_TX", "QUIN_MI2S_TX", + "SLIM_1_TX", "SEC_TDM_TX"}; + +static const struct soc_enum msm_route_ext_ec_ref_rx_enum[] = { + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(ext_ec_ref_rx), ext_ec_ref_rx), +}; + +static const struct snd_kcontrol_new voc_ext_ec_mux = + SOC_DAPM_ENUM_EXT("VOC_EXT_EC MUX Mux", msm_route_ext_ec_ref_rx_enum[0], + msm_routing_ext_ec_get, msm_routing_ext_ec_put); + + +static const struct snd_kcontrol_new pri_i2s_rx_mixer_controls[] = { + SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_I2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_I2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_I2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_I2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_I2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_I2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_I2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_I2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_I2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_I2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_I2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_I2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_I2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_I2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_I2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_I2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia17", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_I2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia18", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_I2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia19", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_I2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia26", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_I2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia28", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_I2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia29", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_I2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new sec_i2s_rx_mixer_controls[] = { + SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_I2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_I2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_I2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_I2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_I2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_I2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_I2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_I2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_I2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_I2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_I2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_I2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_I2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_I2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_I2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_I2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia17", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_I2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia18", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_I2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia19", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_I2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia26", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_I2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia28", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_I2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia29", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_I2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new pri_spdif_rx_mixer_controls[] = { + SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_SPDIF_RX, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_SPDIF_RX, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_SPDIF_RX, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_SPDIF_RX, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_SPDIF_RX, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_SPDIF_RX, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_SPDIF_RX, + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_SPDIF_RX, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_SPDIF_RX, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_SPDIF_RX, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_SPDIF_RX, + MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_SPDIF_RX, + MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_SPDIF_RX, + MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_SPDIF_RX, + MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_SPDIF_RX, + MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_SPDIF_RX, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia17", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_SPDIF_RX, + MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia18", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_SPDIF_RX, + MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia19", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_SPDIF_RX, + MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia26", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_SPDIF_RX, + MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia28", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_SPDIF_RX, + MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia29", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_SPDIF_RX, + MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new sec_spdif_rx_mixer_controls[] = { + SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_SPDIF_RX, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_SPDIF_RX, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_SPDIF_RX, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_SPDIF_RX, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_SPDIF_RX, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_SPDIF_RX, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_SPDIF_RX, + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_SPDIF_RX, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_SPDIF_RX, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_SPDIF_RX, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_SPDIF_RX, + MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_SPDIF_RX, + MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_SPDIF_RX, + MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_SPDIF_RX, + MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_SPDIF_RX, + MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_SPDIF_RX, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia17", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_SPDIF_RX, + MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia18", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_SPDIF_RX, + MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia19", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_SPDIF_RX, + MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia26", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_SPDIF_RX, + MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia28", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_SPDIF_RX, + MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia29", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_SPDIF_RX, + MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new slimbus_2_rx_mixer_controls[] = { + SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_2_RX, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_2_RX, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_2_RX, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_2_RX, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_2_RX, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_2_RX, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_2_RX, + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_2_RX, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_2_RX, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_2_RX, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_2_RX, + MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_2_RX, + MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_2_RX, + MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_2_RX, + MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_2_RX, + MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_2_RX, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia26", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_2_RX, + MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new slimbus_5_rx_mixer_controls[] = { + SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_5_RX, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_5_RX, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_5_RX, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_5_RX, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_5_RX, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_5_RX, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_5_RX, + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_5_RX, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_5_RX, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_5_RX, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_5_RX, + MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_5_RX, + MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_5_RX, + MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_5_RX, + MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_5_RX, + MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_5_RX, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia17", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_5_RX, + MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia18", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_5_RX, + MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia19", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_5_RX, + MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia26", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_5_RX, + MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia28", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_5_RX, + MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia29", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_5_RX, + MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new slimbus_rx_mixer_controls[] = { + SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_0_RX, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_0_RX, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_0_RX, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_0_RX, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_0_RX, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_0_RX, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_0_RX, + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_0_RX, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_0_RX, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_0_RX, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_0_RX, + MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_0_RX, + MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_0_RX, + MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_0_RX, + MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_0_RX, + MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_0_RX, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia17", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_0_RX, + MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia18", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_0_RX, + MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia19", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_0_RX, + MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia26", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_0_RX, + MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia28", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_0_RX, + MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia29", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_0_RX, + MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new mi2s_rx_mixer_controls[] = { + SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM, + MSM_BACKEND_DAI_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM, + MSM_BACKEND_DAI_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM, + MSM_BACKEND_DAI_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM, + MSM_BACKEND_DAI_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM, + MSM_BACKEND_DAI_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM, + MSM_BACKEND_DAI_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM, + MSM_BACKEND_DAI_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM, + MSM_BACKEND_DAI_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM, + MSM_BACKEND_DAI_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM, + MSM_BACKEND_DAI_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM, + MSM_BACKEND_DAI_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM, + MSM_BACKEND_DAI_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM, + MSM_BACKEND_DAI_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM, + MSM_BACKEND_DAI_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM, + MSM_BACKEND_DAI_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM, + MSM_BACKEND_DAI_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia17", SND_SOC_NOPM, + MSM_BACKEND_DAI_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia18", SND_SOC_NOPM, + MSM_BACKEND_DAI_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia19", SND_SOC_NOPM, + MSM_BACKEND_DAI_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia26", SND_SOC_NOPM, + MSM_BACKEND_DAI_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia28", SND_SOC_NOPM, + MSM_BACKEND_DAI_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia29", SND_SOC_NOPM, + MSM_BACKEND_DAI_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new quaternary_mi2s_rx_mixer_controls[] = { + SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUATERNARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUATERNARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUATERNARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUATERNARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUATERNARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUATERNARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUATERNARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUATERNARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUATERNARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUATERNARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUATERNARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUATERNARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUATERNARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUATERNARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUATERNARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUATERNARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia17", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUATERNARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia18", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUATERNARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia19", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUATERNARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia26", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUATERNARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia28", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUATERNARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia29", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUATERNARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new quinary_mi2s_rx_mixer_controls[] = { + SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUINARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUINARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUINARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUINARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUINARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUINARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUINARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUINARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUINARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUINARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUINARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUINARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUINARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUINARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUINARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUINARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia17", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUINARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia18", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUINARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia19", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUINARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia26", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUINARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia28", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUINARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia29", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUINARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + +}; + +static const struct snd_kcontrol_new tertiary_mi2s_rx_mixer_controls[] = { + SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERTIARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERTIARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERTIARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERTIARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERTIARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERTIARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERTIARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERTIARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERTIARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERTIARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERTIARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERTIARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERTIARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERTIARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia17", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERTIARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia18", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERTIARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia19", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERTIARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia26", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERTIARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia28", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERTIARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia29", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERTIARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new secondary_mi2s_rx2_mixer_controls[] = { + SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM, + MSM_BACKEND_DAI_SECONDARY_MI2S_RX_SD1, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new secondary_mi2s_rx_mixer_controls[] = { + SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM, + MSM_BACKEND_DAI_SECONDARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM, + MSM_BACKEND_DAI_SECONDARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM, + MSM_BACKEND_DAI_SECONDARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM, + MSM_BACKEND_DAI_SECONDARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM, + MSM_BACKEND_DAI_SECONDARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM, + MSM_BACKEND_DAI_SECONDARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM, + MSM_BACKEND_DAI_SECONDARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM, + MSM_BACKEND_DAI_SECONDARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM, + MSM_BACKEND_DAI_SECONDARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM, + MSM_BACKEND_DAI_SECONDARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM, + MSM_BACKEND_DAI_SECONDARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM, + MSM_BACKEND_DAI_SECONDARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM, + MSM_BACKEND_DAI_SECONDARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM, + MSM_BACKEND_DAI_SECONDARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM, + MSM_BACKEND_DAI_SECONDARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM, + MSM_BACKEND_DAI_SECONDARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia17", SND_SOC_NOPM, + MSM_BACKEND_DAI_SECONDARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia18", SND_SOC_NOPM, + MSM_BACKEND_DAI_SECONDARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia19", SND_SOC_NOPM, + MSM_BACKEND_DAI_SECONDARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia26", SND_SOC_NOPM, + MSM_BACKEND_DAI_SECONDARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia28", SND_SOC_NOPM, + MSM_BACKEND_DAI_SECONDARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia29", SND_SOC_NOPM, + MSM_BACKEND_DAI_SECONDARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new primary_mi2s_rx_mixer_controls[] = { + SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia17", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia18", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia19", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia26", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia28", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia29", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + +}; + +static const struct snd_kcontrol_new int0_mi2s_rx_mixer_controls[] = { + SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT0_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT0_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT0_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT0_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT0_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT0_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT0_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT0_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT0_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT0_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT0_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT0_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT0_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT0_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT0_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT0_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new int4_mi2s_rx_mixer_controls[] = { + SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT4_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT4_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT4_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT4_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT4_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT4_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT4_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT4_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT4_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT4_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT4_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT4_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT4_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT4_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT4_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT4_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new hdmi_mixer_controls[] = { + SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM, + MSM_BACKEND_DAI_HDMI_RX, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM, + MSM_BACKEND_DAI_HDMI_RX, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM, + MSM_BACKEND_DAI_HDMI_RX, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM, + MSM_BACKEND_DAI_HDMI_RX, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM, + MSM_BACKEND_DAI_HDMI_RX, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM, + MSM_BACKEND_DAI_HDMI_RX, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM, + MSM_BACKEND_DAI_HDMI_RX, + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM, + MSM_BACKEND_DAI_HDMI_RX, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM, + MSM_BACKEND_DAI_HDMI_RX, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM, + MSM_BACKEND_DAI_HDMI_RX, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM, + MSM_BACKEND_DAI_HDMI_RX, + MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM, + MSM_BACKEND_DAI_HDMI_RX, + MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM, + MSM_BACKEND_DAI_HDMI_RX, + MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM, + MSM_BACKEND_DAI_HDMI_RX, + MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM, + MSM_BACKEND_DAI_HDMI_RX, + MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM, + MSM_BACKEND_DAI_HDMI_RX, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia17", SND_SOC_NOPM, + MSM_BACKEND_DAI_HDMI_RX, + MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia18", SND_SOC_NOPM, + MSM_BACKEND_DAI_HDMI_RX, + MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia19", SND_SOC_NOPM, + MSM_BACKEND_DAI_HDMI_RX, + MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia26", SND_SOC_NOPM, + MSM_BACKEND_DAI_HDMI_RX, + MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia28", SND_SOC_NOPM, + MSM_BACKEND_DAI_HDMI_RX, + MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia29", SND_SOC_NOPM, + MSM_BACKEND_DAI_HDMI_RX, + MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new display_port_mixer_controls[] = { + SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM, + MSM_BACKEND_DAI_DISPLAY_PORT_RX, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM, + MSM_BACKEND_DAI_DISPLAY_PORT_RX, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM, + MSM_BACKEND_DAI_DISPLAY_PORT_RX, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM, + MSM_BACKEND_DAI_DISPLAY_PORT_RX, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM, + MSM_BACKEND_DAI_DISPLAY_PORT_RX, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM, + MSM_BACKEND_DAI_DISPLAY_PORT_RX, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM, + MSM_BACKEND_DAI_DISPLAY_PORT_RX, + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM, + MSM_BACKEND_DAI_DISPLAY_PORT_RX, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM, + MSM_BACKEND_DAI_DISPLAY_PORT_RX, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM, + MSM_BACKEND_DAI_DISPLAY_PORT_RX, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM, + MSM_BACKEND_DAI_DISPLAY_PORT_RX, + MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM, + MSM_BACKEND_DAI_DISPLAY_PORT_RX, + MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM, + MSM_BACKEND_DAI_DISPLAY_PORT_RX, + MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM, + MSM_BACKEND_DAI_DISPLAY_PORT_RX, + MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM, + MSM_BACKEND_DAI_DISPLAY_PORT_RX, + MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM, + MSM_BACKEND_DAI_DISPLAY_PORT_RX, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia26", SND_SOC_NOPM, + MSM_BACKEND_DAI_DISPLAY_PORT_RX, + MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new display_port1_mixer_controls[] = { + SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM, + MSM_BACKEND_DAI_DISPLAY_PORT_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM, + MSM_BACKEND_DAI_DISPLAY_PORT_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM, + MSM_BACKEND_DAI_DISPLAY_PORT_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM, + MSM_BACKEND_DAI_DISPLAY_PORT_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM, + MSM_BACKEND_DAI_DISPLAY_PORT_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM, + MSM_BACKEND_DAI_DISPLAY_PORT_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM, + MSM_BACKEND_DAI_DISPLAY_PORT_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM, + MSM_BACKEND_DAI_DISPLAY_PORT_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM, + MSM_BACKEND_DAI_DISPLAY_PORT_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM, + MSM_BACKEND_DAI_DISPLAY_PORT_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM, + MSM_BACKEND_DAI_DISPLAY_PORT_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM, + MSM_BACKEND_DAI_DISPLAY_PORT_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM, + MSM_BACKEND_DAI_DISPLAY_PORT_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM, + MSM_BACKEND_DAI_DISPLAY_PORT_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM, + MSM_BACKEND_DAI_DISPLAY_PORT_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM, + MSM_BACKEND_DAI_DISPLAY_PORT_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia26", SND_SOC_NOPM, + MSM_BACKEND_DAI_DISPLAY_PORT_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + + /* incall music delivery mixer */ +static const struct snd_kcontrol_new incall_music_delivery_mixer_controls[] = { + SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM, + MSM_BACKEND_DAI_VOICE_PLAYBACK_TX, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM, + MSM_BACKEND_DAI_VOICE_PLAYBACK_TX, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM, + MSM_BACKEND_DAI_VOICE_PLAYBACK_TX, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM, + MSM_BACKEND_DAI_VOICE_PLAYBACK_TX, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new incall_music2_delivery_mixer_controls[] = { + SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM, + MSM_BACKEND_DAI_VOICE2_PLAYBACK_TX, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM, + MSM_BACKEND_DAI_VOICE2_PLAYBACK_TX, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM, + MSM_BACKEND_DAI_VOICE2_PLAYBACK_TX, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM, + MSM_BACKEND_DAI_VOICE2_PLAYBACK_TX, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new slimbus_4_rx_mixer_controls[] = { + SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_4_RX, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_4_RX, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_4_RX, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_4_RX, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new slimbus_6_rx_mixer_controls[] = { + SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_6_RX, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_6_RX, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_6_RX, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_6_RX, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_6_RX, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_6_RX, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_6_RX, + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_6_RX, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_6_RX, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_6_RX, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_6_RX, + MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_6_RX, + MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_6_RX, + MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_6_RX, + MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_6_RX, + MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_6_RX, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia26", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_6_RX, + MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new wsa_cdc_dma_rx_0_mixer_controls[] = { + SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM, + MSM_BACKEND_DAI_WSA_CDC_DMA_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM, + MSM_BACKEND_DAI_WSA_CDC_DMA_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM, + MSM_BACKEND_DAI_WSA_CDC_DMA_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM, + MSM_BACKEND_DAI_WSA_CDC_DMA_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM, + MSM_BACKEND_DAI_WSA_CDC_DMA_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM, + MSM_BACKEND_DAI_WSA_CDC_DMA_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM, + MSM_BACKEND_DAI_WSA_CDC_DMA_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM, + MSM_BACKEND_DAI_WSA_CDC_DMA_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM, + MSM_BACKEND_DAI_WSA_CDC_DMA_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM, + MSM_BACKEND_DAI_WSA_CDC_DMA_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM, + MSM_BACKEND_DAI_WSA_CDC_DMA_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM, + MSM_BACKEND_DAI_WSA_CDC_DMA_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM, + MSM_BACKEND_DAI_WSA_CDC_DMA_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM, + MSM_BACKEND_DAI_WSA_CDC_DMA_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM, + MSM_BACKEND_DAI_WSA_CDC_DMA_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM, + MSM_BACKEND_DAI_WSA_CDC_DMA_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia26", SND_SOC_NOPM, + MSM_BACKEND_DAI_WSA_CDC_DMA_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new wsa_cdc_dma_rx_1_mixer_controls[] = { + SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM, + MSM_BACKEND_DAI_WSA_CDC_DMA_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM, + MSM_BACKEND_DAI_WSA_CDC_DMA_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM, + MSM_BACKEND_DAI_WSA_CDC_DMA_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM, + MSM_BACKEND_DAI_WSA_CDC_DMA_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM, + MSM_BACKEND_DAI_WSA_CDC_DMA_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM, + MSM_BACKEND_DAI_WSA_CDC_DMA_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM, + MSM_BACKEND_DAI_WSA_CDC_DMA_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM, + MSM_BACKEND_DAI_WSA_CDC_DMA_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM, + MSM_BACKEND_DAI_WSA_CDC_DMA_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM, + MSM_BACKEND_DAI_WSA_CDC_DMA_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM, + MSM_BACKEND_DAI_WSA_CDC_DMA_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM, + MSM_BACKEND_DAI_WSA_CDC_DMA_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM, + MSM_BACKEND_DAI_WSA_CDC_DMA_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM, + MSM_BACKEND_DAI_WSA_CDC_DMA_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM, + MSM_BACKEND_DAI_WSA_CDC_DMA_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM, + MSM_BACKEND_DAI_WSA_CDC_DMA_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia26", SND_SOC_NOPM, + MSM_BACKEND_DAI_WSA_CDC_DMA_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new rx_cdc_dma_rx_0_mixer_controls[] = { + SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia26", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new rx_cdc_dma_rx_1_mixer_controls[] = { + SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia26", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new rx_cdc_dma_rx_2_mixer_controls[] = { + SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia26", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new rx_cdc_dma_rx_3_mixer_controls[] = { + SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia26", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new rx_cdc_dma_rx_4_mixer_controls[] = { + SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_4, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_4, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_4, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_4, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_4, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_4, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_4, + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_4, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_4, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_4, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_4, + MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_4, + MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_4, + MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_4, + MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_4, + MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_4, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia26", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_4, + MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new rx_cdc_dma_rx_5_mixer_controls[] = { + SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_5, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_5, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_5, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_5, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_5, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_5, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_5, + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_5, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_5, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_5, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_5, + MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_5, + MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_5, + MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_5, + MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_5, + MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_5, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia26", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_5, + MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new rx_cdc_dma_rx_6_mixer_controls[] = { + SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_6, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_6, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_6, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_6, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_6, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_6, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_6, + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_6, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_6, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_6, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_6, + MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_6, + MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_6, + MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_6, + MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_6, + MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_6, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia26", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_6, + MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new rx_cdc_dma_rx_7_mixer_controls[] = { + SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_7, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_7, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_7, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_7, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_7, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_7, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_7, + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_7, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_7, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_7, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_7, + MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_7, + MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_7, + MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_7, + MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_7, + MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_7, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia26", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_7, + MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new slimbus_7_rx_mixer_controls[] = { + SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_7_RX, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_7_RX, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_7_RX, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_7_RX, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_7_RX, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_7_RX, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_7_RX, + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_7_RX, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_7_RX, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_7_RX, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_7_RX, + MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_7_RX, + MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_7_RX, + MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_7_RX, + MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_7_RX, + MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_7_RX, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia26", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_7_RX, + MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new slimbus_9_rx_mixer_controls[] = { + SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_9_RX, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_9_RX, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_9_RX, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_9_RX, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_9_RX, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_9_RX, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_9_RX, + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_9_RX, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_9_RX, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_9_RX, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_9_RX, + MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_9_RX, + MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_9_RX, + MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_9_RX, + MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_9_RX, + MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_9_RX, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia26", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_9_RX, + MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; +static const struct snd_kcontrol_new usb_audio_rx_mixer_controls[] = { + SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM, + MSM_BACKEND_DAI_USB_RX, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM, + MSM_BACKEND_DAI_USB_RX, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM, + MSM_BACKEND_DAI_USB_RX, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM, + MSM_BACKEND_DAI_USB_RX, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM, + MSM_BACKEND_DAI_USB_RX, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM, + MSM_BACKEND_DAI_USB_RX, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM, + MSM_BACKEND_DAI_USB_RX, + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM, + MSM_BACKEND_DAI_USB_RX, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM, + MSM_BACKEND_DAI_USB_RX, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM, + MSM_BACKEND_DAI_USB_RX, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM, + MSM_BACKEND_DAI_USB_RX, + MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM, + MSM_BACKEND_DAI_USB_RX, + MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM, + MSM_BACKEND_DAI_USB_RX, + MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM, + MSM_BACKEND_DAI_USB_RX, + MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM, + MSM_BACKEND_DAI_USB_RX, + MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM, + MSM_BACKEND_DAI_USB_RX, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia26", SND_SOC_NOPM, + MSM_BACKEND_DAI_USB_RX, + MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new int_bt_sco_rx_mixer_controls[] = { + SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT_BT_SCO_RX, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT_BT_SCO_RX, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT_BT_SCO_RX, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT_BT_SCO_RX, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT_BT_SCO_RX, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT_BT_SCO_RX, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT_BT_SCO_RX, + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT_BT_SCO_RX, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT_BT_SCO_RX, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT_BT_SCO_RX, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT_BT_SCO_RX, + MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT_BT_SCO_RX, + MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT_BT_SCO_RX, + MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT_BT_SCO_RX, + MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT_BT_SCO_RX, + MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT_BT_SCO_RX, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia17", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT_BT_SCO_RX, + MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia18", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT_BT_SCO_RX, + MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia19", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT_BT_SCO_RX, + MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia26", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT_BT_SCO_RX, + MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia28", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT_BT_SCO_RX, + MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia29", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT_BT_SCO_RX, + MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new int_bt_a2dp_rx_mixer_controls[] = { + SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT_BT_A2DP_RX, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT_BT_A2DP_RX, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT_BT_A2DP_RX, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT_BT_A2DP_RX, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT_BT_A2DP_RX, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT_BT_A2DP_RX, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT_BT_A2DP_RX, + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT_BT_A2DP_RX, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT_BT_A2DP_RX, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT_BT_A2DP_RX, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT_BT_A2DP_RX, + MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT_BT_A2DP_RX, + MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT_BT_A2DP_RX, + MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT_BT_A2DP_RX, + MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT_BT_A2DP_RX, + MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT_BT_A2DP_RX, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia26", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT_BT_A2DP_RX, + MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new int_fm_rx_mixer_controls[] = { + SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT_FM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT_FM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT_FM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT_FM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT_FM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT_FM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT_FM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT_FM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT_FM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT_FM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT_FM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT_FM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT_FM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT_FM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT_FM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT_FM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia17", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT_FM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia18", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT_FM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia19", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT_FM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia26", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT_FM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia28", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT_FM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia29", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT_FM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new afe_pcm_rx_mixer_controls[] = { + SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM, + MSM_BACKEND_DAI_AFE_PCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM, + MSM_BACKEND_DAI_AFE_PCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM, + MSM_BACKEND_DAI_AFE_PCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM, + MSM_BACKEND_DAI_AFE_PCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM, + MSM_BACKEND_DAI_AFE_PCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM, + MSM_BACKEND_DAI_AFE_PCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM, + MSM_BACKEND_DAI_AFE_PCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM, + MSM_BACKEND_DAI_AFE_PCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM, + MSM_BACKEND_DAI_AFE_PCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM, + MSM_BACKEND_DAI_AFE_PCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM, + MSM_BACKEND_DAI_AFE_PCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM, + MSM_BACKEND_DAI_AFE_PCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM, + MSM_BACKEND_DAI_AFE_PCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM, + MSM_BACKEND_DAI_AFE_PCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM, + MSM_BACKEND_DAI_AFE_PCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM, + MSM_BACKEND_DAI_AFE_PCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia17", SND_SOC_NOPM, + MSM_BACKEND_DAI_AFE_PCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia18", SND_SOC_NOPM, + MSM_BACKEND_DAI_AFE_PCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia19", SND_SOC_NOPM, + MSM_BACKEND_DAI_AFE_PCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia26", SND_SOC_NOPM, + MSM_BACKEND_DAI_AFE_PCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia28", SND_SOC_NOPM, + MSM_BACKEND_DAI_AFE_PCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia29", SND_SOC_NOPM, + MSM_BACKEND_DAI_AFE_PCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new auxpcm_rx_mixer_controls[] = { + SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM, + MSM_BACKEND_DAI_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM, + MSM_BACKEND_DAI_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM, + MSM_BACKEND_DAI_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM, + MSM_BACKEND_DAI_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM, + MSM_BACKEND_DAI_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM, + MSM_BACKEND_DAI_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM, + MSM_BACKEND_DAI_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM, + MSM_BACKEND_DAI_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM, + MSM_BACKEND_DAI_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM, + MSM_BACKEND_DAI_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM, + MSM_BACKEND_DAI_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM, + MSM_BACKEND_DAI_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM, + MSM_BACKEND_DAI_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM, + MSM_BACKEND_DAI_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM, + MSM_BACKEND_DAI_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM, + MSM_BACKEND_DAI_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia17", SND_SOC_NOPM, + MSM_BACKEND_DAI_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia18", SND_SOC_NOPM, + MSM_BACKEND_DAI_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia19", SND_SOC_NOPM, + MSM_BACKEND_DAI_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia21", SND_SOC_NOPM, + MSM_BACKEND_DAI_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia26", SND_SOC_NOPM, + MSM_BACKEND_DAI_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia28", SND_SOC_NOPM, + MSM_BACKEND_DAI_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia29", SND_SOC_NOPM, + MSM_BACKEND_DAI_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new sec_auxpcm_rx_mixer_controls[] = { + SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia17", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia18", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia19", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia21", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia26", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia28", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia29", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new tert_auxpcm_rx_mixer_controls[] = { + SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia26", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new quat_auxpcm_rx_mixer_controls[] = { + SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia26", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new quin_auxpcm_rx_mixer_controls[] = { + SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia26", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; +static const struct snd_kcontrol_new pri_tdm_rx_0_mixer_controls[] = { + SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia21", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new pri_tdm_rx_1_mixer_controls[] = { + SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia21", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new pri_tdm_rx_2_mixer_controls[] = { + SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia21", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new pri_tdm_rx_3_mixer_controls[] = { + SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia21", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new pri_tdm_tx_0_mixer_controls[] = { + SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new sec_tdm_rx_0_mixer_controls[] = { + SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia21", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new sec_tdm_rx_1_mixer_controls[] = { + SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia21", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new sec_tdm_rx_2_mixer_controls[] = { + SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia21", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new sec_tdm_rx_3_mixer_controls[] = { + SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia21", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new sec_tdm_tx_0_mixer_controls[] = { + SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new tert_tdm_rx_0_mixer_controls[] = { + SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia21", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new tert_tdm_tx_0_mixer_controls[] = { + SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new tert_tdm_rx_1_mixer_controls[] = { + SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia21", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new tert_tdm_rx_2_mixer_controls[] = { + SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia21", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new tert_tdm_rx_3_mixer_controls[] = { + SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia21", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new tert_tdm_rx_4_mixer_controls[] = { + SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_4, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_4, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_4, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_4, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_4, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_4, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_4, + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_4, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_4, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_4, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_4, + MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_4, + MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_4, + MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_4, + MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_4, + MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_4, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia21", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_4, + MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new quat_tdm_rx_0_mixer_controls[] = { + SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia20", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA20, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia21", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia30", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA30, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia31", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA31, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new quat_tdm_tx_0_mixer_controls[] = { + SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new quat_tdm_rx_1_mixer_controls[] = { + SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia20", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA20, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia21", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia30", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA30, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia31", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA31, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new quat_tdm_rx_2_mixer_controls[] = { + SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia20", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA20, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia21", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia30", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA30, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia31", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA31, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new quat_tdm_rx_3_mixer_controls[] = { + SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia20", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA20, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia21", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia30", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA30, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia31", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA31, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new quin_tdm_rx_0_mixer_controls[] = { + SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia20", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA20, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia21", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new quin_tdm_tx_0_mixer_controls[] = { + SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new quin_tdm_rx_1_mixer_controls[] = { + SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia20", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA20, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia21", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new quin_tdm_rx_2_mixer_controls[] = { + SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia20", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA20, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia21", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new quin_tdm_rx_3_mixer_controls[] = { + SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia20", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA20, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia21", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new mmul1_mixer_controls[] = { + SOC_DOUBLE_EXT("PRI_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_I2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("PRI_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("QUAT_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TERT_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERTIARY_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("INT2_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT2_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("INT3_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT3_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("SEC_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SECONDARY_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("SLIM_0_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_0_TX, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("AUX_PCM_UL_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_AUXPCM_TX, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("SEC_AUX_PCM_UL_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_AUXPCM_TX, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TERT_AUXPCM_UL_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_AUXPCM_TX, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("QUAT_AUXPCM_UL_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_AUXPCM_TX, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("QUIN_AUXPCM_UL_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_AUXPCM_TX, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("INTERNAL_BT_SCO_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT_BT_SCO_TX, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("INTERNAL_FM_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT_FM_TX, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("AFE_PCM_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_AFE_PCM_TX, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("VOC_REC_DL", SND_SOC_NOPM, + MSM_BACKEND_DAI_INCALL_RECORD_RX, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("VOC_REC_UL", SND_SOC_NOPM, + MSM_BACKEND_DAI_INCALL_RECORD_TX, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("SLIM_4_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_4_TX, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("SLIM_6_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_6_TX, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("QUIN_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUINARY_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("PRI_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("PRI_TDM_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("PRI_TDM_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("PRI_TDM_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_TX_3, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("SEC_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("SEC_TDM_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("SEC_TDM_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("SEC_TDM_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_TX_3, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TERT_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TERT_TDM_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TERT_TDM_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TERT_TDM_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_TX_3, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_TX_3, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_TX_3, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("SLIM_7_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_7_TX, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("SLIM_8_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_8_TX, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("USB_AUDIO_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_USB_TX, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("WSA_CDC_DMA_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_WSA_CDC_DMA_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("WSA_CDC_DMA_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_WSA_CDC_DMA_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("WSA_CDC_DMA_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_WSA_CDC_DMA_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("VA_CDC_DMA_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_VA_CDC_DMA_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("VA_CDC_DMA_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_VA_CDC_DMA_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_3, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_4", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_4, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_5", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_5, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("PRI_SPDIF_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_SPDIF_TX, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("SEC_SPDIF_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_SPDIF_TX, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("SLIM_9_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_9_TX, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new mmul2_mixer_controls[] = { + SOC_DOUBLE_EXT("INTERNAL_FM_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT_FM_TX, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("PRI_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("SEC_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SECONDARY_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TERT_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERTIARY_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("INT2_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT2_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("INT3_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT3_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("QUAT_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("SLIM_0_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_0_TX, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("SLIM_6_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_6_TX, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("SLIM_1_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_1_TX, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("QUIN_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUINARY_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("PRI_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("PRI_TDM_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("PRI_TDM_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("PRI_TDM_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_TX_3, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("SEC_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("SEC_TDM_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("SEC_TDM_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("SEC_TDM_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_TX_3, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TERT_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TERT_TDM_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TERT_TDM_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TERT_TDM_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_TX_3, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_TX_3, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_TX_3, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("SLIM_8_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_8_TX, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("USB_AUDIO_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_USB_TX, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("WSA_CDC_DMA_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_WSA_CDC_DMA_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("WSA_CDC_DMA_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_WSA_CDC_DMA_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("WSA_CDC_DMA_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_WSA_CDC_DMA_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("VA_CDC_DMA_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_VA_CDC_DMA_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("VA_CDC_DMA_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_VA_CDC_DMA_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_3, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_4", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_4, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_5", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_5, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("PRI_SPDIF_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_SPDIF_TX, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("SEC_SPDIF_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_SPDIF_TX, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("SLIM_9_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_9_TX, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new mmul3_mixer_controls[] = { + SOC_DOUBLE_EXT("SLIM_0_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_0_TX, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("INTERNAL_FM_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT_FM_TX, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("AFE_PCM_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_AFE_PCM_TX, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("INTERNAL_BT_SCO_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT_BT_SCO_TX, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("AUX_PCM_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_AUXPCM_TX, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("SEC_AUX_PCM_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_AUXPCM_TX, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TERT_AUX_PCM_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_AUXPCM_TX, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("QUAT_AUX_PCM_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_AUXPCM_TX, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("QUIN_AUX_PCM_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_AUXPCM_TX, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("PRI_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TERT_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERTIARY_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("INT2_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT2_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("INT3_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT3_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("PRI_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("PRI_TDM_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("PRI_TDM_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("PRI_TDM_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_TX_3, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("SEC_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("SEC_TDM_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("SEC_TDM_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("SEC_TDM_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_TX_3, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TERT_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TERT_TDM_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TERT_TDM_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TERT_TDM_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_TX_3, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_TX_3, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_TX_3, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("WSA_CDC_DMA_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_WSA_CDC_DMA_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("WSA_CDC_DMA_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_WSA_CDC_DMA_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("WSA_CDC_DMA_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_WSA_CDC_DMA_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("VA_CDC_DMA_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_VA_CDC_DMA_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("VA_CDC_DMA_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_VA_CDC_DMA_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_3, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_4", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_4, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_5", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_5, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("PRI_SPDIF_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_SPDIF_TX, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("SEC_SPDIF_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_SPDIF_TX, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new mmul4_mixer_controls[] = { + SOC_DOUBLE_EXT("SLIM_0_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_0_TX, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("PRI_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("INTERNAL_FM_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT_FM_TX, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("INTERNAL_BT_SCO_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT_BT_SCO_TX, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("AFE_PCM_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_AFE_PCM_TX, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("VOC_REC_DL", SND_SOC_NOPM, + MSM_BACKEND_DAI_INCALL_RECORD_RX, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("VOC_REC_UL", SND_SOC_NOPM, + MSM_BACKEND_DAI_INCALL_RECORD_TX, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TERT_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("PRI_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("PRI_TDM_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("PRI_TDM_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("PRI_TDM_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_TX_3, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("SEC_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("SEC_TDM_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("SEC_TDM_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("SEC_TDM_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_TX_3, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TERT_TDM_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TERT_TDM_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TERT_TDM_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_TX_3, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_TX_3, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_TX_3, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("SEC_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SECONDARY_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TERT_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERTIARY_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("INT2_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT2_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("INT3_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT3_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("QUAT_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("USB_AUDIO_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_USB_TX, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("WSA_CDC_DMA_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_WSA_CDC_DMA_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("WSA_CDC_DMA_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_WSA_CDC_DMA_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("WSA_CDC_DMA_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_WSA_CDC_DMA_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("VA_CDC_DMA_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_VA_CDC_DMA_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("VA_CDC_DMA_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_VA_CDC_DMA_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_3, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_4", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_4, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_5", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_5, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("PRI_SPDIF_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_SPDIF_TX, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("SEC_SPDIF_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_SPDIF_TX, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new mmul5_mixer_controls[] = { + SOC_DOUBLE_EXT("SLIM_0_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_0_TX, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("INTERNAL_FM_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT_FM_TX, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("AFE_PCM_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_AFE_PCM_TX, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("INTERNAL_BT_SCO_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT_BT_SCO_TX, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("AUX_PCM_UL_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_AUXPCM_TX, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("SEC_AUX_PCM_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_AUXPCM_TX, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TERT_AUX_PCM_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_AUXPCM_TX, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("QUAT_AUX_PCM_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_AUXPCM_TX, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("QUIN_AUX_PCM_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_AUXPCM_TX, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("PRI_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TERT_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERTIARY_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("INT2_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT2_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("INT3_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT3_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("PRI_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("PRI_TDM_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("PRI_TDM_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("PRI_TDM_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_TX_3, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("SEC_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("SEC_TDM_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("SEC_TDM_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("SEC_TDM_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_TX_3, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TERT_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TERT_TDM_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TERT_TDM_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TERT_TDM_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_TX_3, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_TX_3, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_TX_3, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("SEC_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SECONDARY_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("QUAT_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("SLIM_7_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_7_TX, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("SLIM_8_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_8_TX, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("USB_AUDIO_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_USB_TX, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("WSA_CDC_DMA_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_WSA_CDC_DMA_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("WSA_CDC_DMA_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_WSA_CDC_DMA_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("WSA_CDC_DMA_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_WSA_CDC_DMA_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("VA_CDC_DMA_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_VA_CDC_DMA_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("VA_CDC_DMA_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_VA_CDC_DMA_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_3, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_4", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_4, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_5", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_5, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("PRI_SPDIF_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_SPDIF_TX, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("SEC_SPDIF_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_SPDIF_TX, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("SLIM_9_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_9_TX, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new mmul6_mixer_controls[] = { + SOC_DOUBLE_EXT("INTERNAL_FM_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT_FM_TX, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("SLIM_0_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_0_TX, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("PRI_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("SEC_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SECONDARY_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TERT_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERTIARY_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("INT2_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT2_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("INT3_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT3_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("QUAT_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("QUIN_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUINARY_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("AUX_PCM_UL_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_AUXPCM_TX, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("SEC_AUX_PCM_UL_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_AUXPCM_TX, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TERT_AUXPCM_UL_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_AUXPCM_TX, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("QUAT_AUXPCM_UL_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_AUXPCM_TX, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("PRI_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("PRI_TDM_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("PRI_TDM_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("PRI_TDM_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_TX_3, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("SEC_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("SEC_TDM_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("SEC_TDM_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("SEC_TDM_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_TX_3, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TERT_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TERT_TDM_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TERT_TDM_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TERT_TDM_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_TX_3, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_TX_3, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_TX_3, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("USB_AUDIO_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_USB_TX, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("WSA_CDC_DMA_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_WSA_CDC_DMA_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("WSA_CDC_DMA_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_WSA_CDC_DMA_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("WSA_CDC_DMA_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_WSA_CDC_DMA_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("VA_CDC_DMA_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_VA_CDC_DMA_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("VA_CDC_DMA_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_VA_CDC_DMA_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_3, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_4", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_4, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_5", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_5, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("PRI_SPDIF_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_SPDIF_TX, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("SEC_SPDIF_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_SPDIF_TX, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new mmul8_mixer_controls[] = { + SOC_DOUBLE_EXT("SLIM_0_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_0_TX, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("PRI_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("SEC_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SECONDARY_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TERT_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERTIARY_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("INT2_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT2_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("INT3_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT3_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("QUAT_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("INTERNAL_FM_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT_FM_TX, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("INTERNAL_BT_SCO_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT_BT_SCO_TX, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("AFE_PCM_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_AFE_PCM_TX, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("VOC_REC_DL", SND_SOC_NOPM, + MSM_BACKEND_DAI_INCALL_RECORD_RX, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("VOC_REC_UL", SND_SOC_NOPM, + MSM_BACKEND_DAI_INCALL_RECORD_TX, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("SLIM_6_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_6_TX, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("PRI_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("PRI_TDM_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("PRI_TDM_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("PRI_TDM_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_TX_3, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("SEC_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("SEC_TDM_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("SEC_TDM_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("SEC_TDM_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_TX_3, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TERT_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TERT_TDM_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TERT_TDM_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TERT_TDM_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_TX_3, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_TX_3, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_TX_3, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("SLIM_7_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_7_TX, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("USB_AUDIO_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_USB_TX, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("WSA_CDC_DMA_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_WSA_CDC_DMA_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("WSA_CDC_DMA_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_WSA_CDC_DMA_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("WSA_CDC_DMA_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_WSA_CDC_DMA_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("VA_CDC_DMA_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_VA_CDC_DMA_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("VA_CDC_DMA_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_VA_CDC_DMA_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_3, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_4", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_4, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_5", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_5, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("PRI_SPDIF_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_SPDIF_TX, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("SEC_SPDIF_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_SPDIF_TX, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("SLIM_9_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_9_TX, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new mmul16_mixer_controls[] = { + SOC_DOUBLE_EXT("SLIM_0_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_0_TX, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("PRI_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("SEC_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SECONDARY_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TERT_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERTIARY_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("INT2_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT2_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("INT3_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT3_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("QUAT_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("INTERNAL_FM_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT_FM_TX, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("INTERNAL_BT_SCO_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT_BT_SCO_TX, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("AFE_PCM_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_AFE_PCM_TX, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("VOC_REC_DL", SND_SOC_NOPM, + MSM_BACKEND_DAI_INCALL_RECORD_RX, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("VOC_REC_UL", SND_SOC_NOPM, + MSM_BACKEND_DAI_INCALL_RECORD_TX, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("SLIM_6_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_6_TX, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("PRI_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("PRI_TDM_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("PRI_TDM_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("PRI_TDM_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_TX_3, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("SEC_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("SEC_TDM_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("SEC_TDM_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("SEC_TDM_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_TX_3, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TERT_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TERT_TDM_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TERT_TDM_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TERT_TDM_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_TX_3, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_TX_3, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("SLIM_7_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_7_TX, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("USB_AUDIO_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_USB_TX, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("AUX_PCM_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_AUXPCM_TX, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("SEC_AUX_PCM_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_AUXPCM_TX, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("QUAT_AUX_PCM_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_AUXPCM_TX, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("WSA_CDC_DMA_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_WSA_CDC_DMA_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("WSA_CDC_DMA_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_WSA_CDC_DMA_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("WSA_CDC_DMA_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_WSA_CDC_DMA_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("VA_CDC_DMA_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_VA_CDC_DMA_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("VA_CDC_DMA_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_VA_CDC_DMA_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, + msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, + msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, + msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_3, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, + msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_4", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_4, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, + msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_5", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_5, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, + msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("PRI_SPDIF_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_SPDIF_TX, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("SEC_SPDIF_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_SPDIF_TX, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("SLIM_9_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_9_TX, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new mmul9_mixer_controls[] = { + SOC_DOUBLE_EXT("SLIM_0_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_0_TX, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("PRI_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("INTERNAL_FM_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT_FM_TX, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("INTERNAL_BT_SCO_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT_BT_SCO_TX, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("AFE_PCM_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_AFE_PCM_TX, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("VOC_REC_DL", SND_SOC_NOPM, + MSM_BACKEND_DAI_INCALL_RECORD_RX, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("VOC_REC_UL", SND_SOC_NOPM, + MSM_BACKEND_DAI_INCALL_RECORD_TX, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("SLIM_6_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_6_TX, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TERT_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TERT_TDM_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TERT_TDM_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TERT_TDM_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_TX_3, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_TX_3, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_TX_3, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("WSA_CDC_DMA_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_WSA_CDC_DMA_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("WSA_CDC_DMA_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_WSA_CDC_DMA_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("WSA_CDC_DMA_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_WSA_CDC_DMA_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("VA_CDC_DMA_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_VA_CDC_DMA_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("VA_CDC_DMA_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_VA_CDC_DMA_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_3, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_4", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_4, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_5", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_5, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("PRI_SPDIF_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_SPDIF_TX, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("SEC_SPDIF_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_SPDIF_TX, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new mmul10_mixer_controls[] = { + SOC_DOUBLE_EXT("SLIM_0_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_0_TX, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("PRI_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("INTERNAL_FM_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT_FM_TX, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("INTERNAL_BT_SCO_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT_BT_SCO_TX, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("AFE_PCM_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_AFE_PCM_TX, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("VOC_REC_DL", SND_SOC_NOPM, + MSM_BACKEND_DAI_INCALL_RECORD_RX, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("VOC_REC_UL", SND_SOC_NOPM, + MSM_BACKEND_DAI_INCALL_RECORD_TX, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("SLIM_6_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_6_TX, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("SLIM_7_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_7_TX, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TERT_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TERT_TDM_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TERT_TDM_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TERT_TDM_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_TX_3, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_TX_3, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("USB_AUDIO_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_USB_TX, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("AUX_PCM_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_AUXPCM_TX, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("SEC_AUX_PCM_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_AUXPCM_TX, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TERT_AUX_PCM_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_AUXPCM_TX, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("QUAT_AUX_PCM_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_AUXPCM_TX, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TERT_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERTIARY_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("INT2_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT2_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("INT3_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT3_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("WSA_CDC_DMA_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_WSA_CDC_DMA_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("WSA_CDC_DMA_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_WSA_CDC_DMA_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("WSA_CDC_DMA_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_WSA_CDC_DMA_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("VA_CDC_DMA_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_VA_CDC_DMA_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("VA_CDC_DMA_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_VA_CDC_DMA_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, + msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, + msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, + msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_3, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, + msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_4", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_4, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, + msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_5", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_5, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, + msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("PRI_SPDIF_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_SPDIF_TX, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("SEC_SPDIF_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_SPDIF_TX, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("SLIM_9_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_9_TX, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; +static const struct snd_kcontrol_new mmul17_mixer_controls[] = { + SOC_DOUBLE_EXT("SLIM_0_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_0_TX, + MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("PRI_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("INT3_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT3_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("INTERNAL_FM_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT_FM_TX, + MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("INTERNAL_BT_SCO_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT_BT_SCO_TX, + MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("AFE_PCM_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_AFE_PCM_TX, + MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("VOC_REC_DL", SND_SOC_NOPM, + MSM_BACKEND_DAI_INCALL_RECORD_RX, + MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("VOC_REC_UL", SND_SOC_NOPM, + MSM_BACKEND_DAI_INCALL_RECORD_TX, + MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TERT_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERTIARY_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("QUAT_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("WSA_CDC_DMA_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_WSA_CDC_DMA_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("WSA_CDC_DMA_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_WSA_CDC_DMA_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("WSA_CDC_DMA_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_WSA_CDC_DMA_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("VA_CDC_DMA_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_VA_CDC_DMA_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("VA_CDC_DMA_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_VA_CDC_DMA_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, + msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, + msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, + msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_3, + MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, + msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_4", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_4, + MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, + msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_5", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_5, + MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, + msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("PRI_SPDIF_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_SPDIF_TX, + MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("SEC_SPDIF_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_SPDIF_TX, + MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new mmul18_mixer_controls[] = { + SOC_DOUBLE_EXT("SLIM_0_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_0_TX, + MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("PRI_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("INT3_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT3_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("SEC_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SECONDARY_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TERT_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERTIARY_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("QUAT_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("INTERNAL_FM_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT_FM_TX, + MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("INTERNAL_BT_SCO_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT_BT_SCO_TX, + MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("AFE_PCM_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_AFE_PCM_TX, + MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("VOC_REC_DL", SND_SOC_NOPM, + MSM_BACKEND_DAI_INCALL_RECORD_RX, + MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("VOC_REC_UL", SND_SOC_NOPM, + MSM_BACKEND_DAI_INCALL_RECORD_TX, + MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("WSA_CDC_DMA_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_WSA_CDC_DMA_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("WSA_CDC_DMA_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_WSA_CDC_DMA_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("WSA_CDC_DMA_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_WSA_CDC_DMA_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("VA_CDC_DMA_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_VA_CDC_DMA_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("VA_CDC_DMA_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_VA_CDC_DMA_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, + msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, + msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, + msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_3, + MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, + msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_4", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_4, + MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, + msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_5", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_5, + MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, + msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("PRI_SPDIF_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_SPDIF_TX, + MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("SEC_SPDIF_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_SPDIF_TX, + MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new mmul19_mixer_controls[] = { + SOC_DOUBLE_EXT("SLIM_0_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_0_TX, + MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("PRI_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("INT3_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT3_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("INTERNAL_FM_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT_FM_TX, + MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("INTERNAL_BT_SCO_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT_BT_SCO_TX, + MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("AFE_PCM_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_AFE_PCM_TX, + MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("VOC_REC_DL", SND_SOC_NOPM, + MSM_BACKEND_DAI_INCALL_RECORD_RX, + MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("VOC_REC_UL", SND_SOC_NOPM, + MSM_BACKEND_DAI_INCALL_RECORD_TX, + MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TERT_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERTIARY_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("QUAT_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("WSA_CDC_DMA_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_WSA_CDC_DMA_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("WSA_CDC_DMA_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_WSA_CDC_DMA_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("WSA_CDC_DMA_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_WSA_CDC_DMA_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("VA_CDC_DMA_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_VA_CDC_DMA_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("VA_CDC_DMA_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_VA_CDC_DMA_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, + msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, + msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, + msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_3, + MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, + msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_4", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_4, + MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, + msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_5", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_5, + MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, + msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("PRI_SPDIF_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_SPDIF_TX, + MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("SEC_SPDIF_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_SPDIF_TX, + MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new mmul20_mixer_controls[] = { + SOC_DOUBLE_EXT("PRI_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA20, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("SEC_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SECONDARY_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA20, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TERT_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERTIARY_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA20, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("QUAT_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA20, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("QUIN_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUINARY_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA20, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("PRI_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA20, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("PRI_TDM_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA20, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("PRI_TDM_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA20, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("PRI_TDM_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_TX_3, + MSM_FRONTEND_DAI_MULTIMEDIA20, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("SEC_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA20, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("SEC_TDM_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA20, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("SEC_TDM_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA20, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("SEC_TDM_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_TX_3, + MSM_FRONTEND_DAI_MULTIMEDIA20, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TERT_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA20, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TERT_TDM_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA20, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TERT_TDM_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA20, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TERT_TDM_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_TX_3, + MSM_FRONTEND_DAI_MULTIMEDIA20, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA20, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA20, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA20, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_TX_3, + MSM_FRONTEND_DAI_MULTIMEDIA20, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA20, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA20, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA20, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_TX_3, + MSM_FRONTEND_DAI_MULTIMEDIA20, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("WSA_CDC_DMA_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_WSA_CDC_DMA_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA20, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("WSA_CDC_DMA_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_WSA_CDC_DMA_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA20, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("WSA_CDC_DMA_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_WSA_CDC_DMA_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA20, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("VA_CDC_DMA_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_VA_CDC_DMA_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA20, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("VA_CDC_DMA_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_VA_CDC_DMA_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA20, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA20, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA20, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA20, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_3, + MSM_FRONTEND_DAI_MULTIMEDIA20, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_4", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_4, + MSM_FRONTEND_DAI_MULTIMEDIA20, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_5", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_5, + MSM_FRONTEND_DAI_MULTIMEDIA20, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("PRI_SPDIF_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_SPDIF_TX, + MSM_FRONTEND_DAI_MULTIMEDIA20, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("SEC_SPDIF_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_SPDIF_TX, + MSM_FRONTEND_DAI_MULTIMEDIA20, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new mmul21_mixer_controls[] = { + SOC_DOUBLE_EXT("AUX_PCM_UL_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_AUXPCM_TX, + MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("SEC_AUX_PCM_UL_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_AUXPCM_TX, + MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("PRI_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("PRI_TDM_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("PRI_TDM_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("PRI_TDM_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_TX_3, + MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("SEC_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("SEC_TDM_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("SEC_TDM_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("SEC_TDM_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_TX_3, + MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TERT_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TERT_TDM_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TERT_TDM_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TERT_TDM_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_TX_3, + MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_TX_3, + MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_TX_3, + MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("WSA_CDC_DMA_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_WSA_CDC_DMA_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("WSA_CDC_DMA_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_WSA_CDC_DMA_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("WSA_CDC_DMA_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_WSA_CDC_DMA_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("VA_CDC_DMA_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_VA_CDC_DMA_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("VA_CDC_DMA_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_VA_CDC_DMA_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_3, + MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_4", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_4, + MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_5", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_5, + MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("PRI_SPDIF_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_SPDIF_TX, + MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("SEC_SPDIF_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_SPDIF_TX, + MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new mmul27_mixer_controls[] = { + SOC_DOUBLE_EXT("SLIM_0_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_0_TX, + MSM_FRONTEND_DAI_MULTIMEDIA27, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("SLIM_6_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_6_TX, + MSM_FRONTEND_DAI_MULTIMEDIA27, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("SLIM_7_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_7_TX, + MSM_FRONTEND_DAI_MULTIMEDIA27, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("PRI_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA27, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("SEC_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SECONDARY_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA27, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TERT_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERTIARY_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA27, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("QUAT_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA27, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("QUIN_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUINARY_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA27, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("PRI_SPDIF_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_SPDIF_TX, + MSM_FRONTEND_DAI_MULTIMEDIA27, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("SEC_SPDIF_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_SPDIF_TX, + MSM_FRONTEND_DAI_MULTIMEDIA27, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("SLIM_9_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_9_TX, + MSM_FRONTEND_DAI_MULTIMEDIA27, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new mmul28_mixer_controls[] = { + SOC_DOUBLE_EXT("SLIM_0_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_0_TX, + MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("PRI_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("INT3_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT3_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("INTERNAL_FM_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT_FM_TX, + MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("INTERNAL_BT_SCO_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT_BT_SCO_TX, + MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("AFE_PCM_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_AFE_PCM_TX, + MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("VOC_REC_DL", SND_SOC_NOPM, + MSM_BACKEND_DAI_INCALL_RECORD_RX, + MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("VOC_REC_UL", SND_SOC_NOPM, + MSM_BACKEND_DAI_INCALL_RECORD_TX, + MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TERT_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERTIARY_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("QUAT_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("WSA_CDC_DMA_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_WSA_CDC_DMA_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("WSA_CDC_DMA_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_WSA_CDC_DMA_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("WSA_CDC_DMA_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_WSA_CDC_DMA_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("VA_CDC_DMA_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_VA_CDC_DMA_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("VA_CDC_DMA_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_VA_CDC_DMA_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, + msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, + msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, + msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_3, + MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, + msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_4", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_4, + MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, + msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_5", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_5, + MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, + msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("PRI_SPDIF_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_SPDIF_TX, + MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("SEC_SPDIF_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_SPDIF_TX, + MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new mmul29_mixer_controls[] = { + SOC_DOUBLE_EXT("SLIM_0_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_0_TX, + MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("PRI_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("INT3_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT3_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("INTERNAL_FM_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT_FM_TX, + MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("INTERNAL_BT_SCO_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT_BT_SCO_TX, + MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("AFE_PCM_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_AFE_PCM_TX, + MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("VOC_REC_DL", SND_SOC_NOPM, + MSM_BACKEND_DAI_INCALL_RECORD_RX, + MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("VOC_REC_UL", SND_SOC_NOPM, + MSM_BACKEND_DAI_INCALL_RECORD_TX, + MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TERT_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERTIARY_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("QUAT_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("WSA_CDC_DMA_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_WSA_CDC_DMA_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("WSA_CDC_DMA_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_WSA_CDC_DMA_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("WSA_CDC_DMA_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_WSA_CDC_DMA_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("VA_CDC_DMA_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_VA_CDC_DMA_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("VA_CDC_DMA_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_VA_CDC_DMA_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, + msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, + msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, + msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_3, + MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, + msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_4", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_4, + MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, + msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_5", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_5, + MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, + msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("PRI_SPDIF_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_SPDIF_TX, + MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("SEC_SPDIF_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_SPDIF_TX, + MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new pri_rx_voice_mixer_controls[] = { + SOC_DOUBLE_EXT("Voip", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_I2S_RX, + MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("VoiceMMode1", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_I2S_RX, + MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("VoiceMMode2", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_I2S_RX, + MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("DTMF", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_I2S_RX, + MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("QCHAT", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_I2S_RX, + MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), +}; + +static const struct snd_kcontrol_new sec_i2s_rx_voice_mixer_controls[] = { + SOC_DOUBLE_EXT("Voip", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_I2S_RX, + MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("DTMF", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_I2S_RX, + MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("QCHAT", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_I2S_RX, + MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), +}; + +static const struct snd_kcontrol_new sec_mi2s_rx_voice_mixer_controls[] = { + SOC_DOUBLE_EXT("Voip", SND_SOC_NOPM, + MSM_BACKEND_DAI_SECONDARY_MI2S_RX, + MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("DTMF", SND_SOC_NOPM, + MSM_BACKEND_DAI_SECONDARY_MI2S_RX, + MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("QCHAT", SND_SOC_NOPM, + MSM_BACKEND_DAI_SECONDARY_MI2S_RX, + MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("VoiceMMode1", SND_SOC_NOPM, + MSM_BACKEND_DAI_SECONDARY_MI2S_RX, + MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("VoiceMMode2", SND_SOC_NOPM, + MSM_BACKEND_DAI_SECONDARY_MI2S_RX, + MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), +}; + +static const struct snd_kcontrol_new slimbus_rx_voice_mixer_controls[] = { + SOC_DOUBLE_EXT("Voip", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_0_RX, + MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("DTMF", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_0_RX, + MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("QCHAT", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_0_RX, + MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("VoiceMMode1", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_0_RX, + MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("VoiceMMode2", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_0_RX, + MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), +}; + +static const struct snd_kcontrol_new slimbus_6_rx_voice_mixer_controls[] = { + SOC_DOUBLE_EXT("Voip", SND_SOC_NOPM, +MSM_BACKEND_DAI_SLIMBUS_6_RX, + MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("DTMF", SND_SOC_NOPM, +MSM_BACKEND_DAI_SLIMBUS_6_RX, + MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("QCHAT", SND_SOC_NOPM, +MSM_BACKEND_DAI_SLIMBUS_6_RX, + MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("VoiceMMode1", SND_SOC_NOPM, +MSM_BACKEND_DAI_SLIMBUS_6_RX, + MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("VoiceMMode2", SND_SOC_NOPM, +MSM_BACKEND_DAI_SLIMBUS_6_RX, + MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), +}; + +static const struct snd_kcontrol_new usb_audio_rx_voice_mixer_controls[] = { + SOC_DOUBLE_EXT("Voip", SND_SOC_NOPM, + MSM_BACKEND_DAI_USB_RX, + MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("DTMF", SND_SOC_NOPM, + MSM_BACKEND_DAI_USB_RX, + MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("QCHAT", SND_SOC_NOPM, + MSM_BACKEND_DAI_USB_RX, + MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("VoiceMMode1", SND_SOC_NOPM, + MSM_BACKEND_DAI_USB_RX, + MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("VoiceMMode2", SND_SOC_NOPM, + MSM_BACKEND_DAI_USB_RX, + MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), +}; + +static const struct snd_kcontrol_new display_port_rx_voice_mixer_controls[] = { + SOC_DOUBLE_EXT("Voip", SND_SOC_NOPM, +MSM_BACKEND_DAI_DISPLAY_PORT_RX, + MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("DTMF", SND_SOC_NOPM, +MSM_BACKEND_DAI_DISPLAY_PORT_RX, + MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("QCHAT", SND_SOC_NOPM, +MSM_BACKEND_DAI_DISPLAY_PORT_RX, + MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("VoiceMMode1", SND_SOC_NOPM, +MSM_BACKEND_DAI_DISPLAY_PORT_RX, + MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("VoiceMMode2", SND_SOC_NOPM, +MSM_BACKEND_DAI_DISPLAY_PORT_RX, + MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), +}; + +static const struct snd_kcontrol_new display_port_rx1_voice_mixer_controls[] = { + SOC_DOUBLE_EXT("Voip", SND_SOC_NOPM, + MSM_BACKEND_DAI_DISPLAY_PORT_RX_1, + MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("DTMF", SND_SOC_NOPM, + MSM_BACKEND_DAI_DISPLAY_PORT_RX_1, + MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("QCHAT", SND_SOC_NOPM, + MSM_BACKEND_DAI_DISPLAY_PORT_RX_1, + MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("VoiceMMode1", SND_SOC_NOPM, + MSM_BACKEND_DAI_DISPLAY_PORT_RX_1, + MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("VoiceMMode2", SND_SOC_NOPM, + MSM_BACKEND_DAI_DISPLAY_PORT_RX_1, + MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), +}; + +static const struct snd_kcontrol_new bt_sco_rx_voice_mixer_controls[] = { + SOC_DOUBLE_EXT("Voip", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT_BT_SCO_RX, + MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("DTMF", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT_BT_SCO_RX, + MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("QCHAT", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT_BT_SCO_RX, + MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("VoiceMMode1", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT_BT_SCO_RX, + MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("VoiceMMode2", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT_BT_SCO_RX, + MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), +}; + +static const struct snd_kcontrol_new mi2s_rx_voice_mixer_controls[] = { + SOC_DOUBLE_EXT("Voip", SND_SOC_NOPM, + MSM_BACKEND_DAI_MI2S_RX, + MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("DTMF", SND_SOC_NOPM, + MSM_BACKEND_DAI_MI2S_RX, + MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("QCHAT", SND_SOC_NOPM, + MSM_BACKEND_DAI_MI2S_RX, + MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("VoiceMMode1", SND_SOC_NOPM, + MSM_BACKEND_DAI_MI2S_RX, + MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("VoiceMMode2", SND_SOC_NOPM, + MSM_BACKEND_DAI_MI2S_RX, + MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), +}; + +static const struct snd_kcontrol_new pri_mi2s_rx_voice_mixer_controls[] = { + SOC_DOUBLE_EXT("Voip", SND_SOC_NOPM, +MSM_BACKEND_DAI_PRI_MI2S_RX, + MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("DTMF", SND_SOC_NOPM, +MSM_BACKEND_DAI_PRI_MI2S_RX, + MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("QCHAT", SND_SOC_NOPM, +MSM_BACKEND_DAI_PRI_MI2S_RX, + MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("VoiceMMode1", SND_SOC_NOPM, +MSM_BACKEND_DAI_PRI_MI2S_RX, + MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("VoiceMMode2", SND_SOC_NOPM, +MSM_BACKEND_DAI_PRI_MI2S_RX, + MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), +}; + +static const struct snd_kcontrol_new int0_mi2s_rx_voice_mixer_controls[] = { + SOC_DOUBLE_EXT("Voip", SND_SOC_NOPM, +MSM_BACKEND_DAI_INT0_MI2S_RX, + MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("DTMF", SND_SOC_NOPM, +MSM_BACKEND_DAI_INT0_MI2S_RX, + MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("QCHAT", SND_SOC_NOPM, +MSM_BACKEND_DAI_INT0_MI2S_RX, + MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("VoiceMMode1", SND_SOC_NOPM, +MSM_BACKEND_DAI_INT0_MI2S_RX, + MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("VoiceMMode2", SND_SOC_NOPM, +MSM_BACKEND_DAI_INT0_MI2S_RX, + MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), +}; + +static const struct snd_kcontrol_new int4_mi2s_rx_voice_mixer_controls[] = { + SOC_DOUBLE_EXT("Voip", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT4_MI2S_RX, + MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("DTMF", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT4_MI2S_RX, + MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("QCHAT", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT4_MI2S_RX, + MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("VoiceMMode1", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT4_MI2S_RX, + MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("VoiceMMode2", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT4_MI2S_RX, + MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), +}; + +static const struct snd_kcontrol_new tert_mi2s_rx_voice_mixer_controls[] = { + SOC_DOUBLE_EXT("Voip", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERTIARY_MI2S_RX, + MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("DTMF", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERTIARY_MI2S_RX, + MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("QCHAT", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERTIARY_MI2S_RX, + MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("VoiceMMode1", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERTIARY_MI2S_RX, + MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("VoiceMMode2", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERTIARY_MI2S_RX, + MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), +}; + +static const struct snd_kcontrol_new quat_mi2s_rx_voice_mixer_controls[] = { + SOC_DOUBLE_EXT("Voip", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUATERNARY_MI2S_RX, + MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("DTMF", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUATERNARY_MI2S_RX, + MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("QCHAT", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUATERNARY_MI2S_RX, + MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("VoiceMMode1", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUATERNARY_MI2S_RX, + MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("VoiceMMode2", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUATERNARY_MI2S_RX, + MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), +}; + +static const struct snd_kcontrol_new quin_mi2s_rx_voice_mixer_controls[] = { + SOC_DOUBLE_EXT("Voip", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUINARY_MI2S_RX, + MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("DTMF", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUINARY_MI2S_RX, + MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("QCHAT", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUINARY_MI2S_RX, + MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("VoiceMMode1", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUINARY_MI2S_RX, + MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("VoiceMMode2", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUINARY_MI2S_RX, + MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), +}; + +static const struct snd_kcontrol_new afe_pcm_rx_voice_mixer_controls[] = { + SOC_DOUBLE_EXT("Voip", SND_SOC_NOPM, + MSM_BACKEND_DAI_AFE_PCM_RX, + MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("DTMF", SND_SOC_NOPM, + MSM_BACKEND_DAI_AFE_PCM_RX, + MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("QCHAT", SND_SOC_NOPM, + MSM_BACKEND_DAI_AFE_PCM_RX, + MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("VoiceMMode1", SND_SOC_NOPM, + MSM_BACKEND_DAI_AFE_PCM_RX, + MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("VoiceMMode2", SND_SOC_NOPM, + MSM_BACKEND_DAI_AFE_PCM_RX, + MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), +}; + +static const struct snd_kcontrol_new aux_pcm_rx_voice_mixer_controls[] = { + SOC_DOUBLE_EXT("Voip", SND_SOC_NOPM, + MSM_BACKEND_DAI_AUXPCM_RX, + MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("DTMF", SND_SOC_NOPM, + MSM_BACKEND_DAI_AUXPCM_RX, + MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("QCHAT", SND_SOC_NOPM, + MSM_BACKEND_DAI_AUXPCM_RX, + MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("VoiceMMode1", SND_SOC_NOPM, + MSM_BACKEND_DAI_AUXPCM_RX, + MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("VoiceMMode2", SND_SOC_NOPM, + MSM_BACKEND_DAI_AUXPCM_RX, + MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), +}; + +static const struct snd_kcontrol_new sec_aux_pcm_rx_voice_mixer_controls[] = { + SOC_DOUBLE_EXT("Voip", SND_SOC_NOPM, +MSM_BACKEND_DAI_SEC_AUXPCM_RX, + MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("DTMF", SND_SOC_NOPM, +MSM_BACKEND_DAI_SEC_AUXPCM_RX, + MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("QCHAT", SND_SOC_NOPM, +MSM_BACKEND_DAI_SEC_AUXPCM_RX, + MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("VoiceMMode1", SND_SOC_NOPM, +MSM_BACKEND_DAI_SEC_AUXPCM_RX, + MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("VoiceMMode2", SND_SOC_NOPM, +MSM_BACKEND_DAI_SEC_AUXPCM_RX, + MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), +}; + +static const struct snd_kcontrol_new tert_aux_pcm_rx_voice_mixer_controls[] = { + SOC_DOUBLE_EXT("Voip", SND_SOC_NOPM, +MSM_BACKEND_DAI_TERT_AUXPCM_RX, + MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("DTMF", SND_SOC_NOPM, +MSM_BACKEND_DAI_TERT_AUXPCM_RX, + MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("QCHAT", SND_SOC_NOPM, +MSM_BACKEND_DAI_TERT_AUXPCM_RX, + MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("VoiceMMode1", SND_SOC_NOPM, +MSM_BACKEND_DAI_TERT_AUXPCM_RX, + MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("VoiceMMode2", SND_SOC_NOPM, +MSM_BACKEND_DAI_TERT_AUXPCM_RX, + MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), +}; + +static const struct snd_kcontrol_new quat_aux_pcm_rx_voice_mixer_controls[] = { + SOC_DOUBLE_EXT("Voip", SND_SOC_NOPM, +MSM_BACKEND_DAI_QUAT_AUXPCM_RX, + MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("DTMF", SND_SOC_NOPM, +MSM_BACKEND_DAI_QUAT_AUXPCM_RX, + MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("QCHAT", SND_SOC_NOPM, +MSM_BACKEND_DAI_QUAT_AUXPCM_RX, + MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("VoiceMMode1", SND_SOC_NOPM, +MSM_BACKEND_DAI_QUAT_AUXPCM_RX, + MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("VoiceMMode2", SND_SOC_NOPM, +MSM_BACKEND_DAI_QUAT_AUXPCM_RX, + MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), +}; + +static const struct snd_kcontrol_new quin_aux_pcm_rx_voice_mixer_controls[] = { + SOC_DOUBLE_EXT("Voip", SND_SOC_NOPM, +MSM_BACKEND_DAI_QUIN_AUXPCM_RX, + MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("DTMF", SND_SOC_NOPM, +MSM_BACKEND_DAI_QUIN_AUXPCM_RX, + MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("QCHAT", SND_SOC_NOPM, +MSM_BACKEND_DAI_QUIN_AUXPCM_RX, + MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("VoiceMMode1", SND_SOC_NOPM, +MSM_BACKEND_DAI_QUIN_AUXPCM_RX, + MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("VoiceMMode2", SND_SOC_NOPM, +MSM_BACKEND_DAI_QUIN_AUXPCM_RX, + MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), +}; + +static const struct snd_kcontrol_new hdmi_rx_voice_mixer_controls[] = { + SOC_DOUBLE_EXT("Voip", SND_SOC_NOPM, + MSM_BACKEND_DAI_HDMI_RX, + MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("DTMF", SND_SOC_NOPM, + MSM_BACKEND_DAI_HDMI_RX, + MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("QCHAT", SND_SOC_NOPM, + MSM_BACKEND_DAI_HDMI_RX, + MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("VoiceMMode1", SND_SOC_NOPM, + MSM_BACKEND_DAI_HDMI_RX, + MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("VoiceMMode2", SND_SOC_NOPM, + MSM_BACKEND_DAI_HDMI_RX, + MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), +}; + +static const struct snd_kcontrol_new slimbus_7_rx_voice_mixer_controls[] = { + SOC_DOUBLE_EXT("Voip", SND_SOC_NOPM, +MSM_BACKEND_DAI_SLIMBUS_7_RX, + MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("DTMF", SND_SOC_NOPM, +MSM_BACKEND_DAI_SLIMBUS_7_RX, + MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("QCHAT", SND_SOC_NOPM, +MSM_BACKEND_DAI_SLIMBUS_7_RX, + MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("VoiceMMode1", SND_SOC_NOPM, +MSM_BACKEND_DAI_SLIMBUS_7_RX, + MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("VoiceMMode2", SND_SOC_NOPM, +MSM_BACKEND_DAI_SLIMBUS_7_RX, + MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), +}; + +static const struct snd_kcontrol_new slimbus_8_rx_voice_mixer_controls[] = { + SOC_DOUBLE_EXT("Voip", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_8_RX, + MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("DTMF", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_8_RX, + MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("QCHAT", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_8_RX, + MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("VoiceMMode1", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_8_RX, + MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("VoiceMMode2", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_8_RX, + MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), +}; + +static const struct snd_kcontrol_new quat_tdm_rx_2_voice_mixer_controls[] = { + SOC_DOUBLE_EXT("VoiceMMode1", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_2, + MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), +}; + +static const struct snd_kcontrol_new wsa_cdc_dma_rx_0_voice_mixer_controls[] = { + SOC_DOUBLE_EXT("Voip", SND_SOC_NOPM, + MSM_BACKEND_DAI_WSA_CDC_DMA_RX_0, + MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("DTMF", SND_SOC_NOPM, + MSM_BACKEND_DAI_WSA_CDC_DMA_RX_0, + MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("QCHAT", SND_SOC_NOPM, + MSM_BACKEND_DAI_WSA_CDC_DMA_RX_0, + MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("VoiceMMode1", SND_SOC_NOPM, + MSM_BACKEND_DAI_WSA_CDC_DMA_RX_0, + MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("VoiceMMode2", SND_SOC_NOPM, + MSM_BACKEND_DAI_WSA_CDC_DMA_RX_0, + MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), +}; + +static const struct snd_kcontrol_new wsa_cdc_dma_rx_1_voice_mixer_controls[] = { + SOC_DOUBLE_EXT("Voip", SND_SOC_NOPM, + MSM_BACKEND_DAI_WSA_CDC_DMA_RX_1, + MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("DTMF", SND_SOC_NOPM, + MSM_BACKEND_DAI_WSA_CDC_DMA_RX_1, + MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("QCHAT", SND_SOC_NOPM, + MSM_BACKEND_DAI_WSA_CDC_DMA_RX_1, + MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("VoiceMMode1", SND_SOC_NOPM, + MSM_BACKEND_DAI_WSA_CDC_DMA_RX_1, + MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("VoiceMMode2", SND_SOC_NOPM, + MSM_BACKEND_DAI_WSA_CDC_DMA_RX_1, + MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), +}; + +static const struct snd_kcontrol_new rx_cdc_dma_rx_0_voice_mixer_controls[] = { + SOC_DOUBLE_EXT("Voip", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_0, + MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("DTMF", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_0, + MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("QCHAT", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_0, + MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("VoiceMMode1", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_0, + MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("VoiceMMode2", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_0, + MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), +}; + +static const struct snd_kcontrol_new rx_cdc_dma_rx_1_voice_mixer_controls[] = { + SOC_DOUBLE_EXT("Voip", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_1, + MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("DTMF", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_1, + MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("QCHAT", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_1, + MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("VoiceMMode1", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_1, + MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("VoiceMMode2", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_1, + MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), +}; + +static const struct snd_kcontrol_new rx_cdc_dma_rx_2_voice_mixer_controls[] = { + SOC_DOUBLE_EXT("Voip", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_2, + MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("DTMF", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_2, + MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("QCHAT", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_2, + MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("VoiceMMode1", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_2, + MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("VoiceMMode2", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_2, + MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), +}; + +static const struct snd_kcontrol_new rx_cdc_dma_rx_3_voice_mixer_controls[] = { + SOC_DOUBLE_EXT("Voip", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_3, + MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("DTMF", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_3, + MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("QCHAT", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_3, + MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("VoiceMMode1", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_3, + MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("VoiceMMode2", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_3, + MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), +}; + +static const struct snd_kcontrol_new rx_cdc_dma_rx_4_voice_mixer_controls[] = { + SOC_DOUBLE_EXT("Voip", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_4, + MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("DTMF", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_4, + MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("QCHAT", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_4, + MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("VoiceMMode1", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_4, + MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("VoiceMMode2", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_4, + MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), +}; + +static const struct snd_kcontrol_new rx_cdc_dma_rx_5_voice_mixer_controls[] = { + SOC_DOUBLE_EXT("Voip", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_5, + MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("DTMF", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_5, + MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("QCHAT", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_5, + MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("VoiceMMode1", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_5, + MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("VoiceMMode2", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_5, + MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), +}; + +static const struct snd_kcontrol_new rx_cdc_dma_rx_6_voice_mixer_controls[] = { + SOC_DOUBLE_EXT("Voip", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_6, + MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("DTMF", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_6, + MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("QCHAT", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_6, + MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("VoiceMMode1", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_6, + MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("VoiceMMode2", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_6, + MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), +}; + +static const struct snd_kcontrol_new rx_cdc_dma_rx_7_voice_mixer_controls[] = { + SOC_DOUBLE_EXT("Voip", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_7, + MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("DTMF", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_7, + MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("QCHAT", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_7, + MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("VoiceMMode1", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_7, + MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("VoiceMMode2", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_7, + MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), +}; + +static const struct snd_kcontrol_new stub_rx_mixer_controls[] = { + SOC_DOUBLE_EXT("VoiceMMode1", SND_SOC_NOPM, + MSM_BACKEND_DAI_EXTPROC_RX, + MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("VoiceMMode2", SND_SOC_NOPM, + MSM_BACKEND_DAI_EXTPROC_RX, + MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), +}; + +static const struct snd_kcontrol_new slimbus_1_rx_mixer_controls[] = { + SOC_DOUBLE_EXT("VoiceMMode1", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_1_RX, + MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("VoiceMMode2", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_1_RX, + MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), +}; + +static const struct snd_kcontrol_new slimbus_3_rx_mixer_controls[] = { + SOC_DOUBLE_EXT("VoiceMMode1", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_3_RX, + MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("VoiceMMode2", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_3_RX, + MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), +}; + +static const struct snd_kcontrol_new tx_voicemmode1_mixer_controls[] = { + SOC_DOUBLE_EXT("PRI_TX_MMode1", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_I2S_TX, + MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("MI2S_TX_MMode1", SND_SOC_NOPM, + MSM_BACKEND_DAI_MI2S_TX, + MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("SLIM_0_TX_MMode1", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_0_TX, MSM_FRONTEND_DAI_VOICEMMODE1, 1, + 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("INT_BT_SCO_TX_MMode1", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT_BT_SCO_TX, MSM_FRONTEND_DAI_VOICEMMODE1, 1, + 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("AFE_PCM_TX_MMode1", SND_SOC_NOPM, + MSM_BACKEND_DAI_AFE_PCM_TX, MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, + msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("AUX_PCM_TX_MMode1", SND_SOC_NOPM, + MSM_BACKEND_DAI_AUXPCM_TX, MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, + msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("SEC_AUX_PCM_TX_MMode1", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_AUXPCM_TX, MSM_FRONTEND_DAI_VOICEMMODE1, 1, + 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("TERT_AUX_PCM_TX_MMode1", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_AUXPCM_TX, MSM_FRONTEND_DAI_VOICEMMODE1, 1, + 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("QUAT_AUX_PCM_TX_MMode1", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_AUXPCM_TX, MSM_FRONTEND_DAI_VOICEMMODE1, 1, + 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("QUIN_AUX_PCM_TX_MMode1", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_AUXPCM_TX, MSM_FRONTEND_DAI_VOICEMMODE1, 1, + 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("PRI_MI2S_TX_MMode1", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_MI2S_TX, MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, + msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("TERT_MI2S_TX_MMode1", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERTIARY_MI2S_TX, MSM_FRONTEND_DAI_VOICEMMODE1, + 1, 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("INT3_MI2S_TX_MMode1", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT3_MI2S_TX, MSM_FRONTEND_DAI_VOICEMMODE1, + 1, 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("SLIM_7_TX_MMode1", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_7_TX, MSM_FRONTEND_DAI_VOICEMMODE1, 1, + 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("SLIM_8_TX_MMode1", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_8_TX, MSM_FRONTEND_DAI_VOICEMMODE1, 1, + 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("USB_AUDIO_TX_MMode1", SND_SOC_NOPM, + MSM_BACKEND_DAI_USB_TX, + MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_0_MMode1", SND_SOC_NOPM, +MSM_BACKEND_DAI_QUAT_TDM_TX_0, MSM_FRONTEND_DAI_VOICEMMODE1, + 1, 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("VA_CDC_DMA_TX_0_MMode1", SND_SOC_NOPM, + MSM_BACKEND_DAI_VA_CDC_DMA_TX_0, MSM_FRONTEND_DAI_VOICEMMODE1, + 1, 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("VA_CDC_DMA_TX_1_MMode1", SND_SOC_NOPM, + MSM_BACKEND_DAI_VA_CDC_DMA_TX_1, MSM_FRONTEND_DAI_VOICEMMODE1, + 1, 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_0_MMode1", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_0, MSM_FRONTEND_DAI_VOICEMMODE1, + 1, 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_1_MMode1", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_1, MSM_FRONTEND_DAI_VOICEMMODE1, + 1, 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_2_MMode1", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_2, MSM_FRONTEND_DAI_VOICEMMODE1, + 1, 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_3_MMode1", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_3, MSM_FRONTEND_DAI_VOICEMMODE1, + 1, 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_4_MMode1", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_4, MSM_FRONTEND_DAI_VOICEMMODE1, + 1, 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_5_MMode1", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_5, MSM_FRONTEND_DAI_VOICEMMODE1, + 1, 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), +}; + +static const struct snd_kcontrol_new tx_voicemmode2_mixer_controls[] = { + SOC_DOUBLE_EXT("PRI_TX_MMode2", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_I2S_TX, + MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("MI2S_TX_MMode2", SND_SOC_NOPM, + MSM_BACKEND_DAI_MI2S_TX, + MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("SLIM_0_TX_MMode2", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_0_TX, MSM_FRONTEND_DAI_VOICEMMODE2, 1, + 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("INT_BT_SCO_TX_MMode2", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT_BT_SCO_TX, MSM_FRONTEND_DAI_VOICEMMODE2, 1, + 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("AFE_PCM_TX_MMode2", SND_SOC_NOPM, + MSM_BACKEND_DAI_AFE_PCM_TX, MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, + msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("AUX_PCM_TX_MMode2", SND_SOC_NOPM, + MSM_BACKEND_DAI_AUXPCM_TX, MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, + msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("SEC_AUX_PCM_TX_MMode2", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_AUXPCM_TX, MSM_FRONTEND_DAI_VOICEMMODE2, 1, + 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("TERT_AUX_PCM_TX_MMode2", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_AUXPCM_TX, MSM_FRONTEND_DAI_VOICEMMODE2, 1, + 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("QUAT_AUX_PCM_TX_MMode2", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_AUXPCM_TX, MSM_FRONTEND_DAI_VOICEMMODE2, 1, + 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("QUIN_AUX_PCM_TX_MMode2", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_AUXPCM_TX, MSM_FRONTEND_DAI_VOICEMMODE2, 1, + 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("PRI_MI2S_TX_MMode2", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_MI2S_TX, MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, + msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("TERT_MI2S_TX_MMode2", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERTIARY_MI2S_TX, MSM_FRONTEND_DAI_VOICEMMODE2, + 1, 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("INT3_MI2S_TX_MMode2", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT3_MI2S_TX, MSM_FRONTEND_DAI_VOICEMMODE2, + 1, 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("SLIM_7_TX_MMode2", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_7_TX, MSM_FRONTEND_DAI_VOICEMMODE2, 1, + 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("SLIM_8_TX_MMode2", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_8_TX, MSM_FRONTEND_DAI_VOICEMMODE2, 1, + 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("USB_AUDIO_TX_MMode2", SND_SOC_NOPM, + MSM_BACKEND_DAI_USB_TX, MSM_FRONTEND_DAI_VOICEMMODE2, 1, + 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("VA_CDC_DMA_TX_0_MMode2", SND_SOC_NOPM, + MSM_BACKEND_DAI_VA_CDC_DMA_TX_0, MSM_FRONTEND_DAI_VOICEMMODE2, + 1, 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("VA_CDC_DMA_TX_1_MMode2", SND_SOC_NOPM, + MSM_BACKEND_DAI_VA_CDC_DMA_TX_1, MSM_FRONTEND_DAI_VOICEMMODE2, + 1, 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_0_MMode2", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_0, MSM_FRONTEND_DAI_VOICEMMODE2, + 1, 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_1_MMode2", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_1, MSM_FRONTEND_DAI_VOICEMMODE2, + 1, 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_2_MMode2", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_2, MSM_FRONTEND_DAI_VOICEMMODE2, + 1, 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_3_MMode2", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_3, MSM_FRONTEND_DAI_VOICEMMODE2, + 1, 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_4_MMode2", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_4, MSM_FRONTEND_DAI_VOICEMMODE2, + 1, 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_5_MMode2", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_5, MSM_FRONTEND_DAI_VOICEMMODE2, + 1, 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), +}; + +static const struct snd_kcontrol_new tx_voip_mixer_controls[] = { + SOC_DOUBLE_EXT("PRI_TX_Voip", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_I2S_TX, + MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("MI2S_TX_Voip", SND_SOC_NOPM, + MSM_BACKEND_DAI_MI2S_TX, + MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("SLIM_0_TX_Voip", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_0_TX, + MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("INTERNAL_BT_SCO_TX_Voip", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT_BT_SCO_TX, + MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("AFE_PCM_TX_Voip", SND_SOC_NOPM, + MSM_BACKEND_DAI_AFE_PCM_TX, + MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("AUX_PCM_TX_Voip", SND_SOC_NOPM, + MSM_BACKEND_DAI_AUXPCM_TX, + MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("SEC_AUX_PCM_TX_Voip", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_AUXPCM_TX, + MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("TERT_AUX_PCM_TX_Voip", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_AUXPCM_TX, + MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("QUAT_AUX_PCM_TX_Voip", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_AUXPCM_TX, + MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("QUIN_AUX_PCM_TX_Voip", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_AUXPCM_TX, + MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("PRI_MI2S_TX_Voip", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_MI2S_TX, + MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("TERT_MI2S_TX_Voip", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERTIARY_MI2S_TX, + MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("INT3_MI2S_TX_Voip", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT3_MI2S_TX, + MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("SLIM_7_TX_Voip", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_7_TX, + MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("SLIM_8_TX_Voip", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_8_TX, + MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("USB_AUDIO_TX_Voip", SND_SOC_NOPM, + MSM_BACKEND_DAI_USB_TX, + MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("VA_CDC_DMA_TX_0_Voip", SND_SOC_NOPM, + MSM_BACKEND_DAI_VA_CDC_DMA_TX_0, MSM_FRONTEND_DAI_VOIP, + 1, 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("VA_CDC_DMA_TX_1_Voip", SND_SOC_NOPM, + MSM_BACKEND_DAI_VA_CDC_DMA_TX_1, MSM_FRONTEND_DAI_VOIP, + 1, 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_0_Voip", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_0, MSM_FRONTEND_DAI_VOIP, + 1, 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_1_Voip", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_1, MSM_FRONTEND_DAI_VOIP, + 1, 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_2_Voip", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_2, MSM_FRONTEND_DAI_VOIP, + 1, 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_3_Voip", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_3, MSM_FRONTEND_DAI_VOIP, + 1, 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_4_Voip", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_4, MSM_FRONTEND_DAI_VOIP, + 1, 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_5_Voip", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_5, MSM_FRONTEND_DAI_VOIP, + 1, 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), +}; + +static const struct snd_kcontrol_new tx_voice_stub_mixer_controls[] = { + SOC_DOUBLE_EXT("STUB_TX_HL", SND_SOC_NOPM, + MSM_BACKEND_DAI_EXTPROC_TX, + MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_DOUBLE_EXT("INTERNAL_BT_SCO_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT_BT_SCO_TX, + MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_DOUBLE_EXT("SLIM_1_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_1_TX, + MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_DOUBLE_EXT("STUB_1_TX_HL", SND_SOC_NOPM, + MSM_BACKEND_DAI_EXTPROC_EC_TX, + MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_DOUBLE_EXT("MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_MI2S_TX, + MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_DOUBLE_EXT("PRI_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_MI2S_TX, + MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_DOUBLE_EXT("TERT_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERTIARY_MI2S_TX, + MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_DOUBLE_EXT("INT3_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT3_MI2S_TX, + MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_DOUBLE_EXT("QUAT_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, + MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_DOUBLE_EXT("AUX_PCM_UL_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_AUXPCM_TX, + MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_DOUBLE_EXT("SEC_AUX_PCM_UL_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_AUXPCM_TX, + MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_DOUBLE_EXT("TERT_AUXPCM_UL_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_AUXPCM_TX, + MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_DOUBLE_EXT("QUAT_AUXPCM_UL_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_AUXPCM_TX, + MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_DOUBLE_EXT("QUIN_AUXPCM_UL_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_AUXPCM_TX, + MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_DOUBLE_EXT("SLIM_0_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_0_TX, + MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_DOUBLE_EXT("SLIM_3_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_3_TX, + MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_DOUBLE_EXT("AFE_PCM_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_AFE_PCM_TX, + MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_DOUBLE_EXT("SLIM_7_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_7_TX, + MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_DOUBLE_EXT("SLIM_8_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_8_TX, + MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_DOUBLE_EXT("VA_CDC_DMA_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_VA_CDC_DMA_TX_0, MSM_FRONTEND_DAI_VOICE_STUB, + 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_DOUBLE_EXT("VA_CDC_DMA_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_VA_CDC_DMA_TX_1, MSM_FRONTEND_DAI_VOICE_STUB, + 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_0, MSM_FRONTEND_DAI_VOICE_STUB, + 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_1, MSM_FRONTEND_DAI_VOICE_STUB, + 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_2, MSM_FRONTEND_DAI_VOICE_STUB, + 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_3, MSM_FRONTEND_DAI_VOICE_STUB, + 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_4", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_4, MSM_FRONTEND_DAI_VOICE_STUB, + 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_5", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_5, MSM_FRONTEND_DAI_VOICE_STUB, + 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), +}; + +static const struct snd_kcontrol_new tx_voice2_stub_mixer_controls[] = { + SOC_DOUBLE_EXT("STUB_TX_HL", SND_SOC_NOPM, + MSM_BACKEND_DAI_EXTPROC_TX, + MSM_FRONTEND_DAI_VOICE2_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_DOUBLE_EXT("SLIM_1_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_1_TX, + MSM_FRONTEND_DAI_VOICE2_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_DOUBLE_EXT("STUB_1_TX_HL", SND_SOC_NOPM, + MSM_BACKEND_DAI_EXTPROC_EC_TX, + MSM_FRONTEND_DAI_VOICE2_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_DOUBLE_EXT("AUX_PCM_UL_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_AUXPCM_TX, + MSM_FRONTEND_DAI_VOICE2_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_DOUBLE_EXT("SEC_AUX_PCM_UL_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_AUXPCM_TX, + MSM_FRONTEND_DAI_VOICE2_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_DOUBLE_EXT("TERT_AUXPCM_UL_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_AUXPCM_TX, + MSM_FRONTEND_DAI_VOICE2_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_DOUBLE_EXT("QUAT_AUXPCM_UL_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_AUXPCM_TX, + MSM_FRONTEND_DAI_VOICE2_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_DOUBLE_EXT("QUIN_AUXPCM_UL_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_AUXPCM_TX, + MSM_FRONTEND_DAI_VOICE2_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_DOUBLE_EXT("SLIM_0_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_0_TX, + MSM_FRONTEND_DAI_VOICE2_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_DOUBLE_EXT("SLIM_3_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_3_TX, + MSM_FRONTEND_DAI_VOICE2_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_DOUBLE_EXT("AFE_PCM_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_AFE_PCM_TX, + MSM_FRONTEND_DAI_VOICE2_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_DOUBLE_EXT("PRI_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_MI2S_TX, + MSM_FRONTEND_DAI_VOICE2_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_DOUBLE_EXT("QUAT_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, + MSM_FRONTEND_DAI_VOICE2_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_DOUBLE_EXT("SLIM_7_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_7_TX, + MSM_FRONTEND_DAI_VOICE2_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_DOUBLE_EXT("SLIM_8_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_8_TX, + MSM_FRONTEND_DAI_VOICE2_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_DOUBLE_EXT("VA_CDC_DMA_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_VA_CDC_DMA_TX_0, MSM_FRONTEND_DAI_VOICE2_STUB, + 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_DOUBLE_EXT("VA_CDC_DMA_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_VA_CDC_DMA_TX_1, MSM_FRONTEND_DAI_VOICE2_STUB, + 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_0, MSM_FRONTEND_DAI_VOICE2_STUB, + 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_1, MSM_FRONTEND_DAI_VOICE2_STUB, + 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_2, MSM_FRONTEND_DAI_VOICE2_STUB, + 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_3, MSM_FRONTEND_DAI_VOICE2_STUB, + 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_4", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_4, MSM_FRONTEND_DAI_VOICE2_STUB, + 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_5", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_5, MSM_FRONTEND_DAI_VOICE2_STUB, + 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), +}; + +static const struct snd_kcontrol_new tx_volte_stub_mixer_controls[] = { + SOC_DOUBLE_EXT("STUB_TX_HL", SND_SOC_NOPM, + MSM_BACKEND_DAI_EXTPROC_TX, + MSM_FRONTEND_DAI_VOLTE_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_DOUBLE_EXT("SLIM_1_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_1_TX, + MSM_FRONTEND_DAI_VOLTE_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_DOUBLE_EXT("STUB_1_TX_HL", SND_SOC_NOPM, + MSM_BACKEND_DAI_EXTPROC_EC_TX, + MSM_FRONTEND_DAI_VOLTE_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_DOUBLE_EXT("AUX_PCM_UL_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_AUXPCM_TX, + MSM_FRONTEND_DAI_VOLTE_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_DOUBLE_EXT("SEC_AUX_PCM_UL_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_AUXPCM_TX, + MSM_FRONTEND_DAI_VOLTE_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_DOUBLE_EXT("TERT_AUXPCM_UL_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_AUXPCM_TX, + MSM_FRONTEND_DAI_VOLTE_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_DOUBLE_EXT("QUAT_AUXPCM_UL_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_AUXPCM_TX, + MSM_FRONTEND_DAI_VOLTE_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_DOUBLE_EXT("QUIN_AUXPCM_UL_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_AUXPCM_TX, + MSM_FRONTEND_DAI_VOLTE_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_DOUBLE_EXT("SLIM_0_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_0_TX, + MSM_FRONTEND_DAI_VOLTE_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_DOUBLE_EXT("SLIM_3_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_3_TX, + MSM_FRONTEND_DAI_VOLTE_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_DOUBLE_EXT("AFE_PCM_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_AFE_PCM_TX, + MSM_FRONTEND_DAI_VOLTE_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_DOUBLE_EXT("PRI_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_MI2S_TX, + MSM_FRONTEND_DAI_VOLTE_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_DOUBLE_EXT("QUAT_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, + MSM_FRONTEND_DAI_VOLTE_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_DOUBLE_EXT("SLIM_7_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_7_TX, + MSM_FRONTEND_DAI_VOLTE_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_DOUBLE_EXT("SLIM_8_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_8_TX, + MSM_FRONTEND_DAI_VOLTE_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_DOUBLE_EXT("VA_CDC_DMA_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_VA_CDC_DMA_TX_0, MSM_FRONTEND_DAI_VOLTE_STUB, + 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_DOUBLE_EXT("VA_CDC_DMA_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_VA_CDC_DMA_TX_1, MSM_FRONTEND_DAI_VOLTE_STUB, + 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_0, MSM_FRONTEND_DAI_VOLTE_STUB, + 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_1, MSM_FRONTEND_DAI_VOLTE_STUB, + 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_2, MSM_FRONTEND_DAI_VOLTE_STUB, + 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_3, MSM_FRONTEND_DAI_VOLTE_STUB, + 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_4", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_4, MSM_FRONTEND_DAI_VOLTE_STUB, + 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_5", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_5, MSM_FRONTEND_DAI_VOLTE_STUB, + 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_mixer), +}; + +static const struct snd_kcontrol_new tx_qchat_mixer_controls[] = { + SOC_DOUBLE_EXT("PRI_TX_QCHAT", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_I2S_TX, + MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("SLIM_0_TX_QCHAT", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_0_TX, + MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("INTERNAL_BT_SCO_TX_QCHAT", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT_BT_SCO_TX, + MSM_FRONTEND_DAI_QCHAT, 1, 0, + msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("AFE_PCM_TX_QCHAT", SND_SOC_NOPM, + MSM_BACKEND_DAI_AFE_PCM_TX, + MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("AUX_PCM_TX_QCHAT", SND_SOC_NOPM, + MSM_BACKEND_DAI_AUXPCM_TX, + MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("SEC_AUX_PCM_TX_QCHAT", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_AUXPCM_TX, + MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("TERT_AUX_PCM_TX_QCHAT", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_AUXPCM_TX, + MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("QUAT_AUX_PCM_TX_QCHAT", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_AUXPCM_TX, + MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("QUIN_AUX_PCM_TX_QCHAT", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_AUXPCM_TX, + MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("MI2S_TX_QCHAT", SND_SOC_NOPM, + MSM_BACKEND_DAI_MI2S_TX, + MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("PRI_MI2S_TX_QCHAT", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_MI2S_TX, + MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("TERT_MI2S_TX_QCHAT", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERTIARY_MI2S_TX, + MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("INT3_MI2S_TX_QCHAT", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT3_MI2S_TX, + MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("SLIM_7_TX_QCHAT", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_7_TX, + MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("SLIM_8_TX_QCHAT", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_8_TX, + MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("USB_AUDIO_TX_QCHAT", SND_SOC_NOPM, + MSM_BACKEND_DAI_USB_TX, + MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("VA_CDC_DMA_TX_0_QCHAT", SND_SOC_NOPM, + MSM_BACKEND_DAI_VA_CDC_DMA_TX_0, MSM_FRONTEND_DAI_QCHAT, + 1, 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("VA_CDC_DMA_TX_1_QCHAT", SND_SOC_NOPM, + MSM_BACKEND_DAI_VA_CDC_DMA_TX_1, MSM_FRONTEND_DAI_QCHAT, + 1, 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_0_QCHAT", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_0, MSM_FRONTEND_DAI_QCHAT, + 1, 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_1_QCHAT", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_1, MSM_FRONTEND_DAI_QCHAT, + 1, 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_2_QCHAT", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_2, MSM_FRONTEND_DAI_QCHAT, + 1, 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_3_QCHAT", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_3, MSM_FRONTEND_DAI_QCHAT, + 1, 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_4_QCHAT", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_4, MSM_FRONTEND_DAI_QCHAT, + 1, 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_5_QCHAT", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_5, MSM_FRONTEND_DAI_QCHAT, + 1, 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), +}; + +static const struct snd_kcontrol_new int0_mi2s_rx_port_mixer_controls[] = { + SOC_DOUBLE_EXT("PRI_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT0_MI2S_RX, + MSM_BACKEND_DAI_PRI_MI2S_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("SEC_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT0_MI2S_RX, + MSM_BACKEND_DAI_SECONDARY_MI2S_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("TERT_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT0_MI2S_RX, + MSM_BACKEND_DAI_TERTIARY_MI2S_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUAT_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT0_MI2S_RX, + MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUIN_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT0_MI2S_RX, + MSM_BACKEND_DAI_QUINARY_MI2S_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("INT3_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT0_MI2S_RX, + MSM_BACKEND_DAI_INT3_MI2S_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("INTERNAL_FM_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT0_MI2S_RX, + MSM_BACKEND_DAI_INT_FM_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("INTERNAL_BT_SCO_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT0_MI2S_RX, + MSM_BACKEND_DAI_INT_BT_SCO_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("SLIM_7_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT0_MI2S_RX, + MSM_BACKEND_DAI_SLIMBUS_7_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("SLIM_8_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT0_MI2S_RX, + MSM_BACKEND_DAI_SLIMBUS_8_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("SLIM_9_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT0_MI2S_RX, + MSM_BACKEND_DAI_SLIMBUS_9_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), +}; + +static const struct snd_kcontrol_new int4_mi2s_rx_port_mixer_controls[] = { + SOC_DOUBLE_EXT("PRI_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT4_MI2S_RX, + MSM_BACKEND_DAI_PRI_MI2S_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("SEC_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT4_MI2S_RX, + MSM_BACKEND_DAI_SECONDARY_MI2S_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("TERT_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT4_MI2S_RX, + MSM_BACKEND_DAI_TERTIARY_MI2S_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUAT_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT4_MI2S_RX, + MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUIN_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT4_MI2S_RX, + MSM_BACKEND_DAI_QUINARY_MI2S_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("INT3_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT4_MI2S_RX, + MSM_BACKEND_DAI_INT3_MI2S_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("INTERNAL_FM_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT4_MI2S_RX, + MSM_BACKEND_DAI_INT_FM_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("INTERNAL_BT_SCO_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT4_MI2S_RX, + MSM_BACKEND_DAI_INT_BT_SCO_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("SLIM_7_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT4_MI2S_RX, + MSM_BACKEND_DAI_SLIMBUS_7_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("SLIM_8_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT4_MI2S_RX, + MSM_BACKEND_DAI_SLIMBUS_8_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("SLIM_9_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT4_MI2S_RX, + MSM_BACKEND_DAI_SLIMBUS_9_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), +}; + +static const struct snd_kcontrol_new wsa_cdc_dma_rx_0_port_mixer_controls[] = { + SOC_DOUBLE_EXT("VA_CDC_DMA_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_WSA_CDC_DMA_RX_0, + MSM_BACKEND_DAI_VA_CDC_DMA_TX_0, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_WSA_CDC_DMA_RX_0, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_3, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("SLIM_8_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_WSA_CDC_DMA_RX_0, + MSM_BACKEND_DAI_SLIMBUS_8_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), +}; + +static const struct snd_kcontrol_new rx_cdc_dma_rx_0_port_mixer_controls[] = { + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_0, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_3, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("SLIM_8_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_0, + MSM_BACKEND_DAI_SLIMBUS_8_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("SLIM_9_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT4_MI2S_RX, + MSM_BACKEND_DAI_SLIMBUS_9_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), +}; + +static const struct snd_kcontrol_new sbus_0_rx_port_mixer_controls[] = { + SOC_DOUBLE_EXT("INTERNAL_FM_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_0_RX, + MSM_BACKEND_DAI_INT_FM_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("SLIM_0_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_0_RX, + MSM_BACKEND_DAI_SLIMBUS_0_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("SLIM_1_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_0_RX, + MSM_BACKEND_DAI_SLIMBUS_1_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("SLIM_7_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_0_RX, + MSM_BACKEND_DAI_SLIMBUS_7_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("SLIM_8_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_0_RX, + MSM_BACKEND_DAI_SLIMBUS_8_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("AUX_PCM_UL_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_0_RX, + MSM_BACKEND_DAI_AUXPCM_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("SEC_AUX_PCM_UL_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_0_RX, + MSM_BACKEND_DAI_SEC_AUXPCM_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("TERT_AUXPCM_UL_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_0_RX, + MSM_BACKEND_DAI_TERT_AUXPCM_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUAT_AUXPCM_UL_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_0_RX, + MSM_BACKEND_DAI_QUAT_AUXPCM_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUIN_AUXPCM_UL_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_0_RX, + MSM_BACKEND_DAI_QUIN_AUXPCM_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_0_RX, + MSM_BACKEND_DAI_MI2S_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("PRI_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_0_RX, + MSM_BACKEND_DAI_PRI_MI2S_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("SEC_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_0_RX, + MSM_BACKEND_DAI_SECONDARY_MI2S_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("TERT_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_0_RX, + MSM_BACKEND_DAI_TERTIARY_MI2S_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUAT_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_0_RX, + MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUIN_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_0_RX, + MSM_BACKEND_DAI_QUINARY_MI2S_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("INTERNAL_BT_SCO_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_0_RX, + MSM_BACKEND_DAI_INT_BT_SCO_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("PRI_MI2S_RX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_0_RX, + MSM_BACKEND_DAI_PRI_MI2S_RX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("SEC_MI2S_RX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_0_RX, + MSM_BACKEND_DAI_SECONDARY_MI2S_RX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("TERT_MI2S_RX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_0_RX, + MSM_BACKEND_DAI_TERTIARY_MI2S_RX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUAT_MI2S_RX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_0_RX, + MSM_BACKEND_DAI_QUATERNARY_MI2S_RX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUIN_MI2S_RX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_0_RX, + MSM_BACKEND_DAI_QUINARY_MI2S_RX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("SLIM_9_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_0_RX, + MSM_BACKEND_DAI_SLIMBUS_9_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), +}; + +static const struct snd_kcontrol_new aux_pcm_rx_port_mixer_controls[] = { + SOC_DOUBLE_EXT("AUX_PCM_UL_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_AUXPCM_RX, + MSM_BACKEND_DAI_AUXPCM_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("SLIM_0_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_AUXPCM_RX, + MSM_BACKEND_DAI_SLIMBUS_0_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("SLIM_1_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_AUXPCM_RX, + MSM_BACKEND_DAI_SLIMBUS_1_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("SEC_AUX_PCM_UL_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_AUXPCM_RX, + MSM_BACKEND_DAI_SEC_AUXPCM_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_AUXPCM_RX, + MSM_BACKEND_DAI_QUAT_TDM_TX_0, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("SEC_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_AUXPCM_RX, + MSM_BACKEND_DAI_SECONDARY_MI2S_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), +}; + +static const struct snd_kcontrol_new sec_auxpcm_rx_port_mixer_controls[] = { + SOC_DOUBLE_EXT("SEC_AUX_PCM_UL_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_AUXPCM_RX, + MSM_BACKEND_DAI_SEC_AUXPCM_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("SLIM_0_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_AUXPCM_RX, + MSM_BACKEND_DAI_SLIMBUS_0_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("AUX_PCM_UL_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_AUXPCM_RX, + MSM_BACKEND_DAI_AUXPCM_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("PRI_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_AUXPCM_RX, + MSM_BACKEND_DAI_PRI_MI2S_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), +}; + +static const struct snd_kcontrol_new tert_auxpcm_rx_port_mixer_controls[] = { + SOC_DOUBLE_EXT("TERT_AUXPCM_UL_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_AUXPCM_RX, + MSM_BACKEND_DAI_TERT_AUXPCM_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("SLIM_0_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_AUXPCM_RX, + MSM_BACKEND_DAI_SLIMBUS_0_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("AUX_PCM_UL_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_AUXPCM_RX, + MSM_BACKEND_DAI_AUXPCM_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), +}; + +static const struct snd_kcontrol_new quat_auxpcm_rx_port_mixer_controls[] = { + SOC_DOUBLE_EXT("QUAT_AUXPCM_UL_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_AUXPCM_RX, + MSM_BACKEND_DAI_QUAT_AUXPCM_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("SLIM_0_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_AUXPCM_RX, + MSM_BACKEND_DAI_SLIMBUS_0_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("AUX_PCM_UL_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_AUXPCM_RX, + MSM_BACKEND_DAI_AUXPCM_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), +}; + +static const struct snd_kcontrol_new quin_auxpcm_rx_port_mixer_controls[] = { + SOC_DOUBLE_EXT("QUIN_AUXPCM_UL_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_AUXPCM_RX, + MSM_BACKEND_DAI_QUIN_AUXPCM_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("SLIM_0_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_AUXPCM_RX, + MSM_BACKEND_DAI_SLIMBUS_0_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("AUX_PCM_UL_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_AUXPCM_RX, + MSM_BACKEND_DAI_AUXPCM_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), +}; + +static const struct snd_kcontrol_new sbus_1_rx_port_mixer_controls[] = { + SOC_DOUBLE_EXT("INTERNAL_BT_SCO_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_1_RX, + MSM_BACKEND_DAI_INT_BT_SCO_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("AFE_PCM_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_1_RX, + MSM_BACKEND_DAI_AFE_PCM_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("AUX_PCM_UL_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_1_RX, + MSM_BACKEND_DAI_AUXPCM_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("SEC_AUX_PCM_UL_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_1_RX, + MSM_BACKEND_DAI_SEC_AUXPCM_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("TERT_AUXPCM_UL_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_1_RX, + MSM_BACKEND_DAI_TERT_AUXPCM_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUAT_AUXPCM_UL_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_1_RX, + MSM_BACKEND_DAI_QUAT_AUXPCM_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), +}; + +static const struct snd_kcontrol_new sbus_3_rx_port_mixer_controls[] = { + SOC_DOUBLE_EXT("INTERNAL_BT_SCO_RX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_3_RX, + MSM_BACKEND_DAI_INT_BT_SCO_RX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_3_RX, + MSM_BACKEND_DAI_MI2S_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("AFE_PCM_RX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_3_RX, + MSM_BACKEND_DAI_AFE_PCM_RX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("AUX_PCM_RX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_3_RX, + MSM_BACKEND_DAI_AUXPCM_RX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("SLIM_0_RX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_3_RX, + MSM_BACKEND_DAI_SLIMBUS_0_RX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), +}; + +static const struct snd_kcontrol_new sbus_6_rx_port_mixer_controls[] = { + SOC_DOUBLE_EXT("INTERNAL_FM_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_6_RX, + MSM_BACKEND_DAI_INT_FM_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("SLIM_0_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_6_RX, + MSM_BACKEND_DAI_SLIMBUS_0_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("SLIM_1_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_6_RX, + MSM_BACKEND_DAI_SLIMBUS_1_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("SLIM_7_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_6_RX, + MSM_BACKEND_DAI_SLIMBUS_7_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("SLIM_8_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_6_RX, + MSM_BACKEND_DAI_SLIMBUS_8_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("AUX_PCM_UL_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_6_RX, + MSM_BACKEND_DAI_AUXPCM_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("SEC_AUX_PCM_UL_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_6_RX, + MSM_BACKEND_DAI_SEC_AUXPCM_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_6_RX, + MSM_BACKEND_DAI_MI2S_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("PRI_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_6_RX, + MSM_BACKEND_DAI_PRI_MI2S_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("TERT_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_6_RX, + MSM_BACKEND_DAI_TERTIARY_MI2S_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUAT_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_6_RX, + MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("INTERNAL_BT_SCO_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_6_RX, + MSM_BACKEND_DAI_INT_BT_SCO_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("SLIM_9_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_6_RX, + MSM_BACKEND_DAI_SLIMBUS_9_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), +}; + +static const struct snd_kcontrol_new bt_sco_rx_port_mixer_controls[] = { + SOC_DOUBLE_EXT("SLIM_1_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT_BT_SCO_RX, + MSM_BACKEND_DAI_SLIMBUS_1_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("SLIM_0_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT_BT_SCO_RX, + MSM_BACKEND_DAI_SLIMBUS_0_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), +}; + +static const struct snd_kcontrol_new afe_pcm_rx_port_mixer_controls[] = { + SOC_DOUBLE_EXT("INTERNAL_FM_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_AFE_PCM_RX, + MSM_BACKEND_DAI_INT_FM_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("SLIM_1_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_AFE_PCM_RX, + MSM_BACKEND_DAI_SLIMBUS_1_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), +}; + + +static const struct snd_kcontrol_new hdmi_rx_port_mixer_controls[] = { + SOC_DOUBLE_EXT("MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_HDMI_RX, + MSM_BACKEND_DAI_MI2S_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), +}; + +static const struct snd_kcontrol_new display_port_rx_port_mixer_controls[] = { + SOC_DOUBLE_EXT("MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_DISPLAY_PORT_RX, + MSM_BACKEND_DAI_MI2S_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), +}; + +static const struct snd_kcontrol_new display_port_rx1_port_mixer_controls[] = { + SOC_DOUBLE_EXT("MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_DISPLAY_PORT_RX_1, + MSM_BACKEND_DAI_MI2S_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), +}; + +static const struct snd_kcontrol_new sec_i2s_rx_port_mixer_controls[] = { + SOC_DOUBLE_EXT("MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_I2S_RX, + MSM_BACKEND_DAI_MI2S_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), +}; + +static const struct snd_kcontrol_new mi2s_rx_port_mixer_controls[] = { + SOC_DOUBLE_EXT("SLIM_1_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_MI2S_RX, + MSM_BACKEND_DAI_SLIMBUS_1_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_MI2S_RX, + MSM_BACKEND_DAI_MI2S_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), +}; + +static const struct snd_kcontrol_new primary_mi2s_rx_port_mixer_controls[] = { + SOC_DOUBLE_EXT("SEC_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_MI2S_RX, + MSM_BACKEND_DAI_SECONDARY_MI2S_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("TERT_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_MI2S_RX, + MSM_BACKEND_DAI_TERTIARY_MI2S_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("INTERNAL_FM_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_MI2S_RX, + MSM_BACKEND_DAI_INT_FM_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUAT_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_MI2S_RX, + MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("INTERNAL_BT_SCO_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_MI2S_RX, + MSM_BACKEND_DAI_INT_BT_SCO_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("PRI_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_MI2S_RX, + MSM_BACKEND_DAI_PRI_MI2S_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUIN_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_MI2S_RX, + MSM_BACKEND_DAI_QUINARY_MI2S_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("SLIM_0_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_MI2S_RX, + MSM_BACKEND_DAI_SLIMBUS_0_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("SLIM_8_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_MI2S_RX, + MSM_BACKEND_DAI_SLIMBUS_8_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("SEC_AUX_PCM_UL_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_MI2S_RX, + MSM_BACKEND_DAI_SEC_AUXPCM_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), +}; + +static const struct snd_kcontrol_new usb_rx_port_mixer_controls[] = { + SOC_DOUBLE_EXT("USB_AUDIO_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_USB_RX, + MSM_BACKEND_DAI_USB_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), +}; + +static const struct snd_kcontrol_new quat_mi2s_rx_port_mixer_controls[] = { + SOC_DOUBLE_EXT("PRI_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUATERNARY_MI2S_RX, + MSM_BACKEND_DAI_PRI_MI2S_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("TERT_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUATERNARY_MI2S_RX, + MSM_BACKEND_DAI_TERTIARY_MI2S_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("INTERNAL_FM_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUATERNARY_MI2S_RX, + MSM_BACKEND_DAI_INT_FM_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("AUX_PCM_UL_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUATERNARY_MI2S_RX, + MSM_BACKEND_DAI_AUXPCM_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("SLIM_0_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUATERNARY_MI2S_RX, + MSM_BACKEND_DAI_SLIMBUS_0_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("SEC_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUATERNARY_MI2S_RX, + MSM_BACKEND_DAI_SECONDARY_MI2S_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUAT_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUATERNARY_MI2S_RX, + MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUIN_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUATERNARY_MI2S_RX, + MSM_BACKEND_DAI_QUINARY_MI2S_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("SLIM_8_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUATERNARY_MI2S_RX, + MSM_BACKEND_DAI_SLIMBUS_8_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), +}; + +static const struct snd_kcontrol_new quin_mi2s_rx_port_mixer_controls[] = { + SOC_DOUBLE_EXT("PRI_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUINARY_MI2S_RX, + MSM_BACKEND_DAI_PRI_MI2S_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("TERT_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUINARY_MI2S_RX, + MSM_BACKEND_DAI_TERTIARY_MI2S_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("SLIM_0_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUINARY_MI2S_RX, + MSM_BACKEND_DAI_SLIMBUS_0_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUAT_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUINARY_MI2S_RX, + MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUIN_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUINARY_MI2S_RX, + MSM_BACKEND_DAI_QUINARY_MI2S_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("SEC_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUINARY_MI2S_RX, + MSM_BACKEND_DAI_SECONDARY_MI2S_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("SLIM_8_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUINARY_MI2S_RX, + MSM_BACKEND_DAI_SLIMBUS_8_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), +}; + +static const struct snd_kcontrol_new pri_tdm_rx_0_port_mixer_controls[] = { + SOC_DOUBLE_EXT("PRI_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_0, + MSM_BACKEND_DAI_PRI_MI2S_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("SEC_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_0, + MSM_BACKEND_DAI_SECONDARY_MI2S_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUAT_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_0, + MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("INTERNAL_FM_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_0, + MSM_BACKEND_DAI_INT_FM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("INTERNAL_BT_SCO_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_0, + MSM_BACKEND_DAI_INT_BT_SCO_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("AFE_PCM_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_0, + MSM_BACKEND_DAI_AFE_PCM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("AUX_PCM_UL_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_0, + MSM_BACKEND_DAI_AUXPCM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("SEC_AUX_PCM_UL_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_0, + MSM_BACKEND_DAI_SEC_AUXPCM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("PRI_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_0, + MSM_BACKEND_DAI_PRI_TDM_TX_0, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("PRI_TDM_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_0, + MSM_BACKEND_DAI_PRI_TDM_TX_1, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("PRI_TDM_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_0, + MSM_BACKEND_DAI_PRI_TDM_TX_2, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("PRI_TDM_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_0, + MSM_BACKEND_DAI_PRI_TDM_TX_3, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_0, + MSM_BACKEND_DAI_QUAT_TDM_TX_0, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_0, + MSM_BACKEND_DAI_QUAT_TDM_TX_1, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_0, + MSM_BACKEND_DAI_QUAT_TDM_TX_2, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_0, + MSM_BACKEND_DAI_QUAT_TDM_TX_3, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_0, + MSM_BACKEND_DAI_QUIN_TDM_TX_0, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_0, + MSM_BACKEND_DAI_QUIN_TDM_TX_1, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_0, + MSM_BACKEND_DAI_QUIN_TDM_TX_2, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_0, + MSM_BACKEND_DAI_QUIN_TDM_TX_3, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), +}; + +static const struct snd_kcontrol_new pri_tdm_rx_1_port_mixer_controls[] = { + SOC_DOUBLE_EXT("PRI_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_1, + MSM_BACKEND_DAI_PRI_MI2S_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("SEC_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_1, + MSM_BACKEND_DAI_SECONDARY_MI2S_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUAT_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_1, + MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("INTERNAL_FM_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_1, + MSM_BACKEND_DAI_INT_FM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("INTERNAL_BT_SCO_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_1, + MSM_BACKEND_DAI_INT_BT_SCO_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("AFE_PCM_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_1, + MSM_BACKEND_DAI_AFE_PCM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("AUX_PCM_UL_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_1, + MSM_BACKEND_DAI_AUXPCM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("SEC_AUX_PCM_UL_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_1, + MSM_BACKEND_DAI_SEC_AUXPCM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("PRI_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_1, + MSM_BACKEND_DAI_PRI_TDM_TX_0, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("PRI_TDM_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_1, + MSM_BACKEND_DAI_PRI_TDM_TX_1, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("PRI_TDM_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_1, + MSM_BACKEND_DAI_PRI_TDM_TX_2, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("PRI_TDM_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_1, + MSM_BACKEND_DAI_PRI_TDM_TX_3, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_1, + MSM_BACKEND_DAI_QUAT_TDM_TX_0, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_1, + MSM_BACKEND_DAI_QUAT_TDM_TX_1, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_1, + MSM_BACKEND_DAI_QUAT_TDM_TX_2, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_1, + MSM_BACKEND_DAI_QUAT_TDM_TX_3, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_1, + MSM_BACKEND_DAI_QUIN_TDM_TX_0, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_1, + MSM_BACKEND_DAI_QUIN_TDM_TX_1, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_1, + MSM_BACKEND_DAI_QUIN_TDM_TX_2, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_1, + MSM_BACKEND_DAI_QUIN_TDM_TX_3, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), +}; + +static const struct snd_kcontrol_new pri_tdm_rx_2_port_mixer_controls[] = { + SOC_DOUBLE_EXT("PRI_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_2, + MSM_BACKEND_DAI_PRI_MI2S_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("SEC_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_2, + MSM_BACKEND_DAI_SECONDARY_MI2S_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUAT_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_2, + MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("INTERNAL_FM_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_2, + MSM_BACKEND_DAI_INT_FM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("INTERNAL_BT_SCO_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_2, + MSM_BACKEND_DAI_INT_BT_SCO_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("AFE_PCM_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_2, + MSM_BACKEND_DAI_AFE_PCM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("AUX_PCM_UL_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_2, + MSM_BACKEND_DAI_AUXPCM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("SEC_AUX_PCM_UL_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_2, + MSM_BACKEND_DAI_SEC_AUXPCM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("PRI_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_2, + MSM_BACKEND_DAI_PRI_TDM_TX_0, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("PRI_TDM_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_2, + MSM_BACKEND_DAI_PRI_TDM_TX_1, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("PRI_TDM_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_2, + MSM_BACKEND_DAI_PRI_TDM_TX_2, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("PRI_TDM_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_2, + MSM_BACKEND_DAI_PRI_TDM_TX_3, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_2, + MSM_BACKEND_DAI_QUAT_TDM_TX_0, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_2, + MSM_BACKEND_DAI_QUAT_TDM_TX_1, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_2, + MSM_BACKEND_DAI_QUAT_TDM_TX_2, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_2, + MSM_BACKEND_DAI_QUAT_TDM_TX_3, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_2, + MSM_BACKEND_DAI_QUIN_TDM_TX_0, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_2, + MSM_BACKEND_DAI_QUIN_TDM_TX_1, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_2, + MSM_BACKEND_DAI_QUIN_TDM_TX_2, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_2, + MSM_BACKEND_DAI_QUIN_TDM_TX_3, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), +}; + +static const struct snd_kcontrol_new pri_tdm_rx_3_port_mixer_controls[] = { + SOC_DOUBLE_EXT("PRI_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_3, + MSM_BACKEND_DAI_PRI_MI2S_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("SEC_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_3, + MSM_BACKEND_DAI_SECONDARY_MI2S_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUAT_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_3, + MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("INTERNAL_FM_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_3, + MSM_BACKEND_DAI_INT_FM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("INTERNAL_BT_SCO_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_3, + MSM_BACKEND_DAI_INT_BT_SCO_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("AFE_PCM_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_3, + MSM_BACKEND_DAI_AFE_PCM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("AUX_PCM_UL_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_3, + MSM_BACKEND_DAI_AUXPCM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("SEC_AUX_PCM_UL_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_3, + MSM_BACKEND_DAI_SEC_AUXPCM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("PRI_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_3, + MSM_BACKEND_DAI_PRI_TDM_TX_0, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("PRI_TDM_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_3, + MSM_BACKEND_DAI_PRI_TDM_TX_1, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("PRI_TDM_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_3, + MSM_BACKEND_DAI_PRI_TDM_TX_2, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("PRI_TDM_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_3, + MSM_BACKEND_DAI_PRI_TDM_TX_3, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_3, + MSM_BACKEND_DAI_QUAT_TDM_TX_0, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_3, + MSM_BACKEND_DAI_QUAT_TDM_TX_1, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_3, + MSM_BACKEND_DAI_QUAT_TDM_TX_2, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_3, + MSM_BACKEND_DAI_QUAT_TDM_TX_3, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_3, + MSM_BACKEND_DAI_QUIN_TDM_TX_0, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_3, + MSM_BACKEND_DAI_QUIN_TDM_TX_1, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_3, + MSM_BACKEND_DAI_QUIN_TDM_TX_2, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_3, + MSM_BACKEND_DAI_QUIN_TDM_TX_3, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), +}; + +static const struct snd_kcontrol_new sec_tdm_rx_0_port_mixer_controls[] = { + SOC_DOUBLE_EXT("PRI_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_0, + MSM_BACKEND_DAI_PRI_MI2S_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("SEC_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_0, + MSM_BACKEND_DAI_SECONDARY_MI2S_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUAT_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_0, + MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("INTERNAL_FM_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_0, + MSM_BACKEND_DAI_INT_FM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("INTERNAL_BT_SCO_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_0, + MSM_BACKEND_DAI_INT_BT_SCO_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("AFE_PCM_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_0, + MSM_BACKEND_DAI_AFE_PCM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("AUX_PCM_UL_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_0, + MSM_BACKEND_DAI_AUXPCM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("SEC_AUX_PCM_UL_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_0, + MSM_BACKEND_DAI_SEC_AUXPCM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("SEC_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_0, + MSM_BACKEND_DAI_SEC_TDM_TX_0, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("SEC_TDM_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_0, + MSM_BACKEND_DAI_SEC_TDM_TX_1, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("SEC_TDM_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_0, + MSM_BACKEND_DAI_SEC_TDM_TX_2, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("SEC_TDM_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_0, + MSM_BACKEND_DAI_SEC_TDM_TX_3, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_0, + MSM_BACKEND_DAI_QUAT_TDM_TX_0, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_0, + MSM_BACKEND_DAI_QUAT_TDM_TX_1, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_0, + MSM_BACKEND_DAI_QUAT_TDM_TX_2, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_0, + MSM_BACKEND_DAI_QUAT_TDM_TX_3, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_0, + MSM_BACKEND_DAI_QUIN_TDM_TX_0, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_0, + MSM_BACKEND_DAI_QUIN_TDM_TX_1, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_0, + MSM_BACKEND_DAI_QUIN_TDM_TX_2, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_0, + MSM_BACKEND_DAI_QUIN_TDM_TX_3, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), +}; + +static const struct snd_kcontrol_new sec_tdm_rx_1_port_mixer_controls[] = { + SOC_DOUBLE_EXT("PRI_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_1, + MSM_BACKEND_DAI_PRI_MI2S_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("SEC_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_1, + MSM_BACKEND_DAI_SECONDARY_MI2S_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUAT_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_1, + MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("INTERNAL_FM_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_1, + MSM_BACKEND_DAI_INT_FM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("INTERNAL_BT_SCO_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_1, + MSM_BACKEND_DAI_INT_BT_SCO_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("AFE_PCM_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_1, + MSM_BACKEND_DAI_AFE_PCM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("AUX_PCM_UL_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_1, + MSM_BACKEND_DAI_AUXPCM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("SEC_AUX_PCM_UL_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_1, + MSM_BACKEND_DAI_SEC_AUXPCM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("SEC_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_1, + MSM_BACKEND_DAI_SEC_TDM_TX_0, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("SEC_TDM_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_1, + MSM_BACKEND_DAI_SEC_TDM_TX_1, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("SEC_TDM_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_1, + MSM_BACKEND_DAI_SEC_TDM_TX_2, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("SEC_TDM_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_1, + MSM_BACKEND_DAI_SEC_TDM_TX_3, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_1, + MSM_BACKEND_DAI_QUAT_TDM_TX_0, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_1, + MSM_BACKEND_DAI_QUAT_TDM_TX_1, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_1, + MSM_BACKEND_DAI_QUAT_TDM_TX_2, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_1, + MSM_BACKEND_DAI_QUAT_TDM_TX_3, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_1, + MSM_BACKEND_DAI_QUIN_TDM_TX_0, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_1, + MSM_BACKEND_DAI_QUIN_TDM_TX_1, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_1, + MSM_BACKEND_DAI_QUIN_TDM_TX_2, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_1, + MSM_BACKEND_DAI_QUIN_TDM_TX_3, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), +}; + +static const struct snd_kcontrol_new sec_tdm_rx_2_port_mixer_controls[] = { + SOC_DOUBLE_EXT("PRI_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_2, + MSM_BACKEND_DAI_PRI_MI2S_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("SEC_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_2, + MSM_BACKEND_DAI_SECONDARY_MI2S_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUAT_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_2, + MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("INTERNAL_FM_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_2, + MSM_BACKEND_DAI_INT_FM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("INTERNAL_BT_SCO_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_2, + MSM_BACKEND_DAI_INT_BT_SCO_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("AFE_PCM_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_2, + MSM_BACKEND_DAI_AFE_PCM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("AUX_PCM_UL_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_2, + MSM_BACKEND_DAI_AUXPCM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("SEC_AUX_PCM_UL_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_2, + MSM_BACKEND_DAI_SEC_AUXPCM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("SEC_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_2, + MSM_BACKEND_DAI_SEC_TDM_TX_0, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("SEC_TDM_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_2, + MSM_BACKEND_DAI_SEC_TDM_TX_1, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("SEC_TDM_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_2, + MSM_BACKEND_DAI_SEC_TDM_TX_2, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("SEC_TDM_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_2, + MSM_BACKEND_DAI_SEC_TDM_TX_3, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_2, + MSM_BACKEND_DAI_QUAT_TDM_TX_0, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_2, + MSM_BACKEND_DAI_QUAT_TDM_TX_1, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_2, + MSM_BACKEND_DAI_QUAT_TDM_TX_2, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_2, + MSM_BACKEND_DAI_QUAT_TDM_TX_3, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_2, + MSM_BACKEND_DAI_QUIN_TDM_TX_0, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_2, + MSM_BACKEND_DAI_QUIN_TDM_TX_1, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_2, + MSM_BACKEND_DAI_QUIN_TDM_TX_2, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_2, + MSM_BACKEND_DAI_QUIN_TDM_TX_3, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), +}; + +static const struct snd_kcontrol_new sec_tdm_rx_3_port_mixer_controls[] = { + SOC_DOUBLE_EXT("PRI_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_3, + MSM_BACKEND_DAI_PRI_MI2S_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("SEC_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_3, + MSM_BACKEND_DAI_SECONDARY_MI2S_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUAT_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_3, + MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("INTERNAL_FM_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_3, + MSM_BACKEND_DAI_INT_FM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("INTERNAL_BT_SCO_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_3, + MSM_BACKEND_DAI_INT_BT_SCO_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("AFE_PCM_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_3, + MSM_BACKEND_DAI_AFE_PCM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("AUX_PCM_UL_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_3, + MSM_BACKEND_DAI_AUXPCM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("SEC_AUX_PCM_UL_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_3, + MSM_BACKEND_DAI_SEC_AUXPCM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("SEC_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_3, + MSM_BACKEND_DAI_SEC_TDM_TX_0, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("SEC_TDM_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_3, + MSM_BACKEND_DAI_SEC_TDM_TX_1, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("SEC_TDM_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_3, + MSM_BACKEND_DAI_SEC_TDM_TX_2, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("SEC_TDM_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_3, + MSM_BACKEND_DAI_SEC_TDM_TX_3, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_3, + MSM_BACKEND_DAI_QUAT_TDM_TX_0, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_3, + MSM_BACKEND_DAI_QUAT_TDM_TX_1, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_3, + MSM_BACKEND_DAI_QUAT_TDM_TX_2, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_3, + MSM_BACKEND_DAI_QUAT_TDM_TX_3, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_3, + MSM_BACKEND_DAI_QUIN_TDM_TX_0, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_3, + MSM_BACKEND_DAI_QUIN_TDM_TX_1, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_3, + MSM_BACKEND_DAI_QUIN_TDM_TX_2, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_3, + MSM_BACKEND_DAI_QUIN_TDM_TX_3, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), +}; + +static const struct snd_kcontrol_new sec_tdm_rx_7_port_mixer_controls[] = { + SOC_DOUBLE_EXT("TERT_TDM_TX_7", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_7, + MSM_BACKEND_DAI_TERT_TDM_TX_7, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), +}; + +static const struct snd_kcontrol_new tert_tdm_rx_0_port_mixer_controls[] = { + SOC_DOUBLE_EXT("PRI_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_0, + MSM_BACKEND_DAI_PRI_MI2S_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("SEC_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_0, + MSM_BACKEND_DAI_SECONDARY_MI2S_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUAT_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_0, + MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("INTERNAL_FM_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_0, + MSM_BACKEND_DAI_INT_FM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("INTERNAL_BT_SCO_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_0, + MSM_BACKEND_DAI_INT_BT_SCO_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("AFE_PCM_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_0, + MSM_BACKEND_DAI_AFE_PCM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("AUX_PCM_UL_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_0, + MSM_BACKEND_DAI_AUXPCM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("SEC_AUX_PCM_UL_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_0, + MSM_BACKEND_DAI_SEC_AUXPCM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("TERT_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_0, + MSM_BACKEND_DAI_TERT_TDM_TX_0, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("TERT_TDM_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_0, + MSM_BACKEND_DAI_TERT_TDM_TX_1, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("TERT_TDM_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_0, + MSM_BACKEND_DAI_TERT_TDM_TX_2, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("TERT_TDM_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_0, + MSM_BACKEND_DAI_TERT_TDM_TX_3, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_0, + MSM_BACKEND_DAI_QUAT_TDM_TX_0, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_0, + MSM_BACKEND_DAI_QUAT_TDM_TX_1, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_0, + MSM_BACKEND_DAI_QUAT_TDM_TX_2, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_0, + MSM_BACKEND_DAI_QUAT_TDM_TX_3, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_0, + MSM_BACKEND_DAI_QUIN_TDM_TX_0, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_0, + MSM_BACKEND_DAI_QUIN_TDM_TX_1, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_0, + MSM_BACKEND_DAI_QUIN_TDM_TX_2, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_0, + MSM_BACKEND_DAI_QUIN_TDM_TX_3, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), +}; + +static const struct snd_kcontrol_new tert_tdm_rx_1_port_mixer_controls[] = { + SOC_DOUBLE_EXT("PRI_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_1, + MSM_BACKEND_DAI_PRI_MI2S_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("SEC_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_1, + MSM_BACKEND_DAI_SECONDARY_MI2S_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUAT_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_1, + MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("INTERNAL_FM_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_1, + MSM_BACKEND_DAI_INT_FM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("INTERNAL_BT_SCO_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_1, + MSM_BACKEND_DAI_INT_BT_SCO_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("AFE_PCM_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_1, + MSM_BACKEND_DAI_AFE_PCM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("AUX_PCM_UL_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_1, + MSM_BACKEND_DAI_AUXPCM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("SEC_AUX_PCM_UL_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_1, + MSM_BACKEND_DAI_SEC_AUXPCM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("TERT_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_1, + MSM_BACKEND_DAI_TERT_TDM_TX_0, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("TERT_TDM_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_1, + MSM_BACKEND_DAI_TERT_TDM_TX_1, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("TERT_TDM_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_1, + MSM_BACKEND_DAI_TERT_TDM_TX_2, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("TERT_TDM_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_1, + MSM_BACKEND_DAI_TERT_TDM_TX_3, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_1, + MSM_BACKEND_DAI_QUAT_TDM_TX_0, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_1, + MSM_BACKEND_DAI_QUAT_TDM_TX_1, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_1, + MSM_BACKEND_DAI_QUAT_TDM_TX_2, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_1, + MSM_BACKEND_DAI_QUAT_TDM_TX_3, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_1, + MSM_BACKEND_DAI_QUIN_TDM_TX_0, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_1, + MSM_BACKEND_DAI_QUIN_TDM_TX_1, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_1, + MSM_BACKEND_DAI_QUIN_TDM_TX_2, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_1, + MSM_BACKEND_DAI_QUIN_TDM_TX_3, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), +}; + +static const struct snd_kcontrol_new tert_tdm_rx_2_port_mixer_controls[] = { + SOC_DOUBLE_EXT("PRI_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_2, + MSM_BACKEND_DAI_PRI_MI2S_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("SEC_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_2, + MSM_BACKEND_DAI_SECONDARY_MI2S_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUAT_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_2, + MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("INTERNAL_FM_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_2, + MSM_BACKEND_DAI_INT_FM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("INTERNAL_BT_SCO_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_2, + MSM_BACKEND_DAI_INT_BT_SCO_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("AFE_PCM_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_2, + MSM_BACKEND_DAI_AFE_PCM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("AUX_PCM_UL_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_2, + MSM_BACKEND_DAI_AUXPCM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("SEC_AUX_PCM_UL_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_2, + MSM_BACKEND_DAI_SEC_AUXPCM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("TERT_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_2, + MSM_BACKEND_DAI_TERT_TDM_TX_0, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("TERT_TDM_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_2, + MSM_BACKEND_DAI_TERT_TDM_TX_1, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("TERT_TDM_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_2, + MSM_BACKEND_DAI_TERT_TDM_TX_2, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("TERT_TDM_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_2, + MSM_BACKEND_DAI_TERT_TDM_TX_3, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_2, + MSM_BACKEND_DAI_QUAT_TDM_TX_0, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_2, + MSM_BACKEND_DAI_QUAT_TDM_TX_1, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_2, + MSM_BACKEND_DAI_QUAT_TDM_TX_2, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_2, + MSM_BACKEND_DAI_QUAT_TDM_TX_3, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_2, + MSM_BACKEND_DAI_QUIN_TDM_TX_0, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_2, + MSM_BACKEND_DAI_QUIN_TDM_TX_1, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_2, + MSM_BACKEND_DAI_QUIN_TDM_TX_2, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_2, + MSM_BACKEND_DAI_QUIN_TDM_TX_3, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), +}; + +static const struct snd_kcontrol_new tert_tdm_rx_3_port_mixer_controls[] = { + SOC_DOUBLE_EXT("PRI_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_3, + MSM_BACKEND_DAI_PRI_MI2S_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("SEC_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_3, + MSM_BACKEND_DAI_SECONDARY_MI2S_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUAT_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_3, + MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("INTERNAL_FM_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_3, + MSM_BACKEND_DAI_INT_FM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("INTERNAL_BT_SCO_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_3, + MSM_BACKEND_DAI_INT_BT_SCO_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("AFE_PCM_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_3, + MSM_BACKEND_DAI_AFE_PCM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("AUX_PCM_UL_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_3, + MSM_BACKEND_DAI_AUXPCM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("SEC_AUX_PCM_UL_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_3, + MSM_BACKEND_DAI_SEC_AUXPCM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("TERT_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_3, + MSM_BACKEND_DAI_TERT_TDM_TX_0, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("TERT_TDM_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_3, + MSM_BACKEND_DAI_TERT_TDM_TX_1, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("TERT_TDM_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_3, + MSM_BACKEND_DAI_TERT_TDM_TX_2, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("TERT_TDM_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_3, + MSM_BACKEND_DAI_TERT_TDM_TX_3, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_3, + MSM_BACKEND_DAI_QUAT_TDM_TX_0, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_3, + MSM_BACKEND_DAI_QUAT_TDM_TX_1, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_3, + MSM_BACKEND_DAI_QUAT_TDM_TX_2, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_3, + MSM_BACKEND_DAI_QUAT_TDM_TX_3, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_3, + MSM_BACKEND_DAI_QUIN_TDM_TX_0, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_3, + MSM_BACKEND_DAI_QUIN_TDM_TX_1, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_3, + MSM_BACKEND_DAI_QUIN_TDM_TX_2, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_3, + MSM_BACKEND_DAI_QUIN_TDM_TX_3, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), +}; + +static const struct snd_kcontrol_new quat_tdm_rx_0_port_mixer_controls[] = { + SOC_DOUBLE_EXT("PRI_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_0, + MSM_BACKEND_DAI_PRI_MI2S_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("SEC_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_0, + MSM_BACKEND_DAI_SECONDARY_MI2S_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("TERT_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_0, + MSM_BACKEND_DAI_TERTIARY_MI2S_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("INTERNAL_FM_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_0, + MSM_BACKEND_DAI_INT_FM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("INTERNAL_BT_SCO_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_0, + MSM_BACKEND_DAI_INT_BT_SCO_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("AFE_PCM_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_0, + MSM_BACKEND_DAI_AFE_PCM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("AUX_PCM_UL_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_0, + MSM_BACKEND_DAI_AUXPCM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("SEC_AUX_PCM_UL_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_0, + MSM_BACKEND_DAI_SEC_AUXPCM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("TERT_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_0, + MSM_BACKEND_DAI_TERT_TDM_TX_0, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("TERT_TDM_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_0, + MSM_BACKEND_DAI_TERT_TDM_TX_1, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("TERT_TDM_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_0, + MSM_BACKEND_DAI_TERT_TDM_TX_2, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("TERT_TDM_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_0, + MSM_BACKEND_DAI_TERT_TDM_TX_3, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_0, + MSM_BACKEND_DAI_QUAT_TDM_TX_0, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_0, + MSM_BACKEND_DAI_QUAT_TDM_TX_1, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_0, + MSM_BACKEND_DAI_QUAT_TDM_TX_2, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_0, + MSM_BACKEND_DAI_QUAT_TDM_TX_3, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_0, + MSM_BACKEND_DAI_QUIN_TDM_TX_0, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_0, + MSM_BACKEND_DAI_QUIN_TDM_TX_1, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_0, + MSM_BACKEND_DAI_QUIN_TDM_TX_2, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_0, + MSM_BACKEND_DAI_QUIN_TDM_TX_3, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), +}; + +static const struct snd_kcontrol_new quat_tdm_rx_1_port_mixer_controls[] = { + SOC_DOUBLE_EXT("PRI_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_1, + MSM_BACKEND_DAI_PRI_MI2S_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("SEC_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_1, + MSM_BACKEND_DAI_SECONDARY_MI2S_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("TERT_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_1, + MSM_BACKEND_DAI_TERTIARY_MI2S_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("INTERNAL_FM_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_1, + MSM_BACKEND_DAI_INT_FM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("INTERNAL_BT_SCO_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_1, + MSM_BACKEND_DAI_INT_BT_SCO_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("AFE_PCM_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_1, + MSM_BACKEND_DAI_AFE_PCM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("AUX_PCM_UL_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_1, + MSM_BACKEND_DAI_AUXPCM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("SEC_AUX_PCM_UL_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_1, + MSM_BACKEND_DAI_SEC_AUXPCM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("TERT_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_1, + MSM_BACKEND_DAI_TERT_TDM_TX_0, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("TERT_TDM_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_1, + MSM_BACKEND_DAI_TERT_TDM_TX_1, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("TERT_TDM_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_1, + MSM_BACKEND_DAI_TERT_TDM_TX_2, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("TERT_TDM_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_1, + MSM_BACKEND_DAI_TERT_TDM_TX_3, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_1, + MSM_BACKEND_DAI_QUAT_TDM_TX_0, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_1, + MSM_BACKEND_DAI_QUAT_TDM_TX_1, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_1, + MSM_BACKEND_DAI_QUAT_TDM_TX_2, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_1, + MSM_BACKEND_DAI_QUAT_TDM_TX_3, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_1, + MSM_BACKEND_DAI_QUIN_TDM_TX_0, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_1, + MSM_BACKEND_DAI_QUIN_TDM_TX_1, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_1, + MSM_BACKEND_DAI_QUIN_TDM_TX_2, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_1, + MSM_BACKEND_DAI_QUIN_TDM_TX_3, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), +}; + +static const struct snd_kcontrol_new quat_tdm_rx_2_port_mixer_controls[] = { + SOC_DOUBLE_EXT("PRI_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_2, + MSM_BACKEND_DAI_PRI_MI2S_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("SEC_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_2, + MSM_BACKEND_DAI_SECONDARY_MI2S_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("TERT_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_2, + MSM_BACKEND_DAI_TERTIARY_MI2S_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("INTERNAL_FM_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_2, + MSM_BACKEND_DAI_INT_FM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("INTERNAL_BT_SCO_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_2, + MSM_BACKEND_DAI_INT_BT_SCO_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("AFE_PCM_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_2, + MSM_BACKEND_DAI_AFE_PCM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("AUX_PCM_UL_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_2, + MSM_BACKEND_DAI_AUXPCM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("SEC_AUX_PCM_UL_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_2, + MSM_BACKEND_DAI_SEC_AUXPCM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("TERT_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_2, + MSM_BACKEND_DAI_TERT_TDM_TX_0, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("TERT_TDM_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_2, + MSM_BACKEND_DAI_TERT_TDM_TX_1, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("TERT_TDM_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_2, + MSM_BACKEND_DAI_TERT_TDM_TX_2, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("TERT_TDM_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_2, + MSM_BACKEND_DAI_TERT_TDM_TX_3, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_2, + MSM_BACKEND_DAI_QUAT_TDM_TX_0, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_2, + MSM_BACKEND_DAI_QUAT_TDM_TX_1, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_2, + MSM_BACKEND_DAI_QUAT_TDM_TX_2, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_2, + MSM_BACKEND_DAI_QUAT_TDM_TX_3, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_2, + MSM_BACKEND_DAI_QUIN_TDM_TX_0, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_2, + MSM_BACKEND_DAI_QUIN_TDM_TX_1, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_2, + MSM_BACKEND_DAI_QUIN_TDM_TX_2, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_2, + MSM_BACKEND_DAI_QUIN_TDM_TX_3, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), +}; + +static const struct snd_kcontrol_new quat_tdm_rx_3_port_mixer_controls[] = { + SOC_DOUBLE_EXT("PRI_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_3, + MSM_BACKEND_DAI_PRI_MI2S_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("SEC_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_3, + MSM_BACKEND_DAI_SECONDARY_MI2S_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("TERT_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_3, + MSM_BACKEND_DAI_TERTIARY_MI2S_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("INTERNAL_FM_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_3, + MSM_BACKEND_DAI_INT_FM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("INTERNAL_BT_SCO_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_3, + MSM_BACKEND_DAI_INT_BT_SCO_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("AFE_PCM_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_3, + MSM_BACKEND_DAI_AFE_PCM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("AUX_PCM_UL_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_3, + MSM_BACKEND_DAI_AUXPCM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("SEC_AUX_PCM_UL_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_3, + MSM_BACKEND_DAI_SEC_AUXPCM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("TERT_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_3, + MSM_BACKEND_DAI_TERT_TDM_TX_0, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("TERT_TDM_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_3, + MSM_BACKEND_DAI_TERT_TDM_TX_1, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("TERT_TDM_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_3, + MSM_BACKEND_DAI_TERT_TDM_TX_2, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("TERT_TDM_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_3, + MSM_BACKEND_DAI_TERT_TDM_TX_3, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_3, + MSM_BACKEND_DAI_QUAT_TDM_TX_0, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_3, + MSM_BACKEND_DAI_QUAT_TDM_TX_1, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_3, + MSM_BACKEND_DAI_QUAT_TDM_TX_2, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_3, + MSM_BACKEND_DAI_QUAT_TDM_TX_3, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_3, + MSM_BACKEND_DAI_QUIN_TDM_TX_0, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_3, + MSM_BACKEND_DAI_QUIN_TDM_TX_1, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_3, + MSM_BACKEND_DAI_QUIN_TDM_TX_2, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_3, + MSM_BACKEND_DAI_QUIN_TDM_TX_3, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), +}; + +static const struct snd_kcontrol_new quin_tdm_rx_0_port_mixer_controls[] = { + SOC_DOUBLE_EXT("PRI_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_0, + MSM_BACKEND_DAI_PRI_MI2S_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("SEC_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_0, + MSM_BACKEND_DAI_SECONDARY_MI2S_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("TERT_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_0, + MSM_BACKEND_DAI_TERTIARY_MI2S_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("INTERNAL_FM_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_0, + MSM_BACKEND_DAI_INT_FM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("INTERNAL_BT_SCO_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_0, + MSM_BACKEND_DAI_INT_BT_SCO_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("AFE_PCM_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_0, + MSM_BACKEND_DAI_AFE_PCM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("AUX_PCM_UL_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_0, + MSM_BACKEND_DAI_AUXPCM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("SEC_AUX_PCM_UL_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_0, + MSM_BACKEND_DAI_SEC_AUXPCM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("TERT_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_0, + MSM_BACKEND_DAI_TERT_TDM_TX_0, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("TERT_TDM_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_0, + MSM_BACKEND_DAI_TERT_TDM_TX_1, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("TERT_TDM_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_0, + MSM_BACKEND_DAI_TERT_TDM_TX_2, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("TERT_TDM_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_0, + MSM_BACKEND_DAI_TERT_TDM_TX_3, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_0, + MSM_BACKEND_DAI_QUAT_TDM_TX_0, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_0, + MSM_BACKEND_DAI_QUAT_TDM_TX_1, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_0, + MSM_BACKEND_DAI_QUAT_TDM_TX_2, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_0, + MSM_BACKEND_DAI_QUAT_TDM_TX_3, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_0, + MSM_BACKEND_DAI_QUIN_TDM_TX_0, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_0, + MSM_BACKEND_DAI_QUIN_TDM_TX_1, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_0, + MSM_BACKEND_DAI_QUIN_TDM_TX_2, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_0, + MSM_BACKEND_DAI_QUIN_TDM_TX_3, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), +}; + +static const struct snd_kcontrol_new quin_tdm_rx_1_port_mixer_controls[] = { + SOC_DOUBLE_EXT("PRI_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_1, + MSM_BACKEND_DAI_PRI_MI2S_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("SEC_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_1, + MSM_BACKEND_DAI_SECONDARY_MI2S_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("TERT_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_1, + MSM_BACKEND_DAI_TERTIARY_MI2S_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("INTERNAL_FM_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_1, + MSM_BACKEND_DAI_INT_FM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("INTERNAL_BT_SCO_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_1, + MSM_BACKEND_DAI_INT_BT_SCO_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("AFE_PCM_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_1, + MSM_BACKEND_DAI_AFE_PCM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("AUX_PCM_UL_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_1, + MSM_BACKEND_DAI_AUXPCM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("SEC_AUX_PCM_UL_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_1, + MSM_BACKEND_DAI_SEC_AUXPCM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("TERT_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_1, + MSM_BACKEND_DAI_TERT_TDM_TX_0, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("TERT_TDM_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_1, + MSM_BACKEND_DAI_TERT_TDM_TX_1, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("TERT_TDM_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_1, + MSM_BACKEND_DAI_TERT_TDM_TX_2, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("TERT_TDM_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_1, + MSM_BACKEND_DAI_TERT_TDM_TX_3, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_1, + MSM_BACKEND_DAI_QUAT_TDM_TX_0, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_1, + MSM_BACKEND_DAI_QUAT_TDM_TX_1, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_1, + MSM_BACKEND_DAI_QUAT_TDM_TX_2, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_1, + MSM_BACKEND_DAI_QUAT_TDM_TX_3, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_1, + MSM_BACKEND_DAI_QUIN_TDM_TX_0, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_1, + MSM_BACKEND_DAI_QUIN_TDM_TX_1, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_1, + MSM_BACKEND_DAI_QUIN_TDM_TX_2, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_1, + MSM_BACKEND_DAI_QUIN_TDM_TX_3, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), +}; + +static const struct snd_kcontrol_new quin_tdm_rx_2_port_mixer_controls[] = { + SOC_DOUBLE_EXT("PRI_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_2, + MSM_BACKEND_DAI_PRI_MI2S_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("SEC_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_2, + MSM_BACKEND_DAI_SECONDARY_MI2S_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("TERT_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_2, + MSM_BACKEND_DAI_TERTIARY_MI2S_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("INTERNAL_FM_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_2, + MSM_BACKEND_DAI_INT_FM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("INTERNAL_BT_SCO_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_2, + MSM_BACKEND_DAI_INT_BT_SCO_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("AFE_PCM_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_2, + MSM_BACKEND_DAI_AFE_PCM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("AUX_PCM_UL_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_2, + MSM_BACKEND_DAI_AUXPCM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("SEC_AUX_PCM_UL_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_2, + MSM_BACKEND_DAI_SEC_AUXPCM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("TERT_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_2, + MSM_BACKEND_DAI_TERT_TDM_TX_0, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("TERT_TDM_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_2, + MSM_BACKEND_DAI_TERT_TDM_TX_1, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("TERT_TDM_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_2, + MSM_BACKEND_DAI_TERT_TDM_TX_2, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("TERT_TDM_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_2, + MSM_BACKEND_DAI_TERT_TDM_TX_3, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_2, + MSM_BACKEND_DAI_QUAT_TDM_TX_0, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_2, + MSM_BACKEND_DAI_QUAT_TDM_TX_1, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_2, + MSM_BACKEND_DAI_QUAT_TDM_TX_2, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_2, + MSM_BACKEND_DAI_QUAT_TDM_TX_3, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_2, + MSM_BACKEND_DAI_QUIN_TDM_TX_0, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_2, + MSM_BACKEND_DAI_QUIN_TDM_TX_1, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_2, + MSM_BACKEND_DAI_QUIN_TDM_TX_2, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_2, + MSM_BACKEND_DAI_QUIN_TDM_TX_3, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), +}; + +static const struct snd_kcontrol_new quin_tdm_rx_3_port_mixer_controls[] = { + SOC_DOUBLE_EXT("PRI_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_3, + MSM_BACKEND_DAI_PRI_MI2S_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("SEC_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_3, + MSM_BACKEND_DAI_SECONDARY_MI2S_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("TERT_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_3, + MSM_BACKEND_DAI_TERTIARY_MI2S_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("INTERNAL_FM_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_3, + MSM_BACKEND_DAI_INT_FM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("INTERNAL_BT_SCO_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_3, + MSM_BACKEND_DAI_INT_BT_SCO_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("AFE_PCM_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_3, + MSM_BACKEND_DAI_AFE_PCM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("AUX_PCM_UL_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_3, + MSM_BACKEND_DAI_AUXPCM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("SEC_AUX_PCM_UL_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_3, + MSM_BACKEND_DAI_SEC_AUXPCM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("TERT_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_3, + MSM_BACKEND_DAI_TERT_TDM_TX_0, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("TERT_TDM_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_3, + MSM_BACKEND_DAI_TERT_TDM_TX_1, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("TERT_TDM_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_3, + MSM_BACKEND_DAI_TERT_TDM_TX_2, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("TERT_TDM_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_3, + MSM_BACKEND_DAI_TERT_TDM_TX_3, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_3, + MSM_BACKEND_DAI_QUAT_TDM_TX_0, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_3, + MSM_BACKEND_DAI_QUAT_TDM_TX_1, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_3, + MSM_BACKEND_DAI_QUAT_TDM_TX_2, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUAT_TDM_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_3, + MSM_BACKEND_DAI_QUAT_TDM_TX_3, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_3, + MSM_BACKEND_DAI_QUIN_TDM_TX_0, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_3, + MSM_BACKEND_DAI_QUIN_TDM_TX_1, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_2", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_3, + MSM_BACKEND_DAI_QUIN_TDM_TX_2, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_3, + MSM_BACKEND_DAI_QUIN_TDM_TX_3, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), +}; + +static const struct snd_kcontrol_new tert_mi2s_rx_port_mixer_controls[] = { + SOC_DOUBLE_EXT("PRI_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERTIARY_MI2S_RX, + MSM_BACKEND_DAI_PRI_MI2S_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("TERT_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERTIARY_MI2S_RX, + MSM_BACKEND_DAI_TERTIARY_MI2S_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("SLIM_0_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERTIARY_MI2S_RX, + MSM_BACKEND_DAI_SLIMBUS_0_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUAT_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERTIARY_MI2S_RX, + MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUIN_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERTIARY_MI2S_RX, + MSM_BACKEND_DAI_QUINARY_MI2S_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("SEC_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERTIARY_MI2S_RX, + MSM_BACKEND_DAI_SECONDARY_MI2S_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("SLIM_8_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERTIARY_MI2S_RX, + MSM_BACKEND_DAI_SLIMBUS_8_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), +}; + +static const struct snd_kcontrol_new sec_mi2s_rx_port_mixer_controls[] = { + SOC_DOUBLE_EXT("PRI_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SECONDARY_MI2S_RX, + MSM_BACKEND_DAI_PRI_MI2S_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("SEC_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SECONDARY_MI2S_RX, + MSM_BACKEND_DAI_SECONDARY_MI2S_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("TERT_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SECONDARY_MI2S_RX, + MSM_BACKEND_DAI_TERTIARY_MI2S_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUAT_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SECONDARY_MI2S_RX, + MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("QUIN_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SECONDARY_MI2S_RX, + MSM_BACKEND_DAI_QUINARY_MI2S_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("SLIM_0_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SECONDARY_MI2S_RX, + MSM_BACKEND_DAI_SLIMBUS_0_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("INTERNAL_FM_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SECONDARY_MI2S_RX, + MSM_BACKEND_DAI_INT_FM_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("SLIM_8_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SECONDARY_MI2S_RX, + MSM_BACKEND_DAI_SLIMBUS_8_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_DOUBLE_EXT("AUX_PCM_UL_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SECONDARY_MI2S_RX, + MSM_BACKEND_DAI_AUXPCM_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), +}; + +static const struct snd_kcontrol_new lsm1_mixer_controls[] = { + SOC_DOUBLE_EXT("SLIMBUS_0_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_0_TX, + MSM_FRONTEND_DAI_LSM1, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_DOUBLE_EXT("SLIMBUS_1_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_1_TX, + MSM_FRONTEND_DAI_LSM1, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_DOUBLE_EXT("SLIMBUS_3_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_3_TX, + MSM_FRONTEND_DAI_LSM1, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_DOUBLE_EXT("SLIMBUS_4_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_4_TX, + MSM_FRONTEND_DAI_LSM1, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_DOUBLE_EXT("SLIMBUS_5_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_5_TX, + MSM_FRONTEND_DAI_LSM1, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_DOUBLE_EXT("TERT_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERTIARY_MI2S_TX, + MSM_FRONTEND_DAI_LSM1, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_DOUBLE_EXT("QUAT_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, + MSM_FRONTEND_DAI_LSM1, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_DOUBLE_EXT("INT3_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT3_MI2S_TX, + MSM_FRONTEND_DAI_LSM1, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_DOUBLE_EXT("VA_CDC_DMA_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_VA_CDC_DMA_TX_0, + MSM_FRONTEND_DAI_LSM1, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_DOUBLE_EXT("VA_CDC_DMA_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_VA_CDC_DMA_TX_1, + MSM_FRONTEND_DAI_LSM1, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_3, + MSM_FRONTEND_DAI_LSM1, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_TX_0, + MSM_FRONTEND_DAI_LSM1, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), +}; + +static const struct snd_kcontrol_new lsm2_mixer_controls[] = { + SOC_DOUBLE_EXT("SLIMBUS_0_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_0_TX, + MSM_FRONTEND_DAI_LSM2, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_DOUBLE_EXT("SLIMBUS_1_TX", + SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_1_TX, + MSM_FRONTEND_DAI_LSM2, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_DOUBLE_EXT("SLIMBUS_3_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_3_TX, + MSM_FRONTEND_DAI_LSM2, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_DOUBLE_EXT("SLIMBUS_4_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_4_TX, + MSM_FRONTEND_DAI_LSM2, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_DOUBLE_EXT("SLIMBUS_5_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_5_TX, + MSM_FRONTEND_DAI_LSM2, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_DOUBLE_EXT("TERT_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERTIARY_MI2S_TX, + MSM_FRONTEND_DAI_LSM2, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_DOUBLE_EXT("QUAT_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, + MSM_FRONTEND_DAI_LSM2, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_DOUBLE_EXT("INT3_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT3_MI2S_TX, + MSM_FRONTEND_DAI_LSM2, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_DOUBLE_EXT("VA_CDC_DMA_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_VA_CDC_DMA_TX_0, + MSM_FRONTEND_DAI_LSM2, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_DOUBLE_EXT("VA_CDC_DMA_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_VA_CDC_DMA_TX_1, + MSM_FRONTEND_DAI_LSM2, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_3, + MSM_FRONTEND_DAI_LSM2, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_TX_0, + MSM_FRONTEND_DAI_LSM2, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), +}; + +static const struct snd_kcontrol_new lsm3_mixer_controls[] = { + SOC_DOUBLE_EXT("SLIMBUS_0_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_0_TX, + MSM_FRONTEND_DAI_LSM3, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_DOUBLE_EXT("SLIMBUS_1_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_1_TX, + MSM_FRONTEND_DAI_LSM3, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_DOUBLE_EXT("SLIMBUS_3_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_3_TX, + MSM_FRONTEND_DAI_LSM3, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_DOUBLE_EXT("SLIMBUS_4_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_4_TX, + MSM_FRONTEND_DAI_LSM3, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_DOUBLE_EXT("SLIMBUS_5_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_5_TX, + MSM_FRONTEND_DAI_LSM3, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_DOUBLE_EXT("TERT_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERTIARY_MI2S_TX, + MSM_FRONTEND_DAI_LSM3, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_DOUBLE_EXT("QUAT_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, + MSM_FRONTEND_DAI_LSM3, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_DOUBLE_EXT("INT3_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT3_MI2S_TX, + MSM_FRONTEND_DAI_LSM3, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_DOUBLE_EXT("VA_CDC_DMA_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_VA_CDC_DMA_TX_0, + MSM_FRONTEND_DAI_LSM3, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_DOUBLE_EXT("VA_CDC_DMA_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_VA_CDC_DMA_TX_1, + MSM_FRONTEND_DAI_LSM3, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_3, + MSM_FRONTEND_DAI_LSM3, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_TX_0, + MSM_FRONTEND_DAI_LSM3, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), +}; + +static const struct snd_kcontrol_new lsm4_mixer_controls[] = { + SOC_DOUBLE_EXT("SLIMBUS_0_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_0_TX, + MSM_FRONTEND_DAI_LSM4, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_DOUBLE_EXT("SLIMBUS_1_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_1_TX, + MSM_FRONTEND_DAI_LSM4, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_DOUBLE_EXT("SLIMBUS_3_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_3_TX, + MSM_FRONTEND_DAI_LSM4, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_DOUBLE_EXT("SLIMBUS_4_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_4_TX, + MSM_FRONTEND_DAI_LSM4, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_DOUBLE_EXT("SLIMBUS_5_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_5_TX, + MSM_FRONTEND_DAI_LSM4, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_DOUBLE_EXT("TERT_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERTIARY_MI2S_TX, + MSM_FRONTEND_DAI_LSM4, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_DOUBLE_EXT("QUAT_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, + MSM_FRONTEND_DAI_LSM4, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_DOUBLE_EXT("INT3_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT3_MI2S_TX, + MSM_FRONTEND_DAI_LSM4, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_DOUBLE_EXT("VA_CDC_DMA_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_VA_CDC_DMA_TX_0, + MSM_FRONTEND_DAI_LSM4, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_DOUBLE_EXT("VA_CDC_DMA_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_VA_CDC_DMA_TX_1, + MSM_FRONTEND_DAI_LSM4, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_3, + MSM_FRONTEND_DAI_LSM4, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_TX_0, + MSM_FRONTEND_DAI_LSM4, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), +}; + +static const struct snd_kcontrol_new lsm5_mixer_controls[] = { + SOC_DOUBLE_EXT("SLIMBUS_0_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_0_TX, + MSM_FRONTEND_DAI_LSM5, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_DOUBLE_EXT("SLIMBUS_1_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_1_TX, + MSM_FRONTEND_DAI_LSM5, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_DOUBLE_EXT("SLIMBUS_3_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_3_TX, + MSM_FRONTEND_DAI_LSM5, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_DOUBLE_EXT("SLIMBUS_4_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_4_TX, + MSM_FRONTEND_DAI_LSM5, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_DOUBLE_EXT("SLIMBUS_5_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_5_TX, + MSM_FRONTEND_DAI_LSM5, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_DOUBLE_EXT("TERT_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERTIARY_MI2S_TX, + MSM_FRONTEND_DAI_LSM5, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_DOUBLE_EXT("QUAT_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, + MSM_FRONTEND_DAI_LSM5, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_DOUBLE_EXT("INT3_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT3_MI2S_TX, + MSM_FRONTEND_DAI_LSM5, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_DOUBLE_EXT("VA_CDC_DMA_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_VA_CDC_DMA_TX_0, + MSM_FRONTEND_DAI_LSM5, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_DOUBLE_EXT("VA_CDC_DMA_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_VA_CDC_DMA_TX_1, + MSM_FRONTEND_DAI_LSM5, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_3, + MSM_FRONTEND_DAI_LSM5, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_TX_0, + MSM_FRONTEND_DAI_LSM5, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), +}; + +static const struct snd_kcontrol_new lsm6_mixer_controls[] = { + SOC_DOUBLE_EXT("SLIMBUS_0_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_0_TX, + MSM_FRONTEND_DAI_LSM6, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_DOUBLE_EXT("SLIMBUS_1_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_1_TX, + MSM_FRONTEND_DAI_LSM6, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_DOUBLE_EXT("SLIMBUS_3_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_3_TX, + MSM_FRONTEND_DAI_LSM6, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_DOUBLE_EXT("SLIMBUS_4_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_4_TX, + MSM_FRONTEND_DAI_LSM6, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_DOUBLE_EXT("SLIMBUS_5_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_5_TX, + MSM_FRONTEND_DAI_LSM6, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_DOUBLE_EXT("TERT_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERTIARY_MI2S_TX, + MSM_FRONTEND_DAI_LSM6, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_DOUBLE_EXT("QUAT_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, + MSM_FRONTEND_DAI_LSM6, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_DOUBLE_EXT("INT3_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT3_MI2S_TX, + MSM_FRONTEND_DAI_LSM6, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_DOUBLE_EXT("VA_CDC_DMA_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_VA_CDC_DMA_TX_0, + MSM_FRONTEND_DAI_LSM6, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_DOUBLE_EXT("VA_CDC_DMA_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_VA_CDC_DMA_TX_1, + MSM_FRONTEND_DAI_LSM6, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_3, + MSM_FRONTEND_DAI_LSM6, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_TX_0, + MSM_FRONTEND_DAI_LSM6, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), +}; + +static const struct snd_kcontrol_new lsm7_mixer_controls[] = { + SOC_DOUBLE_EXT("SLIMBUS_0_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_0_TX, + MSM_FRONTEND_DAI_LSM7, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_DOUBLE_EXT("SLIMBUS_1_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_1_TX, + MSM_FRONTEND_DAI_LSM7, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_DOUBLE_EXT("SLIMBUS_3_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_3_TX, + MSM_FRONTEND_DAI_LSM7, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_DOUBLE_EXT("SLIMBUS_4_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_4_TX, + MSM_FRONTEND_DAI_LSM7, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_DOUBLE_EXT("SLIMBUS_5_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_5_TX, + MSM_FRONTEND_DAI_LSM7, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_DOUBLE_EXT("TERT_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERTIARY_MI2S_TX, + MSM_FRONTEND_DAI_LSM7, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_DOUBLE_EXT("QUAT_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, + MSM_FRONTEND_DAI_LSM7, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_DOUBLE_EXT("INT3_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT3_MI2S_TX, + MSM_FRONTEND_DAI_LSM7, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_DOUBLE_EXT("VA_CDC_DMA_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_VA_CDC_DMA_TX_0, + MSM_FRONTEND_DAI_LSM7, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_DOUBLE_EXT("VA_CDC_DMA_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_VA_CDC_DMA_TX_1, + MSM_FRONTEND_DAI_LSM7, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_3, + MSM_FRONTEND_DAI_LSM7, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_TX_0, + MSM_FRONTEND_DAI_LSM7, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), +}; + +static const struct snd_kcontrol_new lsm8_mixer_controls[] = { + SOC_DOUBLE_EXT("SLIMBUS_0_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_0_TX, + MSM_FRONTEND_DAI_LSM8, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_DOUBLE_EXT("SLIMBUS_1_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_1_TX, + MSM_FRONTEND_DAI_LSM8, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_DOUBLE_EXT("SLIMBUS_3_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_3_TX, + MSM_FRONTEND_DAI_LSM8, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_DOUBLE_EXT("SLIMBUS_4_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_4_TX, + MSM_FRONTEND_DAI_LSM8, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_DOUBLE_EXT("SLIMBUS_5_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SLIMBUS_5_TX, + MSM_FRONTEND_DAI_LSM8, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_DOUBLE_EXT("TERT_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERTIARY_MI2S_TX, + MSM_FRONTEND_DAI_LSM8, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_DOUBLE_EXT("QUAT_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, + MSM_FRONTEND_DAI_LSM8, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_DOUBLE_EXT("INT3_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_INT3_MI2S_TX, + MSM_FRONTEND_DAI_LSM8, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_DOUBLE_EXT("VA_CDC_DMA_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_VA_CDC_DMA_TX_0, + MSM_FRONTEND_DAI_LSM8, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_DOUBLE_EXT("VA_CDC_DMA_TX_1", SND_SOC_NOPM, + MSM_BACKEND_DAI_VA_CDC_DMA_TX_1, + MSM_FRONTEND_DAI_LSM8, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_DOUBLE_EXT("TX_CDC_DMA_TX_3", SND_SOC_NOPM, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_3, + MSM_FRONTEND_DAI_LSM8, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_DOUBLE_EXT("QUIN_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_TX_0, + MSM_FRONTEND_DAI_LSM8, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), +}; + +static const struct snd_kcontrol_new slim_fm_switch_mixer_controls = + SOC_SINGLE_EXT("Switch", SND_SOC_NOPM, + 0, 1, 0, msm_routing_get_switch_mixer, + msm_routing_put_switch_mixer); + +static const struct snd_kcontrol_new slim1_fm_switch_mixer_controls = + SOC_SINGLE_EXT("Switch", SND_SOC_NOPM, + 0, 1, 0, msm_routing_get_switch_mixer, + msm_routing_put_switch_mixer); + +static const struct snd_kcontrol_new slim3_fm_switch_mixer_controls = + SOC_SINGLE_EXT("Switch", SND_SOC_NOPM, + 0, 1, 0, msm_routing_get_switch_mixer, + msm_routing_put_switch_mixer); + +static const struct snd_kcontrol_new slim4_fm_switch_mixer_controls = + SOC_SINGLE_EXT("Switch", SND_SOC_NOPM, + 0, 1, 0, msm_routing_get_switch_mixer, + msm_routing_put_switch_mixer); + +static const struct snd_kcontrol_new cdc_dma_wsa_switch_mixer_controls = + SOC_SINGLE_EXT("Switch", SND_SOC_NOPM, + 0, 1, 0, msm_routing_get_switch_mixer, + msm_routing_put_switch_mixer); + +static const struct snd_kcontrol_new cdc_dma_rx_switch_mixer_controls = + SOC_SINGLE_EXT("Switch", SND_SOC_NOPM, + 0, 1, 0, msm_routing_get_switch_mixer, + msm_routing_put_switch_mixer); + +static const struct snd_kcontrol_new slim6_fm_switch_mixer_controls = + SOC_SINGLE_EXT("Switch", SND_SOC_NOPM, + 0, 1, 0, msm_routing_get_switch_mixer, + msm_routing_put_switch_mixer); + +static const struct snd_kcontrol_new pcm_rx_switch_mixer_controls = + SOC_SINGLE_EXT("Switch", SND_SOC_NOPM, + 0, 1, 0, msm_routing_get_fm_pcmrx_switch_mixer, + msm_routing_put_fm_pcmrx_switch_mixer); + +static const struct snd_kcontrol_new int0_mi2s_rx_switch_mixer_controls = + SOC_SINGLE_EXT("Switch", SND_SOC_NOPM, + 0, 1, 0, msm_routing_get_int0_mi2s_switch_mixer, + msm_routing_put_int0_mi2s_switch_mixer); + +static const struct snd_kcontrol_new int4_mi2s_rx_switch_mixer_controls = + SOC_SINGLE_EXT("Switch", SND_SOC_NOPM, + 0, 1, 0, msm_routing_get_int4_mi2s_switch_mixer, + msm_routing_put_int4_mi2s_switch_mixer); + +static const struct snd_kcontrol_new pri_mi2s_rx_switch_mixer_controls = + SOC_SINGLE_EXT("Switch", SND_SOC_NOPM, + 0, 1, 0, msm_routing_get_pri_mi2s_switch_mixer, + msm_routing_put_pri_mi2s_switch_mixer); + +static const struct snd_kcontrol_new sec_mi2s_rx_switch_mixer_controls = + SOC_SINGLE_EXT("Switch", SND_SOC_NOPM, + 0, 1, 0, msm_routing_get_sec_mi2s_switch_mixer, + msm_routing_put_sec_mi2s_switch_mixer); + +static const struct snd_kcontrol_new tert_mi2s_rx_switch_mixer_controls = + SOC_SINGLE_EXT("Switch", SND_SOC_NOPM, + 0, 1, 0, msm_routing_get_tert_mi2s_switch_mixer, + msm_routing_put_tert_mi2s_switch_mixer); + +static const struct snd_kcontrol_new quat_mi2s_rx_switch_mixer_controls = + SOC_SINGLE_EXT("Switch", SND_SOC_NOPM, + 0, 1, 0, msm_routing_get_quat_mi2s_switch_mixer, + msm_routing_put_quat_mi2s_switch_mixer); + +static const struct snd_kcontrol_new quin_mi2s_rx_switch_mixer_controls = + SOC_SINGLE_EXT("Switch", SND_SOC_NOPM, + 0, 1, 0, msm_routing_get_quin_mi2s_switch_mixer, + msm_routing_put_quin_mi2s_switch_mixer); + +static const struct snd_kcontrol_new hfp_pri_aux_switch_mixer_controls = + SOC_SINGLE_EXT("Switch", SND_SOC_NOPM, + 0, 1, 0, msm_routing_get_hfp_switch_mixer, + msm_routing_put_hfp_switch_mixer); + +static const struct snd_kcontrol_new hfp_aux_switch_mixer_controls = + SOC_SINGLE_EXT("Switch", SND_SOC_NOPM, + 0, 1, 0, msm_routing_get_hfp_switch_mixer, + msm_routing_put_hfp_switch_mixer); + +static const struct snd_kcontrol_new hfp_int_switch_mixer_controls = + SOC_SINGLE_EXT("Switch", SND_SOC_NOPM, + 0, 1, 0, msm_routing_get_hfp_switch_mixer, + msm_routing_put_hfp_switch_mixer); + +static const struct snd_kcontrol_new hfp_slim7_switch_mixer_controls = + SOC_SINGLE_EXT("Switch", SND_SOC_NOPM, + 0, 1, 0, msm_routing_get_hfp_switch_mixer, + msm_routing_put_hfp_switch_mixer); + +static const struct snd_kcontrol_new usb_switch_mixer_controls = + SOC_SINGLE_EXT("Switch", SND_SOC_NOPM, + 0, 1, 0, msm_routing_get_usb_switch_mixer, + msm_routing_put_usb_switch_mixer); + +static const struct snd_kcontrol_new a2dp_slim7_switch_mixer_controls = + SOC_SINGLE_EXT("Switch", SND_SOC_NOPM, + 0, 1, 0, msm_routing_a2dp_switch_mixer_get, + msm_routing_a2dp_switch_mixer_put); + +static const struct soc_enum lsm_port_enum = + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(lsm_port_text), lsm_port_text); + +static const char * const lsm_func_text[] = { + "None", "AUDIO", "BEACON", "ULTRASOUND", "SWAUDIO", +}; +static const struct soc_enum lsm_func_enum = + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(lsm_func_text), lsm_func_text); + +static const struct snd_kcontrol_new lsm_controls[] = { + /* kcontrol of lsm_function */ + SOC_ENUM_EXT(SLIMBUS_0_TX_TEXT" "LSM_FUNCTION_TEXT, lsm_func_enum, + msm_routing_lsm_func_get, msm_routing_lsm_func_put), + SOC_ENUM_EXT(SLIMBUS_1_TX_TEXT" "LSM_FUNCTION_TEXT, lsm_func_enum, + msm_routing_lsm_func_get, msm_routing_lsm_func_put), + SOC_ENUM_EXT(SLIMBUS_2_TX_TEXT" "LSM_FUNCTION_TEXT, lsm_func_enum, + msm_routing_lsm_func_get, msm_routing_lsm_func_put), + SOC_ENUM_EXT(SLIMBUS_3_TX_TEXT" "LSM_FUNCTION_TEXT, lsm_func_enum, + msm_routing_lsm_func_get, msm_routing_lsm_func_put), + SOC_ENUM_EXT(SLIMBUS_4_TX_TEXT" "LSM_FUNCTION_TEXT, lsm_func_enum, + msm_routing_lsm_func_get, msm_routing_lsm_func_put), + SOC_ENUM_EXT(SLIMBUS_5_TX_TEXT" "LSM_FUNCTION_TEXT, lsm_func_enum, + msm_routing_lsm_func_get, msm_routing_lsm_func_put), + SOC_ENUM_EXT(TERT_MI2S_TX_TEXT" "LSM_FUNCTION_TEXT, lsm_func_enum, + msm_routing_lsm_func_get, msm_routing_lsm_func_put), + SOC_ENUM_EXT(QUAT_MI2S_TX_TEXT" "LSM_FUNCTION_TEXT, lsm_func_enum, + msm_routing_lsm_func_get, msm_routing_lsm_func_put), + SOC_ENUM_EXT(INT3_MI2S_TX_TEXT" "LSM_FUNCTION_TEXT, lsm_func_enum, + msm_routing_lsm_func_get, msm_routing_lsm_func_put), + SOC_ENUM_EXT(TX_CDC_DMA_TX_3_TEXT" "LSM_FUNCTION_TEXT, lsm_func_enum, + msm_routing_lsm_func_get, msm_routing_lsm_func_put), + SOC_ENUM_EXT(QUIN_TDM_TX_TEXT" "LSM_FUNCTION_TEXT, lsm_func_enum, + msm_routing_lsm_func_get, msm_routing_lsm_func_put), + /* kcontrol of lsm_port */ + SOC_ENUM_EXT("LSM1 Port", lsm_port_enum, + msm_routing_lsm_port_get, + msm_routing_lsm_port_put), + SOC_ENUM_EXT("LSM2 Port", lsm_port_enum, + msm_routing_lsm_port_get, + msm_routing_lsm_port_put), + SOC_ENUM_EXT("LSM3 Port", lsm_port_enum, + msm_routing_lsm_port_get, + msm_routing_lsm_port_put), + SOC_ENUM_EXT("LSM4 Port", lsm_port_enum, + msm_routing_lsm_port_get, + msm_routing_lsm_port_put), + SOC_ENUM_EXT("LSM5 Port", lsm_port_enum, + msm_routing_lsm_port_get, + msm_routing_lsm_port_put), + SOC_ENUM_EXT("LSM6 Port", lsm_port_enum, + msm_routing_lsm_port_get, + msm_routing_lsm_port_put), + SOC_ENUM_EXT("LSM7 Port", lsm_port_enum, + msm_routing_lsm_port_get, + msm_routing_lsm_port_put), + SOC_ENUM_EXT("LSM8 Port", lsm_port_enum, + msm_routing_lsm_port_get, + msm_routing_lsm_port_put), +}; + +static const char * const aanc_slim_0_rx_text[] = { + "ZERO", "SLIMBUS_0_TX", "SLIMBUS_1_TX", "SLIMBUS_2_TX", "SLIMBUS_3_TX", + "SLIMBUS_4_TX", "SLIMBUS_5_TX", "SLIMBUS_6_TX" +}; + +static const struct soc_enum aanc_slim_0_rx_enum = + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(aanc_slim_0_rx_text), + aanc_slim_0_rx_text); + +static const struct snd_kcontrol_new aanc_slim_0_rx_mux[] = { + SOC_ENUM_EXT("AANC_SLIM_0_RX MUX", aanc_slim_0_rx_enum, + msm_routing_slim_0_rx_aanc_mux_get, + msm_routing_slim_0_rx_aanc_mux_put) +}; + +static int msm_routing_aanc_noise_level_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = aanc_level; + + return 0; +} + +static int msm_routing_aanc_noise_level_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret = 0; + + mutex_lock(&routing_lock); + aanc_level = ucontrol->value.integer.value[0]; + pr_debug("%s: value: %ld\n", + __func__, ucontrol->value.integer.value[0]); + ret = afe_set_aanc_noise_level(aanc_level); + mutex_unlock(&routing_lock); + + return ret; +} + +static const struct snd_kcontrol_new aanc_noise_level[] = { + SOC_SINGLE_EXT("AANC Noise Level", SND_SOC_NOPM, 0, 255, + 0, msm_routing_aanc_noise_level_get, msm_routing_aanc_noise_level_put) +}; + +static int msm_routing_get_stereo_to_custom_stereo_control( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = is_custom_stereo_on; + return 0; +} + +static int msm_routing_put_stereo_to_custom_stereo_control( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int flag = 0, i = 0, rc = 0, idx = 0; + int be_index = 0, port_id, topo_id; + unsigned int session_id = 0; + uint16_t op_FL_ip_FL_weight = 0; + uint16_t op_FL_ip_FR_weight = 0; + uint16_t op_FR_ip_FL_weight = 0; + uint16_t op_FR_ip_FR_weight = 0; + + flag = ucontrol->value.integer.value[0]; + pr_debug("%s E flag %d\n", __func__, flag); + + if ((is_custom_stereo_on && flag) || (!is_custom_stereo_on && !flag)) { + pr_err("%s: is_custom_stereo_on %d, flag %d\n", + __func__, is_custom_stereo_on, flag); + return 0; + } + is_custom_stereo_on = flag ? true : false; + pr_debug("%s:is_custom_stereo_on %d\n", __func__, is_custom_stereo_on); + for (be_index = 0; be_index < MSM_BACKEND_DAI_MAX; be_index++) { + port_id = msm_bedais[be_index].port_id; + if (!msm_bedais[be_index].active) + continue; + if ((port_id != SLIMBUS_0_RX) && + (port_id != RT_PROXY_PORT_001_RX) && + (port_id != AFE_PORT_ID_PRIMARY_MI2S_RX) && + (port_id != AFE_PORT_ID_INT4_MI2S_RX)) + continue; + + for_each_set_bit(i, &msm_bedais[be_index].fe_sessions[0], + MSM_FRONTEND_DAI_MM_SIZE) { + if (fe_dai_map[i][SESSION_TYPE_RX].perf_mode != + LEGACY_PCM_MODE) + goto skip_send_custom_stereo; + session_id = + fe_dai_map[i][SESSION_TYPE_RX].strm_id; + if (is_custom_stereo_on) { + op_FL_ip_FL_weight = + Q14_GAIN_ZERO_POINT_FIVE; + op_FL_ip_FR_weight = + Q14_GAIN_ZERO_POINT_FIVE; + op_FR_ip_FL_weight = + Q14_GAIN_ZERO_POINT_FIVE; + op_FR_ip_FR_weight = + Q14_GAIN_ZERO_POINT_FIVE; + } else { + op_FL_ip_FL_weight = Q14_GAIN_UNITY; + op_FL_ip_FR_weight = 0; + op_FR_ip_FL_weight = 0; + op_FR_ip_FR_weight = Q14_GAIN_UNITY; + } + for (idx = 0; idx < MAX_COPPS_PER_PORT; idx++) { + unsigned long copp = + session_copp_map[i] + [SESSION_TYPE_RX][be_index]; + if (!test_bit(idx, &copp)) + goto skip_send_custom_stereo; + topo_id = adm_get_topology_for_port_copp_idx( + msm_bedais[be_index].port_id, idx); + if (topo_id < 0) + pr_debug("%s:Err:custom stereo topo %d", + __func__, topo_id); + pr_debug("idx %d\n", idx); + if (topo_id == DS2_ADM_COPP_TOPOLOGY_ID) + rc = msm_ds2_dap_set_custom_stereo_onoff + (msm_bedais[be_index].port_id, + idx, is_custom_stereo_on); + else if (topo_id == DOLBY_ADM_COPP_TOPOLOGY_ID) + rc = dolby_dap_set_custom_stereo_onoff( + msm_bedais[be_index].port_id, + idx, is_custom_stereo_on); + else + rc = msm_qti_pp_send_stereo_to_custom_stereo_cmd + (msm_bedais[be_index].port_id, + idx, session_id, + op_FL_ip_FL_weight, + op_FL_ip_FR_weight, + op_FR_ip_FL_weight, + op_FR_ip_FR_weight); + if (rc < 0) +skip_send_custom_stereo: + pr_err("%s: err setting custom stereo\n", + __func__); + } + + } + } + return 0; +} + +static const struct snd_kcontrol_new stereo_to_custom_stereo_controls[] = { + SOC_SINGLE_EXT("Set Custom Stereo OnOff", SND_SOC_NOPM, 0, + 1, 0, msm_routing_get_stereo_to_custom_stereo_control, + msm_routing_put_stereo_to_custom_stereo_control), +}; + +static int msm_routing_get_app_type_cfg_control(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + return 0; +} + +static int msm_routing_put_app_type_cfg_control(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int i = 0, j; + int num_app_types = ucontrol->value.integer.value[i++]; + + pr_debug("%s\n", __func__); + + memset(app_type_cfg, 0, MAX_APP_TYPES* + sizeof(struct msm_pcm_routing_app_type_data)); + if (num_app_types > MAX_APP_TYPES) { + pr_err("%s: number of app types exceed the max supported\n", + __func__); + return -EINVAL; + } + for (j = 0; j < num_app_types; j++) { + app_type_cfg[j].app_type = + ucontrol->value.integer.value[i++]; + app_type_cfg[j].sample_rate = + ucontrol->value.integer.value[i++]; + app_type_cfg[j].bit_width = + ucontrol->value.integer.value[i++]; + } + + return 0; +} + +static int msm_routing_put_app_type_gain_control(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int j, fe_id, be_id, port_type; + int ret = 0; + unsigned long copp; + struct msm_pcm_routing_bdai_data *bedai; + int dir = ucontrol->value.integer.value[0] ? SESSION_TYPE_TX : + SESSION_TYPE_RX; + int app_type = ucontrol->value.integer.value[1]; + int gain = (ucontrol->value.integer.value[2] + + ucontrol->value.integer.value[3])/2; + + port_type = (dir == SESSION_TYPE_RX) ? MSM_AFE_PORT_TYPE_RX : + MSM_AFE_PORT_TYPE_TX; + + mutex_lock(&routing_lock); + for (be_id = 0; be_id < MSM_BACKEND_DAI_MAX; be_id++) { + if (is_be_dai_extproc(be_id)) + continue; + + bedai = &msm_bedais[be_id]; + if (afe_get_port_type(bedai->port_id) != port_type) + continue; + + if (!bedai->active) + continue; + + for (fe_id = 0; fe_id < MSM_FRONTEND_DAI_MAX; fe_id++) { + if (!test_bit(fe_id, &bedai->fe_sessions[0])) + continue; + + if (app_type != + fe_dai_app_type_cfg[fe_id][dir][be_id].app_type) + continue; + + copp = session_copp_map[fe_id][dir][be_id]; + for (j = 0; j < MAX_COPPS_PER_PORT; j++) { + if (!test_bit(j, &copp)) + continue; + ret |= adm_set_volume(bedai->port_id, j, gain); + } + } + } + mutex_unlock(&routing_lock); + return ret ? -EINVAL : 0; +} + +static const struct snd_kcontrol_new app_type_cfg_controls[] = { + SOC_SINGLE_MULTI_EXT("App Type Config", SND_SOC_NOPM, 0, + 0xFFFFFFFF, 0, 128, msm_routing_get_app_type_cfg_control, + msm_routing_put_app_type_cfg_control), + SOC_SINGLE_MULTI_EXT("App Type Gain", SND_SOC_NOPM, 0, + 0x2000, 0, 4, NULL, msm_routing_put_app_type_gain_control) +}; + +static int msm_routing_put_module_cfg_control(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int copp_idx, fe_id, be_id, port_type; + int ret = 0; + unsigned long copp; + struct msm_pcm_routing_bdai_data *bedai; + u8 *packed_params = NULL; + struct param_hdr_v3 param_hdr; + u32 packed_param_size = (sizeof(struct param_hdr_v3) + + sizeof(uint32_t)); + + int dir = ucontrol->value.integer.value[0] ? SESSION_TYPE_TX : + SESSION_TYPE_RX; + int app_type = ucontrol->value.integer.value[1]; + int module_id = ucontrol->value.integer.value[2]; + int instance_id = ucontrol->value.integer.value[3]; + int param_id = ucontrol->value.integer.value[4]; + int param_value = ucontrol->value.integer.value[5]; + + port_type = (dir == SESSION_TYPE_RX) ? MSM_AFE_PORT_TYPE_RX : + MSM_AFE_PORT_TYPE_TX; + pr_debug("%s app_type:%d mod_id:%d instance_id:%d param_id:%d value:%d\n", + __func__, app_type, module_id, + instance_id, param_id, param_value); + + packed_params = kzalloc(packed_param_size, GFP_KERNEL); + if (!packed_params) + return -ENOMEM; + + memset(¶m_hdr, 0, sizeof(param_hdr)); + param_hdr.module_id = module_id; + param_hdr.instance_id = instance_id; + param_hdr.param_id = param_id; + param_hdr.param_size = sizeof(uint32_t); + + packed_param_size = 0; + + mutex_lock(&routing_lock); + for (be_id = 0; be_id < MSM_BACKEND_DAI_MAX; be_id++) { + if (is_be_dai_extproc(be_id)) + continue; + + bedai = &msm_bedais[be_id]; + if (afe_get_port_type(bedai->port_id) != port_type) + continue; + + if (!bedai->active) + continue; + + for (fe_id = 0; fe_id < MSM_FRONTEND_DAI_MAX; fe_id++) { + if (!test_bit(fe_id, &bedai->fe_sessions[0])) + continue; + + if (app_type != + fe_dai_app_type_cfg[fe_id][dir][be_id].app_type) + continue; + + copp = session_copp_map[fe_id][dir][be_id]; + for (copp_idx = 0; copp_idx < MAX_COPPS_PER_PORT; + copp_idx++) { + if (!test_bit(copp_idx, &copp)) + continue; + + ret = q6common_pack_pp_params(packed_params, + ¶m_hdr, + (u8 *) ¶m_value, + &packed_param_size); + if (ret) { + pr_err("%s: Failed to pack params, error %d\n", + __func__, ret); + goto done; + } + + ret = adm_set_pp_params(bedai->port_id, + copp_idx, NULL, + packed_params, + packed_param_size); + if (ret) { + pr_err("%s: Setting param failed with err=%d\n", + __func__, ret); + ret = -EINVAL; + goto done; + } + } + } + } +done: + mutex_unlock(&routing_lock); + kfree(packed_params); + return ret; +} + +static const struct snd_kcontrol_new module_cfg_controls[] = { + SOC_SINGLE_MULTI_EXT("Audio Effect", SND_SOC_NOPM, 0, + 0x2000, 0, 6, NULL, msm_routing_put_module_cfg_control) +}; + +static int msm_routing_get_lsm_app_type_cfg_control( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + return 0; +} + +static int msm_routing_put_lsm_app_type_cfg_control( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int shift = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + int i = 0, j; + int num_app_types = ucontrol->value.integer.value[i++]; + + memset(lsm_app_type_cfg, 0, MAX_APP_TYPES* + sizeof(struct msm_pcm_routing_app_type_data)); + + if (num_app_types > MAX_APP_TYPES) { + pr_err("%s: number of app types exceed the max supported\n", + __func__); + return -EINVAL; + } + + for (j = 0; j < num_app_types; j++) { + lsm_app_type_cfg[j].app_type = + ucontrol->value.integer.value[i++]; + lsm_app_type_cfg[j].sample_rate = + ucontrol->value.integer.value[i++]; + lsm_app_type_cfg[j].bit_width = + ucontrol->value.integer.value[i++]; + /* Shift of 1 indicates this is V2 mixer control */ + if (shift == 1) + lsm_app_type_cfg[j].num_out_channels = + ucontrol->value.integer.value[i++]; + } + + return 0; +} + +static const struct snd_kcontrol_new lsm_app_type_cfg_controls[] = { + SOC_SINGLE_MULTI_EXT("Listen App Type Config", SND_SOC_NOPM, 0, + 0xFFFFFFFF, 0, 128, msm_routing_get_lsm_app_type_cfg_control, + msm_routing_put_lsm_app_type_cfg_control), + SOC_SINGLE_MULTI_EXT("Listen App Type Config V2", SND_SOC_NOPM, 1, + 0xFFFFFFFF, 0, 128, msm_routing_get_lsm_app_type_cfg_control, + msm_routing_put_lsm_app_type_cfg_control), +}; + +static int msm_routing_get_use_ds1_or_ds2_control( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = is_ds2_on; + return 0; +} + +static int msm_routing_put_use_ds1_or_ds2_control( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + is_ds2_on = ucontrol->value.integer.value[0]; + return 0; +} + +static const struct snd_kcontrol_new use_ds1_or_ds2_controls[] = { + SOC_SINGLE_EXT("DS2 OnOff", SND_SOC_NOPM, 0, + 1, 0, msm_routing_get_use_ds1_or_ds2_control, + msm_routing_put_use_ds1_or_ds2_control), +}; + +int msm_routing_get_rms_value_control(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { + int rc = 0; + int be_idx = 0; + char *param_value; + int *update_param_value; + uint32_t param_size = (RMS_PAYLOAD_LEN + 1) * sizeof(uint32_t); + struct param_hdr_v3 param_hdr; + + param_value = kzalloc(param_size, GFP_KERNEL); + if (!param_value) + return -ENOMEM; + + memset(¶m_hdr, 0, sizeof(param_hdr)); + for (be_idx = 0; be_idx < MSM_BACKEND_DAI_MAX; be_idx++) + if (msm_bedais[be_idx].port_id == SLIMBUS_0_TX) + break; + if ((be_idx < MSM_BACKEND_DAI_MAX) && msm_bedais[be_idx].active) { + param_hdr.module_id = RMS_MODULEID_APPI_PASSTHRU; + param_hdr.instance_id = INSTANCE_ID_0; + param_hdr.param_id = RMS_PARAM_FIRST_SAMPLE; + param_hdr.param_size = param_size; + rc = adm_get_pp_params(SLIMBUS_0_TX, 0, ADM_CLIENT_ID_DEFAULT, + NULL, ¶m_hdr, (u8 *) param_value); + if (rc) { + pr_err("%s: get parameters failed:%d\n", __func__, rc); + kfree(param_value); + return -EINVAL; + } + update_param_value = (int *)param_value; + ucontrol->value.integer.value[0] = update_param_value[0]; + + pr_debug("%s: FROM DSP value[0] 0x%x\n", + __func__, update_param_value[0]); + } + kfree(param_value); + return 0; +} + +static int msm_voc_session_id_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + voc_session_id = ucontrol->value.integer.value[0]; + + pr_debug("%s: voc_session_id=%u\n", __func__, voc_session_id); + + return 0; +} + +static int msm_voc_session_id_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = voc_session_id; + + return 0; +} + +static struct snd_kcontrol_new msm_voc_session_controls[] = { + SOC_SINGLE_MULTI_EXT("Voc VSID", SND_SOC_NOPM, 0, + 0xFFFFFFFF, 0, 1, msm_voc_session_id_get, + msm_voc_session_id_put), +}; + +static int msm_sound_focus_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; + uinfo->count = sizeof(struct sound_focus_param); + + return 0; +} + +static int msm_voice_sound_focus_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret = 0; + struct sound_focus_param soundFocusData; + + memcpy((void *)&soundFocusData, ucontrol->value.bytes.data, + sizeof(struct sound_focus_param)); + ret = voc_set_sound_focus(soundFocusData); + if (ret) { + pr_err("%s: Error setting Sound Focus Params, err=%d\n", + __func__, ret); + + ret = -EINVAL; + } + + return ret; +} + +static int msm_voice_sound_focus_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret = 0; + struct sound_focus_param soundFocusData; + + memset(&soundFocusData, 0, sizeof(struct sound_focus_param)); + + ret = voc_get_sound_focus(&soundFocusData); + if (ret) { + pr_debug("%s: Error getting Sound Focus Params, err=%d\n", + __func__, ret); + + ret = -EINVAL; + goto done; + } + memcpy(ucontrol->value.bytes.data, (void *)&soundFocusData, + sizeof(struct sound_focus_param)); + +done: + return ret; +} + +static int msm_source_tracking_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; + uinfo->count = sizeof(struct source_tracking_param); + + return 0; +} + +static int msm_voice_source_tracking_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret = 0; + struct source_tracking_param sourceTrackingData; + + memset(&sourceTrackingData, 0, sizeof(struct source_tracking_param)); + + ret = voc_get_source_tracking(&sourceTrackingData); + if (ret) { + pr_debug("%s: Error getting Source Tracking Params, err=%d\n", + __func__, ret); + + ret = -EINVAL; + goto done; + } + memcpy(ucontrol->value.bytes.data, (void *)&sourceTrackingData, + sizeof(struct source_tracking_param)); + +done: + return ret; +} + +static int msm_audio_get_copp_idx_from_port_id(int port_id, int session_type, + int *copp_idx) +{ + int i, idx, be_idx; + int ret = 0; + unsigned long copp; + + pr_debug("%s: Enter, port_id=%d\n", __func__, port_id); + + ret = q6audio_validate_port(port_id); + if (ret < 0) { + pr_err("%s: port validation failed id 0x%x ret %d\n", + __func__, port_id, ret); + + ret = -EINVAL; + goto done; + } + + for (be_idx = 0; be_idx < MSM_BACKEND_DAI_MAX; be_idx++) { + if (msm_bedais[be_idx].port_id == port_id) + break; + } + if (be_idx >= MSM_BACKEND_DAI_MAX) { + pr_err("%s: Invalid be id %d\n", __func__, be_idx); + + ret = -EINVAL; + goto done; + } + + for_each_set_bit(i, &msm_bedais[be_idx].fe_sessions[0], + MSM_FRONTEND_DAI_MM_SIZE) { + for (idx = 0; idx < MAX_COPPS_PER_PORT; idx++) { + copp = session_copp_map[i] + [session_type][be_idx]; + if (test_bit(idx, &copp)) + break; + } + if (idx >= MAX_COPPS_PER_PORT) + continue; + else + break; + } + if (i >= MSM_FRONTEND_DAI_MM_SIZE) { + pr_debug("%s: Invalid FE, exiting\n", __func__); + + ret = -EINVAL; + goto done; + } + *copp_idx = idx; + pr_debug("%s: copp_idx=%d\n", __func__, *copp_idx); + +done: + return ret; +} + +static int msm_audio_sound_focus_derive_port_id(struct snd_kcontrol *kcontrol, + const char *prefix, int *port_id) +{ + int ret = 0; + + pr_debug("%s: Enter, prefix:%s\n", __func__, prefix); + + /* + * Mixer control name will be like "Sound Focus Audio Tx SLIMBUS_0" + * where the prefix is "Sound Focus Audio Tx ". Skip the prefix + * and compare the string with the backend name to derive the port id. + */ + if (!strcmp(kcontrol->id.name + strlen(prefix), + "SLIMBUS_0")) { + *port_id = SLIMBUS_0_TX; + } else if (!strcmp(kcontrol->id.name + strlen(prefix), + "TERT_MI2S")) { + *port_id = AFE_PORT_ID_TERTIARY_MI2S_TX; + } else if (!strcmp(kcontrol->id.name + strlen(prefix), + "INT3_MI2S")) { + *port_id = AFE_PORT_ID_INT3_MI2S_TX; + } else if (!strcmp(kcontrol->id.name + strlen(prefix), + "VA_CDC_DMA_TX_0")) { + *port_id = AFE_PORT_ID_VA_CODEC_DMA_TX_0; + } else if (!strcmp(kcontrol->id.name + strlen(prefix), + "TX_CDC_DMA_TX_3")) { + *port_id = AFE_PORT_ID_TX_CODEC_DMA_TX_3; + } else if (!strcmp(kcontrol->id.name + strlen(prefix), + "QUIN_TDM_TX_0")) { + *port_id = AFE_PORT_ID_QUINARY_TDM_TX; + } else { + pr_err("%s: mixer ctl name=%s, could not derive valid port id\n", + __func__, kcontrol->id.name); + + ret = -EINVAL; + goto done; + } + pr_debug("%s: mixer ctl name=%s, derived port_id=%d\n", + __func__, kcontrol->id.name, *port_id); + +done: + return ret; +} + +static int msm_audio_sound_focus_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret = 0; + struct sound_focus_param soundFocusData; + int port_id, copp_idx; + + ret = msm_audio_sound_focus_derive_port_id(kcontrol, + "Sound Focus Audio Tx ", &port_id); + if (ret != 0) { + pr_err("%s: Error in deriving port id, err=%d\n", + __func__, ret); + + ret = -EINVAL; + goto done; + } + + ret = msm_audio_get_copp_idx_from_port_id(port_id, SESSION_TYPE_TX, + &copp_idx); + if (ret) { + pr_err("%s: Could not get copp idx for port_id=%d\n", + __func__, port_id); + + ret = -EINVAL; + goto done; + } + + memcpy((void *)&soundFocusData, ucontrol->value.bytes.data, + sizeof(struct sound_focus_param)); + + ret = adm_set_sound_focus(port_id, copp_idx, soundFocusData); + if (ret) { + pr_err("%s: Error setting Sound Focus Params, err=%d\n", + __func__, ret); + + ret = -EINVAL; + goto done; + } + +done: + return ret; +} + +static int msm_audio_sound_focus_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret = 0; + struct sound_focus_param soundFocusData; + int port_id, copp_idx; + + ret = msm_audio_sound_focus_derive_port_id(kcontrol, + "Sound Focus Audio Tx ", &port_id); + if (ret) { + pr_err("%s: Error in deriving port id, err=%d\n", + __func__, ret); + + ret = -EINVAL; + goto done; + } + + ret = msm_audio_get_copp_idx_from_port_id(port_id, SESSION_TYPE_TX, + &copp_idx); + if (ret) { + pr_debug("%s: Could not get copp idx for port_id=%d\n", + __func__, port_id); + + ret = -EINVAL; + goto done; + } + + ret = adm_get_sound_focus(port_id, copp_idx, &soundFocusData); + if (ret) { + pr_err("%s: Error getting Sound Focus Params, err=%d\n", + __func__, ret); + + ret = -EINVAL; + goto done; + } + + memcpy(ucontrol->value.bytes.data, (void *)&soundFocusData, + sizeof(struct sound_focus_param)); + +done: + return ret; +} + +static int msm_audio_source_tracking_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret = 0; + struct source_tracking_param sourceTrackingData; + int port_id, copp_idx; + + ret = msm_audio_sound_focus_derive_port_id(kcontrol, + "Source Tracking Audio Tx ", &port_id); + if (ret) { + pr_err("%s: Error in deriving port id, err=%d\n", + __func__, ret); + + ret = -EINVAL; + goto done; + } + + ret = msm_audio_get_copp_idx_from_port_id(port_id, SESSION_TYPE_TX, + &copp_idx); + if (ret) { + pr_debug("%s: Could not get copp idx for port_id=%d\n", + __func__, port_id); + + ret = -EINVAL; + goto done; + } + + ret = adm_get_source_tracking(port_id, copp_idx, &sourceTrackingData); + if (ret) { + pr_err("%s: Error getting Source Tracking Params, err=%d\n", + __func__, ret); + + ret = -EINVAL; + goto done; + } + + memcpy(ucontrol->value.bytes.data, (void *)&sourceTrackingData, + sizeof(struct source_tracking_param)); + +done: + return ret; +} + +static const struct snd_kcontrol_new msm_source_tracking_controls[] = { + { + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Sound Focus Voice Tx SLIMBUS_0", + .info = msm_sound_focus_info, + .get = msm_voice_sound_focus_get, + .put = msm_voice_sound_focus_put, + }, + { + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Source Tracking Voice Tx SLIMBUS_0", + .info = msm_source_tracking_info, + .get = msm_voice_source_tracking_get, + }, + { + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Sound Focus Audio Tx SLIMBUS_0", + .info = msm_sound_focus_info, + .get = msm_audio_sound_focus_get, + .put = msm_audio_sound_focus_put, + }, + { + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Source Tracking Audio Tx SLIMBUS_0", + .info = msm_source_tracking_info, + .get = msm_audio_source_tracking_get, + }, + { + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Sound Focus Voice Tx TERT_MI2S", + .info = msm_sound_focus_info, + .get = msm_voice_sound_focus_get, + .put = msm_voice_sound_focus_put, + }, + { + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Source Tracking Voice Tx TERT_MI2S", + .info = msm_source_tracking_info, + .get = msm_voice_source_tracking_get, + }, + { + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Sound Focus Audio Tx TERT_MI2S", + .info = msm_sound_focus_info, + .get = msm_audio_sound_focus_get, + .put = msm_audio_sound_focus_put, + }, + { + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Source Tracking Audio Tx TERT_MI2S", + .info = msm_source_tracking_info, + .get = msm_audio_source_tracking_get, + }, + { + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Sound Focus Voice Tx INT3_MI2S", + .info = msm_sound_focus_info, + .get = msm_voice_sound_focus_get, + .put = msm_voice_sound_focus_put, + }, + { + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Source Tracking Voice Tx INT3_MI2S", + .info = msm_source_tracking_info, + .get = msm_voice_source_tracking_get, + }, + { + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Sound Focus Audio Tx INT3_MI2S", + .info = msm_sound_focus_info, + .get = msm_audio_sound_focus_get, + .put = msm_audio_sound_focus_put, + }, + { + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Source Tracking Audio Tx INT3_MI2S", + .info = msm_source_tracking_info, + .get = msm_audio_source_tracking_get, + }, + { + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Sound Focus Voice Tx VA_CDC_DMA_TX_0", + .info = msm_sound_focus_info, + .get = msm_voice_sound_focus_get, + .put = msm_voice_sound_focus_put, + }, + { + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Source Tracking Voice Tx VA_CDC_DMA_TX_0", + .info = msm_source_tracking_info, + .get = msm_voice_source_tracking_get, + }, + { + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Sound Focus Audio Tx VA_CDC_DMA_TX_0", + .info = msm_sound_focus_info, + .get = msm_audio_sound_focus_get, + .put = msm_audio_sound_focus_put, + }, + { + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Source Tracking Audio Tx VA_CDC_DMA_TX_0", + .info = msm_source_tracking_info, + .get = msm_audio_source_tracking_get, + }, + { + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Sound Focus Voice Tx TX_CDC_DMA_TX_3", + .info = msm_sound_focus_info, + .get = msm_voice_sound_focus_get, + .put = msm_voice_sound_focus_put, + }, + { + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Source Tracking Voice Tx TX_CDC_DMA_TX_3", + .info = msm_source_tracking_info, + .get = msm_voice_source_tracking_get, + }, + { + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Sound Focus Audio Tx TX_CDC_DMA_TX_3", + .info = msm_sound_focus_info, + .get = msm_audio_sound_focus_get, + .put = msm_audio_sound_focus_put, + }, + { + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Source Tracking Audio Tx TX_CDC_DMA_TX_3", + .info = msm_source_tracking_info, + .get = msm_audio_source_tracking_get, + }, + { + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Sound Focus Voice Tx QUIN_TDM_TX_0", + .info = msm_sound_focus_info, + .get = msm_voice_sound_focus_get, + .put = msm_voice_sound_focus_put, + }, + { + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Source Tracking Voice Tx QUIN_TDM_TX_0", + .info = msm_source_tracking_info, + .get = msm_voice_source_tracking_get, + }, + { + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Sound Focus Audio Tx QUIN_TDM_TX_0", + .info = msm_sound_focus_info, + .get = msm_audio_sound_focus_get, + .put = msm_audio_sound_focus_put, + }, + { + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Source Tracking Audio Tx QUIN_TDM_TX_0", + .info = msm_source_tracking_info, + .get = msm_audio_source_tracking_get, + }, +}; + +static int spkr_prot_put_vi_lch_port(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret = 0; + int item; + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + + pr_debug("%s item is %d\n", __func__, + ucontrol->value.enumerated.item[0]); + mutex_lock(&routing_lock); + item = ucontrol->value.enumerated.item[0]; + if (item < e->items) { + pr_debug("%s RX DAI ID %d TX DAI id %d\n", + __func__, e->shift_l, e->values[item]); + if (e->shift_l < MSM_BACKEND_DAI_MAX && + e->values[item] < MSM_BACKEND_DAI_MAX) + /* Enable feedback TX path */ + ret = afe_spk_prot_feed_back_cfg( + msm_bedais[e->values[item]].port_id, + msm_bedais[e->shift_l].port_id, 1, 0, 1); + else { + pr_debug("%s values are out of range item %d\n", + __func__, e->values[item]); + /* Disable feedback TX path */ + if (e->values[item] == MSM_BACKEND_DAI_MAX) + ret = afe_spk_prot_feed_back_cfg(0, 0, 0, 0, 0); + else + ret = -EINVAL; + } + } else { + pr_err("%s item value is out of range item\n", __func__); + ret = -EINVAL; + } + mutex_unlock(&routing_lock); + return ret; +} + +static int spkr_prot_put_vi_rch_port(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret = 0; + int item; + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + + pr_debug("%s item is %d\n", __func__, + ucontrol->value.enumerated.item[0]); + mutex_lock(&routing_lock); + item = ucontrol->value.enumerated.item[0]; + if (item < e->items) { + pr_debug("%s RX DAI ID %d TX DAI id %d\n", + __func__, e->shift_l, e->values[item]); + if (e->shift_l < MSM_BACKEND_DAI_MAX && + e->values[item] < MSM_BACKEND_DAI_MAX) + /* Enable feedback TX path */ + ret = afe_spk_prot_feed_back_cfg( + msm_bedais[e->values[item]].port_id, + msm_bedais[e->shift_l].port_id, + 1, 1, 1); + else { + pr_debug("%s values are out of range item %d\n", + __func__, e->values[item]); + /* Disable feedback TX path */ + if (e->values[item] == MSM_BACKEND_DAI_MAX) + ret = afe_spk_prot_feed_back_cfg(0, + 0, 0, 0, 0); + else + ret = -EINVAL; + } + } else { + pr_err("%s item value is out of range item\n", __func__); + ret = -EINVAL; + } + mutex_unlock(&routing_lock); + return ret; +} + +static int spkr_prot_get_vi_lch_port(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s\n", __func__); + return 0; +} + +static int spkr_prot_get_vi_rch_port(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s\n", __func__); + ucontrol->value.enumerated.item[0] = 0; + return 0; +} + +static const char * const slim0_rx_vi_fb_tx_lch_mux_text[] = { + "ZERO", "SLIM4_TX" +}; + +static const char * const slim0_rx_vi_fb_tx_rch_mux_text[] = { + "ZERO", "SLIM4_TX" +}; + +static const char * const wsa_rx_0_vi_fb_tx_lch_mux_text[] = { + "ZERO", "WSA_CDC_DMA_TX_0" +}; + +static const char * const wsa_rx_0_vi_fb_tx_rch_mux_text[] = { + "ZERO", "WSA_CDC_DMA_TX_0" +}; + +static const char * const mi2s_rx_vi_fb_tx_mux_text[] = { + "ZERO", "SENARY_TX" +}; + +static const char * const int4_mi2s_rx_vi_fb_tx_mono_mux_text[] = { + "ZERO", "INT5_MI2S_TX" +}; + +static const char * const int4_mi2s_rx_vi_fb_tx_stereo_mux_text[] = { + "ZERO", "INT5_MI2S_TX" +}; + +static const int const slim0_rx_vi_fb_tx_lch_value[] = { + MSM_BACKEND_DAI_MAX, MSM_BACKEND_DAI_SLIMBUS_4_TX +}; + +static const int const slim0_rx_vi_fb_tx_rch_value[] = { + MSM_BACKEND_DAI_MAX, MSM_BACKEND_DAI_SLIMBUS_4_TX +}; + +static const int const wsa_rx_0_vi_fb_tx_lch_value[] = { + MSM_BACKEND_DAI_MAX, MSM_BACKEND_DAI_WSA_CDC_DMA_TX_0 +}; + +static const int const wsa_rx_0_vi_fb_tx_rch_value[] = { + MSM_BACKEND_DAI_MAX, MSM_BACKEND_DAI_WSA_CDC_DMA_TX_0 +}; + + +static const int const mi2s_rx_vi_fb_tx_value[] = { + MSM_BACKEND_DAI_MAX, MSM_BACKEND_DAI_SENARY_MI2S_TX +}; + +static const int const int4_mi2s_rx_vi_fb_tx_mono_ch_value[] = { + MSM_BACKEND_DAI_MAX, MSM_BACKEND_DAI_INT5_MI2S_TX +}; + +static const int const int4_mi2s_rx_vi_fb_tx_stereo_ch_value[] = { + MSM_BACKEND_DAI_MAX, MSM_BACKEND_DAI_INT5_MI2S_TX +}; + +static const struct soc_enum slim0_rx_vi_fb_lch_mux_enum = + SOC_VALUE_ENUM_DOUBLE(0, MSM_BACKEND_DAI_SLIMBUS_0_RX, 0, 0, + ARRAY_SIZE(slim0_rx_vi_fb_tx_lch_mux_text), + slim0_rx_vi_fb_tx_lch_mux_text, slim0_rx_vi_fb_tx_lch_value); + +static const struct soc_enum slim0_rx_vi_fb_rch_mux_enum = + SOC_VALUE_ENUM_DOUBLE(0, MSM_BACKEND_DAI_SLIMBUS_0_RX, 0, 0, + ARRAY_SIZE(slim0_rx_vi_fb_tx_rch_mux_text), + slim0_rx_vi_fb_tx_rch_mux_text, slim0_rx_vi_fb_tx_rch_value); + +static const struct soc_enum wsa_rx_0_vi_fb_lch_mux_enum = + SOC_VALUE_ENUM_DOUBLE(0, MSM_BACKEND_DAI_WSA_CDC_DMA_RX_0, 0, 0, + ARRAY_SIZE(wsa_rx_0_vi_fb_tx_lch_mux_text), + wsa_rx_0_vi_fb_tx_lch_mux_text, wsa_rx_0_vi_fb_tx_lch_value); + +static const struct soc_enum wsa_rx_0_vi_fb_rch_mux_enum = + SOC_VALUE_ENUM_DOUBLE(0, MSM_BACKEND_DAI_WSA_CDC_DMA_RX_0, 0, 0, + ARRAY_SIZE(wsa_rx_0_vi_fb_tx_rch_mux_text), + wsa_rx_0_vi_fb_tx_rch_mux_text, wsa_rx_0_vi_fb_tx_rch_value); + +static const struct soc_enum mi2s_rx_vi_fb_mux_enum = + SOC_VALUE_ENUM_DOUBLE(0, MSM_BACKEND_DAI_PRI_MI2S_RX, 0, 0, + ARRAY_SIZE(mi2s_rx_vi_fb_tx_mux_text), + mi2s_rx_vi_fb_tx_mux_text, mi2s_rx_vi_fb_tx_value); + +static const struct soc_enum int4_mi2s_rx_vi_fb_mono_ch_mux_enum = + SOC_VALUE_ENUM_DOUBLE(0, MSM_BACKEND_DAI_INT4_MI2S_RX, 0, 0, + ARRAY_SIZE(int4_mi2s_rx_vi_fb_tx_mono_mux_text), + int4_mi2s_rx_vi_fb_tx_mono_mux_text, + int4_mi2s_rx_vi_fb_tx_mono_ch_value); + +static const struct soc_enum int4_mi2s_rx_vi_fb_stereo_ch_mux_enum = + SOC_VALUE_ENUM_DOUBLE(0, MSM_BACKEND_DAI_INT4_MI2S_RX, 0, 0, + ARRAY_SIZE(int4_mi2s_rx_vi_fb_tx_stereo_mux_text), + int4_mi2s_rx_vi_fb_tx_stereo_mux_text, + int4_mi2s_rx_vi_fb_tx_stereo_ch_value); + +static const struct snd_kcontrol_new slim0_rx_vi_fb_lch_mux = + SOC_DAPM_ENUM_EXT("SLIM0_RX_VI_FB_LCH_MUX", + slim0_rx_vi_fb_lch_mux_enum, spkr_prot_get_vi_lch_port, + spkr_prot_put_vi_lch_port); + +static const struct snd_kcontrol_new slim0_rx_vi_fb_rch_mux = + SOC_DAPM_ENUM_EXT("SLIM0_RX_VI_FB_RCH_MUX", + slim0_rx_vi_fb_rch_mux_enum, spkr_prot_get_vi_rch_port, + spkr_prot_put_vi_rch_port); + +static const struct snd_kcontrol_new wsa_rx_0_vi_fb_lch_mux = + SOC_DAPM_ENUM_EXT("WSA_RX_0_VI_FB_LCH_MUX", + wsa_rx_0_vi_fb_lch_mux_enum, spkr_prot_get_vi_lch_port, + spkr_prot_put_vi_lch_port); + +static const struct snd_kcontrol_new wsa_rx_0_vi_fb_rch_mux = + SOC_DAPM_ENUM_EXT("WSA_RX_0_VI_FB_RCH_MUX", + wsa_rx_0_vi_fb_rch_mux_enum, spkr_prot_get_vi_rch_port, + spkr_prot_put_vi_rch_port); + +static const struct snd_kcontrol_new mi2s_rx_vi_fb_mux = + SOC_DAPM_ENUM_EXT("PRI_MI2S_RX_VI_FB_MUX", + mi2s_rx_vi_fb_mux_enum, spkr_prot_get_vi_lch_port, + spkr_prot_put_vi_lch_port); + +static const struct snd_kcontrol_new int4_mi2s_rx_vi_fb_mono_ch_mux = + SOC_DAPM_ENUM_EXT("INT4_MI2S_RX_VI_FB_MONO_CH_MUX", + int4_mi2s_rx_vi_fb_mono_ch_mux_enum, spkr_prot_get_vi_lch_port, + spkr_prot_put_vi_lch_port); + +static const struct snd_kcontrol_new int4_mi2s_rx_vi_fb_stereo_ch_mux = + SOC_DAPM_ENUM_EXT("INT4_MI2S_RX_VI_FB_STEREO_CH_MUX", + int4_mi2s_rx_vi_fb_stereo_ch_mux_enum, spkr_prot_get_vi_rch_port, + spkr_prot_put_vi_rch_port); + +static const struct snd_soc_dapm_widget msm_qdsp6_widgets[] = { + /* Frontend AIF */ + /* Widget name equals to Front-End DAI name, + * Stream name must contains substring of front-end dai name + */ + SND_SOC_DAPM_AIF_IN("MM_DL1", "MultiMedia1 Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("MM_DL2", "MultiMedia2 Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("MM_DL3", "MultiMedia3 Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("MM_DL4", "MultiMedia4 Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("MM_DL5", "MultiMedia5 Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("MM_DL6", "MultiMedia6 Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("MM_DL7", "MultiMedia7 Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("MM_DL8", "MultiMedia8 Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("MM_DL9", "MultiMedia9 Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("MM_DL10", "MultiMedia10 Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("MM_DL11", "MultiMedia11 Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("MM_DL12", "MultiMedia12 Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("MM_DL13", "MultiMedia13 Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("MM_DL14", "MultiMedia14 Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("MM_DL15", "MultiMedia15 Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("MM_DL16", "MultiMedia16 Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("MM_DL20", "MultiMedia20 Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("MM_DL21", "MultiMedia21 Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("MM_DL26", "MultiMedia26 Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("MM_DL30", "MultiMedia30 Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("MM_DL31", "MultiMedia31 Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("VOIP_DL", "VoIP Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("MM_UL1", "MultiMedia1 Capture", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("MM_UL2", "MultiMedia2 Capture", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("MM_UL3", "MultiMedia3 Capture", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("MM_UL4", "MultiMedia4 Capture", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("MM_UL5", "MultiMedia5 Capture", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("MM_UL6", "MultiMedia6 Capture", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("MM_UL8", "MultiMedia8 Capture", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("MM_UL9", "MultiMedia9 Capture", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("MM_UL10", "MultiMedia10 Capture", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("MM_UL16", "MultiMedia16 Capture", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("MM_UL17", "MultiMedia17 Capture", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("MM_UL18", "MultiMedia18 Capture", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("MM_UL19", "MultiMedia19 Capture", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("MM_UL20", "MultiMedia20 Capture", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("MM_UL21", "MultiMedia21 Capture", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("MM_UL27", "MultiMedia27 Capture", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("MM_UL28", "MultiMedia28 Capture", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("MM_UL29", "MultiMedia29 Capture", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("VOIP_UL", "VoIP Capture", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("VOICEMMODE1_DL", + "VoiceMMode1 Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("VOICEMMODE1_UL", + "VoiceMMode1 Capture", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("VOICEMMODE2_DL", + "VoiceMMode2 Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("VOICEMMODE2_UL", + "VoiceMMode2 Capture", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("SLIM0_DL_HL", "SLIMBUS0_HOSTLESS Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("SLIM0_UL_HL", "SLIMBUS0_HOSTLESS Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("CDC_DMA_DL_HL", "CDC_DMA_HOSTLESS Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("CDC_DMA_UL_HL", "CDC_DMA_HOSTLESS Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("TX3_CDC_DMA_UL_HL", + "TX3_CDC_DMA_HOSTLESS Capture", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("CPE_LSM_UL_HL", "CPE LSM capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("SLIM1_DL_HL", "SLIMBUS1_HOSTLESS Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("SLIM1_UL_HL", "SLIMBUS1_HOSTLESS Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("SLIM3_DL_HL", "SLIMBUS3_HOSTLESS Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("SLIM3_UL_HL", "SLIMBUS3_HOSTLESS Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("SLIM4_DL_HL", "SLIMBUS4_HOSTLESS Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("SLIM4_UL_HL", "SLIMBUS4_HOSTLESS Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("SLIM6_DL_HL", "SLIMBUS6_HOSTLESS Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("SLIM6_UL_HL", "SLIMBUS6_HOSTLESS Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("SLIM7_DL_HL", "SLIMBUS7_HOSTLESS Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("SLIM7_UL_HL", "SLIMBUS7_HOSTLESS Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("SLIM8_DL_HL", "SLIMBUS8_HOSTLESS Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("SLIM8_UL_HL", "SLIMBUS8_HOSTLESS Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("INTFM_DL_HL", "INT_FM_HOSTLESS Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("INTFM_UL_HL", "INT_FM_HOSTLESS Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("INTHFP_DL_HL", "INT_HFP_BT_HOSTLESS Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("INTHFP_UL_HL", "INT_HFP_BT_HOSTLESS Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("USBAUDIO_DL_HL", "USBAUDIO_HOSTLESS Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("USBAUDIO_UL_HL", "USBAUDIO_HOSTLESS Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("HDMI_DL_HL", "HDMI_HOSTLESS Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("SEC_I2S_DL_HL", "SEC_I2S_RX_HOSTLESS Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("INT0_MI2S_DL_HL", + "INT0 MI2S_RX Hostless Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("INT4_MI2S_DL_HL", + "INT4 MI2S_RX Hostless Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("PRI_MI2S_DL_HL", + "Primary MI2S_RX Hostless Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("SEC_MI2S_DL_HL", + "Secondary MI2S_RX Hostless Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("TERT_MI2S_DL_HL", + "Tertiary MI2S_RX Hostless Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("QUAT_MI2S_DL_HL", + "Quaternary MI2S_RX Hostless Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("QUIN_MI2S_DL_HL", + "Quinary MI2S_RX Hostless Playback", + 0, 0, 0, 0), + + SND_SOC_DAPM_AIF_IN("AUXPCM_DL_HL", "AUXPCM_HOSTLESS Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("AUXPCM_UL_HL", "AUXPCM_HOSTLESS Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("SEC_AUXPCM_DL_HL", "SEC_AUXPCM_HOSTLESS Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("SEC_AUXPCM_UL_HL", "SEC_AUXPCM_HOSTLESS Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("MI2S_UL_HL", "MI2S_TX_HOSTLESS Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("INT3_MI2S_UL_HL", + "INT3 MI2S_TX Hostless Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("TERT_MI2S_UL_HL", + "Tertiary MI2S_TX Hostless Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("SEC_MI2S_UL_HL", + "Secondary MI2S_TX Hostless Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("PRI_MI2S_UL_HL", + "Primary MI2S_TX Hostless Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("MI2S_DL_HL", "MI2S_RX_HOSTLESS Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("DTMF_DL_HL", "DTMF_RX_HOSTLESS Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("QUAT_MI2S_UL_HL", + "Quaternary MI2S_TX Hostless Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("QUIN_MI2S_UL_HL", + "Quinary MI2S_TX Hostless Capture", + 0, 0, 0, 0), + + SND_SOC_DAPM_AIF_IN("PRI_TDM_RX_0_DL_HL", + "Primary TDM0 Hostless Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("PRI_TDM_TX_0_UL_HL", + "Primary TDM0 Hostless Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("PRI_TDM_RX_1_DL_HL", + "Primary TDM1 Hostless Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("PRI_TDM_TX_1_UL_HL", + "Primary TDM1 Hostless Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("PRI_TDM_RX_2_DL_HL", + "Primary TDM2 Hostless Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("PRI_TDM_TX_2_UL_HL", + "Primary TDM2 Hostless Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("PRI_TDM_RX_3_DL_HL", + "Primary TDM3 Hostless Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("PRI_TDM_TX_3_UL_HL", + "Primary TDM3 Hostless Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("PRI_TDM_RX_4_DL_HL", + "Primary TDM4 Hostless Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("PRI_TDM_TX_4_UL_HL", + "Primary TDM4 Hostless Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("PRI_TDM_RX_5_DL_HL", + "Primary TDM5 Hostless Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("PRI_TDM_TX_5_UL_HL", + "Primary TDM5 Hostless Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("PRI_TDM_RX_6_DL_HL", + "Primary TDM6 Hostless Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("PRI_TDM_TX_6_UL_HL", + "Primary TDM6 Hostless Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("PRI_TDM_RX_7_DL_HL", + "Primary TDM7 Hostless Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("PRI_TDM_TX_7_UL_HL", + "Primary TDM7 Hostless Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("SEC_TDM_RX_0_DL_HL", + "Secondary TDM0 Hostless Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("SEC_TDM_TX_0_UL_HL", + "Secondary TDM0 Hostless Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("SEC_TDM_RX_1_DL_HL", + "Secondary TDM1 Hostless Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("SEC_TDM_TX_1_UL_HL", + "Secondary TDM1 Hostless Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("SEC_TDM_RX_2_DL_HL", + "Secondary TDM2 Hostless Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("SEC_TDM_TX_2_UL_HL", + "Secondary TDM2 Hostless Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("SEC_TDM_RX_3_DL_HL", + "Secondary TDM3 Hostless Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("SEC_TDM_TX_3_UL_HL", + "Secondary TDM3 Hostless Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("SEC_TDM_RX_4_DL_HL", + "Secondary TDM4 Hostless Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("SEC_TDM_TX_4_UL_HL", + "Secondary TDM4 Hostless Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("SEC_TDM_RX_5_DL_HL", + "Secondary TDM5 Hostless Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("SEC_TDM_TX_5_UL_HL", + "Secondary TDM5 Hostless Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("SEC_TDM_RX_6_DL_HL", + "Secondary TDM6 Hostless Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("SEC_TDM_TX_6_UL_HL", + "Secondary TDM6 Hostless Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("SEC_TDM_RX_7_DL_HL", + "Secondary TDM7 Hostless Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("SEC_TDM_TX_7_UL_HL", + "Secondary TDM7 Hostless Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("TERT_TDM_RX_0_DL_HL", + "Tertiary TDM0 Hostless Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("TERT_TDM_TX_0_UL_HL", + "Tertiary TDM0 Hostless Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("TERT_TDM_RX_1_DL_HL", + "Tertiary TDM1 Hostless Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("TERT_TDM_TX_1_UL_HL", + "Tertiary TDM1 Hostless Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("TERT_TDM_RX_2_DL_HL", + "Tertiary TDM2 Hostless Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("TERT_TDM_TX_2_UL_HL", + "Tertiary TDM2 Hostless Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("TERT_TDM_RX_3_DL_HL", + "Tertiary TDM3 Hostless Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("TERT_TDM_TX_3_UL_HL", + "Tertiary TDM3 Hostless Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("TERT_TDM_RX_4_DL_HL", + "Tertiary TDM4 Hostless Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("TERT_TDM_TX_4_UL_HL", + "Tertiary TDM4 Hostless Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("TERT_TDM_RX_5_DL_HL", + "Tertiary TDM5 Hostless Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("TERT_TDM_TX_5_UL_HL", + "Tertiary TDM5 Hostless Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("TERT_TDM_RX_6_DL_HL", + "Tertiary TDM6 Hostless Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("TERT_TDM_TX_6_UL_HL", + "Tertiary TDM6 Hostless Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("TERT_TDM_RX_7_DL_HL", + "Tertiary TDM7 Hostless Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("TERT_TDM_TX_7_UL_HL", + "Tertiary TDM7 Hostless Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("QUAT_TDM_RX_0_DL_HL", + "Quaternary TDM0 Hostless Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("QUAT_TDM_TX_0_UL_HL", + "Quaternary TDM0 Hostless Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("QUAT_TDM_RX_1_DL_HL", + "Quaternary TDM1 Hostless Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("QUAT_TDM_TX_1_UL_HL", + "Quaternary TDM1 Hostless Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("QUAT_TDM_RX_2_DL_HL", + "Quaternary TDM2 Hostless Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("QUAT_TDM_TX_2_UL_HL", + "Quaternary TDM2 Hostless Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("QUAT_TDM_RX_3_DL_HL", + "Quaternary TDM3 Hostless Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("QUAT_TDM_TX_3_UL_HL", + "Quaternary TDM3 Hostless Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("QUAT_TDM_RX_4_DL_HL", + "Quaternary TDM4 Hostless Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("QUAT_TDM_TX_4_UL_HL", + "Quaternary TDM4 Hostless Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("QUAT_TDM_RX_5_DL_HL", + "Quaternary TDM5 Hostless Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("QUAT_TDM_TX_5_UL_HL", + "Quaternary TDM5 Hostless Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("QUAT_TDM_RX_6_DL_HL", + "Quaternary TDM6 Hostless Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("QUAT_TDM_TX_6_UL_HL", + "Quaternary TDM6 Hostless Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("QUAT_TDM_RX_7_DL_HL", + "Quaternary TDM7 Hostless Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("QUAT_TDM_TX_7_UL_HL", + "Quaternary TDM7 Hostless Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("QUIN_TDM_RX_0_DL_HL", + "Quinary TDM0 Hostless Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("QUIN_TDM_TX_0_UL_HL", + "Quinary TDM0 Hostless Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("QUIN_TDM_RX_1_DL_HL", + "Quinary TDM1 Hostless Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("QUIN_TDM_TX_1_UL_HL", + "Quinary TDM1 Hostless Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("QUIN_TDM_RX_2_DL_HL", + "Quinary TDM2 Hostless Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("QUIN_TDM_TX_2_UL_HL", + "Quinary TDM2 Hostless Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("QUIN_TDM_RX_3_DL_HL", + "Quinary TDM3 Hostless Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("QUIN_TDM_TX_3_UL_HL", + "Quinary TDM3 Hostless Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("QUIN_TDM_RX_4_DL_HL", + "Quinary TDM4 Hostless Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("QUIN_TDM_TX_4_UL_HL", + "Quinary TDM4 Hostless Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("QUIN_TDM_RX_5_DL_HL", + "Quinary TDM5 Hostless Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("QUIN_TDM_TX_5_UL_HL", + "Quinary TDM5 Hostless Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("QUIN_TDM_RX_6_DL_HL", + "Quinary TDM6 Hostless Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("QUIN_TDM_TX_6_UL_HL", + "Quinary TDM6 Hostless Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("QUIN_TDM_RX_7_DL_HL", + "Quinary TDM7 Hostless Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("QUIN_TDM_TX_7_UL_HL", + "Quinary TDM7 Hostless Capture", + 0, 0, 0, 0), + + /* LSM */ + SND_SOC_DAPM_AIF_OUT("LSM1_UL_HL", "Listen 1 Audio Service Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("LSM2_UL_HL", "Listen 2 Audio Service Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("LSM3_UL_HL", "Listen 3 Audio Service Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("LSM4_UL_HL", "Listen 4 Audio Service Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("LSM5_UL_HL", "Listen 5 Audio Service Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("LSM6_UL_HL", "Listen 6 Audio Service Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("LSM7_UL_HL", "Listen 7 Audio Service Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("LSM8_UL_HL", "Listen 8 Audio Service Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("QCHAT_DL", "QCHAT Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("QCHAT_UL", "QCHAT Capture", 0, 0, 0, 0), + /* Backend AIF */ + /* Stream name equals to backend dai link stream name */ + SND_SOC_DAPM_AIF_OUT("PRI_I2S_RX", "Primary I2S Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("SEC_I2S_RX", "Secondary I2S Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("PRI_SPDIF_RX", "Primary SPDIF Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("PRI_SPDIF_TX", "Primary SPDIF Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("SEC_SPDIF_RX", "Secondary SPDIF Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("SEC_SPDIF_TX", "Secondary SPDIF Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("SLIMBUS_0_RX", "Slimbus Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("SLIMBUS_2_RX", "Slimbus2 Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("SLIMBUS_5_RX", "Slimbus5 Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("HDMI", "HDMI Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("DISPLAY_PORT", "Display Port Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("DISPLAY_PORT1", "Display Port1 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("MI2S_RX", "MI2S Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("QUAT_MI2S_RX", "Quaternary MI2S Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("TERT_MI2S_RX", "Tertiary MI2S Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("SEC_MI2S_RX", "Secondary MI2S Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("SEC_MI2S_RX_SD1", + "Secondary MI2S Playback SD1", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("PRI_MI2S_RX", "Primary MI2S Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("INT0_MI2S_RX", "INT0 MI2S Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("INT2_MI2S_RX", "INT2 MI2S Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("INT3_MI2S_RX", "INT3 MI2S Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("INT5_MI2S_RX", "INT5 MI2S Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("INT4_MI2S_RX", "INT4 MI2S Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("INT4_MI2S_TX", "INT4 MI2S Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("QUIN_MI2S_RX", "Quinary MI2S Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("PRI_I2S_TX", "Primary I2S Capture", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("MI2S_TX", "MI2S Capture", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("QUAT_MI2S_TX", "Quaternary MI2S Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("PRI_MI2S_TX", "Primary MI2S Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("TERT_MI2S_TX", "Tertiary MI2S Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("INT0_MI2S_TX", "INT0 MI2S Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("INT2_MI2S_TX", "INT2 MI2S Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("INT3_MI2S_TX", "INT3 MI2S Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("SEC_MI2S_TX", "Secondary MI2S Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("SLIMBUS_0_TX", "Slimbus Capture", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("SLIMBUS_2_TX", "Slimbus2 Capture", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("QUIN_MI2S_TX", "Quinary MI2S Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("SENARY_MI2S_TX", "Senary MI2S Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("INT_BT_SCO_RX", "Internal BT-SCO Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("INT_BT_SCO_TX", "Internal BT-SCO Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("INT_BT_A2DP_RX", "Internal BT-A2DP Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("INT_FM_RX", "Internal FM Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("INT_FM_TX", "Internal FM Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("PCM_RX", "AFE Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("PCM_TX", "AFE Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("PRI_TDM_RX_0", "Primary TDM0 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("PRI_TDM_TX_0", "Primary TDM0 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("PRI_TDM_RX_1", "Primary TDM1 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("PRI_TDM_TX_1", "Primary TDM1 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("PRI_TDM_RX_2", "Primary TDM2 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("PRI_TDM_TX_2", "Primary TDM2 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("PRI_TDM_RX_3", "Primary TDM3 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("PRI_TDM_TX_3", "Primary TDM3 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("PRI_TDM_RX_4", "Primary TDM4 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("PRI_TDM_TX_4", "Primary TDM4 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("PRI_TDM_RX_5", "Primary TDM5 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("PRI_TDM_TX_5", "Primary TDM5 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("PRI_TDM_RX_6", "Primary TDM6 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("PRI_TDM_TX_6", "Primary TDM6 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("PRI_TDM_RX_7", "Primary TDM7 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("PRI_TDM_TX_7", "Primary TDM7 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("SEC_TDM_RX_0", "Secondary TDM0 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("SEC_TDM_TX_0", "Secondary TDM0 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("SEC_TDM_RX_1", "Secondary TDM1 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("SEC_TDM_TX_1", "Secondary TDM1 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("SEC_TDM_RX_2", "Secondary TDM2 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("SEC_TDM_TX_2", "Secondary TDM2 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("SEC_TDM_RX_3", "Secondary TDM3 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("SEC_TDM_TX_3", "Secondary TDM3 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("SEC_TDM_RX_4", "Secondary TDM4 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("SEC_TDM_TX_4", "Secondary TDM4 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("SEC_TDM_RX_5", "Secondary TDM5 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("SEC_TDM_TX_5", "Secondary TDM5 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("SEC_TDM_RX_6", "Secondary TDM6 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("SEC_TDM_TX_6", "Secondary TDM6 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("SEC_TDM_RX_7", "Secondary TDM7 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("SEC_TDM_TX_7", "Secondary TDM7 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("TERT_TDM_RX_0", "Tertiary TDM0 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("TERT_TDM_TX_0", "Tertiary TDM0 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("TERT_TDM_RX_1", "Tertiary TDM1 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("TERT_TDM_TX_1", "Tertiary TDM1 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("TERT_TDM_RX_2", "Tertiary TDM2 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("TERT_TDM_TX_2", "Tertiary TDM2 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("TERT_TDM_RX_3", "Tertiary TDM3 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("TERT_TDM_TX_3", "Tertiary TDM3 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("TERT_TDM_RX_4", "Tertiary TDM4 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("TERT_TDM_TX_4", "Tertiary TDM4 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("TERT_TDM_RX_5", "Tertiary TDM5 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("TERT_TDM_TX_5", "Tertiary TDM5 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("TERT_TDM_RX_6", "Tertiary TDM6 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("TERT_TDM_TX_6", "Tertiary TDM6 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("TERT_TDM_RX_7", "Tertiary TDM7 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("TERT_TDM_TX_7", "Tertiary TDM7 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("QUAT_TDM_RX_0", "Quaternary TDM0 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("QUAT_TDM_TX_0", "Quaternary TDM0 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("QUAT_TDM_RX_1", "Quaternary TDM1 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("QUAT_TDM_TX_1", "Quaternary TDM1 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("QUAT_TDM_RX_2", "Quaternary TDM2 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("QUAT_TDM_TX_2", "Quaternary TDM2 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("QUAT_TDM_RX_3", "Quaternary TDM3 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("QUAT_TDM_TX_3", "Quaternary TDM3 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("QUAT_TDM_RX_4", "Quaternary TDM4 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("QUAT_TDM_TX_4", "Quaternary TDM4 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("QUAT_TDM_RX_5", "Quaternary TDM5 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("QUAT_TDM_TX_5", "Quaternary TDM5 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("QUAT_TDM_RX_6", "Quaternary TDM6 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("QUAT_TDM_TX_6", "Quaternary TDM6 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("QUAT_TDM_RX_7", "Quaternary TDM7 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("QUAT_TDM_TX_7", "Quaternary TDM7 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("QUIN_TDM_RX_0", "Quinary TDM0 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("QUIN_TDM_TX_0", "Quinary TDM0 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("QUIN_TDM_RX_1", "Quinary TDM1 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("QUIN_TDM_TX_1", "Quinary TDM1 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("QUIN_TDM_RX_2", "Quinary TDM2 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("QUIN_TDM_TX_2", "Quinary TDM2 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("QUIN_TDM_RX_3", "Quinary TDM3 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("QUIN_TDM_TX_3", "Quinary TDM3 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("QUIN_TDM_RX_4", "Quinary TDM4 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("QUIN_TDM_TX_4", "Quinary TDM4 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("QUIN_TDM_RX_5", "Quinary TDM5 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("QUIN_TDM_TX_5", "Quinary TDM5 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("QUIN_TDM_RX_6", "Quinary TDM6 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("QUIN_TDM_TX_6", "Quinary TDM6 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("QUIN_TDM_RX_7", "Quinary TDM7 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("QUIN_TDM_TX_7", "Quinary TDM7 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("WSA_CDC_DMA_RX_0", "WSA CDC DMA0 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("WSA_CDC_DMA_TX_0", "WSA CDC DMA0 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("WSA_CDC_DMA_RX_1", "WSA CDC DMA1 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("WSA_CDC_DMA_TX_1", "WSA CDC DMA1 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("WSA_CDC_DMA_TX_2", "WSA CDC DMA2 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("VA_CDC_DMA_TX_0", "VA CDC DMA0 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("VA_CDC_DMA_TX_1", "VA CDC DMA1 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("RX_CDC_DMA_RX_0", "RX CDC DMA0 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("TX_CDC_DMA_TX_0", "TX CDC DMA0 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("RX_CDC_DMA_RX_1", "RX CDC DMA1 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("TX_CDC_DMA_TX_1", "TX CDC DMA1 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("RX_CDC_DMA_RX_2", "RX CDC DMA2 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("TX_CDC_DMA_TX_2", "TX CDC DMA2 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("RX_CDC_DMA_RX_3", "RX CDC DMA3 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("TX_CDC_DMA_TX_3", "TX CDC DMA3 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("RX_CDC_DMA_RX_4", "RX CDC DMA4 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("TX_CDC_DMA_TX_4", "TX CDC DMA4 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("RX_CDC_DMA_RX_5", "RX CDC DMA5 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("TX_CDC_DMA_TX_5", "TX CDC DMA5 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("RX_CDC_DMA_RX_6", "RX CDC DMA6 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("RX_CDC_DMA_RX_7", "RX CDC DMA7 Playback", + 0, 0, 0, 0), + /* incall */ + SND_SOC_DAPM_AIF_OUT("VOICE_PLAYBACK_TX", "Voice Farend Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("VOICE2_PLAYBACK_TX", "Voice2 Farend Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("SLIMBUS_4_RX", "Slimbus4 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("INCALL_RECORD_TX", "Voice Uplink Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("INCALL_RECORD_RX", "Voice Downlink Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("SLIMBUS_4_TX", "Slimbus4 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("SENARY_TX", "Senary_mi2s Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("INT5_MI2S_TX", "INT5 MI2S Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("SLIMBUS_5_TX", "Slimbus5 Capture", 0, 0, 0, 0), + + SND_SOC_DAPM_AIF_OUT("AUX_PCM_RX", "AUX PCM Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("AUX_PCM_TX", "AUX PCM Capture", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("SEC_AUX_PCM_RX", "Sec AUX PCM Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("SEC_AUX_PCM_TX", "Sec AUX PCM Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("TERT_AUX_PCM_RX", "Tert AUX PCM Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("TERT_AUX_PCM_TX", "Tert AUX PCM Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("QUAT_AUX_PCM_RX", "Quat AUX PCM Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("QUAT_AUX_PCM_TX", "Quat AUX PCM Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("QUIN_AUX_PCM_RX", "Quin AUX PCM Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("QUIN_AUX_PCM_TX", "Quin AUX PCM Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("VOICE_STUB_DL", "VOICE_STUB Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("VOICE_STUB_UL", "VOICE_STUB Capture", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("VOICE2_STUB_DL", "VOICE2_STUB Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("VOICE2_STUB_UL", "VOICE2_STUB Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("VOLTE_STUB_DL", "VOLTE_STUB Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("VOLTE_STUB_UL", "VOLTE_STUB Capture", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("STUB_RX", "Stub Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("STUB_TX", "Stub Capture", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("SLIMBUS_1_RX", "Slimbus1 Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("SLIMBUS_1_TX", "Slimbus1 Capture", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("STUB_1_TX", "Stub1 Capture", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("SLIMBUS_3_RX", "Slimbus3 Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("SLIMBUS_3_TX", "Slimbus3 Capture", 0, 0, 0, 0), + /* In- call recording */ + SND_SOC_DAPM_AIF_OUT("SLIMBUS_6_RX", "Slimbus6 Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("SLIMBUS_6_TX", "Slimbus6 Capture", 0, 0, 0, 0), + + SND_SOC_DAPM_AIF_OUT("SLIMBUS_7_RX", "Slimbus7 Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("SLIMBUS_7_TX", "Slimbus7 Capture", 0, 0, 0, 0), + + SND_SOC_DAPM_AIF_OUT("SLIMBUS_8_RX", "Slimbus8 Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("SLIMBUS_8_TX", "Slimbus8 Capture", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("SLIMBUS_9_RX", "Slimbus9 Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("SLIMBUS_9_TX", "Slimbus9 Capture", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("USB_AUDIO_RX", "USB Audio Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("USB_AUDIO_TX", "USB Audio Capture", 0, 0, 0, 0), + + /* Switch Definitions */ + SND_SOC_DAPM_SWITCH("SLIMBUS_DL_HL", SND_SOC_NOPM, 0, 0, + &slim_fm_switch_mixer_controls), + SND_SOC_DAPM_SWITCH("SLIMBUS1_DL_HL", SND_SOC_NOPM, 0, 0, + &slim1_fm_switch_mixer_controls), + SND_SOC_DAPM_SWITCH("SLIMBUS3_DL_HL", SND_SOC_NOPM, 0, 0, + &slim3_fm_switch_mixer_controls), + SND_SOC_DAPM_SWITCH("SLIMBUS4_DL_HL", SND_SOC_NOPM, 0, 0, + &slim4_fm_switch_mixer_controls), + SND_SOC_DAPM_SWITCH("SLIMBUS6_DL_HL", SND_SOC_NOPM, 0, 0, + &slim6_fm_switch_mixer_controls), + SND_SOC_DAPM_SWITCH("PCM_RX_DL_HL", SND_SOC_NOPM, 0, 0, + &pcm_rx_switch_mixer_controls), + SND_SOC_DAPM_SWITCH("INT0_MI2S_RX_DL_HL", SND_SOC_NOPM, 0, 0, + &int0_mi2s_rx_switch_mixer_controls), + SND_SOC_DAPM_SWITCH("INT4_MI2S_RX_DL_HL", SND_SOC_NOPM, 0, 0, + &int4_mi2s_rx_switch_mixer_controls), + SND_SOC_DAPM_SWITCH("PRI_MI2S_RX_DL_HL", SND_SOC_NOPM, 0, 0, + &pri_mi2s_rx_switch_mixer_controls), + SND_SOC_DAPM_SWITCH("SEC_MI2S_RX_DL_HL", SND_SOC_NOPM, 0, 0, + &sec_mi2s_rx_switch_mixer_controls), + SND_SOC_DAPM_SWITCH("TERT_MI2S_RX_DL_HL", SND_SOC_NOPM, 0, 0, + &tert_mi2s_rx_switch_mixer_controls), + SND_SOC_DAPM_SWITCH("QUAT_MI2S_RX_DL_HL", SND_SOC_NOPM, 0, 0, + &quat_mi2s_rx_switch_mixer_controls), + SND_SOC_DAPM_SWITCH("QUIN_MI2S_RX_DL_HL", SND_SOC_NOPM, 0, 0, + &quin_mi2s_rx_switch_mixer_controls), + SND_SOC_DAPM_SWITCH("HFP_PRI_AUX_UL_HL", SND_SOC_NOPM, 0, 0, + &hfp_pri_aux_switch_mixer_controls), + SND_SOC_DAPM_SWITCH("HFP_AUX_UL_HL", SND_SOC_NOPM, 0, 0, + &hfp_aux_switch_mixer_controls), + SND_SOC_DAPM_SWITCH("HFP_INT_UL_HL", SND_SOC_NOPM, 0, 0, + &hfp_int_switch_mixer_controls), + SND_SOC_DAPM_SWITCH("HFP_SLIM7_UL_HL", SND_SOC_NOPM, 0, 0, + &hfp_slim7_switch_mixer_controls), + SND_SOC_DAPM_SWITCH("USB_DL_HL", SND_SOC_NOPM, 0, 0, + &usb_switch_mixer_controls), + SND_SOC_DAPM_SWITCH("A2DP_SLIM7_UL_HL", SND_SOC_NOPM, 0, 0, + &a2dp_slim7_switch_mixer_controls), + SND_SOC_DAPM_SWITCH("WSA_CDC_DMA_RX_0_DL_HL", SND_SOC_NOPM, 0, 0, + &cdc_dma_wsa_switch_mixer_controls), + SND_SOC_DAPM_SWITCH("RX_CDC_DMA_RX_0_DL_HL", SND_SOC_NOPM, 0, 0, + &cdc_dma_rx_switch_mixer_controls), + + /* Mixer definitions */ + SND_SOC_DAPM_MIXER("PRI_RX Audio Mixer", SND_SOC_NOPM, 0, 0, + pri_i2s_rx_mixer_controls, ARRAY_SIZE(pri_i2s_rx_mixer_controls)), + SND_SOC_DAPM_MIXER("SEC_RX Audio Mixer", SND_SOC_NOPM, 0, 0, + sec_i2s_rx_mixer_controls, ARRAY_SIZE(sec_i2s_rx_mixer_controls)), + SND_SOC_DAPM_MIXER("SLIMBUS_0_RX Audio Mixer", SND_SOC_NOPM, 0, 0, + slimbus_rx_mixer_controls, ARRAY_SIZE(slimbus_rx_mixer_controls)), + SND_SOC_DAPM_MIXER("SLIMBUS_2_RX Audio Mixer", SND_SOC_NOPM, 0, 0, + slimbus_2_rx_mixer_controls, ARRAY_SIZE(slimbus_2_rx_mixer_controls)), + SND_SOC_DAPM_MIXER("SLIMBUS_5_RX Audio Mixer", SND_SOC_NOPM, 0, 0, + slimbus_5_rx_mixer_controls, ARRAY_SIZE(slimbus_5_rx_mixer_controls)), + SND_SOC_DAPM_MIXER("SLIMBUS_7_RX Audio Mixer", SND_SOC_NOPM, 0, 0, + slimbus_7_rx_mixer_controls, ARRAY_SIZE(slimbus_7_rx_mixer_controls)), + SND_SOC_DAPM_MIXER("SLIMBUS_9_RX Audio Mixer", SND_SOC_NOPM, 0, 0, + slimbus_9_rx_mixer_controls, ARRAY_SIZE(slimbus_9_rx_mixer_controls)), + SND_SOC_DAPM_MIXER("HDMI Mixer", SND_SOC_NOPM, 0, 0, + hdmi_mixer_controls, ARRAY_SIZE(hdmi_mixer_controls)), + SND_SOC_DAPM_MIXER("DISPLAY_PORT Mixer", SND_SOC_NOPM, 0, 0, + display_port_mixer_controls, ARRAY_SIZE(display_port_mixer_controls)), + SND_SOC_DAPM_MIXER("DISPLAY_PORT1 Mixer", SND_SOC_NOPM, 0, 0, + display_port1_mixer_controls, ARRAY_SIZE(display_port1_mixer_controls)), + SND_SOC_DAPM_MIXER("PRI_SPDIF_RX Audio Mixer", SND_SOC_NOPM, 0, 0, + pri_spdif_rx_mixer_controls, ARRAY_SIZE(pri_spdif_rx_mixer_controls)), + SND_SOC_DAPM_MIXER("SEC_SPDIF_RX Audio Mixer", SND_SOC_NOPM, 0, 0, + sec_spdif_rx_mixer_controls, ARRAY_SIZE(sec_spdif_rx_mixer_controls)), + SND_SOC_DAPM_MIXER("MI2S_RX Audio Mixer", SND_SOC_NOPM, 0, 0, + mi2s_rx_mixer_controls, ARRAY_SIZE(mi2s_rx_mixer_controls)), + SND_SOC_DAPM_MIXER("QUAT_MI2S_RX Audio Mixer", SND_SOC_NOPM, 0, 0, + quaternary_mi2s_rx_mixer_controls, + ARRAY_SIZE(quaternary_mi2s_rx_mixer_controls)), + SND_SOC_DAPM_MIXER("TERT_MI2S_RX Audio Mixer", SND_SOC_NOPM, 0, 0, + tertiary_mi2s_rx_mixer_controls, + ARRAY_SIZE(tertiary_mi2s_rx_mixer_controls)), + SND_SOC_DAPM_MIXER("SEC_MI2S_RX Audio Mixer", SND_SOC_NOPM, 0, 0, + secondary_mi2s_rx_mixer_controls, + ARRAY_SIZE(secondary_mi2s_rx_mixer_controls)), + SND_SOC_DAPM_MIXER("SEC_MI2S_RX_SD1 Audio Mixer", SND_SOC_NOPM, 0, 0, + secondary_mi2s_rx2_mixer_controls, + ARRAY_SIZE(secondary_mi2s_rx2_mixer_controls)), + SND_SOC_DAPM_MIXER("PRI_MI2S_RX Audio Mixer", SND_SOC_NOPM, 0, 0, + primary_mi2s_rx_mixer_controls, + ARRAY_SIZE(primary_mi2s_rx_mixer_controls)), + SND_SOC_DAPM_MIXER("INT0_MI2S_RX Audio Mixer", SND_SOC_NOPM, 0, 0, + int0_mi2s_rx_mixer_controls, + ARRAY_SIZE(int0_mi2s_rx_mixer_controls)), + SND_SOC_DAPM_MIXER("INT4_MI2S_RX Audio Mixer", SND_SOC_NOPM, 0, 0, + int4_mi2s_rx_mixer_controls, + ARRAY_SIZE(int4_mi2s_rx_mixer_controls)), + SND_SOC_DAPM_MIXER("QUIN_MI2S_RX Audio Mixer", SND_SOC_NOPM, 0, 0, + quinary_mi2s_rx_mixer_controls, + ARRAY_SIZE(quinary_mi2s_rx_mixer_controls)), + SND_SOC_DAPM_MIXER("PRI_TDM_RX_0 Audio Mixer", SND_SOC_NOPM, 0, 0, + pri_tdm_rx_0_mixer_controls, + ARRAY_SIZE(pri_tdm_rx_0_mixer_controls)), + SND_SOC_DAPM_MIXER("PRI_TDM_RX_1 Audio Mixer", SND_SOC_NOPM, 0, 0, + pri_tdm_rx_1_mixer_controls, + ARRAY_SIZE(pri_tdm_rx_1_mixer_controls)), + SND_SOC_DAPM_MIXER("PRI_TDM_RX_2 Audio Mixer", SND_SOC_NOPM, 0, 0, + pri_tdm_rx_2_mixer_controls, + ARRAY_SIZE(pri_tdm_rx_2_mixer_controls)), + SND_SOC_DAPM_MIXER("PRI_TDM_RX_3 Audio Mixer", SND_SOC_NOPM, 0, 0, + pri_tdm_rx_3_mixer_controls, + ARRAY_SIZE(pri_tdm_rx_3_mixer_controls)), + SND_SOC_DAPM_MIXER("PRI_TDM_TX_0 Audio Mixer", SND_SOC_NOPM, 0, 0, + pri_tdm_tx_0_mixer_controls, + ARRAY_SIZE(pri_tdm_tx_0_mixer_controls)), + SND_SOC_DAPM_MIXER("SEC_TDM_RX_0 Audio Mixer", SND_SOC_NOPM, 0, 0, + sec_tdm_rx_0_mixer_controls, + ARRAY_SIZE(sec_tdm_rx_0_mixer_controls)), + SND_SOC_DAPM_MIXER("SEC_TDM_RX_1 Audio Mixer", SND_SOC_NOPM, 0, 0, + sec_tdm_rx_1_mixer_controls, + ARRAY_SIZE(sec_tdm_rx_1_mixer_controls)), + SND_SOC_DAPM_MIXER("SEC_TDM_RX_2 Audio Mixer", SND_SOC_NOPM, 0, 0, + sec_tdm_rx_2_mixer_controls, + ARRAY_SIZE(sec_tdm_rx_2_mixer_controls)), + SND_SOC_DAPM_MIXER("SEC_TDM_RX_3 Audio Mixer", SND_SOC_NOPM, 0, 0, + sec_tdm_rx_3_mixer_controls, + ARRAY_SIZE(sec_tdm_rx_3_mixer_controls)), + SND_SOC_DAPM_MIXER("SEC_TDM_TX_0 Audio Mixer", SND_SOC_NOPM, 0, 0, + sec_tdm_tx_0_mixer_controls, + ARRAY_SIZE(sec_tdm_tx_0_mixer_controls)), + SND_SOC_DAPM_MIXER("TERT_TDM_RX_0 Audio Mixer", SND_SOC_NOPM, 0, 0, + tert_tdm_rx_0_mixer_controls, + ARRAY_SIZE(tert_tdm_rx_0_mixer_controls)), + SND_SOC_DAPM_MIXER("TERT_TDM_TX_0 Audio Mixer", SND_SOC_NOPM, 0, 0, + tert_tdm_tx_0_mixer_controls, + ARRAY_SIZE(tert_tdm_tx_0_mixer_controls)), + SND_SOC_DAPM_MIXER("TERT_TDM_RX_1 Audio Mixer", SND_SOC_NOPM, 0, 0, + tert_tdm_rx_1_mixer_controls, + ARRAY_SIZE(tert_tdm_rx_1_mixer_controls)), + SND_SOC_DAPM_MIXER("TERT_TDM_RX_2 Audio Mixer", SND_SOC_NOPM, 0, 0, + tert_tdm_rx_2_mixer_controls, + ARRAY_SIZE(tert_tdm_rx_2_mixer_controls)), + SND_SOC_DAPM_MIXER("TERT_TDM_RX_3 Audio Mixer", SND_SOC_NOPM, 0, 0, + tert_tdm_rx_3_mixer_controls, + ARRAY_SIZE(tert_tdm_rx_3_mixer_controls)), + SND_SOC_DAPM_MIXER("TERT_TDM_RX_4 Audio Mixer", SND_SOC_NOPM, 0, 0, + tert_tdm_rx_4_mixer_controls, + ARRAY_SIZE(tert_tdm_rx_4_mixer_controls)), + SND_SOC_DAPM_MIXER("QUAT_TDM_RX_0 Audio Mixer", SND_SOC_NOPM, 0, 0, + quat_tdm_rx_0_mixer_controls, + ARRAY_SIZE(quat_tdm_rx_0_mixer_controls)), + SND_SOC_DAPM_MIXER("QUAT_TDM_TX_0 Audio Mixer", SND_SOC_NOPM, 0, 0, + quat_tdm_tx_0_mixer_controls, + ARRAY_SIZE(quat_tdm_tx_0_mixer_controls)), + SND_SOC_DAPM_MIXER("QUAT_TDM_RX_1 Audio Mixer", SND_SOC_NOPM, 0, 0, + quat_tdm_rx_1_mixer_controls, + ARRAY_SIZE(quat_tdm_rx_1_mixer_controls)), + SND_SOC_DAPM_MIXER("QUAT_TDM_RX_2 Audio Mixer", SND_SOC_NOPM, 0, 0, + quat_tdm_rx_2_mixer_controls, + ARRAY_SIZE(quat_tdm_rx_2_mixer_controls)), + SND_SOC_DAPM_MIXER("QUAT_TDM_RX_3 Audio Mixer", SND_SOC_NOPM, 0, 0, + quat_tdm_rx_3_mixer_controls, + ARRAY_SIZE(quat_tdm_rx_3_mixer_controls)), + SND_SOC_DAPM_MIXER("QUIN_TDM_RX_0 Audio Mixer", SND_SOC_NOPM, 0, 0, + quin_tdm_rx_0_mixer_controls, + ARRAY_SIZE(quin_tdm_rx_0_mixer_controls)), + SND_SOC_DAPM_MIXER("QUIN_TDM_TX_0 Audio Mixer", SND_SOC_NOPM, 0, 0, + quin_tdm_tx_0_mixer_controls, + ARRAY_SIZE(quin_tdm_tx_0_mixer_controls)), + SND_SOC_DAPM_MIXER("QUIN_TDM_RX_1 Audio Mixer", SND_SOC_NOPM, 0, 0, + quin_tdm_rx_1_mixer_controls, + ARRAY_SIZE(quin_tdm_rx_1_mixer_controls)), + SND_SOC_DAPM_MIXER("QUIN_TDM_RX_2 Audio Mixer", SND_SOC_NOPM, 0, 0, + quin_tdm_rx_2_mixer_controls, + ARRAY_SIZE(quin_tdm_rx_2_mixer_controls)), + SND_SOC_DAPM_MIXER("QUIN_TDM_RX_3 Audio Mixer", SND_SOC_NOPM, 0, 0, + quin_tdm_rx_3_mixer_controls, + ARRAY_SIZE(quin_tdm_rx_3_mixer_controls)), + SND_SOC_DAPM_MIXER("WSA_CDC_DMA_RX_0 Audio Mixer", SND_SOC_NOPM, 0, 0, + wsa_cdc_dma_rx_0_mixer_controls, + ARRAY_SIZE(wsa_cdc_dma_rx_0_mixer_controls)), + SND_SOC_DAPM_MIXER("WSA_CDC_DMA_RX_1 Audio Mixer", SND_SOC_NOPM, 0, 0, + wsa_cdc_dma_rx_1_mixer_controls, + ARRAY_SIZE(wsa_cdc_dma_rx_1_mixer_controls)), + SND_SOC_DAPM_MIXER("RX_CDC_DMA_RX_0 Audio Mixer", SND_SOC_NOPM, 0, 0, + rx_cdc_dma_rx_0_mixer_controls, + ARRAY_SIZE(rx_cdc_dma_rx_0_mixer_controls)), + SND_SOC_DAPM_MIXER("RX_CDC_DMA_RX_1 Audio Mixer", SND_SOC_NOPM, 0, 0, + rx_cdc_dma_rx_1_mixer_controls, + ARRAY_SIZE(rx_cdc_dma_rx_1_mixer_controls)), + SND_SOC_DAPM_MIXER("RX_CDC_DMA_RX_2 Audio Mixer", SND_SOC_NOPM, 0, 0, + rx_cdc_dma_rx_2_mixer_controls, + ARRAY_SIZE(rx_cdc_dma_rx_2_mixer_controls)), + SND_SOC_DAPM_MIXER("RX_CDC_DMA_RX_3 Audio Mixer", SND_SOC_NOPM, 0, 0, + rx_cdc_dma_rx_3_mixer_controls, + ARRAY_SIZE(rx_cdc_dma_rx_3_mixer_controls)), + SND_SOC_DAPM_MIXER("RX_CDC_DMA_RX_4 Audio Mixer", SND_SOC_NOPM, 0, 0, + rx_cdc_dma_rx_4_mixer_controls, + ARRAY_SIZE(rx_cdc_dma_rx_4_mixer_controls)), + SND_SOC_DAPM_MIXER("RX_CDC_DMA_RX_5 Audio Mixer", SND_SOC_NOPM, 0, 0, + rx_cdc_dma_rx_5_mixer_controls, + ARRAY_SIZE(rx_cdc_dma_rx_5_mixer_controls)), + SND_SOC_DAPM_MIXER("RX_CDC_DMA_RX_6 Audio Mixer", SND_SOC_NOPM, 0, 0, + rx_cdc_dma_rx_6_mixer_controls, + ARRAY_SIZE(rx_cdc_dma_rx_6_mixer_controls)), + SND_SOC_DAPM_MIXER("RX_CDC_DMA_RX_7 Audio Mixer", SND_SOC_NOPM, 0, 0, + rx_cdc_dma_rx_7_mixer_controls, + ARRAY_SIZE(rx_cdc_dma_rx_7_mixer_controls)), + SND_SOC_DAPM_MIXER("MultiMedia1 Mixer", SND_SOC_NOPM, 0, 0, + mmul1_mixer_controls, ARRAY_SIZE(mmul1_mixer_controls)), + SND_SOC_DAPM_MIXER("MultiMedia2 Mixer", SND_SOC_NOPM, 0, 0, + mmul2_mixer_controls, ARRAY_SIZE(mmul2_mixer_controls)), + SND_SOC_DAPM_MIXER("MultiMedia3 Mixer", SND_SOC_NOPM, 0, 0, + mmul3_mixer_controls, ARRAY_SIZE(mmul3_mixer_controls)), + SND_SOC_DAPM_MIXER("MultiMedia4 Mixer", SND_SOC_NOPM, 0, 0, + mmul4_mixer_controls, ARRAY_SIZE(mmul4_mixer_controls)), + SND_SOC_DAPM_MIXER("MultiMedia5 Mixer", SND_SOC_NOPM, 0, 0, + mmul5_mixer_controls, ARRAY_SIZE(mmul5_mixer_controls)), + SND_SOC_DAPM_MIXER("MultiMedia6 Mixer", SND_SOC_NOPM, 0, 0, + mmul6_mixer_controls, ARRAY_SIZE(mmul6_mixer_controls)), + SND_SOC_DAPM_MIXER("MultiMedia8 Mixer", SND_SOC_NOPM, 0, 0, + mmul8_mixer_controls, ARRAY_SIZE(mmul8_mixer_controls)), + SND_SOC_DAPM_MIXER("MultiMedia9 Mixer", SND_SOC_NOPM, 0, 0, + mmul9_mixer_controls, ARRAY_SIZE(mmul9_mixer_controls)), + SND_SOC_DAPM_MIXER("MultiMedia10 Mixer", SND_SOC_NOPM, 0, 0, + mmul10_mixer_controls, ARRAY_SIZE(mmul10_mixer_controls)), + SND_SOC_DAPM_MIXER("MultiMedia16 Mixer", SND_SOC_NOPM, 0, 0, + mmul16_mixer_controls, ARRAY_SIZE(mmul16_mixer_controls)), + SND_SOC_DAPM_MIXER("MultiMedia17 Mixer", SND_SOC_NOPM, 0, 0, + mmul17_mixer_controls, ARRAY_SIZE(mmul17_mixer_controls)), + SND_SOC_DAPM_MIXER("MultiMedia18 Mixer", SND_SOC_NOPM, 0, 0, + mmul18_mixer_controls, ARRAY_SIZE(mmul18_mixer_controls)), + SND_SOC_DAPM_MIXER("MultiMedia19 Mixer", SND_SOC_NOPM, 0, 0, + mmul19_mixer_controls, ARRAY_SIZE(mmul19_mixer_controls)), + SND_SOC_DAPM_MIXER("MultiMedia20 Mixer", SND_SOC_NOPM, 0, 0, + mmul20_mixer_controls, ARRAY_SIZE(mmul20_mixer_controls)), + SND_SOC_DAPM_MIXER("MultiMedia21 Mixer", SND_SOC_NOPM, 0, 0, + mmul21_mixer_controls, ARRAY_SIZE(mmul21_mixer_controls)), + SND_SOC_DAPM_MIXER("MultiMedia27 Mixer", SND_SOC_NOPM, 0, 0, + mmul27_mixer_controls, ARRAY_SIZE(mmul27_mixer_controls)), + SND_SOC_DAPM_MIXER("MultiMedia28 Mixer", SND_SOC_NOPM, 0, 0, + mmul28_mixer_controls, ARRAY_SIZE(mmul28_mixer_controls)), + SND_SOC_DAPM_MIXER("MultiMedia29 Mixer", SND_SOC_NOPM, 0, 0, + mmul29_mixer_controls, ARRAY_SIZE(mmul29_mixer_controls)), + SND_SOC_DAPM_MIXER("AUX_PCM_RX Audio Mixer", SND_SOC_NOPM, 0, 0, + auxpcm_rx_mixer_controls, ARRAY_SIZE(auxpcm_rx_mixer_controls)), + SND_SOC_DAPM_MIXER("SEC_AUX_PCM_RX Audio Mixer", SND_SOC_NOPM, 0, 0, + sec_auxpcm_rx_mixer_controls, ARRAY_SIZE(sec_auxpcm_rx_mixer_controls)), + SND_SOC_DAPM_MIXER("TERT_AUX_PCM_RX Audio Mixer", SND_SOC_NOPM, 0, 0, + tert_auxpcm_rx_mixer_controls, + ARRAY_SIZE(tert_auxpcm_rx_mixer_controls)), + SND_SOC_DAPM_MIXER("QUAT_AUX_PCM_RX Audio Mixer", SND_SOC_NOPM, 0, 0, + quat_auxpcm_rx_mixer_controls, + ARRAY_SIZE(quat_auxpcm_rx_mixer_controls)), + SND_SOC_DAPM_MIXER("QUIN_AUX_PCM_RX Audio Mixer", SND_SOC_NOPM, 0, 0, + quin_auxpcm_rx_mixer_controls, + ARRAY_SIZE(quin_auxpcm_rx_mixer_controls)), + /* incall */ + SND_SOC_DAPM_MIXER("Incall_Music Audio Mixer", SND_SOC_NOPM, 0, 0, + incall_music_delivery_mixer_controls, + ARRAY_SIZE(incall_music_delivery_mixer_controls)), + SND_SOC_DAPM_MIXER("Incall_Music_2 Audio Mixer", SND_SOC_NOPM, 0, 0, + incall_music2_delivery_mixer_controls, + ARRAY_SIZE(incall_music2_delivery_mixer_controls)), + SND_SOC_DAPM_MIXER("SLIMBUS_4_RX Audio Mixer", SND_SOC_NOPM, 0, 0, + slimbus_4_rx_mixer_controls, + ARRAY_SIZE(slimbus_4_rx_mixer_controls)), + SND_SOC_DAPM_MIXER("SLIMBUS_6_RX Audio Mixer", SND_SOC_NOPM, 0, 0, + slimbus_6_rx_mixer_controls, + ARRAY_SIZE(slimbus_6_rx_mixer_controls)), + SND_SOC_DAPM_MIXER("USB_AUDIO_RX Audio Mixer", SND_SOC_NOPM, 0, 0, + usb_audio_rx_mixer_controls, + ARRAY_SIZE(usb_audio_rx_mixer_controls)), + /* Voice Mixer */ + SND_SOC_DAPM_MIXER("PRI_RX_Voice Mixer", + SND_SOC_NOPM, 0, 0, pri_rx_voice_mixer_controls, + ARRAY_SIZE(pri_rx_voice_mixer_controls)), + SND_SOC_DAPM_MIXER("SEC_RX_Voice Mixer", + SND_SOC_NOPM, 0, 0, + sec_i2s_rx_voice_mixer_controls, + ARRAY_SIZE(sec_i2s_rx_voice_mixer_controls)), + SND_SOC_DAPM_MIXER("SEC_MI2S_RX_Voice Mixer", + SND_SOC_NOPM, 0, 0, + sec_mi2s_rx_voice_mixer_controls, + ARRAY_SIZE(sec_mi2s_rx_voice_mixer_controls)), + SND_SOC_DAPM_MIXER("SLIM_0_RX_Voice Mixer", + SND_SOC_NOPM, 0, 0, + slimbus_rx_voice_mixer_controls, + ARRAY_SIZE(slimbus_rx_voice_mixer_controls)), + SND_SOC_DAPM_MIXER("INTERNAL_BT_SCO_RX_Voice Mixer", + SND_SOC_NOPM, 0, 0, + bt_sco_rx_voice_mixer_controls, + ARRAY_SIZE(bt_sco_rx_voice_mixer_controls)), + SND_SOC_DAPM_MIXER("AFE_PCM_RX_Voice Mixer", + SND_SOC_NOPM, 0, 0, + afe_pcm_rx_voice_mixer_controls, + ARRAY_SIZE(afe_pcm_rx_voice_mixer_controls)), + SND_SOC_DAPM_MIXER("AUX_PCM_RX_Voice Mixer", + SND_SOC_NOPM, 0, 0, + aux_pcm_rx_voice_mixer_controls, + ARRAY_SIZE(aux_pcm_rx_voice_mixer_controls)), + SND_SOC_DAPM_MIXER("SEC_AUX_PCM_RX_Voice Mixer", + SND_SOC_NOPM, 0, 0, + sec_aux_pcm_rx_voice_mixer_controls, + ARRAY_SIZE(sec_aux_pcm_rx_voice_mixer_controls)), + SND_SOC_DAPM_MIXER("TERT_AUX_PCM_RX_Voice Mixer", + SND_SOC_NOPM, 0, 0, + tert_aux_pcm_rx_voice_mixer_controls, + ARRAY_SIZE(tert_aux_pcm_rx_voice_mixer_controls)), + SND_SOC_DAPM_MIXER("QUAT_AUX_PCM_RX_Voice Mixer", + SND_SOC_NOPM, 0, 0, + quat_aux_pcm_rx_voice_mixer_controls, + ARRAY_SIZE(quat_aux_pcm_rx_voice_mixer_controls)), + SND_SOC_DAPM_MIXER("QUIN_AUX_PCM_RX_Voice Mixer", + SND_SOC_NOPM, 0, 0, + quin_aux_pcm_rx_voice_mixer_controls, + ARRAY_SIZE(quin_aux_pcm_rx_voice_mixer_controls)), + SND_SOC_DAPM_MIXER("HDMI_RX_Voice Mixer", + SND_SOC_NOPM, 0, 0, + hdmi_rx_voice_mixer_controls, + ARRAY_SIZE(hdmi_rx_voice_mixer_controls)), + SND_SOC_DAPM_MIXER("MI2S_RX_Voice Mixer", + SND_SOC_NOPM, 0, 0, + mi2s_rx_voice_mixer_controls, + ARRAY_SIZE(mi2s_rx_voice_mixer_controls)), + SND_SOC_DAPM_MIXER("PRI_MI2S_RX_Voice Mixer", + SND_SOC_NOPM, 0, 0, + pri_mi2s_rx_voice_mixer_controls, + ARRAY_SIZE(pri_mi2s_rx_voice_mixer_controls)), + SND_SOC_DAPM_MIXER("INT0_MI2S_RX_Voice Mixer", + SND_SOC_NOPM, 0, 0, + int0_mi2s_rx_voice_mixer_controls, + ARRAY_SIZE(int0_mi2s_rx_voice_mixer_controls)), + SND_SOC_DAPM_MIXER("INT4_MI2S_RX_Voice Mixer", + SND_SOC_NOPM, 0, 0, + int4_mi2s_rx_voice_mixer_controls, + ARRAY_SIZE(int4_mi2s_rx_voice_mixer_controls)), + SND_SOC_DAPM_MIXER("TERT_MI2S_RX_Voice Mixer", + SND_SOC_NOPM, 0, 0, + tert_mi2s_rx_voice_mixer_controls, + ARRAY_SIZE(tert_mi2s_rx_voice_mixer_controls)), + SND_SOC_DAPM_MIXER("QUAT_MI2S_RX_Voice Mixer", + SND_SOC_NOPM, 0, 0, + quat_mi2s_rx_voice_mixer_controls, + ARRAY_SIZE(quat_mi2s_rx_voice_mixer_controls)), + SND_SOC_DAPM_MIXER("QUIN_MI2S_RX_Voice Mixer", + SND_SOC_NOPM, 0, 0, + quin_mi2s_rx_voice_mixer_controls, + ARRAY_SIZE(quin_mi2s_rx_voice_mixer_controls)), + SND_SOC_DAPM_MIXER("QUAT_TDM_RX_2_Voice Mixer", + SND_SOC_NOPM, 0, 0, + quat_tdm_rx_2_voice_mixer_controls, + ARRAY_SIZE(quat_tdm_rx_2_voice_mixer_controls)), + SND_SOC_DAPM_MIXER("WSA_CDC_DMA_RX_0_Voice Mixer", + SND_SOC_NOPM, 0, 0, + wsa_cdc_dma_rx_0_voice_mixer_controls, + ARRAY_SIZE(wsa_cdc_dma_rx_0_voice_mixer_controls)), + SND_SOC_DAPM_MIXER("RX_CDC_DMA_RX_0_Voice Mixer", + SND_SOC_NOPM, 0, 0, + rx_cdc_dma_rx_0_voice_mixer_controls, + ARRAY_SIZE(rx_cdc_dma_rx_0_voice_mixer_controls)), + SND_SOC_DAPM_MIXER("Voip_Tx Mixer", + SND_SOC_NOPM, 0, 0, tx_voip_mixer_controls, + ARRAY_SIZE(tx_voip_mixer_controls)), + SND_SOC_DAPM_MIXER("VoiceMMode1_Tx Mixer", + SND_SOC_NOPM, 0, 0, tx_voicemmode1_mixer_controls, + ARRAY_SIZE(tx_voicemmode1_mixer_controls)), + SND_SOC_DAPM_MIXER("VoiceMMode2_Tx Mixer", + SND_SOC_NOPM, 0, 0, tx_voicemmode2_mixer_controls, + ARRAY_SIZE(tx_voicemmode2_mixer_controls)), + SND_SOC_DAPM_MIXER("INTERNAL_BT_SCO_RX Audio Mixer", SND_SOC_NOPM, 0, 0, + int_bt_sco_rx_mixer_controls, ARRAY_SIZE(int_bt_sco_rx_mixer_controls)), + SND_SOC_DAPM_MIXER("INTERNAL_A2DP_RX Audio Mixer", SND_SOC_NOPM, 0, 0, + int_bt_a2dp_rx_mixer_controls, + ARRAY_SIZE(int_bt_a2dp_rx_mixer_controls)), + SND_SOC_DAPM_MIXER("INTERNAL_FM_RX Audio Mixer", SND_SOC_NOPM, 0, 0, + int_fm_rx_mixer_controls, ARRAY_SIZE(int_fm_rx_mixer_controls)), + SND_SOC_DAPM_MIXER("AFE_PCM_RX Audio Mixer", SND_SOC_NOPM, 0, 0, + afe_pcm_rx_mixer_controls, ARRAY_SIZE(afe_pcm_rx_mixer_controls)), + SND_SOC_DAPM_MIXER("Voice Stub Tx Mixer", SND_SOC_NOPM, 0, 0, + tx_voice_stub_mixer_controls, ARRAY_SIZE(tx_voice_stub_mixer_controls)), + SND_SOC_DAPM_MIXER("Voice2 Stub Tx Mixer", SND_SOC_NOPM, 0, 0, + tx_voice2_stub_mixer_controls, + ARRAY_SIZE(tx_voice2_stub_mixer_controls)), + SND_SOC_DAPM_MIXER("VoLTE Stub Tx Mixer", SND_SOC_NOPM, 0, 0, + tx_volte_stub_mixer_controls, ARRAY_SIZE(tx_volte_stub_mixer_controls)), + SND_SOC_DAPM_MIXER("STUB_RX Mixer", SND_SOC_NOPM, 0, 0, + stub_rx_mixer_controls, ARRAY_SIZE(stub_rx_mixer_controls)), + SND_SOC_DAPM_MIXER("SLIMBUS_1_RX Mixer", SND_SOC_NOPM, 0, 0, + slimbus_1_rx_mixer_controls, ARRAY_SIZE(slimbus_1_rx_mixer_controls)), + SND_SOC_DAPM_MIXER("SLIMBUS_3_RX_Voice Mixer", SND_SOC_NOPM, 0, 0, + slimbus_3_rx_mixer_controls, ARRAY_SIZE(slimbus_3_rx_mixer_controls)), + SND_SOC_DAPM_MIXER("SLIM_6_RX_Voice Mixer", + SND_SOC_NOPM, 0, 0, + slimbus_6_rx_voice_mixer_controls, + ARRAY_SIZE(slimbus_6_rx_voice_mixer_controls)), + SND_SOC_DAPM_MIXER("SLIM_7_RX_Voice Mixer", SND_SOC_NOPM, 0, 0, + slimbus_7_rx_voice_mixer_controls, + ARRAY_SIZE(slimbus_7_rx_voice_mixer_controls)), + SND_SOC_DAPM_MIXER("SLIM_8_RX_Voice Mixer", SND_SOC_NOPM, 0, 0, + slimbus_8_rx_voice_mixer_controls, + ARRAY_SIZE(slimbus_8_rx_voice_mixer_controls)), + /* port mixer */ + SND_SOC_DAPM_MIXER("SLIMBUS_0_RX Port Mixer", + SND_SOC_NOPM, 0, 0, sbus_0_rx_port_mixer_controls, + ARRAY_SIZE(sbus_0_rx_port_mixer_controls)), + SND_SOC_DAPM_MIXER("AUX_PCM_RX Port Mixer", + SND_SOC_NOPM, 0, 0, aux_pcm_rx_port_mixer_controls, + ARRAY_SIZE(aux_pcm_rx_port_mixer_controls)), + SND_SOC_DAPM_MIXER("SEC_AUXPCM_RX Port Mixer", + SND_SOC_NOPM, 0, 0, sec_auxpcm_rx_port_mixer_controls, + ARRAY_SIZE(sec_auxpcm_rx_port_mixer_controls)), + SND_SOC_DAPM_MIXER("TERT_AUXPCM_RX Port Mixer", + SND_SOC_NOPM, 0, 0, tert_auxpcm_rx_port_mixer_controls, + ARRAY_SIZE(tert_auxpcm_rx_port_mixer_controls)), + SND_SOC_DAPM_MIXER("QUAT_AUXPCM_RX Port Mixer", + SND_SOC_NOPM, 0, 0, quat_auxpcm_rx_port_mixer_controls, + ARRAY_SIZE(quat_auxpcm_rx_port_mixer_controls)), + SND_SOC_DAPM_MIXER("QUIN_AUXPCM_RX Port Mixer", + SND_SOC_NOPM, 0, 0, quin_auxpcm_rx_port_mixer_controls, + ARRAY_SIZE(quin_auxpcm_rx_port_mixer_controls)), + SND_SOC_DAPM_MIXER("SLIMBUS_1_RX Port Mixer", SND_SOC_NOPM, 0, 0, + sbus_1_rx_port_mixer_controls, + ARRAY_SIZE(sbus_1_rx_port_mixer_controls)), + SND_SOC_DAPM_MIXER("INTERNAL_BT_SCO_RX Port Mixer", SND_SOC_NOPM, 0, 0, + bt_sco_rx_port_mixer_controls, + ARRAY_SIZE(bt_sco_rx_port_mixer_controls)), + SND_SOC_DAPM_MIXER("AFE_PCM_RX Port Mixer", + SND_SOC_NOPM, 0, 0, afe_pcm_rx_port_mixer_controls, + ARRAY_SIZE(afe_pcm_rx_port_mixer_controls)), + SND_SOC_DAPM_MIXER("HDMI_RX Port Mixer", + SND_SOC_NOPM, 0, 0, hdmi_rx_port_mixer_controls, + ARRAY_SIZE(hdmi_rx_port_mixer_controls)), + SND_SOC_DAPM_MIXER("DISPLAY_PORT_RX Port Mixer", + SND_SOC_NOPM, 0, 0, display_port_rx_port_mixer_controls, + ARRAY_SIZE(display_port_rx_port_mixer_controls)), + SND_SOC_DAPM_MIXER("DISPLAY_PORT_RX1 Port Mixer", + SND_SOC_NOPM, 0, 0, display_port_rx1_port_mixer_controls, + ARRAY_SIZE(display_port_rx1_port_mixer_controls)), + SND_SOC_DAPM_MIXER("SEC_I2S_RX Port Mixer", + SND_SOC_NOPM, 0, 0, sec_i2s_rx_port_mixer_controls, + ARRAY_SIZE(sec_i2s_rx_port_mixer_controls)), + SND_SOC_DAPM_MIXER("SLIMBUS_3_RX Port Mixer", + SND_SOC_NOPM, 0, 0, sbus_3_rx_port_mixer_controls, + ARRAY_SIZE(sbus_3_rx_port_mixer_controls)), + SND_SOC_DAPM_MIXER("SLIMBUS_6_RX Port Mixer", + SND_SOC_NOPM, 0, 0, sbus_6_rx_port_mixer_controls, + ARRAY_SIZE(sbus_6_rx_port_mixer_controls)), + SND_SOC_DAPM_MIXER("MI2S_RX Port Mixer", SND_SOC_NOPM, 0, 0, + mi2s_rx_port_mixer_controls, ARRAY_SIZE(mi2s_rx_port_mixer_controls)), + SND_SOC_DAPM_MIXER("PRI_MI2S_RX Port Mixer", SND_SOC_NOPM, 0, 0, + primary_mi2s_rx_port_mixer_controls, + ARRAY_SIZE(primary_mi2s_rx_port_mixer_controls)), + SND_SOC_DAPM_MIXER("SEC_MI2S_RX Port Mixer", SND_SOC_NOPM, 0, 0, + sec_mi2s_rx_port_mixer_controls, + ARRAY_SIZE(sec_mi2s_rx_port_mixer_controls)), + SND_SOC_DAPM_MIXER("TERT_MI2S_RX Port Mixer", SND_SOC_NOPM, 0, 0, + tert_mi2s_rx_port_mixer_controls, + ARRAY_SIZE(tert_mi2s_rx_port_mixer_controls)), + SND_SOC_DAPM_MIXER("QUAT_MI2S_RX Port Mixer", SND_SOC_NOPM, 0, 0, + quat_mi2s_rx_port_mixer_controls, + ARRAY_SIZE(quat_mi2s_rx_port_mixer_controls)), + SND_SOC_DAPM_MIXER("QUIN_MI2S_RX Port Mixer", SND_SOC_NOPM, 0, 0, + quin_mi2s_rx_port_mixer_controls, + ARRAY_SIZE(quin_mi2s_rx_port_mixer_controls)), + SND_SOC_DAPM_MIXER("PRI_TDM_RX_0 Port Mixer", SND_SOC_NOPM, 0, 0, + pri_tdm_rx_0_port_mixer_controls, + ARRAY_SIZE(pri_tdm_rx_0_port_mixer_controls)), + SND_SOC_DAPM_MIXER("PRI_TDM_RX_1 Port Mixer", SND_SOC_NOPM, 0, 0, + pri_tdm_rx_1_port_mixer_controls, + ARRAY_SIZE(pri_tdm_rx_1_port_mixer_controls)), + SND_SOC_DAPM_MIXER("PRI_TDM_RX_2 Port Mixer", SND_SOC_NOPM, 0, 0, + pri_tdm_rx_2_port_mixer_controls, + ARRAY_SIZE(pri_tdm_rx_2_port_mixer_controls)), + SND_SOC_DAPM_MIXER("PRI_TDM_RX_3 Port Mixer", SND_SOC_NOPM, 0, 0, + pri_tdm_rx_3_port_mixer_controls, + ARRAY_SIZE(pri_tdm_rx_3_port_mixer_controls)), + SND_SOC_DAPM_MIXER("SEC_TDM_RX_0 Port Mixer", SND_SOC_NOPM, 0, 0, + sec_tdm_rx_0_port_mixer_controls, + ARRAY_SIZE(sec_tdm_rx_0_port_mixer_controls)), + SND_SOC_DAPM_MIXER("SEC_TDM_RX_1 Port Mixer", SND_SOC_NOPM, 0, 0, + sec_tdm_rx_1_port_mixer_controls, + ARRAY_SIZE(sec_tdm_rx_1_port_mixer_controls)), + SND_SOC_DAPM_MIXER("SEC_TDM_RX_2 Port Mixer", SND_SOC_NOPM, 0, 0, + sec_tdm_rx_2_port_mixer_controls, + ARRAY_SIZE(sec_tdm_rx_2_port_mixer_controls)), + SND_SOC_DAPM_MIXER("SEC_TDM_RX_3 Port Mixer", SND_SOC_NOPM, 0, 0, + sec_tdm_rx_3_port_mixer_controls, + ARRAY_SIZE(sec_tdm_rx_3_port_mixer_controls)), + SND_SOC_DAPM_MIXER("SEC_TDM_RX_7 Port Mixer", SND_SOC_NOPM, 0, 0, + sec_tdm_rx_7_port_mixer_controls, + ARRAY_SIZE(sec_tdm_rx_7_port_mixer_controls)), + SND_SOC_DAPM_MIXER("TERT_TDM_RX_0 Port Mixer", SND_SOC_NOPM, 0, 0, + tert_tdm_rx_0_port_mixer_controls, + ARRAY_SIZE(tert_tdm_rx_0_port_mixer_controls)), + SND_SOC_DAPM_MIXER("TERT_TDM_RX_1 Port Mixer", SND_SOC_NOPM, 0, 0, + tert_tdm_rx_1_port_mixer_controls, + ARRAY_SIZE(tert_tdm_rx_1_port_mixer_controls)), + SND_SOC_DAPM_MIXER("TERT_TDM_RX_2 Port Mixer", SND_SOC_NOPM, 0, 0, + tert_tdm_rx_2_port_mixer_controls, + ARRAY_SIZE(tert_tdm_rx_2_port_mixer_controls)), + SND_SOC_DAPM_MIXER("TERT_TDM_RX_3 Port Mixer", SND_SOC_NOPM, 0, 0, + tert_tdm_rx_3_port_mixer_controls, + ARRAY_SIZE(tert_tdm_rx_3_port_mixer_controls)), + SND_SOC_DAPM_MIXER("QUAT_TDM_RX_0 Port Mixer", SND_SOC_NOPM, 0, 0, + quat_tdm_rx_0_port_mixer_controls, + ARRAY_SIZE(quat_tdm_rx_0_port_mixer_controls)), + SND_SOC_DAPM_MIXER("QUAT_TDM_RX_1 Port Mixer", SND_SOC_NOPM, 0, 0, + quat_tdm_rx_1_port_mixer_controls, + ARRAY_SIZE(quat_tdm_rx_1_port_mixer_controls)), + SND_SOC_DAPM_MIXER("QUAT_TDM_RX_2 Port Mixer", SND_SOC_NOPM, 0, 0, + quat_tdm_rx_2_port_mixer_controls, + ARRAY_SIZE(quat_tdm_rx_2_port_mixer_controls)), + SND_SOC_DAPM_MIXER("QUAT_TDM_RX_3 Port Mixer", SND_SOC_NOPM, 0, 0, + quat_tdm_rx_3_port_mixer_controls, + ARRAY_SIZE(quat_tdm_rx_3_port_mixer_controls)), + SND_SOC_DAPM_MIXER("QUIN_TDM_RX_0 Port Mixer", SND_SOC_NOPM, 0, 0, + quin_tdm_rx_0_port_mixer_controls, + ARRAY_SIZE(quin_tdm_rx_0_port_mixer_controls)), + SND_SOC_DAPM_MIXER("QUIN_TDM_RX_1 Port Mixer", SND_SOC_NOPM, 0, 0, + quin_tdm_rx_1_port_mixer_controls, + ARRAY_SIZE(quin_tdm_rx_1_port_mixer_controls)), + SND_SOC_DAPM_MIXER("QUIN_TDM_RX_2 Port Mixer", SND_SOC_NOPM, 0, 0, + quin_tdm_rx_2_port_mixer_controls, + ARRAY_SIZE(quin_tdm_rx_2_port_mixer_controls)), + SND_SOC_DAPM_MIXER("QUIN_TDM_RX_3 Port Mixer", SND_SOC_NOPM, 0, 0, + quin_tdm_rx_3_port_mixer_controls, + ARRAY_SIZE(quin_tdm_rx_3_port_mixer_controls)), + SND_SOC_DAPM_MIXER("INT0_MI2S_RX Port Mixer", SND_SOC_NOPM, 0, 0, + int0_mi2s_rx_port_mixer_controls, + ARRAY_SIZE(int0_mi2s_rx_port_mixer_controls)), + SND_SOC_DAPM_MIXER("INT4_MI2S_RX Port Mixer", SND_SOC_NOPM, 0, 0, + int4_mi2s_rx_port_mixer_controls, + ARRAY_SIZE(int4_mi2s_rx_port_mixer_controls)), + SND_SOC_DAPM_MIXER("WSA_CDC_DMA_RX_0 Port Mixer", SND_SOC_NOPM, 0, 0, + wsa_cdc_dma_rx_0_port_mixer_controls, + ARRAY_SIZE(wsa_cdc_dma_rx_0_port_mixer_controls)), + SND_SOC_DAPM_MIXER("RX_CDC_DMA_RX_0 Port Mixer", SND_SOC_NOPM, 0, 0, + rx_cdc_dma_rx_0_port_mixer_controls, + ARRAY_SIZE(rx_cdc_dma_rx_0_port_mixer_controls)), + SND_SOC_DAPM_MIXER("QCHAT_Tx Mixer", + SND_SOC_NOPM, 0, 0, tx_qchat_mixer_controls, + ARRAY_SIZE(tx_qchat_mixer_controls)), + SND_SOC_DAPM_MIXER("USB_AUDIO_RX_Voice Mixer", + SND_SOC_NOPM, 0, 0, usb_audio_rx_voice_mixer_controls, + ARRAY_SIZE(usb_audio_rx_voice_mixer_controls)), + SND_SOC_DAPM_MIXER("USB_AUDIO_RX Port Mixer", + SND_SOC_NOPM, 0, 0, usb_rx_port_mixer_controls, + ARRAY_SIZE(usb_rx_port_mixer_controls)), + SND_SOC_DAPM_MIXER("DISPLAY_PORT_RX_Voice Mixer", + SND_SOC_NOPM, 0, 0, display_port_rx_voice_mixer_controls, + ARRAY_SIZE(display_port_rx_voice_mixer_controls)), + SND_SOC_DAPM_MIXER("DISPLAY_PORT_RX1_Voice Mixer", + SND_SOC_NOPM, 0, 0, display_port_rx1_voice_mixer_controls, + ARRAY_SIZE(display_port_rx1_voice_mixer_controls)), + /* lsm mixer definitions */ + SND_SOC_DAPM_MIXER("LSM1 Mixer", SND_SOC_NOPM, 0, 0, + lsm1_mixer_controls, ARRAY_SIZE(lsm1_mixer_controls)), + SND_SOC_DAPM_MIXER("LSM2 Mixer", SND_SOC_NOPM, 0, 0, + lsm2_mixer_controls, ARRAY_SIZE(lsm2_mixer_controls)), + SND_SOC_DAPM_MIXER("LSM3 Mixer", SND_SOC_NOPM, 0, 0, + lsm3_mixer_controls, ARRAY_SIZE(lsm3_mixer_controls)), + SND_SOC_DAPM_MIXER("LSM4 Mixer", SND_SOC_NOPM, 0, 0, + lsm4_mixer_controls, ARRAY_SIZE(lsm4_mixer_controls)), + SND_SOC_DAPM_MIXER("LSM5 Mixer", SND_SOC_NOPM, 0, 0, + lsm5_mixer_controls, ARRAY_SIZE(lsm5_mixer_controls)), + SND_SOC_DAPM_MIXER("LSM6 Mixer", SND_SOC_NOPM, 0, 0, + lsm6_mixer_controls, ARRAY_SIZE(lsm6_mixer_controls)), + SND_SOC_DAPM_MIXER("LSM7 Mixer", SND_SOC_NOPM, 0, 0, + lsm7_mixer_controls, ARRAY_SIZE(lsm7_mixer_controls)), + SND_SOC_DAPM_MIXER("LSM8 Mixer", SND_SOC_NOPM, 0, 0, + lsm8_mixer_controls, ARRAY_SIZE(lsm8_mixer_controls)), + /* Virtual Pins to force backends ON atm */ + SND_SOC_DAPM_OUTPUT("BE_OUT"), + SND_SOC_DAPM_INPUT("BE_IN"), + + SND_SOC_DAPM_MUX("SLIM0_RX_VI_FB_LCH_MUX", SND_SOC_NOPM, 0, 0, + &slim0_rx_vi_fb_lch_mux), + SND_SOC_DAPM_MUX("SLIM0_RX_VI_FB_RCH_MUX", SND_SOC_NOPM, 0, 0, + &slim0_rx_vi_fb_rch_mux), + SND_SOC_DAPM_MUX("WSA_RX_0_VI_FB_LCH_MUX", SND_SOC_NOPM, 0, 0, + &wsa_rx_0_vi_fb_lch_mux), + SND_SOC_DAPM_MUX("WSA_RX_0_VI_FB_RCH_MUX", SND_SOC_NOPM, 0, 0, + &wsa_rx_0_vi_fb_rch_mux), + SND_SOC_DAPM_MUX("PRI_MI2S_RX_VI_FB_MUX", SND_SOC_NOPM, 0, 0, + &mi2s_rx_vi_fb_mux), + SND_SOC_DAPM_MUX("INT4_MI2S_RX_VI_FB_MONO_CH_MUX", SND_SOC_NOPM, 0, 0, + &int4_mi2s_rx_vi_fb_mono_ch_mux), + SND_SOC_DAPM_MUX("INT4_MI2S_RX_VI_FB_STEREO_CH_MUX", SND_SOC_NOPM, 0, 0, + &int4_mi2s_rx_vi_fb_stereo_ch_mux), + + SND_SOC_DAPM_MUX("VOC_EXT_EC MUX", SND_SOC_NOPM, 0, 0, + &voc_ext_ec_mux), + SND_SOC_DAPM_MUX("AUDIO_REF_EC_UL1 MUX", SND_SOC_NOPM, 0, 0, + &ext_ec_ref_mux_ul1), + SND_SOC_DAPM_MUX("AUDIO_REF_EC_UL2 MUX", SND_SOC_NOPM, 0, 0, + &ext_ec_ref_mux_ul2), + SND_SOC_DAPM_MUX("AUDIO_REF_EC_UL3 MUX", SND_SOC_NOPM, 0, 0, + &ext_ec_ref_mux_ul3), + SND_SOC_DAPM_MUX("AUDIO_REF_EC_UL4 MUX", SND_SOC_NOPM, 0, 0, + &ext_ec_ref_mux_ul4), + SND_SOC_DAPM_MUX("AUDIO_REF_EC_UL5 MUX", SND_SOC_NOPM, 0, 0, + &ext_ec_ref_mux_ul5), + SND_SOC_DAPM_MUX("AUDIO_REF_EC_UL6 MUX", SND_SOC_NOPM, 0, 0, + &ext_ec_ref_mux_ul6), + SND_SOC_DAPM_MUX("AUDIO_REF_EC_UL8 MUX", SND_SOC_NOPM, 0, 0, + &ext_ec_ref_mux_ul8), + SND_SOC_DAPM_MUX("AUDIO_REF_EC_UL9 MUX", SND_SOC_NOPM, 0, 0, + &ext_ec_ref_mux_ul9), + SND_SOC_DAPM_MUX("AUDIO_REF_EC_UL10 MUX", SND_SOC_NOPM, 0, 0, + &ext_ec_ref_mux_ul10), + SND_SOC_DAPM_MUX("AUDIO_REF_EC_UL16 MUX", SND_SOC_NOPM, 0, 0, + &ext_ec_ref_mux_ul16), + SND_SOC_DAPM_MUX("AUDIO_REF_EC_UL17 MUX", SND_SOC_NOPM, 0, 0, + &ext_ec_ref_mux_ul17), + SND_SOC_DAPM_MUX("AUDIO_REF_EC_UL18 MUX", SND_SOC_NOPM, 0, 0, + &ext_ec_ref_mux_ul18), + SND_SOC_DAPM_MUX("AUDIO_REF_EC_UL19 MUX", SND_SOC_NOPM, 0, 0, + &ext_ec_ref_mux_ul19), + SND_SOC_DAPM_MUX("AUDIO_REF_EC_UL28 MUX", SND_SOC_NOPM, 0, 0, + &ext_ec_ref_mux_ul28), + SND_SOC_DAPM_MUX("AUDIO_REF_EC_UL29 MUX", SND_SOC_NOPM, 0, 0, + &ext_ec_ref_mux_ul29), +}; + +static const struct snd_soc_dapm_route intercon[] = { + {"PRI_RX Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"PRI_RX Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"PRI_RX Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"PRI_RX Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"PRI_RX Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"PRI_RX Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"PRI_RX Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"PRI_RX Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"PRI_RX Audio Mixer", "MultiMedia9", "MM_DL9"}, + {"PRI_RX Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"PRI_RX Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"PRI_RX Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"PRI_RX Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"PRI_RX Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"PRI_RX Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"PRI_RX Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"PRI_RX Audio Mixer", "MultiMedia26", "MM_DL26"}, + {"PRI_I2S_RX", NULL, "PRI_RX Audio Mixer"}, + + {"SEC_RX Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"SEC_RX Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"SEC_RX Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"SEC_RX Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"SEC_RX Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"SEC_RX Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"SEC_RX Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"SEC_RX Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"SEC_RX Audio Mixer", "MultiMedia9", "MM_DL9"}, + {"SEC_RX Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"SEC_RX Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"SEC_RX Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"SEC_RX Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"SEC_RX Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"SEC_RX Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"SEC_RX Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"SEC_RX Audio Mixer", "MultiMedia26", "MM_DL26"}, + {"SEC_I2S_RX", NULL, "SEC_RX Audio Mixer"}, + + {"SLIMBUS_0_RX Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"SLIMBUS_0_RX Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"SLIMBUS_0_RX Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"SLIMBUS_0_RX Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"SLIMBUS_0_RX Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"SLIMBUS_0_RX Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"SLIMBUS_0_RX Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"SLIMBUS_0_RX Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"SLIMBUS_0_RX Audio Mixer", "MultiMedia9", "MM_DL9"}, + {"SLIMBUS_0_RX Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"SLIMBUS_0_RX Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"SLIMBUS_0_RX Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"SLIMBUS_0_RX Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"SLIMBUS_0_RX Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"SLIMBUS_0_RX Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"SLIMBUS_0_RX Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"SLIMBUS_0_RX Audio Mixer", "MultiMedia26", "MM_DL26"}, + {"SLIMBUS_0_RX", NULL, "SLIMBUS_0_RX Audio Mixer"}, + + {"SLIMBUS_2_RX Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"SLIMBUS_2_RX Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"SLIMBUS_2_RX Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"SLIMBUS_2_RX Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"SLIMBUS_2_RX Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"SLIMBUS_2_RX Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"SLIMBUS_2_RX Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"SLIMBUS_2_RX Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"SLIMBUS_2_RX Audio Mixer", "MultiMedia9", "MM_DL9"}, + {"SLIMBUS_2_RX Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"SLIMBUS_2_RX Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"SLIMBUS_2_RX Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"SLIMBUS_2_RX Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"SLIMBUS_2_RX Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"SLIMBUS_2_RX Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"SLIMBUS_2_RX Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"SLIMBUS_2_RX Audio Mixer", "MultiMedia26", "MM_DL26"}, + {"SLIMBUS_2_RX", NULL, "SLIMBUS_2_RX Audio Mixer"}, + + {"SLIMBUS_5_RX Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"SLIMBUS_5_RX Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"SLIMBUS_5_RX Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"SLIMBUS_5_RX Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"SLIMBUS_5_RX Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"SLIMBUS_5_RX Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"SLIMBUS_5_RX Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"SLIMBUS_5_RX Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"SLIMBUS_5_RX Audio Mixer", "MultiMedia9", "MM_DL9"}, + {"SLIMBUS_5_RX Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"SLIMBUS_5_RX Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"SLIMBUS_5_RX Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"SLIMBUS_5_RX Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"SLIMBUS_5_RX Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"SLIMBUS_5_RX Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"SLIMBUS_5_RX Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"SLIMBUS_5_RX Audio Mixer", "MultiMedia26", "MM_DL26"}, + {"SLIMBUS_5_RX", NULL, "SLIMBUS_5_RX Audio Mixer"}, + + {"WSA_CDC_DMA_RX_0 Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"WSA_CDC_DMA_RX_0 Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"WSA_CDC_DMA_RX_0 Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"WSA_CDC_DMA_RX_0 Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"WSA_CDC_DMA_RX_0 Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"WSA_CDC_DMA_RX_0 Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"WSA_CDC_DMA_RX_0 Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"WSA_CDC_DMA_RX_0 Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"WSA_CDC_DMA_RX_0 Audio Mixer", "MultiMedia9", "MM_DL9"}, + {"WSA_CDC_DMA_RX_0 Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"WSA_CDC_DMA_RX_0 Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"WSA_CDC_DMA_RX_0 Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"WSA_CDC_DMA_RX_0 Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"WSA_CDC_DMA_RX_0 Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"WSA_CDC_DMA_RX_0 Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"WSA_CDC_DMA_RX_0 Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"WSA_CDC_DMA_RX_0 Audio Mixer", "MultiMedia26", "MM_DL26"}, + {"WSA_CDC_DMA_RX_0", NULL, "WSA_CDC_DMA_RX_0 Audio Mixer"}, + + {"WSA_CDC_DMA_RX_1 Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"WSA_CDC_DMA_RX_1 Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"WSA_CDC_DMA_RX_1 Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"WSA_CDC_DMA_RX_1 Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"WSA_CDC_DMA_RX_1 Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"WSA_CDC_DMA_RX_1 Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"WSA_CDC_DMA_RX_1 Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"WSA_CDC_DMA_RX_1 Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"WSA_CDC_DMA_RX_1 Audio Mixer", "MultiMedia9", "MM_DL9"}, + {"WSA_CDC_DMA_RX_1 Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"WSA_CDC_DMA_RX_1 Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"WSA_CDC_DMA_RX_1 Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"WSA_CDC_DMA_RX_1 Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"WSA_CDC_DMA_RX_1 Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"WSA_CDC_DMA_RX_1 Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"WSA_CDC_DMA_RX_1 Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"WSA_CDC_DMA_RX_1 Audio Mixer", "MultiMedia26", "MM_DL26"}, + {"WSA_CDC_DMA_RX_1", NULL, "WSA_CDC_DMA_RX_1 Audio Mixer"}, + + {"RX_CDC_DMA_RX_0 Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"RX_CDC_DMA_RX_0 Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"RX_CDC_DMA_RX_0 Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"RX_CDC_DMA_RX_0 Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"RX_CDC_DMA_RX_0 Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"RX_CDC_DMA_RX_0 Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"RX_CDC_DMA_RX_0 Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"RX_CDC_DMA_RX_0 Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"RX_CDC_DMA_RX_0 Audio Mixer", "MultiMedia9", "MM_DL9"}, + {"RX_CDC_DMA_RX_0 Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"RX_CDC_DMA_RX_0 Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"RX_CDC_DMA_RX_0 Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"RX_CDC_DMA_RX_0 Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"RX_CDC_DMA_RX_0 Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"RX_CDC_DMA_RX_0 Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"RX_CDC_DMA_RX_0 Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"RX_CDC_DMA_RX_0 Audio Mixer", "MultiMedia26", "MM_DL26"}, + {"RX_CDC_DMA_RX_0", NULL, "RX_CDC_DMA_RX_0 Audio Mixer"}, + + {"RX_CDC_DMA_RX_1 Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"RX_CDC_DMA_RX_1 Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"RX_CDC_DMA_RX_1 Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"RX_CDC_DMA_RX_1 Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"RX_CDC_DMA_RX_1 Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"RX_CDC_DMA_RX_1 Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"RX_CDC_DMA_RX_1 Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"RX_CDC_DMA_RX_1 Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"RX_CDC_DMA_RX_1 Audio Mixer", "MultiMedia9", "MM_DL9"}, + {"RX_CDC_DMA_RX_1 Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"RX_CDC_DMA_RX_1 Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"RX_CDC_DMA_RX_1 Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"RX_CDC_DMA_RX_1 Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"RX_CDC_DMA_RX_1 Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"RX_CDC_DMA_RX_1 Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"RX_CDC_DMA_RX_1 Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"RX_CDC_DMA_RX_1 Audio Mixer", "MultiMedia26", "MM_DL26"}, + {"RX_CDC_DMA_RX_1", NULL, "RX_CDC_DMA_RX_1 Audio Mixer"}, + + {"RX_CDC_DMA_RX_2 Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"RX_CDC_DMA_RX_2 Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"RX_CDC_DMA_RX_2 Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"RX_CDC_DMA_RX_2 Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"RX_CDC_DMA_RX_2 Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"RX_CDC_DMA_RX_2 Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"RX_CDC_DMA_RX_2 Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"RX_CDC_DMA_RX_2 Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"RX_CDC_DMA_RX_2 Audio Mixer", "MultiMedia9", "MM_DL9"}, + {"RX_CDC_DMA_RX_2 Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"RX_CDC_DMA_RX_2 Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"RX_CDC_DMA_RX_2 Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"RX_CDC_DMA_RX_2 Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"RX_CDC_DMA_RX_2 Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"RX_CDC_DMA_RX_2 Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"RX_CDC_DMA_RX_2 Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"RX_CDC_DMA_RX_2 Audio Mixer", "MultiMedia26", "MM_DL26"}, + {"RX_CDC_DMA_RX_2", NULL, "RX_CDC_DMA_RX_2 Audio Mixer"}, + + {"RX_CDC_DMA_RX_3 Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"RX_CDC_DMA_RX_3 Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"RX_CDC_DMA_RX_3 Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"RX_CDC_DMA_RX_3 Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"RX_CDC_DMA_RX_3 Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"RX_CDC_DMA_RX_3 Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"RX_CDC_DMA_RX_3 Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"RX_CDC_DMA_RX_3 Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"RX_CDC_DMA_RX_3 Audio Mixer", "MultiMedia9", "MM_DL9"}, + {"RX_CDC_DMA_RX_3 Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"RX_CDC_DMA_RX_3 Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"RX_CDC_DMA_RX_3 Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"RX_CDC_DMA_RX_3 Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"RX_CDC_DMA_RX_3 Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"RX_CDC_DMA_RX_3 Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"RX_CDC_DMA_RX_3 Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"RX_CDC_DMA_RX_3 Audio Mixer", "MultiMedia26", "MM_DL26"}, + {"RX_CDC_DMA_RX_3", NULL, "RX_CDC_DMA_RX_3 Audio Mixer"}, + + {"RX_CDC_DMA_RX_4 Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"RX_CDC_DMA_RX_4 Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"RX_CDC_DMA_RX_4 Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"RX_CDC_DMA_RX_4 Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"RX_CDC_DMA_RX_4 Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"RX_CDC_DMA_RX_4 Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"RX_CDC_DMA_RX_4 Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"RX_CDC_DMA_RX_4 Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"RX_CDC_DMA_RX_4 Audio Mixer", "MultiMedia9", "MM_DL9"}, + {"RX_CDC_DMA_RX_4 Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"RX_CDC_DMA_RX_4 Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"RX_CDC_DMA_RX_4 Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"RX_CDC_DMA_RX_4 Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"RX_CDC_DMA_RX_4 Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"RX_CDC_DMA_RX_4 Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"RX_CDC_DMA_RX_4 Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"RX_CDC_DMA_RX_4 Audio Mixer", "MultiMedia26", "MM_DL26"}, + {"RX_CDC_DMA_RX_4", NULL, "RX_CDC_DMA_RX_4 Audio Mixer"}, + + {"RX_CDC_DMA_RX_5 Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"RX_CDC_DMA_RX_5 Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"RX_CDC_DMA_RX_5 Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"RX_CDC_DMA_RX_5 Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"RX_CDC_DMA_RX_5 Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"RX_CDC_DMA_RX_5 Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"RX_CDC_DMA_RX_5 Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"RX_CDC_DMA_RX_5 Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"RX_CDC_DMA_RX_5 Audio Mixer", "MultiMedia9", "MM_DL9"}, + {"RX_CDC_DMA_RX_5 Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"RX_CDC_DMA_RX_5 Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"RX_CDC_DMA_RX_5 Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"RX_CDC_DMA_RX_5 Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"RX_CDC_DMA_RX_5 Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"RX_CDC_DMA_RX_5 Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"RX_CDC_DMA_RX_5 Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"RX_CDC_DMA_RX_5 Audio Mixer", "MultiMedia26", "MM_DL26"}, + {"RX_CDC_DMA_RX_5", NULL, "RX_CDC_DMA_RX_5 Audio Mixer"}, + + {"RX_CDC_DMA_RX_6 Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"RX_CDC_DMA_RX_6 Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"RX_CDC_DMA_RX_6 Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"RX_CDC_DMA_RX_6 Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"RX_CDC_DMA_RX_6 Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"RX_CDC_DMA_RX_6 Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"RX_CDC_DMA_RX_6 Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"RX_CDC_DMA_RX_6 Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"RX_CDC_DMA_RX_6 Audio Mixer", "MultiMedia9", "MM_DL9"}, + {"RX_CDC_DMA_RX_6 Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"RX_CDC_DMA_RX_6 Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"RX_CDC_DMA_RX_6 Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"RX_CDC_DMA_RX_6 Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"RX_CDC_DMA_RX_6 Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"RX_CDC_DMA_RX_6 Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"RX_CDC_DMA_RX_6 Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"RX_CDC_DMA_RX_6 Audio Mixer", "MultiMedia26", "MM_DL26"}, + {"RX_CDC_DMA_RX_6", NULL, "RX_CDC_DMA_RX_6 Audio Mixer"}, + + {"RX_CDC_DMA_RX_7 Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"RX_CDC_DMA_RX_7 Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"RX_CDC_DMA_RX_7 Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"RX_CDC_DMA_RX_7 Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"RX_CDC_DMA_RX_7 Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"RX_CDC_DMA_RX_7 Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"RX_CDC_DMA_RX_7 Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"RX_CDC_DMA_RX_7 Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"RX_CDC_DMA_RX_7 Audio Mixer", "MultiMedia9", "MM_DL9"}, + {"RX_CDC_DMA_RX_7 Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"RX_CDC_DMA_RX_7 Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"RX_CDC_DMA_RX_7 Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"RX_CDC_DMA_RX_7 Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"RX_CDC_DMA_RX_7 Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"RX_CDC_DMA_RX_7 Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"RX_CDC_DMA_RX_7 Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"RX_CDC_DMA_RX_7 Audio Mixer", "MultiMedia26", "MM_DL26"}, + {"RX_CDC_DMA_RX_7", NULL, "RX_CDC_DMA_RX_7 Audio Mixer"}, + + {"HDMI Mixer", "MultiMedia1", "MM_DL1"}, + {"HDMI Mixer", "MultiMedia2", "MM_DL2"}, + {"HDMI Mixer", "MultiMedia3", "MM_DL3"}, + {"HDMI Mixer", "MultiMedia4", "MM_DL4"}, + {"HDMI Mixer", "MultiMedia5", "MM_DL5"}, + {"HDMI Mixer", "MultiMedia6", "MM_DL6"}, + {"HDMI Mixer", "MultiMedia7", "MM_DL7"}, + {"HDMI Mixer", "MultiMedia8", "MM_DL8"}, + {"HDMI Mixer", "MultiMedia9", "MM_DL9"}, + {"HDMI Mixer", "MultiMedia10", "MM_DL10"}, + {"HDMI Mixer", "MultiMedia11", "MM_DL11"}, + {"HDMI Mixer", "MultiMedia12", "MM_DL12"}, + {"HDMI Mixer", "MultiMedia13", "MM_DL13"}, + {"HDMI Mixer", "MultiMedia14", "MM_DL14"}, + {"HDMI Mixer", "MultiMedia15", "MM_DL15"}, + {"HDMI Mixer", "MultiMedia16", "MM_DL16"}, + {"HDMI Mixer", "MultiMedia26", "MM_DL26"}, + {"HDMI", NULL, "HDMI Mixer"}, + + {"DISPLAY_PORT Mixer", "MultiMedia1", "MM_DL1"}, + {"DISPLAY_PORT Mixer", "MultiMedia2", "MM_DL2"}, + {"DISPLAY_PORT Mixer", "MultiMedia3", "MM_DL3"}, + {"DISPLAY_PORT Mixer", "MultiMedia4", "MM_DL4"}, + {"DISPLAY_PORT Mixer", "MultiMedia5", "MM_DL5"}, + {"DISPLAY_PORT Mixer", "MultiMedia6", "MM_DL6"}, + {"DISPLAY_PORT Mixer", "MultiMedia7", "MM_DL7"}, + {"DISPLAY_PORT Mixer", "MultiMedia8", "MM_DL8"}, + {"DISPLAY_PORT Mixer", "MultiMedia9", "MM_DL9"}, + {"DISPLAY_PORT Mixer", "MultiMedia10", "MM_DL10"}, + {"DISPLAY_PORT Mixer", "MultiMedia11", "MM_DL11"}, + {"DISPLAY_PORT Mixer", "MultiMedia12", "MM_DL12"}, + {"DISPLAY_PORT Mixer", "MultiMedia13", "MM_DL13"}, + {"DISPLAY_PORT Mixer", "MultiMedia14", "MM_DL14"}, + {"DISPLAY_PORT Mixer", "MultiMedia15", "MM_DL15"}, + {"DISPLAY_PORT Mixer", "MultiMedia16", "MM_DL16"}, + {"DISPLAY_PORT Mixer", "MultiMedia26", "MM_DL26"}, + {"DISPLAY_PORT", NULL, "DISPLAY_PORT Mixer"}, + + {"DISPLAY_PORT1 Mixer", "MultiMedia1", "MM_DL1"}, + {"DISPLAY_PORT1 Mixer", "MultiMedia2", "MM_DL2"}, + {"DISPLAY_PORT1 Mixer", "MultiMedia3", "MM_DL3"}, + {"DISPLAY_PORT1 Mixer", "MultiMedia4", "MM_DL4"}, + {"DISPLAY_PORT1 Mixer", "MultiMedia5", "MM_DL5"}, + {"DISPLAY_PORT1 Mixer", "MultiMedia6", "MM_DL6"}, + {"DISPLAY_PORT1 Mixer", "MultiMedia7", "MM_DL7"}, + {"DISPLAY_PORT1 Mixer", "MultiMedia8", "MM_DL8"}, + {"DISPLAY_PORT1 Mixer", "MultiMedia9", "MM_DL9"}, + {"DISPLAY_PORT1 Mixer", "MultiMedia10", "MM_DL10"}, + {"DISPLAY_PORT1 Mixer", "MultiMedia11", "MM_DL11"}, + {"DISPLAY_PORT1 Mixer", "MultiMedia12", "MM_DL12"}, + {"DISPLAY_PORT1 Mixer", "MultiMedia13", "MM_DL13"}, + {"DISPLAY_PORT1 Mixer", "MultiMedia14", "MM_DL14"}, + {"DISPLAY_PORT1 Mixer", "MultiMedia15", "MM_DL15"}, + {"DISPLAY_PORT1 Mixer", "MultiMedia16", "MM_DL16"}, + {"DISPLAY_PORT1 Mixer", "MultiMedia26", "MM_DL26"}, + {"DISPLAY_PORT1", NULL, "DISPLAY_PORT1 Mixer"}, + + {"PRI_SPDIF_RX Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"PRI_SPDIF_RX Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"PRI_SPDIF_RX Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"PRI_SPDIF_RX Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"PRI_SPDIF_RX Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"PRI_SPDIF_RX Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"PRI_SPDIF_RX Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"PRI_SPDIF_RX Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"PRI_SPDIF_RX Audio Mixer", "MultiMedia9", "MM_DL9"}, + {"PRI_SPDIF_RX Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"PRI_SPDIF_RX Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"PRI_SPDIF_RX Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"PRI_SPDIF_RX Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"PRI_SPDIF_RX Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"PRI_SPDIF_RX Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"PRI_SPDIF_RX Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"PRI_SPDIF_RX Audio Mixer", "MultiMedia26", "MM_DL26"}, + {"PRI_SPDIF_RX", NULL, "PRI_SPDIF_RX Audio Mixer"}, + + {"SEC_SPDIF_RX Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"SEC_SPDIF_RX Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"SEC_SPDIF_RX Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"SEC_SPDIF_RX Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"SEC_SPDIF_RX Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"SEC_SPDIF_RX Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"SEC_SPDIF_RX Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"SEC_SPDIF_RX Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"SEC_SPDIF_RX Audio Mixer", "MultiMedia9", "MM_DL9"}, + {"SEC_SPDIF_RX Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"SEC_SPDIF_RX Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"SEC_SPDIF_RX Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"SEC_SPDIF_RX Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"SEC_SPDIF_RX Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"SEC_SPDIF_RX Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"SEC_SPDIF_RX Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"SEC_SPDIF_RX Audio Mixer", "MultiMedia26", "MM_DL26"}, + {"SEC_SPDIF_RX", NULL, "SEC_SPDIF_RX Audio Mixer"}, + + /* incall */ + {"Incall_Music Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"Incall_Music Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"Incall_Music Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"Incall_Music Audio Mixer", "MultiMedia9", "MM_DL9"}, + {"VOICE_PLAYBACK_TX", NULL, "Incall_Music Audio Mixer"}, + {"Incall_Music_2 Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"Incall_Music_2 Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"Incall_Music_2 Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"Incall_Music_2 Audio Mixer", "MultiMedia9", "MM_DL9"}, + {"VOICE2_PLAYBACK_TX", NULL, "Incall_Music_2 Audio Mixer"}, + {"SLIMBUS_4_RX Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"SLIMBUS_4_RX Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"SLIMBUS_4_RX Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"SLIMBUS_4_RX Audio Mixer", "MultiMedia9", "MM_DL9"}, + {"SLIMBUS_4_RX", NULL, "SLIMBUS_4_RX Audio Mixer"}, + + {"SLIMBUS_6_RX Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"SLIMBUS_6_RX Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"SLIMBUS_6_RX Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"SLIMBUS_6_RX Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"SLIMBUS_6_RX Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"SLIMBUS_6_RX Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"SLIMBUS_6_RX Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"SLIMBUS_6_RX Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"SLIMBUS_6_RX Audio Mixer", "MultiMedia9", "MM_DL9"}, + {"SLIMBUS_6_RX Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"SLIMBUS_6_RX Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"SLIMBUS_6_RX Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"SLIMBUS_6_RX Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"SLIMBUS_6_RX Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"SLIMBUS_6_RX Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"SLIMBUS_6_RX Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"SLIMBUS_6_RX Audio Mixer", "MultiMedia26", "MM_DL26"}, + {"SLIMBUS_6_RX", NULL, "SLIMBUS_6_RX Audio Mixer"}, + + {"SLIMBUS_7_RX Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"SLIMBUS_7_RX Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"SLIMBUS_7_RX Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"SLIMBUS_7_RX Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"SLIMBUS_7_RX Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"SLIMBUS_7_RX Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"SLIMBUS_7_RX Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"SLIMBUS_7_RX Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"SLIMBUS_7_RX Audio Mixer", "MultiMedia9", "MM_DL9"}, + {"SLIMBUS_7_RX Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"SLIMBUS_7_RX Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"SLIMBUS_7_RX Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"SLIMBUS_7_RX Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"SLIMBUS_7_RX Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"SLIMBUS_7_RX Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"SLIMBUS_7_RX Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"SLIMBUS_7_RX Audio Mixer", "MultiMedia26", "MM_DL26"}, + {"SLIMBUS_7_RX", NULL, "SLIMBUS_7_RX Audio Mixer"}, + + {"SLIMBUS_9_RX Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"SLIMBUS_9_RX Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"SLIMBUS_9_RX Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"SLIMBUS_9_RX Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"SLIMBUS_9_RX Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"SLIMBUS_9_RX Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"SLIMBUS_9_RX Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"SLIMBUS_9_RX Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"SLIMBUS_9_RX Audio Mixer", "MultiMedia9", "MM_DL9"}, + {"SLIMBUS_9_RX Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"SLIMBUS_9_RX Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"SLIMBUS_9_RX Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"SLIMBUS_9_RX Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"SLIMBUS_9_RX Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"SLIMBUS_9_RX Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"SLIMBUS_9_RX Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"SLIMBUS_9_RX Audio Mixer", "MultiMedia26", "MM_DL26"}, + {"SLIMBUS_9_RX", NULL, "SLIMBUS_9_RX Audio Mixer"}, + + {"USB_AUDIO_RX Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"USB_AUDIO_RX Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"USB_AUDIO_RX Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"USB_AUDIO_RX Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"USB_AUDIO_RX Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"USB_AUDIO_RX Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"USB_AUDIO_RX Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"USB_AUDIO_RX Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"USB_AUDIO_RX Audio Mixer", "MultiMedia9", "MM_DL9"}, + {"USB_AUDIO_RX Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"USB_AUDIO_RX Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"USB_AUDIO_RX Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"USB_AUDIO_RX Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"USB_AUDIO_RX Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"USB_AUDIO_RX Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"USB_AUDIO_RX Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"USB_AUDIO_RX Audio Mixer", "MultiMedia26", "MM_DL26"}, + {"USB_AUDIO_RX", NULL, "USB_AUDIO_RX Audio Mixer"}, + + {"MultiMedia1 Mixer", "VOC_REC_UL", "INCALL_RECORD_TX"}, + {"MultiMedia4 Mixer", "VOC_REC_UL", "INCALL_RECORD_TX"}, + {"MultiMedia8 Mixer", "VOC_REC_UL", "INCALL_RECORD_TX"}, + {"MultiMedia1 Mixer", "VOC_REC_DL", "INCALL_RECORD_RX"}, + {"MultiMedia4 Mixer", "VOC_REC_DL", "INCALL_RECORD_RX"}, + {"MultiMedia8 Mixer", "VOC_REC_DL", "INCALL_RECORD_RX"}, + {"MultiMedia1 Mixer", "SLIM_4_TX", "SLIMBUS_4_TX"}, + {"MultiMedia1 Mixer", "SLIM_6_TX", "SLIMBUS_6_TX"}, + {"MultiMedia1 Mixer", "SLIM_7_TX", "SLIMBUS_7_TX"}, + {"MultiMedia1 Mixer", "SLIM_8_TX", "SLIMBUS_8_TX"}, + {"MultiMedia1 Mixer", "SLIM_9_TX", "SLIMBUS_9_TX"}, + {"MultiMedia8 Mixer", "SLIM_6_TX", "SLIMBUS_6_TX"}, + {"MultiMedia8 Mixer", "SLIM_7_TX", "SLIMBUS_7_TX"}, + {"MultiMedia8 Mixer", "SLIM_9_TX", "SLIMBUS_9_TX"}, + {"MultiMedia4 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"}, + {"MultiMedia17 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"}, + {"MultiMedia18 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"}, + {"MultiMedia19 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"}, + {"MultiMedia28 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"}, + {"MultiMedia29 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"}, + {"MultiMedia8 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"}, + {"MultiMedia2 Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"MultiMedia4 Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"MultiMedia17 Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"MultiMedia18 Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"MultiMedia19 Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"MultiMedia28 Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"MultiMedia29 Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"MultiMedia8 Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"MultiMedia18 Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"}, + {"MultiMedia17 Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"}, + {"MultiMedia18 Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"}, + {"MultiMedia19 Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"}, + {"MultiMedia28 Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"}, + {"MultiMedia29 Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"}, + {"MultiMedia17 Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + {"MultiMedia18 Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + {"MultiMedia19 Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + {"MultiMedia28 Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + {"MultiMedia29 Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + {"MultiMedia8 Mixer", "INT3_MI2S_TX", "INT3_MI2S_TX"}, + {"MultiMedia3 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"}, + {"MultiMedia5 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"}, + {"MultiMedia10 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"}, + {"MultiMedia16 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"}, + {"MultiMedia5 Mixer", "SLIM_7_TX", "SLIMBUS_7_TX"}, + {"MultiMedia5 Mixer", "SLIM_8_TX", "SLIMBUS_8_TX"}, + {"MultiMedia5 Mixer", "SLIM_9_TX", "SLIMBUS_9_TX"}, + {"MultiMedia10 Mixer", "SLIM_7_TX", "SLIMBUS_7_TX"}, + {"MultiMedia10 Mixer", "SLIM_9_TX", "SLIMBUS_9_TX"}, + {"MultiMedia18 Mixer", "PRI_SPDIF_TX", "PRI_SPDIF_TX"}, + {"MultiMedia18 Mixer", "SEC_SPDIF_TX", "SEC_SPDIF_TX"}, + + {"MI2S_RX Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"MI2S_RX Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"MI2S_RX Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"MI2S_RX Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"MI2S_RX Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"MI2S_RX Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"MI2S_RX Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"MI2S_RX Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"MI2S_RX Audio Mixer", "MultiMedia9", "MM_DL9"}, + {"MI2S_RX Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"MI2S_RX Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"MI2S_RX Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"MI2S_RX Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"MI2S_RX Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"MI2S_RX Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"MI2S_RX Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"MI2S_RX Audio Mixer", "MultiMedia26", "MM_DL26"}, + {"MI2S_RX", NULL, "MI2S_RX Audio Mixer"}, + + {"QUAT_MI2S_RX Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"QUAT_MI2S_RX Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"QUAT_MI2S_RX Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"QUAT_MI2S_RX Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"QUAT_MI2S_RX Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"QUAT_MI2S_RX Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"QUAT_MI2S_RX Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"QUAT_MI2S_RX Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"QUAT_MI2S_RX Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"QUAT_MI2S_RX Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"QUAT_MI2S_RX Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"QUAT_MI2S_RX Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"QUAT_MI2S_RX Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"QUAT_MI2S_RX Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"QUAT_MI2S_RX Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"QUAT_MI2S_RX Audio Mixer", "MultiMedia26", "MM_DL26"}, + {"QUAT_MI2S_RX", NULL, "QUAT_MI2S_RX Audio Mixer"}, + + {"TERT_MI2S_RX Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"TERT_MI2S_RX Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"TERT_MI2S_RX Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"TERT_MI2S_RX Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"TERT_MI2S_RX Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"TERT_MI2S_RX Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"TERT_MI2S_RX Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"TERT_MI2S_RX Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"TERT_MI2S_RX Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"TERT_MI2S_RX Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"TERT_MI2S_RX Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"TERT_MI2S_RX Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"TERT_MI2S_RX Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"TERT_MI2S_RX Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"TERT_MI2S_RX Audio Mixer", "MultiMedia26", "MM_DL26"}, + {"TERT_MI2S_RX", NULL, "TERT_MI2S_RX Audio Mixer"}, + + {"SEC_MI2S_RX Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"SEC_MI2S_RX Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"SEC_MI2S_RX Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"SEC_MI2S_RX Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"SEC_MI2S_RX Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"SEC_MI2S_RX Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"SEC_MI2S_RX Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"SEC_MI2S_RX Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"SEC_MI2S_RX Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"SEC_MI2S_RX Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"SEC_MI2S_RX Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"SEC_MI2S_RX Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"SEC_MI2S_RX Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"SEC_MI2S_RX Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"SEC_MI2S_RX Audio Mixer", "MultiMedia26", "MM_DL26"}, + {"SEC_MI2S_RX", NULL, "SEC_MI2S_RX Audio Mixer"}, + + {"SEC_MI2S_RX_SD1 Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"SEC_MI2S_RX_SD1", NULL, "SEC_MI2S_RX_SD1 Audio Mixer"}, + + {"SEC_MI2S_RX Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"SEC_MI2S_RX Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"}, + + {"PRI_MI2S_RX Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"PRI_MI2S_RX Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"PRI_MI2S_RX Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"PRI_MI2S_RX Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"PRI_MI2S_RX Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"PRI_MI2S_RX Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"PRI_MI2S_RX Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"PRI_MI2S_RX Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"PRI_MI2S_RX Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"PRI_MI2S_RX Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"PRI_MI2S_RX Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"PRI_MI2S_RX Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"PRI_MI2S_RX Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"PRI_MI2S_RX Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"PRI_MI2S_RX Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"PRI_MI2S_RX Audio Mixer", "MultiMedia26", "MM_DL26"}, + {"PRI_MI2S_RX", NULL, "PRI_MI2S_RX Audio Mixer"}, + + {"INT0_MI2S_RX Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"INT0_MI2S_RX Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"INT0_MI2S_RX Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"INT0_MI2S_RX Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"INT0_MI2S_RX Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"INT0_MI2S_RX Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"INT0_MI2S_RX Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"INT0_MI2S_RX Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"INT0_MI2S_RX Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"INT0_MI2S_RX Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"INT0_MI2S_RX Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"INT0_MI2S_RX Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"INT0_MI2S_RX Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"INT0_MI2S_RX Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"INT0_MI2S_RX Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"INT0_MI2S_RX", NULL, "INT0_MI2S_RX Audio Mixer"}, + + {"INT4_MI2S_RX Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"INT4_MI2S_RX Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"INT4_MI2S_RX Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"INT4_MI2S_RX Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"INT4_MI2S_RX Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"INT4_MI2S_RX Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"INT4_MI2S_RX Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"INT4_MI2S_RX Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"INT4_MI2S_RX Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"INT4_MI2S_RX Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"INT4_MI2S_RX Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"INT4_MI2S_RX Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"INT4_MI2S_RX Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"INT4_MI2S_RX Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"INT4_MI2S_RX Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"INT4_MI2S_RX", NULL, "INT4_MI2S_RX Audio Mixer"}, + + {"QUIN_MI2S_RX Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"QUIN_MI2S_RX Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"QUIN_MI2S_RX Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"QUIN_MI2S_RX Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"QUIN_MI2S_RX Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"QUIN_MI2S_RX Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"QUIN_MI2S_RX Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"QUIN_MI2S_RX Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"QUIN_MI2S_RX Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"QUIN_MI2S_RX Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"QUIN_MI2S_RX Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"QUIN_MI2S_RX Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"QUIN_MI2S_RX Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"QUIN_MI2S_RX Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"QUIN_MI2S_RX Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"QUIN_MI2S_RX Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"QUIN_MI2S_RX Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"QUIN_MI2S_RX", NULL, "QUIN_MI2S_RX Audio Mixer"}, + + {"PRI_TDM_RX_0 Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"PRI_TDM_RX_0 Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"PRI_TDM_RX_0 Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"PRI_TDM_RX_0 Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"PRI_TDM_RX_0 Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"PRI_TDM_RX_0 Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"PRI_TDM_RX_0 Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"PRI_TDM_RX_0 Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"PRI_TDM_RX_0 Audio Mixer", "MultiMedia9", "MM_DL9"}, + {"PRI_TDM_RX_0 Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"PRI_TDM_RX_0 Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"PRI_TDM_RX_0 Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"PRI_TDM_RX_0 Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"PRI_TDM_RX_0 Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"PRI_TDM_RX_0 Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"PRI_TDM_RX_0 Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"PRI_TDM_RX_0 Audio Mixer", "MultiMedia21", "MM_DL21"}, + {"PRI_TDM_RX_0", NULL, "PRI_TDM_RX_0 Audio Mixer"}, + + {"PRI_TDM_RX_1 Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"PRI_TDM_RX_1 Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"PRI_TDM_RX_1 Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"PRI_TDM_RX_1 Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"PRI_TDM_RX_1 Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"PRI_TDM_RX_1 Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"PRI_TDM_RX_1 Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"PRI_TDM_RX_1 Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"PRI_TDM_RX_1 Audio Mixer", "MultiMedia9", "MM_DL9"}, + {"PRI_TDM_RX_1 Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"PRI_TDM_RX_1 Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"PRI_TDM_RX_1 Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"PRI_TDM_RX_1 Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"PRI_TDM_RX_1 Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"PRI_TDM_RX_1 Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"PRI_TDM_RX_1 Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"PRI_TDM_RX_1 Audio Mixer", "MultiMedia21", "MM_DL21"}, + {"PRI_TDM_RX_1", NULL, "PRI_TDM_RX_1 Audio Mixer"}, + + {"PRI_TDM_RX_2 Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"PRI_TDM_RX_2 Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"PRI_TDM_RX_2 Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"PRI_TDM_RX_2 Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"PRI_TDM_RX_2 Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"PRI_TDM_RX_2 Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"PRI_TDM_RX_2 Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"PRI_TDM_RX_2 Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"PRI_TDM_RX_2 Audio Mixer", "MultiMedia9", "MM_DL9"}, + {"PRI_TDM_RX_2 Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"PRI_TDM_RX_2 Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"PRI_TDM_RX_2 Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"PRI_TDM_RX_2 Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"PRI_TDM_RX_2 Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"PRI_TDM_RX_2 Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"PRI_TDM_RX_2 Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"PRI_TDM_RX_2 Audio Mixer", "MultiMedia21", "MM_DL21"}, + {"PRI_TDM_RX_2", NULL, "PRI_TDM_RX_2 Audio Mixer"}, + + {"PRI_TDM_RX_3 Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"PRI_TDM_RX_3 Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"PRI_TDM_RX_3 Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"PRI_TDM_RX_3 Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"PRI_TDM_RX_3 Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"PRI_TDM_RX_3 Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"PRI_TDM_RX_3 Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"PRI_TDM_RX_3 Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"PRI_TDM_RX_3 Audio Mixer", "MultiMedia9", "MM_DL9"}, + {"PRI_TDM_RX_3 Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"PRI_TDM_RX_3 Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"PRI_TDM_RX_3 Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"PRI_TDM_RX_3 Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"PRI_TDM_RX_3 Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"PRI_TDM_RX_3 Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"PRI_TDM_RX_3 Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"PRI_TDM_RX_3 Audio Mixer", "MultiMedia21", "MM_DL21"}, + {"PRI_TDM_RX_3", NULL, "PRI_TDM_RX_3 Audio Mixer"}, + + {"PRI_TDM_TX_0 Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"PRI_TDM_TX_0 Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"PRI_TDM_TX_0 Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"PRI_TDM_TX_0 Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"PRI_TDM_TX_0 Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"PRI_TDM_TX_0 Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"PRI_TDM_TX_0 Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"PRI_TDM_TX_0 Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"PRI_TDM_TX_0 Audio Mixer", "MultiMedia9", "MM_DL9"}, + {"PRI_TDM_TX_0 Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"PRI_TDM_TX_0 Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"PRI_TDM_TX_0 Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"PRI_TDM_TX_0 Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"PRI_TDM_TX_0 Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"PRI_TDM_TX_0 Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"PRI_TDM_TX_0 Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"PRI_TDM_TX_0", NULL, "PRI_TDM_TX_0 Audio Mixer"}, + + {"SEC_TDM_RX_0 Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"SEC_TDM_RX_0 Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"SEC_TDM_RX_0 Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"SEC_TDM_RX_0 Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"SEC_TDM_RX_0 Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"SEC_TDM_RX_0 Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"SEC_TDM_RX_0 Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"SEC_TDM_RX_0 Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"SEC_TDM_RX_0 Audio Mixer", "MultiMedia9", "MM_DL9"}, + {"SEC_TDM_RX_0 Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"SEC_TDM_RX_0 Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"SEC_TDM_RX_0 Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"SEC_TDM_RX_0 Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"SEC_TDM_RX_0 Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"SEC_TDM_RX_0 Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"SEC_TDM_RX_0 Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"SEC_TDM_RX_0 Audio Mixer", "MultiMedia21", "MM_DL21"}, + {"SEC_TDM_RX_0", NULL, "SEC_TDM_RX_0 Audio Mixer"}, + + {"SEC_TDM_RX_1 Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"SEC_TDM_RX_1 Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"SEC_TDM_RX_1 Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"SEC_TDM_RX_1 Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"SEC_TDM_RX_1 Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"SEC_TDM_RX_1 Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"SEC_TDM_RX_1 Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"SEC_TDM_RX_1 Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"SEC_TDM_RX_1 Audio Mixer", "MultiMedia9", "MM_DL9"}, + {"SEC_TDM_RX_1 Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"SEC_TDM_RX_1 Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"SEC_TDM_RX_1 Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"SEC_TDM_RX_1 Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"SEC_TDM_RX_1 Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"SEC_TDM_RX_1 Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"SEC_TDM_RX_1 Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"SEC_TDM_RX_1 Audio Mixer", "MultiMedia21", "MM_DL21"}, + {"SEC_TDM_RX_1", NULL, "SEC_TDM_RX_1 Audio Mixer"}, + + {"SEC_TDM_RX_2 Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"SEC_TDM_RX_2 Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"SEC_TDM_RX_2 Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"SEC_TDM_RX_2 Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"SEC_TDM_RX_2 Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"SEC_TDM_RX_2 Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"SEC_TDM_RX_2 Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"SEC_TDM_RX_2 Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"SEC_TDM_RX_2 Audio Mixer", "MultiMedia9", "MM_DL9"}, + {"SEC_TDM_RX_2 Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"SEC_TDM_RX_2 Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"SEC_TDM_RX_2 Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"SEC_TDM_RX_2 Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"SEC_TDM_RX_2 Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"SEC_TDM_RX_2 Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"SEC_TDM_RX_2 Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"SEC_TDM_RX_2 Audio Mixer", "MultiMedia21", "MM_DL21"}, + {"SEC_TDM_RX_2", NULL, "SEC_TDM_RX_2 Audio Mixer"}, + + {"SEC_TDM_RX_3 Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"SEC_TDM_RX_3 Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"SEC_TDM_RX_3 Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"SEC_TDM_RX_3 Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"SEC_TDM_RX_3 Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"SEC_TDM_RX_3 Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"SEC_TDM_RX_3 Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"SEC_TDM_RX_3 Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"SEC_TDM_RX_3 Audio Mixer", "MultiMedia9", "MM_DL9"}, + {"SEC_TDM_RX_3 Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"SEC_TDM_RX_3 Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"SEC_TDM_RX_3 Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"SEC_TDM_RX_3 Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"SEC_TDM_RX_3 Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"SEC_TDM_RX_3 Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"SEC_TDM_RX_3 Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"SEC_TDM_RX_3 Audio Mixer", "MultiMedia21", "MM_DL21"}, + {"SEC_TDM_RX_3", NULL, "SEC_TDM_RX_3 Audio Mixer"}, + + {"SEC_TDM_TX_0 Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"SEC_TDM_TX_0 Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"SEC_TDM_TX_0 Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"SEC_TDM_TX_0 Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"SEC_TDM_TX_0 Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"SEC_TDM_TX_0 Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"SEC_TDM_TX_0 Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"SEC_TDM_TX_0 Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"SEC_TDM_TX_0 Audio Mixer", "MultiMedia9", "MM_DL9"}, + {"SEC_TDM_TX_0 Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"SEC_TDM_TX_0 Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"SEC_TDM_TX_0 Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"SEC_TDM_TX_0 Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"SEC_TDM_TX_0 Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"SEC_TDM_TX_0 Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"SEC_TDM_TX_0 Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"SEC_TDM_TX_0", NULL, "SEC_TDM_TX_0 Audio Mixer"}, + + {"TERT_TDM_RX_0 Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"TERT_TDM_RX_0 Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"TERT_TDM_RX_0 Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"TERT_TDM_RX_0 Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"TERT_TDM_RX_0 Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"TERT_TDM_RX_0 Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"TERT_TDM_RX_0 Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"TERT_TDM_RX_0 Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"TERT_TDM_RX_0 Audio Mixer", "MultiMedia9", "MM_DL9"}, + {"TERT_TDM_RX_0 Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"TERT_TDM_RX_0 Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"TERT_TDM_RX_0 Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"TERT_TDM_RX_0 Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"TERT_TDM_RX_0 Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"TERT_TDM_RX_0 Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"TERT_TDM_RX_0 Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"TERT_TDM_RX_0 Audio Mixer", "MultiMedia21", "MM_DL21"}, + {"TERT_TDM_RX_0", NULL, "TERT_TDM_RX_0 Audio Mixer"}, + + {"TERT_TDM_TX_0 Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"TERT_TDM_TX_0 Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"TERT_TDM_TX_0 Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"TERT_TDM_TX_0 Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"TERT_TDM_TX_0 Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"TERT_TDM_TX_0 Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"TERT_TDM_TX_0 Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"TERT_TDM_TX_0 Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"TERT_TDM_TX_0 Audio Mixer", "MultiMedia9", "MM_DL9"}, + {"TERT_TDM_TX_0 Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"TERT_TDM_TX_0 Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"TERT_TDM_TX_0 Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"TERT_TDM_TX_0 Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"TERT_TDM_TX_0 Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"TERT_TDM_TX_0 Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"TERT_TDM_TX_0 Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"TERT_TDM_TX_0", NULL, "TERT_TDM_TX_0 Audio Mixer"}, + + {"TERT_TDM_RX_1 Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"TERT_TDM_RX_1 Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"TERT_TDM_RX_1 Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"TERT_TDM_RX_1 Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"TERT_TDM_RX_1 Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"TERT_TDM_RX_1 Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"TERT_TDM_RX_1 Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"TERT_TDM_RX_1 Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"TERT_TDM_RX_1 Audio Mixer", "MultiMedia9", "MM_DL9"}, + {"TERT_TDM_RX_1 Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"TERT_TDM_RX_1 Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"TERT_TDM_RX_1 Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"TERT_TDM_RX_1 Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"TERT_TDM_RX_1 Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"TERT_TDM_RX_1 Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"TERT_TDM_RX_1 Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"TERT_TDM_RX_1 Audio Mixer", "MultiMedia21", "MM_DL21"}, + {"TERT_TDM_RX_1", NULL, "TERT_TDM_RX_1 Audio Mixer"}, + + {"TERT_TDM_RX_2 Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"TERT_TDM_RX_2 Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"TERT_TDM_RX_2 Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"TERT_TDM_RX_2 Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"TERT_TDM_RX_2 Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"TERT_TDM_RX_2 Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"TERT_TDM_RX_2 Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"TERT_TDM_RX_2 Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"TERT_TDM_RX_2 Audio Mixer", "MultiMedia9", "MM_DL9"}, + {"TERT_TDM_RX_2 Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"TERT_TDM_RX_2 Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"TERT_TDM_RX_2 Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"TERT_TDM_RX_2 Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"TERT_TDM_RX_2 Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"TERT_TDM_RX_2 Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"TERT_TDM_RX_2 Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"TERT_TDM_RX_2 Audio Mixer", "MultiMedia21", "MM_DL21"}, + {"TERT_TDM_RX_2", NULL, "TERT_TDM_RX_2 Audio Mixer"}, + + {"TERT_TDM_RX_3 Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"TERT_TDM_RX_3 Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"TERT_TDM_RX_3 Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"TERT_TDM_RX_3 Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"TERT_TDM_RX_3 Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"TERT_TDM_RX_3 Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"TERT_TDM_RX_3 Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"TERT_TDM_RX_3 Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"TERT_TDM_RX_3 Audio Mixer", "MultiMedia9", "MM_DL9"}, + {"TERT_TDM_RX_3 Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"TERT_TDM_RX_3 Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"TERT_TDM_RX_3 Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"TERT_TDM_RX_3 Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"TERT_TDM_RX_3 Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"TERT_TDM_RX_3 Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"TERT_TDM_RX_3 Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"TERT_TDM_RX_3 Audio Mixer", "MultiMedia21", "MM_DL21"}, + {"TERT_TDM_RX_3", NULL, "TERT_TDM_RX_3 Audio Mixer"}, + + {"TERT_TDM_RX_4 Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"TERT_TDM_RX_4 Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"TERT_TDM_RX_4 Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"TERT_TDM_RX_4 Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"TERT_TDM_RX_4 Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"TERT_TDM_RX_4 Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"TERT_TDM_RX_4 Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"TERT_TDM_RX_4 Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"TERT_TDM_RX_4 Audio Mixer", "MultiMedia9", "MM_DL9"}, + {"TERT_TDM_RX_4 Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"TERT_TDM_RX_4 Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"TERT_TDM_RX_4 Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"TERT_TDM_RX_4 Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"TERT_TDM_RX_4 Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"TERT_TDM_RX_4 Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"TERT_TDM_RX_4 Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"TERT_TDM_RX_4 Audio Mixer", "MultiMedia21", "MM_DL21"}, + {"TERT_TDM_RX_4", NULL, "TERT_TDM_RX_4 Audio Mixer"}, + + {"QUAT_TDM_RX_0 Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"QUAT_TDM_RX_0 Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"QUAT_TDM_RX_0 Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"QUAT_TDM_RX_0 Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"QUAT_TDM_RX_0 Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"QUAT_TDM_RX_0 Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"QUAT_TDM_RX_0 Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"QUAT_TDM_RX_0 Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"QUAT_TDM_RX_0 Audio Mixer", "MultiMedia9", "MM_DL9"}, + {"QUAT_TDM_RX_0 Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"QUAT_TDM_RX_0 Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"QUAT_TDM_RX_0 Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"QUAT_TDM_RX_0 Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"QUAT_TDM_RX_0 Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"QUAT_TDM_RX_0 Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"QUAT_TDM_RX_0 Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"QUAT_TDM_RX_0 Audio Mixer", "MultiMedia20", "MM_DL20"}, + {"QUAT_TDM_RX_0 Audio Mixer", "MultiMedia21", "MM_DL21"}, + {"QUAT_TDM_RX_0 Audio Mixer", "MultiMedia30", "MM_DL30"}, + {"QUAT_TDM_RX_0 Audio Mixer", "MultiMedia31", "MM_DL31"}, + {"QUAT_TDM_RX_0", NULL, "QUAT_TDM_RX_0 Audio Mixer"}, + + {"QUAT_TDM_TX_0 Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"QUAT_TDM_TX_0 Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"QUAT_TDM_TX_0 Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"QUAT_TDM_TX_0 Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"QUAT_TDM_TX_0 Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"QUAT_TDM_TX_0 Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"QUAT_TDM_TX_0 Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"QUAT_TDM_TX_0 Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"QUAT_TDM_TX_0 Audio Mixer", "MultiMedia9", "MM_DL9"}, + {"QUAT_TDM_TX_0 Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"QUAT_TDM_TX_0 Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"QUAT_TDM_TX_0 Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"QUAT_TDM_TX_0 Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"QUAT_TDM_TX_0 Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"QUAT_TDM_TX_0 Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"QUAT_TDM_TX_0 Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"QUAT_TDM_TX_0", NULL, "QUAT_TDM_TX_0 Audio Mixer"}, + + {"QUAT_TDM_RX_1 Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"QUAT_TDM_RX_1 Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"QUAT_TDM_RX_1 Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"QUAT_TDM_RX_1 Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"QUAT_TDM_RX_1 Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"QUAT_TDM_RX_1 Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"QUAT_TDM_RX_1 Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"QUAT_TDM_RX_1 Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"QUAT_TDM_RX_1 Audio Mixer", "MultiMedia9", "MM_DL9"}, + {"QUAT_TDM_RX_1 Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"QUAT_TDM_RX_1 Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"QUAT_TDM_RX_1 Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"QUAT_TDM_RX_1 Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"QUAT_TDM_RX_1 Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"QUAT_TDM_RX_1 Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"QUAT_TDM_RX_1 Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"QUAT_TDM_RX_1 Audio Mixer", "MultiMedia20", "MM_DL20"}, + {"QUAT_TDM_RX_1 Audio Mixer", "MultiMedia21", "MM_DL21"}, + {"QUAT_TDM_RX_1 Audio Mixer", "MultiMedia30", "MM_DL30"}, + {"QUAT_TDM_RX_1 Audio Mixer", "MultiMedia31", "MM_DL31"}, + {"QUAT_TDM_RX_1", NULL, "QUAT_TDM_RX_1 Audio Mixer"}, + + {"QUAT_TDM_RX_2 Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"QUAT_TDM_RX_2 Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"QUAT_TDM_RX_2 Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"QUAT_TDM_RX_2 Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"QUAT_TDM_RX_2 Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"QUAT_TDM_RX_2 Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"QUAT_TDM_RX_2 Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"QUAT_TDM_RX_2 Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"QUAT_TDM_RX_2 Audio Mixer", "MultiMedia9", "MM_DL9"}, + {"QUAT_TDM_RX_2 Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"QUAT_TDM_RX_2 Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"QUAT_TDM_RX_2 Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"QUAT_TDM_RX_2 Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"QUAT_TDM_RX_2 Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"QUAT_TDM_RX_2 Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"QUAT_TDM_RX_2 Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"QUAT_TDM_RX_2 Audio Mixer", "MultiMedia20", "MM_DL20"}, + {"QUAT_TDM_RX_2 Audio Mixer", "MultiMedia21", "MM_DL21"}, + {"QUAT_TDM_RX_2 Audio Mixer", "MultiMedia30", "MM_DL30"}, + {"QUAT_TDM_RX_2 Audio Mixer", "MultiMedia31", "MM_DL31"}, + {"QUAT_TDM_RX_2", NULL, "QUAT_TDM_RX_2 Audio Mixer"}, + + {"QUAT_TDM_RX_3 Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"QUAT_TDM_RX_3 Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"QUAT_TDM_RX_3 Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"QUAT_TDM_RX_3 Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"QUAT_TDM_RX_3 Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"QUAT_TDM_RX_3 Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"QUAT_TDM_RX_3 Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"QUAT_TDM_RX_3 Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"QUAT_TDM_RX_3 Audio Mixer", "MultiMedia9", "MM_DL9"}, + {"QUAT_TDM_RX_3 Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"QUAT_TDM_RX_3 Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"QUAT_TDM_RX_3 Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"QUAT_TDM_RX_3 Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"QUAT_TDM_RX_3 Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"QUAT_TDM_RX_3 Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"QUAT_TDM_RX_3 Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"QUAT_TDM_RX_3 Audio Mixer", "MultiMedia20", "MM_DL20"}, + {"QUAT_TDM_RX_3 Audio Mixer", "MultiMedia21", "MM_DL21"}, + {"QUAT_TDM_RX_3 Audio Mixer", "MultiMedia30", "MM_DL30"}, + {"QUAT_TDM_RX_3 Audio Mixer", "MultiMedia31", "MM_DL31"}, + {"QUAT_TDM_RX_3", NULL, "QUAT_TDM_RX_3 Audio Mixer"}, + + {"QUIN_TDM_RX_0 Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"QUIN_TDM_RX_0 Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"QUIN_TDM_RX_0 Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"QUIN_TDM_RX_0 Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"QUIN_TDM_RX_0 Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"QUIN_TDM_RX_0 Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"QUIN_TDM_RX_0 Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"QUIN_TDM_RX_0 Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"QUIN_TDM_RX_0 Audio Mixer", "MultiMedia9", "MM_DL9"}, + {"QUIN_TDM_RX_0 Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"QUIN_TDM_RX_0 Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"QUIN_TDM_RX_0 Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"QUIN_TDM_RX_0 Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"QUIN_TDM_RX_0 Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"QUIN_TDM_RX_0 Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"QUIN_TDM_RX_0 Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"QUIN_TDM_RX_0 Audio Mixer", "MultiMedia20", "MM_DL20"}, + {"QUIN_TDM_RX_0 Audio Mixer", "MultiMedia21", "MM_DL21"}, + {"QUIN_TDM_RX_0", NULL, "QUIN_TDM_RX_0 Audio Mixer"}, + + {"QUIN_TDM_TX_0 Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"QUIN_TDM_TX_0 Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"QUIN_TDM_TX_0 Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"QUIN_TDM_TX_0 Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"QUIN_TDM_TX_0 Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"QUIN_TDM_TX_0 Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"QUIN_TDM_TX_0 Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"QUIN_TDM_TX_0 Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"QUIN_TDM_TX_0 Audio Mixer", "MultiMedia9", "MM_DL9"}, + {"QUIN_TDM_TX_0 Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"QUIN_TDM_TX_0 Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"QUIN_TDM_TX_0 Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"QUIN_TDM_TX_0 Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"QUIN_TDM_TX_0 Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"QUIN_TDM_TX_0 Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"QUIN_TDM_TX_0 Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"QUIN_TDM_TX_0", NULL, "QUIN_TDM_TX_0 Audio Mixer"}, + + {"QUIN_TDM_RX_1 Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"QUIN_TDM_RX_1 Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"QUIN_TDM_RX_1 Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"QUIN_TDM_RX_1 Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"QUIN_TDM_RX_1 Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"QUIN_TDM_RX_1 Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"QUIN_TDM_RX_1 Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"QUIN_TDM_RX_1 Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"QUIN_TDM_RX_1 Audio Mixer", "MultiMedia9", "MM_DL9"}, + {"QUIN_TDM_RX_1 Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"QUIN_TDM_RX_1 Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"QUIN_TDM_RX_1 Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"QUIN_TDM_RX_1 Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"QUIN_TDM_RX_1 Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"QUIN_TDM_RX_1 Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"QUIN_TDM_RX_1 Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"QUIN_TDM_RX_1 Audio Mixer", "MultiMedia20", "MM_DL20"}, + {"QUIN_TDM_RX_1 Audio Mixer", "MultiMedia21", "MM_DL21"}, + {"QUIN_TDM_RX_1", NULL, "QUIN_TDM_RX_1 Audio Mixer"}, + + {"QUIN_TDM_RX_2 Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"QUIN_TDM_RX_2 Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"QUIN_TDM_RX_2 Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"QUIN_TDM_RX_2 Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"QUIN_TDM_RX_2 Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"QUIN_TDM_RX_2 Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"QUIN_TDM_RX_2 Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"QUIN_TDM_RX_2 Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"QUIN_TDM_RX_2 Audio Mixer", "MultiMedia9", "MM_DL9"}, + {"QUIN_TDM_RX_2 Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"QUIN_TDM_RX_2 Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"QUIN_TDM_RX_2 Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"QUIN_TDM_RX_2 Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"QUIN_TDM_RX_2 Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"QUIN_TDM_RX_2 Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"QUIN_TDM_RX_2 Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"QUIN_TDM_RX_2 Audio Mixer", "MultiMedia20", "MM_DL20"}, + {"QUIN_TDM_RX_2 Audio Mixer", "MultiMedia21", "MM_DL21"}, + {"QUIN_TDM_RX_2", NULL, "QUIN_TDM_RX_2 Audio Mixer"}, + + {"QUIN_TDM_RX_3 Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"QUIN_TDM_RX_3 Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"QUIN_TDM_RX_3 Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"QUIN_TDM_RX_3 Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"QUIN_TDM_RX_3 Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"QUIN_TDM_RX_3 Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"QUIN_TDM_RX_3 Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"QUIN_TDM_RX_3 Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"QUIN_TDM_RX_3 Audio Mixer", "MultiMedia9", "MM_DL9"}, + {"QUIN_TDM_RX_3 Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"QUIN_TDM_RX_3 Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"QUIN_TDM_RX_3 Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"QUIN_TDM_RX_3 Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"QUIN_TDM_RX_3 Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"QUIN_TDM_RX_3 Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"QUIN_TDM_RX_3 Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"QUIN_TDM_RX_3 Audio Mixer", "MultiMedia20", "MM_DL20"}, + {"QUIN_TDM_RX_3 Audio Mixer", "MultiMedia21", "MM_DL21"}, + {"QUIN_TDM_RX_3", NULL, "QUIN_TDM_RX_3 Audio Mixer"}, + + {"MultiMedia1 Mixer", "PRI_TX", "PRI_I2S_TX"}, + {"MultiMedia1 Mixer", "MI2S_TX", "MI2S_TX"}, + {"MultiMedia2 Mixer", "MI2S_TX", "MI2S_TX"}, + {"MultiMedia3 Mixer", "MI2S_TX", "MI2S_TX"}, + {"MultiMedia5 Mixer", "MI2S_TX", "MI2S_TX"}, + {"MultiMedia10 Mixer", "MI2S_TX", "MI2S_TX"}, + {"MultiMedia16 Mixer", "MI2S_TX", "MI2S_TX"}, + {"MultiMedia1 Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + {"MultiMedia2 Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + {"MultiMedia6 Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + {"MultiMedia1 Mixer", "QUIN_MI2S_TX", "QUIN_MI2S_TX"}, + {"MultiMedia2 Mixer", "QUIN_MI2S_TX", "QUIN_MI2S_TX"}, + {"MultiMedia1 Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"}, + {"MultiMedia2 Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"}, + {"MultiMedia1 Mixer", "INT2_MI2S_TX", "INT2_MI2S_TX"}, + {"MultiMedia2 Mixer", "INT2_MI2S_TX", "INT2_MI2S_TX"}, + {"MultiMedia1 Mixer", "INT3_MI2S_TX", "INT3_MI2S_TX"}, + {"MultiMedia2 Mixer", "INT3_MI2S_TX", "INT3_MI2S_TX"}, + {"MultiMedia1 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"}, + {"MultiMedia1 Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"}, + {"MultiMedia3 Mixer", "AUX_PCM_TX", "AUX_PCM_TX"}, + {"MultiMedia5 Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"}, + {"MultiMedia10 Mixer", "AUX_PCM_TX", "AUX_PCM_TX"}, + {"MultiMedia1 Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"}, + {"MultiMedia3 Mixer", "SEC_AUX_PCM_TX", "SEC_AUX_PCM_TX"}, + {"MultiMedia5 Mixer", "SEC_AUX_PCM_TX", "SEC_AUX_PCM_TX"}, + {"MultiMedia10 Mixer", "SEC_AUX_PCM_TX", "SEC_AUX_PCM_TX"}, + {"MultiMedia16 Mixer", "AUX_PCM_TX", "AUX_PCM_TX"}, + {"MultiMedia16 Mixer", "SEC_AUX_PCM_TX", "SEC_AUX_PCM_TX"}, + {"MultiMedia1 Mixer", "TERT_AUXPCM_UL_TX", "TERT_AUX_PCM_TX"}, + {"MultiMedia3 Mixer", "TERT_AUX_PCM_TX", "TERT_AUX_PCM_TX"}, + {"MultiMedia5 Mixer", "TERT_AUX_PCM_TX", "TERT_AUX_PCM_TX"}, + {"MultiMedia10 Mixer", "TERT_AUX_PCM_TX", "TERT_AUX_PCM_TX"}, + {"MultiMedia1 Mixer", "QUAT_AUXPCM_UL_TX", "QUAT_AUX_PCM_TX"}, + {"MultiMedia3 Mixer", "QUAT_AUX_PCM_TX", "QUAT_AUX_PCM_TX"}, + {"MultiMedia5 Mixer", "QUAT_AUX_PCM_TX", "QUAT_AUX_PCM_TX"}, + {"MultiMedia10 Mixer", "QUAT_AUX_PCM_TX", "QUAT_AUX_PCM_TX"}, + {"MultiMedia16 Mixer", "QUAT_AUX_PCM_TX", "QUAT_AUX_PCM_TX"}, + {"MultiMedia1 Mixer", "QUIN_AUXPCM_UL_TX", "QUIN_AUX_PCM_TX"}, + {"MultiMedia3 Mixer", "QUIN_AUX_PCM_TX", "QUIN_AUX_PCM_TX"}, + {"MultiMedia5 Mixer", "QUIN_AUX_PCM_TX", "QUIN_AUX_PCM_TX"}, + {"MultiMedia2 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"}, + {"MultiMedia2 Mixer", "SLIM_6_TX", "SLIMBUS_6_TX"}, + {"MultiMedia2 Mixer", "SLIM_1_TX", "SLIMBUS_1_TX"}, + {"MultiMedia2 Mixer", "SLIM_8_TX", "SLIMBUS_8_TX"}, + {"MultiMedia1 Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"}, + {"MultiMedia1 Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"MultiMedia2 Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"}, + {"MultiMedia6 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"}, + {"MultiMedia6 Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"}, + {"MultiMedia3 Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"}, + {"MultiMedia5 Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"}, + {"MultiMedia10 Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"}, + {"MultiMedia6 Mixer", "INT2_MI2S_TX", "INT2_MI2S_TX"}, + {"MultiMedia3 Mixer", "INT2_MI2S_TX", "INT2_MI2S_TX"}, + {"MultiMedia5 Mixer", "INT2_MI2S_TX", "INT2_MI2S_TX"}, + {"MultiMedia10 Mixer", "INT2_MI2S_TX", "INT2_MI2S_TX"}, + {"MultiMedia16 Mixer", "INT2_MI2S_TX", "INT2_MI2S_TX"}, + {"MultiMedia6 Mixer", "INT3_MI2S_TX", "INT3_MI2S_TX"}, + {"MultiMedia3 Mixer", "INT3_MI2S_TX", "INT3_MI2S_TX"}, + {"MultiMedia5 Mixer", "INT3_MI2S_TX", "INT3_MI2S_TX"}, + {"MultiMedia10 Mixer", "INT3_MI2S_TX", "INT3_MI2S_TX"}, + {"MultiMedia16 Mixer", "INT3_MI2S_TX", "INT3_MI2S_TX"}, + {"MultiMedia17 Mixer", "INT3_MI2S_TX", "INT3_MI2S_TX"}, + {"MultiMedia18 Mixer", "INT3_MI2S_TX", "INT3_MI2S_TX"}, + {"MultiMedia19 Mixer", "INT3_MI2S_TX", "INT3_MI2S_TX"}, + {"MultiMedia28 Mixer", "INT3_MI2S_TX", "INT3_MI2S_TX"}, + {"MultiMedia29 Mixer", "INT3_MI2S_TX", "INT3_MI2S_TX"}, + {"MultiMedia6 Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"MultiMedia6 Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"}, + {"MultiMedia6 Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"}, + {"MultiMedia6 Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"}, + {"MultiMedia6 Mixer", "QUIN_MI2S_TX", "QUIN_MI2S_TX"}, + + {"MultiMedia1 Mixer", "PRI_TDM_TX_0", "PRI_TDM_TX_0"}, + {"MultiMedia1 Mixer", "PRI_TDM_TX_1", "PRI_TDM_TX_1"}, + {"MultiMedia1 Mixer", "PRI_TDM_TX_2", "PRI_TDM_TX_2"}, + {"MultiMedia1 Mixer", "PRI_TDM_TX_3", "PRI_TDM_TX_3"}, + {"MultiMedia1 Mixer", "SEC_TDM_TX_0", "SEC_TDM_TX_0"}, + {"MultiMedia1 Mixer", "SEC_TDM_TX_1", "SEC_TDM_TX_1"}, + {"MultiMedia1 Mixer", "SEC_TDM_TX_2", "SEC_TDM_TX_2"}, + {"MultiMedia1 Mixer", "SEC_TDM_TX_3", "SEC_TDM_TX_3"}, + {"MultiMedia1 Mixer", "TERT_TDM_TX_0", "TERT_TDM_TX_0"}, + {"MultiMedia1 Mixer", "TERT_TDM_TX_1", "TERT_TDM_TX_1"}, + {"MultiMedia1 Mixer", "TERT_TDM_TX_2", "TERT_TDM_TX_2"}, + {"MultiMedia1 Mixer", "TERT_TDM_TX_3", "TERT_TDM_TX_3"}, + {"MultiMedia1 Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"}, + {"MultiMedia1 Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"}, + {"MultiMedia1 Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"}, + {"MultiMedia1 Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"}, + {"MultiMedia1 Mixer", "QUIN_TDM_TX_0", "QUIN_TDM_TX_0"}, + {"MultiMedia1 Mixer", "QUIN_TDM_TX_1", "QUIN_TDM_TX_1"}, + {"MultiMedia1 Mixer", "QUIN_TDM_TX_2", "QUIN_TDM_TX_2"}, + {"MultiMedia1 Mixer", "QUIN_TDM_TX_3", "QUIN_TDM_TX_3"}, + {"MultiMedia1 Mixer", "WSA_CDC_DMA_TX_0", "WSA_CDC_DMA_TX_0"}, + {"MultiMedia1 Mixer", "WSA_CDC_DMA_TX_1", "WSA_CDC_DMA_TX_1"}, + {"MultiMedia1 Mixer", "WSA_CDC_DMA_TX_2", "WSA_CDC_DMA_TX_2"}, + {"MultiMedia1 Mixer", "VA_CDC_DMA_TX_0", "VA_CDC_DMA_TX_0"}, + {"MultiMedia1 Mixer", "VA_CDC_DMA_TX_1", "VA_CDC_DMA_TX_1"}, + {"MultiMedia1 Mixer", "TX_CDC_DMA_TX_0", "TX_CDC_DMA_TX_0"}, + {"MultiMedia1 Mixer", "TX_CDC_DMA_TX_1", "TX_CDC_DMA_TX_1"}, + {"MultiMedia1 Mixer", "TX_CDC_DMA_TX_2", "TX_CDC_DMA_TX_2"}, + {"MultiMedia1 Mixer", "TX_CDC_DMA_TX_3", "TX_CDC_DMA_TX_3"}, + {"MultiMedia1 Mixer", "TX_CDC_DMA_TX_4", "TX_CDC_DMA_TX_4"}, + {"MultiMedia1 Mixer", "TX_CDC_DMA_TX_5", "TX_CDC_DMA_TX_5"}, + {"MultiMedia1 Mixer", "PRI_SPDIF_TX", "PRI_SPDIF_TX"}, + {"MultiMedia1 Mixer", "SEC_SPDIF_TX", "SEC_SPDIF_TX"}, + + {"MultiMedia2 Mixer", "PRI_TDM_TX_0", "PRI_TDM_TX_0"}, + {"MultiMedia2 Mixer", "PRI_TDM_TX_1", "PRI_TDM_TX_1"}, + {"MultiMedia2 Mixer", "PRI_TDM_TX_2", "PRI_TDM_TX_2"}, + {"MultiMedia2 Mixer", "PRI_TDM_TX_3", "PRI_TDM_TX_3"}, + {"MultiMedia2 Mixer", "SEC_TDM_TX_0", "SEC_TDM_TX_0"}, + {"MultiMedia2 Mixer", "SEC_TDM_TX_1", "SEC_TDM_TX_1"}, + {"MultiMedia2 Mixer", "SEC_TDM_TX_2", "SEC_TDM_TX_2"}, + {"MultiMedia2 Mixer", "SEC_TDM_TX_3", "SEC_TDM_TX_3"}, + {"MultiMedia2 Mixer", "TERT_TDM_TX_0", "TERT_TDM_TX_0"}, + {"MultiMedia2 Mixer", "TERT_TDM_TX_1", "TERT_TDM_TX_1"}, + {"MultiMedia2 Mixer", "TERT_TDM_TX_2", "TERT_TDM_TX_2"}, + {"MultiMedia2 Mixer", "TERT_TDM_TX_3", "TERT_TDM_TX_3"}, + {"MultiMedia2 Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"}, + {"MultiMedia2 Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"}, + {"MultiMedia2 Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"}, + {"MultiMedia2 Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"}, + {"MultiMedia2 Mixer", "QUIN_TDM_TX_0", "QUIN_TDM_TX_0"}, + {"MultiMedia2 Mixer", "QUIN_TDM_TX_1", "QUIN_TDM_TX_1"}, + {"MultiMedia2 Mixer", "QUIN_TDM_TX_2", "QUIN_TDM_TX_2"}, + {"MultiMedia2 Mixer", "QUIN_TDM_TX_3", "QUIN_TDM_TX_3"}, + {"MultiMedia2 Mixer", "WSA_CDC_DMA_TX_0", "WSA_CDC_DMA_TX_0"}, + {"MultiMedia2 Mixer", "WSA_CDC_DMA_TX_1", "WSA_CDC_DMA_TX_1"}, + {"MultiMedia2 Mixer", "WSA_CDC_DMA_TX_2", "WSA_CDC_DMA_TX_2"}, + {"MultiMedia2 Mixer", "VA_CDC_DMA_TX_0", "VA_CDC_DMA_TX_0"}, + {"MultiMedia2 Mixer", "VA_CDC_DMA_TX_1", "VA_CDC_DMA_TX_1"}, + {"MultiMedia2 Mixer", "TX_CDC_DMA_TX_0", "TX_CDC_DMA_TX_0"}, + {"MultiMedia2 Mixer", "TX_CDC_DMA_TX_1", "TX_CDC_DMA_TX_1"}, + {"MultiMedia2 Mixer", "TX_CDC_DMA_TX_2", "TX_CDC_DMA_TX_2"}, + {"MultiMedia2 Mixer", "TX_CDC_DMA_TX_3", "TX_CDC_DMA_TX_3"}, + {"MultiMedia2 Mixer", "TX_CDC_DMA_TX_4", "TX_CDC_DMA_TX_4"}, + {"MultiMedia2 Mixer", "TX_CDC_DMA_TX_5", "TX_CDC_DMA_TX_5"}, + {"MultiMedia2 Mixer", "PRI_SPDIF_TX", "PRI_SPDIF_TX"}, + {"MultiMedia2 Mixer", "SEC_SPDIF_TX", "SEC_SPDIF_TX"}, + + {"MultiMedia3 Mixer", "PRI_TDM_TX_0", "PRI_TDM_TX_0"}, + {"MultiMedia3 Mixer", "PRI_TDM_TX_1", "PRI_TDM_TX_1"}, + {"MultiMedia3 Mixer", "PRI_TDM_TX_2", "PRI_TDM_TX_2"}, + {"MultiMedia3 Mixer", "PRI_TDM_TX_3", "PRI_TDM_TX_3"}, + {"MultiMedia3 Mixer", "SEC_TDM_TX_0", "SEC_TDM_TX_0"}, + {"MultiMedia3 Mixer", "SEC_TDM_TX_1", "SEC_TDM_TX_1"}, + {"MultiMedia3 Mixer", "SEC_TDM_TX_2", "SEC_TDM_TX_2"}, + {"MultiMedia3 Mixer", "SEC_TDM_TX_3", "SEC_TDM_TX_3"}, + {"MultiMedia3 Mixer", "TERT_TDM_TX_0", "TERT_TDM_TX_0"}, + {"MultiMedia3 Mixer", "TERT_TDM_TX_1", "TERT_TDM_TX_1"}, + {"MultiMedia3 Mixer", "TERT_TDM_TX_2", "TERT_TDM_TX_2"}, + {"MultiMedia3 Mixer", "TERT_TDM_TX_3", "TERT_TDM_TX_3"}, + {"MultiMedia3 Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"}, + {"MultiMedia3 Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"}, + {"MultiMedia3 Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"}, + {"MultiMedia3 Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"}, + {"MultiMedia3 Mixer", "QUIN_TDM_TX_0", "QUIN_TDM_TX_0"}, + {"MultiMedia3 Mixer", "QUIN_TDM_TX_1", "QUIN_TDM_TX_1"}, + {"MultiMedia3 Mixer", "QUIN_TDM_TX_2", "QUIN_TDM_TX_2"}, + {"MultiMedia3 Mixer", "QUIN_TDM_TX_3", "QUIN_TDM_TX_3"}, + {"MultiMedia3 Mixer", "WSA_CDC_DMA_TX_0", "WSA_CDC_DMA_TX_0"}, + {"MultiMedia3 Mixer", "WSA_CDC_DMA_TX_1", "WSA_CDC_DMA_TX_1"}, + {"MultiMedia3 Mixer", "WSA_CDC_DMA_TX_2", "WSA_CDC_DMA_TX_2"}, + {"MultiMedia3 Mixer", "VA_CDC_DMA_TX_0", "VA_CDC_DMA_TX_0"}, + {"MultiMedia3 Mixer", "VA_CDC_DMA_TX_1", "VA_CDC_DMA_TX_1"}, + {"MultiMedia3 Mixer", "TX_CDC_DMA_TX_0", "TX_CDC_DMA_TX_0"}, + {"MultiMedia3 Mixer", "TX_CDC_DMA_TX_1", "TX_CDC_DMA_TX_1"}, + {"MultiMedia3 Mixer", "TX_CDC_DMA_TX_2", "TX_CDC_DMA_TX_2"}, + {"MultiMedia3 Mixer", "TX_CDC_DMA_TX_3", "TX_CDC_DMA_TX_3"}, + {"MultiMedia3 Mixer", "TX_CDC_DMA_TX_4", "TX_CDC_DMA_TX_4"}, + {"MultiMedia3 Mixer", "TX_CDC_DMA_TX_5", "TX_CDC_DMA_TX_5"}, + {"MultiMedia3 Mixer", "PRI_SPDIF_TX", "PRI_SPDIF_TX"}, + {"MultiMedia3 Mixer", "SEC_SPDIF_TX", "SEC_SPDIF_TX"}, + + {"MultiMedia4 Mixer", "PRI_TDM_TX_0", "PRI_TDM_TX_0"}, + {"MultiMedia4 Mixer", "PRI_TDM_TX_1", "PRI_TDM_TX_1"}, + {"MultiMedia4 Mixer", "PRI_TDM_TX_2", "PRI_TDM_TX_2"}, + {"MultiMedia4 Mixer", "PRI_TDM_TX_3", "PRI_TDM_TX_3"}, + {"MultiMedia4 Mixer", "SEC_TDM_TX_0", "SEC_TDM_TX_0"}, + {"MultiMedia4 Mixer", "SEC_TDM_TX_1", "SEC_TDM_TX_1"}, + {"MultiMedia4 Mixer", "SEC_TDM_TX_2", "SEC_TDM_TX_2"}, + {"MultiMedia4 Mixer", "SEC_TDM_TX_3", "SEC_TDM_TX_3"}, + {"MultiMedia4 Mixer", "TERT_TDM_TX_0", "TERT_TDM_TX_0"}, + {"MultiMedia4 Mixer", "TERT_TDM_TX_1", "TERT_TDM_TX_1"}, + {"MultiMedia4 Mixer", "TERT_TDM_TX_2", "TERT_TDM_TX_2"}, + {"MultiMedia4 Mixer", "TERT_TDM_TX_3", "TERT_TDM_TX_3"}, + {"MultiMedia4 Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"}, + {"MultiMedia4 Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"}, + {"MultiMedia4 Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"}, + {"MultiMedia4 Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"}, + {"MultiMedia4 Mixer", "QUIN_TDM_TX_0", "QUIN_TDM_TX_0"}, + {"MultiMedia4 Mixer", "QUIN_TDM_TX_1", "QUIN_TDM_TX_1"}, + {"MultiMedia4 Mixer", "QUIN_TDM_TX_2", "QUIN_TDM_TX_2"}, + {"MultiMedia4 Mixer", "QUIN_TDM_TX_3", "QUIN_TDM_TX_3"}, + {"MultiMedia4 Mixer", "WSA_CDC_DMA_TX_0", "WSA_CDC_DMA_TX_0"}, + {"MultiMedia4 Mixer", "WSA_CDC_DMA_TX_1", "WSA_CDC_DMA_TX_1"}, + {"MultiMedia4 Mixer", "WSA_CDC_DMA_TX_2", "WSA_CDC_DMA_TX_2"}, + {"MultiMedia4 Mixer", "VA_CDC_DMA_TX_0", "VA_CDC_DMA_TX_0"}, + {"MultiMedia4 Mixer", "VA_CDC_DMA_TX_1", "VA_CDC_DMA_TX_1"}, + {"MultiMedia4 Mixer", "TX_CDC_DMA_TX_0", "TX_CDC_DMA_TX_0"}, + {"MultiMedia4 Mixer", "TX_CDC_DMA_TX_1", "TX_CDC_DMA_TX_1"}, + {"MultiMedia4 Mixer", "TX_CDC_DMA_TX_2", "TX_CDC_DMA_TX_2"}, + {"MultiMedia4 Mixer", "TX_CDC_DMA_TX_3", "TX_CDC_DMA_TX_3"}, + {"MultiMedia4 Mixer", "TX_CDC_DMA_TX_4", "TX_CDC_DMA_TX_4"}, + {"MultiMedia4 Mixer", "TX_CDC_DMA_TX_5", "TX_CDC_DMA_TX_5"}, + {"MultiMedia4 Mixer", "PRI_SPDIF_TX", "PRI_SPDIF_TX"}, + {"MultiMedia4 Mixer", "SEC_SPDIF_TX", "SEC_SPDIF_TX"}, + + {"MultiMedia5 Mixer", "PRI_TDM_TX_0", "PRI_TDM_TX_0"}, + {"MultiMedia5 Mixer", "PRI_TDM_TX_1", "PRI_TDM_TX_1"}, + {"MultiMedia5 Mixer", "PRI_TDM_TX_2", "PRI_TDM_TX_2"}, + {"MultiMedia5 Mixer", "PRI_TDM_TX_3", "PRI_TDM_TX_3"}, + {"MultiMedia5 Mixer", "SEC_TDM_TX_0", "SEC_TDM_TX_0"}, + {"MultiMedia5 Mixer", "SEC_TDM_TX_1", "SEC_TDM_TX_1"}, + {"MultiMedia5 Mixer", "SEC_TDM_TX_2", "SEC_TDM_TX_2"}, + {"MultiMedia5 Mixer", "SEC_TDM_TX_3", "SEC_TDM_TX_3"}, + {"MultiMedia5 Mixer", "TERT_TDM_TX_0", "TERT_TDM_TX_0"}, + {"MultiMedia5 Mixer", "TERT_TDM_TX_1", "TERT_TDM_TX_1"}, + {"MultiMedia5 Mixer", "TERT_TDM_TX_2", "TERT_TDM_TX_2"}, + {"MultiMedia5 Mixer", "TERT_TDM_TX_3", "TERT_TDM_TX_3"}, + {"MultiMedia5 Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"}, + {"MultiMedia5 Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"}, + {"MultiMedia5 Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"}, + {"MultiMedia5 Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"}, + {"MultiMedia5 Mixer", "QUIN_TDM_TX_0", "QUIN_TDM_TX_0"}, + {"MultiMedia5 Mixer", "QUIN_TDM_TX_1", "QUIN_TDM_TX_1"}, + {"MultiMedia5 Mixer", "QUIN_TDM_TX_2", "QUIN_TDM_TX_2"}, + {"MultiMedia5 Mixer", "QUIN_TDM_TX_3", "QUIN_TDM_TX_3"}, + {"MultiMedia5 Mixer", "WSA_CDC_DMA_TX_0", "WSA_CDC_DMA_TX_0"}, + {"MultiMedia5 Mixer", "WSA_CDC_DMA_TX_1", "WSA_CDC_DMA_TX_1"}, + {"MultiMedia5 Mixer", "WSA_CDC_DMA_TX_2", "WSA_CDC_DMA_TX_2"}, + {"MultiMedia5 Mixer", "VA_CDC_DMA_TX_0", "VA_CDC_DMA_TX_0"}, + {"MultiMedia5 Mixer", "VA_CDC_DMA_TX_1", "VA_CDC_DMA_TX_1"}, + {"MultiMedia5 Mixer", "TX_CDC_DMA_TX_0", "TX_CDC_DMA_TX_0"}, + {"MultiMedia5 Mixer", "TX_CDC_DMA_TX_1", "TX_CDC_DMA_TX_1"}, + {"MultiMedia5 Mixer", "TX_CDC_DMA_TX_2", "TX_CDC_DMA_TX_2"}, + {"MultiMedia5 Mixer", "TX_CDC_DMA_TX_3", "TX_CDC_DMA_TX_3"}, + {"MultiMedia5 Mixer", "TX_CDC_DMA_TX_4", "TX_CDC_DMA_TX_4"}, + {"MultiMedia5 Mixer", "TX_CDC_DMA_TX_5", "TX_CDC_DMA_TX_5"}, + {"MultiMedia5 Mixer", "PRI_SPDIF_TX", "PRI_SPDIF_TX"}, + {"MultiMedia5 Mixer", "SEC_SPDIF_TX", "SEC_SPDIF_TX"}, + + {"MultiMedia6 Mixer", "PRI_TDM_TX_0", "PRI_TDM_TX_0"}, + {"MultiMedia6 Mixer", "PRI_TDM_TX_1", "PRI_TDM_TX_1"}, + {"MultiMedia6 Mixer", "PRI_TDM_TX_2", "PRI_TDM_TX_2"}, + {"MultiMedia6 Mixer", "PRI_TDM_TX_3", "PRI_TDM_TX_3"}, + {"MultiMedia6 Mixer", "SEC_TDM_TX_0", "SEC_TDM_TX_0"}, + {"MultiMedia6 Mixer", "SEC_TDM_TX_1", "SEC_TDM_TX_1"}, + {"MultiMedia6 Mixer", "SEC_TDM_TX_2", "SEC_TDM_TX_2"}, + {"MultiMedia6 Mixer", "SEC_TDM_TX_3", "SEC_TDM_TX_3"}, + {"MultiMedia6 Mixer", "TERT_TDM_TX_0", "TERT_TDM_TX_0"}, + {"MultiMedia6 Mixer", "TERT_TDM_TX_1", "TERT_TDM_TX_1"}, + {"MultiMedia6 Mixer", "TERT_TDM_TX_2", "TERT_TDM_TX_2"}, + {"MultiMedia6 Mixer", "TERT_TDM_TX_3", "TERT_TDM_TX_3"}, + {"MultiMedia6 Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"}, + {"MultiMedia6 Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"}, + {"MultiMedia6 Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"}, + {"MultiMedia6 Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"}, + {"MultiMedia6 Mixer", "QUIN_TDM_TX_0", "QUIN_TDM_TX_0"}, + {"MultiMedia6 Mixer", "QUIN_TDM_TX_1", "QUIN_TDM_TX_1"}, + {"MultiMedia6 Mixer", "QUIN_TDM_TX_2", "QUIN_TDM_TX_2"}, + {"MultiMedia6 Mixer", "QUIN_TDM_TX_3", "QUIN_TDM_TX_3"}, + {"MultiMedia6 Mixer", "WSA_CDC_DMA_TX_0", "WSA_CDC_DMA_TX_0"}, + {"MultiMedia6 Mixer", "WSA_CDC_DMA_TX_1", "WSA_CDC_DMA_TX_1"}, + {"MultiMedia6 Mixer", "WSA_CDC_DMA_TX_2", "WSA_CDC_DMA_TX_2"}, + {"MultiMedia6 Mixer", "VA_CDC_DMA_TX_0", "VA_CDC_DMA_TX_0"}, + {"MultiMedia6 Mixer", "VA_CDC_DMA_TX_1", "VA_CDC_DMA_TX_1"}, + {"MultiMedia6 Mixer", "TX_CDC_DMA_TX_0", "TX_CDC_DMA_TX_0"}, + {"MultiMedia6 Mixer", "TX_CDC_DMA_TX_1", "TX_CDC_DMA_TX_1"}, + {"MultiMedia6 Mixer", "TX_CDC_DMA_TX_2", "TX_CDC_DMA_TX_2"}, + {"MultiMedia6 Mixer", "TX_CDC_DMA_TX_3", "TX_CDC_DMA_TX_3"}, + {"MultiMedia6 Mixer", "TX_CDC_DMA_TX_4", "TX_CDC_DMA_TX_4"}, + {"MultiMedia6 Mixer", "TX_CDC_DMA_TX_5", "TX_CDC_DMA_TX_5"}, + {"MultiMedia6 Mixer", "PRI_SPDIF_TX", "PRI_SPDIF_TX"}, + {"MultiMedia6 Mixer", "SEC_SPDIF_TX", "SEC_SPDIF_TX"}, + + {"MultiMedia8 Mixer", "PRI_TDM_TX_0", "PRI_TDM_TX_0"}, + {"MultiMedia8 Mixer", "PRI_TDM_TX_1", "PRI_TDM_TX_1"}, + {"MultiMedia8 Mixer", "PRI_TDM_TX_2", "PRI_TDM_TX_2"}, + {"MultiMedia8 Mixer", "PRI_TDM_TX_3", "PRI_TDM_TX_3"}, + {"MultiMedia8 Mixer", "SEC_TDM_TX_0", "SEC_TDM_TX_0"}, + {"MultiMedia8 Mixer", "SEC_TDM_TX_1", "SEC_TDM_TX_1"}, + {"MultiMedia8 Mixer", "SEC_TDM_TX_2", "SEC_TDM_TX_2"}, + {"MultiMedia8 Mixer", "SEC_TDM_TX_3", "SEC_TDM_TX_3"}, + {"MultiMedia8 Mixer", "TERT_TDM_TX_0", "TERT_TDM_TX_0"}, + {"MultiMedia8 Mixer", "TERT_TDM_TX_1", "TERT_TDM_TX_1"}, + {"MultiMedia8 Mixer", "TERT_TDM_TX_2", "TERT_TDM_TX_2"}, + {"MultiMedia8 Mixer", "TERT_TDM_TX_3", "TERT_TDM_TX_3"}, + {"MultiMedia8 Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"}, + {"MultiMedia8 Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"}, + {"MultiMedia8 Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"}, + {"MultiMedia8 Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"}, + {"MultiMedia8 Mixer", "QUIN_TDM_TX_0", "QUIN_TDM_TX_0"}, + {"MultiMedia8 Mixer", "QUIN_TDM_TX_1", "QUIN_TDM_TX_1"}, + {"MultiMedia8 Mixer", "QUIN_TDM_TX_2", "QUIN_TDM_TX_2"}, + {"MultiMedia8 Mixer", "QUIN_TDM_TX_3", "QUIN_TDM_TX_3"}, + {"MultiMedia8 Mixer", "WSA_CDC_DMA_TX_0", "WSA_CDC_DMA_TX_0"}, + {"MultiMedia8 Mixer", "WSA_CDC_DMA_TX_1", "WSA_CDC_DMA_TX_1"}, + {"MultiMedia8 Mixer", "WSA_CDC_DMA_TX_2", "WSA_CDC_DMA_TX_2"}, + {"MultiMedia8 Mixer", "VA_CDC_DMA_TX_0", "VA_CDC_DMA_TX_0"}, + {"MultiMedia8 Mixer", "VA_CDC_DMA_TX_1", "VA_CDC_DMA_TX_1"}, + {"MultiMedia8 Mixer", "TX_CDC_DMA_TX_0", "TX_CDC_DMA_TX_0"}, + {"MultiMedia8 Mixer", "TX_CDC_DMA_TX_1", "TX_CDC_DMA_TX_1"}, + {"MultiMedia8 Mixer", "TX_CDC_DMA_TX_2", "TX_CDC_DMA_TX_2"}, + {"MultiMedia8 Mixer", "TX_CDC_DMA_TX_3", "TX_CDC_DMA_TX_3"}, + {"MultiMedia8 Mixer", "TX_CDC_DMA_TX_4", "TX_CDC_DMA_TX_4"}, + {"MultiMedia8 Mixer", "TX_CDC_DMA_TX_5", "TX_CDC_DMA_TX_5"}, + {"MultiMedia8 Mixer", "PRI_SPDIF_TX", "PRI_SPDIF_TX"}, + {"MultiMedia8 Mixer", "SEC_SPDIF_TX", "SEC_SPDIF_TX"}, + + {"MultiMedia9 Mixer", "TERT_TDM_TX_0", "TERT_TDM_TX_0"}, + {"MultiMedia9 Mixer", "TERT_TDM_TX_1", "TERT_TDM_TX_1"}, + {"MultiMedia9 Mixer", "TERT_TDM_TX_2", "TERT_TDM_TX_2"}, + {"MultiMedia9 Mixer", "TERT_TDM_TX_3", "TERT_TDM_TX_3"}, + {"MultiMedia9 Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"}, + {"MultiMedia9 Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"}, + {"MultiMedia9 Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"}, + {"MultiMedia9 Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"}, + {"MultiMedia9 Mixer", "QUIN_TDM_TX_0", "QUIN_TDM_TX_0"}, + {"MultiMedia9 Mixer", "QUIN_TDM_TX_1", "QUIN_TDM_TX_1"}, + {"MultiMedia9 Mixer", "QUIN_TDM_TX_2", "QUIN_TDM_TX_2"}, + {"MultiMedia9 Mixer", "QUIN_TDM_TX_3", "QUIN_TDM_TX_3"}, + {"MultiMedia9 Mixer", "WSA_CDC_DMA_TX_0", "WSA_CDC_DMA_TX_0"}, + {"MultiMedia9 Mixer", "WSA_CDC_DMA_TX_1", "WSA_CDC_DMA_TX_1"}, + {"MultiMedia9 Mixer", "WSA_CDC_DMA_TX_2", "WSA_CDC_DMA_TX_2"}, + {"MultiMedia9 Mixer", "VA_CDC_DMA_TX_0", "VA_CDC_DMA_TX_0"}, + {"MultiMedia9 Mixer", "VA_CDC_DMA_TX_1", "VA_CDC_DMA_TX_1"}, + {"MultiMedia9 Mixer", "TX_CDC_DMA_TX_0", "TX_CDC_DMA_TX_0"}, + {"MultiMedia9 Mixer", "TX_CDC_DMA_TX_1", "TX_CDC_DMA_TX_1"}, + {"MultiMedia9 Mixer", "TX_CDC_DMA_TX_2", "TX_CDC_DMA_TX_2"}, + {"MultiMedia9 Mixer", "TX_CDC_DMA_TX_3", "TX_CDC_DMA_TX_3"}, + {"MultiMedia9 Mixer", "TX_CDC_DMA_TX_4", "TX_CDC_DMA_TX_4"}, + {"MultiMedia9 Mixer", "TX_CDC_DMA_TX_5", "TX_CDC_DMA_TX_5"}, + {"MultiMedia9 Mixer", "PRI_SPDIF_TX", "PRI_SPDIF_TX"}, + {"MultiMedia9 Mixer", "SEC_SPDIF_TX", "SEC_SPDIF_TX"}, + + {"MultiMedia10 Mixer", "TERT_TDM_TX_0", "TERT_TDM_TX_0"}, + {"MultiMedia10 Mixer", "TERT_TDM_TX_1", "TERT_TDM_TX_1"}, + {"MultiMedia10 Mixer", "TERT_TDM_TX_2", "TERT_TDM_TX_2"}, + {"MultiMedia10 Mixer", "TERT_TDM_TX_3", "TERT_TDM_TX_3"}, + {"MultiMedia10 Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"}, + {"MultiMedia10 Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"}, + {"MultiMedia10 Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"}, + {"MultiMedia10 Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"}, + {"MultiMedia10 Mixer", "TX_CDC_DMA_TX_0", "TX_CDC_DMA_TX_0"}, + {"MultiMedia10 Mixer", "TX_CDC_DMA_TX_1", "TX_CDC_DMA_TX_1"}, + {"MultiMedia10 Mixer", "TX_CDC_DMA_TX_2", "TX_CDC_DMA_TX_2"}, + {"MultiMedia10 Mixer", "TX_CDC_DMA_TX_3", "TX_CDC_DMA_TX_3"}, + {"MultiMedia10 Mixer", "TX_CDC_DMA_TX_4", "TX_CDC_DMA_TX_4"}, + {"MultiMedia10 Mixer", "TX_CDC_DMA_TX_5", "TX_CDC_DMA_TX_5"}, + + {"MultiMedia20 Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"MultiMedia20 Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"}, + {"MultiMedia20 Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"}, + {"MultiMedia20 Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + {"MultiMedia20 Mixer", "QUIN_MI2S_TX", "QUIN_MI2S_TX"}, + {"MultiMedia20 Mixer", "PRI_TDM_TX_0", "PRI_TDM_TX_0"}, + {"MultiMedia20 Mixer", "PRI_TDM_TX_1", "PRI_TDM_TX_1"}, + {"MultiMedia20 Mixer", "PRI_TDM_TX_2", "PRI_TDM_TX_2"}, + {"MultiMedia20 Mixer", "PRI_TDM_TX_3", "PRI_TDM_TX_3"}, + {"MultiMedia20 Mixer", "SEC_TDM_TX_0", "SEC_TDM_TX_0"}, + {"MultiMedia20 Mixer", "SEC_TDM_TX_1", "SEC_TDM_TX_1"}, + {"MultiMedia20 Mixer", "SEC_TDM_TX_2", "SEC_TDM_TX_2"}, + {"MultiMedia20 Mixer", "SEC_TDM_TX_3", "SEC_TDM_TX_3"}, + {"MultiMedia20 Mixer", "TERT_TDM_TX_0", "TERT_TDM_TX_0"}, + {"MultiMedia20 Mixer", "TERT_TDM_TX_1", "TERT_TDM_TX_1"}, + {"MultiMedia20 Mixer", "TERT_TDM_TX_2", "TERT_TDM_TX_2"}, + {"MultiMedia20 Mixer", "TERT_TDM_TX_3", "TERT_TDM_TX_3"}, + {"MultiMedia20 Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"}, + {"MultiMedia20 Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"}, + {"MultiMedia20 Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"}, + {"MultiMedia20 Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"}, + {"MultiMedia20 Mixer", "QUIN_TDM_TX_0", "QUIN_TDM_TX_0"}, + {"MultiMedia20 Mixer", "QUIN_TDM_TX_1", "QUIN_TDM_TX_1"}, + {"MultiMedia20 Mixer", "QUIN_TDM_TX_2", "QUIN_TDM_TX_2"}, + {"MultiMedia20 Mixer", "QUIN_TDM_TX_3", "QUIN_TDM_TX_3"}, + {"MultiMedia20 Mixer", "WSA_CDC_DMA_TX_0", "WSA_CDC_DMA_TX_0"}, + {"MultiMedia20 Mixer", "WSA_CDC_DMA_TX_1", "WSA_CDC_DMA_TX_1"}, + {"MultiMedia20 Mixer", "WSA_CDC_DMA_TX_2", "WSA_CDC_DMA_TX_2"}, + {"MultiMedia20 Mixer", "VA_CDC_DMA_TX_0", "VA_CDC_DMA_TX_0"}, + {"MultiMedia20 Mixer", "VA_CDC_DMA_TX_1", "VA_CDC_DMA_TX_1"}, + {"MultiMedia20 Mixer", "TX_CDC_DMA_TX_0", "TX_CDC_DMA_TX_0"}, + {"MultiMedia20 Mixer", "TX_CDC_DMA_TX_1", "TX_CDC_DMA_TX_1"}, + {"MultiMedia20 Mixer", "TX_CDC_DMA_TX_2", "TX_CDC_DMA_TX_2"}, + {"MultiMedia20 Mixer", "TX_CDC_DMA_TX_3", "TX_CDC_DMA_TX_3"}, + {"MultiMedia20 Mixer", "TX_CDC_DMA_TX_4", "TX_CDC_DMA_TX_4"}, + {"MultiMedia20 Mixer", "TX_CDC_DMA_TX_5", "TX_CDC_DMA_TX_5"}, + {"MultiMedia20 Mixer", "PRI_SPDIF_TX", "PRI_SPDIF_TX"}, + {"MultiMedia20 Mixer", "SEC_SPDIF_TX", "SEC_SPDIF_TX"}, + + {"MultiMedia21 Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"}, + {"MultiMedia21 Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"}, + {"MultiMedia21 Mixer", "PRI_TDM_TX_0", "PRI_TDM_TX_0"}, + {"MultiMedia21 Mixer", "PRI_TDM_TX_1", "PRI_TDM_TX_1"}, + {"MultiMedia21 Mixer", "PRI_TDM_TX_2", "PRI_TDM_TX_2"}, + {"MultiMedia21 Mixer", "PRI_TDM_TX_3", "PRI_TDM_TX_3"}, + {"MultiMedia21 Mixer", "SEC_TDM_TX_0", "SEC_TDM_TX_0"}, + {"MultiMedia21 Mixer", "SEC_TDM_TX_1", "SEC_TDM_TX_1"}, + {"MultiMedia21 Mixer", "SEC_TDM_TX_2", "SEC_TDM_TX_2"}, + {"MultiMedia21 Mixer", "SEC_TDM_TX_3", "SEC_TDM_TX_3"}, + {"MultiMedia21 Mixer", "TERT_TDM_TX_0", "TERT_TDM_TX_0"}, + {"MultiMedia21 Mixer", "TERT_TDM_TX_1", "TERT_TDM_TX_1"}, + {"MultiMedia21 Mixer", "TERT_TDM_TX_2", "TERT_TDM_TX_2"}, + {"MultiMedia21 Mixer", "TERT_TDM_TX_3", "TERT_TDM_TX_3"}, + {"MultiMedia21 Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"}, + {"MultiMedia21 Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"}, + {"MultiMedia21 Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"}, + {"MultiMedia21 Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"}, + {"MultiMedia21 Mixer", "QUIN_TDM_TX_0", "QUIN_TDM_TX_0"}, + {"MultiMedia21 Mixer", "QUIN_TDM_TX_1", "QUIN_TDM_TX_1"}, + {"MultiMedia21 Mixer", "QUIN_TDM_TX_2", "QUIN_TDM_TX_2"}, + {"MultiMedia21 Mixer", "QUIN_TDM_TX_3", "QUIN_TDM_TX_3"}, + {"MultiMedia21 Mixer", "WSA_CDC_DMA_TX_0", "WSA_CDC_DMA_TX_0"}, + {"MultiMedia21 Mixer", "WSA_CDC_DMA_TX_1", "WSA_CDC_DMA_TX_1"}, + {"MultiMedia21 Mixer", "WSA_CDC_DMA_TX_2", "WSA_CDC_DMA_TX_2"}, + {"MultiMedia21 Mixer", "VA_CDC_DMA_TX_0", "VA_CDC_DMA_TX_0"}, + {"MultiMedia21 Mixer", "VA_CDC_DMA_TX_1", "VA_CDC_DMA_TX_1"}, + {"MultiMedia21 Mixer", "TX_CDC_DMA_TX_0", "TX_CDC_DMA_TX_0"}, + {"MultiMedia21 Mixer", "TX_CDC_DMA_TX_1", "TX_CDC_DMA_TX_1"}, + {"MultiMedia21 Mixer", "TX_CDC_DMA_TX_2", "TX_CDC_DMA_TX_2"}, + {"MultiMedia21 Mixer", "TX_CDC_DMA_TX_3", "TX_CDC_DMA_TX_3"}, + {"MultiMedia21 Mixer", "TX_CDC_DMA_TX_4", "TX_CDC_DMA_TX_4"}, + {"MultiMedia21 Mixer", "TX_CDC_DMA_TX_5", "TX_CDC_DMA_TX_5"}, + {"MultiMedia21 Mixer", "PRI_SPDIF_TX", "PRI_SPDIF_TX"}, + {"MultiMedia21 Mixer", "SEC_SPDIF_TX", "SEC_SPDIF_TX"}, + + {"MultiMedia27 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"}, + {"MultiMedia27 Mixer", "SLIM_6_TX", "SLIMBUS_6_TX"}, + {"MultiMedia27 Mixer", "SLIM_7_TX", "SLIMBUS_7_TX"}, + {"MultiMedia27 Mixer", "SLIM_9_TX", "SLIMBUS_9_TX"}, + {"MultiMedia27 Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"MultiMedia27 Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"}, + {"MultiMedia27 Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"}, + {"MultiMedia27 Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + {"MultiMedia27 Mixer", "QUIN_MI2S_TX", "QUIN_MI2S_TX"}, + {"MultiMedia27 Mixer", "PRI_SPDIF_TX", "PRI_SPDIF_TX"}, + {"MultiMedia27 Mixer", "SEC_SPDIF_TX", "SEC_SPDIF_TX"}, + + {"MultiMedia1 Mixer", "USB_AUDIO_TX", "USB_AUDIO_TX"}, + {"MultiMedia2 Mixer", "USB_AUDIO_TX", "USB_AUDIO_TX"}, + {"MultiMedia4 Mixer", "USB_AUDIO_TX", "USB_AUDIO_TX"}, + {"MultiMedia5 Mixer", "USB_AUDIO_TX", "USB_AUDIO_TX"}, + {"MultiMedia6 Mixer", "USB_AUDIO_TX", "USB_AUDIO_TX"}, + {"MultiMedia8 Mixer", "USB_AUDIO_TX", "USB_AUDIO_TX"}, + {"MultiMedia10 Mixer", "USB_AUDIO_TX", "USB_AUDIO_TX"}, + + {"MultiMedia16 Mixer", "PRI_TDM_TX_0", "PRI_TDM_TX_0"}, + {"MultiMedia16 Mixer", "PRI_TDM_TX_1", "PRI_TDM_TX_1"}, + {"MultiMedia16 Mixer", "PRI_TDM_TX_2", "PRI_TDM_TX_2"}, + {"MultiMedia16 Mixer", "PRI_TDM_TX_3", "PRI_TDM_TX_3"}, + {"MultiMedia16 Mixer", "SEC_TDM_TX_0", "SEC_TDM_TX_0"}, + {"MultiMedia16 Mixer", "SEC_TDM_TX_1", "SEC_TDM_TX_1"}, + {"MultiMedia16 Mixer", "SEC_TDM_TX_2", "SEC_TDM_TX_2"}, + {"MultiMedia16 Mixer", "SEC_TDM_TX_3", "SEC_TDM_TX_3"}, + {"MultiMedia16 Mixer", "TERT_TDM_TX_0", "TERT_TDM_TX_0"}, + {"MultiMedia16 Mixer", "TERT_TDM_TX_1", "TERT_TDM_TX_1"}, + {"MultiMedia16 Mixer", "TERT_TDM_TX_2", "TERT_TDM_TX_2"}, + {"MultiMedia16 Mixer", "TERT_TDM_TX_3", "TERT_TDM_TX_3"}, + {"MultiMedia16 Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"}, + {"MultiMedia16 Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"}, + {"MultiMedia16 Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"}, + {"MultiMedia16 Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"}, + {"MultiMedia16 Mixer", "USB_AUDIO_TX", "USB_AUDIO_TX"}, + {"MultiMedia16 Mixer", "WSA_CDC_DMA_TX_0", "WSA_CDC_DMA_TX_0"}, + {"MultiMedia16 Mixer", "WSA_CDC_DMA_TX_1", "WSA_CDC_DMA_TX_1"}, + {"MultiMedia16 Mixer", "WSA_CDC_DMA_TX_2", "WSA_CDC_DMA_TX_2"}, + {"MultiMedia16 Mixer", "VA_CDC_DMA_TX_0", "VA_CDC_DMA_TX_0"}, + {"MultiMedia16 Mixer", "VA_CDC_DMA_TX_1", "VA_CDC_DMA_TX_1"}, + {"MultiMedia16 Mixer", "TX_CDC_DMA_TX_0", "TX_CDC_DMA_TX_0"}, + {"MultiMedia16 Mixer", "TX_CDC_DMA_TX_1", "TX_CDC_DMA_TX_1"}, + {"MultiMedia16 Mixer", "TX_CDC_DMA_TX_2", "TX_CDC_DMA_TX_2"}, + {"MultiMedia16 Mixer", "TX_CDC_DMA_TX_3", "TX_CDC_DMA_TX_3"}, + {"MultiMedia16 Mixer", "TX_CDC_DMA_TX_4", "TX_CDC_DMA_TX_4"}, + {"MultiMedia16 Mixer", "TX_CDC_DMA_TX_5", "TX_CDC_DMA_TX_5"}, + {"MultiMedia16 Mixer", "PRI_SPDIF_TX", "PRI_SPDIF_TX"}, + {"MultiMedia16 Mixer", "SEC_SPDIF_TX", "SEC_SPDIF_TX"}, + + {"MultiMedia17 Mixer", "TX_CDC_DMA_TX_0", "TX_CDC_DMA_TX_0"}, + {"MultiMedia17 Mixer", "TX_CDC_DMA_TX_1", "TX_CDC_DMA_TX_1"}, + {"MultiMedia17 Mixer", "TX_CDC_DMA_TX_2", "TX_CDC_DMA_TX_2"}, + {"MultiMedia17 Mixer", "TX_CDC_DMA_TX_3", "TX_CDC_DMA_TX_3"}, + {"MultiMedia17 Mixer", "TX_CDC_DMA_TX_4", "TX_CDC_DMA_TX_4"}, + {"MultiMedia17 Mixer", "TX_CDC_DMA_TX_5", "TX_CDC_DMA_TX_5"}, + + {"MultiMedia18 Mixer", "TX_CDC_DMA_TX_0", "TX_CDC_DMA_TX_0"}, + {"MultiMedia18 Mixer", "TX_CDC_DMA_TX_1", "TX_CDC_DMA_TX_1"}, + {"MultiMedia18 Mixer", "TX_CDC_DMA_TX_2", "TX_CDC_DMA_TX_2"}, + {"MultiMedia18 Mixer", "TX_CDC_DMA_TX_3", "TX_CDC_DMA_TX_3"}, + {"MultiMedia18 Mixer", "TX_CDC_DMA_TX_4", "TX_CDC_DMA_TX_4"}, + {"MultiMedia18 Mixer", "TX_CDC_DMA_TX_5", "TX_CDC_DMA_TX_5"}, + + {"MultiMedia19 Mixer", "TX_CDC_DMA_TX_0", "TX_CDC_DMA_TX_0"}, + {"MultiMedia19 Mixer", "TX_CDC_DMA_TX_1", "TX_CDC_DMA_TX_1"}, + {"MultiMedia19 Mixer", "TX_CDC_DMA_TX_2", "TX_CDC_DMA_TX_2"}, + {"MultiMedia19 Mixer", "TX_CDC_DMA_TX_3", "TX_CDC_DMA_TX_3"}, + {"MultiMedia19 Mixer", "TX_CDC_DMA_TX_4", "TX_CDC_DMA_TX_4"}, + {"MultiMedia19 Mixer", "TX_CDC_DMA_TX_5", "TX_CDC_DMA_TX_5"}, + + {"MultiMedia28 Mixer", "TX_CDC_DMA_TX_0", "TX_CDC_DMA_TX_0"}, + {"MultiMedia28 Mixer", "TX_CDC_DMA_TX_1", "TX_CDC_DMA_TX_1"}, + {"MultiMedia28 Mixer", "TX_CDC_DMA_TX_2", "TX_CDC_DMA_TX_2"}, + {"MultiMedia28 Mixer", "TX_CDC_DMA_TX_3", "TX_CDC_DMA_TX_3"}, + {"MultiMedia28 Mixer", "TX_CDC_DMA_TX_4", "TX_CDC_DMA_TX_4"}, + {"MultiMedia28 Mixer", "TX_CDC_DMA_TX_5", "TX_CDC_DMA_TX_5"}, + + {"MultiMedia29 Mixer", "TX_CDC_DMA_TX_0", "TX_CDC_DMA_TX_0"}, + {"MultiMedia29 Mixer", "TX_CDC_DMA_TX_1", "TX_CDC_DMA_TX_1"}, + {"MultiMedia29 Mixer", "TX_CDC_DMA_TX_2", "TX_CDC_DMA_TX_2"}, + {"MultiMedia29 Mixer", "TX_CDC_DMA_TX_3", "TX_CDC_DMA_TX_3"}, + {"MultiMedia29 Mixer", "TX_CDC_DMA_TX_4", "TX_CDC_DMA_TX_4"}, + {"MultiMedia29 Mixer", "TX_CDC_DMA_TX_5", "TX_CDC_DMA_TX_5"}, + + {"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia9", "MM_DL9"}, + {"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia6", "MM_UL6"}, + {"INT_BT_SCO_RX", NULL, "INTERNAL_BT_SCO_RX Audio Mixer"}, + + {"INTERNAL_A2DP_RX Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"INTERNAL_A2DP_RX Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"INTERNAL_A2DP_RX Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"INTERNAL_A2DP_RX Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"INTERNAL_A2DP_RX Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"INTERNAL_A2DP_RX Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"INTERNAL_A2DP_RX Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"INTERNAL_A2DP_RX Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"INTERNAL_A2DP_RX Audio Mixer", "MultiMedia9", "MM_DL9"}, + {"INTERNAL_A2DP_RX Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"INTERNAL_A2DP_RX Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"INTERNAL_A2DP_RX Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"INTERNAL_A2DP_RX Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"INTERNAL_A2DP_RX Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"INTERNAL_A2DP_RX Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"INTERNAL_A2DP_RX Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"INTERNAL_A2DP_RX Audio Mixer", "MultiMedia6", "MM_UL6"}, + {"INT_BT_A2DP_RX", NULL, "INTERNAL_A2DP_RX Audio Mixer"}, + + {"INTERNAL_FM_RX Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"INTERNAL_FM_RX Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"INTERNAL_FM_RX Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"INTERNAL_FM_RX Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"INTERNAL_FM_RX Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"INTERNAL_FM_RX Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"INTERNAL_FM_RX Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"INTERNAL_FM_RX Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"INTERNAL_FM_RX Audio Mixer", "MultiMedia9", "MM_DL9"}, + {"INTERNAL_FM_RX Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"INTERNAL_FM_RX Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"INTERNAL_FM_RX Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"INTERNAL_FM_RX Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"INTERNAL_FM_RX Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"INTERNAL_FM_RX Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"INTERNAL_FM_RX Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"INT_FM_RX", NULL, "INTERNAL_FM_RX Audio Mixer"}, + + {"AFE_PCM_RX Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"AFE_PCM_RX Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"AFE_PCM_RX Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"AFE_PCM_RX Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"AFE_PCM_RX Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"AFE_PCM_RX Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"AFE_PCM_RX Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"AFE_PCM_RX Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"AFE_PCM_RX Audio Mixer", "MultiMedia9", "MM_DL9"}, + {"AFE_PCM_RX Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"AFE_PCM_RX Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"AFE_PCM_RX Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"AFE_PCM_RX Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"AFE_PCM_RX Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"AFE_PCM_RX Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"AFE_PCM_RX Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"PCM_RX", NULL, "AFE_PCM_RX Audio Mixer"}, + + {"MultiMedia1 Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"}, + {"MultiMedia3 Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"}, + {"MultiMedia4 Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"}, + {"MultiMedia10 Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"}, + {"MultiMedia17 Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"}, + {"MultiMedia18 Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"}, + {"MultiMedia19 Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"}, + {"MultiMedia28 Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"}, + {"MultiMedia29 Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"}, + {"MultiMedia5 Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"}, + {"MultiMedia8 Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"}, + {"MultiMedia16 Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"}, + {"MultiMedia1 Mixer", "INTERNAL_FM_TX", "INT_FM_TX"}, + {"MultiMedia4 Mixer", "INTERNAL_FM_TX", "INT_FM_TX"}, + {"MultiMedia16 Mixer", "INTERNAL_FM_TX", "INT_FM_TX"}, + {"MultiMedia17 Mixer", "INTERNAL_FM_TX", "INT_FM_TX"}, + {"MultiMedia18 Mixer", "INTERNAL_FM_TX", "INT_FM_TX"}, + {"MultiMedia19 Mixer", "INTERNAL_FM_TX", "INT_FM_TX"}, + {"MultiMedia28 Mixer", "INTERNAL_FM_TX", "INT_FM_TX"}, + {"MultiMedia29 Mixer", "INTERNAL_FM_TX", "INT_FM_TX"}, + {"MultiMedia5 Mixer", "INTERNAL_FM_TX", "INT_FM_TX"}, + {"MultiMedia6 Mixer", "INTERNAL_FM_TX", "INT_FM_TX"}, + {"MultiMedia8 Mixer", "INTERNAL_FM_TX", "INT_FM_TX"}, + + {"MultiMedia1 Mixer", "AFE_PCM_TX", "PCM_TX"}, + {"MultiMedia3 Mixer", "AFE_PCM_TX", "PCM_TX"}, + {"MultiMedia4 Mixer", "AFE_PCM_TX", "PCM_TX"}, + {"MultiMedia10 Mixer", "AFE_PCM_TX", "PCM_TX"}, + {"MultiMedia17 Mixer", "AFE_PCM_TX", "PCM_TX"}, + {"MultiMedia18 Mixer", "AFE_PCM_TX", "PCM_TX"}, + {"MultiMedia19 Mixer", "AFE_PCM_TX", "PCM_TX"}, + {"MultiMedia28 Mixer", "AFE_PCM_TX", "PCM_TX"}, + {"MultiMedia29 Mixer", "AFE_PCM_TX", "PCM_TX"}, + {"MultiMedia5 Mixer", "AFE_PCM_TX", "PCM_TX"}, + {"MultiMedia8 Mixer", "AFE_PCM_TX", "PCM_TX"}, + {"MultiMedia16 Mixer", "AFE_PCM_TX", "PCM_TX"}, + {"MM_UL1", NULL, "MultiMedia1 Mixer"}, + {"MultiMedia2 Mixer", "INTERNAL_FM_TX", "INT_FM_TX"}, + {"MM_UL2", NULL, "MultiMedia2 Mixer"}, + {"MM_UL3", NULL, "MultiMedia3 Mixer"}, + {"MM_UL4", NULL, "MultiMedia4 Mixer"}, + {"MM_UL5", NULL, "MultiMedia5 Mixer"}, + {"MM_UL6", NULL, "MultiMedia6 Mixer"}, + {"MM_UL8", NULL, "MultiMedia8 Mixer"}, + {"MM_UL9", NULL, "MultiMedia9 Mixer"}, + {"MM_UL10", NULL, "MultiMedia10 Mixer"}, + {"MM_UL16", NULL, "MultiMedia16 Mixer"}, + {"MM_UL17", NULL, "MultiMedia17 Mixer"}, + {"MM_UL18", NULL, "MultiMedia18 Mixer"}, + {"MM_UL19", NULL, "MultiMedia19 Mixer"}, + {"MM_UL20", NULL, "MultiMedia20 Mixer"}, + {"MM_UL21", NULL, "MultiMedia21 Mixer"}, + {"MM_UL27", NULL, "MultiMedia27 Mixer"}, + {"MM_UL28", NULL, "MultiMedia28 Mixer"}, + {"MM_UL29", NULL, "MultiMedia29 Mixer"}, + + {"AUX_PCM_RX Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"AUX_PCM_RX Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"AUX_PCM_RX Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"AUX_PCM_RX Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"AUX_PCM_RX Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"AUX_PCM_RX Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"AUX_PCM_RX Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"AUX_PCM_RX Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"AUX_PCM_RX Audio Mixer", "MultiMedia9", "MM_DL9"}, + {"AUX_PCM_RX Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"AUX_PCM_RX Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"AUX_PCM_RX Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"AUX_PCM_RX Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"AUX_PCM_RX Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"AUX_PCM_RX Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"AUX_PCM_RX Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"AUX_PCM_RX Audio Mixer", "MultiMedia21", "MM_DL21"}, + {"AUX_PCM_RX", NULL, "AUX_PCM_RX Audio Mixer"}, + + {"SEC_AUX_PCM_RX Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"SEC_AUX_PCM_RX Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"SEC_AUX_PCM_RX Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"SEC_AUX_PCM_RX Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"SEC_AUX_PCM_RX Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"SEC_AUX_PCM_RX Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"SEC_AUX_PCM_RX Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"SEC_AUX_PCM_RX Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"SEC_AUX_PCM_RX Audio Mixer", "MultiMedia9", "MM_DL9"}, + {"SEC_AUX_PCM_RX Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"SEC_AUX_PCM_RX Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"SEC_AUX_PCM_RX Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"SEC_AUX_PCM_RX Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"SEC_AUX_PCM_RX Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"SEC_AUX_PCM_RX Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"SEC_AUX_PCM_RX Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"SEC_AUX_PCM_RX Audio Mixer", "MultiMedia21", "MM_DL21"}, + {"SEC_AUX_PCM_RX", NULL, "SEC_AUX_PCM_RX Audio Mixer"}, + + {"TERT_AUX_PCM_RX Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"TERT_AUX_PCM_RX Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"TERT_AUX_PCM_RX Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"TERT_AUX_PCM_RX Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"TERT_AUX_PCM_RX Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"TERT_AUX_PCM_RX Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"TERT_AUX_PCM_RX Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"TERT_AUX_PCM_RX Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"TERT_AUX_PCM_RX Audio Mixer", "MultiMedia9", "MM_DL9"}, + {"TERT_AUX_PCM_RX Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"TERT_AUX_PCM_RX Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"TERT_AUX_PCM_RX Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"TERT_AUX_PCM_RX Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"TERT_AUX_PCM_RX Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"TERT_AUX_PCM_RX Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"TERT_AUX_PCM_RX Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"TERT_AUX_PCM_RX", NULL, "TERT_AUX_PCM_RX Audio Mixer"}, + + {"QUAT_AUX_PCM_RX Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"QUAT_AUX_PCM_RX Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"QUAT_AUX_PCM_RX Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"QUAT_AUX_PCM_RX Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"QUAT_AUX_PCM_RX Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"QUAT_AUX_PCM_RX Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"QUAT_AUX_PCM_RX Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"QUAT_AUX_PCM_RX Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"QUAT_AUX_PCM_RX Audio Mixer", "MultiMedia9", "MM_DL9"}, + {"QUAT_AUX_PCM_RX Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"QUAT_AUX_PCM_RX Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"QUAT_AUX_PCM_RX Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"QUAT_AUX_PCM_RX Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"QUAT_AUX_PCM_RX Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"QUAT_AUX_PCM_RX Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"QUAT_AUX_PCM_RX Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"QUAT_AUX_PCM_RX", NULL, "QUAT_AUX_PCM_RX Audio Mixer"}, + + {"QUIN_AUX_PCM_RX Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"QUIN_AUX_PCM_RX Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"QUIN_AUX_PCM_RX Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"QUIN_AUX_PCM_RX Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"QUIN_AUX_PCM_RX Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"QUIN_AUX_PCM_RX Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"QUIN_AUX_PCM_RX Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"QUIN_AUX_PCM_RX Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"QUIN_AUX_PCM_RX Audio Mixer", "MultiMedia9", "MM_DL9"}, + {"QUIN_AUX_PCM_RX Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"QUIN_AUX_PCM_RX Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"QUIN_AUX_PCM_RX Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"QUIN_AUX_PCM_RX Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"QUIN_AUX_PCM_RX Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"QUIN_AUX_PCM_RX Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"QUIN_AUX_PCM_RX Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"QUIN_AUX_PCM_RX", NULL, "QUIN_AUX_PCM_RX Audio Mixer"}, + + {"PRI_RX_Voice Mixer", "Voip", "VOIP_DL"}, + {"PRI_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"}, + {"PRI_RX_Voice Mixer", "QCHAT", "QCHAT_DL"}, + {"PRI_RX_Voice Mixer", "VoiceMMode1", "VOICEMMODE1_DL"}, + {"PRI_RX_Voice Mixer", "VoiceMMode2", "VOICEMMODE2_DL"}, + {"PRI_I2S_RX", NULL, "PRI_RX_Voice Mixer"}, + + {"SEC_RX_Voice Mixer", "Voip", "VOIP_DL"}, + {"SEC_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"}, + {"SEC_RX_Voice Mixer", "QCHAT", "QCHAT_DL"}, + {"SEC_I2S_RX", NULL, "SEC_RX_Voice Mixer"}, + + {"SEC_MI2S_RX_Voice Mixer", "Voip", "VOIP_DL"}, + {"SEC_MI2S_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"}, + {"SEC_MI2S_RX_Voice Mixer", "QCHAT", "QCHAT_DL"}, + {"SEC_MI2S_RX_Voice Mixer", "VoiceMMode1", "VOICEMMODE1_DL"}, + {"SEC_MI2S_RX_Voice Mixer", "VoiceMMode2", "VOICEMMODE2_DL"}, + {"SEC_MI2S_RX", NULL, "SEC_MI2S_RX_Voice Mixer"}, + + {"SLIM_0_RX_Voice Mixer", "Voip", "VOIP_DL"}, + {"SLIM_0_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"}, + {"SLIM_0_RX_Voice Mixer", "QCHAT", "QCHAT_DL"}, + {"SLIM_0_RX_Voice Mixer", "VoiceMMode1", "VOICEMMODE1_DL"}, + {"SLIM_0_RX_Voice Mixer", "VoiceMMode2", "VOICEMMODE2_DL"}, + {"SLIMBUS_0_RX", NULL, "SLIM_0_RX_Voice Mixer"}, + + {"SLIM_6_RX_Voice Mixer", "Voip", "VOIP_DL"}, + {"SLIM_6_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"}, + {"SLIM_6_RX_Voice Mixer", "QCHAT", "QCHAT_DL"}, + {"SLIM_6_RX_Voice Mixer", "VoiceMMode1", "VOICEMMODE1_DL"}, + {"SLIM_6_RX_Voice Mixer", "VoiceMMode2", "VOICEMMODE2_DL"}, + {"SLIMBUS_6_RX", NULL, "SLIM_6_RX_Voice Mixer"}, + + {"USB_AUDIO_RX_Voice Mixer", "Voip", "VOIP_DL"}, + {"USB_AUDIO_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"}, + {"USB_AUDIO_RX_Voice Mixer", "QCHAT", "QCHAT_DL"}, + {"USB_AUDIO_RX_Voice Mixer", "VoiceMMode1", "VOICEMMODE1_DL"}, + {"USB_AUDIO_RX_Voice Mixer", "VoiceMMode2", "VOICEMMODE2_DL"}, + {"USB_AUDIO_RX", NULL, "USB_AUDIO_RX_Voice Mixer"}, + + {"DISPLAY_PORT_RX_Voice Mixer", "Voip", "VOIP_DL"}, + {"DISPLAY_PORT_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"}, + {"DISPLAY_PORT_RX_Voice Mixer", "QCHAT", "QCHAT_DL"}, + {"DISPLAY_PORT_RX_Voice Mixer", "VoiceMMode1", "VOICEMMODE1_DL"}, + {"DISPLAY_PORT_RX_Voice Mixer", "VoiceMMode2", "VOICEMMODE2_DL"}, + {"DISPLAY_PORT", NULL, "DISPLAY_PORT_RX_Voice Mixer"}, + + {"DISPLAY_PORT_RX1_Voice Mixer", "Voip", "VOIP_DL"}, + {"DISPLAY_PORT_RX1_Voice Mixer", "DTMF", "DTMF_DL_HL"}, + {"DISPLAY_PORT_RX1_Voice Mixer", "QCHAT", "QCHAT_DL"}, + {"DISPLAY_PORT_RX1_Voice Mixer", "VoiceMMode1", "VOICEMMODE1_DL"}, + {"DISPLAY_PORT_RX1_Voice Mixer", "VoiceMMode2", "VOICEMMODE2_DL"}, + {"DISPLAY_PORT1", NULL, "DISPLAY_PORT_RX1_Voice Mixer"}, + + {"INTERNAL_BT_SCO_RX_Voice Mixer", "Voip", "VOIP_DL"}, + {"INTERNAL_BT_SCO_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"}, + {"INTERNAL_BT_SCO_RX_Voice Mixer", "QCHAT", "QCHAT_DL"}, + {"INTERNAL_BT_SCO_RX_Voice Mixer", "VoiceMMode1", "VOICEMMODE1_DL"}, + {"INTERNAL_BT_SCO_RX_Voice Mixer", "VoiceMMode2", "VOICEMMODE2_DL"}, + {"INT_BT_SCO_RX", NULL, "INTERNAL_BT_SCO_RX_Voice Mixer"}, + + {"AFE_PCM_RX_Voice Mixer", "Voip", "VOIP_DL"}, + {"AFE_PCM_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"}, + {"AFE_PCM_RX_Voice Mixer", "QCHAT", "QCHAT_DL"}, + {"AFE_PCM_RX_Voice Mixer", "VoiceMMode1", "VOICEMMODE1_DL"}, + {"AFE_PCM_RX_Voice Mixer", "VoiceMMode2", "VOICEMMODE2_DL"}, + {"PCM_RX", NULL, "AFE_PCM_RX_Voice Mixer"}, + + {"AUX_PCM_RX_Voice Mixer", "Voip", "VOIP_DL"}, + {"AUX_PCM_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"}, + {"AUX_PCM_RX_Voice Mixer", "QCHAT", "QCHAT_DL"}, + {"AUX_PCM_RX_Voice Mixer", "VoiceMMode1", "VOICEMMODE1_DL"}, + {"AUX_PCM_RX_Voice Mixer", "VoiceMMode2", "VOICEMMODE2_DL"}, + {"AUX_PCM_RX", NULL, "AUX_PCM_RX_Voice Mixer"}, + + {"SEC_AUX_PCM_RX_Voice Mixer", "Voip", "VOIP_DL"}, + {"SEC_AUX_PCM_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"}, + {"SEC_AUX_PCM_RX_Voice Mixer", "QCHAT", "QCHAT_DL"}, + {"SEC_AUX_PCM_RX_Voice Mixer", "VoiceMMode1", "VOICEMMODE1_DL"}, + {"SEC_AUX_PCM_RX_Voice Mixer", "VoiceMMode2", "VOICEMMODE2_DL"}, + {"SEC_AUX_PCM_RX", NULL, "SEC_AUX_PCM_RX_Voice Mixer"}, + + {"TERT_AUX_PCM_RX_Voice Mixer", "Voip", "VOIP_DL"}, + {"TERT_AUX_PCM_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"}, + {"TERT_AUX_PCM_RX_Voice Mixer", "QCHAT", "QCHAT_DL"}, + {"TERT_AUX_PCM_RX_Voice Mixer", "VoiceMMode1", "VOICEMMODE1_DL"}, + {"TERT_AUX_PCM_RX_Voice Mixer", "VoiceMMode2", "VOICEMMODE2_DL"}, + {"TERT_AUX_PCM_RX", NULL, "TERT_AUX_PCM_RX_Voice Mixer"}, + + {"QUAT_AUX_PCM_RX_Voice Mixer", "Voip", "VOIP_DL"}, + {"QUAT_AUX_PCM_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"}, + {"QUAT_AUX_PCM_RX_Voice Mixer", "QCHAT", "QCHAT_DL"}, + {"QUAT_AUX_PCM_RX_Voice Mixer", "VoiceMMode1", "VOICEMMODE1_DL"}, + {"QUAT_AUX_PCM_RX_Voice Mixer", "VoiceMMode2", "VOICEMMODE2_DL"}, + {"QUAT_AUX_PCM_RX", NULL, "QUAT_AUX_PCM_RX_Voice Mixer"}, + + {"QUIN_AUX_PCM_RX_Voice Mixer", "Voip", "VOIP_DL"}, + {"QUIN_AUX_PCM_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"}, + {"QUIN_AUX_PCM_RX_Voice Mixer", "QCHAT", "QCHAT_DL"}, + {"QUIN_AUX_PCM_RX_Voice Mixer", "VoiceMMode1", "VOICEMMODE1_DL"}, + {"QUIN_AUX_PCM_RX_Voice Mixer", "VoiceMMode2", "VOICEMMODE2_DL"}, + {"QUIN_AUX_PCM_RX", NULL, "QUIN_AUX_PCM_RX_Voice Mixer"}, + + {"HDMI_RX_Voice Mixer", "Voip", "VOIP_DL"}, + {"HDMI_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"}, + {"HDMI_RX_Voice Mixer", "QCHAT", "QCHAT_DL"}, + {"HDMI_RX_Voice Mixer", "VoiceMMode1", "VOICEMMODE1_DL"}, + {"HDMI_RX_Voice Mixer", "VoiceMMode2", "VOICEMMODE2_DL"}, + {"HDMI", NULL, "HDMI_RX_Voice Mixer"}, + {"HDMI", NULL, "HDMI_DL_HL"}, + + {"MI2S_RX_Voice Mixer", "Voip", "VOIP_DL"}, + {"MI2S_RX_Voice Mixer", "QCHAT", "QCHAT_DL"}, + {"MI2S_RX_Voice Mixer", "VoiceMMode1", "VOICEMMODE1_DL"}, + {"MI2S_RX_Voice Mixer", "VoiceMMode2", "VOICEMMODE2_DL"}, + {"MI2S_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"}, + {"MI2S_RX", NULL, "MI2S_RX_Voice Mixer"}, + + {"PRI_MI2S_RX_Voice Mixer", "Voip", "VOIP_DL"}, + {"PRI_MI2S_RX_Voice Mixer", "QCHAT", "QCHAT_DL"}, + {"PRI_MI2S_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"}, + {"PRI_MI2S_RX_Voice Mixer", "VoiceMMode1", "VOICEMMODE1_DL"}, + {"PRI_MI2S_RX_Voice Mixer", "VoiceMMode2", "VOICEMMODE2_DL"}, + {"PRI_MI2S_RX", NULL, "PRI_MI2S_RX_Voice Mixer"}, + + {"INT0_MI2S_RX_Voice Mixer", "Voip", "VOIP_DL"}, + {"INT0_MI2S_RX_Voice Mixer", "QCHAT", "QCHAT_DL"}, + {"INT0_MI2S_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"}, + {"INT0_MI2S_RX_Voice Mixer", "VoiceMMode1", "VOICEMMODE1_DL"}, + {"INT0_MI2S_RX_Voice Mixer", "VoiceMMode2", "VOICEMMODE2_DL"}, + {"INT0_MI2S_RX", NULL, "INT0_MI2S_RX_Voice Mixer"}, + + {"INT4_MI2S_RX_Voice Mixer", "Voip", "VOIP_DL"}, + {"INT4_MI2S_RX_Voice Mixer", "QCHAT", "QCHAT_DL"}, + {"INT4_MI2S_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"}, + {"INT4_MI2S_RX_Voice Mixer", "VoiceMMode1", "VOICEMMODE1_DL"}, + {"INT4_MI2S_RX_Voice Mixer", "VoiceMMode2", "VOICEMMODE2_DL"}, + {"INT4_MI2S_RX", NULL, "INT4_MI2S_RX_Voice Mixer"}, + + {"TERT_MI2S_RX_Voice Mixer", "Voip", "VOIP_DL"}, + {"TERT_MI2S_RX_Voice Mixer", "QCHAT", "QCHAT_DL"}, + {"TERT_MI2S_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"}, + {"TERT_MI2S_RX_Voice Mixer", "VoiceMMode1", "VOICEMMODE1_DL"}, + {"TERT_MI2S_RX_Voice Mixer", "VoiceMMode2", "VOICEMMODE2_DL"}, + {"TERT_MI2S_RX", NULL, "TERT_MI2S_RX_Voice Mixer"}, + + {"QUAT_MI2S_RX_Voice Mixer", "Voip", "VOIP_DL"}, + {"QUAT_MI2S_RX_Voice Mixer", "QCHAT", "QCHAT_DL"}, + {"QUAT_MI2S_RX_Voice Mixer", "VoiceMMode1", "VOICEMMODE1_DL"}, + {"QUAT_MI2S_RX_Voice Mixer", "VoiceMMode2", "VOICEMMODE2_DL"}, + {"QUAT_MI2S_RX", NULL, "QUAT_MI2S_RX_Voice Mixer"}, + + {"QUIN_MI2S_RX_Voice Mixer", "Voip", "VOIP_DL"}, + {"QUIN_MI2S_RX_Voice Mixer", "QCHAT", "QCHAT_DL"}, + {"QUIN_MI2S_RX_Voice Mixer", "VoiceMMode1", "VOICEMMODE1_DL"}, + {"QUIN_MI2S_RX_Voice Mixer", "VoiceMMode2", "VOICEMMODE2_DL"}, + {"QUIN_MI2S_RX", NULL, "QUIN_MI2S_RX_Voice Mixer"}, + + {"QUAT_TDM_RX_2_Voice Mixer", "VoiceMMode1", "VOICEMMODE1_DL"}, + {"QUAT_TDM_RX_2", NULL, "QUAT_TDM_RX_2_Voice Mixer"}, + + {"WSA_CDC_DMA_RX_0_Voice Mixer", "VoiceMMode1", "VOICEMMODE1_DL"}, + {"WSA_CDC_DMA_RX_0_Voice Mixer", "VoiceMMode2", "VOICEMMODE2_DL"}, + {"WSA_CDC_DMA_RX_0", NULL, "WSA_CDC_DMA_RX_0_Voice Mixer"}, + + {"RX_CDC_DMA_RX_0_Voice Mixer", "VoiceMMode1", "VOICEMMODE1_DL"}, + {"RX_CDC_DMA_RX_0_Voice Mixer", "VoiceMMode2", "VOICEMMODE2_DL"}, + {"RX_CDC_DMA_RX_0", NULL, "RX_CDC_DMA_RX_0_Voice Mixer"}, + + {"VOC_EXT_EC MUX", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"VOC_EXT_EC MUX", "SEC_MI2S_TX", "SEC_MI2S_TX"}, + {"VOC_EXT_EC MUX", "TERT_MI2S_TX", "TERT_MI2S_TX"}, + {"VOC_EXT_EC MUX", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + {"VOC_EXT_EC MUX", "QUIN_MI2S_TX", "QUIN_MI2S_TX"}, + {"VOC_EXT_EC MUX", "SLIM_1_TX", "SLIMBUS_1_TX"}, + {"VOC_EXT_EC MUX", "SEC_TDM_TX", "SEC_TDM_TX_0"}, + {"VOIP_UL", NULL, "VOC_EXT_EC MUX"}, + {"VOICEMMODE1_UL", NULL, "VOC_EXT_EC MUX"}, + {"VOICEMMODE2_UL", NULL, "VOC_EXT_EC MUX"}, + + {"AUDIO_REF_EC_UL1 MUX", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"AUDIO_REF_EC_UL1 MUX", "SEC_MI2S_TX", "SEC_MI2S_TX"}, + {"AUDIO_REF_EC_UL1 MUX", "TERT_MI2S_TX", "TERT_MI2S_TX"}, + {"AUDIO_REF_EC_UL1 MUX", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + {"AUDIO_REF_EC_UL1 MUX", "SLIM_1_TX", "SLIMBUS_1_TX"}, + {"AUDIO_REF_EC_UL1 MUX", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"}, + {"AUDIO_REF_EC_UL1 MUX", "QUAT_TDM_RX_0", "QUAT_TDM_RX_0"}, + {"AUDIO_REF_EC_UL1 MUX", "QUAT_TDM_RX_1", "QUAT_TDM_RX_1"}, + {"AUDIO_REF_EC_UL1 MUX", "QUAT_TDM_RX_2", "QUAT_TDM_RX_2"}, + {"AUDIO_REF_EC_UL1 MUX", "TERT_TDM_TX_0", "TERT_TDM_TX_0"}, + {"AUDIO_REF_EC_UL1 MUX", "TERT_TDM_RX_2", "TERT_TDM_RX_2"}, + {"AUDIO_REF_EC_UL1 MUX", "SEC_TDM_TX_0", "SEC_TDM_TX_0"}, + + {"AUDIO_REF_EC_UL2 MUX", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"AUDIO_REF_EC_UL2 MUX", "SEC_MI2S_TX", "SEC_MI2S_TX"}, + {"AUDIO_REF_EC_UL2 MUX", "TERT_MI2S_TX", "TERT_MI2S_TX"}, + {"AUDIO_REF_EC_UL2 MUX", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + + {"AUDIO_REF_EC_UL3 MUX", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"AUDIO_REF_EC_UL3 MUX", "SEC_MI2S_TX", "SEC_MI2S_TX"}, + {"AUDIO_REF_EC_UL3 MUX", "TERT_MI2S_TX", "TERT_MI2S_TX"}, + {"AUDIO_REF_EC_UL3 MUX", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + + {"AUDIO_REF_EC_UL4 MUX", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"AUDIO_REF_EC_UL4 MUX", "SEC_MI2S_TX", "SEC_MI2S_TX"}, + {"AUDIO_REF_EC_UL4 MUX", "TERT_MI2S_TX", "TERT_MI2S_TX"}, + {"AUDIO_REF_EC_UL4 MUX", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + + {"AUDIO_REF_EC_UL5 MUX", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"AUDIO_REF_EC_UL5 MUX", "SEC_MI2S_TX", "SEC_MI2S_TX"}, + {"AUDIO_REF_EC_UL5 MUX", "TERT_MI2S_TX", "TERT_MI2S_TX"}, + {"AUDIO_REF_EC_UL5 MUX", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + + {"AUDIO_REF_EC_UL6 MUX", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"AUDIO_REF_EC_UL6 MUX", "SEC_MI2S_TX", "SEC_MI2S_TX"}, + {"AUDIO_REF_EC_UL6 MUX", "TERT_MI2S_TX", "TERT_MI2S_TX"}, + {"AUDIO_REF_EC_UL6 MUX", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + + {"AUDIO_REF_EC_UL8 MUX", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"AUDIO_REF_EC_UL8 MUX", "SEC_MI2S_TX", "SEC_MI2S_TX"}, + {"AUDIO_REF_EC_UL8 MUX", "TERT_MI2S_TX", "TERT_MI2S_TX"}, + {"AUDIO_REF_EC_UL8 MUX", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + + {"AUDIO_REF_EC_UL9 MUX", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"AUDIO_REF_EC_UL9 MUX", "SEC_MI2S_TX", "SEC_MI2S_TX"}, + {"AUDIO_REF_EC_UL9 MUX", "TERT_MI2S_TX", "TERT_MI2S_TX"}, + {"AUDIO_REF_EC_UL9 MUX", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + + {"AUDIO_REF_EC_UL10 MUX", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"AUDIO_REF_EC_UL10 MUX", "SEC_MI2S_TX", "SEC_MI2S_TX"}, + {"AUDIO_REF_EC_UL10 MUX", "TERT_MI2S_TX", "TERT_MI2S_TX"}, + {"AUDIO_REF_EC_UL10 MUX", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + {"AUDIO_REF_EC_UL10 MUX", "SLIM_1_TX", "SLIMBUS_1_TX"}, + {"AUDIO_REF_EC_UL10 MUX", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"}, + {"AUDIO_REF_EC_UL10 MUX", "QUAT_TDM_RX_0", "QUAT_TDM_RX_0"}, + {"AUDIO_REF_EC_UL10 MUX", "QUAT_TDM_RX_1", "QUAT_TDM_RX_1"}, + {"AUDIO_REF_EC_UL10 MUX", "QUAT_TDM_RX_2", "QUAT_TDM_RX_2"}, + {"AUDIO_REF_EC_UL10 MUX", "TERT_TDM_TX_0", "TERT_TDM_TX_0"}, + {"AUDIO_REF_EC_UL10 MUX", "TERT_TDM_RX_2", "TERT_TDM_RX_2"}, + {"AUDIO_REF_EC_UL10 MUX", "SEC_TDM_TX_0", "SEC_TDM_TX_0"}, + + {"AUDIO_REF_EC_UL16 MUX", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"AUDIO_REF_EC_UL16 MUX", "SEC_MI2S_TX", "SEC_MI2S_TX"}, + {"AUDIO_REF_EC_UL16 MUX", "TERT_MI2S_TX", "TERT_MI2S_TX"}, + {"AUDIO_REF_EC_UL16 MUX", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + + {"AUDIO_REF_EC_UL17 MUX", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"AUDIO_REF_EC_UL17 MUX", "SEC_MI2S_TX", "SEC_MI2S_TX"}, + {"AUDIO_REF_EC_UL17 MUX", "TERT_MI2S_TX", "TERT_MI2S_TX"}, + {"AUDIO_REF_EC_UL17 MUX", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + + {"AUDIO_REF_EC_UL18 MUX", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"AUDIO_REF_EC_UL18 MUX", "SEC_MI2S_TX", "SEC_MI2S_TX"}, + {"AUDIO_REF_EC_UL18 MUX", "TERT_MI2S_TX", "TERT_MI2S_TX"}, + {"AUDIO_REF_EC_UL18 MUX", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + + {"AUDIO_REF_EC_UL19 MUX", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"AUDIO_REF_EC_UL19 MUX", "SEC_MI2S_TX", "SEC_MI2S_TX"}, + {"AUDIO_REF_EC_UL19 MUX", "TERT_MI2S_TX", "TERT_MI2S_TX"}, + {"AUDIO_REF_EC_UL19 MUX", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + + {"AUDIO_REF_EC_UL28 MUX", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"AUDIO_REF_EC_UL28 MUX", "SEC_MI2S_TX", "SEC_MI2S_TX"}, + {"AUDIO_REF_EC_UL28 MUX", "TERT_MI2S_TX", "TERT_MI2S_TX"}, + {"AUDIO_REF_EC_UL28 MUX", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + + {"AUDIO_REF_EC_UL29 MUX", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"AUDIO_REF_EC_UL29 MUX", "SEC_MI2S_TX", "SEC_MI2S_TX"}, + {"AUDIO_REF_EC_UL29 MUX", "TERT_MI2S_TX", "TERT_MI2S_TX"}, + {"AUDIO_REF_EC_UL29 MUX", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + + {"LSM1_UL_HL", NULL, "AUDIO_REF_EC_UL1 MUX"}, + {"LSM2_UL_HL", NULL, "AUDIO_REF_EC_UL1 MUX"}, + {"LSM3_UL_HL", NULL, "AUDIO_REF_EC_UL1 MUX"}, + {"LSM4_UL_HL", NULL, "AUDIO_REF_EC_UL1 MUX"}, + {"LSM5_UL_HL", NULL, "AUDIO_REF_EC_UL1 MUX"}, + {"LSM6_UL_HL", NULL, "AUDIO_REF_EC_UL1 MUX"}, + {"LSM7_UL_HL", NULL, "AUDIO_REF_EC_UL1 MUX"}, + {"LSM8_UL_HL", NULL, "AUDIO_REF_EC_UL1 MUX"}, + + {"MM_UL1", NULL, "AUDIO_REF_EC_UL1 MUX"}, + {"MM_UL2", NULL, "AUDIO_REF_EC_UL2 MUX"}, + {"MM_UL3", NULL, "AUDIO_REF_EC_UL3 MUX"}, + {"MM_UL4", NULL, "AUDIO_REF_EC_UL4 MUX"}, + {"MM_UL5", NULL, "AUDIO_REF_EC_UL5 MUX"}, + {"MM_UL6", NULL, "AUDIO_REF_EC_UL6 MUX"}, + {"MM_UL8", NULL, "AUDIO_REF_EC_UL8 MUX"}, + {"MM_UL9", NULL, "AUDIO_REF_EC_UL9 MUX"}, + {"MM_UL10", NULL, "AUDIO_REF_EC_UL10 MUX"}, + {"MM_UL16", NULL, "AUDIO_REF_EC_UL16 MUX"}, + {"MM_UL17", NULL, "AUDIO_REF_EC_UL17 MUX"}, + {"MM_UL18", NULL, "AUDIO_REF_EC_UL18 MUX"}, + {"MM_UL19", NULL, "AUDIO_REF_EC_UL19 MUX"}, + {"MM_UL28", NULL, "AUDIO_REF_EC_UL28 MUX"}, + {"MM_UL29", NULL, "AUDIO_REF_EC_UL29 MUX"}, + + {"VoiceMMode1_Tx Mixer", "PRI_TX_MMode1", "PRI_I2S_TX"}, + {"VoiceMMode1_Tx Mixer", "PRI_MI2S_TX_MMode1", "PRI_MI2S_TX"}, + {"VoiceMMode1_Tx Mixer", "MI2S_TX_MMode1", "MI2S_TX"}, + {"VoiceMMode1_Tx Mixer", "TERT_MI2S_TX_MMode1", "TERT_MI2S_TX"}, + {"VoiceMMode1_Tx Mixer", "INT3_MI2S_TX_MMode1", "INT3_MI2S_TX"}, + {"VoiceMMode1_Tx Mixer", "SLIM_0_TX_MMode1", "SLIMBUS_0_TX"}, + {"VoiceMMode1_Tx Mixer", "SLIM_7_TX_MMode1", "SLIMBUS_7_TX"}, + {"VoiceMMode1_Tx Mixer", "SLIM_8_TX_MMode1", "SLIMBUS_8_TX"}, + {"VoiceMMode1_Tx Mixer", "USB_AUDIO_TX_MMode1", "USB_AUDIO_TX"}, + {"VoiceMMode1_Tx Mixer", "INT_BT_SCO_TX_MMode1", "INT_BT_SCO_TX"}, + {"VoiceMMode1_Tx Mixer", "AFE_PCM_TX_MMode1", "PCM_TX"}, + {"VoiceMMode1_Tx Mixer", "AUX_PCM_TX_MMode1", "AUX_PCM_TX"}, + {"VoiceMMode1_Tx Mixer", "SEC_AUX_PCM_TX_MMode1", "SEC_AUX_PCM_TX"}, + {"VoiceMMode1_Tx Mixer", "TERT_AUX_PCM_TX_MMode1", "TERT_AUX_PCM_TX"}, + {"VoiceMMode1_Tx Mixer", "QUAT_AUX_PCM_TX_MMode1", "QUAT_AUX_PCM_TX"}, + {"VoiceMMode1_Tx Mixer", "QUIN_AUX_PCM_TX_MMode1", "QUIN_AUX_PCM_TX"}, + {"VoiceMMode1_Tx Mixer", "QUAT_TDM_TX_0_MMode1", "QUAT_TDM_TX_0"}, + {"VoiceMMode1_Tx Mixer", "TX_CDC_DMA_TX_0_MMode1", "TX_CDC_DMA_TX_0"}, + {"VoiceMMode1_Tx Mixer", "TX_CDC_DMA_TX_1_MMode1", "TX_CDC_DMA_TX_1"}, + {"VoiceMMode1_Tx Mixer", "TX_CDC_DMA_TX_2_MMode1", "TX_CDC_DMA_TX_2"}, + {"VoiceMMode1_Tx Mixer", "TX_CDC_DMA_TX_3_MMode1", "TX_CDC_DMA_TX_3"}, + {"VoiceMMode1_Tx Mixer", "TX_CDC_DMA_TX_4_MMode1", "TX_CDC_DMA_TX_4"}, + {"VoiceMMode1_Tx Mixer", "TX_CDC_DMA_TX_5_MMode1", "TX_CDC_DMA_TX_5"}, + {"VOICEMMODE1_UL", NULL, "VoiceMMode1_Tx Mixer"}, + + {"VoiceMMode2_Tx Mixer", "PRI_TX_MMode2", "PRI_I2S_TX"}, + {"VoiceMMode2_Tx Mixer", "PRI_MI2S_TX_MMode2", "PRI_MI2S_TX"}, + {"VoiceMMode2_Tx Mixer", "MI2S_TX_MMode2", "MI2S_TX"}, + {"VoiceMMode2_Tx Mixer", "TERT_MI2S_TX_MMode2", "TERT_MI2S_TX"}, + {"VoiceMMode2_Tx Mixer", "INT3_MI2S_TX_MMode2", "INT3_MI2S_TX"}, + {"VoiceMMode2_Tx Mixer", "SLIM_0_TX_MMode2", "SLIMBUS_0_TX"}, + {"VoiceMMode2_Tx Mixer", "SLIM_7_TX_MMode2", "SLIMBUS_7_TX"}, + {"VoiceMMode2_Tx Mixer", "SLIM_8_TX_MMode2", "SLIMBUS_8_TX"}, + {"VoiceMMode2_Tx Mixer", "USB_AUDIO_TX_MMode2", "USB_AUDIO_TX"}, + {"VoiceMMode2_Tx Mixer", "INT_BT_SCO_TX_MMode2", "INT_BT_SCO_TX"}, + {"VoiceMMode2_Tx Mixer", "AFE_PCM_TX_MMode2", "PCM_TX"}, + {"VoiceMMode2_Tx Mixer", "AUX_PCM_TX_MMode2", "AUX_PCM_TX"}, + {"VoiceMMode2_Tx Mixer", "SEC_AUX_PCM_TX_MMode2", "SEC_AUX_PCM_TX"}, + {"VoiceMMode2_Tx Mixer", "TERT_AUX_PCM_TX_MMode2", "TERT_AUX_PCM_TX"}, + {"VoiceMMode2_Tx Mixer", "QUAT_AUX_PCM_TX_MMode2", "QUAT_AUX_PCM_TX"}, + {"VoiceMMode2_Tx Mixer", "QUIN_AUX_PCM_TX_MMode2", "QUIN_AUX_PCM_TX"}, + {"VoiceMMode2_Tx Mixer", "TX_CDC_DMA_TX_0_MMode2", "TX_CDC_DMA_TX_0"}, + {"VoiceMMode2_Tx Mixer", "TX_CDC_DMA_TX_1_MMode2", "TX_CDC_DMA_TX_1"}, + {"VoiceMMode2_Tx Mixer", "TX_CDC_DMA_TX_2_MMode2", "TX_CDC_DMA_TX_2"}, + {"VoiceMMode2_Tx Mixer", "TX_CDC_DMA_TX_3_MMode2", "TX_CDC_DMA_TX_3"}, + {"VoiceMMode2_Tx Mixer", "TX_CDC_DMA_TX_4_MMode2", "TX_CDC_DMA_TX_4"}, + {"VoiceMMode2_Tx Mixer", "TX_CDC_DMA_TX_5_MMode2", "TX_CDC_DMA_TX_5"}, + {"VOICEMMODE2_UL", NULL, "VoiceMMode2_Tx Mixer"}, + + {"Voip_Tx Mixer", "PRI_TX_Voip", "PRI_I2S_TX"}, + {"Voip_Tx Mixer", "MI2S_TX_Voip", "MI2S_TX"}, + {"Voip_Tx Mixer", "TERT_MI2S_TX_Voip", "TERT_MI2S_TX"}, + {"Voip_Tx Mixer", "INT3_MI2S_TX_Voip", "INT3_MI2S_TX"}, + {"Voip_Tx Mixer", "SLIM_0_TX_Voip", "SLIMBUS_0_TX"}, + {"Voip_Tx Mixer", "SLIM_7_TX_Voip", "SLIMBUS_7_TX"}, + {"Voip_Tx Mixer", "SLIM_8_TX_Voip", "SLIMBUS_8_TX"}, + {"Voip_Tx Mixer", "USB_AUDIO_TX_Voip", "USB_AUDIO_TX"}, + {"Voip_Tx Mixer", "INTERNAL_BT_SCO_TX_Voip", "INT_BT_SCO_TX"}, + {"Voip_Tx Mixer", "AFE_PCM_TX_Voip", "PCM_TX"}, + {"Voip_Tx Mixer", "AUX_PCM_TX_Voip", "AUX_PCM_TX"}, + {"Voip_Tx Mixer", "SEC_AUX_PCM_TX_Voip", "SEC_AUX_PCM_TX"}, + {"Voip_Tx Mixer", "TERT_AUX_PCM_TX_Voip", "TERT_AUX_PCM_TX"}, + {"Voip_Tx Mixer", "QUAT_AUX_PCM_TX_Voip", "QUAT_AUX_PCM_TX"}, + {"Voip_Tx Mixer", "QUIN_AUX_PCM_TX_Voip", "QUIN_AUX_PCM_TX"}, + {"Voip_Tx Mixer", "PRI_MI2S_TX_Voip", "PRI_MI2S_TX"}, + {"VOIP_UL", NULL, "Voip_Tx Mixer"}, + + {"SLIMBUS_DL_HL", "Switch", "SLIM0_DL_HL"}, + {"SLIMBUS_0_RX", NULL, "SLIMBUS_DL_HL"}, + {"SLIMBUS1_DL_HL", "Switch", "SLIM1_DL_HL"}, + {"SLIMBUS_1_RX", NULL, "SLIMBUS1_DL_HL"}, + {"SLIMBUS3_DL_HL", "Switch", "SLIM3_DL_HL"}, + {"SLIMBUS_3_RX", NULL, "SLIMBUS3_DL_HL"}, + {"SLIMBUS4_DL_HL", "Switch", "SLIM4_DL_HL"}, + {"SLIMBUS_4_RX", NULL, "SLIMBUS4_DL_HL"}, + {"SLIMBUS6_DL_HL", "Switch", "SLIM0_DL_HL"}, + {"SLIMBUS_6_RX", NULL, "SLIMBUS6_DL_HL"}, + {"SLIM0_UL_HL", NULL, "SLIMBUS_0_TX"}, + {"SLIM1_UL_HL", NULL, "SLIMBUS_1_TX"}, + {"SLIM3_UL_HL", NULL, "SLIMBUS_3_TX"}, + {"SLIM4_UL_HL", NULL, "SLIMBUS_4_TX"}, + {"SLIM8_UL_HL", NULL, "SLIMBUS_8_TX"}, + {"WSA_CDC_DMA_RX_0_DL_HL", "Switch", "CDC_DMA_DL_HL"}, + {"WSA_CDC_DMA_RX_0", NULL, "WSA_CDC_DMA_RX_0_DL_HL"}, + {"CDC_DMA_UL_HL", NULL, "VA_CDC_DMA_TX_0"}, + {"RX_CDC_DMA_RX_0_DL_HL", "Switch", "CDC_DMA_DL_HL"}, + {"RX_CDC_DMA_RX_0", NULL, "RX_CDC_DMA_RX_0_DL_HL"}, + {"TX3_CDC_DMA_UL_HL", NULL, "TX_CDC_DMA_TX_3"}, + {"LSM1 Mixer", "SLIMBUS_0_TX", "SLIMBUS_0_TX"}, + {"LSM1 Mixer", "SLIMBUS_1_TX", "SLIMBUS_1_TX"}, + {"LSM1 Mixer", "SLIMBUS_3_TX", "SLIMBUS_3_TX"}, + {"LSM1 Mixer", "SLIMBUS_4_TX", "SLIMBUS_4_TX"}, + {"LSM1 Mixer", "SLIMBUS_5_TX", "SLIMBUS_5_TX"}, + {"LSM1 Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"}, + {"LSM1 Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + {"LSM1 Mixer", "INT3_MI2S_TX", "INT3_MI2S_TX"}, + {"LSM1 Mixer", "VA_CDC_DMA_TX_0", "VA_CDC_DMA_TX_0"}, + {"LSM1 Mixer", "VA_CDC_DMA_TX_1", "VA_CDC_DMA_TX_1"}, + {"LSM1 Mixer", "TX_CDC_DMA_TX_3", "TX_CDC_DMA_TX_3"}, + {"LSM1 Mixer", "QUIN_TDM_TX_0", "QUIN_TDM_TX_0"}, + {"LSM1_UL_HL", NULL, "LSM1 Mixer"}, + + {"LSM2 Mixer", "SLIMBUS_0_TX", "SLIMBUS_0_TX"}, + {"LSM2 Mixer", "SLIMBUS_1_TX", "SLIMBUS_1_TX"}, + {"LSM2 Mixer", "SLIMBUS_3_TX", "SLIMBUS_3_TX"}, + {"LSM2 Mixer", "SLIMBUS_4_TX", "SLIMBUS_4_TX"}, + {"LSM2 Mixer", "SLIMBUS_5_TX", "SLIMBUS_5_TX"}, + {"LSM2 Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"}, + {"LSM2 Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + {"LSM2 Mixer", "INT3_MI2S_TX", "INT3_MI2S_TX"}, + {"LSM2 Mixer", "VA_CDC_DMA_TX_0", "VA_CDC_DMA_TX_0"}, + {"LSM2 Mixer", "VA_CDC_DMA_TX_1", "VA_CDC_DMA_TX_1"}, + {"LSM2 Mixer", "TX_CDC_DMA_TX_3", "TX_CDC_DMA_TX_3"}, + {"LSM2 Mixer", "QUIN_TDM_TX_0", "QUIN_TDM_TX_0"}, + {"LSM2_UL_HL", NULL, "LSM2 Mixer"}, + + + {"LSM3 Mixer", "SLIMBUS_0_TX", "SLIMBUS_0_TX"}, + {"LSM3 Mixer", "SLIMBUS_1_TX", "SLIMBUS_1_TX"}, + {"LSM3 Mixer", "SLIMBUS_3_TX", "SLIMBUS_3_TX"}, + {"LSM3 Mixer", "SLIMBUS_4_TX", "SLIMBUS_4_TX"}, + {"LSM3 Mixer", "SLIMBUS_5_TX", "SLIMBUS_5_TX"}, + {"LSM3 Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"}, + {"LSM3 Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + {"LSM3 Mixer", "INT3_MI2S_TX", "INT3_MI2S_TX"}, + {"LSM3 Mixer", "VA_CDC_DMA_TX_0", "VA_CDC_DMA_TX_0"}, + {"LSM3 Mixer", "VA_CDC_DMA_TX_1", "VA_CDC_DMA_TX_1"}, + {"LSM3 Mixer", "TX_CDC_DMA_TX_3", "TX_CDC_DMA_TX_3"}, + {"LSM3 Mixer", "QUIN_TDM_TX_0", "QUIN_TDM_TX_0"}, + {"LSM3_UL_HL", NULL, "LSM3 Mixer"}, + + + {"LSM4 Mixer", "SLIMBUS_0_TX", "SLIMBUS_0_TX"}, + {"LSM4 Mixer", "SLIMBUS_1_TX", "SLIMBUS_1_TX"}, + {"LSM4 Mixer", "SLIMBUS_3_TX", "SLIMBUS_3_TX"}, + {"LSM4 Mixer", "SLIMBUS_4_TX", "SLIMBUS_4_TX"}, + {"LSM4 Mixer", "SLIMBUS_5_TX", "SLIMBUS_5_TX"}, + {"LSM4 Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"}, + {"LSM4 Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + {"LSM4 Mixer", "INT3_MI2S_TX", "INT3_MI2S_TX"}, + {"LSM4 Mixer", "VA_CDC_DMA_TX_0", "VA_CDC_DMA_TX_0"}, + {"LSM4 Mixer", "VA_CDC_DMA_TX_1", "VA_CDC_DMA_TX_1"}, + {"LSM4 Mixer", "TX_CDC_DMA_TX_3", "TX_CDC_DMA_TX_3"}, + {"LSM4 Mixer", "QUIN_TDM_TX_0", "QUIN_TDM_TX_0"}, + {"LSM4_UL_HL", NULL, "LSM4 Mixer"}, + + {"LSM5 Mixer", "SLIMBUS_0_TX", "SLIMBUS_0_TX"}, + {"LSM5 Mixer", "SLIMBUS_1_TX", "SLIMBUS_1_TX"}, + {"LSM5 Mixer", "SLIMBUS_3_TX", "SLIMBUS_3_TX"}, + {"LSM5 Mixer", "SLIMBUS_4_TX", "SLIMBUS_4_TX"}, + {"LSM5 Mixer", "SLIMBUS_5_TX", "SLIMBUS_5_TX"}, + {"LSM5 Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"}, + {"LSM5 Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + {"LSM5 Mixer", "INT3_MI2S_TX", "INT3_MI2S_TX"}, + {"LSM5 Mixer", "VA_CDC_DMA_TX_0", "VA_CDC_DMA_TX_0"}, + {"LSM5 Mixer", "VA_CDC_DMA_TX_1", "VA_CDC_DMA_TX_1"}, + {"LSM5 Mixer", "TX_CDC_DMA_TX_3", "TX_CDC_DMA_TX_3"}, + {"LSM5 Mixer", "QUIN_TDM_TX_0", "QUIN_TDM_TX_0"}, + {"LSM5_UL_HL", NULL, "LSM5 Mixer"}, + + {"LSM6 Mixer", "SLIMBUS_0_TX", "SLIMBUS_0_TX"}, + {"LSM6 Mixer", "SLIMBUS_1_TX", "SLIMBUS_1_TX"}, + {"LSM6 Mixer", "SLIMBUS_3_TX", "SLIMBUS_3_TX"}, + {"LSM6 Mixer", "SLIMBUS_4_TX", "SLIMBUS_4_TX"}, + {"LSM6 Mixer", "SLIMBUS_5_TX", "SLIMBUS_5_TX"}, + {"LSM6 Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + {"LSM6 Mixer", "VA_CDC_DMA_TX_0", "VA_CDC_DMA_TX_0"}, + {"LSM6 Mixer", "VA_CDC_DMA_TX_1", "VA_CDC_DMA_TX_1"}, + {"LSM6 Mixer", "TX_CDC_DMA_TX_3", "TX_CDC_DMA_TX_3"}, + {"LSM6 Mixer", "QUIN_TDM_TX_0", "QUIN_TDM_TX_0"}, + {"LSM6_UL_HL", NULL, "LSM6 Mixer"}, + + {"LSM7 Mixer", "SLIMBUS_0_TX", "SLIMBUS_0_TX"}, + {"LSM7 Mixer", "SLIMBUS_1_TX", "SLIMBUS_1_TX"}, + {"LSM7 Mixer", "SLIMBUS_3_TX", "SLIMBUS_3_TX"}, + {"LSM7 Mixer", "SLIMBUS_4_TX", "SLIMBUS_4_TX"}, + {"LSM7 Mixer", "SLIMBUS_5_TX", "SLIMBUS_5_TX"}, + {"LSM7 Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + {"LSM7 Mixer", "VA_CDC_DMA_TX_0", "VA_CDC_DMA_TX_0"}, + {"LSM7 Mixer", "VA_CDC_DMA_TX_1", "VA_CDC_DMA_TX_1"}, + {"LSM7 Mixer", "TX_CDC_DMA_TX_3", "TX_CDC_DMA_TX_3"}, + {"LSM7 Mixer", "QUIN_TDM_TX_0", "QUIN_TDM_TX_0"}, + {"LSM7_UL_HL", NULL, "LSM7 Mixer"}, + + {"LSM8 Mixer", "SLIMBUS_0_TX", "SLIMBUS_0_TX"}, + {"LSM8 Mixer", "SLIMBUS_1_TX", "SLIMBUS_1_TX"}, + {"LSM8 Mixer", "SLIMBUS_3_TX", "SLIMBUS_3_TX"}, + {"LSM8 Mixer", "SLIMBUS_4_TX", "SLIMBUS_4_TX"}, + {"LSM8 Mixer", "SLIMBUS_5_TX", "SLIMBUS_5_TX"}, + {"LSM8 Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + {"LSM8 Mixer", "VA_CDC_DMA_TX_0", "VA_CDC_DMA_TX_0"}, + {"LSM8 Mixer", "VA_CDC_DMA_TX_1", "VA_CDC_DMA_TX_1"}, + {"LSM8 Mixer", "TX_CDC_DMA_TX_3", "TX_CDC_DMA_TX_3"}, + {"LSM8 Mixer", "QUIN_TDM_TX_0", "QUIN_TDM_TX_0"}, + {"LSM8_UL_HL", NULL, "LSM8 Mixer"}, + + + {"CPE_LSM_UL_HL", NULL, "BE_IN"}, + {"QCHAT_Tx Mixer", "PRI_TX_QCHAT", "PRI_I2S_TX"}, + {"QCHAT_Tx Mixer", "SLIM_0_TX_QCHAT", "SLIMBUS_0_TX"}, + {"QCHAT_Tx Mixer", "SLIM_7_TX_QCHAT", "SLIMBUS_7_TX"}, + {"QCHAT_Tx Mixer", "SLIM_8_TX_QCHAT", "SLIMBUS_8_TX"}, + {"QCHAT_Tx Mixer", "INTERNAL_BT_SCO_TX_QCHAT", "INT_BT_SCO_TX"}, + {"QCHAT_Tx Mixer", "AFE_PCM_TX_QCHAT", "PCM_TX"}, + {"QCHAT_Tx Mixer", "AUX_PCM_TX_QCHAT", "AUX_PCM_TX"}, + {"QCHAT_Tx Mixer", "SEC_AUX_PCM_TX_QCHAT", "SEC_AUX_PCM_TX"}, + {"QCHAT_Tx Mixer", "TERT_AUX_PCM_TX_QCHAT", "TERT_AUX_PCM_TX"}, + {"QCHAT_Tx Mixer", "QUAT_AUX_PCM_TX_QCHAT", "QUAT_AUX_PCM_TX"}, + {"QCHAT_Tx Mixer", "QUIN_AUX_PCM_TX_QCHAT", "QUIN_AUX_PCM_TX"}, + {"QCHAT_Tx Mixer", "MI2S_TX_QCHAT", "MI2S_TX"}, + {"QCHAT_Tx Mixer", "PRI_MI2S_TX_QCHAT", "PRI_MI2S_TX"}, + {"QCHAT_Tx Mixer", "TERT_MI2S_TX_QCHAT", "TERT_MI2S_TX"}, + {"QCHAT_Tx Mixer", "INT3_MI2S_TX_QCHAT", "INT3_MI2S_TX"}, + {"QCHAT_Tx Mixer", "USB_AUDIO_TX_QCHAT", "USB_AUDIO_TX"}, + {"QCHAT_UL", NULL, "QCHAT_Tx Mixer"}, + + {"INT_FM_RX", NULL, "INTFM_DL_HL"}, + {"INTFM_UL_HL", NULL, "INT_FM_TX"}, + {"INTHFP_UL_HL", NULL, "HFP_PRI_AUX_UL_HL"}, + {"HFP_PRI_AUX_UL_HL", "Switch", "AUX_PCM_TX"}, + {"INTHFP_UL_HL", NULL, "HFP_AUX_UL_HL"}, + {"HFP_AUX_UL_HL", "Switch", "SEC_AUX_PCM_TX"}, + {"INTHFP_UL_HL", NULL, "HFP_INT_UL_HL"}, + {"HFP_INT_UL_HL", "Switch", "INT_BT_SCO_TX"}, + {"SLIM7_UL_HL", NULL, "HFP_SLIM7_UL_HL"}, + {"HFP_SLIM7_UL_HL", "Switch", "SLIMBUS_7_TX"}, + {"AUX_PCM_RX", NULL, "AUXPCM_DL_HL"}, + {"AUX_PCM_RX", NULL, "INTHFP_DL_HL"}, + {"SLIM7_UL_HL", NULL, "A2DP_SLIM7_UL_HL"}, + {"A2DP_SLIM7_UL_HL", "Switch", "SLIMBUS_7_TX"}, + {"SEC_AUX_PCM_RX", NULL, "SEC_AUXPCM_DL_HL"}, + {"AUXPCM_UL_HL", NULL, "AUX_PCM_TX"}, + {"SEC_AUXPCM_UL_HL", NULL, "SEC_AUX_PCM_TX"}, + {"MI2S_RX", NULL, "MI2S_DL_HL"}, + {"MI2S_UL_HL", NULL, "MI2S_TX"}, + {"PCM_RX_DL_HL", "Switch", "SLIM0_DL_HL"}, + {"PCM_RX", NULL, "PCM_RX_DL_HL"}, + + /* connect to INT4_MI2S_DL_HL since same pcm_id */ + {"INT0_MI2S_RX_DL_HL", "Switch", "INT4_MI2S_DL_HL"}, + {"INT0_MI2S_RX", NULL, "INT0_MI2S_RX_DL_HL"}, + {"INT4_MI2S_RX_DL_HL", "Switch", "INT4_MI2S_DL_HL"}, + {"INT4_MI2S_RX", NULL, "INT4_MI2S_RX_DL_HL"}, + {"PRI_MI2S_RX_DL_HL", "Switch", "PRI_MI2S_DL_HL"}, + {"PRI_MI2S_RX", NULL, "PRI_MI2S_RX_DL_HL"}, + {"SEC_MI2S_RX_DL_HL", "Switch", "SEC_MI2S_DL_HL"}, + {"SEC_MI2S_RX", NULL, "SEC_MI2S_RX_DL_HL"}, + {"TERT_MI2S_RX_DL_HL", "Switch", "TERT_MI2S_DL_HL"}, + {"TERT_MI2S_RX", NULL, "TERT_MI2S_RX_DL_HL"}, + + {"QUAT_MI2S_RX_DL_HL", "Switch", "QUAT_MI2S_DL_HL"}, + {"QUAT_MI2S_RX", NULL, "QUAT_MI2S_RX_DL_HL"}, + {"QUIN_MI2S_RX_DL_HL", "Switch", "QUIN_MI2S_DL_HL"}, + {"QUIN_MI2S_RX", NULL, "QUIN_MI2S_RX_DL_HL"}, + {"MI2S_UL_HL", NULL, "TERT_MI2S_TX"}, + {"INT3_MI2S_UL_HL", NULL, "INT3_MI2S_TX"}, + {"TERT_MI2S_UL_HL", NULL, "TERT_MI2S_TX"}, + {"SEC_I2S_RX", NULL, "SEC_I2S_DL_HL"}, + {"PRI_MI2S_UL_HL", NULL, "PRI_MI2S_TX"}, + {"SEC_MI2S_UL_HL", NULL, "SEC_MI2S_TX"}, + {"SEC_MI2S_RX", NULL, "SEC_MI2S_DL_HL"}, + {"PRI_MI2S_RX", NULL, "PRI_MI2S_DL_HL"}, + {"TERT_MI2S_RX", NULL, "TERT_MI2S_DL_HL"}, + {"QUAT_MI2S_UL_HL", NULL, "QUAT_MI2S_TX"}, + + {"PRI_TDM_TX_0_UL_HL", NULL, "PRI_TDM_TX_0"}, + {"PRI_TDM_TX_1_UL_HL", NULL, "PRI_TDM_TX_1"}, + {"PRI_TDM_TX_2_UL_HL", NULL, "PRI_TDM_TX_2"}, + {"PRI_TDM_TX_3_UL_HL", NULL, "PRI_TDM_TX_3"}, + {"PRI_TDM_RX_0", NULL, "PRI_TDM_RX_0_DL_HL"}, + {"PRI_TDM_RX_1", NULL, "PRI_TDM_RX_1_DL_HL"}, + {"PRI_TDM_RX_2", NULL, "PRI_TDM_RX_2_DL_HL"}, + {"PRI_TDM_RX_3", NULL, "PRI_TDM_RX_3_DL_HL"}, + {"SEC_TDM_TX_0_UL_HL", NULL, "SEC_TDM_TX_0"}, + {"SEC_TDM_TX_1_UL_HL", NULL, "SEC_TDM_TX_1"}, + {"SEC_TDM_TX_2_UL_HL", NULL, "SEC_TDM_TX_2"}, + {"SEC_TDM_TX_3_UL_HL", NULL, "SEC_TDM_TX_3"}, + {"SEC_TDM_RX_0", NULL, "SEC_TDM_RX_0_DL_HL"}, + {"SEC_TDM_RX_1", NULL, "SEC_TDM_RX_1_DL_HL"}, + {"SEC_TDM_RX_2", NULL, "SEC_TDM_RX_2_DL_HL"}, + {"SEC_TDM_RX_3", NULL, "SEC_TDM_RX_3_DL_HL"}, + {"SEC_TDM_RX_7", NULL, "SEC_TDM_RX_7_DL_HL"}, + {"TERT_TDM_TX_0_UL_HL", NULL, "TERT_TDM_TX_0"}, + {"TERT_TDM_TX_1_UL_HL", NULL, "TERT_TDM_TX_1"}, + {"TERT_TDM_TX_2_UL_HL", NULL, "TERT_TDM_TX_2"}, + {"TERT_TDM_TX_3_UL_HL", NULL, "TERT_TDM_TX_3"}, + {"TERT_TDM_TX_7_UL_HL", NULL, "TERT_TDM_TX_7"}, + {"TERT_TDM_RX_0", NULL, "TERT_TDM_RX_0_DL_HL"}, + {"TERT_TDM_RX_1", NULL, "TERT_TDM_RX_1_DL_HL"}, + {"TERT_TDM_RX_2", NULL, "TERT_TDM_RX_2_DL_HL"}, + {"TERT_TDM_RX_3", NULL, "TERT_TDM_RX_3_DL_HL"}, + {"QUAT_TDM_TX_0_UL_HL", NULL, "QUAT_TDM_TX_0"}, + {"QUAT_TDM_TX_1_UL_HL", NULL, "QUAT_TDM_TX_1"}, + {"QUAT_TDM_TX_2_UL_HL", NULL, "QUAT_TDM_TX_2"}, + {"QUAT_TDM_TX_3_UL_HL", NULL, "QUAT_TDM_TX_3"}, + {"QUAT_TDM_RX_0", NULL, "QUAT_TDM_RX_0_DL_HL"}, + {"QUAT_TDM_RX_1", NULL, "QUAT_TDM_RX_1_DL_HL"}, + {"QUAT_TDM_RX_2", NULL, "QUAT_TDM_RX_2_DL_HL"}, + {"QUAT_TDM_RX_3", NULL, "QUAT_TDM_RX_3_DL_HL"}, + {"QUIN_TDM_TX_0_UL_HL", NULL, "QUIN_TDM_TX_0"}, + {"QUIN_TDM_TX_1_UL_HL", NULL, "QUIN_TDM_TX_1"}, + {"QUIN_TDM_TX_2_UL_HL", NULL, "QUIN_TDM_TX_2"}, + {"QUIN_TDM_TX_3_UL_HL", NULL, "QUIN_TDM_TX_3"}, + {"QUIN_TDM_RX_0", NULL, "QUIN_TDM_RX_0_DL_HL"}, + {"QUIN_TDM_RX_1", NULL, "QUIN_TDM_RX_1_DL_HL"}, + {"QUIN_TDM_RX_2", NULL, "QUIN_TDM_RX_2_DL_HL"}, + {"QUIN_TDM_RX_3", NULL, "QUIN_TDM_RX_3_DL_HL"}, + + {"PRI_TDM_RX_0 Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"PRI_TDM_RX_0 Port Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"}, + {"PRI_TDM_RX_0 Port Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + {"PRI_TDM_RX_0 Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"}, + {"PRI_TDM_RX_0 Port Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"}, + {"PRI_TDM_RX_0 Port Mixer", "AFE_PCM_TX", "PCM_TX"}, + {"PRI_TDM_RX_0 Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"}, + {"PRI_TDM_RX_0 Port Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"}, + {"PRI_TDM_RX_0 Port Mixer", "PRI_TDM_TX_0", "PRI_TDM_TX_0"}, + {"PRI_TDM_RX_0 Port Mixer", "PRI_TDM_TX_1", "PRI_TDM_TX_1"}, + {"PRI_TDM_RX_0 Port Mixer", "PRI_TDM_TX_2", "PRI_TDM_TX_2"}, + {"PRI_TDM_RX_0 Port Mixer", "PRI_TDM_TX_3", "PRI_TDM_TX_3"}, + {"PRI_TDM_RX_0 Port Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"}, + {"PRI_TDM_RX_0 Port Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"}, + {"PRI_TDM_RX_0 Port Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"}, + {"PRI_TDM_RX_0 Port Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"}, + {"PRI_TDM_RX_0 Port Mixer", "QUIN_TDM_TX_0", "QUIN_TDM_TX_0"}, + {"PRI_TDM_RX_0 Port Mixer", "QUIN_TDM_TX_1", "QUIN_TDM_TX_1"}, + {"PRI_TDM_RX_0 Port Mixer", "QUIN_TDM_TX_2", "QUIN_TDM_TX_2"}, + {"PRI_TDM_RX_0 Port Mixer", "QUIN_TDM_TX_3", "QUIN_TDM_TX_3"}, + {"PRI_TDM_RX_0", NULL, "PRI_TDM_RX_0 Port Mixer"}, + + {"PRI_TDM_RX_1 Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"PRI_TDM_RX_1 Port Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"}, + {"PRI_TDM_RX_1 Port Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + {"PRI_TDM_RX_1 Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"}, + {"PRI_TDM_RX_1 Port Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"}, + {"PRI_TDM_RX_1 Port Mixer", "AFE_PCM_TX", "PCM_TX"}, + {"PRI_TDM_RX_1 Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"}, + {"PRI_TDM_RX_1 Port Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"}, + {"PRI_TDM_RX_1 Port Mixer", "PRI_TDM_TX_0", "PRI_TDM_TX_0"}, + {"PRI_TDM_RX_1 Port Mixer", "PRI_TDM_TX_1", "PRI_TDM_TX_1"}, + {"PRI_TDM_RX_1 Port Mixer", "PRI_TDM_TX_2", "PRI_TDM_TX_2"}, + {"PRI_TDM_RX_1 Port Mixer", "PRI_TDM_TX_3", "PRI_TDM_TX_3"}, + {"PRI_TDM_RX_1 Port Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"}, + {"PRI_TDM_RX_1 Port Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"}, + {"PRI_TDM_RX_1 Port Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"}, + {"PRI_TDM_RX_1 Port Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"}, + {"PRI_TDM_RX_1 Port Mixer", "QUIN_TDM_TX_0", "QUIN_TDM_TX_0"}, + {"PRI_TDM_RX_1 Port Mixer", "QUIN_TDM_TX_1", "QUIN_TDM_TX_1"}, + {"PRI_TDM_RX_1 Port Mixer", "QUIN_TDM_TX_2", "QUIN_TDM_TX_2"}, + {"PRI_TDM_RX_1 Port Mixer", "QUIN_TDM_TX_3", "QUIN_TDM_TX_3"}, + {"PRI_TDM_RX_1", NULL, "PRI_TDM_RX_1 Port Mixer"}, + + {"PRI_TDM_RX_2 Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"PRI_TDM_RX_2 Port Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"}, + {"PRI_TDM_RX_2 Port Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + {"PRI_TDM_RX_2 Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"}, + {"PRI_TDM_RX_2 Port Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"}, + {"PRI_TDM_RX_2 Port Mixer", "AFE_PCM_TX", "PCM_TX"}, + {"PRI_TDM_RX_2 Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"}, + {"PRI_TDM_RX_2 Port Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"}, + {"PRI_TDM_RX_2 Port Mixer", "PRI_TDM_TX_0", "PRI_TDM_TX_0"}, + {"PRI_TDM_RX_2 Port Mixer", "PRI_TDM_TX_1", "PRI_TDM_TX_1"}, + {"PRI_TDM_RX_2 Port Mixer", "PRI_TDM_TX_2", "PRI_TDM_TX_2"}, + {"PRI_TDM_RX_2 Port Mixer", "PRI_TDM_TX_3", "PRI_TDM_TX_3"}, + {"PRI_TDM_RX_2 Port Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"}, + {"PRI_TDM_RX_2 Port Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"}, + {"PRI_TDM_RX_2 Port Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"}, + {"PRI_TDM_RX_2 Port Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"}, + {"PRI_TDM_RX_2 Port Mixer", "QUIN_TDM_TX_0", "QUIN_TDM_TX_0"}, + {"PRI_TDM_RX_2 Port Mixer", "QUIN_TDM_TX_1", "QUIN_TDM_TX_1"}, + {"PRI_TDM_RX_2 Port Mixer", "QUIN_TDM_TX_2", "QUIN_TDM_TX_2"}, + {"PRI_TDM_RX_2 Port Mixer", "QUIN_TDM_TX_3", "QUIN_TDM_TX_3"}, + {"PRI_TDM_RX_2", NULL, "PRI_TDM_RX_2 Port Mixer"}, + + {"PRI_TDM_RX_3 Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"PRI_TDM_RX_3 Port Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"}, + {"PRI_TDM_RX_3 Port Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + {"PRI_TDM_RX_3 Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"}, + {"PRI_TDM_RX_3 Port Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"}, + {"PRI_TDM_RX_3 Port Mixer", "AFE_PCM_TX", "PCM_TX"}, + {"PRI_TDM_RX_3 Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"}, + {"PRI_TDM_RX_3 Port Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"}, + {"PRI_TDM_RX_3 Port Mixer", "PRI_TDM_TX_0", "PRI_TDM_TX_0"}, + {"PRI_TDM_RX_3 Port Mixer", "PRI_TDM_TX_1", "PRI_TDM_TX_1"}, + {"PRI_TDM_RX_3 Port Mixer", "PRI_TDM_TX_2", "PRI_TDM_TX_2"}, + {"PRI_TDM_RX_3 Port Mixer", "PRI_TDM_TX_3", "PRI_TDM_TX_3"}, + {"PRI_TDM_RX_3 Port Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"}, + {"PRI_TDM_RX_3 Port Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"}, + {"PRI_TDM_RX_3 Port Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"}, + {"PRI_TDM_RX_3 Port Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"}, + {"PRI_TDM_RX_3 Port Mixer", "QUIN_TDM_TX_0", "QUIN_TDM_TX_0"}, + {"PRI_TDM_RX_3 Port Mixer", "QUIN_TDM_TX_1", "QUIN_TDM_TX_1"}, + {"PRI_TDM_RX_3 Port Mixer", "QUIN_TDM_TX_2", "QUIN_TDM_TX_2"}, + {"PRI_TDM_RX_3 Port Mixer", "QUIN_TDM_TX_3", "QUIN_TDM_TX_3"}, + {"PRI_TDM_RX_3", NULL, "PRI_TDM_RX_3 Port Mixer"}, + + {"SEC_TDM_RX_0 Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"SEC_TDM_RX_0 Port Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"}, + {"SEC_TDM_RX_0 Port Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + {"SEC_TDM_RX_0 Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"}, + {"SEC_TDM_RX_0 Port Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"}, + {"SEC_TDM_RX_0 Port Mixer", "AFE_PCM_TX", "PCM_TX"}, + {"SEC_TDM_RX_0 Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"}, + {"SEC_TDM_RX_0 Port Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"}, + {"SEC_TDM_RX_0 Port Mixer", "SEC_TDM_TX_0", "SEC_TDM_TX_0"}, + {"SEC_TDM_RX_0 Port Mixer", "SEC_TDM_TX_1", "SEC_TDM_TX_1"}, + {"SEC_TDM_RX_0 Port Mixer", "SEC_TDM_TX_2", "SEC_TDM_TX_2"}, + {"SEC_TDM_RX_0 Port Mixer", "SEC_TDM_TX_3", "SEC_TDM_TX_3"}, + {"SEC_TDM_RX_0 Port Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"}, + {"SEC_TDM_RX_0 Port Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"}, + {"SEC_TDM_RX_0 Port Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"}, + {"SEC_TDM_RX_0 Port Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"}, + {"SEC_TDM_RX_0 Port Mixer", "QUIN_TDM_TX_0", "QUIN_TDM_TX_0"}, + {"SEC_TDM_RX_0 Port Mixer", "QUIN_TDM_TX_1", "QUIN_TDM_TX_1"}, + {"SEC_TDM_RX_0 Port Mixer", "QUIN_TDM_TX_2", "QUIN_TDM_TX_2"}, + {"SEC_TDM_RX_0 Port Mixer", "QUIN_TDM_TX_3", "QUIN_TDM_TX_3"}, + {"SEC_TDM_RX_0", NULL, "SEC_TDM_RX_0 Port Mixer"}, + + {"SEC_TDM_RX_1 Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"SEC_TDM_RX_1 Port Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"}, + {"SEC_TDM_RX_1 Port Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + {"SEC_TDM_RX_1 Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"}, + {"SEC_TDM_RX_1 Port Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"}, + {"SEC_TDM_RX_1 Port Mixer", "AFE_PCM_TX", "PCM_TX"}, + {"SEC_TDM_RX_1 Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"}, + {"SEC_TDM_RX_1 Port Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"}, + {"SEC_TDM_RX_1 Port Mixer", "SEC_TDM_TX_0", "SEC_TDM_TX_0"}, + {"SEC_TDM_RX_1 Port Mixer", "SEC_TDM_TX_1", "SEC_TDM_TX_1"}, + {"SEC_TDM_RX_1 Port Mixer", "SEC_TDM_TX_2", "SEC_TDM_TX_2"}, + {"SEC_TDM_RX_1 Port Mixer", "SEC_TDM_TX_3", "SEC_TDM_TX_3"}, + {"SEC_TDM_RX_1 Port Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"}, + {"SEC_TDM_RX_1 Port Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"}, + {"SEC_TDM_RX_1 Port Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"}, + {"SEC_TDM_RX_1 Port Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"}, + {"SEC_TDM_RX_1 Port Mixer", "QUIN_TDM_TX_0", "QUIN_TDM_TX_0"}, + {"SEC_TDM_RX_1 Port Mixer", "QUIN_TDM_TX_1", "QUIN_TDM_TX_1"}, + {"SEC_TDM_RX_1 Port Mixer", "QUIN_TDM_TX_2", "QUIN_TDM_TX_2"}, + {"SEC_TDM_RX_1 Port Mixer", "QUIN_TDM_TX_3", "QUIN_TDM_TX_3"}, + {"SEC_TDM_RX_1", NULL, "SEC_TDM_RX_1 Port Mixer"}, + + {"SEC_TDM_RX_2 Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"SEC_TDM_RX_2 Port Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"}, + {"SEC_TDM_RX_2 Port Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + {"SEC_TDM_RX_2 Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"}, + {"SEC_TDM_RX_2 Port Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"}, + {"SEC_TDM_RX_2 Port Mixer", "AFE_PCM_TX", "PCM_TX"}, + {"SEC_TDM_RX_2 Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"}, + {"SEC_TDM_RX_2 Port Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"}, + {"SEC_TDM_RX_2 Port Mixer", "SEC_TDM_TX_0", "SEC_TDM_TX_0"}, + {"SEC_TDM_RX_2 Port Mixer", "SEC_TDM_TX_1", "SEC_TDM_TX_1"}, + {"SEC_TDM_RX_2 Port Mixer", "SEC_TDM_TX_2", "SEC_TDM_TX_2"}, + {"SEC_TDM_RX_2 Port Mixer", "SEC_TDM_TX_3", "SEC_TDM_TX_3"}, + {"SEC_TDM_RX_2 Port Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"}, + {"SEC_TDM_RX_2 Port Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"}, + {"SEC_TDM_RX_2 Port Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"}, + {"SEC_TDM_RX_2 Port Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"}, + {"SEC_TDM_RX_2 Port Mixer", "QUIN_TDM_TX_0", "QUIN_TDM_TX_0"}, + {"SEC_TDM_RX_2 Port Mixer", "QUIN_TDM_TX_1", "QUIN_TDM_TX_1"}, + {"SEC_TDM_RX_2 Port Mixer", "QUIN_TDM_TX_2", "QUIN_TDM_TX_2"}, + {"SEC_TDM_RX_2 Port Mixer", "QUIN_TDM_TX_3", "QUIN_TDM_TX_3"}, + {"SEC_TDM_RX_2", NULL, "SEC_TDM_RX_2 Port Mixer"}, + + {"SEC_TDM_RX_3 Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"SEC_TDM_RX_3 Port Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"}, + {"SEC_TDM_RX_3 Port Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + {"SEC_TDM_RX_3 Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"}, + {"SEC_TDM_RX_3 Port Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"}, + {"SEC_TDM_RX_3 Port Mixer", "AFE_PCM_TX", "PCM_TX"}, + {"SEC_TDM_RX_3 Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"}, + {"SEC_TDM_RX_3 Port Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"}, + {"SEC_TDM_RX_3 Port Mixer", "SEC_TDM_TX_0", "SEC_TDM_TX_0"}, + {"SEC_TDM_RX_3 Port Mixer", "SEC_TDM_TX_1", "SEC_TDM_TX_1"}, + {"SEC_TDM_RX_3 Port Mixer", "SEC_TDM_TX_2", "SEC_TDM_TX_2"}, + {"SEC_TDM_RX_3 Port Mixer", "SEC_TDM_TX_3", "SEC_TDM_TX_3"}, + {"SEC_TDM_RX_3 Port Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"}, + {"SEC_TDM_RX_3 Port Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"}, + {"SEC_TDM_RX_3 Port Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"}, + {"SEC_TDM_RX_3 Port Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"}, + {"SEC_TDM_RX_3 Port Mixer", "QUIN_TDM_TX_0", "QUIN_TDM_TX_0"}, + {"SEC_TDM_RX_3 Port Mixer", "QUIN_TDM_TX_1", "QUIN_TDM_TX_1"}, + {"SEC_TDM_RX_3 Port Mixer", "QUIN_TDM_TX_2", "QUIN_TDM_TX_2"}, + {"SEC_TDM_RX_3 Port Mixer", "QUIN_TDM_TX_3", "QUIN_TDM_TX_3"}, + {"SEC_TDM_RX_3", NULL, "SEC_TDM_RX_3 Port Mixer"}, + + {"SEC_TDM_RX_7 Port Mixer", "TERT_TDM_TX_7", "TERT_TDM_TX_7"}, + {"SEC_TDM_RX_7", NULL, "SEC_TDM_RX_7 Port Mixer"}, + + {"TERT_TDM_RX_0 Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"TERT_TDM_RX_0 Port Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"}, + {"TERT_TDM_RX_0 Port Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + {"TERT_TDM_RX_0 Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"}, + {"TERT_TDM_RX_0 Port Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"}, + {"TERT_TDM_RX_0 Port Mixer", "AFE_PCM_TX", "PCM_TX"}, + {"TERT_TDM_RX_0 Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"}, + {"TERT_TDM_RX_0 Port Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"}, + {"TERT_TDM_RX_0 Port Mixer", "TERT_TDM_TX_0", "TERT_TDM_TX_0"}, + {"TERT_TDM_RX_0 Port Mixer", "TERT_TDM_TX_1", "TERT_TDM_TX_1"}, + {"TERT_TDM_RX_0 Port Mixer", "TERT_TDM_TX_2", "TERT_TDM_TX_2"}, + {"TERT_TDM_RX_0 Port Mixer", "TERT_TDM_TX_3", "TERT_TDM_TX_3"}, + {"TERT_TDM_RX_0 Port Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"}, + {"TERT_TDM_RX_0 Port Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"}, + {"TERT_TDM_RX_0 Port Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"}, + {"TERT_TDM_RX_0 Port Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"}, + {"TERT_TDM_RX_0 Port Mixer", "QUIN_TDM_TX_0", "QUIN_TDM_TX_0"}, + {"TERT_TDM_RX_0 Port Mixer", "QUIN_TDM_TX_1", "QUIN_TDM_TX_1"}, + {"TERT_TDM_RX_0 Port Mixer", "QUIN_TDM_TX_2", "QUIN_TDM_TX_2"}, + {"TERT_TDM_RX_0 Port Mixer", "QUIN_TDM_TX_3", "QUIN_TDM_TX_3"}, + {"TERT_TDM_RX_0", NULL, "TERT_TDM_RX_0 Port Mixer"}, + + {"TERT_TDM_RX_1 Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"TERT_TDM_RX_1 Port Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"}, + {"TERT_TDM_RX_1 Port Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + {"TERT_TDM_RX_1 Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"}, + {"TERT_TDM_RX_1 Port Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"}, + {"TERT_TDM_RX_1 Port Mixer", "AFE_PCM_TX", "PCM_TX"}, + {"TERT_TDM_RX_1 Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"}, + {"TERT_TDM_RX_1 Port Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"}, + {"TERT_TDM_RX_1 Port Mixer", "TERT_TDM_TX_0", "TERT_TDM_TX_0"}, + {"TERT_TDM_RX_1 Port Mixer", "TERT_TDM_TX_1", "TERT_TDM_TX_1"}, + {"TERT_TDM_RX_1 Port Mixer", "TERT_TDM_TX_2", "TERT_TDM_TX_2"}, + {"TERT_TDM_RX_1 Port Mixer", "TERT_TDM_TX_3", "TERT_TDM_TX_3"}, + {"TERT_TDM_RX_1 Port Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"}, + {"TERT_TDM_RX_1 Port Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"}, + {"TERT_TDM_RX_1 Port Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"}, + {"TERT_TDM_RX_1 Port Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"}, + {"TERT_TDM_RX_1 Port Mixer", "QUIN_TDM_TX_0", "QUIN_TDM_TX_0"}, + {"TERT_TDM_RX_1 Port Mixer", "QUIN_TDM_TX_1", "QUIN_TDM_TX_1"}, + {"TERT_TDM_RX_1 Port Mixer", "QUIN_TDM_TX_2", "QUIN_TDM_TX_2"}, + {"TERT_TDM_RX_1 Port Mixer", "QUIN_TDM_TX_3", "QUIN_TDM_TX_3"}, + {"TERT_TDM_RX_1", NULL, "TERT_TDM_RX_1 Port Mixer"}, + + {"TERT_TDM_RX_2 Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"TERT_TDM_RX_2 Port Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"}, + {"TERT_TDM_RX_2 Port Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + {"TERT_TDM_RX_2 Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"}, + {"TERT_TDM_RX_2 Port Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"}, + {"TERT_TDM_RX_2 Port Mixer", "AFE_PCM_TX", "PCM_TX"}, + {"TERT_TDM_RX_2 Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"}, + {"TERT_TDM_RX_2 Port Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"}, + {"TERT_TDM_RX_2 Port Mixer", "TERT_TDM_TX_0", "TERT_TDM_TX_0"}, + {"TERT_TDM_RX_2 Port Mixer", "TERT_TDM_TX_1", "TERT_TDM_TX_1"}, + {"TERT_TDM_RX_2 Port Mixer", "TERT_TDM_TX_2", "TERT_TDM_TX_2"}, + {"TERT_TDM_RX_2 Port Mixer", "TERT_TDM_TX_3", "TERT_TDM_TX_3"}, + {"TERT_TDM_RX_2 Port Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"}, + {"TERT_TDM_RX_2 Port Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"}, + {"TERT_TDM_RX_2 Port Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"}, + {"TERT_TDM_RX_2 Port Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"}, + {"TERT_TDM_RX_2 Port Mixer", "QUIN_TDM_TX_0", "QUIN_TDM_TX_0"}, + {"TERT_TDM_RX_2 Port Mixer", "QUIN_TDM_TX_1", "QUIN_TDM_TX_1"}, + {"TERT_TDM_RX_2 Port Mixer", "QUIN_TDM_TX_2", "QUIN_TDM_TX_2"}, + {"TERT_TDM_RX_2 Port Mixer", "QUIN_TDM_TX_3", "QUIN_TDM_TX_3"}, + {"TERT_TDM_RX_2", NULL, "TERT_TDM_RX_2 Port Mixer"}, + + {"TERT_TDM_RX_3 Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"TERT_TDM_RX_3 Port Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"}, + {"TERT_TDM_RX_3 Port Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + {"TERT_TDM_RX_3 Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"}, + {"TERT_TDM_RX_3 Port Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"}, + {"TERT_TDM_RX_3 Port Mixer", "AFE_PCM_TX", "PCM_TX"}, + {"TERT_TDM_RX_3 Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"}, + {"TERT_TDM_RX_3 Port Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"}, + {"TERT_TDM_RX_3 Port Mixer", "TERT_TDM_TX_0", "TERT_TDM_TX_0"}, + {"TERT_TDM_RX_3 Port Mixer", "TERT_TDM_TX_1", "TERT_TDM_TX_1"}, + {"TERT_TDM_RX_3 Port Mixer", "TERT_TDM_TX_2", "TERT_TDM_TX_2"}, + {"TERT_TDM_RX_3 Port Mixer", "TERT_TDM_TX_3", "TERT_TDM_TX_3"}, + {"TERT_TDM_RX_3 Port Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"}, + {"TERT_TDM_RX_3 Port Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"}, + {"TERT_TDM_RX_3 Port Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"}, + {"TERT_TDM_RX_3 Port Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"}, + {"TERT_TDM_RX_3 Port Mixer", "QUIN_TDM_TX_0", "QUIN_TDM_TX_0"}, + {"TERT_TDM_RX_3 Port Mixer", "QUIN_TDM_TX_1", "QUIN_TDM_TX_1"}, + {"TERT_TDM_RX_3 Port Mixer", "QUIN_TDM_TX_2", "QUIN_TDM_TX_2"}, + {"TERT_TDM_RX_3 Port Mixer", "QUIN_TDM_TX_3", "QUIN_TDM_TX_3"}, + {"TERT_TDM_RX_3", NULL, "TERT_TDM_RX_3 Port Mixer"}, + + {"QUAT_TDM_RX_0 Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"QUAT_TDM_RX_0 Port Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"}, + {"QUAT_TDM_RX_0 Port Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"}, + {"QUAT_TDM_RX_0 Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"}, + {"QUAT_TDM_RX_0 Port Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"}, + {"QUAT_TDM_RX_0 Port Mixer", "AFE_PCM_TX", "PCM_TX"}, + {"QUAT_TDM_RX_0 Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"}, + {"QUAT_TDM_RX_0 Port Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"}, + {"QUAT_TDM_RX_0 Port Mixer", "TERT_TDM_TX_0", "TERT_TDM_TX_0"}, + {"QUAT_TDM_RX_0 Port Mixer", "TERT_TDM_TX_1", "TERT_TDM_TX_1"}, + {"QUAT_TDM_RX_0 Port Mixer", "TERT_TDM_TX_2", "TERT_TDM_TX_2"}, + {"QUAT_TDM_RX_0 Port Mixer", "TERT_TDM_TX_3", "TERT_TDM_TX_3"}, + {"QUAT_TDM_RX_0 Port Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"}, + {"QUAT_TDM_RX_0 Port Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"}, + {"QUAT_TDM_RX_0 Port Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"}, + {"QUAT_TDM_RX_0 Port Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"}, + {"QUAT_TDM_RX_0 Port Mixer", "QUIN_TDM_TX_0", "QUIN_TDM_TX_0"}, + {"QUAT_TDM_RX_0 Port Mixer", "QUIN_TDM_TX_1", "QUIN_TDM_TX_1"}, + {"QUAT_TDM_RX_0 Port Mixer", "QUIN_TDM_TX_2", "QUIN_TDM_TX_2"}, + {"QUAT_TDM_RX_0 Port Mixer", "QUIN_TDM_TX_3", "QUIN_TDM_TX_3"}, + {"QUAT_TDM_RX_0", NULL, "QUAT_TDM_RX_0 Port Mixer"}, + + {"QUAT_TDM_RX_1 Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"QUAT_TDM_RX_1 Port Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"}, + {"QUAT_TDM_RX_1 Port Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"}, + {"QUAT_TDM_RX_1 Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"}, + {"QUAT_TDM_RX_1 Port Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"}, + {"QUAT_TDM_RX_1 Port Mixer", "AFE_PCM_TX", "PCM_TX"}, + {"QUAT_TDM_RX_1 Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"}, + {"QUAT_TDM_RX_1 Port Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"}, + {"QUAT_TDM_RX_1 Port Mixer", "TERT_TDM_TX_0", "TERT_TDM_TX_0"}, + {"QUAT_TDM_RX_1 Port Mixer", "TERT_TDM_TX_1", "TERT_TDM_TX_1"}, + {"QUAT_TDM_RX_1 Port Mixer", "TERT_TDM_TX_2", "TERT_TDM_TX_2"}, + {"QUAT_TDM_RX_1 Port Mixer", "TERT_TDM_TX_3", "TERT_TDM_TX_3"}, + {"QUAT_TDM_RX_1 Port Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"}, + {"QUAT_TDM_RX_1 Port Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"}, + {"QUAT_TDM_RX_1 Port Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"}, + {"QUAT_TDM_RX_1 Port Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"}, + {"QUAT_TDM_RX_1 Port Mixer", "QUIN_TDM_TX_0", "QUIN_TDM_TX_0"}, + {"QUAT_TDM_RX_1 Port Mixer", "QUIN_TDM_TX_1", "QUIN_TDM_TX_1"}, + {"QUAT_TDM_RX_1 Port Mixer", "QUIN_TDM_TX_2", "QUIN_TDM_TX_2"}, + {"QUAT_TDM_RX_1 Port Mixer", "QUIN_TDM_TX_3", "QUIN_TDM_TX_3"}, + {"QUAT_TDM_RX_1", NULL, "QUAT_TDM_RX_1 Port Mixer"}, + + {"QUAT_TDM_RX_2 Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"QUAT_TDM_RX_2 Port Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"}, + {"QUAT_TDM_RX_2 Port Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"}, + {"QUAT_TDM_RX_2 Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"}, + {"QUAT_TDM_RX_2 Port Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"}, + {"QUAT_TDM_RX_2 Port Mixer", "AFE_PCM_TX", "PCM_TX"}, + {"QUAT_TDM_RX_2 Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"}, + {"QUAT_TDM_RX_2 Port Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"}, + {"QUAT_TDM_RX_2 Port Mixer", "TERT_TDM_TX_0", "TERT_TDM_TX_0"}, + {"QUAT_TDM_RX_2 Port Mixer", "TERT_TDM_TX_1", "TERT_TDM_TX_1"}, + {"QUAT_TDM_RX_2 Port Mixer", "TERT_TDM_TX_2", "TERT_TDM_TX_2"}, + {"QUAT_TDM_RX_2 Port Mixer", "TERT_TDM_TX_3", "TERT_TDM_TX_3"}, + {"QUAT_TDM_RX_2 Port Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"}, + {"QUAT_TDM_RX_2 Port Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"}, + {"QUAT_TDM_RX_2 Port Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"}, + {"QUAT_TDM_RX_2 Port Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"}, + {"QUAT_TDM_RX_2 Port Mixer", "QUIN_TDM_TX_0", "QUIN_TDM_TX_0"}, + {"QUAT_TDM_RX_2 Port Mixer", "QUIN_TDM_TX_1", "QUIN_TDM_TX_1"}, + {"QUAT_TDM_RX_2 Port Mixer", "QUIN_TDM_TX_2", "QUIN_TDM_TX_2"}, + {"QUAT_TDM_RX_2 Port Mixer", "QUIN_TDM_TX_3", "QUIN_TDM_TX_3"}, + {"QUAT_TDM_RX_2", NULL, "QUAT_TDM_RX_2 Port Mixer"}, + + {"QUAT_TDM_RX_3 Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"QUAT_TDM_RX_3 Port Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"}, + {"QUAT_TDM_RX_3 Port Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"}, + {"QUAT_TDM_RX_3 Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"}, + {"QUAT_TDM_RX_3 Port Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"}, + {"QUAT_TDM_RX_3 Port Mixer", "AFE_PCM_TX", "PCM_TX"}, + {"QUAT_TDM_RX_3 Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"}, + {"QUAT_TDM_RX_3 Port Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"}, + {"QUAT_TDM_RX_3 Port Mixer", "TERT_TDM_TX_0", "TERT_TDM_TX_0"}, + {"QUAT_TDM_RX_3 Port Mixer", "TERT_TDM_TX_1", "TERT_TDM_TX_1"}, + {"QUAT_TDM_RX_3 Port Mixer", "TERT_TDM_TX_2", "TERT_TDM_TX_2"}, + {"QUAT_TDM_RX_3 Port Mixer", "TERT_TDM_TX_3", "TERT_TDM_TX_3"}, + {"QUAT_TDM_RX_3 Port Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"}, + {"QUAT_TDM_RX_3 Port Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"}, + {"QUAT_TDM_RX_3 Port Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"}, + {"QUAT_TDM_RX_3 Port Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"}, + {"QUAT_TDM_RX_3 Port Mixer", "QUIN_TDM_TX_0", "QUIN_TDM_TX_0"}, + {"QUAT_TDM_RX_3 Port Mixer", "QUIN_TDM_TX_1", "QUIN_TDM_TX_1"}, + {"QUAT_TDM_RX_3 Port Mixer", "QUIN_TDM_TX_2", "QUIN_TDM_TX_2"}, + {"QUAT_TDM_RX_3 Port Mixer", "QUIN_TDM_TX_3", "QUIN_TDM_TX_3"}, + {"QUAT_TDM_RX_3", NULL, "QUAT_TDM_RX_3 Port Mixer"}, + + {"QUIN_TDM_RX_0 Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"QUIN_TDM_RX_0 Port Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"}, + {"QUIN_TDM_RX_0 Port Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"}, + {"QUIN_TDM_RX_0 Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"}, + {"QUIN_TDM_RX_0 Port Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"}, + {"QUIN_TDM_RX_0 Port Mixer", "AFE_PCM_TX", "PCM_TX"}, + {"QUIN_TDM_RX_0 Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"}, + {"QUIN_TDM_RX_0 Port Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"}, + {"QUIN_TDM_RX_0 Port Mixer", "TERT_TDM_TX_0", "TERT_TDM_TX_0"}, + {"QUIN_TDM_RX_0 Port Mixer", "TERT_TDM_TX_1", "TERT_TDM_TX_1"}, + {"QUIN_TDM_RX_0 Port Mixer", "TERT_TDM_TX_2", "TERT_TDM_TX_2"}, + {"QUIN_TDM_RX_0 Port Mixer", "TERT_TDM_TX_3", "TERT_TDM_TX_3"}, + {"QUIN_TDM_RX_0 Port Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"}, + {"QUIN_TDM_RX_0 Port Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"}, + {"QUIN_TDM_RX_0 Port Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"}, + {"QUIN_TDM_RX_0 Port Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"}, + {"QUIN_TDM_RX_0 Port Mixer", "QUIN_TDM_TX_0", "QUIN_TDM_TX_0"}, + {"QUIN_TDM_RX_0 Port Mixer", "QUIN_TDM_TX_1", "QUIN_TDM_TX_1"}, + {"QUIN_TDM_RX_0 Port Mixer", "QUIN_TDM_TX_2", "QUIN_TDM_TX_2"}, + {"QUIN_TDM_RX_0 Port Mixer", "QUIN_TDM_TX_3", "QUIN_TDM_TX_3"}, + {"QUIN_TDM_RX_0", NULL, "QUIN_TDM_RX_0 Port Mixer"}, + + {"QUIN_TDM_RX_1 Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"QUIN_TDM_RX_1 Port Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"}, + {"QUIN_TDM_RX_1 Port Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"}, + {"QUIN_TDM_RX_1 Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"}, + {"QUIN_TDM_RX_1 Port Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"}, + {"QUIN_TDM_RX_1 Port Mixer", "AFE_PCM_TX", "PCM_TX"}, + {"QUIN_TDM_RX_1 Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"}, + {"QUIN_TDM_RX_1 Port Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"}, + {"QUIN_TDM_RX_1 Port Mixer", "TERT_TDM_TX_0", "TERT_TDM_TX_0"}, + {"QUIN_TDM_RX_1 Port Mixer", "TERT_TDM_TX_1", "TERT_TDM_TX_1"}, + {"QUIN_TDM_RX_1 Port Mixer", "TERT_TDM_TX_2", "TERT_TDM_TX_2"}, + {"QUIN_TDM_RX_1 Port Mixer", "TERT_TDM_TX_3", "TERT_TDM_TX_3"}, + {"QUIN_TDM_RX_1 Port Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"}, + {"QUIN_TDM_RX_1 Port Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"}, + {"QUIN_TDM_RX_1 Port Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"}, + {"QUIN_TDM_RX_1 Port Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"}, + {"QUIN_TDM_RX_1 Port Mixer", "QUIN_TDM_TX_0", "QUIN_TDM_TX_0"}, + {"QUIN_TDM_RX_1 Port Mixer", "QUIN_TDM_TX_1", "QUIN_TDM_TX_1"}, + {"QUIN_TDM_RX_1 Port Mixer", "QUIN_TDM_TX_2", "QUIN_TDM_TX_2"}, + {"QUIN_TDM_RX_1 Port Mixer", "QUIN_TDM_TX_3", "QUIN_TDM_TX_3"}, + {"QUIN_TDM_RX_1", NULL, "QUIN_TDM_RX_1 Port Mixer"}, + + {"QUIN_TDM_RX_2 Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"QUIN_TDM_RX_2 Port Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"}, + {"QUIN_TDM_RX_2 Port Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"}, + {"QUIN_TDM_RX_2 Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"}, + {"QUIN_TDM_RX_2 Port Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"}, + {"QUIN_TDM_RX_2 Port Mixer", "AFE_PCM_TX", "PCM_TX"}, + {"QUIN_TDM_RX_2 Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"}, + {"QUIN_TDM_RX_2 Port Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"}, + {"QUIN_TDM_RX_2 Port Mixer", "TERT_TDM_TX_0", "TERT_TDM_TX_0"}, + {"QUIN_TDM_RX_2 Port Mixer", "TERT_TDM_TX_1", "TERT_TDM_TX_1"}, + {"QUIN_TDM_RX_2 Port Mixer", "TERT_TDM_TX_2", "TERT_TDM_TX_2"}, + {"QUIN_TDM_RX_2 Port Mixer", "TERT_TDM_TX_3", "TERT_TDM_TX_3"}, + {"QUIN_TDM_RX_2 Port Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"}, + {"QUIN_TDM_RX_2 Port Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"}, + {"QUIN_TDM_RX_2 Port Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"}, + {"QUIN_TDM_RX_2 Port Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"}, + {"QUIN_TDM_RX_2 Port Mixer", "QUIN_TDM_TX_0", "QUIN_TDM_TX_0"}, + {"QUIN_TDM_RX_2 Port Mixer", "QUIN_TDM_TX_1", "QUIN_TDM_TX_1"}, + {"QUIN_TDM_RX_2 Port Mixer", "QUIN_TDM_TX_2", "QUIN_TDM_TX_2"}, + {"QUIN_TDM_RX_2 Port Mixer", "QUIN_TDM_TX_3", "QUIN_TDM_TX_3"}, + {"QUIN_TDM_RX_2", NULL, "QUIN_TDM_RX_2 Port Mixer"}, + + {"QUIN_TDM_RX_3 Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"QUIN_TDM_RX_3 Port Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"}, + {"QUIN_TDM_RX_3 Port Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"}, + {"QUIN_TDM_RX_3 Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"}, + {"QUIN_TDM_RX_3 Port Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"}, + {"QUIN_TDM_RX_3 Port Mixer", "AFE_PCM_TX", "PCM_TX"}, + {"QUIN_TDM_RX_3 Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"}, + {"QUIN_TDM_RX_3 Port Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"}, + {"QUIN_TDM_RX_3 Port Mixer", "TERT_TDM_TX_0", "TERT_TDM_TX_0"}, + {"QUIN_TDM_RX_3 Port Mixer", "TERT_TDM_TX_1", "TERT_TDM_TX_1"}, + {"QUIN_TDM_RX_3 Port Mixer", "TERT_TDM_TX_2", "TERT_TDM_TX_2"}, + {"QUIN_TDM_RX_3 Port Mixer", "TERT_TDM_TX_3", "TERT_TDM_TX_3"}, + {"QUIN_TDM_RX_3 Port Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"}, + {"QUIN_TDM_RX_3 Port Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"}, + {"QUIN_TDM_RX_3 Port Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"}, + {"QUIN_TDM_RX_3 Port Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"}, + {"QUIN_TDM_RX_3 Port Mixer", "QUIN_TDM_TX_0", "QUIN_TDM_TX_0"}, + {"QUIN_TDM_RX_3 Port Mixer", "QUIN_TDM_TX_1", "QUIN_TDM_TX_1"}, + {"QUIN_TDM_RX_3 Port Mixer", "QUIN_TDM_TX_2", "QUIN_TDM_TX_2"}, + {"QUIN_TDM_RX_3 Port Mixer", "QUIN_TDM_TX_3", "QUIN_TDM_TX_3"}, + {"QUIN_TDM_RX_3", NULL, "QUIN_TDM_RX_3 Port Mixer"}, + + {"INT0_MI2S_RX Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"INT0_MI2S_RX Port Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"}, + {"INT0_MI2S_RX Port Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"}, + {"INT0_MI2S_RX Port Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + {"INT0_MI2S_RX Port Mixer", "QUIN_MI2S_TX", "QUIN_MI2S_TX"}, + {"INT0_MI2S_RX Port Mixer", "INT3_MI2S_TX", "INT3_MI2S_TX"}, + {"INT0_MI2S_RX Port Mixer", "SLIM_7_TX", "SLIMBUS_7_TX"}, + {"INT0_MI2S_RX Port Mixer", "SLIM_8_TX", "SLIMBUS_8_TX"}, + {"INT0_MI2S_RX Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"}, + {"INT0_MI2S_RX Port Mixer", "SLIM_9_TX", "SLIMBUS_9_TX"}, + {"INT0_MI2S_RX Port Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"}, + {"INT0_MI2S_RX", NULL, "INT0_MI2S_RX Port Mixer"}, + + {"INT4_MI2S_RX Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"INT4_MI2S_RX Port Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"}, + {"INT4_MI2S_RX Port Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"}, + {"INT4_MI2S_RX Port Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + {"INT4_MI2S_RX Port Mixer", "QUIN_MI2S_TX", "QUIN_MI2S_TX"}, + {"INT4_MI2S_RX Port Mixer", "INT3_MI2S_TX", "INT3_MI2S_TX"}, + {"INT4_MI2S_RX Port Mixer", "SLIM_7_TX", "SLIMBUS_7_TX"}, + {"INT4_MI2S_RX Port Mixer", "SLIM_8_TX", "SLIMBUS_8_TX"}, + {"INT4_MI2S_RX Port Mixer", "SLIM_9_TX", "SLIMBUS_9_TX"}, + {"INT4_MI2S_RX Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"}, + {"INT4_MI2S_RX Port Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"}, + {"INT4_MI2S_RX", NULL, "INT4_MI2S_RX Port Mixer"}, + + {"WSA_CDC_DMA_RX_0 Port Mixer", "VA_CDC_DMA_TX_0", "VA_CDC_DMA_TX_0"}, + {"WSA_CDC_DMA_RX_0 Port Mixer", "TX_CDC_DMA_TX_3", "TX_CDC_DMA_TX_3"}, + {"RX_CDC_DMA_RX_0 Port Mixer", "TX_CDC_DMA_TX_3", "TX_CDC_DMA_TX_3"}, + {"WSA_CDC_DMA_RX_0 Port Mixer", "SLIM_8_TX", "SLIMBUS_8_TX"}, + {"RX_CDC_DMA_RX_0 Port Mixer", "SLIM_8_TX", "SLIMBUS_8_TX"}, + + + {"SLIMBUS_0_RX Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"}, + {"SLIMBUS_0_RX Port Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"}, + {"SLIMBUS_0_RX Port Mixer", "SLIM_1_TX", "SLIMBUS_1_TX"}, + {"SLIMBUS_0_RX Port Mixer", "SLIM_7_TX", "SLIMBUS_7_TX"}, + {"SLIMBUS_0_RX Port Mixer", "SLIM_8_TX", "SLIMBUS_8_TX"}, + {"SLIMBUS_0_RX Port Mixer", "SLIM_9_TX", "SLIMBUS_9_TX"}, + {"SLIMBUS_0_RX Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"}, + {"SLIMBUS_0_RX Port Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"}, + {"SLIMBUS_0_RX Port Mixer", "TERT_AUXPCM_UL_TX", "TERT_AUX_PCM_TX"}, + {"SLIMBUS_0_RX Port Mixer", "QUAT_AUXPCM_UL_TX", "QUAT_AUX_PCM_TX"}, + {"SLIMBUS_0_RX Port Mixer", "QUIN_AUXPCM_UL_TX", "QUIN_AUX_PCM_TX"}, + {"SLIMBUS_0_RX Port Mixer", "MI2S_TX", "MI2S_TX"}, + {"SLIMBUS_0_RX Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"SLIMBUS_0_RX Port Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"}, + {"SLIMBUS_0_RX Port Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"}, + {"SLIMBUS_0_RX Port Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + {"SLIMBUS_0_RX Port Mixer", "QUIN_MI2S_TX", "QUIN_MI2S_TX"}, + {"SLIMBUS_0_RX Port Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"}, + {"SLIMBUS_0_RX", NULL, "SLIMBUS_0_RX Port Mixer"}, + {"AFE_PCM_RX Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"}, + {"AFE_PCM_RX Port Mixer", "SLIM_1_TX", "SLIMBUS_1_TX"}, + {"PCM_RX", NULL, "AFE_PCM_RX Port Mixer"}, + {"USB_AUDIO_RX Port Mixer", "USB_AUDIO_TX", "USB_AUDIO_TX"}, + {"USB_AUDIO_RX", NULL, "USB_AUDIO_RX Port Mixer"}, + {"USB_DL_HL", "Switch", "USBAUDIO_DL_HL"}, + {"USB_AUDIO_RX", NULL, "USB_DL_HL"}, + {"USBAUDIO_UL_HL", NULL, "USB_AUDIO_TX"}, + + + {"AUX_PCM_RX Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"}, + {"AUX_PCM_RX Port Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"}, + {"AUX_PCM_RX Port Mixer", "SLIM_1_TX", "SLIMBUS_1_TX"}, + {"AUX_PCM_RX Port Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"}, + {"AUX_PCM_RX Port Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"}, + {"AUX_PCM_RX Port Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"}, + {"AUX_PCM_RX", NULL, "AUX_PCM_RX Port Mixer"}, + + {"SEC_AUXPCM_RX Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"}, + {"SEC_AUXPCM_RX Port Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"}, + {"SEC_AUXPCM_RX Port Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"}, + {"SEC_AUXPCM_RX Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"SEC_AUX_PCM_RX", NULL, "SEC_AUXPCM_RX Port Mixer"}, + + {"TERT_AUXPCM_RX Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"}, + {"TERT_AUXPCM_RX Port Mixer", "TERT_AUXPCM_UL_TX", "TERT_AUX_PCM_TX"}, + {"TERT_AUXPCM_RX Port Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"}, + {"TERT_AUX_PCM_RX", NULL, "TERT_AUXPCM_RX Port Mixer"}, + + {"QUAT_AUXPCM_RX Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"}, + {"QUAT_AUXPCM_RX Port Mixer", "QUAT_AUXPCM_UL_TX", "QUAT_AUX_PCM_TX"}, + {"QUAT_AUXPCM_RX Port Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"}, + {"QUAT_AUX_PCM_RX", NULL, "QUAT_AUXPCM_RX Port Mixer"}, + + {"QUIN_AUXPCM_RX Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"}, + {"QUIN_AUXPCM_RX Port Mixer", "QUIN_AUXPCM_UL_TX", "QUIN_AUX_PCM_TX"}, + {"QUIN_AUXPCM_RX Port Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"}, + {"QUIN_AUX_PCM_RX", NULL, "QUIN_AUXPCM_RX Port Mixer"}, + + {"Voice Stub Tx Mixer", "STUB_TX_HL", "STUB_TX"}, + {"Voice Stub Tx Mixer", "SLIM_1_TX", "SLIMBUS_1_TX"}, + {"Voice Stub Tx Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"}, + {"Voice Stub Tx Mixer", "STUB_1_TX_HL", "STUB_1_TX"}, + {"Voice Stub Tx Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"}, + {"Voice Stub Tx Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"}, + {"Voice Stub Tx Mixer", "TERT_AUXPCM_UL_TX", "TERT_AUX_PCM_TX"}, + {"Voice Stub Tx Mixer", "QUAT_AUXPCM_UL_TX", "QUAT_AUX_PCM_TX"}, + {"Voice Stub Tx Mixer", "QUIN_AUXPCM_UL_TX", "QUIN_AUX_PCM_TX"}, + {"Voice Stub Tx Mixer", "MI2S_TX", "MI2S_TX"}, + {"Voice Stub Tx Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"Voice Stub Tx Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"}, + {"Voice Stub Tx Mixer", "INT3_MI2S_TX", "INT3_MI2S_TX"}, + {"Voice Stub Tx Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + {"Voice Stub Tx Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"}, + {"Voice Stub Tx Mixer", "SLIM_3_TX", "SLIMBUS_3_TX"}, + {"Voice Stub Tx Mixer", "SLIM_7_TX", "SLIMBUS_7_TX"}, + {"Voice Stub Tx Mixer", "SLIM_8_TX", "SLIMBUS_8_TX"}, + {"Voice Stub Tx Mixer", "AFE_PCM_TX", "PCM_TX"}, + {"VOICE_STUB_UL", NULL, "Voice Stub Tx Mixer"}, + + {"VoLTE Stub Tx Mixer", "STUB_TX_HL", "STUB_TX"}, + {"VoLTE Stub Tx Mixer", "SLIM_1_TX", "SLIMBUS_1_TX"}, + {"VoLTE Stub Tx Mixer", "STUB_1_TX_HL", "STUB_1_TX"}, + {"VoLTE Stub Tx Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"}, + {"VoLTE Stub Tx Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"}, + {"VoLTE Stub Tx Mixer", "SLIM_3_TX", "SLIMBUS_3_TX"}, + {"VoLTE Stub Tx Mixer", "SLIM_7_TX", "SLIMBUS_7_TX"}, + {"VoLTE Stub Tx Mixer", "SLIM_8_TX", "SLIMBUS_8_TX"}, + {"VoLTE Stub Tx Mixer", "AFE_PCM_TX", "PCM_TX"}, + {"VoLTE Stub Tx Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"VoLTE Stub Tx Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + {"VOLTE_STUB_UL", NULL, "VoLTE Stub Tx Mixer"}, + + {"Voice2 Stub Tx Mixer", "STUB_TX_HL", "STUB_TX"}, + {"Voice2 Stub Tx Mixer", "SLIM_1_TX", "SLIMBUS_1_TX"}, + {"Voice2 Stub Tx Mixer", "STUB_1_TX_HL", "STUB_1_TX"}, + {"Voice2 Stub Tx Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"}, + {"Voice2 Stub Tx Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"}, + {"Voice2 Stub Tx Mixer", "SLIM_3_TX", "SLIMBUS_3_TX"}, + {"Voice2 Stub Tx Mixer", "SLIM_7_TX", "SLIMBUS_7_TX"}, + {"Voice2 Stub Tx Mixer", "SLIM_8_TX", "SLIMBUS_8_TX"}, + {"Voice2 Stub Tx Mixer", "AFE_PCM_TX", "PCM_TX"}, + {"Voice2 Stub Tx Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"Voice2 Stub Tx Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + {"VOICE2_STUB_UL", NULL, "Voice2 Stub Tx Mixer"}, + + {"STUB_RX Mixer", "VoiceMMode1", "VOICEMMODE1_DL"}, + {"STUB_RX Mixer", "VoiceMMode2", "VOICEMMODE2_DL"}, + {"STUB_RX", NULL, "STUB_RX Mixer"}, + + {"SLIMBUS_1_RX Mixer", "VoiceMMode1", "VOICEMMODE1_DL"}, + {"SLIMBUS_1_RX Mixer", "VoiceMMode2", "VOICEMMODE2_DL"}, + {"SLIMBUS_1_RX", NULL, "SLIMBUS_1_RX Mixer"}, + + {"SLIMBUS_3_RX_Voice Mixer", "VoiceMMode1", "VOICEMMODE1_DL"}, + {"SLIMBUS_3_RX_Voice Mixer", "VoiceMMode2", "VOICEMMODE2_DL"}, + {"SLIMBUS_3_RX", NULL, "SLIMBUS_3_RX_Voice Mixer"}, + + {"SLIM_7_RX_Voice Mixer", "Voip", "VOIP_DL"}, + {"SLIM_7_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"}, + {"SLIM_7_RX_Voice Mixer", "QCHAT", "QCHAT_DL"}, + {"SLIM_7_RX_Voice Mixer", "VoiceMMode1", "VOICEMMODE1_DL"}, + {"SLIM_7_RX_Voice Mixer", "VoiceMMode2", "VOICEMMODE2_DL"}, + {"SLIMBUS_7_RX", NULL, "SLIM_7_RX_Voice Mixer"}, + + {"SLIM_8_RX_Voice Mixer", "Voip", "VOIP_DL"}, + {"SLIM_8_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"}, + {"SLIM_8_RX_Voice Mixer", "QCHAT", "QCHAT_DL"}, + {"SLIM_8_RX_Voice Mixer", "VoiceMMode1", "VOICEMMODE1_DL"}, + {"SLIM_8_RX_Voice Mixer", "VoiceMMode2", "VOICEMMODE2_DL"}, + {"SLIMBUS_8_RX", NULL, "SLIM_8_RX_Voice Mixer"}, + + {"SLIMBUS_1_RX Port Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"}, + {"SLIMBUS_1_RX Port Mixer", "AFE_PCM_TX", "PCM_TX"}, + {"SLIMBUS_1_RX Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"}, + {"SLIMBUS_1_RX", NULL, "SLIMBUS_1_RX Port Mixer"}, + {"INTERNAL_BT_SCO_RX Port Mixer", "SLIM_1_TX", "SLIMBUS_1_TX"}, + {"INTERNAL_BT_SCO_RX Port Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"}, + {"INT_BT_SCO_RX", NULL, "INTERNAL_BT_SCO_RX Port Mixer"}, + {"SLIMBUS_3_RX Port Mixer", "INTERNAL_BT_SCO_RX", "INT_BT_SCO_RX"}, + {"SLIMBUS_3_RX Port Mixer", "MI2S_TX", "MI2S_TX"}, + {"SLIMBUS_3_RX Port Mixer", "AFE_PCM_RX", "PCM_RX"}, + {"SLIMBUS_3_RX Port Mixer", "AUX_PCM_RX", "AUX_PCM_RX"}, + {"SLIMBUS_3_RX Port Mixer", "SLIM_0_RX", "SLIMBUS_0_RX"}, + {"SLIMBUS_3_RX", NULL, "SLIMBUS_3_RX Port Mixer"}, + + {"SLIMBUS_6_RX Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"}, + {"SLIMBUS_6_RX Port Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"}, + {"SLIMBUS_6_RX Port Mixer", "SLIM_1_TX", "SLIMBUS_1_TX"}, + {"SLIMBUS_6_RX Port Mixer", "SLIM_7_TX", "SLIMBUS_7_TX"}, + {"SLIMBUS_6_RX Port Mixer", "SLIM_8_TX", "SLIMBUS_8_TX"}, + {"SLIMBUS_6_RX Port Mixer", "SLIM_9_TX", "SLIMBUS_9_TX"}, + {"SLIMBUS_6_RX Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"}, + {"SLIMBUS_6_RX Port Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"}, + {"SLIMBUS_6_RX Port Mixer", "MI2S_TX", "MI2S_TX"}, + {"SLIMBUS_6_RX Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"SLIMBUS_6_RX Port Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"}, + {"SLIMBUS_6_RX Port Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + {"SLIMBUS_6_RX Port Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"}, + {"SLIMBUS_6_RX", NULL, "SLIMBUS_6_RX Port Mixer"}, + + {"HDMI_RX Port Mixer", "MI2S_TX", "MI2S_TX"}, + {"HDMI", NULL, "HDMI_RX Port Mixer"}, + + {"DISPLAY_PORT_RX Port Mixer", "MI2S_TX", "MI2S_TX"}, + {"DISPLAY_PORT", NULL, "DISPLAY_PORT_RX Port Mixer"}, + + {"DISPLAY_PORT_RX1 Port Mixer", "MI2S_TX", "MI2S_TX"}, + {"DISPLAY_PORT1", NULL, "DISPLAY_PORT_RX1 Port Mixer"}, + + {"SEC_I2S_RX Port Mixer", "MI2S_TX", "MI2S_TX"}, + {"SEC_I2S_RX", NULL, "SEC_I2S_RX Port Mixer"}, + + {"MI2S_RX Port Mixer", "SLIM_1_TX", "SLIMBUS_1_TX"}, + {"MI2S_RX Port Mixer", "MI2S_TX", "MI2S_TX"}, + {"MI2S_RX", NULL, "MI2S_RX Port Mixer"}, + + {"PRI_MI2S_RX Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"PRI_MI2S_RX Port Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"}, + {"PRI_MI2S_RX Port Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + {"PRI_MI2S_RX Port Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"}, + {"PRI_MI2S_RX Port Mixer", "QUIN_MI2S_TX", "QUIN_MI2S_TX"}, + {"PRI_MI2S_RX Port Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"}, + {"PRI_MI2S_RX Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"}, + {"PRI_MI2S_RX Port Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"}, + {"PRI_MI2S_RX Port Mixer", "SLIM_8_TX", "SLIMBUS_8_TX"}, + {"PRI_MI2S_RX Port Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"}, + {"PRI_MI2S_RX", NULL, "PRI_MI2S_RX Port Mixer"}, + + {"SEC_MI2S_RX Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"SEC_MI2S_RX Port Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"}, + {"SEC_MI2S_RX Port Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"}, + {"SEC_MI2S_RX Port Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + {"SEC_MI2S_RX Port Mixer", "QUIN_MI2S_TX", "QUIN_MI2S_TX"}, + {"SEC_MI2S_RX Port Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"}, + {"SEC_MI2S_RX Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"}, + {"SEC_MI2S_RX Port Mixer", "SLIM_8_TX", "SLIMBUS_8_TX"}, + {"SEC_MI2S_RX Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"}, + {"SEC_MI2S_RX", NULL, "SEC_MI2S_RX Port Mixer"}, + + {"TERT_MI2S_RX Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"TERT_MI2S_RX Port Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"}, + {"TERT_MI2S_RX Port Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"}, + {"TERT_MI2S_RX Port Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + {"TERT_MI2S_RX Port Mixer", "QUIN_MI2S_TX", "QUIN_MI2S_TX"}, + {"TERT_MI2S_RX Port Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"}, + {"TERT_MI2S_RX Port Mixer", "SLIM_8_TX", "SLIMBUS_8_TX"}, + {"TERT_MI2S_RX", NULL, "TERT_MI2S_RX Port Mixer"}, + + {"QUAT_MI2S_RX Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"QUAT_MI2S_RX Port Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"}, + {"QUAT_MI2S_RX Port Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"}, + {"QUAT_MI2S_RX Port Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + {"QUAT_MI2S_RX Port Mixer", "QUIN_MI2S_TX", "QUIN_MI2S_TX"}, + {"QUAT_MI2S_RX Port Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"}, + {"QUAT_MI2S_RX Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"}, + {"QUAT_MI2S_RX Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"}, + {"QUAT_MI2S_RX Port Mixer", "SLIM_8_TX", "SLIMBUS_8_TX"}, + {"QUAT_MI2S_RX", NULL, "QUAT_MI2S_RX Port Mixer"}, + + {"QUIN_MI2S_RX Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"QUIN_MI2S_RX Port Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"}, + {"QUIN_MI2S_RX Port Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"}, + {"QUIN_MI2S_RX Port Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + {"QUIN_MI2S_RX Port Mixer", "QUIN_MI2S_TX", "QUIN_MI2S_TX"}, + {"QUIN_MI2S_RX Port Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"}, + {"QUIN_MI2S_RX Port Mixer", "SLIM_8_TX", "SLIMBUS_8_TX"}, + {"QUIN_MI2S_RX", NULL, "QUIN_MI2S_RX Port Mixer"}, + + /* Backend Enablement */ + + {"BE_OUT", NULL, "PRI_I2S_RX"}, + {"BE_OUT", NULL, "SEC_I2S_RX"}, + {"BE_OUT", NULL, "SLIMBUS_0_RX"}, + {"BE_OUT", NULL, "SLIMBUS_1_RX"}, + {"BE_OUT", NULL, "SLIMBUS_2_RX"}, + {"BE_OUT", NULL, "SLIMBUS_3_RX"}, + {"BE_OUT", NULL, "SLIMBUS_4_RX"}, + {"BE_OUT", NULL, "SLIMBUS_5_RX"}, + {"BE_OUT", NULL, "SLIMBUS_6_RX"}, + {"BE_OUT", NULL, "SLIMBUS_7_RX"}, + {"BE_OUT", NULL, "SLIMBUS_8_RX"}, + {"BE_OUT", NULL, "SLIMBUS_9_RX"}, + {"BE_OUT", NULL, "USB_AUDIO_RX"}, + {"BE_OUT", NULL, "HDMI"}, + {"BE_OUT", NULL, "DISPLAY_PORT"}, + {"BE_OUT", NULL, "DISPLAY_PORT1"}, + {"BE_OUT", NULL, "PRI_SPDIF_RX"}, + {"BE_OUT", NULL, "SEC_SPDIF_RX"}, + {"BE_OUT", NULL, "MI2S_RX"}, + {"BE_OUT", NULL, "QUAT_MI2S_RX"}, + {"BE_OUT", NULL, "QUIN_MI2S_RX"}, + {"BE_OUT", NULL, "TERT_MI2S_RX"}, + {"BE_OUT", NULL, "SEC_MI2S_RX"}, + {"BE_OUT", NULL, "SEC_MI2S_RX_SD1"}, + {"BE_OUT", NULL, "PRI_MI2S_RX"}, + {"BE_OUT", NULL, "INT0_MI2S_RX"}, + {"BE_OUT", NULL, "INT4_MI2S_RX"}, + {"BE_OUT", NULL, "INT2_MI2S_RX"}, + {"BE_OUT", NULL, "INT3_MI2S_RX"}, + {"BE_OUT", NULL, "INT5_MI2S_RX"}, + {"BE_OUT", NULL, "INT_BT_SCO_RX"}, + {"BE_OUT", NULL, "INT_BT_A2DP_RX"}, + {"BE_OUT", NULL, "INT_FM_RX"}, + {"BE_OUT", NULL, "PCM_RX"}, + {"BE_OUT", NULL, "SLIMBUS_3_RX"}, + {"BE_OUT", NULL, "AUX_PCM_RX"}, + {"BE_OUT", NULL, "SEC_AUX_PCM_RX"}, + {"BE_OUT", NULL, "TERT_AUX_PCM_RX"}, + {"BE_OUT", NULL, "QUAT_AUX_PCM_RX"}, + {"BE_OUT", NULL, "QUIN_AUX_PCM_RX"}, + {"BE_OUT", NULL, "INT_BT_SCO_RX"}, + {"BE_OUT", NULL, "INT_FM_RX"}, + {"BE_OUT", NULL, "PCM_RX"}, + {"BE_OUT", NULL, "SLIMBUS_3_RX"}, + {"BE_OUT", NULL, "VOICE_PLAYBACK_TX"}, + {"BE_OUT", NULL, "VOICE2_PLAYBACK_TX"}, + {"BE_OUT", NULL, "PRI_TDM_RX_0"}, + {"BE_OUT", NULL, "PRI_TDM_RX_1"}, + {"BE_OUT", NULL, "PRI_TDM_RX_2"}, + {"BE_OUT", NULL, "PRI_TDM_RX_3"}, + {"BE_OUT", NULL, "SEC_TDM_RX_0"}, + {"BE_OUT", NULL, "SEC_TDM_RX_1"}, + {"BE_OUT", NULL, "SEC_TDM_RX_2"}, + {"BE_OUT", NULL, "SEC_TDM_RX_3"}, + {"BE_OUT", NULL, "SEC_TDM_RX_7"}, + {"BE_OUT", NULL, "TERT_TDM_RX_0"}, + {"BE_OUT", NULL, "TERT_TDM_RX_1"}, + {"BE_OUT", NULL, "TERT_TDM_RX_2"}, + {"BE_OUT", NULL, "TERT_TDM_RX_3"}, + {"BE_OUT", NULL, "TERT_TDM_RX_4"}, + {"BE_OUT", NULL, "QUAT_TDM_RX_0"}, + {"BE_OUT", NULL, "QUAT_TDM_RX_1"}, + {"BE_OUT", NULL, "QUAT_TDM_RX_2"}, + {"BE_OUT", NULL, "QUAT_TDM_RX_3"}, + {"BE_OUT", NULL, "QUIN_TDM_RX_0"}, + {"BE_OUT", NULL, "QUIN_TDM_RX_1"}, + {"BE_OUT", NULL, "QUIN_TDM_RX_2"}, + {"BE_OUT", NULL, "QUIN_TDM_RX_3"}, + {"BE_OUT", NULL, "WSA_CDC_DMA_RX_0"}, + {"BE_OUT", NULL, "WSA_CDC_DMA_RX_1"}, + {"BE_OUT", NULL, "RX_CDC_DMA_RX_0"}, + {"BE_OUT", NULL, "RX_CDC_DMA_RX_1"}, + {"BE_OUT", NULL, "RX_CDC_DMA_RX_2"}, + {"BE_OUT", NULL, "RX_CDC_DMA_RX_3"}, + {"BE_OUT", NULL, "RX_CDC_DMA_RX_4"}, + {"BE_OUT", NULL, "RX_CDC_DMA_RX_5"}, + {"BE_OUT", NULL, "RX_CDC_DMA_RX_6"}, + {"BE_OUT", NULL, "RX_CDC_DMA_RX_7"}, + + {"PRI_I2S_TX", NULL, "BE_IN"}, + {"MI2S_TX", NULL, "BE_IN"}, + {"QUAT_MI2S_TX", NULL, "BE_IN"}, + {"QUIN_MI2S_TX", NULL, "BE_IN"}, + {"PRI_MI2S_TX", NULL, "BE_IN"}, + {"TERT_MI2S_TX", NULL, "BE_IN"}, + {"INT0_MI2S_TX", NULL, "BE_IN"}, + {"INT2_MI2S_TX", NULL, "BE_IN"}, + {"INT3_MI2S_TX", NULL, "BE_IN"}, + {"INT4_MI2S_TX", NULL, "BE_IN"}, + {"INT5_MI2S_TX", NULL, "BE_IN"}, + {"SEC_MI2S_TX", NULL, "BE_IN"}, + {"SENARY_MI2S_TX", NULL, "BE_IN" }, + {"SLIMBUS_0_TX", NULL, "BE_IN" }, + {"SLIMBUS_1_TX", NULL, "BE_IN" }, + {"SLIMBUS_3_TX", NULL, "BE_IN" }, + {"SLIMBUS_4_TX", NULL, "BE_IN" }, + {"SLIMBUS_5_TX", NULL, "BE_IN" }, + {"SLIMBUS_6_TX", NULL, "BE_IN" }, + {"SLIMBUS_7_TX", NULL, "BE_IN" }, + {"SLIMBUS_8_TX", NULL, "BE_IN" }, + {"SLIMBUS_9_TX", NULL, "BE_IN" }, + {"USB_AUDIO_TX", NULL, "BE_IN" }, + {"INT_BT_SCO_TX", NULL, "BE_IN"}, + {"INT_FM_TX", NULL, "BE_IN"}, + {"PCM_TX", NULL, "BE_IN"}, + {"BE_OUT", NULL, "SLIMBUS_3_RX"}, + {"BE_OUT", NULL, "STUB_RX"}, + {"STUB_TX", NULL, "BE_IN"}, + {"STUB_1_TX", NULL, "BE_IN"}, + {"BE_OUT", NULL, "AUX_PCM_RX"}, + {"AUX_PCM_TX", NULL, "BE_IN"}, + {"SEC_AUX_PCM_TX", NULL, "BE_IN"}, + {"TERT_AUX_PCM_TX", NULL, "BE_IN"}, + {"QUAT_AUX_PCM_TX", NULL, "BE_IN"}, + {"QUIN_AUX_PCM_TX", NULL, "BE_IN"}, + {"INCALL_RECORD_TX", NULL, "BE_IN"}, + {"INCALL_RECORD_RX", NULL, "BE_IN"}, + {"SLIM0_RX_VI_FB_LCH_MUX", "SLIM4_TX", "SLIMBUS_4_TX"}, + {"SLIM0_RX_VI_FB_RCH_MUX", "SLIM4_TX", "SLIMBUS_4_TX"}, + {"WSA_RX_0_VI_FB_LCH_MUX", "WSA_CDC_DMA_TX_0", "WSA_CDC_DMA_TX_0"}, + {"WSA_RX_0_VI_FB_RCH_MUX", "WSA_CDC_DMA_TX_0", "WSA_CDC_DMA_TX_0"}, + {"PRI_MI2S_RX_VI_FB_MUX", "SENARY_TX", "SENARY_TX"}, + {"INT4_MI2S_RX_VI_FB_MONO_CH_MUX", "INT5_MI2S_TX", "INT5_MI2S_TX"}, + {"INT4_MI2S_RX_VI_FB_STEREO_CH_MUX", "INT5_MI2S_TX", "INT5_MI2S_TX"}, + {"SLIMBUS_0_RX", NULL, "SLIM0_RX_VI_FB_LCH_MUX"}, + {"SLIMBUS_0_RX", NULL, "SLIM0_RX_VI_FB_RCH_MUX"}, + {"WSA_CDC_DMA_RX_0", NULL, "WSA_RX_0_VI_FB_LCH_MUX"}, + {"WSA_CDC_DMA_RX_0", NULL, "WSA_RX_0_VI_FB_RCH_MUX"}, + {"PRI_MI2S_RX", NULL, "PRI_MI2S_RX_VI_FB_MUX"}, + {"INT4_MI2S_RX", NULL, "INT4_MI2S_RX_VI_FB_MONO_CH_MUX"}, + {"INT4_MI2S_RX", NULL, "INT4_MI2S_RX_VI_FB_STEREO_CH_MUX"}, + {"PRI_TDM_TX_0", NULL, "BE_IN"}, + {"PRI_TDM_TX_1", NULL, "BE_IN"}, + {"PRI_TDM_TX_2", NULL, "BE_IN"}, + {"PRI_TDM_TX_3", NULL, "BE_IN"}, + {"SEC_TDM_TX_0", NULL, "BE_IN"}, + {"SEC_TDM_TX_1", NULL, "BE_IN"}, + {"SEC_TDM_TX_2", NULL, "BE_IN"}, + {"SEC_TDM_TX_3", NULL, "BE_IN"}, + {"TERT_TDM_TX_0", NULL, "BE_IN"}, + {"TERT_TDM_TX_1", NULL, "BE_IN"}, + {"TERT_TDM_TX_2", NULL, "BE_IN"}, + {"TERT_TDM_TX_3", NULL, "BE_IN"}, + {"TERT_TDM_TX_7", NULL, "BE_IN"}, + {"QUAT_TDM_TX_0", NULL, "BE_IN"}, + {"QUAT_TDM_TX_1", NULL, "BE_IN"}, + {"QUAT_TDM_TX_2", NULL, "BE_IN"}, + {"QUAT_TDM_TX_3", NULL, "BE_IN"}, + {"QUIN_TDM_TX_0", NULL, "BE_IN"}, + {"QUIN_TDM_TX_1", NULL, "BE_IN"}, + {"QUIN_TDM_TX_2", NULL, "BE_IN"}, + {"QUIN_TDM_TX_3", NULL, "BE_IN"}, + {"WSA_CDC_DMA_TX_0", NULL, "BE_IN"}, + {"WSA_CDC_DMA_TX_1", NULL, "BE_IN"}, + {"WSA_CDC_DMA_TX_2", NULL, "BE_IN"}, + {"VA_CDC_DMA_TX_0", NULL, "BE_IN"}, + {"VA_CDC_DMA_TX_1", NULL, "BE_IN"}, + {"TX_CDC_DMA_TX_0", NULL, "BE_IN"}, + {"TX_CDC_DMA_TX_1", NULL, "BE_IN"}, + {"TX_CDC_DMA_TX_2", NULL, "BE_IN"}, + {"TX_CDC_DMA_TX_3", NULL, "BE_IN"}, + {"TX_CDC_DMA_TX_4", NULL, "BE_IN"}, + {"TX_CDC_DMA_TX_5", NULL, "BE_IN"}, + {"PRI_SPDIF_TX", NULL, "BE_IN"}, + {"SEC_SPDIF_TX", NULL, "BE_IN"}, +}; + +static int msm_pcm_routing_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + unsigned int be_id = rtd->dai_link->id; + + if (be_id >= MSM_BACKEND_DAI_MAX) { + pr_err("%s: unexpected BE id %d\n", __func__, be_id); + return -EINVAL; + } + + mutex_lock(&routing_lock); + msm_bedais[be_id].sample_rate = params_rate(params); + msm_bedais[be_id].channel = params_channels(params); + msm_bedais[be_id].format = params_format(params); + pr_debug("%s: BE Sample Rate (%d) format (%d) BE id %d\n", + __func__, msm_bedais[be_id].sample_rate, + msm_bedais[be_id].format, be_id); + mutex_unlock(&routing_lock); + return 0; +} + +static int msm_pcm_routing_close(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + unsigned int be_id = rtd->dai_link->id; + int i, session_type, path_type, topology; + struct msm_pcm_routing_bdai_data *bedai; + struct msm_pcm_routing_fdai_data *fdai; + + pr_debug("%s: substream->pcm->id:%s\n", + __func__, substream->pcm->id); + + if (be_id >= MSM_BACKEND_DAI_MAX) { + pr_err("%s: unexpected BE id %d\n", __func__, be_id); + return -EINVAL; + } + + bedai = &msm_bedais[be_id]; + session_type = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? + 0 : 1); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + path_type = ADM_PATH_PLAYBACK; + else + path_type = ADM_PATH_LIVE_REC; + + mutex_lock(&routing_lock); + for_each_set_bit(i, &bedai->fe_sessions[0], MSM_FRONTEND_DAI_MAX) { + if (!is_mm_lsm_fe_id(i)) + continue; + fdai = &fe_dai_map[i][session_type]; + if (fdai->strm_id != INVALID_SESSION) { + int idx; + int port_id; + unsigned long copp = + session_copp_map[i][session_type][be_id]; + for (idx = 0; idx < MAX_COPPS_PER_PORT; idx++) + if (test_bit(idx, &copp)) + break; + + if (idx >= MAX_COPPS_PER_PORT) { + pr_debug("%s: copp idx is invalid, exiting\n", + __func__); + continue; + } + fdai->be_srate = bedai->sample_rate; + port_id = bedai->port_id; + topology = adm_get_topology_for_port_copp_idx(port_id, + idx); + msm_routing_unload_topology(topology); + adm_close(bedai->port_id, fdai->perf_mode, idx); + pr_debug("%s: copp:%ld,idx bit fe:%d, type:%d,be:%d topology=0x%x\n", + __func__, copp, i, session_type, be_id, + topology); + clear_bit(idx, + &session_copp_map[i][session_type][be_id]); + if ((fdai->perf_mode == LEGACY_PCM_MODE) && + (fdai->passthr_mode == LEGACY_PCM)) + msm_pcm_routing_deinit_pp(bedai->port_id, + topology); + } + } + + bedai->active = 0; + bedai->sample_rate = 0; + bedai->channel = 0; + mutex_unlock(&routing_lock); + + return 0; +} + +static int msm_pcm_routing_prepare(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + unsigned int be_id = rtd->dai_link->id; + int i, path_type, topology; + int session_type = INVALID_SESSION; + struct msm_pcm_routing_bdai_data *bedai; + u32 channels, sample_rate; + uint16_t bits_per_sample = 16, voc_path_type; + struct msm_pcm_routing_fdai_data *fdai; + u32 session_id; + struct media_format_info voc_be_media_format; + bool is_lsm; + + pr_debug("%s: substream->pcm->id:%s\n", + __func__, substream->pcm->id); + + if (be_id >= MSM_BACKEND_DAI_MAX) { + pr_err("%s: unexpected BE id %d\n", __func__, be_id); + return -EINVAL; + } + + bedai = &msm_bedais[be_id]; + + mutex_lock(&routing_lock); + if (bedai->active == 1) + goto done; /* Ignore prepare if back-end already active */ + + /* AFE port is not active at this point. However, still + * go ahead setting active flag under the notion that + * QDSP6 is able to handle ADM starting before AFE port + * is started. + */ + bedai->active = 1; + + for_each_set_bit(i, &bedai->fe_sessions[0], MSM_FRONTEND_DAI_MAX) { + if (!(is_mm_lsm_fe_id(i) && + route_check_fe_id_adm_support(i))) + continue; + + session_type = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? + SESSION_TYPE_RX : SESSION_TYPE_TX; + fdai = &fe_dai_map[i][session_type]; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + if (fdai->passthr_mode != LEGACY_PCM) + path_type = ADM_PATH_COMPRESSED_RX; + else + path_type = ADM_PATH_PLAYBACK; + } else { + if ((fdai->passthr_mode != LEGACY_PCM) && + (fdai->passthr_mode != LISTEN)) + path_type = ADM_PATH_COMPRESSED_TX; + else + path_type = ADM_PATH_LIVE_REC; + } + + is_lsm = (i >= MSM_FRONTEND_DAI_LSM1) && + (i <= MSM_FRONTEND_DAI_LSM8); + if (fdai->strm_id != INVALID_SESSION) { + int app_type, app_type_idx, copp_idx, acdb_dev_id; + + if (session_type == SESSION_TYPE_TX && + fdai->be_srate && + (fdai->be_srate != bedai->sample_rate)) { + pr_debug("%s: flush strm %d diff BE rates\n", + __func__, + fdai->strm_id); + + if (fdai->event_info.event_func) + fdai->event_info.event_func( + MSM_PCM_RT_EVT_BUF_RECFG, + fdai->event_info.priv_data); + fdai->be_srate = 0; /* might not need it */ + } + bits_per_sample = msm_routing_get_bit_width( + bedai->format); + + app_type = + fe_dai_app_type_cfg[i][session_type][be_id].app_type; + if (app_type && is_lsm) { + app_type_idx = + msm_pcm_routing_get_lsm_app_type_idx(app_type); + sample_rate = + fe_dai_app_type_cfg[i][session_type][be_id] + .sample_rate; + bits_per_sample = + lsm_app_type_cfg[app_type_idx].bit_width; + } else if (app_type) { + app_type_idx = + msm_pcm_routing_get_app_type_idx(app_type); + sample_rate = + fe_dai_app_type_cfg[i][session_type] + [be_id].sample_rate; + bits_per_sample = + app_type_cfg[app_type_idx].bit_width; + } else + sample_rate = bedai->sample_rate; + /* + * check if ADM needs to be configured with different + * channel mapping than backend + */ + if (!bedai->adm_override_ch) + channels = bedai->channel; + else + channels = bedai->adm_override_ch; + acdb_dev_id = + fe_dai_app_type_cfg[i][session_type][be_id].acdb_dev_id; + topology = msm_routing_get_adm_topology(i, session_type, + be_id); + + if ((fdai->passthr_mode == COMPRESSED_PASSTHROUGH_DSD) + || (fdai->passthr_mode == COMPRESSED_PASSTHROUGH_GEN) + || (fdai->passthr_mode == COMPRESSED_PASSTHROUGH_IEC61937)) + topology = COMPRESSED_PASSTHROUGH_NONE_TOPOLOGY; + + copp_idx = adm_open(bedai->port_id, path_type, + sample_rate, channels, topology, + fdai->perf_mode, bits_per_sample, + app_type, acdb_dev_id); + if ((copp_idx < 0) || + (copp_idx >= MAX_COPPS_PER_PORT)) { + pr_err("%s: adm open failed\n", __func__); + mutex_unlock(&routing_lock); + return -EINVAL; + } + pr_debug("%s: setting idx bit of fe:%d, type: %d, be:%d\n", + __func__, i, session_type, be_id); + set_bit(copp_idx, + &session_copp_map[i][session_type][be_id]); + + if (msm_is_resample_needed( + sample_rate, + bedai->sample_rate)) + adm_copp_mfc_cfg( + bedai->port_id, copp_idx, + bedai->sample_rate); + + msm_pcm_routing_build_matrix(i, session_type, path_type, + fdai->perf_mode, fdai->passthr_mode); + if ((fdai->perf_mode == LEGACY_PCM_MODE) && + (fdai->passthr_mode == LEGACY_PCM)) + msm_pcm_routing_cfg_pp(bedai->port_id, copp_idx, + topology, channels); + } + } + + for_each_set_bit(i, &bedai->fe_sessions[0], MSM_FRONTEND_DAI_MAX) { + session_id = msm_pcm_routing_get_voc_sessionid(i); + if (session_id) { + pr_debug("%s voice session_id: 0x%x\n", __func__, + session_id); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + voc_path_type = RX_PATH; + else + voc_path_type = TX_PATH; + + voc_set_route_flag(session_id, voc_path_type, 1); + + memset(&voc_be_media_format, 0, + sizeof(struct media_format_info)); + + voc_be_media_format.port_id = bedai->port_id; + voc_be_media_format.num_channels = bedai->channel; + voc_be_media_format.sample_rate = bedai->sample_rate; + voc_be_media_format.bits_per_sample = bedai->format; + /* Defaulting this to 1 for voice call usecases */ + voc_be_media_format.channel_mapping[0] = 1; + + voc_set_device_config(session_id, voc_path_type, + &voc_be_media_format); + + if (voc_get_route_flag(session_id, RX_PATH) && + voc_get_route_flag(session_id, TX_PATH)) + voc_enable_device(session_id); + } + } + + /* Check if backend is an external ec ref port and set as needed */ + if (unlikely(bedai->port_id == voc_get_ext_ec_ref_port_id())) { + + memset(&voc_be_media_format, 0, + sizeof(struct media_format_info)); + + /* Get format info for ec ref port from msm_bedais[] */ + voc_be_media_format.port_id = bedai->port_id; + voc_be_media_format.num_channels = bedai->channel; + voc_be_media_format.bits_per_sample = bedai->format; + voc_be_media_format.sample_rate = bedai->sample_rate; + /* Defaulting this to 1 for voice call usecases */ + voc_be_media_format.channel_mapping[0] = 1; + voc_set_ext_ec_ref_media_fmt_info(&voc_be_media_format); + pr_debug("%s: EC Ref media format info set to port_id=%d, num_channels=%d, bits_per_sample=%d, sample_rate=%d\n", + __func__, voc_be_media_format.port_id, + voc_be_media_format.num_channels, + voc_be_media_format.bits_per_sample, + voc_be_media_format.sample_rate); + } + +done: + mutex_unlock(&routing_lock); + + return 0; +} + +static int msm_routing_send_device_pp_params(int port_id, int copp_idx, + int fe_id) +{ + int index, topo_id, be_idx; + unsigned long pp_config = 0; + bool mute_on; + int latency, session_type; + bool compr_passthr_mode = true; + + pr_debug("%s: port_id %d, copp_idx %d\n", __func__, port_id, copp_idx); + + if (port_id != HDMI_RX && port_id != DISPLAY_PORT_RX) { + pr_err("%s: Device pp params on invalid port %d\n", + __func__, port_id); + return -EINVAL; + } + + for (be_idx = 0; be_idx < MSM_BACKEND_DAI_MAX; be_idx++) { + if (port_id == msm_bedais[be_idx].port_id) + break; + } + + if (be_idx >= MSM_BACKEND_DAI_MAX) { + pr_debug("%s: Invalid be id %d\n", __func__, be_idx); + return -EINVAL; + } + + for (index = 0; index < MSM_BACKEND_DAI_PP_PARAMS_REQ_MAX; index++) { + if (msm_bedais_pp_params[index].port_id == port_id) + break; + } + if (index >= MSM_BACKEND_DAI_PP_PARAMS_REQ_MAX) { + pr_err("%s: Invalid backend pp params index %d\n", + __func__, index); + return -EINVAL; + } + + topo_id = adm_get_topology_for_port_copp_idx(port_id, copp_idx); + if (topo_id != COMPRESSED_PASSTHROUGH_DEFAULT_TOPOLOGY) { + pr_err("%s: Invalid passthrough topology 0x%x\n", + __func__, topo_id); + return -EINVAL; + } + + session_type = + (afe_get_port_type(port_id) == MSM_AFE_PORT_TYPE_RX) ? + SESSION_TYPE_RX : SESSION_TYPE_TX; + + if ((fe_dai_map[fe_id][session_type].passthr_mode == LEGACY_PCM) || + (fe_dai_map[fe_id][session_type].passthr_mode == LISTEN)) + compr_passthr_mode = false; + + pp_config = msm_bedais_pp_params[index].pp_params_config; + if (test_bit(ADM_PP_PARAM_MUTE_BIT, &pp_config)) { + pr_debug("%s: ADM_PP_PARAM_MUTE\n", __func__); + clear_bit(ADM_PP_PARAM_MUTE_BIT, &pp_config); + mute_on = msm_bedais_pp_params[index].mute_on; + if ((msm_bedais[be_idx].active) && compr_passthr_mode) + adm_send_compressed_device_mute(port_id, + copp_idx, + mute_on); + } + if (test_bit(ADM_PP_PARAM_LATENCY_BIT, &pp_config)) { + pr_debug("%s: ADM_PP_PARAM_LATENCY\n", __func__); + clear_bit(ADM_PP_PARAM_LATENCY_BIT, + &pp_config); + latency = msm_bedais_pp_params[index].latency; + if ((msm_bedais[be_idx].active) && compr_passthr_mode) + adm_send_compressed_device_latency(port_id, + copp_idx, + latency); + } + return 0; +} + +static uint32_t msm_routing_get_topology(size_t data_size, void *data) +{ + uint32_t topology = NULL_COPP_TOPOLOGY; + void *cal_info = NULL; + uint32_t size = 0; + + /* Retrieve cal_info size from cal data*/ + if (data_size < sizeof(struct audio_cal_type_basic) + + sizeof(struct audio_cal_info_adm_top)) { + pr_err("%s: Invalid data size: %zd\n", __func__, data_size); + goto done; + } + size = data_size - sizeof(struct audio_cal_type_basic); + cal_info = kzalloc(size, GFP_KERNEL); + + if (!cal_info) + goto done; + + memcpy(cal_info, + ((uint8_t *)data + sizeof(struct audio_cal_type_basic)), size); + + topology = ((struct audio_cal_info_adm_top *)cal_info)->topology; + kfree(cal_info); + cal_info = NULL; + +done: + pr_debug("%s: Using topology %d\n", __func__, topology); + + return topology; +} + +static void msm_routing_load_topology(size_t data_size, void *data) +{ + uint32_t topology_id; + int ret = -EINVAL; + + topology_id = msm_routing_get_topology(data_size, data); + if (topology_id != NULL_COPP_TOPOLOGY) + ret = q6core_load_unload_topo_modules(topology_id, + CORE_LOAD_TOPOLOGY); + if (ret < 0) + pr_debug("%s %d load topology failed\n", + __func__, topology_id); + +} + +static void msm_routing_unload_topology(uint32_t topology_id) +{ + int ret = -EINVAL; + + if (topology_id != NULL_COPP_TOPOLOGY) + ret = q6core_load_unload_topo_modules(topology_id, + CORE_UNLOAD_TOPOLOGY); + if (ret < 0) + pr_debug("%s %d unload topology failed\n", + __func__, topology_id); + +} + +static int msm_routing_put_device_pp_params_mixer(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int pp_id = ucontrol->value.integer.value[0]; + int port_id = 0; + int index, be_idx, i, topo_id, idx; + bool mute; + int latency, session_type; + bool compr_passthr_mode = true; + + pr_debug("%s: pp_id: 0x%x\n", __func__, pp_id); + + for (be_idx = 0; be_idx < MSM_BACKEND_DAI_MAX; be_idx++) { + port_id = msm_bedais[be_idx].port_id; + if (port_id == HDMI_RX || port_id == DISPLAY_PORT_RX) + break; + } + + if (be_idx >= MSM_BACKEND_DAI_MAX) { + pr_debug("%s: Invalid be id %d\n", __func__, be_idx); + return -EINVAL; + } + + for (index = 0; index < MSM_BACKEND_DAI_PP_PARAMS_REQ_MAX; index++) { + if (msm_bedais_pp_params[index].port_id == port_id) + break; + } + if (index >= MSM_BACKEND_DAI_PP_PARAMS_REQ_MAX) { + pr_err("%s: Invalid pp params backend index %d\n", + __func__, index); + return -EINVAL; + } + + session_type = + (afe_get_port_type(port_id) == MSM_AFE_PORT_TYPE_RX) ? + SESSION_TYPE_RX : SESSION_TYPE_TX; + + for_each_set_bit(i, &msm_bedais[be_idx].fe_sessions[0], + MSM_FRONTEND_DAI_MM_SIZE) { + if ((fe_dai_map[i][session_type].passthr_mode == LEGACY_PCM) || + (fe_dai_map[i][session_type].passthr_mode == LISTEN)) + compr_passthr_mode = false; + + for (idx = 0; idx < MAX_COPPS_PER_PORT; idx++) { + unsigned long copp = + session_copp_map[i] + [SESSION_TYPE_RX][be_idx]; + if (!test_bit(idx, &copp)) + continue; + topo_id = adm_get_topology_for_port_copp_idx(port_id, + idx); + if (topo_id != COMPRESSED_PASSTHROUGH_DEFAULT_TOPOLOGY) + continue; + pr_debug("%s: port: 0x%x, copp %ld, be active: %d, passt: %d\n", + __func__, port_id, copp, msm_bedais[be_idx].active, + fe_dai_map[i][session_type].passthr_mode); + switch (pp_id) { + case ADM_PP_PARAM_MUTE_ID: + pr_debug("%s: ADM_PP_PARAM_MUTE\n", __func__); + mute = ucontrol->value.integer.value[1] ? true : false; + msm_bedais_pp_params[index].mute_on = mute; + set_bit(ADM_PP_PARAM_MUTE_BIT, + &msm_bedais_pp_params[index].pp_params_config); + if ((msm_bedais[be_idx].active) && compr_passthr_mode) + adm_send_compressed_device_mute(port_id, + idx, mute); + break; + case ADM_PP_PARAM_LATENCY_ID: + pr_debug("%s: ADM_PP_PARAM_LATENCY\n", __func__); + msm_bedais_pp_params[index].latency = + ucontrol->value.integer.value[1]; + set_bit(ADM_PP_PARAM_LATENCY_BIT, + &msm_bedais_pp_params[index].pp_params_config); + latency = msm_bedais_pp_params[index].latency = + ucontrol->value.integer.value[1]; + if ((msm_bedais[be_idx].active) && compr_passthr_mode) + adm_send_compressed_device_latency(port_id, + idx, latency); + break; + default: + pr_info("%s, device pp param %d not supported\n", + __func__, pp_id); + break; + } + } + } + return 0; +} + +static int msm_routing_get_device_pp_params_mixer(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s:msm_routing_get_device_pp_params_mixer", __func__); + return 0; +} + +static const struct snd_kcontrol_new device_pp_params_mixer_controls[] = { + SOC_SINGLE_MULTI_EXT("Device PP Params", SND_SOC_NOPM, 0, 0xFFFFFFFF, + 0, 3, msm_routing_get_device_pp_params_mixer, + msm_routing_put_device_pp_params_mixer), +}; + +static int msm_aptx_dec_license_control_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = + core_get_license_status(ASM_MEDIA_FMT_APTX); + pr_debug("%s: status %ld\n", __func__, + ucontrol->value.integer.value[0]); + return 0; +} + +static int msm_aptx_dec_license_control_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int32_t status = 0; + + status = core_set_license(ucontrol->value.integer.value[0], + APTX_CLASSIC_DEC_LICENSE_ID); + pr_debug("%s: status %d\n", __func__, status); + return status; +} + +static const struct snd_kcontrol_new aptx_dec_license_controls[] = { + SOC_SINGLE_EXT("APTX Dec License", SND_SOC_NOPM, 0, + 0xFFFF, 0, msm_aptx_dec_license_control_get, + msm_aptx_dec_license_control_put), +}; + +static int msm_routing_be_dai_name_table_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; + uinfo->count = sizeof(be_dai_name_table); + return 0; +} + +static int msm_routing_be_dai_name_table_tlv_get(struct snd_kcontrol *kcontrol, + unsigned int __user *bytes, + unsigned int size) +{ + int i; + int ret = 0; + + if (size < sizeof(be_dai_name_table)) { + pr_err("%s: invalid size %d requested, returning\n", + __func__, size); + ret = -EINVAL; + goto done; + } + + /* + * Fill be_dai_name_table from msm_bedais table to reduce code changes + * needed when adding new backends + */ + for (i = 0; i < MSM_BACKEND_DAI_MAX; i++) { + be_dai_name_table[i].be_id = i; + strlcpy(be_dai_name_table[i].be_name, + msm_bedais[i].name, + LPASS_BE_NAME_MAX_LENGTH); + } + + ret = copy_to_user(bytes, &be_dai_name_table, + sizeof(be_dai_name_table)); + if (ret) { + pr_err("%s: failed to copy be_dai_name_table\n", __func__); + ret = -EFAULT; + } + +done: + return ret; +} + +static const struct snd_kcontrol_new + msm_routing_be_dai_name_table_mixer_controls[] = { + { + .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | + SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK, + .info = msm_routing_be_dai_name_table_info, + .name = "Backend DAI Name Table", + .tlv.c = snd_soc_bytes_tlv_callback, + .private_value = (unsigned long) &(struct soc_bytes_ext) { + .max = sizeof(be_dai_name_table), + .get = msm_routing_be_dai_name_table_tlv_get, + } + }, +}; + +static int msm_routing_stereo_channel_reverse_control_get( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = swap_ch; + pr_debug("%s: Swap channel value: %ld\n", __func__, + ucontrol->value.integer.value[0]); + return 0; +} + +static int msm_routing_stereo_channel_reverse_control_put( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int i, idx, be_index, port_id; + int ret = 0; + unsigned long copp; + + pr_debug("%s Swap channel value:%ld\n", __func__, + ucontrol->value.integer.value[0]); + + swap_ch = ucontrol->value.integer.value[0]; + + mutex_lock(&routing_lock); + for (be_index = 0; be_index < MSM_BACKEND_DAI_MAX; be_index++) { + port_id = msm_bedais[be_index].port_id; + if (!msm_bedais[be_index].active) + continue; + + for_each_set_bit(i, &msm_bedais[be_index].fe_sessions[0], + MSM_FRONTEND_DAI_MM_SIZE) { + copp = session_copp_map[i][SESSION_TYPE_RX][be_index]; + for (idx = 0; idx < MAX_COPPS_PER_PORT; idx++) { + if (!test_bit(idx, &copp)) + continue; + + pr_debug("%s: swap channel control of portid:%d, coppid:%d\n", + __func__, port_id, idx); + ret = adm_swap_speaker_channels( + port_id, idx, + msm_bedais[be_index].sample_rate, + swap_ch); + if (ret) { + pr_err("%s:Swap_channel failed, err=%d\n", + __func__, ret); + goto done; + } + } + } + } +done: + mutex_unlock(&routing_lock); + return ret; +} + +static const struct snd_kcontrol_new stereo_channel_reverse_control[] = { + SOC_SINGLE_EXT("Swap channel", SND_SOC_NOPM, 0, + 1, 0, msm_routing_stereo_channel_reverse_control_get, + msm_routing_stereo_channel_reverse_control_put), +}; + +static int msm_routing_instance_id_support_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + return 0; +} + +static int msm_routing_instance_id_support_put( + struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +{ + bool supported = ucontrol->value.integer.value[0] ? true : false; + + q6common_update_instance_id_support(supported); + return 0; +} + +static int msm_routing_instance_id_support_get( + struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +{ + bool supported = false; + + supported = q6common_is_instance_id_supported(); + ucontrol->value.integer.value[0] = supported ? 1 : 0; + + return 0; +} + +static const struct snd_kcontrol_new + msm_routing_feature_support_mixer_controls[] = { + { + .access = SNDRV_CTL_ELEM_ACCESS_READ | + SNDRV_CTL_ELEM_ACCESS_WRITE, + .info = msm_routing_instance_id_support_info, + .name = "Instance ID Support", + .put = msm_routing_instance_id_support_put, + .get = msm_routing_instance_id_support_get, + }, +}; + +static const struct snd_pcm_ops msm_routing_pcm_ops = { + .hw_params = msm_pcm_routing_hw_params, + .close = msm_pcm_routing_close, + .prepare = msm_pcm_routing_prepare, +}; + +/* Not used but frame seems to require it */ +static int msm_routing_probe(struct snd_soc_platform *platform) +{ + snd_soc_dapm_new_controls(&platform->component.dapm, msm_qdsp6_widgets, + ARRAY_SIZE(msm_qdsp6_widgets)); + snd_soc_dapm_add_routes(&platform->component.dapm, intercon, + ARRAY_SIZE(intercon)); + + snd_soc_dapm_new_widgets(platform->component.dapm.card); + + snd_soc_add_platform_controls(platform, lsm_controls, + ARRAY_SIZE(lsm_controls)); + + snd_soc_add_platform_controls(platform, aanc_slim_0_rx_mux, + ARRAY_SIZE(aanc_slim_0_rx_mux)); + + snd_soc_add_platform_controls(platform, aanc_noise_level, + ARRAY_SIZE(aanc_noise_level)); + + snd_soc_add_platform_controls(platform, msm_voc_session_controls, + ARRAY_SIZE(msm_voc_session_controls)); + + snd_soc_add_platform_controls(platform, app_type_cfg_controls, + ARRAY_SIZE(app_type_cfg_controls)); + + snd_soc_add_platform_controls(platform, lsm_app_type_cfg_controls, + ARRAY_SIZE(lsm_app_type_cfg_controls)); + + snd_soc_add_platform_controls(platform, module_cfg_controls, + ARRAY_SIZE(module_cfg_controls)); + + snd_soc_add_platform_controls(platform, + stereo_to_custom_stereo_controls, + ARRAY_SIZE(stereo_to_custom_stereo_controls)); + + snd_soc_add_platform_controls(platform, ec_ref_param_controls, + ARRAY_SIZE(ec_ref_param_controls)); + + snd_soc_add_platform_controls(platform, channel_mixer_controls, + ARRAY_SIZE(channel_mixer_controls)); + + msm_qti_pp_add_controls(platform); + + msm_dts_srs_tm_add_controls(platform); + + msm_dolby_dap_add_controls(platform); + + snd_soc_add_platform_controls(platform, + use_ds1_or_ds2_controls, + ARRAY_SIZE(use_ds1_or_ds2_controls)); + + snd_soc_add_platform_controls(platform, + device_pp_params_mixer_controls, + ARRAY_SIZE(device_pp_params_mixer_controls)); + + snd_soc_add_platform_controls(platform, + msm_routing_be_dai_name_table_mixer_controls, + ARRAY_SIZE(msm_routing_be_dai_name_table_mixer_controls)); + + snd_soc_add_platform_controls(platform, msm_source_tracking_controls, + ARRAY_SIZE(msm_source_tracking_controls)); + snd_soc_add_platform_controls(platform, adm_channel_config_controls, + ARRAY_SIZE(adm_channel_config_controls)); + + snd_soc_add_platform_controls(platform, aptx_dec_license_controls, + ARRAY_SIZE(aptx_dec_license_controls)); + snd_soc_add_platform_controls(platform, stereo_channel_reverse_control, + ARRAY_SIZE(stereo_channel_reverse_control)); + snd_soc_add_platform_controls( + platform, msm_routing_feature_support_mixer_controls, + ARRAY_SIZE(msm_routing_feature_support_mixer_controls)); + + return 0; +} + +int msm_routing_pcm_new(struct snd_soc_pcm_runtime *runtime) +{ + return msm_pcm_routing_hwdep_new(runtime, msm_bedais); +} + +void msm_routing_pcm_free(struct snd_pcm *pcm) +{ + msm_pcm_routing_hwdep_free(pcm); +} + +static struct snd_soc_platform_driver msm_soc_routing_platform = { + .ops = &msm_routing_pcm_ops, + .probe = msm_routing_probe, + .pcm_new = msm_routing_pcm_new, + .pcm_free = msm_routing_pcm_free, +}; + +static int msm_routing_pcm_probe(struct platform_device *pdev) +{ + + dev_dbg(&pdev->dev, "dev name %s\n", dev_name(&pdev->dev)); + return snd_soc_register_platform(&pdev->dev, + &msm_soc_routing_platform); +} + +static int msm_routing_pcm_remove(struct platform_device *pdev) +{ + snd_soc_unregister_platform(&pdev->dev); + return 0; +} + +static const struct of_device_id msm_pcm_routing_dt_match[] = { + {.compatible = "qcom,msm-pcm-routing"}, + {} +}; +MODULE_DEVICE_TABLE(of, msm_pcm_routing_dt_match); + +static struct platform_driver msm_routing_pcm_driver = { + .driver = { + .name = "msm-pcm-routing", + .owner = THIS_MODULE, + .of_match_table = msm_pcm_routing_dt_match, + }, + .probe = msm_routing_pcm_probe, + .remove = msm_routing_pcm_remove, +}; + +int msm_routing_check_backend_enabled(int fedai_id) +{ + int i; + + if (fedai_id > MSM_FRONTEND_DAI_MM_MAX_ID) { + /* bad ID assigned in machine driver */ + pr_err("%s: bad MM ID\n", __func__); + return 0; + } + for (i = 0; i < MSM_BACKEND_DAI_MAX; i++) { + if (test_bit(fedai_id, &msm_bedais[i].fe_sessions[0])) + return msm_bedais[i].active; + } + return 0; +} + +static int get_cal_type_index(int32_t cal_type) +{ + int ret = -EINVAL; + + switch (cal_type) { + case ADM_TOPOLOGY_CAL_TYPE: + ret = ADM_TOPOLOGY_CAL_TYPE_IDX; + break; + case ADM_LSM_TOPOLOGY_CAL_TYPE: + ret = ADM_LSM_TOPOLOGY_CAL_TYPE_IDX; + break; + default: + pr_err("%s: Invalid cal type %d\n", __func__, cal_type); + } + return ret; +} + +static int msm_routing_set_cal(int32_t cal_type, + size_t data_size, void *data) +{ + int ret = 0; + int cal_index; + pr_debug("%s\n", __func__); + + cal_index = get_cal_type_index(cal_type); + if (cal_index < 0) { + pr_err("%s: Could not get cal index %d\n", + __func__, cal_index); + ret = -EINVAL; + goto done; + } + + ret = cal_utils_set_cal(data_size, data, cal_data[cal_index], 0, NULL); + if (ret < 0) { + pr_err("%s: cal_utils_set_cal failed, ret = %d, cal type = %d!\n", + __func__, ret, cal_type); + ret = -EINVAL; + goto done; + } + /* Pre-load if it is ADM topology */ + if ((cal_index == ADM_TOPOLOGY_CAL_TYPE_IDX) || + (cal_index == ADM_LSM_TOPOLOGY_CAL_TYPE_IDX)) { + msm_routing_load_topology(data_size, data); + } +done: + return ret; +} + +static void msm_routing_delete_cal_data(void) +{ + pr_debug("%s\n", __func__); + + cal_utils_destroy_cal_types(MAX_ROUTING_CAL_TYPES, &cal_data[0]); +} + +static int msm_routing_init_cal_data(void) +{ + int ret = 0; + struct cal_type_info cal_type_info[] = { + {{ADM_TOPOLOGY_CAL_TYPE, + {NULL, NULL, NULL, + msm_routing_set_cal, NULL, NULL} }, + {NULL, NULL, cal_utils_match_buf_num} }, + + {{ADM_LSM_TOPOLOGY_CAL_TYPE, + {NULL, NULL, NULL, + msm_routing_set_cal, NULL, NULL} }, + {NULL, NULL, cal_utils_match_buf_num} }, + }; + pr_debug("%s\n", __func__); + + ret = cal_utils_create_cal_types(MAX_ROUTING_CAL_TYPES, &cal_data[0], + &cal_type_info[0]); + if (ret < 0) { + pr_err("%s: could not create cal type!\n", + __func__); + ret = -EINVAL; + goto err; + } + + return ret; +err: + msm_routing_delete_cal_data(); + return ret; +} + +int __init msm_soc_routing_platform_init(void) +{ + mutex_init(&routing_lock); + if (msm_routing_init_cal_data()) + pr_err("%s: could not init cal data!\n", __func__); + + afe_set_routing_callback( + (routing_cb)msm_pcm_get_dev_acdb_id_by_port_id); + + memset(&be_dai_name_table, 0, sizeof(be_dai_name_table)); + memset(&last_be_id_configured, 0, sizeof(last_be_id_configured)); + + return platform_driver_register(&msm_routing_pcm_driver); +} + +void msm_soc_routing_platform_exit(void) +{ + msm_routing_delete_cal_data(); + memset(&be_dai_name_table, 0, sizeof(be_dai_name_table)); + mutex_destroy(&routing_lock); + platform_driver_unregister(&msm_routing_pcm_driver); +} + +MODULE_DESCRIPTION("MSM routing platform driver"); +MODULE_LICENSE("GPL v2"); diff --git a/audio-kernel/asoc/msm-pcm-routing-v2.h b/audio-kernel/asoc/msm-pcm-routing-v2.h new file mode 100644 index 000000000..fa5c85858 --- /dev/null +++ b/audio-kernel/asoc/msm-pcm-routing-v2.h @@ -0,0 +1,603 @@ +/* Copyright (c) 2012-2019, 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 _MSM_PCM_ROUTING_H +#define _MSM_PCM_ROUTING_H +#include + +/* + * These names are used by HAL to specify the BE. If any changes are + * made to the string names or the max name length corresponding + * changes need to be made in the HAL to ensure they still match. + */ +#define LPASS_BE_NAME_MAX_LENGTH 24 +#define LPASS_BE_PRI_I2S_RX "PRIMARY_I2S_RX" +#define LPASS_BE_PRI_I2S_TX "PRIMARY_I2S_TX" +#define LPASS_BE_SLIMBUS_0_RX "SLIMBUS_0_RX" +#define LPASS_BE_SLIMBUS_0_TX "SLIMBUS_0_TX" +#define LPASS_BE_HDMI "HDMI" +#define LPASS_BE_DISPLAY_PORT "DISPLAY_PORT" +#define LPASS_BE_DISPLAY_PORT1 "DISPLAY_PORT1" +#define LPASS_BE_INT_BT_SCO_RX "INT_BT_SCO_RX" +#define LPASS_BE_INT_BT_SCO_TX "INT_BT_SCO_TX" +#define LPASS_BE_INT_BT_A2DP_RX "INT_BT_A2DP_RX" +#define LPASS_BE_INT_FM_RX "INT_FM_RX" +#define LPASS_BE_INT_FM_TX "INT_FM_TX" +#define LPASS_BE_AFE_PCM_RX "RT_PROXY_DAI_001_RX" +#define LPASS_BE_AFE_PCM_TX "RT_PROXY_DAI_002_TX" +#define LPASS_BE_AUXPCM_RX "AUX_PCM_RX" +#define LPASS_BE_AUXPCM_TX "AUX_PCM_TX" +#define LPASS_BE_SEC_AUXPCM_RX "SEC_AUX_PCM_RX" +#define LPASS_BE_SEC_AUXPCM_TX "SEC_AUX_PCM_TX" +#define LPASS_BE_TERT_AUXPCM_RX "TERT_AUX_PCM_RX" +#define LPASS_BE_TERT_AUXPCM_TX "TERT_AUX_PCM_TX" +#define LPASS_BE_QUAT_AUXPCM_RX "QUAT_AUX_PCM_RX" +#define LPASS_BE_QUAT_AUXPCM_TX "QUAT_AUX_PCM_TX" +#define LPASS_BE_QUIN_AUXPCM_RX "QUIN_AUX_PCM_RX" +#define LPASS_BE_QUIN_AUXPCM_TX "QUIN_AUX_PCM_TX" +#define LPASS_BE_SEN_AUXPCM_RX "SEN_AUX_PCM_RX" +#define LPASS_BE_SEN_AUXPCM_TX "SEN_AUX_PCM_TX" +#define LPASS_BE_VOICE_PLAYBACK_TX "VOICE_PLAYBACK_TX" +#define LPASS_BE_VOICE2_PLAYBACK_TX "VOICE2_PLAYBACK_TX" +#define LPASS_BE_INCALL_RECORD_RX "INCALL_RECORD_RX" +#define LPASS_BE_INCALL_RECORD_TX "INCALL_RECORD_TX" +#define LPASS_BE_SEC_I2S_RX "SECONDARY_I2S_RX" +#define LPASS_BE_PRI_SPDIF_RX "PRI_SPDIF_RX" +#define LPASS_BE_PRI_SPDIF_TX "PRI_SPDIF_TX" +#define LPASS_BE_SEC_SPDIF_RX "SEC_SPDIF_RX" +#define LPASS_BE_SEC_SPDIF_TX "SEC_SPDIF_TX" + +#define LPASS_BE_MI2S_RX "MI2S_RX" +#define LPASS_BE_MI2S_TX "MI2S_TX" +#define LPASS_BE_QUAT_MI2S_RX "QUAT_MI2S_RX" +#define LPASS_BE_QUAT_MI2S_TX "QUAT_MI2S_TX" +#define LPASS_BE_SEC_MI2S_RX "SEC_MI2S_RX" +#define LPASS_BE_SEC_MI2S_RX_SD1 "SEC_MI2S_RX_SD1" +#define LPASS_BE_SEC_MI2S_TX "SEC_MI2S_TX" +#define LPASS_BE_PRI_MI2S_RX "PRI_MI2S_RX" +#define LPASS_BE_PRI_MI2S_TX "PRI_MI2S_TX" +#define LPASS_BE_TERT_MI2S_RX "TERT_MI2S_RX" +#define LPASS_BE_TERT_MI2S_TX "TERT_MI2S_TX" +#define LPASS_BE_AUDIO_I2S_RX "AUDIO_I2S_RX" +#define LPASS_BE_STUB_RX "STUB_RX" +#define LPASS_BE_STUB_TX "STUB_TX" +#define LPASS_BE_SLIMBUS_1_RX "SLIMBUS_1_RX" +#define LPASS_BE_SLIMBUS_1_TX "SLIMBUS_1_TX" +#define LPASS_BE_STUB_1_TX "STUB_1_TX" +#define LPASS_BE_SLIMBUS_2_RX "SLIMBUS_2_RX" +#define LPASS_BE_SLIMBUS_2_TX "SLIMBUS_2_TX" +#define LPASS_BE_SLIMBUS_3_RX "SLIMBUS_3_RX" +#define LPASS_BE_SLIMBUS_3_TX "SLIMBUS_3_TX" +#define LPASS_BE_SLIMBUS_4_RX "SLIMBUS_4_RX" +#define LPASS_BE_SLIMBUS_4_TX "SLIMBUS_4_TX" +#define LPASS_BE_SLIMBUS_TX_VI "SLIMBUS_TX_VI" +#define LPASS_BE_SLIMBUS_5_RX "SLIMBUS_5_RX" +#define LPASS_BE_SLIMBUS_5_TX "SLIMBUS_5_TX" +#define LPASS_BE_SLIMBUS_6_RX "SLIMBUS_6_RX" +#define LPASS_BE_SLIMBUS_6_TX "SLIMBUS_6_TX" +#define LPASS_BE_QUIN_MI2S_RX "QUIN_MI2S_RX" +#define LPASS_BE_QUIN_MI2S_TX "QUIN_MI2S_TX" +#define LPASS_BE_SENARY_MI2S_TX "SENARY_MI2S_TX" +#define LPASS_BE_SENARY_MI2S_RX "SENARY_MI2S_RX" + +#define LPASS_BE_PRI_TDM_RX_0 "PRI_TDM_RX_0" +#define LPASS_BE_PRI_TDM_TX_0 "PRI_TDM_TX_0" +#define LPASS_BE_PRI_TDM_RX_1 "PRI_TDM_RX_1" +#define LPASS_BE_PRI_TDM_TX_1 "PRI_TDM_TX_1" +#define LPASS_BE_PRI_TDM_RX_2 "PRI_TDM_RX_2" +#define LPASS_BE_PRI_TDM_TX_2 "PRI_TDM_TX_2" +#define LPASS_BE_PRI_TDM_RX_3 "PRI_TDM_RX_3" +#define LPASS_BE_PRI_TDM_TX_3 "PRI_TDM_TX_3" +#define LPASS_BE_PRI_TDM_RX_4 "PRI_TDM_RX_4" +#define LPASS_BE_PRI_TDM_TX_4 "PRI_TDM_TX_4" +#define LPASS_BE_PRI_TDM_RX_5 "PRI_TDM_RX_5" +#define LPASS_BE_PRI_TDM_TX_5 "PRI_TDM_TX_5" +#define LPASS_BE_PRI_TDM_RX_6 "PRI_TDM_RX_6" +#define LPASS_BE_PRI_TDM_TX_6 "PRI_TDM_TX_6" +#define LPASS_BE_PRI_TDM_RX_7 "PRI_TDM_RX_7" +#define LPASS_BE_PRI_TDM_TX_7 "PRI_TDM_TX_7" +#define LPASS_BE_SEC_TDM_RX_0 "SEC_TDM_RX_0" +#define LPASS_BE_SEC_TDM_TX_0 "SEC_TDM_TX_0" +#define LPASS_BE_SEC_TDM_RX_1 "SEC_TDM_RX_1" +#define LPASS_BE_SEC_TDM_TX_1 "SEC_TDM_TX_1" +#define LPASS_BE_SEC_TDM_RX_2 "SEC_TDM_RX_2" +#define LPASS_BE_SEC_TDM_TX_2 "SEC_TDM_TX_2" +#define LPASS_BE_SEC_TDM_RX_3 "SEC_TDM_RX_3" +#define LPASS_BE_SEC_TDM_TX_3 "SEC_TDM_TX_3" +#define LPASS_BE_SEC_TDM_RX_4 "SEC_TDM_RX_4" +#define LPASS_BE_SEC_TDM_TX_4 "SEC_TDM_TX_4" +#define LPASS_BE_SEC_TDM_RX_5 "SEC_TDM_RX_5" +#define LPASS_BE_SEC_TDM_TX_5 "SEC_TDM_TX_5" +#define LPASS_BE_SEC_TDM_RX_6 "SEC_TDM_RX_6" +#define LPASS_BE_SEC_TDM_TX_6 "SEC_TDM_TX_6" +#define LPASS_BE_SEC_TDM_RX_7 "SEC_TDM_RX_7" +#define LPASS_BE_SEC_TDM_TX_7 "SEC_TDM_TX_7" +#define LPASS_BE_TERT_TDM_RX_0 "TERT_TDM_RX_0" +#define LPASS_BE_TERT_TDM_TX_0 "TERT_TDM_TX_0" +#define LPASS_BE_TERT_TDM_RX_1 "TERT_TDM_RX_1" +#define LPASS_BE_TERT_TDM_TX_1 "TERT_TDM_TX_1" +#define LPASS_BE_TERT_TDM_RX_2 "TERT_TDM_RX_2" +#define LPASS_BE_TERT_TDM_TX_2 "TERT_TDM_TX_2" +#define LPASS_BE_TERT_TDM_RX_3 "TERT_TDM_RX_3" +#define LPASS_BE_TERT_TDM_TX_3 "TERT_TDM_TX_3" +#define LPASS_BE_TERT_TDM_RX_4 "TERT_TDM_RX_4" +#define LPASS_BE_TERT_TDM_TX_4 "TERT_TDM_TX_4" +#define LPASS_BE_TERT_TDM_RX_5 "TERT_TDM_RX_5" +#define LPASS_BE_TERT_TDM_TX_5 "TERT_TDM_TX_5" +#define LPASS_BE_TERT_TDM_RX_6 "TERT_TDM_RX_6" +#define LPASS_BE_TERT_TDM_TX_6 "TERT_TDM_TX_6" +#define LPASS_BE_TERT_TDM_RX_7 "TERT_TDM_RX_7" +#define LPASS_BE_TERT_TDM_TX_7 "TERT_TDM_TX_7" +#define LPASS_BE_QUAT_TDM_RX_0 "QUAT_TDM_RX_0" +#define LPASS_BE_QUAT_TDM_TX_0 "QUAT_TDM_TX_0" +#define LPASS_BE_QUAT_TDM_RX_1 "QUAT_TDM_RX_1" +#define LPASS_BE_QUAT_TDM_TX_1 "QUAT_TDM_TX_1" +#define LPASS_BE_QUAT_TDM_RX_2 "QUAT_TDM_RX_2" +#define LPASS_BE_QUAT_TDM_TX_2 "QUAT_TDM_TX_2" +#define LPASS_BE_QUAT_TDM_RX_3 "QUAT_TDM_RX_3" +#define LPASS_BE_QUAT_TDM_TX_3 "QUAT_TDM_TX_3" +#define LPASS_BE_QUAT_TDM_RX_4 "QUAT_TDM_RX_4" +#define LPASS_BE_QUAT_TDM_TX_4 "QUAT_TDM_TX_4" +#define LPASS_BE_QUAT_TDM_RX_5 "QUAT_TDM_RX_5" +#define LPASS_BE_QUAT_TDM_TX_5 "QUAT_TDM_TX_5" +#define LPASS_BE_QUAT_TDM_RX_6 "QUAT_TDM_RX_6" +#define LPASS_BE_QUAT_TDM_TX_6 "QUAT_TDM_TX_6" +#define LPASS_BE_QUAT_TDM_RX_7 "QUAT_TDM_RX_7" +#define LPASS_BE_QUAT_TDM_TX_7 "QUAT_TDM_TX_7" +#define LPASS_BE_QUIN_TDM_RX_0 "QUIN_TDM_RX_0" +#define LPASS_BE_QUIN_TDM_TX_0 "QUIN_TDM_TX_0" +#define LPASS_BE_QUIN_TDM_RX_1 "QUIN_TDM_RX_1" +#define LPASS_BE_QUIN_TDM_TX_1 "QUIN_TDM_TX_1" +#define LPASS_BE_QUIN_TDM_RX_2 "QUIN_TDM_RX_2" +#define LPASS_BE_QUIN_TDM_TX_2 "QUIN_TDM_TX_2" +#define LPASS_BE_QUIN_TDM_RX_3 "QUIN_TDM_RX_3" +#define LPASS_BE_QUIN_TDM_TX_3 "QUIN_TDM_TX_3" +#define LPASS_BE_QUIN_TDM_RX_4 "QUIN_TDM_RX_4" +#define LPASS_BE_QUIN_TDM_TX_4 "QUIN_TDM_TX_4" +#define LPASS_BE_QUIN_TDM_RX_5 "QUIN_TDM_RX_5" +#define LPASS_BE_QUIN_TDM_TX_5 "QUIN_TDM_TX_5" +#define LPASS_BE_QUIN_TDM_RX_6 "QUIN_TDM_RX_6" +#define LPASS_BE_QUIN_TDM_TX_6 "QUIN_TDM_TX_6" +#define LPASS_BE_QUIN_TDM_RX_7 "QUIN_TDM_RX_7" +#define LPASS_BE_QUIN_TDM_TX_7 "QUIN_TDM_TX_7" + +#define LPASS_BE_SLIMBUS_7_RX "SLIMBUS_7_RX" +#define LPASS_BE_SLIMBUS_7_TX "SLIMBUS_7_TX" +#define LPASS_BE_SLIMBUS_8_RX "SLIMBUS_8_RX" +#define LPASS_BE_SLIMBUS_8_TX "SLIMBUS_8_TX" +#define LPASS_BE_SLIMBUS_9_RX "SLIMBUS_9_RX" +#define LPASS_BE_SLIMBUS_9_TX "SLIMBUS_9_TX" + +#define LPASS_BE_USB_AUDIO_RX "USB_AUDIO_RX" +#define LPASS_BE_USB_AUDIO_TX "USB_AUDIO_TX" + +#define LPASS_BE_INT0_MI2S_RX "INT0_MI2S_RX" +#define LPASS_BE_INT0_MI2S_TX "INT0_MI2S_TX" +#define LPASS_BE_INT1_MI2S_RX "INT1_MI2S_RX" +#define LPASS_BE_INT1_MI2S_TX "INT1_MI2S_TX" +#define LPASS_BE_INT2_MI2S_RX "INT2_MI2S_RX" +#define LPASS_BE_INT2_MI2S_TX "INT2_MI2S_TX" +#define LPASS_BE_INT3_MI2S_RX "INT3_MI2S_RX" +#define LPASS_BE_INT3_MI2S_TX "INT3_MI2S_TX" +#define LPASS_BE_INT4_MI2S_RX "INT4_MI2S_RX" +#define LPASS_BE_INT4_MI2S_TX "INT4_MI2S_TX" +#define LPASS_BE_INT5_MI2S_RX "INT5_MI2S_RX" +#define LPASS_BE_INT5_MI2S_TX "INT5_MI2S_TX" +#define LPASS_BE_INT6_MI2S_RX "INT6_MI2S_RX" +#define LPASS_BE_INT6_MI2S_TX "INT6_MI2S_TX" + +#define LPASS_BE_WSA_CDC_DMA_RX_0 "WSA_CDC_DMA_RX_0" +#define LPASS_BE_WSA_CDC_DMA_TX_0 "WSA_CDC_DMA_TX_0" +#define LPASS_BE_WSA_CDC_DMA_RX_1 "WSA_CDC_DMA_RX_1" +#define LPASS_BE_WSA_CDC_DMA_TX_1 "WSA_CDC_DMA_TX_1" +#define LPASS_BE_WSA_CDC_DMA_TX_2 "WSA_CDC_DMA_TX_2" +#define LPASS_BE_VA_CDC_DMA_TX_0 "VA_CDC_DMA_TX_0" +#define LPASS_BE_VA_CDC_DMA_TX_1 "VA_CDC_DMA_TX_1" + +#define LPASS_BE_RX_CDC_DMA_RX_0 "RX_CDC_DMA_RX_0" +#define LPASS_BE_RX_CDC_DMA_RX_1 "RX_CDC_DMA_RX_1" +#define LPASS_BE_RX_CDC_DMA_RX_2 "RX_CDC_DMA_RX_2" +#define LPASS_BE_RX_CDC_DMA_RX_3 "RX_CDC_DMA_RX_3" +#define LPASS_BE_RX_CDC_DMA_RX_4 "RX_CDC_DMA_RX_4" +#define LPASS_BE_RX_CDC_DMA_RX_5 "RX_CDC_DMA_RX_5" +#define LPASS_BE_RX_CDC_DMA_RX_6 "RX_CDC_DMA_RX_6" +#define LPASS_BE_RX_CDC_DMA_RX_7 "RX_CDC_DMA_RX_7" +#define LPASS_BE_TX_CDC_DMA_TX_0 "TX_CDC_DMA_TX_0" +#define LPASS_BE_TX_CDC_DMA_TX_1 "TX_CDC_DMA_TX_1" +#define LPASS_BE_TX_CDC_DMA_TX_2 "TX_CDC_DMA_TX_2" +#define LPASS_BE_TX_CDC_DMA_TX_3 "TX_CDC_DMA_TX_3" +#define LPASS_BE_TX_CDC_DMA_TX_4 "TX_CDC_DMA_TX_4" +#define LPASS_BE_TX_CDC_DMA_TX_5 "TX_CDC_DMA_TX_5" + +/* For multimedia front-ends, asm session is allocated dynamically. + * Hence, asm session/multimedia front-end mapping has to be maintained. + * Due to this reason, additional multimedia front-end must be placed before + * non-multimedia front-ends. + */ + +enum { + MSM_FRONTEND_DAI_MULTIMEDIA1 = 0, + MSM_FRONTEND_DAI_MULTIMEDIA2, + MSM_FRONTEND_DAI_MULTIMEDIA3, + MSM_FRONTEND_DAI_MULTIMEDIA4, + MSM_FRONTEND_DAI_MULTIMEDIA5, + MSM_FRONTEND_DAI_MULTIMEDIA6, + MSM_FRONTEND_DAI_MULTIMEDIA7, + MSM_FRONTEND_DAI_MULTIMEDIA8, + MSM_FRONTEND_DAI_MULTIMEDIA9, + MSM_FRONTEND_DAI_MULTIMEDIA10, + MSM_FRONTEND_DAI_MULTIMEDIA11, + MSM_FRONTEND_DAI_MULTIMEDIA12, + MSM_FRONTEND_DAI_MULTIMEDIA13, + MSM_FRONTEND_DAI_MULTIMEDIA14, + MSM_FRONTEND_DAI_MULTIMEDIA15, + MSM_FRONTEND_DAI_MULTIMEDIA16, + MSM_FRONTEND_DAI_MULTIMEDIA17, + MSM_FRONTEND_DAI_MULTIMEDIA18, + MSM_FRONTEND_DAI_MULTIMEDIA19, + MSM_FRONTEND_DAI_MULTIMEDIA20, + MSM_FRONTEND_DAI_MULTIMEDIA21, + MSM_FRONTEND_DAI_MULTIMEDIA26, + MSM_FRONTEND_DAI_MULTIMEDIA27, + MSM_FRONTEND_DAI_MULTIMEDIA28, + MSM_FRONTEND_DAI_MULTIMEDIA29, + MSM_FRONTEND_DAI_MULTIMEDIA30, + MSM_FRONTEND_DAI_MULTIMEDIA31, + MSM_FRONTEND_DAI_VOIP, + MSM_FRONTEND_DAI_AFE_RX, + MSM_FRONTEND_DAI_AFE_TX, + MSM_FRONTEND_DAI_VOICE_STUB, + MSM_FRONTEND_DAI_DTMF_RX, + MSM_FRONTEND_DAI_QCHAT, + MSM_FRONTEND_DAI_VOLTE_STUB, + MSM_FRONTEND_DAI_LSM1, + MSM_FRONTEND_DAI_LSM2, + MSM_FRONTEND_DAI_LSM3, + MSM_FRONTEND_DAI_LSM4, + MSM_FRONTEND_DAI_LSM5, + MSM_FRONTEND_DAI_LSM6, + MSM_FRONTEND_DAI_LSM7, + MSM_FRONTEND_DAI_LSM8, + MSM_FRONTEND_DAI_VOICE2_STUB, + MSM_FRONTEND_DAI_VOICEMMODE1, + MSM_FRONTEND_DAI_VOICEMMODE2, + MSM_FRONTEND_DAI_MAX, +}; + +#define MSM_FRONTEND_DAI_MM_SIZE (MSM_FRONTEND_DAI_MULTIMEDIA31 + 1) +#define MSM_FRONTEND_DAI_MM_MAX_ID MSM_FRONTEND_DAI_MULTIMEDIA31 + +enum { + MSM_BACKEND_DAI_PRI_I2S_RX = 0, + MSM_BACKEND_DAI_PRI_I2S_TX, + MSM_BACKEND_DAI_SLIMBUS_0_RX, + MSM_BACKEND_DAI_SLIMBUS_0_TX, + MSM_BACKEND_DAI_HDMI_RX, + MSM_BACKEND_DAI_INT_BT_SCO_RX, + MSM_BACKEND_DAI_INT_BT_SCO_TX, + MSM_BACKEND_DAI_INT_FM_RX, + MSM_BACKEND_DAI_INT_FM_TX, + MSM_BACKEND_DAI_AFE_PCM_RX, + MSM_BACKEND_DAI_AFE_PCM_TX, + MSM_BACKEND_DAI_AUXPCM_RX, + MSM_BACKEND_DAI_AUXPCM_TX, + MSM_BACKEND_DAI_VOICE_PLAYBACK_TX, + MSM_BACKEND_DAI_VOICE2_PLAYBACK_TX, + MSM_BACKEND_DAI_INCALL_RECORD_RX, + MSM_BACKEND_DAI_INCALL_RECORD_TX, + MSM_BACKEND_DAI_MI2S_RX, + MSM_BACKEND_DAI_MI2S_TX, + MSM_BACKEND_DAI_SEC_I2S_RX, + MSM_BACKEND_DAI_SLIMBUS_1_RX, + MSM_BACKEND_DAI_SLIMBUS_1_TX, + MSM_BACKEND_DAI_SLIMBUS_2_RX, + MSM_BACKEND_DAI_SLIMBUS_2_TX, + MSM_BACKEND_DAI_SLIMBUS_3_RX, + MSM_BACKEND_DAI_SLIMBUS_3_TX, + MSM_BACKEND_DAI_SLIMBUS_4_RX, + MSM_BACKEND_DAI_SLIMBUS_4_TX, + MSM_BACKEND_DAI_SLIMBUS_5_RX, + MSM_BACKEND_DAI_SLIMBUS_5_TX, + MSM_BACKEND_DAI_SLIMBUS_6_RX, + MSM_BACKEND_DAI_SLIMBUS_6_TX, + MSM_BACKEND_DAI_SLIMBUS_7_RX, + MSM_BACKEND_DAI_SLIMBUS_7_TX, + MSM_BACKEND_DAI_SLIMBUS_8_RX, + MSM_BACKEND_DAI_SLIMBUS_8_TX, + MSM_BACKEND_DAI_EXTPROC_RX, + MSM_BACKEND_DAI_EXTPROC_TX, + MSM_BACKEND_DAI_EXTPROC_EC_TX, + MSM_BACKEND_DAI_QUATERNARY_MI2S_RX, + MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, + MSM_BACKEND_DAI_SECONDARY_MI2S_RX, + MSM_BACKEND_DAI_SECONDARY_MI2S_TX, + MSM_BACKEND_DAI_PRI_MI2S_RX, + MSM_BACKEND_DAI_PRI_MI2S_TX, + MSM_BACKEND_DAI_TERTIARY_MI2S_RX, + MSM_BACKEND_DAI_TERTIARY_MI2S_TX, + MSM_BACKEND_DAI_AUDIO_I2S_RX, + MSM_BACKEND_DAI_SEC_AUXPCM_RX, + MSM_BACKEND_DAI_SEC_AUXPCM_TX, + MSM_BACKEND_DAI_PRI_SPDIF_RX, + MSM_BACKEND_DAI_SECONDARY_MI2S_RX_SD1, + MSM_BACKEND_DAI_QUINARY_MI2S_RX, + MSM_BACKEND_DAI_QUINARY_MI2S_TX, + MSM_BACKEND_DAI_SENARY_MI2S_TX, + MSM_BACKEND_DAI_PRI_TDM_RX_0, + MSM_BACKEND_DAI_PRI_TDM_TX_0, + MSM_BACKEND_DAI_PRI_TDM_RX_1, + MSM_BACKEND_DAI_PRI_TDM_TX_1, + MSM_BACKEND_DAI_PRI_TDM_RX_2, + MSM_BACKEND_DAI_PRI_TDM_TX_2, + MSM_BACKEND_DAI_PRI_TDM_RX_3, + MSM_BACKEND_DAI_PRI_TDM_TX_3, + MSM_BACKEND_DAI_PRI_TDM_RX_4, + MSM_BACKEND_DAI_PRI_TDM_TX_4, + MSM_BACKEND_DAI_PRI_TDM_RX_5, + MSM_BACKEND_DAI_PRI_TDM_TX_5, + MSM_BACKEND_DAI_PRI_TDM_RX_6, + MSM_BACKEND_DAI_PRI_TDM_TX_6, + MSM_BACKEND_DAI_PRI_TDM_RX_7, + MSM_BACKEND_DAI_PRI_TDM_TX_7, + MSM_BACKEND_DAI_SEC_TDM_RX_0, + MSM_BACKEND_DAI_SEC_TDM_TX_0, + MSM_BACKEND_DAI_SEC_TDM_RX_1, + MSM_BACKEND_DAI_SEC_TDM_TX_1, + MSM_BACKEND_DAI_SEC_TDM_RX_2, + MSM_BACKEND_DAI_SEC_TDM_TX_2, + MSM_BACKEND_DAI_SEC_TDM_RX_3, + MSM_BACKEND_DAI_SEC_TDM_TX_3, + MSM_BACKEND_DAI_SEC_TDM_RX_4, + MSM_BACKEND_DAI_SEC_TDM_TX_4, + MSM_BACKEND_DAI_SEC_TDM_RX_5, + MSM_BACKEND_DAI_SEC_TDM_TX_5, + MSM_BACKEND_DAI_SEC_TDM_RX_6, + MSM_BACKEND_DAI_SEC_TDM_TX_6, + MSM_BACKEND_DAI_SEC_TDM_RX_7, + MSM_BACKEND_DAI_SEC_TDM_TX_7, + MSM_BACKEND_DAI_TERT_TDM_RX_0, + MSM_BACKEND_DAI_TERT_TDM_TX_0, + MSM_BACKEND_DAI_TERT_TDM_RX_1, + MSM_BACKEND_DAI_TERT_TDM_TX_1, + MSM_BACKEND_DAI_TERT_TDM_RX_2, + MSM_BACKEND_DAI_TERT_TDM_TX_2, + MSM_BACKEND_DAI_TERT_TDM_RX_3, + MSM_BACKEND_DAI_TERT_TDM_TX_3, + MSM_BACKEND_DAI_TERT_TDM_RX_4, + MSM_BACKEND_DAI_TERT_TDM_TX_4, + MSM_BACKEND_DAI_TERT_TDM_RX_5, + MSM_BACKEND_DAI_TERT_TDM_TX_5, + MSM_BACKEND_DAI_TERT_TDM_RX_6, + MSM_BACKEND_DAI_TERT_TDM_TX_6, + MSM_BACKEND_DAI_TERT_TDM_RX_7, + MSM_BACKEND_DAI_TERT_TDM_TX_7, + MSM_BACKEND_DAI_QUAT_TDM_RX_0, + MSM_BACKEND_DAI_QUAT_TDM_TX_0, + MSM_BACKEND_DAI_QUAT_TDM_RX_1, + MSM_BACKEND_DAI_QUAT_TDM_TX_1, + MSM_BACKEND_DAI_QUAT_TDM_RX_2, + MSM_BACKEND_DAI_QUAT_TDM_TX_2, + MSM_BACKEND_DAI_QUAT_TDM_RX_3, + MSM_BACKEND_DAI_QUAT_TDM_TX_3, + MSM_BACKEND_DAI_QUAT_TDM_RX_4, + MSM_BACKEND_DAI_QUAT_TDM_TX_4, + MSM_BACKEND_DAI_QUAT_TDM_RX_5, + MSM_BACKEND_DAI_QUAT_TDM_TX_5, + MSM_BACKEND_DAI_QUAT_TDM_RX_6, + MSM_BACKEND_DAI_QUAT_TDM_TX_6, + MSM_BACKEND_DAI_QUAT_TDM_RX_7, + MSM_BACKEND_DAI_QUAT_TDM_TX_7, + MSM_BACKEND_DAI_QUIN_TDM_RX_0, + MSM_BACKEND_DAI_QUIN_TDM_TX_0, + MSM_BACKEND_DAI_QUIN_TDM_RX_1, + MSM_BACKEND_DAI_QUIN_TDM_TX_1, + MSM_BACKEND_DAI_QUIN_TDM_RX_2, + MSM_BACKEND_DAI_QUIN_TDM_TX_2, + MSM_BACKEND_DAI_QUIN_TDM_RX_3, + MSM_BACKEND_DAI_QUIN_TDM_TX_3, + MSM_BACKEND_DAI_QUIN_TDM_RX_4, + MSM_BACKEND_DAI_QUIN_TDM_TX_4, + MSM_BACKEND_DAI_QUIN_TDM_RX_5, + MSM_BACKEND_DAI_QUIN_TDM_TX_5, + MSM_BACKEND_DAI_QUIN_TDM_RX_6, + MSM_BACKEND_DAI_QUIN_TDM_TX_6, + MSM_BACKEND_DAI_QUIN_TDM_RX_7, + MSM_BACKEND_DAI_QUIN_TDM_TX_7, + MSM_BACKEND_DAI_INT_BT_A2DP_RX, + MSM_BACKEND_DAI_USB_RX, + MSM_BACKEND_DAI_USB_TX, + MSM_BACKEND_DAI_DISPLAY_PORT_RX, + MSM_BACKEND_DAI_DISPLAY_PORT_RX_1, + MSM_BACKEND_DAI_TERT_AUXPCM_RX, + MSM_BACKEND_DAI_TERT_AUXPCM_TX, + MSM_BACKEND_DAI_QUAT_AUXPCM_RX, + MSM_BACKEND_DAI_QUAT_AUXPCM_TX, + MSM_BACKEND_DAI_QUIN_AUXPCM_RX, + MSM_BACKEND_DAI_QUIN_AUXPCM_TX, + MSM_BACKEND_DAI_INT0_MI2S_RX, + MSM_BACKEND_DAI_INT0_MI2S_TX, + MSM_BACKEND_DAI_INT1_MI2S_RX, + MSM_BACKEND_DAI_INT1_MI2S_TX, + MSM_BACKEND_DAI_INT2_MI2S_RX, + MSM_BACKEND_DAI_INT2_MI2S_TX, + MSM_BACKEND_DAI_INT3_MI2S_RX, + MSM_BACKEND_DAI_INT3_MI2S_TX, + MSM_BACKEND_DAI_INT4_MI2S_RX, + MSM_BACKEND_DAI_INT4_MI2S_TX, + MSM_BACKEND_DAI_INT5_MI2S_RX, + MSM_BACKEND_DAI_INT5_MI2S_TX, + MSM_BACKEND_DAI_INT6_MI2S_RX, + MSM_BACKEND_DAI_INT6_MI2S_TX, + MSM_BACKEND_DAI_SEN_AUXPCM_RX, + MSM_BACKEND_DAI_SEN_AUXPCM_TX, + MSM_BACKEND_DAI_SENARY_MI2S_RX, + MSM_BACKEND_DAI_WSA_CDC_DMA_RX_0, + MSM_BACKEND_DAI_WSA_CDC_DMA_TX_0, + MSM_BACKEND_DAI_WSA_CDC_DMA_RX_1, + MSM_BACKEND_DAI_WSA_CDC_DMA_TX_1, + MSM_BACKEND_DAI_WSA_CDC_DMA_TX_2, + MSM_BACKEND_DAI_VA_CDC_DMA_TX_0, + MSM_BACKEND_DAI_VA_CDC_DMA_TX_1, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_0, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_0, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_1, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_1, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_2, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_2, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_3, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_3, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_4, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_4, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_5, + MSM_BACKEND_DAI_TX_CDC_DMA_TX_5, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_6, + MSM_BACKEND_DAI_RX_CDC_DMA_RX_7, + MSM_BACKEND_DAI_PRI_SPDIF_TX, + MSM_BACKEND_DAI_SEC_SPDIF_RX, + MSM_BACKEND_DAI_SEC_SPDIF_TX, + MSM_BACKEND_DAI_SLIMBUS_9_RX, + MSM_BACKEND_DAI_SLIMBUS_9_TX, + MSM_BACKEND_DAI_MAX, +}; + +enum msm_pcm_routing_event { + MSM_PCM_RT_EVT_BUF_RECFG, + MSM_PCM_RT_EVT_DEVSWITCH, + MSM_PCM_RT_EVT_MAX, +}; + +enum { + EXT_EC_REF_NONE = 0, + EXT_EC_REF_PRI_MI2S_TX, + EXT_EC_REF_SEC_MI2S_TX, + EXT_EC_REF_TERT_MI2S_TX, + EXT_EC_REF_QUAT_MI2S_TX, + EXT_EC_REF_QUIN_MI2S_TX, + EXT_EC_REF_SLIM_1_TX, + EXT_EC_REF_SEC_TDM_TX, +}; + +#define INVALID_SESSION -1 +#define SESSION_TYPE_RX 0 +#define SESSION_TYPE_TX 1 +#define MAX_SESSION_TYPES 2 +#define INT_RX_VOL_MAX_STEPS 0x2000 +#define INT_RX_VOL_GAIN 0x2000 + +#define RELEASE_LOCK 0 +#define ACQUIRE_LOCK 1 + +#define MSM_BACKEND_DAI_PP_PARAMS_REQ_MAX 2 +#define HDMI_RX_ID 0x8001 +#define ADM_PP_PARAM_MUTE_ID 0 +#define ADM_PP_PARAM_MUTE_BIT 1 +#define ADM_PP_PARAM_LATENCY_ID 1 +#define ADM_PP_PARAM_LATENCY_BIT 2 +#define BE_DAI_PORT_SESSIONS_IDX_MAX 4 +#define BE_DAI_FE_SESSIONS_IDX_MAX 2 + +#define STREAM_TYPE_ASM 0 +#define STREAM_TYPE_LSM 1 + +enum { + ADM_TOPOLOGY_CAL_TYPE_IDX = 0, + ADM_LSM_TOPOLOGY_CAL_TYPE_IDX, + MAX_ROUTING_CAL_TYPES +}; + +struct msm_pcm_routing_evt { + void (*event_func)(enum msm_pcm_routing_event, void *); + void *priv_data; +}; + +struct msm_pcm_routing_bdai_data { + u16 port_id; /* AFE port ID */ + u8 active; /* track if this backend is enabled */ + + /* Front-end sessions */ + unsigned long fe_sessions[BE_DAI_FE_SESSIONS_IDX_MAX]; + /* + * Track Tx BE ports -> Rx BE ports. + * port_sessions[0] used to track BE 0 to BE 63. + * port_sessions[1] used to track BE 64 to BE 127. + * port_sessions[2] used to track BE 128 to BE 191. + * port_sessions[3] used to track BE 192 to BE 255. + */ + u64 port_sessions[BE_DAI_PORT_SESSIONS_IDX_MAX]; + + unsigned int sample_rate; + unsigned int channel; + unsigned int format; + unsigned int adm_override_ch; + char *name; +}; + +struct msm_pcm_routing_fdai_data { + u16 be_srate; /* track prior backend sample rate for flushing purpose */ + int strm_id; /* ASM stream ID */ + int perf_mode; + struct msm_pcm_routing_evt event_info; + u32 passthr_mode; +}; + +#define MAX_APP_TYPES 16 +struct msm_pcm_routing_app_type_data { + int app_type; + u32 sample_rate; + int bit_width; + u32 num_out_channels; +}; + +struct msm_pcm_stream_app_type_cfg { + int app_type; + int acdb_dev_id; + int sample_rate; +}; + +/* dai_id: front-end ID, + * dspst_id: DSP audio stream ID + * stream_type: playback or capture + */ +int msm_pcm_routing_reg_phy_stream(int fedai_id, int perf_mode, int dspst_id, + int stream_type); +void msm_pcm_routing_reg_psthr_stream(int fedai_id, int dspst_id, + int stream_type); +int msm_pcm_routing_reg_phy_compr_stream(int fedai_id, int perf_mode, + int dspst_id, int stream_type, + uint32_t compr_passthr); + +int msm_pcm_routing_reg_phy_stream_v2(int fedai_id, int perf_mode, + int dspst_id, int stream_type, + struct msm_pcm_routing_evt event_info); + +void msm_pcm_routing_dereg_phy_stream(int fedai_id, int stream_type); + +int msm_routing_check_backend_enabled(int fedai_id); + + +void msm_pcm_routing_get_bedai_info(int be_idx, + struct msm_pcm_routing_bdai_data *bedai); +void msm_pcm_routing_get_fedai_info(int fe_idx, int sess_type, + struct msm_pcm_routing_fdai_data *fe_dai); +void msm_pcm_routing_acquire_lock(void); +void msm_pcm_routing_release_lock(void); + +int msm_pcm_routing_reg_stream_app_type_cfg( + int fedai_id, int session_type, int be_id, + struct msm_pcm_stream_app_type_cfg *cfg_data); +int msm_pcm_routing_get_stream_app_type_cfg( + int fedai_id, int session_type, int *be_id, + struct msm_pcm_stream_app_type_cfg *cfg_data); +int msm_pcm_routing_send_chmix_cfg(int fe_id, int ip_channel_cnt, + int op_channel_cnt, int *ch_wght_coeff, + int session_type, int stream_type); +int msm_pcm_routing_get_pp_ch_cnt(int fe_id, int session_type); +#endif /*_MSM_PCM_H*/ diff --git a/audio-kernel/asoc/msm-pcm-voice-v2.c b/audio-kernel/asoc/msm-pcm-voice-v2.c new file mode 100644 index 000000000..38da7e7d7 --- /dev/null +++ b/audio-kernel/asoc/msm-pcm-voice-v2.c @@ -0,0 +1,800 @@ +/* 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 + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "msm-pcm-voice-v2.h" + +static struct msm_voice voice_info[VOICE_SESSION_INDEX_MAX]; + +static struct snd_pcm_hardware msm_pcm_hardware = { + + .info = (SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_RESUME), + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000, + .rate_min = 8000, + .rate_max = 16000, + .channels_min = 1, + .channels_max = 1, + + .buffer_bytes_max = 4096 * 2, + .period_bytes_min = 2048, + .period_bytes_max = 4096, + .periods_min = 2, + .periods_max = 4, + + .fifo_size = 0, +}; +static bool is_volte(struct msm_voice *pvolte) +{ + if (pvolte == &voice_info[VOLTE_SESSION_INDEX]) + return true; + else + return false; +} + +static bool is_voice2(struct msm_voice *pvoice2) +{ + if (pvoice2 == &voice_info[VOICE2_SESSION_INDEX]) + return true; + else + return false; +} + +static bool is_qchat(struct msm_voice *pqchat) +{ + if (pqchat == &voice_info[QCHAT_SESSION_INDEX]) + return true; + else + return false; +} + +static bool is_vowlan(struct msm_voice *pvowlan) +{ + if (pvowlan == &voice_info[VOWLAN_SESSION_INDEX]) + return true; + else + return false; +} + +static bool is_voicemmode1(struct msm_voice *pvoicemmode1) +{ + if (pvoicemmode1 == &voice_info[VOICEMMODE1_INDEX]) + return true; + else + return false; +} + +static bool is_voicemmode2(struct msm_voice *pvoicemmode2) +{ + if (pvoicemmode2 == &voice_info[VOICEMMODE2_INDEX]) + return true; + else + return false; +} + +static uint32_t get_session_id(struct msm_voice *pvoc) +{ + uint32_t session_id = 0; + + if (is_volte(pvoc)) + session_id = voc_get_session_id(VOLTE_SESSION_NAME); + else if (is_voice2(pvoc)) + session_id = voc_get_session_id(VOICE2_SESSION_NAME); + else if (is_qchat(pvoc)) + session_id = voc_get_session_id(QCHAT_SESSION_NAME); + else if (is_vowlan(pvoc)) + session_id = voc_get_session_id(VOWLAN_SESSION_NAME); + else if (is_voicemmode1(pvoc)) + session_id = voc_get_session_id(VOICEMMODE1_NAME); + else if (is_voicemmode2(pvoc)) + session_id = voc_get_session_id(VOICEMMODE2_NAME); + else + session_id = voc_get_session_id(VOICE_SESSION_NAME); + + return session_id; +} + + +static int msm_pcm_playback_prepare(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct msm_voice *prtd = runtime->private_data; + + pr_debug("%s\n", __func__); + + if (!prtd->playback_start) + prtd->playback_start = 1; + + return 0; +} + +static int msm_pcm_capture_prepare(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct msm_voice *prtd = runtime->private_data; + + pr_debug("%s\n", __func__); + + if (!prtd->capture_start) + prtd->capture_start = 1; + + return 0; +} +static int msm_pcm_open(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct msm_voice *voice; + + if (!strncmp("VoLTE", substream->pcm->id, 5)) { + voice = &voice_info[VOLTE_SESSION_INDEX]; + pr_debug("%s: Open VoLTE Substream Id=%s\n", + __func__, substream->pcm->id); + } else if (!strncmp("Voice2", substream->pcm->id, 6)) { + voice = &voice_info[VOICE2_SESSION_INDEX]; + pr_debug("%s: Open Voice2 Substream Id=%s\n", + __func__, substream->pcm->id); + } else if (!strncmp("QCHAT", substream->pcm->id, 5)) { + voice = &voice_info[QCHAT_SESSION_INDEX]; + pr_debug("%s: Open QCHAT Substream Id=%s\n", + __func__, substream->pcm->id); + } else if (!strncmp("VoWLAN", substream->pcm->id, 6)) { + voice = &voice_info[VOWLAN_SESSION_INDEX]; + pr_debug("%s: Open VoWLAN Substream Id=%s\n", + __func__, substream->pcm->id); + } else if (!strncmp("VoiceMMode1", substream->pcm->id, 11)) { + voice = &voice_info[VOICEMMODE1_INDEX]; + pr_debug("%s: Open VoiceMMode1 Substream Id=%s\n", + __func__, substream->pcm->id); + } else if (!strncmp("VoiceMMode2", substream->pcm->id, 11)) { + voice = &voice_info[VOICEMMODE2_INDEX]; + pr_debug("%s: Open VoiceMMode2 Substream Id=%s\n", + __func__, substream->pcm->id); + } else { + voice = &voice_info[VOICE_SESSION_INDEX]; + pr_debug("%s: Open VOICE Substream Id=%s\n", + __func__, substream->pcm->id); + } + mutex_lock(&voice->lock); + + runtime->hw = msm_pcm_hardware; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + voice->playback_substream = substream; + else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + voice->capture_substream = substream; + + voice->instance++; + pr_debug("%s: Instance = %d, Stream ID = %s\n", + __func__, voice->instance, substream->pcm->id); + runtime->private_data = voice; + + mutex_unlock(&voice->lock); + + return 0; +} +static int msm_pcm_playback_close(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct msm_voice *prtd = runtime->private_data; + + pr_debug("%s\n", __func__); + + if (prtd->playback_start) + prtd->playback_start = 0; + + prtd->playback_substream = NULL; + + return 0; +} +static int msm_pcm_capture_close(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct msm_voice *prtd = runtime->private_data; + + pr_debug("%s\n", __func__); + + if (prtd->capture_start) + prtd->capture_start = 0; + prtd->capture_substream = NULL; + + return 0; +} +static int msm_pcm_close(struct snd_pcm_substream *substream) +{ + + struct snd_pcm_runtime *runtime = substream->runtime; + struct msm_voice *prtd = runtime->private_data; + uint32_t session_id = 0; + int ret = 0; + + mutex_lock(&prtd->lock); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + ret = msm_pcm_playback_close(substream); + else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + ret = msm_pcm_capture_close(substream); + + prtd->instance--; + if (!prtd->playback_start && !prtd->capture_start) { + pr_debug("end voice call\n"); + + session_id = get_session_id(prtd); + if (session_id) + voc_end_voice_call(session_id); + } + mutex_unlock(&prtd->lock); + + return ret; +} +static int msm_pcm_prepare(struct snd_pcm_substream *substream) +{ + int ret = 0; + struct snd_pcm_runtime *runtime = substream->runtime; + struct msm_voice *prtd = runtime->private_data; + uint32_t session_id = 0; + + mutex_lock(&prtd->lock); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + ret = msm_pcm_playback_prepare(substream); + else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + ret = msm_pcm_capture_prepare(substream); + + if (prtd->playback_start && prtd->capture_start) { + session_id = get_session_id(prtd); + if (session_id) + voc_start_voice_call(session_id); + } + mutex_unlock(&prtd->lock); + + return ret; +} + +static int msm_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + + pr_debug("%s: Voice\n", __func__); + + snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); + + return 0; +} + +static int msm_pcm_trigger(struct snd_pcm_substream *substream, int cmd) +{ + int ret = 0; + struct snd_pcm_runtime *runtime = substream->runtime; + struct msm_voice *prtd = runtime->private_data; + uint32_t session_id = 0; + + pr_debug("%s: cmd = %d\n", __func__, cmd); + + session_id = get_session_id(prtd); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_STOP: + pr_debug("Start & Stop Voice call not handled in Trigger.\n"); + break; + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + pr_debug("%s: resume call session_id = %d\n", __func__, + session_id); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + ret = msm_pcm_playback_prepare(substream); + else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + ret = msm_pcm_capture_prepare(substream); + if (prtd->playback_start && prtd->capture_start) { + if (session_id) + voc_resume_voice_call(session_id); + } + break; + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + pr_debug("%s: pause call session_id=%d\n", + __func__, session_id); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + if (prtd->playback_start) + prtd->playback_start = 0; + } else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + if (prtd->capture_start) + prtd->capture_start = 0; + } + if (session_id) + voc_standby_voice_call(session_id); + break; + default: + ret = -EINVAL; + break; + } + return ret; +} + +static int msm_pcm_ioctl(struct snd_pcm_substream *substream, + unsigned int cmd, void *arg) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct msm_voice *prtd = runtime->private_data; + uint32_t session_id = get_session_id(prtd); + enum voice_lch_mode lch_mode; + int ret = 0; + + switch (cmd) { + case SNDRV_VOICE_IOCTL_LCH: + if (copy_from_user(&lch_mode, (void *)arg, + sizeof(enum voice_lch_mode))) { + pr_err("%s: Copy from user failed, size %zd\n", + __func__, sizeof(enum voice_lch_mode)); + + ret = -EFAULT; + break; + } + + pr_debug("%s: %s lch_mode:%d\n", + __func__, substream->pcm->id, lch_mode); + + switch (lch_mode) { + case VOICE_LCH_START: + case VOICE_LCH_STOP: + ret = voc_set_lch(session_id, lch_mode); + break; + + default: + pr_err("%s: Invalid LCH MODE %d\n", __func__, lch_mode); + + ret = -EFAULT; + } + + break; + default: + pr_debug("%s: Falling into default snd_lib_ioctl cmd 0x%x\n", + __func__, cmd); + + ret = snd_pcm_lib_ioctl(substream, cmd, arg); + break; + } + + if (!ret) + pr_debug("%s: ret %d\n", __func__, ret); + else + pr_err("%s: cmd 0x%x failed %d\n", __func__, cmd, ret); + + return ret; +} + +static int msm_voice_sidetone_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret; + long value = ucontrol->value.integer.value[0]; + bool sidetone_enable = value; + uint32_t session_id = ALL_SESSION_VSID; + + if (value < 0) { + pr_err("%s: Invalid arguments sidetone enable %ld\n", + __func__, value); + ret = -EINVAL; + return ret; + } + ret = voc_set_afe_sidetone(session_id, sidetone_enable); + pr_debug("%s: AFE Sidetone enable=%d session_id=0x%x ret=%d\n", + __func__, sidetone_enable, session_id, ret); + return ret; +} + +static int msm_voice_sidetone_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = voc_get_afe_sidetone(); + return 0; +} + +static int msm_voice_gain_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret = 0; + int volume = ucontrol->value.integer.value[0]; + uint32_t session_id = ucontrol->value.integer.value[1]; + int ramp_duration = ucontrol->value.integer.value[2]; + + if ((volume < 0) || (ramp_duration < 0) + || (ramp_duration > MAX_RAMP_DURATION)) { + pr_err(" %s Invalid arguments", __func__); + + ret = -EINVAL; + goto done; + } + + pr_debug("%s: volume: %d session_id: %#x ramp_duration: %d\n", __func__, + volume, session_id, ramp_duration); + + voc_set_rx_vol_step(session_id, RX_PATH, volume, ramp_duration); + +done: + return ret; +} + +static int msm_voice_mute_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret = 0; + int mute = ucontrol->value.integer.value[0]; + uint32_t session_id = ucontrol->value.integer.value[1]; + int ramp_duration = ucontrol->value.integer.value[2]; + + if ((mute < 0) || (mute > 1) || (ramp_duration < 0) + || (ramp_duration > MAX_RAMP_DURATION)) { + pr_err(" %s Invalid arguments", __func__); + + ret = -EINVAL; + goto done; + } + + pr_debug("%s: mute=%d session_id=%#x ramp_duration=%d\n", __func__, + mute, session_id, ramp_duration); + + ret = voc_set_tx_mute(session_id, TX_PATH, mute, ramp_duration); + +done: + return ret; +} + +static int msm_voice_tx_device_mute_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret = 0; + int mute = ucontrol->value.integer.value[0]; + uint32_t session_id = ucontrol->value.integer.value[1]; + int ramp_duration = ucontrol->value.integer.value[2]; + + if ((mute < 0) || (mute > 1) || (ramp_duration < 0) || + (ramp_duration > MAX_RAMP_DURATION)) { + pr_err(" %s Invalid arguments", __func__); + + ret = -EINVAL; + goto done; + } + + pr_debug("%s: mute=%d session_id=%#x ramp_duration=%d\n", __func__, + mute, session_id, ramp_duration); + + ret = voc_set_device_mute(session_id, VSS_IVOLUME_DIRECTION_TX, + mute, ramp_duration); + +done: + return ret; +} + +static int msm_voice_rx_device_mute_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret = 0; + int mute = ucontrol->value.integer.value[0]; + uint32_t session_id = ucontrol->value.integer.value[1]; + int ramp_duration = ucontrol->value.integer.value[2]; + + if ((mute < 0) || (mute > 1) || (ramp_duration < 0) || + (ramp_duration > MAX_RAMP_DURATION)) { + pr_err(" %s Invalid arguments", __func__); + + ret = -EINVAL; + goto done; + } + + pr_debug("%s: mute=%d session_id=%#x ramp_duration=%d\n", __func__, + mute, session_id, ramp_duration); + + voc_set_device_mute(session_id, VSS_IVOLUME_DIRECTION_RX, + mute, ramp_duration); + +done: + return ret; +} + +static int msm_voice_mbd_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = voc_get_mbd_enable(); + return 0; +} + +static int msm_voice_mbd_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + bool enable = ucontrol->value.integer.value[0]; + + voc_set_mbd_enable(enable); + + return 0; +} + + +static const char * const tty_mode[] = {"OFF", "HCO", "VCO", "FULL"}; +static const struct soc_enum msm_tty_mode_enum[] = { + SOC_ENUM_SINGLE_EXT(4, tty_mode), +}; + +static int msm_voice_tty_mode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = + voc_get_tty_mode(voc_get_session_id(VOICE_SESSION_NAME)); + return 0; +} + +static int msm_voice_tty_mode_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int tty_mode = ucontrol->value.integer.value[0]; + + pr_debug("%s: tty_mode=%d\n", __func__, tty_mode); + + voc_set_tty_mode(voc_get_session_id(VOICE_SESSION_NAME), tty_mode); + voc_set_tty_mode(voc_get_session_id(VOICE2_SESSION_NAME), tty_mode); + voc_set_tty_mode(voc_get_session_id(VOLTE_SESSION_NAME), tty_mode); + voc_set_tty_mode(voc_get_session_id(VOWLAN_SESSION_NAME), tty_mode); + voc_set_tty_mode(voc_get_session_id(VOICEMMODE1_NAME), tty_mode); + voc_set_tty_mode(voc_get_session_id(VOICEMMODE2_NAME), tty_mode); + + return 0; +} + +static int msm_voice_slowtalk_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int st_enable = ucontrol->value.integer.value[0]; + uint32_t session_id = ucontrol->value.integer.value[1]; + struct module_instance_info mod_inst_info; + + memset(&mod_inst_info, 0, sizeof(mod_inst_info)); + pr_debug("%s: st enable=%d session_id=%#x\n", __func__, st_enable, + session_id); + + mod_inst_info.module_id = MODULE_ID_VOICE_MODULE_ST; + mod_inst_info.instance_id = INSTANCE_ID_0; + voc_set_pp_enable(session_id, mod_inst_info, st_enable); + + return 0; +} + +static int msm_voice_hd_voice_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret = 0; + uint32_t hd_enable = ucontrol->value.integer.value[0]; + uint32_t session_id = ucontrol->value.integer.value[1]; + + pr_debug("%s: HD Voice enable=%d session_id=%#x\n", __func__, hd_enable, + session_id); + + ret = voc_set_hd_enable(session_id, hd_enable); + + return ret; +} + +static int msm_voice_topology_disable_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret = 0; + int disable = ucontrol->value.integer.value[0]; + uint32_t session_id = ucontrol->value.integer.value[1]; + + if ((disable < 0) || (disable > 1)) { + pr_err(" %s Invalid arguments: %d\n", __func__, disable); + + ret = -EINVAL; + goto done; + } + pr_debug("%s: disable = %d, session_id = %d\n", __func__, disable, + session_id); + + ret = voc_disable_topology(session_id, disable); + +done: + return ret; +} + +static int msm_voice_cvd_version_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + int ret = 0; + + pr_debug("%s:\n", __func__); + + uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; + uinfo->count = CVD_VERSION_STRING_MAX_SIZE; + + return ret; +} + +static int msm_voice_cvd_version_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + char cvd_version[CVD_VERSION_STRING_MAX_SIZE] = CVD_VERSION_DEFAULT; + int ret; + + pr_debug("%s:\n", __func__); + + ret = voc_get_cvd_version(cvd_version); + + if (ret) + pr_err("%s: Error retrieving CVD version, error:%d\n", + __func__, ret); + + memcpy(ucontrol->value.bytes.data, cvd_version, sizeof(cvd_version)); + + return 0; +} +static struct snd_kcontrol_new msm_voice_controls[] = { + SOC_SINGLE_MULTI_EXT("Voice Rx Device Mute", SND_SOC_NOPM, 0, VSID_MAX, + 0, 3, NULL, msm_voice_rx_device_mute_put), + SOC_SINGLE_MULTI_EXT("Voice Tx Device Mute", SND_SOC_NOPM, 0, VSID_MAX, + 0, 3, NULL, msm_voice_tx_device_mute_put), + SOC_SINGLE_MULTI_EXT("Voice Tx Mute", SND_SOC_NOPM, 0, VSID_MAX, + 0, 3, NULL, msm_voice_mute_put), + SOC_SINGLE_MULTI_EXT("Voice Rx Gain", SND_SOC_NOPM, 0, VSID_MAX, 0, 3, + NULL, msm_voice_gain_put), + SOC_ENUM_EXT("TTY Mode", msm_tty_mode_enum[0], msm_voice_tty_mode_get, + msm_voice_tty_mode_put), + SOC_SINGLE_MULTI_EXT("Slowtalk Enable", SND_SOC_NOPM, 0, VSID_MAX, 0, 2, + NULL, msm_voice_slowtalk_put), + SOC_SINGLE_MULTI_EXT("Voice Topology Disable", SND_SOC_NOPM, 0, + VSID_MAX, 0, 2, NULL, + msm_voice_topology_disable_put), + SOC_SINGLE_MULTI_EXT("HD Voice Enable", SND_SOC_NOPM, 0, VSID_MAX, 0, 2, + NULL, msm_voice_hd_voice_put), + { + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "CVD Version", + .info = msm_voice_cvd_version_info, + .get = msm_voice_cvd_version_get, + }, + SOC_SINGLE_MULTI_EXT("Voice Sidetone Enable", SND_SOC_NOPM, 0, 1, 0, 1, + msm_voice_sidetone_get, msm_voice_sidetone_put), + SOC_SINGLE_BOOL_EXT("Voice Mic Break Enable", 0, msm_voice_mbd_get, + msm_voice_mbd_put), +}; + +static const struct snd_pcm_ops msm_pcm_ops = { + .open = msm_pcm_open, + .hw_params = msm_pcm_hw_params, + .close = msm_pcm_close, + .prepare = msm_pcm_prepare, + .trigger = msm_pcm_trigger, + .ioctl = msm_pcm_ioctl, + .compat_ioctl = msm_pcm_ioctl, +}; + + +static int msm_asoc_pcm_new(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_card *card = rtd->card->snd_card; + int ret = 0; + + if (!card->dev->coherent_dma_mask) + card->dev->coherent_dma_mask = DMA_BIT_MASK(32); + return ret; +} + +static int msm_pcm_voice_probe(struct snd_soc_platform *platform) +{ + snd_soc_add_platform_controls(platform, msm_voice_controls, + ARRAY_SIZE(msm_voice_controls)); + + return 0; +} + +static struct snd_soc_platform_driver msm_soc_platform = { + .ops = &msm_pcm_ops, + .pcm_new = msm_asoc_pcm_new, + .probe = msm_pcm_voice_probe, +}; + +static int msm_pcm_probe(struct platform_device *pdev) +{ + int rc; + bool destroy_cvd = false; + const char *is_destroy_cvd = "qcom,destroy-cvd"; + + if (!is_voc_initialized()) { + pr_debug("%s: voice module not initialized yet, deferring probe()\n", + __func__); + + rc = -EPROBE_DEFER; + goto done; + } + + rc = voc_alloc_cal_shared_memory(); + if (rc == -EPROBE_DEFER) { + pr_debug("%s: memory allocation for calibration deferred %d\n", + __func__, rc); + + goto done; + } else if (rc < 0) { + pr_err("%s: memory allocation for calibration failed %d\n", + __func__, rc); + } + + pr_debug("%s: dev name %s\n", + __func__, dev_name(&pdev->dev)); + destroy_cvd = of_property_read_bool(pdev->dev.of_node, + is_destroy_cvd); + voc_set_destroy_cvd_flag(destroy_cvd); + + rc = snd_soc_register_platform(&pdev->dev, + &msm_soc_platform); + +done: + return rc; +} + +static int msm_pcm_remove(struct platform_device *pdev) +{ + snd_soc_unregister_platform(&pdev->dev); + return 0; +} + +static const struct of_device_id msm_voice_dt_match[] = { + {.compatible = "qcom,msm-pcm-voice"}, + {} +}; +MODULE_DEVICE_TABLE(of, msm_voice_dt_match); + +static struct platform_driver msm_pcm_driver = { + .driver = { + .name = "msm-pcm-voice", + .owner = THIS_MODULE, + .of_match_table = msm_voice_dt_match, + }, + .probe = msm_pcm_probe, + .remove = msm_pcm_remove, +}; + +int __init msm_pcm_voice_init(void) +{ + int i = 0; + + memset(&voice_info, 0, sizeof(voice_info)); + + for (i = 0; i < VOICE_SESSION_INDEX_MAX; i++) + mutex_init(&voice_info[i].lock); + + return platform_driver_register(&msm_pcm_driver); +} + +void msm_pcm_voice_exit(void) +{ + platform_driver_unregister(&msm_pcm_driver); +} + +MODULE_DESCRIPTION("Voice PCM module platform driver"); +MODULE_LICENSE("GPL v2"); diff --git a/audio-kernel/asoc/msm-pcm-voice-v2.h b/audio-kernel/asoc/msm-pcm-voice-v2.h new file mode 100644 index 000000000..60f5adcf5 --- /dev/null +++ b/audio-kernel/asoc/msm-pcm-voice-v2.h @@ -0,0 +1,42 @@ +/* Copyright (c) 2012-2015, 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 _MSM_PCM_VOICE_H +#define _MSM_PCM_VOICE_H +#include + +enum { + VOICE_SESSION_INDEX, + VOLTE_SESSION_INDEX, + VOICE2_SESSION_INDEX, + QCHAT_SESSION_INDEX, + VOWLAN_SESSION_INDEX, + VOICEMMODE1_INDEX, + VOICEMMODE2_INDEX, + VOICE_SESSION_INDEX_MAX, +}; + +struct msm_voice { + struct snd_pcm_substream *playback_substream; + struct snd_pcm_substream *capture_substream; + + int instance; + + struct mutex lock; + + uint32_t samp_rate; + uint32_t channel_mode; + + int playback_start; + int capture_start; +}; + +#endif /*_MSM_PCM_VOICE_H*/ diff --git a/audio-kernel/asoc/msm-pcm-voip-v2.c b/audio-kernel/asoc/msm-pcm-voip-v2.c new file mode 100644 index 000000000..b8d7390bb --- /dev/null +++ b/audio-kernel/asoc/msm-pcm-voip-v2.c @@ -0,0 +1,1708 @@ +/* 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 + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "msm-pcm-q6-v2.h" +#include "msm-pcm-routing-v2.h" + +#define SHARED_MEM_BUF 2 +#define VOIP_MAX_Q_LEN 10 +#define VOIP_MAX_VOC_PKT_SIZE 4096 +#define VOIP_MIN_VOC_PKT_SIZE 320 + +/* Length of the DSP frame info header added to the voc packet. */ +#define DSP_FRAME_HDR_LEN 1 + +#define MODE_IS127 0x2 +#define MODE_4GV_NB 0x3 +#define MODE_4GV_WB 0x4 +#define MODE_AMR 0x5 +#define MODE_AMR_WB 0xD +#define MODE_PCM 0xC +#define MODE_4GV_NW 0xE +#define MODE_G711 0xA +#define MODE_G711A 0xF + +enum msm_audio_g711a_frame_type { + MVS_G711A_SPEECH_GOOD, + MVS_G711A_SID, + MVS_G711A_NO_DATA, + MVS_G711A_ERASURE +}; + +enum msm_audio_g711a_mode { + MVS_G711A_MODE_MULAW, + MVS_G711A_MODE_ALAW +}; + +enum msm_audio_g711_mode { + MVS_G711_MODE_MULAW, + MVS_G711_MODE_ALAW +}; + +#define VOIP_MODE_MAX MODE_G711A +#define VOIP_RATE_MAX 23850 + +enum format { + FORMAT_S16_LE = 2, + FORMAT_SPECIAL = 31, +}; + + +enum amr_rate_type { + AMR_RATE_4750, /* AMR 4.75 kbps */ + AMR_RATE_5150, /* AMR 5.15 kbps */ + AMR_RATE_5900, /* AMR 5.90 kbps */ + AMR_RATE_6700, /* AMR 6.70 kbps */ + AMR_RATE_7400, /* AMR 7.40 kbps */ + AMR_RATE_7950, /* AMR 7.95 kbps */ + AMR_RATE_10200, /* AMR 10.20 kbps */ + AMR_RATE_12200, /* AMR 12.20 kbps */ + AMR_RATE_6600, /* AMR-WB 6.60 kbps */ + AMR_RATE_8850, /* AMR-WB 8.85 kbps */ + AMR_RATE_12650, /* AMR-WB 12.65 kbps */ + AMR_RATE_14250, /* AMR-WB 14.25 kbps */ + AMR_RATE_15850, /* AMR-WB 15.85 kbps */ + AMR_RATE_18250, /* AMR-WB 18.25 kbps */ + AMR_RATE_19850, /* AMR-WB 19.85 kbps */ + AMR_RATE_23050, /* AMR-WB 23.05 kbps */ + AMR_RATE_23850, /* AMR-WB 23.85 kbps */ + AMR_RATE_UNDEF +}; + +enum voip_state { + VOIP_STOPPED, + VOIP_STARTED, +}; + +struct voip_frame_hdr { + uint32_t timestamp; + union { + /* + * Bits 0-3: Frame type + * [optional] Bits 16-19: Frame rate + */ + uint32_t frame_type; + uint32_t packet_rate; + }; +}; +struct voip_frame { + struct voip_frame_hdr frm_hdr; + uint32_t pktlen; + uint8_t voc_pkt[VOIP_MAX_VOC_PKT_SIZE]; +}; + +struct voip_buf_node { + struct list_head list; + struct voip_frame frame; +}; + +struct voip_drv_info { + enum voip_state state; + + struct snd_pcm_substream *playback_substream; + struct snd_pcm_substream *capture_substream; + + struct list_head in_queue; + struct list_head free_in_queue; + + struct list_head out_queue; + struct list_head free_out_queue; + + wait_queue_head_t out_wait; + wait_queue_head_t in_wait; + + struct mutex lock; + + spinlock_t dsp_lock; + spinlock_t dsp_ul_lock; + + bool voip_reset; + uint32_t mode; + uint32_t rate_type; + uint32_t rate; + uint32_t dtx_mode; + + uint8_t capture_start; + uint8_t playback_start; + + uint8_t playback_prepare; + uint8_t capture_prepare; + + unsigned int play_samp_rate; + unsigned int cap_samp_rate; + + unsigned int pcm_size; + unsigned int pcm_count; + unsigned int pcm_playback_irq_pos; /* IRQ position */ + unsigned int pcm_playback_buf_pos; /* position in buffer */ + + unsigned int pcm_capture_size; + unsigned int pcm_capture_count; + unsigned int pcm_capture_irq_pos; /* IRQ position */ + unsigned int pcm_capture_buf_pos; /* position in buffer */ + + uint32_t evrc_min_rate; + uint32_t evrc_max_rate; +}; + +static int voip_get_media_type(uint32_t mode, uint32_t rate_type, + unsigned int samp_rate, + unsigned int *media_type); +static int voip_get_rate_type(uint32_t mode, + uint32_t rate, + uint32_t *rate_type); +static int voip_config_vocoder(struct snd_pcm_substream *substream); +static int msm_voip_mode_config_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +static int msm_voip_mode_config_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +static int msm_voip_rate_config_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +static int msm_voip_evrc_min_max_rate_config_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +static int msm_voip_evrc_min_max_rate_config_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); + +static struct voip_drv_info voip_info; + +static struct snd_pcm_hardware msm_pcm_hardware = { + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED), + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_SPECIAL, + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000, + .rate_min = 8000, + .rate_max = 48000, + .channels_min = 1, + .channels_max = 1, + .buffer_bytes_max = sizeof(struct voip_buf_node) * VOIP_MAX_Q_LEN, + .period_bytes_min = VOIP_MIN_VOC_PKT_SIZE, + .period_bytes_max = VOIP_MAX_VOC_PKT_SIZE, + .periods_min = VOIP_MAX_Q_LEN, + .periods_max = VOIP_MAX_Q_LEN, + .fifo_size = 0, +}; + + +static int msm_voip_mute_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret = 0; + int mute = ucontrol->value.integer.value[0]; + int ramp_duration = ucontrol->value.integer.value[1]; + + if ((mute < 0) || (mute > 1) || (ramp_duration < 0)) { + pr_err(" %s Invalid arguments", __func__); + + ret = -EINVAL; + goto done; + } + + pr_debug("%s: mute=%d ramp_duration=%d\n", __func__, mute, + ramp_duration); + + voc_set_tx_mute(voc_get_session_id(VOIP_SESSION_NAME), TX_PATH, mute, + ramp_duration); + +done: + return ret; +} + +static int msm_voip_gain_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret = 0; + int volume = ucontrol->value.integer.value[0]; + int ramp_duration = ucontrol->value.integer.value[1]; + + if ((volume < 0) || (ramp_duration < 0)) { + pr_err(" %s Invalid arguments", __func__); + + ret = -EINVAL; + goto done; + } + + pr_debug("%s: volume: %d ramp_duration: %d\n", __func__, volume, + ramp_duration); + + voc_set_rx_vol_step(voc_get_session_id(VOIP_SESSION_NAME), + RX_PATH, + volume, + ramp_duration); + +done: + return ret; +} + +static int msm_voip_dtx_mode_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + mutex_lock(&voip_info.lock); + + voip_info.dtx_mode = ucontrol->value.integer.value[0]; + + pr_debug("%s: dtx: %d\n", __func__, voip_info.dtx_mode); + + mutex_unlock(&voip_info.lock); + + return 0; +} +static int msm_voip_dtx_mode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + mutex_lock(&voip_info.lock); + + ucontrol->value.integer.value[0] = voip_info.dtx_mode; + + mutex_unlock(&voip_info.lock); + + return 0; +} + +static struct snd_kcontrol_new msm_voip_controls[] = { + SOC_SINGLE_MULTI_EXT("Voip Tx Mute", SND_SOC_NOPM, 0, + MAX_RAMP_DURATION, + 0, 2, NULL, msm_voip_mute_put), + SOC_SINGLE_MULTI_EXT("Voip Rx Gain", SND_SOC_NOPM, 0, + MAX_RAMP_DURATION, + 0, 2, NULL, msm_voip_gain_put), + SOC_SINGLE_EXT("Voip Mode Config", SND_SOC_NOPM, 0, VOIP_MODE_MAX, 0, + msm_voip_mode_config_get, msm_voip_mode_config_put), + SOC_SINGLE_EXT("Voip Rate Config", SND_SOC_NOPM, 0, VOIP_RATE_MAX, 0, + NULL, msm_voip_rate_config_put), + SOC_SINGLE_MULTI_EXT("Voip Evrc Min Max Rate Config", SND_SOC_NOPM, + 0, VOC_1_RATE, 0, 2, + msm_voip_evrc_min_max_rate_config_get, + msm_voip_evrc_min_max_rate_config_put), + SOC_SINGLE_EXT("Voip Dtx Mode", SND_SOC_NOPM, 0, 1, 0, + msm_voip_dtx_mode_get, msm_voip_dtx_mode_put), +}; + +static int msm_pcm_voip_probe(struct snd_soc_platform *platform) +{ + snd_soc_add_platform_controls(platform, msm_voip_controls, + ARRAY_SIZE(msm_voip_controls)); + + return 0; +} + +/* sample rate supported */ +static unsigned int supported_sample_rates[] = {8000, 16000, 32000, 48000}; + +static void voip_ssr_cb_fn(uint32_t opcode, void *private_data) +{ + + /* Notify ASoC to send next playback/Capture to unblock write/read */ + struct voip_drv_info *prtd = private_data; + + if (opcode == 0xFFFFFFFF) { + + prtd->voip_reset = true; + pr_debug("%s: Notify ASoC to send next playback/Capture\n", + __func__); + + prtd->pcm_playback_irq_pos += prtd->pcm_count; + if (prtd->state == VOIP_STARTED) + snd_pcm_period_elapsed(prtd->playback_substream); + wake_up(&prtd->out_wait); + + prtd->pcm_capture_irq_pos += prtd->pcm_capture_count; + if (prtd->state == VOIP_STARTED) + snd_pcm_period_elapsed(prtd->capture_substream); + wake_up(&prtd->in_wait); + + } else { + pr_err("%s: Invalid opcode during reset : %d\n", + __func__, opcode); + } +} + +/* capture path */ +static void voip_process_ul_pkt(uint8_t *voc_pkt, + uint32_t pkt_len, + uint32_t timestamp, + void *private_data) +{ + struct voip_buf_node *buf_node = NULL; + struct voip_drv_info *prtd = private_data; + unsigned long dsp_flags; + + if (prtd->capture_substream == NULL) + return; + + /* Copy up-link packet into out_queue. */ + spin_lock_irqsave(&prtd->dsp_ul_lock, dsp_flags); + + /* discarding UL packets till start is received */ + if (!list_empty(&prtd->free_out_queue) && prtd->capture_start) { + buf_node = list_first_entry(&prtd->free_out_queue, + struct voip_buf_node, list); + list_del(&buf_node->list); + switch (prtd->mode) { + case MODE_AMR_WB: + case MODE_AMR: { + /* Remove the DSP frame info header. Header format: + * Bits 0-3: Frame rate + * Bits 4-7: Frame type + */ + buf_node->frame.frm_hdr.timestamp = timestamp; + buf_node->frame.frm_hdr.frame_type = + ((*voc_pkt) & 0xF0) >> 4; + voc_pkt = voc_pkt + DSP_FRAME_HDR_LEN; + buf_node->frame.pktlen = pkt_len - DSP_FRAME_HDR_LEN; + memcpy(&buf_node->frame.voc_pkt[0], + voc_pkt, + buf_node->frame.pktlen); + + list_add_tail(&buf_node->list, &prtd->out_queue); + break; + } + case MODE_IS127: + case MODE_4GV_NB: + case MODE_4GV_WB: + case MODE_4GV_NW: { + /* Remove the DSP frame info header. + * Header format: + * Bits 0-3: frame rate + */ + buf_node->frame.frm_hdr.timestamp = timestamp; + buf_node->frame.frm_hdr.packet_rate = (*voc_pkt) & 0x0F; + voc_pkt = voc_pkt + DSP_FRAME_HDR_LEN; + buf_node->frame.pktlen = pkt_len - DSP_FRAME_HDR_LEN; + + memcpy(&buf_node->frame.voc_pkt[0], + voc_pkt, + buf_node->frame.pktlen); + + list_add_tail(&buf_node->list, &prtd->out_queue); + break; + } + case MODE_G711: + case MODE_G711A:{ + /* G711 frames are 10ms each, but the DSP works with + * 20ms frames and sends two 10ms frames per buffer. + * Extract the two frames and put them in separate + * buffers. + */ + /* Remove the first DSP frame info header. + * Header format: G711A + * Bits 0-1: Frame type + * Bits 2-3: Frame rate + * + * Header format: G711 + * Bits 2-3: Frame rate + */ + if (prtd->mode == MODE_G711A) + buf_node->frame.frm_hdr.frame_type = + (*voc_pkt) & 0x03; + buf_node->frame.frm_hdr.timestamp = timestamp; + voc_pkt = voc_pkt + DSP_FRAME_HDR_LEN; + + /* There are two frames in the buffer. Length of the + * first frame: + */ + buf_node->frame.pktlen = (pkt_len - + 2 * DSP_FRAME_HDR_LEN) / 2; + + memcpy(&buf_node->frame.voc_pkt[0], + voc_pkt, + buf_node->frame.pktlen); + voc_pkt = voc_pkt + buf_node->frame.pktlen; + + list_add_tail(&buf_node->list, &prtd->out_queue); + + /* Get another buffer from the free Q and fill in the + * second frame. + */ + if (!list_empty(&prtd->free_out_queue)) { + buf_node = + list_first_entry(&prtd->free_out_queue, + struct voip_buf_node, + list); + list_del(&buf_node->list); + + /* Remove the second DSP frame info header. + * Header format: + * Bits 0-1: Frame type + * Bits 2-3: Frame rate + */ + + if (prtd->mode == MODE_G711A) + buf_node->frame.frm_hdr.frame_type = + (*voc_pkt) & 0x03; + buf_node->frame.frm_hdr.timestamp = timestamp; + voc_pkt = voc_pkt + DSP_FRAME_HDR_LEN; + + /* There are two frames in the buffer. Length + * of the second frame: + */ + buf_node->frame.pktlen = (pkt_len - + 2 * DSP_FRAME_HDR_LEN) / 2; + + memcpy(&buf_node->frame.voc_pkt[0], + voc_pkt, + buf_node->frame.pktlen); + + list_add_tail(&buf_node->list, + &prtd->out_queue); + } else { + /* Drop the second frame */ + pr_err("%s: UL data dropped, read is slow\n", + __func__); + } + break; + } + default: { + buf_node->frame.frm_hdr.timestamp = timestamp; + buf_node->frame.pktlen = pkt_len; + memcpy(&buf_node->frame.voc_pkt[0], + voc_pkt, + buf_node->frame.pktlen); + list_add_tail(&buf_node->list, &prtd->out_queue); + } + } + pr_debug("%s: pkt_len =%d, frame.pktlen=%d, timestamp=%d\n", + __func__, pkt_len, buf_node->frame.pktlen, timestamp); + + if (prtd->mode == MODE_PCM) + prtd->pcm_capture_irq_pos += buf_node->frame.pktlen; + else + prtd->pcm_capture_irq_pos += prtd->pcm_capture_count; + + spin_unlock_irqrestore(&prtd->dsp_ul_lock, dsp_flags); + snd_pcm_period_elapsed(prtd->capture_substream); + } else { + spin_unlock_irqrestore(&prtd->dsp_ul_lock, dsp_flags); + pr_err("UL data dropped\n"); + } + + wake_up(&prtd->out_wait); +} + +/* playback path */ +static void voip_process_dl_pkt(uint8_t *voc_pkt, void *private_data) +{ + struct voip_buf_node *buf_node = NULL; + struct voip_drv_info *prtd = private_data; + unsigned long dsp_flags; + uint32_t rate_type; + uint32_t frame_rate; + u32 pkt_len; + u8 *voc_addr = NULL; + + if (prtd->playback_substream == NULL) + return; + + spin_lock_irqsave(&prtd->dsp_lock, dsp_flags); + + if (!list_empty(&prtd->in_queue) && prtd->playback_start) { + buf_node = list_first_entry(&prtd->in_queue, + struct voip_buf_node, list); + list_del(&buf_node->list); + switch (prtd->mode) { + case MODE_AMR: + case MODE_AMR_WB: { + *((uint32_t *)voc_pkt) = buf_node->frame.pktlen + + DSP_FRAME_HDR_LEN; + /* Advance to the header of voip packet */ + voc_pkt = voc_pkt + sizeof(uint32_t); + /* + * Add the DSP frame info header. Header format: + * Bits 0-3: Frame rate + * Bits 4-7: Frame type + */ + *voc_pkt = ((buf_node->frame.frm_hdr.frame_type & + 0x0F) << 4); + frame_rate = (buf_node->frame.frm_hdr.frame_type & + 0xFFFF0000) >> 16; + if (frame_rate) { + if (voip_get_rate_type(prtd->mode, frame_rate, + &rate_type)) { + pr_err("%s(): fail at getting rate_type\n", + __func__); + } else + prtd->rate_type = rate_type; + } + *voc_pkt |= prtd->rate_type & 0x0F; + + voc_pkt = voc_pkt + DSP_FRAME_HDR_LEN; + memcpy(voc_pkt, + &buf_node->frame.voc_pkt[0], + buf_node->frame.pktlen); + list_add_tail(&buf_node->list, &prtd->free_in_queue); + break; + } + case MODE_IS127: + case MODE_4GV_NB: + case MODE_4GV_WB: + case MODE_4GV_NW: { + *((uint32_t *)voc_pkt) = buf_node->frame.pktlen + + DSP_FRAME_HDR_LEN; + /* Advance to the header of voip packet */ + voc_pkt = voc_pkt + sizeof(uint32_t); + /* + * Add the DSP frame info header. Header format: + * Bits 0-3 : Frame rate + */ + *voc_pkt = buf_node->frame.frm_hdr.packet_rate & 0x0F; + voc_pkt = voc_pkt + DSP_FRAME_HDR_LEN; + + memcpy(voc_pkt, + &buf_node->frame.voc_pkt[0], + buf_node->frame.pktlen); + + list_add_tail(&buf_node->list, &prtd->free_in_queue); + break; + } + case MODE_G711: + case MODE_G711A:{ + /* G711 frames are 10ms each but the DSP expects 20ms + * worth of data, so send two 10ms frames per buffer. + */ + /* Add the first DSP frame info header. Header format: + * Bits 0-1: Frame type + * Bits 2-3: Frame rate + */ + voc_addr = voc_pkt; + voc_pkt = voc_pkt + sizeof(uint32_t); + + *voc_pkt = ((prtd->rate_type & 0x0F) << 2) | + (buf_node->frame.frm_hdr.frame_type & 0x03); + voc_pkt = voc_pkt + DSP_FRAME_HDR_LEN; + + pkt_len = buf_node->frame.pktlen + DSP_FRAME_HDR_LEN; + + memcpy(voc_pkt, + &buf_node->frame.voc_pkt[0], + buf_node->frame.pktlen); + voc_pkt = voc_pkt + buf_node->frame.pktlen; + + list_add_tail(&buf_node->list, &prtd->free_in_queue); + + if (!list_empty(&prtd->in_queue)) { + /* Get the second buffer. */ + buf_node = list_first_entry(&prtd->in_queue, + struct voip_buf_node, + list); + list_del(&buf_node->list); + + /* Add the second DSP frame info header. + * Header format: + * Bits 0-1: Frame type + * Bits 2-3: Frame rate + */ + *voc_pkt = ((prtd->rate_type & 0x0F) << 2) | + (buf_node->frame.frm_hdr.frame_type & 0x03); + voc_pkt = voc_pkt + DSP_FRAME_HDR_LEN; + + pkt_len = pkt_len + buf_node->frame.pktlen + + DSP_FRAME_HDR_LEN; + + memcpy(voc_pkt, + &buf_node->frame.voc_pkt[0], + buf_node->frame.pktlen); + + list_add_tail(&buf_node->list, + &prtd->free_in_queue); + } else { + /* Only 10ms worth of data is available, signal + * erasure frame. + */ + *voc_pkt = ((prtd->rate_type & 0x0F) << 2) | + (MVS_G711A_ERASURE & 0x03); + + pkt_len = pkt_len + DSP_FRAME_HDR_LEN; + pr_debug("%s, Only 10ms read, erase 2nd frame\n", + __func__); + } + *((uint32_t *)voc_addr) = pkt_len; + break; + } + default: { + *((uint32_t *)voc_pkt) = buf_node->frame.pktlen; + voc_pkt = voc_pkt + sizeof(uint32_t); + memcpy(voc_pkt, + &buf_node->frame.voc_pkt[0], + buf_node->frame.pktlen); + list_add_tail(&buf_node->list, &prtd->free_in_queue); + } + } + pr_debug("%s: frame.pktlen=%d\n", __func__, + buf_node->frame.pktlen); + + if (prtd->mode == MODE_PCM) + prtd->pcm_playback_irq_pos += buf_node->frame.pktlen; + else + prtd->pcm_playback_irq_pos += prtd->pcm_count; + + spin_unlock_irqrestore(&prtd->dsp_lock, dsp_flags); + snd_pcm_period_elapsed(prtd->playback_substream); + } else { + *((uint32_t *)voc_pkt) = 0; + spin_unlock_irqrestore(&prtd->dsp_lock, dsp_flags); + pr_err_ratelimited("DL data not available\n"); + } + wake_up(&prtd->in_wait); +} + +static struct snd_pcm_hw_constraint_list constraints_sample_rates = { + .count = ARRAY_SIZE(supported_sample_rates), + .list = supported_sample_rates, + .mask = 0, +}; + +static int msm_pcm_playback_prepare(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct voip_drv_info *prtd = runtime->private_data; + + prtd->play_samp_rate = runtime->rate; + prtd->pcm_size = snd_pcm_lib_buffer_bytes(substream); + prtd->pcm_count = snd_pcm_lib_period_bytes(substream); + prtd->pcm_playback_irq_pos = 0; + prtd->pcm_playback_buf_pos = 0; + prtd->playback_prepare = 1; + + return 0; +} + +static int msm_pcm_capture_prepare(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct voip_drv_info *prtd = runtime->private_data; + int ret = 0; + + prtd->cap_samp_rate = runtime->rate; + prtd->pcm_capture_size = snd_pcm_lib_buffer_bytes(substream); + prtd->pcm_capture_count = snd_pcm_lib_period_bytes(substream); + prtd->pcm_capture_irq_pos = 0; + prtd->pcm_capture_buf_pos = 0; + prtd->capture_prepare = 1; + return ret; +} + +static int msm_pcm_trigger(struct snd_pcm_substream *substream, int cmd) +{ + int ret = 0; + struct snd_pcm_runtime *runtime = substream->runtime; + struct voip_drv_info *prtd = runtime->private_data; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + pr_debug("%s: Trigger start\n", __func__); + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + prtd->capture_start = 1; + else + prtd->playback_start = 1; + break; + case SNDRV_PCM_TRIGGER_STOP: + pr_debug("SNDRV_PCM_TRIGGER_STOP\n"); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + prtd->playback_start = 0; + else + prtd->capture_start = 0; + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static int msm_pcm_open(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct voip_drv_info *prtd = &voip_info; + int ret = 0; + + pr_debug("%s, VoIP\n", __func__); + mutex_lock(&prtd->lock); + + runtime->hw = msm_pcm_hardware; + + ret = snd_pcm_hw_constraint_list(runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + &constraints_sample_rates); + if (ret < 0) + pr_debug("snd_pcm_hw_constraint_list failed\n"); + + ret = snd_pcm_hw_constraint_integer(runtime, + SNDRV_PCM_HW_PARAM_PERIODS); + if (ret < 0) { + pr_debug("snd_pcm_hw_constraint_integer failed\n"); + goto err; + } + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + prtd->playback_substream = substream; + else + prtd->capture_substream = substream; + + runtime->private_data = prtd; +err: + mutex_unlock(&prtd->lock); + + return ret; +} + +static int msm_pcm_playback_copy(struct snd_pcm_substream *substream, int a, + unsigned long hwoff, void __user *buf, unsigned long fbytes) +{ + int ret = 0; + struct voip_buf_node *buf_node = NULL; + struct snd_pcm_runtime *runtime = substream->runtime; + struct voip_drv_info *prtd = runtime->private_data; + unsigned long dsp_flags; + + pr_debug("%s: fbytes=%lu\n", __func__, fbytes); + + if (prtd->voip_reset) { + pr_debug("%s: RESET event happened during VoIP\n", __func__); + return -ENETRESET; + } + + ret = wait_event_interruptible_timeout(prtd->in_wait, + (!list_empty(&prtd->free_in_queue) || + prtd->state == VOIP_STOPPED), + 1 * HZ); + if (prtd->voip_reset) { + pr_debug("%s: RESET event happened during VoIP\n", __func__); + return -ENETRESET; + } + + if (ret > 0) { + if (fbytes <= VOIP_MAX_VOC_PKT_SIZE) { + spin_lock_irqsave(&prtd->dsp_lock, dsp_flags); + buf_node = + list_first_entry(&prtd->free_in_queue, + struct voip_buf_node, list); + list_del(&buf_node->list); + spin_unlock_irqrestore(&prtd->dsp_lock, dsp_flags); + if (prtd->mode == MODE_PCM) { + ret = copy_from_user(&buf_node->frame.voc_pkt, + buf, fbytes); + if (ret) { + pr_err("%s: copy from user failed %d\n", + __func__, ret); + return -EFAULT; + } + buf_node->frame.pktlen = fbytes; + } else { + ret = copy_from_user(&buf_node->frame, + buf, fbytes); + if (ret) { + pr_err("%s: copy from user failed %d\n", + __func__, ret); + return -EFAULT; + } + if (buf_node->frame.pktlen >= fbytes) + buf_node->frame.pktlen = fbytes - + (sizeof(buf_node->frame.frm_hdr) + + sizeof(buf_node->frame.pktlen)); + } + spin_lock_irqsave(&prtd->dsp_lock, dsp_flags); + list_add_tail(&buf_node->list, &prtd->in_queue); + spin_unlock_irqrestore(&prtd->dsp_lock, dsp_flags); + } else { + pr_err("%s: Write cnt %lu is > VOIP_MAX_VOC_PKT_SIZE\n", + __func__, fbytes); + ret = -ENOMEM; + } + + } else if (ret == 0) { + pr_err("%s: No free DL buffs\n", __func__); + ret = -ETIMEDOUT; + } else { + pr_err("%s: playback copy was interrupted %d\n", __func__, ret); + } + + return ret; +} +static int msm_pcm_capture_copy(struct snd_pcm_substream *substream, + int channel, unsigned long hwoff, void __user *buf, + unsigned long fbytes) +{ + int ret = 0; + struct voip_buf_node *buf_node = NULL; + struct snd_pcm_runtime *runtime = substream->runtime; + struct voip_drv_info *prtd = runtime->private_data; + unsigned long dsp_flags; + int size; + + pr_debug("%s: fbytes = %lu\n", __func__, fbytes); + + if (prtd->voip_reset) { + pr_debug("%s: RESET event happened during VoIP\n", __func__); + return -ENETRESET; + } + + ret = wait_event_interruptible_timeout(prtd->out_wait, + (!list_empty(&prtd->out_queue) || + prtd->state == VOIP_STOPPED), + 1 * HZ); + + if (prtd->voip_reset) { + pr_debug("%s: RESET event happened during VoIP\n", __func__); + return -ENETRESET; + } + + if (ret > 0) { + + if (fbytes <= VOIP_MAX_VOC_PKT_SIZE) { + spin_lock_irqsave(&prtd->dsp_ul_lock, dsp_flags); + buf_node = list_first_entry(&prtd->out_queue, + struct voip_buf_node, list); + list_del(&buf_node->list); + spin_unlock_irqrestore(&prtd->dsp_ul_lock, dsp_flags); + if (prtd->mode == MODE_PCM) { + ret = copy_to_user(buf, + &buf_node->frame.voc_pkt, + buf_node->frame.pktlen); + } else { + size = sizeof(buf_node->frame.frm_hdr) + + sizeof(buf_node->frame.pktlen) + + buf_node->frame.pktlen; + + ret = copy_to_user(buf, + &buf_node->frame, + size); + } + if (ret) { + pr_err("%s: Copy to user returned %d\n", + __func__, ret); + ret = -EFAULT; + } + spin_lock_irqsave(&prtd->dsp_ul_lock, dsp_flags); + list_add_tail(&buf_node->list, + &prtd->free_out_queue); + spin_unlock_irqrestore(&prtd->dsp_ul_lock, dsp_flags); + } else { + pr_err("%s: Read fbytes %lu > VOIP_MAX_VOC_PKT_SIZE\n", + __func__, fbytes); + ret = -ENOMEM; + } + + + } else if (ret == 0) { + pr_err_ratelimited("%s: No UL data available\n", __func__); + ret = -ETIMEDOUT; + } else { + pr_err("%s: Read was interrupted\n", __func__); + ret = -ERESTARTSYS; + } + return ret; +} +static int msm_pcm_copy(struct snd_pcm_substream *substream, int a, + unsigned long hwoff, void __user *buf, unsigned long fbytes) +{ + int ret = 0; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + ret = msm_pcm_playback_copy(substream, a, hwoff, buf, fbytes); + else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + ret = msm_pcm_capture_copy(substream, a, hwoff, buf, fbytes); + + return ret; +} + +static int msm_pcm_close(struct snd_pcm_substream *substream) +{ + int ret = 0; + struct list_head *ptr = NULL; + struct list_head *next = NULL; + struct voip_buf_node *buf_node = NULL; + struct snd_dma_buffer *p_dma_buf, *c_dma_buf; + struct snd_pcm_substream *p_substream, *c_substream; + struct snd_pcm_runtime *runtime; + struct voip_drv_info *prtd; + unsigned long dsp_flags; + + if (substream == NULL) { + pr_err("substream is NULL\n"); + return -EINVAL; + } + runtime = substream->runtime; + prtd = runtime->private_data; + + wake_up(&prtd->out_wait); + + mutex_lock(&prtd->lock); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + prtd->playback_prepare = 0; + else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + prtd->capture_prepare = 0; + + if (!prtd->playback_prepare && !prtd->capture_prepare) { + if (prtd->state == VOIP_STARTED) { + prtd->voip_reset = false; + prtd->state = VOIP_STOPPED; + voc_end_voice_call( + voc_get_session_id(VOIP_SESSION_NAME)); + voc_register_mvs_cb(NULL, NULL, NULL, prtd); + } + /* release all buffer */ + /* release in_queue and free_in_queue */ + pr_debug("release all buffer\n"); + p_substream = prtd->playback_substream; + if (p_substream == NULL) { + pr_debug("p_substream is NULL\n"); + goto capt; + } + p_dma_buf = &p_substream->dma_buffer; + if (p_dma_buf == NULL) { + pr_debug("p_dma_buf is NULL\n"); + goto capt; + } + if (p_dma_buf->area != NULL) { + spin_lock_irqsave(&prtd->dsp_lock, dsp_flags); + list_for_each_safe(ptr, next, &prtd->in_queue) { + buf_node = list_entry(ptr, + struct voip_buf_node, list); + list_del(&buf_node->list); + } + list_for_each_safe(ptr, next, &prtd->free_in_queue) { + buf_node = list_entry(ptr, + struct voip_buf_node, list); + list_del(&buf_node->list); + } + spin_unlock_irqrestore(&prtd->dsp_lock, dsp_flags); + dma_free_coherent(p_substream->pcm->card->dev, + runtime->hw.buffer_bytes_max, p_dma_buf->area, + p_dma_buf->addr); + p_dma_buf->area = NULL; + } + /* release out_queue and free_out_queue */ +capt: c_substream = prtd->capture_substream; + if (c_substream == NULL) { + pr_debug("c_substream is NULL\n"); + goto done; + } + c_dma_buf = &c_substream->dma_buffer; + if (c_substream == NULL) { + pr_debug("c_dma_buf is NULL.\n"); + goto done; + } + if (c_dma_buf->area != NULL) { + spin_lock_irqsave(&prtd->dsp_ul_lock, dsp_flags); + list_for_each_safe(ptr, next, &prtd->out_queue) { + buf_node = list_entry(ptr, + struct voip_buf_node, list); + list_del(&buf_node->list); + } + list_for_each_safe(ptr, next, &prtd->free_out_queue) { + buf_node = list_entry(ptr, + struct voip_buf_node, list); + list_del(&buf_node->list); + } + spin_unlock_irqrestore(&prtd->dsp_ul_lock, dsp_flags); + dma_free_coherent(c_substream->pcm->card->dev, + runtime->hw.buffer_bytes_max, c_dma_buf->area, + c_dma_buf->addr); + c_dma_buf->area = NULL; + } +done: + prtd->capture_substream = NULL; + prtd->playback_substream = NULL; + } + mutex_unlock(&prtd->lock); + + return ret; +} + +static int voip_config_vocoder(struct snd_pcm_substream *substream) +{ + int ret = 0; + struct snd_pcm_runtime *runtime = substream->runtime; + struct voip_drv_info *prtd = runtime->private_data; + uint32_t media_type = 0; + uint32_t rate_type = 0; + uint32_t evrc_min_rate_type = 0; + uint32_t evrc_max_rate_type = 0; + + pr_debug("%s(): mode=%d, playback rate=%d, capture rate=%d\n", + __func__, prtd->mode, prtd->play_samp_rate, + prtd->cap_samp_rate); + + if ((runtime->format != FORMAT_S16_LE && + runtime->format != FORMAT_SPECIAL) && + ((prtd->mode == MODE_AMR) || (prtd->mode == MODE_AMR_WB) || + (prtd->mode == MODE_IS127) || (prtd->mode == MODE_4GV_NB) || + (prtd->mode == MODE_4GV_WB) || (prtd->mode == MODE_4GV_NW) || + (prtd->mode == MODE_G711) || (prtd->mode == MODE_G711A))) { + pr_err("%s(): mode:%d and format:%u are not matched\n", + __func__, prtd->mode, (uint32_t)runtime->format); + + ret = -EINVAL; + goto done; + } + + if (runtime->format != FORMAT_S16_LE && (prtd->mode == MODE_PCM)) { + pr_err("%s(): mode:%d and format:%u are not matched\n", + __func__, prtd->mode, runtime->format); + + ret = -EINVAL; + goto done; + } + + if ((prtd->mode == MODE_PCM) || + (prtd->mode == MODE_AMR) || + (prtd->mode == MODE_AMR_WB) || + (prtd->mode == MODE_G711) || + (prtd->mode == MODE_G711A)) { + ret = voip_get_rate_type(prtd->mode, + prtd->rate, + &rate_type); + if (ret < 0) { + pr_err("%s(): fail at getting rate_type, ret=%d\n", + __func__, ret); + + ret = -EINVAL; + goto done; + } + prtd->rate_type = rate_type; + pr_debug("rate_type=%d\n", rate_type); + + } else if ((prtd->mode == MODE_IS127) || + (prtd->mode == MODE_4GV_NB) || + (prtd->mode == MODE_4GV_WB) || + (prtd->mode == MODE_4GV_NW)) { + ret = voip_get_rate_type(prtd->mode, + prtd->evrc_min_rate, + &evrc_min_rate_type); + if (ret < 0) { + pr_err("%s(): fail at getting min rate, ret=%d\n", + __func__, ret); + + ret = -EINVAL; + goto done; + } + if (evrc_min_rate_type == VOC_0_RATE) + evrc_min_rate_type = VOC_8_RATE; + + ret = voip_get_rate_type(prtd->mode, + prtd->evrc_max_rate, + &evrc_max_rate_type); + if (ret < 0) { + pr_err("%s(): fail at getting max rate, ret=%d\n", + __func__, ret); + + ret = -EINVAL; + goto done; + } + if (evrc_max_rate_type == VOC_0_RATE) + evrc_max_rate_type = VOC_1_RATE; + + if (evrc_max_rate_type < evrc_min_rate_type) { + pr_err("%s(): Invalid EVRC min max rates: %d, %d\n", + __func__, evrc_min_rate_type, + evrc_max_rate_type); + + ret = -EINVAL; + goto done; + } + pr_debug("%s(): min rate=%d, max rate=%d\n", + __func__, evrc_min_rate_type, evrc_max_rate_type); + } + ret = voip_get_media_type(prtd->mode, + prtd->rate_type, + prtd->play_samp_rate, + &media_type); + if (ret < 0) { + pr_err("%s(): fail at getting media_type, ret=%d\n", + __func__, ret); + + ret = -EINVAL; + goto done; + } + pr_debug("%s(): media_type=%d\n", __func__, media_type); + + if ((prtd->play_samp_rate == 8000 && prtd->cap_samp_rate == 8000) || + (prtd->play_samp_rate == 16000 && prtd->cap_samp_rate == 16000) || + (prtd->play_samp_rate == 32000 && prtd->cap_samp_rate == 32000) || + (prtd->play_samp_rate == 48000 && prtd->cap_samp_rate == 48000)) { + voc_config_vocoder(media_type, rate_type, + VSS_NETWORK_ID_VOIP, + voip_info.dtx_mode, + evrc_min_rate_type, + evrc_max_rate_type); + } else { + pr_debug("%s: Invalid rate playback %d, capture %d\n", + __func__, prtd->play_samp_rate, + prtd->cap_samp_rate); + + ret = -EINVAL; + } +done: + + return ret; +} + +static int msm_pcm_prepare(struct snd_pcm_substream *substream) +{ + int ret = 0; + struct snd_pcm_runtime *runtime = substream->runtime; + struct voip_drv_info *prtd = runtime->private_data; + + mutex_lock(&prtd->lock); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + ret = msm_pcm_playback_prepare(substream); + else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + ret = msm_pcm_capture_prepare(substream); + + if (prtd->playback_prepare && prtd->capture_prepare + && (prtd->state != VOIP_STARTED)) { + ret = voip_config_vocoder(substream); + if (ret < 0) { + pr_err("%s(): fail at configuring vocoder for voip, ret=%d\n", + __func__, ret); + + goto done; + } + + /* Initialaizing cb variables */ + voc_register_mvs_cb(voip_process_ul_pkt, + voip_process_dl_pkt, + voip_ssr_cb_fn, prtd); + + ret = voc_start_voice_call( + voc_get_session_id(VOIP_SESSION_NAME)); + + if (ret < 0) { + pr_err("%s: voc_start_voice_call() failed err %d", + __func__, ret); + + goto done; + } + prtd->state = VOIP_STARTED; + } +done: + mutex_unlock(&prtd->lock); + + return ret; +} + +static snd_pcm_uframes_t +msm_pcm_playback_pointer(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct voip_drv_info *prtd = runtime->private_data; + + pr_debug("%s\n", __func__); + if (prtd->pcm_playback_irq_pos >= prtd->pcm_size) + prtd->pcm_playback_irq_pos = 0; + return bytes_to_frames(runtime, (prtd->pcm_playback_irq_pos)); +} + +static snd_pcm_uframes_t +msm_pcm_capture_pointer(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct voip_drv_info *prtd = runtime->private_data; + + if (prtd->pcm_capture_irq_pos >= prtd->pcm_capture_size) + prtd->pcm_capture_irq_pos = 0; + return bytes_to_frames(runtime, (prtd->pcm_capture_irq_pos)); +} + +static snd_pcm_uframes_t msm_pcm_pointer(struct snd_pcm_substream *substream) +{ + snd_pcm_uframes_t ret = 0; + + pr_debug("%s\n", __func__); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + ret = msm_pcm_playback_pointer(substream); + else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + ret = msm_pcm_capture_pointer(substream); + return ret; +} + +static int msm_pcm_mmap(struct snd_pcm_substream *substream, + struct vm_area_struct *vma) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + + pr_debug("%s\n", __func__); + dma_mmap_coherent(substream->pcm->card->dev, vma, + runtime->dma_area, + runtime->dma_addr, + runtime->dma_bytes); + return 0; +} + +static int msm_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_dma_buffer *dma_buf = &substream->dma_buffer; + struct voip_buf_node *buf_node = NULL; + int i = 0, offset = 0; + + pr_debug("%s: voip\n", __func__); + + mutex_lock(&voip_info.lock); + + dma_buf->dev.type = SNDRV_DMA_TYPE_DEV; + dma_buf->dev.dev = substream->pcm->card->dev; + dma_buf->private_data = NULL; + + dma_buf->area = dma_alloc_coherent(substream->pcm->card->dev, + runtime->hw.buffer_bytes_max, + &dma_buf->addr, GFP_KERNEL); + if (!dma_buf->area) { + pr_err("%s:MSM VOIP dma_alloc failed\n", __func__); + mutex_unlock(&voip_info.lock); + return -ENOMEM; + } + + dma_buf->bytes = runtime->hw.buffer_bytes_max; + memset(dma_buf->area, 0, runtime->hw.buffer_bytes_max); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + for (i = 0; i < VOIP_MAX_Q_LEN; i++) { + buf_node = (void *)dma_buf->area + offset; + + list_add_tail(&buf_node->list, + &voip_info.free_in_queue); + offset = offset + sizeof(struct voip_buf_node); + } + } else { + for (i = 0; i < VOIP_MAX_Q_LEN; i++) { + buf_node = (void *) dma_buf->area + offset; + list_add_tail(&buf_node->list, + &voip_info.free_out_queue); + offset = offset + sizeof(struct voip_buf_node); + } + } + + mutex_unlock(&voip_info.lock); + + snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); + + return 0; +} + +static int msm_voip_mode_config_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + mutex_lock(&voip_info.lock); + + ucontrol->value.integer.value[0] = voip_info.mode; + + mutex_unlock(&voip_info.lock); + + return 0; +} + +static int msm_voip_mode_config_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + mutex_lock(&voip_info.lock); + + voip_info.mode = ucontrol->value.integer.value[0]; + + pr_debug("%s: mode=%d\n", __func__, voip_info.mode); + + mutex_unlock(&voip_info.lock); + + return 0; +} + +static int msm_voip_rate_config_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret = 0; + int rate = ucontrol->value.integer.value[0]; + + mutex_lock(&voip_info.lock); + + if (voip_info.rate != rate) { + voip_info.rate = rate; + pr_debug("%s: rate=%d\n", __func__, voip_info.rate); + + if (voip_info.state == VOIP_STARTED && + (voip_info.mode == MODE_AMR || + voip_info.mode == MODE_AMR_WB)) { + ret = voip_config_vocoder( + voip_info.capture_substream); + if (ret) { + pr_err("%s:Failed to configure vocoder, ret=%d\n", + __func__, ret); + + goto done; + } + + ret = voc_update_amr_vocoder_rate( + voc_get_session_id(VOIP_SESSION_NAME)); + if (ret) { + pr_err("%s:Failed to update AMR rate, ret=%d\n", + __func__, ret); + } + } + } + +done: + mutex_unlock(&voip_info.lock); + + return ret; +} + +static int msm_voip_evrc_min_max_rate_config_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + mutex_lock(&voip_info.lock); + + ucontrol->value.integer.value[0] = voip_info.evrc_min_rate; + ucontrol->value.integer.value[1] = voip_info.evrc_max_rate; + + mutex_unlock(&voip_info.lock); + + return 0; +} + +static int msm_voip_evrc_min_max_rate_config_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + mutex_lock(&voip_info.lock); + + voip_info.evrc_min_rate = ucontrol->value.integer.value[0]; + voip_info.evrc_max_rate = ucontrol->value.integer.value[1]; + + pr_debug("%s(): evrc_min_rate=%d,evrc_max_rate=%d\n", __func__, + voip_info.evrc_min_rate, voip_info.evrc_max_rate); + + mutex_unlock(&voip_info.lock); + + return 0; +} + +static int voip_get_rate_type(uint32_t mode, uint32_t rate, + uint32_t *rate_type) +{ + int ret = 0; + + switch (mode) { + case MODE_AMR: { + switch (rate) { + case 4750: + *rate_type = AMR_RATE_4750; + break; + case 5150: + *rate_type = AMR_RATE_5150; + break; + case 5900: + *rate_type = AMR_RATE_5900; + break; + case 6700: + *rate_type = AMR_RATE_6700; + break; + case 7400: + *rate_type = AMR_RATE_7400; + break; + case 7950: + *rate_type = AMR_RATE_7950; + break; + case 10200: + *rate_type = AMR_RATE_10200; + break; + case 12200: + *rate_type = AMR_RATE_12200; + break; + default: + pr_err("wrong rate for AMR NB.\n"); + ret = -EINVAL; + break; + } + break; + } + case MODE_AMR_WB: { + switch (rate) { + case 6600: + *rate_type = AMR_RATE_6600 - AMR_RATE_6600; + break; + case 8850: + *rate_type = AMR_RATE_8850 - AMR_RATE_6600; + break; + case 12650: + *rate_type = AMR_RATE_12650 - AMR_RATE_6600; + break; + case 14250: + *rate_type = AMR_RATE_14250 - AMR_RATE_6600; + break; + case 15850: + *rate_type = AMR_RATE_15850 - AMR_RATE_6600; + break; + case 18250: + *rate_type = AMR_RATE_18250 - AMR_RATE_6600; + break; + case 19850: + *rate_type = AMR_RATE_19850 - AMR_RATE_6600; + break; + case 23050: + *rate_type = AMR_RATE_23050 - AMR_RATE_6600; + break; + case 23850: + *rate_type = AMR_RATE_23850 - AMR_RATE_6600; + break; + default: + pr_err("wrong rate for AMR_WB.\n"); + ret = -EINVAL; + break; + } + break; + } + case MODE_PCM: { + *rate_type = 0; + break; + } + case MODE_IS127: + case MODE_4GV_NB: + case MODE_4GV_WB: { + switch (rate) { + case VOC_0_RATE: + case VOC_8_RATE: + case VOC_4_RATE: + case VOC_2_RATE: + case VOC_1_RATE: + *rate_type = rate; + break; + default: + pr_err("wrong rate for IS127/4GV_NB/WB.\n"); + ret = -EINVAL; + break; + } + break; + } + case MODE_4GV_NW: { + switch (rate) { + case VOC_0_RATE: + case VOC_8_RATE: + case VOC_4_RATE: + case VOC_2_RATE: + case VOC_1_RATE: + case VOC_8_RATE_NC: + *rate_type = rate; + break; + default: + pr_err("wrong rate for 4GV_NW.\n"); + ret = -EINVAL; + break; + } + break; + } + case MODE_G711: + case MODE_G711A: + *rate_type = rate; + break; + default: + pr_err("wrong mode type.\n"); + ret = -EINVAL; + } + pr_debug("%s, mode=%d, rate=%u, rate_type=%d\n", + __func__, mode, rate, *rate_type); + return ret; +} + +static int voip_get_media_type(uint32_t mode, uint32_t rate_type, + unsigned int samp_rate, + unsigned int *media_type) +{ + int ret = 0; + + pr_debug("%s: mode=%d, samp_rate=%d\n", __func__, + mode, samp_rate); + switch (mode) { + case MODE_AMR: + *media_type = VSS_MEDIA_ID_AMR_NB_MODEM; + break; + case MODE_AMR_WB: + *media_type = VSS_MEDIA_ID_AMR_WB_MODEM; + break; + case MODE_PCM: + if (samp_rate == 8000) + *media_type = VSS_MEDIA_ID_PCM_8_KHZ; + else if (samp_rate == 16000) + *media_type = VSS_MEDIA_ID_PCM_16_KHZ; + else if (samp_rate == 32000) + *media_type = VSS_MEDIA_ID_PCM_32_KHZ; + else + *media_type = VSS_MEDIA_ID_PCM_48_KHZ; + break; + case MODE_IS127: /* EVRC-A */ + *media_type = VSS_MEDIA_ID_EVRC_MODEM; + break; + case MODE_4GV_NB: /* EVRC-B */ + *media_type = VSS_MEDIA_ID_4GV_NB_MODEM; + break; + case MODE_4GV_WB: /* EVRC-WB */ + *media_type = VSS_MEDIA_ID_4GV_WB_MODEM; + break; + case MODE_4GV_NW: /* EVRC-NW */ + *media_type = VSS_MEDIA_ID_4GV_NW_MODEM; + break; + case MODE_G711: + case MODE_G711A: + if (rate_type == MVS_G711A_MODE_MULAW) + *media_type = VSS_MEDIA_ID_G711_MULAW; + else + *media_type = VSS_MEDIA_ID_G711_ALAW; + break; + default: + pr_debug(" input mode is not supported\n"); + ret = -EINVAL; + } + + pr_debug("%s: media_type is 0x%x\n", __func__, *media_type); + + return ret; +} + + +static const struct snd_pcm_ops msm_pcm_ops = { + .open = msm_pcm_open, + .copy_user = msm_pcm_copy, + .hw_params = msm_pcm_hw_params, + .close = msm_pcm_close, + .prepare = msm_pcm_prepare, + .trigger = msm_pcm_trigger, + .pointer = msm_pcm_pointer, + .mmap = msm_pcm_mmap, +}; + +static int msm_asoc_pcm_new(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_card *card = rtd->card->snd_card; + int ret = 0; + + pr_debug("msm_asoc_pcm_new\n"); + if (!card->dev->coherent_dma_mask) + card->dev->coherent_dma_mask = DMA_BIT_MASK(32); + return ret; +} + +static struct snd_soc_platform_driver msm_soc_platform = { + .ops = &msm_pcm_ops, + .pcm_new = msm_asoc_pcm_new, + .probe = msm_pcm_voip_probe, +}; + +static int msm_pcm_probe(struct platform_device *pdev) +{ + int rc; + + if (!is_voc_initialized()) { + pr_debug("%s: voice module not initialized yet, deferring probe()\n", + __func__); + + rc = -EPROBE_DEFER; + goto done; + } + + rc = voc_alloc_cal_shared_memory(); + if (rc == -EPROBE_DEFER) { + pr_debug("%s: memory allocation for calibration deferred %d\n", + __func__, rc); + + goto done; + } else if (rc < 0) { + pr_err("%s: memory allocation for calibration failed %d\n", + __func__, rc); + } + + rc = voc_alloc_voip_shared_memory(); + if (rc < 0) { + pr_err("%s: error allocating shared mem err %d\n", + __func__, rc); + } + + + pr_debug("%s: dev name %s\n", __func__, dev_name(&pdev->dev)); + rc = snd_soc_register_platform(&pdev->dev, + &msm_soc_platform); + +done: + return rc; +} + +static int msm_pcm_remove(struct platform_device *pdev) +{ + snd_soc_unregister_platform(&pdev->dev); + return 0; +} + +static const struct of_device_id msm_voip_dt_match[] = { + {.compatible = "qcom,msm-voip-dsp"}, + {} +}; +MODULE_DEVICE_TABLE(of, msm_voip_dt_match); + +static struct platform_driver msm_pcm_driver = { + .driver = { + .name = "msm-voip-dsp", + .owner = THIS_MODULE, + .of_match_table = msm_voip_dt_match, + }, + .probe = msm_pcm_probe, + .remove = msm_pcm_remove, +}; + +int __init msm_pcm_voip_init(void) +{ + memset(&voip_info, 0, sizeof(voip_info)); + voip_info.mode = MODE_PCM; + mutex_init(&voip_info.lock); + + spin_lock_init(&voip_info.dsp_lock); + spin_lock_init(&voip_info.dsp_ul_lock); + + init_waitqueue_head(&voip_info.out_wait); + init_waitqueue_head(&voip_info.in_wait); + + INIT_LIST_HEAD(&voip_info.in_queue); + INIT_LIST_HEAD(&voip_info.free_in_queue); + INIT_LIST_HEAD(&voip_info.out_queue); + INIT_LIST_HEAD(&voip_info.free_out_queue); + + return platform_driver_register(&msm_pcm_driver); +} + +void msm_pcm_voip_exit(void) +{ + platform_driver_unregister(&msm_pcm_driver); +} + +MODULE_DESCRIPTION("PCM module platform driver"); +MODULE_LICENSE("GPL v2"); diff --git a/audio-kernel/asoc/msm-qti-pp-config.c b/audio-kernel/asoc/msm-qti-pp-config.c new file mode 100644 index 000000000..882329172 --- /dev/null +++ b/audio-kernel/asoc/msm-qti-pp-config.c @@ -0,0 +1,1638 @@ +/* Copyright (c) 2012-2019, 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "msm-qti-pp-config.h" +#include "msm-pcm-routing-v2.h" + +/* EQUALIZER */ +/* Equal to Frontend after last of the MULTIMEDIA SESSIONS */ +#define MAX_EQ_SESSIONS (MSM_FRONTEND_DAI_MAX + 1) +#define CHMIX_CFG_CONST_PARAM_SIZE 4 + +enum { + EQ_BAND1 = 0, + EQ_BAND2, + EQ_BAND3, + EQ_BAND4, + EQ_BAND5, + EQ_BAND6, + EQ_BAND7, + EQ_BAND8, + EQ_BAND9, + EQ_BAND10, + EQ_BAND11, + EQ_BAND12, + EQ_BAND_MAX, +}; + +/* Audio Sphere data structures */ +struct msm_audio_pp_asphere_state_s { + uint32_t enabled; + uint32_t strength; + uint32_t mode; + uint32_t version; + int port_id[AFE_MAX_PORTS]; + int copp_idx[AFE_MAX_PORTS]; + bool initialized; + uint32_t enabled_prev; + uint32_t strength_prev; +}; + +static struct msm_audio_pp_asphere_state_s asphere_state; + +struct msm_audio_eq_stream_config eq_data[MAX_EQ_SESSIONS]; + +static int msm_route_hfp_vol_control; +static const DECLARE_TLV_DB_LINEAR(hfp_rx_vol_gain, 0, + INT_RX_VOL_MAX_STEPS); + +static int msm_route_icc_vol_control; +static const DECLARE_TLV_DB_LINEAR(icc_rx_vol_gain, 0, + INT_RX_VOL_MAX_STEPS); + +static int msm_route_pri_auxpcm_lb_vol_ctrl; +static const DECLARE_TLV_DB_LINEAR(pri_auxpcm_lb_vol_gain, 0, + INT_RX_VOL_MAX_STEPS); + +static int msm_route_sec_auxpcm_lb_vol_ctrl; +static const DECLARE_TLV_DB_LINEAR(sec_auxpcm_lb_vol_gain, 0, + INT_RX_VOL_MAX_STEPS); + +static int msm_multichannel_ec_primary_mic_ch; + +static void msm_qti_pp_send_eq_values_(int eq_idx) +{ + int result; + struct msm_pcm_routing_fdai_data fe_dai; + struct audio_client *ac = NULL; + + msm_pcm_routing_get_fedai_info(eq_idx, SESSION_TYPE_RX, &fe_dai); + ac = q6asm_get_audio_client(fe_dai.strm_id); + + if (ac == NULL) { + pr_err("%s: Could not get audio client for session: %d\n", + __func__, fe_dai.strm_id); + goto done; + } + + result = q6asm_equalizer(ac, &eq_data[eq_idx]); + + if (result < 0) + pr_err("%s: Call to ASM equalizer failed, returned = %d\n", + __func__, result); +done: + return; +} + +static int msm_qti_pp_get_eq_enable_mixer(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int eq_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->reg; + + if ((eq_idx < 0) || (eq_idx >= MAX_EQ_SESSIONS)) + return -EINVAL; + + ucontrol->value.integer.value[0] = eq_data[eq_idx].enable; + + pr_debug("%s: EQ #%d enable %d\n", __func__, + eq_idx, eq_data[eq_idx].enable); + return 0; +} + +static int msm_qti_pp_put_eq_enable_mixer(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int eq_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->reg; + int value = ucontrol->value.integer.value[0]; + + if ((eq_idx < 0) || (eq_idx >= MAX_EQ_SESSIONS)) + return -EINVAL; + pr_debug("%s: EQ #%d enable %d\n", __func__, + eq_idx, value); + eq_data[eq_idx].enable = value; + msm_pcm_routing_acquire_lock(); + msm_qti_pp_send_eq_values_(eq_idx); + msm_pcm_routing_release_lock(); + return 0; +} + +static int msm_qti_pp_get_eq_band_count_audio_mixer( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int eq_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->reg; + + if ((eq_idx < 0) || (eq_idx >= MAX_EQ_SESSIONS)) + return -EINVAL; + ucontrol->value.integer.value[0] = eq_data[eq_idx].num_bands; + + pr_debug("%s: EQ #%d bands %d\n", __func__, + eq_idx, eq_data[eq_idx].num_bands); + return eq_data[eq_idx].num_bands; +} + +static int msm_qti_pp_put_eq_band_count_audio_mixer( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int eq_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->reg; + int value = ucontrol->value.integer.value[0]; + + if ((eq_idx < 0) || (eq_idx >= MAX_EQ_SESSIONS)) + return -EINVAL; + + pr_debug("%s: EQ #%d bands %d\n", __func__, + eq_idx, value); + eq_data[eq_idx].num_bands = value; + return 0; +} + +static int msm_qti_pp_get_eq_band_audio_mixer(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int eq_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->reg; + int band_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + + if ((eq_idx < 0) || (eq_idx >= MAX_EQ_SESSIONS) || + (band_idx < EQ_BAND1) || (band_idx >= EQ_BAND_MAX)) + return -EINVAL; + + ucontrol->value.integer.value[0] = + eq_data[eq_idx].eq_bands[band_idx].band_idx; + ucontrol->value.integer.value[1] = + eq_data[eq_idx].eq_bands[band_idx].filter_type; + ucontrol->value.integer.value[2] = + eq_data[eq_idx].eq_bands[band_idx].center_freq_hz; + ucontrol->value.integer.value[3] = + eq_data[eq_idx].eq_bands[band_idx].filter_gain; + ucontrol->value.integer.value[4] = + eq_data[eq_idx].eq_bands[band_idx].q_factor; + + pr_debug("%s: band_idx = %d\n", __func__, + eq_data[eq_idx].eq_bands[band_idx].band_idx); + pr_debug("%s: filter_type = %d\n", __func__, + eq_data[eq_idx].eq_bands[band_idx].filter_type); + pr_debug("%s: center_freq_hz = %d\n", __func__, + eq_data[eq_idx].eq_bands[band_idx].center_freq_hz); + pr_debug("%s: filter_gain = %d\n", __func__, + eq_data[eq_idx].eq_bands[band_idx].filter_gain); + pr_debug("%s: q_factor = %d\n", __func__, + eq_data[eq_idx].eq_bands[band_idx].q_factor); + return 0; +} + +static int msm_qti_pp_put_eq_band_audio_mixer(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int eq_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->reg; + int band_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + + if ((eq_idx < 0) || (eq_idx >= MAX_EQ_SESSIONS) || + (band_idx < EQ_BAND1) || (band_idx >= EQ_BAND_MAX)) + return -EINVAL; + + eq_data[eq_idx].eq_bands[band_idx].band_idx = + ucontrol->value.integer.value[0]; + eq_data[eq_idx].eq_bands[band_idx].filter_type = + ucontrol->value.integer.value[1]; + eq_data[eq_idx].eq_bands[band_idx].center_freq_hz = + ucontrol->value.integer.value[2]; + eq_data[eq_idx].eq_bands[band_idx].filter_gain = + ucontrol->value.integer.value[3]; + eq_data[eq_idx].eq_bands[band_idx].q_factor = + ucontrol->value.integer.value[4]; + return 0; +} + +#ifdef CONFIG_QTI_PP +void msm_qti_pp_send_eq_values(int fedai_id) +{ + if (eq_data[fedai_id].enable) + msm_qti_pp_send_eq_values_(fedai_id); +} + +/* CUSTOM MIXING */ +int msm_qti_pp_send_stereo_to_custom_stereo_cmd(int port_id, int copp_idx, + unsigned int session_id, + uint16_t op_FL_ip_FL_weight, + uint16_t op_FL_ip_FR_weight, + uint16_t op_FR_ip_FL_weight, + uint16_t op_FR_ip_FR_weight) +{ + char *params_value; + int *update_params_value32, rc = 0; + int16_t *update_params_value16 = 0; + uint32_t params_length = CUSTOM_STEREO_PAYLOAD_SIZE * sizeof(uint32_t); + uint32_t avail_length = params_length; + + pr_debug("%s: port_id - %d, session id - %d\n", __func__, port_id, + session_id); + params_value = kzalloc(params_length, GFP_KERNEL); + if (!params_value) { + pr_err("%s, params memory alloc failed\n", __func__); + return -ENOMEM; + } + update_params_value32 = (int *)params_value; + if (avail_length < 2 * sizeof(uint32_t)) + goto skip_send_cmd; + + /* + * This module is internal to ADSP and cannot be configured with + * an instance id + */ + *update_params_value32++ = MTMX_MODULE_ID_DEFAULT_CHMIXER; + *update_params_value32++ = DEFAULT_CHMIXER_PARAM_ID_COEFF; + avail_length = avail_length - (2 * sizeof(uint32_t)); + + update_params_value16 = (int16_t *)update_params_value32; + if (avail_length < 10 * sizeof(uint16_t)) + goto skip_send_cmd; + *update_params_value16++ = CUSTOM_STEREO_CMD_PARAM_SIZE; + /*for alignment only*/ + *update_params_value16++ = 0; + /*index is 32-bit param in little endian*/ + *update_params_value16++ = CUSTOM_STEREO_INDEX_PARAM; + *update_params_value16++ = 0; + /*for stereo mixing num out ch*/ + *update_params_value16++ = CUSTOM_STEREO_NUM_OUT_CH; + /*for stereo mixing num in ch*/ + *update_params_value16++ = CUSTOM_STEREO_NUM_IN_CH; + + /* Out ch map FL/FR*/ + *update_params_value16++ = PCM_CHANNEL_FL; + *update_params_value16++ = PCM_CHANNEL_FR; + + /* In ch map FL/FR*/ + *update_params_value16++ = PCM_CHANNEL_FL; + *update_params_value16++ = PCM_CHANNEL_FR; + avail_length = avail_length - (10 * sizeof(uint16_t)); + /* weighting coefficients as name suggests, + * mixing will be done according to these coefficients + */ + if (avail_length < 4 * sizeof(uint16_t)) + goto skip_send_cmd; + *update_params_value16++ = op_FL_ip_FL_weight; + *update_params_value16++ = op_FL_ip_FR_weight; + *update_params_value16++ = op_FR_ip_FL_weight; + *update_params_value16++ = op_FR_ip_FR_weight; + avail_length = avail_length - (4 * sizeof(uint16_t)); + if (params_length) { + rc = adm_set_stereo_to_custom_stereo(port_id, + copp_idx, + session_id, + params_value, + params_length); + if (rc) { + pr_err("%s: send params failed rc=%d\n", __func__, rc); + kfree(params_value); + return -EINVAL; + } + } + kfree(params_value); + return 0; +skip_send_cmd: + pr_err("%s: insufficient memory, send cmd failed\n", + __func__); + kfree(params_value); + return -ENOMEM; +} + +static int msm_qti_pp_arrange_mch_map(int16_t *update_params_value16, + int channel_count) +{ + int i; + int16_t ch_map[PCM_FORMAT_MAX_CHANNELS_9] = { + PCM_CHANNEL_FL, PCM_CHANNEL_FR, PCM_CHANNEL_FC, + PCM_CHANNEL_LS, PCM_CHANNEL_RS, PCM_CHANNEL_LFE, + PCM_CHANNEL_LB, PCM_CHANNEL_RB, PCM_CHANNEL_CS }; + + if (channel_count < 1 || + channel_count > PCM_FORMAT_MAX_CHANNELS_9) { + pr_err("%s: invalid ch_cnt %d\n", + __func__, channel_count); + return -EINVAL; + } + + switch (channel_count) { + /* Add special cases here */ + case 1: + *update_params_value16++ = PCM_CHANNEL_FC; + break; + case 4: + *update_params_value16++ = PCM_CHANNEL_FL; + *update_params_value16++ = PCM_CHANNEL_FR; + *update_params_value16++ = PCM_CHANNEL_LS; + *update_params_value16++ = PCM_CHANNEL_RS; + break; + + /* Add standard cases here */ + default: + for (i = 0; i < channel_count; i++) + *update_params_value16++ = ch_map[i]; + break; + } + + return 0; +} + +static uint32_t msm_qti_pp_get_chmix_param_size(int ip_ch_cnt, int op_ch_cnt) +{ + uint32_t param_size; + /* Assign constant part of param length initially - + * Index, Num out channels, Num in channels. + */ + param_size = CHMIX_CFG_CONST_PARAM_SIZE * sizeof(uint16_t); + + /* Calculate variable part of param length using ip and op channels */ + + /* channel map for input and output channels */ + param_size += op_ch_cnt * sizeof(uint16_t); + param_size += ip_ch_cnt * sizeof(uint16_t); + + /* weightage coeff for each op ch corresponding to each ip ch */ + param_size += (ip_ch_cnt * op_ch_cnt) * sizeof(uint16_t); + + /* Params length should be multiple of 4 bytes i.e 32bit aligned*/ + param_size = (param_size + 3) & 0xFFFFFFFC; + + return param_size; +} + +/* + * msm_qti_pp_send_chmix_cfg_cmd: + * Send the custom channel mixer configuration command. + * + * @port_id: Backend port id + * @copp_idx: ADM copp index + * @session_id: id for the session requesting channel mixer + * @ip_channel_cnt: Input channel count + * @op_channel_cnt: Output channel count + * @ch_wght_coeff: Channel weight co-efficients for mixing + * @session_type: Indicates TX or RX session + * @stream_type: Indicates Audio or Listen stream type + */ +int msm_qti_pp_send_chmix_cfg_cmd(int port_id, int copp_idx, + unsigned int session_id, int ip_channel_cnt, + int op_channel_cnt, int *ch_wght_coeff, + int session_type, int stream_type) +{ + char *params_value; + int rc = 0, i, direction; + u8 *param_ptr; + int16_t *update_params_value16 = 0; + uint32_t param_size = msm_qti_pp_get_chmix_param_size(ip_channel_cnt, + op_channel_cnt); + struct param_hdr_v3 *param_hdr; + + /* constant payload data size represents module_id, param_id, + * param size, reserved field. + */ + uint32_t params_length = param_size + sizeof(*param_hdr); + + pr_debug("%s: port_id - %d, session id - %d\n", __func__, port_id, + session_id); + + params_value = kzalloc(params_length, GFP_KERNEL); + if (!params_value) + return -ENOMEM; + + param_ptr = params_value; + + param_hdr = (struct param_hdr_v3 *) param_ptr; + param_hdr->module_id = MTMX_MODULE_ID_DEFAULT_CHMIXER; + param_hdr->instance_id = INSTANCE_ID_0; + param_hdr->param_id = DEFAULT_CHMIXER_PARAM_ID_COEFF; + param_hdr->param_size = param_size; + + param_ptr += sizeof(*param_hdr); + + update_params_value16 = (int16_t *) param_ptr; + /*for alignment only*/ + *update_params_value16++ = 0; + /*index is 32-bit param in little endian*/ + *update_params_value16++ = CUSTOM_STEREO_INDEX_PARAM; + *update_params_value16++ = 0; + /*number of out ch*/ + *update_params_value16++ = op_channel_cnt; + /*number of in ch*/ + *update_params_value16++ = ip_channel_cnt; + + /* Out ch map FL/FR*/ + msm_qti_pp_arrange_mch_map(update_params_value16, op_channel_cnt); + update_params_value16 += op_channel_cnt; + + /* In ch map FL/FR*/ + msm_qti_pp_arrange_mch_map(update_params_value16, ip_channel_cnt); + update_params_value16 += ip_channel_cnt; + + /* weighting coefficients as name suggests, + * mixing will be done according to these coefficients. + */ + for (i = 0; i < ip_channel_cnt * op_channel_cnt; i++) + *update_params_value16++ = + ch_wght_coeff[i] ? Q14_GAIN_UNITY : 0; + if (params_length) { + direction = (session_type == SESSION_TYPE_RX) ? + ADM_MATRIX_ID_AUDIO_RX : ADM_MATRIX_ID_AUDIO_TX; + rc = adm_set_custom_chmix_cfg(port_id, + copp_idx, + session_id, + params_value, + params_length, + direction, + stream_type); + if (rc) { + pr_err("%s: send params failed rc=%d\n", __func__, rc); + kfree(params_value); + return -EINVAL; + } + } + kfree(params_value); + return 0; +} +EXPORT_SYMBOL(msm_qti_pp_send_chmix_cfg_cmd); +#endif /* CONFIG_QTI_PP */ + +/* RMS */ +static int msm_qti_pp_get_rms_value_control(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int rc = 0; + int be_idx = 0, copp_idx; + char *param_value; + int *update_param_value; + uint32_t param_size = (RMS_PAYLOAD_LEN + 1) * sizeof(uint32_t); + struct msm_pcm_routing_bdai_data msm_bedai; + struct param_hdr_v3 param_hdr; + + param_value = kzalloc(param_size, GFP_KERNEL); + if (!param_value) + return -ENOMEM; + msm_pcm_routing_acquire_lock(); + for (be_idx = 0; be_idx < MSM_BACKEND_DAI_MAX; be_idx++) { + msm_pcm_routing_get_bedai_info(be_idx, &msm_bedai); + if (msm_bedai.port_id == SLIMBUS_0_TX) + break; + } + if ((be_idx >= MSM_BACKEND_DAI_MAX) || !msm_bedai.active) { + pr_err("%s, back not active to query rms be_idx:%d\n", + __func__, be_idx); + rc = -EINVAL; + goto get_rms_value_err; + } + copp_idx = adm_get_default_copp_idx(SLIMBUS_0_TX); + if ((copp_idx < 0) || (copp_idx > MAX_COPPS_PER_PORT)) { + pr_err("%s, no active copp to query rms copp_idx:%d\n", + __func__, copp_idx); + rc = -EINVAL; + goto get_rms_value_err; + } + memset(¶m_hdr, 0, sizeof(param_hdr)); + param_hdr.module_id = RMS_MODULEID_APPI_PASSTHRU; + param_hdr.instance_id = INSTANCE_ID_0; + param_hdr.param_id = RMS_PARAM_FIRST_SAMPLE; + param_hdr.param_size = param_size; + rc = adm_get_pp_params(SLIMBUS_0_TX, copp_idx, ADM_CLIENT_ID_DEFAULT, + NULL, ¶m_hdr, param_value); + if (rc) { + pr_err("%s: get parameters failed rc=%d\n", __func__, rc); + rc = -EINVAL; + goto get_rms_value_err; + } + update_param_value = (int *)param_value; + ucontrol->value.integer.value[0] = update_param_value[0]; + + pr_debug("%s: FROM DSP value[0] 0x%x\n", + __func__, update_param_value[0]); +get_rms_value_err: + msm_pcm_routing_release_lock(); + kfree(param_value); + return rc; +} + +static int msm_qti_pp_put_rms_value_control(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + /* not used */ + return 0; +} + +/* VOLUME */ +static int msm_route_fm_vol_control; +static int msm_afe_lb_vol_ctrl; +static int msm_afe_sec_mi2s_lb_vol_ctrl; +static int msm_afe_tert_mi2s_lb_vol_ctrl; +static int msm_afe_quat_mi2s_lb_vol_ctrl; +static int msm_afe_slimbus_7_lb_vol_ctrl; +static int msm_afe_slimbus_8_lb_vol_ctrl; +static int msm_asm_bit_width; +static const DECLARE_TLV_DB_LINEAR(fm_rx_vol_gain, 0, INT_RX_VOL_MAX_STEPS); +static const DECLARE_TLV_DB_LINEAR(afe_lb_vol_gain, 0, INT_RX_VOL_MAX_STEPS); + +static int msm_qti_pp_get_fm_vol_mixer(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = msm_route_fm_vol_control; + return 0; +} + +static int msm_qti_pp_set_fm_vol_mixer(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + afe_loopback_gain(INT_FM_TX, ucontrol->value.integer.value[0]); + + msm_route_fm_vol_control = ucontrol->value.integer.value[0]; + + return 0; +} + +static int msm_asm_bit_width_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s get ASM bitwidth = %d\n", + __func__, msm_asm_bit_width); + + ucontrol->value.integer.value[0] = msm_asm_bit_width; + + return 0; +} + +static int msm_asm_bit_width_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (ucontrol->value.integer.value[0]) { + case 16: + msm_asm_bit_width = 16; + break; + case 24: + msm_asm_bit_width = 24; + break; + case 32: + msm_asm_bit_width = 32; + break; + default: + msm_asm_bit_width = 0; + break; + } + + return 0; +} + +static int msm_qti_pp_get_pri_mi2s_lb_vol_mixer(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = msm_afe_lb_vol_ctrl; + return 0; +} + +static int msm_qti_pp_set_pri_mi2s_lb_vol_mixer(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + afe_loopback_gain(AFE_PORT_ID_PRIMARY_MI2S_TX, + ucontrol->value.integer.value[0]); + + msm_afe_lb_vol_ctrl = ucontrol->value.integer.value[0]; + + return 0; +} + +static int msm_qti_pp_get_sec_mi2s_lb_vol_mixer(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = msm_afe_sec_mi2s_lb_vol_ctrl; + return 0; +} + +static int msm_qti_pp_set_sec_mi2s_lb_vol_mixer(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + afe_loopback_gain(AFE_PORT_ID_SECONDARY_MI2S_TX, + ucontrol->value.integer.value[0]); + msm_afe_sec_mi2s_lb_vol_ctrl = ucontrol->value.integer.value[0]; + + return 0; +} + +static int msm_qti_pp_get_tert_mi2s_lb_vol_mixer(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = msm_afe_tert_mi2s_lb_vol_ctrl; + return 0; +} + +static int msm_qti_pp_set_tert_mi2s_lb_vol_mixer(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + afe_loopback_gain(AFE_PORT_ID_TERTIARY_MI2S_TX, + ucontrol->value.integer.value[0]); + msm_afe_tert_mi2s_lb_vol_ctrl = ucontrol->value.integer.value[0]; + return 0; +} + +static int msm_qti_pp_get_slimbus_7_lb_vol_mixer(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = msm_afe_slimbus_7_lb_vol_ctrl; + return 0; +} + +static int msm_qti_pp_set_slimbus_7_lb_vol_mixer(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret = afe_loopback_gain(SLIMBUS_7_TX, + ucontrol->value.integer.value[0]); + + if (ret) + pr_err("%s: failed to set LB vol for SLIMBUS_7_TX, err %d\n", + __func__, ret); + else + msm_afe_slimbus_7_lb_vol_ctrl = + ucontrol->value.integer.value[0]; + + return ret; +} + +static int msm_qti_pp_get_slimbus_8_lb_vol_mixer(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = msm_afe_slimbus_8_lb_vol_ctrl; + return 0; +} + +static int msm_qti_pp_set_slimbus_8_lb_vol_mixer(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret = 0; + + ret = afe_loopback_gain(SLIMBUS_8_TX, + ucontrol->value.integer.value[0]); + + if (ret) + pr_err("%s: failed to set LB vol for SLIMBUS_8_TX", __func__); + else + msm_afe_slimbus_8_lb_vol_ctrl = + ucontrol->value.integer.value[0]; + + return ret; +} + +static int msm_qti_pp_get_icc_vol_mixer(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = msm_route_icc_vol_control; + return 0; +} + +static int msm_qti_pp_set_icc_vol_mixer(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + adm_set_mic_gain(AFE_PORT_ID_QUATERNARY_TDM_TX, + adm_get_default_copp_idx(AFE_PORT_ID_QUATERNARY_TDM_TX), + ucontrol->value.integer.value[0]); + msm_route_icc_vol_control = ucontrol->value.integer.value[0]; + return 0; +} + +static int msm_qti_pp_get_quat_mi2s_fm_vol_mixer(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = msm_afe_quat_mi2s_lb_vol_ctrl; + return 0; +} + +static int msm_qti_pp_set_quat_mi2s_fm_vol_mixer(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + afe_loopback_gain(AFE_PORT_ID_QUATERNARY_MI2S_TX, + ucontrol->value.integer.value[0]); + + msm_afe_quat_mi2s_lb_vol_ctrl = ucontrol->value.integer.value[0]; + + return 0; +} + +static int msm_qti_pp_get_hfp_vol_mixer(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = msm_route_hfp_vol_control; + return 0; +} + +static int msm_qti_pp_set_hfp_vol_mixer(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + afe_loopback_gain(INT_BT_SCO_TX, ucontrol->value.integer.value[0]); + + msm_route_hfp_vol_control = ucontrol->value.integer.value[0]; + + return 0; +} + +static int msm_qti_pp_get_pri_auxpcm_lb_vol_mixer( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = msm_route_pri_auxpcm_lb_vol_ctrl; + pr_debug("%s: Volume = %ld\n", __func__, + ucontrol->value.integer.value[0]); + return 0; +} + +static int msm_qti_pp_set_pri_auxpcm_lb_vol_mixer( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + + afe_loopback_gain(mc->reg, ucontrol->value.integer.value[0]); + + msm_route_pri_auxpcm_lb_vol_ctrl = ucontrol->value.integer.value[0]; + + return 0; +} + +static int msm_qti_pp_get_sec_auxpcm_lb_vol_mixer( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = msm_route_sec_auxpcm_lb_vol_ctrl; + pr_debug("%s: Volume = %ld\n", __func__, + ucontrol->value.integer.value[0]); + return 0; +} + +static int msm_qti_pp_set_sec_auxpcm_lb_vol_mixer( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + + afe_loopback_gain(mc->reg, ucontrol->value.integer.value[0]); + + msm_route_sec_auxpcm_lb_vol_ctrl = ucontrol->value.integer.value[0]; + + return 0; +} + +static int msm_qti_pp_get_channel_map_mixer(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + char channel_map[PCM_FORMAT_MAX_NUM_CHANNEL_V8] = {0}; + int i; + + adm_get_multi_ch_map(channel_map, ADM_PATH_PLAYBACK); + for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++) + ucontrol->value.integer.value[i] = + (unsigned int) channel_map[i]; + return 0; +} + +static int msm_qti_pp_put_channel_map_mixer(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + char channel_map[PCM_FORMAT_MAX_NUM_CHANNEL_V8]; + int i; + + for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++) + channel_map[i] = (char)(ucontrol->value.integer.value[i]); + adm_set_multi_ch_map(channel_map, ADM_PATH_PLAYBACK); + + return 0; +} + +/* Audio Sphere functions */ + +static void msm_qti_pp_asphere_init_state(void) +{ + int i; + + if (asphere_state.initialized) + return; + asphere_state.initialized = true; + for (i = 0; i < AFE_MAX_PORTS; i++) { + asphere_state.port_id[i] = -1; + asphere_state.copp_idx[i] = -1; + } + asphere_state.enabled = 0; + asphere_state.strength = 0; + asphere_state.mode = 0; + asphere_state.version = 0; + asphere_state.enabled_prev = 0; + asphere_state.strength_prev = 0; +} + +static int msm_qti_pp_asphere_send_params(int port_id, int copp_idx, bool force) +{ + u8 *packed_params = NULL; + u32 packed_params_size = 0; + u32 param_size = 0; + struct param_hdr_v3 param_hdr; + bool set_enable = force || + (asphere_state.enabled != asphere_state.enabled_prev); + bool set_strength = asphere_state.enabled == 1 && (set_enable || + (asphere_state.strength != asphere_state.strength_prev)); + int param_count = 0; + int ret = 0; + + if (set_enable) + param_count++; + if (set_strength) + param_count++; + + if (param_count == 0) { + pr_debug("%s: Nothing to send, exiting\n", __func__); + return 0; + } + + pr_debug("%s: port_id %d, copp_id %d, forced %d, param_count %d\n", + __func__, port_id, copp_idx, force, param_count); + pr_debug("%s: enable prev:%u cur:%u, strength prev:%u cur:%u\n", + __func__, asphere_state.enabled_prev, asphere_state.enabled, + asphere_state.strength_prev, asphere_state.strength); + + packed_params_size = + param_count * (sizeof(struct param_hdr_v3) + sizeof(uint32_t)); + packed_params = kzalloc(packed_params_size, GFP_KERNEL); + if (!packed_params) + return -ENOMEM; + + memset(¶m_hdr, 0, sizeof(param_hdr)); + packed_params_size = 0; + param_hdr.module_id = AUDPROC_MODULE_ID_AUDIOSPHERE; + param_hdr.instance_id = INSTANCE_ID_0; + if (set_strength) { + /* add strength command */ + param_hdr.param_id = AUDPROC_PARAM_ID_AUDIOSPHERE_STRENGTH; + param_hdr.param_size = sizeof(asphere_state.strength); + ret = q6common_pack_pp_params(packed_params + + packed_params_size, + ¶m_hdr, + (u8 *) &asphere_state.strength, + ¶m_size); + if (ret) { + pr_err("%s: Failed to pack params for audio sphere" + " strength, error %d\n", __func__, ret); + goto done; + } + packed_params_size += param_size; + } + if (set_enable) { + /* add enable command */ + param_hdr.param_id = AUDPROC_PARAM_ID_AUDIOSPHERE_ENABLE; + param_hdr.param_size = sizeof(asphere_state.enabled); + q6common_pack_pp_params(packed_params + packed_params_size, + ¶m_hdr, + (u8 *) &asphere_state.enabled, + ¶m_size); + if (ret) { + pr_err("%s: Failed to pack params for audio sphere" + " enable, error %d\n", __func__, ret); + goto done; + } + packed_params_size += param_size; + } + + pr_debug("%s: packed data size: %d\n", __func__, packed_params_size); + ret = adm_set_pp_params(port_id, copp_idx, NULL, packed_params, + packed_params_size); + if (ret) + pr_err("%s: set param failed with err=%d\n", __func__, ret); + +done: + kfree(packed_params); + return 0; +} + +#if defined(CONFIG_QTI_PP) && defined(CONFIG_QTI_PP_AUDIOSPHERE) +int msm_qti_pp_asphere_init(int port_id, int copp_idx) +{ + int index = adm_validate_and_get_port_index(port_id); + + pr_debug("%s, port_id %d, copp_id %d\n", __func__, port_id, copp_idx); + if (index < 0) { + pr_err("%s: Invalid port idx %d port_id %#x\n", __func__, index, + port_id); + return -EINVAL; + } + msm_qti_pp_asphere_init_state(); + + asphere_state.port_id[index] = port_id; + asphere_state.copp_idx[index] = copp_idx; + + if (asphere_state.enabled) + msm_qti_pp_asphere_send_params(port_id, copp_idx, true); + + return 0; +} + +void msm_qti_pp_asphere_deinit(int port_id) +{ + int index = adm_validate_and_get_port_index(port_id); + + pr_debug("%s, port_id %d\n", __func__, port_id); + if (index < 0) { + pr_err("%s: Invalid port idx %d port_id %#x\n", __func__, index, + port_id); + return; + } + + if (asphere_state.port_id[index] == port_id) { + asphere_state.port_id[index] = -1; + asphere_state.copp_idx[index] = -1; + } +} +#endif + +static int msm_qti_pp_asphere_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + if (!asphere_state.initialized) + return -EAGAIN; + ucontrol->value.integer.value[0] = asphere_state.enabled; + ucontrol->value.integer.value[1] = asphere_state.strength; + pr_debug("%s, enable %u, strength %u\n", __func__, + asphere_state.enabled, asphere_state.strength); + return 0; +} + +static int msm_qti_pp_asphere_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int32_t enable = ucontrol->value.integer.value[0]; + int32_t strength = ucontrol->value.integer.value[1]; + int i; + + pr_debug("%s, enable %u, strength %u\n", __func__, enable, strength); + + msm_qti_pp_asphere_init_state(); + + if (enable == 0 || enable == 1) { + asphere_state.enabled_prev = asphere_state.enabled; + asphere_state.enabled = enable; + } + + if (strength >= 0 && strength <= 1000) { + asphere_state.strength_prev = asphere_state.strength; + asphere_state.strength = strength; + } + + if (asphere_state.strength != asphere_state.strength_prev || + asphere_state.enabled != asphere_state.enabled_prev) { + for (i = 0; i < AFE_MAX_PORTS; i++) { + if (asphere_state.port_id[i] >= 0) + msm_qti_pp_asphere_send_params( + asphere_state.port_id[i], + asphere_state.copp_idx[i], + false); + } + } + return 0; +} + +int msm_adsp_init_mixer_ctl_pp_event_queue(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_kcontrol *kctl; + const char *deviceNo = "NN"; + char *mixer_str = NULL; + int ctl_len = 0, ret = 0; + const char *mixer_ctl_name = DSP_STREAM_CALLBACK; + struct dsp_stream_callback_prtd *kctl_prtd = NULL; + + if (!rtd) { + pr_err("%s: rtd is NULL\n", __func__); + ret = -EINVAL; + goto done; + } + + ctl_len = strlen(mixer_ctl_name) + 1 + strlen(deviceNo) + 1; + mixer_str = kzalloc(ctl_len, GFP_KERNEL); + if (!mixer_str) { + ret = -EINVAL; + goto done; + } + + snprintf(mixer_str, ctl_len, "%s %d", mixer_ctl_name, + rtd->pcm->device); + kctl = snd_soc_card_get_kcontrol(rtd->card, mixer_str); + kfree(mixer_str); + if (!kctl) { + pr_err("%s: failed to get kctl.\n", __func__); + ret = -EINVAL; + goto done; + } + + if (kctl->private_data != NULL) { + pr_err("%s: kctl_prtd is not NULL at initialization.\n", + __func__); + return -EINVAL; + } + + kctl_prtd = kzalloc(sizeof(struct dsp_stream_callback_prtd), + GFP_KERNEL); + if (!kctl_prtd) { + ret = -ENOMEM; + goto done; + } + + spin_lock_init(&kctl_prtd->prtd_spin_lock); + INIT_LIST_HEAD(&kctl_prtd->event_queue); + kctl_prtd->event_count = 0; + kctl->private_data = kctl_prtd; + +done: + return ret; +} + +int msm_adsp_clean_mixer_ctl_pp_event_queue(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_kcontrol *kctl; + const char *deviceNo = "NN"; + char *mixer_str = NULL; + int ctl_len = 0, ret = 0; + struct dsp_stream_callback_list *node, *n; + unsigned long spin_flags; + const char *mixer_ctl_name = DSP_STREAM_CALLBACK; + struct dsp_stream_callback_prtd *kctl_prtd = NULL; + + if (!rtd) { + pr_err("%s: rtd is NULL\n", __func__); + ret = -EINVAL; + goto done; + } + + ctl_len = strlen(mixer_ctl_name) + 1 + strlen(deviceNo) + 1; + mixer_str = kzalloc(ctl_len, GFP_KERNEL); + if (!mixer_str) { + ret = -EINVAL; + goto done; + } + + snprintf(mixer_str, ctl_len, "%s %d", mixer_ctl_name, + rtd->pcm->device); + kctl = snd_soc_card_get_kcontrol(rtd->card, mixer_str); + kfree(mixer_str); + if (!kctl) { + pr_err("%s: failed to get kctl.\n", __func__); + ret = -EINVAL; + goto done; + } + + kctl_prtd = (struct dsp_stream_callback_prtd *) + kctl->private_data; + if (kctl_prtd != NULL) { + spin_lock_irqsave(&kctl_prtd->prtd_spin_lock, spin_flags); + /* clean the queue */ + list_for_each_entry_safe(node, n, + &kctl_prtd->event_queue, list) { + list_del(&node->list); + kctl_prtd->event_count--; + pr_debug("%s: %d remaining events after del.\n", + __func__, kctl_prtd->event_count); + kfree(node); + } + spin_unlock_irqrestore(&kctl_prtd->prtd_spin_lock, spin_flags); + } + + kfree(kctl_prtd); + kctl->private_data = NULL; + +done: + return ret; +} + +int msm_adsp_inform_mixer_ctl(struct snd_soc_pcm_runtime *rtd, + uint32_t *payload) +{ + /* adsp pp event notifier */ + struct snd_kcontrol *kctl; + struct snd_ctl_elem_value control; + const char *deviceNo = "NN"; + char *mixer_str = NULL; + int ctl_len = 0, ret = 0; + struct dsp_stream_callback_list *new_event; + struct dsp_stream_callback_list *oldest_event; + unsigned long spin_flags; + struct dsp_stream_callback_prtd *kctl_prtd = NULL; + struct msm_adsp_event_data *event_data = NULL; + const char *mixer_ctl_name = DSP_STREAM_CALLBACK; + struct snd_ctl_elem_info kctl_info; + + if (!rtd || !payload) { + pr_err("%s: %s is NULL\n", __func__, + (!rtd) ? "rtd" : "payload"); + ret = -EINVAL; + goto done; + } + + if (rtd->card->snd_card == NULL) { + pr_err("%s: snd_card is null.\n", __func__); + ret = -EINVAL; + goto done; + } + + ctl_len = strlen(mixer_ctl_name) + 1 + strlen(deviceNo) + 1; + mixer_str = kzalloc(ctl_len, GFP_ATOMIC); + if (!mixer_str) { + ret = -EINVAL; + goto done; + } + + snprintf(mixer_str, ctl_len, "%s %d", mixer_ctl_name, + rtd->pcm->device); + kctl = snd_soc_card_get_kcontrol(rtd->card, mixer_str); + kfree(mixer_str); + if (!kctl) { + pr_err("%s: failed to get kctl.\n", __func__); + ret = -EINVAL; + goto done; + } + + event_data = (struct msm_adsp_event_data *)payload; + if (event_data->payload_len < sizeof(struct msm_adsp_event_data)) { + pr_err("%s: event_data size of %x is less than expected.\n", + __func__, event_data->payload_len); + ret = -EINVAL; + goto done; + } + + kctl->info(kctl, &kctl_info); + + if (event_data->payload_len > + kctl_info.count - sizeof(struct msm_adsp_event_data)) { + pr_err("%s: payload length exceeds limit of %u bytes.\n", + __func__, kctl_info.count); + ret = -EINVAL; + goto done; + } + + kctl_prtd = (struct dsp_stream_callback_prtd *) + kctl->private_data; + if (kctl_prtd == NULL) { + /* queue is not initialized */ + ret = -EINVAL; + pr_err("%s: event queue is not initialized.\n", __func__); + goto done; + } + + new_event = kzalloc(sizeof(struct dsp_stream_callback_list) + + event_data->payload_len, + GFP_ATOMIC); + if (new_event == NULL) { + ret = -ENOMEM; + goto done; + } + memcpy((void *)&new_event->event, (void *)payload, + event_data->payload_len + + sizeof(struct msm_adsp_event_data)); + + spin_lock_irqsave(&kctl_prtd->prtd_spin_lock, spin_flags); + while (kctl_prtd->event_count >= DSP_STREAM_CALLBACK_QUEUE_SIZE) { + pr_info("%s: queue of size %d is full. delete oldest one.\n", + __func__, DSP_STREAM_CALLBACK_QUEUE_SIZE); + oldest_event = list_first_entry(&kctl_prtd->event_queue, + struct dsp_stream_callback_list, list); + pr_info("%s: event deleted: type %d length %d\n", + __func__, oldest_event->event.event_type, + oldest_event->event.payload_len); + list_del(&oldest_event->list); + kctl_prtd->event_count--; + kfree(oldest_event); + } + + list_add_tail(&new_event->list, &kctl_prtd->event_queue); + kctl_prtd->event_count++; + spin_unlock_irqrestore(&kctl_prtd->prtd_spin_lock, spin_flags); + + control.id = kctl->id; + snd_ctl_notify(rtd->card->snd_card, + SNDRV_CTL_EVENT_MASK_INFO, + &control.id); + +done: + return ret; +} + +int msm_adsp_stream_cmd_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; + uinfo->count = + sizeof(((struct snd_ctl_elem_value *)0)->value.bytes.data); + + return 0; +} + +int msm_adsp_stream_callback_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + uint32_t payload_size = 0; + struct dsp_stream_callback_list *oldest_event; + unsigned long spin_flags; + struct dsp_stream_callback_prtd *kctl_prtd = NULL; + int ret = 0; + + kctl_prtd = (struct dsp_stream_callback_prtd *) + kcontrol->private_data; + if (kctl_prtd == NULL) { + pr_debug("%s: ASM Stream PP event queue is not initialized.\n", + __func__); + ret = -EINVAL; + goto done; + } + + spin_lock_irqsave(&kctl_prtd->prtd_spin_lock, spin_flags); + pr_debug("%s: %d events in queue.\n", __func__, kctl_prtd->event_count); + if (list_empty(&kctl_prtd->event_queue)) { + pr_err("%s: ASM Stream PP event queue is empty.\n", __func__); + ret = -EINVAL; + spin_unlock_irqrestore(&kctl_prtd->prtd_spin_lock, spin_flags); + goto done; + } + + oldest_event = list_first_entry(&kctl_prtd->event_queue, + struct dsp_stream_callback_list, list); + list_del(&oldest_event->list); + kctl_prtd->event_count--; + spin_unlock_irqrestore(&kctl_prtd->prtd_spin_lock, spin_flags); + + payload_size = oldest_event->event.payload_len; + pr_debug("%s: event fetched: type %d length %d\n", + __func__, oldest_event->event.event_type, + oldest_event->event.payload_len); + memcpy(ucontrol->value.bytes.data, &oldest_event->event, + sizeof(struct msm_adsp_event_data) + payload_size); + kfree(oldest_event); + +done: + return ret; +} + +int msm_adsp_stream_callback_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; + uinfo->count = + sizeof(((struct snd_ctl_elem_value *)0)->value.bytes.data); + + return 0; +} + +static int msm_multichannel_ec_primary_mic_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret = 0; + int copp_idx = 0; + int port_id = AFE_PORT_ID_QUATERNARY_TDM_TX; + + msm_multichannel_ec_primary_mic_ch = ucontrol->value.integer.value[0]; + pr_debug("%s: msm_multichannel_ec_primary_mic_ch = %u\n", + __func__, msm_multichannel_ec_primary_mic_ch); + copp_idx = adm_get_default_copp_idx(port_id); + if ((copp_idx < 0) || (copp_idx > MAX_COPPS_PER_PORT)) { + pr_err("%s : no active copp to query multichannel ec copp_idx: %u\n", + __func__, copp_idx); + return -EINVAL; + } + adm_send_set_multichannel_ec_primary_mic_ch(port_id, copp_idx, + msm_multichannel_ec_primary_mic_ch); + + return ret; +} + +static int msm_multichannel_ec_primary_mic_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = msm_multichannel_ec_primary_mic_ch; + pr_debug("%s: msm_multichannel_ec_primary_mic_ch = %lu\n", + __func__, ucontrol->value.integer.value[0]); + return 0; +} + +static const struct snd_kcontrol_new msm_multichannel_ec_controls[] = { + SOC_SINGLE_EXT("Multichannel EC Primary Mic Ch", SND_SOC_NOPM, 0, + 0xFFFFFFFF, 0, msm_multichannel_ec_primary_mic_ch_get, + msm_multichannel_ec_primary_mic_ch_put), +}; + +static const struct snd_kcontrol_new int_fm_vol_mixer_controls[] = { + SOC_SINGLE_EXT_TLV("Internal FM RX Volume", SND_SOC_NOPM, 0, + INT_RX_VOL_GAIN, 0, msm_qti_pp_get_fm_vol_mixer, + msm_qti_pp_set_fm_vol_mixer, fm_rx_vol_gain), + SOC_SINGLE_EXT_TLV("Quat MI2S FM RX Volume", SND_SOC_NOPM, 0, + INT_RX_VOL_GAIN, 0, msm_qti_pp_get_quat_mi2s_fm_vol_mixer, + msm_qti_pp_set_quat_mi2s_fm_vol_mixer, fm_rx_vol_gain), +}; + +static const struct snd_kcontrol_new dsp_bit_width_controls[] = { + SOC_SINGLE_EXT(DSP_BIT_WIDTH_MIXER_CTL, SND_SOC_NOPM, 0, 0x20, + 0, msm_asm_bit_width_get, msm_asm_bit_width_put), +}; + +static const struct snd_kcontrol_new pri_mi2s_lb_vol_mixer_controls[] = { + SOC_SINGLE_EXT_TLV("PRI MI2S LOOPBACK Volume", SND_SOC_NOPM, 0, + INT_RX_VOL_GAIN, 0, msm_qti_pp_get_pri_mi2s_lb_vol_mixer, + msm_qti_pp_set_pri_mi2s_lb_vol_mixer, afe_lb_vol_gain), +}; + +static const struct snd_kcontrol_new sec_mi2s_lb_vol_mixer_controls[] = { + SOC_SINGLE_EXT_TLV("SEC MI2S LOOPBACK Volume", SND_SOC_NOPM, 0, + INT_RX_VOL_GAIN, 0, msm_qti_pp_get_sec_mi2s_lb_vol_mixer, + msm_qti_pp_set_sec_mi2s_lb_vol_mixer, afe_lb_vol_gain), +}; + +static const struct snd_kcontrol_new tert_mi2s_lb_vol_mixer_controls[] = { + SOC_SINGLE_EXT_TLV("Tert MI2S LOOPBACK Volume", SND_SOC_NOPM, 0, + INT_RX_VOL_GAIN, 0, msm_qti_pp_get_tert_mi2s_lb_vol_mixer, + msm_qti_pp_set_tert_mi2s_lb_vol_mixer, afe_lb_vol_gain), +}; + +static const struct snd_kcontrol_new slimbus_7_lb_vol_mixer_controls[] = { + SOC_SINGLE_EXT_TLV("SLIMBUS_7 LOOPBACK Volume", SND_SOC_NOPM, 0, + INT_RX_VOL_GAIN, 0, + msm_qti_pp_get_slimbus_7_lb_vol_mixer, + msm_qti_pp_set_slimbus_7_lb_vol_mixer, + afe_lb_vol_gain), +}; + +static const struct snd_kcontrol_new slimbus_8_lb_vol_mixer_controls[] = { + SOC_SINGLE_EXT_TLV("SLIMBUS_8 LOOPBACK Volume", SND_SOC_NOPM, 0, + INT_RX_VOL_GAIN, 0, msm_qti_pp_get_slimbus_8_lb_vol_mixer, + msm_qti_pp_set_slimbus_8_lb_vol_mixer, afe_lb_vol_gain), +}; + +static const struct snd_kcontrol_new int_hfp_vol_mixer_controls[] = { + SOC_SINGLE_EXT_TLV("Internal HFP RX Volume", SND_SOC_NOPM, 0, + INT_RX_VOL_GAIN, 0, msm_qti_pp_get_hfp_vol_mixer, + msm_qti_pp_set_hfp_vol_mixer, hfp_rx_vol_gain), +}; + +static const struct snd_kcontrol_new int_icc_vol_mixer_controls[] = { + SOC_SINGLE_EXT_TLV("Internal ICC Volume", SND_SOC_NOPM, 0, + INT_RX_VOL_GAIN, 0, msm_qti_pp_get_icc_vol_mixer, + msm_qti_pp_set_icc_vol_mixer, icc_rx_vol_gain), +}; + +static const struct snd_kcontrol_new pri_auxpcm_lb_vol_mixer_controls[] = { + SOC_SINGLE_EXT_TLV("PRI AUXPCM LOOPBACK Volume", + AFE_PORT_ID_PRIMARY_PCM_TX, 0, INT_RX_VOL_GAIN, 0, + msm_qti_pp_get_pri_auxpcm_lb_vol_mixer, + msm_qti_pp_set_pri_auxpcm_lb_vol_mixer, + pri_auxpcm_lb_vol_gain), +}; + +static const struct snd_kcontrol_new sec_auxpcm_lb_vol_mixer_controls[] = { + SOC_SINGLE_EXT_TLV("SEC AUXPCM LOOPBACK Volume", + AFE_PORT_ID_SECONDARY_PCM_TX, 0, INT_RX_VOL_GAIN, 0, + msm_qti_pp_get_sec_auxpcm_lb_vol_mixer, + msm_qti_pp_set_sec_auxpcm_lb_vol_mixer, + sec_auxpcm_lb_vol_gain), +}; + +static const struct snd_kcontrol_new multi_ch_channel_map_mixer_controls[] = { + SOC_SINGLE_MULTI_EXT("Playback Device Channel Map", SND_SOC_NOPM, 0, 34, + 0, PCM_FORMAT_MAX_NUM_CHANNEL_V8, msm_qti_pp_get_channel_map_mixer, + msm_qti_pp_put_channel_map_mixer), +}; + + +static const struct snd_kcontrol_new get_rms_controls[] = { + SOC_SINGLE_EXT("Get RMS", SND_SOC_NOPM, 0, 0xFFFFFFFF, + 0, msm_qti_pp_get_rms_value_control, msm_qti_pp_put_rms_value_control), +}; + +static const struct snd_kcontrol_new eq_enable_mixer_controls[] = { + SOC_SINGLE_EXT("MultiMedia1 EQ Enable", SND_SOC_NOPM, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_qti_pp_get_eq_enable_mixer, + msm_qti_pp_put_eq_enable_mixer), + SOC_SINGLE_EXT("MultiMedia2 EQ Enable", SND_SOC_NOPM, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_qti_pp_get_eq_enable_mixer, + msm_qti_pp_put_eq_enable_mixer), + SOC_SINGLE_EXT("MultiMedia3 EQ Enable", SND_SOC_NOPM, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_qti_pp_get_eq_enable_mixer, + msm_qti_pp_put_eq_enable_mixer), +}; + +static const struct snd_kcontrol_new eq_band_mixer_controls[] = { + SOC_SINGLE_EXT("MultiMedia1 EQ Band Count", SND_SOC_NOPM, + MSM_FRONTEND_DAI_MULTIMEDIA1, 11, 0, + msm_qti_pp_get_eq_band_count_audio_mixer, + msm_qti_pp_put_eq_band_count_audio_mixer), + SOC_SINGLE_EXT("MultiMedia2 EQ Band Count", SND_SOC_NOPM, + MSM_FRONTEND_DAI_MULTIMEDIA2, 11, 0, + msm_qti_pp_get_eq_band_count_audio_mixer, + msm_qti_pp_put_eq_band_count_audio_mixer), + SOC_SINGLE_EXT("MultiMedia3 EQ Band Count", SND_SOC_NOPM, + MSM_FRONTEND_DAI_MULTIMEDIA3, 11, 0, + msm_qti_pp_get_eq_band_count_audio_mixer, + msm_qti_pp_put_eq_band_count_audio_mixer), +}; + +static const struct snd_kcontrol_new eq_coeff_mixer_controls[] = { + SOC_SINGLE_MULTI_EXT("MultiMedia1 EQ Band1", EQ_BAND1, + MSM_FRONTEND_DAI_MULTIMEDIA1, 255, 0, 5, + msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("MultiMedia1 EQ Band2", EQ_BAND2, + MSM_FRONTEND_DAI_MULTIMEDIA1, 255, 0, 5, + msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("MultiMedia1 EQ Band3", EQ_BAND3, + MSM_FRONTEND_DAI_MULTIMEDIA1, 255, 0, 5, + msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("MultiMedia1 EQ Band4", EQ_BAND4, + MSM_FRONTEND_DAI_MULTIMEDIA1, 255, 0, 5, + msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("MultiMedia1 EQ Band5", EQ_BAND5, + MSM_FRONTEND_DAI_MULTIMEDIA1, 255, 0, 5, + msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("MultiMedia1 EQ Band6", EQ_BAND6, + MSM_FRONTEND_DAI_MULTIMEDIA1, 255, 0, 5, + msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("MultiMedia1 EQ Band7", EQ_BAND7, + MSM_FRONTEND_DAI_MULTIMEDIA1, 255, 0, 5, + msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("MultiMedia1 EQ Band8", EQ_BAND8, + MSM_FRONTEND_DAI_MULTIMEDIA1, 255, 0, 5, + msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("MultiMedia1 EQ Band9", EQ_BAND9, + MSM_FRONTEND_DAI_MULTIMEDIA1, 255, 0, 5, + msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("MultiMedia1 EQ Band10", EQ_BAND10, + MSM_FRONTEND_DAI_MULTIMEDIA1, 255, 0, 5, + msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("MultiMedia1 EQ Band11", EQ_BAND11, + MSM_FRONTEND_DAI_MULTIMEDIA1, 255, 0, 5, + msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("MultiMedia1 EQ Band12", EQ_BAND12, + MSM_FRONTEND_DAI_MULTIMEDIA1, 255, 0, 5, + msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("MultiMedia2 EQ Band1", EQ_BAND1, + MSM_FRONTEND_DAI_MULTIMEDIA2, 255, 0, 5, + msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("MultiMedia2 EQ Band2", EQ_BAND2, + MSM_FRONTEND_DAI_MULTIMEDIA2, 255, 0, 5, + msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("MultiMedia2 EQ Band3", EQ_BAND3, + MSM_FRONTEND_DAI_MULTIMEDIA2, 255, 0, 5, + msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("MultiMedia2 EQ Band4", EQ_BAND4, + MSM_FRONTEND_DAI_MULTIMEDIA2, 255, 0, 5, + msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("MultiMedia2 EQ Band5", EQ_BAND5, + MSM_FRONTEND_DAI_MULTIMEDIA2, 255, 0, 5, + msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("MultiMedia2 EQ Band6", EQ_BAND6, + MSM_FRONTEND_DAI_MULTIMEDIA2, 255, 0, 5, + msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("MultiMedia2 EQ Band7", EQ_BAND7, + MSM_FRONTEND_DAI_MULTIMEDIA2, 255, 0, 5, + msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("MultiMedia2 EQ Band8", EQ_BAND8, + MSM_FRONTEND_DAI_MULTIMEDIA2, 255, 0, 5, + msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("MultiMedia2 EQ Band9", EQ_BAND9, + MSM_FRONTEND_DAI_MULTIMEDIA2, 255, 0, 5, + msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("MultiMedia2 EQ Band10", EQ_BAND10, + MSM_FRONTEND_DAI_MULTIMEDIA2, 255, 0, 5, + msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("MultiMedia2 EQ Band11", EQ_BAND11, + MSM_FRONTEND_DAI_MULTIMEDIA2, 255, 0, 5, + msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("MultiMedia2 EQ Band12", EQ_BAND12, + MSM_FRONTEND_DAI_MULTIMEDIA2, 255, 0, 5, + msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("MultiMedia3 EQ Band1", EQ_BAND1, + MSM_FRONTEND_DAI_MULTIMEDIA3, 255, 0, 5, + msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("MultiMedia3 EQ Band2", EQ_BAND2, + MSM_FRONTEND_DAI_MULTIMEDIA3, 255, 0, 5, + msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("MultiMedia3 EQ Band3", EQ_BAND3, + MSM_FRONTEND_DAI_MULTIMEDIA3, 255, 0, 5, + msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("MultiMedia3 EQ Band4", EQ_BAND4, + MSM_FRONTEND_DAI_MULTIMEDIA3, 255, 0, 5, + msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("MultiMedia3 EQ Band5", EQ_BAND5, + MSM_FRONTEND_DAI_MULTIMEDIA3, 255, 0, 5, + msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("MultiMedia3 EQ Band6", EQ_BAND6, + MSM_FRONTEND_DAI_MULTIMEDIA3, 255, 0, 5, + msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("MultiMedia3 EQ Band7", EQ_BAND7, + MSM_FRONTEND_DAI_MULTIMEDIA3, 255, 0, 5, + msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("MultiMedia3 EQ Band8", EQ_BAND8, + MSM_FRONTEND_DAI_MULTIMEDIA3, 255, 0, 5, + msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("MultiMedia3 EQ Band9", EQ_BAND9, + MSM_FRONTEND_DAI_MULTIMEDIA3, 255, 0, 5, + msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("MultiMedia3 EQ Band10", EQ_BAND10, + MSM_FRONTEND_DAI_MULTIMEDIA3, 255, 0, 5, + msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("MultiMedia3 EQ Band11", EQ_BAND11, + MSM_FRONTEND_DAI_MULTIMEDIA3, 255, 0, 5, + msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("MultiMedia3 EQ Band12", EQ_BAND12, + MSM_FRONTEND_DAI_MULTIMEDIA3, 255, 0, 5, + msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer), +}; + +static const struct snd_kcontrol_new asphere_mixer_controls[] = { + SOC_SINGLE_MULTI_EXT("MSM ASphere Set Param", SND_SOC_NOPM, 0, + 0xFFFFFFFF, 0, 2, msm_qti_pp_asphere_get, msm_qti_pp_asphere_set), +}; + +#ifdef CONFIG_QTI_PP +void msm_qti_pp_add_controls(struct snd_soc_platform *platform) +{ + snd_soc_add_platform_controls(platform, int_fm_vol_mixer_controls, + ARRAY_SIZE(int_fm_vol_mixer_controls)); + + snd_soc_add_platform_controls(platform, pri_mi2s_lb_vol_mixer_controls, + ARRAY_SIZE(pri_mi2s_lb_vol_mixer_controls)); + + snd_soc_add_platform_controls(platform, sec_mi2s_lb_vol_mixer_controls, + ARRAY_SIZE(sec_mi2s_lb_vol_mixer_controls)); + + snd_soc_add_platform_controls(platform, tert_mi2s_lb_vol_mixer_controls, + ARRAY_SIZE(tert_mi2s_lb_vol_mixer_controls)); + + snd_soc_add_platform_controls(platform, slimbus_7_lb_vol_mixer_controls, + ARRAY_SIZE(slimbus_7_lb_vol_mixer_controls)); + + snd_soc_add_platform_controls(platform, slimbus_8_lb_vol_mixer_controls, + ARRAY_SIZE(slimbus_8_lb_vol_mixer_controls)); + + snd_soc_add_platform_controls(platform, int_hfp_vol_mixer_controls, + ARRAY_SIZE(int_hfp_vol_mixer_controls)); + + snd_soc_add_platform_controls(platform, int_icc_vol_mixer_controls, + ARRAY_SIZE(int_icc_vol_mixer_controls)); + + snd_soc_add_platform_controls(platform, + pri_auxpcm_lb_vol_mixer_controls, + ARRAY_SIZE(pri_auxpcm_lb_vol_mixer_controls)); + + snd_soc_add_platform_controls(platform, + sec_auxpcm_lb_vol_mixer_controls, + ARRAY_SIZE(sec_auxpcm_lb_vol_mixer_controls)); + + snd_soc_add_platform_controls(platform, + multi_ch_channel_map_mixer_controls, + ARRAY_SIZE(multi_ch_channel_map_mixer_controls)); + + snd_soc_add_platform_controls(platform, get_rms_controls, + ARRAY_SIZE(get_rms_controls)); + + snd_soc_add_platform_controls(platform, eq_enable_mixer_controls, + ARRAY_SIZE(eq_enable_mixer_controls)); + + snd_soc_add_platform_controls(platform, eq_band_mixer_controls, + ARRAY_SIZE(eq_band_mixer_controls)); + + snd_soc_add_platform_controls(platform, eq_coeff_mixer_controls, + ARRAY_SIZE(eq_coeff_mixer_controls)); + + snd_soc_add_platform_controls(platform, asphere_mixer_controls, + ARRAY_SIZE(asphere_mixer_controls)); + + snd_soc_add_platform_controls(platform, msm_multichannel_ec_controls, + ARRAY_SIZE(msm_multichannel_ec_controls)); + + snd_soc_add_platform_controls(platform, dsp_bit_width_controls, + ARRAY_SIZE(dsp_bit_width_controls)); +} +#endif /* CONFIG_QTI_PP */ diff --git a/audio-kernel/asoc/msm-qti-pp-config.h b/audio-kernel/asoc/msm-qti-pp-config.h new file mode 100644 index 000000000..edbd60348 --- /dev/null +++ b/audio-kernel/asoc/msm-qti-pp-config.h @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2012-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 + * 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 _MSM_QTI_PP_H_ +#define _MSM_QTI_PP_H_ + +#include +#define DSP_BIT_WIDTH_MIXER_CTL "ASM Bit Width" +#ifdef CONFIG_QTI_PP +int msm_adsp_inform_mixer_ctl(struct snd_soc_pcm_runtime *rtd, + uint32_t *payload); +int msm_adsp_init_mixer_ctl_pp_event_queue(struct snd_soc_pcm_runtime *rtd); +int msm_adsp_clean_mixer_ctl_pp_event_queue(struct snd_soc_pcm_runtime *rtd); +int msm_adsp_stream_cmd_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo); +int msm_adsp_stream_callback_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +int msm_adsp_stream_callback_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo); +void msm_qti_pp_send_eq_values(int fedai_id); +int msm_qti_pp_send_stereo_to_custom_stereo_cmd(int port_id, int copp_idx, + unsigned int session_id, + uint16_t op_FL_ip_FL_weight, + uint16_t op_FL_ip_FR_weight, + uint16_t op_FR_ip_FL_weight, + uint16_t op_FR_ip_FR_weight); +void msm_qti_pp_add_controls(struct snd_soc_platform *platform); +int msm_qti_pp_send_chmix_cfg_cmd(int port_id, int copp_idx, + unsigned int session_id, int ip_channel_count, + int out_channel_cnt, int *ch_wght_coeff, + int session_type, int stream_type); +#else /* CONFIG_QTI_PP */ +static inline int msm_adsp_inform_mixer_ctl(struct snd_soc_pcm_runtime *rtd, + uint32_t *payload) +{ + return 0; +} + +static inline int msm_adsp_init_mixer_ctl_pp_event_queue( + struct snd_soc_pcm_runtime *rtd) +{ + return 0; +} + +static inline int msm_adsp_clean_mixer_ctl_pp_event_queue( + struct snd_soc_pcm_runtime *rtd) +{ + return 0; +} + +static inline int msm_adsp_stream_cmd_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + return 0; +} + +static inline int msm_adsp_stream_callback_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + return 0; +} + +static inline int msm_adsp_stream_callback_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + return 0; +} + +int msm_qti_pp_send_chmix_cfg_cmd(int port_id, int copp_idx, + unsigned int session_id, int ip_channel_count, + int out_channel_cnt, int *ch_wght_coeff, + int session_type, int stream_type) +{ + return 0; +} +#define msm_qti_pp_send_eq_values(fedai_id) do {} while (0) +#define msm_qti_pp_send_stereo_to_custom_stereo_cmd(port_id, copp_idx, \ + session_id, op_FL_ip_FL_weight, op_FL_ip_FR_weight, \ + op_FR_ip_FL_weight, op_FR_ip_FR_weight) (0) +#define msm_qti_pp_add_controls(platform) do {} while (0) +#endif /* CONFIG_QTI_PP */ + + +#if defined(CONFIG_QTI_PP) && defined(CONFIG_QTI_PP_AUDIOSPHERE) +int msm_qti_pp_asphere_init(int port_id, int copp_idx); +void msm_qti_pp_asphere_deinit(int port_id); +#else +#define msm_qti_pp_asphere_init(port_id, copp_idx) (0) +#define msm_qti_pp_asphere_deinit(port_id) do {} while (0) +#endif + +#endif /* _MSM_QTI_PP_H_ */ diff --git a/audio-kernel/asoc/msm-slim-dma.h b/audio-kernel/asoc/msm-slim-dma.h new file mode 100644 index 000000000..daf394bff --- /dev/null +++ b/audio-kernel/asoc/msm-slim-dma.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2014, 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 + * 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 _MSM_SLIMBUS_DMA_H +#define _MSM_SLIMBUS_DMA_H + +#include + +/* + * struct msm_slim_dma_data - DMA data for slimbus data transfer + * + * @sdev: Handle to the slim_device instance associated with the + * data transfer. + * @ph: Port handle for the slimbus ports. + * @dai_channel_ctl: callback function into the CPU dai driver + * to setup the data path. + * + * This structure is used to share the slimbus port handles and + * other data path setup related handles with other drivers. + */ +struct msm_slim_dma_data { + + /* Handle to slimbus device */ + struct slim_device *sdev; + + /* Port Handle */ + u32 ph; + + /* Callback for data channel control */ + int (*dai_channel_ctl)(struct msm_slim_dma_data *dma_data, + struct snd_soc_dai *dai, bool enable); +}; + +#endif diff --git a/audio-kernel/asoc/msm-transcode-loopback-q6-v2.c b/audio-kernel/asoc/msm-transcode-loopback-q6-v2.c new file mode 100644 index 000000000..f44e87d44 --- /dev/null +++ b/audio-kernel/asoc/msm-transcode-loopback-q6-v2.c @@ -0,0 +1,1552 @@ +/* Copyright (c) 2017-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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "msm-pcm-routing-v2.h" +#include "msm-qti-pp-config.h" + +#define LOOPBACK_SESSION_MAX_NUM_STREAMS 2 +/* Max volume corresponding to 24dB */ +#define TRANSCODE_LR_VOL_MAX_DB 0xFFFF + +#define APP_TYPE_CONFIG_IDX_APP_TYPE 0 +#define APP_TYPE_CONFIG_IDX_ACDB_ID 1 +#define APP_TYPE_CONFIG_IDX_SAMPLE_RATE 2 +#define APP_TYPE_CONFIG_IDX_BE_ID 3 + +static DEFINE_MUTEX(transcode_loopback_session_lock); + +struct msm_transcode_audio_effects { + struct bass_boost_params bass_boost; + struct pbe_params pbe; + struct virtualizer_params virtualizer; + struct reverb_params reverb; + struct eq_params equalizer; + struct soft_volume_params volume; +}; + +struct trans_loopback_pdata { + struct snd_compr_stream *cstream[MSM_FRONTEND_DAI_MAX]; + uint32_t master_gain; + int perf_mode; + struct msm_transcode_audio_effects *audio_effects[MSM_FRONTEND_DAI_MAX]; +}; + +struct loopback_stream { + struct snd_compr_stream *cstream; + uint32_t codec_format; + bool start; +}; + +enum loopback_session_state { + /* One or both streams not opened */ + LOOPBACK_SESSION_CLOSE = 0, + /* Loopback streams opened */ + LOOPBACK_SESSION_READY, + /* Loopback streams opened and formats configured */ + LOOPBACK_SESSION_START, + /* Trigger issued on either of streams when in START state */ + LOOPBACK_SESSION_RUN +}; + +struct msm_transcode_loopback { + struct loopback_stream source; + struct loopback_stream sink; + + struct snd_compr_caps source_compr_cap; + struct snd_compr_caps sink_compr_cap; + + uint32_t instance; + uint32_t num_streams; + int session_state; + + struct mutex lock; + + int session_id; + struct audio_client *audio_client; +}; + +/* Transcode loopback global info struct */ +static struct msm_transcode_loopback transcode_info; + +static void loopback_event_handler(uint32_t opcode, + uint32_t token, uint32_t *payload, void *priv) +{ + struct msm_transcode_loopback *trans = + (struct msm_transcode_loopback *)priv; + struct snd_soc_pcm_runtime *rtd; + struct snd_compr_stream *cstream; + struct audio_client *ac; + int stream_id; + int ret; + + if (!trans || !payload) { + pr_err("%s: rtd or payload is NULL\n", __func__); + return; + } + + cstream = trans->sink.cstream; + ac = trans->audio_client; + + /* + * Token for rest of the compressed commands use to set + * session id, stream id, dir etc. + */ + stream_id = q6asm_get_stream_id_from_token(token); + + switch (opcode) { + case ASM_STREAM_CMD_ENCDEC_EVENTS: + case ASM_IEC_61937_MEDIA_FMT_EVENT: + pr_debug("%s: Handling stream event : 0X%x\n", + __func__, opcode); + rtd = cstream->private_data; + if (!rtd) { + pr_err("%s: rtd is NULL\n", __func__); + return; + } + + ret = msm_adsp_inform_mixer_ctl(rtd, payload); + if (ret) { + pr_err("%s: failed to inform mixer ctrl. err = %d\n", + __func__, ret); + return; + } + break; + case APR_BASIC_RSP_RESULT: { + switch (payload[0]) { + case ASM_SESSION_CMD_RUN_V2: + pr_debug("%s: ASM_SESSION_CMD_RUN_V2:", __func__); + pr_debug("token 0x%x, stream id %d\n", token, + stream_id); + break; + case ASM_STREAM_CMD_CLOSE: + pr_debug("%s: ASM_DATA_CMD_CLOSE:", __func__); + pr_debug("token 0x%x, stream id %d\n", token, + stream_id); + break; + default: + break; + } + break; + } + default: + pr_debug("%s: Not Supported Event opcode[0x%x]\n", + __func__, opcode); + break; + } +} + +static void populate_codec_list(struct msm_transcode_loopback *trans, + struct snd_compr_stream *cstream) +{ + struct snd_compr_caps compr_cap; + + pr_debug("%s\n", __func__); + + memset(&compr_cap, 0, sizeof(struct snd_compr_caps)); + + if (cstream->direction == SND_COMPRESS_CAPTURE) { + compr_cap.direction = SND_COMPRESS_CAPTURE; + compr_cap.num_codecs = 3; + compr_cap.codecs[0] = SND_AUDIOCODEC_PCM; + compr_cap.codecs[1] = SND_AUDIOCODEC_AC3; + compr_cap.codecs[2] = SND_AUDIOCODEC_EAC3; + memcpy(&trans->source_compr_cap, &compr_cap, + sizeof(struct snd_compr_caps)); + } + + if (cstream->direction == SND_COMPRESS_PLAYBACK) { + compr_cap.direction = SND_COMPRESS_PLAYBACK; + compr_cap.num_codecs = 1; + compr_cap.codecs[0] = SND_AUDIOCODEC_PCM; + memcpy(&trans->sink_compr_cap, &compr_cap, + sizeof(struct snd_compr_caps)); + } +} + +static int msm_transcode_loopback_open(struct snd_compr_stream *cstream) +{ + int ret = 0; + struct snd_compr_runtime *runtime; + struct snd_soc_pcm_runtime *rtd; + struct msm_transcode_loopback *trans = &transcode_info; + struct trans_loopback_pdata *pdata; + + if (cstream == NULL) { + pr_err("%s: Invalid substream\n", __func__); + return -EINVAL; + } + runtime = cstream->runtime; + rtd = snd_pcm_substream_chip(cstream); + pdata = snd_soc_platform_get_drvdata(rtd->platform); + pdata->cstream[rtd->dai_link->id] = cstream; + pdata->audio_effects[rtd->dai_link->id] = + kzalloc(sizeof(struct msm_transcode_audio_effects), GFP_KERNEL); + + if (pdata->audio_effects[rtd->dai_link->id] == NULL) { + ret = -ENOMEM; + goto effect_error; + } + + mutex_lock(&trans->lock); + if (trans->num_streams > LOOPBACK_SESSION_MAX_NUM_STREAMS) { + pr_err("msm_transcode_open failed..invalid stream\n"); + ret = -EINVAL; + goto exit; + } + + if (cstream->direction == SND_COMPRESS_CAPTURE) { + if (trans->source.cstream == NULL) { + trans->source.cstream = cstream; + trans->num_streams++; + } else { + pr_err("%s: capture stream already opened\n", + __func__); + ret = -EINVAL; + goto exit; + } + } else if (cstream->direction == SND_COMPRESS_PLAYBACK) { + if (trans->sink.cstream == NULL) { + trans->sink.cstream = cstream; + trans->num_streams++; + } else { + pr_debug("%s: playback stream already opened\n", + __func__); + ret = -EINVAL; + goto exit; + } + msm_adsp_init_mixer_ctl_pp_event_queue(rtd); + } + + pr_debug("%s: num stream%d, stream name %s\n", __func__, + trans->num_streams, cstream->name); + + populate_codec_list(trans, cstream); + + if (trans->num_streams == LOOPBACK_SESSION_MAX_NUM_STREAMS) { + pr_debug("%s: Moving loopback session to READY state %d\n", + __func__, trans->session_state); + trans->session_state = LOOPBACK_SESSION_READY; + } + + runtime->private_data = trans; + +exit: + mutex_unlock(&trans->lock); + if ((pdata->audio_effects[rtd->dai_link->id] != NULL) && (ret < 0)) { + kfree(pdata->audio_effects[rtd->dai_link->id]); + pdata->audio_effects[rtd->dai_link->id] = NULL; + } +effect_error: + return ret; +} + +static void stop_transcoding(struct msm_transcode_loopback *trans) +{ + struct snd_soc_pcm_runtime *soc_pcm_rx; + struct snd_soc_pcm_runtime *soc_pcm_tx; + + if (trans->audio_client != NULL) { + q6asm_cmd(trans->audio_client, CMD_CLOSE); + + if (trans->sink.cstream != NULL) { + soc_pcm_rx = trans->sink.cstream->private_data; + msm_pcm_routing_dereg_phy_stream( + soc_pcm_rx->dai_link->id, + SND_COMPRESS_PLAYBACK); + } + if (trans->source.cstream != NULL) { + soc_pcm_tx = trans->source.cstream->private_data; + msm_pcm_routing_dereg_phy_stream( + soc_pcm_tx->dai_link->id, + SND_COMPRESS_CAPTURE); + } + q6asm_audio_client_free(trans->audio_client); + trans->audio_client = NULL; + } +} + +static int msm_transcode_loopback_free(struct snd_compr_stream *cstream) +{ + struct snd_compr_runtime *runtime = cstream->runtime; + struct msm_transcode_loopback *trans = runtime->private_data; + struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(cstream); + struct trans_loopback_pdata *pdata; + int ret = 0; + + pdata = snd_soc_platform_get_drvdata(rtd->platform); + + mutex_lock(&trans->lock); + + if (pdata->audio_effects[rtd->dai_link->id] != NULL) { + kfree(pdata->audio_effects[rtd->dai_link->id]); + pdata->audio_effects[rtd->dai_link->id] = NULL; + } + + pr_debug("%s: Transcode loopback end:%d, streams %d\n", __func__, + cstream->direction, trans->num_streams); + trans->num_streams--; + stop_transcoding(trans); + + if (cstream->direction == SND_COMPRESS_PLAYBACK) { + memset(&trans->sink, 0, sizeof(struct loopback_stream)); + msm_adsp_clean_mixer_ctl_pp_event_queue(rtd); + } else if (cstream->direction == SND_COMPRESS_CAPTURE) { + memset(&trans->source, 0, sizeof(struct loopback_stream)); + } + + trans->session_state = LOOPBACK_SESSION_CLOSE; + mutex_unlock(&trans->lock); + return ret; +} + +static int msm_transcode_loopback_trigger(struct snd_compr_stream *cstream, + int cmd) +{ + struct snd_compr_runtime *runtime = cstream->runtime; + struct msm_transcode_loopback *trans = runtime->private_data; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + + if (trans->session_state == LOOPBACK_SESSION_START) { + pr_debug("%s: Issue Loopback session %d RUN\n", + __func__, trans->instance); + q6asm_run_nowait(trans->audio_client, 0, 0, 0); + trans->session_state = LOOPBACK_SESSION_RUN; + } + break; + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_STOP: + pr_debug("%s: Issue Loopback session %d STOP\n", __func__, + trans->instance); + if (trans->session_state == LOOPBACK_SESSION_RUN) + q6asm_cmd_nowait(trans->audio_client, CMD_PAUSE); + trans->session_state = LOOPBACK_SESSION_START; + break; + + default: + break; + } + return 0; +} + +static int msm_transcode_set_render_window(struct audio_client *ac, + uint32_t ws_lsw, uint32_t ws_msw, + uint32_t we_lsw, uint32_t we_msw) +{ + int ret = -EINVAL; + struct asm_session_mtmx_strtr_param_window_v2_t asm_mtmx_strtr_window; + uint32_t param_id; + + pr_debug("%s, ws_lsw 0x%x ws_msw 0x%x we_lsw 0x%x we_msw 0x%x\n", + __func__, ws_lsw, ws_msw, we_lsw, we_msw); + + memset(&asm_mtmx_strtr_window, 0, + sizeof(struct asm_session_mtmx_strtr_param_window_v2_t)); + asm_mtmx_strtr_window.window_lsw = ws_lsw; + asm_mtmx_strtr_window.window_msw = ws_msw; + param_id = ASM_SESSION_MTMX_STRTR_PARAM_RENDER_WINDOW_START_V2; + ret = q6asm_send_mtmx_strtr_window(ac, &asm_mtmx_strtr_window, param_id); + if (ret) { + pr_err("%s, start window can't be set error %d\n", __func__, ret); + goto exit; + } + + asm_mtmx_strtr_window.window_lsw = we_lsw; + asm_mtmx_strtr_window.window_msw = we_msw; + param_id = ASM_SESSION_MTMX_STRTR_PARAM_RENDER_WINDOW_END_V2; + ret = q6asm_send_mtmx_strtr_window(ac, &asm_mtmx_strtr_window, param_id); + if (ret) + pr_err("%s, end window can't be set error %d\n", __func__, ret); + +exit: + return ret; +} + +static int msm_transcode_loopback_set_params(struct snd_compr_stream *cstream, + struct snd_compr_params *codec_param) +{ + + struct snd_compr_runtime *runtime = cstream->runtime; + struct msm_transcode_loopback *trans = runtime->private_data; + struct snd_soc_pcm_runtime *soc_pcm_rx; + struct snd_soc_pcm_runtime *soc_pcm_tx; + struct snd_soc_pcm_runtime *rtd; + struct trans_loopback_pdata *pdata; + uint32_t bit_width = 16; + int ret = 0; + + if (trans == NULL) { + pr_err("%s: Invalid param\n", __func__); + return -EINVAL; + } + + mutex_lock(&trans->lock); + + rtd = snd_pcm_substream_chip(cstream); + pdata = snd_soc_platform_get_drvdata(rtd->platform); + + if (cstream->direction == SND_COMPRESS_PLAYBACK) { + if (codec_param->codec.id == SND_AUDIOCODEC_PCM) { + trans->sink.codec_format = + FORMAT_LINEAR_PCM; + switch (codec_param->codec.format) { + case SNDRV_PCM_FORMAT_S32_LE: + bit_width = 32; + break; + case SNDRV_PCM_FORMAT_S24_LE: + bit_width = 24; + break; + case SNDRV_PCM_FORMAT_S24_3LE: + bit_width = 24; + break; + case SNDRV_PCM_FORMAT_S16_LE: + default: + bit_width = 16; + break; + } + } else { + pr_debug("%s: unknown sink codec\n", __func__); + ret = -EINVAL; + goto exit; + } + trans->sink.start = true; + } + + if (cstream->direction == SND_COMPRESS_CAPTURE) { + switch (codec_param->codec.id) { + case SND_AUDIOCODEC_PCM: + pr_debug("Source SND_AUDIOCODEC_PCM\n"); + trans->source.codec_format = + FORMAT_LINEAR_PCM; + break; + case SND_AUDIOCODEC_AC3: + pr_debug("Source SND_AUDIOCODEC_AC3\n"); + trans->source.codec_format = + FORMAT_AC3; + break; + case SND_AUDIOCODEC_EAC3: + pr_debug("Source SND_AUDIOCODEC_EAC3\n"); + trans->source.codec_format = + FORMAT_EAC3; + break; + default: + pr_debug("%s: unknown source codec\n", __func__); + ret = -EINVAL; + goto exit; + } + trans->source.start = true; + } + + pr_debug("%s: trans->source.start %d trans->sink.start %d trans->source.cstream %pK trans->sink.cstream %pK trans->session_state %d\n", + __func__, trans->source.start, trans->sink.start, + trans->source.cstream, trans->sink.cstream, + trans->session_state); + + if ((trans->session_state == LOOPBACK_SESSION_READY) && + trans->source.start && trans->sink.start) { + pr_debug("%s: Moving loopback session to start state\n", + __func__); + trans->session_state = LOOPBACK_SESSION_START; + } + + if (trans->session_state == LOOPBACK_SESSION_START) { + if (trans->audio_client != NULL) { + pr_debug("%s: ASM client already opened, closing\n", + __func__); + stop_transcoding(trans); + } + + trans->audio_client = q6asm_audio_client_alloc( + (app_cb)loopback_event_handler, trans); + if (!trans->audio_client) { + pr_err("%s: Could not allocate memory\n", __func__); + ret = -EINVAL; + goto exit; + } + pr_debug("%s: ASM client allocated, callback %pK\n", __func__, + loopback_event_handler); + trans->session_id = trans->audio_client->session; + trans->audio_client->perf_mode = pdata->perf_mode; + ret = q6asm_open_transcode_loopback(trans->audio_client, + bit_width, + trans->source.codec_format, + trans->sink.codec_format); + if (ret < 0) { + pr_err("%s: Session transcode loopback open failed\n", + __func__); + q6asm_audio_client_free(trans->audio_client); + trans->audio_client = NULL; + goto exit; + } + + pr_debug("%s: Starting ADM open for loopback\n", __func__); + soc_pcm_rx = trans->sink.cstream->private_data; + soc_pcm_tx = trans->source.cstream->private_data; + if (trans->source.codec_format != FORMAT_LINEAR_PCM) + msm_pcm_routing_reg_phy_compr_stream( + soc_pcm_tx->dai_link->id, + LEGACY_PCM_MODE, + trans->session_id, + SNDRV_PCM_STREAM_CAPTURE, + COMPRESSED_PASSTHROUGH_GEN); + else + msm_pcm_routing_reg_phy_stream( + soc_pcm_tx->dai_link->id, + trans->audio_client->perf_mode, + trans->session_id, + SNDRV_PCM_STREAM_CAPTURE); + /* Opening Rx ADM in LOW_LATENCY mode by default */ + msm_pcm_routing_reg_phy_stream( + soc_pcm_rx->dai_link->id, + trans->audio_client->perf_mode, + trans->session_id, + SNDRV_PCM_STREAM_PLAYBACK); + pr_debug("%s: Successfully opened ADM sessions\n", __func__); + } +exit: + mutex_unlock(&trans->lock); + return ret; +} + +static int msm_transcode_loopback_get_caps(struct snd_compr_stream *cstream, + struct snd_compr_caps *arg) +{ + struct snd_compr_runtime *runtime; + struct msm_transcode_loopback *trans; + + if (!arg || !cstream) { + pr_err("%s: Invalid arguments\n", __func__); + return -EINVAL; + } + + runtime = cstream->runtime; + trans = runtime->private_data; + pr_debug("%s\n", __func__); + if (cstream->direction == SND_COMPRESS_CAPTURE) + memcpy(arg, &trans->source_compr_cap, + sizeof(struct snd_compr_caps)); + else + memcpy(arg, &trans->sink_compr_cap, + sizeof(struct snd_compr_caps)); + return 0; +} + +static int msm_transcode_loopback_set_metadata(struct snd_compr_stream *cstream, + struct snd_compr_metadata *metadata) +{ + struct snd_soc_pcm_runtime *rtd; + struct trans_loopback_pdata *pdata; + struct msm_transcode_loopback *prtd = NULL; + struct audio_client *ac = NULL; + + if (!metadata || !cstream) { + pr_err("%s: Invalid arguments\n", __func__); + return -EINVAL; + } + + rtd = snd_pcm_substream_chip(cstream); + pdata = snd_soc_platform_get_drvdata(rtd->platform); + + prtd = cstream->runtime->private_data; + + if (!prtd || !prtd->audio_client) { + pr_err("%s: prtd or audio client is NULL\n", __func__); + return -EINVAL; + } + + ac = prtd->audio_client; + + switch (metadata->key) { + case SNDRV_COMPRESS_LATENCY_MODE: + { + switch (metadata->value[0]) { + case SNDRV_COMPRESS_LEGACY_LATENCY_MODE: + pdata->perf_mode = LEGACY_PCM_MODE; + break; + case SNDRV_COMPRESS_LOW_LATENCY_MODE: + pdata->perf_mode = LOW_LATENCY_PCM_MODE; + break; + default: + pr_debug("%s: Unsupported latency mode %d, default to Legacy\n", + __func__, metadata->value[0]); + pdata->perf_mode = LEGACY_PCM_MODE; + break; + } + break; + } + case SNDRV_COMPRESS_RENDER_WINDOW: + { + return msm_transcode_set_render_window( + ac, + metadata->value[0], + metadata->value[1], + metadata->value[2], + metadata->value[3]); + } + default: + pr_debug("%s: Unsupported metadata %d\n", + __func__, metadata->key); + break; + } + return 0; +} + +static int msm_transcode_stream_cmd_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol); + unsigned long fe_id = kcontrol->private_value; + struct trans_loopback_pdata *pdata = (struct trans_loopback_pdata *) + snd_soc_component_get_drvdata(comp); + struct snd_compr_stream *cstream = NULL; + struct msm_transcode_loopback *prtd; + int ret = 0; + struct msm_adsp_event_data *event_data = NULL; + + if (fe_id >= MSM_FRONTEND_DAI_MAX) { + pr_err("%s Received invalid fe_id %lu\n", + __func__, fe_id); + ret = -EINVAL; + goto done; + } + + cstream = pdata->cstream[fe_id]; + if (cstream == NULL) { + pr_err("%s cstream is null.\n", __func__); + ret = -EINVAL; + goto done; + } + + prtd = cstream->runtime->private_data; + if (!prtd) { + pr_err("%s: prtd is null.\n", __func__); + ret = -EINVAL; + goto done; + } + + if (prtd->audio_client == NULL) { + pr_err("%s: audio_client is null.\n", __func__); + ret = -EINVAL; + goto done; + } + + event_data = (struct msm_adsp_event_data *)ucontrol->value.bytes.data; + if ((event_data->event_type < ADSP_STREAM_PP_EVENT) || + (event_data->event_type >= ADSP_STREAM_EVENT_MAX)) { + pr_err("%s: invalid event_type=%d", + __func__, event_data->event_type); + ret = -EINVAL; + goto done; + } + + if (event_data->payload_len > sizeof(ucontrol->value.bytes.data) + - sizeof(struct msm_adsp_event_data)) { + pr_err("%s param length=%d exceeds limit", + __func__, event_data->payload_len); + ret = -EINVAL; + goto done; + } + + ret = q6asm_send_stream_cmd(prtd->audio_client, event_data); + if (ret < 0) + pr_err("%s: failed to send stream event cmd, err = %d\n", + __func__, ret); +done: + return ret; +} + +static int msm_transcode_ion_fd_map_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol); + unsigned long fe_id = kcontrol->private_value; + struct trans_loopback_pdata *pdata = (struct trans_loopback_pdata *) + snd_soc_component_get_drvdata(comp); + struct snd_compr_stream *cstream = NULL; + struct msm_transcode_loopback *prtd; + int fd; + int ret = 0; + + if (fe_id >= MSM_FRONTEND_DAI_MAX) { + pr_err("%s Received out of bounds invalid fe_id %lu\n", + __func__, fe_id); + ret = -EINVAL; + goto done; + } + + cstream = pdata->cstream[fe_id]; + if (cstream == NULL) { + pr_err("%s cstream is null\n", __func__); + ret = -EINVAL; + goto done; + } + + prtd = cstream->runtime->private_data; + if (!prtd) { + pr_err("%s: prtd is null\n", __func__); + ret = -EINVAL; + goto done; + } + + if (prtd->audio_client == NULL) { + pr_err("%s: audio_client is null\n", __func__); + ret = -EINVAL; + goto done; + } + + memcpy(&fd, ucontrol->value.bytes.data, sizeof(fd)); + ret = q6asm_send_ion_fd(prtd->audio_client, fd); + if (ret < 0) + pr_err("%s: failed to register ion fd\n", __func__); +done: + return ret; +} + +static int msm_transcode_rtic_event_ack_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol); + unsigned long fe_id = kcontrol->private_value; + struct trans_loopback_pdata *pdata = (struct trans_loopback_pdata *) + snd_soc_component_get_drvdata(comp); + struct snd_compr_stream *cstream = NULL; + struct msm_transcode_loopback *prtd; + int ret = 0; + int param_length = 0; + + if (fe_id >= MSM_FRONTEND_DAI_MAX) { + pr_err("%s Received invalid fe_id %lu\n", + __func__, fe_id); + ret = -EINVAL; + goto done; + } + + cstream = pdata->cstream[fe_id]; + if (cstream == NULL) { + pr_err("%s cstream is null\n", __func__); + ret = -EINVAL; + goto done; + } + + prtd = cstream->runtime->private_data; + if (!prtd) { + pr_err("%s: prtd is null\n", __func__); + ret = -EINVAL; + goto done; + } + + if (prtd->audio_client == NULL) { + pr_err("%s: audio_client is null\n", __func__); + ret = -EINVAL; + goto done; + } + + memcpy(¶m_length, ucontrol->value.bytes.data, + sizeof(param_length)); + if ((param_length + sizeof(param_length)) + >= sizeof(ucontrol->value.bytes.data)) { + pr_err("%s param length=%d exceeds limit", + __func__, param_length); + ret = -EINVAL; + goto done; + } + + ret = q6asm_send_rtic_event_ack(prtd->audio_client, + ucontrol->value.bytes.data + sizeof(param_length), + param_length); + if (ret < 0) + pr_err("%s: failed to send rtic event ack, err = %d\n", + __func__, ret); +done: + return ret; +} + +static int msm_transcode_playback_app_type_cfg_put( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u64 fe_id = kcontrol->private_value; + int session_type = SESSION_TYPE_RX; + int be_id = ucontrol->value.integer.value[APP_TYPE_CONFIG_IDX_BE_ID]; + struct msm_pcm_stream_app_type_cfg cfg_data = {0, 0, 48000}; + int ret = 0; + + cfg_data.app_type = ucontrol->value.integer.value[ + APP_TYPE_CONFIG_IDX_APP_TYPE]; + cfg_data.acdb_dev_id = ucontrol->value.integer.value[ + APP_TYPE_CONFIG_IDX_ACDB_ID]; + if (ucontrol->value.integer.value[APP_TYPE_CONFIG_IDX_SAMPLE_RATE] != 0) + cfg_data.sample_rate = ucontrol->value.integer.value[ + APP_TYPE_CONFIG_IDX_SAMPLE_RATE]; + pr_debug("%s: fe_id %llu session_type %d be_id %d app_type %d acdb_dev_id %d sample_rate- %d\n", + __func__, fe_id, session_type, be_id, + cfg_data.app_type, cfg_data.acdb_dev_id, cfg_data.sample_rate); + ret = msm_pcm_routing_reg_stream_app_type_cfg(fe_id, session_type, + be_id, &cfg_data); + if (ret < 0) + pr_err("%s: msm_transcode_playback_stream_app_type_cfg set failed returned %d\n", + __func__, ret); + + return ret; +} + +static int msm_transcode_playback_app_type_cfg_get( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u64 fe_id = kcontrol->private_value; + int session_type = SESSION_TYPE_RX; + int be_id = 0; + struct msm_pcm_stream_app_type_cfg cfg_data = {0}; + int ret = 0; + + ret = msm_pcm_routing_get_stream_app_type_cfg(fe_id, session_type, + &be_id, &cfg_data); + if (ret < 0) { + pr_err("%s: msm_transcode_playback_stream_app_type_cfg get failed returned %d\n", + __func__, ret); + goto done; + } + + ucontrol->value.integer.value[APP_TYPE_CONFIG_IDX_APP_TYPE] = + cfg_data.app_type; + ucontrol->value.integer.value[APP_TYPE_CONFIG_IDX_ACDB_ID] = + cfg_data.acdb_dev_id; + ucontrol->value.integer.value[APP_TYPE_CONFIG_IDX_SAMPLE_RATE] = + cfg_data.sample_rate; + ucontrol->value.integer.value[APP_TYPE_CONFIG_IDX_BE_ID] = be_id; + pr_debug("%s: fedai_id %llu, session_type %d, be_id %d, app_type %d, acdb_dev_id %d, sample_rate %d\n", + __func__, fe_id, session_type, be_id, + cfg_data.app_type, cfg_data.acdb_dev_id, cfg_data.sample_rate); +done: + return ret; +} + +static int msm_transcode_set_volume(struct snd_compr_stream *cstream, + uint32_t master_gain) +{ + int rc = 0; + struct msm_transcode_loopback *prtd; + struct snd_soc_pcm_runtime *rtd; + + pr_debug("%s: master_gain %d\n", __func__, master_gain); + if (!cstream || !cstream->runtime) { + pr_err("%s: session not active\n", __func__); + return -EINVAL; + } + rtd = cstream->private_data; + prtd = cstream->runtime->private_data; + + if (!rtd || !rtd->platform || !prtd || !prtd->audio_client) { + pr_err("%s: invalid rtd, prtd or audio client", __func__); + return -EINVAL; + } + + rc = q6asm_set_volume(prtd->audio_client, master_gain); + if (rc < 0) + pr_err("%s: Send vol gain command failed rc=%d\n", + __func__, rc); + + return rc; +} + +static int msm_transcode_volume_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol); + unsigned long fe_id = kcontrol->private_value; + struct trans_loopback_pdata *pdata = (struct trans_loopback_pdata *) + snd_soc_component_get_drvdata(comp); + struct snd_compr_stream *cstream = NULL; + uint32_t ret = 0; + + if (fe_id >= MSM_FRONTEND_DAI_MAX) { + pr_err("%s Received out of bounds fe_id %lu\n", + __func__, fe_id); + return -EINVAL; + } + + cstream = pdata->cstream[fe_id]; + pdata->master_gain = ucontrol->value.integer.value[0]; + + pr_debug("%s: fe_id %lu master_gain %d\n", + __func__, fe_id, pdata->master_gain); + if (cstream) + ret = msm_transcode_set_volume(cstream, pdata->master_gain); + return ret; +} + +static int msm_transcode_volume_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol); + unsigned long fe_id = kcontrol->private_value; + + struct trans_loopback_pdata *pdata = (struct trans_loopback_pdata *) + snd_soc_component_get_drvdata(comp); + + if (fe_id >= MSM_FRONTEND_DAI_MAX) { + pr_err("%s Received out of bound fe_id %lu\n", __func__, fe_id); + return -EINVAL; + } + + pr_debug("%s: fe_id %lu\n", __func__, fe_id); + ucontrol->value.integer.value[0] = pdata->master_gain; + + return 0; +} + +static int msm_transcode_audio_effects_config_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = MAX_PP_PARAMS_SZ; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 0xFFFFFFFF; + return 0; +} + +static int msm_transcode_audio_effects_config_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol); + unsigned long fe_id = kcontrol->private_value; + struct trans_loopback_pdata *pdata = (struct trans_loopback_pdata *) + snd_soc_component_get_drvdata(comp); + struct msm_transcode_audio_effects *audio_effects = NULL; + struct snd_compr_stream *cstream = NULL; + + pr_debug("%s: fe_id: %lu\n", __func__, fe_id); + if (fe_id >= MSM_FRONTEND_DAI_MAX) { + pr_err("%s Received out of bounds fe_id %lu\n", + __func__, fe_id); + return -EINVAL; + } + cstream = pdata->cstream[fe_id]; + audio_effects = pdata->audio_effects[fe_id]; + if (!cstream || !audio_effects) { + pr_err("%s: stream or effects inactive\n", __func__); + return -EINVAL; + } + + return 0; +} + +static int msm_transcode_audio_effects_config_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol); + unsigned long fe_id = kcontrol->private_value; + struct trans_loopback_pdata *pdata = (struct trans_loopback_pdata *) + snd_soc_component_get_drvdata(comp); + struct msm_transcode_audio_effects *audio_effects = NULL; + struct snd_compr_stream *cstream = NULL; + struct msm_transcode_loopback *prtd = NULL; + long *values = &(ucontrol->value.integer.value[0]); + int effects_module; + int ret = 0; + + pr_debug("%s: fe_id: %lu\n", __func__, fe_id); + if (fe_id >= MSM_FRONTEND_DAI_MAX) { + pr_err("%s Received out of bounds fe_id %lu\n", + __func__, fe_id); + ret = -EINVAL; + goto exit; + } + cstream = pdata->cstream[fe_id]; + audio_effects = pdata->audio_effects[fe_id]; + if (!cstream || !audio_effects) { + pr_err("%s: stream or effects inactive\n", __func__); + ret = -EINVAL; + goto exit; + } + prtd = cstream->runtime->private_data; + if (!prtd) { + pr_err("%s: cannot set audio effects\n", __func__); + ret = -EINVAL; + goto exit; + } + + effects_module = *values++; + switch (effects_module) { + case VIRTUALIZER_MODULE: + pr_debug("%s: VIRTUALIZER_MODULE\n", __func__); + if (msm_audio_effects_is_effmodule_supp_in_top(effects_module, + prtd->audio_client->topology)) + ret = msm_audio_effects_virtualizer_handler( + prtd->audio_client, + &(audio_effects->virtualizer), + values); + break; + case REVERB_MODULE: + pr_debug("%s: REVERB_MODULE\n", __func__); + if (msm_audio_effects_is_effmodule_supp_in_top(effects_module, + prtd->audio_client->topology)) + ret = msm_audio_effects_reverb_handler(prtd->audio_client, + &(audio_effects->reverb), + values); + break; + case BASS_BOOST_MODULE: + pr_debug("%s: BASS_BOOST_MODULE\n", __func__); + if (msm_audio_effects_is_effmodule_supp_in_top(effects_module, + prtd->audio_client->topology)) + ret = msm_audio_effects_bass_boost_handler(prtd->audio_client, + &(audio_effects->bass_boost), + values); + break; + case PBE_MODULE: + pr_debug("%s: PBE_MODULE\n", __func__); + if (msm_audio_effects_is_effmodule_supp_in_top(effects_module, + prtd->audio_client->topology)) + ret = msm_audio_effects_pbe_handler(prtd->audio_client, + &(audio_effects->pbe), + values); + break; + case EQ_MODULE: + pr_debug("%s: EQ_MODULE\n", __func__); + if (msm_audio_effects_is_effmodule_supp_in_top(effects_module, + prtd->audio_client->topology)) + ret = msm_audio_effects_popless_eq_handler(prtd->audio_client, + &(audio_effects->equalizer), + values); + break; + case SOFT_VOLUME_MODULE: + pr_debug("%s: SOFT_VOLUME_MODULE\n", __func__); + break; + case SOFT_VOLUME2_MODULE: + pr_debug("%s: SOFT_VOLUME2_MODULE\n", __func__); + if (msm_audio_effects_is_effmodule_supp_in_top(effects_module, + prtd->audio_client->topology)) + ret = msm_audio_effects_volume_handler_v2(prtd->audio_client, + &(audio_effects->volume), + values, SOFT_VOLUME_INSTANCE_2); + break; + default: + pr_err("%s Invalid effects config module\n", __func__); + ret = -EINVAL; + } + +exit: + return ret; +} + +static int msm_transcode_add_audio_effects_control(struct snd_soc_pcm_runtime *rtd) +{ + const char *mixer_ctl_name = "Audio Effects Config"; + const char *deviceNo = "NN"; + char *mixer_str = NULL; + int ctl_len = 0; + int ret = 0; + struct snd_kcontrol_new fe_audio_effects_config_control[1] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "?", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = msm_transcode_audio_effects_config_info, + .get = msm_transcode_audio_effects_config_get, + .put = msm_transcode_audio_effects_config_put, + .private_value = 0, + } + }; + + if (!rtd) { + pr_err("%s NULL rtd\n", __func__); + ret = -EINVAL; + goto done; + } + + pr_debug("%s: added new compr FE with name %s, id %d, cpu dai %s, device no %d\n", __func__, + rtd->dai_link->name, rtd->dai_link->id, + rtd->dai_link->cpu_dai_name, rtd->pcm->device); + + ctl_len = strlen(mixer_ctl_name) + 1 + strlen(deviceNo) + 1; + mixer_str = kzalloc(ctl_len, GFP_KERNEL); + + if (!mixer_str) { + ret = -ENOMEM; + goto done; + } + + snprintf(mixer_str, ctl_len, "%s %d", mixer_ctl_name, rtd->pcm->device); + + fe_audio_effects_config_control[0].name = mixer_str; + fe_audio_effects_config_control[0].private_value = rtd->dai_link->id; + ret = snd_soc_add_platform_controls(rtd->platform, + fe_audio_effects_config_control, + ARRAY_SIZE(fe_audio_effects_config_control)); + if (ret < 0) + pr_err("%s: failed to add ctl %s. err = %d\n", __func__, mixer_str, ret); + + kfree(mixer_str); +done: + return ret; +} + +static int msm_transcode_stream_cmd_control( + struct snd_soc_pcm_runtime *rtd) +{ + const char *mixer_ctl_name = DSP_STREAM_CMD; + const char *deviceNo = "NN"; + char *mixer_str = NULL; + int ctl_len = 0, ret = 0; + struct snd_kcontrol_new fe_loopback_stream_cmd_config_control[1] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "?", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = msm_adsp_stream_cmd_info, + .put = msm_transcode_stream_cmd_put, + .private_value = 0, + } + }; + + if (!rtd) { + pr_err("%s NULL rtd\n", __func__); + ret = -EINVAL; + goto done; + } + + ctl_len = strlen(mixer_ctl_name) + 1 + strlen(deviceNo) + 1; + mixer_str = kzalloc(ctl_len, GFP_KERNEL); + if (!mixer_str) { + ret = -ENOMEM; + goto done; + } + + snprintf(mixer_str, ctl_len, "%s %d", mixer_ctl_name, rtd->pcm->device); + fe_loopback_stream_cmd_config_control[0].name = mixer_str; + fe_loopback_stream_cmd_config_control[0].private_value = + rtd->dai_link->id; + pr_debug("%s: Registering new mixer ctl %s\n", __func__, mixer_str); + ret = snd_soc_add_platform_controls(rtd->platform, + fe_loopback_stream_cmd_config_control, + ARRAY_SIZE(fe_loopback_stream_cmd_config_control)); + if (ret < 0) + pr_err("%s: failed to add ctl %s. err = %d\n", + __func__, mixer_str, ret); + + kfree(mixer_str); +done: + return ret; +} + +static int msm_transcode_stream_callback_control( + struct snd_soc_pcm_runtime *rtd) +{ + const char *mixer_ctl_name = DSP_STREAM_CALLBACK; + const char *deviceNo = "NN"; + char *mixer_str = NULL; + int ctl_len = 0, ret = 0; + struct snd_kcontrol *kctl; + + struct snd_kcontrol_new fe_loopback_callback_config_control[1] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "?", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = msm_adsp_stream_callback_info, + .get = msm_adsp_stream_callback_get, + .private_value = 0, + } + }; + + if (!rtd) { + pr_err("%s: rtd is NULL\n", __func__); + ret = -EINVAL; + goto done; + } + + ctl_len = strlen(mixer_ctl_name) + 1 + strlen(deviceNo) + 1; + mixer_str = kzalloc(ctl_len, GFP_KERNEL); + if (!mixer_str) { + ret = -ENOMEM; + goto done; + } + + snprintf(mixer_str, ctl_len, "%s %d", mixer_ctl_name, rtd->pcm->device); + fe_loopback_callback_config_control[0].name = mixer_str; + fe_loopback_callback_config_control[0].private_value = + rtd->dai_link->id; + pr_debug("%s: Registering new mixer ctl %s\n", __func__, mixer_str); + ret = snd_soc_add_platform_controls(rtd->platform, + fe_loopback_callback_config_control, + ARRAY_SIZE(fe_loopback_callback_config_control)); + if (ret < 0) { + pr_err("%s: failed to add ctl %s. err = %d\n", + __func__, mixer_str, ret); + ret = -EINVAL; + goto free_mixer_str; + } + + kctl = snd_soc_card_get_kcontrol(rtd->card, mixer_str); + if (!kctl) { + pr_err("%s: failed to get kctl %s.\n", __func__, mixer_str); + ret = -EINVAL; + goto free_mixer_str; + } + + kctl->private_data = NULL; +free_mixer_str: + kfree(mixer_str); +done: + return ret; +} + +static int msm_transcode_add_ion_fd_cmd_control(struct snd_soc_pcm_runtime *rtd) +{ + const char *mixer_ctl_name = "Playback ION FD"; + const char *deviceNo = "NN"; + char *mixer_str = NULL; + int ctl_len = 0, ret = 0; + struct snd_kcontrol_new fe_ion_fd_config_control[1] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "?", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = msm_adsp_stream_cmd_info, + .put = msm_transcode_ion_fd_map_put, + .private_value = 0, + } + }; + + if (!rtd) { + pr_err("%s NULL rtd\n", __func__); + ret = -EINVAL; + goto done; + } + + ctl_len = strlen(mixer_ctl_name) + 1 + strlen(deviceNo) + 1; + mixer_str = kzalloc(ctl_len, GFP_KERNEL); + if (!mixer_str) { + ret = -ENOMEM; + goto done; + } + + snprintf(mixer_str, ctl_len, "%s %d", mixer_ctl_name, rtd->pcm->device); + fe_ion_fd_config_control[0].name = mixer_str; + fe_ion_fd_config_control[0].private_value = rtd->dai_link->id; + pr_debug("%s: Registering new mixer ctl %s\n", __func__, mixer_str); + ret = snd_soc_add_platform_controls(rtd->platform, + fe_ion_fd_config_control, + ARRAY_SIZE(fe_ion_fd_config_control)); + if (ret < 0) + pr_err("%s: failed to add ctl %s\n", __func__, mixer_str); + + kfree(mixer_str); +done: + return ret; +} + +static int msm_transcode_add_event_ack_cmd_control( + struct snd_soc_pcm_runtime *rtd) +{ + const char *mixer_ctl_name = "Playback Event Ack"; + const char *deviceNo = "NN"; + char *mixer_str = NULL; + int ctl_len = 0, ret = 0; + struct snd_kcontrol_new fe_event_ack_config_control[1] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "?", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = msm_adsp_stream_cmd_info, + .put = msm_transcode_rtic_event_ack_put, + .private_value = 0, + } + }; + + if (!rtd) { + pr_err("%s NULL rtd\n", __func__); + ret = -EINVAL; + goto done; + } + + ctl_len = strlen(mixer_ctl_name) + 1 + strlen(deviceNo) + 1; + mixer_str = kzalloc(ctl_len, GFP_KERNEL); + if (!mixer_str) { + ret = -ENOMEM; + goto done; + } + + snprintf(mixer_str, ctl_len, "%s %d", mixer_ctl_name, rtd->pcm->device); + fe_event_ack_config_control[0].name = mixer_str; + fe_event_ack_config_control[0].private_value = rtd->dai_link->id; + pr_debug("%s: Registering new mixer ctl %s\n", __func__, mixer_str); + ret = snd_soc_add_platform_controls(rtd->platform, + fe_event_ack_config_control, + ARRAY_SIZE(fe_event_ack_config_control)); + if (ret < 0) + pr_err("%s: failed to add ctl %s\n", __func__, mixer_str); + + kfree(mixer_str); +done: + return ret; +} + +static int msm_transcode_app_type_cfg_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 5; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 0xFFFFFFFF; + return 0; +} + +static int msm_transcode_add_app_type_cfg_control( + struct snd_soc_pcm_runtime *rtd) +{ + char mixer_str[32]; + struct snd_kcontrol_new fe_app_type_cfg_control[1] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = msm_transcode_app_type_cfg_info, + .put = msm_transcode_playback_app_type_cfg_put, + .get = msm_transcode_playback_app_type_cfg_get, + .private_value = 0, + } + }; + + if (!rtd) { + pr_err("%s NULL rtd\n", __func__); + return -EINVAL; + } + + if (rtd->compr->direction == SND_COMPRESS_PLAYBACK) { + snprintf(mixer_str, sizeof(mixer_str), + "Audio Stream %d App Type Cfg", + rtd->pcm->device); + + fe_app_type_cfg_control[0].name = mixer_str; + fe_app_type_cfg_control[0].private_value = rtd->dai_link->id; + + fe_app_type_cfg_control[0].put = + msm_transcode_playback_app_type_cfg_put; + fe_app_type_cfg_control[0].get = + msm_transcode_playback_app_type_cfg_get; + + pr_debug("Registering new mixer ctl %s", mixer_str); + snd_soc_add_platform_controls(rtd->platform, + fe_app_type_cfg_control, + ARRAY_SIZE(fe_app_type_cfg_control)); + } + + return 0; +} +static int msm_transcode_volume_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = TRANSCODE_LR_VOL_MAX_DB; + return 0; +} + +static int msm_transcode_add_volume_control(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_kcontrol_new fe_volume_control[1] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Transcode Loopback Rx Volume", + .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | + SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = msm_transcode_volume_info, + .get = msm_transcode_volume_get, + .put = msm_transcode_volume_put, + .private_value = 0, + } + }; + + if (!rtd) { + pr_err("%s NULL rtd\n", __func__); + return -EINVAL; + } + if (rtd->compr->direction == SND_COMPRESS_PLAYBACK) { + fe_volume_control[0].private_value = rtd->dai_link->id; + pr_debug("Registering new mixer ctl %s", + fe_volume_control[0].name); + snd_soc_add_platform_controls(rtd->platform, fe_volume_control, + ARRAY_SIZE(fe_volume_control)); + } + return 0; +} + +static int msm_transcode_loopback_new(struct snd_soc_pcm_runtime *rtd) +{ + int rc; + + rc = msm_transcode_add_audio_effects_control(rtd); + if (rc) + pr_err("%s: Could not add Compr Audio Effects Control\n", + __func__); + + rc = msm_transcode_stream_cmd_control(rtd); + if (rc) + pr_err("%s: ADSP Stream Cmd Control open failed\n", __func__); + + rc = msm_transcode_stream_callback_control(rtd); + if (rc) + pr_err("%s: ADSP Stream callback Control open failed\n", + __func__); + + rc = msm_transcode_add_ion_fd_cmd_control(rtd); + if (rc) + pr_err("%s: Could not add transcode ion fd Control\n", + __func__); + + rc = msm_transcode_add_event_ack_cmd_control(rtd); + if (rc) + pr_err("%s: Could not add transcode event ack Control\n", + __func__); + + rc = msm_transcode_add_app_type_cfg_control(rtd); + if (rc) + pr_err("%s: Could not add Compr App Type Cfg Control\n", + __func__); + + rc = msm_transcode_add_volume_control(rtd); + if (rc) + pr_err("%s: Could not add transcode volume Control\n", + __func__); + + return 0; +} + +static struct snd_compr_ops msm_transcode_loopback_ops = { + .open = msm_transcode_loopback_open, + .free = msm_transcode_loopback_free, + .trigger = msm_transcode_loopback_trigger, + .set_params = msm_transcode_loopback_set_params, + .get_caps = msm_transcode_loopback_get_caps, + .set_metadata = msm_transcode_loopback_set_metadata, +}; + + +static int msm_transcode_loopback_probe(struct snd_soc_platform *platform) +{ + struct trans_loopback_pdata *pdata = NULL; + int i; + + pr_debug("%s\n", __func__); + pdata = (struct trans_loopback_pdata *) + kzalloc(sizeof(struct trans_loopback_pdata), + GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + pdata->perf_mode = LOW_LATENCY_PCM_MODE; + for (i = 0; i < MSM_FRONTEND_DAI_MAX; i++) + pdata->audio_effects[i] = NULL; + + snd_soc_platform_set_drvdata(platform, pdata); + return 0; +} + +static int msm_transcode_loopback_remove(struct snd_soc_platform *platform) +{ + struct trans_loopback_pdata *pdata = NULL; + + pdata = (struct trans_loopback_pdata *) + snd_soc_platform_get_drvdata(platform); + kfree(pdata); + return 0; +} + +static struct snd_soc_platform_driver msm_soc_platform = { + .probe = msm_transcode_loopback_probe, + .compr_ops = &msm_transcode_loopback_ops, + .pcm_new = msm_transcode_loopback_new, + .remove = msm_transcode_loopback_remove, +}; + +static int msm_transcode_dev_probe(struct platform_device *pdev) +{ + + pr_debug("%s: dev name %s\n", __func__, dev_name(&pdev->dev)); + if (pdev->dev.of_node) + dev_set_name(&pdev->dev, "%s", "msm-transcode-loopback"); + + return snd_soc_register_platform(&pdev->dev, + &msm_soc_platform); +} + +static int msm_transcode_remove(struct platform_device *pdev) +{ + snd_soc_unregister_platform(&pdev->dev); + return 0; +} + +static const struct of_device_id msm_transcode_loopback_dt_match[] = { + {.compatible = "qcom,msm-transcode-loopback"}, + {} +}; +MODULE_DEVICE_TABLE(of, msm_transcode_loopback_dt_match); + +static struct platform_driver msm_transcode_loopback_driver = { + .driver = { + .name = "msm-transcode-loopback", + .owner = THIS_MODULE, + .of_match_table = msm_transcode_loopback_dt_match, + }, + .probe = msm_transcode_dev_probe, + .remove = msm_transcode_remove, +}; + +int __init msm_transcode_loopback_init(void) +{ + memset(&transcode_info, 0, sizeof(struct msm_transcode_loopback)); + mutex_init(&transcode_info.lock); + return platform_driver_register(&msm_transcode_loopback_driver); +} + +void msm_transcode_loopback_exit(void) +{ + mutex_destroy(&transcode_info.lock); + platform_driver_unregister(&msm_transcode_loopback_driver); +} + +MODULE_DESCRIPTION("Transcode loopback platform driver"); +MODULE_LICENSE("GPL v2"); diff --git a/audio-kernel/asoc/msm8998.c b/audio-kernel/asoc/msm8998.c new file mode 100644 index 000000000..1faa1930c --- /dev/null +++ b/audio-kernel/asoc/msm8998.c @@ -0,0 +1,7483 @@ +/* + * Copyright (c) 2016-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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "msm-pcm-routing-v2.h" +#include "codecs/msm-cdc-pinctrl.h" +#include "codecs/wcd9335.h" +#include "codecs/wcd934x/wcd934x.h" +#include "codecs/wcd934x/wcd934x-mbhc.h" +#include "codecs/wsa881x.h" + +#define DRV_NAME "msm8998-asoc-snd" + +#define __CHIPSET__ "MSM8998 " +#define MSM_DAILINK_NAME(name) (__CHIPSET__#name) + +#define SAMPLING_RATE_8KHZ 8000 +#define SAMPLING_RATE_11P025KHZ 11025 +#define SAMPLING_RATE_16KHZ 16000 +#define SAMPLING_RATE_22P05KHZ 22050 +#define SAMPLING_RATE_32KHZ 32000 +#define SAMPLING_RATE_44P1KHZ 44100 +#define SAMPLING_RATE_48KHZ 48000 +#define SAMPLING_RATE_88P2KHZ 88200 +#define SAMPLING_RATE_96KHZ 96000 +#define SAMPLING_RATE_176P4KHZ 176400 +#define SAMPLING_RATE_192KHZ 192000 +#define SAMPLING_RATE_352P8KHZ 352800 +#define SAMPLING_RATE_384KHZ 384000 + +#define WCD9XXX_MBHC_DEF_BUTTONS 8 +#define WCD9XXX_MBHC_DEF_RLOADS 5 +#define CODEC_EXT_CLK_RATE 9600000 +#define ADSP_STATE_READY_TIMEOUT_MS 3000 +#define DEV_NAME_STR_LEN 32 + +#define WSA8810_NAME_1 "wsa881x.20170211" +#define WSA8810_NAME_2 "wsa881x.20170212" + +#define WCN_CDC_SLIM_RX_CH_MAX 2 +#define WCN_CDC_SLIM_TX_CH_MAX 3 + +#define TDM_CHANNEL_MAX 8 +#define TDM_SLOT_OFFSET_MAX 8 + +#define MSM_HIFI_ON 1 + +enum { + SLIM_RX_0 = 0, + SLIM_RX_1, + SLIM_RX_2, + SLIM_RX_3, + SLIM_RX_4, + SLIM_RX_5, + SLIM_RX_6, + SLIM_RX_7, + SLIM_RX_MAX, +}; + +enum { + SLIM_TX_0 = 0, + SLIM_TX_1, + SLIM_TX_2, + SLIM_TX_3, + SLIM_TX_4, + SLIM_TX_5, + SLIM_TX_6, + SLIM_TX_7, + SLIM_TX_8, + SLIM_TX_MAX, +}; + +enum { + PRIM_MI2S = 0, + SEC_MI2S, + TERT_MI2S, + QUAT_MI2S, + MI2S_MAX, +}; + +enum { + PRIM_AUX_PCM = 0, + SEC_AUX_PCM, + TERT_AUX_PCM, + QUAT_AUX_PCM, + AUX_PCM_MAX, +}; + +enum { + PCM_I2S_SEL_PRIM = 0, + PCM_I2S_SEL_SEC, + PCM_I2S_SEL_TERT, + PCM_I2S_SEL_QUAT, + PCM_I2S_SEL_MAX, +}; + +struct mi2s_aux_pcm_common_conf { + struct mutex lock; + void *pcm_i2s_sel_vt_addr; +}; + +struct mi2s_conf { + struct mutex lock; + u32 ref_cnt; + u32 msm_is_mi2s_master; +}; + +struct auxpcm_conf { + struct mutex lock; + u32 ref_cnt; +}; + +struct dev_config { + u32 sample_rate; + u32 bit_format; + u32 channels; +}; + +enum { + HDMI_RX_IDX = 0, + DP_RX_IDX, + EXT_DISP_RX_IDX_MAX, +}; + +struct msm_wsa881x_dev_info { + struct device_node *of_node; + u32 index; +}; + +enum pinctrl_pin_state { + STATE_DISABLE = 0, /* All pins are in sleep state */ + STATE_MI2S_ACTIVE, /* IS2 = active, TDM = sleep */ + STATE_TDM_ACTIVE, /* IS2 = sleep, TDM = active */ +}; + +struct msm_pinctrl_info { + struct pinctrl *pinctrl; + struct pinctrl_state *mi2s_disable; + struct pinctrl_state *tdm_disable; + struct pinctrl_state *mi2s_active; + struct pinctrl_state *tdm_active; + enum pinctrl_pin_state curr_state; +}; + +struct msm_asoc_mach_data { + u32 mclk_freq; + int us_euro_gpio; /* used by gpio driver API */ + struct device_node *us_euro_gpio_p; /* used by pinctrl API */ + struct device_node *hph_en1_gpio_p; /* used by pinctrl API */ + struct device_node *hph_en0_gpio_p; /* used by pinctrl API */ + struct snd_info_entry *codec_root; + struct msm_pinctrl_info pinctrl_info; +}; + +struct msm_asoc_wcd93xx_codec { + void* (*get_afe_config_fn)(struct snd_soc_codec *codec, + enum afe_config_type config_type); + void (*mbhc_hs_detect_exit)(struct snd_soc_codec *codec); +}; + +static const char *const pin_states[] = {"sleep", "i2s-active", + "tdm-active"}; + +enum { + TDM_0 = 0, + TDM_1, + TDM_2, + TDM_3, + TDM_4, + TDM_5, + TDM_6, + TDM_7, + TDM_PORT_MAX, +}; + +enum { + TDM_PRI = 0, + TDM_SEC, + TDM_TERT, + TDM_QUAT, + TDM_INTERFACE_MAX, +}; + +struct tdm_port { + u32 mode; + u32 channel; +}; + +/* TDM default config */ +static struct dev_config tdm_rx_cfg[TDM_INTERFACE_MAX][TDM_PORT_MAX] = { + { /* PRI TDM */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_0 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_1 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_2 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_3 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_4 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_5 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_6 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_7 */ + }, + { /* SEC TDM */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_0 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_1 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_2 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_3 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_4 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_5 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_6 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_7 */ + }, + { /* TERT TDM */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_0 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_1 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_2 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_3 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_4 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_5 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_6 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_7 */ + }, + { /* QUAT TDM */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_0 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_1 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_2 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_3 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_4 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_5 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_6 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_7 */ + } +}; + +/* TDM default config */ +static struct dev_config tdm_tx_cfg[TDM_INTERFACE_MAX][TDM_PORT_MAX] = { + { /* PRI TDM */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_0 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_1 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_2 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_3 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_4 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_5 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_6 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_7 */ + }, + { /* SEC TDM */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_0 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_1 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_2 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_3 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_4 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_5 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_6 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_7 */ + }, + { /* TERT TDM */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_0 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_1 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_2 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_3 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_4 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_5 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_6 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_7 */ + }, + { /* QUAT TDM */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_0 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_1 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_2 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_3 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_4 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_5 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_6 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_7 */ + } +}; + +/*TDM default offset currently only supporting TDM_RX_0 and TDM_TX_0 */ +static unsigned int tdm_slot_offset[TDM_PORT_MAX][TDM_SLOT_OFFSET_MAX] = { + {0, 4, 8, 12, 16, 20, 24, 28},/* TX_0 | RX_0 */ + {AFE_SLOT_MAPPING_OFFSET_INVALID},/* TX_1 | RX_1 */ + {AFE_SLOT_MAPPING_OFFSET_INVALID},/* TX_2 | RX_2 */ + {AFE_SLOT_MAPPING_OFFSET_INVALID},/* TX_3 | RX_3 */ + {AFE_SLOT_MAPPING_OFFSET_INVALID},/* TX_4 | RX_4 */ + {AFE_SLOT_MAPPING_OFFSET_INVALID},/* TX_5 | RX_5 */ + {AFE_SLOT_MAPPING_OFFSET_INVALID},/* TX_6 | RX_6 */ + {AFE_SLOT_MAPPING_OFFSET_INVALID},/* TX_7 | RX_7 */ +}; + +/* Default configuration of slimbus channels */ +static struct dev_config slim_rx_cfg[] = { + [SLIM_RX_0] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_RX_1] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_RX_2] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_RX_3] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_RX_4] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_RX_5] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_RX_6] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_RX_7] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, +}; + +static struct dev_config slim_tx_cfg[] = { + [SLIM_TX_0] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_TX_1] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_TX_2] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_TX_3] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_TX_4] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_TX_5] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_TX_6] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_TX_7] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_TX_8] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, +}; + + +/* Default configuration of external display BE */ +static struct dev_config ext_disp_rx_cfg[] = { + [HDMI_RX_IDX] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, + [DP_RX_IDX] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, +}; + +static struct dev_config usb_rx_cfg = { + .sample_rate = SAMPLING_RATE_48KHZ, + .bit_format = SNDRV_PCM_FORMAT_S16_LE, + .channels = 2, +}; + +static struct dev_config usb_tx_cfg = { + .sample_rate = SAMPLING_RATE_48KHZ, + .bit_format = SNDRV_PCM_FORMAT_S16_LE, + .channels = 1, +}; + +static struct dev_config proxy_rx_cfg = { + .sample_rate = SAMPLING_RATE_48KHZ, + .bit_format = SNDRV_PCM_FORMAT_S16_LE, + .channels = 2, +}; + +/* Default configuration of MI2S channels */ +static struct dev_config mi2s_rx_cfg[] = { + [PRIM_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, + [SEC_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, + [TERT_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, + [QUAT_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, +}; + +static struct dev_config mi2s_tx_cfg[] = { + [PRIM_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SEC_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [TERT_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [QUAT_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, +}; + +static struct dev_config aux_pcm_rx_cfg[] = { + [PRIM_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SEC_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [TERT_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [QUAT_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, +}; + +static struct dev_config aux_pcm_tx_cfg[] = { + [PRIM_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SEC_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [TERT_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [QUAT_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, +}; + +static int msm_vi_feed_tx_ch = 2; +static const char *const slim_rx_ch_text[] = {"One", "Two"}; +static const char *const slim_tx_ch_text[] = {"One", "Two", "Three", "Four", + "Five", "Six", "Seven", + "Eight"}; +static const char *const vi_feed_ch_text[] = {"One", "Two"}; +static char const *bit_format_text[] = {"S16_LE", "S24_LE", "S24_3LE", + "S32_LE"}; +static char const *ext_disp_bit_format_text[] = {"S16_LE", "S24_LE"}; +static char const *slim_sample_rate_text[] = {"KHZ_8", "KHZ_16", + "KHZ_32", "KHZ_44P1", "KHZ_48", + "KHZ_88P2", "KHZ_96", "KHZ_176P4", + "KHZ_192", "KHZ_352P8", "KHZ_384"}; +static char const *bt_sample_rate_text[] = {"KHZ_8", "KHZ_16", "KHZ_48"}; +static const char *const usb_ch_text[] = {"One", "Two", "Three", "Four", + "Five", "Six", "Seven", + "Eight"}; +static char const *ch_text[] = {"Two", "Three", "Four", "Five", + "Six", "Seven", "Eight"}; +static char const *usb_sample_rate_text[] = {"KHZ_8", "KHZ_11P025", + "KHZ_16", "KHZ_22P05", + "KHZ_32", "KHZ_44P1", "KHZ_48", + "KHZ_88P2", "KHZ_96", "KHZ_176P4", + "KHZ_192", "KHZ_352P8", "KHZ_384"}; +static char const *ext_disp_sample_rate_text[] = {"KHZ_48", "KHZ_96", + "KHZ_192", "KHZ_32", "KHZ_44P1", + "KHZ_88P2", "KHZ_176P4"}; +static char const *tdm_ch_text[] = {"One", "Two", "Three", "Four", + "Five", "Six", "Seven", "Eight"}; +static char const *tdm_bit_format_text[] = {"S16_LE", "S24_LE", "S32_LE"}; +static char const *tdm_sample_rate_text[] = {"KHZ_8", "KHZ_16", "KHZ_32", + "KHZ_44P1", "KHZ_48", "KHZ_96", + "KHZ_192", "KHZ_352P8", "KHZ_384"}; +static const char *const auxpcm_rate_text[] = {"KHZ_8", "KHZ_16"}; +static char const *mi2s_rate_text[] = {"KHZ_8", "KHZ_16", + "KHZ_32", "KHZ_44P1", "KHZ_48", + "KHZ_96", "KHZ_192"}; +static const char *const mi2s_ch_text[] = {"One", "Two", "Three", "Four", + "Five", "Six", "Seven", + "Eight"}; +static const char *const hifi_text[] = {"Off", "On"}; + +static SOC_ENUM_SINGLE_EXT_DECL(slim_0_rx_chs, slim_rx_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_2_rx_chs, slim_rx_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_0_tx_chs, slim_tx_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_1_tx_chs, slim_tx_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_5_rx_chs, slim_rx_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_6_rx_chs, slim_rx_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(usb_rx_chs, usb_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(usb_tx_chs, usb_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(vi_feed_tx_chs, vi_feed_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(ext_disp_rx_chs, ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(proxy_rx_chs, ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_0_rx_format, bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_5_rx_format, bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_6_rx_format, bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_0_tx_format, bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(usb_rx_format, bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(usb_tx_format, bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(ext_disp_rx_format, ext_disp_bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_0_rx_sample_rate, slim_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_2_rx_sample_rate, slim_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_0_tx_sample_rate, slim_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_5_rx_sample_rate, slim_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_6_rx_sample_rate, slim_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(bt_sample_rate, bt_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(usb_rx_sample_rate, usb_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(usb_tx_sample_rate, usb_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(ext_disp_rx_sample_rate, + ext_disp_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(tdm_tx_chs, tdm_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(tdm_tx_format, tdm_bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(tdm_tx_sample_rate, tdm_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(tdm_rx_chs, tdm_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(tdm_rx_format, tdm_bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(tdm_rx_sample_rate, tdm_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(prim_aux_pcm_rx_sample_rate, auxpcm_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(sec_aux_pcm_rx_sample_rate, auxpcm_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(tert_aux_pcm_rx_sample_rate, auxpcm_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(quat_aux_pcm_rx_sample_rate, auxpcm_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(prim_aux_pcm_tx_sample_rate, auxpcm_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(sec_aux_pcm_tx_sample_rate, auxpcm_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(tert_aux_pcm_tx_sample_rate, auxpcm_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(quat_aux_pcm_tx_sample_rate, auxpcm_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(prim_mi2s_rx_sample_rate, mi2s_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(sec_mi2s_rx_sample_rate, mi2s_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(tert_mi2s_rx_sample_rate, mi2s_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(quat_mi2s_rx_sample_rate, mi2s_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(prim_mi2s_tx_sample_rate, mi2s_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(sec_mi2s_tx_sample_rate, mi2s_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(tert_mi2s_tx_sample_rate, mi2s_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(quat_mi2s_tx_sample_rate, mi2s_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(prim_mi2s_rx_chs, mi2s_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(prim_mi2s_tx_chs, mi2s_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(sec_mi2s_rx_chs, mi2s_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(sec_mi2s_tx_chs, mi2s_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(tert_mi2s_rx_chs, mi2s_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(tert_mi2s_tx_chs, mi2s_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(quat_mi2s_rx_chs, mi2s_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(quat_mi2s_tx_chs, mi2s_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(mi2s_rx_format, bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(mi2s_tx_format, bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(hifi_function, hifi_text); + +static struct platform_device *spdev; +static int msm_hifi_control; + +static bool is_initial_boot; +static bool codec_reg_done; +static struct snd_soc_aux_dev *msm_aux_dev; +static struct snd_soc_codec_conf *msm_codec_conf; +static struct msm_asoc_wcd93xx_codec msm_codec_fn; + +static void *def_tasha_mbhc_cal(void); +static void *def_tavil_mbhc_cal(void); +static int msm_snd_enable_codec_ext_clk(struct snd_soc_codec *codec, + int enable, bool dapm); +static int msm_wsa881x_init(struct snd_soc_component *component); + +/* + * Need to report LINEIN + * if R/L channel impedance is larger than 5K ohm + */ +static struct wcd_mbhc_config wcd_mbhc_cfg = { + .read_fw_bin = false, + .calibration = NULL, + .detect_extn_cable = true, + .mono_stero_detection = false, + .swap_gnd_mic = NULL, + .hs_ext_micbias = true, + .key_code[0] = KEY_MEDIA, + .key_code[1] = KEY_VOICECOMMAND, + .key_code[2] = KEY_VOLUMEUP, + .key_code[3] = KEY_VOLUMEDOWN, + .key_code[4] = 0, + .key_code[5] = 0, + .key_code[6] = 0, + .key_code[7] = 0, + .linein_th = 5000, + .moisture_en = true, + .mbhc_micbias = MIC_BIAS_2, + .anc_micbias = MIC_BIAS_2, + .enable_anc_mic_detect = false, +}; + +static struct snd_soc_dapm_route wcd_audio_paths_tasha[] = { + {"MIC BIAS1", NULL, "MCLK TX"}, + {"MIC BIAS2", NULL, "MCLK TX"}, + {"MIC BIAS3", NULL, "MCLK TX"}, + {"MIC BIAS4", NULL, "MCLK TX"}, +}; + +static struct snd_soc_dapm_route wcd_audio_paths[] = { + {"MIC BIAS1", NULL, "MCLK"}, + {"MIC BIAS2", NULL, "MCLK"}, + {"MIC BIAS3", NULL, "MCLK"}, + {"MIC BIAS4", NULL, "MCLK"}, +}; + +static struct afe_clk_set mi2s_clk[MI2S_MAX] = { + { + AFE_API_VERSION_I2S_CONFIG, + Q6AFE_LPASS_CLK_ID_PRI_MI2S_IBIT, + Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ, + Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO, + Q6AFE_LPASS_CLK_ROOT_DEFAULT, + 0, + }, + { + AFE_API_VERSION_I2S_CONFIG, + Q6AFE_LPASS_CLK_ID_SEC_MI2S_IBIT, + Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ, + Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO, + Q6AFE_LPASS_CLK_ROOT_DEFAULT, + 0, + }, + { + AFE_API_VERSION_I2S_CONFIG, + Q6AFE_LPASS_CLK_ID_TER_MI2S_IBIT, + Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ, + Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO, + Q6AFE_LPASS_CLK_ROOT_DEFAULT, + 0, + }, + { + AFE_API_VERSION_I2S_CONFIG, + Q6AFE_LPASS_CLK_ID_QUAD_MI2S_IBIT, + Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ, + Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO, + Q6AFE_LPASS_CLK_ROOT_DEFAULT, + 0, + } +}; + +static struct mi2s_aux_pcm_common_conf mi2s_auxpcm_conf[PCM_I2S_SEL_MAX]; +static struct mi2s_conf mi2s_intf_conf[MI2S_MAX]; +static struct auxpcm_conf auxpcm_intf_conf[AUX_PCM_MAX]; + +static int slim_get_sample_rate_val(int sample_rate) +{ + int sample_rate_val = 0; + + switch (sample_rate) { + case SAMPLING_RATE_8KHZ: + sample_rate_val = 0; + break; + case SAMPLING_RATE_16KHZ: + sample_rate_val = 1; + break; + case SAMPLING_RATE_32KHZ: + sample_rate_val = 2; + break; + case SAMPLING_RATE_44P1KHZ: + sample_rate_val = 3; + break; + case SAMPLING_RATE_48KHZ: + sample_rate_val = 4; + break; + case SAMPLING_RATE_88P2KHZ: + sample_rate_val = 5; + break; + case SAMPLING_RATE_96KHZ: + sample_rate_val = 6; + break; + case SAMPLING_RATE_176P4KHZ: + sample_rate_val = 7; + break; + case SAMPLING_RATE_192KHZ: + sample_rate_val = 8; + break; + case SAMPLING_RATE_352P8KHZ: + sample_rate_val = 9; + break; + case SAMPLING_RATE_384KHZ: + sample_rate_val = 10; + break; + default: + sample_rate_val = 4; + break; + } + return sample_rate_val; +} + +static int slim_get_sample_rate(int value) +{ + int sample_rate = 0; + + switch (value) { + case 0: + sample_rate = SAMPLING_RATE_8KHZ; + break; + case 1: + sample_rate = SAMPLING_RATE_16KHZ; + break; + case 2: + sample_rate = SAMPLING_RATE_32KHZ; + break; + case 3: + sample_rate = SAMPLING_RATE_44P1KHZ; + break; + case 4: + sample_rate = SAMPLING_RATE_48KHZ; + break; + case 5: + sample_rate = SAMPLING_RATE_88P2KHZ; + break; + case 6: + sample_rate = SAMPLING_RATE_96KHZ; + break; + case 7: + sample_rate = SAMPLING_RATE_176P4KHZ; + break; + case 8: + sample_rate = SAMPLING_RATE_192KHZ; + break; + case 9: + sample_rate = SAMPLING_RATE_352P8KHZ; + break; + case 10: + sample_rate = SAMPLING_RATE_384KHZ; + break; + default: + sample_rate = SAMPLING_RATE_48KHZ; + break; + } + return sample_rate; +} + +static int slim_get_bit_format_val(int bit_format) +{ + int val = 0; + + switch (bit_format) { + case SNDRV_PCM_FORMAT_S32_LE: + val = 3; + break; + case SNDRV_PCM_FORMAT_S24_3LE: + val = 2; + break; + case SNDRV_PCM_FORMAT_S24_LE: + val = 1; + break; + case SNDRV_PCM_FORMAT_S16_LE: + default: + val = 0; + break; + } + return val; +} + +static int slim_get_bit_format(int val) +{ + int bit_fmt = SNDRV_PCM_FORMAT_S16_LE; + + switch (val) { + case 0: + bit_fmt = SNDRV_PCM_FORMAT_S16_LE; + break; + case 1: + bit_fmt = SNDRV_PCM_FORMAT_S24_LE; + break; + case 2: + bit_fmt = SNDRV_PCM_FORMAT_S24_3LE; + break; + case 3: + bit_fmt = SNDRV_PCM_FORMAT_S32_LE; + break; + default: + bit_fmt = SNDRV_PCM_FORMAT_S16_LE; + break; + } + return bit_fmt; +} + +static int slim_get_port_idx(struct snd_kcontrol *kcontrol) +{ + int port_id = 0; + + if (strnstr(kcontrol->id.name, "SLIM_0_RX", sizeof("SLIM_0_RX"))) + port_id = SLIM_RX_0; + else if (strnstr(kcontrol->id.name, "SLIM_2_RX", sizeof("SLIM_2_RX"))) + port_id = SLIM_RX_2; + else if (strnstr(kcontrol->id.name, "SLIM_5_RX", sizeof("SLIM_5_RX"))) + port_id = SLIM_RX_5; + else if (strnstr(kcontrol->id.name, "SLIM_6_RX", sizeof("SLIM_6_RX"))) + port_id = SLIM_RX_6; + else if (strnstr(kcontrol->id.name, "SLIM_0_TX", sizeof("SLIM_0_TX"))) + port_id = SLIM_TX_0; + else if (strnstr(kcontrol->id.name, "SLIM_1_TX", sizeof("SLIM_1_TX"))) + port_id = SLIM_TX_1; + else { + pr_err("%s: unsupported channel: %s", + __func__, kcontrol->id.name); + return -EINVAL; + } + + return port_id; +} + +static int slim_rx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = slim_get_port_idx(kcontrol); + + if (ch_num < 0) + return ch_num; + + ucontrol->value.enumerated.item[0] = + slim_get_sample_rate_val(slim_rx_cfg[ch_num].sample_rate); + + pr_debug("%s: slim[%d]_rx_sample_rate = %d, item = %d\n", __func__, + ch_num, slim_rx_cfg[ch_num].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int slim_rx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = slim_get_port_idx(kcontrol); + + if (ch_num < 0) + return ch_num; + + slim_rx_cfg[ch_num].sample_rate = + slim_get_sample_rate(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: slim[%d]_rx_sample_rate = %d, item = %d\n", __func__, + ch_num, slim_rx_cfg[ch_num].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int slim_tx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = slim_get_port_idx(kcontrol); + + if (ch_num < 0) + return ch_num; + + ucontrol->value.enumerated.item[0] = + slim_get_sample_rate_val(slim_tx_cfg[ch_num].sample_rate); + + pr_debug("%s: slim[%d]_tx_sample_rate = %d, item = %d\n", __func__, + ch_num, slim_tx_cfg[ch_num].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int slim_tx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int sample_rate = 0; + int ch_num = slim_get_port_idx(kcontrol); + + if (ch_num < 0) + return ch_num; + + sample_rate = slim_get_sample_rate(ucontrol->value.enumerated.item[0]); + if (sample_rate == SAMPLING_RATE_44P1KHZ) { + pr_err("%s: Unsupported sample rate %d: for Tx path\n", + __func__, sample_rate); + return -EINVAL; + } + slim_tx_cfg[ch_num].sample_rate = sample_rate; + + pr_debug("%s: slim[%d]_tx_sample_rate = %d, value = %d\n", __func__, + ch_num, slim_tx_cfg[ch_num].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int slim_rx_bit_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = slim_get_port_idx(kcontrol); + + if (ch_num < 0) + return ch_num; + + ucontrol->value.enumerated.item[0] = + slim_get_bit_format_val(slim_rx_cfg[ch_num].bit_format); + + pr_debug("%s: slim[%d]_rx_bit_format = %d, ucontrol value = %d\n", + __func__, ch_num, slim_rx_cfg[ch_num].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int slim_rx_bit_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = slim_get_port_idx(kcontrol); + + if (ch_num < 0) + return ch_num; + + slim_rx_cfg[ch_num].bit_format = + slim_get_bit_format(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: slim[%d]_rx_bit_format = %d, ucontrol value = %d\n", + __func__, ch_num, slim_rx_cfg[ch_num].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int slim_tx_bit_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = slim_get_port_idx(kcontrol); + + if (ch_num < 0) + return ch_num; + + ucontrol->value.enumerated.item[0] = + slim_get_bit_format_val(slim_tx_cfg[ch_num].bit_format); + + pr_debug("%s: slim[%d]_tx_bit_format = %d, ucontrol value = %d\n", + __func__, ch_num, slim_tx_cfg[ch_num].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int slim_tx_bit_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = slim_get_port_idx(kcontrol); + + if (ch_num < 0) + return ch_num; + + slim_tx_cfg[ch_num].bit_format = + slim_get_bit_format(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: slim[%d]_tx_bit_format = %d, ucontrol value = %d\n", + __func__, ch_num, slim_tx_cfg[ch_num].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int msm_slim_rx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = slim_get_port_idx(kcontrol); + + if (ch_num < 0) + return ch_num; + + pr_debug("%s: msm_slim_[%d]_rx_ch = %d\n", __func__, + ch_num, slim_rx_cfg[ch_num].channels); + ucontrol->value.enumerated.item[0] = slim_rx_cfg[ch_num].channels - 1; + + return 0; +} + +static int msm_slim_rx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = slim_get_port_idx(kcontrol); + + if (ch_num < 0) + return ch_num; + + slim_rx_cfg[ch_num].channels = ucontrol->value.enumerated.item[0] + 1; + pr_debug("%s: msm_slim_[%d]_rx_ch = %d\n", __func__, + ch_num, slim_rx_cfg[ch_num].channels); + + return 1; +} + +static int msm_slim_tx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = slim_get_port_idx(kcontrol); + + if (ch_num < 0) + return ch_num; + + pr_debug("%s: msm_slim_[%d]_tx_ch = %d\n", __func__, + ch_num, slim_tx_cfg[ch_num].channels); + ucontrol->value.enumerated.item[0] = slim_tx_cfg[ch_num].channels - 1; + + return 0; +} + +static int msm_slim_tx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = slim_get_port_idx(kcontrol); + + if (ch_num < 0) + return ch_num; + + slim_tx_cfg[ch_num].channels = ucontrol->value.enumerated.item[0] + 1; + pr_debug("%s: msm_slim_[%d]_tx_ch = %d\n", __func__, + ch_num, slim_tx_cfg[ch_num].channels); + + return 1; +} + +static int msm_vi_feed_tx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = msm_vi_feed_tx_ch - 1; + pr_debug("%s: msm_vi_feed_tx_ch = %ld\n", __func__, + ucontrol->value.integer.value[0]); + return 0; +} + +static int msm_vi_feed_tx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + msm_vi_feed_tx_ch = ucontrol->value.integer.value[0] + 1; + + pr_debug("%s: msm_vi_feed_tx_ch = %d\n", __func__, msm_vi_feed_tx_ch); + return 1; +} + +static int msm_bt_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + /* + * Slimbus_7_Rx/Tx sample rate values should always be in sync (same) + * when used for BT_SCO use case. Return either Rx or Tx sample rate + * value. + */ + switch (slim_rx_cfg[SLIM_RX_7].sample_rate) { + case SAMPLING_RATE_48KHZ: + ucontrol->value.integer.value[0] = 2; + break; + case SAMPLING_RATE_16KHZ: + ucontrol->value.integer.value[0] = 1; + break; + case SAMPLING_RATE_8KHZ: + default: + ucontrol->value.integer.value[0] = 0; + break; + } + pr_debug("%s: sample rate = %d", __func__, + slim_rx_cfg[SLIM_RX_7].sample_rate); + + return 0; +} + +static int msm_bt_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (ucontrol->value.integer.value[0]) { + case 1: + slim_rx_cfg[SLIM_RX_7].sample_rate = SAMPLING_RATE_16KHZ; + slim_tx_cfg[SLIM_TX_7].sample_rate = SAMPLING_RATE_16KHZ; + break; + case 2: + slim_rx_cfg[SLIM_RX_7].sample_rate = SAMPLING_RATE_48KHZ; + slim_tx_cfg[SLIM_TX_7].sample_rate = SAMPLING_RATE_48KHZ; + break; + case 0: + default: + slim_rx_cfg[SLIM_RX_7].sample_rate = SAMPLING_RATE_8KHZ; + slim_tx_cfg[SLIM_TX_7].sample_rate = SAMPLING_RATE_8KHZ; + break; + } + pr_debug("%s: sample rates: slim7_rx = %d, slim7_tx = %d, value = %d\n", + __func__, + slim_rx_cfg[SLIM_RX_7].sample_rate, + slim_tx_cfg[SLIM_TX_7].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int usb_audio_rx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s: usb_audio_rx_ch = %d\n", __func__, + usb_rx_cfg.channels); + ucontrol->value.integer.value[0] = usb_rx_cfg.channels - 1; + return 0; +} + +static int usb_audio_rx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + usb_rx_cfg.channels = ucontrol->value.integer.value[0] + 1; + + pr_debug("%s: usb_audio_rx_ch = %d\n", __func__, usb_rx_cfg.channels); + return 1; +} + +static int usb_audio_rx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int sample_rate_val; + + switch (usb_rx_cfg.sample_rate) { + case SAMPLING_RATE_384KHZ: + sample_rate_val = 12; + break; + case SAMPLING_RATE_352P8KHZ: + sample_rate_val = 11; + break; + case SAMPLING_RATE_192KHZ: + sample_rate_val = 10; + break; + case SAMPLING_RATE_176P4KHZ: + sample_rate_val = 9; + break; + case SAMPLING_RATE_96KHZ: + sample_rate_val = 8; + break; + case SAMPLING_RATE_88P2KHZ: + sample_rate_val = 7; + break; + case SAMPLING_RATE_48KHZ: + sample_rate_val = 6; + break; + case SAMPLING_RATE_44P1KHZ: + sample_rate_val = 5; + break; + case SAMPLING_RATE_32KHZ: + sample_rate_val = 4; + break; + case SAMPLING_RATE_22P05KHZ: + sample_rate_val = 3; + break; + case SAMPLING_RATE_16KHZ: + sample_rate_val = 2; + break; + case SAMPLING_RATE_11P025KHZ: + sample_rate_val = 1; + break; + case SAMPLING_RATE_8KHZ: + default: + sample_rate_val = 0; + break; + } + + ucontrol->value.integer.value[0] = sample_rate_val; + pr_debug("%s: usb_audio_rx_sample_rate = %d\n", __func__, + usb_rx_cfg.sample_rate); + return 0; +} + +static int usb_audio_rx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (ucontrol->value.integer.value[0]) { + case 12: + usb_rx_cfg.sample_rate = SAMPLING_RATE_384KHZ; + break; + case 11: + usb_rx_cfg.sample_rate = SAMPLING_RATE_352P8KHZ; + break; + case 10: + usb_rx_cfg.sample_rate = SAMPLING_RATE_192KHZ; + break; + case 9: + usb_rx_cfg.sample_rate = SAMPLING_RATE_176P4KHZ; + break; + case 8: + usb_rx_cfg.sample_rate = SAMPLING_RATE_96KHZ; + break; + case 7: + usb_rx_cfg.sample_rate = SAMPLING_RATE_88P2KHZ; + break; + case 6: + usb_rx_cfg.sample_rate = SAMPLING_RATE_48KHZ; + break; + case 5: + usb_rx_cfg.sample_rate = SAMPLING_RATE_44P1KHZ; + break; + case 4: + usb_rx_cfg.sample_rate = SAMPLING_RATE_32KHZ; + break; + case 3: + usb_rx_cfg.sample_rate = SAMPLING_RATE_22P05KHZ; + break; + case 2: + usb_rx_cfg.sample_rate = SAMPLING_RATE_16KHZ; + break; + case 1: + usb_rx_cfg.sample_rate = SAMPLING_RATE_11P025KHZ; + break; + case 0: + usb_rx_cfg.sample_rate = SAMPLING_RATE_8KHZ; + break; + default: + usb_rx_cfg.sample_rate = SAMPLING_RATE_48KHZ; + break; + } + + pr_debug("%s: control value = %ld, usb_audio_rx_sample_rate = %d\n", + __func__, ucontrol->value.integer.value[0], + usb_rx_cfg.sample_rate); + return 0; +} + +static int usb_audio_rx_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (usb_rx_cfg.bit_format) { + case SNDRV_PCM_FORMAT_S32_LE: + ucontrol->value.integer.value[0] = 3; + break; + case SNDRV_PCM_FORMAT_S24_3LE: + ucontrol->value.integer.value[0] = 2; + break; + case SNDRV_PCM_FORMAT_S24_LE: + ucontrol->value.integer.value[0] = 1; + break; + case SNDRV_PCM_FORMAT_S16_LE: + default: + ucontrol->value.integer.value[0] = 0; + break; + } + + pr_debug("%s: usb_audio_rx_format = %d, ucontrol value = %ld\n", + __func__, usb_rx_cfg.bit_format, + ucontrol->value.integer.value[0]); + return 0; +} + +static int usb_audio_rx_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int rc = 0; + + switch (ucontrol->value.integer.value[0]) { + case 3: + usb_rx_cfg.bit_format = SNDRV_PCM_FORMAT_S32_LE; + break; + case 2: + usb_rx_cfg.bit_format = SNDRV_PCM_FORMAT_S24_3LE; + break; + case 1: + usb_rx_cfg.bit_format = SNDRV_PCM_FORMAT_S24_LE; + break; + case 0: + default: + usb_rx_cfg.bit_format = SNDRV_PCM_FORMAT_S16_LE; + break; + } + pr_debug("%s: usb_audio_rx_format = %d, ucontrol value = %ld\n", + __func__, usb_rx_cfg.bit_format, + ucontrol->value.integer.value[0]); + + return rc; +} + +static int usb_audio_tx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s: usb_audio_tx_ch = %d\n", __func__, + usb_tx_cfg.channels); + ucontrol->value.integer.value[0] = usb_tx_cfg.channels - 1; + return 0; +} + +static int usb_audio_tx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + usb_tx_cfg.channels = ucontrol->value.integer.value[0] + 1; + + pr_debug("%s: usb_audio_tx_ch = %d\n", __func__, usb_tx_cfg.channels); + return 1; +} + +static int usb_audio_tx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int sample_rate_val; + + switch (usb_tx_cfg.sample_rate) { + case SAMPLING_RATE_384KHZ: + sample_rate_val = 12; + break; + case SAMPLING_RATE_352P8KHZ: + sample_rate_val = 11; + break; + case SAMPLING_RATE_192KHZ: + sample_rate_val = 10; + break; + case SAMPLING_RATE_176P4KHZ: + sample_rate_val = 9; + break; + case SAMPLING_RATE_96KHZ: + sample_rate_val = 8; + break; + case SAMPLING_RATE_88P2KHZ: + sample_rate_val = 7; + break; + case SAMPLING_RATE_48KHZ: + sample_rate_val = 6; + break; + case SAMPLING_RATE_44P1KHZ: + sample_rate_val = 5; + break; + case SAMPLING_RATE_32KHZ: + sample_rate_val = 4; + break; + case SAMPLING_RATE_22P05KHZ: + sample_rate_val = 3; + break; + case SAMPLING_RATE_16KHZ: + sample_rate_val = 2; + break; + case SAMPLING_RATE_11P025KHZ: + sample_rate_val = 1; + break; + case SAMPLING_RATE_8KHZ: + sample_rate_val = 0; + break; + default: + sample_rate_val = 6; + break; + } + + ucontrol->value.integer.value[0] = sample_rate_val; + pr_debug("%s: usb_audio_tx_sample_rate = %d\n", __func__, + usb_tx_cfg.sample_rate); + return 0; +} + +static int usb_audio_tx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (ucontrol->value.integer.value[0]) { + case 12: + usb_tx_cfg.sample_rate = SAMPLING_RATE_384KHZ; + break; + case 11: + usb_tx_cfg.sample_rate = SAMPLING_RATE_352P8KHZ; + break; + case 10: + usb_tx_cfg.sample_rate = SAMPLING_RATE_192KHZ; + break; + case 9: + usb_tx_cfg.sample_rate = SAMPLING_RATE_176P4KHZ; + break; + case 8: + usb_tx_cfg.sample_rate = SAMPLING_RATE_96KHZ; + break; + case 7: + usb_tx_cfg.sample_rate = SAMPLING_RATE_88P2KHZ; + break; + case 6: + usb_tx_cfg.sample_rate = SAMPLING_RATE_48KHZ; + break; + case 5: + usb_tx_cfg.sample_rate = SAMPLING_RATE_44P1KHZ; + break; + case 4: + usb_tx_cfg.sample_rate = SAMPLING_RATE_32KHZ; + break; + case 3: + usb_tx_cfg.sample_rate = SAMPLING_RATE_22P05KHZ; + break; + case 2: + usb_tx_cfg.sample_rate = SAMPLING_RATE_16KHZ; + break; + case 1: + usb_tx_cfg.sample_rate = SAMPLING_RATE_11P025KHZ; + break; + case 0: + usb_tx_cfg.sample_rate = SAMPLING_RATE_8KHZ; + break; + default: + usb_tx_cfg.sample_rate = SAMPLING_RATE_48KHZ; + break; + } + + pr_debug("%s: control value = %ld, usb_audio_tx_sample_rate = %d\n", + __func__, ucontrol->value.integer.value[0], + usb_tx_cfg.sample_rate); + return 0; +} + +static int usb_audio_tx_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (usb_tx_cfg.bit_format) { + case SNDRV_PCM_FORMAT_S32_LE: + ucontrol->value.integer.value[0] = 3; + break; + case SNDRV_PCM_FORMAT_S24_3LE: + ucontrol->value.integer.value[0] = 2; + break; + case SNDRV_PCM_FORMAT_S24_LE: + ucontrol->value.integer.value[0] = 1; + break; + case SNDRV_PCM_FORMAT_S16_LE: + default: + ucontrol->value.integer.value[0] = 0; + break; + } + + pr_debug("%s: usb_audio_tx_format = %d, ucontrol value = %ld\n", + __func__, usb_tx_cfg.bit_format, + ucontrol->value.integer.value[0]); + return 0; +} + +static int usb_audio_tx_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int rc = 0; + + switch (ucontrol->value.integer.value[0]) { + case 3: + usb_tx_cfg.bit_format = SNDRV_PCM_FORMAT_S32_LE; + break; + case 2: + usb_tx_cfg.bit_format = SNDRV_PCM_FORMAT_S24_3LE; + break; + case 1: + usb_tx_cfg.bit_format = SNDRV_PCM_FORMAT_S24_LE; + break; + case 0: + default: + usb_tx_cfg.bit_format = SNDRV_PCM_FORMAT_S16_LE; + break; + } + pr_debug("%s: usb_audio_tx_format = %d, ucontrol value = %ld\n", + __func__, usb_tx_cfg.bit_format, + ucontrol->value.integer.value[0]); + + return rc; +} + +static int ext_disp_get_port_idx(struct snd_kcontrol *kcontrol) +{ + int idx; + + if (strnstr(kcontrol->id.name, "HDMI_RX", sizeof("HDMI_RX"))) + idx = HDMI_RX_IDX; + else if (strnstr(kcontrol->id.name, "Display Port RX", + sizeof("Display Port RX"))) + idx = DP_RX_IDX; + else { + pr_err("%s: unsupported BE: %s", + __func__, kcontrol->id.name); + idx = -EINVAL; + } + + return idx; +} + +static int ext_disp_rx_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = ext_disp_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + switch (ext_disp_rx_cfg[idx].bit_format) { + case SNDRV_PCM_FORMAT_S24_LE: + ucontrol->value.integer.value[0] = 1; + break; + + case SNDRV_PCM_FORMAT_S16_LE: + default: + ucontrol->value.integer.value[0] = 0; + break; + } + + pr_debug("%s: ext_disp_rx[%d].format = %d, ucontrol value = %ld\n", + __func__, idx, ext_disp_rx_cfg[idx].bit_format, + ucontrol->value.integer.value[0]); + return 0; +} + +static int ext_disp_rx_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = ext_disp_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + switch (ucontrol->value.integer.value[0]) { + case 1: + ext_disp_rx_cfg[idx].bit_format = SNDRV_PCM_FORMAT_S24_LE; + break; + case 0: + default: + ext_disp_rx_cfg[idx].bit_format = SNDRV_PCM_FORMAT_S16_LE; + break; + } + pr_debug("%s: ext_disp_rx[%d].format = %d, ucontrol value = %ld\n", + __func__, idx, ext_disp_rx_cfg[idx].bit_format, + ucontrol->value.integer.value[0]); + + return 0; +} + +static int ext_disp_rx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = ext_disp_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + ucontrol->value.integer.value[0] = + ext_disp_rx_cfg[idx].channels - 2; + + pr_debug("%s: ext_disp_rx[%d].ch = %d\n", __func__, + idx, ext_disp_rx_cfg[idx].channels); + + return 0; +} + +static int ext_disp_rx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = ext_disp_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + ext_disp_rx_cfg[idx].channels = + ucontrol->value.integer.value[0] + 2; + + pr_debug("%s: ext_disp_rx[%d].ch = %d\n", __func__, + idx, ext_disp_rx_cfg[idx].channels); + return 1; +} + +static int ext_disp_rx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int sample_rate_val; + int idx = ext_disp_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + switch (ext_disp_rx_cfg[idx].sample_rate) { + case SAMPLING_RATE_176P4KHZ: + sample_rate_val = 6; + break; + + case SAMPLING_RATE_88P2KHZ: + sample_rate_val = 5; + break; + + case SAMPLING_RATE_44P1KHZ: + sample_rate_val = 4; + break; + + case SAMPLING_RATE_32KHZ: + sample_rate_val = 3; + break; + + case SAMPLING_RATE_192KHZ: + sample_rate_val = 2; + break; + + case SAMPLING_RATE_96KHZ: + sample_rate_val = 1; + break; + + case SAMPLING_RATE_48KHZ: + default: + sample_rate_val = 0; + break; + } + + ucontrol->value.integer.value[0] = sample_rate_val; + pr_debug("%s: ext_disp_rx[%d].sample_rate = %d\n", __func__, + idx, ext_disp_rx_cfg[idx].sample_rate); + + return 0; +} + +static int ext_disp_rx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = ext_disp_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + switch (ucontrol->value.integer.value[0]) { + case 6: + ext_disp_rx_cfg[idx].sample_rate = SAMPLING_RATE_176P4KHZ; + break; + case 5: + ext_disp_rx_cfg[idx].sample_rate = SAMPLING_RATE_88P2KHZ; + break; + case 4: + ext_disp_rx_cfg[idx].sample_rate = SAMPLING_RATE_44P1KHZ; + break; + case 3: + ext_disp_rx_cfg[idx].sample_rate = SAMPLING_RATE_32KHZ; + break; + case 2: + ext_disp_rx_cfg[idx].sample_rate = SAMPLING_RATE_192KHZ; + break; + case 1: + ext_disp_rx_cfg[idx].sample_rate = SAMPLING_RATE_96KHZ; + break; + case 0: + default: + ext_disp_rx_cfg[idx].sample_rate = SAMPLING_RATE_48KHZ; + break; + } + + pr_debug("%s: control value = %ld, ext_disp_rx[%d].sample_rate = %d\n", + __func__, ucontrol->value.integer.value[0], idx, + ext_disp_rx_cfg[idx].sample_rate); + return 0; +} + +static int proxy_rx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s: proxy_rx channels = %d\n", + __func__, proxy_rx_cfg.channels); + ucontrol->value.integer.value[0] = proxy_rx_cfg.channels - 2; + + return 0; +} + +static int proxy_rx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + proxy_rx_cfg.channels = ucontrol->value.integer.value[0] + 2; + pr_debug("%s: proxy_rx channels = %d\n", + __func__, proxy_rx_cfg.channels); + + return 1; +} + +static int tdm_get_sample_rate(int value) +{ + int sample_rate = 0; + + switch (value) { + case 0: + sample_rate = SAMPLING_RATE_8KHZ; + break; + case 1: + sample_rate = SAMPLING_RATE_16KHZ; + break; + case 2: + sample_rate = SAMPLING_RATE_32KHZ; + break; + case 3: + sample_rate = SAMPLING_RATE_44P1KHZ; + break; + case 4: + sample_rate = SAMPLING_RATE_48KHZ; + break; + case 5: + sample_rate = SAMPLING_RATE_96KHZ; + break; + case 6: + sample_rate = SAMPLING_RATE_192KHZ; + break; + case 7: + sample_rate = SAMPLING_RATE_352P8KHZ; + break; + case 8: + sample_rate = SAMPLING_RATE_384KHZ; + break; + default: + sample_rate = SAMPLING_RATE_48KHZ; + break; + } + return sample_rate; +} + +static int aux_pcm_get_sample_rate(int value) +{ + int sample_rate; + + switch (value) { + case 1: + sample_rate = SAMPLING_RATE_16KHZ; + break; + case 0: + default: + sample_rate = SAMPLING_RATE_8KHZ; + break; + } + return sample_rate; +} + +static int tdm_get_sample_rate_val(int sample_rate) +{ + int sample_rate_val = 0; + + switch (sample_rate) { + case SAMPLING_RATE_8KHZ: + sample_rate_val = 0; + break; + case SAMPLING_RATE_16KHZ: + sample_rate_val = 1; + break; + case SAMPLING_RATE_32KHZ: + sample_rate_val = 2; + break; + case SAMPLING_RATE_44P1KHZ: + sample_rate_val = 3; + break; + case SAMPLING_RATE_48KHZ: + sample_rate_val = 4; + break; + case SAMPLING_RATE_96KHZ: + sample_rate_val = 5; + break; + case SAMPLING_RATE_192KHZ: + sample_rate_val = 6; + break; + case SAMPLING_RATE_352P8KHZ: + sample_rate_val = 7; + break; + case SAMPLING_RATE_384KHZ: + sample_rate_val = 8; + break; + default: + sample_rate_val = 4; + break; + } + return sample_rate_val; +} + +static int aux_pcm_get_sample_rate_val(int sample_rate) +{ + int sample_rate_val; + + switch (sample_rate) { + case SAMPLING_RATE_16KHZ: + sample_rate_val = 1; + break; + case SAMPLING_RATE_8KHZ: + default: + sample_rate_val = 0; + break; + } + return sample_rate_val; +} + +static int tdm_get_port_idx(struct snd_kcontrol *kcontrol, + struct tdm_port *port) +{ + if (port) { + if (strnstr(kcontrol->id.name, "PRI", + sizeof(kcontrol->id.name))) { + port->mode = TDM_PRI; + } else if (strnstr(kcontrol->id.name, "SEC", + sizeof(kcontrol->id.name))) { + port->mode = TDM_SEC; + } else if (strnstr(kcontrol->id.name, "TERT", + sizeof(kcontrol->id.name))) { + port->mode = TDM_TERT; + } else if (strnstr(kcontrol->id.name, "QUAT", + sizeof(kcontrol->id.name))) { + port->mode = TDM_QUAT; + } else { + pr_err("%s: unsupported mode in: %s", + __func__, kcontrol->id.name); + return -EINVAL; + } + + if (strnstr(kcontrol->id.name, "RX_0", + sizeof(kcontrol->id.name)) || + strnstr(kcontrol->id.name, "TX_0", + sizeof(kcontrol->id.name))) { + port->channel = TDM_0; + } else if (strnstr(kcontrol->id.name, "RX_1", + sizeof(kcontrol->id.name)) || + strnstr(kcontrol->id.name, "TX_1", + sizeof(kcontrol->id.name))) { + port->channel = TDM_1; + } else if (strnstr(kcontrol->id.name, "RX_2", + sizeof(kcontrol->id.name)) || + strnstr(kcontrol->id.name, "TX_2", + sizeof(kcontrol->id.name))) { + port->channel = TDM_2; + } else if (strnstr(kcontrol->id.name, "RX_3", + sizeof(kcontrol->id.name)) || + strnstr(kcontrol->id.name, "TX_3", + sizeof(kcontrol->id.name))) { + port->channel = TDM_3; + } else if (strnstr(kcontrol->id.name, "RX_4", + sizeof(kcontrol->id.name)) || + strnstr(kcontrol->id.name, "TX_4", + sizeof(kcontrol->id.name))) { + port->channel = TDM_4; + } else if (strnstr(kcontrol->id.name, "RX_5", + sizeof(kcontrol->id.name)) || + strnstr(kcontrol->id.name, "TX_5", + sizeof(kcontrol->id.name))) { + port->channel = TDM_5; + } else if (strnstr(kcontrol->id.name, "RX_6", + sizeof(kcontrol->id.name)) || + strnstr(kcontrol->id.name, "TX_6", + sizeof(kcontrol->id.name))) { + port->channel = TDM_6; + } else if (strnstr(kcontrol->id.name, "RX_7", + sizeof(kcontrol->id.name)) || + strnstr(kcontrol->id.name, "TX_7", + sizeof(kcontrol->id.name))) { + port->channel = TDM_7; + } else { + pr_err("%s: unsupported channel in: %s", + __func__, kcontrol->id.name); + return -EINVAL; + } + } else + return -EINVAL; + return 0; +} + +static int tdm_rx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + ucontrol->value.enumerated.item[0] = tdm_get_sample_rate_val( + tdm_rx_cfg[port.mode][port.channel].sample_rate); + + pr_debug("%s: tdm_rx_sample_rate = %d, item = %d\n", __func__, + tdm_rx_cfg[port.mode][port.channel].sample_rate, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_rx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + tdm_rx_cfg[port.mode][port.channel].sample_rate = + tdm_get_sample_rate(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: tdm_rx_sample_rate = %d, item = %d\n", __func__, + tdm_rx_cfg[port.mode][port.channel].sample_rate, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_tx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + ucontrol->value.enumerated.item[0] = tdm_get_sample_rate_val( + tdm_tx_cfg[port.mode][port.channel].sample_rate); + + pr_debug("%s: tdm_tx_sample_rate = %d, item = %d\n", __func__, + tdm_tx_cfg[port.mode][port.channel].sample_rate, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_tx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + tdm_tx_cfg[port.mode][port.channel].sample_rate = + tdm_get_sample_rate(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: tdm_tx_sample_rate = %d, item = %d\n", __func__, + tdm_tx_cfg[port.mode][port.channel].sample_rate, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_get_format(int value) +{ + int format = 0; + + switch (value) { + case 0: + format = SNDRV_PCM_FORMAT_S16_LE; + break; + case 1: + format = SNDRV_PCM_FORMAT_S24_LE; + break; + case 2: + format = SNDRV_PCM_FORMAT_S32_LE; + break; + default: + format = SNDRV_PCM_FORMAT_S16_LE; + break; + } + return format; +} + +static int tdm_get_format_val(int format) +{ + int value = 0; + + switch (format) { + case SNDRV_PCM_FORMAT_S16_LE: + value = 0; + break; + case SNDRV_PCM_FORMAT_S24_LE: + value = 1; + break; + case SNDRV_PCM_FORMAT_S32_LE: + value = 2; + break; + default: + value = 0; + break; + } + return value; +} + +static int tdm_rx_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + ucontrol->value.enumerated.item[0] = tdm_get_format_val( + tdm_rx_cfg[port.mode][port.channel].bit_format); + + pr_debug("%s: tdm_rx_bit_format = %d, item = %d\n", __func__, + tdm_rx_cfg[port.mode][port.channel].bit_format, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_rx_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + tdm_rx_cfg[port.mode][port.channel].bit_format = + tdm_get_format(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: tdm_rx_bit_format = %d, item = %d\n", __func__, + tdm_rx_cfg[port.mode][port.channel].bit_format, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_tx_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + ucontrol->value.enumerated.item[0] = tdm_get_format_val( + tdm_tx_cfg[port.mode][port.channel].bit_format); + + pr_debug("%s: tdm_tx_bit_format = %d, item = %d\n", __func__, + tdm_tx_cfg[port.mode][port.channel].bit_format, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_tx_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + tdm_tx_cfg[port.mode][port.channel].bit_format = + tdm_get_format(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: tdm_tx_bit_format = %d, item = %d\n", __func__, + tdm_tx_cfg[port.mode][port.channel].bit_format, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_rx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + + ucontrol->value.enumerated.item[0] = + tdm_rx_cfg[port.mode][port.channel].channels - 1; + + pr_debug("%s: tdm_rx_ch = %d, item = %d\n", __func__, + tdm_rx_cfg[port.mode][port.channel].channels - 1, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_rx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + tdm_rx_cfg[port.mode][port.channel].channels = + ucontrol->value.enumerated.item[0] + 1; + + pr_debug("%s: tdm_rx_ch = %d, item = %d\n", __func__, + tdm_rx_cfg[port.mode][port.channel].channels, + ucontrol->value.enumerated.item[0] + 1); + } + return ret; +} + +static int tdm_tx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + ucontrol->value.enumerated.item[0] = + tdm_tx_cfg[port.mode][port.channel].channels - 1; + + pr_debug("%s: tdm_tx_ch = %d, item = %d\n", __func__, + tdm_tx_cfg[port.mode][port.channel].channels - 1, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_tx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + tdm_tx_cfg[port.mode][port.channel].channels = + ucontrol->value.enumerated.item[0] + 1; + + pr_debug("%s: tdm_tx_ch = %d, item = %d\n", __func__, + tdm_tx_cfg[port.mode][port.channel].channels, + ucontrol->value.enumerated.item[0] + 1); + } + return ret; +} + +static int aux_pcm_get_port_idx(struct snd_kcontrol *kcontrol) +{ + int idx; + + if (strnstr(kcontrol->id.name, "PRIM_AUX_PCM", + sizeof("PRIM_AUX_PCM"))) + idx = PRIM_AUX_PCM; + else if (strnstr(kcontrol->id.name, "SEC_AUX_PCM", + sizeof("SEC_AUX_PCM"))) + idx = SEC_AUX_PCM; + else if (strnstr(kcontrol->id.name, "TERT_AUX_PCM", + sizeof("TERT_AUX_PCM"))) + idx = TERT_AUX_PCM; + else if (strnstr(kcontrol->id.name, "QUAT_AUX_PCM", + sizeof("QUAT_AUX_PCM"))) + idx = QUAT_AUX_PCM; + else { + pr_err("%s: unsupported port: %s", + __func__, kcontrol->id.name); + idx = -EINVAL; + } + + return idx; +} + +static int aux_pcm_rx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = aux_pcm_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + aux_pcm_rx_cfg[idx].sample_rate = + aux_pcm_get_sample_rate(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: idx[%d]_rx_sample_rate = %d, item = %d\n", __func__, + idx, aux_pcm_rx_cfg[idx].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int aux_pcm_rx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = aux_pcm_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + ucontrol->value.enumerated.item[0] = + aux_pcm_get_sample_rate_val(aux_pcm_rx_cfg[idx].sample_rate); + + pr_debug("%s: idx[%d]_rx_sample_rate = %d, item = %d\n", __func__, + idx, aux_pcm_rx_cfg[idx].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int aux_pcm_tx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = aux_pcm_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + aux_pcm_tx_cfg[idx].sample_rate = + aux_pcm_get_sample_rate(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: idx[%d]_tx_sample_rate = %d, item = %d\n", __func__, + idx, aux_pcm_tx_cfg[idx].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int aux_pcm_tx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = aux_pcm_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + ucontrol->value.enumerated.item[0] = + aux_pcm_get_sample_rate_val(aux_pcm_tx_cfg[idx].sample_rate); + + pr_debug("%s: idx[%d]_tx_sample_rate = %d, item = %d\n", __func__, + idx, aux_pcm_tx_cfg[idx].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int mi2s_get_port_idx(struct snd_kcontrol *kcontrol) +{ + int idx; + + if (strnstr(kcontrol->id.name, "PRIM_MI2S_RX", + sizeof("PRIM_MI2S_RX"))) + idx = PRIM_MI2S; + else if (strnstr(kcontrol->id.name, "SEC_MI2S_RX", + sizeof("SEC_MI2S_RX"))) + idx = SEC_MI2S; + else if (strnstr(kcontrol->id.name, "TERT_MI2S_RX", + sizeof("TERT_MI2S_RX"))) + idx = TERT_MI2S; + else if (strnstr(kcontrol->id.name, "QUAT_MI2S_RX", + sizeof("QUAT_MI2S_RX"))) + idx = QUAT_MI2S; + else if (strnstr(kcontrol->id.name, "PRIM_MI2S_TX", + sizeof("PRIM_MI2S_TX"))) + idx = PRIM_MI2S; + else if (strnstr(kcontrol->id.name, "SEC_MI2S_TX", + sizeof("SEC_MI2S_TX"))) + idx = SEC_MI2S; + else if (strnstr(kcontrol->id.name, "TERT_MI2S_TX", + sizeof("TERT_MI2S_TX"))) + idx = TERT_MI2S; + else if (strnstr(kcontrol->id.name, "QUAT_MI2S_TX", + sizeof("QUAT_MI2S_TX"))) + idx = QUAT_MI2S; + else { + pr_err("%s: unsupported channel: %s", + __func__, kcontrol->id.name); + idx = -EINVAL; + } + + return idx; +} + +static int mi2s_get_sample_rate_val(int sample_rate) +{ + int sample_rate_val; + + switch (sample_rate) { + case SAMPLING_RATE_8KHZ: + sample_rate_val = 0; + break; + case SAMPLING_RATE_16KHZ: + sample_rate_val = 1; + break; + case SAMPLING_RATE_32KHZ: + sample_rate_val = 2; + break; + case SAMPLING_RATE_44P1KHZ: + sample_rate_val = 3; + break; + case SAMPLING_RATE_48KHZ: + sample_rate_val = 4; + break; + case SAMPLING_RATE_96KHZ: + sample_rate_val = 5; + break; + case SAMPLING_RATE_192KHZ: + sample_rate_val = 6; + break; + default: + sample_rate_val = 4; + break; + } + return sample_rate_val; +} + +static int mi2s_get_sample_rate(int value) +{ + int sample_rate; + + switch (value) { + case 0: + sample_rate = SAMPLING_RATE_8KHZ; + break; + case 1: + sample_rate = SAMPLING_RATE_16KHZ; + break; + case 2: + sample_rate = SAMPLING_RATE_32KHZ; + break; + case 3: + sample_rate = SAMPLING_RATE_44P1KHZ; + break; + case 4: + sample_rate = SAMPLING_RATE_48KHZ; + break; + case 5: + sample_rate = SAMPLING_RATE_96KHZ; + break; + case 6: + sample_rate = SAMPLING_RATE_192KHZ; + break; + default: + sample_rate = SAMPLING_RATE_48KHZ; + break; + } + return sample_rate; +} + +static int mi2s_get_format(int value) +{ + int format; + + switch (value) { + case 0: + format = SNDRV_PCM_FORMAT_S16_LE; + break; + case 1: + format = SNDRV_PCM_FORMAT_S24_LE; + break; + case 2: + format = SNDRV_PCM_FORMAT_S24_3LE; + break; + case 3: + format = SNDRV_PCM_FORMAT_S32_LE; + break; + default: + format = SNDRV_PCM_FORMAT_S16_LE; + break; + } + return format; +} + +static int mi2s_get_format_value(int format) +{ + int value; + + switch (format) { + case SNDRV_PCM_FORMAT_S16_LE: + value = 0; + break; + case SNDRV_PCM_FORMAT_S24_LE: + value = 1; + break; + case SNDRV_PCM_FORMAT_S24_3LE: + value = 2; + break; + case SNDRV_PCM_FORMAT_S32_LE: + value = 3; + break; + default: + value = 0; + break; + } + return value; +} + +static int mi2s_rx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + mi2s_rx_cfg[idx].sample_rate = + mi2s_get_sample_rate(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: idx[%d]_rx_sample_rate = %d, item = %d\n", __func__, + idx, mi2s_rx_cfg[idx].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int mi2s_rx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + ucontrol->value.enumerated.item[0] = + mi2s_get_sample_rate_val(mi2s_rx_cfg[idx].sample_rate); + + pr_debug("%s: idx[%d]_rx_sample_rate = %d, item = %d\n", __func__, + idx, mi2s_rx_cfg[idx].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int mi2s_tx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + mi2s_tx_cfg[idx].sample_rate = + mi2s_get_sample_rate(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: idx[%d]_tx_sample_rate = %d, item = %d\n", __func__, + idx, mi2s_tx_cfg[idx].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int mi2s_tx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + ucontrol->value.enumerated.item[0] = + mi2s_get_sample_rate_val(mi2s_tx_cfg[idx].sample_rate); + + pr_debug("%s: idx[%d]_tx_sample_rate = %d, item = %d\n", __func__, + idx, mi2s_tx_cfg[idx].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int msm_mi2s_rx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + pr_debug("%s: msm_mi2s_[%d]_rx_ch = %d\n", __func__, + idx, mi2s_rx_cfg[idx].channels); + ucontrol->value.enumerated.item[0] = mi2s_rx_cfg[idx].channels - 1; + + return 0; +} + +static int msm_mi2s_rx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + mi2s_rx_cfg[idx].channels = ucontrol->value.enumerated.item[0] + 1; + pr_debug("%s: msm_mi2s_[%d]_rx_ch = %d\n", __func__, + idx, mi2s_rx_cfg[idx].channels); + + return 1; +} + +static int msm_mi2s_tx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + pr_debug("%s: msm_mi2s_[%d]_tx_ch = %d\n", __func__, + idx, mi2s_tx_cfg[idx].channels); + ucontrol->value.enumerated.item[0] = mi2s_tx_cfg[idx].channels - 1; + + return 0; +} + +static int msm_mi2s_tx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + mi2s_tx_cfg[idx].channels = ucontrol->value.enumerated.item[0] + 1; + pr_debug("%s: msm_mi2s_[%d]_tx_ch = %d\n", __func__, + idx, mi2s_tx_cfg[idx].channels); + + return 1; +} + +static int msm_mi2s_rx_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + ucontrol->value.enumerated.item[0] = + mi2s_get_format_value(mi2s_rx_cfg[idx].bit_format); + + pr_debug("%s: idx[%d]_rx_format = %d, item = %d\n", __func__, + idx, mi2s_rx_cfg[idx].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int msm_mi2s_rx_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + mi2s_rx_cfg[idx].bit_format = + mi2s_get_format(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: idx[%d]_rx_format = %d, item = %d\n", __func__, + idx, mi2s_rx_cfg[idx].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int msm_mi2s_tx_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + ucontrol->value.enumerated.item[0] = + mi2s_get_format_value(mi2s_tx_cfg[idx].bit_format); + + pr_debug("%s: idx[%d]_tx_format = %d, item = %d\n", __func__, + idx, mi2s_tx_cfg[idx].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int msm_mi2s_tx_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + mi2s_tx_cfg[idx].bit_format = + mi2s_get_format(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: idx[%d]_tx_format = %d, item = %d\n", __func__, + idx, mi2s_tx_cfg[idx].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int msm_hifi_ctrl(struct snd_soc_codec *codec) +{ + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + struct snd_soc_card *card = codec->component.card; + struct msm_asoc_mach_data *pdata = + snd_soc_card_get_drvdata(card); + + pr_debug("%s: msm_hifi_control = %d", __func__, + msm_hifi_control); + + if (!pdata || !pdata->hph_en1_gpio_p) { + pr_err("%s: hph_en1_gpio is invalid\n", __func__); + return -EINVAL; + } + if (msm_hifi_control == MSM_HIFI_ON) { + msm_cdc_pinctrl_select_active_state(pdata->hph_en1_gpio_p); + /* 5msec delay needed as per HW requirement */ + usleep_range(5000, 5010); + } else { + msm_cdc_pinctrl_select_sleep_state(pdata->hph_en1_gpio_p); + } + snd_soc_dapm_sync(dapm); + + return 0; +} + +static int msm_hifi_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s: msm_hifi_control = %d\n", + __func__, msm_hifi_control); + ucontrol->value.integer.value[0] = msm_hifi_control; + + return 0; +} + +static int msm_hifi_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + + pr_debug("%s() ucontrol->value.integer.value[0] = %ld\n", + __func__, ucontrol->value.integer.value[0]); + + msm_hifi_control = ucontrol->value.integer.value[0]; + msm_hifi_ctrl(codec); + + return 0; +} + +static const struct snd_kcontrol_new msm_snd_controls[] = { + SOC_ENUM_EXT("SLIM_0_RX Channels", slim_0_rx_chs, + msm_slim_rx_ch_get, msm_slim_rx_ch_put), + SOC_ENUM_EXT("SLIM_2_RX Channels", slim_2_rx_chs, + msm_slim_rx_ch_get, msm_slim_rx_ch_put), + SOC_ENUM_EXT("SLIM_0_TX Channels", slim_0_tx_chs, + msm_slim_tx_ch_get, msm_slim_tx_ch_put), + SOC_ENUM_EXT("SLIM_1_TX Channels", slim_1_tx_chs, + msm_slim_tx_ch_get, msm_slim_tx_ch_put), + SOC_ENUM_EXT("SLIM_5_RX Channels", slim_5_rx_chs, + msm_slim_rx_ch_get, msm_slim_rx_ch_put), + SOC_ENUM_EXT("SLIM_6_RX Channels", slim_6_rx_chs, + msm_slim_rx_ch_get, msm_slim_rx_ch_put), + SOC_ENUM_EXT("VI_FEED_TX Channels", vi_feed_tx_chs, + msm_vi_feed_tx_ch_get, msm_vi_feed_tx_ch_put), + SOC_ENUM_EXT("USB_AUDIO_RX Channels", usb_rx_chs, + usb_audio_rx_ch_get, usb_audio_rx_ch_put), + SOC_ENUM_EXT("USB_AUDIO_TX Channels", usb_tx_chs, + usb_audio_tx_ch_get, usb_audio_tx_ch_put), + SOC_ENUM_EXT("HDMI_RX Channels", ext_disp_rx_chs, + ext_disp_rx_ch_get, ext_disp_rx_ch_put), + SOC_ENUM_EXT("Display Port RX Channels", ext_disp_rx_chs, + ext_disp_rx_ch_get, ext_disp_rx_ch_put), + SOC_ENUM_EXT("PROXY_RX Channels", proxy_rx_chs, + proxy_rx_ch_get, proxy_rx_ch_put), + SOC_ENUM_EXT("SLIM_0_RX Format", slim_0_rx_format, + slim_rx_bit_format_get, slim_rx_bit_format_put), + SOC_ENUM_EXT("SLIM_5_RX Format", slim_5_rx_format, + slim_rx_bit_format_get, slim_rx_bit_format_put), + SOC_ENUM_EXT("SLIM_6_RX Format", slim_6_rx_format, + slim_rx_bit_format_get, slim_rx_bit_format_put), + SOC_ENUM_EXT("SLIM_0_TX Format", slim_0_tx_format, + slim_tx_bit_format_get, slim_tx_bit_format_put), + SOC_ENUM_EXT("USB_AUDIO_RX Format", usb_rx_format, + usb_audio_rx_format_get, usb_audio_rx_format_put), + SOC_ENUM_EXT("USB_AUDIO_TX Format", usb_tx_format, + usb_audio_tx_format_get, usb_audio_tx_format_put), + SOC_ENUM_EXT("HDMI_RX Bit Format", ext_disp_rx_format, + ext_disp_rx_format_get, ext_disp_rx_format_put), + SOC_ENUM_EXT("Display Port RX Bit Format", ext_disp_rx_format, + ext_disp_rx_format_get, ext_disp_rx_format_put), + SOC_ENUM_EXT("SLIM_0_RX SampleRate", slim_0_rx_sample_rate, + slim_rx_sample_rate_get, slim_rx_sample_rate_put), + SOC_ENUM_EXT("SLIM_2_RX SampleRate", slim_2_rx_sample_rate, + slim_rx_sample_rate_get, slim_rx_sample_rate_put), + SOC_ENUM_EXT("SLIM_0_TX SampleRate", slim_0_tx_sample_rate, + slim_tx_sample_rate_get, slim_tx_sample_rate_put), + SOC_ENUM_EXT("SLIM_5_RX SampleRate", slim_5_rx_sample_rate, + slim_rx_sample_rate_get, slim_rx_sample_rate_put), + SOC_ENUM_EXT("SLIM_6_RX SampleRate", slim_6_rx_sample_rate, + slim_rx_sample_rate_get, slim_rx_sample_rate_put), + SOC_ENUM_EXT("BT SampleRate", bt_sample_rate, + msm_bt_sample_rate_get, + msm_bt_sample_rate_put), + SOC_ENUM_EXT("USB_AUDIO_RX SampleRate", usb_rx_sample_rate, + usb_audio_rx_sample_rate_get, + usb_audio_rx_sample_rate_put), + SOC_ENUM_EXT("USB_AUDIO_TX SampleRate", usb_tx_sample_rate, + usb_audio_tx_sample_rate_get, + usb_audio_tx_sample_rate_put), + SOC_ENUM_EXT("HDMI_RX SampleRate", ext_disp_rx_sample_rate, + ext_disp_rx_sample_rate_get, + ext_disp_rx_sample_rate_put), + SOC_ENUM_EXT("Display Port RX SampleRate", ext_disp_rx_sample_rate, + ext_disp_rx_sample_rate_get, + ext_disp_rx_sample_rate_put), + SOC_ENUM_EXT("PRI_TDM_RX_0 SampleRate", tdm_rx_sample_rate, + tdm_rx_sample_rate_get, + tdm_rx_sample_rate_put), + SOC_ENUM_EXT("PRI_TDM_TX_0 SampleRate", tdm_tx_sample_rate, + tdm_tx_sample_rate_get, + tdm_tx_sample_rate_put), + SOC_ENUM_EXT("PRI_TDM_RX_0 Format", tdm_rx_format, + tdm_rx_format_get, + tdm_rx_format_put), + SOC_ENUM_EXT("PRI_TDM_TX_0 Format", tdm_tx_format, + tdm_tx_format_get, + tdm_tx_format_put), + SOC_ENUM_EXT("PRI_TDM_RX_0 Channels", tdm_rx_chs, + tdm_rx_ch_get, + tdm_rx_ch_put), + SOC_ENUM_EXT("PRI_TDM_TX_0 Channels", tdm_tx_chs, + tdm_tx_ch_get, + tdm_tx_ch_put), + SOC_ENUM_EXT("SEC_TDM_RX_0 SampleRate", tdm_rx_sample_rate, + tdm_rx_sample_rate_get, + tdm_rx_sample_rate_put), + SOC_ENUM_EXT("SEC_TDM_TX_0 SampleRate", tdm_tx_sample_rate, + tdm_tx_sample_rate_get, + tdm_tx_sample_rate_put), + SOC_ENUM_EXT("SEC_TDM_RX_0 Format", tdm_rx_format, + tdm_rx_format_get, + tdm_rx_format_put), + SOC_ENUM_EXT("SEC_TDM_TX_0 Format", tdm_tx_format, + tdm_tx_format_get, + tdm_tx_format_put), + SOC_ENUM_EXT("SEC_TDM_RX_0 Channels", tdm_rx_chs, + tdm_rx_ch_get, + tdm_rx_ch_put), + SOC_ENUM_EXT("SEC_TDM_TX_0 Channels", tdm_tx_chs, + tdm_tx_ch_get, + tdm_tx_ch_put), + SOC_ENUM_EXT("TERT_TDM_RX_0 SampleRate", tdm_rx_sample_rate, + tdm_rx_sample_rate_get, + tdm_rx_sample_rate_put), + SOC_ENUM_EXT("TERT_TDM_TX_0 SampleRate", tdm_tx_sample_rate, + tdm_tx_sample_rate_get, + tdm_tx_sample_rate_put), + SOC_ENUM_EXT("TERT_TDM_RX_0 Format", tdm_rx_format, + tdm_rx_format_get, + tdm_rx_format_put), + SOC_ENUM_EXT("TERT_TDM_TX_0 Format", tdm_tx_format, + tdm_tx_format_get, + tdm_tx_format_put), + SOC_ENUM_EXT("TERT_TDM_RX_0 Channels", tdm_rx_chs, + tdm_rx_ch_get, + tdm_rx_ch_put), + SOC_ENUM_EXT("TERT_TDM_TX_0 Channels", tdm_tx_chs, + tdm_tx_ch_get, + tdm_tx_ch_put), + SOC_ENUM_EXT("QUAT_TDM_RX_0 SampleRate", tdm_rx_sample_rate, + tdm_rx_sample_rate_get, + tdm_rx_sample_rate_put), + SOC_ENUM_EXT("QUAT_TDM_TX_0 SampleRate", tdm_tx_sample_rate, + tdm_tx_sample_rate_get, + tdm_tx_sample_rate_put), + SOC_ENUM_EXT("QUAT_TDM_RX_0 Format", tdm_rx_format, + tdm_rx_format_get, + tdm_rx_format_put), + SOC_ENUM_EXT("QUAT_TDM_TX_0 Format", tdm_tx_format, + tdm_tx_format_get, + tdm_tx_format_put), + SOC_ENUM_EXT("QUAT_TDM_RX_0 Channels", tdm_rx_chs, + tdm_rx_ch_get, + tdm_rx_ch_put), + SOC_ENUM_EXT("QUAT_TDM_TX_0 Channels", tdm_tx_chs, + tdm_tx_ch_get, + tdm_tx_ch_put), + SOC_ENUM_EXT("PRIM_AUX_PCM_RX SampleRate", prim_aux_pcm_rx_sample_rate, + aux_pcm_rx_sample_rate_get, + aux_pcm_rx_sample_rate_put), + SOC_ENUM_EXT("SEC_AUX_PCM_RX SampleRate", sec_aux_pcm_rx_sample_rate, + aux_pcm_rx_sample_rate_get, + aux_pcm_rx_sample_rate_put), + SOC_ENUM_EXT("TERT_AUX_PCM_RX SampleRate", tert_aux_pcm_rx_sample_rate, + aux_pcm_rx_sample_rate_get, + aux_pcm_rx_sample_rate_put), + SOC_ENUM_EXT("QUAT_AUX_PCM_RX SampleRate", quat_aux_pcm_rx_sample_rate, + aux_pcm_rx_sample_rate_get, + aux_pcm_rx_sample_rate_put), + SOC_ENUM_EXT("PRIM_AUX_PCM_TX SampleRate", prim_aux_pcm_tx_sample_rate, + aux_pcm_tx_sample_rate_get, + aux_pcm_tx_sample_rate_put), + SOC_ENUM_EXT("SEC_AUX_PCM_TX SampleRate", sec_aux_pcm_tx_sample_rate, + aux_pcm_tx_sample_rate_get, + aux_pcm_tx_sample_rate_put), + SOC_ENUM_EXT("TERT_AUX_PCM_TX SampleRate", tert_aux_pcm_tx_sample_rate, + aux_pcm_tx_sample_rate_get, + aux_pcm_tx_sample_rate_put), + SOC_ENUM_EXT("QUAT_AUX_PCM_TX SampleRate", quat_aux_pcm_tx_sample_rate, + aux_pcm_tx_sample_rate_get, + aux_pcm_tx_sample_rate_put), + SOC_ENUM_EXT("PRIM_MI2S_RX SampleRate", prim_mi2s_rx_sample_rate, + mi2s_rx_sample_rate_get, + mi2s_rx_sample_rate_put), + SOC_ENUM_EXT("SEC_MI2S_RX SampleRate", sec_mi2s_rx_sample_rate, + mi2s_rx_sample_rate_get, + mi2s_rx_sample_rate_put), + SOC_ENUM_EXT("TERT_MI2S_RX SampleRate", tert_mi2s_rx_sample_rate, + mi2s_rx_sample_rate_get, + mi2s_rx_sample_rate_put), + SOC_ENUM_EXT("QUAT_MI2S_RX SampleRate", quat_mi2s_rx_sample_rate, + mi2s_rx_sample_rate_get, + mi2s_rx_sample_rate_put), + SOC_ENUM_EXT("PRIM_MI2S_TX SampleRate", prim_mi2s_tx_sample_rate, + mi2s_tx_sample_rate_get, + mi2s_tx_sample_rate_put), + SOC_ENUM_EXT("SEC_MI2S_TX SampleRate", sec_mi2s_tx_sample_rate, + mi2s_tx_sample_rate_get, + mi2s_tx_sample_rate_put), + SOC_ENUM_EXT("TERT_MI2S_TX SampleRate", tert_mi2s_tx_sample_rate, + mi2s_tx_sample_rate_get, + mi2s_tx_sample_rate_put), + SOC_ENUM_EXT("QUAT_MI2S_TX SampleRate", quat_mi2s_tx_sample_rate, + mi2s_tx_sample_rate_get, + mi2s_tx_sample_rate_put), + SOC_ENUM_EXT("PRIM_MI2S_RX Channels", prim_mi2s_rx_chs, + msm_mi2s_rx_ch_get, msm_mi2s_rx_ch_put), + SOC_ENUM_EXT("PRIM_MI2S_TX Channels", prim_mi2s_tx_chs, + msm_mi2s_tx_ch_get, msm_mi2s_tx_ch_put), + SOC_ENUM_EXT("SEC_MI2S_RX Channels", sec_mi2s_rx_chs, + msm_mi2s_rx_ch_get, msm_mi2s_rx_ch_put), + SOC_ENUM_EXT("SEC_MI2S_TX Channels", sec_mi2s_tx_chs, + msm_mi2s_tx_ch_get, msm_mi2s_tx_ch_put), + SOC_ENUM_EXT("TERT_MI2S_RX Channels", tert_mi2s_rx_chs, + msm_mi2s_rx_ch_get, msm_mi2s_rx_ch_put), + SOC_ENUM_EXT("TERT_MI2S_TX Channels", tert_mi2s_tx_chs, + msm_mi2s_tx_ch_get, msm_mi2s_tx_ch_put), + SOC_ENUM_EXT("QUAT_MI2S_RX Channels", quat_mi2s_rx_chs, + msm_mi2s_rx_ch_get, msm_mi2s_rx_ch_put), + SOC_ENUM_EXT("QUAT_MI2S_TX Channels", quat_mi2s_tx_chs, + msm_mi2s_tx_ch_get, msm_mi2s_tx_ch_put), + SOC_ENUM_EXT("PRIM_MI2S_RX Format", mi2s_rx_format, + msm_mi2s_rx_format_get, msm_mi2s_rx_format_put), + SOC_ENUM_EXT("PRIM_MI2S_TX Format", mi2s_tx_format, + msm_mi2s_tx_format_get, msm_mi2s_tx_format_put), + SOC_ENUM_EXT("SEC_MI2S_RX Format", mi2s_rx_format, + msm_mi2s_rx_format_get, msm_mi2s_rx_format_put), + SOC_ENUM_EXT("SEC_MI2S_TX Format", mi2s_tx_format, + msm_mi2s_tx_format_get, msm_mi2s_tx_format_put), + SOC_ENUM_EXT("TERT_MI2S_RX Format", mi2s_rx_format, + msm_mi2s_rx_format_get, msm_mi2s_rx_format_put), + SOC_ENUM_EXT("TERT_MI2S_TX Format", mi2s_tx_format, + msm_mi2s_tx_format_get, msm_mi2s_tx_format_put), + SOC_ENUM_EXT("QUAT_MI2S_RX Format", mi2s_rx_format, + msm_mi2s_rx_format_get, msm_mi2s_rx_format_put), + SOC_ENUM_EXT("QUAT_MI2S_TX Format", mi2s_tx_format, + msm_mi2s_tx_format_get, msm_mi2s_tx_format_put), + SOC_ENUM_EXT("HiFi Function", hifi_function, msm_hifi_get, + msm_hifi_put), +}; + +static int msm_snd_enable_codec_ext_clk(struct snd_soc_codec *codec, + int enable, bool dapm) +{ + int ret = 0; + + if (!strcmp(dev_name(codec->dev), "tasha_codec")) + ret = tasha_cdc_mclk_enable(codec, enable, dapm); + else if (!strcmp(dev_name(codec->dev), "tavil_codec")) + ret = tavil_cdc_mclk_enable(codec, enable); + else { + dev_err(codec->dev, "%s: unknown codec to enable ext clk\n", + __func__); + ret = -EINVAL; + } + return ret; +} + +static int msm_snd_enable_codec_ext_tx_clk(struct snd_soc_codec *codec, + int enable, bool dapm) +{ + int ret = 0; + + if (!strcmp(dev_name(codec->dev), "tasha_codec")) + ret = tasha_cdc_mclk_tx_enable(codec, enable, dapm); + else { + dev_err(codec->dev, "%s: unknown codec to enable ext clk\n", + __func__); + ret = -EINVAL; + } + return ret; +} + +static int msm_mclk_tx_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + + pr_debug("%s: event = %d\n", __func__, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + return msm_snd_enable_codec_ext_tx_clk(codec, 1, true); + case SND_SOC_DAPM_POST_PMD: + return msm_snd_enable_codec_ext_tx_clk(codec, 0, true); + } + return 0; +} + +static int msm_mclk_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + + pr_debug("%s: event = %d\n", __func__, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + return msm_snd_enable_codec_ext_clk(codec, 1, true); + case SND_SOC_DAPM_POST_PMD: + return msm_snd_enable_codec_ext_clk(codec, 0, true); + } + return 0; +} + +static int msm_hifi_ctrl_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct snd_soc_card *card = codec->component.card; + struct msm_asoc_mach_data *pdata = + snd_soc_card_get_drvdata(card); + + pr_debug("%s: msm_hifi_control = %d", __func__, msm_hifi_control); + + if (!pdata || !pdata->hph_en0_gpio_p) { + pr_err("%s: hph_en0_gpio is invalid\n", __func__); + return -EINVAL; + } + + if (msm_hifi_control != MSM_HIFI_ON) { + pr_debug("%s: HiFi mixer control is not set\n", + __func__); + return 0; + } + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + msm_cdc_pinctrl_select_active_state(pdata->hph_en0_gpio_p); + break; + case SND_SOC_DAPM_PRE_PMD: + msm_cdc_pinctrl_select_sleep_state(pdata->hph_en0_gpio_p); + break; + } + + return 0; +} + +static const struct snd_soc_dapm_widget msm_dapm_widgets[] = { + + SND_SOC_DAPM_SUPPLY("MCLK", SND_SOC_NOPM, 0, 0, + msm_mclk_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_SUPPLY("MCLK TX", SND_SOC_NOPM, 0, 0, + msm_mclk_tx_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_SPK("Lineout_1 amp", NULL), + SND_SOC_DAPM_SPK("Lineout_3 amp", NULL), + SND_SOC_DAPM_SPK("Lineout_2 amp", NULL), + SND_SOC_DAPM_SPK("Lineout_4 amp", NULL), + SND_SOC_DAPM_SPK("hifi amp", msm_hifi_ctrl_event), + SND_SOC_DAPM_MIC("Handset Mic", NULL), + SND_SOC_DAPM_MIC("Headset Mic", NULL), + SND_SOC_DAPM_MIC("ANCRight Headset Mic", NULL), + SND_SOC_DAPM_MIC("ANCLeft Headset Mic", NULL), + SND_SOC_DAPM_MIC("Analog Mic5", NULL), + SND_SOC_DAPM_MIC("Analog Mic6", NULL), + + SND_SOC_DAPM_MIC("Digital Mic0", NULL), + SND_SOC_DAPM_MIC("Digital Mic1", NULL), + SND_SOC_DAPM_MIC("Digital Mic2", NULL), + SND_SOC_DAPM_MIC("Digital Mic3", NULL), + SND_SOC_DAPM_MIC("Digital Mic4", NULL), + SND_SOC_DAPM_MIC("Digital Mic5", NULL), +}; + +static inline int param_is_mask(int p) +{ + return (p >= SNDRV_PCM_HW_PARAM_FIRST_MASK) && + (p <= SNDRV_PCM_HW_PARAM_LAST_MASK); +} + +static inline struct snd_mask *param_to_mask(struct snd_pcm_hw_params *p, + int n) +{ + return &(p->masks[n - SNDRV_PCM_HW_PARAM_FIRST_MASK]); +} + +static void param_set_mask(struct snd_pcm_hw_params *p, int n, unsigned int bit) +{ + if (bit >= SNDRV_MASK_MAX) + return; + if (param_is_mask(n)) { + struct snd_mask *m = param_to_mask(p, n); + + m->bits[0] = 0; + m->bits[1] = 0; + m->bits[bit >> 5] |= (1 << (bit & 31)); + } +} + +static int msm_slim_get_ch_from_beid(int32_t be_id) +{ + int ch_id = 0; + + switch (be_id) { + case MSM_BACKEND_DAI_SLIMBUS_0_RX: + ch_id = SLIM_RX_0; + break; + case MSM_BACKEND_DAI_SLIMBUS_1_RX: + ch_id = SLIM_RX_1; + break; + case MSM_BACKEND_DAI_SLIMBUS_2_RX: + ch_id = SLIM_RX_2; + break; + case MSM_BACKEND_DAI_SLIMBUS_3_RX: + ch_id = SLIM_RX_3; + break; + case MSM_BACKEND_DAI_SLIMBUS_4_RX: + ch_id = SLIM_RX_4; + break; + case MSM_BACKEND_DAI_SLIMBUS_6_RX: + ch_id = SLIM_RX_6; + break; + case MSM_BACKEND_DAI_SLIMBUS_0_TX: + ch_id = SLIM_TX_0; + break; + case MSM_BACKEND_DAI_SLIMBUS_3_TX: + ch_id = SLIM_TX_3; + break; + default: + ch_id = SLIM_RX_0; + break; + } + + return ch_id; +} + +static int msm_ext_disp_get_idx_from_beid(int32_t be_id) +{ + int idx; + + switch (be_id) { + case MSM_BACKEND_DAI_HDMI_RX: + idx = HDMI_RX_IDX; + break; + case MSM_BACKEND_DAI_DISPLAY_PORT_RX: + idx = DP_RX_IDX; + break; + default: + pr_err("%s: Incorrect ext_disp BE id %d\n", __func__, be_id); + idx = -EINVAL; + break; + } + + return idx; +} + +static int msm_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_dai_link *dai_link = rtd->dai_link; + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + int rc = 0; + int idx; + void *config = NULL; + struct snd_soc_codec *codec = NULL; + + pr_debug("%s: format = %d, rate = %d\n", + __func__, params_format(params), params_rate(params)); + + switch (dai_link->id) { + case MSM_BACKEND_DAI_SLIMBUS_0_RX: + case MSM_BACKEND_DAI_SLIMBUS_1_RX: + case MSM_BACKEND_DAI_SLIMBUS_2_RX: + case MSM_BACKEND_DAI_SLIMBUS_3_RX: + case MSM_BACKEND_DAI_SLIMBUS_4_RX: + case MSM_BACKEND_DAI_SLIMBUS_6_RX: + idx = msm_slim_get_ch_from_beid(dai_link->id); + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + slim_rx_cfg[idx].bit_format); + rate->min = rate->max = slim_rx_cfg[idx].sample_rate; + channels->min = channels->max = slim_rx_cfg[idx].channels; + break; + + case MSM_BACKEND_DAI_SLIMBUS_0_TX: + case MSM_BACKEND_DAI_SLIMBUS_3_TX: + idx = msm_slim_get_ch_from_beid(dai_link->id); + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + slim_tx_cfg[idx].bit_format); + rate->min = rate->max = slim_tx_cfg[idx].sample_rate; + channels->min = channels->max = slim_tx_cfg[idx].channels; + break; + + case MSM_BACKEND_DAI_SLIMBUS_1_TX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + slim_tx_cfg[1].bit_format); + rate->min = rate->max = slim_tx_cfg[1].sample_rate; + channels->min = channels->max = slim_tx_cfg[1].channels; + break; + + case MSM_BACKEND_DAI_SLIMBUS_4_TX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + SNDRV_PCM_FORMAT_S32_LE); + rate->min = rate->max = SAMPLING_RATE_8KHZ; + channels->min = channels->max = msm_vi_feed_tx_ch; + break; + + case MSM_BACKEND_DAI_SLIMBUS_5_RX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + slim_rx_cfg[5].bit_format); + rate->min = rate->max = slim_rx_cfg[5].sample_rate; + channels->min = channels->max = slim_rx_cfg[5].channels; + break; + + case MSM_BACKEND_DAI_SLIMBUS_5_TX: + codec = rtd->codec; + rate->min = rate->max = SAMPLING_RATE_16KHZ; + channels->min = channels->max = 1; + + config = msm_codec_fn.get_afe_config_fn(codec, + AFE_SLIMBUS_SLAVE_PORT_CONFIG); + if (config) { + rc = afe_set_config(AFE_SLIMBUS_SLAVE_PORT_CONFIG, + config, SLIMBUS_5_TX); + if (rc) + pr_err("%s: Failed to set slimbus slave port config %d\n", + __func__, rc); + } + break; + + case MSM_BACKEND_DAI_SLIMBUS_7_RX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + slim_rx_cfg[SLIM_RX_7].bit_format); + rate->min = rate->max = slim_rx_cfg[SLIM_RX_7].sample_rate; + channels->min = channels->max = + slim_rx_cfg[SLIM_RX_7].channels; + break; + + case MSM_BACKEND_DAI_SLIMBUS_7_TX: + rate->min = rate->max = slim_tx_cfg[SLIM_TX_7].sample_rate; + channels->min = channels->max = + slim_tx_cfg[SLIM_TX_7].channels; + break; + + case MSM_BACKEND_DAI_SLIMBUS_8_TX: + rate->min = rate->max = slim_tx_cfg[SLIM_TX_8].sample_rate; + channels->min = channels->max = + slim_tx_cfg[SLIM_TX_8].channels; + break; + + case MSM_BACKEND_DAI_USB_RX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + usb_rx_cfg.bit_format); + rate->min = rate->max = usb_rx_cfg.sample_rate; + channels->min = channels->max = usb_rx_cfg.channels; + break; + + case MSM_BACKEND_DAI_USB_TX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + usb_tx_cfg.bit_format); + rate->min = rate->max = usb_tx_cfg.sample_rate; + channels->min = channels->max = usb_tx_cfg.channels; + break; + + case MSM_BACKEND_DAI_HDMI_RX: + case MSM_BACKEND_DAI_DISPLAY_PORT_RX: + idx = msm_ext_disp_get_idx_from_beid(dai_link->id); + if (idx < 0) { + pr_err("%s: Incorrect ext disp idx %d\n", + __func__, idx); + rc = idx; + goto done; + } + + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + ext_disp_rx_cfg[idx].bit_format); + rate->min = rate->max = ext_disp_rx_cfg[idx].sample_rate; + channels->min = channels->max = ext_disp_rx_cfg[idx].channels; + break; + + case MSM_BACKEND_DAI_AFE_PCM_RX: + channels->min = channels->max = proxy_rx_cfg.channels; + rate->min = rate->max = SAMPLING_RATE_48KHZ; + break; + + case MSM_BACKEND_DAI_PRI_TDM_RX_0: + channels->min = channels->max = + tdm_rx_cfg[TDM_PRI][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_PRI][TDM_0].bit_format); + rate->min = rate->max = tdm_rx_cfg[TDM_PRI][TDM_0].sample_rate; + break; + + case MSM_BACKEND_DAI_PRI_TDM_TX_0: + channels->min = channels->max = + tdm_tx_cfg[TDM_PRI][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_tx_cfg[TDM_PRI][TDM_0].bit_format); + rate->min = rate->max = tdm_tx_cfg[TDM_PRI][TDM_0].sample_rate; + break; + + case MSM_BACKEND_DAI_SEC_TDM_RX_0: + channels->min = channels->max = + tdm_rx_cfg[TDM_SEC][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_SEC][TDM_0].bit_format); + rate->min = rate->max = tdm_rx_cfg[TDM_SEC][TDM_0].sample_rate; + break; + + case MSM_BACKEND_DAI_SEC_TDM_TX_0: + channels->min = channels->max = + tdm_tx_cfg[TDM_SEC][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_tx_cfg[TDM_SEC][TDM_0].bit_format); + rate->min = rate->max = tdm_tx_cfg[TDM_SEC][TDM_0].sample_rate; + break; + + case MSM_BACKEND_DAI_TERT_TDM_RX_0: + channels->min = channels->max = + tdm_rx_cfg[TDM_TERT][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_TERT][TDM_0].bit_format); + rate->min = rate->max = tdm_rx_cfg[TDM_TERT][TDM_0].sample_rate; + break; + + case MSM_BACKEND_DAI_TERT_TDM_TX_0: + channels->min = channels->max = + tdm_tx_cfg[TDM_TERT][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_tx_cfg[TDM_TERT][TDM_0].bit_format); + rate->min = rate->max = tdm_tx_cfg[TDM_TERT][TDM_0].sample_rate; + break; + + case MSM_BACKEND_DAI_QUAT_TDM_RX_0: + channels->min = channels->max = + tdm_rx_cfg[TDM_QUAT][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_QUAT][TDM_0].bit_format); + rate->min = rate->max = tdm_rx_cfg[TDM_QUAT][TDM_0].sample_rate; + break; + + case MSM_BACKEND_DAI_QUAT_TDM_TX_0: + channels->min = channels->max = + tdm_tx_cfg[TDM_QUAT][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_tx_cfg[TDM_QUAT][TDM_0].bit_format); + rate->min = rate->max = tdm_tx_cfg[TDM_QUAT][TDM_0].sample_rate; + break; + + case MSM_BACKEND_DAI_AUXPCM_RX: + rate->min = rate->max = + aux_pcm_rx_cfg[PRIM_AUX_PCM].sample_rate; + channels->min = channels->max = + aux_pcm_rx_cfg[PRIM_AUX_PCM].channels; + break; + + case MSM_BACKEND_DAI_AUXPCM_TX: + rate->min = rate->max = + aux_pcm_tx_cfg[PRIM_AUX_PCM].sample_rate; + channels->min = channels->max = + aux_pcm_tx_cfg[PRIM_AUX_PCM].channels; + break; + + case MSM_BACKEND_DAI_SEC_AUXPCM_RX: + rate->min = rate->max = + aux_pcm_rx_cfg[SEC_AUX_PCM].sample_rate; + channels->min = channels->max = + aux_pcm_rx_cfg[SEC_AUX_PCM].channels; + break; + + case MSM_BACKEND_DAI_SEC_AUXPCM_TX: + rate->min = rate->max = + aux_pcm_tx_cfg[SEC_AUX_PCM].sample_rate; + channels->min = channels->max = + aux_pcm_tx_cfg[SEC_AUX_PCM].channels; + break; + + case MSM_BACKEND_DAI_TERT_AUXPCM_RX: + rate->min = rate->max = + aux_pcm_rx_cfg[TERT_AUX_PCM].sample_rate; + channels->min = channels->max = + aux_pcm_rx_cfg[TERT_AUX_PCM].channels; + break; + + case MSM_BACKEND_DAI_TERT_AUXPCM_TX: + rate->min = rate->max = + aux_pcm_tx_cfg[TERT_AUX_PCM].sample_rate; + channels->min = channels->max = + aux_pcm_tx_cfg[TERT_AUX_PCM].channels; + break; + + case MSM_BACKEND_DAI_QUAT_AUXPCM_RX: + rate->min = rate->max = + aux_pcm_rx_cfg[QUAT_AUX_PCM].sample_rate; + channels->min = channels->max = + aux_pcm_rx_cfg[QUAT_AUX_PCM].channels; + break; + + case MSM_BACKEND_DAI_QUAT_AUXPCM_TX: + rate->min = rate->max = + aux_pcm_tx_cfg[QUAT_AUX_PCM].sample_rate; + channels->min = channels->max = + aux_pcm_tx_cfg[QUAT_AUX_PCM].channels; + break; + + case MSM_BACKEND_DAI_PRI_MI2S_RX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + mi2s_rx_cfg[PRIM_MI2S].bit_format); + rate->min = rate->max = mi2s_rx_cfg[PRIM_MI2S].sample_rate; + channels->min = channels->max = + mi2s_rx_cfg[PRIM_MI2S].channels; + break; + + case MSM_BACKEND_DAI_PRI_MI2S_TX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + mi2s_tx_cfg[PRIM_MI2S].bit_format); + rate->min = rate->max = mi2s_tx_cfg[PRIM_MI2S].sample_rate; + channels->min = channels->max = + mi2s_tx_cfg[PRIM_MI2S].channels; + break; + + case MSM_BACKEND_DAI_SECONDARY_MI2S_RX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + mi2s_rx_cfg[SEC_MI2S].bit_format); + rate->min = rate->max = mi2s_rx_cfg[SEC_MI2S].sample_rate; + channels->min = channels->max = + mi2s_rx_cfg[SEC_MI2S].channels; + break; + + case MSM_BACKEND_DAI_SECONDARY_MI2S_TX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + mi2s_tx_cfg[SEC_MI2S].bit_format); + rate->min = rate->max = mi2s_tx_cfg[SEC_MI2S].sample_rate; + channels->min = channels->max = + mi2s_tx_cfg[SEC_MI2S].channels; + break; + + case MSM_BACKEND_DAI_TERTIARY_MI2S_RX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + mi2s_rx_cfg[TERT_MI2S].bit_format); + rate->min = rate->max = mi2s_rx_cfg[TERT_MI2S].sample_rate; + channels->min = channels->max = + mi2s_rx_cfg[TERT_MI2S].channels; + break; + + case MSM_BACKEND_DAI_TERTIARY_MI2S_TX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + mi2s_tx_cfg[TERT_MI2S].bit_format); + rate->min = rate->max = mi2s_tx_cfg[TERT_MI2S].sample_rate; + channels->min = channels->max = + mi2s_tx_cfg[TERT_MI2S].channels; + break; + + case MSM_BACKEND_DAI_QUATERNARY_MI2S_RX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + mi2s_rx_cfg[QUAT_MI2S].bit_format); + rate->min = rate->max = mi2s_rx_cfg[QUAT_MI2S].sample_rate; + channels->min = channels->max = + mi2s_rx_cfg[QUAT_MI2S].channels; + break; + + case MSM_BACKEND_DAI_QUATERNARY_MI2S_TX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + mi2s_tx_cfg[QUAT_MI2S].bit_format); + rate->min = rate->max = mi2s_tx_cfg[QUAT_MI2S].sample_rate; + channels->min = channels->max = + mi2s_tx_cfg[QUAT_MI2S].channels; + break; + + default: + rate->min = rate->max = SAMPLING_RATE_48KHZ; + break; + } + +done: + return rc; +} + +static bool msm_swap_gnd_mic(struct snd_soc_codec *codec) +{ + struct snd_soc_card *card = codec->component.card; + struct msm_asoc_mach_data *pdata = + snd_soc_card_get_drvdata(card); + int value = 0; + + if (pdata->us_euro_gpio_p) { + value = msm_cdc_pinctrl_get_state(pdata->us_euro_gpio_p); + if (value) + msm_cdc_pinctrl_select_sleep_state( + pdata->us_euro_gpio_p); + else + msm_cdc_pinctrl_select_active_state( + pdata->us_euro_gpio_p); + } else if (pdata->us_euro_gpio >= 0) { + value = gpio_get_value_cansleep(pdata->us_euro_gpio); + gpio_set_value_cansleep(pdata->us_euro_gpio, !value); + } + pr_debug("%s: swap select switch %d to %d\n", __func__, value, !value); + return true; +} + +static int msm_afe_set_config(struct snd_soc_codec *codec) +{ + int ret = 0; + void *config_data = NULL; + + if (!msm_codec_fn.get_afe_config_fn) { + dev_err(codec->dev, "%s: codec get afe config not init'ed\n", + __func__); + return -EINVAL; + } + + config_data = msm_codec_fn.get_afe_config_fn(codec, + AFE_CDC_REGISTERS_CONFIG); + if (config_data) { + ret = afe_set_config(AFE_CDC_REGISTERS_CONFIG, config_data, 0); + if (ret) { + dev_err(codec->dev, + "%s: Failed to set codec registers config %d\n", + __func__, ret); + return ret; + } + } + + config_data = msm_codec_fn.get_afe_config_fn(codec, + AFE_CDC_REGISTER_PAGE_CONFIG); + if (config_data) { + ret = afe_set_config(AFE_CDC_REGISTER_PAGE_CONFIG, config_data, + 0); + if (ret) + dev_err(codec->dev, + "%s: Failed to set cdc register page config\n", + __func__); + } + + config_data = msm_codec_fn.get_afe_config_fn(codec, + AFE_SLIMBUS_SLAVE_CONFIG); + if (config_data) { + ret = afe_set_config(AFE_SLIMBUS_SLAVE_CONFIG, config_data, 0); + if (ret) { + dev_err(codec->dev, + "%s: Failed to set slimbus slave config %d\n", + __func__, ret); + return ret; + } + } + + return 0; +} + +static void msm_afe_clear_config(void) +{ + afe_clear_config(AFE_CDC_REGISTERS_CONFIG); + afe_clear_config(AFE_SLIMBUS_SLAVE_CONFIG); +} + +static int msm_adsp_power_up_config(struct snd_soc_codec *codec) +{ + int ret = 0; + unsigned long timeout; + int adsp_ready = 0; + + timeout = jiffies + + msecs_to_jiffies(ADSP_STATE_READY_TIMEOUT_MS); + + do { + if (q6core_is_adsp_ready()) { + pr_debug("%s: ADSP Audio is ready\n", __func__); + adsp_ready = 1; + break; + } + /* + * ADSP will be coming up after subsystem restart and + * it might not be fully up when the control reaches + * here. So, wait for 50msec before checking ADSP state + */ + msleep(50); + } while (time_after(timeout, jiffies)); + + if (!adsp_ready) { + pr_err("%s: timed out waiting for ADSP Audio\n", __func__); + ret = -ETIMEDOUT; + goto err_fail; + } + + ret = msm_afe_set_config(codec); + if (ret) + pr_err("%s: Failed to set AFE config. err %d\n", + __func__, ret); + + return 0; + +err_fail: + return ret; +} + +static int msm8998_notifier_service_cb(struct notifier_block *this, + unsigned long opcode, void *ptr) +{ + int ret; + struct snd_soc_card *card = NULL; + const char *be_dl_name = LPASS_BE_SLIMBUS_0_RX; + struct snd_soc_pcm_runtime *rtd; + struct snd_soc_codec *codec; + + pr_debug("%s: Service opcode 0x%lx\n", __func__, opcode); + + switch (opcode) { + case AUDIO_NOTIFIER_SERVICE_DOWN: + /* + * Use flag to ignore initial boot notifications + * On initial boot msm_adsp_power_up_config is + * called on init. There is no need to clear + * and set the config again on initial boot. + */ + if (is_initial_boot) + break; + msm_afe_clear_config(); + break; + case AUDIO_NOTIFIER_SERVICE_UP: + if (is_initial_boot) { + is_initial_boot = false; + break; + } + if (!spdev) + return -EINVAL; + + card = platform_get_drvdata(spdev); + rtd = snd_soc_get_pcm_runtime(card, be_dl_name); + if (!rtd) { + dev_err(card->dev, + "%s: snd_soc_get_pcm_runtime for %s failed!\n", + __func__, be_dl_name); + ret = -EINVAL; + goto done; + } + codec = rtd->codec; + + ret = msm_adsp_power_up_config(codec); + if (ret < 0) { + dev_err(card->dev, + "%s: msm_adsp_power_up_config failed ret = %d!\n", + __func__, ret); + goto done; + } + break; + default: + break; + } +done: + return NOTIFY_OK; +} + +static struct notifier_block service_nb = { + .notifier_call = msm8998_notifier_service_cb, + .priority = -INT_MAX, +}; + +static int msm_audrx_init(struct snd_soc_pcm_runtime *rtd) +{ + int ret = 0; + void *config_data; + struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_component *aux_comp; + struct snd_card *card; + struct snd_info_entry *entry; + struct msm_asoc_mach_data *pdata = + snd_soc_card_get_drvdata(rtd->card); + + /* Codec SLIMBUS configuration + * RX1, RX2, RX3, RX4, RX5, RX6, RX7, RX8, RX9, RX10, RX11, RX12, RX13 + * TX1, TX2, TX3, TX4, TX5, TX6, TX7, TX8, TX9, TX10, TX11, TX12, TX13 + * TX14, TX15, TX16 + */ + unsigned int rx_ch[TASHA_RX_MAX] = {144, 145, 146, 147, 148, 149, 150, + 151, 152, 153, 154, 155, 156}; + unsigned int tx_ch[TASHA_TX_MAX] = {128, 129, 130, 131, 132, 133, + 134, 135, 136, 137, 138, 139, + 140, 141, 142, 143}; + + /* Tavil Codec SLIMBUS configuration + * RX1, RX2, RX3, RX4, RX5, RX6, RX7, RX8 + * TX1, TX2, TX3, TX4, TX5, TX6, TX7, TX8, TX9, TX10, TX11, TX12, TX13 + * TX14, TX15, TX16 + */ + unsigned int rx_ch_tavil[WCD934X_RX_MAX] = {144, 145, 146, 147, 148, + 149, 150, 151}; + unsigned int tx_ch_tavil[WCD934X_TX_MAX] = {128, 129, 130, 131, 132, + 133, 134, 135, 136, 137, + 138, 139, 140, 141, 142, + 143}; + + pr_info("%s: dev_name%s\n", __func__, dev_name(cpu_dai->dev)); + + rtd->pmdown_time = 0; + + ret = snd_soc_add_codec_controls(codec, msm_snd_controls, + ARRAY_SIZE(msm_snd_controls)); + if (ret < 0) { + pr_err("%s: add_codec_controls failed, err %d\n", + __func__, ret); + return ret; + } + + snd_soc_dapm_new_controls(dapm, msm_dapm_widgets, + ARRAY_SIZE(msm_dapm_widgets)); + + if (!strcmp(dev_name(codec_dai->dev), "tasha_codec")) + snd_soc_dapm_add_routes(dapm, wcd_audio_paths_tasha, + ARRAY_SIZE(wcd_audio_paths_tasha)); + else + snd_soc_dapm_add_routes(dapm, wcd_audio_paths, + ARRAY_SIZE(wcd_audio_paths)); + + snd_soc_dapm_ignore_suspend(dapm, "Handset Mic"); + snd_soc_dapm_ignore_suspend(dapm, "Headset Mic"); + snd_soc_dapm_ignore_suspend(dapm, "ANCRight Headset Mic"); + snd_soc_dapm_ignore_suspend(dapm, "ANCLeft Headset Mic"); + snd_soc_dapm_ignore_suspend(dapm, "Digital Mic0"); + snd_soc_dapm_ignore_suspend(dapm, "Digital Mic1"); + snd_soc_dapm_ignore_suspend(dapm, "Digital Mic2"); + snd_soc_dapm_ignore_suspend(dapm, "Digital Mic3"); + snd_soc_dapm_ignore_suspend(dapm, "Digital Mic4"); + snd_soc_dapm_ignore_suspend(dapm, "Digital Mic5"); + snd_soc_dapm_ignore_suspend(dapm, "Analog Mic5"); + snd_soc_dapm_ignore_suspend(dapm, "Analog Mic6"); + snd_soc_dapm_ignore_suspend(dapm, "MADINPUT"); + snd_soc_dapm_ignore_suspend(dapm, "MAD_CPE_INPUT"); + snd_soc_dapm_ignore_suspend(dapm, "MAD_CPE_OUT1"); + snd_soc_dapm_ignore_suspend(dapm, "MAD_CPE_OUT2"); + snd_soc_dapm_ignore_suspend(dapm, "EAR"); + snd_soc_dapm_ignore_suspend(dapm, "LINEOUT1"); + snd_soc_dapm_ignore_suspend(dapm, "LINEOUT2"); + snd_soc_dapm_ignore_suspend(dapm, "ANC EAR"); + snd_soc_dapm_ignore_suspend(dapm, "SPK1 OUT"); + snd_soc_dapm_ignore_suspend(dapm, "SPK2 OUT"); + snd_soc_dapm_ignore_suspend(dapm, "HPHL"); + snd_soc_dapm_ignore_suspend(dapm, "HPHR"); + snd_soc_dapm_ignore_suspend(dapm, "AIF4 VI"); + snd_soc_dapm_ignore_suspend(dapm, "VIINPUT"); + snd_soc_dapm_ignore_suspend(dapm, "ANC HPHL"); + snd_soc_dapm_ignore_suspend(dapm, "ANC HPHR"); + + if (!strcmp(dev_name(codec_dai->dev), "tasha_codec")) { + snd_soc_dapm_ignore_suspend(dapm, "LINEOUT3"); + snd_soc_dapm_ignore_suspend(dapm, "LINEOUT4"); + snd_soc_dapm_ignore_suspend(dapm, "ANC LINEOUT1"); + snd_soc_dapm_ignore_suspend(dapm, "ANC LINEOUT2"); + } + + snd_soc_dapm_sync(dapm); + + if (!strcmp(dev_name(codec_dai->dev), "tavil_codec")) { + snd_soc_dai_set_channel_map(codec_dai, ARRAY_SIZE(tx_ch_tavil), + tx_ch_tavil, ARRAY_SIZE(rx_ch_tavil), + rx_ch_tavil); + } else { + snd_soc_dai_set_channel_map(codec_dai, ARRAY_SIZE(tx_ch), + tx_ch, ARRAY_SIZE(rx_ch), + rx_ch); + } + + if (!strcmp(dev_name(codec_dai->dev), "tavil_codec")) { + msm_codec_fn.get_afe_config_fn = tavil_get_afe_config; + } else { + msm_codec_fn.get_afe_config_fn = tasha_get_afe_config; + msm_codec_fn.mbhc_hs_detect_exit = tasha_mbhc_hs_detect_exit; + } + + ret = msm_adsp_power_up_config(codec); + if (ret) { + pr_err("%s: Failed to set AFE config %d\n", __func__, ret); + goto err_afe_cfg; + } + + config_data = msm_codec_fn.get_afe_config_fn(codec, + AFE_AANC_VERSION); + if (config_data) { + ret = afe_set_config(AFE_AANC_VERSION, config_data, 0); + if (ret) { + pr_err("%s: Failed to set aanc version %d\n", + __func__, ret); + goto err_afe_cfg; + } + } + + if (!strcmp(dev_name(codec_dai->dev), "tasha_codec")) { + config_data = msm_codec_fn.get_afe_config_fn(codec, + AFE_CDC_CLIP_REGISTERS_CONFIG); + if (config_data) { + ret = afe_set_config(AFE_CDC_CLIP_REGISTERS_CONFIG, + config_data, 0); + if (ret) { + pr_err("%s: Failed to set clip registers %d\n", + __func__, ret); + goto err_afe_cfg; + } + } + config_data = msm_codec_fn.get_afe_config_fn(codec, + AFE_CLIP_BANK_SEL); + if (config_data) { + ret = afe_set_config(AFE_CLIP_BANK_SEL, config_data, 0); + if (ret) { + pr_err("%s: Failed to set AFE bank selection %d\n", + __func__, ret); + goto err_afe_cfg; + } + } + } + + /* + * Send speaker configuration only for WSA8810. + * Defalut configuration is for WSA8815. + */ + pr_debug("%s: Number of aux devices: %d\n", + __func__, rtd->card->num_aux_devs); + if (!strcmp(dev_name(codec_dai->dev), "tavil_codec")) { + if (rtd->card->num_aux_devs && + !list_empty(&rtd->card->aux_comp_list)) { + aux_comp = list_first_entry(&rtd->card->aux_comp_list, + struct snd_soc_component, list_aux); + if (!strcmp(aux_comp->name, WSA8810_NAME_1) || + !strcmp(aux_comp->name, WSA8810_NAME_2)) { + tavil_set_spkr_mode(rtd->codec, + WCD934X_SPKR_MODE_1); + tavil_set_spkr_gain_offset(rtd->codec, + WCD934X_RX_GAIN_OFFSET_M1P5_DB); + } + } + card = rtd->card->snd_card; + entry = snd_info_create_subdir(card->module, "codecs", + card->proc_root); + if (!entry) { + pr_debug("%s: Cannot create codecs module entry\n", + __func__); + pdata->codec_root = NULL; + goto done; + } + pdata->codec_root = entry; + tavil_codec_info_create_codec_entry(pdata->codec_root, codec); + } else { + if (rtd->card->num_aux_devs && + !list_empty(&rtd->card->aux_comp_list)) { + aux_comp = list_first_entry(&rtd->card->aux_comp_list, + struct snd_soc_component, list_aux); + if (!strcmp(aux_comp->name, WSA8810_NAME_1) || + !strcmp(aux_comp->name, WSA8810_NAME_2)) { + tasha_set_spkr_mode(rtd->codec, SPKR_MODE_1); + tasha_set_spkr_gain_offset(rtd->codec, + RX_GAIN_OFFSET_M1P5_DB); + } + } + card = rtd->card->snd_card; + entry = snd_info_create_subdir(card->module, "codecs", + card->proc_root); + if (!entry) { + pr_debug("%s: Cannot create codecs module entry\n", + __func__); + ret = 0; + goto err_snd_module; + } + pdata->codec_root = entry; + tasha_codec_info_create_codec_entry(pdata->codec_root, codec); + } +done: + codec_reg_done = true; + return 0; + +err_snd_module: +err_afe_cfg: + return ret; +} + +static int msm_wcn_init(struct snd_soc_pcm_runtime *rtd) +{ + unsigned int rx_ch[WCN_CDC_SLIM_RX_CH_MAX] = {157, 158}; + unsigned int tx_ch[WCN_CDC_SLIM_TX_CH_MAX] = {159, 160, 161}; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + + return snd_soc_dai_set_channel_map(codec_dai, ARRAY_SIZE(tx_ch), + tx_ch, ARRAY_SIZE(rx_ch), rx_ch); +} + +static void *def_tasha_mbhc_cal(void) +{ + void *tasha_wcd_cal; + struct wcd_mbhc_btn_detect_cfg *btn_cfg; + u16 *btn_high; + + tasha_wcd_cal = kzalloc(WCD_MBHC_CAL_SIZE(WCD_MBHC_DEF_BUTTONS, + WCD9XXX_MBHC_DEF_RLOADS), GFP_KERNEL); + if (!tasha_wcd_cal) + return NULL; + +#define S(X, Y) ((WCD_MBHC_CAL_PLUG_TYPE_PTR(tasha_wcd_cal)->X) = (Y)) + S(v_hs_max, 1600); +#undef S +#define S(X, Y) ((WCD_MBHC_CAL_BTN_DET_PTR(tasha_wcd_cal)->X) = (Y)) + S(num_btn, WCD_MBHC_DEF_BUTTONS); +#undef S + + btn_cfg = WCD_MBHC_CAL_BTN_DET_PTR(tasha_wcd_cal); + btn_high = ((void *)&btn_cfg->_v_btn_low) + + (sizeof(btn_cfg->_v_btn_low[0]) * btn_cfg->num_btn); + + btn_high[0] = 75; + btn_high[1] = 150; + btn_high[2] = 237; + btn_high[3] = 500; + btn_high[4] = 500; + btn_high[5] = 500; + btn_high[6] = 500; + btn_high[7] = 500; + + return tasha_wcd_cal; +} + +static void *def_tavil_mbhc_cal(void) +{ + void *tavil_wcd_cal; + struct wcd_mbhc_btn_detect_cfg *btn_cfg; + u16 *btn_high; + + tavil_wcd_cal = kzalloc(WCD_MBHC_CAL_SIZE(WCD_MBHC_DEF_BUTTONS, + WCD9XXX_MBHC_DEF_RLOADS), GFP_KERNEL); + if (!tavil_wcd_cal) + return NULL; + +#define S(X, Y) ((WCD_MBHC_CAL_PLUG_TYPE_PTR(tavil_wcd_cal)->X) = (Y)) + S(v_hs_max, 1600); +#undef S +#define S(X, Y) ((WCD_MBHC_CAL_BTN_DET_PTR(tavil_wcd_cal)->X) = (Y)) + S(num_btn, WCD_MBHC_DEF_BUTTONS); +#undef S + + btn_cfg = WCD_MBHC_CAL_BTN_DET_PTR(tavil_wcd_cal); + btn_high = ((void *)&btn_cfg->_v_btn_low) + + (sizeof(btn_cfg->_v_btn_low[0]) * btn_cfg->num_btn); + + btn_high[0] = 75; + btn_high[1] = 150; + btn_high[2] = 237; + btn_high[3] = 500; + btn_high[4] = 500; + btn_high[5] = 500; + btn_high[6] = 500; + btn_high[7] = 500; + + return tavil_wcd_cal; +} + +static int msm_snd_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai_link *dai_link = rtd->dai_link; + + int ret = 0; + u32 rx_ch[SLIM_MAX_RX_PORTS], tx_ch[SLIM_MAX_TX_PORTS]; + u32 rx_ch_cnt = 0, tx_ch_cnt = 0; + u32 user_set_tx_ch = 0; + u32 rx_ch_count; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + ret = snd_soc_dai_get_channel_map(codec_dai, + &tx_ch_cnt, tx_ch, &rx_ch_cnt, rx_ch); + if (ret < 0) { + pr_err("%s: failed to get codec chan map, err:%d\n", + __func__, ret); + goto err_ch_map; + } + if (dai_link->id == MSM_BACKEND_DAI_SLIMBUS_5_RX) { + pr_debug("%s: rx_5_ch=%d\n", __func__, + slim_rx_cfg[5].channels); + rx_ch_count = slim_rx_cfg[5].channels; + } else if (dai_link->id == MSM_BACKEND_DAI_SLIMBUS_2_RX) { + pr_debug("%s: rx_2_ch=%d\n", __func__, + slim_rx_cfg[2].channels); + rx_ch_count = slim_rx_cfg[2].channels; + } else if (dai_link->id == MSM_BACKEND_DAI_SLIMBUS_6_RX) { + pr_debug("%s: rx_6_ch=%d\n", __func__, + slim_rx_cfg[6].channels); + rx_ch_count = slim_rx_cfg[6].channels; + } else { + pr_debug("%s: rx_0_ch=%d\n", __func__, + slim_rx_cfg[0].channels); + rx_ch_count = slim_rx_cfg[0].channels; + } + ret = snd_soc_dai_set_channel_map(cpu_dai, 0, 0, + rx_ch_count, rx_ch); + if (ret < 0) { + pr_err("%s: failed to set cpu chan map, err:%d\n", + __func__, ret); + goto err_ch_map; + } + } else { + + pr_debug("%s: %s_tx_dai_id_%d_ch=%d\n", __func__, + codec_dai->name, codec_dai->id, user_set_tx_ch); + ret = snd_soc_dai_get_channel_map(codec_dai, + &tx_ch_cnt, tx_ch, &rx_ch_cnt, rx_ch); + if (ret < 0) { + pr_err("%s: failed to get codec chan map\n, err:%d\n", + __func__, ret); + goto err_ch_map; + } + /* For _tx1 case */ + if (dai_link->id == MSM_BACKEND_DAI_SLIMBUS_0_TX) + user_set_tx_ch = slim_tx_cfg[0].channels; + /* For _tx3 case */ + else if (dai_link->id == MSM_BACKEND_DAI_SLIMBUS_1_TX) + user_set_tx_ch = slim_tx_cfg[1].channels; + else if (dai_link->id == MSM_BACKEND_DAI_SLIMBUS_4_TX) + user_set_tx_ch = msm_vi_feed_tx_ch; + else + user_set_tx_ch = tx_ch_cnt; + + pr_debug("%s: msm_slim_0_tx_ch(%d) user_set_tx_ch(%d) tx_ch_cnt(%d), BE id (%d)\n", + __func__, slim_tx_cfg[0].channels, user_set_tx_ch, + tx_ch_cnt, dai_link->id); + + ret = snd_soc_dai_set_channel_map(cpu_dai, + user_set_tx_ch, tx_ch, 0, 0); + if (ret < 0) + pr_err("%s: failed to set cpu chan map, err:%d\n", + __func__, ret); + } + +err_ch_map: + return ret; +} + +static int msm_snd_cpe_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai_link *dai_link = rtd->dai_link; + + int ret = 0; + u32 tx_ch[SLIM_MAX_TX_PORTS]; + u32 tx_ch_cnt = 0; + u32 user_set_tx_ch = 0; + + if (substream->stream != SNDRV_PCM_STREAM_CAPTURE) { + pr_err("%s: Invalid stream type %d\n", + __func__, substream->stream); + ret = -EINVAL; + goto err_stream_type; + } + + pr_debug("%s: %s_tx_dai_id_%d\n", __func__, + codec_dai->name, codec_dai->id); + ret = snd_soc_dai_get_channel_map(codec_dai, + &tx_ch_cnt, tx_ch, NULL, NULL); + if (ret < 0) { + pr_err("%s: failed to get codec chan map\n, err:%d\n", + __func__, ret); + goto err_ch_map; + } + + user_set_tx_ch = tx_ch_cnt; + + pr_debug("%s: tx_ch_cnt(%d) BE id %d\n", + __func__, tx_ch_cnt, dai_link->id); + + ret = snd_soc_dai_set_channel_map(cpu_dai, + user_set_tx_ch, tx_ch, 0, 0); + if (ret < 0) + pr_err("%s: failed to set cpu chan map, err:%d\n", + __func__, ret); +err_ch_map: +err_stream_type: + return ret; +} + +static int msm_slimbus_2_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + unsigned int rx_ch[SLIM_MAX_RX_PORTS], tx_ch[SLIM_MAX_TX_PORTS]; + unsigned int rx_ch_cnt = 0, tx_ch_cnt = 0; + unsigned int num_tx_ch = 0; + unsigned int num_rx_ch = 0; + int ret = 0; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + num_rx_ch = params_channels(params); + pr_debug("%s: %s rx_dai_id = %d num_ch = %d\n", __func__, + codec_dai->name, codec_dai->id, num_rx_ch); + ret = snd_soc_dai_get_channel_map(codec_dai, + &tx_ch_cnt, tx_ch, &rx_ch_cnt, rx_ch); + if (ret < 0) { + pr_err("%s: failed to get codec chan map, err:%d\n", + __func__, ret); + goto err_ch_map; + } + ret = snd_soc_dai_set_channel_map(cpu_dai, 0, 0, + num_rx_ch, rx_ch); + if (ret < 0) { + pr_err("%s: failed to set cpu chan map, err:%d\n", + __func__, ret); + goto err_ch_map; + } + } else { + num_tx_ch = params_channels(params); + pr_debug("%s: %s tx_dai_id = %d num_ch = %d\n", __func__, + codec_dai->name, codec_dai->id, num_tx_ch); + ret = snd_soc_dai_get_channel_map(codec_dai, + &tx_ch_cnt, tx_ch, &rx_ch_cnt, rx_ch); + if (ret < 0) { + pr_err("%s: failed to get codec chan map, err:%d\n", + __func__, ret); + goto err_ch_map; + } + ret = snd_soc_dai_set_channel_map(cpu_dai, + num_tx_ch, tx_ch, 0, 0); + if (ret < 0) { + pr_err("%s: failed to set cpu chan map, err:%d\n", + __func__, ret); + goto err_ch_map; + } + } + +err_ch_map: + return ret; +} + +static int msm_wcn_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai_link *dai_link = rtd->dai_link; + u32 rx_ch[WCN_CDC_SLIM_RX_CH_MAX], tx_ch[WCN_CDC_SLIM_TX_CH_MAX]; + u32 rx_ch_cnt = 0, tx_ch_cnt = 0; + int ret; + + dev_dbg(rtd->dev, "%s: %s_tx_dai_id_%d\n", __func__, + codec_dai->name, codec_dai->id); + ret = snd_soc_dai_get_channel_map(codec_dai, + &tx_ch_cnt, tx_ch, &rx_ch_cnt, rx_ch); + if (ret) { + dev_err(rtd->dev, + "%s: failed to get BTFM codec chan map\n, err:%d\n", + __func__, ret); + goto exit; + } + + dev_dbg(rtd->dev, "%s: tx_ch_cnt(%d) BE id %d\n", + __func__, tx_ch_cnt, dai_link->id); + + ret = snd_soc_dai_set_channel_map(cpu_dai, + tx_ch_cnt, tx_ch, rx_ch_cnt, rx_ch); + if (ret) + dev_err(rtd->dev, "%s: failed to set cpu chan map, err:%d\n", + __func__, ret); + +exit: + return ret; +} + +static int msm_aux_pcm_snd_startup(struct snd_pcm_substream *substream) +{ + int ret = 0; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + int index = cpu_dai->id - 1; + + dev_dbg(rtd->card->dev, + "%s: substream = %s stream = %d, dai name %s, dai ID %d\n", + __func__, substream->name, substream->stream, + cpu_dai->name, cpu_dai->id); + + if (index < PRIM_AUX_PCM || index > QUAT_AUX_PCM) { + ret = -EINVAL; + dev_err(rtd->card->dev, + "%s: CPU DAI id (%d) out of range\n", + __func__, cpu_dai->id); + goto done; + } + + mutex_lock(&auxpcm_intf_conf[index].lock); + if (++auxpcm_intf_conf[index].ref_cnt == 1) { + if (mi2s_auxpcm_conf[index].pcm_i2s_sel_vt_addr != NULL) { + mutex_lock(&mi2s_auxpcm_conf[index].lock); + iowrite32(1, + mi2s_auxpcm_conf[index].pcm_i2s_sel_vt_addr); + mutex_unlock(&mi2s_auxpcm_conf[index].lock); + } else { + dev_err(rtd->card->dev, + "%s lpaif_tert_muxsel_virt_addr is NULL\n", + __func__); + ret = -EINVAL; + } + } + if (ret < 0) + auxpcm_intf_conf[index].ref_cnt--; + + mutex_unlock(&auxpcm_intf_conf[index].lock); + +done: + return ret; +} + +static void msm_aux_pcm_snd_shutdown(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + int index = rtd->cpu_dai->id - 1; + + dev_dbg(rtd->card->dev, + "%s: substream = %s stream = %d, dai name %s, dai ID %d\n", + __func__, + substream->name, substream->stream, + rtd->cpu_dai->name, rtd->cpu_dai->id); + + if (index < PRIM_AUX_PCM || index > QUAT_AUX_PCM) { + dev_err(rtd->card->dev, + "%s: CPU DAI id (%d) out of range\n", + __func__, rtd->cpu_dai->id); + return; + } + + mutex_lock(&auxpcm_intf_conf[index].lock); + if (--auxpcm_intf_conf[index].ref_cnt == 0) { + if (mi2s_auxpcm_conf[index].pcm_i2s_sel_vt_addr != NULL) { + mutex_lock(&mi2s_auxpcm_conf[index].lock); + iowrite32(0, + mi2s_auxpcm_conf[index].pcm_i2s_sel_vt_addr); + mutex_unlock(&mi2s_auxpcm_conf[index].lock); + } else { + dev_err(rtd->card->dev, + "%s lpaif_tert_muxsel_virt_addr is NULL\n", + __func__); + } + } + mutex_unlock(&auxpcm_intf_conf[index].lock); +} + +static int msm_get_port_id(int be_id) +{ + int afe_port_id; + + switch (be_id) { + case MSM_BACKEND_DAI_PRI_MI2S_RX: + afe_port_id = AFE_PORT_ID_PRIMARY_MI2S_RX; + break; + case MSM_BACKEND_DAI_PRI_MI2S_TX: + afe_port_id = AFE_PORT_ID_PRIMARY_MI2S_TX; + break; + case MSM_BACKEND_DAI_SECONDARY_MI2S_RX: + afe_port_id = AFE_PORT_ID_SECONDARY_MI2S_RX; + break; + case MSM_BACKEND_DAI_SECONDARY_MI2S_TX: + afe_port_id = AFE_PORT_ID_SECONDARY_MI2S_TX; + break; + case MSM_BACKEND_DAI_TERTIARY_MI2S_RX: + afe_port_id = AFE_PORT_ID_TERTIARY_MI2S_RX; + break; + case MSM_BACKEND_DAI_TERTIARY_MI2S_TX: + afe_port_id = AFE_PORT_ID_TERTIARY_MI2S_TX; + break; + case MSM_BACKEND_DAI_QUATERNARY_MI2S_RX: + afe_port_id = AFE_PORT_ID_QUATERNARY_MI2S_RX; + break; + case MSM_BACKEND_DAI_QUATERNARY_MI2S_TX: + afe_port_id = AFE_PORT_ID_QUATERNARY_MI2S_TX; + break; + default: + pr_err("%s: Invalid BE id: %d\n", __func__, be_id); + afe_port_id = -EINVAL; + } + + return afe_port_id; +} + +static u32 get_mi2s_bits_per_sample(u32 bit_format) +{ + u32 bit_per_sample; + + switch (bit_format) { + case SNDRV_PCM_FORMAT_S32_LE: + case SNDRV_PCM_FORMAT_S24_3LE: + case SNDRV_PCM_FORMAT_S24_LE: + bit_per_sample = 32; + break; + case SNDRV_PCM_FORMAT_S16_LE: + default: + bit_per_sample = 16; + break; + } + + return bit_per_sample; +} + +static void update_mi2s_clk_val(int dai_id, int stream) +{ + u32 bit_per_sample; + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + bit_per_sample = + get_mi2s_bits_per_sample(mi2s_rx_cfg[dai_id].bit_format); + mi2s_clk[dai_id].clk_freq_in_hz = + mi2s_rx_cfg[dai_id].sample_rate * 2 * bit_per_sample; + } else { + bit_per_sample = + get_mi2s_bits_per_sample(mi2s_tx_cfg[dai_id].bit_format); + mi2s_clk[dai_id].clk_freq_in_hz = + mi2s_tx_cfg[dai_id].sample_rate * 2 * bit_per_sample; + } +} + +static int msm_mi2s_set_sclk(struct snd_pcm_substream *substream, bool enable) +{ + int ret = 0; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + int port_id = 0; + int index = cpu_dai->id; + + port_id = msm_get_port_id(rtd->dai_link->id); + if (port_id < 0) { + dev_err(rtd->card->dev, "%s: Invalid port_id\n", __func__); + ret = port_id; + goto done; + } + + if (enable) { + update_mi2s_clk_val(index, substream->stream); + dev_dbg(rtd->card->dev, "%s: clock rate %ul\n", __func__, + mi2s_clk[index].clk_freq_in_hz); + } + + mi2s_clk[index].enable = enable; + ret = afe_set_lpass_clock_v2(port_id, + &mi2s_clk[index]); + if (ret < 0) { + dev_err(rtd->card->dev, + "%s: afe lpass clock failed for port 0x%x , err:%d\n", + __func__, port_id, ret); + goto done; + } + +done: + return ret; +} + +static int msm_set_pinctrl(struct msm_pinctrl_info *pinctrl_info, + enum pinctrl_pin_state new_state) +{ + int ret = 0; + int curr_state = 0; + + if (pinctrl_info == NULL) { + pr_err("%s: pinctrl_info is NULL\n", __func__); + ret = -EINVAL; + goto err; + } + + if (pinctrl_info->pinctrl == NULL) { + pr_err("%s: pinctrl_info->pinctrl is NULL\n", __func__); + ret = -EINVAL; + goto err; + } + + curr_state = pinctrl_info->curr_state; + pinctrl_info->curr_state = new_state; + pr_debug("%s: curr_state = %s new_state = %s\n", __func__, + pin_states[curr_state], pin_states[pinctrl_info->curr_state]); + + if (curr_state == pinctrl_info->curr_state) { + pr_debug("%s: Already in same state\n", __func__); + goto err; + } + + if (curr_state != STATE_DISABLE && + pinctrl_info->curr_state != STATE_DISABLE) { + pr_debug("%s: state already active cannot switch\n", __func__); + ret = -EIO; + goto err; + } + + switch (pinctrl_info->curr_state) { + case STATE_MI2S_ACTIVE: + ret = pinctrl_select_state(pinctrl_info->pinctrl, + pinctrl_info->mi2s_active); + if (ret) { + pr_err("%s: MI2S state select failed with %d\n", + __func__, ret); + ret = -EIO; + goto err; + } + break; + case STATE_TDM_ACTIVE: + ret = pinctrl_select_state(pinctrl_info->pinctrl, + pinctrl_info->tdm_active); + if (ret) { + pr_err("%s: TDM state select failed with %d\n", + __func__, ret); + ret = -EIO; + goto err; + } + break; + case STATE_DISABLE: + if (curr_state == STATE_MI2S_ACTIVE) { + ret = pinctrl_select_state(pinctrl_info->pinctrl, + pinctrl_info->mi2s_disable); + } else { + ret = pinctrl_select_state(pinctrl_info->pinctrl, + pinctrl_info->tdm_disable); + } + if (ret) { + pr_err("%s: state disable failed with %d\n", + __func__, ret); + ret = -EIO; + goto err; + } + break; + default: + pr_err("%s: TLMM pin state is invalid\n", __func__); + return -EINVAL; + } + +err: + return ret; +} + +static void msm_release_pinctrl(struct platform_device *pdev) +{ + struct snd_soc_card *card = platform_get_drvdata(pdev); + struct msm_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card); + struct msm_pinctrl_info *pinctrl_info = &pdata->pinctrl_info; + + if (pinctrl_info->pinctrl) { + devm_pinctrl_put(pinctrl_info->pinctrl); + pinctrl_info->pinctrl = NULL; + } +} + +static int msm_get_pinctrl(struct platform_device *pdev) +{ + struct snd_soc_card *card = platform_get_drvdata(pdev); + struct msm_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card); + struct msm_pinctrl_info *pinctrl_info = NULL; + struct pinctrl *pinctrl; + int ret; + + pinctrl_info = &pdata->pinctrl_info; + + if (pinctrl_info == NULL) { + pr_err("%s: pinctrl_info is NULL\n", __func__); + return -EINVAL; + } + + pinctrl = devm_pinctrl_get(&pdev->dev); + if (IS_ERR_OR_NULL(pinctrl)) { + pr_err("%s: Unable to get pinctrl handle\n", __func__); + return -EINVAL; + } + pinctrl_info->pinctrl = pinctrl; + + /* get all the states handles from Device Tree */ + pinctrl_info->mi2s_disable = pinctrl_lookup_state(pinctrl, + "quat-mi2s-sleep"); + if (IS_ERR(pinctrl_info->mi2s_disable)) { + pr_err("%s: could not get mi2s_disable pinstate\n", __func__); + goto err; + } + pinctrl_info->mi2s_active = pinctrl_lookup_state(pinctrl, + "quat-mi2s-active"); + if (IS_ERR(pinctrl_info->mi2s_active)) { + pr_err("%s: could not get mi2s_active pinstate\n", __func__); + goto err; + } + pinctrl_info->tdm_disable = pinctrl_lookup_state(pinctrl, + "quat-tdm-sleep"); + if (IS_ERR(pinctrl_info->tdm_disable)) { + pr_err("%s: could not get tdm_disable pinstate\n", __func__); + goto err; + } + pinctrl_info->tdm_active = pinctrl_lookup_state(pinctrl, + "quat-tdm-active"); + if (IS_ERR(pinctrl_info->tdm_active)) { + pr_err("%s: could not get tdm_active pinstate\n", + __func__); + goto err; + } + /* Reset the TLMM pins to a default state */ + ret = pinctrl_select_state(pinctrl_info->pinctrl, + pinctrl_info->mi2s_disable); + if (ret != 0) { + pr_err("%s: Disable TLMM pins failed with %d\n", + __func__, ret); + ret = -EIO; + goto err; + } + pinctrl_info->curr_state = STATE_DISABLE; + + return 0; + +err: + devm_pinctrl_put(pinctrl); + pinctrl_info->pinctrl = NULL; + return -EINVAL; +} + +static int msm_tdm_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + + if (cpu_dai->id == AFE_PORT_ID_QUATERNARY_TDM_RX) { + channels->min = channels->max = + tdm_rx_cfg[TDM_QUAT][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_QUAT][TDM_0].bit_format); + rate->min = rate->max = + tdm_rx_cfg[TDM_QUAT][TDM_0].sample_rate; + } else if (cpu_dai->id == AFE_PORT_ID_SECONDARY_TDM_RX) { + channels->min = channels->max = + tdm_rx_cfg[TDM_SEC][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_SEC][TDM_0].bit_format); + rate->min = rate->max = tdm_rx_cfg[TDM_SEC][TDM_0].sample_rate; + } else { + pr_err("%s: dai id 0x%x not supported\n", + __func__, cpu_dai->id); + return -EINVAL; + } + + pr_debug("%s: dai id = 0x%x channels = %d rate = %d format = 0x%x\n", + __func__, cpu_dai->id, channels->max, rate->max, + params_format(params)); + + return 0; +} + +static int msm8998_tdm_snd_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + int ret = 0; + int channels, slot_width, slots; + unsigned int slot_mask; + unsigned int slot_offset[8] = {0, 4, 8, 12, 16, 20, 24, 28}; + + pr_debug("%s: dai id = 0x%x\n", __func__, cpu_dai->id); + + slots = tdm_rx_cfg[TDM_QUAT][TDM_0].channels; + /*2 slot config - bits 0 and 1 set for the first two slots */ + slot_mask = 0x0000FFFF >> (16-slots); + slot_width = 32; + channels = slots; + + pr_debug("%s: slot_width %d slots %d\n", __func__, slot_width, slots); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + pr_debug("%s: slot_width %d\n", __func__, slot_width); + ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0, slot_mask, + slots, slot_width); + if (ret < 0) { + pr_err("%s: failed to set tdm slot, err:%d\n", + __func__, ret); + goto end; + } + + ret = snd_soc_dai_set_channel_map(cpu_dai, + 0, NULL, channels, slot_offset); + if (ret < 0) { + pr_err("%s: failed to set channel map, err:%d\n", + __func__, ret); + goto end; + } + } else { + pr_err("%s: invalid use case, err:%d\n", + __func__, ret); + } + +end: + return ret; +} + +static int msm8998_tdm_snd_startup(struct snd_pcm_substream *substream) +{ + int ret = 0; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_card *card = rtd->card; + struct msm_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card); + struct msm_pinctrl_info *pinctrl_info = &pdata->pinctrl_info; + + ret = msm_set_pinctrl(pinctrl_info, STATE_TDM_ACTIVE); + if (ret) + pr_err("%s: MI2S TLMM pinctrl set failed with %d\n", + __func__, ret); + + return ret; +} + +static void msm8998_tdm_snd_shutdown(struct snd_pcm_substream *substream) +{ + int ret = 0; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_card *card = rtd->card; + struct msm_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card); + struct msm_pinctrl_info *pinctrl_info = &pdata->pinctrl_info; + + ret = msm_set_pinctrl(pinctrl_info, STATE_DISABLE); + if (ret) + pr_err("%s: MI2S TLMM pinctrl set failed with %d\n", + __func__, ret); + +} + +static struct snd_soc_ops msm8998_tdm_be_ops = { + .hw_params = msm8998_tdm_snd_hw_params, + .startup = msm8998_tdm_snd_startup, + .shutdown = msm8998_tdm_snd_shutdown +}; + +static int msm_mi2s_snd_startup(struct snd_pcm_substream *substream) +{ + int ret = 0; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + int index = cpu_dai->id; + unsigned int fmt = SND_SOC_DAIFMT_CBS_CFS; + struct snd_soc_card *card = rtd->card; + struct msm_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card); + struct msm_pinctrl_info *pinctrl_info = &pdata->pinctrl_info; + int ret_pinctrl = 0; + + dev_dbg(rtd->card->dev, + "%s: substream = %s stream = %d, dai name %s, dai ID %d\n", + __func__, substream->name, substream->stream, + cpu_dai->name, cpu_dai->id); + + if (index < PRIM_MI2S || index > QUAT_MI2S) { + ret = -EINVAL; + dev_err(rtd->card->dev, + "%s: CPU DAI id (%d) out of range\n", + __func__, cpu_dai->id); + goto done; + } + if (index == QUAT_MI2S) { + ret_pinctrl = msm_set_pinctrl(pinctrl_info, STATE_MI2S_ACTIVE); + if (ret_pinctrl) { + pr_err("%s: MI2S TLMM pinctrl set failed with %d\n", + __func__, ret_pinctrl); + } + } + + /* + * Muxtex protection in case the same MI2S + * interface using for both TX and RX so + * that the same clock won't be enable twice. + */ + mutex_lock(&mi2s_intf_conf[index].lock); + if (++mi2s_intf_conf[index].ref_cnt == 1) { + ret = msm_mi2s_set_sclk(substream, true); + if (ret < 0) { + dev_err(rtd->card->dev, + "%s: afe lpass clock failed to enable MI2S clock, err:%d\n", + __func__, ret); + goto clean_up; + } + if (mi2s_auxpcm_conf[index].pcm_i2s_sel_vt_addr != NULL) { + mutex_lock(&mi2s_auxpcm_conf[index].lock); + iowrite32(0, + mi2s_auxpcm_conf[index].pcm_i2s_sel_vt_addr); + mutex_unlock(&mi2s_auxpcm_conf[index].lock); + } else { + dev_err(rtd->card->dev, + "%s lpaif_muxsel_virt_addr is NULL for dai %d\n", + __func__, index); + ret = -EINVAL; + goto clk_off; + } + /* Check if msm needs to provide the clock to the interface */ + if (!mi2s_intf_conf[index].msm_is_mi2s_master) + fmt = SND_SOC_DAIFMT_CBM_CFM; + ret = snd_soc_dai_set_fmt(cpu_dai, fmt); + if (ret < 0) { + pr_err("%s: set fmt cpu dai failed for MI2S (%d), err:%d\n", + __func__, index, ret); + goto clk_off; + } + } +clk_off: + if (ret < 0) + msm_mi2s_set_sclk(substream, false); +clean_up: + if (ret < 0) + mi2s_intf_conf[index].ref_cnt--; + mutex_unlock(&mi2s_intf_conf[index].lock); +done: + return ret; +} + +static void msm_mi2s_snd_shutdown(struct snd_pcm_substream *substream) +{ + int ret; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + int index = rtd->cpu_dai->id; + struct snd_soc_card *card = rtd->card; + struct msm_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card); + struct msm_pinctrl_info *pinctrl_info = &pdata->pinctrl_info; + int ret_pinctrl = 0; + + pr_debug("%s(): substream = %s stream = %d\n", __func__, + substream->name, substream->stream); + if (index < PRIM_MI2S || index > QUAT_MI2S) { + pr_err("%s:invalid MI2S DAI(%d)\n", __func__, index); + return; + } + + mutex_lock(&mi2s_intf_conf[index].lock); + if (--mi2s_intf_conf[index].ref_cnt == 0) { + ret = msm_mi2s_set_sclk(substream, false); + if (ret < 0) + pr_err("%s:clock disable failed for MI2S (%d); ret=%d\n", + __func__, index, ret); + } + mutex_unlock(&mi2s_intf_conf[index].lock); + + if (index == QUAT_MI2S) { + ret_pinctrl = msm_set_pinctrl(pinctrl_info, STATE_DISABLE); + if (ret_pinctrl) + pr_err("%s: MI2S TLMM pinctrl set failed with %d\n", + __func__, ret_pinctrl); + } +} + +static struct snd_soc_ops msm_mi2s_be_ops = { + .startup = msm_mi2s_snd_startup, + .shutdown = msm_mi2s_snd_shutdown, +}; + +static struct snd_soc_ops msm_aux_pcm_be_ops = { + .startup = msm_aux_pcm_snd_startup, + .shutdown = msm_aux_pcm_snd_shutdown, +}; + +static unsigned int tdm_param_set_slot_mask(u16 port_id, int slot_width, + int slots) +{ + unsigned int slot_mask = 0; + int i, j; + unsigned int *slot_offset; + + for (i = TDM_0; i < TDM_PORT_MAX; i++) { + slot_offset = tdm_slot_offset[i]; + + for (j = 0; j < TDM_SLOT_OFFSET_MAX; j++) { + if (slot_offset[j] != AFE_SLOT_MAPPING_OFFSET_INVALID) + slot_mask |= + (1 << ((slot_offset[j] * 8) / slot_width)); + else + break; + } + } + + return slot_mask; +} + +static int msm_tdm_snd_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + int ret = 0; + int channels, slot_width, slots; + unsigned int slot_mask; + unsigned int *slot_offset; + int offset_channels = 0; + int i; + + pr_debug("%s: dai id = 0x%x\n", __func__, cpu_dai->id); + + channels = params_channels(params); + switch (channels) { + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + case 8: + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S32_LE: + case SNDRV_PCM_FORMAT_S24_LE: + case SNDRV_PCM_FORMAT_S16_LE: + /* + * up to 8 channels HW config should + * use 32 bit slot width for max support of + * stream bit width. (slot_width > bit_width) + */ + slot_width = 32; + break; + default: + pr_err("%s: invalid param format 0x%x\n", + __func__, params_format(params)); + return -EINVAL; + } + slots = 8; + slot_mask = tdm_param_set_slot_mask(cpu_dai->id, + slot_width, + slots); + if (!slot_mask) { + pr_err("%s: invalid slot_mask 0x%x\n", + __func__, slot_mask); + return -EINVAL; + } + break; + default: + pr_err("%s: invalid param channels %d\n", + __func__, channels); + return -EINVAL; + } + /* currently only supporting TDM_RX_0 and TDM_TX_0 */ + switch (cpu_dai->id) { + case AFE_PORT_ID_PRIMARY_TDM_RX: + case AFE_PORT_ID_SECONDARY_TDM_RX: + case AFE_PORT_ID_TERTIARY_TDM_RX: + case AFE_PORT_ID_QUATERNARY_TDM_RX: + case AFE_PORT_ID_PRIMARY_TDM_TX: + case AFE_PORT_ID_SECONDARY_TDM_TX: + case AFE_PORT_ID_TERTIARY_TDM_TX: + case AFE_PORT_ID_QUATERNARY_TDM_TX: + slot_offset = tdm_slot_offset[TDM_0]; + break; + default: + pr_err("%s: dai id 0x%x not supported\n", + __func__, cpu_dai->id); + return -EINVAL; + } + + for (i = 0; i < TDM_SLOT_OFFSET_MAX; i++) { + if (slot_offset[i] != AFE_SLOT_MAPPING_OFFSET_INVALID) + offset_channels++; + else + break; + } + + if (offset_channels == 0) { + pr_err("%s: slot offset not supported, offset_channels %d\n", + __func__, offset_channels); + return -EINVAL; + } + + if (channels > offset_channels) { + pr_err("%s: channels %d exceed offset_channels %d\n", + __func__, channels, offset_channels); + return -EINVAL; + } + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0, slot_mask, + slots, slot_width); + if (ret < 0) { + pr_err("%s: failed to set tdm slot, err:%d\n", + __func__, ret); + goto end; + } + + ret = snd_soc_dai_set_channel_map(cpu_dai, 0, NULL, + channels, slot_offset); + if (ret < 0) { + pr_err("%s: failed to set channel map, err:%d\n", + __func__, ret); + goto end; + } + } else { + ret = snd_soc_dai_set_tdm_slot(cpu_dai, slot_mask, 0, + slots, slot_width); + if (ret < 0) { + pr_err("%s: failed to set tdm slot, err:%d\n", + __func__, ret); + goto end; + } + + ret = snd_soc_dai_set_channel_map(cpu_dai, channels, + slot_offset, 0, NULL); + if (ret < 0) { + pr_err("%s: failed to set channel map, err:%d\n", + __func__, ret); + goto end; + } + } +end: + return ret; +} + +static struct snd_soc_ops msm_be_ops = { + .hw_params = msm_snd_hw_params, +}; + +static struct snd_soc_ops msm_cpe_ops = { + .hw_params = msm_snd_cpe_hw_params, +}; + +static struct snd_soc_ops msm_slimbus_2_be_ops = { + .hw_params = msm_slimbus_2_hw_params, +}; + +static struct snd_soc_ops msm_wcn_ops = { + .hw_params = msm_wcn_hw_params, +}; + +static struct snd_soc_ops msm_tdm_be_ops = { + .hw_params = msm_tdm_snd_hw_params +}; + +/* Digital audio interface glue - connects codec <---> CPU */ +static struct snd_soc_dai_link msm_common_dai_links[] = { + /* FrontEnd DAI Links */ + { + .name = MSM_DAILINK_NAME(Media1), + .stream_name = "MultiMedia1", + .cpu_dai_name = "MultiMedia1", + .platform_name = "msm-pcm-dsp.0", + .dynamic = 1, + .async_ops = ASYNC_DPCM_SND_SOC_PREPARE, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .id = MSM_FRONTEND_DAI_MULTIMEDIA1 + }, + { + .name = MSM_DAILINK_NAME(Media2), + .stream_name = "MultiMedia2", + .cpu_dai_name = "MultiMedia2", + .platform_name = "msm-pcm-dsp.0", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .id = MSM_FRONTEND_DAI_MULTIMEDIA2, + }, + { + .name = "VoiceMMode1", + .stream_name = "VoiceMMode1", + .cpu_dai_name = "VoiceMMode1", + .platform_name = "msm-pcm-voice", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 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", + .id = MSM_FRONTEND_DAI_VOICEMMODE1, + }, + { + .name = "MSM VoIP", + .stream_name = "VoIP", + .cpu_dai_name = "VoIP", + .platform_name = "msm-voip-dsp", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .id = MSM_FRONTEND_DAI_VOIP, + }, + { + .name = MSM_DAILINK_NAME(ULL), + .stream_name = "MultiMedia3", + .cpu_dai_name = "MultiMedia3", + .platform_name = "msm-pcm-dsp.2", + .dynamic = 1, + .async_ops = ASYNC_DPCM_SND_SOC_PREPARE, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .id = MSM_FRONTEND_DAI_MULTIMEDIA3, + }, + /* Hostless PCM purpose */ + { + .name = "SLIMBUS_0 Hostless", + .stream_name = "SLIMBUS_0 Hostless", + .cpu_dai_name = "SLIMBUS0_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + /* this dailink has playback support */ + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + { + .name = "MSM AFE-PCM RX", + .stream_name = "AFE-PROXY RX", + .cpu_dai_name = "msm-dai-q6-dev.241", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .platform_name = "msm-pcm-afe", + .dpcm_playback = 1, + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + }, + { + .name = "MSM AFE-PCM TX", + .stream_name = "AFE-PROXY TX", + .cpu_dai_name = "msm-dai-q6-dev.240", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .platform_name = "msm-pcm-afe", + .dpcm_capture = 1, + .ignore_suspend = 1, + }, + { + .name = MSM_DAILINK_NAME(Compress1), + .stream_name = "Compress1", + .cpu_dai_name = "MultiMedia4", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .async_ops = ASYNC_DPCM_SND_SOC_HW_PARAMS, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dainlink has playback support */ + .id = MSM_FRONTEND_DAI_MULTIMEDIA4, + }, + { + .name = "AUXPCM Hostless", + .stream_name = "AUXPCM Hostless", + .cpu_dai_name = "AUXPCM_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + { + .name = "SLIMBUS_1 Hostless", + .stream_name = "SLIMBUS_1 Hostless", + .cpu_dai_name = "SLIMBUS1_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + /* this dailink has playback support */ + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + { + .name = "SLIMBUS_3 Hostless", + .stream_name = "SLIMBUS_3 Hostless", + .cpu_dai_name = "SLIMBUS3_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + /* this dailink has playback support */ + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + { + .name = "SLIMBUS_4 Hostless", + .stream_name = "SLIMBUS_4 Hostless", + .cpu_dai_name = "SLIMBUS4_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + /* this dailink has playback support */ + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + { + .name = MSM_DAILINK_NAME(LowLatency), + .stream_name = "MultiMedia5", + .cpu_dai_name = "MultiMedia5", + .platform_name = "msm-pcm-dsp.1", + .dynamic = 1, + .async_ops = ASYNC_DPCM_SND_SOC_PREPARE, + .dpcm_playback = 1, + .dpcm_capture = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .id = MSM_FRONTEND_DAI_MULTIMEDIA5, + }, + { + .name = "Listen 1 Audio Service", + .stream_name = "Listen 1 Audio Service", + .cpu_dai_name = "LSM1", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 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", + .id = MSM_FRONTEND_DAI_LSM1, + }, + /* Multiple Tunnel instances */ + { + .name = MSM_DAILINK_NAME(Compress2), + .stream_name = "Compress2", + .cpu_dai_name = "MultiMedia7", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dainlink has playback support */ + .id = MSM_FRONTEND_DAI_MULTIMEDIA7, + }, + { + .name = MSM_DAILINK_NAME(MultiMedia10), + .stream_name = "MultiMedia10", + .cpu_dai_name = "MultiMedia10", + .platform_name = "msm-pcm-dsp.1", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dainlink has playback support */ + .id = MSM_FRONTEND_DAI_MULTIMEDIA10, + }, + { + .name = MSM_DAILINK_NAME(ULL_NOIRQ), + .stream_name = "MM_NOIRQ", + .cpu_dai_name = "MultiMedia8", + .platform_name = "msm-pcm-dsp-noirq", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dainlink has playback support */ + .id = MSM_FRONTEND_DAI_MULTIMEDIA8, + }, + /* HDMI Hostless */ + { + .name = "HDMI_RX_HOSTLESS", + .stream_name = "HDMI_RX_HOSTLESS", + .cpu_dai_name = "HDMI_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .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", + }, + { + .name = "VoiceMMode2", + .stream_name = "VoiceMMode2", + .cpu_dai_name = "VoiceMMode2", + .platform_name = "msm-pcm-voice", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 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", + .id = MSM_FRONTEND_DAI_VOICEMMODE2, + }, + /* LSM FE */ + { + .name = "Listen 2 Audio Service", + .stream_name = "Listen 2 Audio Service", + .cpu_dai_name = "LSM2", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 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", + .id = MSM_FRONTEND_DAI_LSM2, + }, + { + .name = "Listen 3 Audio Service", + .stream_name = "Listen 3 Audio Service", + .cpu_dai_name = "LSM3", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 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", + .id = MSM_FRONTEND_DAI_LSM3, + }, + { + .name = "Listen 4 Audio Service", + .stream_name = "Listen 4 Audio Service", + .cpu_dai_name = "LSM4", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 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", + .id = MSM_FRONTEND_DAI_LSM4, + }, + { + .name = "Listen 5 Audio Service", + .stream_name = "Listen 5 Audio Service", + .cpu_dai_name = "LSM5", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 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", + .id = MSM_FRONTEND_DAI_LSM5, + }, + { + .name = "Listen 6 Audio Service", + .stream_name = "Listen 6 Audio Service", + .cpu_dai_name = "LSM6", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 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", + .id = MSM_FRONTEND_DAI_LSM6, + }, + { + .name = "Listen 7 Audio Service", + .stream_name = "Listen 7 Audio Service", + .cpu_dai_name = "LSM7", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 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", + .id = MSM_FRONTEND_DAI_LSM7, + }, + { + .name = "Listen 8 Audio Service", + .stream_name = "Listen 8 Audio Service", + .cpu_dai_name = "LSM8", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 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", + .id = MSM_FRONTEND_DAI_LSM8, + }, + { + .name = MSM_DAILINK_NAME(Media9), + .stream_name = "MultiMedia9", + .cpu_dai_name = "MultiMedia9", + .platform_name = "msm-pcm-dsp.0", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .id = MSM_FRONTEND_DAI_MULTIMEDIA9, + }, + { + .name = MSM_DAILINK_NAME(Compress4), + .stream_name = "Compress4", + .cpu_dai_name = "MultiMedia11", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dainlink has playback support */ + .id = MSM_FRONTEND_DAI_MULTIMEDIA11, + }, + { + .name = MSM_DAILINK_NAME(Compress5), + .stream_name = "Compress5", + .cpu_dai_name = "MultiMedia12", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dainlink has playback support */ + .id = MSM_FRONTEND_DAI_MULTIMEDIA12, + }, + { + .name = MSM_DAILINK_NAME(Compress6), + .stream_name = "Compress6", + .cpu_dai_name = "MultiMedia13", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dainlink has playback support */ + .id = MSM_FRONTEND_DAI_MULTIMEDIA13, + }, + { + .name = MSM_DAILINK_NAME(Compress7), + .stream_name = "Compress7", + .cpu_dai_name = "MultiMedia14", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dainlink has playback support */ + .id = MSM_FRONTEND_DAI_MULTIMEDIA14, + }, + { + .name = MSM_DAILINK_NAME(Compress8), + .stream_name = "Compress8", + .cpu_dai_name = "MultiMedia15", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dainlink has playback support */ + .id = MSM_FRONTEND_DAI_MULTIMEDIA15, + }, + { + .name = MSM_DAILINK_NAME(ULL_NOIRQ_2), + .stream_name = "MM_NOIRQ_2", + .cpu_dai_name = "MultiMedia16", + .platform_name = "msm-pcm-dsp-noirq", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dainlink has playback support */ + .id = MSM_FRONTEND_DAI_MULTIMEDIA16, + }, + { + .name = "SLIMBUS_8 Hostless", + .stream_name = "SLIMBUS8_HOSTLESS Capture", + .cpu_dai_name = "SLIMBUS8_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_capture = 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", + }, +}; + +static struct snd_soc_dai_link msm_tasha_fe_dai_links[] = { + { + .name = LPASS_BE_SLIMBUS_4_TX, + .stream_name = "Slimbus4 Capture", + .cpu_dai_name = "msm-dai-q6-dev.16393", + .platform_name = "msm-pcm-hostless", + .codec_name = "tasha_codec", + .codec_dai_name = "tasha_vifeedback", + .id = MSM_BACKEND_DAI_SLIMBUS_4_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_be_ops, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + }, + /* Ultrasound RX DAI Link */ + { + .name = "SLIMBUS_2 Hostless Playback", + .stream_name = "SLIMBUS_2 Hostless Playback", + .cpu_dai_name = "msm-dai-q6-dev.16388", + .platform_name = "msm-pcm-hostless", + .codec_name = "tasha_codec", + .codec_dai_name = "tasha_rx2", + .ignore_suspend = 1, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ops = &msm_slimbus_2_be_ops, + }, + /* Ultrasound TX DAI Link */ + { + .name = "SLIMBUS_2 Hostless Capture", + .stream_name = "SLIMBUS_2 Hostless Capture", + .cpu_dai_name = "msm-dai-q6-dev.16389", + .platform_name = "msm-pcm-hostless", + .codec_name = "tasha_codec", + .codec_dai_name = "tasha_tx2", + .ignore_suspend = 1, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ops = &msm_slimbus_2_be_ops, + }, + /* CPE LSM direct dai-link */ + { + .name = "CPE Listen service", + .stream_name = "CPE Listen Audio Service", + .cpu_dai_name = "msm-dai-slim", + .platform_name = "msm-cpe-lsm", + .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 = "tasha_mad1", + .codec_name = "tasha_codec", + .ops = &msm_cpe_ops, + }, + { + .name = "SLIMBUS_6 Hostless Playback", + .stream_name = "SLIMBUS_6 Hostless", + .cpu_dai_name = "SLIMBUS6_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .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, + /* this dailink has playback support */ + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + /* CPE LSM EC PP direct dai-link */ + { + .name = "CPE Listen service ECPP", + .stream_name = "CPE Listen Audio Service ECPP", + .cpu_dai_name = "CPE_LSM_NOHOST", + .platform_name = "msm-cpe-lsm.3", + .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 = "tasha_cpe", + .codec_name = "tasha_codec", + }, +}; + +static struct snd_soc_dai_link msm_tavil_fe_dai_links[] = { + { + .name = LPASS_BE_SLIMBUS_4_TX, + .stream_name = "Slimbus4 Capture", + .cpu_dai_name = "msm-dai-q6-dev.16393", + .platform_name = "msm-pcm-hostless", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_vifeedback", + .id = MSM_BACKEND_DAI_SLIMBUS_4_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_be_ops, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + }, + /* Ultrasound RX DAI Link */ + { + .name = "SLIMBUS_2 Hostless Playback", + .stream_name = "SLIMBUS_2 Hostless Playback", + .cpu_dai_name = "msm-dai-q6-dev.16388", + .platform_name = "msm-pcm-hostless", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_rx2", + .ignore_suspend = 1, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ops = &msm_slimbus_2_be_ops, + }, + /* Ultrasound TX DAI Link */ + { + .name = "SLIMBUS_2 Hostless Capture", + .stream_name = "SLIMBUS_2 Hostless Capture", + .cpu_dai_name = "msm-dai-q6-dev.16389", + .platform_name = "msm-pcm-hostless", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_tx2", + .ignore_suspend = 1, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ops = &msm_slimbus_2_be_ops, + }, +}; + +static struct snd_soc_dai_link msm_common_misc_fe_dai_links[] = { + { + .name = MSM_DAILINK_NAME(ASM Loopback), + .stream_name = "MultiMedia6", + .cpu_dai_name = "MultiMedia6", + .platform_name = "msm-pcm-loopback", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .ignore_suspend = 1, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_pmdown_time = 1, + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA6, + }, + { + .name = "USB Audio Hostless", + .stream_name = "USB Audio Hostless", + .cpu_dai_name = "USBAUDIO_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 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", + }, + { + .name = MSM_DAILINK_NAME(Transcode Loopback Playback), + .stream_name = "Transcode Loopback Playback", + .cpu_dai_name = "MultiMedia14", + .platform_name = "msm-transcode-loopback", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dainlink has playback support */ + .id = MSM_FRONTEND_DAI_MULTIMEDIA14, + }, + { + .name = MSM_DAILINK_NAME(Transcode Loopback Capture), + .stream_name = "Transcode Loopback Capture", + .cpu_dai_name = "MultiMedia18", + .platform_name = "msm-transcode-loopback", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .id = MSM_FRONTEND_DAI_MULTIMEDIA18, + }, +}; + +static struct snd_soc_dai_link msm_common_be_dai_links[] = { + /* Backend AFE DAI Links */ + { + .name = LPASS_BE_AFE_PCM_RX, + .stream_name = "AFE Playback", + .cpu_dai_name = "msm-dai-q6-dev.224", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_AFE_PCM_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_AFE_PCM_TX, + .stream_name = "AFE Capture", + .cpu_dai_name = "msm-dai-q6-dev.225", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_AFE_PCM_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + }, + /* Incall Record Uplink BACK END DAI Link */ + { + .name = LPASS_BE_INCALL_RECORD_TX, + .stream_name = "Voice Uplink Capture", + .cpu_dai_name = "msm-dai-q6-dev.32772", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_INCALL_RECORD_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + }, + /* Incall Record Downlink BACK END DAI Link */ + { + .name = LPASS_BE_INCALL_RECORD_RX, + .stream_name = "Voice Downlink Capture", + .cpu_dai_name = "msm-dai-q6-dev.32771", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_INCALL_RECORD_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + }, + /* Incall Music BACK END DAI Link */ + { + .name = LPASS_BE_VOICE_PLAYBACK_TX, + .stream_name = "Voice Farend Playback", + .cpu_dai_name = "msm-dai-q6-dev.32773", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_VOICE_PLAYBACK_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + }, + /* Incall Music 2 BACK END DAI Link */ + { + .name = LPASS_BE_VOICE2_PLAYBACK_TX, + .stream_name = "Voice2 Farend Playback", + .cpu_dai_name = "msm-dai-q6-dev.32770", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_VOICE2_PLAYBACK_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_USB_AUDIO_RX, + .stream_name = "USB Audio Playback", + .cpu_dai_name = "msm-dai-q6-dev.28672", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_USB_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_USB_AUDIO_TX, + .stream_name = "USB Audio Capture", + .cpu_dai_name = "msm-dai-q6-dev.28673", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_USB_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_PRI_TDM_RX_0, + .stream_name = "Primary TDM0 Playback", + .cpu_dai_name = "msm-dai-q6-tdm.36864", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_PRI_TDM_RX_0, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_PRI_TDM_TX_0, + .stream_name = "Primary TDM0 Capture", + .cpu_dai_name = "msm-dai-q6-tdm.36865", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_PRI_TDM_TX_0, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SEC_TDM_RX_0, + .stream_name = "Secondary TDM0 Playback", + .cpu_dai_name = "msm-dai-q6-tdm.36880", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SEC_TDM_RX_0, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SEC_TDM_TX_0, + .stream_name = "Secondary TDM0 Capture", + .cpu_dai_name = "msm-dai-q6-tdm.36881", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_SEC_TDM_TX_0, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_TERT_TDM_RX_0, + .stream_name = "Tertiary TDM0 Playback", + .cpu_dai_name = "msm-dai-q6-tdm.36896", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_TERT_TDM_RX_0, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_TERT_TDM_TX_0, + .stream_name = "Tertiary TDM0 Capture", + .cpu_dai_name = "msm-dai-q6-tdm.36897", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_TERT_TDM_TX_0, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_QUAT_TDM_RX_0, + .stream_name = "Quaternary TDM0 Playback", + .cpu_dai_name = "msm-dai-q6-tdm.36912", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_QUAT_TDM_RX_0, + .be_hw_params_fixup = msm_tdm_be_hw_params_fixup, + .ops = &msm8998_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_QUAT_TDM_TX_0, + .stream_name = "Quaternary TDM0 Capture", + .cpu_dai_name = "msm-dai-q6-tdm.36913", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_QUAT_TDM_TX_0, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_tdm_be_ops, + .ignore_suspend = 1, + }, +}; + +static struct snd_soc_dai_link msm_tasha_be_dai_links[] = { + { + .name = LPASS_BE_SLIMBUS_0_RX, + .stream_name = "Slimbus Playback", + .cpu_dai_name = "msm-dai-q6-dev.16384", + .platform_name = "msm-pcm-routing", + .codec_name = "tasha_codec", + .codec_dai_name = "tasha_mix_rx1", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_0_RX, + .init = &msm_audrx_init, + .be_hw_params_fixup = msm_be_hw_params_fixup, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + .ops = &msm_be_ops, + }, + { + .name = LPASS_BE_SLIMBUS_0_TX, + .stream_name = "Slimbus Capture", + .cpu_dai_name = "msm-dai-q6-dev.16385", + .platform_name = "msm-pcm-routing", + .codec_name = "tasha_codec", + .codec_dai_name = "tasha_tx1", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_0_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + .ops = &msm_be_ops, + }, + { + .name = LPASS_BE_SLIMBUS_1_RX, + .stream_name = "Slimbus1 Playback", + .cpu_dai_name = "msm-dai-q6-dev.16386", + .platform_name = "msm-pcm-routing", + .codec_name = "tasha_codec", + .codec_dai_name = "tasha_mix_rx1", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_1_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_be_ops, + /* dai link has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_1_TX, + .stream_name = "Slimbus1 Capture", + .cpu_dai_name = "msm-dai-q6-dev.16387", + .platform_name = "msm-pcm-routing", + .codec_name = "tasha_codec", + .codec_dai_name = "tasha_tx3", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_1_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_3_RX, + .stream_name = "Slimbus3 Playback", + .cpu_dai_name = "msm-dai-q6-dev.16390", + .platform_name = "msm-pcm-routing", + .codec_name = "tasha_codec", + .codec_dai_name = "tasha_mix_rx1", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_3_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_be_ops, + /* dai link has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_3_TX, + .stream_name = "Slimbus3 Capture", + .cpu_dai_name = "msm-dai-q6-dev.16391", + .platform_name = "msm-pcm-routing", + .codec_name = "tasha_codec", + .codec_dai_name = "tasha_tx1", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_3_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_4_RX, + .stream_name = "Slimbus4 Playback", + .cpu_dai_name = "msm-dai-q6-dev.16392", + .platform_name = "msm-pcm-routing", + .codec_name = "tasha_codec", + .codec_dai_name = "tasha_mix_rx1", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_4_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_be_ops, + /* dai link has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_5_RX, + .stream_name = "Slimbus5 Playback", + .cpu_dai_name = "msm-dai-q6-dev.16394", + .platform_name = "msm-pcm-routing", + .codec_name = "tasha_codec", + .codec_dai_name = "tasha_rx3", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_5_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_be_ops, + /* dai link has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + /* MAD BE */ + { + .name = LPASS_BE_SLIMBUS_5_TX, + .stream_name = "Slimbus5 Capture", + .cpu_dai_name = "msm-dai-q6-dev.16395", + .platform_name = "msm-pcm-routing", + .codec_name = "tasha_codec", + .codec_dai_name = "tasha_mad1", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_5_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_6_RX, + .stream_name = "Slimbus6 Playback", + .cpu_dai_name = "msm-dai-q6-dev.16396", + .platform_name = "msm-pcm-routing", + .codec_name = "tasha_codec", + .codec_dai_name = "tasha_rx4", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_6_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_be_ops, + /* dai link has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + /* Slimbus VI Recording */ + { + .name = LPASS_BE_SLIMBUS_TX_VI, + .stream_name = "Slimbus4 Capture", + .cpu_dai_name = "msm-dai-q6-dev.16393", + .platform_name = "msm-pcm-routing", + .codec_name = "tasha_codec", + .codec_dai_name = "tasha_vifeedback", + .be_id = MSM_BACKEND_DAI_SLIMBUS_4_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_be_ops, + .ignore_suspend = 1, + .no_pcm = 1, + .dpcm_capture = 1, + .ignore_pmdown_time = 1, + }, +}; + +static struct snd_soc_dai_link msm_tavil_be_dai_links[] = { + { + .name = LPASS_BE_SLIMBUS_0_RX, + .stream_name = "Slimbus Playback", + .cpu_dai_name = "msm-dai-q6-dev.16384", + .platform_name = "msm-pcm-routing", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_rx1", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_0_RX, + .init = &msm_audrx_init, + .be_hw_params_fixup = msm_be_hw_params_fixup, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + .ops = &msm_be_ops, + }, + { + .name = LPASS_BE_SLIMBUS_0_TX, + .stream_name = "Slimbus Capture", + .cpu_dai_name = "msm-dai-q6-dev.16385", + .platform_name = "msm-pcm-routing", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_tx1", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_0_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + .ops = &msm_be_ops, + }, + { + .name = LPASS_BE_SLIMBUS_1_RX, + .stream_name = "Slimbus1 Playback", + .cpu_dai_name = "msm-dai-q6-dev.16386", + .platform_name = "msm-pcm-routing", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_rx1", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_1_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_be_ops, + /* dai link has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_1_TX, + .stream_name = "Slimbus1 Capture", + .cpu_dai_name = "msm-dai-q6-dev.16387", + .platform_name = "msm-pcm-routing", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_tx3", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_1_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_2_RX, + .stream_name = "Slimbus2 Playback", + .cpu_dai_name = "msm-dai-q6-dev.16388", + .platform_name = "msm-pcm-routing", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_rx2", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_2_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_be_ops, + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_3_RX, + .stream_name = "Slimbus3 Playback", + .cpu_dai_name = "msm-dai-q6-dev.16390", + .platform_name = "msm-pcm-routing", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_rx1", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_3_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_be_ops, + /* dai link has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_3_TX, + .stream_name = "Slimbus3 Capture", + .cpu_dai_name = "msm-dai-q6-dev.16391", + .platform_name = "msm-pcm-routing", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_tx1", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_3_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_4_RX, + .stream_name = "Slimbus4 Playback", + .cpu_dai_name = "msm-dai-q6-dev.16392", + .platform_name = "msm-pcm-routing", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_rx1", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_4_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_be_ops, + /* dai link has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_5_RX, + .stream_name = "Slimbus5 Playback", + .cpu_dai_name = "msm-dai-q6-dev.16394", + .platform_name = "msm-pcm-routing", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_rx3", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_5_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_be_ops, + /* dai link has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + /* MAD BE */ + { + .name = LPASS_BE_SLIMBUS_5_TX, + .stream_name = "Slimbus5 Capture", + .cpu_dai_name = "msm-dai-q6-dev.16395", + .platform_name = "msm-pcm-routing", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_mad1", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_5_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_6_RX, + .stream_name = "Slimbus6 Playback", + .cpu_dai_name = "msm-dai-q6-dev.16396", + .platform_name = "msm-pcm-routing", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_rx4", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_6_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_be_ops, + /* dai link has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + /* Slimbus VI Recording */ + { + .name = LPASS_BE_SLIMBUS_TX_VI, + .stream_name = "Slimbus4 Capture", + .cpu_dai_name = "msm-dai-q6-dev.16393", + .platform_name = "msm-pcm-routing", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_vifeedback", + .be_id = MSM_BACKEND_DAI_SLIMBUS_4_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_be_ops, + .ignore_suspend = 1, + .no_pcm = 1, + .dpcm_capture = 1, + .ignore_pmdown_time = 1, + }, +}; + +static struct snd_soc_dai_link msm_wcn_be_dai_links[] = { + { + .name = LPASS_BE_SLIMBUS_7_RX, + .stream_name = "Slimbus7 Playback", + .cpu_dai_name = "msm-dai-q6-dev.16398", + .platform_name = "msm-pcm-routing", + .codec_name = "btfmslim_slave", + /* BT codec driver determines capabilities based on + * dai name, bt codecdai name should always contains + * supported usecase information + */ + .codec_dai_name = "btfm_bt_sco_a2dp_slim_rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_7_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_wcn_ops, + /* dai link has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_7_TX, + .stream_name = "Slimbus7 Capture", + .cpu_dai_name = "msm-dai-q6-dev.16399", + .platform_name = "msm-pcm-routing", + .codec_name = "btfmslim_slave", + .codec_dai_name = "btfm_bt_sco_slim_tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_7_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_wcn_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_8_TX, + .stream_name = "Slimbus8 Capture", + .cpu_dai_name = "msm-dai-q6-dev.16401", + .platform_name = "msm-pcm-routing", + .codec_name = "btfmslim_slave", + .codec_dai_name = "btfm_fm_slim_tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_8_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .init = &msm_wcn_init, + .ops = &msm_wcn_ops, + .ignore_suspend = 1, + }, +}; + +static struct snd_soc_dai_link ext_disp_be_dai_link[] = { + /* HDMI BACK END DAI Link */ + { + .name = LPASS_BE_HDMI, + .stream_name = "HDMI Playback", + .cpu_dai_name = "msm-dai-q6-hdmi.8", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-ext-disp-audio-codec-rx", + .codec_dai_name = "msm_hdmi_audio_codec_rx_dai", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_HDMI_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + /* DISP PORT BACK END DAI Link */ + { + .name = LPASS_BE_DISPLAY_PORT, + .stream_name = "Display Port Playback", + .cpu_dai_name = "msm-dai-q6-dp.24608", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-ext-disp-audio-codec-rx", + .codec_dai_name = "msm_dp_audio_codec_rx_dai", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_DISPLAY_PORT_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, +}; + +static struct snd_soc_dai_link msm_mi2s_be_dai_links[] = { + { + .name = LPASS_BE_PRI_MI2S_RX, + .stream_name = "Primary MI2S Playback", + .cpu_dai_name = "msm-dai-q6-mi2s.0", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_PRI_MI2S_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + }, + { + .name = LPASS_BE_PRI_MI2S_TX, + .stream_name = "Primary MI2S Capture", + .cpu_dai_name = "msm-dai-q6-mi2s.0", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_PRI_MI2S_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SEC_MI2S_RX, + .stream_name = "Secondary MI2S Playback", + .cpu_dai_name = "msm-dai-q6-mi2s.1", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SECONDARY_MI2S_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + }, + { + .name = LPASS_BE_SEC_MI2S_TX, + .stream_name = "Secondary MI2S Capture", + .cpu_dai_name = "msm-dai-q6-mi2s.1", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_SECONDARY_MI2S_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_TERT_MI2S_RX, + .stream_name = "Tertiary MI2S Playback", + .cpu_dai_name = "msm-dai-q6-mi2s.2", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_TERTIARY_MI2S_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + }, + { + .name = LPASS_BE_TERT_MI2S_TX, + .stream_name = "Tertiary MI2S Capture", + .cpu_dai_name = "msm-dai-q6-mi2s.2", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_TERTIARY_MI2S_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_QUAT_MI2S_RX, + .stream_name = "Quaternary MI2S Playback", + .cpu_dai_name = "msm-dai-q6-mi2s.3", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_QUATERNARY_MI2S_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + }, + { + .name = LPASS_BE_QUAT_MI2S_TX, + .stream_name = "Quaternary MI2S Capture", + .cpu_dai_name = "msm-dai-q6-mi2s.3", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + }, +}; + +static struct snd_soc_dai_link msm_auxpcm_be_dai_links[] = { + /* Primary AUX PCM Backend DAI Links */ + { + .name = LPASS_BE_AUXPCM_RX, + .stream_name = "AUX PCM Playback", + .cpu_dai_name = "msm-dai-q6-auxpcm.1", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_AUXPCM_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + .ops = &msm_aux_pcm_be_ops, + }, + { + .name = LPASS_BE_AUXPCM_TX, + .stream_name = "AUX PCM Capture", + .cpu_dai_name = "msm-dai-q6-auxpcm.1", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_AUXPCM_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + .ops = &msm_aux_pcm_be_ops, + }, + /* Secondary AUX PCM Backend DAI Links */ + { + .name = LPASS_BE_SEC_AUXPCM_RX, + .stream_name = "Sec AUX PCM Playback", + .cpu_dai_name = "msm-dai-q6-auxpcm.2", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SEC_AUXPCM_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + .ops = &msm_aux_pcm_be_ops, + }, + { + .name = LPASS_BE_SEC_AUXPCM_TX, + .stream_name = "Sec AUX PCM Capture", + .cpu_dai_name = "msm-dai-q6-auxpcm.2", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_SEC_AUXPCM_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .ops = &msm_aux_pcm_be_ops, + }, + /* Tertiary AUX PCM Backend DAI Links */ + { + .name = LPASS_BE_TERT_AUXPCM_RX, + .stream_name = "Tert AUX PCM Playback", + .cpu_dai_name = "msm-dai-q6-auxpcm.3", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_TERT_AUXPCM_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + .ops = &msm_aux_pcm_be_ops, + }, + { + .name = LPASS_BE_TERT_AUXPCM_TX, + .stream_name = "Tert AUX PCM Capture", + .cpu_dai_name = "msm-dai-q6-auxpcm.3", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_TERT_AUXPCM_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .ops = &msm_aux_pcm_be_ops, + }, + /* Quaternary AUX PCM Backend DAI Links */ + { + .name = LPASS_BE_QUAT_AUXPCM_RX, + .stream_name = "Quat AUX PCM Playback", + .cpu_dai_name = "msm-dai-q6-auxpcm.4", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_QUAT_AUXPCM_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + .ops = &msm_aux_pcm_be_ops, + }, + { + .name = LPASS_BE_QUAT_AUXPCM_TX, + .stream_name = "Quat AUX PCM Capture", + .cpu_dai_name = "msm-dai-q6-auxpcm.4", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_QUAT_AUXPCM_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .ops = &msm_aux_pcm_be_ops, + }, +}; + +static struct snd_soc_dai_link msm_tasha_dai_links[ + ARRAY_SIZE(msm_common_dai_links) + + ARRAY_SIZE(msm_tasha_fe_dai_links) + + ARRAY_SIZE(msm_common_misc_fe_dai_links) + + ARRAY_SIZE(msm_common_be_dai_links) + + ARRAY_SIZE(msm_tasha_be_dai_links) + + ARRAY_SIZE(msm_wcn_be_dai_links) + + ARRAY_SIZE(ext_disp_be_dai_link) + + ARRAY_SIZE(msm_mi2s_be_dai_links) + + ARRAY_SIZE(msm_auxpcm_be_dai_links)]; + +static struct snd_soc_dai_link msm_tavil_dai_links[ + ARRAY_SIZE(msm_common_dai_links) + + ARRAY_SIZE(msm_tavil_fe_dai_links) + + ARRAY_SIZE(msm_common_misc_fe_dai_links) + + ARRAY_SIZE(msm_common_be_dai_links) + + ARRAY_SIZE(msm_tavil_be_dai_links) + + ARRAY_SIZE(msm_wcn_be_dai_links) + + ARRAY_SIZE(ext_disp_be_dai_link) + + ARRAY_SIZE(msm_mi2s_be_dai_links) + + ARRAY_SIZE(msm_auxpcm_be_dai_links)]; + +static int msm_snd_card_late_probe(struct snd_soc_card *card) +{ + const char *be_dl_name = LPASS_BE_SLIMBUS_0_RX; + struct snd_soc_pcm_runtime *rtd; + int ret = 0; + void *mbhc_calibration; + + rtd = snd_soc_get_pcm_runtime(card, be_dl_name); + if (!rtd) { + dev_err(card->dev, + "%s: snd_soc_get_pcm_runtime for %s failed!\n", + __func__, be_dl_name); + ret = -EINVAL; + goto err_pcm_runtime; + } + + mbhc_calibration = def_tasha_mbhc_cal(); + if (!mbhc_calibration) { + ret = -ENOMEM; + goto err_mbhc_cal; + } + wcd_mbhc_cfg.calibration = mbhc_calibration; + ret = tasha_mbhc_hs_detect(rtd->codec, &wcd_mbhc_cfg); + if (ret) { + dev_err(card->dev, "%s: mbhc hs detect failed, err:%d\n", + __func__, ret); + goto err_hs_detect; + } + return 0; + +err_hs_detect: + kfree(mbhc_calibration); +err_mbhc_cal: +err_pcm_runtime: + return ret; +} + +static int msm_snd_card_tavil_late_probe(struct snd_soc_card *card) +{ + const char *be_dl_name = LPASS_BE_SLIMBUS_0_RX; + struct snd_soc_pcm_runtime *rtd; + int ret = 0; + void *mbhc_calibration; + + rtd = snd_soc_get_pcm_runtime(card, be_dl_name); + if (!rtd) { + dev_err(card->dev, + "%s: snd_soc_get_pcm_runtime for %s failed!\n", + __func__, be_dl_name); + ret = -EINVAL; + goto err_pcm_runtime; + } + + mbhc_calibration = def_tavil_mbhc_cal(); + if (!mbhc_calibration) { + ret = -ENOMEM; + goto err_mbhc_cal; + } + wcd_mbhc_cfg.calibration = mbhc_calibration; + ret = tavil_mbhc_hs_detect(rtd->codec, &wcd_mbhc_cfg); + if (ret) { + dev_err(card->dev, "%s: mbhc hs detect failed, err:%d\n", + __func__, ret); + goto err_hs_detect; + } + return 0; + +err_hs_detect: + kfree(mbhc_calibration); +err_mbhc_cal: +err_pcm_runtime: + return ret; +} + +struct snd_soc_card snd_soc_card_tasha_msm = { + .name = "msm8998-tasha-snd-card", + .late_probe = msm_snd_card_late_probe, +}; + +struct snd_soc_card snd_soc_card_tavil_msm = { + .name = "msm8998-tavil-snd-card", + .late_probe = msm_snd_card_tavil_late_probe, +}; + +static int msm_populate_dai_link_component_of_node( + struct snd_soc_card *card) +{ + int i, index, ret = 0; + struct device *cdev = card->dev; + struct snd_soc_dai_link *dai_link = card->dai_link; + struct device_node *np; + + if (!cdev) { + pr_err("%s: Sound card device memory NULL\n", __func__); + return -ENODEV; + } + + for (i = 0; i < card->num_links; i++) { + if (dai_link[i].platform_of_node && dai_link[i].cpu_of_node) + continue; + + /* populate platform_of_node for snd card dai links */ + if (dai_link[i].platform_name && + !dai_link[i].platform_of_node) { + index = of_property_match_string(cdev->of_node, + "asoc-platform-names", + dai_link[i].platform_name); + if (index < 0) { + pr_err("%s: No match found for platform name: %s\n", + __func__, dai_link[i].platform_name); + ret = index; + goto err; + } + np = of_parse_phandle(cdev->of_node, "asoc-platform", + index); + if (!np) { + pr_err("%s: retrieving phandle for platform %s, index %d failed\n", + __func__, dai_link[i].platform_name, + index); + ret = -ENODEV; + goto err; + } + dai_link[i].platform_of_node = np; + dai_link[i].platform_name = NULL; + } + + /* populate cpu_of_node for snd card dai links */ + if (dai_link[i].cpu_dai_name && !dai_link[i].cpu_of_node) { + index = of_property_match_string(cdev->of_node, + "asoc-cpu-names", + dai_link[i].cpu_dai_name); + if (index >= 0) { + np = of_parse_phandle(cdev->of_node, "asoc-cpu", + index); + if (!np) { + pr_err("%s: retrieving phandle for cpu dai %s failed\n", + __func__, + dai_link[i].cpu_dai_name); + ret = -ENODEV; + goto err; + } + dai_link[i].cpu_of_node = np; + dai_link[i].cpu_dai_name = NULL; + } + } + + /* populate codec_of_node for snd card dai links */ + if (dai_link[i].codec_name && !dai_link[i].codec_of_node) { + index = of_property_match_string(cdev->of_node, + "asoc-codec-names", + dai_link[i].codec_name); + if (index < 0) + continue; + np = of_parse_phandle(cdev->of_node, "asoc-codec", + index); + if (!np) { + pr_err("%s: retrieving phandle for codec %s failed\n", + __func__, dai_link[i].codec_name); + ret = -ENODEV; + goto err; + } + dai_link[i].codec_of_node = np; + dai_link[i].codec_name = NULL; + } + } + +err: + return ret; +} + +static int msm_prepare_us_euro(struct snd_soc_card *card) +{ + struct msm_asoc_mach_data *pdata = + snd_soc_card_get_drvdata(card); + int ret = 0; + + if (pdata->us_euro_gpio >= 0) { + dev_dbg(card->dev, "%s: us_euro gpio request %d", __func__, + pdata->us_euro_gpio); + ret = gpio_request(pdata->us_euro_gpio, "TASHA_CODEC_US_EURO"); + if (ret) { + dev_err(card->dev, + "%s: Failed to request codec US/EURO gpio %d error %d\n", + __func__, pdata->us_euro_gpio, ret); + } + } + + return ret; +} + +static int msm_audrx_stub_init(struct snd_soc_pcm_runtime *rtd) +{ + int ret = 0; + struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + + ret = snd_soc_add_codec_controls(codec, msm_snd_controls, + ARRAY_SIZE(msm_snd_controls)); + if (ret < 0) { + dev_err(codec->dev, "%s: add_codec_controls failed, err%d\n", + __func__, ret); + return ret; + } + + snd_soc_dapm_new_controls(dapm, msm_dapm_widgets, + ARRAY_SIZE(msm_dapm_widgets)); + + return 0; +} + +static int msm_snd_stub_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + + int ret = 0; + unsigned int rx_ch[] = {144, 145, 146, 147, 148, 149, 150, + 151, 152, 153, 154, 155, 156}; + unsigned int tx_ch[] = {128, 129, 130, 131, 132, 133, + 134, 135, 136, 137, 138, 139, + 140, 141, 142, 143}; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + ret = snd_soc_dai_set_channel_map(cpu_dai, 0, 0, + slim_rx_cfg[0].channels, + rx_ch); + if (ret < 0) + pr_err("%s: RX failed to set cpu chan map error %d\n", + __func__, ret); + } else { + ret = snd_soc_dai_set_channel_map(cpu_dai, + slim_tx_cfg[0].channels, + tx_ch, 0, 0); + if (ret < 0) + pr_err("%s: TX failed to set cpu chan map error %d\n", + __func__, ret); + } + + return ret; +} + +static struct snd_soc_ops msm_stub_be_ops = { + .hw_params = msm_snd_stub_hw_params, +}; + +static struct snd_soc_dai_link msm_stub_fe_dai_links[] = { + + /* FrontEnd DAI Links */ + { + .name = "MSMSTUB Media1", + .stream_name = "MultiMedia1", + .cpu_dai_name = "MultiMedia1", + .platform_name = "msm-pcm-dsp.0", + .dynamic = 1, + .async_ops = ASYNC_DPCM_SND_SOC_PREPARE, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .id = MSM_FRONTEND_DAI_MULTIMEDIA1 + }, +}; + +static struct snd_soc_dai_link msm_stub_be_dai_links[] = { + + /* Backend DAI Links */ + { + .name = LPASS_BE_SLIMBUS_0_RX, + .stream_name = "Slimbus Playback", + .cpu_dai_name = "msm-dai-q6-dev.16384", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_0_RX, + .init = &msm_audrx_stub_init, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_pmdown_time = 1, /* dai link has playback support */ + .ignore_suspend = 1, + .ops = &msm_stub_be_ops, + }, + { + .name = LPASS_BE_SLIMBUS_0_TX, + .stream_name = "Slimbus Capture", + .cpu_dai_name = "msm-dai-q6-dev.16385", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_0_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + .ops = &msm_stub_be_ops, + }, +}; + +static struct snd_soc_dai_link msm_stub_dai_links[ + ARRAY_SIZE(msm_stub_fe_dai_links) + + ARRAY_SIZE(msm_stub_be_dai_links)]; + +struct snd_soc_card snd_soc_card_stub_msm = { + .name = "msm8998-stub-snd-card", +}; + +static const struct of_device_id msm8998_asoc_machine_of_match[] = { + { .compatible = "qcom,msm8998-asoc-snd-tasha", + .data = "tasha_codec"}, + { .compatible = "qcom,msm8998-asoc-snd-tavil", + .data = "tavil_codec"}, + { .compatible = "qcom,msm8998-asoc-snd-stub", + .data = "stub_codec"}, + {}, +}; + +static struct snd_soc_card *populate_snd_card_dailinks(struct device *dev) +{ + struct snd_soc_card *card = NULL; + struct snd_soc_dai_link *dailink; + int len_1, len_2, len_3, len_4; + int total_links; + const struct of_device_id *match; + + match = of_match_node(msm8998_asoc_machine_of_match, dev->of_node); + if (!match) { + dev_err(dev, "%s: No DT match found for sound card\n", + __func__); + return NULL; + } + + if (!strcmp(match->data, "tasha_codec")) { + card = &snd_soc_card_tasha_msm; + len_1 = ARRAY_SIZE(msm_common_dai_links); + len_2 = len_1 + ARRAY_SIZE(msm_tasha_fe_dai_links); + len_3 = len_2 + ARRAY_SIZE(msm_common_misc_fe_dai_links); + len_4 = len_3 + ARRAY_SIZE(msm_common_be_dai_links); + total_links = len_4 + ARRAY_SIZE(msm_tasha_be_dai_links); + memcpy(msm_tasha_dai_links, + msm_common_dai_links, + sizeof(msm_common_dai_links)); + memcpy(msm_tasha_dai_links + len_1, + msm_tasha_fe_dai_links, + sizeof(msm_tasha_fe_dai_links)); + memcpy(msm_tasha_dai_links + len_2, + msm_common_misc_fe_dai_links, + sizeof(msm_common_misc_fe_dai_links)); + memcpy(msm_tasha_dai_links + len_3, + msm_common_be_dai_links, + sizeof(msm_common_be_dai_links)); + memcpy(msm_tasha_dai_links + len_4, + msm_tasha_be_dai_links, + sizeof(msm_tasha_be_dai_links)); + + if (of_property_read_bool(dev->of_node, "qcom,wcn-btfm")) { + dev_dbg(dev, "%s(): WCN BTFM support present\n", + __func__); + memcpy(msm_tasha_dai_links + total_links, + msm_wcn_be_dai_links, + sizeof(msm_wcn_be_dai_links)); + total_links += ARRAY_SIZE(msm_wcn_be_dai_links); + } + + if (of_property_read_bool(dev->of_node, + "qcom,ext-disp-audio-rx")) { + dev_dbg(dev, "%s(): External display audio support present\n", + __func__); + memcpy(msm_tasha_dai_links + total_links, + ext_disp_be_dai_link, + sizeof(ext_disp_be_dai_link)); + total_links += ARRAY_SIZE(ext_disp_be_dai_link); + } + if (of_property_read_bool(dev->of_node, + "qcom,mi2s-audio-intf")) { + memcpy(msm_tasha_dai_links + total_links, + msm_mi2s_be_dai_links, + sizeof(msm_mi2s_be_dai_links)); + total_links += ARRAY_SIZE(msm_mi2s_be_dai_links); + } + if (of_property_read_bool(dev->of_node, + "qcom,auxpcm-audio-intf")) { + memcpy(msm_tasha_dai_links + total_links, + msm_auxpcm_be_dai_links, + sizeof(msm_auxpcm_be_dai_links)); + total_links += ARRAY_SIZE(msm_auxpcm_be_dai_links); + } + dailink = msm_tasha_dai_links; + } else if (!strcmp(match->data, "tavil_codec")) { + card = &snd_soc_card_tavil_msm; + len_1 = ARRAY_SIZE(msm_common_dai_links); + len_2 = len_1 + ARRAY_SIZE(msm_tavil_fe_dai_links); + len_3 = len_2 + ARRAY_SIZE(msm_common_misc_fe_dai_links); + len_4 = len_3 + ARRAY_SIZE(msm_common_be_dai_links); + total_links = len_4 + ARRAY_SIZE(msm_tavil_be_dai_links); + memcpy(msm_tavil_dai_links, + msm_common_dai_links, + sizeof(msm_common_dai_links)); + memcpy(msm_tavil_dai_links + len_1, + msm_tavil_fe_dai_links, + sizeof(msm_tavil_fe_dai_links)); + memcpy(msm_tavil_dai_links + len_2, + msm_common_misc_fe_dai_links, + sizeof(msm_common_misc_fe_dai_links)); + memcpy(msm_tavil_dai_links + len_3, + msm_common_be_dai_links, + sizeof(msm_common_be_dai_links)); + memcpy(msm_tavil_dai_links + len_4, + msm_tavil_be_dai_links, + sizeof(msm_tavil_be_dai_links)); + + if (of_property_read_bool(dev->of_node, "qcom,wcn-btfm")) { + dev_dbg(dev, "%s(): WCN BTFM support present\n", + __func__); + memcpy(msm_tavil_dai_links + total_links, + msm_wcn_be_dai_links, + sizeof(msm_wcn_be_dai_links)); + total_links += ARRAY_SIZE(msm_wcn_be_dai_links); + } + + if (of_property_read_bool(dev->of_node, + "qcom,ext-disp-audio-rx")) { + dev_dbg(dev, "%s(): ext disp audio support present\n", + __func__); + memcpy(msm_tavil_dai_links + total_links, + ext_disp_be_dai_link, + sizeof(ext_disp_be_dai_link)); + total_links += ARRAY_SIZE(ext_disp_be_dai_link); + } + if (of_property_read_bool(dev->of_node, + "qcom,mi2s-audio-intf")) { + memcpy(msm_tavil_dai_links + total_links, + msm_mi2s_be_dai_links, + sizeof(msm_mi2s_be_dai_links)); + total_links += ARRAY_SIZE(msm_mi2s_be_dai_links); + } + if (of_property_read_bool(dev->of_node, + "qcom,auxpcm-audio-intf")) { + memcpy(msm_tavil_dai_links + total_links, + msm_auxpcm_be_dai_links, + sizeof(msm_auxpcm_be_dai_links)); + total_links += ARRAY_SIZE(msm_auxpcm_be_dai_links); + } + dailink = msm_tavil_dai_links; + } else if (!strcmp(match->data, "stub_codec")) { + card = &snd_soc_card_stub_msm; + len_1 = ARRAY_SIZE(msm_stub_fe_dai_links); + len_2 = len_1 + ARRAY_SIZE(msm_stub_be_dai_links); + + memcpy(msm_stub_dai_links, + msm_stub_fe_dai_links, + sizeof(msm_stub_fe_dai_links)); + memcpy(msm_stub_dai_links + len_1, + msm_stub_be_dai_links, + sizeof(msm_stub_be_dai_links)); + + dailink = msm_stub_dai_links; + total_links = len_2; + } + + if (card) { + card->dai_link = dailink; + card->num_links = total_links; + } + + return card; +} + +static int msm_wsa881x_init(struct snd_soc_component *component) +{ + u8 spkleft_ports[WSA881X_MAX_SWR_PORTS] = {100, 101, 102, 106}; + u8 spkright_ports[WSA881X_MAX_SWR_PORTS] = {103, 104, 105, 107}; + unsigned int ch_rate[WSA881X_MAX_SWR_PORTS] = {2400, 600, 300, 1200}; + unsigned int ch_mask[WSA881X_MAX_SWR_PORTS] = {0x1, 0xF, 0x3, 0x3}; + struct snd_soc_codec *codec = snd_soc_component_to_codec(component); + struct msm_asoc_mach_data *pdata; + struct snd_soc_dapm_context *dapm; + int ret = 0; + + if (!codec) { + pr_err("%s codec is NULL\n", __func__); + return -EINVAL; + } + + dapm = snd_soc_codec_get_dapm(codec); + + if (!strcmp(component->name_prefix, "SpkrLeft")) { + dev_dbg(codec->dev, "%s: setting left ch map to codec %s\n", + __func__, codec->component.name); + wsa881x_set_channel_map(codec, &spkleft_ports[0], + WSA881X_MAX_SWR_PORTS, &ch_mask[0], + &ch_rate[0], NULL); + if (dapm->component) { + snd_soc_dapm_ignore_suspend(dapm, "SpkrLeft IN"); + snd_soc_dapm_ignore_suspend(dapm, "SpkrLeft SPKR"); + } + } else if (!strcmp(component->name_prefix, "SpkrRight")) { + dev_dbg(codec->dev, "%s: setting right ch map to codec %s\n", + __func__, codec->component.name); + wsa881x_set_channel_map(codec, &spkright_ports[0], + WSA881X_MAX_SWR_PORTS, &ch_mask[0], + &ch_rate[0], NULL); + if (dapm->component) { + snd_soc_dapm_ignore_suspend(dapm, "SpkrRight IN"); + snd_soc_dapm_ignore_suspend(dapm, "SpkrRight SPKR"); + } + } else { + dev_err(codec->dev, "%s: wrong codec name %s\n", __func__, + codec->component.name); + ret = -EINVAL; + goto err_codec; + } + pdata = snd_soc_card_get_drvdata(component->card); + if (pdata && pdata->codec_root) + wsa881x_codec_info_create_codec_entry(pdata->codec_root, + codec); + +err_codec: + return ret; +} + +static int msm_init_wsa_dev(struct platform_device *pdev, + struct snd_soc_card *card) +{ + struct device_node *wsa_of_node; + u32 wsa_max_devs; + u32 wsa_dev_cnt; + int i; + struct msm_wsa881x_dev_info *wsa881x_dev_info; + const char *wsa_auxdev_name_prefix[1]; + char *dev_name_str = NULL; + int found = 0; + int ret = 0; + + /* Get maximum WSA device count for this platform */ + ret = of_property_read_u32(pdev->dev.of_node, + "qcom,wsa-max-devs", &wsa_max_devs); + if (ret) { + dev_dbg(&pdev->dev, + "%s: wsa-max-devs property missing in DT %s, ret = %d\n", + __func__, pdev->dev.of_node->full_name, ret); + goto err_dt; + } + if (wsa_max_devs == 0) { + dev_warn(&pdev->dev, + "%s: Max WSA devices is 0 for this target?\n", + __func__); + goto err_dt; + } + + /* Get count of WSA device phandles for this platform */ + wsa_dev_cnt = of_count_phandle_with_args(pdev->dev.of_node, + "qcom,wsa-devs", NULL); + if (wsa_dev_cnt == -ENOENT) { + dev_warn(&pdev->dev, "%s: No wsa device defined in DT.\n", + __func__); + goto err_dt; + } else if (wsa_dev_cnt <= 0) { + dev_err(&pdev->dev, + "%s: Error reading wsa device from DT. wsa_dev_cnt = %d\n", + __func__, wsa_dev_cnt); + ret = -EINVAL; + goto err_dt; + } + + /* + * Expect total phandles count to be NOT less than maximum possible + * WSA count. However, if it is less, then assign same value to + * max count as well. + */ + if (wsa_dev_cnt < wsa_max_devs) { + dev_dbg(&pdev->dev, + "%s: wsa_max_devs = %d cannot exceed wsa_dev_cnt = %d\n", + __func__, wsa_max_devs, wsa_dev_cnt); + wsa_max_devs = wsa_dev_cnt; + } + + /* Make sure prefix string passed for each WSA device */ + ret = of_property_count_strings(pdev->dev.of_node, + "qcom,wsa-aux-dev-prefix"); + if (ret != wsa_dev_cnt) { + dev_err(&pdev->dev, + "%s: expecting %d wsa prefix. Defined only %d in DT\n", + __func__, wsa_dev_cnt, ret); + ret = -EINVAL; + goto err_dt; + } + + /* + * Alloc mem to store phandle and index info of WSA device, if already + * registered with ALSA core + */ + wsa881x_dev_info = devm_kcalloc(&pdev->dev, wsa_max_devs, + sizeof(struct msm_wsa881x_dev_info), + GFP_KERNEL); + if (!wsa881x_dev_info) { + ret = -ENOMEM; + goto err_mem; + } + + /* + * search and check whether all WSA devices are already + * registered with ALSA core or not. If found a node, store + * the node and the index in a local array of struct for later + * use. + */ + for (i = 0; i < wsa_dev_cnt; i++) { + wsa_of_node = of_parse_phandle(pdev->dev.of_node, + "qcom,wsa-devs", i); + if (unlikely(!wsa_of_node)) { + /* we should not be here */ + dev_err(&pdev->dev, + "%s: wsa dev node is not present\n", + __func__); + ret = -EINVAL; + goto err_dev_node; + } + if (soc_find_component(wsa_of_node, NULL)) { + /* WSA device registered with ALSA core */ + wsa881x_dev_info[found].of_node = wsa_of_node; + wsa881x_dev_info[found].index = i; + found++; + if (found == wsa_max_devs) + break; + } + } + + if (found < wsa_max_devs) { + dev_dbg(&pdev->dev, + "%s: failed to find %d components. Found only %d\n", + __func__, wsa_max_devs, found); + return -EPROBE_DEFER; + } + dev_info(&pdev->dev, + "%s: found %d wsa881x devices registered with ALSA core\n", + __func__, found); + + card->num_aux_devs = wsa_max_devs; + card->num_configs = wsa_max_devs; + + /* Alloc array of AUX devs struct */ + msm_aux_dev = devm_kcalloc(&pdev->dev, card->num_aux_devs, + sizeof(struct snd_soc_aux_dev), + GFP_KERNEL); + if (!msm_aux_dev) { + ret = -ENOMEM; + goto err_auxdev_mem; + } + + /* Alloc array of codec conf struct */ + msm_codec_conf = devm_kcalloc(&pdev->dev, card->num_aux_devs, + sizeof(struct snd_soc_codec_conf), + GFP_KERNEL); + if (!msm_codec_conf) { + ret = -ENOMEM; + goto err_codec_conf; + } + + for (i = 0; i < card->num_aux_devs; i++) { + dev_name_str = devm_kzalloc(&pdev->dev, DEV_NAME_STR_LEN, + GFP_KERNEL); + if (!dev_name_str) { + ret = -ENOMEM; + goto err_dev_str; + } + + ret = of_property_read_string_index(pdev->dev.of_node, + "qcom,wsa-aux-dev-prefix", + wsa881x_dev_info[i].index, + wsa_auxdev_name_prefix); + if (ret) { + dev_err(&pdev->dev, + "%s: failed to read wsa aux dev prefix, ret = %d\n", + __func__, ret); + ret = -EINVAL; + goto err_dt_prop; + } + + snprintf(dev_name_str, strlen("wsa881x.%d"), "wsa881x.%d", i); + msm_aux_dev[i].name = dev_name_str; + msm_aux_dev[i].codec_name = NULL; + msm_aux_dev[i].codec_of_node = + wsa881x_dev_info[i].of_node; + msm_aux_dev[i].init = msm_wsa881x_init; + msm_codec_conf[i].dev_name = NULL; + msm_codec_conf[i].name_prefix = wsa_auxdev_name_prefix[0]; + msm_codec_conf[i].of_node = + wsa881x_dev_info[i].of_node; + } + card->codec_conf = msm_codec_conf; + card->aux_dev = msm_aux_dev; + + return 0; + +err_dt_prop: + devm_kfree(&pdev->dev, dev_name_str); +err_dev_str: + devm_kfree(&pdev->dev, msm_codec_conf); +err_codec_conf: + devm_kfree(&pdev->dev, msm_aux_dev); +err_auxdev_mem: +err_dev_node: + devm_kfree(&pdev->dev, wsa881x_dev_info); +err_mem: +err_dt: + return ret; +} + +static void i2s_auxpcm_init(struct platform_device *pdev) +{ + struct resource *muxsel; + int count; + u32 mi2s_master_slave[MI2S_MAX]; + int ret; + char *str[PCM_I2S_SEL_MAX] = { + "lpaif_pri_mode_muxsel", + "lpaif_sec_mode_muxsel", + "lpaif_tert_mode_muxsel", + "lpaif_quat_mode_muxsel" + }; + + for (count = 0; count < MI2S_MAX; count++) { + mutex_init(&mi2s_intf_conf[count].lock); + mi2s_intf_conf[count].ref_cnt = 0; + } + + for (count = 0; count < AUX_PCM_MAX; count++) { + mutex_init(&auxpcm_intf_conf[count].lock); + auxpcm_intf_conf[count].ref_cnt = 0; + } + + for (count = 0; count < PCM_I2S_SEL_MAX; count++) { + mutex_init(&mi2s_auxpcm_conf[count].lock); + mi2s_auxpcm_conf[count].pcm_i2s_sel_vt_addr = NULL; + } + + for (count = 0; count < PCM_I2S_SEL_MAX; count++) { + muxsel = platform_get_resource_byname(pdev, IORESOURCE_MEM, + str[count]); + if (muxsel) { + mi2s_auxpcm_conf[count].pcm_i2s_sel_vt_addr + = ioremap(muxsel->start, resource_size(muxsel)); + } + } + + ret = of_property_read_u32_array(pdev->dev.of_node, + "qcom,msm-mi2s-master", + mi2s_master_slave, MI2S_MAX); + if (ret) { + dev_dbg(&pdev->dev, "%s: no qcom,msm-mi2s-master in DT node\n", + __func__); + } else { + for (count = 0; count < MI2S_MAX; count++) { + mi2s_intf_conf[count].msm_is_mi2s_master = + mi2s_master_slave[count]; + } + } +} + +static void i2s_auxpcm_deinit(void) +{ + int count; + + for (count = 0; count < PCM_I2S_SEL_MAX; count++) + if (mi2s_auxpcm_conf[count].pcm_i2s_sel_vt_addr != + NULL) + iounmap( + mi2s_auxpcm_conf[count].pcm_i2s_sel_vt_addr); +} + +static int msm_asoc_machine_probe(struct platform_device *pdev) +{ + struct snd_soc_card *card; + struct msm_asoc_mach_data *pdata; + const char *mbhc_audio_jack_type = NULL; + char *mclk_freq_prop_name; + const struct of_device_id *match; + int ret; + + if (!pdev->dev.of_node) { + dev_err(&pdev->dev, "No platform supplied from device tree\n"); + return -EINVAL; + } + + pdata = devm_kzalloc(&pdev->dev, + sizeof(struct msm_asoc_mach_data), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + card = populate_snd_card_dailinks(&pdev->dev); + if (!card) { + dev_err(&pdev->dev, "%s: Card uninitialized\n", __func__); + ret = -EINVAL; + goto err; + } + card->dev = &pdev->dev; + platform_set_drvdata(pdev, card); + snd_soc_card_set_drvdata(card, pdata); + + ret = snd_soc_of_parse_card_name(card, "qcom,model"); + if (ret) { + dev_err(&pdev->dev, "parse card name failed, err:%d\n", + ret); + goto err; + } + + ret = snd_soc_of_parse_audio_routing(card, "qcom,audio-routing"); + if (ret) { + dev_err(&pdev->dev, "parse audio routing failed, err:%d\n", + ret); + goto err; + } + + match = of_match_node(msm8998_asoc_machine_of_match, + pdev->dev.of_node); + if (!match) { + dev_err(&pdev->dev, "%s: no matched codec is found.\n", + __func__); + goto err; + } + + if (!strcmp(match->data, "tasha_codec")) + mclk_freq_prop_name = "qcom,tasha-mclk-clk-freq"; + else + mclk_freq_prop_name = "qcom,tavil-mclk-clk-freq"; + + ret = of_property_read_u32(pdev->dev.of_node, + mclk_freq_prop_name, &pdata->mclk_freq); + if (ret) { + dev_err(&pdev->dev, + "Looking up %s property in node %s failed, err%d\n", + mclk_freq_prop_name, + pdev->dev.of_node->full_name, ret); + goto err; + } + + if (pdata->mclk_freq != CODEC_EXT_CLK_RATE) { + dev_err(&pdev->dev, "unsupported mclk freq %u\n", + pdata->mclk_freq); + ret = -EINVAL; + goto err; + } + + ret = msm_populate_dai_link_component_of_node(card); + if (ret) { + ret = -EPROBE_DEFER; + goto err; + } + ret = msm_init_wsa_dev(pdev, card); + if (ret) + goto err; + + ret = devm_snd_soc_register_card(&pdev->dev, card); + if (ret == -EPROBE_DEFER) { + if (codec_reg_done) + ret = -EINVAL; + goto err; + } else if (ret) { + dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", + ret); + goto err; + } + dev_info(&pdev->dev, "Sound card %s registered\n", card->name); + spdev = pdev; + + ret = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev); + if (ret) { + dev_dbg(&pdev->dev, "%s: failed to add child nodes, ret=%d\n", + __func__, ret); + } else { + pdata->hph_en1_gpio_p = of_parse_phandle(pdev->dev.of_node, + "qcom,hph-en1-gpio", 0); + if (!pdata->hph_en1_gpio_p) { + dev_dbg(&pdev->dev, "property %s not detected in node %s", + "qcom,hph-en1-gpio", + pdev->dev.of_node->full_name); + } + + pdata->hph_en0_gpio_p = of_parse_phandle(pdev->dev.of_node, + "qcom,hph-en0-gpio", 0); + if (!pdata->hph_en0_gpio_p) { + dev_dbg(&pdev->dev, "property %s not detected in node %s", + "qcom,hph-en0-gpio", + pdev->dev.of_node->full_name); + } + } + + ret = of_property_read_string(pdev->dev.of_node, + "qcom,mbhc-audio-jack-type", &mbhc_audio_jack_type); + if (ret) { + dev_dbg(&pdev->dev, "Looking up %s property in node %s failed", + "qcom,mbhc-audio-jack-type", + pdev->dev.of_node->full_name); + dev_dbg(&pdev->dev, "Jack type properties set to default"); + } else { + if (!strcmp(mbhc_audio_jack_type, "4-pole-jack")) { + wcd_mbhc_cfg.enable_anc_mic_detect = false; + dev_dbg(&pdev->dev, "This hardware has 4 pole jack"); + } else if (!strcmp(mbhc_audio_jack_type, "5-pole-jack")) { + wcd_mbhc_cfg.enable_anc_mic_detect = true; + dev_dbg(&pdev->dev, "This hardware has 5 pole jack"); + } else if (!strcmp(mbhc_audio_jack_type, "6-pole-jack")) { + wcd_mbhc_cfg.enable_anc_mic_detect = true; + dev_dbg(&pdev->dev, "This hardware has 6 pole jack"); + } else { + wcd_mbhc_cfg.enable_anc_mic_detect = false; + dev_dbg(&pdev->dev, "Unknown value, set to default"); + } + } + /* + * Parse US-Euro gpio info from DT. Report no error if us-euro + * entry is not found in DT file as some targets do not support + * US-Euro detection + */ + pdata->us_euro_gpio = of_get_named_gpio(pdev->dev.of_node, + "qcom,us-euro-gpios", 0); + if (!gpio_is_valid(pdata->us_euro_gpio)) + pdata->us_euro_gpio_p = of_parse_phandle(pdev->dev.of_node, + "qcom,us-euro-gpios", 0); + if (!gpio_is_valid(pdata->us_euro_gpio) && (!pdata->us_euro_gpio_p)) { + dev_dbg(&pdev->dev, "property %s not detected in node %s", + "qcom,us-euro-gpios", pdev->dev.of_node->full_name); + } else { + dev_dbg(&pdev->dev, "%s detected", + "qcom,us-euro-gpios"); + wcd_mbhc_cfg.swap_gnd_mic = msm_swap_gnd_mic; + } + + ret = msm_prepare_us_euro(card); + if (ret) + dev_dbg(&pdev->dev, "msm_prepare_us_euro failed (%d)\n", + ret); + + /* Parse pinctrl info from devicetree */ + ret = msm_get_pinctrl(pdev); + if (!ret) { + pr_debug("%s: pinctrl parsing successful\n", __func__); + } else { + dev_dbg(&pdev->dev, + "%s: Parsing pinctrl failed with %d. Cannot use Ports\n", + __func__, ret); + ret = 0; + } + + i2s_auxpcm_init(pdev); + + is_initial_boot = true; + ret = audio_notifier_register("msm8998", AUDIO_NOTIFIER_ADSP_DOMAIN, + &service_nb); + if (ret < 0) + pr_err("%s: Audio notifier register failed ret = %d\n", + __func__, ret); + + return 0; +err: + if (pdata->us_euro_gpio > 0) { + dev_dbg(&pdev->dev, "%s free us_euro gpio %d\n", + __func__, pdata->us_euro_gpio); + gpio_free(pdata->us_euro_gpio); + pdata->us_euro_gpio = 0; + } + msm_release_pinctrl(pdev); + devm_kfree(&pdev->dev, pdata); + return ret; +} + +static int msm_asoc_machine_remove(struct platform_device *pdev) +{ + struct snd_soc_card *card = platform_get_drvdata(pdev); + struct msm_asoc_mach_data *pdata = + snd_soc_card_get_drvdata(card); + + if (gpio_is_valid(pdata->us_euro_gpio)) + gpio_free(pdata->us_euro_gpio); + i2s_auxpcm_deinit(); + + snd_soc_unregister_card(card); + audio_notifier_deregister("msm8998"); + return 0; +} + +static struct platform_driver msm8998_asoc_machine_driver = { + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + .pm = &snd_soc_pm_ops, + .of_match_table = msm8998_asoc_machine_of_match, + }, + .probe = msm_asoc_machine_probe, + .remove = msm_asoc_machine_remove, +}; +module_platform_driver(msm8998_asoc_machine_driver); + +MODULE_DESCRIPTION("ALSA SoC msm"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:" DRV_NAME); +MODULE_DEVICE_TABLE(of, msm8998_asoc_machine_of_match); diff --git a/audio-kernel/asoc/platform_init.c b/audio-kernel/asoc/platform_init.c new file mode 100644 index 000000000..79d0e4199 --- /dev/null +++ b/audio-kernel/asoc/platform_init.c @@ -0,0 +1,69 @@ +/* +Copyright (c) 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 +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 +#include +#include "platform_init.h" + +static int __init audio_platform_init(void) +{ + msm_compress_dsp_init(); + msm_fe_dai_init(); + msm_dai_q6_hdmi_init(); + msm_dai_q6_init(); + msm_dai_slim_init(); + msm_dai_stub_init(); + msm_lsm_client_init(); + msm_pcm_afe_init(); + msm_pcm_dtmf_init(); + msm_pcm_hostless_init(); + msm_voice_host_init(); + msm_pcm_loopback_init(); + msm_pcm_noirq_init(); + msm_pcm_dsp_init(); + msm_soc_routing_platform_init(); + msm_pcm_voice_init(); + msm_pcm_voip_init(); + msm_transcode_loopback_init(); + + return 0; +} + +static void audio_platform_exit(void) +{ + msm_transcode_loopback_exit(); + msm_pcm_voip_exit(); + msm_pcm_voice_exit(); + msm_soc_routing_platform_exit(); + msm_pcm_dsp_exit(); + msm_pcm_noirq_exit(); + msm_pcm_loopback_exit(); + msm_voice_host_exit(); + msm_pcm_hostless_exit(); + msm_pcm_dtmf_exit(); + msm_pcm_afe_exit(); + msm_lsm_client_exit(); + msm_dai_stub_exit(); + msm_dai_slim_exit(); + msm_dai_q6_exit(); + msm_dai_q6_hdmi_exit(); + msm_fe_dai_exit(); + msm_compress_dsp_exit(); +} + +module_init(audio_platform_init); +module_exit(audio_platform_exit); + +MODULE_DESCRIPTION("Audio Platform driver"); +MODULE_LICENSE("GPL v2"); diff --git a/audio-kernel/asoc/platform_init.h b/audio-kernel/asoc/platform_init.h new file mode 100644 index 000000000..db9b32d2e --- /dev/null +++ b/audio-kernel/asoc/platform_init.h @@ -0,0 +1,58 @@ +/* +Copyright (c) 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 +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 __PLATFORM_INIT_H__ +#define __PLATFORM_INIT_H__ +int msm_compress_dsp_init(void); +int msm_fe_dai_init(void); +int msm_dai_q6_hdmi_init(void); +int msm_dai_q6_init(void); +int msm_dai_slim_init(void); +int msm_dai_stub_init(void); +int msm_lsm_client_init(void); +int msm_pcm_afe_init(void); +int msm_pcm_dtmf_init(void); +int msm_pcm_hostless_init(void); +int msm_voice_host_init(void); +int msm_pcm_loopback_init(void); +int msm_pcm_noirq_init(void); +int msm_pcm_dsp_init(void); +int msm_soc_routing_platform_init(void); +int msm_pcm_voice_init(void); +int msm_pcm_voip_init(void); +int msm_transcode_loopback_init(void); +int msm_cpe_lsm_init(void); + +void msm_cpe_lsm_exit(void); +void msm_transcode_loopback_exit(void); +void msm_pcm_voip_exit(void); +void msm_pcm_voice_exit(void); +void msm_soc_routing_platform_exit(void); +void msm_pcm_dsp_exit(void); +void msm_pcm_noirq_exit(void); +void msm_pcm_loopback_exit(void); +void msm_voice_host_exit(void); +void msm_pcm_hostless_exit(void); +void msm_pcm_dtmf_exit(void); +void msm_pcm_afe_exit(void); +void msm_lsm_client_exit(void); +void msm_dai_stub_exit(void); +void msm_dai_slim_exit(void); +void msm_dai_q6_exit(void); +void msm_dai_q6_hdmi_exit(void); +void msm_fe_dai_exit(void); +void msm_compress_dsp_exit(void); + +#endif + diff --git a/audio-kernel/asoc/qcs405.c b/audio-kernel/asoc/qcs405.c new file mode 100644 index 000000000..a0f54600e --- /dev/null +++ b/audio-kernel/asoc/qcs405.c @@ -0,0 +1,8645 @@ +/* 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "device_event.h" +#include "msm-pcm-routing-v2.h" +#include "codecs/msm-cdc-pinctrl.h" +#include "codecs/wcd9335.h" +#include "codecs/wsa881x.h" +#include "codecs/csra66x0/csra66x0.h" +#include +#include "codecs/bolero/bolero-cdc.h" +#include "codecs/bolero/wsa-macro.h" + +#define DRV_NAME "qcs405-asoc-snd" + +#define __CHIPSET__ "QCS405 " +#define MSM_DAILINK_NAME(name) (__CHIPSET__#name) + +#define DEV_NAME_STR_LEN 32 + +#define SAMPLING_RATE_8KHZ 8000 +#define SAMPLING_RATE_11P025KHZ 11025 +#define SAMPLING_RATE_16KHZ 16000 +#define SAMPLING_RATE_22P05KHZ 22050 +#define SAMPLING_RATE_32KHZ 32000 +#define SAMPLING_RATE_44P1KHZ 44100 +#define SAMPLING_RATE_48KHZ 48000 +#define SAMPLING_RATE_88P2KHZ 88200 +#define SAMPLING_RATE_96KHZ 96000 +#define SAMPLING_RATE_176P4KHZ 176400 +#define SAMPLING_RATE_192KHZ 192000 +#define SAMPLING_RATE_352P8KHZ 352800 +#define SAMPLING_RATE_384KHZ 384000 + +#define SPDIF_TX_CORE_CLK_163_P84_MHZ 163840000 +#define TLMM_EAST_SPARE 0x07BA0000 +#define TLMM_SPDIF_HDMI_ARC_CTL 0x07BA2000 + +#define WSA8810_NAME_1 "wsa881x.20170211" +#define WSA8810_NAME_2 "wsa881x.20170212" +#define WCN_CDC_SLIM_RX_CH_MAX 2 +#define WCN_CDC_SLIM_TX_CH_MAX 4 +#define TDM_CHANNEL_MAX 8 +#define BT_SLIM_TX SLIM_TX_9 + +#define ADSP_STATE_READY_TIMEOUT_MS 3000 +#define MSM_LL_QOS_VALUE 300 /* time in us to ensure LPM doesn't go in C3/C4 */ + +enum { + SLIM_RX_0 = 0, + SLIM_RX_1, + SLIM_RX_2, + SLIM_RX_3, + SLIM_RX_4, + SLIM_RX_5, + SLIM_RX_6, + SLIM_RX_7, + SLIM_RX_MAX, +}; + +enum { + SLIM_TX_0 = 0, + SLIM_TX_1, + SLIM_TX_2, + SLIM_TX_3, + SLIM_TX_4, + SLIM_TX_5, + SLIM_TX_6, + SLIM_TX_7, + SLIM_TX_8, + SLIM_TX_9, + SLIM_TX_MAX, +}; + +enum { + PRIM_MI2S = 0, + SEC_MI2S, + TERT_MI2S, + QUAT_MI2S, + QUIN_MI2S, + SEN_MI2S, + MI2S_MAX, +}; + +enum { + PRIM_AUX_PCM = 0, + SEC_AUX_PCM, + TERT_AUX_PCM, + QUAT_AUX_PCM, + QUIN_AUX_PCM, + SEN_AUX_PCM, + AUX_PCM_MAX, +}; + +enum { + WSA_CDC_DMA_RX_0 = 0, + WSA_CDC_DMA_RX_1, + CDC_DMA_RX_MAX, +}; + +enum { + WSA_CDC_DMA_TX_0 = 0, + WSA_CDC_DMA_TX_1, + WSA_CDC_DMA_TX_2, + VA_CDC_DMA_TX_0, + VA_CDC_DMA_TX_1, + CDC_DMA_TX_MAX, +}; + +enum { + PRIM_SPDIF_RX = 0, + SEC_SPDIF_RX, + SPDIF_RX_MAX, +}; + +enum { + PRIM_SPDIF_TX = 0, + SEC_SPDIF_TX, + SPDIF_TX_MAX, +}; + +struct mi2s_conf { + struct mutex lock; + u32 ref_cnt; + u32 msm_is_mi2s_master; +}; + +static u32 mi2s_ebit_clk[MI2S_MAX] = { + Q6AFE_LPASS_CLK_ID_PRI_MI2S_EBIT, + Q6AFE_LPASS_CLK_ID_SEC_MI2S_EBIT, + Q6AFE_LPASS_CLK_ID_TER_MI2S_EBIT, + Q6AFE_LPASS_CLK_ID_QUAD_MI2S_EBIT, + Q6AFE_LPASS_CLK_ID_QUI_MI2S_EBIT, + Q6AFE_LPASS_CLK_ID_SEN_MI2S_EBIT +}; + +struct dev_config { + u32 sample_rate; + u32 bit_format; + u32 channels; +}; + +struct msm_wsa881x_dev_info { + struct device_node *of_node; + u32 index; +}; + +struct msm_csra66x0_dev_info { + struct device_node *of_node; + u32 index; +}; + +struct msm_asoc_mach_data { + struct snd_info_entry *codec_root; + struct device_node *dmic_01_gpio_p; /* used by pinctrl API */ + struct device_node *dmic_23_gpio_p; /* used by pinctrl API */ + struct device_node *dmic_45_gpio_p; /* used by pinctrl API */ + struct device_node *dmic_67_gpio_p; /* used by pinctrl API */ + struct device_node *mi2s_gpio_p[MI2S_MAX]; /* used by pinctrl API */ + int dmic_01_gpio_cnt; + int dmic_23_gpio_cnt; + int dmic_45_gpio_cnt; + int dmic_67_gpio_cnt; + struct regulator *tdm_micb_supply; + u32 tdm_micb_voltage; + u32 tdm_micb_current; + bool codec_is_csra; +}; + +struct msm_asoc_wcd93xx_codec { + void* (*get_afe_config_fn)(struct snd_soc_codec *codec, + enum afe_config_type config_type); +}; + +static const char *const pin_states[] = {"sleep", "i2s-active", + "tdm-active"}; + +enum { + TDM_0 = 0, + TDM_1, + TDM_2, + TDM_3, + TDM_4, + TDM_5, + TDM_6, + TDM_7, + TDM_PORT_MAX, +}; + +enum { + TDM_PRI = 0, + TDM_SEC, + TDM_TERT, + TDM_QUAT, + TDM_QUIN, + TDM_INTERFACE_MAX, +}; + +struct tdm_port { + u32 mode; + u32 channel; +}; + +/* TDM default config */ +static struct dev_config tdm_rx_cfg[TDM_INTERFACE_MAX][TDM_PORT_MAX] = { + { /* PRI TDM */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_0 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_1 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_2 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_3 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_4 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_5 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_6 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_7 */ + }, + { /* SEC TDM */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_0 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_1 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_2 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_3 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_4 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_5 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_6 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_7 */ + }, + { /* TERT TDM */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_0 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_1 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_2 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_3 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_4 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_5 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_6 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_7 */ + }, + { /* QUAT TDM */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_0 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_1 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_2 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_3 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_4 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_5 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_6 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_7 */ + }, + { /* QUIN TDM */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_0 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_1 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_2 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_3 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_4 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_5 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_6 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_7 */ + } +}; + +/* TDM default config */ +static struct dev_config tdm_tx_cfg[TDM_INTERFACE_MAX][TDM_PORT_MAX] = { + { /* PRI TDM */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_0 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_1 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_2 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_3 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_4 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_5 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_6 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_7 */ + }, + { /* SEC TDM */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_0 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_1 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_2 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_3 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_4 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_5 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_6 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_7 */ + }, + { /* TERT TDM */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_0 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_1 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_2 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_3 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_4 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_5 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_6 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_7 */ + }, + { /* QUAT TDM */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_0 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_1 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_2 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_3 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_4 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_5 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_6 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_7 */ + }, + { /* QUIN TDM */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_0 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_1 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_2 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_3 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_4 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_5 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_6 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_7 */ + } +}; + + +/* Default configuration of slimbus channels */ +static struct dev_config slim_rx_cfg[] = { + [SLIM_RX_0] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_RX_1] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_RX_2] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_RX_3] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_RX_4] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_RX_5] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_RX_6] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_RX_7] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, +}; + +static struct dev_config slim_tx_cfg[] = { + [SLIM_TX_0] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_TX_1] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_TX_2] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_TX_3] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_TX_4] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_TX_5] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_TX_6] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_TX_7] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_TX_8] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, + [SLIM_TX_9] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, +}; + +/* Default configuration of Codec DMA Interface Tx */ +static struct dev_config cdc_dma_rx_cfg[] = { + [WSA_CDC_DMA_RX_0] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, + [WSA_CDC_DMA_RX_1] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, +}; + +/* Default configuration of Codec DMA Interface Rx */ +static struct dev_config cdc_dma_tx_cfg[] = { + [WSA_CDC_DMA_TX_0] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, + [WSA_CDC_DMA_TX_1] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, + [WSA_CDC_DMA_TX_2] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, + [VA_CDC_DMA_TX_0] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 8}, + [VA_CDC_DMA_TX_1] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 8}, +}; + +static struct dev_config usb_rx_cfg = { + .sample_rate = SAMPLING_RATE_48KHZ, + .bit_format = SNDRV_PCM_FORMAT_S16_LE, + .channels = 2, +}; + +static struct dev_config usb_tx_cfg = { + .sample_rate = SAMPLING_RATE_48KHZ, + .bit_format = SNDRV_PCM_FORMAT_S16_LE, + .channels = 1, +}; + +static struct dev_config proxy_rx_cfg = { + .sample_rate = SAMPLING_RATE_48KHZ, + .bit_format = SNDRV_PCM_FORMAT_S16_LE, + .channels = 2, +}; + +/* Default configuration of MI2S channels */ +static struct dev_config mi2s_rx_cfg[] = { + [PRIM_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, + [SEC_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, + [TERT_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, + [QUAT_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, + [QUIN_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, + [SEN_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, +}; + +/* Default configuration of SPDIF channels */ +static struct dev_config spdif_rx_cfg[] = { + [PRIM_SPDIF_RX] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, + [SEC_SPDIF_RX] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, +}; + +static struct dev_config spdif_tx_cfg[] = { + [PRIM_SPDIF_TX] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, + [SEC_SPDIF_TX] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, +}; + +static struct dev_config mi2s_tx_cfg[] = { + [PRIM_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SEC_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [TERT_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [QUAT_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [QUIN_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SEN_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, +}; + +static struct dev_config aux_pcm_rx_cfg[] = { + [PRIM_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SEC_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [TERT_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [QUAT_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [QUIN_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SEN_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, +}; + +static struct dev_config aux_pcm_tx_cfg[] = { + [PRIM_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SEC_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [TERT_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [QUAT_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [QUIN_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SEN_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, +}; + +static int msm_vi_feed_tx_ch = 2; +static const char *const slim_rx_ch_text[] = {"One", "Two"}; +static const char *const slim_tx_ch_text[] = {"One", "Two", "Three", "Four", + "Five", "Six", "Seven", + "Eight"}; +static const char *const vi_feed_ch_text[] = {"One", "Two"}; +static char const *bit_format_text[] = {"S16_LE", "S24_LE", "S24_3LE", + "S32_LE"}; +static char const *slim_sample_rate_text[] = {"KHZ_8", "KHZ_16", + "KHZ_32", "KHZ_44P1", "KHZ_48", + "KHZ_88P2", "KHZ_96", "KHZ_176P4", + "KHZ_192", "KHZ_352P8", "KHZ_384"}; +static char const *bt_sample_rate_text[] = {"KHZ_8", "KHZ_16", + "KHZ_44P1", "KHZ_48", + "KHZ_88P2", "KHZ_96"}; +static const char *const usb_ch_text[] = {"One", "Two", "Three", "Four", + "Five", "Six", "Seven", + "Eight"}; +static char const *ch_text[] = {"Two", "Three", "Four", "Five", + "Six", "Seven", "Eight"}; +static char const *usb_sample_rate_text[] = {"KHZ_8", "KHZ_11P025", + "KHZ_16", "KHZ_22P05", + "KHZ_32", "KHZ_44P1", "KHZ_48", + "KHZ_88P2", "KHZ_96", "KHZ_176P4", + "KHZ_192", "KHZ_352P8", "KHZ_384"}; +static char const *tdm_ch_text[] = {"One", "Two", "Three", "Four", + "Five", "Six", "Seven", "Eight"}; +static char const *tdm_bit_format_text[] = {"S16_LE", "S24_LE", "S32_LE"}; +static char const *tdm_sample_rate_text[] = {"KHZ_8", "KHZ_16", "KHZ_32", + "KHZ_48", "KHZ_176P4", + "KHZ_352P8"}; +static const char *const auxpcm_rate_text[] = {"KHZ_8", "KHZ_16"}; +static char const *mi2s_rate_text[] = {"KHZ_8", "KHZ_11P025", "KHZ_16", + "KHZ_22P05", "KHZ_32", "KHZ_44P1", + "KHZ_48", "KHZ_96", "KHZ_192", "KHZ_384"}; +static const char *const mi2s_ch_text[] = { + "One", "Two", "Three", "Four", "Five", "Six", "Seven", + "Eight", "Nine", "Ten", "Eleven", "Twelve", "Thirteen", + "Fourteen", "Fifteen", "Sixteen" +}; +static const char *const qos_text[] = {"Disable", "Enable"}; + +static const char *const cdc_dma_rx_ch_text[] = {"One", "Two"}; +static const char *const cdc_dma_tx_ch_text[] = {"One", "Two", "Three", "Four", + "Five", "Six", "Seven", + "Eight"}; +static char const *cdc_dma_sample_rate_text[] = {"KHZ_8", "KHZ_11P025", + "KHZ_16", "KHZ_22P05", + "KHZ_32", "KHZ_44P1", "KHZ_48", + "KHZ_88P2", "KHZ_96", "KHZ_176P4", + "KHZ_192", "KHZ_352P8", "KHZ_384"}; + +static const char *spdif_rate_text[] = {"KHZ_32", "KHZ_44P1", "KHZ_48", + "KHZ_88P2", "KHZ_96", "KHZ_176P4", + "KHZ_192"}; +static const char *spdif_ch_text[] = {"One", "Two"}; +static const char *spdif_bit_format_text[] = {"S16_LE", "S24_LE"}; + +static SOC_ENUM_SINGLE_EXT_DECL(slim_0_rx_chs, slim_rx_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_2_rx_chs, slim_rx_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_0_tx_chs, slim_tx_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_1_tx_chs, slim_tx_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_5_rx_chs, slim_rx_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_6_rx_chs, slim_rx_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(usb_rx_chs, usb_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(usb_tx_chs, usb_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(vi_feed_tx_chs, vi_feed_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(proxy_rx_chs, ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_0_rx_format, bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_5_rx_format, bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_6_rx_format, bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_0_tx_format, bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(usb_rx_format, bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(usb_tx_format, bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_0_rx_sample_rate, slim_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_2_rx_sample_rate, slim_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_0_tx_sample_rate, slim_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_5_rx_sample_rate, slim_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_6_rx_sample_rate, slim_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(bt_sample_rate, bt_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(bt_sample_rate_sink, bt_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(usb_rx_sample_rate, usb_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(usb_tx_sample_rate, usb_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(tdm_tx_chs, tdm_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(tdm_tx_format, tdm_bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(tdm_tx_sample_rate, tdm_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(tdm_rx_chs, tdm_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(tdm_rx_format, tdm_bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(tdm_rx_sample_rate, tdm_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(prim_aux_pcm_rx_sample_rate, auxpcm_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(sec_aux_pcm_rx_sample_rate, auxpcm_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(tert_aux_pcm_rx_sample_rate, auxpcm_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(quat_aux_pcm_rx_sample_rate, auxpcm_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(quin_aux_pcm_rx_sample_rate, auxpcm_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(sen_aux_pcm_rx_sample_rate, auxpcm_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(prim_aux_pcm_tx_sample_rate, auxpcm_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(sec_aux_pcm_tx_sample_rate, auxpcm_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(tert_aux_pcm_tx_sample_rate, auxpcm_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(quat_aux_pcm_tx_sample_rate, auxpcm_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(quin_aux_pcm_tx_sample_rate, auxpcm_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(sen_aux_pcm_tx_sample_rate, auxpcm_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(prim_mi2s_rx_sample_rate, mi2s_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(sec_mi2s_rx_sample_rate, mi2s_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(tert_mi2s_rx_sample_rate, mi2s_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(quat_mi2s_rx_sample_rate, mi2s_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(quin_mi2s_rx_sample_rate, mi2s_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(sen_mi2s_rx_sample_rate, mi2s_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(prim_mi2s_tx_sample_rate, mi2s_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(sec_mi2s_tx_sample_rate, mi2s_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(tert_mi2s_tx_sample_rate, mi2s_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(quat_mi2s_tx_sample_rate, mi2s_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(quin_mi2s_tx_sample_rate, mi2s_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(sen_mi2s_tx_sample_rate, mi2s_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(prim_mi2s_rx_chs, mi2s_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(prim_mi2s_tx_chs, mi2s_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(sec_mi2s_rx_chs, mi2s_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(sec_mi2s_tx_chs, mi2s_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(tert_mi2s_rx_chs, mi2s_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(tert_mi2s_tx_chs, mi2s_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(quat_mi2s_rx_chs, mi2s_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(quat_mi2s_tx_chs, mi2s_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(quin_mi2s_rx_chs, mi2s_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(quin_mi2s_tx_chs, mi2s_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(sen_mi2s_rx_chs, mi2s_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(sen_mi2s_tx_chs, mi2s_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(mi2s_rx_format, bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(mi2s_tx_format, bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(aux_pcm_rx_format, bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(aux_pcm_tx_format, bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(wsa_cdc_dma_rx_0_chs, cdc_dma_rx_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(wsa_cdc_dma_rx_1_chs, cdc_dma_rx_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(wsa_cdc_dma_tx_0_chs, cdc_dma_tx_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(wsa_cdc_dma_tx_1_chs, cdc_dma_tx_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(wsa_cdc_dma_tx_2_chs, cdc_dma_tx_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(va_cdc_dma_tx_0_chs, cdc_dma_tx_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(va_cdc_dma_tx_1_chs, cdc_dma_tx_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(wsa_cdc_dma_rx_0_format, bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(wsa_cdc_dma_rx_1_format, bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(wsa_cdc_dma_tx_1_format, bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(wsa_cdc_dma_tx_2_format, bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(va_cdc_dma_tx_0_format, bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(va_cdc_dma_tx_1_format, bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(wsa_cdc_dma_rx_0_sample_rate, + cdc_dma_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(wsa_cdc_dma_rx_1_sample_rate, + cdc_dma_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(wsa_cdc_dma_tx_0_sample_rate, + cdc_dma_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(wsa_cdc_dma_tx_1_sample_rate, + cdc_dma_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(wsa_cdc_dma_tx_2_sample_rate, + cdc_dma_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(va_cdc_dma_tx_0_sample_rate, + cdc_dma_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(va_cdc_dma_tx_1_sample_rate, + cdc_dma_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(spdif_rx_sample_rate, spdif_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(spdif_tx_sample_rate, spdif_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(spdif_rx_chs, spdif_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(spdif_tx_chs, spdif_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(spdif_rx_format, spdif_bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(spdif_tx_format, spdif_bit_format_text); + +static struct platform_device *spdev; + +static bool is_initial_boot; +static bool codec_reg_done; +static struct snd_soc_aux_dev *msm_aux_dev; +static struct snd_soc_codec_conf *msm_codec_conf; +static struct msm_asoc_wcd93xx_codec msm_codec_fn; + +static int msm_snd_enable_codec_ext_clk(struct snd_soc_codec *codec, + int enable, bool dapm); +static int msm_wsa881x_init(struct snd_soc_component *component); +static int msm_snd_vad_cfg_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); + +static struct snd_soc_dapm_route wcd_audio_paths[] = { + {"MIC BIAS1", NULL, "MCLK TX"}, + {"MIC BIAS2", NULL, "MCLK TX"}, + {"MIC BIAS3", NULL, "MCLK TX"}, + {"MIC BIAS4", NULL, "MCLK TX"}, +}; + +static struct afe_clk_set mi2s_clk[MI2S_MAX] = { + { + AFE_API_VERSION_I2S_CONFIG, + Q6AFE_LPASS_CLK_ID_PRI_MI2S_IBIT, + Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ, + Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO, + Q6AFE_LPASS_CLK_ROOT_DEFAULT, + 0, + }, + { + AFE_API_VERSION_I2S_CONFIG, + Q6AFE_LPASS_CLK_ID_SEC_MI2S_IBIT, + Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ, + Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO, + Q6AFE_LPASS_CLK_ROOT_DEFAULT, + 0, + }, + { + AFE_API_VERSION_I2S_CONFIG, + Q6AFE_LPASS_CLK_ID_TER_MI2S_IBIT, + Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ, + Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO, + Q6AFE_LPASS_CLK_ROOT_DEFAULT, + 0, + }, + { + AFE_API_VERSION_I2S_CONFIG, + Q6AFE_LPASS_CLK_ID_QUAD_MI2S_IBIT, + Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ, + Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO, + Q6AFE_LPASS_CLK_ROOT_DEFAULT, + 0, + }, + { + AFE_API_VERSION_I2S_CONFIG, + Q6AFE_LPASS_CLK_ID_QUI_MI2S_IBIT, + Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ, + Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO, + Q6AFE_LPASS_CLK_ROOT_DEFAULT, + 0, + }, + { + AFE_API_VERSION_I2S_CONFIG, + Q6AFE_LPASS_CLK_ID_SEN_MI2S_IBIT, + Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ, + Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO, + Q6AFE_LPASS_CLK_ROOT_DEFAULT, + 0, + } + +}; + +static struct mi2s_conf mi2s_intf_conf[MI2S_MAX]; + +static int slim_get_sample_rate_val(int sample_rate) +{ + int sample_rate_val = 0; + + switch (sample_rate) { + case SAMPLING_RATE_8KHZ: + sample_rate_val = 0; + break; + case SAMPLING_RATE_16KHZ: + sample_rate_val = 1; + break; + case SAMPLING_RATE_32KHZ: + sample_rate_val = 2; + break; + case SAMPLING_RATE_44P1KHZ: + sample_rate_val = 3; + break; + case SAMPLING_RATE_48KHZ: + sample_rate_val = 4; + break; + case SAMPLING_RATE_88P2KHZ: + sample_rate_val = 5; + break; + case SAMPLING_RATE_96KHZ: + sample_rate_val = 6; + break; + case SAMPLING_RATE_176P4KHZ: + sample_rate_val = 7; + break; + case SAMPLING_RATE_192KHZ: + sample_rate_val = 8; + break; + case SAMPLING_RATE_352P8KHZ: + sample_rate_val = 9; + break; + case SAMPLING_RATE_384KHZ: + sample_rate_val = 10; + break; + default: + sample_rate_val = 4; + break; + } + return sample_rate_val; +} + +static int slim_get_sample_rate(int value) +{ + int sample_rate = 0; + + switch (value) { + case 0: + sample_rate = SAMPLING_RATE_8KHZ; + break; + case 1: + sample_rate = SAMPLING_RATE_16KHZ; + break; + case 2: + sample_rate = SAMPLING_RATE_32KHZ; + break; + case 3: + sample_rate = SAMPLING_RATE_44P1KHZ; + break; + case 4: + sample_rate = SAMPLING_RATE_48KHZ; + break; + case 5: + sample_rate = SAMPLING_RATE_88P2KHZ; + break; + case 6: + sample_rate = SAMPLING_RATE_96KHZ; + break; + case 7: + sample_rate = SAMPLING_RATE_176P4KHZ; + break; + case 8: + sample_rate = SAMPLING_RATE_192KHZ; + break; + case 9: + sample_rate = SAMPLING_RATE_352P8KHZ; + break; + case 10: + sample_rate = SAMPLING_RATE_384KHZ; + break; + default: + sample_rate = SAMPLING_RATE_48KHZ; + break; + } + return sample_rate; +} + +static int slim_get_bit_format_val(int bit_format) +{ + int val = 0; + + switch (bit_format) { + case SNDRV_PCM_FORMAT_S32_LE: + val = 3; + break; + case SNDRV_PCM_FORMAT_S24_3LE: + val = 2; + break; + case SNDRV_PCM_FORMAT_S24_LE: + val = 1; + break; + case SNDRV_PCM_FORMAT_S16_LE: + default: + val = 0; + break; + } + return val; +} + +static int slim_get_bit_format(int val) +{ + int bit_fmt = SNDRV_PCM_FORMAT_S16_LE; + + switch (val) { + case 0: + bit_fmt = SNDRV_PCM_FORMAT_S16_LE; + break; + case 1: + bit_fmt = SNDRV_PCM_FORMAT_S24_LE; + break; + case 2: + bit_fmt = SNDRV_PCM_FORMAT_S24_3LE; + break; + case 3: + bit_fmt = SNDRV_PCM_FORMAT_S32_LE; + break; + default: + bit_fmt = SNDRV_PCM_FORMAT_S16_LE; + break; + } + return bit_fmt; +} + +static int slim_get_port_idx(struct snd_kcontrol *kcontrol) +{ + int port_id = 0; + + if (strnstr(kcontrol->id.name, "SLIM_0_RX", sizeof("SLIM_0_RX"))) { + port_id = SLIM_RX_0; + } else if (strnstr(kcontrol->id.name, + "SLIM_2_RX", sizeof("SLIM_2_RX"))) { + port_id = SLIM_RX_2; + } else if (strnstr(kcontrol->id.name, + "SLIM_5_RX", sizeof("SLIM_5_RX"))) { + port_id = SLIM_RX_5; + } else if (strnstr(kcontrol->id.name, + "SLIM_6_RX", sizeof("SLIM_6_RX"))) { + port_id = SLIM_RX_6; + } else if (strnstr(kcontrol->id.name, + "SLIM_0_TX", sizeof("SLIM_0_TX"))) { + port_id = SLIM_TX_0; + } else if (strnstr(kcontrol->id.name, + "SLIM_1_TX", sizeof("SLIM_1_TX"))) { + port_id = SLIM_TX_1; + } else { + pr_err("%s: unsupported channel: %s", + __func__, kcontrol->id.name); + return -EINVAL; + } + + return port_id; +} + +static int slim_rx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = slim_get_port_idx(kcontrol); + + if (ch_num < 0) + return ch_num; + + ucontrol->value.enumerated.item[0] = + slim_get_sample_rate_val(slim_rx_cfg[ch_num].sample_rate); + + pr_debug("%s: slim[%d]_rx_sample_rate = %d, item = %d\n", __func__, + ch_num, slim_rx_cfg[ch_num].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int slim_rx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = slim_get_port_idx(kcontrol); + + if (ch_num < 0) + return ch_num; + + slim_rx_cfg[ch_num].sample_rate = + slim_get_sample_rate(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: slim[%d]_rx_sample_rate = %d, item = %d\n", __func__, + ch_num, slim_rx_cfg[ch_num].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int slim_tx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = slim_get_port_idx(kcontrol); + + if (ch_num < 0) + return ch_num; + + ucontrol->value.enumerated.item[0] = + slim_get_sample_rate_val(slim_tx_cfg[ch_num].sample_rate); + + pr_debug("%s: slim[%d]_tx_sample_rate = %d, item = %d\n", __func__, + ch_num, slim_tx_cfg[ch_num].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int slim_tx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int sample_rate = 0; + int ch_num = slim_get_port_idx(kcontrol); + + if (ch_num < 0) + return ch_num; + + sample_rate = slim_get_sample_rate(ucontrol->value.enumerated.item[0]); + if (sample_rate == SAMPLING_RATE_44P1KHZ) { + pr_err("%s: Unsupported sample rate %d: for Tx path\n", + __func__, sample_rate); + return -EINVAL; + } + slim_tx_cfg[ch_num].sample_rate = sample_rate; + + pr_debug("%s: slim[%d]_tx_sample_rate = %d, value = %d\n", __func__, + ch_num, slim_tx_cfg[ch_num].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int slim_rx_bit_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = slim_get_port_idx(kcontrol); + + if (ch_num < 0) + return ch_num; + + ucontrol->value.enumerated.item[0] = + slim_get_bit_format_val(slim_rx_cfg[ch_num].bit_format); + + pr_debug("%s: slim[%d]_rx_bit_format = %d, ucontrol value = %d\n", + __func__, ch_num, slim_rx_cfg[ch_num].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int slim_rx_bit_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = slim_get_port_idx(kcontrol); + + if (ch_num < 0) + return ch_num; + + slim_rx_cfg[ch_num].bit_format = + slim_get_bit_format(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: slim[%d]_rx_bit_format = %d, ucontrol value = %d\n", + __func__, ch_num, slim_rx_cfg[ch_num].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int slim_tx_bit_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = slim_get_port_idx(kcontrol); + + if (ch_num < 0) + return ch_num; + + ucontrol->value.enumerated.item[0] = + slim_get_bit_format_val(slim_tx_cfg[ch_num].bit_format); + + pr_debug("%s: slim[%d]_tx_bit_format = %d, ucontrol value = %d\n", + __func__, ch_num, slim_tx_cfg[ch_num].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int slim_tx_bit_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = slim_get_port_idx(kcontrol); + + if (ch_num < 0) + return ch_num; + + slim_tx_cfg[ch_num].bit_format = + slim_get_bit_format(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: slim[%d]_tx_bit_format = %d, ucontrol value = %d\n", + __func__, ch_num, slim_tx_cfg[ch_num].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int slim_rx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = slim_get_port_idx(kcontrol); + + if (ch_num < 0) + return ch_num; + + pr_debug("%s: msm_slim_[%d]_rx_ch = %d\n", __func__, + ch_num, slim_rx_cfg[ch_num].channels); + ucontrol->value.enumerated.item[0] = slim_rx_cfg[ch_num].channels - 1; + + return 0; +} + +static int slim_rx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = slim_get_port_idx(kcontrol); + + if (ch_num < 0) + return ch_num; + + slim_rx_cfg[ch_num].channels = ucontrol->value.enumerated.item[0] + 1; + pr_debug("%s: msm_slim_[%d]_rx_ch = %d\n", __func__, + ch_num, slim_rx_cfg[ch_num].channels); + + return 1; +} + +static int slim_tx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = slim_get_port_idx(kcontrol); + + if (ch_num < 0) + return ch_num; + + pr_debug("%s: msm_slim_[%d]_tx_ch = %d\n", __func__, + ch_num, slim_tx_cfg[ch_num].channels); + ucontrol->value.enumerated.item[0] = slim_tx_cfg[ch_num].channels - 1; + + return 0; +} + +static int slim_tx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = slim_get_port_idx(kcontrol); + + if (ch_num < 0) + return ch_num; + + slim_tx_cfg[ch_num].channels = ucontrol->value.enumerated.item[0] + 1; + pr_debug("%s: msm_slim_[%d]_tx_ch = %d\n", __func__, + ch_num, slim_tx_cfg[ch_num].channels); + + return 1; +} + +static int msm_vi_feed_tx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = msm_vi_feed_tx_ch - 1; + pr_debug("%s: msm_vi_feed_tx_ch = %ld\n", __func__, + ucontrol->value.integer.value[0]); + return 0; +} + +static int msm_vi_feed_tx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + msm_vi_feed_tx_ch = ucontrol->value.integer.value[0] + 1; + + pr_debug("%s: msm_vi_feed_tx_ch = %d\n", __func__, msm_vi_feed_tx_ch); + return 1; +} + +static int msm_bt_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + /* + * Slimbus_7_Rx/Tx sample rate values should always be in sync (same) + * when used for BT_SCO use case. Return either Rx or Tx sample rate + * value. + */ + switch (slim_rx_cfg[SLIM_RX_7].sample_rate) { + case SAMPLING_RATE_96KHZ: + ucontrol->value.integer.value[0] = 5; + break; + case SAMPLING_RATE_88P2KHZ: + ucontrol->value.integer.value[0] = 4; + break; + case SAMPLING_RATE_48KHZ: + ucontrol->value.integer.value[0] = 3; + break; + case SAMPLING_RATE_44P1KHZ: + ucontrol->value.integer.value[0] = 2; + break; + case SAMPLING_RATE_16KHZ: + ucontrol->value.integer.value[0] = 1; + break; + case SAMPLING_RATE_8KHZ: + default: + ucontrol->value.integer.value[0] = 0; + break; + } + pr_debug("%s: sample rate = %d", __func__, + slim_rx_cfg[SLIM_RX_7].sample_rate); + + return 0; +} + +static int msm_bt_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (ucontrol->value.integer.value[0]) { + case 1: + slim_rx_cfg[SLIM_RX_7].sample_rate = SAMPLING_RATE_16KHZ; + slim_tx_cfg[SLIM_TX_7].sample_rate = SAMPLING_RATE_16KHZ; + break; + case 2: + slim_rx_cfg[SLIM_RX_7].sample_rate = SAMPLING_RATE_44P1KHZ; + slim_tx_cfg[SLIM_TX_7].sample_rate = SAMPLING_RATE_44P1KHZ; + break; + case 3: + slim_rx_cfg[SLIM_RX_7].sample_rate = SAMPLING_RATE_48KHZ; + slim_tx_cfg[SLIM_TX_7].sample_rate = SAMPLING_RATE_48KHZ; + break; + case 4: + slim_rx_cfg[SLIM_RX_7].sample_rate = SAMPLING_RATE_88P2KHZ; + slim_tx_cfg[SLIM_TX_7].sample_rate = SAMPLING_RATE_88P2KHZ; + break; + case 5: + slim_rx_cfg[SLIM_RX_7].sample_rate = SAMPLING_RATE_96KHZ; + slim_tx_cfg[SLIM_TX_7].sample_rate = SAMPLING_RATE_96KHZ; + break; + case 0: + default: + slim_rx_cfg[SLIM_RX_7].sample_rate = SAMPLING_RATE_8KHZ; + slim_tx_cfg[SLIM_TX_7].sample_rate = SAMPLING_RATE_8KHZ; + break; + } + pr_debug("%s: sample rates: slim7_rx = %d, slim7_tx = %d, value = %d\n", + __func__, + slim_rx_cfg[SLIM_RX_7].sample_rate, + slim_tx_cfg[SLIM_TX_7].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int msm_bt_sample_rate_sink_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (slim_tx_cfg[BT_SLIM_TX].sample_rate) { + case SAMPLING_RATE_96KHZ: + ucontrol->value.integer.value[0] = 5; + break; + case SAMPLING_RATE_88P2KHZ: + ucontrol->value.integer.value[0] = 4; + break; + case SAMPLING_RATE_48KHZ: + ucontrol->value.integer.value[0] = 3; + break; + case SAMPLING_RATE_44P1KHZ: + ucontrol->value.integer.value[0] = 2; + break; + case SAMPLING_RATE_16KHZ: + ucontrol->value.integer.value[0] = 1; + break; + case SAMPLING_RATE_8KHZ: + default: + ucontrol->value.integer.value[0] = 0; + break; + } + pr_debug("%s: sample rate = %d", __func__, + slim_tx_cfg[BT_SLIM_TX].sample_rate); + + return 0; +} + +static int msm_bt_sample_rate_sink_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (ucontrol->value.integer.value[0]) { + case 1: + slim_tx_cfg[BT_SLIM_TX].sample_rate = SAMPLING_RATE_16KHZ; + break; + case 2: + slim_tx_cfg[BT_SLIM_TX].sample_rate = SAMPLING_RATE_44P1KHZ; + break; + case 3: + slim_tx_cfg[BT_SLIM_TX].sample_rate = SAMPLING_RATE_48KHZ; + break; + case 4: + slim_tx_cfg[BT_SLIM_TX].sample_rate = SAMPLING_RATE_88P2KHZ; + break; + case 5: + slim_tx_cfg[BT_SLIM_TX].sample_rate = SAMPLING_RATE_96KHZ; + break; + case 0: + default: + slim_tx_cfg[BT_SLIM_TX].sample_rate = SAMPLING_RATE_8KHZ; + break; + } + pr_debug("%s: sample rate = %d, value = %d\n", + __func__, + slim_tx_cfg[BT_SLIM_TX].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int cdc_dma_get_port_idx(struct snd_kcontrol *kcontrol) +{ + int idx = 0; + + if (strnstr(kcontrol->id.name, "WSA_CDC_DMA_RX_0", + sizeof("WSA_CDC_DMA_RX_0"))) + idx = WSA_CDC_DMA_RX_0; + else if (strnstr(kcontrol->id.name, "WSA_CDC_DMA_RX_1", + sizeof("WSA_CDC_DMA_RX_0"))) + idx = WSA_CDC_DMA_RX_1; + else if (strnstr(kcontrol->id.name, "WSA_CDC_DMA_TX_0", + sizeof("WSA_CDC_DMA_TX_0"))) + idx = WSA_CDC_DMA_TX_0; + else if (strnstr(kcontrol->id.name, "WSA_CDC_DMA_TX_1", + sizeof("WSA_CDC_DMA_TX_1"))) + idx = WSA_CDC_DMA_TX_1; + else if (strnstr(kcontrol->id.name, "WSA_CDC_DMA_TX_2", + sizeof("WSA_CDC_DMA_TX_2"))) + idx = WSA_CDC_DMA_TX_2; + else if (strnstr(kcontrol->id.name, "VA_CDC_DMA_TX_0", + sizeof("VA_CDC_DMA_TX_0"))) + idx = VA_CDC_DMA_TX_0; + else if (strnstr(kcontrol->id.name, "VA_CDC_DMA_TX_1", + sizeof("VA_CDC_DMA_TX_1"))) + idx = VA_CDC_DMA_TX_1; + else { + pr_err("%s: unsupported port: %s\n", + __func__, kcontrol->id.name); + return -EINVAL; + } + + return idx; +} + +static int cdc_dma_rx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = cdc_dma_get_port_idx(kcontrol); + + if (ch_num < 0) + return ch_num; + + pr_debug("%s: cdc_dma_rx_ch = %d\n", __func__, + cdc_dma_rx_cfg[ch_num].channels - 1); + ucontrol->value.integer.value[0] = cdc_dma_rx_cfg[ch_num].channels - 1; + return 0; +} + +static int cdc_dma_rx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = cdc_dma_get_port_idx(kcontrol); + + if (ch_num < 0) + return ch_num; + + cdc_dma_rx_cfg[ch_num].channels = ucontrol->value.integer.value[0] + 1; + + pr_debug("%s: cdc_dma_rx_ch = %d\n", __func__, + cdc_dma_rx_cfg[ch_num].channels); + return 1; +} + +static int cdc_dma_rx_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = cdc_dma_get_port_idx(kcontrol); + + switch (cdc_dma_rx_cfg[ch_num].bit_format) { + case SNDRV_PCM_FORMAT_S32_LE: + ucontrol->value.integer.value[0] = 3; + break; + case SNDRV_PCM_FORMAT_S24_3LE: + ucontrol->value.integer.value[0] = 2; + break; + case SNDRV_PCM_FORMAT_S24_LE: + ucontrol->value.integer.value[0] = 1; + break; + case SNDRV_PCM_FORMAT_S16_LE: + default: + ucontrol->value.integer.value[0] = 0; + break; + } + + pr_debug("%s: cdc_dma_rx_format = %d, ucontrol value = %ld\n", + __func__, cdc_dma_rx_cfg[ch_num].bit_format, + ucontrol->value.integer.value[0]); + return 0; +} + +static int cdc_dma_rx_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int rc = 0; + int ch_num = cdc_dma_get_port_idx(kcontrol); + + switch (ucontrol->value.integer.value[0]) { + case 3: + cdc_dma_rx_cfg[ch_num].bit_format = SNDRV_PCM_FORMAT_S32_LE; + break; + case 2: + cdc_dma_rx_cfg[ch_num].bit_format = SNDRV_PCM_FORMAT_S24_3LE; + break; + case 1: + cdc_dma_rx_cfg[ch_num].bit_format = SNDRV_PCM_FORMAT_S24_LE; + break; + case 0: + default: + cdc_dma_rx_cfg[ch_num].bit_format = SNDRV_PCM_FORMAT_S16_LE; + break; + } + pr_debug("%s: cdc_dma_rx_format = %d, ucontrol value = %ld\n", + __func__, cdc_dma_rx_cfg[ch_num].bit_format, + ucontrol->value.integer.value[0]); + + return rc; +} + +static int cdc_dma_get_sample_rate_val(int sample_rate) +{ + int sample_rate_val = 0; + + switch (sample_rate) { + case SAMPLING_RATE_8KHZ: + sample_rate_val = 0; + break; + case SAMPLING_RATE_11P025KHZ: + sample_rate_val = 1; + break; + case SAMPLING_RATE_16KHZ: + sample_rate_val = 2; + break; + case SAMPLING_RATE_22P05KHZ: + sample_rate_val = 3; + break; + case SAMPLING_RATE_32KHZ: + sample_rate_val = 4; + break; + case SAMPLING_RATE_44P1KHZ: + sample_rate_val = 5; + break; + case SAMPLING_RATE_48KHZ: + sample_rate_val = 6; + break; + case SAMPLING_RATE_88P2KHZ: + sample_rate_val = 7; + break; + case SAMPLING_RATE_96KHZ: + sample_rate_val = 8; + break; + case SAMPLING_RATE_176P4KHZ: + sample_rate_val = 9; + break; + case SAMPLING_RATE_192KHZ: + sample_rate_val = 10; + break; + case SAMPLING_RATE_352P8KHZ: + sample_rate_val = 11; + break; + case SAMPLING_RATE_384KHZ: + sample_rate_val = 12; + break; + default: + sample_rate_val = 6; + break; + } + return sample_rate_val; +} + +static int cdc_dma_get_sample_rate(int value) +{ + int sample_rate = 0; + + switch (value) { + case 0: + sample_rate = SAMPLING_RATE_8KHZ; + break; + case 1: + sample_rate = SAMPLING_RATE_11P025KHZ; + break; + case 2: + sample_rate = SAMPLING_RATE_16KHZ; + break; + case 3: + sample_rate = SAMPLING_RATE_22P05KHZ; + break; + case 4: + sample_rate = SAMPLING_RATE_32KHZ; + break; + case 5: + sample_rate = SAMPLING_RATE_44P1KHZ; + break; + case 6: + sample_rate = SAMPLING_RATE_48KHZ; + break; + case 7: + sample_rate = SAMPLING_RATE_88P2KHZ; + break; + case 8: + sample_rate = SAMPLING_RATE_96KHZ; + break; + case 9: + sample_rate = SAMPLING_RATE_176P4KHZ; + break; + case 10: + sample_rate = SAMPLING_RATE_192KHZ; + break; + case 11: + sample_rate = SAMPLING_RATE_352P8KHZ; + break; + case 12: + sample_rate = SAMPLING_RATE_384KHZ; + break; + default: + sample_rate = SAMPLING_RATE_48KHZ; + break; + } + return sample_rate; +} + + +static int cdc_dma_rx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = cdc_dma_get_port_idx(kcontrol); + + if (ch_num < 0) + return ch_num; + + ucontrol->value.enumerated.item[0] = + cdc_dma_get_sample_rate_val(cdc_dma_rx_cfg[ch_num].sample_rate); + + pr_debug("%s: cdc_dma_rx_sample_rate = %d\n", __func__, + cdc_dma_rx_cfg[ch_num].sample_rate); + return 0; +} + +static int cdc_dma_rx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = cdc_dma_get_port_idx(kcontrol); + + if (ch_num < 0) + return ch_num; + + cdc_dma_rx_cfg[ch_num].sample_rate = + cdc_dma_get_sample_rate(ucontrol->value.enumerated.item[0]); + + + pr_debug("%s: control value = %d, cdc_dma_rx_sample_rate = %d\n", + __func__, ucontrol->value.enumerated.item[0], + cdc_dma_rx_cfg[ch_num].sample_rate); + return 0; +} + +static int cdc_dma_tx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = cdc_dma_get_port_idx(kcontrol); + + pr_debug("%s: cdc_dma_tx_ch = %d\n", __func__, + cdc_dma_tx_cfg[ch_num].channels); + ucontrol->value.integer.value[0] = cdc_dma_tx_cfg[ch_num].channels - 1; + return 0; +} + +static int cdc_dma_tx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = cdc_dma_get_port_idx(kcontrol); + + cdc_dma_tx_cfg[ch_num].channels = ucontrol->value.integer.value[0] + 1; + + pr_debug("%s: cdc_dma_tx_ch = %d\n", __func__, + cdc_dma_tx_cfg[ch_num].channels); + return 1; +} + +static int cdc_dma_tx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int sample_rate_val; + int ch_num = cdc_dma_get_port_idx(kcontrol); + + switch (cdc_dma_tx_cfg[ch_num].sample_rate) { + case SAMPLING_RATE_384KHZ: + sample_rate_val = 12; + break; + case SAMPLING_RATE_352P8KHZ: + sample_rate_val = 11; + break; + case SAMPLING_RATE_192KHZ: + sample_rate_val = 10; + break; + case SAMPLING_RATE_176P4KHZ: + sample_rate_val = 9; + break; + case SAMPLING_RATE_96KHZ: + sample_rate_val = 8; + break; + case SAMPLING_RATE_88P2KHZ: + sample_rate_val = 7; + break; + case SAMPLING_RATE_48KHZ: + sample_rate_val = 6; + break; + case SAMPLING_RATE_44P1KHZ: + sample_rate_val = 5; + break; + case SAMPLING_RATE_32KHZ: + sample_rate_val = 4; + break; + case SAMPLING_RATE_22P05KHZ: + sample_rate_val = 3; + break; + case SAMPLING_RATE_16KHZ: + sample_rate_val = 2; + break; + case SAMPLING_RATE_11P025KHZ: + sample_rate_val = 1; + break; + case SAMPLING_RATE_8KHZ: + sample_rate_val = 0; + break; + default: + sample_rate_val = 6; + break; + } + + ucontrol->value.integer.value[0] = sample_rate_val; + pr_debug("%s: cdc_dma_tx_sample_rate = %d\n", __func__, + cdc_dma_tx_cfg[ch_num].sample_rate); + return 0; +} + +static int cdc_dma_tx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = cdc_dma_get_port_idx(kcontrol); + + switch (ucontrol->value.integer.value[0]) { + case 12: + cdc_dma_tx_cfg[ch_num].sample_rate = SAMPLING_RATE_384KHZ; + break; + case 11: + cdc_dma_tx_cfg[ch_num].sample_rate = SAMPLING_RATE_352P8KHZ; + break; + case 10: + cdc_dma_tx_cfg[ch_num].sample_rate = SAMPLING_RATE_192KHZ; + break; + case 9: + cdc_dma_tx_cfg[ch_num].sample_rate = SAMPLING_RATE_176P4KHZ; + break; + case 8: + cdc_dma_tx_cfg[ch_num].sample_rate = SAMPLING_RATE_96KHZ; + break; + case 7: + cdc_dma_tx_cfg[ch_num].sample_rate = SAMPLING_RATE_88P2KHZ; + break; + case 6: + cdc_dma_tx_cfg[ch_num].sample_rate = SAMPLING_RATE_48KHZ; + break; + case 5: + cdc_dma_tx_cfg[ch_num].sample_rate = SAMPLING_RATE_44P1KHZ; + break; + case 4: + cdc_dma_tx_cfg[ch_num].sample_rate = SAMPLING_RATE_32KHZ; + break; + case 3: + cdc_dma_tx_cfg[ch_num].sample_rate = SAMPLING_RATE_22P05KHZ; + break; + case 2: + cdc_dma_tx_cfg[ch_num].sample_rate = SAMPLING_RATE_16KHZ; + break; + case 1: + cdc_dma_tx_cfg[ch_num].sample_rate = SAMPLING_RATE_11P025KHZ; + break; + case 0: + cdc_dma_tx_cfg[ch_num].sample_rate = SAMPLING_RATE_8KHZ; + break; + default: + cdc_dma_tx_cfg[ch_num].sample_rate = SAMPLING_RATE_48KHZ; + break; + } + + pr_debug("%s: control value = %ld, cdc_dma_tx_sample_rate = %d\n", + __func__, ucontrol->value.integer.value[0], + cdc_dma_tx_cfg[ch_num].sample_rate); + return 0; +} + +static int cdc_dma_tx_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = cdc_dma_get_port_idx(kcontrol); + + switch (cdc_dma_tx_cfg[ch_num].bit_format) { + case SNDRV_PCM_FORMAT_S32_LE: + ucontrol->value.integer.value[0] = 3; + break; + case SNDRV_PCM_FORMAT_S24_3LE: + ucontrol->value.integer.value[0] = 2; + break; + case SNDRV_PCM_FORMAT_S24_LE: + ucontrol->value.integer.value[0] = 1; + break; + case SNDRV_PCM_FORMAT_S16_LE: + default: + ucontrol->value.integer.value[0] = 0; + break; + } + + pr_debug("%s: cdc_dma_tx_format = %d, ucontrol value = %ld\n", + __func__, cdc_dma_tx_cfg[ch_num].bit_format, + ucontrol->value.integer.value[0]); + return 0; +} + +static int cdc_dma_tx_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int rc = 0; + int ch_num = cdc_dma_get_port_idx(kcontrol); + + switch (ucontrol->value.integer.value[0]) { + case 3: + cdc_dma_tx_cfg[ch_num].bit_format = SNDRV_PCM_FORMAT_S32_LE; + break; + case 2: + cdc_dma_tx_cfg[ch_num].bit_format = SNDRV_PCM_FORMAT_S24_3LE; + break; + case 1: + cdc_dma_tx_cfg[ch_num].bit_format = SNDRV_PCM_FORMAT_S24_LE; + break; + case 0: + default: + cdc_dma_tx_cfg[ch_num].bit_format = SNDRV_PCM_FORMAT_S16_LE; + break; + } + pr_debug("%s: cdc_dma_tx_format = %d, ucontrol value = %ld\n", + __func__, cdc_dma_tx_cfg[ch_num].bit_format, + ucontrol->value.integer.value[0]); + + return rc; +} + +static int usb_audio_rx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s: usb_audio_rx_ch = %d\n", __func__, + usb_rx_cfg.channels); + ucontrol->value.integer.value[0] = usb_rx_cfg.channels - 1; + return 0; +} + +static int usb_audio_rx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + usb_rx_cfg.channels = ucontrol->value.integer.value[0] + 1; + + pr_debug("%s: usb_audio_rx_ch = %d\n", __func__, usb_rx_cfg.channels); + return 1; +} + +static int usb_audio_rx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int sample_rate_val; + + switch (usb_rx_cfg.sample_rate) { + case SAMPLING_RATE_384KHZ: + sample_rate_val = 12; + break; + case SAMPLING_RATE_352P8KHZ: + sample_rate_val = 11; + break; + case SAMPLING_RATE_192KHZ: + sample_rate_val = 10; + break; + case SAMPLING_RATE_176P4KHZ: + sample_rate_val = 9; + break; + case SAMPLING_RATE_96KHZ: + sample_rate_val = 8; + break; + case SAMPLING_RATE_88P2KHZ: + sample_rate_val = 7; + break; + case SAMPLING_RATE_48KHZ: + sample_rate_val = 6; + break; + case SAMPLING_RATE_44P1KHZ: + sample_rate_val = 5; + break; + case SAMPLING_RATE_32KHZ: + sample_rate_val = 4; + break; + case SAMPLING_RATE_22P05KHZ: + sample_rate_val = 3; + break; + case SAMPLING_RATE_16KHZ: + sample_rate_val = 2; + break; + case SAMPLING_RATE_11P025KHZ: + sample_rate_val = 1; + break; + case SAMPLING_RATE_8KHZ: + default: + sample_rate_val = 0; + break; + } + + ucontrol->value.integer.value[0] = sample_rate_val; + pr_debug("%s: usb_audio_rx_sample_rate = %d\n", __func__, + usb_rx_cfg.sample_rate); + return 0; +} + +static int usb_audio_rx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (ucontrol->value.integer.value[0]) { + case 12: + usb_rx_cfg.sample_rate = SAMPLING_RATE_384KHZ; + break; + case 11: + usb_rx_cfg.sample_rate = SAMPLING_RATE_352P8KHZ; + break; + case 10: + usb_rx_cfg.sample_rate = SAMPLING_RATE_192KHZ; + break; + case 9: + usb_rx_cfg.sample_rate = SAMPLING_RATE_176P4KHZ; + break; + case 8: + usb_rx_cfg.sample_rate = SAMPLING_RATE_96KHZ; + break; + case 7: + usb_rx_cfg.sample_rate = SAMPLING_RATE_88P2KHZ; + break; + case 6: + usb_rx_cfg.sample_rate = SAMPLING_RATE_48KHZ; + break; + case 5: + usb_rx_cfg.sample_rate = SAMPLING_RATE_44P1KHZ; + break; + case 4: + usb_rx_cfg.sample_rate = SAMPLING_RATE_32KHZ; + break; + case 3: + usb_rx_cfg.sample_rate = SAMPLING_RATE_22P05KHZ; + break; + case 2: + usb_rx_cfg.sample_rate = SAMPLING_RATE_16KHZ; + break; + case 1: + usb_rx_cfg.sample_rate = SAMPLING_RATE_11P025KHZ; + break; + case 0: + usb_rx_cfg.sample_rate = SAMPLING_RATE_8KHZ; + break; + default: + usb_rx_cfg.sample_rate = SAMPLING_RATE_48KHZ; + break; + } + + pr_debug("%s: control value = %ld, usb_audio_rx_sample_rate = %d\n", + __func__, ucontrol->value.integer.value[0], + usb_rx_cfg.sample_rate); + return 0; +} + +static int usb_audio_rx_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (usb_rx_cfg.bit_format) { + case SNDRV_PCM_FORMAT_S32_LE: + ucontrol->value.integer.value[0] = 3; + break; + case SNDRV_PCM_FORMAT_S24_3LE: + ucontrol->value.integer.value[0] = 2; + break; + case SNDRV_PCM_FORMAT_S24_LE: + ucontrol->value.integer.value[0] = 1; + break; + case SNDRV_PCM_FORMAT_S16_LE: + default: + ucontrol->value.integer.value[0] = 0; + break; + } + + pr_debug("%s: usb_audio_rx_format = %d, ucontrol value = %ld\n", + __func__, usb_rx_cfg.bit_format, + ucontrol->value.integer.value[0]); + return 0; +} + +static int usb_audio_rx_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int rc = 0; + + switch (ucontrol->value.integer.value[0]) { + case 3: + usb_rx_cfg.bit_format = SNDRV_PCM_FORMAT_S32_LE; + break; + case 2: + usb_rx_cfg.bit_format = SNDRV_PCM_FORMAT_S24_3LE; + break; + case 1: + usb_rx_cfg.bit_format = SNDRV_PCM_FORMAT_S24_LE; + break; + case 0: + default: + usb_rx_cfg.bit_format = SNDRV_PCM_FORMAT_S16_LE; + break; + } + pr_debug("%s: usb_audio_rx_format = %d, ucontrol value = %ld\n", + __func__, usb_rx_cfg.bit_format, + ucontrol->value.integer.value[0]); + + return rc; +} + +static int usb_audio_tx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s: usb_audio_tx_ch = %d\n", __func__, + usb_tx_cfg.channels); + ucontrol->value.integer.value[0] = usb_tx_cfg.channels - 1; + return 0; +} + +static int usb_audio_tx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + usb_tx_cfg.channels = ucontrol->value.integer.value[0] + 1; + + pr_debug("%s: usb_audio_tx_ch = %d\n", __func__, usb_tx_cfg.channels); + return 1; +} + +static int usb_audio_tx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int sample_rate_val; + + switch (usb_tx_cfg.sample_rate) { + case SAMPLING_RATE_384KHZ: + sample_rate_val = 12; + break; + case SAMPLING_RATE_352P8KHZ: + sample_rate_val = 11; + break; + case SAMPLING_RATE_192KHZ: + sample_rate_val = 10; + break; + case SAMPLING_RATE_176P4KHZ: + sample_rate_val = 9; + break; + case SAMPLING_RATE_96KHZ: + sample_rate_val = 8; + break; + case SAMPLING_RATE_88P2KHZ: + sample_rate_val = 7; + break; + case SAMPLING_RATE_48KHZ: + sample_rate_val = 6; + break; + case SAMPLING_RATE_44P1KHZ: + sample_rate_val = 5; + break; + case SAMPLING_RATE_32KHZ: + sample_rate_val = 4; + break; + case SAMPLING_RATE_22P05KHZ: + sample_rate_val = 3; + break; + case SAMPLING_RATE_16KHZ: + sample_rate_val = 2; + break; + case SAMPLING_RATE_11P025KHZ: + sample_rate_val = 1; + break; + case SAMPLING_RATE_8KHZ: + sample_rate_val = 0; + break; + default: + sample_rate_val = 6; + break; + } + + ucontrol->value.integer.value[0] = sample_rate_val; + pr_debug("%s: usb_audio_tx_sample_rate = %d\n", __func__, + usb_tx_cfg.sample_rate); + return 0; +} + +static int usb_audio_tx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (ucontrol->value.integer.value[0]) { + case 12: + usb_tx_cfg.sample_rate = SAMPLING_RATE_384KHZ; + break; + case 11: + usb_tx_cfg.sample_rate = SAMPLING_RATE_352P8KHZ; + break; + case 10: + usb_tx_cfg.sample_rate = SAMPLING_RATE_192KHZ; + break; + case 9: + usb_tx_cfg.sample_rate = SAMPLING_RATE_176P4KHZ; + break; + case 8: + usb_tx_cfg.sample_rate = SAMPLING_RATE_96KHZ; + break; + case 7: + usb_tx_cfg.sample_rate = SAMPLING_RATE_88P2KHZ; + break; + case 6: + usb_tx_cfg.sample_rate = SAMPLING_RATE_48KHZ; + break; + case 5: + usb_tx_cfg.sample_rate = SAMPLING_RATE_44P1KHZ; + break; + case 4: + usb_tx_cfg.sample_rate = SAMPLING_RATE_32KHZ; + break; + case 3: + usb_tx_cfg.sample_rate = SAMPLING_RATE_22P05KHZ; + break; + case 2: + usb_tx_cfg.sample_rate = SAMPLING_RATE_16KHZ; + break; + case 1: + usb_tx_cfg.sample_rate = SAMPLING_RATE_11P025KHZ; + break; + case 0: + usb_tx_cfg.sample_rate = SAMPLING_RATE_8KHZ; + break; + default: + usb_tx_cfg.sample_rate = SAMPLING_RATE_48KHZ; + break; + } + + pr_debug("%s: control value = %ld, usb_audio_tx_sample_rate = %d\n", + __func__, ucontrol->value.integer.value[0], + usb_tx_cfg.sample_rate); + return 0; +} + +static int usb_audio_tx_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (usb_tx_cfg.bit_format) { + case SNDRV_PCM_FORMAT_S32_LE: + ucontrol->value.integer.value[0] = 3; + break; + case SNDRV_PCM_FORMAT_S24_3LE: + ucontrol->value.integer.value[0] = 2; + break; + case SNDRV_PCM_FORMAT_S24_LE: + ucontrol->value.integer.value[0] = 1; + break; + case SNDRV_PCM_FORMAT_S16_LE: + default: + ucontrol->value.integer.value[0] = 0; + break; + } + + pr_debug("%s: usb_audio_tx_format = %d, ucontrol value = %ld\n", + __func__, usb_tx_cfg.bit_format, + ucontrol->value.integer.value[0]); + return 0; +} + +static int usb_audio_tx_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int rc = 0; + + switch (ucontrol->value.integer.value[0]) { + case 3: + usb_tx_cfg.bit_format = SNDRV_PCM_FORMAT_S32_LE; + break; + case 2: + usb_tx_cfg.bit_format = SNDRV_PCM_FORMAT_S24_3LE; + break; + case 1: + usb_tx_cfg.bit_format = SNDRV_PCM_FORMAT_S24_LE; + break; + case 0: + default: + usb_tx_cfg.bit_format = SNDRV_PCM_FORMAT_S16_LE; + break; + } + pr_debug("%s: usb_audio_tx_format = %d, ucontrol value = %ld\n", + __func__, usb_tx_cfg.bit_format, + ucontrol->value.integer.value[0]); + + return rc; +} + +static int proxy_rx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s: proxy_rx channels = %d\n", + __func__, proxy_rx_cfg.channels); + ucontrol->value.integer.value[0] = proxy_rx_cfg.channels - 2; + + return 0; +} + +static int proxy_rx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + proxy_rx_cfg.channels = ucontrol->value.integer.value[0] + 2; + pr_debug("%s: proxy_rx channels = %d\n", + __func__, proxy_rx_cfg.channels); + + return 1; +} + +static int tdm_get_sample_rate(int value) +{ + int sample_rate = 0; + + switch (value) { + case 0: + sample_rate = SAMPLING_RATE_8KHZ; + break; + case 1: + sample_rate = SAMPLING_RATE_16KHZ; + break; + case 2: + sample_rate = SAMPLING_RATE_32KHZ; + break; + case 3: + sample_rate = SAMPLING_RATE_48KHZ; + break; + case 4: + sample_rate = SAMPLING_RATE_176P4KHZ; + break; + case 5: + sample_rate = SAMPLING_RATE_352P8KHZ; + break; + default: + sample_rate = SAMPLING_RATE_48KHZ; + break; + } + return sample_rate; +} + +static int aux_pcm_get_sample_rate(int value) +{ + int sample_rate; + + switch (value) { + case 1: + sample_rate = SAMPLING_RATE_16KHZ; + break; + case 0: + default: + sample_rate = SAMPLING_RATE_8KHZ; + break; + } + return sample_rate; +} + +static int tdm_get_sample_rate_val(int sample_rate) +{ + int sample_rate_val = 0; + + switch (sample_rate) { + case SAMPLING_RATE_8KHZ: + sample_rate_val = 0; + break; + case SAMPLING_RATE_16KHZ: + sample_rate_val = 1; + break; + case SAMPLING_RATE_32KHZ: + sample_rate_val = 2; + break; + case SAMPLING_RATE_48KHZ: + sample_rate_val = 3; + break; + case SAMPLING_RATE_176P4KHZ: + sample_rate_val = 4; + break; + case SAMPLING_RATE_352P8KHZ: + sample_rate_val = 5; + break; + default: + sample_rate_val = 3; + break; + } + return sample_rate_val; +} + +static int aux_pcm_get_sample_rate_val(int sample_rate) +{ + int sample_rate_val; + + switch (sample_rate) { + case SAMPLING_RATE_16KHZ: + sample_rate_val = 1; + break; + case SAMPLING_RATE_8KHZ: + default: + sample_rate_val = 0; + break; + } + return sample_rate_val; +} + +static int tdm_get_port_idx(struct snd_kcontrol *kcontrol, + struct tdm_port *port) +{ + if (port) { + if (strnstr(kcontrol->id.name, "PRI", + sizeof(kcontrol->id.name))) { + port->mode = TDM_PRI; + } else if (strnstr(kcontrol->id.name, "SEC", + sizeof(kcontrol->id.name))) { + port->mode = TDM_SEC; + } else if (strnstr(kcontrol->id.name, "TERT", + sizeof(kcontrol->id.name))) { + port->mode = TDM_TERT; + } else if (strnstr(kcontrol->id.name, "QUAT", + sizeof(kcontrol->id.name))) { + port->mode = TDM_QUAT; + } else if (strnstr(kcontrol->id.name, "QUIN", + sizeof(kcontrol->id.name))) { + port->mode = TDM_QUIN; + } else { + pr_err("%s: unsupported mode in: %s", + __func__, kcontrol->id.name); + return -EINVAL; + } + + if (strnstr(kcontrol->id.name, "RX_0", + sizeof(kcontrol->id.name)) || + strnstr(kcontrol->id.name, "TX_0", + sizeof(kcontrol->id.name))) { + port->channel = TDM_0; + } else if (strnstr(kcontrol->id.name, "RX_1", + sizeof(kcontrol->id.name)) || + strnstr(kcontrol->id.name, "TX_1", + sizeof(kcontrol->id.name))) { + port->channel = TDM_1; + } else if (strnstr(kcontrol->id.name, "RX_2", + sizeof(kcontrol->id.name)) || + strnstr(kcontrol->id.name, "TX_2", + sizeof(kcontrol->id.name))) { + port->channel = TDM_2; + } else if (strnstr(kcontrol->id.name, "RX_3", + sizeof(kcontrol->id.name)) || + strnstr(kcontrol->id.name, "TX_3", + sizeof(kcontrol->id.name))) { + port->channel = TDM_3; + } else if (strnstr(kcontrol->id.name, "RX_4", + sizeof(kcontrol->id.name)) || + strnstr(kcontrol->id.name, "TX_4", + sizeof(kcontrol->id.name))) { + port->channel = TDM_4; + } else if (strnstr(kcontrol->id.name, "RX_5", + sizeof(kcontrol->id.name)) || + strnstr(kcontrol->id.name, "TX_5", + sizeof(kcontrol->id.name))) { + port->channel = TDM_5; + } else if (strnstr(kcontrol->id.name, "RX_6", + sizeof(kcontrol->id.name)) || + strnstr(kcontrol->id.name, "TX_6", + sizeof(kcontrol->id.name))) { + port->channel = TDM_6; + } else if (strnstr(kcontrol->id.name, "RX_7", + sizeof(kcontrol->id.name)) || + strnstr(kcontrol->id.name, "TX_7", + sizeof(kcontrol->id.name))) { + port->channel = TDM_7; + } else { + pr_err("%s: unsupported channel in: %s", + __func__, kcontrol->id.name); + return -EINVAL; + } + } else + return -EINVAL; + return 0; +} + +static int tdm_rx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + ucontrol->value.enumerated.item[0] = tdm_get_sample_rate_val( + tdm_rx_cfg[port.mode][port.channel].sample_rate); + + pr_debug("%s: tdm_rx_sample_rate = %d, item = %d\n", __func__, + tdm_rx_cfg[port.mode][port.channel].sample_rate, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_rx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + tdm_rx_cfg[port.mode][port.channel].sample_rate = + tdm_get_sample_rate(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: tdm_rx_sample_rate = %d, item = %d\n", __func__, + tdm_rx_cfg[port.mode][port.channel].sample_rate, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_tx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + ucontrol->value.enumerated.item[0] = tdm_get_sample_rate_val( + tdm_tx_cfg[port.mode][port.channel].sample_rate); + + pr_debug("%s: tdm_tx_sample_rate = %d, item = %d\n", __func__, + tdm_tx_cfg[port.mode][port.channel].sample_rate, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_tx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + tdm_tx_cfg[port.mode][port.channel].sample_rate = + tdm_get_sample_rate(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: tdm_tx_sample_rate = %d, item = %d\n", __func__, + tdm_tx_cfg[port.mode][port.channel].sample_rate, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_get_format(int value) +{ + int format = 0; + + switch (value) { + case 0: + format = SNDRV_PCM_FORMAT_S16_LE; + break; + case 1: + format = SNDRV_PCM_FORMAT_S24_LE; + break; + case 2: + format = SNDRV_PCM_FORMAT_S32_LE; + break; + default: + format = SNDRV_PCM_FORMAT_S16_LE; + break; + } + return format; +} + +static int tdm_get_format_val(int format) +{ + int value = 0; + + switch (format) { + case SNDRV_PCM_FORMAT_S16_LE: + value = 0; + break; + case SNDRV_PCM_FORMAT_S24_LE: + value = 1; + break; + case SNDRV_PCM_FORMAT_S32_LE: + value = 2; + break; + default: + value = 0; + break; + } + return value; +} + +static int tdm_rx_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + ucontrol->value.enumerated.item[0] = tdm_get_format_val( + tdm_rx_cfg[port.mode][port.channel].bit_format); + + pr_debug("%s: tdm_rx_bit_format = %d, item = %d\n", __func__, + tdm_rx_cfg[port.mode][port.channel].bit_format, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_rx_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + tdm_rx_cfg[port.mode][port.channel].bit_format = + tdm_get_format(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: tdm_rx_bit_format = %d, item = %d\n", __func__, + tdm_rx_cfg[port.mode][port.channel].bit_format, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_tx_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + ucontrol->value.enumerated.item[0] = tdm_get_format_val( + tdm_tx_cfg[port.mode][port.channel].bit_format); + + pr_debug("%s: tdm_tx_bit_format = %d, item = %d\n", __func__, + tdm_tx_cfg[port.mode][port.channel].bit_format, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_tx_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + tdm_tx_cfg[port.mode][port.channel].bit_format = + tdm_get_format(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: tdm_tx_bit_format = %d, item = %d\n", __func__, + tdm_tx_cfg[port.mode][port.channel].bit_format, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_rx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + + ucontrol->value.enumerated.item[0] = + tdm_rx_cfg[port.mode][port.channel].channels - 1; + + pr_debug("%s: tdm_rx_ch = %d, item = %d\n", __func__, + tdm_rx_cfg[port.mode][port.channel].channels - 1, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_rx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + tdm_rx_cfg[port.mode][port.channel].channels = + ucontrol->value.enumerated.item[0] + 1; + + pr_debug("%s: tdm_rx_ch = %d, item = %d\n", __func__, + tdm_rx_cfg[port.mode][port.channel].channels, + ucontrol->value.enumerated.item[0] + 1); + } + return ret; +} + +static int tdm_tx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + ucontrol->value.enumerated.item[0] = + tdm_tx_cfg[port.mode][port.channel].channels - 1; + + pr_debug("%s: tdm_tx_ch = %d, item = %d\n", __func__, + tdm_tx_cfg[port.mode][port.channel].channels - 1, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_tx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + tdm_tx_cfg[port.mode][port.channel].channels = + ucontrol->value.enumerated.item[0] + 1; + + pr_debug("%s: tdm_tx_ch = %d, item = %d\n", __func__, + tdm_tx_cfg[port.mode][port.channel].channels, + ucontrol->value.enumerated.item[0] + 1); + } + return ret; +} + +static int aux_pcm_get_port_idx(struct snd_kcontrol *kcontrol) +{ + int idx; + + if (strnstr(kcontrol->id.name, "PRIM_AUX_PCM", + sizeof("PRIM_AUX_PCM"))) + idx = PRIM_AUX_PCM; + else if (strnstr(kcontrol->id.name, "SEC_AUX_PCM", + sizeof("SEC_AUX_PCM"))) + idx = SEC_AUX_PCM; + else if (strnstr(kcontrol->id.name, "TERT_AUX_PCM", + sizeof("TERT_AUX_PCM"))) + idx = TERT_AUX_PCM; + else if (strnstr(kcontrol->id.name, "QUAT_AUX_PCM", + sizeof("QUAT_AUX_PCM"))) + idx = QUAT_AUX_PCM; + else if (strnstr(kcontrol->id.name, "QUIN_AUX_PCM", + sizeof("QUIN_AUX_PCM"))) + idx = QUIN_AUX_PCM; + else if (strnstr(kcontrol->id.name, "SEN_AUX_PCM", + sizeof("SENN_AUX_PCM"))) + idx = SEN_AUX_PCM; + else { + pr_err("%s: unsupported port: %s", + __func__, kcontrol->id.name); + idx = -EINVAL; + } + + return idx; +} + +static int aux_pcm_rx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = aux_pcm_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + aux_pcm_rx_cfg[idx].sample_rate = + aux_pcm_get_sample_rate(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: idx[%d]_rx_sample_rate = %d, item = %d\n", __func__, + idx, aux_pcm_rx_cfg[idx].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int aux_pcm_rx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = aux_pcm_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + ucontrol->value.enumerated.item[0] = + aux_pcm_get_sample_rate_val(aux_pcm_rx_cfg[idx].sample_rate); + + pr_debug("%s: idx[%d]_rx_sample_rate = %d, item = %d\n", __func__, + idx, aux_pcm_rx_cfg[idx].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int aux_pcm_tx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = aux_pcm_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + aux_pcm_tx_cfg[idx].sample_rate = + aux_pcm_get_sample_rate(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: idx[%d]_tx_sample_rate = %d, item = %d\n", __func__, + idx, aux_pcm_tx_cfg[idx].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int aux_pcm_tx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = aux_pcm_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + ucontrol->value.enumerated.item[0] = + aux_pcm_get_sample_rate_val(aux_pcm_tx_cfg[idx].sample_rate); + + pr_debug("%s: idx[%d]_tx_sample_rate = %d, item = %d\n", __func__, + idx, aux_pcm_tx_cfg[idx].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int mi2s_get_port_idx(struct snd_kcontrol *kcontrol) +{ + int idx; + + if (strnstr(kcontrol->id.name, "PRIM_MI2S_RX", + sizeof("PRIM_MI2S_RX"))) + idx = PRIM_MI2S; + else if (strnstr(kcontrol->id.name, "SEC_MI2S_RX", + sizeof("SEC_MI2S_RX"))) + idx = SEC_MI2S; + else if (strnstr(kcontrol->id.name, "TERT_MI2S_RX", + sizeof("TERT_MI2S_RX"))) + idx = TERT_MI2S; + else if (strnstr(kcontrol->id.name, "QUAT_MI2S_RX", + sizeof("QUAT_MI2S_RX"))) + idx = QUAT_MI2S; + else if (strnstr(kcontrol->id.name, "QUIN_MI2S_RX", + sizeof("QUIN_MI2S_RX"))) + idx = QUIN_MI2S; + else if (strnstr(kcontrol->id.name, "SEN_MI2S_RX", + sizeof("SEN_MI2S_RX"))) + idx = SEN_MI2S; + else if (strnstr(kcontrol->id.name, "PRIM_MI2S_TX", + sizeof("PRIM_MI2S_TX"))) + idx = PRIM_MI2S; + else if (strnstr(kcontrol->id.name, "SEC_MI2S_TX", + sizeof("SEC_MI2S_TX"))) + idx = SEC_MI2S; + else if (strnstr(kcontrol->id.name, "TERT_MI2S_TX", + sizeof("TERT_MI2S_TX"))) + idx = TERT_MI2S; + else if (strnstr(kcontrol->id.name, "QUAT_MI2S_TX", + sizeof("QUAT_MI2S_TX"))) + idx = QUAT_MI2S; + else if (strnstr(kcontrol->id.name, "QUIN_MI2S_TX", + sizeof("QUIN_MI2S_TX"))) + idx = QUIN_MI2S; + else if (strnstr(kcontrol->id.name, "SEN_MI2S_TX", + sizeof("SEN_MI2S_TX"))) + idx = SEN_MI2S; + else { + pr_err("%s: unsupported channel: %s", + __func__, kcontrol->id.name); + idx = -EINVAL; + } + + return idx; +} + +static int mi2s_get_sample_rate_val(int sample_rate) +{ + int sample_rate_val; + + switch (sample_rate) { + case SAMPLING_RATE_8KHZ: + sample_rate_val = 0; + break; + case SAMPLING_RATE_11P025KHZ: + sample_rate_val = 1; + break; + case SAMPLING_RATE_16KHZ: + sample_rate_val = 2; + break; + case SAMPLING_RATE_22P05KHZ: + sample_rate_val = 3; + break; + case SAMPLING_RATE_32KHZ: + sample_rate_val = 4; + break; + case SAMPLING_RATE_44P1KHZ: + sample_rate_val = 5; + break; + case SAMPLING_RATE_48KHZ: + sample_rate_val = 6; + break; + case SAMPLING_RATE_96KHZ: + sample_rate_val = 7; + break; + case SAMPLING_RATE_192KHZ: + sample_rate_val = 8; + break; + case SAMPLING_RATE_384KHZ: + sample_rate_val = 9; + break; + default: + sample_rate_val = 6; + break; + } + return sample_rate_val; +} + +static int mi2s_get_sample_rate(int value) +{ + int sample_rate; + + switch (value) { + case 0: + sample_rate = SAMPLING_RATE_8KHZ; + break; + case 1: + sample_rate = SAMPLING_RATE_11P025KHZ; + break; + case 2: + sample_rate = SAMPLING_RATE_16KHZ; + break; + case 3: + sample_rate = SAMPLING_RATE_22P05KHZ; + break; + case 4: + sample_rate = SAMPLING_RATE_32KHZ; + break; + case 5: + sample_rate = SAMPLING_RATE_44P1KHZ; + break; + case 6: + sample_rate = SAMPLING_RATE_48KHZ; + break; + case 7: + sample_rate = SAMPLING_RATE_96KHZ; + break; + case 8: + sample_rate = SAMPLING_RATE_192KHZ; + break; + case 9: + sample_rate = SAMPLING_RATE_384KHZ; + break; + default: + sample_rate = SAMPLING_RATE_48KHZ; + break; + } + return sample_rate; +} + +static int mi2s_auxpcm_get_format(int value) +{ + int format; + + switch (value) { + case 0: + format = SNDRV_PCM_FORMAT_S16_LE; + break; + case 1: + format = SNDRV_PCM_FORMAT_S24_LE; + break; + case 2: + format = SNDRV_PCM_FORMAT_S24_3LE; + break; + case 3: + format = SNDRV_PCM_FORMAT_S32_LE; + break; + default: + format = SNDRV_PCM_FORMAT_S16_LE; + break; + } + return format; +} + +static int mi2s_auxpcm_get_format_value(int format) +{ + int value; + + switch (format) { + case SNDRV_PCM_FORMAT_S16_LE: + value = 0; + break; + case SNDRV_PCM_FORMAT_S24_LE: + value = 1; + break; + case SNDRV_PCM_FORMAT_S24_3LE: + value = 2; + break; + case SNDRV_PCM_FORMAT_S32_LE: + value = 3; + break; + default: + value = 0; + break; + } + return value; +} + +static int mi2s_rx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + mi2s_rx_cfg[idx].sample_rate = + mi2s_get_sample_rate(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: idx[%d]_rx_sample_rate = %d, item = %d\n", __func__, + idx, mi2s_rx_cfg[idx].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int mi2s_rx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + ucontrol->value.enumerated.item[0] = + mi2s_get_sample_rate_val(mi2s_rx_cfg[idx].sample_rate); + + pr_debug("%s: idx[%d]_rx_sample_rate = %d, item = %d\n", __func__, + idx, mi2s_rx_cfg[idx].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int mi2s_tx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + mi2s_tx_cfg[idx].sample_rate = + mi2s_get_sample_rate(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: idx[%d]_tx_sample_rate = %d, item = %d\n", __func__, + idx, mi2s_tx_cfg[idx].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int mi2s_tx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + ucontrol->value.enumerated.item[0] = + mi2s_get_sample_rate_val(mi2s_tx_cfg[idx].sample_rate); + + pr_debug("%s: idx[%d]_tx_sample_rate = %d, item = %d\n", __func__, + idx, mi2s_tx_cfg[idx].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int msm_mi2s_rx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + pr_debug("%s: msm_mi2s_[%d]_rx_ch = %d\n", __func__, + idx, mi2s_rx_cfg[idx].channels); + ucontrol->value.enumerated.item[0] = mi2s_rx_cfg[idx].channels - 1; + + return 0; +} + +static int msm_mi2s_rx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + mi2s_rx_cfg[idx].channels = ucontrol->value.enumerated.item[0] + 1; + pr_debug("%s: msm_mi2s_[%d]_rx_ch = %d\n", __func__, + idx, mi2s_rx_cfg[idx].channels); + + return 1; +} + +static int msm_mi2s_tx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + pr_debug("%s: msm_mi2s_[%d]_tx_ch = %d\n", __func__, + idx, mi2s_tx_cfg[idx].channels); + ucontrol->value.enumerated.item[0] = mi2s_tx_cfg[idx].channels - 1; + + return 0; +} + +static int msm_mi2s_tx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + mi2s_tx_cfg[idx].channels = ucontrol->value.enumerated.item[0] + 1; + pr_debug("%s: msm_mi2s_[%d]_tx_ch = %d\n", __func__, + idx, mi2s_tx_cfg[idx].channels); + + return 1; +} + +static int msm_mi2s_rx_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + ucontrol->value.enumerated.item[0] = + mi2s_auxpcm_get_format_value(mi2s_rx_cfg[idx].bit_format); + + pr_debug("%s: idx[%d]_rx_format = %d, item = %d\n", __func__, + idx, mi2s_rx_cfg[idx].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int msm_mi2s_rx_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + + struct msm_asoc_mach_data *pdata = NULL; + struct snd_soc_component *component = NULL; + struct snd_soc_card *card = NULL; + int idx = mi2s_get_port_idx(kcontrol); + + component = snd_soc_kcontrol_component(kcontrol); + card = kcontrol->private_data; + pdata = snd_soc_card_get_drvdata(card); + + if (idx < 0) + return idx; + + /* check for PRIM_MI2S and CSRAx config to allow 24bit BE config only */ + if ((PRIM_MI2S == idx) && (true==pdata->codec_is_csra)) + { + mi2s_rx_cfg[idx].bit_format = SNDRV_PCM_FORMAT_S24_LE; + pr_debug("%s: Keeping default format idx[%d]_rx_format = %d, item = %d\n", + __func__, idx, mi2s_rx_cfg[idx].bit_format, + ucontrol->value.enumerated.item[0]); + } else { + mi2s_rx_cfg[idx].bit_format = + mi2s_auxpcm_get_format(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: idx[%d]_rx_format = %d, item = %d\n", __func__, + idx, mi2s_rx_cfg[idx].bit_format, + ucontrol->value.enumerated.item[0]); + } + + return 0; +} + +static int msm_mi2s_tx_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + ucontrol->value.enumerated.item[0] = + mi2s_auxpcm_get_format_value(mi2s_tx_cfg[idx].bit_format); + + pr_debug("%s: idx[%d]_tx_format = %d, item = %d\n", __func__, + idx, mi2s_tx_cfg[idx].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int msm_mi2s_tx_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + mi2s_tx_cfg[idx].bit_format = + mi2s_auxpcm_get_format(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: idx[%d]_tx_format = %d, item = %d\n", __func__, + idx, mi2s_tx_cfg[idx].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int msm_aux_pcm_rx_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = aux_pcm_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + ucontrol->value.enumerated.item[0] = + mi2s_auxpcm_get_format_value(aux_pcm_rx_cfg[idx].bit_format); + + pr_debug("%s: idx[%d]_rx_format = %d, item = %d\n", __func__, + idx, aux_pcm_rx_cfg[idx].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int msm_aux_pcm_rx_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = aux_pcm_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + aux_pcm_rx_cfg[idx].bit_format = + mi2s_auxpcm_get_format(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: idx[%d]_rx_format = %d, item = %d\n", __func__, + idx, aux_pcm_rx_cfg[idx].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int msm_aux_pcm_tx_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = aux_pcm_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + ucontrol->value.enumerated.item[0] = + mi2s_auxpcm_get_format_value(aux_pcm_tx_cfg[idx].bit_format); + + pr_debug("%s: idx[%d]_tx_format = %d, item = %d\n", __func__, + idx, aux_pcm_tx_cfg[idx].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int msm_aux_pcm_tx_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = aux_pcm_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + aux_pcm_tx_cfg[idx].bit_format = + mi2s_auxpcm_get_format(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: idx[%d]_tx_format = %d, item = %d\n", __func__, + idx, aux_pcm_tx_cfg[idx].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int spdif_get_port_idx(struct snd_kcontrol *kcontrol) +{ + int idx; + + if (strnstr(kcontrol->id.name, "PRIM_SPDIF_RX", + sizeof("PRIM_SPDIF_RX"))) + idx = PRIM_SPDIF_RX; + else if (strnstr(kcontrol->id.name, "SEC_SPDIF_RX", + sizeof("SEC_SPDIF_RX"))) + idx = SEC_SPDIF_RX; + else if (strnstr(kcontrol->id.name, "PRIM_SPDIF_TX", + sizeof("PRIM_SPDIF_TX"))) + idx = PRIM_SPDIF_TX; + else if (strnstr(kcontrol->id.name, "SEC_SPDIF_TX", + sizeof("SEC_SPDIF_TX"))) + idx = SEC_SPDIF_TX; + else { + pr_err("%s: unsupported channel: %s", + __func__, kcontrol->id.name); + idx = -EINVAL; + } + + return idx; +} + +static int spdif_get_sample_rate_val(int sample_rate) +{ + int sample_rate_val; + + switch (sample_rate) { + case SAMPLING_RATE_32KHZ: + sample_rate_val = 0; + break; + case SAMPLING_RATE_44P1KHZ: + sample_rate_val = 1; + break; + case SAMPLING_RATE_48KHZ: + sample_rate_val = 2; + break; + case SAMPLING_RATE_88P2KHZ: + sample_rate_val = 3; + break; + case SAMPLING_RATE_96KHZ: + sample_rate_val = 4; + break; + case SAMPLING_RATE_176P4KHZ: + sample_rate_val = 5; + break; + case SAMPLING_RATE_192KHZ: + sample_rate_val = 6; + break; + default: + sample_rate_val = 2; + break; + } + return sample_rate_val; +} + +static int spdif_get_sample_rate(int value) +{ + int sample_rate; + + switch (value) { + case 0: + sample_rate = SAMPLING_RATE_32KHZ; + break; + case 1: + sample_rate = SAMPLING_RATE_44P1KHZ; + break; + case 2: + sample_rate = SAMPLING_RATE_48KHZ; + break; + case 3: + sample_rate = SAMPLING_RATE_88P2KHZ; + break; + case 4: + sample_rate = SAMPLING_RATE_96KHZ; + break; + case 5: + sample_rate = SAMPLING_RATE_176P4KHZ; + break; + case 6: + sample_rate = SAMPLING_RATE_192KHZ; + break; + default: + sample_rate = SAMPLING_RATE_48KHZ; + break; + } + return sample_rate; +} + +static int spdif_get_format(int value) +{ + int format; + + switch (value) { + case 0: + format = SNDRV_PCM_FORMAT_S16_LE; + break; + case 1: + format = SNDRV_PCM_FORMAT_S24_LE; + break; + default: + format = SNDRV_PCM_FORMAT_S16_LE; + break; + } + return format; +} + +static int spdif_get_format_value(int format) +{ + int value; + + switch (format) { + case SNDRV_PCM_FORMAT_S16_LE: + value = 0; + break; + case SNDRV_PCM_FORMAT_S24_LE: + value = 1; + break; + default: + value = 0; + break; + } + return value; +} + +static int msm_spdif_rx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = spdif_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + spdif_rx_cfg[idx].sample_rate = + spdif_get_sample_rate(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: idx[%d]_rx_sample_rate = %d, item = %d\n", __func__, + idx, spdif_rx_cfg[idx].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int msm_spdif_rx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = spdif_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + ucontrol->value.enumerated.item[0] = + spdif_get_sample_rate_val(spdif_rx_cfg[idx].sample_rate); + + pr_debug("%s: idx[%d]_rx_sample_rate = %d, item = %d\n", __func__, + idx, spdif_rx_cfg[idx].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int msm_spdif_tx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = spdif_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + spdif_tx_cfg[idx].sample_rate = + spdif_get_sample_rate(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: idx[%d]_tx_sample_rate = %d, item = %d\n", __func__, + idx, spdif_tx_cfg[idx].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int msm_spdif_tx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = spdif_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + ucontrol->value.enumerated.item[0] = + spdif_get_sample_rate_val(spdif_tx_cfg[idx].sample_rate); + + pr_debug("%s: idx[%d]_tx_sample_rate = %d, item = %d\n", __func__, + idx, spdif_tx_cfg[idx].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int msm_spdif_rx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = spdif_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + pr_debug("%s: msm_spdif_[%d]_rx_ch = %d\n", __func__, + idx, spdif_rx_cfg[idx].channels); + ucontrol->value.enumerated.item[0] = spdif_rx_cfg[idx].channels - 1; + + return 0; +} + +static int msm_spdif_rx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = spdif_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + spdif_rx_cfg[idx].channels = ucontrol->value.enumerated.item[0] + 1; + pr_debug("%s: msm_spdif_[%d]_rx_ch = %d\n", __func__, + idx, spdif_rx_cfg[idx].channels); + + return 1; +} + +static int msm_spdif_tx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = spdif_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + pr_debug("%s: msm_spdif_[%d]_tx_ch = %d\n", __func__, + idx, spdif_tx_cfg[idx].channels); + ucontrol->value.enumerated.item[0] = spdif_tx_cfg[idx].channels - 1; + + return 0; +} + +static int msm_spdif_tx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = spdif_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + spdif_tx_cfg[idx].channels = ucontrol->value.enumerated.item[0] + 1; + pr_debug("%s: msm_spdif_[%d]_tx_ch = %d\n", __func__, + idx, spdif_tx_cfg[idx].channels); + + return 1; +} + +static int msm_spdif_rx_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = spdif_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + ucontrol->value.enumerated.item[0] = + spdif_get_format_value(spdif_rx_cfg[idx].bit_format); + + pr_debug("%s: idx[%d]_rx_format = %d, item = %d\n", __func__, + idx, spdif_rx_cfg[idx].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int msm_spdif_rx_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = spdif_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + spdif_rx_cfg[idx].bit_format = + spdif_get_format(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: idx[%d]_rx_format = %d, item = %d\n", __func__, + idx, spdif_rx_cfg[idx].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int msm_spdif_tx_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = spdif_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + ucontrol->value.enumerated.item[0] = + spdif_get_format_value(spdif_tx_cfg[idx].bit_format); + + pr_debug("%s: idx[%d]_tx_format = %d, item = %d\n", __func__, + idx, spdif_tx_cfg[idx].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int msm_spdif_tx_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = spdif_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + spdif_tx_cfg[idx].bit_format = + spdif_get_format(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: idx[%d]_tx_format = %d, item = %d\n", __func__, + idx, spdif_tx_cfg[idx].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static const struct snd_kcontrol_new msm_snd_sb_controls[] = { + SOC_ENUM_EXT("SLIM_0_RX Channels", slim_0_rx_chs, + slim_rx_ch_get, slim_rx_ch_put), + SOC_ENUM_EXT("SLIM_2_RX Channels", slim_2_rx_chs, + slim_rx_ch_get, slim_rx_ch_put), + SOC_ENUM_EXT("SLIM_0_TX Channels", slim_0_tx_chs, + slim_tx_ch_get, slim_tx_ch_put), + SOC_ENUM_EXT("SLIM_1_TX Channels", slim_1_tx_chs, + slim_tx_ch_get, slim_tx_ch_put), + SOC_ENUM_EXT("SLIM_5_RX Channels", slim_5_rx_chs, + slim_rx_ch_get, slim_rx_ch_put), + SOC_ENUM_EXT("SLIM_6_RX Channels", slim_6_rx_chs, + slim_rx_ch_get, slim_rx_ch_put), + SOC_ENUM_EXT("SLIM_0_RX Format", slim_0_rx_format, + slim_rx_bit_format_get, slim_rx_bit_format_put), + SOC_ENUM_EXT("SLIM_5_RX Format", slim_5_rx_format, + slim_rx_bit_format_get, slim_rx_bit_format_put), + SOC_ENUM_EXT("SLIM_6_RX Format", slim_6_rx_format, + slim_rx_bit_format_get, slim_rx_bit_format_put), + SOC_ENUM_EXT("SLIM_0_TX Format", slim_0_tx_format, + slim_tx_bit_format_get, slim_tx_bit_format_put), + SOC_ENUM_EXT("SLIM_0_RX SampleRate", slim_0_rx_sample_rate, + slim_rx_sample_rate_get, slim_rx_sample_rate_put), + SOC_ENUM_EXT("SLIM_2_RX SampleRate", slim_2_rx_sample_rate, + slim_rx_sample_rate_get, slim_rx_sample_rate_put), + SOC_ENUM_EXT("SLIM_0_TX SampleRate", slim_0_tx_sample_rate, + slim_tx_sample_rate_get, slim_tx_sample_rate_put), + SOC_ENUM_EXT("SLIM_5_RX SampleRate", slim_5_rx_sample_rate, + slim_rx_sample_rate_get, slim_rx_sample_rate_put), + SOC_ENUM_EXT("SLIM_6_RX SampleRate", slim_6_rx_sample_rate, + slim_rx_sample_rate_get, slim_rx_sample_rate_put), +}; + +static const struct snd_kcontrol_new msm_snd_va_controls[] = { + SOC_ENUM_EXT("VA_CDC_DMA_TX_0 Channels", va_cdc_dma_tx_0_chs, + cdc_dma_tx_ch_get, cdc_dma_tx_ch_put), + SOC_ENUM_EXT("VA_CDC_DMA_TX_1 Channels", va_cdc_dma_tx_1_chs, + cdc_dma_tx_ch_get, cdc_dma_tx_ch_put), + SOC_ENUM_EXT("VA_CDC_DMA_TX_0 Format", va_cdc_dma_tx_0_format, + cdc_dma_tx_format_get, cdc_dma_tx_format_put), + SOC_ENUM_EXT("VA_CDC_DMA_TX_1 Format", va_cdc_dma_tx_1_format, + cdc_dma_tx_format_get, cdc_dma_tx_format_put), + SOC_ENUM_EXT("VA_CDC_DMA_TX_0 SampleRate", + va_cdc_dma_tx_0_sample_rate, + cdc_dma_tx_sample_rate_get, + cdc_dma_tx_sample_rate_put), + SOC_ENUM_EXT("VA_CDC_DMA_TX_1 SampleRate", + va_cdc_dma_tx_1_sample_rate, + cdc_dma_tx_sample_rate_get, + cdc_dma_tx_sample_rate_put), +}; + +static const struct snd_kcontrol_new msm_snd_wsa_controls[] = { + SOC_ENUM_EXT("VI_FEED_TX Channels", vi_feed_tx_chs, + msm_vi_feed_tx_ch_get, msm_vi_feed_tx_ch_put), + SOC_ENUM_EXT("WSA_CDC_DMA_RX_0 Channels", wsa_cdc_dma_rx_0_chs, + cdc_dma_rx_ch_get, cdc_dma_rx_ch_put), + SOC_ENUM_EXT("WSA_CDC_DMA_RX_1 Channels", wsa_cdc_dma_rx_1_chs, + cdc_dma_rx_ch_get, cdc_dma_rx_ch_put), + SOC_ENUM_EXT("WSA_CDC_DMA_TX_0 Channels", wsa_cdc_dma_tx_0_chs, + cdc_dma_tx_ch_get, cdc_dma_tx_ch_put), + SOC_ENUM_EXT("WSA_CDC_DMA_TX_1 Channels", wsa_cdc_dma_tx_1_chs, + cdc_dma_tx_ch_get, cdc_dma_tx_ch_put), + SOC_ENUM_EXT("WSA_CDC_DMA_TX_2 Channels", wsa_cdc_dma_tx_2_chs, + cdc_dma_tx_ch_get, cdc_dma_tx_ch_put), + SOC_ENUM_EXT("WSA_CDC_DMA_RX_0 Format", wsa_cdc_dma_rx_0_format, + cdc_dma_rx_format_get, cdc_dma_rx_format_put), + SOC_ENUM_EXT("WSA_CDC_DMA_RX_1 Format", wsa_cdc_dma_rx_1_format, + cdc_dma_rx_format_get, cdc_dma_rx_format_put), + SOC_ENUM_EXT("WSA_CDC_DMA_TX_1 Format", wsa_cdc_dma_tx_1_format, + cdc_dma_tx_format_get, cdc_dma_tx_format_put), + SOC_ENUM_EXT("WSA_CDC_DMA_TX_2 Format", wsa_cdc_dma_tx_2_format, + cdc_dma_tx_format_get, cdc_dma_tx_format_put), + SOC_ENUM_EXT("WSA_CDC_DMA_RX_0 SampleRate", + wsa_cdc_dma_rx_0_sample_rate, + cdc_dma_rx_sample_rate_get, + cdc_dma_rx_sample_rate_put), + SOC_ENUM_EXT("WSA_CDC_DMA_RX_1 SampleRate", + wsa_cdc_dma_rx_1_sample_rate, + cdc_dma_rx_sample_rate_get, + cdc_dma_rx_sample_rate_put), + SOC_ENUM_EXT("WSA_CDC_DMA_TX_0 SampleRate", + wsa_cdc_dma_tx_0_sample_rate, + cdc_dma_tx_sample_rate_get, + cdc_dma_tx_sample_rate_put), + SOC_ENUM_EXT("WSA_CDC_DMA_TX_1 SampleRate", + wsa_cdc_dma_tx_1_sample_rate, + cdc_dma_tx_sample_rate_get, + cdc_dma_tx_sample_rate_put), + SOC_ENUM_EXT("WSA_CDC_DMA_TX_2 SampleRate", + wsa_cdc_dma_tx_2_sample_rate, + cdc_dma_tx_sample_rate_get, + cdc_dma_tx_sample_rate_put), +}; + +static const struct snd_kcontrol_new msm_snd_controls[] = { + SOC_ENUM_EXT("BT_TX SampleRate", bt_sample_rate_sink, + msm_bt_sample_rate_sink_get, + msm_bt_sample_rate_sink_put), + SOC_ENUM_EXT("BT SampleRate", bt_sample_rate, + msm_bt_sample_rate_get, + msm_bt_sample_rate_put), + SOC_ENUM_EXT("BT_RX SampleRate", bt_sample_rate, + msm_bt_sample_rate_get, + msm_bt_sample_rate_put), + SOC_ENUM_EXT("PROXY_RX Channels", proxy_rx_chs, + proxy_rx_ch_get, proxy_rx_ch_put), + SOC_ENUM_EXT("USB_AUDIO_RX Channels", usb_rx_chs, + usb_audio_rx_ch_get, usb_audio_rx_ch_put), + SOC_ENUM_EXT("USB_AUDIO_TX Channels", usb_tx_chs, + usb_audio_tx_ch_get, usb_audio_tx_ch_put), + SOC_ENUM_EXT("USB_AUDIO_RX Format", usb_rx_format, + usb_audio_rx_format_get, usb_audio_rx_format_put), + SOC_ENUM_EXT("USB_AUDIO_TX Format", usb_tx_format, + usb_audio_tx_format_get, usb_audio_tx_format_put), + SOC_ENUM_EXT("USB_AUDIO_RX SampleRate", usb_rx_sample_rate, + usb_audio_rx_sample_rate_get, + usb_audio_rx_sample_rate_put), + SOC_ENUM_EXT("USB_AUDIO_TX SampleRate", usb_tx_sample_rate, + usb_audio_tx_sample_rate_get, + usb_audio_tx_sample_rate_put), + SOC_ENUM_EXT("PRI_TDM_RX_0 SampleRate", tdm_rx_sample_rate, + tdm_rx_sample_rate_get, + tdm_rx_sample_rate_put), + SOC_ENUM_EXT("PRI_TDM_TX_0 SampleRate", tdm_tx_sample_rate, + tdm_tx_sample_rate_get, + tdm_tx_sample_rate_put), + SOC_ENUM_EXT("PRI_TDM_RX_0 Format", tdm_rx_format, + tdm_rx_format_get, + tdm_rx_format_put), + SOC_ENUM_EXT("PRI_TDM_TX_0 Format", tdm_tx_format, + tdm_tx_format_get, + tdm_tx_format_put), + SOC_ENUM_EXT("PRI_TDM_RX_0 Channels", tdm_rx_chs, + tdm_rx_ch_get, + tdm_rx_ch_put), + SOC_ENUM_EXT("PRI_TDM_TX_0 Channels", tdm_tx_chs, + tdm_tx_ch_get, + tdm_tx_ch_put), + SOC_ENUM_EXT("SEC_TDM_RX_0 SampleRate", tdm_rx_sample_rate, + tdm_rx_sample_rate_get, + tdm_rx_sample_rate_put), + SOC_ENUM_EXT("SEC_TDM_TX_0 SampleRate", tdm_tx_sample_rate, + tdm_tx_sample_rate_get, + tdm_tx_sample_rate_put), + SOC_ENUM_EXT("SEC_TDM_RX_0 Format", tdm_rx_format, + tdm_rx_format_get, + tdm_rx_format_put), + SOC_ENUM_EXT("SEC_TDM_TX_0 Format", tdm_tx_format, + tdm_tx_format_get, + tdm_tx_format_put), + SOC_ENUM_EXT("SEC_TDM_RX_0 Channels", tdm_rx_chs, + tdm_rx_ch_get, + tdm_rx_ch_put), + SOC_ENUM_EXT("SEC_TDM_TX_0 Channels", tdm_tx_chs, + tdm_tx_ch_get, + tdm_tx_ch_put), + SOC_ENUM_EXT("TERT_TDM_RX_0 SampleRate", tdm_rx_sample_rate, + tdm_rx_sample_rate_get, + tdm_rx_sample_rate_put), + SOC_ENUM_EXT("TERT_TDM_TX_0 SampleRate", tdm_tx_sample_rate, + tdm_tx_sample_rate_get, + tdm_tx_sample_rate_put), + SOC_ENUM_EXT("TERT_TDM_RX_0 Format", tdm_rx_format, + tdm_rx_format_get, + tdm_rx_format_put), + SOC_ENUM_EXT("TERT_TDM_TX_0 Format", tdm_tx_format, + tdm_tx_format_get, + tdm_tx_format_put), + SOC_ENUM_EXT("TERT_TDM_RX_0 Channels", tdm_rx_chs, + tdm_rx_ch_get, + tdm_rx_ch_put), + SOC_ENUM_EXT("TERT_TDM_TX_0 Channels", tdm_tx_chs, + tdm_tx_ch_get, + tdm_tx_ch_put), + SOC_ENUM_EXT("QUAT_TDM_RX_0 SampleRate", tdm_rx_sample_rate, + tdm_rx_sample_rate_get, + tdm_rx_sample_rate_put), + SOC_ENUM_EXT("QUAT_TDM_TX_0 SampleRate", tdm_tx_sample_rate, + tdm_tx_sample_rate_get, + tdm_tx_sample_rate_put), + SOC_ENUM_EXT("QUAT_TDM_RX_0 Format", tdm_rx_format, + tdm_rx_format_get, + tdm_rx_format_put), + SOC_ENUM_EXT("QUAT_TDM_TX_0 Format", tdm_tx_format, + tdm_tx_format_get, + tdm_tx_format_put), + SOC_ENUM_EXT("QUAT_TDM_RX_0 Channels", tdm_rx_chs, + tdm_rx_ch_get, + tdm_rx_ch_put), + SOC_ENUM_EXT("QUAT_TDM_TX_0 Channels", tdm_tx_chs, + tdm_tx_ch_get, + tdm_tx_ch_put), + SOC_ENUM_EXT("QUIN_TDM_RX_0 SampleRate", tdm_rx_sample_rate, + tdm_rx_sample_rate_get, + tdm_rx_sample_rate_put), + SOC_ENUM_EXT("QUIN_TDM_TX_0 SampleRate", tdm_tx_sample_rate, + tdm_tx_sample_rate_get, + tdm_tx_sample_rate_put), + SOC_ENUM_EXT("QUIN_TDM_RX_0 Format", tdm_rx_format, + tdm_rx_format_get, + tdm_rx_format_put), + SOC_ENUM_EXT("QUIN_TDM_TX_0 Format", tdm_tx_format, + tdm_tx_format_get, + tdm_tx_format_put), + SOC_ENUM_EXT("QUIN_TDM_RX_0 Channels", tdm_rx_chs, + tdm_rx_ch_get, + tdm_rx_ch_put), + SOC_ENUM_EXT("QUIN_TDM_TX_0 Channels", tdm_tx_chs, + tdm_tx_ch_get, + tdm_tx_ch_put), + SOC_ENUM_EXT("PRIM_AUX_PCM_RX SampleRate", prim_aux_pcm_rx_sample_rate, + aux_pcm_rx_sample_rate_get, + aux_pcm_rx_sample_rate_put), + SOC_ENUM_EXT("SEC_AUX_PCM_RX SampleRate", sec_aux_pcm_rx_sample_rate, + aux_pcm_rx_sample_rate_get, + aux_pcm_rx_sample_rate_put), + SOC_ENUM_EXT("TERT_AUX_PCM_RX SampleRate", tert_aux_pcm_rx_sample_rate, + aux_pcm_rx_sample_rate_get, + aux_pcm_rx_sample_rate_put), + SOC_ENUM_EXT("QUAT_AUX_PCM_RX SampleRate", quat_aux_pcm_rx_sample_rate, + aux_pcm_rx_sample_rate_get, + aux_pcm_rx_sample_rate_put), + SOC_ENUM_EXT("QUIN_AUX_PCM_RX SampleRate", quin_aux_pcm_rx_sample_rate, + aux_pcm_rx_sample_rate_get, + aux_pcm_rx_sample_rate_put), + SOC_ENUM_EXT("PRIM_AUX_PCM_TX SampleRate", prim_aux_pcm_tx_sample_rate, + aux_pcm_tx_sample_rate_get, + aux_pcm_tx_sample_rate_put), + SOC_ENUM_EXT("SEC_AUX_PCM_TX SampleRate", sec_aux_pcm_tx_sample_rate, + aux_pcm_tx_sample_rate_get, + aux_pcm_tx_sample_rate_put), + SOC_ENUM_EXT("TERT_AUX_PCM_TX SampleRate", tert_aux_pcm_tx_sample_rate, + aux_pcm_tx_sample_rate_get, + aux_pcm_tx_sample_rate_put), + SOC_ENUM_EXT("QUAT_AUX_PCM_TX SampleRate", quat_aux_pcm_tx_sample_rate, + aux_pcm_tx_sample_rate_get, + aux_pcm_tx_sample_rate_put), + SOC_ENUM_EXT("QUIN_AUX_PCM_TX SampleRate", quin_aux_pcm_tx_sample_rate, + aux_pcm_tx_sample_rate_get, + aux_pcm_tx_sample_rate_put), + SOC_ENUM_EXT("SEN_AUX_PCM_TX SampleRate", sen_aux_pcm_tx_sample_rate, + aux_pcm_tx_sample_rate_get, + aux_pcm_tx_sample_rate_put), + SOC_ENUM_EXT("PRIM_MI2S_RX SampleRate", prim_mi2s_rx_sample_rate, + mi2s_rx_sample_rate_get, + mi2s_rx_sample_rate_put), + SOC_ENUM_EXT("SEC_MI2S_RX SampleRate", sec_mi2s_rx_sample_rate, + mi2s_rx_sample_rate_get, + mi2s_rx_sample_rate_put), + SOC_ENUM_EXT("TERT_MI2S_RX SampleRate", tert_mi2s_rx_sample_rate, + mi2s_rx_sample_rate_get, + mi2s_rx_sample_rate_put), + SOC_ENUM_EXT("QUAT_MI2S_RX SampleRate", quat_mi2s_rx_sample_rate, + mi2s_rx_sample_rate_get, + mi2s_rx_sample_rate_put), + SOC_ENUM_EXT("QUIN_MI2S_RX SampleRate", quin_mi2s_rx_sample_rate, + mi2s_rx_sample_rate_get, + mi2s_rx_sample_rate_put), + SOC_ENUM_EXT("SEN_MI2S_RX SampleRate", sen_mi2s_rx_sample_rate, + mi2s_rx_sample_rate_get, + mi2s_rx_sample_rate_put), + SOC_ENUM_EXT("PRIM_MI2S_TX SampleRate", prim_mi2s_tx_sample_rate, + mi2s_tx_sample_rate_get, + mi2s_tx_sample_rate_put), + SOC_ENUM_EXT("SEC_MI2S_TX SampleRate", sec_mi2s_tx_sample_rate, + mi2s_tx_sample_rate_get, + mi2s_tx_sample_rate_put), + SOC_ENUM_EXT("TERT_MI2S_TX SampleRate", tert_mi2s_tx_sample_rate, + mi2s_tx_sample_rate_get, + mi2s_tx_sample_rate_put), + SOC_ENUM_EXT("QUAT_MI2S_TX SampleRate", quat_mi2s_tx_sample_rate, + mi2s_tx_sample_rate_get, + mi2s_tx_sample_rate_put), + SOC_ENUM_EXT("QUIN_MI2S_TX SampleRate", quin_mi2s_tx_sample_rate, + mi2s_tx_sample_rate_get, + mi2s_tx_sample_rate_put), + SOC_ENUM_EXT("SEN_MI2S_TX SampleRate", sen_mi2s_tx_sample_rate, + mi2s_tx_sample_rate_get, + mi2s_tx_sample_rate_put), + SOC_ENUM_EXT("PRIM_MI2S_RX Channels", prim_mi2s_rx_chs, + msm_mi2s_rx_ch_get, msm_mi2s_rx_ch_put), + SOC_ENUM_EXT("PRIM_MI2S_TX Channels", prim_mi2s_tx_chs, + msm_mi2s_tx_ch_get, msm_mi2s_tx_ch_put), + SOC_ENUM_EXT("SEC_MI2S_RX Channels", sec_mi2s_rx_chs, + msm_mi2s_rx_ch_get, msm_mi2s_rx_ch_put), + SOC_ENUM_EXT("SEC_MI2S_TX Channels", sec_mi2s_tx_chs, + msm_mi2s_tx_ch_get, msm_mi2s_tx_ch_put), + SOC_ENUM_EXT("TERT_MI2S_RX Channels", tert_mi2s_rx_chs, + msm_mi2s_rx_ch_get, msm_mi2s_rx_ch_put), + SOC_ENUM_EXT("TERT_MI2S_TX Channels", tert_mi2s_tx_chs, + msm_mi2s_tx_ch_get, msm_mi2s_tx_ch_put), + SOC_ENUM_EXT("QUAT_MI2S_RX Channels", quat_mi2s_rx_chs, + msm_mi2s_rx_ch_get, msm_mi2s_rx_ch_put), + SOC_ENUM_EXT("QUAT_MI2S_TX Channels", quat_mi2s_tx_chs, + msm_mi2s_tx_ch_get, msm_mi2s_tx_ch_put), + SOC_ENUM_EXT("QUIN_MI2S_RX Channels", quin_mi2s_rx_chs, + msm_mi2s_rx_ch_get, msm_mi2s_rx_ch_put), + SOC_ENUM_EXT("QUIN_MI2S_TX Channels", quin_mi2s_tx_chs, + msm_mi2s_tx_ch_get, msm_mi2s_tx_ch_put), + SOC_ENUM_EXT("SEN_MI2S_RX Channels", sen_mi2s_rx_chs, + msm_mi2s_rx_ch_get, msm_mi2s_rx_ch_put), + SOC_ENUM_EXT("SEN_MI2S_TX Channels", sen_mi2s_tx_chs, + msm_mi2s_tx_ch_get, msm_mi2s_tx_ch_put), + SOC_ENUM_EXT("PRIM_MI2S_RX Format", mi2s_rx_format, + msm_mi2s_rx_format_get, msm_mi2s_rx_format_put), + SOC_ENUM_EXT("PRIM_MI2S_TX Format", mi2s_tx_format, + msm_mi2s_tx_format_get, msm_mi2s_tx_format_put), + SOC_ENUM_EXT("SEC_MI2S_RX Format", mi2s_rx_format, + msm_mi2s_rx_format_get, msm_mi2s_rx_format_put), + SOC_ENUM_EXT("SEC_MI2S_TX Format", mi2s_tx_format, + msm_mi2s_tx_format_get, msm_mi2s_tx_format_put), + SOC_ENUM_EXT("TERT_MI2S_RX Format", mi2s_rx_format, + msm_mi2s_rx_format_get, msm_mi2s_rx_format_put), + SOC_ENUM_EXT("TERT_MI2S_TX Format", mi2s_tx_format, + msm_mi2s_tx_format_get, msm_mi2s_tx_format_put), + SOC_ENUM_EXT("QUAT_MI2S_RX Format", mi2s_rx_format, + msm_mi2s_rx_format_get, msm_mi2s_rx_format_put), + SOC_ENUM_EXT("QUAT_MI2S_TX Format", mi2s_tx_format, + msm_mi2s_tx_format_get, msm_mi2s_tx_format_put), + SOC_ENUM_EXT("QUIN_MI2S_RX Format", mi2s_rx_format, + msm_mi2s_rx_format_get, msm_mi2s_rx_format_put), + SOC_ENUM_EXT("QUIN_MI2S_TX Format", mi2s_tx_format, + msm_mi2s_tx_format_get, msm_mi2s_tx_format_put), + SOC_ENUM_EXT("SEN_MI2S_RX Format", mi2s_rx_format, + msm_mi2s_rx_format_get, msm_mi2s_rx_format_put), + SOC_ENUM_EXT("SEN_MI2S_TX Format", mi2s_tx_format, + msm_mi2s_tx_format_get, msm_mi2s_tx_format_put), + SOC_ENUM_EXT("PRIM_AUX_PCM_RX Format", aux_pcm_rx_format, + msm_aux_pcm_rx_format_get, msm_aux_pcm_rx_format_put), + SOC_ENUM_EXT("PRIM_AUX_PCM_TX Format", aux_pcm_tx_format, + msm_aux_pcm_tx_format_get, msm_aux_pcm_tx_format_put), + SOC_ENUM_EXT("SEC_AUX_PCM_RX Format", aux_pcm_rx_format, + msm_aux_pcm_rx_format_get, msm_aux_pcm_rx_format_put), + SOC_ENUM_EXT("SEC_AUX_PCM_TX Format", aux_pcm_tx_format, + msm_aux_pcm_tx_format_get, msm_aux_pcm_tx_format_put), + SOC_ENUM_EXT("TERT_AUX_PCM_RX Format", aux_pcm_rx_format, + msm_aux_pcm_rx_format_get, msm_aux_pcm_rx_format_put), + SOC_ENUM_EXT("TERT_AUX_PCM_TX Format", aux_pcm_tx_format, + msm_aux_pcm_tx_format_get, msm_aux_pcm_tx_format_put), + SOC_ENUM_EXT("QUAT_AUX_PCM_RX Format", aux_pcm_rx_format, + msm_aux_pcm_rx_format_get, msm_aux_pcm_rx_format_put), + SOC_ENUM_EXT("QUAT_AUX_PCM_TX Format", aux_pcm_tx_format, + msm_aux_pcm_tx_format_get, msm_aux_pcm_tx_format_put), + SOC_ENUM_EXT("QUIN_AUX_PCM_RX Format", aux_pcm_rx_format, + msm_aux_pcm_rx_format_get, msm_aux_pcm_rx_format_put), + SOC_ENUM_EXT("QUIN_AUX_PCM_TX Format", aux_pcm_tx_format, + msm_aux_pcm_tx_format_get, msm_aux_pcm_tx_format_put), + SOC_ENUM_EXT("SEN_AUX_PCM_RX Format", aux_pcm_rx_format, + msm_aux_pcm_rx_format_get, msm_aux_pcm_rx_format_put), + SOC_ENUM_EXT("SEN_AUX_PCM_TX Format", aux_pcm_tx_format, + msm_aux_pcm_tx_format_get, msm_aux_pcm_tx_format_put), + SOC_SINGLE_MULTI_EXT("VAD CFG", SND_SOC_NOPM, 0, 1000, 0, 3, NULL, + msm_snd_vad_cfg_put), + SOC_ENUM_EXT("PRIM_SPDIF_RX SampleRate", spdif_rx_sample_rate, + msm_spdif_rx_sample_rate_get, + msm_spdif_rx_sample_rate_put), + SOC_ENUM_EXT("PRIM_SPDIF_TX SampleRate", spdif_tx_sample_rate, + msm_spdif_tx_sample_rate_get, + msm_spdif_tx_sample_rate_put), + SOC_ENUM_EXT("SEC_SPDIF_RX SampleRate", spdif_rx_sample_rate, + msm_spdif_rx_sample_rate_get, + msm_spdif_rx_sample_rate_put), + SOC_ENUM_EXT("SEC_SPDIF_TX SampleRate", spdif_tx_sample_rate, + msm_spdif_tx_sample_rate_get, + msm_spdif_tx_sample_rate_put), + SOC_ENUM_EXT("PRIM_SPDIF_RX Channels", spdif_rx_chs, + msm_spdif_rx_ch_get, msm_spdif_rx_ch_put), + SOC_ENUM_EXT("PRIM_SPDIF_TX Channels", spdif_tx_chs, + msm_spdif_tx_ch_get, msm_spdif_tx_ch_put), + SOC_ENUM_EXT("SEC_SPDIF_RX Channels", spdif_rx_chs, + msm_spdif_rx_ch_get, msm_spdif_rx_ch_put), + SOC_ENUM_EXT("SEC_SPDIF_TX Channels", spdif_tx_chs, + msm_spdif_tx_ch_get, msm_spdif_tx_ch_put), + SOC_ENUM_EXT("PRIM_SPDIF_RX Format", spdif_rx_format, + msm_spdif_rx_format_get, msm_spdif_rx_format_put), + SOC_ENUM_EXT("PRIM_SPDIF_TX Format", spdif_tx_format, + msm_spdif_tx_format_get, msm_spdif_tx_format_put), + SOC_ENUM_EXT("SEC_SPDIF_RX Format", spdif_rx_format, + msm_spdif_rx_format_get, msm_spdif_rx_format_put), + SOC_ENUM_EXT("SEC_SPDIF_TX Format", spdif_tx_format, + msm_spdif_tx_format_get, msm_spdif_tx_format_put), +}; + +static int msm_snd_enable_codec_ext_clk(struct snd_soc_codec *codec, + int enable, bool dapm) +{ + int ret = 0; + + if (!strcmp(dev_name(codec->dev), "tasha_codec")) { + ret = tasha_cdc_mclk_enable(codec, enable, dapm); + } else { + dev_err(codec->dev, "%s: unknown codec to enable ext clk\n", + __func__); + ret = -EINVAL; + } + return ret; +} + +static int msm_snd_enable_codec_ext_tx_clk(struct snd_soc_codec *codec, + int enable, bool dapm) +{ + int ret = 0; + + if (!strcmp(dev_name(codec->dev), "tasha_codec")) { + ret = tasha_cdc_mclk_tx_enable(codec, enable, dapm); + } else { + dev_err(codec->dev, "%s: unknown codec to enable TX ext clk\n", + __func__); + ret = -EINVAL; + } + + return ret; +} + +static int msm_mclk_tx_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + + pr_debug("%s: event = %d\n", __func__, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + return msm_snd_enable_codec_ext_tx_clk(codec, 1, true); + case SND_SOC_DAPM_POST_PMD: + return msm_snd_enable_codec_ext_tx_clk(codec, 0, true); + } + return 0; +} + +static int msm_mclk_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + + pr_debug("%s: event = %d\n", __func__, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + return msm_snd_enable_codec_ext_clk(codec, 1, true); + case SND_SOC_DAPM_POST_PMD: + return msm_snd_enable_codec_ext_clk(codec, 0, true); + } + return 0; +} + +static const struct snd_soc_dapm_widget msm_dapm_widgets[] = { + + SND_SOC_DAPM_SUPPLY("MCLK", SND_SOC_NOPM, 0, 0, + msm_mclk_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_SUPPLY("MCLK TX", SND_SOC_NOPM, 0, 0, + msm_mclk_tx_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MIC("Analog Mic3", NULL), + SND_SOC_DAPM_MIC("Analog Mic4", NULL), + +}; + +static int msm_dmic_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct msm_asoc_mach_data *pdata = NULL; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + int ret = 0; + uint32_t dmic_idx; + int *dmic_gpio_cnt; + struct device_node *dmic_gpio; + char *wname; + + wname = strpbrk(w->name, "01234567"); + if (!wname) { + dev_err(codec->dev, "%s: widget not found\n", __func__); + return -EINVAL; + } + + ret = kstrtouint(wname, 10, &dmic_idx); + if (ret < 0) { + dev_err(codec->dev, "%s: Invalid DMIC line on the codec\n", + __func__); + return -EINVAL; + } + + pdata = snd_soc_card_get_drvdata(codec->component.card); + + switch (dmic_idx) { + case 0: + case 1: + dmic_gpio_cnt = &pdata->dmic_01_gpio_cnt; + dmic_gpio = pdata->dmic_01_gpio_p; + break; + case 2: + case 3: + dmic_gpio_cnt = &pdata->dmic_23_gpio_cnt; + dmic_gpio = pdata->dmic_23_gpio_p; + break; + case 4: + case 5: + dmic_gpio_cnt = &pdata->dmic_45_gpio_cnt; + dmic_gpio = pdata->dmic_45_gpio_p; + break; + case 6: + case 7: + dmic_gpio_cnt = &pdata->dmic_67_gpio_cnt; + dmic_gpio = pdata->dmic_67_gpio_p; + break; + default: + dev_err(codec->dev, "%s: Invalid DMIC Selection\n", + __func__); + return -EINVAL; + } + + dev_dbg(codec->dev, "%s: event %d DMIC%d dmic_gpio_cnt %d\n", + __func__, event, dmic_idx, *dmic_gpio_cnt); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + (*dmic_gpio_cnt)++; + if (*dmic_gpio_cnt == 1) { + ret = msm_cdc_pinctrl_select_active_state( + dmic_gpio); + if (ret < 0) { + dev_err(codec->dev, "%s: gpio set cannot be activated %sd\n", + __func__, "dmic_gpio"); + return ret; + } + } + break; + case SND_SOC_DAPM_POST_PMD: + (*dmic_gpio_cnt)--; + if (*dmic_gpio_cnt == 0) { + ret = msm_cdc_pinctrl_select_sleep_state( + dmic_gpio); + if (ret < 0) { + dev_err(codec->dev, "%s: gpio set cannot be de-activated %sd\n", + __func__, "dmic_gpio"); + return ret; + } + } + break; + default: + dev_err(codec->dev, "%s: invalid DAPM event %d\n", + __func__, event); + return -EINVAL; + } + return 0; +} + + +static const struct snd_soc_dapm_widget msm_va_dapm_widgets[] = { + + SND_SOC_DAPM_MIC("Digital Mic0", msm_dmic_event), + SND_SOC_DAPM_MIC("Digital Mic1", msm_dmic_event), + SND_SOC_DAPM_MIC("Digital Mic2", msm_dmic_event), + SND_SOC_DAPM_MIC("Digital Mic3", msm_dmic_event), + SND_SOC_DAPM_MIC("Digital Mic4", msm_dmic_event), + SND_SOC_DAPM_MIC("Digital Mic5", msm_dmic_event), + SND_SOC_DAPM_MIC("Digital Mic6", msm_dmic_event), + SND_SOC_DAPM_MIC("Digital Mic7", msm_dmic_event), +}; + +static const struct snd_soc_dapm_widget msm_wsa_dapm_widgets[] = { + +}; + +static inline int param_is_mask(int p) +{ + return (p >= SNDRV_PCM_HW_PARAM_FIRST_MASK) && + (p <= SNDRV_PCM_HW_PARAM_LAST_MASK); +} + +static inline struct snd_mask *param_to_mask(struct snd_pcm_hw_params *p, + int n) +{ + return &(p->masks[n - SNDRV_PCM_HW_PARAM_FIRST_MASK]); +} + +static void param_set_mask(struct snd_pcm_hw_params *p, int n, + unsigned int bit) +{ + if (bit >= SNDRV_MASK_MAX) + return; + if (param_is_mask(n)) { + struct snd_mask *m = param_to_mask(p, n); + + m->bits[0] = 0; + m->bits[1] = 0; + m->bits[bit >> 5] |= (1 << (bit & 31)); + } +} + +static int msm_slim_get_ch_from_beid(int32_t be_id) +{ + int ch_id = 0; + + switch (be_id) { + case MSM_BACKEND_DAI_SLIMBUS_0_RX: + ch_id = SLIM_RX_0; + break; + case MSM_BACKEND_DAI_SLIMBUS_1_RX: + ch_id = SLIM_RX_1; + break; + case MSM_BACKEND_DAI_SLIMBUS_2_RX: + ch_id = SLIM_RX_2; + break; + case MSM_BACKEND_DAI_SLIMBUS_3_RX: + ch_id = SLIM_RX_3; + break; + case MSM_BACKEND_DAI_SLIMBUS_4_RX: + ch_id = SLIM_RX_4; + break; + case MSM_BACKEND_DAI_SLIMBUS_6_RX: + ch_id = SLIM_RX_6; + break; + case MSM_BACKEND_DAI_SLIMBUS_0_TX: + ch_id = SLIM_TX_0; + break; + case MSM_BACKEND_DAI_SLIMBUS_3_TX: + ch_id = SLIM_TX_3; + break; + default: + ch_id = SLIM_RX_0; + break; + } + + return ch_id; +} + +static int msm_vad_get_portid_from_beid(int32_t be_id, int *port_id) +{ + *port_id = 0xFFFF; + + switch (be_id) { + case MSM_BACKEND_DAI_VA_CDC_DMA_TX_0: + *port_id = AFE_PORT_ID_VA_CODEC_DMA_TX_0; + break; + case MSM_BACKEND_DAI_QUINARY_MI2S_TX: + *port_id = AFE_PORT_ID_QUINARY_MI2S_TX; + break; + case MSM_BACKEND_DAI_QUIN_TDM_TX_0: + *port_id = AFE_PORT_ID_QUINARY_TDM_TX; + break; + case MSM_BACKEND_DAI_QUIN_AUXPCM_TX: + *port_id = AFE_PORT_ID_QUINARY_PCM_TX; + break; + default: + return -EINVAL; + } + return 0; +} + +static int msm_cdc_dma_get_idx_from_beid(int32_t be_id) +{ + int idx = 0; + + switch (be_id) { + case MSM_BACKEND_DAI_WSA_CDC_DMA_RX_0: + idx = WSA_CDC_DMA_RX_0; + break; + case MSM_BACKEND_DAI_WSA_CDC_DMA_TX_0: + idx = WSA_CDC_DMA_TX_0; + break; + case MSM_BACKEND_DAI_WSA_CDC_DMA_RX_1: + idx = WSA_CDC_DMA_RX_1; + break; + case MSM_BACKEND_DAI_WSA_CDC_DMA_TX_1: + idx = WSA_CDC_DMA_TX_1; + break; + case MSM_BACKEND_DAI_WSA_CDC_DMA_TX_2: + idx = WSA_CDC_DMA_TX_2; + break; + case MSM_BACKEND_DAI_VA_CDC_DMA_TX_0: + idx = VA_CDC_DMA_TX_0; + break; + case MSM_BACKEND_DAI_VA_CDC_DMA_TX_1: + idx = VA_CDC_DMA_TX_1; + break; + default: + idx = VA_CDC_DMA_TX_0; + break; + } + + return idx; +} + +static int msm_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_dai_link *dai_link = rtd->dai_link; + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + int rc = 0; + int idx; + void *config = NULL; + struct snd_soc_codec *codec = NULL; + + pr_debug("%s: format = %d, rate = %d\n", + __func__, params_format(params), params_rate(params)); + + switch (dai_link->id) { + case MSM_BACKEND_DAI_SLIMBUS_0_RX: + case MSM_BACKEND_DAI_SLIMBUS_1_RX: + case MSM_BACKEND_DAI_SLIMBUS_2_RX: + case MSM_BACKEND_DAI_SLIMBUS_3_RX: + case MSM_BACKEND_DAI_SLIMBUS_4_RX: + case MSM_BACKEND_DAI_SLIMBUS_6_RX: + idx = msm_slim_get_ch_from_beid(dai_link->id); + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + slim_rx_cfg[idx].bit_format); + rate->min = rate->max = slim_rx_cfg[idx].sample_rate; + channels->min = channels->max = slim_rx_cfg[idx].channels; + break; + + case MSM_BACKEND_DAI_SLIMBUS_0_TX: + case MSM_BACKEND_DAI_SLIMBUS_3_TX: + idx = msm_slim_get_ch_from_beid(dai_link->id); + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + slim_tx_cfg[idx].bit_format); + rate->min = rate->max = slim_tx_cfg[idx].sample_rate; + channels->min = channels->max = slim_tx_cfg[idx].channels; + break; + + case MSM_BACKEND_DAI_SLIMBUS_1_TX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + slim_tx_cfg[1].bit_format); + rate->min = rate->max = slim_tx_cfg[1].sample_rate; + channels->min = channels->max = slim_tx_cfg[1].channels; + break; + + case MSM_BACKEND_DAI_SLIMBUS_4_TX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + SNDRV_PCM_FORMAT_S32_LE); + rate->min = rate->max = SAMPLING_RATE_8KHZ; + channels->min = channels->max = msm_vi_feed_tx_ch; + break; + + case MSM_BACKEND_DAI_SLIMBUS_5_RX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + slim_rx_cfg[5].bit_format); + rate->min = rate->max = slim_rx_cfg[5].sample_rate; + channels->min = channels->max = slim_rx_cfg[5].channels; + break; + + case MSM_BACKEND_DAI_SLIMBUS_5_TX: + codec = rtd->codec; + rate->min = rate->max = SAMPLING_RATE_16KHZ; + channels->min = channels->max = 1; + + config = msm_codec_fn.get_afe_config_fn(codec, + AFE_SLIMBUS_SLAVE_PORT_CONFIG); + if (config) { + rc = afe_set_config(AFE_SLIMBUS_SLAVE_PORT_CONFIG, + config, SLIMBUS_5_TX); + if (rc) + pr_err("%s: Failed to set slimbus slave port config %d\n", + __func__, rc); + } + break; + + case MSM_BACKEND_DAI_SLIMBUS_7_RX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + slim_rx_cfg[SLIM_RX_7].bit_format); + rate->min = rate->max = slim_rx_cfg[SLIM_RX_7].sample_rate; + channels->min = channels->max = + slim_rx_cfg[SLIM_RX_7].channels; + break; + + case MSM_BACKEND_DAI_SLIMBUS_7_TX: + rate->min = rate->max = slim_tx_cfg[SLIM_TX_7].sample_rate; + channels->min = channels->max = + slim_tx_cfg[SLIM_TX_7].channels; + break; + + case MSM_BACKEND_DAI_SLIMBUS_8_TX: + rate->min = rate->max = slim_tx_cfg[SLIM_TX_8].sample_rate; + channels->min = channels->max = + slim_tx_cfg[SLIM_TX_8].channels; + break; + + case MSM_BACKEND_DAI_SLIMBUS_9_TX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + slim_tx_cfg[SLIM_TX_9].bit_format); + rate->min = rate->max = slim_tx_cfg[SLIM_TX_9].sample_rate; + channels->min = channels->max = + slim_tx_cfg[SLIM_TX_9].channels; + break; + + case MSM_BACKEND_DAI_USB_RX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + usb_rx_cfg.bit_format); + rate->min = rate->max = usb_rx_cfg.sample_rate; + channels->min = channels->max = usb_rx_cfg.channels; + break; + + case MSM_BACKEND_DAI_USB_TX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + usb_tx_cfg.bit_format); + rate->min = rate->max = usb_tx_cfg.sample_rate; + channels->min = channels->max = usb_tx_cfg.channels; + break; + + case MSM_BACKEND_DAI_AFE_PCM_RX: + channels->min = channels->max = proxy_rx_cfg.channels; + rate->min = rate->max = SAMPLING_RATE_48KHZ; + break; + + case MSM_BACKEND_DAI_PRI_TDM_RX_0: + channels->min = channels->max = + tdm_rx_cfg[TDM_PRI][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_PRI][TDM_0].bit_format); + rate->min = rate->max = tdm_rx_cfg[TDM_PRI][TDM_0].sample_rate; + break; + + case MSM_BACKEND_DAI_PRI_TDM_TX_0: + channels->min = channels->max = + tdm_tx_cfg[TDM_PRI][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_tx_cfg[TDM_PRI][TDM_0].bit_format); + rate->min = rate->max = tdm_tx_cfg[TDM_PRI][TDM_0].sample_rate; + break; + + case MSM_BACKEND_DAI_SEC_TDM_RX_0: + channels->min = channels->max = + tdm_rx_cfg[TDM_SEC][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_SEC][TDM_0].bit_format); + rate->min = rate->max = tdm_rx_cfg[TDM_SEC][TDM_0].sample_rate; + break; + + case MSM_BACKEND_DAI_SEC_TDM_TX_0: + channels->min = channels->max = + tdm_tx_cfg[TDM_SEC][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_tx_cfg[TDM_SEC][TDM_0].bit_format); + rate->min = rate->max = tdm_tx_cfg[TDM_SEC][TDM_0].sample_rate; + break; + + case MSM_BACKEND_DAI_TERT_TDM_RX_0: + channels->min = channels->max = + tdm_rx_cfg[TDM_TERT][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_TERT][TDM_0].bit_format); + rate->min = rate->max = tdm_rx_cfg[TDM_TERT][TDM_0].sample_rate; + break; + + case MSM_BACKEND_DAI_TERT_TDM_TX_0: + channels->min = channels->max = + tdm_tx_cfg[TDM_TERT][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_tx_cfg[TDM_TERT][TDM_0].bit_format); + rate->min = rate->max = tdm_tx_cfg[TDM_TERT][TDM_0].sample_rate; + break; + + case MSM_BACKEND_DAI_QUAT_TDM_RX_0: + channels->min = channels->max = + tdm_rx_cfg[TDM_QUAT][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_QUAT][TDM_0].bit_format); + rate->min = rate->max = tdm_rx_cfg[TDM_QUAT][TDM_0].sample_rate; + break; + + case MSM_BACKEND_DAI_QUAT_TDM_TX_0: + channels->min = channels->max = + tdm_tx_cfg[TDM_QUAT][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_tx_cfg[TDM_QUAT][TDM_0].bit_format); + rate->min = rate->max = tdm_tx_cfg[TDM_QUAT][TDM_0].sample_rate; + break; + + case MSM_BACKEND_DAI_QUIN_TDM_RX_0: + channels->min = channels->max = + tdm_rx_cfg[TDM_QUIN][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_QUIN][TDM_0].bit_format); + rate->min = rate->max = tdm_rx_cfg[TDM_QUIN][TDM_0].sample_rate; + break; + + case MSM_BACKEND_DAI_QUIN_TDM_TX_0: + channels->min = channels->max = + tdm_tx_cfg[TDM_QUIN][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_tx_cfg[TDM_QUIN][TDM_0].bit_format); + rate->min = rate->max = tdm_tx_cfg[TDM_QUIN][TDM_0].sample_rate; + break; + + case MSM_BACKEND_DAI_AUXPCM_RX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + aux_pcm_rx_cfg[PRIM_AUX_PCM].bit_format); + rate->min = rate->max = + aux_pcm_rx_cfg[PRIM_AUX_PCM].sample_rate; + channels->min = channels->max = + aux_pcm_rx_cfg[PRIM_AUX_PCM].channels; + break; + + case MSM_BACKEND_DAI_AUXPCM_TX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + aux_pcm_tx_cfg[PRIM_AUX_PCM].bit_format); + rate->min = rate->max = + aux_pcm_tx_cfg[PRIM_AUX_PCM].sample_rate; + channels->min = channels->max = + aux_pcm_tx_cfg[PRIM_AUX_PCM].channels; + break; + + case MSM_BACKEND_DAI_SEC_AUXPCM_RX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + aux_pcm_rx_cfg[SEC_AUX_PCM].bit_format); + rate->min = rate->max = + aux_pcm_rx_cfg[SEC_AUX_PCM].sample_rate; + channels->min = channels->max = + aux_pcm_rx_cfg[SEC_AUX_PCM].channels; + break; + + case MSM_BACKEND_DAI_SEC_AUXPCM_TX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + aux_pcm_tx_cfg[SEC_AUX_PCM].bit_format); + rate->min = rate->max = + aux_pcm_tx_cfg[SEC_AUX_PCM].sample_rate; + channels->min = channels->max = + aux_pcm_tx_cfg[SEC_AUX_PCM].channels; + break; + + case MSM_BACKEND_DAI_TERT_AUXPCM_RX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + aux_pcm_rx_cfg[TERT_AUX_PCM].bit_format); + rate->min = rate->max = + aux_pcm_rx_cfg[TERT_AUX_PCM].sample_rate; + channels->min = channels->max = + aux_pcm_rx_cfg[TERT_AUX_PCM].channels; + break; + + case MSM_BACKEND_DAI_TERT_AUXPCM_TX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + aux_pcm_tx_cfg[TERT_AUX_PCM].bit_format); + rate->min = rate->max = + aux_pcm_tx_cfg[TERT_AUX_PCM].sample_rate; + channels->min = channels->max = + aux_pcm_tx_cfg[TERT_AUX_PCM].channels; + break; + + case MSM_BACKEND_DAI_QUAT_AUXPCM_RX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + aux_pcm_rx_cfg[QUAT_AUX_PCM].bit_format); + rate->min = rate->max = + aux_pcm_rx_cfg[QUAT_AUX_PCM].sample_rate; + channels->min = channels->max = + aux_pcm_rx_cfg[QUAT_AUX_PCM].channels; + break; + + case MSM_BACKEND_DAI_QUAT_AUXPCM_TX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + aux_pcm_tx_cfg[QUAT_AUX_PCM].bit_format); + rate->min = rate->max = + aux_pcm_tx_cfg[QUAT_AUX_PCM].sample_rate; + channels->min = channels->max = + aux_pcm_tx_cfg[QUAT_AUX_PCM].channels; + break; + + case MSM_BACKEND_DAI_QUIN_AUXPCM_RX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + aux_pcm_rx_cfg[QUIN_AUX_PCM].bit_format); + rate->min = rate->max = + aux_pcm_rx_cfg[QUIN_AUX_PCM].sample_rate; + channels->min = channels->max = + aux_pcm_rx_cfg[QUIN_AUX_PCM].channels; + break; + + case MSM_BACKEND_DAI_QUIN_AUXPCM_TX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + aux_pcm_tx_cfg[QUIN_AUX_PCM].bit_format); + rate->min = rate->max = + aux_pcm_tx_cfg[QUIN_AUX_PCM].sample_rate; + channels->min = channels->max = + aux_pcm_tx_cfg[QUIN_AUX_PCM].channels; + break; + + case MSM_BACKEND_DAI_SEN_AUXPCM_RX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + aux_pcm_rx_cfg[SEN_AUX_PCM].bit_format); + rate->min = rate->max = + aux_pcm_rx_cfg[SEN_AUX_PCM].sample_rate; + channels->min = channels->max = + aux_pcm_rx_cfg[SEN_AUX_PCM].channels; + break; + + case MSM_BACKEND_DAI_SEN_AUXPCM_TX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + aux_pcm_tx_cfg[SEN_AUX_PCM].bit_format); + rate->min = rate->max = + aux_pcm_tx_cfg[SEN_AUX_PCM].sample_rate; + channels->min = channels->max = + aux_pcm_tx_cfg[SEN_AUX_PCM].channels; + break; + + case MSM_BACKEND_DAI_PRI_MI2S_RX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + mi2s_rx_cfg[PRIM_MI2S].bit_format); + rate->min = rate->max = mi2s_rx_cfg[PRIM_MI2S].sample_rate; + channels->min = channels->max = + mi2s_rx_cfg[PRIM_MI2S].channels; + break; + + case MSM_BACKEND_DAI_PRI_MI2S_TX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + mi2s_tx_cfg[PRIM_MI2S].bit_format); + rate->min = rate->max = mi2s_tx_cfg[PRIM_MI2S].sample_rate; + channels->min = channels->max = + mi2s_tx_cfg[PRIM_MI2S].channels; + break; + + case MSM_BACKEND_DAI_SECONDARY_MI2S_RX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + mi2s_rx_cfg[SEC_MI2S].bit_format); + rate->min = rate->max = mi2s_rx_cfg[SEC_MI2S].sample_rate; + channels->min = channels->max = + mi2s_rx_cfg[SEC_MI2S].channels; + break; + + case MSM_BACKEND_DAI_SECONDARY_MI2S_TX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + mi2s_tx_cfg[SEC_MI2S].bit_format); + rate->min = rate->max = mi2s_tx_cfg[SEC_MI2S].sample_rate; + channels->min = channels->max = + mi2s_tx_cfg[SEC_MI2S].channels; + break; + + case MSM_BACKEND_DAI_TERTIARY_MI2S_RX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + mi2s_rx_cfg[TERT_MI2S].bit_format); + rate->min = rate->max = mi2s_rx_cfg[TERT_MI2S].sample_rate; + channels->min = channels->max = + mi2s_rx_cfg[TERT_MI2S].channels; + break; + + case MSM_BACKEND_DAI_TERTIARY_MI2S_TX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + mi2s_tx_cfg[TERT_MI2S].bit_format); + rate->min = rate->max = mi2s_tx_cfg[TERT_MI2S].sample_rate; + channels->min = channels->max = + mi2s_tx_cfg[TERT_MI2S].channels; + break; + + case MSM_BACKEND_DAI_QUATERNARY_MI2S_RX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + mi2s_rx_cfg[QUAT_MI2S].bit_format); + rate->min = rate->max = mi2s_rx_cfg[QUAT_MI2S].sample_rate; + channels->min = channels->max = + mi2s_rx_cfg[QUAT_MI2S].channels; + break; + + case MSM_BACKEND_DAI_QUATERNARY_MI2S_TX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + mi2s_tx_cfg[QUAT_MI2S].bit_format); + rate->min = rate->max = mi2s_tx_cfg[QUAT_MI2S].sample_rate; + channels->min = channels->max = + mi2s_tx_cfg[QUAT_MI2S].channels; + break; + + case MSM_BACKEND_DAI_QUINARY_MI2S_RX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + mi2s_rx_cfg[QUIN_MI2S].bit_format); + rate->min = rate->max = mi2s_rx_cfg[QUIN_MI2S].sample_rate; + channels->min = channels->max = + mi2s_rx_cfg[QUIN_MI2S].channels; + break; + + case MSM_BACKEND_DAI_QUINARY_MI2S_TX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + mi2s_tx_cfg[QUIN_MI2S].bit_format); + rate->min = rate->max = mi2s_tx_cfg[QUIN_MI2S].sample_rate; + channels->min = channels->max = + mi2s_tx_cfg[QUIN_MI2S].channels; + break; + + case MSM_BACKEND_DAI_SENARY_MI2S_RX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + mi2s_rx_cfg[SEN_MI2S].bit_format); + rate->min = rate->max = mi2s_rx_cfg[SEN_MI2S].sample_rate; + channels->min = channels->max = + mi2s_rx_cfg[SEN_MI2S].channels; + break; + + case MSM_BACKEND_DAI_SENARY_MI2S_TX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + mi2s_tx_cfg[SEN_MI2S].bit_format); + rate->min = rate->max = mi2s_tx_cfg[SEN_MI2S].sample_rate; + channels->min = channels->max = + mi2s_tx_cfg[SEN_MI2S].channels; + break; + case MSM_BACKEND_DAI_WSA_CDC_DMA_RX_0: + case MSM_BACKEND_DAI_WSA_CDC_DMA_RX_1: + idx = msm_cdc_dma_get_idx_from_beid(dai_link->id); + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + cdc_dma_rx_cfg[idx].bit_format); + rate->min = rate->max = cdc_dma_rx_cfg[idx].sample_rate; + channels->min = channels->max = cdc_dma_rx_cfg[idx].channels; + break; + + case MSM_BACKEND_DAI_WSA_CDC_DMA_TX_1: + case MSM_BACKEND_DAI_WSA_CDC_DMA_TX_2: + case MSM_BACKEND_DAI_VA_CDC_DMA_TX_0: + case MSM_BACKEND_DAI_VA_CDC_DMA_TX_1: + idx = msm_cdc_dma_get_idx_from_beid(dai_link->id); + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + cdc_dma_tx_cfg[idx].bit_format); + rate->min = rate->max = cdc_dma_tx_cfg[idx].sample_rate; + channels->min = channels->max = cdc_dma_tx_cfg[idx].channels; + break; + + case MSM_BACKEND_DAI_WSA_CDC_DMA_TX_0: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + SNDRV_PCM_FORMAT_S32_LE); + rate->min = rate->max = SAMPLING_RATE_8KHZ; + channels->min = channels->max = msm_vi_feed_tx_ch; + break; + + case MSM_BACKEND_DAI_PRI_SPDIF_RX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + spdif_rx_cfg[PRIM_SPDIF_RX].bit_format); + rate->min = rate->max = + spdif_rx_cfg[PRIM_SPDIF_RX].sample_rate; + channels->min = channels->max = + spdif_rx_cfg[PRIM_SPDIF_RX].channels; + break; + + case MSM_BACKEND_DAI_PRI_SPDIF_TX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + spdif_tx_cfg[PRIM_SPDIF_TX].bit_format); + rate->min = rate->max = + spdif_tx_cfg[PRIM_SPDIF_TX].sample_rate; + channels->min = channels->max = + spdif_tx_cfg[PRIM_SPDIF_TX].channels; + break; + + case MSM_BACKEND_DAI_SEC_SPDIF_RX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + spdif_rx_cfg[SEC_SPDIF_RX].bit_format); + rate->min = rate->max = + spdif_rx_cfg[SEC_SPDIF_RX].sample_rate; + channels->min = channels->max = + spdif_rx_cfg[SEC_SPDIF_RX].channels; + break; + + case MSM_BACKEND_DAI_SEC_SPDIF_TX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + spdif_tx_cfg[SEC_SPDIF_TX].bit_format); + rate->min = rate->max = + spdif_tx_cfg[SEC_SPDIF_TX].sample_rate; + channels->min = channels->max = + spdif_tx_cfg[SEC_SPDIF_TX].channels; + break; + + default: + rate->min = rate->max = SAMPLING_RATE_48KHZ; + break; + } + + return rc; +} + +static int msm_afe_set_config(struct snd_soc_codec *codec) +{ + int ret = 0; + void *config_data = NULL; + + if (!msm_codec_fn.get_afe_config_fn) { + dev_err(codec->dev, "%s: codec get afe config not init'ed\n", + __func__); + return -EINVAL; + } + + config_data = msm_codec_fn.get_afe_config_fn(codec, + AFE_CDC_REGISTERS_CONFIG); + if (config_data) { + ret = afe_set_config(AFE_CDC_REGISTERS_CONFIG, config_data, 0); + if (ret) { + dev_err(codec->dev, + "%s: Failed to set codec registers config %d\n", + __func__, ret); + return ret; + } + } + + config_data = msm_codec_fn.get_afe_config_fn(codec, + AFE_CDC_REGISTER_PAGE_CONFIG); + if (config_data) { + ret = afe_set_config(AFE_CDC_REGISTER_PAGE_CONFIG, config_data, + 0); + if (ret) + dev_err(codec->dev, + "%s: Failed to set cdc register page config\n", + __func__); + } + + config_data = msm_codec_fn.get_afe_config_fn(codec, + AFE_SLIMBUS_SLAVE_CONFIG); + if (config_data) { + ret = afe_set_config(AFE_SLIMBUS_SLAVE_CONFIG, config_data, 0); + if (ret) { + dev_err(codec->dev, + "%s: Failed to set slimbus slave config %d\n", + __func__, ret); + return ret; + } + } + + return 0; +} + +static void msm_afe_clear_config(void) +{ + afe_clear_config(AFE_CDC_REGISTERS_CONFIG); + afe_clear_config(AFE_SLIMBUS_SLAVE_CONFIG); +} + +static int msm_adsp_power_up_config(struct snd_soc_codec *codec, + struct snd_card *card) +{ + int ret = 0; + unsigned long timeout; + int adsp_ready = 0; + bool snd_card_online = 0; + + timeout = jiffies + + msecs_to_jiffies(ADSP_STATE_READY_TIMEOUT_MS); + + do { + if (!snd_card_online) { + snd_card_online = snd_card_is_online_state(card); + pr_debug("%s: Sound card is %s\n", __func__, + snd_card_online ? "Online" : "Offline"); + } + if (!adsp_ready) { + adsp_ready = q6core_is_adsp_ready(); + pr_debug("%s: ADSP Audio is %s\n", __func__, + adsp_ready ? "ready" : "not ready"); + } + if (snd_card_online && adsp_ready) + break; + + /* + * Sound card/ADSP will be coming up after subsystem restart and + * it might not be fully up when the control reaches + * here. So, wait for 50msec before checking ADSP state + */ + msleep(50); + } while (time_after(timeout, jiffies)); + + if (!snd_card_online || !adsp_ready) { + pr_err("%s: Timeout. Sound card is %s, ADSP Audio is %s\n", + __func__, + snd_card_online ? "Online" : "Offline", + adsp_ready ? "ready" : "not ready"); + ret = -ETIMEDOUT; + goto err; + } + + ret = msm_afe_set_config(codec); + if (ret) + pr_err("%s: Failed to set AFE config. err %d\n", + __func__, ret); + + return 0; + +err: + return ret; +} + +static int qcs405_notifier_service_cb(struct notifier_block *this, + unsigned long opcode, void *ptr) +{ + int ret; + struct snd_soc_card *card = NULL; + const char *be_dl_name = LPASS_BE_SLIMBUS_0_RX; + struct snd_soc_pcm_runtime *rtd; + struct snd_soc_codec *codec; + + pr_debug("%s: Service opcode 0x%lx\n", __func__, opcode); + + switch (opcode) { + case AUDIO_NOTIFIER_SERVICE_DOWN: + /* + * Use flag to ignore initial boot notifications + * On initial boot msm_adsp_power_up_config is + * called on init. There is no need to clear + * and set the config again on initial boot. + */ + if (is_initial_boot) + break; + msm_afe_clear_config(); + break; + case AUDIO_NOTIFIER_SERVICE_UP: + if (is_initial_boot) { + is_initial_boot = false; + break; + } + if (!spdev) + return -EINVAL; + + card = platform_get_drvdata(spdev); + rtd = snd_soc_get_pcm_runtime(card, be_dl_name); + if (!rtd) { + dev_err(card->dev, + "%s: snd_soc_get_pcm_runtime for %s failed!\n", + __func__, be_dl_name); + ret = -EINVAL; + goto err; + } + codec = rtd->codec; + + ret = msm_adsp_power_up_config(codec, card->snd_card); + if (ret < 0) { + dev_err(card->dev, + "%s: msm_adsp_power_up_config failed ret = %d!\n", + __func__, ret); + goto err; + } + break; + default: + break; + } +err: + return NOTIFY_OK; +} + +static struct notifier_block service_nb = { + .notifier_call = qcs405_notifier_service_cb, + .priority = -INT_MAX, +}; + +static int msm_audrx_init(struct snd_soc_pcm_runtime *rtd) +{ + int ret = 0; + void *config_data; + struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_card *card; + struct msm_asoc_mach_data *pdata = + snd_soc_card_get_drvdata(rtd->card); + + /* + * Codec SLIMBUS configuration + * RX1, RX2, RX3, RX4, RX5, RX6, RX7, RX8 + * TX1, TX2, TX3, TX4, TX5, TX6, TX7, TX8, TX9, TX10, TX11, TX12, TX13 + * TX14, TX15, TX16 + */ + unsigned int rx_ch[TASHA_RX_MAX] = {144, 145, 146, 147, 148, 149, 150, + 151, 152, 153, 154, 155, 156}; + unsigned int tx_ch[TASHA_TX_MAX] = {128, 129, 130, 131, 132, 133, + 134, 135, 136, 137, 138, 139, + 140, 141, 142, 143}; + + pr_info("%s: dev_name:%s\n", __func__, dev_name(cpu_dai->dev)); + + rtd->pmdown_time = 0; + + ret = snd_soc_add_codec_controls(codec, msm_snd_sb_controls, + ARRAY_SIZE(msm_snd_sb_controls)); + if (ret < 0) { + pr_err("%s: add_codec_controls failed, err %d\n", + __func__, ret); + return ret; + } + + snd_soc_dapm_new_controls(dapm, msm_dapm_widgets, + ARRAY_SIZE(msm_dapm_widgets)); + + snd_soc_dapm_add_routes(dapm, wcd_audio_paths, + ARRAY_SIZE(wcd_audio_paths)); + + snd_soc_dapm_ignore_suspend(dapm, "LINEOUT1"); + snd_soc_dapm_ignore_suspend(dapm, "LINEOUT2"); + snd_soc_dapm_ignore_suspend(dapm, "Analog Mic3"); + snd_soc_dapm_ignore_suspend(dapm, "Analog Mic4"); + + snd_soc_dapm_sync(dapm); + + snd_soc_dai_set_channel_map(codec_dai, ARRAY_SIZE(tx_ch), + tx_ch, ARRAY_SIZE(rx_ch), rx_ch); + + msm_codec_fn.get_afe_config_fn = tasha_get_afe_config; + + ret = msm_adsp_power_up_config(codec, rtd->card->snd_card); + if (ret) { + dev_err(codec->dev, "%s: Failed to set AFE config %d\n", + __func__, ret); + goto err; + } + + config_data = msm_codec_fn.get_afe_config_fn(codec, + AFE_AANC_VERSION); + if (config_data) { + ret = afe_set_config(AFE_AANC_VERSION, config_data, 0); + if (ret) { + dev_err(codec->dev, "%s: Failed to set aanc version %d\n", + __func__, ret); + goto err; + } + } + + card = rtd->card->snd_card; + if (!pdata->codec_root) + pdata->codec_root = snd_info_create_subdir(card->module, + "codecs", card->proc_root); + if (!pdata->codec_root) { + dev_dbg(codec->dev, "%s: Cannot create codecs module entry\n", + __func__); + ret = 0; + goto err; + } + tasha_codec_info_create_codec_entry(pdata->codec_root, codec); + + codec_reg_done = true; + return 0; + +err: + return ret; +} + +static int msm_va_cdc_dma_init(struct snd_soc_pcm_runtime *rtd) +{ + int ret = 0; + struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + struct snd_card *card; + struct msm_asoc_mach_data *pdata = + snd_soc_card_get_drvdata(rtd->card); + + ret = snd_soc_add_codec_controls(codec, msm_snd_va_controls, + ARRAY_SIZE(msm_snd_va_controls)); + if (ret < 0) { + dev_err(codec->dev, "%s: add_codec_controls for va failed, err %d\n", + __func__, ret); + return ret; + } + + snd_soc_dapm_new_controls(dapm, msm_va_dapm_widgets, + ARRAY_SIZE(msm_va_dapm_widgets)); + + snd_soc_dapm_ignore_suspend(dapm, "Digital Mic0"); + snd_soc_dapm_ignore_suspend(dapm, "Digital Mic1"); + snd_soc_dapm_ignore_suspend(dapm, "Digital Mic2"); + snd_soc_dapm_ignore_suspend(dapm, "Digital Mic3"); + snd_soc_dapm_ignore_suspend(dapm, "Digital Mic4"); + snd_soc_dapm_ignore_suspend(dapm, "Digital Mic5"); + snd_soc_dapm_ignore_suspend(dapm, "Digital Mic6"); + snd_soc_dapm_ignore_suspend(dapm, "Digital Mic7"); + + snd_soc_dapm_sync(dapm); + + card = rtd->card->snd_card; + if (!pdata->codec_root) + pdata->codec_root = snd_info_create_subdir(card->module, + "codecs", card->proc_root); + if (!pdata->codec_root) { + dev_dbg(codec->dev, "%s: Cannot create codecs module entry\n", + __func__); + ret = 0; + goto done; + } + bolero_info_create_codec_entry(pdata->codec_root, codec); +done: + return ret; +} + +static int msm_wsa_cdc_dma_init(struct snd_soc_pcm_runtime *rtd) +{ + int ret = 0; + struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + struct snd_soc_component *aux_comp; + struct snd_card *card; + struct msm_asoc_mach_data *pdata = + snd_soc_card_get_drvdata(rtd->card); + + ret = snd_soc_add_codec_controls(codec, msm_snd_wsa_controls, + ARRAY_SIZE(msm_snd_wsa_controls)); + if (ret < 0) { + dev_err(codec->dev, "%s: add_codec_controls for wsa failed, err %d\n", + __func__, ret); + return ret; + } + snd_soc_dapm_new_controls(dapm, msm_wsa_dapm_widgets, + ARRAY_SIZE(msm_wsa_dapm_widgets)); + + snd_soc_dapm_ignore_suspend(dapm, "WSA_SPK1 OUT"); + snd_soc_dapm_ignore_suspend(dapm, "WSA_SPK2 OUT"); + snd_soc_dapm_ignore_suspend(dapm, "WSA AIF VI"); + snd_soc_dapm_ignore_suspend(dapm, "VIINPUT_WSA"); + + snd_soc_dapm_sync(dapm); + + /* + * Send speaker configuration only for WSA8810. + * Default configuration is for WSA8815. + */ + dev_dbg(codec->dev, "%s: Number of aux devices: %d\n", + __func__, rtd->card->num_aux_devs); + if (rtd->card->num_aux_devs && + !list_empty(&rtd->card->component_dev_list)) { + aux_comp = list_first_entry( + &rtd->card->component_dev_list, + struct snd_soc_component, + card_aux_list); + if (!strcmp(aux_comp->name, WSA8810_NAME_1) || + !strcmp(aux_comp->name, WSA8810_NAME_2)) { + wsa_macro_set_spkr_mode(rtd->codec, + WSA_MACRO_SPKR_MODE_1); + wsa_macro_set_spkr_gain_offset(rtd->codec, + WSA_MACRO_GAIN_OFFSET_M1P5_DB); + } + } + card = rtd->card->snd_card; + if (!pdata->codec_root) + pdata->codec_root = snd_info_create_subdir(card->module, + "codecs", card->proc_root); + if (!pdata->codec_root) { + dev_dbg(codec->dev, "%s: Cannot create codecs module entry\n", + __func__); + ret = 0; + goto done; + } + bolero_info_create_codec_entry(pdata->codec_root, codec); +done: + return ret; +} + +static int msm_wcn_init(struct snd_soc_pcm_runtime *rtd) +{ + unsigned int rx_ch[WCN_CDC_SLIM_RX_CH_MAX] = {157, 158}; + unsigned int tx_ch[WCN_CDC_SLIM_TX_CH_MAX] = {159, 160, 161, 162}; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + + return snd_soc_dai_set_channel_map(codec_dai, ARRAY_SIZE(tx_ch), + tx_ch, ARRAY_SIZE(rx_ch), rx_ch); +} + +static int msm_snd_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai_link *dai_link = rtd->dai_link; + + int ret = 0; + u32 rx_ch[SLIM_MAX_RX_PORTS], tx_ch[SLIM_MAX_TX_PORTS]; + u32 rx_ch_cnt = 0, tx_ch_cnt = 0; + u32 user_set_tx_ch = 0; + u32 rx_ch_count; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + ret = snd_soc_dai_get_channel_map(codec_dai, + &tx_ch_cnt, tx_ch, &rx_ch_cnt, rx_ch); + if (ret < 0) { + pr_err("%s: failed to get codec chan map, err:%d\n", + __func__, ret); + goto err; + } + if (dai_link->id == MSM_BACKEND_DAI_SLIMBUS_5_RX) { + pr_debug("%s: rx_5_ch=%d\n", __func__, + slim_rx_cfg[5].channels); + rx_ch_count = slim_rx_cfg[5].channels; + } else if (dai_link->id == MSM_BACKEND_DAI_SLIMBUS_2_RX) { + pr_debug("%s: rx_2_ch=%d\n", __func__, + slim_rx_cfg[2].channels); + rx_ch_count = slim_rx_cfg[2].channels; + } else if (dai_link->id == MSM_BACKEND_DAI_SLIMBUS_6_RX) { + pr_debug("%s: rx_6_ch=%d\n", __func__, + slim_rx_cfg[6].channels); + rx_ch_count = slim_rx_cfg[6].channels; + } else { + pr_debug("%s: rx_0_ch=%d\n", __func__, + slim_rx_cfg[0].channels); + rx_ch_count = slim_rx_cfg[0].channels; + } + ret = snd_soc_dai_set_channel_map(cpu_dai, 0, 0, + rx_ch_count, rx_ch); + if (ret < 0) { + pr_err("%s: failed to set cpu chan map, err:%d\n", + __func__, ret); + goto err; + } + } else { + + pr_debug("%s: %s_tx_dai_id_%d_ch=%d\n", __func__, + codec_dai->name, codec_dai->id, user_set_tx_ch); + ret = snd_soc_dai_get_channel_map(codec_dai, + &tx_ch_cnt, tx_ch, &rx_ch_cnt, rx_ch); + if (ret < 0) { + pr_err("%s: failed to get tx codec chan map, err:%d\n", + __func__, ret); + goto err; + } + /* For _tx1 case */ + if (dai_link->id == MSM_BACKEND_DAI_SLIMBUS_0_TX) + user_set_tx_ch = slim_tx_cfg[0].channels; + /* For _tx3 case */ + else if (dai_link->id == MSM_BACKEND_DAI_SLIMBUS_1_TX) + user_set_tx_ch = slim_tx_cfg[1].channels; + else if (dai_link->id == MSM_BACKEND_DAI_SLIMBUS_4_TX) + user_set_tx_ch = msm_vi_feed_tx_ch; + else + user_set_tx_ch = tx_ch_cnt; + + pr_debug("%s: msm_slim_0_tx_ch(%d) user_set_tx_ch(%d) tx_ch_cnt(%d), BE id (%d)\n", + __func__, slim_tx_cfg[0].channels, user_set_tx_ch, + tx_ch_cnt, dai_link->id); + + ret = snd_soc_dai_set_channel_map(cpu_dai, + user_set_tx_ch, tx_ch, 0, 0); + if (ret < 0) + pr_err("%s: failed to set tx cpu chan map, err:%d\n", + __func__, ret); + } + +err: + return ret; +} + + +static int msm_snd_cdc_dma_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai_link *dai_link = rtd->dai_link; + + int ret = 0; + u32 rx_ch_cdc_dma, tx_ch_cdc_dma; + u32 rx_ch_cnt = 0, tx_ch_cnt = 0; + u32 user_set_tx_ch = 0; + u32 user_set_rx_ch = 0; + u32 ch_id; + + ret = snd_soc_dai_get_channel_map(codec_dai, + &tx_ch_cnt, &tx_ch_cdc_dma, &rx_ch_cnt, + &rx_ch_cdc_dma); + if (ret < 0) { + pr_err("%s: failed to get codec chan map, err:%d\n", + __func__, ret); + goto err; + } + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + switch (dai_link->id) { + case MSM_BACKEND_DAI_WSA_CDC_DMA_RX_0: + case MSM_BACKEND_DAI_WSA_CDC_DMA_RX_1: + { + ch_id = msm_cdc_dma_get_idx_from_beid(dai_link->id); + pr_debug("%s: id %d rx_ch=%d\n", __func__, + ch_id, cdc_dma_rx_cfg[ch_id].channels); + user_set_rx_ch = cdc_dma_rx_cfg[ch_id].channels; + ret = snd_soc_dai_set_channel_map(cpu_dai, 0, 0, + user_set_rx_ch, &rx_ch_cdc_dma); + if (ret < 0) { + pr_err("%s: failed to set cpu chan map, err:%d\n", + __func__, ret); + goto err; + } + + } + break; + } + } else { + switch (dai_link->id) { + case MSM_BACKEND_DAI_WSA_CDC_DMA_TX_0: + { + user_set_tx_ch = msm_vi_feed_tx_ch; + } + break; + case MSM_BACKEND_DAI_WSA_CDC_DMA_TX_1: + case MSM_BACKEND_DAI_WSA_CDC_DMA_TX_2: + case MSM_BACKEND_DAI_VA_CDC_DMA_TX_0: + case MSM_BACKEND_DAI_VA_CDC_DMA_TX_1: + { + ch_id = msm_cdc_dma_get_idx_from_beid(dai_link->id); + pr_debug("%s: id %d tx_ch=%d\n", __func__, + ch_id, cdc_dma_tx_cfg[ch_id].channels); + user_set_tx_ch = cdc_dma_tx_cfg[ch_id].channels; + } + break; + } + + ret = snd_soc_dai_set_channel_map(cpu_dai, user_set_tx_ch, + &tx_ch_cdc_dma, 0, 0); + if (ret < 0) { + pr_err("%s: failed to set cpu chan map, err:%d\n", + __func__, ret); + goto err; + } + } + +err: + return ret; +} + +static int msm_wcn_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai_link *dai_link = rtd->dai_link; + u32 rx_ch[WCN_CDC_SLIM_RX_CH_MAX], tx_ch[WCN_CDC_SLIM_TX_CH_MAX]; + u32 rx_ch_cnt = 0, tx_ch_cnt = 0; + int ret; + + dev_dbg(rtd->dev, "%s: %s_tx_dai_id_%d\n", __func__, + codec_dai->name, codec_dai->id); + ret = snd_soc_dai_get_channel_map(codec_dai, + &tx_ch_cnt, tx_ch, &rx_ch_cnt, rx_ch); + if (ret) { + dev_err(rtd->dev, + "%s: failed to get BTFM codec chan map\n, err:%d\n", + __func__, ret); + goto err; + } + + dev_dbg(rtd->dev, "%s: tx_ch_cnt(%d) BE id %d\n", + __func__, tx_ch_cnt, dai_link->id); + + ret = snd_soc_dai_set_channel_map(cpu_dai, + tx_ch_cnt, tx_ch, rx_ch_cnt, rx_ch); + if (ret) + dev_err(rtd->dev, "%s: failed to set cpu chan map, err:%d\n", + __func__, ret); + +err: + return ret; +} + +static int msm_get_port_id(int be_id) +{ + int afe_port_id; + + switch (be_id) { + case MSM_BACKEND_DAI_PRI_MI2S_RX: + afe_port_id = AFE_PORT_ID_PRIMARY_MI2S_RX; + break; + case MSM_BACKEND_DAI_PRI_MI2S_TX: + afe_port_id = AFE_PORT_ID_PRIMARY_MI2S_TX; + break; + case MSM_BACKEND_DAI_SECONDARY_MI2S_RX: + afe_port_id = AFE_PORT_ID_SECONDARY_MI2S_RX; + break; + case MSM_BACKEND_DAI_SECONDARY_MI2S_TX: + afe_port_id = AFE_PORT_ID_SECONDARY_MI2S_TX; + break; + case MSM_BACKEND_DAI_TERTIARY_MI2S_RX: + afe_port_id = AFE_PORT_ID_TERTIARY_MI2S_RX; + break; + case MSM_BACKEND_DAI_TERTIARY_MI2S_TX: + afe_port_id = AFE_PORT_ID_TERTIARY_MI2S_TX; + break; + case MSM_BACKEND_DAI_QUATERNARY_MI2S_RX: + afe_port_id = AFE_PORT_ID_QUATERNARY_MI2S_RX; + break; + case MSM_BACKEND_DAI_QUATERNARY_MI2S_TX: + afe_port_id = AFE_PORT_ID_QUATERNARY_MI2S_TX; + break; + case MSM_BACKEND_DAI_QUINARY_MI2S_RX: + afe_port_id = AFE_PORT_ID_QUINARY_MI2S_RX; + break; + case MSM_BACKEND_DAI_QUINARY_MI2S_TX: + afe_port_id = AFE_PORT_ID_QUINARY_MI2S_TX; + break; + case MSM_BACKEND_DAI_SENARY_MI2S_RX: + afe_port_id = AFE_PORT_ID_SENARY_MI2S_RX; + break; + case MSM_BACKEND_DAI_SENARY_MI2S_TX: + afe_port_id = AFE_PORT_ID_SENARY_MI2S_TX; + break; + default: + pr_err("%s: Invalid BE id: %d\n", __func__, be_id); + afe_port_id = -EINVAL; + } + + return afe_port_id; +} + +static u32 get_mi2s_bits_per_sample(u32 bit_format) +{ + u32 bit_per_sample; + + switch (bit_format) { + case SNDRV_PCM_FORMAT_S32_LE: + case SNDRV_PCM_FORMAT_S24_3LE: + case SNDRV_PCM_FORMAT_S24_LE: + bit_per_sample = 32; + break; + case SNDRV_PCM_FORMAT_S16_LE: + default: + bit_per_sample = 16; + break; + } + + return bit_per_sample; +} + +static void update_mi2s_clk_val(int dai_id, int stream) +{ + u32 bit_per_sample; + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + bit_per_sample = + get_mi2s_bits_per_sample(mi2s_rx_cfg[dai_id].bit_format); + mi2s_clk[dai_id].clk_freq_in_hz = + mi2s_rx_cfg[dai_id].sample_rate * 2 * bit_per_sample; + } else { + bit_per_sample = + get_mi2s_bits_per_sample(mi2s_tx_cfg[dai_id].bit_format); + mi2s_clk[dai_id].clk_freq_in_hz = + mi2s_tx_cfg[dai_id].sample_rate * 2 * bit_per_sample; + } +} + +static int msm_mi2s_set_sclk(struct snd_pcm_substream *substream, bool enable) +{ + int ret = 0; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + int port_id = 0; + int index = cpu_dai->id; + + port_id = msm_get_port_id(rtd->dai_link->id); + if (port_id < 0) { + dev_err(rtd->card->dev, "%s: Invalid port_id\n", __func__); + ret = port_id; + goto err; + } + + if (enable) { + update_mi2s_clk_val(index, substream->stream); + dev_dbg(rtd->card->dev, "%s: clock rate %ul\n", __func__, + mi2s_clk[index].clk_freq_in_hz); + } + + mi2s_clk[index].enable = enable; + ret = afe_set_lpass_clock_v2(port_id, + &mi2s_clk[index]); + if (ret < 0) { + dev_err(rtd->card->dev, + "%s: afe lpass clock failed for port 0x%x , err:%d\n", + __func__, port_id, ret); + goto err; + } + +err: + return ret; +} + + +static int msm_tdm_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + + if (cpu_dai->id == AFE_PORT_ID_QUATERNARY_TDM_RX) { + channels->min = channels->max = + tdm_rx_cfg[TDM_QUAT][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_QUAT][TDM_0].bit_format); + rate->min = rate->max = + tdm_rx_cfg[TDM_QUAT][TDM_0].sample_rate; + } else if (cpu_dai->id == AFE_PORT_ID_SECONDARY_TDM_RX) { + channels->min = channels->max = + tdm_rx_cfg[TDM_SEC][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_SEC][TDM_0].bit_format); + rate->min = rate->max = tdm_rx_cfg[TDM_SEC][TDM_0].sample_rate; + } else if (cpu_dai->id == AFE_PORT_ID_QUINARY_TDM_RX) { + channels->min = channels->max = + tdm_rx_cfg[TDM_QUIN][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_QUIN][TDM_0].bit_format); + rate->min = rate->max = tdm_rx_cfg[TDM_QUIN][TDM_0].sample_rate; + } else { + pr_err("%s: dai id 0x%x not supported\n", + __func__, cpu_dai->id); + return -EINVAL; + } + + pr_debug("%s: dai id = 0x%x channels = %d rate = %d format = 0x%x\n", + __func__, cpu_dai->id, channels->max, rate->max, + params_format(params)); + + return 0; +} + +static int qcs405_tdm_snd_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + int ret = 0; + int slot_width = 32; + int channels, slots = 8; + unsigned int slot_mask, rate, clk_freq; + unsigned int slot_offset[8] = {0, 4, 8, 12, 16, 20, 24, 28}; + + pr_debug("%s: dai id = 0x%x\n", __func__, cpu_dai->id); + + /* currently only supporting TDM_RX_0 and TDM_TX_0 */ + switch (cpu_dai->id) { + case AFE_PORT_ID_PRIMARY_TDM_RX: + channels = tdm_rx_cfg[TDM_PRI][TDM_0].channels; + break; + case AFE_PORT_ID_SECONDARY_TDM_RX: + channels = tdm_rx_cfg[TDM_SEC][TDM_0].channels; + break; + case AFE_PORT_ID_TERTIARY_TDM_RX: + channels = tdm_rx_cfg[TDM_TERT][TDM_0].channels; + break; + case AFE_PORT_ID_QUATERNARY_TDM_RX: + channels = tdm_rx_cfg[TDM_QUAT][TDM_0].channels; + break; + case AFE_PORT_ID_QUINARY_TDM_RX: + channels = tdm_rx_cfg[TDM_QUIN][TDM_0].channels; + break; + case AFE_PORT_ID_PRIMARY_TDM_TX: + channels = tdm_tx_cfg[TDM_PRI][TDM_0].channels; + break; + case AFE_PORT_ID_SECONDARY_TDM_TX: + channels = tdm_tx_cfg[TDM_SEC][TDM_0].channels; + break; + case AFE_PORT_ID_TERTIARY_TDM_TX: + channels = tdm_tx_cfg[TDM_TERT][TDM_0].channels; + break; + case AFE_PORT_ID_QUATERNARY_TDM_TX: + channels = tdm_tx_cfg[TDM_QUAT][TDM_0].channels; + break; + case AFE_PORT_ID_QUINARY_TDM_TX: + channels = tdm_tx_cfg[TDM_QUIN][TDM_0].channels; + break; + default: + pr_err("%s: dai id 0x%x not supported\n", + __func__, cpu_dai->id); + return -EINVAL; + } + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + /*2 slot config - bits 0 and 1 set for the first two slots */ + slot_mask = 0x0000FFFF >> (16-channels); + + pr_debug("%s: tdm rx slot_width %d slots %d\n", + __func__, slot_width, slots); + + ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0, slot_mask, + slots, slot_width); + if (ret < 0) { + pr_err("%s: failed to set tdm rx slot, err:%d\n", + __func__, ret); + goto end; + } + + ret = snd_soc_dai_set_channel_map(cpu_dai, + 0, NULL, channels, slot_offset); + if (ret < 0) { + pr_err("%s: failed to set tdm rx channel map, err:%d\n", + __func__, ret); + goto end; + } + } else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + /*2 slot config - bits 0 and 1 set for the first two slots */ + slot_mask = 0x0000FFFF >> (16-channels); + + pr_debug("%s: tdm tx slot_width %d slots %d\n", + __func__, slot_width, slots); + + ret = snd_soc_dai_set_tdm_slot(cpu_dai, slot_mask, 0, + slots, slot_width); + if (ret < 0) { + pr_err("%s: failed to set tdm tx slot, err:%d\n", + __func__, ret); + goto end; + } + + ret = snd_soc_dai_set_channel_map(cpu_dai, + channels, slot_offset, 0, NULL); + if (ret < 0) { + pr_err("%s: failed to set tdm tx channel map, err:%d\n", + __func__, ret); + goto end; + } + } else { + ret = -EINVAL; + pr_err("%s: invalid use case, err:%d\n", + __func__, ret); + goto end; + } + + rate = params_rate(params); + clk_freq = rate * slot_width * slots; + ret = snd_soc_dai_set_sysclk(cpu_dai, 0, clk_freq, SND_SOC_CLOCK_OUT); + if (ret < 0) + pr_err("%s: failed to set tdm clk, err:%d\n", + __func__, ret); + +end: + return ret; +} + +static int msm_get_tdm_mode(u32 port_id) +{ + u32 tdm_mode; + + switch (port_id) { + case AFE_PORT_ID_PRIMARY_TDM_RX: + case AFE_PORT_ID_PRIMARY_TDM_TX: + tdm_mode = TDM_PRI; + break; + case AFE_PORT_ID_SECONDARY_TDM_RX: + case AFE_PORT_ID_SECONDARY_TDM_TX: + tdm_mode = TDM_SEC; + break; + case AFE_PORT_ID_TERTIARY_TDM_RX: + case AFE_PORT_ID_TERTIARY_TDM_TX: + tdm_mode = TDM_TERT; + break; + case AFE_PORT_ID_QUATERNARY_TDM_RX: + case AFE_PORT_ID_QUATERNARY_TDM_TX: + tdm_mode = TDM_QUAT; + break; + case AFE_PORT_ID_QUINARY_TDM_RX: + case AFE_PORT_ID_QUINARY_TDM_TX: + tdm_mode = TDM_QUIN; + break; + default: + pr_err("%s: Invalid port id: %d\n", __func__, port_id); + tdm_mode = -EINVAL; + } + return tdm_mode; +} + +static int qcs405_tdm_snd_startup(struct snd_pcm_substream *substream) +{ + int ret = 0; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_card *card = rtd->card; + struct msm_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card); + u32 tdm_mode = msm_get_tdm_mode(cpu_dai->id); + + if (tdm_mode >= TDM_INTERFACE_MAX) { + ret = -EINVAL; + pr_err("%s: Invalid TDM interface %d\n", + __func__, ret); + return ret; + } + + if (pdata->mi2s_gpio_p[tdm_mode]) { + ret = msm_cdc_pinctrl_select_active_state( + pdata->mi2s_gpio_p[tdm_mode]); + if (ret) + pr_err("%s: TDM GPIO pinctrl set active failed with %d\n", + __func__, ret); + } + + /* Enable Mic bias for TDM Mics */ + if (cpu_dai->id == AFE_PORT_ID_QUINARY_TDM_TX) { + if (pdata->tdm_micb_supply) { + ret = regulator_set_voltage(pdata->tdm_micb_supply, + pdata->tdm_micb_voltage, + pdata->tdm_micb_voltage); + if (ret) { + pr_err("%s: Setting voltage failed, err = %d\n", + __func__, ret); + return ret; + } + ret = regulator_set_load(pdata->tdm_micb_supply, + pdata->tdm_micb_current); + if (ret) { + pr_err("%s: Setting current failed, err = %d\n", + __func__, ret); + return ret; + } + ret = regulator_enable(pdata->tdm_micb_supply); + if (ret) { + pr_err("%s: regulator enable failed, err = %d\n", + __func__, ret); + return ret; + } + } + } + + return ret; +} + +static void qcs405_tdm_snd_shutdown(struct snd_pcm_substream *substream) +{ + int ret = 0; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_card *card = rtd->card; + struct msm_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card); + u32 tdm_mode = msm_get_tdm_mode(cpu_dai->id); + + if (cpu_dai->id == AFE_PORT_ID_QUINARY_TDM_TX) { + if (pdata->tdm_micb_supply) { + ret = regulator_disable(pdata->tdm_micb_supply); + if (ret) + pr_err("%s: regulator disable failed, err = %d\n", + __func__, ret); + regulator_set_voltage(pdata->tdm_micb_supply, 0, + pdata->tdm_micb_voltage); + regulator_set_load(pdata->tdm_micb_supply, 0); + } + } + + if (pdata->mi2s_gpio_p[tdm_mode]) { + ret = msm_cdc_pinctrl_select_sleep_state( + pdata->mi2s_gpio_p[tdm_mode]); + if (ret) + pr_err("%s: TDM GPIO pinctrl set sleep failed with %d\n", + __func__, ret); + } +} + +static struct snd_soc_ops qcs405_tdm_be_ops = { + .hw_params = qcs405_tdm_snd_hw_params, + .startup = qcs405_tdm_snd_startup, + .shutdown = qcs405_tdm_snd_shutdown +}; + +static int msm_fe_qos_prepare(struct snd_pcm_substream *substream) +{ + cpumask_t mask; + + if (pm_qos_request_active(&substream->latency_pm_qos_req)) + pm_qos_remove_request(&substream->latency_pm_qos_req); + + cpumask_clear(&mask); + cpumask_set_cpu(1, &mask); /* affine to core 1 */ + cpumask_set_cpu(2, &mask); /* affine to core 2 */ + cpumask_copy(&substream->latency_pm_qos_req.cpus_affine, &mask); + + substream->latency_pm_qos_req.type = PM_QOS_REQ_AFFINE_CORES; + + pm_qos_add_request(&substream->latency_pm_qos_req, + PM_QOS_CPU_DMA_LATENCY, + MSM_LL_QOS_VALUE); + return 0; +} + +static struct snd_soc_ops msm_fe_qos_ops = { + .prepare = msm_fe_qos_prepare, +}; + + +static int msm_mi2s_snd_startup(struct snd_pcm_substream *substream) +{ + int ret = 0; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + + int index = cpu_dai->id; + unsigned int fmt = SND_SOC_DAIFMT_CBS_CFS; + struct snd_soc_card *card = rtd->card; + struct msm_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card); + + dev_dbg(rtd->card->dev, + "%s: substream = %s stream = %d, dai name %s, dai ID %d\n", + __func__, substream->name, substream->stream, + cpu_dai->name, cpu_dai->id); + + if (index < PRIM_MI2S || index >= MI2S_MAX) { + ret = -EINVAL; + dev_err(rtd->card->dev, + "%s: CPU DAI id (%d) out of range\n", + __func__, cpu_dai->id); + goto err; + } + /* + * Mutex protection in case the same MI2S + * interface using for both TX and RX so + * that the same clock won't be enable twice. + */ + mutex_lock(&mi2s_intf_conf[index].lock); + if (++mi2s_intf_conf[index].ref_cnt == 1) { + /* Check if msm needs to provide the clock to the interface */ + if (!mi2s_intf_conf[index].msm_is_mi2s_master) { + mi2s_clk[index].clk_id = mi2s_ebit_clk[index]; + fmt = SND_SOC_DAIFMT_CBM_CFM; + } + ret = msm_mi2s_set_sclk(substream, true); + if (ret < 0) { + dev_err(rtd->card->dev, + "%s: afe lpass clock failed to enable MI2S clock, err:%d\n", + __func__, ret); + goto clean_up; + } + + ret = snd_soc_dai_set_fmt(cpu_dai, fmt); + if (ret < 0) { + pr_err("%s: set fmt cpu dai failed for MI2S (%d), err:%d\n", + __func__, index, ret); + goto clk_off; + } + if (pdata->mi2s_gpio_p[index]) + msm_cdc_pinctrl_select_active_state( + pdata->mi2s_gpio_p[index]); + } +clk_off: + if (ret < 0) + msm_mi2s_set_sclk(substream, false); +clean_up: + if (ret < 0) + mi2s_intf_conf[index].ref_cnt--; + mutex_unlock(&mi2s_intf_conf[index].lock); +err: + return ret; +} + +static void msm_mi2s_snd_shutdown(struct snd_pcm_substream *substream) +{ + int ret; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + int index = rtd->cpu_dai->id; + struct snd_soc_card *card = rtd->card; + struct msm_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card); + + pr_debug("%s(): substream = %s stream = %d\n", __func__, + substream->name, substream->stream); + if (index < PRIM_MI2S || index >= MI2S_MAX) { + pr_err("%s:invalid MI2S DAI(%d)\n", __func__, index); + return; + } + + mutex_lock(&mi2s_intf_conf[index].lock); + if (--mi2s_intf_conf[index].ref_cnt == 0) { + if (pdata->mi2s_gpio_p[index]) + msm_cdc_pinctrl_select_sleep_state( + pdata->mi2s_gpio_p[index]); + + ret = msm_mi2s_set_sclk(substream, false); + if (ret < 0) + pr_err("%s:clock disable failed for MI2S (%d); ret=%d\n", + __func__, index, ret); + } + mutex_unlock(&mi2s_intf_conf[index].lock); +} + +static int msm_spdif_set_clk(struct snd_pcm_substream *substream, bool enable) +{ + int ret = 0; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + int port_id = cpu_dai->id; + struct afe_clk_set clk_cfg; + + clk_cfg.clk_set_minor_version = Q6AFE_LPASS_CLK_CONFIG_API_VERSION; + clk_cfg.clk_attri = Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO; + clk_cfg.clk_root = Q6AFE_LPASS_CLK_ROOT_DEFAULT; + clk_cfg.enable = enable; + + /* Set core clock (based on sample rate for RX, fixed for TX) */ + switch (port_id) { + case AFE_PORT_ID_PRIMARY_SPDIF_RX: + clk_cfg.clk_id = AFE_CLOCK_SET_CLOCK_ID_PRI_SPDIF_OUTPUT_CORE; + /* rate x 2ch x 2_for_biphase_coding x 32_bits_per_sample */ + clk_cfg.clk_freq_in_hz = + spdif_rx_cfg[PRIM_SPDIF_RX].sample_rate * 2 * 2 * 32; + break; + case AFE_PORT_ID_SECONDARY_SPDIF_RX: + clk_cfg.clk_id = AFE_CLOCK_SET_CLOCK_ID_SEC_SPDIF_OUTPUT_CORE; + clk_cfg.clk_freq_in_hz = + spdif_rx_cfg[SEC_SPDIF_RX].sample_rate * 2 * 2 * 32; + break; + case AFE_PORT_ID_PRIMARY_SPDIF_TX: + clk_cfg.clk_id = AFE_CLOCK_SET_CLOCK_ID_PRI_SPDIF_INPUT_CORE; + clk_cfg.clk_freq_in_hz = SPDIF_TX_CORE_CLK_163_P84_MHZ; + break; + case AFE_PORT_ID_SECONDARY_SPDIF_TX: + clk_cfg.clk_id = AFE_CLOCK_SET_CLOCK_ID_SEC_SPDIF_INPUT_CORE; + clk_cfg.clk_freq_in_hz = SPDIF_TX_CORE_CLK_163_P84_MHZ; + break; + } + + ret = afe_set_lpass_clock_v2(port_id, &clk_cfg); + if (ret < 0) { + dev_err(rtd->card->dev, + "%s: afe lpass clock failed for port 0x%x , err:%d\n", + __func__, port_id, ret); + goto err; + } + + /* Set NPL clock for RX in addition */ + switch (port_id) { + case AFE_PORT_ID_PRIMARY_SPDIF_RX: + clk_cfg.clk_id = AFE_CLOCK_SET_CLOCK_ID_PRI_SPDIF_OUTPUT_NPL; + + ret = afe_set_lpass_clock_v2(port_id, &clk_cfg); + if (ret < 0) { + dev_err(rtd->card->dev, + "%s: afe NPL failed port 0x%x, err:%d\n", + __func__, port_id, ret); + goto err; + } + break; + case AFE_PORT_ID_SECONDARY_SPDIF_RX: + clk_cfg.clk_id = AFE_CLOCK_SET_CLOCK_ID_SEC_SPDIF_OUTPUT_NPL; + + ret = afe_set_lpass_clock_v2(port_id, &clk_cfg); + if (ret < 0) { + dev_err(rtd->card->dev, + "%s: afe NPL failed for port 0x%x, err:%d\n", + __func__, port_id, ret); + goto err; + } + break; + } + + if (enable) { + dev_dbg(rtd->card->dev, "%s: clock rate %ul\n", __func__, + clk_cfg.clk_freq_in_hz); + } + +err: + return ret; +} + +static int msm_spdif_snd_startup(struct snd_pcm_substream *substream) +{ + int ret = 0; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + int port_id = cpu_dai->id; + + dev_dbg(rtd->card->dev, + "%s: substream = %s stream = %d, dai name %s, dai ID %d\n", + __func__, substream->name, substream->stream, + cpu_dai->name, cpu_dai->id); + + if (port_id < AFE_PORT_ID_PRIMARY_SPDIF_RX || + port_id > AFE_PORT_ID_SECONDARY_SPDIF_TX) { + ret = -EINVAL; + dev_err(rtd->card->dev, + "%s: CPU DAI id (%d) out of range\n", + __func__, cpu_dai->id); + goto err; + } + + ret = msm_spdif_set_clk(substream, true); + if (ret < 0) { + dev_err(rtd->card->dev, + "%s: afe lpass clock failed to enable (%d), err:%d\n", + __func__, port_id, ret); + } +err: + return ret; +} + +static void msm_spdif_snd_shutdown(struct snd_pcm_substream *substream) +{ + int ret; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + int port_id = rtd->cpu_dai->id; + + pr_debug("%s(): substream = %s stream = %d\n", __func__, + substream->name, substream->stream); + if (port_id < AFE_PORT_ID_PRIMARY_SPDIF_RX || + port_id > AFE_PORT_ID_SECONDARY_SPDIF_TX) { + pr_err("%s:invalid SPDIF DAI(%d)\n", __func__, port_id); + return; + } + + ret = msm_spdif_set_clk(substream, false); + if (ret < 0) + pr_err("%s:clock disable failed for SPDIF (%d); ret=%d\n", + __func__, port_id, ret); +} + +static struct snd_soc_ops msm_mi2s_be_ops = { + .startup = msm_mi2s_snd_startup, + .shutdown = msm_mi2s_snd_shutdown, +}; + +static struct snd_soc_ops msm_cdc_dma_be_ops = { + .hw_params = msm_snd_cdc_dma_hw_params, +}; + +static struct snd_soc_ops msm_be_ops = { + .hw_params = msm_snd_hw_params, +}; + +static struct snd_soc_ops msm_wcn_ops = { + .hw_params = msm_wcn_hw_params, +}; + +static struct snd_soc_ops msm_spdif_be_ops = { + .startup = msm_spdif_snd_startup, + .shutdown = msm_spdif_snd_shutdown, +}; + + +/* Digital audio interface glue - connects codec <---> CPU */ +static struct snd_soc_dai_link msm_common_dai_links[] = { + /* FrontEnd DAI Links */ + { + .name = MSM_DAILINK_NAME(Media1), + .stream_name = "MultiMedia1", + .cpu_dai_name = "MultiMedia1", + .platform_name = "msm-pcm-dsp.0", + .dynamic = 1, + .async_ops = ASYNC_DPCM_SND_SOC_PREPARE, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .id = MSM_FRONTEND_DAI_MULTIMEDIA1 + }, + { + .name = MSM_DAILINK_NAME(Media2), + .stream_name = "MultiMedia2", + .cpu_dai_name = "MultiMedia2", + .platform_name = "msm-pcm-dsp.0", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .id = MSM_FRONTEND_DAI_MULTIMEDIA2, + }, + { + .name = "VoiceMMode1", + .stream_name = "VoiceMMode1", + .cpu_dai_name = "VoiceMMode1", + .platform_name = "msm-pcm-voice", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 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", + .id = MSM_FRONTEND_DAI_VOICEMMODE1, + }, + { + .name = "MSM VoIP", + .stream_name = "VoIP", + .cpu_dai_name = "VoIP", + .platform_name = "msm-voip-dsp", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .id = MSM_FRONTEND_DAI_VOIP, + }, + { + .name = MSM_DAILINK_NAME(ULL), + .stream_name = "MultiMedia3", + .cpu_dai_name = "MultiMedia3", + .platform_name = "msm-pcm-dsp.2", + .dynamic = 1, + .async_ops = ASYNC_DPCM_SND_SOC_PREPARE, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .id = MSM_FRONTEND_DAI_MULTIMEDIA3, + }, + /* Hostless PCM purpose */ + { + .name = "SLIMBUS_0 Hostless", + .stream_name = "SLIMBUS_0 Hostless", + .cpu_dai_name = "SLIMBUS0_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + /* this dailink has playback support */ + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + { + .name = "MSM AFE-PCM RX", + .stream_name = "AFE-PROXY RX", + .cpu_dai_name = "msm-dai-q6-dev.241", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .platform_name = "msm-pcm-afe", + .dpcm_playback = 1, + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + }, + { + .name = "MSM AFE-PCM TX", + .stream_name = "AFE-PROXY TX", + .cpu_dai_name = "msm-dai-q6-dev.240", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .platform_name = "msm-pcm-afe", + .dpcm_capture = 1, + .ignore_suspend = 1, + }, + { + .name = MSM_DAILINK_NAME(Compress1), + .stream_name = "Compress1", + .cpu_dai_name = "MultiMedia4", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .async_ops = ASYNC_DPCM_SND_SOC_HW_PARAMS, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dainlink has playback support */ + .id = MSM_FRONTEND_DAI_MULTIMEDIA4, + }, + { + .name = "AUXPCM Hostless", + .stream_name = "AUXPCM Hostless", + .cpu_dai_name = "AUXPCM_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + { + .name = "SLIMBUS_1 Hostless", + .stream_name = "SLIMBUS_1 Hostless", + .cpu_dai_name = "SLIMBUS1_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + /* this dailink has playback support */ + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + { + .name = "SLIMBUS_3 Hostless", + .stream_name = "SLIMBUS_3 Hostless", + .cpu_dai_name = "SLIMBUS3_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + /* this dailink has playback support */ + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + { + .name = "SLIMBUS_4 Hostless", + .stream_name = "SLIMBUS_4 Hostless", + .cpu_dai_name = "SLIMBUS4_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + /* this dailink has playback support */ + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + { + .name = MSM_DAILINK_NAME(LowLatency), + .stream_name = "MultiMedia5", + .cpu_dai_name = "MultiMedia5", + .platform_name = "msm-pcm-dsp.1", + .dynamic = 1, + .async_ops = ASYNC_DPCM_SND_SOC_PREPARE, + .dpcm_playback = 1, + .dpcm_capture = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .id = MSM_FRONTEND_DAI_MULTIMEDIA5, + .ops = &msm_fe_qos_ops, + }, + { + .name = "Listen 1 Audio Service", + .stream_name = "Listen 1 Audio Service", + .cpu_dai_name = "LSM1", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = { SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST }, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .id = MSM_FRONTEND_DAI_LSM1, + }, + /* Multiple Tunnel instances */ + { + .name = MSM_DAILINK_NAME(Compress2), + .stream_name = "Compress2", + .cpu_dai_name = "MultiMedia7", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dainlink has playback support */ + .id = MSM_FRONTEND_DAI_MULTIMEDIA7, + }, + { + .name = MSM_DAILINK_NAME(MultiMedia10), + .stream_name = "MultiMedia10", + .cpu_dai_name = "MultiMedia10", + .platform_name = "msm-pcm-dsp.1", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dainlink has playback support */ + .id = MSM_FRONTEND_DAI_MULTIMEDIA10, + }, + { + .name = MSM_DAILINK_NAME(ULL_NOIRQ), + .stream_name = "MM_NOIRQ", + .cpu_dai_name = "MultiMedia8", + .platform_name = "msm-pcm-dsp-noirq", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dainlink has playback support */ + .id = MSM_FRONTEND_DAI_MULTIMEDIA8, + .ops = &msm_fe_qos_ops, + }, + /* HDMI Hostless */ + { + .name = "HDMI_RX_HOSTLESS", + .stream_name = "HDMI_RX_HOSTLESS", + .cpu_dai_name = "HDMI_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .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", + }, + { + .name = "VoiceMMode2", + .stream_name = "VoiceMMode2", + .cpu_dai_name = "VoiceMMode2", + .platform_name = "msm-pcm-voice", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 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", + .id = MSM_FRONTEND_DAI_VOICEMMODE2, + }, + /* LSM FE */ + { + .name = "Listen 2 Audio Service", + .stream_name = "Listen 2 Audio Service", + .cpu_dai_name = "LSM2", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = { SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST }, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .id = MSM_FRONTEND_DAI_LSM2, + }, + { + .name = "Listen 3 Audio Service", + .stream_name = "Listen 3 Audio Service", + .cpu_dai_name = "LSM3", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = { SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST }, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .id = MSM_FRONTEND_DAI_LSM3, + }, + { + .name = "Listen 4 Audio Service", + .stream_name = "Listen 4 Audio Service", + .cpu_dai_name = "LSM4", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = { SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST }, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .id = MSM_FRONTEND_DAI_LSM4, + }, + { + .name = "Listen 5 Audio Service", + .stream_name = "Listen 5 Audio Service", + .cpu_dai_name = "LSM5", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = { SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST }, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .id = MSM_FRONTEND_DAI_LSM5, + }, + { + .name = "Listen 6 Audio Service", + .stream_name = "Listen 6 Audio Service", + .cpu_dai_name = "LSM6", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = { SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST }, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .id = MSM_FRONTEND_DAI_LSM6, + }, + { + .name = "Listen 7 Audio Service", + .stream_name = "Listen 7 Audio Service", + .cpu_dai_name = "LSM7", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = { SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST }, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .id = MSM_FRONTEND_DAI_LSM7, + }, + { + .name = "Listen 8 Audio Service", + .stream_name = "Listen 8 Audio Service", + .cpu_dai_name = "LSM8", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = { SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST }, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .id = MSM_FRONTEND_DAI_LSM8, + }, + { + .name = MSM_DAILINK_NAME(Media9), + .stream_name = "MultiMedia9", + .cpu_dai_name = "MultiMedia9", + .platform_name = "msm-pcm-dsp.0", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .id = MSM_FRONTEND_DAI_MULTIMEDIA9, + }, + { + .name = MSM_DAILINK_NAME(Compress4), + .stream_name = "Compress4", + .cpu_dai_name = "MultiMedia11", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dainlink has playback support */ + .id = MSM_FRONTEND_DAI_MULTIMEDIA11, + }, + { + .name = MSM_DAILINK_NAME(Compress5), + .stream_name = "Compress5", + .cpu_dai_name = "MultiMedia12", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dainlink has playback support */ + .id = MSM_FRONTEND_DAI_MULTIMEDIA12, + }, + { + .name = MSM_DAILINK_NAME(Compress6), + .stream_name = "Compress6", + .cpu_dai_name = "MultiMedia13", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dainlink has playback support */ + .id = MSM_FRONTEND_DAI_MULTIMEDIA13, + }, + { + .name = MSM_DAILINK_NAME(Compress7), + .stream_name = "Compress7", + .cpu_dai_name = "MultiMedia14", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dainlink has playback support */ + .id = MSM_FRONTEND_DAI_MULTIMEDIA14, + }, + { + .name = MSM_DAILINK_NAME(Compress8), + .stream_name = "Compress8", + .cpu_dai_name = "MultiMedia15", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dainlink has playback support */ + .id = MSM_FRONTEND_DAI_MULTIMEDIA15, + }, + { + .name = MSM_DAILINK_NAME(ULL_NOIRQ_2), + .stream_name = "MM_NOIRQ_2", + .cpu_dai_name = "MultiMedia16", + .platform_name = "msm-pcm-dsp-noirq", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dainlink has playback support */ + .id = MSM_FRONTEND_DAI_MULTIMEDIA16, + }, + { + .name = "SLIMBUS_8 Hostless", + .stream_name = "SLIMBUS8_HOSTLESS Capture", + .cpu_dai_name = "SLIMBUS8_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + /* Hostless PCM purpose */ + { + .name = "CDC_DMA Hostless", + .stream_name = "CDC_DMA Hostless", + .cpu_dai_name = "CDC_DMA_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + /* this dailink has playback support */ + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, +}; + +static struct snd_soc_dai_link msm_bolero_fe_dai_links[] = { + { + .name = LPASS_BE_WSA_CDC_DMA_TX_0, + .stream_name = "WSA CDC DMA0 Capture", + .cpu_dai_name = "msm-dai-cdc-dma-dev.45057", + .platform_name = "msm-pcm-hostless", + .codec_name = "bolero_codec", + .codec_dai_name = "wsa_macro_vifeedback", + .id = MSM_BACKEND_DAI_WSA_CDC_DMA_TX_0, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ops = &msm_cdc_dma_be_ops, + }, +}; + +static struct snd_soc_dai_link msm_common_misc_fe_dai_links[] = { + { + .name = MSM_DAILINK_NAME(ASM Loopback), + .stream_name = "MultiMedia6", + .cpu_dai_name = "MultiMedia6", + .platform_name = "msm-pcm-loopback", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .ignore_suspend = 1, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_pmdown_time = 1, + .id = MSM_FRONTEND_DAI_MULTIMEDIA6, + }, + { + .name = "USB Audio Hostless", + .stream_name = "USB Audio Hostless", + .cpu_dai_name = "USBAUDIO_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 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", + }, + { + .name = "SLIMBUS_7 Hostless", + .stream_name = "SLIMBUS_7 Hostless", + .cpu_dai_name = "SLIMBUS7_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_capture = 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", + }, + { + .name = MSM_DAILINK_NAME(Compr Capture), + .stream_name = "Compr Capture", + .cpu_dai_name = "MultiMedia18", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_capture = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .ignore_pmdown_time = 1, + .id = MSM_FRONTEND_DAI_MULTIMEDIA18, + }, + { + .name = MSM_DAILINK_NAME(Transcode Loopback Playback), + .stream_name = "Transcode Loopback Playback", + .cpu_dai_name = "MultiMedia26", + .platform_name = "msm-transcode-loopback", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dailink has playback support */ + .id = MSM_FRONTEND_DAI_MULTIMEDIA26, + }, + { + .name = MSM_DAILINK_NAME(Transcode Loopback Capture), + .stream_name = "Transcode Loopback Capture", + .cpu_dai_name = "MultiMedia27", + .platform_name = "msm-transcode-loopback", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .id = MSM_FRONTEND_DAI_MULTIMEDIA27, + }, +}; + +static struct snd_soc_dai_link msm_common_be_dai_links[] = { + /* Backend AFE DAI Links */ + { + .name = LPASS_BE_AFE_PCM_RX, + .stream_name = "AFE Playback", + .cpu_dai_name = "msm-dai-q6-dev.224", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_AFE_PCM_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_AFE_PCM_TX, + .stream_name = "AFE Capture", + .cpu_dai_name = "msm-dai-q6-dev.225", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_AFE_PCM_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + }, + /* Incall Record Uplink BACK END DAI Link */ + { + .name = LPASS_BE_INCALL_RECORD_TX, + .stream_name = "Voice Uplink Capture", + .cpu_dai_name = "msm-dai-q6-dev.32772", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_INCALL_RECORD_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + }, + /* Incall Record Downlink BACK END DAI Link */ + { + .name = LPASS_BE_INCALL_RECORD_RX, + .stream_name = "Voice Downlink Capture", + .cpu_dai_name = "msm-dai-q6-dev.32771", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_INCALL_RECORD_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + }, + /* Incall Music BACK END DAI Link */ + { + .name = LPASS_BE_VOICE_PLAYBACK_TX, + .stream_name = "Voice Farend Playback", + .cpu_dai_name = "msm-dai-q6-dev.32773", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_VOICE_PLAYBACK_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + }, + /* Incall Music 2 BACK END DAI Link */ + { + .name = LPASS_BE_VOICE2_PLAYBACK_TX, + .stream_name = "Voice2 Farend Playback", + .cpu_dai_name = "msm-dai-q6-dev.32770", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_VOICE2_PLAYBACK_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + }, + { + .name = LPASS_BE_USB_AUDIO_RX, + .stream_name = "USB Audio Playback", + .cpu_dai_name = "msm-dai-q6-dev.28672", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_USB_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_USB_AUDIO_TX, + .stream_name = "USB Audio Capture", + .cpu_dai_name = "msm-dai-q6-dev.28673", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_USB_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_PRI_TDM_RX_0, + .stream_name = "Primary TDM0 Playback", + .cpu_dai_name = "msm-dai-q6-tdm.36864", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_PRI_TDM_RX_0, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &qcs405_tdm_be_ops, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + }, + { + .name = LPASS_BE_PRI_TDM_TX_0, + .stream_name = "Primary TDM0 Capture", + .cpu_dai_name = "msm-dai-q6-tdm.36865", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_PRI_TDM_TX_0, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &qcs405_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SEC_TDM_RX_0, + .stream_name = "Secondary TDM0 Playback", + .cpu_dai_name = "msm-dai-q6-tdm.36880", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SEC_TDM_RX_0, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &qcs405_tdm_be_ops, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + }, + { + .name = LPASS_BE_SEC_TDM_TX_0, + .stream_name = "Secondary TDM0 Capture", + .cpu_dai_name = "msm-dai-q6-tdm.36881", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_SEC_TDM_TX_0, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &qcs405_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_TERT_TDM_RX_0, + .stream_name = "Tertiary TDM0 Playback", + .cpu_dai_name = "msm-dai-q6-tdm.36896", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_TERT_TDM_RX_0, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &qcs405_tdm_be_ops, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + }, + { + .name = LPASS_BE_TERT_TDM_TX_0, + .stream_name = "Tertiary TDM0 Capture", + .cpu_dai_name = "msm-dai-q6-tdm.36897", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_TERT_TDM_TX_0, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &qcs405_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_QUAT_TDM_RX_0, + .stream_name = "Quaternary TDM0 Playback", + .cpu_dai_name = "msm-dai-q6-tdm.36912", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_QUAT_TDM_RX_0, + .be_hw_params_fixup = msm_tdm_be_hw_params_fixup, + .ops = &qcs405_tdm_be_ops, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + }, + { + .name = LPASS_BE_QUAT_TDM_TX_0, + .stream_name = "Quaternary TDM0 Capture", + .cpu_dai_name = "msm-dai-q6-tdm.36913", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_QUAT_TDM_TX_0, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &qcs405_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_QUIN_TDM_RX_0, + .stream_name = "Quinary TDM0 Playback", + .cpu_dai_name = "msm-dai-q6-tdm.36928", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_QUIN_TDM_RX_0, + .be_hw_params_fixup = msm_tdm_be_hw_params_fixup, + .ops = &qcs405_tdm_be_ops, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + }, + { + .name = LPASS_BE_QUIN_TDM_TX_0, + .stream_name = "Quinary TDM0 Capture", + .cpu_dai_name = "msm-dai-q6-tdm.36929", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_QUIN_TDM_TX_0, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &qcs405_tdm_be_ops, + .ignore_suspend = 1, + }, +}; + +static struct snd_soc_dai_link msm_tasha_be_dai_links[] = { + { + .name = LPASS_BE_SLIMBUS_0_RX, + .stream_name = "Slimbus Playback", + .cpu_dai_name = "msm-dai-q6-dev.16384", + .platform_name = "msm-pcm-routing", + .codec_name = "tasha_codec", + .codec_dai_name = "tasha_mix_rx1", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_0_RX, + .init = &msm_audrx_init, + .be_hw_params_fixup = msm_be_hw_params_fixup, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + .ops = &msm_be_ops, + }, + { + .name = LPASS_BE_SLIMBUS_0_TX, + .stream_name = "Slimbus Capture", + .cpu_dai_name = "msm-dai-q6-dev.16385", + .platform_name = "msm-pcm-routing", + .codec_name = "tasha_codec", + .codec_dai_name = "tasha_tx1", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_0_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + .ops = &msm_be_ops, + }, + { + .name = LPASS_BE_SLIMBUS_1_RX, + .stream_name = "Slimbus1 Playback", + .cpu_dai_name = "msm-dai-q6-dev.16386", + .platform_name = "msm-pcm-routing", + .codec_name = "tasha_codec", + .codec_dai_name = "tasha_mix_rx1", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_1_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_be_ops, + /* dai link has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_1_TX, + .stream_name = "Slimbus1 Capture", + .cpu_dai_name = "msm-dai-q6-dev.16387", + .platform_name = "msm-pcm-routing", + .codec_name = "tasha_codec", + .codec_dai_name = "tasha_tx3", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_1_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_2_RX, + .stream_name = "Slimbus2 Playback", + .cpu_dai_name = "msm-dai-q6-dev.16388", + .platform_name = "msm-pcm-routing", + .codec_name = "tasha_codec", + .codec_dai_name = "tasha_rx2", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_2_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_be_ops, + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_3_RX, + .stream_name = "Slimbus3 Playback", + .cpu_dai_name = "msm-dai-q6-dev.16390", + .platform_name = "msm-pcm-routing", + .codec_name = "tasha_codec", + .codec_dai_name = "tasha_mix_rx1", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_3_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_be_ops, + /* dai link has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_3_TX, + .stream_name = "Slimbus3 Capture", + .cpu_dai_name = "msm-dai-q6-dev.16391", + .platform_name = "msm-pcm-routing", + .codec_name = "tasha_codec", + .codec_dai_name = "tasha_tx1", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_3_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_4_RX, + .stream_name = "Slimbus4 Playback", + .cpu_dai_name = "msm-dai-q6-dev.16392", + .platform_name = "msm-pcm-routing", + .codec_name = "tasha_codec", + .codec_dai_name = "tasha_mix_rx1", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_4_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_be_ops, + /* dai link has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_5_RX, + .stream_name = "Slimbus5 Playback", + .cpu_dai_name = "msm-dai-q6-dev.16394", + .platform_name = "msm-pcm-routing", + .codec_name = "tasha_codec", + .codec_dai_name = "tasha_rx3", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_5_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_be_ops, + /* dai link has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_6_RX, + .stream_name = "Slimbus6 Playback", + .cpu_dai_name = "msm-dai-q6-dev.16396", + .platform_name = "msm-pcm-routing", + .codec_name = "tasha_codec", + .codec_dai_name = "tasha_rx4", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_6_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_be_ops, + /* dai link has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + /* Slimbus VI Recording */ + { + .name = LPASS_BE_SLIMBUS_TX_VI, + .stream_name = "Slimbus4 Capture", + .cpu_dai_name = "msm-dai-q6-dev.16393", + .platform_name = "msm-pcm-routing", + .codec_name = "tasha_codec", + .codec_dai_name = "tasha_vifeedback", + .id = MSM_BACKEND_DAI_SLIMBUS_4_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_be_ops, + .ignore_suspend = 1, + .no_pcm = 1, + .dpcm_capture = 1, + .ignore_pmdown_time = 1, + }, +}; + +static struct snd_soc_dai_link msm_wcn_be_dai_links[] = { + { + .name = LPASS_BE_SLIMBUS_7_RX, + .stream_name = "Slimbus7 Playback", + .cpu_dai_name = "msm-dai-q6-dev.16398", + .platform_name = "msm-pcm-routing", + .codec_name = "btfmslim_slave", + /* BT codec driver determines capabilities based on + * dai name, bt codecdai name should always contains + * supported usecase information + */ + .codec_dai_name = "btfm_bt_sco_a2dp_slim_rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_7_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_wcn_ops, + /* dai link has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_7_TX, + .stream_name = "Slimbus7 Capture", + .cpu_dai_name = "msm-dai-q6-dev.16399", + .platform_name = "msm-pcm-routing", + .codec_name = "btfmslim_slave", + .codec_dai_name = "btfm_bt_sco_slim_tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_7_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_wcn_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_8_TX, + .stream_name = "Slimbus8 Capture", + .cpu_dai_name = "msm-dai-q6-dev.16401", + .platform_name = "msm-pcm-routing", + .codec_name = "btfmslim_slave", + .codec_dai_name = "btfm_fm_slim_tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_8_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .init = &msm_wcn_init, + .ops = &msm_wcn_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_9_TX, + .stream_name = "Slimbus9 Capture", + .cpu_dai_name = "msm-dai-q6-dev.16403", + .platform_name = "msm-pcm-routing", + .codec_name = "btfmslim_slave", + .codec_dai_name = "btfm_bt_split_a2dp_slim_tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_9_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_wcn_ops, + .ignore_suspend = 1, + }, +}; + +static struct snd_soc_dai_link msm_mi2s_be_dai_links[] = { + { + .name = LPASS_BE_PRI_MI2S_RX, + .stream_name = "Primary MI2S Playback", + .cpu_dai_name = "msm-dai-q6-mi2s.0", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_PRI_MI2S_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + }, + { + .name = LPASS_BE_PRI_MI2S_TX, + .stream_name = "Primary MI2S Capture", + .cpu_dai_name = "msm-dai-q6-mi2s.0", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_PRI_MI2S_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SEC_MI2S_RX, + .stream_name = "Secondary MI2S Playback", + .cpu_dai_name = "msm-dai-q6-mi2s.1", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SECONDARY_MI2S_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + }, + { + .name = LPASS_BE_SEC_MI2S_TX, + .stream_name = "Secondary MI2S Capture", + .cpu_dai_name = "msm-dai-q6-mi2s.1", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_SECONDARY_MI2S_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_TERT_MI2S_RX, + .stream_name = "Tertiary MI2S Playback", + .cpu_dai_name = "msm-dai-q6-mi2s.2", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_TERTIARY_MI2S_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + }, + { + .name = LPASS_BE_TERT_MI2S_TX, + .stream_name = "Tertiary MI2S Capture", + .cpu_dai_name = "msm-dai-q6-mi2s.2", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_TERTIARY_MI2S_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_QUAT_MI2S_RX, + .stream_name = "Quaternary MI2S Playback", + .cpu_dai_name = "msm-dai-q6-mi2s.3", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_QUATERNARY_MI2S_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + }, + { + .name = LPASS_BE_QUAT_MI2S_TX, + .stream_name = "Quaternary MI2S Capture", + .cpu_dai_name = "msm-dai-q6-mi2s.3", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_QUIN_MI2S_RX, + .stream_name = "Quinary MI2S Playback", + .cpu_dai_name = "msm-dai-q6-mi2s.4", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_QUINARY_MI2S_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + }, + { + .name = LPASS_BE_QUIN_MI2S_TX, + .stream_name = "Quinary MI2S Capture", + .cpu_dai_name = "msm-dai-q6-mi2s.4", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_QUINARY_MI2S_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + }, + +}; + +static struct snd_soc_dai_link msm_auxpcm_be_dai_links[] = { + /* Primary AUX PCM Backend DAI Links */ + { + .name = LPASS_BE_AUXPCM_RX, + .stream_name = "AUX PCM Playback", + .cpu_dai_name = "msm-dai-q6-auxpcm.1", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_AUXPCM_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_AUXPCM_TX, + .stream_name = "AUX PCM Capture", + .cpu_dai_name = "msm-dai-q6-auxpcm.1", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_AUXPCM_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + }, + /* Secondary AUX PCM Backend DAI Links */ + { + .name = LPASS_BE_SEC_AUXPCM_RX, + .stream_name = "Sec AUX PCM Playback", + .cpu_dai_name = "msm-dai-q6-auxpcm.2", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SEC_AUXPCM_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SEC_AUXPCM_TX, + .stream_name = "Sec AUX PCM Capture", + .cpu_dai_name = "msm-dai-q6-auxpcm.2", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_SEC_AUXPCM_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + }, + /* Tertiary AUX PCM Backend DAI Links */ + { + .name = LPASS_BE_TERT_AUXPCM_RX, + .stream_name = "Tert AUX PCM Playback", + .cpu_dai_name = "msm-dai-q6-auxpcm.3", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_TERT_AUXPCM_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_TERT_AUXPCM_TX, + .stream_name = "Tert AUX PCM Capture", + .cpu_dai_name = "msm-dai-q6-auxpcm.3", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_TERT_AUXPCM_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + }, + /* Quaternary AUX PCM Backend DAI Links */ + { + .name = LPASS_BE_QUAT_AUXPCM_RX, + .stream_name = "Quat AUX PCM Playback", + .cpu_dai_name = "msm-dai-q6-auxpcm.4", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_QUAT_AUXPCM_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_QUAT_AUXPCM_TX, + .stream_name = "Quat AUX PCM Capture", + .cpu_dai_name = "msm-dai-q6-auxpcm.4", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_QUAT_AUXPCM_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + }, + /* Quinary AUX PCM Backend DAI Links */ + { + .name = LPASS_BE_QUIN_AUXPCM_RX, + .stream_name = "Quin AUX PCM Playback", + .cpu_dai_name = "msm-dai-q6-auxpcm.5", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_QUIN_AUXPCM_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_QUIN_AUXPCM_TX, + .stream_name = "Quin AUX PCM Capture", + .cpu_dai_name = "msm-dai-q6-auxpcm.5", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_QUIN_AUXPCM_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + }, +}; + +static struct snd_soc_dai_link msm_wsa_cdc_dma_be_dai_links[] = { + /* WSA CDC DMA Backend DAI Links */ + { + .name = LPASS_BE_WSA_CDC_DMA_RX_0, + .stream_name = "WSA CDC DMA0 Playback", + .cpu_dai_name = "msm-dai-cdc-dma-dev.45056", + .platform_name = "msm-pcm-routing", + .codec_name = "bolero_codec", + .codec_dai_name = "wsa_macro_rx1", + .no_pcm = 1, + .dpcm_playback = 1, + .init = &msm_wsa_cdc_dma_init, + .id = MSM_BACKEND_DAI_WSA_CDC_DMA_RX_0, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + .ops = &msm_cdc_dma_be_ops, + }, + { + .name = LPASS_BE_WSA_CDC_DMA_RX_1, + .stream_name = "WSA CDC DMA1 Playback", + .cpu_dai_name = "msm-dai-cdc-dma-dev.45058", + .platform_name = "msm-pcm-routing", + .codec_name = "bolero_codec", + .codec_dai_name = "wsa_macro_rx_mix", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_WSA_CDC_DMA_RX_1, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + .ops = &msm_cdc_dma_be_ops, + }, + { + .name = LPASS_BE_WSA_CDC_DMA_TX_1, + .stream_name = "WSA CDC DMA1 Capture", + .cpu_dai_name = "msm-dai-cdc-dma-dev.45059", + .platform_name = "msm-pcm-routing", + .codec_name = "bolero_codec", + .codec_dai_name = "wsa_macro_echo", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_WSA_CDC_DMA_TX_1, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + .ops = &msm_cdc_dma_be_ops, + }, +}; + +static struct snd_soc_dai_link msm_va_cdc_dma_be_dai_links[] = { + { + .name = LPASS_BE_VA_CDC_DMA_TX_0, + .stream_name = "VA CDC DMA0 Capture", + .cpu_dai_name = "msm-dai-cdc-dma-dev.45089", + .platform_name = "msm-pcm-routing", + .codec_name = "bolero_codec", + .codec_dai_name = "va_macro_tx1", + .no_pcm = 1, + .dpcm_capture = 1, + .init = &msm_va_cdc_dma_init, + .id = MSM_BACKEND_DAI_VA_CDC_DMA_TX_0, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + .ops = &msm_cdc_dma_be_ops, + }, + { + .name = LPASS_BE_VA_CDC_DMA_TX_1, + .stream_name = "VA CDC DMA1 Capture", + .cpu_dai_name = "msm-dai-cdc-dma-dev.45091", + .platform_name = "msm-pcm-routing", + .codec_name = "bolero_codec", + .codec_dai_name = "va_macro_tx2", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_VA_CDC_DMA_TX_1, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + .ops = &msm_cdc_dma_be_ops, + }, +}; + +static struct snd_soc_dai_link msm_spdif_be_dai_links[] = { + { + .name = LPASS_BE_PRI_SPDIF_RX, + .stream_name = "Primary SPDIF Playback", + .cpu_dai_name = "msm-dai-q6-spdif.20480", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_PRI_SPDIF_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_spdif_be_ops, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + }, + { + .name = LPASS_BE_PRI_SPDIF_TX, + .stream_name = "Primary SPDIF Capture", + .cpu_dai_name = "msm-dai-q6-spdif.20481", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_PRI_SPDIF_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_spdif_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SEC_SPDIF_RX, + .stream_name = "Secondary SPDIF Playback", + .cpu_dai_name = "msm-dai-q6-spdif.20482", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SEC_SPDIF_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_spdif_be_ops, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + }, + { + .name = LPASS_BE_SEC_SPDIF_TX, + .stream_name = "Secondary SPDIF Capture", + .cpu_dai_name = "msm-dai-q6-spdif.20483", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_SEC_SPDIF_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_spdif_be_ops, + .ignore_suspend = 1, + }, +}; + +static struct snd_soc_dai_link msm_qcs405_dai_links[ + ARRAY_SIZE(msm_common_dai_links) + + ARRAY_SIZE(msm_common_misc_fe_dai_links) + + ARRAY_SIZE(msm_common_be_dai_links) + + ARRAY_SIZE(msm_tasha_be_dai_links) + + ARRAY_SIZE(msm_wcn_be_dai_links) + + ARRAY_SIZE(msm_mi2s_be_dai_links) + + ARRAY_SIZE(msm_auxpcm_be_dai_links) + + ARRAY_SIZE(msm_va_cdc_dma_be_dai_links) + + ARRAY_SIZE(msm_wsa_cdc_dma_be_dai_links) + + ARRAY_SIZE(msm_bolero_fe_dai_links) + + ARRAY_SIZE(msm_spdif_be_dai_links)]; + +static int msm_snd_card_tasha_late_probe(struct snd_soc_card *card) +{ + int ret = 0; + + ret = audio_notifier_register("qcs405", AUDIO_NOTIFIER_ADSP_DOMAIN, + &service_nb); + if (ret < 0) + pr_err("%s: Audio notifier register failed ret = %d\n", + __func__, ret); + + return ret; +} + + +static int msm_snd_vad_cfg_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret = 0; + int port_id; + uint32_t vad_enable = ucontrol->value.integer.value[0]; + uint32_t preroll_config = ucontrol->value.integer.value[1]; + uint32_t vad_intf = ucontrol->value.integer.value[2]; + + if ((preroll_config < 0) || (preroll_config > 1000) || + (vad_enable < 0) || (vad_enable > 1) || + (vad_intf > MSM_BACKEND_DAI_MAX)) { + pr_err("%s: Invalid arguments\n", __func__); + ret = -EINVAL; + goto done; + } + + pr_debug("%s: vad_enable=%d preroll_config=%d vad_intf=%d\n", __func__, + vad_enable, preroll_config, vad_intf); + + ret = msm_vad_get_portid_from_beid(vad_intf, &port_id); + if (ret) { + pr_err("%s: Invalid vad interface\n", __func__); + goto done; + } + + afe_set_vad_cfg(vad_enable, preroll_config, port_id); + +done: + return ret; +} + +static int msm_snd_card_codec_late_probe(struct snd_soc_card *card) +{ + int ret = 0; + uint32_t tasha_codec = 0; + + ret = afe_cal_init_hwdep(card); + if (ret) { + dev_err(card->dev, "afe cal hwdep init failed (%d)\n", ret); + ret = 0; + } + + /* tasha late probe when it is present */ + ret = of_property_read_u32(card->dev->of_node, "qcom,tasha-codec", + &tasha_codec); + if (ret) { + dev_err(card->dev, "%s: No DT match tasha codec\n", __func__); + ret = 0; + } else { + if (tasha_codec) { + ret = msm_snd_card_tasha_late_probe(card); + if (ret) + dev_err(card->dev, "%s: tasha late probe err\n", + __func__); + } + } + return ret; +} + +struct snd_soc_card snd_soc_card_qcs405_msm = { + .name = "qcs405-snd-card", + .controls = msm_snd_controls, + .num_controls = ARRAY_SIZE(msm_snd_controls), + .late_probe = msm_snd_card_codec_late_probe, +}; + +static int msm_populate_dai_link_component_of_node( + struct snd_soc_card *card) +{ + int i, index, ret = 0; + struct device *cdev = card->dev; + struct snd_soc_dai_link *dai_link = card->dai_link; + struct device_node *np; + + if (!cdev) { + pr_err("%s: Sound card device memory NULL\n", __func__); + return -ENODEV; + } + + for (i = 0; i < card->num_links; i++) { + if (dai_link[i].platform_of_node && dai_link[i].cpu_of_node) + continue; + + /* populate platform_of_node for snd card dai links */ + if (dai_link[i].platform_name && + !dai_link[i].platform_of_node) { + index = of_property_match_string(cdev->of_node, + "asoc-platform-names", + dai_link[i].platform_name); + if (index < 0) { + pr_err("%s: No match found for platform name: %s\n", + __func__, dai_link[i].platform_name); + ret = index; + goto err; + } + np = of_parse_phandle(cdev->of_node, "asoc-platform", + index); + if (!np) { + pr_err("%s: retrieving phandle for platform %s, index %d failed\n", + __func__, dai_link[i].platform_name, + index); + ret = -ENODEV; + goto err; + } + dai_link[i].platform_of_node = np; + dai_link[i].platform_name = NULL; + } + + /* populate cpu_of_node for snd card dai links */ + if (dai_link[i].cpu_dai_name && !dai_link[i].cpu_of_node) { + index = of_property_match_string(cdev->of_node, + "asoc-cpu-names", + dai_link[i].cpu_dai_name); + if (index >= 0) { + np = of_parse_phandle(cdev->of_node, "asoc-cpu", + index); + if (!np) { + pr_err("%s: retrieving phandle for cpu dai %s failed\n", + __func__, + dai_link[i].cpu_dai_name); + ret = -ENODEV; + goto err; + } + dai_link[i].cpu_of_node = np; + dai_link[i].cpu_dai_name = NULL; + } + } + + /* populate codec_of_node for snd card dai links */ + if (dai_link[i].codec_name && !dai_link[i].codec_of_node) { + index = of_property_match_string(cdev->of_node, + "asoc-codec-names", + dai_link[i].codec_name); + if (index < 0) + continue; + np = of_parse_phandle(cdev->of_node, "asoc-codec", + index); + if (!np) { + pr_err("%s: retrieving phandle for codec %s failed\n", + __func__, dai_link[i].codec_name); + ret = -ENODEV; + goto err; + } + dai_link[i].codec_of_node = np; + dai_link[i].codec_name = NULL; + } + } + +err: + return ret; +} + +static struct snd_soc_dai_link msm_stub_fe_dai_links[] = { + + /* FrontEnd DAI Links */ + { + .name = "MSMSTUB Media1", + .stream_name = "MultiMedia1", + .cpu_dai_name = "MultiMedia1", + .platform_name = "msm-pcm-dsp.0", + .dynamic = 1, + .async_ops = ASYNC_DPCM_SND_SOC_PREPARE, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .id = MSM_FRONTEND_DAI_MULTIMEDIA1 + }, +}; + +static struct snd_soc_dai_link msm_stub_be_dai_links[] = { + + /* Backend DAI Links */ + { + .name = LPASS_BE_VA_CDC_DMA_TX_0, + .stream_name = "VA CDC DMA0 Capture", + .cpu_dai_name = "msm-dai-cdc-dma-dev.45089", + .platform_name = "msm-pcm-routing", + .codec_name = "bolero_codec", + .codec_dai_name = "va_macro_tx1", + .no_pcm = 1, + .dpcm_capture = 1, + .init = &msm_va_cdc_dma_init, + .id = MSM_BACKEND_DAI_VA_CDC_DMA_TX_0, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + .ops = &msm_cdc_dma_be_ops, + }, + { + .name = LPASS_BE_VA_CDC_DMA_TX_1, + .stream_name = "VA CDC DMA1 Capture", + .cpu_dai_name = "msm-dai-cdc-dma-dev.45091", + .platform_name = "msm-pcm-routing", + .codec_name = "bolero_codec", + .codec_dai_name = "va_macro_tx2", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_VA_CDC_DMA_TX_1, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + .ops = &msm_cdc_dma_be_ops, + }, +}; + +static struct snd_soc_dai_link msm_stub_dai_links[ + ARRAY_SIZE(msm_stub_fe_dai_links) + + ARRAY_SIZE(msm_stub_be_dai_links)]; + +struct snd_soc_card snd_soc_card_stub_msm = { + .name = "qcs405-stub-snd-card", +}; + +static const struct of_device_id qcs405_asoc_machine_of_match[] = { + { .compatible = "qcom,qcs405-asoc-snd", + .data = "codec"}, + { .compatible = "qcom,qcs405-asoc-snd-stub", + .data = "stub_codec"}, + {}, +}; + +static struct snd_soc_card *populate_snd_card_dailinks(struct device *dev) +{ + struct snd_soc_card *card = NULL; + struct snd_soc_dai_link *dailink; + int total_links = 0; + uint32_t tasha_codec = 0, auxpcm_audio_intf = 0; + uint32_t va_bolero_codec = 0, wsa_bolero_codec = 0, mi2s_audio_intf = 0; + uint32_t spdif_audio_intf = 0, wcn_audio_intf = 0; + const struct of_device_id *match; + char __iomem *spdif_cfg, *spdif_pin_ctl; + int rc = 0; + + match = of_match_node(qcs405_asoc_machine_of_match, dev->of_node); + if (!match) { + dev_err(dev, "%s: No DT match found for sound card\n", + __func__); + return NULL; + } + + if (!strcmp(match->data, "codec")) { + card = &snd_soc_card_qcs405_msm; + memcpy(msm_qcs405_dai_links + total_links, + msm_common_dai_links, + sizeof(msm_common_dai_links)); + + total_links += ARRAY_SIZE(msm_common_dai_links); + + rc = of_property_read_u32(dev->of_node, "qcom,wsa-bolero-codec", + &wsa_bolero_codec); + if (rc) { + dev_dbg(dev, "%s: No DT match WSA Macro codec\n", + __func__); + } else { + if (wsa_bolero_codec) { + dev_dbg(dev, "%s(): WSA macro in bolero codec present\n", + __func__); + + memcpy(msm_qcs405_dai_links + total_links, + msm_bolero_fe_dai_links, + sizeof(msm_bolero_fe_dai_links)); + total_links += + ARRAY_SIZE(msm_bolero_fe_dai_links); + } + } + + memcpy(msm_qcs405_dai_links + total_links, + msm_common_misc_fe_dai_links, + sizeof(msm_common_misc_fe_dai_links)); + + total_links += ARRAY_SIZE(msm_common_misc_fe_dai_links); + + memcpy(msm_qcs405_dai_links + total_links, + msm_common_be_dai_links, + sizeof(msm_common_be_dai_links)); + + total_links += ARRAY_SIZE(msm_common_be_dai_links); + + rc = of_property_read_u32(dev->of_node, "qcom,tasha-codec", + &tasha_codec); + if (rc) { + dev_dbg(dev, "%s: No DT match tasha codec\n", + __func__); + } else { + if (tasha_codec) { + memcpy(msm_qcs405_dai_links + total_links, + msm_tasha_be_dai_links, + sizeof(msm_tasha_be_dai_links)); + total_links += + ARRAY_SIZE(msm_tasha_be_dai_links); + } + } + + rc = of_property_read_u32(dev->of_node, "qcom,va-bolero-codec", + &va_bolero_codec); + if (rc) { + dev_dbg(dev, "%s: No DT match VA Macro codec\n", + __func__); + } else { + if (va_bolero_codec) { + dev_dbg(dev, "%s(): VA macro in bolero codec present\n", + __func__); + + memcpy(msm_qcs405_dai_links + total_links, + msm_va_cdc_dma_be_dai_links, + sizeof(msm_va_cdc_dma_be_dai_links)); + total_links += + ARRAY_SIZE(msm_va_cdc_dma_be_dai_links); + } + } + + if (wsa_bolero_codec) { + dev_dbg(dev, "%s(): WSAmacro in bolero codec present\n", + __func__); + + memcpy(msm_qcs405_dai_links + total_links, + msm_wsa_cdc_dma_be_dai_links, + sizeof(msm_wsa_cdc_dma_be_dai_links)); + total_links += + ARRAY_SIZE(msm_wsa_cdc_dma_be_dai_links); + } + + rc = of_property_read_u32(dev->of_node, "qcom,mi2s-audio-intf", + &mi2s_audio_intf); + if (rc) { + dev_dbg(dev, "%s: No DT match MI2S audio interface\n", + __func__); + } else { + if (mi2s_audio_intf) { + memcpy(msm_qcs405_dai_links + total_links, + msm_mi2s_be_dai_links, + sizeof(msm_mi2s_be_dai_links)); + total_links += + ARRAY_SIZE(msm_mi2s_be_dai_links); + } + } + rc = of_property_read_u32(dev->of_node, + "qcom,auxpcm-audio-intf", + &auxpcm_audio_intf); + if (rc) { + dev_dbg(dev, "%s: No DT match Aux PCM interface\n", + __func__); + } else { + if (auxpcm_audio_intf) { + memcpy(msm_qcs405_dai_links + total_links, + msm_auxpcm_be_dai_links, + sizeof(msm_auxpcm_be_dai_links)); + total_links += + ARRAY_SIZE(msm_auxpcm_be_dai_links); + } + } + rc = of_property_read_u32(dev->of_node, "qcom,spdif-audio-intf", + &spdif_audio_intf); + if (rc) { + dev_dbg(dev, "%s: No DT match SPDIF audio interface\n", + __func__); + } else { + if (spdif_audio_intf) { + memcpy(msm_qcs405_dai_links + total_links, + msm_spdif_be_dai_links, + sizeof(msm_spdif_be_dai_links)); + total_links += + ARRAY_SIZE(msm_spdif_be_dai_links); + + /* enable spdif coax pins */ + spdif_cfg = ioremap(TLMM_EAST_SPARE, 0x4); + spdif_pin_ctl = + ioremap(TLMM_SPDIF_HDMI_ARC_CTL, 0x4); + iowrite32(0xc0, spdif_cfg); + iowrite32(0x2220, spdif_pin_ctl); + } + } + rc = of_property_read_u32(dev->of_node, "qcom,wcn-btfm", + &wcn_audio_intf); + if (rc) { + dev_dbg(dev, "%s: No DT match WCN audio interface\n", + __func__); + } else { + if (wcn_audio_intf) { + memcpy(msm_qcs405_dai_links + total_links, + msm_wcn_be_dai_links, + sizeof(msm_wcn_be_dai_links)); + total_links += + ARRAY_SIZE(msm_wcn_be_dai_links); + } + } + dailink = msm_qcs405_dai_links; + } else if (!strcmp(match->data, "stub_codec")) { + card = &snd_soc_card_stub_msm; + + memcpy(msm_stub_dai_links + total_links, + msm_stub_fe_dai_links, + sizeof(msm_stub_fe_dai_links)); + total_links += ARRAY_SIZE(msm_stub_fe_dai_links); + + memcpy(msm_stub_dai_links + total_links, + msm_stub_be_dai_links, + sizeof(msm_stub_be_dai_links)); + total_links += ARRAY_SIZE(msm_stub_be_dai_links); + + dailink = msm_stub_dai_links; + } + + if (card) { + card->dai_link = dailink; + card->num_links = total_links; + } + + return card; +} + +static int msm_wsa881x_init(struct snd_soc_component *component) +{ + u8 spkleft_ports[WSA881X_MAX_SWR_PORTS] = {0, 1, 2, 3}; + u8 spkright_ports[WSA881X_MAX_SWR_PORTS] = {0, 1, 2, 3}; + u8 spkleft_port_types[WSA881X_MAX_SWR_PORTS] = {SPKR_L, SPKR_L_COMP, + SPKR_L_BOOST, SPKR_L_VI}; + u8 spkright_port_types[WSA881X_MAX_SWR_PORTS] = {SPKR_R, SPKR_R_COMP, + SPKR_R_BOOST, SPKR_R_VI}; + unsigned int ch_rate[WSA881X_MAX_SWR_PORTS] = {2400, 600, 300, 1200}; + unsigned int ch_mask[WSA881X_MAX_SWR_PORTS] = {0x1, 0xF, 0x3, 0x3}; + struct snd_soc_codec *codec = snd_soc_component_to_codec(component); + struct msm_asoc_mach_data *pdata; + struct snd_soc_dapm_context *dapm; + int ret = 0; + + if (!codec) { + pr_err("%s codec is NULL\n", __func__); + return -EINVAL; + } + + dapm = snd_soc_codec_get_dapm(codec); + + if (!strcmp(component->name_prefix, "SpkrLeft")) { + dev_dbg(codec->dev, "%s: setting left ch map to codec %s\n", + __func__, codec->component.name); + wsa881x_set_channel_map(codec, &spkleft_ports[0], + WSA881X_MAX_SWR_PORTS, &ch_mask[0], + &ch_rate[0], &spkleft_port_types[0]); + if (dapm->component) { + snd_soc_dapm_ignore_suspend(dapm, "SpkrLeft IN"); + snd_soc_dapm_ignore_suspend(dapm, "SpkrLeft SPKR"); + } + } else if (!strcmp(component->name_prefix, "SpkrRight")) { + dev_dbg(codec->dev, "%s: setting right ch map to codec %s\n", + __func__, codec->component.name); + wsa881x_set_channel_map(codec, &spkright_ports[0], + WSA881X_MAX_SWR_PORTS, &ch_mask[0], + &ch_rate[0], &spkright_port_types[0]); + if (dapm->component) { + snd_soc_dapm_ignore_suspend(dapm, "SpkrRight IN"); + snd_soc_dapm_ignore_suspend(dapm, "SpkrRight SPKR"); + } + } else { + dev_err(codec->dev, "%s: wrong codec name %s\n", __func__, + codec->component.name); + ret = -EINVAL; + goto err; + } + pdata = snd_soc_card_get_drvdata(component->card); + if (pdata && pdata->codec_root) + wsa881x_codec_info_create_codec_entry(pdata->codec_root, + codec); + +err: + return ret; +} + +static int msm_init_wsa_dev(struct platform_device *pdev, + struct snd_soc_card *card) +{ + struct device_node *wsa_of_node; + u32 wsa_max_devs; + u32 wsa_dev_cnt; + int i; + struct msm_wsa881x_dev_info *wsa881x_dev_info; + const char *wsa_auxdev_name_prefix[1]; + char *dev_name_str = NULL; + int found = 0; + int ret = 0; + + /* Get maximum WSA device count for this platform */ + ret = of_property_read_u32(pdev->dev.of_node, + "qcom,wsa-max-devs", &wsa_max_devs); + if (ret) { + dev_info(&pdev->dev, + "%s: wsa-max-devs property missing in DT %s, ret = %d\n", + __func__, pdev->dev.of_node->full_name, ret); + card->num_aux_devs = 0; + return 0; + } + if (wsa_max_devs == 0) { + dev_warn(&pdev->dev, + "%s: Max WSA devices is 0 for this target?\n", + __func__); + card->num_aux_devs = 0; + return 0; + } + + /* Get count of WSA device phandles for this platform */ + wsa_dev_cnt = of_count_phandle_with_args(pdev->dev.of_node, + "qcom,wsa-devs", NULL); + if (wsa_dev_cnt == -ENOENT) { + dev_warn(&pdev->dev, "%s: No wsa device defined in DT.\n", + __func__); + goto err; + } else if (wsa_dev_cnt <= 0) { + dev_err(&pdev->dev, + "%s: Error reading wsa device from DT. wsa_dev_cnt = %d\n", + __func__, wsa_dev_cnt); + ret = -EINVAL; + goto err; + } + + /* + * Expect total phandles count to be NOT less than maximum possible + * WSA count. However, if it is less, then assign same value to + * max count as well. + */ + if (wsa_dev_cnt < wsa_max_devs) { + dev_dbg(&pdev->dev, + "%s: wsa_max_devs = %d cannot exceed wsa_dev_cnt = %d\n", + __func__, wsa_max_devs, wsa_dev_cnt); + wsa_max_devs = wsa_dev_cnt; + } + + /* Make sure prefix string passed for each WSA device */ + ret = of_property_count_strings(pdev->dev.of_node, + "qcom,wsa-aux-dev-prefix"); + if (ret != wsa_dev_cnt) { + dev_err(&pdev->dev, + "%s: expecting %d wsa prefix. Defined only %d in DT\n", + __func__, wsa_dev_cnt, ret); + ret = -EINVAL; + goto err; + } + + /* + * Alloc mem to store phandle and index info of WSA device, if already + * registered with ALSA core + */ + wsa881x_dev_info = devm_kcalloc(&pdev->dev, wsa_max_devs, + sizeof(struct msm_wsa881x_dev_info), + GFP_KERNEL); + if (!wsa881x_dev_info) { + ret = -ENOMEM; + goto err; + } + + /* + * search and check whether all WSA devices are already + * registered with ALSA core or not. If found a node, store + * the node and the index in a local array of struct for later + * use. + */ + for (i = 0; i < wsa_dev_cnt; i++) { + wsa_of_node = of_parse_phandle(pdev->dev.of_node, + "qcom,wsa-devs", i); + if (unlikely(!wsa_of_node)) { + /* we should not be here */ + dev_err(&pdev->dev, + "%s: wsa dev node is not present\n", + __func__); + ret = -EINVAL; + goto err_free_dev_info; + } + if (soc_find_component(wsa_of_node, NULL)) { + /* WSA device registered with ALSA core */ + wsa881x_dev_info[found].of_node = wsa_of_node; + wsa881x_dev_info[found].index = i; + found++; + if (found == wsa_max_devs) + break; + } + } + + if (found < wsa_max_devs) { + dev_err(&pdev->dev, + "%s: failed to find %d components. Found only %d\n", + __func__, wsa_max_devs, found); + return -EPROBE_DEFER; + } + dev_info(&pdev->dev, + "%s: found %d wsa881x devices registered with ALSA core\n", + __func__, found); + + card->num_aux_devs = wsa_max_devs; + card->num_configs = wsa_max_devs; + + /* Alloc array of AUX devs struct */ + msm_aux_dev = devm_kcalloc(&pdev->dev, card->num_aux_devs, + sizeof(struct snd_soc_aux_dev), + GFP_KERNEL); + if (!msm_aux_dev) { + ret = -ENOMEM; + goto err_free_dev_info; + } + + /* Alloc array of codec conf struct */ + msm_codec_conf = devm_kcalloc(&pdev->dev, card->num_aux_devs, + sizeof(struct snd_soc_codec_conf), + GFP_KERNEL); + if (!msm_codec_conf) { + ret = -ENOMEM; + goto err_free_aux_dev; + } + + for (i = 0; i < card->num_aux_devs; i++) { + dev_name_str = devm_kzalloc(&pdev->dev, DEV_NAME_STR_LEN, + GFP_KERNEL); + if (!dev_name_str) { + ret = -ENOMEM; + goto err_free_cdc_conf; + } + + ret = of_property_read_string_index(pdev->dev.of_node, + "qcom,wsa-aux-dev-prefix", + wsa881x_dev_info[i].index, + wsa_auxdev_name_prefix); + if (ret) { + dev_err(&pdev->dev, + "%s: failed to read wsa aux dev prefix, ret = %d\n", + __func__, ret); + ret = -EINVAL; + goto err_free_dev_name_str; + } + + snprintf(dev_name_str, strlen("wsa881x.%d"), "wsa881x.%d", i); + msm_aux_dev[i].name = dev_name_str; + msm_aux_dev[i].codec_name = NULL; + msm_aux_dev[i].codec_of_node = + wsa881x_dev_info[i].of_node; + msm_aux_dev[i].init = msm_wsa881x_init; + msm_codec_conf[i].dev_name = NULL; + msm_codec_conf[i].name_prefix = wsa_auxdev_name_prefix[0]; + msm_codec_conf[i].of_node = + wsa881x_dev_info[i].of_node; + } + card->codec_conf = msm_codec_conf; + card->aux_dev = msm_aux_dev; + + return 0; + +err_free_dev_name_str: + devm_kfree(&pdev->dev, dev_name_str); +err_free_cdc_conf: + devm_kfree(&pdev->dev, msm_codec_conf); +err_free_aux_dev: + devm_kfree(&pdev->dev, msm_aux_dev); +err_free_dev_info: + devm_kfree(&pdev->dev, wsa881x_dev_info); +err: + return ret; +} + +static int msm_csra66x0_init(struct snd_soc_component *component) +{ + struct snd_soc_codec *codec = snd_soc_component_to_codec(component); + + if (!codec) { + pr_err("%s codec is NULL\n", __func__); + return -EINVAL; + } + return 0; +} + +static int msm_init_csra_dev(struct platform_device *pdev, + struct snd_soc_card *card) +{ + struct device_node *csra_of_node; + u32 csra_max_devs; + u32 csra_dev_cnt; + char *dev_name_str = NULL; + struct msm_csra66x0_dev_info *csra66x0_dev_info; + const char *csra_auxdev_name_prefix[1]; + int i; + int found = 0; + int ret = 0; + + /* Get maximum CSRA device count for this platform */ + ret = of_property_read_u32(pdev->dev.of_node, + "qcom,csra-max-devs", &csra_max_devs); + if (ret) { + dev_info(&pdev->dev, + "%s: csra-max-devs property missing in DT %s, ret = %d\n", + __func__, pdev->dev.of_node->full_name, ret); + card->num_aux_devs = 0; + return 0; + } + if (csra_max_devs == 0) { + dev_warn(&pdev->dev, + "%s: Max CSRA devices is 0 for this target?\n", + __func__); + return 0; + } + + /* Get count of CSRA device phandles for this platform */ + csra_dev_cnt = of_count_phandle_with_args(pdev->dev.of_node, + "qcom,csra-devs", NULL); + if (csra_dev_cnt == -ENOENT) { + dev_warn(&pdev->dev, "%s: No csra device defined in DT.\n", + __func__); + goto err; + } else if (csra_dev_cnt <= 0) { + dev_err(&pdev->dev, + "%s: Error reading csra device from DT. csra_dev_cnt = %d\n", + __func__, csra_dev_cnt); + ret = -EINVAL; + goto err; + } + + /* + * Expect total phandles count to be NOT less than maximum possible + * CSRA count. However, if it is less, then assign same value to + * max count as well. + */ + if (csra_dev_cnt < csra_max_devs) { + dev_dbg(&pdev->dev, + "%s: csra_max_devs = %d cannot exceed csra_dev_cnt = %d\n", + __func__, csra_max_devs, csra_dev_cnt); + csra_max_devs = csra_dev_cnt; + } + + /* Make sure prefix string passed for each CSRA device */ + ret = of_property_count_strings(pdev->dev.of_node, + "qcom,csra-aux-dev-prefix"); + if (ret != csra_dev_cnt) { + dev_err(&pdev->dev, + "%s: expecting %d csra prefix. Defined only %d in DT\n", + __func__, csra_dev_cnt, ret); + ret = -EINVAL; + goto err; + } + + /* + * Alloc mem to store phandle and index info of CSRA device, if already + * registered with ALSA core + */ + csra66x0_dev_info = devm_kcalloc(&pdev->dev, csra_max_devs, + sizeof(struct msm_csra66x0_dev_info), + GFP_KERNEL); + if (!csra66x0_dev_info) { + ret = -ENOMEM; + goto err; + } + + /* + * search and check whether all CSRA devices are already + * registered with ALSA core or not. If found a node, store + * the node and the index in a local array of struct for later + * use. + */ + for (i = 0; i < csra_dev_cnt; i++) { + csra_of_node = of_parse_phandle(pdev->dev.of_node, + "qcom,csra-devs", i); + if (unlikely(!csra_of_node)) { + /* we should not be here */ + dev_err(&pdev->dev, + "%s: csra dev node is not present\n", + __func__); + ret = -EINVAL; + goto err_free_dev_info; + } + if (soc_find_component(csra_of_node, NULL)) { + /* CSRA device registered with ALSA core */ + csra66x0_dev_info[found].of_node = csra_of_node; + csra66x0_dev_info[found].index = i; + found++; + if (found == csra_max_devs) + break; + } + } + + if (found < csra_max_devs) { + dev_dbg(&pdev->dev, + "%s: failed to find %d components. Found only %d\n", + __func__, csra_max_devs, found); + return -EPROBE_DEFER; + } + dev_info(&pdev->dev, + "%s: found %d csra66x0 devices registered with ALSA core\n", + __func__, found); + + card->num_aux_devs = csra_max_devs; + card->num_configs = csra_max_devs; + + /* Alloc array of AUX devs struct */ + msm_aux_dev = devm_kcalloc(&pdev->dev, card->num_aux_devs, + sizeof(struct snd_soc_aux_dev), GFP_KERNEL); + if (!msm_aux_dev) { + ret = -ENOMEM; + goto err_free_dev_info; + } + + /* Alloc array of codec conf struct */ + msm_codec_conf = devm_kcalloc(&pdev->dev, card->num_aux_devs, + sizeof(struct snd_soc_codec_conf), GFP_KERNEL); + if (!msm_codec_conf) { + ret = -ENOMEM; + goto err_free_aux_dev; + } + + for (i = 0; i < card->num_aux_devs; i++) { + dev_name_str = devm_kzalloc(&pdev->dev, DEV_NAME_STR_LEN, + GFP_KERNEL); + if (!dev_name_str) { + ret = -ENOMEM; + goto err_free_cdc_conf; + } + + ret = of_property_read_string_index(pdev->dev.of_node, + "qcom,csra-aux-dev-prefix", + csra66x0_dev_info[i].index, + csra_auxdev_name_prefix); + if (ret) { + dev_err(&pdev->dev, + "%s: failed to read csra aux dev prefix, ret = %d\n", + __func__, ret); + ret = -EINVAL; + goto err_free_dev_name_str; + } + + snprintf(dev_name_str, strlen("csra66x0.%d"), "csra66x0.%d", i); + msm_aux_dev[i].name = dev_name_str; + msm_aux_dev[i].codec_name = NULL; + msm_aux_dev[i].codec_of_node = + csra66x0_dev_info[i].of_node; + msm_aux_dev[i].init = msm_csra66x0_init; /* codec specific init */ + msm_codec_conf[i].dev_name = NULL; + msm_codec_conf[i].name_prefix = csra_auxdev_name_prefix[0]; + msm_codec_conf[i].of_node = csra66x0_dev_info[i].of_node; + } + card->codec_conf = msm_codec_conf; + card->aux_dev = msm_aux_dev; + + return 0; + +err_free_dev_name_str: + devm_kfree(&pdev->dev, dev_name_str); +err_free_cdc_conf: + devm_kfree(&pdev->dev, msm_codec_conf); +err_free_aux_dev: + devm_kfree(&pdev->dev, msm_aux_dev); +err_free_dev_info: + devm_kfree(&pdev->dev, csra66x0_dev_info); +err: + return ret; +} + +static void msm_i2s_auxpcm_init(struct platform_device *pdev) +{ + int count; + u32 mi2s_master_slave[MI2S_MAX]; + int ret; + + for (count = 0; count < MI2S_MAX; count++) { + mutex_init(&mi2s_intf_conf[count].lock); + mi2s_intf_conf[count].ref_cnt = 0; + } + + ret = of_property_read_u32_array(pdev->dev.of_node, + "qcom,msm-mi2s-master", + mi2s_master_slave, MI2S_MAX); + if (ret) { + dev_dbg(&pdev->dev, "%s: no qcom,msm-mi2s-master in DT node\n", + __func__); + } else { + for (count = 0; count < MI2S_MAX; count++) { + mi2s_intf_conf[count].msm_is_mi2s_master = + mi2s_master_slave[count]; + } + } +} + +static void msm_i2s_auxpcm_deinit(void) +{ + int count; + + for (count = 0; count < MI2S_MAX; count++) { + mutex_destroy(&mi2s_intf_conf[count].lock); + mi2s_intf_conf[count].ref_cnt = 0; + mi2s_intf_conf[count].msm_is_mi2s_master = 0; + } +} + +static int msm_scan_i2c_addr(struct platform_device *pdev, + uint32_t busnum, uint32_t addr) +{ + struct i2c_adapter *adap; + u8 rbuf; + struct i2c_msg msg; + int status = 0; + + adap = i2c_get_adapter(busnum); + if (!adap) { + dev_err(&pdev->dev, "%s: Cannot get I2C adapter %d\n", + __func__, busnum); + return -EBUSY; + } + + /* to test presence, read one byte from device */ + msg.addr = addr; + msg.flags = I2C_M_RD; + msg.len = 1; + msg.buf = &rbuf; + + status = i2c_transfer(adap, &msg, 1); + + i2c_put_adapter(adap); + + if (status != 1) { + dev_dbg(&pdev->dev, "%s: I2C read from addr 0x%02x failed\n", + __func__, addr); + return -ENODEV; + } + + dev_dbg(&pdev->dev, "%s: I2C read from addr 0x%02x successful\n", + __func__, addr); + + return 0; +} + +static int msm_detect_ep92_dev(struct platform_device *pdev, + struct snd_soc_card *card) +{ + int i; + uint32_t ep92_busnum = 0; + uint32_t ep92_reg = 0; + const char *ep92_name = NULL; + struct snd_soc_dai_link *dai; + int rc = 0; + + rc = of_property_read_u32(pdev->dev.of_node, "qcom,ep92-busnum", + &ep92_busnum); + if (rc) { + dev_info(&pdev->dev, "%s: No DT match ep92-reg\n", __func__); + return 0; + } + + rc = of_property_read_u32(pdev->dev.of_node, "qcom,ep92-reg", + &ep92_reg); + if (rc) { + dev_info(&pdev->dev, "%s: No DT match ep92-busnum\n", __func__); + return 0; + } + + rc = of_property_read_string(pdev->dev.of_node, "qcom,ep92-name", + &ep92_name); + if (rc) { + dev_info(&pdev->dev, "%s: No DT match ep92-name\n", __func__); + return 0; + } + + /* check I2C bus for connected ep92 chip */ + if (msm_scan_i2c_addr(pdev, ep92_busnum, ep92_reg) < 0) { + /* check a second time after a short delay */ + msleep(20); + if (msm_scan_i2c_addr(pdev, ep92_busnum, ep92_reg) < 0) { + dev_info(&pdev->dev, "%s: No ep92 device found\n", + __func__); + /* continue with snd_card registration without ep92 */ + return 0; + } + } + + dev_info(&pdev->dev, "%s: ep92 device found\n", __func__); + + /* update codec info in MI2S dai link */ + dai = &msm_mi2s_be_dai_links[0]; + for (i=0; iname, LPASS_BE_SEC_MI2S_TX) == 0) { + dev_dbg(&pdev->dev, + "%s: Set Sec MI2S dai to ep92 codec\n", + __func__); + dai->codec_name = ep92_name; + dai->codec_dai_name = "ep92-hdmi"; + break; + } + dai++; + } + + /* update codec info in SPDIF dai link */ + dai = &msm_spdif_be_dai_links[0]; + for (i=0; iname, LPASS_BE_SEC_SPDIF_TX) == 0) { + dev_dbg(&pdev->dev, + "%s: Set Sec SPDIF dai to ep92 codec\n", + __func__); + dai->codec_name = ep92_name; + dai->codec_dai_name = "ep92-arc"; + break; + } + dai++; + } + + return 0; +} + +static int msm_asoc_machine_probe(struct platform_device *pdev) +{ + struct snd_soc_card *card; + struct msm_asoc_mach_data *pdata; + int ret; + u32 val; + const char *micb_supply_str = "tdm-vdd-micb-supply"; + const char *micb_supply_str1 = "tdm-vdd-micb"; + const char *micb_voltage_str = "qcom,tdm-vdd-micb-voltage"; + const char *micb_current_str = "qcom,tdm-vdd-micb-current"; + + if (!pdev->dev.of_node) { + dev_err(&pdev->dev, "No platform supplied from device tree\n"); + return -EINVAL; + } + + pdata = devm_kzalloc(&pdev->dev, + sizeof(struct msm_asoc_mach_data), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + /* test for ep92 HDMI bridge and update dai links accordingly */ + ret = msm_detect_ep92_dev(pdev, card); + if (ret) + goto err; + + card = populate_snd_card_dailinks(&pdev->dev); + if (!card) { + dev_err(&pdev->dev, "%s: Card uninitialized\n", __func__); + ret = -EINVAL; + goto err; + } + card->dev = &pdev->dev; + platform_set_drvdata(pdev, card); + snd_soc_card_set_drvdata(card, pdata); + + ret = snd_soc_of_parse_card_name(card, "qcom,model"); + if (ret) { + dev_err(&pdev->dev, "parse card name failed, err:%d\n", + ret); + goto err; + } + + ret = snd_soc_of_parse_audio_routing(card, "qcom,audio-routing"); + if (ret) { + dev_err(&pdev->dev, "parse audio routing failed, err:%d\n", + ret); + goto err; + } + + ret = msm_populate_dai_link_component_of_node(card); + if (ret) { + ret = -EPROBE_DEFER; + goto err; + } + + ret = of_property_read_u32(pdev->dev.of_node, "qcom,csra-codec", &val); + if (ret) { + dev_info(&pdev->dev, "no 'qcom,csra-codec' in DT\n"); + val = 0; + } + if (val) { + pdata->codec_is_csra = true; + mi2s_rx_cfg[PRIM_MI2S].bit_format = SNDRV_PCM_FORMAT_S24_LE; + ret = msm_init_csra_dev(pdev, card); + if (ret) + goto err; + } else { + pdata->codec_is_csra = false; + ret = msm_init_wsa_dev(pdev, card); + if (ret) + goto err; + } + + pdata->dmic_01_gpio_p = of_parse_phandle(pdev->dev.of_node, + "qcom,cdc-dmic01-gpios", 0); + pdata->dmic_23_gpio_p = of_parse_phandle(pdev->dev.of_node, + "qcom,cdc-dmic23-gpios", 0); + pdata->dmic_45_gpio_p = of_parse_phandle(pdev->dev.of_node, + "qcom,cdc-dmic45-gpios", 0); + pdata->dmic_67_gpio_p = of_parse_phandle(pdev->dev.of_node, + "qcom,cdc-dmic67-gpios", 0); + + pdata->mi2s_gpio_p[PRIM_MI2S] = of_parse_phandle(pdev->dev.of_node, + "qcom,pri-mi2s-gpios", 0); + pdata->mi2s_gpio_p[SEC_MI2S] = of_parse_phandle(pdev->dev.of_node, + "qcom,sec-mi2s-gpios", 0); + pdata->mi2s_gpio_p[TERT_MI2S] = of_parse_phandle(pdev->dev.of_node, + "qcom,tert-mi2s-gpios", 0); + pdata->mi2s_gpio_p[QUAT_MI2S] = of_parse_phandle(pdev->dev.of_node, + "qcom,quat-mi2s-gpios", 0); + pdata->mi2s_gpio_p[QUIN_MI2S] = of_parse_phandle(pdev->dev.of_node, + "qcom,quin-mi2s-gpios", 0); + + if (of_parse_phandle(pdev->dev.of_node, micb_supply_str, 0)) { + pdata->tdm_micb_supply = devm_regulator_get(&pdev->dev, + micb_supply_str1); + if (IS_ERR(pdata->tdm_micb_supply)) { + ret = PTR_ERR(pdata->tdm_micb_supply); + dev_err(&pdev->dev, + "%s:Failed to get micbias supply for TDM Mic %d\n", + __func__, ret); + } + ret = of_property_read_u32(pdev->dev.of_node, + micb_voltage_str, + &pdata->tdm_micb_voltage); + if (ret) { + dev_err(&pdev->dev, + "%s:Looking up %s property in node %s failed\n", + __func__, micb_voltage_str, + pdev->dev.of_node->full_name); + } + ret = of_property_read_u32(pdev->dev.of_node, + micb_current_str, + &pdata->tdm_micb_current); + if (ret) { + dev_err(&pdev->dev, + "%s:Looking up %s property in node %s failed\n", + __func__, micb_current_str, + pdev->dev.of_node->full_name); + } + } + + ret = devm_snd_soc_register_card(&pdev->dev, card); + if (ret == -EPROBE_DEFER) { + if (codec_reg_done) + ret = -EINVAL; + goto err; + } else if (ret) { + dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", + ret); + goto err; + } + dev_info(&pdev->dev, "Sound card %s registered\n", card->name); + spdev = pdev; + + ret = msm_mdf_mem_init(); + if (ret) + dev_err(&pdev->dev, "msm_mdf_mem_init failed (%d)\n", + ret); + + msm_i2s_auxpcm_init(pdev); + + is_initial_boot = true; + return 0; +err: + return ret; +} + +static int msm_asoc_machine_remove(struct platform_device *pdev) +{ + audio_notifier_deregister("qcs405"); + msm_i2s_auxpcm_deinit(); + msm_mdf_mem_deinit(); + + return 0; +} + +static struct platform_driver qcs405_asoc_machine_driver = { + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + .pm = &snd_soc_pm_ops, + .of_match_table = qcs405_asoc_machine_of_match, + }, + .probe = msm_asoc_machine_probe, + .remove = msm_asoc_machine_remove, +}; +module_platform_driver(qcs405_asoc_machine_driver); + +MODULE_DESCRIPTION("ALSA SoC QCS405 Machine driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:" DRV_NAME); +MODULE_DEVICE_TABLE(of, qcs405_asoc_machine_of_match); diff --git a/audio-kernel/asoc/sa8155.c b/audio-kernel/asoc/sa8155.c new file mode 100644 index 000000000..1c67e9b5e --- /dev/null +++ b/audio-kernel/asoc/sa8155.c @@ -0,0 +1,7218 @@ +/* Copyright (c) 2014-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. + */ +/* + * Copyright 2011, The Android Open Source Project + + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of The Android Open Source Project nor the names of + its contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + * THIS SOFTWARE IS PROVIDED BY The Android Open Source Project ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL The Android Open Source Project BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "device_event.h" +#include "msm-pcm-routing-v2.h" + +#define DRV_NAME "sa8155-asoc-snd" + +#define __CHIPSET__ "SA8155 " +#define MSM_DAILINK_NAME(name) (__CHIPSET__#name) + +#define DEV_NAME_STR_LEN 32 + +#define SAMPLING_RATE_8KHZ 8000 +#define SAMPLING_RATE_11P025KHZ 11025 +#define SAMPLING_RATE_16KHZ 16000 +#define SAMPLING_RATE_22P05KHZ 22050 +#define SAMPLING_RATE_32KHZ 32000 +#define SAMPLING_RATE_44P1KHZ 44100 +#define SAMPLING_RATE_48KHZ 48000 +#define SAMPLING_RATE_88P2KHZ 88200 +#define SAMPLING_RATE_96KHZ 96000 +#define SAMPLING_RATE_176P4KHZ 176400 +#define SAMPLING_RATE_192KHZ 192000 +#define SAMPLING_RATE_352P8KHZ 352800 +#define SAMPLING_RATE_384KHZ 384000 + +#define ADSP_STATE_READY_TIMEOUT_MS 3000 +#define MSM_LL_QOS_VALUE 300 /* time in us to ensure LPM doesn't go in C3/C4 */ + +enum { + PRIM_MI2S = 0, + SEC_MI2S, + TERT_MI2S, + QUAT_MI2S, + QUIN_MI2S, + MI2S_MAX, +}; + +enum { + PRIM_AUX_PCM = 0, + SEC_AUX_PCM, + TERT_AUX_PCM, + QUAT_AUX_PCM, + QUIN_AUX_PCM, + AUX_PCM_MAX, +}; + +struct mi2s_conf { + struct mutex lock; + u32 ref_cnt; + u32 msm_is_mi2s_master; +}; + +static u32 mi2s_ebit_clk[MI2S_MAX] = { + Q6AFE_LPASS_CLK_ID_PRI_MI2S_EBIT, + Q6AFE_LPASS_CLK_ID_SEC_MI2S_EBIT, + Q6AFE_LPASS_CLK_ID_TER_MI2S_EBIT, + Q6AFE_LPASS_CLK_ID_QUAD_MI2S_EBIT, + Q6AFE_LPASS_CLK_ID_QUI_MI2S_EBIT +}; + +struct dev_config { + u32 sample_rate; + u32 bit_format; + u32 channels; +}; + +enum { + DP_RX_IDX = 0, + EXT_DISP_RX_IDX_MAX, +}; + +enum pinctrl_pin_state { + STATE_DISABLE = 0, /* All pins are in sleep state */ + STATE_MI2S_ACTIVE, /* IS2 = active, TDM = sleep */ + STATE_TDM_ACTIVE, /* IS2 = sleep, TDM = active */ +}; + +struct msm_pinctrl_info { + struct pinctrl *pinctrl; + struct pinctrl_state *mi2s_disable; + struct pinctrl_state *tdm_disable; + struct pinctrl_state *mi2s_active; + struct pinctrl_state *tdm_active; + enum pinctrl_pin_state curr_state; +}; + +struct msm_asoc_mach_data { + struct msm_pinctrl_info pinctrl_info; +}; + +static const char *const pin_states[] = {"sleep", "i2s-active", + "tdm-active"}; + +enum { + TDM_0 = 0, + TDM_1, + TDM_2, + TDM_3, + TDM_4, + TDM_5, + TDM_6, + TDM_7, + TDM_PORT_MAX, +}; + +enum { + TDM_PRI = 0, + TDM_SEC, + TDM_TERT, + TDM_QUAT, + TDM_QUIN, + TDM_INTERFACE_MAX, +}; + +struct tdm_port { + u32 mode; + u32 channel; +}; + +/* TDM default config */ +static struct dev_config tdm_rx_cfg[TDM_INTERFACE_MAX][TDM_PORT_MAX] = { + { /* PRI TDM */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, /* RX_0 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, /* RX_1 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, /* RX_2 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, /* RX_3 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_4 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_5 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_6 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_7 */ + }, + { /* SEC TDM */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, /* RX_0 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, /* RX_1 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, /* RX_2 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, /* RX_3 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_4 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_5 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_6 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_7 */ + }, + { /* TERT TDM */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 6}, /* RX_0 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_1 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_2 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_3 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_4 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_5 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_6 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_7 */ + }, + { /* QUAT TDM */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 8}, /* RX_0 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_1 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_2 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_3 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_4 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_5 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_6 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_7 */ + }, + { /* QUIN TDM */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, /* RX_0 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, /* RX_1 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, /* RX_2 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, /* RX_3 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_4 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_5 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_6 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_7 */ + } +}; + +/* TDM default config */ +static struct dev_config tdm_tx_cfg[TDM_INTERFACE_MAX][TDM_PORT_MAX] = { + { /* PRI TDM */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, /* TX_0 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, /* TX_1 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, /* TX_2 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, /* TX_3 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_4 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_5 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_6 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_7 */ + }, + { /* SEC TDM */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 6}, /* TX_0 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_1 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_2 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_3 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_4 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_5 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_6 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_7 */ + }, + { /* TERT TDM */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 4}, /* TX_0 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, /* TX_1 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, /* TX_2 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_3 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_4 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_5 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_6 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_7 */ + }, + { /* QUAT TDM */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 8}, /* TX_0 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_1 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_2 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_3 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_4 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_5 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_6 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_7 */ + }, + { /* QUIN TDM */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 6}, /* TX_0 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_1 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_2 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_3 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_4 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_5 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_6 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_7 */ + } +}; + +/* Default configuration of external display BE */ +static struct dev_config ext_disp_rx_cfg[] = { + [DP_RX_IDX] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, +}; + +static struct dev_config usb_rx_cfg = { + .sample_rate = SAMPLING_RATE_48KHZ, + .bit_format = SNDRV_PCM_FORMAT_S16_LE, + .channels = 2, +}; + +static struct dev_config usb_tx_cfg = { + .sample_rate = SAMPLING_RATE_48KHZ, + .bit_format = SNDRV_PCM_FORMAT_S16_LE, + .channels = 1, +}; + +static struct dev_config proxy_rx_cfg = { + .sample_rate = SAMPLING_RATE_48KHZ, + .bit_format = SNDRV_PCM_FORMAT_S16_LE, + .channels = 2, +}; + +/* Default configuration of MI2S channels */ +static struct dev_config mi2s_rx_cfg[] = { + [PRIM_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, + [SEC_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, + [TERT_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, + [QUAT_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, + [QUIN_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, +}; + +static struct dev_config mi2s_tx_cfg[] = { + [PRIM_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SEC_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [TERT_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [QUAT_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [QUIN_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, +}; + +static struct dev_config aux_pcm_rx_cfg[] = { + [PRIM_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SEC_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [TERT_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [QUAT_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [QUIN_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, +}; + +static struct dev_config aux_pcm_tx_cfg[] = { + [PRIM_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SEC_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [TERT_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [QUAT_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [QUIN_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, +}; + +/* TDM default slot config */ +struct tdm_slot_cfg { + u32 width; + u32 num; +}; + +static struct tdm_slot_cfg tdm_slot[TDM_INTERFACE_MAX] = { + /* PRI TDM */ + {32, 8}, + /* SEC TDM */ + {32, 8}, + /* TERT TDM */ + {32, 8}, + /* QUAT TDM */ + {32, 16}, + /* QUIN TDM */ + {32, 16} +}; + +/***************************************************************************** +* TO BE UPDATED: Codec/Platform specific tdm slot table +*****************************************************************************/ +static struct tdm_slot_cfg tdm_slot_custom[TDM_INTERFACE_MAX] = { + /* PRI TDM */ + {16, 16}, + /* SEC TDM */ + {16, 16}, + /* TERT TDM */ + {16, 16}, + /* QUAT TDM */ + {16, 16}, + /* QUIN TDM */ + {16, 16} +}; + + +/* TDM default slot offset config */ +#define TDM_SLOT_OFFSET_MAX 32 + +static unsigned int tdm_rx_slot_offset + [TDM_INTERFACE_MAX][TDM_PORT_MAX][TDM_SLOT_OFFSET_MAX] = { + {/* PRI TDM */ + {0, 4, 0xFFFF}, + {8, 12, 0xFFFF}, + {16, 20, 0xFFFF}, + {24, 28, 0xFFFF}, + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + }, + {/* SEC TDM */ + {0, 4, 0xFFFF}, + {8, 12, 0xFFFF}, + {16, 20, 0xFFFF}, + {24, 28, 0xFFFF}, + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {28, 0xFFFF}, + }, + {/* TERT TDM */ + {0, 4, 8, 12, 16, 20, 0xFFFF}, + {24, 0xFFFF}, + {28, 0xFFFF}, + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + }, + {/* QUAT TDM */ + {0, 4, 8, 12, 16, 20, 24, 28, 0xFFFF},/*AMP OUT*/ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + }, + {/* QUIN TDM */ + {0, 4, 0xFFFF},/*STEREO SPKR1*/ + {8, 12, 0xFFFF},/*STEREO SPKR2*/ + {16, 20, 0xFFFF},/*STEREO SPKR3*/ + {24, 28, 0xFFFF},/*STEREO SPKR4*/ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + } +}; + +static unsigned int tdm_tx_slot_offset + [TDM_INTERFACE_MAX][TDM_PORT_MAX][TDM_SLOT_OFFSET_MAX] = { + {/* PRI TDM */ + {0, 4, 0xFFFF}, + {8, 12, 0xFFFF}, + {16, 20, 0xFFFF}, + {24, 28, 0xFFFF}, + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + }, + {/* SEC TDM */ + {0, 4, 8, 12, 16, 20, 0xFFFF}, + {24, 0xFFFF}, + {28, 0xFFFF}, + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + }, + {/* TERT TDM */ + {0, 4, 8, 12, 0xFFFF}, + {16, 20, 0xFFFF}, + {24, 28, 0xFFFF}, + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {28, 0xFFFF}, + }, + {/* QUAT TDM */ + {0, 4, 8, 12, 16, 20, 24, 28, + 32, 36, 40, 44, 48, 52, 56, 60, 0xFFFF},/*MIC ARR*/ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + }, + {/* QUIN TDM */ + {0, 4, 8, 12, 16, 20, 0xFFFF},/*EC/ANC REF*/ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + } +}; + +/***************************************************************************** +* TO BE UPDATED: Codec/Platform specific tdm slot offset table +* NOTE: +* Each entry represents the slot offset array of one backend tdm device +* valid offset represents the starting offset in byte for the channel +* use 0xFFFF for end or unused slot offset entry. +*****************************************************************************/ +static unsigned int tdm_rx_slot_offset_custom + [TDM_INTERFACE_MAX][TDM_PORT_MAX][TDM_SLOT_OFFSET_MAX] = { + {/* PRI TDM */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + }, + {/* SEC TDM */ + {0, 2, 0xFFFF}, + {4, 0xFFFF}, + {6, 0xFFFF}, + {8, 0xFFFF}, + {10, 0xFFFF}, + {12, 14, 16, 18, 20, 22, 24, 26, 0xFFFF}, + {28, 30, 0xFFFF}, + {30, 0xFFFF}, + }, + {/* TERT TDM */ + {0, 2, 0xFFFF}, + {4, 6, 8, 10, 12, 14, 16, 18, 0xFFFF}, + {20, 22, 24, 26, 28, 30, 0xFFFF}, + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + }, + {/* QUAT TDM */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + }, + {/* QUIN TDM */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + } +}; + +static unsigned int tdm_tx_slot_offset_custom + [TDM_INTERFACE_MAX][TDM_PORT_MAX][TDM_SLOT_OFFSET_MAX] = { + {/* PRI TDM */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + }, + {/* SEC TDM */ + {0, 2, 0xFFFF}, + {4, 6, 8, 10, 12, 14, 16, 18, 0xFFFF}, + {20, 22, 24, 26, 28, 30, 0xFFFF}, + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + }, + {/* TERT TDM */ + {0, 2, 4, 6, 8, 10, 12, 0xFFFF}, + {14, 16, 0xFFFF}, + {18, 20, 22, 24, 26, 28, 30, 0xFFFF}, + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {30, 0xFFFF}, + }, + {/* QUAT TDM */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + }, + {/* QUIN TDM */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + } +}; + + +static char const *bit_format_text[] = {"S16_LE", "S24_LE", "S24_3LE", + "S32_LE"}; +static char const *ext_disp_bit_format_text[] = {"S16_LE", "S24_LE", + "S24_3LE"}; +static const char *const usb_ch_text[] = {"One", "Two", "Three", "Four", + "Five", "Six", "Seven", + "Eight"}; +static char const *ch_text[] = {"Two", "Three", "Four", "Five", + "Six", "Seven", "Eight"}; +static char const *usb_sample_rate_text[] = {"KHZ_8", "KHZ_11P025", + "KHZ_16", "KHZ_22P05", + "KHZ_32", "KHZ_44P1", "KHZ_48", + "KHZ_88P2", "KHZ_96", "KHZ_176P4", + "KHZ_192", "KHZ_352P8", "KHZ_384"}; +static char const *ext_disp_sample_rate_text[] = {"KHZ_48", "KHZ_96", + "KHZ_192", "KHZ_32", "KHZ_44P1", + "KHZ_88P2", "KHZ_176P4" }; +static char const *tdm_ch_text[] = { + "One", "Two", "Three", "Four", + "Five", "Six", "Seven", "Eight", + "Nine", "Ten", "Eleven", "Twelve", + "Thirteen", "Fourteen", "Fifteen", "Sixteen", + "Seventeen", "Eighteen", "Nineteen", "Twenty", + "TwentyOne", "TwentyTwo", "TwentyThree", "TwentyFour", + "TwentyFive", "TwentySix", "TwentySeven", "TwentyEight", + "TwentyNine", "Thirty", "ThirtyOne", "ThirtyTwo"}; +static char const *tdm_bit_format_text[] = {"S16_LE", "S24_LE", "S32_LE"}; +static char const *tdm_sample_rate_text[] = {"KHZ_8", "KHZ_16", "KHZ_32", + "KHZ_48", "KHZ_176P4", + "KHZ_352P8"}; +static const char *const tdm_slot_num_text[] = {"One", "Two", "Four", + "Eight", "Sixteen", "ThirtyTwo"}; +static const char *const tdm_slot_width_text[] = {"16", "24", "32"}; +static const char *const auxpcm_rate_text[] = {"KHZ_8", "KHZ_16"}; +static char const *mi2s_rate_text[] = {"KHZ_8", "KHZ_11P025", "KHZ_16", + "KHZ_22P05", "KHZ_32", "KHZ_44P1", + "KHZ_48", "KHZ_96", "KHZ_192"}; +static const char *const mi2s_ch_text[] = {"One", "Two", "Three", "Four", + "Five", "Six", "Seven", + "Eight"}; +static const char *const qos_text[] = {"Disable", "Enable"}; + +static SOC_ENUM_SINGLE_EXT_DECL(usb_rx_chs, usb_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(usb_tx_chs, usb_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(ext_disp_rx_chs, ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(proxy_rx_chs, ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(usb_rx_format, bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(usb_tx_format, bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(ext_disp_rx_format, ext_disp_bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(usb_rx_sample_rate, usb_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(usb_tx_sample_rate, usb_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(ext_disp_rx_sample_rate, + ext_disp_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(tdm_tx_chs, tdm_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(tdm_tx_format, tdm_bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(tdm_tx_sample_rate, tdm_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(tdm_rx_chs, tdm_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(tdm_rx_format, tdm_bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(tdm_rx_sample_rate, tdm_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(tdm_slot_num, tdm_slot_num_text); +static SOC_ENUM_SINGLE_EXT_DECL(tdm_slot_width, tdm_slot_width_text); +static SOC_ENUM_SINGLE_EXT_DECL(prim_aux_pcm_rx_sample_rate, auxpcm_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(sec_aux_pcm_rx_sample_rate, auxpcm_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(tert_aux_pcm_rx_sample_rate, auxpcm_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(quat_aux_pcm_rx_sample_rate, auxpcm_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(quin_aux_pcm_rx_sample_rate, auxpcm_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(prim_aux_pcm_tx_sample_rate, auxpcm_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(sec_aux_pcm_tx_sample_rate, auxpcm_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(tert_aux_pcm_tx_sample_rate, auxpcm_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(quat_aux_pcm_tx_sample_rate, auxpcm_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(quin_aux_pcm_tx_sample_rate, auxpcm_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(prim_mi2s_rx_sample_rate, mi2s_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(sec_mi2s_rx_sample_rate, mi2s_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(tert_mi2s_rx_sample_rate, mi2s_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(quat_mi2s_rx_sample_rate, mi2s_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(quin_mi2s_rx_sample_rate, mi2s_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(prim_mi2s_tx_sample_rate, mi2s_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(sec_mi2s_tx_sample_rate, mi2s_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(tert_mi2s_tx_sample_rate, mi2s_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(quat_mi2s_tx_sample_rate, mi2s_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(quin_mi2s_tx_sample_rate, mi2s_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(prim_mi2s_rx_chs, mi2s_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(prim_mi2s_tx_chs, mi2s_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(sec_mi2s_rx_chs, mi2s_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(sec_mi2s_tx_chs, mi2s_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(tert_mi2s_rx_chs, mi2s_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(tert_mi2s_tx_chs, mi2s_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(quat_mi2s_rx_chs, mi2s_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(quat_mi2s_tx_chs, mi2s_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(quin_mi2s_rx_chs, mi2s_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(quin_mi2s_tx_chs, mi2s_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(mi2s_rx_format, bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(mi2s_tx_format, bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(aux_pcm_rx_format, bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(aux_pcm_tx_format, bit_format_text); + +static bool is_initial_boot = true; + +static struct afe_clk_set mi2s_clk[MI2S_MAX] = { + { + AFE_API_VERSION_I2S_CONFIG, + Q6AFE_LPASS_CLK_ID_PRI_MI2S_IBIT, + Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ, + Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO, + Q6AFE_LPASS_CLK_ROOT_DEFAULT, + 0, + }, + { + AFE_API_VERSION_I2S_CONFIG, + Q6AFE_LPASS_CLK_ID_SEC_MI2S_IBIT, + Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ, + Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO, + Q6AFE_LPASS_CLK_ROOT_DEFAULT, + 0, + }, + { + AFE_API_VERSION_I2S_CONFIG, + Q6AFE_LPASS_CLK_ID_TER_MI2S_IBIT, + Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ, + Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO, + Q6AFE_LPASS_CLK_ROOT_DEFAULT, + 0, + }, + { + AFE_API_VERSION_I2S_CONFIG, + Q6AFE_LPASS_CLK_ID_QUAD_MI2S_IBIT, + Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ, + Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO, + Q6AFE_LPASS_CLK_ROOT_DEFAULT, + 0, + }, + { + AFE_API_VERSION_I2S_CONFIG, + Q6AFE_LPASS_CLK_ID_QUI_MI2S_IBIT, + Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ, + Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO, + Q6AFE_LPASS_CLK_ROOT_DEFAULT, + 0, + } + +}; + +static struct mi2s_conf mi2s_intf_conf[MI2S_MAX]; + +static int usb_audio_rx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s: usb_audio_rx_ch = %d\n", __func__, + usb_rx_cfg.channels); + ucontrol->value.integer.value[0] = usb_rx_cfg.channels - 1; + return 0; +} + +static int usb_audio_rx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + usb_rx_cfg.channels = ucontrol->value.integer.value[0] + 1; + + pr_debug("%s: usb_audio_rx_ch = %d\n", __func__, usb_rx_cfg.channels); + return 1; +} + +static int usb_audio_rx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int sample_rate_val; + + switch (usb_rx_cfg.sample_rate) { + case SAMPLING_RATE_384KHZ: + sample_rate_val = 12; + break; + case SAMPLING_RATE_352P8KHZ: + sample_rate_val = 11; + break; + case SAMPLING_RATE_192KHZ: + sample_rate_val = 10; + break; + case SAMPLING_RATE_176P4KHZ: + sample_rate_val = 9; + break; + case SAMPLING_RATE_96KHZ: + sample_rate_val = 8; + break; + case SAMPLING_RATE_88P2KHZ: + sample_rate_val = 7; + break; + case SAMPLING_RATE_48KHZ: + sample_rate_val = 6; + break; + case SAMPLING_RATE_44P1KHZ: + sample_rate_val = 5; + break; + case SAMPLING_RATE_32KHZ: + sample_rate_val = 4; + break; + case SAMPLING_RATE_22P05KHZ: + sample_rate_val = 3; + break; + case SAMPLING_RATE_16KHZ: + sample_rate_val = 2; + break; + case SAMPLING_RATE_11P025KHZ: + sample_rate_val = 1; + break; + case SAMPLING_RATE_8KHZ: + default: + sample_rate_val = 0; + break; + } + + ucontrol->value.integer.value[0] = sample_rate_val; + pr_debug("%s: usb_audio_rx_sample_rate = %d\n", __func__, + usb_rx_cfg.sample_rate); + return 0; +} + +static int usb_audio_rx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (ucontrol->value.integer.value[0]) { + case 12: + usb_rx_cfg.sample_rate = SAMPLING_RATE_384KHZ; + break; + case 11: + usb_rx_cfg.sample_rate = SAMPLING_RATE_352P8KHZ; + break; + case 10: + usb_rx_cfg.sample_rate = SAMPLING_RATE_192KHZ; + break; + case 9: + usb_rx_cfg.sample_rate = SAMPLING_RATE_176P4KHZ; + break; + case 8: + usb_rx_cfg.sample_rate = SAMPLING_RATE_96KHZ; + break; + case 7: + usb_rx_cfg.sample_rate = SAMPLING_RATE_88P2KHZ; + break; + case 6: + usb_rx_cfg.sample_rate = SAMPLING_RATE_48KHZ; + break; + case 5: + usb_rx_cfg.sample_rate = SAMPLING_RATE_44P1KHZ; + break; + case 4: + usb_rx_cfg.sample_rate = SAMPLING_RATE_32KHZ; + break; + case 3: + usb_rx_cfg.sample_rate = SAMPLING_RATE_22P05KHZ; + break; + case 2: + usb_rx_cfg.sample_rate = SAMPLING_RATE_16KHZ; + break; + case 1: + usb_rx_cfg.sample_rate = SAMPLING_RATE_11P025KHZ; + break; + case 0: + usb_rx_cfg.sample_rate = SAMPLING_RATE_8KHZ; + break; + default: + usb_rx_cfg.sample_rate = SAMPLING_RATE_48KHZ; + break; + } + + pr_debug("%s: control value = %ld, usb_audio_rx_sample_rate = %d\n", + __func__, ucontrol->value.integer.value[0], + usb_rx_cfg.sample_rate); + return 0; +} + +static int usb_audio_rx_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (usb_rx_cfg.bit_format) { + case SNDRV_PCM_FORMAT_S32_LE: + ucontrol->value.integer.value[0] = 3; + break; + case SNDRV_PCM_FORMAT_S24_3LE: + ucontrol->value.integer.value[0] = 2; + break; + case SNDRV_PCM_FORMAT_S24_LE: + ucontrol->value.integer.value[0] = 1; + break; + case SNDRV_PCM_FORMAT_S16_LE: + default: + ucontrol->value.integer.value[0] = 0; + break; + } + + pr_debug("%s: usb_audio_rx_format = %d, ucontrol value = %ld\n", + __func__, usb_rx_cfg.bit_format, + ucontrol->value.integer.value[0]); + return 0; +} + +static int usb_audio_rx_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int rc = 0; + + switch (ucontrol->value.integer.value[0]) { + case 3: + usb_rx_cfg.bit_format = SNDRV_PCM_FORMAT_S32_LE; + break; + case 2: + usb_rx_cfg.bit_format = SNDRV_PCM_FORMAT_S24_3LE; + break; + case 1: + usb_rx_cfg.bit_format = SNDRV_PCM_FORMAT_S24_LE; + break; + case 0: + default: + usb_rx_cfg.bit_format = SNDRV_PCM_FORMAT_S16_LE; + break; + } + pr_debug("%s: usb_audio_rx_format = %d, ucontrol value = %ld\n", + __func__, usb_rx_cfg.bit_format, + ucontrol->value.integer.value[0]); + + return rc; +} + +static int usb_audio_tx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s: usb_audio_tx_ch = %d\n", __func__, + usb_tx_cfg.channels); + ucontrol->value.integer.value[0] = usb_tx_cfg.channels - 1; + return 0; +} + +static int usb_audio_tx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + usb_tx_cfg.channels = ucontrol->value.integer.value[0] + 1; + + pr_debug("%s: usb_audio_tx_ch = %d\n", __func__, usb_tx_cfg.channels); + return 1; +} + +static int usb_audio_tx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int sample_rate_val; + + switch (usb_tx_cfg.sample_rate) { + case SAMPLING_RATE_384KHZ: + sample_rate_val = 12; + break; + case SAMPLING_RATE_352P8KHZ: + sample_rate_val = 11; + break; + case SAMPLING_RATE_192KHZ: + sample_rate_val = 10; + break; + case SAMPLING_RATE_176P4KHZ: + sample_rate_val = 9; + break; + case SAMPLING_RATE_96KHZ: + sample_rate_val = 8; + break; + case SAMPLING_RATE_88P2KHZ: + sample_rate_val = 7; + break; + case SAMPLING_RATE_48KHZ: + sample_rate_val = 6; + break; + case SAMPLING_RATE_44P1KHZ: + sample_rate_val = 5; + break; + case SAMPLING_RATE_32KHZ: + sample_rate_val = 4; + break; + case SAMPLING_RATE_22P05KHZ: + sample_rate_val = 3; + break; + case SAMPLING_RATE_16KHZ: + sample_rate_val = 2; + break; + case SAMPLING_RATE_11P025KHZ: + sample_rate_val = 1; + break; + case SAMPLING_RATE_8KHZ: + sample_rate_val = 0; + break; + default: + sample_rate_val = 6; + break; + } + + ucontrol->value.integer.value[0] = sample_rate_val; + pr_debug("%s: usb_audio_tx_sample_rate = %d\n", __func__, + usb_tx_cfg.sample_rate); + return 0; +} + +static int usb_audio_tx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (ucontrol->value.integer.value[0]) { + case 12: + usb_tx_cfg.sample_rate = SAMPLING_RATE_384KHZ; + break; + case 11: + usb_tx_cfg.sample_rate = SAMPLING_RATE_352P8KHZ; + break; + case 10: + usb_tx_cfg.sample_rate = SAMPLING_RATE_192KHZ; + break; + case 9: + usb_tx_cfg.sample_rate = SAMPLING_RATE_176P4KHZ; + break; + case 8: + usb_tx_cfg.sample_rate = SAMPLING_RATE_96KHZ; + break; + case 7: + usb_tx_cfg.sample_rate = SAMPLING_RATE_88P2KHZ; + break; + case 6: + usb_tx_cfg.sample_rate = SAMPLING_RATE_48KHZ; + break; + case 5: + usb_tx_cfg.sample_rate = SAMPLING_RATE_44P1KHZ; + break; + case 4: + usb_tx_cfg.sample_rate = SAMPLING_RATE_32KHZ; + break; + case 3: + usb_tx_cfg.sample_rate = SAMPLING_RATE_22P05KHZ; + break; + case 2: + usb_tx_cfg.sample_rate = SAMPLING_RATE_16KHZ; + break; + case 1: + usb_tx_cfg.sample_rate = SAMPLING_RATE_11P025KHZ; + break; + case 0: + usb_tx_cfg.sample_rate = SAMPLING_RATE_8KHZ; + break; + default: + usb_tx_cfg.sample_rate = SAMPLING_RATE_48KHZ; + break; + } + + pr_debug("%s: control value = %ld, usb_audio_tx_sample_rate = %d\n", + __func__, ucontrol->value.integer.value[0], + usb_tx_cfg.sample_rate); + return 0; +} + +static int usb_audio_tx_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (usb_tx_cfg.bit_format) { + case SNDRV_PCM_FORMAT_S32_LE: + ucontrol->value.integer.value[0] = 3; + break; + case SNDRV_PCM_FORMAT_S24_3LE: + ucontrol->value.integer.value[0] = 2; + break; + case SNDRV_PCM_FORMAT_S24_LE: + ucontrol->value.integer.value[0] = 1; + break; + case SNDRV_PCM_FORMAT_S16_LE: + default: + ucontrol->value.integer.value[0] = 0; + break; + } + + pr_debug("%s: usb_audio_tx_format = %d, ucontrol value = %ld\n", + __func__, usb_tx_cfg.bit_format, + ucontrol->value.integer.value[0]); + return 0; +} + +static int usb_audio_tx_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int rc = 0; + + switch (ucontrol->value.integer.value[0]) { + case 3: + usb_tx_cfg.bit_format = SNDRV_PCM_FORMAT_S32_LE; + break; + case 2: + usb_tx_cfg.bit_format = SNDRV_PCM_FORMAT_S24_3LE; + break; + case 1: + usb_tx_cfg.bit_format = SNDRV_PCM_FORMAT_S24_LE; + break; + case 0: + default: + usb_tx_cfg.bit_format = SNDRV_PCM_FORMAT_S16_LE; + break; + } + pr_debug("%s: usb_audio_tx_format = %d, ucontrol value = %ld\n", + __func__, usb_tx_cfg.bit_format, + ucontrol->value.integer.value[0]); + + return rc; +} + +static int ext_disp_get_port_idx(struct snd_kcontrol *kcontrol) +{ + int idx; + + if (strnstr(kcontrol->id.name, "Display Port RX", + sizeof("Display Port RX"))) { + idx = DP_RX_IDX; + } else { + pr_err("%s: unsupported BE: %s", + __func__, kcontrol->id.name); + idx = -EINVAL; + } + + return idx; +} + +static int ext_disp_rx_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = ext_disp_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + switch (ext_disp_rx_cfg[idx].bit_format) { + case SNDRV_PCM_FORMAT_S24_3LE: + ucontrol->value.integer.value[0] = 2; + break; + case SNDRV_PCM_FORMAT_S24_LE: + ucontrol->value.integer.value[0] = 1; + break; + case SNDRV_PCM_FORMAT_S16_LE: + default: + ucontrol->value.integer.value[0] = 0; + break; + } + + pr_debug("%s: ext_disp_rx[%d].format = %d, ucontrol value = %ld\n", + __func__, idx, ext_disp_rx_cfg[idx].bit_format, + ucontrol->value.integer.value[0]); + return 0; +} + +static int ext_disp_rx_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = ext_disp_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + switch (ucontrol->value.integer.value[0]) { + case 2: + ext_disp_rx_cfg[idx].bit_format = SNDRV_PCM_FORMAT_S24_3LE; + break; + case 1: + ext_disp_rx_cfg[idx].bit_format = SNDRV_PCM_FORMAT_S24_LE; + break; + case 0: + default: + ext_disp_rx_cfg[idx].bit_format = SNDRV_PCM_FORMAT_S16_LE; + break; + } + pr_debug("%s: ext_disp_rx[%d].format = %d, ucontrol value = %ld\n", + __func__, idx, ext_disp_rx_cfg[idx].bit_format, + ucontrol->value.integer.value[0]); + + return 0; +} + +static int ext_disp_rx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = ext_disp_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + ucontrol->value.integer.value[0] = + ext_disp_rx_cfg[idx].channels - 2; + + pr_debug("%s: ext_disp_rx[%d].ch = %d\n", __func__, + idx, ext_disp_rx_cfg[idx].channels); + + return 0; +} + +static int ext_disp_rx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = ext_disp_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + ext_disp_rx_cfg[idx].channels = + ucontrol->value.integer.value[0] + 2; + + pr_debug("%s: ext_disp_rx[%d].ch = %d\n", __func__, + idx, ext_disp_rx_cfg[idx].channels); + return 1; +} + +static int ext_disp_rx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int sample_rate_val; + int idx = ext_disp_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + switch (ext_disp_rx_cfg[idx].sample_rate) { + case SAMPLING_RATE_176P4KHZ: + sample_rate_val = 6; + break; + + case SAMPLING_RATE_88P2KHZ: + sample_rate_val = 5; + break; + + case SAMPLING_RATE_44P1KHZ: + sample_rate_val = 4; + break; + + case SAMPLING_RATE_32KHZ: + sample_rate_val = 3; + break; + + case SAMPLING_RATE_192KHZ: + sample_rate_val = 2; + break; + + case SAMPLING_RATE_96KHZ: + sample_rate_val = 1; + break; + + case SAMPLING_RATE_48KHZ: + default: + sample_rate_val = 0; + break; + } + + ucontrol->value.integer.value[0] = sample_rate_val; + pr_debug("%s: ext_disp_rx[%d].sample_rate = %d\n", __func__, + idx, ext_disp_rx_cfg[idx].sample_rate); + + return 0; +} + +static int ext_disp_rx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = ext_disp_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + switch (ucontrol->value.integer.value[0]) { + case 6: + ext_disp_rx_cfg[idx].sample_rate = SAMPLING_RATE_176P4KHZ; + break; + case 5: + ext_disp_rx_cfg[idx].sample_rate = SAMPLING_RATE_88P2KHZ; + break; + case 4: + ext_disp_rx_cfg[idx].sample_rate = SAMPLING_RATE_44P1KHZ; + break; + case 3: + ext_disp_rx_cfg[idx].sample_rate = SAMPLING_RATE_32KHZ; + break; + case 2: + ext_disp_rx_cfg[idx].sample_rate = SAMPLING_RATE_192KHZ; + break; + case 1: + ext_disp_rx_cfg[idx].sample_rate = SAMPLING_RATE_96KHZ; + break; + case 0: + default: + ext_disp_rx_cfg[idx].sample_rate = SAMPLING_RATE_48KHZ; + break; + } + + pr_debug("%s: control value = %ld, ext_disp_rx[%d].sample_rate = %d\n", + __func__, ucontrol->value.integer.value[0], idx, + ext_disp_rx_cfg[idx].sample_rate); + return 0; +} + +static int proxy_rx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s: proxy_rx channels = %d\n", + __func__, proxy_rx_cfg.channels); + ucontrol->value.integer.value[0] = proxy_rx_cfg.channels - 2; + + return 0; +} + +static int proxy_rx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + proxy_rx_cfg.channels = ucontrol->value.integer.value[0] + 2; + pr_debug("%s: proxy_rx channels = %d\n", + __func__, proxy_rx_cfg.channels); + + return 1; +} + +static int tdm_get_sample_rate(int value) +{ + int sample_rate = 0; + + switch (value) { + case 0: + sample_rate = SAMPLING_RATE_8KHZ; + break; + case 1: + sample_rate = SAMPLING_RATE_16KHZ; + break; + case 2: + sample_rate = SAMPLING_RATE_32KHZ; + break; + case 3: + sample_rate = SAMPLING_RATE_48KHZ; + break; + case 4: + sample_rate = SAMPLING_RATE_176P4KHZ; + break; + case 5: + sample_rate = SAMPLING_RATE_352P8KHZ; + break; + default: + sample_rate = SAMPLING_RATE_48KHZ; + break; + } + return sample_rate; +} + +static int aux_pcm_get_sample_rate(int value) +{ + int sample_rate; + + switch (value) { + case 1: + sample_rate = SAMPLING_RATE_16KHZ; + break; + case 0: + default: + sample_rate = SAMPLING_RATE_8KHZ; + break; + } + return sample_rate; +} + +static int tdm_get_sample_rate_val(int sample_rate) +{ + int sample_rate_val = 0; + + switch (sample_rate) { + case SAMPLING_RATE_8KHZ: + sample_rate_val = 0; + break; + case SAMPLING_RATE_16KHZ: + sample_rate_val = 1; + break; + case SAMPLING_RATE_32KHZ: + sample_rate_val = 2; + break; + case SAMPLING_RATE_48KHZ: + sample_rate_val = 3; + break; + case SAMPLING_RATE_176P4KHZ: + sample_rate_val = 4; + break; + case SAMPLING_RATE_352P8KHZ: + sample_rate_val = 5; + break; + default: + sample_rate_val = 3; + break; + } + return sample_rate_val; +} + +static int aux_pcm_get_sample_rate_val(int sample_rate) +{ + int sample_rate_val; + + switch (sample_rate) { + case SAMPLING_RATE_16KHZ: + sample_rate_val = 1; + break; + case SAMPLING_RATE_8KHZ: + default: + sample_rate_val = 0; + break; + } + return sample_rate_val; +} + +static int tdm_get_mode(struct snd_kcontrol *kcontrol) +{ + int mode; + + if (strnstr(kcontrol->id.name, "PRI", + sizeof(kcontrol->id.name))) { + mode = TDM_PRI; + } else if (strnstr(kcontrol->id.name, "SEC", + sizeof(kcontrol->id.name))) { + mode = TDM_SEC; + } else if (strnstr(kcontrol->id.name, "TERT", + sizeof(kcontrol->id.name))) { + mode = TDM_TERT; + } else if (strnstr(kcontrol->id.name, "QUAT", + sizeof(kcontrol->id.name))) { + mode = TDM_QUAT; + } else if (strnstr(kcontrol->id.name, "QUIN", + sizeof(kcontrol->id.name))) { + mode = TDM_QUIN; + } else { + pr_err("%s: unsupported mode in: %s", + __func__, kcontrol->id.name); + mode = -EINVAL; + } + + return mode; +} + +static int tdm_get_channel(struct snd_kcontrol *kcontrol) +{ + int channel; + + if (strnstr(kcontrol->id.name, "RX_0", + sizeof(kcontrol->id.name)) || + strnstr(kcontrol->id.name, "TX_0", + sizeof(kcontrol->id.name))) { + channel = TDM_0; + } else if (strnstr(kcontrol->id.name, "RX_1", + sizeof(kcontrol->id.name)) || + strnstr(kcontrol->id.name, "TX_1", + sizeof(kcontrol->id.name))) { + channel = TDM_1; + } else if (strnstr(kcontrol->id.name, "RX_2", + sizeof(kcontrol->id.name)) || + strnstr(kcontrol->id.name, "TX_2", + sizeof(kcontrol->id.name))) { + channel = TDM_2; + } else if (strnstr(kcontrol->id.name, "RX_3", + sizeof(kcontrol->id.name)) || + strnstr(kcontrol->id.name, "TX_3", + sizeof(kcontrol->id.name))) { + channel = TDM_3; + } else if (strnstr(kcontrol->id.name, "RX_4", + sizeof(kcontrol->id.name)) || + strnstr(kcontrol->id.name, "TX_4", + sizeof(kcontrol->id.name))) { + channel = TDM_4; + } else if (strnstr(kcontrol->id.name, "RX_5", + sizeof(kcontrol->id.name)) || + strnstr(kcontrol->id.name, "TX_5", + sizeof(kcontrol->id.name))) { + channel = TDM_5; + } else if (strnstr(kcontrol->id.name, "RX_6", + sizeof(kcontrol->id.name)) || + strnstr(kcontrol->id.name, "TX_6", + sizeof(kcontrol->id.name))) { + channel = TDM_6; + } else if (strnstr(kcontrol->id.name, "RX_7", + sizeof(kcontrol->id.name)) || + strnstr(kcontrol->id.name, "TX_7", + sizeof(kcontrol->id.name))) { + channel = TDM_7; + } else { + pr_err("%s: unsupported channel in: %s", + __func__, kcontrol->id.name); + channel = -EINVAL; + } + + return channel; +} + +static int tdm_get_port_idx(struct snd_kcontrol *kcontrol, + struct tdm_port *port) +{ + if (port) { + port->mode = tdm_get_mode(kcontrol); + if (port->mode < 0) + return port->mode; + + port->channel = tdm_get_channel(kcontrol); + if (port->channel < 0) + return port->channel; + } else + return -EINVAL; + return 0; +} + +static int tdm_rx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + ucontrol->value.enumerated.item[0] = tdm_get_sample_rate_val( + tdm_rx_cfg[port.mode][port.channel].sample_rate); + + pr_debug("%s: tdm_rx_sample_rate = %d, item = %d\n", __func__, + tdm_rx_cfg[port.mode][port.channel].sample_rate, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_rx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + tdm_rx_cfg[port.mode][port.channel].sample_rate = + tdm_get_sample_rate(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: tdm_rx_sample_rate = %d, item = %d\n", __func__, + tdm_rx_cfg[port.mode][port.channel].sample_rate, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_tx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + ucontrol->value.enumerated.item[0] = tdm_get_sample_rate_val( + tdm_tx_cfg[port.mode][port.channel].sample_rate); + + pr_debug("%s: tdm_tx_sample_rate = %d, item = %d\n", __func__, + tdm_tx_cfg[port.mode][port.channel].sample_rate, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_tx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + tdm_tx_cfg[port.mode][port.channel].sample_rate = + tdm_get_sample_rate(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: tdm_tx_sample_rate = %d, item = %d\n", __func__, + tdm_tx_cfg[port.mode][port.channel].sample_rate, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_get_format(int value) +{ + int format = 0; + + switch (value) { + case 0: + format = SNDRV_PCM_FORMAT_S16_LE; + break; + case 1: + format = SNDRV_PCM_FORMAT_S24_LE; + break; + case 2: + format = SNDRV_PCM_FORMAT_S32_LE; + break; + default: + format = SNDRV_PCM_FORMAT_S16_LE; + break; + } + return format; +} + +static int tdm_get_format_val(int format) +{ + int value = 0; + + switch (format) { + case SNDRV_PCM_FORMAT_S16_LE: + value = 0; + break; + case SNDRV_PCM_FORMAT_S24_LE: + value = 1; + break; + case SNDRV_PCM_FORMAT_S32_LE: + value = 2; + break; + default: + value = 0; + break; + } + return value; +} + +static int tdm_rx_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + ucontrol->value.enumerated.item[0] = tdm_get_format_val( + tdm_rx_cfg[port.mode][port.channel].bit_format); + + pr_debug("%s: tdm_rx_bit_format = %d, item = %d\n", __func__, + tdm_rx_cfg[port.mode][port.channel].bit_format, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_rx_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + tdm_rx_cfg[port.mode][port.channel].bit_format = + tdm_get_format(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: tdm_rx_bit_format = %d, item = %d\n", __func__, + tdm_rx_cfg[port.mode][port.channel].bit_format, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_tx_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + ucontrol->value.enumerated.item[0] = tdm_get_format_val( + tdm_tx_cfg[port.mode][port.channel].bit_format); + + pr_debug("%s: tdm_tx_bit_format = %d, item = %d\n", __func__, + tdm_tx_cfg[port.mode][port.channel].bit_format, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_tx_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + tdm_tx_cfg[port.mode][port.channel].bit_format = + tdm_get_format(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: tdm_tx_bit_format = %d, item = %d\n", __func__, + tdm_tx_cfg[port.mode][port.channel].bit_format, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_rx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + + ucontrol->value.enumerated.item[0] = + tdm_rx_cfg[port.mode][port.channel].channels - 1; + + pr_debug("%s: tdm_rx_ch = %d, item = %d\n", __func__, + tdm_rx_cfg[port.mode][port.channel].channels - 1, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_rx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + tdm_rx_cfg[port.mode][port.channel].channels = + ucontrol->value.enumerated.item[0] + 1; + + pr_debug("%s: tdm_rx_ch = %d, item = %d\n", __func__, + tdm_rx_cfg[port.mode][port.channel].channels, + ucontrol->value.enumerated.item[0] + 1); + } + return ret; +} + +static int tdm_tx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + ucontrol->value.enumerated.item[0] = + tdm_tx_cfg[port.mode][port.channel].channels - 1; + + pr_debug("%s: tdm_tx_ch = %d, item = %d\n", __func__, + tdm_tx_cfg[port.mode][port.channel].channels - 1, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_tx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + tdm_tx_cfg[port.mode][port.channel].channels = + ucontrol->value.enumerated.item[0] + 1; + + pr_debug("%s: tdm_tx_ch = %d, item = %d\n", __func__, + tdm_tx_cfg[port.mode][port.channel].channels, + ucontrol->value.enumerated.item[0] + 1); + } + return ret; +} + +static int tdm_get_slot_num_val(int slot_num) +{ + int slot_num_val; + + switch (slot_num) { + case 1: + slot_num_val = 0; + break; + case 2: + slot_num_val = 1; + break; + case 4: + slot_num_val = 2; + break; + case 8: + slot_num_val = 3; + break; + case 16: + slot_num_val = 4; + break; + case 32: + slot_num_val = 5; + break; + default: + slot_num_val = 5; + break; + } + return slot_num_val; +} + +static int tdm_slot_num_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int mode = tdm_get_mode(kcontrol); + + if (mode < 0) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + return mode; + } + + ucontrol->value.enumerated.item[0] = + tdm_get_slot_num_val(tdm_slot[mode].num); + + pr_debug("%s: mode = %d, tdm_slot_num = %d, item = %d\n", __func__, + mode, tdm_slot[mode].num, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int tdm_get_slot_num(int value) +{ + int slot_num; + + switch (value) { + case 0: + slot_num = 1; + break; + case 1: + slot_num = 2; + break; + case 2: + slot_num = 4; + break; + case 3: + slot_num = 8; + break; + case 4: + slot_num = 16; + break; + case 5: + slot_num = 32; + break; + default: + slot_num = 8; + break; + } + return slot_num; +} + +static int tdm_slot_num_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int mode = tdm_get_mode(kcontrol); + + if (mode < 0) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + return mode; + } + + tdm_slot[mode].num = + tdm_get_slot_num(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: mode = %d, tdm_slot_num = %d, item = %d\n", __func__, + mode, tdm_slot[mode].num, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int tdm_get_slot_width_val(int slot_width) +{ + int slot_width_val; + + switch (slot_width) { + case 16: + slot_width_val = 0; + break; + case 24: + slot_width_val = 1; + break; + case 32: + slot_width_val = 2; + break; + default: + slot_width_val = 2; + break; + } + return slot_width_val; +} + +static int tdm_slot_width_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int mode = tdm_get_mode(kcontrol); + + if (mode < 0) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + return mode; + } + + ucontrol->value.enumerated.item[0] = + tdm_get_slot_width_val(tdm_slot[mode].width); + + pr_debug("%s: mode = %d, tdm_slot_width = %d, item = %d\n", __func__, + mode, tdm_slot[mode].width, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int tdm_get_slot_width(int value) +{ + int slot_width; + + switch (value) { + case 0: + slot_width = 16; + break; + case 1: + slot_width = 24; + break; + case 2: + slot_width = 32; + break; + default: + slot_width = 32; + break; + } + return slot_width; +} + +static int tdm_slot_width_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int mode = tdm_get_mode(kcontrol); + + if (mode < 0) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + return mode; + } + + tdm_slot[mode].width = + tdm_get_slot_width(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: mode = %d, tdm_slot_width = %d, item = %d\n", __func__, + mode, tdm_slot[mode].width, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int tdm_rx_slot_mapping_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + unsigned int *slot_offset; + int i; + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + slot_offset = tdm_rx_slot_offset[port.mode][port.channel]; + pr_debug("%s: mode = %d, channel = %d\n", + __func__, port.mode, port.channel); + for (i = 0; i < TDM_SLOT_OFFSET_MAX; i++) { + ucontrol->value.integer.value[i] = slot_offset[i]; + pr_debug("%s: offset %d, value %d\n", + __func__, i, slot_offset[i]); + } + } + return ret; +} + +static int tdm_rx_slot_mapping_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + unsigned int *slot_offset; + int i; + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + slot_offset = tdm_rx_slot_offset[port.mode][port.channel]; + pr_debug("%s: mode = %d, channel = %d\n", + __func__, port.mode, port.channel); + for (i = 0; i < TDM_SLOT_OFFSET_MAX; i++) { + slot_offset[i] = ucontrol->value.integer.value[i]; + pr_debug("%s: offset %d, value %d\n", + __func__, i, slot_offset[i]); + } + } + return ret; +} + +static int tdm_tx_slot_mapping_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + unsigned int *slot_offset; + int i; + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + slot_offset = tdm_tx_slot_offset[port.mode][port.channel]; + pr_debug("%s: mode = %d, channel = %d\n", + __func__, port.mode, port.channel); + for (i = 0; i < TDM_SLOT_OFFSET_MAX; i++) { + ucontrol->value.integer.value[i] = slot_offset[i]; + pr_debug("%s: offset %d, value %d\n", + __func__, i, slot_offset[i]); + } + } + return ret; +} + +static int tdm_tx_slot_mapping_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + unsigned int *slot_offset; + int i; + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + slot_offset = tdm_tx_slot_offset[port.mode][port.channel]; + pr_debug("%s: mode = %d, channel = %d\n", + __func__, port.mode, port.channel); + for (i = 0; i < TDM_SLOT_OFFSET_MAX; i++) { + slot_offset[i] = ucontrol->value.integer.value[i]; + pr_debug("%s: offset %d, value %d\n", + __func__, i, slot_offset[i]); + } + } + return ret; +} + +static int aux_pcm_get_port_idx(struct snd_kcontrol *kcontrol) +{ + int idx; + + if (strnstr(kcontrol->id.name, "PRIM_AUX_PCM", + sizeof("PRIM_AUX_PCM"))) + idx = PRIM_AUX_PCM; + else if (strnstr(kcontrol->id.name, "SEC_AUX_PCM", + sizeof("SEC_AUX_PCM"))) + idx = SEC_AUX_PCM; + else if (strnstr(kcontrol->id.name, "TERT_AUX_PCM", + sizeof("TERT_AUX_PCM"))) + idx = TERT_AUX_PCM; + else if (strnstr(kcontrol->id.name, "QUAT_AUX_PCM", + sizeof("QUAT_AUX_PCM"))) + idx = QUAT_AUX_PCM; + else if (strnstr(kcontrol->id.name, "QUIN_AUX_PCM", + sizeof("QUIN_AUX_PCM"))) + idx = QUIN_AUX_PCM; + else { + pr_err("%s: unsupported port: %s", + __func__, kcontrol->id.name); + idx = -EINVAL; + } + + return idx; +} + +static int aux_pcm_rx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = aux_pcm_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + aux_pcm_rx_cfg[idx].sample_rate = + aux_pcm_get_sample_rate(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: idx[%d]_rx_sample_rate = %d, item = %d\n", __func__, + idx, aux_pcm_rx_cfg[idx].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int aux_pcm_rx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = aux_pcm_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + ucontrol->value.enumerated.item[0] = + aux_pcm_get_sample_rate_val(aux_pcm_rx_cfg[idx].sample_rate); + + pr_debug("%s: idx[%d]_rx_sample_rate = %d, item = %d\n", __func__, + idx, aux_pcm_rx_cfg[idx].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int aux_pcm_tx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = aux_pcm_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + aux_pcm_tx_cfg[idx].sample_rate = + aux_pcm_get_sample_rate(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: idx[%d]_tx_sample_rate = %d, item = %d\n", __func__, + idx, aux_pcm_tx_cfg[idx].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int aux_pcm_tx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = aux_pcm_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + ucontrol->value.enumerated.item[0] = + aux_pcm_get_sample_rate_val(aux_pcm_tx_cfg[idx].sample_rate); + + pr_debug("%s: idx[%d]_tx_sample_rate = %d, item = %d\n", __func__, + idx, aux_pcm_tx_cfg[idx].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int mi2s_get_port_idx(struct snd_kcontrol *kcontrol) +{ + int idx; + + if (strnstr(kcontrol->id.name, "PRIM_MI2S_RX", + sizeof("PRIM_MI2S_RX"))) + idx = PRIM_MI2S; + else if (strnstr(kcontrol->id.name, "SEC_MI2S_RX", + sizeof("SEC_MI2S_RX"))) + idx = SEC_MI2S; + else if (strnstr(kcontrol->id.name, "TERT_MI2S_RX", + sizeof("TERT_MI2S_RX"))) + idx = TERT_MI2S; + else if (strnstr(kcontrol->id.name, "QUAT_MI2S_RX", + sizeof("QUAT_MI2S_RX"))) + idx = QUAT_MI2S; + else if (strnstr(kcontrol->id.name, "QUIN_MI2S_RX", + sizeof("QUIN_MI2S_RX"))) + idx = QUIN_MI2S; + else if (strnstr(kcontrol->id.name, "PRIM_MI2S_TX", + sizeof("PRIM_MI2S_TX"))) + idx = PRIM_MI2S; + else if (strnstr(kcontrol->id.name, "SEC_MI2S_TX", + sizeof("SEC_MI2S_TX"))) + idx = SEC_MI2S; + else if (strnstr(kcontrol->id.name, "TERT_MI2S_TX", + sizeof("TERT_MI2S_TX"))) + idx = TERT_MI2S; + else if (strnstr(kcontrol->id.name, "QUAT_MI2S_TX", + sizeof("QUAT_MI2S_TX"))) + idx = QUAT_MI2S; + else if (strnstr(kcontrol->id.name, "QUIN_MI2S_TX", + sizeof("QUIN_MI2S_TX"))) + idx = QUIN_MI2S; + else { + pr_err("%s: unsupported channel: %s", + __func__, kcontrol->id.name); + idx = -EINVAL; + } + + return idx; +} + +static int mi2s_get_sample_rate_val(int sample_rate) +{ + int sample_rate_val; + + switch (sample_rate) { + case SAMPLING_RATE_8KHZ: + sample_rate_val = 0; + break; + case SAMPLING_RATE_11P025KHZ: + sample_rate_val = 1; + break; + case SAMPLING_RATE_16KHZ: + sample_rate_val = 2; + break; + case SAMPLING_RATE_22P05KHZ: + sample_rate_val = 3; + break; + case SAMPLING_RATE_32KHZ: + sample_rate_val = 4; + break; + case SAMPLING_RATE_44P1KHZ: + sample_rate_val = 5; + break; + case SAMPLING_RATE_48KHZ: + sample_rate_val = 6; + break; + case SAMPLING_RATE_96KHZ: + sample_rate_val = 7; + break; + case SAMPLING_RATE_192KHZ: + sample_rate_val = 8; + break; + default: + sample_rate_val = 6; + break; + } + return sample_rate_val; +} + +static int mi2s_get_sample_rate(int value) +{ + int sample_rate; + + switch (value) { + case 0: + sample_rate = SAMPLING_RATE_8KHZ; + break; + case 1: + sample_rate = SAMPLING_RATE_11P025KHZ; + break; + case 2: + sample_rate = SAMPLING_RATE_16KHZ; + break; + case 3: + sample_rate = SAMPLING_RATE_22P05KHZ; + break; + case 4: + sample_rate = SAMPLING_RATE_32KHZ; + break; + case 5: + sample_rate = SAMPLING_RATE_44P1KHZ; + break; + case 6: + sample_rate = SAMPLING_RATE_48KHZ; + break; + case 7: + sample_rate = SAMPLING_RATE_96KHZ; + break; + case 8: + sample_rate = SAMPLING_RATE_192KHZ; + break; + default: + sample_rate = SAMPLING_RATE_48KHZ; + break; + } + return sample_rate; +} + +static int mi2s_auxpcm_get_format(int value) +{ + int format; + + switch (value) { + case 0: + format = SNDRV_PCM_FORMAT_S16_LE; + break; + case 1: + format = SNDRV_PCM_FORMAT_S24_LE; + break; + case 2: + format = SNDRV_PCM_FORMAT_S24_3LE; + break; + case 3: + format = SNDRV_PCM_FORMAT_S32_LE; + break; + default: + format = SNDRV_PCM_FORMAT_S16_LE; + break; + } + return format; +} + +static int mi2s_auxpcm_get_format_value(int format) +{ + int value; + + switch (format) { + case SNDRV_PCM_FORMAT_S16_LE: + value = 0; + break; + case SNDRV_PCM_FORMAT_S24_LE: + value = 1; + break; + case SNDRV_PCM_FORMAT_S24_3LE: + value = 2; + break; + case SNDRV_PCM_FORMAT_S32_LE: + value = 3; + break; + default: + value = 0; + break; + } + return value; +} + +static int mi2s_rx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + mi2s_rx_cfg[idx].sample_rate = + mi2s_get_sample_rate(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: idx[%d]_rx_sample_rate = %d, item = %d\n", __func__, + idx, mi2s_rx_cfg[idx].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int mi2s_rx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + ucontrol->value.enumerated.item[0] = + mi2s_get_sample_rate_val(mi2s_rx_cfg[idx].sample_rate); + + pr_debug("%s: idx[%d]_rx_sample_rate = %d, item = %d\n", __func__, + idx, mi2s_rx_cfg[idx].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int mi2s_tx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + mi2s_tx_cfg[idx].sample_rate = + mi2s_get_sample_rate(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: idx[%d]_tx_sample_rate = %d, item = %d\n", __func__, + idx, mi2s_tx_cfg[idx].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int mi2s_tx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + ucontrol->value.enumerated.item[0] = + mi2s_get_sample_rate_val(mi2s_tx_cfg[idx].sample_rate); + + pr_debug("%s: idx[%d]_tx_sample_rate = %d, item = %d\n", __func__, + idx, mi2s_tx_cfg[idx].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int msm_mi2s_rx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + pr_debug("%s: msm_mi2s_[%d]_rx_ch = %d\n", __func__, + idx, mi2s_rx_cfg[idx].channels); + ucontrol->value.enumerated.item[0] = mi2s_rx_cfg[idx].channels - 1; + + return 0; +} + +static int msm_mi2s_rx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + mi2s_rx_cfg[idx].channels = ucontrol->value.enumerated.item[0] + 1; + pr_debug("%s: msm_mi2s_[%d]_rx_ch = %d\n", __func__, + idx, mi2s_rx_cfg[idx].channels); + + return 1; +} + +static int msm_mi2s_tx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + pr_debug("%s: msm_mi2s_[%d]_tx_ch = %d\n", __func__, + idx, mi2s_tx_cfg[idx].channels); + ucontrol->value.enumerated.item[0] = mi2s_tx_cfg[idx].channels - 1; + + return 0; +} + +static int msm_mi2s_tx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + mi2s_tx_cfg[idx].channels = ucontrol->value.enumerated.item[0] + 1; + pr_debug("%s: msm_mi2s_[%d]_tx_ch = %d\n", __func__, + idx, mi2s_tx_cfg[idx].channels); + + return 1; +} + +static int msm_mi2s_rx_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + ucontrol->value.enumerated.item[0] = + mi2s_auxpcm_get_format_value(mi2s_rx_cfg[idx].bit_format); + + pr_debug("%s: idx[%d]_rx_format = %d, item = %d\n", __func__, + idx, mi2s_rx_cfg[idx].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int msm_mi2s_rx_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + mi2s_rx_cfg[idx].bit_format = + mi2s_auxpcm_get_format(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: idx[%d]_rx_format = %d, item = %d\n", __func__, + idx, mi2s_rx_cfg[idx].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int msm_mi2s_tx_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + ucontrol->value.enumerated.item[0] = + mi2s_auxpcm_get_format_value(mi2s_tx_cfg[idx].bit_format); + + pr_debug("%s: idx[%d]_tx_format = %d, item = %d\n", __func__, + idx, mi2s_tx_cfg[idx].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int msm_mi2s_tx_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + mi2s_tx_cfg[idx].bit_format = + mi2s_auxpcm_get_format(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: idx[%d]_tx_format = %d, item = %d\n", __func__, + idx, mi2s_tx_cfg[idx].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int msm_aux_pcm_rx_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = aux_pcm_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + ucontrol->value.enumerated.item[0] = + mi2s_auxpcm_get_format_value(aux_pcm_rx_cfg[idx].bit_format); + + pr_debug("%s: idx[%d]_rx_format = %d, item = %d\n", __func__, + idx, aux_pcm_rx_cfg[idx].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int msm_aux_pcm_rx_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = aux_pcm_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + aux_pcm_rx_cfg[idx].bit_format = + mi2s_auxpcm_get_format(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: idx[%d]_rx_format = %d, item = %d\n", __func__, + idx, aux_pcm_rx_cfg[idx].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int msm_aux_pcm_tx_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = aux_pcm_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + ucontrol->value.enumerated.item[0] = + mi2s_auxpcm_get_format_value(aux_pcm_tx_cfg[idx].bit_format); + + pr_debug("%s: idx[%d]_tx_format = %d, item = %d\n", __func__, + idx, aux_pcm_tx_cfg[idx].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int msm_aux_pcm_tx_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = aux_pcm_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + aux_pcm_tx_cfg[idx].bit_format = + mi2s_auxpcm_get_format(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: idx[%d]_tx_format = %d, item = %d\n", __func__, + idx, aux_pcm_tx_cfg[idx].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static const struct snd_kcontrol_new msm_snd_controls[] = { + SOC_ENUM_EXT("USB_AUDIO_RX Channels", usb_rx_chs, + usb_audio_rx_ch_get, usb_audio_rx_ch_put), + SOC_ENUM_EXT("USB_AUDIO_TX Channels", usb_tx_chs, + usb_audio_tx_ch_get, usb_audio_tx_ch_put), + SOC_ENUM_EXT("Display Port RX Channels", ext_disp_rx_chs, + ext_disp_rx_ch_get, ext_disp_rx_ch_put), + SOC_ENUM_EXT("PROXY_RX Channels", proxy_rx_chs, + proxy_rx_ch_get, proxy_rx_ch_put), + SOC_ENUM_EXT("USB_AUDIO_RX Format", usb_rx_format, + usb_audio_rx_format_get, usb_audio_rx_format_put), + SOC_ENUM_EXT("USB_AUDIO_TX Format", usb_tx_format, + usb_audio_tx_format_get, usb_audio_tx_format_put), + SOC_ENUM_EXT("Display Port RX Bit Format", ext_disp_rx_format, + ext_disp_rx_format_get, ext_disp_rx_format_put), + SOC_ENUM_EXT("USB_AUDIO_RX SampleRate", usb_rx_sample_rate, + usb_audio_rx_sample_rate_get, + usb_audio_rx_sample_rate_put), + SOC_ENUM_EXT("USB_AUDIO_TX SampleRate", usb_tx_sample_rate, + usb_audio_tx_sample_rate_get, + usb_audio_tx_sample_rate_put), + SOC_ENUM_EXT("Display Port RX SampleRate", ext_disp_rx_sample_rate, + ext_disp_rx_sample_rate_get, + ext_disp_rx_sample_rate_put), + SOC_ENUM_EXT("PRI_TDM_RX_0 SampleRate", tdm_rx_sample_rate, + tdm_rx_sample_rate_get, + tdm_rx_sample_rate_put), + SOC_ENUM_EXT("PRI_TDM_RX_1 SampleRate", tdm_rx_sample_rate, + tdm_rx_sample_rate_get, + tdm_rx_sample_rate_put), + SOC_ENUM_EXT("PRI_TDM_RX_2 SampleRate", tdm_rx_sample_rate, + tdm_rx_sample_rate_get, + tdm_rx_sample_rate_put), + SOC_ENUM_EXT("PRI_TDM_RX_3 SampleRate", tdm_rx_sample_rate, + tdm_rx_sample_rate_get, + tdm_rx_sample_rate_put), + SOC_ENUM_EXT("PRI_TDM_TX_0 SampleRate", tdm_tx_sample_rate, + tdm_tx_sample_rate_get, + tdm_tx_sample_rate_put), + SOC_ENUM_EXT("PRI_TDM_TX_1 SampleRate", tdm_tx_sample_rate, + tdm_tx_sample_rate_get, + tdm_tx_sample_rate_put), + SOC_ENUM_EXT("PRI_TDM_TX_2 SampleRate", tdm_tx_sample_rate, + tdm_tx_sample_rate_get, + tdm_tx_sample_rate_put), + SOC_ENUM_EXT("PRI_TDM_TX_3 SampleRate", tdm_tx_sample_rate, + tdm_tx_sample_rate_get, + tdm_tx_sample_rate_put), + SOC_ENUM_EXT("PRI_TDM_RX_0 Format", tdm_rx_format, + tdm_rx_format_get, + tdm_rx_format_put), + SOC_ENUM_EXT("PRI_TDM_RX_1 Format", tdm_rx_format, + tdm_rx_format_get, + tdm_rx_format_put), + SOC_ENUM_EXT("PRI_TDM_RX_2 Format", tdm_rx_format, + tdm_rx_format_get, + tdm_rx_format_put), + SOC_ENUM_EXT("PRI_TDM_RX_3 Format", tdm_rx_format, + tdm_rx_format_get, + tdm_rx_format_put), + SOC_ENUM_EXT("PRI_TDM_TX_0 Format", tdm_tx_format, + tdm_tx_format_get, + tdm_tx_format_put), + SOC_ENUM_EXT("PRI_TDM_TX_1 Format", tdm_tx_format, + tdm_tx_format_get, + tdm_tx_format_put), + SOC_ENUM_EXT("PRI_TDM_TX_2 Format", tdm_tx_format, + tdm_tx_format_get, + tdm_tx_format_put), + SOC_ENUM_EXT("PRI_TDM_TX_3 Format", tdm_tx_format, + tdm_tx_format_get, + tdm_tx_format_put), + SOC_ENUM_EXT("PRI_TDM_RX_0 Channels", tdm_rx_chs, + tdm_rx_ch_get, + tdm_rx_ch_put), + SOC_ENUM_EXT("PRI_TDM_RX_1 Channels", tdm_rx_chs, + tdm_rx_ch_get, + tdm_rx_ch_put), + SOC_ENUM_EXT("PRI_TDM_RX_2 Channels", tdm_rx_chs, + tdm_rx_ch_get, + tdm_rx_ch_put), + SOC_ENUM_EXT("PRI_TDM_RX_3 Channels", tdm_rx_chs, + tdm_rx_ch_get, + tdm_rx_ch_put), + SOC_ENUM_EXT("PRI_TDM_TX_0 Channels", tdm_tx_chs, + tdm_tx_ch_get, + tdm_tx_ch_put), + SOC_ENUM_EXT("PRI_TDM_TX_1 Channels", tdm_tx_chs, + tdm_tx_ch_get, + tdm_tx_ch_put), + SOC_ENUM_EXT("PRI_TDM_TX_2 Channels", tdm_tx_chs, + tdm_tx_ch_get, + tdm_tx_ch_put), + SOC_ENUM_EXT("PRI_TDM_TX_3 Channels", tdm_tx_chs, + tdm_tx_ch_get, + tdm_tx_ch_put), + SOC_ENUM_EXT("SEC_TDM_RX_0 SampleRate", tdm_rx_sample_rate, + tdm_rx_sample_rate_get, + tdm_rx_sample_rate_put), + SOC_ENUM_EXT("SEC_TDM_RX_1 SampleRate", tdm_rx_sample_rate, + tdm_rx_sample_rate_get, + tdm_rx_sample_rate_put), + SOC_ENUM_EXT("SEC_TDM_RX_2 SampleRate", tdm_rx_sample_rate, + tdm_rx_sample_rate_get, + tdm_rx_sample_rate_put), + SOC_ENUM_EXT("SEC_TDM_RX_3 SampleRate", tdm_rx_sample_rate, + tdm_rx_sample_rate_get, + tdm_rx_sample_rate_put), + SOC_ENUM_EXT("SEC_TDM_TX_0 SampleRate", tdm_tx_sample_rate, + tdm_tx_sample_rate_get, + tdm_tx_sample_rate_put), + SOC_ENUM_EXT("SEC_TDM_TX_1 SampleRate", tdm_tx_sample_rate, + tdm_tx_sample_rate_get, + tdm_tx_sample_rate_put), + SOC_ENUM_EXT("SEC_TDM_TX_2 SampleRate", tdm_tx_sample_rate, + tdm_tx_sample_rate_get, + tdm_tx_sample_rate_put), + SOC_ENUM_EXT("SEC_TDM_TX_3 SampleRate", tdm_tx_sample_rate, + tdm_tx_sample_rate_get, + tdm_tx_sample_rate_put), + SOC_ENUM_EXT("SEC_TDM_RX_0 Format", tdm_rx_format, + tdm_rx_format_get, + tdm_rx_format_put), + SOC_ENUM_EXT("SEC_TDM_RX_1 Format", tdm_rx_format, + tdm_rx_format_get, + tdm_rx_format_put), + SOC_ENUM_EXT("SEC_TDM_RX_2 Format", tdm_rx_format, + tdm_rx_format_get, + tdm_rx_format_put), + SOC_ENUM_EXT("SEC_TDM_RX_3 Format", tdm_rx_format, + tdm_rx_format_get, + tdm_rx_format_put), + SOC_ENUM_EXT("SEC_TDM_TX_0 Format", tdm_tx_format, + tdm_tx_format_get, + tdm_tx_format_put), + SOC_ENUM_EXT("SEC_TDM_TX_1 Format", tdm_tx_format, + tdm_tx_format_get, + tdm_tx_format_put), + SOC_ENUM_EXT("SEC_TDM_TX_2 Format", tdm_tx_format, + tdm_tx_format_get, + tdm_tx_format_put), + SOC_ENUM_EXT("SEC_TDM_TX_3 Format", tdm_tx_format, + tdm_tx_format_get, + tdm_tx_format_put), + SOC_ENUM_EXT("SEC_TDM_RX_0 Channels", tdm_rx_chs, + tdm_rx_ch_get, + tdm_rx_ch_put), + SOC_ENUM_EXT("SEC_TDM_RX_1 Channels", tdm_rx_chs, + tdm_rx_ch_get, + tdm_rx_ch_put), + SOC_ENUM_EXT("SEC_TDM_RX_2 Channels", tdm_rx_chs, + tdm_rx_ch_get, + tdm_rx_ch_put), + SOC_ENUM_EXT("SEC_TDM_RX_3 Channels", tdm_rx_chs, + tdm_rx_ch_get, + tdm_rx_ch_put), + SOC_ENUM_EXT("SEC_TDM_TX_0 Channels", tdm_tx_chs, + tdm_tx_ch_get, + tdm_tx_ch_put), + SOC_ENUM_EXT("SEC_TDM_TX_1 Channels", tdm_tx_chs, + tdm_tx_ch_get, + tdm_tx_ch_put), + SOC_ENUM_EXT("SEC_TDM_TX_2 Channels", tdm_tx_chs, + tdm_tx_ch_get, + tdm_tx_ch_put), + SOC_ENUM_EXT("SEC_TDM_TX_3 Channels", tdm_tx_chs, + tdm_tx_ch_get, + tdm_tx_ch_put), + SOC_ENUM_EXT("TERT_TDM_RX_0 SampleRate", tdm_rx_sample_rate, + tdm_rx_sample_rate_get, + tdm_rx_sample_rate_put), + SOC_ENUM_EXT("TERT_TDM_RX_1 SampleRate", tdm_rx_sample_rate, + tdm_rx_sample_rate_get, + tdm_rx_sample_rate_put), + SOC_ENUM_EXT("TERT_TDM_RX_2 SampleRate", tdm_rx_sample_rate, + tdm_rx_sample_rate_get, + tdm_rx_sample_rate_put), + SOC_ENUM_EXT("TERT_TDM_RX_3 SampleRate", tdm_rx_sample_rate, + tdm_rx_sample_rate_get, + tdm_rx_sample_rate_put), + SOC_ENUM_EXT("TERT_TDM_RX_4 SampleRate", tdm_rx_sample_rate, + tdm_rx_sample_rate_get, + tdm_rx_sample_rate_put), + SOC_ENUM_EXT("TERT_TDM_TX_0 SampleRate", tdm_tx_sample_rate, + tdm_tx_sample_rate_get, + tdm_tx_sample_rate_put), + SOC_ENUM_EXT("TERT_TDM_TX_1 SampleRate", tdm_tx_sample_rate, + tdm_tx_sample_rate_get, + tdm_tx_sample_rate_put), + SOC_ENUM_EXT("TERT_TDM_TX_2 SampleRate", tdm_tx_sample_rate, + tdm_tx_sample_rate_get, + tdm_tx_sample_rate_put), + SOC_ENUM_EXT("TERT_TDM_TX_3 SampleRate", tdm_tx_sample_rate, + tdm_tx_sample_rate_get, + tdm_tx_sample_rate_put), + SOC_ENUM_EXT("TERT_TDM_RX_0 Format", tdm_rx_format, + tdm_rx_format_get, + tdm_rx_format_put), + SOC_ENUM_EXT("TERT_TDM_RX_1 Format", tdm_rx_format, + tdm_rx_format_get, + tdm_rx_format_put), + SOC_ENUM_EXT("TERT_TDM_RX_2 Format", tdm_rx_format, + tdm_rx_format_get, + tdm_rx_format_put), + SOC_ENUM_EXT("TERT_TDM_RX_3 Format", tdm_rx_format, + tdm_rx_format_get, + tdm_rx_format_put), + SOC_ENUM_EXT("TERT_TDM_RX_4 Format", tdm_rx_format, + tdm_rx_format_get, + tdm_rx_format_put), + SOC_ENUM_EXT("TERT_TDM_TX_0 Format", tdm_tx_format, + tdm_tx_format_get, + tdm_tx_format_put), + SOC_ENUM_EXT("TERT_TDM_TX_1 Format", tdm_tx_format, + tdm_tx_format_get, + tdm_tx_format_put), + SOC_ENUM_EXT("TERT_TDM_TX_2 Format", tdm_tx_format, + tdm_tx_format_get, + tdm_tx_format_put), + SOC_ENUM_EXT("TERT_TDM_TX_3 Format", tdm_tx_format, + tdm_tx_format_get, + tdm_tx_format_put), + SOC_ENUM_EXT("TERT_TDM_RX_0 Channels", tdm_rx_chs, + tdm_rx_ch_get, + tdm_rx_ch_put), + SOC_ENUM_EXT("TERT_TDM_RX_1 Channels", tdm_rx_chs, + tdm_rx_ch_get, + tdm_rx_ch_put), + SOC_ENUM_EXT("TERT_TDM_RX_2 Channels", tdm_rx_chs, + tdm_rx_ch_get, + tdm_rx_ch_put), + SOC_ENUM_EXT("TERT_TDM_RX_3 Channels", tdm_rx_chs, + tdm_rx_ch_get, + tdm_rx_ch_put), + SOC_ENUM_EXT("TERT_TDM_RX_4 Channels", tdm_rx_chs, + tdm_rx_ch_get, + tdm_rx_ch_put), + SOC_ENUM_EXT("TERT_TDM_TX_0 Channels", tdm_tx_chs, + tdm_tx_ch_get, + tdm_tx_ch_put), + SOC_ENUM_EXT("TERT_TDM_TX_1 Channels", tdm_tx_chs, + tdm_tx_ch_get, + tdm_tx_ch_put), + SOC_ENUM_EXT("TERT_TDM_TX_2 Channels", tdm_tx_chs, + tdm_tx_ch_get, + tdm_tx_ch_put), + SOC_ENUM_EXT("TERT_TDM_TX_3 Channels", tdm_tx_chs, + tdm_tx_ch_get, + tdm_tx_ch_put), + SOC_ENUM_EXT("QUAT_TDM_RX_0 SampleRate", tdm_rx_sample_rate, + tdm_rx_sample_rate_get, + tdm_rx_sample_rate_put), + SOC_ENUM_EXT("QUAT_TDM_RX_1 SampleRate", tdm_rx_sample_rate, + tdm_rx_sample_rate_get, + tdm_rx_sample_rate_put), + SOC_ENUM_EXT("QUAT_TDM_RX_2 SampleRate", tdm_rx_sample_rate, + tdm_rx_sample_rate_get, + tdm_rx_sample_rate_put), + SOC_ENUM_EXT("QUAT_TDM_RX_3 SampleRate", tdm_rx_sample_rate, + tdm_rx_sample_rate_get, + tdm_rx_sample_rate_put), + SOC_ENUM_EXT("QUAT_TDM_TX_0 SampleRate", tdm_tx_sample_rate, + tdm_tx_sample_rate_get, + tdm_tx_sample_rate_put), + SOC_ENUM_EXT("QUAT_TDM_TX_1 SampleRate", tdm_tx_sample_rate, + tdm_tx_sample_rate_get, + tdm_tx_sample_rate_put), + SOC_ENUM_EXT("QUAT_TDM_TX_2 SampleRate", tdm_tx_sample_rate, + tdm_tx_sample_rate_get, + tdm_tx_sample_rate_put), + SOC_ENUM_EXT("QUAT_TDM_TX_3 SampleRate", tdm_tx_sample_rate, + tdm_tx_sample_rate_get, + tdm_tx_sample_rate_put), + SOC_ENUM_EXT("QUAT_TDM_RX_0 Format", tdm_rx_format, + tdm_rx_format_get, + tdm_rx_format_put), + SOC_ENUM_EXT("QUAT_TDM_RX_1 Format", tdm_rx_format, + tdm_rx_format_get, + tdm_rx_format_put), + SOC_ENUM_EXT("QUAT_TDM_RX_2 Format", tdm_rx_format, + tdm_rx_format_get, + tdm_rx_format_put), + SOC_ENUM_EXT("QUAT_TDM_RX_3 Format", tdm_rx_format, + tdm_rx_format_get, + tdm_rx_format_put), + SOC_ENUM_EXT("QUAT_TDM_TX_0 Format", tdm_tx_format, + tdm_tx_format_get, + tdm_tx_format_put), + SOC_ENUM_EXT("QUAT_TDM_TX_1 Format", tdm_tx_format, + tdm_tx_format_get, + tdm_tx_format_put), + SOC_ENUM_EXT("QUAT_TDM_TX_2 Format", tdm_tx_format, + tdm_tx_format_get, + tdm_tx_format_put), + SOC_ENUM_EXT("QUAT_TDM_TX_3 Format", tdm_tx_format, + tdm_tx_format_get, + tdm_tx_format_put), + SOC_ENUM_EXT("QUAT_TDM_RX_0 Channels", tdm_rx_chs, + tdm_rx_ch_get, + tdm_rx_ch_put), + SOC_ENUM_EXT("QUAT_TDM_RX_1 Channels", tdm_rx_chs, + tdm_rx_ch_get, + tdm_rx_ch_put), + SOC_ENUM_EXT("QUAT_TDM_RX_2 Channels", tdm_rx_chs, + tdm_rx_ch_get, + tdm_rx_ch_put), + SOC_ENUM_EXT("QUAT_TDM_RX_3 Channels", tdm_rx_chs, + tdm_rx_ch_get, + tdm_rx_ch_put), + SOC_ENUM_EXT("QUAT_TDM_TX_0 Channels", tdm_tx_chs, + tdm_tx_ch_get, + tdm_tx_ch_put), + SOC_ENUM_EXT("QUAT_TDM_TX_1 Channels", tdm_tx_chs, + tdm_tx_ch_get, + tdm_tx_ch_put), + SOC_ENUM_EXT("QUAT_TDM_TX_2 Channels", tdm_tx_chs, + tdm_tx_ch_get, + tdm_tx_ch_put), + SOC_ENUM_EXT("QUAT_TDM_TX_3 Channels", tdm_tx_chs, + tdm_tx_ch_get, + tdm_tx_ch_put), + SOC_ENUM_EXT("QUIN_TDM_RX_0 SampleRate", tdm_rx_sample_rate, + tdm_rx_sample_rate_get, + tdm_rx_sample_rate_put), + SOC_ENUM_EXT("QUIN_TDM_RX_1 SampleRate", tdm_rx_sample_rate, + tdm_rx_sample_rate_get, + tdm_rx_sample_rate_put), + SOC_ENUM_EXT("QUIN_TDM_RX_2 SampleRate", tdm_rx_sample_rate, + tdm_rx_sample_rate_get, + tdm_rx_sample_rate_put), + SOC_ENUM_EXT("QUIN_TDM_RX_3 SampleRate", tdm_rx_sample_rate, + tdm_rx_sample_rate_get, + tdm_rx_sample_rate_put), + SOC_ENUM_EXT("QUIN_TDM_TX_0 SampleRate", tdm_tx_sample_rate, + tdm_tx_sample_rate_get, + tdm_tx_sample_rate_put), + SOC_ENUM_EXT("QUIN_TDM_TX_1 SampleRate", tdm_tx_sample_rate, + tdm_tx_sample_rate_get, + tdm_tx_sample_rate_put), + SOC_ENUM_EXT("QUIN_TDM_TX_2 SampleRate", tdm_tx_sample_rate, + tdm_tx_sample_rate_get, + tdm_tx_sample_rate_put), + SOC_ENUM_EXT("QUIN_TDM_TX_3 SampleRate", tdm_tx_sample_rate, + tdm_tx_sample_rate_get, + tdm_tx_sample_rate_put), + SOC_ENUM_EXT("QUIN_TDM_RX_0 Format", tdm_rx_format, + tdm_rx_format_get, + tdm_rx_format_put), + SOC_ENUM_EXT("QUIN_TDM_RX_1 Format", tdm_rx_format, + tdm_rx_format_get, + tdm_rx_format_put), + SOC_ENUM_EXT("QUIN_TDM_RX_2 Format", tdm_rx_format, + tdm_rx_format_get, + tdm_rx_format_put), + SOC_ENUM_EXT("QUIN_TDM_RX_3 Format", tdm_rx_format, + tdm_rx_format_get, + tdm_rx_format_put), + SOC_ENUM_EXT("QUIN_TDM_TX_0 Format", tdm_tx_format, + tdm_tx_format_get, + tdm_tx_format_put), + SOC_ENUM_EXT("QUIN_TDM_TX_1 Format", tdm_tx_format, + tdm_tx_format_get, + tdm_tx_format_put), + SOC_ENUM_EXT("QUIN_TDM_TX_2 Format", tdm_tx_format, + tdm_tx_format_get, + tdm_tx_format_put), + SOC_ENUM_EXT("QUIN_TDM_TX_3 Format", tdm_tx_format, + tdm_tx_format_get, + tdm_tx_format_put), + SOC_ENUM_EXT("QUIN_TDM_RX_0 Channels", tdm_rx_chs, + tdm_rx_ch_get, + tdm_rx_ch_put), + SOC_ENUM_EXT("QUIN_TDM_RX_1 Channels", tdm_rx_chs, + tdm_rx_ch_get, + tdm_rx_ch_put), + SOC_ENUM_EXT("QUIN_TDM_RX_2 Channels", tdm_rx_chs, + tdm_rx_ch_get, + tdm_rx_ch_put), + SOC_ENUM_EXT("QUIN_TDM_RX_3 Channels", tdm_rx_chs, + tdm_rx_ch_get, + tdm_rx_ch_put), + SOC_ENUM_EXT("QUIN_TDM_TX_0 Channels", tdm_tx_chs, + tdm_tx_ch_get, + tdm_tx_ch_put), + SOC_ENUM_EXT("QUIN_TDM_TX_1 Channels", tdm_tx_chs, + tdm_tx_ch_get, + tdm_tx_ch_put), + SOC_ENUM_EXT("QUIN_TDM_TX_2 Channels", tdm_tx_chs, + tdm_tx_ch_get, + tdm_tx_ch_put), + SOC_ENUM_EXT("QUIN_TDM_TX_3 Channels", tdm_tx_chs, + tdm_tx_ch_get, + tdm_tx_ch_put), + SOC_ENUM_EXT("PRI_TDM SlotNumber", tdm_slot_num, + tdm_slot_num_get, tdm_slot_num_put), + SOC_ENUM_EXT("PRI_TDM SlotWidth", tdm_slot_width, + tdm_slot_width_get, tdm_slot_width_put), + SOC_ENUM_EXT("SEC_TDM SlotNumber", tdm_slot_num, + tdm_slot_num_get, tdm_slot_num_put), + SOC_ENUM_EXT("SEC_TDM SlotWidth", tdm_slot_width, + tdm_slot_width_get, tdm_slot_width_put), + SOC_ENUM_EXT("TERT_TDM SlotNumber", tdm_slot_num, + tdm_slot_num_get, tdm_slot_num_put), + SOC_ENUM_EXT("TERT_TDM SlotWidth", tdm_slot_width, + tdm_slot_width_get, tdm_slot_width_put), + SOC_ENUM_EXT("QUAT_TDM SlotNumber", tdm_slot_num, + tdm_slot_num_get, tdm_slot_num_put), + SOC_ENUM_EXT("QUAT_TDM SlotWidth", tdm_slot_width, + tdm_slot_width_get, tdm_slot_width_put), + SOC_ENUM_EXT("QUIN_TDM SlotNumber", tdm_slot_num, + tdm_slot_num_get, tdm_slot_num_put), + SOC_ENUM_EXT("QUIN_TDM SlotWidth", tdm_slot_width, + tdm_slot_width_get, tdm_slot_width_put), + SOC_SINGLE_MULTI_EXT("PRI_TDM_RX_0 SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_rx_slot_mapping_get, tdm_rx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("PRI_TDM_RX_1 SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_rx_slot_mapping_get, tdm_rx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("PRI_TDM_RX_2 SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_rx_slot_mapping_get, tdm_rx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("PRI_TDM_RX_3 SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_rx_slot_mapping_get, tdm_rx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("PRI_TDM_TX_0 SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_tx_slot_mapping_get, tdm_tx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("PRI_TDM_TX_1 SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_tx_slot_mapping_get, tdm_tx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("PRI_TDM_TX_2 SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_tx_slot_mapping_get, tdm_tx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("PRI_TDM_TX_3 SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_tx_slot_mapping_get, tdm_tx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("SEC_TDM_RX_0 SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_rx_slot_mapping_get, tdm_rx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("SEC_TDM_RX_1 SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_rx_slot_mapping_get, tdm_rx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("SEC_TDM_RX_2 SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_rx_slot_mapping_get, tdm_rx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("SEC_TDM_RX_3 SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_rx_slot_mapping_get, tdm_rx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("SEC_TDM_TX_0 SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_tx_slot_mapping_get, tdm_tx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("SEC_TDM_TX_1 SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_tx_slot_mapping_get, tdm_tx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("SEC_TDM_TX_2 SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_tx_slot_mapping_get, tdm_tx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("SEC_TDM_TX_3 SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_tx_slot_mapping_get, tdm_tx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("TERT_TDM_RX_0 SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_rx_slot_mapping_get, tdm_rx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("TERT_TDM_RX_1 SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_rx_slot_mapping_get, tdm_rx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("TERT_TDM_RX_2 SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_rx_slot_mapping_get, tdm_rx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("TERT_TDM_RX_3 SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_rx_slot_mapping_get, tdm_rx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("TERT_TDM_RX_4 SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_rx_slot_mapping_get, tdm_rx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("TERT_TDM_TX_0 SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_tx_slot_mapping_get, tdm_tx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("TERT_TDM_TX_1 SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_tx_slot_mapping_get, tdm_tx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("TERT_TDM_TX_2 SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_tx_slot_mapping_get, tdm_tx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("TERT_TDM_TX_3 SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_tx_slot_mapping_get, tdm_tx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("QUAT_TDM_RX_0 SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_rx_slot_mapping_get, tdm_rx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("QUAT_TDM_RX_1 SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_rx_slot_mapping_get, tdm_rx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("QUAT_TDM_RX_2 SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_rx_slot_mapping_get, tdm_rx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("QUAT_TDM_RX_3 SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_rx_slot_mapping_get, tdm_rx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("QUAT_TDM_TX_0 SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_tx_slot_mapping_get, tdm_tx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("QUAT_TDM_TX_1 SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_tx_slot_mapping_get, tdm_tx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("QUAT_TDM_TX_2 SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_tx_slot_mapping_get, tdm_tx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("QUAT_TDM_TX_3 SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_tx_slot_mapping_get, tdm_tx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("QUIN_TDM_RX_0 SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_rx_slot_mapping_get, tdm_rx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("QUIN_TDM_RX_1 SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_rx_slot_mapping_get, tdm_rx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("QUIN_TDM_RX_2 SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_rx_slot_mapping_get, tdm_rx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("QUIN_TDM_RX_3 SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_rx_slot_mapping_get, tdm_rx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("QUIN_TDM_TX_0 SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_tx_slot_mapping_get, tdm_tx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("QUIN_TDM_TX_1 SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_tx_slot_mapping_get, tdm_tx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("QUIN_TDM_TX_2 SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_tx_slot_mapping_get, tdm_tx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("QUIN_TDM_TX_3 SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_tx_slot_mapping_get, tdm_tx_slot_mapping_put), + SOC_ENUM_EXT("PRIM_AUX_PCM_RX SampleRate", prim_aux_pcm_rx_sample_rate, + aux_pcm_rx_sample_rate_get, + aux_pcm_rx_sample_rate_put), + SOC_ENUM_EXT("SEC_AUX_PCM_RX SampleRate", sec_aux_pcm_rx_sample_rate, + aux_pcm_rx_sample_rate_get, + aux_pcm_rx_sample_rate_put), + SOC_ENUM_EXT("TERT_AUX_PCM_RX SampleRate", tert_aux_pcm_rx_sample_rate, + aux_pcm_rx_sample_rate_get, + aux_pcm_rx_sample_rate_put), + SOC_ENUM_EXT("QUAT_AUX_PCM_RX SampleRate", quat_aux_pcm_rx_sample_rate, + aux_pcm_rx_sample_rate_get, + aux_pcm_rx_sample_rate_put), + SOC_ENUM_EXT("QUIN_AUX_PCM_RX SampleRate", quin_aux_pcm_rx_sample_rate, + aux_pcm_rx_sample_rate_get, + aux_pcm_rx_sample_rate_put), + SOC_ENUM_EXT("PRIM_AUX_PCM_TX SampleRate", prim_aux_pcm_tx_sample_rate, + aux_pcm_tx_sample_rate_get, + aux_pcm_tx_sample_rate_put), + SOC_ENUM_EXT("SEC_AUX_PCM_TX SampleRate", sec_aux_pcm_tx_sample_rate, + aux_pcm_tx_sample_rate_get, + aux_pcm_tx_sample_rate_put), + SOC_ENUM_EXT("TERT_AUX_PCM_TX SampleRate", tert_aux_pcm_tx_sample_rate, + aux_pcm_tx_sample_rate_get, + aux_pcm_tx_sample_rate_put), + SOC_ENUM_EXT("QUAT_AUX_PCM_TX SampleRate", quat_aux_pcm_tx_sample_rate, + aux_pcm_tx_sample_rate_get, + aux_pcm_tx_sample_rate_put), + SOC_ENUM_EXT("QUIN_AUX_PCM_TX SampleRate", quin_aux_pcm_tx_sample_rate, + aux_pcm_tx_sample_rate_get, + aux_pcm_tx_sample_rate_put), + SOC_ENUM_EXT("PRIM_MI2S_RX SampleRate", prim_mi2s_rx_sample_rate, + mi2s_rx_sample_rate_get, + mi2s_rx_sample_rate_put), + SOC_ENUM_EXT("SEC_MI2S_RX SampleRate", sec_mi2s_rx_sample_rate, + mi2s_rx_sample_rate_get, + mi2s_rx_sample_rate_put), + SOC_ENUM_EXT("TERT_MI2S_RX SampleRate", tert_mi2s_rx_sample_rate, + mi2s_rx_sample_rate_get, + mi2s_rx_sample_rate_put), + SOC_ENUM_EXT("QUAT_MI2S_RX SampleRate", quat_mi2s_rx_sample_rate, + mi2s_rx_sample_rate_get, + mi2s_rx_sample_rate_put), + SOC_ENUM_EXT("QUIN_MI2S_RX SampleRate", quin_mi2s_rx_sample_rate, + mi2s_rx_sample_rate_get, + mi2s_rx_sample_rate_put), + SOC_ENUM_EXT("PRIM_MI2S_TX SampleRate", prim_mi2s_tx_sample_rate, + mi2s_tx_sample_rate_get, + mi2s_tx_sample_rate_put), + SOC_ENUM_EXT("SEC_MI2S_TX SampleRate", sec_mi2s_tx_sample_rate, + mi2s_tx_sample_rate_get, + mi2s_tx_sample_rate_put), + SOC_ENUM_EXT("TERT_MI2S_TX SampleRate", tert_mi2s_tx_sample_rate, + mi2s_tx_sample_rate_get, + mi2s_tx_sample_rate_put), + SOC_ENUM_EXT("QUAT_MI2S_TX SampleRate", quat_mi2s_tx_sample_rate, + mi2s_tx_sample_rate_get, + mi2s_tx_sample_rate_put), + SOC_ENUM_EXT("QUIN_MI2S_TX SampleRate", quin_mi2s_tx_sample_rate, + mi2s_tx_sample_rate_get, + mi2s_tx_sample_rate_put), + SOC_ENUM_EXT("PRIM_MI2S_RX Channels", prim_mi2s_rx_chs, + msm_mi2s_rx_ch_get, msm_mi2s_rx_ch_put), + SOC_ENUM_EXT("PRIM_MI2S_TX Channels", prim_mi2s_tx_chs, + msm_mi2s_tx_ch_get, msm_mi2s_tx_ch_put), + SOC_ENUM_EXT("SEC_MI2S_RX Channels", sec_mi2s_rx_chs, + msm_mi2s_rx_ch_get, msm_mi2s_rx_ch_put), + SOC_ENUM_EXT("SEC_MI2S_TX Channels", sec_mi2s_tx_chs, + msm_mi2s_tx_ch_get, msm_mi2s_tx_ch_put), + SOC_ENUM_EXT("TERT_MI2S_RX Channels", tert_mi2s_rx_chs, + msm_mi2s_rx_ch_get, msm_mi2s_rx_ch_put), + SOC_ENUM_EXT("TERT_MI2S_TX Channels", tert_mi2s_tx_chs, + msm_mi2s_tx_ch_get, msm_mi2s_tx_ch_put), + SOC_ENUM_EXT("QUAT_MI2S_RX Channels", quat_mi2s_rx_chs, + msm_mi2s_rx_ch_get, msm_mi2s_rx_ch_put), + SOC_ENUM_EXT("QUAT_MI2S_TX Channels", quat_mi2s_tx_chs, + msm_mi2s_tx_ch_get, msm_mi2s_tx_ch_put), + SOC_ENUM_EXT("QUIN_MI2S_RX Channels", quin_mi2s_rx_chs, + msm_mi2s_rx_ch_get, msm_mi2s_rx_ch_put), + SOC_ENUM_EXT("QUIN_MI2S_TX Channels", quin_mi2s_tx_chs, + msm_mi2s_tx_ch_get, msm_mi2s_tx_ch_put), + SOC_ENUM_EXT("PRIM_MI2S_RX Format", mi2s_rx_format, + msm_mi2s_rx_format_get, msm_mi2s_rx_format_put), + SOC_ENUM_EXT("PRIM_MI2S_TX Format", mi2s_tx_format, + msm_mi2s_tx_format_get, msm_mi2s_tx_format_put), + SOC_ENUM_EXT("SEC_MI2S_RX Format", mi2s_rx_format, + msm_mi2s_rx_format_get, msm_mi2s_rx_format_put), + SOC_ENUM_EXT("SEC_MI2S_TX Format", mi2s_tx_format, + msm_mi2s_tx_format_get, msm_mi2s_tx_format_put), + SOC_ENUM_EXT("TERT_MI2S_RX Format", mi2s_rx_format, + msm_mi2s_rx_format_get, msm_mi2s_rx_format_put), + SOC_ENUM_EXT("TERT_MI2S_TX Format", mi2s_tx_format, + msm_mi2s_tx_format_get, msm_mi2s_tx_format_put), + SOC_ENUM_EXT("QUAT_MI2S_RX Format", mi2s_rx_format, + msm_mi2s_rx_format_get, msm_mi2s_rx_format_put), + SOC_ENUM_EXT("QUAT_MI2S_TX Format", mi2s_tx_format, + msm_mi2s_tx_format_get, msm_mi2s_tx_format_put), + SOC_ENUM_EXT("QUIN_MI2S_RX Format", mi2s_rx_format, + msm_mi2s_rx_format_get, msm_mi2s_rx_format_put), + SOC_ENUM_EXT("QUIN_MI2S_TX Format", mi2s_tx_format, + msm_mi2s_tx_format_get, msm_mi2s_tx_format_put), + SOC_ENUM_EXT("PRIM_AUX_PCM_RX Format", aux_pcm_rx_format, + msm_aux_pcm_rx_format_get, msm_aux_pcm_rx_format_put), + SOC_ENUM_EXT("PRIM_AUX_PCM_TX Format", aux_pcm_tx_format, + msm_aux_pcm_tx_format_get, msm_aux_pcm_tx_format_put), + SOC_ENUM_EXT("SEC_AUX_PCM_RX Format", aux_pcm_rx_format, + msm_aux_pcm_rx_format_get, msm_aux_pcm_rx_format_put), + SOC_ENUM_EXT("SEC_AUX_PCM_TX Format", aux_pcm_tx_format, + msm_aux_pcm_tx_format_get, msm_aux_pcm_tx_format_put), + SOC_ENUM_EXT("TERT_AUX_PCM_RX Format", aux_pcm_rx_format, + msm_aux_pcm_rx_format_get, msm_aux_pcm_rx_format_put), + SOC_ENUM_EXT("TERT_AUX_PCM_TX Format", aux_pcm_tx_format, + msm_aux_pcm_tx_format_get, msm_aux_pcm_tx_format_put), + SOC_ENUM_EXT("QUAT_AUX_PCM_RX Format", aux_pcm_rx_format, + msm_aux_pcm_rx_format_get, msm_aux_pcm_rx_format_put), + SOC_ENUM_EXT("QUAT_AUX_PCM_TX Format", aux_pcm_tx_format, + msm_aux_pcm_tx_format_get, msm_aux_pcm_tx_format_put), + SOC_ENUM_EXT("QUIN_AUX_PCM_RX Format", aux_pcm_rx_format, + msm_aux_pcm_rx_format_get, msm_aux_pcm_rx_format_put), + SOC_ENUM_EXT("QUIN_AUX_PCM_TX Format", aux_pcm_tx_format, + msm_aux_pcm_tx_format_get, msm_aux_pcm_tx_format_put), +}; + +static inline int param_is_mask(int p) +{ + return (p >= SNDRV_PCM_HW_PARAM_FIRST_MASK) && + (p <= SNDRV_PCM_HW_PARAM_LAST_MASK); +} + +static inline struct snd_mask *param_to_mask(struct snd_pcm_hw_params *p, + int n) +{ + return &(p->masks[n - SNDRV_PCM_HW_PARAM_FIRST_MASK]); +} + +static void param_set_mask(struct snd_pcm_hw_params *p, int n, + unsigned int bit) +{ + if (bit >= SNDRV_MASK_MAX) + return; + if (param_is_mask(n)) { + struct snd_mask *m = param_to_mask(p, n); + + m->bits[0] = 0; + m->bits[1] = 0; + m->bits[bit >> 5] |= (1 << (bit & 31)); + } +} + +static int msm_ext_disp_get_idx_from_beid(int32_t be_id) +{ + int idx; + + switch (be_id) { + case MSM_BACKEND_DAI_DISPLAY_PORT_RX: + idx = DP_RX_IDX; + break; + default: + pr_err("%s: Incorrect ext_disp BE id %d\n", __func__, be_id); + idx = -EINVAL; + break; + } + + return idx; +} + +static int msm_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_dai_link *dai_link = rtd->dai_link; + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + int rc = 0; + int idx; + + pr_debug("%s: format = %d, rate = %d\n", + __func__, params_format(params), params_rate(params)); + + switch (dai_link->id) { + case MSM_BACKEND_DAI_USB_RX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + usb_rx_cfg.bit_format); + rate->min = rate->max = usb_rx_cfg.sample_rate; + channels->min = channels->max = usb_rx_cfg.channels; + break; + + case MSM_BACKEND_DAI_USB_TX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + usb_tx_cfg.bit_format); + rate->min = rate->max = usb_tx_cfg.sample_rate; + channels->min = channels->max = usb_tx_cfg.channels; + break; + + case MSM_BACKEND_DAI_DISPLAY_PORT_RX: + idx = msm_ext_disp_get_idx_from_beid(dai_link->id); + if (idx < 0) { + pr_err("%s: Incorrect ext disp idx %d\n", + __func__, idx); + rc = idx; + goto done; + } + + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + ext_disp_rx_cfg[idx].bit_format); + rate->min = rate->max = ext_disp_rx_cfg[idx].sample_rate; + channels->min = channels->max = ext_disp_rx_cfg[idx].channels; + break; + + case MSM_BACKEND_DAI_AFE_PCM_RX: + channels->min = channels->max = proxy_rx_cfg.channels; + rate->min = rate->max = SAMPLING_RATE_48KHZ; + break; + + case MSM_BACKEND_DAI_PRI_TDM_RX_0: + channels->min = channels->max = + tdm_rx_cfg[TDM_PRI][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_PRI][TDM_0].bit_format); + rate->min = rate->max = tdm_rx_cfg[TDM_PRI][TDM_0].sample_rate; + break; + + case MSM_BACKEND_DAI_PRI_TDM_TX_0: + channels->min = channels->max = + tdm_tx_cfg[TDM_PRI][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_tx_cfg[TDM_PRI][TDM_0].bit_format); + rate->min = rate->max = tdm_tx_cfg[TDM_PRI][TDM_0].sample_rate; + break; + + case MSM_BACKEND_DAI_SEC_TDM_RX_0: + channels->min = channels->max = + tdm_rx_cfg[TDM_SEC][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_SEC][TDM_0].bit_format); + rate->min = rate->max = tdm_rx_cfg[TDM_SEC][TDM_0].sample_rate; + break; + + case MSM_BACKEND_DAI_SEC_TDM_TX_0: + channels->min = channels->max = + tdm_tx_cfg[TDM_SEC][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_tx_cfg[TDM_SEC][TDM_0].bit_format); + rate->min = rate->max = tdm_tx_cfg[TDM_SEC][TDM_0].sample_rate; + break; + + case MSM_BACKEND_DAI_TERT_TDM_RX_0: + channels->min = channels->max = + tdm_rx_cfg[TDM_TERT][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_TERT][TDM_0].bit_format); + rate->min = rate->max = tdm_rx_cfg[TDM_TERT][TDM_0].sample_rate; + break; + + case MSM_BACKEND_DAI_TERT_TDM_TX_0: + channels->min = channels->max = + tdm_tx_cfg[TDM_TERT][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_tx_cfg[TDM_TERT][TDM_0].bit_format); + rate->min = rate->max = tdm_tx_cfg[TDM_TERT][TDM_0].sample_rate; + break; + + case MSM_BACKEND_DAI_QUAT_TDM_RX_0: + channels->min = channels->max = + tdm_rx_cfg[TDM_QUAT][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_QUAT][TDM_0].bit_format); + rate->min = rate->max = tdm_rx_cfg[TDM_QUAT][TDM_0].sample_rate; + break; + + case MSM_BACKEND_DAI_QUAT_TDM_TX_0: + channels->min = channels->max = + tdm_tx_cfg[TDM_QUAT][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_tx_cfg[TDM_QUAT][TDM_0].bit_format); + rate->min = rate->max = tdm_tx_cfg[TDM_QUAT][TDM_0].sample_rate; + break; + + case MSM_BACKEND_DAI_QUIN_TDM_RX_0: + channels->min = channels->max = + tdm_rx_cfg[TDM_QUIN][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_QUIN][TDM_0].bit_format); + rate->min = rate->max = tdm_rx_cfg[TDM_QUIN][TDM_0].sample_rate; + break; + + case MSM_BACKEND_DAI_QUIN_TDM_TX_0: + channels->min = channels->max = + tdm_tx_cfg[TDM_QUIN][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_tx_cfg[TDM_QUIN][TDM_0].bit_format); + rate->min = rate->max = tdm_tx_cfg[TDM_QUIN][TDM_0].sample_rate; + break; + + + case MSM_BACKEND_DAI_AUXPCM_RX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + aux_pcm_rx_cfg[PRIM_AUX_PCM].bit_format); + rate->min = rate->max = + aux_pcm_rx_cfg[PRIM_AUX_PCM].sample_rate; + channels->min = channels->max = + aux_pcm_rx_cfg[PRIM_AUX_PCM].channels; + break; + + case MSM_BACKEND_DAI_AUXPCM_TX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + aux_pcm_tx_cfg[PRIM_AUX_PCM].bit_format); + rate->min = rate->max = + aux_pcm_tx_cfg[PRIM_AUX_PCM].sample_rate; + channels->min = channels->max = + aux_pcm_tx_cfg[PRIM_AUX_PCM].channels; + break; + + case MSM_BACKEND_DAI_SEC_AUXPCM_RX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + aux_pcm_rx_cfg[SEC_AUX_PCM].bit_format); + rate->min = rate->max = + aux_pcm_rx_cfg[SEC_AUX_PCM].sample_rate; + channels->min = channels->max = + aux_pcm_rx_cfg[SEC_AUX_PCM].channels; + break; + + case MSM_BACKEND_DAI_SEC_AUXPCM_TX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + aux_pcm_tx_cfg[SEC_AUX_PCM].bit_format); + rate->min = rate->max = + aux_pcm_tx_cfg[SEC_AUX_PCM].sample_rate; + channels->min = channels->max = + aux_pcm_tx_cfg[SEC_AUX_PCM].channels; + break; + + case MSM_BACKEND_DAI_TERT_AUXPCM_RX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + aux_pcm_rx_cfg[TERT_AUX_PCM].bit_format); + rate->min = rate->max = + aux_pcm_rx_cfg[TERT_AUX_PCM].sample_rate; + channels->min = channels->max = + aux_pcm_rx_cfg[TERT_AUX_PCM].channels; + break; + + case MSM_BACKEND_DAI_TERT_AUXPCM_TX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + aux_pcm_tx_cfg[TERT_AUX_PCM].bit_format); + rate->min = rate->max = + aux_pcm_tx_cfg[TERT_AUX_PCM].sample_rate; + channels->min = channels->max = + aux_pcm_tx_cfg[TERT_AUX_PCM].channels; + break; + + case MSM_BACKEND_DAI_QUAT_AUXPCM_RX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + aux_pcm_rx_cfg[QUAT_AUX_PCM].bit_format); + rate->min = rate->max = + aux_pcm_rx_cfg[QUAT_AUX_PCM].sample_rate; + channels->min = channels->max = + aux_pcm_rx_cfg[QUAT_AUX_PCM].channels; + break; + + case MSM_BACKEND_DAI_QUAT_AUXPCM_TX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + aux_pcm_tx_cfg[QUAT_AUX_PCM].bit_format); + rate->min = rate->max = + aux_pcm_tx_cfg[QUAT_AUX_PCM].sample_rate; + channels->min = channels->max = + aux_pcm_tx_cfg[QUAT_AUX_PCM].channels; + break; + + case MSM_BACKEND_DAI_QUIN_AUXPCM_RX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + aux_pcm_rx_cfg[QUIN_AUX_PCM].bit_format); + rate->min = rate->max = + aux_pcm_rx_cfg[QUIN_AUX_PCM].sample_rate; + channels->min = channels->max = + aux_pcm_rx_cfg[QUIN_AUX_PCM].channels; + break; + + case MSM_BACKEND_DAI_QUIN_AUXPCM_TX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + aux_pcm_tx_cfg[QUIN_AUX_PCM].bit_format); + rate->min = rate->max = + aux_pcm_tx_cfg[QUIN_AUX_PCM].sample_rate; + channels->min = channels->max = + aux_pcm_tx_cfg[QUIN_AUX_PCM].channels; + break; + + case MSM_BACKEND_DAI_PRI_MI2S_RX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + mi2s_rx_cfg[PRIM_MI2S].bit_format); + rate->min = rate->max = mi2s_rx_cfg[PRIM_MI2S].sample_rate; + channels->min = channels->max = + mi2s_rx_cfg[PRIM_MI2S].channels; + break; + + case MSM_BACKEND_DAI_PRI_MI2S_TX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + mi2s_tx_cfg[PRIM_MI2S].bit_format); + rate->min = rate->max = mi2s_tx_cfg[PRIM_MI2S].sample_rate; + channels->min = channels->max = + mi2s_tx_cfg[PRIM_MI2S].channels; + break; + + case MSM_BACKEND_DAI_SECONDARY_MI2S_RX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + mi2s_rx_cfg[SEC_MI2S].bit_format); + rate->min = rate->max = mi2s_rx_cfg[SEC_MI2S].sample_rate; + channels->min = channels->max = + mi2s_rx_cfg[SEC_MI2S].channels; + break; + + case MSM_BACKEND_DAI_SECONDARY_MI2S_TX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + mi2s_tx_cfg[SEC_MI2S].bit_format); + rate->min = rate->max = mi2s_tx_cfg[SEC_MI2S].sample_rate; + channels->min = channels->max = + mi2s_tx_cfg[SEC_MI2S].channels; + break; + + case MSM_BACKEND_DAI_TERTIARY_MI2S_RX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + mi2s_rx_cfg[TERT_MI2S].bit_format); + rate->min = rate->max = mi2s_rx_cfg[TERT_MI2S].sample_rate; + channels->min = channels->max = + mi2s_rx_cfg[TERT_MI2S].channels; + break; + + case MSM_BACKEND_DAI_TERTIARY_MI2S_TX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + mi2s_tx_cfg[TERT_MI2S].bit_format); + rate->min = rate->max = mi2s_tx_cfg[TERT_MI2S].sample_rate; + channels->min = channels->max = + mi2s_tx_cfg[TERT_MI2S].channels; + break; + + case MSM_BACKEND_DAI_QUATERNARY_MI2S_RX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + mi2s_rx_cfg[QUAT_MI2S].bit_format); + rate->min = rate->max = mi2s_rx_cfg[QUAT_MI2S].sample_rate; + channels->min = channels->max = + mi2s_rx_cfg[QUAT_MI2S].channels; + break; + + case MSM_BACKEND_DAI_QUATERNARY_MI2S_TX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + mi2s_tx_cfg[QUAT_MI2S].bit_format); + rate->min = rate->max = mi2s_tx_cfg[QUAT_MI2S].sample_rate; + channels->min = channels->max = + mi2s_tx_cfg[QUAT_MI2S].channels; + break; + + case MSM_BACKEND_DAI_QUINARY_MI2S_RX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + mi2s_rx_cfg[QUIN_MI2S].bit_format); + rate->min = rate->max = mi2s_rx_cfg[QUIN_MI2S].sample_rate; + channels->min = channels->max = + mi2s_rx_cfg[QUIN_MI2S].channels; + break; + + case MSM_BACKEND_DAI_QUINARY_MI2S_TX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + mi2s_tx_cfg[QUIN_MI2S].bit_format); + rate->min = rate->max = mi2s_tx_cfg[QUIN_MI2S].sample_rate; + channels->min = channels->max = + mi2s_tx_cfg[QUIN_MI2S].channels; + break; + + default: + rate->min = rate->max = SAMPLING_RATE_48KHZ; + break; + } + +done: + return rc; +} + +static int msm_get_port_id(int be_id) +{ + int afe_port_id; + + switch (be_id) { + case MSM_BACKEND_DAI_PRI_MI2S_RX: + afe_port_id = AFE_PORT_ID_PRIMARY_MI2S_RX; + break; + case MSM_BACKEND_DAI_PRI_MI2S_TX: + afe_port_id = AFE_PORT_ID_PRIMARY_MI2S_TX; + break; + case MSM_BACKEND_DAI_SECONDARY_MI2S_RX: + afe_port_id = AFE_PORT_ID_SECONDARY_MI2S_RX; + break; + case MSM_BACKEND_DAI_SECONDARY_MI2S_TX: + afe_port_id = AFE_PORT_ID_SECONDARY_MI2S_TX; + break; + case MSM_BACKEND_DAI_TERTIARY_MI2S_RX: + afe_port_id = AFE_PORT_ID_TERTIARY_MI2S_RX; + break; + case MSM_BACKEND_DAI_TERTIARY_MI2S_TX: + afe_port_id = AFE_PORT_ID_TERTIARY_MI2S_TX; + break; + case MSM_BACKEND_DAI_QUATERNARY_MI2S_RX: + afe_port_id = AFE_PORT_ID_QUATERNARY_MI2S_RX; + break; + case MSM_BACKEND_DAI_QUATERNARY_MI2S_TX: + afe_port_id = AFE_PORT_ID_QUATERNARY_MI2S_TX; + break; + case MSM_BACKEND_DAI_QUINARY_MI2S_RX: + afe_port_id = AFE_PORT_ID_QUINARY_MI2S_RX; + break; + case MSM_BACKEND_DAI_QUINARY_MI2S_TX: + afe_port_id = AFE_PORT_ID_QUINARY_MI2S_TX; + break; + default: + pr_err("%s: Invalid BE id: %d\n", __func__, be_id); + afe_port_id = -EINVAL; + } + + return afe_port_id; +} + +static u32 get_mi2s_bits_per_sample(u32 bit_format) +{ + u32 bit_per_sample; + + switch (bit_format) { + case SNDRV_PCM_FORMAT_S32_LE: + case SNDRV_PCM_FORMAT_S24_3LE: + case SNDRV_PCM_FORMAT_S24_LE: + bit_per_sample = 32; + break; + case SNDRV_PCM_FORMAT_S16_LE: + default: + bit_per_sample = 16; + break; + } + + return bit_per_sample; +} + +static void update_mi2s_clk_val(int dai_id, int stream) +{ + u32 bit_per_sample; + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + bit_per_sample = + get_mi2s_bits_per_sample(mi2s_rx_cfg[dai_id].bit_format); + mi2s_clk[dai_id].clk_freq_in_hz = + mi2s_rx_cfg[dai_id].sample_rate * 2 * bit_per_sample; + } else { + bit_per_sample = + get_mi2s_bits_per_sample(mi2s_tx_cfg[dai_id].bit_format); + mi2s_clk[dai_id].clk_freq_in_hz = + mi2s_tx_cfg[dai_id].sample_rate * 2 * bit_per_sample; + } +} + +static int msm_mi2s_set_sclk(struct snd_pcm_substream *substream, bool enable) +{ + int ret = 0; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + int port_id = 0; + int index = cpu_dai->id; + + port_id = msm_get_port_id(rtd->dai_link->id); + if (port_id < 0) { + dev_err(rtd->card->dev, "%s: Invalid port_id\n", __func__); + ret = port_id; + goto err; + } + + if (enable) { + update_mi2s_clk_val(index, substream->stream); + dev_dbg(rtd->card->dev, "%s: clock rate %ul\n", __func__, + mi2s_clk[index].clk_freq_in_hz); + } + + mi2s_clk[index].enable = enable; + ret = afe_set_lpass_clock_v2(port_id, + &mi2s_clk[index]); + if (ret < 0) { + dev_err(rtd->card->dev, + "%s: afe lpass clock failed for port 0x%x , err:%d\n", + __func__, port_id, ret); + goto err; + } + +err: + return ret; +} + +#ifdef ENABLE_PINCTRL +static int msm_set_pinctrl(struct msm_pinctrl_info *pinctrl_info, + enum pinctrl_pin_state new_state) +{ + int ret = 0; + int curr_state = 0; + + if (pinctrl_info == NULL) { + pr_err("%s: pinctrl_info is NULL\n", __func__); + ret = -EINVAL; + goto err; + } + + if (pinctrl_info->pinctrl == NULL) { + pr_err("%s: pinctrl_info->pinctrl is NULL\n", __func__); + ret = -EINVAL; + goto err; + } + + curr_state = pinctrl_info->curr_state; + pinctrl_info->curr_state = new_state; + pr_debug("%s: curr_state = %s new_state = %s\n", __func__, + pin_states[curr_state], pin_states[pinctrl_info->curr_state]); + + if (curr_state == pinctrl_info->curr_state) { + pr_debug("%s: Already in same state\n", __func__); + goto err; + } + + if (curr_state != STATE_DISABLE && + pinctrl_info->curr_state != STATE_DISABLE) { + pr_debug("%s: state already active cannot switch\n", __func__); + ret = -EIO; + goto err; + } + + switch (pinctrl_info->curr_state) { + case STATE_MI2S_ACTIVE: + ret = pinctrl_select_state(pinctrl_info->pinctrl, + pinctrl_info->mi2s_active); + if (ret) { + pr_err("%s: MI2S state select failed with %d\n", + __func__, ret); + ret = -EIO; + goto err; + } + break; + case STATE_TDM_ACTIVE: + ret = pinctrl_select_state(pinctrl_info->pinctrl, + pinctrl_info->tdm_active); + if (ret) { + pr_err("%s: TDM state select failed with %d\n", + __func__, ret); + ret = -EIO; + goto err; + } + break; + case STATE_DISABLE: + if (curr_state == STATE_MI2S_ACTIVE) { + ret = pinctrl_select_state(pinctrl_info->pinctrl, + pinctrl_info->mi2s_disable); + } else { + ret = pinctrl_select_state(pinctrl_info->pinctrl, + pinctrl_info->tdm_disable); + } + if (ret) { + pr_err("%s: state disable failed with %d\n", + __func__, ret); + ret = -EIO; + goto err; + } + break; + default: + pr_err("%s: TLMM pin state is invalid\n", __func__); + return -EINVAL; + } + +err: + return ret; +} + +static void msm_release_pinctrl(struct platform_device *pdev) +{ + struct snd_soc_card *card = platform_get_drvdata(pdev); + struct msm_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card); + struct msm_pinctrl_info *pinctrl_info = &pdata->pinctrl_info; + + if (pinctrl_info->pinctrl) { + devm_pinctrl_put(pinctrl_info->pinctrl); + pinctrl_info->pinctrl = NULL; + } +} + +static int msm_get_pinctrl(struct platform_device *pdev) +{ + struct snd_soc_card *card = platform_get_drvdata(pdev); + struct msm_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card); + struct msm_pinctrl_info *pinctrl_info = NULL; + struct pinctrl *pinctrl; + int ret; + + pinctrl_info = &pdata->pinctrl_info; + + if (pinctrl_info == NULL) { + pr_err("%s: pinctrl_info is NULL\n", __func__); + return -EINVAL; + } + + pinctrl = devm_pinctrl_get(&pdev->dev); + if (IS_ERR_OR_NULL(pinctrl)) { + pr_err("%s: Unable to get pinctrl handle\n", __func__); + return -EINVAL; + } + pinctrl_info->pinctrl = pinctrl; + + /* get all the states handles from Device Tree */ + pinctrl_info->mi2s_disable = pinctrl_lookup_state(pinctrl, + "quat_mi2s_disable"); + if (IS_ERR(pinctrl_info->mi2s_disable)) { + pr_err("%s: could not get mi2s_disable pinstate\n", __func__); + goto err; + } + pinctrl_info->mi2s_active = pinctrl_lookup_state(pinctrl, + "quat_mi2s_enable"); + if (IS_ERR(pinctrl_info->mi2s_active)) { + pr_err("%s: could not get mi2s_active pinstate\n", __func__); + goto err; + } + pinctrl_info->tdm_disable = pinctrl_lookup_state(pinctrl, + "quat_tdm_disable"); + if (IS_ERR(pinctrl_info->tdm_disable)) { + pr_err("%s: could not get tdm_disable pinstate\n", __func__); + goto err; + } + pinctrl_info->tdm_active = pinctrl_lookup_state(pinctrl, + "quat_tdm_enable"); + if (IS_ERR(pinctrl_info->tdm_active)) { + pr_err("%s: could not get tdm_active pinstate\n", + __func__); + goto err; + } + /* Reset the TLMM pins to a default state */ + ret = pinctrl_select_state(pinctrl_info->pinctrl, + pinctrl_info->mi2s_disable); + if (ret != 0) { + pr_err("%s: Disable TLMM pins failed with %d\n", + __func__, ret); + ret = -EIO; + goto err; + } + pinctrl_info->curr_state = STATE_DISABLE; + + return 0; + +err: + devm_pinctrl_put(pinctrl); + pinctrl_info->pinctrl = NULL; + return -EINVAL; +} +#else +static int msm_set_pinctrl(struct msm_pinctrl_info *pinctrl_info, + enum pinctrl_pin_state new_state) +{ + return 0; +} + +static void msm_release_pinctrl(struct platform_device *pdev) +{ + return; +} + +static int msm_get_pinctrl(struct platform_device *pdev) +{ + return 0; +} +#endif + +static int msm_tdm_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + + switch (cpu_dai->id) { + case AFE_PORT_ID_PRIMARY_TDM_RX: + channels->min = channels->max = + tdm_rx_cfg[TDM_PRI][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_PRI][TDM_0].bit_format); + rate->min = rate->max = + tdm_rx_cfg[TDM_PRI][TDM_0].sample_rate; + break; + case AFE_PORT_ID_PRIMARY_TDM_RX_1: + channels->min = channels->max = + tdm_rx_cfg[TDM_PRI][TDM_1].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_PRI][TDM_1].bit_format); + rate->min = rate->max = + tdm_rx_cfg[TDM_PRI][TDM_1].sample_rate; + break; + case AFE_PORT_ID_PRIMARY_TDM_RX_2: + channels->min = channels->max = + tdm_rx_cfg[TDM_PRI][TDM_2].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_PRI][TDM_2].bit_format); + rate->min = rate->max = + tdm_rx_cfg[TDM_PRI][TDM_2].sample_rate; + break; + case AFE_PORT_ID_PRIMARY_TDM_RX_3: + channels->min = channels->max = + tdm_rx_cfg[TDM_PRI][TDM_3].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_PRI][TDM_3].bit_format); + rate->min = rate->max = + tdm_rx_cfg[TDM_PRI][TDM_3].sample_rate; + break; + case AFE_PORT_ID_PRIMARY_TDM_TX: + channels->min = channels->max = + tdm_tx_cfg[TDM_PRI][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_tx_cfg[TDM_PRI][TDM_0].bit_format); + rate->min = rate->max = + tdm_tx_cfg[TDM_PRI][TDM_0].sample_rate; + break; + case AFE_PORT_ID_PRIMARY_TDM_TX_1: + channels->min = channels->max = + tdm_tx_cfg[TDM_PRI][TDM_1].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_tx_cfg[TDM_PRI][TDM_1].bit_format); + rate->min = rate->max = + tdm_tx_cfg[TDM_PRI][TDM_1].sample_rate; + break; + case AFE_PORT_ID_PRIMARY_TDM_TX_2: + channels->min = channels->max = + tdm_tx_cfg[TDM_PRI][TDM_2].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_tx_cfg[TDM_PRI][TDM_2].bit_format); + rate->min = rate->max = + tdm_tx_cfg[TDM_PRI][TDM_2].sample_rate; + break; + case AFE_PORT_ID_PRIMARY_TDM_TX_3: + channels->min = channels->max = + tdm_tx_cfg[TDM_PRI][TDM_3].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_tx_cfg[TDM_PRI][TDM_3].bit_format); + rate->min = rate->max = + tdm_tx_cfg[TDM_PRI][TDM_3].sample_rate; + break; + case AFE_PORT_ID_SECONDARY_TDM_RX: + channels->min = channels->max = + tdm_rx_cfg[TDM_SEC][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_SEC][TDM_0].bit_format); + rate->min = rate->max = + tdm_rx_cfg[TDM_SEC][TDM_0].sample_rate; + break; + case AFE_PORT_ID_SECONDARY_TDM_RX_1: + channels->min = channels->max = + tdm_rx_cfg[TDM_SEC][TDM_1].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_SEC][TDM_1].bit_format); + rate->min = rate->max = + tdm_rx_cfg[TDM_SEC][TDM_1].sample_rate; + break; + case AFE_PORT_ID_SECONDARY_TDM_RX_2: + channels->min = channels->max = + tdm_rx_cfg[TDM_SEC][TDM_2].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_SEC][TDM_2].bit_format); + rate->min = rate->max = + tdm_rx_cfg[TDM_SEC][TDM_2].sample_rate; + break; + case AFE_PORT_ID_SECONDARY_TDM_RX_3: + channels->min = channels->max = + tdm_rx_cfg[TDM_SEC][TDM_3].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_SEC][TDM_3].bit_format); + rate->min = rate->max = + tdm_rx_cfg[TDM_SEC][TDM_3].sample_rate; + break; + case AFE_PORT_ID_SECONDARY_TDM_RX_7: + channels->min = channels->max = + tdm_rx_cfg[TDM_SEC][TDM_7].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_SEC][TDM_7].bit_format); + rate->min = rate->max = + tdm_rx_cfg[TDM_SEC][TDM_7].sample_rate; + break; + case AFE_PORT_ID_SECONDARY_TDM_TX: + channels->min = channels->max = + tdm_tx_cfg[TDM_SEC][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_tx_cfg[TDM_SEC][TDM_0].bit_format); + rate->min = rate->max = + tdm_tx_cfg[TDM_SEC][TDM_0].sample_rate; + break; + case AFE_PORT_ID_SECONDARY_TDM_TX_1: + channels->min = channels->max = + tdm_tx_cfg[TDM_SEC][TDM_1].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_tx_cfg[TDM_SEC][TDM_1].bit_format); + rate->min = rate->max = + tdm_tx_cfg[TDM_SEC][TDM_1].sample_rate; + break; + case AFE_PORT_ID_SECONDARY_TDM_TX_2: + channels->min = channels->max = + tdm_tx_cfg[TDM_SEC][TDM_2].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_tx_cfg[TDM_SEC][TDM_2].bit_format); + rate->min = rate->max = + tdm_tx_cfg[TDM_SEC][TDM_2].sample_rate; + break; + case AFE_PORT_ID_SECONDARY_TDM_TX_3: + channels->min = channels->max = + tdm_tx_cfg[TDM_SEC][TDM_3].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_tx_cfg[TDM_SEC][TDM_3].bit_format); + rate->min = rate->max = + tdm_tx_cfg[TDM_SEC][TDM_3].sample_rate; + break; + case AFE_PORT_ID_TERTIARY_TDM_RX: + channels->min = channels->max = + tdm_rx_cfg[TDM_TERT][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_TERT][TDM_0].bit_format); + rate->min = rate->max = + tdm_rx_cfg[TDM_TERT][TDM_0].sample_rate; + break; + case AFE_PORT_ID_TERTIARY_TDM_RX_1: + channels->min = channels->max = + tdm_rx_cfg[TDM_TERT][TDM_1].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_TERT][TDM_1].bit_format); + rate->min = rate->max = + tdm_rx_cfg[TDM_TERT][TDM_1].sample_rate; + break; + case AFE_PORT_ID_TERTIARY_TDM_RX_2: + channels->min = channels->max = + tdm_rx_cfg[TDM_TERT][TDM_2].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_TERT][TDM_2].bit_format); + rate->min = rate->max = + tdm_rx_cfg[TDM_TERT][TDM_2].sample_rate; + break; + case AFE_PORT_ID_TERTIARY_TDM_RX_3: + channels->min = channels->max = + tdm_rx_cfg[TDM_TERT][TDM_3].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_TERT][TDM_3].bit_format); + rate->min = rate->max = + tdm_rx_cfg[TDM_TERT][TDM_3].sample_rate; + break; + case AFE_PORT_ID_TERTIARY_TDM_RX_4: + channels->min = channels->max = + tdm_rx_cfg[TDM_TERT][TDM_4].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_TERT][TDM_4].bit_format); + rate->min = rate->max = + tdm_rx_cfg[TDM_TERT][TDM_4].sample_rate; + break; + case AFE_PORT_ID_TERTIARY_TDM_TX: + channels->min = channels->max = + tdm_tx_cfg[TDM_TERT][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_tx_cfg[TDM_TERT][TDM_0].bit_format); + rate->min = rate->max = + tdm_tx_cfg[TDM_TERT][TDM_0].sample_rate; + break; + case AFE_PORT_ID_TERTIARY_TDM_TX_1: + channels->min = channels->max = + tdm_tx_cfg[TDM_TERT][TDM_1].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_tx_cfg[TDM_TERT][TDM_1].bit_format); + rate->min = rate->max = + tdm_tx_cfg[TDM_TERT][TDM_1].sample_rate; + break; + case AFE_PORT_ID_TERTIARY_TDM_TX_2: + channels->min = channels->max = + tdm_tx_cfg[TDM_TERT][TDM_2].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_tx_cfg[TDM_TERT][TDM_2].bit_format); + rate->min = rate->max = + tdm_tx_cfg[TDM_TERT][TDM_2].sample_rate; + break; + case AFE_PORT_ID_TERTIARY_TDM_TX_3: + channels->min = channels->max = + tdm_tx_cfg[TDM_TERT][TDM_3].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_tx_cfg[TDM_TERT][TDM_3].bit_format); + rate->min = rate->max = + tdm_tx_cfg[TDM_TERT][TDM_3].sample_rate; + break; + case AFE_PORT_ID_TERTIARY_TDM_TX_7: + channels->min = channels->max = + tdm_tx_cfg[TDM_TERT][TDM_7].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_tx_cfg[TDM_TERT][TDM_7].bit_format); + rate->min = rate->max = + tdm_tx_cfg[TDM_TERT][TDM_7].sample_rate; + break; + case AFE_PORT_ID_QUATERNARY_TDM_RX: + channels->min = channels->max = + tdm_rx_cfg[TDM_QUAT][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_QUAT][TDM_0].bit_format); + rate->min = rate->max = + tdm_rx_cfg[TDM_QUAT][TDM_0].sample_rate; + break; + case AFE_PORT_ID_QUATERNARY_TDM_RX_1: + channels->min = channels->max = + tdm_rx_cfg[TDM_QUAT][TDM_1].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_QUAT][TDM_1].bit_format); + rate->min = rate->max = + tdm_rx_cfg[TDM_QUAT][TDM_1].sample_rate; + break; + case AFE_PORT_ID_QUATERNARY_TDM_RX_2: + channels->min = channels->max = + tdm_rx_cfg[TDM_QUAT][TDM_2].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_QUAT][TDM_2].bit_format); + rate->min = rate->max = + tdm_rx_cfg[TDM_QUAT][TDM_2].sample_rate; + break; + case AFE_PORT_ID_QUATERNARY_TDM_RX_3: + channels->min = channels->max = + tdm_rx_cfg[TDM_QUAT][TDM_3].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_QUAT][TDM_3].bit_format); + rate->min = rate->max = + tdm_rx_cfg[TDM_QUAT][TDM_3].sample_rate; + break; + case AFE_PORT_ID_QUATERNARY_TDM_TX: + channels->min = channels->max = + tdm_tx_cfg[TDM_QUAT][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_tx_cfg[TDM_QUAT][TDM_0].bit_format); + rate->min = rate->max = + tdm_tx_cfg[TDM_QUAT][TDM_0].sample_rate; + break; + case AFE_PORT_ID_QUATERNARY_TDM_TX_1: + channels->min = channels->max = + tdm_tx_cfg[TDM_QUAT][TDM_1].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_tx_cfg[TDM_QUAT][TDM_1].bit_format); + rate->min = rate->max = + tdm_tx_cfg[TDM_QUAT][TDM_1].sample_rate; + break; + case AFE_PORT_ID_QUATERNARY_TDM_TX_2: + channels->min = channels->max = + tdm_tx_cfg[TDM_QUAT][TDM_2].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_tx_cfg[TDM_QUAT][TDM_2].bit_format); + rate->min = rate->max = + tdm_tx_cfg[TDM_QUAT][TDM_2].sample_rate; + break; + case AFE_PORT_ID_QUATERNARY_TDM_TX_3: + channels->min = channels->max = + tdm_tx_cfg[TDM_QUAT][TDM_3].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_tx_cfg[TDM_QUAT][TDM_3].bit_format); + rate->min = rate->max = + tdm_tx_cfg[TDM_QUAT][TDM_3].sample_rate; + break; + case AFE_PORT_ID_QUINARY_TDM_RX: + channels->min = channels->max = + tdm_rx_cfg[TDM_QUIN][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_QUIN][TDM_0].bit_format); + rate->min = rate->max = + tdm_rx_cfg[TDM_QUIN][TDM_0].sample_rate; + break; + case AFE_PORT_ID_QUINARY_TDM_RX_1: + channels->min = channels->max = + tdm_rx_cfg[TDM_QUIN][TDM_1].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_QUIN][TDM_1].bit_format); + rate->min = rate->max = + tdm_rx_cfg[TDM_QUIN][TDM_1].sample_rate; + break; + case AFE_PORT_ID_QUINARY_TDM_RX_2: + channels->min = channels->max = + tdm_rx_cfg[TDM_QUIN][TDM_2].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_QUIN][TDM_2].bit_format); + rate->min = rate->max = + tdm_rx_cfg[TDM_QUIN][TDM_2].sample_rate; + break; + case AFE_PORT_ID_QUINARY_TDM_RX_3: + channels->min = channels->max = + tdm_rx_cfg[TDM_QUIN][TDM_3].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_QUIN][TDM_3].bit_format); + rate->min = rate->max = + tdm_rx_cfg[TDM_QUIN][TDM_3].sample_rate; + break; + case AFE_PORT_ID_QUINARY_TDM_TX: + channels->min = channels->max = + tdm_tx_cfg[TDM_QUIN][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_tx_cfg[TDM_QUIN][TDM_0].bit_format); + rate->min = rate->max = + tdm_tx_cfg[TDM_QUIN][TDM_0].sample_rate; + break; + case AFE_PORT_ID_QUINARY_TDM_TX_1: + channels->min = channels->max = + tdm_tx_cfg[TDM_QUIN][TDM_1].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_tx_cfg[TDM_QUIN][TDM_1].bit_format); + rate->min = rate->max = + tdm_tx_cfg[TDM_QUIN][TDM_1].sample_rate; + break; + case AFE_PORT_ID_QUINARY_TDM_TX_2: + channels->min = channels->max = + tdm_tx_cfg[TDM_QUIN][TDM_2].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_tx_cfg[TDM_QUIN][TDM_2].bit_format); + rate->min = rate->max = + tdm_tx_cfg[TDM_QUIN][TDM_2].sample_rate; + break; + case AFE_PORT_ID_QUINARY_TDM_TX_3: + channels->min = channels->max = + tdm_tx_cfg[TDM_QUIN][TDM_3].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_tx_cfg[TDM_QUIN][TDM_3].bit_format); + rate->min = rate->max = + tdm_tx_cfg[TDM_QUIN][TDM_3].sample_rate; + break; + default: + pr_err("%s: dai id 0x%x not supported\n", + __func__, cpu_dai->id); + return -EINVAL; + } + + pr_debug("%s: dai id = 0x%x channels = %d rate = %d format = 0x%x\n", + __func__, cpu_dai->id, channels->max, rate->max, + params_format(params)); + + return 0; +} + +static unsigned int tdm_param_set_slot_mask(int slots) +{ + unsigned int slot_mask = 0; + int i = 0; + + if ((slots <= 0) || (slots > 32)) { + pr_err("%s: invalid slot number %d\n", __func__, slots); + return -EINVAL; + } + + for (i = 0; i < slots ; i++) + slot_mask |= 1 << i; + return slot_mask; +} + +static int sa8155_tdm_snd_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + int ret = 0; + int channels, slot_width, slots, rate, format; + unsigned int slot_mask; + unsigned int *slot_offset; + int offset_channels = 0; + int i; + int clk_freq; + + pr_debug("%s: dai id = 0x%x\n", __func__, cpu_dai->id); + + channels = params_channels(params); + if (channels < 1 || channels > 32) { + pr_err("%s: invalid param channels %d\n", + __func__, channels); + return -EINVAL; + } + + format = params_format(params); + if (format != SNDRV_PCM_FORMAT_S32_LE && + format != SNDRV_PCM_FORMAT_S24_LE && + format != SNDRV_PCM_FORMAT_S16_LE) { + /* + * Up to 8 channel HW configuration should + * use 32 bit slot width for max support of + * stream bit width. (slot_width > bit_width) + */ + pr_err("%s: invalid param format 0x%x\n", + __func__, format); + return -EINVAL; + } + + switch (cpu_dai->id) { + case AFE_PORT_ID_PRIMARY_TDM_RX: + slots = tdm_slot[TDM_PRI].num; + slot_width = tdm_slot[TDM_PRI].width; + slot_offset = tdm_rx_slot_offset[TDM_PRI][TDM_0]; + break; + case AFE_PORT_ID_PRIMARY_TDM_RX_1: + slots = tdm_slot[TDM_PRI].num; + slot_width = tdm_slot[TDM_PRI].width; + slot_offset = tdm_rx_slot_offset[TDM_PRI][TDM_1]; + break; + case AFE_PORT_ID_PRIMARY_TDM_RX_2: + slots = tdm_slot[TDM_PRI].num; + slot_width = tdm_slot[TDM_PRI].width; + slot_offset = tdm_rx_slot_offset[TDM_PRI][TDM_2]; + break; + case AFE_PORT_ID_PRIMARY_TDM_RX_3: + slots = tdm_slot[TDM_PRI].num; + slot_width = tdm_slot[TDM_PRI].width; + slot_offset = tdm_rx_slot_offset[TDM_PRI][TDM_3]; + break; + case AFE_PORT_ID_PRIMARY_TDM_TX: + slots = tdm_slot[TDM_PRI].num; + slot_width = tdm_slot[TDM_PRI].width; + slot_offset = tdm_tx_slot_offset[TDM_PRI][TDM_0]; + break; + case AFE_PORT_ID_PRIMARY_TDM_TX_1: + slots = tdm_slot[TDM_PRI].num; + slot_width = tdm_slot[TDM_PRI].width; + slot_offset = tdm_tx_slot_offset[TDM_PRI][TDM_1]; + break; + case AFE_PORT_ID_PRIMARY_TDM_TX_2: + slots = tdm_slot[TDM_PRI].num; + slot_width = tdm_slot[TDM_PRI].width; + slot_offset = tdm_tx_slot_offset[TDM_PRI][TDM_2]; + break; + case AFE_PORT_ID_PRIMARY_TDM_TX_3: + slots = tdm_slot[TDM_PRI].num; + slot_width = tdm_slot[TDM_PRI].width; + slot_offset = tdm_tx_slot_offset[TDM_PRI][TDM_3]; + break; + case AFE_PORT_ID_SECONDARY_TDM_RX: + slots = tdm_slot[TDM_SEC].num; + slot_width = tdm_slot[TDM_SEC].width; + slot_offset = tdm_rx_slot_offset[TDM_SEC][TDM_0]; + break; + case AFE_PORT_ID_SECONDARY_TDM_RX_1: + slots = tdm_slot[TDM_SEC].num; + slot_width = tdm_slot[TDM_SEC].width; + slot_offset = tdm_rx_slot_offset[TDM_SEC][TDM_1]; + break; + case AFE_PORT_ID_SECONDARY_TDM_RX_2: + slots = tdm_slot[TDM_SEC].num; + slot_width = tdm_slot[TDM_SEC].width; + slot_offset = tdm_rx_slot_offset[TDM_SEC][TDM_2]; + break; + case AFE_PORT_ID_SECONDARY_TDM_RX_3: + slots = tdm_slot[TDM_SEC].num; + slot_width = tdm_slot[TDM_SEC].width; + slot_offset = tdm_rx_slot_offset[TDM_SEC][TDM_3]; + break; + case AFE_PORT_ID_SECONDARY_TDM_RX_7: + slots = tdm_slot[TDM_SEC].num; + slot_width = tdm_slot[TDM_SEC].width; + slot_offset = tdm_rx_slot_offset[TDM_SEC][TDM_7]; + break; + case AFE_PORT_ID_SECONDARY_TDM_TX: + slots = tdm_slot[TDM_SEC].num; + slot_width = tdm_slot[TDM_SEC].width; + slot_offset = tdm_tx_slot_offset[TDM_SEC][TDM_0]; + break; + case AFE_PORT_ID_SECONDARY_TDM_TX_1: + slots = tdm_slot[TDM_SEC].num; + slot_width = tdm_slot[TDM_SEC].width; + slot_offset = tdm_tx_slot_offset[TDM_SEC][TDM_1]; + break; + case AFE_PORT_ID_SECONDARY_TDM_TX_2: + slots = tdm_slot[TDM_SEC].num; + slot_width = tdm_slot[TDM_SEC].width; + slot_offset = tdm_tx_slot_offset[TDM_SEC][TDM_2]; + break; + case AFE_PORT_ID_SECONDARY_TDM_TX_3: + slots = tdm_slot[TDM_SEC].num; + slot_width = tdm_slot[TDM_SEC].width; + slot_offset = tdm_tx_slot_offset[TDM_SEC][TDM_3]; + break; + case AFE_PORT_ID_TERTIARY_TDM_RX: + slots = tdm_slot[TDM_TERT].num; + slot_width = tdm_slot[TDM_TERT].width; + slot_offset = tdm_rx_slot_offset[TDM_TERT][TDM_0]; + break; + case AFE_PORT_ID_TERTIARY_TDM_RX_1: + slots = tdm_slot[TDM_TERT].num; + slot_width = tdm_slot[TDM_TERT].width; + slot_offset = tdm_rx_slot_offset[TDM_TERT][TDM_1]; + break; + case AFE_PORT_ID_TERTIARY_TDM_RX_2: + slots = tdm_slot[TDM_TERT].num; + slot_width = tdm_slot[TDM_TERT].width; + slot_offset = tdm_rx_slot_offset[TDM_TERT][TDM_2]; + break; + case AFE_PORT_ID_TERTIARY_TDM_RX_3: + slots = tdm_slot[TDM_TERT].num; + slot_width = tdm_slot[TDM_TERT].width; + slot_offset = tdm_rx_slot_offset[TDM_TERT][TDM_3]; + break; + case AFE_PORT_ID_TERTIARY_TDM_RX_4: + slots = tdm_slot[TDM_TERT].num; + slot_width = tdm_slot[TDM_TERT].width; + slot_offset = tdm_rx_slot_offset[TDM_TERT][TDM_4]; + break; + case AFE_PORT_ID_TERTIARY_TDM_TX: + slots = tdm_slot[TDM_TERT].num; + slot_width = tdm_slot[TDM_TERT].width; + slot_offset = tdm_tx_slot_offset[TDM_TERT][TDM_0]; + break; + case AFE_PORT_ID_TERTIARY_TDM_TX_1: + slots = tdm_slot[TDM_TERT].num; + slot_width = tdm_slot[TDM_TERT].width; + slot_offset = tdm_tx_slot_offset[TDM_TERT][TDM_1]; + break; + case AFE_PORT_ID_TERTIARY_TDM_TX_2: + slots = tdm_slot[TDM_TERT].num; + slot_width = tdm_slot[TDM_TERT].width; + slot_offset = tdm_tx_slot_offset[TDM_TERT][TDM_2]; + break; + case AFE_PORT_ID_TERTIARY_TDM_TX_3: + slots = tdm_slot[TDM_TERT].num; + slot_width = tdm_slot[TDM_TERT].width; + slot_offset = tdm_tx_slot_offset[TDM_TERT][TDM_3]; + break; + case AFE_PORT_ID_TERTIARY_TDM_TX_7: + slots = tdm_slot[TDM_TERT].num; + slot_width = tdm_slot[TDM_TERT].width; + slot_offset = tdm_tx_slot_offset[TDM_TERT][TDM_7]; + break; + case AFE_PORT_ID_QUATERNARY_TDM_RX: + slots = tdm_slot[TDM_QUAT].num; + slot_width = tdm_slot[TDM_QUAT].width; + slot_offset = tdm_rx_slot_offset[TDM_QUAT][TDM_0]; + break; + case AFE_PORT_ID_QUATERNARY_TDM_RX_1: + slots = tdm_slot[TDM_QUAT].num; + slot_width = tdm_slot[TDM_QUAT].width; + slot_offset = tdm_rx_slot_offset[TDM_QUAT][TDM_1]; + break; + case AFE_PORT_ID_QUATERNARY_TDM_RX_2: + slots = tdm_slot[TDM_QUAT].num; + slot_width = tdm_slot[TDM_QUAT].width; + slot_offset = tdm_rx_slot_offset[TDM_QUAT][TDM_2]; + break; + case AFE_PORT_ID_QUATERNARY_TDM_RX_3: + slots = tdm_slot[TDM_QUAT].num; + slot_width = tdm_slot[TDM_QUAT].width; + slot_offset = tdm_rx_slot_offset[TDM_QUAT][TDM_3]; + break; + case AFE_PORT_ID_QUATERNARY_TDM_TX: + slots = tdm_slot[TDM_QUAT].num; + slot_width = tdm_slot[TDM_QUAT].width; + slot_offset = tdm_tx_slot_offset[TDM_QUAT][TDM_0]; + break; + case AFE_PORT_ID_QUATERNARY_TDM_TX_1: + slots = tdm_slot[TDM_QUAT].num; + slot_width = tdm_slot[TDM_QUAT].width; + slot_offset = tdm_tx_slot_offset[TDM_QUAT][TDM_1]; + break; + case AFE_PORT_ID_QUATERNARY_TDM_TX_2: + slots = tdm_slot[TDM_QUAT].num; + slot_width = tdm_slot[TDM_QUAT].width; + slot_offset = tdm_tx_slot_offset[TDM_QUAT][TDM_2]; + break; + case AFE_PORT_ID_QUATERNARY_TDM_TX_3: + slots = tdm_slot[TDM_QUAT].num; + slot_width = tdm_slot[TDM_QUAT].width; + slot_offset = tdm_tx_slot_offset[TDM_QUAT][TDM_3]; + break; + case AFE_PORT_ID_QUINARY_TDM_RX: + slots = tdm_slot[TDM_QUIN].num; + slot_width = tdm_slot[TDM_QUIN].width; + slot_offset = tdm_rx_slot_offset[TDM_QUIN][TDM_0]; + break; + case AFE_PORT_ID_QUINARY_TDM_RX_1: + slots = tdm_slot[TDM_QUIN].num; + slot_width = tdm_slot[TDM_QUIN].width; + slot_offset = tdm_rx_slot_offset[TDM_QUIN][TDM_1]; + break; + case AFE_PORT_ID_QUINARY_TDM_RX_2: + slots = tdm_slot[TDM_QUIN].num; + slot_width = tdm_slot[TDM_QUIN].width; + slot_offset = tdm_rx_slot_offset[TDM_QUIN][TDM_2]; + break; + case AFE_PORT_ID_QUINARY_TDM_RX_3: + slots = tdm_slot[TDM_QUIN].num; + slot_width = tdm_slot[TDM_QUIN].width; + slot_offset = tdm_rx_slot_offset[TDM_QUIN][TDM_3]; + break; + case AFE_PORT_ID_QUINARY_TDM_TX: + slots = tdm_slot[TDM_QUIN].num; + slot_width = tdm_slot[TDM_QUIN].width; + slot_offset = tdm_tx_slot_offset[TDM_QUIN][TDM_0]; + break; + case AFE_PORT_ID_QUINARY_TDM_TX_1: + slots = tdm_slot[TDM_QUIN].num; + slot_width = tdm_slot[TDM_QUIN].width; + slot_offset = tdm_tx_slot_offset[TDM_QUIN][TDM_1]; + break; + case AFE_PORT_ID_QUINARY_TDM_TX_2: + slots = tdm_slot[TDM_QUIN].num; + slot_width = tdm_slot[TDM_QUIN].width; + slot_offset = tdm_tx_slot_offset[TDM_QUIN][TDM_2]; + break; + case AFE_PORT_ID_QUINARY_TDM_TX_3: + slots = tdm_slot[TDM_QUIN].num; + slot_width = tdm_slot[TDM_QUIN].width; + slot_offset = tdm_tx_slot_offset[TDM_QUIN][TDM_3]; + break; + default: + pr_err("%s: dai id 0x%x not supported\n", + __func__, cpu_dai->id); + return -EINVAL; + } + + for (i = 0; i < TDM_SLOT_OFFSET_MAX; i++) { + if (slot_offset[i] != AFE_SLOT_MAPPING_OFFSET_INVALID) + offset_channels++; + else + break; + } + + if (offset_channels == 0) { + pr_err("%s: invalid offset_channels %d\n", + __func__, offset_channels); + return -EINVAL; + } + + if (channels > offset_channels) { + pr_err("%s: channels %d exceed offset_channels %d\n", + __func__, channels, offset_channels); + return -EINVAL; + } + + slot_mask = tdm_param_set_slot_mask(slots); + if (!slot_mask) { + pr_err("%s: invalid slot_mask 0x%x\n", + __func__, slot_mask); + return -EINVAL; + } + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0, slot_mask, + slots, slot_width); + if (ret < 0) { + pr_err("%s: failed to set tdm slot, err:%d\n", + __func__, ret); + goto end; + } + + ret = snd_soc_dai_set_channel_map(cpu_dai, + 0, NULL, channels, slot_offset); + if (ret < 0) { + pr_err("%s: failed to set channel map, err:%d\n", + __func__, ret); + goto end; + } + } else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + ret = snd_soc_dai_set_tdm_slot(cpu_dai, slot_mask, 0, + slots, slot_width); + if (ret < 0) { + pr_err("%s: failed to set tdm slot, err:%d\n", + __func__, ret); + goto end; + } + + ret = snd_soc_dai_set_channel_map(cpu_dai, + channels, slot_offset, 0, NULL); + if (ret < 0) { + pr_err("%s: failed to set channel map, err:%d\n", + __func__, ret); + goto end; + } + } else { + ret = -EINVAL; + pr_err("%s: invalid use case, err:%d\n", + __func__, ret); + goto end; + } + + rate = params_rate(params); + clk_freq = rate * slot_width * slots; + ret = snd_soc_dai_set_sysclk(cpu_dai, 0, clk_freq, SND_SOC_CLOCK_OUT); + if (ret < 0) { + pr_err("%s: failed to set tdm clk, err:%d\n", + __func__, ret); + } + +end: + return ret; +} + +static int sa8155_tdm_snd_startup(struct snd_pcm_substream *substream) +{ + int ret = 0; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_card *card = rtd->card; + struct msm_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card); + struct msm_pinctrl_info *pinctrl_info = &pdata->pinctrl_info; + + /* currently only supporting TDM_RX_0 and TDM_TX_0 */ + if ((cpu_dai->id == AFE_PORT_ID_QUATERNARY_TDM_RX) || + (cpu_dai->id == AFE_PORT_ID_QUATERNARY_TDM_TX)) { + ret = msm_set_pinctrl(pinctrl_info, STATE_TDM_ACTIVE); + if (ret) + pr_err("%s: TDM TLMM pinctrl set failed with %d\n", + __func__, ret); + } + + return ret; +} + +static void sa8155_tdm_snd_shutdown(struct snd_pcm_substream *substream) +{ + int ret = 0; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_card *card = rtd->card; + struct msm_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card); + struct msm_pinctrl_info *pinctrl_info = &pdata->pinctrl_info; + + /* currently only supporting TDM_RX_0 and TDM_TX_0 */ + if ((cpu_dai->id == AFE_PORT_ID_QUATERNARY_TDM_RX) || + (cpu_dai->id == AFE_PORT_ID_QUATERNARY_TDM_TX)) { + ret = msm_set_pinctrl(pinctrl_info, STATE_DISABLE); + if (ret) + pr_err("%s: TDM TLMM pinctrl set failed with %d\n", + __func__, ret); + } +} + +static struct snd_soc_ops sa8155_tdm_be_ops = { + .hw_params = sa8155_tdm_snd_hw_params, + .startup = sa8155_tdm_snd_startup, + .shutdown = sa8155_tdm_snd_shutdown +}; + +static int msm_fe_qos_prepare(struct snd_pcm_substream *substream) +{ + cpumask_t mask; + + if (pm_qos_request_active(&substream->latency_pm_qos_req)) + pm_qos_remove_request(&substream->latency_pm_qos_req); + + cpumask_clear(&mask); + cpumask_set_cpu(1, &mask); /* affine to core 1 */ + cpumask_set_cpu(2, &mask); /* affine to core 2 */ + cpumask_copy(&substream->latency_pm_qos_req.cpus_affine, &mask); + + substream->latency_pm_qos_req.type = PM_QOS_REQ_AFFINE_CORES; + + pm_qos_add_request(&substream->latency_pm_qos_req, + PM_QOS_CPU_DMA_LATENCY, + MSM_LL_QOS_VALUE); + return 0; +} + +static struct snd_soc_ops msm_fe_qos_ops = { + .prepare = msm_fe_qos_prepare, +}; + +static int msm_mi2s_snd_startup(struct snd_pcm_substream *substream) +{ + int ret = 0; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + int index = cpu_dai->id; + unsigned int fmt = SND_SOC_DAIFMT_CBS_CFS; + struct snd_soc_card *card = rtd->card; + struct msm_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card); + struct msm_pinctrl_info *pinctrl_info = &pdata->pinctrl_info; + int ret_pinctrl = 0; + + dev_dbg(rtd->card->dev, + "%s: substream = %s stream = %d, dai name %s, dai ID %d\n", + __func__, substream->name, substream->stream, + cpu_dai->name, cpu_dai->id); + + if (index < PRIM_MI2S || index >= MI2S_MAX) { + ret = -EINVAL; + dev_err(rtd->card->dev, + "%s: CPU DAI id (%d) out of range\n", + __func__, cpu_dai->id); + goto err; + } + /* + * Mutex protection in case the same MI2S + * interface using for both TX and RX so + * that the same clock won't be enable twice. + */ + mutex_lock(&mi2s_intf_conf[index].lock); + if (++mi2s_intf_conf[index].ref_cnt == 1) { + /* Check if msm needs to provide the clock to the interface */ + if (!mi2s_intf_conf[index].msm_is_mi2s_master) { + mi2s_clk[index].clk_id = mi2s_ebit_clk[index]; + fmt = SND_SOC_DAIFMT_CBM_CFM; + } + ret = msm_mi2s_set_sclk(substream, true); + if (ret < 0) { + dev_err(rtd->card->dev, + "%s: afe lpass clock failed to enable MI2S clock, err:%d\n", + __func__, ret); + goto clean_up; + } + + ret = snd_soc_dai_set_fmt(cpu_dai, fmt); + if (ret < 0) { + pr_err("%s: set fmt cpu dai failed for MI2S (%d), err:%d\n", + __func__, index, ret); + goto clk_off; + } + if (index == QUAT_MI2S) { + ret_pinctrl = msm_set_pinctrl(pinctrl_info, + STATE_MI2S_ACTIVE); + if (ret_pinctrl) + pr_err("%s: MI2S TLMM pinctrl set failed with %d\n", + __func__, ret_pinctrl); + } + } +clk_off: + if (ret < 0) + msm_mi2s_set_sclk(substream, false); +clean_up: + if (ret < 0) + mi2s_intf_conf[index].ref_cnt--; + mutex_unlock(&mi2s_intf_conf[index].lock); +err: + return ret; +} + +static void msm_mi2s_snd_shutdown(struct snd_pcm_substream *substream) +{ + int ret; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + int index = rtd->cpu_dai->id; + struct snd_soc_card *card = rtd->card; + struct msm_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card); + struct msm_pinctrl_info *pinctrl_info = &pdata->pinctrl_info; + int ret_pinctrl = 0; + + pr_debug("%s(): substream = %s stream = %d\n", __func__, + substream->name, substream->stream); + if (index < PRIM_MI2S || index >= MI2S_MAX) { + pr_err("%s:invalid MI2S DAI(%d)\n", __func__, index); + return; + } + + mutex_lock(&mi2s_intf_conf[index].lock); + if (--mi2s_intf_conf[index].ref_cnt == 0) { + ret = msm_mi2s_set_sclk(substream, false); + if (ret < 0) + pr_err("%s:clock disable failed for MI2S (%d); ret=%d\n", + __func__, index, ret); + if (index == QUAT_MI2S) { + ret_pinctrl = msm_set_pinctrl(pinctrl_info, + STATE_DISABLE); + if (ret_pinctrl) + pr_err("%s: MI2S TLMM pinctrl set failed with %d\n", + __func__, ret_pinctrl); + } + } + mutex_unlock(&mi2s_intf_conf[index].lock); +} + +static struct snd_soc_ops msm_mi2s_be_ops = { + .startup = msm_mi2s_snd_startup, + .shutdown = msm_mi2s_snd_shutdown, +}; + + +/* Digital audio interface glue - connects codec <---> CPU */ +static struct snd_soc_dai_link msm_common_dai_links[] = { + /* FrontEnd DAI Links */ + { + .name = MSM_DAILINK_NAME(Media1), + .stream_name = "MultiMedia1", + .cpu_dai_name = "MultiMedia1", + .platform_name = "msm-pcm-dsp.0", + .dynamic = 1, + .async_ops = ASYNC_DPCM_SND_SOC_PREPARE, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .id = MSM_FRONTEND_DAI_MULTIMEDIA1 + }, + { + .name = MSM_DAILINK_NAME(Media2), + .stream_name = "MultiMedia2", + .cpu_dai_name = "MultiMedia2", + .platform_name = "msm-pcm-dsp.0", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .id = MSM_FRONTEND_DAI_MULTIMEDIA2, + }, + { + .name = "VoiceMMode1", + .stream_name = "VoiceMMode1", + .cpu_dai_name = "VoiceMMode1", + .platform_name = "msm-pcm-voice", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 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", + .id = MSM_FRONTEND_DAI_VOICEMMODE1, + }, + { + .name = "MSM VoIP", + .stream_name = "VoIP", + .cpu_dai_name = "VoIP", + .platform_name = "msm-voip-dsp", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .id = MSM_FRONTEND_DAI_VOIP, + }, + { + .name = MSM_DAILINK_NAME(ULL), + .stream_name = "MultiMedia3", + .cpu_dai_name = "MultiMedia3", + .platform_name = "msm-pcm-dsp.2", + .dynamic = 1, + .async_ops = ASYNC_DPCM_SND_SOC_PREPARE, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .id = MSM_FRONTEND_DAI_MULTIMEDIA3, + }, + /* - SLIMBUS_0 Hostless */ + { + .name = "MSM AFE-PCM RX", + .stream_name = "AFE-PROXY RX", + .cpu_dai_name = "msm-dai-q6-dev.241", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .platform_name = "msm-pcm-afe", + .dpcm_playback = 1, + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + }, + { + .name = "MSM AFE-PCM TX", + .stream_name = "AFE-PROXY TX", + .cpu_dai_name = "msm-dai-q6-dev.240", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .platform_name = "msm-pcm-afe", + .dpcm_capture = 1, + .ignore_suspend = 1, + }, + { + .name = MSM_DAILINK_NAME(Compress1), + .stream_name = "Compress1", + .cpu_dai_name = "MultiMedia4", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .async_ops = ASYNC_DPCM_SND_SOC_HW_PARAMS, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dainlink has playback support */ + .id = MSM_FRONTEND_DAI_MULTIMEDIA4, + }, + /* Hostless PCM purpose */ + { + .name = "AUXPCM Hostless", + .stream_name = "AUXPCM Hostless", + .cpu_dai_name = "AUXPCM_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + /* - SLIMBUS_1 Hostless */ + /* - SLIMBUS_3 Hostless */ + /* - SLIMBUS_4 Hostless */ + { + .name = MSM_DAILINK_NAME(LowLatency), + .stream_name = "MultiMedia5", + .cpu_dai_name = "MultiMedia5", + .platform_name = "msm-pcm-dsp.1", + .dynamic = 1, + .async_ops = ASYNC_DPCM_SND_SOC_PREPARE, + .dpcm_playback = 1, + .dpcm_capture = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .id = MSM_FRONTEND_DAI_MULTIMEDIA5, + .ops = &msm_fe_qos_ops, + }, + { + .name = "Listen 1 Audio Service", + .stream_name = "Listen 1 Audio Service", + .cpu_dai_name = "LSM1", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = { SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST }, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .id = MSM_FRONTEND_DAI_LSM1, + }, + /* Multiple Tunnel instances */ + { + .name = MSM_DAILINK_NAME(Compress2), + .stream_name = "Compress2", + .cpu_dai_name = "MultiMedia7", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dainlink has playback support */ + .id = MSM_FRONTEND_DAI_MULTIMEDIA7, + }, + { + .name = MSM_DAILINK_NAME(MultiMedia10), + .stream_name = "MultiMedia10", + .cpu_dai_name = "MultiMedia10", + .platform_name = "msm-pcm-dsp.1", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dainlink has playback support */ + .id = MSM_FRONTEND_DAI_MULTIMEDIA10, + }, + { + .name = MSM_DAILINK_NAME(ULL_NOIRQ), + .stream_name = "MM_NOIRQ", + .cpu_dai_name = "MultiMedia8", + .platform_name = "msm-pcm-dsp-noirq", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dainlink has playback support */ + .id = MSM_FRONTEND_DAI_MULTIMEDIA8, + .ops = &msm_fe_qos_ops, + }, + /* HDMI Hostless */ + { + .name = "HDMI_RX_HOSTLESS", + .stream_name = "HDMI_RX_HOSTLESS", + .cpu_dai_name = "HDMI_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .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", + }, + { + .name = "VoiceMMode2", + .stream_name = "VoiceMMode2", + .cpu_dai_name = "VoiceMMode2", + .platform_name = "msm-pcm-voice", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 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", + .id = MSM_FRONTEND_DAI_VOICEMMODE2, + }, + /* LSM FE */ + { + .name = "Listen 2 Audio Service", + .stream_name = "Listen 2 Audio Service", + .cpu_dai_name = "LSM2", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = { SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST }, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .id = MSM_FRONTEND_DAI_LSM2, + }, + { + .name = "Listen 3 Audio Service", + .stream_name = "Listen 3 Audio Service", + .cpu_dai_name = "LSM3", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = { SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST }, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .id = MSM_FRONTEND_DAI_LSM3, + }, + { + .name = "Listen 4 Audio Service", + .stream_name = "Listen 4 Audio Service", + .cpu_dai_name = "LSM4", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = { SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST }, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .id = MSM_FRONTEND_DAI_LSM4, + }, + { + .name = "Listen 5 Audio Service", + .stream_name = "Listen 5 Audio Service", + .cpu_dai_name = "LSM5", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = { SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST }, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .id = MSM_FRONTEND_DAI_LSM5, + }, + { + .name = "Listen 6 Audio Service", + .stream_name = "Listen 6 Audio Service", + .cpu_dai_name = "LSM6", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = { SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST }, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .id = MSM_FRONTEND_DAI_LSM6, + }, + { + .name = "Listen 7 Audio Service", + .stream_name = "Listen 7 Audio Service", + .cpu_dai_name = "LSM7", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = { SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST }, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .id = MSM_FRONTEND_DAI_LSM7, + }, + { + .name = "Listen 8 Audio Service", + .stream_name = "Listen 8 Audio Service", + .cpu_dai_name = "LSM8", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = { SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST }, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .id = MSM_FRONTEND_DAI_LSM8, + }, + /* - Multimedia9 */ + { + .name = MSM_DAILINK_NAME(Compress4), + .stream_name = "Compress4", + .cpu_dai_name = "MultiMedia11", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dainlink has playback support */ + .id = MSM_FRONTEND_DAI_MULTIMEDIA11, + }, + { + .name = MSM_DAILINK_NAME(Compress5), + .stream_name = "Compress5", + .cpu_dai_name = "MultiMedia12", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dainlink has playback support */ + .id = MSM_FRONTEND_DAI_MULTIMEDIA12, + }, + { + .name = MSM_DAILINK_NAME(Compress6), + .stream_name = "Compress6", + .cpu_dai_name = "MultiMedia13", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dainlink has playback support */ + .id = MSM_FRONTEND_DAI_MULTIMEDIA13, + }, + { + .name = MSM_DAILINK_NAME(Compress7), + .stream_name = "Compress7", + .cpu_dai_name = "MultiMedia14", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dainlink has playback support */ + .id = MSM_FRONTEND_DAI_MULTIMEDIA14, + }, + { + .name = MSM_DAILINK_NAME(Compress8), + .stream_name = "Compress8", + .cpu_dai_name = "MultiMedia15", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dainlink has playback support */ + .id = MSM_FRONTEND_DAI_MULTIMEDIA15, + }, + { + .name = MSM_DAILINK_NAME(ULL_NOIRQ_2), + .stream_name = "MM_NOIRQ_2", + .cpu_dai_name = "MultiMedia16", + .platform_name = "msm-pcm-dsp-noirq", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dainlink has playback support */ + .id = MSM_FRONTEND_DAI_MULTIMEDIA16, + }, + /* - SLIMBUS_8 Hostless */ + /* - Slimbus4 Capture */ + /* - SLIMBUS_2 Hostless Playback */ + /* - SLIMBUS_2 Hostless Capture */ + /* HFP TX */ + { + .name = MSM_DAILINK_NAME(ASM Loopback), + .stream_name = "MultiMedia6", + .cpu_dai_name = "MultiMedia6", + .platform_name = "msm-pcm-loopback", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .ignore_suspend = 1, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_pmdown_time = 1, + .id = MSM_FRONTEND_DAI_MULTIMEDIA6, + }, + { + .name = "USB Audio Hostless", + .stream_name = "USB Audio Hostless", + .cpu_dai_name = "USBAUDIO_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 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", + }, + /* - SLIMBUS_7 Hostless */ + { + .name = "Compress Capture", + .stream_name = "Compress9", + .cpu_dai_name = "MultiMedia17", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .id = MSM_FRONTEND_DAI_MULTIMEDIA17, + }, +}; + +static struct snd_soc_dai_link msm_auto_fe_dai_links[] = { + { + .name = "INT_HFP_BT Hostless", + .stream_name = "INT_HFP_BT Hostless", + .cpu_dai_name = "INT_HFP_BT_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + /* Low latency ASM loopback for ICC */ + { + .name = MSM_DAILINK_NAME(LowLatency Loopback), + .stream_name = "MultiMedia9", + .cpu_dai_name = "MultiMedia9", + .platform_name = "msm-pcm-loopback.1", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .id = MSM_FRONTEND_DAI_MULTIMEDIA9, + }, + { + .name = "Tertiary MI2S TX_Hostless", + .stream_name = "Tertiary MI2S_TX Hostless Capture", + .cpu_dai_name = "TERT_MI2S_TX_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_capture = 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", + }, + { + .name = MSM_DAILINK_NAME(Media20), + .stream_name = "MultiMedia20", + .cpu_dai_name = "MultiMedia20", + .platform_name = "msm-pcm-loopback", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .id = MSM_FRONTEND_DAI_MULTIMEDIA20, + }, + { + .name = MSM_DAILINK_NAME(HFP RX), + .stream_name = "MultiMedia21", + .cpu_dai_name = "MultiMedia21", + .platform_name = "msm-pcm-loopback", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .ignore_suspend = 1, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_pmdown_time = 1, + .id = MSM_FRONTEND_DAI_MULTIMEDIA21, + }, + /* TDM Hostless */ + { + .name = "Primary TDM RX 0 Hostless", + .stream_name = "Primary TDM RX 0 Hostless", + .cpu_dai_name = "PRI_TDM_RX_0_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .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", + }, + { + .name = "Primary TDM TX 0 Hostless", + .stream_name = "Primary TDM TX 0 Hostless", + .cpu_dai_name = "PRI_TDM_TX_0_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_capture = 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", + }, + { + .name = "Secondary TDM RX 0 Hostless", + .stream_name = "Secondary TDM RX 0 Hostless", + .cpu_dai_name = "SEC_TDM_RX_0_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .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", + }, + { + .name = "Secondary TDM TX 0 Hostless", + .stream_name = "Secondary TDM TX 0 Hostless", + .cpu_dai_name = "SEC_TDM_TX_0_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_capture = 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", + }, + { + .name = "Tertiary TDM RX 0 Hostless", + .stream_name = "Tertiary TDM RX 0 Hostless", + .cpu_dai_name = "TERT_TDM_RX_0_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .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", + }, + { + .name = "Tertiary TDM TX 0 Hostless", + .stream_name = "Tertiary TDM TX 0 Hostless", + .cpu_dai_name = "TERT_TDM_TX_0_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_capture = 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", + }, + { + .name = "Quaternary TDM RX 0 Hostless", + .stream_name = "Quaternary TDM RX 0 Hostless", + .cpu_dai_name = "QUAT_TDM_RX_0_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .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", + }, + { + .name = "Quaternary TDM TX 0 Hostless", + .stream_name = "Quaternary TDM TX 0 Hostless", + .cpu_dai_name = "QUAT_TDM_TX_0_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_capture = 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", + }, + { + .name = "Quaternary MI2S_RX Hostless Playback", + .stream_name = "Quaternary MI2S_RX Hostless Playback", + .cpu_dai_name = "QUAT_MI2S_RX_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .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", + }, + { + .name = "Secondary MI2S_TX Hostless Capture", + .stream_name = "Secondary MI2S_TX Hostless Capture", + .cpu_dai_name = "SEC_MI2S_TX_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_capture = 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", + }, + { + .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", + .id = MSM_FRONTEND_DAI_DTMF_RX, + }, + { + .name = "Secondary TDM RX 7 Hostless", + .stream_name = "Secondary TDM RX 7 Hostless", + .cpu_dai_name = "SEC_TDM_RX_7_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .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", + }, + { + .name = "Tertiary TDM TX 7 Hostless", + .stream_name = "Tertiary TDM TX 7 Hostless", + .cpu_dai_name = "TERT_TDM_TX_7_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_capture = 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", + }, +}; + +static struct snd_soc_dai_link msm_custom_fe_dai_links[] = { + /* FrontEnd DAI Links */ + { + .name = MSM_DAILINK_NAME(Media1), + .stream_name = "MultiMedia1", + .cpu_dai_name = "MultiMedia1", + .platform_name = "msm-pcm-dsp.1", + .dynamic = 1, + .async_ops = ASYNC_DPCM_SND_SOC_PREPARE, + .dpcm_playback = 1, + .dpcm_capture = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .id = MSM_FRONTEND_DAI_MULTIMEDIA1, + .ops = &msm_fe_qos_ops, + }, + { + .name = MSM_DAILINK_NAME(Media2), + .stream_name = "MultiMedia2", + .cpu_dai_name = "MultiMedia2", + .platform_name = "msm-pcm-dsp.1", + .dynamic = 1, + .async_ops = ASYNC_DPCM_SND_SOC_PREPARE, + .dpcm_playback = 1, + .dpcm_capture = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .id = MSM_FRONTEND_DAI_MULTIMEDIA2, + .ops = &msm_fe_qos_ops, + }, + { + .name = MSM_DAILINK_NAME(Media3), + .stream_name = "MultiMedia3", + .cpu_dai_name = "MultiMedia3", + .platform_name = "msm-pcm-dsp.1", + .dynamic = 1, + .async_ops = ASYNC_DPCM_SND_SOC_PREPARE, + .dpcm_playback = 1, + .dpcm_capture = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .id = MSM_FRONTEND_DAI_MULTIMEDIA3, + .ops = &msm_fe_qos_ops, + }, + { + .name = MSM_DAILINK_NAME(Media5), + .stream_name = "MultiMedia5", + .cpu_dai_name = "MultiMedia5", + .platform_name = "msm-pcm-dsp.1", + .dynamic = 1, + .async_ops = ASYNC_DPCM_SND_SOC_PREPARE, + .dpcm_playback = 1, + .dpcm_capture = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .id = MSM_FRONTEND_DAI_MULTIMEDIA5, + .ops = &msm_fe_qos_ops, + }, + { + .name = MSM_DAILINK_NAME(Media6), + .stream_name = "MultiMedia6", + .cpu_dai_name = "MultiMedia6", + .platform_name = "msm-pcm-dsp.1", + .dynamic = 1, + .async_ops = ASYNC_DPCM_SND_SOC_PREPARE, + .dpcm_playback = 1, + .dpcm_capture = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .id = MSM_FRONTEND_DAI_MULTIMEDIA6, + .ops = &msm_fe_qos_ops, + }, + { + .name = MSM_DAILINK_NAME(Media8), + .stream_name = "MultiMedia8", + .cpu_dai_name = "MultiMedia8", + .platform_name = "msm-pcm-dsp.1", + .dynamic = 1, + .async_ops = ASYNC_DPCM_SND_SOC_PREPARE, + .dpcm_playback = 1, + .dpcm_capture = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .id = MSM_FRONTEND_DAI_MULTIMEDIA8, + .ops = &msm_fe_qos_ops, + }, + { + .name = MSM_DAILINK_NAME(Media9), + .stream_name = "MultiMedia9", + .cpu_dai_name = "MultiMedia9", + .platform_name = "msm-pcm-dsp.1", + .dynamic = 1, + .async_ops = ASYNC_DPCM_SND_SOC_PREPARE, + .dpcm_playback = 1, + .dpcm_capture = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .id = MSM_FRONTEND_DAI_MULTIMEDIA9, + .ops = &msm_fe_qos_ops, + }, + { + .name = "INT_HFP_BT Hostless", + .stream_name = "INT_HFP_BT Hostless", + .cpu_dai_name = "INT_HFP_BT_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + { + .name = "AUXPCM Hostless", + .stream_name = "AUXPCM Hostless", + .cpu_dai_name = "AUXPCM_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + { + .name = MSM_DAILINK_NAME(Media20), + .stream_name = "MultiMedia20", + .cpu_dai_name = "MultiMedia20", + .platform_name = "msm-pcm-loopback", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .ignore_suspend = 1, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .id = MSM_FRONTEND_DAI_MULTIMEDIA20, + }, +}; + +static struct snd_soc_dai_link msm_common_be_dai_links[] = { + /* Backend AFE DAI Links */ + { + .name = LPASS_BE_AFE_PCM_RX, + .stream_name = "AFE Playback", + .cpu_dai_name = "msm-dai-q6-dev.224", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_AFE_PCM_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_AFE_PCM_TX, + .stream_name = "AFE Capture", + .cpu_dai_name = "msm-dai-q6-dev.225", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_AFE_PCM_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + }, + /* Incall Record Uplink BACK END DAI Link */ + { + .name = LPASS_BE_INCALL_RECORD_TX, + .stream_name = "Voice Uplink Capture", + .cpu_dai_name = "msm-dai-q6-dev.32772", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_INCALL_RECORD_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + }, + /* Incall Record Downlink BACK END DAI Link */ + { + .name = LPASS_BE_INCALL_RECORD_RX, + .stream_name = "Voice Downlink Capture", + .cpu_dai_name = "msm-dai-q6-dev.32771", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_INCALL_RECORD_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + }, + /* Incall Music BACK END DAI Link */ + { + .name = LPASS_BE_VOICE_PLAYBACK_TX, + .stream_name = "Voice Farend Playback", + .cpu_dai_name = "msm-dai-q6-dev.32773", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_VOICE_PLAYBACK_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + }, + /* Incall Music 2 BACK END DAI Link */ + { + .name = LPASS_BE_VOICE2_PLAYBACK_TX, + .stream_name = "Voice2 Farend Playback", + .cpu_dai_name = "msm-dai-q6-dev.32770", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_VOICE2_PLAYBACK_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + }, + { + .name = LPASS_BE_USB_AUDIO_RX, + .stream_name = "USB Audio Playback", + .cpu_dai_name = "msm-dai-q6-dev.28672", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_USB_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_USB_AUDIO_TX, + .stream_name = "USB Audio Capture", + .cpu_dai_name = "msm-dai-q6-dev.28673", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_USB_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_PRI_TDM_RX_0, + .stream_name = "Primary TDM0 Playback", + .cpu_dai_name = "msm-dai-q6-tdm.36864", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_PRI_TDM_RX_0, + .be_hw_params_fixup = msm_tdm_be_hw_params_fixup, + .ops = &sa8155_tdm_be_ops, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + }, + { + .name = LPASS_BE_PRI_TDM_TX_0, + .stream_name = "Primary TDM0 Capture", + .cpu_dai_name = "msm-dai-q6-tdm.36865", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_PRI_TDM_TX_0, + .be_hw_params_fixup = msm_tdm_be_hw_params_fixup, + .ops = &sa8155_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SEC_TDM_RX_0, + .stream_name = "Secondary TDM0 Playback", + .cpu_dai_name = "msm-dai-q6-tdm.36880", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SEC_TDM_RX_0, + .be_hw_params_fixup = msm_tdm_be_hw_params_fixup, + .ops = &sa8155_tdm_be_ops, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + }, + { + .name = LPASS_BE_SEC_TDM_TX_0, + .stream_name = "Secondary TDM0 Capture", + .cpu_dai_name = "msm-dai-q6-tdm.36881", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_SEC_TDM_TX_0, + .be_hw_params_fixup = msm_tdm_be_hw_params_fixup, + .ops = &sa8155_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_TERT_TDM_RX_0, + .stream_name = "Tertiary TDM0 Playback", + .cpu_dai_name = "msm-dai-q6-tdm.36896", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_TERT_TDM_RX_0, + .be_hw_params_fixup = msm_tdm_be_hw_params_fixup, + .ops = &sa8155_tdm_be_ops, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + }, + { + .name = LPASS_BE_TERT_TDM_TX_0, + .stream_name = "Tertiary TDM0 Capture", + .cpu_dai_name = "msm-dai-q6-tdm.36897", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_TERT_TDM_TX_0, + .be_hw_params_fixup = msm_tdm_be_hw_params_fixup, + .ops = &sa8155_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_QUAT_TDM_RX_0, + .stream_name = "Quaternary TDM0 Playback", + .cpu_dai_name = "msm-dai-q6-tdm.36912", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_QUAT_TDM_RX_0, + .be_hw_params_fixup = msm_tdm_be_hw_params_fixup, + .ops = &sa8155_tdm_be_ops, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + }, + { + .name = LPASS_BE_QUAT_TDM_TX_0, + .stream_name = "Quaternary TDM0 Capture", + .cpu_dai_name = "msm-dai-q6-tdm.36913", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_QUAT_TDM_TX_0, + .be_hw_params_fixup = msm_tdm_be_hw_params_fixup, + .ops = &sa8155_tdm_be_ops, + .ignore_suspend = 1, + }, +}; + +static struct snd_soc_dai_link msm_auto_be_dai_links[] = { + /* Backend DAI Links */ + { + .name = LPASS_BE_PRI_TDM_RX_1, + .stream_name = "Primary TDM1 Playback", + .cpu_dai_name = "msm-dai-q6-tdm.36866", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_PRI_TDM_RX_1, + .be_hw_params_fixup = msm_tdm_be_hw_params_fixup, + .ops = &sa8155_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_PRI_TDM_RX_2, + .stream_name = "Primary TDM2 Playback", + .cpu_dai_name = "msm-dai-q6-tdm.36868", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_PRI_TDM_RX_2, + .be_hw_params_fixup = msm_tdm_be_hw_params_fixup, + .ops = &sa8155_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_PRI_TDM_RX_3, + .stream_name = "Primary TDM3 Playback", + .cpu_dai_name = "msm-dai-q6-tdm.36870", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_PRI_TDM_RX_3, + .be_hw_params_fixup = msm_tdm_be_hw_params_fixup, + .ops = &sa8155_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_PRI_TDM_TX_1, + .stream_name = "Primary TDM1 Capture", + .cpu_dai_name = "msm-dai-q6-tdm.36867", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_PRI_TDM_TX_1, + .be_hw_params_fixup = msm_tdm_be_hw_params_fixup, + .ops = &sa8155_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_PRI_TDM_TX_2, + .stream_name = "Primary TDM2 Capture", + .cpu_dai_name = "msm-dai-q6-tdm.36869", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_PRI_TDM_TX_2, + .be_hw_params_fixup = msm_tdm_be_hw_params_fixup, + .ops = &sa8155_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_PRI_TDM_TX_3, + .stream_name = "Primary TDM3 Capture", + .cpu_dai_name = "msm-dai-q6-tdm.36871", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_PRI_TDM_TX_3, + .be_hw_params_fixup = msm_tdm_be_hw_params_fixup, + .ops = &sa8155_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SEC_TDM_RX_1, + .stream_name = "Secondary TDM1 Playback", + .cpu_dai_name = "msm-dai-q6-tdm.36882", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SEC_TDM_RX_1, + .be_hw_params_fixup = msm_tdm_be_hw_params_fixup, + .ops = &sa8155_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SEC_TDM_RX_2, + .stream_name = "Secondary TDM2 Playback", + .cpu_dai_name = "msm-dai-q6-tdm.36884", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SEC_TDM_RX_2, + .be_hw_params_fixup = msm_tdm_be_hw_params_fixup, + .ops = &sa8155_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SEC_TDM_RX_3, + .stream_name = "Secondary TDM3 Playback", + .cpu_dai_name = "msm-dai-q6-tdm.36886", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SEC_TDM_RX_3, + .be_hw_params_fixup = msm_tdm_be_hw_params_fixup, + .ops = &sa8155_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SEC_TDM_RX_7, + .stream_name = "Secondary TDM7 Playback", + .cpu_dai_name = "msm-dai-q6-tdm.36894", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SEC_TDM_RX_7, + .be_hw_params_fixup = msm_tdm_be_hw_params_fixup, + .ops = &sa8155_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SEC_TDM_TX_1, + .stream_name = "Secondary TDM1 Capture", + .cpu_dai_name = "msm-dai-q6-tdm.36883", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_SEC_TDM_TX_1, + .be_hw_params_fixup = msm_tdm_be_hw_params_fixup, + .ops = &sa8155_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SEC_TDM_TX_2, + .stream_name = "Secondary TDM2 Capture", + .cpu_dai_name = "msm-dai-q6-tdm.36885", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_SEC_TDM_TX_2, + .be_hw_params_fixup = msm_tdm_be_hw_params_fixup, + .ops = &sa8155_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SEC_TDM_TX_3, + .stream_name = "Secondary TDM3 Capture", + .cpu_dai_name = "msm-dai-q6-tdm.36887", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_SEC_TDM_TX_3, + .be_hw_params_fixup = msm_tdm_be_hw_params_fixup, + .ops = &sa8155_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_TERT_TDM_RX_1, + .stream_name = "Tertiary TDM1 Playback", + .cpu_dai_name = "msm-dai-q6-tdm.36898", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_TERT_TDM_RX_1, + .be_hw_params_fixup = msm_tdm_be_hw_params_fixup, + .ops = &sa8155_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_TERT_TDM_RX_2, + .stream_name = "Tertiary TDM2 Playback", + .cpu_dai_name = "msm-dai-q6-tdm.36900", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_TERT_TDM_RX_2, + .be_hw_params_fixup = msm_tdm_be_hw_params_fixup, + .ops = &sa8155_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_TERT_TDM_RX_3, + .stream_name = "Tertiary TDM3 Playback", + .cpu_dai_name = "msm-dai-q6-tdm.36902", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_TERT_TDM_RX_3, + .be_hw_params_fixup = msm_tdm_be_hw_params_fixup, + .ops = &sa8155_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_TERT_TDM_RX_4, + .stream_name = "Tertiary TDM4 Playback", + .cpu_dai_name = "msm-dai-q6-tdm.36904", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_TERT_TDM_RX_4, + .be_hw_params_fixup = msm_tdm_be_hw_params_fixup, + .ops = &sa8155_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_TERT_TDM_TX_1, + .stream_name = "Tertiary TDM1 Capture", + .cpu_dai_name = "msm-dai-q6-tdm.36899", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_TERT_TDM_TX_1, + .be_hw_params_fixup = msm_tdm_be_hw_params_fixup, + .ops = &sa8155_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_TERT_TDM_TX_2, + .stream_name = "Tertiary TDM2 Capture", + .cpu_dai_name = "msm-dai-q6-tdm.36901", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_TERT_TDM_TX_2, + .be_hw_params_fixup = msm_tdm_be_hw_params_fixup, + .ops = &sa8155_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_TERT_TDM_TX_3, + .stream_name = "Tertiary TDM3 Capture", + .cpu_dai_name = "msm-dai-q6-tdm.36903", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_TERT_TDM_TX_3, + .be_hw_params_fixup = msm_tdm_be_hw_params_fixup, + .ops = &sa8155_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_TERT_TDM_TX_7, + .stream_name = "Tertiary TDM7 Capture", + .cpu_dai_name = "msm-dai-q6-tdm.36911", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_TERT_TDM_TX_7, + .be_hw_params_fixup = msm_tdm_be_hw_params_fixup, + .ops = &sa8155_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_QUAT_TDM_RX_1, + .stream_name = "Quaternary TDM1 Playback", + .cpu_dai_name = "msm-dai-q6-tdm.36914", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_QUAT_TDM_RX_1, + .be_hw_params_fixup = msm_tdm_be_hw_params_fixup, + .ops = &sa8155_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_QUAT_TDM_RX_2, + .stream_name = "Quaternary TDM2 Playback", + .cpu_dai_name = "msm-dai-q6-tdm.36916", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_QUAT_TDM_RX_2, + .be_hw_params_fixup = msm_tdm_be_hw_params_fixup, + .ops = &sa8155_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_QUAT_TDM_RX_3, + .stream_name = "Quaternary TDM3 Playback", + .cpu_dai_name = "msm-dai-q6-tdm.36918", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_QUAT_TDM_RX_3, + .be_hw_params_fixup = msm_tdm_be_hw_params_fixup, + .ops = &sa8155_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_QUAT_TDM_TX_1, + .stream_name = "Quaternary TDM1 Capture", + .cpu_dai_name = "msm-dai-q6-tdm.36915", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_QUAT_TDM_TX_1, + .be_hw_params_fixup = msm_tdm_be_hw_params_fixup, + .ops = &sa8155_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_QUAT_TDM_TX_2, + .stream_name = "Quaternary TDM2 Capture", + .cpu_dai_name = "msm-dai-q6-tdm.36917", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_QUAT_TDM_TX_2, + .be_hw_params_fixup = msm_tdm_be_hw_params_fixup, + .ops = &sa8155_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_QUAT_TDM_TX_3, + .stream_name = "Quaternary TDM3 Capture", + .cpu_dai_name = "msm-dai-q6-tdm.36919", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_QUAT_TDM_TX_3, + .be_hw_params_fixup = msm_tdm_be_hw_params_fixup, + .ops = &sa8155_tdm_be_ops, + .ignore_suspend = 1, + }, +}; + +static struct snd_soc_dai_link ext_disp_be_dai_link[] = { + /* DISP PORT BACK END DAI Link */ + { + .name = LPASS_BE_DISPLAY_PORT, + .stream_name = "Display Port Playback", + .cpu_dai_name = "msm-dai-q6-dp.24608", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-ext-disp-audio-codec-rx", + .codec_dai_name = "msm_dp_audio_codec_rx_dai", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_DISPLAY_PORT_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, +}; + +static struct snd_soc_dai_link msm_mi2s_be_dai_links[] = { + { + .name = LPASS_BE_PRI_MI2S_RX, + .stream_name = "Primary MI2S Playback", + .cpu_dai_name = "msm-dai-q6-mi2s.0", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_PRI_MI2S_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + }, + { + .name = LPASS_BE_PRI_MI2S_TX, + .stream_name = "Primary MI2S Capture", + .cpu_dai_name = "msm-dai-q6-mi2s.0", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_PRI_MI2S_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SEC_MI2S_RX, + .stream_name = "Secondary MI2S Playback", + .cpu_dai_name = "msm-dai-q6-mi2s.1", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SECONDARY_MI2S_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + }, + { + .name = LPASS_BE_SEC_MI2S_TX, + .stream_name = "Secondary MI2S Capture", + .cpu_dai_name = "msm-dai-q6-mi2s.1", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_SECONDARY_MI2S_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_TERT_MI2S_RX, + .stream_name = "Tertiary MI2S Playback", + .cpu_dai_name = "msm-dai-q6-mi2s.2", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_TERTIARY_MI2S_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + }, + { + .name = LPASS_BE_TERT_MI2S_TX, + .stream_name = "Tertiary MI2S Capture", + .cpu_dai_name = "msm-dai-q6-mi2s.2", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_TERTIARY_MI2S_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_QUAT_MI2S_RX, + .stream_name = "Quaternary MI2S Playback", + .cpu_dai_name = "msm-dai-q6-mi2s.3", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_QUATERNARY_MI2S_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + }, + { + .name = LPASS_BE_QUAT_MI2S_TX, + .stream_name = "Quaternary MI2S Capture", + .cpu_dai_name = "msm-dai-q6-mi2s.3", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_QUIN_MI2S_RX, + .stream_name = "Quinary MI2S Playback", + .cpu_dai_name = "msm-dai-q6-mi2s.4", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_QUINARY_MI2S_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + }, + { + .name = LPASS_BE_QUIN_MI2S_TX, + .stream_name = "Quinary MI2S Capture", + .cpu_dai_name = "msm-dai-q6-mi2s.4", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_QUINARY_MI2S_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + }, +}; + +static struct snd_soc_dai_link msm_auxpcm_be_dai_links[] = { + /* Primary AUX PCM Backend DAI Links */ + { + .name = LPASS_BE_AUXPCM_RX, + .stream_name = "AUX PCM Playback", + .cpu_dai_name = "msm-dai-q6-auxpcm.1", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_AUXPCM_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_AUXPCM_TX, + .stream_name = "AUX PCM Capture", + .cpu_dai_name = "msm-dai-q6-auxpcm.1", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_AUXPCM_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + }, + /* Secondary AUX PCM Backend DAI Links */ + { + .name = LPASS_BE_SEC_AUXPCM_RX, + .stream_name = "Sec AUX PCM Playback", + .cpu_dai_name = "msm-dai-q6-auxpcm.2", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SEC_AUXPCM_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SEC_AUXPCM_TX, + .stream_name = "Sec AUX PCM Capture", + .cpu_dai_name = "msm-dai-q6-auxpcm.2", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_SEC_AUXPCM_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + }, + /* Tertiary AUX PCM Backend DAI Links */ + { + .name = LPASS_BE_TERT_AUXPCM_RX, + .stream_name = "Tert AUX PCM Playback", + .cpu_dai_name = "msm-dai-q6-auxpcm.3", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_TERT_AUXPCM_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_TERT_AUXPCM_TX, + .stream_name = "Tert AUX PCM Capture", + .cpu_dai_name = "msm-dai-q6-auxpcm.3", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_TERT_AUXPCM_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + }, + /* Quaternary AUX PCM Backend DAI Links */ + { + .name = LPASS_BE_QUAT_AUXPCM_RX, + .stream_name = "Quat AUX PCM Playback", + .cpu_dai_name = "msm-dai-q6-auxpcm.4", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_QUAT_AUXPCM_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_QUAT_AUXPCM_TX, + .stream_name = "Quat AUX PCM Capture", + .cpu_dai_name = "msm-dai-q6-auxpcm.4", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_QUAT_AUXPCM_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + }, + /* Quinary AUX PCM Backend DAI Links */ + { + .name = LPASS_BE_QUIN_AUXPCM_RX, + .stream_name = "Quin AUX PCM Playback", + .cpu_dai_name = "msm-dai-q6-auxpcm.5", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_QUIN_AUXPCM_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_QUIN_AUXPCM_TX, + .stream_name = "Quin AUX PCM Capture", + .cpu_dai_name = "msm-dai-q6-auxpcm.5", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_QUIN_AUXPCM_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + }, +}; + +static struct snd_soc_dai_link msm_auto_dai_links[ + ARRAY_SIZE(msm_common_dai_links) + + ARRAY_SIZE(msm_auto_fe_dai_links) + + ARRAY_SIZE(msm_common_be_dai_links) + + ARRAY_SIZE(msm_auto_be_dai_links) + + ARRAY_SIZE(ext_disp_be_dai_link) + + ARRAY_SIZE(msm_mi2s_be_dai_links) + + ARRAY_SIZE(msm_auxpcm_be_dai_links)]; + +static struct snd_soc_dai_link msm_auto_custom_dai_links[ + ARRAY_SIZE(msm_custom_fe_dai_links) + + ARRAY_SIZE(msm_auto_fe_dai_links) + + ARRAY_SIZE(msm_common_be_dai_links) + + ARRAY_SIZE(msm_auto_be_dai_links) + + ARRAY_SIZE(ext_disp_be_dai_link) + + ARRAY_SIZE(msm_mi2s_be_dai_links) + + ARRAY_SIZE(msm_auxpcm_be_dai_links)]; + +struct snd_soc_card snd_soc_card_auto_msm = { + .name = "sa8155-adp-star-snd-card", +}; + +struct snd_soc_card snd_soc_card_auto_custom_msm = { + .name = "sa8155-custom-snd-card", +}; + +static int msm_populate_dai_link_component_of_node( + struct snd_soc_card *card) +{ + int i, index, ret = 0; + struct device *cdev = card->dev; + struct snd_soc_dai_link *dai_link = card->dai_link; + struct device_node *np; + + if (!cdev) { + pr_err("%s: Sound card device memory NULL\n", __func__); + return -ENODEV; + } + + for (i = 0; i < card->num_links; i++) { + if (dai_link[i].platform_of_node && dai_link[i].cpu_of_node) + continue; + + /* populate platform_of_node for snd card dai links */ + if (dai_link[i].platform_name && + !dai_link[i].platform_of_node) { + index = of_property_match_string(cdev->of_node, + "asoc-platform-names", + dai_link[i].platform_name); + if (index < 0) { + pr_err("%s: No match found for platform name: %s\n", + __func__, dai_link[i].platform_name); + ret = index; + goto err; + } + np = of_parse_phandle(cdev->of_node, "asoc-platform", + index); + if (!np) { + pr_err("%s: retrieving phandle for platform %s, index %d failed\n", + __func__, dai_link[i].platform_name, + index); + ret = -ENODEV; + goto err; + } + dai_link[i].platform_of_node = np; + dai_link[i].platform_name = NULL; + } + + /* populate cpu_of_node for snd card dai links */ + if (dai_link[i].cpu_dai_name && !dai_link[i].cpu_of_node) { + index = of_property_match_string(cdev->of_node, + "asoc-cpu-names", + dai_link[i].cpu_dai_name); + if (index >= 0) { + np = of_parse_phandle(cdev->of_node, "asoc-cpu", + index); + if (!np) { + pr_err("%s: retrieving phandle for cpu dai %s failed\n", + __func__, + dai_link[i].cpu_dai_name); + ret = -ENODEV; + goto err; + } + dai_link[i].cpu_of_node = np; + dai_link[i].cpu_dai_name = NULL; + } + } + + /* populate codec_of_node for snd card dai links */ + if (dai_link[i].codec_name && !dai_link[i].codec_of_node) { + index = of_property_match_string(cdev->of_node, + "asoc-codec-names", + dai_link[i].codec_name); + if (index < 0) + continue; + np = of_parse_phandle(cdev->of_node, "asoc-codec", + index); + if (!np) { + pr_err("%s: retrieving phandle for codec %s failed\n", + __func__, dai_link[i].codec_name); + ret = -ENODEV; + goto err; + } + dai_link[i].codec_of_node = np; + dai_link[i].codec_name = NULL; + } + } + +err: + return ret; +} + +static const struct of_device_id sa8155_asoc_machine_of_match[] = { + { .compatible = "qcom,sa8155-asoc-snd-adp-star", + .data = "adp_star_codec"}, + { .compatible = "qcom,sa8155-asoc-snd-custom", + .data = "custom_codec"}, + {}, +}; + +static struct snd_soc_card *populate_snd_card_dailinks(struct device *dev) +{ + struct snd_soc_card *card = NULL; + struct snd_soc_dai_link *dailink; + int len_1, len_2, len_3; + int total_links; + const struct of_device_id *match; + + match = of_match_node(sa8155_asoc_machine_of_match, dev->of_node); + if (!match) { + dev_err(dev, "%s: No DT match found for sound card\n", + __func__); + return NULL; + } + + if (!strcmp(match->data, "adp_star_codec")) { + card = &snd_soc_card_auto_msm; + len_1 = ARRAY_SIZE(msm_common_dai_links); + len_2 = len_1 + ARRAY_SIZE(msm_auto_fe_dai_links); + len_3 = len_2 + ARRAY_SIZE(msm_common_be_dai_links); + total_links = len_3 + ARRAY_SIZE(msm_auto_be_dai_links); + memcpy(msm_auto_dai_links, + msm_common_dai_links, + sizeof(msm_common_dai_links)); + memcpy(msm_auto_dai_links + len_1, + msm_auto_fe_dai_links, + sizeof(msm_auto_fe_dai_links)); + memcpy(msm_auto_dai_links + len_2, + msm_common_be_dai_links, + sizeof(msm_common_be_dai_links)); + memcpy(msm_auto_dai_links + len_3, + msm_auto_be_dai_links, + sizeof(msm_auto_be_dai_links)); + + if (of_property_read_bool(dev->of_node, + "qcom,ext-disp-audio-rx")) { + dev_dbg(dev, "%s(): ext disp audio support present\n", + __func__); + memcpy(msm_auto_dai_links + total_links, + ext_disp_be_dai_link, + sizeof(ext_disp_be_dai_link)); + total_links += ARRAY_SIZE(ext_disp_be_dai_link); + } + if (of_property_read_bool(dev->of_node, + "qcom,mi2s-audio-intf")) { + memcpy(msm_auto_dai_links + total_links, + msm_mi2s_be_dai_links, + sizeof(msm_mi2s_be_dai_links)); + total_links += ARRAY_SIZE(msm_mi2s_be_dai_links); + } + if (of_property_read_bool(dev->of_node, + "qcom,auxpcm-audio-intf")) { + memcpy(msm_auto_dai_links + total_links, + msm_auxpcm_be_dai_links, + sizeof(msm_auxpcm_be_dai_links)); + total_links += ARRAY_SIZE(msm_auxpcm_be_dai_links); + } + + dailink = msm_auto_dai_links; + } else if (!strcmp(match->data, "custom_codec")) { + card = &snd_soc_card_auto_custom_msm; + len_1 = ARRAY_SIZE(msm_custom_fe_dai_links); + len_2 = len_1 + ARRAY_SIZE(msm_auto_fe_dai_links); + len_3 = len_2 + ARRAY_SIZE(msm_common_be_dai_links); + total_links = len_3 + ARRAY_SIZE(msm_auto_be_dai_links); + memcpy(msm_auto_custom_dai_links, + msm_custom_fe_dai_links, + sizeof(msm_custom_fe_dai_links)); + memcpy(msm_auto_custom_dai_links + len_1, + msm_auto_fe_dai_links, + sizeof(msm_auto_fe_dai_links)); + memcpy(msm_auto_custom_dai_links + len_2, + msm_common_be_dai_links, + sizeof(msm_common_be_dai_links)); + memcpy(msm_auto_custom_dai_links + len_3, + msm_auto_be_dai_links, + sizeof(msm_auto_be_dai_links)); + + if (of_property_read_bool(dev->of_node, + "qcom,ext-disp-audio-rx")) { + dev_dbg(dev, "%s(): ext disp audio support present\n", + __func__); + memcpy(msm_auto_custom_dai_links + total_links, + ext_disp_be_dai_link, + sizeof(ext_disp_be_dai_link)); + total_links += ARRAY_SIZE(ext_disp_be_dai_link); + } + if (of_property_read_bool(dev->of_node, + "qcom,mi2s-audio-intf")) { + memcpy(msm_auto_custom_dai_links + total_links, + msm_mi2s_be_dai_links, + sizeof(msm_mi2s_be_dai_links)); + total_links += ARRAY_SIZE(msm_mi2s_be_dai_links); + } + if (of_property_read_bool(dev->of_node, + "qcom,auxpcm-audio-intf")) { + memcpy(msm_auto_custom_dai_links + total_links, + msm_auxpcm_be_dai_links, + sizeof(msm_auxpcm_be_dai_links)); + total_links += ARRAY_SIZE(msm_auxpcm_be_dai_links); + } + dailink = msm_auto_custom_dai_links; + } else { + dev_err(dev, "%s: Codec not supported\n", + __func__); + return NULL; + } + + if (card) { + card->dai_link = dailink; + card->num_links = total_links; + } + + return card; +} + +/***************************************************************************** +* TO BE UPDATED: Codec/Platform specific tdm slot and offset table selection +*****************************************************************************/ +static int msm_tdm_init(struct device *dev) +{ + const struct of_device_id *match; + + match = of_match_node(sa8155_asoc_machine_of_match, dev->of_node); + if (!match) { + dev_err(dev, "%s: No DT match found for sound card\n", + __func__); + return -EINVAL; + } + + if (!strcmp(match->data, "custom_codec")) { + dev_dbg(dev, "%s: custom tdm configuration\n", __func__); + + memcpy(tdm_rx_slot_offset, + tdm_rx_slot_offset_custom, + sizeof(tdm_rx_slot_offset_custom)); + memcpy(tdm_tx_slot_offset, + tdm_tx_slot_offset_custom, + sizeof(tdm_tx_slot_offset_custom)); + memcpy(tdm_slot, + tdm_slot_custom, + sizeof(tdm_slot_custom)); + } else { + dev_dbg(dev, "%s: default tdm configuration\n", __func__); + } + + return 0; +} + +static void msm_i2s_auxpcm_init(struct platform_device *pdev) +{ + int count; + u32 mi2s_master_slave[MI2S_MAX]; + int ret; + + for (count = 0; count < MI2S_MAX; count++) { + mutex_init(&mi2s_intf_conf[count].lock); + mi2s_intf_conf[count].ref_cnt = 0; + } + + ret = of_property_read_u32_array(pdev->dev.of_node, + "qcom,msm-mi2s-master", + mi2s_master_slave, MI2S_MAX); + if (ret) { + dev_dbg(&pdev->dev, "%s: no qcom,msm-mi2s-master in DT node\n", + __func__); + } else { + for (count = 0; count < MI2S_MAX; count++) { + mi2s_intf_conf[count].msm_is_mi2s_master = + mi2s_master_slave[count]; + } + } +} + +static void msm_i2s_auxpcm_deinit(void) +{ + int count; + + for (count = 0; count < MI2S_MAX; count++) { + mutex_destroy(&mi2s_intf_conf[count].lock); + mi2s_intf_conf[count].ref_cnt = 0; + mi2s_intf_conf[count].msm_is_mi2s_master = 0; + } +} +static int msm_asoc_machine_probe(struct platform_device *pdev) +{ + struct snd_soc_card *card; + struct msm_asoc_mach_data *pdata; + int ret; + enum apr_subsys_state q6_state; + + if (!pdev->dev.of_node) { + dev_err(&pdev->dev, "No platform supplied from device tree\n"); + return -EINVAL; + } + + q6_state = apr_get_q6_state(); + if (q6_state == APR_SUBSYS_DOWN) { + dev_dbg(&pdev->dev, "deferring %s, adsp_state %d\n", + __func__, q6_state); + return -EPROBE_DEFER; + } + + pdata = devm_kzalloc(&pdev->dev, + sizeof(struct msm_asoc_mach_data), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + card = populate_snd_card_dailinks(&pdev->dev); + if (!card) { + dev_err(&pdev->dev, "%s: Card uninitialized\n", __func__); + ret = -EINVAL; + goto err; + } + card->dev = &pdev->dev; + platform_set_drvdata(pdev, card); + snd_soc_card_set_drvdata(card, pdata); + + ret = snd_soc_of_parse_card_name(card, "qcom,model"); + if (ret) { + dev_err(&pdev->dev, "parse card name failed, err:%d\n", + ret); + goto err; + } + + ret = msm_populate_dai_link_component_of_node(card); + if (ret) { + ret = -EPROBE_DEFER; + goto err; + } + + /* Populate controls of snd card */ + card->controls = msm_snd_controls; + card->num_controls = ARRAY_SIZE(msm_snd_controls); + + ret = msm_tdm_init(&pdev->dev); + if (ret) { + ret = -EPROBE_DEFER; + goto err; + } + + ret = devm_snd_soc_register_card(&pdev->dev, card); + if (ret == -EPROBE_DEFER) { + goto err; + } else if (ret) { + dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", + ret); + goto err; + } + dev_info(&pdev->dev, "Sound card %s registered\n", card->name); + + /* Parse pinctrl info from devicetree */ + ret = msm_get_pinctrl(pdev); + if (!ret) { + pr_debug("%s: pinctrl parsing successful\n", __func__); + } else { + dev_dbg(&pdev->dev, + "%s: Parsing pinctrl failed with %d. Cannot use Ports\n", + __func__, ret); + ret = 0; + } + + msm_i2s_auxpcm_init(pdev); + + return 0; +err: + msm_release_pinctrl(pdev); + devm_kfree(&pdev->dev, pdata); + return ret; +} + +static int msm_asoc_machine_remove(struct platform_device *pdev) +{ + msm_i2s_auxpcm_deinit(); + + msm_release_pinctrl(pdev); + return 0; +} + +static struct platform_driver sa8155_asoc_machine_driver = { + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + .pm = &snd_soc_pm_ops, + .of_match_table = sa8155_asoc_machine_of_match, + }, + .probe = msm_asoc_machine_probe, + .remove = msm_asoc_machine_remove, +}; + +static int dummy_asoc_machine_probe(struct platform_device *pdev) +{ + return 0; +} + +static int dummy_asoc_machine_remove(struct platform_device *pdev) +{ + return 0; +} + +static struct platform_device sa8155_dummy_asoc_machine_device = { + .name = "sa8155-asoc-snd-dummy", +}; + +static struct platform_driver sa8155_dummy_asoc_machine_driver = { + .driver = { + .name = "sa8155-asoc-snd-dummy", + .owner = THIS_MODULE, + }, + .probe = dummy_asoc_machine_probe, + .remove = dummy_asoc_machine_remove, +}; + +static int sa8155_notifier_service_cb(struct notifier_block *this, + unsigned long opcode, void *ptr) +{ + pr_debug("%s: Service opcode 0x%lx\n", __func__, opcode); + + switch (opcode) { + case AUDIO_NOTIFIER_SERVICE_DOWN: + break; + case AUDIO_NOTIFIER_SERVICE_UP: + if (is_initial_boot) { + platform_driver_register(&sa8155_dummy_asoc_machine_driver); + platform_device_register(&sa8155_dummy_asoc_machine_device); + is_initial_boot = false; + } + break; + default: + break; + } + + return NOTIFY_OK; +} + +static struct notifier_block service_nb = { + .notifier_call = sa8155_notifier_service_cb, + .priority = -INT_MAX, +}; + +int __init sa8155_init(void) +{ + pr_debug("%s\n", __func__); + audio_notifier_register("sa8155", AUDIO_NOTIFIER_ADSP_DOMAIN, + &service_nb); + return platform_driver_register(&sa8155_asoc_machine_driver); +} + +void sa8155_exit(void) +{ + pr_debug("%s\n", __func__); + platform_driver_unregister(&sa8155_asoc_machine_driver); + audio_notifier_deregister("sa8155"); +} + +MODULE_DESCRIPTION("ALSA SoC msm"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:" DRV_NAME); +MODULE_DEVICE_TABLE(of, sa8155_asoc_machine_of_match); diff --git a/audio-kernel/asoc/sdm660-common.c b/audio-kernel/asoc/sdm660-common.c new file mode 100644 index 000000000..a33bbff22 --- /dev/null +++ b/audio-kernel/asoc/sdm660-common.c @@ -0,0 +1,3551 @@ +/* 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 + * 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 +#include +#include +#include +#include +#include +#include +#include +#include "msm-pcm-routing-v2.h" +#include "sdm660-common.h" +#include "sdm660-internal.h" +#include "sdm660-external.h" +#include "codecs/msm-cdc-pinctrl.h" +#include "codecs/sdm660_cdc/msm-analog-cdc.h" +#include "codecs/wsa881x.h" + +#define __CHIPSET__ "SDM660 " +#define MSM_DAILINK_NAME(name) (__CHIPSET__#name) + +#define DRV_NAME "sdm660-asoc-snd" + +#define MSM_INT_DIGITAL_CODEC "msm-dig-codec" +#define PMIC_INT_ANALOG_CODEC "analog-codec" + +#define DEV_NAME_STR_LEN 32 +#define DEFAULT_MCLK_RATE 9600000 +#define MSM_LL_QOS_VALUE 300 /* time in us to ensure LPM doesn't go in C3/C4 */ + +struct dev_config { + u32 sample_rate; + u32 bit_format; + u32 channels; +}; + +enum { + DP_RX_IDX, + EXT_DISP_RX_IDX_MAX, +}; + +bool codec_reg_done; + +/* TDM default config */ +static struct dev_config tdm_rx_cfg[TDM_INTERFACE_MAX][TDM_PORT_MAX] = { + { /* PRI TDM */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_0 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_1 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_2 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_3 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_4 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_5 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_6 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_7 */ + }, + { /* SEC TDM */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_0 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_1 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_2 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_3 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_4 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_5 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_6 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_7 */ + }, + { /* TERT TDM */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_0 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_1 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_2 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_3 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_4 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_5 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_6 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_7 */ + }, + { /* QUAT TDM */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_0 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_1 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_2 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_3 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_4 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_5 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_6 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_7 */ + }, + { /* QUIN TDM */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_0 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_1 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_2 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_3 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_4 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_5 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_6 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_7 */ + } +}; + +/* TDM default config */ +static struct dev_config tdm_tx_cfg[TDM_INTERFACE_MAX][TDM_PORT_MAX] = { + { /* PRI TDM */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_0 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_1 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_2 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_3 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_4 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_5 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_6 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_7 */ + }, + { /* SEC TDM */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_0 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_1 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_2 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_3 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_4 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_5 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_6 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_7 */ + }, + { /* TERT TDM */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_0 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_1 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_2 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_3 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_4 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_5 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_6 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_7 */ + }, + { /* QUAT TDM */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_0 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_1 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_2 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_3 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_4 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_5 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_6 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_7 */ + }, + { /* QUIN TDM */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_0 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_1 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_2 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_3 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_4 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_5 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_6 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_7 */ + } +}; + +/* Default configuration of external display BE */ +static struct dev_config ext_disp_rx_cfg[] = { + [DP_RX_IDX] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, +}; +static struct dev_config usb_rx_cfg = { + .sample_rate = SAMPLING_RATE_48KHZ, + .bit_format = SNDRV_PCM_FORMAT_S16_LE, + .channels = 2, +}; + +static struct dev_config usb_tx_cfg = { + .sample_rate = SAMPLING_RATE_48KHZ, + .bit_format = SNDRV_PCM_FORMAT_S16_LE, + .channels = 1, +}; + +enum { + PRIM_AUX_PCM = 0, + SEC_AUX_PCM, + TERT_AUX_PCM, + QUAT_AUX_PCM, + QUIN_AUX_PCM, + AUX_PCM_MAX, +}; + +enum { + PCM_I2S_SEL_PRIM = 0, + PCM_I2S_SEL_SEC, + PCM_I2S_SEL_TERT, + PCM_I2S_SEL_QUAT, + PCM_I2S_SEL_QUIN, + PCM_I2S_SEL_MAX, +}; + +struct mi2s_conf { + struct mutex lock; + u32 ref_cnt; + u32 msm_is_mi2s_master; + u32 msm_is_ext_mclk; +}; + +static u32 mi2s_ebit_clk[MI2S_MAX] = { + Q6AFE_LPASS_CLK_ID_PRI_MI2S_EBIT, + Q6AFE_LPASS_CLK_ID_SEC_MI2S_EBIT, + Q6AFE_LPASS_CLK_ID_TER_MI2S_EBIT, + Q6AFE_LPASS_CLK_ID_QUAD_MI2S_EBIT, + Q6AFE_LPASS_CLK_ID_QUI_MI2S_EBIT +}; + +struct msm_wsa881x_dev_info { + struct device_node *of_node; + u32 index; +}; +static struct snd_soc_aux_dev *msm_aux_dev; +static struct snd_soc_codec_conf *msm_codec_conf; + +static bool msm_swap_gnd_mic(struct snd_soc_codec *codec, bool active); + +static struct wcd_mbhc_config mbhc_cfg = { + .read_fw_bin = false, + .calibration = NULL, + .detect_extn_cable = true, + .mono_stero_detection = false, + .swap_gnd_mic = NULL, + .hs_ext_micbias = true, + .key_code[0] = KEY_MEDIA, + .key_code[1] = KEY_VOICECOMMAND, + .key_code[2] = KEY_VOLUMEUP, + .key_code[3] = KEY_VOLUMEDOWN, + .key_code[4] = 0, + .key_code[5] = 0, + .key_code[6] = 0, + .key_code[7] = 0, + .linein_th = 5000, + .moisture_en = false, + .mbhc_micbias = 0, + .anc_micbias = 0, + .enable_anc_mic_detect = false, +}; + +static struct dev_config proxy_rx_cfg = { + .sample_rate = SAMPLING_RATE_48KHZ, + .bit_format = SNDRV_PCM_FORMAT_S16_LE, + .channels = 2, +}; + +/* Default configuration of MI2S channels */ +static struct dev_config mi2s_rx_cfg[] = { + [PRIM_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, + [SEC_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, + [TERT_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, + [QUAT_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, + [QUIN_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, +}; + +static struct dev_config mi2s_tx_cfg[] = { + [PRIM_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SEC_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [TERT_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [QUAT_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [QUIN_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, +}; + +static struct dev_config aux_pcm_rx_cfg[] = { + [PRIM_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SEC_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [TERT_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [QUAT_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [QUIN_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, +}; + +static struct dev_config aux_pcm_tx_cfg[] = { + [PRIM_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SEC_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [TERT_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [QUAT_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [QUIN_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, +}; + +static char const *ch_text[] = {"Two", "Three", "Four", "Five", + "Six", "Seven", "Eight"}; +static const char *const auxpcm_rate_text[] = {"KHZ_8", "KHZ_16"}; +static char const *mi2s_rate_text[] = {"KHZ_8", "KHZ_16", + "KHZ_32", "KHZ_44P1", "KHZ_48", + "KHZ_96", "KHZ_192"}; +static const char *const mi2s_ch_text[] = {"One", "Two", "Three", "Four", + "Five", "Six", "Seven", + "Eight"}; +static char const *bit_format_text[] = {"S16_LE", "S24_LE", "S24_3LE", + "S32_LE"}; +static char const *mi2s_format_text[] = {"S16_LE", "S24_LE", "S24_3LE", + "S32_LE"}; +static char const *tdm_ch_text[] = {"One", "Two", "Three", "Four", + "Five", "Six", "Seven", "Eight"}; +static char const *tdm_bit_format_text[] = {"S16_LE", "S24_LE", "S32_LE"}; +static char const *tdm_sample_rate_text[] = {"KHZ_8", "KHZ_16", "KHZ_32", + "KHZ_44P1", "KHZ_48", "KHZ_96", + "KHZ_192", "KHZ_352P8", "KHZ_384"}; +static const char *const usb_ch_text[] = {"One", "Two", "Three", "Four", + "Five", "Six", "Seven", + "Eight"}; +static char const *usb_sample_rate_text[] = {"KHZ_8", "KHZ_11P025", + "KHZ_16", "KHZ_22P05", + "KHZ_32", "KHZ_44P1", "KHZ_48", + "KHZ_96", "KHZ_192", "KHZ_384"}; +static char const *ext_disp_bit_format_text[] = {"S16_LE", "S24_LE"}; +static char const *ext_disp_sample_rate_text[] = {"KHZ_48", "KHZ_96", + "KHZ_192"}; +static const char *const qos_text[] = {"Disable", "Enable"}; + +static SOC_ENUM_SINGLE_EXT_DECL(ext_disp_rx_chs, ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(proxy_rx_chs, ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(prim_aux_pcm_rx_sample_rate, auxpcm_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(sec_aux_pcm_rx_sample_rate, auxpcm_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(tert_aux_pcm_rx_sample_rate, auxpcm_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(quat_aux_pcm_rx_sample_rate, auxpcm_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(quin_aux_pcm_rx_sample_rate, auxpcm_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(prim_aux_pcm_tx_sample_rate, auxpcm_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(sec_aux_pcm_tx_sample_rate, auxpcm_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(tert_aux_pcm_tx_sample_rate, auxpcm_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(quat_aux_pcm_tx_sample_rate, auxpcm_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(quin_aux_pcm_tx_sample_rate, auxpcm_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(prim_mi2s_rx_sample_rate, mi2s_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(sec_mi2s_rx_sample_rate, mi2s_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(tert_mi2s_rx_sample_rate, mi2s_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(quat_mi2s_rx_sample_rate, mi2s_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(quin_mi2s_rx_sample_rate, mi2s_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(prim_mi2s_tx_sample_rate, mi2s_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(sec_mi2s_tx_sample_rate, mi2s_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(tert_mi2s_tx_sample_rate, mi2s_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(quat_mi2s_tx_sample_rate, mi2s_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(quin_mi2s_tx_sample_rate, mi2s_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(prim_mi2s_rx_format, mi2s_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(sec_mi2s_rx_format, mi2s_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(tert_mi2s_rx_format, mi2s_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(quat_mi2s_rx_format, mi2s_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(quin_mi2s_rx_format, mi2s_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(prim_mi2s_tx_format, mi2s_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(sec_mi2s_tx_format, mi2s_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(tert_mi2s_tx_format, mi2s_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(quat_mi2s_tx_format, mi2s_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(quin_mi2s_tx_format, mi2s_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(prim_mi2s_rx_chs, mi2s_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(prim_mi2s_tx_chs, mi2s_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(sec_mi2s_rx_chs, mi2s_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(sec_mi2s_tx_chs, mi2s_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(tert_mi2s_rx_chs, mi2s_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(tert_mi2s_tx_chs, mi2s_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(quat_mi2s_rx_chs, mi2s_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(quat_mi2s_tx_chs, mi2s_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(quin_mi2s_rx_chs, mi2s_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(quin_mi2s_tx_chs, mi2s_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(usb_rx_chs, usb_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(usb_tx_chs, usb_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(usb_rx_format, bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(usb_tx_format, bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(ext_disp_rx_format, ext_disp_bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(usb_rx_sample_rate, usb_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(usb_tx_sample_rate, usb_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(ext_disp_rx_sample_rate, + ext_disp_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(tdm_tx_chs, tdm_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(tdm_tx_format, tdm_bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(tdm_tx_sample_rate, tdm_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(tdm_rx_chs, tdm_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(tdm_rx_format, tdm_bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(tdm_rx_sample_rate, tdm_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(qos_vote, qos_text); + +static int qos_vote_status; + +static struct afe_clk_set mi2s_clk[MI2S_MAX] = { + { + AFE_API_VERSION_I2S_CONFIG, + Q6AFE_LPASS_CLK_ID_PRI_MI2S_IBIT, + Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ, + Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO, + Q6AFE_LPASS_CLK_ROOT_DEFAULT, + 0, + }, + { + AFE_API_VERSION_I2S_CONFIG, + Q6AFE_LPASS_CLK_ID_SEC_MI2S_IBIT, + Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ, + Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO, + Q6AFE_LPASS_CLK_ROOT_DEFAULT, + 0, + }, + { + AFE_API_VERSION_I2S_CONFIG, + Q6AFE_LPASS_CLK_ID_TER_MI2S_IBIT, + Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ, + Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO, + Q6AFE_LPASS_CLK_ROOT_DEFAULT, + 0, + }, + { + AFE_API_VERSION_I2S_CONFIG, + Q6AFE_LPASS_CLK_ID_QUAD_MI2S_IBIT, + Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ, + Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO, + Q6AFE_LPASS_CLK_ROOT_DEFAULT, + 0, + }, + { + AFE_API_VERSION_I2S_CONFIG, + Q6AFE_LPASS_CLK_ID_QUI_MI2S_IBIT, + Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ, + Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO, + Q6AFE_LPASS_CLK_ROOT_DEFAULT, + 0, + } +}; + +static struct afe_clk_set mi2s_mclk[MI2S_MAX] = { + { + AFE_API_VERSION_I2S_CONFIG, + Q6AFE_LPASS_CLK_ID_MCLK_3, + Q6AFE_LPASS_OSR_CLK_9_P600_MHZ, + Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO, + Q6AFE_LPASS_CLK_ROOT_DEFAULT, + 0, + }, + { + AFE_API_VERSION_I2S_CONFIG, + Q6AFE_LPASS_CLK_ID_MCLK_2, + Q6AFE_LPASS_OSR_CLK_9_P600_MHZ, + Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO, + Q6AFE_LPASS_CLK_ROOT_DEFAULT, + 0, + }, + { + AFE_API_VERSION_I2S_CONFIG, + Q6AFE_LPASS_CLK_ID_MCLK_1, + Q6AFE_LPASS_OSR_CLK_9_P600_MHZ, + Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO, + Q6AFE_LPASS_CLK_ROOT_DEFAULT, + 0, + }, + { + AFE_API_VERSION_I2S_CONFIG, + Q6AFE_LPASS_CLK_ID_MCLK_1, + Q6AFE_LPASS_OSR_CLK_9_P600_MHZ, + Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO, + Q6AFE_LPASS_CLK_ROOT_DEFAULT, + 0, + }, + { + AFE_API_VERSION_I2S_CONFIG, + Q6AFE_LPASS_CLK_ID_QUI_MI2S_OSR, + Q6AFE_LPASS_OSR_CLK_9_P600_MHZ, + Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO, + Q6AFE_LPASS_CLK_ROOT_DEFAULT, + 0, + } +}; + +static struct mi2s_conf mi2s_intf_conf[MI2S_MAX]; + +static int proxy_rx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s: proxy_rx channels = %d\n", + __func__, proxy_rx_cfg.channels); + ucontrol->value.integer.value[0] = proxy_rx_cfg.channels - 2; + + return 0; +} + +static int proxy_rx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + proxy_rx_cfg.channels = ucontrol->value.integer.value[0] + 2; + pr_debug("%s: proxy_rx channels = %d\n", + __func__, proxy_rx_cfg.channels); + + return 1; +} + +static int tdm_get_sample_rate(int value) +{ + int sample_rate = 0; + + switch (value) { + case 0: + sample_rate = SAMPLING_RATE_8KHZ; + break; + case 1: + sample_rate = SAMPLING_RATE_16KHZ; + break; + case 2: + sample_rate = SAMPLING_RATE_32KHZ; + break; + case 3: + sample_rate = SAMPLING_RATE_44P1KHZ; + break; + case 4: + sample_rate = SAMPLING_RATE_48KHZ; + break; + case 5: + sample_rate = SAMPLING_RATE_96KHZ; + break; + case 6: + sample_rate = SAMPLING_RATE_192KHZ; + break; + case 7: + sample_rate = SAMPLING_RATE_352P8KHZ; + break; + case 8: + sample_rate = SAMPLING_RATE_384KHZ; + break; + default: + sample_rate = SAMPLING_RATE_48KHZ; + break; + } + return sample_rate; +} + +static int tdm_get_sample_rate_val(int sample_rate) +{ + int sample_rate_val = 0; + + switch (sample_rate) { + case SAMPLING_RATE_8KHZ: + sample_rate_val = 0; + break; + case SAMPLING_RATE_16KHZ: + sample_rate_val = 1; + break; + case SAMPLING_RATE_32KHZ: + sample_rate_val = 2; + break; + case SAMPLING_RATE_44P1KHZ: + sample_rate_val = 3; + break; + case SAMPLING_RATE_48KHZ: + sample_rate_val = 4; + break; + case SAMPLING_RATE_96KHZ: + sample_rate_val = 5; + break; + case SAMPLING_RATE_192KHZ: + sample_rate_val = 6; + break; + case SAMPLING_RATE_352P8KHZ: + sample_rate_val = 7; + break; + case SAMPLING_RATE_384KHZ: + sample_rate_val = 8; + break; + default: + sample_rate_val = 4; + break; + } + return sample_rate_val; +} + +static int tdm_get_port_idx(struct snd_kcontrol *kcontrol, + struct tdm_port *port) +{ + if (port) { + if (strnstr(kcontrol->id.name, "PRI", + sizeof(kcontrol->id.name))) { + port->mode = TDM_PRI; + } else if (strnstr(kcontrol->id.name, "SEC", + sizeof(kcontrol->id.name))) { + port->mode = TDM_SEC; + } else if (strnstr(kcontrol->id.name, "TERT", + sizeof(kcontrol->id.name))) { + port->mode = TDM_TERT; + } else if (strnstr(kcontrol->id.name, "QUAT", + sizeof(kcontrol->id.name))) { + port->mode = TDM_QUAT; + } else if (strnstr(kcontrol->id.name, "QUIN", + sizeof(kcontrol->id.name))) { + port->mode = TDM_QUIN; + } else { + pr_err("%s: unsupported mode in: %s", + __func__, kcontrol->id.name); + return -EINVAL; + } + + if (strnstr(kcontrol->id.name, "RX_0", + sizeof(kcontrol->id.name)) || + strnstr(kcontrol->id.name, "TX_0", + sizeof(kcontrol->id.name))) { + port->channel = TDM_0; + } else if (strnstr(kcontrol->id.name, "RX_1", + sizeof(kcontrol->id.name)) || + strnstr(kcontrol->id.name, "TX_1", + sizeof(kcontrol->id.name))) { + port->channel = TDM_1; + } else if (strnstr(kcontrol->id.name, "RX_2", + sizeof(kcontrol->id.name)) || + strnstr(kcontrol->id.name, "TX_2", + sizeof(kcontrol->id.name))) { + port->channel = TDM_2; + } else if (strnstr(kcontrol->id.name, "RX_3", + sizeof(kcontrol->id.name)) || + strnstr(kcontrol->id.name, "TX_3", + sizeof(kcontrol->id.name))) { + port->channel = TDM_3; + } else if (strnstr(kcontrol->id.name, "RX_4", + sizeof(kcontrol->id.name)) || + strnstr(kcontrol->id.name, "TX_4", + sizeof(kcontrol->id.name))) { + port->channel = TDM_4; + } else if (strnstr(kcontrol->id.name, "RX_5", + sizeof(kcontrol->id.name)) || + strnstr(kcontrol->id.name, "TX_5", + sizeof(kcontrol->id.name))) { + port->channel = TDM_5; + } else if (strnstr(kcontrol->id.name, "RX_6", + sizeof(kcontrol->id.name)) || + strnstr(kcontrol->id.name, "TX_6", + sizeof(kcontrol->id.name))) { + port->channel = TDM_6; + } else if (strnstr(kcontrol->id.name, "RX_7", + sizeof(kcontrol->id.name)) || + strnstr(kcontrol->id.name, "TX_7", + sizeof(kcontrol->id.name))) { + port->channel = TDM_7; + } else { + pr_err("%s: unsupported channel in: %s", + __func__, kcontrol->id.name); + return -EINVAL; + } + } else + return -EINVAL; + return 0; +} + +static int tdm_rx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + ucontrol->value.enumerated.item[0] = tdm_get_sample_rate_val( + tdm_rx_cfg[port.mode][port.channel].sample_rate); + + pr_debug("%s: tdm_rx_sample_rate = %d, item = %d\n", __func__, + tdm_rx_cfg[port.mode][port.channel].sample_rate, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_rx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + tdm_rx_cfg[port.mode][port.channel].sample_rate = + tdm_get_sample_rate(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: tdm_rx_sample_rate = %d, item = %d\n", __func__, + tdm_rx_cfg[port.mode][port.channel].sample_rate, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_tx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + ucontrol->value.enumerated.item[0] = tdm_get_sample_rate_val( + tdm_tx_cfg[port.mode][port.channel].sample_rate); + + pr_debug("%s: tdm_tx_sample_rate = %d, item = %d\n", __func__, + tdm_tx_cfg[port.mode][port.channel].sample_rate, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_tx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + tdm_tx_cfg[port.mode][port.channel].sample_rate = + tdm_get_sample_rate(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: tdm_tx_sample_rate = %d, item = %d\n", __func__, + tdm_tx_cfg[port.mode][port.channel].sample_rate, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_get_format(int value) +{ + int format = 0; + + switch (value) { + case 0: + format = SNDRV_PCM_FORMAT_S16_LE; + break; + case 1: + format = SNDRV_PCM_FORMAT_S24_LE; + break; + case 2: + format = SNDRV_PCM_FORMAT_S32_LE; + break; + default: + format = SNDRV_PCM_FORMAT_S16_LE; + break; + } + return format; +} + +static int tdm_get_format_val(int format) +{ + int value = 0; + + switch (format) { + case SNDRV_PCM_FORMAT_S16_LE: + value = 0; + break; + case SNDRV_PCM_FORMAT_S24_LE: + value = 1; + break; + case SNDRV_PCM_FORMAT_S32_LE: + value = 2; + break; + default: + value = 0; + break; + } + return value; +} + +static int mi2s_get_format(int value) +{ + int format = 0; + + switch (value) { + case 0: + format = SNDRV_PCM_FORMAT_S16_LE; + break; + case 1: + format = SNDRV_PCM_FORMAT_S24_LE; + break; + case 2: + format = SNDRV_PCM_FORMAT_S24_3LE; + break; + case 3: + format = SNDRV_PCM_FORMAT_S32_LE; + break; + default: + format = SNDRV_PCM_FORMAT_S16_LE; + break; + } + return format; +} + +static int mi2s_get_format_value(int format) +{ + int value = 0; + + switch (format) { + case SNDRV_PCM_FORMAT_S16_LE: + value = 0; + break; + case SNDRV_PCM_FORMAT_S24_LE: + value = 1; + break; + case SNDRV_PCM_FORMAT_S24_3LE: + value = 2; + break; + case SNDRV_PCM_FORMAT_S32_LE: + value = 3; + break; + default: + value = 0; + break; + } + return value; +} + +static int tdm_rx_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + ucontrol->value.enumerated.item[0] = tdm_get_format_val( + tdm_rx_cfg[port.mode][port.channel].bit_format); + + pr_debug("%s: tdm_rx_bit_format = %d, item = %d\n", __func__, + tdm_rx_cfg[port.mode][port.channel].bit_format, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_rx_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + tdm_rx_cfg[port.mode][port.channel].bit_format = + tdm_get_format(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: tdm_rx_bit_format = %d, item = %d\n", __func__, + tdm_rx_cfg[port.mode][port.channel].bit_format, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_tx_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + ucontrol->value.enumerated.item[0] = tdm_get_format_val( + tdm_tx_cfg[port.mode][port.channel].bit_format); + + pr_debug("%s: tdm_tx_bit_format = %d, item = %d\n", __func__, + tdm_tx_cfg[port.mode][port.channel].bit_format, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_tx_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + tdm_tx_cfg[port.mode][port.channel].bit_format = + tdm_get_format(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: tdm_tx_bit_format = %d, item = %d\n", __func__, + tdm_tx_cfg[port.mode][port.channel].bit_format, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_rx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + + ucontrol->value.enumerated.item[0] = + tdm_rx_cfg[port.mode][port.channel].channels - 1; + + pr_debug("%s: tdm_rx_ch = %d, item = %d\n", __func__, + tdm_rx_cfg[port.mode][port.channel].channels - 1, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_rx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + tdm_rx_cfg[port.mode][port.channel].channels = + ucontrol->value.enumerated.item[0] + 1; + + pr_debug("%s: tdm_rx_ch = %d, item = %d\n", __func__, + tdm_rx_cfg[port.mode][port.channel].channels, + ucontrol->value.enumerated.item[0] + 1); + } + return ret; +} + +static int tdm_tx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + ucontrol->value.enumerated.item[0] = + tdm_tx_cfg[port.mode][port.channel].channels - 1; + + pr_debug("%s: tdm_tx_ch = %d, item = %d\n", __func__, + tdm_tx_cfg[port.mode][port.channel].channels - 1, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_tx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + tdm_tx_cfg[port.mode][port.channel].channels = + ucontrol->value.enumerated.item[0] + 1; + + pr_debug("%s: tdm_tx_ch = %d, item = %d\n", __func__, + tdm_tx_cfg[port.mode][port.channel].channels, + ucontrol->value.enumerated.item[0] + 1); + } + return ret; +} + +static int aux_pcm_get_sample_rate(int value) +{ + int sample_rate; + + switch (value) { + case 1: + sample_rate = SAMPLING_RATE_16KHZ; + break; + case 0: + default: + sample_rate = SAMPLING_RATE_8KHZ; + break; + } + return sample_rate; +} + +static int aux_pcm_get_sample_rate_val(int sample_rate) +{ + int sample_rate_val; + + switch (sample_rate) { + case SAMPLING_RATE_16KHZ: + sample_rate_val = 1; + break; + case SAMPLING_RATE_8KHZ: + default: + sample_rate_val = 0; + break; + } + return sample_rate_val; +} + +static int aux_pcm_get_port_idx(struct snd_kcontrol *kcontrol) +{ + int idx; + + if (strnstr(kcontrol->id.name, "PRIM_AUX_PCM", + sizeof("PRIM_AUX_PCM"))) + idx = PRIM_AUX_PCM; + else if (strnstr(kcontrol->id.name, "SEC_AUX_PCM", + sizeof("SEC_AUX_PCM"))) + idx = SEC_AUX_PCM; + else if (strnstr(kcontrol->id.name, "TERT_AUX_PCM", + sizeof("TERT_AUX_PCM"))) + idx = TERT_AUX_PCM; + else if (strnstr(kcontrol->id.name, "QUAT_AUX_PCM", + sizeof("QUAT_AUX_PCM"))) + idx = QUAT_AUX_PCM; + else if (strnstr(kcontrol->id.name, "QUIN_AUX_PCM", + sizeof("QUIN_AUX_PCM"))) + idx = QUIN_AUX_PCM; + else { + pr_err("%s: unsupported port: %s", + __func__, kcontrol->id.name); + idx = -EINVAL; + } + + return idx; +} + +static int aux_pcm_rx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = aux_pcm_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + aux_pcm_rx_cfg[idx].sample_rate = + aux_pcm_get_sample_rate(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: idx[%d]_rx_sample_rate = %d, item = %d\n", __func__, + idx, aux_pcm_rx_cfg[idx].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int aux_pcm_rx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = aux_pcm_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + ucontrol->value.enumerated.item[0] = + aux_pcm_get_sample_rate_val(aux_pcm_rx_cfg[idx].sample_rate); + + pr_debug("%s: idx[%d]_rx_sample_rate = %d, item = %d\n", __func__, + idx, aux_pcm_rx_cfg[idx].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int aux_pcm_tx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = aux_pcm_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + aux_pcm_tx_cfg[idx].sample_rate = + aux_pcm_get_sample_rate(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: idx[%d]_tx_sample_rate = %d, item = %d\n", __func__, + idx, aux_pcm_tx_cfg[idx].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int aux_pcm_tx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = aux_pcm_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + ucontrol->value.enumerated.item[0] = + aux_pcm_get_sample_rate_val(aux_pcm_tx_cfg[idx].sample_rate); + + pr_debug("%s: idx[%d]_tx_sample_rate = %d, item = %d\n", __func__, + idx, aux_pcm_tx_cfg[idx].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int mi2s_get_port_idx(struct snd_kcontrol *kcontrol) +{ + int idx; + + if (strnstr(kcontrol->id.name, "PRIM_MI2S_RX", + sizeof("PRIM_MI2S_RX"))) + idx = PRIM_MI2S; + else if (strnstr(kcontrol->id.name, "SEC_MI2S_RX", + sizeof("SEC_MI2S_RX"))) + idx = SEC_MI2S; + else if (strnstr(kcontrol->id.name, "TERT_MI2S_RX", + sizeof("TERT_MI2S_RX"))) + idx = TERT_MI2S; + else if (strnstr(kcontrol->id.name, "QUAT_MI2S_RX", + sizeof("QUAT_MI2S_RX"))) + idx = QUAT_MI2S; + else if (strnstr(kcontrol->id.name, "QUIN_MI2S_RX", + sizeof("QUIN_MI2S_RX"))) + idx = QUIN_MI2S; + else if (strnstr(kcontrol->id.name, "PRIM_MI2S_TX", + sizeof("PRIM_MI2S_TX"))) + idx = PRIM_MI2S; + else if (strnstr(kcontrol->id.name, "SEC_MI2S_TX", + sizeof("SEC_MI2S_TX"))) + idx = SEC_MI2S; + else if (strnstr(kcontrol->id.name, "TERT_MI2S_TX", + sizeof("TERT_MI2S_TX"))) + idx = TERT_MI2S; + else if (strnstr(kcontrol->id.name, "QUAT_MI2S_TX", + sizeof("QUAT_MI2S_TX"))) + idx = QUAT_MI2S; + else if (strnstr(kcontrol->id.name, "QUIN_MI2S_TX", + sizeof("QUIN_MI2S_TX"))) + idx = QUIN_MI2S; + else { + pr_err("%s: unsupported channel: %s", + __func__, kcontrol->id.name); + idx = -EINVAL; + } + + return idx; +} + +static int mi2s_get_sample_rate_val(int sample_rate) +{ + int sample_rate_val; + + switch (sample_rate) { + case SAMPLING_RATE_8KHZ: + sample_rate_val = 0; + break; + case SAMPLING_RATE_16KHZ: + sample_rate_val = 1; + break; + case SAMPLING_RATE_32KHZ: + sample_rate_val = 2; + break; + case SAMPLING_RATE_44P1KHZ: + sample_rate_val = 3; + break; + case SAMPLING_RATE_48KHZ: + sample_rate_val = 4; + break; + case SAMPLING_RATE_96KHZ: + sample_rate_val = 5; + break; + case SAMPLING_RATE_192KHZ: + sample_rate_val = 6; + break; + default: + sample_rate_val = 4; + break; + } + return sample_rate_val; +} + +static int mi2s_get_sample_rate(int value) +{ + int sample_rate; + + switch (value) { + case 0: + sample_rate = SAMPLING_RATE_8KHZ; + break; + case 1: + sample_rate = SAMPLING_RATE_16KHZ; + break; + case 2: + sample_rate = SAMPLING_RATE_32KHZ; + break; + case 3: + sample_rate = SAMPLING_RATE_44P1KHZ; + break; + case 4: + sample_rate = SAMPLING_RATE_48KHZ; + break; + case 5: + sample_rate = SAMPLING_RATE_96KHZ; + break; + case 6: + sample_rate = SAMPLING_RATE_192KHZ; + break; + default: + sample_rate = SAMPLING_RATE_48KHZ; + break; + } + return sample_rate; +} + +static int mi2s_rx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + mi2s_rx_cfg[idx].sample_rate = + mi2s_get_sample_rate(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: idx[%d]_rx_sample_rate = %d, item = %d\n", __func__, + idx, mi2s_rx_cfg[idx].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int mi2s_rx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + ucontrol->value.enumerated.item[0] = + mi2s_get_sample_rate_val(mi2s_rx_cfg[idx].sample_rate); + + pr_debug("%s: idx[%d]_rx_sample_rate = %d, item = %d\n", __func__, + idx, mi2s_rx_cfg[idx].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int mi2s_tx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + mi2s_tx_cfg[idx].sample_rate = + mi2s_get_sample_rate(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: idx[%d]_tx_sample_rate = %d, item = %d\n", __func__, + idx, mi2s_tx_cfg[idx].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int mi2s_tx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + ucontrol->value.enumerated.item[0] = + mi2s_get_sample_rate_val(mi2s_tx_cfg[idx].sample_rate); + + pr_debug("%s: idx[%d]_tx_sample_rate = %d, item = %d\n", __func__, + idx, mi2s_tx_cfg[idx].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int mi2s_tx_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + mi2s_tx_cfg[idx].bit_format = + mi2s_get_format(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: idx[%d] _tx_format = %d, item = %d\n", __func__, + idx, mi2s_tx_cfg[idx].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int mi2s_tx_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + ucontrol->value.enumerated.item[0] = + mi2s_get_format_value(mi2s_tx_cfg[idx].bit_format); + + pr_debug("%s: idx[%d]_tx_format = %d, item = %d\n", __func__, + idx, mi2s_tx_cfg[idx].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int mi2s_rx_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + mi2s_rx_cfg[idx].bit_format = + mi2s_get_format(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: idx[%d] _rx_format = %d, item = %d\n", __func__, + idx, mi2s_rx_cfg[idx].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int mi2s_rx_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + ucontrol->value.enumerated.item[0] = + mi2s_get_format_value(mi2s_rx_cfg[idx].bit_format); + + pr_debug("%s: idx[%d]_rx_format = %d, item = %d\n", __func__, + idx, mi2s_rx_cfg[idx].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int msm_mi2s_rx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + pr_debug("%s: msm_mi2s_[%d]_rx_ch = %d\n", __func__, + idx, mi2s_rx_cfg[idx].channels); + ucontrol->value.enumerated.item[0] = mi2s_rx_cfg[idx].channels - 1; + + return 0; +} + +static int msm_mi2s_rx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + mi2s_rx_cfg[idx].channels = ucontrol->value.enumerated.item[0] + 1; + pr_debug("%s: msm_mi2s_[%d]_rx_ch = %d\n", __func__, + idx, mi2s_rx_cfg[idx].channels); + + return 1; +} + +static int msm_mi2s_tx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + pr_debug("%s: msm_mi2s_[%d]_tx_ch = %d\n", __func__, + idx, mi2s_tx_cfg[idx].channels); + ucontrol->value.enumerated.item[0] = mi2s_tx_cfg[idx].channels - 1; + + return 0; +} + +static int msm_mi2s_tx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + mi2s_tx_cfg[idx].channels = ucontrol->value.enumerated.item[0] + 1; + pr_debug("%s: msm_mi2s_[%d]_tx_ch = %d\n", __func__, + idx, mi2s_tx_cfg[idx].channels); + + return 1; +} + +static int usb_audio_rx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s: usb_audio_rx_ch = %d\n", __func__, + usb_rx_cfg.channels); + ucontrol->value.integer.value[0] = usb_rx_cfg.channels - 1; + return 0; +} + +static int usb_audio_rx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + usb_rx_cfg.channels = ucontrol->value.integer.value[0] + 1; + + pr_debug("%s: usb_audio_rx_ch = %d\n", __func__, usb_rx_cfg.channels); + return 1; +} + +static int usb_audio_rx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int sample_rate_val; + + switch (usb_rx_cfg.sample_rate) { + case SAMPLING_RATE_384KHZ: + sample_rate_val = 9; + break; + case SAMPLING_RATE_192KHZ: + sample_rate_val = 8; + break; + case SAMPLING_RATE_96KHZ: + sample_rate_val = 7; + break; + case SAMPLING_RATE_48KHZ: + sample_rate_val = 6; + break; + case SAMPLING_RATE_44P1KHZ: + sample_rate_val = 5; + break; + case SAMPLING_RATE_32KHZ: + sample_rate_val = 4; + break; + case SAMPLING_RATE_22P05KHZ: + sample_rate_val = 3; + break; + case SAMPLING_RATE_16KHZ: + sample_rate_val = 2; + break; + case SAMPLING_RATE_11P025KHZ: + sample_rate_val = 1; + break; + case SAMPLING_RATE_8KHZ: + default: + sample_rate_val = 0; + break; + } + + ucontrol->value.integer.value[0] = sample_rate_val; + pr_debug("%s: usb_audio_rx_sample_rate = %d\n", __func__, + usb_rx_cfg.sample_rate); + return 0; +} + +static int usb_audio_rx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (ucontrol->value.integer.value[0]) { + case 9: + usb_rx_cfg.sample_rate = SAMPLING_RATE_384KHZ; + break; + case 8: + usb_rx_cfg.sample_rate = SAMPLING_RATE_192KHZ; + break; + case 7: + usb_rx_cfg.sample_rate = SAMPLING_RATE_96KHZ; + break; + case 6: + usb_rx_cfg.sample_rate = SAMPLING_RATE_48KHZ; + break; + case 5: + usb_rx_cfg.sample_rate = SAMPLING_RATE_44P1KHZ; + break; + case 4: + usb_rx_cfg.sample_rate = SAMPLING_RATE_32KHZ; + break; + case 3: + usb_rx_cfg.sample_rate = SAMPLING_RATE_22P05KHZ; + break; + case 2: + usb_rx_cfg.sample_rate = SAMPLING_RATE_16KHZ; + break; + case 1: + usb_rx_cfg.sample_rate = SAMPLING_RATE_11P025KHZ; + break; + case 0: + usb_rx_cfg.sample_rate = SAMPLING_RATE_8KHZ; + break; + default: + usb_rx_cfg.sample_rate = SAMPLING_RATE_48KHZ; + break; + } + + pr_debug("%s: control value = %ld, usb_audio_rx_sample_rate = %d\n", + __func__, ucontrol->value.integer.value[0], + usb_rx_cfg.sample_rate); + return 0; +} + +static int usb_audio_rx_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (usb_rx_cfg.bit_format) { + case SNDRV_PCM_FORMAT_S32_LE: + ucontrol->value.integer.value[0] = 3; + break; + case SNDRV_PCM_FORMAT_S24_3LE: + ucontrol->value.integer.value[0] = 2; + break; + case SNDRV_PCM_FORMAT_S24_LE: + ucontrol->value.integer.value[0] = 1; + break; + case SNDRV_PCM_FORMAT_S16_LE: + default: + ucontrol->value.integer.value[0] = 0; + break; + } + + pr_debug("%s: usb_audio_rx_format = %d, ucontrol value = %ld\n", + __func__, usb_rx_cfg.bit_format, + ucontrol->value.integer.value[0]); + return 0; +} + +static int usb_audio_rx_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int rc = 0; + + switch (ucontrol->value.integer.value[0]) { + case 3: + usb_rx_cfg.bit_format = SNDRV_PCM_FORMAT_S32_LE; + break; + case 2: + usb_rx_cfg.bit_format = SNDRV_PCM_FORMAT_S24_3LE; + break; + case 1: + usb_rx_cfg.bit_format = SNDRV_PCM_FORMAT_S24_LE; + break; + case 0: + default: + usb_rx_cfg.bit_format = SNDRV_PCM_FORMAT_S16_LE; + break; + } + pr_debug("%s: usb_audio_rx_format = %d, ucontrol value = %ld\n", + __func__, usb_rx_cfg.bit_format, + ucontrol->value.integer.value[0]); + + return rc; +} + +static int usb_audio_tx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s: usb_audio_tx_ch = %d\n", __func__, + usb_tx_cfg.channels); + ucontrol->value.integer.value[0] = usb_tx_cfg.channels - 1; + return 0; +} + +static int usb_audio_tx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + usb_tx_cfg.channels = ucontrol->value.integer.value[0] + 1; + + pr_debug("%s: usb_audio_tx_ch = %d\n", __func__, usb_tx_cfg.channels); + return 1; +} + +static int usb_audio_tx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int sample_rate_val; + + switch (usb_tx_cfg.sample_rate) { + case SAMPLING_RATE_384KHZ: + sample_rate_val = 9; + break; + case SAMPLING_RATE_192KHZ: + sample_rate_val = 8; + break; + case SAMPLING_RATE_96KHZ: + sample_rate_val = 7; + break; + case SAMPLING_RATE_48KHZ: + sample_rate_val = 6; + break; + case SAMPLING_RATE_44P1KHZ: + sample_rate_val = 5; + break; + case SAMPLING_RATE_32KHZ: + sample_rate_val = 4; + break; + case SAMPLING_RATE_22P05KHZ: + sample_rate_val = 3; + break; + case SAMPLING_RATE_16KHZ: + sample_rate_val = 2; + break; + case SAMPLING_RATE_11P025KHZ: + sample_rate_val = 1; + break; + case SAMPLING_RATE_8KHZ: + sample_rate_val = 0; + break; + default: + sample_rate_val = 6; + break; + } + + ucontrol->value.integer.value[0] = sample_rate_val; + pr_debug("%s: usb_audio_tx_sample_rate = %d\n", __func__, + usb_tx_cfg.sample_rate); + return 0; +} + +static int usb_audio_tx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (ucontrol->value.integer.value[0]) { + case 9: + usb_tx_cfg.sample_rate = SAMPLING_RATE_384KHZ; + break; + case 8: + usb_tx_cfg.sample_rate = SAMPLING_RATE_192KHZ; + break; + case 7: + usb_tx_cfg.sample_rate = SAMPLING_RATE_96KHZ; + break; + case 6: + usb_tx_cfg.sample_rate = SAMPLING_RATE_48KHZ; + break; + case 5: + usb_tx_cfg.sample_rate = SAMPLING_RATE_44P1KHZ; + break; + case 4: + usb_tx_cfg.sample_rate = SAMPLING_RATE_32KHZ; + break; + case 3: + usb_tx_cfg.sample_rate = SAMPLING_RATE_22P05KHZ; + break; + case 2: + usb_tx_cfg.sample_rate = SAMPLING_RATE_16KHZ; + break; + case 1: + usb_tx_cfg.sample_rate = SAMPLING_RATE_11P025KHZ; + break; + case 0: + usb_tx_cfg.sample_rate = SAMPLING_RATE_8KHZ; + break; + default: + usb_tx_cfg.sample_rate = SAMPLING_RATE_48KHZ; + break; + } + + pr_debug("%s: control value = %ld, usb_audio_tx_sample_rate = %d\n", + __func__, ucontrol->value.integer.value[0], + usb_tx_cfg.sample_rate); + return 0; +} + +static int usb_audio_tx_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (usb_tx_cfg.bit_format) { + case SNDRV_PCM_FORMAT_S32_LE: + ucontrol->value.integer.value[0] = 3; + break; + case SNDRV_PCM_FORMAT_S24_3LE: + ucontrol->value.integer.value[0] = 2; + break; + case SNDRV_PCM_FORMAT_S24_LE: + ucontrol->value.integer.value[0] = 1; + break; + case SNDRV_PCM_FORMAT_S16_LE: + default: + ucontrol->value.integer.value[0] = 0; + break; + } + + pr_debug("%s: usb_audio_tx_format = %d, ucontrol value = %ld\n", + __func__, usb_tx_cfg.bit_format, + ucontrol->value.integer.value[0]); + return 0; +} + +static int usb_audio_tx_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int rc = 0; + + switch (ucontrol->value.integer.value[0]) { + case 3: + usb_tx_cfg.bit_format = SNDRV_PCM_FORMAT_S32_LE; + break; + case 2: + usb_tx_cfg.bit_format = SNDRV_PCM_FORMAT_S24_3LE; + break; + case 1: + usb_tx_cfg.bit_format = SNDRV_PCM_FORMAT_S24_LE; + break; + case 0: + default: + usb_tx_cfg.bit_format = SNDRV_PCM_FORMAT_S16_LE; + break; + } + pr_debug("%s: usb_audio_tx_format = %d, ucontrol value = %ld\n", + __func__, usb_tx_cfg.bit_format, + ucontrol->value.integer.value[0]); + + return rc; +} + +static int ext_disp_get_port_idx(struct snd_kcontrol *kcontrol) +{ + int idx; + + if (strnstr(kcontrol->id.name, "Display Port RX", + sizeof("Display Port RX"))) + idx = DP_RX_IDX; + else { + pr_err("%s: unsupported BE: %s", + __func__, kcontrol->id.name); + idx = -EINVAL; + } + + return idx; +} + +static int ext_disp_rx_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = ext_disp_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + switch (ext_disp_rx_cfg[idx].bit_format) { + case SNDRV_PCM_FORMAT_S24_LE: + ucontrol->value.integer.value[0] = 1; + break; + + case SNDRV_PCM_FORMAT_S16_LE: + default: + ucontrol->value.integer.value[0] = 0; + break; + } + + pr_debug("%s: ext_disp_rx[%d].format = %d, ucontrol value = %ld\n", + __func__, idx, ext_disp_rx_cfg[idx].bit_format, + ucontrol->value.integer.value[0]); + return 0; +} + +static int ext_disp_rx_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = ext_disp_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + switch (ucontrol->value.integer.value[0]) { + case 1: + ext_disp_rx_cfg[idx].bit_format = SNDRV_PCM_FORMAT_S24_LE; + break; + case 0: + default: + ext_disp_rx_cfg[idx].bit_format = SNDRV_PCM_FORMAT_S16_LE; + break; + } + pr_debug("%s: ext_disp_rx[%d].format = %d, ucontrol value = %ld\n", + __func__, idx, ext_disp_rx_cfg[idx].bit_format, + ucontrol->value.integer.value[0]); + + return 0; +} + +static int ext_disp_rx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = ext_disp_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + ucontrol->value.integer.value[0] = + ext_disp_rx_cfg[idx].channels - 2; + + pr_debug("%s: ext_disp_rx[%d].ch = %d\n", __func__, + idx, ext_disp_rx_cfg[idx].channels); + + return 0; +} + +static int ext_disp_rx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = ext_disp_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + ext_disp_rx_cfg[idx].channels = + ucontrol->value.integer.value[0] + 2; + + pr_debug("%s: ext_disp_rx[%d].ch = %d\n", __func__, + idx, ext_disp_rx_cfg[idx].channels); + return 1; +} + +static int ext_disp_rx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int sample_rate_val; + int idx = ext_disp_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + switch (ext_disp_rx_cfg[idx].sample_rate) { + case SAMPLING_RATE_192KHZ: + sample_rate_val = 2; + break; + + case SAMPLING_RATE_96KHZ: + sample_rate_val = 1; + break; + + case SAMPLING_RATE_48KHZ: + default: + sample_rate_val = 0; + break; + } + + ucontrol->value.integer.value[0] = sample_rate_val; + pr_debug("%s: ext_disp_rx[%d].sample_rate = %d\n", __func__, + idx, ext_disp_rx_cfg[idx].sample_rate); + + return 0; +} + +static int ext_disp_rx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = ext_disp_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + switch (ucontrol->value.integer.value[0]) { + case 2: + ext_disp_rx_cfg[idx].sample_rate = SAMPLING_RATE_192KHZ; + break; + case 1: + ext_disp_rx_cfg[idx].sample_rate = SAMPLING_RATE_96KHZ; + break; + case 0: + default: + ext_disp_rx_cfg[idx].sample_rate = SAMPLING_RATE_48KHZ; + break; + } + + pr_debug("%s: control value = %ld, ext_disp_rx[%d].sample_rate = %d\n", + __func__, ucontrol->value.integer.value[0], idx, + ext_disp_rx_cfg[idx].sample_rate); + return 0; +} + +static int msm_qos_ctl_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.enumerated.item[0] = qos_vote_status; + + return 0; +} + +static int msm_qos_ctl_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct snd_soc_card *card = codec->component.card; + const char *fe_name = MSM_DAILINK_NAME(LowLatency); + struct snd_soc_pcm_runtime *rtd; + struct snd_pcm_substream *substream; + s32 usecs; + + rtd = snd_soc_get_pcm_runtime(card, fe_name); + if (!rtd) { + pr_err("%s: fail to get pcm runtime for %s\n", + __func__, fe_name); + return -EINVAL; + } + + substream = rtd->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; + if (!substream) { + pr_err("%s: substream is null\n", __func__); + return -EINVAL; + } + + qos_vote_status = ucontrol->value.enumerated.item[0]; + if (qos_vote_status) { + if (pm_qos_request_active(&substream->latency_pm_qos_req)) + pm_qos_remove_request(&substream->latency_pm_qos_req); + if (!substream->runtime) { + pr_err("%s: runtime is null\n", __func__); + return -EINVAL; + } + usecs = MSM_LL_QOS_VALUE; + if (usecs >= 0) + pm_qos_add_request(&substream->latency_pm_qos_req, + PM_QOS_CPU_DMA_LATENCY, usecs); + } else { + if (pm_qos_request_active(&substream->latency_pm_qos_req)) + pm_qos_remove_request(&substream->latency_pm_qos_req); + } + + return 0; +} + +const struct snd_kcontrol_new msm_common_snd_controls[] = { + SOC_ENUM_EXT("PROXY_RX Channels", proxy_rx_chs, + proxy_rx_ch_get, proxy_rx_ch_put), + SOC_ENUM_EXT("PRIM_AUX_PCM_RX SampleRate", prim_aux_pcm_rx_sample_rate, + aux_pcm_rx_sample_rate_get, + aux_pcm_rx_sample_rate_put), + SOC_ENUM_EXT("SEC_AUX_PCM_RX SampleRate", sec_aux_pcm_rx_sample_rate, + aux_pcm_rx_sample_rate_get, + aux_pcm_rx_sample_rate_put), + SOC_ENUM_EXT("TERT_AUX_PCM_RX SampleRate", tert_aux_pcm_rx_sample_rate, + aux_pcm_rx_sample_rate_get, + aux_pcm_rx_sample_rate_put), + SOC_ENUM_EXT("QUAT_AUX_PCM_RX SampleRate", quat_aux_pcm_rx_sample_rate, + aux_pcm_rx_sample_rate_get, + aux_pcm_rx_sample_rate_put), + SOC_ENUM_EXT("QUIN_AUX_PCM_RX SampleRate", quin_aux_pcm_rx_sample_rate, + aux_pcm_rx_sample_rate_get, + aux_pcm_rx_sample_rate_put), + SOC_ENUM_EXT("PRIM_AUX_PCM_TX SampleRate", prim_aux_pcm_tx_sample_rate, + aux_pcm_tx_sample_rate_get, + aux_pcm_tx_sample_rate_put), + SOC_ENUM_EXT("SEC_AUX_PCM_TX SampleRate", sec_aux_pcm_tx_sample_rate, + aux_pcm_tx_sample_rate_get, + aux_pcm_tx_sample_rate_put), + SOC_ENUM_EXT("TERT_AUX_PCM_TX SampleRate", tert_aux_pcm_tx_sample_rate, + aux_pcm_tx_sample_rate_get, + aux_pcm_tx_sample_rate_put), + SOC_ENUM_EXT("QUAT_AUX_PCM_TX SampleRate", quat_aux_pcm_tx_sample_rate, + aux_pcm_tx_sample_rate_get, + aux_pcm_tx_sample_rate_put), + SOC_ENUM_EXT("QUIN_AUX_PCM_TX SampleRate", quin_aux_pcm_tx_sample_rate, + aux_pcm_tx_sample_rate_get, + aux_pcm_tx_sample_rate_put), + SOC_ENUM_EXT("PRIM_MI2S_RX SampleRate", prim_mi2s_rx_sample_rate, + mi2s_rx_sample_rate_get, + mi2s_rx_sample_rate_put), + SOC_ENUM_EXT("SEC_MI2S_RX SampleRate", sec_mi2s_rx_sample_rate, + mi2s_rx_sample_rate_get, + mi2s_rx_sample_rate_put), + SOC_ENUM_EXT("TERT_MI2S_RX SampleRate", tert_mi2s_rx_sample_rate, + mi2s_rx_sample_rate_get, + mi2s_rx_sample_rate_put), + SOC_ENUM_EXT("QUAT_MI2S_RX SampleRate", quat_mi2s_rx_sample_rate, + mi2s_rx_sample_rate_get, + mi2s_rx_sample_rate_put), + SOC_ENUM_EXT("QUIN_MI2S_RX SampleRate", quin_mi2s_rx_sample_rate, + mi2s_rx_sample_rate_get, + mi2s_rx_sample_rate_put), + SOC_ENUM_EXT("PRIM_MI2S_TX SampleRate", prim_mi2s_tx_sample_rate, + mi2s_tx_sample_rate_get, + mi2s_tx_sample_rate_put), + SOC_ENUM_EXT("SEC_MI2S_TX SampleRate", sec_mi2s_tx_sample_rate, + mi2s_tx_sample_rate_get, + mi2s_tx_sample_rate_put), + SOC_ENUM_EXT("TERT_MI2S_TX SampleRate", tert_mi2s_tx_sample_rate, + mi2s_tx_sample_rate_get, + mi2s_tx_sample_rate_put), + SOC_ENUM_EXT("QUAT_MI2S_TX SampleRate", quat_mi2s_tx_sample_rate, + mi2s_tx_sample_rate_get, + mi2s_tx_sample_rate_put), + SOC_ENUM_EXT("QUIN_MI2S_TX SampleRate", quin_mi2s_tx_sample_rate, + mi2s_tx_sample_rate_get, + mi2s_tx_sample_rate_put), + SOC_ENUM_EXT("PRIM_MI2S_RX Format", prim_mi2s_rx_format, + mi2s_rx_format_get, + mi2s_rx_format_put), + SOC_ENUM_EXT("SEC_MI2S_RX Format", sec_mi2s_rx_format, + mi2s_rx_format_get, + mi2s_rx_format_put), + SOC_ENUM_EXT("TERT_MI2S_RX Format", tert_mi2s_rx_format, + mi2s_rx_format_get, + mi2s_rx_format_put), + SOC_ENUM_EXT("QUAT_MI2S_RX Format", quat_mi2s_rx_format, + mi2s_rx_format_get, + mi2s_rx_format_put), + SOC_ENUM_EXT("QUIN_MI2S_RX Format", quin_mi2s_rx_format, + mi2s_rx_format_get, + mi2s_rx_format_put), + SOC_ENUM_EXT("PRIM_MI2S_TX Format", prim_mi2s_tx_format, + mi2s_tx_format_get, + mi2s_tx_format_put), + SOC_ENUM_EXT("SEC_MI2S_TX Format", sec_mi2s_tx_format, + mi2s_tx_format_get, + mi2s_tx_format_put), + SOC_ENUM_EXT("TERT_MI2S_TX Format", tert_mi2s_tx_format, + mi2s_tx_format_get, + mi2s_tx_format_put), + SOC_ENUM_EXT("QUAT_MI2S_TX Format", quat_mi2s_tx_format, + mi2s_tx_format_get, + mi2s_tx_format_put), + SOC_ENUM_EXT("QUIN_MI2S_TX Format", quin_mi2s_tx_format, + mi2s_tx_format_get, + mi2s_tx_format_put), + SOC_ENUM_EXT("PRIM_MI2S_RX Channels", prim_mi2s_rx_chs, + msm_mi2s_rx_ch_get, msm_mi2s_rx_ch_put), + SOC_ENUM_EXT("PRIM_MI2S_TX Channels", prim_mi2s_tx_chs, + msm_mi2s_tx_ch_get, msm_mi2s_tx_ch_put), + SOC_ENUM_EXT("SEC_MI2S_RX Channels", sec_mi2s_rx_chs, + msm_mi2s_rx_ch_get, msm_mi2s_rx_ch_put), + SOC_ENUM_EXT("SEC_MI2S_TX Channels", sec_mi2s_tx_chs, + msm_mi2s_tx_ch_get, msm_mi2s_tx_ch_put), + SOC_ENUM_EXT("TERT_MI2S_RX Channels", tert_mi2s_rx_chs, + msm_mi2s_rx_ch_get, msm_mi2s_rx_ch_put), + SOC_ENUM_EXT("TERT_MI2S_TX Channels", tert_mi2s_tx_chs, + msm_mi2s_tx_ch_get, msm_mi2s_tx_ch_put), + SOC_ENUM_EXT("QUAT_MI2S_RX Channels", quat_mi2s_rx_chs, + msm_mi2s_rx_ch_get, msm_mi2s_rx_ch_put), + SOC_ENUM_EXT("QUAT_MI2S_TX Channels", quat_mi2s_tx_chs, + msm_mi2s_tx_ch_get, msm_mi2s_tx_ch_put), + SOC_ENUM_EXT("QUIN_MI2S_RX Channels", quin_mi2s_rx_chs, + msm_mi2s_rx_ch_get, msm_mi2s_rx_ch_put), + SOC_ENUM_EXT("QUIN_MI2S_TX Channels", quin_mi2s_tx_chs, + msm_mi2s_tx_ch_get, msm_mi2s_tx_ch_put), + SOC_ENUM_EXT("USB_AUDIO_RX Channels", usb_rx_chs, + usb_audio_rx_ch_get, usb_audio_rx_ch_put), + SOC_ENUM_EXT("USB_AUDIO_TX Channels", usb_tx_chs, + usb_audio_tx_ch_get, usb_audio_tx_ch_put), + SOC_ENUM_EXT("Display Port RX Channels", ext_disp_rx_chs, + ext_disp_rx_ch_get, ext_disp_rx_ch_put), + SOC_ENUM_EXT("USB_AUDIO_RX Format", usb_rx_format, + usb_audio_rx_format_get, usb_audio_rx_format_put), + SOC_ENUM_EXT("USB_AUDIO_TX Format", usb_tx_format, + usb_audio_tx_format_get, usb_audio_tx_format_put), + SOC_ENUM_EXT("Display Port RX Bit Format", ext_disp_rx_format, + ext_disp_rx_format_get, ext_disp_rx_format_put), + SOC_ENUM_EXT("USB_AUDIO_RX SampleRate", usb_rx_sample_rate, + usb_audio_rx_sample_rate_get, + usb_audio_rx_sample_rate_put), + SOC_ENUM_EXT("USB_AUDIO_TX SampleRate", usb_tx_sample_rate, + usb_audio_tx_sample_rate_get, + usb_audio_tx_sample_rate_put), + SOC_ENUM_EXT("Display Port RX SampleRate", ext_disp_rx_sample_rate, + ext_disp_rx_sample_rate_get, + ext_disp_rx_sample_rate_put), + SOC_ENUM_EXT("PRI_TDM_RX_0 SampleRate", tdm_rx_sample_rate, + tdm_rx_sample_rate_get, + tdm_rx_sample_rate_put), + SOC_ENUM_EXT("PRI_TDM_TX_0 SampleRate", tdm_tx_sample_rate, + tdm_tx_sample_rate_get, + tdm_tx_sample_rate_put), + SOC_ENUM_EXT("PRI_TDM_RX_0 Format", tdm_rx_format, + tdm_rx_format_get, + tdm_rx_format_put), + SOC_ENUM_EXT("PRI_TDM_TX_0 Format", tdm_tx_format, + tdm_tx_format_get, + tdm_tx_format_put), + SOC_ENUM_EXT("PRI_TDM_RX_0 Channels", tdm_rx_chs, + tdm_rx_ch_get, + tdm_rx_ch_put), + SOC_ENUM_EXT("PRI_TDM_TX_0 Channels", tdm_tx_chs, + tdm_tx_ch_get, + tdm_tx_ch_put), + SOC_ENUM_EXT("SEC_TDM_RX_0 SampleRate", tdm_rx_sample_rate, + tdm_rx_sample_rate_get, + tdm_rx_sample_rate_put), + SOC_ENUM_EXT("SEC_TDM_TX_0 SampleRate", tdm_tx_sample_rate, + tdm_tx_sample_rate_get, + tdm_tx_sample_rate_put), + SOC_ENUM_EXT("SEC_TDM_RX_0 Format", tdm_rx_format, + tdm_rx_format_get, + tdm_rx_format_put), + SOC_ENUM_EXT("SEC_TDM_TX_0 Format", tdm_tx_format, + tdm_tx_format_get, + tdm_tx_format_put), + SOC_ENUM_EXT("SEC_TDM_RX_0 Channels", tdm_rx_chs, + tdm_rx_ch_get, + tdm_rx_ch_put), + SOC_ENUM_EXT("SEC_TDM_TX_0 Channels", tdm_tx_chs, + tdm_tx_ch_get, + tdm_tx_ch_put), + SOC_ENUM_EXT("TERT_TDM_RX_0 SampleRate", tdm_rx_sample_rate, + tdm_rx_sample_rate_get, + tdm_rx_sample_rate_put), + SOC_ENUM_EXT("TERT_TDM_TX_0 SampleRate", tdm_tx_sample_rate, + tdm_tx_sample_rate_get, + tdm_tx_sample_rate_put), + SOC_ENUM_EXT("TERT_TDM_RX_0 Format", tdm_rx_format, + tdm_rx_format_get, + tdm_rx_format_put), + SOC_ENUM_EXT("TERT_TDM_TX_0 Format", tdm_tx_format, + tdm_tx_format_get, + tdm_tx_format_put), + SOC_ENUM_EXT("TERT_TDM_RX_0 Channels", tdm_rx_chs, + tdm_rx_ch_get, + tdm_rx_ch_put), + SOC_ENUM_EXT("TERT_TDM_TX_0 Channels", tdm_tx_chs, + tdm_tx_ch_get, + tdm_tx_ch_put), + SOC_ENUM_EXT("QUAT_TDM_RX_0 SampleRate", tdm_rx_sample_rate, + tdm_rx_sample_rate_get, + tdm_rx_sample_rate_put), + SOC_ENUM_EXT("QUAT_TDM_TX_0 SampleRate", tdm_tx_sample_rate, + tdm_tx_sample_rate_get, + tdm_tx_sample_rate_put), + SOC_ENUM_EXT("QUAT_TDM_RX_0 Format", tdm_rx_format, + tdm_rx_format_get, + tdm_rx_format_put), + SOC_ENUM_EXT("QUAT_TDM_TX_0 Format", tdm_tx_format, + tdm_tx_format_get, + tdm_tx_format_put), + SOC_ENUM_EXT("QUAT_TDM_RX_0 Channels", tdm_rx_chs, + tdm_rx_ch_get, + tdm_rx_ch_put), + SOC_ENUM_EXT("QUAT_TDM_TX_0 Channels", tdm_tx_chs, + tdm_tx_ch_get, + tdm_tx_ch_put), + SOC_ENUM_EXT("QUIN_TDM_RX_0 SampleRate", tdm_rx_sample_rate, + tdm_rx_sample_rate_get, + tdm_rx_sample_rate_put), + SOC_ENUM_EXT("QUIN_TDM_TX_0 SampleRate", tdm_tx_sample_rate, + tdm_tx_sample_rate_get, + tdm_tx_sample_rate_put), + SOC_ENUM_EXT("QUIN_TDM_RX_0 Format", tdm_rx_format, + tdm_rx_format_get, + tdm_rx_format_put), + SOC_ENUM_EXT("QUIN_TDM_TX_0 Format", tdm_tx_format, + tdm_tx_format_get, + tdm_tx_format_put), + SOC_ENUM_EXT("QUIN_TDM_RX_0 Channels", tdm_rx_chs, + tdm_rx_ch_get, + tdm_rx_ch_put), + SOC_ENUM_EXT("QUIN_TDM_TX_0 Channels", tdm_tx_chs, + tdm_tx_ch_get, + tdm_tx_ch_put), + SOC_ENUM_EXT("MultiMedia5_RX QOS Vote", qos_vote, msm_qos_ctl_get, + msm_qos_ctl_put), +}; + +/** + * msm_common_snd_controls_size - to return controls size + * + * Return: returns size of common controls array + */ +int msm_common_snd_controls_size(void) +{ + return ARRAY_SIZE(msm_common_snd_controls); +} +EXPORT_SYMBOL(msm_common_snd_controls_size); + +void msm_set_codec_reg_done(bool done) +{ + codec_reg_done = done; +} +EXPORT_SYMBOL(msm_set_codec_reg_done); + +static inline int param_is_mask(int p) +{ + return (p >= SNDRV_PCM_HW_PARAM_FIRST_MASK) && + (p <= SNDRV_PCM_HW_PARAM_LAST_MASK); +} + +static inline struct snd_mask *param_to_mask(struct snd_pcm_hw_params *p, + int n) +{ + return &(p->masks[n - SNDRV_PCM_HW_PARAM_FIRST_MASK]); +} + +static void param_set_mask(struct snd_pcm_hw_params *p, int n, unsigned int bit) +{ + if (bit >= SNDRV_MASK_MAX) + return; + if (param_is_mask(n)) { + struct snd_mask *m = param_to_mask(p, n); + + m->bits[0] = 0; + m->bits[1] = 0; + m->bits[bit >> 5] |= (1 << (bit & 31)); + } +} + +static int msm_ext_disp_get_idx_from_beid(int32_t id) +{ + int idx; + + switch (id) { + case MSM_BACKEND_DAI_DISPLAY_PORT_RX: + idx = DP_RX_IDX; + break; + default: + pr_err("%s: Incorrect ext_disp id %d\n", __func__, id); + idx = -EINVAL; + break; + } + + return idx; +} + +/** + * msm_common_be_hw_params_fixup - updates settings of ALSA BE hw params. + * + * @rtd: runtime dailink instance + * @params: HW params of associated backend dailink. + * + * Returns 0. + */ +int msm_common_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_dai_link *dai_link = rtd->dai_link; + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + int rc = 0; + int idx; + + pr_debug("%s: format = %d, rate = %d\n", + __func__, params_format(params), params_rate(params)); + + switch (dai_link->id) { + case MSM_BACKEND_DAI_USB_RX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + usb_rx_cfg.bit_format); + rate->min = rate->max = usb_rx_cfg.sample_rate; + channels->min = channels->max = usb_rx_cfg.channels; + break; + + case MSM_BACKEND_DAI_USB_TX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + usb_tx_cfg.bit_format); + rate->min = rate->max = usb_tx_cfg.sample_rate; + channels->min = channels->max = usb_tx_cfg.channels; + break; + + case MSM_BACKEND_DAI_DISPLAY_PORT_RX: + idx = msm_ext_disp_get_idx_from_beid(dai_link->id); + if (idx < 0) { + pr_err("%s: Incorrect ext disp idx %d\n", + __func__, idx); + rc = idx; + break; + } + + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + ext_disp_rx_cfg[idx].bit_format); + rate->min = rate->max = ext_disp_rx_cfg[idx].sample_rate; + channels->min = channels->max = ext_disp_rx_cfg[idx].channels; + break; + + case MSM_BACKEND_DAI_AFE_PCM_RX: + channels->min = channels->max = proxy_rx_cfg.channels; + rate->min = rate->max = SAMPLING_RATE_48KHZ; + break; + + case MSM_BACKEND_DAI_PRI_TDM_RX_0: + channels->min = channels->max = + tdm_rx_cfg[TDM_PRI][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_PRI][TDM_0].bit_format); + rate->min = rate->max = tdm_rx_cfg[TDM_PRI][TDM_0].sample_rate; + break; + + case MSM_BACKEND_DAI_PRI_TDM_TX_0: + channels->min = channels->max = + tdm_tx_cfg[TDM_PRI][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_tx_cfg[TDM_PRI][TDM_0].bit_format); + rate->min = rate->max = tdm_tx_cfg[TDM_PRI][TDM_0].sample_rate; + break; + + case MSM_BACKEND_DAI_SEC_TDM_RX_0: + channels->min = channels->max = + tdm_rx_cfg[TDM_SEC][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_SEC][TDM_0].bit_format); + rate->min = rate->max = tdm_rx_cfg[TDM_SEC][TDM_0].sample_rate; + break; + + case MSM_BACKEND_DAI_SEC_TDM_TX_0: + channels->min = channels->max = + tdm_tx_cfg[TDM_SEC][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_tx_cfg[TDM_SEC][TDM_0].bit_format); + rate->min = rate->max = tdm_tx_cfg[TDM_SEC][TDM_0].sample_rate; + break; + + case MSM_BACKEND_DAI_TERT_TDM_RX_0: + channels->min = channels->max = + tdm_rx_cfg[TDM_TERT][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_TERT][TDM_0].bit_format); + rate->min = rate->max = tdm_rx_cfg[TDM_TERT][TDM_0].sample_rate; + break; + + case MSM_BACKEND_DAI_TERT_TDM_TX_0: + channels->min = channels->max = + tdm_tx_cfg[TDM_TERT][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_tx_cfg[TDM_TERT][TDM_0].bit_format); + rate->min = rate->max = tdm_tx_cfg[TDM_TERT][TDM_0].sample_rate; + break; + + case MSM_BACKEND_DAI_QUAT_TDM_RX_0: + channels->min = channels->max = + tdm_rx_cfg[TDM_QUAT][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_QUAT][TDM_0].bit_format); + rate->min = rate->max = tdm_rx_cfg[TDM_QUAT][TDM_0].sample_rate; + break; + + case MSM_BACKEND_DAI_QUAT_TDM_TX_0: + channels->min = channels->max = + tdm_tx_cfg[TDM_QUAT][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_tx_cfg[TDM_QUAT][TDM_0].bit_format); + rate->min = rate->max = tdm_tx_cfg[TDM_QUAT][TDM_0].sample_rate; + break; + + case MSM_BACKEND_DAI_QUIN_TDM_RX_0: + channels->min = channels->max = + tdm_rx_cfg[TDM_QUIN][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_QUIN][TDM_0].bit_format); + rate->min = rate->max = tdm_rx_cfg[TDM_QUIN][TDM_0].sample_rate; + break; + + case MSM_BACKEND_DAI_QUIN_TDM_TX_0: + channels->min = channels->max = + tdm_tx_cfg[TDM_QUIN][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_tx_cfg[TDM_QUIN][TDM_0].bit_format); + rate->min = rate->max = tdm_tx_cfg[TDM_QUIN][TDM_0].sample_rate; + break; + + case MSM_BACKEND_DAI_AUXPCM_RX: + rate->min = rate->max = + aux_pcm_rx_cfg[PRIM_AUX_PCM].sample_rate; + channels->min = channels->max = + aux_pcm_rx_cfg[PRIM_AUX_PCM].channels; + break; + + case MSM_BACKEND_DAI_AUXPCM_TX: + rate->min = rate->max = + aux_pcm_tx_cfg[PRIM_AUX_PCM].sample_rate; + channels->min = channels->max = + aux_pcm_tx_cfg[PRIM_AUX_PCM].channels; + break; + + case MSM_BACKEND_DAI_SEC_AUXPCM_RX: + rate->min = rate->max = + aux_pcm_rx_cfg[SEC_AUX_PCM].sample_rate; + channels->min = channels->max = + aux_pcm_rx_cfg[SEC_AUX_PCM].channels; + break; + + case MSM_BACKEND_DAI_SEC_AUXPCM_TX: + rate->min = rate->max = + aux_pcm_tx_cfg[SEC_AUX_PCM].sample_rate; + channels->min = channels->max = + aux_pcm_tx_cfg[SEC_AUX_PCM].channels; + break; + + case MSM_BACKEND_DAI_TERT_AUXPCM_RX: + rate->min = rate->max = + aux_pcm_rx_cfg[TERT_AUX_PCM].sample_rate; + channels->min = channels->max = + aux_pcm_rx_cfg[TERT_AUX_PCM].channels; + break; + + case MSM_BACKEND_DAI_TERT_AUXPCM_TX: + rate->min = rate->max = + aux_pcm_tx_cfg[TERT_AUX_PCM].sample_rate; + channels->min = channels->max = + aux_pcm_tx_cfg[TERT_AUX_PCM].channels; + break; + + case MSM_BACKEND_DAI_QUAT_AUXPCM_RX: + rate->min = rate->max = + aux_pcm_rx_cfg[QUAT_AUX_PCM].sample_rate; + channels->min = channels->max = + aux_pcm_rx_cfg[QUAT_AUX_PCM].channels; + break; + + case MSM_BACKEND_DAI_QUAT_AUXPCM_TX: + rate->min = rate->max = + aux_pcm_tx_cfg[QUAT_AUX_PCM].sample_rate; + channels->min = channels->max = + aux_pcm_tx_cfg[QUAT_AUX_PCM].channels; + break; + + case MSM_BACKEND_DAI_QUIN_AUXPCM_RX: + rate->min = rate->max = + aux_pcm_rx_cfg[QUIN_AUX_PCM].sample_rate; + channels->min = channels->max = + aux_pcm_rx_cfg[QUIN_AUX_PCM].channels; + break; + + case MSM_BACKEND_DAI_QUIN_AUXPCM_TX: + rate->min = rate->max = + aux_pcm_tx_cfg[QUIN_AUX_PCM].sample_rate; + channels->min = channels->max = + aux_pcm_tx_cfg[QUIN_AUX_PCM].channels; + break; + + case MSM_BACKEND_DAI_PRI_MI2S_RX: + rate->min = rate->max = mi2s_rx_cfg[PRIM_MI2S].sample_rate; + channels->min = channels->max = + mi2s_rx_cfg[PRIM_MI2S].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + mi2s_rx_cfg[PRIM_MI2S].bit_format); + break; + + case MSM_BACKEND_DAI_PRI_MI2S_TX: + rate->min = rate->max = mi2s_tx_cfg[PRIM_MI2S].sample_rate; + channels->min = channels->max = + mi2s_tx_cfg[PRIM_MI2S].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + mi2s_tx_cfg[PRIM_MI2S].bit_format); + break; + + case MSM_BACKEND_DAI_SECONDARY_MI2S_RX: + rate->min = rate->max = mi2s_rx_cfg[SEC_MI2S].sample_rate; + channels->min = channels->max = + mi2s_rx_cfg[SEC_MI2S].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + mi2s_rx_cfg[SEC_MI2S].bit_format); + break; + + case MSM_BACKEND_DAI_SECONDARY_MI2S_TX: + rate->min = rate->max = mi2s_tx_cfg[SEC_MI2S].sample_rate; + channels->min = channels->max = + mi2s_tx_cfg[SEC_MI2S].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + mi2s_tx_cfg[SEC_MI2S].bit_format); + break; + + case MSM_BACKEND_DAI_TERTIARY_MI2S_RX: + rate->min = rate->max = mi2s_rx_cfg[TERT_MI2S].sample_rate; + channels->min = channels->max = + mi2s_rx_cfg[TERT_MI2S].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + mi2s_rx_cfg[TERT_MI2S].bit_format); + break; + + case MSM_BACKEND_DAI_TERTIARY_MI2S_TX: + rate->min = rate->max = mi2s_tx_cfg[TERT_MI2S].sample_rate; + channels->min = channels->max = + mi2s_tx_cfg[TERT_MI2S].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + mi2s_tx_cfg[TERT_MI2S].bit_format); + break; + + case MSM_BACKEND_DAI_QUATERNARY_MI2S_RX: + rate->min = rate->max = mi2s_rx_cfg[QUAT_MI2S].sample_rate; + channels->min = channels->max = + mi2s_rx_cfg[QUAT_MI2S].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + mi2s_rx_cfg[QUAT_MI2S].bit_format); + break; + + case MSM_BACKEND_DAI_QUATERNARY_MI2S_TX: + rate->min = rate->max = mi2s_tx_cfg[QUAT_MI2S].sample_rate; + channels->min = channels->max = + mi2s_tx_cfg[QUAT_MI2S].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + mi2s_tx_cfg[QUAT_MI2S].bit_format); + break; + + case MSM_BACKEND_DAI_QUINARY_MI2S_RX: + rate->min = rate->max = mi2s_rx_cfg[QUIN_MI2S].sample_rate; + channels->min = channels->max = + mi2s_rx_cfg[QUIN_MI2S].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + mi2s_rx_cfg[QUIN_MI2S].bit_format); + break; + + case MSM_BACKEND_DAI_QUINARY_MI2S_TX: + rate->min = rate->max = mi2s_tx_cfg[QUIN_MI2S].sample_rate; + channels->min = channels->max = + mi2s_tx_cfg[QUIN_MI2S].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + mi2s_tx_cfg[QUIN_MI2S].bit_format); + break; + + default: + rate->min = rate->max = SAMPLING_RATE_48KHZ; + break; + } + return rc; +} +EXPORT_SYMBOL(msm_common_be_hw_params_fixup); + +/** + * msm_aux_pcm_snd_startup - startup ops of auxpcm. + * + * @substream: PCM stream pointer of associated backend dailink + * + * Returns 0 on success or -EINVAL on error. + */ +int msm_aux_pcm_snd_startup(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + + dev_dbg(rtd->card->dev, + "%s: substream = %s stream = %d, dai name %s, dai ID %d\n", + __func__, substream->name, substream->stream, + rtd->cpu_dai->name, rtd->cpu_dai->id); + + return 0; +} +EXPORT_SYMBOL(msm_aux_pcm_snd_startup); + +/** + * msm_aux_pcm_snd_shutdown - shutdown ops of auxpcm. + * + * @substream: PCM stream pointer of associated backend dailink + */ +void msm_aux_pcm_snd_shutdown(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + + dev_dbg(rtd->card->dev, + "%s: substream = %s stream = %d, dai name %s, dai ID %d\n", + __func__, + substream->name, substream->stream, + rtd->cpu_dai->name, rtd->cpu_dai->id); +} +EXPORT_SYMBOL(msm_aux_pcm_snd_shutdown); + +static int msm_get_port_id(int id) +{ + int afe_port_id; + + switch (id) { + case MSM_BACKEND_DAI_PRI_MI2S_RX: + afe_port_id = AFE_PORT_ID_PRIMARY_MI2S_RX; + break; + case MSM_BACKEND_DAI_PRI_MI2S_TX: + afe_port_id = AFE_PORT_ID_PRIMARY_MI2S_TX; + break; + case MSM_BACKEND_DAI_SECONDARY_MI2S_RX: + afe_port_id = AFE_PORT_ID_SECONDARY_MI2S_RX; + break; + case MSM_BACKEND_DAI_SECONDARY_MI2S_TX: + afe_port_id = AFE_PORT_ID_SECONDARY_MI2S_TX; + break; + case MSM_BACKEND_DAI_TERTIARY_MI2S_RX: + afe_port_id = AFE_PORT_ID_TERTIARY_MI2S_RX; + break; + case MSM_BACKEND_DAI_TERTIARY_MI2S_TX: + afe_port_id = AFE_PORT_ID_TERTIARY_MI2S_TX; + break; + case MSM_BACKEND_DAI_QUATERNARY_MI2S_RX: + afe_port_id = AFE_PORT_ID_QUATERNARY_MI2S_RX; + break; + case MSM_BACKEND_DAI_QUATERNARY_MI2S_TX: + afe_port_id = AFE_PORT_ID_QUATERNARY_MI2S_TX; + break; + case MSM_BACKEND_DAI_QUINARY_MI2S_RX: + afe_port_id = AFE_PORT_ID_QUINARY_MI2S_RX; + break; + case MSM_BACKEND_DAI_QUINARY_MI2S_TX: + afe_port_id = AFE_PORT_ID_QUINARY_MI2S_TX; + break; + default: + pr_err("%s: Invalid id: %d\n", __func__, id); + afe_port_id = -EINVAL; + } + + return afe_port_id; +} + +static u32 get_mi2s_bits_per_sample(u32 bit_format) +{ + u32 bit_per_sample; + + switch (bit_format) { + case SNDRV_PCM_FORMAT_S32_LE: + case SNDRV_PCM_FORMAT_S24_3LE: + case SNDRV_PCM_FORMAT_S24_LE: + bit_per_sample = 32; + break; + case SNDRV_PCM_FORMAT_S16_LE: + default: + bit_per_sample = 16; + break; + } + + return bit_per_sample; +} + +static void update_mi2s_clk_val(int dai_id, int stream) +{ + u32 bit_per_sample; + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + bit_per_sample = + get_mi2s_bits_per_sample(mi2s_rx_cfg[dai_id].bit_format); + mi2s_clk[dai_id].clk_freq_in_hz = + mi2s_rx_cfg[dai_id].sample_rate * 2 * bit_per_sample; + } else { + bit_per_sample = + get_mi2s_bits_per_sample(mi2s_tx_cfg[dai_id].bit_format); + mi2s_clk[dai_id].clk_freq_in_hz = + mi2s_tx_cfg[dai_id].sample_rate * 2 * bit_per_sample; + } +} + +static int msm_mi2s_set_sclk(struct snd_pcm_substream *substream, bool enable) +{ + int ret = 0; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + int port_id = 0; + int index = cpu_dai->id; + + port_id = msm_get_port_id(rtd->dai_link->id); + if (port_id < 0) { + dev_err(rtd->card->dev, "%s: Invalid port_id\n", __func__); + ret = port_id; + goto done; + } + + if (enable) { + update_mi2s_clk_val(index, substream->stream); + dev_dbg(rtd->card->dev, "%s: clock rate %ul\n", __func__, + mi2s_clk[index].clk_freq_in_hz); + } + + mi2s_clk[index].enable = enable; + ret = afe_set_lpass_clock_v2(port_id, + &mi2s_clk[index]); + if (ret < 0) { + dev_err(rtd->card->dev, + "%s: afe lpass clock failed for port 0x%x , err:%d\n", + __func__, port_id, ret); + goto done; + } + +done: + return ret; +} + +/** + * msm_mi2s_snd_startup - startup ops of mi2s. + * + * @substream: PCM stream pointer of associated backend dailink + * + * Returns 0 on success or -EINVAL on error. + */ +int msm_mi2s_snd_startup(struct snd_pcm_substream *substream) +{ + int ret = 0; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + int port_id = msm_get_port_id(rtd->dai_link->id); + int index = cpu_dai->id; + unsigned int fmt = SND_SOC_DAIFMT_CBS_CFS; + struct msm_asoc_mach_data *pdata = + snd_soc_card_get_drvdata(rtd->card); + + dev_dbg(rtd->card->dev, + "%s: substream = %s stream = %d, dai name %s, dai ID %d\n", + __func__, substream->name, substream->stream, + cpu_dai->name, cpu_dai->id); + + if (index < PRIM_MI2S || index >= MI2S_MAX) { + ret = -EINVAL; + dev_err(rtd->card->dev, + "%s: CPU DAI id (%d) out of range\n", + __func__, cpu_dai->id); + goto done; + } + /* + * Muxtex protection in case the same MI2S + * interface using for both TX and RX so + * that the same clock won't be enable twice. + */ + mutex_lock(&mi2s_intf_conf[index].lock); + if (++mi2s_intf_conf[index].ref_cnt == 1) { + /* Check if msm needs to provide the clock to the interface */ + if (!mi2s_intf_conf[index].msm_is_mi2s_master) { + mi2s_clk[index].clk_id = mi2s_ebit_clk[index]; + fmt = SND_SOC_DAIFMT_CBM_CFM; + } + ret = msm_mi2s_set_sclk(substream, true); + if (ret < 0) { + dev_err(rtd->card->dev, + "%s: afe lpass clock failed to enable MI2S clock, err:%d\n", + __func__, ret); + goto clean_up; + } + ret = snd_soc_dai_set_fmt(cpu_dai, fmt); + if (ret < 0) { + dev_err(rtd->card->dev, + "%s: set fmt cpu dai failed for MI2S (%d), err:%d\n", + __func__, index, ret); + goto clk_off; + } + if (mi2s_intf_conf[index].msm_is_ext_mclk) { + mi2s_mclk[index].enable = 1; + pr_debug("%s: Enabling mclk, clk_freq_in_hz = %u\n", + __func__, mi2s_mclk[index].clk_freq_in_hz); + ret = afe_set_lpass_clock_v2(port_id, + &mi2s_mclk[index]); + if (ret < 0) { + pr_err("%s: afe lpass mclk failed, err:%d\n", + __func__, ret); + goto clk_off; + } + } + if (pdata->mi2s_gpio_p[index]) + msm_cdc_pinctrl_select_active_state( + pdata->mi2s_gpio_p[index]); + } + mutex_unlock(&mi2s_intf_conf[index].lock); + return 0; +clk_off: + if (ret < 0) + msm_mi2s_set_sclk(substream, false); +clean_up: + if (ret < 0) + mi2s_intf_conf[index].ref_cnt--; + mutex_unlock(&mi2s_intf_conf[index].lock); +done: + return ret; +} +EXPORT_SYMBOL(msm_mi2s_snd_startup); + +/** + * msm_mi2s_snd_shutdown - shutdown ops of mi2s. + * + * @substream: PCM stream pointer of associated backend dailink + */ +void msm_mi2s_snd_shutdown(struct snd_pcm_substream *substream) +{ + int ret; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + int port_id = msm_get_port_id(rtd->dai_link->id); + int index = rtd->cpu_dai->id; + struct msm_asoc_mach_data *pdata = + snd_soc_card_get_drvdata(rtd->card); + + pr_debug("%s(): substream = %s stream = %d\n", __func__, + substream->name, substream->stream); + if (index < PRIM_MI2S || index >= MI2S_MAX) { + pr_err("%s:invalid MI2S DAI(%d)\n", __func__, index); + return; + } + + mutex_lock(&mi2s_intf_conf[index].lock); + if (--mi2s_intf_conf[index].ref_cnt == 0) { + if (pdata->mi2s_gpio_p[index]) + msm_cdc_pinctrl_select_sleep_state( + pdata->mi2s_gpio_p[index]); + + ret = msm_mi2s_set_sclk(substream, false); + if (ret < 0) + pr_err("%s:clock disable failed for MI2S (%d); ret=%d\n", + __func__, index, ret); + + if (mi2s_intf_conf[index].msm_is_ext_mclk) { + mi2s_mclk[index].enable = 0; + pr_debug("%s: Disabling mclk, clk_freq_in_hz = %u\n", + __func__, mi2s_mclk[index].clk_freq_in_hz); + ret = afe_set_lpass_clock_v2(port_id, + &mi2s_mclk[index]); + if (ret < 0) { + pr_err("%s: mclk disable failed for MCLK (%d); ret=%d\n", + __func__, index, ret); + } + } + } + mutex_unlock(&mi2s_intf_conf[index].lock); +} +EXPORT_SYMBOL(msm_mi2s_snd_shutdown); + +/* Validate whether US EU switch is present or not */ +static int msm_prepare_us_euro(struct snd_soc_card *card) +{ + struct msm_asoc_mach_data *pdata = + snd_soc_card_get_drvdata(card); + int ret = 0; + + if (pdata->us_euro_gpio >= 0) { + dev_dbg(card->dev, "%s: us_euro gpio request %d", __func__, + pdata->us_euro_gpio); + ret = gpio_request(pdata->us_euro_gpio, "TASHA_CODEC_US_EURO"); + if (ret) { + dev_err(card->dev, + "%s: Failed to request codec US/EURO gpio %d error %d\n", + __func__, pdata->us_euro_gpio, ret); + } + } + + return ret; +} + + +static bool msm_usbc_swap_gnd_mic(struct snd_soc_codec *codec, bool active) +{ + int value = 0; + bool ret = false; + struct snd_soc_card *card = codec->component.card; + struct msm_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card); + struct pinctrl_state *en2_pinctrl_active; + struct pinctrl_state *en2_pinctrl_sleep; + + if (!pdata->usbc_en2_gpio_p) { + if (active) { + /* if active and usbc_en2_gpio undefined, get pin */ + pdata->usbc_en2_gpio_p = devm_pinctrl_get(card->dev); + if (IS_ERR_OR_NULL(pdata->usbc_en2_gpio_p)) { + dev_err(card->dev, + "%s: Can't get EN2 gpio pinctrl:%ld\n", + __func__, + PTR_ERR(pdata->usbc_en2_gpio_p)); + pdata->usbc_en2_gpio_p = NULL; + return false; + } + } else { + /* if not active and usbc_en2_gpio undefined, return */ + return false; + } + } + + pdata->usbc_en2_gpio = of_get_named_gpio(card->dev->of_node, + "qcom,usbc-analog-en2-gpio", 0); + if (!gpio_is_valid(pdata->usbc_en2_gpio)) { + dev_err(card->dev, "%s, property %s not in node %s\n", + __func__, "qcom,usbc-analog-en2-gpio", + card->dev->of_node->full_name); + return false; + } + + en2_pinctrl_active = pinctrl_lookup_state( + pdata->usbc_en2_gpio_p, "aud_active"); + if (IS_ERR_OR_NULL(en2_pinctrl_active)) { + dev_err(card->dev, + "%s: Cannot get aud_active pinctrl state:%ld\n", + __func__, PTR_ERR(en2_pinctrl_active)); + ret = false; + goto err_lookup_state; + } + + en2_pinctrl_sleep = pinctrl_lookup_state( + pdata->usbc_en2_gpio_p, "aud_sleep"); + if (IS_ERR_OR_NULL(en2_pinctrl_sleep)) { + dev_err(card->dev, + "%s: Cannot get aud_sleep pinctrl state:%ld\n", + __func__, PTR_ERR(en2_pinctrl_sleep)); + ret = false; + goto err_lookup_state; + } + + /* if active and usbc_en2_gpio_p defined, swap using usbc_en2_gpio_p */ + if (active) { + dev_dbg(codec->dev, "%s: enter\n", __func__); + if (pdata->usbc_en2_gpio_p) { + value = gpio_get_value_cansleep(pdata->usbc_en2_gpio); + if (value) + pinctrl_select_state(pdata->usbc_en2_gpio_p, + en2_pinctrl_sleep); + else + pinctrl_select_state(pdata->usbc_en2_gpio_p, + en2_pinctrl_active); + } else if (pdata->usbc_en2_gpio >= 0) { + value = gpio_get_value_cansleep(pdata->usbc_en2_gpio); + gpio_set_value_cansleep(pdata->usbc_en2_gpio, !value); + } + pr_debug("%s: swap select switch %d to %d\n", __func__, + value, !value); + ret = true; + } else { + /* if not active, release usbc_en2_gpio_p pin */ + pinctrl_select_state(pdata->usbc_en2_gpio_p, + en2_pinctrl_sleep); + } + +err_lookup_state: + devm_pinctrl_put(pdata->usbc_en2_gpio_p); + pdata->usbc_en2_gpio_p = NULL; + return ret; +} + +static bool msm_swap_gnd_mic(struct snd_soc_codec *codec, bool active) +{ + struct snd_soc_card *card = codec->component.card; + struct msm_asoc_mach_data *pdata = + snd_soc_card_get_drvdata(card); + int value = 0; + bool ret = 0; + + if (!mbhc_cfg.enable_usbc_analog) { + if (pdata->us_euro_gpio_p) { + value = msm_cdc_pinctrl_get_state( + pdata->us_euro_gpio_p); + if (value) + msm_cdc_pinctrl_select_sleep_state( + pdata->us_euro_gpio_p); + else + msm_cdc_pinctrl_select_active_state( + pdata->us_euro_gpio_p); + } else if (pdata->us_euro_gpio >= 0) { + value = gpio_get_value_cansleep(pdata->us_euro_gpio); + gpio_set_value_cansleep(pdata->us_euro_gpio, !value); + } + pr_debug("%s: swap select switch %d to %d\n", + __func__, value, !value); + ret = true; + } else { + /* if usbc is defined, swap using usbc_en2 */ + ret = msm_usbc_swap_gnd_mic(codec, active); + } + return ret; +} + +static int msm_populate_dai_link_component_of_node( + struct msm_asoc_mach_data *pdata, + struct snd_soc_card *card) +{ + int i, index, ret = 0; + struct device *cdev = card->dev; + struct snd_soc_dai_link *dai_link = card->dai_link; + struct device_node *phandle; + + if (!cdev) { + pr_err("%s: Sound card device memory NULL\n", __func__); + return -ENODEV; + } + + for (i = 0; i < card->num_links; i++) { + if (dai_link[i].platform_of_node && dai_link[i].cpu_of_node) + continue; + + /* populate platform_of_node for snd card dai links */ + if (dai_link[i].platform_name && + !dai_link[i].platform_of_node) { + index = of_property_match_string(cdev->of_node, + "asoc-platform-names", + dai_link[i].platform_name); + if (index < 0) { + pr_err("%s: No match found for platform name: %s\n", + __func__, dai_link[i].platform_name); + ret = index; + goto cpu_dai; + } + phandle = of_parse_phandle(cdev->of_node, + "asoc-platform", + index); + if (!phandle) { + pr_err("%s: retrieving phandle for platform %s, index %d failed\n", + __func__, dai_link[i].platform_name, + index); + ret = -ENODEV; + goto err; + } + dai_link[i].platform_of_node = phandle; + dai_link[i].platform_name = NULL; + } +cpu_dai: + /* populate cpu_of_node for snd card dai links */ + if (dai_link[i].cpu_dai_name && !dai_link[i].cpu_of_node) { + index = of_property_match_string(cdev->of_node, + "asoc-cpu-names", + dai_link[i].cpu_dai_name); + if (index < 0) + goto codec_dai; + phandle = of_parse_phandle(cdev->of_node, "asoc-cpu", + index); + if (!phandle) { + pr_err("%s: retrieving phandle for cpu dai %s failed\n", + __func__, dai_link[i].cpu_dai_name); + ret = -ENODEV; + goto err; + } + dai_link[i].cpu_of_node = phandle; + dai_link[i].cpu_dai_name = NULL; + } +codec_dai: + /* populate codec_of_node for snd card dai links */ + if (dai_link[i].codec_name && !dai_link[i].codec_of_node) { + index = of_property_match_string(cdev->of_node, + "asoc-codec-names", + dai_link[i].codec_name); + if (index < 0) + continue; + phandle = of_parse_phandle(cdev->of_node, "asoc-codec", + index); + if (!phandle) { + pr_err("%s: retrieving phandle for codec dai %s failed\n", + __func__, dai_link[i].codec_name); + ret = -ENODEV; + goto err; + } + dai_link[i].codec_of_node = phandle; + dai_link[i].codec_name = NULL; + } + if (pdata->snd_card_val == INT_SND_CARD) { + if ((dai_link[i].id == + MSM_BACKEND_DAI_INT0_MI2S_RX) || + (dai_link[i].id == + MSM_BACKEND_DAI_INT1_MI2S_RX) || + (dai_link[i].id == + MSM_BACKEND_DAI_INT2_MI2S_TX) || + (dai_link[i].id == + MSM_BACKEND_DAI_INT3_MI2S_TX)) { + index = of_property_match_string(cdev->of_node, + "asoc-codec-names", + MSM_INT_DIGITAL_CODEC); + phandle = of_parse_phandle(cdev->of_node, + "asoc-codec", + index); + dai_link[i].codecs[DIG_CDC].of_node = phandle; + index = of_property_match_string(cdev->of_node, + "asoc-codec-names", + PMIC_INT_ANALOG_CODEC); + phandle = of_parse_phandle(cdev->of_node, + "asoc-codec", + index); + dai_link[i].codecs[ANA_CDC].of_node = phandle; + } + } + } +err: + return ret; +} + +static int msm_wsa881x_init(struct snd_soc_component *component) +{ + u8 spkleft_ports[WSA881X_MAX_SWR_PORTS] = {100, 101, 102, 106}; + u8 spkright_ports[WSA881X_MAX_SWR_PORTS] = {103, 104, 105, 107}; + unsigned int ch_rate[WSA881X_MAX_SWR_PORTS] = {2400, 600, 300, 1200}; + unsigned int ch_mask[WSA881X_MAX_SWR_PORTS] = {0x1, 0xF, 0x3, 0x3}; + struct snd_soc_codec *codec = snd_soc_component_to_codec(component); + struct msm_asoc_mach_data *pdata; + struct snd_soc_dapm_context *dapm = + snd_soc_codec_get_dapm(codec); + + if (!codec) { + pr_err("%s codec is NULL\n", __func__); + return -EINVAL; + } + + if (!strcmp(component->name_prefix, "SpkrLeft")) { + dev_dbg(codec->dev, "%s: setting left ch map to codec %s\n", + __func__, codec->component.name); + wsa881x_set_channel_map(codec, &spkleft_ports[0], + WSA881X_MAX_SWR_PORTS, &ch_mask[0], + &ch_rate[0], NULL); + if (dapm->component) { + snd_soc_dapm_ignore_suspend(dapm, "SpkrLeft IN"); + snd_soc_dapm_ignore_suspend(dapm, "SpkrLeft SPKR"); + } + } else if (!strcmp(component->name_prefix, "SpkrRight")) { + dev_dbg(codec->dev, "%s: setting right ch map to codec %s\n", + __func__, codec->component.name); + wsa881x_set_channel_map(codec, &spkright_ports[0], + WSA881X_MAX_SWR_PORTS, &ch_mask[0], + &ch_rate[0], NULL); + if (dapm->component) { + snd_soc_dapm_ignore_suspend(dapm, "SpkrRight IN"); + snd_soc_dapm_ignore_suspend(dapm, "SpkrRight SPKR"); + } + } else { + dev_err(codec->dev, "%s: wrong codec name %s\n", __func__, + codec->component.name); + return -EINVAL; + } + + + pdata = snd_soc_card_get_drvdata(component->card); + if (pdata && pdata->codec_root) + wsa881x_codec_info_create_codec_entry(pdata->codec_root, + codec); + return 0; +} + + +static int msm_init_wsa_dev(struct platform_device *pdev, + struct snd_soc_card *card) +{ + struct device_node *wsa_of_node; + u32 wsa_max_devs; + u32 wsa_dev_cnt; + char *dev_name_str = NULL; + struct msm_wsa881x_dev_info *wsa881x_dev_info; + const char *wsa_auxdev_name_prefix[1]; + int found = 0; + int i; + int ret; + + /* Get maximum WSA device count for this platform */ + ret = of_property_read_u32(pdev->dev.of_node, + "qcom,wsa-max-devs", &wsa_max_devs); + if (ret) { + dev_dbg(&pdev->dev, + "%s: wsa-max-devs property missing in DT %s, ret = %d\n", + __func__, pdev->dev.of_node->full_name, ret); + goto err_dt; + } + if (wsa_max_devs == 0) { + dev_warn(&pdev->dev, + "%s: Max WSA devices is 0 for this target?\n", + __func__); + goto err_dt; + } + + /* Get count of WSA device phandles for this platform */ + wsa_dev_cnt = of_count_phandle_with_args(pdev->dev.of_node, + "qcom,wsa-devs", NULL); + if (wsa_dev_cnt == -ENOENT) { + dev_warn(&pdev->dev, "%s: No wsa device defined in DT.\n", + __func__); + goto err_dt; + } else if (wsa_dev_cnt <= 0) { + dev_err(&pdev->dev, + "%s: Error reading wsa device from DT. wsa_dev_cnt = %d\n", + __func__, wsa_dev_cnt); + ret = -EINVAL; + goto err_dt; + } + + /* + * Expect total phandles count to be NOT less than maximum possible + * WSA count. However, if it is less, then assign same value to + * max count as well. + */ + if (wsa_dev_cnt < wsa_max_devs) { + dev_dbg(&pdev->dev, + "%s: wsa_max_devs = %d cannot exceed wsa_dev_cnt = %d\n", + __func__, wsa_max_devs, wsa_dev_cnt); + wsa_max_devs = wsa_dev_cnt; + } + + /* Make sure prefix string passed for each WSA device */ + ret = of_property_count_strings(pdev->dev.of_node, + "qcom,wsa-aux-dev-prefix"); + if (ret != wsa_dev_cnt) { + dev_err(&pdev->dev, + "%s: expecting %d wsa prefix. Defined only %d in DT\n", + __func__, wsa_dev_cnt, ret); + ret = -EINVAL; + goto err_dt; + } + + /* + * Alloc mem to store phandle and index info of WSA device, if already + * registered with ALSA core + */ + wsa881x_dev_info = devm_kcalloc(&pdev->dev, wsa_max_devs, + sizeof(struct msm_wsa881x_dev_info), + GFP_KERNEL); + if (!wsa881x_dev_info) { + ret = -ENOMEM; + goto err_mem; + } + + /* + * search and check whether all WSA devices are already + * registered with ALSA core or not. If found a node, store + * the node and the index in a local array of struct for later + * use. + */ + for (i = 0; i < wsa_dev_cnt; i++) { + wsa_of_node = of_parse_phandle(pdev->dev.of_node, + "qcom,wsa-devs", i); + if (unlikely(!wsa_of_node)) { + /* we should not be here */ + dev_err(&pdev->dev, + "%s: wsa dev node is not present\n", + __func__); + ret = -EINVAL; + goto err_dev_node; + } + if (soc_find_component(wsa_of_node, NULL)) { + /* WSA device registered with ALSA core */ + wsa881x_dev_info[found].of_node = wsa_of_node; + wsa881x_dev_info[found].index = i; + found++; + if (found == wsa_max_devs) + break; + } + } + + if (found < wsa_max_devs) { + dev_dbg(&pdev->dev, + "%s: failed to find %d components. Found only %d\n", + __func__, wsa_max_devs, found); + return -EPROBE_DEFER; + } + dev_info(&pdev->dev, + "%s: found %d wsa881x devices registered with ALSA core\n", + __func__, found); + + card->num_aux_devs = wsa_max_devs; + card->num_configs = wsa_max_devs; + + /* Alloc array of AUX devs struct */ + msm_aux_dev = devm_kcalloc(&pdev->dev, card->num_aux_devs, + sizeof(struct snd_soc_aux_dev), + GFP_KERNEL); + if (!msm_aux_dev) { + ret = -ENOMEM; + goto err_auxdev_mem; + } + + /* Alloc array of codec conf struct */ + msm_codec_conf = devm_kcalloc(&pdev->dev, card->num_aux_devs, + sizeof(struct snd_soc_codec_conf), + GFP_KERNEL); + if (!msm_codec_conf) { + ret = -ENOMEM; + goto err_codec_conf; + } + + for (i = 0; i < card->num_aux_devs; i++) { + dev_name_str = devm_kzalloc(&pdev->dev, DEV_NAME_STR_LEN, + GFP_KERNEL); + if (!dev_name_str) { + ret = -ENOMEM; + goto err_dev_str; + } + + ret = of_property_read_string_index(pdev->dev.of_node, + "qcom,wsa-aux-dev-prefix", + wsa881x_dev_info[i].index, + wsa_auxdev_name_prefix); + if (ret) { + dev_err(&pdev->dev, + "%s: failed to read wsa aux dev prefix, ret = %d\n", + __func__, ret); + ret = -EINVAL; + goto err_dt_prop; + } + + snprintf(dev_name_str, strlen("wsa881x.%d"), "wsa881x.%d", i); + msm_aux_dev[i].name = dev_name_str; + msm_aux_dev[i].codec_name = NULL; + msm_aux_dev[i].codec_of_node = + wsa881x_dev_info[i].of_node; + msm_aux_dev[i].init = msm_wsa881x_init; + msm_codec_conf[i].dev_name = NULL; + msm_codec_conf[i].name_prefix = wsa_auxdev_name_prefix[0]; + msm_codec_conf[i].of_node = wsa881x_dev_info[i].of_node; + } + card->codec_conf = msm_codec_conf; + card->aux_dev = msm_aux_dev; + + return 0; + +err_dt_prop: + devm_kfree(&pdev->dev, dev_name_str); +err_dev_str: + devm_kfree(&pdev->dev, msm_codec_conf); +err_codec_conf: + devm_kfree(&pdev->dev, msm_aux_dev); +err_auxdev_mem: +err_dev_node: + devm_kfree(&pdev->dev, wsa881x_dev_info); +err_mem: +err_dt: + return ret; +} + +static void i2s_auxpcm_init(struct platform_device *pdev) +{ + int count; + u32 mi2s_master_slave[MI2S_MAX]; + u32 mi2s_ext_mclk[MI2S_MAX]; + int ret; + + for (count = 0; count < MI2S_MAX; count++) { + mutex_init(&mi2s_intf_conf[count].lock); + mi2s_intf_conf[count].ref_cnt = 0; + } + + ret = of_property_read_u32_array(pdev->dev.of_node, + "qcom,msm-mi2s-master", + mi2s_master_slave, MI2S_MAX); + if (ret) { + dev_dbg(&pdev->dev, "%s: no qcom,msm-mi2s-master in DT node\n", + __func__); + } else { + for (count = 0; count < MI2S_MAX; count++) { + mi2s_intf_conf[count].msm_is_mi2s_master = + mi2s_master_slave[count]; + } + } + + ret = of_property_read_u32_array(pdev->dev.of_node, + "qcom,msm-mi2s-ext-mclk", + mi2s_ext_mclk, MI2S_MAX); + if (ret) { + dev_dbg(&pdev->dev, "%s: no qcom,msm-mi2s-ext-mclk in DT node\n", + __func__); + } else { + for (count = 0; count < MI2S_MAX; count++) + mi2s_intf_conf[count].msm_is_ext_mclk = + mi2s_ext_mclk[count]; + } +} + +static const struct of_device_id sdm660_asoc_machine_of_match[] = { + { .compatible = "qcom,sdm660-asoc-snd", + .data = "internal_codec"}, + { .compatible = "qcom,sdm660-asoc-snd-tasha", + .data = "tasha_codec"}, + { .compatible = "qcom,sdm660-asoc-snd-tavil", + .data = "tavil_codec"}, + { .compatible = "qcom,sdm670-asoc-snd", + .data = "internal_codec"}, + { .compatible = "qcom,sdm670-asoc-snd-tasha", + .data = "tasha_codec"}, + { .compatible = "qcom,sdm670-asoc-snd-tavil", + .data = "tavil_codec"}, + {}, +}; + +static int msm_asoc_machine_probe(struct platform_device *pdev) +{ + struct snd_soc_card *card = NULL; + struct msm_asoc_mach_data *pdata = NULL; + const char *mclk = "qcom,msm-mclk-freq"; + int ret = -EINVAL, id; + const struct of_device_id *match; + const char *usb_c_dt = "qcom,msm-mbhc-usbc-audio-supported"; + + pdata = devm_kzalloc(&pdev->dev, + sizeof(struct msm_asoc_mach_data), + GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + msm_set_codec_reg_done(false); + match = of_match_node(sdm660_asoc_machine_of_match, + pdev->dev.of_node); + if (!match) + goto err; + + ret = of_property_read_u32(pdev->dev.of_node, mclk, &id); + if (ret) { + dev_err(&pdev->dev, + "%s: missing %s in dt node\n", __func__, mclk); + id = DEFAULT_MCLK_RATE; + } + pdata->mclk_freq = id; + + if (!strcmp(match->data, "tasha_codec") || + !strcmp(match->data, "tavil_codec")) { + if (!strcmp(match->data, "tasha_codec")) + pdata->snd_card_val = EXT_SND_CARD_TASHA; + else + pdata->snd_card_val = EXT_SND_CARD_TAVIL; + ret = msm_ext_cdc_init(pdev, pdata, &card, &mbhc_cfg); + if (ret) + goto err; + } else if (!strcmp(match->data, "internal_codec")) { + pdata->snd_card_val = INT_SND_CARD; + ret = msm_int_cdc_init(pdev, pdata, &card, &mbhc_cfg); + if (ret) + goto err; + } else { + dev_err(&pdev->dev, + "%s: Not a matching DT sound node\n", __func__); + goto err; + } + if (!card) + goto err; + + if (pdata->snd_card_val == INT_SND_CARD) { + /*reading the gpio configurations from dtsi file*/ + pdata->pdm_gpio_p = of_parse_phandle(pdev->dev.of_node, + "qcom,cdc-pdm-gpios", 0); + pdata->comp_gpio_p = of_parse_phandle(pdev->dev.of_node, + "qcom,cdc-comp-gpios", 0); + pdata->dmic_gpio_p = of_parse_phandle(pdev->dev.of_node, + "qcom,cdc-dmic-gpios", 0); + pdata->ext_spk_gpio_p = of_parse_phandle(pdev->dev.of_node, + "qcom,cdc-ext-spk-gpios", 0); + } + + pdata->mi2s_gpio_p[PRIM_MI2S] = of_parse_phandle(pdev->dev.of_node, + "qcom,pri-mi2s-gpios", 0); + pdata->mi2s_gpio_p[SEC_MI2S] = of_parse_phandle(pdev->dev.of_node, + "qcom,sec-mi2s-gpios", 0); + pdata->mi2s_gpio_p[TERT_MI2S] = of_parse_phandle(pdev->dev.of_node, + "qcom,tert-mi2s-gpios", 0); + pdata->mi2s_gpio_p[QUAT_MI2S] = of_parse_phandle(pdev->dev.of_node, + "qcom,quat-mi2s-gpios", 0); + pdata->mi2s_gpio_p[QUIN_MI2S] = of_parse_phandle(pdev->dev.of_node, + "qcom,quin-mi2s-gpios", 0); + /* + * Parse US-Euro gpio info from DT. Report no error if us-euro + * entry is not found in DT file as some targets do not support + * US-Euro detection + */ + pdata->us_euro_gpio = of_get_named_gpio(pdev->dev.of_node, + "qcom,us-euro-gpios", 0); + if (!gpio_is_valid(pdata->us_euro_gpio)) + pdata->us_euro_gpio_p = of_parse_phandle(pdev->dev.of_node, + "qcom,us-euro-gpios", 0); + if (!gpio_is_valid(pdata->us_euro_gpio) && (!pdata->us_euro_gpio_p)) { + dev_dbg(&pdev->dev, "property %s not detected in node %s", + "qcom,us-euro-gpios", pdev->dev.of_node->full_name); + } else { + dev_dbg(&pdev->dev, "%s detected", + "qcom,us-euro-gpios"); + mbhc_cfg.swap_gnd_mic = msm_swap_gnd_mic; + } + + if (of_find_property(pdev->dev.of_node, usb_c_dt, NULL)) + mbhc_cfg.swap_gnd_mic = msm_swap_gnd_mic; + + ret = msm_prepare_us_euro(card); + if (ret) + dev_dbg(&pdev->dev, "msm_prepare_us_euro failed (%d)\n", + ret); + + i2s_auxpcm_init(pdev); + + ret = snd_soc_of_parse_audio_routing(card, "qcom,audio-routing"); + if (ret) + goto err; + + ret = msm_populate_dai_link_component_of_node(pdata, card); + if (ret) { + ret = -EPROBE_DEFER; + goto err; + } + + if (!of_property_read_bool(pdev->dev.of_node, "qcom,wsa-disable")) { + ret = msm_init_wsa_dev(pdev, card); + if (ret) + goto err; + } + + ret = devm_snd_soc_register_card(&pdev->dev, card); + if (ret == -EPROBE_DEFER) { + if (codec_reg_done) { + /* + * return failure as EINVAL since other codec + * registered sound card successfully. + * This avoids any further probe calls. + */ + ret = -EINVAL; + } + goto err; + } else if (ret) { + dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", + ret); + goto err; + } + if (pdata->snd_card_val != INT_SND_CARD) + msm_ext_register_audio_notifier(pdev); + + return 0; +err: + if (pdata->us_euro_gpio > 0) { + dev_dbg(&pdev->dev, "%s free us_euro gpio %d\n", + __func__, pdata->us_euro_gpio); + pdata->us_euro_gpio = 0; + } + if (pdata->hph_en1_gpio > 0) { + dev_dbg(&pdev->dev, "%s free hph_en1_gpio %d\n", + __func__, pdata->hph_en1_gpio); + gpio_free(pdata->hph_en1_gpio); + pdata->hph_en1_gpio = 0; + } + if (pdata->hph_en0_gpio > 0) { + dev_dbg(&pdev->dev, "%s free hph_en0_gpio %d\n", + __func__, pdata->hph_en0_gpio); + gpio_free(pdata->hph_en0_gpio); + pdata->hph_en0_gpio = 0; + } + devm_kfree(&pdev->dev, pdata); + return ret; +} + +static int msm_asoc_machine_remove(struct platform_device *pdev) +{ + struct snd_soc_card *card = platform_get_drvdata(pdev); + struct msm_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card); + + if (pdata->snd_card_val == INT_SND_CARD) + mutex_destroy(&pdata->cdc_int_mclk0_mutex); + + if (gpio_is_valid(pdata->us_euro_gpio)) { + gpio_free(pdata->us_euro_gpio); + pdata->us_euro_gpio = 0; + } + if (gpio_is_valid(pdata->hph_en1_gpio)) { + gpio_free(pdata->hph_en1_gpio); + pdata->hph_en1_gpio = 0; + } + if (gpio_is_valid(pdata->hph_en0_gpio)) { + gpio_free(pdata->hph_en0_gpio); + pdata->hph_en0_gpio = 0; + } + + if (pdata->snd_card_val != INT_SND_CARD) + audio_notifier_deregister("sdm660"); + + snd_soc_unregister_card(card); + return 0; +} + +static struct platform_driver sdm660_asoc_machine_driver = { + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + .pm = &snd_soc_pm_ops, + .of_match_table = sdm660_asoc_machine_of_match, + }, + .probe = msm_asoc_machine_probe, + .remove = msm_asoc_machine_remove, +}; +module_platform_driver(sdm660_asoc_machine_driver); + +MODULE_DESCRIPTION("ALSA SoC msm"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:" DRV_NAME); +MODULE_DEVICE_TABLE(of, sdm660_asoc_machine_of_match); diff --git a/audio-kernel/asoc/sdm660-common.h b/audio-kernel/asoc/sdm660-common.h new file mode 100644 index 000000000..6c3eefa26 --- /dev/null +++ b/audio-kernel/asoc/sdm660-common.h @@ -0,0 +1,133 @@ +/* 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 + * 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 __MSM_COMMON +#define __MSM_COMMON + +#include +#include +#include "codecs/wcd-mbhc-v2.h" + +#define DEFAULT_MCLK_RATE 9600000 +#define NATIVE_MCLK_RATE 11289600 + +#define SAMPLING_RATE_8KHZ 8000 +#define SAMPLING_RATE_11P025KHZ 11025 +#define SAMPLING_RATE_16KHZ 16000 +#define SAMPLING_RATE_22P05KHZ 22050 +#define SAMPLING_RATE_32KHZ 32000 +#define SAMPLING_RATE_44P1KHZ 44100 +#define SAMPLING_RATE_48KHZ 48000 +#define SAMPLING_RATE_88P2KHZ 88200 +#define SAMPLING_RATE_96KHZ 96000 +#define SAMPLING_RATE_176P4KHZ 176400 +#define SAMPLING_RATE_192KHZ 192000 +#define SAMPLING_RATE_352P8KHZ 352800 +#define SAMPLING_RATE_384KHZ 384000 + +#define TDM_CHANNEL_MAX 8 +#define TDM_SLOT_OFFSET_MAX 8 + +enum { + TDM_0 = 0, + TDM_1, + TDM_2, + TDM_3, + TDM_4, + TDM_5, + TDM_6, + TDM_7, + TDM_PORT_MAX, +}; + +enum { + TDM_PRI = 0, + TDM_SEC, + TDM_TERT, + TDM_QUAT, + TDM_QUIN, + TDM_INTERFACE_MAX, +}; + +struct tdm_port { + u32 mode; + u32 channel; +}; + +enum { + PRIM_MI2S = 0, + SEC_MI2S, + TERT_MI2S, + QUAT_MI2S, + QUIN_MI2S, + MI2S_MAX, +}; + +enum { + DIG_CDC, + ANA_CDC, + CODECS_MAX, +}; + +extern const struct snd_kcontrol_new msm_common_snd_controls[]; +extern bool codec_reg_done; +struct sdm660_codec { + void* (*get_afe_config_fn)(struct snd_soc_codec *codec, + enum afe_config_type config_type); +}; + +enum { + INT_SND_CARD, + EXT_SND_CARD_TASHA, + EXT_SND_CARD_TAVIL, +}; + +struct msm_asoc_mach_data { + int us_euro_gpio; /* used by gpio driver API */ + int usbc_en2_gpio; /* used by gpio driver API */ + int hph_en1_gpio; + int hph_en0_gpio; + struct device_node *us_euro_gpio_p; /* used by pinctrl API */ + struct pinctrl *usbc_en2_gpio_p; /* used by pinctrl API */ + struct device_node *hph_en1_gpio_p; /* used by pinctrl API */ + struct device_node *hph_en0_gpio_p; /* used by pinctrl API */ + struct device_node *pdm_gpio_p; /* used by pinctrl API */ + struct device_node *comp_gpio_p; /* used by pinctrl API */ + struct device_node *dmic_gpio_p; /* used by pinctrl API */ + struct device_node *ext_spk_gpio_p; /* used by pinctrl API */ + struct device_node *mi2s_gpio_p[MI2S_MAX]; /* used by pinctrl API */ + struct snd_soc_codec *codec; + struct sdm660_codec sdm660_codec_fn; + struct snd_info_entry *codec_root; + int spk_ext_pa_gpio; + int mclk_freq; + bool native_clk_set; + int lb_mode; + int snd_card_val; + u8 micbias1_cap_mode; + u8 micbias2_cap_mode; + atomic_t int_mclk0_rsc_ref; + atomic_t int_mclk0_enabled; + struct mutex cdc_int_mclk0_mutex; + struct delayed_work disable_int_mclk0_work; + struct afe_clk_set digital_cdc_core_clk; +}; + +int msm_common_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params); +int msm_aux_pcm_snd_startup(struct snd_pcm_substream *substream); +void msm_aux_pcm_snd_shutdown(struct snd_pcm_substream *substream); +int msm_mi2s_snd_startup(struct snd_pcm_substream *substream); +void msm_mi2s_snd_shutdown(struct snd_pcm_substream *substream); +int msm_common_snd_controls_size(void); +void msm_set_codec_reg_done(bool done); +#endif diff --git a/audio-kernel/asoc/sdm660-ext-dai-links.c b/audio-kernel/asoc/sdm660-ext-dai-links.c new file mode 100644 index 000000000..5f5a60e21 --- /dev/null +++ b/audio-kernel/asoc/sdm660-ext-dai-links.c @@ -0,0 +1,2162 @@ +/* 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 + * 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 +#include +#include +#include +#include +#include +#include "msm-pcm-routing-v2.h" +#include "sdm660-common.h" +#include "sdm660-external.h" +#include "codecs/core.h" +#include "codecs/wcd9335.h" +#include + +#define DEV_NAME_STR_LEN 32 +#define __CHIPSET__ "SDM660 " +#define MSM_DAILINK_NAME(name) (__CHIPSET__#name) + +#define WCN_CDC_SLIM_RX_CH_MAX 2 +#define WCN_CDC_SLIM_TX_CH_MAX 3 +#define MSM_LL_QOS_VALUE 300 /* time in us to ensure LPM doesn't go in C3/C4 */ + +static struct snd_soc_card snd_soc_card_msm_card_tavil = { + .name = "sdm670-tavil-snd-card", + .late_probe = msm_snd_card_tavil_late_probe, +}; + +static struct snd_soc_card snd_soc_card_msm_card_tasha = { + .name = "sdm670-tasha-snd-card", + .late_probe = msm_snd_card_tasha_late_probe, +}; + +static struct snd_soc_ops msm_ext_slimbus_be_ops = { + .hw_params = msm_snd_hw_params, +}; + +static struct snd_soc_ops msm_ext_cpe_ops = { + .hw_params = msm_snd_cpe_hw_params, +}; + +static struct snd_soc_ops msm_ext_slimbus_2_be_ops = { + .hw_params = msm_ext_slimbus_2_hw_params, +}; + +static struct snd_soc_ops msm_mi2s_be_ops = { + .startup = msm_mi2s_snd_startup, + .shutdown = msm_mi2s_snd_shutdown, +}; + +static struct snd_soc_ops msm_aux_pcm_be_ops = { + .startup = msm_aux_pcm_snd_startup, + .shutdown = msm_aux_pcm_snd_shutdown, +}; + +static int msm_wcn_init(struct snd_soc_pcm_runtime *rtd) +{ + unsigned int rx_ch[WCN_CDC_SLIM_RX_CH_MAX] = {157, 158}; + unsigned int tx_ch[WCN_CDC_SLIM_TX_CH_MAX] = {159, 160, 161}; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + + return snd_soc_dai_set_channel_map(codec_dai, ARRAY_SIZE(tx_ch), + tx_ch, ARRAY_SIZE(rx_ch), rx_ch); +} + +static int msm_wcn_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai_link *dai_link = rtd->dai_link; + u32 rx_ch[WCN_CDC_SLIM_RX_CH_MAX], tx_ch[WCN_CDC_SLIM_TX_CH_MAX]; + u32 rx_ch_cnt = 0, tx_ch_cnt = 0; + int ret; + + dev_dbg(rtd->dev, "%s: %s_tx_dai_id_%d\n", __func__, + codec_dai->name, codec_dai->id); + ret = snd_soc_dai_get_channel_map(codec_dai, + &tx_ch_cnt, tx_ch, &rx_ch_cnt, rx_ch); + if (ret) { + dev_err(rtd->dev, + "%s: failed to get BTFM codec chan map\n, err:%d\n", + __func__, ret); + goto exit; + } + + dev_dbg(rtd->dev, "%s: tx_ch_cnt(%d) id %d\n", + __func__, tx_ch_cnt, dai_link->id); + + ret = snd_soc_dai_set_channel_map(cpu_dai, + tx_ch_cnt, tx_ch, rx_ch_cnt, rx_ch); + if (ret) + dev_err(rtd->dev, "%s: failed to set cpu chan map, err:%d\n", + __func__, ret); + +exit: + return ret; +} + +static struct snd_soc_ops msm_wcn_ops = { + .hw_params = msm_wcn_hw_params, +}; + +/*TDM default offset currently only supporting TDM_RX_0 and TDM_TX_0 */ +static unsigned int tdm_slot_offset[TDM_PORT_MAX][TDM_SLOT_OFFSET_MAX] = { + {0, 4, 8, 12, 16, 20, 24, 28},/* TX_0 | RX_0 */ + {AFE_SLOT_MAPPING_OFFSET_INVALID},/* TX_1 | RX_1 */ + {AFE_SLOT_MAPPING_OFFSET_INVALID},/* TX_2 | RX_2 */ + {AFE_SLOT_MAPPING_OFFSET_INVALID},/* TX_3 | RX_3 */ + {AFE_SLOT_MAPPING_OFFSET_INVALID},/* TX_4 | RX_4 */ + {AFE_SLOT_MAPPING_OFFSET_INVALID},/* TX_5 | RX_5 */ + {AFE_SLOT_MAPPING_OFFSET_INVALID},/* TX_6 | RX_6 */ + {AFE_SLOT_MAPPING_OFFSET_INVALID},/* TX_7 | RX_7 */ +}; + +static unsigned int tdm_param_set_slot_mask(u16 port_id, int slot_width, + int slots) +{ + unsigned int slot_mask = 0; + int i, j; + unsigned int *slot_offset; + + for (i = TDM_0; i < TDM_PORT_MAX; i++) { + slot_offset = tdm_slot_offset[i]; + + for (j = 0; j < TDM_SLOT_OFFSET_MAX; j++) { + if (slot_offset[j] != AFE_SLOT_MAPPING_OFFSET_INVALID) + slot_mask |= + (1 << ((slot_offset[j] * 8) / slot_width)); + else + break; + } + } + + return slot_mask; +} + +static int msm_tdm_snd_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + int ret = 0; + int channels, slot_width, slots; + unsigned int slot_mask; + unsigned int *slot_offset; + int offset_channels = 0; + int i; + + pr_debug("%s: dai id = 0x%x\n", __func__, cpu_dai->id); + + channels = params_channels(params); + switch (channels) { + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + case 8: + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S32_LE: + case SNDRV_PCM_FORMAT_S24_LE: + case SNDRV_PCM_FORMAT_S16_LE: + /* + * up to 8 channels HW config should + * use 32 bit slot width for max support of + * stream bit width. (slot_width > bit_width) + */ + slot_width = 32; + break; + default: + pr_err("%s: invalid param format 0x%x\n", + __func__, params_format(params)); + return -EINVAL; + } + slots = 8; + slot_mask = tdm_param_set_slot_mask(cpu_dai->id, + slot_width, + slots); + if (!slot_mask) { + pr_err("%s: invalid slot_mask 0x%x\n", + __func__, slot_mask); + return -EINVAL; + } + break; + default: + pr_err("%s: invalid param channels %d\n", + __func__, channels); + return -EINVAL; + } + /* currently only supporting TDM_RX_0 and TDM_TX_0 */ + switch (cpu_dai->id) { + case AFE_PORT_ID_PRIMARY_TDM_RX: + case AFE_PORT_ID_SECONDARY_TDM_RX: + case AFE_PORT_ID_TERTIARY_TDM_RX: + case AFE_PORT_ID_QUATERNARY_TDM_RX: + case AFE_PORT_ID_QUINARY_TDM_RX: + case AFE_PORT_ID_PRIMARY_TDM_TX: + case AFE_PORT_ID_SECONDARY_TDM_TX: + case AFE_PORT_ID_TERTIARY_TDM_TX: + case AFE_PORT_ID_QUATERNARY_TDM_TX: + case AFE_PORT_ID_QUINARY_TDM_TX: + slot_offset = tdm_slot_offset[TDM_0]; + break; + default: + pr_err("%s: dai id 0x%x not supported\n", + __func__, cpu_dai->id); + return -EINVAL; + } + + for (i = 0; i < TDM_SLOT_OFFSET_MAX; i++) { + if (slot_offset[i] != AFE_SLOT_MAPPING_OFFSET_INVALID) + offset_channels++; + else + break; + } + + if (offset_channels == 0) { + pr_err("%s: slot offset not supported, offset_channels %d\n", + __func__, offset_channels); + return -EINVAL; + } + + if (channels > offset_channels) { + pr_err("%s: channels %d exceed offset_channels %d\n", + __func__, channels, offset_channels); + return -EINVAL; + } + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0, slot_mask, + slots, slot_width); + if (ret < 0) { + pr_err("%s: failed to set tdm slot, err:%d\n", + __func__, ret); + goto end; + } + + ret = snd_soc_dai_set_channel_map(cpu_dai, 0, NULL, + channels, slot_offset); + if (ret < 0) { + pr_err("%s: failed to set channel map, err:%d\n", + __func__, ret); + goto end; + } + } else { + ret = snd_soc_dai_set_tdm_slot(cpu_dai, slot_mask, 0, + slots, slot_width); + if (ret < 0) { + pr_err("%s: failed to set tdm slot, err:%d\n", + __func__, ret); + goto end; + } + + ret = snd_soc_dai_set_channel_map(cpu_dai, channels, + slot_offset, 0, NULL); + if (ret < 0) { + pr_err("%s: failed to set channel map, err:%d\n", + __func__, ret); + goto end; + } + } +end: + return ret; +} + +static struct snd_soc_ops msm_tdm_be_ops = { + .hw_params = msm_tdm_snd_hw_params +}; + +static int msm_fe_qos_prepare(struct snd_pcm_substream *substream) +{ + cpumask_t mask; + + if (pm_qos_request_active(&substream->latency_pm_qos_req)) + pm_qos_remove_request(&substream->latency_pm_qos_req); + + cpumask_clear(&mask); + cpumask_set_cpu(1, &mask); /* affine to core 1 */ + cpumask_set_cpu(2, &mask); /* affine to core 2 */ + cpumask_copy(&substream->latency_pm_qos_req.cpus_affine, &mask); + + substream->latency_pm_qos_req.type = PM_QOS_REQ_AFFINE_CORES; + + pm_qos_add_request(&substream->latency_pm_qos_req, + PM_QOS_CPU_DMA_LATENCY, + MSM_LL_QOS_VALUE); + return 0; +} + +static struct snd_soc_ops msm_fe_qos_ops = { + .prepare = msm_fe_qos_prepare, +}; + +static struct snd_soc_dai_link msm_ext_tasha_fe_dai[] = { + /* tasha_vifeedback for speaker protection */ + { + .name = LPASS_BE_SLIMBUS_4_TX, + .stream_name = "Slimbus4 Capture", + .cpu_dai_name = "msm-dai-q6-dev.16393", + .platform_name = "msm-pcm-hostless", + .codec_name = "tasha_codec", + .codec_dai_name = "tasha_vifeedback", + .id = MSM_BACKEND_DAI_SLIMBUS_4_TX, + .be_hw_params_fixup = msm_ext_be_hw_params_fixup, + .ops = &msm_ext_slimbus_be_ops, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + }, + /* Ultrasound RX DAI Link */ + { + .name = "SLIMBUS_2 Hostless Playback", + .stream_name = "SLIMBUS_2 Hostless Playback", + .cpu_dai_name = "msm-dai-q6-dev.16388", + .platform_name = "msm-pcm-hostless", + .codec_name = "tasha_codec", + .codec_dai_name = "tasha_rx2", + .ignore_suspend = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .ignore_pmdown_time = 1, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ops = &msm_ext_slimbus_2_be_ops, + }, + /* Ultrasound TX DAI Link */ + { + .name = "SLIMBUS_2 Hostless Capture", + .stream_name = "SLIMBUS_2 Hostless Capture", + .cpu_dai_name = "msm-dai-q6-dev.16389", + .platform_name = "msm-pcm-hostless", + .codec_name = "tasha_codec", + .codec_dai_name = "tasha_tx2", + .ignore_suspend = 1, + .dpcm_capture = 1, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ops = &msm_ext_slimbus_2_be_ops, + }, + /* CPE LSM direct dai-link */ + { + .name = "CPE Listen service", + .stream_name = "CPE Listen Audio Service", + .cpu_dai_name = "msm-dai-slim", + .platform_name = "msm-cpe-lsm", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .dpcm_capture = 1, + .codec_dai_name = "tasha_mad1", + .codec_name = "tasha_codec", + .ops = &msm_ext_cpe_ops, + }, + { + .name = "SLIMBUS_6 Hostless Playback", + .stream_name = "SLIMBUS_6 Hostless", + .cpu_dai_name = "SLIMBUS6_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .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, + /* this dailink has playback support */ + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + /* CPE LSM EC PP direct dai-link */ + { + .name = "CPE Listen service ECPP", + .stream_name = "CPE Listen Audio Service ECPP", + .cpu_dai_name = "CPE_LSM_NOHOST", + .platform_name = "msm-cpe-lsm.3", + .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 = "tasha_cpe", + .codec_name = "tasha_codec", + }, +}; + +static struct snd_soc_dai_link msm_ext_tavil_fe_dai[] = { + { + .name = LPASS_BE_SLIMBUS_4_TX, + .stream_name = "Slimbus4 Capture", + .cpu_dai_name = "msm-dai-q6-dev.16393", + .platform_name = "msm-pcm-hostless", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_vifeedback", + .id = MSM_BACKEND_DAI_SLIMBUS_4_TX, + .be_hw_params_fixup = msm_ext_be_hw_params_fixup, + .ops = &msm_ext_slimbus_be_ops, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + }, + /* Ultrasound RX DAI Link */ + { + .name = "SLIMBUS_2 Hostless Playback", + .stream_name = "SLIMBUS_2 Hostless Playback", + .cpu_dai_name = "msm-dai-q6-dev.16388", + .platform_name = "msm-pcm-hostless", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_rx2", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ops = &msm_ext_slimbus_2_be_ops, + }, + /* Ultrasound TX DAI Link */ + { + .name = "SLIMBUS_2 Hostless Capture", + .stream_name = "SLIMBUS_2 Hostless Capture", + .cpu_dai_name = "msm-dai-q6-dev.16389", + .platform_name = "msm-pcm-hostless", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_tx2", + .ignore_suspend = 1, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ops = &msm_ext_slimbus_2_be_ops, + }, +}; + +static struct snd_soc_dai_link msm_ext_tasha_be_dai[] = { + /* Backend DAI Links */ + { + .name = LPASS_BE_SLIMBUS_0_RX, + .stream_name = "Slimbus Playback", + .cpu_dai_name = "msm-dai-q6-dev.16384", + .platform_name = "msm-pcm-routing", + .codec_name = "tasha_codec", + .codec_dai_name = "tasha_mix_rx1", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_0_RX, + .init = &msm_audrx_init, + .be_hw_params_fixup = msm_ext_be_hw_params_fixup, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + .ops = &msm_ext_slimbus_be_ops, + }, + { + .name = LPASS_BE_SLIMBUS_0_TX, + .stream_name = "Slimbus Capture", + .cpu_dai_name = "msm-dai-q6-dev.16385", + .platform_name = "msm-pcm-routing", + .codec_name = "tasha_codec", + .codec_dai_name = "tasha_tx1", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_0_TX, + .be_hw_params_fixup = msm_ext_be_hw_params_fixup, + .ignore_suspend = 1, + .ops = &msm_ext_slimbus_be_ops, + }, + { + .name = LPASS_BE_SLIMBUS_1_RX, + .stream_name = "Slimbus1 Playback", + .cpu_dai_name = "msm-dai-q6-dev.16386", + .platform_name = "msm-pcm-routing", + .codec_name = "tasha_codec", + .codec_dai_name = "tasha_mix_rx1", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_1_RX, + .be_hw_params_fixup = msm_ext_be_hw_params_fixup, + .ops = &msm_ext_slimbus_be_ops, + /* dai link has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_1_TX, + .stream_name = "Slimbus1 Capture", + .cpu_dai_name = "msm-dai-q6-dev.16387", + .platform_name = "msm-pcm-routing", + .codec_name = "tasha_codec", + .codec_dai_name = "tasha_tx3", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_1_TX, + .be_hw_params_fixup = msm_ext_be_hw_params_fixup, + .ops = &msm_ext_slimbus_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_3_RX, + .stream_name = "Slimbus3 Playback", + .cpu_dai_name = "msm-dai-q6-dev.16390", + .platform_name = "msm-pcm-routing", + .codec_name = "tasha_codec", + .codec_dai_name = "tasha_mix_rx1", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_3_RX, + .be_hw_params_fixup = msm_ext_be_hw_params_fixup, + .ops = &msm_ext_slimbus_be_ops, + /* dai link has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_3_TX, + .stream_name = "Slimbus3 Capture", + .cpu_dai_name = "msm-dai-q6-dev.16391", + .platform_name = "msm-pcm-routing", + .codec_name = "tasha_codec", + .codec_dai_name = "tasha_tx1", + .no_pcm = 1, + .dpcm_capture = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_3_TX, + .be_hw_params_fixup = msm_ext_be_hw_params_fixup, + .ops = &msm_ext_slimbus_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_4_RX, + .stream_name = "Slimbus4 Playback", + .cpu_dai_name = "msm-dai-q6-dev.16392", + .platform_name = "msm-pcm-routing", + .codec_name = "tasha_codec", + .codec_dai_name = "tasha_mix_rx1", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_4_RX, + .be_hw_params_fixup = msm_ext_be_hw_params_fixup, + .ops = &msm_ext_slimbus_be_ops, + /* dai link has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_5_RX, + .stream_name = "Slimbus5 Playback", + .cpu_dai_name = "msm-dai-q6-dev.16394", + .platform_name = "msm-pcm-routing", + .codec_name = "tasha_codec", + .codec_dai_name = "tasha_rx3", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_5_RX, + .be_hw_params_fixup = msm_ext_be_hw_params_fixup, + .ops = &msm_ext_slimbus_be_ops, + /* dai link has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + /* MAD BE */ + { + .name = LPASS_BE_SLIMBUS_5_TX, + .stream_name = "Slimbus5 Capture", + .cpu_dai_name = "msm-dai-q6-dev.16395", + .platform_name = "msm-pcm-routing", + .codec_name = "tasha_codec", + .codec_dai_name = "tasha_mad1", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_5_TX, + .be_hw_params_fixup = msm_ext_be_hw_params_fixup, + .ops = &msm_ext_slimbus_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_6_RX, + .stream_name = "Slimbus6 Playback", + .cpu_dai_name = "msm-dai-q6-dev.16396", + .platform_name = "msm-pcm-routing", + .codec_name = "tasha_codec", + .codec_dai_name = "tasha_rx4", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_6_RX, + .be_hw_params_fixup = msm_ext_be_hw_params_fixup, + .ops = &msm_ext_slimbus_be_ops, + /* dai link has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, +}; + +static struct snd_soc_dai_link msm_ext_tavil_be_dai[] = { + { + .name = LPASS_BE_SLIMBUS_0_RX, + .stream_name = "Slimbus Playback", + .cpu_dai_name = "msm-dai-q6-dev.16384", + .platform_name = "msm-pcm-routing", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_rx1", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_0_RX, + .init = &msm_audrx_init, + .be_hw_params_fixup = msm_ext_be_hw_params_fixup, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + .ops = &msm_ext_slimbus_be_ops, + }, + { + .name = LPASS_BE_SLIMBUS_0_TX, + .stream_name = "Slimbus Capture", + .cpu_dai_name = "msm-dai-q6-dev.16385", + .platform_name = "msm-pcm-routing", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_tx1", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_0_TX, + .be_hw_params_fixup = msm_ext_be_hw_params_fixup, + .ignore_suspend = 1, + .ops = &msm_ext_slimbus_be_ops, + }, + { + .name = LPASS_BE_SLIMBUS_1_RX, + .stream_name = "Slimbus1 Playback", + .cpu_dai_name = "msm-dai-q6-dev.16386", + .platform_name = "msm-pcm-routing", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_rx1", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_1_RX, + .be_hw_params_fixup = msm_ext_be_hw_params_fixup, + .ops = &msm_ext_slimbus_be_ops, + /* dai link has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_1_TX, + .stream_name = "Slimbus1 Capture", + .cpu_dai_name = "msm-dai-q6-dev.16387", + .platform_name = "msm-pcm-routing", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_tx3", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_1_TX, + .be_hw_params_fixup = msm_ext_be_hw_params_fixup, + .ops = &msm_ext_slimbus_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_2_RX, + .stream_name = "Slimbus2 Playback", + .cpu_dai_name = "msm-dai-q6-dev.16388", + .platform_name = "msm-pcm-routing", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_rx2", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_2_RX, + .be_hw_params_fixup = msm_ext_be_hw_params_fixup, + .ops = &msm_ext_slimbus_be_ops, + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_3_RX, + .stream_name = "Slimbus3 Playback", + .cpu_dai_name = "msm-dai-q6-dev.16390", + .platform_name = "msm-pcm-routing", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_rx1", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_3_RX, + .be_hw_params_fixup = msm_ext_be_hw_params_fixup, + .ops = &msm_ext_slimbus_be_ops, + /* dai link has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_3_TX, + .stream_name = "Slimbus3 Capture", + .cpu_dai_name = "msm-dai-q6-dev.16391", + .platform_name = "msm-pcm-routing", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_tx1", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_3_TX, + .be_hw_params_fixup = msm_ext_be_hw_params_fixup, + .ops = &msm_ext_slimbus_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_4_RX, + .stream_name = "Slimbus4 Playback", + .cpu_dai_name = "msm-dai-q6-dev.16392", + .platform_name = "msm-pcm-routing", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_rx1", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_4_RX, + .be_hw_params_fixup = msm_ext_be_hw_params_fixup, + .ops = &msm_ext_slimbus_be_ops, + /* dai link has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_5_RX, + .stream_name = "Slimbus5 Playback", + .cpu_dai_name = "msm-dai-q6-dev.16394", + .platform_name = "msm-pcm-routing", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_rx3", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_5_RX, + .be_hw_params_fixup = msm_ext_be_hw_params_fixup, + .ops = &msm_ext_slimbus_be_ops, + /* dai link has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + /* MAD BE */ + { + .name = LPASS_BE_SLIMBUS_5_TX, + .stream_name = "Slimbus5 Capture", + .cpu_dai_name = "msm-dai-q6-dev.16395", + .platform_name = "msm-pcm-routing", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_mad1", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_5_TX, + .be_hw_params_fixup = msm_ext_be_hw_params_fixup, + .ops = &msm_ext_slimbus_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_6_RX, + .stream_name = "Slimbus6 Playback", + .cpu_dai_name = "msm-dai-q6-dev.16396", + .platform_name = "msm-pcm-routing", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_rx4", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_6_RX, + .be_hw_params_fixup = msm_ext_be_hw_params_fixup, + .ops = &msm_ext_slimbus_be_ops, + /* dai link has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, +}; + +static struct snd_soc_dai_link msm_ext_common_fe_dai[] = { + /* FrontEnd DAI Links */ + {/* hw:x,0 */ + .name = MSM_DAILINK_NAME(Media1), + .stream_name = "MultiMedia1", + .cpu_dai_name = "MultiMedia1", + .platform_name = "msm-pcm-dsp.0", + .dynamic = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + /* this dai link has playback support */ + .ignore_pmdown_time = 1, + .id = MSM_FRONTEND_DAI_MULTIMEDIA1 + }, + {/* hw:x,1 */ + .name = MSM_DAILINK_NAME(Media2), + .stream_name = "MultiMedia2", + .cpu_dai_name = "MultiMedia2", + .platform_name = "msm-pcm-dsp.0", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .ignore_suspend = 1, + /* this dai link has playback support */ + .ignore_pmdown_time = 1, + .id = MSM_FRONTEND_DAI_MULTIMEDIA2, + }, + {/* hw:x,2 */ + .name = "VoiceMMode1", + .stream_name = "VoiceMMode1", + .cpu_dai_name = "VoiceMMode1", + .platform_name = "msm-pcm-voice", + .dynamic = 1, + .dpcm_capture = 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", + .id = MSM_FRONTEND_DAI_VOICEMMODE1, + }, + {/* hw:x,3 */ + .name = "MSM VoIP", + .stream_name = "VoIP", + .cpu_dai_name = "VoIP", + .platform_name = "msm-voip-dsp", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + /* this dai link has playback support */ + .ignore_pmdown_time = 1, + .id = MSM_FRONTEND_DAI_VOIP, + }, + {/* hw:x,4 */ + .name = MSM_DAILINK_NAME(ULL), + .stream_name = "ULL", + .cpu_dai_name = "MultiMedia3", + .platform_name = "msm-pcm-dsp.2", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + /* this dai link has playback support */ + .ignore_pmdown_time = 1, + .id = MSM_FRONTEND_DAI_MULTIMEDIA3, + }, + /* Hostless PCM purpose */ + {/* hw:x,5 */ + .name = "SLIMBUS_0 Hostless", + .stream_name = "SLIMBUS_0 Hostless", + .cpu_dai_name = "SLIMBUS0_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_capture = 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, + /* This dai link has MI2S support */ + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + {/* hw:x,6 */ + .name = "MSM AFE-PCM RX", + .stream_name = "AFE-PROXY RX", + .cpu_dai_name = "msm-dai-q6-dev.241", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .platform_name = "msm-pcm-afe", + .ignore_suspend = 1, + /* this dai link has playback support */ + .ignore_pmdown_time = 1, + }, + {/* hw:x,7 */ + .name = "MSM AFE-PCM TX", + .stream_name = "AFE-PROXY TX", + .cpu_dai_name = "msm-dai-q6-dev.240", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .platform_name = "msm-pcm-afe", + .ignore_suspend = 1, + }, + {/* hw:x,8 */ + .name = MSM_DAILINK_NAME(Compress1), + .stream_name = "Compress1", + .cpu_dai_name = "MultiMedia4", + .platform_name = "msm-compress-dsp", + .async_ops = ASYNC_DPCM_SND_SOC_HW_PARAMS, + .dynamic = 1, + .dpcm_capture = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dai link has playback support */ + .id = MSM_FRONTEND_DAI_MULTIMEDIA4, + }, + {/* hw:x,9*/ + .name = "AUXPCM Hostless", + .stream_name = "AUXPCM Hostless", + .cpu_dai_name = "AUXPCM_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_capture = 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, + /* this dai link has playback support */ + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + {/* hw:x,10 */ + .name = "SLIMBUS_1 Hostless", + .stream_name = "SLIMBUS_1 Hostless", + .cpu_dai_name = "SLIMBUS1_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_capture = 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, /* dai link has playback support */ + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + {/* hw:x,11 */ + .name = "SLIMBUS_3 Hostless", + .stream_name = "SLIMBUS_3 Hostless", + .cpu_dai_name = "SLIMBUS3_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_capture = 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, /* dai link has playback support */ + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + {/* hw:x,12 */ + .name = "SLIMBUS_4 Hostless", + .stream_name = "SLIMBUS_4 Hostless", + .cpu_dai_name = "SLIMBUS4_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_capture = 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, /* dai link has playback support */ + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + {/* hw:x,13 */ + .name = MSM_DAILINK_NAME(LowLatency), + .stream_name = "MultiMedia5", + .cpu_dai_name = "MultiMedia5", + .platform_name = "msm-pcm-dsp.1", + .dynamic = 1, + .dpcm_capture = 1, + .dpcm_playback = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .ignore_suspend = 1, + /* this dai link has playback support */ + .ignore_pmdown_time = 1, + .id = MSM_FRONTEND_DAI_MULTIMEDIA5, + .ops = &msm_fe_qos_ops, + }, + /* LSM FE */ + {/* hw:x,14 */ + .name = "Listen 1 Audio Service", + .stream_name = "Listen 1 Audio Service", + .cpu_dai_name = "LSM1", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST }, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .id = MSM_FRONTEND_DAI_LSM1, + }, + {/* hw:x,15 */ + .name = MSM_DAILINK_NAME(Compress2), + .stream_name = "Compress2", + .cpu_dai_name = "MultiMedia7", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_capture = 1, + .dpcm_playback = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .ignore_suspend = 1, + /* this dai link has playback support */ + .ignore_pmdown_time = 1, + .id = MSM_FRONTEND_DAI_MULTIMEDIA7, + }, + {/* hw:x,16 */ + .name = MSM_DAILINK_NAME(MultiMedia10), + .stream_name = "MultiMedia10", + .cpu_dai_name = "MultiMedia10", + .platform_name = "msm-pcm-dsp.1", + .dynamic = 1, + .dpcm_capture = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dai link has playback support */ + .id = MSM_FRONTEND_DAI_MULTIMEDIA10, + }, + {/* hw:x,17 */ + .name = MSM_DAILINK_NAME(ULL_NOIRQ), + .stream_name = "MM_NOIRQ", + .cpu_dai_name = "MultiMedia8", + .platform_name = "msm-pcm-dsp-noirq", + .dynamic = 1, + .dpcm_capture = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dai link has playback support */ + .id = MSM_FRONTEND_DAI_MULTIMEDIA8, + .ops = &msm_fe_qos_ops, + }, + {/* hw:x,18 */ + .name = "HDMI_RX_HOSTLESS", + .stream_name = "HDMI_RX_HOSTLESS", + .cpu_dai_name = "HDMI_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .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", + }, + {/* hw:x,19 */ + .name = "VoiceMMode2", + .stream_name = "VoiceMMode2", + .cpu_dai_name = "VoiceMMode2", + .platform_name = "msm-pcm-voice", + .dynamic = 1, + .dpcm_capture = 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", + .id = MSM_FRONTEND_DAI_VOICEMMODE2, + }, + {/* hw:x,20 */ + .name = "Listen 2 Audio Service", + .stream_name = "Listen 2 Audio Service", + .cpu_dai_name = "LSM2", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST }, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .id = MSM_FRONTEND_DAI_LSM2, + }, + {/* hw:x,21 */ + .name = "Listen 3 Audio Service", + .stream_name = "Listen 3 Audio Service", + .cpu_dai_name = "LSM3", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST }, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .id = MSM_FRONTEND_DAI_LSM3, + }, + {/* hw:x,22 */ + .name = "Listen 4 Audio Service", + .stream_name = "Listen 4 Audio Service", + .cpu_dai_name = "LSM4", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST }, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .id = MSM_FRONTEND_DAI_LSM4, + }, + {/* hw:x,23 */ + .name = "Listen 5 Audio Service", + .stream_name = "Listen 5 Audio Service", + .cpu_dai_name = "LSM5", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST }, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .id = MSM_FRONTEND_DAI_LSM5, + }, + {/* hw:x,24 */ + .name = "Listen 6 Audio Service", + .stream_name = "Listen 6 Audio Service", + .cpu_dai_name = "LSM6", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST }, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .id = MSM_FRONTEND_DAI_LSM6 + }, + {/* hw:x,25 */ + .name = "Listen 7 Audio Service", + .stream_name = "Listen 7 Audio Service", + .cpu_dai_name = "LSM7", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST }, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .id = MSM_FRONTEND_DAI_LSM7, + }, + {/* hw:x,26 */ + .name = "Listen 8 Audio Service", + .stream_name = "Listen 8 Audio Service", + .cpu_dai_name = "LSM8", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST }, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .id = MSM_FRONTEND_DAI_LSM8, + }, + {/* hw:x,27 */ + .name = MSM_DAILINK_NAME(Media9), + .stream_name = "MultiMedia9", + .cpu_dai_name = "MultiMedia9", + .platform_name = "msm-pcm-dsp.0", + .dynamic = 1, + .dpcm_capture = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dai link has playback support */ + .id = MSM_FRONTEND_DAI_MULTIMEDIA9, + }, + {/* hw:x,28 */ + .name = MSM_DAILINK_NAME(Compress4), + .stream_name = "Compress4", + .cpu_dai_name = "MultiMedia11", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_capture = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dai link has playback support */ + .id = MSM_FRONTEND_DAI_MULTIMEDIA11, + }, + {/* hw:x,29 */ + .name = MSM_DAILINK_NAME(Compress5), + .stream_name = "Compress5", + .cpu_dai_name = "MultiMedia12", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_capture = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dai link has playback support */ + .id = MSM_FRONTEND_DAI_MULTIMEDIA12, + }, + {/* hw:x,30 */ + .name = MSM_DAILINK_NAME(Compress6), + .stream_name = "Compress6", + .cpu_dai_name = "MultiMedia13", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_capture = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dai link has playback support */ + .id = MSM_FRONTEND_DAI_MULTIMEDIA13, + }, + {/* hw:x,31 */ + .name = MSM_DAILINK_NAME(Compress7), + .stream_name = "Compress7", + .cpu_dai_name = "MultiMedia14", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_capture = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dai link has playback support */ + .id = MSM_FRONTEND_DAI_MULTIMEDIA14, + }, + {/* hw:x,32 */ + .name = MSM_DAILINK_NAME(Compress8), + .stream_name = "Compress8", + .cpu_dai_name = "MultiMedia15", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_capture = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dai link has playback support */ + .id = MSM_FRONTEND_DAI_MULTIMEDIA15, + }, + {/* hw:x,33 */ + .name = MSM_DAILINK_NAME(ULL_NOIRQ_2), + .stream_name = "MM_NOIRQ_2", + .cpu_dai_name = "MultiMedia16", + .platform_name = "msm-pcm-dsp-noirq", + .dynamic = 1, + .dpcm_capture = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dai link has playback support */ + .id = MSM_FRONTEND_DAI_MULTIMEDIA16, + }, + {/* hw:x,34 */ + .name = "SLIMBUS_8 Hostless", + .stream_name = "SLIMBUS8_HOSTLESS Capture", + .cpu_dai_name = "SLIMBUS8_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + {/* hw:x,35 */ + .name = "SLIMBUS7 Hostless", + .stream_name = "SLIMBUS7 Hostless", + .cpu_dai_name = "SLIMBUS7_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 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", + }, + {/* hw:x,36 */ + .name = "SDM660 HFP TX", + .stream_name = "MultiMedia6", + .cpu_dai_name = "MultiMedia6", + .platform_name = "msm-pcm-loopback", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .ignore_suspend = 1, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_pmdown_time = 1, + .id = MSM_FRONTEND_DAI_MULTIMEDIA6, + }, +}; + +static struct snd_soc_dai_link msm_ext_common_be_dai[] = { + { + .name = LPASS_BE_AFE_PCM_RX, + .stream_name = "AFE Playback", + .cpu_dai_name = "msm-dai-q6-dev.224", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_AFE_PCM_RX, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + /* this dai link has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_AFE_PCM_TX, + .stream_name = "AFE Capture", + .cpu_dai_name = "msm-dai-q6-dev.225", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_AFE_PCM_TX, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ignore_suspend = 1, + }, + /* Incall Record Uplink BACK END DAI Link */ + { + .name = LPASS_BE_INCALL_RECORD_TX, + .stream_name = "Voice Uplink Capture", + .cpu_dai_name = "msm-dai-q6-dev.32772", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_INCALL_RECORD_TX, + .be_hw_params_fixup = msm_ext_be_hw_params_fixup, + .ignore_suspend = 1, + }, + /* Incall Record Downlink BACK END DAI Link */ + { + .name = LPASS_BE_INCALL_RECORD_RX, + .stream_name = "Voice Downlink Capture", + .cpu_dai_name = "msm-dai-q6-dev.32771", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_INCALL_RECORD_RX, + .be_hw_params_fixup = msm_ext_be_hw_params_fixup, + .ignore_suspend = 1, + }, + /* Incall Music BACK END DAI Link */ + { + .name = LPASS_BE_VOICE_PLAYBACK_TX, + .stream_name = "Voice Farend Playback", + .cpu_dai_name = "msm-dai-q6-dev.32773", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_VOICE_PLAYBACK_TX, + .be_hw_params_fixup = msm_ext_be_hw_params_fixup, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + }, + /* Incall Music 2 BACK END DAI Link */ + { + .name = LPASS_BE_VOICE2_PLAYBACK_TX, + .stream_name = "Voice2 Farend Playback", + .cpu_dai_name = "msm-dai-q6-dev.32770", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_VOICE2_PLAYBACK_TX, + .be_hw_params_fixup = msm_ext_be_hw_params_fixup, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + }, + { + .name = LPASS_BE_USB_AUDIO_RX, + .stream_name = "USB Audio Playback", + .cpu_dai_name = "msm-dai-q6-dev.28672", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_USB_RX, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_USB_AUDIO_TX, + .stream_name = "USB Audio Capture", + .cpu_dai_name = "msm-dai-q6-dev.28673", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_USB_TX, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_PRI_TDM_RX_0, + .stream_name = "Primary TDM0 Playback", + .cpu_dai_name = "msm-dai-q6-tdm.36864", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_PRI_TDM_RX_0, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ops = &msm_tdm_be_ops, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + }, + { + .name = LPASS_BE_PRI_TDM_TX_0, + .stream_name = "Primary TDM0 Capture", + .cpu_dai_name = "msm-dai-q6-tdm.36865", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_PRI_TDM_TX_0, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ops = &msm_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SEC_TDM_RX_0, + .stream_name = "Secondary TDM0 Playback", + .cpu_dai_name = "msm-dai-q6-tdm.36880", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SEC_TDM_RX_0, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ops = &msm_tdm_be_ops, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + }, + { + .name = LPASS_BE_SEC_TDM_TX_0, + .stream_name = "Secondary TDM0 Capture", + .cpu_dai_name = "msm-dai-q6-tdm.36881", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_SEC_TDM_TX_0, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ops = &msm_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_TERT_TDM_RX_0, + .stream_name = "Tertiary TDM0 Playback", + .cpu_dai_name = "msm-dai-q6-tdm.36896", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_TERT_TDM_RX_0, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ops = &msm_tdm_be_ops, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + }, + { + .name = LPASS_BE_TERT_TDM_TX_0, + .stream_name = "Tertiary TDM0 Capture", + .cpu_dai_name = "msm-dai-q6-tdm.36897", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_TERT_TDM_TX_0, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ops = &msm_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_QUAT_TDM_RX_0, + .stream_name = "Quaternary TDM0 Playback", + .cpu_dai_name = "msm-dai-q6-tdm.36912", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_QUAT_TDM_RX_0, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ops = &msm_tdm_be_ops, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + }, + { + .name = LPASS_BE_QUAT_TDM_TX_0, + .stream_name = "Quaternary TDM0 Capture", + .cpu_dai_name = "msm-dai-q6-tdm.36913", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_QUAT_TDM_TX_0, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ops = &msm_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_QUIN_TDM_RX_0, + .stream_name = "Quinary TDM0 Playback", + .cpu_dai_name = "msm-dai-q6-tdm.36928", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_QUIN_TDM_RX_0, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ops = &msm_tdm_be_ops, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + }, + { + .name = LPASS_BE_QUIN_TDM_TX_0, + .stream_name = "Quinary TDM0 Capture", + .cpu_dai_name = "msm-dai-q6-tdm.36929", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_QUIN_TDM_TX_0, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ops = &msm_tdm_be_ops, + .ignore_suspend = 1, + }, +}; + +static struct snd_soc_dai_link msm_mi2s_be_dai_links[] = { + { + .name = LPASS_BE_PRI_MI2S_RX, + .stream_name = "Primary MI2S Playback", + .cpu_dai_name = "msm-dai-q6-mi2s.0", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_PRI_MI2S_RX, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + }, + { + .name = LPASS_BE_PRI_MI2S_TX, + .stream_name = "Primary MI2S Capture", + .cpu_dai_name = "msm-dai-q6-mi2s.0", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_PRI_MI2S_TX, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SEC_MI2S_RX, + .stream_name = "Secondary MI2S Playback", + .cpu_dai_name = "msm-dai-q6-mi2s.1", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SECONDARY_MI2S_RX, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + }, + { + .name = LPASS_BE_SEC_MI2S_TX, + .stream_name = "Secondary MI2S Capture", + .cpu_dai_name = "msm-dai-q6-mi2s.1", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_SECONDARY_MI2S_TX, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_TERT_MI2S_RX, + .stream_name = "Tertiary MI2S Playback", + .cpu_dai_name = "msm-dai-q6-mi2s.2", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_TERTIARY_MI2S_RX, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + }, + { + .name = LPASS_BE_TERT_MI2S_TX, + .stream_name = "Tertiary MI2S Capture", + .cpu_dai_name = "msm-dai-q6-mi2s.2", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_TERTIARY_MI2S_TX, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_QUAT_MI2S_RX, + .stream_name = "Quaternary MI2S Playback", + .cpu_dai_name = "msm-dai-q6-mi2s.3", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_QUATERNARY_MI2S_RX, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + }, + { + .name = LPASS_BE_QUAT_MI2S_TX, + .stream_name = "Quaternary MI2S Capture", + .cpu_dai_name = "msm-dai-q6-mi2s.3", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_QUIN_MI2S_RX, + .stream_name = "Quinary MI2S Playback", + .cpu_dai_name = "msm-dai-q6-mi2s.4", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_QUINARY_MI2S_RX, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + }, + { + .name = LPASS_BE_QUIN_MI2S_TX, + .stream_name = "Quinary MI2S Capture", + .cpu_dai_name = "msm-dai-q6-mi2s.4", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_QUINARY_MI2S_TX, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + }, +}; + +static struct snd_soc_dai_link msm_auxpcm_be_dai_links[] = { + /* Primary AUX PCM Backend DAI Links */ + { + .name = LPASS_BE_AUXPCM_RX, + .stream_name = "AUX PCM Playback", + .cpu_dai_name = "msm-dai-q6-auxpcm.1", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_AUXPCM_RX, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + .ops = &msm_aux_pcm_be_ops, + }, + { + .name = LPASS_BE_AUXPCM_TX, + .stream_name = "AUX PCM Capture", + .cpu_dai_name = "msm-dai-q6-auxpcm.1", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_AUXPCM_TX, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ignore_suspend = 1, + .ops = &msm_aux_pcm_be_ops, + }, + /* Secondary AUX PCM Backend DAI Links */ + { + .name = LPASS_BE_SEC_AUXPCM_RX, + .stream_name = "Sec AUX PCM Playback", + .cpu_dai_name = "msm-dai-q6-auxpcm.2", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SEC_AUXPCM_RX, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + .ops = &msm_aux_pcm_be_ops, + }, + { + .name = LPASS_BE_SEC_AUXPCM_TX, + .stream_name = "Sec AUX PCM Capture", + .cpu_dai_name = "msm-dai-q6-auxpcm.2", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_SEC_AUXPCM_TX, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ignore_suspend = 1, + .ops = &msm_aux_pcm_be_ops, + }, + /* Tertiary AUX PCM Backend DAI Links */ + { + .name = LPASS_BE_TERT_AUXPCM_RX, + .stream_name = "Tert AUX PCM Playback", + .cpu_dai_name = "msm-dai-q6-auxpcm.3", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_TERT_AUXPCM_RX, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + .ops = &msm_aux_pcm_be_ops, + }, + { + .name = LPASS_BE_TERT_AUXPCM_TX, + .stream_name = "Tert AUX PCM Capture", + .cpu_dai_name = "msm-dai-q6-auxpcm.3", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_TERT_AUXPCM_TX, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ignore_suspend = 1, + .ops = &msm_aux_pcm_be_ops, + }, + /* Quaternary AUX PCM Backend DAI Links */ + { + .name = LPASS_BE_QUAT_AUXPCM_RX, + .stream_name = "Quat AUX PCM Playback", + .cpu_dai_name = "msm-dai-q6-auxpcm.4", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_QUAT_AUXPCM_RX, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + .ops = &msm_aux_pcm_be_ops, + }, + { + .name = LPASS_BE_QUAT_AUXPCM_TX, + .stream_name = "Quat AUX PCM Capture", + .cpu_dai_name = "msm-dai-q6-auxpcm.4", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_QUAT_AUXPCM_TX, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ignore_suspend = 1, + .ops = &msm_aux_pcm_be_ops, + }, + /* Quinary AUX PCM Backend DAI Links */ + { + .name = LPASS_BE_QUIN_AUXPCM_RX, + .stream_name = "Quin AUX PCM Playback", + .cpu_dai_name = "msm-dai-q6-auxpcm.5", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_QUIN_AUXPCM_RX, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + .ops = &msm_aux_pcm_be_ops, + }, + { + .name = LPASS_BE_QUIN_AUXPCM_TX, + .stream_name = "Quin AUX PCM Capture", + .cpu_dai_name = "msm-dai-q6-auxpcm.5", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_QUIN_AUXPCM_TX, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ignore_suspend = 1, + .ops = &msm_aux_pcm_be_ops, + }, +}; + +static struct snd_soc_dai_link msm_wcn_be_dai_links[] = { + { + .name = LPASS_BE_SLIMBUS_7_RX, + .stream_name = "Slimbus7 Playback", + .cpu_dai_name = "msm-dai-q6-dev.16398", + .platform_name = "msm-pcm-routing", + .codec_name = "btfmslim_slave", + /* BT codec driver determines capabilities based on + * dai name, bt codecdai name should always contains + * supported usecase information + */ + .codec_dai_name = "btfm_bt_sco_a2dp_slim_rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_7_RX, + .be_hw_params_fixup = msm_ext_be_hw_params_fixup, + .ops = &msm_wcn_ops, + /* dai link has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_7_TX, + .stream_name = "Slimbus7 Capture", + .cpu_dai_name = "msm-dai-q6-dev.16399", + .platform_name = "msm-pcm-routing", + .codec_name = "btfmslim_slave", + .codec_dai_name = "btfm_bt_sco_slim_tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_7_TX, + .be_hw_params_fixup = msm_ext_be_hw_params_fixup, + .ops = &msm_wcn_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_8_TX, + .stream_name = "Slimbus8 Capture", + .cpu_dai_name = "msm-dai-q6-dev.16401", + .platform_name = "msm-pcm-routing", + .codec_name = "btfmslim_slave", + .codec_dai_name = "btfm_fm_slim_tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_8_TX, + .be_hw_params_fixup = msm_ext_be_hw_params_fixup, + .init = &msm_wcn_init, + .ops = &msm_wcn_ops, + .ignore_suspend = 1, + }, +}; + +static struct snd_soc_dai_link ext_disp_be_dai_link[] = { + /* DISP PORT BACK END DAI Link */ + { + .name = LPASS_BE_DISPLAY_PORT, + .stream_name = "Display Port Playback", + .cpu_dai_name = "msm-dai-q6-dp.24608", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-ext-disp-audio-codec-rx", + .codec_dai_name = "msm_dp_audio_codec_rx_dai", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_DISPLAY_PORT_RX, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, +}; + +static struct snd_soc_dai_link msm_ext_tasha_dai_links[ +ARRAY_SIZE(msm_ext_common_fe_dai) + +ARRAY_SIZE(msm_ext_tasha_fe_dai) + +ARRAY_SIZE(msm_ext_common_be_dai) + +ARRAY_SIZE(msm_ext_tasha_be_dai) + +ARRAY_SIZE(msm_mi2s_be_dai_links) + +ARRAY_SIZE(msm_auxpcm_be_dai_links) + +ARRAY_SIZE(msm_wcn_be_dai_links) + +ARRAY_SIZE(ext_disp_be_dai_link)]; + +static struct snd_soc_dai_link msm_ext_tavil_dai_links[ +ARRAY_SIZE(msm_ext_common_fe_dai) + +ARRAY_SIZE(msm_ext_tavil_fe_dai) + +ARRAY_SIZE(msm_ext_common_be_dai) + +ARRAY_SIZE(msm_ext_tavil_be_dai) + +ARRAY_SIZE(msm_mi2s_be_dai_links) + +ARRAY_SIZE(msm_auxpcm_be_dai_links) + +ARRAY_SIZE(msm_wcn_be_dai_links) + +ARRAY_SIZE(ext_disp_be_dai_link)]; + +/** + * populate_snd_card_dailinks - prepares dailink array and initializes card. + * + * @dev: device handle + * + * Returns card on success or NULL on failure. + */ +struct snd_soc_card *populate_snd_card_dailinks(struct device *dev, + int snd_card_val) +{ + struct snd_soc_card *card; + struct snd_soc_dai_link *msm_ext_dai_links = NULL; + int ret, len1, len2, len3, len4; + enum codec_variant codec_ver = 0; + + if (snd_card_val == EXT_SND_CARD_TASHA) { + card = &snd_soc_card_msm_card_tasha; + } else if (snd_card_val == EXT_SND_CARD_TAVIL) { + card = &snd_soc_card_msm_card_tavil; + } else { + dev_err(dev, "%s: failing as no matching card name\n", + __func__); + return NULL; + } + + card->dev = dev; + ret = snd_soc_of_parse_card_name(card, "qcom,model"); + if (ret) { + dev_err(dev, "%s: parse card name failed, err:%d\n", + __func__, ret); + return NULL; + } + + if (strnstr(card->name, "tasha", strlen(card->name))) { + codec_ver = tasha_codec_ver(); + if (codec_ver == WCD9326) { + card->name = "sdm670-tashalite-snd-card"; + } else if (codec_ver == WCD9XXX) { + dev_err(dev, "%s: Invalid codec version %d\n", + __func__, codec_ver); + return NULL; + } + + len1 = ARRAY_SIZE(msm_ext_common_fe_dai); + len2 = len1 + ARRAY_SIZE(msm_ext_tasha_fe_dai); + len3 = len2 + ARRAY_SIZE(msm_ext_common_be_dai); + memcpy(msm_ext_tasha_dai_links, msm_ext_common_fe_dai, + sizeof(msm_ext_common_fe_dai)); + memcpy(msm_ext_tasha_dai_links + len1, + msm_ext_tasha_fe_dai, sizeof(msm_ext_tasha_fe_dai)); + memcpy(msm_ext_tasha_dai_links + len2, + msm_ext_common_be_dai, sizeof(msm_ext_common_be_dai)); + memcpy(msm_ext_tasha_dai_links + len3, + msm_ext_tasha_be_dai, sizeof(msm_ext_tasha_be_dai)); + len4 = len3 + ARRAY_SIZE(msm_ext_tasha_be_dai); + if (of_property_read_bool(dev->of_node, + "qcom,mi2s-audio-intf")) { + memcpy(msm_ext_tasha_dai_links + len4, + msm_mi2s_be_dai_links, + sizeof(msm_mi2s_be_dai_links)); + len4 += ARRAY_SIZE(msm_mi2s_be_dai_links); + } + if (of_property_read_bool(dev->of_node, + "qcom,auxpcm-audio-intf")) { + memcpy(msm_ext_tasha_dai_links + len4, + msm_auxpcm_be_dai_links, + sizeof(msm_auxpcm_be_dai_links)); + len4 += ARRAY_SIZE(msm_auxpcm_be_dai_links); + } + if (of_property_read_bool(dev->of_node, "qcom,wcn-btfm")) { + dev_dbg(dev, "%s(): WCN BTFM support present\n", + __func__); + memcpy(msm_ext_tasha_dai_links + len4, + msm_wcn_be_dai_links, + sizeof(msm_wcn_be_dai_links)); + len4 += ARRAY_SIZE(msm_wcn_be_dai_links); + } + if (of_property_read_bool(dev->of_node, + "qcom,ext-disp-audio-rx")) { + dev_dbg(dev, "%s(): ext disp audio support present\n", + __func__); + memcpy(msm_ext_tasha_dai_links + len4, + ext_disp_be_dai_link, + sizeof(ext_disp_be_dai_link)); + len4 += ARRAY_SIZE(ext_disp_be_dai_link); + } + msm_ext_dai_links = msm_ext_tasha_dai_links; + } else if (strnstr(card->name, "tavil", strlen(card->name))) { + len1 = ARRAY_SIZE(msm_ext_common_fe_dai); + len2 = len1 + ARRAY_SIZE(msm_ext_tavil_fe_dai); + len3 = len2 + ARRAY_SIZE(msm_ext_common_be_dai); + memcpy(msm_ext_tavil_dai_links, msm_ext_common_fe_dai, + sizeof(msm_ext_common_fe_dai)); + memcpy(msm_ext_tavil_dai_links + len1, + msm_ext_tavil_fe_dai, sizeof(msm_ext_tavil_fe_dai)); + memcpy(msm_ext_tavil_dai_links + len2, + msm_ext_common_be_dai, sizeof(msm_ext_common_be_dai)); + memcpy(msm_ext_tavil_dai_links + len3, + msm_ext_tavil_be_dai, sizeof(msm_ext_tavil_be_dai)); + len4 = len3 + ARRAY_SIZE(msm_ext_tavil_be_dai); + if (of_property_read_bool(dev->of_node, + "qcom,mi2s-audio-intf")) { + memcpy(msm_ext_tavil_dai_links + len4, + msm_mi2s_be_dai_links, + sizeof(msm_mi2s_be_dai_links)); + len4 += ARRAY_SIZE(msm_mi2s_be_dai_links); + } + if (of_property_read_bool(dev->of_node, + "qcom,auxpcm-audio-intf")) { + memcpy(msm_ext_tavil_dai_links + len4, + msm_auxpcm_be_dai_links, + sizeof(msm_auxpcm_be_dai_links)); + len4 += ARRAY_SIZE(msm_auxpcm_be_dai_links); + } + if (of_property_read_bool(dev->of_node, "qcom,wcn-btfm")) { + dev_dbg(dev, "%s(): WCN BTFM support present\n", + __func__); + memcpy(msm_ext_tavil_dai_links + len4, + msm_wcn_be_dai_links, + sizeof(msm_wcn_be_dai_links)); + len4 += ARRAY_SIZE(msm_wcn_be_dai_links); + } + if (of_property_read_bool(dev->of_node, + "qcom,ext-disp-audio-rx")) { + dev_dbg(dev, "%s(): ext disp audio support present\n", + __func__); + memcpy(msm_ext_tavil_dai_links + len4, + ext_disp_be_dai_link, + sizeof(ext_disp_be_dai_link)); + len4 += ARRAY_SIZE(ext_disp_be_dai_link); + } + msm_ext_dai_links = msm_ext_tavil_dai_links; + } else { + dev_err(dev, "%s: failing as no matching card name\n", + __func__); + return NULL; + } + card->dai_link = msm_ext_dai_links; + card->num_links = len4; + + return card; +} +EXPORT_SYMBOL(populate_snd_card_dailinks); diff --git a/audio-kernel/asoc/sdm660-external.c b/audio-kernel/asoc/sdm660-external.c new file mode 100644 index 000000000..8ff98edde --- /dev/null +++ b/audio-kernel/asoc/sdm660-external.c @@ -0,0 +1,1882 @@ +/* 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 + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include "msm-pcm-routing-v2.h" +#include "sdm660-common.h" +#include "sdm660-external.h" +#include "codecs/wcd9335.h" +#include "codecs/wcd934x/wcd934x.h" +#include "codecs/wcd934x/wcd934x-mbhc.h" + +#define SDM660_SPK_ON 1 +#define SDM660_SPK_OFF 0 + +#define WCD9XXX_MBHC_DEF_BUTTONS 8 +#define WCD9XXX_MBHC_DEF_RLOADS 5 +#define CODEC_EXT_CLK_RATE 9600000 +#define ADSP_STATE_READY_TIMEOUT_MS 3000 + +#define WSA8810_NAME_1 "wsa881x.20170211" +#define WSA8810_NAME_2 "wsa881x.20170212" + +static int msm_ext_spk_control = 1; +static struct wcd_mbhc_config *wcd_mbhc_cfg_ptr; + +struct msm_asoc_wcd93xx_codec { + void* (*get_afe_config_fn)(struct snd_soc_codec *codec, + enum afe_config_type config_type); + void (*mbhc_hs_detect_exit)(struct snd_soc_codec *codec); +}; + +static struct msm_asoc_wcd93xx_codec msm_codec_fn; +static struct platform_device *spdev; + +static bool is_initial_boot; + +static void *def_ext_mbhc_cal(void); + +enum { + SLIM_RX_0 = 0, + SLIM_RX_1, + SLIM_RX_2, + SLIM_RX_3, + SLIM_RX_4, + SLIM_RX_5, + SLIM_RX_6, + SLIM_RX_7, + SLIM_RX_MAX, +}; + +enum { + SLIM_TX_0 = 0, + SLIM_TX_1, + SLIM_TX_2, + SLIM_TX_3, + SLIM_TX_4, + SLIM_TX_5, + SLIM_TX_6, + SLIM_TX_7, + SLIM_TX_8, + SLIM_TX_MAX, +}; + +struct dev_config { + u32 sample_rate; + u32 bit_format; + u32 channels; +}; + +/* Default configuration of slimbus channels */ +static struct dev_config slim_rx_cfg[] = { + [SLIM_RX_0] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_RX_1] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_RX_2] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_RX_3] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_RX_4] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_RX_5] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_RX_6] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_RX_7] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, +}; + +static struct dev_config slim_tx_cfg[] = { + [SLIM_TX_0] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_TX_1] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_TX_2] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_TX_3] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_TX_4] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_TX_5] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_TX_6] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_TX_7] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_TX_8] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, +}; + +static int msm_vi_feed_tx_ch = 2; +static const char *const slim_rx_ch_text[] = {"One", "Two"}; +static const char *const slim_tx_ch_text[] = {"One", "Two", "Three", "Four", + "Five", "Six", "Seven", + "Eight"}; +static const char *const vi_feed_ch_text[] = {"One", "Two"}; +static char const *bit_format_text[] = {"S16_LE", "S24_LE", "S24_3LE", + "S32_LE"}; +static char const *slim_sample_rate_text[] = {"KHZ_8", "KHZ_16", + "KHZ_32", "KHZ_44P1", "KHZ_48", + "KHZ_88P2", "KHZ_96", "KHZ_176P4", + "KHZ_192", "KHZ_352P8", "KHZ_384"}; +static const char *const spk_function_text[] = {"Off", "On"}; +static char const *bt_sample_rate_text[] = {"KHZ_8", "KHZ_16", + "KHZ_44P1", "KHZ_48", + "KHZ_88P2", "KHZ_96"}; + +static SOC_ENUM_SINGLE_EXT_DECL(spk_func_en, spk_function_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_0_rx_chs, slim_rx_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_2_rx_chs, slim_rx_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_0_tx_chs, slim_tx_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_1_tx_chs, slim_tx_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_5_rx_chs, slim_rx_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_6_rx_chs, slim_rx_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(vi_feed_tx_chs, vi_feed_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_0_rx_format, bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_5_rx_format, bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_6_rx_format, bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_0_tx_format, bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_0_rx_sample_rate, slim_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_2_rx_sample_rate, slim_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_0_tx_sample_rate, slim_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_5_rx_sample_rate, slim_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_6_rx_sample_rate, slim_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(bt_sample_rate, bt_sample_rate_text); + +static int slim_get_sample_rate_val(int sample_rate) +{ + int sample_rate_val = 0; + + switch (sample_rate) { + case SAMPLING_RATE_8KHZ: + sample_rate_val = 0; + break; + case SAMPLING_RATE_16KHZ: + sample_rate_val = 1; + break; + case SAMPLING_RATE_32KHZ: + sample_rate_val = 2; + break; + case SAMPLING_RATE_44P1KHZ: + sample_rate_val = 3; + break; + case SAMPLING_RATE_48KHZ: + sample_rate_val = 4; + break; + case SAMPLING_RATE_88P2KHZ: + sample_rate_val = 5; + break; + case SAMPLING_RATE_96KHZ: + sample_rate_val = 6; + break; + case SAMPLING_RATE_176P4KHZ: + sample_rate_val = 7; + break; + case SAMPLING_RATE_192KHZ: + sample_rate_val = 8; + break; + case SAMPLING_RATE_352P8KHZ: + sample_rate_val = 9; + break; + case SAMPLING_RATE_384KHZ: + sample_rate_val = 10; + break; + default: + sample_rate_val = 4; + break; + } + return sample_rate_val; +} + +static int slim_get_sample_rate(int value) +{ + int sample_rate = 0; + + switch (value) { + case 0: + sample_rate = SAMPLING_RATE_8KHZ; + break; + case 1: + sample_rate = SAMPLING_RATE_16KHZ; + break; + case 2: + sample_rate = SAMPLING_RATE_32KHZ; + break; + case 3: + sample_rate = SAMPLING_RATE_44P1KHZ; + break; + case 4: + sample_rate = SAMPLING_RATE_48KHZ; + break; + case 5: + sample_rate = SAMPLING_RATE_88P2KHZ; + break; + case 6: + sample_rate = SAMPLING_RATE_96KHZ; + break; + case 7: + sample_rate = SAMPLING_RATE_176P4KHZ; + break; + case 8: + sample_rate = SAMPLING_RATE_192KHZ; + break; + case 9: + sample_rate = SAMPLING_RATE_352P8KHZ; + break; + case 10: + sample_rate = SAMPLING_RATE_384KHZ; + break; + default: + sample_rate = SAMPLING_RATE_48KHZ; + break; + } + return sample_rate; +} + +static int slim_get_bit_format_val(int bit_format) +{ + int val = 0; + + switch (bit_format) { + case SNDRV_PCM_FORMAT_S32_LE: + val = 3; + break; + case SNDRV_PCM_FORMAT_S24_3LE: + val = 2; + break; + case SNDRV_PCM_FORMAT_S24_LE: + val = 1; + break; + case SNDRV_PCM_FORMAT_S16_LE: + default: + val = 0; + break; + } + return val; +} + +static int slim_get_bit_format(int val) +{ + int bit_fmt = SNDRV_PCM_FORMAT_S16_LE; + + switch (val) { + case 0: + bit_fmt = SNDRV_PCM_FORMAT_S16_LE; + break; + case 1: + bit_fmt = SNDRV_PCM_FORMAT_S24_LE; + break; + case 2: + bit_fmt = SNDRV_PCM_FORMAT_S24_3LE; + break; + case 3: + bit_fmt = SNDRV_PCM_FORMAT_S32_LE; + break; + default: + bit_fmt = SNDRV_PCM_FORMAT_S16_LE; + break; + } + return bit_fmt; +} + +static int slim_get_port_idx(struct snd_kcontrol *kcontrol) +{ + int port_id = 0; + + if (strnstr(kcontrol->id.name, "SLIM_0_RX", sizeof("SLIM_0_RX"))) + port_id = SLIM_RX_0; + else if (strnstr(kcontrol->id.name, "SLIM_2_RX", sizeof("SLIM_2_RX"))) + port_id = SLIM_RX_2; + else if (strnstr(kcontrol->id.name, "SLIM_5_RX", sizeof("SLIM_5_RX"))) + port_id = SLIM_RX_5; + else if (strnstr(kcontrol->id.name, "SLIM_6_RX", sizeof("SLIM_6_RX"))) + port_id = SLIM_RX_6; + else if (strnstr(kcontrol->id.name, "SLIM_0_TX", sizeof("SLIM_0_TX"))) + port_id = SLIM_TX_0; + else if (strnstr(kcontrol->id.name, "SLIM_1_TX", sizeof("SLIM_1_TX"))) + port_id = SLIM_TX_1; + else { + pr_err("%s: unsupported channel: %s", + __func__, kcontrol->id.name); + return -EINVAL; + } + + return port_id; +} + +static int msm_bt_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + /* + * Slimbus_7_Rx/Tx sample rate values should always be in sync (same) + * when used for BT_SCO use case. Return either Rx or Tx sample rate + * value. + */ + switch (slim_rx_cfg[SLIM_RX_7].sample_rate) { + case SAMPLING_RATE_96KHZ: + ucontrol->value.integer.value[0] = 5; + break; + case SAMPLING_RATE_88P2KHZ: + ucontrol->value.integer.value[0] = 4; + break; + case SAMPLING_RATE_48KHZ: + ucontrol->value.integer.value[0] = 3; + break; + case SAMPLING_RATE_44P1KHZ: + ucontrol->value.integer.value[0] = 2; + break; + case SAMPLING_RATE_16KHZ: + ucontrol->value.integer.value[0] = 1; + break; + case SAMPLING_RATE_8KHZ: + default: + ucontrol->value.integer.value[0] = 0; + break; + } + pr_debug("%s: sample rate = %d", __func__, + slim_rx_cfg[SLIM_RX_7].sample_rate); + + return 0; +} + +static int msm_bt_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (ucontrol->value.integer.value[0]) { + case 1: + slim_rx_cfg[SLIM_RX_7].sample_rate = SAMPLING_RATE_16KHZ; + slim_tx_cfg[SLIM_TX_7].sample_rate = SAMPLING_RATE_16KHZ; + break; + case 2: + slim_rx_cfg[SLIM_RX_7].sample_rate = SAMPLING_RATE_44P1KHZ; + slim_tx_cfg[SLIM_TX_7].sample_rate = SAMPLING_RATE_44P1KHZ; + break; + case 3: + slim_rx_cfg[SLIM_RX_7].sample_rate = SAMPLING_RATE_48KHZ; + slim_tx_cfg[SLIM_TX_7].sample_rate = SAMPLING_RATE_48KHZ; + break; + case 4: + slim_rx_cfg[SLIM_RX_7].sample_rate = SAMPLING_RATE_88P2KHZ; + slim_tx_cfg[SLIM_TX_7].sample_rate = SAMPLING_RATE_88P2KHZ; + break; + case 5: + slim_rx_cfg[SLIM_RX_7].sample_rate = SAMPLING_RATE_96KHZ; + slim_tx_cfg[SLIM_TX_7].sample_rate = SAMPLING_RATE_96KHZ; + break; + case 0: + default: + slim_rx_cfg[SLIM_RX_7].sample_rate = SAMPLING_RATE_8KHZ; + slim_tx_cfg[SLIM_TX_7].sample_rate = SAMPLING_RATE_8KHZ; + break; + } + pr_debug("%s: sample rates: slim7_rx = %d, slim7_tx = %d, value = %d\n", + __func__, + slim_rx_cfg[SLIM_RX_7].sample_rate, + slim_tx_cfg[SLIM_TX_7].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int slim_rx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = slim_get_port_idx(kcontrol); + + if (ch_num < 0) + return ch_num; + + ucontrol->value.enumerated.item[0] = + slim_get_sample_rate_val(slim_rx_cfg[ch_num].sample_rate); + + pr_debug("%s: slim[%d]_rx_sample_rate = %d, item = %d\n", __func__, + ch_num, slim_rx_cfg[ch_num].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int slim_rx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = slim_get_port_idx(kcontrol); + + if (ch_num < 0) + return ch_num; + + slim_rx_cfg[ch_num].sample_rate = + slim_get_sample_rate(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: slim[%d]_rx_sample_rate = %d, item = %d\n", __func__, + ch_num, slim_rx_cfg[ch_num].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int slim_tx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = slim_get_port_idx(kcontrol); + + if (ch_num < 0) + return ch_num; + + ucontrol->value.enumerated.item[0] = + slim_get_sample_rate_val(slim_tx_cfg[ch_num].sample_rate); + + pr_debug("%s: slim[%d]_tx_sample_rate = %d, item = %d\n", __func__, + ch_num, slim_tx_cfg[ch_num].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int slim_tx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int sample_rate = 0; + int ch_num = slim_get_port_idx(kcontrol); + + if (ch_num < 0) + return ch_num; + + sample_rate = slim_get_sample_rate(ucontrol->value.enumerated.item[0]); + if (sample_rate == SAMPLING_RATE_44P1KHZ) { + pr_err("%s: Unsupported sample rate %d: for Tx path\n", + __func__, sample_rate); + return -EINVAL; + } + slim_tx_cfg[ch_num].sample_rate = sample_rate; + + pr_debug("%s: slim[%d]_tx_sample_rate = %d, value = %d\n", __func__, + ch_num, slim_tx_cfg[ch_num].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int slim_rx_bit_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = slim_get_port_idx(kcontrol); + + if (ch_num < 0) + return ch_num; + + ucontrol->value.enumerated.item[0] = + slim_get_bit_format_val(slim_rx_cfg[ch_num].bit_format); + + pr_debug("%s: slim[%d]_rx_bit_format = %d, ucontrol value = %d\n", + __func__, ch_num, slim_rx_cfg[ch_num].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int slim_rx_bit_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = slim_get_port_idx(kcontrol); + + if (ch_num < 0) + return ch_num; + + slim_rx_cfg[ch_num].bit_format = + slim_get_bit_format(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: slim[%d]_rx_bit_format = %d, ucontrol value = %d\n", + __func__, ch_num, slim_rx_cfg[ch_num].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int slim_tx_bit_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = slim_get_port_idx(kcontrol); + + if (ch_num < 0) + return ch_num; + + ucontrol->value.enumerated.item[0] = + slim_get_bit_format_val(slim_tx_cfg[ch_num].bit_format); + + pr_debug("%s: slim[%d]_tx_bit_format = %d, ucontrol value = %d\n", + __func__, ch_num, slim_tx_cfg[ch_num].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int slim_tx_bit_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = slim_get_port_idx(kcontrol); + + if (ch_num < 0) + return ch_num; + + slim_tx_cfg[ch_num].bit_format = + slim_get_bit_format(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: slim[%d]_tx_bit_format = %d, ucontrol value = %d\n", + __func__, ch_num, slim_tx_cfg[ch_num].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int msm_slim_rx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = slim_get_port_idx(kcontrol); + + if (ch_num < 0) + return ch_num; + + pr_debug("%s: msm_slim_[%d]_rx_ch = %d\n", __func__, + ch_num, slim_rx_cfg[ch_num].channels); + ucontrol->value.enumerated.item[0] = slim_rx_cfg[ch_num].channels - 1; + + return 0; +} + +static int msm_slim_rx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = slim_get_port_idx(kcontrol); + + if (ch_num < 0) + return ch_num; + + slim_rx_cfg[ch_num].channels = ucontrol->value.enumerated.item[0] + 1; + pr_debug("%s: msm_slim_[%d]_rx_ch = %d\n", __func__, + ch_num, slim_rx_cfg[ch_num].channels); + + return 1; +} + +static int msm_slim_tx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = slim_get_port_idx(kcontrol); + + if (ch_num < 0) + return ch_num; + + pr_debug("%s: msm_slim_[%d]_tx_ch = %d\n", __func__, + ch_num, slim_tx_cfg[ch_num].channels); + ucontrol->value.enumerated.item[0] = slim_tx_cfg[ch_num].channels - 1; + + return 0; +} + +static int msm_slim_tx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = slim_get_port_idx(kcontrol); + + if (ch_num < 0) + return ch_num; + + slim_tx_cfg[ch_num].channels = ucontrol->value.enumerated.item[0] + 1; + pr_debug("%s: msm_slim_[%d]_tx_ch = %d\n", __func__, + ch_num, slim_tx_cfg[ch_num].channels); + + return 1; +} + +static int msm_vi_feed_tx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = msm_vi_feed_tx_ch - 1; + pr_debug("%s: msm_vi_feed_tx_ch = %ld\n", __func__, + ucontrol->value.integer.value[0]); + return 0; +} + +static int msm_vi_feed_tx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + msm_vi_feed_tx_ch = ucontrol->value.integer.value[0] + 1; + + pr_debug("%s: msm_vi_feed_tx_ch = %d\n", __func__, msm_vi_feed_tx_ch); + return 1; +} + +static void *def_ext_mbhc_cal(void) +{ + void *wcd_mbhc_cal; + struct wcd_mbhc_btn_detect_cfg *btn_cfg; + u16 *btn_high; + + wcd_mbhc_cal = kzalloc(WCD_MBHC_CAL_SIZE(WCD_MBHC_DEF_BUTTONS, + WCD9XXX_MBHC_DEF_RLOADS), GFP_KERNEL); + if (!wcd_mbhc_cal) + return NULL; + +#define S(X, Y) ((WCD_MBHC_CAL_PLUG_TYPE_PTR(wcd_mbhc_cal)->X) = (Y)) + S(v_hs_max, 1600); +#undef S +#define S(X, Y) ((WCD_MBHC_CAL_BTN_DET_PTR(wcd_mbhc_cal)->X) = (Y)) + S(num_btn, WCD_MBHC_DEF_BUTTONS); +#undef S + + btn_cfg = WCD_MBHC_CAL_BTN_DET_PTR(wcd_mbhc_cal); + btn_high = ((void *)&btn_cfg->_v_btn_low) + + (sizeof(btn_cfg->_v_btn_low[0]) * btn_cfg->num_btn); + + btn_high[0] = 75; + btn_high[1] = 150; + btn_high[2] = 237; + btn_high[3] = 500; + btn_high[4] = 500; + btn_high[5] = 500; + btn_high[6] = 500; + btn_high[7] = 500; + + return wcd_mbhc_cal; +} + +static inline int param_is_mask(int p) +{ + return (p >= SNDRV_PCM_HW_PARAM_FIRST_MASK) && + (p <= SNDRV_PCM_HW_PARAM_LAST_MASK); +} + +static inline struct snd_mask *param_to_mask(struct snd_pcm_hw_params *p, int n) +{ + return &(p->masks[n - SNDRV_PCM_HW_PARAM_FIRST_MASK]); +} + + +static void msm_ext_control(struct snd_soc_codec *codec) +{ + struct snd_soc_dapm_context *dapm = + snd_soc_codec_get_dapm(codec); + + pr_debug("%s: msm_ext_spk_control = %d", __func__, msm_ext_spk_control); + if (msm_ext_spk_control == SDM660_SPK_ON) { + snd_soc_dapm_enable_pin(dapm, "Lineout_1 amp"); + snd_soc_dapm_enable_pin(dapm, "Lineout_3 amp"); + } else { + snd_soc_dapm_disable_pin(dapm, "Lineout_1 amp"); + snd_soc_dapm_disable_pin(dapm, "Lineout_3 amp"); + } + snd_soc_dapm_sync(dapm); +} + +static int msm_ext_get_spk(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s: msm_ext_spk_control = %d\n", + __func__, msm_ext_spk_control); + ucontrol->value.integer.value[0] = msm_ext_spk_control; + return 0; +} + +static int msm_ext_set_spk(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + + pr_debug("%s()\n", __func__); + if (msm_ext_spk_control == ucontrol->value.integer.value[0]) + return 0; + + msm_ext_spk_control = ucontrol->value.integer.value[0]; + msm_ext_control(codec); + return 1; +} + + +int msm_ext_enable_codec_mclk(struct snd_soc_codec *codec, int enable, + bool dapm) +{ + int ret; + + pr_debug("%s: enable = %d\n", __func__, enable); + + if (!strcmp(dev_name(codec->dev), "tasha_codec")) + ret = tasha_cdc_mclk_enable(codec, enable, dapm); + else if (!strcmp(dev_name(codec->dev), "tavil_codec")) + ret = tavil_cdc_mclk_enable(codec, enable); + else { + dev_err(codec->dev, "%s: unknown codec to enable ext clk\n", + __func__); + ret = -EINVAL; + } + return ret; +} + +static const struct snd_kcontrol_new msm_snd_controls[] = { + SOC_ENUM_EXT("Speaker Function", spk_func_en, msm_ext_get_spk, + msm_ext_set_spk), + SOC_ENUM_EXT("SLIM_0_RX Channels", slim_0_rx_chs, + msm_slim_rx_ch_get, msm_slim_rx_ch_put), + SOC_ENUM_EXT("SLIM_2_RX Channels", slim_2_rx_chs, + msm_slim_rx_ch_get, msm_slim_rx_ch_put), + SOC_ENUM_EXT("SLIM_0_TX Channels", slim_0_tx_chs, + msm_slim_tx_ch_get, msm_slim_tx_ch_put), + SOC_ENUM_EXT("SLIM_1_TX Channels", slim_1_tx_chs, + msm_slim_tx_ch_get, msm_slim_tx_ch_put), + SOC_ENUM_EXT("SLIM_5_RX Channels", slim_5_rx_chs, + msm_slim_rx_ch_get, msm_slim_rx_ch_put), + SOC_ENUM_EXT("SLIM_6_RX Channels", slim_6_rx_chs, + msm_slim_rx_ch_get, msm_slim_rx_ch_put), + SOC_ENUM_EXT("VI_FEED_TX Channels", vi_feed_tx_chs, + msm_vi_feed_tx_ch_get, msm_vi_feed_tx_ch_put), + SOC_ENUM_EXT("SLIM_0_RX Format", slim_0_rx_format, + slim_rx_bit_format_get, slim_rx_bit_format_put), + SOC_ENUM_EXT("SLIM_5_RX Format", slim_5_rx_format, + slim_rx_bit_format_get, slim_rx_bit_format_put), + SOC_ENUM_EXT("SLIM_6_RX Format", slim_6_rx_format, + slim_rx_bit_format_get, slim_rx_bit_format_put), + SOC_ENUM_EXT("SLIM_0_TX Format", slim_0_tx_format, + slim_tx_bit_format_get, slim_tx_bit_format_put), + SOC_ENUM_EXT("SLIM_0_RX SampleRate", slim_0_rx_sample_rate, + slim_rx_sample_rate_get, slim_rx_sample_rate_put), + SOC_ENUM_EXT("SLIM_2_RX SampleRate", slim_2_rx_sample_rate, + slim_rx_sample_rate_get, slim_rx_sample_rate_put), + SOC_ENUM_EXT("SLIM_0_TX SampleRate", slim_0_tx_sample_rate, + slim_tx_sample_rate_get, slim_tx_sample_rate_put), + SOC_ENUM_EXT("SLIM_5_RX SampleRate", slim_5_rx_sample_rate, + slim_rx_sample_rate_get, slim_rx_sample_rate_put), + SOC_ENUM_EXT("SLIM_6_RX SampleRate", slim_6_rx_sample_rate, + slim_rx_sample_rate_get, slim_rx_sample_rate_put), + SOC_ENUM_EXT("BT SampleRate", bt_sample_rate, + msm_bt_sample_rate_get, + msm_bt_sample_rate_put), +}; + +static int msm_slim_get_ch_from_beid(int32_t id) +{ + int ch_id = 0; + + switch (id) { + case MSM_BACKEND_DAI_SLIMBUS_0_RX: + ch_id = SLIM_RX_0; + break; + case MSM_BACKEND_DAI_SLIMBUS_1_RX: + ch_id = SLIM_RX_1; + break; + case MSM_BACKEND_DAI_SLIMBUS_2_RX: + ch_id = SLIM_RX_2; + break; + case MSM_BACKEND_DAI_SLIMBUS_3_RX: + ch_id = SLIM_RX_3; + break; + case MSM_BACKEND_DAI_SLIMBUS_4_RX: + ch_id = SLIM_RX_4; + break; + case MSM_BACKEND_DAI_SLIMBUS_6_RX: + ch_id = SLIM_RX_6; + break; + case MSM_BACKEND_DAI_SLIMBUS_0_TX: + ch_id = SLIM_TX_0; + break; + case MSM_BACKEND_DAI_SLIMBUS_3_TX: + ch_id = SLIM_TX_3; + break; + default: + ch_id = SLIM_RX_0; + break; + } + + return ch_id; +} + +static void param_set_mask(struct snd_pcm_hw_params *p, int n, unsigned int bit) +{ + if (bit >= SNDRV_MASK_MAX) + return; + if (param_is_mask(n)) { + struct snd_mask *m = param_to_mask(p, n); + + m->bits[0] = 0; + m->bits[1] = 0; + m->bits[bit >> 5] |= (1 << (bit & 31)); + } +} + +/** + * msm_ext_be_hw_params_fixup - updates settings of ALSA BE hw params. + * + * @rtd: runtime dailink instance + * @params: HW params of associated backend dailink. + * + * Returns 0 on success or rc on failure. + */ +int msm_ext_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_dai_link *dai_link = rtd->dai_link; + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + int rc = 0; + int idx; + void *config = NULL; + struct snd_soc_codec *codec = rtd->codec; + + pr_debug("%s: format = %d, rate = %d\n", + __func__, params_format(params), params_rate(params)); + + switch (dai_link->id) { + case MSM_BACKEND_DAI_SLIMBUS_0_RX: + case MSM_BACKEND_DAI_SLIMBUS_1_RX: + case MSM_BACKEND_DAI_SLIMBUS_2_RX: + case MSM_BACKEND_DAI_SLIMBUS_3_RX: + case MSM_BACKEND_DAI_SLIMBUS_4_RX: + case MSM_BACKEND_DAI_SLIMBUS_6_RX: + idx = msm_slim_get_ch_from_beid(dai_link->id); + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + slim_rx_cfg[idx].bit_format); + rate->min = rate->max = slim_rx_cfg[idx].sample_rate; + channels->min = channels->max = slim_rx_cfg[idx].channels; + break; + + case MSM_BACKEND_DAI_SLIMBUS_0_TX: + case MSM_BACKEND_DAI_SLIMBUS_3_TX: + idx = msm_slim_get_ch_from_beid(dai_link->id); + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + slim_tx_cfg[idx].bit_format); + rate->min = rate->max = slim_tx_cfg[idx].sample_rate; + channels->min = channels->max = slim_tx_cfg[idx].channels; + break; + + case MSM_BACKEND_DAI_SLIMBUS_1_TX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + slim_tx_cfg[1].bit_format); + rate->min = rate->max = slim_tx_cfg[1].sample_rate; + channels->min = channels->max = slim_tx_cfg[1].channels; + break; + + case MSM_BACKEND_DAI_SLIMBUS_4_TX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + SNDRV_PCM_FORMAT_S32_LE); + rate->min = rate->max = SAMPLING_RATE_8KHZ; + channels->min = channels->max = msm_vi_feed_tx_ch; + break; + + case MSM_BACKEND_DAI_SLIMBUS_5_RX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + slim_rx_cfg[5].bit_format); + rate->min = rate->max = slim_rx_cfg[5].sample_rate; + channels->min = channels->max = slim_rx_cfg[5].channels; + break; + + case MSM_BACKEND_DAI_SLIMBUS_5_TX: + rate->min = rate->max = SAMPLING_RATE_16KHZ; + channels->min = channels->max = 1; + + config = msm_codec_fn.get_afe_config_fn(codec, + AFE_SLIMBUS_SLAVE_PORT_CONFIG); + if (config) { + rc = afe_set_config(AFE_SLIMBUS_SLAVE_PORT_CONFIG, + config, SLIMBUS_5_TX); + if (rc) + pr_err("%s: Failed to set slimbus slave port config %d\n", + __func__, rc); + } + break; + + case MSM_BACKEND_DAI_SLIMBUS_7_RX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + slim_rx_cfg[SLIM_RX_7].bit_format); + rate->min = rate->max = slim_rx_cfg[SLIM_RX_7].sample_rate; + channels->min = channels->max = + slim_rx_cfg[SLIM_RX_7].channels; + break; + + case MSM_BACKEND_DAI_SLIMBUS_7_TX: + rate->min = rate->max = slim_tx_cfg[SLIM_TX_7].sample_rate; + channels->min = channels->max = + slim_tx_cfg[SLIM_TX_7].channels; + break; + + case MSM_BACKEND_DAI_SLIMBUS_8_TX: + rate->min = rate->max = slim_tx_cfg[SLIM_TX_8].sample_rate; + channels->min = channels->max = + slim_tx_cfg[SLIM_TX_8].channels; + break; + + default: + rate->min = rate->max = SAMPLING_RATE_48KHZ; + break; + } + return rc; +} +EXPORT_SYMBOL(msm_ext_be_hw_params_fixup); + +/** + * msm_snd_hw_params - hw params ops of backend dailink. + * + * @substream: PCM stream of associated backend dailink. + * @params: HW params of associated backend dailink. + * + * Returns 0 on success or ret on failure. + */ +int msm_snd_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai_link *dai_link = rtd->dai_link; + + int ret = 0; + u32 rx_ch[SLIM_MAX_RX_PORTS], tx_ch[SLIM_MAX_TX_PORTS]; + u32 rx_ch_cnt = 0, tx_ch_cnt = 0; + u32 user_set_tx_ch = 0; + u32 rx_ch_count; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + ret = snd_soc_dai_get_channel_map(codec_dai, + &tx_ch_cnt, tx_ch, &rx_ch_cnt, rx_ch); + if (ret < 0) { + pr_err("%s: failed to get codec chan map, err:%d\n", + __func__, ret); + goto err_ch_map; + } + if (dai_link->id == MSM_BACKEND_DAI_SLIMBUS_5_RX) { + pr_debug("%s: rx_5_ch=%d\n", __func__, + slim_rx_cfg[5].channels); + rx_ch_count = slim_rx_cfg[5].channels; + } else if (dai_link->id == MSM_BACKEND_DAI_SLIMBUS_2_RX) { + pr_debug("%s: rx_2_ch=%d\n", __func__, + slim_rx_cfg[2].channels); + rx_ch_count = slim_rx_cfg[2].channels; + } else if (dai_link->id == MSM_BACKEND_DAI_SLIMBUS_6_RX) { + pr_debug("%s: rx_6_ch=%d\n", __func__, + slim_rx_cfg[6].channels); + rx_ch_count = slim_rx_cfg[6].channels; + } else { + pr_debug("%s: rx_0_ch=%d\n", __func__, + slim_rx_cfg[0].channels); + rx_ch_count = slim_rx_cfg[0].channels; + } + ret = snd_soc_dai_set_channel_map(cpu_dai, 0, 0, + rx_ch_count, rx_ch); + if (ret < 0) { + pr_err("%s: failed to set cpu chan map, err:%d\n", + __func__, ret); + goto err_ch_map; + } + } else { + pr_debug("%s: %s_tx_dai_id_%d_ch=%d\n", __func__, + codec_dai->name, codec_dai->id, user_set_tx_ch); + ret = snd_soc_dai_get_channel_map(codec_dai, + &tx_ch_cnt, tx_ch, &rx_ch_cnt, rx_ch); + if (ret < 0) { + pr_err("%s: failed to get codec chan map\n, err:%d\n", + __func__, ret); + goto err_ch_map; + } + /* For _tx1 case */ + if (dai_link->id == MSM_BACKEND_DAI_SLIMBUS_0_TX) + user_set_tx_ch = slim_tx_cfg[0].channels; + /* For _tx3 case */ + else if (dai_link->id == MSM_BACKEND_DAI_SLIMBUS_1_TX) + user_set_tx_ch = slim_tx_cfg[1].channels; + else if (dai_link->id == MSM_BACKEND_DAI_SLIMBUS_4_TX) + user_set_tx_ch = msm_vi_feed_tx_ch; + else + user_set_tx_ch = tx_ch_cnt; + + pr_debug("%s: msm_slim_0_tx_ch(%d) user_set_tx_ch(%d) tx_ch_cnt(%d), id (%d)\n", + __func__, slim_tx_cfg[0].channels, user_set_tx_ch, + tx_ch_cnt, dai_link->id); + + ret = snd_soc_dai_set_channel_map(cpu_dai, + user_set_tx_ch, tx_ch, 0, 0); + if (ret < 0) + pr_err("%s: failed to set cpu chan map, err:%d\n", + __func__, ret); + } + +err_ch_map: + return ret; +} +EXPORT_SYMBOL(msm_snd_hw_params); + +/** + * msm_ext_slimbus_2_hw_params - hw params ops of slimbus_2 BE. + * + * @substream: PCM stream of associated backend dailink. + * @params: HW params of associated backend dailink. + * + * Returns 0 on success or ret on failure. + */ +int msm_ext_slimbus_2_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + int ret = 0; + unsigned int rx_ch[SLIM_MAX_RX_PORTS], tx_ch[SLIM_MAX_TX_PORTS]; + unsigned int rx_ch_cnt = 0, tx_ch_cnt = 0; + unsigned int num_tx_ch = 0; + unsigned int num_rx_ch = 0; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + num_rx_ch = params_channels(params); + pr_debug("%s: %s rx_dai_id = %d num_ch = %d\n", __func__, + codec_dai->name, codec_dai->id, num_rx_ch); + ret = snd_soc_dai_get_channel_map(codec_dai, + &tx_ch_cnt, tx_ch, &rx_ch_cnt, rx_ch); + if (ret < 0) { + pr_err("%s: failed to get codec chan map, err:%d\n", + __func__, ret); + goto end; + } + ret = snd_soc_dai_set_channel_map(cpu_dai, 0, 0, + num_rx_ch, rx_ch); + if (ret < 0) { + pr_err("%s: failed to set cpu chan map, err:%d\n", + __func__, ret); + goto end; + } + } else { + num_tx_ch = params_channels(params); + pr_debug("%s: %s tx_dai_id = %d num_ch = %d\n", __func__, + codec_dai->name, codec_dai->id, num_tx_ch); + ret = snd_soc_dai_get_channel_map(codec_dai, + &tx_ch_cnt, tx_ch, &rx_ch_cnt, rx_ch); + if (ret < 0) { + pr_err("%s: failed to get codec chan map, err:%d\n", + __func__, ret); + goto end; + } + ret = snd_soc_dai_set_channel_map(cpu_dai, + num_tx_ch, tx_ch, 0, 0); + if (ret < 0) { + pr_err("%s: failed to set cpu chan map, err:%d\n", + __func__, ret); + goto end; + } + } +end: + return ret; +} +EXPORT_SYMBOL(msm_ext_slimbus_2_hw_params); + +/** + * msm_snd_cpe_hw_params - hw params ops of CPE backend. + * + * @substream: PCM stream of associated backend dailink. + * @params: HW params of associated backend dailink. + * + * Returns 0 on success or ret on failure. + */ +int msm_snd_cpe_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai_link *dai_link = rtd->dai_link; + int ret = 0; + u32 tx_ch[SLIM_MAX_TX_PORTS]; + u32 tx_ch_cnt = 0; + + if (substream->stream != SNDRV_PCM_STREAM_CAPTURE) { + pr_err("%s: Invalid stream type %d\n", + __func__, substream->stream); + ret = -EINVAL; + goto end; + } + + pr_debug("%s: %s_tx_dai_id_%d\n", __func__, + codec_dai->name, codec_dai->id); + ret = snd_soc_dai_get_channel_map(codec_dai, + &tx_ch_cnt, tx_ch, NULL, NULL); + if (ret < 0) { + pr_err("%s: failed to get codec chan map\n, err:%d\n", + __func__, ret); + goto end; + } + + pr_debug("%s: tx_ch_cnt(%d) id %d\n", + __func__, tx_ch_cnt, dai_link->id); + + ret = snd_soc_dai_set_channel_map(cpu_dai, + tx_ch_cnt, tx_ch, 0, 0); + if (ret < 0) { + pr_err("%s: failed to set cpu chan map, err:%d\n", + __func__, ret); + goto end; + } +end: + return ret; +} +EXPORT_SYMBOL(msm_snd_cpe_hw_params); + +static int msm_afe_set_config(struct snd_soc_codec *codec) +{ + int rc; + void *config_data; + + pr_debug("%s: enter\n", __func__); + + if (!msm_codec_fn.get_afe_config_fn) { + dev_err(codec->dev, "%s: codec get afe config not init'ed\n", + __func__); + return -EINVAL; + } + config_data = msm_codec_fn.get_afe_config_fn(codec, + AFE_CDC_REGISTERS_CONFIG); + if (config_data) { + rc = afe_set_config(AFE_CDC_REGISTERS_CONFIG, config_data, 0); + if (rc) { + pr_err("%s: Failed to set codec registers config %d\n", + __func__, rc); + return rc; + } + } + + config_data = msm_codec_fn.get_afe_config_fn(codec, + AFE_CDC_REGISTER_PAGE_CONFIG); + if (config_data) { + rc = afe_set_config(AFE_CDC_REGISTER_PAGE_CONFIG, config_data, + 0); + if (rc) + pr_err("%s: Failed to set cdc register page config\n", + __func__); + } + + config_data = msm_codec_fn.get_afe_config_fn(codec, + AFE_SLIMBUS_SLAVE_CONFIG); + if (config_data) { + rc = afe_set_config(AFE_SLIMBUS_SLAVE_CONFIG, config_data, 0); + if (rc) { + pr_err("%s: Failed to set slimbus slave config %d\n", + __func__, rc); + return rc; + } + } + + config_data = msm_codec_fn.get_afe_config_fn(codec, + AFE_AANC_VERSION); + if (config_data) { + rc = afe_set_config(AFE_AANC_VERSION, config_data, 0); + if (rc) { + pr_err("%s: Failed to set AANC version %d\n", + __func__, rc); + return rc; + } + } + + config_data = msm_codec_fn.get_afe_config_fn(codec, + AFE_CDC_CLIP_REGISTERS_CONFIG); + if (config_data) { + rc = afe_set_config(AFE_CDC_CLIP_REGISTERS_CONFIG, + config_data, 0); + if (rc) { + pr_err("%s: Failed to set clip registers %d\n", + __func__, rc); + return rc; + } + } + + config_data = msm_codec_fn.get_afe_config_fn(codec, + AFE_CLIP_BANK_SEL); + if (config_data) { + rc = afe_set_config(AFE_CLIP_BANK_SEL, + config_data, 0); + if (rc) { + pr_err("%s: Failed to set AFE bank selection %d\n", + __func__, rc); + return rc; + } + } + + config_data = msm_codec_fn.get_afe_config_fn(codec, + AFE_CDC_REGISTER_PAGE_CONFIG); + if (config_data) { + rc = afe_set_config(AFE_CDC_REGISTER_PAGE_CONFIG, config_data, + 0); + if (rc) + pr_err("%s: Failed to set cdc register page config\n", + __func__); + } + + return 0; +} + +static void msm_afe_clear_config(void) +{ + afe_clear_config(AFE_CDC_REGISTERS_CONFIG); + afe_clear_config(AFE_SLIMBUS_SLAVE_CONFIG); +} + +static int msm_adsp_power_up_config(struct snd_soc_codec *codec, + struct snd_card *card) +{ + int ret = 0; + unsigned long timeout; + int adsp_ready = 0; + bool snd_card_online = 0; + + timeout = jiffies + + msecs_to_jiffies(ADSP_STATE_READY_TIMEOUT_MS); + + do { + if (!snd_card_online) { + snd_card_online = snd_card_is_online_state(card); + pr_debug("%s: Sound card is %s\n", __func__, + snd_card_online ? "Online" : "Offline"); + } + if (!adsp_ready) { + adsp_ready = q6core_is_adsp_ready(); + pr_debug("%s: ADSP Audio is %s\n", __func__, + adsp_ready ? "ready" : "not ready"); + } + if (snd_card_online && adsp_ready) + break; + + /* + * Sound card/ADSP will be coming up after subsystem restart and + * it might not be fully up when the control reaches + * here. So, wait for 50msec before checking ADSP state + */ + msleep(50); + } while (time_after(timeout, jiffies)); + + if (!snd_card_online || !adsp_ready) { + pr_err("%s: Timeout. Sound card is %s, ADSP Audio is %s\n", + __func__, + snd_card_online ? "Online" : "Offline", + adsp_ready ? "ready" : "not ready"); + ret = -ETIMEDOUT; + goto err_fail; + } + + ret = msm_afe_set_config(codec); + if (ret) + pr_err("%s: Failed to set AFE config. err %d\n", + __func__, ret); + + return 0; + +err_fail: + return ret; +} + +static int sdm660_notifier_service_cb(struct notifier_block *this, + unsigned long opcode, void *ptr) +{ + int ret; + struct snd_soc_card *card = NULL; + const char *be_dl_name = LPASS_BE_SLIMBUS_0_RX; + struct snd_soc_pcm_runtime *rtd; + struct snd_soc_codec *codec; + + pr_debug("%s: Service opcode 0x%lx\n", __func__, opcode); + + switch (opcode) { + case AUDIO_NOTIFIER_SERVICE_DOWN: + /* + * Use flag to ignore initial boot notifications + * On initial boot msm_adsp_power_up_config is + * called on init. There is no need to clear + * and set the config again on initial boot. + */ + if (is_initial_boot) + break; + msm_afe_clear_config(); + break; + case AUDIO_NOTIFIER_SERVICE_UP: + if (is_initial_boot) { + is_initial_boot = false; + break; + } + if (!spdev) + return -EINVAL; + + card = platform_get_drvdata(spdev); + rtd = snd_soc_get_pcm_runtime(card, be_dl_name); + if (!rtd) { + dev_err(card->dev, + "%s: snd_soc_get_pcm_runtime for %s failed!\n", + __func__, be_dl_name); + ret = -EINVAL; + goto done; + } + codec = rtd->codec; + + ret = msm_adsp_power_up_config(codec, card->snd_card); + if (ret < 0) { + dev_err(card->dev, + "%s: msm_adsp_power_up_config failed ret = %d!\n", + __func__, ret); + goto done; + } + break; + default: + break; + } +done: + return NOTIFY_OK; +} + +static struct notifier_block service_nb = { + .notifier_call = sdm660_notifier_service_cb, + .priority = -INT_MAX, +}; + +static int msm_config_hph_en0_gpio(struct snd_soc_codec *codec, bool high) +{ + struct snd_soc_card *card = codec->component.card; + struct msm_asoc_mach_data *pdata; + int val; + + if (!card) + return 0; + + pdata = snd_soc_card_get_drvdata(card); + if (!pdata || !gpio_is_valid(pdata->hph_en0_gpio)) + return 0; + + val = gpio_get_value_cansleep(pdata->hph_en0_gpio); + if ((!!val) == high) + return 0; + + gpio_direction_output(pdata->hph_en0_gpio, (int)high); + + return 1; +} + +static int msm_snd_enable_codec_ext_tx_clk(struct snd_soc_codec *codec, + int enable, bool dapm) +{ + int ret = 0; + + if (!strcmp(dev_name(codec->dev), "tasha_codec")) + ret = tasha_cdc_mclk_tx_enable(codec, enable, dapm); + else { + dev_err(codec->dev, "%s: unknown codec to enable ext clk\n", + __func__); + ret = -EINVAL; + } + return ret; +} + +static int msm_ext_mclk_tx_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + + pr_debug("%s: event = %d\n", __func__, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + return msm_snd_enable_codec_ext_tx_clk(codec, 1, true); + case SND_SOC_DAPM_POST_PMD: + return msm_snd_enable_codec_ext_tx_clk(codec, 0, true); + } + return 0; +} + +static int msm_ext_mclk_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + + pr_debug("%s: event = %d\n", __func__, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + return msm_ext_enable_codec_mclk(codec, 1, true); + case SND_SOC_DAPM_POST_PMD: + return msm_ext_enable_codec_mclk(codec, 0, true); + } + return 0; +} + +static int msm_ext_prepare_hifi(struct msm_asoc_mach_data *pdata) +{ + int ret = 0; + + if (gpio_is_valid(pdata->hph_en1_gpio)) { + pr_debug("%s: hph_en1_gpio request %d\n", __func__, + pdata->hph_en1_gpio); + ret = gpio_request(pdata->hph_en1_gpio, "hph_en1_gpio"); + if (ret) { + pr_err("%s: hph_en1_gpio request failed, ret:%d\n", + __func__, ret); + goto err; + } + } + if (gpio_is_valid(pdata->hph_en0_gpio)) { + pr_debug("%s: hph_en0_gpio request %d\n", __func__, + pdata->hph_en0_gpio); + ret = gpio_request(pdata->hph_en0_gpio, "hph_en0_gpio"); + if (ret) + pr_err("%s: hph_en0_gpio request failed, ret:%d\n", + __func__, ret); + } + +err: + return ret; +} + +static const struct snd_soc_dapm_widget msm_dapm_widgets[] = { + + SND_SOC_DAPM_SUPPLY_S("MCLK", -1, SND_SOC_NOPM, 0, 0, + msm_ext_mclk_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_SUPPLY_S("MCLK TX", -1, SND_SOC_NOPM, 0, 0, + msm_ext_mclk_tx_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_SPK("Lineout_1 amp", NULL), + SND_SOC_DAPM_SPK("Lineout_3 amp", NULL), + SND_SOC_DAPM_SPK("Lineout_2 amp", NULL), + SND_SOC_DAPM_SPK("Lineout_4 amp", NULL), + SND_SOC_DAPM_MIC("Handset Mic", NULL), + SND_SOC_DAPM_MIC("Headset Mic", NULL), + SND_SOC_DAPM_MIC("Secondary Mic", NULL), + SND_SOC_DAPM_MIC("ANCRight Headset Mic", NULL), + SND_SOC_DAPM_MIC("ANCLeft Headset Mic", NULL), + SND_SOC_DAPM_MIC("Analog Mic4", NULL), + SND_SOC_DAPM_MIC("Analog Mic6", NULL), + SND_SOC_DAPM_MIC("Analog Mic7", NULL), + SND_SOC_DAPM_MIC("Analog Mic8", NULL), + + SND_SOC_DAPM_MIC("Digital Mic0", NULL), + SND_SOC_DAPM_MIC("Digital Mic1", NULL), + SND_SOC_DAPM_MIC("Digital Mic2", NULL), + SND_SOC_DAPM_MIC("Digital Mic3", NULL), + SND_SOC_DAPM_MIC("Digital Mic4", NULL), + SND_SOC_DAPM_MIC("Digital Mic5", NULL), + SND_SOC_DAPM_MIC("Digital Mic6", NULL), +}; + +static struct snd_soc_dapm_route wcd_audio_paths_tasha[] = { + {"MIC BIAS1", NULL, "MCLK TX"}, + {"MIC BIAS2", NULL, "MCLK TX"}, + {"MIC BIAS3", NULL, "MCLK TX"}, + {"MIC BIAS4", NULL, "MCLK TX"}, +}; + +static struct snd_soc_dapm_route wcd_audio_paths[] = { + {"MIC BIAS1", NULL, "MCLK"}, + {"MIC BIAS2", NULL, "MCLK"}, + {"MIC BIAS3", NULL, "MCLK"}, + {"MIC BIAS4", NULL, "MCLK"}, +}; + +int msm_snd_card_tasha_late_probe(struct snd_soc_card *card) +{ + const char *be_dl_name = LPASS_BE_SLIMBUS_0_RX; + struct snd_soc_pcm_runtime *rtd; + int ret = 0; + void *mbhc_calibration; + + rtd = snd_soc_get_pcm_runtime(card, be_dl_name); + if (!rtd) { + dev_err(card->dev, + "%s: snd_soc_get_pcm_runtime for %s failed!\n", + __func__, be_dl_name); + ret = -EINVAL; + goto err_pcm_runtime; + } + + mbhc_calibration = def_ext_mbhc_cal(); + if (!mbhc_calibration) { + ret = -ENOMEM; + goto err_mbhc_cal; + } + wcd_mbhc_cfg_ptr->calibration = mbhc_calibration; + ret = tasha_mbhc_hs_detect(rtd->codec, wcd_mbhc_cfg_ptr); + if (ret) { + dev_err(card->dev, "%s: mbhc hs detect failed, err:%d\n", + __func__, ret); + goto err_hs_detect; + } + return 0; + +err_hs_detect: + kfree(mbhc_calibration); +err_mbhc_cal: +err_pcm_runtime: + return ret; +} + +int msm_snd_card_tavil_late_probe(struct snd_soc_card *card) +{ + const char *be_dl_name = LPASS_BE_SLIMBUS_0_RX; + struct snd_soc_pcm_runtime *rtd; + int ret = 0; + void *mbhc_calibration; + + rtd = snd_soc_get_pcm_runtime(card, be_dl_name); + if (!rtd) { + dev_err(card->dev, + "%s: snd_soc_get_pcm_runtime for %s failed!\n", + __func__, be_dl_name); + ret = -EINVAL; + goto err; + } + + mbhc_calibration = def_ext_mbhc_cal(); + if (!mbhc_calibration) { + ret = -ENOMEM; + goto err; + } + wcd_mbhc_cfg_ptr->calibration = mbhc_calibration; + ret = tavil_mbhc_hs_detect(rtd->codec, wcd_mbhc_cfg_ptr); + if (ret) { + dev_err(card->dev, "%s: mbhc hs detect failed, err:%d\n", + __func__, ret); + goto err_free_mbhc_cal; + } + return 0; + +err_free_mbhc_cal: + kfree(mbhc_calibration); +err: + return ret; +} + +/** + * msm_audrx_init - Audio init function of sound card instantiate. + * + * @rtd: runtime dailink instance + * + * Returns 0 on success or ret on failure. + */ +int msm_audrx_init(struct snd_soc_pcm_runtime *rtd) +{ + int ret; + void *config_data; + struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_dapm_context *dapm = + snd_soc_codec_get_dapm(codec); + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_component *aux_comp; + struct snd_card *card; + struct snd_info_entry *entry; + struct msm_asoc_mach_data *pdata = + snd_soc_card_get_drvdata(rtd->card); + + /* Codec SLIMBUS configuration + * RX1, RX2, RX3, RX4, RX5, RX6, RX7, RX8, RX9, RX10, RX11, RX12, RX13 + * TX1, TX2, TX3, TX4, TX5, TX6, TX7, TX8, TX9, TX10, TX11, TX12, TX13 + * TX14, TX15, TX16 + */ + unsigned int rx_ch[TASHA_RX_MAX] = {144, 145, 146, 147, 148, 149, 150, + 151, 152, 153, 154, 155, 156}; + unsigned int tx_ch[TASHA_TX_MAX] = {128, 129, 130, 131, 132, 133, + 134, 135, 136, 137, 138, 139, + 140, 141, 142, 143}; + + /* Tavil Codec SLIMBUS configuration + * RX1, RX2, RX3, RX4, RX5, RX6, RX7, RX8 + * TX1, TX2, TX3, TX4, TX5, TX6, TX7, TX8, TX9, TX10, TX11, TX12, TX13 + * TX14, TX15, TX16 + */ + unsigned int rx_ch_tavil[WCD934X_RX_MAX] = {144, 145, 146, 147, 148, + 149, 150, 151}; + unsigned int tx_ch_tavil[WCD934X_TX_MAX] = {128, 129, 130, 131, 132, + 133, 134, 135, 136, 137, 138, + 139, 140, 141, 142, 143}; + + pr_debug("%s: dev_name%s\n", __func__, dev_name(cpu_dai->dev)); + + rtd->pmdown_time = 0; + + ret = snd_soc_add_codec_controls(codec, msm_snd_controls, + ARRAY_SIZE(msm_snd_controls)); + if (ret < 0) { + pr_err("%s: add_codec_controls failed: %d\n", + __func__, ret); + return ret; + } + + ret = snd_soc_add_codec_controls(codec, msm_common_snd_controls, + msm_common_snd_controls_size()); + if (ret < 0) { + pr_err("%s: add_common_snd_controls failed: %d\n", + __func__, ret); + return ret; + } + + snd_soc_dapm_new_controls(dapm, msm_dapm_widgets, + ARRAY_SIZE(msm_dapm_widgets)); + + if (!strcmp(dev_name(codec_dai->dev), "tasha_codec")) + snd_soc_dapm_add_routes(dapm, wcd_audio_paths_tasha, + ARRAY_SIZE(wcd_audio_paths_tasha)); + else + snd_soc_dapm_add_routes(dapm, wcd_audio_paths, + ARRAY_SIZE(wcd_audio_paths)); + + snd_soc_dapm_enable_pin(dapm, "Lineout_1 amp"); + snd_soc_dapm_enable_pin(dapm, "Lineout_3 amp"); + snd_soc_dapm_enable_pin(dapm, "Lineout_2 amp"); + snd_soc_dapm_enable_pin(dapm, "Lineout_4 amp"); + + snd_soc_dapm_ignore_suspend(dapm, "MADINPUT"); + snd_soc_dapm_ignore_suspend(dapm, "MAD_CPE_INPUT"); + snd_soc_dapm_ignore_suspend(dapm, "Handset Mic"); + snd_soc_dapm_ignore_suspend(dapm, "Headset Mic"); + snd_soc_dapm_ignore_suspend(dapm, "Secondary Mic"); + snd_soc_dapm_ignore_suspend(dapm, "Lineout_1 amp"); + snd_soc_dapm_ignore_suspend(dapm, "Lineout_3 amp"); + snd_soc_dapm_ignore_suspend(dapm, "Lineout_2 amp"); + snd_soc_dapm_ignore_suspend(dapm, "Lineout_4 amp"); + snd_soc_dapm_ignore_suspend(dapm, "ANCRight Headset Mic"); + snd_soc_dapm_ignore_suspend(dapm, "ANCLeft Headset Mic"); + snd_soc_dapm_ignore_suspend(dapm, "Digital Mic0"); + snd_soc_dapm_ignore_suspend(dapm, "Digital Mic1"); + snd_soc_dapm_ignore_suspend(dapm, "Digital Mic2"); + snd_soc_dapm_ignore_suspend(dapm, "Digital Mic3"); + snd_soc_dapm_ignore_suspend(dapm, "Digital Mic4"); + snd_soc_dapm_ignore_suspend(dapm, "Digital Mic5"); + snd_soc_dapm_ignore_suspend(dapm, "Analog Mic4"); + snd_soc_dapm_ignore_suspend(dapm, "Analog Mic6"); + snd_soc_dapm_ignore_suspend(dapm, "Analog Mic7"); + snd_soc_dapm_ignore_suspend(dapm, "Analog Mic8"); + + snd_soc_dapm_ignore_suspend(dapm, "EAR"); + snd_soc_dapm_ignore_suspend(dapm, "LINEOUT1"); + snd_soc_dapm_ignore_suspend(dapm, "LINEOUT2"); + snd_soc_dapm_ignore_suspend(dapm, "AMIC1"); + snd_soc_dapm_ignore_suspend(dapm, "AMIC2"); + snd_soc_dapm_ignore_suspend(dapm, "AMIC3"); + snd_soc_dapm_ignore_suspend(dapm, "AMIC4"); + snd_soc_dapm_ignore_suspend(dapm, "AMIC5"); + snd_soc_dapm_ignore_suspend(dapm, "DMIC0"); + snd_soc_dapm_ignore_suspend(dapm, "DMIC1"); + snd_soc_dapm_ignore_suspend(dapm, "DMIC2"); + snd_soc_dapm_ignore_suspend(dapm, "DMIC3"); + snd_soc_dapm_ignore_suspend(dapm, "DMIC4"); + snd_soc_dapm_ignore_suspend(dapm, "DMIC5"); + snd_soc_dapm_ignore_suspend(dapm, "ANC EAR"); + snd_soc_dapm_ignore_suspend(dapm, "SPK1 OUT"); + snd_soc_dapm_ignore_suspend(dapm, "SPK2 OUT"); + snd_soc_dapm_ignore_suspend(dapm, "HPHL"); + snd_soc_dapm_ignore_suspend(dapm, "HPHR"); + snd_soc_dapm_ignore_suspend(dapm, "AIF4 VI"); + snd_soc_dapm_ignore_suspend(dapm, "VIINPUT"); + + if (!strcmp(dev_name(codec_dai->dev), "tasha_codec")) { + snd_soc_dapm_ignore_suspend(dapm, "LINEOUT3"); + snd_soc_dapm_ignore_suspend(dapm, "LINEOUT4"); + snd_soc_dapm_ignore_suspend(dapm, "ANC HPHL"); + snd_soc_dapm_ignore_suspend(dapm, "ANC HPHR"); + snd_soc_dapm_ignore_suspend(dapm, "ANC LINEOUT1"); + snd_soc_dapm_ignore_suspend(dapm, "ANC LINEOUT2"); + } else { + snd_soc_dapm_ignore_suspend(dapm, "MAD_CPE_OUT1"); + snd_soc_dapm_ignore_suspend(dapm, "MAD_CPE_OUT2"); + } + + snd_soc_dapm_sync(dapm); + + if (!strcmp(dev_name(codec_dai->dev), "tavil_codec")) { + snd_soc_dai_set_channel_map(codec_dai, ARRAY_SIZE(tx_ch_tavil), + tx_ch_tavil, ARRAY_SIZE(rx_ch_tavil), + rx_ch_tavil); + } else { + snd_soc_dai_set_channel_map(codec_dai, ARRAY_SIZE(tx_ch), + tx_ch, ARRAY_SIZE(rx_ch), + rx_ch); + } + + if (!strcmp(dev_name(codec_dai->dev), "tavil_codec")) { + msm_codec_fn.get_afe_config_fn = tavil_get_afe_config; + } else { + msm_codec_fn.get_afe_config_fn = tasha_get_afe_config; + msm_codec_fn.mbhc_hs_detect_exit = tasha_mbhc_hs_detect_exit; + } + + ret = msm_adsp_power_up_config(codec, rtd->card->snd_card); + if (ret) { + pr_err("%s: Failed to set AFE config %d\n", __func__, ret); + goto err_afe_cfg; + } + + config_data = msm_codec_fn.get_afe_config_fn(codec, + AFE_AANC_VERSION); + if (config_data) { + ret = afe_set_config(AFE_AANC_VERSION, config_data, 0); + if (ret) { + pr_err("%s: Failed to set aanc version %d\n", + __func__, ret); + goto err_afe_cfg; + } + } + + if (!strcmp(dev_name(codec_dai->dev), "tasha_codec")) { + config_data = msm_codec_fn.get_afe_config_fn(codec, + AFE_CDC_CLIP_REGISTERS_CONFIG); + if (config_data) { + ret = afe_set_config(AFE_CDC_CLIP_REGISTERS_CONFIG, + config_data, 0); + if (ret) { + pr_err("%s: Failed to set clip registers %d\n", + __func__, ret); + goto err_afe_cfg; + } + } + config_data = msm_codec_fn.get_afe_config_fn(codec, + AFE_CLIP_BANK_SEL); + if (config_data) { + ret = afe_set_config(AFE_CLIP_BANK_SEL, config_data, 0); + if (ret) { + pr_err("%s: Failed to set AFE bank selection %d\n", + __func__, ret); + goto err_afe_cfg; + } + } + } + + /* + * Send speaker configuration only for WSA8810. + * Defalut configuration is for WSA8815. + */ + pr_debug("%s: Number of aux devices: %d\n", + __func__, rtd->card->num_aux_devs); + + if (!strcmp(dev_name(codec_dai->dev), "tavil_codec")) { + if (rtd->card->num_aux_devs && + !list_empty(&rtd->card->aux_comp_list)) { + aux_comp = list_first_entry(&rtd->card->aux_comp_list, + struct snd_soc_component, list_aux); + if (!strcmp(aux_comp->name, WSA8810_NAME_1) || + !strcmp(aux_comp->name, WSA8810_NAME_2)) { + tavil_set_spkr_mode(rtd->codec, SPKR_MODE_1); + tavil_set_spkr_gain_offset(rtd->codec, + RX_GAIN_OFFSET_M1P5_DB); + } + } + card = rtd->card->snd_card; + entry = snd_info_create_subdir(card->module, "codecs", + card->proc_root); + if (!entry) { + pr_debug("%s: Cannot create codecs module entry\n", + __func__); + goto done; + } + pdata->codec_root = entry; + tavil_codec_info_create_codec_entry(pdata->codec_root, codec); + } else { + if (rtd->card->num_aux_devs && + !list_empty(&rtd->card->aux_comp_list)) { + aux_comp = list_first_entry(&rtd->card->aux_comp_list, + struct snd_soc_component, list_aux); + if (!strcmp(aux_comp->name, WSA8810_NAME_1) || + !strcmp(aux_comp->name, WSA8810_NAME_2)) { + tasha_set_spkr_mode(rtd->codec, SPKR_MODE_1); + tasha_set_spkr_gain_offset(rtd->codec, + RX_GAIN_OFFSET_M1P5_DB); + } + } + card = rtd->card->snd_card; + entry = snd_info_create_subdir(card->module, "codecs", + card->proc_root); + if (!entry) { + pr_debug("%s: Cannot create codecs module entry\n", + __func__); + goto done; + } + pdata->codec_root = entry; + tasha_codec_info_create_codec_entry(pdata->codec_root, codec); + tasha_mbhc_zdet_gpio_ctrl(msm_config_hph_en0_gpio, rtd->codec); + } +done: + msm_set_codec_reg_done(true); + return 0; + +err_afe_cfg: + return ret; +} +EXPORT_SYMBOL(msm_audrx_init); + +/** + * msm_ext_register_audio_notifier - register SSR notifier. + */ +void msm_ext_register_audio_notifier(struct platform_device *pdev) +{ + int ret; + + is_initial_boot = true; + spdev = pdev; + ret = audio_notifier_register("sdm660", AUDIO_NOTIFIER_ADSP_DOMAIN, + &service_nb); + if (ret < 0) + pr_err("%s: Audio notifier register failed ret = %d\n", + __func__, ret); +} +EXPORT_SYMBOL(msm_ext_register_audio_notifier); + +/** + * msm_ext_cdc_init - external codec machine specific init. + * + * @pdev: platform device handle + * @pdata: private data of machine driver + * @card: sound card pointer reference + * @mbhc_cfg: MBHC config reference + * + * Returns 0 on success or ret on failure. + */ +int msm_ext_cdc_init(struct platform_device *pdev, + struct msm_asoc_mach_data *pdata, + struct snd_soc_card **card, + struct wcd_mbhc_config *wcd_mbhc_cfg_ptr1) +{ + int ret = 0; + + wcd_mbhc_cfg_ptr = wcd_mbhc_cfg_ptr1; + pdev->id = 0; + wcd_mbhc_cfg_ptr->moisture_en = true; + wcd_mbhc_cfg_ptr->mbhc_micbias = MIC_BIAS_2; + wcd_mbhc_cfg_ptr->anc_micbias = MIC_BIAS_2; + wcd_mbhc_cfg_ptr->enable_anc_mic_detect = false; + + *card = populate_snd_card_dailinks(&pdev->dev, pdata->snd_card_val); + if (!(*card)) { + dev_err(&pdev->dev, "%s: Card uninitialized\n", __func__); + ret = -EPROBE_DEFER; + goto err; + } + platform_set_drvdata(pdev, *card); + snd_soc_card_set_drvdata(*card, pdata); + pdata->hph_en1_gpio = of_get_named_gpio(pdev->dev.of_node, + "qcom,hph-en1-gpio", 0); + if (!gpio_is_valid(pdata->hph_en1_gpio)) + pdata->hph_en1_gpio_p = of_parse_phandle(pdev->dev.of_node, + "qcom,hph-en1-gpio", 0); + if (!gpio_is_valid(pdata->hph_en1_gpio) && (!pdata->hph_en1_gpio_p)) { + dev_dbg(&pdev->dev, "property %s not detected in node %s", + "qcom,hph-en1-gpio", pdev->dev.of_node->full_name); + } + + pdata->hph_en0_gpio = of_get_named_gpio(pdev->dev.of_node, + "qcom,hph-en0-gpio", 0); + if (!gpio_is_valid(pdata->hph_en0_gpio)) + pdata->hph_en0_gpio_p = of_parse_phandle(pdev->dev.of_node, + "qcom,hph-en0-gpio", 0); + if (!gpio_is_valid(pdata->hph_en0_gpio) && (!pdata->hph_en0_gpio_p)) { + dev_dbg(&pdev->dev, "property %s not detected in node %s", + "qcom,hph-en0-gpio", pdev->dev.of_node->full_name); + } + + ret = msm_ext_prepare_hifi(pdata); + if (ret) { + dev_dbg(&pdev->dev, "msm_ext_prepare_hifi failed (%d)\n", + ret); + ret = 0; + } +err: + return ret; +} +EXPORT_SYMBOL(msm_ext_cdc_init); diff --git a/audio-kernel/asoc/sdm660-external.h b/audio-kernel/asoc/sdm660-external.h new file mode 100644 index 000000000..b81e158be --- /dev/null +++ b/audio-kernel/asoc/sdm660-external.h @@ -0,0 +1,52 @@ +/* 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 + * 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 __SDM660_EXTERNAL +#define __SDM660_EXTERNAL + +int msm_snd_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params); +int msm_ext_slimbus_2_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params); +int msm_btsco_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params); +int msm_proxy_rx_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params); +int msm_proxy_tx_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params); +int msm_audrx_init(struct snd_soc_pcm_runtime *rtd); +int msm_snd_cpe_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params); +struct snd_soc_card *populate_snd_card_dailinks(struct device *dev, + int snd_card_val); +int msm_ext_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params); +int msm_snd_card_tavil_late_probe(struct snd_soc_card *card); +int msm_snd_card_tasha_late_probe(struct snd_soc_card *card); +#if IS_ENABLED(CONFIG_SND_SOC_EXT_CODEC) +int msm_ext_cdc_init(struct platform_device *, struct msm_asoc_mach_data *, + struct snd_soc_card **, struct wcd_mbhc_config *); +void msm_ext_register_audio_notifier(struct platform_device *pdev); +#else +inline int msm_ext_cdc_init(struct platform_device *pdev, + struct msm_asoc_mach_data *pdata, + struct snd_soc_card **card, + struct wcd_mbhc_config *wcd_mbhc_cfg_ptr1) +{ + return 0; +} + +inline void msm_ext_register_audio_notifier(struct platform_device *pdev) +{ +} +#endif +#endif diff --git a/audio-kernel/asoc/sdm660-internal.c b/audio-kernel/asoc/sdm660-internal.c new file mode 100644 index 000000000..fc141e325 --- /dev/null +++ b/audio-kernel/asoc/sdm660-internal.c @@ -0,0 +1,3354 @@ +/* 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 + * 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 +#include +#include +#include +#include "msm-pcm-routing-v2.h" +#include "sdm660-common.h" +#include "codecs/msm-cdc-pinctrl.h" +#include "codecs/sdm660_cdc/msm-digital-cdc.h" +#include "codecs/sdm660_cdc/msm-analog-cdc.h" +#include "codecs/msm_sdw/msm_sdw.h" +#include + +#define __CHIPSET__ "SDM660 " +#define MSM_DAILINK_NAME(name) (__CHIPSET__#name) + +#define WCD_MBHC_DEF_RLOADS 5 + +#define WCN_CDC_SLIM_RX_CH_MAX 2 +#define WCN_CDC_SLIM_TX_CH_MAX 3 + +#define WSA8810_NAME_1 "wsa881x.20170211" +#define WSA8810_NAME_2 "wsa881x.20170212" +#define MSM_LL_QOS_VALUE 300 /* time in us to ensure LPM doesn't go in C3/C4 */ +enum { + INT0_MI2S = 0, + INT1_MI2S, + INT2_MI2S, + INT3_MI2S, + INT4_MI2S, + INT5_MI2S, + INT6_MI2S, + INT_MI2S_MAX, +}; + +enum { + BT_SLIM7, + FM_SLIM8, + SLIM_MAX, +}; + +/*TDM default offset currently only supporting TDM_RX_0 and TDM_TX_0 */ +static unsigned int tdm_slot_offset[TDM_PORT_MAX][TDM_SLOT_OFFSET_MAX] = { + {0, 4, 8, 12, 16, 20, 24, 28},/* TX_0 | RX_0 */ + {AFE_SLOT_MAPPING_OFFSET_INVALID},/* TX_1 | RX_1 */ + {AFE_SLOT_MAPPING_OFFSET_INVALID},/* TX_2 | RX_2 */ + {AFE_SLOT_MAPPING_OFFSET_INVALID},/* TX_3 | RX_3 */ + {AFE_SLOT_MAPPING_OFFSET_INVALID},/* TX_4 | RX_4 */ + {AFE_SLOT_MAPPING_OFFSET_INVALID},/* TX_5 | RX_5 */ + {AFE_SLOT_MAPPING_OFFSET_INVALID},/* TX_6 | RX_6 */ + {AFE_SLOT_MAPPING_OFFSET_INVALID},/* TX_7 | RX_7 */ +}; + +static struct afe_clk_set int_mi2s_clk[INT_MI2S_MAX] = { + { + AFE_API_VERSION_I2S_CONFIG, + Q6AFE_LPASS_CLK_ID_INT0_MI2S_IBIT, + Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ, + Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO, + Q6AFE_LPASS_CLK_ROOT_DEFAULT, + 0, + }, + { + AFE_API_VERSION_I2S_CONFIG, + Q6AFE_LPASS_CLK_ID_INT1_MI2S_IBIT, + Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ, + Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO, + Q6AFE_LPASS_CLK_ROOT_DEFAULT, + 0, + }, + { + AFE_API_VERSION_I2S_CONFIG, + Q6AFE_LPASS_CLK_ID_INT2_MI2S_IBIT, + Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ, + Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO, + Q6AFE_LPASS_CLK_ROOT_DEFAULT, + 0, + }, + { + AFE_API_VERSION_I2S_CONFIG, + Q6AFE_LPASS_CLK_ID_INT3_MI2S_IBIT, + Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ, + Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO, + Q6AFE_LPASS_CLK_ROOT_DEFAULT, + 0, + }, + { + AFE_API_VERSION_I2S_CONFIG, + Q6AFE_LPASS_CLK_ID_INT4_MI2S_IBIT, + Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ, + Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO, + Q6AFE_LPASS_CLK_ROOT_DEFAULT, + 0, + }, + { + AFE_API_VERSION_I2S_CONFIG, + Q6AFE_LPASS_CLK_ID_INT5_MI2S_IBIT, + Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ, + Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO, + Q6AFE_LPASS_CLK_ROOT_DEFAULT, + 0, + }, + { + AFE_API_VERSION_I2S_CONFIG, + Q6AFE_LPASS_CLK_ID_INT6_MI2S_IBIT, + Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ, + Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO, + Q6AFE_LPASS_CLK_ROOT_DEFAULT, + 0, + }, +}; + +struct dev_config { + u32 sample_rate; + u32 bit_format; + u32 channels; +}; + +/* Default configuration of MI2S channels */ +static struct dev_config int_mi2s_cfg[] = { + [INT0_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, + [INT1_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [INT2_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [INT3_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [INT4_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [INT5_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, + [INT6_MI2S] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, +}; + +static struct dev_config bt_fm_cfg[] = { + [BT_SLIM7] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [FM_SLIM8] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, +}; + +static char const *int_mi2s_rate_text[] = {"KHZ_8", "KHZ_16", + "KHZ_32", "KHZ_44P1", "KHZ_48", + "KHZ_96", "KHZ_192"}; +static const char *const int_mi2s_ch_text[] = {"One", "Two"}; +static const char *const int_mi2s_tx_ch_text[] = {"One", "Two", + "Three", "Four"}; +static char const *bit_format_text[] = {"S16_LE", "S24_LE", "S24_3LE"}; +static const char *const loopback_mclk_text[] = {"DISABLE", "ENABLE"}; +static char const *bt_sample_rate_text[] = {"KHZ_8", "KHZ_16", + "KHZ_44P1", "KHZ_48", + "KHZ_88P2", "KHZ_96"}; + +static SOC_ENUM_SINGLE_EXT_DECL(int0_mi2s_rx_sample_rate, int_mi2s_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(int0_mi2s_rx_chs, int_mi2s_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(int0_mi2s_rx_format, bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(int2_mi2s_tx_sample_rate, int_mi2s_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(int2_mi2s_tx_chs, int_mi2s_tx_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(int2_mi2s_tx_format, bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(int3_mi2s_tx_sample_rate, int_mi2s_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(int3_mi2s_tx_chs, int_mi2s_tx_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(int3_mi2s_tx_format, bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(int4_mi2s_rx_sample_rate, int_mi2s_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(int4_mi2s_rx_chs, int_mi2s_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(int4_mi2s_rx_format, bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(int5_mi2s_tx_chs, int_mi2s_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(loopback_mclk_en, loopback_mclk_text); +static SOC_ENUM_SINGLE_EXT_DECL(bt_sample_rate, bt_sample_rate_text); + +static int msm_dmic_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event); +static int msm_int_enable_dig_cdc_clk(struct snd_soc_codec *codec, int enable, + bool dapm); +static int msm_int_mclk0_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event); +static int msm_int_mi2s_snd_startup(struct snd_pcm_substream *substream); +static void msm_int_mi2s_snd_shutdown(struct snd_pcm_substream *substream); + +static struct wcd_mbhc_config *mbhc_cfg_ptr; +static struct snd_info_entry *codec_root; + +static int int_mi2s_get_bit_format_val(int bit_format) +{ + int val = 0; + + switch (bit_format) { + case SNDRV_PCM_FORMAT_S24_3LE: + val = 2; + break; + case SNDRV_PCM_FORMAT_S24_LE: + val = 1; + break; + case SNDRV_PCM_FORMAT_S16_LE: + default: + val = 0; + break; + } + return val; +} + +static int int_mi2s_get_bit_format(int val) +{ + int bit_fmt = SNDRV_PCM_FORMAT_S16_LE; + + switch (val) { + case 0: + bit_fmt = SNDRV_PCM_FORMAT_S16_LE; + break; + case 1: + bit_fmt = SNDRV_PCM_FORMAT_S24_LE; + break; + case 2: + bit_fmt = SNDRV_PCM_FORMAT_S24_3LE; + break; + default: + bit_fmt = SNDRV_PCM_FORMAT_S16_LE; + break; + } + return bit_fmt; +} + +static int int_mi2s_get_port_idx(struct snd_kcontrol *kcontrol) +{ + int port_id = 0; + + if (strnstr(kcontrol->id.name, "INT0_MI2S", sizeof("INT0_MI2S"))) + port_id = INT0_MI2S; + else if (strnstr(kcontrol->id.name, "INT2_MI2S", sizeof("INT2_MI2S"))) + port_id = INT2_MI2S; + else if (strnstr(kcontrol->id.name, "INT3_MI2S", sizeof("INT3_MI2S"))) + port_id = INT3_MI2S; + else if (strnstr(kcontrol->id.name, "INT4_MI2S", sizeof("INT4_MI2S"))) + port_id = INT4_MI2S; + else { + pr_err("%s: unsupported channel: %s", + __func__, kcontrol->id.name); + return -EINVAL; + } + + return port_id; +} + +static int int_mi2s_bit_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = int_mi2s_get_port_idx(kcontrol); + + if (ch_num < 0) + return ch_num; + + ucontrol->value.enumerated.item[0] = + int_mi2s_get_bit_format_val(int_mi2s_cfg[ch_num].bit_format); + + pr_debug("%s: int_mi2s[%d]_bit_format = %d, ucontrol value = %d\n", + __func__, ch_num, int_mi2s_cfg[ch_num].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int int_mi2s_bit_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = int_mi2s_get_port_idx(kcontrol); + + if (ch_num < 0) + return ch_num; + + int_mi2s_cfg[ch_num].bit_format = + int_mi2s_get_bit_format(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: int_mi2s[%d]_rx_bit_format = %d, ucontrol value = %d\n", + __func__, ch_num, int_mi2s_cfg[ch_num].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static inline int param_is_mask(int p) +{ + return (p >= SNDRV_PCM_HW_PARAM_FIRST_MASK) && + (p <= SNDRV_PCM_HW_PARAM_LAST_MASK); +} + +static inline struct snd_mask *param_to_mask(struct snd_pcm_hw_params *p, + int n) +{ + return &(p->masks[n - SNDRV_PCM_HW_PARAM_FIRST_MASK]); +} + +static void param_set_mask(struct snd_pcm_hw_params *p, int n, unsigned int bit) +{ + if (bit >= SNDRV_MASK_MAX) + return; + if (param_is_mask(n)) { + struct snd_mask *m = param_to_mask(p, n); + + m->bits[0] = 0; + m->bits[1] = 0; + m->bits[bit >> 5] |= (1 << (bit & 31)); + } +} + +static int int_mi2s_get_sample_rate_val(int sample_rate) +{ + int sample_rate_val; + + switch (sample_rate) { + case SAMPLING_RATE_8KHZ: + sample_rate_val = 0; + break; + case SAMPLING_RATE_16KHZ: + sample_rate_val = 1; + break; + case SAMPLING_RATE_32KHZ: + sample_rate_val = 2; + break; + case SAMPLING_RATE_44P1KHZ: + sample_rate_val = 3; + break; + case SAMPLING_RATE_48KHZ: + sample_rate_val = 4; + break; + case SAMPLING_RATE_96KHZ: + sample_rate_val = 5; + break; + case SAMPLING_RATE_192KHZ: + sample_rate_val = 6; + break; + default: + sample_rate_val = 4; + break; + } + return sample_rate_val; +} + +static int int_mi2s_get_sample_rate(int value) +{ + int sample_rate; + + switch (value) { + case 0: + sample_rate = SAMPLING_RATE_8KHZ; + break; + case 1: + sample_rate = SAMPLING_RATE_16KHZ; + break; + case 2: + sample_rate = SAMPLING_RATE_32KHZ; + break; + case 3: + sample_rate = SAMPLING_RATE_44P1KHZ; + break; + case 4: + sample_rate = SAMPLING_RATE_48KHZ; + break; + case 5: + sample_rate = SAMPLING_RATE_96KHZ; + break; + case 6: + sample_rate = SAMPLING_RATE_192KHZ; + break; + default: + sample_rate = SAMPLING_RATE_48KHZ; + break; + } + return sample_rate; +} + +static int int_mi2s_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = int_mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + int_mi2s_cfg[idx].sample_rate = + int_mi2s_get_sample_rate(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: idx[%d]_sample_rate = %d, item = %d\n", __func__, + idx, int_mi2s_cfg[idx].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int int_mi2s_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = int_mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + ucontrol->value.enumerated.item[0] = + int_mi2s_get_sample_rate_val(int_mi2s_cfg[idx].sample_rate); + + pr_debug("%s: idx[%d]_sample_rate = %d, item = %d\n", __func__, + idx, int_mi2s_cfg[idx].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int int_mi2s_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = int_mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + pr_debug("%s: int_mi2s_[%d]_rx_ch = %d\n", __func__, + idx, int_mi2s_cfg[idx].channels); + ucontrol->value.enumerated.item[0] = int_mi2s_cfg[idx].channels - 1; + + return 0; +} + +static int int_mi2s_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = int_mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + int_mi2s_cfg[idx].channels = ucontrol->value.enumerated.item[0] + 1; + pr_debug("%s: int_mi2s_[%d]_ch = %d\n", __func__, + idx, int_mi2s_cfg[idx].channels); + + return 1; +} + +static const struct snd_soc_dapm_widget msm_int_dapm_widgets[] = { + SND_SOC_DAPM_SUPPLY_S("INT_MCLK0", -1, SND_SOC_NOPM, 0, 0, + msm_int_mclk0_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIC("Handset Mic", NULL), + SND_SOC_DAPM_MIC("Headset Mic", NULL), + SND_SOC_DAPM_MIC("Secondary Mic", NULL), + SND_SOC_DAPM_MIC("Digital Mic1", msm_dmic_event), + SND_SOC_DAPM_MIC("Digital Mic2", msm_dmic_event), + SND_SOC_DAPM_MIC("Digital Mic3", msm_dmic_event), + SND_SOC_DAPM_MIC("Digital Mic4", msm_dmic_event), +}; + +static int msm_config_hph_compander_gpio(bool enable, + struct snd_soc_codec *codec) +{ + struct snd_soc_card *card = codec->component.card; + struct msm_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card); + int ret = 0; + + pr_debug("%s: %s HPH Compander\n", __func__, + enable ? "Enable" : "Disable"); + + if (enable) { + ret = msm_cdc_pinctrl_select_active_state(pdata->comp_gpio_p); + if (ret) { + pr_err("%s: gpio set cannot be activated %s\n", + __func__, "comp_gpio"); + goto done; + } + } else { + ret = msm_cdc_pinctrl_select_sleep_state(pdata->comp_gpio_p); + if (ret) { + pr_err("%s: gpio set cannot be de-activated %s\n", + __func__, "comp_gpio"); + goto done; + } + } + +done: + return ret; +} + +static int is_ext_spk_gpio_support(struct platform_device *pdev, + struct msm_asoc_mach_data *pdata) +{ + const char *spk_ext_pa = "qcom,msm-spk-ext-pa"; + + pr_debug("%s:Enter\n", __func__); + + pdata->spk_ext_pa_gpio = of_get_named_gpio(pdev->dev.of_node, + spk_ext_pa, 0); + + if (pdata->spk_ext_pa_gpio < 0) { + dev_dbg(&pdev->dev, + "%s: missing %s in dt node\n", __func__, spk_ext_pa); + } else { + if (!gpio_is_valid(pdata->spk_ext_pa_gpio)) { + pr_err("%s: Invalid external speaker gpio: %d", + __func__, pdata->spk_ext_pa_gpio); + return -EINVAL; + } + } + return 0; +} + +static int enable_spk_ext_pa(struct snd_soc_codec *codec, int enable) +{ + struct snd_soc_card *card = codec->component.card; + struct msm_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card); + int ret; + + if (!gpio_is_valid(pdata->spk_ext_pa_gpio)) { + pr_err("%s: Invalid gpio: %d\n", __func__, + pdata->spk_ext_pa_gpio); + return false; + } + + pr_debug("%s: %s external speaker PA\n", __func__, + enable ? "Enable" : "Disable"); + + if (enable) { + ret = msm_cdc_pinctrl_select_active_state( + pdata->ext_spk_gpio_p); + if (ret) { + pr_err("%s: gpio set cannot be de-activated %s\n", + __func__, "ext_spk_gpio"); + return ret; + } + gpio_set_value_cansleep(pdata->spk_ext_pa_gpio, enable); + } else { + gpio_set_value_cansleep(pdata->spk_ext_pa_gpio, enable); + ret = msm_cdc_pinctrl_select_sleep_state( + pdata->ext_spk_gpio_p); + if (ret) { + pr_err("%s: gpio set cannot be de-activated %s\n", + __func__, "ext_spk_gpio"); + return ret; + } + } + return 0; +} + +static int int_mi2s_get_idx_from_beid(int32_t id) +{ + int idx = 0; + + switch (id) { + case MSM_BACKEND_DAI_INT0_MI2S_RX: + idx = INT0_MI2S; + break; + case MSM_BACKEND_DAI_INT2_MI2S_TX: + idx = INT2_MI2S; + break; + case MSM_BACKEND_DAI_INT3_MI2S_TX: + idx = INT3_MI2S; + break; + case MSM_BACKEND_DAI_INT4_MI2S_RX: + idx = INT4_MI2S; + break; + case MSM_BACKEND_DAI_INT5_MI2S_TX: + idx = INT5_MI2S; + break; + default: + idx = INT0_MI2S; + break; + } + + return idx; +} + +static int msm_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + + pr_debug("%s()\n", __func__); + rate->min = rate->max = 48000; + channels->min = channels->max = 2; + + return 0; +} + +static int int_mi2s_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_dai_link *dai_link = rtd->dai_link; + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + int idx; + + pr_debug("%s: format = %d, rate = %d\n", + __func__, params_format(params), params_rate(params)); + + switch (dai_link->id) { + case MSM_BACKEND_DAI_INT0_MI2S_RX: + case MSM_BACKEND_DAI_INT2_MI2S_TX: + case MSM_BACKEND_DAI_INT3_MI2S_TX: + case MSM_BACKEND_DAI_INT4_MI2S_RX: + case MSM_BACKEND_DAI_INT5_MI2S_TX: + idx = int_mi2s_get_idx_from_beid(dai_link->id); + rate->min = rate->max = int_mi2s_cfg[idx].sample_rate; + channels->min = channels->max = + int_mi2s_cfg[idx].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + int_mi2s_cfg[idx].bit_format); + break; + default: + rate->min = rate->max = SAMPLING_RATE_48KHZ; + break; + } + return 0; +} + +static int msm_btfm_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_dai_link *dai_link = rtd->dai_link; + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + + switch (dai_link->id) { + case MSM_BACKEND_DAI_SLIMBUS_7_RX: + case MSM_BACKEND_DAI_SLIMBUS_7_TX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + bt_fm_cfg[BT_SLIM7].bit_format); + rate->min = rate->max = bt_fm_cfg[BT_SLIM7].sample_rate; + channels->min = channels->max = + bt_fm_cfg[BT_SLIM7].channels; + break; + + case MSM_BACKEND_DAI_SLIMBUS_8_TX: + rate->min = rate->max = bt_fm_cfg[FM_SLIM8].sample_rate; + channels->min = channels->max = + bt_fm_cfg[FM_SLIM8].channels; + break; + + default: + rate->min = rate->max = SAMPLING_RATE_48KHZ; + break; + } + return 0; +} + +static int msm_vi_feed_tx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = + (int_mi2s_cfg[INT5_MI2S].channels/2 - 1); + pr_debug("%s: msm_vi_feed_tx_ch = %ld\n", __func__, + ucontrol->value.integer.value[0]); + return 0; +} + +static int msm_vi_feed_tx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int_mi2s_cfg[INT5_MI2S].channels = + roundup_pow_of_two(ucontrol->value.integer.value[0] + 2); + + pr_debug("%s: msm_vi_feed_tx_ch = %d\n", + __func__, int_mi2s_cfg[INT5_MI2S].channels); + return 1; +} + +static int msm_int_enable_dig_cdc_clk(struct snd_soc_codec *codec, + int enable, bool dapm) +{ + int ret = 0; + struct msm_asoc_mach_data *pdata = NULL; + int clk_freq_in_hz; + bool int_mclk0_freq_chg = false; + + pdata = snd_soc_card_get_drvdata(codec->component.card); + pr_debug("%s: enable %d mclk ref counter %d\n", + __func__, enable, + atomic_read(&pdata->int_mclk0_rsc_ref)); + if (enable) { + if (int_mi2s_cfg[INT0_MI2S].sample_rate == + SAMPLING_RATE_44P1KHZ) { + clk_freq_in_hz = NATIVE_MCLK_RATE; + pdata->native_clk_set = true; + } else { + clk_freq_in_hz = pdata->mclk_freq; + pdata->native_clk_set = false; + } + + if (pdata->digital_cdc_core_clk.clk_freq_in_hz + != clk_freq_in_hz) + int_mclk0_freq_chg = true; + if (!atomic_read(&pdata->int_mclk0_rsc_ref) || + int_mclk0_freq_chg) { + cancel_delayed_work_sync( + &pdata->disable_int_mclk0_work); + mutex_lock(&pdata->cdc_int_mclk0_mutex); + if (atomic_read(&pdata->int_mclk0_enabled) == false || + int_mclk0_freq_chg) { + if (atomic_read(&pdata->int_mclk0_enabled)) { + pdata->digital_cdc_core_clk.enable = 0; + afe_set_lpass_clock_v2( + AFE_PORT_ID_INT0_MI2S_RX, + &pdata->digital_cdc_core_clk); + } + pdata->digital_cdc_core_clk.clk_freq_in_hz = + clk_freq_in_hz; + pdata->digital_cdc_core_clk.enable = 1; + ret = afe_set_lpass_clock_v2( + AFE_PORT_ID_INT0_MI2S_RX, + &pdata->digital_cdc_core_clk); + if (ret < 0) { + pr_err("%s: failed to enable CCLK\n", + __func__); + mutex_unlock( + &pdata->cdc_int_mclk0_mutex); + return ret; + } + pr_debug("enabled digital codec core clk\n"); + atomic_set(&pdata->int_mclk0_enabled, true); + } + mutex_unlock(&pdata->cdc_int_mclk0_mutex); + } + atomic_inc(&pdata->int_mclk0_rsc_ref); + } else { + cancel_delayed_work_sync(&pdata->disable_int_mclk0_work); + mutex_lock(&pdata->cdc_int_mclk0_mutex); + if (atomic_read(&pdata->int_mclk0_enabled) == true) { + pdata->digital_cdc_core_clk.clk_freq_in_hz = + DEFAULT_MCLK_RATE; + pdata->digital_cdc_core_clk.enable = 0; + ret = afe_set_lpass_clock_v2( + AFE_PORT_ID_INT0_MI2S_RX, + &pdata->digital_cdc_core_clk); + if (ret < 0) + pr_err("%s: failed to disable CCLK\n", + __func__); + atomic_set(&pdata->int_mclk0_enabled, false); + atomic_set(&pdata->int_mclk0_rsc_ref, 0); + } + mutex_unlock(&pdata->cdc_int_mclk0_mutex); + } + return ret; +} + +static int loopback_mclk_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s\n", __func__); + return 0; +} + +static int loopback_mclk_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret = -EINVAL; + struct msm_asoc_mach_data *pdata = NULL; + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + + pdata = snd_soc_card_get_drvdata(codec->component.card); + pr_debug("%s: mclk_rsc_ref %d enable %ld\n", + __func__, atomic_read(&pdata->int_mclk0_rsc_ref), + ucontrol->value.integer.value[0]); + switch (ucontrol->value.integer.value[0]) { + case 1: + ret = msm_cdc_pinctrl_select_active_state(pdata->pdm_gpio_p); + if (ret) { + pr_err("%s: failed to enable the pri gpios: %d\n", + __func__, ret); + break; + } + mutex_lock(&pdata->cdc_int_mclk0_mutex); + if ((!atomic_read(&pdata->int_mclk0_rsc_ref)) && + (!atomic_read(&pdata->int_mclk0_enabled))) { + pdata->digital_cdc_core_clk.enable = 1; + ret = afe_set_lpass_clock_v2( + AFE_PORT_ID_INT0_MI2S_RX, + &pdata->digital_cdc_core_clk); + if (ret < 0) { + pr_err("%s: failed to enable the MCLK: %d\n", + __func__, ret); + mutex_unlock(&pdata->cdc_int_mclk0_mutex); + ret = msm_cdc_pinctrl_select_sleep_state( + pdata->pdm_gpio_p); + if (ret) + pr_err("%s: failed to disable the pri gpios: %d\n", + __func__, ret); + break; + } + atomic_set(&pdata->int_mclk0_enabled, true); + } + mutex_unlock(&pdata->cdc_int_mclk0_mutex); + atomic_inc(&pdata->int_mclk0_rsc_ref); + msm_anlg_cdc_mclk_enable(codec, 1, true); + break; + case 0: + if (atomic_read(&pdata->int_mclk0_rsc_ref) <= 0) + break; + msm_anlg_cdc_mclk_enable(codec, 0, true); + mutex_lock(&pdata->cdc_int_mclk0_mutex); + if ((!atomic_dec_return(&pdata->int_mclk0_rsc_ref)) && + (atomic_read(&pdata->int_mclk0_enabled))) { + pdata->digital_cdc_core_clk.enable = 0; + ret = afe_set_lpass_clock_v2( + AFE_PORT_ID_INT0_MI2S_RX, + &pdata->digital_cdc_core_clk); + if (ret < 0) { + pr_err("%s: failed to disable the CCLK: %d\n", + __func__, ret); + mutex_unlock(&pdata->cdc_int_mclk0_mutex); + break; + } + atomic_set(&pdata->int_mclk0_enabled, false); + } + mutex_unlock(&pdata->cdc_int_mclk0_mutex); + ret = msm_cdc_pinctrl_select_sleep_state(pdata->pdm_gpio_p); + if (ret) + pr_err("%s: failed to disable the pri gpios: %d\n", + __func__, ret); + break; + default: + pr_err("%s: Unexpected input value\n", __func__); + break; + } + return ret; +} + +static int msm_bt_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + /* + * Slimbus_7_Rx/Tx sample rate values should always be in sync (same) + * when used for BT_SCO use case. Return either Rx or Tx sample rate + * value. + */ + switch (bt_fm_cfg[BT_SLIM7].sample_rate) { + case SAMPLING_RATE_96KHZ: + ucontrol->value.integer.value[0] = 5; + break; + case SAMPLING_RATE_88P2KHZ: + ucontrol->value.integer.value[0] = 4; + break; + case SAMPLING_RATE_48KHZ: + ucontrol->value.integer.value[0] = 3; + break; + case SAMPLING_RATE_44P1KHZ: + ucontrol->value.integer.value[0] = 2; + break; + case SAMPLING_RATE_16KHZ: + ucontrol->value.integer.value[0] = 1; + break; + case SAMPLING_RATE_8KHZ: + default: + ucontrol->value.integer.value[0] = 0; + break; + } + pr_debug("%s: sample rate = %d", __func__, + bt_fm_cfg[BT_SLIM7].sample_rate); + + return 0; +} + +static int msm_bt_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (ucontrol->value.integer.value[0]) { + case 1: + bt_fm_cfg[BT_SLIM7].sample_rate = SAMPLING_RATE_16KHZ; + break; + case 2: + bt_fm_cfg[BT_SLIM7].sample_rate = SAMPLING_RATE_44P1KHZ; + break; + case 3: + bt_fm_cfg[BT_SLIM7].sample_rate = SAMPLING_RATE_48KHZ; + break; + case 4: + bt_fm_cfg[BT_SLIM7].sample_rate = SAMPLING_RATE_88P2KHZ; + break; + case 5: + bt_fm_cfg[BT_SLIM7].sample_rate = SAMPLING_RATE_96KHZ; + break; + case 0: + default: + bt_fm_cfg[BT_SLIM7].sample_rate = SAMPLING_RATE_8KHZ; + break; + } + pr_debug("%s: sample rates: slim7_rx = %d, value = %d\n", + __func__, + bt_fm_cfg[BT_SLIM7].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static const struct snd_kcontrol_new msm_snd_controls[] = { + SOC_ENUM_EXT("INT0_MI2S_RX Format", int0_mi2s_rx_format, + int_mi2s_bit_format_get, int_mi2s_bit_format_put), + SOC_ENUM_EXT("INT2_MI2S_TX Format", int2_mi2s_tx_format, + int_mi2s_bit_format_get, int_mi2s_bit_format_put), + SOC_ENUM_EXT("INT3_MI2S_TX Format", int3_mi2s_tx_format, + int_mi2s_bit_format_get, int_mi2s_bit_format_put), + SOC_ENUM_EXT("INT0_MI2S_RX SampleRate", int0_mi2s_rx_sample_rate, + int_mi2s_sample_rate_get, + int_mi2s_sample_rate_put), + SOC_ENUM_EXT("INT2_MI2S_TX SampleRate", int2_mi2s_tx_sample_rate, + int_mi2s_sample_rate_get, + int_mi2s_sample_rate_put), + SOC_ENUM_EXT("INT3_MI2S_TX SampleRate", int3_mi2s_tx_sample_rate, + int_mi2s_sample_rate_get, + int_mi2s_sample_rate_put), + SOC_ENUM_EXT("INT0_MI2S_RX Channels", int0_mi2s_rx_chs, + int_mi2s_ch_get, int_mi2s_ch_put), + SOC_ENUM_EXT("INT2_MI2S_TX Channels", int2_mi2s_tx_chs, + int_mi2s_ch_get, int_mi2s_ch_put), + SOC_ENUM_EXT("INT3_MI2S_TX Channels", int3_mi2s_tx_chs, + int_mi2s_ch_get, int_mi2s_ch_put), + SOC_ENUM_EXT("Loopback MCLK", loopback_mclk_en, + loopback_mclk_get, loopback_mclk_put), + SOC_ENUM_EXT("BT SampleRate", bt_sample_rate, + msm_bt_sample_rate_get, + msm_bt_sample_rate_put), +}; + +static const struct snd_kcontrol_new msm_sdw_controls[] = { + SOC_ENUM_EXT("INT4_MI2S_RX Format", int4_mi2s_rx_format, + int_mi2s_bit_format_get, int_mi2s_bit_format_put), + SOC_ENUM_EXT("INT4_MI2S_RX SampleRate", int4_mi2s_rx_sample_rate, + int_mi2s_sample_rate_get, + int_mi2s_sample_rate_put), + SOC_ENUM_EXT("INT4_MI2S_RX Channels", int4_mi2s_rx_chs, + int_mi2s_ch_get, int_mi2s_ch_put), + SOC_ENUM_EXT("VI_FEED_TX Channels", int5_mi2s_tx_chs, + msm_vi_feed_tx_ch_get, msm_vi_feed_tx_ch_put), +}; + +static int msm_dmic_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct msm_asoc_mach_data *pdata = NULL; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + int ret = 0; + + pdata = snd_soc_card_get_drvdata(codec->component.card); + pr_debug("%s: event = %d\n", __func__, event); + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + ret = msm_cdc_pinctrl_select_active_state(pdata->dmic_gpio_p); + if (ret < 0) { + pr_err("%s: gpio set cannot be activated %sd", + __func__, "dmic_gpio"); + return ret; + } + break; + case SND_SOC_DAPM_POST_PMD: + ret = msm_cdc_pinctrl_select_sleep_state(pdata->dmic_gpio_p); + if (ret < 0) { + pr_err("%s: gpio set cannot be de-activated %sd", + __func__, "dmic_gpio"); + return ret; + } + break; + default: + pr_err("%s: invalid DAPM event %d\n", __func__, event); + return -EINVAL; + } + return 0; +} + +static int msm_int_mclk0_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct msm_asoc_mach_data *pdata = NULL; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + int ret = 0; + + pdata = snd_soc_card_get_drvdata(codec->component.card); + pr_debug("%s: event = %d\n", __func__, event); + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + ret = msm_cdc_pinctrl_select_active_state(pdata->pdm_gpio_p); + if (ret < 0) { + pr_err("%s: gpio set cannot be activated %s\n", + __func__, "int_pdm"); + return ret; + } + msm_int_enable_dig_cdc_clk(codec, 1, true); + msm_anlg_cdc_mclk_enable(codec, 1, true); + break; + case SND_SOC_DAPM_POST_PMD: + pr_debug("%s: mclk_res_ref = %d\n", + __func__, atomic_read(&pdata->int_mclk0_rsc_ref)); + ret = msm_cdc_pinctrl_select_sleep_state(pdata->pdm_gpio_p); + if (ret < 0) { + pr_err("%s: gpio set cannot be de-activated %sd", + __func__, "int_pdm"); + return ret; + } + pr_debug("%s: disabling MCLK\n", __func__); + /* disable the codec mclk config*/ + msm_anlg_cdc_mclk_enable(codec, 0, true); + msm_int_enable_dig_cdc_clk(codec, 0, true); + break; + default: + pr_err("%s: invalid DAPM event %d\n", __func__, event); + return -EINVAL; + } + return 0; +} + +static int int_mi2s_get_port_id(int id) +{ + int afe_port_id; + + switch (id) { + case MSM_BACKEND_DAI_INT0_MI2S_RX: + afe_port_id = AFE_PORT_ID_INT0_MI2S_RX; + break; + case MSM_BACKEND_DAI_INT2_MI2S_TX: + afe_port_id = AFE_PORT_ID_INT2_MI2S_TX; + break; + case MSM_BACKEND_DAI_INT3_MI2S_TX: + afe_port_id = AFE_PORT_ID_INT3_MI2S_TX; + break; + case MSM_BACKEND_DAI_INT4_MI2S_RX: + afe_port_id = AFE_PORT_ID_INT4_MI2S_RX; + break; + case MSM_BACKEND_DAI_INT5_MI2S_TX: + afe_port_id = AFE_PORT_ID_INT5_MI2S_TX; + break; + default: + pr_err("%s: Invalid id: %d\n", __func__, id); + afe_port_id = -EINVAL; + } + + return afe_port_id; +} + +static int int_mi2s_get_index(int port_id) +{ + int index; + + switch (port_id) { + case AFE_PORT_ID_INT0_MI2S_RX: + index = INT0_MI2S; + break; + case AFE_PORT_ID_INT2_MI2S_TX: + index = INT2_MI2S; + break; + case AFE_PORT_ID_INT3_MI2S_TX: + index = INT3_MI2S; + break; + case AFE_PORT_ID_INT4_MI2S_RX: + index = INT4_MI2S; + break; + case AFE_PORT_ID_INT5_MI2S_TX: + index = INT5_MI2S; + break; + default: + pr_err("%s: Invalid port_id: %d\n", __func__, port_id); + index = -EINVAL; + } + + return index; +} + +static u32 get_int_mi2s_bits_per_sample(u32 bit_format) +{ + u32 bit_per_sample; + + switch (bit_format) { + case SNDRV_PCM_FORMAT_S24_3LE: + case SNDRV_PCM_FORMAT_S24_LE: + bit_per_sample = 32; + break; + case SNDRV_PCM_FORMAT_S16_LE: + default: + bit_per_sample = 16; + break; + } + + return bit_per_sample; +} + +static void update_int_mi2s_clk_val(int idx, int stream) +{ + u32 bit_per_sample; + + bit_per_sample = + get_int_mi2s_bits_per_sample(int_mi2s_cfg[idx].bit_format); + int_mi2s_clk[idx].clk_freq_in_hz = + (int_mi2s_cfg[idx].sample_rate * 2 * bit_per_sample); +} + +static int int_mi2s_set_sclk(struct snd_pcm_substream *substream, bool enable) +{ + int ret = 0; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + int port_id = 0; + int index; + + port_id = int_mi2s_get_port_id(rtd->dai_link->id); + if (port_id < 0) { + dev_err(rtd->card->dev, "%s: Invalid port_id\n", __func__); + ret = port_id; + goto done; + } + index = int_mi2s_get_index(port_id); + if (index < 0) { + dev_err(rtd->card->dev, "%s: Invalid port_id\n", __func__); + ret = port_id; + goto done; + } + if (enable) { + update_int_mi2s_clk_val(index, substream->stream); + dev_dbg(rtd->card->dev, "%s: clock rate %ul\n", __func__, + int_mi2s_clk[index].clk_freq_in_hz); + } + + int_mi2s_clk[index].enable = enable; + ret = afe_set_lpass_clock_v2(port_id, + &int_mi2s_clk[index]); + if (ret < 0) { + dev_err(rtd->card->dev, + "%s: afe lpass clock failed for port 0x%x , err:%d\n", + __func__, port_id, ret); + goto done; + } + +done: + return ret; +} + +static int msm_sdw_mi2s_snd_startup(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + int ret = 0; + + pr_debug("%s(): substream = %s stream = %d\n", __func__, + substream->name, substream->stream); + + ret = int_mi2s_set_sclk(substream, true); + if (ret < 0) { + pr_err("%s: failed to enable sclk %d\n", + __func__, ret); + return ret; + } + ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_CBS_CFS); + if (ret < 0) + pr_err("%s: set fmt cpu dai failed; ret=%d\n", __func__, ret); + + return ret; +} + +static void msm_sdw_mi2s_snd_shutdown(struct snd_pcm_substream *substream) +{ + int ret; + + pr_debug("%s(): substream = %s stream = %d\n", __func__, + substream->name, substream->stream); + + ret = int_mi2s_set_sclk(substream, false); + if (ret < 0) + pr_err("%s:clock disable failed; ret=%d\n", __func__, + ret); +} + +static int msm_int_mi2s_snd_startup(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_codec *codec = rtd->codec_dais[ANA_CDC]->codec; + int ret = 0; + struct msm_asoc_mach_data *pdata = NULL; + + pdata = snd_soc_card_get_drvdata(codec->component.card); + pr_debug("%s(): substream = %s stream = %d\n", __func__, + substream->name, substream->stream); + + ret = int_mi2s_set_sclk(substream, true); + if (ret < 0) { + pr_err("%s: failed to enable sclk %d\n", + __func__, ret); + return ret; + } + ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_CBS_CFS); + if (ret < 0) + pr_err("%s: set fmt cpu dai failed; ret=%d\n", __func__, ret); + + return ret; +} + +static void msm_int_mi2s_snd_shutdown(struct snd_pcm_substream *substream) +{ + int ret; + + pr_debug("%s(): substream = %s stream = %d\n", __func__, + substream->name, substream->stream); + + ret = int_mi2s_set_sclk(substream, false); + if (ret < 0) + pr_err("%s:clock disable failed; ret=%d\n", __func__, + ret); +} + +static void *def_msm_int_wcd_mbhc_cal(void) +{ + void *msm_int_wcd_cal; + struct wcd_mbhc_btn_detect_cfg *btn_cfg; + u16 *btn_low, *btn_high; + + msm_int_wcd_cal = kzalloc(WCD_MBHC_CAL_SIZE(WCD_MBHC_DEF_BUTTONS, + WCD_MBHC_DEF_RLOADS), GFP_KERNEL); + if (!msm_int_wcd_cal) + return NULL; + +#define S(X, Y) ((WCD_MBHC_CAL_PLUG_TYPE_PTR(msm_int_wcd_cal)->X) = (Y)) + S(v_hs_max, 1500); +#undef S +#define S(X, Y) ((WCD_MBHC_CAL_BTN_DET_PTR(msm_int_wcd_cal)->X) = (Y)) + S(num_btn, WCD_MBHC_DEF_BUTTONS); +#undef S + + + btn_cfg = WCD_MBHC_CAL_BTN_DET_PTR(msm_int_wcd_cal); + btn_low = btn_cfg->_v_btn_low; + btn_high = ((void *)&btn_cfg->_v_btn_low) + + (sizeof(btn_cfg->_v_btn_low[0]) * btn_cfg->num_btn); + + /* + * In SW we are maintaining two sets of threshold register + * one for current source and another for Micbias. + * all btn_low corresponds to threshold for current source + * all bt_high corresponds to threshold for Micbias + * Below thresholds are based on following resistances + * 0-70 == Button 0 + * 110-180 == Button 1 + * 210-290 == Button 2 + * 360-680 == Button 3 + */ + btn_low[0] = 75; + btn_high[0] = 75; + btn_low[1] = 150; + btn_high[1] = 150; + btn_low[2] = 225; + btn_high[2] = 225; + btn_low[3] = 450; + btn_high[3] = 450; + btn_low[4] = 500; + btn_high[4] = 500; + + return msm_int_wcd_cal; +} + +static int msm_audrx_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_codec *dig_cdc = rtd->codec_dais[DIG_CDC]->codec; + struct snd_soc_codec *ana_cdc = rtd->codec_dais[ANA_CDC]->codec; + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(ana_cdc); + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct msm_asoc_mach_data *pdata = snd_soc_card_get_drvdata(rtd->card); + struct snd_card *card; + int ret = -ENOMEM; + + pr_debug("%s(),dev_name%s\n", __func__, dev_name(cpu_dai->dev)); + + ret = snd_soc_add_codec_controls(ana_cdc, msm_snd_controls, + ARRAY_SIZE(msm_snd_controls)); + if (ret < 0) { + pr_err("%s: add_codec_controls failed: %d\n", + __func__, ret); + return ret; + } + ret = snd_soc_add_codec_controls(ana_cdc, msm_common_snd_controls, + msm_common_snd_controls_size()); + if (ret < 0) { + pr_err("%s: add common snd controls failed: %d\n", + __func__, ret); + return ret; + } + + snd_soc_dapm_new_controls(dapm, msm_int_dapm_widgets, + ARRAY_SIZE(msm_int_dapm_widgets)); + + snd_soc_dapm_ignore_suspend(dapm, "Handset Mic"); + snd_soc_dapm_ignore_suspend(dapm, "Headset Mic"); + snd_soc_dapm_ignore_suspend(dapm, "Secondary Mic"); + snd_soc_dapm_ignore_suspend(dapm, "Digital Mic1"); + snd_soc_dapm_ignore_suspend(dapm, "Digital Mic2"); + + snd_soc_dapm_ignore_suspend(dapm, "EAR"); + snd_soc_dapm_ignore_suspend(dapm, "HEADPHONE"); + snd_soc_dapm_ignore_suspend(dapm, "SPK_OUT"); + snd_soc_dapm_ignore_suspend(dapm, "AMIC1"); + snd_soc_dapm_ignore_suspend(dapm, "AMIC2"); + snd_soc_dapm_ignore_suspend(dapm, "AMIC3"); + snd_soc_dapm_sync(dapm); + + dapm = snd_soc_codec_get_dapm(dig_cdc); + snd_soc_dapm_ignore_suspend(dapm, "DMIC1"); + snd_soc_dapm_ignore_suspend(dapm, "DMIC2"); + snd_soc_dapm_ignore_suspend(dapm, "DMIC3"); + snd_soc_dapm_ignore_suspend(dapm, "DMIC4"); + + snd_soc_dapm_sync(dapm); + + msm_anlg_cdc_spk_ext_pa_cb(enable_spk_ext_pa, ana_cdc); + msm_dig_cdc_hph_comp_cb(msm_config_hph_compander_gpio, dig_cdc); + + card = rtd->card->snd_card; + if (!codec_root) + codec_root = snd_info_create_subdir(card->module, "codecs", + card->proc_root); + if (!codec_root) { + pr_debug("%s: Cannot create codecs module entry\n", + __func__); + goto done; + } + pdata->codec_root = codec_root; + msm_dig_codec_info_create_codec_entry(codec_root, dig_cdc); + msm_anlg_codec_info_create_codec_entry(codec_root, ana_cdc); +done: + msm_set_codec_reg_done(true); + return 0; +} + +static int msm_sdw_audrx_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_dapm_context *dapm = + snd_soc_codec_get_dapm(codec); + struct msm_asoc_mach_data *pdata = snd_soc_card_get_drvdata(rtd->card); + struct snd_soc_component *aux_comp; + struct snd_card *card; + + snd_soc_add_codec_controls(codec, msm_sdw_controls, + ARRAY_SIZE(msm_sdw_controls)); + + snd_soc_dapm_ignore_suspend(dapm, "AIF1_SDW Playback"); + snd_soc_dapm_ignore_suspend(dapm, "VIfeed_SDW"); + snd_soc_dapm_ignore_suspend(dapm, "SPK1 OUT"); + snd_soc_dapm_ignore_suspend(dapm, "SPK2 OUT"); + snd_soc_dapm_ignore_suspend(dapm, "AIF1_SDW VI"); + snd_soc_dapm_ignore_suspend(dapm, "VIINPUT_SDW"); + + snd_soc_dapm_sync(dapm); + + /* + * Send speaker configuration only for WSA8810. + * Default configuration is for WSA8815. + */ + pr_debug("%s: Number of aux devices: %d\n", + __func__, rtd->card->num_aux_devs); + if (rtd->card->num_aux_devs && + !list_empty(&rtd->card->aux_comp_list)) { + aux_comp = list_first_entry(&rtd->card->aux_comp_list, + struct snd_soc_component, list_aux); + if (!strcmp(aux_comp->name, WSA8810_NAME_1) || + !strcmp(aux_comp->name, WSA8810_NAME_2)) { + msm_sdw_set_spkr_mode(rtd->codec, SPKR_MODE_1); + msm_sdw_set_spkr_gain_offset(rtd->codec, + RX_GAIN_OFFSET_M1P5_DB); + } + } + card = rtd->card->snd_card; + if (!codec_root) + codec_root = snd_info_create_subdir(card->module, "codecs", + card->proc_root); + if (!codec_root) { + pr_debug("%s: Cannot create codecs module entry\n", + __func__); + goto done; + } + pdata->codec_root = codec_root; + msm_sdw_codec_info_create_codec_entry(codec_root, codec); +done: + return 0; +} + +static int msm_wcn_init(struct snd_soc_pcm_runtime *rtd) +{ + unsigned int rx_ch[WCN_CDC_SLIM_RX_CH_MAX] = {157, 158}; + unsigned int tx_ch[WCN_CDC_SLIM_TX_CH_MAX] = {159, 160, 161}; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + + return snd_soc_dai_set_channel_map(codec_dai, ARRAY_SIZE(tx_ch), + tx_ch, ARRAY_SIZE(rx_ch), rx_ch); +} + +static int msm_wcn_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai_link *dai_link = rtd->dai_link; + u32 rx_ch[WCN_CDC_SLIM_RX_CH_MAX], tx_ch[WCN_CDC_SLIM_TX_CH_MAX]; + u32 rx_ch_cnt = 0, tx_ch_cnt = 0; + int ret; + + dev_dbg(rtd->dev, "%s: %s_tx_dai_id_%d\n", __func__, + codec_dai->name, codec_dai->id); + ret = snd_soc_dai_get_channel_map(codec_dai, + &tx_ch_cnt, tx_ch, &rx_ch_cnt, rx_ch); + if (ret) { + dev_err(rtd->dev, + "%s: failed to get BTFM codec chan map\n, err:%d\n", + __func__, ret); + goto exit; + } + + dev_dbg(rtd->dev, "%s: tx_ch_cnt(%d) id %d\n", + __func__, tx_ch_cnt, dai_link->id); + + ret = snd_soc_dai_set_channel_map(cpu_dai, + tx_ch_cnt, tx_ch, rx_ch_cnt, rx_ch); + if (ret) + dev_err(rtd->dev, "%s: failed to set cpu chan map, err:%d\n", + __func__, ret); + +exit: + return ret; +} + +static unsigned int tdm_param_set_slot_mask(u16 port_id, int slot_width, + int slots) +{ + unsigned int slot_mask = 0; + int i, j; + unsigned int *slot_offset; + + for (i = TDM_0; i < TDM_PORT_MAX; i++) { + slot_offset = tdm_slot_offset[i]; + + for (j = 0; j < TDM_SLOT_OFFSET_MAX; j++) { + if (slot_offset[j] != AFE_SLOT_MAPPING_OFFSET_INVALID) + slot_mask |= + (1 << ((slot_offset[j] * 8) / slot_width)); + else + break; + } + } + + return slot_mask; +} + +static int msm_tdm_snd_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + int ret = 0; + int channels, slot_width, slots; + unsigned int slot_mask; + unsigned int *slot_offset; + int offset_channels = 0; + int i; + + pr_debug("%s: dai id = 0x%x\n", __func__, cpu_dai->id); + + channels = params_channels(params); + switch (channels) { + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + case 8: + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S32_LE: + case SNDRV_PCM_FORMAT_S24_LE: + case SNDRV_PCM_FORMAT_S16_LE: + /* + * up to 8 channels HW config should + * use 32 bit slot width for max support of + * stream bit width. (slot_width > bit_width) + */ + slot_width = 32; + break; + default: + pr_err("%s: invalid param format 0x%x\n", + __func__, params_format(params)); + return -EINVAL; + } + slots = 8; + slot_mask = tdm_param_set_slot_mask(cpu_dai->id, + slot_width, + slots); + if (!slot_mask) { + pr_err("%s: invalid slot_mask 0x%x\n", + __func__, slot_mask); + return -EINVAL; + } + break; + default: + pr_err("%s: invalid param channels %d\n", + __func__, channels); + return -EINVAL; + } + /* currently only supporting TDM_RX_0 and TDM_TX_0 */ + switch (cpu_dai->id) { + case AFE_PORT_ID_PRIMARY_TDM_RX: + case AFE_PORT_ID_SECONDARY_TDM_RX: + case AFE_PORT_ID_TERTIARY_TDM_RX: + case AFE_PORT_ID_QUATERNARY_TDM_RX: + case AFE_PORT_ID_QUINARY_TDM_RX: + case AFE_PORT_ID_PRIMARY_TDM_TX: + case AFE_PORT_ID_SECONDARY_TDM_TX: + case AFE_PORT_ID_TERTIARY_TDM_TX: + case AFE_PORT_ID_QUATERNARY_TDM_TX: + case AFE_PORT_ID_QUINARY_TDM_TX: + slot_offset = tdm_slot_offset[TDM_0]; + break; + default: + pr_err("%s: dai id 0x%x not supported\n", + __func__, cpu_dai->id); + return -EINVAL; + } + + for (i = 0; i < TDM_SLOT_OFFSET_MAX; i++) { + if (slot_offset[i] != AFE_SLOT_MAPPING_OFFSET_INVALID) + offset_channels++; + else + break; + } + + if (offset_channels == 0) { + pr_err("%s: slot offset not supported, offset_channels %d\n", + __func__, offset_channels); + return -EINVAL; + } + + if (channels > offset_channels) { + pr_err("%s: channels %d exceed offset_channels %d\n", + __func__, channels, offset_channels); + return -EINVAL; + } + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0, slot_mask, + slots, slot_width); + if (ret < 0) { + pr_err("%s: failed to set tdm slot, err:%d\n", + __func__, ret); + goto end; + } + + ret = snd_soc_dai_set_channel_map(cpu_dai, 0, NULL, + channels, slot_offset); + if (ret < 0) { + pr_err("%s: failed to set channel map, err:%d\n", + __func__, ret); + goto end; + } + } else { + ret = snd_soc_dai_set_tdm_slot(cpu_dai, slot_mask, 0, + slots, slot_width); + if (ret < 0) { + pr_err("%s: failed to set tdm slot, err:%d\n", + __func__, ret); + goto end; + } + + ret = snd_soc_dai_set_channel_map(cpu_dai, channels, + slot_offset, 0, NULL); + if (ret < 0) { + pr_err("%s: failed to set channel map, err:%d\n", + __func__, ret); + goto end; + } + } +end: + return ret; +} + +static int msm_snd_card_late_probe(struct snd_soc_card *card) +{ + const char *be_dl_name = LPASS_BE_INT0_MI2S_RX; + struct snd_soc_codec *ana_cdc; + struct snd_soc_pcm_runtime *rtd; + int ret = 0; + + rtd = snd_soc_get_pcm_runtime(card, be_dl_name); + if (!rtd) { + dev_err(card->dev, + "%s: snd_soc_get_pcm_runtime for %s failed!\n", + __func__, be_dl_name); + return -EINVAL; + } + + ana_cdc = rtd->codec_dais[ANA_CDC]->codec; + mbhc_cfg_ptr->calibration = def_msm_int_wcd_mbhc_cal(); + if (!mbhc_cfg_ptr->calibration) + return -ENOMEM; + + ret = msm_anlg_cdc_hs_detect(ana_cdc, mbhc_cfg_ptr); + if (ret) { + dev_err(card->dev, + "%s: msm_anlg_cdc_hs_detect failed\n", __func__); + kfree(mbhc_cfg_ptr->calibration); + } + + return ret; +} + +static struct snd_soc_ops msm_tdm_be_ops = { + .hw_params = msm_tdm_snd_hw_params +}; + +static struct snd_soc_ops msm_wcn_ops = { + .hw_params = msm_wcn_hw_params, +}; + +static struct snd_soc_ops msm_mi2s_be_ops = { + .startup = msm_mi2s_snd_startup, + .shutdown = msm_mi2s_snd_shutdown, +}; + +static struct snd_soc_ops msm_aux_pcm_be_ops = { + .startup = msm_aux_pcm_snd_startup, + .shutdown = msm_aux_pcm_snd_shutdown, +}; + +static struct snd_soc_ops msm_int_mi2s_be_ops = { + .startup = msm_int_mi2s_snd_startup, + .shutdown = msm_int_mi2s_snd_shutdown, +}; + +static struct snd_soc_ops msm_sdw_mi2s_be_ops = { + .startup = msm_sdw_mi2s_snd_startup, + .shutdown = msm_sdw_mi2s_snd_shutdown, +}; + +static int msm_fe_qos_prepare(struct snd_pcm_substream *substream) +{ + cpumask_t mask; + + if (pm_qos_request_active(&substream->latency_pm_qos_req)) + pm_qos_remove_request(&substream->latency_pm_qos_req); + + cpumask_clear(&mask); + cpumask_set_cpu(1, &mask); /* affine to core 1 */ + cpumask_set_cpu(2, &mask); /* affine to core 2 */ + cpumask_copy(&substream->latency_pm_qos_req.cpus_affine, &mask); + + substream->latency_pm_qos_req.type = PM_QOS_REQ_AFFINE_CORES; + + pm_qos_add_request(&substream->latency_pm_qos_req, + PM_QOS_CPU_DMA_LATENCY, + MSM_LL_QOS_VALUE); + return 0; +} + +static struct snd_soc_ops msm_fe_qos_ops = { + .prepare = msm_fe_qos_prepare, +}; + +struct snd_soc_dai_link_component dlc_rx1[] = { + { + .of_node = NULL, + .dai_name = "msm_dig_cdc_dai_rx1", + }, + { + .of_node = NULL, + .dai_name = "msm_anlg_cdc_i2s_rx1", + }, +}; + +struct snd_soc_dai_link_component dlc_tx1[] = { + { + .of_node = NULL, + .dai_name = "msm_dig_cdc_dai_tx1", + }, + { + .of_node = NULL, + .dai_name = "msm_anlg_cdc_i2s_tx1", + }, +}; + +struct snd_soc_dai_link_component dlc_tx2[] = { + { + .of_node = NULL, + .dai_name = "msm_dig_cdc_dai_tx2", + }, + { + .of_node = NULL, + .dai_name = "msm_anlg_cdc_i2s_tx2", + }, +}; + +/* Digital audio interface glue - connects codec <---> CPU */ +static struct snd_soc_dai_link msm_int_dai[] = { + /* FrontEnd DAI Links */ + {/* hw:x,0 */ + .name = MSM_DAILINK_NAME(Media1), + .stream_name = "MultiMedia1", + .cpu_dai_name = "MultiMedia1", + .platform_name = "msm-pcm-dsp.0", + .dynamic = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + /* this dai link has playback support */ + .ignore_pmdown_time = 1, + .id = MSM_FRONTEND_DAI_MULTIMEDIA1 + }, + {/* hw:x,1 */ + .name = MSM_DAILINK_NAME(Media2), + .stream_name = "MultiMedia2", + .cpu_dai_name = "MultiMedia2", + .platform_name = "msm-pcm-dsp.0", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .ignore_suspend = 1, + /* this dai link has playback support */ + .ignore_pmdown_time = 1, + .id = MSM_FRONTEND_DAI_MULTIMEDIA2, + }, + {/* hw:x,2 */ + .name = "VoiceMMode1", + .stream_name = "VoiceMMode1", + .cpu_dai_name = "VoiceMMode1", + .platform_name = "msm-pcm-voice", + .dynamic = 1, + .dpcm_capture = 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", + .id = MSM_FRONTEND_DAI_VOICEMMODE1, + }, + {/* hw:x,3 */ + .name = "MSM VoIP", + .stream_name = "VoIP", + .cpu_dai_name = "VoIP", + .platform_name = "msm-voip-dsp", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + /* this dai link has playback support */ + .ignore_pmdown_time = 1, + .id = MSM_FRONTEND_DAI_VOIP, + }, + {/* hw:x,4 */ + .name = MSM_DAILINK_NAME(ULL), + .stream_name = "ULL", + .cpu_dai_name = "MultiMedia3", + .platform_name = "msm-pcm-dsp.2", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + /* this dai link has playback support */ + .ignore_pmdown_time = 1, + .id = MSM_FRONTEND_DAI_MULTIMEDIA3, + }, + /* Hostless PCM purpose */ + {/* hw:x,5 */ + .name = "INT4 MI2S_RX Hostless", + .stream_name = "INT4 MI2S_RX Hostless", + .cpu_dai_name = "INT4_MI2S_RX_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .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, + /* this dailink has playback support */ + .ignore_pmdown_time = 1, + /* This dainlink has MI2S support */ + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + {/* hw:x,6 */ + .name = "MSM AFE-PCM RX", + .stream_name = "AFE-PROXY RX", + .cpu_dai_name = "msm-dai-q6-dev.241", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .platform_name = "msm-pcm-afe", + .ignore_suspend = 1, + /* this dai link has playback support */ + .ignore_pmdown_time = 1, + }, + {/* hw:x,7 */ + .name = "MSM AFE-PCM TX", + .stream_name = "AFE-PROXY TX", + .cpu_dai_name = "msm-dai-q6-dev.240", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .platform_name = "msm-pcm-afe", + .ignore_suspend = 1, + }, + {/* hw:x,8 */ + .name = MSM_DAILINK_NAME(Compress1), + .stream_name = "Compress1", + .cpu_dai_name = "MultiMedia4", + .platform_name = "msm-compress-dsp", + .async_ops = ASYNC_DPCM_SND_SOC_HW_PARAMS, + .dynamic = 1, + .dpcm_capture = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dai link has playback support */ + .id = MSM_FRONTEND_DAI_MULTIMEDIA4, + }, + {/* hw:x,9*/ + .name = "AUXPCM Hostless", + .stream_name = "AUXPCM Hostless", + .cpu_dai_name = "AUXPCM_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_capture = 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, + /* this dai link has playback support */ + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + {/* hw:x,10 */ + .name = "SLIMBUS_1 Hostless", + .stream_name = "SLIMBUS_1 Hostless", + .cpu_dai_name = "SLIMBUS1_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_capture = 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, /* dai link has playback support */ + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + {/* hw:x,11 */ + .name = "INT3 MI2S_TX Hostless", + .stream_name = "INT3 MI2S_TX Hostless", + .cpu_dai_name = "INT3_MI2S_TX_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + {/* hw:x,12 */ + .name = "SLIMBUS_7 Hostless", + .stream_name = "SLIMBUS_7 Hostless", + .cpu_dai_name = "SLIMBUS7_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_capture = 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, /* dai link has playback support */ + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + {/* hw:x,13 */ + .name = MSM_DAILINK_NAME(LowLatency), + .stream_name = "MultiMedia5", + .cpu_dai_name = "MultiMedia5", + .platform_name = "msm-pcm-dsp.1", + .dynamic = 1, + .dpcm_capture = 1, + .dpcm_playback = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .ignore_suspend = 1, + /* this dai link has playback support */ + .ignore_pmdown_time = 1, + .id = MSM_FRONTEND_DAI_MULTIMEDIA5, + .ops = &msm_fe_qos_ops, + }, + /* LSM FE */ + {/* hw:x,14 */ + .name = "Listen 1 Audio Service", + .stream_name = "Listen 1 Audio Service", + .cpu_dai_name = "LSM1", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST }, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .id = MSM_FRONTEND_DAI_LSM1, + }, + {/* hw:x,15 */ + .name = MSM_DAILINK_NAME(Compress2), + .stream_name = "Compress2", + .cpu_dai_name = "MultiMedia7", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_capture = 1, + .dpcm_playback = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .ignore_suspend = 1, + .id = MSM_FRONTEND_DAI_MULTIMEDIA7, + }, + {/* hw:x,16 */ + .name = MSM_DAILINK_NAME(MultiMedia10), + .stream_name = "MultiMedia10", + .cpu_dai_name = "MultiMedia10", + .platform_name = "msm-pcm-dsp.1", + .dynamic = 1, + .dpcm_capture = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dai link has playback support */ + .id = MSM_FRONTEND_DAI_MULTIMEDIA10, + }, + {/* hw:x,17 */ + .name = MSM_DAILINK_NAME(ULL_NOIRQ), + .stream_name = "MM_NOIRQ", + .cpu_dai_name = "MultiMedia8", + .platform_name = "msm-pcm-dsp-noirq", + .dynamic = 1, + .dpcm_capture = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dai link has playback support */ + .id = MSM_FRONTEND_DAI_MULTIMEDIA8, + .ops = &msm_fe_qos_ops, + }, + {/* hw:x,18 */ + .name = "HDMI_RX_HOSTLESS", + .stream_name = "HDMI_RX_HOSTLESS", + .cpu_dai_name = "HDMI_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .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", + }, + {/* hw:x,19 */ + .name = "VoiceMMode2", + .stream_name = "VoiceMMode2", + .cpu_dai_name = "VoiceMMode2", + .platform_name = "msm-pcm-voice", + .dynamic = 1, + .dpcm_capture = 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", + .id = MSM_FRONTEND_DAI_VOICEMMODE2, + }, + {/* hw:x,20 */ + .name = "Listen 2 Audio Service", + .stream_name = "Listen 2 Audio Service", + .cpu_dai_name = "LSM2", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST }, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .id = MSM_FRONTEND_DAI_LSM2, + }, + {/* hw:x,21 */ + .name = "Listen 3 Audio Service", + .stream_name = "Listen 3 Audio Service", + .cpu_dai_name = "LSM3", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST }, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .id = MSM_FRONTEND_DAI_LSM3, + }, + {/* hw:x,22 */ + .name = "Listen 4 Audio Service", + .stream_name = "Listen 4 Audio Service", + .cpu_dai_name = "LSM4", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST }, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .id = MSM_FRONTEND_DAI_LSM4, + }, + {/* hw:x,23 */ + .name = "Listen 5 Audio Service", + .stream_name = "Listen 5 Audio Service", + .cpu_dai_name = "LSM5", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST }, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .id = MSM_FRONTEND_DAI_LSM5, + }, + {/* hw:x,24 */ + .name = "Listen 6 Audio Service", + .stream_name = "Listen 6 Audio Service", + .cpu_dai_name = "LSM6", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST }, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .id = MSM_FRONTEND_DAI_LSM6 + }, + {/* hw:x,25 */ + .name = "Listen 7 Audio Service", + .stream_name = "Listen 7 Audio Service", + .cpu_dai_name = "LSM7", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST }, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .id = MSM_FRONTEND_DAI_LSM7, + }, + {/* hw:x,26 */ + .name = "Listen 8 Audio Service", + .stream_name = "Listen 8 Audio Service", + .cpu_dai_name = "LSM8", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST }, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .id = MSM_FRONTEND_DAI_LSM8, + }, + {/* hw:x,27 */ + .name = MSM_DAILINK_NAME(Media9), + .stream_name = "MultiMedia9", + .cpu_dai_name = "MultiMedia9", + .platform_name = "msm-pcm-dsp.0", + .dynamic = 1, + .dpcm_capture = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dai link has playback support */ + .id = MSM_FRONTEND_DAI_MULTIMEDIA9, + }, + {/* hw:x,28 */ + .name = MSM_DAILINK_NAME(Compress4), + .stream_name = "Compress4", + .cpu_dai_name = "MultiMedia11", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_capture = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dai link has playback support */ + .id = MSM_FRONTEND_DAI_MULTIMEDIA11, + }, + {/* hw:x,29 */ + .name = MSM_DAILINK_NAME(Compress5), + .stream_name = "Compress5", + .cpu_dai_name = "MultiMedia12", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_capture = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dai link has playback support */ + .id = MSM_FRONTEND_DAI_MULTIMEDIA12, + }, + {/* hw:x,30 */ + .name = MSM_DAILINK_NAME(Compress6), + .stream_name = "Compress6", + .cpu_dai_name = "MultiMedia13", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_capture = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dai link has playback support */ + .id = MSM_FRONTEND_DAI_MULTIMEDIA13, + }, + {/* hw:x,31 */ + .name = MSM_DAILINK_NAME(Compress7), + .stream_name = "Compress7", + .cpu_dai_name = "MultiMedia14", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_capture = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dai link has playback support */ + .id = MSM_FRONTEND_DAI_MULTIMEDIA14, + }, + {/* hw:x,32 */ + .name = MSM_DAILINK_NAME(Compress8), + .stream_name = "Compress8", + .cpu_dai_name = "MultiMedia15", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_capture = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dai link has playback support */ + .id = MSM_FRONTEND_DAI_MULTIMEDIA15, + }, + {/* hw:x,33 */ + .name = MSM_DAILINK_NAME(ULL_NOIRQ_2), + .stream_name = "MM_NOIRQ_2", + .cpu_dai_name = "MultiMedia16", + .platform_name = "msm-pcm-dsp-noirq", + .dynamic = 1, + .dpcm_capture = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dai link has playback support */ + .id = MSM_FRONTEND_DAI_MULTIMEDIA16, + }, + {/* hw:x,34 */ + .name = "SLIMBUS_8 Hostless", + .stream_name = "SLIMBUS8_HOSTLESS Capture", + .cpu_dai_name = "SLIMBUS8_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + {/* hw:x,35 */ + .name = "Primary MI2S_RX Hostless", + .stream_name = "Primary MI2S_RX Hostless", + .cpu_dai_name = "PRI_MI2S_RX_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .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, + /* this dailink has playback support */ + .ignore_pmdown_time = 1, + /* This dainlink has MI2S support */ + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + {/* hw:x,36 */ + .name = "Secondary MI2S_RX Hostless", + .stream_name = "Secondary MI2S_RX Hostless", + .cpu_dai_name = "SEC_MI2S_RX_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .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, + /* this dailink has playback support */ + .ignore_pmdown_time = 1, + /* This dainlink has MI2S support */ + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + {/* hw:x,37 */ + .name = "Tertiary MI2S_RX Hostless", + .stream_name = "Tertiary MI2S_RX Hostless", + .cpu_dai_name = "TERT_MI2S_RX_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .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, + /* this dailink has playback support */ + .ignore_pmdown_time = 1, + /* This dainlink has MI2S support */ + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + {/* hw:x,38 */ + .name = "INT0 MI2S_RX Hostless", + .stream_name = "INT0 MI2S_RX Hostless", + .cpu_dai_name = "INT0_MI2S_RX_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .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, + /* this dailink has playback support */ + .ignore_pmdown_time = 1, + /* This dainlink has MI2S support */ + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + {/* hw:x,39 */ + .name = "SDM660 HFP TX", + .stream_name = "MultiMedia6", + .cpu_dai_name = "MultiMedia6", + .platform_name = "msm-pcm-loopback", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .ignore_suspend = 1, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_pmdown_time = 1, + .id = MSM_FRONTEND_DAI_MULTIMEDIA6, + }, +}; + + +static struct snd_soc_dai_link msm_int_wsa_dai[] = { + {/* hw:x,40 */ + .name = LPASS_BE_INT5_MI2S_TX, + .stream_name = "INT5_mi2s Capture", + .cpu_dai_name = "msm-dai-q6-mi2s.12", + .platform_name = "msm-pcm-hostless", + .codec_name = "msm_sdw_codec", + .codec_dai_name = "msm_sdw_vifeedback", + .id = MSM_BACKEND_DAI_INT5_MI2S_TX, + .be_hw_params_fixup = int_mi2s_be_hw_params_fixup, + .ops = &msm_sdw_mi2s_be_ops, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .dpcm_capture = 1, + }, +}; + +static struct snd_soc_dai_link msm_int_compress_capture_dai[] = { + {/* hw:x,41 */ + .name = "Compress9", + .stream_name = "Compress9", + .cpu_dai_name = "MultiMedia17", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .id = MSM_FRONTEND_DAI_MULTIMEDIA17, + }, + {/* hw:x,42 */ + .name = "Compress10", + .stream_name = "Compress10", + .cpu_dai_name = "MultiMedia18", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .id = MSM_FRONTEND_DAI_MULTIMEDIA18, + }, + {/* hw:x,43 */ + .name = "Compress11", + .stream_name = "Compress11", + .cpu_dai_name = "MultiMedia19", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .id = MSM_FRONTEND_DAI_MULTIMEDIA19, + }, + {/* hw:x,44 */ + .name = "Compress12", + .stream_name = "Compress12", + .cpu_dai_name = "MultiMedia28", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .id = MSM_FRONTEND_DAI_MULTIMEDIA28, + }, + {/* hw:x,45 */ + .name = "Compress13", + .stream_name = "Compress13", + .cpu_dai_name = "MultiMedia29", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .id = MSM_FRONTEND_DAI_MULTIMEDIA29, + }, +}; + +static struct snd_soc_dai_link msm_int_be_dai[] = { + /* Backend I2S DAI Links */ + { + .name = LPASS_BE_INT0_MI2S_RX, + .stream_name = "INT0 MI2S Playback", + .cpu_dai_name = "msm-dai-q6-mi2s.7", + .platform_name = "msm-pcm-routing", + .codecs = dlc_rx1, + .num_codecs = CODECS_MAX, + .no_pcm = 1, + .dpcm_playback = 1, + .async_ops = ASYNC_DPCM_SND_SOC_PREPARE | + ASYNC_DPCM_SND_SOC_HW_PARAMS, + .id = MSM_BACKEND_DAI_INT0_MI2S_RX, + .init = &msm_audrx_init, + .be_hw_params_fixup = int_mi2s_be_hw_params_fixup, + .ops = &msm_int_mi2s_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_INT3_MI2S_TX, + .stream_name = "INT3 MI2S Capture", + .cpu_dai_name = "msm-dai-q6-mi2s.10", + .platform_name = "msm-pcm-routing", + .codecs = dlc_tx1, + .num_codecs = CODECS_MAX, + .no_pcm = 1, + .dpcm_capture = 1, + .async_ops = ASYNC_DPCM_SND_SOC_PREPARE | + ASYNC_DPCM_SND_SOC_HW_PARAMS, + .id = MSM_BACKEND_DAI_INT3_MI2S_TX, + .be_hw_params_fixup = int_mi2s_be_hw_params_fixup, + .ops = &msm_int_mi2s_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_INT2_MI2S_TX, + .stream_name = "INT2 MI2S Capture", + .cpu_dai_name = "msm-dai-q6-mi2s.9", + .platform_name = "msm-pcm-routing", + .codecs = dlc_tx2, + .num_codecs = CODECS_MAX, + .no_pcm = 1, + .dpcm_capture = 1, + .async_ops = ASYNC_DPCM_SND_SOC_PREPARE | + ASYNC_DPCM_SND_SOC_HW_PARAMS, + .id = MSM_BACKEND_DAI_INT2_MI2S_TX, + .be_hw_params_fixup = int_mi2s_be_hw_params_fixup, + .ops = &msm_int_mi2s_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_AFE_PCM_RX, + .stream_name = "AFE Playback", + .cpu_dai_name = "msm-dai-q6-dev.224", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_AFE_PCM_RX, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_AFE_PCM_TX, + .stream_name = "AFE Capture", + .cpu_dai_name = "msm-dai-q6-dev.225", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_AFE_PCM_TX, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ignore_suspend = 1, + }, + /* Incall Record Uplink BACK END DAI Link */ + { + .name = LPASS_BE_INCALL_RECORD_TX, + .stream_name = "Voice Uplink Capture", + .cpu_dai_name = "msm-dai-q6-dev.32772", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_INCALL_RECORD_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + }, + /* Incall Record Downlink BACK END DAI Link */ + { + .name = LPASS_BE_INCALL_RECORD_RX, + .stream_name = "Voice Downlink Capture", + .cpu_dai_name = "msm-dai-q6-dev.32771", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_INCALL_RECORD_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + }, + /* Incall Music BACK END DAI Link */ + { + .name = LPASS_BE_VOICE_PLAYBACK_TX, + .stream_name = "Voice Farend Playback", + .cpu_dai_name = "msm-dai-q6-dev.32773", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_VOICE_PLAYBACK_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + }, + /* Incall Music 2 BACK END DAI Link */ + { + .name = LPASS_BE_VOICE2_PLAYBACK_TX, + .stream_name = "Voice2 Farend Playback", + .cpu_dai_name = "msm-dai-q6-dev.32770", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_VOICE2_PLAYBACK_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + }, + { + .name = LPASS_BE_USB_AUDIO_RX, + .stream_name = "USB Audio Playback", + .cpu_dai_name = "msm-dai-q6-dev.28672", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_USB_RX, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_USB_AUDIO_TX, + .stream_name = "USB Audio Capture", + .cpu_dai_name = "msm-dai-q6-dev.28673", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_USB_TX, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_PRI_TDM_RX_0, + .stream_name = "Primary TDM0 Playback", + .cpu_dai_name = "msm-dai-q6-tdm.36864", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_PRI_TDM_RX_0, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ops = &msm_tdm_be_ops, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + }, + { + .name = LPASS_BE_PRI_TDM_TX_0, + .stream_name = "Primary TDM0 Capture", + .cpu_dai_name = "msm-dai-q6-tdm.36865", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_PRI_TDM_TX_0, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ops = &msm_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SEC_TDM_RX_0, + .stream_name = "Secondary TDM0 Playback", + .cpu_dai_name = "msm-dai-q6-tdm.36880", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SEC_TDM_RX_0, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ops = &msm_tdm_be_ops, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + }, + { + .name = LPASS_BE_SEC_TDM_TX_0, + .stream_name = "Secondary TDM0 Capture", + .cpu_dai_name = "msm-dai-q6-tdm.36881", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_SEC_TDM_TX_0, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ops = &msm_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_TERT_TDM_RX_0, + .stream_name = "Tertiary TDM0 Playback", + .cpu_dai_name = "msm-dai-q6-tdm.36896", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_TERT_TDM_RX_0, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ops = &msm_tdm_be_ops, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + }, + { + .name = LPASS_BE_TERT_TDM_TX_0, + .stream_name = "Tertiary TDM0 Capture", + .cpu_dai_name = "msm-dai-q6-tdm.36897", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_TERT_TDM_TX_0, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ops = &msm_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_QUAT_TDM_RX_0, + .stream_name = "Quaternary TDM0 Playback", + .cpu_dai_name = "msm-dai-q6-tdm.36912", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_QUAT_TDM_RX_0, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ops = &msm_tdm_be_ops, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + }, + { + .name = LPASS_BE_QUAT_TDM_TX_0, + .stream_name = "Quaternary TDM0 Capture", + .cpu_dai_name = "msm-dai-q6-tdm.36913", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_QUAT_TDM_TX_0, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ops = &msm_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_QUIN_TDM_RX_0, + .stream_name = "Quinary TDM0 Playback", + .cpu_dai_name = "msm-dai-q6-tdm.36928", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_QUIN_TDM_RX_0, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ops = &msm_tdm_be_ops, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + }, + { + .name = LPASS_BE_QUIN_TDM_TX_0, + .stream_name = "Quinary TDM0 Capture", + .cpu_dai_name = "msm-dai-q6-tdm.36929", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_QUIN_TDM_TX_0, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ops = &msm_tdm_be_ops, + .ignore_suspend = 1, + }, +}; + +static struct snd_soc_dai_link msm_mi2s_be_dai_links[] = { + { + .name = LPASS_BE_PRI_MI2S_RX, + .stream_name = "Primary MI2S Playback", + .cpu_dai_name = "msm-dai-q6-mi2s.0", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_PRI_MI2S_RX, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + }, + { + .name = LPASS_BE_PRI_MI2S_TX, + .stream_name = "Primary MI2S Capture", + .cpu_dai_name = "msm-dai-q6-mi2s.0", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_PRI_MI2S_TX, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SEC_MI2S_RX, + .stream_name = "Secondary MI2S Playback", + .cpu_dai_name = "msm-dai-q6-mi2s.1", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SECONDARY_MI2S_RX, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + }, + { + .name = LPASS_BE_SEC_MI2S_TX, + .stream_name = "Secondary MI2S Capture", + .cpu_dai_name = "msm-dai-q6-mi2s.1", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_SECONDARY_MI2S_TX, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_TERT_MI2S_RX, + .stream_name = "Tertiary MI2S Playback", + .cpu_dai_name = "msm-dai-q6-mi2s.2", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_TERTIARY_MI2S_RX, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + }, + { + .name = LPASS_BE_TERT_MI2S_TX, + .stream_name = "Tertiary MI2S Capture", + .cpu_dai_name = "msm-dai-q6-mi2s.2", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_TERTIARY_MI2S_TX, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_QUAT_MI2S_RX, + .stream_name = "Quaternary MI2S Playback", + .cpu_dai_name = "msm-dai-q6-mi2s.3", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_QUATERNARY_MI2S_RX, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + }, + { + .name = LPASS_BE_QUAT_MI2S_TX, + .stream_name = "Quaternary MI2S Capture", + .cpu_dai_name = "msm-dai-q6-mi2s.3", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_QUIN_MI2S_RX, + .stream_name = "Quinary MI2S Playback", + .cpu_dai_name = "msm-dai-q6-mi2s.4", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_QUINARY_MI2S_RX, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + }, + { + .name = LPASS_BE_QUIN_MI2S_TX, + .stream_name = "Quinary MI2S Capture", + .cpu_dai_name = "msm-dai-q6-mi2s.4", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_QUINARY_MI2S_TX, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + }, +}; + +static struct snd_soc_dai_link msm_auxpcm_be_dai_links[] = { + /* Primary AUX PCM Backend DAI Links */ + { + .name = LPASS_BE_AUXPCM_RX, + .stream_name = "AUX PCM Playback", + .cpu_dai_name = "msm-dai-q6-auxpcm.1", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_AUXPCM_RX, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + .ops = &msm_aux_pcm_be_ops, + }, + { + .name = LPASS_BE_AUXPCM_TX, + .stream_name = "AUX PCM Capture", + .cpu_dai_name = "msm-dai-q6-auxpcm.1", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_AUXPCM_TX, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ignore_suspend = 1, + .ops = &msm_aux_pcm_be_ops, + }, + /* Secondary AUX PCM Backend DAI Links */ + { + .name = LPASS_BE_SEC_AUXPCM_RX, + .stream_name = "Sec AUX PCM Playback", + .cpu_dai_name = "msm-dai-q6-auxpcm.2", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SEC_AUXPCM_RX, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + .ops = &msm_aux_pcm_be_ops, + }, + { + .name = LPASS_BE_SEC_AUXPCM_TX, + .stream_name = "Sec AUX PCM Capture", + .cpu_dai_name = "msm-dai-q6-auxpcm.2", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_SEC_AUXPCM_TX, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ignore_suspend = 1, + .ops = &msm_aux_pcm_be_ops, + }, + /* Tertiary AUX PCM Backend DAI Links */ + { + .name = LPASS_BE_TERT_AUXPCM_RX, + .stream_name = "Tert AUX PCM Playback", + .cpu_dai_name = "msm-dai-q6-auxpcm.3", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_TERT_AUXPCM_RX, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + .ops = &msm_aux_pcm_be_ops, + }, + { + .name = LPASS_BE_TERT_AUXPCM_TX, + .stream_name = "Tert AUX PCM Capture", + .cpu_dai_name = "msm-dai-q6-auxpcm.3", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_TERT_AUXPCM_TX, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ignore_suspend = 1, + .ops = &msm_aux_pcm_be_ops, + }, + /* Quaternary AUX PCM Backend DAI Links */ + { + .name = LPASS_BE_QUAT_AUXPCM_RX, + .stream_name = "Quat AUX PCM Playback", + .cpu_dai_name = "msm-dai-q6-auxpcm.4", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_QUAT_AUXPCM_RX, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + .ops = &msm_aux_pcm_be_ops, + }, + { + .name = LPASS_BE_QUAT_AUXPCM_TX, + .stream_name = "Quat AUX PCM Capture", + .cpu_dai_name = "msm-dai-q6-auxpcm.4", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_QUAT_AUXPCM_TX, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ignore_suspend = 1, + .ops = &msm_aux_pcm_be_ops, + }, + /* Quinary AUX PCM Backend DAI Links */ + { + .name = LPASS_BE_QUIN_AUXPCM_RX, + .stream_name = "Quin AUX PCM Playback", + .cpu_dai_name = "msm-dai-q6-auxpcm.5", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_QUIN_AUXPCM_RX, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + .ops = &msm_aux_pcm_be_ops, + }, + { + .name = LPASS_BE_QUIN_AUXPCM_TX, + .stream_name = "Quin AUX PCM Capture", + .cpu_dai_name = "msm-dai-q6-auxpcm.5", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_QUIN_AUXPCM_TX, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ignore_suspend = 1, + .ops = &msm_aux_pcm_be_ops, + }, +}; + + +static struct snd_soc_dai_link msm_wcn_be_dai_links[] = { + { + .name = LPASS_BE_SLIMBUS_7_RX, + .stream_name = "Slimbus7 Playback", + .cpu_dai_name = "msm-dai-q6-dev.16398", + .platform_name = "msm-pcm-routing", + .codec_name = "btfmslim_slave", + /* BT codec driver determines capabilities based on + * dai name, bt codecdai name should always contains + * supported usecase information + */ + .codec_dai_name = "btfm_bt_sco_a2dp_slim_rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_7_RX, + .be_hw_params_fixup = msm_btfm_be_hw_params_fixup, + .ops = &msm_wcn_ops, + /* dai link has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_7_TX, + .stream_name = "Slimbus7 Capture", + .cpu_dai_name = "msm-dai-q6-dev.16399", + .platform_name = "msm-pcm-routing", + .codec_name = "btfmslim_slave", + .codec_dai_name = "btfm_bt_sco_slim_tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_7_TX, + .be_hw_params_fixup = msm_btfm_be_hw_params_fixup, + .ops = &msm_wcn_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_8_TX, + .stream_name = "Slimbus8 Capture", + .cpu_dai_name = "msm-dai-q6-dev.16401", + .platform_name = "msm-pcm-routing", + .codec_name = "btfmslim_slave", + .codec_dai_name = "btfm_fm_slim_tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_8_TX, + .be_hw_params_fixup = msm_btfm_be_hw_params_fixup, + .init = &msm_wcn_init, + .ops = &msm_wcn_ops, + .ignore_suspend = 1, + }, +}; + +static struct snd_soc_dai_link msm_wsa_be_dai_links[] = { + { + .name = LPASS_BE_INT4_MI2S_RX, + .stream_name = "INT4 MI2S Playback", + .cpu_dai_name = "msm-dai-q6-mi2s.11", + .platform_name = "msm-pcm-routing", + .codec_name = "msm_sdw_codec", + .codec_dai_name = "msm_sdw_i2s_rx1", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_INT4_MI2S_RX, + .init = &msm_sdw_audrx_init, + .be_hw_params_fixup = int_mi2s_be_hw_params_fixup, + .ops = &msm_sdw_mi2s_be_ops, + .ignore_suspend = 1, + }, +}; + +static struct snd_soc_dai_link ext_disp_be_dai_link[] = { + /* DISP PORT BACK END DAI Link */ + { + .name = LPASS_BE_DISPLAY_PORT, + .stream_name = "Display Port Playback", + .cpu_dai_name = "msm-dai-q6-dp.24608", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-ext-disp-audio-codec-rx", + .codec_dai_name = "msm_dp_audio_codec_rx_dai", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_DISPLAY_PORT_RX, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, +}; + +static struct snd_soc_dai_link msm_int_dai_links[ +ARRAY_SIZE(msm_int_dai) + +ARRAY_SIZE(msm_int_wsa_dai) + +ARRAY_SIZE(msm_int_compress_capture_dai) + +ARRAY_SIZE(msm_int_be_dai) + +ARRAY_SIZE(msm_mi2s_be_dai_links) + +ARRAY_SIZE(msm_auxpcm_be_dai_links)+ +ARRAY_SIZE(msm_wcn_be_dai_links) + +ARRAY_SIZE(msm_wsa_be_dai_links) + +ARRAY_SIZE(ext_disp_be_dai_link)]; + +static struct snd_soc_card sdm660_card = { + /* snd_soc_card_sdm660 */ + .name = "sdm660-snd-card", + .dai_link = msm_int_dai, + .num_links = ARRAY_SIZE(msm_int_dai), + .late_probe = msm_snd_card_late_probe, +}; + +static void msm_disable_int_mclk0(struct work_struct *work) +{ + struct msm_asoc_mach_data *pdata = NULL; + struct delayed_work *dwork; + int ret = 0; + + dwork = to_delayed_work(work); + pdata = container_of(dwork, struct msm_asoc_mach_data, + disable_int_mclk0_work); + mutex_lock(&pdata->cdc_int_mclk0_mutex); + pr_debug("%s: mclk_enabled %d mclk_rsc_ref %d\n", __func__, + atomic_read(&pdata->int_mclk0_enabled), + atomic_read(&pdata->int_mclk0_rsc_ref)); + + if (atomic_read(&pdata->int_mclk0_enabled) == true + && atomic_read(&pdata->int_mclk0_rsc_ref) == 0) { + pr_debug("Disable the mclk\n"); + pdata->digital_cdc_core_clk.enable = 0; + pdata->digital_cdc_core_clk.clk_freq_in_hz = + DEFAULT_MCLK_RATE; + ret = afe_set_lpass_clock_v2( + AFE_PORT_ID_INT0_MI2S_RX, + &pdata->digital_cdc_core_clk); + if (ret < 0) + pr_err("%s failed to disable the CCLK\n", __func__); + atomic_set(&pdata->int_mclk0_enabled, false); + } + mutex_unlock(&pdata->cdc_int_mclk0_mutex); +} + +static void msm_int_dt_parse_cap_info(struct platform_device *pdev, + struct msm_asoc_mach_data *pdata) +{ + const char *ext1_cap = "qcom,msm-micbias1-ext-cap"; + const char *ext2_cap = "qcom,msm-micbias2-ext-cap"; + + pdata->micbias1_cap_mode = + (of_property_read_bool(pdev->dev.of_node, ext1_cap) ? + MICBIAS_EXT_BYP_CAP : MICBIAS_NO_EXT_BYP_CAP); + + pdata->micbias2_cap_mode = + (of_property_read_bool(pdev->dev.of_node, ext2_cap) ? + MICBIAS_EXT_BYP_CAP : MICBIAS_NO_EXT_BYP_CAP); +} + +static struct snd_soc_card *msm_int_populate_sndcard_dailinks( + struct device *dev) +{ + struct snd_soc_card *card = &sdm660_card; + struct snd_soc_dai_link *dailink; + int len1; + + card->name = dev_name(dev); + len1 = ARRAY_SIZE(msm_int_dai); + memcpy(msm_int_dai_links, msm_int_dai, sizeof(msm_int_dai)); + dailink = msm_int_dai_links; + if (!of_property_read_bool(dev->of_node, + "qcom,wsa-disable")) { + memcpy(dailink + len1, + msm_int_wsa_dai, + sizeof(msm_int_wsa_dai)); + len1 += ARRAY_SIZE(msm_int_wsa_dai); + } + memcpy(dailink + len1, msm_int_compress_capture_dai, + sizeof(msm_int_compress_capture_dai)); + len1 += ARRAY_SIZE(msm_int_compress_capture_dai); + + memcpy(dailink + len1, msm_int_be_dai, sizeof(msm_int_be_dai)); + len1 += ARRAY_SIZE(msm_int_be_dai); + + if (of_property_read_bool(dev->of_node, + "qcom,mi2s-audio-intf")) { + memcpy(dailink + len1, + msm_mi2s_be_dai_links, + sizeof(msm_mi2s_be_dai_links)); + len1 += ARRAY_SIZE(msm_mi2s_be_dai_links); + } + if (of_property_read_bool(dev->of_node, + "qcom,auxpcm-audio-intf")) { + memcpy(dailink + len1, + msm_auxpcm_be_dai_links, + sizeof(msm_auxpcm_be_dai_links)); + len1 += ARRAY_SIZE(msm_auxpcm_be_dai_links); + } + if (of_property_read_bool(dev->of_node, "qcom,wcn-btfm")) { + dev_dbg(dev, "%s(): WCN BTFM support present\n", + __func__); + memcpy(dailink + len1, + msm_wcn_be_dai_links, + sizeof(msm_wcn_be_dai_links)); + len1 += ARRAY_SIZE(msm_wcn_be_dai_links); + } + if (!of_property_read_bool(dev->of_node, "qcom,wsa-disable")) { + memcpy(dailink + len1, + msm_wsa_be_dai_links, + sizeof(msm_wsa_be_dai_links)); + len1 += ARRAY_SIZE(msm_wsa_be_dai_links); + } + if (of_property_read_bool(dev->of_node, "qcom,ext-disp-audio-rx")) { + dev_dbg(dev, "%s(): ext disp audio support present\n", + __func__); + memcpy(dailink + len1, + ext_disp_be_dai_link, + sizeof(ext_disp_be_dai_link)); + len1 += ARRAY_SIZE(ext_disp_be_dai_link); + } + card->dai_link = dailink; + card->num_links = len1; + return card; +} + +static int msm_internal_init(struct platform_device *pdev, + struct msm_asoc_mach_data *pdata, + struct snd_soc_card *card) +{ + const char *type = NULL; + const char *hs_micbias_type = "qcom,msm-hs-micbias-type"; + int ret; + + ret = is_ext_spk_gpio_support(pdev, pdata); + if (ret < 0) + dev_dbg(&pdev->dev, + "%s: doesn't support external speaker pa\n", + __func__); + + ret = of_property_read_string(pdev->dev.of_node, + hs_micbias_type, &type); + if (ret) { + dev_err(&pdev->dev, "%s: missing %s in dt node\n", + __func__, hs_micbias_type); + goto err; + } + if (!strcmp(type, "external")) { + dev_dbg(&pdev->dev, "Headset is using external micbias\n"); + mbhc_cfg_ptr->hs_ext_micbias = true; + } else { + dev_dbg(&pdev->dev, "Headset is using internal micbias\n"); + mbhc_cfg_ptr->hs_ext_micbias = false; + } + + /* initialize the int_mclk0 */ + pdata->digital_cdc_core_clk.clk_set_minor_version = + AFE_API_VERSION_I2S_CONFIG; + pdata->digital_cdc_core_clk.clk_id = + Q6AFE_LPASS_CLK_ID_INT_MCLK_0; + pdata->digital_cdc_core_clk.clk_freq_in_hz = pdata->mclk_freq; + pdata->digital_cdc_core_clk.clk_attri = + Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO; + pdata->digital_cdc_core_clk.clk_root = + Q6AFE_LPASS_CLK_ROOT_DEFAULT; + pdata->digital_cdc_core_clk.enable = 1; + + /* Initialize loopback mode to false */ + pdata->lb_mode = false; + + msm_int_dt_parse_cap_info(pdev, pdata); + + card->dev = &pdev->dev; + platform_set_drvdata(pdev, card); + snd_soc_card_set_drvdata(card, pdata); + ret = snd_soc_of_parse_card_name(card, "qcom,model"); + if (ret) + goto err; + /* initialize timer */ + INIT_DELAYED_WORK(&pdata->disable_int_mclk0_work, + msm_disable_int_mclk0); + mutex_init(&pdata->cdc_int_mclk0_mutex); + atomic_set(&pdata->int_mclk0_rsc_ref, 0); + atomic_set(&pdata->int_mclk0_enabled, false); + + dev_info(&pdev->dev, "%s: default codec configured\n", __func__); + + return 0; +err: + return ret; +} + +/** + * msm_int_cdc_init - internal codec machine specific init. + * + * @pdev: platform device handle + * @pdata: private data of machine driver + * @card: sound card pointer reference + * @mbhc_cfg: MBHC config reference + * + * Returns 0. + */ +int msm_int_cdc_init(struct platform_device *pdev, + struct msm_asoc_mach_data *pdata, + struct snd_soc_card **card, + struct wcd_mbhc_config *mbhc_cfg) +{ + mbhc_cfg_ptr = mbhc_cfg; + + *card = msm_int_populate_sndcard_dailinks(&pdev->dev); + msm_internal_init(pdev, pdata, *card); + return 0; +} +EXPORT_SYMBOL(msm_int_cdc_init); diff --git a/audio-kernel/asoc/sdm660-internal.h b/audio-kernel/asoc/sdm660-internal.h new file mode 100644 index 000000000..6918231b8 --- /dev/null +++ b/audio-kernel/asoc/sdm660-internal.h @@ -0,0 +1,32 @@ +/* Copyright (c) 2016-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 + * 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 __SDM660_INTERNAL +#define __SDM660_INTERNAL + +#include + +#if IS_ENABLED(CONFIG_SND_SOC_INT_CODEC) +int msm_int_cdc_init(struct platform_device *pdev, + struct msm_asoc_mach_data *pdata, + struct snd_soc_card **card, + struct wcd_mbhc_config *mbhc_cfg); +#else +int msm_int_cdc_init(struct platform_device *pdev, + struct msm_asoc_mach_data *pdata, + struct snd_soc_card **card, + struct wcd_mbhc_config *mbhc_cfg) +{ + return 0; +} +#endif +#endif diff --git a/audio-kernel/asoc/sdm845.c b/audio-kernel/asoc/sdm845.c new file mode 100644 index 000000000..392ac85b9 --- /dev/null +++ b/audio-kernel/asoc/sdm845.c @@ -0,0 +1,7124 @@ +/* + * Copyright (c) 2016-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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "msm-pcm-routing-v2.h" +#include "codecs/msm-cdc-pinctrl.h" +#include "codecs/wcd934x/wcd934x.h" +#include "codecs/wcd934x/wcd934x-mbhc.h" +#include "codecs/wsa881x.h" + +#define DRV_NAME "sdm845-asoc-snd" + +#define __CHIPSET__ "SDM845 " +#define MSM_DAILINK_NAME(name) (__CHIPSET__#name) + +#define SAMPLING_RATE_8KHZ 8000 +#define SAMPLING_RATE_11P025KHZ 11025 +#define SAMPLING_RATE_16KHZ 16000 +#define SAMPLING_RATE_22P05KHZ 22050 +#define SAMPLING_RATE_32KHZ 32000 +#define SAMPLING_RATE_44P1KHZ 44100 +#define SAMPLING_RATE_48KHZ 48000 +#define SAMPLING_RATE_88P2KHZ 88200 +#define SAMPLING_RATE_96KHZ 96000 +#define SAMPLING_RATE_176P4KHZ 176400 +#define SAMPLING_RATE_192KHZ 192000 +#define SAMPLING_RATE_352P8KHZ 352800 +#define SAMPLING_RATE_384KHZ 384000 + +#define WCD9XXX_MBHC_DEF_BUTTONS 8 +#define WCD9XXX_MBHC_DEF_RLOADS 5 +#define CODEC_EXT_CLK_RATE 9600000 +#define ADSP_STATE_READY_TIMEOUT_MS 3000 +#define DEV_NAME_STR_LEN 32 + +#define WSA8810_NAME_1 "wsa881x.20170211" +#define WSA8810_NAME_2 "wsa881x.20170212" + +#define WCN_CDC_SLIM_RX_CH_MAX 2 +#define WCN_CDC_SLIM_TX_CH_MAX 3 + +#define TDM_CHANNEL_MAX 8 + +#define MSM_HIFI_ON 1 +#define MSM_LL_QOS_VALUE 300 /* time in us to ensure LPM doesn't go in C3/C4 */ + +enum { + SLIM_RX_0 = 0, + SLIM_RX_1, + SLIM_RX_2, + SLIM_RX_3, + SLIM_RX_4, + SLIM_RX_5, + SLIM_RX_6, + SLIM_RX_7, + SLIM_RX_MAX, +}; + +enum { + SLIM_TX_0 = 0, + SLIM_TX_1, + SLIM_TX_2, + SLIM_TX_3, + SLIM_TX_4, + SLIM_TX_5, + SLIM_TX_6, + SLIM_TX_7, + SLIM_TX_8, + SLIM_TX_MAX, +}; + +enum { + PRIM_MI2S = 0, + SEC_MI2S, + TERT_MI2S, + QUAT_MI2S, + MI2S_MAX, +}; + +enum { + PRIM_AUX_PCM = 0, + SEC_AUX_PCM, + TERT_AUX_PCM, + QUAT_AUX_PCM, + AUX_PCM_MAX, +}; + +enum { + PCM_I2S_SEL_PRIM = 0, + PCM_I2S_SEL_SEC, + PCM_I2S_SEL_TERT, + PCM_I2S_SEL_QUAT, + PCM_I2S_SEL_MAX, +}; + +struct mi2s_aux_pcm_common_conf { + struct mutex lock; + void *pcm_i2s_sel_vt_addr; +}; + +struct mi2s_conf { + struct mutex lock; + u32 ref_cnt; + u32 msm_is_mi2s_master; +}; + +static u32 mi2s_ebit_clk[MI2S_MAX] = { + Q6AFE_LPASS_CLK_ID_PRI_MI2S_EBIT, + Q6AFE_LPASS_CLK_ID_SEC_MI2S_EBIT, + Q6AFE_LPASS_CLK_ID_TER_MI2S_EBIT, + Q6AFE_LPASS_CLK_ID_QUAD_MI2S_EBIT +}; + +struct auxpcm_conf { + struct mutex lock; + u32 ref_cnt; +}; + +struct dev_config { + u32 sample_rate; + u32 bit_format; + u32 channels; +}; + +enum { + DP_RX_IDX = 0, + EXT_DISP_RX_IDX_MAX, +}; + +struct msm_wsa881x_dev_info { + struct device_node *of_node; + u32 index; +}; + +enum pinctrl_pin_state { + STATE_DISABLE = 0, /* All pins are in sleep state */ + STATE_MI2S_ACTIVE, /* IS2 = active, TDM = sleep */ + STATE_TDM_ACTIVE, /* IS2 = sleep, TDM = active */ +}; + +struct msm_pinctrl_info { + struct pinctrl *pinctrl; + struct pinctrl_state *mi2s_disable; + struct pinctrl_state *tdm_disable; + struct pinctrl_state *mi2s_active; + struct pinctrl_state *tdm_active; + enum pinctrl_pin_state curr_state; +}; + +struct msm_asoc_mach_data { + u32 mclk_freq; + int us_euro_gpio; /* used by gpio driver API */ + int usbc_en2_gpio; /* used by gpio driver API */ + struct device_node *us_euro_gpio_p; /* used by pinctrl API */ + struct pinctrl *usbc_en2_gpio_p; /* used by pinctrl API */ + struct device_node *hph_en1_gpio_p; /* used by pinctrl API */ + struct device_node *hph_en0_gpio_p; /* used by pinctrl API */ + struct snd_info_entry *codec_root; + struct msm_pinctrl_info pinctrl_info; +}; + +struct msm_asoc_wcd93xx_codec { + void* (*get_afe_config_fn)(struct snd_soc_codec *codec, + enum afe_config_type config_type); + void (*mbhc_hs_detect_exit)(struct snd_soc_codec *codec); +}; + +static const char *const pin_states[] = {"sleep", "i2s-active", + "tdm-active"}; + +enum { + TDM_0 = 0, + TDM_1, + TDM_2, + TDM_3, + TDM_4, + TDM_5, + TDM_6, + TDM_7, + TDM_PORT_MAX, +}; + +enum { + TDM_PRI = 0, + TDM_SEC, + TDM_TERT, + TDM_QUAT, + TDM_INTERFACE_MAX, +}; + +struct tdm_port { + u32 mode; + u32 channel; +}; + +/* TDM default config */ +static struct dev_config tdm_rx_cfg[TDM_INTERFACE_MAX][TDM_PORT_MAX] = { + { /* PRI TDM */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_0 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_1 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_2 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_3 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_4 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_5 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_6 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_7 */ + }, + { /* SEC TDM */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_0 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_1 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_2 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_3 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_4 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_5 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_6 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_7 */ + }, + { /* TERT TDM */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_0 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_1 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_2 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_3 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_4 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_5 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_6 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_7 */ + }, + { /* QUAT TDM */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_0 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_1 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_2 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_3 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_4 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_5 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_6 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_7 */ + } +}; + +/* TDM default config */ +static struct dev_config tdm_tx_cfg[TDM_INTERFACE_MAX][TDM_PORT_MAX] = { + { /* PRI TDM */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_0 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_1 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_2 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_3 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_4 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_5 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_6 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_7 */ + }, + { /* SEC TDM */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_0 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_1 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_2 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_3 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_4 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_5 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_6 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_7 */ + }, + { /* TERT TDM */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_0 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_1 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_2 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_3 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_4 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_5 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_6 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_7 */ + }, + { /* QUAT TDM */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_0 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_1 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_2 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_3 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_4 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_5 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_6 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_7 */ + } +}; + + +/* Default configuration of slimbus channels */ +static struct dev_config slim_rx_cfg[] = { + [SLIM_RX_0] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_RX_1] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_RX_2] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_RX_3] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_RX_4] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_RX_5] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_RX_6] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_RX_7] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, +}; + +static struct dev_config slim_tx_cfg[] = { + [SLIM_TX_0] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_TX_1] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_TX_2] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_TX_3] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_TX_4] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_TX_5] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_TX_6] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_TX_7] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_TX_8] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, +}; + + +/* Default configuration of external display BE */ +static struct dev_config ext_disp_rx_cfg[] = { + [DP_RX_IDX] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, +}; + +static struct dev_config usb_rx_cfg = { + .sample_rate = SAMPLING_RATE_48KHZ, + .bit_format = SNDRV_PCM_FORMAT_S16_LE, + .channels = 2, +}; + +static struct dev_config usb_tx_cfg = { + .sample_rate = SAMPLING_RATE_48KHZ, + .bit_format = SNDRV_PCM_FORMAT_S16_LE, + .channels = 1, +}; + +static struct dev_config proxy_rx_cfg = { + .sample_rate = SAMPLING_RATE_48KHZ, + .bit_format = SNDRV_PCM_FORMAT_S16_LE, + .channels = 2, +}; + +/* Default configuration of MI2S channels */ +static struct dev_config mi2s_rx_cfg[] = { + [PRIM_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, + [SEC_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, + [TERT_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, + [QUAT_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, +}; + +static struct dev_config mi2s_tx_cfg[] = { + [PRIM_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SEC_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [TERT_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [QUAT_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, +}; + +static struct dev_config aux_pcm_rx_cfg[] = { + [PRIM_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SEC_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [TERT_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [QUAT_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, +}; + +static struct dev_config aux_pcm_tx_cfg[] = { + [PRIM_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SEC_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [TERT_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [QUAT_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, +}; + +static int msm_vi_feed_tx_ch = 2; +static const char *const slim_rx_ch_text[] = {"One", "Two"}; +static const char *const slim_tx_ch_text[] = {"One", "Two", "Three", "Four", + "Five", "Six", "Seven", + "Eight"}; +static const char *const vi_feed_ch_text[] = {"One", "Two"}; +static char const *bit_format_text[] = {"S16_LE", "S24_LE", "S24_3LE", + "S32_LE"}; +static char const *ext_disp_bit_format_text[] = {"S16_LE", "S24_LE", + "S24_3LE"}; +static char const *slim_sample_rate_text[] = {"KHZ_8", "KHZ_16", + "KHZ_32", "KHZ_44P1", "KHZ_48", + "KHZ_88P2", "KHZ_96", "KHZ_176P4", + "KHZ_192", "KHZ_352P8", "KHZ_384"}; +static char const *bt_sample_rate_text[] = {"KHZ_8", "KHZ_16", + "KHZ_44P1", "KHZ_48", + "KHZ_88P2", "KHZ_96"}; +static const char *const usb_ch_text[] = {"One", "Two", "Three", "Four", + "Five", "Six", "Seven", + "Eight"}; +static char const *ch_text[] = {"Two", "Three", "Four", "Five", + "Six", "Seven", "Eight"}; +static char const *usb_sample_rate_text[] = {"KHZ_8", "KHZ_11P025", + "KHZ_16", "KHZ_22P05", + "KHZ_32", "KHZ_44P1", "KHZ_48", + "KHZ_88P2", "KHZ_96", "KHZ_176P4", + "KHZ_192", "KHZ_352P8", "KHZ_384"}; +static char const *ext_disp_sample_rate_text[] = {"KHZ_48", "KHZ_96", + "KHZ_192", "KHZ_32", "KHZ_44P1", + "KHZ_88P2", "KHZ_176P4" }; +static char const *tdm_ch_text[] = {"One", "Two", "Three", "Four", + "Five", "Six", "Seven", "Eight"}; +static char const *tdm_bit_format_text[] = {"S16_LE", "S24_LE", "S32_LE"}; +static char const *tdm_sample_rate_text[] = {"KHZ_8", "KHZ_16", "KHZ_32", + "KHZ_48", "KHZ_176P4", + "KHZ_352P8"}; +static const char *const auxpcm_rate_text[] = {"KHZ_8", "KHZ_16"}; +static char const *mi2s_rate_text[] = {"KHZ_8", "KHZ_11P025", "KHZ_16", + "KHZ_22P05", "KHZ_32", "KHZ_44P1", + "KHZ_48", "KHZ_96", "KHZ_192"}; +static const char *const mi2s_ch_text[] = {"One", "Two", "Three", "Four", + "Five", "Six", "Seven", + "Eight"}; +static const char *const hifi_text[] = {"Off", "On"}; +static const char *const qos_text[] = {"Disable", "Enable"}; + +static SOC_ENUM_SINGLE_EXT_DECL(slim_0_rx_chs, slim_rx_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_2_rx_chs, slim_rx_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_0_tx_chs, slim_tx_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_1_tx_chs, slim_tx_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_5_rx_chs, slim_rx_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_6_rx_chs, slim_rx_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(usb_rx_chs, usb_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(usb_tx_chs, usb_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(vi_feed_tx_chs, vi_feed_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(ext_disp_rx_chs, ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(proxy_rx_chs, ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_0_rx_format, bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_5_rx_format, bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_6_rx_format, bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_0_tx_format, bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(usb_rx_format, bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(usb_tx_format, bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(ext_disp_rx_format, ext_disp_bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_0_rx_sample_rate, slim_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_2_rx_sample_rate, slim_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_0_tx_sample_rate, slim_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_5_rx_sample_rate, slim_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_6_rx_sample_rate, slim_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(bt_sample_rate, bt_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(usb_rx_sample_rate, usb_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(usb_tx_sample_rate, usb_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(ext_disp_rx_sample_rate, + ext_disp_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(tdm_tx_chs, tdm_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(tdm_tx_format, tdm_bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(tdm_tx_sample_rate, tdm_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(tdm_rx_chs, tdm_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(tdm_rx_format, tdm_bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(tdm_rx_sample_rate, tdm_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(prim_aux_pcm_rx_sample_rate, auxpcm_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(sec_aux_pcm_rx_sample_rate, auxpcm_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(tert_aux_pcm_rx_sample_rate, auxpcm_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(quat_aux_pcm_rx_sample_rate, auxpcm_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(prim_aux_pcm_tx_sample_rate, auxpcm_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(sec_aux_pcm_tx_sample_rate, auxpcm_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(tert_aux_pcm_tx_sample_rate, auxpcm_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(quat_aux_pcm_tx_sample_rate, auxpcm_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(prim_mi2s_rx_sample_rate, mi2s_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(sec_mi2s_rx_sample_rate, mi2s_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(tert_mi2s_rx_sample_rate, mi2s_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(quat_mi2s_rx_sample_rate, mi2s_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(prim_mi2s_tx_sample_rate, mi2s_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(sec_mi2s_tx_sample_rate, mi2s_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(tert_mi2s_tx_sample_rate, mi2s_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(quat_mi2s_tx_sample_rate, mi2s_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(prim_mi2s_rx_chs, mi2s_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(prim_mi2s_tx_chs, mi2s_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(sec_mi2s_rx_chs, mi2s_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(sec_mi2s_tx_chs, mi2s_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(tert_mi2s_rx_chs, mi2s_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(tert_mi2s_tx_chs, mi2s_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(quat_mi2s_rx_chs, mi2s_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(quat_mi2s_tx_chs, mi2s_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(mi2s_rx_format, bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(mi2s_tx_format, bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(aux_pcm_rx_format, bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(aux_pcm_tx_format, bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(hifi_function, hifi_text); +static SOC_ENUM_SINGLE_EXT_DECL(qos_vote, qos_text); + +static struct platform_device *spdev; +static int msm_hifi_control; +static int qos_vote_status; + +static bool is_initial_boot; +static bool codec_reg_done; +static struct snd_soc_aux_dev *msm_aux_dev; +static struct snd_soc_codec_conf *msm_codec_conf; +static struct msm_asoc_wcd93xx_codec msm_codec_fn; + +static void *def_tavil_mbhc_cal(void); +static int msm_snd_enable_codec_ext_clk(struct snd_soc_codec *codec, + int enable, bool dapm); +static int msm_wsa881x_init(struct snd_soc_component *component); + +/* + * Need to report LINEIN + * if R/L channel impedance is larger than 5K ohm + */ +static struct wcd_mbhc_config wcd_mbhc_cfg = { + .read_fw_bin = false, + .calibration = NULL, + .detect_extn_cable = true, + .mono_stero_detection = false, + .swap_gnd_mic = NULL, + .hs_ext_micbias = true, + .key_code[0] = KEY_MEDIA, + .key_code[1] = KEY_VOICECOMMAND, + .key_code[2] = KEY_VOLUMEUP, + .key_code[3] = KEY_VOLUMEDOWN, + .key_code[4] = 0, + .key_code[5] = 0, + .key_code[6] = 0, + .key_code[7] = 0, + .linein_th = 5000, + .moisture_en = true, + .mbhc_micbias = MIC_BIAS_2, + .anc_micbias = MIC_BIAS_2, + .enable_anc_mic_detect = false, +}; + +static struct snd_soc_dapm_route wcd_audio_paths[] = { + {"MIC BIAS1", NULL, "MCLK TX"}, + {"MIC BIAS2", NULL, "MCLK TX"}, + {"MIC BIAS3", NULL, "MCLK TX"}, + {"MIC BIAS4", NULL, "MCLK TX"}, +}; + +static struct afe_clk_set mi2s_clk[MI2S_MAX] = { + { + AFE_API_VERSION_I2S_CONFIG, + Q6AFE_LPASS_CLK_ID_PRI_MI2S_IBIT, + Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ, + Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO, + Q6AFE_LPASS_CLK_ROOT_DEFAULT, + 0, + }, + { + AFE_API_VERSION_I2S_CONFIG, + Q6AFE_LPASS_CLK_ID_SEC_MI2S_IBIT, + Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ, + Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO, + Q6AFE_LPASS_CLK_ROOT_DEFAULT, + 0, + }, + { + AFE_API_VERSION_I2S_CONFIG, + Q6AFE_LPASS_CLK_ID_TER_MI2S_IBIT, + Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ, + Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO, + Q6AFE_LPASS_CLK_ROOT_DEFAULT, + 0, + }, + { + AFE_API_VERSION_I2S_CONFIG, + Q6AFE_LPASS_CLK_ID_QUAD_MI2S_IBIT, + Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ, + Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO, + Q6AFE_LPASS_CLK_ROOT_DEFAULT, + 0, + } +}; + +static struct mi2s_aux_pcm_common_conf mi2s_auxpcm_conf[PCM_I2S_SEL_MAX]; +static struct mi2s_conf mi2s_intf_conf[MI2S_MAX]; +static struct auxpcm_conf auxpcm_intf_conf[AUX_PCM_MAX]; + +static int slim_get_sample_rate_val(int sample_rate) +{ + int sample_rate_val = 0; + + switch (sample_rate) { + case SAMPLING_RATE_8KHZ: + sample_rate_val = 0; + break; + case SAMPLING_RATE_16KHZ: + sample_rate_val = 1; + break; + case SAMPLING_RATE_32KHZ: + sample_rate_val = 2; + break; + case SAMPLING_RATE_44P1KHZ: + sample_rate_val = 3; + break; + case SAMPLING_RATE_48KHZ: + sample_rate_val = 4; + break; + case SAMPLING_RATE_88P2KHZ: + sample_rate_val = 5; + break; + case SAMPLING_RATE_96KHZ: + sample_rate_val = 6; + break; + case SAMPLING_RATE_176P4KHZ: + sample_rate_val = 7; + break; + case SAMPLING_RATE_192KHZ: + sample_rate_val = 8; + break; + case SAMPLING_RATE_352P8KHZ: + sample_rate_val = 9; + break; + case SAMPLING_RATE_384KHZ: + sample_rate_val = 10; + break; + default: + sample_rate_val = 4; + break; + } + return sample_rate_val; +} + +static int slim_get_sample_rate(int value) +{ + int sample_rate = 0; + + switch (value) { + case 0: + sample_rate = SAMPLING_RATE_8KHZ; + break; + case 1: + sample_rate = SAMPLING_RATE_16KHZ; + break; + case 2: + sample_rate = SAMPLING_RATE_32KHZ; + break; + case 3: + sample_rate = SAMPLING_RATE_44P1KHZ; + break; + case 4: + sample_rate = SAMPLING_RATE_48KHZ; + break; + case 5: + sample_rate = SAMPLING_RATE_88P2KHZ; + break; + case 6: + sample_rate = SAMPLING_RATE_96KHZ; + break; + case 7: + sample_rate = SAMPLING_RATE_176P4KHZ; + break; + case 8: + sample_rate = SAMPLING_RATE_192KHZ; + break; + case 9: + sample_rate = SAMPLING_RATE_352P8KHZ; + break; + case 10: + sample_rate = SAMPLING_RATE_384KHZ; + break; + default: + sample_rate = SAMPLING_RATE_48KHZ; + break; + } + return sample_rate; +} + +static int slim_get_bit_format_val(int bit_format) +{ + int val = 0; + + switch (bit_format) { + case SNDRV_PCM_FORMAT_S32_LE: + val = 3; + break; + case SNDRV_PCM_FORMAT_S24_3LE: + val = 2; + break; + case SNDRV_PCM_FORMAT_S24_LE: + val = 1; + break; + case SNDRV_PCM_FORMAT_S16_LE: + default: + val = 0; + break; + } + return val; +} + +static int slim_get_bit_format(int val) +{ + int bit_fmt = SNDRV_PCM_FORMAT_S16_LE; + + switch (val) { + case 0: + bit_fmt = SNDRV_PCM_FORMAT_S16_LE; + break; + case 1: + bit_fmt = SNDRV_PCM_FORMAT_S24_LE; + break; + case 2: + bit_fmt = SNDRV_PCM_FORMAT_S24_3LE; + break; + case 3: + bit_fmt = SNDRV_PCM_FORMAT_S32_LE; + break; + default: + bit_fmt = SNDRV_PCM_FORMAT_S16_LE; + break; + } + return bit_fmt; +} + +static int slim_get_port_idx(struct snd_kcontrol *kcontrol) +{ + int port_id = 0; + + if (strnstr(kcontrol->id.name, "SLIM_0_RX", sizeof("SLIM_0_RX"))) + port_id = SLIM_RX_0; + else if (strnstr(kcontrol->id.name, "SLIM_2_RX", sizeof("SLIM_2_RX"))) + port_id = SLIM_RX_2; + else if (strnstr(kcontrol->id.name, "SLIM_5_RX", sizeof("SLIM_5_RX"))) + port_id = SLIM_RX_5; + else if (strnstr(kcontrol->id.name, "SLIM_6_RX", sizeof("SLIM_6_RX"))) + port_id = SLIM_RX_6; + else if (strnstr(kcontrol->id.name, "SLIM_0_TX", sizeof("SLIM_0_TX"))) + port_id = SLIM_TX_0; + else if (strnstr(kcontrol->id.name, "SLIM_1_TX", sizeof("SLIM_1_TX"))) + port_id = SLIM_TX_1; + else { + pr_err("%s: unsupported channel: %s", + __func__, kcontrol->id.name); + return -EINVAL; + } + + return port_id; +} + +static int slim_rx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = slim_get_port_idx(kcontrol); + + if (ch_num < 0) + return ch_num; + + ucontrol->value.enumerated.item[0] = + slim_get_sample_rate_val(slim_rx_cfg[ch_num].sample_rate); + + pr_debug("%s: slim[%d]_rx_sample_rate = %d, item = %d\n", __func__, + ch_num, slim_rx_cfg[ch_num].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int slim_rx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = slim_get_port_idx(kcontrol); + + if (ch_num < 0) + return ch_num; + + slim_rx_cfg[ch_num].sample_rate = + slim_get_sample_rate(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: slim[%d]_rx_sample_rate = %d, item = %d\n", __func__, + ch_num, slim_rx_cfg[ch_num].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int slim_tx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = slim_get_port_idx(kcontrol); + + if (ch_num < 0) + return ch_num; + + ucontrol->value.enumerated.item[0] = + slim_get_sample_rate_val(slim_tx_cfg[ch_num].sample_rate); + + pr_debug("%s: slim[%d]_tx_sample_rate = %d, item = %d\n", __func__, + ch_num, slim_tx_cfg[ch_num].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int slim_tx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int sample_rate = 0; + int ch_num = slim_get_port_idx(kcontrol); + + if (ch_num < 0) + return ch_num; + + sample_rate = slim_get_sample_rate(ucontrol->value.enumerated.item[0]); + if (sample_rate == SAMPLING_RATE_44P1KHZ) { + pr_err("%s: Unsupported sample rate %d: for Tx path\n", + __func__, sample_rate); + return -EINVAL; + } + slim_tx_cfg[ch_num].sample_rate = sample_rate; + + pr_debug("%s: slim[%d]_tx_sample_rate = %d, value = %d\n", __func__, + ch_num, slim_tx_cfg[ch_num].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int slim_rx_bit_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = slim_get_port_idx(kcontrol); + + if (ch_num < 0) + return ch_num; + + ucontrol->value.enumerated.item[0] = + slim_get_bit_format_val(slim_rx_cfg[ch_num].bit_format); + + pr_debug("%s: slim[%d]_rx_bit_format = %d, ucontrol value = %d\n", + __func__, ch_num, slim_rx_cfg[ch_num].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int slim_rx_bit_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = slim_get_port_idx(kcontrol); + + if (ch_num < 0) + return ch_num; + + slim_rx_cfg[ch_num].bit_format = + slim_get_bit_format(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: slim[%d]_rx_bit_format = %d, ucontrol value = %d\n", + __func__, ch_num, slim_rx_cfg[ch_num].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int slim_tx_bit_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = slim_get_port_idx(kcontrol); + + if (ch_num < 0) + return ch_num; + + ucontrol->value.enumerated.item[0] = + slim_get_bit_format_val(slim_tx_cfg[ch_num].bit_format); + + pr_debug("%s: slim[%d]_tx_bit_format = %d, ucontrol value = %d\n", + __func__, ch_num, slim_tx_cfg[ch_num].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int slim_tx_bit_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = slim_get_port_idx(kcontrol); + + if (ch_num < 0) + return ch_num; + + slim_tx_cfg[ch_num].bit_format = + slim_get_bit_format(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: slim[%d]_tx_bit_format = %d, ucontrol value = %d\n", + __func__, ch_num, slim_tx_cfg[ch_num].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int msm_slim_rx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = slim_get_port_idx(kcontrol); + + if (ch_num < 0) + return ch_num; + + pr_debug("%s: msm_slim_[%d]_rx_ch = %d\n", __func__, + ch_num, slim_rx_cfg[ch_num].channels); + ucontrol->value.enumerated.item[0] = slim_rx_cfg[ch_num].channels - 1; + + return 0; +} + +static int msm_slim_rx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = slim_get_port_idx(kcontrol); + + if (ch_num < 0) + return ch_num; + + slim_rx_cfg[ch_num].channels = ucontrol->value.enumerated.item[0] + 1; + pr_debug("%s: msm_slim_[%d]_rx_ch = %d\n", __func__, + ch_num, slim_rx_cfg[ch_num].channels); + + return 1; +} + +static int msm_slim_tx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = slim_get_port_idx(kcontrol); + + if (ch_num < 0) + return ch_num; + + pr_debug("%s: msm_slim_[%d]_tx_ch = %d\n", __func__, + ch_num, slim_tx_cfg[ch_num].channels); + ucontrol->value.enumerated.item[0] = slim_tx_cfg[ch_num].channels - 1; + + return 0; +} + +static int msm_slim_tx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = slim_get_port_idx(kcontrol); + + if (ch_num < 0) + return ch_num; + + slim_tx_cfg[ch_num].channels = ucontrol->value.enumerated.item[0] + 1; + pr_debug("%s: msm_slim_[%d]_tx_ch = %d\n", __func__, + ch_num, slim_tx_cfg[ch_num].channels); + + return 1; +} + +static int msm_vi_feed_tx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = msm_vi_feed_tx_ch - 1; + pr_debug("%s: msm_vi_feed_tx_ch = %ld\n", __func__, + ucontrol->value.integer.value[0]); + return 0; +} + +static int msm_vi_feed_tx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + msm_vi_feed_tx_ch = ucontrol->value.integer.value[0] + 1; + + pr_debug("%s: msm_vi_feed_tx_ch = %d\n", __func__, msm_vi_feed_tx_ch); + return 1; +} + +static int msm_bt_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + /* + * Slimbus_7_Rx/Tx sample rate values should always be in sync (same) + * when used for BT_SCO use case. Return either Rx or Tx sample rate + * value. + */ + switch (slim_rx_cfg[SLIM_RX_7].sample_rate) { + case SAMPLING_RATE_96KHZ: + ucontrol->value.integer.value[0] = 5; + break; + case SAMPLING_RATE_88P2KHZ: + ucontrol->value.integer.value[0] = 4; + break; + case SAMPLING_RATE_48KHZ: + ucontrol->value.integer.value[0] = 3; + break; + case SAMPLING_RATE_44P1KHZ: + ucontrol->value.integer.value[0] = 2; + break; + case SAMPLING_RATE_16KHZ: + ucontrol->value.integer.value[0] = 1; + break; + case SAMPLING_RATE_8KHZ: + default: + ucontrol->value.integer.value[0] = 0; + break; + } + pr_debug("%s: sample rate = %d", __func__, + slim_rx_cfg[SLIM_RX_7].sample_rate); + + return 0; +} + +static int msm_bt_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (ucontrol->value.integer.value[0]) { + case 1: + slim_rx_cfg[SLIM_RX_7].sample_rate = SAMPLING_RATE_16KHZ; + slim_tx_cfg[SLIM_TX_7].sample_rate = SAMPLING_RATE_16KHZ; + break; + case 2: + slim_rx_cfg[SLIM_RX_7].sample_rate = SAMPLING_RATE_44P1KHZ; + slim_tx_cfg[SLIM_TX_7].sample_rate = SAMPLING_RATE_44P1KHZ; + break; + case 3: + slim_rx_cfg[SLIM_RX_7].sample_rate = SAMPLING_RATE_48KHZ; + slim_tx_cfg[SLIM_TX_7].sample_rate = SAMPLING_RATE_48KHZ; + break; + case 4: + slim_rx_cfg[SLIM_RX_7].sample_rate = SAMPLING_RATE_88P2KHZ; + slim_tx_cfg[SLIM_TX_7].sample_rate = SAMPLING_RATE_88P2KHZ; + break; + case 5: + slim_rx_cfg[SLIM_RX_7].sample_rate = SAMPLING_RATE_96KHZ; + slim_tx_cfg[SLIM_TX_7].sample_rate = SAMPLING_RATE_96KHZ; + break; + case 0: + default: + slim_rx_cfg[SLIM_RX_7].sample_rate = SAMPLING_RATE_8KHZ; + slim_tx_cfg[SLIM_TX_7].sample_rate = SAMPLING_RATE_8KHZ; + break; + } + pr_debug("%s: sample rates: slim7_rx = %d, slim7_tx = %d, value = %d\n", + __func__, + slim_rx_cfg[SLIM_RX_7].sample_rate, + slim_tx_cfg[SLIM_TX_7].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int usb_audio_rx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s: usb_audio_rx_ch = %d\n", __func__, + usb_rx_cfg.channels); + ucontrol->value.integer.value[0] = usb_rx_cfg.channels - 1; + return 0; +} + +static int usb_audio_rx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + usb_rx_cfg.channels = ucontrol->value.integer.value[0] + 1; + + pr_debug("%s: usb_audio_rx_ch = %d\n", __func__, usb_rx_cfg.channels); + return 1; +} + +static int usb_audio_rx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int sample_rate_val; + + switch (usb_rx_cfg.sample_rate) { + case SAMPLING_RATE_384KHZ: + sample_rate_val = 12; + break; + case SAMPLING_RATE_352P8KHZ: + sample_rate_val = 11; + break; + case SAMPLING_RATE_192KHZ: + sample_rate_val = 10; + break; + case SAMPLING_RATE_176P4KHZ: + sample_rate_val = 9; + break; + case SAMPLING_RATE_96KHZ: + sample_rate_val = 8; + break; + case SAMPLING_RATE_88P2KHZ: + sample_rate_val = 7; + break; + case SAMPLING_RATE_48KHZ: + sample_rate_val = 6; + break; + case SAMPLING_RATE_44P1KHZ: + sample_rate_val = 5; + break; + case SAMPLING_RATE_32KHZ: + sample_rate_val = 4; + break; + case SAMPLING_RATE_22P05KHZ: + sample_rate_val = 3; + break; + case SAMPLING_RATE_16KHZ: + sample_rate_val = 2; + break; + case SAMPLING_RATE_11P025KHZ: + sample_rate_val = 1; + break; + case SAMPLING_RATE_8KHZ: + default: + sample_rate_val = 0; + break; + } + + ucontrol->value.integer.value[0] = sample_rate_val; + pr_debug("%s: usb_audio_rx_sample_rate = %d\n", __func__, + usb_rx_cfg.sample_rate); + return 0; +} + +static int usb_audio_rx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (ucontrol->value.integer.value[0]) { + case 12: + usb_rx_cfg.sample_rate = SAMPLING_RATE_384KHZ; + break; + case 11: + usb_rx_cfg.sample_rate = SAMPLING_RATE_352P8KHZ; + break; + case 10: + usb_rx_cfg.sample_rate = SAMPLING_RATE_192KHZ; + break; + case 9: + usb_rx_cfg.sample_rate = SAMPLING_RATE_176P4KHZ; + break; + case 8: + usb_rx_cfg.sample_rate = SAMPLING_RATE_96KHZ; + break; + case 7: + usb_rx_cfg.sample_rate = SAMPLING_RATE_88P2KHZ; + break; + case 6: + usb_rx_cfg.sample_rate = SAMPLING_RATE_48KHZ; + break; + case 5: + usb_rx_cfg.sample_rate = SAMPLING_RATE_44P1KHZ; + break; + case 4: + usb_rx_cfg.sample_rate = SAMPLING_RATE_32KHZ; + break; + case 3: + usb_rx_cfg.sample_rate = SAMPLING_RATE_22P05KHZ; + break; + case 2: + usb_rx_cfg.sample_rate = SAMPLING_RATE_16KHZ; + break; + case 1: + usb_rx_cfg.sample_rate = SAMPLING_RATE_11P025KHZ; + break; + case 0: + usb_rx_cfg.sample_rate = SAMPLING_RATE_8KHZ; + break; + default: + usb_rx_cfg.sample_rate = SAMPLING_RATE_48KHZ; + break; + } + + pr_debug("%s: control value = %ld, usb_audio_rx_sample_rate = %d\n", + __func__, ucontrol->value.integer.value[0], + usb_rx_cfg.sample_rate); + return 0; +} + +static int usb_audio_rx_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (usb_rx_cfg.bit_format) { + case SNDRV_PCM_FORMAT_S32_LE: + ucontrol->value.integer.value[0] = 3; + break; + case SNDRV_PCM_FORMAT_S24_3LE: + ucontrol->value.integer.value[0] = 2; + break; + case SNDRV_PCM_FORMAT_S24_LE: + ucontrol->value.integer.value[0] = 1; + break; + case SNDRV_PCM_FORMAT_S16_LE: + default: + ucontrol->value.integer.value[0] = 0; + break; + } + + pr_debug("%s: usb_audio_rx_format = %d, ucontrol value = %ld\n", + __func__, usb_rx_cfg.bit_format, + ucontrol->value.integer.value[0]); + return 0; +} + +static int usb_audio_rx_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int rc = 0; + + switch (ucontrol->value.integer.value[0]) { + case 3: + usb_rx_cfg.bit_format = SNDRV_PCM_FORMAT_S32_LE; + break; + case 2: + usb_rx_cfg.bit_format = SNDRV_PCM_FORMAT_S24_3LE; + break; + case 1: + usb_rx_cfg.bit_format = SNDRV_PCM_FORMAT_S24_LE; + break; + case 0: + default: + usb_rx_cfg.bit_format = SNDRV_PCM_FORMAT_S16_LE; + break; + } + pr_debug("%s: usb_audio_rx_format = %d, ucontrol value = %ld\n", + __func__, usb_rx_cfg.bit_format, + ucontrol->value.integer.value[0]); + + return rc; +} + +static int usb_audio_tx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s: usb_audio_tx_ch = %d\n", __func__, + usb_tx_cfg.channels); + ucontrol->value.integer.value[0] = usb_tx_cfg.channels - 1; + return 0; +} + +static int usb_audio_tx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + usb_tx_cfg.channels = ucontrol->value.integer.value[0] + 1; + + pr_debug("%s: usb_audio_tx_ch = %d\n", __func__, usb_tx_cfg.channels); + return 1; +} + +static int usb_audio_tx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int sample_rate_val; + + switch (usb_tx_cfg.sample_rate) { + case SAMPLING_RATE_384KHZ: + sample_rate_val = 12; + break; + case SAMPLING_RATE_352P8KHZ: + sample_rate_val = 11; + break; + case SAMPLING_RATE_192KHZ: + sample_rate_val = 10; + break; + case SAMPLING_RATE_176P4KHZ: + sample_rate_val = 9; + break; + case SAMPLING_RATE_96KHZ: + sample_rate_val = 8; + break; + case SAMPLING_RATE_88P2KHZ: + sample_rate_val = 7; + break; + case SAMPLING_RATE_48KHZ: + sample_rate_val = 6; + break; + case SAMPLING_RATE_44P1KHZ: + sample_rate_val = 5; + break; + case SAMPLING_RATE_32KHZ: + sample_rate_val = 4; + break; + case SAMPLING_RATE_22P05KHZ: + sample_rate_val = 3; + break; + case SAMPLING_RATE_16KHZ: + sample_rate_val = 2; + break; + case SAMPLING_RATE_11P025KHZ: + sample_rate_val = 1; + break; + case SAMPLING_RATE_8KHZ: + sample_rate_val = 0; + break; + default: + sample_rate_val = 6; + break; + } + + ucontrol->value.integer.value[0] = sample_rate_val; + pr_debug("%s: usb_audio_tx_sample_rate = %d\n", __func__, + usb_tx_cfg.sample_rate); + return 0; +} + +static int usb_audio_tx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (ucontrol->value.integer.value[0]) { + case 12: + usb_tx_cfg.sample_rate = SAMPLING_RATE_384KHZ; + break; + case 11: + usb_tx_cfg.sample_rate = SAMPLING_RATE_352P8KHZ; + break; + case 10: + usb_tx_cfg.sample_rate = SAMPLING_RATE_192KHZ; + break; + case 9: + usb_tx_cfg.sample_rate = SAMPLING_RATE_176P4KHZ; + break; + case 8: + usb_tx_cfg.sample_rate = SAMPLING_RATE_96KHZ; + break; + case 7: + usb_tx_cfg.sample_rate = SAMPLING_RATE_88P2KHZ; + break; + case 6: + usb_tx_cfg.sample_rate = SAMPLING_RATE_48KHZ; + break; + case 5: + usb_tx_cfg.sample_rate = SAMPLING_RATE_44P1KHZ; + break; + case 4: + usb_tx_cfg.sample_rate = SAMPLING_RATE_32KHZ; + break; + case 3: + usb_tx_cfg.sample_rate = SAMPLING_RATE_22P05KHZ; + break; + case 2: + usb_tx_cfg.sample_rate = SAMPLING_RATE_16KHZ; + break; + case 1: + usb_tx_cfg.sample_rate = SAMPLING_RATE_11P025KHZ; + break; + case 0: + usb_tx_cfg.sample_rate = SAMPLING_RATE_8KHZ; + break; + default: + usb_tx_cfg.sample_rate = SAMPLING_RATE_48KHZ; + break; + } + + pr_debug("%s: control value = %ld, usb_audio_tx_sample_rate = %d\n", + __func__, ucontrol->value.integer.value[0], + usb_tx_cfg.sample_rate); + return 0; +} + +static int usb_audio_tx_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (usb_tx_cfg.bit_format) { + case SNDRV_PCM_FORMAT_S32_LE: + ucontrol->value.integer.value[0] = 3; + break; + case SNDRV_PCM_FORMAT_S24_3LE: + ucontrol->value.integer.value[0] = 2; + break; + case SNDRV_PCM_FORMAT_S24_LE: + ucontrol->value.integer.value[0] = 1; + break; + case SNDRV_PCM_FORMAT_S16_LE: + default: + ucontrol->value.integer.value[0] = 0; + break; + } + + pr_debug("%s: usb_audio_tx_format = %d, ucontrol value = %ld\n", + __func__, usb_tx_cfg.bit_format, + ucontrol->value.integer.value[0]); + return 0; +} + +static int usb_audio_tx_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int rc = 0; + + switch (ucontrol->value.integer.value[0]) { + case 3: + usb_tx_cfg.bit_format = SNDRV_PCM_FORMAT_S32_LE; + break; + case 2: + usb_tx_cfg.bit_format = SNDRV_PCM_FORMAT_S24_3LE; + break; + case 1: + usb_tx_cfg.bit_format = SNDRV_PCM_FORMAT_S24_LE; + break; + case 0: + default: + usb_tx_cfg.bit_format = SNDRV_PCM_FORMAT_S16_LE; + break; + } + pr_debug("%s: usb_audio_tx_format = %d, ucontrol value = %ld\n", + __func__, usb_tx_cfg.bit_format, + ucontrol->value.integer.value[0]); + + return rc; +} + +static int ext_disp_get_port_idx(struct snd_kcontrol *kcontrol) +{ + int idx; + + if (strnstr(kcontrol->id.name, "Display Port RX", + sizeof("Display Port RX"))) { + idx = DP_RX_IDX; + } else { + pr_err("%s: unsupported BE: %s", + __func__, kcontrol->id.name); + idx = -EINVAL; + } + + return idx; +} + +static int ext_disp_rx_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = ext_disp_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + switch (ext_disp_rx_cfg[idx].bit_format) { + case SNDRV_PCM_FORMAT_S24_3LE: + ucontrol->value.integer.value[0] = 2; + break; + case SNDRV_PCM_FORMAT_S24_LE: + ucontrol->value.integer.value[0] = 1; + break; + case SNDRV_PCM_FORMAT_S16_LE: + default: + ucontrol->value.integer.value[0] = 0; + break; + } + + pr_debug("%s: ext_disp_rx[%d].format = %d, ucontrol value = %ld\n", + __func__, idx, ext_disp_rx_cfg[idx].bit_format, + ucontrol->value.integer.value[0]); + return 0; +} + +static int ext_disp_rx_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = ext_disp_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + switch (ucontrol->value.integer.value[0]) { + case 2: + ext_disp_rx_cfg[idx].bit_format = SNDRV_PCM_FORMAT_S24_3LE; + break; + case 1: + ext_disp_rx_cfg[idx].bit_format = SNDRV_PCM_FORMAT_S24_LE; + break; + case 0: + default: + ext_disp_rx_cfg[idx].bit_format = SNDRV_PCM_FORMAT_S16_LE; + break; + } + pr_debug("%s: ext_disp_rx[%d].format = %d, ucontrol value = %ld\n", + __func__, idx, ext_disp_rx_cfg[idx].bit_format, + ucontrol->value.integer.value[0]); + + return 0; +} + +static int ext_disp_rx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = ext_disp_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + ucontrol->value.integer.value[0] = + ext_disp_rx_cfg[idx].channels - 2; + + pr_debug("%s: ext_disp_rx[%d].ch = %d\n", __func__, + idx, ext_disp_rx_cfg[idx].channels); + + return 0; +} + +static int ext_disp_rx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = ext_disp_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + ext_disp_rx_cfg[idx].channels = + ucontrol->value.integer.value[0] + 2; + + pr_debug("%s: ext_disp_rx[%d].ch = %d\n", __func__, + idx, ext_disp_rx_cfg[idx].channels); + return 1; +} + +static int ext_disp_rx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int sample_rate_val; + int idx = ext_disp_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + switch (ext_disp_rx_cfg[idx].sample_rate) { + case SAMPLING_RATE_176P4KHZ: + sample_rate_val = 6; + break; + + case SAMPLING_RATE_88P2KHZ: + sample_rate_val = 5; + break; + + case SAMPLING_RATE_44P1KHZ: + sample_rate_val = 4; + break; + + case SAMPLING_RATE_32KHZ: + sample_rate_val = 3; + break; + + case SAMPLING_RATE_192KHZ: + sample_rate_val = 2; + break; + + case SAMPLING_RATE_96KHZ: + sample_rate_val = 1; + break; + + case SAMPLING_RATE_48KHZ: + default: + sample_rate_val = 0; + break; + } + + ucontrol->value.integer.value[0] = sample_rate_val; + pr_debug("%s: ext_disp_rx[%d].sample_rate = %d\n", __func__, + idx, ext_disp_rx_cfg[idx].sample_rate); + + return 0; +} + +static int ext_disp_rx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = ext_disp_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + switch (ucontrol->value.integer.value[0]) { + case 6: + ext_disp_rx_cfg[idx].sample_rate = SAMPLING_RATE_176P4KHZ; + break; + case 5: + ext_disp_rx_cfg[idx].sample_rate = SAMPLING_RATE_88P2KHZ; + break; + case 4: + ext_disp_rx_cfg[idx].sample_rate = SAMPLING_RATE_44P1KHZ; + break; + case 3: + ext_disp_rx_cfg[idx].sample_rate = SAMPLING_RATE_32KHZ; + break; + case 2: + ext_disp_rx_cfg[idx].sample_rate = SAMPLING_RATE_192KHZ; + break; + case 1: + ext_disp_rx_cfg[idx].sample_rate = SAMPLING_RATE_96KHZ; + break; + case 0: + default: + ext_disp_rx_cfg[idx].sample_rate = SAMPLING_RATE_48KHZ; + break; + } + + pr_debug("%s: control value = %ld, ext_disp_rx[%d].sample_rate = %d\n", + __func__, ucontrol->value.integer.value[0], idx, + ext_disp_rx_cfg[idx].sample_rate); + return 0; +} + +static int proxy_rx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s: proxy_rx channels = %d\n", + __func__, proxy_rx_cfg.channels); + ucontrol->value.integer.value[0] = proxy_rx_cfg.channels - 2; + + return 0; +} + +static int proxy_rx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + proxy_rx_cfg.channels = ucontrol->value.integer.value[0] + 2; + pr_debug("%s: proxy_rx channels = %d\n", + __func__, proxy_rx_cfg.channels); + + return 1; +} + +static int tdm_get_sample_rate(int value) +{ + int sample_rate = 0; + + switch (value) { + case 0: + sample_rate = SAMPLING_RATE_8KHZ; + break; + case 1: + sample_rate = SAMPLING_RATE_16KHZ; + break; + case 2: + sample_rate = SAMPLING_RATE_32KHZ; + break; + case 3: + sample_rate = SAMPLING_RATE_48KHZ; + break; + case 4: + sample_rate = SAMPLING_RATE_176P4KHZ; + break; + case 5: + sample_rate = SAMPLING_RATE_352P8KHZ; + break; + default: + sample_rate = SAMPLING_RATE_48KHZ; + break; + } + return sample_rate; +} + +static int aux_pcm_get_sample_rate(int value) +{ + int sample_rate; + + switch (value) { + case 1: + sample_rate = SAMPLING_RATE_16KHZ; + break; + case 0: + default: + sample_rate = SAMPLING_RATE_8KHZ; + break; + } + return sample_rate; +} + +static int tdm_get_sample_rate_val(int sample_rate) +{ + int sample_rate_val = 0; + + switch (sample_rate) { + case SAMPLING_RATE_8KHZ: + sample_rate_val = 0; + break; + case SAMPLING_RATE_16KHZ: + sample_rate_val = 1; + break; + case SAMPLING_RATE_32KHZ: + sample_rate_val = 2; + break; + case SAMPLING_RATE_48KHZ: + sample_rate_val = 3; + break; + case SAMPLING_RATE_176P4KHZ: + sample_rate_val = 4; + break; + case SAMPLING_RATE_352P8KHZ: + sample_rate_val = 5; + break; + default: + sample_rate_val = 3; + break; + } + return sample_rate_val; +} + +static int aux_pcm_get_sample_rate_val(int sample_rate) +{ + int sample_rate_val; + + switch (sample_rate) { + case SAMPLING_RATE_16KHZ: + sample_rate_val = 1; + break; + case SAMPLING_RATE_8KHZ: + default: + sample_rate_val = 0; + break; + } + return sample_rate_val; +} + +static int tdm_get_port_idx(struct snd_kcontrol *kcontrol, + struct tdm_port *port) +{ + if (port) { + if (strnstr(kcontrol->id.name, "PRI", + sizeof(kcontrol->id.name))) { + port->mode = TDM_PRI; + } else if (strnstr(kcontrol->id.name, "SEC", + sizeof(kcontrol->id.name))) { + port->mode = TDM_SEC; + } else if (strnstr(kcontrol->id.name, "TERT", + sizeof(kcontrol->id.name))) { + port->mode = TDM_TERT; + } else if (strnstr(kcontrol->id.name, "QUAT", + sizeof(kcontrol->id.name))) { + port->mode = TDM_QUAT; + } else { + pr_err("%s: unsupported mode in: %s", + __func__, kcontrol->id.name); + return -EINVAL; + } + + if (strnstr(kcontrol->id.name, "RX_0", + sizeof(kcontrol->id.name)) || + strnstr(kcontrol->id.name, "TX_0", + sizeof(kcontrol->id.name))) { + port->channel = TDM_0; + } else if (strnstr(kcontrol->id.name, "RX_1", + sizeof(kcontrol->id.name)) || + strnstr(kcontrol->id.name, "TX_1", + sizeof(kcontrol->id.name))) { + port->channel = TDM_1; + } else if (strnstr(kcontrol->id.name, "RX_2", + sizeof(kcontrol->id.name)) || + strnstr(kcontrol->id.name, "TX_2", + sizeof(kcontrol->id.name))) { + port->channel = TDM_2; + } else if (strnstr(kcontrol->id.name, "RX_3", + sizeof(kcontrol->id.name)) || + strnstr(kcontrol->id.name, "TX_3", + sizeof(kcontrol->id.name))) { + port->channel = TDM_3; + } else if (strnstr(kcontrol->id.name, "RX_4", + sizeof(kcontrol->id.name)) || + strnstr(kcontrol->id.name, "TX_4", + sizeof(kcontrol->id.name))) { + port->channel = TDM_4; + } else if (strnstr(kcontrol->id.name, "RX_5", + sizeof(kcontrol->id.name)) || + strnstr(kcontrol->id.name, "TX_5", + sizeof(kcontrol->id.name))) { + port->channel = TDM_5; + } else if (strnstr(kcontrol->id.name, "RX_6", + sizeof(kcontrol->id.name)) || + strnstr(kcontrol->id.name, "TX_6", + sizeof(kcontrol->id.name))) { + port->channel = TDM_6; + } else if (strnstr(kcontrol->id.name, "RX_7", + sizeof(kcontrol->id.name)) || + strnstr(kcontrol->id.name, "TX_7", + sizeof(kcontrol->id.name))) { + port->channel = TDM_7; + } else { + pr_err("%s: unsupported channel in: %s", + __func__, kcontrol->id.name); + return -EINVAL; + } + } else + return -EINVAL; + return 0; +} + +static int tdm_rx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + ucontrol->value.enumerated.item[0] = tdm_get_sample_rate_val( + tdm_rx_cfg[port.mode][port.channel].sample_rate); + + pr_debug("%s: tdm_rx_sample_rate = %d, item = %d\n", __func__, + tdm_rx_cfg[port.mode][port.channel].sample_rate, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_rx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + tdm_rx_cfg[port.mode][port.channel].sample_rate = + tdm_get_sample_rate(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: tdm_rx_sample_rate = %d, item = %d\n", __func__, + tdm_rx_cfg[port.mode][port.channel].sample_rate, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_tx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + ucontrol->value.enumerated.item[0] = tdm_get_sample_rate_val( + tdm_tx_cfg[port.mode][port.channel].sample_rate); + + pr_debug("%s: tdm_tx_sample_rate = %d, item = %d\n", __func__, + tdm_tx_cfg[port.mode][port.channel].sample_rate, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_tx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + tdm_tx_cfg[port.mode][port.channel].sample_rate = + tdm_get_sample_rate(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: tdm_tx_sample_rate = %d, item = %d\n", __func__, + tdm_tx_cfg[port.mode][port.channel].sample_rate, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_get_format(int value) +{ + int format = 0; + + switch (value) { + case 0: + format = SNDRV_PCM_FORMAT_S16_LE; + break; + case 1: + format = SNDRV_PCM_FORMAT_S24_LE; + break; + case 2: + format = SNDRV_PCM_FORMAT_S32_LE; + break; + default: + format = SNDRV_PCM_FORMAT_S16_LE; + break; + } + return format; +} + +static int tdm_get_format_val(int format) +{ + int value = 0; + + switch (format) { + case SNDRV_PCM_FORMAT_S16_LE: + value = 0; + break; + case SNDRV_PCM_FORMAT_S24_LE: + value = 1; + break; + case SNDRV_PCM_FORMAT_S32_LE: + value = 2; + break; + default: + value = 0; + break; + } + return value; +} + +static int tdm_rx_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + ucontrol->value.enumerated.item[0] = tdm_get_format_val( + tdm_rx_cfg[port.mode][port.channel].bit_format); + + pr_debug("%s: tdm_rx_bit_format = %d, item = %d\n", __func__, + tdm_rx_cfg[port.mode][port.channel].bit_format, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_rx_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + tdm_rx_cfg[port.mode][port.channel].bit_format = + tdm_get_format(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: tdm_rx_bit_format = %d, item = %d\n", __func__, + tdm_rx_cfg[port.mode][port.channel].bit_format, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_tx_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + ucontrol->value.enumerated.item[0] = tdm_get_format_val( + tdm_tx_cfg[port.mode][port.channel].bit_format); + + pr_debug("%s: tdm_tx_bit_format = %d, item = %d\n", __func__, + tdm_tx_cfg[port.mode][port.channel].bit_format, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_tx_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + tdm_tx_cfg[port.mode][port.channel].bit_format = + tdm_get_format(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: tdm_tx_bit_format = %d, item = %d\n", __func__, + tdm_tx_cfg[port.mode][port.channel].bit_format, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_rx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + + ucontrol->value.enumerated.item[0] = + tdm_rx_cfg[port.mode][port.channel].channels - 1; + + pr_debug("%s: tdm_rx_ch = %d, item = %d\n", __func__, + tdm_rx_cfg[port.mode][port.channel].channels - 1, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_rx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + tdm_rx_cfg[port.mode][port.channel].channels = + ucontrol->value.enumerated.item[0] + 1; + + pr_debug("%s: tdm_rx_ch = %d, item = %d\n", __func__, + tdm_rx_cfg[port.mode][port.channel].channels, + ucontrol->value.enumerated.item[0] + 1); + } + return ret; +} + +static int tdm_tx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + ucontrol->value.enumerated.item[0] = + tdm_tx_cfg[port.mode][port.channel].channels - 1; + + pr_debug("%s: tdm_tx_ch = %d, item = %d\n", __func__, + tdm_tx_cfg[port.mode][port.channel].channels - 1, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_tx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + tdm_tx_cfg[port.mode][port.channel].channels = + ucontrol->value.enumerated.item[0] + 1; + + pr_debug("%s: tdm_tx_ch = %d, item = %d\n", __func__, + tdm_tx_cfg[port.mode][port.channel].channels, + ucontrol->value.enumerated.item[0] + 1); + } + return ret; +} + +static int aux_pcm_get_port_idx(struct snd_kcontrol *kcontrol) +{ + int idx; + + if (strnstr(kcontrol->id.name, "PRIM_AUX_PCM", + sizeof("PRIM_AUX_PCM"))) + idx = PRIM_AUX_PCM; + else if (strnstr(kcontrol->id.name, "SEC_AUX_PCM", + sizeof("SEC_AUX_PCM"))) + idx = SEC_AUX_PCM; + else if (strnstr(kcontrol->id.name, "TERT_AUX_PCM", + sizeof("TERT_AUX_PCM"))) + idx = TERT_AUX_PCM; + else if (strnstr(kcontrol->id.name, "QUAT_AUX_PCM", + sizeof("QUAT_AUX_PCM"))) + idx = QUAT_AUX_PCM; + else { + pr_err("%s: unsupported port: %s", + __func__, kcontrol->id.name); + idx = -EINVAL; + } + + return idx; +} + +static int aux_pcm_rx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = aux_pcm_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + aux_pcm_rx_cfg[idx].sample_rate = + aux_pcm_get_sample_rate(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: idx[%d]_rx_sample_rate = %d, item = %d\n", __func__, + idx, aux_pcm_rx_cfg[idx].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int aux_pcm_rx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = aux_pcm_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + ucontrol->value.enumerated.item[0] = + aux_pcm_get_sample_rate_val(aux_pcm_rx_cfg[idx].sample_rate); + + pr_debug("%s: idx[%d]_rx_sample_rate = %d, item = %d\n", __func__, + idx, aux_pcm_rx_cfg[idx].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int aux_pcm_tx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = aux_pcm_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + aux_pcm_tx_cfg[idx].sample_rate = + aux_pcm_get_sample_rate(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: idx[%d]_tx_sample_rate = %d, item = %d\n", __func__, + idx, aux_pcm_tx_cfg[idx].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int aux_pcm_tx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = aux_pcm_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + ucontrol->value.enumerated.item[0] = + aux_pcm_get_sample_rate_val(aux_pcm_tx_cfg[idx].sample_rate); + + pr_debug("%s: idx[%d]_tx_sample_rate = %d, item = %d\n", __func__, + idx, aux_pcm_tx_cfg[idx].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int mi2s_get_port_idx(struct snd_kcontrol *kcontrol) +{ + int idx; + + if (strnstr(kcontrol->id.name, "PRIM_MI2S_RX", + sizeof("PRIM_MI2S_RX"))) + idx = PRIM_MI2S; + else if (strnstr(kcontrol->id.name, "SEC_MI2S_RX", + sizeof("SEC_MI2S_RX"))) + idx = SEC_MI2S; + else if (strnstr(kcontrol->id.name, "TERT_MI2S_RX", + sizeof("TERT_MI2S_RX"))) + idx = TERT_MI2S; + else if (strnstr(kcontrol->id.name, "QUAT_MI2S_RX", + sizeof("QUAT_MI2S_RX"))) + idx = QUAT_MI2S; + else if (strnstr(kcontrol->id.name, "PRIM_MI2S_TX", + sizeof("PRIM_MI2S_TX"))) + idx = PRIM_MI2S; + else if (strnstr(kcontrol->id.name, "SEC_MI2S_TX", + sizeof("SEC_MI2S_TX"))) + idx = SEC_MI2S; + else if (strnstr(kcontrol->id.name, "TERT_MI2S_TX", + sizeof("TERT_MI2S_TX"))) + idx = TERT_MI2S; + else if (strnstr(kcontrol->id.name, "QUAT_MI2S_TX", + sizeof("QUAT_MI2S_TX"))) + idx = QUAT_MI2S; + else { + pr_err("%s: unsupported channel: %s", + __func__, kcontrol->id.name); + idx = -EINVAL; + } + + return idx; +} + +static int mi2s_get_sample_rate_val(int sample_rate) +{ + int sample_rate_val; + + switch (sample_rate) { + case SAMPLING_RATE_8KHZ: + sample_rate_val = 0; + break; + case SAMPLING_RATE_11P025KHZ: + sample_rate_val = 1; + break; + case SAMPLING_RATE_16KHZ: + sample_rate_val = 2; + break; + case SAMPLING_RATE_22P05KHZ: + sample_rate_val = 3; + break; + case SAMPLING_RATE_32KHZ: + sample_rate_val = 4; + break; + case SAMPLING_RATE_44P1KHZ: + sample_rate_val = 5; + break; + case SAMPLING_RATE_48KHZ: + sample_rate_val = 6; + break; + case SAMPLING_RATE_96KHZ: + sample_rate_val = 7; + break; + case SAMPLING_RATE_192KHZ: + sample_rate_val = 8; + break; + default: + sample_rate_val = 6; + break; + } + return sample_rate_val; +} + +static int mi2s_get_sample_rate(int value) +{ + int sample_rate; + + switch (value) { + case 0: + sample_rate = SAMPLING_RATE_8KHZ; + break; + case 1: + sample_rate = SAMPLING_RATE_11P025KHZ; + break; + case 2: + sample_rate = SAMPLING_RATE_16KHZ; + break; + case 3: + sample_rate = SAMPLING_RATE_22P05KHZ; + break; + case 4: + sample_rate = SAMPLING_RATE_32KHZ; + break; + case 5: + sample_rate = SAMPLING_RATE_44P1KHZ; + break; + case 6: + sample_rate = SAMPLING_RATE_48KHZ; + break; + case 7: + sample_rate = SAMPLING_RATE_96KHZ; + break; + case 8: + sample_rate = SAMPLING_RATE_192KHZ; + break; + default: + sample_rate = SAMPLING_RATE_48KHZ; + break; + } + return sample_rate; +} + +static int mi2s_auxpcm_get_format(int value) +{ + int format; + + switch (value) { + case 0: + format = SNDRV_PCM_FORMAT_S16_LE; + break; + case 1: + format = SNDRV_PCM_FORMAT_S24_LE; + break; + case 2: + format = SNDRV_PCM_FORMAT_S24_3LE; + break; + case 3: + format = SNDRV_PCM_FORMAT_S32_LE; + break; + default: + format = SNDRV_PCM_FORMAT_S16_LE; + break; + } + return format; +} + +static int mi2s_auxpcm_get_format_value(int format) +{ + int value; + + switch (format) { + case SNDRV_PCM_FORMAT_S16_LE: + value = 0; + break; + case SNDRV_PCM_FORMAT_S24_LE: + value = 1; + break; + case SNDRV_PCM_FORMAT_S24_3LE: + value = 2; + break; + case SNDRV_PCM_FORMAT_S32_LE: + value = 3; + break; + default: + value = 0; + break; + } + return value; +} + +static int mi2s_rx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + mi2s_rx_cfg[idx].sample_rate = + mi2s_get_sample_rate(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: idx[%d]_rx_sample_rate = %d, item = %d\n", __func__, + idx, mi2s_rx_cfg[idx].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int mi2s_rx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + ucontrol->value.enumerated.item[0] = + mi2s_get_sample_rate_val(mi2s_rx_cfg[idx].sample_rate); + + pr_debug("%s: idx[%d]_rx_sample_rate = %d, item = %d\n", __func__, + idx, mi2s_rx_cfg[idx].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int mi2s_tx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + mi2s_tx_cfg[idx].sample_rate = + mi2s_get_sample_rate(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: idx[%d]_tx_sample_rate = %d, item = %d\n", __func__, + idx, mi2s_tx_cfg[idx].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int mi2s_tx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + ucontrol->value.enumerated.item[0] = + mi2s_get_sample_rate_val(mi2s_tx_cfg[idx].sample_rate); + + pr_debug("%s: idx[%d]_tx_sample_rate = %d, item = %d\n", __func__, + idx, mi2s_tx_cfg[idx].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int msm_mi2s_rx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + pr_debug("%s: msm_mi2s_[%d]_rx_ch = %d\n", __func__, + idx, mi2s_rx_cfg[idx].channels); + ucontrol->value.enumerated.item[0] = mi2s_rx_cfg[idx].channels - 1; + + return 0; +} + +static int msm_mi2s_rx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + mi2s_rx_cfg[idx].channels = ucontrol->value.enumerated.item[0] + 1; + pr_debug("%s: msm_mi2s_[%d]_rx_ch = %d\n", __func__, + idx, mi2s_rx_cfg[idx].channels); + + return 1; +} + +static int msm_mi2s_tx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + pr_debug("%s: msm_mi2s_[%d]_tx_ch = %d\n", __func__, + idx, mi2s_tx_cfg[idx].channels); + ucontrol->value.enumerated.item[0] = mi2s_tx_cfg[idx].channels - 1; + + return 0; +} + +static int msm_mi2s_tx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + mi2s_tx_cfg[idx].channels = ucontrol->value.enumerated.item[0] + 1; + pr_debug("%s: msm_mi2s_[%d]_tx_ch = %d\n", __func__, + idx, mi2s_tx_cfg[idx].channels); + + return 1; +} + +static int msm_mi2s_rx_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + ucontrol->value.enumerated.item[0] = + mi2s_auxpcm_get_format_value(mi2s_rx_cfg[idx].bit_format); + + pr_debug("%s: idx[%d]_rx_format = %d, item = %d\n", __func__, + idx, mi2s_rx_cfg[idx].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int msm_mi2s_rx_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + mi2s_rx_cfg[idx].bit_format = + mi2s_auxpcm_get_format(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: idx[%d]_rx_format = %d, item = %d\n", __func__, + idx, mi2s_rx_cfg[idx].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int msm_mi2s_tx_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + ucontrol->value.enumerated.item[0] = + mi2s_auxpcm_get_format_value(mi2s_tx_cfg[idx].bit_format); + + pr_debug("%s: idx[%d]_tx_format = %d, item = %d\n", __func__, + idx, mi2s_tx_cfg[idx].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int msm_mi2s_tx_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + mi2s_tx_cfg[idx].bit_format = + mi2s_auxpcm_get_format(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: idx[%d]_tx_format = %d, item = %d\n", __func__, + idx, mi2s_tx_cfg[idx].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int msm_aux_pcm_rx_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = aux_pcm_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + ucontrol->value.enumerated.item[0] = + mi2s_auxpcm_get_format_value(aux_pcm_rx_cfg[idx].bit_format); + + pr_debug("%s: idx[%d]_rx_format = %d, item = %d\n", __func__, + idx, aux_pcm_rx_cfg[idx].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int msm_aux_pcm_rx_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = aux_pcm_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + aux_pcm_rx_cfg[idx].bit_format = + mi2s_auxpcm_get_format(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: idx[%d]_rx_format = %d, item = %d\n", __func__, + idx, aux_pcm_rx_cfg[idx].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int msm_aux_pcm_tx_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = aux_pcm_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + ucontrol->value.enumerated.item[0] = + mi2s_auxpcm_get_format_value(aux_pcm_tx_cfg[idx].bit_format); + + pr_debug("%s: idx[%d]_tx_format = %d, item = %d\n", __func__, + idx, aux_pcm_tx_cfg[idx].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int msm_aux_pcm_tx_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = aux_pcm_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + aux_pcm_tx_cfg[idx].bit_format = + mi2s_auxpcm_get_format(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: idx[%d]_tx_format = %d, item = %d\n", __func__, + idx, aux_pcm_tx_cfg[idx].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int msm_hifi_ctrl(struct snd_soc_codec *codec) +{ + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + struct snd_soc_card *card = codec->component.card; + struct msm_asoc_mach_data *pdata = + snd_soc_card_get_drvdata(card); + + pr_debug("%s: msm_hifi_control = %d", __func__, + msm_hifi_control); + + if (!pdata || !pdata->hph_en1_gpio_p) { + pr_err("%s: hph_en1_gpio is invalid\n", __func__); + return -EINVAL; + } + if (msm_hifi_control == MSM_HIFI_ON) { + msm_cdc_pinctrl_select_active_state(pdata->hph_en1_gpio_p); + /* 5msec delay needed as per HW requirement */ + usleep_range(5000, 5010); + } else { + msm_cdc_pinctrl_select_sleep_state(pdata->hph_en1_gpio_p); + } + snd_soc_dapm_sync(dapm); + + return 0; +} + +static int msm_hifi_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s: msm_hifi_control = %d\n", + __func__, msm_hifi_control); + ucontrol->value.integer.value[0] = msm_hifi_control; + + return 0; +} + +static int msm_hifi_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + + pr_debug("%s() ucontrol->value.integer.value[0] = %ld\n", + __func__, ucontrol->value.integer.value[0]); + + msm_hifi_control = ucontrol->value.integer.value[0]; + msm_hifi_ctrl(codec); + + return 0; +} + +static int msm_qos_ctl_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.enumerated.item[0] = qos_vote_status; + + return 0; +} + +static int msm_qos_ctl_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct snd_soc_card *card = codec->component.card; + const char *be_name = MSM_DAILINK_NAME(LowLatency); + struct snd_soc_pcm_runtime *rtd; + struct snd_pcm_substream *substream; + s32 usecs; + + rtd = snd_soc_get_pcm_runtime(card, be_name); + if (!rtd) { + pr_err("%s: fail to get pcm runtime for %s\n", + __func__, be_name); + return -EINVAL; + } + + substream = rtd->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; + if (!substream) { + pr_err("%s: substream is null\n", __func__); + return -EINVAL; + } + + qos_vote_status = ucontrol->value.enumerated.item[0]; + if (qos_vote_status) { + if (pm_qos_request_active(&substream->latency_pm_qos_req)) + pm_qos_remove_request(&substream->latency_pm_qos_req); + if (!substream->runtime) { + pr_err("%s: runtime is null\n", __func__); + return -EINVAL; + } + usecs = MSM_LL_QOS_VALUE; + if (usecs >= 0) + pm_qos_add_request(&substream->latency_pm_qos_req, + PM_QOS_CPU_DMA_LATENCY, usecs); + } else { + if (pm_qos_request_active(&substream->latency_pm_qos_req)) + pm_qos_remove_request(&substream->latency_pm_qos_req); + } + + return 0; +} + +static const struct snd_kcontrol_new msm_snd_controls[] = { + SOC_ENUM_EXT("SLIM_0_RX Channels", slim_0_rx_chs, + msm_slim_rx_ch_get, msm_slim_rx_ch_put), + SOC_ENUM_EXT("SLIM_2_RX Channels", slim_2_rx_chs, + msm_slim_rx_ch_get, msm_slim_rx_ch_put), + SOC_ENUM_EXT("SLIM_0_TX Channels", slim_0_tx_chs, + msm_slim_tx_ch_get, msm_slim_tx_ch_put), + SOC_ENUM_EXT("SLIM_1_TX Channels", slim_1_tx_chs, + msm_slim_tx_ch_get, msm_slim_tx_ch_put), + SOC_ENUM_EXT("SLIM_5_RX Channels", slim_5_rx_chs, + msm_slim_rx_ch_get, msm_slim_rx_ch_put), + SOC_ENUM_EXT("SLIM_6_RX Channels", slim_6_rx_chs, + msm_slim_rx_ch_get, msm_slim_rx_ch_put), + SOC_ENUM_EXT("VI_FEED_TX Channels", vi_feed_tx_chs, + msm_vi_feed_tx_ch_get, msm_vi_feed_tx_ch_put), + SOC_ENUM_EXT("USB_AUDIO_RX Channels", usb_rx_chs, + usb_audio_rx_ch_get, usb_audio_rx_ch_put), + SOC_ENUM_EXT("USB_AUDIO_TX Channels", usb_tx_chs, + usb_audio_tx_ch_get, usb_audio_tx_ch_put), + SOC_ENUM_EXT("Display Port RX Channels", ext_disp_rx_chs, + ext_disp_rx_ch_get, ext_disp_rx_ch_put), + SOC_ENUM_EXT("PROXY_RX Channels", proxy_rx_chs, + proxy_rx_ch_get, proxy_rx_ch_put), + SOC_ENUM_EXT("SLIM_0_RX Format", slim_0_rx_format, + slim_rx_bit_format_get, slim_rx_bit_format_put), + SOC_ENUM_EXT("SLIM_5_RX Format", slim_5_rx_format, + slim_rx_bit_format_get, slim_rx_bit_format_put), + SOC_ENUM_EXT("SLIM_6_RX Format", slim_6_rx_format, + slim_rx_bit_format_get, slim_rx_bit_format_put), + SOC_ENUM_EXT("SLIM_0_TX Format", slim_0_tx_format, + slim_tx_bit_format_get, slim_tx_bit_format_put), + SOC_ENUM_EXT("USB_AUDIO_RX Format", usb_rx_format, + usb_audio_rx_format_get, usb_audio_rx_format_put), + SOC_ENUM_EXT("USB_AUDIO_TX Format", usb_tx_format, + usb_audio_tx_format_get, usb_audio_tx_format_put), + SOC_ENUM_EXT("Display Port RX Bit Format", ext_disp_rx_format, + ext_disp_rx_format_get, ext_disp_rx_format_put), + SOC_ENUM_EXT("SLIM_0_RX SampleRate", slim_0_rx_sample_rate, + slim_rx_sample_rate_get, slim_rx_sample_rate_put), + SOC_ENUM_EXT("SLIM_2_RX SampleRate", slim_2_rx_sample_rate, + slim_rx_sample_rate_get, slim_rx_sample_rate_put), + SOC_ENUM_EXT("SLIM_0_TX SampleRate", slim_0_tx_sample_rate, + slim_tx_sample_rate_get, slim_tx_sample_rate_put), + SOC_ENUM_EXT("SLIM_5_RX SampleRate", slim_5_rx_sample_rate, + slim_rx_sample_rate_get, slim_rx_sample_rate_put), + SOC_ENUM_EXT("SLIM_6_RX SampleRate", slim_6_rx_sample_rate, + slim_rx_sample_rate_get, slim_rx_sample_rate_put), + SOC_ENUM_EXT("BT SampleRate", bt_sample_rate, + msm_bt_sample_rate_get, + msm_bt_sample_rate_put), + SOC_ENUM_EXT("USB_AUDIO_RX SampleRate", usb_rx_sample_rate, + usb_audio_rx_sample_rate_get, + usb_audio_rx_sample_rate_put), + SOC_ENUM_EXT("USB_AUDIO_TX SampleRate", usb_tx_sample_rate, + usb_audio_tx_sample_rate_get, + usb_audio_tx_sample_rate_put), + SOC_ENUM_EXT("Display Port RX SampleRate", ext_disp_rx_sample_rate, + ext_disp_rx_sample_rate_get, + ext_disp_rx_sample_rate_put), + SOC_ENUM_EXT("PRI_TDM_RX_0 SampleRate", tdm_rx_sample_rate, + tdm_rx_sample_rate_get, + tdm_rx_sample_rate_put), + SOC_ENUM_EXT("PRI_TDM_TX_0 SampleRate", tdm_tx_sample_rate, + tdm_tx_sample_rate_get, + tdm_tx_sample_rate_put), + SOC_ENUM_EXT("PRI_TDM_RX_0 Format", tdm_rx_format, + tdm_rx_format_get, + tdm_rx_format_put), + SOC_ENUM_EXT("PRI_TDM_TX_0 Format", tdm_tx_format, + tdm_tx_format_get, + tdm_tx_format_put), + SOC_ENUM_EXT("PRI_TDM_RX_0 Channels", tdm_rx_chs, + tdm_rx_ch_get, + tdm_rx_ch_put), + SOC_ENUM_EXT("PRI_TDM_TX_0 Channels", tdm_tx_chs, + tdm_tx_ch_get, + tdm_tx_ch_put), + SOC_ENUM_EXT("SEC_TDM_RX_0 SampleRate", tdm_rx_sample_rate, + tdm_rx_sample_rate_get, + tdm_rx_sample_rate_put), + SOC_ENUM_EXT("SEC_TDM_TX_0 SampleRate", tdm_tx_sample_rate, + tdm_tx_sample_rate_get, + tdm_tx_sample_rate_put), + SOC_ENUM_EXT("SEC_TDM_RX_0 Format", tdm_rx_format, + tdm_rx_format_get, + tdm_rx_format_put), + SOC_ENUM_EXT("SEC_TDM_TX_0 Format", tdm_tx_format, + tdm_tx_format_get, + tdm_tx_format_put), + SOC_ENUM_EXT("SEC_TDM_RX_0 Channels", tdm_rx_chs, + tdm_rx_ch_get, + tdm_rx_ch_put), + SOC_ENUM_EXT("SEC_TDM_TX_0 Channels", tdm_tx_chs, + tdm_tx_ch_get, + tdm_tx_ch_put), + SOC_ENUM_EXT("TERT_TDM_RX_0 SampleRate", tdm_rx_sample_rate, + tdm_rx_sample_rate_get, + tdm_rx_sample_rate_put), + SOC_ENUM_EXT("TERT_TDM_TX_0 SampleRate", tdm_tx_sample_rate, + tdm_tx_sample_rate_get, + tdm_tx_sample_rate_put), + SOC_ENUM_EXT("TERT_TDM_RX_0 Format", tdm_rx_format, + tdm_rx_format_get, + tdm_rx_format_put), + SOC_ENUM_EXT("TERT_TDM_TX_0 Format", tdm_tx_format, + tdm_tx_format_get, + tdm_tx_format_put), + SOC_ENUM_EXT("TERT_TDM_RX_0 Channels", tdm_rx_chs, + tdm_rx_ch_get, + tdm_rx_ch_put), + SOC_ENUM_EXT("TERT_TDM_TX_0 Channels", tdm_tx_chs, + tdm_tx_ch_get, + tdm_tx_ch_put), + SOC_ENUM_EXT("QUAT_TDM_RX_0 SampleRate", tdm_rx_sample_rate, + tdm_rx_sample_rate_get, + tdm_rx_sample_rate_put), + SOC_ENUM_EXT("QUAT_TDM_TX_0 SampleRate", tdm_tx_sample_rate, + tdm_tx_sample_rate_get, + tdm_tx_sample_rate_put), + SOC_ENUM_EXT("QUAT_TDM_RX_0 Format", tdm_rx_format, + tdm_rx_format_get, + tdm_rx_format_put), + SOC_ENUM_EXT("QUAT_TDM_TX_0 Format", tdm_tx_format, + tdm_tx_format_get, + tdm_tx_format_put), + SOC_ENUM_EXT("QUAT_TDM_RX_0 Channels", tdm_rx_chs, + tdm_rx_ch_get, + tdm_rx_ch_put), + SOC_ENUM_EXT("QUAT_TDM_TX_0 Channels", tdm_tx_chs, + tdm_tx_ch_get, + tdm_tx_ch_put), + SOC_ENUM_EXT("PRIM_AUX_PCM_RX SampleRate", prim_aux_pcm_rx_sample_rate, + aux_pcm_rx_sample_rate_get, + aux_pcm_rx_sample_rate_put), + SOC_ENUM_EXT("SEC_AUX_PCM_RX SampleRate", sec_aux_pcm_rx_sample_rate, + aux_pcm_rx_sample_rate_get, + aux_pcm_rx_sample_rate_put), + SOC_ENUM_EXT("TERT_AUX_PCM_RX SampleRate", tert_aux_pcm_rx_sample_rate, + aux_pcm_rx_sample_rate_get, + aux_pcm_rx_sample_rate_put), + SOC_ENUM_EXT("QUAT_AUX_PCM_RX SampleRate", quat_aux_pcm_rx_sample_rate, + aux_pcm_rx_sample_rate_get, + aux_pcm_rx_sample_rate_put), + SOC_ENUM_EXT("PRIM_AUX_PCM_TX SampleRate", prim_aux_pcm_tx_sample_rate, + aux_pcm_tx_sample_rate_get, + aux_pcm_tx_sample_rate_put), + SOC_ENUM_EXT("SEC_AUX_PCM_TX SampleRate", sec_aux_pcm_tx_sample_rate, + aux_pcm_tx_sample_rate_get, + aux_pcm_tx_sample_rate_put), + SOC_ENUM_EXT("TERT_AUX_PCM_TX SampleRate", tert_aux_pcm_tx_sample_rate, + aux_pcm_tx_sample_rate_get, + aux_pcm_tx_sample_rate_put), + SOC_ENUM_EXT("QUAT_AUX_PCM_TX SampleRate", quat_aux_pcm_tx_sample_rate, + aux_pcm_tx_sample_rate_get, + aux_pcm_tx_sample_rate_put), + SOC_ENUM_EXT("PRIM_MI2S_RX SampleRate", prim_mi2s_rx_sample_rate, + mi2s_rx_sample_rate_get, + mi2s_rx_sample_rate_put), + SOC_ENUM_EXT("SEC_MI2S_RX SampleRate", sec_mi2s_rx_sample_rate, + mi2s_rx_sample_rate_get, + mi2s_rx_sample_rate_put), + SOC_ENUM_EXT("TERT_MI2S_RX SampleRate", tert_mi2s_rx_sample_rate, + mi2s_rx_sample_rate_get, + mi2s_rx_sample_rate_put), + SOC_ENUM_EXT("QUAT_MI2S_RX SampleRate", quat_mi2s_rx_sample_rate, + mi2s_rx_sample_rate_get, + mi2s_rx_sample_rate_put), + SOC_ENUM_EXT("PRIM_MI2S_TX SampleRate", prim_mi2s_tx_sample_rate, + mi2s_tx_sample_rate_get, + mi2s_tx_sample_rate_put), + SOC_ENUM_EXT("SEC_MI2S_TX SampleRate", sec_mi2s_tx_sample_rate, + mi2s_tx_sample_rate_get, + mi2s_tx_sample_rate_put), + SOC_ENUM_EXT("TERT_MI2S_TX SampleRate", tert_mi2s_tx_sample_rate, + mi2s_tx_sample_rate_get, + mi2s_tx_sample_rate_put), + SOC_ENUM_EXT("QUAT_MI2S_TX SampleRate", quat_mi2s_tx_sample_rate, + mi2s_tx_sample_rate_get, + mi2s_tx_sample_rate_put), + SOC_ENUM_EXT("PRIM_MI2S_RX Channels", prim_mi2s_rx_chs, + msm_mi2s_rx_ch_get, msm_mi2s_rx_ch_put), + SOC_ENUM_EXT("PRIM_MI2S_TX Channels", prim_mi2s_tx_chs, + msm_mi2s_tx_ch_get, msm_mi2s_tx_ch_put), + SOC_ENUM_EXT("SEC_MI2S_RX Channels", sec_mi2s_rx_chs, + msm_mi2s_rx_ch_get, msm_mi2s_rx_ch_put), + SOC_ENUM_EXT("SEC_MI2S_TX Channels", sec_mi2s_tx_chs, + msm_mi2s_tx_ch_get, msm_mi2s_tx_ch_put), + SOC_ENUM_EXT("TERT_MI2S_RX Channels", tert_mi2s_rx_chs, + msm_mi2s_rx_ch_get, msm_mi2s_rx_ch_put), + SOC_ENUM_EXT("TERT_MI2S_TX Channels", tert_mi2s_tx_chs, + msm_mi2s_tx_ch_get, msm_mi2s_tx_ch_put), + SOC_ENUM_EXT("QUAT_MI2S_RX Channels", quat_mi2s_rx_chs, + msm_mi2s_rx_ch_get, msm_mi2s_rx_ch_put), + SOC_ENUM_EXT("QUAT_MI2S_TX Channels", quat_mi2s_tx_chs, + msm_mi2s_tx_ch_get, msm_mi2s_tx_ch_put), + SOC_ENUM_EXT("PRIM_MI2S_RX Format", mi2s_rx_format, + msm_mi2s_rx_format_get, msm_mi2s_rx_format_put), + SOC_ENUM_EXT("PRIM_MI2S_TX Format", mi2s_tx_format, + msm_mi2s_tx_format_get, msm_mi2s_tx_format_put), + SOC_ENUM_EXT("SEC_MI2S_RX Format", mi2s_rx_format, + msm_mi2s_rx_format_get, msm_mi2s_rx_format_put), + SOC_ENUM_EXT("SEC_MI2S_TX Format", mi2s_tx_format, + msm_mi2s_tx_format_get, msm_mi2s_tx_format_put), + SOC_ENUM_EXT("TERT_MI2S_RX Format", mi2s_rx_format, + msm_mi2s_rx_format_get, msm_mi2s_rx_format_put), + SOC_ENUM_EXT("TERT_MI2S_TX Format", mi2s_tx_format, + msm_mi2s_tx_format_get, msm_mi2s_tx_format_put), + SOC_ENUM_EXT("QUAT_MI2S_RX Format", mi2s_rx_format, + msm_mi2s_rx_format_get, msm_mi2s_rx_format_put), + SOC_ENUM_EXT("QUAT_MI2S_TX Format", mi2s_tx_format, + msm_mi2s_tx_format_get, msm_mi2s_tx_format_put), + SOC_ENUM_EXT("PRIM_AUX_PCM_RX Format", aux_pcm_rx_format, + msm_aux_pcm_rx_format_get, msm_aux_pcm_rx_format_put), + SOC_ENUM_EXT("PRIM_AUX_PCM_TX Format", aux_pcm_tx_format, + msm_aux_pcm_tx_format_get, msm_aux_pcm_tx_format_put), + SOC_ENUM_EXT("SEC_AUX_PCM_RX Format", aux_pcm_rx_format, + msm_aux_pcm_rx_format_get, msm_aux_pcm_rx_format_put), + SOC_ENUM_EXT("SEC_AUX_PCM_TX Format", aux_pcm_tx_format, + msm_aux_pcm_tx_format_get, msm_aux_pcm_tx_format_put), + SOC_ENUM_EXT("TERT_AUX_PCM_RX Format", aux_pcm_rx_format, + msm_aux_pcm_rx_format_get, msm_aux_pcm_rx_format_put), + SOC_ENUM_EXT("TERT_AUX_PCM_TX Format", aux_pcm_tx_format, + msm_aux_pcm_tx_format_get, msm_aux_pcm_tx_format_put), + SOC_ENUM_EXT("QUAT_AUX_PCM_RX Format", aux_pcm_rx_format, + msm_aux_pcm_rx_format_get, msm_aux_pcm_rx_format_put), + SOC_ENUM_EXT("QUAT_AUX_PCM_TX Format", aux_pcm_tx_format, + msm_aux_pcm_tx_format_get, msm_aux_pcm_tx_format_put), + SOC_ENUM_EXT("HiFi Function", hifi_function, msm_hifi_get, + msm_hifi_put), + SOC_ENUM_EXT("MultiMedia5_RX QOS Vote", qos_vote, msm_qos_ctl_get, + msm_qos_ctl_put), +}; + +static int msm_snd_enable_codec_ext_clk(struct snd_soc_codec *codec, + int enable, bool dapm) +{ + int ret = 0; + + if (!strcmp(dev_name(codec->dev), "tavil_codec")) { + ret = tavil_cdc_mclk_enable(codec, enable); + } else { + dev_err(codec->dev, "%s: unknown codec to enable ext clk\n", + __func__); + ret = -EINVAL; + } + return ret; +} + +static int msm_snd_enable_codec_ext_tx_clk(struct snd_soc_codec *codec, + int enable, bool dapm) +{ + int ret = 0; + + if (!strcmp(dev_name(codec->dev), "tavil_codec")) { + ret = tavil_cdc_mclk_tx_enable(codec, enable); + } else { + dev_err(codec->dev, "%s: unknown codec to enable TX ext clk\n", + __func__); + ret = -EINVAL; + } + + return ret; +} + +static int msm_mclk_tx_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + + pr_debug("%s: event = %d\n", __func__, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + return msm_snd_enable_codec_ext_tx_clk(codec, 1, true); + case SND_SOC_DAPM_POST_PMD: + return msm_snd_enable_codec_ext_tx_clk(codec, 0, true); + } + return 0; +} + +static int msm_mclk_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + + pr_debug("%s: event = %d\n", __func__, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + return msm_snd_enable_codec_ext_clk(codec, 1, true); + case SND_SOC_DAPM_POST_PMD: + return msm_snd_enable_codec_ext_clk(codec, 0, true); + } + return 0; +} + +static int msm_hifi_ctrl_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct snd_soc_card *card = codec->component.card; + struct msm_asoc_mach_data *pdata = + snd_soc_card_get_drvdata(card); + + pr_debug("%s: msm_hifi_control = %d", __func__, msm_hifi_control); + + if (!pdata || !pdata->hph_en0_gpio_p) { + pr_err("%s: hph_en0_gpio is invalid\n", __func__); + return -EINVAL; + } + + if (msm_hifi_control != MSM_HIFI_ON) { + pr_debug("%s: HiFi mixer control is not set\n", + __func__); + return 0; + } + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + msm_cdc_pinctrl_select_active_state(pdata->hph_en0_gpio_p); + break; + case SND_SOC_DAPM_PRE_PMD: + msm_cdc_pinctrl_select_sleep_state(pdata->hph_en0_gpio_p); + break; + } + + return 0; +} + +static const struct snd_soc_dapm_widget msm_dapm_widgets[] = { + + SND_SOC_DAPM_SUPPLY("MCLK", SND_SOC_NOPM, 0, 0, + msm_mclk_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_SUPPLY("MCLK TX", SND_SOC_NOPM, 0, 0, + msm_mclk_tx_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_SPK("Lineout_1 amp", NULL), + SND_SOC_DAPM_SPK("Lineout_2 amp", NULL), + SND_SOC_DAPM_SPK("hifi amp", msm_hifi_ctrl_event), + SND_SOC_DAPM_MIC("Handset Mic", NULL), + SND_SOC_DAPM_MIC("Headset Mic", NULL), + SND_SOC_DAPM_MIC("ANCRight Headset Mic", NULL), + SND_SOC_DAPM_MIC("ANCLeft Headset Mic", NULL), + SND_SOC_DAPM_MIC("Analog Mic5", NULL), + + SND_SOC_DAPM_MIC("Digital Mic0", NULL), + SND_SOC_DAPM_MIC("Digital Mic1", NULL), + SND_SOC_DAPM_MIC("Digital Mic2", NULL), + SND_SOC_DAPM_MIC("Digital Mic3", NULL), + SND_SOC_DAPM_MIC("Digital Mic4", NULL), + SND_SOC_DAPM_MIC("Digital Mic5", NULL), +}; + +static inline int param_is_mask(int p) +{ + return (p >= SNDRV_PCM_HW_PARAM_FIRST_MASK) && + (p <= SNDRV_PCM_HW_PARAM_LAST_MASK); +} + +static inline struct snd_mask *param_to_mask(struct snd_pcm_hw_params *p, + int n) +{ + return &(p->masks[n - SNDRV_PCM_HW_PARAM_FIRST_MASK]); +} + +static void param_set_mask(struct snd_pcm_hw_params *p, int n, + unsigned int bit) +{ + if (bit >= SNDRV_MASK_MAX) + return; + if (param_is_mask(n)) { + struct snd_mask *m = param_to_mask(p, n); + + m->bits[0] = 0; + m->bits[1] = 0; + m->bits[bit >> 5] |= (1 << (bit & 31)); + } +} + +static int msm_slim_get_ch_from_beid(int32_t be_id) +{ + int ch_id = 0; + + switch (be_id) { + case MSM_BACKEND_DAI_SLIMBUS_0_RX: + ch_id = SLIM_RX_0; + break; + case MSM_BACKEND_DAI_SLIMBUS_1_RX: + ch_id = SLIM_RX_1; + break; + case MSM_BACKEND_DAI_SLIMBUS_2_RX: + ch_id = SLIM_RX_2; + break; + case MSM_BACKEND_DAI_SLIMBUS_3_RX: + ch_id = SLIM_RX_3; + break; + case MSM_BACKEND_DAI_SLIMBUS_4_RX: + ch_id = SLIM_RX_4; + break; + case MSM_BACKEND_DAI_SLIMBUS_6_RX: + ch_id = SLIM_RX_6; + break; + case MSM_BACKEND_DAI_SLIMBUS_0_TX: + ch_id = SLIM_TX_0; + break; + case MSM_BACKEND_DAI_SLIMBUS_3_TX: + ch_id = SLIM_TX_3; + break; + default: + ch_id = SLIM_RX_0; + break; + } + + return ch_id; +} + +static int msm_ext_disp_get_idx_from_beid(int32_t be_id) +{ + int idx; + + switch (be_id) { + case MSM_BACKEND_DAI_DISPLAY_PORT_RX: + idx = DP_RX_IDX; + break; + default: + pr_err("%s: Incorrect ext_disp BE id %d\n", __func__, be_id); + idx = -EINVAL; + break; + } + + return idx; +} + +static int msm_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_dai_link *dai_link = rtd->dai_link; + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + int rc = 0; + int idx; + void *config = NULL; + struct snd_soc_codec *codec = NULL; + + pr_debug("%s: format = %d, rate = %d\n", + __func__, params_format(params), params_rate(params)); + + switch (dai_link->id) { + case MSM_BACKEND_DAI_SLIMBUS_0_RX: + case MSM_BACKEND_DAI_SLIMBUS_1_RX: + case MSM_BACKEND_DAI_SLIMBUS_2_RX: + case MSM_BACKEND_DAI_SLIMBUS_3_RX: + case MSM_BACKEND_DAI_SLIMBUS_4_RX: + case MSM_BACKEND_DAI_SLIMBUS_6_RX: + idx = msm_slim_get_ch_from_beid(dai_link->id); + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + slim_rx_cfg[idx].bit_format); + rate->min = rate->max = slim_rx_cfg[idx].sample_rate; + channels->min = channels->max = slim_rx_cfg[idx].channels; + break; + + case MSM_BACKEND_DAI_SLIMBUS_0_TX: + case MSM_BACKEND_DAI_SLIMBUS_3_TX: + idx = msm_slim_get_ch_from_beid(dai_link->id); + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + slim_tx_cfg[idx].bit_format); + rate->min = rate->max = slim_tx_cfg[idx].sample_rate; + channels->min = channels->max = slim_tx_cfg[idx].channels; + break; + + case MSM_BACKEND_DAI_SLIMBUS_1_TX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + slim_tx_cfg[1].bit_format); + rate->min = rate->max = slim_tx_cfg[1].sample_rate; + channels->min = channels->max = slim_tx_cfg[1].channels; + break; + + case MSM_BACKEND_DAI_SLIMBUS_4_TX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + SNDRV_PCM_FORMAT_S32_LE); + rate->min = rate->max = SAMPLING_RATE_8KHZ; + channels->min = channels->max = msm_vi_feed_tx_ch; + break; + + case MSM_BACKEND_DAI_SLIMBUS_5_RX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + slim_rx_cfg[5].bit_format); + rate->min = rate->max = slim_rx_cfg[5].sample_rate; + channels->min = channels->max = slim_rx_cfg[5].channels; + break; + + case MSM_BACKEND_DAI_SLIMBUS_5_TX: + codec = rtd->codec; + rate->min = rate->max = SAMPLING_RATE_16KHZ; + channels->min = channels->max = 1; + + config = msm_codec_fn.get_afe_config_fn(codec, + AFE_SLIMBUS_SLAVE_PORT_CONFIG); + if (config) { + rc = afe_set_config(AFE_SLIMBUS_SLAVE_PORT_CONFIG, + config, SLIMBUS_5_TX); + if (rc) + pr_err("%s: Failed to set slimbus slave port config %d\n", + __func__, rc); + } + break; + + case MSM_BACKEND_DAI_SLIMBUS_7_RX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + slim_rx_cfg[SLIM_RX_7].bit_format); + rate->min = rate->max = slim_rx_cfg[SLIM_RX_7].sample_rate; + channels->min = channels->max = + slim_rx_cfg[SLIM_RX_7].channels; + break; + + case MSM_BACKEND_DAI_SLIMBUS_7_TX: + rate->min = rate->max = slim_tx_cfg[SLIM_TX_7].sample_rate; + channels->min = channels->max = + slim_tx_cfg[SLIM_TX_7].channels; + break; + + case MSM_BACKEND_DAI_SLIMBUS_8_TX: + rate->min = rate->max = slim_tx_cfg[SLIM_TX_8].sample_rate; + channels->min = channels->max = + slim_tx_cfg[SLIM_TX_8].channels; + break; + + case MSM_BACKEND_DAI_USB_RX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + usb_rx_cfg.bit_format); + rate->min = rate->max = usb_rx_cfg.sample_rate; + channels->min = channels->max = usb_rx_cfg.channels; + break; + + case MSM_BACKEND_DAI_USB_TX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + usb_tx_cfg.bit_format); + rate->min = rate->max = usb_tx_cfg.sample_rate; + channels->min = channels->max = usb_tx_cfg.channels; + break; + + case MSM_BACKEND_DAI_DISPLAY_PORT_RX: + idx = msm_ext_disp_get_idx_from_beid(dai_link->id); + if (idx < 0) { + pr_err("%s: Incorrect ext disp idx %d\n", + __func__, idx); + rc = idx; + goto done; + } + + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + ext_disp_rx_cfg[idx].bit_format); + rate->min = rate->max = ext_disp_rx_cfg[idx].sample_rate; + channels->min = channels->max = ext_disp_rx_cfg[idx].channels; + break; + + case MSM_BACKEND_DAI_AFE_PCM_RX: + channels->min = channels->max = proxy_rx_cfg.channels; + rate->min = rate->max = SAMPLING_RATE_48KHZ; + break; + + case MSM_BACKEND_DAI_PRI_TDM_RX_0: + channels->min = channels->max = + tdm_rx_cfg[TDM_PRI][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_PRI][TDM_0].bit_format); + rate->min = rate->max = tdm_rx_cfg[TDM_PRI][TDM_0].sample_rate; + break; + + case MSM_BACKEND_DAI_PRI_TDM_TX_0: + channels->min = channels->max = + tdm_tx_cfg[TDM_PRI][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_tx_cfg[TDM_PRI][TDM_0].bit_format); + rate->min = rate->max = tdm_tx_cfg[TDM_PRI][TDM_0].sample_rate; + break; + + case MSM_BACKEND_DAI_SEC_TDM_RX_0: + channels->min = channels->max = + tdm_rx_cfg[TDM_SEC][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_SEC][TDM_0].bit_format); + rate->min = rate->max = tdm_rx_cfg[TDM_SEC][TDM_0].sample_rate; + break; + + case MSM_BACKEND_DAI_SEC_TDM_TX_0: + channels->min = channels->max = + tdm_tx_cfg[TDM_SEC][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_tx_cfg[TDM_SEC][TDM_0].bit_format); + rate->min = rate->max = tdm_tx_cfg[TDM_SEC][TDM_0].sample_rate; + break; + + case MSM_BACKEND_DAI_TERT_TDM_RX_0: + channels->min = channels->max = + tdm_rx_cfg[TDM_TERT][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_TERT][TDM_0].bit_format); + rate->min = rate->max = tdm_rx_cfg[TDM_TERT][TDM_0].sample_rate; + break; + + case MSM_BACKEND_DAI_TERT_TDM_TX_0: + channels->min = channels->max = + tdm_tx_cfg[TDM_TERT][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_tx_cfg[TDM_TERT][TDM_0].bit_format); + rate->min = rate->max = tdm_tx_cfg[TDM_TERT][TDM_0].sample_rate; + break; + + case MSM_BACKEND_DAI_QUAT_TDM_RX_0: + channels->min = channels->max = + tdm_rx_cfg[TDM_QUAT][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_QUAT][TDM_0].bit_format); + rate->min = rate->max = tdm_rx_cfg[TDM_QUAT][TDM_0].sample_rate; + break; + + case MSM_BACKEND_DAI_QUAT_TDM_TX_0: + channels->min = channels->max = + tdm_tx_cfg[TDM_QUAT][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_tx_cfg[TDM_QUAT][TDM_0].bit_format); + rate->min = rate->max = tdm_tx_cfg[TDM_QUAT][TDM_0].sample_rate; + break; + + case MSM_BACKEND_DAI_AUXPCM_RX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + aux_pcm_rx_cfg[PRIM_AUX_PCM].bit_format); + rate->min = rate->max = + aux_pcm_rx_cfg[PRIM_AUX_PCM].sample_rate; + channels->min = channels->max = + aux_pcm_rx_cfg[PRIM_AUX_PCM].channels; + break; + + case MSM_BACKEND_DAI_AUXPCM_TX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + aux_pcm_tx_cfg[PRIM_AUX_PCM].bit_format); + rate->min = rate->max = + aux_pcm_tx_cfg[PRIM_AUX_PCM].sample_rate; + channels->min = channels->max = + aux_pcm_tx_cfg[PRIM_AUX_PCM].channels; + break; + + case MSM_BACKEND_DAI_SEC_AUXPCM_RX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + aux_pcm_rx_cfg[SEC_AUX_PCM].bit_format); + rate->min = rate->max = + aux_pcm_rx_cfg[SEC_AUX_PCM].sample_rate; + channels->min = channels->max = + aux_pcm_rx_cfg[SEC_AUX_PCM].channels; + break; + + case MSM_BACKEND_DAI_SEC_AUXPCM_TX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + aux_pcm_tx_cfg[SEC_AUX_PCM].bit_format); + rate->min = rate->max = + aux_pcm_tx_cfg[SEC_AUX_PCM].sample_rate; + channels->min = channels->max = + aux_pcm_tx_cfg[SEC_AUX_PCM].channels; + break; + + case MSM_BACKEND_DAI_TERT_AUXPCM_RX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + aux_pcm_rx_cfg[TERT_AUX_PCM].bit_format); + rate->min = rate->max = + aux_pcm_rx_cfg[TERT_AUX_PCM].sample_rate; + channels->min = channels->max = + aux_pcm_rx_cfg[TERT_AUX_PCM].channels; + break; + + case MSM_BACKEND_DAI_TERT_AUXPCM_TX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + aux_pcm_tx_cfg[TERT_AUX_PCM].bit_format); + rate->min = rate->max = + aux_pcm_tx_cfg[TERT_AUX_PCM].sample_rate; + channels->min = channels->max = + aux_pcm_tx_cfg[TERT_AUX_PCM].channels; + break; + + case MSM_BACKEND_DAI_QUAT_AUXPCM_RX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + aux_pcm_rx_cfg[QUAT_AUX_PCM].bit_format); + rate->min = rate->max = + aux_pcm_rx_cfg[QUAT_AUX_PCM].sample_rate; + channels->min = channels->max = + aux_pcm_rx_cfg[QUAT_AUX_PCM].channels; + break; + + case MSM_BACKEND_DAI_QUAT_AUXPCM_TX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + aux_pcm_tx_cfg[QUAT_AUX_PCM].bit_format); + rate->min = rate->max = + aux_pcm_tx_cfg[QUAT_AUX_PCM].sample_rate; + channels->min = channels->max = + aux_pcm_tx_cfg[QUAT_AUX_PCM].channels; + break; + + case MSM_BACKEND_DAI_PRI_MI2S_RX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + mi2s_rx_cfg[PRIM_MI2S].bit_format); + rate->min = rate->max = mi2s_rx_cfg[PRIM_MI2S].sample_rate; + channels->min = channels->max = + mi2s_rx_cfg[PRIM_MI2S].channels; + break; + + case MSM_BACKEND_DAI_PRI_MI2S_TX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + mi2s_tx_cfg[PRIM_MI2S].bit_format); + rate->min = rate->max = mi2s_tx_cfg[PRIM_MI2S].sample_rate; + channels->min = channels->max = + mi2s_tx_cfg[PRIM_MI2S].channels; + break; + + case MSM_BACKEND_DAI_SECONDARY_MI2S_RX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + mi2s_rx_cfg[SEC_MI2S].bit_format); + rate->min = rate->max = mi2s_rx_cfg[SEC_MI2S].sample_rate; + channels->min = channels->max = + mi2s_rx_cfg[SEC_MI2S].channels; + break; + + case MSM_BACKEND_DAI_SECONDARY_MI2S_TX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + mi2s_tx_cfg[SEC_MI2S].bit_format); + rate->min = rate->max = mi2s_tx_cfg[SEC_MI2S].sample_rate; + channels->min = channels->max = + mi2s_tx_cfg[SEC_MI2S].channels; + break; + + case MSM_BACKEND_DAI_TERTIARY_MI2S_RX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + mi2s_rx_cfg[TERT_MI2S].bit_format); + rate->min = rate->max = mi2s_rx_cfg[TERT_MI2S].sample_rate; + channels->min = channels->max = + mi2s_rx_cfg[TERT_MI2S].channels; + break; + + case MSM_BACKEND_DAI_TERTIARY_MI2S_TX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + mi2s_tx_cfg[TERT_MI2S].bit_format); + rate->min = rate->max = mi2s_tx_cfg[TERT_MI2S].sample_rate; + channels->min = channels->max = + mi2s_tx_cfg[TERT_MI2S].channels; + break; + + case MSM_BACKEND_DAI_QUATERNARY_MI2S_RX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + mi2s_rx_cfg[QUAT_MI2S].bit_format); + rate->min = rate->max = mi2s_rx_cfg[QUAT_MI2S].sample_rate; + channels->min = channels->max = + mi2s_rx_cfg[QUAT_MI2S].channels; + break; + + case MSM_BACKEND_DAI_QUATERNARY_MI2S_TX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + mi2s_tx_cfg[QUAT_MI2S].bit_format); + rate->min = rate->max = mi2s_tx_cfg[QUAT_MI2S].sample_rate; + channels->min = channels->max = + mi2s_tx_cfg[QUAT_MI2S].channels; + break; + + default: + rate->min = rate->max = SAMPLING_RATE_48KHZ; + break; + } + +done: + return rc; +} + +static bool msm_usbc_swap_gnd_mic(struct snd_soc_codec *codec, bool active) +{ + int value = 0; + bool ret = 0; + struct snd_soc_card *card = codec->component.card; + struct msm_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card); + struct pinctrl_state *en2_pinctrl_active; + struct pinctrl_state *en2_pinctrl_sleep; + + if (!pdata->usbc_en2_gpio_p) { + if (active) { + /* if active and usbc_en2_gpio undefined, get pin */ + pdata->usbc_en2_gpio_p = devm_pinctrl_get(card->dev); + if (IS_ERR_OR_NULL(pdata->usbc_en2_gpio_p)) { + dev_err(card->dev, + "%s: Can't get EN2 gpio pinctrl:%ld\n", + __func__, + PTR_ERR(pdata->usbc_en2_gpio_p)); + pdata->usbc_en2_gpio_p = NULL; + return false; + } + } else + /* if not active and usbc_en2_gpio undefined, return */ + return false; + } + + pdata->usbc_en2_gpio = of_get_named_gpio(card->dev->of_node, + "qcom,usbc-analog-en2-gpio", 0); + if (!gpio_is_valid(pdata->usbc_en2_gpio)) { + dev_err(card->dev, "%s, property %s not in node %s", + __func__, "qcom,usbc-analog-en2-gpio", + card->dev->of_node->full_name); + return false; + } + + en2_pinctrl_active = pinctrl_lookup_state( + pdata->usbc_en2_gpio_p, "aud_active"); + if (IS_ERR_OR_NULL(en2_pinctrl_active)) { + dev_err(card->dev, + "%s: Cannot get aud_active pinctrl state:%ld\n", + __func__, PTR_ERR(en2_pinctrl_active)); + ret = false; + goto err_lookup_state; + } + + en2_pinctrl_sleep = pinctrl_lookup_state( + pdata->usbc_en2_gpio_p, "aud_sleep"); + if (IS_ERR_OR_NULL(en2_pinctrl_sleep)) { + dev_err(card->dev, + "%s: Cannot get aud_sleep pinctrl state:%ld\n", + __func__, PTR_ERR(en2_pinctrl_sleep)); + ret = false; + goto err_lookup_state; + } + + /* if active and usbc_en2_gpio_p defined, swap using usbc_en2_gpio_p */ + if (active) { + dev_dbg(codec->dev, "%s: enter\n", __func__); + if (pdata->usbc_en2_gpio_p) { + value = gpio_get_value_cansleep(pdata->usbc_en2_gpio); + if (value) + pinctrl_select_state(pdata->usbc_en2_gpio_p, + en2_pinctrl_sleep); + else + pinctrl_select_state(pdata->usbc_en2_gpio_p, + en2_pinctrl_active); + } else if (pdata->usbc_en2_gpio >= 0) { + value = gpio_get_value_cansleep(pdata->usbc_en2_gpio); + gpio_set_value_cansleep(pdata->usbc_en2_gpio, !value); + } + pr_debug("%s: swap select switch %d to %d\n", __func__, + value, !value); + ret = true; + } else { + /* if not active, release usbc_en2_gpio_p pin */ + pinctrl_select_state(pdata->usbc_en2_gpio_p, + en2_pinctrl_sleep); + } + +err_lookup_state: + devm_pinctrl_put(pdata->usbc_en2_gpio_p); + pdata->usbc_en2_gpio_p = NULL; + return ret; +} + +static bool msm_swap_gnd_mic(struct snd_soc_codec *codec, bool active) +{ + int value = 0; + int ret = 0; + struct snd_soc_card *card = codec->component.card; + struct msm_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card); + + if (!pdata) + return false; + + if (!wcd_mbhc_cfg.enable_usbc_analog) { + /* if usbc is not defined, swap using us_euro_gpio_p */ + if (pdata->us_euro_gpio_p) { + value = msm_cdc_pinctrl_get_state( + pdata->us_euro_gpio_p); + if (value) + msm_cdc_pinctrl_select_sleep_state( + pdata->us_euro_gpio_p); + else + msm_cdc_pinctrl_select_active_state( + pdata->us_euro_gpio_p); + } else if (pdata->us_euro_gpio >= 0) { + value = gpio_get_value_cansleep( + pdata->us_euro_gpio); + gpio_set_value_cansleep( + pdata->us_euro_gpio, !value); + } + pr_debug("%s: swap select switch %d to %d\n", __func__, + value, !value); + ret = true; + } else { + /* if usbc is defined, swap using usbc_en2 */ + ret = msm_usbc_swap_gnd_mic(codec, active); + } + return ret; +} + +static int msm_afe_set_config(struct snd_soc_codec *codec) +{ + int ret = 0; + void *config_data = NULL; + + if (!msm_codec_fn.get_afe_config_fn) { + dev_err(codec->dev, "%s: codec get afe config not init'ed\n", + __func__); + return -EINVAL; + } + + config_data = msm_codec_fn.get_afe_config_fn(codec, + AFE_CDC_REGISTERS_CONFIG); + if (config_data) { + ret = afe_set_config(AFE_CDC_REGISTERS_CONFIG, config_data, 0); + if (ret) { + dev_err(codec->dev, + "%s: Failed to set codec registers config %d\n", + __func__, ret); + return ret; + } + } + + config_data = msm_codec_fn.get_afe_config_fn(codec, + AFE_CDC_REGISTER_PAGE_CONFIG); + if (config_data) { + ret = afe_set_config(AFE_CDC_REGISTER_PAGE_CONFIG, config_data, + 0); + if (ret) + dev_err(codec->dev, + "%s: Failed to set cdc register page config\n", + __func__); + } + + config_data = msm_codec_fn.get_afe_config_fn(codec, + AFE_SLIMBUS_SLAVE_CONFIG); + if (config_data) { + ret = afe_set_config(AFE_SLIMBUS_SLAVE_CONFIG, config_data, 0); + if (ret) { + dev_err(codec->dev, + "%s: Failed to set slimbus slave config %d\n", + __func__, ret); + return ret; + } + } + + return 0; +} + +static void msm_afe_clear_config(void) +{ + afe_clear_config(AFE_CDC_REGISTERS_CONFIG); + afe_clear_config(AFE_SLIMBUS_SLAVE_CONFIG); +} + +static int msm_adsp_power_up_config(struct snd_soc_codec *codec, + struct snd_card *card) +{ + int ret = 0; + unsigned long timeout; + int adsp_ready = 0; + bool snd_card_online = 0; + + timeout = jiffies + + msecs_to_jiffies(ADSP_STATE_READY_TIMEOUT_MS); + + do { + if (!snd_card_online) { + snd_card_online = snd_card_is_online_state(card); + pr_debug("%s: Sound card is %s\n", __func__, + snd_card_online ? "Online" : "Offline"); + } + if (!adsp_ready) { + adsp_ready = q6core_is_adsp_ready(); + pr_debug("%s: ADSP Audio is %s\n", __func__, + adsp_ready ? "ready" : "not ready"); + } + if (snd_card_online && adsp_ready) + break; + + /* + * Sound card/ADSP will be coming up after subsystem restart and + * it might not be fully up when the control reaches + * here. So, wait for 50msec before checking ADSP state + */ + msleep(50); + } while (time_after(timeout, jiffies)); + + if (!snd_card_online || !adsp_ready) { + pr_err("%s: Timeout. Sound card is %s, ADSP Audio is %s\n", + __func__, + snd_card_online ? "Online" : "Offline", + adsp_ready ? "ready" : "not ready"); + ret = -ETIMEDOUT; + goto err; + } + + ret = msm_afe_set_config(codec); + if (ret) + pr_err("%s: Failed to set AFE config. err %d\n", + __func__, ret); + + return 0; + +err: + return ret; +} + +static int sdm845_notifier_service_cb(struct notifier_block *this, + unsigned long opcode, void *ptr) +{ + int ret; + struct snd_soc_card *card = NULL; + const char *be_dl_name = LPASS_BE_SLIMBUS_0_RX; + struct snd_soc_pcm_runtime *rtd; + struct snd_soc_codec *codec; + + pr_debug("%s: Service opcode 0x%lx\n", __func__, opcode); + + switch (opcode) { + case AUDIO_NOTIFIER_SERVICE_DOWN: + /* + * Use flag to ignore initial boot notifications + * On initial boot msm_adsp_power_up_config is + * called on init. There is no need to clear + * and set the config again on initial boot. + */ + if (is_initial_boot) + break; + msm_afe_clear_config(); + break; + case AUDIO_NOTIFIER_SERVICE_UP: + if (is_initial_boot) { + is_initial_boot = false; + break; + } + if (!spdev) + return -EINVAL; + + card = platform_get_drvdata(spdev); + rtd = snd_soc_get_pcm_runtime(card, be_dl_name); + if (!rtd) { + dev_err(card->dev, + "%s: snd_soc_get_pcm_runtime for %s failed!\n", + __func__, be_dl_name); + ret = -EINVAL; + goto err; + } + codec = rtd->codec; + + ret = msm_adsp_power_up_config(codec, card->snd_card); + if (ret < 0) { + dev_err(card->dev, + "%s: msm_adsp_power_up_config failed ret = %d!\n", + __func__, ret); + goto err; + } + break; + default: + break; + } +err: + return NOTIFY_OK; +} + +static struct notifier_block service_nb = { + .notifier_call = sdm845_notifier_service_cb, + .priority = -INT_MAX, +}; + +static int msm_audrx_init(struct snd_soc_pcm_runtime *rtd) +{ + int ret = 0; + void *config_data; + struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_component *aux_comp; + struct snd_card *card; + struct snd_info_entry *entry; + struct msm_asoc_mach_data *pdata = + snd_soc_card_get_drvdata(rtd->card); + + /* Codec SLIMBUS configuration + * RX1, RX2, RX3, RX4, RX5, RX6, RX7, RX8 + * TX1, TX2, TX3, TX4, TX5, TX6, TX7, TX8, TX9, TX10, TX11, TX12, TX13 + * TX14, TX15, TX16 + */ + unsigned int rx_ch[WCD934X_RX_MAX] = {144, 145, 146, 147, 148, 149, + 150, 151}; + unsigned int tx_ch[WCD934X_TX_MAX] = {128, 129, 130, 131, 132, 133, + 134, 135, 136, 137, 138, 139, + 140, 141, 142, 143}; + + pr_info("%s: dev_name%s\n", __func__, dev_name(cpu_dai->dev)); + + rtd->pmdown_time = 0; + + ret = snd_soc_add_codec_controls(codec, msm_snd_controls, + ARRAY_SIZE(msm_snd_controls)); + if (ret < 0) { + pr_err("%s: add_codec_controls failed, err %d\n", + __func__, ret); + return ret; + } + + snd_soc_dapm_new_controls(dapm, msm_dapm_widgets, + ARRAY_SIZE(msm_dapm_widgets)); + + snd_soc_dapm_add_routes(dapm, wcd_audio_paths, + ARRAY_SIZE(wcd_audio_paths)); + + snd_soc_dapm_ignore_suspend(dapm, "Handset Mic"); + snd_soc_dapm_ignore_suspend(dapm, "Headset Mic"); + snd_soc_dapm_ignore_suspend(dapm, "ANCRight Headset Mic"); + snd_soc_dapm_ignore_suspend(dapm, "ANCLeft Headset Mic"); + snd_soc_dapm_ignore_suspend(dapm, "Digital Mic0"); + snd_soc_dapm_ignore_suspend(dapm, "Digital Mic1"); + snd_soc_dapm_ignore_suspend(dapm, "Digital Mic2"); + snd_soc_dapm_ignore_suspend(dapm, "Digital Mic3"); + snd_soc_dapm_ignore_suspend(dapm, "Digital Mic4"); + snd_soc_dapm_ignore_suspend(dapm, "Digital Mic5"); + snd_soc_dapm_ignore_suspend(dapm, "Analog Mic5"); + snd_soc_dapm_ignore_suspend(dapm, "MADINPUT"); + snd_soc_dapm_ignore_suspend(dapm, "MAD_CPE_INPUT"); + snd_soc_dapm_ignore_suspend(dapm, "MAD_CPE_OUT1"); + snd_soc_dapm_ignore_suspend(dapm, "MAD_CPE_OUT2"); + snd_soc_dapm_ignore_suspend(dapm, "EAR"); + snd_soc_dapm_ignore_suspend(dapm, "LINEOUT1"); + snd_soc_dapm_ignore_suspend(dapm, "LINEOUT2"); + snd_soc_dapm_ignore_suspend(dapm, "ANC EAR"); + snd_soc_dapm_ignore_suspend(dapm, "SPK1 OUT"); + snd_soc_dapm_ignore_suspend(dapm, "SPK2 OUT"); + snd_soc_dapm_ignore_suspend(dapm, "HPHL"); + snd_soc_dapm_ignore_suspend(dapm, "HPHR"); + snd_soc_dapm_ignore_suspend(dapm, "AIF4 VI"); + snd_soc_dapm_ignore_suspend(dapm, "VIINPUT"); + snd_soc_dapm_ignore_suspend(dapm, "ANC HPHL"); + snd_soc_dapm_ignore_suspend(dapm, "ANC HPHR"); + + snd_soc_dapm_sync(dapm); + + snd_soc_dai_set_channel_map(codec_dai, ARRAY_SIZE(tx_ch), + tx_ch, ARRAY_SIZE(rx_ch), rx_ch); + + msm_codec_fn.get_afe_config_fn = tavil_get_afe_config; + + ret = msm_adsp_power_up_config(codec, rtd->card->snd_card); + if (ret) { + pr_err("%s: Failed to set AFE config %d\n", __func__, ret); + goto err; + } + + config_data = msm_codec_fn.get_afe_config_fn(codec, + AFE_AANC_VERSION); + if (config_data) { + ret = afe_set_config(AFE_AANC_VERSION, config_data, 0); + if (ret) { + pr_err("%s: Failed to set aanc version %d\n", + __func__, ret); + goto err; + } + } + + /* + * Send speaker configuration only for WSA8810. + * Default configuration is for WSA8815. + */ + pr_debug("%s: Number of aux devices: %d\n", + __func__, rtd->card->num_aux_devs); + if (rtd->card->num_aux_devs && + !list_empty(&rtd->card->aux_comp_list)) { + aux_comp = list_first_entry(&rtd->card->aux_comp_list, + struct snd_soc_component, list_aux); + if (!strcmp(aux_comp->name, WSA8810_NAME_1) || + !strcmp(aux_comp->name, WSA8810_NAME_2)) { + tavil_set_spkr_mode(rtd->codec, WCD934X_SPKR_MODE_1); + tavil_set_spkr_gain_offset(rtd->codec, + WCD934X_RX_GAIN_OFFSET_M1P5_DB); + } + } + card = rtd->card->snd_card; + entry = snd_info_create_subdir(card->module, "codecs", + card->proc_root); + if (!entry) { + pr_debug("%s: Cannot create codecs module entry\n", + __func__); + pdata->codec_root = NULL; + goto done; + } + pdata->codec_root = entry; + tavil_codec_info_create_codec_entry(pdata->codec_root, codec); + +done: + codec_reg_done = true; + return 0; + +err: + return ret; +} + +static int msm_wcn_init(struct snd_soc_pcm_runtime *rtd) +{ + unsigned int rx_ch[WCN_CDC_SLIM_RX_CH_MAX] = {157, 158}; + unsigned int tx_ch[WCN_CDC_SLIM_TX_CH_MAX] = {159, 160, 161}; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + + return snd_soc_dai_set_channel_map(codec_dai, ARRAY_SIZE(tx_ch), + tx_ch, ARRAY_SIZE(rx_ch), rx_ch); +} + +static void *def_tavil_mbhc_cal(void) +{ + void *tavil_wcd_cal; + struct wcd_mbhc_btn_detect_cfg *btn_cfg; + u16 *btn_high; + + tavil_wcd_cal = kzalloc(WCD_MBHC_CAL_SIZE(WCD_MBHC_DEF_BUTTONS, + WCD9XXX_MBHC_DEF_RLOADS), GFP_KERNEL); + if (!tavil_wcd_cal) + return NULL; + +#define S(X, Y) ((WCD_MBHC_CAL_PLUG_TYPE_PTR(tavil_wcd_cal)->X) = (Y)) + S(v_hs_max, 1600); +#undef S +#define S(X, Y) ((WCD_MBHC_CAL_BTN_DET_PTR(tavil_wcd_cal)->X) = (Y)) + S(num_btn, WCD_MBHC_DEF_BUTTONS); +#undef S + + btn_cfg = WCD_MBHC_CAL_BTN_DET_PTR(tavil_wcd_cal); + btn_high = ((void *)&btn_cfg->_v_btn_low) + + (sizeof(btn_cfg->_v_btn_low[0]) * btn_cfg->num_btn); + + btn_high[0] = 75; + btn_high[1] = 150; + btn_high[2] = 237; + btn_high[3] = 500; + btn_high[4] = 500; + btn_high[5] = 500; + btn_high[6] = 500; + btn_high[7] = 500; + + return tavil_wcd_cal; +} + +static int msm_snd_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai_link *dai_link = rtd->dai_link; + + int ret = 0; + u32 rx_ch[SLIM_MAX_RX_PORTS], tx_ch[SLIM_MAX_TX_PORTS]; + u32 rx_ch_cnt = 0, tx_ch_cnt = 0; + u32 user_set_tx_ch = 0; + u32 rx_ch_count; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + ret = snd_soc_dai_get_channel_map(codec_dai, + &tx_ch_cnt, tx_ch, &rx_ch_cnt, rx_ch); + if (ret < 0) { + pr_err("%s: failed to get codec chan map, err:%d\n", + __func__, ret); + goto err; + } + if (dai_link->id == MSM_BACKEND_DAI_SLIMBUS_5_RX) { + pr_debug("%s: rx_5_ch=%d\n", __func__, + slim_rx_cfg[5].channels); + rx_ch_count = slim_rx_cfg[5].channels; + } else if (dai_link->id == MSM_BACKEND_DAI_SLIMBUS_2_RX) { + pr_debug("%s: rx_2_ch=%d\n", __func__, + slim_rx_cfg[2].channels); + rx_ch_count = slim_rx_cfg[2].channels; + } else if (dai_link->id == MSM_BACKEND_DAI_SLIMBUS_6_RX) { + pr_debug("%s: rx_6_ch=%d\n", __func__, + slim_rx_cfg[6].channels); + rx_ch_count = slim_rx_cfg[6].channels; + } else { + pr_debug("%s: rx_0_ch=%d\n", __func__, + slim_rx_cfg[0].channels); + rx_ch_count = slim_rx_cfg[0].channels; + } + ret = snd_soc_dai_set_channel_map(cpu_dai, 0, 0, + rx_ch_count, rx_ch); + if (ret < 0) { + pr_err("%s: failed to set cpu chan map, err:%d\n", + __func__, ret); + goto err; + } + } else { + + pr_debug("%s: %s_tx_dai_id_%d_ch=%d\n", __func__, + codec_dai->name, codec_dai->id, user_set_tx_ch); + ret = snd_soc_dai_get_channel_map(codec_dai, + &tx_ch_cnt, tx_ch, &rx_ch_cnt, rx_ch); + if (ret < 0) { + pr_err("%s: failed to get codec chan map\n, err:%d\n", + __func__, ret); + goto err; + } + /* For _tx1 case */ + if (dai_link->id == MSM_BACKEND_DAI_SLIMBUS_0_TX) + user_set_tx_ch = slim_tx_cfg[0].channels; + /* For _tx3 case */ + else if (dai_link->id == MSM_BACKEND_DAI_SLIMBUS_1_TX) + user_set_tx_ch = slim_tx_cfg[1].channels; + else if (dai_link->id == MSM_BACKEND_DAI_SLIMBUS_4_TX) + user_set_tx_ch = msm_vi_feed_tx_ch; + else + user_set_tx_ch = tx_ch_cnt; + + pr_debug("%s: msm_slim_0_tx_ch(%d) user_set_tx_ch(%d) tx_ch_cnt(%d), BE id (%d)\n", + __func__, slim_tx_cfg[0].channels, user_set_tx_ch, + tx_ch_cnt, dai_link->id); + + ret = snd_soc_dai_set_channel_map(cpu_dai, + user_set_tx_ch, tx_ch, 0, 0); + if (ret < 0) + pr_err("%s: failed to set cpu chan map, err:%d\n", + __func__, ret); + } + +err: + return ret; +} + +static int msm_slimbus_2_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + unsigned int rx_ch[SLIM_MAX_RX_PORTS], tx_ch[SLIM_MAX_TX_PORTS]; + unsigned int rx_ch_cnt = 0, tx_ch_cnt = 0; + unsigned int num_tx_ch = 0; + unsigned int num_rx_ch = 0; + int ret = 0; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + num_rx_ch = params_channels(params); + pr_debug("%s: %s rx_dai_id = %d num_ch = %d\n", __func__, + codec_dai->name, codec_dai->id, num_rx_ch); + ret = snd_soc_dai_get_channel_map(codec_dai, + &tx_ch_cnt, tx_ch, &rx_ch_cnt, rx_ch); + if (ret < 0) { + pr_err("%s: failed to get codec chan map, err:%d\n", + __func__, ret); + goto err; + } + ret = snd_soc_dai_set_channel_map(cpu_dai, 0, 0, + num_rx_ch, rx_ch); + if (ret < 0) { + pr_err("%s: failed to set cpu chan map, err:%d\n", + __func__, ret); + goto err; + } + } else { + num_tx_ch = params_channels(params); + pr_debug("%s: %s tx_dai_id = %d num_ch = %d\n", __func__, + codec_dai->name, codec_dai->id, num_tx_ch); + ret = snd_soc_dai_get_channel_map(codec_dai, + &tx_ch_cnt, tx_ch, &rx_ch_cnt, rx_ch); + if (ret < 0) { + pr_err("%s: failed to get codec chan map, err:%d\n", + __func__, ret); + goto err; + } + ret = snd_soc_dai_set_channel_map(cpu_dai, + num_tx_ch, tx_ch, 0, 0); + if (ret < 0) { + pr_err("%s: failed to set cpu chan map, err:%d\n", + __func__, ret); + goto err; + } + } + +err: + return ret; +} + +static int msm_wcn_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai_link *dai_link = rtd->dai_link; + u32 rx_ch[WCN_CDC_SLIM_RX_CH_MAX], tx_ch[WCN_CDC_SLIM_TX_CH_MAX]; + u32 rx_ch_cnt = 0, tx_ch_cnt = 0; + int ret; + + dev_dbg(rtd->dev, "%s: %s_tx_dai_id_%d\n", __func__, + codec_dai->name, codec_dai->id); + ret = snd_soc_dai_get_channel_map(codec_dai, + &tx_ch_cnt, tx_ch, &rx_ch_cnt, rx_ch); + if (ret) { + dev_err(rtd->dev, + "%s: failed to get BTFM codec chan map\n, err:%d\n", + __func__, ret); + goto err; + } + + dev_dbg(rtd->dev, "%s: tx_ch_cnt(%d) BE id %d\n", + __func__, tx_ch_cnt, dai_link->id); + + ret = snd_soc_dai_set_channel_map(cpu_dai, + tx_ch_cnt, tx_ch, rx_ch_cnt, rx_ch); + if (ret) + dev_err(rtd->dev, "%s: failed to set cpu chan map, err:%d\n", + __func__, ret); + +err: + return ret; +} + +static int msm_aux_pcm_snd_startup(struct snd_pcm_substream *substream) +{ + int ret = 0; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + int index = cpu_dai->id - 1; + + dev_dbg(rtd->card->dev, + "%s: substream = %s stream = %d, dai name %s, dai ID %d\n", + __func__, substream->name, substream->stream, + cpu_dai->name, cpu_dai->id); + + if (index < PRIM_AUX_PCM || index > QUAT_AUX_PCM) { + ret = -EINVAL; + dev_err(rtd->card->dev, + "%s: CPU DAI id (%d) out of range\n", + __func__, cpu_dai->id); + goto err; + } + + mutex_lock(&auxpcm_intf_conf[index].lock); + if (++auxpcm_intf_conf[index].ref_cnt == 1) { + if (mi2s_auxpcm_conf[index].pcm_i2s_sel_vt_addr != NULL) { + mutex_lock(&mi2s_auxpcm_conf[index].lock); + iowrite32(1, + mi2s_auxpcm_conf[index].pcm_i2s_sel_vt_addr); + mutex_unlock(&mi2s_auxpcm_conf[index].lock); + } else { + dev_err(rtd->card->dev, + "%s lpaif_tert_muxsel_virt_addr is NULL\n", + __func__); + ret = -EINVAL; + } + } + if (ret < 0) + auxpcm_intf_conf[index].ref_cnt--; + + mutex_unlock(&auxpcm_intf_conf[index].lock); + +err: + return ret; +} + +static void msm_aux_pcm_snd_shutdown(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + int index = rtd->cpu_dai->id - 1; + + dev_dbg(rtd->card->dev, + "%s: substream = %s stream = %d, dai name %s, dai ID %d\n", + __func__, + substream->name, substream->stream, + rtd->cpu_dai->name, rtd->cpu_dai->id); + + if (index < PRIM_AUX_PCM || index > QUAT_AUX_PCM) { + dev_err(rtd->card->dev, + "%s: CPU DAI id (%d) out of range\n", + __func__, rtd->cpu_dai->id); + return; + } + + mutex_lock(&auxpcm_intf_conf[index].lock); + if (--auxpcm_intf_conf[index].ref_cnt == 0) { + if (mi2s_auxpcm_conf[index].pcm_i2s_sel_vt_addr != NULL) { + mutex_lock(&mi2s_auxpcm_conf[index].lock); + iowrite32(0, + mi2s_auxpcm_conf[index].pcm_i2s_sel_vt_addr); + mutex_unlock(&mi2s_auxpcm_conf[index].lock); + } else { + dev_err(rtd->card->dev, + "%s lpaif_tert_muxsel_virt_addr is NULL\n", + __func__); + } + } + mutex_unlock(&auxpcm_intf_conf[index].lock); +} + +static int msm_get_port_id(int be_id) +{ + int afe_port_id; + + switch (be_id) { + case MSM_BACKEND_DAI_PRI_MI2S_RX: + afe_port_id = AFE_PORT_ID_PRIMARY_MI2S_RX; + break; + case MSM_BACKEND_DAI_PRI_MI2S_TX: + afe_port_id = AFE_PORT_ID_PRIMARY_MI2S_TX; + break; + case MSM_BACKEND_DAI_SECONDARY_MI2S_RX: + afe_port_id = AFE_PORT_ID_SECONDARY_MI2S_RX; + break; + case MSM_BACKEND_DAI_SECONDARY_MI2S_TX: + afe_port_id = AFE_PORT_ID_SECONDARY_MI2S_TX; + break; + case MSM_BACKEND_DAI_TERTIARY_MI2S_RX: + afe_port_id = AFE_PORT_ID_TERTIARY_MI2S_RX; + break; + case MSM_BACKEND_DAI_TERTIARY_MI2S_TX: + afe_port_id = AFE_PORT_ID_TERTIARY_MI2S_TX; + break; + case MSM_BACKEND_DAI_QUATERNARY_MI2S_RX: + afe_port_id = AFE_PORT_ID_QUATERNARY_MI2S_RX; + break; + case MSM_BACKEND_DAI_QUATERNARY_MI2S_TX: + afe_port_id = AFE_PORT_ID_QUATERNARY_MI2S_TX; + break; + default: + pr_err("%s: Invalid BE id: %d\n", __func__, be_id); + afe_port_id = -EINVAL; + } + + return afe_port_id; +} + +static u32 get_mi2s_bits_per_sample(u32 bit_format) +{ + u32 bit_per_sample; + + switch (bit_format) { + case SNDRV_PCM_FORMAT_S32_LE: + case SNDRV_PCM_FORMAT_S24_3LE: + case SNDRV_PCM_FORMAT_S24_LE: + bit_per_sample = 32; + break; + case SNDRV_PCM_FORMAT_S16_LE: + default: + bit_per_sample = 16; + break; + } + + return bit_per_sample; +} + +static void update_mi2s_clk_val(int dai_id, int stream) +{ + u32 bit_per_sample; + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + bit_per_sample = + get_mi2s_bits_per_sample(mi2s_rx_cfg[dai_id].bit_format); + mi2s_clk[dai_id].clk_freq_in_hz = + mi2s_rx_cfg[dai_id].sample_rate * 2 * bit_per_sample; + } else { + bit_per_sample = + get_mi2s_bits_per_sample(mi2s_tx_cfg[dai_id].bit_format); + mi2s_clk[dai_id].clk_freq_in_hz = + mi2s_tx_cfg[dai_id].sample_rate * 2 * bit_per_sample; + } +} + +static int msm_mi2s_set_sclk(struct snd_pcm_substream *substream, bool enable) +{ + int ret = 0; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + int port_id = 0; + int index = cpu_dai->id; + + port_id = msm_get_port_id(rtd->dai_link->id); + if (port_id < 0) { + dev_err(rtd->card->dev, "%s: Invalid port_id\n", __func__); + ret = port_id; + goto err; + } + + if (enable) { + update_mi2s_clk_val(index, substream->stream); + dev_dbg(rtd->card->dev, "%s: clock rate %ul\n", __func__, + mi2s_clk[index].clk_freq_in_hz); + } + + mi2s_clk[index].enable = enable; + ret = afe_set_lpass_clock_v2(port_id, + &mi2s_clk[index]); + if (ret < 0) { + dev_err(rtd->card->dev, + "%s: afe lpass clock failed for port 0x%x , err:%d\n", + __func__, port_id, ret); + goto err; + } + +err: + return ret; +} + +static int msm_set_pinctrl(struct msm_pinctrl_info *pinctrl_info, + enum pinctrl_pin_state new_state) +{ + int ret = 0; + int curr_state = 0; + + if (pinctrl_info == NULL) { + pr_err("%s: pinctrl_info is NULL\n", __func__); + ret = -EINVAL; + goto err; + } + + if (pinctrl_info->pinctrl == NULL) { + pr_err("%s: pinctrl_info->pinctrl is NULL\n", __func__); + ret = -EINVAL; + goto err; + } + + curr_state = pinctrl_info->curr_state; + pinctrl_info->curr_state = new_state; + pr_debug("%s: curr_state = %s new_state = %s\n", __func__, + pin_states[curr_state], pin_states[pinctrl_info->curr_state]); + + if (curr_state == pinctrl_info->curr_state) { + pr_debug("%s: Already in same state\n", __func__); + goto err; + } + + if (curr_state != STATE_DISABLE && + pinctrl_info->curr_state != STATE_DISABLE) { + pr_debug("%s: state already active cannot switch\n", __func__); + ret = -EIO; + goto err; + } + + switch (pinctrl_info->curr_state) { + case STATE_MI2S_ACTIVE: + ret = pinctrl_select_state(pinctrl_info->pinctrl, + pinctrl_info->mi2s_active); + if (ret) { + pr_err("%s: MI2S state select failed with %d\n", + __func__, ret); + ret = -EIO; + goto err; + } + break; + case STATE_TDM_ACTIVE: + ret = pinctrl_select_state(pinctrl_info->pinctrl, + pinctrl_info->tdm_active); + if (ret) { + pr_err("%s: TDM state select failed with %d\n", + __func__, ret); + ret = -EIO; + goto err; + } + break; + case STATE_DISABLE: + if (curr_state == STATE_MI2S_ACTIVE) { + ret = pinctrl_select_state(pinctrl_info->pinctrl, + pinctrl_info->mi2s_disable); + } else { + ret = pinctrl_select_state(pinctrl_info->pinctrl, + pinctrl_info->tdm_disable); + } + if (ret) { + pr_err("%s: state disable failed with %d\n", + __func__, ret); + ret = -EIO; + goto err; + } + break; + default: + pr_err("%s: TLMM pin state is invalid\n", __func__); + return -EINVAL; + } + +err: + return ret; +} + +static void msm_release_pinctrl(struct platform_device *pdev) +{ + struct snd_soc_card *card = platform_get_drvdata(pdev); + struct msm_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card); + struct msm_pinctrl_info *pinctrl_info = &pdata->pinctrl_info; + + if (pinctrl_info->pinctrl) { + devm_pinctrl_put(pinctrl_info->pinctrl); + pinctrl_info->pinctrl = NULL; + } +} + +static int msm_get_pinctrl(struct platform_device *pdev) +{ + struct snd_soc_card *card = platform_get_drvdata(pdev); + struct msm_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card); + struct msm_pinctrl_info *pinctrl_info = NULL; + struct pinctrl *pinctrl; + int ret; + + pinctrl_info = &pdata->pinctrl_info; + + if (pinctrl_info == NULL) { + pr_err("%s: pinctrl_info is NULL\n", __func__); + return -EINVAL; + } + + pinctrl = devm_pinctrl_get(&pdev->dev); + if (IS_ERR_OR_NULL(pinctrl)) { + pr_err("%s: Unable to get pinctrl handle\n", __func__); + return -EINVAL; + } + pinctrl_info->pinctrl = pinctrl; + + /* get all the states handles from Device Tree */ + pinctrl_info->mi2s_disable = pinctrl_lookup_state(pinctrl, + "quat-mi2s-sleep"); + if (IS_ERR(pinctrl_info->mi2s_disable)) { + pr_err("%s: could not get mi2s_disable pinstate\n", __func__); + goto err; + } + pinctrl_info->mi2s_active = pinctrl_lookup_state(pinctrl, + "quat-mi2s-active"); + if (IS_ERR(pinctrl_info->mi2s_active)) { + pr_err("%s: could not get mi2s_active pinstate\n", __func__); + goto err; + } + pinctrl_info->tdm_disable = pinctrl_lookup_state(pinctrl, + "quat-tdm-sleep"); + if (IS_ERR(pinctrl_info->tdm_disable)) { + pr_err("%s: could not get tdm_disable pinstate\n", __func__); + goto err; + } + pinctrl_info->tdm_active = pinctrl_lookup_state(pinctrl, + "quat-tdm-active"); + if (IS_ERR(pinctrl_info->tdm_active)) { + pr_err("%s: could not get tdm_active pinstate\n", + __func__); + goto err; + } + /* Reset the TLMM pins to a default state */ + ret = pinctrl_select_state(pinctrl_info->pinctrl, + pinctrl_info->mi2s_disable); + if (ret != 0) { + pr_err("%s: Disable TLMM pins failed with %d\n", + __func__, ret); + ret = -EIO; + goto err; + } + pinctrl_info->curr_state = STATE_DISABLE; + + return 0; + +err: + devm_pinctrl_put(pinctrl); + pinctrl_info->pinctrl = NULL; + return -EINVAL; +} + +static int msm_tdm_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + + if (cpu_dai->id == AFE_PORT_ID_QUATERNARY_TDM_RX) { + channels->min = channels->max = + tdm_rx_cfg[TDM_QUAT][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_QUAT][TDM_0].bit_format); + rate->min = rate->max = + tdm_rx_cfg[TDM_QUAT][TDM_0].sample_rate; + } else if (cpu_dai->id == AFE_PORT_ID_SECONDARY_TDM_RX) { + channels->min = channels->max = + tdm_rx_cfg[TDM_SEC][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_SEC][TDM_0].bit_format); + rate->min = rate->max = tdm_rx_cfg[TDM_SEC][TDM_0].sample_rate; + } else { + pr_err("%s: dai id 0x%x not supported\n", + __func__, cpu_dai->id); + return -EINVAL; + } + + pr_debug("%s: dai id = 0x%x channels = %d rate = %d format = 0x%x\n", + __func__, cpu_dai->id, channels->max, rate->max, + params_format(params)); + + return 0; +} + +static int sdm845_tdm_snd_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + int ret = 0; + int slot_width = 32; + int channels, slots; + unsigned int slot_mask, rate, clk_freq; + unsigned int slot_offset[8] = {0, 4, 8, 12, 16, 20, 24, 28}; + + pr_debug("%s: dai id = 0x%x\n", __func__, cpu_dai->id); + + /* currently only supporting TDM_RX_0 and TDM_TX_0 */ + switch (cpu_dai->id) { + case AFE_PORT_ID_PRIMARY_TDM_RX: + slots = tdm_rx_cfg[TDM_PRI][TDM_0].channels; + break; + case AFE_PORT_ID_SECONDARY_TDM_RX: + slots = tdm_rx_cfg[TDM_SEC][TDM_0].channels; + break; + case AFE_PORT_ID_TERTIARY_TDM_RX: + slots = tdm_rx_cfg[TDM_TERT][TDM_0].channels; + break; + case AFE_PORT_ID_QUATERNARY_TDM_RX: + slots = tdm_rx_cfg[TDM_QUAT][TDM_0].channels; + break; + case AFE_PORT_ID_PRIMARY_TDM_TX: + slots = tdm_tx_cfg[TDM_PRI][TDM_0].channels; + break; + case AFE_PORT_ID_SECONDARY_TDM_TX: + slots = tdm_tx_cfg[TDM_SEC][TDM_0].channels; + break; + case AFE_PORT_ID_TERTIARY_TDM_TX: + slots = tdm_tx_cfg[TDM_TERT][TDM_0].channels; + break; + case AFE_PORT_ID_QUATERNARY_TDM_TX: + slots = tdm_tx_cfg[TDM_QUAT][TDM_0].channels; + break; + default: + pr_err("%s: dai id 0x%x not supported\n", + __func__, cpu_dai->id); + return -EINVAL; + } + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + /*2 slot config - bits 0 and 1 set for the first two slots */ + slot_mask = 0x0000FFFF >> (16-slots); + channels = slots; + + pr_debug("%s: tdm rx slot_width %d slots %d\n", + __func__, slot_width, slots); + + ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0, slot_mask, + slots, slot_width); + if (ret < 0) { + pr_err("%s: failed to set tdm rx slot, err:%d\n", + __func__, ret); + goto end; + } + + ret = snd_soc_dai_set_channel_map(cpu_dai, + 0, NULL, channels, slot_offset); + if (ret < 0) { + pr_err("%s: failed to set tdm rx channel map, err:%d\n", + __func__, ret); + goto end; + } + } else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + /*2 slot config - bits 0 and 1 set for the first two slots */ + slot_mask = 0x0000FFFF >> (16-slots); + channels = slots; + + pr_debug("%s: tdm tx slot_width %d slots %d\n", + __func__, slot_width, slots); + + ret = snd_soc_dai_set_tdm_slot(cpu_dai, slot_mask, 0, + slots, slot_width); + if (ret < 0) { + pr_err("%s: failed to set tdm tx slot, err:%d\n", + __func__, ret); + goto end; + } + + ret = snd_soc_dai_set_channel_map(cpu_dai, + channels, slot_offset, 0, NULL); + if (ret < 0) { + pr_err("%s: failed to set tdm tx channel map, err:%d\n", + __func__, ret); + goto end; + } + } else { + ret = -EINVAL; + pr_err("%s: invalid use case, err:%d\n", + __func__, ret); + goto end; + } + + rate = params_rate(params); + clk_freq = rate * slot_width * slots; + ret = snd_soc_dai_set_sysclk(cpu_dai, 0, clk_freq, SND_SOC_CLOCK_OUT); + if (ret < 0) + pr_err("%s: failed to set tdm clk, err:%d\n", + __func__, ret); + +end: + return ret; +} + +static int sdm845_tdm_snd_startup(struct snd_pcm_substream *substream) +{ + int ret = 0; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_card *card = rtd->card; + struct msm_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card); + struct msm_pinctrl_info *pinctrl_info = &pdata->pinctrl_info; + + /* currently only supporting TDM_RX_0 and TDM_TX_0 */ + if ((cpu_dai->id == AFE_PORT_ID_QUATERNARY_TDM_RX) || + (cpu_dai->id == AFE_PORT_ID_QUATERNARY_TDM_TX)) { + ret = msm_set_pinctrl(pinctrl_info, STATE_TDM_ACTIVE); + if (ret) + pr_err("%s: TDM TLMM pinctrl set failed with %d\n", + __func__, ret); + } + + return ret; +} + +static void sdm845_tdm_snd_shutdown(struct snd_pcm_substream *substream) +{ + int ret = 0; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_card *card = rtd->card; + struct msm_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card); + struct msm_pinctrl_info *pinctrl_info = &pdata->pinctrl_info; + + /* currently only supporting TDM_RX_0 and TDM_TX_0 */ + if ((cpu_dai->id == AFE_PORT_ID_QUATERNARY_TDM_RX) || + (cpu_dai->id == AFE_PORT_ID_QUATERNARY_TDM_TX)) { + ret = msm_set_pinctrl(pinctrl_info, STATE_DISABLE); + if (ret) + pr_err("%s: TDM TLMM pinctrl set failed with %d\n", + __func__, ret); + } +} + +static struct snd_soc_ops sdm845_tdm_be_ops = { + .hw_params = sdm845_tdm_snd_hw_params, + .startup = sdm845_tdm_snd_startup, + .shutdown = sdm845_tdm_snd_shutdown +}; + +static int msm_fe_qos_prepare(struct snd_pcm_substream *substream) +{ + cpumask_t mask; + + if (pm_qos_request_active(&substream->latency_pm_qos_req)) + pm_qos_remove_request(&substream->latency_pm_qos_req); + + cpumask_clear(&mask); + cpumask_set_cpu(1, &mask); /* affine to core 1 */ + cpumask_set_cpu(2, &mask); /* affine to core 2 */ + cpumask_copy(&substream->latency_pm_qos_req.cpus_affine, &mask); + + substream->latency_pm_qos_req.type = PM_QOS_REQ_AFFINE_CORES; + + pm_qos_add_request(&substream->latency_pm_qos_req, + PM_QOS_CPU_DMA_LATENCY, + MSM_LL_QOS_VALUE); + return 0; +} + +static struct snd_soc_ops msm_fe_qos_ops = { + .prepare = msm_fe_qos_prepare, +}; + +static int msm_mi2s_snd_startup(struct snd_pcm_substream *substream) +{ + int ret = 0; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + int index = cpu_dai->id; + unsigned int fmt = SND_SOC_DAIFMT_CBS_CFS; + struct snd_soc_card *card = rtd->card; + struct msm_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card); + struct msm_pinctrl_info *pinctrl_info = &pdata->pinctrl_info; + int ret_pinctrl = 0; + + dev_dbg(rtd->card->dev, + "%s: substream = %s stream = %d, dai name %s, dai ID %d\n", + __func__, substream->name, substream->stream, + cpu_dai->name, cpu_dai->id); + + if (index < PRIM_MI2S || index > QUAT_MI2S) { + ret = -EINVAL; + dev_err(rtd->card->dev, + "%s: CPU DAI id (%d) out of range\n", + __func__, cpu_dai->id); + goto err; + } + if (index == QUAT_MI2S) { + ret_pinctrl = msm_set_pinctrl(pinctrl_info, STATE_MI2S_ACTIVE); + if (ret_pinctrl) + pr_err("%s: MI2S TLMM pinctrl set failed with %d\n", + __func__, ret_pinctrl); + } + /* + * Muxtex protection in case the same MI2S + * interface using for both TX and RX so + * that the same clock won't be enable twice. + */ + mutex_lock(&mi2s_intf_conf[index].lock); + if (++mi2s_intf_conf[index].ref_cnt == 1) { + /* Check if msm needs to provide the clock to the interface */ + if (!mi2s_intf_conf[index].msm_is_mi2s_master) { + mi2s_clk[index].clk_id = mi2s_ebit_clk[index]; + fmt = SND_SOC_DAIFMT_CBM_CFM; + } + ret = msm_mi2s_set_sclk(substream, true); + if (ret < 0) { + dev_err(rtd->card->dev, + "%s: afe lpass clock failed to enable MI2S clock, err:%d\n", + __func__, ret); + goto clean_up; + } + if (mi2s_auxpcm_conf[index].pcm_i2s_sel_vt_addr != NULL) { + mutex_lock(&mi2s_auxpcm_conf[index].lock); + iowrite32(0, + mi2s_auxpcm_conf[index].pcm_i2s_sel_vt_addr); + mutex_unlock(&mi2s_auxpcm_conf[index].lock); + } else { + dev_err(rtd->card->dev, + "%s lpaif_muxsel_virt_addr is NULL for dai %d\n", + __func__, index); + ret = -EINVAL; + goto clk_off; + } + ret = snd_soc_dai_set_fmt(cpu_dai, fmt); + if (ret < 0) { + pr_err("%s: set fmt cpu dai failed for MI2S (%d), err:%d\n", + __func__, index, ret); + goto clk_off; + } + } +clk_off: + if (ret < 0) + msm_mi2s_set_sclk(substream, false); +clean_up: + if (ret < 0) + mi2s_intf_conf[index].ref_cnt--; + mutex_unlock(&mi2s_intf_conf[index].lock); +err: + return ret; +} + +static void msm_mi2s_snd_shutdown(struct snd_pcm_substream *substream) +{ + int ret; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + int index = rtd->cpu_dai->id; + struct snd_soc_card *card = rtd->card; + struct msm_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card); + struct msm_pinctrl_info *pinctrl_info = &pdata->pinctrl_info; + int ret_pinctrl = 0; + + pr_debug("%s(): substream = %s stream = %d\n", __func__, + substream->name, substream->stream); + if (index < PRIM_MI2S || index > QUAT_MI2S) { + pr_err("%s:invalid MI2S DAI(%d)\n", __func__, index); + return; + } + + mutex_lock(&mi2s_intf_conf[index].lock); + if (--mi2s_intf_conf[index].ref_cnt == 0) { + ret = msm_mi2s_set_sclk(substream, false); + if (ret < 0) + pr_err("%s:clock disable failed for MI2S (%d); ret=%d\n", + __func__, index, ret); + } + mutex_unlock(&mi2s_intf_conf[index].lock); + + if (index == QUAT_MI2S) { + ret_pinctrl = msm_set_pinctrl(pinctrl_info, STATE_DISABLE); + if (ret_pinctrl) + pr_err("%s: MI2S TLMM pinctrl set failed with %d\n", + __func__, ret_pinctrl); + } +} + +static struct snd_soc_ops msm_mi2s_be_ops = { + .startup = msm_mi2s_snd_startup, + .shutdown = msm_mi2s_snd_shutdown, +}; + +static struct snd_soc_ops msm_aux_pcm_be_ops = { + .startup = msm_aux_pcm_snd_startup, + .shutdown = msm_aux_pcm_snd_shutdown, +}; + +static struct snd_soc_ops msm_be_ops = { + .hw_params = msm_snd_hw_params, +}; + +static struct snd_soc_ops msm_slimbus_2_be_ops = { + .hw_params = msm_slimbus_2_hw_params, +}; + +static struct snd_soc_ops msm_wcn_ops = { + .hw_params = msm_wcn_hw_params, +}; + + +/* Digital audio interface glue - connects codec <---> CPU */ +static struct snd_soc_dai_link msm_common_dai_links[] = { + /* FrontEnd DAI Links */ + { + .name = MSM_DAILINK_NAME(Media1), + .stream_name = "MultiMedia1", + .cpu_dai_name = "MultiMedia1", + .platform_name = "msm-pcm-dsp.0", + .dynamic = 1, + .async_ops = ASYNC_DPCM_SND_SOC_PREPARE, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .id = MSM_FRONTEND_DAI_MULTIMEDIA1 + }, + { + .name = MSM_DAILINK_NAME(Media2), + .stream_name = "MultiMedia2", + .cpu_dai_name = "MultiMedia2", + .platform_name = "msm-pcm-dsp.0", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .id = MSM_FRONTEND_DAI_MULTIMEDIA2, + }, + { + .name = "VoiceMMode1", + .stream_name = "VoiceMMode1", + .cpu_dai_name = "VoiceMMode1", + .platform_name = "msm-pcm-voice", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 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", + .id = MSM_FRONTEND_DAI_VOICEMMODE1, + }, + { + .name = "MSM VoIP", + .stream_name = "VoIP", + .cpu_dai_name = "VoIP", + .platform_name = "msm-voip-dsp", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .id = MSM_FRONTEND_DAI_VOIP, + }, + { + .name = MSM_DAILINK_NAME(ULL), + .stream_name = "MultiMedia3", + .cpu_dai_name = "MultiMedia3", + .platform_name = "msm-pcm-dsp.2", + .dynamic = 1, + .async_ops = ASYNC_DPCM_SND_SOC_PREPARE, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .id = MSM_FRONTEND_DAI_MULTIMEDIA3, + }, + /* Hostless PCM purpose */ + { + .name = "SLIMBUS_0 Hostless", + .stream_name = "SLIMBUS_0 Hostless", + .cpu_dai_name = "SLIMBUS0_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + /* this dailink has playback support */ + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + { + .name = "MSM AFE-PCM RX", + .stream_name = "AFE-PROXY RX", + .cpu_dai_name = "msm-dai-q6-dev.241", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .platform_name = "msm-pcm-afe", + .dpcm_playback = 1, + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + }, + { + .name = "MSM AFE-PCM TX", + .stream_name = "AFE-PROXY TX", + .cpu_dai_name = "msm-dai-q6-dev.240", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .platform_name = "msm-pcm-afe", + .dpcm_capture = 1, + .ignore_suspend = 1, + }, + { + .name = MSM_DAILINK_NAME(Compress1), + .stream_name = "Compress1", + .cpu_dai_name = "MultiMedia4", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .async_ops = ASYNC_DPCM_SND_SOC_HW_PARAMS, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dainlink has playback support */ + .id = MSM_FRONTEND_DAI_MULTIMEDIA4, + }, + { + .name = "AUXPCM Hostless", + .stream_name = "AUXPCM Hostless", + .cpu_dai_name = "AUXPCM_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + { + .name = "SLIMBUS_1 Hostless", + .stream_name = "SLIMBUS_1 Hostless", + .cpu_dai_name = "SLIMBUS1_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + /* this dailink has playback support */ + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + { + .name = "SLIMBUS_3 Hostless", + .stream_name = "SLIMBUS_3 Hostless", + .cpu_dai_name = "SLIMBUS3_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + /* this dailink has playback support */ + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + { + .name = "SLIMBUS_4 Hostless", + .stream_name = "SLIMBUS_4 Hostless", + .cpu_dai_name = "SLIMBUS4_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + /* this dailink has playback support */ + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + { + .name = MSM_DAILINK_NAME(LowLatency), + .stream_name = "MultiMedia5", + .cpu_dai_name = "MultiMedia5", + .platform_name = "msm-pcm-dsp.1", + .dynamic = 1, + .async_ops = ASYNC_DPCM_SND_SOC_PREPARE, + .dpcm_playback = 1, + .dpcm_capture = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .id = MSM_FRONTEND_DAI_MULTIMEDIA5, + .ops = &msm_fe_qos_ops, + }, + { + .name = "Listen 1 Audio Service", + .stream_name = "Listen 1 Audio Service", + .cpu_dai_name = "LSM1", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 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", + .id = MSM_FRONTEND_DAI_LSM1, + }, + /* Multiple Tunnel instances */ + { + .name = MSM_DAILINK_NAME(Compress2), + .stream_name = "Compress2", + .cpu_dai_name = "MultiMedia7", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dainlink has playback support */ + .id = MSM_FRONTEND_DAI_MULTIMEDIA7, + }, + { + .name = MSM_DAILINK_NAME(MultiMedia10), + .stream_name = "MultiMedia10", + .cpu_dai_name = "MultiMedia10", + .platform_name = "msm-pcm-dsp.1", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dainlink has playback support */ + .id = MSM_FRONTEND_DAI_MULTIMEDIA10, + }, + { + .name = MSM_DAILINK_NAME(ULL_NOIRQ), + .stream_name = "MM_NOIRQ", + .cpu_dai_name = "MultiMedia8", + .platform_name = "msm-pcm-dsp-noirq", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dainlink has playback support */ + .id = MSM_FRONTEND_DAI_MULTIMEDIA8, + .ops = &msm_fe_qos_ops, + }, + /* HDMI Hostless */ + { + .name = "HDMI_RX_HOSTLESS", + .stream_name = "HDMI_RX_HOSTLESS", + .cpu_dai_name = "HDMI_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .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", + }, + { + .name = "VoiceMMode2", + .stream_name = "VoiceMMode2", + .cpu_dai_name = "VoiceMMode2", + .platform_name = "msm-pcm-voice", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 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", + .id = MSM_FRONTEND_DAI_VOICEMMODE2, + }, + /* LSM FE */ + { + .name = "Listen 2 Audio Service", + .stream_name = "Listen 2 Audio Service", + .cpu_dai_name = "LSM2", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 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", + .id = MSM_FRONTEND_DAI_LSM2, + }, + { + .name = "Listen 3 Audio Service", + .stream_name = "Listen 3 Audio Service", + .cpu_dai_name = "LSM3", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 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", + .id = MSM_FRONTEND_DAI_LSM3, + }, + { + .name = "Listen 4 Audio Service", + .stream_name = "Listen 4 Audio Service", + .cpu_dai_name = "LSM4", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 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", + .id = MSM_FRONTEND_DAI_LSM4, + }, + { + .name = "Listen 5 Audio Service", + .stream_name = "Listen 5 Audio Service", + .cpu_dai_name = "LSM5", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 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", + .id = MSM_FRONTEND_DAI_LSM5, + }, + { + .name = "Listen 6 Audio Service", + .stream_name = "Listen 6 Audio Service", + .cpu_dai_name = "LSM6", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 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", + .id = MSM_FRONTEND_DAI_LSM6, + }, + { + .name = "Listen 7 Audio Service", + .stream_name = "Listen 7 Audio Service", + .cpu_dai_name = "LSM7", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 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", + .id = MSM_FRONTEND_DAI_LSM7, + }, + { + .name = "Listen 8 Audio Service", + .stream_name = "Listen 8 Audio Service", + .cpu_dai_name = "LSM8", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 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", + .id = MSM_FRONTEND_DAI_LSM8, + }, + { + .name = MSM_DAILINK_NAME(Media9), + .stream_name = "MultiMedia9", + .cpu_dai_name = "MultiMedia9", + .platform_name = "msm-pcm-dsp.0", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .id = MSM_FRONTEND_DAI_MULTIMEDIA9, + }, + { + .name = MSM_DAILINK_NAME(Compress4), + .stream_name = "Compress4", + .cpu_dai_name = "MultiMedia11", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dainlink has playback support */ + .id = MSM_FRONTEND_DAI_MULTIMEDIA11, + }, + { + .name = MSM_DAILINK_NAME(Compress5), + .stream_name = "Compress5", + .cpu_dai_name = "MultiMedia12", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dainlink has playback support */ + .id = MSM_FRONTEND_DAI_MULTIMEDIA12, + }, + { + .name = MSM_DAILINK_NAME(Compress6), + .stream_name = "Compress6", + .cpu_dai_name = "MultiMedia13", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dainlink has playback support */ + .id = MSM_FRONTEND_DAI_MULTIMEDIA13, + }, + { + .name = MSM_DAILINK_NAME(Compress7), + .stream_name = "Compress7", + .cpu_dai_name = "MultiMedia14", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dainlink has playback support */ + .id = MSM_FRONTEND_DAI_MULTIMEDIA14, + }, + { + .name = MSM_DAILINK_NAME(Compress8), + .stream_name = "Compress8", + .cpu_dai_name = "MultiMedia15", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dainlink has playback support */ + .id = MSM_FRONTEND_DAI_MULTIMEDIA15, + }, + { + .name = MSM_DAILINK_NAME(ULL_NOIRQ_2), + .stream_name = "MM_NOIRQ_2", + .cpu_dai_name = "MultiMedia16", + .platform_name = "msm-pcm-dsp-noirq", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dainlink has playback support */ + .id = MSM_FRONTEND_DAI_MULTIMEDIA16, + }, + { + .name = "SLIMBUS_8 Hostless", + .stream_name = "SLIMBUS8_HOSTLESS Capture", + .cpu_dai_name = "SLIMBUS8_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_capture = 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", + }, +}; + +static struct snd_soc_dai_link msm_tavil_fe_dai_links[] = { + { + .name = LPASS_BE_SLIMBUS_4_TX, + .stream_name = "Slimbus4 Capture", + .cpu_dai_name = "msm-dai-q6-dev.16393", + .platform_name = "msm-pcm-hostless", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_vifeedback", + .id = MSM_BACKEND_DAI_SLIMBUS_4_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_be_ops, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + }, + /* Ultrasound RX DAI Link */ + { + .name = "SLIMBUS_2 Hostless Playback", + .stream_name = "SLIMBUS_2 Hostless Playback", + .cpu_dai_name = "msm-dai-q6-dev.16388", + .platform_name = "msm-pcm-hostless", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_rx2", + .ignore_suspend = 1, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ops = &msm_slimbus_2_be_ops, + }, + /* Ultrasound TX DAI Link */ + { + .name = "SLIMBUS_2 Hostless Capture", + .stream_name = "SLIMBUS_2 Hostless Capture", + .cpu_dai_name = "msm-dai-q6-dev.16389", + .platform_name = "msm-pcm-hostless", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_tx2", + .ignore_suspend = 1, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ops = &msm_slimbus_2_be_ops, + }, +}; + +static struct snd_soc_dai_link msm_common_misc_fe_dai_links[] = { + { + .name = MSM_DAILINK_NAME(ASM Loopback), + .stream_name = "MultiMedia6", + .cpu_dai_name = "MultiMedia6", + .platform_name = "msm-pcm-loopback", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .ignore_suspend = 1, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_pmdown_time = 1, + .id = MSM_FRONTEND_DAI_MULTIMEDIA6, + }, + { + .name = "USB Audio Hostless", + .stream_name = "USB Audio Hostless", + .cpu_dai_name = "USBAUDIO_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 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", + }, +}; + +static struct snd_soc_dai_link msm_common_be_dai_links[] = { + /* Backend AFE DAI Links */ + { + .name = LPASS_BE_AFE_PCM_RX, + .stream_name = "AFE Playback", + .cpu_dai_name = "msm-dai-q6-dev.224", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_AFE_PCM_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_AFE_PCM_TX, + .stream_name = "AFE Capture", + .cpu_dai_name = "msm-dai-q6-dev.225", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_AFE_PCM_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + }, + /* Incall Record Uplink BACK END DAI Link */ + { + .name = LPASS_BE_INCALL_RECORD_TX, + .stream_name = "Voice Uplink Capture", + .cpu_dai_name = "msm-dai-q6-dev.32772", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_INCALL_RECORD_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + }, + /* Incall Record Downlink BACK END DAI Link */ + { + .name = LPASS_BE_INCALL_RECORD_RX, + .stream_name = "Voice Downlink Capture", + .cpu_dai_name = "msm-dai-q6-dev.32771", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_INCALL_RECORD_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + }, + /* Incall Music BACK END DAI Link */ + { + .name = LPASS_BE_VOICE_PLAYBACK_TX, + .stream_name = "Voice Farend Playback", + .cpu_dai_name = "msm-dai-q6-dev.32773", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_VOICE_PLAYBACK_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + }, + /* Incall Music 2 BACK END DAI Link */ + { + .name = LPASS_BE_VOICE2_PLAYBACK_TX, + .stream_name = "Voice2 Farend Playback", + .cpu_dai_name = "msm-dai-q6-dev.32770", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_VOICE2_PLAYBACK_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_USB_AUDIO_RX, + .stream_name = "USB Audio Playback", + .cpu_dai_name = "msm-dai-q6-dev.28672", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_USB_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_USB_AUDIO_TX, + .stream_name = "USB Audio Capture", + .cpu_dai_name = "msm-dai-q6-dev.28673", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_USB_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_PRI_TDM_RX_0, + .stream_name = "Primary TDM0 Playback", + .cpu_dai_name = "msm-dai-q6-tdm.36864", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_PRI_TDM_RX_0, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &sdm845_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_PRI_TDM_TX_0, + .stream_name = "Primary TDM0 Capture", + .cpu_dai_name = "msm-dai-q6-tdm.36865", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_PRI_TDM_TX_0, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &sdm845_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SEC_TDM_RX_0, + .stream_name = "Secondary TDM0 Playback", + .cpu_dai_name = "msm-dai-q6-tdm.36880", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SEC_TDM_RX_0, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &sdm845_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SEC_TDM_TX_0, + .stream_name = "Secondary TDM0 Capture", + .cpu_dai_name = "msm-dai-q6-tdm.36881", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_SEC_TDM_TX_0, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &sdm845_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_TERT_TDM_RX_0, + .stream_name = "Tertiary TDM0 Playback", + .cpu_dai_name = "msm-dai-q6-tdm.36896", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_TERT_TDM_RX_0, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &sdm845_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_TERT_TDM_TX_0, + .stream_name = "Tertiary TDM0 Capture", + .cpu_dai_name = "msm-dai-q6-tdm.36897", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_TERT_TDM_TX_0, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &sdm845_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_QUAT_TDM_RX_0, + .stream_name = "Quaternary TDM0 Playback", + .cpu_dai_name = "msm-dai-q6-tdm.36912", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_QUAT_TDM_RX_0, + .be_hw_params_fixup = msm_tdm_be_hw_params_fixup, + .ops = &sdm845_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_QUAT_TDM_TX_0, + .stream_name = "Quaternary TDM0 Capture", + .cpu_dai_name = "msm-dai-q6-tdm.36913", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_QUAT_TDM_TX_0, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &sdm845_tdm_be_ops, + .ignore_suspend = 1, + }, +}; + +static struct snd_soc_dai_link msm_tavil_be_dai_links[] = { + { + .name = LPASS_BE_SLIMBUS_0_RX, + .stream_name = "Slimbus Playback", + .cpu_dai_name = "msm-dai-q6-dev.16384", + .platform_name = "msm-pcm-routing", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_rx1", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_0_RX, + .init = &msm_audrx_init, + .be_hw_params_fixup = msm_be_hw_params_fixup, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + .ops = &msm_be_ops, + }, + { + .name = LPASS_BE_SLIMBUS_0_TX, + .stream_name = "Slimbus Capture", + .cpu_dai_name = "msm-dai-q6-dev.16385", + .platform_name = "msm-pcm-routing", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_tx1", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_0_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + .ops = &msm_be_ops, + }, + { + .name = LPASS_BE_SLIMBUS_1_RX, + .stream_name = "Slimbus1 Playback", + .cpu_dai_name = "msm-dai-q6-dev.16386", + .platform_name = "msm-pcm-routing", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_rx1", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_1_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_be_ops, + /* dai link has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_1_TX, + .stream_name = "Slimbus1 Capture", + .cpu_dai_name = "msm-dai-q6-dev.16387", + .platform_name = "msm-pcm-routing", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_tx3", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_1_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_2_RX, + .stream_name = "Slimbus2 Playback", + .cpu_dai_name = "msm-dai-q6-dev.16388", + .platform_name = "msm-pcm-routing", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_rx2", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_2_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_be_ops, + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_3_RX, + .stream_name = "Slimbus3 Playback", + .cpu_dai_name = "msm-dai-q6-dev.16390", + .platform_name = "msm-pcm-routing", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_rx1", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_3_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_be_ops, + /* dai link has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_3_TX, + .stream_name = "Slimbus3 Capture", + .cpu_dai_name = "msm-dai-q6-dev.16391", + .platform_name = "msm-pcm-routing", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_tx1", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_3_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_4_RX, + .stream_name = "Slimbus4 Playback", + .cpu_dai_name = "msm-dai-q6-dev.16392", + .platform_name = "msm-pcm-routing", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_rx1", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_4_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_be_ops, + /* dai link has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_5_RX, + .stream_name = "Slimbus5 Playback", + .cpu_dai_name = "msm-dai-q6-dev.16394", + .platform_name = "msm-pcm-routing", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_rx3", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_5_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_be_ops, + /* dai link has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + /* MAD BE */ + { + .name = LPASS_BE_SLIMBUS_5_TX, + .stream_name = "Slimbus5 Capture", + .cpu_dai_name = "msm-dai-q6-dev.16395", + .platform_name = "msm-pcm-routing", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_mad1", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_5_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_6_RX, + .stream_name = "Slimbus6 Playback", + .cpu_dai_name = "msm-dai-q6-dev.16396", + .platform_name = "msm-pcm-routing", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_rx4", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_6_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_be_ops, + /* dai link has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + /* Slimbus VI Recording */ + { + .name = LPASS_BE_SLIMBUS_TX_VI, + .stream_name = "Slimbus4 Capture", + .cpu_dai_name = "msm-dai-q6-dev.16393", + .platform_name = "msm-pcm-routing", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_vifeedback", + .id = MSM_BACKEND_DAI_SLIMBUS_4_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_be_ops, + .ignore_suspend = 1, + .no_pcm = 1, + .dpcm_capture = 1, + .ignore_pmdown_time = 1, + }, +}; + +static struct snd_soc_dai_link msm_wcn_be_dai_links[] = { + { + .name = LPASS_BE_SLIMBUS_7_RX, + .stream_name = "Slimbus7 Playback", + .cpu_dai_name = "msm-dai-q6-dev.16398", + .platform_name = "msm-pcm-routing", + .codec_name = "btfmslim_slave", + /* BT codec driver determines capabilities based on + * dai name, bt codecdai name should always contains + * supported usecase information + */ + .codec_dai_name = "btfm_bt_sco_a2dp_slim_rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_7_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_wcn_ops, + /* dai link has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_7_TX, + .stream_name = "Slimbus7 Capture", + .cpu_dai_name = "msm-dai-q6-dev.16399", + .platform_name = "msm-pcm-routing", + .codec_name = "btfmslim_slave", + .codec_dai_name = "btfm_bt_sco_slim_tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_7_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_wcn_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_8_TX, + .stream_name = "Slimbus8 Capture", + .cpu_dai_name = "msm-dai-q6-dev.16401", + .platform_name = "msm-pcm-routing", + .codec_name = "btfmslim_slave", + .codec_dai_name = "btfm_fm_slim_tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_8_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .init = &msm_wcn_init, + .ops = &msm_wcn_ops, + .ignore_suspend = 1, + }, +}; + +static struct snd_soc_dai_link ext_disp_be_dai_link[] = { + /* DISP PORT BACK END DAI Link */ + { + .name = LPASS_BE_DISPLAY_PORT, + .stream_name = "Display Port Playback", + .cpu_dai_name = "msm-dai-q6-dp.24608", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-ext-disp-audio-codec-rx", + .codec_dai_name = "msm_dp_audio_codec_rx_dai", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_DISPLAY_PORT_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, +}; + +static struct snd_soc_dai_link msm_mi2s_be_dai_links[] = { + { + .name = LPASS_BE_PRI_MI2S_RX, + .stream_name = "Primary MI2S Playback", + .cpu_dai_name = "msm-dai-q6-mi2s.0", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_PRI_MI2S_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + }, + { + .name = LPASS_BE_PRI_MI2S_TX, + .stream_name = "Primary MI2S Capture", + .cpu_dai_name = "msm-dai-q6-mi2s.0", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_PRI_MI2S_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SEC_MI2S_RX, + .stream_name = "Secondary MI2S Playback", + .cpu_dai_name = "msm-dai-q6-mi2s.1", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SECONDARY_MI2S_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + }, + { + .name = LPASS_BE_SEC_MI2S_TX, + .stream_name = "Secondary MI2S Capture", + .cpu_dai_name = "msm-dai-q6-mi2s.1", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_SECONDARY_MI2S_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_TERT_MI2S_RX, + .stream_name = "Tertiary MI2S Playback", + .cpu_dai_name = "msm-dai-q6-mi2s.2", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_TERTIARY_MI2S_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + }, + { + .name = LPASS_BE_TERT_MI2S_TX, + .stream_name = "Tertiary MI2S Capture", + .cpu_dai_name = "msm-dai-q6-mi2s.2", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_TERTIARY_MI2S_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_QUAT_MI2S_RX, + .stream_name = "Quaternary MI2S Playback", + .cpu_dai_name = "msm-dai-q6-mi2s.3", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_QUATERNARY_MI2S_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + }, + { + .name = LPASS_BE_QUAT_MI2S_TX, + .stream_name = "Quaternary MI2S Capture", + .cpu_dai_name = "msm-dai-q6-mi2s.3", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + }, +}; + +static struct snd_soc_dai_link msm_auxpcm_be_dai_links[] = { + /* Primary AUX PCM Backend DAI Links */ + { + .name = LPASS_BE_AUXPCM_RX, + .stream_name = "AUX PCM Playback", + .cpu_dai_name = "msm-dai-q6-auxpcm.1", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_AUXPCM_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + .ops = &msm_aux_pcm_be_ops, + }, + { + .name = LPASS_BE_AUXPCM_TX, + .stream_name = "AUX PCM Capture", + .cpu_dai_name = "msm-dai-q6-auxpcm.1", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_AUXPCM_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + .ops = &msm_aux_pcm_be_ops, + }, + /* Secondary AUX PCM Backend DAI Links */ + { + .name = LPASS_BE_SEC_AUXPCM_RX, + .stream_name = "Sec AUX PCM Playback", + .cpu_dai_name = "msm-dai-q6-auxpcm.2", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SEC_AUXPCM_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + .ops = &msm_aux_pcm_be_ops, + }, + { + .name = LPASS_BE_SEC_AUXPCM_TX, + .stream_name = "Sec AUX PCM Capture", + .cpu_dai_name = "msm-dai-q6-auxpcm.2", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_SEC_AUXPCM_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .ops = &msm_aux_pcm_be_ops, + }, + /* Tertiary AUX PCM Backend DAI Links */ + { + .name = LPASS_BE_TERT_AUXPCM_RX, + .stream_name = "Tert AUX PCM Playback", + .cpu_dai_name = "msm-dai-q6-auxpcm.3", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_TERT_AUXPCM_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + .ops = &msm_aux_pcm_be_ops, + }, + { + .name = LPASS_BE_TERT_AUXPCM_TX, + .stream_name = "Tert AUX PCM Capture", + .cpu_dai_name = "msm-dai-q6-auxpcm.3", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_TERT_AUXPCM_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .ops = &msm_aux_pcm_be_ops, + }, + /* Quaternary AUX PCM Backend DAI Links */ + { + .name = LPASS_BE_QUAT_AUXPCM_RX, + .stream_name = "Quat AUX PCM Playback", + .cpu_dai_name = "msm-dai-q6-auxpcm.4", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_QUAT_AUXPCM_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + .ops = &msm_aux_pcm_be_ops, + }, + { + .name = LPASS_BE_QUAT_AUXPCM_TX, + .stream_name = "Quat AUX PCM Capture", + .cpu_dai_name = "msm-dai-q6-auxpcm.4", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_QUAT_AUXPCM_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .ops = &msm_aux_pcm_be_ops, + }, +}; + +static struct snd_soc_dai_link msm_tavil_snd_card_dai_links[ + ARRAY_SIZE(msm_common_dai_links) + + ARRAY_SIZE(msm_tavil_fe_dai_links) + + ARRAY_SIZE(msm_common_misc_fe_dai_links) + + ARRAY_SIZE(msm_common_be_dai_links) + + ARRAY_SIZE(msm_tavil_be_dai_links) + + ARRAY_SIZE(msm_wcn_be_dai_links) + + ARRAY_SIZE(ext_disp_be_dai_link) + + ARRAY_SIZE(msm_mi2s_be_dai_links) + + ARRAY_SIZE(msm_auxpcm_be_dai_links)]; + +static int msm_snd_card_tavil_late_probe(struct snd_soc_card *card) +{ + const char *be_dl_name = LPASS_BE_SLIMBUS_0_RX; + struct snd_soc_pcm_runtime *rtd; + int ret = 0; + void *mbhc_calibration; + + rtd = snd_soc_get_pcm_runtime(card, be_dl_name); + if (!rtd) { + dev_err(card->dev, + "%s: snd_soc_get_pcm_runtime for %s failed!\n", + __func__, be_dl_name); + ret = -EINVAL; + goto err; + } + + mbhc_calibration = def_tavil_mbhc_cal(); + if (!mbhc_calibration) { + ret = -ENOMEM; + goto err; + } + wcd_mbhc_cfg.calibration = mbhc_calibration; + ret = tavil_mbhc_hs_detect(rtd->codec, &wcd_mbhc_cfg); + if (ret) { + dev_err(card->dev, "%s: mbhc hs detect failed, err:%d\n", + __func__, ret); + goto err_free_mbhc_cal; + } + return 0; + +err_free_mbhc_cal: + kfree(mbhc_calibration); +err: + return ret; +} + +struct snd_soc_card snd_soc_card_tavil_msm = { + .name = "sdm845-tavil-snd-card", + .late_probe = msm_snd_card_tavil_late_probe, +}; + +static int msm_populate_dai_link_component_of_node( + struct snd_soc_card *card) +{ + int i, index, ret = 0; + struct device *cdev = card->dev; + struct snd_soc_dai_link *dai_link = card->dai_link; + struct device_node *np; + + if (!cdev) { + pr_err("%s: Sound card device memory NULL\n", __func__); + return -ENODEV; + } + + for (i = 0; i < card->num_links; i++) { + if (dai_link[i].platform_of_node && dai_link[i].cpu_of_node) + continue; + + /* populate platform_of_node for snd card dai links */ + if (dai_link[i].platform_name && + !dai_link[i].platform_of_node) { + index = of_property_match_string(cdev->of_node, + "asoc-platform-names", + dai_link[i].platform_name); + if (index < 0) { + pr_err("%s: No match found for platform name: %s\n", + __func__, dai_link[i].platform_name); + ret = index; + goto err; + } + np = of_parse_phandle(cdev->of_node, "asoc-platform", + index); + if (!np) { + pr_err("%s: retrieving phandle for platform %s, index %d failed\n", + __func__, dai_link[i].platform_name, + index); + ret = -ENODEV; + goto err; + } + dai_link[i].platform_of_node = np; + dai_link[i].platform_name = NULL; + } + + /* populate cpu_of_node for snd card dai links */ + if (dai_link[i].cpu_dai_name && !dai_link[i].cpu_of_node) { + index = of_property_match_string(cdev->of_node, + "asoc-cpu-names", + dai_link[i].cpu_dai_name); + if (index >= 0) { + np = of_parse_phandle(cdev->of_node, "asoc-cpu", + index); + if (!np) { + pr_err("%s: retrieving phandle for cpu dai %s failed\n", + __func__, + dai_link[i].cpu_dai_name); + ret = -ENODEV; + goto err; + } + dai_link[i].cpu_of_node = np; + dai_link[i].cpu_dai_name = NULL; + } + } + + /* populate codec_of_node for snd card dai links */ + if (dai_link[i].codec_name && !dai_link[i].codec_of_node) { + index = of_property_match_string(cdev->of_node, + "asoc-codec-names", + dai_link[i].codec_name); + if (index < 0) + continue; + np = of_parse_phandle(cdev->of_node, "asoc-codec", + index); + if (!np) { + pr_err("%s: retrieving phandle for codec %s failed\n", + __func__, dai_link[i].codec_name); + ret = -ENODEV; + goto err; + } + dai_link[i].codec_of_node = np; + dai_link[i].codec_name = NULL; + } + } + +err: + return ret; +} + +static int msm_prepare_us_euro(struct snd_soc_card *card) +{ + struct msm_asoc_mach_data *pdata = + snd_soc_card_get_drvdata(card); + int ret = 0; + + if (pdata->us_euro_gpio >= 0) { + dev_dbg(card->dev, "%s: us_euro gpio request %d", __func__, + pdata->us_euro_gpio); + ret = gpio_request(pdata->us_euro_gpio, "TAVIL_CODEC_US_EURO"); + if (ret) { + dev_err(card->dev, + "%s: Failed to request codec US/EURO gpio %d error %d\n", + __func__, pdata->us_euro_gpio, ret); + } + } + + return ret; +} + +static int msm_audrx_stub_init(struct snd_soc_pcm_runtime *rtd) +{ + int ret = 0; + struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + + ret = snd_soc_add_codec_controls(codec, msm_snd_controls, + ARRAY_SIZE(msm_snd_controls)); + if (ret < 0) { + dev_err(codec->dev, + "%s: add_codec_controls failed, err = %d\n", + __func__, ret); + return ret; + } + + snd_soc_dapm_new_controls(dapm, msm_dapm_widgets, + ARRAY_SIZE(msm_dapm_widgets)); + + return 0; +} + +static int msm_snd_stub_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + + int ret = 0; + unsigned int rx_ch[] = {144, 145, 146, 147, 148, 149, 150, + 151}; + unsigned int tx_ch[] = {128, 129, 130, 131, 132, 133, + 134, 135, 136, 137, 138, 139, + 140, 141, 142, 143}; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + ret = snd_soc_dai_set_channel_map(cpu_dai, 0, 0, + slim_rx_cfg[0].channels, + rx_ch); + if (ret < 0) + pr_err("%s: RX failed to set cpu chan map error %d\n", + __func__, ret); + } else { + ret = snd_soc_dai_set_channel_map(cpu_dai, + slim_tx_cfg[0].channels, + tx_ch, 0, 0); + if (ret < 0) + pr_err("%s: TX failed to set cpu chan map error %d\n", + __func__, ret); + } + + return ret; +} + +static struct snd_soc_ops msm_stub_be_ops = { + .hw_params = msm_snd_stub_hw_params, +}; + +static struct snd_soc_dai_link msm_stub_fe_dai_links[] = { + + /* FrontEnd DAI Links */ + { + .name = "MSMSTUB Media1", + .stream_name = "MultiMedia1", + .cpu_dai_name = "MultiMedia1", + .platform_name = "msm-pcm-dsp.0", + .dynamic = 1, + .async_ops = ASYNC_DPCM_SND_SOC_PREPARE, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .id = MSM_FRONTEND_DAI_MULTIMEDIA1 + }, +}; + +static struct snd_soc_dai_link msm_stub_be_dai_links[] = { + + /* Backend DAI Links */ + { + .name = LPASS_BE_SLIMBUS_0_RX, + .stream_name = "Slimbus Playback", + .cpu_dai_name = "msm-dai-q6-dev.16384", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_0_RX, + .init = &msm_audrx_stub_init, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_pmdown_time = 1, /* dai link has playback support */ + .ignore_suspend = 1, + .ops = &msm_stub_be_ops, + }, + { + .name = LPASS_BE_SLIMBUS_0_TX, + .stream_name = "Slimbus Capture", + .cpu_dai_name = "msm-dai-q6-dev.16385", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_0_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + .ops = &msm_stub_be_ops, + }, +}; + +static struct snd_soc_dai_link msm_stub_dai_links[ + ARRAY_SIZE(msm_stub_fe_dai_links) + + ARRAY_SIZE(msm_stub_be_dai_links)]; + +struct snd_soc_card snd_soc_card_stub_msm = { + .name = "sdm845-stub-snd-card", +}; + +static const struct of_device_id sdm845_asoc_machine_of_match[] = { + { .compatible = "qcom,sdm845-asoc-snd-tavil", + .data = "tavil_codec"}, + { .compatible = "qcom,sdm845-asoc-snd-stub", + .data = "stub_codec"}, + {}, +}; + +static struct snd_soc_card *populate_snd_card_dailinks(struct device *dev) +{ + struct snd_soc_card *card = NULL; + struct snd_soc_dai_link *dailink; + int len_1, len_2, len_3, len_4; + int total_links; + const struct of_device_id *match; + + match = of_match_node(sdm845_asoc_machine_of_match, dev->of_node); + if (!match) { + dev_err(dev, "%s: No DT match found for sound card\n", + __func__); + return NULL; + } + + if (!strcmp(match->data, "tavil_codec")) { + card = &snd_soc_card_tavil_msm; + len_1 = ARRAY_SIZE(msm_common_dai_links); + len_2 = len_1 + ARRAY_SIZE(msm_tavil_fe_dai_links); + len_3 = len_2 + ARRAY_SIZE(msm_common_misc_fe_dai_links); + len_4 = len_3 + ARRAY_SIZE(msm_common_be_dai_links); + total_links = len_4 + ARRAY_SIZE(msm_tavil_be_dai_links); + memcpy(msm_tavil_snd_card_dai_links, + msm_common_dai_links, + sizeof(msm_common_dai_links)); + memcpy(msm_tavil_snd_card_dai_links + len_1, + msm_tavil_fe_dai_links, + sizeof(msm_tavil_fe_dai_links)); + memcpy(msm_tavil_snd_card_dai_links + len_2, + msm_common_misc_fe_dai_links, + sizeof(msm_common_misc_fe_dai_links)); + memcpy(msm_tavil_snd_card_dai_links + len_3, + msm_common_be_dai_links, + sizeof(msm_common_be_dai_links)); + memcpy(msm_tavil_snd_card_dai_links + len_4, + msm_tavil_be_dai_links, + sizeof(msm_tavil_be_dai_links)); + + if (of_property_read_bool(dev->of_node, "qcom,wcn-btfm")) { + dev_dbg(dev, "%s(): WCN BTFM support present\n", + __func__); + memcpy(msm_tavil_snd_card_dai_links + total_links, + msm_wcn_be_dai_links, + sizeof(msm_wcn_be_dai_links)); + total_links += ARRAY_SIZE(msm_wcn_be_dai_links); + } + + if (of_property_read_bool(dev->of_node, + "qcom,ext-disp-audio-rx")) { + dev_dbg(dev, "%s(): ext disp audio support present\n", + __func__); + memcpy(msm_tavil_snd_card_dai_links + total_links, + ext_disp_be_dai_link, + sizeof(ext_disp_be_dai_link)); + total_links += ARRAY_SIZE(ext_disp_be_dai_link); + } + if (of_property_read_bool(dev->of_node, + "qcom,mi2s-audio-intf")) { + memcpy(msm_tavil_snd_card_dai_links + total_links, + msm_mi2s_be_dai_links, + sizeof(msm_mi2s_be_dai_links)); + total_links += ARRAY_SIZE(msm_mi2s_be_dai_links); + } + if (of_property_read_bool(dev->of_node, + "qcom,auxpcm-audio-intf")) { + memcpy(msm_tavil_snd_card_dai_links + total_links, + msm_auxpcm_be_dai_links, + sizeof(msm_auxpcm_be_dai_links)); + total_links += ARRAY_SIZE(msm_auxpcm_be_dai_links); + } + dailink = msm_tavil_snd_card_dai_links; + } else if (!strcmp(match->data, "stub_codec")) { + card = &snd_soc_card_stub_msm; + len_1 = ARRAY_SIZE(msm_stub_fe_dai_links); + len_2 = len_1 + ARRAY_SIZE(msm_stub_be_dai_links); + + memcpy(msm_stub_dai_links, + msm_stub_fe_dai_links, + sizeof(msm_stub_fe_dai_links)); + memcpy(msm_stub_dai_links + len_1, + msm_stub_be_dai_links, + sizeof(msm_stub_be_dai_links)); + + dailink = msm_stub_dai_links; + total_links = len_2; + } + + if (card) { + card->dai_link = dailink; + card->num_links = total_links; + } + + return card; +} + +static int msm_wsa881x_init(struct snd_soc_component *component) +{ + u8 spkleft_ports[WSA881X_MAX_SWR_PORTS] = {100, 101, 102, 106}; + u8 spkright_ports[WSA881X_MAX_SWR_PORTS] = {103, 104, 105, 107}; + unsigned int ch_rate[WSA881X_MAX_SWR_PORTS] = {2400, 600, 300, 1200}; + unsigned int ch_mask[WSA881X_MAX_SWR_PORTS] = {0x1, 0xF, 0x3, 0x3}; + struct snd_soc_codec *codec = snd_soc_component_to_codec(component); + struct msm_asoc_mach_data *pdata; + struct snd_soc_dapm_context *dapm; + int ret = 0; + + if (!codec) { + pr_err("%s codec is NULL\n", __func__); + return -EINVAL; + } + + dapm = snd_soc_codec_get_dapm(codec); + + if (!strcmp(component->name_prefix, "SpkrLeft")) { + dev_dbg(codec->dev, "%s: setting left ch map to codec %s\n", + __func__, codec->component.name); + wsa881x_set_channel_map(codec, &spkleft_ports[0], + WSA881X_MAX_SWR_PORTS, &ch_mask[0], + &ch_rate[0], NULL); + if (dapm->component) { + snd_soc_dapm_ignore_suspend(dapm, "SpkrLeft IN"); + snd_soc_dapm_ignore_suspend(dapm, "SpkrLeft SPKR"); + } + } else if (!strcmp(component->name_prefix, "SpkrRight")) { + dev_dbg(codec->dev, "%s: setting right ch map to codec %s\n", + __func__, codec->component.name); + wsa881x_set_channel_map(codec, &spkright_ports[0], + WSA881X_MAX_SWR_PORTS, &ch_mask[0], + &ch_rate[0], NULL); + if (dapm->component) { + snd_soc_dapm_ignore_suspend(dapm, "SpkrRight IN"); + snd_soc_dapm_ignore_suspend(dapm, "SpkrRight SPKR"); + } + } else { + dev_err(codec->dev, "%s: wrong codec name %s\n", __func__, + codec->component.name); + ret = -EINVAL; + goto err; + } + pdata = snd_soc_card_get_drvdata(component->card); + if (pdata && pdata->codec_root) + wsa881x_codec_info_create_codec_entry(pdata->codec_root, + codec); + +err: + return ret; +} + +static int msm_init_wsa_dev(struct platform_device *pdev, + struct snd_soc_card *card) +{ + struct device_node *wsa_of_node; + u32 wsa_max_devs; + u32 wsa_dev_cnt; + int i; + struct msm_wsa881x_dev_info *wsa881x_dev_info; + const char *wsa_auxdev_name_prefix[1]; + char *dev_name_str = NULL; + int found = 0; + int ret = 0; + + /* Get maximum WSA device count for this platform */ + ret = of_property_read_u32(pdev->dev.of_node, + "qcom,wsa-max-devs", &wsa_max_devs); + if (ret) { + dev_info(&pdev->dev, + "%s: wsa-max-devs property missing in DT %s, ret = %d\n", + __func__, pdev->dev.of_node->full_name, ret); + card->num_aux_devs = 0; + return 0; + } + if (wsa_max_devs == 0) { + dev_warn(&pdev->dev, + "%s: Max WSA devices is 0 for this target?\n", + __func__); + card->num_aux_devs = 0; + return 0; + } + + /* Get count of WSA device phandles for this platform */ + wsa_dev_cnt = of_count_phandle_with_args(pdev->dev.of_node, + "qcom,wsa-devs", NULL); + if (wsa_dev_cnt == -ENOENT) { + dev_warn(&pdev->dev, "%s: No wsa device defined in DT.\n", + __func__); + goto err; + } else if (wsa_dev_cnt <= 0) { + dev_err(&pdev->dev, + "%s: Error reading wsa device from DT. wsa_dev_cnt = %d\n", + __func__, wsa_dev_cnt); + ret = -EINVAL; + goto err; + } + + /* + * Expect total phandles count to be NOT less than maximum possible + * WSA count. However, if it is less, then assign same value to + * max count as well. + */ + if (wsa_dev_cnt < wsa_max_devs) { + dev_dbg(&pdev->dev, + "%s: wsa_max_devs = %d cannot exceed wsa_dev_cnt = %d\n", + __func__, wsa_max_devs, wsa_dev_cnt); + wsa_max_devs = wsa_dev_cnt; + } + + /* Make sure prefix string passed for each WSA device */ + ret = of_property_count_strings(pdev->dev.of_node, + "qcom,wsa-aux-dev-prefix"); + if (ret != wsa_dev_cnt) { + dev_err(&pdev->dev, + "%s: expecting %d wsa prefix. Defined only %d in DT\n", + __func__, wsa_dev_cnt, ret); + ret = -EINVAL; + goto err; + } + + /* + * Alloc mem to store phandle and index info of WSA device, if already + * registered with ALSA core + */ + wsa881x_dev_info = devm_kcalloc(&pdev->dev, wsa_max_devs, + sizeof(struct msm_wsa881x_dev_info), + GFP_KERNEL); + if (!wsa881x_dev_info) { + ret = -ENOMEM; + goto err; + } + + /* + * search and check whether all WSA devices are already + * registered with ALSA core or not. If found a node, store + * the node and the index in a local array of struct for later + * use. + */ + for (i = 0; i < wsa_dev_cnt; i++) { + wsa_of_node = of_parse_phandle(pdev->dev.of_node, + "qcom,wsa-devs", i); + if (unlikely(!wsa_of_node)) { + /* we should not be here */ + dev_err(&pdev->dev, + "%s: wsa dev node is not present\n", + __func__); + ret = -EINVAL; + goto err_free_dev_info; + } + if (soc_find_component(wsa_of_node, NULL)) { + /* WSA device registered with ALSA core */ + wsa881x_dev_info[found].of_node = wsa_of_node; + wsa881x_dev_info[found].index = i; + found++; + if (found == wsa_max_devs) + break; + } + } + + if (found < wsa_max_devs) { + dev_dbg(&pdev->dev, + "%s: failed to find %d components. Found only %d\n", + __func__, wsa_max_devs, found); + return -EPROBE_DEFER; + } + dev_info(&pdev->dev, + "%s: found %d wsa881x devices registered with ALSA core\n", + __func__, found); + + card->num_aux_devs = wsa_max_devs; + card->num_configs = wsa_max_devs; + + /* Alloc array of AUX devs struct */ + msm_aux_dev = devm_kcalloc(&pdev->dev, card->num_aux_devs, + sizeof(struct snd_soc_aux_dev), + GFP_KERNEL); + if (!msm_aux_dev) { + ret = -ENOMEM; + goto err_free_dev_info; + } + + /* Alloc array of codec conf struct */ + msm_codec_conf = devm_kcalloc(&pdev->dev, card->num_aux_devs, + sizeof(struct snd_soc_codec_conf), + GFP_KERNEL); + if (!msm_codec_conf) { + ret = -ENOMEM; + goto err_free_aux_dev; + } + + for (i = 0; i < card->num_aux_devs; i++) { + dev_name_str = devm_kzalloc(&pdev->dev, DEV_NAME_STR_LEN, + GFP_KERNEL); + if (!dev_name_str) { + ret = -ENOMEM; + goto err_free_cdc_conf; + } + + ret = of_property_read_string_index(pdev->dev.of_node, + "qcom,wsa-aux-dev-prefix", + wsa881x_dev_info[i].index, + wsa_auxdev_name_prefix); + if (ret) { + dev_err(&pdev->dev, + "%s: failed to read wsa aux dev prefix, ret = %d\n", + __func__, ret); + ret = -EINVAL; + goto err_free_dev_name_str; + } + + snprintf(dev_name_str, strlen("wsa881x.%d"), "wsa881x.%d", i); + msm_aux_dev[i].name = dev_name_str; + msm_aux_dev[i].codec_name = NULL; + msm_aux_dev[i].codec_of_node = + wsa881x_dev_info[i].of_node; + msm_aux_dev[i].init = msm_wsa881x_init; + msm_codec_conf[i].dev_name = NULL; + msm_codec_conf[i].name_prefix = wsa_auxdev_name_prefix[0]; + msm_codec_conf[i].of_node = + wsa881x_dev_info[i].of_node; + } + card->codec_conf = msm_codec_conf; + card->aux_dev = msm_aux_dev; + + return 0; + +err_free_dev_name_str: + devm_kfree(&pdev->dev, dev_name_str); +err_free_cdc_conf: + devm_kfree(&pdev->dev, msm_codec_conf); +err_free_aux_dev: + devm_kfree(&pdev->dev, msm_aux_dev); +err_free_dev_info: + devm_kfree(&pdev->dev, wsa881x_dev_info); +err: + return ret; +} + +static void msm_i2s_auxpcm_init(struct platform_device *pdev) +{ + struct resource *muxsel; + int count; + u32 mi2s_master_slave[MI2S_MAX]; + int ret; + char *str[PCM_I2S_SEL_MAX] = { + "lpaif_pri_mode_muxsel", + "lpaif_sec_mode_muxsel", + "lpaif_tert_mode_muxsel", + "lpaif_quat_mode_muxsel" + }; + + for (count = 0; count < MI2S_MAX; count++) { + mutex_init(&mi2s_intf_conf[count].lock); + mi2s_intf_conf[count].ref_cnt = 0; + } + + for (count = 0; count < AUX_PCM_MAX; count++) { + mutex_init(&auxpcm_intf_conf[count].lock); + auxpcm_intf_conf[count].ref_cnt = 0; + } + + for (count = 0; count < PCM_I2S_SEL_MAX; count++) { + mutex_init(&mi2s_auxpcm_conf[count].lock); + mi2s_auxpcm_conf[count].pcm_i2s_sel_vt_addr = NULL; + } + + for (count = 0; count < PCM_I2S_SEL_MAX; count++) { + muxsel = platform_get_resource_byname(pdev, IORESOURCE_MEM, + str[count]); + if (muxsel) { + mi2s_auxpcm_conf[count].pcm_i2s_sel_vt_addr + = ioremap(muxsel->start, resource_size(muxsel)); + } + } + + ret = of_property_read_u32_array(pdev->dev.of_node, + "qcom,msm-mi2s-master", + mi2s_master_slave, MI2S_MAX); + if (ret) { + dev_dbg(&pdev->dev, "%s: no qcom,msm-mi2s-master in DT node\n", + __func__); + } else { + for (count = 0; count < MI2S_MAX; count++) { + mi2s_intf_conf[count].msm_is_mi2s_master = + mi2s_master_slave[count]; + } + } +} + +static void msm_i2s_auxpcm_deinit(void) +{ + int count; + + for (count = 0; count < PCM_I2S_SEL_MAX; count++) { + if (mi2s_auxpcm_conf[count].pcm_i2s_sel_vt_addr != + NULL) { + iounmap( + mi2s_auxpcm_conf[count].pcm_i2s_sel_vt_addr); + mi2s_auxpcm_conf[count].pcm_i2s_sel_vt_addr = NULL; + } + mutex_destroy(&mi2s_auxpcm_conf[count].lock); + } + + for (count = 0; count < AUX_PCM_MAX; count++) { + mutex_destroy(&auxpcm_intf_conf[count].lock); + auxpcm_intf_conf[count].ref_cnt = 0; + } + + for (count = 0; count < MI2S_MAX; count++) { + mutex_destroy(&mi2s_intf_conf[count].lock); + mi2s_intf_conf[count].ref_cnt = 0; + mi2s_intf_conf[count].msm_is_mi2s_master = 0; + } +} + +static int msm_asoc_machine_probe(struct platform_device *pdev) +{ + struct snd_soc_card *card; + struct msm_asoc_mach_data *pdata; + const char *mbhc_audio_jack_type = NULL; + char *mclk_freq_prop_name; + const struct of_device_id *match; + int ret; + const char *usb_c_dt = "qcom,msm-mbhc-usbc-audio-supported"; + + if (!pdev->dev.of_node) { + dev_err(&pdev->dev, "No platform supplied from device tree\n"); + return -EINVAL; + } + + pdata = devm_kzalloc(&pdev->dev, + sizeof(struct msm_asoc_mach_data), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + card = populate_snd_card_dailinks(&pdev->dev); + if (!card) { + dev_err(&pdev->dev, "%s: Card uninitialized\n", __func__); + ret = -EINVAL; + goto err; + } + card->dev = &pdev->dev; + platform_set_drvdata(pdev, card); + snd_soc_card_set_drvdata(card, pdata); + + ret = snd_soc_of_parse_card_name(card, "qcom,model"); + if (ret) { + dev_err(&pdev->dev, "parse card name failed, err:%d\n", + ret); + goto err; + } + + ret = snd_soc_of_parse_audio_routing(card, "qcom,audio-routing"); + if (ret) { + dev_err(&pdev->dev, "parse audio routing failed, err:%d\n", + ret); + goto err; + } + + match = of_match_node(sdm845_asoc_machine_of_match, + pdev->dev.of_node); + if (!match) { + dev_err(&pdev->dev, "%s: no matched codec is found.\n", + __func__); + goto err; + } + + mclk_freq_prop_name = "qcom,tavil-mclk-clk-freq"; + + ret = of_property_read_u32(pdev->dev.of_node, + mclk_freq_prop_name, &pdata->mclk_freq); + if (ret) { + dev_err(&pdev->dev, + "Looking up %s property in node %s failed, err%d\n", + mclk_freq_prop_name, + pdev->dev.of_node->full_name, ret); + goto err; + } + + if (pdata->mclk_freq != CODEC_EXT_CLK_RATE) { + dev_err(&pdev->dev, "unsupported mclk freq %u\n", + pdata->mclk_freq); + ret = -EINVAL; + goto err; + } + + ret = msm_populate_dai_link_component_of_node(card); + if (ret) { + ret = -EPROBE_DEFER; + goto err; + } + ret = msm_init_wsa_dev(pdev, card); + if (ret) + goto err; + + ret = devm_snd_soc_register_card(&pdev->dev, card); + if (ret == -EPROBE_DEFER) { + if (codec_reg_done) + ret = -EINVAL; + goto err; + } else if (ret) { + dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", + ret); + goto err; + } + dev_info(&pdev->dev, "Sound card %s registered\n", card->name); + spdev = pdev; + + ret = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev); + if (ret) { + dev_dbg(&pdev->dev, "%s: failed to add child nodes, ret=%d\n", + __func__, ret); + } else { + pdata->hph_en1_gpio_p = of_parse_phandle(pdev->dev.of_node, + "qcom,hph-en1-gpio", 0); + if (!pdata->hph_en1_gpio_p) { + dev_dbg(&pdev->dev, "property %s not detected in node %s", + "qcom,hph-en1-gpio", + pdev->dev.of_node->full_name); + } + + pdata->hph_en0_gpio_p = of_parse_phandle(pdev->dev.of_node, + "qcom,hph-en0-gpio", 0); + if (!pdata->hph_en0_gpio_p) { + dev_dbg(&pdev->dev, "property %s not detected in node %s", + "qcom,hph-en0-gpio", + pdev->dev.of_node->full_name); + } + } + + ret = of_property_read_string(pdev->dev.of_node, + "qcom,mbhc-audio-jack-type", &mbhc_audio_jack_type); + if (ret) { + dev_dbg(&pdev->dev, "Looking up %s property in node %s failed", + "qcom,mbhc-audio-jack-type", + pdev->dev.of_node->full_name); + dev_dbg(&pdev->dev, "Jack type properties set to default"); + } else { + if (!strcmp(mbhc_audio_jack_type, "4-pole-jack")) { + wcd_mbhc_cfg.enable_anc_mic_detect = false; + dev_dbg(&pdev->dev, "This hardware has 4 pole jack"); + } else if (!strcmp(mbhc_audio_jack_type, "5-pole-jack")) { + wcd_mbhc_cfg.enable_anc_mic_detect = true; + dev_dbg(&pdev->dev, "This hardware has 5 pole jack"); + } else if (!strcmp(mbhc_audio_jack_type, "6-pole-jack")) { + wcd_mbhc_cfg.enable_anc_mic_detect = true; + dev_dbg(&pdev->dev, "This hardware has 6 pole jack"); + } else { + wcd_mbhc_cfg.enable_anc_mic_detect = false; + dev_dbg(&pdev->dev, "Unknown value, set to default"); + } + } + /* + * Parse US-Euro gpio info from DT. Report no error if us-euro + * entry is not found in DT file as some targets do not support + * US-Euro detection + */ + pdata->us_euro_gpio = of_get_named_gpio(pdev->dev.of_node, + "qcom,us-euro-gpios", 0); + if (!gpio_is_valid(pdata->us_euro_gpio)) + pdata->us_euro_gpio_p = of_parse_phandle(pdev->dev.of_node, + "qcom,us-euro-gpios", 0); + if (!gpio_is_valid(pdata->us_euro_gpio) && (!pdata->us_euro_gpio_p)) { + dev_dbg(&pdev->dev, "property %s not detected in node %s", + "qcom,us-euro-gpios", pdev->dev.of_node->full_name); + } else { + dev_dbg(&pdev->dev, "%s detected", + "qcom,us-euro-gpios"); + wcd_mbhc_cfg.swap_gnd_mic = msm_swap_gnd_mic; + } + + if (of_find_property(pdev->dev.of_node, usb_c_dt, NULL)) + wcd_mbhc_cfg.swap_gnd_mic = msm_swap_gnd_mic; + + ret = msm_prepare_us_euro(card); + if (ret) + dev_dbg(&pdev->dev, "msm_prepare_us_euro failed (%d)\n", + ret); + + /* Parse pinctrl info from devicetree */ + ret = msm_get_pinctrl(pdev); + if (!ret) { + pr_debug("%s: pinctrl parsing successful\n", __func__); + } else { + dev_dbg(&pdev->dev, + "%s: Parsing pinctrl failed with %d. Cannot use Ports\n", + __func__, ret); + ret = 0; + } + + msm_i2s_auxpcm_init(pdev); + + is_initial_boot = true; + ret = audio_notifier_register("sdm845", AUDIO_NOTIFIER_ADSP_DOMAIN, + &service_nb); + if (ret < 0) + pr_err("%s: Audio notifier register failed ret = %d\n", + __func__, ret); + + return 0; +err: + msm_release_pinctrl(pdev); + devm_kfree(&pdev->dev, pdata); + return ret; +} + +static int msm_asoc_machine_remove(struct platform_device *pdev) +{ + struct snd_soc_card *card = platform_get_drvdata(pdev); + struct msm_asoc_mach_data *pdata = + snd_soc_card_get_drvdata(card); + + audio_notifier_deregister("sdm845"); + if (pdata->us_euro_gpio > 0) { + gpio_free(pdata->us_euro_gpio); + pdata->us_euro_gpio = 0; + } + msm_i2s_auxpcm_deinit(); + + msm_release_pinctrl(pdev); + snd_soc_unregister_card(card); + return 0; +} + +static struct platform_driver sdm845_asoc_machine_driver = { + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + .pm = &snd_soc_pm_ops, + .of_match_table = sdm845_asoc_machine_of_match, + }, + .probe = msm_asoc_machine_probe, + .remove = msm_asoc_machine_remove, +}; +module_platform_driver(sdm845_asoc_machine_driver); + +MODULE_DESCRIPTION("ALSA SoC msm"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:" DRV_NAME); +MODULE_DEVICE_TABLE(of, sdm845_asoc_machine_of_match); diff --git a/audio-kernel/asoc/sm6150.c b/audio-kernel/asoc/sm6150.c new file mode 100644 index 000000000..9c5c303a6 --- /dev/null +++ b/audio-kernel/asoc/sm6150.c @@ -0,0 +1,9278 @@ +/* + * Copyright (c) 2018-2019, 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "device_event.h" +#include "msm-pcm-routing-v2.h" +#include "codecs/msm-cdc-pinctrl.h" +#include "codecs/wcd934x/wcd934x.h" +#include "codecs/wcd9335.h" +#include "codecs/wcd934x/wcd934x-mbhc.h" +#include "codecs/wcd937x/wcd937x-mbhc.h" +#include "codecs/wsa881x.h" +#include "codecs/bolero/bolero-cdc.h" +#include +#include "codecs/bolero/wsa-macro.h" +#include "codecs/wcd937x/wcd937x.h" + +#define DRV_NAME "sm6150-asoc-snd" + +#define __CHIPSET__ "SM6150 " +#define MSM_DAILINK_NAME(name) (__CHIPSET__#name) + +#define SAMPLING_RATE_8KHZ 8000 +#define SAMPLING_RATE_11P025KHZ 11025 +#define SAMPLING_RATE_16KHZ 16000 +#define SAMPLING_RATE_22P05KHZ 22050 +#define SAMPLING_RATE_32KHZ 32000 +#define SAMPLING_RATE_44P1KHZ 44100 +#define SAMPLING_RATE_48KHZ 48000 +#define SAMPLING_RATE_88P2KHZ 88200 +#define SAMPLING_RATE_96KHZ 96000 +#define SAMPLING_RATE_176P4KHZ 176400 +#define SAMPLING_RATE_192KHZ 192000 +#define SAMPLING_RATE_352P8KHZ 352800 +#define SAMPLING_RATE_384KHZ 384000 + +#define WCD9XXX_MBHC_DEF_BUTTONS 8 +#define WCD9XXX_MBHC_DEF_RLOADS 5 +#define CODEC_EXT_CLK_RATE 9600000 +#define ADSP_STATE_READY_TIMEOUT_MS 3000 +#define DEV_NAME_STR_LEN 32 + +#define WSA8810_NAME_1 "wsa881x.20170211" +#define WSA8810_NAME_2 "wsa881x.20170212" +#define WCN_CDC_SLIM_RX_CH_MAX 2 +#define WCN_CDC_SLIM_TX_CH_MAX 3 +#define TDM_CHANNEL_MAX 8 + +#define ADSP_STATE_READY_TIMEOUT_MS 3000 +#define MSM_LL_QOS_VALUE 300 /* time in us to ensure LPM doesn't go in C3/C4 */ +#define MSM_HIFI_ON 1 + +#define SM6150_SOC_VERSION_1_0 0x00010000 +#define SM6150_SOC_MSM_ID 0x163 + +enum { + SLIM_RX_0 = 0, + SLIM_RX_1, + SLIM_RX_2, + SLIM_RX_3, + SLIM_RX_4, + SLIM_RX_5, + SLIM_RX_6, + SLIM_RX_7, + SLIM_RX_MAX, +}; +enum { + SLIM_TX_0 = 0, + SLIM_TX_1, + SLIM_TX_2, + SLIM_TX_3, + SLIM_TX_4, + SLIM_TX_5, + SLIM_TX_6, + SLIM_TX_7, + SLIM_TX_8, + SLIM_TX_MAX, +}; + +enum { + PRIM_MI2S = 0, + SEC_MI2S, + TERT_MI2S, + QUAT_MI2S, + QUIN_MI2S, + MI2S_MAX, +}; + +enum { + PRIM_AUX_PCM = 0, + SEC_AUX_PCM, + TERT_AUX_PCM, + QUAT_AUX_PCM, + QUIN_AUX_PCM, + AUX_PCM_MAX, +}; + +enum { + WSA_CDC_DMA_RX_0 = 0, + WSA_CDC_DMA_RX_1, + RX_CDC_DMA_RX_0, + RX_CDC_DMA_RX_1, + RX_CDC_DMA_RX_2, + RX_CDC_DMA_RX_3, + RX_CDC_DMA_RX_5, + CDC_DMA_RX_MAX, +}; + +enum { + WSA_CDC_DMA_TX_0 = 0, + WSA_CDC_DMA_TX_1, + WSA_CDC_DMA_TX_2, + TX_CDC_DMA_TX_0, + TX_CDC_DMA_TX_3, + TX_CDC_DMA_TX_4, + CDC_DMA_TX_MAX, +}; + +struct mi2s_conf { + struct mutex lock; + u32 ref_cnt; + u32 msm_is_mi2s_master; +}; + +static u32 mi2s_ebit_clk[MI2S_MAX] = { + Q6AFE_LPASS_CLK_ID_PRI_MI2S_EBIT, + Q6AFE_LPASS_CLK_ID_SEC_MI2S_EBIT, + Q6AFE_LPASS_CLK_ID_TER_MI2S_EBIT, + Q6AFE_LPASS_CLK_ID_QUAD_MI2S_EBIT, + Q6AFE_LPASS_CLK_ID_QUI_MI2S_EBIT +}; + +struct dev_config { + u32 sample_rate; + u32 bit_format; + u32 channels; +}; + +enum { + DP_RX_IDX = 0, + EXT_DISP_RX_IDX_MAX, +}; + +struct msm_wsa881x_dev_info { + struct device_node *of_node; + u32 index; +}; + +struct aux_codec_dev_info { + struct device_node *of_node; + u32 index; +}; + +enum pinctrl_pin_state { + STATE_DISABLE = 0, /* All pins are in sleep state */ + STATE_MI2S_ACTIVE, /* I2S = active, TDM = sleep */ + STATE_TDM_ACTIVE, /* I2S = sleep, TDM = active */ +}; + +struct msm_pinctrl_info { + struct pinctrl *pinctrl; + struct pinctrl_state *mi2s_disable; + struct pinctrl_state *tdm_disable; + struct pinctrl_state *mi2s_active; + struct pinctrl_state *tdm_active; + enum pinctrl_pin_state curr_state; +}; + +struct msm_asoc_mach_data { + struct snd_info_entry *codec_root; + struct msm_pinctrl_info pinctrl_info; + int usbc_en2_gpio; /* used by gpio driver API */ + int hph_en1_gpio; + int hph_en0_gpio; + struct device_node *dmic01_gpio_p; /* used by pinctrl API */ + struct device_node *dmic23_gpio_p; /* used by pinctrl API */ + struct device_node *us_euro_gpio_p; /* used by pinctrl API */ + struct pinctrl *usbc_en2_gpio_p; /* used by pinctrl API */ + struct device_node *hph_en1_gpio_p; /* used by pinctrl API */ + struct device_node *hph_en0_gpio_p; /* used by pinctrl API */ + bool is_afe_config_done; + struct device_node *fsa_handle; +}; + +struct msm_asoc_wcd93xx_codec { + void* (*get_afe_config_fn)(struct snd_soc_codec *codec, + enum afe_config_type config_type); +}; + +static const char *const pin_states[] = {"sleep", "i2s-active", + "tdm-active"}; + +static struct snd_soc_card snd_soc_card_sm6150_msm; + +enum { + TDM_0 = 0, + TDM_1, + TDM_2, + TDM_3, + TDM_4, + TDM_5, + TDM_6, + TDM_7, + TDM_PORT_MAX, +}; + +enum { + TDM_PRI = 0, + TDM_SEC, + TDM_TERT, + TDM_QUAT, + TDM_QUIN, + TDM_INTERFACE_MAX, +}; + +struct tdm_port { + u32 mode; + u32 channel; +}; + +/* TDM default config */ +static struct dev_config tdm_rx_cfg[TDM_INTERFACE_MAX][TDM_PORT_MAX] = { + { /* PRI TDM */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_0 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_1 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_2 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_3 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_4 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_5 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_6 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_7 */ + }, + { /* SEC TDM */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_0 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_1 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_2 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_3 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_4 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_5 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_6 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_7 */ + }, + { /* TERT TDM */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_0 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_1 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_2 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_3 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_4 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_5 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_6 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_7 */ + }, + { /* QUAT TDM */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_0 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_1 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_2 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_3 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_4 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_5 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_6 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_7 */ + }, + { /* QUIN TDM */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_0 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_1 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_2 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_3 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_4 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_5 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_6 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_7 */ + } + +}; + +/* TDM default config */ +static struct dev_config tdm_tx_cfg[TDM_INTERFACE_MAX][TDM_PORT_MAX] = { + { /* PRI TDM */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_0 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_1 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_2 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_3 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_4 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_5 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_6 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_7 */ + }, + { /* SEC TDM */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_0 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_1 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_2 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_3 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_4 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_5 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_6 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_7 */ + }, + { /* TERT TDM */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_0 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_1 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_2 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_3 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_4 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_5 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_6 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_7 */ + }, + { /* QUAT TDM */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_0 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_1 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_2 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_3 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_4 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_5 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_6 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_7 */ + }, + { /* QUIN TDM */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_0 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_1 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_2 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_3 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_4 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_5 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_6 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_7 */ + } +}; + + +/* Default configuration of slimbus channels */ +static struct dev_config slim_rx_cfg[] = { + [SLIM_RX_0] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_RX_1] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_RX_2] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_RX_3] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_RX_4] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_RX_5] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_RX_6] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_RX_7] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, +}; + +static struct dev_config slim_tx_cfg[] = { + [SLIM_TX_0] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_TX_1] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_TX_2] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_TX_3] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_TX_4] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_TX_5] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_TX_6] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_TX_7] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_TX_8] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, +}; + +/* Default configuration of Codec DMA Interface Tx */ +static struct dev_config cdc_dma_rx_cfg[] = { + [WSA_CDC_DMA_RX_0] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, + [WSA_CDC_DMA_RX_1] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, + [RX_CDC_DMA_RX_0] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, + [RX_CDC_DMA_RX_1] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, + [RX_CDC_DMA_RX_2] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, + [RX_CDC_DMA_RX_3] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, + [RX_CDC_DMA_RX_5] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, +}; + +/* Default configuration of Codec DMA Interface Rx */ +static struct dev_config cdc_dma_tx_cfg[] = { + [WSA_CDC_DMA_TX_0] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, + [WSA_CDC_DMA_TX_1] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, + [WSA_CDC_DMA_TX_2] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, + [TX_CDC_DMA_TX_0] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, + [TX_CDC_DMA_TX_3] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, + [TX_CDC_DMA_TX_4] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, +}; + +/* Default configuration of external display BE */ +static struct dev_config ext_disp_rx_cfg[] = { + [DP_RX_IDX] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, +}; + +static struct dev_config usb_rx_cfg = { + .sample_rate = SAMPLING_RATE_48KHZ, + .bit_format = SNDRV_PCM_FORMAT_S16_LE, + .channels = 2, +}; + +static struct dev_config usb_tx_cfg = { + .sample_rate = SAMPLING_RATE_48KHZ, + .bit_format = SNDRV_PCM_FORMAT_S16_LE, + .channels = 1, +}; + +static struct dev_config proxy_rx_cfg = { + .sample_rate = SAMPLING_RATE_48KHZ, + .bit_format = SNDRV_PCM_FORMAT_S16_LE, + .channels = 2, +}; + +/* Default configuration of MI2S channels */ +static struct dev_config mi2s_rx_cfg[] = { + [PRIM_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, + [SEC_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, + [TERT_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, + [QUAT_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, + [QUIN_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, +}; + +static struct dev_config mi2s_tx_cfg[] = { + [PRIM_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SEC_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [TERT_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [QUAT_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [QUIN_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, +}; + +static struct dev_config aux_pcm_rx_cfg[] = { + [PRIM_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SEC_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [TERT_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [QUAT_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [QUIN_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, +}; + +static struct dev_config aux_pcm_tx_cfg[] = { + [PRIM_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SEC_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [TERT_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [QUAT_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [QUIN_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, +}; +static int msm_vi_feed_tx_ch = 2; +static const char *const slim_rx_ch_text[] = {"One", "Two"}; +static const char *const slim_tx_ch_text[] = {"One", "Two", "Three", "Four", + "Five", "Six", "Seven", + "Eight"}; +static const char *const vi_feed_ch_text[] = {"One", "Two"}; +static char const *bit_format_text[] = {"S16_LE", "S24_LE", "S24_3LE", + "S32_LE"}; +static char const *ext_disp_bit_format_text[] = {"S16_LE", "S24_LE", + "S24_3LE"}; +static char const *slim_sample_rate_text[] = {"KHZ_8", "KHZ_16", + "KHZ_32", "KHZ_44P1", "KHZ_48", + "KHZ_88P2", "KHZ_96", "KHZ_176P4", + "KHZ_192", "KHZ_352P8", "KHZ_384"}; +static char const *bt_sample_rate_text[] = {"KHZ_8", "KHZ_16", + "KHZ_44P1", "KHZ_48", + "KHZ_88P2", "KHZ_96"}; +static char const *bt_sample_rate_rx_text[] = {"KHZ_8", "KHZ_16", + "KHZ_44P1", "KHZ_48", + "KHZ_88P2", "KHZ_96"}; +static char const *bt_sample_rate_tx_text[] = {"KHZ_8", "KHZ_16", + "KHZ_44P1", "KHZ_48", + "KHZ_88P2", "KHZ_96"}; +static const char *const usb_ch_text[] = {"One", "Two", "Three", "Four", + "Five", "Six", "Seven", + "Eight"}; +static char const *ch_text[] = {"Two", "Three", "Four", "Five", + "Six", "Seven", "Eight"}; +static char const *usb_sample_rate_text[] = {"KHZ_8", "KHZ_11P025", + "KHZ_16", "KHZ_22P05", + "KHZ_32", "KHZ_44P1", "KHZ_48", + "KHZ_88P2", "KHZ_96", "KHZ_176P4", + "KHZ_192", "KHZ_352P8", "KHZ_384"}; +static char const *ext_disp_sample_rate_text[] = {"KHZ_48", "KHZ_96", + "KHZ_192", "KHZ_32", "KHZ_44P1", + "KHZ_88P2", "KHZ_176P4" }; +static char const *tdm_ch_text[] = {"One", "Two", "Three", "Four", + "Five", "Six", "Seven", "Eight"}; +static char const *tdm_bit_format_text[] = {"S16_LE", "S24_LE", "S32_LE"}; +static char const *tdm_sample_rate_text[] = {"KHZ_8", "KHZ_16", "KHZ_32", + "KHZ_48", "KHZ_176P4", + "KHZ_352P8"}; +static const char *const auxpcm_rate_text[] = {"KHZ_8", "KHZ_16"}; +static char const *mi2s_rate_text[] = {"KHZ_8", "KHZ_11P025", "KHZ_16", + "KHZ_22P05", "KHZ_32", "KHZ_44P1", + "KHZ_48", "KHZ_96", "KHZ_192"}; +static const char *const mi2s_ch_text[] = {"One", "Two", "Three", "Four", + "Five", "Six", "Seven", + "Eight"}; +static const char *const hifi_text[] = {"Off", "On"}; +static const char *const qos_text[] = {"Disable", "Enable"}; + +static const char *const cdc_dma_rx_ch_text[] = {"One", "Two"}; +static const char *const cdc_dma_tx_ch_text[] = {"One", "Two", "Three", "Four", + "Five", "Six", "Seven", + "Eight"}; +static char const *cdc_dma_sample_rate_text[] = {"KHZ_8", "KHZ_11P025", + "KHZ_16", "KHZ_22P05", + "KHZ_32", "KHZ_44P1", "KHZ_48", + "KHZ_88P2", "KHZ_96", + "KHZ_176P4", "KHZ_192", + "KHZ_352P8", "KHZ_384"}; + + +static SOC_ENUM_SINGLE_EXT_DECL(slim_0_rx_chs, slim_rx_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_2_rx_chs, slim_rx_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_0_tx_chs, slim_tx_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_1_tx_chs, slim_tx_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_5_rx_chs, slim_rx_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_6_rx_chs, slim_rx_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(usb_rx_chs, usb_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(usb_tx_chs, usb_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(vi_feed_tx_chs, vi_feed_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(ext_disp_rx_chs, ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(proxy_rx_chs, ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_0_rx_format, bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_5_rx_format, bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_6_rx_format, bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_0_tx_format, bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(usb_rx_format, bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(usb_tx_format, bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(ext_disp_rx_format, ext_disp_bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_0_rx_sample_rate, slim_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_2_rx_sample_rate, slim_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_0_tx_sample_rate, slim_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_5_rx_sample_rate, slim_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_6_rx_sample_rate, slim_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(bt_sample_rate, bt_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(bt_sample_rate_rx, bt_sample_rate_rx_text); +static SOC_ENUM_SINGLE_EXT_DECL(bt_sample_rate_tx, bt_sample_rate_tx_text); +static SOC_ENUM_SINGLE_EXT_DECL(usb_rx_sample_rate, usb_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(usb_tx_sample_rate, usb_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(ext_disp_rx_sample_rate, + ext_disp_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(tdm_tx_chs, tdm_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(tdm_tx_format, tdm_bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(tdm_tx_sample_rate, tdm_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(tdm_rx_chs, tdm_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(tdm_rx_format, tdm_bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(tdm_rx_sample_rate, tdm_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(prim_aux_pcm_rx_sample_rate, auxpcm_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(sec_aux_pcm_rx_sample_rate, auxpcm_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(tert_aux_pcm_rx_sample_rate, auxpcm_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(quat_aux_pcm_rx_sample_rate, auxpcm_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(quin_aux_pcm_rx_sample_rate, auxpcm_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(prim_aux_pcm_tx_sample_rate, auxpcm_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(sec_aux_pcm_tx_sample_rate, auxpcm_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(tert_aux_pcm_tx_sample_rate, auxpcm_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(quat_aux_pcm_tx_sample_rate, auxpcm_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(quin_aux_pcm_tx_sample_rate, auxpcm_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(prim_mi2s_rx_sample_rate, mi2s_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(sec_mi2s_rx_sample_rate, mi2s_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(tert_mi2s_rx_sample_rate, mi2s_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(quat_mi2s_rx_sample_rate, mi2s_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(quin_mi2s_rx_sample_rate, mi2s_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(prim_mi2s_tx_sample_rate, mi2s_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(sec_mi2s_tx_sample_rate, mi2s_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(tert_mi2s_tx_sample_rate, mi2s_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(quat_mi2s_tx_sample_rate, mi2s_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(quin_mi2s_tx_sample_rate, mi2s_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(prim_mi2s_rx_chs, mi2s_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(prim_mi2s_tx_chs, mi2s_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(sec_mi2s_rx_chs, mi2s_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(sec_mi2s_tx_chs, mi2s_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(tert_mi2s_rx_chs, mi2s_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(tert_mi2s_tx_chs, mi2s_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(quat_mi2s_rx_chs, mi2s_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(quat_mi2s_tx_chs, mi2s_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(quin_mi2s_rx_chs, mi2s_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(quin_mi2s_tx_chs, mi2s_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(mi2s_rx_format, bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(mi2s_tx_format, bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(aux_pcm_rx_format, bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(aux_pcm_tx_format, bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(hifi_function, hifi_text); +static SOC_ENUM_SINGLE_EXT_DECL(wsa_cdc_dma_rx_0_chs, cdc_dma_rx_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(wsa_cdc_dma_rx_1_chs, cdc_dma_rx_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(rx_cdc_dma_rx_0_chs, cdc_dma_rx_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(rx_cdc_dma_rx_1_chs, cdc_dma_rx_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(rx_cdc_dma_rx_2_chs, cdc_dma_rx_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(rx_cdc_dma_rx_3_chs, cdc_dma_rx_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(rx_cdc_dma_rx_5_chs, cdc_dma_rx_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(wsa_cdc_dma_tx_0_chs, cdc_dma_tx_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(wsa_cdc_dma_tx_1_chs, cdc_dma_tx_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(wsa_cdc_dma_tx_2_chs, cdc_dma_tx_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(tx_cdc_dma_tx_0_chs, cdc_dma_tx_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(tx_cdc_dma_tx_3_chs, cdc_dma_tx_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(tx_cdc_dma_tx_4_chs, cdc_dma_tx_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(wsa_cdc_dma_rx_0_format, bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(wsa_cdc_dma_rx_1_format, bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(rx_cdc_dma_rx_0_format, bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(rx_cdc_dma_rx_1_format, bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(rx_cdc_dma_rx_2_format, bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(rx_cdc_dma_rx_3_format, bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(rx_cdc_dma_rx_5_format, bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(wsa_cdc_dma_tx_1_format, bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(wsa_cdc_dma_tx_2_format, bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(tx_cdc_dma_tx_0_format, bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(tx_cdc_dma_tx_3_format, bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(tx_cdc_dma_tx_4_format, bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(wsa_cdc_dma_rx_0_sample_rate, + cdc_dma_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(wsa_cdc_dma_rx_1_sample_rate, + cdc_dma_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(rx_cdc_dma_rx_0_sample_rate, + cdc_dma_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(rx_cdc_dma_rx_1_sample_rate, + cdc_dma_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(rx_cdc_dma_rx_2_sample_rate, + cdc_dma_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(rx_cdc_dma_rx_3_sample_rate, + cdc_dma_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(rx_cdc_dma_rx_5_sample_rate, + cdc_dma_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(wsa_cdc_dma_tx_0_sample_rate, + cdc_dma_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(wsa_cdc_dma_tx_1_sample_rate, + cdc_dma_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(wsa_cdc_dma_tx_2_sample_rate, + cdc_dma_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(tx_cdc_dma_tx_0_sample_rate, + cdc_dma_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(tx_cdc_dma_tx_3_sample_rate, + cdc_dma_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(tx_cdc_dma_tx_4_sample_rate, + cdc_dma_sample_rate_text); + +static int msm_hifi_control; +static bool codec_reg_done; +static struct snd_soc_aux_dev *msm_aux_dev; +static struct snd_soc_codec_conf *msm_codec_conf; +static struct msm_asoc_wcd93xx_codec msm_codec_fn; + +static int dmic_0_1_gpio_cnt; +static int dmic_2_3_gpio_cnt; + +static void *def_wcd_mbhc_cal(void); +static int msm_snd_enable_codec_ext_clk(struct snd_soc_codec *codec, + int enable, bool dapm); +static int msm_wsa881x_init(struct snd_soc_component *component); +static int msm_aux_codec_init(struct snd_soc_component *component); + +/* + * Need to report LINEIN + * if R/L channel impedance is larger than 5K ohm + */ +static struct wcd_mbhc_config wcd_mbhc_cfg = { + .read_fw_bin = false, + .calibration = NULL, + .detect_extn_cable = true, + .mono_stero_detection = false, + .swap_gnd_mic = NULL, + .hs_ext_micbias = true, + .key_code[0] = KEY_MEDIA, + .key_code[1] = KEY_VOICECOMMAND, + .key_code[2] = KEY_VOLUMEUP, + .key_code[3] = KEY_VOLUMEDOWN, + .key_code[4] = 0, + .key_code[5] = 0, + .key_code[6] = 0, + .key_code[7] = 0, + .linein_th = 5000, + .moisture_en = true, + .mbhc_micbias = MIC_BIAS_2, + .anc_micbias = MIC_BIAS_2, + .enable_anc_mic_detect = false, +}; + +static struct snd_soc_dapm_route wcd_audio_paths[] = { + {"MIC BIAS1", NULL, "MCLK TX"}, + {"MIC BIAS2", NULL, "MCLK TX"}, + {"MIC BIAS3", NULL, "MCLK TX"}, + {"MIC BIAS4", NULL, "MCLK TX"}, +}; + +static struct afe_clk_set mi2s_clk[MI2S_MAX] = { + { + AFE_API_VERSION_I2S_CONFIG, + Q6AFE_LPASS_CLK_ID_PRI_MI2S_IBIT, + Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ, + Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO, + Q6AFE_LPASS_CLK_ROOT_DEFAULT, + 0, + }, + { + AFE_API_VERSION_I2S_CONFIG, + Q6AFE_LPASS_CLK_ID_SEC_MI2S_IBIT, + Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ, + Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO, + Q6AFE_LPASS_CLK_ROOT_DEFAULT, + 0, + }, + { + AFE_API_VERSION_I2S_CONFIG, + Q6AFE_LPASS_CLK_ID_TER_MI2S_IBIT, + Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ, + Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO, + Q6AFE_LPASS_CLK_ROOT_DEFAULT, + 0, + }, + { + AFE_API_VERSION_I2S_CONFIG, + Q6AFE_LPASS_CLK_ID_QUAD_MI2S_IBIT, + Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ, + Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO, + Q6AFE_LPASS_CLK_ROOT_DEFAULT, + 0, + }, + { + AFE_API_VERSION_I2S_CONFIG, + Q6AFE_LPASS_CLK_ID_QUI_MI2S_IBIT, + Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ, + Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO, + Q6AFE_LPASS_CLK_ROOT_DEFAULT, + 0, + } + +}; + +static struct mi2s_conf mi2s_intf_conf[MI2S_MAX]; + +static int slim_get_sample_rate_val(int sample_rate) +{ + int sample_rate_val = 0; + + switch (sample_rate) { + case SAMPLING_RATE_8KHZ: + sample_rate_val = 0; + break; + case SAMPLING_RATE_16KHZ: + sample_rate_val = 1; + break; + case SAMPLING_RATE_32KHZ: + sample_rate_val = 2; + break; + case SAMPLING_RATE_44P1KHZ: + sample_rate_val = 3; + break; + case SAMPLING_RATE_48KHZ: + sample_rate_val = 4; + break; + case SAMPLING_RATE_88P2KHZ: + sample_rate_val = 5; + break; + case SAMPLING_RATE_96KHZ: + sample_rate_val = 6; + break; + case SAMPLING_RATE_176P4KHZ: + sample_rate_val = 7; + break; + case SAMPLING_RATE_192KHZ: + sample_rate_val = 8; + break; + case SAMPLING_RATE_352P8KHZ: + sample_rate_val = 9; + break; + case SAMPLING_RATE_384KHZ: + sample_rate_val = 10; + break; + default: + sample_rate_val = 4; + break; + } + return sample_rate_val; +} + +static int slim_get_sample_rate(int value) +{ + int sample_rate = 0; + + switch (value) { + case 0: + sample_rate = SAMPLING_RATE_8KHZ; + break; + case 1: + sample_rate = SAMPLING_RATE_16KHZ; + break; + case 2: + sample_rate = SAMPLING_RATE_32KHZ; + break; + case 3: + sample_rate = SAMPLING_RATE_44P1KHZ; + break; + case 4: + sample_rate = SAMPLING_RATE_48KHZ; + break; + case 5: + sample_rate = SAMPLING_RATE_88P2KHZ; + break; + case 6: + sample_rate = SAMPLING_RATE_96KHZ; + break; + case 7: + sample_rate = SAMPLING_RATE_176P4KHZ; + break; + case 8: + sample_rate = SAMPLING_RATE_192KHZ; + break; + case 9: + sample_rate = SAMPLING_RATE_352P8KHZ; + break; + case 10: + sample_rate = SAMPLING_RATE_384KHZ; + break; + default: + sample_rate = SAMPLING_RATE_48KHZ; + break; + } + return sample_rate; +} + +static int slim_get_bit_format_val(int bit_format) +{ + int val = 0; + + switch (bit_format) { + case SNDRV_PCM_FORMAT_S32_LE: + val = 3; + break; + case SNDRV_PCM_FORMAT_S24_3LE: + val = 2; + break; + case SNDRV_PCM_FORMAT_S24_LE: + val = 1; + break; + case SNDRV_PCM_FORMAT_S16_LE: + default: + val = 0; + break; + } + return val; +} + +static int slim_get_bit_format(int val) +{ + int bit_fmt = SNDRV_PCM_FORMAT_S16_LE; + + switch (val) { + case 0: + bit_fmt = SNDRV_PCM_FORMAT_S16_LE; + break; + case 1: + bit_fmt = SNDRV_PCM_FORMAT_S24_LE; + break; + case 2: + bit_fmt = SNDRV_PCM_FORMAT_S24_3LE; + break; + case 3: + bit_fmt = SNDRV_PCM_FORMAT_S32_LE; + break; + default: + bit_fmt = SNDRV_PCM_FORMAT_S16_LE; + break; + } + return bit_fmt; +} + +static int slim_get_port_idx(struct snd_kcontrol *kcontrol) +{ + int port_id = 0; + + if (strnstr(kcontrol->id.name, "SLIM_0_RX", sizeof("SLIM_0_RX"))) { + port_id = SLIM_RX_0; + } else if (strnstr(kcontrol->id.name, + "SLIM_2_RX", sizeof("SLIM_2_RX"))) { + port_id = SLIM_RX_2; + } else if (strnstr(kcontrol->id.name, + "SLIM_5_RX", sizeof("SLIM_5_RX"))) { + port_id = SLIM_RX_5; + } else if (strnstr(kcontrol->id.name, + "SLIM_6_RX", sizeof("SLIM_6_RX"))) { + port_id = SLIM_RX_6; + } else if (strnstr(kcontrol->id.name, + "SLIM_0_TX", sizeof("SLIM_0_TX"))) { + port_id = SLIM_TX_0; + } else if (strnstr(kcontrol->id.name, + "SLIM_1_TX", sizeof("SLIM_1_TX"))) { + port_id = SLIM_TX_1; + } else { + pr_err("%s: unsupported channel: %s\n", + __func__, kcontrol->id.name); + return -EINVAL; + } + + return port_id; +} + +static int slim_rx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = slim_get_port_idx(kcontrol); + + if (ch_num < 0) + return ch_num; + + ucontrol->value.enumerated.item[0] = + slim_get_sample_rate_val(slim_rx_cfg[ch_num].sample_rate); + + pr_debug("%s: slim[%d]_rx_sample_rate = %d, item = %d\n", __func__, + ch_num, slim_rx_cfg[ch_num].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int slim_rx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = slim_get_port_idx(kcontrol); + + if (ch_num < 0) + return ch_num; + + slim_rx_cfg[ch_num].sample_rate = + slim_get_sample_rate(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: slim[%d]_rx_sample_rate = %d, item = %d\n", __func__, + ch_num, slim_rx_cfg[ch_num].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int slim_tx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = slim_get_port_idx(kcontrol); + + if (ch_num < 0) + return ch_num; + + ucontrol->value.enumerated.item[0] = + slim_get_sample_rate_val(slim_tx_cfg[ch_num].sample_rate); + + pr_debug("%s: slim[%d]_tx_sample_rate = %d, item = %d\n", __func__, + ch_num, slim_tx_cfg[ch_num].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int slim_tx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int sample_rate = 0; + int ch_num = slim_get_port_idx(kcontrol); + + if (ch_num < 0) + return ch_num; + + sample_rate = slim_get_sample_rate(ucontrol->value.enumerated.item[0]); + if (sample_rate == SAMPLING_RATE_44P1KHZ) { + pr_err("%s: Unsupported sample rate %d: for Tx path\n", + __func__, sample_rate); + return -EINVAL; + } + slim_tx_cfg[ch_num].sample_rate = sample_rate; + + pr_debug("%s: slim[%d]_tx_sample_rate = %d, value = %d\n", __func__, + ch_num, slim_tx_cfg[ch_num].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int slim_rx_bit_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = slim_get_port_idx(kcontrol); + + if (ch_num < 0) + return ch_num; + + ucontrol->value.enumerated.item[0] = + slim_get_bit_format_val(slim_rx_cfg[ch_num].bit_format); + + pr_debug("%s: slim[%d]_rx_bit_format = %d, ucontrol value = %d\n", + __func__, ch_num, slim_rx_cfg[ch_num].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int slim_rx_bit_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = slim_get_port_idx(kcontrol); + + if (ch_num < 0) + return ch_num; + + slim_rx_cfg[ch_num].bit_format = + slim_get_bit_format(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: slim[%d]_rx_bit_format = %d, ucontrol value = %d\n", + __func__, ch_num, slim_rx_cfg[ch_num].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int slim_tx_bit_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = slim_get_port_idx(kcontrol); + + if (ch_num < 0) + return ch_num; + + ucontrol->value.enumerated.item[0] = + slim_get_bit_format_val(slim_tx_cfg[ch_num].bit_format); + + pr_debug("%s: slim[%d]_tx_bit_format = %d, ucontrol value = %d\n", + __func__, ch_num, slim_tx_cfg[ch_num].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int slim_tx_bit_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = slim_get_port_idx(kcontrol); + + if (ch_num < 0) + return ch_num; + + slim_tx_cfg[ch_num].bit_format = + slim_get_bit_format(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: slim[%d]_tx_bit_format = %d, ucontrol value = %d\n", + __func__, ch_num, slim_tx_cfg[ch_num].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int slim_rx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = slim_get_port_idx(kcontrol); + + if (ch_num < 0) + return ch_num; + + pr_debug("%s: msm_slim_[%d]_rx_ch = %d\n", __func__, + ch_num, slim_rx_cfg[ch_num].channels); + ucontrol->value.enumerated.item[0] = slim_rx_cfg[ch_num].channels - 1; + + return 0; +} + +static int slim_rx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = slim_get_port_idx(kcontrol); + + if (ch_num < 0) + return ch_num; + + slim_rx_cfg[ch_num].channels = ucontrol->value.enumerated.item[0] + 1; + pr_debug("%s: msm_slim_[%d]_rx_ch = %d\n", __func__, + ch_num, slim_rx_cfg[ch_num].channels); + + return 1; +} + +static int slim_tx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = slim_get_port_idx(kcontrol); + + if (ch_num < 0) + return ch_num; + + pr_debug("%s: msm_slim_[%d]_tx_ch = %d\n", __func__, + ch_num, slim_tx_cfg[ch_num].channels); + ucontrol->value.enumerated.item[0] = slim_tx_cfg[ch_num].channels - 1; + + return 0; +} + +static int slim_tx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = slim_get_port_idx(kcontrol); + + if (ch_num < 0) + return ch_num; + + slim_tx_cfg[ch_num].channels = ucontrol->value.enumerated.item[0] + 1; + pr_debug("%s: msm_slim_[%d]_tx_ch = %d\n", __func__, + ch_num, slim_tx_cfg[ch_num].channels); + + return 1; +} + +static int msm_vi_feed_tx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = msm_vi_feed_tx_ch - 1; + pr_debug("%s: msm_vi_feed_tx_ch = %ld\n", __func__, + ucontrol->value.integer.value[0]); + return 0; +} + +static int msm_vi_feed_tx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + msm_vi_feed_tx_ch = ucontrol->value.integer.value[0] + 1; + + pr_debug("%s: msm_vi_feed_tx_ch = %d\n", __func__, msm_vi_feed_tx_ch); + return 1; +} + +static int msm_bt_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + /* + * Slimbus_7_Rx/Tx sample rate values should always be in sync (same) + * when used for BT_SCO use case. Return either Rx or Tx sample rate + * value. + */ + switch (slim_rx_cfg[SLIM_RX_7].sample_rate) { + case SAMPLING_RATE_96KHZ: + ucontrol->value.integer.value[0] = 5; + break; + case SAMPLING_RATE_88P2KHZ: + ucontrol->value.integer.value[0] = 4; + break; + case SAMPLING_RATE_48KHZ: + ucontrol->value.integer.value[0] = 3; + break; + case SAMPLING_RATE_44P1KHZ: + ucontrol->value.integer.value[0] = 2; + break; + case SAMPLING_RATE_16KHZ: + ucontrol->value.integer.value[0] = 1; + break; + case SAMPLING_RATE_8KHZ: + default: + ucontrol->value.integer.value[0] = 0; + break; + } + pr_debug("%s: sample rate = %d\n", __func__, + slim_rx_cfg[SLIM_RX_7].sample_rate); + + return 0; +} + +static int msm_bt_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (ucontrol->value.integer.value[0]) { + case 1: + slim_rx_cfg[SLIM_RX_7].sample_rate = SAMPLING_RATE_16KHZ; + slim_tx_cfg[SLIM_TX_7].sample_rate = SAMPLING_RATE_16KHZ; + break; + case 2: + slim_rx_cfg[SLIM_RX_7].sample_rate = SAMPLING_RATE_44P1KHZ; + slim_tx_cfg[SLIM_TX_7].sample_rate = SAMPLING_RATE_44P1KHZ; + break; + case 3: + slim_rx_cfg[SLIM_RX_7].sample_rate = SAMPLING_RATE_48KHZ; + slim_tx_cfg[SLIM_TX_7].sample_rate = SAMPLING_RATE_48KHZ; + break; + case 4: + slim_rx_cfg[SLIM_RX_7].sample_rate = SAMPLING_RATE_88P2KHZ; + slim_tx_cfg[SLIM_TX_7].sample_rate = SAMPLING_RATE_88P2KHZ; + break; + case 5: + slim_rx_cfg[SLIM_RX_7].sample_rate = SAMPLING_RATE_96KHZ; + slim_tx_cfg[SLIM_TX_7].sample_rate = SAMPLING_RATE_96KHZ; + break; + case 0: + default: + slim_rx_cfg[SLIM_RX_7].sample_rate = SAMPLING_RATE_8KHZ; + slim_tx_cfg[SLIM_TX_7].sample_rate = SAMPLING_RATE_8KHZ; + break; + } + pr_debug("%s: sample rates: slim7_rx = %d, slim7_tx = %d, value = %d\n", + __func__, + slim_rx_cfg[SLIM_RX_7].sample_rate, + slim_tx_cfg[SLIM_TX_7].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} +static int msm_bt_sample_rate_rx_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (slim_rx_cfg[SLIM_RX_7].sample_rate) { + case SAMPLING_RATE_96KHZ: + ucontrol->value.integer.value[0] = 5; + break; + case SAMPLING_RATE_88P2KHZ: + ucontrol->value.integer.value[0] = 4; + break; + case SAMPLING_RATE_48KHZ: + ucontrol->value.integer.value[0] = 3; + break; + case SAMPLING_RATE_44P1KHZ: + ucontrol->value.integer.value[0] = 2; + break; + case SAMPLING_RATE_16KHZ: + ucontrol->value.integer.value[0] = 1; + break; + case SAMPLING_RATE_8KHZ: + default: + ucontrol->value.integer.value[0] = 0; + break; + } + pr_debug("%s: sample rate rx = %d", __func__, + slim_rx_cfg[SLIM_RX_7].sample_rate); + + return 0; +} + +static int msm_bt_sample_rate_rx_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (ucontrol->value.integer.value[0]) { + case 1: + slim_rx_cfg[SLIM_RX_7].sample_rate = SAMPLING_RATE_16KHZ; + break; + case 2: + slim_rx_cfg[SLIM_RX_7].sample_rate = SAMPLING_RATE_44P1KHZ; + break; + case 3: + slim_rx_cfg[SLIM_RX_7].sample_rate = SAMPLING_RATE_48KHZ; + break; + case 4: + slim_rx_cfg[SLIM_RX_7].sample_rate = SAMPLING_RATE_88P2KHZ; + break; + case 5: + slim_rx_cfg[SLIM_RX_7].sample_rate = SAMPLING_RATE_96KHZ; + break; + case 0: + default: + slim_rx_cfg[SLIM_RX_7].sample_rate = SAMPLING_RATE_8KHZ; + break; + } + pr_debug("%s: sample rate: slim7_rx = %d, value = %d\n", + __func__, + slim_rx_cfg[SLIM_RX_7].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int msm_bt_sample_rate_tx_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (slim_tx_cfg[SLIM_TX_7].sample_rate) { + case SAMPLING_RATE_96KHZ: + ucontrol->value.integer.value[0] = 5; + break; + case SAMPLING_RATE_88P2KHZ: + ucontrol->value.integer.value[0] = 4; + break; + case SAMPLING_RATE_48KHZ: + ucontrol->value.integer.value[0] = 3; + break; + case SAMPLING_RATE_44P1KHZ: + ucontrol->value.integer.value[0] = 2; + break; + case SAMPLING_RATE_16KHZ: + ucontrol->value.integer.value[0] = 1; + break; + case SAMPLING_RATE_8KHZ: + default: + ucontrol->value.integer.value[0] = 0; + break; + } + pr_debug("%s: sample rate tx = %d", __func__, + slim_tx_cfg[SLIM_TX_7].sample_rate); + + return 0; +} + +static int msm_bt_sample_rate_tx_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (ucontrol->value.integer.value[0]) { + case 1: + slim_tx_cfg[SLIM_TX_7].sample_rate = SAMPLING_RATE_16KHZ; + break; + case 2: + slim_tx_cfg[SLIM_TX_7].sample_rate = SAMPLING_RATE_44P1KHZ; + break; + case 3: + slim_tx_cfg[SLIM_TX_7].sample_rate = SAMPLING_RATE_48KHZ; + break; + case 4: + slim_tx_cfg[SLIM_TX_7].sample_rate = SAMPLING_RATE_88P2KHZ; + break; + case 5: + slim_tx_cfg[SLIM_TX_7].sample_rate = SAMPLING_RATE_96KHZ; + break; + case 0: + default: + slim_tx_cfg[SLIM_TX_7].sample_rate = SAMPLING_RATE_8KHZ; + break; + } + pr_debug("%s: sample rate: slim7_tx = %d, value = %d\n", + __func__, + slim_tx_cfg[SLIM_TX_7].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} +static int cdc_dma_get_port_idx(struct snd_kcontrol *kcontrol) +{ + int idx = 0; + + if (strnstr(kcontrol->id.name, "WSA_CDC_DMA_RX_0", + sizeof("WSA_CDC_DMA_RX_0"))) + idx = WSA_CDC_DMA_RX_0; + else if (strnstr(kcontrol->id.name, "WSA_CDC_DMA_RX_1", + sizeof("WSA_CDC_DMA_RX_0"))) + idx = WSA_CDC_DMA_RX_1; + else if (strnstr(kcontrol->id.name, "RX_CDC_DMA_RX_0", + sizeof("RX_CDC_DMA_RX_0"))) + idx = RX_CDC_DMA_RX_0; + else if (strnstr(kcontrol->id.name, "RX_CDC_DMA_RX_1", + sizeof("RX_CDC_DMA_RX_1"))) + idx = RX_CDC_DMA_RX_1; + else if (strnstr(kcontrol->id.name, "RX_CDC_DMA_RX_2", + sizeof("RX_CDC_DMA_RX_2"))) + idx = RX_CDC_DMA_RX_2; + else if (strnstr(kcontrol->id.name, "RX_CDC_DMA_RX_3", + sizeof("RX_CDC_DMA_RX_3"))) + idx = RX_CDC_DMA_RX_3; + else if (strnstr(kcontrol->id.name, "RX_CDC_DMA_RX_5", + sizeof("RX_CDC_DMA_RX_5"))) + idx = RX_CDC_DMA_RX_5; + else if (strnstr(kcontrol->id.name, "WSA_CDC_DMA_TX_0", + sizeof("WSA_CDC_DMA_TX_0"))) + idx = WSA_CDC_DMA_TX_0; + else if (strnstr(kcontrol->id.name, "WSA_CDC_DMA_TX_1", + sizeof("WSA_CDC_DMA_TX_1"))) + idx = WSA_CDC_DMA_TX_1; + else if (strnstr(kcontrol->id.name, "WSA_CDC_DMA_TX_2", + sizeof("WSA_CDC_DMA_TX_2"))) + idx = WSA_CDC_DMA_TX_2; + else if (strnstr(kcontrol->id.name, "TX_CDC_DMA_TX_0", + sizeof("TX_CDC_DMA_TX_0"))) + idx = TX_CDC_DMA_TX_0; + else if (strnstr(kcontrol->id.name, "TX_CDC_DMA_TX_3", + sizeof("TX_CDC_DMA_TX_3"))) + idx = TX_CDC_DMA_TX_3; + else if (strnstr(kcontrol->id.name, "TX_CDC_DMA_TX_4", + sizeof("TX_CDC_DMA_TX_4"))) + idx = TX_CDC_DMA_TX_4; + else { + pr_err("%s: unsupported channel: %s\n", + __func__, kcontrol->id.name); + return -EINVAL; + } + + return idx; +} + +static int cdc_dma_rx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = cdc_dma_get_port_idx(kcontrol); + + if (ch_num < 0) { + pr_err("%s: ch_num: %d is invalid\n", __func__, ch_num); + return ch_num; + } + + pr_debug("%s: cdc_dma_rx_ch = %d\n", __func__, + cdc_dma_rx_cfg[ch_num].channels - 1); + ucontrol->value.integer.value[0] = cdc_dma_rx_cfg[ch_num].channels - 1; + return 0; +} + +static int cdc_dma_rx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = cdc_dma_get_port_idx(kcontrol); + + if (ch_num < 0) { + pr_err("%s: ch_num: %d is invalid\n", __func__, ch_num); + return ch_num; + } + + cdc_dma_rx_cfg[ch_num].channels = ucontrol->value.integer.value[0] + 1; + + pr_debug("%s: cdc_dma_rx_ch = %d\n", __func__, + cdc_dma_rx_cfg[ch_num].channels); + return 1; +} + +static int cdc_dma_rx_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = cdc_dma_get_port_idx(kcontrol); + + if (ch_num < 0) { + pr_err("%s: ch_num: %d is invalid\n", __func__, ch_num); + return ch_num; + } + + switch (cdc_dma_rx_cfg[ch_num].bit_format) { + case SNDRV_PCM_FORMAT_S32_LE: + ucontrol->value.integer.value[0] = 3; + break; + case SNDRV_PCM_FORMAT_S24_3LE: + ucontrol->value.integer.value[0] = 2; + break; + case SNDRV_PCM_FORMAT_S24_LE: + ucontrol->value.integer.value[0] = 1; + break; + case SNDRV_PCM_FORMAT_S16_LE: + default: + ucontrol->value.integer.value[0] = 0; + break; + } + + pr_debug("%s: cdc_dma_rx_format = %d, ucontrol value = %ld\n", + __func__, cdc_dma_rx_cfg[ch_num].bit_format, + ucontrol->value.integer.value[0]); + return 0; +} + +static int cdc_dma_rx_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int rc = 0; + int ch_num = cdc_dma_get_port_idx(kcontrol); + + if (ch_num < 0) { + pr_err("%s: ch_num: %d is invalid\n", __func__, ch_num); + return ch_num; + } + + switch (ucontrol->value.integer.value[0]) { + case 3: + cdc_dma_rx_cfg[ch_num].bit_format = SNDRV_PCM_FORMAT_S32_LE; + break; + case 2: + cdc_dma_rx_cfg[ch_num].bit_format = SNDRV_PCM_FORMAT_S24_3LE; + break; + case 1: + cdc_dma_rx_cfg[ch_num].bit_format = SNDRV_PCM_FORMAT_S24_LE; + break; + case 0: + default: + cdc_dma_rx_cfg[ch_num].bit_format = SNDRV_PCM_FORMAT_S16_LE; + break; + } + pr_debug("%s: cdc_dma_rx_format = %d, ucontrol value = %ld\n", + __func__, cdc_dma_rx_cfg[ch_num].bit_format, + ucontrol->value.integer.value[0]); + + return rc; +} + + +static int cdc_dma_get_sample_rate_val(int sample_rate) +{ + int sample_rate_val = 0; + + switch (sample_rate) { + case SAMPLING_RATE_8KHZ: + sample_rate_val = 0; + break; + case SAMPLING_RATE_11P025KHZ: + sample_rate_val = 1; + break; + case SAMPLING_RATE_16KHZ: + sample_rate_val = 2; + break; + case SAMPLING_RATE_22P05KHZ: + sample_rate_val = 3; + break; + case SAMPLING_RATE_32KHZ: + sample_rate_val = 4; + break; + case SAMPLING_RATE_44P1KHZ: + sample_rate_val = 5; + break; + case SAMPLING_RATE_48KHZ: + sample_rate_val = 6; + break; + case SAMPLING_RATE_88P2KHZ: + sample_rate_val = 7; + break; + case SAMPLING_RATE_96KHZ: + sample_rate_val = 8; + break; + case SAMPLING_RATE_176P4KHZ: + sample_rate_val = 9; + break; + case SAMPLING_RATE_192KHZ: + sample_rate_val = 10; + break; + case SAMPLING_RATE_352P8KHZ: + sample_rate_val = 11; + break; + case SAMPLING_RATE_384KHZ: + sample_rate_val = 12; + break; + default: + sample_rate_val = 6; + break; + } + return sample_rate_val; +} + +static int cdc_dma_get_sample_rate(int value) +{ + int sample_rate = 0; + + switch (value) { + case 0: + sample_rate = SAMPLING_RATE_8KHZ; + break; + case 1: + sample_rate = SAMPLING_RATE_11P025KHZ; + break; + case 2: + sample_rate = SAMPLING_RATE_16KHZ; + break; + case 3: + sample_rate = SAMPLING_RATE_22P05KHZ; + break; + case 4: + sample_rate = SAMPLING_RATE_32KHZ; + break; + case 5: + sample_rate = SAMPLING_RATE_44P1KHZ; + break; + case 6: + sample_rate = SAMPLING_RATE_48KHZ; + break; + case 7: + sample_rate = SAMPLING_RATE_88P2KHZ; + break; + case 8: + sample_rate = SAMPLING_RATE_96KHZ; + break; + case 9: + sample_rate = SAMPLING_RATE_176P4KHZ; + break; + case 10: + sample_rate = SAMPLING_RATE_192KHZ; + break; + case 11: + sample_rate = SAMPLING_RATE_352P8KHZ; + break; + case 12: + sample_rate = SAMPLING_RATE_384KHZ; + break; + default: + sample_rate = SAMPLING_RATE_48KHZ; + break; + } + return sample_rate; +} + +static int cdc_dma_rx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = cdc_dma_get_port_idx(kcontrol); + + if (ch_num < 0) { + pr_err("%s: ch_num: %d is invalid\n", __func__, ch_num); + return ch_num; + } + + ucontrol->value.enumerated.item[0] = + cdc_dma_get_sample_rate_val(cdc_dma_rx_cfg[ch_num].sample_rate); + + pr_debug("%s: cdc_dma_rx_sample_rate = %d\n", __func__, + cdc_dma_rx_cfg[ch_num].sample_rate); + return 0; +} + +static int cdc_dma_rx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = cdc_dma_get_port_idx(kcontrol); + + if (ch_num < 0) { + pr_err("%s: ch_num: %d is invalid\n", __func__, ch_num); + return ch_num; + } + + cdc_dma_rx_cfg[ch_num].sample_rate = + cdc_dma_get_sample_rate(ucontrol->value.enumerated.item[0]); + + + pr_debug("%s: control value = %d, cdc_dma_rx_sample_rate = %d\n", + __func__, ucontrol->value.enumerated.item[0], + cdc_dma_rx_cfg[ch_num].sample_rate); + return 0; +} + +static int cdc_dma_tx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = cdc_dma_get_port_idx(kcontrol); + + if (ch_num < 0) { + pr_err("%s: ch_num: %d is invalid\n", __func__, ch_num); + return ch_num; + } + + pr_debug("%s: cdc_dma_tx_ch = %d\n", __func__, + cdc_dma_tx_cfg[ch_num].channels); + ucontrol->value.integer.value[0] = cdc_dma_tx_cfg[ch_num].channels - 1; + return 0; +} + +static int cdc_dma_tx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = cdc_dma_get_port_idx(kcontrol); + + if (ch_num < 0) { + pr_err("%s: ch_num: %d is invalid\n", __func__, ch_num); + return ch_num; + } + + cdc_dma_tx_cfg[ch_num].channels = ucontrol->value.integer.value[0] + 1; + + pr_debug("%s: cdc_dma_tx_ch = %d\n", __func__, + cdc_dma_tx_cfg[ch_num].channels); + return 1; +} + +static int cdc_dma_tx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int sample_rate_val; + int ch_num = cdc_dma_get_port_idx(kcontrol); + + if (ch_num < 0) { + pr_err("%s: ch_num: %d is invalid\n", __func__, ch_num); + return ch_num; + } + + switch (cdc_dma_tx_cfg[ch_num].sample_rate) { + case SAMPLING_RATE_384KHZ: + sample_rate_val = 12; + break; + case SAMPLING_RATE_352P8KHZ: + sample_rate_val = 11; + break; + case SAMPLING_RATE_192KHZ: + sample_rate_val = 10; + break; + case SAMPLING_RATE_176P4KHZ: + sample_rate_val = 9; + break; + case SAMPLING_RATE_96KHZ: + sample_rate_val = 8; + break; + case SAMPLING_RATE_88P2KHZ: + sample_rate_val = 7; + break; + case SAMPLING_RATE_48KHZ: + sample_rate_val = 6; + break; + case SAMPLING_RATE_44P1KHZ: + sample_rate_val = 5; + break; + case SAMPLING_RATE_32KHZ: + sample_rate_val = 4; + break; + case SAMPLING_RATE_22P05KHZ: + sample_rate_val = 3; + break; + case SAMPLING_RATE_16KHZ: + sample_rate_val = 2; + break; + case SAMPLING_RATE_11P025KHZ: + sample_rate_val = 1; + break; + case SAMPLING_RATE_8KHZ: + sample_rate_val = 0; + break; + default: + sample_rate_val = 6; + break; + } + + ucontrol->value.integer.value[0] = sample_rate_val; + pr_debug("%s: cdc_dma_tx_sample_rate = %d\n", __func__, + cdc_dma_tx_cfg[ch_num].sample_rate); + return 0; +} + +static int cdc_dma_tx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = cdc_dma_get_port_idx(kcontrol); + + if (ch_num < 0) { + pr_err("%s: ch_num: %d is invalid\n", __func__, ch_num); + return ch_num; + } + + switch (ucontrol->value.integer.value[0]) { + case 12: + cdc_dma_tx_cfg[ch_num].sample_rate = SAMPLING_RATE_384KHZ; + break; + case 11: + cdc_dma_tx_cfg[ch_num].sample_rate = SAMPLING_RATE_352P8KHZ; + break; + case 10: + cdc_dma_tx_cfg[ch_num].sample_rate = SAMPLING_RATE_192KHZ; + break; + case 9: + cdc_dma_tx_cfg[ch_num].sample_rate = SAMPLING_RATE_176P4KHZ; + break; + case 8: + cdc_dma_tx_cfg[ch_num].sample_rate = SAMPLING_RATE_96KHZ; + break; + case 7: + cdc_dma_tx_cfg[ch_num].sample_rate = SAMPLING_RATE_88P2KHZ; + break; + case 6: + cdc_dma_tx_cfg[ch_num].sample_rate = SAMPLING_RATE_48KHZ; + break; + case 5: + cdc_dma_tx_cfg[ch_num].sample_rate = SAMPLING_RATE_44P1KHZ; + break; + case 4: + cdc_dma_tx_cfg[ch_num].sample_rate = SAMPLING_RATE_32KHZ; + break; + case 3: + cdc_dma_tx_cfg[ch_num].sample_rate = SAMPLING_RATE_22P05KHZ; + break; + case 2: + cdc_dma_tx_cfg[ch_num].sample_rate = SAMPLING_RATE_16KHZ; + break; + case 1: + cdc_dma_tx_cfg[ch_num].sample_rate = SAMPLING_RATE_11P025KHZ; + break; + case 0: + cdc_dma_tx_cfg[ch_num].sample_rate = SAMPLING_RATE_8KHZ; + break; + default: + cdc_dma_tx_cfg[ch_num].sample_rate = SAMPLING_RATE_48KHZ; + break; + } + + pr_debug("%s: control value = %ld, cdc_dma_tx_sample_rate = %d\n", + __func__, ucontrol->value.integer.value[0], + cdc_dma_tx_cfg[ch_num].sample_rate); + return 0; +} + +static int cdc_dma_tx_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = cdc_dma_get_port_idx(kcontrol); + + if (ch_num < 0) { + pr_err("%s: ch_num: %d is invalid\n", __func__, ch_num); + return ch_num; + } + + switch (cdc_dma_tx_cfg[ch_num].bit_format) { + case SNDRV_PCM_FORMAT_S32_LE: + ucontrol->value.integer.value[0] = 3; + break; + case SNDRV_PCM_FORMAT_S24_3LE: + ucontrol->value.integer.value[0] = 2; + break; + case SNDRV_PCM_FORMAT_S24_LE: + ucontrol->value.integer.value[0] = 1; + break; + case SNDRV_PCM_FORMAT_S16_LE: + default: + ucontrol->value.integer.value[0] = 0; + break; + } + + pr_debug("%s: cdc_dma_tx_format = %d, ucontrol value = %ld\n", + __func__, cdc_dma_tx_cfg[ch_num].bit_format, + ucontrol->value.integer.value[0]); + return 0; +} + +static int cdc_dma_tx_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int rc = 0; + int ch_num = cdc_dma_get_port_idx(kcontrol); + + if (ch_num < 0) { + pr_err("%s: ch_num: %d is invalid\n", __func__, ch_num); + return ch_num; + } + + switch (ucontrol->value.integer.value[0]) { + case 3: + cdc_dma_tx_cfg[ch_num].bit_format = SNDRV_PCM_FORMAT_S32_LE; + break; + case 2: + cdc_dma_tx_cfg[ch_num].bit_format = SNDRV_PCM_FORMAT_S24_3LE; + break; + case 1: + cdc_dma_tx_cfg[ch_num].bit_format = SNDRV_PCM_FORMAT_S24_LE; + break; + case 0: + default: + cdc_dma_tx_cfg[ch_num].bit_format = SNDRV_PCM_FORMAT_S16_LE; + break; + } + pr_debug("%s: cdc_dma_tx_format = %d, ucontrol value = %ld\n", + __func__, cdc_dma_tx_cfg[ch_num].bit_format, + ucontrol->value.integer.value[0]); + + return rc; +} + +static int usb_audio_rx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s: usb_audio_rx_ch = %d\n", __func__, + usb_rx_cfg.channels); + ucontrol->value.integer.value[0] = usb_rx_cfg.channels - 1; + return 0; +} + +static int usb_audio_rx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + usb_rx_cfg.channels = ucontrol->value.integer.value[0] + 1; + + pr_debug("%s: usb_audio_rx_ch = %d\n", __func__, usb_rx_cfg.channels); + return 1; +} + +static int usb_audio_rx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int sample_rate_val; + + switch (usb_rx_cfg.sample_rate) { + case SAMPLING_RATE_384KHZ: + sample_rate_val = 12; + break; + case SAMPLING_RATE_352P8KHZ: + sample_rate_val = 11; + break; + case SAMPLING_RATE_192KHZ: + sample_rate_val = 10; + break; + case SAMPLING_RATE_176P4KHZ: + sample_rate_val = 9; + break; + case SAMPLING_RATE_96KHZ: + sample_rate_val = 8; + break; + case SAMPLING_RATE_88P2KHZ: + sample_rate_val = 7; + break; + case SAMPLING_RATE_48KHZ: + sample_rate_val = 6; + break; + case SAMPLING_RATE_44P1KHZ: + sample_rate_val = 5; + break; + case SAMPLING_RATE_32KHZ: + sample_rate_val = 4; + break; + case SAMPLING_RATE_22P05KHZ: + sample_rate_val = 3; + break; + case SAMPLING_RATE_16KHZ: + sample_rate_val = 2; + break; + case SAMPLING_RATE_11P025KHZ: + sample_rate_val = 1; + break; + case SAMPLING_RATE_8KHZ: + default: + sample_rate_val = 0; + break; + } + + ucontrol->value.integer.value[0] = sample_rate_val; + pr_debug("%s: usb_audio_rx_sample_rate = %d\n", __func__, + usb_rx_cfg.sample_rate); + return 0; +} + +static int usb_audio_rx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (ucontrol->value.integer.value[0]) { + case 12: + usb_rx_cfg.sample_rate = SAMPLING_RATE_384KHZ; + break; + case 11: + usb_rx_cfg.sample_rate = SAMPLING_RATE_352P8KHZ; + break; + case 10: + usb_rx_cfg.sample_rate = SAMPLING_RATE_192KHZ; + break; + case 9: + usb_rx_cfg.sample_rate = SAMPLING_RATE_176P4KHZ; + break; + case 8: + usb_rx_cfg.sample_rate = SAMPLING_RATE_96KHZ; + break; + case 7: + usb_rx_cfg.sample_rate = SAMPLING_RATE_88P2KHZ; + break; + case 6: + usb_rx_cfg.sample_rate = SAMPLING_RATE_48KHZ; + break; + case 5: + usb_rx_cfg.sample_rate = SAMPLING_RATE_44P1KHZ; + break; + case 4: + usb_rx_cfg.sample_rate = SAMPLING_RATE_32KHZ; + break; + case 3: + usb_rx_cfg.sample_rate = SAMPLING_RATE_22P05KHZ; + break; + case 2: + usb_rx_cfg.sample_rate = SAMPLING_RATE_16KHZ; + break; + case 1: + usb_rx_cfg.sample_rate = SAMPLING_RATE_11P025KHZ; + break; + case 0: + usb_rx_cfg.sample_rate = SAMPLING_RATE_8KHZ; + break; + default: + usb_rx_cfg.sample_rate = SAMPLING_RATE_48KHZ; + break; + } + + pr_debug("%s: control value = %ld, usb_audio_rx_sample_rate = %d\n", + __func__, ucontrol->value.integer.value[0], + usb_rx_cfg.sample_rate); + return 0; +} + +static int usb_audio_rx_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (usb_rx_cfg.bit_format) { + case SNDRV_PCM_FORMAT_S32_LE: + ucontrol->value.integer.value[0] = 3; + break; + case SNDRV_PCM_FORMAT_S24_3LE: + ucontrol->value.integer.value[0] = 2; + break; + case SNDRV_PCM_FORMAT_S24_LE: + ucontrol->value.integer.value[0] = 1; + break; + case SNDRV_PCM_FORMAT_S16_LE: + default: + ucontrol->value.integer.value[0] = 0; + break; + } + + pr_debug("%s: usb_audio_rx_format = %d, ucontrol value = %ld\n", + __func__, usb_rx_cfg.bit_format, + ucontrol->value.integer.value[0]); + return 0; +} + +static int usb_audio_rx_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int rc = 0; + + switch (ucontrol->value.integer.value[0]) { + case 3: + usb_rx_cfg.bit_format = SNDRV_PCM_FORMAT_S32_LE; + break; + case 2: + usb_rx_cfg.bit_format = SNDRV_PCM_FORMAT_S24_3LE; + break; + case 1: + usb_rx_cfg.bit_format = SNDRV_PCM_FORMAT_S24_LE; + break; + case 0: + default: + usb_rx_cfg.bit_format = SNDRV_PCM_FORMAT_S16_LE; + break; + } + pr_debug("%s: usb_audio_rx_format = %d, ucontrol value = %ld\n", + __func__, usb_rx_cfg.bit_format, + ucontrol->value.integer.value[0]); + + return rc; +} + +static int usb_audio_tx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s: usb_audio_tx_ch = %d\n", __func__, + usb_tx_cfg.channels); + ucontrol->value.integer.value[0] = usb_tx_cfg.channels - 1; + return 0; +} + +static int usb_audio_tx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + usb_tx_cfg.channels = ucontrol->value.integer.value[0] + 1; + + pr_debug("%s: usb_audio_tx_ch = %d\n", __func__, usb_tx_cfg.channels); + return 1; +} + +static int usb_audio_tx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int sample_rate_val; + + switch (usb_tx_cfg.sample_rate) { + case SAMPLING_RATE_384KHZ: + sample_rate_val = 12; + break; + case SAMPLING_RATE_352P8KHZ: + sample_rate_val = 11; + break; + case SAMPLING_RATE_192KHZ: + sample_rate_val = 10; + break; + case SAMPLING_RATE_176P4KHZ: + sample_rate_val = 9; + break; + case SAMPLING_RATE_96KHZ: + sample_rate_val = 8; + break; + case SAMPLING_RATE_88P2KHZ: + sample_rate_val = 7; + break; + case SAMPLING_RATE_48KHZ: + sample_rate_val = 6; + break; + case SAMPLING_RATE_44P1KHZ: + sample_rate_val = 5; + break; + case SAMPLING_RATE_32KHZ: + sample_rate_val = 4; + break; + case SAMPLING_RATE_22P05KHZ: + sample_rate_val = 3; + break; + case SAMPLING_RATE_16KHZ: + sample_rate_val = 2; + break; + case SAMPLING_RATE_11P025KHZ: + sample_rate_val = 1; + break; + case SAMPLING_RATE_8KHZ: + sample_rate_val = 0; + break; + default: + sample_rate_val = 6; + break; + } + + ucontrol->value.integer.value[0] = sample_rate_val; + pr_debug("%s: usb_audio_tx_sample_rate = %d\n", __func__, + usb_tx_cfg.sample_rate); + return 0; +} + +static int usb_audio_tx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (ucontrol->value.integer.value[0]) { + case 12: + usb_tx_cfg.sample_rate = SAMPLING_RATE_384KHZ; + break; + case 11: + usb_tx_cfg.sample_rate = SAMPLING_RATE_352P8KHZ; + break; + case 10: + usb_tx_cfg.sample_rate = SAMPLING_RATE_192KHZ; + break; + case 9: + usb_tx_cfg.sample_rate = SAMPLING_RATE_176P4KHZ; + break; + case 8: + usb_tx_cfg.sample_rate = SAMPLING_RATE_96KHZ; + break; + case 7: + usb_tx_cfg.sample_rate = SAMPLING_RATE_88P2KHZ; + break; + case 6: + usb_tx_cfg.sample_rate = SAMPLING_RATE_48KHZ; + break; + case 5: + usb_tx_cfg.sample_rate = SAMPLING_RATE_44P1KHZ; + break; + case 4: + usb_tx_cfg.sample_rate = SAMPLING_RATE_32KHZ; + break; + case 3: + usb_tx_cfg.sample_rate = SAMPLING_RATE_22P05KHZ; + break; + case 2: + usb_tx_cfg.sample_rate = SAMPLING_RATE_16KHZ; + break; + case 1: + usb_tx_cfg.sample_rate = SAMPLING_RATE_11P025KHZ; + break; + case 0: + usb_tx_cfg.sample_rate = SAMPLING_RATE_8KHZ; + break; + default: + usb_tx_cfg.sample_rate = SAMPLING_RATE_48KHZ; + break; + } + + pr_debug("%s: control value = %ld, usb_audio_tx_sample_rate = %d\n", + __func__, ucontrol->value.integer.value[0], + usb_tx_cfg.sample_rate); + return 0; +} + +static int usb_audio_tx_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (usb_tx_cfg.bit_format) { + case SNDRV_PCM_FORMAT_S32_LE: + ucontrol->value.integer.value[0] = 3; + break; + case SNDRV_PCM_FORMAT_S24_3LE: + ucontrol->value.integer.value[0] = 2; + break; + case SNDRV_PCM_FORMAT_S24_LE: + ucontrol->value.integer.value[0] = 1; + break; + case SNDRV_PCM_FORMAT_S16_LE: + default: + ucontrol->value.integer.value[0] = 0; + break; + } + + pr_debug("%s: usb_audio_tx_format = %d, ucontrol value = %ld\n", + __func__, usb_tx_cfg.bit_format, + ucontrol->value.integer.value[0]); + return 0; +} + +static int usb_audio_tx_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int rc = 0; + + switch (ucontrol->value.integer.value[0]) { + case 3: + usb_tx_cfg.bit_format = SNDRV_PCM_FORMAT_S32_LE; + break; + case 2: + usb_tx_cfg.bit_format = SNDRV_PCM_FORMAT_S24_3LE; + break; + case 1: + usb_tx_cfg.bit_format = SNDRV_PCM_FORMAT_S24_LE; + break; + case 0: + default: + usb_tx_cfg.bit_format = SNDRV_PCM_FORMAT_S16_LE; + break; + } + pr_debug("%s: usb_audio_tx_format = %d, ucontrol value = %ld\n", + __func__, usb_tx_cfg.bit_format, + ucontrol->value.integer.value[0]); + + return rc; +} + +static int ext_disp_get_port_idx(struct snd_kcontrol *kcontrol) +{ + int idx; + + if (strnstr(kcontrol->id.name, "Display Port RX", + sizeof("Display Port RX"))) { + idx = DP_RX_IDX; + } else { + pr_err("%s: unsupported BE: %s\n", + __func__, kcontrol->id.name); + idx = -EINVAL; + } + + return idx; +} + +static int ext_disp_rx_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = ext_disp_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + switch (ext_disp_rx_cfg[idx].bit_format) { + case SNDRV_PCM_FORMAT_S24_3LE: + ucontrol->value.integer.value[0] = 2; + break; + case SNDRV_PCM_FORMAT_S24_LE: + ucontrol->value.integer.value[0] = 1; + break; + case SNDRV_PCM_FORMAT_S16_LE: + default: + ucontrol->value.integer.value[0] = 0; + break; + } + + pr_debug("%s: ext_disp_rx[%d].format = %d, ucontrol value = %ld\n", + __func__, idx, ext_disp_rx_cfg[idx].bit_format, + ucontrol->value.integer.value[0]); + return 0; +} + +static int ext_disp_rx_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = ext_disp_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + switch (ucontrol->value.integer.value[0]) { + case 2: + ext_disp_rx_cfg[idx].bit_format = SNDRV_PCM_FORMAT_S24_3LE; + break; + case 1: + ext_disp_rx_cfg[idx].bit_format = SNDRV_PCM_FORMAT_S24_LE; + break; + case 0: + default: + ext_disp_rx_cfg[idx].bit_format = SNDRV_PCM_FORMAT_S16_LE; + break; + } + pr_debug("%s: ext_disp_rx[%d].format = %d, ucontrol value = %ld\n", + __func__, idx, ext_disp_rx_cfg[idx].bit_format, + ucontrol->value.integer.value[0]); + + return 0; +} + +static int ext_disp_rx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = ext_disp_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + ucontrol->value.integer.value[0] = + ext_disp_rx_cfg[idx].channels - 2; + + pr_debug("%s: ext_disp_rx[%d].ch = %d\n", __func__, + idx, ext_disp_rx_cfg[idx].channels); + + return 0; +} + +static int ext_disp_rx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = ext_disp_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + ext_disp_rx_cfg[idx].channels = + ucontrol->value.integer.value[0] + 2; + + pr_debug("%s: ext_disp_rx[%d].ch = %d\n", __func__, + idx, ext_disp_rx_cfg[idx].channels); + return 1; +} + +static int ext_disp_rx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int sample_rate_val; + int idx = ext_disp_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + switch (ext_disp_rx_cfg[idx].sample_rate) { + case SAMPLING_RATE_176P4KHZ: + sample_rate_val = 6; + break; + + case SAMPLING_RATE_88P2KHZ: + sample_rate_val = 5; + break; + + case SAMPLING_RATE_44P1KHZ: + sample_rate_val = 4; + break; + + case SAMPLING_RATE_32KHZ: + sample_rate_val = 3; + break; + + case SAMPLING_RATE_192KHZ: + sample_rate_val = 2; + break; + + case SAMPLING_RATE_96KHZ: + sample_rate_val = 1; + break; + + case SAMPLING_RATE_48KHZ: + default: + sample_rate_val = 0; + break; + } + + ucontrol->value.integer.value[0] = sample_rate_val; + pr_debug("%s: ext_disp_rx[%d].sample_rate = %d\n", __func__, + idx, ext_disp_rx_cfg[idx].sample_rate); + + return 0; +} + +static int ext_disp_rx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = ext_disp_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + switch (ucontrol->value.integer.value[0]) { + case 6: + ext_disp_rx_cfg[idx].sample_rate = SAMPLING_RATE_176P4KHZ; + break; + case 5: + ext_disp_rx_cfg[idx].sample_rate = SAMPLING_RATE_88P2KHZ; + break; + case 4: + ext_disp_rx_cfg[idx].sample_rate = SAMPLING_RATE_44P1KHZ; + break; + case 3: + ext_disp_rx_cfg[idx].sample_rate = SAMPLING_RATE_32KHZ; + break; + case 2: + ext_disp_rx_cfg[idx].sample_rate = SAMPLING_RATE_192KHZ; + break; + case 1: + ext_disp_rx_cfg[idx].sample_rate = SAMPLING_RATE_96KHZ; + break; + case 0: + default: + ext_disp_rx_cfg[idx].sample_rate = SAMPLING_RATE_48KHZ; + break; + } + + pr_debug("%s: control value = %ld, ext_disp_rx[%d].sample_rate = %d\n", + __func__, ucontrol->value.integer.value[0], idx, + ext_disp_rx_cfg[idx].sample_rate); + return 0; +} + +static int proxy_rx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s: proxy_rx channels = %d\n", + __func__, proxy_rx_cfg.channels); + ucontrol->value.integer.value[0] = proxy_rx_cfg.channels - 2; + + return 0; +} + +static int proxy_rx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + proxy_rx_cfg.channels = ucontrol->value.integer.value[0] + 2; + pr_debug("%s: proxy_rx channels = %d\n", + __func__, proxy_rx_cfg.channels); + + return 1; +} + +static int tdm_get_sample_rate(int value) +{ + int sample_rate = 0; + + switch (value) { + case 0: + sample_rate = SAMPLING_RATE_8KHZ; + break; + case 1: + sample_rate = SAMPLING_RATE_16KHZ; + break; + case 2: + sample_rate = SAMPLING_RATE_32KHZ; + break; + case 3: + sample_rate = SAMPLING_RATE_48KHZ; + break; + case 4: + sample_rate = SAMPLING_RATE_176P4KHZ; + break; + case 5: + sample_rate = SAMPLING_RATE_352P8KHZ; + break; + default: + sample_rate = SAMPLING_RATE_48KHZ; + break; + } + return sample_rate; +} + +static int aux_pcm_get_sample_rate(int value) +{ + int sample_rate; + + switch (value) { + case 1: + sample_rate = SAMPLING_RATE_16KHZ; + break; + case 0: + default: + sample_rate = SAMPLING_RATE_8KHZ; + break; + } + return sample_rate; +} + +static int tdm_get_sample_rate_val(int sample_rate) +{ + int sample_rate_val = 0; + + switch (sample_rate) { + case SAMPLING_RATE_8KHZ: + sample_rate_val = 0; + break; + case SAMPLING_RATE_16KHZ: + sample_rate_val = 1; + break; + case SAMPLING_RATE_32KHZ: + sample_rate_val = 2; + break; + case SAMPLING_RATE_48KHZ: + sample_rate_val = 3; + break; + case SAMPLING_RATE_176P4KHZ: + sample_rate_val = 4; + break; + case SAMPLING_RATE_352P8KHZ: + sample_rate_val = 5; + break; + default: + sample_rate_val = 3; + break; + } + return sample_rate_val; +} + +static int aux_pcm_get_sample_rate_val(int sample_rate) +{ + int sample_rate_val; + + switch (sample_rate) { + case SAMPLING_RATE_16KHZ: + sample_rate_val = 1; + break; + case SAMPLING_RATE_8KHZ: + default: + sample_rate_val = 0; + break; + } + return sample_rate_val; +} + +static int tdm_get_port_idx(struct snd_kcontrol *kcontrol, + struct tdm_port *port) +{ + if (port) { + if (strnstr(kcontrol->id.name, "PRI", + sizeof(kcontrol->id.name))) { + port->mode = TDM_PRI; + } else if (strnstr(kcontrol->id.name, "SEC", + sizeof(kcontrol->id.name))) { + port->mode = TDM_SEC; + } else if (strnstr(kcontrol->id.name, "TERT", + sizeof(kcontrol->id.name))) { + port->mode = TDM_TERT; + } else if (strnstr(kcontrol->id.name, "QUAT", + sizeof(kcontrol->id.name))) { + port->mode = TDM_QUAT; + } else if (strnstr(kcontrol->id.name, "QUIN", + sizeof(kcontrol->id.name))) { + port->mode = TDM_QUIN; + } else { + pr_err("%s: unsupported mode in: %s\n", + __func__, kcontrol->id.name); + return -EINVAL; + } + + if (strnstr(kcontrol->id.name, "RX_0", + sizeof(kcontrol->id.name)) || + strnstr(kcontrol->id.name, "TX_0", + sizeof(kcontrol->id.name))) { + port->channel = TDM_0; + } else if (strnstr(kcontrol->id.name, "RX_1", + sizeof(kcontrol->id.name)) || + strnstr(kcontrol->id.name, "TX_1", + sizeof(kcontrol->id.name))) { + port->channel = TDM_1; + } else if (strnstr(kcontrol->id.name, "RX_2", + sizeof(kcontrol->id.name)) || + strnstr(kcontrol->id.name, "TX_2", + sizeof(kcontrol->id.name))) { + port->channel = TDM_2; + } else if (strnstr(kcontrol->id.name, "RX_3", + sizeof(kcontrol->id.name)) || + strnstr(kcontrol->id.name, "TX_3", + sizeof(kcontrol->id.name))) { + port->channel = TDM_3; + } else if (strnstr(kcontrol->id.name, "RX_4", + sizeof(kcontrol->id.name)) || + strnstr(kcontrol->id.name, "TX_4", + sizeof(kcontrol->id.name))) { + port->channel = TDM_4; + } else if (strnstr(kcontrol->id.name, "RX_5", + sizeof(kcontrol->id.name)) || + strnstr(kcontrol->id.name, "TX_5", + sizeof(kcontrol->id.name))) { + port->channel = TDM_5; + } else if (strnstr(kcontrol->id.name, "RX_6", + sizeof(kcontrol->id.name)) || + strnstr(kcontrol->id.name, "TX_6", + sizeof(kcontrol->id.name))) { + port->channel = TDM_6; + } else if (strnstr(kcontrol->id.name, "RX_7", + sizeof(kcontrol->id.name)) || + strnstr(kcontrol->id.name, "TX_7", + sizeof(kcontrol->id.name))) { + port->channel = TDM_7; + } else { + pr_err("%s: unsupported channel in: %s\n", + __func__, kcontrol->id.name); + return -EINVAL; + } + } else { + return -EINVAL; + } + return 0; +} + +static int tdm_rx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s\n", + __func__, kcontrol->id.name); + } else { + ucontrol->value.enumerated.item[0] = tdm_get_sample_rate_val( + tdm_rx_cfg[port.mode][port.channel].sample_rate); + + pr_debug("%s: tdm_rx_sample_rate = %d, item = %d\n", __func__, + tdm_rx_cfg[port.mode][port.channel].sample_rate, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_rx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s\n", + __func__, kcontrol->id.name); + } else { + tdm_rx_cfg[port.mode][port.channel].sample_rate = + tdm_get_sample_rate(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: tdm_rx_sample_rate = %d, item = %d\n", __func__, + tdm_rx_cfg[port.mode][port.channel].sample_rate, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_tx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s\n", + __func__, kcontrol->id.name); + } else { + ucontrol->value.enumerated.item[0] = tdm_get_sample_rate_val( + tdm_tx_cfg[port.mode][port.channel].sample_rate); + + pr_debug("%s: tdm_tx_sample_rate = %d, item = %d\n", __func__, + tdm_tx_cfg[port.mode][port.channel].sample_rate, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_tx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s\n", + __func__, kcontrol->id.name); + } else { + tdm_tx_cfg[port.mode][port.channel].sample_rate = + tdm_get_sample_rate(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: tdm_tx_sample_rate = %d, item = %d\n", __func__, + tdm_tx_cfg[port.mode][port.channel].sample_rate, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_get_format(int value) +{ + int format = 0; + + switch (value) { + case 0: + format = SNDRV_PCM_FORMAT_S16_LE; + break; + case 1: + format = SNDRV_PCM_FORMAT_S24_LE; + break; + case 2: + format = SNDRV_PCM_FORMAT_S32_LE; + break; + default: + format = SNDRV_PCM_FORMAT_S16_LE; + break; + } + return format; +} + +static int tdm_get_format_val(int format) +{ + int value = 0; + + switch (format) { + case SNDRV_PCM_FORMAT_S16_LE: + value = 0; + break; + case SNDRV_PCM_FORMAT_S24_LE: + value = 1; + break; + case SNDRV_PCM_FORMAT_S32_LE: + value = 2; + break; + default: + value = 0; + break; + } + return value; +} + +static int tdm_rx_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s\n", + __func__, kcontrol->id.name); + } else { + ucontrol->value.enumerated.item[0] = tdm_get_format_val( + tdm_rx_cfg[port.mode][port.channel].bit_format); + + pr_debug("%s: tdm_rx_bit_format = %d, item = %d\n", __func__, + tdm_rx_cfg[port.mode][port.channel].bit_format, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_rx_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s\n", + __func__, kcontrol->id.name); + } else { + tdm_rx_cfg[port.mode][port.channel].bit_format = + tdm_get_format(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: tdm_rx_bit_format = %d, item = %d\n", __func__, + tdm_rx_cfg[port.mode][port.channel].bit_format, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_tx_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s\n", + __func__, kcontrol->id.name); + } else { + ucontrol->value.enumerated.item[0] = tdm_get_format_val( + tdm_tx_cfg[port.mode][port.channel].bit_format); + + pr_debug("%s: tdm_tx_bit_format = %d, item = %d\n", __func__, + tdm_tx_cfg[port.mode][port.channel].bit_format, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_tx_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s\n", + __func__, kcontrol->id.name); + } else { + tdm_tx_cfg[port.mode][port.channel].bit_format = + tdm_get_format(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: tdm_tx_bit_format = %d, item = %d\n", __func__, + tdm_tx_cfg[port.mode][port.channel].bit_format, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_rx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s\n", + __func__, kcontrol->id.name); + } else { + + ucontrol->value.enumerated.item[0] = + tdm_rx_cfg[port.mode][port.channel].channels - 1; + + pr_debug("%s: tdm_rx_ch = %d, item = %d\n", __func__, + tdm_rx_cfg[port.mode][port.channel].channels - 1, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_rx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s\n", + __func__, kcontrol->id.name); + } else { + tdm_rx_cfg[port.mode][port.channel].channels = + ucontrol->value.enumerated.item[0] + 1; + + pr_debug("%s: tdm_rx_ch = %d, item = %d\n", __func__, + tdm_rx_cfg[port.mode][port.channel].channels, + ucontrol->value.enumerated.item[0] + 1); + } + return ret; +} + +static int tdm_tx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s\n", + __func__, kcontrol->id.name); + } else { + ucontrol->value.enumerated.item[0] = + tdm_tx_cfg[port.mode][port.channel].channels - 1; + + pr_debug("%s: tdm_tx_ch = %d, item = %d\n", __func__, + tdm_tx_cfg[port.mode][port.channel].channels - 1, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_tx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s\n", + __func__, kcontrol->id.name); + } else { + tdm_tx_cfg[port.mode][port.channel].channels = + ucontrol->value.enumerated.item[0] + 1; + + pr_debug("%s: tdm_tx_ch = %d, item = %d\n", __func__, + tdm_tx_cfg[port.mode][port.channel].channels, + ucontrol->value.enumerated.item[0] + 1); + } + return ret; +} + +static int aux_pcm_get_port_idx(struct snd_kcontrol *kcontrol) +{ + int idx; + + if (strnstr(kcontrol->id.name, "PRIM_AUX_PCM", + sizeof("PRIM_AUX_PCM"))) { + idx = PRIM_AUX_PCM; + } else if (strnstr(kcontrol->id.name, "SEC_AUX_PCM", + sizeof("SEC_AUX_PCM"))) { + idx = SEC_AUX_PCM; + } else if (strnstr(kcontrol->id.name, "TERT_AUX_PCM", + sizeof("TERT_AUX_PCM"))) { + idx = TERT_AUX_PCM; + } else if (strnstr(kcontrol->id.name, "QUAT_AUX_PCM", + sizeof("QUAT_AUX_PCM"))) { + idx = QUAT_AUX_PCM; + } else if (strnstr(kcontrol->id.name, "QUIN_AUX_PCM", + sizeof("QUIN_AUX_PCM"))) { + idx = QUIN_AUX_PCM; + } else { + pr_err("%s: unsupported port: %s\n", + __func__, kcontrol->id.name); + idx = -EINVAL; + } + + return idx; +} + +static int aux_pcm_rx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = aux_pcm_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + aux_pcm_rx_cfg[idx].sample_rate = + aux_pcm_get_sample_rate(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: idx[%d]_rx_sample_rate = %d, item = %d\n", __func__, + idx, aux_pcm_rx_cfg[idx].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int aux_pcm_rx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = aux_pcm_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + ucontrol->value.enumerated.item[0] = + aux_pcm_get_sample_rate_val(aux_pcm_rx_cfg[idx].sample_rate); + + pr_debug("%s: idx[%d]_rx_sample_rate = %d, item = %d\n", __func__, + idx, aux_pcm_rx_cfg[idx].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int aux_pcm_tx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = aux_pcm_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + aux_pcm_tx_cfg[idx].sample_rate = + aux_pcm_get_sample_rate(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: idx[%d]_tx_sample_rate = %d, item = %d\n", __func__, + idx, aux_pcm_tx_cfg[idx].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int aux_pcm_tx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = aux_pcm_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + ucontrol->value.enumerated.item[0] = + aux_pcm_get_sample_rate_val(aux_pcm_tx_cfg[idx].sample_rate); + + pr_debug("%s: idx[%d]_tx_sample_rate = %d, item = %d\n", __func__, + idx, aux_pcm_tx_cfg[idx].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int mi2s_get_port_idx(struct snd_kcontrol *kcontrol) +{ + int idx; + + if (strnstr(kcontrol->id.name, "PRIM_MI2S_RX", + sizeof("PRIM_MI2S_RX"))) { + idx = PRIM_MI2S; + } else if (strnstr(kcontrol->id.name, "SEC_MI2S_RX", + sizeof("SEC_MI2S_RX"))) { + idx = SEC_MI2S; + } else if (strnstr(kcontrol->id.name, "TERT_MI2S_RX", + sizeof("TERT_MI2S_RX"))) { + idx = TERT_MI2S; + } else if (strnstr(kcontrol->id.name, "QUAT_MI2S_RX", + sizeof("QUAT_MI2S_RX"))) { + idx = QUAT_MI2S; + } else if (strnstr(kcontrol->id.name, "QUIN_MI2S_RX", + sizeof("QUIN_MI2S_RX"))) { + idx = QUIN_MI2S; + } else if (strnstr(kcontrol->id.name, "PRIM_MI2S_TX", + sizeof("PRIM_MI2S_TX"))) { + idx = PRIM_MI2S; + } else if (strnstr(kcontrol->id.name, "SEC_MI2S_TX", + sizeof("SEC_MI2S_TX"))) { + idx = SEC_MI2S; + } else if (strnstr(kcontrol->id.name, "TERT_MI2S_TX", + sizeof("TERT_MI2S_TX"))) { + idx = TERT_MI2S; + } else if (strnstr(kcontrol->id.name, "QUAT_MI2S_TX", + sizeof("QUAT_MI2S_TX"))) { + idx = QUAT_MI2S; + } else if (strnstr(kcontrol->id.name, "QUIN_MI2S_TX", + sizeof("QUIN_MI2S_TX"))) { + idx = QUIN_MI2S; + } else { + pr_err("%s: unsupported channel: %s\n", + __func__, kcontrol->id.name); + idx = -EINVAL; + } + + return idx; +} + +static int mi2s_get_sample_rate_val(int sample_rate) +{ + int sample_rate_val; + + switch (sample_rate) { + case SAMPLING_RATE_8KHZ: + sample_rate_val = 0; + break; + case SAMPLING_RATE_11P025KHZ: + sample_rate_val = 1; + break; + case SAMPLING_RATE_16KHZ: + sample_rate_val = 2; + break; + case SAMPLING_RATE_22P05KHZ: + sample_rate_val = 3; + break; + case SAMPLING_RATE_32KHZ: + sample_rate_val = 4; + break; + case SAMPLING_RATE_44P1KHZ: + sample_rate_val = 5; + break; + case SAMPLING_RATE_48KHZ: + sample_rate_val = 6; + break; + case SAMPLING_RATE_96KHZ: + sample_rate_val = 7; + break; + case SAMPLING_RATE_192KHZ: + sample_rate_val = 8; + break; + default: + sample_rate_val = 6; + break; + } + return sample_rate_val; +} + +static int mi2s_get_sample_rate(int value) +{ + int sample_rate; + + switch (value) { + case 0: + sample_rate = SAMPLING_RATE_8KHZ; + break; + case 1: + sample_rate = SAMPLING_RATE_11P025KHZ; + break; + case 2: + sample_rate = SAMPLING_RATE_16KHZ; + break; + case 3: + sample_rate = SAMPLING_RATE_22P05KHZ; + break; + case 4: + sample_rate = SAMPLING_RATE_32KHZ; + break; + case 5: + sample_rate = SAMPLING_RATE_44P1KHZ; + break; + case 6: + sample_rate = SAMPLING_RATE_48KHZ; + break; + case 7: + sample_rate = SAMPLING_RATE_96KHZ; + break; + case 8: + sample_rate = SAMPLING_RATE_192KHZ; + break; + default: + sample_rate = SAMPLING_RATE_48KHZ; + break; + } + return sample_rate; +} + +static int mi2s_auxpcm_get_format(int value) +{ + int format; + + switch (value) { + case 0: + format = SNDRV_PCM_FORMAT_S16_LE; + break; + case 1: + format = SNDRV_PCM_FORMAT_S24_LE; + break; + case 2: + format = SNDRV_PCM_FORMAT_S24_3LE; + break; + case 3: + format = SNDRV_PCM_FORMAT_S32_LE; + break; + default: + format = SNDRV_PCM_FORMAT_S16_LE; + break; + } + return format; +} + +static int mi2s_auxpcm_get_format_value(int format) +{ + int value; + + switch (format) { + case SNDRV_PCM_FORMAT_S16_LE: + value = 0; + break; + case SNDRV_PCM_FORMAT_S24_LE: + value = 1; + break; + case SNDRV_PCM_FORMAT_S24_3LE: + value = 2; + break; + case SNDRV_PCM_FORMAT_S32_LE: + value = 3; + break; + default: + value = 0; + break; + } + return value; +} + +static int mi2s_rx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + mi2s_rx_cfg[idx].sample_rate = + mi2s_get_sample_rate(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: idx[%d]_rx_sample_rate = %d, item = %d\n", __func__, + idx, mi2s_rx_cfg[idx].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int mi2s_rx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + ucontrol->value.enumerated.item[0] = + mi2s_get_sample_rate_val(mi2s_rx_cfg[idx].sample_rate); + + pr_debug("%s: idx[%d]_rx_sample_rate = %d, item = %d\n", __func__, + idx, mi2s_rx_cfg[idx].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int mi2s_tx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + mi2s_tx_cfg[idx].sample_rate = + mi2s_get_sample_rate(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: idx[%d]_tx_sample_rate = %d, item = %d\n", __func__, + idx, mi2s_tx_cfg[idx].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int mi2s_tx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + ucontrol->value.enumerated.item[0] = + mi2s_get_sample_rate_val(mi2s_tx_cfg[idx].sample_rate); + + pr_debug("%s: idx[%d]_tx_sample_rate = %d, item = %d\n", __func__, + idx, mi2s_tx_cfg[idx].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int msm_mi2s_rx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + pr_debug("%s: msm_mi2s_[%d]_rx_ch = %d\n", __func__, + idx, mi2s_rx_cfg[idx].channels); + ucontrol->value.enumerated.item[0] = mi2s_rx_cfg[idx].channels - 1; + + return 0; +} + +static int msm_mi2s_rx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + mi2s_rx_cfg[idx].channels = ucontrol->value.enumerated.item[0] + 1; + pr_debug("%s: msm_mi2s_[%d]_rx_ch = %d\n", __func__, + idx, mi2s_rx_cfg[idx].channels); + + return 1; +} + +static int msm_mi2s_tx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + pr_debug("%s: msm_mi2s_[%d]_tx_ch = %d\n", __func__, + idx, mi2s_tx_cfg[idx].channels); + ucontrol->value.enumerated.item[0] = mi2s_tx_cfg[idx].channels - 1; + + return 0; +} + +static int msm_mi2s_tx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + mi2s_tx_cfg[idx].channels = ucontrol->value.enumerated.item[0] + 1; + pr_debug("%s: msm_mi2s_[%d]_tx_ch = %d\n", __func__, + idx, mi2s_tx_cfg[idx].channels); + + return 1; +} + +static int msm_mi2s_rx_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + ucontrol->value.enumerated.item[0] = + mi2s_auxpcm_get_format_value(mi2s_rx_cfg[idx].bit_format); + + pr_debug("%s: idx[%d]_rx_format = %d, item = %d\n", __func__, + idx, mi2s_rx_cfg[idx].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int msm_mi2s_rx_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + mi2s_rx_cfg[idx].bit_format = + mi2s_auxpcm_get_format(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: idx[%d]_rx_format = %d, item = %d\n", __func__, + idx, mi2s_rx_cfg[idx].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int msm_mi2s_tx_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + ucontrol->value.enumerated.item[0] = + mi2s_auxpcm_get_format_value(mi2s_tx_cfg[idx].bit_format); + + pr_debug("%s: idx[%d]_tx_format = %d, item = %d\n", __func__, + idx, mi2s_tx_cfg[idx].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int msm_mi2s_tx_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + mi2s_tx_cfg[idx].bit_format = + mi2s_auxpcm_get_format(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: idx[%d]_tx_format = %d, item = %d\n", __func__, + idx, mi2s_tx_cfg[idx].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int msm_aux_pcm_rx_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = aux_pcm_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + ucontrol->value.enumerated.item[0] = + mi2s_auxpcm_get_format_value(aux_pcm_rx_cfg[idx].bit_format); + + pr_debug("%s: idx[%d]_rx_format = %d, item = %d\n", __func__, + idx, aux_pcm_rx_cfg[idx].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int msm_aux_pcm_rx_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = aux_pcm_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + aux_pcm_rx_cfg[idx].bit_format = + mi2s_auxpcm_get_format(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: idx[%d]_rx_format = %d, item = %d\n", __func__, + idx, aux_pcm_rx_cfg[idx].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int msm_aux_pcm_tx_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = aux_pcm_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + ucontrol->value.enumerated.item[0] = + mi2s_auxpcm_get_format_value(aux_pcm_tx_cfg[idx].bit_format); + + pr_debug("%s: idx[%d]_tx_format = %d, item = %d\n", __func__, + idx, aux_pcm_tx_cfg[idx].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int msm_aux_pcm_tx_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = aux_pcm_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + aux_pcm_tx_cfg[idx].bit_format = + mi2s_auxpcm_get_format(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: idx[%d]_tx_format = %d, item = %d\n", __func__, + idx, aux_pcm_tx_cfg[idx].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int msm_hifi_ctrl(struct snd_soc_codec *codec) +{ + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + struct snd_soc_card *card = codec->component.card; + struct msm_asoc_mach_data *pdata = + snd_soc_card_get_drvdata(card); + + dev_dbg(codec->dev, "%s: msm_hifi_control = %d\n", __func__, + msm_hifi_control); + + if (!pdata || !pdata->hph_en1_gpio_p) { + dev_err(codec->dev, "%s: hph_en1_gpio is invalid\n", __func__); + return -EINVAL; + } + if (msm_hifi_control == MSM_HIFI_ON) { + msm_cdc_pinctrl_select_active_state(pdata->hph_en1_gpio_p); + /* 5msec delay needed as per HW requirement */ + usleep_range(5000, 5010); + } else { + msm_cdc_pinctrl_select_sleep_state(pdata->hph_en1_gpio_p); + } + snd_soc_dapm_sync(dapm); + + return 0; +} + +static int msm_hifi_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s: msm_hifi_control = %d\n", + __func__, msm_hifi_control); + ucontrol->value.integer.value[0] = msm_hifi_control; + + return 0; +} + +static int msm_hifi_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + + dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n", + __func__, ucontrol->value.integer.value[0]); + + msm_hifi_control = ucontrol->value.integer.value[0]; + msm_hifi_ctrl(codec); + + return 0; +} + +static const struct snd_kcontrol_new msm_int_snd_controls[] = { + SOC_ENUM_EXT("WSA_CDC_DMA_RX_0 Channels", wsa_cdc_dma_rx_0_chs, + cdc_dma_rx_ch_get, cdc_dma_rx_ch_put), + SOC_ENUM_EXT("WSA_CDC_DMA_RX_1 Channels", wsa_cdc_dma_rx_1_chs, + cdc_dma_rx_ch_get, cdc_dma_rx_ch_put), + SOC_ENUM_EXT("RX_CDC_DMA_RX_0 Channels", rx_cdc_dma_rx_0_chs, + cdc_dma_rx_ch_get, cdc_dma_rx_ch_put), + SOC_ENUM_EXT("RX_CDC_DMA_RX_1 Channels", rx_cdc_dma_rx_1_chs, + cdc_dma_rx_ch_get, cdc_dma_rx_ch_put), + SOC_ENUM_EXT("RX_CDC_DMA_RX_2 Channels", rx_cdc_dma_rx_2_chs, + cdc_dma_rx_ch_get, cdc_dma_rx_ch_put), + SOC_ENUM_EXT("RX_CDC_DMA_RX_3 Channels", rx_cdc_dma_rx_3_chs, + cdc_dma_rx_ch_get, cdc_dma_rx_ch_put), + SOC_ENUM_EXT("RX_CDC_DMA_RX_5 Channels", rx_cdc_dma_rx_5_chs, + cdc_dma_rx_ch_get, cdc_dma_rx_ch_put), + SOC_ENUM_EXT("WSA_CDC_DMA_TX_0 Channels", wsa_cdc_dma_tx_0_chs, + cdc_dma_tx_ch_get, cdc_dma_tx_ch_put), + SOC_ENUM_EXT("WSA_CDC_DMA_TX_1 Channels", wsa_cdc_dma_tx_1_chs, + cdc_dma_tx_ch_get, cdc_dma_tx_ch_put), + SOC_ENUM_EXT("WSA_CDC_DMA_TX_2 Channels", wsa_cdc_dma_tx_2_chs, + cdc_dma_tx_ch_get, cdc_dma_tx_ch_put), + SOC_ENUM_EXT("TX_CDC_DMA_TX_0 Channels", tx_cdc_dma_tx_0_chs, + cdc_dma_tx_ch_get, cdc_dma_tx_ch_put), + SOC_ENUM_EXT("TX_CDC_DMA_TX_3 Channels", tx_cdc_dma_tx_3_chs, + cdc_dma_tx_ch_get, cdc_dma_tx_ch_put), + SOC_ENUM_EXT("TX_CDC_DMA_TX_4 Channels", tx_cdc_dma_tx_4_chs, + cdc_dma_tx_ch_get, cdc_dma_tx_ch_put), + SOC_ENUM_EXT("WSA_CDC_DMA_RX_0 Format", wsa_cdc_dma_rx_0_format, + cdc_dma_rx_format_get, cdc_dma_rx_format_put), + SOC_ENUM_EXT("WSA_CDC_DMA_RX_1 Format", wsa_cdc_dma_rx_1_format, + cdc_dma_rx_format_get, cdc_dma_rx_format_put), + SOC_ENUM_EXT("RX_CDC_DMA_RX_0 Format", rx_cdc_dma_rx_0_format, + cdc_dma_rx_format_get, cdc_dma_rx_format_put), + SOC_ENUM_EXT("RX_CDC_DMA_RX_1 Format", rx_cdc_dma_rx_1_format, + cdc_dma_rx_format_get, cdc_dma_rx_format_put), + SOC_ENUM_EXT("RX_CDC_DMA_RX_2 Format", rx_cdc_dma_rx_2_format, + cdc_dma_rx_format_get, cdc_dma_rx_format_put), + SOC_ENUM_EXT("RX_CDC_DMA_RX_3 Format", rx_cdc_dma_rx_3_format, + cdc_dma_rx_format_get, cdc_dma_rx_format_put), + SOC_ENUM_EXT("RX_CDC_DMA_RX_5 Format", rx_cdc_dma_rx_5_format, + cdc_dma_rx_format_get, cdc_dma_rx_format_put), + SOC_ENUM_EXT("WSA_CDC_DMA_TX_1 Format", wsa_cdc_dma_tx_1_format, + cdc_dma_tx_format_get, cdc_dma_tx_format_put), + SOC_ENUM_EXT("WSA_CDC_DMA_TX_2 Format", wsa_cdc_dma_tx_2_format, + cdc_dma_tx_format_get, cdc_dma_tx_format_put), + SOC_ENUM_EXT("TX_CDC_DMA_TX_0 Format", tx_cdc_dma_tx_0_format, + cdc_dma_tx_format_get, cdc_dma_tx_format_put), + SOC_ENUM_EXT("TX_CDC_DMA_TX_3 Format", tx_cdc_dma_tx_3_format, + cdc_dma_tx_format_get, cdc_dma_tx_format_put), + SOC_ENUM_EXT("TX_CDC_DMA_TX_4 Format", tx_cdc_dma_tx_4_format, + cdc_dma_tx_format_get, cdc_dma_tx_format_put), + SOC_ENUM_EXT("WSA_CDC_DMA_RX_0 SampleRate", + wsa_cdc_dma_rx_0_sample_rate, + cdc_dma_rx_sample_rate_get, + cdc_dma_rx_sample_rate_put), + SOC_ENUM_EXT("WSA_CDC_DMA_RX_1 SampleRate", + wsa_cdc_dma_rx_1_sample_rate, + cdc_dma_rx_sample_rate_get, + cdc_dma_rx_sample_rate_put), + SOC_ENUM_EXT("RX_CDC_DMA_RX_0 SampleRate", + rx_cdc_dma_rx_0_sample_rate, + cdc_dma_rx_sample_rate_get, + cdc_dma_rx_sample_rate_put), + SOC_ENUM_EXT("RX_CDC_DMA_RX_1 SampleRate", + rx_cdc_dma_rx_1_sample_rate, + cdc_dma_rx_sample_rate_get, + cdc_dma_rx_sample_rate_put), + SOC_ENUM_EXT("RX_CDC_DMA_RX_2 SampleRate", + rx_cdc_dma_rx_2_sample_rate, + cdc_dma_rx_sample_rate_get, + cdc_dma_rx_sample_rate_put), + SOC_ENUM_EXT("RX_CDC_DMA_RX_3 SampleRate", + rx_cdc_dma_rx_3_sample_rate, + cdc_dma_rx_sample_rate_get, + cdc_dma_rx_sample_rate_put), + SOC_ENUM_EXT("RX_CDC_DMA_RX_5 SampleRate", + rx_cdc_dma_rx_5_sample_rate, + cdc_dma_rx_sample_rate_get, + cdc_dma_rx_sample_rate_put), + SOC_ENUM_EXT("WSA_CDC_DMA_TX_0 SampleRate", + wsa_cdc_dma_tx_0_sample_rate, + cdc_dma_tx_sample_rate_get, + cdc_dma_tx_sample_rate_put), + SOC_ENUM_EXT("WSA_CDC_DMA_TX_1 SampleRate", + wsa_cdc_dma_tx_1_sample_rate, + cdc_dma_tx_sample_rate_get, + cdc_dma_tx_sample_rate_put), + SOC_ENUM_EXT("WSA_CDC_DMA_TX_2 SampleRate", + wsa_cdc_dma_tx_2_sample_rate, + cdc_dma_tx_sample_rate_get, + cdc_dma_tx_sample_rate_put), + SOC_ENUM_EXT("TX_CDC_DMA_TX_0 SampleRate", + tx_cdc_dma_tx_0_sample_rate, + cdc_dma_tx_sample_rate_get, + cdc_dma_tx_sample_rate_put), + SOC_ENUM_EXT("TX_CDC_DMA_TX_3 SampleRate", + tx_cdc_dma_tx_3_sample_rate, + cdc_dma_tx_sample_rate_get, + cdc_dma_tx_sample_rate_put), + SOC_ENUM_EXT("TX_CDC_DMA_TX_4 SampleRate", + tx_cdc_dma_tx_4_sample_rate, + cdc_dma_tx_sample_rate_get, + cdc_dma_tx_sample_rate_put), +}; + +static const struct snd_kcontrol_new msm_ext_snd_controls[] = { + SOC_ENUM_EXT("SLIM_0_RX Channels", slim_0_rx_chs, + slim_rx_ch_get, slim_rx_ch_put), + SOC_ENUM_EXT("SLIM_2_RX Channels", slim_2_rx_chs, + slim_rx_ch_get, slim_rx_ch_put), + SOC_ENUM_EXT("SLIM_0_TX Channels", slim_0_tx_chs, + slim_tx_ch_get, slim_tx_ch_put), + SOC_ENUM_EXT("SLIM_1_TX Channels", slim_1_tx_chs, + slim_tx_ch_get, slim_tx_ch_put), + SOC_ENUM_EXT("SLIM_5_RX Channels", slim_5_rx_chs, + slim_rx_ch_get, slim_rx_ch_put), + SOC_ENUM_EXT("SLIM_6_RX Channels", slim_6_rx_chs, + slim_rx_ch_get, slim_rx_ch_put), + SOC_ENUM_EXT("SLIM_0_RX Format", slim_0_rx_format, + slim_rx_bit_format_get, slim_rx_bit_format_put), + SOC_ENUM_EXT("SLIM_5_RX Format", slim_5_rx_format, + slim_rx_bit_format_get, slim_rx_bit_format_put), + SOC_ENUM_EXT("SLIM_6_RX Format", slim_6_rx_format, + slim_rx_bit_format_get, slim_rx_bit_format_put), + SOC_ENUM_EXT("SLIM_0_TX Format", slim_0_tx_format, + slim_tx_bit_format_get, slim_tx_bit_format_put), + SOC_ENUM_EXT("SLIM_0_RX SampleRate", slim_0_rx_sample_rate, + slim_rx_sample_rate_get, slim_rx_sample_rate_put), + SOC_ENUM_EXT("SLIM_2_RX SampleRate", slim_2_rx_sample_rate, + slim_rx_sample_rate_get, slim_rx_sample_rate_put), + SOC_ENUM_EXT("SLIM_0_TX SampleRate", slim_0_tx_sample_rate, + slim_tx_sample_rate_get, slim_tx_sample_rate_put), + SOC_ENUM_EXT("SLIM_5_RX SampleRate", slim_5_rx_sample_rate, + slim_rx_sample_rate_get, slim_rx_sample_rate_put), + SOC_ENUM_EXT("SLIM_6_RX SampleRate", slim_6_rx_sample_rate, + slim_rx_sample_rate_get, slim_rx_sample_rate_put), +}; + +static const struct snd_kcontrol_new msm_common_snd_controls[] = { + SOC_ENUM_EXT("USB_AUDIO_RX Channels", usb_rx_chs, + usb_audio_rx_ch_get, usb_audio_rx_ch_put), + SOC_ENUM_EXT("USB_AUDIO_TX Channels", usb_tx_chs, + usb_audio_tx_ch_get, usb_audio_tx_ch_put), + SOC_ENUM_EXT("Display Port RX Channels", ext_disp_rx_chs, + ext_disp_rx_ch_get, ext_disp_rx_ch_put), + SOC_ENUM_EXT("PROXY_RX Channels", proxy_rx_chs, + proxy_rx_ch_get, proxy_rx_ch_put), + SOC_ENUM_EXT("USB_AUDIO_RX Format", usb_rx_format, + usb_audio_rx_format_get, usb_audio_rx_format_put), + SOC_ENUM_EXT("USB_AUDIO_TX Format", usb_tx_format, + usb_audio_tx_format_get, usb_audio_tx_format_put), + SOC_ENUM_EXT("Display Port RX Bit Format", ext_disp_rx_format, + ext_disp_rx_format_get, ext_disp_rx_format_put), + SOC_ENUM_EXT("USB_AUDIO_RX SampleRate", usb_rx_sample_rate, + usb_audio_rx_sample_rate_get, + usb_audio_rx_sample_rate_put), + SOC_ENUM_EXT("USB_AUDIO_TX SampleRate", usb_tx_sample_rate, + usb_audio_tx_sample_rate_get, + usb_audio_tx_sample_rate_put), + SOC_ENUM_EXT("Display Port RX SampleRate", ext_disp_rx_sample_rate, + ext_disp_rx_sample_rate_get, + ext_disp_rx_sample_rate_put), + SOC_ENUM_EXT("PRI_TDM_RX_0 SampleRate", tdm_rx_sample_rate, + tdm_rx_sample_rate_get, + tdm_rx_sample_rate_put), + SOC_ENUM_EXT("PRI_TDM_TX_0 SampleRate", tdm_tx_sample_rate, + tdm_tx_sample_rate_get, + tdm_tx_sample_rate_put), + SOC_ENUM_EXT("PRI_TDM_RX_0 Format", tdm_rx_format, + tdm_rx_format_get, + tdm_rx_format_put), + SOC_ENUM_EXT("PRI_TDM_TX_0 Format", tdm_tx_format, + tdm_tx_format_get, + tdm_tx_format_put), + SOC_ENUM_EXT("PRI_TDM_RX_0 Channels", tdm_rx_chs, + tdm_rx_ch_get, + tdm_rx_ch_put), + SOC_ENUM_EXT("PRI_TDM_TX_0 Channels", tdm_tx_chs, + tdm_tx_ch_get, + tdm_tx_ch_put), + SOC_ENUM_EXT("SEC_TDM_RX_0 SampleRate", tdm_rx_sample_rate, + tdm_rx_sample_rate_get, + tdm_rx_sample_rate_put), + SOC_ENUM_EXT("SEC_TDM_TX_0 SampleRate", tdm_tx_sample_rate, + tdm_tx_sample_rate_get, + tdm_tx_sample_rate_put), + SOC_ENUM_EXT("SEC_TDM_RX_0 Format", tdm_rx_format, + tdm_rx_format_get, + tdm_rx_format_put), + SOC_ENUM_EXT("SEC_TDM_TX_0 Format", tdm_tx_format, + tdm_tx_format_get, + tdm_tx_format_put), + SOC_ENUM_EXT("SEC_TDM_RX_0 Channels", tdm_rx_chs, + tdm_rx_ch_get, + tdm_rx_ch_put), + SOC_ENUM_EXT("SEC_TDM_TX_0 Channels", tdm_tx_chs, + tdm_tx_ch_get, + tdm_tx_ch_put), + SOC_ENUM_EXT("TERT_TDM_RX_0 SampleRate", tdm_rx_sample_rate, + tdm_rx_sample_rate_get, + tdm_rx_sample_rate_put), + SOC_ENUM_EXT("TERT_TDM_TX_0 SampleRate", tdm_tx_sample_rate, + tdm_tx_sample_rate_get, + tdm_tx_sample_rate_put), + SOC_ENUM_EXT("TERT_TDM_RX_0 Format", tdm_rx_format, + tdm_rx_format_get, + tdm_rx_format_put), + SOC_ENUM_EXT("TERT_TDM_TX_0 Format", tdm_tx_format, + tdm_tx_format_get, + tdm_tx_format_put), + SOC_ENUM_EXT("TERT_TDM_RX_0 Channels", tdm_rx_chs, + tdm_rx_ch_get, + tdm_rx_ch_put), + SOC_ENUM_EXT("TERT_TDM_TX_0 Channels", tdm_tx_chs, + tdm_tx_ch_get, + tdm_tx_ch_put), + SOC_ENUM_EXT("QUAT_TDM_RX_0 SampleRate", tdm_rx_sample_rate, + tdm_rx_sample_rate_get, + tdm_rx_sample_rate_put), + SOC_ENUM_EXT("QUAT_TDM_TX_0 SampleRate", tdm_tx_sample_rate, + tdm_tx_sample_rate_get, + tdm_tx_sample_rate_put), + SOC_ENUM_EXT("QUAT_TDM_RX_0 Format", tdm_rx_format, + tdm_rx_format_get, + tdm_rx_format_put), + SOC_ENUM_EXT("QUAT_TDM_TX_0 Format", tdm_tx_format, + tdm_tx_format_get, + tdm_tx_format_put), + SOC_ENUM_EXT("QUAT_TDM_RX_0 Channels", tdm_rx_chs, + tdm_rx_ch_get, + tdm_rx_ch_put), + SOC_ENUM_EXT("QUAT_TDM_TX_0 Channels", tdm_tx_chs, + tdm_tx_ch_get, + tdm_tx_ch_put), + SOC_ENUM_EXT("QUIN_TDM_RX_0 SampleRate", tdm_rx_sample_rate, + tdm_rx_sample_rate_get, + tdm_rx_sample_rate_put), + SOC_ENUM_EXT("QUIN_TDM_TX_0 SampleRate", tdm_tx_sample_rate, + tdm_tx_sample_rate_get, + tdm_tx_sample_rate_put), + SOC_ENUM_EXT("QUIN_TDM_RX_0 Format", tdm_rx_format, + tdm_rx_format_get, + tdm_rx_format_put), + SOC_ENUM_EXT("QUIN_TDM_TX_0 Format", tdm_tx_format, + tdm_tx_format_get, + tdm_tx_format_put), + SOC_ENUM_EXT("QUIN_TDM_RX_0 Channels", tdm_rx_chs, + tdm_rx_ch_get, + tdm_rx_ch_put), + SOC_ENUM_EXT("QUIN_TDM_TX_0 Channels", tdm_tx_chs, + tdm_tx_ch_get, + tdm_tx_ch_put), + SOC_ENUM_EXT("PRIM_AUX_PCM_RX SampleRate", prim_aux_pcm_rx_sample_rate, + aux_pcm_rx_sample_rate_get, + aux_pcm_rx_sample_rate_put), + SOC_ENUM_EXT("SEC_AUX_PCM_RX SampleRate", sec_aux_pcm_rx_sample_rate, + aux_pcm_rx_sample_rate_get, + aux_pcm_rx_sample_rate_put), + SOC_ENUM_EXT("TERT_AUX_PCM_RX SampleRate", tert_aux_pcm_rx_sample_rate, + aux_pcm_rx_sample_rate_get, + aux_pcm_rx_sample_rate_put), + SOC_ENUM_EXT("QUAT_AUX_PCM_RX SampleRate", quat_aux_pcm_rx_sample_rate, + aux_pcm_rx_sample_rate_get, + aux_pcm_rx_sample_rate_put), + SOC_ENUM_EXT("QUIN_AUX_PCM_RX SampleRate", quin_aux_pcm_rx_sample_rate, + aux_pcm_rx_sample_rate_get, + aux_pcm_rx_sample_rate_put), + SOC_ENUM_EXT("PRIM_AUX_PCM_TX SampleRate", prim_aux_pcm_tx_sample_rate, + aux_pcm_tx_sample_rate_get, + aux_pcm_tx_sample_rate_put), + SOC_ENUM_EXT("SEC_AUX_PCM_TX SampleRate", sec_aux_pcm_tx_sample_rate, + aux_pcm_tx_sample_rate_get, + aux_pcm_tx_sample_rate_put), + SOC_ENUM_EXT("TERT_AUX_PCM_TX SampleRate", tert_aux_pcm_tx_sample_rate, + aux_pcm_tx_sample_rate_get, + aux_pcm_tx_sample_rate_put), + SOC_ENUM_EXT("QUAT_AUX_PCM_TX SampleRate", quat_aux_pcm_tx_sample_rate, + aux_pcm_tx_sample_rate_get, + aux_pcm_tx_sample_rate_put), + SOC_ENUM_EXT("QUIN_AUX_PCM_TX SampleRate", quin_aux_pcm_tx_sample_rate, + aux_pcm_tx_sample_rate_get, + aux_pcm_tx_sample_rate_put), + SOC_ENUM_EXT("PRIM_MI2S_RX SampleRate", prim_mi2s_rx_sample_rate, + mi2s_rx_sample_rate_get, + mi2s_rx_sample_rate_put), + SOC_ENUM_EXT("SEC_MI2S_RX SampleRate", sec_mi2s_rx_sample_rate, + mi2s_rx_sample_rate_get, + mi2s_rx_sample_rate_put), + SOC_ENUM_EXT("TERT_MI2S_RX SampleRate", tert_mi2s_rx_sample_rate, + mi2s_rx_sample_rate_get, + mi2s_rx_sample_rate_put), + SOC_ENUM_EXT("QUAT_MI2S_RX SampleRate", quat_mi2s_rx_sample_rate, + mi2s_rx_sample_rate_get, + mi2s_rx_sample_rate_put), + SOC_ENUM_EXT("QUIN_MI2S_RX SampleRate", quin_mi2s_rx_sample_rate, + mi2s_rx_sample_rate_get, + mi2s_rx_sample_rate_put), + SOC_ENUM_EXT("PRIM_MI2S_TX SampleRate", prim_mi2s_tx_sample_rate, + mi2s_tx_sample_rate_get, + mi2s_tx_sample_rate_put), + SOC_ENUM_EXT("SEC_MI2S_TX SampleRate", sec_mi2s_tx_sample_rate, + mi2s_tx_sample_rate_get, + mi2s_tx_sample_rate_put), + SOC_ENUM_EXT("TERT_MI2S_TX SampleRate", tert_mi2s_tx_sample_rate, + mi2s_tx_sample_rate_get, + mi2s_tx_sample_rate_put), + SOC_ENUM_EXT("QUAT_MI2S_TX SampleRate", quat_mi2s_tx_sample_rate, + mi2s_tx_sample_rate_get, + mi2s_tx_sample_rate_put), + SOC_ENUM_EXT("QUIN_MI2S_TX SampleRate", quin_mi2s_tx_sample_rate, + mi2s_tx_sample_rate_get, + mi2s_tx_sample_rate_put), + SOC_ENUM_EXT("PRIM_MI2S_RX Channels", prim_mi2s_rx_chs, + msm_mi2s_rx_ch_get, msm_mi2s_rx_ch_put), + SOC_ENUM_EXT("PRIM_MI2S_TX Channels", prim_mi2s_tx_chs, + msm_mi2s_tx_ch_get, msm_mi2s_tx_ch_put), + SOC_ENUM_EXT("SEC_MI2S_RX Channels", sec_mi2s_rx_chs, + msm_mi2s_rx_ch_get, msm_mi2s_rx_ch_put), + SOC_ENUM_EXT("SEC_MI2S_TX Channels", sec_mi2s_tx_chs, + msm_mi2s_tx_ch_get, msm_mi2s_tx_ch_put), + SOC_ENUM_EXT("TERT_MI2S_RX Channels", tert_mi2s_rx_chs, + msm_mi2s_rx_ch_get, msm_mi2s_rx_ch_put), + SOC_ENUM_EXT("TERT_MI2S_TX Channels", tert_mi2s_tx_chs, + msm_mi2s_tx_ch_get, msm_mi2s_tx_ch_put), + SOC_ENUM_EXT("QUAT_MI2S_RX Channels", quat_mi2s_rx_chs, + msm_mi2s_rx_ch_get, msm_mi2s_rx_ch_put), + SOC_ENUM_EXT("QUAT_MI2S_TX Channels", quat_mi2s_tx_chs, + msm_mi2s_tx_ch_get, msm_mi2s_tx_ch_put), + SOC_ENUM_EXT("QUIN_MI2S_RX Channels", quin_mi2s_rx_chs, + msm_mi2s_rx_ch_get, msm_mi2s_rx_ch_put), + SOC_ENUM_EXT("QUIN_MI2S_TX Channels", quin_mi2s_tx_chs, + msm_mi2s_tx_ch_get, msm_mi2s_tx_ch_put), + SOC_ENUM_EXT("PRIM_MI2S_RX Format", mi2s_rx_format, + msm_mi2s_rx_format_get, msm_mi2s_rx_format_put), + SOC_ENUM_EXT("PRIM_MI2S_TX Format", mi2s_tx_format, + msm_mi2s_tx_format_get, msm_mi2s_tx_format_put), + SOC_ENUM_EXT("SEC_MI2S_RX Format", mi2s_rx_format, + msm_mi2s_rx_format_get, msm_mi2s_rx_format_put), + SOC_ENUM_EXT("SEC_MI2S_TX Format", mi2s_tx_format, + msm_mi2s_tx_format_get, msm_mi2s_tx_format_put), + SOC_ENUM_EXT("TERT_MI2S_RX Format", mi2s_rx_format, + msm_mi2s_rx_format_get, msm_mi2s_rx_format_put), + SOC_ENUM_EXT("TERT_MI2S_TX Format", mi2s_tx_format, + msm_mi2s_tx_format_get, msm_mi2s_tx_format_put), + SOC_ENUM_EXT("QUAT_MI2S_RX Format", mi2s_rx_format, + msm_mi2s_rx_format_get, msm_mi2s_rx_format_put), + SOC_ENUM_EXT("QUAT_MI2S_TX Format", mi2s_tx_format, + msm_mi2s_tx_format_get, msm_mi2s_tx_format_put), + SOC_ENUM_EXT("QUIN_MI2S_RX Format", mi2s_rx_format, + msm_mi2s_rx_format_get, msm_mi2s_rx_format_put), + SOC_ENUM_EXT("QUIN_MI2S_TX Format", mi2s_tx_format, + msm_mi2s_tx_format_get, msm_mi2s_tx_format_put), + SOC_ENUM_EXT("PRIM_AUX_PCM_RX Format", aux_pcm_rx_format, + msm_aux_pcm_rx_format_get, msm_aux_pcm_rx_format_put), + SOC_ENUM_EXT("PRIM_AUX_PCM_TX Format", aux_pcm_tx_format, + msm_aux_pcm_tx_format_get, msm_aux_pcm_tx_format_put), + SOC_ENUM_EXT("SEC_AUX_PCM_RX Format", aux_pcm_rx_format, + msm_aux_pcm_rx_format_get, msm_aux_pcm_rx_format_put), + SOC_ENUM_EXT("SEC_AUX_PCM_TX Format", aux_pcm_tx_format, + msm_aux_pcm_tx_format_get, msm_aux_pcm_tx_format_put), + SOC_ENUM_EXT("TERT_AUX_PCM_RX Format", aux_pcm_rx_format, + msm_aux_pcm_rx_format_get, msm_aux_pcm_rx_format_put), + SOC_ENUM_EXT("TERT_AUX_PCM_TX Format", aux_pcm_tx_format, + msm_aux_pcm_tx_format_get, msm_aux_pcm_tx_format_put), + SOC_ENUM_EXT("QUAT_AUX_PCM_RX Format", aux_pcm_rx_format, + msm_aux_pcm_rx_format_get, msm_aux_pcm_rx_format_put), + SOC_ENUM_EXT("QUAT_AUX_PCM_TX Format", aux_pcm_tx_format, + msm_aux_pcm_tx_format_get, msm_aux_pcm_tx_format_put), + SOC_ENUM_EXT("QUIN_AUX_PCM_RX Format", aux_pcm_rx_format, + msm_aux_pcm_rx_format_get, msm_aux_pcm_rx_format_put), + SOC_ENUM_EXT("QUIN_AUX_PCM_TX Format", aux_pcm_tx_format, + msm_aux_pcm_tx_format_get, msm_aux_pcm_tx_format_put), + SOC_ENUM_EXT("HiFi Function", hifi_function, msm_hifi_get, + msm_hifi_put), + SOC_ENUM_EXT("BT SampleRate", bt_sample_rate, + msm_bt_sample_rate_get, + msm_bt_sample_rate_put), + SOC_ENUM_EXT("BT SampleRate RX", bt_sample_rate_rx, + msm_bt_sample_rate_rx_get, + msm_bt_sample_rate_rx_put), + SOC_ENUM_EXT("BT SampleRate TX", bt_sample_rate_tx, + msm_bt_sample_rate_tx_get, + msm_bt_sample_rate_tx_put), + SOC_ENUM_EXT("VI_FEED_TX Channels", vi_feed_tx_chs, + msm_vi_feed_tx_ch_get, msm_vi_feed_tx_ch_put), +}; + +static int msm_snd_enable_codec_ext_clk(struct snd_soc_codec *codec, + int enable, bool dapm) +{ + int ret = 0; + + if (!strcmp(dev_name(codec->dev), "tavil_codec")) { + ret = tavil_cdc_mclk_enable(codec, enable); + } else if (!strcmp(dev_name(codec->dev), "tasha_codec")) { + ret = tasha_cdc_mclk_enable(codec, enable, dapm); + } else { + dev_err(codec->dev, "%s: unknown codec to enable ext clk\n", + __func__); + ret = -EINVAL; + } + return ret; +} + +static int msm_snd_enable_codec_ext_tx_clk(struct snd_soc_codec *codec, + int enable, bool dapm) +{ + int ret = 0; + + if (!strcmp(dev_name(codec->dev), "tavil_codec")) { + ret = tavil_cdc_mclk_tx_enable(codec, enable); + } else if (!strcmp(dev_name(codec->dev), "tasha_codec")) { + ret = tasha_cdc_mclk_tx_enable(codec, enable, dapm); + } else { + dev_err(codec->dev, "%s: unknown codec to enable TX ext clk\n", + __func__); + ret = -EINVAL; + } + + return ret; +} + +static int msm_mclk_tx_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + + pr_debug("%s: event = %d\n", __func__, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + return msm_snd_enable_codec_ext_tx_clk(codec, 1, true); + case SND_SOC_DAPM_POST_PMD: + return msm_snd_enable_codec_ext_tx_clk(codec, 0, true); + } + return 0; +} + +static int msm_mclk_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + + pr_debug("%s: event = %d\n", __func__, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + return msm_snd_enable_codec_ext_clk(codec, 1, true); + case SND_SOC_DAPM_POST_PMD: + return msm_snd_enable_codec_ext_clk(codec, 0, true); + } + return 0; +} + +static int msm_hifi_ctrl_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct snd_soc_card *card = codec->component.card; + struct msm_asoc_mach_data *pdata = + snd_soc_card_get_drvdata(card); + + dev_dbg(codec->dev, "%s: msm_hifi_control = %d\n", + __func__, msm_hifi_control); + + if (!pdata || !pdata->hph_en0_gpio_p) { + dev_err(codec->dev, "%s: hph_en0_gpio is invalid\n", __func__); + return -EINVAL; + } + + if (msm_hifi_control != MSM_HIFI_ON) { + dev_dbg(codec->dev, "%s: HiFi mixer control is not set\n", + __func__); + return 0; + } + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + msm_cdc_pinctrl_select_active_state(pdata->hph_en0_gpio_p); + break; + case SND_SOC_DAPM_PRE_PMD: + msm_cdc_pinctrl_select_sleep_state(pdata->hph_en0_gpio_p); + break; + } + + return 0; +} + +static const struct snd_soc_dapm_widget msm_ext_dapm_widgets[] = { + + SND_SOC_DAPM_SUPPLY("MCLK", SND_SOC_NOPM, 0, 0, + msm_mclk_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_SUPPLY("MCLK TX", SND_SOC_NOPM, 0, 0, + msm_mclk_tx_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_SPK("Lineout_1 amp", NULL), + SND_SOC_DAPM_SPK("Lineout_2 amp", NULL), + SND_SOC_DAPM_SPK("Lineout_3 amp", NULL), + SND_SOC_DAPM_SPK("Lineout_4 amp", NULL), + SND_SOC_DAPM_SPK("hifi amp", msm_hifi_ctrl_event), + SND_SOC_DAPM_MIC("Handset Mic", NULL), + SND_SOC_DAPM_MIC("Headset Mic", NULL), + SND_SOC_DAPM_MIC("Secondary Mic", NULL), + SND_SOC_DAPM_MIC("ANCRight Headset Mic", NULL), + SND_SOC_DAPM_MIC("ANCLeft Headset Mic", NULL), + SND_SOC_DAPM_MIC("Analog Mic4", NULL), + SND_SOC_DAPM_MIC("Analog Mic5", NULL), + SND_SOC_DAPM_MIC("Analog Mic6", NULL), + SND_SOC_DAPM_MIC("Analog Mic7", NULL), + SND_SOC_DAPM_MIC("Analog Mic8", NULL), + + SND_SOC_DAPM_MIC("Digital Mic0", NULL), + SND_SOC_DAPM_MIC("Digital Mic1", NULL), + SND_SOC_DAPM_MIC("Digital Mic2", NULL), + SND_SOC_DAPM_MIC("Digital Mic3", NULL), + SND_SOC_DAPM_MIC("Digital Mic4", NULL), + SND_SOC_DAPM_MIC("Digital Mic5", NULL), + SND_SOC_DAPM_MIC("Digital Mic6", NULL), +}; + +static int msm_dmic_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct msm_asoc_mach_data *pdata = NULL; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + int ret = 0; + u32 dmic_idx; + int *dmic_gpio_cnt; + struct device_node *dmic_gpio; + char *wname; + + wname = strpbrk(w->name, "0123"); + if (!wname) { + dev_err(codec->dev, "%s: widget not found\n", __func__); + return -EINVAL; + } + + ret = kstrtouint(wname, 10, &dmic_idx); + if (ret < 0) { + dev_err(codec->dev, "%s: Invalid DMIC line on the codec\n", + __func__); + return -EINVAL; + } + + pdata = snd_soc_card_get_drvdata(codec->component.card); + + switch (dmic_idx) { + case 0: + case 1: + dmic_gpio_cnt = &dmic_0_1_gpio_cnt; + dmic_gpio = pdata->dmic01_gpio_p; + break; + case 2: + case 3: + dmic_gpio_cnt = &dmic_2_3_gpio_cnt; + dmic_gpio = pdata->dmic23_gpio_p; + break; + default: + dev_err(codec->dev, "%s: Invalid DMIC Selection\n", + __func__); + return -EINVAL; + } + + dev_dbg(codec->dev, "%s: event %d DMIC%d dmic_gpio_cnt %d\n", + __func__, event, dmic_idx, *dmic_gpio_cnt); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + (*dmic_gpio_cnt)++; + if (*dmic_gpio_cnt == 1) { + ret = msm_cdc_pinctrl_select_active_state( + dmic_gpio); + if (ret < 0) { + pr_err("%s: gpio set cannot be activated %sd", + __func__, "dmic_gpio"); + return ret; + } + } + + break; + case SND_SOC_DAPM_POST_PMD: + (*dmic_gpio_cnt)--; + if (*dmic_gpio_cnt == 0) { + ret = msm_cdc_pinctrl_select_sleep_state( + dmic_gpio); + if (ret < 0) { + pr_err("%s: gpio set cannot be de-activated %sd", + __func__, "dmic_gpio"); + return ret; + } + } + break; + default: + pr_err("%s: invalid DAPM event %d\n", __func__, event); + return -EINVAL; + } + return 0; +} + +static const struct snd_soc_dapm_widget msm_int_dapm_widgets[] = { + SND_SOC_DAPM_MIC("Analog Mic1", NULL), + SND_SOC_DAPM_MIC("Analog Mic2", NULL), + SND_SOC_DAPM_MIC("Analog Mic3", NULL), + SND_SOC_DAPM_MIC("Analog Mic4", NULL), + SND_SOC_DAPM_MIC("Digital Mic0", msm_dmic_event), + SND_SOC_DAPM_MIC("Digital Mic1", msm_dmic_event), + SND_SOC_DAPM_MIC("Digital Mic2", msm_dmic_event), + SND_SOC_DAPM_MIC("Digital Mic3", msm_dmic_event), +}; + +static inline int param_is_mask(int p) +{ + return (p >= SNDRV_PCM_HW_PARAM_FIRST_MASK) && + (p <= SNDRV_PCM_HW_PARAM_LAST_MASK); +} + +static inline struct snd_mask *param_to_mask(struct snd_pcm_hw_params *p, + int n) +{ + return &(p->masks[n - SNDRV_PCM_HW_PARAM_FIRST_MASK]); +} + +static void param_set_mask(struct snd_pcm_hw_params *p, int n, + unsigned int bit) +{ + if (bit >= SNDRV_MASK_MAX) + return; + if (param_is_mask(n)) { + struct snd_mask *m = param_to_mask(p, n); + + m->bits[0] = 0; + m->bits[1] = 0; + m->bits[bit >> 5] |= (1 << (bit & 31)); + } +} + +static int msm_slim_get_ch_from_beid(int32_t be_id) +{ + int ch_id = 0; + + switch (be_id) { + case MSM_BACKEND_DAI_SLIMBUS_0_RX: + ch_id = SLIM_RX_0; + break; + case MSM_BACKEND_DAI_SLIMBUS_1_RX: + ch_id = SLIM_RX_1; + break; + case MSM_BACKEND_DAI_SLIMBUS_2_RX: + ch_id = SLIM_RX_2; + break; + case MSM_BACKEND_DAI_SLIMBUS_3_RX: + ch_id = SLIM_RX_3; + break; + case MSM_BACKEND_DAI_SLIMBUS_4_RX: + ch_id = SLIM_RX_4; + break; + case MSM_BACKEND_DAI_SLIMBUS_6_RX: + ch_id = SLIM_RX_6; + break; + case MSM_BACKEND_DAI_SLIMBUS_0_TX: + ch_id = SLIM_TX_0; + break; + case MSM_BACKEND_DAI_SLIMBUS_3_TX: + ch_id = SLIM_TX_3; + break; + default: + ch_id = SLIM_RX_0; + break; + } + + return ch_id; +} + +static int msm_cdc_dma_get_idx_from_beid(int32_t be_id) +{ + int idx = 0; + + switch (be_id) { + case MSM_BACKEND_DAI_WSA_CDC_DMA_RX_0: + idx = WSA_CDC_DMA_RX_0; + break; + case MSM_BACKEND_DAI_WSA_CDC_DMA_TX_0: + idx = WSA_CDC_DMA_TX_0; + break; + case MSM_BACKEND_DAI_WSA_CDC_DMA_RX_1: + idx = WSA_CDC_DMA_RX_1; + break; + case MSM_BACKEND_DAI_WSA_CDC_DMA_TX_1: + idx = WSA_CDC_DMA_TX_1; + break; + case MSM_BACKEND_DAI_WSA_CDC_DMA_TX_2: + idx = WSA_CDC_DMA_TX_2; + break; + case MSM_BACKEND_DAI_RX_CDC_DMA_RX_0: + idx = RX_CDC_DMA_RX_0; + break; + case MSM_BACKEND_DAI_RX_CDC_DMA_RX_1: + idx = RX_CDC_DMA_RX_1; + break; + case MSM_BACKEND_DAI_RX_CDC_DMA_RX_2: + idx = RX_CDC_DMA_RX_2; + break; + case MSM_BACKEND_DAI_RX_CDC_DMA_RX_3: + idx = RX_CDC_DMA_RX_3; + break; + case MSM_BACKEND_DAI_RX_CDC_DMA_RX_5: + idx = RX_CDC_DMA_RX_5; + break; + case MSM_BACKEND_DAI_TX_CDC_DMA_TX_0: + idx = TX_CDC_DMA_TX_0; + break; + case MSM_BACKEND_DAI_TX_CDC_DMA_TX_3: + idx = TX_CDC_DMA_TX_3; + break; + case MSM_BACKEND_DAI_TX_CDC_DMA_TX_4: + idx = TX_CDC_DMA_TX_4; + break; + default: + idx = RX_CDC_DMA_RX_0; + break; + } + + return idx; +} + +static int msm_ext_disp_get_idx_from_beid(int32_t be_id) +{ + int idx = -EINVAL; + + switch (be_id) { + case MSM_BACKEND_DAI_DISPLAY_PORT_RX: + idx = DP_RX_IDX; + break; + default: + pr_err("%s: Incorrect ext_disp BE id %d\n", __func__, be_id); + idx = -EINVAL; + break; + } + + return idx; +} + +static int msm_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_dai_link *dai_link = rtd->dai_link; + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + int rc = 0; + int idx; + void *config = NULL; + struct snd_soc_codec *codec = NULL; + + pr_debug("%s: format = %d, rate = %d\n", + __func__, params_format(params), params_rate(params)); + + switch (dai_link->id) { + case MSM_BACKEND_DAI_SLIMBUS_0_RX: + case MSM_BACKEND_DAI_SLIMBUS_1_RX: + case MSM_BACKEND_DAI_SLIMBUS_2_RX: + case MSM_BACKEND_DAI_SLIMBUS_3_RX: + case MSM_BACKEND_DAI_SLIMBUS_4_RX: + case MSM_BACKEND_DAI_SLIMBUS_6_RX: + idx = msm_slim_get_ch_from_beid(dai_link->id); + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + slim_rx_cfg[idx].bit_format); + rate->min = rate->max = slim_rx_cfg[idx].sample_rate; + channels->min = channels->max = slim_rx_cfg[idx].channels; + break; + + case MSM_BACKEND_DAI_SLIMBUS_0_TX: + case MSM_BACKEND_DAI_SLIMBUS_3_TX: + idx = msm_slim_get_ch_from_beid(dai_link->id); + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + slim_tx_cfg[idx].bit_format); + rate->min = rate->max = slim_tx_cfg[idx].sample_rate; + channels->min = channels->max = slim_tx_cfg[idx].channels; + break; + + case MSM_BACKEND_DAI_SLIMBUS_1_TX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + slim_tx_cfg[1].bit_format); + rate->min = rate->max = slim_tx_cfg[1].sample_rate; + channels->min = channels->max = slim_tx_cfg[1].channels; + break; + + case MSM_BACKEND_DAI_SLIMBUS_4_TX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + SNDRV_PCM_FORMAT_S32_LE); + rate->min = rate->max = SAMPLING_RATE_8KHZ; + channels->min = channels->max = msm_vi_feed_tx_ch; + break; + + case MSM_BACKEND_DAI_SLIMBUS_5_RX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + slim_rx_cfg[5].bit_format); + rate->min = rate->max = slim_rx_cfg[5].sample_rate; + channels->min = channels->max = slim_rx_cfg[5].channels; + break; + + case MSM_BACKEND_DAI_SLIMBUS_5_TX: + codec = rtd->codec; + rate->min = rate->max = SAMPLING_RATE_16KHZ; + channels->min = channels->max = 1; + + config = msm_codec_fn.get_afe_config_fn(codec, + AFE_SLIMBUS_SLAVE_PORT_CONFIG); + if (config) { + rc = afe_set_config(AFE_SLIMBUS_SLAVE_PORT_CONFIG, + config, SLIMBUS_5_TX); + if (rc) + pr_err("%s: Failed to set slimbus slave port config %d\n", + __func__, rc); + } + break; + + case MSM_BACKEND_DAI_SLIMBUS_7_RX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + slim_rx_cfg[SLIM_RX_7].bit_format); + rate->min = rate->max = slim_rx_cfg[SLIM_RX_7].sample_rate; + channels->min = channels->max = + slim_rx_cfg[SLIM_RX_7].channels; + break; + + case MSM_BACKEND_DAI_SLIMBUS_7_TX: + rate->min = rate->max = slim_tx_cfg[SLIM_TX_7].sample_rate; + channels->min = channels->max = + slim_tx_cfg[SLIM_TX_7].channels; + break; + + case MSM_BACKEND_DAI_SLIMBUS_8_TX: + rate->min = rate->max = slim_tx_cfg[SLIM_TX_8].sample_rate; + channels->min = channels->max = + slim_tx_cfg[SLIM_TX_8].channels; + break; + + case MSM_BACKEND_DAI_USB_RX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + usb_rx_cfg.bit_format); + rate->min = rate->max = usb_rx_cfg.sample_rate; + channels->min = channels->max = usb_rx_cfg.channels; + break; + + case MSM_BACKEND_DAI_USB_TX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + usb_tx_cfg.bit_format); + rate->min = rate->max = usb_tx_cfg.sample_rate; + channels->min = channels->max = usb_tx_cfg.channels; + break; + + case MSM_BACKEND_DAI_DISPLAY_PORT_RX: + idx = msm_ext_disp_get_idx_from_beid(dai_link->id); + if (idx < 0) { + pr_err("%s: Incorrect ext disp idx %d\n", + __func__, idx); + rc = idx; + goto done; + } + + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + ext_disp_rx_cfg[idx].bit_format); + rate->min = rate->max = ext_disp_rx_cfg[idx].sample_rate; + channels->min = channels->max = ext_disp_rx_cfg[idx].channels; + break; + + case MSM_BACKEND_DAI_AFE_PCM_RX: + channels->min = channels->max = proxy_rx_cfg.channels; + rate->min = rate->max = SAMPLING_RATE_48KHZ; + break; + + case MSM_BACKEND_DAI_PRI_TDM_RX_0: + channels->min = channels->max = + tdm_rx_cfg[TDM_PRI][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_PRI][TDM_0].bit_format); + rate->min = rate->max = tdm_rx_cfg[TDM_PRI][TDM_0].sample_rate; + break; + + case MSM_BACKEND_DAI_PRI_TDM_TX_0: + channels->min = channels->max = + tdm_tx_cfg[TDM_PRI][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_tx_cfg[TDM_PRI][TDM_0].bit_format); + rate->min = rate->max = tdm_tx_cfg[TDM_PRI][TDM_0].sample_rate; + break; + + case MSM_BACKEND_DAI_SEC_TDM_RX_0: + channels->min = channels->max = + tdm_rx_cfg[TDM_SEC][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_SEC][TDM_0].bit_format); + rate->min = rate->max = tdm_rx_cfg[TDM_SEC][TDM_0].sample_rate; + break; + + case MSM_BACKEND_DAI_SEC_TDM_TX_0: + channels->min = channels->max = + tdm_tx_cfg[TDM_SEC][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_tx_cfg[TDM_SEC][TDM_0].bit_format); + rate->min = rate->max = tdm_tx_cfg[TDM_SEC][TDM_0].sample_rate; + break; + + case MSM_BACKEND_DAI_TERT_TDM_RX_0: + channels->min = channels->max = + tdm_rx_cfg[TDM_TERT][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_TERT][TDM_0].bit_format); + rate->min = rate->max = tdm_rx_cfg[TDM_TERT][TDM_0].sample_rate; + break; + + case MSM_BACKEND_DAI_TERT_TDM_TX_0: + channels->min = channels->max = + tdm_tx_cfg[TDM_TERT][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_tx_cfg[TDM_TERT][TDM_0].bit_format); + rate->min = rate->max = tdm_tx_cfg[TDM_TERT][TDM_0].sample_rate; + break; + + case MSM_BACKEND_DAI_QUAT_TDM_RX_0: + channels->min = channels->max = + tdm_rx_cfg[TDM_QUAT][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_QUAT][TDM_0].bit_format); + rate->min = rate->max = tdm_rx_cfg[TDM_QUAT][TDM_0].sample_rate; + break; + + case MSM_BACKEND_DAI_QUAT_TDM_TX_0: + channels->min = channels->max = + tdm_tx_cfg[TDM_QUAT][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_tx_cfg[TDM_QUAT][TDM_0].bit_format); + rate->min = rate->max = tdm_tx_cfg[TDM_QUAT][TDM_0].sample_rate; + break; + + case MSM_BACKEND_DAI_QUIN_TDM_RX_0: + channels->min = channels->max = + tdm_rx_cfg[TDM_QUIN][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_QUIN][TDM_0].bit_format); + rate->min = rate->max = tdm_rx_cfg[TDM_QUIN][TDM_0].sample_rate; + break; + + case MSM_BACKEND_DAI_QUIN_TDM_TX_0: + channels->min = channels->max = + tdm_tx_cfg[TDM_QUIN][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_tx_cfg[TDM_QUIN][TDM_0].bit_format); + rate->min = rate->max = tdm_tx_cfg[TDM_QUIN][TDM_0].sample_rate; + break; + + + case MSM_BACKEND_DAI_AUXPCM_RX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + aux_pcm_rx_cfg[PRIM_AUX_PCM].bit_format); + rate->min = rate->max = + aux_pcm_rx_cfg[PRIM_AUX_PCM].sample_rate; + channels->min = channels->max = + aux_pcm_rx_cfg[PRIM_AUX_PCM].channels; + break; + + case MSM_BACKEND_DAI_AUXPCM_TX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + aux_pcm_tx_cfg[PRIM_AUX_PCM].bit_format); + rate->min = rate->max = + aux_pcm_tx_cfg[PRIM_AUX_PCM].sample_rate; + channels->min = channels->max = + aux_pcm_tx_cfg[PRIM_AUX_PCM].channels; + break; + + case MSM_BACKEND_DAI_SEC_AUXPCM_RX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + aux_pcm_rx_cfg[SEC_AUX_PCM].bit_format); + rate->min = rate->max = + aux_pcm_rx_cfg[SEC_AUX_PCM].sample_rate; + channels->min = channels->max = + aux_pcm_rx_cfg[SEC_AUX_PCM].channels; + break; + + case MSM_BACKEND_DAI_SEC_AUXPCM_TX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + aux_pcm_tx_cfg[SEC_AUX_PCM].bit_format); + rate->min = rate->max = + aux_pcm_tx_cfg[SEC_AUX_PCM].sample_rate; + channels->min = channels->max = + aux_pcm_tx_cfg[SEC_AUX_PCM].channels; + break; + + case MSM_BACKEND_DAI_TERT_AUXPCM_RX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + aux_pcm_rx_cfg[TERT_AUX_PCM].bit_format); + rate->min = rate->max = + aux_pcm_rx_cfg[TERT_AUX_PCM].sample_rate; + channels->min = channels->max = + aux_pcm_rx_cfg[TERT_AUX_PCM].channels; + break; + + case MSM_BACKEND_DAI_TERT_AUXPCM_TX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + aux_pcm_tx_cfg[TERT_AUX_PCM].bit_format); + rate->min = rate->max = + aux_pcm_tx_cfg[TERT_AUX_PCM].sample_rate; + channels->min = channels->max = + aux_pcm_tx_cfg[TERT_AUX_PCM].channels; + break; + + case MSM_BACKEND_DAI_QUAT_AUXPCM_RX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + aux_pcm_rx_cfg[QUAT_AUX_PCM].bit_format); + rate->min = rate->max = + aux_pcm_rx_cfg[QUAT_AUX_PCM].sample_rate; + channels->min = channels->max = + aux_pcm_rx_cfg[QUAT_AUX_PCM].channels; + break; + + case MSM_BACKEND_DAI_QUAT_AUXPCM_TX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + aux_pcm_tx_cfg[QUAT_AUX_PCM].bit_format); + rate->min = rate->max = + aux_pcm_tx_cfg[QUAT_AUX_PCM].sample_rate; + channels->min = channels->max = + aux_pcm_tx_cfg[QUAT_AUX_PCM].channels; + break; + + case MSM_BACKEND_DAI_QUIN_AUXPCM_RX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + aux_pcm_rx_cfg[QUIN_AUX_PCM].bit_format); + rate->min = rate->max = + aux_pcm_rx_cfg[QUIN_AUX_PCM].sample_rate; + channels->min = channels->max = + aux_pcm_rx_cfg[QUIN_AUX_PCM].channels; + break; + + case MSM_BACKEND_DAI_QUIN_AUXPCM_TX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + aux_pcm_tx_cfg[QUIN_AUX_PCM].bit_format); + rate->min = rate->max = + aux_pcm_tx_cfg[QUIN_AUX_PCM].sample_rate; + channels->min = channels->max = + aux_pcm_tx_cfg[QUIN_AUX_PCM].channels; + break; + + case MSM_BACKEND_DAI_PRI_MI2S_RX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + mi2s_rx_cfg[PRIM_MI2S].bit_format); + rate->min = rate->max = mi2s_rx_cfg[PRIM_MI2S].sample_rate; + channels->min = channels->max = + mi2s_rx_cfg[PRIM_MI2S].channels; + break; + + case MSM_BACKEND_DAI_PRI_MI2S_TX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + mi2s_tx_cfg[PRIM_MI2S].bit_format); + rate->min = rate->max = mi2s_tx_cfg[PRIM_MI2S].sample_rate; + channels->min = channels->max = + mi2s_tx_cfg[PRIM_MI2S].channels; + break; + + case MSM_BACKEND_DAI_SECONDARY_MI2S_RX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + mi2s_rx_cfg[SEC_MI2S].bit_format); + rate->min = rate->max = mi2s_rx_cfg[SEC_MI2S].sample_rate; + channels->min = channels->max = + mi2s_rx_cfg[SEC_MI2S].channels; + break; + + case MSM_BACKEND_DAI_SECONDARY_MI2S_TX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + mi2s_tx_cfg[SEC_MI2S].bit_format); + rate->min = rate->max = mi2s_tx_cfg[SEC_MI2S].sample_rate; + channels->min = channels->max = + mi2s_tx_cfg[SEC_MI2S].channels; + break; + + case MSM_BACKEND_DAI_TERTIARY_MI2S_RX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + mi2s_rx_cfg[TERT_MI2S].bit_format); + rate->min = rate->max = mi2s_rx_cfg[TERT_MI2S].sample_rate; + channels->min = channels->max = + mi2s_rx_cfg[TERT_MI2S].channels; + break; + + case MSM_BACKEND_DAI_TERTIARY_MI2S_TX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + mi2s_tx_cfg[TERT_MI2S].bit_format); + rate->min = rate->max = mi2s_tx_cfg[TERT_MI2S].sample_rate; + channels->min = channels->max = + mi2s_tx_cfg[TERT_MI2S].channels; + break; + + case MSM_BACKEND_DAI_QUATERNARY_MI2S_RX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + mi2s_rx_cfg[QUAT_MI2S].bit_format); + rate->min = rate->max = mi2s_rx_cfg[QUAT_MI2S].sample_rate; + channels->min = channels->max = + mi2s_rx_cfg[QUAT_MI2S].channels; + break; + + case MSM_BACKEND_DAI_QUATERNARY_MI2S_TX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + mi2s_tx_cfg[QUAT_MI2S].bit_format); + rate->min = rate->max = mi2s_tx_cfg[QUAT_MI2S].sample_rate; + channels->min = channels->max = + mi2s_tx_cfg[QUAT_MI2S].channels; + break; + + case MSM_BACKEND_DAI_QUINARY_MI2S_RX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + mi2s_rx_cfg[QUIN_MI2S].bit_format); + rate->min = rate->max = mi2s_rx_cfg[QUIN_MI2S].sample_rate; + channels->min = channels->max = + mi2s_rx_cfg[QUIN_MI2S].channels; + break; + + case MSM_BACKEND_DAI_QUINARY_MI2S_TX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + mi2s_tx_cfg[QUIN_MI2S].bit_format); + rate->min = rate->max = mi2s_tx_cfg[QUIN_MI2S].sample_rate; + channels->min = channels->max = + mi2s_tx_cfg[QUIN_MI2S].channels; + break; + + case MSM_BACKEND_DAI_WSA_CDC_DMA_RX_0: + case MSM_BACKEND_DAI_WSA_CDC_DMA_RX_1: + case MSM_BACKEND_DAI_RX_CDC_DMA_RX_0: + case MSM_BACKEND_DAI_RX_CDC_DMA_RX_1: + case MSM_BACKEND_DAI_RX_CDC_DMA_RX_2: + idx = msm_cdc_dma_get_idx_from_beid(dai_link->id); + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + cdc_dma_rx_cfg[idx].bit_format); + rate->min = rate->max = cdc_dma_rx_cfg[idx].sample_rate; + channels->min = channels->max = cdc_dma_rx_cfg[idx].channels; + break; + + case MSM_BACKEND_DAI_WSA_CDC_DMA_TX_1: + case MSM_BACKEND_DAI_WSA_CDC_DMA_TX_2: + case MSM_BACKEND_DAI_TX_CDC_DMA_TX_0: + case MSM_BACKEND_DAI_TX_CDC_DMA_TX_3: + case MSM_BACKEND_DAI_TX_CDC_DMA_TX_4: + idx = msm_cdc_dma_get_idx_from_beid(dai_link->id); + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + cdc_dma_tx_cfg[idx].bit_format); + rate->min = rate->max = cdc_dma_tx_cfg[idx].sample_rate; + channels->min = channels->max = cdc_dma_tx_cfg[idx].channels; + break; + + case MSM_BACKEND_DAI_WSA_CDC_DMA_TX_0: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + SNDRV_PCM_FORMAT_S32_LE); + rate->min = rate->max = SAMPLING_RATE_8KHZ; + channels->min = channels->max = msm_vi_feed_tx_ch; + break; + + default: + rate->min = rate->max = SAMPLING_RATE_48KHZ; + break; + } + +done: + return rc; +} + +static bool msm_usbc_swap_gnd_mic(struct snd_soc_codec *codec, bool active) +{ + struct snd_soc_card *card = codec->component.card; + struct msm_asoc_mach_data *pdata = + snd_soc_card_get_drvdata(card); + + if (!pdata->fsa_handle) + return false; + + return fsa4480_switch_event(pdata->fsa_handle, FSA_MIC_GND_SWAP); +} + +static bool msm_swap_gnd_mic(struct snd_soc_codec *codec, bool active) +{ + int value = 0; + bool ret = false; + struct snd_soc_card *card; + struct msm_asoc_mach_data *pdata; + + if (!codec) { + pr_err("%s codec is NULL\n", __func__); + return false; + } + card = codec->component.card; + pdata = snd_soc_card_get_drvdata(card); + + if (!pdata) + return false; + + if (wcd_mbhc_cfg.enable_usbc_analog) + return msm_usbc_swap_gnd_mic(codec, active); + + /* if usbc is not defined, swap using us_euro_gpio_p */ + if (pdata->us_euro_gpio_p) { + value = msm_cdc_pinctrl_get_state( + pdata->us_euro_gpio_p); + if (value) + msm_cdc_pinctrl_select_sleep_state( + pdata->us_euro_gpio_p); + else + msm_cdc_pinctrl_select_active_state( + pdata->us_euro_gpio_p); + dev_dbg(codec->dev, "%s: swap select switch %d to %d\n", + __func__, value, !value); + ret = true; + } + return ret; +} + +static int msm_afe_set_config(struct snd_soc_codec *codec) +{ + int ret = 0; + void *config_data = NULL; + + if (!msm_codec_fn.get_afe_config_fn) { + dev_err(codec->dev, "%s: codec get afe config not init'ed\n", + __func__); + return -EINVAL; + } + + config_data = msm_codec_fn.get_afe_config_fn(codec, + AFE_CDC_REGISTERS_CONFIG); + if (config_data) { + ret = afe_set_config(AFE_CDC_REGISTERS_CONFIG, config_data, 0); + if (ret) { + dev_err(codec->dev, + "%s: Failed to set codec registers config %d\n", + __func__, ret); + return ret; + } + } + + config_data = msm_codec_fn.get_afe_config_fn(codec, + AFE_CDC_REGISTER_PAGE_CONFIG); + if (config_data) { + ret = afe_set_config(AFE_CDC_REGISTER_PAGE_CONFIG, config_data, + 0); + if (ret) + dev_err(codec->dev, + "%s: Failed to set cdc register page config\n", + __func__); + } + + config_data = msm_codec_fn.get_afe_config_fn(codec, + AFE_SLIMBUS_SLAVE_CONFIG); + if (config_data) { + ret = afe_set_config(AFE_SLIMBUS_SLAVE_CONFIG, config_data, 0); + if (ret) { + dev_err(codec->dev, + "%s: Failed to set slimbus slave config %d\n", + __func__, ret); + return ret; + } + } + + return 0; +} + +static void msm_afe_clear_config(void) +{ + afe_clear_config(AFE_CDC_REGISTERS_CONFIG); + afe_clear_config(AFE_SLIMBUS_SLAVE_CONFIG); +} + +static int msm_audrx_tavil_init(struct snd_soc_pcm_runtime *rtd) +{ + int ret = 0; + void *config_data; + struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_component *aux_comp; + struct snd_card *card; + struct snd_info_entry *entry; + struct msm_asoc_mach_data *pdata = + snd_soc_card_get_drvdata(rtd->card); + + /* + * Codec SLIMBUS configuration + * RX1, RX2, RX3, RX4, RX5, RX6, RX7, RX8 + * TX1, TX2, TX3, TX4, TX5, TX6, TX7, TX8, TX9, TX10, TX11, TX12, TX13 + * TX14, TX15, TX16 + */ + unsigned int rx_ch[WCD934X_RX_MAX] = {144, 145, 146, 147, 148, 149, + 150, 151}; + unsigned int tx_ch[WCD934X_TX_MAX] = {128, 129, 130, 131, 132, 133, + 134, 135, 136, 137, 138, 139, + 140, 141, 142, 143}; + + pr_info("%s: dev_name:%s\n", __func__, dev_name(cpu_dai->dev)); + + rtd->pmdown_time = 0; + + ret = snd_soc_add_codec_controls(codec, msm_ext_snd_controls, + ARRAY_SIZE(msm_ext_snd_controls)); + if (ret < 0) { + pr_err("%s: add_codec_controls failed, err %d\n", + __func__, ret); + return ret; + } + + ret = snd_soc_add_codec_controls(codec, msm_common_snd_controls, + ARRAY_SIZE(msm_common_snd_controls)); + if (ret < 0) { + pr_err("%s: add_codec_controls failed, err %d\n", + __func__, ret); + return ret; + } + + snd_soc_dapm_new_controls(dapm, msm_ext_dapm_widgets, + ARRAY_SIZE(msm_ext_dapm_widgets)); + + snd_soc_dapm_add_routes(dapm, wcd_audio_paths, + ARRAY_SIZE(wcd_audio_paths)); + + snd_soc_dapm_ignore_suspend(dapm, "Handset Mic"); + snd_soc_dapm_ignore_suspend(dapm, "Headset Mic"); + snd_soc_dapm_ignore_suspend(dapm, "ANCRight Headset Mic"); + snd_soc_dapm_ignore_suspend(dapm, "ANCLeft Headset Mic"); + snd_soc_dapm_ignore_suspend(dapm, "Digital Mic0"); + snd_soc_dapm_ignore_suspend(dapm, "Digital Mic1"); + snd_soc_dapm_ignore_suspend(dapm, "Digital Mic2"); + snd_soc_dapm_ignore_suspend(dapm, "Digital Mic3"); + snd_soc_dapm_ignore_suspend(dapm, "Digital Mic4"); + snd_soc_dapm_ignore_suspend(dapm, "Digital Mic5"); + snd_soc_dapm_ignore_suspend(dapm, "Analog Mic5"); + snd_soc_dapm_ignore_suspend(dapm, "MADINPUT"); + snd_soc_dapm_ignore_suspend(dapm, "MAD_CPE_INPUT"); + snd_soc_dapm_ignore_suspend(dapm, "MAD_CPE_OUT1"); + snd_soc_dapm_ignore_suspend(dapm, "MAD_CPE_OUT2"); + snd_soc_dapm_ignore_suspend(dapm, "EAR"); + snd_soc_dapm_ignore_suspend(dapm, "LINEOUT1"); + snd_soc_dapm_ignore_suspend(dapm, "LINEOUT2"); + snd_soc_dapm_ignore_suspend(dapm, "ANC EAR"); + snd_soc_dapm_ignore_suspend(dapm, "SPK1 OUT"); + snd_soc_dapm_ignore_suspend(dapm, "SPK2 OUT"); + snd_soc_dapm_ignore_suspend(dapm, "HPHL"); + snd_soc_dapm_ignore_suspend(dapm, "HPHR"); + snd_soc_dapm_ignore_suspend(dapm, "AIF4 VI"); + snd_soc_dapm_ignore_suspend(dapm, "VIINPUT"); + snd_soc_dapm_ignore_suspend(dapm, "ANC HPHL"); + snd_soc_dapm_ignore_suspend(dapm, "ANC HPHR"); + + snd_soc_dapm_sync(dapm); + + snd_soc_dai_set_channel_map(codec_dai, ARRAY_SIZE(tx_ch), + tx_ch, ARRAY_SIZE(rx_ch), rx_ch); + + msm_codec_fn.get_afe_config_fn = tavil_get_afe_config; + + ret = msm_afe_set_config(codec); + if (ret) { + pr_err("%s: Failed to set AFE config %d\n", __func__, ret); + goto err; + } + pdata->is_afe_config_done = true; + + config_data = msm_codec_fn.get_afe_config_fn(codec, + AFE_AANC_VERSION); + if (config_data) { + ret = afe_set_config(AFE_AANC_VERSION, config_data, 0); + if (ret) { + pr_err("%s: Failed to set aanc version %d\n", + __func__, ret); + goto err; + } + } + + /* + * Send speaker configuration only for WSA8810. + * Default configuration is for WSA8815. + */ + pr_debug("%s: Number of aux devices: %d\n", + __func__, rtd->card->num_aux_devs); + if (rtd->card->num_aux_devs && + !list_empty(&rtd->card->aux_comp_list)) { + aux_comp = list_first_entry(&rtd->card->aux_comp_list, + struct snd_soc_component, card_aux_list); + if (!strcmp(aux_comp->name, WSA8810_NAME_1) || + !strcmp(aux_comp->name, WSA8810_NAME_2)) { + tavil_set_spkr_mode(rtd->codec, WCD934X_SPKR_MODE_1); + tavil_set_spkr_gain_offset(rtd->codec, + WCD934X_RX_GAIN_OFFSET_M1P5_DB); + } + } + + card = rtd->card->snd_card; + if (!pdata->codec_root) { + entry = snd_info_create_subdir(card->module, "codecs", + card->proc_root); + if (!entry) { + pr_debug("%s: Cannot create codecs module entry\n", + __func__); + ret = 0; + goto err; + } + pdata->codec_root = entry; + } + tavil_codec_info_create_codec_entry(pdata->codec_root, codec); + + codec_reg_done = true; + return 0; +err: + return ret; +} + +static int msm_audrx_tasha_init(struct snd_soc_pcm_runtime *rtd) +{ + int ret = 0; + void *config_data; + struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_component *aux_comp; + struct snd_card *card; + struct snd_info_entry *entry; + struct msm_asoc_mach_data *pdata = + snd_soc_card_get_drvdata(rtd->card); + + /* Codec SLIMBUS configuration + * RX1, RX2, RX3, RX4, RX5, RX6, RX7, RX8, RX9, RX10, RX11, RX12, RX13 + * TX1, TX2, TX3, TX4, TX5, TX6, TX7, TX8, TX9, TX10, TX11, TX12, TX13 + * TX14, TX15, TX16 + */ + unsigned int rx_ch[TASHA_RX_MAX] = {144, 145, 146, 147, 148, 149, 150, + 151, 152, 153, 154, 155, 156}; + unsigned int tx_ch[TASHA_TX_MAX] = {128, 129, 130, 131, 132, 133, + 134, 135, 136, 137, 138, 139, + 140, 141, 142, 143}; + + pr_info("%s: dev_name:%s\n", __func__, dev_name(cpu_dai->dev)); + + rtd->pmdown_time = 0; + + ret = snd_soc_add_codec_controls(codec, msm_ext_snd_controls, + ARRAY_SIZE(msm_ext_snd_controls)); + if (ret < 0) { + pr_err("%s: add_codec_controls failed, err %d\n", + __func__, ret); + return ret; + } + + ret = snd_soc_add_codec_controls(codec, msm_common_snd_controls, + ARRAY_SIZE(msm_common_snd_controls)); + if (ret < 0) { + pr_err("%s: add_codec_controls failed, err %d\n", + __func__, ret); + return ret; + } + + snd_soc_dapm_new_controls(dapm, msm_ext_dapm_widgets, + ARRAY_SIZE(msm_ext_dapm_widgets)); + + snd_soc_dapm_add_routes(dapm, wcd_audio_paths, + ARRAY_SIZE(wcd_audio_paths)); + + snd_soc_dapm_enable_pin(dapm, "Lineout_1 amp"); + snd_soc_dapm_enable_pin(dapm, "Lineout_2 amp"); + snd_soc_dapm_enable_pin(dapm, "Lineout_3 amp"); + snd_soc_dapm_enable_pin(dapm, "Lineout_4 amp"); + + snd_soc_dapm_ignore_suspend(dapm, "MADINPUT"); + snd_soc_dapm_ignore_suspend(dapm, "MAD_CPE_INPUT"); + snd_soc_dapm_ignore_suspend(dapm, "Handset Mic"); + snd_soc_dapm_ignore_suspend(dapm, "Headset Mic"); + snd_soc_dapm_ignore_suspend(dapm, "Secondary Mic"); + snd_soc_dapm_ignore_suspend(dapm, "Lineout_1 amp"); + snd_soc_dapm_ignore_suspend(dapm, "Lineout_2 amp"); + snd_soc_dapm_ignore_suspend(dapm, "Lineout_3 amp"); + snd_soc_dapm_ignore_suspend(dapm, "Lineout_4 amp"); + snd_soc_dapm_ignore_suspend(dapm, "ANCRight Headset Mic"); + snd_soc_dapm_ignore_suspend(dapm, "ANCLeft Headset Mic"); + snd_soc_dapm_ignore_suspend(dapm, "Digital Mic0"); + snd_soc_dapm_ignore_suspend(dapm, "Digital Mic1"); + snd_soc_dapm_ignore_suspend(dapm, "Digital Mic2"); + snd_soc_dapm_ignore_suspend(dapm, "Digital Mic3"); + snd_soc_dapm_ignore_suspend(dapm, "Digital Mic4"); + snd_soc_dapm_ignore_suspend(dapm, "Digital Mic5"); + snd_soc_dapm_ignore_suspend(dapm, "Analog Mic4"); + snd_soc_dapm_ignore_suspend(dapm, "Analog Mic6"); + snd_soc_dapm_ignore_suspend(dapm, "Analog Mic7"); + snd_soc_dapm_ignore_suspend(dapm, "Analog Mic8"); + + snd_soc_dapm_ignore_suspend(dapm, "EAR"); + snd_soc_dapm_ignore_suspend(dapm, "LINEOUT1"); + snd_soc_dapm_ignore_suspend(dapm, "LINEOUT2"); + snd_soc_dapm_ignore_suspend(dapm, "AMIC1"); + snd_soc_dapm_ignore_suspend(dapm, "AMIC2"); + snd_soc_dapm_ignore_suspend(dapm, "AMIC3"); + snd_soc_dapm_ignore_suspend(dapm, "AMIC4"); + snd_soc_dapm_ignore_suspend(dapm, "AMIC5"); + snd_soc_dapm_ignore_suspend(dapm, "DMIC0"); + snd_soc_dapm_ignore_suspend(dapm, "DMIC1"); + snd_soc_dapm_ignore_suspend(dapm, "DMIC2"); + snd_soc_dapm_ignore_suspend(dapm, "DMIC3"); + snd_soc_dapm_ignore_suspend(dapm, "DMIC4"); + snd_soc_dapm_ignore_suspend(dapm, "DMIC5"); + snd_soc_dapm_ignore_suspend(dapm, "ANC EAR"); + snd_soc_dapm_ignore_suspend(dapm, "SPK1 OUT"); + snd_soc_dapm_ignore_suspend(dapm, "SPK2 OUT"); + snd_soc_dapm_ignore_suspend(dapm, "HPHL"); + snd_soc_dapm_ignore_suspend(dapm, "HPHR"); + snd_soc_dapm_ignore_suspend(dapm, "AIF4 VI"); + snd_soc_dapm_ignore_suspend(dapm, "VIINPUT"); + + snd_soc_dapm_ignore_suspend(dapm, "LINEOUT3"); + snd_soc_dapm_ignore_suspend(dapm, "LINEOUT4"); + snd_soc_dapm_ignore_suspend(dapm, "ANC HPHL"); + snd_soc_dapm_ignore_suspend(dapm, "ANC HPHR"); + snd_soc_dapm_ignore_suspend(dapm, "ANC LINEOUT1"); + snd_soc_dapm_ignore_suspend(dapm, "ANC LINEOUT2"); + + snd_soc_dapm_sync(dapm); + + snd_soc_dai_set_channel_map(codec_dai, ARRAY_SIZE(tx_ch), + tx_ch, ARRAY_SIZE(rx_ch), rx_ch); + + msm_codec_fn.get_afe_config_fn = tasha_get_afe_config; + + ret = msm_afe_set_config(codec); + if (ret) { + pr_err("%s: Failed to set AFE config %d\n", __func__, ret); + goto err; + } + pdata->is_afe_config_done = true; + + config_data = msm_codec_fn.get_afe_config_fn(codec, + AFE_AANC_VERSION); + if (config_data) { + ret = afe_set_config(AFE_AANC_VERSION, config_data, 0); + if (ret) { + pr_err("%s: Failed to set aanc version %d\n", + __func__, ret); + goto err; + } + } + + /* + * Send speaker configuration only for WSA8810. + * Default configuration is for WSA8815. + */ + pr_debug("%s: Number of aux devices: %d\n", + __func__, rtd->card->num_aux_devs); + if (rtd->card->num_aux_devs && + !list_empty(&rtd->card->aux_comp_list)) { + aux_comp = list_first_entry(&rtd->card->aux_comp_list, + struct snd_soc_component, card_aux_list); + if (!strcmp(aux_comp->name, WSA8810_NAME_1) || + !strcmp(aux_comp->name, WSA8810_NAME_2)) { + tasha_set_spkr_mode(rtd->codec, SPKR_MODE_1); + tasha_set_spkr_gain_offset(rtd->codec, + RX_GAIN_OFFSET_M1P5_DB); + } + } + + card = rtd->card->snd_card; + if (!pdata->codec_root) { + entry = snd_info_create_subdir(card->module, "codecs", + card->proc_root); + if (!entry) { + pr_debug("%s: Cannot create codecs module entry\n", + __func__); + ret = 0; + goto err; + } + pdata->codec_root = entry; + } + tasha_codec_info_create_codec_entry(pdata->codec_root, codec); + + codec_reg_done = true; + return 0; +err: + return ret; +} + +static int msm_int_audrx_init(struct snd_soc_pcm_runtime *rtd) +{ + int ret = 0; + struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + struct snd_card *card; + struct snd_info_entry *entry; + struct snd_soc_component *aux_comp; + struct msm_asoc_mach_data *pdata = + snd_soc_card_get_drvdata(rtd->card); + + ret = snd_soc_add_codec_controls(codec, msm_int_snd_controls, + ARRAY_SIZE(msm_int_snd_controls)); + if (ret < 0) { + pr_err("%s: add_codec_controls failed: %d\n", + __func__, ret); + return ret; + } + ret = snd_soc_add_codec_controls(codec, msm_common_snd_controls, + ARRAY_SIZE(msm_common_snd_controls)); + if (ret < 0) { + pr_err("%s: add common snd controls failed: %d\n", + __func__, ret); + return ret; + } + + snd_soc_dapm_new_controls(dapm, msm_int_dapm_widgets, + ARRAY_SIZE(msm_int_dapm_widgets)); + + snd_soc_dapm_ignore_suspend(dapm, "Digital Mic0"); + snd_soc_dapm_ignore_suspend(dapm, "Digital Mic1"); + snd_soc_dapm_ignore_suspend(dapm, "Digital Mic2"); + snd_soc_dapm_ignore_suspend(dapm, "Digital Mic3"); + + snd_soc_dapm_ignore_suspend(dapm, "Analog Mic1"); + snd_soc_dapm_ignore_suspend(dapm, "Analog Mic2"); + snd_soc_dapm_ignore_suspend(dapm, "Analog Mic3"); + snd_soc_dapm_ignore_suspend(dapm, "Analog Mic4"); + + snd_soc_dapm_ignore_suspend(dapm, "WSA_SPK1 OUT"); + snd_soc_dapm_ignore_suspend(dapm, "WSA_SPK2 OUT"); + snd_soc_dapm_ignore_suspend(dapm, "WSA AIF VI"); + snd_soc_dapm_ignore_suspend(dapm, "VIINPUT_WSA"); + + snd_soc_dapm_sync(dapm); + + /* + * Send speaker configuration only for WSA8810. + * Default configuration is for WSA8815. + */ + dev_dbg(codec->dev, "%s: Number of aux devices: %d\n", + __func__, rtd->card->num_aux_devs); + if (rtd->card->num_aux_devs && + !list_empty(&rtd->card->aux_comp_list)) { + list_for_each_entry(aux_comp, &rtd->card->aux_comp_list, + card_aux_list) { + if (!strcmp(aux_comp->name, WSA8810_NAME_1) || + !strcmp(aux_comp->name, WSA8810_NAME_2)) { + wsa_macro_set_spkr_mode(rtd->codec, + WSA_MACRO_SPKR_MODE_1); + wsa_macro_set_spkr_gain_offset(rtd->codec, + WSA_MACRO_GAIN_OFFSET_M1P5_DB); + break; + } + } + } + card = rtd->card->snd_card; + if (!pdata->codec_root) { + entry = snd_info_create_subdir(card->module, "codecs", + card->proc_root); + if (!entry) { + pr_debug("%s: Cannot create codecs module entry\n", + __func__); + ret = 0; + goto err; + } + pdata->codec_root = entry; + } + bolero_info_create_codec_entry(pdata->codec_root, codec); + /* + * SM6150 MSM 1.0 doesn't have hardware wake up interrupt line + * from AOSS to APSS. So, it uses SW workaround and listens to + * interrupt from AFE over IPC. + * Check for MSM version and MSM ID and register wake irq + * accordingly to provide compatibility to all chipsets. + */ + if (socinfo_get_id() == SM6150_SOC_MSM_ID && + socinfo_get_version() == SM6150_SOC_VERSION_1_0) + bolero_register_wake_irq(codec, true); + else + bolero_register_wake_irq(codec, false); + + codec_reg_done = true; + return 0; +err: + return ret; +} + +static int msm_wcn_init(struct snd_soc_pcm_runtime *rtd) +{ + unsigned int rx_ch[WCN_CDC_SLIM_RX_CH_MAX] = {157, 158}; + unsigned int tx_ch[WCN_CDC_SLIM_TX_CH_MAX] = {159, 160, 161}; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + + return snd_soc_dai_set_channel_map(codec_dai, ARRAY_SIZE(tx_ch), + tx_ch, ARRAY_SIZE(rx_ch), rx_ch); +} + +static void *def_wcd_mbhc_cal(void) +{ + void *wcd_mbhc_cal; + struct wcd_mbhc_btn_detect_cfg *btn_cfg; + u16 *btn_high; + + wcd_mbhc_cal = kzalloc(WCD_MBHC_CAL_SIZE(WCD_MBHC_DEF_BUTTONS, + WCD9XXX_MBHC_DEF_RLOADS), GFP_KERNEL); + if (!wcd_mbhc_cal) + return NULL; + +#define S(X, Y) ((WCD_MBHC_CAL_PLUG_TYPE_PTR(wcd_mbhc_cal)->X) = (Y)) + S(v_hs_max, 1600); +#undef S +#define S(X, Y) ((WCD_MBHC_CAL_BTN_DET_PTR(wcd_mbhc_cal)->X) = (Y)) + S(num_btn, WCD_MBHC_DEF_BUTTONS); +#undef S + + btn_cfg = WCD_MBHC_CAL_BTN_DET_PTR(wcd_mbhc_cal); + btn_high = ((void *)&btn_cfg->_v_btn_low) + + (sizeof(btn_cfg->_v_btn_low[0]) * btn_cfg->num_btn); + + btn_high[0] = 75; + btn_high[1] = 150; + btn_high[2] = 237; + btn_high[3] = 500; + btn_high[4] = 500; + btn_high[5] = 500; + btn_high[6] = 500; + btn_high[7] = 500; + + return wcd_mbhc_cal; +} + +static int msm_snd_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai_link *dai_link = rtd->dai_link; + + int ret = 0; + u32 rx_ch[SLIM_MAX_RX_PORTS], tx_ch[SLIM_MAX_TX_PORTS]; + u32 rx_ch_cnt = 0, tx_ch_cnt = 0; + u32 user_set_tx_ch = 0; + u32 rx_ch_count; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + ret = snd_soc_dai_get_channel_map(codec_dai, + &tx_ch_cnt, tx_ch, &rx_ch_cnt, rx_ch); + if (ret < 0) { + pr_err("%s: failed to get codec chan map, err:%d\n", + __func__, ret); + goto err; + } + if (dai_link->id == MSM_BACKEND_DAI_SLIMBUS_5_RX) { + pr_debug("%s: rx_5_ch=%d\n", __func__, + slim_rx_cfg[5].channels); + rx_ch_count = slim_rx_cfg[5].channels; + } else if (dai_link->id == MSM_BACKEND_DAI_SLIMBUS_2_RX) { + pr_debug("%s: rx_2_ch=%d\n", __func__, + slim_rx_cfg[2].channels); + rx_ch_count = slim_rx_cfg[2].channels; + } else if (dai_link->id == MSM_BACKEND_DAI_SLIMBUS_6_RX) { + pr_debug("%s: rx_6_ch=%d\n", __func__, + slim_rx_cfg[6].channels); + rx_ch_count = slim_rx_cfg[6].channels; + } else { + pr_debug("%s: rx_0_ch=%d\n", __func__, + slim_rx_cfg[0].channels); + rx_ch_count = slim_rx_cfg[0].channels; + } + ret = snd_soc_dai_set_channel_map(cpu_dai, 0, 0, + rx_ch_count, rx_ch); + if (ret < 0) { + pr_err("%s: failed to set cpu chan map, err:%d\n", + __func__, ret); + goto err; + } + } else { + + pr_debug("%s: %s_tx_dai_id_%d_ch=%d\n", __func__, + codec_dai->name, codec_dai->id, user_set_tx_ch); + ret = snd_soc_dai_get_channel_map(codec_dai, + &tx_ch_cnt, tx_ch, &rx_ch_cnt, rx_ch); + if (ret < 0) { + pr_err("%s: failed to get tx codec chan map, err:%d\n", + __func__, ret); + goto err; + } + /* For _tx1 case */ + if (dai_link->id == MSM_BACKEND_DAI_SLIMBUS_0_TX) + user_set_tx_ch = slim_tx_cfg[0].channels; + /* For _tx3 case */ + else if (dai_link->id == MSM_BACKEND_DAI_SLIMBUS_1_TX) + user_set_tx_ch = slim_tx_cfg[1].channels; + else if (dai_link->id == MSM_BACKEND_DAI_SLIMBUS_4_TX) + user_set_tx_ch = msm_vi_feed_tx_ch; + else + user_set_tx_ch = tx_ch_cnt; + + pr_debug("%s: msm_slim_0_tx_ch(%d) user_set_tx_ch(%d) tx_ch_cnt(%d), BE id (%d)\n", + __func__, slim_tx_cfg[0].channels, user_set_tx_ch, + tx_ch_cnt, dai_link->id); + + ret = snd_soc_dai_set_channel_map(cpu_dai, + user_set_tx_ch, tx_ch, 0, 0); + if (ret < 0) + pr_err("%s: failed to set tx cpu chan map, err:%d\n", + __func__, ret); + } + +err: + return ret; +} + + +static int msm_snd_cdc_dma_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai_link *dai_link = rtd->dai_link; + + int ret = 0; + u32 rx_ch_cdc_dma, tx_ch_cdc_dma; + u32 rx_ch_cnt = 0, tx_ch_cnt = 0; + u32 user_set_tx_ch = 0; + u32 user_set_rx_ch = 0; + u32 ch_id; + + ret = snd_soc_dai_get_channel_map(codec_dai, + &tx_ch_cnt, &tx_ch_cdc_dma, &rx_ch_cnt, + &rx_ch_cdc_dma); + if (ret < 0) { + pr_err("%s: failed to get codec chan map, err:%d\n", + __func__, ret); + goto err; + } + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + switch (dai_link->id) { + case MSM_BACKEND_DAI_WSA_CDC_DMA_RX_0: + case MSM_BACKEND_DAI_WSA_CDC_DMA_RX_1: + case MSM_BACKEND_DAI_RX_CDC_DMA_RX_0: + case MSM_BACKEND_DAI_RX_CDC_DMA_RX_1: + case MSM_BACKEND_DAI_RX_CDC_DMA_RX_2: + case MSM_BACKEND_DAI_RX_CDC_DMA_RX_3: + case MSM_BACKEND_DAI_RX_CDC_DMA_RX_4: + case MSM_BACKEND_DAI_RX_CDC_DMA_RX_5: + { + ch_id = msm_cdc_dma_get_idx_from_beid(dai_link->id); + pr_debug("%s: id %d rx_ch=%d\n", __func__, + ch_id, cdc_dma_rx_cfg[ch_id].channels); + user_set_rx_ch = cdc_dma_rx_cfg[ch_id].channels; + ret = snd_soc_dai_set_channel_map(cpu_dai, 0, 0, + user_set_rx_ch, &rx_ch_cdc_dma); + if (ret < 0) { + pr_err("%s: failed to set cpu chan map, err:%d\n", + __func__, ret); + goto err; + } + + } + break; + } + } else { + switch (dai_link->id) { + case MSM_BACKEND_DAI_WSA_CDC_DMA_TX_0: + { + user_set_tx_ch = msm_vi_feed_tx_ch; + } + break; + case MSM_BACKEND_DAI_WSA_CDC_DMA_TX_1: + case MSM_BACKEND_DAI_WSA_CDC_DMA_TX_2: + case MSM_BACKEND_DAI_TX_CDC_DMA_TX_0: + case MSM_BACKEND_DAI_TX_CDC_DMA_TX_3: + case MSM_BACKEND_DAI_TX_CDC_DMA_TX_4: + { + ch_id = msm_cdc_dma_get_idx_from_beid(dai_link->id); + pr_debug("%s: id %d tx_ch=%d\n", __func__, + ch_id, cdc_dma_tx_cfg[ch_id].channels); + user_set_tx_ch = cdc_dma_tx_cfg[ch_id].channels; + } + break; + } + + ret = snd_soc_dai_set_channel_map(cpu_dai, user_set_tx_ch, + &tx_ch_cdc_dma, 0, 0); + if (ret < 0) { + pr_err("%s: failed to set cpu chan map, err:%d\n", + __func__, ret); + goto err; + } + } + +err: + return ret; +} + +static int msm_slimbus_2_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + unsigned int rx_ch[SLIM_MAX_RX_PORTS], tx_ch[SLIM_MAX_TX_PORTS]; + unsigned int rx_ch_cnt = 0, tx_ch_cnt = 0; + unsigned int num_tx_ch = 0; + unsigned int num_rx_ch = 0; + int ret = 0; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + num_rx_ch = params_channels(params); + pr_debug("%s: %s rx_dai_id = %d num_ch = %d\n", __func__, + codec_dai->name, codec_dai->id, num_rx_ch); + ret = snd_soc_dai_get_channel_map(codec_dai, + &tx_ch_cnt, tx_ch, &rx_ch_cnt, rx_ch); + if (ret < 0) { + pr_err("%s: failed to get codec chan map, err:%d\n", + __func__, ret); + goto err; + } + ret = snd_soc_dai_set_channel_map(cpu_dai, 0, 0, + num_rx_ch, rx_ch); + if (ret < 0) { + pr_err("%s: failed to set cpu chan map, err:%d\n", + __func__, ret); + goto err; + } + } else { + num_tx_ch = params_channels(params); + pr_debug("%s: %s tx_dai_id = %d num_ch = %d\n", __func__, + codec_dai->name, codec_dai->id, num_tx_ch); + ret = snd_soc_dai_get_channel_map(codec_dai, + &tx_ch_cnt, tx_ch, &rx_ch_cnt, rx_ch); + if (ret < 0) { + pr_err("%s: failed to get tx codec chan map, err:%d\n", + __func__, ret); + goto err; + } + ret = snd_soc_dai_set_channel_map(cpu_dai, + num_tx_ch, tx_ch, 0, 0); + if (ret < 0) { + pr_err("%s: failed to set tx cpu chan map, err:%d\n", + __func__, ret); + goto err; + } + } + +err: + return ret; +} + +static int msm_wcn_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai_link *dai_link = rtd->dai_link; + u32 rx_ch[WCN_CDC_SLIM_RX_CH_MAX], tx_ch[WCN_CDC_SLIM_TX_CH_MAX]; + u32 rx_ch_cnt = 0, tx_ch_cnt = 0; + int ret; + + dev_dbg(rtd->dev, "%s: %s_tx_dai_id_%d\n", __func__, + codec_dai->name, codec_dai->id); + ret = snd_soc_dai_get_channel_map(codec_dai, + &tx_ch_cnt, tx_ch, &rx_ch_cnt, rx_ch); + if (ret) { + dev_err(rtd->dev, + "%s: failed to get BTFM codec chan map\n, err:%d\n", + __func__, ret); + goto err; + } + + dev_dbg(rtd->dev, "%s: tx_ch_cnt(%d) BE id %d\n", + __func__, tx_ch_cnt, dai_link->id); + + ret = snd_soc_dai_set_channel_map(cpu_dai, + tx_ch_cnt, tx_ch, rx_ch_cnt, rx_ch); + if (ret) + dev_err(rtd->dev, "%s: failed to set cpu chan map, err:%d\n", + __func__, ret); + +err: + return ret; +} + +int msm_snd_cpe_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai_link *dai_link = rtd->dai_link; + int ret = 0; + u32 tx_ch[SLIM_MAX_TX_PORTS]; + u32 tx_ch_cnt = 0; + + if (substream->stream != SNDRV_PCM_STREAM_CAPTURE) { + pr_err("%s: Invalid stream type %d\n", + __func__, substream->stream); + ret = -EINVAL; + goto end; + } + + pr_debug("%s: %s_tx_dai_id_%d\n", __func__, + codec_dai->name, codec_dai->id); + ret = snd_soc_dai_get_channel_map(codec_dai, + &tx_ch_cnt, tx_ch, NULL, NULL); + if (ret < 0) { + pr_err("%s: failed to get codec chan map\n, err:%d\n", + __func__, ret); + goto end; + } + + pr_debug("%s: tx_ch_cnt(%d) id %d\n", + __func__, tx_ch_cnt, dai_link->id); + + ret = snd_soc_dai_set_channel_map(cpu_dai, + tx_ch_cnt, tx_ch, 0, 0); + if (ret < 0) { + pr_err("%s: failed to set cpu chan map, err:%d\n", + __func__, ret); + goto end; + } +end: + return ret; +} + +static int msm_get_port_id(int be_id) +{ + int afe_port_id; + + switch (be_id) { + case MSM_BACKEND_DAI_PRI_MI2S_RX: + afe_port_id = AFE_PORT_ID_PRIMARY_MI2S_RX; + break; + case MSM_BACKEND_DAI_PRI_MI2S_TX: + afe_port_id = AFE_PORT_ID_PRIMARY_MI2S_TX; + break; + case MSM_BACKEND_DAI_SECONDARY_MI2S_RX: + afe_port_id = AFE_PORT_ID_SECONDARY_MI2S_RX; + break; + case MSM_BACKEND_DAI_SECONDARY_MI2S_TX: + afe_port_id = AFE_PORT_ID_SECONDARY_MI2S_TX; + break; + case MSM_BACKEND_DAI_TERTIARY_MI2S_RX: + afe_port_id = AFE_PORT_ID_TERTIARY_MI2S_RX; + break; + case MSM_BACKEND_DAI_TERTIARY_MI2S_TX: + afe_port_id = AFE_PORT_ID_TERTIARY_MI2S_TX; + break; + case MSM_BACKEND_DAI_QUATERNARY_MI2S_RX: + afe_port_id = AFE_PORT_ID_QUATERNARY_MI2S_RX; + break; + case MSM_BACKEND_DAI_QUATERNARY_MI2S_TX: + afe_port_id = AFE_PORT_ID_QUATERNARY_MI2S_TX; + break; + case MSM_BACKEND_DAI_QUINARY_MI2S_RX: + afe_port_id = AFE_PORT_ID_QUINARY_MI2S_RX; + break; + case MSM_BACKEND_DAI_QUINARY_MI2S_TX: + afe_port_id = AFE_PORT_ID_QUINARY_MI2S_TX; + break; + default: + pr_err("%s: Invalid BE id: %d\n", __func__, be_id); + afe_port_id = -EINVAL; + } + + return afe_port_id; +} + +static u32 get_mi2s_bits_per_sample(u32 bit_format) +{ + u32 bit_per_sample; + + switch (bit_format) { + case SNDRV_PCM_FORMAT_S32_LE: + case SNDRV_PCM_FORMAT_S24_3LE: + case SNDRV_PCM_FORMAT_S24_LE: + bit_per_sample = 32; + break; + case SNDRV_PCM_FORMAT_S16_LE: + default: + bit_per_sample = 16; + break; + } + + return bit_per_sample; +} + +static void update_mi2s_clk_val(int dai_id, int stream) +{ + u32 bit_per_sample; + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + bit_per_sample = + get_mi2s_bits_per_sample(mi2s_rx_cfg[dai_id].bit_format); + mi2s_clk[dai_id].clk_freq_in_hz = + mi2s_rx_cfg[dai_id].sample_rate * 2 * bit_per_sample; + } else { + bit_per_sample = + get_mi2s_bits_per_sample(mi2s_tx_cfg[dai_id].bit_format); + mi2s_clk[dai_id].clk_freq_in_hz = + mi2s_tx_cfg[dai_id].sample_rate * 2 * bit_per_sample; + } +} + +static int msm_mi2s_set_sclk(struct snd_pcm_substream *substream, bool enable) +{ + int ret = 0; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + int port_id = 0; + int index = cpu_dai->id; + + port_id = msm_get_port_id(rtd->dai_link->id); + if (port_id < 0) { + dev_err(rtd->card->dev, "%s: Invalid port_id\n", __func__); + ret = port_id; + goto err; + } + + if (enable) { + update_mi2s_clk_val(index, substream->stream); + dev_dbg(rtd->card->dev, "%s: clock rate %ul\n", __func__, + mi2s_clk[index].clk_freq_in_hz); + } + + mi2s_clk[index].enable = enable; + ret = afe_set_lpass_clock_v2(port_id, + &mi2s_clk[index]); + if (ret < 0) { + dev_err(rtd->card->dev, + "%s: afe lpass clock failed for port 0x%x , err:%d\n", + __func__, port_id, ret); + goto err; + } + +err: + return ret; +} + +static int msm_set_pinctrl(struct msm_pinctrl_info *pinctrl_info, + enum pinctrl_pin_state new_state) +{ + int ret = 0; + int curr_state = 0; + + if (pinctrl_info == NULL) { + pr_err("%s: pinctrl_info is NULL\n", __func__); + ret = -EINVAL; + goto err; + } + + if (pinctrl_info->pinctrl == NULL) { + pr_err("%s: pinctrl_info->pinctrl is NULL\n", __func__); + ret = -EINVAL; + goto err; + } + + curr_state = pinctrl_info->curr_state; + pinctrl_info->curr_state = new_state; + pr_debug("%s: curr_state = %s new_state = %s\n", __func__, + pin_states[curr_state], pin_states[pinctrl_info->curr_state]); + + if (curr_state == pinctrl_info->curr_state) { + pr_debug("%s: Already in same state\n", __func__); + goto err; + } + + if (curr_state != STATE_DISABLE && + pinctrl_info->curr_state != STATE_DISABLE) { + pr_debug("%s: state already active cannot switch\n", __func__); + ret = -EIO; + goto err; + } + + switch (pinctrl_info->curr_state) { + case STATE_MI2S_ACTIVE: + ret = pinctrl_select_state(pinctrl_info->pinctrl, + pinctrl_info->mi2s_active); + if (ret) { + pr_err("%s: MI2S state select failed with %d\n", + __func__, ret); + ret = -EIO; + goto err; + } + break; + case STATE_TDM_ACTIVE: + ret = pinctrl_select_state(pinctrl_info->pinctrl, + pinctrl_info->tdm_active); + if (ret) { + pr_err("%s: TDM state select failed with %d\n", + __func__, ret); + ret = -EIO; + goto err; + } + break; + case STATE_DISABLE: + if (curr_state == STATE_MI2S_ACTIVE) { + ret = pinctrl_select_state(pinctrl_info->pinctrl, + pinctrl_info->mi2s_disable); + } else { + ret = pinctrl_select_state(pinctrl_info->pinctrl, + pinctrl_info->tdm_disable); + } + if (ret) { + pr_err("%s: state disable failed with %d\n", + __func__, ret); + ret = -EIO; + goto err; + } + break; + default: + pr_err("%s: TLMM pin state is invalid\n", __func__); + return -EINVAL; + } + +err: + return ret; +} + +static int msm_get_pinctrl(struct platform_device *pdev) +{ + struct snd_soc_card *card = platform_get_drvdata(pdev); + struct msm_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card); + struct msm_pinctrl_info *pinctrl_info = NULL; + struct pinctrl *pinctrl; + int ret = 0; + + pinctrl_info = &pdata->pinctrl_info; + + if (pinctrl_info == NULL) { + pr_err("%s: pinctrl_info is NULL\n", __func__); + return -EINVAL; + } + + pinctrl = devm_pinctrl_get(&pdev->dev); + if (IS_ERR_OR_NULL(pinctrl)) { + pr_err("%s: Unable to get pinctrl handle\n", __func__); + return -EINVAL; + } + pinctrl_info->pinctrl = pinctrl; + + /* get all the states handles from Device Tree */ + pinctrl_info->mi2s_disable = pinctrl_lookup_state(pinctrl, + "quat-mi2s-sleep"); + if (IS_ERR(pinctrl_info->mi2s_disable)) { + pr_err("%s: could not get mi2s_disable pinstate\n", __func__); + goto err; + } + pinctrl_info->mi2s_active = pinctrl_lookup_state(pinctrl, + "quat-mi2s-active"); + if (IS_ERR(pinctrl_info->mi2s_active)) { + pr_err("%s: could not get mi2s_active pinstate\n", __func__); + goto err; + } + pinctrl_info->tdm_disable = pinctrl_lookup_state(pinctrl, + "quat-tdm-sleep"); + if (IS_ERR(pinctrl_info->tdm_disable)) { + pr_err("%s: could not get tdm_disable pinstate\n", __func__); + goto err; + } + pinctrl_info->tdm_active = pinctrl_lookup_state(pinctrl, + "quat-tdm-active"); + if (IS_ERR(pinctrl_info->tdm_active)) { + pr_err("%s: could not get tdm_active pinstate\n", + __func__); + goto err; + } + /* Reset the TLMM pins to a default state */ + ret = pinctrl_select_state(pinctrl_info->pinctrl, + pinctrl_info->mi2s_disable); + if (ret != 0) { + pr_err("%s: Disable TLMM pins failed with %d\n", + __func__, ret); + ret = -EIO; + goto err; + } + pinctrl_info->curr_state = STATE_DISABLE; + + return 0; + +err: + devm_pinctrl_put(pinctrl); + pinctrl_info->pinctrl = NULL; + return -EINVAL; +} + +static int msm_tdm_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + + if (cpu_dai->id == AFE_PORT_ID_QUATERNARY_TDM_RX) { + channels->min = channels->max = + tdm_rx_cfg[TDM_QUAT][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_QUAT][TDM_0].bit_format); + rate->min = rate->max = + tdm_rx_cfg[TDM_QUAT][TDM_0].sample_rate; + } else if (cpu_dai->id == AFE_PORT_ID_SECONDARY_TDM_RX) { + channels->min = channels->max = + tdm_rx_cfg[TDM_SEC][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_SEC][TDM_0].bit_format); + rate->min = rate->max = tdm_rx_cfg[TDM_SEC][TDM_0].sample_rate; + } else if (cpu_dai->id == AFE_PORT_ID_QUINARY_TDM_RX) { + channels->min = channels->max = + tdm_rx_cfg[TDM_QUIN][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_QUIN][TDM_0].bit_format); + rate->min = rate->max = tdm_rx_cfg[TDM_QUIN][TDM_0].sample_rate; + } else { + pr_err("%s: dai id 0x%x not supported\n", + __func__, cpu_dai->id); + return -EINVAL; + } + + pr_debug("%s: dai id = 0x%x channels = %d rate = %d format = 0x%x\n", + __func__, cpu_dai->id, channels->max, rate->max, + params_format(params)); + + return 0; +} + +static int sm6150_tdm_snd_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + int ret = 0; + int slot_width = 32; + int channels, slots; + unsigned int slot_mask, rate, clk_freq; + unsigned int slot_offset[8] = {0, 4, 8, 12, 16, 20, 24, 28}; + + pr_debug("%s: dai id = 0x%x\n", __func__, cpu_dai->id); + + /* currently only supporting TDM_RX_0 and TDM_TX_0 */ + switch (cpu_dai->id) { + case AFE_PORT_ID_PRIMARY_TDM_RX: + slots = tdm_rx_cfg[TDM_PRI][TDM_0].channels; + break; + case AFE_PORT_ID_SECONDARY_TDM_RX: + slots = tdm_rx_cfg[TDM_SEC][TDM_0].channels; + break; + case AFE_PORT_ID_TERTIARY_TDM_RX: + slots = tdm_rx_cfg[TDM_TERT][TDM_0].channels; + break; + case AFE_PORT_ID_QUATERNARY_TDM_RX: + slots = tdm_rx_cfg[TDM_QUAT][TDM_0].channels; + break; + case AFE_PORT_ID_QUINARY_TDM_RX: + slots = tdm_rx_cfg[TDM_QUIN][TDM_0].channels; + break; + case AFE_PORT_ID_PRIMARY_TDM_TX: + slots = tdm_tx_cfg[TDM_PRI][TDM_0].channels; + break; + case AFE_PORT_ID_SECONDARY_TDM_TX: + slots = tdm_tx_cfg[TDM_SEC][TDM_0].channels; + break; + case AFE_PORT_ID_TERTIARY_TDM_TX: + slots = tdm_tx_cfg[TDM_TERT][TDM_0].channels; + break; + case AFE_PORT_ID_QUATERNARY_TDM_TX: + slots = tdm_tx_cfg[TDM_QUAT][TDM_0].channels; + break; + case AFE_PORT_ID_QUINARY_TDM_TX: + slots = tdm_tx_cfg[TDM_QUIN][TDM_0].channels; + break; + + default: + pr_err("%s: dai id 0x%x not supported\n", + __func__, cpu_dai->id); + return -EINVAL; + } + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + /*2 slot config - bits 0 and 1 set for the first two slots */ + slot_mask = 0x0000FFFF >> (16-slots); + channels = slots; + + pr_debug("%s: tdm rx slot_width %d slots %d\n", + __func__, slot_width, slots); + + ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0, slot_mask, + slots, slot_width); + if (ret < 0) { + pr_err("%s: failed to set tdm rx slot, err:%d\n", + __func__, ret); + goto end; + } + + ret = snd_soc_dai_set_channel_map(cpu_dai, + 0, NULL, channels, slot_offset); + if (ret < 0) { + pr_err("%s: failed to set tdm rx channel map, err:%d\n", + __func__, ret); + goto end; + } + } else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + /*2 slot config - bits 0 and 1 set for the first two slots */ + slot_mask = 0x0000FFFF >> (16-slots); + channels = slots; + + pr_debug("%s: tdm tx slot_width %d slots %d\n", + __func__, slot_width, slots); + + ret = snd_soc_dai_set_tdm_slot(cpu_dai, slot_mask, 0, + slots, slot_width); + if (ret < 0) { + pr_err("%s: failed to set tdm tx slot, err:%d\n", + __func__, ret); + goto end; + } + + ret = snd_soc_dai_set_channel_map(cpu_dai, + channels, slot_offset, 0, NULL); + if (ret < 0) { + pr_err("%s: failed to set tdm tx channel map, err:%d\n", + __func__, ret); + goto end; + } + } else { + ret = -EINVAL; + pr_err("%s: invalid use case, err:%d\n", + __func__, ret); + goto end; + } + + rate = params_rate(params); + clk_freq = rate * slot_width * slots; + ret = snd_soc_dai_set_sysclk(cpu_dai, 0, clk_freq, SND_SOC_CLOCK_OUT); + if (ret < 0) + pr_err("%s: failed to set tdm clk, err:%d\n", + __func__, ret); + +end: + return ret; +} + +static int sm6150_tdm_snd_startup(struct snd_pcm_substream *substream) +{ + int ret = 0; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_card *card = rtd->card; + struct msm_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card); + struct msm_pinctrl_info *pinctrl_info = &pdata->pinctrl_info; + + /* currently only supporting TDM_RX_0 and TDM_TX_0 */ + if ((cpu_dai->id == AFE_PORT_ID_QUATERNARY_TDM_RX) || + (cpu_dai->id == AFE_PORT_ID_QUATERNARY_TDM_TX)) { + ret = msm_set_pinctrl(pinctrl_info, STATE_TDM_ACTIVE); + if (ret) + pr_err("%s: TDM TLMM pinctrl set failed with %d\n", + __func__, ret); + } + + return ret; +} + +static void sm6150_tdm_snd_shutdown(struct snd_pcm_substream *substream) +{ + int ret = 0; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_card *card = rtd->card; + struct msm_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card); + struct msm_pinctrl_info *pinctrl_info = &pdata->pinctrl_info; + + /* currently only supporting TDM_RX_0 and TDM_TX_0 */ + if ((cpu_dai->id == AFE_PORT_ID_QUATERNARY_TDM_RX) || + (cpu_dai->id == AFE_PORT_ID_QUATERNARY_TDM_TX)) { + ret = msm_set_pinctrl(pinctrl_info, STATE_DISABLE); + if (ret) + pr_err("%s: TDM TLMM pinctrl set failed with %d\n", + __func__, ret); + } +} + +static struct snd_soc_ops sm6150_tdm_be_ops = { + .hw_params = sm6150_tdm_snd_hw_params, + .startup = sm6150_tdm_snd_startup, + .shutdown = sm6150_tdm_snd_shutdown +}; + +static int msm_fe_qos_prepare(struct snd_pcm_substream *substream) +{ + cpumask_t mask; + + if (pm_qos_request_active(&substream->latency_pm_qos_req)) + pm_qos_remove_request(&substream->latency_pm_qos_req); + + cpumask_clear(&mask); + cpumask_set_cpu(1, &mask); /* affine to core 1 */ + cpumask_set_cpu(2, &mask); /* affine to core 2 */ + cpumask_copy(&substream->latency_pm_qos_req.cpus_affine, &mask); + + substream->latency_pm_qos_req.type = PM_QOS_REQ_AFFINE_CORES; + + pm_qos_add_request(&substream->latency_pm_qos_req, + PM_QOS_CPU_DMA_LATENCY, + MSM_LL_QOS_VALUE); + return 0; +} + +static struct snd_soc_ops msm_fe_qos_ops = { + .prepare = msm_fe_qos_prepare, +}; + +static int msm_mi2s_snd_startup(struct snd_pcm_substream *substream) +{ + int ret = 0; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + int index = cpu_dai->id; + unsigned int fmt = SND_SOC_DAIFMT_CBS_CFS; + struct snd_soc_card *card = rtd->card; + struct msm_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card); + struct msm_pinctrl_info *pinctrl_info = &pdata->pinctrl_info; + int ret_pinctrl = 0; + + dev_dbg(rtd->card->dev, + "%s: substream = %s stream = %d, dai name %s, dai ID %d\n", + __func__, substream->name, substream->stream, + cpu_dai->name, cpu_dai->id); + + if (index < PRIM_MI2S || index >= MI2S_MAX) { + ret = -EINVAL; + dev_err(rtd->card->dev, + "%s: CPU DAI id (%d) out of range\n", + __func__, cpu_dai->id); + goto err; + } + /* + * Mutex protection in case the same MI2S + * interface using for both TX and RX so + * that the same clock won't be enable twice. + */ + mutex_lock(&mi2s_intf_conf[index].lock); + if (++mi2s_intf_conf[index].ref_cnt == 1) { + /* Check if msm needs to provide the clock to the interface */ + if (!mi2s_intf_conf[index].msm_is_mi2s_master) { + mi2s_clk[index].clk_id = mi2s_ebit_clk[index]; + fmt = SND_SOC_DAIFMT_CBM_CFM; + } + ret = msm_mi2s_set_sclk(substream, true); + if (ret < 0) { + dev_err(rtd->card->dev, + "%s: afe lpass clock failed to enable MI2S clock, err:%d\n", + __func__, ret); + goto clean_up; + } + + ret = snd_soc_dai_set_fmt(cpu_dai, fmt); + if (ret < 0) { + pr_err("%s: set fmt cpu dai failed for MI2S (%d), err:%d\n", + __func__, index, ret); + goto clk_off; + } + if (index == QUAT_MI2S) { + ret_pinctrl = msm_set_pinctrl(pinctrl_info, + STATE_MI2S_ACTIVE); + if (ret_pinctrl) + pr_err("%s: MI2S TLMM pinctrl set failed with %d\n", + __func__, ret_pinctrl); + } + } +clk_off: + if (ret < 0) + msm_mi2s_set_sclk(substream, false); +clean_up: + if (ret < 0) + mi2s_intf_conf[index].ref_cnt--; + mutex_unlock(&mi2s_intf_conf[index].lock); +err: + return ret; +} + +static void msm_mi2s_snd_shutdown(struct snd_pcm_substream *substream) +{ + int ret; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + int index = rtd->cpu_dai->id; + struct snd_soc_card *card = rtd->card; + struct msm_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card); + struct msm_pinctrl_info *pinctrl_info = &pdata->pinctrl_info; + int ret_pinctrl = 0; + + pr_debug("%s(): substream = %s stream = %d\n", __func__, + substream->name, substream->stream); + if (index < PRIM_MI2S || index >= MI2S_MAX) { + pr_err("%s:invalid MI2S DAI(%d)\n", __func__, index); + return; + } + + mutex_lock(&mi2s_intf_conf[index].lock); + if (--mi2s_intf_conf[index].ref_cnt == 0) { + ret = msm_mi2s_set_sclk(substream, false); + if (ret < 0) + pr_err("%s:clock disable failed for MI2S (%d); ret=%d\n", + __func__, index, ret); + if (index == QUAT_MI2S) { + ret_pinctrl = msm_set_pinctrl(pinctrl_info, + STATE_DISABLE); + if (ret_pinctrl) + pr_err("%s: MI2S TLMM pinctrl set failed with %d\n", + __func__, ret_pinctrl); + } + } + mutex_unlock(&mi2s_intf_conf[index].lock); +} + +static struct snd_soc_ops msm_mi2s_be_ops = { + .startup = msm_mi2s_snd_startup, + .shutdown = msm_mi2s_snd_shutdown, +}; + +static struct snd_soc_ops msm_cdc_dma_be_ops = { + .hw_params = msm_snd_cdc_dma_hw_params, +}; + +static struct snd_soc_ops msm_be_ops = { + .hw_params = msm_snd_hw_params, +}; + +static struct snd_soc_ops msm_slimbus_2_be_ops = { + .hw_params = msm_slimbus_2_hw_params, +}; + +static struct snd_soc_ops msm_wcn_ops = { + .hw_params = msm_wcn_hw_params, +}; + +static struct snd_soc_ops msm_ext_cpe_ops = { + .hw_params = msm_snd_cpe_hw_params, +}; + +/* Digital audio interface glue - connects codec <---> CPU */ +static struct snd_soc_dai_link msm_common_dai_links[] = { + /* FrontEnd DAI Links */ + {/* hw:x,0 */ + .name = MSM_DAILINK_NAME(Media1), + .stream_name = "MultiMedia1", + .cpu_dai_name = "MultiMedia1", + .platform_name = "msm-pcm-dsp.0", + .dynamic = 1, + .async_ops = ASYNC_DPCM_SND_SOC_PREPARE, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .id = MSM_FRONTEND_DAI_MULTIMEDIA1 + }, + {/* hw:x,1 */ + .name = MSM_DAILINK_NAME(Media2), + .stream_name = "MultiMedia2", + .cpu_dai_name = "MultiMedia2", + .platform_name = "msm-pcm-dsp.0", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .id = MSM_FRONTEND_DAI_MULTIMEDIA2, + }, + {/* hw:x,2 */ + .name = "VoiceMMode1", + .stream_name = "VoiceMMode1", + .cpu_dai_name = "VoiceMMode1", + .platform_name = "msm-pcm-voice", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 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", + .id = MSM_FRONTEND_DAI_VOICEMMODE1, + }, + {/* hw:x,3 */ + .name = "MSM VoIP", + .stream_name = "VoIP", + .cpu_dai_name = "VoIP", + .platform_name = "msm-voip-dsp", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .id = MSM_FRONTEND_DAI_VOIP, + }, + {/* hw:x,4 */ + .name = MSM_DAILINK_NAME(ULL), + .stream_name = "MultiMedia3", + .cpu_dai_name = "MultiMedia3", + .platform_name = "msm-pcm-dsp.2", + .dynamic = 1, + .async_ops = ASYNC_DPCM_SND_SOC_PREPARE, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .id = MSM_FRONTEND_DAI_MULTIMEDIA3, + }, + /* Hostless PCM purpose */ + {/* hw:x,5 */ + .name = "SLIMBUS_0 Hostless", + .stream_name = "SLIMBUS_0 Hostless", + .cpu_dai_name = "SLIMBUS0_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + /* this dailink has playback support */ + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + {/* hw:x,6 */ + .name = "MSM AFE-PCM RX", + .stream_name = "AFE-PROXY RX", + .cpu_dai_name = "msm-dai-q6-dev.241", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .platform_name = "msm-pcm-afe", + .dpcm_playback = 1, + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + }, + {/* hw:x,7 */ + .name = "MSM AFE-PCM TX", + .stream_name = "AFE-PROXY TX", + .cpu_dai_name = "msm-dai-q6-dev.240", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .platform_name = "msm-pcm-afe", + .dpcm_capture = 1, + .ignore_suspend = 1, + }, + {/* hw:x,8 */ + .name = MSM_DAILINK_NAME(Compress1), + .stream_name = "Compress1", + .cpu_dai_name = "MultiMedia4", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .async_ops = ASYNC_DPCM_SND_SOC_HW_PARAMS, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dainlink has playback support */ + .id = MSM_FRONTEND_DAI_MULTIMEDIA4, + }, + {/* hw:x,9 */ + .name = "AUXPCM Hostless", + .stream_name = "AUXPCM Hostless", + .cpu_dai_name = "AUXPCM_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + {/* hw:x,10 */ + .name = "SLIMBUS_1 Hostless", + .stream_name = "SLIMBUS_1 Hostless", + .cpu_dai_name = "SLIMBUS1_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + /* this dailink has playback support */ + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + {/* hw:x,11 */ + .name = "SLIMBUS_3 Hostless", + .stream_name = "SLIMBUS_3 Hostless", + .cpu_dai_name = "SLIMBUS3_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + /* this dailink has playback support */ + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + {/* hw:x,12 */ + .name = "SLIMBUS_7 Hostless", + .stream_name = "SLIMBUS_7 Hostless", + .cpu_dai_name = "SLIMBUS7_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + /* this dailink has playback support */ + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + {/* hw:x,13 */ + .name = MSM_DAILINK_NAME(LowLatency), + .stream_name = "MultiMedia5", + .cpu_dai_name = "MultiMedia5", + .platform_name = "msm-pcm-dsp.1", + .dynamic = 1, + .async_ops = ASYNC_DPCM_SND_SOC_PREPARE, + .dpcm_playback = 1, + .dpcm_capture = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .id = MSM_FRONTEND_DAI_MULTIMEDIA5, + .ops = &msm_fe_qos_ops, + }, + {/* hw:x,14 */ + .name = "Listen 1 Audio Service", + .stream_name = "Listen 1 Audio Service", + .cpu_dai_name = "LSM1", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = { SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST }, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .id = MSM_FRONTEND_DAI_LSM1, + }, + /* Multiple Tunnel instances */ + {/* hw:x,15 */ + .name = MSM_DAILINK_NAME(Compress2), + .stream_name = "Compress2", + .cpu_dai_name = "MultiMedia7", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dainlink has playback support */ + .id = MSM_FRONTEND_DAI_MULTIMEDIA7, + }, + {/* hw:x,16 */ + .name = MSM_DAILINK_NAME(MultiMedia10), + .stream_name = "MultiMedia10", + .cpu_dai_name = "MultiMedia10", + .platform_name = "msm-pcm-dsp.1", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dainlink has playback support */ + .id = MSM_FRONTEND_DAI_MULTIMEDIA10, + }, + {/* hw:x,17 */ + .name = MSM_DAILINK_NAME(ULL_NOIRQ), + .stream_name = "MM_NOIRQ", + .cpu_dai_name = "MultiMedia8", + .platform_name = "msm-pcm-dsp-noirq", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dainlink has playback support */ + .id = MSM_FRONTEND_DAI_MULTIMEDIA8, + .ops = &msm_fe_qos_ops, + }, + /* HDMI Hostless */ + {/* hw:x,18 */ + .name = "HDMI_RX_HOSTLESS", + .stream_name = "HDMI_RX_HOSTLESS", + .cpu_dai_name = "HDMI_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .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", + }, + {/* hw:x,19 */ + .name = "VoiceMMode2", + .stream_name = "VoiceMMode2", + .cpu_dai_name = "VoiceMMode2", + .platform_name = "msm-pcm-voice", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 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", + .id = MSM_FRONTEND_DAI_VOICEMMODE2, + }, + /* LSM FE */ + {/* hw:x,20 */ + .name = "Listen 2 Audio Service", + .stream_name = "Listen 2 Audio Service", + .cpu_dai_name = "LSM2", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = { SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST }, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .id = MSM_FRONTEND_DAI_LSM2, + }, + {/* hw:x,21 */ + .name = "Listen 3 Audio Service", + .stream_name = "Listen 3 Audio Service", + .cpu_dai_name = "LSM3", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = { SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST }, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .id = MSM_FRONTEND_DAI_LSM3, + }, + {/* hw:x,22 */ + .name = "Listen 4 Audio Service", + .stream_name = "Listen 4 Audio Service", + .cpu_dai_name = "LSM4", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = { SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST }, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .id = MSM_FRONTEND_DAI_LSM4, + }, + {/* hw:x,23 */ + .name = "Listen 5 Audio Service", + .stream_name = "Listen 5 Audio Service", + .cpu_dai_name = "LSM5", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = { SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST }, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .id = MSM_FRONTEND_DAI_LSM5, + }, + {/* hw:x,24 */ + .name = "Listen 6 Audio Service", + .stream_name = "Listen 6 Audio Service", + .cpu_dai_name = "LSM6", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = { SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST }, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .id = MSM_FRONTEND_DAI_LSM6, + }, + {/* hw:x,25 */ + .name = "Listen 7 Audio Service", + .stream_name = "Listen 7 Audio Service", + .cpu_dai_name = "LSM7", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = { SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST }, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .id = MSM_FRONTEND_DAI_LSM7, + }, + {/* hw:x,26 */ + .name = "Listen 8 Audio Service", + .stream_name = "Listen 8 Audio Service", + .cpu_dai_name = "LSM8", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = { SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST }, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .id = MSM_FRONTEND_DAI_LSM8, + }, + {/* hw:x,27 */ + .name = MSM_DAILINK_NAME(Media9), + .stream_name = "MultiMedia9", + .cpu_dai_name = "MultiMedia9", + .platform_name = "msm-pcm-dsp.0", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .id = MSM_FRONTEND_DAI_MULTIMEDIA9, + }, + {/* hw:x,28 */ + .name = MSM_DAILINK_NAME(Compress4), + .stream_name = "Compress4", + .cpu_dai_name = "MultiMedia11", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dainlink has playback support */ + .id = MSM_FRONTEND_DAI_MULTIMEDIA11, + }, + {/* hw:x,29 */ + .name = MSM_DAILINK_NAME(Compress5), + .stream_name = "Compress5", + .cpu_dai_name = "MultiMedia12", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dainlink has playback support */ + .id = MSM_FRONTEND_DAI_MULTIMEDIA12, + }, + {/* hw:x,30 */ + .name = MSM_DAILINK_NAME(Compress6), + .stream_name = "Compress6", + .cpu_dai_name = "MultiMedia13", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dainlink has playback support */ + .id = MSM_FRONTEND_DAI_MULTIMEDIA13, + }, + {/* hw:x,31 */ + .name = MSM_DAILINK_NAME(Compress7), + .stream_name = "Compress7", + .cpu_dai_name = "MultiMedia14", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dainlink has playback support */ + .id = MSM_FRONTEND_DAI_MULTIMEDIA14, + }, + {/* hw:x,32 */ + .name = MSM_DAILINK_NAME(Compress8), + .stream_name = "Compress8", + .cpu_dai_name = "MultiMedia15", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dainlink has playback support */ + .id = MSM_FRONTEND_DAI_MULTIMEDIA15, + }, + {/* hw:x,33 */ + .name = MSM_DAILINK_NAME(ULL_NOIRQ_2), + .stream_name = "MM_NOIRQ_2", + .cpu_dai_name = "MultiMedia16", + .platform_name = "msm-pcm-dsp-noirq", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dainlink has playback support */ + .id = MSM_FRONTEND_DAI_MULTIMEDIA16, + }, + {/* hw:x,34 */ + .name = "SLIMBUS_8 Hostless", + .stream_name = "SLIMBUS8_HOSTLESS Capture", + .cpu_dai_name = "SLIMBUS8_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + {/* hw:x,35 */ + .name = "CDC_DMA Hostless", + .stream_name = "CDC_DMA Hostless", + .cpu_dai_name = "CDC_DMA_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + /* this dailink has playback support */ + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + {/* hw:x,36 */ + .name = "TX3_CDC_DMA Hostless", + .stream_name = "TX3_CDC_DMA Hostless", + .cpu_dai_name = "TX3_CDC_DMA_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, +}; + +static struct snd_soc_dai_link msm_tavil_fe_dai_links[] = { + {/* hw:x,37 */ + .name = LPASS_BE_SLIMBUS_4_TX, + .stream_name = "Slimbus4 Capture", + .cpu_dai_name = "msm-dai-q6-dev.16393", + .platform_name = "msm-pcm-hostless", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_vifeedback", + .id = MSM_BACKEND_DAI_SLIMBUS_4_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_be_ops, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + }, + /* Ultrasound RX DAI Link */ + {/* hw:x,38 */ + .name = "SLIMBUS_2 Hostless Playback", + .stream_name = "SLIMBUS_2 Hostless Playback", + .cpu_dai_name = "msm-dai-q6-dev.16388", + .platform_name = "msm-pcm-hostless", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_rx2", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ops = &msm_slimbus_2_be_ops, + }, + /* Ultrasound TX DAI Link */ + {/* hw:x,39 */ + .name = "SLIMBUS_2 Hostless Capture", + .stream_name = "SLIMBUS_2 Hostless Capture", + .cpu_dai_name = "msm-dai-q6-dev.16389", + .platform_name = "msm-pcm-hostless", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_tx2", + .ignore_suspend = 1, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ops = &msm_slimbus_2_be_ops, + }, +}; + +static struct snd_soc_dai_link msm_int_compress_capture_dai[] = { + { + .name = "Compress9", + .stream_name = "Compress9", + .cpu_dai_name = "MultiMedia17", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .id = MSM_FRONTEND_DAI_MULTIMEDIA17, + }, + { + .name = "Compress10", + .stream_name = "Compress10", + .cpu_dai_name = "MultiMedia18", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .id = MSM_FRONTEND_DAI_MULTIMEDIA18, + }, + { + .name = "Compress11", + .stream_name = "Compress11", + .cpu_dai_name = "MultiMedia19", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .id = MSM_FRONTEND_DAI_MULTIMEDIA19, + }, + { + .name = "Compress12", + .stream_name = "Compress12", + .cpu_dai_name = "MultiMedia28", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .id = MSM_FRONTEND_DAI_MULTIMEDIA28, + }, + { + .name = "Compress13", + .stream_name = "Compress13", + .cpu_dai_name = "MultiMedia29", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .id = MSM_FRONTEND_DAI_MULTIMEDIA29, + }, +}; + +static struct snd_soc_dai_link msm_bolero_fe_dai_links[] = { + {/* hw:x,37 */ + .name = LPASS_BE_WSA_CDC_DMA_TX_0, + .stream_name = "WSA CDC DMA0 Capture", + .cpu_dai_name = "msm-dai-cdc-dma-dev.45057", + .platform_name = "msm-pcm-hostless", + .codec_name = "bolero_codec", + .codec_dai_name = "wsa_macro_vifeedback", + .id = MSM_BACKEND_DAI_WSA_CDC_DMA_TX_0, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ops = &msm_cdc_dma_be_ops, + }, +}; + +static struct snd_soc_dai_link msm_tasha_fe_dai_links[] = { + /* tasha_vifeedback for speaker protection */ + { + .name = LPASS_BE_SLIMBUS_4_TX, + .stream_name = "Slimbus4 Capture", + .cpu_dai_name = "msm-dai-q6-dev.16393", + .platform_name = "msm-pcm-hostless", + .codec_name = "tasha_codec", + .codec_dai_name = "tasha_vifeedback", + .id = MSM_BACKEND_DAI_SLIMBUS_4_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_be_ops, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + }, + /* Ultrasound RX DAI Link */ + { + .name = "SLIMBUS_2 Hostless Playback", + .stream_name = "SLIMBUS_2 Hostless Playback", + .cpu_dai_name = "msm-dai-q6-dev.16388", + .platform_name = "msm-pcm-hostless", + .codec_name = "tasha_codec", + .codec_dai_name = "tasha_rx2", + .ignore_suspend = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .ignore_pmdown_time = 1, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ops = &msm_slimbus_2_be_ops, + }, + /* Ultrasound TX DAI Link */ + { + .name = "SLIMBUS_2 Hostless Capture", + .stream_name = "SLIMBUS_2 Hostless Capture", + .cpu_dai_name = "msm-dai-q6-dev.16389", + .platform_name = "msm-pcm-hostless", + .codec_name = "tasha_codec", + .codec_dai_name = "tasha_tx2", + .ignore_suspend = 1, + .dpcm_capture = 1, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ops = &msm_slimbus_2_be_ops, + }, + /* CPE LSM direct dai-link */ + { + .name = "CPE Listen service", + .stream_name = "CPE Listen Audio Service", + .cpu_dai_name = "msm-dai-slim", + .platform_name = "msm-cpe-lsm", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .dpcm_capture = 1, + .codec_dai_name = "tasha_mad1", + .codec_name = "tasha_codec", + .ops = &msm_ext_cpe_ops, + }, + { + .name = "SLIMBUS_6 Hostless Playback", + .stream_name = "SLIMBUS_6 Hostless", + .cpu_dai_name = "SLIMBUS6_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .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, + /* this dailink has playback support */ + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + /* CPE LSM EC PP direct dai-link */ + { + .name = "CPE Listen service ECPP", + .stream_name = "CPE Listen Audio Service ECPP", + .cpu_dai_name = "CPE_LSM_NOHOST", + .platform_name = "msm-cpe-lsm.3", + .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 = "tasha_cpe", + .codec_name = "tasha_codec", + }, +}; + +static struct snd_soc_dai_link msm_common_misc_fe_dai_links[] = { + { + .name = MSM_DAILINK_NAME(ASM Loopback), + .stream_name = "MultiMedia6", + .cpu_dai_name = "MultiMedia6", + .platform_name = "msm-pcm-loopback", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .ignore_suspend = 1, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_pmdown_time = 1, + .id = MSM_FRONTEND_DAI_MULTIMEDIA6, + }, + { + .name = "USB Audio Hostless", + .stream_name = "USB Audio Hostless", + .cpu_dai_name = "USBAUDIO_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 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", + }, +}; + +static struct snd_soc_dai_link msm_common_be_dai_links[] = { + /* Backend AFE DAI Links */ + { + .name = LPASS_BE_AFE_PCM_RX, + .stream_name = "AFE Playback", + .cpu_dai_name = "msm-dai-q6-dev.224", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_AFE_PCM_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_AFE_PCM_TX, + .stream_name = "AFE Capture", + .cpu_dai_name = "msm-dai-q6-dev.225", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_AFE_PCM_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + }, + /* Incall Record Uplink BACK END DAI Link */ + { + .name = LPASS_BE_INCALL_RECORD_TX, + .stream_name = "Voice Uplink Capture", + .cpu_dai_name = "msm-dai-q6-dev.32772", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_INCALL_RECORD_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + }, + /* Incall Record Downlink BACK END DAI Link */ + { + .name = LPASS_BE_INCALL_RECORD_RX, + .stream_name = "Voice Downlink Capture", + .cpu_dai_name = "msm-dai-q6-dev.32771", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_INCALL_RECORD_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + }, + /* Incall Music BACK END DAI Link */ + { + .name = LPASS_BE_VOICE_PLAYBACK_TX, + .stream_name = "Voice Farend Playback", + .cpu_dai_name = "msm-dai-q6-dev.32773", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_VOICE_PLAYBACK_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + }, + /* Incall Music 2 BACK END DAI Link */ + { + .name = LPASS_BE_VOICE2_PLAYBACK_TX, + .stream_name = "Voice2 Farend Playback", + .cpu_dai_name = "msm-dai-q6-dev.32770", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_VOICE2_PLAYBACK_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + }, + { + .name = LPASS_BE_USB_AUDIO_RX, + .stream_name = "USB Audio Playback", + .cpu_dai_name = "msm-dai-q6-dev.28672", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_USB_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_USB_AUDIO_TX, + .stream_name = "USB Audio Capture", + .cpu_dai_name = "msm-dai-q6-dev.28673", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_USB_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_PRI_TDM_RX_0, + .stream_name = "Primary TDM0 Playback", + .cpu_dai_name = "msm-dai-q6-tdm.36864", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_PRI_TDM_RX_0, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &sm6150_tdm_be_ops, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + }, + { + .name = LPASS_BE_PRI_TDM_TX_0, + .stream_name = "Primary TDM0 Capture", + .cpu_dai_name = "msm-dai-q6-tdm.36865", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_PRI_TDM_TX_0, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &sm6150_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SEC_TDM_RX_0, + .stream_name = "Secondary TDM0 Playback", + .cpu_dai_name = "msm-dai-q6-tdm.36880", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SEC_TDM_RX_0, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &sm6150_tdm_be_ops, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + }, + { + .name = LPASS_BE_SEC_TDM_TX_0, + .stream_name = "Secondary TDM0 Capture", + .cpu_dai_name = "msm-dai-q6-tdm.36881", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_SEC_TDM_TX_0, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &sm6150_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_TERT_TDM_RX_0, + .stream_name = "Tertiary TDM0 Playback", + .cpu_dai_name = "msm-dai-q6-tdm.36896", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_TERT_TDM_RX_0, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &sm6150_tdm_be_ops, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + }, + { + .name = LPASS_BE_TERT_TDM_TX_0, + .stream_name = "Tertiary TDM0 Capture", + .cpu_dai_name = "msm-dai-q6-tdm.36897", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_TERT_TDM_TX_0, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &sm6150_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_QUAT_TDM_RX_0, + .stream_name = "Quaternary TDM0 Playback", + .cpu_dai_name = "msm-dai-q6-tdm.36912", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_QUAT_TDM_RX_0, + .be_hw_params_fixup = msm_tdm_be_hw_params_fixup, + .ops = &sm6150_tdm_be_ops, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + }, + { + .name = LPASS_BE_QUAT_TDM_TX_0, + .stream_name = "Quaternary TDM0 Capture", + .cpu_dai_name = "msm-dai-q6-tdm.36913", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_QUAT_TDM_TX_0, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &sm6150_tdm_be_ops, + .ignore_suspend = 1, + }, +}; + +static struct snd_soc_dai_link msm_tavil_be_dai_links[] = { + { + .name = LPASS_BE_SLIMBUS_0_RX, + .stream_name = "Slimbus Playback", + .cpu_dai_name = "msm-dai-q6-dev.16384", + .platform_name = "msm-pcm-routing", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_rx1", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_0_RX, + .init = &msm_audrx_tavil_init, + .be_hw_params_fixup = msm_be_hw_params_fixup, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + .ops = &msm_be_ops, + }, + { + .name = LPASS_BE_SLIMBUS_0_TX, + .stream_name = "Slimbus Capture", + .cpu_dai_name = "msm-dai-q6-dev.16385", + .platform_name = "msm-pcm-routing", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_tx1", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_0_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + .ops = &msm_be_ops, + }, + { + .name = LPASS_BE_SLIMBUS_1_RX, + .stream_name = "Slimbus1 Playback", + .cpu_dai_name = "msm-dai-q6-dev.16386", + .platform_name = "msm-pcm-routing", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_rx1", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_1_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_be_ops, + /* dai link has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_1_TX, + .stream_name = "Slimbus1 Capture", + .cpu_dai_name = "msm-dai-q6-dev.16387", + .platform_name = "msm-pcm-routing", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_tx3", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_1_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_2_RX, + .stream_name = "Slimbus2 Playback", + .cpu_dai_name = "msm-dai-q6-dev.16388", + .platform_name = "msm-pcm-routing", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_rx2", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_2_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_be_ops, + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_3_RX, + .stream_name = "Slimbus3 Playback", + .cpu_dai_name = "msm-dai-q6-dev.16390", + .platform_name = "msm-pcm-routing", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_rx1", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_3_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_be_ops, + /* dai link has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_3_TX, + .stream_name = "Slimbus3 Capture", + .cpu_dai_name = "msm-dai-q6-dev.16391", + .platform_name = "msm-pcm-routing", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_tx1", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_3_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_4_RX, + .stream_name = "Slimbus4 Playback", + .cpu_dai_name = "msm-dai-q6-dev.16392", + .platform_name = "msm-pcm-routing", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_rx1", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_4_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_be_ops, + /* dai link has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_5_RX, + .stream_name = "Slimbus5 Playback", + .cpu_dai_name = "msm-dai-q6-dev.16394", + .platform_name = "msm-pcm-routing", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_rx3", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_5_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_be_ops, + /* dai link has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + /* MAD BE */ + { + .name = LPASS_BE_SLIMBUS_5_TX, + .stream_name = "Slimbus5 Capture", + .cpu_dai_name = "msm-dai-q6-dev.16395", + .platform_name = "msm-pcm-routing", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_mad1", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_5_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_6_RX, + .stream_name = "Slimbus6 Playback", + .cpu_dai_name = "msm-dai-q6-dev.16396", + .platform_name = "msm-pcm-routing", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_rx4", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_6_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_be_ops, + /* dai link has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + /* Slimbus VI Recording */ + { + .name = LPASS_BE_SLIMBUS_TX_VI, + .stream_name = "Slimbus4 Capture", + .cpu_dai_name = "msm-dai-q6-dev.16393", + .platform_name = "msm-pcm-routing", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_vifeedback", + .id = MSM_BACKEND_DAI_SLIMBUS_4_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_be_ops, + .ignore_suspend = 1, + .no_pcm = 1, + .dpcm_capture = 1, + }, +}; + +static struct snd_soc_dai_link msm_tasha_be_dai_links[] = { + /* Backend DAI Links */ + { + .name = LPASS_BE_SLIMBUS_0_RX, + .stream_name = "Slimbus Playback", + .cpu_dai_name = "msm-dai-q6-dev.16384", + .platform_name = "msm-pcm-routing", + .codec_name = "tasha_codec", + .codec_dai_name = "tasha_mix_rx1", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_0_RX, + .init = &msm_audrx_tasha_init, + .be_hw_params_fixup = msm_be_hw_params_fixup, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + .ops = &msm_be_ops, + }, + { + .name = LPASS_BE_SLIMBUS_0_TX, + .stream_name = "Slimbus Capture", + .cpu_dai_name = "msm-dai-q6-dev.16385", + .platform_name = "msm-pcm-routing", + .codec_name = "tasha_codec", + .codec_dai_name = "tasha_tx1", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_0_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + .ops = &msm_be_ops, + }, + { + .name = LPASS_BE_SLIMBUS_1_RX, + .stream_name = "Slimbus1 Playback", + .cpu_dai_name = "msm-dai-q6-dev.16386", + .platform_name = "msm-pcm-routing", + .codec_name = "tasha_codec", + .codec_dai_name = "tasha_mix_rx1", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_1_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_be_ops, + /* dai link has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_1_TX, + .stream_name = "Slimbus1 Capture", + .cpu_dai_name = "msm-dai-q6-dev.16387", + .platform_name = "msm-pcm-routing", + .codec_name = "tasha_codec", + .codec_dai_name = "tasha_tx3", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_1_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_3_RX, + .stream_name = "Slimbus3 Playback", + .cpu_dai_name = "msm-dai-q6-dev.16390", + .platform_name = "msm-pcm-routing", + .codec_name = "tasha_codec", + .codec_dai_name = "tasha_mix_rx1", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_3_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_be_ops, + /* dai link has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_3_TX, + .stream_name = "Slimbus3 Capture", + .cpu_dai_name = "msm-dai-q6-dev.16391", + .platform_name = "msm-pcm-routing", + .codec_name = "tasha_codec", + .codec_dai_name = "tasha_tx1", + .no_pcm = 1, + .dpcm_capture = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_3_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_4_RX, + .stream_name = "Slimbus4 Playback", + .cpu_dai_name = "msm-dai-q6-dev.16392", + .platform_name = "msm-pcm-routing", + .codec_name = "tasha_codec", + .codec_dai_name = "tasha_mix_rx1", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_4_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_be_ops, + /* dai link has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_5_RX, + .stream_name = "Slimbus5 Playback", + .cpu_dai_name = "msm-dai-q6-dev.16394", + .platform_name = "msm-pcm-routing", + .codec_name = "tasha_codec", + .codec_dai_name = "tasha_rx3", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_5_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_be_ops, + /* dai link has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + /* MAD BE */ + { + .name = LPASS_BE_SLIMBUS_5_TX, + .stream_name = "Slimbus5 Capture", + .cpu_dai_name = "msm-dai-q6-dev.16395", + .platform_name = "msm-pcm-routing", + .codec_name = "tasha_codec", + .codec_dai_name = "tasha_mad1", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_5_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_6_RX, + .stream_name = "Slimbus6 Playback", + .cpu_dai_name = "msm-dai-q6-dev.16396", + .platform_name = "msm-pcm-routing", + .codec_name = "tasha_codec", + .codec_dai_name = "tasha_rx4", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_6_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_be_ops, + /* dai link has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, +}; + +static struct snd_soc_dai_link msm_wcn_be_dai_links[] = { + { + .name = LPASS_BE_SLIMBUS_7_RX, + .stream_name = "Slimbus7 Playback", + .cpu_dai_name = "msm-dai-q6-dev.16398", + .platform_name = "msm-pcm-routing", + .codec_name = "btfmslim_slave", + /* BT codec driver determines capabilities based on + * dai name, bt codecdai name should always contains + * supported usecase information + */ + .codec_dai_name = "btfm_bt_sco_a2dp_slim_rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_7_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_wcn_ops, + /* dai link has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_7_TX, + .stream_name = "Slimbus7 Capture", + .cpu_dai_name = "msm-dai-q6-dev.16399", + .platform_name = "msm-pcm-routing", + .codec_name = "btfmslim_slave", + .codec_dai_name = "btfm_bt_sco_slim_tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_7_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_wcn_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_8_TX, + .stream_name = "Slimbus8 Capture", + .cpu_dai_name = "msm-dai-q6-dev.16401", + .platform_name = "msm-pcm-routing", + .codec_name = "btfmslim_slave", + .codec_dai_name = "btfm_fm_slim_tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_8_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .init = &msm_wcn_init, + .ops = &msm_wcn_ops, + .ignore_suspend = 1, + }, +}; + +static struct snd_soc_dai_link ext_disp_be_dai_link[] = { + /* DISP PORT BACK END DAI Link */ + { + .name = LPASS_BE_DISPLAY_PORT, + .stream_name = "Display Port Playback", + .cpu_dai_name = "msm-dai-q6-dp.24608", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-ext-disp-audio-codec-rx", + .codec_dai_name = "msm_dp_audio_codec_rx_dai", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_DISPLAY_PORT_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, +}; + +static struct snd_soc_dai_link msm_mi2s_be_dai_links[] = { + { + .name = LPASS_BE_PRI_MI2S_RX, + .stream_name = "Primary MI2S Playback", + .cpu_dai_name = "msm-dai-q6-mi2s.0", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_PRI_MI2S_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + }, + { + .name = LPASS_BE_PRI_MI2S_TX, + .stream_name = "Primary MI2S Capture", + .cpu_dai_name = "msm-dai-q6-mi2s.0", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_PRI_MI2S_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SEC_MI2S_RX, + .stream_name = "Secondary MI2S Playback", + .cpu_dai_name = "msm-dai-q6-mi2s.1", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SECONDARY_MI2S_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + }, + { + .name = LPASS_BE_SEC_MI2S_TX, + .stream_name = "Secondary MI2S Capture", + .cpu_dai_name = "msm-dai-q6-mi2s.1", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_SECONDARY_MI2S_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_TERT_MI2S_RX, + .stream_name = "Tertiary MI2S Playback", + .cpu_dai_name = "msm-dai-q6-mi2s.2", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_TERTIARY_MI2S_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + }, + { + .name = LPASS_BE_TERT_MI2S_TX, + .stream_name = "Tertiary MI2S Capture", + .cpu_dai_name = "msm-dai-q6-mi2s.2", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_TERTIARY_MI2S_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_QUAT_MI2S_RX, + .stream_name = "Quaternary MI2S Playback", + .cpu_dai_name = "msm-dai-q6-mi2s.3", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_QUATERNARY_MI2S_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + }, + { + .name = LPASS_BE_QUAT_MI2S_TX, + .stream_name = "Quaternary MI2S Capture", + .cpu_dai_name = "msm-dai-q6-mi2s.3", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_QUIN_MI2S_RX, + .stream_name = "Quinary MI2S Playback", + .cpu_dai_name = "msm-dai-q6-mi2s.4", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_QUINARY_MI2S_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + }, + { + .name = LPASS_BE_QUIN_MI2S_TX, + .stream_name = "Quinary MI2S Capture", + .cpu_dai_name = "msm-dai-q6-mi2s.4", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_QUINARY_MI2S_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + }, + +}; + +static struct snd_soc_dai_link msm_auxpcm_be_dai_links[] = { + /* Primary AUX PCM Backend DAI Links */ + { + .name = LPASS_BE_AUXPCM_RX, + .stream_name = "AUX PCM Playback", + .cpu_dai_name = "msm-dai-q6-auxpcm.1", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_AUXPCM_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_AUXPCM_TX, + .stream_name = "AUX PCM Capture", + .cpu_dai_name = "msm-dai-q6-auxpcm.1", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_AUXPCM_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + }, + /* Secondary AUX PCM Backend DAI Links */ + { + .name = LPASS_BE_SEC_AUXPCM_RX, + .stream_name = "Sec AUX PCM Playback", + .cpu_dai_name = "msm-dai-q6-auxpcm.2", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SEC_AUXPCM_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SEC_AUXPCM_TX, + .stream_name = "Sec AUX PCM Capture", + .cpu_dai_name = "msm-dai-q6-auxpcm.2", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_SEC_AUXPCM_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + }, + /* Tertiary AUX PCM Backend DAI Links */ + { + .name = LPASS_BE_TERT_AUXPCM_RX, + .stream_name = "Tert AUX PCM Playback", + .cpu_dai_name = "msm-dai-q6-auxpcm.3", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_TERT_AUXPCM_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_TERT_AUXPCM_TX, + .stream_name = "Tert AUX PCM Capture", + .cpu_dai_name = "msm-dai-q6-auxpcm.3", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_TERT_AUXPCM_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + }, + /* Quaternary AUX PCM Backend DAI Links */ + { + .name = LPASS_BE_QUAT_AUXPCM_RX, + .stream_name = "Quat AUX PCM Playback", + .cpu_dai_name = "msm-dai-q6-auxpcm.4", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_QUAT_AUXPCM_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_QUAT_AUXPCM_TX, + .stream_name = "Quat AUX PCM Capture", + .cpu_dai_name = "msm-dai-q6-auxpcm.4", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_QUAT_AUXPCM_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + }, + /* Quinary AUX PCM Backend DAI Links */ + { + .name = LPASS_BE_QUIN_AUXPCM_RX, + .stream_name = "Quin AUX PCM Playback", + .cpu_dai_name = "msm-dai-q6-auxpcm.5", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_QUIN_AUXPCM_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_QUIN_AUXPCM_TX, + .stream_name = "Quin AUX PCM Capture", + .cpu_dai_name = "msm-dai-q6-auxpcm.5", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_QUIN_AUXPCM_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + }, +}; + +static struct snd_soc_dai_link msm_wsa_cdc_dma_be_dai_links[] = { + /* WSA CDC DMA Backend DAI Links */ + { + .name = LPASS_BE_WSA_CDC_DMA_RX_0, + .stream_name = "WSA CDC DMA0 Playback", + .cpu_dai_name = "msm-dai-cdc-dma-dev.45056", + .platform_name = "msm-pcm-routing", + .codec_name = "bolero_codec", + .codec_dai_name = "wsa_macro_rx1", + .no_pcm = 1, + .dpcm_playback = 1, + .init = &msm_int_audrx_init, + .id = MSM_BACKEND_DAI_WSA_CDC_DMA_RX_0, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + .ops = &msm_cdc_dma_be_ops, + }, + { + .name = LPASS_BE_WSA_CDC_DMA_RX_1, + .stream_name = "WSA CDC DMA1 Playback", + .cpu_dai_name = "msm-dai-cdc-dma-dev.45058", + .platform_name = "msm-pcm-routing", + .codec_name = "bolero_codec", + .codec_dai_name = "wsa_macro_rx_mix", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_WSA_CDC_DMA_RX_1, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + .ops = &msm_cdc_dma_be_ops, + }, + { + .name = LPASS_BE_WSA_CDC_DMA_TX_1, + .stream_name = "WSA CDC DMA1 Capture", + .cpu_dai_name = "msm-dai-cdc-dma-dev.45059", + .platform_name = "msm-pcm-routing", + .codec_name = "bolero_codec", + .codec_dai_name = "wsa_macro_echo", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_WSA_CDC_DMA_TX_1, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + .ops = &msm_cdc_dma_be_ops, + }, +}; + +static struct snd_soc_dai_link msm_rx_tx_cdc_dma_be_dai_links[] = { + /* RX CDC DMA Backend DAI Links */ + { + .name = LPASS_BE_RX_CDC_DMA_RX_0, + .stream_name = "RX CDC DMA0 Playback", + .cpu_dai_name = "msm-dai-cdc-dma-dev.45104", + .platform_name = "msm-pcm-routing", + .codec_name = "bolero_codec", + .codec_dai_name = "rx_macro_rx1", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_RX_CDC_DMA_RX_0, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + .ops = &msm_cdc_dma_be_ops, + }, + { + .name = LPASS_BE_RX_CDC_DMA_RX_1, + .stream_name = "RX CDC DMA1 Playback", + .cpu_dai_name = "msm-dai-cdc-dma-dev.45106", + .platform_name = "msm-pcm-routing", + .codec_name = "bolero_codec", + .codec_dai_name = "rx_macro_rx2", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_RX_CDC_DMA_RX_1, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + .ops = &msm_cdc_dma_be_ops, + }, + { + .name = LPASS_BE_RX_CDC_DMA_RX_2, + .stream_name = "RX CDC DMA2 Playback", + .cpu_dai_name = "msm-dai-cdc-dma-dev.45108", + .platform_name = "msm-pcm-routing", + .codec_name = "bolero_codec", + .codec_dai_name = "rx_macro_rx3", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_RX_CDC_DMA_RX_2, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + .ops = &msm_cdc_dma_be_ops, + }, + { + .name = LPASS_BE_RX_CDC_DMA_RX_3, + .stream_name = "RX CDC DMA3 Playback", + .cpu_dai_name = "msm-dai-cdc-dma-dev.45110", + .platform_name = "msm-pcm-routing", + .codec_name = "bolero_codec", + .codec_dai_name = "rx_macro_rx4", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_RX_CDC_DMA_RX_3, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + .ops = &msm_cdc_dma_be_ops, + }, + /* TX CDC DMA Backend DAI Links */ + { + .name = LPASS_BE_TX_CDC_DMA_TX_0, + .stream_name = "TX CDC DMA0 Capture", + .cpu_dai_name = "msm-dai-cdc-dma-dev.45105", + .platform_name = "msm-pcm-routing", + .codec_name = "bolero_codec", + .codec_dai_name = "rx_macro_echo", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_TX_CDC_DMA_TX_0, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + .ops = &msm_cdc_dma_be_ops, + }, + { + .name = LPASS_BE_TX_CDC_DMA_TX_3, + .stream_name = "TX CDC DMA3 Capture", + .cpu_dai_name = "msm-dai-cdc-dma-dev.45111", + .platform_name = "msm-pcm-routing", + .codec_name = "bolero_codec", + .codec_dai_name = "tx_macro_tx1", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_TX_CDC_DMA_TX_3, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + .ops = &msm_cdc_dma_be_ops, + }, + { + .name = LPASS_BE_TX_CDC_DMA_TX_4, + .stream_name = "TX CDC DMA4 Capture", + .cpu_dai_name = "msm-dai-cdc-dma-dev.45113", + .platform_name = "msm-pcm-routing", + .codec_name = "bolero_codec", + .codec_dai_name = "tx_macro_tx2", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_TX_CDC_DMA_TX_4, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + .ops = &msm_cdc_dma_be_ops, + }, +}; + +static struct snd_soc_dai_link msm_sm6150_dai_links[ + ARRAY_SIZE(msm_common_dai_links) + + ARRAY_SIZE(msm_tavil_fe_dai_links) + + ARRAY_SIZE(msm_bolero_fe_dai_links) + + ARRAY_SIZE(msm_tasha_fe_dai_links) + + ARRAY_SIZE(msm_common_misc_fe_dai_links) + + ARRAY_SIZE(msm_int_compress_capture_dai) + + ARRAY_SIZE(msm_common_be_dai_links) + + ARRAY_SIZE(msm_tavil_be_dai_links) + + ARRAY_SIZE(msm_tasha_be_dai_links) + + ARRAY_SIZE(msm_wcn_be_dai_links) + + ARRAY_SIZE(ext_disp_be_dai_link) + + ARRAY_SIZE(msm_mi2s_be_dai_links) + + ARRAY_SIZE(msm_auxpcm_be_dai_links) + + ARRAY_SIZE(msm_wsa_cdc_dma_be_dai_links) + + ARRAY_SIZE(msm_rx_tx_cdc_dma_be_dai_links)]; + +static int msm_snd_card_tavil_late_probe(struct snd_soc_card *card) +{ + const char *be_dl_name = LPASS_BE_SLIMBUS_0_RX; + struct snd_soc_pcm_runtime *rtd; + int ret = 0; + void *mbhc_calibration; + + rtd = snd_soc_get_pcm_runtime(card, be_dl_name); + if (!rtd) { + dev_err(card->dev, + "%s: snd_soc_get_pcm_runtime for %s failed!\n", + __func__, be_dl_name); + ret = -EINVAL; + goto err_pcm_runtime; + } + + mbhc_calibration = def_wcd_mbhc_cal(); + if (!mbhc_calibration) { + ret = -ENOMEM; + goto err_mbhc_cal; + } + wcd_mbhc_cfg.calibration = mbhc_calibration; + ret = tavil_mbhc_hs_detect(rtd->codec, &wcd_mbhc_cfg); + if (ret) { + dev_err(card->dev, "%s: mbhc hs detect failed, err:%d\n", + __func__, ret); + goto err_hs_detect; + } + return 0; + +err_hs_detect: + kfree(mbhc_calibration); +err_mbhc_cal: +err_pcm_runtime: + return ret; +} + +static int msm_snd_card_tasha_late_probe(struct snd_soc_card *card) +{ + const char *be_dl_name = LPASS_BE_SLIMBUS_0_RX; + struct snd_soc_pcm_runtime *rtd; + int ret = 0; + void *mbhc_calibration; + + rtd = snd_soc_get_pcm_runtime(card, be_dl_name); + if (!rtd) { + dev_err(card->dev, + "%s: snd_soc_get_pcm_runtime for %s failed!\n", + __func__, be_dl_name); + ret = -EINVAL; + goto err_pcm_runtime; + } + + mbhc_calibration = def_wcd_mbhc_cal(); + if (!mbhc_calibration) { + ret = -ENOMEM; + goto err_mbhc_cal; + } + wcd_mbhc_cfg.calibration = mbhc_calibration; + ret = tasha_mbhc_hs_detect(rtd->codec, &wcd_mbhc_cfg); + if (ret) { + dev_err(card->dev, "%s: mbhc hs detect failed, err:%d\n", + __func__, ret); + goto err_hs_detect; + } + return 0; + +err_hs_detect: + kfree(mbhc_calibration); +err_mbhc_cal: +err_pcm_runtime: + return ret; +} + +static int msm_populate_dai_link_component_of_node( + struct snd_soc_card *card) +{ + int i, index, ret = 0; + struct device *cdev = card->dev; + struct snd_soc_dai_link *dai_link = card->dai_link; + struct device_node *np; + + if (!cdev) { + pr_err("%s: Sound card device memory NULL\n", __func__); + return -ENODEV; + } + + for (i = 0; i < card->num_links; i++) { + if (dai_link[i].platform_of_node && dai_link[i].cpu_of_node) + continue; + + /* populate platform_of_node for snd card dai links */ + if (dai_link[i].platform_name && + !dai_link[i].platform_of_node) { + index = of_property_match_string(cdev->of_node, + "asoc-platform-names", + dai_link[i].platform_name); + if (index < 0) { + pr_err("%s: No match found for platform name: %s\n", + __func__, dai_link[i].platform_name); + ret = index; + goto err; + } + np = of_parse_phandle(cdev->of_node, "asoc-platform", + index); + if (!np) { + pr_err("%s: retrieving phandle for platform %s, index %d failed\n", + __func__, dai_link[i].platform_name, + index); + ret = -ENODEV; + goto err; + } + dai_link[i].platform_of_node = np; + dai_link[i].platform_name = NULL; + } + + /* populate cpu_of_node for snd card dai links */ + if (dai_link[i].cpu_dai_name && !dai_link[i].cpu_of_node) { + index = of_property_match_string(cdev->of_node, + "asoc-cpu-names", + dai_link[i].cpu_dai_name); + if (index >= 0) { + np = of_parse_phandle(cdev->of_node, "asoc-cpu", + index); + if (!np) { + pr_err("%s: retrieving phandle for cpu dai %s failed\n", + __func__, + dai_link[i].cpu_dai_name); + ret = -ENODEV; + goto err; + } + dai_link[i].cpu_of_node = np; + dai_link[i].cpu_dai_name = NULL; + } + } + + /* populate codec_of_node for snd card dai links */ + if (dai_link[i].codec_name && !dai_link[i].codec_of_node) { + index = of_property_match_string(cdev->of_node, + "asoc-codec-names", + dai_link[i].codec_name); + if (index < 0) + continue; + np = of_parse_phandle(cdev->of_node, "asoc-codec", + index); + if (!np) { + pr_err("%s: retrieving phandle for codec %s failed\n", + __func__, dai_link[i].codec_name); + ret = -ENODEV; + goto err; + } + dai_link[i].codec_of_node = np; + dai_link[i].codec_name = NULL; + } + } + +err: + return ret; +} + +static int msm_audrx_stub_init(struct snd_soc_pcm_runtime *rtd) +{ + int ret = 0; + struct snd_soc_codec *codec = rtd->codec; + + ret = snd_soc_add_codec_controls(codec, msm_ext_snd_controls, + ARRAY_SIZE(msm_ext_snd_controls)); + if (ret < 0) { + dev_err(codec->dev, + "%s: add_codec_controls failed, err = %d\n", + __func__, ret); + return ret; + } + + return 0; +} + +static int msm_snd_stub_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + + int ret = 0; + unsigned int rx_ch[] = {144, 145, 146, 147, 148, 149, 150, + 151}; + unsigned int tx_ch[] = {128, 129, 130, 131, 132, 133, + 134, 135, 136, 137, 138, 139, + 140, 141, 142, 143}; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + ret = snd_soc_dai_set_channel_map(cpu_dai, 0, 0, + slim_rx_cfg[SLIM_RX_0].channels, + rx_ch); + if (ret < 0) + pr_err("%s: RX failed to set cpu chan map error %d\n", + __func__, ret); + } else { + ret = snd_soc_dai_set_channel_map(cpu_dai, + slim_tx_cfg[SLIM_TX_0].channels, + tx_ch, 0, 0); + if (ret < 0) + pr_err("%s: TX failed to set cpu chan map error %d\n", + __func__, ret); + } + + return ret; +} + +static struct snd_soc_ops msm_stub_be_ops = { + .hw_params = msm_snd_stub_hw_params, +}; + +static struct snd_soc_dai_link msm_stub_fe_dai_links[] = { + + /* FrontEnd DAI Links */ + { + .name = "MSMSTUB Media1", + .stream_name = "MultiMedia1", + .cpu_dai_name = "MultiMedia1", + .platform_name = "msm-pcm-dsp.0", + .dynamic = 1, + .async_ops = ASYNC_DPCM_SND_SOC_PREPARE, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .id = MSM_FRONTEND_DAI_MULTIMEDIA1 + }, +}; + +static struct snd_soc_dai_link msm_stub_be_dai_links[] = { + + /* Backend DAI Links */ + { + .name = LPASS_BE_SLIMBUS_0_RX, + .stream_name = "Slimbus Playback", + .cpu_dai_name = "msm-dai-q6-dev.16384", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_0_RX, + .init = &msm_audrx_stub_init, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_pmdown_time = 1, /* dai link has playback support */ + .ignore_suspend = 1, + .ops = &msm_stub_be_ops, + }, + { + .name = LPASS_BE_SLIMBUS_0_TX, + .stream_name = "Slimbus Capture", + .cpu_dai_name = "msm-dai-q6-dev.16385", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_0_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + .ops = &msm_stub_be_ops, + }, +}; + +static struct snd_soc_dai_link msm_stub_dai_links[ + ARRAY_SIZE(msm_stub_fe_dai_links) + + ARRAY_SIZE(msm_stub_be_dai_links)]; + +struct snd_soc_card snd_soc_card_stub_msm = { + .name = "sm6150-stub-snd-card", +}; + +static const struct of_device_id sm6150_asoc_machine_of_match[] = { + { .compatible = "qcom,sm6150-asoc-snd", + .data = "codec"}, + { .compatible = "qcom,sm6150-asoc-snd-stub", + .data = "stub_codec"}, + {}, +}; + +static struct snd_soc_card *populate_snd_card_dailinks(struct device *dev) +{ + struct snd_soc_card *card = NULL; + struct snd_soc_dai_link *dailink; + int total_links = 0, rc = 0; + u32 tavil_codec = 0, auxpcm_audio_intf = 0; + u32 mi2s_audio_intf = 0, ext_disp_audio_intf = 0; + u32 wcn_btfm_intf = 0; + const struct of_device_id *match; + u32 tasha_codec = 0; + + match = of_match_node(sm6150_asoc_machine_of_match, dev->of_node); + if (!match) { + dev_err(dev, "%s: No DT match found for sound card\n", + __func__); + return NULL; + } + + if (!strcmp(match->data, "codec")) { + card = &snd_soc_card_sm6150_msm; + memcpy(msm_sm6150_dai_links + total_links, + msm_common_dai_links, + sizeof(msm_common_dai_links)); + + total_links += ARRAY_SIZE(msm_common_dai_links); + + memcpy(msm_sm6150_dai_links + total_links, + msm_common_misc_fe_dai_links, + sizeof(msm_common_misc_fe_dai_links)); + + total_links += ARRAY_SIZE(msm_common_misc_fe_dai_links); + + rc = of_property_read_u32(dev->of_node, "qcom,tavil_codec", + &tavil_codec); + if (rc) + dev_dbg(dev, "%s: No DT match for tavil codec\n", + __func__); + + rc = of_property_read_u32(dev->of_node, "qcom,tasha_codec", + &tasha_codec); + if (rc) + dev_dbg(dev, "%s: No DT match for tasha codec\n", + __func__); + + if (tavil_codec) { + card->late_probe = + msm_snd_card_tavil_late_probe; + memcpy(msm_sm6150_dai_links + total_links, + msm_tavil_fe_dai_links, + sizeof(msm_tavil_fe_dai_links)); + total_links += + ARRAY_SIZE(msm_tavil_fe_dai_links); + } else if (tasha_codec) { + card->late_probe = + msm_snd_card_tasha_late_probe; + memcpy(msm_sm6150_dai_links + total_links, + msm_tasha_fe_dai_links, + sizeof(msm_tasha_fe_dai_links)); + total_links += + ARRAY_SIZE(msm_tasha_fe_dai_links); + } else { + memcpy(msm_sm6150_dai_links + total_links, + msm_bolero_fe_dai_links, + sizeof(msm_bolero_fe_dai_links)); + total_links += + ARRAY_SIZE(msm_bolero_fe_dai_links); + } + + memcpy(msm_sm6150_dai_links + total_links, + msm_int_compress_capture_dai, + sizeof(msm_int_compress_capture_dai)); + + total_links += ARRAY_SIZE(msm_int_compress_capture_dai); + + memcpy(msm_sm6150_dai_links + total_links, + msm_common_be_dai_links, + sizeof(msm_common_be_dai_links)); + + total_links += ARRAY_SIZE(msm_common_be_dai_links); + + if (tavil_codec) { + memcpy(msm_sm6150_dai_links + total_links, + msm_tavil_be_dai_links, + sizeof(msm_tavil_be_dai_links)); + total_links += ARRAY_SIZE(msm_tavil_be_dai_links); + } else if (tasha_codec) { + memcpy(msm_sm6150_dai_links + total_links, + msm_tasha_be_dai_links, + sizeof(msm_tasha_be_dai_links)); + total_links += ARRAY_SIZE(msm_tasha_be_dai_links); + } else { + memcpy(msm_sm6150_dai_links + total_links, + msm_wsa_cdc_dma_be_dai_links, + sizeof(msm_wsa_cdc_dma_be_dai_links)); + total_links += + ARRAY_SIZE(msm_wsa_cdc_dma_be_dai_links); + + memcpy(msm_sm6150_dai_links + total_links, + msm_rx_tx_cdc_dma_be_dai_links, + sizeof(msm_rx_tx_cdc_dma_be_dai_links)); + total_links += + ARRAY_SIZE(msm_rx_tx_cdc_dma_be_dai_links); + } + + rc = of_property_read_u32(dev->of_node, + "qcom,ext-disp-audio-rx", + &ext_disp_audio_intf); + if (rc) { + dev_dbg(dev, "%s: No DT match Ext Disp interface\n", + __func__); + } else { + if (ext_disp_audio_intf) { + memcpy(msm_sm6150_dai_links + total_links, + ext_disp_be_dai_link, + sizeof(ext_disp_be_dai_link)); + total_links += + ARRAY_SIZE(ext_disp_be_dai_link); + } + } + + rc = of_property_read_u32(dev->of_node, "qcom,mi2s-audio-intf", + &mi2s_audio_intf); + if (rc) { + dev_dbg(dev, "%s: No DT match MI2S audio interface\n", + __func__); + } else { + if (mi2s_audio_intf) { + memcpy(msm_sm6150_dai_links + total_links, + msm_mi2s_be_dai_links, + sizeof(msm_mi2s_be_dai_links)); + total_links += + ARRAY_SIZE(msm_mi2s_be_dai_links); + } + } + + + rc = of_property_read_u32(dev->of_node, "qcom,wcn-btfm", + &wcn_btfm_intf); + if (rc) { + dev_dbg(dev, "%s: No DT match wcn btfm interface\n", + __func__); + } else { + if (wcn_btfm_intf) { + memcpy(msm_sm6150_dai_links + total_links, + msm_wcn_be_dai_links, + sizeof(msm_wcn_be_dai_links)); + total_links += + ARRAY_SIZE(msm_wcn_be_dai_links); + } + } + + rc = of_property_read_u32(dev->of_node, + "qcom,auxpcm-audio-intf", + &auxpcm_audio_intf); + if (rc) { + dev_dbg(dev, "%s: No DT match Aux PCM interface\n", + __func__); + } else { + if (auxpcm_audio_intf) { + memcpy(msm_sm6150_dai_links + total_links, + msm_auxpcm_be_dai_links, + sizeof(msm_auxpcm_be_dai_links)); + total_links += + ARRAY_SIZE(msm_auxpcm_be_dai_links); + } + } + + dailink = msm_sm6150_dai_links; + } else if (!strcmp(match->data, "stub_codec")) { + card = &snd_soc_card_stub_msm; + + memcpy(msm_stub_dai_links + total_links, + msm_stub_fe_dai_links, + sizeof(msm_stub_fe_dai_links)); + total_links += ARRAY_SIZE(msm_stub_fe_dai_links); + + memcpy(msm_stub_dai_links + total_links, + msm_stub_be_dai_links, + sizeof(msm_stub_be_dai_links)); + total_links += ARRAY_SIZE(msm_stub_be_dai_links); + + dailink = msm_stub_dai_links; + } + + if (card) { + card->dai_link = dailink; + card->num_links = total_links; + } + + return card; +} + +static int msm_wsa881x_init(struct snd_soc_component *component) +{ + u8 spkleft_ports[WSA881X_MAX_SWR_PORTS] = {0, 1, 2, 3}; + u8 spkright_ports[WSA881X_MAX_SWR_PORTS] = {0, 1, 2, 3}; + u8 spkleft_port_types[WSA881X_MAX_SWR_PORTS] = {SPKR_L, SPKR_L_COMP, + SPKR_L_BOOST, SPKR_L_VI}; + u8 spkright_port_types[WSA881X_MAX_SWR_PORTS] = {SPKR_R, SPKR_R_COMP, + SPKR_R_BOOST, SPKR_R_VI}; + unsigned int ch_rate[WSA881X_MAX_SWR_PORTS] = {2400, 600, 300, 1200}; + unsigned int ch_mask[WSA881X_MAX_SWR_PORTS] = {0x1, 0xF, 0x3, 0x3}; + struct snd_soc_codec *codec = snd_soc_component_to_codec(component); + struct msm_asoc_mach_data *pdata; + struct snd_soc_dapm_context *dapm; + struct snd_card *card = component->card->snd_card; + struct snd_info_entry *entry; + int ret = 0; + + if (!codec) { + pr_err("%s codec is NULL\n", __func__); + return -EINVAL; + } + + dapm = snd_soc_codec_get_dapm(codec); + + if (!strcmp(component->name_prefix, "SpkrLeft")) { + dev_dbg(codec->dev, "%s: setting left ch map to codec %s\n", + __func__, codec->component.name); + wsa881x_set_channel_map(codec, &spkleft_ports[0], + WSA881X_MAX_SWR_PORTS, &ch_mask[0], + &ch_rate[0], &spkleft_port_types[0]); + if (dapm->component) { + snd_soc_dapm_ignore_suspend(dapm, "SpkrLeft IN"); + snd_soc_dapm_ignore_suspend(dapm, "SpkrLeft SPKR"); + } + } else if (!strcmp(component->name_prefix, "SpkrRight")) { + dev_dbg(codec->dev, "%s: setting right ch map to codec %s\n", + __func__, codec->component.name); + wsa881x_set_channel_map(codec, &spkright_ports[0], + WSA881X_MAX_SWR_PORTS, &ch_mask[0], + &ch_rate[0], &spkright_port_types[0]); + if (dapm->component) { + snd_soc_dapm_ignore_suspend(dapm, "SpkrRight IN"); + snd_soc_dapm_ignore_suspend(dapm, "SpkrRight SPKR"); + } + } else { + dev_err(codec->dev, "%s: wrong codec name %s\n", __func__, + codec->component.name); + ret = -EINVAL; + goto err; + } + pdata = snd_soc_card_get_drvdata(component->card); + if (!pdata->codec_root) { + entry = snd_info_create_subdir(card->module, "codecs", + card->proc_root); + if (!entry) { + pr_err("%s: Cannot create codecs module entry\n", + __func__); + ret = 0; + goto err; + } + pdata->codec_root = entry; + } + wsa881x_codec_info_create_codec_entry(pdata->codec_root, + codec); +err: + return ret; +} + +static int msm_aux_codec_init(struct snd_soc_component *component) +{ + struct snd_soc_codec *codec = snd_soc_component_to_codec(component); + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + int ret = 0; + void *mbhc_calibration; + struct snd_info_entry *entry; + struct snd_card *card = component->card->snd_card; + struct msm_asoc_mach_data *pdata; + + snd_soc_dapm_ignore_suspend(dapm, "EAR"); + snd_soc_dapm_ignore_suspend(dapm, "AUX"); + snd_soc_dapm_ignore_suspend(dapm, "HPHL"); + snd_soc_dapm_ignore_suspend(dapm, "HPHR"); + snd_soc_dapm_ignore_suspend(dapm, "AMIC1"); + snd_soc_dapm_ignore_suspend(dapm, "AMIC2"); + snd_soc_dapm_ignore_suspend(dapm, "AMIC3"); + snd_soc_dapm_sync(dapm); + + pdata = snd_soc_card_get_drvdata(component->card); + if (!pdata->codec_root) { + entry = snd_info_create_subdir(card->module, "codecs", + card->proc_root); + if (!entry) { + pr_err("%s: Cannot create codecs module entry\n", + __func__); + ret = 0; + goto codec_root_err; + } + pdata->codec_root = entry; + } + wcd937x_info_create_codec_entry(pdata->codec_root, codec); +codec_root_err: + mbhc_calibration = def_wcd_mbhc_cal(); + if (!mbhc_calibration) { + return -ENOMEM; + } + wcd_mbhc_cfg.calibration = mbhc_calibration; + ret = wcd937x_mbhc_hs_detect(codec, &wcd_mbhc_cfg); + + return ret; +} + +static int msm_init_aux_dev(struct platform_device *pdev, + struct snd_soc_card *card) +{ + struct device_node *wsa_of_node; + struct device_node *aux_codec_of_node; + u32 wsa_max_devs; + u32 wsa_dev_cnt; + u32 codec_aux_dev_cnt = 0; + int i; + struct msm_wsa881x_dev_info *wsa881x_dev_info = NULL; + struct aux_codec_dev_info *aux_cdc_dev_info = NULL; + const char *auxdev_name_prefix[1]; + char *dev_name_str = NULL; + int found = 0; + int codecs_found = 0; + int ret = 0; + + /* Get maximum WSA device count for this platform */ + ret = of_property_read_u32(pdev->dev.of_node, + "qcom,wsa-max-devs", &wsa_max_devs); + if (ret) { + dev_info(&pdev->dev, + "%s: wsa-max-devs property missing in DT %s, ret = %d\n", + __func__, pdev->dev.of_node->full_name, ret); + wsa_max_devs = 0; + goto codec_aux_dev; + } + if (wsa_max_devs == 0) { + dev_warn(&pdev->dev, + "%s: Max WSA devices is 0 for this target?\n", + __func__); + goto codec_aux_dev; + } + + /* Get count of WSA device phandles for this platform */ + wsa_dev_cnt = of_count_phandle_with_args(pdev->dev.of_node, + "qcom,wsa-devs", NULL); + if (wsa_dev_cnt == -ENOENT) { + dev_warn(&pdev->dev, "%s: No wsa device defined in DT.\n", + __func__); + goto err; + } else if (wsa_dev_cnt <= 0) { + dev_err(&pdev->dev, + "%s: Error reading wsa device from DT. wsa_dev_cnt = %d\n", + __func__, wsa_dev_cnt); + ret = -EINVAL; + goto err; + } + + /* + * Expect total phandles count to be NOT less than maximum possible + * WSA count. However, if it is less, then assign same value to + * max count as well. + */ + if (wsa_dev_cnt < wsa_max_devs) { + dev_dbg(&pdev->dev, + "%s: wsa_max_devs = %d cannot exceed wsa_dev_cnt = %d\n", + __func__, wsa_max_devs, wsa_dev_cnt); + wsa_max_devs = wsa_dev_cnt; + } + + /* Make sure prefix string passed for each WSA device */ + ret = of_property_count_strings(pdev->dev.of_node, + "qcom,wsa-aux-dev-prefix"); + if (ret != wsa_dev_cnt) { + dev_err(&pdev->dev, + "%s: expecting %d wsa prefix. Defined only %d in DT\n", + __func__, wsa_dev_cnt, ret); + ret = -EINVAL; + goto err; + } + + /* + * Alloc mem to store phandle and index info of WSA device, if already + * registered with ALSA core + */ + wsa881x_dev_info = devm_kcalloc(&pdev->dev, wsa_max_devs, + sizeof(struct msm_wsa881x_dev_info), + GFP_KERNEL); + if (!wsa881x_dev_info) { + ret = -ENOMEM; + goto err; + } + + /* + * search and check whether all WSA devices are already + * registered with ALSA core or not. If found a node, store + * the node and the index in a local array of struct for later + * use. + */ + for (i = 0; i < wsa_dev_cnt; i++) { + wsa_of_node = of_parse_phandle(pdev->dev.of_node, + "qcom,wsa-devs", i); + if (unlikely(!wsa_of_node)) { + /* we should not be here */ + dev_err(&pdev->dev, + "%s: wsa dev node is not present\n", + __func__); + ret = -EINVAL; + goto err; + } + if (soc_find_component_locked(wsa_of_node, NULL)) { + /* WSA device registered with ALSA core */ + wsa881x_dev_info[found].of_node = wsa_of_node; + wsa881x_dev_info[found].index = i; + found++; + if (found == wsa_max_devs) + break; + } + } + + if (found < wsa_max_devs) { + dev_dbg(&pdev->dev, + "%s: failed to find %d components. Found only %d\n", + __func__, wsa_max_devs, found); + return -EPROBE_DEFER; + } + dev_info(&pdev->dev, + "%s: found %d wsa881x devices registered with ALSA core\n", + __func__, found); + +codec_aux_dev: + if (!strnstr(card->name, "tavil", strlen(card->name)) && + !strnstr(card->name, "tasha", strlen(card->name))) { + /* Get count of aux codec device phandles for this platform */ + codec_aux_dev_cnt = of_count_phandle_with_args( + pdev->dev.of_node, + "qcom,codec-aux-devs", NULL); + if (codec_aux_dev_cnt == -ENOENT) { + dev_warn(&pdev->dev, "%s: No aux codec defined in DT.\n", + __func__); + goto err; + } else if (codec_aux_dev_cnt <= 0) { + dev_err(&pdev->dev, + "%s: Error reading aux codec device from DT, dev_cnt=%d\n", + __func__, codec_aux_dev_cnt); + ret = -EINVAL; + goto err; + } + + /* + * Alloc mem to store phandle and index info of aux codec + * if already registered with ALSA core + */ + aux_cdc_dev_info = devm_kcalloc(&pdev->dev, codec_aux_dev_cnt, + sizeof(struct aux_codec_dev_info), + GFP_KERNEL); + if (!aux_cdc_dev_info) { + ret = -ENOMEM; + goto err; + } + + /* + * search and check whether all aux codecs are already + * registered with ALSA core or not. If found a node, store + * the node and the index in a local array of struct for later + * use. + */ + for (i = 0; i < codec_aux_dev_cnt; i++) { + aux_codec_of_node = of_parse_phandle(pdev->dev.of_node, + "qcom,codec-aux-devs", i); + if (unlikely(!aux_codec_of_node)) { + /* we should not be here */ + dev_err(&pdev->dev, + "%s: aux codec dev node is not present\n", + __func__); + ret = -EINVAL; + goto err; + } + if (soc_find_component_locked(aux_codec_of_node, NULL)) { + /* AUX codec registered with ALSA core */ + aux_cdc_dev_info[codecs_found].of_node = + aux_codec_of_node; + aux_cdc_dev_info[codecs_found].index = i; + codecs_found++; + } + } + + if (codecs_found < codec_aux_dev_cnt) { + dev_dbg(&pdev->dev, + "%s: failed to find %d components. Found only %d\n", + __func__, codec_aux_dev_cnt, codecs_found); + return -EPROBE_DEFER; + } + dev_info(&pdev->dev, + "%s: found %d AUX codecs registered with ALSA core\n", + __func__, codecs_found); + + } + + card->num_aux_devs = wsa_max_devs + codec_aux_dev_cnt; + card->num_configs = wsa_max_devs + codec_aux_dev_cnt; + + /* Alloc array of AUX devs struct */ + msm_aux_dev = devm_kcalloc(&pdev->dev, card->num_aux_devs, + sizeof(struct snd_soc_aux_dev), + GFP_KERNEL); + if (!msm_aux_dev) { + ret = -ENOMEM; + goto err; + } + + /* Alloc array of codec conf struct */ + msm_codec_conf = devm_kcalloc(&pdev->dev, card->num_configs, + sizeof(struct snd_soc_codec_conf), + GFP_KERNEL); + if (!msm_codec_conf) { + ret = -ENOMEM; + goto err; + } + + for (i = 0; i < wsa_max_devs; i++) { + dev_name_str = devm_kzalloc(&pdev->dev, DEV_NAME_STR_LEN, + GFP_KERNEL); + if (!dev_name_str) { + ret = -ENOMEM; + goto err; + } + + ret = of_property_read_string_index(pdev->dev.of_node, + "qcom,wsa-aux-dev-prefix", + wsa881x_dev_info[i].index, + auxdev_name_prefix); + if (ret) { + dev_err(&pdev->dev, + "%s: failed to read wsa aux dev prefix, ret = %d\n", + __func__, ret); + ret = -EINVAL; + goto err; + } + + snprintf(dev_name_str, strlen("wsa881x.%d"), "wsa881x.%d", i); + msm_aux_dev[i].name = dev_name_str; + msm_aux_dev[i].codec_name = NULL; + msm_aux_dev[i].codec_of_node = + wsa881x_dev_info[i].of_node; + msm_aux_dev[i].init = msm_wsa881x_init; + msm_codec_conf[i].dev_name = NULL; + msm_codec_conf[i].name_prefix = auxdev_name_prefix[0]; + msm_codec_conf[i].of_node = + wsa881x_dev_info[i].of_node; + } + + for (i = 0; i < codec_aux_dev_cnt; i++) { + msm_aux_dev[wsa_max_devs + i].name = NULL; + msm_aux_dev[wsa_max_devs + i].codec_name = NULL; + msm_aux_dev[wsa_max_devs + i].codec_of_node = + aux_cdc_dev_info[i].of_node; + msm_aux_dev[wsa_max_devs + i].init = msm_aux_codec_init; + msm_codec_conf[wsa_max_devs + i].dev_name = NULL; + msm_codec_conf[wsa_max_devs + i].name_prefix = + NULL; + msm_codec_conf[wsa_max_devs + i].of_node = + aux_cdc_dev_info[i].of_node; + } + + card->codec_conf = msm_codec_conf; + card->aux_dev = msm_aux_dev; +err: + return ret; +} + +static void msm_i2s_auxpcm_init(struct platform_device *pdev) +{ + int count; + u32 mi2s_master_slave[MI2S_MAX]; + int ret; + + for (count = 0; count < MI2S_MAX; count++) { + mutex_init(&mi2s_intf_conf[count].lock); + mi2s_intf_conf[count].ref_cnt = 0; + } + + ret = of_property_read_u32_array(pdev->dev.of_node, + "qcom,msm-mi2s-master", + mi2s_master_slave, MI2S_MAX); + if (ret) { + dev_dbg(&pdev->dev, "%s: no qcom,msm-mi2s-master in DT node\n", + __func__); + } else { + for (count = 0; count < MI2S_MAX; count++) { + mi2s_intf_conf[count].msm_is_mi2s_master = + mi2s_master_slave[count]; + } + } +} + +static void msm_i2s_auxpcm_deinit(void) +{ + int count; + + for (count = 0; count < MI2S_MAX; count++) { + mutex_destroy(&mi2s_intf_conf[count].lock); + mi2s_intf_conf[count].ref_cnt = 0; + mi2s_intf_conf[count].msm_is_mi2s_master = 0; + } +} + +static int sm6150_ssr_enable(struct device *dev, void *data) +{ + struct platform_device *pdev = to_platform_device(dev); + struct snd_soc_card *card = platform_get_drvdata(pdev); + struct msm_asoc_mach_data *pdata; + int ret = 0; + + if (!card) { + dev_err(dev, "%s: card is NULL\n", __func__); + ret = -EINVAL; + goto err; + } + + if (strnstr(card->name, "tavil", strlen(card->name)) || + strnstr(card->name, "tasha", strlen(card->name))) { + pdata = snd_soc_card_get_drvdata(card); + if (!pdata->is_afe_config_done) { + const char *be_dl_name = LPASS_BE_SLIMBUS_0_RX; + struct snd_soc_pcm_runtime *rtd; + + rtd = snd_soc_get_pcm_runtime(card, be_dl_name); + if (!rtd) { + dev_err(dev, + "%s: snd_soc_get_pcm_runtime for %s failed!\n", + __func__, be_dl_name); + ret = -EINVAL; + goto err; + } + ret = msm_afe_set_config(rtd->codec); + if (ret) + dev_err(dev, "%s: Failed to set AFE config. err %d\n", + __func__, ret); + else + pdata->is_afe_config_done = true; + } + } + snd_soc_card_change_online_state(card, 1); + dev_dbg(dev, "%s: setting snd_card to ONLINE\n", __func__); + +err: + return ret; +} + +static void sm6150_ssr_disable(struct device *dev, void *data) +{ + struct platform_device *pdev = to_platform_device(dev); + struct snd_soc_card *card = platform_get_drvdata(pdev); + struct msm_asoc_mach_data *pdata; + + if (!card) { + dev_err(dev, "%s: card is NULL\n", __func__); + return; + } + + dev_dbg(dev, "%s: setting snd_card to OFFLINE\n", __func__); + snd_soc_card_change_online_state(card, 0); + + if (strnstr(card->name, "tavil", strlen(card->name)) || + strnstr(card->name, "tasha", strlen(card->name))) { + pdata = snd_soc_card_get_drvdata(card); + msm_afe_clear_config(); + pdata->is_afe_config_done = false; + } +} + +static int msm_ext_prepare_hifi(struct msm_asoc_mach_data *pdata) +{ + int ret = 0; + + if (gpio_is_valid(pdata->hph_en1_gpio)) { + pr_debug("%s: hph_en1_gpio request %d\n", __func__, + pdata->hph_en1_gpio); + ret = gpio_request(pdata->hph_en1_gpio, "hph_en1_gpio"); + if (ret) { + pr_err("%s: hph_en1_gpio request failed, ret:%d\n", + __func__, ret); + goto err; + } + } + if (gpio_is_valid(pdata->hph_en0_gpio)) { + pr_debug("%s: hph_en0_gpio request %d\n", __func__, + pdata->hph_en0_gpio); + ret = gpio_request(pdata->hph_en0_gpio, "hph_en0_gpio"); + if (ret) + pr_err("%s: hph_en0_gpio request failed, ret:%d\n", + __func__, ret); + } + +err: + return ret; +} + +static const struct snd_event_ops sm6150_ssr_ops = { + .enable = sm6150_ssr_enable, + .disable = sm6150_ssr_disable, +}; + +static int msm_audio_ssr_compare(struct device *dev, void *data) +{ + struct device_node *node = data; + + dev_dbg(dev, "%s: dev->of_node = 0x%p, node = 0x%p\n", + __func__, dev->of_node, node); + return (dev->of_node && dev->of_node == node); +} + +static int msm_audio_ssr_register(struct device *dev) +{ + struct device_node *np = dev->of_node; + struct snd_event_clients *ssr_clients = NULL; + struct device_node *node; + int ret; + int i; + + for (i = 0; ; i++) { + node = of_parse_phandle(np, "qcom,msm_audio_ssr_devs", i); + if (!node) + break; + snd_event_mstr_add_client(&ssr_clients, + msm_audio_ssr_compare, node); + } + + ret = snd_event_master_register(dev, &sm6150_ssr_ops, + ssr_clients, NULL); + if (!ret) + snd_event_notify(dev, SND_EVENT_UP); + + return ret; +} + +static int msm_asoc_machine_probe(struct platform_device *pdev) +{ + struct snd_soc_card *card; + struct msm_asoc_mach_data *pdata; + const char *mbhc_audio_jack_type = NULL; + int ret; + + if (!pdev->dev.of_node) { + dev_err(&pdev->dev, "No platform supplied from device tree\n"); + return -EINVAL; + } + + pdata = devm_kzalloc(&pdev->dev, + sizeof(struct msm_asoc_mach_data), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + card = populate_snd_card_dailinks(&pdev->dev); + if (!card) { + dev_err(&pdev->dev, "%s: Card uninitialized\n", __func__); + ret = -EINVAL; + goto err; + } + card->dev = &pdev->dev; + platform_set_drvdata(pdev, card); + snd_soc_card_set_drvdata(card, pdata); + + ret = snd_soc_of_parse_card_name(card, "qcom,model"); + if (ret) { + dev_err(&pdev->dev, "parse card name failed, err:%d\n", + ret); + goto err; + } + + ret = snd_soc_of_parse_audio_routing(card, "qcom,audio-routing"); + if (ret) { + dev_err(&pdev->dev, "parse audio routing failed, err:%d\n", + ret); + goto err; + } + + ret = msm_populate_dai_link_component_of_node(card); + if (ret) { + ret = -EPROBE_DEFER; + goto err; + } + + ret = msm_init_aux_dev(pdev, card); + if (ret) + goto err; + + ret = devm_snd_soc_register_card(&pdev->dev, card); + if (ret == -EPROBE_DEFER) { + if (codec_reg_done) + ret = -EINVAL; + goto err; + } else if (ret) { + dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", + ret); + goto err; + } + dev_info(&pdev->dev, "Sound card %s registered\n", card->name); + + pdata->hph_en1_gpio = of_get_named_gpio(pdev->dev.of_node, + "qcom,hph-en1-gpio", 0); + if (!gpio_is_valid(pdata->hph_en1_gpio)) + pdata->hph_en1_gpio_p = of_parse_phandle(pdev->dev.of_node, + "qcom,hph-en1-gpio", 0); + if (!gpio_is_valid(pdata->hph_en1_gpio) && (!pdata->hph_en1_gpio_p)) { + dev_dbg(&pdev->dev, "property %s not detected in node %s", + "qcom,hph-en1-gpio", pdev->dev.of_node->full_name); + } + + pdata->hph_en0_gpio = of_get_named_gpio(pdev->dev.of_node, + "qcom,hph-en0-gpio", 0); + if (!gpio_is_valid(pdata->hph_en0_gpio)) + pdata->hph_en0_gpio_p = of_parse_phandle(pdev->dev.of_node, + "qcom,hph-en0-gpio", 0); + if (!gpio_is_valid(pdata->hph_en0_gpio) && (!pdata->hph_en0_gpio_p)) { + dev_dbg(&pdev->dev, "property %s not detected in node %s", + "qcom,hph-en0-gpio", pdev->dev.of_node->full_name); + } + + ret = msm_ext_prepare_hifi(pdata); + if (ret) { + dev_dbg(&pdev->dev, "msm_ext_prepare_hifi failed (%d)\n", + ret); + ret = 0; + } + + ret = of_property_read_string(pdev->dev.of_node, + "qcom,mbhc-audio-jack-type", &mbhc_audio_jack_type); + if (ret) { + dev_dbg(&pdev->dev, "Looking up %s property in node %s failed\n", + "qcom,mbhc-audio-jack-type", + pdev->dev.of_node->full_name); + dev_dbg(&pdev->dev, "Jack type properties set to default\n"); + } else { + if (!strcmp(mbhc_audio_jack_type, "4-pole-jack")) { + wcd_mbhc_cfg.enable_anc_mic_detect = false; + dev_dbg(&pdev->dev, "This hardware has 4 pole jack"); + } else if (!strcmp(mbhc_audio_jack_type, "5-pole-jack")) { + wcd_mbhc_cfg.enable_anc_mic_detect = true; + dev_dbg(&pdev->dev, "This hardware has 5 pole jack"); + } else if (!strcmp(mbhc_audio_jack_type, "6-pole-jack")) { + wcd_mbhc_cfg.enable_anc_mic_detect = true; + dev_dbg(&pdev->dev, "This hardware has 6 pole jack"); + } else { + wcd_mbhc_cfg.enable_anc_mic_detect = false; + dev_dbg(&pdev->dev, "Unknown value, set to default\n"); + } + } + /* + * Parse US-Euro gpio info from DT. Report no error if us-euro + * entry is not found in DT file as some targets do not support + * US-Euro detection + */ + pdata->us_euro_gpio_p = of_parse_phandle(pdev->dev.of_node, + "qcom,us-euro-gpios", 0); + if (!pdata->us_euro_gpio_p) { + dev_dbg(&pdev->dev, "property %s not detected in node %s", + "qcom,us-euro-gpios", pdev->dev.of_node->full_name); + } else { + dev_dbg(&pdev->dev, "%s detected\n", + "qcom,us-euro-gpios"); + wcd_mbhc_cfg.swap_gnd_mic = msm_swap_gnd_mic; + } + + if (wcd_mbhc_cfg.enable_usbc_analog) { + wcd_mbhc_cfg.swap_gnd_mic = msm_usbc_swap_gnd_mic; + + pdata->fsa_handle = of_parse_phandle(pdev->dev.of_node, + "fsa4480-i2c-handle", 0); + if (!pdata->fsa_handle) + dev_err(&pdev->dev, + "property %s not detected in node %s\n", + "fsa4480-i2c-handle", + pdev->dev.of_node->full_name); + } + /* Parse pinctrl info from devicetree */ + ret = msm_get_pinctrl(pdev); + if (!ret) { + pr_debug("%s: pinctrl parsing successful\n", __func__); + } else { + dev_dbg(&pdev->dev, + "%s: Parsing pinctrl failed with %d. Cannot use Ports\n", + __func__, ret); + ret = 0; + } + + msm_i2s_auxpcm_init(pdev); + if (!strnstr(card->name, "tavil", strlen(card->name)) && + !strnstr(card->name, "tasha", strlen(card->name))) { + pdata->dmic01_gpio_p = of_parse_phandle(pdev->dev.of_node, + "qcom,cdc-dmic01-gpios", + 0); + pdata->dmic23_gpio_p = of_parse_phandle(pdev->dev.of_node, + "qcom,cdc-dmic23-gpios", + 0); + } + + ret = msm_audio_ssr_register(&pdev->dev); + if (ret) + pr_err("%s: Registration with SND event FWK failed ret = %d\n", + __func__, ret); + +err: + return ret; +} + +static int msm_asoc_machine_remove(struct platform_device *pdev) +{ + snd_event_master_deregister(&pdev->dev); + msm_i2s_auxpcm_deinit(); + + return 0; +} + +static struct platform_driver sm6150_asoc_machine_driver = { + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + .pm = &snd_soc_pm_ops, + .of_match_table = sm6150_asoc_machine_of_match, + }, + .probe = msm_asoc_machine_probe, + .remove = msm_asoc_machine_remove, +}; +module_platform_driver(sm6150_asoc_machine_driver); + +MODULE_DESCRIPTION("ALSA SoC SM6150 Machine driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:" DRV_NAME); +MODULE_DEVICE_TABLE(of, sm6150_asoc_machine_of_match); diff --git a/audio-kernel/asoc/sm8150.c b/audio-kernel/asoc/sm8150.c new file mode 100644 index 000000000..67378f4b4 --- /dev/null +++ b/audio-kernel/asoc/sm8150.c @@ -0,0 +1,7826 @@ +/* Copyright (c) 2016-2019, 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "device_event.h" +#include "msm-pcm-routing-v2.h" +#include "codecs/msm-cdc-pinctrl.h" +#include "codecs/wcd934x/wcd934x.h" +#include "codecs/wcd934x/wcd934x-mbhc.h" +#include "codecs/wcd9360/wcd9360.h" +#include "codecs/wsa881x.h" +#include "codecs/wcd-mbhc-v2.h" + +#define DRV_NAME "sm8150-asoc-snd" + +#define __CHIPSET__ "SM8150 " +#define MSM_DAILINK_NAME(name) (__CHIPSET__#name) + +#define DEV_NAME_STR_LEN 32 + +#define SAMPLING_RATE_8KHZ 8000 +#define SAMPLING_RATE_11P025KHZ 11025 +#define SAMPLING_RATE_16KHZ 16000 +#define SAMPLING_RATE_22P05KHZ 22050 +#define SAMPLING_RATE_32KHZ 32000 +#define SAMPLING_RATE_44P1KHZ 44100 +#define SAMPLING_RATE_48KHZ 48000 +#define SAMPLING_RATE_88P2KHZ 88200 +#define SAMPLING_RATE_96KHZ 96000 +#define SAMPLING_RATE_176P4KHZ 176400 +#define SAMPLING_RATE_192KHZ 192000 +#define SAMPLING_RATE_352P8KHZ 352800 +#define SAMPLING_RATE_384KHZ 384000 + +#define WCD9XXX_MBHC_DEF_RLOADS 5 + +#define WSA8810_NAME_1 "wsa881x.20170211" +#define WSA8810_NAME_2 "wsa881x.20170212" +#define WCN_CDC_SLIM_RX_CH_MAX 2 +#define WCN_CDC_SLIM_TX_CH_MAX 3 + +#define ADSP_STATE_READY_TIMEOUT_MS 3000 +#define MSM_LL_QOS_VALUE 300 /* time in us to ensure LPM doesn't go in C3/C4 */ +#define MSM_HIFI_ON 1 + +#define TDM_MAX_SLOTS 8 +#define TDM_SLOT_WIDTH_BITS 32 + +enum { + SLIM_RX_0 = 0, + SLIM_RX_1, + SLIM_RX_2, + SLIM_RX_3, + SLIM_RX_4, + SLIM_RX_5, + SLIM_RX_6, + SLIM_RX_7, + SLIM_RX_MAX, +}; +enum { + SLIM_TX_0 = 0, + SLIM_TX_1, + SLIM_TX_2, + SLIM_TX_3, + SLIM_TX_4, + SLIM_TX_5, + SLIM_TX_6, + SLIM_TX_7, + SLIM_TX_8, + SLIM_TX_MAX, +}; + +enum { + PRIM_MI2S = 0, + SEC_MI2S, + TERT_MI2S, + QUAT_MI2S, + QUIN_MI2S, + MI2S_MAX, +}; + +enum { + PRIM_AUX_PCM = 0, + SEC_AUX_PCM, + TERT_AUX_PCM, + QUAT_AUX_PCM, + QUIN_AUX_PCM, + AUX_PCM_MAX, +}; + +struct mi2s_conf { + struct mutex lock; + u32 ref_cnt; + u32 msm_is_mi2s_master; +}; + +static u32 mi2s_ebit_clk[MI2S_MAX] = { + Q6AFE_LPASS_CLK_ID_PRI_MI2S_EBIT, + Q6AFE_LPASS_CLK_ID_SEC_MI2S_EBIT, + Q6AFE_LPASS_CLK_ID_TER_MI2S_EBIT, + Q6AFE_LPASS_CLK_ID_QUAD_MI2S_EBIT, + Q6AFE_LPASS_CLK_ID_QUI_MI2S_EBIT +}; + +struct dev_config { + u32 sample_rate; + u32 bit_format; + u32 channels; +}; + +struct tdm_dev_config { + unsigned int tdm_slot_offset[TDM_MAX_SLOTS]; +}; + +enum { + DP_RX_IDX = 0, + EXT_DISP_RX_IDX_MAX, +}; + +struct msm_wsa881x_dev_info { + struct device_node *of_node; + u32 index; +}; + +enum pinctrl_pin_state { + STATE_DISABLE = 0, /* All pins are in sleep state */ + STATE_MI2S_ACTIVE, /* IS2 = active, TDM = sleep */ + STATE_TDM_ACTIVE, /* IS2 = sleep, TDM = active */ +}; + +struct msm_pinctrl_info { + struct pinctrl *pinctrl; + struct pinctrl_state *mi2s_disable; + struct pinctrl_state *tdm_disable; + struct pinctrl_state *mi2s_active; + struct pinctrl_state *tdm_active; + enum pinctrl_pin_state curr_state; + atomic_t pinctrl_ref_count; +}; + +struct msm_asoc_mach_data { + struct snd_info_entry *codec_root; + struct msm_pinctrl_info pinctrl_info; + struct device_node *us_euro_gpio_p; /* used by pinctrl API */ + struct device_node *hph_en1_gpio_p; /* used by pinctrl API */ + struct device_node *hph_en0_gpio_p; /* used by pinctrl API */ + struct device_node *fsa_handle; + struct snd_soc_codec *codec; + struct work_struct adsp_power_up_work; +}; + +struct msm_asoc_wcd93xx_codec { + void* (*get_afe_config_fn)(struct snd_soc_codec *codec, + enum afe_config_type config_type); +}; + +static const char *const pin_states[] = {"sleep", "i2s-active", + "tdm-active"}; + +enum { + TDM_0 = 0, + TDM_1, + TDM_2, + TDM_3, + TDM_4, + TDM_5, + TDM_6, + TDM_7, + TDM_PORT_MAX, +}; + +enum { + TDM_PRI = 0, + TDM_SEC, + TDM_TERT, + TDM_QUAT, + TDM_QUIN, + TDM_INTERFACE_MAX, +}; + +struct tdm_port { + u32 mode; + u32 channel; +}; + +/* TDM default config */ +static struct dev_config tdm_rx_cfg[TDM_INTERFACE_MAX][TDM_PORT_MAX] = { + { /* PRI TDM */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_0 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_1 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_2 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_3 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_4 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_5 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_6 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_7 */ + }, + { /* SEC TDM */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_0 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_1 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_2 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_3 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_4 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_5 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_6 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_7 */ + }, + { /* TERT TDM */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_0 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_1 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_2 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_3 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_4 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_5 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_6 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_7 */ + }, + { /* QUAT TDM */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_0 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_1 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_2 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_3 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_4 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_5 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_6 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_7 */ + }, + { /* QUIN TDM */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_0 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_1 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_2 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_3 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_4 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_5 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_6 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_7 */ + } + +}; + +/* TDM default config */ +static struct dev_config tdm_tx_cfg[TDM_INTERFACE_MAX][TDM_PORT_MAX] = { + { /* PRI TDM */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_0 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_1 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_2 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_3 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_4 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_5 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_6 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_7 */ + }, + { /* SEC TDM */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_0 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_1 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_2 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_3 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_4 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_5 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_6 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_7 */ + }, + { /* TERT TDM */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_0 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_1 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_2 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_3 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_4 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_5 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_6 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_7 */ + }, + { /* QUAT TDM */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_0 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_1 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_2 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_3 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_4 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_5 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_6 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_7 */ + }, + { /* QUIN TDM */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_0 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_1 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_2 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_3 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_4 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_5 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_6 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_7 */ + } + +}; + +/* Default configuration of slimbus channels */ +static struct dev_config slim_rx_cfg[] = { + [SLIM_RX_0] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_RX_1] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_RX_2] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_RX_3] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_RX_4] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_RX_5] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_RX_6] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_RX_7] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, +}; + +static struct dev_config slim_tx_cfg[] = { + [SLIM_TX_0] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_TX_1] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_TX_2] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_TX_3] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_TX_4] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_TX_5] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_TX_6] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_TX_7] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SLIM_TX_8] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, +}; + +static struct tdm_dev_config tdm_cfg[TDM_INTERFACE_MAX * 2] + [TDM_PORT_MAX] = { + { /* PRI TDM */ + { {0, 4, 0xFFFF} }, /* RX_0 */ + { {8, 12, 0xFFFF} }, /* RX_1 */ + { {16, 20, 0xFFFF} }, /* RX_2 */ + { {24, 28, 0xFFFF} }, /* RX_3 */ + { {0xFFFF} }, /* RX_4 */ + { {0xFFFF} }, /* RX_5 */ + { {0xFFFF} }, /* RX_6 */ + { {0xFFFF} }, /* RX_7 */ + }, + { + { {0, 4, 0xFFFF} }, /* TX_0 */ + { {8, 12, 0xFFFF} }, /* TX_1 */ + { {16, 20, 0xFFFF} }, /* TX_2 */ + { {24, 28, 0xFFFF} }, /* TX_3 */ + { {0xFFFF} }, /* TX_4 */ + { {0xFFFF} }, /* TX_5 */ + { {0xFFFF} }, /* TX_6 */ + { {0xFFFF} }, /* TX_7 */ + }, + { /* SEC TDM */ + { {0, 4, 0xFFFF} }, /* RX_0 */ + { {8, 12, 0xFFFF} }, /* RX_1 */ + { {16, 20, 0xFFFF} }, /* RX_2 */ + { {24, 28, 0xFFFF} }, /* RX_3 */ + { {0xFFFF} }, /* RX_4 */ + { {0xFFFF} }, /* RX_5 */ + { {0xFFFF} }, /* RX_6 */ + { {0xFFFF} }, /* RX_7 */ + }, + { + { {0, 4, 0xFFFF} }, /* TX_0 */ + { {8, 12, 0xFFFF} }, /* TX_1 */ + { {16, 20, 0xFFFF} }, /* TX_2 */ + { {24, 28, 0xFFFF} }, /* TX_3 */ + { {0xFFFF} }, /* TX_4 */ + { {0xFFFF} }, /* TX_5 */ + { {0xFFFF} }, /* TX_6 */ + { {0xFFFF} }, /* TX_7 */ + }, + { /* TERT TDM */ + { {0, 4, 0xFFFF} }, /* RX_0 */ + { {8, 12, 0xFFFF} }, /* RX_1 */ + { {16, 20, 0xFFFF} }, /* RX_2 */ + { {24, 28, 0xFFFF} }, /* RX_3 */ + { {0xFFFF} }, /* RX_4 */ + { {0xFFFF} }, /* RX_5 */ + { {0xFFFF} }, /* RX_6 */ + { {0xFFFF} }, /* RX_7 */ + }, + { + { {0, 4, 0xFFFF} }, /* TX_0 */ + { {8, 12, 0xFFFF} }, /* TX_1 */ + { {16, 20, 0xFFFF} }, /* TX_2 */ + { {24, 28, 0xFFFF} }, /* TX_3 */ + { {0xFFFF} }, /* TX_4 */ + { {0xFFFF} }, /* TX_5 */ + { {0xFFFF} }, /* TX_6 */ + { {0xFFFF} }, /* TX_7 */ + }, + { /* QUAT TDM */ + { {0, 4, 0xFFFF} }, /* RX_0 */ + { {8, 12, 0xFFFF} }, /* RX_1 */ + { {16, 20, 0xFFFF} }, /* RX_2 */ + { {24, 28, 0xFFFF} }, /* RX_3 */ + { {0xFFFF} }, /* RX_4 */ + { {0xFFFF} }, /* RX_5 */ + { {0xFFFF} }, /* RX_6 */ + { {0xFFFF} }, /* RX_7 */ + }, + { + { {0, 4, 0xFFFF} }, /* TX_0 */ + { {8, 12, 0xFFFF} }, /* TX_1 */ + { {16, 20, 0xFFFF} }, /* TX_2 */ + { {24, 28, 0xFFFF} }, /* TX_3 */ + { {0xFFFF} }, /* TX_4 */ + { {0xFFFF} }, /* TX_5 */ + { {0xFFFF} }, /* TX_6 */ + { {0xFFFF} }, /* TX_7 */ + }, +}; + +/* Default configuration of external display BE */ +static struct dev_config ext_disp_rx_cfg[] = { + [DP_RX_IDX] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, +}; + +static struct dev_config usb_rx_cfg = { + .sample_rate = SAMPLING_RATE_48KHZ, + .bit_format = SNDRV_PCM_FORMAT_S16_LE, + .channels = 2, +}; + +static struct dev_config usb_tx_cfg = { + .sample_rate = SAMPLING_RATE_48KHZ, + .bit_format = SNDRV_PCM_FORMAT_S16_LE, + .channels = 1, +}; + +static struct dev_config proxy_rx_cfg = { + .sample_rate = SAMPLING_RATE_48KHZ, + .bit_format = SNDRV_PCM_FORMAT_S16_LE, + .channels = 2, +}; + +/* Default configuration of MI2S channels */ +static struct dev_config mi2s_rx_cfg[] = { + [PRIM_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, + [SEC_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, + [TERT_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, + [QUAT_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, + [QUIN_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, +}; + +static struct dev_config mi2s_tx_cfg[] = { + [PRIM_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SEC_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [TERT_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [QUAT_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [QUIN_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, +}; + +static struct dev_config aux_pcm_rx_cfg[] = { + [PRIM_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SEC_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [TERT_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [QUAT_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [QUIN_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, +}; + +static struct dev_config aux_pcm_tx_cfg[] = { + [PRIM_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [SEC_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [TERT_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [QUAT_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, + [QUIN_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, +}; +static int msm_vi_feed_tx_ch = 2; +static const char *const slim_rx_ch_text[] = {"One", "Two"}; +static const char *const slim_tx_ch_text[] = {"One", "Two", "Three", "Four", + "Five", "Six", "Seven", + "Eight"}; +static const char *const vi_feed_ch_text[] = {"One", "Two"}; +static char const *bit_format_text[] = {"S16_LE", "S24_LE", "S24_3LE", + "S32_LE"}; +static char const *ext_disp_bit_format_text[] = {"S16_LE", "S24_LE", + "S24_3LE"}; +static char const *slim_sample_rate_text[] = {"KHZ_8", "KHZ_16", + "KHZ_32", "KHZ_44P1", "KHZ_48", + "KHZ_88P2", "KHZ_96", "KHZ_176P4", + "KHZ_192", "KHZ_352P8", "KHZ_384"}; +static char const *bt_sample_rate_text[] = {"KHZ_8", "KHZ_16", + "KHZ_44P1", "KHZ_48", + "KHZ_88P2", "KHZ_96"}; +static char const *bt_sample_rate_rx_text[] = {"KHZ_8", "KHZ_16", + "KHZ_44P1", "KHZ_48", + "KHZ_88P2", "KHZ_96"}; +static char const *bt_sample_rate_tx_text[] = {"KHZ_8", "KHZ_16", + "KHZ_44P1", "KHZ_48", + "KHZ_88P2", "KHZ_96"}; +static const char *const usb_ch_text[] = {"One", "Two", "Three", "Four", + "Five", "Six", "Seven", + "Eight"}; +static char const *ch_text[] = {"Two", "Three", "Four", "Five", + "Six", "Seven", "Eight"}; +static char const *usb_sample_rate_text[] = {"KHZ_8", "KHZ_11P025", + "KHZ_16", "KHZ_22P05", + "KHZ_32", "KHZ_44P1", "KHZ_48", + "KHZ_88P2", "KHZ_96", "KHZ_176P4", + "KHZ_192", "KHZ_352P8", "KHZ_384"}; +static char const *ext_disp_sample_rate_text[] = {"KHZ_48", "KHZ_96", + "KHZ_192", "KHZ_32", "KHZ_44P1", + "KHZ_88P2", "KHZ_176P4" }; +static char const *tdm_ch_text[] = {"One", "Two", "Three", "Four", + "Five", "Six", "Seven", "Eight"}; +static char const *tdm_bit_format_text[] = {"S16_LE", "S24_LE", "S32_LE"}; +static char const *tdm_sample_rate_text[] = {"KHZ_8", "KHZ_16", "KHZ_32", + "KHZ_48", "KHZ_176P4", + "KHZ_352P8"}; +static const char *const auxpcm_rate_text[] = {"KHZ_8", "KHZ_16"}; +static char const *mi2s_rate_text[] = {"KHZ_8", "KHZ_11P025", "KHZ_16", + "KHZ_22P05", "KHZ_32", "KHZ_44P1", + "KHZ_48", "KHZ_96", "KHZ_192"}; +static const char *const mi2s_ch_text[] = {"One", "Two", "Three", "Four", + "Five", "Six", "Seven", + "Eight"}; +static const char *const hifi_text[] = {"Off", "On"}; +static const char *const qos_text[] = {"Disable", "Enable"}; + +static SOC_ENUM_SINGLE_EXT_DECL(slim_0_rx_chs, slim_rx_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_2_rx_chs, slim_rx_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_0_tx_chs, slim_tx_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_1_tx_chs, slim_tx_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_5_rx_chs, slim_rx_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_6_rx_chs, slim_rx_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(usb_rx_chs, usb_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(usb_tx_chs, usb_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(vi_feed_tx_chs, vi_feed_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(ext_disp_rx_chs, ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(proxy_rx_chs, ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_0_rx_format, bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_5_rx_format, bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_6_rx_format, bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_0_tx_format, bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(usb_rx_format, bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(usb_tx_format, bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(ext_disp_rx_format, ext_disp_bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_0_rx_sample_rate, slim_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_2_rx_sample_rate, slim_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_0_tx_sample_rate, slim_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_5_rx_sample_rate, slim_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(slim_6_rx_sample_rate, slim_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(bt_sample_rate, bt_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(bt_sample_rate_rx, bt_sample_rate_rx_text); +static SOC_ENUM_SINGLE_EXT_DECL(bt_sample_rate_tx, bt_sample_rate_tx_text); +static SOC_ENUM_SINGLE_EXT_DECL(usb_rx_sample_rate, usb_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(usb_tx_sample_rate, usb_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(ext_disp_rx_sample_rate, + ext_disp_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(tdm_tx_chs, tdm_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(tdm_tx_format, tdm_bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(tdm_tx_sample_rate, tdm_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(tdm_rx_chs, tdm_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(tdm_rx_format, tdm_bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(tdm_rx_sample_rate, tdm_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(prim_aux_pcm_rx_sample_rate, auxpcm_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(sec_aux_pcm_rx_sample_rate, auxpcm_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(tert_aux_pcm_rx_sample_rate, auxpcm_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(quat_aux_pcm_rx_sample_rate, auxpcm_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(quin_aux_pcm_rx_sample_rate, auxpcm_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(prim_aux_pcm_tx_sample_rate, auxpcm_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(sec_aux_pcm_tx_sample_rate, auxpcm_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(tert_aux_pcm_tx_sample_rate, auxpcm_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(quat_aux_pcm_tx_sample_rate, auxpcm_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(quin_aux_pcm_tx_sample_rate, auxpcm_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(prim_mi2s_rx_sample_rate, mi2s_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(sec_mi2s_rx_sample_rate, mi2s_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(tert_mi2s_rx_sample_rate, mi2s_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(quat_mi2s_rx_sample_rate, mi2s_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(quin_mi2s_rx_sample_rate, mi2s_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(prim_mi2s_tx_sample_rate, mi2s_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(sec_mi2s_tx_sample_rate, mi2s_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(tert_mi2s_tx_sample_rate, mi2s_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(quat_mi2s_tx_sample_rate, mi2s_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(quin_mi2s_tx_sample_rate, mi2s_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(prim_mi2s_rx_chs, mi2s_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(prim_mi2s_tx_chs, mi2s_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(sec_mi2s_rx_chs, mi2s_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(sec_mi2s_tx_chs, mi2s_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(tert_mi2s_rx_chs, mi2s_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(tert_mi2s_tx_chs, mi2s_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(quat_mi2s_rx_chs, mi2s_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(quat_mi2s_tx_chs, mi2s_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(quin_mi2s_rx_chs, mi2s_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(quin_mi2s_tx_chs, mi2s_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(mi2s_rx_format, bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(mi2s_tx_format, bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(aux_pcm_rx_format, bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(aux_pcm_tx_format, bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(hifi_function, hifi_text); + +static struct platform_device *spdev; + +static int msm_hifi_control; +static bool is_initial_boot; +static bool codec_reg_done; +static struct snd_soc_aux_dev *msm_aux_dev; +static struct snd_soc_codec_conf *msm_codec_conf; +static struct msm_asoc_wcd93xx_codec msm_codec_fn; + +static void *def_wcd_mbhc_cal(void); +static int msm_snd_enable_codec_ext_clk(struct snd_soc_codec *codec, + int enable, bool dapm); +static int msm_wsa881x_init(struct snd_soc_component *component); + +/* + * Need to report LINEIN + * if R/L channel impedance is larger than 5K ohm + */ +static struct wcd_mbhc_config wcd_mbhc_cfg = { + .read_fw_bin = false, + .calibration = NULL, + .detect_extn_cable = true, + .mono_stero_detection = false, + .swap_gnd_mic = NULL, + .hs_ext_micbias = true, + .key_code[0] = KEY_MEDIA, + .key_code[1] = KEY_VOICECOMMAND, + .key_code[2] = KEY_VOLUMEUP, + .key_code[3] = KEY_VOLUMEDOWN, + .key_code[4] = 0, + .key_code[5] = 0, + .key_code[6] = 0, + .key_code[7] = 0, + .linein_th = 5000, + .moisture_en = true, + .mbhc_micbias = MIC_BIAS_2, + .anc_micbias = MIC_BIAS_2, + .enable_anc_mic_detect = false, +}; + +static struct snd_soc_dapm_route wcd_audio_paths[] = { + {"MIC BIAS1", NULL, "MCLK TX"}, + {"MIC BIAS3", NULL, "MCLK TX"}, + {"MIC BIAS4", NULL, "MCLK TX"}, +}; + +static struct snd_soc_dapm_route wcd_audio_paths_tavil[] = { + {"MIC BIAS1", NULL, "MCLK TX"}, + {"MIC BIAS2", NULL, "MCLK TX"}, + {"MIC BIAS3", NULL, "MCLK TX"}, + {"MIC BIAS4", NULL, "MCLK TX"}, +}; + +static struct afe_clk_set mi2s_clk[MI2S_MAX] = { + { + AFE_API_VERSION_I2S_CONFIG, + Q6AFE_LPASS_CLK_ID_PRI_MI2S_IBIT, + Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ, + Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO, + Q6AFE_LPASS_CLK_ROOT_DEFAULT, + 0, + }, + { + AFE_API_VERSION_I2S_CONFIG, + Q6AFE_LPASS_CLK_ID_SEC_MI2S_IBIT, + Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ, + Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO, + Q6AFE_LPASS_CLK_ROOT_DEFAULT, + 0, + }, + { + AFE_API_VERSION_I2S_CONFIG, + Q6AFE_LPASS_CLK_ID_TER_MI2S_IBIT, + Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ, + Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO, + Q6AFE_LPASS_CLK_ROOT_DEFAULT, + 0, + }, + { + AFE_API_VERSION_I2S_CONFIG, + Q6AFE_LPASS_CLK_ID_QUAD_MI2S_IBIT, + Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ, + Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO, + Q6AFE_LPASS_CLK_ROOT_DEFAULT, + 0, + }, + { + AFE_API_VERSION_I2S_CONFIG, + Q6AFE_LPASS_CLK_ID_QUI_MI2S_IBIT, + Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ, + Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO, + Q6AFE_LPASS_CLK_ROOT_DEFAULT, + 0, + } + +}; + +static struct mi2s_conf mi2s_intf_conf[MI2S_MAX]; + +static int slim_get_sample_rate_val(int sample_rate) +{ + int sample_rate_val = 0; + + switch (sample_rate) { + case SAMPLING_RATE_8KHZ: + sample_rate_val = 0; + break; + case SAMPLING_RATE_16KHZ: + sample_rate_val = 1; + break; + case SAMPLING_RATE_32KHZ: + sample_rate_val = 2; + break; + case SAMPLING_RATE_44P1KHZ: + sample_rate_val = 3; + break; + case SAMPLING_RATE_48KHZ: + sample_rate_val = 4; + break; + case SAMPLING_RATE_88P2KHZ: + sample_rate_val = 5; + break; + case SAMPLING_RATE_96KHZ: + sample_rate_val = 6; + break; + case SAMPLING_RATE_176P4KHZ: + sample_rate_val = 7; + break; + case SAMPLING_RATE_192KHZ: + sample_rate_val = 8; + break; + case SAMPLING_RATE_352P8KHZ: + sample_rate_val = 9; + break; + case SAMPLING_RATE_384KHZ: + sample_rate_val = 10; + break; + default: + sample_rate_val = 4; + break; + } + return sample_rate_val; +} + +static int slim_get_sample_rate(int value) +{ + int sample_rate = 0; + + switch (value) { + case 0: + sample_rate = SAMPLING_RATE_8KHZ; + break; + case 1: + sample_rate = SAMPLING_RATE_16KHZ; + break; + case 2: + sample_rate = SAMPLING_RATE_32KHZ; + break; + case 3: + sample_rate = SAMPLING_RATE_44P1KHZ; + break; + case 4: + sample_rate = SAMPLING_RATE_48KHZ; + break; + case 5: + sample_rate = SAMPLING_RATE_88P2KHZ; + break; + case 6: + sample_rate = SAMPLING_RATE_96KHZ; + break; + case 7: + sample_rate = SAMPLING_RATE_176P4KHZ; + break; + case 8: + sample_rate = SAMPLING_RATE_192KHZ; + break; + case 9: + sample_rate = SAMPLING_RATE_352P8KHZ; + break; + case 10: + sample_rate = SAMPLING_RATE_384KHZ; + break; + default: + sample_rate = SAMPLING_RATE_48KHZ; + break; + } + return sample_rate; +} + +static int slim_get_bit_format_val(int bit_format) +{ + int val = 0; + + switch (bit_format) { + case SNDRV_PCM_FORMAT_S32_LE: + val = 3; + break; + case SNDRV_PCM_FORMAT_S24_3LE: + val = 2; + break; + case SNDRV_PCM_FORMAT_S24_LE: + val = 1; + break; + case SNDRV_PCM_FORMAT_S16_LE: + default: + val = 0; + break; + } + return val; +} + +static int slim_get_bit_format(int val) +{ + int bit_fmt = SNDRV_PCM_FORMAT_S16_LE; + + switch (val) { + case 0: + bit_fmt = SNDRV_PCM_FORMAT_S16_LE; + break; + case 1: + bit_fmt = SNDRV_PCM_FORMAT_S24_LE; + break; + case 2: + bit_fmt = SNDRV_PCM_FORMAT_S24_3LE; + break; + case 3: + bit_fmt = SNDRV_PCM_FORMAT_S32_LE; + break; + default: + bit_fmt = SNDRV_PCM_FORMAT_S16_LE; + break; + } + return bit_fmt; +} + +static int slim_get_port_idx(struct snd_kcontrol *kcontrol) +{ + int port_id = 0; + + if (strnstr(kcontrol->id.name, "SLIM_0_RX", sizeof("SLIM_0_RX"))) { + port_id = SLIM_RX_0; + } else if (strnstr(kcontrol->id.name, + "SLIM_2_RX", sizeof("SLIM_2_RX"))) { + port_id = SLIM_RX_2; + } else if (strnstr(kcontrol->id.name, + "SLIM_5_RX", sizeof("SLIM_5_RX"))) { + port_id = SLIM_RX_5; + } else if (strnstr(kcontrol->id.name, + "SLIM_6_RX", sizeof("SLIM_6_RX"))) { + port_id = SLIM_RX_6; + } else if (strnstr(kcontrol->id.name, + "SLIM_0_TX", sizeof("SLIM_0_TX"))) { + port_id = SLIM_TX_0; + } else if (strnstr(kcontrol->id.name, + "SLIM_1_TX", sizeof("SLIM_1_TX"))) { + port_id = SLIM_TX_1; + } else { + pr_err("%s: unsupported channel: %s", + __func__, kcontrol->id.name); + return -EINVAL; + } + + return port_id; +} + +static int slim_rx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = slim_get_port_idx(kcontrol); + + if (ch_num < 0) + return ch_num; + + ucontrol->value.enumerated.item[0] = + slim_get_sample_rate_val(slim_rx_cfg[ch_num].sample_rate); + + pr_debug("%s: slim[%d]_rx_sample_rate = %d, item = %d\n", __func__, + ch_num, slim_rx_cfg[ch_num].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int slim_rx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = slim_get_port_idx(kcontrol); + + if (ch_num < 0) + return ch_num; + + slim_rx_cfg[ch_num].sample_rate = + slim_get_sample_rate(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: slim[%d]_rx_sample_rate = %d, item = %d\n", __func__, + ch_num, slim_rx_cfg[ch_num].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int slim_tx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = slim_get_port_idx(kcontrol); + + if (ch_num < 0) + return ch_num; + + ucontrol->value.enumerated.item[0] = + slim_get_sample_rate_val(slim_tx_cfg[ch_num].sample_rate); + + pr_debug("%s: slim[%d]_tx_sample_rate = %d, item = %d\n", __func__, + ch_num, slim_tx_cfg[ch_num].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int slim_tx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int sample_rate = 0; + int ch_num = slim_get_port_idx(kcontrol); + + if (ch_num < 0) + return ch_num; + + sample_rate = slim_get_sample_rate(ucontrol->value.enumerated.item[0]); + if (sample_rate == SAMPLING_RATE_44P1KHZ) { + pr_err("%s: Unsupported sample rate %d: for Tx path\n", + __func__, sample_rate); + return -EINVAL; + } + slim_tx_cfg[ch_num].sample_rate = sample_rate; + + pr_debug("%s: slim[%d]_tx_sample_rate = %d, value = %d\n", __func__, + ch_num, slim_tx_cfg[ch_num].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int slim_rx_bit_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = slim_get_port_idx(kcontrol); + + if (ch_num < 0) + return ch_num; + + ucontrol->value.enumerated.item[0] = + slim_get_bit_format_val(slim_rx_cfg[ch_num].bit_format); + + pr_debug("%s: slim[%d]_rx_bit_format = %d, ucontrol value = %d\n", + __func__, ch_num, slim_rx_cfg[ch_num].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int slim_rx_bit_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = slim_get_port_idx(kcontrol); + + if (ch_num < 0) + return ch_num; + + slim_rx_cfg[ch_num].bit_format = + slim_get_bit_format(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: slim[%d]_rx_bit_format = %d, ucontrol value = %d\n", + __func__, ch_num, slim_rx_cfg[ch_num].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int slim_tx_bit_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = slim_get_port_idx(kcontrol); + + if (ch_num < 0) + return ch_num; + + ucontrol->value.enumerated.item[0] = + slim_get_bit_format_val(slim_tx_cfg[ch_num].bit_format); + + pr_debug("%s: slim[%d]_tx_bit_format = %d, ucontrol value = %d\n", + __func__, ch_num, slim_tx_cfg[ch_num].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int slim_tx_bit_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = slim_get_port_idx(kcontrol); + + if (ch_num < 0) + return ch_num; + + slim_tx_cfg[ch_num].bit_format = + slim_get_bit_format(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: slim[%d]_tx_bit_format = %d, ucontrol value = %d\n", + __func__, ch_num, slim_tx_cfg[ch_num].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int slim_rx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = slim_get_port_idx(kcontrol); + + if (ch_num < 0) + return ch_num; + + pr_debug("%s: msm_slim_[%d]_rx_ch = %d\n", __func__, + ch_num, slim_rx_cfg[ch_num].channels); + ucontrol->value.enumerated.item[0] = slim_rx_cfg[ch_num].channels - 1; + + return 0; +} + +static int slim_rx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = slim_get_port_idx(kcontrol); + + if (ch_num < 0) + return ch_num; + + slim_rx_cfg[ch_num].channels = ucontrol->value.enumerated.item[0] + 1; + pr_debug("%s: msm_slim_[%d]_rx_ch = %d\n", __func__, + ch_num, slim_rx_cfg[ch_num].channels); + + return 1; +} + +static int slim_tx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = slim_get_port_idx(kcontrol); + + if (ch_num < 0) + return ch_num; + + pr_debug("%s: msm_slim_[%d]_tx_ch = %d\n", __func__, + ch_num, slim_tx_cfg[ch_num].channels); + ucontrol->value.enumerated.item[0] = slim_tx_cfg[ch_num].channels - 1; + + return 0; +} + +static int slim_tx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ch_num = slim_get_port_idx(kcontrol); + + if (ch_num < 0) + return ch_num; + + slim_tx_cfg[ch_num].channels = ucontrol->value.enumerated.item[0] + 1; + pr_debug("%s: msm_slim_[%d]_tx_ch = %d\n", __func__, + ch_num, slim_tx_cfg[ch_num].channels); + + return 1; +} + +static int msm_vi_feed_tx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = msm_vi_feed_tx_ch - 1; + pr_debug("%s: msm_vi_feed_tx_ch = %ld\n", __func__, + ucontrol->value.integer.value[0]); + return 0; +} + +static int msm_vi_feed_tx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + msm_vi_feed_tx_ch = ucontrol->value.integer.value[0] + 1; + + pr_debug("%s: msm_vi_feed_tx_ch = %d\n", __func__, msm_vi_feed_tx_ch); + return 1; +} + +static int msm_bt_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + /* + * Slimbus_7_Rx/Tx sample rate values should always be in sync (same) + * when used for BT_SCO use case. Return either Rx or Tx sample rate + * value. + */ + switch (slim_rx_cfg[SLIM_RX_7].sample_rate) { + case SAMPLING_RATE_96KHZ: + ucontrol->value.integer.value[0] = 5; + break; + case SAMPLING_RATE_88P2KHZ: + ucontrol->value.integer.value[0] = 4; + break; + case SAMPLING_RATE_48KHZ: + ucontrol->value.integer.value[0] = 3; + break; + case SAMPLING_RATE_44P1KHZ: + ucontrol->value.integer.value[0] = 2; + break; + case SAMPLING_RATE_16KHZ: + ucontrol->value.integer.value[0] = 1; + break; + case SAMPLING_RATE_8KHZ: + default: + ucontrol->value.integer.value[0] = 0; + break; + } + pr_debug("%s: sample rate = %d", __func__, + slim_rx_cfg[SLIM_RX_7].sample_rate); + + return 0; +} + +static int msm_bt_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (ucontrol->value.integer.value[0]) { + case 1: + slim_rx_cfg[SLIM_RX_7].sample_rate = SAMPLING_RATE_16KHZ; + slim_tx_cfg[SLIM_TX_7].sample_rate = SAMPLING_RATE_16KHZ; + break; + case 2: + slim_rx_cfg[SLIM_RX_7].sample_rate = SAMPLING_RATE_44P1KHZ; + slim_tx_cfg[SLIM_TX_7].sample_rate = SAMPLING_RATE_44P1KHZ; + break; + case 3: + slim_rx_cfg[SLIM_RX_7].sample_rate = SAMPLING_RATE_48KHZ; + slim_tx_cfg[SLIM_TX_7].sample_rate = SAMPLING_RATE_48KHZ; + break; + case 4: + slim_rx_cfg[SLIM_RX_7].sample_rate = SAMPLING_RATE_88P2KHZ; + slim_tx_cfg[SLIM_TX_7].sample_rate = SAMPLING_RATE_88P2KHZ; + break; + case 5: + slim_rx_cfg[SLIM_RX_7].sample_rate = SAMPLING_RATE_96KHZ; + slim_tx_cfg[SLIM_TX_7].sample_rate = SAMPLING_RATE_96KHZ; + break; + case 0: + default: + slim_rx_cfg[SLIM_RX_7].sample_rate = SAMPLING_RATE_8KHZ; + slim_tx_cfg[SLIM_TX_7].sample_rate = SAMPLING_RATE_8KHZ; + break; + } + pr_debug("%s: sample rates: slim7_rx = %d, slim7_tx = %d, value = %d\n", + __func__, + slim_rx_cfg[SLIM_RX_7].sample_rate, + slim_tx_cfg[SLIM_TX_7].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int msm_bt_sample_rate_rx_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (slim_rx_cfg[SLIM_RX_7].sample_rate) { + case SAMPLING_RATE_96KHZ: + ucontrol->value.integer.value[0] = 5; + break; + case SAMPLING_RATE_88P2KHZ: + ucontrol->value.integer.value[0] = 4; + break; + case SAMPLING_RATE_48KHZ: + ucontrol->value.integer.value[0] = 3; + break; + case SAMPLING_RATE_44P1KHZ: + ucontrol->value.integer.value[0] = 2; + break; + case SAMPLING_RATE_16KHZ: + ucontrol->value.integer.value[0] = 1; + break; + case SAMPLING_RATE_8KHZ: + default: + ucontrol->value.integer.value[0] = 0; + break; + } + pr_debug("%s: sample rate rx = %d", __func__, + slim_rx_cfg[SLIM_RX_7].sample_rate); + + return 0; +} + +static int msm_bt_sample_rate_rx_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (ucontrol->value.integer.value[0]) { + case 1: + slim_rx_cfg[SLIM_RX_7].sample_rate = SAMPLING_RATE_16KHZ; + break; + case 2: + slim_rx_cfg[SLIM_RX_7].sample_rate = SAMPLING_RATE_44P1KHZ; + break; + case 3: + slim_rx_cfg[SLIM_RX_7].sample_rate = SAMPLING_RATE_48KHZ; + break; + case 4: + slim_rx_cfg[SLIM_RX_7].sample_rate = SAMPLING_RATE_88P2KHZ; + break; + case 5: + slim_rx_cfg[SLIM_RX_7].sample_rate = SAMPLING_RATE_96KHZ; + break; + case 0: + default: + slim_rx_cfg[SLIM_RX_7].sample_rate = SAMPLING_RATE_8KHZ; + break; + } + pr_debug("%s: sample rate: slim7_rx = %d, value = %d\n", + __func__, + slim_rx_cfg[SLIM_RX_7].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int msm_bt_sample_rate_tx_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (slim_tx_cfg[SLIM_TX_7].sample_rate) { + case SAMPLING_RATE_96KHZ: + ucontrol->value.integer.value[0] = 5; + break; + case SAMPLING_RATE_88P2KHZ: + ucontrol->value.integer.value[0] = 4; + break; + case SAMPLING_RATE_48KHZ: + ucontrol->value.integer.value[0] = 3; + break; + case SAMPLING_RATE_44P1KHZ: + ucontrol->value.integer.value[0] = 2; + break; + case SAMPLING_RATE_16KHZ: + ucontrol->value.integer.value[0] = 1; + break; + case SAMPLING_RATE_8KHZ: + default: + ucontrol->value.integer.value[0] = 0; + break; + } + pr_debug("%s: sample rate tx = %d", __func__, + slim_tx_cfg[SLIM_TX_7].sample_rate); + + return 0; +} + +static int msm_bt_sample_rate_tx_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (ucontrol->value.integer.value[0]) { + case 1: + slim_tx_cfg[SLIM_TX_7].sample_rate = SAMPLING_RATE_16KHZ; + break; + case 2: + slim_tx_cfg[SLIM_TX_7].sample_rate = SAMPLING_RATE_44P1KHZ; + break; + case 3: + slim_tx_cfg[SLIM_TX_7].sample_rate = SAMPLING_RATE_48KHZ; + break; + case 4: + slim_tx_cfg[SLIM_TX_7].sample_rate = SAMPLING_RATE_88P2KHZ; + break; + case 5: + slim_tx_cfg[SLIM_TX_7].sample_rate = SAMPLING_RATE_96KHZ; + break; + case 0: + default: + slim_tx_cfg[SLIM_TX_7].sample_rate = SAMPLING_RATE_8KHZ; + break; + } + pr_debug("%s: sample rate: slim7_tx = %d, value = %d\n", + __func__, + slim_tx_cfg[SLIM_TX_7].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int usb_audio_rx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s: usb_audio_rx_ch = %d\n", __func__, + usb_rx_cfg.channels); + ucontrol->value.integer.value[0] = usb_rx_cfg.channels - 1; + return 0; +} + +static int usb_audio_rx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + usb_rx_cfg.channels = ucontrol->value.integer.value[0] + 1; + + pr_debug("%s: usb_audio_rx_ch = %d\n", __func__, usb_rx_cfg.channels); + return 1; +} + +static int usb_audio_rx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int sample_rate_val; + + switch (usb_rx_cfg.sample_rate) { + case SAMPLING_RATE_384KHZ: + sample_rate_val = 12; + break; + case SAMPLING_RATE_352P8KHZ: + sample_rate_val = 11; + break; + case SAMPLING_RATE_192KHZ: + sample_rate_val = 10; + break; + case SAMPLING_RATE_176P4KHZ: + sample_rate_val = 9; + break; + case SAMPLING_RATE_96KHZ: + sample_rate_val = 8; + break; + case SAMPLING_RATE_88P2KHZ: + sample_rate_val = 7; + break; + case SAMPLING_RATE_48KHZ: + sample_rate_val = 6; + break; + case SAMPLING_RATE_44P1KHZ: + sample_rate_val = 5; + break; + case SAMPLING_RATE_32KHZ: + sample_rate_val = 4; + break; + case SAMPLING_RATE_22P05KHZ: + sample_rate_val = 3; + break; + case SAMPLING_RATE_16KHZ: + sample_rate_val = 2; + break; + case SAMPLING_RATE_11P025KHZ: + sample_rate_val = 1; + break; + case SAMPLING_RATE_8KHZ: + default: + sample_rate_val = 0; + break; + } + + ucontrol->value.integer.value[0] = sample_rate_val; + pr_debug("%s: usb_audio_rx_sample_rate = %d\n", __func__, + usb_rx_cfg.sample_rate); + return 0; +} + +static int usb_audio_rx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (ucontrol->value.integer.value[0]) { + case 12: + usb_rx_cfg.sample_rate = SAMPLING_RATE_384KHZ; + break; + case 11: + usb_rx_cfg.sample_rate = SAMPLING_RATE_352P8KHZ; + break; + case 10: + usb_rx_cfg.sample_rate = SAMPLING_RATE_192KHZ; + break; + case 9: + usb_rx_cfg.sample_rate = SAMPLING_RATE_176P4KHZ; + break; + case 8: + usb_rx_cfg.sample_rate = SAMPLING_RATE_96KHZ; + break; + case 7: + usb_rx_cfg.sample_rate = SAMPLING_RATE_88P2KHZ; + break; + case 6: + usb_rx_cfg.sample_rate = SAMPLING_RATE_48KHZ; + break; + case 5: + usb_rx_cfg.sample_rate = SAMPLING_RATE_44P1KHZ; + break; + case 4: + usb_rx_cfg.sample_rate = SAMPLING_RATE_32KHZ; + break; + case 3: + usb_rx_cfg.sample_rate = SAMPLING_RATE_22P05KHZ; + break; + case 2: + usb_rx_cfg.sample_rate = SAMPLING_RATE_16KHZ; + break; + case 1: + usb_rx_cfg.sample_rate = SAMPLING_RATE_11P025KHZ; + break; + case 0: + usb_rx_cfg.sample_rate = SAMPLING_RATE_8KHZ; + break; + default: + usb_rx_cfg.sample_rate = SAMPLING_RATE_48KHZ; + break; + } + + pr_debug("%s: control value = %ld, usb_audio_rx_sample_rate = %d\n", + __func__, ucontrol->value.integer.value[0], + usb_rx_cfg.sample_rate); + return 0; +} + +static int usb_audio_rx_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (usb_rx_cfg.bit_format) { + case SNDRV_PCM_FORMAT_S32_LE: + ucontrol->value.integer.value[0] = 3; + break; + case SNDRV_PCM_FORMAT_S24_3LE: + ucontrol->value.integer.value[0] = 2; + break; + case SNDRV_PCM_FORMAT_S24_LE: + ucontrol->value.integer.value[0] = 1; + break; + case SNDRV_PCM_FORMAT_S16_LE: + default: + ucontrol->value.integer.value[0] = 0; + break; + } + + pr_debug("%s: usb_audio_rx_format = %d, ucontrol value = %ld\n", + __func__, usb_rx_cfg.bit_format, + ucontrol->value.integer.value[0]); + return 0; +} + +static int usb_audio_rx_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int rc = 0; + + switch (ucontrol->value.integer.value[0]) { + case 3: + usb_rx_cfg.bit_format = SNDRV_PCM_FORMAT_S32_LE; + break; + case 2: + usb_rx_cfg.bit_format = SNDRV_PCM_FORMAT_S24_3LE; + break; + case 1: + usb_rx_cfg.bit_format = SNDRV_PCM_FORMAT_S24_LE; + break; + case 0: + default: + usb_rx_cfg.bit_format = SNDRV_PCM_FORMAT_S16_LE; + break; + } + pr_debug("%s: usb_audio_rx_format = %d, ucontrol value = %ld\n", + __func__, usb_rx_cfg.bit_format, + ucontrol->value.integer.value[0]); + + return rc; +} + +static int usb_audio_tx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s: usb_audio_tx_ch = %d\n", __func__, + usb_tx_cfg.channels); + ucontrol->value.integer.value[0] = usb_tx_cfg.channels - 1; + return 0; +} + +static int usb_audio_tx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + usb_tx_cfg.channels = ucontrol->value.integer.value[0] + 1; + + pr_debug("%s: usb_audio_tx_ch = %d\n", __func__, usb_tx_cfg.channels); + return 1; +} + +static int usb_audio_tx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int sample_rate_val; + + switch (usb_tx_cfg.sample_rate) { + case SAMPLING_RATE_384KHZ: + sample_rate_val = 12; + break; + case SAMPLING_RATE_352P8KHZ: + sample_rate_val = 11; + break; + case SAMPLING_RATE_192KHZ: + sample_rate_val = 10; + break; + case SAMPLING_RATE_176P4KHZ: + sample_rate_val = 9; + break; + case SAMPLING_RATE_96KHZ: + sample_rate_val = 8; + break; + case SAMPLING_RATE_88P2KHZ: + sample_rate_val = 7; + break; + case SAMPLING_RATE_48KHZ: + sample_rate_val = 6; + break; + case SAMPLING_RATE_44P1KHZ: + sample_rate_val = 5; + break; + case SAMPLING_RATE_32KHZ: + sample_rate_val = 4; + break; + case SAMPLING_RATE_22P05KHZ: + sample_rate_val = 3; + break; + case SAMPLING_RATE_16KHZ: + sample_rate_val = 2; + break; + case SAMPLING_RATE_11P025KHZ: + sample_rate_val = 1; + break; + case SAMPLING_RATE_8KHZ: + sample_rate_val = 0; + break; + default: + sample_rate_val = 6; + break; + } + + ucontrol->value.integer.value[0] = sample_rate_val; + pr_debug("%s: usb_audio_tx_sample_rate = %d\n", __func__, + usb_tx_cfg.sample_rate); + return 0; +} + +static int usb_audio_tx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (ucontrol->value.integer.value[0]) { + case 12: + usb_tx_cfg.sample_rate = SAMPLING_RATE_384KHZ; + break; + case 11: + usb_tx_cfg.sample_rate = SAMPLING_RATE_352P8KHZ; + break; + case 10: + usb_tx_cfg.sample_rate = SAMPLING_RATE_192KHZ; + break; + case 9: + usb_tx_cfg.sample_rate = SAMPLING_RATE_176P4KHZ; + break; + case 8: + usb_tx_cfg.sample_rate = SAMPLING_RATE_96KHZ; + break; + case 7: + usb_tx_cfg.sample_rate = SAMPLING_RATE_88P2KHZ; + break; + case 6: + usb_tx_cfg.sample_rate = SAMPLING_RATE_48KHZ; + break; + case 5: + usb_tx_cfg.sample_rate = SAMPLING_RATE_44P1KHZ; + break; + case 4: + usb_tx_cfg.sample_rate = SAMPLING_RATE_32KHZ; + break; + case 3: + usb_tx_cfg.sample_rate = SAMPLING_RATE_22P05KHZ; + break; + case 2: + usb_tx_cfg.sample_rate = SAMPLING_RATE_16KHZ; + break; + case 1: + usb_tx_cfg.sample_rate = SAMPLING_RATE_11P025KHZ; + break; + case 0: + usb_tx_cfg.sample_rate = SAMPLING_RATE_8KHZ; + break; + default: + usb_tx_cfg.sample_rate = SAMPLING_RATE_48KHZ; + break; + } + + pr_debug("%s: control value = %ld, usb_audio_tx_sample_rate = %d\n", + __func__, ucontrol->value.integer.value[0], + usb_tx_cfg.sample_rate); + return 0; +} + +static int usb_audio_tx_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (usb_tx_cfg.bit_format) { + case SNDRV_PCM_FORMAT_S32_LE: + ucontrol->value.integer.value[0] = 3; + break; + case SNDRV_PCM_FORMAT_S24_3LE: + ucontrol->value.integer.value[0] = 2; + break; + case SNDRV_PCM_FORMAT_S24_LE: + ucontrol->value.integer.value[0] = 1; + break; + case SNDRV_PCM_FORMAT_S16_LE: + default: + ucontrol->value.integer.value[0] = 0; + break; + } + + pr_debug("%s: usb_audio_tx_format = %d, ucontrol value = %ld\n", + __func__, usb_tx_cfg.bit_format, + ucontrol->value.integer.value[0]); + return 0; +} + +static int usb_audio_tx_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int rc = 0; + + switch (ucontrol->value.integer.value[0]) { + case 3: + usb_tx_cfg.bit_format = SNDRV_PCM_FORMAT_S32_LE; + break; + case 2: + usb_tx_cfg.bit_format = SNDRV_PCM_FORMAT_S24_3LE; + break; + case 1: + usb_tx_cfg.bit_format = SNDRV_PCM_FORMAT_S24_LE; + break; + case 0: + default: + usb_tx_cfg.bit_format = SNDRV_PCM_FORMAT_S16_LE; + break; + } + pr_debug("%s: usb_audio_tx_format = %d, ucontrol value = %ld\n", + __func__, usb_tx_cfg.bit_format, + ucontrol->value.integer.value[0]); + + return rc; +} + +static int ext_disp_get_port_idx(struct snd_kcontrol *kcontrol) +{ + int idx; + + if (strnstr(kcontrol->id.name, "Display Port RX", + sizeof("Display Port RX"))) { + idx = DP_RX_IDX; + } else { + pr_err("%s: unsupported BE: %s", + __func__, kcontrol->id.name); + idx = -EINVAL; + } + + return idx; +} + +static int ext_disp_rx_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = ext_disp_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + switch (ext_disp_rx_cfg[idx].bit_format) { + case SNDRV_PCM_FORMAT_S24_3LE: + ucontrol->value.integer.value[0] = 2; + break; + case SNDRV_PCM_FORMAT_S24_LE: + ucontrol->value.integer.value[0] = 1; + break; + case SNDRV_PCM_FORMAT_S16_LE: + default: + ucontrol->value.integer.value[0] = 0; + break; + } + + pr_debug("%s: ext_disp_rx[%d].format = %d, ucontrol value = %ld\n", + __func__, idx, ext_disp_rx_cfg[idx].bit_format, + ucontrol->value.integer.value[0]); + return 0; +} + +static int ext_disp_rx_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = ext_disp_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + switch (ucontrol->value.integer.value[0]) { + case 2: + ext_disp_rx_cfg[idx].bit_format = SNDRV_PCM_FORMAT_S24_3LE; + break; + case 1: + ext_disp_rx_cfg[idx].bit_format = SNDRV_PCM_FORMAT_S24_LE; + break; + case 0: + default: + ext_disp_rx_cfg[idx].bit_format = SNDRV_PCM_FORMAT_S16_LE; + break; + } + pr_debug("%s: ext_disp_rx[%d].format = %d, ucontrol value = %ld\n", + __func__, idx, ext_disp_rx_cfg[idx].bit_format, + ucontrol->value.integer.value[0]); + + return 0; +} + +static int ext_disp_rx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = ext_disp_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + ucontrol->value.integer.value[0] = + ext_disp_rx_cfg[idx].channels - 2; + + pr_debug("%s: ext_disp_rx[%d].ch = %d\n", __func__, + idx, ext_disp_rx_cfg[idx].channels); + + return 0; +} + +static int ext_disp_rx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = ext_disp_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + ext_disp_rx_cfg[idx].channels = + ucontrol->value.integer.value[0] + 2; + + pr_debug("%s: ext_disp_rx[%d].ch = %d\n", __func__, + idx, ext_disp_rx_cfg[idx].channels); + return 1; +} + +static int ext_disp_rx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int sample_rate_val; + int idx = ext_disp_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + switch (ext_disp_rx_cfg[idx].sample_rate) { + case SAMPLING_RATE_176P4KHZ: + sample_rate_val = 6; + break; + + case SAMPLING_RATE_88P2KHZ: + sample_rate_val = 5; + break; + + case SAMPLING_RATE_44P1KHZ: + sample_rate_val = 4; + break; + + case SAMPLING_RATE_32KHZ: + sample_rate_val = 3; + break; + + case SAMPLING_RATE_192KHZ: + sample_rate_val = 2; + break; + + case SAMPLING_RATE_96KHZ: + sample_rate_val = 1; + break; + + case SAMPLING_RATE_48KHZ: + default: + sample_rate_val = 0; + break; + } + + ucontrol->value.integer.value[0] = sample_rate_val; + pr_debug("%s: ext_disp_rx[%d].sample_rate = %d\n", __func__, + idx, ext_disp_rx_cfg[idx].sample_rate); + + return 0; +} + +static int ext_disp_rx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = ext_disp_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + switch (ucontrol->value.integer.value[0]) { + case 6: + ext_disp_rx_cfg[idx].sample_rate = SAMPLING_RATE_176P4KHZ; + break; + case 5: + ext_disp_rx_cfg[idx].sample_rate = SAMPLING_RATE_88P2KHZ; + break; + case 4: + ext_disp_rx_cfg[idx].sample_rate = SAMPLING_RATE_44P1KHZ; + break; + case 3: + ext_disp_rx_cfg[idx].sample_rate = SAMPLING_RATE_32KHZ; + break; + case 2: + ext_disp_rx_cfg[idx].sample_rate = SAMPLING_RATE_192KHZ; + break; + case 1: + ext_disp_rx_cfg[idx].sample_rate = SAMPLING_RATE_96KHZ; + break; + case 0: + default: + ext_disp_rx_cfg[idx].sample_rate = SAMPLING_RATE_48KHZ; + break; + } + + pr_debug("%s: control value = %ld, ext_disp_rx[%d].sample_rate = %d\n", + __func__, ucontrol->value.integer.value[0], idx, + ext_disp_rx_cfg[idx].sample_rate); + return 0; +} + +static int proxy_rx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s: proxy_rx channels = %d\n", + __func__, proxy_rx_cfg.channels); + ucontrol->value.integer.value[0] = proxy_rx_cfg.channels - 2; + + return 0; +} + +static int proxy_rx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + proxy_rx_cfg.channels = ucontrol->value.integer.value[0] + 2; + pr_debug("%s: proxy_rx channels = %d\n", + __func__, proxy_rx_cfg.channels); + + return 1; +} + +static int tdm_get_sample_rate(int value) +{ + int sample_rate = 0; + + switch (value) { + case 0: + sample_rate = SAMPLING_RATE_8KHZ; + break; + case 1: + sample_rate = SAMPLING_RATE_16KHZ; + break; + case 2: + sample_rate = SAMPLING_RATE_32KHZ; + break; + case 3: + sample_rate = SAMPLING_RATE_48KHZ; + break; + case 4: + sample_rate = SAMPLING_RATE_176P4KHZ; + break; + case 5: + sample_rate = SAMPLING_RATE_352P8KHZ; + break; + default: + sample_rate = SAMPLING_RATE_48KHZ; + break; + } + return sample_rate; +} + +static int aux_pcm_get_sample_rate(int value) +{ + int sample_rate; + + switch (value) { + case 1: + sample_rate = SAMPLING_RATE_16KHZ; + break; + case 0: + default: + sample_rate = SAMPLING_RATE_8KHZ; + break; + } + return sample_rate; +} + +static int tdm_get_sample_rate_val(int sample_rate) +{ + int sample_rate_val = 0; + + switch (sample_rate) { + case SAMPLING_RATE_8KHZ: + sample_rate_val = 0; + break; + case SAMPLING_RATE_16KHZ: + sample_rate_val = 1; + break; + case SAMPLING_RATE_32KHZ: + sample_rate_val = 2; + break; + case SAMPLING_RATE_48KHZ: + sample_rate_val = 3; + break; + case SAMPLING_RATE_176P4KHZ: + sample_rate_val = 4; + break; + case SAMPLING_RATE_352P8KHZ: + sample_rate_val = 5; + break; + default: + sample_rate_val = 3; + break; + } + return sample_rate_val; +} + +static int aux_pcm_get_sample_rate_val(int sample_rate) +{ + int sample_rate_val; + + switch (sample_rate) { + case SAMPLING_RATE_16KHZ: + sample_rate_val = 1; + break; + case SAMPLING_RATE_8KHZ: + default: + sample_rate_val = 0; + break; + } + return sample_rate_val; +} + +static int tdm_get_port_idx(struct snd_kcontrol *kcontrol, + struct tdm_port *port) +{ + if (port) { + if (strnstr(kcontrol->id.name, "PRI", + sizeof(kcontrol->id.name))) { + port->mode = TDM_PRI; + } else if (strnstr(kcontrol->id.name, "SEC", + sizeof(kcontrol->id.name))) { + port->mode = TDM_SEC; + } else if (strnstr(kcontrol->id.name, "TERT", + sizeof(kcontrol->id.name))) { + port->mode = TDM_TERT; + } else if (strnstr(kcontrol->id.name, "QUAT", + sizeof(kcontrol->id.name))) { + port->mode = TDM_QUAT; + } else if (strnstr(kcontrol->id.name, "QUIN", + sizeof(kcontrol->id.name))) { + port->mode = TDM_QUIN; + } else { + pr_err("%s: unsupported mode in: %s", + __func__, kcontrol->id.name); + return -EINVAL; + } + + if (strnstr(kcontrol->id.name, "RX_0", + sizeof(kcontrol->id.name)) || + strnstr(kcontrol->id.name, "TX_0", + sizeof(kcontrol->id.name))) { + port->channel = TDM_0; + } else if (strnstr(kcontrol->id.name, "RX_1", + sizeof(kcontrol->id.name)) || + strnstr(kcontrol->id.name, "TX_1", + sizeof(kcontrol->id.name))) { + port->channel = TDM_1; + } else if (strnstr(kcontrol->id.name, "RX_2", + sizeof(kcontrol->id.name)) || + strnstr(kcontrol->id.name, "TX_2", + sizeof(kcontrol->id.name))) { + port->channel = TDM_2; + } else if (strnstr(kcontrol->id.name, "RX_3", + sizeof(kcontrol->id.name)) || + strnstr(kcontrol->id.name, "TX_3", + sizeof(kcontrol->id.name))) { + port->channel = TDM_3; + } else if (strnstr(kcontrol->id.name, "RX_4", + sizeof(kcontrol->id.name)) || + strnstr(kcontrol->id.name, "TX_4", + sizeof(kcontrol->id.name))) { + port->channel = TDM_4; + } else if (strnstr(kcontrol->id.name, "RX_5", + sizeof(kcontrol->id.name)) || + strnstr(kcontrol->id.name, "TX_5", + sizeof(kcontrol->id.name))) { + port->channel = TDM_5; + } else if (strnstr(kcontrol->id.name, "RX_6", + sizeof(kcontrol->id.name)) || + strnstr(kcontrol->id.name, "TX_6", + sizeof(kcontrol->id.name))) { + port->channel = TDM_6; + } else if (strnstr(kcontrol->id.name, "RX_7", + sizeof(kcontrol->id.name)) || + strnstr(kcontrol->id.name, "TX_7", + sizeof(kcontrol->id.name))) { + port->channel = TDM_7; + } else { + pr_err("%s: unsupported channel in: %s", + __func__, kcontrol->id.name); + return -EINVAL; + } + } else + return -EINVAL; + return 0; +} + +static int tdm_rx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + ucontrol->value.enumerated.item[0] = tdm_get_sample_rate_val( + tdm_rx_cfg[port.mode][port.channel].sample_rate); + + pr_debug("%s: tdm_rx_sample_rate = %d, item = %d\n", __func__, + tdm_rx_cfg[port.mode][port.channel].sample_rate, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_rx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + tdm_rx_cfg[port.mode][port.channel].sample_rate = + tdm_get_sample_rate(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: tdm_rx_sample_rate = %d, item = %d\n", __func__, + tdm_rx_cfg[port.mode][port.channel].sample_rate, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_tx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + ucontrol->value.enumerated.item[0] = tdm_get_sample_rate_val( + tdm_tx_cfg[port.mode][port.channel].sample_rate); + + pr_debug("%s: tdm_tx_sample_rate = %d, item = %d\n", __func__, + tdm_tx_cfg[port.mode][port.channel].sample_rate, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_tx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + tdm_tx_cfg[port.mode][port.channel].sample_rate = + tdm_get_sample_rate(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: tdm_tx_sample_rate = %d, item = %d\n", __func__, + tdm_tx_cfg[port.mode][port.channel].sample_rate, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_get_format(int value) +{ + int format = 0; + + switch (value) { + case 0: + format = SNDRV_PCM_FORMAT_S16_LE; + break; + case 1: + format = SNDRV_PCM_FORMAT_S24_LE; + break; + case 2: + format = SNDRV_PCM_FORMAT_S32_LE; + break; + default: + format = SNDRV_PCM_FORMAT_S16_LE; + break; + } + return format; +} + +static int tdm_get_format_val(int format) +{ + int value = 0; + + switch (format) { + case SNDRV_PCM_FORMAT_S16_LE: + value = 0; + break; + case SNDRV_PCM_FORMAT_S24_LE: + value = 1; + break; + case SNDRV_PCM_FORMAT_S32_LE: + value = 2; + break; + default: + value = 0; + break; + } + return value; +} + +static int tdm_rx_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + ucontrol->value.enumerated.item[0] = tdm_get_format_val( + tdm_rx_cfg[port.mode][port.channel].bit_format); + + pr_debug("%s: tdm_rx_bit_format = %d, item = %d\n", __func__, + tdm_rx_cfg[port.mode][port.channel].bit_format, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_rx_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + tdm_rx_cfg[port.mode][port.channel].bit_format = + tdm_get_format(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: tdm_rx_bit_format = %d, item = %d\n", __func__, + tdm_rx_cfg[port.mode][port.channel].bit_format, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_tx_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + ucontrol->value.enumerated.item[0] = tdm_get_format_val( + tdm_tx_cfg[port.mode][port.channel].bit_format); + + pr_debug("%s: tdm_tx_bit_format = %d, item = %d\n", __func__, + tdm_tx_cfg[port.mode][port.channel].bit_format, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_tx_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + tdm_tx_cfg[port.mode][port.channel].bit_format = + tdm_get_format(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: tdm_tx_bit_format = %d, item = %d\n", __func__, + tdm_tx_cfg[port.mode][port.channel].bit_format, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_rx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + + ucontrol->value.enumerated.item[0] = + tdm_rx_cfg[port.mode][port.channel].channels - 1; + + pr_debug("%s: tdm_rx_ch = %d, item = %d\n", __func__, + tdm_rx_cfg[port.mode][port.channel].channels - 1, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_rx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + tdm_rx_cfg[port.mode][port.channel].channels = + ucontrol->value.enumerated.item[0] + 1; + + pr_debug("%s: tdm_rx_ch = %d, item = %d\n", __func__, + tdm_rx_cfg[port.mode][port.channel].channels, + ucontrol->value.enumerated.item[0] + 1); + } + return ret; +} + +static int tdm_tx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + ucontrol->value.enumerated.item[0] = + tdm_tx_cfg[port.mode][port.channel].channels - 1; + + pr_debug("%s: tdm_tx_ch = %d, item = %d\n", __func__, + tdm_tx_cfg[port.mode][port.channel].channels - 1, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_tx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + tdm_tx_cfg[port.mode][port.channel].channels = + ucontrol->value.enumerated.item[0] + 1; + + pr_debug("%s: tdm_tx_ch = %d, item = %d\n", __func__, + tdm_tx_cfg[port.mode][port.channel].channels, + ucontrol->value.enumerated.item[0] + 1); + } + return ret; +} + +static int tdm_slot_map_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int interface = ucontrol->value.integer.value[0]; + int channel = ucontrol->value.integer.value[1]; + unsigned int offset_val; + unsigned int *slot_offset; + + if (interface < 0 || interface >= (TDM_INTERFACE_MAX * 2)) { + pr_err("%s: incorrect interface = %d", __func__, interface); + return -EINVAL; + } + if (channel < 0 || channel >= TDM_PORT_MAX) { + pr_err("%s: incorrect channel = %d", __func__, channel); + return -EINVAL; + } + + pr_debug("%s: interface = %d, channel = %d", __func__, + interface, channel); + + slot_offset = tdm_cfg[interface][channel].tdm_slot_offset; + + /* Currenly can configure only two slots */ + offset_val = ucontrol->value.integer.value[2]; + /* Offset value can only be 0, 4, 8, ..28 */ + if (offset_val % 4 == 0 && offset_val <= 28) + slot_offset[0] = offset_val; + pr_debug("%s: slot offset[0] = %d\n", __func__, slot_offset[0]); + + offset_val = ucontrol->value.integer.value[3]; + if (offset_val % 4 == 0 && offset_val <= 28) + slot_offset[1] = offset_val; + pr_debug("%s: slot offset[1] = %d\n", __func__, slot_offset[1]); + + return 0; +} + +static int aux_pcm_get_port_idx(struct snd_kcontrol *kcontrol) +{ + int idx; + + if (strnstr(kcontrol->id.name, "PRIM_AUX_PCM", + sizeof("PRIM_AUX_PCM"))) + idx = PRIM_AUX_PCM; + else if (strnstr(kcontrol->id.name, "SEC_AUX_PCM", + sizeof("SEC_AUX_PCM"))) + idx = SEC_AUX_PCM; + else if (strnstr(kcontrol->id.name, "TERT_AUX_PCM", + sizeof("TERT_AUX_PCM"))) + idx = TERT_AUX_PCM; + else if (strnstr(kcontrol->id.name, "QUAT_AUX_PCM", + sizeof("QUAT_AUX_PCM"))) + idx = QUAT_AUX_PCM; + else if (strnstr(kcontrol->id.name, "QUIN_AUX_PCM", + sizeof("QUIN_AUX_PCM"))) + idx = QUIN_AUX_PCM; + else { + pr_err("%s: unsupported port: %s", + __func__, kcontrol->id.name); + idx = -EINVAL; + } + + return idx; +} + +static int aux_pcm_rx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = aux_pcm_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + aux_pcm_rx_cfg[idx].sample_rate = + aux_pcm_get_sample_rate(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: idx[%d]_rx_sample_rate = %d, item = %d\n", __func__, + idx, aux_pcm_rx_cfg[idx].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int aux_pcm_rx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = aux_pcm_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + ucontrol->value.enumerated.item[0] = + aux_pcm_get_sample_rate_val(aux_pcm_rx_cfg[idx].sample_rate); + + pr_debug("%s: idx[%d]_rx_sample_rate = %d, item = %d\n", __func__, + idx, aux_pcm_rx_cfg[idx].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int aux_pcm_tx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = aux_pcm_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + aux_pcm_tx_cfg[idx].sample_rate = + aux_pcm_get_sample_rate(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: idx[%d]_tx_sample_rate = %d, item = %d\n", __func__, + idx, aux_pcm_tx_cfg[idx].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int aux_pcm_tx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = aux_pcm_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + ucontrol->value.enumerated.item[0] = + aux_pcm_get_sample_rate_val(aux_pcm_tx_cfg[idx].sample_rate); + + pr_debug("%s: idx[%d]_tx_sample_rate = %d, item = %d\n", __func__, + idx, aux_pcm_tx_cfg[idx].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int mi2s_get_port_idx(struct snd_kcontrol *kcontrol) +{ + int idx; + + if (strnstr(kcontrol->id.name, "PRIM_MI2S_RX", + sizeof("PRIM_MI2S_RX"))) + idx = PRIM_MI2S; + else if (strnstr(kcontrol->id.name, "SEC_MI2S_RX", + sizeof("SEC_MI2S_RX"))) + idx = SEC_MI2S; + else if (strnstr(kcontrol->id.name, "TERT_MI2S_RX", + sizeof("TERT_MI2S_RX"))) + idx = TERT_MI2S; + else if (strnstr(kcontrol->id.name, "QUAT_MI2S_RX", + sizeof("QUAT_MI2S_RX"))) + idx = QUAT_MI2S; + else if (strnstr(kcontrol->id.name, "QUIN_MI2S_RX", + sizeof("QUIN_MI2S_RX"))) + idx = QUIN_MI2S; + else if (strnstr(kcontrol->id.name, "PRIM_MI2S_TX", + sizeof("PRIM_MI2S_TX"))) + idx = PRIM_MI2S; + else if (strnstr(kcontrol->id.name, "SEC_MI2S_TX", + sizeof("SEC_MI2S_TX"))) + idx = SEC_MI2S; + else if (strnstr(kcontrol->id.name, "TERT_MI2S_TX", + sizeof("TERT_MI2S_TX"))) + idx = TERT_MI2S; + else if (strnstr(kcontrol->id.name, "QUAT_MI2S_TX", + sizeof("QUAT_MI2S_TX"))) + idx = QUAT_MI2S; + else if (strnstr(kcontrol->id.name, "QUIN_MI2S_TX", + sizeof("QUIN_MI2S_TX"))) + idx = QUIN_MI2S; + else { + pr_err("%s: unsupported channel: %s", + __func__, kcontrol->id.name); + idx = -EINVAL; + } + + return idx; +} + +static int mi2s_get_sample_rate_val(int sample_rate) +{ + int sample_rate_val; + + switch (sample_rate) { + case SAMPLING_RATE_8KHZ: + sample_rate_val = 0; + break; + case SAMPLING_RATE_11P025KHZ: + sample_rate_val = 1; + break; + case SAMPLING_RATE_16KHZ: + sample_rate_val = 2; + break; + case SAMPLING_RATE_22P05KHZ: + sample_rate_val = 3; + break; + case SAMPLING_RATE_32KHZ: + sample_rate_val = 4; + break; + case SAMPLING_RATE_44P1KHZ: + sample_rate_val = 5; + break; + case SAMPLING_RATE_48KHZ: + sample_rate_val = 6; + break; + case SAMPLING_RATE_96KHZ: + sample_rate_val = 7; + break; + case SAMPLING_RATE_192KHZ: + sample_rate_val = 8; + break; + default: + sample_rate_val = 6; + break; + } + return sample_rate_val; +} + +static int mi2s_get_sample_rate(int value) +{ + int sample_rate; + + switch (value) { + case 0: + sample_rate = SAMPLING_RATE_8KHZ; + break; + case 1: + sample_rate = SAMPLING_RATE_11P025KHZ; + break; + case 2: + sample_rate = SAMPLING_RATE_16KHZ; + break; + case 3: + sample_rate = SAMPLING_RATE_22P05KHZ; + break; + case 4: + sample_rate = SAMPLING_RATE_32KHZ; + break; + case 5: + sample_rate = SAMPLING_RATE_44P1KHZ; + break; + case 6: + sample_rate = SAMPLING_RATE_48KHZ; + break; + case 7: + sample_rate = SAMPLING_RATE_96KHZ; + break; + case 8: + sample_rate = SAMPLING_RATE_192KHZ; + break; + default: + sample_rate = SAMPLING_RATE_48KHZ; + break; + } + return sample_rate; +} + +static int mi2s_auxpcm_get_format(int value) +{ + int format; + + switch (value) { + case 0: + format = SNDRV_PCM_FORMAT_S16_LE; + break; + case 1: + format = SNDRV_PCM_FORMAT_S24_LE; + break; + case 2: + format = SNDRV_PCM_FORMAT_S24_3LE; + break; + case 3: + format = SNDRV_PCM_FORMAT_S32_LE; + break; + default: + format = SNDRV_PCM_FORMAT_S16_LE; + break; + } + return format; +} + +static int mi2s_auxpcm_get_format_value(int format) +{ + int value; + + switch (format) { + case SNDRV_PCM_FORMAT_S16_LE: + value = 0; + break; + case SNDRV_PCM_FORMAT_S24_LE: + value = 1; + break; + case SNDRV_PCM_FORMAT_S24_3LE: + value = 2; + break; + case SNDRV_PCM_FORMAT_S32_LE: + value = 3; + break; + default: + value = 0; + break; + } + return value; +} + +static int mi2s_rx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + mi2s_rx_cfg[idx].sample_rate = + mi2s_get_sample_rate(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: idx[%d]_rx_sample_rate = %d, item = %d\n", __func__, + idx, mi2s_rx_cfg[idx].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int mi2s_rx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + ucontrol->value.enumerated.item[0] = + mi2s_get_sample_rate_val(mi2s_rx_cfg[idx].sample_rate); + + pr_debug("%s: idx[%d]_rx_sample_rate = %d, item = %d\n", __func__, + idx, mi2s_rx_cfg[idx].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int mi2s_tx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + mi2s_tx_cfg[idx].sample_rate = + mi2s_get_sample_rate(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: idx[%d]_tx_sample_rate = %d, item = %d\n", __func__, + idx, mi2s_tx_cfg[idx].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int mi2s_tx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + ucontrol->value.enumerated.item[0] = + mi2s_get_sample_rate_val(mi2s_tx_cfg[idx].sample_rate); + + pr_debug("%s: idx[%d]_tx_sample_rate = %d, item = %d\n", __func__, + idx, mi2s_tx_cfg[idx].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int msm_mi2s_rx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + pr_debug("%s: msm_mi2s_[%d]_rx_ch = %d\n", __func__, + idx, mi2s_rx_cfg[idx].channels); + ucontrol->value.enumerated.item[0] = mi2s_rx_cfg[idx].channels - 1; + + return 0; +} + +static int msm_mi2s_rx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + mi2s_rx_cfg[idx].channels = ucontrol->value.enumerated.item[0] + 1; + pr_debug("%s: msm_mi2s_[%d]_rx_ch = %d\n", __func__, + idx, mi2s_rx_cfg[idx].channels); + + return 1; +} + +static int msm_mi2s_tx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + pr_debug("%s: msm_mi2s_[%d]_tx_ch = %d\n", __func__, + idx, mi2s_tx_cfg[idx].channels); + ucontrol->value.enumerated.item[0] = mi2s_tx_cfg[idx].channels - 1; + + return 0; +} + +static int msm_mi2s_tx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + mi2s_tx_cfg[idx].channels = ucontrol->value.enumerated.item[0] + 1; + pr_debug("%s: msm_mi2s_[%d]_tx_ch = %d\n", __func__, + idx, mi2s_tx_cfg[idx].channels); + + return 1; +} + +static int msm_mi2s_rx_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + ucontrol->value.enumerated.item[0] = + mi2s_auxpcm_get_format_value(mi2s_rx_cfg[idx].bit_format); + + pr_debug("%s: idx[%d]_rx_format = %d, item = %d\n", __func__, + idx, mi2s_rx_cfg[idx].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int msm_mi2s_rx_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + mi2s_rx_cfg[idx].bit_format = + mi2s_auxpcm_get_format(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: idx[%d]_rx_format = %d, item = %d\n", __func__, + idx, mi2s_rx_cfg[idx].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int msm_mi2s_tx_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + ucontrol->value.enumerated.item[0] = + mi2s_auxpcm_get_format_value(mi2s_tx_cfg[idx].bit_format); + + pr_debug("%s: idx[%d]_tx_format = %d, item = %d\n", __func__, + idx, mi2s_tx_cfg[idx].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int msm_mi2s_tx_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = mi2s_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + mi2s_tx_cfg[idx].bit_format = + mi2s_auxpcm_get_format(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: idx[%d]_tx_format = %d, item = %d\n", __func__, + idx, mi2s_tx_cfg[idx].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int msm_aux_pcm_rx_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = aux_pcm_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + ucontrol->value.enumerated.item[0] = + mi2s_auxpcm_get_format_value(aux_pcm_rx_cfg[idx].bit_format); + + pr_debug("%s: idx[%d]_rx_format = %d, item = %d\n", __func__, + idx, aux_pcm_rx_cfg[idx].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int msm_aux_pcm_rx_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = aux_pcm_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + aux_pcm_rx_cfg[idx].bit_format = + mi2s_auxpcm_get_format(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: idx[%d]_rx_format = %d, item = %d\n", __func__, + idx, aux_pcm_rx_cfg[idx].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int msm_aux_pcm_tx_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = aux_pcm_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + ucontrol->value.enumerated.item[0] = + mi2s_auxpcm_get_format_value(aux_pcm_tx_cfg[idx].bit_format); + + pr_debug("%s: idx[%d]_tx_format = %d, item = %d\n", __func__, + idx, aux_pcm_tx_cfg[idx].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int msm_aux_pcm_tx_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int idx = aux_pcm_get_port_idx(kcontrol); + + if (idx < 0) + return idx; + + aux_pcm_tx_cfg[idx].bit_format = + mi2s_auxpcm_get_format(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: idx[%d]_tx_format = %d, item = %d\n", __func__, + idx, aux_pcm_tx_cfg[idx].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int msm_hifi_ctrl(struct snd_soc_codec *codec) +{ + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + struct snd_soc_card *card = codec->component.card; + struct msm_asoc_mach_data *pdata = + snd_soc_card_get_drvdata(card); + + dev_dbg(codec->dev, "%s: msm_hifi_control = %d\n", __func__, + msm_hifi_control); + + if (!pdata || !pdata->hph_en1_gpio_p) { + dev_err(codec->dev, "%s: hph_en1_gpio is invalid\n", __func__); + return -EINVAL; + } + if (msm_hifi_control == MSM_HIFI_ON) { + msm_cdc_pinctrl_select_active_state(pdata->hph_en1_gpio_p); + /* 5msec delay needed as per HW requirement */ + usleep_range(5000, 5010); + } else { + msm_cdc_pinctrl_select_sleep_state(pdata->hph_en1_gpio_p); + } + snd_soc_dapm_sync(dapm); + + return 0; +} + +static int msm_hifi_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s: msm_hifi_control = %d\n", + __func__, msm_hifi_control); + ucontrol->value.integer.value[0] = msm_hifi_control; + + return 0; +} + +static int msm_hifi_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + + dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n", + __func__, ucontrol->value.integer.value[0]); + + msm_hifi_control = ucontrol->value.integer.value[0]; + msm_hifi_ctrl(codec); + + return 0; +} + +static const struct snd_kcontrol_new msm_snd_controls[] = { + SOC_ENUM_EXT("SLIM_0_RX Channels", slim_0_rx_chs, + slim_rx_ch_get, slim_rx_ch_put), + SOC_ENUM_EXT("SLIM_2_RX Channels", slim_2_rx_chs, + slim_rx_ch_get, slim_rx_ch_put), + SOC_ENUM_EXT("SLIM_0_TX Channels", slim_0_tx_chs, + slim_tx_ch_get, slim_tx_ch_put), + SOC_ENUM_EXT("SLIM_1_TX Channels", slim_1_tx_chs, + slim_tx_ch_get, slim_tx_ch_put), + SOC_ENUM_EXT("SLIM_5_RX Channels", slim_5_rx_chs, + slim_rx_ch_get, slim_rx_ch_put), + SOC_ENUM_EXT("SLIM_6_RX Channels", slim_6_rx_chs, + slim_rx_ch_get, slim_rx_ch_put), + SOC_ENUM_EXT("VI_FEED_TX Channels", vi_feed_tx_chs, + msm_vi_feed_tx_ch_get, msm_vi_feed_tx_ch_put), + SOC_ENUM_EXT("USB_AUDIO_RX Channels", usb_rx_chs, + usb_audio_rx_ch_get, usb_audio_rx_ch_put), + SOC_ENUM_EXT("USB_AUDIO_TX Channels", usb_tx_chs, + usb_audio_tx_ch_get, usb_audio_tx_ch_put), + SOC_ENUM_EXT("Display Port RX Channels", ext_disp_rx_chs, + ext_disp_rx_ch_get, ext_disp_rx_ch_put), + SOC_ENUM_EXT("PROXY_RX Channels", proxy_rx_chs, + proxy_rx_ch_get, proxy_rx_ch_put), + SOC_ENUM_EXT("SLIM_0_RX Format", slim_0_rx_format, + slim_rx_bit_format_get, slim_rx_bit_format_put), + SOC_ENUM_EXT("SLIM_5_RX Format", slim_5_rx_format, + slim_rx_bit_format_get, slim_rx_bit_format_put), + SOC_ENUM_EXT("SLIM_6_RX Format", slim_6_rx_format, + slim_rx_bit_format_get, slim_rx_bit_format_put), + SOC_ENUM_EXT("SLIM_0_TX Format", slim_0_tx_format, + slim_tx_bit_format_get, slim_tx_bit_format_put), + SOC_ENUM_EXT("USB_AUDIO_RX Format", usb_rx_format, + usb_audio_rx_format_get, usb_audio_rx_format_put), + SOC_ENUM_EXT("USB_AUDIO_TX Format", usb_tx_format, + usb_audio_tx_format_get, usb_audio_tx_format_put), + SOC_ENUM_EXT("Display Port RX Bit Format", ext_disp_rx_format, + ext_disp_rx_format_get, ext_disp_rx_format_put), + SOC_ENUM_EXT("SLIM_0_RX SampleRate", slim_0_rx_sample_rate, + slim_rx_sample_rate_get, slim_rx_sample_rate_put), + SOC_ENUM_EXT("SLIM_2_RX SampleRate", slim_2_rx_sample_rate, + slim_rx_sample_rate_get, slim_rx_sample_rate_put), + SOC_ENUM_EXT("SLIM_0_TX SampleRate", slim_0_tx_sample_rate, + slim_tx_sample_rate_get, slim_tx_sample_rate_put), + SOC_ENUM_EXT("SLIM_5_RX SampleRate", slim_5_rx_sample_rate, + slim_rx_sample_rate_get, slim_rx_sample_rate_put), + SOC_ENUM_EXT("SLIM_6_RX SampleRate", slim_6_rx_sample_rate, + slim_rx_sample_rate_get, slim_rx_sample_rate_put), + SOC_ENUM_EXT("BT SampleRate", bt_sample_rate, + msm_bt_sample_rate_get, + msm_bt_sample_rate_put), + SOC_ENUM_EXT("BT SampleRate RX", bt_sample_rate_rx, + msm_bt_sample_rate_rx_get, + msm_bt_sample_rate_rx_put), + SOC_ENUM_EXT("BT SampleRate TX", bt_sample_rate_tx, + msm_bt_sample_rate_tx_get, + msm_bt_sample_rate_tx_put), + SOC_ENUM_EXT("USB_AUDIO_RX SampleRate", usb_rx_sample_rate, + usb_audio_rx_sample_rate_get, + usb_audio_rx_sample_rate_put), + SOC_ENUM_EXT("USB_AUDIO_TX SampleRate", usb_tx_sample_rate, + usb_audio_tx_sample_rate_get, + usb_audio_tx_sample_rate_put), + SOC_ENUM_EXT("Display Port RX SampleRate", ext_disp_rx_sample_rate, + ext_disp_rx_sample_rate_get, + ext_disp_rx_sample_rate_put), + SOC_ENUM_EXT("PRI_TDM_RX_0 SampleRate", tdm_rx_sample_rate, + tdm_rx_sample_rate_get, + tdm_rx_sample_rate_put), + SOC_ENUM_EXT("PRI_TDM_TX_0 SampleRate", tdm_tx_sample_rate, + tdm_tx_sample_rate_get, + tdm_tx_sample_rate_put), + SOC_ENUM_EXT("PRI_TDM_RX_0 Format", tdm_rx_format, + tdm_rx_format_get, + tdm_rx_format_put), + SOC_ENUM_EXT("PRI_TDM_TX_0 Format", tdm_tx_format, + tdm_tx_format_get, + tdm_tx_format_put), + SOC_ENUM_EXT("PRI_TDM_RX_0 Channels", tdm_rx_chs, + tdm_rx_ch_get, + tdm_rx_ch_put), + SOC_ENUM_EXT("PRI_TDM_TX_0 Channels", tdm_tx_chs, + tdm_tx_ch_get, + tdm_tx_ch_put), + SOC_ENUM_EXT("SEC_TDM_RX_0 SampleRate", tdm_rx_sample_rate, + tdm_rx_sample_rate_get, + tdm_rx_sample_rate_put), + SOC_ENUM_EXT("SEC_TDM_TX_0 SampleRate", tdm_tx_sample_rate, + tdm_tx_sample_rate_get, + tdm_tx_sample_rate_put), + SOC_ENUM_EXT("SEC_TDM_RX_0 Format", tdm_rx_format, + tdm_rx_format_get, + tdm_rx_format_put), + SOC_ENUM_EXT("SEC_TDM_TX_0 Format", tdm_tx_format, + tdm_tx_format_get, + tdm_tx_format_put), + SOC_ENUM_EXT("SEC_TDM_RX_0 Channels", tdm_rx_chs, + tdm_rx_ch_get, + tdm_rx_ch_put), + SOC_ENUM_EXT("SEC_TDM_TX_0 Channels", tdm_tx_chs, + tdm_tx_ch_get, + tdm_tx_ch_put), + SOC_ENUM_EXT("TERT_TDM_RX_0 SampleRate", tdm_rx_sample_rate, + tdm_rx_sample_rate_get, + tdm_rx_sample_rate_put), + SOC_ENUM_EXT("TERT_TDM_TX_0 SampleRate", tdm_tx_sample_rate, + tdm_tx_sample_rate_get, + tdm_tx_sample_rate_put), + SOC_ENUM_EXT("TERT_TDM_RX_0 Format", tdm_rx_format, + tdm_rx_format_get, + tdm_rx_format_put), + SOC_ENUM_EXT("TERT_TDM_TX_0 Format", tdm_tx_format, + tdm_tx_format_get, + tdm_tx_format_put), + SOC_ENUM_EXT("TERT_TDM_RX_0 Channels", tdm_rx_chs, + tdm_rx_ch_get, + tdm_rx_ch_put), + SOC_ENUM_EXT("TERT_TDM_TX_0 Channels", tdm_tx_chs, + tdm_tx_ch_get, + tdm_tx_ch_put), + SOC_ENUM_EXT("QUAT_TDM_RX_0 SampleRate", tdm_rx_sample_rate, + tdm_rx_sample_rate_get, + tdm_rx_sample_rate_put), + SOC_ENUM_EXT("QUAT_TDM_RX_1 SampleRate", tdm_rx_sample_rate, + tdm_rx_sample_rate_get, + tdm_rx_sample_rate_put), + SOC_ENUM_EXT("QUAT_TDM_TX_0 SampleRate", tdm_tx_sample_rate, + tdm_tx_sample_rate_get, + tdm_tx_sample_rate_put), + SOC_ENUM_EXT("QUAT_TDM_RX_0 Format", tdm_rx_format, + tdm_rx_format_get, + tdm_rx_format_put), + SOC_ENUM_EXT("QUAT_TDM_RX_1 Format", tdm_rx_format, + tdm_rx_format_get, + tdm_rx_format_put), + SOC_ENUM_EXT("QUAT_TDM_TX_0 Format", tdm_tx_format, + tdm_tx_format_get, + tdm_tx_format_put), + SOC_ENUM_EXT("QUAT_TDM_RX_0 Channels", tdm_rx_chs, + tdm_rx_ch_get, + tdm_rx_ch_put), + SOC_ENUM_EXT("QUAT_TDM_RX_1 Channels", tdm_rx_chs, + tdm_rx_ch_get, + tdm_rx_ch_put), + SOC_ENUM_EXT("QUAT_TDM_TX_0 Channels", tdm_tx_chs, + tdm_tx_ch_get, + tdm_tx_ch_put), + SOC_ENUM_EXT("QUIN_TDM_RX_0 SampleRate", tdm_rx_sample_rate, + tdm_rx_sample_rate_get, + tdm_rx_sample_rate_put), + SOC_ENUM_EXT("QUIN_TDM_TX_0 SampleRate", tdm_tx_sample_rate, + tdm_tx_sample_rate_get, + tdm_tx_sample_rate_put), + SOC_ENUM_EXT("QUIN_TDM_RX_0 Format", tdm_rx_format, + tdm_rx_format_get, + tdm_rx_format_put), + SOC_ENUM_EXT("QUIN_TDM_TX_0 Format", tdm_tx_format, + tdm_tx_format_get, + tdm_tx_format_put), + SOC_ENUM_EXT("QUIN_TDM_RX_0 Channels", tdm_rx_chs, + tdm_rx_ch_get, + tdm_rx_ch_put), + SOC_ENUM_EXT("QUIN_TDM_TX_0 Channels", tdm_tx_chs, + tdm_tx_ch_get, + tdm_tx_ch_put), + SOC_ENUM_EXT("PRIM_AUX_PCM_RX SampleRate", prim_aux_pcm_rx_sample_rate, + aux_pcm_rx_sample_rate_get, + aux_pcm_rx_sample_rate_put), + SOC_ENUM_EXT("SEC_AUX_PCM_RX SampleRate", sec_aux_pcm_rx_sample_rate, + aux_pcm_rx_sample_rate_get, + aux_pcm_rx_sample_rate_put), + SOC_ENUM_EXT("TERT_AUX_PCM_RX SampleRate", tert_aux_pcm_rx_sample_rate, + aux_pcm_rx_sample_rate_get, + aux_pcm_rx_sample_rate_put), + SOC_ENUM_EXT("QUAT_AUX_PCM_RX SampleRate", quat_aux_pcm_rx_sample_rate, + aux_pcm_rx_sample_rate_get, + aux_pcm_rx_sample_rate_put), + SOC_ENUM_EXT("QUIN_AUX_PCM_RX SampleRate", quin_aux_pcm_rx_sample_rate, + aux_pcm_rx_sample_rate_get, + aux_pcm_rx_sample_rate_put), + SOC_ENUM_EXT("PRIM_AUX_PCM_TX SampleRate", prim_aux_pcm_tx_sample_rate, + aux_pcm_tx_sample_rate_get, + aux_pcm_tx_sample_rate_put), + SOC_ENUM_EXT("SEC_AUX_PCM_TX SampleRate", sec_aux_pcm_tx_sample_rate, + aux_pcm_tx_sample_rate_get, + aux_pcm_tx_sample_rate_put), + SOC_ENUM_EXT("TERT_AUX_PCM_TX SampleRate", tert_aux_pcm_tx_sample_rate, + aux_pcm_tx_sample_rate_get, + aux_pcm_tx_sample_rate_put), + SOC_ENUM_EXT("QUAT_AUX_PCM_TX SampleRate", quat_aux_pcm_tx_sample_rate, + aux_pcm_tx_sample_rate_get, + aux_pcm_tx_sample_rate_put), + SOC_ENUM_EXT("QUIN_AUX_PCM_TX SampleRate", quin_aux_pcm_tx_sample_rate, + aux_pcm_tx_sample_rate_get, + aux_pcm_tx_sample_rate_put), + SOC_ENUM_EXT("PRIM_MI2S_RX SampleRate", prim_mi2s_rx_sample_rate, + mi2s_rx_sample_rate_get, + mi2s_rx_sample_rate_put), + SOC_ENUM_EXT("SEC_MI2S_RX SampleRate", sec_mi2s_rx_sample_rate, + mi2s_rx_sample_rate_get, + mi2s_rx_sample_rate_put), + SOC_ENUM_EXT("TERT_MI2S_RX SampleRate", tert_mi2s_rx_sample_rate, + mi2s_rx_sample_rate_get, + mi2s_rx_sample_rate_put), + SOC_ENUM_EXT("QUAT_MI2S_RX SampleRate", quat_mi2s_rx_sample_rate, + mi2s_rx_sample_rate_get, + mi2s_rx_sample_rate_put), + SOC_ENUM_EXT("QUIN_MI2S_RX SampleRate", quin_mi2s_rx_sample_rate, + mi2s_rx_sample_rate_get, + mi2s_rx_sample_rate_put), + SOC_ENUM_EXT("PRIM_MI2S_TX SampleRate", prim_mi2s_tx_sample_rate, + mi2s_tx_sample_rate_get, + mi2s_tx_sample_rate_put), + SOC_ENUM_EXT("SEC_MI2S_TX SampleRate", sec_mi2s_tx_sample_rate, + mi2s_tx_sample_rate_get, + mi2s_tx_sample_rate_put), + SOC_ENUM_EXT("TERT_MI2S_TX SampleRate", tert_mi2s_tx_sample_rate, + mi2s_tx_sample_rate_get, + mi2s_tx_sample_rate_put), + SOC_ENUM_EXT("QUAT_MI2S_TX SampleRate", quat_mi2s_tx_sample_rate, + mi2s_tx_sample_rate_get, + mi2s_tx_sample_rate_put), + SOC_ENUM_EXT("QUIN_MI2S_TX SampleRate", quin_mi2s_tx_sample_rate, + mi2s_tx_sample_rate_get, + mi2s_tx_sample_rate_put), + SOC_ENUM_EXT("PRIM_MI2S_RX Channels", prim_mi2s_rx_chs, + msm_mi2s_rx_ch_get, msm_mi2s_rx_ch_put), + SOC_ENUM_EXT("PRIM_MI2S_TX Channels", prim_mi2s_tx_chs, + msm_mi2s_tx_ch_get, msm_mi2s_tx_ch_put), + SOC_ENUM_EXT("SEC_MI2S_RX Channels", sec_mi2s_rx_chs, + msm_mi2s_rx_ch_get, msm_mi2s_rx_ch_put), + SOC_ENUM_EXT("SEC_MI2S_TX Channels", sec_mi2s_tx_chs, + msm_mi2s_tx_ch_get, msm_mi2s_tx_ch_put), + SOC_ENUM_EXT("TERT_MI2S_RX Channels", tert_mi2s_rx_chs, + msm_mi2s_rx_ch_get, msm_mi2s_rx_ch_put), + SOC_ENUM_EXT("TERT_MI2S_TX Channels", tert_mi2s_tx_chs, + msm_mi2s_tx_ch_get, msm_mi2s_tx_ch_put), + SOC_ENUM_EXT("QUAT_MI2S_RX Channels", quat_mi2s_rx_chs, + msm_mi2s_rx_ch_get, msm_mi2s_rx_ch_put), + SOC_ENUM_EXT("QUAT_MI2S_TX Channels", quat_mi2s_tx_chs, + msm_mi2s_tx_ch_get, msm_mi2s_tx_ch_put), + SOC_ENUM_EXT("QUIN_MI2S_RX Channels", quin_mi2s_rx_chs, + msm_mi2s_rx_ch_get, msm_mi2s_rx_ch_put), + SOC_ENUM_EXT("QUIN_MI2S_TX Channels", quin_mi2s_tx_chs, + msm_mi2s_tx_ch_get, msm_mi2s_tx_ch_put), + SOC_ENUM_EXT("PRIM_MI2S_RX Format", mi2s_rx_format, + msm_mi2s_rx_format_get, msm_mi2s_rx_format_put), + SOC_ENUM_EXT("PRIM_MI2S_TX Format", mi2s_tx_format, + msm_mi2s_tx_format_get, msm_mi2s_tx_format_put), + SOC_ENUM_EXT("SEC_MI2S_RX Format", mi2s_rx_format, + msm_mi2s_rx_format_get, msm_mi2s_rx_format_put), + SOC_ENUM_EXT("SEC_MI2S_TX Format", mi2s_tx_format, + msm_mi2s_tx_format_get, msm_mi2s_tx_format_put), + SOC_ENUM_EXT("TERT_MI2S_RX Format", mi2s_rx_format, + msm_mi2s_rx_format_get, msm_mi2s_rx_format_put), + SOC_ENUM_EXT("TERT_MI2S_TX Format", mi2s_tx_format, + msm_mi2s_tx_format_get, msm_mi2s_tx_format_put), + SOC_ENUM_EXT("QUAT_MI2S_RX Format", mi2s_rx_format, + msm_mi2s_rx_format_get, msm_mi2s_rx_format_put), + SOC_ENUM_EXT("QUAT_MI2S_TX Format", mi2s_tx_format, + msm_mi2s_tx_format_get, msm_mi2s_tx_format_put), + SOC_ENUM_EXT("QUIN_MI2S_RX Format", mi2s_rx_format, + msm_mi2s_rx_format_get, msm_mi2s_rx_format_put), + SOC_ENUM_EXT("QUIN_MI2S_TX Format", mi2s_tx_format, + msm_mi2s_tx_format_get, msm_mi2s_tx_format_put), + SOC_ENUM_EXT("PRIM_AUX_PCM_RX Format", aux_pcm_rx_format, + msm_aux_pcm_rx_format_get, msm_aux_pcm_rx_format_put), + SOC_ENUM_EXT("PRIM_AUX_PCM_TX Format", aux_pcm_tx_format, + msm_aux_pcm_tx_format_get, msm_aux_pcm_tx_format_put), + SOC_ENUM_EXT("SEC_AUX_PCM_RX Format", aux_pcm_rx_format, + msm_aux_pcm_rx_format_get, msm_aux_pcm_rx_format_put), + SOC_ENUM_EXT("SEC_AUX_PCM_TX Format", aux_pcm_tx_format, + msm_aux_pcm_tx_format_get, msm_aux_pcm_tx_format_put), + SOC_ENUM_EXT("TERT_AUX_PCM_RX Format", aux_pcm_rx_format, + msm_aux_pcm_rx_format_get, msm_aux_pcm_rx_format_put), + SOC_ENUM_EXT("TERT_AUX_PCM_TX Format", aux_pcm_tx_format, + msm_aux_pcm_tx_format_get, msm_aux_pcm_tx_format_put), + SOC_ENUM_EXT("QUAT_AUX_PCM_RX Format", aux_pcm_rx_format, + msm_aux_pcm_rx_format_get, msm_aux_pcm_rx_format_put), + SOC_ENUM_EXT("QUAT_AUX_PCM_TX Format", aux_pcm_tx_format, + msm_aux_pcm_tx_format_get, msm_aux_pcm_tx_format_put), + SOC_ENUM_EXT("QUIN_AUX_PCM_RX Format", aux_pcm_rx_format, + msm_aux_pcm_rx_format_get, msm_aux_pcm_rx_format_put), + SOC_ENUM_EXT("QUIN_AUX_PCM_TX Format", aux_pcm_tx_format, + msm_aux_pcm_tx_format_get, msm_aux_pcm_tx_format_put), + SOC_ENUM_EXT("HiFi Function", hifi_function, msm_hifi_get, + msm_hifi_put), + SOC_SINGLE_MULTI_EXT("TDM Slot Map", SND_SOC_NOPM, 0, 255, 0, 4, + NULL, tdm_slot_map_put), + +}; + +static int msm_snd_enable_codec_ext_clk(struct snd_soc_codec *codec, + int enable, bool dapm) +{ + int ret = 0; + + if (!strcmp(dev_name(codec->dev), "pahu_codec")) { + ret = pahu_cdc_mclk_enable(codec, enable); + } else if (!strcmp(dev_name(codec->dev), "tavil_codec")) { + ret = tavil_cdc_mclk_enable(codec, enable); + } else { + dev_err(codec->dev, "%s: unknown codec to enable ext clk\n", + __func__); + ret = -EINVAL; + } + return ret; +} + +static int msm_snd_enable_codec_ext_tx_clk(struct snd_soc_codec *codec, + int enable, bool dapm) +{ + int ret = 0; + + if (!strcmp(dev_name(codec->dev), "pahu_codec")) { + ret = pahu_cdc_mclk_tx_enable(codec, enable); + } else if (!strcmp(dev_name(codec->dev), "tavil_codec")) { + ret = tavil_cdc_mclk_tx_enable(codec, enable); + } else { + dev_err(codec->dev, "%s: unknown codec to enable TX ext clk\n", + __func__); + ret = -EINVAL; + } + + return ret; +} + +static int msm_mclk_tx_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + + pr_debug("%s: event = %d\n", __func__, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + return msm_snd_enable_codec_ext_tx_clk(codec, 1, true); + case SND_SOC_DAPM_POST_PMD: + return msm_snd_enable_codec_ext_tx_clk(codec, 0, true); + } + return 0; +} + +static int msm_mclk_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + + pr_debug("%s: event = %d\n", __func__, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + return msm_snd_enable_codec_ext_clk(codec, 1, true); + case SND_SOC_DAPM_POST_PMD: + return msm_snd_enable_codec_ext_clk(codec, 0, true); + } + return 0; +} + +static int msm_hifi_ctrl_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct snd_soc_card *card = codec->component.card; + struct msm_asoc_mach_data *pdata = + snd_soc_card_get_drvdata(card); + + dev_dbg(codec->dev, "%s: msm_hifi_control = %d\n", + __func__, msm_hifi_control); + + if (!pdata || !pdata->hph_en0_gpio_p) { + dev_err(codec->dev, "%s: hph_en0_gpio is invalid\n", __func__); + return -EINVAL; + } + + if (msm_hifi_control != MSM_HIFI_ON) { + dev_dbg(codec->dev, "%s: HiFi mixer control is not set\n", + __func__); + return 0; + } + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + msm_cdc_pinctrl_select_active_state(pdata->hph_en0_gpio_p); + break; + case SND_SOC_DAPM_PRE_PMD: + msm_cdc_pinctrl_select_sleep_state(pdata->hph_en0_gpio_p); + break; + } + + return 0; +} + +static const struct snd_soc_dapm_widget msm_dapm_widgets[] = { + + SND_SOC_DAPM_SUPPLY("MCLK", SND_SOC_NOPM, 0, 0, + msm_mclk_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_SUPPLY("MCLK TX", SND_SOC_NOPM, 0, 0, + msm_mclk_tx_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MIC("Handset Mic", NULL), + SND_SOC_DAPM_MIC("Analog Mic3", NULL), + SND_SOC_DAPM_MIC("Analog Mic4", NULL), + + SND_SOC_DAPM_MIC("Digital Mic0", NULL), + SND_SOC_DAPM_MIC("Digital Mic1", NULL), + SND_SOC_DAPM_MIC("Digital Mic2", NULL), + SND_SOC_DAPM_MIC("Digital Mic3", NULL), + SND_SOC_DAPM_MIC("Digital Mic4", NULL), + SND_SOC_DAPM_MIC("Digital Mic5", NULL), + SND_SOC_DAPM_MIC("Digital Mic6", NULL), + SND_SOC_DAPM_MIC("Digital Mic7", NULL), +}; + +static const struct snd_soc_dapm_widget msm_dapm_widgets_tavil[] = { + + SND_SOC_DAPM_SUPPLY("MCLK", SND_SOC_NOPM, 0, 0, + msm_mclk_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_SUPPLY("MCLK TX", SND_SOC_NOPM, 0, 0, + msm_mclk_tx_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_SPK("Lineout_1 amp", NULL), + SND_SOC_DAPM_SPK("Lineout_2 amp", NULL), + SND_SOC_DAPM_SPK("hifi amp", msm_hifi_ctrl_event), + SND_SOC_DAPM_MIC("Handset Mic", NULL), + SND_SOC_DAPM_MIC("Headset Mic", NULL), + SND_SOC_DAPM_MIC("ANCRight Headset Mic", NULL), + SND_SOC_DAPM_MIC("ANCLeft Headset Mic", NULL), + SND_SOC_DAPM_MIC("Analog Mic5", NULL), + + SND_SOC_DAPM_MIC("Digital Mic0", NULL), + SND_SOC_DAPM_MIC("Digital Mic1", NULL), + SND_SOC_DAPM_MIC("Digital Mic2", NULL), + SND_SOC_DAPM_MIC("Digital Mic3", NULL), + SND_SOC_DAPM_MIC("Digital Mic4", NULL), + SND_SOC_DAPM_MIC("Digital Mic5", NULL), +}; + +static inline int param_is_mask(int p) +{ + return (p >= SNDRV_PCM_HW_PARAM_FIRST_MASK) && + (p <= SNDRV_PCM_HW_PARAM_LAST_MASK); +} + +static inline struct snd_mask *param_to_mask(struct snd_pcm_hw_params *p, + int n) +{ + return &(p->masks[n - SNDRV_PCM_HW_PARAM_FIRST_MASK]); +} + +static void param_set_mask(struct snd_pcm_hw_params *p, int n, + unsigned int bit) +{ + if (bit >= SNDRV_MASK_MAX) + return; + if (param_is_mask(n)) { + struct snd_mask *m = param_to_mask(p, n); + + m->bits[0] = 0; + m->bits[1] = 0; + m->bits[bit >> 5] |= (1 << (bit & 31)); + } +} + +static int msm_slim_get_ch_from_beid(int32_t be_id) +{ + int ch_id = 0; + + switch (be_id) { + case MSM_BACKEND_DAI_SLIMBUS_0_RX: + ch_id = SLIM_RX_0; + break; + case MSM_BACKEND_DAI_SLIMBUS_1_RX: + ch_id = SLIM_RX_1; + break; + case MSM_BACKEND_DAI_SLIMBUS_2_RX: + ch_id = SLIM_RX_2; + break; + case MSM_BACKEND_DAI_SLIMBUS_3_RX: + ch_id = SLIM_RX_3; + break; + case MSM_BACKEND_DAI_SLIMBUS_4_RX: + ch_id = SLIM_RX_4; + break; + case MSM_BACKEND_DAI_SLIMBUS_6_RX: + ch_id = SLIM_RX_6; + break; + case MSM_BACKEND_DAI_SLIMBUS_0_TX: + ch_id = SLIM_TX_0; + break; + case MSM_BACKEND_DAI_SLIMBUS_3_TX: + ch_id = SLIM_TX_3; + break; + default: + ch_id = SLIM_RX_0; + break; + } + + return ch_id; +} + +static int msm_ext_disp_get_idx_from_beid(int32_t be_id) +{ + int idx; + + switch (be_id) { + case MSM_BACKEND_DAI_DISPLAY_PORT_RX: + idx = DP_RX_IDX; + break; + default: + pr_err("%s: Incorrect ext_disp BE id %d\n", __func__, be_id); + idx = -EINVAL; + break; + } + + return idx; +} + +static int msm_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_dai_link *dai_link = rtd->dai_link; + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + int rc = 0; + int idx; + void *config = NULL; + struct snd_soc_codec *codec = NULL; + + pr_debug("%s: dai_id= %d, format = %d, rate = %d\n", + __func__, dai_link->id, params_format(params), + params_rate(params)); + + + switch (dai_link->id) { + case MSM_BACKEND_DAI_SLIMBUS_0_RX: + case MSM_BACKEND_DAI_SLIMBUS_1_RX: + case MSM_BACKEND_DAI_SLIMBUS_2_RX: + case MSM_BACKEND_DAI_SLIMBUS_3_RX: + case MSM_BACKEND_DAI_SLIMBUS_4_RX: + case MSM_BACKEND_DAI_SLIMBUS_6_RX: + idx = msm_slim_get_ch_from_beid(dai_link->id); + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + slim_rx_cfg[idx].bit_format); + rate->min = rate->max = slim_rx_cfg[idx].sample_rate; + channels->min = channels->max = slim_rx_cfg[idx].channels; + break; + + case MSM_BACKEND_DAI_SLIMBUS_0_TX: + case MSM_BACKEND_DAI_SLIMBUS_3_TX: + idx = msm_slim_get_ch_from_beid(dai_link->id); + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + slim_tx_cfg[idx].bit_format); + rate->min = rate->max = slim_tx_cfg[idx].sample_rate; + channels->min = channels->max = slim_tx_cfg[idx].channels; + break; + + case MSM_BACKEND_DAI_SLIMBUS_1_TX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + slim_tx_cfg[1].bit_format); + rate->min = rate->max = slim_tx_cfg[1].sample_rate; + channels->min = channels->max = slim_tx_cfg[1].channels; + break; + + case MSM_BACKEND_DAI_SLIMBUS_4_TX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + SNDRV_PCM_FORMAT_S32_LE); + rate->min = rate->max = SAMPLING_RATE_8KHZ; + channels->min = channels->max = msm_vi_feed_tx_ch; + break; + + case MSM_BACKEND_DAI_SLIMBUS_5_RX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + slim_rx_cfg[5].bit_format); + rate->min = rate->max = slim_rx_cfg[5].sample_rate; + channels->min = channels->max = slim_rx_cfg[5].channels; + break; + + case MSM_BACKEND_DAI_SLIMBUS_5_TX: + codec = rtd->codec; + rate->min = rate->max = SAMPLING_RATE_16KHZ; + channels->min = channels->max = 1; + + config = msm_codec_fn.get_afe_config_fn(codec, + AFE_SLIMBUS_SLAVE_PORT_CONFIG); + if (config) { + rc = afe_set_config(AFE_SLIMBUS_SLAVE_PORT_CONFIG, + config, SLIMBUS_5_TX); + if (rc) + pr_err("%s: Failed to set slimbus slave port config %d\n", + __func__, rc); + } + break; + + case MSM_BACKEND_DAI_SLIMBUS_7_RX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + slim_rx_cfg[SLIM_RX_7].bit_format); + rate->min = rate->max = slim_rx_cfg[SLIM_RX_7].sample_rate; + channels->min = channels->max = + slim_rx_cfg[SLIM_RX_7].channels; + break; + + case MSM_BACKEND_DAI_SLIMBUS_7_TX: + rate->min = rate->max = slim_tx_cfg[SLIM_TX_7].sample_rate; + channels->min = channels->max = + slim_tx_cfg[SLIM_TX_7].channels; + break; + + case MSM_BACKEND_DAI_SLIMBUS_8_TX: + rate->min = rate->max = slim_tx_cfg[SLIM_TX_8].sample_rate; + channels->min = channels->max = + slim_tx_cfg[SLIM_TX_8].channels; + break; + + case MSM_BACKEND_DAI_USB_RX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + usb_rx_cfg.bit_format); + rate->min = rate->max = usb_rx_cfg.sample_rate; + channels->min = channels->max = usb_rx_cfg.channels; + break; + + case MSM_BACKEND_DAI_USB_TX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + usb_tx_cfg.bit_format); + rate->min = rate->max = usb_tx_cfg.sample_rate; + channels->min = channels->max = usb_tx_cfg.channels; + break; + + case MSM_BACKEND_DAI_DISPLAY_PORT_RX: + idx = msm_ext_disp_get_idx_from_beid(dai_link->id); + if (idx < 0) { + pr_err("%s: Incorrect ext disp idx %d\n", + __func__, idx); + rc = idx; + goto done; + } + + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + ext_disp_rx_cfg[idx].bit_format); + rate->min = rate->max = ext_disp_rx_cfg[idx].sample_rate; + channels->min = channels->max = ext_disp_rx_cfg[idx].channels; + break; + + case MSM_BACKEND_DAI_AFE_PCM_RX: + channels->min = channels->max = proxy_rx_cfg.channels; + rate->min = rate->max = SAMPLING_RATE_48KHZ; + break; + + case MSM_BACKEND_DAI_PRI_TDM_RX_0: + channels->min = channels->max = + tdm_rx_cfg[TDM_PRI][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_PRI][TDM_0].bit_format); + rate->min = rate->max = tdm_rx_cfg[TDM_PRI][TDM_0].sample_rate; + break; + + case MSM_BACKEND_DAI_PRI_TDM_TX_0: + channels->min = channels->max = + tdm_tx_cfg[TDM_PRI][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_tx_cfg[TDM_PRI][TDM_0].bit_format); + rate->min = rate->max = tdm_tx_cfg[TDM_PRI][TDM_0].sample_rate; + break; + + case MSM_BACKEND_DAI_SEC_TDM_RX_0: + channels->min = channels->max = + tdm_rx_cfg[TDM_SEC][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_SEC][TDM_0].bit_format); + rate->min = rate->max = tdm_rx_cfg[TDM_SEC][TDM_0].sample_rate; + break; + + case MSM_BACKEND_DAI_SEC_TDM_TX_0: + channels->min = channels->max = + tdm_tx_cfg[TDM_SEC][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_tx_cfg[TDM_SEC][TDM_0].bit_format); + rate->min = rate->max = tdm_tx_cfg[TDM_SEC][TDM_0].sample_rate; + break; + + case MSM_BACKEND_DAI_TERT_TDM_RX_0: + channels->min = channels->max = + tdm_rx_cfg[TDM_TERT][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_TERT][TDM_0].bit_format); + rate->min = rate->max = tdm_rx_cfg[TDM_TERT][TDM_0].sample_rate; + break; + + case MSM_BACKEND_DAI_TERT_TDM_TX_0: + channels->min = channels->max = + tdm_tx_cfg[TDM_TERT][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_tx_cfg[TDM_TERT][TDM_0].bit_format); + rate->min = rate->max = tdm_tx_cfg[TDM_TERT][TDM_0].sample_rate; + break; + + case MSM_BACKEND_DAI_QUAT_TDM_RX_0: + channels->min = channels->max = + tdm_rx_cfg[TDM_QUAT][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_QUAT][TDM_0].bit_format); + rate->min = rate->max = tdm_rx_cfg[TDM_QUAT][TDM_0].sample_rate; + break; + + case MSM_BACKEND_DAI_QUAT_TDM_TX_0: + channels->min = channels->max = + tdm_tx_cfg[TDM_QUAT][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_tx_cfg[TDM_QUAT][TDM_0].bit_format); + rate->min = rate->max = tdm_tx_cfg[TDM_QUAT][TDM_0].sample_rate; + break; + + case MSM_BACKEND_DAI_QUIN_TDM_RX_0: + channels->min = channels->max = + tdm_rx_cfg[TDM_QUIN][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_QUIN][TDM_0].bit_format); + rate->min = rate->max = tdm_rx_cfg[TDM_QUIN][TDM_0].sample_rate; + break; + + case MSM_BACKEND_DAI_QUIN_TDM_TX_0: + channels->min = channels->max = + tdm_tx_cfg[TDM_QUIN][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_tx_cfg[TDM_QUIN][TDM_0].bit_format); + rate->min = rate->max = tdm_tx_cfg[TDM_QUIN][TDM_0].sample_rate; + break; + + + case MSM_BACKEND_DAI_AUXPCM_RX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + aux_pcm_rx_cfg[PRIM_AUX_PCM].bit_format); + rate->min = rate->max = + aux_pcm_rx_cfg[PRIM_AUX_PCM].sample_rate; + channels->min = channels->max = + aux_pcm_rx_cfg[PRIM_AUX_PCM].channels; + break; + + case MSM_BACKEND_DAI_AUXPCM_TX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + aux_pcm_tx_cfg[PRIM_AUX_PCM].bit_format); + rate->min = rate->max = + aux_pcm_tx_cfg[PRIM_AUX_PCM].sample_rate; + channels->min = channels->max = + aux_pcm_tx_cfg[PRIM_AUX_PCM].channels; + break; + + case MSM_BACKEND_DAI_SEC_AUXPCM_RX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + aux_pcm_rx_cfg[SEC_AUX_PCM].bit_format); + rate->min = rate->max = + aux_pcm_rx_cfg[SEC_AUX_PCM].sample_rate; + channels->min = channels->max = + aux_pcm_rx_cfg[SEC_AUX_PCM].channels; + break; + + case MSM_BACKEND_DAI_SEC_AUXPCM_TX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + aux_pcm_tx_cfg[SEC_AUX_PCM].bit_format); + rate->min = rate->max = + aux_pcm_tx_cfg[SEC_AUX_PCM].sample_rate; + channels->min = channels->max = + aux_pcm_tx_cfg[SEC_AUX_PCM].channels; + break; + + case MSM_BACKEND_DAI_TERT_AUXPCM_RX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + aux_pcm_rx_cfg[TERT_AUX_PCM].bit_format); + rate->min = rate->max = + aux_pcm_rx_cfg[TERT_AUX_PCM].sample_rate; + channels->min = channels->max = + aux_pcm_rx_cfg[TERT_AUX_PCM].channels; + break; + + case MSM_BACKEND_DAI_TERT_AUXPCM_TX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + aux_pcm_tx_cfg[TERT_AUX_PCM].bit_format); + rate->min = rate->max = + aux_pcm_tx_cfg[TERT_AUX_PCM].sample_rate; + channels->min = channels->max = + aux_pcm_tx_cfg[TERT_AUX_PCM].channels; + break; + + case MSM_BACKEND_DAI_QUAT_AUXPCM_RX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + aux_pcm_rx_cfg[QUAT_AUX_PCM].bit_format); + rate->min = rate->max = + aux_pcm_rx_cfg[QUAT_AUX_PCM].sample_rate; + channels->min = channels->max = + aux_pcm_rx_cfg[QUAT_AUX_PCM].channels; + break; + + case MSM_BACKEND_DAI_QUAT_AUXPCM_TX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + aux_pcm_tx_cfg[QUAT_AUX_PCM].bit_format); + rate->min = rate->max = + aux_pcm_tx_cfg[QUAT_AUX_PCM].sample_rate; + channels->min = channels->max = + aux_pcm_tx_cfg[QUAT_AUX_PCM].channels; + break; + + case MSM_BACKEND_DAI_QUIN_AUXPCM_RX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + aux_pcm_rx_cfg[QUIN_AUX_PCM].bit_format); + rate->min = rate->max = + aux_pcm_rx_cfg[QUIN_AUX_PCM].sample_rate; + channels->min = channels->max = + aux_pcm_rx_cfg[QUIN_AUX_PCM].channels; + break; + + case MSM_BACKEND_DAI_QUIN_AUXPCM_TX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + aux_pcm_tx_cfg[QUIN_AUX_PCM].bit_format); + rate->min = rate->max = + aux_pcm_tx_cfg[QUIN_AUX_PCM].sample_rate; + channels->min = channels->max = + aux_pcm_tx_cfg[QUIN_AUX_PCM].channels; + break; + + case MSM_BACKEND_DAI_PRI_MI2S_RX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + mi2s_rx_cfg[PRIM_MI2S].bit_format); + rate->min = rate->max = mi2s_rx_cfg[PRIM_MI2S].sample_rate; + channels->min = channels->max = + mi2s_rx_cfg[PRIM_MI2S].channels; + break; + + case MSM_BACKEND_DAI_PRI_MI2S_TX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + mi2s_tx_cfg[PRIM_MI2S].bit_format); + rate->min = rate->max = mi2s_tx_cfg[PRIM_MI2S].sample_rate; + channels->min = channels->max = + mi2s_tx_cfg[PRIM_MI2S].channels; + break; + + case MSM_BACKEND_DAI_SECONDARY_MI2S_RX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + mi2s_rx_cfg[SEC_MI2S].bit_format); + rate->min = rate->max = mi2s_rx_cfg[SEC_MI2S].sample_rate; + channels->min = channels->max = + mi2s_rx_cfg[SEC_MI2S].channels; + break; + + case MSM_BACKEND_DAI_SECONDARY_MI2S_TX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + mi2s_tx_cfg[SEC_MI2S].bit_format); + rate->min = rate->max = mi2s_tx_cfg[SEC_MI2S].sample_rate; + channels->min = channels->max = + mi2s_tx_cfg[SEC_MI2S].channels; + break; + + case MSM_BACKEND_DAI_TERTIARY_MI2S_RX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + mi2s_rx_cfg[TERT_MI2S].bit_format); + rate->min = rate->max = mi2s_rx_cfg[TERT_MI2S].sample_rate; + channels->min = channels->max = + mi2s_rx_cfg[TERT_MI2S].channels; + break; + + case MSM_BACKEND_DAI_TERTIARY_MI2S_TX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + mi2s_tx_cfg[TERT_MI2S].bit_format); + rate->min = rate->max = mi2s_tx_cfg[TERT_MI2S].sample_rate; + channels->min = channels->max = + mi2s_tx_cfg[TERT_MI2S].channels; + break; + + case MSM_BACKEND_DAI_QUATERNARY_MI2S_RX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + mi2s_rx_cfg[QUAT_MI2S].bit_format); + rate->min = rate->max = mi2s_rx_cfg[QUAT_MI2S].sample_rate; + channels->min = channels->max = + mi2s_rx_cfg[QUAT_MI2S].channels; + break; + + case MSM_BACKEND_DAI_QUATERNARY_MI2S_TX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + mi2s_tx_cfg[QUAT_MI2S].bit_format); + rate->min = rate->max = mi2s_tx_cfg[QUAT_MI2S].sample_rate; + channels->min = channels->max = + mi2s_tx_cfg[QUAT_MI2S].channels; + break; + + case MSM_BACKEND_DAI_QUINARY_MI2S_RX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + mi2s_rx_cfg[QUIN_MI2S].bit_format); + rate->min = rate->max = mi2s_rx_cfg[QUIN_MI2S].sample_rate; + channels->min = channels->max = + mi2s_rx_cfg[QUIN_MI2S].channels; + break; + + case MSM_BACKEND_DAI_QUINARY_MI2S_TX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + mi2s_tx_cfg[QUIN_MI2S].bit_format); + rate->min = rate->max = mi2s_tx_cfg[QUIN_MI2S].sample_rate; + channels->min = channels->max = + mi2s_tx_cfg[QUIN_MI2S].channels; + break; + + default: + rate->min = rate->max = SAMPLING_RATE_48KHZ; + break; + } + +done: + return rc; +} + +static bool msm_usbc_swap_gnd_mic(struct snd_soc_codec *codec, bool active) +{ + struct snd_soc_card *card = codec->component.card; + struct msm_asoc_mach_data *pdata = + snd_soc_card_get_drvdata(card); + + if (!pdata->fsa_handle) + return false; + + return fsa4480_switch_event(pdata->fsa_handle, FSA_MIC_GND_SWAP); +} + +static bool msm_swap_gnd_mic(struct snd_soc_codec *codec, bool active) +{ + int value = 0; + bool ret = false; + struct snd_soc_card *card; + struct msm_asoc_mach_data *pdata; + + if (!codec) { + pr_err("%s codec is NULL\n", __func__); + return false; + } + card = codec->component.card; + pdata = snd_soc_card_get_drvdata(card); + + if (!pdata) + return false; + + if (wcd_mbhc_cfg.enable_usbc_analog) + return msm_usbc_swap_gnd_mic(codec, active); + + /* if usbc is not defined, swap using us_euro_gpio_p */ + if (pdata->us_euro_gpio_p) { + value = msm_cdc_pinctrl_get_state( + pdata->us_euro_gpio_p); + if (value) + msm_cdc_pinctrl_select_sleep_state( + pdata->us_euro_gpio_p); + else + msm_cdc_pinctrl_select_active_state( + pdata->us_euro_gpio_p); + dev_dbg(codec->dev, "%s: swap select switch %d to %d\n", + __func__, value, !value); + ret = true; + } + + return ret; +} + +static int msm_afe_set_config(struct snd_soc_codec *codec) +{ + int ret = 0; + void *config_data = NULL; + + if (!msm_codec_fn.get_afe_config_fn) { + dev_err(codec->dev, "%s: codec get afe config not init'ed\n", + __func__); + return -EINVAL; + } + + config_data = msm_codec_fn.get_afe_config_fn(codec, + AFE_CDC_REGISTERS_CONFIG); + if (config_data) { + ret = afe_set_config(AFE_CDC_REGISTERS_CONFIG, config_data, 0); + if (ret) { + dev_err(codec->dev, + "%s: Failed to set codec registers config %d\n", + __func__, ret); + return ret; + } + } + + config_data = msm_codec_fn.get_afe_config_fn(codec, + AFE_CDC_REGISTER_PAGE_CONFIG); + if (config_data) { + ret = afe_set_config(AFE_CDC_REGISTER_PAGE_CONFIG, config_data, + 0); + if (ret) + dev_err(codec->dev, + "%s: Failed to set cdc register page config\n", + __func__); + } + + config_data = msm_codec_fn.get_afe_config_fn(codec, + AFE_SLIMBUS_SLAVE_CONFIG); + if (config_data) { + ret = afe_set_config(AFE_SLIMBUS_SLAVE_CONFIG, config_data, 0); + if (ret) { + dev_err(codec->dev, + "%s: Failed to set slimbus slave config %d\n", + __func__, ret); + return ret; + } + } + + return 0; +} + +static void msm_afe_clear_config(void) +{ + afe_clear_config(AFE_CDC_REGISTERS_CONFIG); + afe_clear_config(AFE_SLIMBUS_SLAVE_CONFIG); +} + +static int msm_adsp_power_up_config(struct snd_soc_codec *codec, + struct snd_card *card) +{ + int ret = 0; + unsigned long timeout; + int adsp_ready = 0; + bool snd_card_online = 0; + + timeout = jiffies + + msecs_to_jiffies(ADSP_STATE_READY_TIMEOUT_MS); + + /* sleep for 100ms before querying AVS up */ + msleep(100); + do { + if (!snd_card_online) { + snd_card_online = snd_card_is_online_state(card); + pr_debug("%s: Sound card is %s\n", __func__, + snd_card_online ? "Online" : "Offline"); + } + if (!adsp_ready) { + adsp_ready = q6core_is_adsp_ready(); + pr_debug("%s: ADSP Audio is %s\n", __func__, + adsp_ready ? "ready" : "not ready"); + } + if (snd_card_online && adsp_ready) + break; + + /* + * Sound card/ADSP will be coming up after subsystem restart and + * it might not be fully up when the control reaches + * here. So, wait for 50msec before checking ADSP state + */ + msleep(50); + } while (time_after(timeout, jiffies)); + + if (!snd_card_online || !adsp_ready) { + pr_err("%s: Timeout. Sound card is %s, ADSP Audio is %s\n", + __func__, + snd_card_online ? "Online" : "Offline", + adsp_ready ? "ready" : "not ready"); + ret = -ETIMEDOUT; + goto err; + } + + ret = msm_afe_set_config(codec); + if (ret) + pr_err("%s: Failed to set AFE config. err %d\n", + __func__, ret); + + return 0; + +err: + return ret; +} + +static void msm_adsp_power_up_config_work(struct work_struct *work) +{ + struct msm_asoc_mach_data *pdata; + struct snd_soc_codec *codec; + struct snd_card *card; + + pdata = container_of(work, struct msm_asoc_mach_data, + adsp_power_up_work); + codec = pdata->codec; + card = codec->component.card->snd_card; + msm_adsp_power_up_config(codec, card); +} + +static int sm8150_notifier_service_cb(struct notifier_block *this, + unsigned long opcode, void *ptr) +{ + int ret; + struct snd_soc_card *card = NULL; + const char *be_dl_name = LPASS_BE_SLIMBUS_0_RX; + struct snd_soc_pcm_runtime *rtd; + struct snd_soc_codec *codec; + struct msm_asoc_mach_data *pdata; + + pr_debug("%s: Service opcode 0x%lx\n", __func__, opcode); + + switch (opcode) { + case AUDIO_NOTIFIER_SERVICE_DOWN: + /* + * Use flag to ignore initial boot notifications + * On initial boot msm_adsp_power_up_config is + * called on init. There is no need to clear + * and set the config again on initial boot. + */ + if (is_initial_boot) + break; + msm_afe_clear_config(); + break; + case AUDIO_NOTIFIER_SERVICE_UP: + if (is_initial_boot) { + is_initial_boot = false; + break; + } + if (!spdev) + return -EINVAL; + + card = platform_get_drvdata(spdev); + rtd = snd_soc_get_pcm_runtime(card, be_dl_name); + if (!rtd) { + dev_err(card->dev, + "%s: snd_soc_get_pcm_runtime for %s failed!\n", + __func__, be_dl_name); + ret = -EINVAL; + goto err; + } + codec = rtd->codec; + + pdata = snd_soc_card_get_drvdata(card); + pdata->codec = codec; + schedule_work(&pdata->adsp_power_up_work); + break; + default: + break; + } +err: + return NOTIFY_OK; +} + +static struct notifier_block service_nb = { + .notifier_call = sm8150_notifier_service_cb, + .priority = -INT_MAX, +}; + +static int msm_audrx_init(struct snd_soc_pcm_runtime *rtd) +{ + int ret = 0; + void *config_data; + struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_component *aux_comp; + struct snd_card *card; + struct snd_info_entry *entry; + struct msm_asoc_mach_data *pdata = + snd_soc_card_get_drvdata(rtd->card); + + /* + * Codec SLIMBUS configuration + * RX1, RX2, RX3, RX4, RX5, RX6, RX7, RX8 + * TX1, TX2, TX3, TX4, TX5, TX6, TX7, TX8, TX9, TX10, TX11, TX12, TX13 + * TX14, TX15, TX16 + */ + unsigned int rx_ch[WCD9360_RX_MAX] = {144, 145, 146, 147, 148, 149, + 150, 151}; + unsigned int tx_ch[WCD9360_TX_MAX] = {128, 129, 130, 131, 132, 133, + 134, 135, 136, 137, 138, 139, + 140, 141, 142, 143}; + + pr_debug("%s: dev_name:%s\n", __func__, dev_name(cpu_dai->dev)); + + rtd->pmdown_time = 0; + + ret = snd_soc_add_codec_controls(codec, msm_snd_controls, + ARRAY_SIZE(msm_snd_controls)); + if (ret < 0) { + pr_err("%s: add_codec_controls failed, err %d\n", + __func__, ret); + return ret; + } + + if (!strcmp(dev_name(codec_dai->dev), "tavil_codec")) { + snd_soc_dapm_new_controls(dapm, msm_dapm_widgets_tavil, + ARRAY_SIZE(msm_dapm_widgets_tavil)); + snd_soc_dapm_add_routes(dapm, wcd_audio_paths_tavil, + ARRAY_SIZE(wcd_audio_paths_tavil)); + } else { + snd_soc_dapm_new_controls(dapm, msm_dapm_widgets, + ARRAY_SIZE(msm_dapm_widgets)); + snd_soc_dapm_add_routes(dapm, wcd_audio_paths, + ARRAY_SIZE(wcd_audio_paths)); + } + + snd_soc_dapm_ignore_suspend(dapm, "Handset Mic"); + snd_soc_dapm_ignore_suspend(dapm, "Digital Mic0"); + snd_soc_dapm_ignore_suspend(dapm, "Digital Mic1"); + snd_soc_dapm_ignore_suspend(dapm, "Digital Mic2"); + snd_soc_dapm_ignore_suspend(dapm, "Digital Mic3"); + snd_soc_dapm_ignore_suspend(dapm, "Digital Mic4"); + snd_soc_dapm_ignore_suspend(dapm, "Digital Mic5"); + snd_soc_dapm_ignore_suspend(dapm, "MADINPUT"); + snd_soc_dapm_ignore_suspend(dapm, "MAD_CPE_INPUT"); + snd_soc_dapm_ignore_suspend(dapm, "MAD_CPE_OUT1"); + snd_soc_dapm_ignore_suspend(dapm, "MAD_CPE_OUT2"); + snd_soc_dapm_ignore_suspend(dapm, "EAR"); + snd_soc_dapm_ignore_suspend(dapm, "ANC EAR"); + snd_soc_dapm_ignore_suspend(dapm, "SPK1 OUT"); + snd_soc_dapm_ignore_suspend(dapm, "SPK2 OUT"); + snd_soc_dapm_ignore_suspend(dapm, "AIF4 VI"); + snd_soc_dapm_ignore_suspend(dapm, "VIINPUT"); + snd_soc_dapm_ignore_suspend(dapm, "WDMA3_OUT"); + + if (!strcmp(dev_name(codec_dai->dev), "tavil_codec")) { + snd_soc_dapm_ignore_suspend(dapm, "Headset Mic"); + snd_soc_dapm_ignore_suspend(dapm, "ANCRight Headset Mic"); + snd_soc_dapm_ignore_suspend(dapm, "ANCLeft Headset Mic"); + snd_soc_dapm_ignore_suspend(dapm, "Analog Mic5"); + snd_soc_dapm_ignore_suspend(dapm, "LINEOUT1"); + snd_soc_dapm_ignore_suspend(dapm, "LINEOUT2"); + snd_soc_dapm_ignore_suspend(dapm, "HPHL"); + snd_soc_dapm_ignore_suspend(dapm, "HPHR"); + snd_soc_dapm_ignore_suspend(dapm, "ANC HPHL"); + snd_soc_dapm_ignore_suspend(dapm, "ANC HPHR"); + } else { + snd_soc_dapm_ignore_suspend(dapm, "Digital Mic6"); + snd_soc_dapm_ignore_suspend(dapm, "Digital Mic7"); + snd_soc_dapm_ignore_suspend(dapm, "Analog Mic3"); + snd_soc_dapm_ignore_suspend(dapm, "Analog Mic4"); + } + + snd_soc_dapm_sync(dapm); + + snd_soc_dai_set_channel_map(codec_dai, ARRAY_SIZE(tx_ch), + tx_ch, ARRAY_SIZE(rx_ch), rx_ch); + + if (!strcmp(dev_name(codec_dai->dev), "tavil_codec")) + msm_codec_fn.get_afe_config_fn = tavil_get_afe_config; + else + msm_codec_fn.get_afe_config_fn = pahu_get_afe_config; + + ret = msm_adsp_power_up_config(codec, rtd->card->snd_card); + if (ret) { + pr_err("%s: Failed to set AFE config %d\n", __func__, ret); + goto err; + } + + config_data = msm_codec_fn.get_afe_config_fn(codec, + AFE_AANC_VERSION); + if (config_data) { + ret = afe_set_config(AFE_AANC_VERSION, config_data, 0); + if (ret) { + pr_err("%s: Failed to set aanc version %d\n", + __func__, ret); + goto err; + } + } + + /* + * Send speaker configuration only for WSA8810. + * Default configuration is for WSA8815. + */ + pr_debug("%s: Number of aux devices: %d\n", + __func__, rtd->card->num_aux_devs); + if (!strcmp(dev_name(codec_dai->dev), "tavil_codec")) { + if (rtd->card->num_aux_devs && + !list_empty(&rtd->card->component_dev_list)) { + aux_comp = list_first_entry( + &rtd->card->component_dev_list, + struct snd_soc_component, + card_aux_list); + if (!strcmp(aux_comp->name, WSA8810_NAME_1) || + !strcmp(aux_comp->name, WSA8810_NAME_2)) { + tavil_set_spkr_mode(rtd->codec, + WCD934X_SPKR_MODE_1); + tavil_set_spkr_gain_offset(rtd->codec, + WCD934X_RX_GAIN_OFFSET_M1P5_DB); + } + } + card = rtd->card->snd_card; + entry = snd_info_create_subdir(card->module, "codecs", + card->proc_root); + if (!entry) { + pr_debug("%s: Cannot create codecs module entry\n", + __func__); + pdata->codec_root = NULL; + goto done; + } + pdata->codec_root = entry; + tavil_codec_info_create_codec_entry(pdata->codec_root, codec); + } else { + if (rtd->card->num_aux_devs && + !list_empty(&rtd->card->component_dev_list)) { + aux_comp = list_first_entry(&rtd->card->component_dev_list, + struct snd_soc_component, card_aux_list); + if (!strcmp(aux_comp->name, WSA8810_NAME_1) || + !strcmp(aux_comp->name, WSA8810_NAME_2)) { + pahu_set_spkr_mode(rtd->codec, WCD9360_SPKR_MODE_1); + pahu_set_spkr_gain_offset(rtd->codec, + WCD9360_RX_GAIN_OFFSET_M1P5_DB); + } + } + card = rtd->card->snd_card; + entry = snd_info_create_subdir(card->module, "codecs", + card->proc_root); + if (!entry) { + pr_debug("%s: Cannot create codecs module entry\n", + __func__); + pdata->codec_root = NULL; + goto done; + } + pdata->codec_root = entry; + pahu_codec_info_create_codec_entry(pdata->codec_root, codec); + } +done: + codec_reg_done = true; + return 0; + +err: + return ret; +} + +static int msm_wcn_init(struct snd_soc_pcm_runtime *rtd) +{ + unsigned int rx_ch[WCN_CDC_SLIM_RX_CH_MAX] = {157, 158}; + unsigned int tx_ch[WCN_CDC_SLIM_TX_CH_MAX] = {159, 160, 161}; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + + return snd_soc_dai_set_channel_map(codec_dai, ARRAY_SIZE(tx_ch), + tx_ch, ARRAY_SIZE(rx_ch), rx_ch); +} + +static void *def_wcd_mbhc_cal(void) +{ + void *wcd_mbhc_cal; + struct wcd_mbhc_btn_detect_cfg *btn_cfg; + u16 *btn_high; + + wcd_mbhc_cal = kzalloc(WCD_MBHC_CAL_SIZE(WCD_MBHC_DEF_BUTTONS, + WCD9XXX_MBHC_DEF_RLOADS), GFP_KERNEL); + if (!wcd_mbhc_cal) + return NULL; + +#define S(X, Y) ((WCD_MBHC_CAL_PLUG_TYPE_PTR(wcd_mbhc_cal)->X) = (Y)) + S(v_hs_max, 1600); +#undef S +#define S(X, Y) ((WCD_MBHC_CAL_BTN_DET_PTR(wcd_mbhc_cal)->X) = (Y)) + S(num_btn, WCD_MBHC_DEF_BUTTONS); +#undef S + + btn_cfg = WCD_MBHC_CAL_BTN_DET_PTR(wcd_mbhc_cal); + btn_high = ((void *)&btn_cfg->_v_btn_low) + + (sizeof(btn_cfg->_v_btn_low[0]) * btn_cfg->num_btn); + + btn_high[0] = 75; + btn_high[1] = 150; + btn_high[2] = 237; + btn_high[3] = 500; + btn_high[4] = 500; + btn_high[5] = 500; + btn_high[6] = 500; + btn_high[7] = 500; + + return wcd_mbhc_cal; +} + +static int msm_snd_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai_link *dai_link = rtd->dai_link; + + int ret = 0; + u32 rx_ch[SLIM_MAX_RX_PORTS], tx_ch[SLIM_MAX_TX_PORTS]; + u32 rx_ch_cnt = 0, tx_ch_cnt = 0; + u32 user_set_tx_ch = 0; + u32 rx_ch_count; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + ret = snd_soc_dai_get_channel_map(codec_dai, + &tx_ch_cnt, tx_ch, &rx_ch_cnt, rx_ch); + if (ret < 0) { + pr_err("%s: failed to get codec chan map, err:%d\n", + __func__, ret); + goto err; + } + if (dai_link->id == MSM_BACKEND_DAI_SLIMBUS_5_RX) { + pr_debug("%s: rx_5_ch=%d\n", __func__, + slim_rx_cfg[5].channels); + rx_ch_count = slim_rx_cfg[5].channels; + } else if (dai_link->id == MSM_BACKEND_DAI_SLIMBUS_2_RX) { + pr_debug("%s: rx_2_ch=%d\n", __func__, + slim_rx_cfg[2].channels); + rx_ch_count = slim_rx_cfg[2].channels; + } else if (dai_link->id == MSM_BACKEND_DAI_SLIMBUS_6_RX) { + pr_debug("%s: rx_6_ch=%d\n", __func__, + slim_rx_cfg[6].channels); + rx_ch_count = slim_rx_cfg[6].channels; + } else { + pr_debug("%s: rx_0_ch=%d\n", __func__, + slim_rx_cfg[0].channels); + rx_ch_count = slim_rx_cfg[0].channels; + } + ret = snd_soc_dai_set_channel_map(cpu_dai, 0, 0, + rx_ch_count, rx_ch); + if (ret < 0) { + pr_err("%s: failed to set cpu chan map, err:%d\n", + __func__, ret); + goto err; + } + } else { + + pr_debug("%s: %s_tx_dai_id_%d_ch=%d\n", __func__, + codec_dai->name, codec_dai->id, user_set_tx_ch); + ret = snd_soc_dai_get_channel_map(codec_dai, + &tx_ch_cnt, tx_ch, &rx_ch_cnt, rx_ch); + if (ret < 0) { + pr_err("%s: failed to get tx codec chan map, err:%d\n", + __func__, ret); + goto err; + } + /* For _tx1 case */ + if (dai_link->id == MSM_BACKEND_DAI_SLIMBUS_0_TX) + user_set_tx_ch = slim_tx_cfg[0].channels; + /* For _tx3 case */ + else if (dai_link->id == MSM_BACKEND_DAI_SLIMBUS_1_TX) + user_set_tx_ch = slim_tx_cfg[1].channels; + else if (dai_link->id == MSM_BACKEND_DAI_SLIMBUS_4_TX) + user_set_tx_ch = msm_vi_feed_tx_ch; + else + user_set_tx_ch = tx_ch_cnt; + + pr_debug("%s: msm_slim_0_tx_ch(%d) user_set_tx_ch(%d) tx_ch_cnt(%d), BE id (%d)\n", + __func__, slim_tx_cfg[0].channels, user_set_tx_ch, + tx_ch_cnt, dai_link->id); + + ret = snd_soc_dai_set_channel_map(cpu_dai, + user_set_tx_ch, tx_ch, 0, 0); + if (ret < 0) + pr_err("%s: failed to set tx cpu chan map, err:%d\n", + __func__, ret); + } + +err: + return ret; +} + +static int msm_slimbus_2_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + unsigned int rx_ch[SLIM_MAX_RX_PORTS], tx_ch[SLIM_MAX_TX_PORTS]; + unsigned int rx_ch_cnt = 0, tx_ch_cnt = 0; + unsigned int num_tx_ch = 0; + unsigned int num_rx_ch = 0; + int ret = 0; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + num_rx_ch = params_channels(params); + pr_debug("%s: %s rx_dai_id = %d num_ch = %d\n", __func__, + codec_dai->name, codec_dai->id, num_rx_ch); + ret = snd_soc_dai_get_channel_map(codec_dai, + &tx_ch_cnt, tx_ch, &rx_ch_cnt, rx_ch); + if (ret < 0) { + pr_err("%s: failed to get codec chan map, err:%d\n", + __func__, ret); + goto err; + } + ret = snd_soc_dai_set_channel_map(cpu_dai, 0, 0, + num_rx_ch, rx_ch); + if (ret < 0) { + pr_err("%s: failed to set cpu chan map, err:%d\n", + __func__, ret); + goto err; + } + } else { + num_tx_ch = params_channels(params); + pr_debug("%s: %s tx_dai_id = %d num_ch = %d\n", __func__, + codec_dai->name, codec_dai->id, num_tx_ch); + ret = snd_soc_dai_get_channel_map(codec_dai, + &tx_ch_cnt, tx_ch, &rx_ch_cnt, rx_ch); + if (ret < 0) { + pr_err("%s: failed to get tx codec chan map, err:%d\n", + __func__, ret); + goto err; + } + ret = snd_soc_dai_set_channel_map(cpu_dai, + num_tx_ch, tx_ch, 0, 0); + if (ret < 0) { + pr_err("%s: failed to set tx cpu chan map, err:%d\n", + __func__, ret); + goto err; + } + } + +err: + return ret; +} + +static int msm_wcn_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai_link *dai_link = rtd->dai_link; + u32 rx_ch[WCN_CDC_SLIM_RX_CH_MAX], tx_ch[WCN_CDC_SLIM_TX_CH_MAX]; + u32 rx_ch_cnt = 0, tx_ch_cnt = 0; + int ret; + + dev_dbg(rtd->dev, "%s: %s_tx_dai_id_%d\n", __func__, + codec_dai->name, codec_dai->id); + ret = snd_soc_dai_get_channel_map(codec_dai, + &tx_ch_cnt, tx_ch, &rx_ch_cnt, rx_ch); + if (ret) { + dev_err(rtd->dev, + "%s: failed to get BTFM codec chan map\n, err:%d\n", + __func__, ret); + goto err; + } + + dev_dbg(rtd->dev, "%s: tx_ch_cnt(%d) BE id %d\n", + __func__, tx_ch_cnt, dai_link->id); + + ret = snd_soc_dai_set_channel_map(cpu_dai, + tx_ch_cnt, tx_ch, rx_ch_cnt, rx_ch); + if (ret) + dev_err(rtd->dev, "%s: failed to set cpu chan map, err:%d\n", + __func__, ret); + +err: + return ret; +} + +static int msm_get_port_id(int be_id) +{ + int afe_port_id; + + switch (be_id) { + case MSM_BACKEND_DAI_PRI_MI2S_RX: + afe_port_id = AFE_PORT_ID_PRIMARY_MI2S_RX; + break; + case MSM_BACKEND_DAI_PRI_MI2S_TX: + afe_port_id = AFE_PORT_ID_PRIMARY_MI2S_TX; + break; + case MSM_BACKEND_DAI_SECONDARY_MI2S_RX: + afe_port_id = AFE_PORT_ID_SECONDARY_MI2S_RX; + break; + case MSM_BACKEND_DAI_SECONDARY_MI2S_TX: + afe_port_id = AFE_PORT_ID_SECONDARY_MI2S_TX; + break; + case MSM_BACKEND_DAI_TERTIARY_MI2S_RX: + afe_port_id = AFE_PORT_ID_TERTIARY_MI2S_RX; + break; + case MSM_BACKEND_DAI_TERTIARY_MI2S_TX: + afe_port_id = AFE_PORT_ID_TERTIARY_MI2S_TX; + break; + case MSM_BACKEND_DAI_QUATERNARY_MI2S_RX: + afe_port_id = AFE_PORT_ID_QUATERNARY_MI2S_RX; + break; + case MSM_BACKEND_DAI_QUATERNARY_MI2S_TX: + afe_port_id = AFE_PORT_ID_QUATERNARY_MI2S_TX; + break; + case MSM_BACKEND_DAI_QUINARY_MI2S_RX: + afe_port_id = AFE_PORT_ID_QUINARY_MI2S_RX; + break; + case MSM_BACKEND_DAI_QUINARY_MI2S_TX: + afe_port_id = AFE_PORT_ID_QUINARY_MI2S_TX; + break; + default: + pr_err("%s: Invalid BE id: %d\n", __func__, be_id); + afe_port_id = -EINVAL; + } + + return afe_port_id; +} + +static u32 get_mi2s_bits_per_sample(u32 bit_format) +{ + u32 bit_per_sample; + + switch (bit_format) { + case SNDRV_PCM_FORMAT_S32_LE: + case SNDRV_PCM_FORMAT_S24_3LE: + case SNDRV_PCM_FORMAT_S24_LE: + bit_per_sample = 32; + break; + case SNDRV_PCM_FORMAT_S16_LE: + default: + bit_per_sample = 16; + break; + } + + return bit_per_sample; +} + +static void update_mi2s_clk_val(int dai_id, int stream) +{ + u32 bit_per_sample; + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + bit_per_sample = + get_mi2s_bits_per_sample(mi2s_rx_cfg[dai_id].bit_format); + mi2s_clk[dai_id].clk_freq_in_hz = + mi2s_rx_cfg[dai_id].sample_rate * 2 * bit_per_sample; + } else { + bit_per_sample = + get_mi2s_bits_per_sample(mi2s_tx_cfg[dai_id].bit_format); + mi2s_clk[dai_id].clk_freq_in_hz = + mi2s_tx_cfg[dai_id].sample_rate * 2 * bit_per_sample; + } +} + +static int msm_mi2s_set_sclk(struct snd_pcm_substream *substream, bool enable) +{ + int ret = 0; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + int port_id = 0; + int index = cpu_dai->id; + + port_id = msm_get_port_id(rtd->dai_link->id); + if (port_id < 0) { + dev_err(rtd->card->dev, "%s: Invalid port_id\n", __func__); + ret = port_id; + goto err; + } + + if (enable) { + update_mi2s_clk_val(index, substream->stream); + dev_dbg(rtd->card->dev, "%s: clock rate %ul\n", __func__, + mi2s_clk[index].clk_freq_in_hz); + } + + mi2s_clk[index].enable = enable; + ret = afe_set_lpass_clock_v2(port_id, + &mi2s_clk[index]); + if (ret < 0) { + dev_err(rtd->card->dev, + "%s: afe lpass clock failed for port 0x%x , err:%d\n", + __func__, port_id, ret); + goto err; + } + +err: + return ret; +} + +static int msm_set_pinctrl(struct msm_pinctrl_info *pinctrl_info, + enum pinctrl_pin_state new_state) +{ + int ret = 0; + int curr_state = 0; + + if (pinctrl_info == NULL) { + pr_err("%s: pinctrl_info is NULL\n", __func__); + ret = -EINVAL; + goto err; + } + + if (pinctrl_info->pinctrl == NULL) { + pr_err("%s: pinctrl_info->pinctrl is NULL\n", __func__); + ret = -EINVAL; + goto err; + } + + curr_state = pinctrl_info->curr_state; + pinctrl_info->curr_state = new_state; + pr_debug("%s: curr_state = %s new_state = %s\n", __func__, + pin_states[curr_state], pin_states[pinctrl_info->curr_state]); + + if (curr_state == pinctrl_info->curr_state) { + pr_debug("%s: Already in same state\n", __func__); + goto err; + } + + if (curr_state != STATE_DISABLE && + pinctrl_info->curr_state != STATE_DISABLE) { + pr_debug("%s: state already active cannot switch\n", __func__); + ret = -EIO; + goto err; + } + + switch (pinctrl_info->curr_state) { + case STATE_MI2S_ACTIVE: + ret = pinctrl_select_state(pinctrl_info->pinctrl, + pinctrl_info->mi2s_active); + if (ret) { + pr_err("%s: MI2S state select failed with %d\n", + __func__, ret); + ret = -EIO; + goto err; + } + break; + case STATE_TDM_ACTIVE: + ret = pinctrl_select_state(pinctrl_info->pinctrl, + pinctrl_info->tdm_active); + if (ret) { + pr_err("%s: TDM state select failed with %d\n", + __func__, ret); + ret = -EIO; + goto err; + } + break; + case STATE_DISABLE: + if (curr_state == STATE_MI2S_ACTIVE) { + ret = pinctrl_select_state(pinctrl_info->pinctrl, + pinctrl_info->mi2s_disable); + } else { + ret = pinctrl_select_state(pinctrl_info->pinctrl, + pinctrl_info->tdm_disable); + } + if (ret) { + pr_err("%s: state disable failed with %d\n", + __func__, ret); + ret = -EIO; + goto err; + } + break; + default: + pr_err("%s: TLMM pin state is invalid\n", __func__); + return -EINVAL; + } + +err: + return ret; +} + +static void msm_release_pinctrl(struct platform_device *pdev) +{ + struct snd_soc_card *card = platform_get_drvdata(pdev); + struct msm_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card); + struct msm_pinctrl_info *pinctrl_info = &pdata->pinctrl_info; + + if (pinctrl_info->pinctrl) { + devm_pinctrl_put(pinctrl_info->pinctrl); + pinctrl_info->pinctrl = NULL; + } +} + +static int msm_get_pinctrl(struct platform_device *pdev) +{ + struct snd_soc_card *card = platform_get_drvdata(pdev); + struct msm_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card); + struct msm_pinctrl_info *pinctrl_info = NULL; + struct pinctrl *pinctrl; + int ret; + + pinctrl_info = &pdata->pinctrl_info; + + if (pinctrl_info == NULL) { + pr_err("%s: pinctrl_info is NULL\n", __func__); + return -EINVAL; + } + + pinctrl = devm_pinctrl_get(&pdev->dev); + if (IS_ERR_OR_NULL(pinctrl)) { + pr_err("%s: Unable to get pinctrl handle\n", __func__); + return -EINVAL; + } + pinctrl_info->pinctrl = pinctrl; + + /* get all the states handles from Device Tree */ + pinctrl_info->mi2s_disable = pinctrl_lookup_state(pinctrl, + "quat_mi2s_disable"); + if (IS_ERR(pinctrl_info->mi2s_disable)) { + pr_err("%s: could not get mi2s_disable pinstate\n", __func__); + goto err; + } + pinctrl_info->mi2s_active = pinctrl_lookup_state(pinctrl, + "quat_mi2s_enable"); + if (IS_ERR(pinctrl_info->mi2s_active)) { + pr_err("%s: could not get mi2s_active pinstate\n", __func__); + goto err; + } + pinctrl_info->tdm_disable = pinctrl_lookup_state(pinctrl, + "quat_tdm_disable"); + if (IS_ERR(pinctrl_info->tdm_disable)) { + pr_err("%s: could not get tdm_disable pinstate\n", __func__); + goto err; + } + pinctrl_info->tdm_active = pinctrl_lookup_state(pinctrl, + "quat_tdm_enable"); + if (IS_ERR(pinctrl_info->tdm_active)) { + pr_err("%s: could not get tdm_active pinstate\n", + __func__); + goto err; + } + /* Reset the TLMM pins to a default state */ + ret = pinctrl_select_state(pinctrl_info->pinctrl, + pinctrl_info->mi2s_disable); + if (ret != 0) { + pr_err("%s: Disable TLMM pins failed with %d\n", + __func__, ret); + ret = -EIO; + goto err; + } + pinctrl_info->curr_state = STATE_DISABLE; + atomic_set(&(pinctrl_info->pinctrl_ref_count), 0); + + return 0; + +err: + devm_pinctrl_put(pinctrl); + pinctrl_info->pinctrl = NULL; + return -EINVAL; +} + +static int msm_tdm_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + + if (cpu_dai->id == AFE_PORT_ID_QUATERNARY_TDM_RX) { + channels->min = channels->max = + tdm_rx_cfg[TDM_QUAT][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_QUAT][TDM_0].bit_format); + rate->min = rate->max = + tdm_rx_cfg[TDM_QUAT][TDM_0].sample_rate; + } else if (cpu_dai->id == AFE_PORT_ID_QUATERNARY_TDM_RX_1) { + channels->min = channels->max = + tdm_rx_cfg[TDM_QUAT][TDM_1].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_QUAT][TDM_1].bit_format); + rate->min = rate->max = + tdm_rx_cfg[TDM_QUAT][TDM_1].sample_rate; + } else if (cpu_dai->id == AFE_PORT_ID_SECONDARY_TDM_RX) { + channels->min = channels->max = + tdm_rx_cfg[TDM_SEC][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_SEC][TDM_0].bit_format); + rate->min = rate->max = tdm_rx_cfg[TDM_SEC][TDM_0].sample_rate; + } else if (cpu_dai->id == AFE_PORT_ID_QUINARY_TDM_RX) { + channels->min = channels->max = + tdm_rx_cfg[TDM_QUIN][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_QUIN][TDM_0].bit_format); + rate->min = rate->max = tdm_rx_cfg[TDM_QUIN][TDM_0].sample_rate; + } else { + pr_err("%s: dai id 0x%x not supported\n", + __func__, cpu_dai->id); + return -EINVAL; + } + + pr_debug("%s: dai id = 0x%x channels = %d rate = %d format = 0x%x\n", + __func__, cpu_dai->id, channels->max, rate->max, + params_format(params)); + + return 0; +} + +static int sm8150_tdm_snd_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + int ret = 0; + int slot_width = TDM_SLOT_WIDTH_BITS; + int channels, slots = TDM_MAX_SLOTS; + unsigned int slot_mask, rate, clk_freq; + unsigned int *slot_offset; + unsigned int path_dir = 0, interface = 0, channel_interface = 0; + + + pr_debug("%s: dai id = 0x%x\n", __func__, cpu_dai->id); + + if (cpu_dai->id < AFE_PORT_ID_TDM_PORT_RANGE_START) { + pr_err("%s: dai id 0x%x not supported\n", + __func__, cpu_dai->id); + return -EINVAL; + } + + /* RX or TX */ + path_dir = cpu_dai->id % 2; + + /* PRI, SEC, TERT, QUAT, QUIN */ + interface = (cpu_dai->id - AFE_PORT_ID_TDM_PORT_RANGE_START) + / (2 * TDM_PORT_MAX); + + /* 0, 1, 2, .. 7 */ + channel_interface = + ((cpu_dai->id - AFE_PORT_ID_TDM_PORT_RANGE_START) / 2) + % TDM_PORT_MAX; + + pr_debug("%s: interface %u, channel interface %u\n", __func__, + interface, channel_interface); + + slot_offset = tdm_cfg[(interface * 2) + path_dir][channel_interface] + .tdm_slot_offset; + + if (path_dir) + channels = tdm_tx_cfg[interface][channel_interface].channels; + else + channels = tdm_rx_cfg[interface][channel_interface].channels; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + /*2 slot config - bits 0 and 1 set for the first two slots */ + slot_mask = 0x0000FFFF >> (16-slots); + + pr_debug("%s: tdm rx slot_width %d slots %d slot_mask %x\n", + __func__, slot_width, slots, slot_mask); + + ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0, slot_mask, + slots, slot_width); + if (ret < 0) { + pr_err("%s: failed to set tdm rx slot, err:%d\n", + __func__, ret); + goto end; + } + + pr_debug("%s: tdm rx slot_offset[0]: %d, slot_offset[1]: %d\n", + __func__, slot_offset[0], slot_offset[1]); + + ret = snd_soc_dai_set_channel_map(cpu_dai, + 0, NULL, channels, slot_offset); + if (ret < 0) { + pr_err("%s: failed to set tdm rx channel map, err:%d\n", + __func__, ret); + goto end; + } + } else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + /*2 slot config - bits 0 and 1 set for the first two slots */ + slot_mask = 0x0000FFFF >> (16-slots); + + pr_debug("%s: tdm tx slot_width %d slots %d\n", + __func__, slot_width, slots); + + ret = snd_soc_dai_set_tdm_slot(cpu_dai, slot_mask, 0, + slots, slot_width); + if (ret < 0) { + pr_err("%s: failed to set tdm tx slot, err:%d\n", + __func__, ret); + goto end; + } + + ret = snd_soc_dai_set_channel_map(cpu_dai, + channels, slot_offset, 0, NULL); + if (ret < 0) { + pr_err("%s: failed to set tdm tx channel map, err:%d\n", + __func__, ret); + goto end; + } + } else { + ret = -EINVAL; + pr_err("%s: invalid use case, err:%d\n", + __func__, ret); + goto end; + } + + rate = params_rate(params); + clk_freq = rate * slot_width * slots; + ret = snd_soc_dai_set_sysclk(cpu_dai, 0, clk_freq, SND_SOC_CLOCK_OUT); + if (ret < 0) + pr_err("%s: failed to set tdm clk, err:%d\n", + __func__, ret); + +end: + return ret; +} + +static int sm8150_tdm_snd_startup(struct snd_pcm_substream *substream) +{ + int ret = 0; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_card *card = rtd->card; + struct msm_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card); + struct msm_pinctrl_info *pinctrl_info = &pdata->pinctrl_info; + + /* currently only supporting TDM_RX_0/TDM_RX_1 and TDM_TX_0 */ + if ((cpu_dai->id == AFE_PORT_ID_QUATERNARY_TDM_RX) || + (cpu_dai->id == AFE_PORT_ID_QUATERNARY_TDM_TX) || + (cpu_dai->id == AFE_PORT_ID_QUATERNARY_TDM_RX_1)) { + if (atomic_read(&(pinctrl_info->pinctrl_ref_count)) == 0) { + ret = msm_set_pinctrl(pinctrl_info, STATE_TDM_ACTIVE); + if (ret) + pr_err("%s: TDM TLMM pinctrl set failed with %d\n", + __func__, ret); + } + atomic_inc(&(pinctrl_info->pinctrl_ref_count)); + } + + return ret; +} + +static void sm8150_tdm_snd_shutdown(struct snd_pcm_substream *substream) +{ + int ret = 0; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_card *card = rtd->card; + struct msm_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card); + struct msm_pinctrl_info *pinctrl_info = &pdata->pinctrl_info; + + /* currently only supporting TDM_RX_0/TDM_RX_1 and TDM_TX_0 */ + if ((cpu_dai->id == AFE_PORT_ID_QUATERNARY_TDM_RX) || + (cpu_dai->id == AFE_PORT_ID_QUATERNARY_TDM_TX) || + (cpu_dai->id == AFE_PORT_ID_QUATERNARY_TDM_RX_1)) { + atomic_dec(&(pinctrl_info->pinctrl_ref_count)); + if (atomic_read(&(pinctrl_info->pinctrl_ref_count)) == 0) { + ret = msm_set_pinctrl(pinctrl_info, STATE_DISABLE); + if (ret) + pr_err("%s: TDM TLMM pinctrl set failed with %d\n", + __func__, ret); + } + } +} + +static struct snd_soc_ops sm8150_tdm_be_ops = { + .hw_params = sm8150_tdm_snd_hw_params, + .startup = sm8150_tdm_snd_startup, + .shutdown = sm8150_tdm_snd_shutdown +}; + +static int msm_fe_qos_prepare(struct snd_pcm_substream *substream) +{ + cpumask_t mask; + + if (pm_qos_request_active(&substream->latency_pm_qos_req)) + pm_qos_remove_request(&substream->latency_pm_qos_req); + + cpumask_clear(&mask); + cpumask_set_cpu(1, &mask); /* affine to core 1 */ + cpumask_set_cpu(2, &mask); /* affine to core 2 */ + cpumask_copy(&substream->latency_pm_qos_req.cpus_affine, &mask); + + substream->latency_pm_qos_req.type = PM_QOS_REQ_AFFINE_CORES; + + pm_qos_add_request(&substream->latency_pm_qos_req, + PM_QOS_CPU_DMA_LATENCY, + MSM_LL_QOS_VALUE); + return 0; +} + +static struct snd_soc_ops msm_fe_qos_ops = { + .prepare = msm_fe_qos_prepare, +}; + +static int msm_mi2s_snd_startup(struct snd_pcm_substream *substream) +{ + int ret = 0; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + int index = cpu_dai->id; + unsigned int fmt = SND_SOC_DAIFMT_CBS_CFS; + struct snd_soc_card *card = rtd->card; + struct msm_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card); + struct msm_pinctrl_info *pinctrl_info = &pdata->pinctrl_info; + int ret_pinctrl = 0; + + dev_dbg(rtd->card->dev, + "%s: substream = %s stream = %d, dai name %s, dai ID %d\n", + __func__, substream->name, substream->stream, + cpu_dai->name, cpu_dai->id); + + if (index < PRIM_MI2S || index >= MI2S_MAX) { + ret = -EINVAL; + dev_err(rtd->card->dev, + "%s: CPU DAI id (%d) out of range\n", + __func__, cpu_dai->id); + goto err; + } + /* + * Mutex protection in case the same MI2S + * interface using for both TX and RX so + * that the same clock won't be enable twice. + */ + mutex_lock(&mi2s_intf_conf[index].lock); + if (++mi2s_intf_conf[index].ref_cnt == 1) { + /* Check if msm needs to provide the clock to the interface */ + if (!mi2s_intf_conf[index].msm_is_mi2s_master) { + mi2s_clk[index].clk_id = mi2s_ebit_clk[index]; + fmt = SND_SOC_DAIFMT_CBM_CFM; + } + ret = msm_mi2s_set_sclk(substream, true); + if (ret < 0) { + dev_err(rtd->card->dev, + "%s: afe lpass clock failed to enable MI2S clock, err:%d\n", + __func__, ret); + goto clean_up; + } + + ret = snd_soc_dai_set_fmt(cpu_dai, fmt); + if (ret < 0) { + pr_err("%s: set fmt cpu dai failed for MI2S (%d), err:%d\n", + __func__, index, ret); + goto clk_off; + } + if (index == QUAT_MI2S) { + ret_pinctrl = msm_set_pinctrl(pinctrl_info, + STATE_MI2S_ACTIVE); + if (ret_pinctrl) + pr_err("%s: MI2S TLMM pinctrl set failed with %d\n", + __func__, ret_pinctrl); + } + } +clk_off: + if (ret < 0) + msm_mi2s_set_sclk(substream, false); +clean_up: + if (ret < 0) + mi2s_intf_conf[index].ref_cnt--; + mutex_unlock(&mi2s_intf_conf[index].lock); +err: + return ret; +} + +static void msm_mi2s_snd_shutdown(struct snd_pcm_substream *substream) +{ + int ret; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + int index = rtd->cpu_dai->id; + struct snd_soc_card *card = rtd->card; + struct msm_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card); + struct msm_pinctrl_info *pinctrl_info = &pdata->pinctrl_info; + int ret_pinctrl = 0; + + pr_debug("%s(): substream = %s stream = %d\n", __func__, + substream->name, substream->stream); + if (index < PRIM_MI2S || index >= MI2S_MAX) { + pr_err("%s:invalid MI2S DAI(%d)\n", __func__, index); + return; + } + + mutex_lock(&mi2s_intf_conf[index].lock); + if (--mi2s_intf_conf[index].ref_cnt == 0) { + ret = msm_mi2s_set_sclk(substream, false); + if (ret < 0) + pr_err("%s:clock disable failed for MI2S (%d); ret=%d\n", + __func__, index, ret); + if (index == QUAT_MI2S) { + ret_pinctrl = msm_set_pinctrl(pinctrl_info, + STATE_DISABLE); + if (ret_pinctrl) + pr_err("%s: MI2S TLMM pinctrl set failed with %d\n", + __func__, ret_pinctrl); + } + } + mutex_unlock(&mi2s_intf_conf[index].lock); +} + +static struct snd_soc_ops msm_mi2s_be_ops = { + .startup = msm_mi2s_snd_startup, + .shutdown = msm_mi2s_snd_shutdown, +}; + +static struct snd_soc_ops msm_be_ops = { + .hw_params = msm_snd_hw_params, +}; + +static struct snd_soc_ops msm_slimbus_2_be_ops = { + .hw_params = msm_slimbus_2_hw_params, +}; + +static struct snd_soc_ops msm_wcn_ops = { + .hw_params = msm_wcn_hw_params, +}; + + +/* Digital audio interface glue - connects codec <---> CPU */ +static struct snd_soc_dai_link msm_common_dai_links[] = { + /* FrontEnd DAI Links */ + { + .name = MSM_DAILINK_NAME(Media1), + .stream_name = "MultiMedia1", + .cpu_dai_name = "MultiMedia1", + .platform_name = "msm-pcm-dsp.0", + .dynamic = 1, + .async_ops = ASYNC_DPCM_SND_SOC_PREPARE, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .id = MSM_FRONTEND_DAI_MULTIMEDIA1 + }, + { + .name = MSM_DAILINK_NAME(Media2), + .stream_name = "MultiMedia2", + .cpu_dai_name = "MultiMedia2", + .platform_name = "msm-pcm-dsp.0", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .id = MSM_FRONTEND_DAI_MULTIMEDIA2, + }, + { + .name = "VoiceMMode1", + .stream_name = "VoiceMMode1", + .cpu_dai_name = "VoiceMMode1", + .platform_name = "msm-pcm-voice", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 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", + .id = MSM_FRONTEND_DAI_VOICEMMODE1, + }, + { + .name = "MSM VoIP", + .stream_name = "VoIP", + .cpu_dai_name = "VoIP", + .platform_name = "msm-voip-dsp", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .id = MSM_FRONTEND_DAI_VOIP, + }, + { + .name = MSM_DAILINK_NAME(ULL), + .stream_name = "MultiMedia3", + .cpu_dai_name = "MultiMedia3", + .platform_name = "msm-pcm-dsp.2", + .dynamic = 1, + .async_ops = ASYNC_DPCM_SND_SOC_PREPARE, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .id = MSM_FRONTEND_DAI_MULTIMEDIA3, + }, + /* Hostless PCM purpose */ + { + .name = "SLIMBUS_0 Hostless", + .stream_name = "SLIMBUS_0 Hostless", + .cpu_dai_name = "SLIMBUS0_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + /* this dailink has playback support */ + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + { + .name = "MSM AFE-PCM RX", + .stream_name = "AFE-PROXY RX", + .cpu_dai_name = "msm-dai-q6-dev.241", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .platform_name = "msm-pcm-afe", + .dpcm_playback = 1, + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + }, + { + .name = "MSM AFE-PCM TX", + .stream_name = "AFE-PROXY TX", + .cpu_dai_name = "msm-dai-q6-dev.240", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .platform_name = "msm-pcm-afe", + .dpcm_capture = 1, + .ignore_suspend = 1, + }, + { + .name = MSM_DAILINK_NAME(Compress1), + .stream_name = "Compress1", + .cpu_dai_name = "MultiMedia4", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .async_ops = ASYNC_DPCM_SND_SOC_HW_PARAMS, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dainlink has playback support */ + .id = MSM_FRONTEND_DAI_MULTIMEDIA4, + }, + { + .name = "AUXPCM Hostless", + .stream_name = "AUXPCM Hostless", + .cpu_dai_name = "AUXPCM_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + { + .name = "SLIMBUS_1 Hostless", + .stream_name = "SLIMBUS_1 Hostless", + .cpu_dai_name = "SLIMBUS1_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + /* this dailink has playback support */ + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + { + .name = "SLIMBUS_3 Hostless", + .stream_name = "SLIMBUS_3 Hostless", + .cpu_dai_name = "SLIMBUS3_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + /* this dailink has playback support */ + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + { + .name = "SLIMBUS_4 Hostless", + .stream_name = "SLIMBUS_4 Hostless", + .cpu_dai_name = "SLIMBUS4_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + /* this dailink has playback support */ + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + { + .name = MSM_DAILINK_NAME(LowLatency), + .stream_name = "MultiMedia5", + .cpu_dai_name = "MultiMedia5", + .platform_name = "msm-pcm-dsp.1", + .dynamic = 1, + .async_ops = ASYNC_DPCM_SND_SOC_PREPARE, + .dpcm_playback = 1, + .dpcm_capture = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .id = MSM_FRONTEND_DAI_MULTIMEDIA5, + .ops = &msm_fe_qos_ops, + }, + { + .name = "Listen 1 Audio Service", + .stream_name = "Listen 1 Audio Service", + .cpu_dai_name = "LSM1", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = { SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST }, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .id = MSM_FRONTEND_DAI_LSM1, + }, + /* Multiple Tunnel instances */ + { + .name = MSM_DAILINK_NAME(Compress2), + .stream_name = "Compress2", + .cpu_dai_name = "MultiMedia7", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dainlink has playback support */ + .id = MSM_FRONTEND_DAI_MULTIMEDIA7, + }, + { + .name = MSM_DAILINK_NAME(MultiMedia10), + .stream_name = "MultiMedia10", + .cpu_dai_name = "MultiMedia10", + .platform_name = "msm-pcm-dsp.1", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dainlink has playback support */ + .id = MSM_FRONTEND_DAI_MULTIMEDIA10, + }, + { + .name = MSM_DAILINK_NAME(ULL_NOIRQ), + .stream_name = "MM_NOIRQ", + .cpu_dai_name = "MultiMedia8", + .platform_name = "msm-pcm-dsp-noirq", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dainlink has playback support */ + .id = MSM_FRONTEND_DAI_MULTIMEDIA8, + .ops = &msm_fe_qos_ops, + }, + /* HDMI Hostless */ + { + .name = "HDMI_RX_HOSTLESS", + .stream_name = "HDMI_RX_HOSTLESS", + .cpu_dai_name = "HDMI_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .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", + }, + { + .name = "VoiceMMode2", + .stream_name = "VoiceMMode2", + .cpu_dai_name = "VoiceMMode2", + .platform_name = "msm-pcm-voice", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 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", + .id = MSM_FRONTEND_DAI_VOICEMMODE2, + }, + /* LSM FE */ + { + .name = "Listen 2 Audio Service", + .stream_name = "Listen 2 Audio Service", + .cpu_dai_name = "LSM2", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = { SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST }, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .id = MSM_FRONTEND_DAI_LSM2, + }, + { + .name = "Listen 3 Audio Service", + .stream_name = "Listen 3 Audio Service", + .cpu_dai_name = "LSM3", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = { SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST }, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .id = MSM_FRONTEND_DAI_LSM3, + }, + { + .name = "Listen 4 Audio Service", + .stream_name = "Listen 4 Audio Service", + .cpu_dai_name = "LSM4", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = { SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST }, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .id = MSM_FRONTEND_DAI_LSM4, + }, + { + .name = "Listen 5 Audio Service", + .stream_name = "Listen 5 Audio Service", + .cpu_dai_name = "LSM5", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = { SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST }, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .id = MSM_FRONTEND_DAI_LSM5, + }, + { + .name = "Listen 6 Audio Service", + .stream_name = "Listen 6 Audio Service", + .cpu_dai_name = "LSM6", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = { SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST }, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .id = MSM_FRONTEND_DAI_LSM6, + }, + { + .name = "Listen 7 Audio Service", + .stream_name = "Listen 7 Audio Service", + .cpu_dai_name = "LSM7", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = { SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST }, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .id = MSM_FRONTEND_DAI_LSM7, + }, + { + .name = "Listen 8 Audio Service", + .stream_name = "Listen 8 Audio Service", + .cpu_dai_name = "LSM8", + .platform_name = "msm-lsm-client", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = { SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST }, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .id = MSM_FRONTEND_DAI_LSM8, + }, + { + .name = MSM_DAILINK_NAME(Media9), + .stream_name = "MultiMedia9", + .cpu_dai_name = "MultiMedia9", + .platform_name = "msm-pcm-dsp.0", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .id = MSM_FRONTEND_DAI_MULTIMEDIA9, + }, + { + .name = MSM_DAILINK_NAME(Compress4), + .stream_name = "Compress4", + .cpu_dai_name = "MultiMedia11", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dainlink has playback support */ + .id = MSM_FRONTEND_DAI_MULTIMEDIA11, + }, + { + .name = MSM_DAILINK_NAME(Compress5), + .stream_name = "Compress5", + .cpu_dai_name = "MultiMedia12", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dainlink has playback support */ + .id = MSM_FRONTEND_DAI_MULTIMEDIA12, + }, + { + .name = MSM_DAILINK_NAME(Compress6), + .stream_name = "Compress6", + .cpu_dai_name = "MultiMedia13", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dainlink has playback support */ + .id = MSM_FRONTEND_DAI_MULTIMEDIA13, + }, + { + .name = MSM_DAILINK_NAME(Compress7), + .stream_name = "Compress7", + .cpu_dai_name = "MultiMedia14", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dainlink has playback support */ + .id = MSM_FRONTEND_DAI_MULTIMEDIA14, + }, + { + .name = MSM_DAILINK_NAME(Compress8), + .stream_name = "Compress8", + .cpu_dai_name = "MultiMedia15", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dainlink has playback support */ + .id = MSM_FRONTEND_DAI_MULTIMEDIA15, + }, + { + .name = MSM_DAILINK_NAME(ULL_NOIRQ_2), + .stream_name = "MM_NOIRQ_2", + .cpu_dai_name = "MultiMedia16", + .platform_name = "msm-pcm-dsp-noirq", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + /* this dainlink has playback support */ + .id = MSM_FRONTEND_DAI_MULTIMEDIA16, + }, + { + .name = "SLIMBUS_8 Hostless", + .stream_name = "SLIMBUS8_HOSTLESS Capture", + .cpu_dai_name = "SLIMBUS8_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, +}; + +static struct snd_soc_dai_link msm_pahu_fe_dai_links[] = { + { + .name = LPASS_BE_SLIMBUS_4_TX, + .stream_name = "Slimbus4 Capture", + .cpu_dai_name = "msm-dai-q6-dev.16393", + .platform_name = "msm-pcm-hostless", + .codec_name = "pahu_codec", + .codec_dai_name = "pahu_vifeedback", + .id = MSM_BACKEND_DAI_SLIMBUS_4_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_be_ops, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + }, + /* Ultrasound RX DAI Link */ + { + .name = "SLIMBUS_2 Hostless Playback", + .stream_name = "SLIMBUS_2 Hostless Playback", + .cpu_dai_name = "msm-dai-q6-dev.16388", + .platform_name = "msm-pcm-hostless", + .codec_name = "pahu_codec", + .codec_dai_name = "pahu_rx2", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ops = &msm_slimbus_2_be_ops, + }, + /* Ultrasound TX DAI Link */ + { + .name = "SLIMBUS_2 Hostless Capture", + .stream_name = "SLIMBUS_2 Hostless Capture", + .cpu_dai_name = "msm-dai-q6-dev.16389", + .platform_name = "msm-pcm-hostless", + .codec_name = "pahu_codec", + .codec_dai_name = "pahu_tx2", + .ignore_suspend = 1, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ops = &msm_slimbus_2_be_ops, + }, +}; + +static struct snd_soc_dai_link msm_tavil_fe_dai_links[] = { + { + .name = LPASS_BE_SLIMBUS_4_TX, + .stream_name = "Slimbus4 Capture", + .cpu_dai_name = "msm-dai-q6-dev.16393", + .platform_name = "msm-pcm-hostless", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_vifeedback", + .id = MSM_BACKEND_DAI_SLIMBUS_4_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_be_ops, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + }, + /* Ultrasound RX DAI Link */ + { + .name = "SLIMBUS_2 Hostless Playback", + .stream_name = "SLIMBUS_2 Hostless Playback", + .cpu_dai_name = "msm-dai-q6-dev.16388", + .platform_name = "msm-pcm-hostless", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_rx2", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ops = &msm_slimbus_2_be_ops, + }, + /* Ultrasound TX DAI Link */ + { + .name = "SLIMBUS_2 Hostless Capture", + .stream_name = "SLIMBUS_2 Hostless Capture", + .cpu_dai_name = "msm-dai-q6-dev.16389", + .platform_name = "msm-pcm-hostless", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_tx2", + .ignore_suspend = 1, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ops = &msm_slimbus_2_be_ops, + }, +}; + +static struct snd_soc_dai_link msm_common_misc_fe_dai_links[] = { + { + .name = MSM_DAILINK_NAME(ASM Loopback), + .stream_name = "MultiMedia6", + .cpu_dai_name = "MultiMedia6", + .platform_name = "msm-pcm-loopback", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .ignore_suspend = 1, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_pmdown_time = 1, + .id = MSM_FRONTEND_DAI_MULTIMEDIA6, + }, + { + .name = "USB Audio Hostless", + .stream_name = "USB Audio Hostless", + .cpu_dai_name = "USBAUDIO_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 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", + }, + { + .name = "SLIMBUS_7 Hostless", + .stream_name = "SLIMBUS_7 Hostless", + .cpu_dai_name = "SLIMBUS7_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_capture = 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", + }, + { + .name = "Compress Capture", + .stream_name = "Compress9", + .cpu_dai_name = "MultiMedia17", + .platform_name = "msm-compress-dsp", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .id = MSM_FRONTEND_DAI_MULTIMEDIA17, + }, + { + .name = "MultiMedia30 Playback", + .stream_name = "MultiMedia30", + .cpu_dai_name = "MultiMedia30", + .platform_name = "msm-pcm-dsp.1", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .id = MSM_FRONTEND_DAI_MULTIMEDIA30, + }, + { + .name = "MultiMedia31 Playback", + .stream_name = "MultiMedia31", + .cpu_dai_name = "MultiMedia31", + .platform_name = "msm-pcm-dsp.1", + .dynamic = 1, + .dpcm_playback = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .id = MSM_FRONTEND_DAI_MULTIMEDIA31, + }, +}; + +static struct snd_soc_dai_link msm_common_be_dai_links[] = { + /* Backend AFE DAI Links */ + { + .name = LPASS_BE_AFE_PCM_RX, + .stream_name = "AFE Playback", + .cpu_dai_name = "msm-dai-q6-dev.224", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_AFE_PCM_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_AFE_PCM_TX, + .stream_name = "AFE Capture", + .cpu_dai_name = "msm-dai-q6-dev.225", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_AFE_PCM_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + }, + /* Incall Record Uplink BACK END DAI Link */ + { + .name = LPASS_BE_INCALL_RECORD_TX, + .stream_name = "Voice Uplink Capture", + .cpu_dai_name = "msm-dai-q6-dev.32772", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_INCALL_RECORD_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + }, + /* Incall Record Downlink BACK END DAI Link */ + { + .name = LPASS_BE_INCALL_RECORD_RX, + .stream_name = "Voice Downlink Capture", + .cpu_dai_name = "msm-dai-q6-dev.32771", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_INCALL_RECORD_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + }, + /* Incall Music BACK END DAI Link */ + { + .name = LPASS_BE_VOICE_PLAYBACK_TX, + .stream_name = "Voice Farend Playback", + .cpu_dai_name = "msm-dai-q6-dev.32773", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_VOICE_PLAYBACK_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + }, + /* Incall Music 2 BACK END DAI Link */ + { + .name = LPASS_BE_VOICE2_PLAYBACK_TX, + .stream_name = "Voice2 Farend Playback", + .cpu_dai_name = "msm-dai-q6-dev.32770", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_VOICE2_PLAYBACK_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + }, + { + .name = LPASS_BE_USB_AUDIO_RX, + .stream_name = "USB Audio Playback", + .cpu_dai_name = "msm-dai-q6-dev.28672", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_USB_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_USB_AUDIO_TX, + .stream_name = "USB Audio Capture", + .cpu_dai_name = "msm-dai-q6-dev.28673", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_USB_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_PRI_TDM_RX_0, + .stream_name = "Primary TDM0 Playback", + .cpu_dai_name = "msm-dai-q6-tdm.36864", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_PRI_TDM_RX_0, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &sm8150_tdm_be_ops, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + }, + { + .name = LPASS_BE_PRI_TDM_TX_0, + .stream_name = "Primary TDM0 Capture", + .cpu_dai_name = "msm-dai-q6-tdm.36865", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_PRI_TDM_TX_0, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &sm8150_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SEC_TDM_RX_0, + .stream_name = "Secondary TDM0 Playback", + .cpu_dai_name = "msm-dai-q6-tdm.36880", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SEC_TDM_RX_0, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &sm8150_tdm_be_ops, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + }, + { + .name = LPASS_BE_SEC_TDM_TX_0, + .stream_name = "Secondary TDM0 Capture", + .cpu_dai_name = "msm-dai-q6-tdm.36881", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_SEC_TDM_TX_0, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &sm8150_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_TERT_TDM_RX_0, + .stream_name = "Tertiary TDM0 Playback", + .cpu_dai_name = "msm-dai-q6-tdm.36896", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_TERT_TDM_RX_0, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &sm8150_tdm_be_ops, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + }, + { + .name = LPASS_BE_TERT_TDM_TX_0, + .stream_name = "Tertiary TDM0 Capture", + .cpu_dai_name = "msm-dai-q6-tdm.36897", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_TERT_TDM_TX_0, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &sm8150_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_QUAT_TDM_RX_0, + .stream_name = "Quaternary TDM0 Playback", + .cpu_dai_name = "msm-dai-q6-tdm.36912", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_QUAT_TDM_RX_0, + .be_hw_params_fixup = msm_tdm_be_hw_params_fixup, + .ops = &sm8150_tdm_be_ops, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + }, + { + .name = LPASS_BE_QUAT_TDM_TX_0, + .stream_name = "Quaternary TDM0 Capture", + .cpu_dai_name = "msm-dai-q6-tdm.36913", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_QUAT_TDM_TX_0, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &sm8150_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_QUAT_TDM_RX_1, + .stream_name = "Quaternary TDM1 Playback", + .cpu_dai_name = "msm-dai-q6-tdm.36914", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_QUAT_TDM_RX_1, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &sm8150_tdm_be_ops, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + }, +}; + +static struct snd_soc_dai_link msm_pahu_be_dai_links[] = { + { + .name = LPASS_BE_SLIMBUS_0_RX, + .stream_name = "Slimbus Playback", + .cpu_dai_name = "msm-dai-q6-dev.16384", + .platform_name = "msm-pcm-routing", + .codec_name = "pahu_codec", + .codec_dai_name = "pahu_rx1", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_0_RX, + .init = &msm_audrx_init, + .be_hw_params_fixup = msm_be_hw_params_fixup, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + .ops = &msm_be_ops, + }, + { + .name = LPASS_BE_SLIMBUS_0_TX, + .stream_name = "Slimbus Capture", + .cpu_dai_name = "msm-dai-q6-dev.16385", + .platform_name = "msm-pcm-routing", + .codec_name = "pahu_codec", + .codec_dai_name = "pahu_tx1", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_0_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + .ops = &msm_be_ops, + }, + { + .name = LPASS_BE_SLIMBUS_1_RX, + .stream_name = "Slimbus1 Playback", + .cpu_dai_name = "msm-dai-q6-dev.16386", + .platform_name = "msm-pcm-routing", + .codec_name = "pahu_codec", + .codec_dai_name = "pahu_rx1", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_1_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_be_ops, + /* dai link has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_1_TX, + .stream_name = "Slimbus1 Capture", + .cpu_dai_name = "msm-dai-q6-dev.16387", + .platform_name = "msm-pcm-routing", + .codec_name = "pahu_codec", + .codec_dai_name = "pahu_tx3", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_1_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_2_RX, + .stream_name = "Slimbus2 Playback", + .cpu_dai_name = "msm-dai-q6-dev.16388", + .platform_name = "msm-pcm-routing", + .codec_name = "pahu_codec", + .codec_dai_name = "pahu_rx2", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_2_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_be_ops, + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_3_RX, + .stream_name = "Slimbus3 Playback", + .cpu_dai_name = "msm-dai-q6-dev.16390", + .platform_name = "msm-pcm-routing", + .codec_name = "pahu_codec", + .codec_dai_name = "pahu_rx1", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_3_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_be_ops, + /* dai link has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_3_TX, + .stream_name = "Slimbus3 Capture", + .cpu_dai_name = "msm-dai-q6-dev.16391", + .platform_name = "msm-pcm-routing", + .codec_name = "pahu_codec", + .codec_dai_name = "pahu_tx1", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_3_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_4_RX, + .stream_name = "Slimbus4 Playback", + .cpu_dai_name = "msm-dai-q6-dev.16392", + .platform_name = "msm-pcm-routing", + .codec_name = "pahu_codec", + .codec_dai_name = "pahu_rx1", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_4_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_be_ops, + /* dai link has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_5_RX, + .stream_name = "Slimbus5 Playback", + .cpu_dai_name = "msm-dai-q6-dev.16394", + .platform_name = "msm-pcm-routing", + .codec_name = "pahu_codec", + .codec_dai_name = "pahu_rx3", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_5_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_be_ops, + /* dai link has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + /* MAD BE */ + { + .name = LPASS_BE_SLIMBUS_5_TX, + .stream_name = "Slimbus5 Capture", + .cpu_dai_name = "msm-dai-q6-dev.16395", + .platform_name = "msm-pcm-routing", + .codec_name = "pahu_codec", + .codec_dai_name = "pahu_mad1", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_5_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_6_RX, + .stream_name = "Slimbus6 Playback", + .cpu_dai_name = "msm-dai-q6-dev.16396", + .platform_name = "msm-pcm-routing", + .codec_name = "pahu_codec", + .codec_dai_name = "pahu_rx4", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_6_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_be_ops, + /* dai link has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + /* Slimbus VI Recording */ + { + .name = LPASS_BE_SLIMBUS_TX_VI, + .stream_name = "Slimbus4 Capture", + .cpu_dai_name = "msm-dai-q6-dev.16393", + .platform_name = "msm-pcm-routing", + .codec_name = "pahu_codec", + .codec_dai_name = "pahu_vifeedback", + .id = MSM_BACKEND_DAI_SLIMBUS_4_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_be_ops, + .ignore_suspend = 1, + .no_pcm = 1, + .dpcm_capture = 1, + }, +}; + +static struct snd_soc_dai_link msm_tavil_be_dai_links[] = { + { + .name = LPASS_BE_SLIMBUS_0_RX, + .stream_name = "Slimbus Playback", + .cpu_dai_name = "msm-dai-q6-dev.16384", + .platform_name = "msm-pcm-routing", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_rx1", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_0_RX, + .init = &msm_audrx_init, + .be_hw_params_fixup = msm_be_hw_params_fixup, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + .ops = &msm_be_ops, + }, + { + .name = LPASS_BE_SLIMBUS_0_TX, + .stream_name = "Slimbus Capture", + .cpu_dai_name = "msm-dai-q6-dev.16385", + .platform_name = "msm-pcm-routing", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_tx1", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_0_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + .ops = &msm_be_ops, + }, + { + .name = LPASS_BE_SLIMBUS_1_RX, + .stream_name = "Slimbus1 Playback", + .cpu_dai_name = "msm-dai-q6-dev.16386", + .platform_name = "msm-pcm-routing", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_rx1", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_1_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_be_ops, + /* dai link has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_1_TX, + .stream_name = "Slimbus1 Capture", + .cpu_dai_name = "msm-dai-q6-dev.16387", + .platform_name = "msm-pcm-routing", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_tx3", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_1_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_2_RX, + .stream_name = "Slimbus2 Playback", + .cpu_dai_name = "msm-dai-q6-dev.16388", + .platform_name = "msm-pcm-routing", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_rx2", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_2_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_be_ops, + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_3_RX, + .stream_name = "Slimbus3 Playback", + .cpu_dai_name = "msm-dai-q6-dev.16390", + .platform_name = "msm-pcm-routing", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_rx1", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_3_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_be_ops, + /* dai link has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_3_TX, + .stream_name = "Slimbus3 Capture", + .cpu_dai_name = "msm-dai-q6-dev.16391", + .platform_name = "msm-pcm-routing", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_tx1", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_3_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_4_RX, + .stream_name = "Slimbus4 Playback", + .cpu_dai_name = "msm-dai-q6-dev.16392", + .platform_name = "msm-pcm-routing", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_rx1", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_4_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_be_ops, + /* dai link has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_5_RX, + .stream_name = "Slimbus5 Playback", + .cpu_dai_name = "msm-dai-q6-dev.16394", + .platform_name = "msm-pcm-routing", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_rx3", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_5_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_be_ops, + /* dai link has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + /* MAD BE */ + { + .name = LPASS_BE_SLIMBUS_5_TX, + .stream_name = "Slimbus5 Capture", + .cpu_dai_name = "msm-dai-q6-dev.16395", + .platform_name = "msm-pcm-routing", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_mad1", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_5_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_6_RX, + .stream_name = "Slimbus6 Playback", + .cpu_dai_name = "msm-dai-q6-dev.16396", + .platform_name = "msm-pcm-routing", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_rx4", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_6_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_be_ops, + /* dai link has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + /* Slimbus VI Recording */ + { + .name = LPASS_BE_SLIMBUS_TX_VI, + .stream_name = "Slimbus4 Capture", + .cpu_dai_name = "msm-dai-q6-dev.16393", + .platform_name = "msm-pcm-routing", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_vifeedback", + .id = MSM_BACKEND_DAI_SLIMBUS_4_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_be_ops, + .ignore_suspend = 1, + .no_pcm = 1, + .dpcm_capture = 1, + }, +}; + +static struct snd_soc_dai_link msm_wcn_be_dai_links[] = { + { + .name = LPASS_BE_SLIMBUS_7_RX, + .stream_name = "Slimbus7 Playback", + .cpu_dai_name = "msm-dai-q6-dev.16398", + .platform_name = "msm-pcm-routing", + .codec_name = "btfmslim_slave", + /* BT codec driver determines capabilities based on + * dai name, bt codecdai name should always contains + * supported usecase information + */ + .codec_dai_name = "btfm_bt_sco_a2dp_slim_rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_7_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_wcn_ops, + /* dai link has playback support */ + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_7_TX, + .stream_name = "Slimbus7 Capture", + .cpu_dai_name = "msm-dai-q6-dev.16399", + .platform_name = "msm-pcm-routing", + .codec_name = "btfmslim_slave", + .codec_dai_name = "btfm_bt_sco_slim_tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_7_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_wcn_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SLIMBUS_8_TX, + .stream_name = "Slimbus8 Capture", + .cpu_dai_name = "msm-dai-q6-dev.16401", + .platform_name = "msm-pcm-routing", + .codec_name = "btfmslim_slave", + .codec_dai_name = "btfm_fm_slim_tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_8_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .init = &msm_wcn_init, + .ops = &msm_wcn_ops, + .ignore_suspend = 1, + }, +}; + +static struct snd_soc_dai_link ext_disp_be_dai_link[] = { + /* DISP PORT BACK END DAI Link */ + { + .name = LPASS_BE_DISPLAY_PORT, + .stream_name = "Display Port Playback", + .cpu_dai_name = "msm-dai-q6-dp.24608", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-ext-disp-audio-codec-rx", + .codec_dai_name = "msm_dp_audio_codec_rx_dai", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_DISPLAY_PORT_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + /* DISP PORT 1 BACK END DAI Link */ + { + .name = LPASS_BE_DISPLAY_PORT1, + .stream_name = "Display Port1 Playback", + .cpu_dai_name = "msm-dai-q6-dp.24608", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-ext-disp-audio-codec-rx", + .codec_dai_name = "msm_dp_audio_codec_rx1_dai", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_DISPLAY_PORT_RX_1, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, +}; + +static struct snd_soc_dai_link msm_mi2s_be_dai_links[] = { + { + .name = LPASS_BE_PRI_MI2S_RX, + .stream_name = "Primary MI2S Playback", + .cpu_dai_name = "msm-dai-q6-mi2s.0", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_PRI_MI2S_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + }, + { + .name = LPASS_BE_PRI_MI2S_TX, + .stream_name = "Primary MI2S Capture", + .cpu_dai_name = "msm-dai-q6-mi2s.0", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_PRI_MI2S_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SEC_MI2S_RX, + .stream_name = "Secondary MI2S Playback", + .cpu_dai_name = "msm-dai-q6-mi2s.1", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SECONDARY_MI2S_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + }, + { + .name = LPASS_BE_SEC_MI2S_TX, + .stream_name = "Secondary MI2S Capture", + .cpu_dai_name = "msm-dai-q6-mi2s.1", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_SECONDARY_MI2S_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_TERT_MI2S_RX, + .stream_name = "Tertiary MI2S Playback", + .cpu_dai_name = "msm-dai-q6-mi2s.2", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_TERTIARY_MI2S_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + }, + { + .name = LPASS_BE_TERT_MI2S_TX, + .stream_name = "Tertiary MI2S Capture", + .cpu_dai_name = "msm-dai-q6-mi2s.2", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_TERTIARY_MI2S_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + }, + /* Delta Start: (CCM0003: Integrated AW8898 SmartPA) + * 2019-04-24 houxiaofei: M0337731 + */ +#ifdef CONFIG_SND_SMARTPA_AW8898 + { + .name = LPASS_BE_QUAT_MI2S_RX, + .stream_name = "Quaternary MI2S Playback", + .cpu_dai_name = "msm-dai-q6-mi2s.3", + .platform_name = "msm-pcm-routing", + .codec_name = "aw8898_smartpa", + .codec_dai_name = "aw8898-aif", + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS, + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_QUATERNARY_MI2S_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + }, + { + .name = LPASS_BE_QUAT_MI2S_TX, + .stream_name = "Quaternary MI2S Capture", + .cpu_dai_name = "msm-dai-q6-mi2s.3", + .platform_name = "msm-pcm-routing", + .codec_name = "aw8898_smartpa", + .codec_dai_name = "aw8898-aif", + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS, + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + }, +#else + { + .name = LPASS_BE_QUAT_MI2S_RX, + .stream_name = "Quaternary MI2S Playback", + .cpu_dai_name = "msm-dai-q6-mi2s.3", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_QUATERNARY_MI2S_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + }, + { + .name = LPASS_BE_QUAT_MI2S_TX, + .stream_name = "Quaternary MI2S Capture", + .cpu_dai_name = "msm-dai-q6-mi2s.3", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + }, +#endif + /* Delta End: (CCM0003: Integrated AW8898 SmartPA) */ + { + .name = LPASS_BE_QUIN_MI2S_RX, + .stream_name = "Quinary MI2S Playback", + .cpu_dai_name = "msm-dai-q6-mi2s.4", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_QUINARY_MI2S_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + }, + { + .name = LPASS_BE_QUIN_MI2S_TX, + .stream_name = "Quinary MI2S Capture", + .cpu_dai_name = "msm-dai-q6-mi2s.4", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_QUINARY_MI2S_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + }, + +}; + +static struct snd_soc_dai_link msm_auxpcm_be_dai_links[] = { + /* Primary AUX PCM Backend DAI Links */ + { + .name = LPASS_BE_AUXPCM_RX, + .stream_name = "AUX PCM Playback", + .cpu_dai_name = "msm-dai-q6-auxpcm.1", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_AUXPCM_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_AUXPCM_TX, + .stream_name = "AUX PCM Capture", + .cpu_dai_name = "msm-dai-q6-auxpcm.1", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_AUXPCM_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + }, + /* Secondary AUX PCM Backend DAI Links */ + { + .name = LPASS_BE_SEC_AUXPCM_RX, + .stream_name = "Sec AUX PCM Playback", + .cpu_dai_name = "msm-dai-q6-auxpcm.2", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SEC_AUXPCM_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SEC_AUXPCM_TX, + .stream_name = "Sec AUX PCM Capture", + .cpu_dai_name = "msm-dai-q6-auxpcm.2", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_SEC_AUXPCM_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + }, + /* Tertiary AUX PCM Backend DAI Links */ + { + .name = LPASS_BE_TERT_AUXPCM_RX, + .stream_name = "Tert AUX PCM Playback", + .cpu_dai_name = "msm-dai-q6-auxpcm.3", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_TERT_AUXPCM_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_TERT_AUXPCM_TX, + .stream_name = "Tert AUX PCM Capture", + .cpu_dai_name = "msm-dai-q6-auxpcm.3", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_TERT_AUXPCM_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + }, + /* Quaternary AUX PCM Backend DAI Links */ + { + .name = LPASS_BE_QUAT_AUXPCM_RX, + .stream_name = "Quat AUX PCM Playback", + .cpu_dai_name = "msm-dai-q6-auxpcm.4", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_QUAT_AUXPCM_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_QUAT_AUXPCM_TX, + .stream_name = "Quat AUX PCM Capture", + .cpu_dai_name = "msm-dai-q6-auxpcm.4", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_QUAT_AUXPCM_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + }, + /* Quinary AUX PCM Backend DAI Links */ + { + .name = LPASS_BE_QUIN_AUXPCM_RX, + .stream_name = "Quin AUX PCM Playback", + .cpu_dai_name = "msm-dai-q6-auxpcm.5", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_QUIN_AUXPCM_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_QUIN_AUXPCM_TX, + .stream_name = "Quin AUX PCM Capture", + .cpu_dai_name = "msm-dai-q6-auxpcm.5", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_QUIN_AUXPCM_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + }, +}; + +static struct snd_soc_dai_link msm_pahu_snd_card_dai_links[ + ARRAY_SIZE(msm_common_dai_links) + + ARRAY_SIZE(msm_pahu_fe_dai_links) + + ARRAY_SIZE(msm_common_misc_fe_dai_links) + + ARRAY_SIZE(msm_common_be_dai_links) + + ARRAY_SIZE(msm_pahu_be_dai_links) + + ARRAY_SIZE(msm_wcn_be_dai_links) + + ARRAY_SIZE(ext_disp_be_dai_link) + + ARRAY_SIZE(msm_mi2s_be_dai_links) + + ARRAY_SIZE(msm_auxpcm_be_dai_links)]; + +static struct snd_soc_dai_link msm_tavil_dai_links[ + ARRAY_SIZE(msm_common_dai_links) + + ARRAY_SIZE(msm_tavil_fe_dai_links) + + ARRAY_SIZE(msm_common_misc_fe_dai_links) + + ARRAY_SIZE(msm_common_be_dai_links) + + ARRAY_SIZE(msm_tavil_be_dai_links) + + ARRAY_SIZE(msm_wcn_be_dai_links) + + ARRAY_SIZE(ext_disp_be_dai_link) + + ARRAY_SIZE(msm_mi2s_be_dai_links) + + ARRAY_SIZE(msm_auxpcm_be_dai_links)]; + +static int msm_snd_card_tavil_late_probe(struct snd_soc_card *card) +{ + const char *be_dl_name = LPASS_BE_SLIMBUS_0_RX; + struct snd_soc_pcm_runtime *rtd; + int ret = 0; + void *mbhc_calibration; + + rtd = snd_soc_get_pcm_runtime(card, be_dl_name); + if (!rtd) { + dev_err(card->dev, + "%s: snd_soc_get_pcm_runtime for %s failed!\n", + __func__, be_dl_name); + ret = -EINVAL; + goto err_pcm_runtime; + } + + mbhc_calibration = def_wcd_mbhc_cal(); + if (!mbhc_calibration) { + ret = -ENOMEM; + goto err_mbhc_cal; + } + wcd_mbhc_cfg.calibration = mbhc_calibration; + ret = tavil_mbhc_hs_detect(rtd->codec, &wcd_mbhc_cfg); + if (ret) { + dev_err(card->dev, "%s: mbhc hs detect failed, err:%d\n", + __func__, ret); + goto err_hs_detect; + } + return 0; + +err_hs_detect: + kfree(mbhc_calibration); +err_mbhc_cal: +err_pcm_runtime: + return ret; +} + +struct snd_soc_card snd_soc_card_pahu_msm = { + .name = "sm8150-pahu-snd-card", +}; + +struct snd_soc_card snd_soc_card_tavil_msm = { + .name = "sm8150-tavil-snd-card", + .late_probe = msm_snd_card_tavil_late_probe, +}; + +static int msm_populate_dai_link_component_of_node( + struct snd_soc_card *card) +{ + int i, index, ret = 0; + struct device *cdev = card->dev; + struct snd_soc_dai_link *dai_link = card->dai_link; + struct device_node *np; + + if (!cdev) { + pr_err("%s: Sound card device memory NULL\n", __func__); + return -ENODEV; + } + + for (i = 0; i < card->num_links; i++) { + if (dai_link[i].platform_of_node && dai_link[i].cpu_of_node) + continue; + + /* populate platform_of_node for snd card dai links */ + if (dai_link[i].platform_name && + !dai_link[i].platform_of_node) { + index = of_property_match_string(cdev->of_node, + "asoc-platform-names", + dai_link[i].platform_name); + if (index < 0) { + pr_err("%s: No match found for platform name: %s\n", + __func__, dai_link[i].platform_name); + ret = index; + goto err; + } + np = of_parse_phandle(cdev->of_node, "asoc-platform", + index); + if (!np) { + pr_err("%s: retrieving phandle for platform %s, index %d failed\n", + __func__, dai_link[i].platform_name, + index); + ret = -ENODEV; + goto err; + } + dai_link[i].platform_of_node = np; + dai_link[i].platform_name = NULL; + } + + /* populate cpu_of_node for snd card dai links */ + if (dai_link[i].cpu_dai_name && !dai_link[i].cpu_of_node) { + index = of_property_match_string(cdev->of_node, + "asoc-cpu-names", + dai_link[i].cpu_dai_name); + if (index >= 0) { + np = of_parse_phandle(cdev->of_node, "asoc-cpu", + index); + if (!np) { + pr_err("%s: retrieving phandle for cpu dai %s failed\n", + __func__, + dai_link[i].cpu_dai_name); + ret = -ENODEV; + goto err; + } + dai_link[i].cpu_of_node = np; + dai_link[i].cpu_dai_name = NULL; + } + } + + /* populate codec_of_node for snd card dai links */ + if (dai_link[i].codec_name && !dai_link[i].codec_of_node) { + index = of_property_match_string(cdev->of_node, + "asoc-codec-names", + dai_link[i].codec_name); + if (index < 0) + continue; + np = of_parse_phandle(cdev->of_node, "asoc-codec", + index); + if (!np) { + pr_err("%s: retrieving phandle for codec %s failed\n", + __func__, dai_link[i].codec_name); + ret = -ENODEV; + goto err; + } + dai_link[i].codec_of_node = np; + dai_link[i].codec_name = NULL; + } + } + +err: + return ret; +} + +static int msm_audrx_stub_init(struct snd_soc_pcm_runtime *rtd) +{ + int ret = 0; + struct snd_soc_codec *codec = rtd->codec; + + ret = snd_soc_add_codec_controls(codec, msm_snd_controls, + ARRAY_SIZE(msm_snd_controls)); + if (ret < 0) { + dev_err(codec->dev, + "%s: add_codec_controls failed, err = %d\n", + __func__, ret); + return ret; + } + + return 0; +} + +static int msm_snd_stub_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + + int ret = 0; + unsigned int rx_ch[] = {144, 145, 146, 147, 148, 149, 150, + 151}; + unsigned int tx_ch[] = {128, 129, 130, 131, 132, 133, + 134, 135, 136, 137, 138, 139, + 140, 141, 142, 143}; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + ret = snd_soc_dai_set_channel_map(cpu_dai, 0, 0, + slim_rx_cfg[SLIM_RX_0].channels, + rx_ch); + if (ret < 0) + pr_err("%s: RX failed to set cpu chan map error %d\n", + __func__, ret); + } else { + ret = snd_soc_dai_set_channel_map(cpu_dai, + slim_tx_cfg[SLIM_TX_0].channels, + tx_ch, 0, 0); + if (ret < 0) + pr_err("%s: TX failed to set cpu chan map error %d\n", + __func__, ret); + } + + return ret; +} + +static struct snd_soc_ops msm_stub_be_ops = { + .hw_params = msm_snd_stub_hw_params, +}; + +static struct snd_soc_dai_link msm_stub_fe_dai_links[] = { + + /* FrontEnd DAI Links */ + { + .name = "MSMSTUB Media1", + .stream_name = "MultiMedia1", + .cpu_dai_name = "MultiMedia1", + .platform_name = "msm-pcm-dsp.0", + .dynamic = 1, + .async_ops = ASYNC_DPCM_SND_SOC_PREPARE, + .dpcm_playback = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .ignore_suspend = 1, + /* this dainlink has playback support */ + .ignore_pmdown_time = 1, + .id = MSM_FRONTEND_DAI_MULTIMEDIA1 + }, +}; + +static struct snd_soc_dai_link msm_stub_be_dai_links[] = { + + /* Backend DAI Links */ + { + .name = LPASS_BE_SLIMBUS_0_RX, + .stream_name = "Slimbus Playback", + .cpu_dai_name = "msm-dai-q6-dev.16384", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_0_RX, + .init = &msm_audrx_stub_init, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_pmdown_time = 1, /* dai link has playback support */ + .ignore_suspend = 1, + .ops = &msm_stub_be_ops, + }, + { + .name = LPASS_BE_SLIMBUS_0_TX, + .stream_name = "Slimbus Capture", + .cpu_dai_name = "msm-dai-q6-dev.16385", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_SLIMBUS_0_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ignore_suspend = 1, + .ops = &msm_stub_be_ops, + }, +}; + +static struct snd_soc_dai_link msm_stub_dai_links[ + ARRAY_SIZE(msm_stub_fe_dai_links) + + ARRAY_SIZE(msm_stub_be_dai_links)]; + +struct snd_soc_card snd_soc_card_stub_msm = { + .name = "sm8150-stub-snd-card", +}; + +static const struct of_device_id sm8150_asoc_machine_of_match[] = { + { .compatible = "qcom,sm8150-asoc-snd-pahu", + .data = "pahu_codec"}, + { .compatible = "qcom,sm8150-asoc-snd-tavil", + .data = "tavil_codec"}, + { .compatible = "qcom,sm8150-asoc-snd-stub", + .data = "stub_codec"}, + {}, +}; + +static struct snd_soc_card *populate_snd_card_dailinks(struct device *dev) +{ + struct snd_soc_card *card = NULL; + struct snd_soc_dai_link *dailink; + int len_1, len_2, len_3, len_4; + int total_links; + const struct of_device_id *match; + int ret = 0; + u32 val = 0; + + match = of_match_node(sm8150_asoc_machine_of_match, dev->of_node); + if (!match) { + dev_err(dev, "%s: No DT match found for sound card\n", + __func__); + return NULL; + } + + if (!strcmp(match->data, "pahu_codec")) { + card = &snd_soc_card_pahu_msm; + len_1 = ARRAY_SIZE(msm_common_dai_links); + len_2 = len_1 + ARRAY_SIZE(msm_pahu_fe_dai_links); + len_3 = len_2 + ARRAY_SIZE(msm_common_misc_fe_dai_links); + len_4 = len_3 + ARRAY_SIZE(msm_common_be_dai_links); + total_links = len_4 + ARRAY_SIZE(msm_pahu_be_dai_links); + memcpy(msm_pahu_snd_card_dai_links, + msm_common_dai_links, + sizeof(msm_common_dai_links)); + memcpy(msm_pahu_snd_card_dai_links + len_1, + msm_pahu_fe_dai_links, + sizeof(msm_pahu_fe_dai_links)); + memcpy(msm_pahu_snd_card_dai_links + len_2, + msm_common_misc_fe_dai_links, + sizeof(msm_common_misc_fe_dai_links)); + memcpy(msm_pahu_snd_card_dai_links + len_3, + msm_common_be_dai_links, + sizeof(msm_common_be_dai_links)); + memcpy(msm_pahu_snd_card_dai_links + len_4, + msm_pahu_be_dai_links, + sizeof(msm_pahu_be_dai_links)); + + ret = of_property_read_u32(dev->of_node, "qcom,wcn-btfm", &val); + if (!ret && val) { + dev_dbg(dev, "%s(): WCN BTFM support present\n", + __func__); + memcpy(msm_pahu_snd_card_dai_links + total_links, + msm_wcn_be_dai_links, + sizeof(msm_wcn_be_dai_links)); + total_links += ARRAY_SIZE(msm_wcn_be_dai_links); + } + + ret = of_property_read_u32(dev->of_node, + "qcom,ext-disp-audio-rx", &val); + if (!ret && val) { + dev_dbg(dev, "%s(): ext disp audio support present\n", + __func__); + memcpy(msm_pahu_snd_card_dai_links + total_links, + ext_disp_be_dai_link, + sizeof(ext_disp_be_dai_link)); + total_links += ARRAY_SIZE(ext_disp_be_dai_link); + } + + ret = of_property_read_u32(dev->of_node, + "qcom,mi2s-audio-intf", &val); + if (!ret && val) { + memcpy(msm_pahu_snd_card_dai_links + total_links, + msm_mi2s_be_dai_links, + sizeof(msm_mi2s_be_dai_links)); + total_links += ARRAY_SIZE(msm_mi2s_be_dai_links); + } + + ret = of_property_read_u32(dev->of_node, + "qcom,auxpcm-audio-intf", &val); + if (!ret && val) { + memcpy(msm_pahu_snd_card_dai_links + total_links, + msm_auxpcm_be_dai_links, + sizeof(msm_auxpcm_be_dai_links)); + total_links += ARRAY_SIZE(msm_auxpcm_be_dai_links); + } + + dailink = msm_pahu_snd_card_dai_links; + } else if (!strcmp(match->data, "tavil_codec")) { + card = &snd_soc_card_tavil_msm; + len_1 = ARRAY_SIZE(msm_common_dai_links); + len_2 = len_1 + ARRAY_SIZE(msm_tavil_fe_dai_links); + len_3 = len_2 + ARRAY_SIZE(msm_common_misc_fe_dai_links); + len_4 = len_3 + ARRAY_SIZE(msm_common_be_dai_links); + total_links = len_4 + ARRAY_SIZE(msm_tavil_be_dai_links); + memcpy(msm_tavil_dai_links, + msm_common_dai_links, + sizeof(msm_common_dai_links)); + memcpy(msm_tavil_dai_links + len_1, + msm_tavil_fe_dai_links, + sizeof(msm_tavil_fe_dai_links)); + memcpy(msm_tavil_dai_links + len_2, + msm_common_misc_fe_dai_links, + sizeof(msm_common_misc_fe_dai_links)); + memcpy(msm_tavil_dai_links + len_3, + msm_common_be_dai_links, + sizeof(msm_common_be_dai_links)); + memcpy(msm_tavil_dai_links + len_4, + msm_tavil_be_dai_links, + sizeof(msm_tavil_be_dai_links)); + + ret = of_property_read_u32(dev->of_node, "qcom,wcn-btfm", &val); + if (!ret && val) { + dev_dbg(dev, "%s(): WCN BTFM support present\n", + __func__); + memcpy(msm_tavil_dai_links + total_links, + msm_wcn_be_dai_links, + sizeof(msm_wcn_be_dai_links)); + total_links += ARRAY_SIZE(msm_wcn_be_dai_links); + } + + ret = of_property_read_u32(dev->of_node, + "qcom,ext-disp-audio-rx", &val); + if (!ret && val) { + dev_dbg(dev, "%s(): ext disp audio support present\n", + __func__); + memcpy(msm_tavil_dai_links + total_links, + ext_disp_be_dai_link, + sizeof(ext_disp_be_dai_link)); + total_links += ARRAY_SIZE(ext_disp_be_dai_link); + } + + ret = of_property_read_u32(dev->of_node, + "qcom,mi2s-audio-intf", &val); + if (!ret && val) { + memcpy(msm_tavil_dai_links + total_links, + msm_mi2s_be_dai_links, + sizeof(msm_mi2s_be_dai_links)); + total_links += ARRAY_SIZE(msm_mi2s_be_dai_links); + } + + ret = of_property_read_u32(dev->of_node, + "qcom,auxpcm-audio-intf", &val); + if (!ret && val) { + memcpy(msm_tavil_dai_links + total_links, + msm_auxpcm_be_dai_links, + sizeof(msm_auxpcm_be_dai_links)); + total_links += ARRAY_SIZE(msm_auxpcm_be_dai_links); + } + dailink = msm_tavil_dai_links; + } else if (!strcmp(match->data, "stub_codec")) { + card = &snd_soc_card_stub_msm; + len_1 = ARRAY_SIZE(msm_stub_fe_dai_links); + len_2 = len_1 + ARRAY_SIZE(msm_stub_be_dai_links); + + memcpy(msm_stub_dai_links, + msm_stub_fe_dai_links, + sizeof(msm_stub_fe_dai_links)); + memcpy(msm_stub_dai_links + len_1, + msm_stub_be_dai_links, + sizeof(msm_stub_be_dai_links)); + + dailink = msm_stub_dai_links; + total_links = len_2; + } + + if (card) { + card->dai_link = dailink; + card->num_links = total_links; + } + + return card; +} + +static int msm_wsa881x_init(struct snd_soc_component *component) +{ + u8 spkleft_ports[WSA881X_MAX_SWR_PORTS] = {100, 101, 102, 106}; + u8 spkright_ports[WSA881X_MAX_SWR_PORTS] = {103, 104, 105, 107}; + unsigned int ch_rate[WSA881X_MAX_SWR_PORTS] = {2400, 600, 300, 1200}; + unsigned int ch_mask[WSA881X_MAX_SWR_PORTS] = {0x1, 0xF, 0x3, 0x3}; + struct snd_soc_codec *codec = snd_soc_component_to_codec(component); + struct msm_asoc_mach_data *pdata; + struct snd_soc_dapm_context *dapm; + int ret = 0; + + if (!codec) { + pr_err("%s codec is NULL\n", __func__); + return -EINVAL; + } + + dapm = snd_soc_codec_get_dapm(codec); + + if (!strcmp(component->name_prefix, "SpkrLeft")) { + dev_dbg(codec->dev, "%s: setting left ch map to codec %s\n", + __func__, codec->component.name); + wsa881x_set_channel_map(codec, &spkleft_ports[0], + WSA881X_MAX_SWR_PORTS, &ch_mask[0], + &ch_rate[0], NULL); + if (dapm->component) { + snd_soc_dapm_ignore_suspend(dapm, "SpkrLeft IN"); + snd_soc_dapm_ignore_suspend(dapm, "SpkrLeft SPKR"); + } + } else if (!strcmp(component->name_prefix, "SpkrRight")) { + dev_dbg(codec->dev, "%s: setting right ch map to codec %s\n", + __func__, codec->component.name); + wsa881x_set_channel_map(codec, &spkright_ports[0], + WSA881X_MAX_SWR_PORTS, &ch_mask[0], + &ch_rate[0], NULL); + if (dapm->component) { + snd_soc_dapm_ignore_suspend(dapm, "SpkrRight IN"); + snd_soc_dapm_ignore_suspend(dapm, "SpkrRight SPKR"); + } + } else { + dev_err(codec->dev, "%s: wrong codec name %s\n", __func__, + codec->component.name); + ret = -EINVAL; + goto err; + } + pdata = snd_soc_card_get_drvdata(component->card); + if (pdata && pdata->codec_root) + wsa881x_codec_info_create_codec_entry(pdata->codec_root, + codec); + +err: + return ret; +} + +static int msm_init_wsa_dev(struct platform_device *pdev, + struct snd_soc_card *card) +{ + struct device_node *wsa_of_node; + u32 wsa_max_devs; + u32 wsa_dev_cnt; + int i; + struct msm_wsa881x_dev_info *wsa881x_dev_info; + const char *wsa_auxdev_name_prefix[1]; + char *dev_name_str = NULL; + int found = 0; + int ret = 0; + + /* Get maximum WSA device count for this platform */ + ret = of_property_read_u32(pdev->dev.of_node, + "qcom,wsa-max-devs", &wsa_max_devs); + if (ret) { + dev_info(&pdev->dev, + "%s: wsa-max-devs property missing in DT %s, ret = %d\n", + __func__, pdev->dev.of_node->full_name, ret); + card->num_aux_devs = 0; + return 0; + } + if (wsa_max_devs == 0) { + dev_warn(&pdev->dev, + "%s: Max WSA devices is 0 for this target?\n", + __func__); + card->num_aux_devs = 0; + return 0; + } + + /* Get count of WSA device phandles for this platform */ + wsa_dev_cnt = of_count_phandle_with_args(pdev->dev.of_node, + "qcom,wsa-devs", NULL); + if (wsa_dev_cnt == -ENOENT) { + dev_warn(&pdev->dev, "%s: No wsa device defined in DT.\n", + __func__); + goto err; + } else if (wsa_dev_cnt <= 0) { + dev_err(&pdev->dev, + "%s: Error reading wsa device from DT. wsa_dev_cnt = %d\n", + __func__, wsa_dev_cnt); + ret = -EINVAL; + goto err; + } + + /* + * Expect total phandles count to be NOT less than maximum possible + * WSA count. However, if it is less, then assign same value to + * max count as well. + */ + if (wsa_dev_cnt < wsa_max_devs) { + dev_dbg(&pdev->dev, + "%s: wsa_max_devs = %d cannot exceed wsa_dev_cnt = %d\n", + __func__, wsa_max_devs, wsa_dev_cnt); + wsa_max_devs = wsa_dev_cnt; + } + + /* Make sure prefix string passed for each WSA device */ + ret = of_property_count_strings(pdev->dev.of_node, + "qcom,wsa-aux-dev-prefix"); + if (ret != wsa_dev_cnt) { + dev_err(&pdev->dev, + "%s: expecting %d wsa prefix. Defined only %d in DT\n", + __func__, wsa_dev_cnt, ret); + ret = -EINVAL; + goto err; + } + + /* + * Alloc mem to store phandle and index info of WSA device, if already + * registered with ALSA core + */ + wsa881x_dev_info = devm_kcalloc(&pdev->dev, wsa_max_devs, + sizeof(struct msm_wsa881x_dev_info), + GFP_KERNEL); + if (!wsa881x_dev_info) { + ret = -ENOMEM; + goto err; + } + + /* + * search and check whether all WSA devices are already + * registered with ALSA core or not. If found a node, store + * the node and the index in a local array of struct for later + * use. + */ + for (i = 0; i < wsa_dev_cnt; i++) { + wsa_of_node = of_parse_phandle(pdev->dev.of_node, + "qcom,wsa-devs", i); + if (unlikely(!wsa_of_node)) { + /* we should not be here */ + dev_err(&pdev->dev, + "%s: wsa dev node is not present\n", + __func__); + ret = -EINVAL; + goto err_free_dev_info; + } + if (soc_find_component_locked(wsa_of_node, NULL)) { + /* WSA device registered with ALSA core */ + wsa881x_dev_info[found].of_node = wsa_of_node; + wsa881x_dev_info[found].index = i; + found++; + if (found == wsa_max_devs) + break; + } + } + + if (found < wsa_max_devs) { + dev_dbg(&pdev->dev, + "%s: failed to find %d components. Found only %d\n", + __func__, wsa_max_devs, found); + return -EPROBE_DEFER; + } + dev_info(&pdev->dev, + "%s: found %d wsa881x devices registered with ALSA core\n", + __func__, found); + + card->num_aux_devs = wsa_max_devs; + card->num_configs = wsa_max_devs; + + /* Alloc array of AUX devs struct */ + msm_aux_dev = devm_kcalloc(&pdev->dev, card->num_aux_devs, + sizeof(struct snd_soc_aux_dev), + GFP_KERNEL); + if (!msm_aux_dev) { + ret = -ENOMEM; + goto err_free_dev_info; + } + + /* Alloc array of codec conf struct */ + msm_codec_conf = devm_kcalloc(&pdev->dev, card->num_aux_devs, + sizeof(struct snd_soc_codec_conf), + GFP_KERNEL); + if (!msm_codec_conf) { + ret = -ENOMEM; + goto err_free_aux_dev; + } + + for (i = 0; i < card->num_aux_devs; i++) { + dev_name_str = devm_kzalloc(&pdev->dev, DEV_NAME_STR_LEN, + GFP_KERNEL); + if (!dev_name_str) { + ret = -ENOMEM; + goto err_free_cdc_conf; + } + + ret = of_property_read_string_index(pdev->dev.of_node, + "qcom,wsa-aux-dev-prefix", + wsa881x_dev_info[i].index, + wsa_auxdev_name_prefix); + if (ret) { + dev_err(&pdev->dev, + "%s: failed to read wsa aux dev prefix, ret = %d\n", + __func__, ret); + ret = -EINVAL; + goto err_free_dev_name_str; + } + + snprintf(dev_name_str, strlen("wsa881x.%d"), "wsa881x.%d", i); + msm_aux_dev[i].name = dev_name_str; + msm_aux_dev[i].codec_name = NULL; + msm_aux_dev[i].codec_of_node = + wsa881x_dev_info[i].of_node; + msm_aux_dev[i].init = msm_wsa881x_init; + msm_codec_conf[i].dev_name = NULL; + msm_codec_conf[i].name_prefix = wsa_auxdev_name_prefix[0]; + msm_codec_conf[i].of_node = + wsa881x_dev_info[i].of_node; + } + card->codec_conf = msm_codec_conf; + card->aux_dev = msm_aux_dev; + + return 0; + +err_free_dev_name_str: + devm_kfree(&pdev->dev, dev_name_str); +err_free_cdc_conf: + devm_kfree(&pdev->dev, msm_codec_conf); +err_free_aux_dev: + devm_kfree(&pdev->dev, msm_aux_dev); +err_free_dev_info: + devm_kfree(&pdev->dev, wsa881x_dev_info); +err: + return ret; +} + +static void msm_i2s_auxpcm_init(struct platform_device *pdev) +{ + int count; + u32 mi2s_master_slave[MI2S_MAX]; + int ret; + + for (count = 0; count < MI2S_MAX; count++) { + mutex_init(&mi2s_intf_conf[count].lock); + mi2s_intf_conf[count].ref_cnt = 0; + } + + ret = of_property_read_u32_array(pdev->dev.of_node, + "qcom,msm-mi2s-master", + mi2s_master_slave, MI2S_MAX); + if (ret) { + dev_dbg(&pdev->dev, "%s: no qcom,msm-mi2s-master in DT node\n", + __func__); + } else { + for (count = 0; count < MI2S_MAX; count++) { + mi2s_intf_conf[count].msm_is_mi2s_master = + mi2s_master_slave[count]; + } + } +} + +static void msm_i2s_auxpcm_deinit(void) +{ + int count; + + for (count = 0; count < MI2S_MAX; count++) { + mutex_destroy(&mi2s_intf_conf[count].lock); + mi2s_intf_conf[count].ref_cnt = 0; + mi2s_intf_conf[count].msm_is_mi2s_master = 0; + } +} +static int msm_asoc_machine_probe(struct platform_device *pdev) +{ + struct snd_soc_card *card; + struct msm_asoc_mach_data *pdata; + const char *mbhc_audio_jack_type = NULL; + int ret; + + if (!pdev->dev.of_node) { + dev_err(&pdev->dev, "No platform supplied from device tree\n"); + return -EINVAL; + } + + pdata = devm_kzalloc(&pdev->dev, + sizeof(struct msm_asoc_mach_data), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + card = populate_snd_card_dailinks(&pdev->dev); + if (!card) { + dev_err(&pdev->dev, "%s: Card uninitialized\n", __func__); + ret = -EINVAL; + goto err; + } + card->dev = &pdev->dev; + platform_set_drvdata(pdev, card); + snd_soc_card_set_drvdata(card, pdata); + + ret = snd_soc_of_parse_card_name(card, "qcom,model"); + if (ret) { + dev_err(&pdev->dev, "parse card name failed, err:%d\n", + ret); + goto err; + } + + ret = snd_soc_of_parse_audio_routing(card, "qcom,audio-routing"); + if (ret) { + dev_err(&pdev->dev, "parse audio routing failed, err:%d\n", + ret); + goto err; + } + + ret = msm_populate_dai_link_component_of_node(card); + if (ret) { + ret = -EPROBE_DEFER; + goto err; + } + ret = msm_init_wsa_dev(pdev, card); + if (ret) + goto err; + + ret = devm_snd_soc_register_card(&pdev->dev, card); + if (ret == -EPROBE_DEFER) { + if (codec_reg_done) + ret = -EINVAL; + goto err; + } else if (ret) { + dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", + ret); + goto err; + } + dev_info(&pdev->dev, "Sound card %s registered\n", card->name); + spdev = pdev; + + INIT_WORK(&pdata->adsp_power_up_work, msm_adsp_power_up_config_work); + + ret = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev); + if (ret) { + dev_dbg(&pdev->dev, "%s: failed to add child nodes, ret=%d\n", + __func__, ret); + } else { + pdata->hph_en1_gpio_p = of_parse_phandle(pdev->dev.of_node, + "qcom,hph-en1-gpio", 0); + if (!pdata->hph_en1_gpio_p) { + dev_dbg(&pdev->dev, "property %s not detected in node %s\n", + "qcom,hph-en1-gpio", + pdev->dev.of_node->full_name); + } + + pdata->hph_en0_gpio_p = of_parse_phandle(pdev->dev.of_node, + "qcom,hph-en0-gpio", 0); + if (!pdata->hph_en0_gpio_p) { + dev_dbg(&pdev->dev, "property %s not detected in node %s\n", + "qcom,hph-en0-gpio", + pdev->dev.of_node->full_name); + } + } + + ret = of_property_read_string(pdev->dev.of_node, + "qcom,mbhc-audio-jack-type", &mbhc_audio_jack_type); + if (ret) { + dev_dbg(&pdev->dev, "Looking up %s property in node %s failed\n", + "qcom,mbhc-audio-jack-type", + pdev->dev.of_node->full_name); + dev_dbg(&pdev->dev, "Jack type properties set to default\n"); + } else { + if (!strcmp(mbhc_audio_jack_type, "4-pole-jack")) { + wcd_mbhc_cfg.enable_anc_mic_detect = false; + dev_dbg(&pdev->dev, "This hardware has 4 pole jack"); + } else if (!strcmp(mbhc_audio_jack_type, "5-pole-jack")) { + wcd_mbhc_cfg.enable_anc_mic_detect = true; + dev_dbg(&pdev->dev, "This hardware has 5 pole jack"); + } else if (!strcmp(mbhc_audio_jack_type, "6-pole-jack")) { + wcd_mbhc_cfg.enable_anc_mic_detect = true; + dev_dbg(&pdev->dev, "This hardware has 6 pole jack"); + } else { + wcd_mbhc_cfg.enable_anc_mic_detect = false; + dev_dbg(&pdev->dev, "Unknown value, set to default\n"); + } + } + /* + * Parse US-Euro gpio info from DT. Report no error if us-euro + * entry is not found in DT file as some targets do not support + * US-Euro detection + */ + pdata->us_euro_gpio_p = of_parse_phandle(pdev->dev.of_node, + "qcom,us-euro-gpios", 0); + if (!pdata->us_euro_gpio_p) { + dev_dbg(&pdev->dev, "property %s not detected in node %s", + "qcom,us-euro-gpios", pdev->dev.of_node->full_name); + } else { + dev_dbg(&pdev->dev, "%s detected\n", + "qcom,us-euro-gpios"); + wcd_mbhc_cfg.swap_gnd_mic = msm_swap_gnd_mic; + } + + if (wcd_mbhc_cfg.enable_usbc_analog) + wcd_mbhc_cfg.swap_gnd_mic = msm_usbc_swap_gnd_mic; + + pdata->fsa_handle = of_parse_phandle(pdev->dev.of_node, + "fsa4480-i2c-handle", 0); + if (!pdata->fsa_handle) + dev_dbg(&pdev->dev, "property %s not detected in node %s\n", + "fsa4480-i2c-handle", pdev->dev.of_node->full_name); + + /* Parse pinctrl info from devicetree */ + ret = msm_get_pinctrl(pdev); + if (!ret) { + pr_debug("%s: pinctrl parsing successful\n", __func__); + } else { + dev_dbg(&pdev->dev, + "%s: Parsing pinctrl failed with %d. Cannot use Ports\n", + __func__, ret); + ret = 0; + } + + msm_i2s_auxpcm_init(pdev); + is_initial_boot = true; + ret = audio_notifier_register("sm8150", AUDIO_NOTIFIER_ADSP_DOMAIN, + &service_nb); + if (ret < 0) + pr_err("%s: Audio notifier register failed ret = %d\n", + __func__, ret); + + return 0; +err: + msm_release_pinctrl(pdev); + devm_kfree(&pdev->dev, pdata); + return ret; +} + +static int msm_asoc_machine_remove(struct platform_device *pdev) +{ + audio_notifier_deregister("sm8150"); + msm_i2s_auxpcm_deinit(); + + msm_release_pinctrl(pdev); + return 0; +} + +static struct platform_driver sm8150_asoc_machine_driver = { + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + .pm = &snd_soc_pm_ops, + .of_match_table = sm8150_asoc_machine_of_match, + }, + .probe = msm_asoc_machine_probe, + .remove = msm_asoc_machine_remove, +}; + +int __init sm8150_init(void) +{ + pr_debug("%s\n", __func__); + return platform_driver_register(&sm8150_asoc_machine_driver); +} + +void sm8150_exit(void) +{ + pr_debug("%s\n", __func__); + platform_driver_unregister(&sm8150_asoc_machine_driver); +} + +MODULE_DESCRIPTION("ALSA SoC msm"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:" DRV_NAME); +MODULE_DEVICE_TABLE(of, sm8150_asoc_machine_of_match); diff --git a/audio-kernel/config/qcs405auto.conf b/audio-kernel/config/qcs405auto.conf new file mode 100644 index 000000000..afa8140b6 --- /dev/null +++ b/audio-kernel/config/qcs405auto.conf @@ -0,0 +1,39 @@ +CONFIG_PINCTRL_LPI=m +CONFIG_AUDIO_EXT_CLK=m +CONFIG_SND_SOC_WCD9XXX_V2=m +CONFIG_SND_SOC_WSA881X=m +CONFIG_SND_SOC_WCD9335=m +CONFIG_WCD9XXX_CODEC_CORE=m +CONFIG_MSM_CDC_PINCTRL=m +CONFIG_SND_SOC_CSRA66X0=m +CONFIG_MSM_QDSP6V2_CODECS=m +CONFIG_MSM_ULTRASOUND=m +CONFIG_MSM_QDSP6_APRV2_RPMSG=m +CONFIG_SND_SOC_MSM_QDSP6V2_INTF=m +CONFIG_MSM_ADSP_LOADER=m +CONFIG_REGMAP_SWR=m +CONFIG_MSM_QDSP6_SSR=m +CONFIG_MSM_QDSP6_PDR=m +CONFIG_MSM_QDSP6_NOTIFIER=m +CONFIG_SND_SOC_MSM_HOSTLESS_PCM=m +CONFIG_SND_SOC_QCS405=m +CONFIG_SND_SOC_BOLERO=m +CONFIG_WSA_MACRO=m +CONFIG_VA_MACRO=m +CONFIG_SOUNDWIRE=m +CONFIG_SOUNDWIRE_MSTR_CTRL=m +CONFIG_SND_SOC_WCD_MBHC_LEGACY=m +CONFIG_SND_SOC_QDSP6V2=m +CONFIG_QTI_PP=m +CONFIG_SND_HWDEP_ROUTING=m +CONFIG_AFE_HWDEP=m +CONFIG_DTS_EAGLE=m +CONFIG_DOLBY_DS2=m +CONFIG_DOLBY_LICENSE=m +CONFIG_DTS_SRS_TM=m +CONFIG_MSM_MDF=m +CONFIG_SND_SOC_MSM_STUB=m +CONFIG_MSM_AVTIMER=m +CONFIG_SND_SOC_MSM_HDMI_CODEC_RX=m +CONFIG_SND_SOC_EP92=m +CONFIG_USE_Q6_32CH_SUPPORT=m diff --git a/audio-kernel/config/qcs405autoconf.h b/audio-kernel/config/qcs405autoconf.h new file mode 100644 index 000000000..fd9407bdb --- /dev/null +++ b/audio-kernel/config/qcs405autoconf.h @@ -0,0 +1,51 @@ +/* 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. + */ + +#define CONFIG_PINCTRL_LPI 1 +#define CONFIG_AUDIO_EXT_CLK 1 +#define CONFIG_SND_SOC_WCD9XXX_V2 1 +#define CONFIG_SND_SOC_WSA881X 1 +#define CONFIG_SND_SOC_WCD9335 1 +#define CONFIG_SND_SOC_CSRA66X0 1 +#define CONFIG_WCD9XXX_CODEC_CORE 1 +#define CONFIG_MSM_CDC_PINCTRL 1 +#define CONFIG_MSM_QDSP6V2_CODECS 1 +#define CONFIG_MSM_ULTRASOUND 1 +#define CONFIG_MSM_QDSP6_APRV2_RPMSG 1 +#define CONFIG_SND_SOC_MSM_QDSP6V2_INTF 1 +#define CONFIG_MSM_ADSP_LOADER 1 +#define CONFIG_REGMAP_SWR 1 +#define CONFIG_MSM_QDSP6_SSR 1 +#define CONFIG_MSM_QDSP6_PDR 1 +#define CONFIG_MSM_QDSP6_NOTIFIER 1 +#define CONFIG_SND_SOC_MSM_HOSTLESS_PCM 1 +#define CONFIG_SND_SOC_QCS405 1 +#define CONFIG_SND_SOC_BOLERO 1 +#define CONFIG_WSA_MACRO 1 +#define CONFIG_VA_MACRO 1 +#define CONFIG_SOUNDWIRE 1 +#define CONFIG_SOUNDWIRE_MSTR_CTRL 1 +#define CONFIG_SND_SOC_WCD_MBHC_LEGACY 1 +#define CONFIG_SND_SOC_QDSP6V2 1 +#define CONFIG_QTI_PP 1 +#define CONFIG_SND_HWDEP_ROUTING 1 +#define CONFIG_AFE_HWDEP 1 +#define CONFIG_DTS_EAGLE 1 +#define CONFIG_DOLBY_DS2 1 +#define CONFIG_DOLBY_LICENSE 1 +#define CONFIG_DTS_SRS_TM 1 +#define CONFIG_SND_SOC_MSM_STUB 1 +#define CONFIG_MSM_MDF 1 +#define CONFIG_MSM_AVTIMER 1 +#define CONFIG_SND_SOC_MSM_HDMI_CODEC_RX 1 +#define CONFIG_SND_SOC_EP92 1 +#define CONFIG_USE_Q6_32CH_SUPPORT 1 diff --git a/audio-kernel/config/sdm670auto.conf b/audio-kernel/config/sdm670auto.conf new file mode 100644 index 000000000..4c0b284c9 --- /dev/null +++ b/audio-kernel/config/sdm670auto.conf @@ -0,0 +1,50 @@ +CONFIG_PINCTRL_LPI=m +CONFIG_PINCTRL_WCD=m +CONFIG_AUDIO_EXT_CLK=m +CONFIG_SND_SOC_WCD9XXX_V2=m +CONFIG_SND_SOC_WCD_MBHC=m +CONFIG_SND_SOC_WSA881X=m +CONFIG_SND_SOC_WCD_DSP_MGR=m +CONFIG_SND_SOC_WCD_SPI=m +CONFIG_SND_SOC_WCD_CPE=m +CONFIG_SND_SOC_CPE=m +CONFIG_SND_SOC_WCD9335=m +CONFIG_SND_SOC_WCD934X=m +CONFIG_SND_SOC_WCD934X_MBHC=m +CONFIG_SND_SOC_WCD934X_DSD=m +CONFIG_MSM_QDSP6V2_CODECS=m +CONFIG_MSM_ULTRASOUND=m +CONFIG_MSM_QDSP6_APRV2_GLINK=m +CONFIG_MSM_ADSP_LOADER=m +CONFIG_REGMAP_SWR=m +CONFIG_MSM_QDSP6_SSR=m +CONFIG_MSM_QDSP6_PDR=m +CONFIG_MSM_QDSP6_NOTIFIER=m +CONFIG_SND_SOC_MSM_HOSTLESS_PCM=m +CONFIG_SND_SOC_MSM_QDSP6V2_INTF=m +CONFIG_SND_SOC_SDM670=m +CONFIG_MSM_GLINK_SPI_XPRT=m +CONFIG_SOUNDWIRE=m +CONFIG_SOUNDWIRE_WCD_CTRL=m +CONFIG_SND_SOC_QDSP6V2=m +CONFIG_SND_SOC_MSM_QDSP6V2_INTF=m +CONFIG_WCD9XXX_CODEC_CORE=m +CONFIG_MSM_CDC_PINCTRL=m +CONFIG_SND_SOC_WCD_MBHC_ADC=m +CONFIG_SND_SOC_WCD_MBHC_LEGACY=m +CONFIG_QTI_PP=m +CONFIG_SND_HWDEP_ROUTING=m +CONFIG_DTS_EAGLE=m +CONFIG_DOLBY_DS2=m +CONFIG_DOLBY_LICENSE=m +CONFIG_DTS_SRS_TM=m +CONFIG_SND_SOC_EXT_CODEC=m +CONFIG_SND_SOC_INT_CODEC=m +CONFIG_SND_SOC_MSM_STUB=m +CONFIG_WCD_DSP_GLINK=m +CONFIG_MSM_AVTIMER=m +CONFIG_SND_SOC_SDM660_CDC=m +CONFIG_SND_SOC_ANALOG_CDC=m +CONFIG_SND_SOC_DIGITAL_CDC=m +CONFIG_SND_SOC_MSM_SDW=m +CONFIG_SND_SOC_MSM_HDMI_CODEC_RX=m diff --git a/audio-kernel/config/sdm670auto_static.conf b/audio-kernel/config/sdm670auto_static.conf new file mode 100644 index 000000000..91a84649d --- /dev/null +++ b/audio-kernel/config/sdm670auto_static.conf @@ -0,0 +1,50 @@ +CONFIG_PINCTRL_LPI=y +CONFIG_PINCTRL_WCD=y +CONFIG_AUDIO_EXT_CLK=y +CONFIG_SND_SOC_WCD9XXX_V2=y +CONFIG_SND_SOC_WCD_MBHC=y +CONFIG_SND_SOC_WSA881X=y +CONFIG_SND_SOC_WCD_DSP_MGR=y +CONFIG_SND_SOC_WCD_SPI=y +CONFIG_SND_SOC_WCD_CPE=y +CONFIG_SND_SOC_CPE=y +CONFIG_SND_SOC_WCD9335=y +CONFIG_SND_SOC_WCD934X=y +CONFIG_SND_SOC_WCD934X_MBHC=y +CONFIG_SND_SOC_WCD934X_DSD=y +CONFIG_MSM_QDSP6V2_CODECS=y +CONFIG_MSM_ULTRASOUND=y +CONFIG_MSM_QDSP6_APRV2_GLINK=y +CONFIG_MSM_ADSP_LOADER=y +CONFIG_REGMAP_SWR=y +CONFIG_MSM_QDSP6_SSR=y +CONFIG_MSM_QDSP6_PDR=y +CONFIG_MSM_QDSP6_NOTIFIER=y +CONFIG_SND_SOC_MSM_HOSTLESS_PCM=y +CONFIG_SND_SOC_MSM_QDSP6V2_INTF=y +CONFIG_SND_SOC_SDM670=y +CONFIG_MSM_GLINK_SPI_XPRT=y +CONFIG_SOUNDWIRE=y +CONFIG_SOUNDWIRE_WCD_CTRL=y +CONFIG_SND_SOC_QDSP6V2=y +CONFIG_SND_SOC_MSM_QDSP6V2_INTF=y +CONFIG_WCD9XXX_CODEC_CORE=y +CONFIG_MSM_CDC_PINCTRL=y +CONFIG_SND_SOC_WCD_MBHC_ADC=y +CONFIG_SND_SOC_WCD_MBHC_LEGACY=y +CONFIG_QTI_PP=y +CONFIG_SND_HWDEP_ROUTING=y +CONFIG_DTS_EAGLE=y +CONFIG_DOLBY_DS2=y +CONFIG_DOLBY_LICENSE=y +CONFIG_DTS_SRS_TM=y +CONFIG_SND_SOC_EXT_CODEC=y +CONFIG_SND_SOC_INT_CODEC=y +CONFIG_SND_SOC_MSM_STUB=y +CONFIG_WCD_DSP_GLINK=y +CONFIG_MSM_AVTIMER=y +CONFIG_SND_SOC_SDM660_CDC=y +CONFIG_SND_SOC_ANALOG_CDC=y +CONFIG_SND_SOC_DIGITAL_CDC=y +CONFIG_SND_SOC_MSM_SDW=y +CONFIG_SND_SOC_MSM_HDMI_CODEC_RX=y diff --git a/audio-kernel/config/sdm670autoconf.h b/audio-kernel/config/sdm670autoconf.h new file mode 100644 index 000000000..55f9548fc --- /dev/null +++ b/audio-kernel/config/sdm670autoconf.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2017-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. + */ + +#define CONFIG_PINCTRL_LPI 1 +#define CONFIG_PINCTRL_WCD 1 +#define CONFIG_AUDIO_EXT_CLK 1 +#define CONFIG_SND_SOC_WCD9XXX_V2 1 +#define CONFIG_SND_SOC_WCD_CPE 1 +#define CONFIG_SND_SOC_WCD_MBHC 1 +#define CONFIG_SND_SOC_WSA881X 1 +#define CONFIG_SND_SOC_WCD_DSP_MGR 1 +#define CONFIG_SND_SOC_WCD_SPI 1 +#define CONFIG_SND_SOC_WCD9335 1 +#define CONFIG_SND_SOC_WCD934X 1 +#define CONFIG_SND_SOC_WCD934X_MBHC 1 +#define CONFIG_SND_SOC_WCD934X_DSD 1 +#define CONFIG_MSM_QDSP6V2_CODECS 1 +#define CONFIG_MSM_ULTRASOUND 1 +#define CONFIG_MSM_QDSP6_APRV2_GLINK 1 +#define CONFIG_MSM_ADSP_LOADER 1 +#define CONFIG_REGMAP_SWR 1 +#define CONFIG_MSM_QDSP6_SSR 1 +#define CONFIG_MSM_QDSP6_PDR 1 +#define CONFIG_MSM_QDSP6_NOTIFIER 1 +#define CONFIG_SND_SOC_MSM_HOSTLESS_PCM 1 +#define CONFIG_SND_SOC_MSM_QDSP6V2_INTF 1 +#define CONFIG_SND_SOC_SDM670 1 +#define CONFIG_MSM_GLINK_SPI_XPRT 1 +#define CONFIG_SOUNDWIRE 1 +#define CONFIG_SOUNDWIRE_WCD_CTRL 1 +#define CONFIG_SND_SOC_WCD_MBHC_ADC 1 +#define CONFIG_SND_SOC_WCD_MBHC_LEGACY 1 +#define CONFIG_SND_SOC_QDSP6V2 1 +#define CONFIG_SND_SOC_MSM_QDSP6V2_INTF 1 +#define CONFIG_QTI_PP 1 +#define CONFIG_SND_HWDEP_ROUTING 1 +#define CONFIG_DTS_EAGLE 1 +#define CONFIG_DOLBY_DS2 1 +#define CONFIG_DOLBY_LICENSE 1 +#define CONFIG_DTS_SRS_TM 1 +#define CONFIG_WCD9XXX_CODEC_CORE 1 +#define CONFIG_MSM_CDC_PINCTRL 1 +#define CONFIG_SND_SOC_MSM_STUB 1 +#define CONFIG_WCD_DSP_GLINK 1 +#define CONFIG_MSM_AVTIMER 1 +#define CONFIG_SND_SOC_EXT_CODEC 1 +#define CONFIG_SND_SOC_INT_CODEC 1 +#define CONFIG_SND_SOC_CPE 1 +#define CONFIG_SND_SOC_SDM660_CDC 1 +#define CONFIG_SND_SOC_ANALOG_CDC 1 +#define CONFIG_SND_SOC_DIGITAL_CDC 1 +#define CONFIG_SND_SOC_MSM_SDW 1 +#define CONFIG_SND_SOC_MSM_HDMI_CODEC_RX 1 diff --git a/audio-kernel/config/sdm845auto.conf b/audio-kernel/config/sdm845auto.conf new file mode 100644 index 000000000..4c195b43d --- /dev/null +++ b/audio-kernel/config/sdm845auto.conf @@ -0,0 +1,37 @@ +CONFIG_PINCTRL_WCD=m +CONFIG_SND_SOC_WCD9XXX_V2=m +CONFIG_SND_SOC_WCD_MBHC=m +CONFIG_SND_SOC_WSA881X=m +CONFIG_SND_SOC_WCD_SPI=m +CONFIG_SND_SOC_WCD934X=m +CONFIG_SOUNDWIRE_WCD_CTRL=m +CONFIG_WCD9XXX_CODEC_CORE=m +CONFIG_MSM_CDC_PINCTRL=m +CONFIG_SND_SOC_WCD934X_MBHC=m +CONFIG_SND_SOC_WCD934X_DSD=m +CONFIG_SND_SOC_MACHINE_SDM845=m +CONFIG_WCD_DSP_GLINK=m +CONFIG_MSM_QDSP6V2_CODECS=y +CONFIG_MSM_ULTRASOUND=y +CONFIG_MSM_QDSP6_APRV2_GLINK=y +CONFIG_SND_SOC_MSM_QDSP6V2_INTF=y +CONFIG_MSM_ADSP_LOADER=y +CONFIG_REGMAP_SWR=y +CONFIG_MSM_QDSP6_SSR=y +CONFIG_MSM_QDSP6_PDR=y +CONFIG_MSM_QDSP6_NOTIFIER=y +CONFIG_SND_SOC_MSM_HOSTLESS_PCM=y +CONFIG_SND_SOC_SDM845=y +CONFIG_MSM_GLINK_SPI_XPRT=y +CONFIG_SOUNDWIRE=y +CONFIG_SND_SOC_QDSP6V2=y +CONFIG_SND_SOC_WCD_MBHC_ADC=y +CONFIG_QTI_PP=y +CONFIG_SND_HWDEP_ROUTING=y +CONFIG_DTS_EAGLE=y +CONFIG_DOLBY_DS2=y +CONFIG_DOLBY_LICENSE=y +CONFIG_DTS_SRS_TM=y +CONFIG_SND_SOC_MSM_STUB=y +CONFIG_MSM_AVTIMER=y +CONFIG_SND_SOC_MSM_HDMI_CODEC_RX=y diff --git a/audio-kernel/config/sdm845autoconf.h b/audio-kernel/config/sdm845autoconf.h new file mode 100644 index 000000000..6ef465a61 --- /dev/null +++ b/audio-kernel/config/sdm845autoconf.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2017-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. + */ + +#define CONFIG_PINCTRL_WCD 1 +#define CONFIG_SND_SOC_WCD934X 1 +#define CONFIG_SND_SOC_WCD9XXX_V2 1 +#define CONFIG_SND_SOC_WCD_CPE 1 +#define CONFIG_SND_SOC_WCD_MBHC 1 +#define CONFIG_SND_SOC_WSA881X 1 +#define CONFIG_SND_SOC_WCD_SPI 1 +#define CONFIG_SND_SOC_WCD934X_MBHC 1 +#define CONFIG_SND_SOC_WCD934X_DSD 1 +#define CONFIG_MSM_QDSP6V2_CODECS 1 +#define CONFIG_MSM_ULTRASOUND 1 +#define CONFIG_MSM_QDSP6_APRV2_GLINK 1 +#define CONFIG_SND_SOC_MSM_QDSP6V2_INTF 1 +#define CONFIG_MSM_ADSP_LOADER 1 +#define CONFIG_REGMAP_SWR 1 +#define CONFIG_MSM_QDSP6_SSR 1 +#define CONFIG_MSM_QDSP6_PDR 1 +#define CONFIG_MSM_QDSP6_NOTIFIER 1 +#define CONFIG_SND_SOC_MSM_HOSTLESS_PCM 1 +#define CONFIG_SND_SOC_SDM845 1 +#define CONFIG_MSM_GLINK_SPI_XPRT 1 +#define CONFIG_SOUNDWIRE 1 +#define CONFIG_SOUNDWIRE_WCD_CTRL 1 +#define CONFIG_SND_SOC_WCD_MBHC_ADC 1 +#define CONFIG_SND_SOC_QDSP6V2 1 +#define CONFIG_MSM_CDC_PINCTRL 1 +#define CONFIG_QTI_PP 1 +#define CONFIG_SND_HWDEP_ROUTING 1 +#define CONFIG_DTS_EAGLE 1 +#define CONFIG_DOLBY_DS2 1 +#define CONFIG_DOLBY_LICENSE 1 +#define CONFIG_DTS_SRS_TM 1 +#define CONFIG_WCD9XXX_CODEC_CORE 1 +#define CONFIG_SND_SOC_MSM_STUB 1 +#define CONFIG_WCD_DSP_GLINK 1 +#define CONFIG_MSM_AVTIMER 1 +#define CONFIG_SND_SOC_MSM_HDMI_CODEC_RX 1 diff --git a/audio-kernel/config/sdxpoorwillsauto.conf b/audio-kernel/config/sdxpoorwillsauto.conf new file mode 100644 index 000000000..dee34a08b --- /dev/null +++ b/audio-kernel/config/sdxpoorwillsauto.conf @@ -0,0 +1,29 @@ +CONFIG_PINCTRL_WCD=y +CONFIG_SND_SOC_WCD934X=y +CONFIG_AUDIO_EXT_CLK=y +CONFIG_SND_SOC_WCD9XXX_V2=y +CONFIG_SND_SOC_WCD_MBHC=y +CONFIG_SND_SOC_WSA881X=y +CONFIG_SND_SOC_WCD_DSP_MGR=y +CONFIG_SND_SOC_WCD934X=y +CONFIG_SND_SOC_WCD934X_MBHC=y +CONFIG_SND_SOC_WCD934X_DSD=y +CONFIG_MSM_QDSP6V2_CODECS=y +CONFIG_MSM_QDSP6_APRV3_GLINK=y +CONFIG_SND_SOC_MSM_QDSP6V2_INTF=y +CONFIG_MSM_ADSP_LOADER=y +CONFIG_REGMAP_SWR=y +CONFIG_SND_SOC_MSM_HOSTLESS_PCM=y +CONFIG_SND_SOC_MSM_QDSP6V2_INTF=y +CONFIG_SND_SOC_POORWILLS=y +CONFIG_SOUNDWIRE=y +CONFIG_SOUNDWIRE_WCD_CTRL=y +CONFIG_SND_SOC_QDSP6V2=y +CONFIG_SND_SOC_MSM_QDSP6V2_INTF=y +CONFIG_MSM_CDC_PINCTRL=y +CONFIG_WCD9XXX_CODEC_CORE=y +CONFIG_SND_SOC_WCD_MBHC_ADC=y +CONFIG_QTI_PP=y +CONFIG_SND_HWDEP_ROUTING=y +CONFIG_SND_SOC_MACHINE_SDXPOORWILLS=y +CONFIG_SND_SOC_MSM_STUB=y diff --git a/audio-kernel/config/sdxpoorwillsautoconf.h b/audio-kernel/config/sdxpoorwillsautoconf.h new file mode 100644 index 000000000..7b7dac403 --- /dev/null +++ b/audio-kernel/config/sdxpoorwillsautoconf.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2017-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. + */ + +#define CONFIG_PINCTRL_WCD 1 +#define CONFIG_SND_SOC_WCD934X 1 +#define CONFIG_AUDIO_EXT_CLK 1 +#define CONFIG_SND_SOC_WCD9XXX_V2 1 +#define CONFIG_SND_SOC_WCD_MBHC 1 +#define CONFIG_SND_SOC_WSA881X 1 +#define CONFIG_SND_SOC_WCD_DSP_MGR 1 +#define CONFIG_SND_SOC_WCD934X 1 +#define CONFIG_SND_SOC_WCD934X_MBHC 1 +#define CONFIG_SND_SOC_WCD934X_DSD 1 +#define CONFIG_MSM_QDSP6V2_CODECS 1 +#define CONFIG_MSM_QDSP6_APRV3_GLINK 1 +#define CONFIG_SND_SOC_MSM_QDSP6V2_INTF 1 +#define CONFIG_MSM_ADSP_LOADER 1 +#define CONFIG_REGMAP_SWR 1 +#define CONFIG_SND_SOC_MSM_HOSTLESS_PCM 1 +#define CONFIG_SND_SOC_MSM_QDSP6V2_INTF 1 +#define CONFIG_SND_SOC_POORWILLS 1 +#define CONFIG_SOUNDWIRE 1 +#define CONFIG_SOUNDWIRE_WCD_CTRL 1 +#define CONFIG_SND_SOC_QDSP6V2 1 +#define CONFIG_SND_SOC_MSM_QDSP6V2_INTF 1 +#define CONFIG_MSM_CDC_PINCTRL 1 +#define CONFIG_WCD9XXX_CODEC_CORE 1 +#define CONFIG_SND_SOC_WCD_MBHC_ADC 1 +#define CONFIG_QTI_PP 1 +#define CONFIG_SND_HWDEP_ROUTING 1 +#define CONFIG_SND_SOC_MACHINE_SDXPOORWILLS 1 +#define CONFIG_SND_SOC_MSM_STUB 1 diff --git a/audio-kernel/config/sm6150auto.conf b/audio-kernel/config/sm6150auto.conf new file mode 100644 index 000000000..f6074abba --- /dev/null +++ b/audio-kernel/config/sm6150auto.conf @@ -0,0 +1,48 @@ +CONFIG_PINCTRL_WCD=m +CONFIG_PINCTRL_LPI=m +CONFIG_AUDIO_EXT_CLK=m +CONFIG_SND_SOC_WCD9XXX_V2=m +CONFIG_SND_SOC_WCD_MBHC=m +CONFIG_SND_SOC_WSA881X=m +CONFIG_SND_SOC_WCD_DSP_MGR=m +CONFIG_SND_SOC_WCD_SPI=m +CONFIG_SND_SOC_WCD934X=m +CONFIG_SND_SOC_WCD934X_MBHC=m +CONFIG_SND_SOC_WCD934X_DSD=m +CONFIG_WCD9XXX_CODEC_CORE=m +CONFIG_MSM_CDC_PINCTRL=m +CONFIG_MSM_QDSP6V2_CODECS=m +CONFIG_MSM_ULTRASOUND=m +CONFIG_MSM_QDSP6_APRV2_RPMSG=m +CONFIG_MSM_ADSP_LOADER=m +CONFIG_REGMAP_SWR=m +CONFIG_MSM_QDSP6_SSR=m +CONFIG_MSM_QDSP6_PDR=m +CONFIG_MSM_QDSP6_NOTIFIER=m +CONFIG_SND_SOC_MSM_HOSTLESS_PCM=m +CONFIG_SND_SOC_MSM_QDSP6V2_INTF=m +CONFIG_SND_SOC_SM6150=m +CONFIG_MSM_GLINK_SPI_XPRT=m +CONFIG_WCD_DSP_GLINK=m +CONFIG_SOUNDWIRE=m +CONFIG_SOUNDWIRE_MSTR_CTRL=m +CONFIG_SND_SOC_QDSP6V2=m +CONFIG_SND_SOC_WCD_MBHC_ADC=m +CONFIG_QTI_PP=m +CONFIG_SND_HWDEP_ROUTING=m +CONFIG_DTS_EAGLE=m +CONFIG_DOLBY_DS2=m +CONFIG_DOLBY_LICENSE=m +CONFIG_DTS_SRS_TM=m +CONFIG_SND_SOC_MSM_STUB=m +CONFIG_MSM_AVTIMER=m +CONFIG_SND_SOC_MSM_HDMI_CODEC_RX=m +CONFIG_SND_SOC_BOLERO=m +CONFIG_WSA_MACRO=m +CONFIG_VA_MACRO=m +CONFIG_RX_MACRO=m +CONFIG_TX_MACRO=m +CONFIG_SND_SOC_WCD_IRQ=m +CONFIG_SND_SOC_WCD937X=m +CONFIG_SND_SOC_WCD937X_SLAVE=m +CONFIG_SND_EVENT=m diff --git a/audio-kernel/config/sm6150autoconf.h b/audio-kernel/config/sm6150autoconf.h new file mode 100644 index 000000000..d69a9dec0 --- /dev/null +++ b/audio-kernel/config/sm6150autoconf.h @@ -0,0 +1,61 @@ +/* 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. + */ + +#define CONFIG_PINCTRL_WCD 1 +#define CONFIG_PINCTRL_LPI 1 +#define CONFIG_AUDIO_EXT_CLK 1 +#define CONFIG_SND_SOC_WCD9XXX_V2 1 +#define CONFIG_SND_SOC_WCD_MBHC 1 +#define CONFIG_SND_SOC_WSA881X 1 +#define CONFIG_SND_SOC_WCD_DSP_MGR 1 +#define CONFIG_SND_SOC_WCD_SPI 1 +#define CONFIG_SND_SOC_WCD934X 1 +#define CONFIG_SND_SOC_WCD934X_MBHC 1 +#define CONFIG_SND_SOC_WCD934X_DSD 1 +#define CONFIG_SND_SOC_WCD_CPE 1 +#define CONFIG_WCD9XXX_CODEC_CORE 1 +#define CONFIG_MSM_CDC_PINCTRL 1 +#define CONFIG_MSM_QDSP6V2_CODECS 1 +#define CONFIG_MSM_ULTRASOUND 1 +#define CONFIG_MSM_QDSP6_APRV2_RPMSG 1 +#define CONFIG_SND_SOC_MSM_QDSP6V2_INTF 1 +#define CONFIG_MSM_ADSP_LOADER 1 +#define CONFIG_REGMAP_SWR 1 +#define CONFIG_MSM_QDSP6_SSR 1 +#define CONFIG_MSM_QDSP6_PDR 1 +#define CONFIG_MSM_QDSP6_NOTIFIER 1 +#define CONFIG_SND_SOC_MSM_HOSTLESS_PCM 1 +#define CONFIG_SND_SOC_SM6150 1 +#define CONFIG_MSM_GLINK_SPI_XPRT 1 +#define CONFIG_WCD_DSP_GLINK 1 +#define CONFIG_SOUNDWIRE 1 +#define CONFIG_SOUNDWIRE_MSTR_CTRL 1 +#define CONFIG_SND_SOC_WCD_MBHC_ADC 1 +#define CONFIG_SND_SOC_QDSP6V2 1 +#define CONFIG_QTI_PP 1 +#define CONFIG_SND_HWDEP_ROUTING 1 +#define CONFIG_DTS_EAGLE 1 +#define CONFIG_DOLBY_DS2 1 +#define CONFIG_DOLBY_LICENSE 1 +#define CONFIG_DTS_SRS_TM 1 +#define CONFIG_SND_SOC_MSM_STUB 1 +#define CONFIG_MSM_AVTIMER 1 +#define CONFIG_SND_SOC_MSM_HDMI_CODEC_RX 1 +#define CONFIG_SND_SOC_BOLERO 1 +#define CONFIG_WSA_MACRO 1 +#define CONFIG_VA_MACRO 1 +#define CONFIG_RX_MACRO 1 +#define CONFIG_TX_MACRO 1 +#define CONFIG_SND_SOC_WCD_IRQ 1 +#define CONFIG_SND_SOC_WCD937X 1 +#define CONFIG_SND_SOC_WCD937X_SLAVE 1 +#define CONFIG_SND_EVENT 1 diff --git a/audio-kernel/config/sm8150auto.conf b/audio-kernel/config/sm8150auto.conf new file mode 100644 index 000000000..e0e49e775 --- /dev/null +++ b/audio-kernel/config/sm8150auto.conf @@ -0,0 +1,41 @@ +CONFIG_PINCTRL_WCD=m +CONFIG_AUDIO_EXT_CLK=m +CONFIG_SND_SOC_WCD9XXX_V2=m +CONFIG_SND_SOC_WCD_MBHC=m +CONFIG_SND_SOC_WSA881X=m +CONFIG_SND_SOC_WCD9360=m +CONFIG_SND_SOC_WCD_DSP_MGR=m +CONFIG_SND_SOC_WCD_SPI=m +CONFIG_SND_SOC_WCD934X=m +CONFIG_SND_SOC_WCD934X_MBHC=m +CONFIG_SND_SOC_WCD934X_DSD=m +CONFIG_SND_SOC_WCD_CPE=m +CONFIG_SOUNDWIRE_WCD_CTRL=m +CONFIG_WCD9XXX_CODEC_CORE=m +CONFIG_MSM_CDC_PINCTRL=m +CONFIG_MSM_QDSP6V2_CODECS=m +CONFIG_MSM_ULTRASOUND=m +CONFIG_MSM_QDSP6_APRV2_RPMSG=m +CONFIG_MSM_ADSP_LOADER=m +CONFIG_REGMAP_SWR=m +CONFIG_MSM_QDSP6_SSR=m +CONFIG_MSM_QDSP6_PDR=m +CONFIG_MSM_QDSP6_NOTIFIER=m +CONFIG_SND_SOC_MSM_HOSTLESS_PCM=m +CONFIG_SND_SOC_MSM_QDSP6V2_INTF=m +CONFIG_SND_SOC_SM8150=m +CONFIG_MSM_GLINK_SPI_XPRT=m +CONFIG_WCD_DSP_GLINK=m +CONFIG_SOUNDWIRE=m +CONFIG_SND_SOC_QDSP6V2=m +CONFIG_SND_SOC_WCD_MBHC_ADC=m +CONFIG_QTI_PP=m +CONFIG_SND_HWDEP_ROUTING=m +CONFIG_DTS_EAGLE=m +CONFIG_DOLBY_DS2=m +CONFIG_DOLBY_LICENSE=m +CONFIG_DTS_SRS_TM=m +CONFIG_SND_SOC_MSM_STUB=m +CONFIG_MSM_AVTIMER=m +CONFIG_SND_SOC_MSM_HDMI_CODEC_RX=m +CONFIG_VOICE_MHI=m diff --git a/audio-kernel/config/sm8150autoconf.h b/audio-kernel/config/sm8150autoconf.h new file mode 100644 index 000000000..e0eea9d99 --- /dev/null +++ b/audio-kernel/config/sm8150autoconf.h @@ -0,0 +1,53 @@ +/* Copyright (c) 2018-2019, 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. + */ + +#define CONFIG_PINCTRL_WCD 1 +#define CONFIG_AUDIO_EXT_CLK 1 +#define CONFIG_SND_SOC_WCD9XXX_V2 1 +#define CONFIG_SND_SOC_WCD_MBHC 1 +#define CONFIG_SND_SOC_WSA881X 1 +#define CONFIG_SND_SOC_WCD9360 1 +#define CONFIG_SND_SOC_WCD_DSP_MGR 1 +#define CONFIG_SND_SOC_WCD_SPI 1 +#define CONFIG_SND_SOC_WCD934X 1 +#define CONFIG_SND_SOC_WCD934X_MBHC 1 +#define CONFIG_SND_SOC_WCD934X_DSD 1 +#define CONFIG_SND_SOC_WCD_CPE 1 +#define CONFIG_WCD9XXX_CODEC_CORE 1 +#define CONFIG_MSM_CDC_PINCTRL 1 +#define CONFIG_MSM_QDSP6V2_CODECS 1 +#define CONFIG_MSM_ULTRASOUND 1 +#define CONFIG_MSM_QDSP6_APRV2_RPMSG 1 +#define CONFIG_SND_SOC_MSM_QDSP6V2_INTF 1 +#define CONFIG_MSM_ADSP_LOADER 1 +#define CONFIG_REGMAP_SWR 1 +#define CONFIG_MSM_QDSP6_SSR 1 +#define CONFIG_MSM_QDSP6_PDR 1 +#define CONFIG_MSM_QDSP6_NOTIFIER 1 +#define CONFIG_SND_SOC_MSM_HOSTLESS_PCM 1 +#define CONFIG_SND_SOC_SM8150 1 +#define CONFIG_MSM_GLINK_SPI_XPRT 1 +#define CONFIG_WCD_DSP_GLINK 1 +#define CONFIG_SOUNDWIRE 1 +#define CONFIG_SOUNDWIRE_WCD_CTRL 1 +#define CONFIG_SND_SOC_WCD_MBHC_ADC 1 +#define CONFIG_SND_SOC_QDSP6V2 1 +#define CONFIG_QTI_PP 1 +#define CONFIG_SND_HWDEP_ROUTING 1 +#define CONFIG_DTS_EAGLE 1 +#define CONFIG_DOLBY_DS2 1 +#define CONFIG_DOLBY_LICENSE 1 +#define CONFIG_DTS_SRS_TM 1 +#define CONFIG_SND_SOC_MSM_STUB 1 +#define CONFIG_MSM_AVTIMER 1 +#define CONFIG_SND_SOC_MSM_HDMI_CODEC_RX 1 +#define CONFIG_VOICE_MHI 1 diff --git a/audio-kernel/config/trinketauto.conf b/audio-kernel/config/trinketauto.conf new file mode 100644 index 000000000..549b1b7fd --- /dev/null +++ b/audio-kernel/config/trinketauto.conf @@ -0,0 +1,52 @@ +CONFIG_PINCTRL_WCD=m +CONFIG_AUDIO_EXT_CLK=m +CONFIG_SND_SOC_WCD9XXX_V2=m +CONFIG_SND_SOC_WCD_MBHC=m +CONFIG_SND_SOC_WSA881X=m +CONFIG_SND_SOC_WCD_DSP_MGR=m +CONFIG_SND_SOC_WCD_SPI=m +CONFIG_SND_SOC_WCD934X=m +CONFIG_SND_SOC_WCD934X_MBHC=m +CONFIG_SND_SOC_WCD934X_DSD=m +CONFIG_WCD9XXX_CODEC_CORE=m +CONFIG_SND_SOC_WCD_CPE=m +CONFIG_SND_SOC_CPE=m +CONFIG_SND_SOC_WCD9335=m +CONFIG_MSM_CDC_PINCTRL=m +CONFIG_MSM_QDSP6V2_CODECS=m +CONFIG_MSM_ULTRASOUND=m +CONFIG_MSM_QDSP6_APRV2_RPMSG=m +CONFIG_MSM_ADSP_LOADER=m +CONFIG_REGMAP_SWR=m +CONFIG_MSM_QDSP6_SSR=m +CONFIG_MSM_QDSP6_PDR=m +CONFIG_MSM_QDSP6_NOTIFIER=m +CONFIG_SND_SOC_MSM_HOSTLESS_PCM=m +CONFIG_SND_SOC_MSM_QDSP6V2_INTF=m +CONFIG_SND_SOC_SM6150=m +CONFIG_MSM_GLINK_SPI_XPRT=m +CONFIG_WCD_DSP_GLINK=m +CONFIG_SOUNDWIRE=m +CONFIG_SOUNDWIRE_MSTR_CTRL=m +CONFIG_SND_SOC_QDSP6V2=m +CONFIG_SND_SOC_WCD_MBHC_ADC=m +CONFIG_SND_SOC_WCD_MBHC_LEGACY=m +CONFIG_QTI_PP=m +CONFIG_SND_HWDEP_ROUTING=m +CONFIG_DTS_EAGLE=m +CONFIG_DOLBY_DS2=m +CONFIG_DOLBY_LICENSE=m +CONFIG_DTS_SRS_TM=m +CONFIG_SND_SOC_MSM_STUB=m +CONFIG_MSM_AVTIMER=m +CONFIG_AVTIMER_LEGACY=m +CONFIG_SND_SOC_MSM_HDMI_CODEC_RX=m +CONFIG_SND_SOC_BOLERO=m +CONFIG_WSA_MACRO=m +CONFIG_VA_MACRO=m +CONFIG_RX_MACRO=m +CONFIG_TX_MACRO=m +CONFIG_SND_SOC_WCD_IRQ=m +CONFIG_SND_SOC_WCD937X=m +CONFIG_SND_SOC_WCD937X_SLAVE=m +CONFIG_SND_EVENT=m diff --git a/audio-kernel/config/trinketautoconf.h b/audio-kernel/config/trinketautoconf.h new file mode 100644 index 000000000..8315efac7 --- /dev/null +++ b/audio-kernel/config/trinketautoconf.h @@ -0,0 +1,64 @@ +/* Copyright (c) 2019, 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. + */ + +#define CONFIG_PINCTRL_WCD 1 +#define CONFIG_AUDIO_EXT_CLK 1 +#define CONFIG_SND_SOC_WCD9XXX_V2 1 +#define CONFIG_SND_SOC_WCD_MBHC 1 +#define CONFIG_SND_SOC_WSA881X 1 +#define CONFIG_SND_SOC_WCD_DSP_MGR 1 +#define CONFIG_SND_SOC_WCD_SPI 1 +#define CONFIG_SND_SOC_WCD934X 1 +#define CONFIG_SND_SOC_WCD934X_MBHC 1 +#define CONFIG_SND_SOC_WCD934X_DSD 1 +#define CONFIG_SND_SOC_CPE 1 +#define CONFIG_SND_SOC_WCD_CPE 1 +#define CONFIG_SND_SOC_WCD9335 1 +#define CONFIG_WCD9XXX_CODEC_CORE 1 +#define CONFIG_MSM_CDC_PINCTRL 1 +#define CONFIG_MSM_QDSP6V2_CODECS 1 +#define CONFIG_MSM_ULTRASOUND 1 +#define CONFIG_MSM_QDSP6_APRV2_RPMSG 1 +#define CONFIG_SND_SOC_MSM_QDSP6V2_INTF 1 +#define CONFIG_MSM_ADSP_LOADER 1 +#define CONFIG_REGMAP_SWR 1 +#define CONFIG_MSM_QDSP6_SSR 1 +#define CONFIG_MSM_QDSP6_PDR 1 +#define CONFIG_MSM_QDSP6_NOTIFIER 1 +#define CONFIG_SND_SOC_MSM_HOSTLESS_PCM 1 +#define CONFIG_SND_SOC_SM6150 1 +#define CONFIG_MSM_GLINK_SPI_XPRT 1 +#define CONFIG_WCD_DSP_GLINK 1 +#define CONFIG_SOUNDWIRE 1 +#define CONFIG_SOUNDWIRE_MSTR_CTRL 1 +#define CONFIG_SND_SOC_WCD_MBHC_ADC 1 +#define CONFIG_SND_SOC_WCD_MBHC_LEGACY 1 +#define CONFIG_SND_SOC_QDSP6V2 1 +#define CONFIG_QTI_PP 1 +#define CONFIG_SND_HWDEP_ROUTING 1 +#define CONFIG_DTS_EAGLE 1 +#define CONFIG_DOLBY_DS2 1 +#define CONFIG_DOLBY_LICENSE 1 +#define CONFIG_DTS_SRS_TM 1 +#define CONFIG_SND_SOC_MSM_STUB 1 +#define CONFIG_MSM_AVTIMER 1 +#define CONFIG_AVTIMER_LEGACY 1 +#define CONFIG_SND_SOC_MSM_HDMI_CODEC_RX 1 +#define CONFIG_SND_SOC_BOLERO 1 +#define CONFIG_WSA_MACRO 1 +#define CONFIG_VA_MACRO 1 +#define CONFIG_RX_MACRO 1 +#define CONFIG_TX_MACRO 1 +#define CONFIG_SND_SOC_WCD_IRQ 1 +#define CONFIG_SND_SOC_WCD937X 1 +#define CONFIG_SND_SOC_WCD937X_SLAVE 1 +#define CONFIG_SND_EVENT 1 diff --git a/audio-kernel/dsp/Android.mk b/audio-kernel/dsp/Android.mk new file mode 100644 index 000000000..310bd01f1 --- /dev/null +++ b/audio-kernel/dsp/Android.mk @@ -0,0 +1,94 @@ +# Android makefile for audio kernel modules + +# Assume no targets will be supported + +# Check if this driver needs be built for current target +ifeq ($(call is-board-platform,sdm845),true) +AUDIO_SELECT := CONFIG_SND_SOC_SDM845=m +endif + +ifeq ($(call is-board-platform-in-list,msm8953 sdm670 qcs605),true) +AUDIO_SELECT := CONFIG_SND_SOC_SDM670=m +endif + +ifeq ($(call is-board-platform,msmnile),true) +AUDIO_SELECT := CONFIG_SND_SOC_SM8150=m +endif + +ifeq ($(call is-board-platform,$(MSMSTEPPE) $(TRINKET)),true) +AUDIO_SELECT := CONFIG_SND_SOC_SM6150=m +endif + +AUDIO_CHIPSET := audio +# Build/Package only in case of supported target +ifeq ($(call is-board-platform-in-list,msm8953 sdm845 sdm670 qcs605 msmnile $(MSMSTEPPE) $(TRINKET)),true) + +LOCAL_PATH := $(call my-dir) + +# This makefile is only for DLKM +ifneq ($(findstring vendor,$(LOCAL_PATH)),) + +ifneq ($(findstring opensource,$(LOCAL_PATH)),) + AUDIO_BLD_DIR := $(shell pwd)/vendor/qcom/opensource/audio-kernel +endif # opensource + +DLKM_DIR := $(TOP)/device/qcom/common/dlkm + +# Build audio.ko as $(AUDIO_CHIPSET)_audio.ko +########################################################### +# This is set once per LOCAL_PATH, not per (kernel) module +KBUILD_OPTIONS := AUDIO_ROOT=$(AUDIO_BLD_DIR) + +# We are actually building audio.ko here, as per the +# requirement we are specifying _audio.ko as LOCAL_MODULE. +# This means we need to rename the module to _audio.ko +# after audio.ko is built. +KBUILD_OPTIONS += MODNAME=q6_dlkm +KBUILD_OPTIONS += BOARD_PLATFORM=$(TARGET_BOARD_PLATFORM) +KBUILD_OPTIONS += $(AUDIO_SELECT) + +########################################################### +include $(CLEAR_VARS) +LOCAL_MODULE := $(AUDIO_CHIPSET)_q6.ko +LOCAL_MODULE_KBUILD_NAME := q6_dlkm.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_DEBUG_ENABLE := true +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk +########################################################### +include $(CLEAR_VARS) +LOCAL_MODULE := $(AUDIO_CHIPSET)_usf.ko +LOCAL_MODULE_KBUILD_NAME := usf_dlkm.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_DEBUG_ENABLE := true +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk +########################################################### +include $(CLEAR_VARS) +LOCAL_MODULE := $(AUDIO_CHIPSET)_adsp_loader.ko +LOCAL_MODULE_KBUILD_NAME := adsp_loader_dlkm.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_DEBUG_ENABLE := true +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk +########################################################### +include $(CLEAR_VARS) +LOCAL_MODULE := $(AUDIO_CHIPSET)_q6_notifier.ko +LOCAL_MODULE_KBUILD_NAME := q6_notifier_dlkm.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_DEBUG_ENABLE := true +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk +########################################################### +include $(CLEAR_VARS) +LOCAL_MODULE := $(AUDIO_CHIPSET)_q6_pdr.ko +LOCAL_MODULE_KBUILD_NAME := q6_pdr_dlkm.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_DEBUG_ENABLE := true +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk +########################################################### +########################################################### + +endif # DLKM check +endif # supported target check diff --git a/audio-kernel/dsp/Kbuild b/audio-kernel/dsp/Kbuild new file mode 100644 index 000000000..17d883a43 --- /dev/null +++ b/audio-kernel/dsp/Kbuild @@ -0,0 +1,215 @@ +# We can build either as part of a standalone Kernel build or as +# an external module. Determine which mechanism is being used +ifeq ($(MODNAME), ) + KERNEL_BUILD := 1 +else + KERNEL_BUILD := 0 +endif + +ifeq ($(KERNEL_BUILD), 1) + # These are configurable via Kconfig for kernel-based builds + # Need to explicitly configure for Android-based builds + AUDIO_BLD_DIR := $(shell pwd)/kernel/msm-4.14 + AUDIO_ROOT := $(AUDIO_BLD_DIR)/techpack/audio +endif + +ifeq ($(KERNEL_BUILD), 0) + ifeq ($(CONFIG_ARCH_SDM845), y) + include $(AUDIO_ROOT)/config/sdm845auto.conf + export + INCS += -include $(AUDIO_ROOT)/config/sdm845autoconf.h + endif + ifeq ($(CONFIG_ARCH_SDM670), y) + include $(AUDIO_ROOT)/config/sdm670auto.conf + export + INCS += -include $(AUDIO_ROOT)/config/sdm670autoconf.h + endif + ifeq ($(CONFIG_ARCH_SDM450), y) + include $(AUDIO_ROOT)/config/sdm670auto.conf + export + INCS += -include $(AUDIO_ROOT)/config/sdm670autoconf.h + endif + ifeq ($(CONFIG_ARCH_SM6150), y) + include $(AUDIO_ROOT)/config/sm6150auto.conf + export + INCS += -include $(AUDIO_ROOT)/config/sm6150autoconf.h + endif + ifeq ($(CONFIG_ARCH_TRINKET), y) + include $(AUDIO_ROOT)/config/trinketauto.conf + export + INCS += -include $(AUDIO_ROOT)/config/trinketautoconf.h + endif + ifeq ($(CONFIG_ARCH_SM8150), y) + include $(AUDIO_ROOT)/config/sm8150auto.conf + export + INCS += -include $(AUDIO_ROOT)/config/sm8150autoconf.h + endif + ifeq ($(CONFIG_ARCH_SDMSHRIKE), y) + include $(AUDIO_ROOT)/config/sm8150auto.conf + export + INCS += -include $(AUDIO_ROOT)/config/sm8150autoconf.h + endif + ifeq ($(CONFIG_ARCH_QCS405), y) + include $(AUDIO_ROOT)/config/qcs405auto.conf + export + INCS += -include $(AUDIO_ROOT)/config/qcs405autoconf.h + endif +endif + + + +# As per target team, build is done as follows: +# Defconfig : build with default flags +# Slub : defconfig + CONFIG_SLUB_DEBUG := y + +# CONFIG_SLUB_DEBUG_ON := y + CONFIG_PAGE_POISONING := y +# Perf : Using appropriate msmXXXX-perf_defconfig +# +# Shipment builds (user variants) should not have any debug feature +# enabled. This is identified using 'TARGET_BUILD_VARIANT'. Slub builds +# are identified using the CONFIG_SLUB_DEBUG_ON configuration. Since +# there is no other way to identify defconfig builds, QTI internal +# representation of perf builds (identified using the string 'perf'), +# is used to identify if the build is a slub or defconfig one. This +# way no critical debug feature will be enabled for perf and shipment +# builds. Other OEMs are also protected using the TARGET_BUILD_VARIANT +# config. + +############ UAPI ############ +UAPI_DIR := uapi +UAPI_INC := -I$(AUDIO_ROOT)/include/$(UAPI_DIR) + +############ COMMON ############ +COMMON_DIR := include +COMMON_INC := -I$(AUDIO_ROOT)/$(COMMON_DIR) + +############ QDSP6V2 ############ + +ifdef CONFIG_SND_SOC_MSM_QDSP6V2_INTF + Q6_OBJS += msm-audio-event-notify.o + Q6_OBJS += audio_calibration.o + Q6_OBJS += audio_cal_utils.o + Q6_OBJS += q6adm.o + Q6_OBJS += q6afe.o + Q6_OBJS += q6asm.o + Q6_OBJS += q6audio-v2.o + Q6_OBJS += q6voice.o + Q6_OBJS += q6core.o + Q6_OBJS += q6common.o + Q6_OBJS += rtac.o + Q6_OBJS += q6lsm.o + Q6_OBJS += audio_slimslave.o + Q6_OBJS += adsp_err.o + Q6_OBJS += msm_audio_ion.o + Q6_OBJS += avtimer.o + Q6_OBJS += q6_init.o +endif + +ifdef CONFIG_XT_LOGGING + Q6_OBJS += sp_params.o +endif + +ifdef CONFIG_DTS_SRS_TM + Q6_OBJS += msm-dts-srs-tm-config.o +endif + +ifdef CONFIG_AFE_HWDEP + Q6_OBJS += q6afecal-hwdep.o +endif + +ifdef CONFIG_MSM_ADSP_LOADER +ADSP_LOADER_OBJS += adsp-loader.o +endif + +ifdef CONFIG_MSM_QDSP6_PDR +QDSP6_PDR_OBJS += audio_pdr.o +endif + +ifdef CONFIG_MSM_QDSP6_NOTIFIER +QDSP6_NOTIFIER_OBJS += audio_notifier.o audio_ssr.o +endif + +ifdef CONFIG_MSM_ULTRASOUND +USF_OBJS += usf.o usfcdev.o q6usm.o +endif + +ifdef CONFIG_MSM_MDF + Q6_OBJS += msm_mdf.o +endif + +ifdef CONFIG_VOICE_MHI + Q6_OBJS += voice_mhi.o +endif + +LINUX_INC += -Iinclude/linux + +INCS += $(COMMON_INC) \ + $(UAPI_INC) + +EXTRA_CFLAGS += $(INCS) + + +CDEFINES += -DANI_LITTLE_BYTE_ENDIAN \ + -DANI_LITTLE_BIT_ENDIAN \ + -DDOT11F_LITTLE_ENDIAN_HOST \ + -DANI_COMPILER_TYPE_GCC \ + -DANI_OS_TYPE_ANDROID=6 \ + -DPTT_SOCK_SVC_ENABLE \ + -Wall\ + -Werror\ + -D__linux__ + +KBUILD_CPPFLAGS += $(CDEFINES) + +# Currently, for versions of gcc which support it, the kernel Makefile +# is disabling the maybe-uninitialized warning. Re-enable it for the +# AUDIO driver. Note that we must use EXTRA_CFLAGS here so that it +# will override the kernel settings. +ifeq ($(call cc-option-yn, -Wmaybe-uninitialized),y) +EXTRA_CFLAGS += -Wmaybe-uninitialized +endif +#EXTRA_CFLAGS += -Wmissing-prototypes + +ifeq ($(call cc-option-yn, -Wheader-guard),y) +EXTRA_CFLAGS += -Wheader-guard +endif + +# If the module name is not "wlan", then the define MULTI_IF_NAME to be the +# same a the QCA CHIP name. The host driver will then append MULTI_IF_NAME to +# any string that must be unique for all instances of the driver on the system. +# This allows multiple instances of the driver with different module names. +# If the module name is wlan, leave MULTI_IF_NAME undefined and the code will +# treat the driver as the primary driver. +ifneq ($(MODNAME), qdsp6v2) +CHIP_NAME ?= $(MODNAME) +CDEFINES += -DMULTI_IF_NAME=\"$(CHIP_NAME)\" +endif + +ifeq ($(KERNEL_BUILD), 0) +KBUILD_EXTRA_SYMBOLS +=$(OUT)/obj/vendor/qcom/opensource/audio-kernel/ipc/Module.symvers +endif + +ifeq ($(KERNEL_BUILD), 1) + obj-y += codecs/ +endif + +ifeq ($(CONFIG_SND_SOC_GCOV), y) +GCOV_PROFILE := y +endif + +obj-$(CONFIG_SND_SOC_MSM_QDSP6V2_INTF) += q6_dlkm.o +q6_dlkm-y := $(Q6_OBJS) + +obj-$(CONFIG_MSM_ULTRASOUND) += usf_dlkm.o +usf_dlkm-y := $(USF_OBJS) + +obj-$(CONFIG_MSM_ADSP_LOADER) += adsp_loader_dlkm.o +adsp_loader_dlkm-y := $(ADSP_LOADER_OBJS) + +obj-$(CONFIG_MSM_QDSP6_PDR) += q6_pdr_dlkm.o +q6_pdr_dlkm-y := $(QDSP6_PDR_OBJS) + +obj-$(CONFIG_MSM_QDSP6_NOTIFIER) += q6_notifier_dlkm.o +q6_notifier_dlkm-y := $(QDSP6_NOTIFIER_OBJS) + +# inject some build related information +DEFINES += -DBUILD_TIMESTAMP=\"$(shell date -u +'%Y-%m-%dT%H:%M:%SZ')\" diff --git a/audio-kernel/dsp/adsp-loader.c b/audio-kernel/dsp/adsp-loader.c new file mode 100644 index 000000000..5afd48cd8 --- /dev/null +++ b/audio-kernel/dsp/adsp-loader.c @@ -0,0 +1,362 @@ +/* + * Copyright (c) 2012-2014, 2017-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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define Q6_PIL_GET_DELAY_MS 100 +#define BOOT_CMD 1 +#define SSR_RESET_CMD 1 +#define IMAGE_UNLOAD_CMD 0 + +static ssize_t adsp_boot_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t count); + +static ssize_t adsp_ssr_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t count); + +struct adsp_loader_private { + void *pil_h; + struct kobject *boot_adsp_obj; + struct attribute_group *attr_group; +}; + +static struct kobj_attribute adsp_boot_attribute = + __ATTR(boot, 0220, NULL, adsp_boot_store); + +static struct kobj_attribute adsp_ssr_attribute = + __ATTR(ssr, 0220, NULL, adsp_ssr_store); + +static struct attribute *attrs[] = { + &adsp_boot_attribute.attr, + &adsp_ssr_attribute.attr, + NULL, +}; + +static struct work_struct adsp_ldr_work; +static struct platform_device *adsp_private; +static void adsp_loader_unload(struct platform_device *pdev); + +static void adsp_load_fw(struct work_struct *adsp_ldr_work) +{ + struct platform_device *pdev = adsp_private; + struct adsp_loader_private *priv = NULL; + + const char *adsp_dt = "qcom,adsp-state"; + int rc = 0; + u32 adsp_state; + const char *img_name; + + if (!pdev) { + dev_err(&pdev->dev, "%s: Platform device null\n", __func__); + goto fail; + } + + if (!pdev->dev.of_node) { + dev_err(&pdev->dev, + "%s: Device tree information missing\n", __func__); + goto fail; + } + + rc = of_property_read_u32(pdev->dev.of_node, adsp_dt, &adsp_state); + if (rc) { + dev_err(&pdev->dev, + "%s: ADSP state = %x\n", __func__, adsp_state); + goto fail; + } + + rc = of_property_read_string(pdev->dev.of_node, + "qcom,proc-img-to-load", + &img_name); + + if (rc) { + dev_dbg(&pdev->dev, + "%s: loading default image ADSP\n", __func__); + goto load_adsp; + } + if (!strcmp(img_name, "modem")) { + /* adsp_state always returns "0". So load modem image based on + * apr_modem_state to prevent loading of image twice + */ + adsp_state = apr_get_modem_state(); + if (adsp_state == APR_SUBSYS_DOWN) { + priv = platform_get_drvdata(pdev); + if (!priv) { + dev_err(&pdev->dev, + " %s: Private data get failed\n", __func__); + goto fail; + } + + priv->pil_h = subsystem_get("modem"); + if (IS_ERR(priv->pil_h)) { + dev_err(&pdev->dev, "%s: pil get failed,\n", + __func__); + goto fail; + } + + /* Set the state of the ADSP in APR driver */ + apr_set_modem_state(APR_SUBSYS_LOADED); + } else if (adsp_state == APR_SUBSYS_LOADED) { + dev_dbg(&pdev->dev, + "%s: MDSP state = %x\n", __func__, adsp_state); + } + + dev_dbg(&pdev->dev, "%s: Q6/MDSP image is loaded\n", __func__); + return; + } +load_adsp: + { + adsp_state = apr_get_q6_state(); + if (adsp_state == APR_SUBSYS_DOWN) { + priv = platform_get_drvdata(pdev); + if (!priv) { + dev_err(&pdev->dev, + " %s: Private data get failed\n", __func__); + goto fail; + } + + priv->pil_h = subsystem_get("adsp"); + if (IS_ERR(priv->pil_h)) { + dev_err(&pdev->dev, "%s: pil get failed,\n", + __func__); + goto fail; + } + } else if (adsp_state == APR_SUBSYS_LOADED) { + dev_dbg(&pdev->dev, + "%s: ADSP state = %x\n", __func__, adsp_state); + } + + dev_dbg(&pdev->dev, "%s: Q6/ADSP image is loaded\n", __func__); + return; + } +fail: + dev_err(&pdev->dev, "%s: Q6 image loading failed\n", __func__); +} + +static void adsp_loader_do(struct platform_device *pdev) +{ + schedule_work(&adsp_ldr_work); +} + +static ssize_t adsp_ssr_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, + size_t count) +{ + int ssr_command = 0; + struct subsys_device *adsp_dev = NULL; + struct platform_device *pdev = adsp_private; + struct adsp_loader_private *priv = NULL; + int rc; + + dev_dbg(&pdev->dev, "%s: going to call adsp ssr\n ", __func__); + + if (kstrtoint(buf, 10, &ssr_command) < 0) + return -EINVAL; + + if (ssr_command != SSR_RESET_CMD) + return -EINVAL; + + priv = platform_get_drvdata(pdev); + if (!priv) + return -EINVAL; + + adsp_dev = (struct subsys_device *)priv->pil_h; + if (!adsp_dev) + return -EINVAL; + + dev_err(&pdev->dev, "requesting for ADSP restart\n"); + + /* subsystem_restart_dev has worker queue to handle */ + rc = subsystem_restart_dev(adsp_dev); + if (rc) { + dev_err(&pdev->dev, "subsystem_restart_dev failed\n"); + return rc; + } + + dev_dbg(&pdev->dev, "ADSP restarted\n"); + return count; +} + +static ssize_t adsp_boot_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, + size_t count) +{ + int boot = 0; + + if (sscanf(buf, "%du", &boot) != 1) { + pr_err("%s: failed to read boot info from string\n", __func__); + return -EINVAL; + } + + if (boot == BOOT_CMD) { + pr_debug("%s: going to call adsp_loader_do\n", __func__); + adsp_loader_do(adsp_private); + } else if (boot == IMAGE_UNLOAD_CMD) { + pr_debug("%s: going to call adsp_unloader\n", __func__); + adsp_loader_unload(adsp_private); + } + return count; +} + +static void adsp_loader_unload(struct platform_device *pdev) +{ + struct adsp_loader_private *priv = NULL; + + priv = platform_get_drvdata(pdev); + + if (!priv) + return; + + if (priv->pil_h) { + dev_dbg(&pdev->dev, "%s: calling subsystem put\n", __func__); + subsystem_put(priv->pil_h); + priv->pil_h = NULL; + } +} + +static int adsp_loader_init_sysfs(struct platform_device *pdev) +{ + int ret = -EINVAL; + struct adsp_loader_private *priv = NULL; + + adsp_private = NULL; + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) { + ret = -ENOMEM; + return ret; + } + + platform_set_drvdata(pdev, priv); + + priv->pil_h = NULL; + priv->boot_adsp_obj = NULL; + priv->attr_group = devm_kzalloc(&pdev->dev, + sizeof(*(priv->attr_group)), + GFP_KERNEL); + if (!priv->attr_group) { + ret = -ENOMEM; + goto error_return; + } + + priv->attr_group->attrs = attrs; + + priv->boot_adsp_obj = kobject_create_and_add("boot_adsp", kernel_kobj); + if (!priv->boot_adsp_obj) { + dev_err(&pdev->dev, "%s: sysfs create and add failed\n", + __func__); + ret = -ENOMEM; + goto error_return; + } + + ret = sysfs_create_group(priv->boot_adsp_obj, priv->attr_group); + if (ret) { + dev_err(&pdev->dev, "%s: sysfs create group failed %d\n", + __func__, ret); + goto error_return; + } + + adsp_private = pdev; + + return 0; + +error_return: + + if (priv->boot_adsp_obj) { + kobject_del(priv->boot_adsp_obj); + priv->boot_adsp_obj = NULL; + } + + return ret; +} + +static int adsp_loader_remove(struct platform_device *pdev) +{ + struct adsp_loader_private *priv = NULL; + + priv = platform_get_drvdata(pdev); + + if (!priv) + return 0; + + if (priv->pil_h) { + subsystem_put(priv->pil_h); + priv->pil_h = NULL; + } + + if (priv->boot_adsp_obj) { + sysfs_remove_group(priv->boot_adsp_obj, priv->attr_group); + kobject_del(priv->boot_adsp_obj); + priv->boot_adsp_obj = NULL; + } + + return 0; +} + +static int adsp_loader_probe(struct platform_device *pdev) +{ + int ret = adsp_loader_init_sysfs(pdev); + + if (ret != 0) { + dev_err(&pdev->dev, "%s: Error in initing sysfs\n", __func__); + return ret; + } + + INIT_WORK(&adsp_ldr_work, adsp_load_fw); + + return 0; +} + +static const struct of_device_id adsp_loader_dt_match[] = { + { .compatible = "qcom,adsp-loader" }, + { } +}; +MODULE_DEVICE_TABLE(of, adsp_loader_dt_match); + +static struct platform_driver adsp_loader_driver = { + .driver = { + .name = "adsp-loader", + .owner = THIS_MODULE, + .of_match_table = adsp_loader_dt_match, + }, + .probe = adsp_loader_probe, + .remove = adsp_loader_remove, +}; + +static int __init adsp_loader_init(void) +{ + return platform_driver_register(&adsp_loader_driver); +} +module_init(adsp_loader_init); + +static void __exit adsp_loader_exit(void) +{ + platform_driver_unregister(&adsp_loader_driver); +} +module_exit(adsp_loader_exit); + +MODULE_DESCRIPTION("ADSP Loader module"); +MODULE_LICENSE("GPL v2"); diff --git a/audio-kernel/dsp/adsp_err.c b/audio-kernel/dsp/adsp_err.c new file mode 100644 index 000000000..e34d76b6a --- /dev/null +++ b/audio-kernel/dsp/adsp_err.c @@ -0,0 +1,173 @@ +/* + * Copyright (c) 2016-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 + * 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 +#include +#include +#include + + +/* ERROR STRING */ +/* Success. The operation completed with no errors. */ +#define ADSP_EOK_STR "ADSP_EOK" +/* General failure. */ +#define ADSP_EFAILED_STR "ADSP_EFAILED" +/* Bad operation parameter. */ +#define ADSP_EBADPARAM_STR "ADSP_EBADPARAM" +/* Unsupported routine or operation. */ +#define ADSP_EUNSUPPORTED_STR "ADSP_EUNSUPPORTED" +/* Unsupported version. */ +#define ADSP_EVERSION_STR "ADSP_EVERSION" +/* Unexpected problem encountered. */ +#define ADSP_EUNEXPECTED_STR "ADSP_EUNEXPECTED" +/* Unhandled problem occurred. */ +#define ADSP_EPANIC_STR "ADSP_EPANIC" +/* Unable to allocate resource. */ +#define ADSP_ENORESOURCE_STR "ADSP_ENORESOURCE" +/* Invalid handle. */ +#define ADSP_EHANDLE_STR "ADSP_EHANDLE" +/* Operation is already processed. */ +#define ADSP_EALREADY_STR "ADSP_EALREADY" +/* Operation is not ready to be processed. */ +#define ADSP_ENOTREADY_STR "ADSP_ENOTREADY" +/* Operation is pending completion. */ +#define ADSP_EPENDING_STR "ADSP_EPENDING" +/* Operation could not be accepted or processed. */ +#define ADSP_EBUSY_STR "ADSP_EBUSY" +/* Operation aborted due to an error. */ +#define ADSP_EABORTED_STR "ADSP_EABORTED" +/* Operation preempted by a higher priority. */ +#define ADSP_EPREEMPTED_STR "ADSP_EPREEMPTED" +/* Operation requests intervention to complete. */ +#define ADSP_ECONTINUE_STR "ADSP_ECONTINUE" +/* Operation requests immediate intervention to complete. */ +#define ADSP_EIMMEDIATE_STR "ADSP_EIMMEDIATE" +/* Operation is not implemented. */ +#define ADSP_ENOTIMPL_STR "ADSP_ENOTIMPL" +/* Operation needs more data or resources. */ +#define ADSP_ENEEDMORE_STR "ADSP_ENEEDMORE" +/* Operation does not have memory. */ +#define ADSP_ENOMEMORY_STR "ADSP_ENOMEMORY" +/* Item does not exist. */ +#define ADSP_ENOTEXIST_STR "ADSP_ENOTEXIST" +/* Unexpected error code. */ +#define ADSP_ERR_MAX_STR "ADSP_ERR_MAX" + +#if IS_ENABLED(CONFIG_SND_SOC_QDSP_DEBUG) +static bool adsp_err_panic; + +#ifdef CONFIG_DEBUG_FS +static struct dentry *debugfs_adsp_err; + +static ssize_t adsp_err_debug_write(struct file *filp, + const char __user *ubuf, size_t cnt, loff_t *ppos) +{ + char cmd; + + if (copy_from_user(&cmd, ubuf, 1)) + return -EFAULT; + + if (cmd == '0') + adsp_err_panic = false; + else + adsp_err_panic = true; + + return cnt; +} + +static const struct file_operations adsp_err_debug_ops = { + .write = adsp_err_debug_write, +}; +#endif +#endif + +struct adsp_err_code { + int lnx_err_code; + char *adsp_err_str; +}; + + +static struct adsp_err_code adsp_err_code_info[ADSP_ERR_MAX+1] = { + { 0, ADSP_EOK_STR}, + { -ENOTRECOVERABLE, ADSP_EFAILED_STR}, + { -EINVAL, ADSP_EBADPARAM_STR}, + { -EOPNOTSUPP, ADSP_EUNSUPPORTED_STR}, + { -ENOPROTOOPT, ADSP_EVERSION_STR}, + { -ENOTRECOVERABLE, ADSP_EUNEXPECTED_STR}, + { -ENOTRECOVERABLE, ADSP_EPANIC_STR}, + { -ENOSPC, ADSP_ENORESOURCE_STR}, + { -EBADR, ADSP_EHANDLE_STR}, + { -EALREADY, ADSP_EALREADY_STR}, + { -EPERM, ADSP_ENOTREADY_STR}, + { -EINPROGRESS, ADSP_EPENDING_STR}, + { -EBUSY, ADSP_EBUSY_STR}, + { -ECANCELED, ADSP_EABORTED_STR}, + { -EAGAIN, ADSP_EPREEMPTED_STR}, + { -EAGAIN, ADSP_ECONTINUE_STR}, + { -EAGAIN, ADSP_EIMMEDIATE_STR}, + { -EAGAIN, ADSP_ENOTIMPL_STR}, + { -ENODATA, ADSP_ENEEDMORE_STR}, + { -EADV, ADSP_ERR_MAX_STR}, + { -ENOMEM, ADSP_ENOMEMORY_STR}, + { -ENODEV, ADSP_ENOTEXIST_STR}, + { -EADV, ADSP_ERR_MAX_STR}, +}; + +#if IS_ENABLED(CONFIG_SND_SOC_QDSP_DEBUG) +static inline void adsp_err_check_panic(u32 adsp_error) +{ + if (adsp_err_panic && adsp_error != ADSP_EALREADY) + panic("%s: encounter adsp_err=0x%x\n", __func__, adsp_error); +} +#else +static inline void adsp_err_check_panic(u32 adsp_error) {} +#endif + +int adsp_err_get_lnx_err_code(u32 adsp_error) +{ + adsp_err_check_panic(adsp_error); + + if (adsp_error > ADSP_ERR_MAX) + return adsp_err_code_info[ADSP_ERR_MAX].lnx_err_code; + else + return adsp_err_code_info[adsp_error].lnx_err_code; +} + +char *adsp_err_get_err_str(u32 adsp_error) +{ + if (adsp_error > ADSP_ERR_MAX) + return adsp_err_code_info[ADSP_ERR_MAX].adsp_err_str; + else + return adsp_err_code_info[adsp_error].adsp_err_str; +} + +#if IS_ENABLED(CONFIG_SND_SOC_QDSP_DEBUG) && defined(CONFIG_DEBUG_FS) +int __init adsp_err_init(void) +{ + + + debugfs_adsp_err = debugfs_create_file("msm_adsp_audio_debug", + S_IFREG | 0444, NULL, NULL, + &adsp_err_debug_ops); + + return 0; +} +#else +int __init adsp_err_init(void) { return 0; } + +#endif + +void adsp_err_exit(void) +{ + return; +} diff --git a/audio-kernel/dsp/adsp_err.h b/audio-kernel/dsp/adsp_err.h new file mode 100644 index 000000000..43be91d6b --- /dev/null +++ b/audio-kernel/dsp/adsp_err.h @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2015-2016, 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 __ADSP_ERR__ +#define __ADSP_ERR__ + +int adsp_err_get_lnx_err_code(u32 adsp_error); + +char *adsp_err_get_err_str(u32 adsp_error); + +#endif diff --git a/audio-kernel/dsp/audio_cal_utils.c b/audio-kernel/dsp/audio_cal_utils.c new file mode 100644 index 000000000..d8c4ea167 --- /dev/null +++ b/audio-kernel/dsp/audio_cal_utils.c @@ -0,0 +1,1078 @@ +/* Copyright (c) 2014-2019, 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 +#include +#include +#include +#include +#include +#include + +static int unmap_memory(struct cal_type_data *cal_type, + struct cal_block_data *cal_block); + +size_t get_cal_info_size(int32_t cal_type) +{ + size_t size = 0; + size_t size1 = 0, size2 = 0; + + switch (cal_type) { + case CVP_VOC_RX_TOPOLOGY_CAL_TYPE: + size = sizeof(struct audio_cal_info_voc_top); + break; + case CVP_VOC_TX_TOPOLOGY_CAL_TYPE: + size = sizeof(struct audio_cal_info_voc_top); + break; + case CVP_VOCPROC_STATIC_CAL_TYPE: + size = sizeof(struct audio_cal_info_vocproc); + break; + case CVP_VOCPROC_DYNAMIC_CAL_TYPE: + size = sizeof(struct audio_cal_info_vocvol); + break; + case CVS_VOCSTRM_STATIC_CAL_TYPE: + size = 0; + break; + case CVP_VOCDEV_CFG_CAL_TYPE: + size = sizeof(struct audio_cal_info_vocdev_cfg); + break; + case CVP_VOCPROC_STATIC_COL_CAL_TYPE: + size = sizeof(struct audio_cal_info_voc_col); + break; + case CVP_VOCPROC_DYNAMIC_COL_CAL_TYPE: + size = sizeof(struct audio_cal_info_voc_col); + break; + case CVS_VOCSTRM_STATIC_COL_CAL_TYPE: + size = sizeof(struct audio_cal_info_voc_col); + break; + case ADM_TOPOLOGY_CAL_TYPE: + case ADM_LSM_TOPOLOGY_CAL_TYPE: + size = sizeof(struct audio_cal_info_adm_top); + break; + case ADM_CUST_TOPOLOGY_CAL_TYPE: + case CORE_CUSTOM_TOPOLOGIES_CAL_TYPE: + size = 0; + break; + case ADM_AUDPROC_CAL_TYPE: + case ADM_LSM_AUDPROC_CAL_TYPE: + case ADM_LSM_AUDPROC_PERSISTENT_CAL_TYPE: + size = sizeof(struct audio_cal_info_audproc); + break; + case ADM_AUDVOL_CAL_TYPE: + case ADM_RTAC_AUDVOL_CAL_TYPE: + size = sizeof(struct audio_cal_info_audvol); + break; + case ASM_TOPOLOGY_CAL_TYPE: + size = sizeof(struct audio_cal_info_asm_top); + break; + case ASM_CUST_TOPOLOGY_CAL_TYPE: + size = 0; + break; + case ASM_AUDSTRM_CAL_TYPE: + size = sizeof(struct audio_cal_info_audstrm); + break; + case AFE_TOPOLOGY_CAL_TYPE: + case AFE_LSM_TOPOLOGY_CAL_TYPE: + size = sizeof(struct audio_cal_info_afe_top); + break; + case AFE_CUST_TOPOLOGY_CAL_TYPE: + size = 0; + break; + case AFE_COMMON_RX_CAL_TYPE: + size = sizeof(struct audio_cal_info_afe); + break; + case AFE_COMMON_TX_CAL_TYPE: + case AFE_LSM_TX_CAL_TYPE: + size = sizeof(struct audio_cal_info_afe); + break; + case AFE_FB_SPKR_PROT_CAL_TYPE: + size = sizeof(struct audio_cal_info_spk_prot_cfg); + break; + case AFE_FB_SPKR_PROT_TH_VI_CAL_TYPE: + /* + * Since get and set parameter structures are different in size + * use the maximum size of get and set parameter structure + */ + size1 = max(sizeof(struct audio_cal_info_sp_th_vi_ftm_cfg), + sizeof(struct audio_cal_info_sp_th_vi_param)); + size2 = max(sizeof(struct audio_cal_info_sp_th_vi_v_vali_cfg), + sizeof(struct audio_cal_info_sp_th_vi_v_vali_param)); + size = max(size1, size2); + break; + case AFE_FB_SPKR_PROT_EX_VI_CAL_TYPE: + /* + * Since get and set parameter structures are different in size + * use the maximum size of get and set parameter structure + */ + size = max(sizeof(struct audio_cal_info_sp_ex_vi_ftm_cfg), + sizeof(struct audio_cal_info_sp_ex_vi_param)); + break; + case AFE_ANC_CAL_TYPE: + size = 0; + break; + case AFE_AANC_CAL_TYPE: + size = sizeof(struct audio_cal_info_aanc); + break; + case AFE_HW_DELAY_CAL_TYPE: + size = sizeof(struct audio_cal_info_hw_delay); + break; + case AFE_SIDETONE_CAL_TYPE: + size = sizeof(struct audio_cal_info_sidetone); + break; + case AFE_SIDETONE_IIR_CAL_TYPE: + size = sizeof(struct audio_cal_info_sidetone_iir); + break; + case LSM_CUST_TOPOLOGY_CAL_TYPE: + size = 0; + break; + case LSM_TOPOLOGY_CAL_TYPE: + size = sizeof(struct audio_cal_info_lsm_top); + break; + case ULP_LSM_TOPOLOGY_ID_CAL_TYPE: + size = sizeof(struct audio_cal_info_lsm_top); + break; + case LSM_CAL_TYPE: + size = sizeof(struct audio_cal_info_lsm); + break; + case ADM_RTAC_INFO_CAL_TYPE: + size = 0; + break; + case VOICE_RTAC_INFO_CAL_TYPE: + size = 0; + break; + case ADM_RTAC_APR_CAL_TYPE: + size = 0; + break; + case ASM_RTAC_APR_CAL_TYPE: + size = 0; + break; + case VOICE_RTAC_APR_CAL_TYPE: + size = 0; + break; + case MAD_CAL_TYPE: + size = 0; + break; + case ULP_AFE_CAL_TYPE: + size = sizeof(struct audio_cal_info_afe); + break; + case ULP_LSM_CAL_TYPE: + size = sizeof(struct audio_cal_info_lsm); + break; + case AUDIO_CORE_METAINFO_CAL_TYPE: + size = sizeof(struct audio_cal_info_metainfo); + break; + case SRS_TRUMEDIA_CAL_TYPE: + size = 0; + break; + default: + pr_err("%s:Invalid cal type %d!", + __func__, cal_type); + } + return size; +} + +size_t get_user_cal_type_size(int32_t cal_type) +{ + size_t size = 0; + + switch (cal_type) { + case CVP_VOC_RX_TOPOLOGY_CAL_TYPE: + size = sizeof(struct audio_cal_type_voc_top); + break; + case CVP_VOC_TX_TOPOLOGY_CAL_TYPE: + size = sizeof(struct audio_cal_type_voc_top); + break; + case CVP_VOCPROC_STATIC_CAL_TYPE: + size = sizeof(struct audio_cal_type_vocproc); + break; + case CVP_VOCPROC_DYNAMIC_CAL_TYPE: + size = sizeof(struct audio_cal_type_vocvol); + break; + case CVS_VOCSTRM_STATIC_CAL_TYPE: + size = sizeof(struct audio_cal_type_basic); + break; + case CVP_VOCDEV_CFG_CAL_TYPE: + size = sizeof(struct audio_cal_type_vocdev_cfg); + break; + case CVP_VOCPROC_STATIC_COL_CAL_TYPE: + case CVP_VOCPROC_DYNAMIC_COL_CAL_TYPE: + case CVS_VOCSTRM_STATIC_COL_CAL_TYPE: + size = sizeof(struct audio_cal_type_voc_col); + break; + case ADM_TOPOLOGY_CAL_TYPE: + case ADM_LSM_TOPOLOGY_CAL_TYPE: + size = sizeof(struct audio_cal_type_adm_top); + break; + case ADM_CUST_TOPOLOGY_CAL_TYPE: + case CORE_CUSTOM_TOPOLOGIES_CAL_TYPE: + size = sizeof(struct audio_cal_type_basic); + break; + case ADM_AUDPROC_CAL_TYPE: + case ADM_LSM_AUDPROC_CAL_TYPE: + case ADM_LSM_AUDPROC_PERSISTENT_CAL_TYPE: + size = sizeof(struct audio_cal_type_audproc); + break; + case ADM_AUDVOL_CAL_TYPE: + case ADM_RTAC_AUDVOL_CAL_TYPE: + size = sizeof(struct audio_cal_type_audvol); + break; + case ASM_TOPOLOGY_CAL_TYPE: + size = sizeof(struct audio_cal_type_asm_top); + break; + case ASM_CUST_TOPOLOGY_CAL_TYPE: + size = sizeof(struct audio_cal_type_basic); + break; + case ASM_AUDSTRM_CAL_TYPE: + size = sizeof(struct audio_cal_type_audstrm); + break; + case AFE_TOPOLOGY_CAL_TYPE: + case AFE_LSM_TOPOLOGY_CAL_TYPE: + size = sizeof(struct audio_cal_type_afe_top); + break; + case AFE_CUST_TOPOLOGY_CAL_TYPE: + size = sizeof(struct audio_cal_type_basic); + break; + case AFE_COMMON_RX_CAL_TYPE: + size = sizeof(struct audio_cal_type_afe); + break; + case AFE_COMMON_TX_CAL_TYPE: + case AFE_LSM_TX_CAL_TYPE: + size = sizeof(struct audio_cal_type_afe); + break; + case AFE_FB_SPKR_PROT_CAL_TYPE: + size = sizeof(struct audio_cal_type_fb_spk_prot_cfg); + break; + case AFE_FB_SPKR_PROT_TH_VI_CAL_TYPE: + /* + * Since get and set parameter structures are different in size + * use the maximum size of get and set parameter structure + */ + size = max(sizeof(struct audio_cal_type_sp_th_vi_ftm_cfg), + sizeof(struct audio_cal_type_sp_th_vi_param)); + break; + case AFE_FB_SPKR_PROT_EX_VI_CAL_TYPE: + /* + * Since get and set parameter structures are different in size + * use the maximum size of get and set parameter structure + */ + size = max(sizeof(struct audio_cal_type_sp_ex_vi_ftm_cfg), + sizeof(struct audio_cal_type_sp_ex_vi_param)); + break; + case AFE_ANC_CAL_TYPE: + size = 0; + break; + case AFE_AANC_CAL_TYPE: + size = sizeof(struct audio_cal_type_aanc); + break; + case AFE_HW_DELAY_CAL_TYPE: + size = sizeof(struct audio_cal_type_hw_delay); + break; + case AFE_SIDETONE_CAL_TYPE: + size = sizeof(struct audio_cal_type_sidetone); + break; + case AFE_SIDETONE_IIR_CAL_TYPE: + size = sizeof(struct audio_cal_type_sidetone_iir); + break; + case LSM_CUST_TOPOLOGY_CAL_TYPE: + size = sizeof(struct audio_cal_type_basic); + break; + case LSM_TOPOLOGY_CAL_TYPE: + size = sizeof(struct audio_cal_type_lsm_top); + break; + case ULP_LSM_TOPOLOGY_ID_CAL_TYPE: + size = sizeof(struct audio_cal_type_lsm_top); + break; + case LSM_CAL_TYPE: + size = sizeof(struct audio_cal_type_lsm); + break; + case ADM_RTAC_INFO_CAL_TYPE: + size = 0; + break; + case VOICE_RTAC_INFO_CAL_TYPE: + size = 0; + break; + case ADM_RTAC_APR_CAL_TYPE: + size = 0; + break; + case ASM_RTAC_APR_CAL_TYPE: + size = 0; + break; + case VOICE_RTAC_APR_CAL_TYPE: + size = 0; + break; + case MAD_CAL_TYPE: + size = 0; + break; + case ULP_AFE_CAL_TYPE: + size = sizeof(struct audio_cal_type_afe); + break; + case ULP_LSM_CAL_TYPE: + size = sizeof(struct audio_cal_type_lsm); + break; + case AUDIO_CORE_METAINFO_CAL_TYPE: + size = sizeof(struct audio_cal_type_metainfo); + break; + case SRS_TRUMEDIA_CAL_TYPE: + size = 0; + break; + default: + pr_err("%s:Invalid cal type %d!", + __func__, cal_type); + } + return size; +} + +int32_t cal_utils_get_cal_type_version(void *cal_type_data) +{ + struct audio_cal_type_basic *data = NULL; + + data = (struct audio_cal_type_basic *)cal_type_data; + + return data->cal_hdr.version; +} + +static struct cal_type_data *create_cal_type_data( + struct cal_type_info *info) +{ + struct cal_type_data *cal_type = NULL; + + if ((info->reg.cal_type < 0) || + (info->reg.cal_type >= MAX_CAL_TYPES)) { + pr_err("%s: cal type %d is Invalid!\n", + __func__, info->reg.cal_type); + goto done; + } + + if (info->cal_util_callbacks.match_block == NULL) { + pr_err("%s: cal type %d no method to match blocks!\n", + __func__, info->reg.cal_type); + goto done; + } + + cal_type = kmalloc(sizeof(*cal_type), GFP_KERNEL); + if (cal_type == NULL) + goto done; + + INIT_LIST_HEAD(&cal_type->cal_blocks); + mutex_init(&cal_type->lock); + memcpy(&cal_type->info, info, + sizeof(cal_type->info)); +done: + return cal_type; +} + +/** + * cal_utils_create_cal_types + * + * @num_cal_types: number of types + * @cal_type: pointer to the cal types pointer + * @info: pointer to info + * + * Returns 0 on success, EINVAL otherwise + */ +int cal_utils_create_cal_types(int num_cal_types, + struct cal_type_data **cal_type, + struct cal_type_info *info) +{ + int ret = 0; + int i; + + pr_debug("%s\n", __func__); + + if (cal_type == NULL) { + pr_err("%s: cal_type is NULL!\n", __func__); + ret = -EINVAL; + goto done; + } else if (info == NULL) { + pr_err("%s: info is NULL!\n", __func__); + ret = -EINVAL; + goto done; + } else if ((num_cal_types <= 0) || + (num_cal_types > MAX_CAL_TYPES)) { + pr_err("%s: num_cal_types of %d is Invalid!\n", + __func__, num_cal_types); + ret = -EINVAL; + goto done; + } + + for (i = 0; i < num_cal_types; i++) { + if ((info[i].reg.cal_type < 0) || + (info[i].reg.cal_type >= MAX_CAL_TYPES)) { + pr_err("%s: cal type %d at index %d is Invalid!\n", + __func__, info[i].reg.cal_type, i); + ret = -EINVAL; + goto done; + } + + cal_type[i] = create_cal_type_data(&info[i]); + if (cal_type[i] == NULL) { + pr_err("%s: Could not allocate cal_type of index %d!\n", + __func__, i); + ret = -EINVAL; + goto done; + } + + ret = audio_cal_register(1, &info[i].reg); + if (ret < 0) { + pr_err("%s: audio_cal_register failed, ret = %d!\n", + __func__, ret); + ret = -EINVAL; + goto done; + } + pr_debug("%s: cal type %d at index %d!\n", + __func__, info[i].reg.cal_type, i); + } +done: + return ret; +} +EXPORT_SYMBOL(cal_utils_create_cal_types); + +static void delete_cal_block(struct cal_block_data *cal_block) +{ + pr_debug("%s\n", __func__); + + if (cal_block == NULL) + goto done; + + list_del(&cal_block->list); + kfree(cal_block->client_info); + cal_block->client_info = NULL; + kfree(cal_block->cal_info); + cal_block->cal_info = NULL; + if (cal_block->map_data.dma_buf != NULL) { + msm_audio_ion_free(cal_block->map_data.dma_buf); + cal_block->map_data.dma_buf = NULL; + } + kfree(cal_block); +done: + return; +} + +static void destroy_all_cal_blocks(struct cal_type_data *cal_type) +{ + int ret = 0; + struct list_head *ptr, *next; + struct cal_block_data *cal_block; + + list_for_each_safe(ptr, next, + &cal_type->cal_blocks) { + + cal_block = list_entry(ptr, + struct cal_block_data, list); + + ret = unmap_memory(cal_type, cal_block); + if (ret < 0) { + pr_err("%s: unmap_memory failed, cal type %d, ret = %d!\n", + __func__, + cal_type->info.reg.cal_type, + ret); + } + delete_cal_block(cal_block); + cal_block = NULL; + } +} + +static void destroy_cal_type_data(struct cal_type_data *cal_type) +{ + if (cal_type == NULL) + goto done; + + destroy_all_cal_blocks(cal_type); + list_del(&cal_type->cal_blocks); + kfree(cal_type); +done: + return; +} + +/** + * cal_utils_destroy_cal_types - + * Destroys cal types and deregister from cal info + * + * @num_cal_types: number of cal types + * @cal_type: cal type pointer with cal info + * + */ +void cal_utils_destroy_cal_types(int num_cal_types, + struct cal_type_data **cal_type) +{ + int i; + + pr_debug("%s\n", __func__); + + if (cal_type == NULL) { + pr_err("%s: cal_type is NULL!\n", __func__); + goto done; + } else if ((num_cal_types <= 0) || + (num_cal_types > MAX_CAL_TYPES)) { + pr_err("%s: num_cal_types of %d is Invalid!\n", + __func__, num_cal_types); + goto done; + } + + for (i = 0; i < num_cal_types; i++) { + audio_cal_deregister(1, &cal_type[i]->info.reg); + destroy_cal_type_data(cal_type[i]); + cal_type[i] = NULL; + } +done: + return; +} +EXPORT_SYMBOL(cal_utils_destroy_cal_types); + +/** + * cal_utils_get_only_cal_block + * + * @cal_type: pointer to the cal type + * + * Returns cal_block structure + */ +struct cal_block_data *cal_utils_get_only_cal_block( + struct cal_type_data *cal_type) +{ + struct list_head *ptr, *next; + struct cal_block_data *cal_block = NULL; + + if (cal_type == NULL) + goto done; + + list_for_each_safe(ptr, next, + &cal_type->cal_blocks) { + + cal_block = list_entry(ptr, + struct cal_block_data, list); + break; + } +done: + return cal_block; +} +EXPORT_SYMBOL(cal_utils_get_only_cal_block); + +/** + * cal_utils_get_only_cal_block + * + * @cal_block: pointer to cal block struct + * @user_data: pointer to user data + * + * Returns true on match + */ +bool cal_utils_match_buf_num(struct cal_block_data *cal_block, + void *user_data) +{ + bool ret = false; + struct audio_cal_type_basic *data = user_data; + + if (cal_block->buffer_number == data->cal_hdr.buffer_number) + ret = true; + + return ret; +} +EXPORT_SYMBOL(cal_utils_match_buf_num); + +static struct cal_block_data *get_matching_cal_block( + struct cal_type_data *cal_type, + void *data) +{ + struct list_head *ptr, *next; + struct cal_block_data *cal_block = NULL; + + list_for_each_safe(ptr, next, + &cal_type->cal_blocks) { + + cal_block = list_entry(ptr, + struct cal_block_data, list); + + if (cal_type->info.cal_util_callbacks. + match_block(cal_block, data)) + return cal_block; + } + + return NULL; +} + +static int cal_block_ion_alloc(struct cal_block_data *cal_block) +{ + int ret = 0; + + if (cal_block == NULL) { + pr_err("%s: cal_block is NULL!\n", __func__); + ret = -EINVAL; + goto done; + } + + ret = msm_audio_ion_import(&cal_block->map_data.dma_buf, + cal_block->map_data.ion_map_handle, + NULL, 0, + &cal_block->cal_data.paddr, + &cal_block->map_data.map_size, + &cal_block->cal_data.kvaddr); + if (ret) { + pr_err("%s: audio ION import failed, rc = %d\n", + __func__, ret); + ret = -ENOMEM; + goto done; + } +done: + return ret; +} + +static struct cal_block_data *create_cal_block(struct cal_type_data *cal_type, + struct audio_cal_type_basic *basic_cal, + size_t client_info_size, void *client_info) +{ + struct cal_block_data *cal_block = NULL; + + if (cal_type == NULL) { + pr_err("%s: cal_type is NULL!\n", __func__); + goto done; + } else if (basic_cal == NULL) { + pr_err("%s: basic_cal is NULL!\n", __func__); + goto done; + } + + cal_block = kzalloc(sizeof(*cal_block), + GFP_KERNEL); + if (cal_block == NULL) + goto done; + + INIT_LIST_HEAD(&cal_block->list); + + cal_block->map_data.ion_map_handle = basic_cal->cal_data.mem_handle; + if (basic_cal->cal_data.mem_handle > 0) { + if (cal_block_ion_alloc(cal_block)) { + pr_err("%s: cal_block_ion_alloc failed!\n", + __func__); + goto err; + } + } + if (client_info_size > 0) { + cal_block->client_info_size = client_info_size; + cal_block->client_info = kmalloc(client_info_size, GFP_KERNEL); + if (cal_block->client_info == NULL) { + pr_err("%s: could not allocats client_info!\n", + __func__); + goto err; + } + if (client_info != NULL) + memcpy(cal_block->client_info, client_info, + client_info_size); + } + + cal_block->cal_info = kzalloc( + get_cal_info_size(cal_type->info.reg.cal_type), + GFP_KERNEL); + if (cal_block->cal_info == NULL) { + pr_err("%s: could not allocats cal_info!\n", + __func__); + goto err; + } + cal_block->buffer_number = basic_cal->cal_hdr.buffer_number; + list_add_tail(&cal_block->list, &cal_type->cal_blocks); + pr_debug("%s: created block for cal type %d, buf num %d, map handle %d, map size %zd paddr 0x%pK!\n", + __func__, cal_type->info.reg.cal_type, + cal_block->buffer_number, + cal_block->map_data.ion_map_handle, + cal_block->map_data.map_size, + &cal_block->cal_data.paddr); +done: + return cal_block; +err: + kfree(cal_block->cal_info); + cal_block->cal_info = NULL; + kfree(cal_block->client_info); + cal_block->client_info = NULL; + kfree(cal_block); + cal_block = NULL; + return cal_block; +} + +void cal_utils_clear_cal_block_q6maps(int num_cal_types, + struct cal_type_data **cal_type) +{ + int i = 0; + struct list_head *ptr, *next; + struct cal_block_data *cal_block; + + pr_debug("%s\n", __func__); + + if (cal_type == NULL) { + pr_err("%s: cal_type is NULL!\n", __func__); + goto done; + } else if ((num_cal_types <= 0) || + (num_cal_types > MAX_CAL_TYPES)) { + pr_err("%s: num_cal_types of %d is Invalid!\n", + __func__, num_cal_types); + goto done; + } + + for (; i < num_cal_types; i++) { + if (cal_type[i] == NULL) + continue; + + mutex_lock(&cal_type[i]->lock); + list_for_each_safe(ptr, next, + &cal_type[i]->cal_blocks) { + + cal_block = list_entry(ptr, + struct cal_block_data, list); + + cal_block->map_data.q6map_handle = 0; + } + mutex_unlock(&cal_type[i]->lock); + } +done: + return; +} + + + +static int realloc_memory(struct cal_block_data *cal_block) +{ + int ret = 0; + + msm_audio_ion_free(cal_block->map_data.dma_buf); + cal_block->map_data.dma_buf = NULL; + cal_block->cal_data.size = 0; + + ret = cal_block_ion_alloc(cal_block); + if (ret < 0) + pr_err("%s: realloc_memory failed!\n", + __func__); + return ret; +} + +static int map_memory(struct cal_type_data *cal_type, + struct cal_block_data *cal_block) +{ + int ret = 0; + + + if (cal_type->info.cal_util_callbacks.map_cal != NULL) { + if ((cal_block->map_data.ion_map_handle < 0) || + (cal_block->map_data.map_size <= 0) || + (cal_block->map_data.q6map_handle != 0)) { + goto done; + } + + pr_debug("%s: cal type %d call map\n", + __func__, cal_type->info.reg.cal_type); + ret = cal_type->info.cal_util_callbacks. + map_cal(cal_type->info.reg.cal_type, cal_block); + if (ret < 0) { + pr_err("%s: map_cal failed, cal type %d, ret = %d!\n", + __func__, cal_type->info.reg.cal_type, + ret); + goto done; + } + } +done: + return ret; +} + +static int unmap_memory(struct cal_type_data *cal_type, + struct cal_block_data *cal_block) +{ + int ret = 0; + + if (cal_type->info.cal_util_callbacks.unmap_cal != NULL) { + if ((cal_block->map_data.ion_map_handle < 0) || + (cal_block->map_data.map_size <= 0) || + (cal_block->map_data.q6map_handle == 0)) { + goto done; + } + pr_debug("%s: cal type %d call unmap\n", + __func__, cal_type->info.reg.cal_type); + ret = cal_type->info.cal_util_callbacks. + unmap_cal(cal_type->info.reg.cal_type, cal_block); + if (ret < 0) { + pr_err("%s: unmap_cal failed, cal type %d, ret = %d!\n", + __func__, cal_type->info.reg.cal_type, + ret); + goto done; + } + } +done: + return ret; +} + +/** + * cal_utils_alloc_cal + * + * @data_size: size of data to allocate + * @data: data pointer + * @cal_type: pointer to the cal type + * @client_info_size: client info size + * @client_info: pointer to client info + * + * Returns 0 on success, appropriate error code otherwise + */ +int cal_utils_alloc_cal(size_t data_size, void *data, + struct cal_type_data *cal_type, + size_t client_info_size, void *client_info) +{ + int ret = 0; + struct cal_block_data *cal_block; + struct audio_cal_type_alloc *alloc_data = data; + + pr_debug("%s\n", __func__); + + if (cal_type == NULL) { + pr_err("%s: cal_type is NULL!\n", + __func__); + ret = -EINVAL; + goto done; + } + if (data_size < sizeof(struct audio_cal_type_alloc)) { + pr_err("%s: data_size of %zd does not equal alloc struct size of %zd!\n", + __func__, data_size, + sizeof(struct audio_cal_type_alloc)); + ret = -EINVAL; + goto done; + } + if ((client_info_size > 0) && (client_info == NULL)) { + pr_err("%s: User info pointer is NULL but size is %zd!\n", + __func__, client_info_size); + ret = -EINVAL; + goto done; + } + + if (alloc_data->cal_data.mem_handle < 0) { + pr_err("%s: mem_handle %d invalid!\n", + __func__, alloc_data->cal_data.mem_handle); + ret = -EINVAL; + goto done; + } + + mutex_lock(&cal_type->lock); + + cal_block = get_matching_cal_block(cal_type, + data); + if (cal_block != NULL) { + ret = unmap_memory(cal_type, cal_block); + if (ret < 0) + goto err; + ret = realloc_memory(cal_block); + if (ret < 0) + goto err; + } else { + cal_block = create_cal_block(cal_type, + (struct audio_cal_type_basic *)alloc_data, + client_info_size, client_info); + if (cal_block == NULL) { + pr_err("%s: create_cal_block failed for %d!\n", + __func__, alloc_data->cal_data.mem_handle); + ret = -EINVAL; + goto err; + } + } + + ret = map_memory(cal_type, cal_block); + if (ret < 0) + goto err; +err: + mutex_unlock(&cal_type->lock); +done: + return ret; +} +EXPORT_SYMBOL(cal_utils_alloc_cal); + +/** + * cal_utils_dealloc_cal + * + * @data_size: size of data to allocate + * @data: data pointer + * @cal_type: pointer to the cal type + * + * Returns 0 on success, appropriate error code otherwise + */ +int cal_utils_dealloc_cal(size_t data_size, void *data, + struct cal_type_data *cal_type) +{ + int ret = 0; + struct cal_block_data *cal_block; + struct audio_cal_type_dealloc *dealloc_data = data; + + pr_debug("%s\n", __func__); + + + if (cal_type == NULL) { + pr_err("%s: cal_type is NULL!\n", + __func__); + ret = -EINVAL; + goto done; + } + + if (data_size < sizeof(struct audio_cal_type_dealloc)) { + pr_err("%s: data_size of %zd does not equal struct size of %zd!\n", + __func__, data_size, + sizeof(struct audio_cal_type_dealloc)); + ret = -EINVAL; + goto done; + } + + if ((dealloc_data->cal_data.mem_handle == -1) && + (dealloc_data->cal_hdr.buffer_number == ALL_CAL_BLOCKS)) { + destroy_all_cal_blocks(cal_type); + goto done; + } + + if (dealloc_data->cal_data.mem_handle < 0) { + pr_err("%s: mem_handle %d invalid!\n", + __func__, dealloc_data->cal_data.mem_handle); + ret = -EINVAL; + goto done; + } + + mutex_lock(&cal_type->lock); + cal_block = get_matching_cal_block( + cal_type, + data); + if (cal_block == NULL) { + pr_err("%s: allocation does not exist for %d!\n", + __func__, dealloc_data->cal_data.mem_handle); + ret = -EINVAL; + goto err; + } + + ret = unmap_memory(cal_type, cal_block); + if (ret < 0) + goto err; + + delete_cal_block(cal_block); +err: + mutex_unlock(&cal_type->lock); +done: + return ret; +} +EXPORT_SYMBOL(cal_utils_dealloc_cal); + +/** + * cal_utils_set_cal + * + * @data_size: size of data to allocate + * @data: data pointer + * @cal_type: pointer to the cal type + * @client_info_size: client info size + * @client_info: pointer to client info + * + * Returns 0 on success, appropriate error code otherwise + */ +int cal_utils_set_cal(size_t data_size, void *data, + struct cal_type_data *cal_type, + size_t client_info_size, void *client_info) +{ + int ret = 0; + struct cal_block_data *cal_block; + struct audio_cal_type_basic *basic_data = data; + + pr_debug("%s\n", __func__); + + if (cal_type == NULL) { + pr_err("%s: cal_type is NULL!\n", + __func__); + ret = -EINVAL; + goto done; + } + + if ((client_info_size > 0) && (client_info == NULL)) { + pr_err("%s: User info pointer is NULL but size is %zd!\n", + __func__, client_info_size); + ret = -EINVAL; + goto done; + } + + if ((data_size > get_user_cal_type_size( + cal_type->info.reg.cal_type)) || (data_size < 0)) { + pr_err("%s: cal_type %d, data_size of %zd is invalid, expecting %zd!\n", + __func__, cal_type->info.reg.cal_type, data_size, + get_user_cal_type_size(cal_type->info.reg.cal_type)); + ret = -EINVAL; + goto done; + } + + mutex_lock(&cal_type->lock); + cal_block = get_matching_cal_block( + cal_type, + data); + if (cal_block == NULL) { + if (basic_data->cal_data.mem_handle > 0) { + pr_err("%s: allocation does not exist for %d!\n", + __func__, basic_data->cal_data.mem_handle); + ret = -EINVAL; + goto err; + } else { + cal_block = create_cal_block( + cal_type, + basic_data, + client_info_size, client_info); + if (cal_block == NULL) { + pr_err("%s: create_cal_block failed for cal type %d!\n", + __func__, + cal_type->info.reg.cal_type); + ret = -EINVAL; + goto err; + } + } + } + + ret = map_memory(cal_type, cal_block); + if (ret < 0) + goto err; + + cal_block->cal_data.size = basic_data->cal_data.cal_size; + + if (client_info_size > 0) { + memcpy(cal_block->client_info, + client_info, + client_info_size); + } + + memcpy(cal_block->cal_info, + ((uint8_t *)data + sizeof(struct audio_cal_type_basic)), + data_size - sizeof(struct audio_cal_type_basic)); + + /* reset buffer stale flag */ + cal_block->cal_stale = false; + +err: + mutex_unlock(&cal_type->lock); +done: + return ret; +} +EXPORT_SYMBOL(cal_utils_set_cal); + +/** + * cal_utils_mark_cal_used + * + * @cal_block: pointer to cal block + */ +void cal_utils_mark_cal_used(struct cal_block_data *cal_block) +{ + if (cal_block) + cal_block->cal_stale = true; +} +EXPORT_SYMBOL(cal_utils_mark_cal_used); + +/** + * cal_utils_is_cal_stale + * + * @cal_block: pointer to cal block + * + * Returns true if cal block is stale, false otherwise + */ +bool cal_utils_is_cal_stale(struct cal_block_data *cal_block) +{ + if ((cal_block) && (cal_block->cal_stale)) + return true; + + return false; +} +EXPORT_SYMBOL(cal_utils_is_cal_stale); diff --git a/audio-kernel/dsp/audio_calibration.c b/audio-kernel/dsp/audio_calibration.c new file mode 100644 index 000000000..c895fa788 --- /dev/null +++ b/audio-kernel/dsp/audio_calibration.c @@ -0,0 +1,635 @@ +/* Copyright (c) 2014, 2016-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 + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct audio_cal_client_info { + struct list_head list; + struct audio_cal_callbacks *callbacks; +}; + +struct audio_cal_info { + struct mutex common_lock; + struct mutex cal_mutex[MAX_CAL_TYPES]; + struct list_head client_info[MAX_CAL_TYPES]; + int ref_count; +}; + +static struct audio_cal_info audio_cal; + + +static bool callbacks_are_equal(struct audio_cal_callbacks *callback1, + struct audio_cal_callbacks *callback2) +{ + bool ret = true; + struct audio_cal_callbacks *call1 = callback1; + struct audio_cal_callbacks *call2 = callback2; + + pr_debug("%s\n", __func__); + + if ((call1 == NULL) && (call2 == NULL)) + ret = true; + else if ((call1 == NULL) || (call2 == NULL)) + ret = false; + else if ((call1->alloc != call2->alloc) || + (call1->dealloc != call2->dealloc) || + (call1->pre_cal != call2->pre_cal) || + (call1->set_cal != call2->set_cal) || + (call1->get_cal != call2->get_cal) || + (call1->post_cal != call2->post_cal)) + ret = false; + return ret; +} + +int audio_cal_deregister(int num_cal_types, + struct audio_cal_reg *reg_data) +{ + int ret = 0; + int i = 0; + struct list_head *ptr, *next; + struct audio_cal_client_info *client_info_node = NULL; + + pr_debug("%s\n", __func__); + + if (reg_data == NULL) { + pr_err("%s: reg_data is NULL!\n", __func__); + ret = -EINVAL; + goto done; + } else if ((num_cal_types <= 0) || + (num_cal_types > MAX_CAL_TYPES)) { + pr_err("%s: num_cal_types of %d is Invalid!\n", + __func__, num_cal_types); + ret = -EINVAL; + goto done; + } + + for (; i < num_cal_types; i++) { + if ((reg_data[i].cal_type < 0) || + (reg_data[i].cal_type >= MAX_CAL_TYPES)) { + pr_err("%s: cal type %d at index %d is Invalid!\n", + __func__, reg_data[i].cal_type, i); + ret = -EINVAL; + continue; + } + + mutex_lock(&audio_cal.cal_mutex[reg_data[i].cal_type]); + list_for_each_safe(ptr, next, + &audio_cal.client_info[reg_data[i].cal_type]) { + + client_info_node = list_entry(ptr, + struct audio_cal_client_info, list); + if (callbacks_are_equal(client_info_node->callbacks, + ®_data[i].callbacks)) { + list_del(&client_info_node->list); + kfree(client_info_node->callbacks); + client_info_node->callbacks = NULL; + kfree(client_info_node); + client_info_node = NULL; + break; + } + } + mutex_unlock(&audio_cal.cal_mutex[reg_data[i].cal_type]); + } +done: + return ret; +} + + +int audio_cal_register(int num_cal_types, + struct audio_cal_reg *reg_data) +{ + int ret = 0; + int i = 0; + struct audio_cal_client_info *client_info_node = NULL; + struct audio_cal_callbacks *callback_node = NULL; + + pr_debug("%s\n", __func__); + + if (reg_data == NULL) { + pr_err("%s: callbacks are NULL!\n", __func__); + ret = -EINVAL; + goto done; + } else if ((num_cal_types <= 0) || + (num_cal_types > MAX_CAL_TYPES)) { + pr_err("%s: num_cal_types of %d is Invalid!\n", + __func__, num_cal_types); + ret = -EINVAL; + goto done; + } + + for (; i < num_cal_types; i++) { + if ((reg_data[i].cal_type < 0) || + (reg_data[i].cal_type >= MAX_CAL_TYPES)) { + pr_err("%s: cal type %d at index %d is Invalid!\n", + __func__, reg_data[i].cal_type, i); + ret = -EINVAL; + goto err; + } + + client_info_node = kmalloc(sizeof(*client_info_node), + GFP_KERNEL); + if (client_info_node == NULL) { + ret = -ENOMEM; + goto err; + } + INIT_LIST_HEAD(&client_info_node->list); + + callback_node = kmalloc(sizeof(*callback_node), + GFP_KERNEL); + if (callback_node == NULL) { + ret = -ENOMEM; + goto err; + } + + memcpy(callback_node, ®_data[i].callbacks, + sizeof(*callback_node)); + client_info_node->callbacks = callback_node; + + mutex_lock(&audio_cal.cal_mutex[reg_data[i].cal_type]); + list_add_tail(&client_info_node->list, + &audio_cal.client_info[reg_data[i].cal_type]); + mutex_unlock(&audio_cal.cal_mutex[reg_data[i].cal_type]); + } +done: + return ret; +err: + audio_cal_deregister(num_cal_types, reg_data); + return ret; +} + +static int call_allocs(int32_t cal_type, + size_t cal_type_size, void *data) +{ + int ret = 0; + int ret2 = 0; + struct list_head *ptr, *next; + struct audio_cal_client_info *client_info_node = NULL; + + pr_debug("%s\n", __func__); + + list_for_each_safe(ptr, next, + &audio_cal.client_info[cal_type]) { + + client_info_node = list_entry(ptr, + struct audio_cal_client_info, list); + + if (client_info_node->callbacks->alloc == NULL) + continue; + + ret2 = client_info_node->callbacks-> + alloc(cal_type, cal_type_size, data); + if (ret2 < 0) { + pr_err("%s: alloc failed!\n", __func__); + ret = ret2; + } + } + return ret; +} + +static int call_deallocs(int32_t cal_type, + size_t cal_type_size, void *data) +{ + int ret = 0; + int ret2 = 0; + struct list_head *ptr, *next; + struct audio_cal_client_info *client_info_node = NULL; + + pr_debug("%s cal type %d\n", __func__, cal_type); + + list_for_each_safe(ptr, next, + &audio_cal.client_info[cal_type]) { + + client_info_node = list_entry(ptr, + struct audio_cal_client_info, list); + + if (client_info_node->callbacks->dealloc == NULL) + continue; + + ret2 = client_info_node->callbacks-> + dealloc(cal_type, cal_type_size, data); + if (ret2 < 0) { + pr_err("%s: dealloc failed!\n", __func__); + ret = ret2; + } + } + return ret; +} + +static int call_pre_cals(int32_t cal_type, + size_t cal_type_size, void *data) +{ + int ret = 0; + int ret2 = 0; + struct list_head *ptr, *next; + struct audio_cal_client_info *client_info_node = NULL; + + pr_debug("%s cal type %d\n", __func__, cal_type); + + list_for_each_safe(ptr, next, + &audio_cal.client_info[cal_type]) { + + client_info_node = list_entry(ptr, + struct audio_cal_client_info, list); + + if (client_info_node->callbacks->pre_cal == NULL) + continue; + + ret2 = client_info_node->callbacks-> + pre_cal(cal_type, cal_type_size, data); + if (ret2 < 0) { + pr_err("%s: pre_cal failed!\n", __func__); + ret = ret2; + } + } + return ret; +} + +static int call_post_cals(int32_t cal_type, + size_t cal_type_size, void *data) +{ + int ret = 0; + int ret2 = 0; + struct list_head *ptr, *next; + struct audio_cal_client_info *client_info_node = NULL; + + pr_debug("%s cal type %d\n", __func__, cal_type); + + list_for_each_safe(ptr, next, + &audio_cal.client_info[cal_type]) { + + client_info_node = list_entry(ptr, + struct audio_cal_client_info, list); + + if (client_info_node->callbacks->post_cal == NULL) + continue; + + ret2 = client_info_node->callbacks-> + post_cal(cal_type, cal_type_size, data); + if (ret2 < 0) { + pr_err("%s: post_cal failed!\n", __func__); + ret = ret2; + } + } + return ret; +} + +static int call_set_cals(int32_t cal_type, + size_t cal_type_size, void *data) +{ + int ret = 0; + int ret2 = 0; + struct list_head *ptr, *next; + struct audio_cal_client_info *client_info_node = NULL; + + pr_debug("%s cal type %d\n", __func__, cal_type); + + list_for_each_safe(ptr, next, + &audio_cal.client_info[cal_type]) { + + client_info_node = list_entry(ptr, + struct audio_cal_client_info, list); + + if (client_info_node->callbacks->set_cal == NULL) + continue; + + ret2 = client_info_node->callbacks-> + set_cal(cal_type, cal_type_size, data); + if (ret2 < 0) { + pr_err("%s: set_cal failed!\n", __func__); + ret = ret2; + } + } + return ret; +} + +static int call_get_cals(int32_t cal_type, + size_t cal_type_size, void *data) +{ + int ret = 0; + int ret2 = 0; + struct list_head *ptr, *next; + struct audio_cal_client_info *client_info_node = NULL; + + pr_debug("%s cal type %d\n", __func__, cal_type); + + list_for_each_safe(ptr, next, + &audio_cal.client_info[cal_type]) { + + client_info_node = list_entry(ptr, + struct audio_cal_client_info, list); + + if (client_info_node->callbacks->get_cal == NULL) + continue; + + ret2 = client_info_node->callbacks-> + get_cal(cal_type, cal_type_size, data); + if (ret2 < 0) { + pr_err("%s: get_cal failed!\n", __func__); + ret = ret2; + } + } + return ret; +} + +static int audio_cal_open(struct inode *inode, struct file *f) +{ + int ret = 0; + + pr_debug("%s\n", __func__); + + mutex_lock(&audio_cal.common_lock); + audio_cal.ref_count++; + mutex_unlock(&audio_cal.common_lock); + + return ret; +} + +static void dealloc_all_clients(void) +{ + int i = 0; + struct audio_cal_type_dealloc dealloc_data; + + pr_debug("%s\n", __func__); + + dealloc_data.cal_hdr.version = VERSION_0_0; + dealloc_data.cal_hdr.buffer_number = ALL_CAL_BLOCKS; + dealloc_data.cal_data.mem_handle = -1; + + for (; i < MAX_CAL_TYPES; i++) + call_deallocs(i, sizeof(dealloc_data), &dealloc_data); +} + +static int audio_cal_release(struct inode *inode, struct file *f) +{ + int ret = 0; + + pr_debug("%s\n", __func__); + + mutex_lock(&audio_cal.common_lock); + audio_cal.ref_count--; + if (audio_cal.ref_count <= 0) { + audio_cal.ref_count = 0; + dealloc_all_clients(); + } + mutex_unlock(&audio_cal.common_lock); + + return ret; +} + +static long audio_cal_shared_ioctl(struct file *file, unsigned int cmd, + void __user *arg) +{ + int ret = 0; + int32_t size; + struct audio_cal_basic *data = NULL; + + pr_debug("%s\n", __func__); + + switch (cmd) { + case AUDIO_ALLOCATE_CALIBRATION: + case AUDIO_DEALLOCATE_CALIBRATION: + case AUDIO_PREPARE_CALIBRATION: + case AUDIO_SET_CALIBRATION: + case AUDIO_GET_CALIBRATION: + case AUDIO_POST_CALIBRATION: + 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_cal_basic)) + || (size > MAX_IOCTL_CMD_SIZE)) { + pr_err("%s: Invalid size sent to driver: %d, max size is %d, min size is %zd\n", + __func__, size, MAX_IOCTL_CMD_SIZE, + sizeof(struct audio_cal_basic)); + ret = -EINVAL; + goto done; + } + + data = kmalloc(size, GFP_KERNEL); + if (data == NULL) { + ret = -ENOMEM; + 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.cal_type < 0) || + (data->hdr.cal_type >= MAX_CAL_TYPES)) { + pr_err("%s: cal type %d is Invalid!\n", + __func__, data->hdr.cal_type); + ret = -EINVAL; + goto done; + } else if ((data->hdr.cal_type_size < + sizeof(struct audio_cal_type_basic)) || + (data->hdr.cal_type_size > + get_user_cal_type_size(data->hdr.cal_type))) { + pr_err("%s: cal type size %d is Invalid! Max is %zd!\n", + __func__, data->hdr.cal_type_size, + get_user_cal_type_size(data->hdr.cal_type)); + ret = -EINVAL; + goto done; + } else if (data->cal_type.cal_hdr.buffer_number < 0) { + pr_err("%s: cal type %d Invalid buffer number %d!\n", + __func__, data->hdr.cal_type, + data->cal_type.cal_hdr.buffer_number); + ret = -EINVAL; + goto done; + } else if ((data->hdr.cal_type_size + sizeof(data->hdr)) > size) { + pr_err("%s: cal type hdr size %zd + cal type size %d is greater than user buffer size %d\n", + __func__, sizeof(data->hdr), data->hdr.cal_type_size, + size); + ret = -EFAULT; + goto done; + } + + + mutex_lock(&audio_cal.cal_mutex[data->hdr.cal_type]); + + switch (cmd) { + case AUDIO_ALLOCATE_CALIBRATION: + ret = call_allocs(data->hdr.cal_type, + data->hdr.cal_type_size, &data->cal_type); + break; + case AUDIO_DEALLOCATE_CALIBRATION: + ret = call_deallocs(data->hdr.cal_type, + data->hdr.cal_type_size, &data->cal_type); + break; + case AUDIO_PREPARE_CALIBRATION: + ret = call_pre_cals(data->hdr.cal_type, + data->hdr.cal_type_size, &data->cal_type); + break; + case AUDIO_SET_CALIBRATION: + ret = call_set_cals(data->hdr.cal_type, + data->hdr.cal_type_size, &data->cal_type); + break; + case AUDIO_GET_CALIBRATION: + ret = call_get_cals(data->hdr.cal_type, + data->hdr.cal_type_size, &data->cal_type); + break; + case AUDIO_POST_CALIBRATION: + ret = call_post_cals(data->hdr.cal_type, + data->hdr.cal_type_size, &data->cal_type); + break; + } + + if (cmd == AUDIO_GET_CALIBRATION) { + if (data->hdr.cal_type_size == 0) + goto unlock; + if (data == NULL) + goto unlock; + if (copy_to_user(arg, data, + sizeof(data->hdr) + data->hdr.cal_type_size)) { + pr_err("%s: Could not copy cal type to user\n", + __func__); + ret = -EFAULT; + goto unlock; + } + } + +unlock: + mutex_unlock(&audio_cal.cal_mutex[data->hdr.cal_type]); +done: + kfree(data); + return ret; +} + +static long audio_cal_ioctl(struct file *f, + unsigned int cmd, unsigned long arg) +{ + return audio_cal_shared_ioctl(f, cmd, (void __user *)arg); +} + +#ifdef CONFIG_COMPAT + +#define AUDIO_ALLOCATE_CALIBRATION32 _IOWR(CAL_IOCTL_MAGIC, \ + 200, compat_uptr_t) +#define AUDIO_DEALLOCATE_CALIBRATION32 _IOWR(CAL_IOCTL_MAGIC, \ + 201, compat_uptr_t) +#define AUDIO_PREPARE_CALIBRATION32 _IOWR(CAL_IOCTL_MAGIC, \ + 202, compat_uptr_t) +#define AUDIO_SET_CALIBRATION32 _IOWR(CAL_IOCTL_MAGIC, \ + 203, compat_uptr_t) +#define AUDIO_GET_CALIBRATION32 _IOWR(CAL_IOCTL_MAGIC, \ + 204, compat_uptr_t) +#define AUDIO_POST_CALIBRATION32 _IOWR(CAL_IOCTL_MAGIC, \ + 205, compat_uptr_t) + +static long audio_cal_compat_ioctl(struct file *f, + unsigned int cmd, unsigned long arg) +{ + unsigned int cmd64; + int ret = 0; + + switch (cmd) { + case AUDIO_ALLOCATE_CALIBRATION32: + cmd64 = AUDIO_ALLOCATE_CALIBRATION; + break; + case AUDIO_DEALLOCATE_CALIBRATION32: + cmd64 = AUDIO_DEALLOCATE_CALIBRATION; + break; + case AUDIO_PREPARE_CALIBRATION32: + cmd64 = AUDIO_PREPARE_CALIBRATION; + break; + case AUDIO_SET_CALIBRATION32: + cmd64 = AUDIO_SET_CALIBRATION; + break; + case AUDIO_GET_CALIBRATION32: + cmd64 = AUDIO_GET_CALIBRATION; + break; + case AUDIO_POST_CALIBRATION32: + cmd64 = AUDIO_POST_CALIBRATION; + break; + default: + pr_err("%s: ioctl not found!\n", __func__); + ret = -EFAULT; + goto done; + } + + ret = audio_cal_shared_ioctl(f, cmd64, compat_ptr(arg)); +done: + return ret; +} +#endif + +static const struct file_operations audio_cal_fops = { + .owner = THIS_MODULE, + .open = audio_cal_open, + .release = audio_cal_release, + .unlocked_ioctl = audio_cal_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = audio_cal_compat_ioctl, +#endif +}; + +struct miscdevice audio_cal_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_audio_cal", + .fops = &audio_cal_fops, +}; + +int __init audio_cal_init(void) +{ + int i = 0; + + pr_debug("%s\n", __func__); + + memset(&audio_cal, 0, sizeof(audio_cal)); + mutex_init(&audio_cal.common_lock); + for (; i < MAX_CAL_TYPES; i++) { + INIT_LIST_HEAD(&audio_cal.client_info[i]); + mutex_init(&audio_cal.cal_mutex[i]); + } + + return misc_register(&audio_cal_misc); +} + +void audio_cal_exit(void) +{ + int i = 0; + struct list_head *ptr, *next; + struct audio_cal_client_info *client_info_node; + + for (; i < MAX_CAL_TYPES; i++) { + list_for_each_safe(ptr, next, + &audio_cal.client_info[i]) { + client_info_node = list_entry(ptr, + struct audio_cal_client_info, list); + list_del(&client_info_node->list); + kfree(client_info_node->callbacks); + client_info_node->callbacks = NULL; + kfree(client_info_node); + client_info_node = NULL; + } + } + misc_deregister(&audio_cal_misc); +} + + +MODULE_DESCRIPTION("SoC QDSP6v2 Audio Calibration driver"); +MODULE_LICENSE("GPL v2"); diff --git a/audio-kernel/dsp/audio_notifier.c b/audio-kernel/dsp/audio_notifier.c new file mode 100644 index 000000000..485c703d2 --- /dev/null +++ b/audio-kernel/dsp/audio_notifier.c @@ -0,0 +1,643 @@ +/* Copyright (c) 2016-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 + * 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 +#include +#include +#include +#include +#include "audio_ssr.h" +#include "audio_pdr.h" + +/* Audio states internal to notifier. Client */ +/* used states defined in audio_notifier.h */ +/* for AUDIO_NOTIFIER_SERVICE_DOWN & UP */ +#define NO_SERVICE -2 +#define UNINIT_SERVICE -1 + +/* + * Used for each client registered with audio notifier + */ +struct client_data { + struct list_head list; + /* Notifier block given by client */ + struct notifier_block *nb; + char client_name[20]; + int service; + int domain; +}; + +/* + * Used for each service and domain combination + * Tracks information specific to the underlying + * service. + */ +struct service_info { + const char name[20]; + int domain_id; + int state; + void *handle; + /* Notifier block registered to service */ + struct notifier_block *nb; + /* Used to determine when to register and deregister service */ + int num_of_clients; + /* List of all clients registered to the service and domain */ + struct srcu_notifier_head client_nb_list; +}; + +static int audio_notifer_ssr_adsp_cb(struct notifier_block *this, + unsigned long opcode, void *data); +static int audio_notifer_ssr_modem_cb(struct notifier_block *this, + unsigned long opcode, void *data); +static int audio_notifer_pdr_adsp_cb(struct notifier_block *this, + unsigned long opcode, void *data); + +static struct notifier_block notifier_ssr_adsp_nb = { + .notifier_call = audio_notifer_ssr_adsp_cb, + .priority = 0, +}; + +static struct notifier_block notifier_ssr_modem_nb = { + .notifier_call = audio_notifer_ssr_modem_cb, + .priority = 0, +}; + +static struct notifier_block notifier_pdr_adsp_nb = { + .notifier_call = audio_notifer_pdr_adsp_cb, + .priority = 0, +}; + +static struct service_info service_data[AUDIO_NOTIFIER_MAX_SERVICES] + [AUDIO_NOTIFIER_MAX_DOMAINS] = { + + {{ + .name = "SSR_ADSP", + .domain_id = AUDIO_SSR_DOMAIN_ADSP, + .state = AUDIO_NOTIFIER_SERVICE_DOWN, + .nb = ¬ifier_ssr_adsp_nb + }, + { + .name = "SSR_MODEM", + .domain_id = AUDIO_SSR_DOMAIN_MODEM, + .state = AUDIO_NOTIFIER_SERVICE_DOWN, + .nb = ¬ifier_ssr_modem_nb + } }, + + {{ + .name = "PDR_ADSP", + .domain_id = AUDIO_PDR_DOMAIN_ADSP, + .state = UNINIT_SERVICE, + .nb = ¬ifier_pdr_adsp_nb + }, + { /* PDR MODEM service not enabled */ + .name = "INVALID", + .state = NO_SERVICE, + .nb = NULL + } } +}; + +/* Master list of all audio notifier clients */ +struct list_head client_list; +struct mutex notifier_mutex; + +static int audio_notifer_get_default_service(int domain) +{ + int service = NO_SERVICE; + + /* initial service to connect per domain */ + switch (domain) { + case AUDIO_NOTIFIER_ADSP_DOMAIN: + service = AUDIO_NOTIFIER_PDR_SERVICE; + break; + case AUDIO_NOTIFIER_MODEM_DOMAIN: + service = AUDIO_NOTIFIER_SSR_SERVICE; + break; + } + + return service; +} + +static void audio_notifer_disable_service(int service) +{ + int i; + + for (i = 0; i < AUDIO_NOTIFIER_MAX_DOMAINS; i++) + service_data[service][i].state = NO_SERVICE; +} + +static bool audio_notifer_is_service_enabled(int service) +{ + int i; + + for (i = 0; i < AUDIO_NOTIFIER_MAX_DOMAINS; i++) + if (service_data[service][i].state != NO_SERVICE) + return true; + return false; +} + +static void audio_notifer_init_service(int service) +{ + int i; + + for (i = 0; i < AUDIO_NOTIFIER_MAX_DOMAINS; i++) { + if (service_data[service][i].state == UNINIT_SERVICE) + service_data[service][i].state = + AUDIO_NOTIFIER_SERVICE_DOWN; + } +} + +static int audio_notifer_reg_service(int service, int domain) +{ + void *handle; + int ret = 0; + int curr_state = AUDIO_NOTIFIER_SERVICE_DOWN; + + switch (service) { + case AUDIO_NOTIFIER_SSR_SERVICE: + handle = audio_ssr_register( + service_data[service][domain].domain_id, + service_data[service][domain].nb); + break; + case AUDIO_NOTIFIER_PDR_SERVICE: + handle = audio_pdr_service_register( + service_data[service][domain].domain_id, + service_data[service][domain].nb, &curr_state); + + if (curr_state == SERVREG_NOTIF_SERVICE_STATE_UP_V01) + curr_state = AUDIO_NOTIFIER_SERVICE_UP; + else + curr_state = AUDIO_NOTIFIER_SERVICE_DOWN; + break; + default: + pr_err("%s: Invalid service %d\n", + __func__, service); + ret = -EINVAL; + goto done; + } + if (IS_ERR_OR_NULL(handle)) { + pr_err("%s: handle is incorrect for service %s\n", + __func__, service_data[service][domain].name); + ret = -EINVAL; + goto done; + } + service_data[service][domain].state = curr_state; + service_data[service][domain].handle = handle; + + pr_info("%s: service %s is in use\n", + __func__, service_data[service][domain].name); + pr_debug("%s: service %s has current state %d, handle 0x%pK\n", + __func__, service_data[service][domain].name, + service_data[service][domain].state, + service_data[service][domain].handle); +done: + return ret; +} + +static int audio_notifer_dereg_service(int service, int domain) +{ + int ret; + + switch (service) { + case AUDIO_NOTIFIER_SSR_SERVICE: + ret = audio_ssr_deregister( + service_data[service][domain].handle, + service_data[service][domain].nb); + break; + case AUDIO_NOTIFIER_PDR_SERVICE: + ret = audio_pdr_service_deregister( + service_data[service][domain].handle, + service_data[service][domain].nb); + break; + default: + pr_err("%s: Invalid service %d\n", + __func__, service); + ret = -EINVAL; + goto done; + } + if (ret < 0) { + pr_err("%s: deregister failed for service %s, ret %d\n", + __func__, service_data[service][domain].name, ret); + goto done; + } + + pr_debug("%s: service %s with handle 0x%pK deregistered\n", + __func__, service_data[service][domain].name, + service_data[service][domain].handle); + + service_data[service][domain].state = AUDIO_NOTIFIER_SERVICE_DOWN; + service_data[service][domain].handle = NULL; +done: + return ret; +} + +static int audio_notifer_reg_client_service(struct client_data *client_data, + int service) +{ + int ret = 0; + int domain = client_data->domain; + struct audio_notifier_cb_data data; + + switch (service) { + case AUDIO_NOTIFIER_SSR_SERVICE: + case AUDIO_NOTIFIER_PDR_SERVICE: + if (service_data[service][domain].num_of_clients == 0) + ret = audio_notifer_reg_service(service, domain); + break; + default: + pr_err("%s: Invalid service for client %s, service %d, domain %d\n", + __func__, client_data->client_name, service, domain); + ret = -EINVAL; + goto done; + } + + if (ret < 0) { + pr_err("%s: service registration failed on service %s for client %s\n", + __func__, service_data[service][domain].name, + client_data->client_name); + goto done; + } + + client_data->service = service; + srcu_notifier_chain_register( + &service_data[service][domain].client_nb_list, + client_data->nb); + service_data[service][domain].num_of_clients++; + + pr_debug("%s: registered client %s on service %s, current state 0x%x\n", + __func__, client_data->client_name, + service_data[service][domain].name, + service_data[service][domain].state); + + /* + * PDR registration returns current state + * Force callback of client with current state for PDR + */ + if (client_data->service == AUDIO_NOTIFIER_PDR_SERVICE) { + data.service = service; + data.domain = domain; + (void)client_data->nb->notifier_call(client_data->nb, + service_data[service][domain].state, &data); + } +done: + return ret; +} + +static int audio_notifer_reg_client(struct client_data *client_data) +{ + int ret = 0; + int service; + int domain = client_data->domain; + + service = audio_notifer_get_default_service(domain); + if (service < 0) { + pr_err("%s: service %d is incorrect\n", __func__, service); + ret = -EINVAL; + goto done; + } + + /* Search through services to find a valid one to register client on. */ + for (; service >= 0; service--) { + /* If a service is not initialized, wait for it to come up. */ + if (service_data[service][domain].state == UNINIT_SERVICE) + goto done; + /* Skip unsupported service and domain combinations. */ + if (service_data[service][domain].state < 0) + continue; + /* Only register clients who have not acquired a service. */ + if (client_data->service != NO_SERVICE) + continue; + + /* + * Only register clients, who have not acquired a service, on + * the best available service for their domain. Uninitialized + * services will try to register all of their clients after + * they initialize correctly or will disable their service and + * register clients on the next best avaialable service. + */ + pr_debug("%s: register client %s on service %s", + __func__, client_data->client_name, + service_data[service][domain].name); + + ret = audio_notifer_reg_client_service(client_data, service); + if (ret < 0) + pr_err("%s: client %s failed to register on service %s", + __func__, client_data->client_name, + service_data[service][domain].name); + } + +done: + return ret; +} + +static int audio_notifer_dereg_client(struct client_data *client_data) +{ + int ret = 0; + int service = client_data->service; + int domain = client_data->domain; + + switch (client_data->service) { + case AUDIO_NOTIFIER_SSR_SERVICE: + case AUDIO_NOTIFIER_PDR_SERVICE: + if (service_data[service][domain].num_of_clients == 1) + ret = audio_notifer_dereg_service(service, domain); + break; + case NO_SERVICE: + goto done; + default: + pr_err("%s: Invalid service for client %s, service %d\n", + __func__, client_data->client_name, + client_data->service); + ret = -EINVAL; + goto done; + } + + if (ret < 0) { + pr_err("%s: deregister failed for client %s on service %s, ret %d\n", + __func__, client_data->client_name, + service_data[service][domain].name, ret); + goto done; + } + + ret = srcu_notifier_chain_unregister(&service_data[service][domain]. + client_nb_list, client_data->nb); + if (ret < 0) { + pr_err("%s: srcu_notifier_chain_unregister failed, ret %d\n", + __func__, ret); + goto done; + } + + pr_debug("%s: deregistered client %s on service %s\n", + __func__, client_data->client_name, + service_data[service][domain].name); + + client_data->service = NO_SERVICE; + if (service_data[service][domain].num_of_clients > 0) + service_data[service][domain].num_of_clients--; +done: + return ret; +} + +static void audio_notifer_reg_all_clients(void) +{ + struct list_head *ptr, *next; + struct client_data *client_data; + int ret; + + list_for_each_safe(ptr, next, &client_list) { + client_data = list_entry(ptr, struct client_data, list); + + ret = audio_notifer_reg_client(client_data); + if (ret < 0) + pr_err("%s: audio_notifer_reg_client failed for client %s, ret %d\n", + __func__, client_data->client_name, + ret); + } +} + +static int audio_notifer_pdr_callback(struct notifier_block *this, + unsigned long opcode, void *data) +{ + pr_debug("%s: Audio PDR framework state 0x%lx\n", + __func__, opcode); + mutex_lock(¬ifier_mutex); + if (opcode == AUDIO_PDR_FRAMEWORK_DOWN) + audio_notifer_disable_service(AUDIO_NOTIFIER_PDR_SERVICE); + else + audio_notifer_init_service(AUDIO_NOTIFIER_PDR_SERVICE); + + audio_notifer_reg_all_clients(); + mutex_unlock(¬ifier_mutex); + return 0; +} + +static struct notifier_block pdr_nb = { + .notifier_call = audio_notifer_pdr_callback, + .priority = 0, +}; + +static int audio_notifer_convert_opcode(unsigned long opcode, + unsigned long *notifier_opcode) +{ + int ret = 0; + + switch (opcode) { + case SUBSYS_BEFORE_SHUTDOWN: + case SERVREG_NOTIF_SERVICE_STATE_DOWN_V01: + *notifier_opcode = AUDIO_NOTIFIER_SERVICE_DOWN; + break; + case SUBSYS_AFTER_POWERUP: + case SERVREG_NOTIF_SERVICE_STATE_UP_V01: + *notifier_opcode = AUDIO_NOTIFIER_SERVICE_UP; + break; + default: + pr_debug("%s: Unused opcode 0x%lx\n", __func__, opcode); + ret = -EINVAL; + } + + return ret; +} + +static int audio_notifer_service_cb(unsigned long opcode, + int service, int domain) +{ + int ret = 0; + unsigned long notifier_opcode; + struct audio_notifier_cb_data data; + + if (audio_notifer_convert_opcode(opcode, ¬ifier_opcode) < 0) + goto done; + + data.service = service; + data.domain = domain; + + pr_debug("%s: service %s, opcode 0x%lx\n", + __func__, service_data[service][domain].name, notifier_opcode); + + mutex_lock(¬ifier_mutex); + + service_data[service][domain].state = notifier_opcode; + ret = srcu_notifier_call_chain(&service_data[service][domain]. + client_nb_list, notifier_opcode, &data); + if (ret < 0) + pr_err("%s: srcu_notifier_call_chain returned %d, service %s, opcode 0x%lx\n", + __func__, ret, service_data[service][domain].name, + notifier_opcode); + + mutex_unlock(¬ifier_mutex); +done: + return NOTIFY_OK; +} + +static int audio_notifer_pdr_adsp_cb(struct notifier_block *this, + unsigned long opcode, void *data) +{ + return audio_notifer_service_cb(opcode, + AUDIO_NOTIFIER_PDR_SERVICE, + AUDIO_NOTIFIER_ADSP_DOMAIN); +} + +static int audio_notifer_ssr_adsp_cb(struct notifier_block *this, + unsigned long opcode, void *data) +{ + return audio_notifer_service_cb(opcode, + AUDIO_NOTIFIER_SSR_SERVICE, + AUDIO_NOTIFIER_ADSP_DOMAIN); +} + +static int audio_notifer_ssr_modem_cb(struct notifier_block *this, + unsigned long opcode, void *data) +{ + return audio_notifer_service_cb(opcode, + AUDIO_NOTIFIER_SSR_SERVICE, + AUDIO_NOTIFIER_MODEM_DOMAIN); +} + +int audio_notifier_deregister(char *client_name) +{ + int ret = 0; + int ret2; + struct list_head *ptr, *next; + struct client_data *client_data = NULL; + + if (client_name == NULL) { + pr_err("%s: client_name is NULL\n", __func__); + ret = -EINVAL; + goto done; + } + mutex_lock(¬ifier_mutex); + list_for_each_safe(ptr, next, &client_list) { + client_data = list_entry(ptr, struct client_data, list); + if (!strcmp(client_name, client_data->client_name)) { + ret2 = audio_notifer_dereg_client(client_data); + if (ret2 < 0) { + pr_err("%s: audio_notifer_dereg_client failed, ret %d\n, service %d, domain %d", + __func__, ret2, client_data->service, + client_data->domain); + ret = ret2; + continue; + } + list_del(&client_data->list); + kfree(client_data); + } + } + mutex_unlock(¬ifier_mutex); +done: + return ret; +} +EXPORT_SYMBOL(audio_notifier_deregister); + +int audio_notifier_register(char *client_name, int domain, + struct notifier_block *nb) +{ + int ret; + struct client_data *client_data; + + if (client_name == NULL) { + pr_err("%s: client_name is NULL\n", __func__); + ret = -EINVAL; + goto done; + } else if (nb == NULL) { + pr_err("%s: Notifier block is NULL\n", __func__); + ret = -EINVAL; + goto done; + } + + client_data = kmalloc(sizeof(*client_data), GFP_KERNEL); + if (client_data == NULL) { + ret = -ENOMEM; + goto done; + } + INIT_LIST_HEAD(&client_data->list); + client_data->nb = nb; + strlcpy(client_data->client_name, client_name, + sizeof(client_data->client_name)); + client_data->service = NO_SERVICE; + client_data->domain = domain; + + mutex_lock(¬ifier_mutex); + ret = audio_notifer_reg_client(client_data); + if (ret < 0) { + mutex_unlock(¬ifier_mutex); + pr_err("%s: audio_notifer_reg_client for client %s failed ret = %d\n", + __func__, client_data->client_name, + ret); + kfree(client_data); + goto done; + } + list_add_tail(&client_data->list, &client_list); + mutex_unlock(¬ifier_mutex); +done: + return ret; +} +EXPORT_SYMBOL(audio_notifier_register); + +static int __init audio_notifier_subsys_init(void) +{ + int i, j; + + mutex_init(¬ifier_mutex); + INIT_LIST_HEAD(&client_list); + for (i = 0; i < AUDIO_NOTIFIER_MAX_SERVICES; i++) { + for (j = 0; j < AUDIO_NOTIFIER_MAX_DOMAINS; j++) { + if (service_data[i][j].state <= NO_SERVICE) + continue; + + srcu_init_notifier_head( + &service_data[i][j].client_nb_list); + } + } + + return 0; +} + +static int __init audio_notifier_late_init(void) +{ + /* + * If pdr registration failed, register clients on next service + * Do in late init to ensure that SSR subsystem is initialized + */ + mutex_lock(¬ifier_mutex); + if (!audio_notifer_is_service_enabled(AUDIO_NOTIFIER_PDR_SERVICE)) + audio_notifer_reg_all_clients(); + + mutex_unlock(¬ifier_mutex); + return 0; +} + +static int __init audio_notifier_init(void) +{ + int ret; + + audio_notifier_subsys_init(); + + ret = audio_pdr_register(&pdr_nb); + if (ret < 0) { + pr_err("%s: PDR register failed, ret = %d, disable service\n", + __func__, ret); + audio_notifer_disable_service(AUDIO_NOTIFIER_PDR_SERVICE); + } + + /* Do not return error since PDR enablement is not critical */ + audio_notifier_late_init(); + + return 0; +} +module_init(audio_notifier_init); + +static void __exit audio_notifier_exit(void) +{ + audio_pdr_deregister(&pdr_nb); +} +module_exit(audio_notifier_exit); + +MODULE_DESCRIPTION("Audio notifier driver"); +MODULE_LICENSE("GPL v2"); diff --git a/audio-kernel/dsp/audio_pdr.c b/audio-kernel/dsp/audio_pdr.c new file mode 100644 index 000000000..75f3bbb50 --- /dev/null +++ b/audio-kernel/dsp/audio_pdr.c @@ -0,0 +1,182 @@ +/* Copyright (c) 2016-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 + * 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 +#include +#include +#include +#include "audio_pdr.h" + +static struct pd_qmi_client_data audio_pdr_services[AUDIO_PDR_DOMAIN_MAX] = { + { /* AUDIO_PDR_DOMAIN_ADSP */ + .client_name = "audio_pdr_adsp", + .service_name = "avs/audio" + } +}; + +struct srcu_notifier_head audio_pdr_cb_list; + +static int audio_pdr_locator_callback(struct notifier_block *this, + unsigned long opcode, void *data) +{ + unsigned long pdr_state = AUDIO_PDR_FRAMEWORK_DOWN; + + if (opcode == LOCATOR_DOWN) { + pr_debug("%s: Service %s is down!", __func__, + audio_pdr_services[AUDIO_PDR_DOMAIN_ADSP]. + service_name); + goto done; + } + + memcpy(&audio_pdr_services, data, + sizeof(audio_pdr_services[AUDIO_PDR_DOMAIN_ADSP])); + if (audio_pdr_services[AUDIO_PDR_DOMAIN_ADSP].total_domains == 1) { + pr_debug("%s: Service %s, returned total domains %d, ", + __func__, + audio_pdr_services[AUDIO_PDR_DOMAIN_ADSP].service_name, + audio_pdr_services[AUDIO_PDR_DOMAIN_ADSP]. + total_domains); + pdr_state = AUDIO_PDR_FRAMEWORK_UP; + goto done; + } else + pr_err("%s: Service %s returned invalid total domains %d", + __func__, + audio_pdr_services[AUDIO_PDR_DOMAIN_ADSP].service_name, + audio_pdr_services[AUDIO_PDR_DOMAIN_ADSP]. + total_domains); +done: + srcu_notifier_call_chain(&audio_pdr_cb_list, pdr_state, NULL); + return NOTIFY_OK; +} + +static struct notifier_block audio_pdr_locator_nb = { + .notifier_call = audio_pdr_locator_callback, + .priority = 0, +}; + +/** + * audio_pdr_register - + * register to PDR framework + * + * @nb: notifier block + * + * Returns 0 on success or error on failure + */ +int audio_pdr_register(struct notifier_block *nb) +{ + if (nb == NULL) { + pr_err("%s: Notifier block is NULL\n", __func__); + return -EINVAL; + } + return srcu_notifier_chain_register(&audio_pdr_cb_list, nb); +} +EXPORT_SYMBOL(audio_pdr_register); + +/** + * audio_pdr_deregister - + * Deregister from PDR framework + * + * @nb: notifier block + * + * Returns 0 on success or error on failure + */ +int audio_pdr_deregister(struct notifier_block *nb) +{ + if (nb == NULL) { + pr_err("%s: Notifier block is NULL\n", __func__); + return -EINVAL; + } + return srcu_notifier_chain_unregister(&audio_pdr_cb_list, nb); +} +EXPORT_SYMBOL(audio_pdr_deregister); + +void *audio_pdr_service_register(int domain_id, + struct notifier_block *nb, int *curr_state) +{ + void *handle; + + if ((domain_id < 0) || + (domain_id >= AUDIO_PDR_DOMAIN_MAX)) { + pr_err("%s: Invalid service ID %d\n", __func__, domain_id); + return ERR_PTR(-EINVAL); + } + + handle = service_notif_register_notifier( + audio_pdr_services[domain_id].domain_list[0].name, + audio_pdr_services[domain_id].domain_list[0].instance_id, + nb, curr_state); + if (IS_ERR_OR_NULL(handle)) { + pr_err("%s: Failed to register for service %s, instance %d\n", + __func__, + audio_pdr_services[domain_id].domain_list[0].name, + audio_pdr_services[domain_id].domain_list[0]. + instance_id); + } + return handle; +} +EXPORT_SYMBOL(audio_pdr_service_register); + +int audio_pdr_service_deregister(void *service_handle, + struct notifier_block *nb) +{ + int ret; + + if (service_handle == NULL) { + pr_err("%s: service handle is NULL\n", __func__); + ret = -EINVAL; + goto done; + } + + ret = service_notif_unregister_notifier( + service_handle, nb); + if (ret < 0) + pr_err("%s: Failed to deregister service ret %d\n", + __func__, ret); +done: + return ret; +} +EXPORT_SYMBOL(audio_pdr_service_deregister); + +static int __init audio_pdr_subsys_init(void) +{ + srcu_init_notifier_head(&audio_pdr_cb_list); + return 0; +} + +static int __init audio_pdr_late_init(void) +{ + int ret; + + audio_pdr_subsys_init(); + + ret = get_service_location( + audio_pdr_services[AUDIO_PDR_DOMAIN_ADSP].client_name, + audio_pdr_services[AUDIO_PDR_DOMAIN_ADSP].service_name, + &audio_pdr_locator_nb); + if (ret < 0) { + pr_err("%s get_service_location failed ret %d\n", + __func__, ret); + srcu_notifier_call_chain(&audio_pdr_cb_list, + AUDIO_PDR_FRAMEWORK_DOWN, NULL); + } + + return ret; +} +module_init(audio_pdr_late_init); + +static void __exit audio_pdr_late_exit(void) +{ +} +module_exit(audio_pdr_late_exit); + +MODULE_DESCRIPTION("PDR framework driver"); +MODULE_LICENSE("GPL v2"); diff --git a/audio-kernel/dsp/audio_pdr.h b/audio-kernel/dsp/audio_pdr.h new file mode 100644 index 000000000..d1235bcac --- /dev/null +++ b/audio-kernel/dsp/audio_pdr.h @@ -0,0 +1,106 @@ +/* Copyright (c) 2016-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_PDR_H_ +#define __AUDIO_PDR_H_ + +enum { + AUDIO_PDR_DOMAIN_ADSP, + AUDIO_PDR_DOMAIN_MAX +}; + +enum { + AUDIO_PDR_FRAMEWORK_DOWN, + AUDIO_PDR_FRAMEWORK_UP +}; + +#ifdef CONFIG_MSM_QDSP6_PDR + +/* + * Use audio_pdr_register to register with the PDR subsystem this + * should be done before module late init otherwise notification + * of the AUDIO_PDR_FRAMEWORK_UP cannot be guaranteed. + * + * *nb - Pointer to a notifier block. Provide a callback function + * to be notified once the PDR framework has been initialized. + * Callback will receive either the AUDIO_PDR_FRAMEWORK_DOWN + * or AUDIO_PDR_FRAMEWORK_UP ioctl depending on the state of + * the PDR framework. + * + * Returns: Success: 0 + * Failure: Error code + */ +int audio_pdr_register(struct notifier_block *nb); +int audio_pdr_deregister(struct notifier_block *nb); + +/* + * Use audio_pdr_service_register to register with a PDR service + * Function should be called after nb callback registered with + * audio_pdr_register has been called back with the + * AUDIO_PDR_FRAMEWORK_UP ioctl. + * + * domain_id - Domain to use, example: AUDIO_PDR_ADSP + * *nb - Pointer to a notifier block. Provide a callback function + * that will be notified of the state of the domain + * requested. The ioctls received by the callback are + * defined in service-notifier.h. + * + * Returns: Success: Client handle + * Failure: Pointer error code + */ +void *audio_pdr_service_register(int domain_id, + struct notifier_block *nb, int *curr_state); + +/* + * Use audio_pdr_service_deregister to deregister with a PDR + * service that was registered using the audio_pdr_service_register + * API. + * + * *service_handle - Service handle returned by audio_pdr_service_register + * *nb - Pointer to the notifier block. Used in the call to + * audio_pdr_service_register. + * + * Returns: Success: Client handle + * Failure: Error code + */ +int audio_pdr_service_deregister(void *service_handle, + struct notifier_block *nb); + +#else + +static inline int audio_pdr_register(struct notifier_block *nb) +{ + return -ENODEV; +} + +static inline int audio_pdr_deregister(struct notifier_block *nb) +{ + return -ENODEV; +} + +static inline void *audio_pdr_service_register(int domain_id, + struct notifier_block *nb, + int *curr_state) +{ + return NULL; +} + +static inline int audio_pdr_service_deregister(void *service_handle, + struct notifier_block *nb) +{ + return 0; +} + +#endif /* CONFIG_MSM_QDSP6_PDR */ + +#endif diff --git a/audio-kernel/dsp/audio_slimslave.c b/audio-kernel/dsp/audio_slimslave.c new file mode 100644 index 000000000..55982f334 --- /dev/null +++ b/audio-kernel/dsp/audio_slimslave.c @@ -0,0 +1,175 @@ +/* Copyright (c) 2013-2014, 2017-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 +#include +#include +#include +#include +#include +#include +#include +#include + +static struct slim_device *slim; +static int vote_count; +struct mutex suspend_lock; +bool suspend; + +static int audio_slim_open(struct inode *inode, struct file *file) +{ + pr_debug("%s:\n", __func__); + + if (vote_count) { + pr_debug("%s: unvote: vote_count=%d\n", __func__, vote_count); + pm_runtime_mark_last_busy(slim->dev.parent); + pm_runtime_put(slim->dev.parent); + vote_count--; + } + return 0; +}; + +static int audio_slim_release(struct inode *inode, struct file *file) +{ + pr_debug("%s:\n", __func__); + + if (vote_count) { + pr_debug("%s: unvote: vote_count=%d\n", __func__, vote_count); + pm_runtime_mark_last_busy(slim->dev.parent); + pm_runtime_put(slim->dev.parent); + vote_count--; + } else { + pr_debug("%s: vote: vote_count=%d\n", __func__, vote_count); + pm_runtime_get_sync(slim->dev.parent); + vote_count++; + } + return 0; +}; + +static long audio_slim_ioctl(struct file *file, unsigned int cmd, + unsigned long u_arg) +{ + switch (cmd) { + case AUDIO_SLIMSLAVE_VOTE: + mutex_lock(&suspend_lock); + if (!vote_count && !suspend) { + pr_debug("%s:AUDIO_SLIMSLAVE_VOTE\n", __func__); + pm_runtime_get_sync(slim->dev.parent); + vote_count++; + } else { + pr_err("%s:Invalid vote: vote_count=%d suspend=%d\n", + __func__, vote_count, suspend); + } + mutex_unlock(&suspend_lock); + break; + case AUDIO_SLIMSLAVE_UNVOTE: + mutex_lock(&suspend_lock); + if (vote_count && !suspend) { + pr_debug("%s:AUDIO_SLIMSLAVE_UNVOTE\n", __func__); + pm_runtime_mark_last_busy(slim->dev.parent); + pm_runtime_put(slim->dev.parent); + vote_count--; + } else { + pr_err("%s:Invalid unvote: vote_count=%d suspend=%d\n", + __func__, vote_count, suspend); + } + mutex_unlock(&suspend_lock); + break; + default: + pr_debug("%s: Invalid ioctl cmd: %d\n", __func__, cmd); + break; + } + return 0; +} + +static const struct file_operations audio_slimslave_fops = { + .open = audio_slim_open, + .unlocked_ioctl = audio_slim_ioctl, + .release = audio_slim_release, +}; + +struct miscdevice audio_slimslave_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = AUDIO_SLIMSLAVE_IOCTL_NAME, + .fops = &audio_slimslave_fops, +}; + +static int audio_slimslave_probe(struct slim_device *audio_slim) +{ + pr_debug("%s:\n", __func__); + + mutex_init(&suspend_lock); + suspend = false; + slim = audio_slim; + misc_register(&audio_slimslave_misc); + return 0; +} + +static int audio_slimslave_remove(struct slim_device *audio_slim) +{ + pr_debug("%s:\n", __func__); + + misc_deregister(&audio_slimslave_misc); + return 0; +} + +static int audio_slimslave_resume(struct slim_device *audio_slim) +{ + pr_debug("%s:\n", __func__); + + mutex_lock(&suspend_lock); + suspend = false; + mutex_unlock(&suspend_lock); + return 0; +} + +static int audio_slimslave_suspend(struct slim_device *audio_slim, + pm_message_t pmesg) +{ + pr_debug("%s:\n", __func__); + + mutex_lock(&suspend_lock); + suspend = true; + mutex_unlock(&suspend_lock); + return 0; +} + +static const struct slim_device_id audio_slimslave_dt_match[] = { + {"audio-slimslave", 0}, + {} +}; + +static struct slim_driver audio_slimslave_driver = { + .driver = { + .name = "audio-slimslave", + .owner = THIS_MODULE, + }, + .probe = audio_slimslave_probe, + .remove = audio_slimslave_remove, + .id_table = audio_slimslave_dt_match, + .resume = audio_slimslave_resume, + .suspend = audio_slimslave_suspend, +}; + +int __init audio_slimslave_init(void) +{ + return slim_driver_register(&audio_slimslave_driver); +} + +void audio_slimslave_exit(void) +{ + slim_driver_unregister(&audio_slimslave_driver); +} + +/* Module information */ +MODULE_DESCRIPTION("Audio side Slimbus slave driver"); +MODULE_LICENSE("GPL v2"); diff --git a/audio-kernel/dsp/audio_ssr.c b/audio-kernel/dsp/audio_ssr.c new file mode 100644 index 000000000..b1acb918e --- /dev/null +++ b/audio-kernel/dsp/audio_ssr.c @@ -0,0 +1,59 @@ +/* Copyright (c) 2016, 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 +#include +#include +#include "audio_ssr.h" + +static char *audio_ssr_domains[] = { + "adsp", + "modem" +}; + +/** + * audio_ssr_register - + * register to SSR framework + * + * @domain_id: Domain ID to register with + * @nb: notifier block + * + * Returns handle pointer on success or error PTR on failure + */ +void *audio_ssr_register(int domain_id, struct notifier_block *nb) +{ + if ((domain_id < 0) || + (domain_id >= AUDIO_SSR_DOMAIN_MAX)) { + pr_err("%s: Invalid service ID %d\n", __func__, domain_id); + return ERR_PTR(-EINVAL); + } + + return subsys_notif_register_notifier( + audio_ssr_domains[domain_id], nb); +} +EXPORT_SYMBOL(audio_ssr_register); + +/** + * audio_ssr_deregister - + * Deregister handle from SSR framework + * + * @handle: SSR handle + * @nb: notifier block + * + * Returns 0 on success or error on failure + */ +int audio_ssr_deregister(void *handle, struct notifier_block *nb) +{ + return subsys_notif_unregister_notifier(handle, nb); +} +EXPORT_SYMBOL(audio_ssr_deregister); + diff --git a/audio-kernel/dsp/audio_ssr.h b/audio-kernel/dsp/audio_ssr.h new file mode 100644 index 000000000..a807021ba --- /dev/null +++ b/audio-kernel/dsp/audio_ssr.h @@ -0,0 +1,78 @@ +/* Copyright (c) 2016, 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_SSR_H_ +#define __AUDIO_SSR_H_ + +enum { + AUDIO_SSR_DOMAIN_ADSP, + AUDIO_SSR_DOMAIN_MODEM, + AUDIO_SSR_DOMAIN_MAX +}; + +#ifdef CONFIG_MSM_QDSP6_SSR + +/* + * Use audio_ssr_register to register with the SSR subsystem + * + * domain_id - Service to use, example: AUDIO_SSR_DOMAIN_ADSP + * *nb - Pointer to a notifier block. Provide a callback function + * to be notified of an event for that service. The ioctls + * used by the callback are defined in subsystem_notif.h. + * + * Returns: Success: Client handle + * Failure: Pointer error code + */ +void *audio_ssr_register(int domain_id, struct notifier_block *nb); + +/* + * Use audio_ssr_deregister to register with the SSR subsystem + * + * handle - Handle received from audio_ssr_register + * *nb - Pointer to a notifier block. Callback function + * Used from audio_ssr_register. + * + * Returns: Success: 0 + * Failure: Error code + */ +int audio_ssr_deregister(void *handle, struct notifier_block *nb); + + +/* + * Use audio_ssr_send_nmi to force a RAM dump on ADSP + * down event. + * + * *ssr_cb_data - *data received from notifier callback + */ +void audio_ssr_send_nmi(void *ssr_cb_data); + +#else + +static inline void *audio_ssr_register(int domain_id, + struct notifier_block *nb) +{ + return NULL; +} + +static inline int audio_ssr_deregister(void *handle, struct notifier_block *nb) +{ + return 0; +} + +static inline void audio_ssr_send_nmi(void *ssr_cb_data) +{ +} + +#endif /* CONFIG_MSM_QDSP6_SSR */ + +#endif diff --git a/audio-kernel/dsp/avtimer.c b/audio-kernel/dsp/avtimer.c new file mode 100644 index 000000000..41718ef67 --- /dev/null +++ b/audio-kernel/dsp/avtimer.c @@ -0,0 +1,574 @@ +/* + * Copyright (c) 2012-2015, 2017-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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if IS_ENABLED(CONFIG_AVTIMER_LEGACY) +#include +#endif +#include +#include + +#define DEVICE_NAME "avtimer" +#define TIMEOUT_MS 1000 +#define CORE_CLIENT 1 +#define TEMP_PORT ((CORE_CLIENT << 8) | 0x0001) +#define SSR_WAKETIME 1000 +#define Q6_READY_RETRY 250 +#define Q6_READY_MAX_RETRIES 40 + +#define AVCS_CMD_REMOTE_AVTIMER_VOTE_REQUEST 0x00012914 +#define AVCS_CMD_RSP_REMOTE_AVTIMER_VOTE_REQUEST 0x00012915 +#define AVCS_CMD_REMOTE_AVTIMER_RELEASE_REQUEST 0x00012916 +#define AVTIMER_REG_CNT 2 + +struct adsp_avt_timer { + struct apr_hdr hdr; + union { + char client_name[8]; + u32 avtimer_handle; + }; +} __packed; + +static int major; + +struct avtimer_t { + struct apr_svc *core_handle_q; + struct cdev myc; + struct class *avtimer_class; + struct mutex avtimer_lock; + int avtimer_open_cnt; + struct delayed_work ssr_dwork; + wait_queue_head_t adsp_resp_wait; + int enable_timer_resp_received; + int timer_handle; + void __iomem *p_avtimer_msw; + void __iomem *p_avtimer_lsw; + uint32_t clk_div; + uint32_t clk_mult; + atomic_t adsp_ready; + int num_retries; +}; + +static struct avtimer_t avtimer; +static void avcs_set_isp_fptr(bool enable); + +static int32_t aprv2_core_fn_q(struct apr_client_data *data, void *priv) +{ + uint32_t *payload1; + + if (!data) { + pr_err("%s: Invalid params\n", __func__); + return -EINVAL; + } + pr_debug("%s: core msg: payload len = %u, apr resp opcode = 0x%X\n", + __func__, data->payload_size, data->opcode); + + switch (data->opcode) { + + case APR_BASIC_RSP_RESULT:{ + + if (!data->payload_size) { + pr_err("%s: APR_BASIC_RSP_RESULT No Payload ", + __func__); + return 0; + } + + payload1 = data->payload; + switch (payload1[0]) { + case AVCS_CMD_REMOTE_AVTIMER_RELEASE_REQUEST: + pr_debug("%s: Cmd = TIMER RELEASE status[0x%x]\n", + __func__, payload1[1]); + break; + default: + pr_err("Invalid cmd rsp[0x%x][0x%x]\n", + payload1[0], payload1[1]); + break; + } + break; + } + + case RESET_EVENTS:{ + pr_debug("%s: Reset event received in AV timer\n", __func__); + apr_reset(avtimer.core_handle_q); + avtimer.core_handle_q = NULL; + avtimer.avtimer_open_cnt = 0; + atomic_set(&avtimer.adsp_ready, 0); + schedule_delayed_work(&avtimer.ssr_dwork, + msecs_to_jiffies(SSR_WAKETIME)); + break; + } + + case AVCS_CMD_RSP_REMOTE_AVTIMER_VOTE_REQUEST: + payload1 = data->payload; + pr_debug("%s: RSP_REMOTE_AVTIMER_VOTE_REQUEST handle %x\n", + __func__, payload1[0]); + avtimer.timer_handle = payload1[0]; + avtimer.enable_timer_resp_received = 1; + wake_up(&avtimer.adsp_resp_wait); + break; + default: + pr_err("%s: Message adspcore svc: %d\n", + __func__, data->opcode); + break; + } + + return 0; +} + +int avcs_core_open(void) +{ + if (!avtimer.core_handle_q) + avtimer.core_handle_q = apr_register("ADSP", "CORE", + aprv2_core_fn_q, TEMP_PORT, NULL); + pr_debug("%s: Open_q %p\n", __func__, avtimer.core_handle_q); + if (!avtimer.core_handle_q) { + pr_err("%s: Unable to register CORE\n", __func__); + return -EINVAL; + } + return 0; +} +EXPORT_SYMBOL(avcs_core_open); + +static int avcs_core_disable_avtimer(int timerhandle) +{ + int rc = -EINVAL; + struct adsp_avt_timer payload; + + if (!timerhandle) { + pr_err("%s: Invalid timer handle\n", __func__); + return -EINVAL; + } + memset(&payload, 0, sizeof(payload)); + rc = avcs_core_open(); + if (!rc && avtimer.core_handle_q) { + payload.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + payload.hdr.pkt_size = + sizeof(struct adsp_avt_timer); + payload.hdr.src_svc = avtimer.core_handle_q->id; + payload.hdr.src_domain = APR_DOMAIN_APPS; + payload.hdr.dest_domain = APR_DOMAIN_ADSP; + payload.hdr.dest_svc = APR_SVC_ADSP_CORE; + payload.hdr.src_port = TEMP_PORT; + payload.hdr.dest_port = TEMP_PORT; + payload.hdr.token = CORE_CLIENT; + payload.hdr.opcode = AVCS_CMD_REMOTE_AVTIMER_RELEASE_REQUEST; + payload.avtimer_handle = timerhandle; + pr_debug("%s: disable avtimer opcode %x handle %x\n", + __func__, payload.hdr.opcode, payload.avtimer_handle); + rc = apr_send_pkt(avtimer.core_handle_q, + (uint32_t *)&payload); + if (rc < 0) + pr_err("%s: Enable AVtimer failed op[0x%x]rc[%d]\n", + __func__, payload.hdr.opcode, rc); + else + rc = 0; + } + return rc; +} + +static int avcs_core_enable_avtimer(char *client_name) +{ + int rc = -EINVAL, ret = -EINVAL; + struct adsp_avt_timer payload; + + if (!client_name) { + pr_err("%s: Invalid params\n", __func__); + return -EINVAL; + } + memset(&payload, 0, sizeof(payload)); + rc = avcs_core_open(); + if (!rc && avtimer.core_handle_q) { + avtimer.enable_timer_resp_received = 0; + payload.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_EVENT, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + payload.hdr.pkt_size = + sizeof(struct adsp_avt_timer); + payload.hdr.src_svc = avtimer.core_handle_q->id; + payload.hdr.src_domain = APR_DOMAIN_APPS; + payload.hdr.dest_domain = APR_DOMAIN_ADSP; + payload.hdr.dest_svc = APR_SVC_ADSP_CORE; + payload.hdr.src_port = TEMP_PORT; + payload.hdr.dest_port = TEMP_PORT; + payload.hdr.token = CORE_CLIENT; + payload.hdr.opcode = AVCS_CMD_REMOTE_AVTIMER_VOTE_REQUEST; + strlcpy(payload.client_name, client_name, + sizeof(payload.client_name)); + pr_debug("%s: enable avtimer opcode %x client name %s\n", + __func__, payload.hdr.opcode, payload.client_name); + rc = apr_send_pkt(avtimer.core_handle_q, + (uint32_t *)&payload); + if (rc < 0) { + pr_err("%s: Enable AVtimer failed op[0x%x]rc[%d]\n", + __func__, payload.hdr.opcode, rc); + goto bail; + } else + rc = 0; + ret = wait_event_timeout(avtimer.adsp_resp_wait, + (avtimer.enable_timer_resp_received == 1), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout for Enable timer\n", + __func__); + rc = -ETIMEDOUT; + } + if (rc) + avtimer.timer_handle = 0; + } +bail: + return rc; +} + +int avcs_core_disable_power_collapse(int enable) +{ + int rc = 0; + + mutex_lock(&avtimer.avtimer_lock); + if (enable) { + if (avtimer.avtimer_open_cnt) { + avtimer.avtimer_open_cnt++; + pr_debug("%s: opened avtimer open count=%d\n", + __func__, avtimer.avtimer_open_cnt); + rc = 0; + goto done; + } + rc = avcs_core_enable_avtimer("timer"); + if (!rc) { + avtimer.avtimer_open_cnt++; + atomic_set(&avtimer.adsp_ready, 1); + } + } else { + if (avtimer.avtimer_open_cnt > 0) { + avtimer.avtimer_open_cnt--; + if (!avtimer.avtimer_open_cnt) { + rc = avcs_core_disable_avtimer( + avtimer.timer_handle); + avtimer.timer_handle = 0; + } + } + } +done: + mutex_unlock(&avtimer.avtimer_lock); + return rc; +} +EXPORT_SYMBOL(avcs_core_disable_power_collapse); + +static void reset_work(struct work_struct *work) +{ + if (q6core_is_adsp_ready()) { + avcs_core_disable_power_collapse(1); + avtimer.num_retries = Q6_READY_MAX_RETRIES; + return; + } + pr_debug("%s:Q6 not ready-retry after sometime\n", __func__); + if (--avtimer.num_retries > 0) { + schedule_delayed_work(&avtimer.ssr_dwork, + msecs_to_jiffies(Q6_READY_RETRY)); + } else { + pr_err("%s: Q6 failed responding after multiple retries\n", + __func__); + avtimer.num_retries = Q6_READY_MAX_RETRIES; + } +} + +int avcs_core_query_timer(uint64_t *avtimer_tick) +{ + uint32_t avtimer_msw = 0, avtimer_lsw = 0; + uint64_t avtimer_tick_temp; + + if (!atomic_read(&avtimer.adsp_ready)) { + pr_debug("%s:In SSR, return\n", __func__); + return -ENETRESET; + } + avtimer_lsw = ioread32(avtimer.p_avtimer_lsw); + avtimer_msw = ioread32(avtimer.p_avtimer_msw); + + avtimer_tick_temp = (uint64_t)((uint64_t)avtimer_msw << 32) + | avtimer_lsw; + *avtimer_tick = mul_u64_u32_div(avtimer_tick_temp, avtimer.clk_mult, + avtimer.clk_div); + pr_debug_ratelimited("%s:Avtimer: msw: %u, lsw: %u, tick: %llu\n", + __func__, + avtimer_msw, avtimer_lsw, *avtimer_tick); + return 0; +} +EXPORT_SYMBOL(avcs_core_query_timer); + +#if IS_ENABLED(CONFIG_AVTIMER_LEGACY) +static void avcs_set_isp_fptr(bool enable) +{ + struct avtimer_fptr_t av_fptr; + + if (enable) { + av_fptr.fptr_avtimer_open = avcs_core_open; + av_fptr.fptr_avtimer_enable = avcs_core_disable_power_collapse; + av_fptr.fptr_avtimer_get_time = avcs_core_query_timer; + msm_isp_set_avtimer_fptr(av_fptr); + } else { + av_fptr.fptr_avtimer_open = NULL; + av_fptr.fptr_avtimer_enable = NULL; + av_fptr.fptr_avtimer_get_time = NULL; + msm_isp_set_avtimer_fptr(av_fptr); + } +} +#else +static void avcs_set_isp_fptr(bool enable) +{ +} +#endif + +static int avtimer_open(struct inode *inode, struct file *file) +{ + return avcs_core_disable_power_collapse(1); +} + +static int avtimer_release(struct inode *inode, struct file *file) +{ + return avcs_core_disable_power_collapse(0); +} + +/* + * ioctl call provides GET_AVTIMER + */ +static long avtimer_ioctl(struct file *file, unsigned int ioctl_num, + unsigned long ioctl_param) +{ + switch (ioctl_num) { + case IOCTL_GET_AVTIMER_TICK: + { + uint64_t avtimer_tick = 0; + int rc; + + rc = avcs_core_query_timer(&avtimer_tick); + + if (rc) { + pr_err("%s: Error: Invalid AV Timer tick, rc = %d\n", + __func__, rc); + return rc; + } + + pr_debug_ratelimited("%s: AV Timer tick: time %llx\n", + __func__, avtimer_tick); + if (copy_to_user((void __user *)ioctl_param, &avtimer_tick, + sizeof(avtimer_tick))) { + pr_err("%s: copy_to_user failed\n", __func__); + return -EFAULT; + } + } + break; + + default: + pr_err("%s: invalid cmd\n", __func__); + return -EINVAL; + } + return 0; +} + +static const struct file_operations avtimer_fops = { + .unlocked_ioctl = avtimer_ioctl, + .compat_ioctl = avtimer_ioctl, + .open = avtimer_open, + .release = avtimer_release +}; + +static int dev_avtimer_probe(struct platform_device *pdev) +{ + int result = 0; + dev_t dev = MKDEV(major, 0); + struct device *device_handle; + struct resource *reg_lsb = NULL, *reg_msb = NULL; + uint32_t clk_div_val; + uint32_t clk_mult_val; + + if (!pdev) { + pr_err("%s: Invalid params\n", __func__); + return -EINVAL; + } + reg_lsb = platform_get_resource_byname(pdev, + IORESOURCE_MEM, "avtimer_lsb_addr"); + if (!reg_lsb) { + dev_err(&pdev->dev, "%s: Looking up %s property", + "avtimer_lsb_addr", __func__); + return -EINVAL; + } + reg_msb = platform_get_resource_byname(pdev, + IORESOURCE_MEM, "avtimer_msb_addr"); + if (!reg_msb) { + dev_err(&pdev->dev, "%s: Looking up %s property", + "avtimer_msb_addr", __func__); + return -EINVAL; + } + INIT_DELAYED_WORK(&avtimer.ssr_dwork, reset_work); + + avtimer.p_avtimer_lsw = devm_ioremap_nocache(&pdev->dev, + reg_lsb->start, resource_size(reg_lsb)); + if (!avtimer.p_avtimer_lsw) { + dev_err(&pdev->dev, "%s: ioremap failed for lsb avtimer register", + __func__); + return -ENOMEM; + } + + avtimer.p_avtimer_msw = devm_ioremap_nocache(&pdev->dev, + reg_msb->start, resource_size(reg_msb)); + if (!avtimer.p_avtimer_msw) { + dev_err(&pdev->dev, "%s: ioremap failed for msb avtimer register", + __func__); + goto unmap; + } + avtimer.num_retries = Q6_READY_MAX_RETRIES; + /* get the device number */ + 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) { + dev_err(&pdev->dev, "%s: Registering avtimer device failed\n", + __func__); + goto unmap; + } + + avtimer.avtimer_class = class_create(THIS_MODULE, "avtimer"); + if (IS_ERR(avtimer.avtimer_class)) { + result = PTR_ERR(avtimer.avtimer_class); + dev_err(&pdev->dev, "%s: Error creating avtimer class: %d\n", + __func__, result); + goto unregister_chrdev_region; + } + + cdev_init(&avtimer.myc, &avtimer_fops); + result = cdev_add(&avtimer.myc, dev, 1); + + if (result < 0) { + dev_err(&pdev->dev, "%s: Registering file operations failed\n", + __func__); + goto class_destroy; + } + + device_handle = device_create(avtimer.avtimer_class, + NULL, avtimer.myc.dev, NULL, "avtimer"); + if (IS_ERR(device_handle)) { + result = PTR_ERR(device_handle); + pr_err("%s: device_create failed: %d\n", __func__, result); + goto class_destroy; + } + init_waitqueue_head(&avtimer.adsp_resp_wait); + mutex_init(&avtimer.avtimer_lock); + avtimer.avtimer_open_cnt = 0; + + pr_debug("%s: Device create done for avtimer major=%d\n", + __func__, major); + + if (of_property_read_u32(pdev->dev.of_node, + "qcom,clk-div", &clk_div_val)) + avtimer.clk_div = 1; + else + avtimer.clk_div = clk_div_val; + + if (of_property_read_u32(pdev->dev.of_node, + "qcom,clk-mult", &clk_mult_val)) + avtimer.clk_mult = 1; + else + avtimer.clk_mult = clk_mult_val; + + avcs_set_isp_fptr(true); + + pr_debug("%s: avtimer.clk_div = %d, avtimer.clk_mult = %d\n", + __func__, avtimer.clk_div, avtimer.clk_mult); + return 0; + +class_destroy: + class_destroy(avtimer.avtimer_class); +unregister_chrdev_region: + unregister_chrdev_region(MKDEV(major, 0), 1); +unmap: + if (avtimer.p_avtimer_lsw) + devm_iounmap(&pdev->dev, avtimer.p_avtimer_lsw); + if (avtimer.p_avtimer_msw) + devm_iounmap(&pdev->dev, avtimer.p_avtimer_msw); + avtimer.p_avtimer_lsw = NULL; + avtimer.p_avtimer_msw = NULL; + return result; + +} + +static int dev_avtimer_remove(struct platform_device *pdev) +{ + pr_debug("%s: dev_avtimer_remove\n", __func__); + + if (avtimer.p_avtimer_lsw) + devm_iounmap(&pdev->dev, avtimer.p_avtimer_lsw); + if (avtimer.p_avtimer_msw) + devm_iounmap(&pdev->dev, avtimer.p_avtimer_msw); + device_destroy(avtimer.avtimer_class, avtimer.myc.dev); + cdev_del(&avtimer.myc); + class_destroy(avtimer.avtimer_class); + unregister_chrdev_region(MKDEV(major, 0), 1); + avcs_set_isp_fptr(false); + + return 0; +} + +static const struct of_device_id avtimer_machine_of_match[] = { + { .compatible = "qcom,avtimer", }, + {}, +}; +static struct platform_driver dev_avtimer_driver = { + .probe = dev_avtimer_probe, + .remove = dev_avtimer_remove, + .driver = { + .name = "dev_avtimer", + .of_match_table = avtimer_machine_of_match, + }, +}; + +int __init avtimer_init(void) +{ + s32 rc; + + rc = platform_driver_register(&dev_avtimer_driver); + if (rc < 0) { + pr_err("%s: platform_driver_register failed\n", __func__); + goto error_platform_driver; + } + pr_debug("%s: dev_avtimer_init : done\n", __func__); + + return 0; +error_platform_driver: + + pr_err("%s: encounterd error\n", __func__); + return rc; +} + +void avtimer_exit(void) +{ + platform_driver_unregister(&dev_avtimer_driver); +} + +MODULE_DESCRIPTION("avtimer driver"); +MODULE_LICENSE("GPL v2"); diff --git a/audio-kernel/dsp/codecs/Android.mk b/audio-kernel/dsp/codecs/Android.mk new file mode 100644 index 000000000..0e22e03a8 --- /dev/null +++ b/audio-kernel/dsp/codecs/Android.mk @@ -0,0 +1,62 @@ +# Android makefile for audio kernel modules + +# Assume no targets will be supported + +# Check if this driver needs be built for current target +ifeq ($(call is-board-platform,sdm845),true) +AUDIO_SELECT := CONFIG_SND_SOC_SDM845=m +endif + +ifeq ($(call is-board-platform-in-list,msm8953 sdm670 qcs605),true) +AUDIO_SELECT := CONFIG_SND_SOC_SDM670=m +endif + +ifeq ($(call is-board-platform,msmnile),true) +AUDIO_SELECT := CONFIG_SND_SOC_SM8150=m +endif + +ifeq ($(call is-board-platform,$(MSMSTEPPE) $(TRINKET)),true) +AUDIO_SELECT := CONFIG_SND_SOC_SM6150=m +endif + +AUDIO_CHIPSET := audio +# Build/Package only in case of supported target +ifeq ($(call is-board-platform-in-list,msm8953 sdm845 sdm670 qcs605 msmnile $(MSMSTEPPE) $(TRINKET)),true) + +LOCAL_PATH := $(call my-dir) + +# This makefile is only for DLKM +ifneq ($(findstring vendor,$(LOCAL_PATH)),) + +ifneq ($(findstring opensource,$(LOCAL_PATH)),) + AUDIO_BLD_DIR := $(shell pwd)/vendor/qcom/opensource/audio-kernel +endif # opensource + +DLKM_DIR := $(TOP)/device/qcom/common/dlkm + +# Build audio.ko as $(AUDIO_CHIPSET)_audio.ko +########################################################### +# This is set once per LOCAL_PATH, not per (kernel) module +KBUILD_OPTIONS := AUDIO_ROOT=$(AUDIO_BLD_DIR) + +# We are actually building audio.ko here, as per the +# requirement we are specifying _audio.ko as LOCAL_MODULE. +# This means we need to rename the module to _audio.ko +# after audio.ko is built. +KBUILD_OPTIONS += MODNAME=native_dlkm +KBUILD_OPTIONS += BOARD_PLATFORM=$(TARGET_BOARD_PLATFORM) +KBUILD_OPTIONS += $(AUDIO_SELECT) + +########################################################### +include $(CLEAR_VARS) +LOCAL_MODULE := $(AUDIO_CHIPSET)_native.ko +LOCAL_MODULE_KBUILD_NAME := native_dlkm.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_DEBUG_ENABLE := true +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk +########################################################### +########################################################### + +endif # DLKM check +endif # supported target check diff --git a/audio-kernel/dsp/codecs/Kbuild b/audio-kernel/dsp/codecs/Kbuild new file mode 100644 index 000000000..ab4195a85 --- /dev/null +++ b/audio-kernel/dsp/codecs/Kbuild @@ -0,0 +1,162 @@ +# We can build either as part of a standalone Kernel build or as +# an external module. Determine which mechanism is being used +ifeq ($(MODNAME), ) + KERNEL_BUILD := 1 +else + KERNEL_BUILD := 0 +endif + +ifeq ($(KERNEL_BUILD), 1) + # These are configurable via Kconfig for kernel-based builds + # Need to explicitly configure for Android-based builds + AUDIO_BLD_DIR := $(shell pwd)/kernel/msm-4.14 + AUDIO_ROOT := $(AUDIO_BLD_DIR)/techpack/audio +endif + +ifeq ($(KERNEL_BUILD), 0) + ifeq ($(CONFIG_ARCH_SDM845), y) + include $(AUDIO_ROOT)/config/sdm845auto.conf + export + INCS += -include $(AUDIO_ROOT)/config/sdm845autoconf.h + + endif + ifeq ($(CONFIG_ARCH_SDM670), y) + include $(AUDIO_ROOT)/config/sdm670auto.conf + export + INCS += -include $(AUDIO_ROOT)/config/sdm670autoconf.h + endif + ifeq ($(CONFIG_ARCH_SDM450), y) + include $(AUDIO_ROOT)/config/sdm670auto.conf + export + INCS += -include $(AUDIO_ROOT)/config/sdm670autoconf.h + endif + ifeq ($(CONFIG_ARCH_SM6150), y) + include $(AUDIO_ROOT)/config/sm6150auto.conf + export + INCS += -include $(AUDIO_ROOT)/config/sm6150autoconf.h + endif + ifeq ($(CONFIG_ARCH_TRINKET), y) + include $(AUDIO_ROOT)/config/trinketauto.conf + export + INCS += -include $(AUDIO_ROOT)/config/trinketautoconf.h + endif + ifeq ($(CONFIG_ARCH_SM8150), y) + include $(AUDIO_ROOT)/config/sm8150auto.conf + export + INCS += -include $(AUDIO_ROOT)/config/sm8150autoconf.h + endif + ifeq ($(CONFIG_ARCH_SDMSHRIKE), y) + include $(AUDIO_ROOT)/config/sm8150auto.conf + export + INCS += -include $(AUDIO_ROOT)/config/sm8150autoconf.h + endif + ifeq ($(CONFIG_ARCH_QCS405), y) + include $(AUDIO_ROOT)/config/qcs405auto.conf + export + INCS += -include $(AUDIO_ROOT)/config/qcs405autoconf.h + endif +endif + +# As per target team, build is done as follows: +# Defconfig : build with default flags +# Slub : defconfig + CONFIG_SLUB_DEBUG := y + +# CONFIG_SLUB_DEBUG_ON := y + CONFIG_PAGE_POISONING := y +# Perf : Using appropriate msmXXXX-perf_defconfig +# +# Shipment builds (user variants) should not have any debug feature +# enabled. This is identified using 'TARGET_BUILD_VARIANT'. Slub builds +# are identified using the CONFIG_SLUB_DEBUG_ON configuration. Since +# there is no other way to identify defconfig builds, QTI internal +# representation of perf builds (identified using the string 'perf'), +# is used to identify if the build is a slub or defconfig one. This +# way no critical debug feature will be enabled for perf and shipment +# builds. Other OEMs are also protected using the TARGET_BUILD_VARIANT +# config. + +############ UAPI ############ +UAPI_DIR := uapi +UAPI_INC := -I$(AUDIO_ROOT)/include/$(UAPI_DIR) + +############ COMMON ############ +COMMON_DIR := include +COMMON_INC := -I$(AUDIO_ROOT)/$(COMMON_DIR) + +############ Native Enc/Dec ############ + +ifdef CONFIG_MSM_QDSP6V2_CODECS + NATIVE_OBJS += q6audio_v2.o q6audio_v2_aio.o + NATIVE_OBJS += audio_utils_aio.o + NATIVE_OBJS += audio_utils.o + NATIVE_OBJS += audio_native.o + NATIVE_OBJS += aac_in.o + NATIVE_OBJS += amrnb_in.o + NATIVE_OBJS += amrwb_in.o + NATIVE_OBJS += audio_aac.o + NATIVE_OBJS += audio_alac.o + NATIVE_OBJS += audio_amrnb.o + NATIVE_OBJS += audio_amrwb.o + NATIVE_OBJS += audio_amrwbplus.o + NATIVE_OBJS += audio_ape.o + NATIVE_OBJS += audio_evrc.o + NATIVE_OBJS += audio_g711alaw.o + NATIVE_OBJS += audio_g711mlaw.o + NATIVE_OBJS += audio_hwacc_effects.o + NATIVE_OBJS += audio_mp3.o + NATIVE_OBJS += audio_multi_aac.o + NATIVE_OBJS += audio_qcelp.o + NATIVE_OBJS += audio_wma.o + NATIVE_OBJS += audio_wmapro.o + NATIVE_OBJS += evrc_in.o + NATIVE_OBJS += g711alaw_in.o + NATIVE_OBJS += g711mlaw_in.o + NATIVE_OBJS += qcelp_in.o +endif + +LINUX_INC += -Iinclude/linux + +INCS += $(COMMON_INC) \ + $(UAPI_INC) + +EXTRA_CFLAGS += $(INCS) + + +CDEFINES += -DANI_LITTLE_BYTE_ENDIAN \ + -DANI_LITTLE_BIT_ENDIAN \ + -DDOT11F_LITTLE_ENDIAN_HOST \ + -DANI_COMPILER_TYPE_GCC \ + -DANI_OS_TYPE_ANDROID=6 \ + -DPTT_SOCK_SVC_ENABLE \ + -Wall\ + -Werror\ + -D__linux__ + +KBUILD_CPPFLAGS += $(CDEFINES) + +# Currently, for versions of gcc which support it, the kernel Makefile +# is disabling the maybe-uninitialized warning. Re-enable it for the +# AUDIO driver. Note that we must use EXTRA_CFLAGS here so that it +# will override the kernel settings. +ifeq ($(call cc-option-yn, -Wmaybe-uninitialized),y) +EXTRA_CFLAGS += -Wmaybe-uninitialized +endif +#EXTRA_CFLAGS += -Wmissing-prototypes + +ifeq ($(call cc-option-yn, -Wheader-guard),y) +EXTRA_CFLAGS += -Wheader-guard +endif + +ifeq ($(KERNEL_BUILD), 0) +KBUILD_EXTRA_SYMBOLS +=$(OUT)/obj/vendor/qcom/opensource/audio-kernel/dsp/Module.symvers +KBUILD_EXTRA_SYMBOLS +=$(OUT)/obj/vendor/qcom/opensource/audio-kernel/asoc/Module.symvers +endif + +ifeq ($(CONFIG_SND_SOC_GCOV), y) +GCOV_PROFILE := y +endif + +# Module information used by KBuild framework +obj-$(CONFIG_MSM_QDSP6V2_CODECS) += native_dlkm.o +native_dlkm-y := $(NATIVE_OBJS) + +# inject some build related information +DEFINES += -DBUILD_TIMESTAMP=\"$(shell date -u +'%Y-%m-%dT%H:%M:%SZ')\" diff --git a/audio-kernel/dsp/codecs/aac_in.c b/audio-kernel/dsp/codecs/aac_in.c new file mode 100644 index 000000000..e10fbee60 --- /dev/null +++ b/audio-kernel/dsp/codecs/aac_in.c @@ -0,0 +1,713 @@ +/* + * Copyright (c) 2010-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 + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "audio_utils.h" + + +/* Buffer with meta*/ +#define PCM_BUF_SIZE (4096 + sizeof(struct meta_in)) + +/* Maximum 5 frames in buffer with meta */ +#define FRAME_SIZE (1 + ((1536+sizeof(struct meta_out_dsp)) * 5)) + +#define AAC_FORMAT_ADTS 65535 + +#define MAX_SAMPLE_RATE_384K 384000 + +static long aac_in_ioctl_shared(struct file *file, unsigned int cmd, void *arg) +{ + struct q6audio_in *audio = file->private_data; + int rc = 0; + int cnt = 0; + + switch (cmd) { + case AUDIO_START: { + struct msm_audio_aac_enc_config *enc_cfg; + struct msm_audio_aac_config *aac_config; + uint32_t aac_mode = AAC_ENC_MODE_AAC_LC; + + enc_cfg = audio->enc_cfg; + aac_config = audio->codec_cfg; + /* ENCODE CFG (after new set of API's are published )bharath*/ + pr_debug("%s:session id %d: default buf alloc[%d]\n", __func__, + audio->ac->session, audio->buf_alloc); + if (audio->enabled == 1) { + pr_info("%s:AUDIO_START already over\n", __func__); + rc = 0; + break; + } + + if (audio->opened) { + rc = audio_in_buf_alloc(audio); + if (rc < 0) { + pr_err("%s:session id %d: buffer allocation failed\n", + __func__, audio->ac->session); + break; + } + } else { + if (audio->feedback == NON_TUNNEL_MODE) { + pr_debug("%s: starting in non_tunnel mode", + __func__); + rc = q6asm_open_read_write(audio->ac, + FORMAT_MPEG4_AAC, FORMAT_LINEAR_PCM); + if (rc < 0) { + pr_err("%s:open read write failed\n", + __func__); + break; + } + } + if (audio->feedback == TUNNEL_MODE) { + pr_debug("%s: starting in tunnel mode", + __func__); + rc = q6asm_open_read(audio->ac, + FORMAT_MPEG4_AAC); + + if (rc < 0) { + pr_err("%s:open read failed\n", + __func__); + break; + } + } + audio->stopped = 0; + } + + pr_debug("%s:sbr_ps_flag = %d, sbr_flag = %d\n", __func__, + aac_config->sbr_ps_on_flag, aac_config->sbr_on_flag); + if (aac_config->sbr_ps_on_flag) + aac_mode = AAC_ENC_MODE_EAAC_P; + else if (aac_config->sbr_on_flag) + aac_mode = AAC_ENC_MODE_AAC_P; + else + aac_mode = AAC_ENC_MODE_AAC_LC; + + rc = q6asm_enc_cfg_blk_aac(audio->ac, + audio->buf_cfg.frames_per_buf, + enc_cfg->sample_rate, + enc_cfg->channels, + enc_cfg->bit_rate, + aac_mode, + enc_cfg->stream_format); + if (rc < 0) { + pr_err("%s:session id %d: cmd media format block failed\n", + __func__, audio->ac->session); + break; + } + if (audio->feedback == NON_TUNNEL_MODE) { + rc = q6asm_media_format_block_pcm(audio->ac, + audio->pcm_cfg.sample_rate, + audio->pcm_cfg.channel_count); + if (rc < 0) { + pr_err("%s:session id %d: media format block failed\n", + __func__, audio->ac->session); + break; + } + } + rc = audio_in_enable(audio); + if (!rc) { + audio->enabled = 1; + } else { + audio->enabled = 0; + pr_err("%s:session id %d: Audio Start procedure failed rc=%d\n", + __func__, audio->ac->session, rc); + break; + } + while (cnt++ < audio->str_cfg.buffer_count) + q6asm_read(audio->ac); + pr_debug("%s:session id %d: AUDIO_START success enable[%d]\n", + __func__, audio->ac->session, audio->enabled); + break; + } + case AUDIO_STOP: { + pr_debug("%s:session id %d: Rxed AUDIO_STOP\n", __func__, + audio->ac->session); + rc = audio_in_disable(audio); + if (rc < 0) { + pr_err("%s:session id %d: Audio Stop procedure failed rc=%d\n", + __func__, audio->ac->session, rc); + break; + } + break; + } + case AUDIO_GET_AAC_ENC_CONFIG: { + struct msm_audio_aac_enc_config *cfg; + struct msm_audio_aac_enc_config *enc_cfg; + + cfg = (struct msm_audio_aac_enc_config *)arg; + if (cfg == NULL) { + pr_err("%s: NULL config pointer for %s\n", + __func__, "AUDIO_GET_AAC_CONFIG"); + rc = -EINVAL; + break; + } + memset(cfg, 0, sizeof(*cfg)); + enc_cfg = audio->enc_cfg; + if (enc_cfg->channels == CH_MODE_MONO) + cfg->channels = 1; + else + cfg->channels = 2; + + cfg->sample_rate = enc_cfg->sample_rate; + cfg->bit_rate = enc_cfg->bit_rate; + switch (enc_cfg->stream_format) { + case 0x00: + cfg->stream_format = AUDIO_AAC_FORMAT_ADTS; + break; + case 0x01: + cfg->stream_format = AUDIO_AAC_FORMAT_LOAS; + break; + case 0x02: + cfg->stream_format = AUDIO_AAC_FORMAT_ADIF; + break; + default: + case 0x03: + cfg->stream_format = AUDIO_AAC_FORMAT_RAW; + } + pr_debug("%s:session id %d: Get-aac-cfg: format=%d sr=%d bitrate=%d\n", + __func__, audio->ac->session, + cfg->stream_format, cfg->sample_rate, cfg->bit_rate); + break; + } + case AUDIO_SET_AAC_ENC_CONFIG: { + struct msm_audio_aac_enc_config *cfg; + struct msm_audio_aac_enc_config *enc_cfg; + uint32_t min_bitrate, max_bitrate; + + cfg = (struct msm_audio_aac_enc_config *)arg; + if (cfg == NULL) { + pr_err("%s: NULL config pointer for %s\n", + "AUDIO_SET_AAC_ENC_CONFIG", __func__); + rc = -EINVAL; + break; + } + enc_cfg = audio->enc_cfg; + pr_debug("%s:session id %d: Set-aac-cfg: stream=%d\n", __func__, + audio->ac->session, cfg->stream_format); + + switch (cfg->stream_format) { + case AUDIO_AAC_FORMAT_ADTS: + enc_cfg->stream_format = 0x00; + break; + case AUDIO_AAC_FORMAT_LOAS: + enc_cfg->stream_format = 0x01; + break; + case AUDIO_AAC_FORMAT_ADIF: + enc_cfg->stream_format = 0x02; + break; + case AUDIO_AAC_FORMAT_RAW: + enc_cfg->stream_format = 0x03; + break; + default: + pr_err("%s:session id %d: unsupported AAC format %d\n", + __func__, audio->ac->session, + cfg->stream_format); + rc = -EINVAL; + break; + } + + if (cfg->channels == 1) { + cfg->channels = CH_MODE_MONO; + } else if (cfg->channels == 2) { + cfg->channels = CH_MODE_STEREO; + } else { + rc = -EINVAL; + break; + } + + if (cfg->sample_rate > MAX_SAMPLE_RATE_384K) { + pr_err("%s: ERROR: invalid sample rate = %u", + __func__, cfg->sample_rate); + rc = -EINVAL; + break; + } + + min_bitrate = ((cfg->sample_rate)*(cfg->channels))/2; + /* This calculation should be based on AAC mode. But we cannot + * get AAC mode in this setconfig. min_bitrate's logical max + * value is 24000. So if min_bitrate is higher than 24000, + * choose 24000. + */ + if (min_bitrate > 24000) + min_bitrate = 24000; + max_bitrate = 6*(cfg->sample_rate)*(cfg->channels); + if (max_bitrate > 192000) + max_bitrate = 192000; + if ((cfg->bit_rate < min_bitrate) || + (cfg->bit_rate > max_bitrate)) { + pr_err("%s: bitrate permissible: max=%d, min=%d\n", + __func__, max_bitrate, min_bitrate); + pr_err("%s: ERROR in setting bitrate = %d\n", + __func__, cfg->bit_rate); + rc = -EINVAL; + break; + } + enc_cfg->sample_rate = cfg->sample_rate; + enc_cfg->channels = cfg->channels; + enc_cfg->bit_rate = cfg->bit_rate; + pr_debug("%s:session id %d: Set-aac-cfg:SR= 0x%x ch=0x%x bitrate=0x%x, format(adts/raw) = %d\n", + __func__, audio->ac->session, enc_cfg->sample_rate, + enc_cfg->channels, enc_cfg->bit_rate, + enc_cfg->stream_format); + break; + } + case AUDIO_SET_AAC_CONFIG: { + struct msm_audio_aac_config *aac_cfg; + struct msm_audio_aac_config *audio_aac_cfg; + struct msm_audio_aac_enc_config *enc_cfg; + + enc_cfg = audio->enc_cfg; + audio_aac_cfg = audio->codec_cfg; + aac_cfg = (struct msm_audio_aac_config *)arg; + + if (aac_cfg == NULL) { + pr_err("%s: NULL config pointer %s\n", + __func__, "AUDIO_SET_AAC_CONFIG"); + rc = -EINVAL; + break; + } + pr_debug("%s:session id %d: AUDIO_SET_AAC_CONFIG: sbr_flag = %d sbr_ps_flag = %d\n", + __func__, audio->ac->session, aac_cfg->sbr_on_flag, + aac_cfg->sbr_ps_on_flag); + audio_aac_cfg->sbr_on_flag = aac_cfg->sbr_on_flag; + audio_aac_cfg->sbr_ps_on_flag = aac_cfg->sbr_ps_on_flag; + if ((audio_aac_cfg->sbr_on_flag == 1) || + (audio_aac_cfg->sbr_ps_on_flag == 1)) { + if (enc_cfg->sample_rate < 24000) { + pr_err("%s: ERROR in setting samplerate = %d\n", + __func__, enc_cfg->sample_rate); + rc = -EINVAL; + break; + } + } + break; + } + default: + pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd); + rc = -EINVAL; + } + return rc; +} + +static long aac_in_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct q6audio_in *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_START: + case AUDIO_STOP: { + rc = aac_in_ioctl_shared(file, cmd, NULL); + break; + } + case AUDIO_GET_AAC_ENC_CONFIG: { + struct msm_audio_aac_enc_config cfg; + + rc = aac_in_ioctl_shared(file, cmd, &cfg); + if (rc) { + pr_err("%s:AUDIO_GET_AAC_ENC_CONFIG failed. rc=%d\n", + __func__, rc); + break; + } + if (copy_to_user((void *)arg, &cfg, sizeof(cfg))) { + pr_err("%s: copy_to_user for AUDIO_GET_AAC_ENC_CONFIG failed\n", + __func__); + rc = -EFAULT; + } + break; + } + case AUDIO_SET_AAC_ENC_CONFIG: { + struct msm_audio_aac_enc_config cfg; + + if (copy_from_user(&cfg, (void *)arg, sizeof(cfg))) { + pr_err("%s: copy_from_user for AUDIO_SET_AAC_ENC_CONFIG failed\n", + __func__); + rc = -EFAULT; + break; + } + rc = aac_in_ioctl_shared(file, cmd, &cfg); + if (rc) + pr_err("%s:AUDIO_SET_AAC_ENC_CONFIG failed. rc=%d\n", + __func__, rc); + break; + } + case AUDIO_GET_AAC_CONFIG: { + if (copy_to_user((void *)arg, &audio->codec_cfg, + sizeof(struct msm_audio_aac_config))) { + pr_err("%s: copy_to_user for AUDIO_GET_AAC_CONFIG failed\n", + __func__); + rc = -EFAULT; + break; + } + break; + } + case AUDIO_SET_AAC_CONFIG: { + struct msm_audio_aac_config aac_cfg; + + if (copy_from_user(&aac_cfg, (void *)arg, + sizeof(struct msm_audio_aac_config))) { + pr_err("%s: copy_to_user for AUDIO_SET_CONFIG failed\n", + __func__); + rc = -EFAULT; + break; + } + rc = aac_in_ioctl_shared(file, cmd, &aac_cfg); + if (rc) + pr_err("%s:AUDIO_SET_AAC_CONFIG failed. rc=%d\n", + __func__, rc); + break; + } + default: + pr_err("%s: Unknown ioctl cmd=%d\n", __func__, cmd); + rc = -EINVAL; + } + return rc; +} + +#ifdef CONFIG_COMPAT +struct msm_audio_aac_enc_config32 { + u32 channels; + u32 sample_rate; + u32 bit_rate; + u32 stream_format; +}; + +struct msm_audio_aac_config32 { + s16 format; + u16 audio_object; + u16 ep_config; /* 0 ~ 3 useful only obj = ERLC */ + u16 aac_section_data_resilience_flag; + u16 aac_scalefactor_data_resilience_flag; + u16 aac_spectral_data_resilience_flag; + u16 sbr_on_flag; + u16 sbr_ps_on_flag; + u16 dual_mono_mode; + u16 channel_configuration; + u16 sample_rate; +}; + +enum { + AUDIO_SET_AAC_CONFIG_32 = _IOW(AUDIO_IOCTL_MAGIC, + (AUDIO_MAX_COMMON_IOCTL_NUM+0), struct msm_audio_aac_config32), + AUDIO_GET_AAC_CONFIG_32 = _IOR(AUDIO_IOCTL_MAGIC, + (AUDIO_MAX_COMMON_IOCTL_NUM+1), struct msm_audio_aac_config32), + AUDIO_SET_AAC_ENC_CONFIG_32 = _IOW(AUDIO_IOCTL_MAGIC, + (AUDIO_MAX_COMMON_IOCTL_NUM+3), struct msm_audio_aac_enc_config32), + AUDIO_GET_AAC_ENC_CONFIG_32 = _IOR(AUDIO_IOCTL_MAGIC, + (AUDIO_MAX_COMMON_IOCTL_NUM+4), struct msm_audio_aac_enc_config32) +}; + +static long aac_in_compat_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct q6audio_in *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_START: + case AUDIO_STOP: { + rc = aac_in_ioctl_shared(file, cmd, NULL); + break; + } + case AUDIO_GET_AAC_ENC_CONFIG_32: { + struct msm_audio_aac_enc_config cfg; + struct msm_audio_aac_enc_config32 cfg_32; + + memset(&cfg_32, 0, sizeof(cfg_32)); + + cmd = AUDIO_GET_AAC_ENC_CONFIG; + rc = aac_in_ioctl_shared(file, cmd, &cfg); + if (rc) { + pr_err("%s:AUDIO_GET_AAC_ENC_CONFIG_32 failed. Rc= %d\n", + __func__, rc); + break; + } + cfg_32.channels = cfg.channels; + cfg_32.sample_rate = cfg.sample_rate; + cfg_32.bit_rate = cfg.bit_rate; + cfg_32.stream_format = cfg.stream_format; + if (copy_to_user((void *)arg, &cfg_32, sizeof(cfg_32))) { + pr_err("%s: copy_to_user for AUDIO_GET_AAC_ENC_CONFIG_32 failed\n", + __func__); + rc = -EFAULT; + } + break; + } + case AUDIO_SET_AAC_ENC_CONFIG_32: { + struct msm_audio_aac_enc_config cfg; + struct msm_audio_aac_enc_config32 cfg_32; + + if (copy_from_user(&cfg_32, (void *)arg, sizeof(cfg_32))) { + pr_err("%s: copy_from_user for AUDIO_GET_AAC_ENC_CONFIG_32 failed\n", + __func__); + rc = -EFAULT; + break; + } + cfg.channels = cfg_32.channels; + cfg.sample_rate = cfg_32.sample_rate; + cfg.bit_rate = cfg_32.bit_rate; + cfg.stream_format = cfg_32.stream_format; + /* The command should be converted from 32 bit to normal + * before the shared ioctl is called as shared ioctl + * can process only normal commands + */ + cmd = AUDIO_SET_AAC_ENC_CONFIG; + rc = aac_in_ioctl_shared(file, cmd, &cfg); + if (rc) + pr_err("%s:AUDIO_SET_AAC_ENC_CONFIG_32 failed. rc=%d\n", + __func__, rc); + break; + } + case AUDIO_GET_AAC_CONFIG_32: { + struct msm_audio_aac_config *aac_config; + struct msm_audio_aac_config32 aac_config_32; + + aac_config = (struct msm_audio_aac_config *)audio->codec_cfg; + aac_config_32.format = aac_config->format; + aac_config_32.audio_object = aac_config->audio_object; + aac_config_32.ep_config = aac_config->ep_config; + aac_config_32.aac_section_data_resilience_flag = + aac_config->aac_section_data_resilience_flag; + aac_config_32.aac_scalefactor_data_resilience_flag = + aac_config->aac_scalefactor_data_resilience_flag; + aac_config_32.aac_spectral_data_resilience_flag = + aac_config->aac_spectral_data_resilience_flag; + aac_config_32.sbr_on_flag = aac_config->sbr_on_flag; + aac_config_32.sbr_ps_on_flag = aac_config->sbr_ps_on_flag; + aac_config_32.dual_mono_mode = aac_config->dual_mono_mode; + aac_config_32.channel_configuration = + aac_config->channel_configuration; + aac_config_32.sample_rate = aac_config->sample_rate; + + if (copy_to_user((void *)arg, &aac_config_32, + sizeof(aac_config_32))) { + pr_err("%s: copy_to_user for AUDIO_GET_AAC_CONFIG_32 failed\n", + __func__); + rc = -EFAULT; + break; + } + break; + } + case AUDIO_SET_AAC_CONFIG_32: { + struct msm_audio_aac_config aac_cfg; + struct msm_audio_aac_config32 aac_cfg_32; + + if (copy_from_user(&aac_cfg_32, (void *)arg, + sizeof(aac_cfg_32))) { + pr_err("%s: copy_from_user for AUDIO_SET_AAC_CONFIG_32 failed\n", + __func__); + rc = -EFAULT; + break; + } + aac_cfg.format = aac_cfg_32.format; + aac_cfg.audio_object = aac_cfg_32.audio_object; + aac_cfg.ep_config = aac_cfg_32.ep_config; + aac_cfg.aac_section_data_resilience_flag = + aac_cfg_32.aac_section_data_resilience_flag; + aac_cfg.aac_scalefactor_data_resilience_flag = + aac_cfg_32.aac_scalefactor_data_resilience_flag; + aac_cfg.aac_spectral_data_resilience_flag = + aac_cfg_32.aac_spectral_data_resilience_flag; + aac_cfg.sbr_on_flag = aac_cfg_32.sbr_on_flag; + aac_cfg.sbr_ps_on_flag = aac_cfg_32.sbr_ps_on_flag; + aac_cfg.dual_mono_mode = aac_cfg_32.dual_mono_mode; + aac_cfg.channel_configuration = + aac_cfg_32.channel_configuration; + aac_cfg.sample_rate = aac_cfg_32.sample_rate; + + cmd = AUDIO_SET_AAC_CONFIG; + rc = aac_in_ioctl_shared(file, cmd, &aac_cfg); + if (rc) + pr_err("%s:AUDIO_SET_AAC_CONFIG failed. Rc= %d\n", + __func__, rc); + break; + } + default: + pr_err("%s: Unknown ioctl cmd = %d\n", __func__, cmd); + rc = -EINVAL; + } + return rc; +} +#else +#define aac_in_compat_ioctl NULL +#endif + +static int aac_in_open(struct inode *inode, struct file *file) +{ + struct q6audio_in *audio = NULL; + struct msm_audio_aac_enc_config *enc_cfg; + struct msm_audio_aac_config *aac_config; + int rc = 0; + + audio = kzalloc(sizeof(struct q6audio_in), GFP_KERNEL); + + if (audio == NULL) + return -ENOMEM; + + /* Allocate memory for encoder config param */ + audio->enc_cfg = kzalloc(sizeof(struct msm_audio_aac_enc_config), + GFP_KERNEL); + if (audio->enc_cfg == NULL) { + kfree(audio); + return -ENOMEM; + } + enc_cfg = audio->enc_cfg; + + audio->codec_cfg = kzalloc(sizeof(struct msm_audio_aac_config), + GFP_KERNEL); + if (audio->codec_cfg == NULL) { + kfree(audio->enc_cfg); + kfree(audio); + return -ENOMEM; + } + aac_config = audio->codec_cfg; + + mutex_init(&audio->lock); + mutex_init(&audio->read_lock); + mutex_init(&audio->write_lock); + spin_lock_init(&audio->dsp_lock); + init_waitqueue_head(&audio->read_wait); + init_waitqueue_head(&audio->write_wait); + + /* Settings will be re-config at AUDIO_SET_CONFIG, + * but at least we need to have initial config + */ + audio->str_cfg.buffer_size = FRAME_SIZE; + audio->str_cfg.buffer_count = FRAME_NUM; + audio->min_frame_size = 1536; + audio->max_frames_per_buf = 5; + enc_cfg->sample_rate = 8000; + enc_cfg->channels = 1; + enc_cfg->bit_rate = 16000; + enc_cfg->stream_format = 0x00;/* 0:ADTS, 3:RAW */ + audio->buf_cfg.meta_info_enable = 0x01; + audio->buf_cfg.frames_per_buf = 0x01; + audio->pcm_cfg.buffer_count = PCM_BUF_COUNT; + audio->pcm_cfg.buffer_size = PCM_BUF_SIZE; + aac_config->format = AUDIO_AAC_FORMAT_ADTS; + aac_config->audio_object = AUDIO_AAC_OBJECT_LC; + aac_config->sbr_on_flag = 0; + aac_config->sbr_ps_on_flag = 0; + aac_config->channel_configuration = 1; + + audio->ac = q6asm_audio_client_alloc((app_cb)q6asm_in_cb, + (void *)audio); + + if (!audio->ac) { + pr_err("%s: Could not allocate memory for audio client\n", + __func__); + kfree(audio->enc_cfg); + kfree(audio->codec_cfg); + kfree(audio); + return -ENOMEM; + } + /* open aac encoder in tunnel mode */ + audio->buf_cfg.frames_per_buf = 0x01; + + if ((file->f_mode & FMODE_WRITE) && + (file->f_mode & FMODE_READ)) { + audio->feedback = NON_TUNNEL_MODE; + rc = q6asm_open_read_write(audio->ac, FORMAT_MPEG4_AAC, + FORMAT_LINEAR_PCM); + + if (rc < 0) { + pr_err("%s:session id %d: NT Open failed rc=%d\n", + __func__, audio->ac->session, rc); + rc = -ENODEV; + goto fail; + } + audio->buf_cfg.meta_info_enable = 0x01; + pr_info("%s:session id %d: NT mode encoder success\n", __func__, + audio->ac->session); + } else if (!(file->f_mode & FMODE_WRITE) && + (file->f_mode & FMODE_READ)) { + audio->feedback = TUNNEL_MODE; + rc = q6asm_open_read(audio->ac, FORMAT_MPEG4_AAC); + + if (rc < 0) { + pr_err("%s:session id %d: Tunnel Open failed rc=%d\n", + __func__, audio->ac->session, rc); + rc = -ENODEV; + goto fail; + } + /* register for tx overflow (valid for tunnel mode only) */ + rc = q6asm_reg_tx_overflow(audio->ac, 0x01); + if (rc < 0) { + pr_err("%s:session id %d: TX Overflow registration failed rc=%d\n", + __func__, + audio->ac->session, rc); + rc = -ENODEV; + goto fail; + } + audio->buf_cfg.meta_info_enable = 0x00; + pr_info("%s:session id %d: T mode encoder success\n", __func__, + audio->ac->session); + } else { + pr_err("%s:session id %d: Unexpected mode\n", __func__, + audio->ac->session); + rc = -EACCES; + goto fail; + } + audio->opened = 1; + audio->reset_event = false; + atomic_set(&audio->in_count, PCM_BUF_COUNT); + atomic_set(&audio->out_count, 0x00); + audio->enc_compat_ioctl = aac_in_compat_ioctl; + audio->enc_ioctl = aac_in_ioctl; + file->private_data = audio; + + pr_info("%s:session id %d: success\n", __func__, audio->ac->session); + return 0; +fail: + q6asm_audio_client_free(audio->ac); + kfree(audio->enc_cfg); + kfree(audio->codec_cfg); + kfree(audio); + return rc; +} + +static const struct file_operations audio_in_fops = { + .owner = THIS_MODULE, + .open = aac_in_open, + .release = audio_in_release, + .read = audio_in_read, + .write = audio_in_write, + .unlocked_ioctl = audio_in_ioctl, + .compat_ioctl = audio_in_compat_ioctl +}; + +struct miscdevice audio_aac_in_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_aac_in", + .fops = &audio_in_fops, +}; + +int __init aac_in_init(void) +{ + return misc_register(&audio_aac_in_misc); +} + +void aac_in_exit(void) +{ + misc_deregister(&audio_aac_in_misc); +} diff --git a/audio-kernel/dsp/codecs/amrnb_in.c b/audio-kernel/dsp/codecs/amrnb_in.c new file mode 100644 index 000000000..4e3586036 --- /dev/null +++ b/audio-kernel/dsp/codecs/amrnb_in.c @@ -0,0 +1,405 @@ +/* + * Copyright (c) 2010-2012, 2014, 2016-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 + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "audio_utils.h" + +/* Buffer with meta*/ +#define PCM_BUF_SIZE (4096 + sizeof(struct meta_in)) + +/* Maximum 10 frames in buffer with meta */ +#define FRAME_SIZE (1 + ((32+sizeof(struct meta_out_dsp)) * 10)) + +static long amrnb_in_ioctl_shared(struct file *file, + unsigned int cmd, void *arg) +{ + struct q6audio_in *audio = file->private_data; + int rc = 0; + int cnt = 0; + + switch (cmd) { + case AUDIO_START: { + struct msm_audio_amrnb_enc_config_v2 *enc_cfg; + + enc_cfg = audio->enc_cfg; + pr_debug("%s:session id %d: default buf alloc[%d]\n", __func__, + audio->ac->session, audio->buf_alloc); + if (audio->enabled == 1) { + pr_info("%s:AUDIO_START already over\n", __func__); + rc = 0; + break; + } + rc = audio_in_buf_alloc(audio); + if (rc < 0) { + pr_err("%s:session id %d: buffer allocation failed\n", + __func__, audio->ac->session); + break; + } + + rc = q6asm_enc_cfg_blk_amrnb(audio->ac, + audio->buf_cfg.frames_per_buf, + enc_cfg->band_mode, + enc_cfg->dtx_enable); + + if (rc < 0) { + pr_err("%s:session id %d: cmd amrnb media format block failed\n", + __func__, audio->ac->session); + break; + } + if (audio->feedback == NON_TUNNEL_MODE) { + rc = q6asm_media_format_block_pcm(audio->ac, + audio->pcm_cfg.sample_rate, + audio->pcm_cfg.channel_count); + + if (rc < 0) { + pr_err("%s:session id %d: media format block failed\n", + __func__, audio->ac->session); + break; + } + } + pr_debug("%s:session id %d: AUDIO_START enable[%d]\n", + __func__, audio->ac->session, + audio->enabled); + rc = audio_in_enable(audio); + if (!rc) { + audio->enabled = 1; + } else { + audio->enabled = 0; + pr_err("%s:session id %d: Audio Start procedure failed rc=%d\n", + __func__, audio->ac->session, rc); + break; + } + while (cnt++ < audio->str_cfg.buffer_count) + q6asm_read(audio->ac); /* Push buffer to DSP */ + rc = 0; + pr_debug("%s:session id %d: AUDIO_START success enable[%d]\n", + __func__, audio->ac->session, audio->enabled); + break; + } + case AUDIO_STOP: { + pr_debug("%s:AUDIO_STOP\n", __func__); + rc = audio_in_disable(audio); + if (rc < 0) { + pr_err("%s:session id %d: Audio Stop procedure failed rc=%d\n", + __func__, audio->ac->session, rc); + break; + } + break; + } + case AUDIO_SET_AMRNB_ENC_CONFIG_V2: { + struct msm_audio_amrnb_enc_config_v2 *cfg; + struct msm_audio_amrnb_enc_config_v2 *enc_cfg; + + cfg = (struct msm_audio_amrnb_enc_config_v2 *)arg; + if (cfg == NULL) { + pr_err("%s: NULL config pointer for %s\n", + __func__, + "AUDIO_SET_AMRNB_ENC_CONFIG_V2"); + rc = -EINVAL; + break; + } + + enc_cfg = audio->enc_cfg; + if (cfg->band_mode > 8 || + cfg->band_mode < 1) { + pr_err("%s:session id %d: invalid band mode\n", + __func__, audio->ac->session); + rc = -EINVAL; + break; + } + /* AMR NB encoder accepts values between 0-7 + * while openmax provides value between 1-8 + * as per spec + */ + enc_cfg->band_mode = (cfg->band_mode - 1); + enc_cfg->dtx_enable = (cfg->dtx_enable ? 1 : 0); + enc_cfg->frame_format = 0; + pr_debug("%s:session id %d: band_mode = 0x%x dtx_enable=0x%x\n", + __func__, audio->ac->session, + enc_cfg->band_mode, enc_cfg->dtx_enable); + break; + } + default: + pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd); + rc = -EINVAL; + } + return rc; +} + +static long amrnb_in_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct q6audio_in *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_START: + case AUDIO_STOP: { + rc = amrnb_in_ioctl_shared(file, cmd, NULL); + break; + } + case AUDIO_GET_AMRNB_ENC_CONFIG_V2: { + if (copy_to_user((void *)arg, audio->enc_cfg, + sizeof(struct msm_audio_amrnb_enc_config_v2))) { + pr_err("%s: copy_to_user for AUDIO_GET_AMRNB_ENC_CONFIG_V2 failed\n", + __func__); + rc = -EFAULT; + } + break; + } + case AUDIO_SET_AMRNB_ENC_CONFIG_V2: { + struct msm_audio_amrnb_enc_config_v2 cfg; + + if (copy_from_user(&cfg, (void *) arg, + sizeof(cfg))) { + pr_err("%s: copy_from_user for AUDIO_SET_AMRNB_ENC_CONFIG_V2 failed\n", + __func__); + rc = -EFAULT; + break; + } + rc = amrnb_in_ioctl_shared(file, cmd, &cfg); + if (rc) + pr_err("%s: AUDIO_SET_AMRNB_ENC_CONFIG_V2 failed. rc=%d\n", + __func__, rc); + break; + } + default: + pr_err("%s: Unknown ioctl cmd=%d", __func__, cmd); + rc = -EINVAL; + } + return rc; +} + +#ifdef CONFIG_COMPAT +struct msm_audio_amrnb_enc_config_v2_32 { + u32 band_mode; + u32 dtx_enable; + u32 frame_format; +}; + +enum { + AUDIO_GET_AMRNB_ENC_CONFIG_V2_32 = _IOW(AUDIO_IOCTL_MAGIC, + (AUDIO_MAX_COMMON_IOCTL_NUM+2), + struct msm_audio_amrnb_enc_config_v2_32), + AUDIO_SET_AMRNB_ENC_CONFIG_V2_32 = _IOR(AUDIO_IOCTL_MAGIC, + (AUDIO_MAX_COMMON_IOCTL_NUM+3), + struct msm_audio_amrnb_enc_config_v2_32) +}; + +static long amrnb_in_compat_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct q6audio_in *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_START: + case AUDIO_STOP: { + rc = amrnb_in_ioctl_shared(file, cmd, NULL); + break; + } + case AUDIO_GET_AMRNB_ENC_CONFIG_V2_32: { + struct msm_audio_amrnb_enc_config_v2 *amrnb_config; + struct msm_audio_amrnb_enc_config_v2_32 amrnb_config_32; + + memset(&amrnb_config_32, 0, sizeof(amrnb_config_32)); + + amrnb_config = + (struct msm_audio_amrnb_enc_config_v2 *)audio->enc_cfg; + amrnb_config_32.band_mode = amrnb_config->band_mode; + amrnb_config_32.dtx_enable = amrnb_config->dtx_enable; + amrnb_config_32.frame_format = amrnb_config->frame_format; + + if (copy_to_user((void *)arg, &amrnb_config_32, + sizeof(amrnb_config_32))) { + pr_err("%s: copy_to_user for AUDIO_GET_AMRNB_ENC_CONFIG_V2_32 failed", + __func__); + rc = -EFAULT; + } + break; + } + case AUDIO_SET_AMRNB_ENC_CONFIG_V2_32: { + struct msm_audio_amrnb_enc_config_v2_32 cfg_32; + + if (copy_from_user(&cfg_32, (void *) arg, + sizeof(cfg_32))) { + pr_err("%s: copy_from_user for AUDIO_SET_AMRNB_ENC_CONFIG_V2_32 failed\n", + __func__); + rc = -EFAULT; + break; + } + cmd = AUDIO_SET_AMRNB_ENC_CONFIG_V2; + rc = amrnb_in_ioctl_shared(file, cmd, &cfg_32); + if (rc) + pr_err("%s:AUDIO_SET_AMRNB_ENC_CONFIG_V2 failed rc= %d\n", + __func__, rc); + break; + } + default: + pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd); + rc = -EINVAL; + } + return rc; +} +#else +#define amrnb_in_compat_ioctl NULL +#endif + +static int amrnb_in_open(struct inode *inode, struct file *file) +{ + struct q6audio_in *audio = NULL; + struct msm_audio_amrnb_enc_config_v2 *enc_cfg; + int rc = 0; + + audio = kzalloc(sizeof(struct q6audio_in), GFP_KERNEL); + + if (audio == NULL) + return -ENOMEM; + + /* Allocate memory for encoder config param */ + audio->enc_cfg = kzalloc(sizeof(struct msm_audio_amrnb_enc_config_v2), + GFP_KERNEL); + if (audio->enc_cfg == NULL) { + kfree(audio); + return -ENOMEM; + } + enc_cfg = audio->enc_cfg; + + mutex_init(&audio->lock); + mutex_init(&audio->read_lock); + mutex_init(&audio->write_lock); + spin_lock_init(&audio->dsp_lock); + init_waitqueue_head(&audio->read_wait); + init_waitqueue_head(&audio->write_wait); + + /* Settings will be re-config at AUDIO_SET_CONFIG, + * but at least we need to have initial config + */ + audio->str_cfg.buffer_size = FRAME_SIZE; + audio->str_cfg.buffer_count = FRAME_NUM; + audio->min_frame_size = 32; + audio->max_frames_per_buf = 10; + audio->pcm_cfg.buffer_size = PCM_BUF_SIZE; + audio->pcm_cfg.buffer_count = PCM_BUF_COUNT; + enc_cfg->band_mode = 7; + enc_cfg->dtx_enable = 0; + audio->pcm_cfg.channel_count = 1; + audio->pcm_cfg.sample_rate = 8000; + audio->buf_cfg.meta_info_enable = 0x01; + audio->buf_cfg.frames_per_buf = 0x01; + + audio->ac = q6asm_audio_client_alloc((app_cb)q6asm_in_cb, + (void *)audio); + + if (!audio->ac) { + pr_err("%s: Could not allocate memory for audio client\n", + __func__); + kfree(audio->enc_cfg); + kfree(audio); + return -ENOMEM; + } + + /* open amrnb encoder in T/NT mode */ + if ((file->f_mode & FMODE_WRITE) && + (file->f_mode & FMODE_READ)) { + audio->feedback = NON_TUNNEL_MODE; + rc = q6asm_open_read_write(audio->ac, FORMAT_AMRNB, + FORMAT_LINEAR_PCM); + if (rc < 0) { + pr_err("%s:session id %d: NT mode Open failed rc=%d\n", + __func__, audio->ac->session, rc); + rc = -ENODEV; + goto fail; + } + pr_info("%s:session id %d: NT mode encoder success\n", + __func__, audio->ac->session); + } else if (!(file->f_mode & FMODE_WRITE) && + (file->f_mode & FMODE_READ)) { + audio->feedback = TUNNEL_MODE; + rc = q6asm_open_read(audio->ac, FORMAT_AMRNB); + if (rc < 0) { + pr_err("%s:session id %d: T mode Open failed rc=%d\n", + __func__, audio->ac->session, rc); + rc = -ENODEV; + goto fail; + } + /* register for tx overflow (valid for tunnel mode only) */ + rc = q6asm_reg_tx_overflow(audio->ac, 0x01); + if (rc < 0) { + pr_err("%s:session id %d: TX Overflow registration failed rc=%d\n", + __func__, audio->ac->session, + rc); + rc = -ENODEV; + goto fail; + } + pr_info("%s:session id %d: T mode encoder success\n", + __func__, audio->ac->session); + } else { + pr_err("%s:session id %d: Unexpected mode\n", __func__, + audio->ac->session); + rc = -EACCES; + goto fail; + } + + audio->opened = 1; + atomic_set(&audio->in_count, PCM_BUF_COUNT); + atomic_set(&audio->out_count, 0x00); + audio->enc_compat_ioctl = amrnb_in_compat_ioctl; + audio->enc_ioctl = amrnb_in_ioctl; + file->private_data = audio; + + pr_info("%s:session id %d: success\n", __func__, audio->ac->session); + return 0; +fail: + q6asm_audio_client_free(audio->ac); + kfree(audio->enc_cfg); + kfree(audio); + return rc; +} + +static const struct file_operations audio_in_fops = { + .owner = THIS_MODULE, + .open = amrnb_in_open, + .release = audio_in_release, + .read = audio_in_read, + .write = audio_in_write, + .unlocked_ioctl = audio_in_ioctl, + .compat_ioctl = audio_in_compat_ioctl +}; + +struct miscdevice audio_amrnb_in_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_amrnb_in", + .fops = &audio_in_fops, +}; + +int __init amrnb_in_init(void) +{ + return misc_register(&audio_amrnb_in_misc); +} + +void amrnb_in_exit(void) +{ + misc_deregister(&audio_amrnb_in_misc); +} diff --git a/audio-kernel/dsp/codecs/amrwb_in.c b/audio-kernel/dsp/codecs/amrwb_in.c new file mode 100644 index 000000000..ce9dc45c1 --- /dev/null +++ b/audio-kernel/dsp/codecs/amrwb_in.c @@ -0,0 +1,403 @@ +/* + * Copyright (c) 2011-2012, 2014, 2016-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 + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "audio_utils.h" + +/* Buffer with meta*/ +#define PCM_BUF_SIZE (4096 + sizeof(struct meta_in)) + +/* Maximum 10 frames in buffer with meta */ +#define FRAME_SIZE (1 + ((61+sizeof(struct meta_out_dsp)) * 10)) + +static long amrwb_in_ioctl_shared(struct file *file, + unsigned int cmd, void *arg) +{ + struct q6audio_in *audio = file->private_data; + int rc = 0; + int cnt = 0; + + switch (cmd) { + case AUDIO_START: { + struct msm_audio_amrwb_enc_config *enc_cfg; + + enc_cfg = audio->enc_cfg; + pr_debug("%s:session id %d: default buf alloc[%d]\n", __func__, + audio->ac->session, audio->buf_alloc); + if (audio->enabled == 1) { + pr_info("%s:AUDIO_START already over\n", __func__); + rc = 0; + break; + } + rc = audio_in_buf_alloc(audio); + if (rc < 0) { + pr_err("%s:session id %d: buffer allocation failed\n", + __func__, audio->ac->session); + break; + } + + rc = q6asm_enc_cfg_blk_amrwb(audio->ac, + audio->buf_cfg.frames_per_buf, + enc_cfg->band_mode, + enc_cfg->dtx_enable); + + if (rc < 0) { + pr_err("%s:session id %d: cmd amrwb media format block failed\n", + __func__, audio->ac->session); + break; + } + if (audio->feedback == NON_TUNNEL_MODE) { + rc = q6asm_media_format_block_pcm(audio->ac, + audio->pcm_cfg.sample_rate, + audio->pcm_cfg.channel_count); + + if (rc < 0) { + pr_err("%s:session id %d: media format block failed\n", + __func__, audio->ac->session); + break; + } + } + pr_debug("%s:session id %d: AUDIO_START enable[%d]\n", + __func__, audio->ac->session, + audio->enabled); + rc = audio_in_enable(audio); + if (!rc) { + audio->enabled = 1; + } else { + audio->enabled = 0; + pr_err("%s:session id %d: Audio Start procedure failed rc=%d\n", + __func__, audio->ac->session, rc); + break; + } + while (cnt++ < audio->str_cfg.buffer_count) + q6asm_read(audio->ac); /* Push buffer to DSP */ + rc = 0; + pr_debug("%s:session id %d: AUDIO_START success enable[%d]\n", + __func__, audio->ac->session, audio->enabled); + break; + } + case AUDIO_STOP: { + pr_debug("%s:AUDIO_STOP\n", __func__); + rc = audio_in_disable(audio); + if (rc < 0) { + pr_err("%s:session id %d: Audio Stop procedure failed rc=%d\n", + __func__, audio->ac->session, rc); + break; + } + break; + } + case AUDIO_SET_AMRWB_ENC_CONFIG: { + struct msm_audio_amrwb_enc_config *cfg; + struct msm_audio_amrwb_enc_config *enc_cfg; + + enc_cfg = audio->enc_cfg; + cfg = (struct msm_audio_amrwb_enc_config *)arg; + if (cfg == NULL) { + pr_err("%s: NULL config pointer for %s\n", + __func__, "AUDIO_SET_AMRWB_ENC_CONFIG"); + rc = -EINVAL; + break; + } + + if (cfg->band_mode > 8) { + pr_err("%s:session id %d: invalid band mode\n", + __func__, audio->ac->session); + rc = -EINVAL; + break; + } + /* ToDo: AMR WB encoder accepts values between 0-8 + * while openmax provides value between 9-17 + * as per spec + */ + enc_cfg->band_mode = cfg->band_mode; + enc_cfg->dtx_enable = (cfg->dtx_enable ? 1 : 0); + /* Currently DSP does not support different frameformat */ + enc_cfg->frame_format = 0; + pr_debug("%s:session id %d: band_mode = 0x%x dtx_enable=0x%x\n", + __func__, audio->ac->session, + enc_cfg->band_mode, enc_cfg->dtx_enable); + break; + } + default: + pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd); + rc = -EINVAL; + } + return rc; +} + +static long amrwb_in_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct q6audio_in *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_START: + case AUDIO_STOP: { + rc = amrwb_in_ioctl_shared(file, cmd, NULL); + break; + } + case AUDIO_GET_AMRWB_ENC_CONFIG: { + if (copy_to_user((void *)arg, audio->enc_cfg, + sizeof(struct msm_audio_amrwb_enc_config))) + pr_err("%s: copy_to_user for AUDIO_GET_AMRWB_ENC_CONFIG failed\n", + __func__); + rc = -EFAULT; + break; + } + case AUDIO_SET_AMRWB_ENC_CONFIG: { + struct msm_audio_amrwb_enc_config cfg; + + if (copy_from_user(&cfg, (void *) arg, + sizeof(cfg))) { + pr_err("%s: copy_from_user for AUDIO_SET_AMRWB_ENC_CONFIG failed\n", + __func__); + rc = -EFAULT; + break; + } + rc = amrwb_in_ioctl_shared(file, cmd, &cfg); + if (rc) + pr_err("%s:AUDIO_SET_AAC_ENC_CONFIG failed. rc=%d\n", + __func__, rc); + break; + } + default: + pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd); + rc = -EINVAL; + } + return rc; +} + +#ifdef CONFIG_COMPAT +struct msm_audio_amrwb_enc_config_32 { + u32 band_mode; + u32 dtx_enable; + u32 frame_format; +}; + +enum { + AUDIO_GET_AMRWB_ENC_CONFIG_32 = _IOW(AUDIO_IOCTL_MAGIC, + (AUDIO_MAX_COMMON_IOCTL_NUM+0), + struct msm_audio_amrwb_enc_config_32), + AUDIO_SET_AMRWB_ENC_CONFIG_32 = _IOR(AUDIO_IOCTL_MAGIC, + (AUDIO_MAX_COMMON_IOCTL_NUM+1), + struct msm_audio_amrwb_enc_config_32) +}; + +static long amrwb_in_compat_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct q6audio_in *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_START: + case AUDIO_STOP: { + rc = amrwb_in_ioctl_shared(file, cmd, NULL); + break; + } + case AUDIO_GET_AMRWB_ENC_CONFIG_32: { + struct msm_audio_amrwb_enc_config *amrwb_config; + struct msm_audio_amrwb_enc_config_32 amrwb_config_32; + + memset(&amrwb_config_32, 0, sizeof(amrwb_config_32)); + + amrwb_config = + (struct msm_audio_amrwb_enc_config *)audio->enc_cfg; + amrwb_config_32.band_mode = amrwb_config->band_mode; + amrwb_config_32.dtx_enable = amrwb_config->dtx_enable; + amrwb_config_32.frame_format = amrwb_config->frame_format; + + if (copy_to_user((void *)arg, &amrwb_config_32, + sizeof(struct msm_audio_amrwb_enc_config_32))) { + pr_err("%s: copy_to_user for AUDIO_GET_AMRWB_ENC_CONFIG_32 failed\n", + __func__); + rc = -EFAULT; + } + break; + } + case AUDIO_SET_AMRWB_ENC_CONFIG_32: { + struct msm_audio_amrwb_enc_config cfg_32; + + if (copy_from_user(&cfg_32, (void *) arg, + sizeof(cfg_32))) { + pr_err("%s: copy_from_user for AUDIO_SET_AMRWB_ENC_CONFIG_32 failed\n", + __func__); + rc = -EFAULT; + break; + } + cmd = AUDIO_SET_AMRWB_ENC_CONFIG; + rc = amrwb_in_ioctl_shared(file, cmd, &cfg_32); + if (rc) + pr_err("%s:AUDIO_SET_AAC_ENC_CONFIG failed. rc=%d\n", + __func__, rc); + break; + } + default: + pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd); + rc = -EINVAL; + } + return rc; +} +#else +#define amrwb_in_compat_ioctl NULL +#endif + +static int amrwb_in_open(struct inode *inode, struct file *file) +{ + struct q6audio_in *audio = NULL; + struct msm_audio_amrwb_enc_config *enc_cfg; + int rc = 0; + + audio = kzalloc(sizeof(struct q6audio_in), GFP_KERNEL); + + if (audio == NULL) + return -ENOMEM; + + /* Allocate memory for encoder config param */ + audio->enc_cfg = kzalloc(sizeof(struct msm_audio_amrwb_enc_config), + GFP_KERNEL); + if (audio->enc_cfg == NULL) { + kfree(audio); + return -ENOMEM; + } + enc_cfg = audio->enc_cfg; + + mutex_init(&audio->lock); + mutex_init(&audio->read_lock); + mutex_init(&audio->write_lock); + spin_lock_init(&audio->dsp_lock); + init_waitqueue_head(&audio->read_wait); + init_waitqueue_head(&audio->write_wait); + + /* Settings will be re-config at AUDIO_SET_CONFIG, + * but at least we need to have initial config + */ + audio->str_cfg.buffer_size = FRAME_SIZE; + audio->str_cfg.buffer_count = FRAME_NUM; + audio->min_frame_size = 32; + audio->max_frames_per_buf = 10; + audio->pcm_cfg.buffer_size = PCM_BUF_SIZE; + audio->pcm_cfg.buffer_count = PCM_BUF_COUNT; + enc_cfg->band_mode = 8; + enc_cfg->dtx_enable = 0; + audio->pcm_cfg.channel_count = 1; + audio->pcm_cfg.sample_rate = 16000; + audio->buf_cfg.meta_info_enable = 0x01; + audio->buf_cfg.frames_per_buf = 0x01; + + audio->ac = q6asm_audio_client_alloc((app_cb)q6asm_in_cb, + (void *)audio); + + if (!audio->ac) { + pr_err("%s:audio[%pK]: Could not allocate memory for audio client\n", + __func__, audio); + kfree(audio->enc_cfg); + kfree(audio); + return -ENOMEM; + } + + /* open amrwb encoder in T/NT mode */ + if ((file->f_mode & FMODE_WRITE) && + (file->f_mode & FMODE_READ)) { + audio->feedback = NON_TUNNEL_MODE; + rc = q6asm_open_read_write(audio->ac, FORMAT_AMRWB, + FORMAT_LINEAR_PCM); + if (rc < 0) { + pr_err("%s:session id %d: NT mode Open failed rc=%d\n", + __func__, audio->ac->session, rc); + rc = -ENODEV; + goto fail; + } + pr_info("%s:session id %d: NT mode encoder success\n", + __func__, audio->ac->session); + } else if (!(file->f_mode & FMODE_WRITE) && + (file->f_mode & FMODE_READ)) { + audio->feedback = TUNNEL_MODE; + rc = q6asm_open_read(audio->ac, FORMAT_AMRWB); + if (rc < 0) { + pr_err("%s:session id %d: T mode Open failed rc=%d\n", + __func__, audio->ac->session, rc); + rc = -ENODEV; + goto fail; + } + /* register for tx overflow (valid for tunnel mode only) */ + rc = q6asm_reg_tx_overflow(audio->ac, 0x01); + if (rc < 0) { + pr_err("%s:session id %d: TX Overflow registration failed rc=%d\n", + __func__, audio->ac->session, + rc); + rc = -ENODEV; + goto fail; + } + pr_info("%s:session id %d: T mode encoder success\n", + __func__, audio->ac->session); + } else { + pr_err("%s:session id %d: Unexpected mode\n", __func__, + audio->ac->session); + rc = -EACCES; + goto fail; + } + + audio->opened = 1; + atomic_set(&audio->in_count, PCM_BUF_COUNT); + atomic_set(&audio->out_count, 0x00); + audio->enc_compat_ioctl = amrwb_in_compat_ioctl; + audio->enc_ioctl = amrwb_in_ioctl; + file->private_data = audio; + + pr_info("%s:session id %d: success\n", __func__, audio->ac->session); + return 0; +fail: + q6asm_audio_client_free(audio->ac); + kfree(audio->enc_cfg); + kfree(audio); + return rc; +} + +static const struct file_operations audio_in_fops = { + .owner = THIS_MODULE, + .open = amrwb_in_open, + .release = audio_in_release, + .read = audio_in_read, + .write = audio_in_write, + .unlocked_ioctl = audio_in_ioctl, + .compat_ioctl = audio_in_compat_ioctl +}; + +struct miscdevice audio_amrwb_in_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_amrwb_in", + .fops = &audio_in_fops, +}; + +int __init amrwb_in_init(void) +{ + return misc_register(&audio_amrwb_in_misc); +} + +void amrwb_in_exit(void) +{ + misc_deregister(&audio_amrwb_in_misc); +} diff --git a/audio-kernel/dsp/codecs/audio_aac.c b/audio-kernel/dsp/codecs/audio_aac.c new file mode 100644 index 000000000..44444efef --- /dev/null +++ b/audio-kernel/dsp/codecs/audio_aac.c @@ -0,0 +1,480 @@ +/* aac audio output device + * + * Copyright (C) 2008 Google, Inc. + * Copyright (C) 2008 HTC Corporation + * Copyright (c) 2010-2018, The Linux Foundation. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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 +#include +#include "audio_utils_aio.h" + +#define AUDIO_AAC_DUAL_MONO_INVALID -1 +#define PCM_BUFSZ_MIN_AAC ((8*1024) + sizeof(struct dec_meta_out)) + +static struct miscdevice audio_aac_misc; +static struct ws_mgr audio_aac_ws_mgr; + +#ifdef CONFIG_DEBUG_FS +static const struct file_operations audio_aac_debug_fops = { + .read = audio_aio_debug_read, + .open = audio_aio_debug_open, +}; +#endif + +static long audio_ioctl_shared(struct file *file, unsigned int cmd, + void *arg) +{ + struct q6audio_aio *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_START: { + struct asm_aac_cfg aac_cfg; + struct msm_audio_aac_config *aac_config; + uint32_t sbr_ps = 0x00; + + pr_debug("%s: AUDIO_START session_id[%d]\n", __func__, + audio->ac->session); + if (audio->feedback == NON_TUNNEL_MODE) { + /* Configure PCM output block */ + rc = q6asm_enc_cfg_blk_pcm(audio->ac, + audio->pcm_cfg.sample_rate, + audio->pcm_cfg.channel_count); + if (rc < 0) { + pr_err("pcm output block config failed\n"); + break; + } + } + /* turn on both sbr and ps */ + rc = q6asm_enable_sbrps(audio->ac, sbr_ps); + if (rc < 0) + pr_err("sbr-ps enable failed\n"); + aac_config = (struct msm_audio_aac_config *)audio->codec_cfg; + if (aac_config->sbr_ps_on_flag) + aac_cfg.aot = AAC_ENC_MODE_EAAC_P; + else if (aac_config->sbr_on_flag) + aac_cfg.aot = AAC_ENC_MODE_AAC_P; + else + aac_cfg.aot = AAC_ENC_MODE_AAC_LC; + + switch (aac_config->format) { + case AUDIO_AAC_FORMAT_ADTS: + aac_cfg.format = 0x00; + break; + case AUDIO_AAC_FORMAT_LOAS: + aac_cfg.format = 0x01; + break; + case AUDIO_AAC_FORMAT_ADIF: + aac_cfg.format = 0x02; + break; + default: + case AUDIO_AAC_FORMAT_RAW: + aac_cfg.format = 0x03; + } + aac_cfg.ep_config = aac_config->ep_config; + aac_cfg.section_data_resilience = + aac_config->aac_section_data_resilience_flag; + aac_cfg.scalefactor_data_resilience = + aac_config->aac_scalefactor_data_resilience_flag; + aac_cfg.spectral_data_resilience = + aac_config->aac_spectral_data_resilience_flag; + aac_cfg.ch_cfg = audio->pcm_cfg.channel_count; + if (audio->feedback == TUNNEL_MODE) { + aac_cfg.sample_rate = aac_config->sample_rate; + aac_cfg.ch_cfg = aac_config->channel_configuration; + } else { + aac_cfg.sample_rate = audio->pcm_cfg.sample_rate; + aac_cfg.ch_cfg = audio->pcm_cfg.channel_count; + } + + pr_debug("%s:format=%x aot=%d ch=%d sr=%d\n", + __func__, aac_cfg.format, + aac_cfg.aot, aac_cfg.ch_cfg, + aac_cfg.sample_rate); + + /* Configure Media format block */ + rc = q6asm_media_format_block_aac(audio->ac, &aac_cfg); + if (rc < 0) { + pr_err("cmd media format block failed\n"); + break; + } + rc = audio_aio_enable(audio); + audio->eos_rsp = 0; + audio->eos_flag = 0; + if (!rc) { + rc = enable_volume_ramp(audio); + if (rc < 0) { + pr_err("%s: Failed to enable volume ramp\n", + __func__); + } + audio->enabled = 1; + } else { + audio->enabled = 0; + pr_err("Audio Start procedure failed rc=%d\n", rc); + break; + } + pr_info("%s: AUDIO_START sessionid[%d]enable[%d]\n", __func__, + audio->ac->session, + audio->enabled); + if (audio->stopped == 1) + audio->stopped = 0; + break; + } + case AUDIO_SET_AAC_CONFIG: { + struct msm_audio_aac_config *aac_config; + uint16_t sce_left = 1, sce_right = 2; + + pr_debug("%s: AUDIO_SET_AAC_CONFIG\n", __func__); + aac_config = (struct msm_audio_aac_config *)arg; + if (aac_config == NULL) { + pr_err("%s: Invalid config pointer\n", __func__); + rc = -EINVAL; + break; + } + memcpy(audio->codec_cfg, aac_config, + sizeof(struct msm_audio_aac_config)); + /* PL_PR is 0 only need to check PL_SR */ + if (aac_config->dual_mono_mode > + AUDIO_AAC_DUAL_MONO_PL_SR) { + pr_err("%s:Invalid dual_mono mode =%d\n", __func__, + aac_config->dual_mono_mode); + } else { + /* convert the data from user into sce_left + * and sce_right based on the definitions + */ + pr_debug("%s: modify dual_mono mode =%d\n", __func__, + aac_config->dual_mono_mode); + switch (aac_config->dual_mono_mode) { + case AUDIO_AAC_DUAL_MONO_PL_PR: + sce_left = 1; + sce_right = 1; + break; + case AUDIO_AAC_DUAL_MONO_SL_SR: + sce_left = 2; + sce_right = 2; + break; + case AUDIO_AAC_DUAL_MONO_SL_PR: + sce_left = 2; + sce_right = 1; + break; + case AUDIO_AAC_DUAL_MONO_PL_SR: + default: + sce_left = 1; + sce_right = 2; + break; + } + rc = q6asm_cfg_dual_mono_aac(audio->ac, + sce_left, sce_right); + if (rc < 0) + pr_err("%s:asm cmd dualmono failed rc=%d\n", + __func__, rc); + } + break; + } + default: + pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd); + break; + } + return rc; +} + +static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct q6audio_aio *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_START: { + rc = audio_ioctl_shared(file, cmd, (void *)arg); + break; + } + case AUDIO_GET_AAC_CONFIG: { + if (copy_to_user((void *)arg, audio->codec_cfg, + sizeof(struct msm_audio_aac_config))) { + pr_err("%s: copy_to_user for AUDIO_GET_AAC_CONFIG failed\n", + __func__); + rc = -EFAULT; + break; + } + break; + } + case AUDIO_SET_AAC_CONFIG: { + struct msm_audio_aac_config aac_config; + + pr_debug("%s: AUDIO_SET_AAC_CONFIG\n", __func__); + if (copy_from_user(&aac_config, (void *)arg, + sizeof(aac_config))) { + pr_err("%s: copy_from_user for AUDIO_SET_AAC_CONFIG failed\n", + __func__); + rc = -EFAULT; + break; + } + rc = audio_ioctl_shared(file, cmd, &aac_config); + if (rc) + pr_err("%s:AUDIO_SET_AAC_CONFIG failed. Rc= %d\n", + __func__, rc); + break; + } + default: { + pr_debug("%s[%pK]: Calling utils ioctl\n", __func__, audio); + rc = audio->codec_ioctl(file, cmd, arg); + if (rc) + pr_err_ratelimited("%s[%pK]:Failed in utils_ioctl: %d\n", + __func__, audio, rc); + } + } + return rc; +} + +#ifdef CONFIG_COMPAT +struct msm_audio_aac_config32 { + s16 format; + u16 audio_object; + u16 ep_config; /* 0 ~ 3 useful only obj = ERLC */ + u16 aac_section_data_resilience_flag; + u16 aac_scalefactor_data_resilience_flag; + u16 aac_spectral_data_resilience_flag; + u16 sbr_on_flag; + u16 sbr_ps_on_flag; + u16 dual_mono_mode; + u16 channel_configuration; + u16 sample_rate; +}; + +enum { + AUDIO_SET_AAC_CONFIG_32 = _IOW(AUDIO_IOCTL_MAGIC, + (AUDIO_MAX_COMMON_IOCTL_NUM+0), struct msm_audio_aac_config32), + AUDIO_GET_AAC_CONFIG_32 = _IOR(AUDIO_IOCTL_MAGIC, + (AUDIO_MAX_COMMON_IOCTL_NUM+1), struct msm_audio_aac_config32) +}; + +static long audio_compat_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct q6audio_aio *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_START: { + rc = audio_ioctl_shared(file, cmd, (void *)arg); + break; + } + case AUDIO_GET_AAC_CONFIG_32: { + struct msm_audio_aac_config *aac_config; + struct msm_audio_aac_config32 aac_config_32; + + aac_config = (struct msm_audio_aac_config *)audio->codec_cfg; + aac_config_32.format = aac_config->format; + aac_config_32.audio_object = aac_config->audio_object; + aac_config_32.ep_config = aac_config->ep_config; + aac_config_32.aac_section_data_resilience_flag = + aac_config->aac_section_data_resilience_flag; + aac_config_32.aac_scalefactor_data_resilience_flag = + aac_config->aac_scalefactor_data_resilience_flag; + aac_config_32.aac_spectral_data_resilience_flag = + aac_config->aac_spectral_data_resilience_flag; + aac_config_32.sbr_on_flag = aac_config->sbr_on_flag; + aac_config_32.sbr_ps_on_flag = aac_config->sbr_ps_on_flag; + aac_config_32.dual_mono_mode = aac_config->dual_mono_mode; + aac_config_32.channel_configuration = + aac_config->channel_configuration; + aac_config_32.sample_rate = aac_config->sample_rate; + + if (copy_to_user((void *)arg, &aac_config_32, + sizeof(aac_config_32))) { + pr_err("%s: copy_to_user for AUDIO_GET_AAC_CONFIG_32 failed\n", + __func__); + rc = -EFAULT; + break; + } + break; + } + case AUDIO_SET_AAC_CONFIG_32: { + struct msm_audio_aac_config aac_config; + struct msm_audio_aac_config32 aac_config_32; + + pr_debug("%s: AUDIO_SET_AAC_CONFIG\n", __func__); + if (copy_from_user(&aac_config_32, (void *)arg, + sizeof(aac_config_32))) { + pr_err("%s: copy_from_user for AUDIO_SET_AAC_CONFIG_32 failed\n", + __func__); + rc = -EFAULT; + break; + } + aac_config.format = aac_config_32.format; + aac_config.audio_object = aac_config_32.audio_object; + aac_config.ep_config = aac_config_32.ep_config; + aac_config.aac_section_data_resilience_flag = + aac_config_32.aac_section_data_resilience_flag; + aac_config.aac_scalefactor_data_resilience_flag = + aac_config_32.aac_scalefactor_data_resilience_flag; + aac_config.aac_spectral_data_resilience_flag = + aac_config_32.aac_spectral_data_resilience_flag; + aac_config.sbr_on_flag = aac_config_32.sbr_on_flag; + aac_config.sbr_ps_on_flag = aac_config_32.sbr_ps_on_flag; + aac_config.dual_mono_mode = aac_config_32.dual_mono_mode; + aac_config.channel_configuration = + aac_config_32.channel_configuration; + aac_config.sample_rate = aac_config_32.sample_rate; + + cmd = AUDIO_SET_AAC_CONFIG; + rc = audio_ioctl_shared(file, cmd, &aac_config); + if (rc) + pr_err("%s:AUDIO_SET_AAC_CONFIG failed. Rc= %d\n", + __func__, rc); + break; + } + default: { + pr_debug("%s[%pK]: Calling utils ioctl\n", __func__, audio); + rc = audio->codec_compat_ioctl(file, cmd, arg); + if (rc) + pr_err_ratelimited("%s[%pK]:Failed in utils_ioctl: %d\n", + __func__, audio, rc); + } + } + return rc; +} +#else +#define audio_compat_ioctl NULL +#endif + +static int audio_open(struct inode *inode, struct file *file) +{ + struct q6audio_aio *audio = NULL; + int rc = 0; + struct msm_audio_aac_config *aac_config = NULL; + +#ifdef CONFIG_DEBUG_FS + /* 4 bytes represents decoder number, 1 byte for terminate string */ + char name[sizeof "msm_aac_" + 5]; +#endif + audio = kzalloc(sizeof(struct q6audio_aio), GFP_KERNEL); + + if (audio == NULL) + return -ENOMEM; + + audio->codec_cfg = kzalloc(sizeof(struct msm_audio_aac_config), + GFP_KERNEL); + if (audio->codec_cfg == NULL) { + kfree(audio); + return -ENOMEM; + } + aac_config = audio->codec_cfg; + + /* Settings will be re-config at AUDIO_SET_CONFIG, + * but at least we need to have initial config + */ + audio->pcm_cfg.buffer_size = PCM_BUFSZ_MIN_AAC; + audio->miscdevice = &audio_aac_misc; + audio->wakelock_voted = false; + audio->audio_ws_mgr = &audio_aac_ws_mgr; + aac_config->dual_mono_mode = AUDIO_AAC_DUAL_MONO_INVALID; + + audio->ac = q6asm_audio_client_alloc((app_cb) q6_audio_cb, + (void *)audio); + + if (!audio->ac) { + pr_err("Could not allocate memory for audio client\n"); + kfree(audio->codec_cfg); + kfree(audio); + return -ENOMEM; + } + rc = audio_aio_open(audio, file); + if (rc < 0) { + pr_err("%s: audio_aio_open rc=%d\n", + __func__, rc); + goto fail; + } + /* open in T/NT mode */ + if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) { + rc = q6asm_open_read_write(audio->ac, FORMAT_LINEAR_PCM, + FORMAT_MPEG4_AAC); + if (rc < 0) { + pr_err("NT mode Open failed rc=%d\n", rc); + rc = -ENODEV; + goto fail; + } + audio->feedback = NON_TUNNEL_MODE; + /* open AAC decoder, expected frames is always 1 + * audio->buf_cfg.frames_per_buf = 0x01; + */ + audio->buf_cfg.meta_info_enable = 0x01; + } else if ((file->f_mode & FMODE_WRITE) && + !(file->f_mode & FMODE_READ)) { + rc = q6asm_open_write(audio->ac, FORMAT_MPEG4_AAC); + if (rc < 0) { + pr_err("T mode Open failed rc=%d\n", rc); + rc = -ENODEV; + goto fail; + } + audio->feedback = TUNNEL_MODE; + audio->buf_cfg.meta_info_enable = 0x00; + } else { + pr_err("Not supported mode\n"); + rc = -EACCES; + goto fail; + } + +#ifdef CONFIG_DEBUG_FS + snprintf(name, sizeof(name), "msm_aac_%04x", audio->ac->session); + audio->dentry = debugfs_create_file(name, S_IFREG | 0444, + NULL, (void *)audio, + &audio_aac_debug_fops); + + if (IS_ERR(audio->dentry)) + pr_debug("debugfs_create_file failed\n"); +#endif + pr_info("%s:aacdec success mode[%d]session[%d]\n", __func__, + audio->feedback, + audio->ac->session); + return rc; +fail: + q6asm_audio_client_free(audio->ac); + kfree(audio->codec_cfg); + kfree(audio); + return rc; +} + +static const struct file_operations audio_aac_fops = { + .owner = THIS_MODULE, + .open = audio_open, + .release = audio_aio_release, + .unlocked_ioctl = audio_ioctl, + .fsync = audio_aio_fsync, + .compat_ioctl = audio_compat_ioctl +}; + +static struct miscdevice audio_aac_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_aac", + .fops = &audio_aac_fops, +}; + +int __init audio_aac_init(void) +{ + int ret = misc_register(&audio_aac_misc); + + if (ret == 0) + device_init_wakeup(audio_aac_misc.this_device, true); + audio_aac_ws_mgr.ref_cnt = 0; + mutex_init(&audio_aac_ws_mgr.ws_lock); + + return ret; +} + +void audio_aac_exit(void) +{ + mutex_destroy(&audio_aac_ws_mgr.ws_lock); + misc_deregister(&audio_aac_misc); +} diff --git a/audio-kernel/dsp/codecs/audio_alac.c b/audio-kernel/dsp/codecs/audio_alac.c new file mode 100644 index 000000000..975a1e903 --- /dev/null +++ b/audio-kernel/dsp/codecs/audio_alac.c @@ -0,0 +1,439 @@ +/* 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 + * 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 +#include +#include +#include "audio_utils_aio.h" + +static struct miscdevice audio_alac_misc; +static struct ws_mgr audio_alac_ws_mgr; + +static const struct file_operations audio_alac_debug_fops = { + .read = audio_aio_debug_read, + .open = audio_aio_debug_open, +}; + +static struct dentry *config_debugfs_create_file(const char *name, void *data) +{ + return debugfs_create_file(name, S_IFREG | 0444, + NULL, (void *)data, &audio_alac_debug_fops); +} + +static int alac_channel_map(u8 *channel_mapping, uint32_t channels); + +static long audio_ioctl_shared(struct file *file, unsigned int cmd, + void *arg) +{ + struct q6audio_aio *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_START: { + struct asm_alac_cfg alac_cfg; + struct msm_audio_alac_config *alac_config; + u8 channel_mapping[PCM_FORMAT_MAX_NUM_CHANNEL]; + + memset(channel_mapping, 0, PCM_FORMAT_MAX_NUM_CHANNEL); + + if (alac_channel_map(channel_mapping, + audio->pcm_cfg.channel_count)) { + pr_err("%s: setting channel map failed %d\n", + __func__, audio->pcm_cfg.channel_count); + } + + pr_debug("%s[%pK]: AUDIO_START session_id[%d]\n", __func__, + audio, audio->ac->session); + if (audio->feedback == NON_TUNNEL_MODE) { + /* Configure PCM output block */ + rc = q6asm_enc_cfg_blk_pcm_v2(audio->ac, + audio->pcm_cfg.sample_rate, + audio->pcm_cfg.channel_count, + 16, /*bits per sample*/ + false, false, channel_mapping); + if (rc < 0) { + pr_err("pcm output block config failed\n"); + break; + } + } + alac_config = (struct msm_audio_alac_config *)audio->codec_cfg; + alac_cfg.frame_length = alac_config->frameLength; + alac_cfg.compatible_version = alac_config->compatVersion; + alac_cfg.bit_depth = alac_config->bitDepth; + alac_cfg.pb = alac_config->pb; + alac_cfg.mb = alac_config->mb; + alac_cfg.kb = alac_config->kb; + alac_cfg.num_channels = alac_config->channelCount; + alac_cfg.max_run = alac_config->maxRun; + alac_cfg.max_frame_bytes = alac_config->maxSize; + alac_cfg.avg_bit_rate = alac_config->averageBitRate; + alac_cfg.sample_rate = alac_config->sampleRate; + alac_cfg.channel_layout_tag = alac_config->channelLayout; + pr_debug("%s: frame_length %d compatible_version %d bit_depth %d pb %d mb %d kb %d num_channels %d max_run %d max_frame_bytes %d avg_bit_rate %d sample_rate %d channel_layout_tag %d\n", + __func__, alac_config->frameLength, + alac_config->compatVersion, + alac_config->bitDepth, alac_config->pb, + alac_config->mb, alac_config->kb, + alac_config->channelCount, alac_config->maxRun, + alac_config->maxSize, + alac_config->averageBitRate, + alac_config->sampleRate, + alac_config->channelLayout); + /* Configure Media format block */ + rc = q6asm_media_format_block_alac(audio->ac, &alac_cfg, + audio->ac->stream_id); + if (rc < 0) { + pr_err("cmd media format block failed\n"); + break; + } + rc = audio_aio_enable(audio); + audio->eos_rsp = 0; + audio->eos_flag = 0; + if (!rc) { + audio->enabled = 1; + } else { + audio->enabled = 0; + pr_err("Audio Start procedure failed rc=%d\n", rc); + break; + } + pr_debug("AUDIO_START success enable[%d]\n", audio->enabled); + if (audio->stopped == 1) + audio->stopped = 0; + break; + } + default: + pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd); + break; + } + return rc; +} + +static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct q6audio_aio *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_START: { + rc = audio_ioctl_shared(file, cmd, (void *)arg); + break; + } + case AUDIO_GET_ALAC_CONFIG: { + if (copy_to_user((void *)arg, audio->codec_cfg, + sizeof(struct msm_audio_alac_config))) { + pr_err("%s:copy_to_user for AUDIO_GET_ALAC_CONFIG failed\n", + __func__); + rc = -EFAULT; + break; + } + break; + } + case AUDIO_SET_ALAC_CONFIG: { + if (copy_from_user(audio->codec_cfg, (void *)arg, + sizeof(struct msm_audio_alac_config))) { + pr_err("%s:copy_from_user for AUDIO_SET_ALAC_CONFIG failed\n", + __func__); + rc = -EFAULT; + break; + } + break; + } + default: { + rc = audio->codec_ioctl(file, cmd, arg); + if (rc) + pr_err_ratelimited("Failed in utils_ioctl: %d\n", rc); + break; + } + } + return rc; +} + +#ifdef CONFIG_COMPAT +struct msm_audio_alac_config_32 { + u32 frameLength; + u8 compatVersion; + u8 bitDepth; + u8 pb; + u8 mb; + u8 kb; + u8 channelCount; + u16 maxRun; + u32 maxSize; + u32 averageBitRate; + u32 sampleRate; + u32 channelLayout; +}; + +enum { + AUDIO_GET_ALAC_CONFIG_32 = _IOR(AUDIO_IOCTL_MAGIC, + (AUDIO_MAX_COMMON_IOCTL_NUM+0), struct msm_audio_alac_config_32), + AUDIO_SET_ALAC_CONFIG_32 = _IOW(AUDIO_IOCTL_MAGIC, + (AUDIO_MAX_COMMON_IOCTL_NUM+1), struct msm_audio_alac_config_32) +}; + +static long audio_compat_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct q6audio_aio *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_START: { + rc = audio_ioctl_shared(file, cmd, (void *)arg); + break; + } + case AUDIO_GET_ALAC_CONFIG_32: { + struct msm_audio_alac_config *alac_config; + struct msm_audio_alac_config_32 alac_config_32; + + memset(&alac_config_32, 0, sizeof(alac_config_32)); + + alac_config = (struct msm_audio_alac_config *)audio->codec_cfg; + alac_config_32.frameLength = alac_config->frameLength; + alac_config_32.compatVersion = + alac_config->compatVersion; + alac_config_32.bitDepth = alac_config->bitDepth; + alac_config_32.pb = alac_config->pb; + alac_config_32.mb = alac_config->mb; + alac_config_32.kb = alac_config->kb; + alac_config_32.channelCount = alac_config->channelCount; + alac_config_32.maxRun = alac_config->maxRun; + alac_config_32.maxSize = alac_config->maxSize; + alac_config_32.averageBitRate = alac_config->averageBitRate; + alac_config_32.sampleRate = alac_config->sampleRate; + alac_config_32.channelLayout = alac_config->channelLayout; + + if (copy_to_user((void *)arg, &alac_config_32, + sizeof(alac_config_32))) { + pr_err("%s: copy_to_user for GET_ALAC_CONFIG_32 failed\n", + __func__); + rc = -EFAULT; + break; + } + break; + } + case AUDIO_SET_ALAC_CONFIG_32: { + struct msm_audio_alac_config *alac_config; + struct msm_audio_alac_config_32 alac_config_32; + + if (copy_from_user(&alac_config_32, (void *)arg, + sizeof(alac_config_32))) { + pr_err("%s: copy_from_user for SET_ALAC_CONFIG_32 failed\n" + , __func__); + rc = -EFAULT; + break; + } + alac_config = (struct msm_audio_alac_config *)audio->codec_cfg; + alac_config->frameLength = alac_config_32.frameLength; + alac_config->compatVersion = + alac_config_32.compatVersion; + alac_config->bitDepth = alac_config_32.bitDepth; + alac_config->pb = alac_config_32.pb; + alac_config->mb = alac_config_32.mb; + alac_config->kb = alac_config_32.kb; + alac_config->channelCount = alac_config_32.channelCount; + alac_config->maxRun = alac_config_32.maxRun; + alac_config->maxSize = alac_config_32.maxSize; + alac_config->averageBitRate = alac_config_32.averageBitRate; + alac_config->sampleRate = alac_config_32.sampleRate; + alac_config->channelLayout = alac_config_32.channelLayout; + + break; + } + default: { + rc = audio->codec_compat_ioctl(file, cmd, arg); + if (rc) + pr_err_ratelimited("Failed in utils_ioctl: %d\n", rc); + break; + } + } + return rc; +} +#else +#define audio_compat_ioctl NULL +#endif + +static int audio_open(struct inode *inode, struct file *file) +{ + struct q6audio_aio *audio = NULL; + int rc = 0; + + /* 4 bytes represents decoder number, 1 byte for terminate string */ + char name[sizeof "msm_alac_" + 5]; + + audio = kzalloc(sizeof(struct q6audio_aio), GFP_KERNEL); + if (!audio) + return -ENOMEM; + + audio->codec_cfg = kzalloc(sizeof(struct msm_audio_alac_config), + GFP_KERNEL); + if (!audio->codec_cfg) { + kfree(audio); + return -ENOMEM; + } + + audio->pcm_cfg.buffer_size = PCM_BUFSZ_MIN; + audio->miscdevice = &audio_alac_misc; + audio->wakelock_voted = false; + audio->audio_ws_mgr = &audio_alac_ws_mgr; + + audio->ac = q6asm_audio_client_alloc((app_cb) q6_audio_cb, + (void *)audio); + + if (!audio->ac) { + pr_err("Could not allocate memory for audio client\n"); + kfree(audio->codec_cfg); + kfree(audio); + return -ENOMEM; + } + rc = audio_aio_open(audio, file); + if (rc < 0) { + pr_err("%s: audio_aio_open rc=%d\n", + __func__, rc); + goto fail; + } + /* open in T/NT mode */ + if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) { + rc = q6asm_open_read_write(audio->ac, FORMAT_LINEAR_PCM, + FORMAT_ALAC); + if (rc < 0) { + pr_err("NT mode Open failed rc=%d\n", rc); + rc = -ENODEV; + goto fail; + } + audio->feedback = NON_TUNNEL_MODE; + /* open ALAC decoder, expected frames is always 1*/ + audio->buf_cfg.frames_per_buf = 0x01; + audio->buf_cfg.meta_info_enable = 0x01; + } else if ((file->f_mode & FMODE_WRITE) && + !(file->f_mode & FMODE_READ)) { + rc = q6asm_open_write(audio->ac, FORMAT_ALAC); + if (rc < 0) { + pr_err("T mode Open failed rc=%d\n", rc); + rc = -ENODEV; + goto fail; + } + audio->feedback = TUNNEL_MODE; + audio->buf_cfg.meta_info_enable = 0x00; + } else { + pr_err("Not supported mode\n"); + rc = -EACCES; + goto fail; + } + + snprintf(name, sizeof(name), "msm_alac_%04x", audio->ac->session); + audio->dentry = config_debugfs_create_file(name, (void *)audio); + + if (IS_ERR_OR_NULL(audio->dentry)) + pr_debug("debugfs_create_file failed\n"); + pr_debug("%s:alacdec success mode[%d]session[%d]\n", __func__, + audio->feedback, + audio->ac->session); + return rc; +fail: + q6asm_audio_client_free(audio->ac); + kfree(audio->codec_cfg); + kfree(audio); + return rc; +} + +static int alac_channel_map(u8 *channel_mapping, uint32_t channels) +{ + u8 *lchannel_mapping; + + lchannel_mapping = channel_mapping; + pr_debug("%s: channels passed: %d\n", __func__, channels); + if (channels == 1) { + lchannel_mapping[0] = PCM_CHANNEL_FC; + } else if (channels == 2) { + lchannel_mapping[0] = PCM_CHANNEL_FL; + lchannel_mapping[1] = PCM_CHANNEL_FR; + } else if (channels == 3) { + lchannel_mapping[0] = PCM_CHANNEL_FC; + lchannel_mapping[1] = PCM_CHANNEL_FL; + lchannel_mapping[2] = PCM_CHANNEL_FR; + } else if (channels == 4) { + lchannel_mapping[0] = PCM_CHANNEL_FC; + lchannel_mapping[1] = PCM_CHANNEL_FL; + lchannel_mapping[2] = PCM_CHANNEL_FR; + lchannel_mapping[3] = PCM_CHANNEL_CS; + } else if (channels == 5) { + lchannel_mapping[0] = PCM_CHANNEL_FC; + lchannel_mapping[1] = PCM_CHANNEL_FL; + lchannel_mapping[2] = PCM_CHANNEL_FR; + lchannel_mapping[3] = PCM_CHANNEL_LS; + lchannel_mapping[4] = PCM_CHANNEL_RS; + } else if (channels == 6) { + lchannel_mapping[0] = PCM_CHANNEL_FC; + lchannel_mapping[1] = PCM_CHANNEL_FL; + lchannel_mapping[2] = PCM_CHANNEL_FR; + lchannel_mapping[3] = PCM_CHANNEL_LS; + lchannel_mapping[4] = PCM_CHANNEL_RS; + lchannel_mapping[5] = PCM_CHANNEL_LFE; + } else if (channels == 7) { + lchannel_mapping[0] = PCM_CHANNEL_FC; + lchannel_mapping[1] = PCM_CHANNEL_FL; + lchannel_mapping[2] = PCM_CHANNEL_FR; + lchannel_mapping[3] = PCM_CHANNEL_LS; + lchannel_mapping[4] = PCM_CHANNEL_RS; + lchannel_mapping[5] = PCM_CHANNEL_CS; + lchannel_mapping[6] = PCM_CHANNEL_LFE; + } else if (channels == 8) { + lchannel_mapping[0] = PCM_CHANNEL_FC; + lchannel_mapping[1] = PCM_CHANNEL_FLC; + lchannel_mapping[2] = PCM_CHANNEL_FRC; + lchannel_mapping[3] = PCM_CHANNEL_FL; + lchannel_mapping[4] = PCM_CHANNEL_FR; + lchannel_mapping[5] = PCM_CHANNEL_LS; + lchannel_mapping[6] = PCM_CHANNEL_RS; + lchannel_mapping[7] = PCM_CHANNEL_LFE; + } else { + pr_err("%s: ERROR.unsupported num_ch = %u\n", + __func__, channels); + return -EINVAL; + } + return 0; +} + +static const struct file_operations audio_alac_fops = { + .owner = THIS_MODULE, + .open = audio_open, + .release = audio_aio_release, + .unlocked_ioctl = audio_ioctl, + .fsync = audio_aio_fsync, + .compat_ioctl = audio_compat_ioctl +}; + +static struct miscdevice audio_alac_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_alac", + .fops = &audio_alac_fops, +}; + +int __init audio_alac_init(void) +{ + int ret = misc_register(&audio_alac_misc); + + if (ret == 0) + device_init_wakeup(audio_alac_misc.this_device, true); + audio_alac_ws_mgr.ref_cnt = 0; + mutex_init(&audio_alac_ws_mgr.ws_lock); + + return ret; +} + +void audio_alac_exit(void) +{ + mutex_destroy(&audio_alac_ws_mgr.ws_lock); + misc_deregister(&audio_alac_misc); +} diff --git a/audio-kernel/dsp/codecs/audio_amrnb.c b/audio-kernel/dsp/codecs/audio_amrnb.c new file mode 100644 index 000000000..1b5dd949f --- /dev/null +++ b/audio-kernel/dsp/codecs/audio_amrnb.c @@ -0,0 +1,230 @@ +/* amrnb audio output device + * + * Copyright (C) 2008 Google, Inc. + * Copyright (C) 2008 HTC Corporation + * Copyright (c) 2011-2017, The Linux Foundation. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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 +#include +#include "audio_utils_aio.h" + +static struct miscdevice audio_amrnb_misc; +static struct ws_mgr audio_amrnb_ws_mgr; + +#ifdef CONFIG_DEBUG_FS +static const struct file_operations audio_amrnb_debug_fops = { + .read = audio_aio_debug_read, + .open = audio_aio_debug_open, +}; +#endif + +static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct q6audio_aio *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_START: { + pr_debug("%s[%pK]: AUDIO_START session_id[%d]\n", __func__, + audio, audio->ac->session); + if (audio->feedback == NON_TUNNEL_MODE) { + /* Configure PCM output block */ + rc = q6asm_enc_cfg_blk_pcm(audio->ac, + audio->pcm_cfg.sample_rate, + audio->pcm_cfg.channel_count); + if (rc < 0) { + pr_err("pcm output block config failed\n"); + break; + } + } + + rc = audio_aio_enable(audio); + audio->eos_rsp = 0; + audio->eos_flag = 0; + if (!rc) { + audio->enabled = 1; + } else { + audio->enabled = 0; + pr_err("Audio Start procedure failed rc=%d\n", rc); + break; + } + pr_debug("AUDIO_START success enable[%d]\n", audio->enabled); + if (audio->stopped == 1) + audio->stopped = 0; + break; + } + default: + pr_debug("%s[%pK]: Calling utils ioctl\n", __func__, audio); + rc = audio->codec_ioctl(file, cmd, arg); + } + return rc; +} + +static long audio_compat_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct q6audio_aio *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_START: { + pr_debug("%s[%pK]: AUDIO_START session_id[%d]\n", __func__, + audio, audio->ac->session); + if (audio->feedback == NON_TUNNEL_MODE) { + /* Configure PCM output block */ + rc = q6asm_enc_cfg_blk_pcm(audio->ac, + audio->pcm_cfg.sample_rate, + audio->pcm_cfg.channel_count); + if (rc < 0) { + pr_err("%s: pcm output block config failed rc=%d\n", + __func__, rc); + break; + } + } + + rc = audio_aio_enable(audio); + audio->eos_rsp = 0; + audio->eos_flag = 0; + if (!rc) { + audio->enabled = 1; + } else { + audio->enabled = 0; + pr_err("%s: Audio Start procedure failed rc=%d\n", + __func__, rc); + break; + } + pr_debug("AUDIO_START success enable[%d]\n", audio->enabled); + if (audio->stopped == 1) + audio->stopped = 0; + break; + } + default: + pr_debug("%s[%pK]: Calling compat ioctl\n", __func__, audio); + rc = audio->codec_compat_ioctl(file, cmd, arg); + } + return rc; +} + + +static int audio_open(struct inode *inode, struct file *file) +{ + struct q6audio_aio *audio = NULL; + int rc = 0; + +#ifdef CONFIG_DEBUG_FS + /* 4 bytes represents decoder number, 1 byte for terminate string */ + char name[sizeof "msm_amrnb_" + 5]; +#endif + audio = kzalloc(sizeof(struct q6audio_aio), GFP_KERNEL); + + if (audio == NULL) + return -ENOMEM; + + audio->pcm_cfg.buffer_size = PCM_BUFSZ_MIN; + audio->miscdevice = &audio_amrnb_misc; + audio->wakelock_voted = false; + audio->audio_ws_mgr = &audio_amrnb_ws_mgr; + + audio->ac = q6asm_audio_client_alloc((app_cb) q6_audio_cb, + (void *)audio); + + if (!audio->ac) { + pr_err("Could not allocate memory for audio client\n"); + kfree(audio); + return -ENOMEM; + } + rc = audio_aio_open(audio, file); + if (rc < 0) { + pr_err("%s: audio_aio_open rc=%d\n", + __func__, rc); + goto fail; + } + /* open in T/NT mode */ + if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) { + rc = q6asm_open_read_write(audio->ac, FORMAT_LINEAR_PCM, + FORMAT_AMRNB); + if (rc < 0) { + pr_err("NT mode Open failed rc=%d\n", rc); + rc = -ENODEV; + goto fail; + } + audio->feedback = NON_TUNNEL_MODE; + audio->buf_cfg.frames_per_buf = 0x01; + audio->buf_cfg.meta_info_enable = 0x01; + } else if ((file->f_mode & FMODE_WRITE) && + !(file->f_mode & FMODE_READ)) { + rc = q6asm_open_write(audio->ac, FORMAT_AMRNB); + if (rc < 0) { + pr_err("T mode Open failed rc=%d\n", rc); + rc = -ENODEV; + goto fail; + } + audio->feedback = TUNNEL_MODE; + audio->buf_cfg.meta_info_enable = 0x00; + } else { + pr_err("Not supported mode\n"); + rc = -EACCES; + goto fail; + } + +#ifdef CONFIG_DEBUG_FS + snprintf(name, sizeof(name), "msm_amrnb_%04x", audio->ac->session); + audio->dentry = debugfs_create_file(name, S_IFREG | 0444, + NULL, (void *)audio, + &audio_amrnb_debug_fops); + + if (IS_ERR(audio->dentry)) + pr_debug("debugfs_create_file failed\n"); +#endif + pr_info("%s:amrnb decoder open success, session_id = %d\n", __func__, + audio->ac->session); + return rc; +fail: + q6asm_audio_client_free(audio->ac); + kfree(audio); + return rc; +} + +static const struct file_operations audio_amrnb_fops = { + .owner = THIS_MODULE, + .open = audio_open, + .release = audio_aio_release, + .unlocked_ioctl = audio_ioctl, + .fsync = audio_aio_fsync, + .compat_ioctl = audio_compat_ioctl, +}; + +static struct miscdevice audio_amrnb_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_amrnb", + .fops = &audio_amrnb_fops, +}; + +int __init audio_amrnb_init(void) +{ + int ret = misc_register(&audio_amrnb_misc); + + if (ret == 0) + device_init_wakeup(audio_amrnb_misc.this_device, true); + audio_amrnb_ws_mgr.ref_cnt = 0; + mutex_init(&audio_amrnb_ws_mgr.ws_lock); + + return ret; +} + +void audio_amrnb_exit(void) +{ + mutex_destroy(&audio_amrnb_ws_mgr.ws_lock); + misc_deregister(&audio_amrnb_misc); +} diff --git a/audio-kernel/dsp/codecs/audio_amrwb.c b/audio-kernel/dsp/codecs/audio_amrwb.c new file mode 100644 index 000000000..140878214 --- /dev/null +++ b/audio-kernel/dsp/codecs/audio_amrwb.c @@ -0,0 +1,235 @@ +/* amrwb audio output device + * + * Copyright (C) 2008 Google, Inc. + * Copyright (C) 2008 HTC Corporation + * Copyright (c) 2011-2017, The Linux Foundation. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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 +#include +#include "audio_utils_aio.h" + +static struct miscdevice audio_amrwb_misc; +static struct ws_mgr audio_amrwb_ws_mgr; + +#ifdef CONFIG_DEBUG_FS +static const struct file_operations audio_amrwb_debug_fops = { + .read = audio_aio_debug_read, + .open = audio_aio_debug_open, +}; +#endif + +static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct q6audio_aio *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_START: { + pr_debug("%s[%pK]: AUDIO_START session_id[%d]\n", __func__, + audio, audio->ac->session); + if (audio->feedback == NON_TUNNEL_MODE) { + /* Configure PCM output block */ + rc = q6asm_enc_cfg_blk_pcm(audio->ac, + audio->pcm_cfg.sample_rate, + audio->pcm_cfg.channel_count); + if (rc < 0) { + pr_err("pcm output block config failed\n"); + break; + } + } + + rc = audio_aio_enable(audio); + audio->eos_rsp = 0; + audio->eos_flag = 0; + if (!rc) { + audio->enabled = 1; + } else { + audio->enabled = 0; + pr_err("Audio Start procedure failed rc=%d\n", rc); + break; + } + pr_debug("%s: AUDIO_START sessionid[%d]enable[%d]\n", __func__, + audio->ac->session, + audio->enabled); + if (audio->stopped == 1) + audio->stopped = 0; + break; + } + default: + pr_debug("%s[%pK]: Calling utils ioctl\n", __func__, audio); + rc = audio->codec_ioctl(file, cmd, arg); + } + return rc; +} + +static long audio_compat_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct q6audio_aio *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_START: { + pr_debug("%s[%pK]: AUDIO_START session_id[%d]\n", __func__, + audio, audio->ac->session); + if (audio->feedback == NON_TUNNEL_MODE) { + /* Configure PCM output block */ + rc = q6asm_enc_cfg_blk_pcm(audio->ac, + audio->pcm_cfg.sample_rate, + audio->pcm_cfg.channel_count); + if (rc < 0) { + pr_err("%s: pcm output block config failed rc=%d\n", + __func__, rc); + break; + } + } + + rc = audio_aio_enable(audio); + audio->eos_rsp = 0; + audio->eos_flag = 0; + if (!rc) { + audio->enabled = 1; + } else { + audio->enabled = 0; + pr_err("%s: Audio Start procedure failed rc=%d\n", + __func__, rc); + break; + } + pr_debug("%s: AUDIO_START sessionid[%d]enable[%d]\n", __func__, + audio->ac->session, + audio->enabled); + if (audio->stopped == 1) + audio->stopped = 0; + break; + } + default: + pr_debug("%s[%pK]: Calling compat ioctl\n", __func__, audio); + rc = audio->codec_compat_ioctl(file, cmd, arg); + } + return rc; +} + +static int audio_open(struct inode *inode, struct file *file) +{ + struct q6audio_aio *audio = NULL; + int rc = 0; + +#ifdef CONFIG_DEBUG_FS + /* 4 bytes represents decoder number, 1 byte for terminate string */ + char name[sizeof "msm_amrwb_" + 5]; +#endif + audio = kzalloc(sizeof(struct q6audio_aio), GFP_KERNEL); + + if (audio == NULL) + return -ENOMEM; + + audio->pcm_cfg.buffer_size = PCM_BUFSZ_MIN; + audio->miscdevice = &audio_amrwb_misc; + audio->wakelock_voted = false; + audio->audio_ws_mgr = &audio_amrwb_ws_mgr; + + audio->ac = q6asm_audio_client_alloc((app_cb) q6_audio_cb, + (void *)audio); + + if (!audio->ac) { + pr_err("Could not allocate memory for audio client\n"); + kfree(audio); + return -ENOMEM; + } + rc = audio_aio_open(audio, file); + if (rc < 0) { + pr_err("%s: audio_aio_open rc=%d\n", + __func__, rc); + goto fail; + } + + /* open in T/NT mode */ + if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) { + rc = q6asm_open_read_write(audio->ac, FORMAT_LINEAR_PCM, + FORMAT_AMRWB); + if (rc < 0) { + pr_err("NT mode Open failed rc=%d\n", rc); + rc = -ENODEV; + goto fail; + } + audio->feedback = NON_TUNNEL_MODE; + audio->buf_cfg.frames_per_buf = 0x01; + audio->buf_cfg.meta_info_enable = 0x01; + } else if ((file->f_mode & FMODE_WRITE) && + !(file->f_mode & FMODE_READ)) { + rc = q6asm_open_write(audio->ac, FORMAT_AMRWB); + if (rc < 0) { + pr_err("T mode Open failed rc=%d\n", rc); + rc = -ENODEV; + goto fail; + } + audio->feedback = TUNNEL_MODE; + audio->buf_cfg.meta_info_enable = 0x00; + } else { + pr_err("Not supported mode\n"); + rc = -EACCES; + goto fail; + } + +#ifdef CONFIG_DEBUG_FS + snprintf(name, sizeof(name), "msm_amrwb_%04x", audio->ac->session); + audio->dentry = debugfs_create_file(name, S_IFREG | 0444, + NULL, (void *)audio, + &audio_amrwb_debug_fops); + + if (IS_ERR(audio->dentry)) + pr_debug("debugfs_create_file failed\n"); +#endif + pr_info("%s: AMRWB dec success mode[%d]session[%d]\n", __func__, + audio->feedback, + audio->ac->session); + return 0; +fail: + q6asm_audio_client_free(audio->ac); + kfree(audio); + return rc; +} + +static const struct file_operations audio_amrwb_fops = { + .owner = THIS_MODULE, + .open = audio_open, + .release = audio_aio_release, + .unlocked_ioctl = audio_ioctl, + .fsync = audio_aio_fsync, + .compat_ioctl = audio_compat_ioctl, +}; + +static struct miscdevice audio_amrwb_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_amrwb", + .fops = &audio_amrwb_fops, +}; + +int __init audio_amrwb_init(void) +{ + int ret = misc_register(&audio_amrwb_misc); + + if (ret == 0) + device_init_wakeup(audio_amrwb_misc.this_device, true); + audio_amrwb_ws_mgr.ref_cnt = 0; + mutex_init(&audio_amrwb_ws_mgr.ws_lock); + + return ret; +} + +void audio_amrwb_exit(void) +{ + mutex_destroy(&audio_amrwb_ws_mgr.ws_lock); + misc_deregister(&audio_amrwb_misc); +} diff --git a/audio-kernel/dsp/codecs/audio_amrwbplus.c b/audio-kernel/dsp/codecs/audio_amrwbplus.c new file mode 100644 index 000000000..9d6c11871 --- /dev/null +++ b/audio-kernel/dsp/codecs/audio_amrwbplus.c @@ -0,0 +1,401 @@ +/* amr-wbplus audio output device + * + * Copyright (C) 2008 Google, Inc. + * Copyright (C) 2008 HTC Corporation + * Copyright (c) 2010-2017, The Linux Foundation. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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 +#include +#include "audio_utils_aio.h" + +static struct miscdevice audio_amrwbplus_misc; +static struct ws_mgr audio_amrwbplus_ws_mgr; + +#ifdef CONFIG_DEBUG_FS +static const struct file_operations audio_amrwbplus_debug_fops = { + .read = audio_aio_debug_read, + .open = audio_aio_debug_open, +}; +static void config_debug_fs(struct q6audio_aio *audio) +{ + if (audio != NULL) { + char name[sizeof("msm_amrwbplus_") + 5]; + + snprintf(name, sizeof(name), "msm_amrwbplus_%04x", + audio->ac->session); + audio->dentry = debugfs_create_file(name, S_IFREG | 0444, + NULL, (void *)audio, + &audio_amrwbplus_debug_fops); + if (IS_ERR(audio->dentry)) + pr_debug("debugfs_create_file failed\n"); + } +} +#else +static void config_debug_fs(struct q6audio_aio *audio) +{ +} +#endif + +static long audio_ioctl_shared(struct file *file, unsigned int cmd, + void *arg) +{ + struct asm_amrwbplus_cfg q6_amrwbplus_cfg; + struct msm_audio_amrwbplus_config_v2 *amrwbplus_drv_config; + struct q6audio_aio *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_START: + pr_err("%s[%pK]: AUDIO_START session_id[%d]\n", __func__, + audio, audio->ac->session); + if (audio->feedback == NON_TUNNEL_MODE) { + /* Configure PCM output block */ + rc = q6asm_enc_cfg_blk_pcm(audio->ac, + audio->pcm_cfg.sample_rate, + audio->pcm_cfg.channel_count); + if (rc < 0) { + pr_err("pcm output block config failed\n"); + break; + } + } + amrwbplus_drv_config = + (struct msm_audio_amrwbplus_config_v2 *)audio->codec_cfg; + + q6_amrwbplus_cfg.size_bytes = + amrwbplus_drv_config->size_bytes; + q6_amrwbplus_cfg.version = + amrwbplus_drv_config->version; + q6_amrwbplus_cfg.num_channels = + amrwbplus_drv_config->num_channels; + q6_amrwbplus_cfg.amr_band_mode = + amrwbplus_drv_config->amr_band_mode; + q6_amrwbplus_cfg.amr_dtx_mode = + amrwbplus_drv_config->amr_dtx_mode; + q6_amrwbplus_cfg.amr_frame_fmt = + amrwbplus_drv_config->amr_frame_fmt; + q6_amrwbplus_cfg.amr_lsf_idx = + amrwbplus_drv_config->amr_lsf_idx; + + rc = q6asm_media_format_block_amrwbplus(audio->ac, + &q6_amrwbplus_cfg); + if (rc < 0) { + pr_err("q6asm_media_format_block_amrwb+ failed...\n"); + break; + } + rc = audio_aio_enable(audio); + audio->eos_rsp = 0; + audio->eos_flag = 0; + if (!rc) { + audio->enabled = 1; + } else { + audio->enabled = 0; + pr_err("Audio Start procedure failed rc=%d\n", rc); + break; + } + pr_debug("%s:AUDIO_START sessionid[%d]enable[%d]\n", __func__, + audio->ac->session, + audio->enabled); + if (audio->stopped == 1) + audio->stopped = 0; + + break; + default: + pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd); + rc = -EINVAL; + break; + } + return rc; +} + +static long audio_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct q6audio_aio *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_START: { + rc = audio_ioctl_shared(file, cmd, (void *)arg); + break; + } + case AUDIO_GET_AMRWBPLUS_CONFIG_V2: { + if ((audio) && (arg) && (audio->codec_cfg)) { + if (copy_to_user((void *)arg, audio->codec_cfg, + sizeof(struct msm_audio_amrwbplus_config_v2))) { + rc = -EFAULT; + pr_err("%s: copy_to_user for AUDIO_GET_AMRWBPLUS_CONFIG_V2 failed\n", + __func__); + break; + } + } else { + pr_err("%s: wb+ config v2 invalid parameters\n" + , __func__); + rc = -EFAULT; + break; + } + break; + } + case AUDIO_SET_AMRWBPLUS_CONFIG_V2: { + if ((audio) && (arg) && (audio->codec_cfg)) { + if (copy_from_user(audio->codec_cfg, (void *)arg, + sizeof(struct msm_audio_amrwbplus_config_v2))) { + rc = -EFAULT; + pr_err("%s: copy_from_user for AUDIO_SET_AMRWBPLUS_CONFIG_V2 failed\n", + __func__); + break; + } + } else { + pr_err("%s: wb+ config invalid parameters\n", + __func__); + rc = -EFAULT; + break; + } + break; + } + default: { + pr_debug("%s[%pK]: Calling utils ioctl\n", __func__, audio); + rc = audio->codec_ioctl(file, cmd, arg); + break; + } + } + return rc; +} +#ifdef CONFIG_COMPAT +struct msm_audio_amrwbplus_config_v2_32 { + u32 size_bytes; + u32 version; + u32 num_channels; + u32 amr_band_mode; + u32 amr_dtx_mode; + u32 amr_frame_fmt; + u32 amr_lsf_idx; +}; + +enum { + AUDIO_GET_AMRWBPLUS_CONFIG_V2_32 = _IOR(AUDIO_IOCTL_MAGIC, + (AUDIO_MAX_COMMON_IOCTL_NUM+2), + struct msm_audio_amrwbplus_config_v2_32), + AUDIO_SET_AMRWBPLUS_CONFIG_V2_32 = _IOW(AUDIO_IOCTL_MAGIC, + (AUDIO_MAX_COMMON_IOCTL_NUM+3), + struct msm_audio_amrwbplus_config_v2_32) +}; + +static long audio_compat_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct q6audio_aio *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_START: { + rc = audio_ioctl_shared(file, cmd, (void *)arg); + break; + } + case AUDIO_GET_AMRWBPLUS_CONFIG_V2_32: { + if (audio && arg && (audio->codec_cfg)) { + struct msm_audio_amrwbplus_config_v2 *amrwbplus_config; + struct msm_audio_amrwbplus_config_v2_32 + amrwbplus_config_32; + + memset(&amrwbplus_config_32, 0, + sizeof(amrwbplus_config_32)); + + amrwbplus_config = + (struct msm_audio_amrwbplus_config_v2 *) + audio->codec_cfg; + amrwbplus_config_32.size_bytes = + amrwbplus_config->size_bytes; + amrwbplus_config_32.version = + amrwbplus_config->version; + amrwbplus_config_32.num_channels = + amrwbplus_config->num_channels; + amrwbplus_config_32.amr_band_mode = + amrwbplus_config->amr_band_mode; + amrwbplus_config_32.amr_dtx_mode = + amrwbplus_config->amr_dtx_mode; + amrwbplus_config_32.amr_frame_fmt = + amrwbplus_config->amr_frame_fmt; + amrwbplus_config_32.amr_lsf_idx = + amrwbplus_config->amr_lsf_idx; + + if (copy_to_user((void *)arg, &amrwbplus_config_32, + sizeof(amrwbplus_config_32))) { + rc = -EFAULT; + pr_err("%s: copy_to_user for AUDIO_GET_AMRWBPLUS_CONFIG_V2_32 failed\n" + , __func__); + } + } else { + pr_err("%s: wb+ Get config v2 invalid parameters\n" + , __func__); + rc = -EFAULT; + } + break; + } + case AUDIO_SET_AMRWBPLUS_CONFIG_V2_32: { + if ((audio) && (arg) && (audio->codec_cfg)) { + struct msm_audio_amrwbplus_config_v2 *amrwbplus_config; + struct msm_audio_amrwbplus_config_v2_32 + amrwbplus_config_32; + + if (copy_from_user(&amrwbplus_config_32, (void *)arg, + sizeof(struct msm_audio_amrwbplus_config_v2_32))) { + rc = -EFAULT; + pr_err("%s: copy_from_user for AUDIO_SET_AMRWBPLUS_CONFIG_V2_32 failed\n" + , __func__); + break; + } + amrwbplus_config = + (struct msm_audio_amrwbplus_config_v2 *) + audio->codec_cfg; + amrwbplus_config->size_bytes = + amrwbplus_config_32.size_bytes; + amrwbplus_config->version = + amrwbplus_config_32.version; + amrwbplus_config->num_channels = + amrwbplus_config_32.num_channels; + amrwbplus_config->amr_band_mode = + amrwbplus_config_32.amr_band_mode; + amrwbplus_config->amr_dtx_mode = + amrwbplus_config_32.amr_dtx_mode; + amrwbplus_config->amr_frame_fmt = + amrwbplus_config_32.amr_frame_fmt; + amrwbplus_config->amr_lsf_idx = + amrwbplus_config_32.amr_lsf_idx; + } else { + pr_err("%s: wb+ config invalid parameters\n", + __func__); + rc = -EFAULT; + } + break; + } + default: { + pr_debug("%s[%pK]: Calling utils ioctl\n", __func__, audio); + rc = audio->codec_compat_ioctl(file, cmd, arg); + break; + } + } + return rc; +} +#else +#define audio_compat_ioctl NULL +#endif + +static int audio_open(struct inode *inode, struct file *file) +{ + struct q6audio_aio *audio = NULL; + int rc = 0; + + audio = kzalloc(sizeof(struct q6audio_aio), GFP_KERNEL); + + if (audio == NULL) + return -ENOMEM; + + audio->codec_cfg = + kzalloc(sizeof(struct msm_audio_amrwbplus_config_v2), GFP_KERNEL); + if (audio->codec_cfg == NULL) { + kfree(audio); + return -ENOMEM; + } + audio->pcm_cfg.buffer_size = PCM_BUFSZ_MIN; + audio->miscdevice = &audio_amrwbplus_misc; + audio->wakelock_voted = false; + audio->audio_ws_mgr = &audio_amrwbplus_ws_mgr; + + audio->ac = + q6asm_audio_client_alloc((app_cb) q6_audio_cb, (void *)audio); + + if (!audio->ac) { + pr_err("Could not allocate memory for audio client\n"); + kfree(audio->codec_cfg); + kfree(audio); + return -ENOMEM; + } + rc = audio_aio_open(audio, file); + if (rc < 0) { + pr_err("%s: audio_aio_open rc=%d\n", + __func__, rc); + goto fail; + } + + /* open in T/NT mode */ + if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) { + rc = q6asm_open_read_write(audio->ac, FORMAT_LINEAR_PCM, + FORMAT_AMR_WB_PLUS); + if (rc < 0) { + pr_err("amrwbplus NT mode Open failed rc=%d\n", rc); + rc = -ENODEV; + goto fail; + } + audio->feedback = NON_TUNNEL_MODE; + audio->buf_cfg.frames_per_buf = 0x01; + audio->buf_cfg.meta_info_enable = 0x01; + } else if ((file->f_mode & FMODE_WRITE) && + !(file->f_mode & FMODE_READ)) { + rc = q6asm_open_write(audio->ac, FORMAT_AMR_WB_PLUS); + if (rc < 0) { + pr_err("wb+ T mode Open failed rc=%d\n", rc); + rc = -ENODEV; + goto fail; + } + audio->feedback = TUNNEL_MODE; + audio->buf_cfg.meta_info_enable = 0x00; + } else { + pr_err("audio_amrwbplus Not supported mode\n"); + rc = -EACCES; + goto fail; + } + + config_debug_fs(audio); + pr_debug("%s: AMRWBPLUS dec success mode[%d]session[%d]\n", __func__, + audio->feedback, + audio->ac->session); + return 0; +fail: + q6asm_audio_client_free(audio->ac); + kfree(audio->codec_cfg); + kfree(audio); + return rc; +} + +static const struct file_operations audio_amrwbplus_fops = { + .owner = THIS_MODULE, + .open = audio_open, + .release = audio_aio_release, + .unlocked_ioctl = audio_ioctl, + .fsync = audio_aio_fsync, + .compat_ioctl = audio_compat_ioctl +}; + +static struct miscdevice audio_amrwbplus_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_amrwbplus", + .fops = &audio_amrwbplus_fops, +}; + +int __init audio_amrwbplus_init(void) +{ + int ret = misc_register(&audio_amrwbplus_misc); + + if (ret == 0) + device_init_wakeup(audio_amrwbplus_misc.this_device, true); + audio_amrwbplus_ws_mgr.ref_cnt = 0; + mutex_init(&audio_amrwbplus_ws_mgr.ws_lock); + + return ret; +} + +void audio_amrwbplus_exit(void) +{ + mutex_destroy(&audio_amrwbplus_ws_mgr.ws_lock); + misc_deregister(&audio_amrwbplus_misc); +} diff --git a/audio-kernel/dsp/codecs/audio_ape.c b/audio-kernel/dsp/codecs/audio_ape.c new file mode 100644 index 000000000..f2a6bf7cc --- /dev/null +++ b/audio-kernel/dsp/codecs/audio_ape.c @@ -0,0 +1,363 @@ +/* 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 + * 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 +#include +#include +#include "audio_utils_aio.h" + +static struct miscdevice audio_ape_misc; +static struct ws_mgr audio_ape_ws_mgr; + +static const struct file_operations audio_ape_debug_fops = { + .read = audio_aio_debug_read, + .open = audio_aio_debug_open, +}; +static struct dentry *config_debugfs_create_file(const char *name, void *data) +{ + return debugfs_create_file(name, S_IFREG | 0444, + NULL, (void *)data, &audio_ape_debug_fops); +} + +static long audio_ioctl_shared(struct file *file, unsigned int cmd, + void *arg) +{ + struct q6audio_aio *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_START: { + struct asm_ape_cfg ape_cfg; + struct msm_audio_ape_config *ape_config; + + pr_debug("%s[%pK]: AUDIO_START session_id[%d]\n", __func__, + audio, audio->ac->session); + if (audio->feedback == NON_TUNNEL_MODE) { + /* Configure PCM output block */ + rc = q6asm_enc_cfg_blk_pcm(audio->ac, + audio->pcm_cfg.sample_rate, + audio->pcm_cfg.channel_count); + if (rc < 0) { + pr_err("pcm output block config failed\n"); + break; + } + } + ape_config = (struct msm_audio_ape_config *)audio->codec_cfg; + ape_cfg.compatible_version = ape_config->compatibleVersion; + ape_cfg.compression_level = ape_config->compressionLevel; + ape_cfg.format_flags = ape_config->formatFlags; + ape_cfg.blocks_per_frame = ape_config->blocksPerFrame; + ape_cfg.final_frame_blocks = ape_config->finalFrameBlocks; + ape_cfg.total_frames = ape_config->totalFrames; + ape_cfg.bits_per_sample = ape_config->bitsPerSample; + ape_cfg.num_channels = ape_config->numChannels; + ape_cfg.sample_rate = ape_config->sampleRate; + ape_cfg.seek_table_present = ape_config->seekTablePresent; + pr_debug("%s: compatibleVersion %d compressionLevel %d formatFlags %d blocksPerFrame %d finalFrameBlocks %d totalFrames %d bitsPerSample %d numChannels %d sampleRate %d seekTablePresent %d\n", + __func__, ape_config->compatibleVersion, + ape_config->compressionLevel, + ape_config->formatFlags, + ape_config->blocksPerFrame, + ape_config->finalFrameBlocks, + ape_config->totalFrames, + ape_config->bitsPerSample, + ape_config->numChannels, + ape_config->sampleRate, + ape_config->seekTablePresent); + /* Configure Media format block */ + rc = q6asm_media_format_block_ape(audio->ac, &ape_cfg, + audio->ac->stream_id); + if (rc < 0) { + pr_err("cmd media format block failed\n"); + break; + } + rc = audio_aio_enable(audio); + audio->eos_rsp = 0; + audio->eos_flag = 0; + if (!rc) { + audio->enabled = 1; + } else { + audio->enabled = 0; + pr_err("Audio Start procedure failed rc=%d\n", rc); + break; + } + pr_debug("AUDIO_START success enable[%d]\n", audio->enabled); + if (audio->stopped == 1) + audio->stopped = 0; + break; + } + default: + pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd); + break; + } + return rc; +} + +static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct q6audio_aio *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_START: { + rc = audio_ioctl_shared(file, cmd, (void *)arg); + break; + } + case AUDIO_GET_APE_CONFIG: { + if (copy_to_user((void *)arg, audio->codec_cfg, + sizeof(struct msm_audio_ape_config))) { + pr_err("%s:copy_to_user for AUDIO_GET_APE_CONFIG failed\n", + __func__); + rc = -EFAULT; + break; + } + break; + } + case AUDIO_SET_APE_CONFIG: { + if (copy_from_user(audio->codec_cfg, (void *)arg, + sizeof(struct msm_audio_ape_config))) { + pr_err("%s:copy_from_user for AUDIO_SET_APE_CONFIG failed\n", + __func__); + rc = -EFAULT; + break; + } + break; + } + default: { + pr_debug("%s[%pK]: Calling utils ioctl\n", __func__, audio); + rc = audio->codec_ioctl(file, cmd, arg); + if (rc) + pr_err_ratelimited("Failed in utils_ioctl: %d\n", rc); + break; + } + } + return rc; +} + +#ifdef CONFIG_COMPAT +struct msm_audio_ape_config_32 { + u16 compatibleVersion; + u16 compressionLevel; + u32 formatFlags; + u32 blocksPerFrame; + u32 finalFrameBlocks; + u32 totalFrames; + u16 bitsPerSample; + u16 numChannels; + u32 sampleRate; + u32 seekTablePresent; + +}; + +enum { + AUDIO_GET_APE_CONFIG_32 = _IOR(AUDIO_IOCTL_MAGIC, + (AUDIO_MAX_COMMON_IOCTL_NUM+0), struct msm_audio_ape_config_32), + AUDIO_SET_APE_CONFIG_32 = _IOW(AUDIO_IOCTL_MAGIC, + (AUDIO_MAX_COMMON_IOCTL_NUM+1), struct msm_audio_ape_config_32) +}; + +static long audio_compat_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct q6audio_aio *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_START: { + rc = audio_ioctl_shared(file, cmd, (void *)arg); + break; + } + case AUDIO_GET_APE_CONFIG_32: { + struct msm_audio_ape_config *ape_config; + struct msm_audio_ape_config_32 ape_config_32; + + memset(&ape_config_32, 0, sizeof(ape_config_32)); + + ape_config = (struct msm_audio_ape_config *)audio->codec_cfg; + ape_config_32.compatibleVersion = ape_config->compatibleVersion; + ape_config_32.compressionLevel = + ape_config->compressionLevel; + ape_config_32.formatFlags = ape_config->formatFlags; + ape_config_32.blocksPerFrame = ape_config->blocksPerFrame; + ape_config_32.finalFrameBlocks = ape_config->finalFrameBlocks; + ape_config_32.totalFrames = ape_config->totalFrames; + ape_config_32.bitsPerSample = ape_config->bitsPerSample; + ape_config_32.numChannels = ape_config->numChannels; + ape_config_32.sampleRate = ape_config->sampleRate; + ape_config_32.seekTablePresent = ape_config->seekTablePresent; + + if (copy_to_user((void *)arg, &ape_config_32, + sizeof(ape_config_32))) { + pr_err("%s: copy_to_user for GET_APE_CONFIG_32 failed\n", + __func__); + rc = -EFAULT; + break; + } + break; + } + case AUDIO_SET_APE_CONFIG_32: { + struct msm_audio_ape_config *ape_config; + struct msm_audio_ape_config_32 ape_config_32; + + if (copy_from_user(&ape_config_32, (void *)arg, + sizeof(ape_config_32))) { + pr_err("%s: copy_from_user for SET_APE_CONFIG_32 failed\n" + , __func__); + rc = -EFAULT; + break; + } + ape_config = (struct msm_audio_ape_config *)audio->codec_cfg; + ape_config->compatibleVersion = ape_config_32.compatibleVersion; + ape_config->compressionLevel = + ape_config_32.compressionLevel; + ape_config->formatFlags = ape_config_32.formatFlags; + ape_config->blocksPerFrame = ape_config_32.blocksPerFrame; + ape_config->finalFrameBlocks = ape_config_32.finalFrameBlocks; + ape_config->totalFrames = ape_config_32.totalFrames; + ape_config->bitsPerSample = ape_config_32.bitsPerSample; + ape_config->numChannels = ape_config_32.numChannels; + ape_config->sampleRate = ape_config_32.sampleRate; + ape_config->seekTablePresent = ape_config_32.seekTablePresent; + + break; + } + default: { + pr_debug("%s[%pK]: Calling utils ioctl\n", __func__, audio); + rc = audio->codec_compat_ioctl(file, cmd, arg); + if (rc) + pr_err_ratelimited("Failed in utils_ioctl: %d\n", rc); + break; + } + } + return rc; +} +#else +#define audio_compat_ioctl NULL +#endif + +static int audio_open(struct inode *inode, struct file *file) +{ + struct q6audio_aio *audio = NULL; + int rc = 0; + + /* 4 bytes represents decoder number, 1 byte for terminate string */ + char name[sizeof "msm_ape_" + 5]; + + audio = kzalloc(sizeof(struct q6audio_aio), GFP_KERNEL); + if (!audio) + return -ENOMEM; + + audio->codec_cfg = kzalloc(sizeof(struct msm_audio_ape_config), + GFP_KERNEL); + if (!audio->codec_cfg) { + kfree(audio); + return -ENOMEM; + } + + audio->pcm_cfg.buffer_size = PCM_BUFSZ_MIN; + audio->miscdevice = &audio_ape_misc; + audio->wakelock_voted = false; + audio->audio_ws_mgr = &audio_ape_ws_mgr; + + audio->ac = q6asm_audio_client_alloc((app_cb) q6_audio_cb, + (void *)audio); + + if (!audio->ac) { + pr_err("Could not allocate memory for audio client\n"); + kfree(audio->codec_cfg); + kfree(audio); + return -ENOMEM; + } + rc = audio_aio_open(audio, file); + if (rc < 0) { + pr_err("%s: audio_aio_open rc=%d\n", + __func__, rc); + goto fail; + } + /* open in T/NT mode */ + if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) { + rc = q6asm_open_read_write(audio->ac, FORMAT_LINEAR_PCM, + FORMAT_APE); + if (rc < 0) { + pr_err("NT mode Open failed rc=%d\n", rc); + rc = -ENODEV; + goto fail; + } + audio->feedback = NON_TUNNEL_MODE; + /* open APE decoder, expected frames is always 1*/ + audio->buf_cfg.frames_per_buf = 0x01; + audio->buf_cfg.meta_info_enable = 0x01; + } else if ((file->f_mode & FMODE_WRITE) && + !(file->f_mode & FMODE_READ)) { + rc = q6asm_open_write(audio->ac, FORMAT_APE); + if (rc < 0) { + pr_err("T mode Open failed rc=%d\n", rc); + rc = -ENODEV; + goto fail; + } + audio->feedback = TUNNEL_MODE; + audio->buf_cfg.meta_info_enable = 0x00; + } else { + pr_err("Not supported mode\n"); + rc = -EACCES; + goto fail; + } + + snprintf(name, sizeof(name), "msm_ape_%04x", audio->ac->session); + audio->dentry = config_debugfs_create_file(name, (void *)audio); + + if (IS_ERR_OR_NULL(audio->dentry)) + pr_debug("debugfs_create_file failed\n"); + pr_debug("%s:apedec success mode[%d]session[%d]\n", __func__, + audio->feedback, + audio->ac->session); + return rc; +fail: + q6asm_audio_client_free(audio->ac); + kfree(audio->codec_cfg); + kfree(audio); + return rc; +} + +static const struct file_operations audio_ape_fops = { + .owner = THIS_MODULE, + .open = audio_open, + .release = audio_aio_release, + .unlocked_ioctl = audio_ioctl, + .fsync = audio_aio_fsync, + .compat_ioctl = audio_compat_ioctl +}; + +static struct miscdevice audio_ape_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_ape", + .fops = &audio_ape_fops, +}; + +int __init audio_ape_init(void) +{ + int ret = misc_register(&audio_ape_misc); + + if (ret == 0) + device_init_wakeup(audio_ape_misc.this_device, true); + audio_ape_ws_mgr.ref_cnt = 0; + mutex_init(&audio_ape_ws_mgr.ws_lock); + + return ret; +} + +void audio_ape_exit(void) +{ + mutex_destroy(&audio_ape_ws_mgr.ws_lock); + misc_deregister(&audio_ape_misc); +} diff --git a/audio-kernel/dsp/codecs/audio_evrc.c b/audio-kernel/dsp/codecs/audio_evrc.c new file mode 100644 index 000000000..6cddc9cac --- /dev/null +++ b/audio-kernel/dsp/codecs/audio_evrc.c @@ -0,0 +1,188 @@ +/* evrc audio output device + * + * Copyright (C) 2008 Google, Inc. + * Copyright (C) 2008 HTC Corporation + * Copyright (c) 2011-2017, The Linux Foundation. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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 "audio_utils_aio.h" + +static struct miscdevice audio_evrc_misc; +static struct ws_mgr audio_evrc_ws_mgr; + +#ifdef CONFIG_DEBUG_FS +static const struct file_operations audio_evrc_debug_fops = { + .read = audio_aio_debug_read, + .open = audio_aio_debug_open, +}; +#endif + +static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct q6audio_aio *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_START: { + pr_debug("%s[%pK]: AUDIO_START session_id[%d]\n", __func__, + audio, audio->ac->session); + if (audio->feedback == NON_TUNNEL_MODE) { + /* Configure PCM output block */ + rc = q6asm_enc_cfg_blk_pcm(audio->ac, + audio->pcm_cfg.sample_rate, + audio->pcm_cfg.channel_count); + if (rc < 0) { + pr_err("pcm output block config failed\n"); + break; + } + } + + rc = audio_aio_enable(audio); + audio->eos_rsp = 0; + audio->eos_flag = 0; + if (!rc) { + audio->enabled = 1; + } else { + audio->enabled = 0; + pr_err("Audio Start procedure failed rc=%d\n", rc); + break; + } + pr_debug("%s: AUDIO_START sessionid[%d]enable[%d]\n", __func__, + audio->ac->session, + audio->enabled); + if (audio->stopped == 1) + audio->stopped = 0; + break; + } + default: + pr_debug("%s[%pK]: Calling utils ioctl\n", __func__, audio); + rc = audio->codec_ioctl(file, cmd, arg); + } + return rc; +} + +static int audio_open(struct inode *inode, struct file *file) +{ + struct q6audio_aio *audio = NULL; + int rc = 0; + +#ifdef CONFIG_DEBUG_FS + /* 4 bytes represents decoder number, 1 byte for terminate string */ + char name[sizeof "msm_evrc_" + 5]; +#endif + audio = kzalloc(sizeof(struct q6audio_aio), GFP_KERNEL); + + if (audio == NULL) + return -ENOMEM; + + /* Settings will be re-config at AUDIO_SET_CONFIG, + * but at least we need to have initial config + */ + audio->pcm_cfg.buffer_size = PCM_BUFSZ_MIN; + audio->miscdevice = &audio_evrc_misc; + audio->wakelock_voted = false; + audio->audio_ws_mgr = &audio_evrc_ws_mgr; + + audio->ac = q6asm_audio_client_alloc((app_cb) q6_audio_cb, + (void *)audio); + + if (!audio->ac) { + pr_err("Could not allocate memory for audio client\n"); + kfree(audio); + return -ENOMEM; + } + rc = audio_aio_open(audio, file); + if (rc < 0) { + pr_err("%s: audio_aio_open rc=%d\n", + __func__, rc); + goto fail; + } + + /* open in T/NT mode */ + if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) { + rc = q6asm_open_read_write(audio->ac, FORMAT_LINEAR_PCM, + FORMAT_EVRC); + if (rc < 0) { + pr_err("NT mode Open failed rc=%d\n", rc); + rc = -ENODEV; + goto fail; + } + audio->feedback = NON_TUNNEL_MODE; + audio->buf_cfg.frames_per_buf = 0x01; + audio->buf_cfg.meta_info_enable = 0x01; + } else if ((file->f_mode & FMODE_WRITE) && + !(file->f_mode & FMODE_READ)) { + rc = q6asm_open_write(audio->ac, FORMAT_EVRC); + if (rc < 0) { + pr_err("T mode Open failed rc=%d\n", rc); + rc = -ENODEV; + goto fail; + } + audio->feedback = TUNNEL_MODE; + audio->buf_cfg.meta_info_enable = 0x00; + } else { + pr_err("Not supported mode\n"); + rc = -EACCES; + goto fail; + } + +#ifdef CONFIG_DEBUG_FS + snprintf(name, sizeof(name), "msm_evrc_%04x", audio->ac->session); + audio->dentry = debugfs_create_file(name, S_IFREG | 0444, + NULL, (void *)audio, + &audio_evrc_debug_fops); + + if (IS_ERR(audio->dentry)) + pr_debug("debugfs_create_file failed\n"); +#endif + pr_info("%s:dec success mode[%d]session[%d]\n", __func__, + audio->feedback, + audio->ac->session); + return rc; +fail: + q6asm_audio_client_free(audio->ac); + kfree(audio); + return rc; +} + +static const struct file_operations audio_evrc_fops = { + .owner = THIS_MODULE, + .open = audio_open, + .release = audio_aio_release, + .unlocked_ioctl = audio_ioctl, + .fsync = audio_aio_fsync, +}; + +static struct miscdevice audio_evrc_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_evrc", + .fops = &audio_evrc_fops, +}; + +int __init audio_evrc_init(void) +{ + int ret = misc_register(&audio_evrc_misc); + + if (ret == 0) + device_init_wakeup(audio_evrc_misc.this_device, true); + audio_evrc_ws_mgr.ref_cnt = 0; + mutex_init(&audio_evrc_ws_mgr.ws_lock); + + return ret; +} + +void audio_evrc_exit(void) +{ + mutex_destroy(&audio_evrc_ws_mgr.ws_lock); + misc_deregister(&audio_evrc_misc); +} diff --git a/audio-kernel/dsp/codecs/audio_g711alaw.c b/audio-kernel/dsp/codecs/audio_g711alaw.c new file mode 100644 index 000000000..6382616fa --- /dev/null +++ b/audio-kernel/dsp/codecs/audio_g711alaw.c @@ -0,0 +1,394 @@ +/* Copyright (c) 2016-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 + * 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 +#include +#include +#include "audio_utils_aio.h" + +static struct miscdevice audio_g711alaw_misc; +static struct ws_mgr audio_g711_ws_mgr; + +static const struct file_operations audio_g711_debug_fops = { + .read = audio_aio_debug_read, + .open = audio_aio_debug_open, +}; + +static struct dentry *config_debugfs_create_file(const char *name, void *data) +{ + return debugfs_create_file(name, S_IFREG | 0444, + NULL, (void *)data, &audio_g711_debug_fops); +} + +static int g711_channel_map(u8 *channel_mapping, uint32_t channels); + +static long audio_ioctl_shared(struct file *file, unsigned int cmd, + void *arg) +{ + struct q6audio_aio *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_START: { + struct asm_g711_dec_cfg g711_dec_cfg; + struct msm_audio_g711_dec_config *g711_dec_config; + u8 channel_mapping[PCM_FORMAT_MAX_NUM_CHANNEL]; + + memset(channel_mapping, 0, PCM_FORMAT_MAX_NUM_CHANNEL); + memset(&g711_dec_cfg, 0, sizeof(g711_dec_cfg)); + + if (g711_channel_map(channel_mapping, + audio->pcm_cfg.channel_count)) { + pr_err("%s: setting channel map failed %d\n", + __func__, audio->pcm_cfg.channel_count); + } + + pr_debug("%s[%pK]: AUDIO_START session_id[%d]\n", __func__, + audio, audio->ac->session); + if (audio->feedback == NON_TUNNEL_MODE) { + /* Configure PCM output block */ + rc = q6asm_enc_cfg_blk_pcm_v2(audio->ac, + audio->pcm_cfg.sample_rate, + audio->pcm_cfg.channel_count, + 16, /*bits per sample*/ + false, false, channel_mapping); + if (rc < 0) { + pr_err("%s: pcm output block config failed rc=%d\n", + __func__, rc); + break; + } + } + g711_dec_config = + (struct msm_audio_g711_dec_config *)audio->codec_cfg; + g711_dec_cfg.sample_rate = g711_dec_config->sample_rate; + /* Configure Media format block */ + rc = q6asm_media_format_block_g711(audio->ac, &g711_dec_cfg, + audio->ac->stream_id); + if (rc < 0) { + pr_err("%s: cmd media format block failed rc=%d\n", + __func__, rc); + break; + } + rc = audio_aio_enable(audio); + audio->eos_rsp = 0; + audio->eos_flag = 0; + if (!rc) { + audio->enabled = 1; + } else { + audio->enabled = 0; + pr_err("%s: Audio Start procedure failed rc=%d\n", + __func__, rc); + break; + } + pr_debug("%s: AUDIO_START success enable[%d]\n", + __func__, audio->enabled); + if (audio->stopped == 1) + audio->stopped = 0; + break; + } + default: + pr_debug("%s: Unknown ioctl cmd = %d", __func__, cmd); + break; + } + return rc; +} + +static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct q6audio_aio *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_START: { + rc = audio_ioctl_shared(file, cmd, (void *)arg); + break; + } + case AUDIO_GET_G711_DEC_CONFIG: { + if (copy_to_user((void *)arg, audio->codec_cfg, + sizeof(struct msm_audio_g711_dec_config))) { + pr_err("%s: copy_to_user for AUDIO_GET_G711_DEC_CONFIG failed\n", + __func__); + rc = -EFAULT; + } + break; + } + case AUDIO_SET_G711_DEC_CONFIG: { + if (copy_from_user(audio->codec_cfg, (void *)arg, + sizeof(struct msm_audio_g711_dec_config))) { + pr_err("%s: copy_from_user for AUDIO_SET_G711_DEC_CONFIG failed\n", + __func__); + rc = -EFAULT; + } + break; + } + default: { + rc = audio->codec_ioctl(file, cmd, arg); + if (rc) + pr_err("%s: Failed in audio_aio_ioctl: %d cmd=%d\n", + __func__, rc, cmd); + break; + } + } + return rc; +} + +#ifdef CONFIG_COMPAT +struct msm_audio_g711_dec_config_32 { + u32 sample_rate; +}; + +enum { + AUDIO_SET_G711_DEC_CONFIG_32 = _IOW(AUDIO_IOCTL_MAGIC, + (AUDIO_MAX_COMMON_IOCTL_NUM+0), struct msm_audio_g711_dec_config_32), + AUDIO_GET_G711_DEC_CONFIG_32 = _IOR(AUDIO_IOCTL_MAGIC, + (AUDIO_MAX_COMMON_IOCTL_NUM+1), struct msm_audio_g711_dec_config_32) +}; + +static long audio_compat_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct q6audio_aio *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_START: { + rc = audio_ioctl_shared(file, cmd, (void *)arg); + break; + } + case AUDIO_GET_G711_DEC_CONFIG_32: { + struct msm_audio_g711_dec_config *g711_dec_config; + struct msm_audio_g711_dec_config_32 g711_dec_config_32; + + memset(&g711_dec_config_32, 0, sizeof(g711_dec_config_32)); + + g711_dec_config = + (struct msm_audio_g711_dec_config *)audio->codec_cfg; + g711_dec_config_32.sample_rate = g711_dec_config->sample_rate; + + if (copy_to_user((void *)arg, &g711_dec_config_32, + sizeof(g711_dec_config_32))) { + pr_err("%s: copy_to_user for AUDIO_GET_G711_DEC_CONFIG_32 failed\n", + __func__); + rc = -EFAULT; + } + break; + } + case AUDIO_SET_G711_DEC_CONFIG_32: { + struct msm_audio_g711_dec_config *g711_dec_config; + struct msm_audio_g711_dec_config_32 g711_dec_config_32; + + memset(&g711_dec_config_32, 0, sizeof(g711_dec_config_32)); + + if (copy_from_user(&g711_dec_config_32, (void *)arg, + sizeof(g711_dec_config_32))) { + pr_err("%s: copy_from_user for AUDIO_SET_G711_DEC_CONFIG_32 failed\n", + __func__); + rc = -EFAULT; + break; + } + + g711_dec_config = + (struct msm_audio_g711_dec_config *)audio->codec_cfg; + g711_dec_config->sample_rate = g711_dec_config_32.sample_rate; + + break; + } + default: { + rc = audio->codec_compat_ioctl(file, cmd, arg); + if (rc) + pr_err("%s: Failed in audio_aio_compat_ioctl: %d cmd=%d\n", + __func__, rc, cmd); + break; + } + } + return rc; +} +#else +#define audio_compat_ioctl NULL +#endif + +static int audio_open(struct inode *inode, struct file *file) +{ + struct q6audio_aio *audio = NULL; + int rc = 0; + /* 4 bytes represents decoder number, 1 byte for terminate string */ + char name[sizeof "msm_g711_" + 5]; + + audio = kzalloc(sizeof(struct q6audio_aio), GFP_KERNEL); + + if (!audio) + return -ENOMEM; + audio->codec_cfg = kzalloc(sizeof(struct msm_audio_g711_dec_config), + GFP_KERNEL); + if (!audio->codec_cfg) { + kfree(audio); + return -ENOMEM; + } + + audio->pcm_cfg.buffer_size = PCM_BUFSZ_MIN; + audio->miscdevice = &audio_g711alaw_misc; + audio->wakelock_voted = false; + audio->audio_ws_mgr = &audio_g711_ws_mgr; + + init_waitqueue_head(&audio->event_wait); + + audio->ac = q6asm_audio_client_alloc((app_cb) q6_audio_cb, + (void *)audio); + + if (!audio->ac) { + pr_err("%s: Could not allocate memory for audio client\n", + __func__); + kfree(audio->codec_cfg); + kfree(audio); + return -ENOMEM; + } + rc = audio_aio_open(audio, file); + if (rc < 0) { + pr_err("%s: audio_aio_open rc=%d\n", + __func__, rc); + goto fail; + } + /* open in T/NT mode */ /*foramt:G711_ALAW*/ + if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) { + rc = q6asm_open_read_write(audio->ac, FORMAT_LINEAR_PCM, + FORMAT_G711_ALAW_FS); + if (rc < 0) { + pr_err("%s: NT mode Open failed rc=%d\n", __func__, rc); + goto fail; + } + audio->feedback = NON_TUNNEL_MODE; + /* open G711 decoder, expected frames is always 1*/ + audio->buf_cfg.frames_per_buf = 0x01; + audio->buf_cfg.meta_info_enable = 0x01; + } else if ((file->f_mode & FMODE_WRITE) && + !(file->f_mode & FMODE_READ)) { + rc = q6asm_open_write(audio->ac, FORMAT_G711_ALAW_FS); + if (rc < 0) { + pr_err("%s: T mode Open failed rc=%d\n", __func__, rc); + goto fail; + } + audio->feedback = TUNNEL_MODE; + audio->buf_cfg.meta_info_enable = 0x00; + } else { + pr_err("%s: %d mode is not supported mode\n", + __func__, file->f_mode); + rc = -EACCES; + goto fail; + } + + snprintf(name, sizeof(name), "msm_g711_%04x", audio->ac->session); + audio->dentry = config_debugfs_create_file(name, (void *)audio); + + if (IS_ERR_OR_NULL(audio->dentry)) + pr_debug("%s: debugfs_create_file failed\n", __func__); + pr_debug("%s: g711dec success mode[%d]session[%d]\n", __func__, + audio->feedback, + audio->ac->session); + return rc; +fail: + q6asm_audio_client_free(audio->ac); + kfree(audio->codec_cfg); + kfree(audio); + return rc; +} + +static int g711_channel_map(u8 *channel_mapping, uint32_t channels) +{ + u8 *lchannel_mapping; + + lchannel_mapping = channel_mapping; + pr_debug("%s: channels passed: %d\n", __func__, channels); + if (channels == 1) { + lchannel_mapping[0] = PCM_CHANNEL_FC; + } else if (channels == 2) { + lchannel_mapping[0] = PCM_CHANNEL_FL; + lchannel_mapping[1] = PCM_CHANNEL_FR; + } else if (channels == 3) { + lchannel_mapping[0] = PCM_CHANNEL_FC; + lchannel_mapping[1] = PCM_CHANNEL_FL; + lchannel_mapping[2] = PCM_CHANNEL_FR; + } else if (channels == 4) { + lchannel_mapping[0] = PCM_CHANNEL_FC; + lchannel_mapping[1] = PCM_CHANNEL_FL; + lchannel_mapping[2] = PCM_CHANNEL_FR; + lchannel_mapping[3] = PCM_CHANNEL_CS; + } else if (channels == 5) { + lchannel_mapping[0] = PCM_CHANNEL_FC; + lchannel_mapping[1] = PCM_CHANNEL_FL; + lchannel_mapping[2] = PCM_CHANNEL_FR; + lchannel_mapping[3] = PCM_CHANNEL_LS; + lchannel_mapping[4] = PCM_CHANNEL_RS; + } else if (channels == 6) { + lchannel_mapping[0] = PCM_CHANNEL_FC; + lchannel_mapping[1] = PCM_CHANNEL_FL; + lchannel_mapping[2] = PCM_CHANNEL_FR; + lchannel_mapping[3] = PCM_CHANNEL_LS; + lchannel_mapping[4] = PCM_CHANNEL_RS; + lchannel_mapping[5] = PCM_CHANNEL_LFE; + } else if (channels == 7) { + lchannel_mapping[0] = PCM_CHANNEL_FC; + lchannel_mapping[1] = PCM_CHANNEL_FL; + lchannel_mapping[2] = PCM_CHANNEL_FR; + lchannel_mapping[3] = PCM_CHANNEL_LS; + lchannel_mapping[4] = PCM_CHANNEL_RS; + lchannel_mapping[5] = PCM_CHANNEL_CS; + lchannel_mapping[6] = PCM_CHANNEL_LFE; + } else if (channels == 8) { + lchannel_mapping[0] = PCM_CHANNEL_FC; + lchannel_mapping[1] = PCM_CHANNEL_FLC; + lchannel_mapping[2] = PCM_CHANNEL_FRC; + lchannel_mapping[3] = PCM_CHANNEL_FL; + lchannel_mapping[4] = PCM_CHANNEL_FR; + lchannel_mapping[5] = PCM_CHANNEL_LS; + lchannel_mapping[6] = PCM_CHANNEL_RS; + lchannel_mapping[7] = PCM_CHANNEL_LFE; + } else { + pr_err("%s: ERROR.unsupported num_ch = %u\n", + __func__, channels); + return -EINVAL; + } + return 0; +} + +static const struct file_operations audio_g711_fops = { + .owner = THIS_MODULE, + .open = audio_open, + .release = audio_aio_release, + .unlocked_ioctl = audio_ioctl, + .compat_ioctl = audio_compat_ioctl, + .fsync = audio_aio_fsync, +}; + +static struct miscdevice audio_g711alaw_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_g711alaw", + .fops = &audio_g711_fops, +}; + +int __init audio_g711alaw_init(void) +{ + int ret = misc_register(&audio_g711alaw_misc); + + if (ret == 0) + device_init_wakeup(audio_g711alaw_misc.this_device, true); + audio_g711_ws_mgr.ref_cnt = 0; + mutex_init(&audio_g711_ws_mgr.ws_lock); + + return ret; +} + +void audio_g711alaw_exit(void) +{ + mutex_destroy(&audio_g711_ws_mgr.ws_lock); + misc_deregister(&audio_g711alaw_misc); +} diff --git a/audio-kernel/dsp/codecs/audio_g711mlaw.c b/audio-kernel/dsp/codecs/audio_g711mlaw.c new file mode 100644 index 000000000..9994aabc9 --- /dev/null +++ b/audio-kernel/dsp/codecs/audio_g711mlaw.c @@ -0,0 +1,394 @@ +/* Copyright (c) 2016-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 + * 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 +#include +#include +#include "audio_utils_aio.h" + +static struct miscdevice audio_g711mlaw_misc; +static struct ws_mgr audio_g711_ws_mgr; + +static const struct file_operations audio_g711_debug_fops = { + .read = audio_aio_debug_read, + .open = audio_aio_debug_open, +}; + +static struct dentry *config_debugfs_create_file(const char *name, void *data) +{ + return debugfs_create_file(name, S_IFREG | 0444, + NULL, (void *)data, &audio_g711_debug_fops); +} + +static int g711_channel_map(u8 *channel_mapping, uint32_t channels); + +static long audio_ioctl_shared(struct file *file, unsigned int cmd, + void *arg) +{ + struct q6audio_aio *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_START: { + struct asm_g711_dec_cfg g711_dec_cfg; + struct msm_audio_g711_dec_config *g711_dec_config; + u8 channel_mapping[PCM_FORMAT_MAX_NUM_CHANNEL]; + + memset(channel_mapping, 0, PCM_FORMAT_MAX_NUM_CHANNEL); + memset(&g711_dec_cfg, 0, sizeof(g711_dec_cfg)); + + if (g711_channel_map(channel_mapping, + audio->pcm_cfg.channel_count)) { + pr_err("%s: setting channel map failed %d\n", + __func__, audio->pcm_cfg.channel_count); + } + + pr_debug("%s[%pK]: AUDIO_START session_id[%d]\n", __func__, + audio, audio->ac->session); + if (audio->feedback == NON_TUNNEL_MODE) { + /* Configure PCM output block */ + rc = q6asm_enc_cfg_blk_pcm_v2(audio->ac, + audio->pcm_cfg.sample_rate, + audio->pcm_cfg.channel_count, + 16, /*bits per sample*/ + false, false, channel_mapping); + if (rc < 0) { + pr_err("%s: pcm output block config failed rc=%d\n", + __func__, rc); + break; + } + } + g711_dec_config = + (struct msm_audio_g711_dec_config *)audio->codec_cfg; + g711_dec_cfg.sample_rate = g711_dec_config->sample_rate; + /* Configure Media format block */ + rc = q6asm_media_format_block_g711(audio->ac, &g711_dec_cfg, + audio->ac->stream_id); + if (rc < 0) { + pr_err("%s: cmd media format block failed rc=%d\n", + __func__, rc); + break; + } + rc = audio_aio_enable(audio); + audio->eos_rsp = 0; + audio->eos_flag = 0; + if (!rc) { + audio->enabled = 1; + } else { + audio->enabled = 0; + pr_err("%s: Audio Start procedure failed rc=%d\n", + __func__, rc); + break; + } + pr_debug("%s: AUDIO_START success enable[%d]\n", + __func__, audio->enabled); + if (audio->stopped == 1) + audio->stopped = 0; + break; + } + default: + pr_debug("%s: Unknown ioctl cmd = %d", __func__, cmd); + break; + } + return rc; +} + +static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct q6audio_aio *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_START: { + rc = audio_ioctl_shared(file, cmd, (void *)arg); + break; + } + case AUDIO_GET_G711_DEC_CONFIG: { + if (copy_to_user((void *)arg, audio->codec_cfg, + sizeof(struct msm_audio_g711_dec_config))) { + pr_err("%s: AUDIO_GET_G711_DEC_CONFIG failed\n", + __func__); + rc = -EFAULT; + } + break; + } + case AUDIO_SET_G711_DEC_CONFIG: { + if (copy_from_user(audio->codec_cfg, (void *)arg, + sizeof(struct msm_audio_g711_dec_config))) { + pr_err("%s: AUDIO_SET_G711_DEC_CONFIG failed\n", + __func__); + rc = -EFAULT; + } + break; + } + default: { + rc = audio->codec_ioctl(file, cmd, arg); + if (rc) + pr_err("%s: Failed in audio_aio_ioctl: %d cmd=%d\n", + __func__, rc, cmd); + break; + } + } + return rc; +} + +#ifdef CONFIG_COMPAT +struct msm_audio_g711_dec_config_32 { + u32 sample_rate; +}; + +enum { + AUDIO_SET_G711_DEC_CONFIG_32 = _IOW(AUDIO_IOCTL_MAGIC, + (AUDIO_MAX_COMMON_IOCTL_NUM+0), struct msm_audio_g711_dec_config_32), + AUDIO_GET_G711_DEC_CONFIG_32 = _IOR(AUDIO_IOCTL_MAGIC, + (AUDIO_MAX_COMMON_IOCTL_NUM+1), struct msm_audio_g711_dec_config_32) +}; + +static long audio_compat_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct q6audio_aio *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_START: { + rc = audio_ioctl_shared(file, cmd, (void *)arg); + break; + } + case AUDIO_GET_G711_DEC_CONFIG_32: { + struct msm_audio_g711_dec_config *g711_dec_config; + struct msm_audio_g711_dec_config_32 g711_dec_config_32; + + memset(&g711_dec_config_32, 0, sizeof(g711_dec_config_32)); + + g711_dec_config = + (struct msm_audio_g711_dec_config *)audio->codec_cfg; + g711_dec_config_32.sample_rate = g711_dec_config->sample_rate; + + if (copy_to_user((void *)arg, &g711_dec_config_32, + sizeof(g711_dec_config_32))) { + pr_err("%s: copy_to_user for AUDIO_GET_G711_DEC_CONFIG failed\n", + __func__); + rc = -EFAULT; + } + break; + } + case AUDIO_SET_G711_DEC_CONFIG_32: { + struct msm_audio_g711_dec_config *g711_dec_config; + struct msm_audio_g711_dec_config_32 g711_dec_config_32; + + memset(&g711_dec_config_32, 0, sizeof(g711_dec_config_32)); + + if (copy_from_user(&g711_dec_config_32, (void *)arg, + sizeof(g711_dec_config_32))) { + pr_err("%s: copy_from_user for AUDIO_SET_G711_DEC_CONFIG failed\n", + __func__); + rc = -EFAULT; + break; + } + g711_dec_config = + (struct msm_audio_g711_dec_config *)audio->codec_cfg; + g711_dec_config->sample_rate = g711_dec_config_32.sample_rate; + + break; + } + default: { + rc = audio->codec_compat_ioctl(file, cmd, arg); + if (rc) + pr_err("%s: Failed in audio_aio_compat_ioctl: %d cmd=%d\n", + __func__, rc, cmd); + break; + } + } + return rc; +} +#else +#define audio_compat_ioctl NULL +#endif + +static int audio_open(struct inode *inode, struct file *file) +{ + struct q6audio_aio *audio = NULL; + int rc = 0; + /* 4 bytes represents decoder number, 1 byte for terminate string */ + char name[sizeof "msm_g711_" + 5]; + + audio = kzalloc(sizeof(struct q6audio_aio), GFP_KERNEL); + + if (!audio) + return -ENOMEM; + audio->codec_cfg = kzalloc(sizeof(struct msm_audio_g711_dec_config), + GFP_KERNEL); + if (!audio->codec_cfg) { + kfree(audio); + return -ENOMEM; + } + + audio->pcm_cfg.buffer_size = PCM_BUFSZ_MIN; + audio->miscdevice = &audio_g711mlaw_misc; + audio->wakelock_voted = false; + audio->audio_ws_mgr = &audio_g711_ws_mgr; + + init_waitqueue_head(&audio->event_wait); + + audio->ac = q6asm_audio_client_alloc((app_cb) q6_audio_cb, + (void *)audio); + + if (!audio->ac) { + pr_err("%s: Could not allocate memory for audio client\n", + __func__); + kfree(audio->codec_cfg); + kfree(audio); + return -ENOMEM; + } + rc = audio_aio_open(audio, file); + if (rc < 0) { + pr_err("%s: audio_aio_open rc=%d\n", + __func__, rc); + goto fail; + } + /* open in T/NT mode */ /*foramt:G711_ALAW*/ + if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) { + rc = q6asm_open_read_write(audio->ac, FORMAT_LINEAR_PCM, + FORMAT_G711_MLAW_FS); + if (rc < 0) { + pr_err("%s: NT mode Open failed rc=%d\n", __func__, rc); + goto fail; + } + audio->feedback = NON_TUNNEL_MODE; + /* open G711 decoder, expected frames is always 1*/ + audio->buf_cfg.frames_per_buf = 0x01; + audio->buf_cfg.meta_info_enable = 0x01; + } else if ((file->f_mode & FMODE_WRITE) && + !(file->f_mode & FMODE_READ)) { + rc = q6asm_open_write(audio->ac, FORMAT_G711_MLAW_FS); + if (rc < 0) { + pr_err("%s: T mode Open failed rc=%d\n", __func__, rc); + goto fail; + } + audio->feedback = TUNNEL_MODE; + audio->buf_cfg.meta_info_enable = 0x00; + } else { + pr_err("%s: %d mode is not supported\n", __func__, + file->f_mode); + rc = -EACCES; + goto fail; + } + + snprintf(name, sizeof(name), "msm_g711_%04x", audio->ac->session); + audio->dentry = config_debugfs_create_file(name, (void *)audio); + + if (IS_ERR_OR_NULL(audio->dentry)) + pr_debug("%s: debugfs_create_file failed\n", __func__); + pr_debug("%s: g711dec success mode[%d]session[%d]\n", __func__, + audio->feedback, + audio->ac->session); + return rc; +fail: + q6asm_audio_client_free(audio->ac); + kfree(audio->codec_cfg); + kfree(audio); + return rc; +} + +static int g711_channel_map(u8 *channel_mapping, uint32_t channels) +{ + u8 *lchannel_mapping; + + lchannel_mapping = channel_mapping; + pr_debug("%s: channels passed: %d\n", __func__, channels); + if (channels == 1) { + lchannel_mapping[0] = PCM_CHANNEL_FC; + } else if (channels == 2) { + lchannel_mapping[0] = PCM_CHANNEL_FL; + lchannel_mapping[1] = PCM_CHANNEL_FR; + } else if (channels == 3) { + lchannel_mapping[0] = PCM_CHANNEL_FC; + lchannel_mapping[1] = PCM_CHANNEL_FL; + lchannel_mapping[2] = PCM_CHANNEL_FR; + } else if (channels == 4) { + lchannel_mapping[0] = PCM_CHANNEL_FC; + lchannel_mapping[1] = PCM_CHANNEL_FL; + lchannel_mapping[2] = PCM_CHANNEL_FR; + lchannel_mapping[3] = PCM_CHANNEL_CS; + } else if (channels == 5) { + lchannel_mapping[0] = PCM_CHANNEL_FC; + lchannel_mapping[1] = PCM_CHANNEL_FL; + lchannel_mapping[2] = PCM_CHANNEL_FR; + lchannel_mapping[3] = PCM_CHANNEL_LS; + lchannel_mapping[4] = PCM_CHANNEL_RS; + } else if (channels == 6) { + lchannel_mapping[0] = PCM_CHANNEL_FC; + lchannel_mapping[1] = PCM_CHANNEL_FL; + lchannel_mapping[2] = PCM_CHANNEL_FR; + lchannel_mapping[3] = PCM_CHANNEL_LS; + lchannel_mapping[4] = PCM_CHANNEL_RS; + lchannel_mapping[5] = PCM_CHANNEL_LFE; + } else if (channels == 7) { + lchannel_mapping[0] = PCM_CHANNEL_FC; + lchannel_mapping[1] = PCM_CHANNEL_FL; + lchannel_mapping[2] = PCM_CHANNEL_FR; + lchannel_mapping[3] = PCM_CHANNEL_LS; + lchannel_mapping[4] = PCM_CHANNEL_RS; + lchannel_mapping[5] = PCM_CHANNEL_CS; + lchannel_mapping[6] = PCM_CHANNEL_LFE; + } else if (channels == 8) { + lchannel_mapping[0] = PCM_CHANNEL_FC; + lchannel_mapping[1] = PCM_CHANNEL_FLC; + lchannel_mapping[2] = PCM_CHANNEL_FRC; + lchannel_mapping[3] = PCM_CHANNEL_FL; + lchannel_mapping[4] = PCM_CHANNEL_FR; + lchannel_mapping[5] = PCM_CHANNEL_LS; + lchannel_mapping[6] = PCM_CHANNEL_RS; + lchannel_mapping[7] = PCM_CHANNEL_LFE; + } else { + pr_err("%s: ERROR.unsupported num_ch = %u\n", + __func__, channels); + return -EINVAL; + } + return 0; +} + +static const struct file_operations audio_g711_fops = { + .owner = THIS_MODULE, + .open = audio_open, + .release = audio_aio_release, + .unlocked_ioctl = audio_ioctl, + .compat_ioctl = audio_compat_ioctl, + .fsync = audio_aio_fsync, +}; + +static struct miscdevice audio_g711mlaw_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_g711mlaw", + .fops = &audio_g711_fops, +}; + +int __init audio_g711mlaw_init(void) +{ + int ret = misc_register(&audio_g711mlaw_misc); + + if (ret == 0) + device_init_wakeup(audio_g711mlaw_misc.this_device, true); + audio_g711_ws_mgr.ref_cnt = 0; + mutex_init(&audio_g711_ws_mgr.ws_lock); + + return ret; +} + +void audio_g711mlaw_exit(void) +{ + mutex_destroy(&audio_g711_ws_mgr.ws_lock); + misc_deregister(&audio_g711mlaw_misc); +} + diff --git a/audio-kernel/dsp/codecs/audio_hwacc_effects.c b/audio-kernel/dsp/codecs/audio_hwacc_effects.c new file mode 100644 index 000000000..f66164bfc --- /dev/null +++ b/audio-kernel/dsp/codecs/audio_hwacc_effects.c @@ -0,0 +1,781 @@ +/* + * Copyright (c) 2014-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 + * 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 +#include +#include "q6audio_common.h" +#include +#include "audio_utils_aio.h" + +#define MAX_CHANNELS_SUPPORTED 8 +#define WAIT_TIMEDOUT_DURATION_SECS 1 + +struct q6audio_effects { + wait_queue_head_t read_wait; + wait_queue_head_t write_wait; + + struct audio_client *ac; + struct msm_hwacc_effects_config config; + + struct mutex lock; + + atomic_t in_count; + atomic_t out_count; + + int opened; + int started; + int buf_alloc; + struct msm_nt_eff_all_config audio_effects; +}; + +static void audio_effects_init_pp(struct audio_client *ac) +{ + int ret = 0; + struct asm_softvolume_params softvol = { + .period = SOFT_VOLUME_PERIOD, + .step = SOFT_VOLUME_STEP, + .rampingcurve = SOFT_VOLUME_CURVE_LINEAR, + }; + + if (!ac) { + pr_err("%s: audio client null to init pp\n", __func__); + return; + } + ret = q6asm_set_softvolume_v2(ac, &softvol, + SOFT_VOLUME_INSTANCE_1); + if (ret < 0) + pr_err("%s: Send SoftVolume Param failed ret=%d\n", + __func__, ret); +} + +static void audio_effects_deinit_pp(struct audio_client *ac) +{ + if (!ac) { + pr_err("%s: audio client null to deinit pp\n", __func__); + return; + } +} + +static void audio_effects_event_handler(uint32_t opcode, uint32_t token, + uint32_t *payload, void *priv) +{ + struct q6audio_effects *effects; + + if (!payload || !priv) { + pr_err("%s: invalid data to handle events, payload: %pK, priv: %pK\n", + __func__, payload, priv); + return; + } + + effects = (struct q6audio_effects *)priv; + switch (opcode) { + case ASM_DATA_EVENT_WRITE_DONE_V2: { + atomic_inc(&effects->out_count); + wake_up(&effects->write_wait); + break; + } + case ASM_DATA_EVENT_READ_DONE_V2: { + atomic_inc(&effects->in_count); + wake_up(&effects->read_wait); + break; + } + case APR_BASIC_RSP_RESULT: { + pr_debug("%s: APR_BASIC_RSP_RESULT Cmd[0x%x] Status[0x%x]\n", + __func__, payload[0], payload[1]); + switch (payload[0]) { + case ASM_SESSION_CMD_RUN_V2: + pr_debug("ASM_SESSION_CMD_RUN_V2\n"); + break; + default: + pr_debug("%s: Payload = [0x%x] stat[0x%x]\n", + __func__, payload[0], payload[1]); + break; + } + break; + } + default: + pr_debug("%s: Unhandled Event 0x%x token = 0x%x\n", + __func__, opcode, token); + break; + } +} + +static int audio_effects_shared_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct q6audio_effects *effects = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_START: { + pr_debug("%s: AUDIO_START\n", __func__); + + mutex_lock(&effects->lock); + + rc = q6asm_open_read_write_v2(effects->ac, + FORMAT_LINEAR_PCM, + FORMAT_MULTI_CHANNEL_LINEAR_PCM, + effects->config.meta_mode_enabled, + effects->config.output.bits_per_sample, + true /*overwrite topology*/, + ASM_STREAM_POSTPROC_TOPO_ID_HPX_MASTER); + if (rc < 0) { + pr_err("%s: Open failed for hw accelerated effects:rc=%d\n", + __func__, rc); + rc = -EINVAL; + mutex_unlock(&effects->lock); + goto ioctl_fail; + } + effects->opened = 1; + + pr_debug("%s: dec buf size: %d, num_buf: %d, enc buf size: %d, num_buf: %d\n", + __func__, effects->config.output.buf_size, + effects->config.output.num_buf, + effects->config.input.buf_size, + effects->config.input.num_buf); + rc = q6asm_audio_client_buf_alloc_contiguous(IN, effects->ac, + effects->config.output.buf_size, + effects->config.output.num_buf); + if (rc < 0) { + pr_err("%s: Write buffer Allocation failed rc = %d\n", + __func__, rc); + rc = -ENOMEM; + mutex_unlock(&effects->lock); + goto ioctl_fail; + } + atomic_set(&effects->in_count, effects->config.input.num_buf); + rc = q6asm_audio_client_buf_alloc_contiguous(OUT, effects->ac, + effects->config.input.buf_size, + effects->config.input.num_buf); + if (rc < 0) { + pr_err("%s: Read buffer Allocation failed rc = %d\n", + __func__, rc); + rc = -ENOMEM; + goto readbuf_fail; + } + atomic_set(&effects->out_count, effects->config.output.num_buf); + effects->buf_alloc = 1; + + pr_debug("%s: enc: sample_rate: %d, num_channels: %d\n", + __func__, effects->config.input.sample_rate, + effects->config.input.num_channels); + rc = q6asm_enc_cfg_blk_pcm(effects->ac, + effects->config.input.sample_rate, + effects->config.input.num_channels); + if (rc < 0) { + pr_err("%s: pcm read block config failed\n", __func__); + rc = -EINVAL; + goto cfg_fail; + } + pr_debug("%s: dec: sample_rate: %d, num_channels: %d, bit_width: %d\n", + __func__, effects->config.output.sample_rate, + effects->config.output.num_channels, + effects->config.output.bits_per_sample); + rc = q6asm_media_format_block_pcm_format_support( + effects->ac, effects->config.output.sample_rate, + effects->config.output.num_channels, + effects->config.output.bits_per_sample); + if (rc < 0) { + pr_err("%s: pcm write format block config failed\n", + __func__); + rc = -EINVAL; + goto cfg_fail; + } + + audio_effects_init_pp(effects->ac); + + rc = q6asm_run(effects->ac, 0x00, 0x00, 0x00); + if (!rc) + effects->started = 1; + else { + effects->started = 0; + pr_err("%s: ASM run state failed\n", __func__); + } + mutex_unlock(&effects->lock); + break; + } + case AUDIO_EFFECTS_WRITE: { + char *bufptr = NULL; + uint32_t idx = 0; + uint32_t size = 0; + + mutex_lock(&effects->lock); + + if (!effects->started) { + rc = -EFAULT; + mutex_unlock(&effects->lock); + goto ioctl_fail; + } + + rc = wait_event_timeout(effects->write_wait, + atomic_read(&effects->out_count), + WAIT_TIMEDOUT_DURATION_SECS * HZ); + if (!rc) { + pr_err("%s: write wait_event_timeout\n", __func__); + rc = -EFAULT; + mutex_unlock(&effects->lock); + goto ioctl_fail; + } + if (!atomic_read(&effects->out_count)) { + pr_err("%s: pcm stopped out_count 0\n", __func__); + rc = -EFAULT; + mutex_unlock(&effects->lock); + goto ioctl_fail; + } + + bufptr = q6asm_is_cpu_buf_avail(IN, effects->ac, &size, &idx); + if (bufptr) { + if ((effects->config.buf_cfg.output_len > size) || + copy_from_user(bufptr, (void *)arg, + effects->config.buf_cfg.output_len)) { + rc = -EFAULT; + mutex_unlock(&effects->lock); + goto ioctl_fail; + } + rc = q6asm_write(effects->ac, + effects->config.buf_cfg.output_len, + 0, 0, NO_TIMESTAMP); + if (rc < 0) { + rc = -EFAULT; + mutex_unlock(&effects->lock); + goto ioctl_fail; + } + atomic_dec(&effects->out_count); + } else { + pr_err("%s: AUDIO_EFFECTS_WRITE: Buffer dropped\n", + __func__); + } + mutex_unlock(&effects->lock); + break; + } + case AUDIO_EFFECTS_READ: { + char *bufptr = NULL; + uint32_t idx = 0; + uint32_t size = 0; + + mutex_lock(&effects->lock); + + if (!effects->started) { + rc = -EFAULT; + mutex_unlock(&effects->lock); + goto ioctl_fail; + } + + atomic_set(&effects->in_count, 0); + + q6asm_read_v2(effects->ac, effects->config.buf_cfg.input_len); + /* Read might fail initially, don't error out */ + if (rc < 0) + pr_err("%s: read failed\n", __func__); + + rc = wait_event_timeout(effects->read_wait, + atomic_read(&effects->in_count), + WAIT_TIMEDOUT_DURATION_SECS * HZ); + if (!rc) { + pr_err("%s: read wait_event_timeout\n", __func__); + rc = -EFAULT; + mutex_unlock(&effects->lock); + goto ioctl_fail; + } + if (!atomic_read(&effects->in_count)) { + pr_err("%s: pcm stopped in_count 0\n", __func__); + rc = -EFAULT; + mutex_unlock(&effects->lock); + goto ioctl_fail; + } + + bufptr = q6asm_is_cpu_buf_avail(OUT, effects->ac, &size, &idx); + if (bufptr) { + if (!((void *)arg)) { + rc = -EFAULT; + mutex_unlock(&effects->lock); + goto ioctl_fail; + } + if ((effects->config.buf_cfg.input_len > size) || + copy_to_user((void *)arg, bufptr, + effects->config.buf_cfg.input_len)) { + rc = -EFAULT; + mutex_unlock(&effects->lock); + goto ioctl_fail; + } + } + mutex_unlock(&effects->lock); + break; + } + default: + pr_err("%s: Invalid effects config module\n", __func__); + rc = -EINVAL; + break; + } +ioctl_fail: + return rc; +readbuf_fail: + q6asm_audio_client_buf_free_contiguous(IN, + effects->ac); + mutex_unlock(&effects->lock); + return rc; +cfg_fail: + q6asm_audio_client_buf_free_contiguous(IN, + effects->ac); + q6asm_audio_client_buf_free_contiguous(OUT, + effects->ac); + effects->buf_alloc = 0; + mutex_unlock(&effects->lock); + return rc; +} + +static long audio_effects_set_pp_param(struct q6audio_effects *effects, + long *values) +{ + int rc = 0; + int effects_module = values[0]; + + switch (effects_module) { + case VIRTUALIZER_MODULE: + pr_debug("%s: VIRTUALIZER_MODULE\n", __func__); + if (msm_audio_effects_is_effmodule_supp_in_top( + effects_module, effects->ac->topology)) + msm_audio_effects_virtualizer_handler( + effects->ac, + &(effects->audio_effects.virtualizer), + (long *)&values[1]); + break; + case REVERB_MODULE: + pr_debug("%s: REVERB_MODULE\n", __func__); + if (msm_audio_effects_is_effmodule_supp_in_top( + effects_module, effects->ac->topology)) + msm_audio_effects_reverb_handler(effects->ac, + &(effects->audio_effects.reverb), + (long *)&values[1]); + break; + case BASS_BOOST_MODULE: + pr_debug("%s: BASS_BOOST_MODULE\n", __func__); + if (msm_audio_effects_is_effmodule_supp_in_top( + effects_module, effects->ac->topology)) + msm_audio_effects_bass_boost_handler( + effects->ac, + &(effects->audio_effects.bass_boost), + (long *)&values[1]); + break; + case PBE_MODULE: + pr_debug("%s: PBE_MODULE\n", __func__); + if (msm_audio_effects_is_effmodule_supp_in_top( + effects_module, effects->ac->topology)) + msm_audio_effects_pbe_handler( + effects->ac, + &(effects->audio_effects.pbe), + (long *)&values[1]); + break; + case EQ_MODULE: + pr_debug("%s: EQ_MODULE\n", __func__); + if (msm_audio_effects_is_effmodule_supp_in_top( + effects_module, effects->ac->topology)) + msm_audio_effects_popless_eq_handler( + effects->ac, + &(effects->audio_effects.equalizer), + (long *)&values[1]); + break; + case SOFT_VOLUME_MODULE: + pr_debug("%s: SA PLUS VOLUME_MODULE\n", __func__); + msm_audio_effects_volume_handler_v2(effects->ac, + &(effects->audio_effects.saplus_vol), + (long *)&values[1], SOFT_VOLUME_INSTANCE_1); + break; + case SOFT_VOLUME2_MODULE: + pr_debug("%s: TOPOLOGY SWITCH VOLUME MODULE\n", + __func__); + if (msm_audio_effects_is_effmodule_supp_in_top( + effects_module, effects->ac->topology)) + msm_audio_effects_volume_handler_v2(effects->ac, + &(effects->audio_effects.topo_switch_vol), + (long *)&values[1], SOFT_VOLUME_INSTANCE_2); + break; + default: + pr_err("%s: Invalid effects config module\n", __func__); + rc = -EINVAL; + } + return rc; +} + +static long audio_effects_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct q6audio_effects *effects = file->private_data; + int rc = 0; + long argvalues[MAX_PP_PARAMS_SZ] = {0}; + + switch (cmd) { + case AUDIO_SET_EFFECTS_CONFIG: { + pr_debug("%s: AUDIO_SET_EFFECTS_CONFIG\n", __func__); + mutex_lock(&effects->lock); + memset(&effects->config, 0, sizeof(effects->config)); + if (copy_from_user(&effects->config, (void *)arg, + sizeof(effects->config))) { + pr_err("%s: copy from user for AUDIO_SET_EFFECTS_CONFIG failed\n", + __func__); + rc = -EFAULT; + } + pr_debug("%s: write buf_size: %d, num_buf: %d, sample_rate: %d, channel: %d\n", + __func__, effects->config.output.buf_size, + effects->config.output.num_buf, + effects->config.output.sample_rate, + effects->config.output.num_channels); + pr_debug("%s: read buf_size: %d, num_buf: %d, sample_rate: %d, channel: %d\n", + __func__, effects->config.input.buf_size, + effects->config.input.num_buf, + effects->config.input.sample_rate, + effects->config.input.num_channels); + mutex_unlock(&effects->lock); + break; + } + case AUDIO_EFFECTS_SET_BUF_LEN: { + mutex_lock(&effects->lock); + if (copy_from_user(&effects->config.buf_cfg, (void *)arg, + sizeof(effects->config.buf_cfg))) { + pr_err("%s: copy from user for AUDIO_EFFECTS_SET_BUF_LEN failed\n", + __func__); + rc = -EFAULT; + } + pr_debug("%s: write buf len: %d, read buf len: %d\n", + __func__, effects->config.buf_cfg.output_len, + effects->config.buf_cfg.input_len); + mutex_unlock(&effects->lock); + break; + } + case AUDIO_EFFECTS_GET_BUF_AVAIL: { + struct msm_hwacc_buf_avail buf_avail; + + buf_avail.input_num_avail = atomic_read(&effects->in_count); + buf_avail.output_num_avail = atomic_read(&effects->out_count); + mutex_lock(&effects->lock); + pr_debug("%s: write buf avail: %d, read buf avail: %d\n", + __func__, buf_avail.output_num_avail, + buf_avail.input_num_avail); + if (copy_to_user((void *)arg, &buf_avail, + sizeof(buf_avail))) { + pr_err("%s: copy to user for AUDIO_EFFECTS_GET_NUM_BUF_AVAIL failed\n", + __func__); + rc = -EFAULT; + } + mutex_unlock(&effects->lock); + break; + } + case AUDIO_EFFECTS_SET_PP_PARAMS: { + mutex_lock(&effects->lock); + if (copy_from_user(argvalues, (void *)arg, + MAX_PP_PARAMS_SZ*sizeof(long))) { + pr_err("%s: copy from user for pp params failed\n", + __func__); + mutex_unlock(&effects->lock); + return -EFAULT; + } + rc = audio_effects_set_pp_param(effects, argvalues); + mutex_unlock(&effects->lock); + break; + } + default: + pr_debug("%s: Calling shared ioctl\n", __func__); + rc = audio_effects_shared_ioctl(file, cmd, arg); + break; + } + if (rc) + pr_err("%s: cmd 0x%x failed\n", __func__, cmd); + return rc; +} + +#ifdef CONFIG_COMPAT +struct msm_hwacc_data_config32 { + __u32 buf_size; + __u32 num_buf; + __u32 num_channels; + __u8 channel_map[MAX_CHANNELS_SUPPORTED]; + __u32 sample_rate; + __u32 bits_per_sample; +}; + +struct msm_hwacc_buf_cfg32 { + __u32 input_len; + __u32 output_len; +}; + +struct msm_hwacc_buf_avail32 { + __u32 input_num_avail; + __u32 output_num_avail; +}; + +struct msm_hwacc_effects_config32 { + struct msm_hwacc_data_config32 input; + struct msm_hwacc_data_config32 output; + struct msm_hwacc_buf_cfg32 buf_cfg; + __u32 meta_mode_enabled; + __u32 overwrite_topology; + __s32 topology; +}; + +enum { + AUDIO_SET_EFFECTS_CONFIG32 = _IOW(AUDIO_IOCTL_MAGIC, 99, + struct msm_hwacc_effects_config32), + AUDIO_EFFECTS_SET_BUF_LEN32 = _IOW(AUDIO_IOCTL_MAGIC, 100, + struct msm_hwacc_buf_cfg32), + AUDIO_EFFECTS_GET_BUF_AVAIL32 = _IOW(AUDIO_IOCTL_MAGIC, 101, + struct msm_hwacc_buf_avail32), + AUDIO_EFFECTS_WRITE32 = _IOW(AUDIO_IOCTL_MAGIC, 102, compat_uptr_t), + AUDIO_EFFECTS_READ32 = _IOWR(AUDIO_IOCTL_MAGIC, 103, compat_uptr_t), + AUDIO_EFFECTS_SET_PP_PARAMS32 = _IOW(AUDIO_IOCTL_MAGIC, 104, + compat_uptr_t), + AUDIO_START32 = _IOW(AUDIO_IOCTL_MAGIC, 0, unsigned int), +}; + +static long audio_effects_compat_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct q6audio_effects *effects = file->private_data; + int rc = 0, i; + + switch (cmd) { + case AUDIO_SET_EFFECTS_CONFIG32: { + struct msm_hwacc_effects_config32 config32; + struct msm_hwacc_effects_config *config = &effects->config; + + mutex_lock(&effects->lock); + memset(&effects->config, 0, sizeof(effects->config)); + if (copy_from_user(&config32, (void *)arg, + sizeof(config32))) { + pr_err("%s: copy to user for AUDIO_SET_EFFECTS_CONFIG failed\n", + __func__); + rc = -EFAULT; + mutex_unlock(&effects->lock); + break; + } + config->input.buf_size = config32.input.buf_size; + config->input.num_buf = config32.input.num_buf; + config->input.num_channels = config32.input.num_channels; + config->input.sample_rate = config32.input.sample_rate; + config->input.bits_per_sample = config32.input.bits_per_sample; + config->input.buf_size = config32.input.buf_size; + for (i = 0; i < MAX_CHANNELS_SUPPORTED; i++) + config->input.channel_map[i] = + config32.input.channel_map[i]; + config->output.buf_size = config32.output.buf_size; + config->output.num_buf = config32.output.num_buf; + config->output.num_channels = config32.output.num_channels; + config->output.sample_rate = config32.output.sample_rate; + config->output.bits_per_sample = + config32.output.bits_per_sample; + config->output.buf_size = config32.output.buf_size; + for (i = 0; i < MAX_CHANNELS_SUPPORTED; i++) + config->output.channel_map[i] = + config32.output.channel_map[i]; + config->buf_cfg.input_len = config32.buf_cfg.input_len; + config->buf_cfg.output_len = config32.buf_cfg.output_len; + config->meta_mode_enabled = config32.meta_mode_enabled; + config->overwrite_topology = config32.overwrite_topology; + config->topology = config32.topology; + pr_debug("%s: write buf_size: %d, num_buf: %d, sample_rate: %d, channels: %d\n", + __func__, effects->config.output.buf_size, + effects->config.output.num_buf, + effects->config.output.sample_rate, + effects->config.output.num_channels); + pr_debug("%s: read buf_size: %d, num_buf: %d, sample_rate: %d, channels: %d\n", + __func__, effects->config.input.buf_size, + effects->config.input.num_buf, + effects->config.input.sample_rate, + effects->config.input.num_channels); + mutex_unlock(&effects->lock); + break; + } + case AUDIO_EFFECTS_SET_BUF_LEN32: { + struct msm_hwacc_buf_cfg32 buf_cfg32; + struct msm_hwacc_effects_config *config = &effects->config; + + mutex_lock(&effects->lock); + if (copy_from_user(&buf_cfg32, (void *)arg, + sizeof(buf_cfg32))) { + pr_err("%s: copy from user for AUDIO_EFFECTS_SET_BUF_LEN failed\n", + __func__); + rc = -EFAULT; + mutex_unlock(&effects->lock); + break; + } + config->buf_cfg.input_len = buf_cfg32.input_len; + config->buf_cfg.output_len = buf_cfg32.output_len; + pr_debug("%s: write buf len: %d, read buf len: %d\n", + __func__, effects->config.buf_cfg.output_len, + effects->config.buf_cfg.input_len); + mutex_unlock(&effects->lock); + break; + } + case AUDIO_EFFECTS_GET_BUF_AVAIL32: { + struct msm_hwacc_buf_avail32 buf_avail; + + memset(&buf_avail, 0, sizeof(buf_avail)); + + mutex_lock(&effects->lock); + buf_avail.input_num_avail = atomic_read(&effects->in_count); + buf_avail.output_num_avail = atomic_read(&effects->out_count); + pr_debug("%s: write buf avail: %d, read buf avail: %d\n", + __func__, buf_avail.output_num_avail, + buf_avail.input_num_avail); + if (copy_to_user((void *)arg, &buf_avail, + sizeof(buf_avail))) { + pr_err("%s: copy to user for AUDIO_EFFECTS_GET_NUM_BUF_AVAIL failed\n", + __func__); + rc = -EFAULT; + } + mutex_unlock(&effects->lock); + break; + } + case AUDIO_EFFECTS_SET_PP_PARAMS32: { + long argvalues[MAX_PP_PARAMS_SZ] = {0}; + int argvalues32[MAX_PP_PARAMS_SZ] = {0}; + + mutex_lock(&effects->lock); + if (copy_from_user(argvalues32, (void *)arg, + MAX_PP_PARAMS_SZ*sizeof(int))) { + pr_err("%s: copy from user failed for pp params\n", + __func__); + mutex_unlock(&effects->lock); + return -EFAULT; + } + for (i = 0; i < MAX_PP_PARAMS_SZ; i++) + argvalues[i] = argvalues32[i]; + + rc = audio_effects_set_pp_param(effects, argvalues); + mutex_unlock(&effects->lock); + break; + } + case AUDIO_START32: { + rc = audio_effects_shared_ioctl(file, AUDIO_START, arg); + break; + } + case AUDIO_EFFECTS_WRITE32: { + rc = audio_effects_shared_ioctl(file, AUDIO_EFFECTS_WRITE, arg); + break; + } + case AUDIO_EFFECTS_READ32: { + rc = audio_effects_shared_ioctl(file, AUDIO_EFFECTS_READ, arg); + break; + } + default: + pr_debug("%s: unhandled ioctl\n", __func__); + rc = -EINVAL; + break; + } + return rc; +} +#endif + +static int audio_effects_release(struct inode *inode, struct file *file) +{ + struct q6audio_effects *effects = file->private_data; + int rc = 0; + + if (!effects) { + pr_err("%s: effect is NULL\n", __func__); + return -EINVAL; + } + if (effects->opened) { + rc = wait_event_timeout(effects->write_wait, + atomic_read(&effects->out_count), + WAIT_TIMEDOUT_DURATION_SECS * HZ); + if (!rc) + pr_err("%s: write wait_event_timeout failed\n", + __func__); + rc = wait_event_timeout(effects->read_wait, + atomic_read(&effects->in_count), + WAIT_TIMEDOUT_DURATION_SECS * HZ); + if (!rc) + pr_err("%s: read wait_event_timeout failed\n", + __func__); + rc = q6asm_cmd(effects->ac, CMD_CLOSE); + if (rc < 0) + pr_err("%s[%pK]:Failed to close the session rc=%d\n", + __func__, effects, rc); + effects->opened = 0; + effects->started = 0; + + audio_effects_deinit_pp(effects->ac); + } + + if (effects->buf_alloc) { + q6asm_audio_client_buf_free_contiguous(IN, effects->ac); + q6asm_audio_client_buf_free_contiguous(OUT, effects->ac); + } + q6asm_audio_client_free(effects->ac); + + mutex_destroy(&effects->lock); + kfree(effects); + + pr_debug("%s: close session success\n", __func__); + return rc; +} + +static int audio_effects_open(struct inode *inode, struct file *file) +{ + struct q6audio_effects *effects; + int rc = 0; + + effects = kzalloc(sizeof(struct q6audio_effects), GFP_KERNEL); + if (!effects) + return -ENOMEM; + + effects->ac = q6asm_audio_client_alloc( + (app_cb)audio_effects_event_handler, + (void *)effects); + if (!effects->ac) { + pr_err("%s: Could not allocate memory for audio client\n", + __func__); + kfree(effects); + return -ENOMEM; + } + + init_waitqueue_head(&effects->read_wait); + init_waitqueue_head(&effects->write_wait); + mutex_init(&effects->lock); + + effects->opened = 0; + effects->started = 0; + effects->buf_alloc = 0; + file->private_data = effects; + pr_debug("%s: open session success\n", __func__); + return rc; +} + +static const struct file_operations audio_effects_fops = { + .owner = THIS_MODULE, + .open = audio_effects_open, + .release = audio_effects_release, + .unlocked_ioctl = audio_effects_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = audio_effects_compat_ioctl, +#endif +}; + +struct miscdevice audio_effects_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_hweffects", + .fops = &audio_effects_fops, +}; + +int __init audio_effects_init(void) +{ + return misc_register(&audio_effects_misc); +} + +void audio_effects_exit(void) +{ + misc_deregister(&audio_effects_misc); +} + +MODULE_DESCRIPTION("Audio hardware accelerated effects driver"); +MODULE_LICENSE("GPL v2"); diff --git a/audio-kernel/dsp/codecs/audio_mp3.c b/audio-kernel/dsp/codecs/audio_mp3.c new file mode 100644 index 000000000..4435f3e71 --- /dev/null +++ b/audio-kernel/dsp/codecs/audio_mp3.c @@ -0,0 +1,192 @@ +/* mp3 audio output device + * + * Copyright (C) 2008 Google, Inc. + * Copyright (C) 2008 HTC Corporation + * Copyright (c) 2011-2017, The Linux Foundation. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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 "audio_utils_aio.h" + +static struct miscdevice audio_mp3_misc; +static struct ws_mgr audio_mp3_ws_mgr; + +#ifdef CONFIG_DEBUG_FS +static const struct file_operations audio_mp3_debug_fops = { + .read = audio_aio_debug_read, + .open = audio_aio_debug_open, +}; +#endif + +static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct q6audio_aio *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_START: { + pr_debug("%s[%pK]: AUDIO_START session_id[%d]\n", __func__, + audio, audio->ac->session); + if (audio->feedback == NON_TUNNEL_MODE) { + /* Configure PCM output block */ + rc = q6asm_enc_cfg_blk_pcm(audio->ac, + audio->pcm_cfg.sample_rate, + audio->pcm_cfg.channel_count); + if (rc < 0) { + pr_err("pcm output block config failed\n"); + break; + } + } + + rc = audio_aio_enable(audio); + audio->eos_rsp = 0; + audio->eos_flag = 0; + if (!rc) { + rc = enable_volume_ramp(audio); + if (rc < 0) { + pr_err("%s: Failed to enable volume ramp\n", + __func__); + } + audio->enabled = 1; + } else { + audio->enabled = 0; + pr_err("Audio Start procedure failed rc=%d\n", rc); + break; + } + pr_info("%s: AUDIO_START sessionid[%d]enable[%d]\n", __func__, + audio->ac->session, + audio->enabled); + if (audio->stopped == 1) + audio->stopped = 0; + break; + } + default: + pr_debug("%s[%pK]: Calling utils ioctl\n", __func__, audio); + rc = audio->codec_ioctl(file, cmd, arg); + } + return rc; +} + +static int audio_open(struct inode *inode, struct file *file) +{ + struct q6audio_aio *audio = NULL; + int rc = 0; + +#ifdef CONFIG_DEBUG_FS + /* 4 bytes represents decoder number, 1 byte for terminate string */ + char name[sizeof "msm_mp3_" + 5]; +#endif + audio = kzalloc(sizeof(struct q6audio_aio), GFP_KERNEL); + + if (audio == NULL) + return -ENOMEM; + + audio->pcm_cfg.buffer_size = PCM_BUFSZ_MIN; + audio->miscdevice = &audio_mp3_misc; + audio->wakelock_voted = false; + audio->audio_ws_mgr = &audio_mp3_ws_mgr; + + audio->ac = q6asm_audio_client_alloc((app_cb) q6_audio_cb, + (void *)audio); + + if (!audio->ac) { + pr_err("Could not allocate memory for audio client\n"); + kfree(audio); + return -ENOMEM; + } + rc = audio_aio_open(audio, file); + if (rc < 0) { + pr_err("%s: audio_aio_open rc=%d\n", + __func__, rc); + goto fail; + } + + /* open in T/NT mode */ + if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) { + rc = q6asm_open_read_write(audio->ac, FORMAT_LINEAR_PCM, + FORMAT_MP3); + if (rc < 0) { + pr_err("NT mode Open failed rc=%d\n", rc); + rc = -ENODEV; + goto fail; + } + audio->feedback = NON_TUNNEL_MODE; + /* open MP3 decoder, expected frames is always 1 + * audio->buf_cfg.frames_per_buf = 0x01; + */ + audio->buf_cfg.meta_info_enable = 0x01; + } else if ((file->f_mode & FMODE_WRITE) && + !(file->f_mode & FMODE_READ)) { + rc = q6asm_open_write(audio->ac, FORMAT_MP3); + if (rc < 0) { + pr_err("T mode Open failed rc=%d\n", rc); + rc = -ENODEV; + goto fail; + } + audio->feedback = TUNNEL_MODE; + audio->buf_cfg.meta_info_enable = 0x00; + } else { + pr_err("Not supported mode\n"); + rc = -EACCES; + goto fail; + } + +#ifdef CONFIG_DEBUG_FS + snprintf(name, sizeof(name), "msm_mp3_%04x", audio->ac->session); + audio->dentry = debugfs_create_file(name, S_IFREG | 0444, + NULL, (void *)audio, + &audio_mp3_debug_fops); + + if (IS_ERR(audio->dentry)) + pr_debug("debugfs_create_file failed\n"); +#endif + pr_info("%s:mp3dec success mode[%d]session[%d]\n", __func__, + audio->feedback, + audio->ac->session); + return rc; +fail: + q6asm_audio_client_free(audio->ac); + kfree(audio); + return rc; +} + +static const struct file_operations audio_mp3_fops = { + .owner = THIS_MODULE, + .open = audio_open, + .release = audio_aio_release, + .unlocked_ioctl = audio_ioctl, + .fsync = audio_aio_fsync, +}; + +static struct miscdevice audio_mp3_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_mp3", + .fops = &audio_mp3_fops, +}; + +int __init audio_mp3_init(void) +{ + int ret = misc_register(&audio_mp3_misc); + + if (ret == 0) + device_init_wakeup(audio_mp3_misc.this_device, true); + audio_mp3_ws_mgr.ref_cnt = 0; + mutex_init(&audio_mp3_ws_mgr.ws_lock); + + return ret; +} + +void audio_mp3_exit(void) +{ + mutex_destroy(&audio_mp3_ws_mgr.ws_lock); + misc_deregister(&audio_mp3_misc); +} diff --git a/audio-kernel/dsp/codecs/audio_multi_aac.c b/audio-kernel/dsp/codecs/audio_multi_aac.c new file mode 100644 index 000000000..87e4f4ec0 --- /dev/null +++ b/audio-kernel/dsp/codecs/audio_multi_aac.c @@ -0,0 +1,534 @@ +/* aac audio output device + * + * Copyright (C) 2008 Google, Inc. + * Copyright (C) 2008 HTC Corporation + * Copyright (c) 2011-2018, The Linux Foundation. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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 +#include +#include +#include "audio_utils_aio.h" + +#define AUDIO_AAC_DUAL_MONO_INVALID -1 + + +/* Default number of pre-allocated event packets */ +#define PCM_BUFSZ_MIN_AACM ((8*1024) + sizeof(struct dec_meta_out)) +static struct miscdevice audio_multiaac_misc; +static struct ws_mgr audio_multiaac_ws_mgr; + +#ifdef CONFIG_DEBUG_FS +static const struct file_operations audio_aac_debug_fops = { + .read = audio_aio_debug_read, + .open = audio_aio_debug_open, +}; +#endif + +static long audio_ioctl_shared(struct file *file, unsigned int cmd, + void *arg) +{ + struct q6audio_aio *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_START: { + struct asm_aac_cfg aac_cfg; + struct msm_audio_aac_config *aac_config; + uint32_t sbr_ps = 0x00; + + aac_config = (struct msm_audio_aac_config *)audio->codec_cfg; + if (audio->feedback == TUNNEL_MODE) { + aac_cfg.sample_rate = aac_config->sample_rate; + aac_cfg.ch_cfg = aac_config->channel_configuration; + } else { + aac_cfg.sample_rate = audio->pcm_cfg.sample_rate; + aac_cfg.ch_cfg = audio->pcm_cfg.channel_count; + } + pr_debug("%s: AUDIO_START session_id[%d]\n", __func__, + audio->ac->session); + if (audio->feedback == NON_TUNNEL_MODE) { + /* Configure PCM output block */ + rc = q6asm_enc_cfg_blk_pcm_native(audio->ac, + audio->pcm_cfg.sample_rate, + audio->pcm_cfg.channel_count); + if (rc < 0) { + pr_err("pcm output block config failed\n"); + break; + } + } + /* turn on both sbr and ps */ + rc = q6asm_enable_sbrps(audio->ac, sbr_ps); + if (rc < 0) + pr_err("sbr-ps enable failed\n"); + if (aac_config->sbr_ps_on_flag) + aac_cfg.aot = AAC_ENC_MODE_EAAC_P; + else if (aac_config->sbr_on_flag) + aac_cfg.aot = AAC_ENC_MODE_AAC_P; + else + aac_cfg.aot = AAC_ENC_MODE_AAC_LC; + + switch (aac_config->format) { + case AUDIO_AAC_FORMAT_ADTS: + aac_cfg.format = 0x00; + break; + case AUDIO_AAC_FORMAT_LOAS: + aac_cfg.format = 0x01; + break; + case AUDIO_AAC_FORMAT_ADIF: + aac_cfg.format = 0x02; + break; + default: + case AUDIO_AAC_FORMAT_RAW: + aac_cfg.format = 0x03; + } + aac_cfg.ep_config = aac_config->ep_config; + aac_cfg.section_data_resilience = + aac_config->aac_section_data_resilience_flag; + aac_cfg.scalefactor_data_resilience = + aac_config->aac_scalefactor_data_resilience_flag; + aac_cfg.spectral_data_resilience = + aac_config->aac_spectral_data_resilience_flag; + + pr_debug("%s:format=%x aot=%d ch=%d sr=%d\n", + __func__, aac_cfg.format, + aac_cfg.aot, aac_cfg.ch_cfg, + aac_cfg.sample_rate); + + /* Configure Media format block */ + rc = q6asm_media_format_block_multi_aac(audio->ac, &aac_cfg); + if (rc < 0) { + pr_err("cmd media format block failed\n"); + break; + } + + /* Fall back to the default number of channels + * if aac_cfg.ch_cfg is not between 1-6 + */ + if ((aac_cfg.ch_cfg == 0) || (aac_cfg.ch_cfg > 6)) + aac_cfg.ch_cfg = 2; + + rc = q6asm_set_encdec_chan_map(audio->ac, aac_cfg.ch_cfg); + if (rc < 0) { + pr_err("%s: cmd set encdec_chan_map failed\n", + __func__); + break; + } + rc = audio_aio_enable(audio); + audio->eos_rsp = 0; + audio->eos_flag = 0; + if (!rc) { + audio->enabled = 1; + } else { + audio->enabled = 0; + pr_err("Audio Start procedure failed rc=%d\n", rc); + break; + } + pr_info("%s: AUDIO_START sessionid[%d]enable[%d]\n", __func__, + audio->ac->session, + audio->enabled); + if (audio->stopped == 1) + audio->stopped = 0; + break; + } + case AUDIO_SET_AAC_CONFIG: { + struct msm_audio_aac_config *aac_config; + uint16_t sce_left = 1, sce_right = 2; + + if (arg == NULL) { + pr_err("%s: NULL config pointer\n", __func__); + rc = -EINVAL; + break; + } + memcpy(audio->codec_cfg, arg, + sizeof(struct msm_audio_aac_config)); + aac_config = audio->codec_cfg; + if (aac_config->dual_mono_mode > + AUDIO_AAC_DUAL_MONO_PL_SR) { + pr_err("%s:AUDIO_SET_AAC_CONFIG: Invalid dual_mono mode =%d\n", + __func__, aac_config->dual_mono_mode); + } else { + /* convert the data from user into sce_left + * and sce_right based on the definitions + */ + pr_debug("%s: AUDIO_SET_AAC_CONFIG: modify dual_mono mode =%d\n", + __func__, aac_config->dual_mono_mode); + switch (aac_config->dual_mono_mode) { + case AUDIO_AAC_DUAL_MONO_PL_PR: + sce_left = 1; + sce_right = 1; + break; + case AUDIO_AAC_DUAL_MONO_SL_SR: + sce_left = 2; + sce_right = 2; + break; + case AUDIO_AAC_DUAL_MONO_SL_PR: + sce_left = 2; + sce_right = 1; + break; + case AUDIO_AAC_DUAL_MONO_PL_SR: + default: + sce_left = 1; + sce_right = 2; + break; + } + rc = q6asm_cfg_dual_mono_aac(audio->ac, + sce_left, sce_right); + if (rc < 0) + pr_err("%s: asm cmd dualmono failed rc=%d\n", + __func__, rc); + } break; + break; + } + case AUDIO_SET_AAC_MIX_CONFIG: { + u32 *mix_coeff = (u32 *)arg; + + if (!arg) { + pr_err("%s: Invalid param for %s\n", + __func__, "AUDIO_SET_AAC_MIX_CONFIG"); + rc = -EINVAL; + break; + } + pr_debug("%s, AUDIO_SET_AAC_MIX_CONFIG", __func__); + pr_debug("%s, value of coeff = %d", + __func__, *mix_coeff); + q6asm_cfg_aac_sel_mix_coef(audio->ac, *mix_coeff); + if (rc < 0) + pr_err("%s asm aac_sel_mix_coef failed rc=%d\n", + __func__, rc); + break; + } + default: + pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd); + rc = -EINVAL; + break; + } + return rc; +} + +static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct q6audio_aio *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_START: { + rc = audio_ioctl_shared(file, cmd, (void *)arg); + break; + } + case AUDIO_GET_AAC_CONFIG: { + if (copy_to_user((void *)arg, audio->codec_cfg, + sizeof(struct msm_audio_aac_config))) { + pr_err("%s: copy_to_user for AUDIO_GET_AAC_CONFIG failed\n" + , __func__); + rc = -EFAULT; + break; + } + break; + } + case AUDIO_SET_AAC_CONFIG: { + struct msm_audio_aac_config aac_config; + + if (copy_from_user(&aac_config, (void *)arg, + sizeof(aac_config))) { + pr_err("%s: copy_from_user for AUDIO_SET_AAC_CONFIG failed\n" + , __func__); + rc = -EFAULT; + } + rc = audio_ioctl_shared(file, cmd, &aac_config); + if (rc) + pr_err("%s:AUDIO_SET_AAC_CONFIG failed. Rc= %d\n", + __func__, rc); + break; + } + case AUDIO_SET_AAC_MIX_CONFIG: { + u32 mix_config; + + pr_debug("%s, AUDIO_SET_AAC_MIX_CONFIG", __func__); + if (copy_from_user(&mix_config, (void *)arg, + sizeof(u32))) { + pr_err("%s: copy_from_user for AUDIO_SET_AAC_MIX_CONFIG failed\n", + __func__); + rc = -EFAULT; + break; + } + rc = audio_ioctl_shared(file, cmd, &mix_config); + if (rc) + pr_err("%s:AUDIO_SET_AAC_CONFIG failed. Rc= %d\n", + __func__, rc); + break; + } + default: { + pr_debug("Calling utils ioctl\n"); + rc = audio->codec_ioctl(file, cmd, arg); + } + } + return rc; +} + +#ifdef CONFIG_COMPAT +struct msm_audio_aac_config32 { + s16 format; + u16 audio_object; + u16 ep_config; /* 0 ~ 3 useful only obj = ERLC */ + u16 aac_section_data_resilience_flag; + u16 aac_scalefactor_data_resilience_flag; + u16 aac_spectral_data_resilience_flag; + u16 sbr_on_flag; + u16 sbr_ps_on_flag; + u16 dual_mono_mode; + u16 channel_configuration; + u16 sample_rate; +}; + +enum { + AUDIO_SET_AAC_CONFIG_32 = _IOW(AUDIO_IOCTL_MAGIC, + (AUDIO_MAX_COMMON_IOCTL_NUM+0), struct msm_audio_aac_config32), + AUDIO_GET_AAC_CONFIG_32 = _IOR(AUDIO_IOCTL_MAGIC, + (AUDIO_MAX_COMMON_IOCTL_NUM+1), struct msm_audio_aac_config32), +}; + +static long audio_compat_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct q6audio_aio *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_START: { + rc = audio_ioctl_shared(file, cmd, (void *)arg); + break; + } + case AUDIO_GET_AAC_CONFIG_32: { + struct msm_audio_aac_config *aac_config; + struct msm_audio_aac_config32 aac_config_32; + + memset(&aac_config_32, 0, sizeof(aac_config_32)); + + aac_config = (struct msm_audio_aac_config *)audio->codec_cfg; + aac_config_32.format = aac_config->format; + aac_config_32.audio_object = aac_config->audio_object; + aac_config_32.ep_config = aac_config->ep_config; + aac_config_32.aac_section_data_resilience_flag = + aac_config->aac_section_data_resilience_flag; + aac_config_32.aac_scalefactor_data_resilience_flag = + aac_config->aac_scalefactor_data_resilience_flag; + aac_config_32.aac_spectral_data_resilience_flag = + aac_config->aac_spectral_data_resilience_flag; + aac_config_32.sbr_on_flag = aac_config->sbr_on_flag; + aac_config_32.sbr_ps_on_flag = aac_config->sbr_ps_on_flag; + aac_config_32.dual_mono_mode = aac_config->dual_mono_mode; + aac_config_32.channel_configuration = + aac_config->channel_configuration; + aac_config_32.sample_rate = aac_config->sample_rate; + + if (copy_to_user((void *)arg, &aac_config_32, + sizeof(aac_config_32))) { + pr_err("%s: copy_to_user for AUDIO_GET_AAC_CONFIG_32 failed\n", + __func__); + rc = -EFAULT; + break; + } + break; + } + case AUDIO_SET_AAC_CONFIG_32: { + struct msm_audio_aac_config aac_config; + struct msm_audio_aac_config32 aac_config_32; + + pr_debug("%s: AUDIO_SET_AAC_CONFIG\n", __func__); + if (copy_from_user(&aac_config_32, (void *)arg, + sizeof(aac_config_32))) { + pr_err( + "%s: copy_from_user for AUDIO_SET_AAC_CONFIG_32 failed", + __func__); + rc = -EFAULT; + break; + } + aac_config.format = aac_config_32.format; + aac_config.audio_object = aac_config_32.audio_object; + aac_config.ep_config = aac_config_32.ep_config; + aac_config.aac_section_data_resilience_flag = + aac_config_32.aac_section_data_resilience_flag; + aac_config.aac_scalefactor_data_resilience_flag = + aac_config_32.aac_scalefactor_data_resilience_flag; + aac_config.aac_spectral_data_resilience_flag = + aac_config_32.aac_spectral_data_resilience_flag; + aac_config.sbr_on_flag = aac_config_32.sbr_on_flag; + aac_config.sbr_ps_on_flag = aac_config_32.sbr_ps_on_flag; + aac_config.dual_mono_mode = aac_config_32.dual_mono_mode; + aac_config.channel_configuration = + aac_config_32.channel_configuration; + aac_config.sample_rate = aac_config_32.sample_rate; + + cmd = AUDIO_SET_AAC_CONFIG; + rc = audio_ioctl_shared(file, cmd, &aac_config); + if (rc) + pr_err("%s:AUDIO_SET_AAC_CONFIG failed. rc= %d\n", + __func__, rc); + break; + } + case AUDIO_SET_AAC_MIX_CONFIG: { + u32 mix_config; + + pr_debug("%s, AUDIO_SET_AAC_MIX_CONFIG\n", __func__); + if (copy_from_user(&mix_config, (void *)arg, + sizeof(u32))) { + pr_err("%s: copy_from_user for AUDIO_SET_AAC_MIX_CONFIG failed\n" + , __func__); + rc = -EFAULT; + break; + } + rc = audio_ioctl_shared(file, cmd, &mix_config); + if (rc) + pr_err("%s:AUDIO_SET_AAC_CONFIG failed. Rc= %d\n", + __func__, rc); + break; + } + default: { + pr_debug("Calling utils ioctl\n"); + rc = audio->codec_compat_ioctl(file, cmd, arg); + } + } + return rc; +} +#else +#define audio_compat_ioctl NULL +#endif + +static int audio_open(struct inode *inode, struct file *file) +{ + struct q6audio_aio *audio = NULL; + int rc = 0; + struct msm_audio_aac_config *aac_config = NULL; + +#ifdef CONFIG_DEBUG_FS + /* 4 bytes represents decoder number, 1 byte for terminate string */ + char name[sizeof "msm_multi_aac_" + 5]; +#endif + audio = kzalloc(sizeof(struct q6audio_aio), GFP_KERNEL); + + if (audio == NULL) + return -ENOMEM; + + audio->codec_cfg = kzalloc(sizeof(struct msm_audio_aac_config), + GFP_KERNEL); + if (audio->codec_cfg == NULL) { + kfree(audio); + return -ENOMEM; + } + + aac_config = audio->codec_cfg; + + audio->pcm_cfg.buffer_size = PCM_BUFSZ_MIN_AACM; + audio->miscdevice = &audio_multiaac_misc; + audio->wakelock_voted = false; + audio->audio_ws_mgr = &audio_multiaac_ws_mgr; + aac_config->dual_mono_mode = AUDIO_AAC_DUAL_MONO_INVALID; + + audio->ac = q6asm_audio_client_alloc((app_cb) q6_audio_cb, + (void *)audio); + + if (!audio->ac) { + pr_err("Could not allocate memory for audio client\n"); + kfree(audio->codec_cfg); + kfree(audio); + return -ENOMEM; + } + rc = audio_aio_open(audio, file); + if (rc < 0) { + pr_err("%s: audio_aio_open rc=%d\n", + __func__, rc); + goto fail; + } + + /* open in T/NT mode */ + if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) { + rc = q6asm_open_read_write(audio->ac, FORMAT_LINEAR_PCM, + FORMAT_MPEG4_MULTI_AAC); + if (rc < 0) { + pr_err("NT mode Open failed rc=%d\n", rc); + rc = -ENODEV; + goto fail; + } + audio->feedback = NON_TUNNEL_MODE; + /* open AAC decoder, expected frames is always 1 + * audio->buf_cfg.frames_per_buf = 0x01; + */ + audio->buf_cfg.meta_info_enable = 0x01; + } else if ((file->f_mode & FMODE_WRITE) && + !(file->f_mode & FMODE_READ)) { + rc = q6asm_open_write(audio->ac, FORMAT_MPEG4_MULTI_AAC); + if (rc < 0) { + pr_err("T mode Open failed rc=%d\n", rc); + rc = -ENODEV; + goto fail; + } + audio->feedback = TUNNEL_MODE; + audio->buf_cfg.meta_info_enable = 0x00; + } else { + pr_err("Not supported mode\n"); + rc = -EACCES; + goto fail; + } + +#ifdef CONFIG_DEBUG_FS + snprintf(name, sizeof(name), "msm_multi_aac_%04x", audio->ac->session); + audio->dentry = debugfs_create_file(name, S_IFREG | 0444, + NULL, (void *)audio, + &audio_aac_debug_fops); + + if (IS_ERR(audio->dentry)) + pr_debug("debugfs_create_file failed\n"); +#endif + pr_info("%s:AAC 5.1 Decoder OPEN success mode[%d]session[%d]\n", + __func__, audio->feedback, audio->ac->session); + return rc; +fail: + q6asm_audio_client_free(audio->ac); + kfree(audio->codec_cfg); + kfree(audio); + return rc; +} + +static const struct file_operations audio_aac_fops = { + .owner = THIS_MODULE, + .open = audio_open, + .release = audio_aio_release, + .unlocked_ioctl = audio_ioctl, + .fsync = audio_aio_fsync, + .compat_ioctl = audio_compat_ioctl +}; + +static struct miscdevice audio_multiaac_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_multi_aac", + .fops = &audio_aac_fops, +}; + +int __init audio_multiaac_init(void) +{ + int ret = misc_register(&audio_multiaac_misc); + + if (ret == 0) + device_init_wakeup(audio_multiaac_misc.this_device, true); + audio_multiaac_ws_mgr.ref_cnt = 0; + mutex_init(&audio_multiaac_ws_mgr.ws_lock); + + return ret; +} + +void audio_multiaac_exit(void) +{ + mutex_destroy(&audio_multiaac_ws_mgr.ws_lock); + misc_deregister(&audio_multiaac_misc); +} diff --git a/audio-kernel/dsp/codecs/audio_native.c b/audio-kernel/dsp/codecs/audio_native.c new file mode 100644 index 000000000..8f21cc74f --- /dev/null +++ b/audio-kernel/dsp/codecs/audio_native.c @@ -0,0 +1,75 @@ +/* +Copyright (c) 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 +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 +#include +#include "audio_utils.h" + +static int __init audio_native_init(void) +{ + aac_in_init(); + amrnb_in_init(); + amrwb_in_init(); + audio_aac_init(); + audio_alac_init(); + audio_amrnb_init(); + audio_amrwb_init(); + audio_amrwbplus_init(); + audio_ape_init(); + audio_evrc_init(); + audio_g711alaw_init(); + audio_g711mlaw_init(); + audio_effects_init(); + audio_mp3_init(); + audio_multiaac_init(); + audio_qcelp_init(); + audio_wma_init(); + audio_wmapro_init(); + evrc_in_init(); + g711alaw_in_init(); + g711mlaw_in_init(); + qcelp_in_init(); + return 0; +} + +static void __exit audio_native_exit(void) +{ + aac_in_exit(); + amrnb_in_exit(); + amrwb_in_exit(); + audio_aac_exit(); + audio_alac_exit(); + audio_amrnb_exit(); + audio_amrwb_exit(); + audio_amrwbplus_exit(); + audio_ape_exit(); + audio_evrc_exit(); + audio_g711alaw_exit(); + audio_g711mlaw_exit(); + audio_effects_exit(); + audio_mp3_exit(); + audio_multiaac_exit(); + audio_qcelp_exit(); + audio_wma_exit(); + audio_wmapro_exit(); + evrc_in_exit(); + g711alaw_in_exit(); + g711mlaw_in_exit(); + qcelp_in_exit(); +} + +module_init(audio_native_init); +module_exit(audio_native_exit); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Native Encoder/Decoder module"); diff --git a/audio-kernel/dsp/codecs/audio_qcelp.c b/audio-kernel/dsp/codecs/audio_qcelp.c new file mode 100644 index 000000000..4b23bace5 --- /dev/null +++ b/audio-kernel/dsp/codecs/audio_qcelp.c @@ -0,0 +1,195 @@ +/* qcelp(v13k) audio output device + * + * Copyright (C) 2008 Google, Inc. + * Copyright (C) 2008 HTC Corporation + * Copyright (c) 2011-2017, The Linux Foundation. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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 "audio_utils_aio.h" + +#define FRAME_SIZE_DEC_QCELP ((32) + sizeof(struct dec_meta_in)) + +static struct miscdevice audio_qcelp_misc; +static struct ws_mgr audio_qcelp_ws_mgr; + +#ifdef CONFIG_DEBUG_FS +static const struct file_operations audio_qcelp_debug_fops = { + .read = audio_aio_debug_read, + .open = audio_aio_debug_open, +}; +#endif + +static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct q6audio_aio *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_START: { + pr_debug("%s[%pK]: AUDIO_START session_id[%d]\n", __func__, + audio, audio->ac->session); + if (audio->feedback == NON_TUNNEL_MODE) { + /* Configure PCM output block */ + rc = q6asm_enc_cfg_blk_pcm(audio->ac, + audio->pcm_cfg.sample_rate, + audio->pcm_cfg.channel_count); + if (rc < 0) { + pr_err("pcm output block config failed\n"); + break; + } + } + + rc = audio_aio_enable(audio); + audio->eos_rsp = 0; + audio->eos_flag = 0; + if (!rc) { + audio->enabled = 1; + } else { + audio->enabled = 0; + pr_err("Audio Start procedure failed rc=%d\n", rc); + break; + } + pr_debug("%s: AUDIO_START sessionid[%d]enable[%d]\n", __func__, + audio->ac->session, + audio->enabled); + if (audio->stopped == 1) + audio->stopped = 0; + break; + } + default: + pr_debug("%s[%pK]: Calling utils ioctl\n", __func__, audio); + rc = audio->codec_ioctl(file, cmd, arg); + } + return rc; +} + +static int audio_open(struct inode *inode, struct file *file) +{ + struct q6audio_aio *audio = NULL; + int rc = 0; + +#ifdef CONFIG_DEBUG_FS + /* 4 bytes represents decoder number, 1 byte for terminate string */ + char name[sizeof "msm_qcelp_" + 5]; +#endif + audio = kzalloc(sizeof(struct q6audio_aio), GFP_KERNEL); + + if (audio == NULL) + return -ENOMEM; + + /* Settings will be re-config at AUDIO_SET_CONFIG, + * but at least we need to have initial config + */ + audio->str_cfg.buffer_size = FRAME_SIZE_DEC_QCELP; + audio->str_cfg.buffer_count = FRAME_NUM; + audio->pcm_cfg.buffer_size = PCM_BUFSZ_MIN; + audio->pcm_cfg.buffer_count = PCM_BUF_COUNT; + audio->pcm_cfg.sample_rate = 8000; + audio->pcm_cfg.channel_count = 1; + audio->miscdevice = &audio_qcelp_misc; + audio->wakelock_voted = false; + audio->audio_ws_mgr = &audio_qcelp_ws_mgr; + + audio->ac = q6asm_audio_client_alloc((app_cb) q6_audio_cb, + (void *)audio); + + if (!audio->ac) { + pr_err("Could not allocate memory for audio client\n"); + kfree(audio); + return -ENOMEM; + } + rc = audio_aio_open(audio, file); + if (rc < 0) { + pr_err("%s: audio_aio_open rc=%d\n", + __func__, rc); + goto fail; + } + + /* open in T/NT mode */ + if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) { + rc = q6asm_open_read_write(audio->ac, FORMAT_LINEAR_PCM, + FORMAT_V13K); + if (rc < 0) { + pr_err("NT mode Open failed rc=%d\n", rc); + rc = -ENODEV; + goto fail; + } + audio->feedback = NON_TUNNEL_MODE; + audio->buf_cfg.frames_per_buf = 0x01; + audio->buf_cfg.meta_info_enable = 0x01; + } else if ((file->f_mode & FMODE_WRITE) && + !(file->f_mode & FMODE_READ)) { + rc = q6asm_open_write(audio->ac, FORMAT_V13K); + if (rc < 0) { + pr_err("T mode Open failed rc=%d\n", rc); + rc = -ENODEV; + goto fail; + } + audio->feedback = TUNNEL_MODE; + audio->buf_cfg.meta_info_enable = 0x00; + } else { + pr_err("Not supported mode\n"); + rc = -EACCES; + goto fail; + } + +#ifdef CONFIG_DEBUG_FS + snprintf(name, sizeof(name), "msm_qcelp_%04x", audio->ac->session); + audio->dentry = debugfs_create_file(name, S_IFREG | 0444, + NULL, (void *)audio, + &audio_qcelp_debug_fops); + + if (IS_ERR(audio->dentry)) + pr_debug("debugfs_create_file failed\n"); +#endif + pr_info("%s:dec success mode[%d]session[%d]\n", __func__, + audio->feedback, + audio->ac->session); + return 0; +fail: + q6asm_audio_client_free(audio->ac); + kfree(audio); + return rc; +} + +static const struct file_operations audio_qcelp_fops = { + .owner = THIS_MODULE, + .open = audio_open, + .release = audio_aio_release, + .unlocked_ioctl = audio_ioctl, + .fsync = audio_aio_fsync, +}; + +static struct miscdevice audio_qcelp_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_qcelp", + .fops = &audio_qcelp_fops, +}; + +int audio_qcelp_init(void) +{ + int ret = misc_register(&audio_qcelp_misc); + + if (ret == 0) + device_init_wakeup(audio_qcelp_misc.this_device, true); + audio_qcelp_ws_mgr.ref_cnt = 0; + mutex_init(&audio_qcelp_ws_mgr.ws_lock); + + return ret; +} + +void audio_qcelp_exit(void) +{ + mutex_destroy(&audio_qcelp_ws_mgr.ws_lock); + misc_deregister(&audio_qcelp_misc); +} diff --git a/audio-kernel/dsp/codecs/audio_utils.c b/audio-kernel/dsp/codecs/audio_utils.c new file mode 100644 index 000000000..0565685b9 --- /dev/null +++ b/audio-kernel/dsp/codecs/audio_utils.c @@ -0,0 +1,959 @@ +/* Copyright (c) 2010-2019, 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "audio_utils.h" + +/* + * Define maximum buffer size. Below values are chosen considering the higher + * values used among all native drivers. + */ +#define MAX_FRAME_SIZE 1536 +#define MAX_FRAMES 5 +#define META_SIZE (sizeof(struct meta_out_dsp)) +#define MAX_BUFFER_SIZE (1 + ((MAX_FRAME_SIZE + META_SIZE) * MAX_FRAMES)) + +static int audio_in_pause(struct q6audio_in *audio) +{ + int rc; + + rc = q6asm_cmd(audio->ac, CMD_PAUSE); + if (rc < 0) + pr_err("%s:session id %d: pause cmd failed rc=%d\n", __func__, + audio->ac->session, rc); + + return rc; +} + +static int audio_in_flush(struct q6audio_in *audio) +{ + int rc; + + pr_debug("%s:session id %d: flush\n", __func__, audio->ac->session); + /* Flush if session running */ + if (audio->enabled) { + /* Implicitly issue a pause to the encoder before flushing */ + rc = audio_in_pause(audio); + if (rc < 0) { + pr_err("%s:session id %d: pause cmd failed rc=%d\n", + __func__, audio->ac->session, rc); + return rc; + } + + rc = q6asm_cmd(audio->ac, CMD_FLUSH); + if (rc < 0) { + pr_err("%s:session id %d: flush cmd failed rc=%d\n", + __func__, audio->ac->session, rc); + return rc; + } + /* 2nd arg: 0 -> run immediately + * 3rd arg: 0 -> msw_ts, + * 4th arg: 0 ->lsw_ts + */ + q6asm_run(audio->ac, 0x00, 0x00, 0x00); + pr_debug("Rerun the session\n"); + } + audio->rflush = 1; + audio->wflush = 1; + memset(audio->out_frame_info, 0, sizeof(audio->out_frame_info)); + wake_up(&audio->read_wait); + /* get read_lock to ensure no more waiting read thread */ + mutex_lock(&audio->read_lock); + audio->rflush = 0; + mutex_unlock(&audio->read_lock); + wake_up(&audio->write_wait); + /* get write_lock to ensure no more waiting write thread */ + mutex_lock(&audio->write_lock); + audio->wflush = 0; + mutex_unlock(&audio->write_lock); + pr_debug("%s:session id %d: in_bytes %d\n", __func__, + audio->ac->session, atomic_read(&audio->in_bytes)); + pr_debug("%s:session id %d: in_samples %d\n", __func__, + audio->ac->session, atomic_read(&audio->in_samples)); + atomic_set(&audio->in_bytes, 0); + atomic_set(&audio->in_samples, 0); + atomic_set(&audio->out_count, 0); + return 0; +} + +/* must be called with audio->lock held */ +int audio_in_enable(struct q6audio_in *audio) +{ + if (audio->enabled) + return 0; + + /* 2nd arg: 0 -> run immediately + * 3rd arg: 0 -> msw_ts, + * 4th arg: 0 ->lsw_ts + */ + return q6asm_run(audio->ac, 0x00, 0x00, 0x00); +} + +/* must be called with audio->lock held */ +int audio_in_disable(struct q6audio_in *audio) +{ + int rc = 0; + + if (!audio->stopped) { + audio->enabled = 0; + audio->opened = 0; + pr_debug("%s:session id %d: inbytes[%d] insamples[%d]\n", + __func__, audio->ac->session, + atomic_read(&audio->in_bytes), + atomic_read(&audio->in_samples)); + + rc = q6asm_cmd(audio->ac, CMD_CLOSE); + if (rc < 0) + pr_err("%s:session id %d: Failed to close the session rc=%d\n", + __func__, audio->ac->session, + rc); + audio->stopped = 1; + memset(audio->out_frame_info, 0, + sizeof(audio->out_frame_info)); + wake_up(&audio->read_wait); + wake_up(&audio->write_wait); + } + pr_debug("%s:session id %d: enabled[%d]\n", __func__, + audio->ac->session, audio->enabled); + return rc; +} + +int audio_in_buf_alloc(struct q6audio_in *audio) +{ + int rc = 0; + + switch (audio->buf_alloc) { + case NO_BUF_ALLOC: + if (audio->feedback == NON_TUNNEL_MODE) { + rc = q6asm_audio_client_buf_alloc(IN, + audio->ac, + ALIGN_BUF_SIZE(audio->pcm_cfg.buffer_size), + audio->pcm_cfg.buffer_count); + if (rc < 0) { + pr_err("%s:session id %d: Buffer Alloc failed\n", + __func__, + audio->ac->session); + rc = -ENOMEM; + break; + } + audio->buf_alloc |= BUF_ALLOC_IN; + } + rc = q6asm_audio_client_buf_alloc(OUT, audio->ac, + ALIGN_BUF_SIZE(audio->str_cfg.buffer_size), + audio->str_cfg.buffer_count); + if (rc < 0) { + pr_err("%s:session id %d: Buffer Alloc failed rc=%d\n", + __func__, audio->ac->session, rc); + rc = -ENOMEM; + break; + } + audio->buf_alloc |= BUF_ALLOC_OUT; + break; + case BUF_ALLOC_IN: + rc = q6asm_audio_client_buf_alloc(OUT, audio->ac, + ALIGN_BUF_SIZE(audio->str_cfg.buffer_size), + audio->str_cfg.buffer_count); + if (rc < 0) { + pr_err("%s:session id %d: Buffer Alloc failed rc=%d\n", + __func__, audio->ac->session, rc); + rc = -ENOMEM; + break; + } + audio->buf_alloc |= BUF_ALLOC_OUT; + break; + case BUF_ALLOC_OUT: + if (audio->feedback == NON_TUNNEL_MODE) { + rc = q6asm_audio_client_buf_alloc(IN, audio->ac, + ALIGN_BUF_SIZE(audio->pcm_cfg.buffer_size), + audio->pcm_cfg.buffer_count); + if (rc < 0) { + pr_err("%s:session id %d: Buffer Alloc failed\n", + __func__, + audio->ac->session); + rc = -ENOMEM; + break; + } + audio->buf_alloc |= BUF_ALLOC_IN; + } + break; + default: + pr_debug("%s:session id %d: buf[%d]\n", __func__, + audio->ac->session, audio->buf_alloc); + } + + return rc; +} + +int audio_in_set_config(struct file *file, + struct msm_audio_config *cfg) +{ + int rc = 0; + struct q6audio_in *audio = file->private_data; + + if (audio->feedback != NON_TUNNEL_MODE) { + pr_err("%s:session id %d: Not sufficient permission to change the record mode\n", + __func__, audio->ac->session); + rc = -EACCES; + goto ret; + } + if ((cfg->buffer_count > PCM_BUF_COUNT) || + (cfg->buffer_count == 1)) + cfg->buffer_count = PCM_BUF_COUNT; + + audio->pcm_cfg.buffer_count = cfg->buffer_count; + audio->pcm_cfg.buffer_size = cfg->buffer_size; + audio->pcm_cfg.channel_count = cfg->channel_count; + audio->pcm_cfg.sample_rate = cfg->sample_rate; + if (audio->opened && audio->feedback == NON_TUNNEL_MODE) { + rc = q6asm_audio_client_buf_alloc(IN, audio->ac, + ALIGN_BUF_SIZE(audio->pcm_cfg.buffer_size), + audio->pcm_cfg.buffer_count); + if (rc < 0) { + pr_err("%s:session id %d: Buffer Alloc failed\n", + __func__, audio->ac->session); + rc = -ENOMEM; + goto ret; + } + } + audio->buf_alloc |= BUF_ALLOC_IN; + rc = 0; + pr_debug("%s:session id %d: AUDIO_SET_CONFIG %d %d\n", __func__, + audio->ac->session, audio->pcm_cfg.buffer_count, + audio->pcm_cfg.buffer_size); +ret: + return rc; +} +/* ------------------- device --------------------- */ +static long audio_in_ioctl_shared(struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct q6audio_in *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_FLUSH: { + /* Make sure we're stopped and we wake any threads + * that might be blocked holding the read_lock. + * While audio->stopped read threads will always + * exit immediately. + */ + rc = audio_in_flush(audio); + if (rc < 0) + pr_err("%s:session id %d: Flush Fail rc=%d\n", + __func__, audio->ac->session, rc); + else { /* Register back the flushed read buffer with DSP */ + int cnt = 0; + + while (cnt++ < audio->str_cfg.buffer_count) + q6asm_read(audio->ac); /* Push buffer to DSP */ + pr_debug("register the read buffer\n"); + } + break; + } + case AUDIO_PAUSE: { + pr_debug("%s:session id %d: AUDIO_PAUSE\n", __func__, + audio->ac->session); + if (audio->enabled) + audio_in_pause(audio); + break; + } + case AUDIO_GET_SESSION_ID: { + if (copy_to_user((void *) arg, &audio->ac->session, + sizeof(u16))) { + pr_err("%s: copy_to_user for AUDIO_GET_SESSION_ID failed\n", + __func__); + rc = -EFAULT; + } + break; + } + default: + pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd); + rc = -EINVAL; + } + return rc; +} + +long audio_in_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct q6audio_in *audio = file->private_data; + int rc = 0; + + if (cmd == AUDIO_GET_STATS) { + struct msm_audio_stats stats; + + memset(&stats, 0, sizeof(stats)); + stats.byte_count = atomic_read(&audio->in_bytes); + stats.sample_count = atomic_read(&audio->in_samples); + if (copy_to_user((void *) arg, &stats, sizeof(stats))) + return -EFAULT; + return rc; + } + + mutex_lock(&audio->lock); + switch (cmd) { + case AUDIO_FLUSH: + case AUDIO_PAUSE: + case AUDIO_GET_SESSION_ID: + rc = audio_in_ioctl_shared(file, cmd, arg); + break; + case AUDIO_GET_STREAM_CONFIG: { + struct msm_audio_stream_config cfg; + + memset(&cfg, 0, sizeof(cfg)); + cfg.buffer_size = audio->str_cfg.buffer_size; + cfg.buffer_count = audio->str_cfg.buffer_count; + if (copy_to_user((void *)arg, &cfg, sizeof(cfg))) + rc = -EFAULT; + pr_debug("%s:session id %d: AUDIO_GET_STREAM_CONFIG %d %d\n", + __func__, audio->ac->session, cfg.buffer_size, + cfg.buffer_count); + break; + } + case AUDIO_SET_STREAM_CONFIG: { + struct msm_audio_stream_config cfg; + + if (copy_from_user(&cfg, (void *)arg, sizeof(cfg))) { + pr_err("%s: copy_from_user for AUDIO_SET_STREAM_CONFIG failed\n" + , __func__); + rc = -EFAULT; + break; + } + /* Minimum single frame size, + * but with in maximum frames number + */ + if ((cfg.buffer_size < (audio->min_frame_size + + sizeof(struct meta_out_dsp))) || + (cfg.buffer_count < FRAME_NUM)) { + rc = -EINVAL; + break; + } + if (cfg.buffer_size > MAX_BUFFER_SIZE) { + rc = -EINVAL; + break; + } + audio->str_cfg.buffer_size = cfg.buffer_size; + audio->str_cfg.buffer_count = cfg.buffer_count; + if (audio->opened) { + rc = q6asm_audio_client_buf_alloc(OUT, audio->ac, + ALIGN_BUF_SIZE(audio->str_cfg.buffer_size), + audio->str_cfg.buffer_count); + if (rc < 0) { + pr_err("%s: session id %d: Buffer Alloc failed rc=%d\n", + __func__, audio->ac->session, rc); + rc = -ENOMEM; + break; + } + } + audio->buf_alloc |= BUF_ALLOC_OUT; + rc = 0; + pr_debug("%s:session id %d: AUDIO_SET_STREAM_CONFIG %d %d\n", + __func__, audio->ac->session, + audio->str_cfg.buffer_size, + audio->str_cfg.buffer_count); + break; + } + case AUDIO_SET_BUF_CFG: { + struct msm_audio_buf_cfg cfg; + + if (copy_from_user(&cfg, (void *)arg, sizeof(cfg))) { + rc = -EFAULT; + break; + } + if ((audio->feedback == NON_TUNNEL_MODE) && + !cfg.meta_info_enable) { + rc = -EFAULT; + break; + } + + /* Restrict the num of frames per buf to coincide with + * default buf size + */ + if (cfg.frames_per_buf > audio->max_frames_per_buf) { + rc = -EFAULT; + break; + } + audio->buf_cfg.meta_info_enable = cfg.meta_info_enable; + audio->buf_cfg.frames_per_buf = cfg.frames_per_buf; + pr_debug("%s:session id %d: Set-buf-cfg: meta[%d] framesperbuf[%d]\n", + __func__, + audio->ac->session, cfg.meta_info_enable, + cfg.frames_per_buf); + break; + } + case AUDIO_GET_BUF_CFG: { + pr_debug("%s:session id %d: Get-buf-cfg: meta[%d] framesperbuf[%d]\n", + __func__, + audio->ac->session, audio->buf_cfg.meta_info_enable, + audio->buf_cfg.frames_per_buf); + + if (copy_to_user((void *)arg, &audio->buf_cfg, + sizeof(struct msm_audio_buf_cfg))) + rc = -EFAULT; + break; + } + case AUDIO_GET_CONFIG: { + if (copy_to_user((void *)arg, &audio->pcm_cfg, + sizeof(struct msm_audio_config))) + rc = -EFAULT; + break; + + } + case AUDIO_SET_CONFIG: { + struct msm_audio_config cfg; + + if (copy_from_user(&cfg, (void *)arg, sizeof(cfg))) { + pr_err("%s: copy_from_user for AUDIO_SET_CONFIG failed\n", + __func__); + rc = -EFAULT; + break; + } + rc = audio_in_set_config(file, &cfg); + break; + } + default: + /* call codec specific ioctl */ + rc = audio->enc_ioctl(file, cmd, arg); + } + mutex_unlock(&audio->lock); + return rc; +} + +#ifdef CONFIG_COMPAT +struct msm_audio_stats32 { + u32 byte_count; + u32 sample_count; + u32 unused[2]; +}; + +struct msm_audio_stream_config32 { + u32 buffer_size; + u32 buffer_count; +}; + +struct msm_audio_config32 { + u32 buffer_size; + u32 buffer_count; + u32 channel_count; + u32 sample_rate; + u32 type; + u32 meta_field; + u32 bits; + u32 unused[3]; +}; + +struct msm_audio_buf_cfg32 { + u32 meta_info_enable; + u32 frames_per_buf; +}; + +enum { + AUDIO_GET_CONFIG_32 = _IOR(AUDIO_IOCTL_MAGIC, 3, + struct msm_audio_config32), + AUDIO_SET_CONFIG_32 = _IOW(AUDIO_IOCTL_MAGIC, 4, + struct msm_audio_config32), + AUDIO_GET_STATS_32 = _IOR(AUDIO_IOCTL_MAGIC, 5, + struct msm_audio_stats32), + AUDIO_SET_STREAM_CONFIG_32 = _IOW(AUDIO_IOCTL_MAGIC, 80, + struct msm_audio_stream_config32), + AUDIO_GET_STREAM_CONFIG_32 = _IOR(AUDIO_IOCTL_MAGIC, 81, + struct msm_audio_stream_config32), + AUDIO_SET_BUF_CFG_32 = _IOW(AUDIO_IOCTL_MAGIC, 94, + struct msm_audio_buf_cfg32), + AUDIO_GET_BUF_CFG_32 = _IOW(AUDIO_IOCTL_MAGIC, 93, + struct msm_audio_buf_cfg32), +}; + +long audio_in_compat_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct q6audio_in *audio = file->private_data; + int rc = 0; + + if (cmd == AUDIO_GET_STATS_32) { + struct msm_audio_stats32 stats_32; + + memset(&stats_32, 0, sizeof(stats_32)); + stats_32.byte_count = atomic_read(&audio->in_bytes); + stats_32.sample_count = atomic_read(&audio->in_samples); + if (copy_to_user((void *) arg, &stats_32, sizeof(stats_32))) { + pr_err("%s: copy_to_user failed for AUDIO_GET_STATS_32\n", + __func__); + return -EFAULT; + } + return rc; + } + + mutex_lock(&audio->lock); + switch (cmd) { + case AUDIO_FLUSH: + case AUDIO_PAUSE: + case AUDIO_GET_SESSION_ID: + rc = audio_in_ioctl_shared(file, cmd, arg); + break; + case AUDIO_GET_STREAM_CONFIG_32: { + struct msm_audio_stream_config32 cfg_32; + + memset(&cfg_32, 0, sizeof(cfg_32)); + cfg_32.buffer_size = audio->str_cfg.buffer_size; + cfg_32.buffer_count = audio->str_cfg.buffer_count; + if (copy_to_user((void *)arg, &cfg_32, sizeof(cfg_32))) { + pr_err("%s: Copy to user failed\n", __func__); + rc = -EFAULT; + } + pr_debug("%s:session id %d: AUDIO_GET_STREAM_CONFIG %d %d\n", + __func__, audio->ac->session, + cfg_32.buffer_size, + cfg_32.buffer_count); + break; + } + case AUDIO_SET_STREAM_CONFIG_32: { + struct msm_audio_stream_config32 cfg_32; + struct msm_audio_stream_config cfg; + + if (copy_from_user(&cfg_32, (void *)arg, sizeof(cfg_32))) { + pr_err("%s: copy_from_user for AUDIO_SET_STREAM_CONFIG_32 failed\n", + __func__); + rc = -EFAULT; + break; + } + cfg.buffer_size = cfg_32.buffer_size; + cfg.buffer_count = cfg_32.buffer_count; + /* Minimum single frame size, + * but with in maximum frames number + */ + if ((cfg.buffer_size < (audio->min_frame_size + + sizeof(struct meta_out_dsp))) || + (cfg.buffer_count < FRAME_NUM)) { + rc = -EINVAL; + break; + } + audio->str_cfg.buffer_size = cfg.buffer_size; + audio->str_cfg.buffer_count = cfg.buffer_count; + if (audio->opened) { + rc = q6asm_audio_client_buf_alloc(OUT, audio->ac, + ALIGN_BUF_SIZE(audio->str_cfg.buffer_size), + audio->str_cfg.buffer_count); + if (rc < 0) { + pr_err("%s: session id %d:\n", + __func__, audio->ac->session); + pr_err("Buffer Alloc failed rc=%d\n", rc); + rc = -ENOMEM; + break; + } + } + audio->buf_alloc |= BUF_ALLOC_OUT; + pr_debug("%s:session id %d: AUDIO_SET_STREAM_CONFIG %d %d\n", + __func__, audio->ac->session, + audio->str_cfg.buffer_size, + audio->str_cfg.buffer_count); + break; + } + case AUDIO_SET_BUF_CFG_32: { + struct msm_audio_buf_cfg32 cfg_32; + struct msm_audio_buf_cfg cfg; + + if (copy_from_user(&cfg_32, (void *)arg, sizeof(cfg_32))) { + pr_err("%s: copy_from_user for AUDIO_SET_BUG_CFG_32 failed", + __func__); + rc = -EFAULT; + break; + } + cfg.meta_info_enable = cfg_32.meta_info_enable; + cfg.frames_per_buf = cfg_32.frames_per_buf; + + if ((audio->feedback == NON_TUNNEL_MODE) && + !cfg.meta_info_enable) { + rc = -EFAULT; + break; + } + + /* Restrict the num of frames per buf to coincide with + * default buf size + */ + if (cfg.frames_per_buf > audio->max_frames_per_buf) { + rc = -EFAULT; + break; + } + audio->buf_cfg.meta_info_enable = cfg.meta_info_enable; + audio->buf_cfg.frames_per_buf = cfg.frames_per_buf; + pr_debug("%s:session id %d: Set-buf-cfg: meta[%d] framesperbuf[%d]\n", + __func__, audio->ac->session, cfg.meta_info_enable, + cfg.frames_per_buf); + break; + } + case AUDIO_GET_BUF_CFG_32: { + struct msm_audio_buf_cfg32 cfg_32; + + pr_debug("%s:session id %d: Get-buf-cfg: meta[%d] framesperbuf[%d]\n", + __func__, + audio->ac->session, audio->buf_cfg.meta_info_enable, + audio->buf_cfg.frames_per_buf); + cfg_32.meta_info_enable = audio->buf_cfg.meta_info_enable; + cfg_32.frames_per_buf = audio->buf_cfg.frames_per_buf; + + if (copy_to_user((void *)arg, &cfg_32, + sizeof(struct msm_audio_buf_cfg32))) { + pr_err("%s: Copy to user failed\n", __func__); + rc = -EFAULT; + } + break; + } + case AUDIO_GET_CONFIG_32: { + struct msm_audio_config32 cfg_32; + + memset(&cfg_32, 0, sizeof(cfg_32)); + cfg_32.buffer_size = audio->pcm_cfg.buffer_size; + cfg_32.buffer_count = audio->pcm_cfg.buffer_count; + cfg_32.channel_count = audio->pcm_cfg.channel_count; + cfg_32.sample_rate = audio->pcm_cfg.sample_rate; + cfg_32.type = audio->pcm_cfg.type; + cfg_32.meta_field = audio->pcm_cfg.meta_field; + cfg_32.bits = audio->pcm_cfg.bits; + + if (copy_to_user((void *)arg, &cfg_32, + sizeof(struct msm_audio_config32))) { + pr_err("%s: Copy to user failed\n", __func__); + rc = -EFAULT; + } + break; + } + case AUDIO_SET_CONFIG_32: { + struct msm_audio_config32 cfg_32; + struct msm_audio_config cfg; + + if (copy_from_user(&cfg_32, (void *)arg, sizeof(cfg_32))) { + pr_err("%s: copy_from_user for AUDIO_SET_CONFIG_32 failed\n", + __func__); + rc = -EFAULT; + break; + } + cfg.buffer_size = cfg_32.buffer_size; + cfg.buffer_count = cfg_32.buffer_count; + cfg.channel_count = cfg_32.channel_count; + cfg.sample_rate = cfg_32.sample_rate; + cfg.type = cfg_32.type; + cfg.meta_field = cfg_32.meta_field; + cfg.bits = cfg_32.bits; + rc = audio_in_set_config(file, &cfg); + break; + } + default: + /* call codec specific ioctl */ + rc = audio->enc_compat_ioctl(file, cmd, arg); + } + mutex_unlock(&audio->lock); + return rc; +} +#endif + +ssize_t audio_in_read(struct file *file, + char __user *buf, + size_t count, loff_t *pos) +{ + struct q6audio_in *audio = file->private_data; + const char __user *start = buf; + unsigned char *data; + uint32_t offset = 0; + uint32_t size = 0; + int rc = 0; + uint32_t idx; + struct meta_out_dsp meta; + uint32_t bytes_to_copy = 0; + uint32_t mfield_size = (audio->buf_cfg.meta_info_enable == 0) ? 0 : + (sizeof(unsigned char) + + (sizeof(struct meta_out_dsp)*(audio->buf_cfg.frames_per_buf))); + + memset(&meta, 0, sizeof(meta)); + pr_debug("%s:session id %d: read - %zd\n", __func__, audio->ac->session, + count); + if (audio->reset_event) + return -ENETRESET; + + if (!audio->enabled) + return -EFAULT; + mutex_lock(&audio->read_lock); + while (count > 0) { + rc = wait_event_interruptible( + audio->read_wait, + ((atomic_read(&audio->out_count) > 0) || + (audio->stopped) || + audio->rflush || audio->eos_rsp || + audio->event_abort)); + + if (audio->event_abort) { + rc = -EIO; + break; + } + + + if (rc < 0) + break; + + if ((audio->stopped && !(atomic_read(&audio->out_count))) || + audio->rflush) { + pr_debug("%s:session id %d: driver in stop state or flush,No more buf to read", + __func__, + audio->ac->session); + rc = 0;/* End of File */ + break; + } + if (!(atomic_read(&audio->out_count)) && + (audio->eos_rsp == 1) && + (count >= (sizeof(unsigned char) + + sizeof(struct meta_out_dsp)))) { + unsigned char num_of_frames; + + pr_info("%s:session id %d: eos %d at output\n", + __func__, audio->ac->session, audio->eos_rsp); + if (buf != start) + break; + num_of_frames = 0xFF; + if (copy_to_user(buf, &num_of_frames, + sizeof(unsigned char))) { + rc = -EFAULT; + break; + } + buf += sizeof(unsigned char); + meta.frame_size = 0xFFFF; + meta.encoded_pcm_samples = 0xFFFF; + meta.msw_ts = 0x00; + meta.lsw_ts = 0x00; + meta.nflags = AUD_EOS_SET; + audio->eos_rsp = 0; + if (copy_to_user(buf, &meta, sizeof(meta))) { + rc = -EFAULT; + break; + } + buf += sizeof(meta); + break; + } + data = (unsigned char *)q6asm_is_cpu_buf_avail(OUT, audio->ac, + &size, &idx); + if ((count >= (size + mfield_size)) && data) { + if (audio->buf_cfg.meta_info_enable) { + if (copy_to_user(buf, + &audio->out_frame_info[idx][0], + sizeof(unsigned char))) { + rc = -EFAULT; + break; + } + bytes_to_copy = + (size + audio->out_frame_info[idx][1]); + if (bytes_to_copy == 0) { + rc = 0; + break; + } + /* Number of frames information copied */ + buf += sizeof(unsigned char); + count -= sizeof(unsigned char); + } else { + offset = audio->out_frame_info[idx][1]; + bytes_to_copy = size; + } + + pr_debug("%s:session id %d: offset=%d nr of frames= %d\n", + __func__, audio->ac->session, + audio->out_frame_info[idx][1], + audio->out_frame_info[idx][0]); + + if (copy_to_user(buf, &data[offset], bytes_to_copy)) { + rc = -EFAULT; + break; + } + count -= bytes_to_copy; + buf += bytes_to_copy; + } else { + pr_err("%s:session id %d: short read data[%pK] bytesavail[%d]bytesrequest[%zd]\n", + __func__, + audio->ac->session, + data, size, count); + } + atomic_dec(&audio->out_count); + q6asm_read(audio->ac); + break; + } + mutex_unlock(&audio->read_lock); + + pr_debug("%s:session id %d: read: %zd bytes\n", __func__, + audio->ac->session, (buf-start)); + if (!rc) { + if (buf > start) + return buf - start; + } + return rc; +} + +static int extract_meta_info(char *buf, unsigned long *msw_ts, + unsigned long *lsw_ts, unsigned int *flags) +{ + struct meta_in *meta = (struct meta_in *)buf; + *msw_ts = meta->ntimestamp.highpart; + *lsw_ts = meta->ntimestamp.lowpart; + *flags = meta->nflags; + return 0; +} + +ssize_t audio_in_write(struct file *file, + const char __user *buf, + size_t count, loff_t *pos) +{ + struct q6audio_in *audio = file->private_data; + const char __user *start = buf; + size_t xfer = 0; + char *cpy_ptr; + int rc = 0; + unsigned char *data; + uint32_t size = 0; + uint32_t idx = 0; + uint32_t nflags = 0; + unsigned long msw_ts = 0; + unsigned long lsw_ts = 0; + uint32_t mfield_size = (audio->buf_cfg.meta_info_enable == 0) ? 0 : + sizeof(struct meta_in); + + pr_debug("%s:session id %d: to write[%zd]\n", __func__, + audio->ac->session, count); + if (audio->reset_event) + return -ENETRESET; + + if (!audio->enabled) + return -EFAULT; + mutex_lock(&audio->write_lock); + + while (count > 0) { + rc = wait_event_interruptible(audio->write_wait, + ((atomic_read(&audio->in_count) > 0) || + (audio->stopped) || + (audio->wflush) || (audio->event_abort))); + + if (audio->event_abort) { + rc = -EIO; + break; + } + + if (rc < 0) + break; + if (audio->stopped || audio->wflush) { + pr_debug("%s: session id %d: stop or flush\n", __func__, + audio->ac->session); + rc = -EBUSY; + break; + } + /* if no PCM data, might have only eos buffer + * such case do not hold cpu buffer + */ + if ((buf == start) && (count == mfield_size)) { + char eos_buf[sizeof(struct meta_in)]; + /* Processing beginning of user buffer */ + if (copy_from_user(eos_buf, buf, mfield_size)) { + rc = -EFAULT; + break; + } + /* Check if EOS flag is set and buffer has + * contains just meta field + */ + extract_meta_info(eos_buf, &msw_ts, &lsw_ts, + &nflags); + buf += mfield_size; + /* send the EOS and return */ + pr_debug("%s:session id %d: send EOS 0x%8x\n", + __func__, + audio->ac->session, nflags); + break; + } + data = (unsigned char *)q6asm_is_cpu_buf_avail(IN, audio->ac, + &size, &idx); + if (!data) { + pr_debug("%s:session id %d: No buf available\n", + __func__, audio->ac->session); + continue; + } + cpy_ptr = data; + if (audio->buf_cfg.meta_info_enable) { + if (buf == start) { + /* Processing beginning of user buffer */ + if (copy_from_user(cpy_ptr, buf, mfield_size)) { + rc = -EFAULT; + break; + } + /* Check if EOS flag is set and buffer has + * contains just meta field + */ + extract_meta_info(cpy_ptr, &msw_ts, &lsw_ts, + &nflags); + buf += mfield_size; + count -= mfield_size; + } else { + pr_debug("%s:session id %d: continuous buffer\n", + __func__, audio->ac->session); + } + } + + xfer = (count > size) ? size : count; + if (copy_from_user(cpy_ptr, buf, xfer)) { + rc = -EFAULT; + break; + } + rc = q6asm_write(audio->ac, xfer, msw_ts, lsw_ts, 0x00); + if (rc < 0) { + rc = -EFAULT; + break; + } + atomic_dec(&audio->in_count); + count -= xfer; + buf += xfer; + } + mutex_unlock(&audio->write_lock); + pr_debug("%s:session id %d: eos_condition 0x%x buf[0x%pK] start[0x%pK]\n", + __func__, audio->ac->session, + nflags, buf, start); + if (nflags & AUD_EOS_SET) { + rc = q6asm_cmd(audio->ac, CMD_EOS); + pr_info("%s:session id %d: eos %d at input\n", __func__, + audio->ac->session, audio->eos_rsp); + } + pr_debug("%s:session id %d: Written %zd Avail Buf[%d]", __func__, + audio->ac->session, (buf - start - mfield_size), + atomic_read(&audio->in_count)); + if (!rc) { + if (buf > start) + return buf - start; + } + return rc; +} + +int audio_in_release(struct inode *inode, struct file *file) +{ + struct q6audio_in *audio = file->private_data; + + pr_info("%s: session id %d\n", __func__, audio->ac->session); + mutex_lock(&audio->lock); + audio_in_disable(audio); + q6asm_audio_client_free(audio->ac); + mutex_unlock(&audio->lock); + kfree(audio->enc_cfg); + kfree(audio->codec_cfg); + kfree(audio); + return 0; +} diff --git a/audio-kernel/dsp/codecs/audio_utils.h b/audio-kernel/dsp/codecs/audio_utils.h new file mode 100644 index 000000000..3ad6903b3 --- /dev/null +++ b/audio-kernel/dsp/codecs/audio_utils.h @@ -0,0 +1,158 @@ +/* Copyright (c) 2010-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 + * 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 +#include +#include "q6audio_common.h" + +#define FRAME_NUM (8) + +#define PCM_BUF_COUNT (2) + +#define AUD_EOS_SET 0x01 +#define TUNNEL_MODE 0x0000 +#define NON_TUNNEL_MODE 0x0001 + +#define NO_BUF_ALLOC 0x00 +#define BUF_ALLOC_IN 0x01 +#define BUF_ALLOC_OUT 0x02 +#define BUF_ALLOC_INOUT 0x03 +#define ALIGN_BUF_SIZE(size) ((size + 4095) & (~4095)) + +struct timestamp { + u32 lowpart; + u32 highpart; +} __packed; + +struct meta_in { + unsigned short offset; + struct timestamp ntimestamp; + unsigned int nflags; +} __packed; + +struct meta_out_dsp { + u32 offset_to_frame; + u32 frame_size; + u32 encoded_pcm_samples; + u32 msw_ts; + u32 lsw_ts; + u32 nflags; +} __packed; + +struct meta_out { + unsigned char num_of_frames; + struct meta_out_dsp meta_out_dsp[]; +} __packed; + +struct q6audio_in { + spinlock_t dsp_lock; + atomic_t in_bytes; + atomic_t in_samples; + + struct mutex lock; + struct mutex read_lock; + struct mutex write_lock; + wait_queue_head_t read_wait; + wait_queue_head_t write_wait; + + struct audio_client *ac; + struct msm_audio_stream_config str_cfg; + void *enc_cfg; + struct msm_audio_buf_cfg buf_cfg; + struct msm_audio_config pcm_cfg; + void *codec_cfg; + + /* number of buffers available to read/write */ + atomic_t in_count; + atomic_t out_count; + + /* first idx: num of frames per buf, second idx: offset to frame */ + uint32_t out_frame_info[FRAME_NUM][2]; + int eos_rsp; + int opened; + int enabled; + int stopped; + int event_abort; + int feedback; /* Flag indicates whether used + * in Non Tunnel mode + */ + int rflush; + int wflush; + int buf_alloc; + uint16_t min_frame_size; + uint16_t max_frames_per_buf; + bool reset_event; + long (*enc_ioctl)(struct file *, unsigned int, unsigned long); + long (*enc_compat_ioctl)(struct file *, unsigned int, unsigned long); +}; + +int audio_in_enable(struct q6audio_in *audio); +int audio_in_disable(struct q6audio_in *audio); +int audio_in_buf_alloc(struct q6audio_in *audio); +long audio_in_ioctl(struct file *file, + unsigned int cmd, unsigned long arg); +#ifdef CONFIG_COMPAT +long audio_in_compat_ioctl(struct file *file, + unsigned int cmd, unsigned long arg); +#else +#define audio_in_compat_ioctl NULL +#endif +ssize_t audio_in_read(struct file *file, char __user *buf, + size_t count, loff_t *pos); +ssize_t audio_in_write(struct file *file, const char __user *buf, + size_t count, loff_t *pos); +int audio_in_release(struct inode *inode, struct file *file); +int audio_in_set_config(struct file *file, struct msm_audio_config *cfg); +int aac_in_init(void); +int amrnb_in_init(void); +int amrwb_in_init(void); +int audio_aac_init(void); +int audio_alac_init(void); +int audio_amrnb_init(void); +int audio_amrwb_init(void); +int audio_amrwbplus_init(void); +int audio_ape_init(void); +int audio_evrc_init(void); +int audio_g711alaw_init(void); +int audio_g711mlaw_init(void); +int audio_effects_init(void); +int audio_mp3_init(void); +int audio_multiaac_init(void); +int audio_qcelp_init(void); +int audio_wma_init(void); +int audio_wmapro_init(void); +int evrc_in_init(void); +int g711alaw_in_init(void); +int g711mlaw_in_init(void); +int qcelp_in_init(void); +void aac_in_exit(void); +void amrnb_in_exit(void); +void amrwb_in_exit(void); +void audio_aac_exit(void); +void audio_alac_exit(void); +void audio_amrnb_exit(void); +void audio_amrwb_exit(void); +void audio_amrwbplus_exit(void); +void audio_ape_exit(void); +void audio_evrc_exit(void); +void audio_g711alaw_exit(void); +void audio_g711mlaw_exit(void); +void audio_effects_exit(void); +void audio_mp3_exit(void); +void audio_multiaac_exit(void); +void audio_qcelp_exit(void); +void audio_wma_exit(void); +void audio_wmapro_exit(void); +void evrc_in_exit(void); +void g711alaw_in_exit(void); +void g711mlaw_in_exit(void); +void qcelp_in_exit(void); diff --git a/audio-kernel/dsp/codecs/audio_utils_aio.c b/audio-kernel/dsp/codecs/audio_utils_aio.c new file mode 100644 index 000000000..e4cb2cffe --- /dev/null +++ b/audio-kernel/dsp/codecs/audio_utils_aio.c @@ -0,0 +1,2150 @@ +/* Copyright (C) 2008 Google, Inc. + * Copyright (C) 2008 HTC Corporation + * Copyright (c) 2009-2018, The Linux Foundation. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "audio_utils_aio.h" +#ifdef CONFIG_USE_DEV_CTRL_VOLUME +#include +#endif /*CONFIG_USE_DEV_CTRL_VOLUME*/ +static DEFINE_MUTEX(lock); +#ifdef CONFIG_DEBUG_FS + +int audio_aio_debug_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +ssize_t audio_aio_debug_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + const int debug_bufmax = 4096; + static char buffer[4096]; + int n = 0; + struct q6audio_aio *audio; + + mutex_lock(&lock); + if (file->private_data != NULL) { + audio = file->private_data; + mutex_lock(&audio->lock); + n = scnprintf(buffer, debug_bufmax, "opened %d\n", + audio->opened); + n += scnprintf(buffer + n, debug_bufmax - n, + "enabled %d\n", audio->enabled); + n += scnprintf(buffer + n, debug_bufmax - n, + "stopped %d\n", audio->stopped); + n += scnprintf(buffer + n, debug_bufmax - n, + "feedback %d\n", audio->feedback); + mutex_unlock(&audio->lock); + /* Following variables are only useful for debugging when + * when playback halts unexpectedly. Thus, no mutual exclusion + * enforced + */ + n += scnprintf(buffer + n, debug_bufmax - n, + "wflush %d\n", audio->wflush); + n += scnprintf(buffer + n, debug_bufmax - n, + "rflush %d\n", audio->rflush); + n += scnprintf(buffer + n, debug_bufmax - n, + "inqueue empty %d\n", + list_empty(&audio->in_queue)); + n += scnprintf(buffer + n, debug_bufmax - n, + "outqueue empty %d\n", + list_empty(&audio->out_queue)); + } + mutex_unlock(&lock); + buffer[n] = 0; + return simple_read_from_buffer(buf, count, ppos, buffer, n); +} +#endif + +static long audio_aio_ioctl(struct file *file, unsigned int cmd, + unsigned long arg); +#ifdef CONFIG_COMPAT +static long audio_aio_compat_ioctl(struct file *file, unsigned int cmd, + unsigned long arg); +#else +#define audio_aio_compat_ioctl NULL +#endif +int insert_eos_buf(struct q6audio_aio *audio, + struct audio_aio_buffer_node *buf_node) +{ + struct dec_meta_out *eos_buf = buf_node->kvaddr; + + pr_debug("%s[%pK]:insert_eos_buf\n", __func__, audio); + eos_buf->num_of_frames = 0xFFFFFFFF; + eos_buf->meta_out_dsp[0].offset_to_frame = 0x0; + eos_buf->meta_out_dsp[0].nflags = AUDIO_DEC_EOS_SET; + return sizeof(struct dec_meta_out) + + sizeof(eos_buf->meta_out_dsp[0]); +} + +/* Routine which updates read buffers of driver/dsp, + * for flush operation as DSP output might not have proper + * value set + */ +static int insert_meta_data_flush(struct q6audio_aio *audio, + struct audio_aio_buffer_node *buf_node) +{ + struct dec_meta_out *meta_data = buf_node->kvaddr; + + meta_data->num_of_frames = 0x0; + meta_data->meta_out_dsp[0].offset_to_frame = 0x0; + meta_data->meta_out_dsp[0].nflags = 0x0; + return sizeof(struct dec_meta_out) + + sizeof(meta_data->meta_out_dsp[0]); +} + +static int audio_aio_ion_lookup_vaddr(struct q6audio_aio *audio, void *addr, + unsigned long len, + struct audio_aio_ion_region **region) +{ + struct audio_aio_ion_region *region_elt; + + int match_count = 0; + + *region = NULL; + + /* returns physical address or zero */ + list_for_each_entry(region_elt, &audio->ion_region_queue, list) { + if (addr >= region_elt->vaddr && + addr < region_elt->vaddr + region_elt->len && + addr + len <= region_elt->vaddr + region_elt->len && + addr + len > addr) { + /* to avoid integer addition overflow */ + + /* offset since we could pass vaddr inside a registered + * ion buffer + */ + + match_count++; + if (!*region) + *region = region_elt; + } + } + + if (match_count > 1) { + pr_err("%s[%pK]:multiple hits for vaddr %pK, len %ld\n", + __func__, audio, addr, len); + list_for_each_entry(region_elt, &audio->ion_region_queue, + list) { + if (addr >= region_elt->vaddr && + addr < region_elt->vaddr + region_elt->len && + addr + len <= region_elt->vaddr + region_elt->len && + addr + len > addr) + pr_err("\t%s[%pK]:%pK, %ld --> %pK\n", + __func__, audio, + region_elt->vaddr, + region_elt->len, + ®ion_elt->paddr); + } + } + + return *region ? 0 : -1; +} + +static phys_addr_t audio_aio_ion_fixup(struct q6audio_aio *audio, void *addr, + unsigned long len, int ref_up, void **kvaddr) +{ + struct audio_aio_ion_region *region; + phys_addr_t paddr; + int ret; + + ret = audio_aio_ion_lookup_vaddr(audio, addr, len, ®ion); + if (ret) { + pr_err("%s[%pK]:lookup (%pK, %ld) failed\n", + __func__, audio, addr, len); + return 0; + } + if (ref_up) + region->ref_cnt++; + else + region->ref_cnt--; + pr_debug("%s[%pK]:found region %pK ref_cnt %d\n", + __func__, audio, region, region->ref_cnt); + paddr = region->paddr + (addr - region->vaddr); + /* provide kernel virtual address for accessing meta information */ + if (kvaddr) + *kvaddr = (void *) (region->kvaddr + (addr - region->vaddr)); + return paddr; +} + +static int audio_aio_pause(struct q6audio_aio *audio) +{ + int rc = -EINVAL; + + pr_debug("%s[%pK], enabled = %d\n", __func__, audio, + audio->enabled); + if (audio->enabled) { + rc = q6asm_cmd(audio->ac, CMD_PAUSE); + if (rc < 0) + pr_err_ratelimited("%s[%pK]: pause cmd failed rc=%d\n", + __func__, audio, rc); + + if (rc == 0) { + /* Send suspend only if pause was successful */ + rc = q6asm_cmd(audio->ac, CMD_SUSPEND); + if (rc < 0) + pr_err_ratelimited("%s[%pK]: suspend cmd failed rc=%d\n", + __func__, audio, rc); + } else + pr_err_ratelimited("%s[%pK]: not sending suspend since pause failed\n", + __func__, audio); + + } else + pr_err("%s[%pK]: Driver not enabled\n", __func__, audio); + return rc; +} + +static int audio_aio_flush(struct q6audio_aio *audio) +{ + int rc = 0; + + if (audio->enabled) { + /* Implicitly issue a pause to the decoder before flushing if + * it is not in pause state + */ + if (!(audio->drv_status & ADRV_STATUS_PAUSE)) { + rc = audio_aio_pause(audio); + if (rc < 0) + pr_err_ratelimited("%s[%pK}: pause cmd failed rc=%d\n", + __func__, audio, + rc); + else + audio->drv_status |= ADRV_STATUS_PAUSE; + } + rc = q6asm_cmd(audio->ac, CMD_FLUSH); + if (rc < 0) + pr_err_ratelimited("%s[%pK]: flush cmd failed rc=%d\n", + __func__, audio, rc); + /* Not in stop state, reenable the stream */ + if (audio->stopped == 0) { + rc = audio_aio_enable(audio); + if (rc) + pr_err_ratelimited("%s[%pK]:audio re-enable failed\n", + __func__, audio); + else { + audio->enabled = 1; + if (audio->drv_status & ADRV_STATUS_PAUSE) + audio->drv_status &= ~ADRV_STATUS_PAUSE; + } + } + } + pr_debug("%s[%pK]:in_bytes %d\n", + __func__, audio, atomic_read(&audio->in_bytes)); + pr_debug("%s[%pK]:in_samples %d\n", + __func__, audio, atomic_read(&audio->in_samples)); + atomic_set(&audio->in_bytes, 0); + atomic_set(&audio->in_samples, 0); + return rc; +} + +static int audio_aio_outport_flush(struct q6audio_aio *audio) +{ + int rc; + + rc = q6asm_cmd(audio->ac, CMD_OUT_FLUSH); + if (rc < 0) + pr_err_ratelimited("%s[%pK}: output port flush cmd failed rc=%d\n", + __func__, audio, rc); + return rc; +} + +/* Write buffer to DSP / Handle Ack from DSP */ +void audio_aio_async_write_ack(struct q6audio_aio *audio, uint32_t token, + uint32_t *payload) +{ + unsigned long flags; + union msm_audio_event_payload event_payload; + struct audio_aio_buffer_node *used_buf; + + /* No active flush in progress */ + if (audio->wflush) + return; + + spin_lock_irqsave(&audio->dsp_lock, flags); + if (list_empty(&audio->out_queue)) { + pr_warn("%s: ignore unexpected event from dsp\n", __func__); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + return; + } + used_buf = list_first_entry(&audio->out_queue, + struct audio_aio_buffer_node, list); + if (token == used_buf->token) { + list_del(&used_buf->list); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + pr_debug("%s[%pK]:consumed buffer\n", __func__, audio); + event_payload.aio_buf = used_buf->buf; + audio_aio_post_event(audio, AUDIO_EVENT_WRITE_DONE, + event_payload); + kfree(used_buf); + if (list_empty(&audio->out_queue) && + (audio->drv_status & ADRV_STATUS_FSYNC)) { + pr_debug("%s[%pK]: list is empty, reached EOS in Tunnel\n", + __func__, audio); + wake_up(&audio->write_wait); + } + } else { + pr_err("%s[%pK]:expected=%x ret=%x\n", + __func__, audio, used_buf->token, token); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + } +} + +/* ------------------- device --------------------- */ +void audio_aio_async_out_flush(struct q6audio_aio *audio) +{ + struct audio_aio_buffer_node *buf_node; + struct list_head *ptr, *next; + union msm_audio_event_payload payload; + unsigned long flags; + + pr_debug("%s[%pK}\n", __func__, audio); + /* EOS followed by flush, EOS response not guranteed, free EOS i/p + * buffer + */ + spin_lock_irqsave(&audio->dsp_lock, flags); + + if (audio->eos_flag && (audio->eos_write_payload.aio_buf.buf_addr)) { + pr_debug("%s[%pK]: EOS followed by flush received,acknowledge eos i/p buffer immediately\n", + __func__, audio); + audio_aio_post_event(audio, AUDIO_EVENT_WRITE_DONE, + audio->eos_write_payload); + memset(&audio->eos_write_payload, 0, + sizeof(union msm_audio_event_payload)); + } + spin_unlock_irqrestore(&audio->dsp_lock, flags); + list_for_each_safe(ptr, next, &audio->out_queue) { + buf_node = list_entry(ptr, struct audio_aio_buffer_node, list); + list_del(&buf_node->list); + payload.aio_buf = buf_node->buf; + audio_aio_post_event(audio, AUDIO_EVENT_WRITE_DONE, payload); + kfree(buf_node); + pr_debug("%s[%pK]: Propagate WRITE_DONE during flush\n", + __func__, audio); + } +} + +void audio_aio_async_in_flush(struct q6audio_aio *audio) +{ + struct audio_aio_buffer_node *buf_node; + struct list_head *ptr, *next; + union msm_audio_event_payload payload; + + pr_debug("%s[%pK]\n", __func__, audio); + list_for_each_safe(ptr, next, &audio->in_queue) { + buf_node = list_entry(ptr, struct audio_aio_buffer_node, list); + list_del(&buf_node->list); + /* Forcefull send o/p eos buffer after flush, if no eos response + * received by dsp even after sending eos command + */ + if ((audio->eos_rsp != 1) && audio->eos_flag) { + pr_debug("%s[%pK]: send eos on o/p buffer during flush\n", + __func__, audio); + payload.aio_buf = buf_node->buf; + payload.aio_buf.data_len = + insert_eos_buf(audio, buf_node); + audio->eos_flag = 0; + } else { + payload.aio_buf = buf_node->buf; + payload.aio_buf.data_len = + insert_meta_data_flush(audio, buf_node); + } + audio_aio_post_event(audio, AUDIO_EVENT_READ_DONE, payload); + kfree(buf_node); + pr_debug("%s[%pK]: Propagate READ_DONE during flush\n", + __func__, audio); + } +} + +int audio_aio_enable(struct q6audio_aio *audio) +{ + /* 2nd arg: 0 -> run immediately + * 3rd arg: 0 -> msw_ts, + * 4th arg: 0 ->lsw_ts + */ + return q6asm_run(audio->ac, 0x00, 0x00, 0x00); +} + +int audio_aio_disable(struct q6audio_aio *audio) +{ + int rc = 0; + + if (audio->opened) { + audio->enabled = 0; + audio->opened = 0; + pr_debug("%s[%pK]: inbytes[%d] insamples[%d]\n", __func__, + audio, atomic_read(&audio->in_bytes), + atomic_read(&audio->in_samples)); + /* Close the session */ + rc = q6asm_cmd(audio->ac, CMD_CLOSE); + if (rc < 0) + pr_err_ratelimited("%s[%pK]:Failed to close the session rc=%d\n", + __func__, audio, rc); + audio->stopped = 1; + wake_up(&audio->write_wait); + wake_up(&audio->cmd_wait); + } + pr_debug("%s[%pK]:enabled[%d]\n", __func__, audio, audio->enabled); + return rc; +} + +void audio_aio_reset_ion_region(struct q6audio_aio *audio) +{ + struct audio_aio_ion_region *region; + struct list_head *ptr, *next; + + list_for_each_safe(ptr, next, &audio->ion_region_queue) { + region = list_entry(ptr, struct audio_aio_ion_region, list); + list_del(®ion->list); + msm_audio_ion_free(region->dma_buf); + kfree(region); + } +} + +void audio_aio_reset_event_queue(struct q6audio_aio *audio) +{ + unsigned long flags; + struct audio_aio_event *drv_evt; + struct list_head *ptr, *next; + + spin_lock_irqsave(&audio->event_queue_lock, flags); + list_for_each_safe(ptr, next, &audio->event_queue) { + drv_evt = list_first_entry(&audio->event_queue, + struct audio_aio_event, list); + list_del(&drv_evt->list); + kfree(drv_evt); + } + list_for_each_safe(ptr, next, &audio->free_event_queue) { + drv_evt = list_first_entry(&audio->free_event_queue, + struct audio_aio_event, list); + list_del(&drv_evt->list); + kfree(drv_evt); + } + spin_unlock_irqrestore(&audio->event_queue_lock, flags); +} + +static void audio_aio_unmap_ion_region(struct q6audio_aio *audio) +{ + struct audio_aio_ion_region *region; + struct list_head *ptr, *next; + int rc = -EINVAL; + + pr_debug("%s[%pK]:\n", __func__, audio); + list_for_each_safe(ptr, next, &audio->ion_region_queue) { + region = list_entry(ptr, struct audio_aio_ion_region, list); + if (region != NULL) { + pr_debug("%s[%pK]: phy_address = 0x%pK\n", + __func__, audio, ®ion->paddr); + rc = q6asm_memory_unmap(audio->ac, + region->paddr, IN); + if (rc < 0) + pr_err("%s[%pK]: memory unmap failed\n", + __func__, audio); + } + } +} + +#ifdef CONFIG_USE_DEV_CTRL_VOLUME + +static void audio_aio_listner(u32 evt_id, union auddev_evt_data *evt_payload, + void *private_data) +{ + struct q6audio_aio *audio = (struct q6audio_aio *) private_data; + int rc = 0; + + switch (evt_id) { + case AUDDEV_EVT_STREAM_VOL_CHG: + audio->volume = evt_payload->session_vol; + pr_debug("%s[%pK]: AUDDEV_EVT_STREAM_VOL_CHG, stream vol %d, enabled = %d\n", + __func__, audio, audio->volume, audio->enabled); + if (audio->enabled == 1) { + if (audio->ac) { + rc = q6asm_set_volume(audio->ac, audio->volume); + if (rc < 0) { + pr_err("%s[%pK]: Send Volume command failed rc=%d\n", + __func__, audio, rc); + } + } + } + break; + default: + pr_err("%s[%pK]:ERROR:wrong event\n", __func__, audio); + break; + } +} + +int register_volume_listener(struct q6audio_aio *audio) +{ + int rc = 0; + + audio->device_events = AUDDEV_EVT_STREAM_VOL_CHG; + audio->drv_status &= ~ADRV_STATUS_PAUSE; + + rc = auddev_register_evt_listner(audio->device_events, + AUDDEV_CLNT_DEC, + audio->ac->session, + audio_aio_listner, + (void *)audio); + if (rc < 0) { + pr_err("%s[%pK]: Event listener failed\n", __func__, audio); + rc = -EACCES; + } + return rc; +} +void unregister_volume_listener(struct q6audio_aio *audio) +{ + auddev_unregister_evt_listner(AUDDEV_CLNT_DEC, audio->ac->session); +} + +int enable_volume_ramp(struct q6audio_aio *audio) +{ + int rc = 0; + struct asm_softpause_params softpause; + struct asm_softvolume_params softvol; + + if (audio->ac == NULL) + return -EINVAL; + pr_debug("%s[%pK]\n", __func__, audio); + softpause.enable = SOFT_PAUSE_ENABLE; + softpause.period = SOFT_PAUSE_PERIOD; + softpause.step = SOFT_PAUSE_STEP; + softpause.rampingcurve = SOFT_PAUSE_CURVE_LINEAR; + + softvol.period = SOFT_VOLUME_PERIOD; + softvol.step = SOFT_VOLUME_STEP; + softvol.rampingcurve = SOFT_VOLUME_CURVE_LINEAR; + + if (softpause.rampingcurve == SOFT_PAUSE_CURVE_LINEAR) + softpause.step = SOFT_PAUSE_STEP_LINEAR; + if (softvol.rampingcurve == SOFT_VOLUME_CURVE_LINEAR) + softvol.step = SOFT_VOLUME_STEP_LINEAR; + rc = q6asm_set_volume(audio->ac, audio->volume); + if (rc < 0) { + pr_err("%s: Send Volume command failed rc=%d\n", + __func__, rc); + return rc; + } + rc = q6asm_set_softpause(audio->ac, &softpause); + if (rc < 0) { + pr_err("%s: Send SoftPause Param failed rc=%d\n", + __func__, rc); + return rc; + } + rc = q6asm_set_softvolume(audio->ac, &softvol); + if (rc < 0) { + pr_err("%s: Send SoftVolume Param failed rc=%d\n", + __func__, rc); + return rc; + } + /* disable mute by default */ + rc = q6asm_set_mute(audio->ac, 0); + if (rc < 0) { + pr_err("%s: Send mute command failed rc=%d\n", + __func__, rc); + return rc; + } + return rc; +} + +#else /*CONFIG_USE_DEV_CTRL_VOLUME*/ +int register_volume_listener(struct q6audio_aio *audio) +{ + return 0;/* do nothing */ +} +void unregister_volume_listener(struct q6audio_aio *audio) +{ + return;/* do nothing */ +} +int enable_volume_ramp(struct q6audio_aio *audio) +{ + return 0; /* do nothing */ +} +#endif /*CONFIG_USE_DEV_CTRL_VOLUME*/ + +int audio_aio_release(struct inode *inode, struct file *file) +{ + struct q6audio_aio *audio = file->private_data; + + pr_debug("%s[%pK]\n", __func__, audio); + mutex_lock(&lock); + mutex_lock(&audio->lock); + mutex_lock(&audio->read_lock); + mutex_lock(&audio->write_lock); + audio->wflush = 1; + if (audio->wakelock_voted && + (audio->audio_ws_mgr != NULL) && + (audio->miscdevice != NULL)) { + audio->wakelock_voted = false; + mutex_lock(&audio->audio_ws_mgr->ws_lock); + if ((audio->audio_ws_mgr->ref_cnt > 0) && + (--audio->audio_ws_mgr->ref_cnt == 0)) { + pm_relax(audio->miscdevice->this_device); + } + mutex_unlock(&audio->audio_ws_mgr->ws_lock); + } + if (audio->enabled) + audio_aio_flush(audio); + audio->wflush = 0; + audio->drv_ops.out_flush(audio); + audio->drv_ops.in_flush(audio); + audio_aio_disable(audio); + audio_aio_unmap_ion_region(audio); + audio_aio_reset_ion_region(audio); + audio->event_abort = 1; + wake_up(&audio->event_wait); + audio_aio_reset_event_queue(audio); + q6asm_audio_client_free(audio->ac); + mutex_unlock(&audio->write_lock); + mutex_unlock(&audio->read_lock); + mutex_unlock(&audio->lock); + mutex_destroy(&audio->lock); + mutex_destroy(&audio->read_lock); + mutex_destroy(&audio->write_lock); + mutex_destroy(&audio->get_event_lock); + unregister_volume_listener(audio); + +#ifdef CONFIG_DEBUG_FS + debugfs_remove(audio->dentry); +#endif + kfree(audio->codec_cfg); + kfree(audio); + file->private_data = NULL; + mutex_unlock(&lock); + return 0; +} + +int audio_aio_fsync(struct file *file, loff_t start, loff_t end, int datasync) +{ + int rc = 0; + struct q6audio_aio *audio = file->private_data; + + if (!audio->enabled || audio->feedback) + return -EINVAL; + + /* Blocking client sends more data */ + mutex_lock(&audio->lock); + audio->drv_status |= ADRV_STATUS_FSYNC; + mutex_unlock(&audio->lock); + + pr_debug("%s[%pK]:\n", __func__, audio); + + audio->eos_rsp = 0; + + pr_debug("%s[%pK]Wait for write done from DSP\n", __func__, audio); + rc = wait_event_interruptible(audio->write_wait, + (list_empty(&audio->out_queue)) || + audio->wflush || audio->stopped); + + if (audio->stopped || audio->wflush) { + pr_debug("%s[%pK]: Audio Flushed or Stopped,this is not EOS\n" + , __func__, audio); + audio->wflush = 0; + rc = -EBUSY; + } + + if (rc < 0) { + pr_err("%s[%pK]: wait event for list_empty failed, rc = %d\n", + __func__, audio, rc); + goto done; + } + + rc = q6asm_cmd(audio->ac, CMD_EOS); + pr_debug("%s[%pK]: EOS cmd sent to DSP\n", __func__, audio); + + if (rc < 0) + pr_err_ratelimited("%s[%pK]: q6asm_cmd failed, rc = %d", + __func__, audio, rc); + + pr_debug("%s[%pK]: wait for RENDERED_EOS from DSP\n" + , __func__, audio); + rc = wait_event_interruptible(audio->write_wait, + (audio->eos_rsp || audio->wflush || + audio->stopped)); + + if (rc < 0) { + pr_err("%s[%pK]: wait event for eos_rsp failed, rc = %d\n", + __func__, audio, rc); + goto done; + } + + if (audio->stopped || audio->wflush) { + audio->wflush = 0; + pr_debug("%s[%pK]: Audio Flushed or Stopped,this is not EOS\n" + , __func__, audio); + rc = -EBUSY; + } + + if (audio->eos_rsp == 1) + pr_debug("%s[%pK]: EOS\n", __func__, audio); + + +done: + mutex_lock(&audio->lock); + audio->drv_status &= ~ADRV_STATUS_FSYNC; + mutex_unlock(&audio->lock); + + return rc; +} + +static int audio_aio_events_pending(struct q6audio_aio *audio) +{ + unsigned long flags; + int empty; + + spin_lock_irqsave(&audio->event_queue_lock, flags); + empty = !list_empty(&audio->event_queue); + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + return empty || audio->event_abort || audio->reset_event; +} + +static long audio_aio_process_event_req_common(struct q6audio_aio *audio, + struct msm_audio_event *usr_evt) +{ + long rc; + struct audio_aio_event *drv_evt = NULL; + int timeout; + unsigned long flags; + + timeout = usr_evt->timeout_ms; + + if (timeout > 0) { + rc = wait_event_interruptible_timeout(audio->event_wait, + audio_aio_events_pending + (audio), + msecs_to_jiffies + (timeout)); + if (rc == 0) + return -ETIMEDOUT; + } else { + rc = wait_event_interruptible(audio->event_wait, + audio_aio_events_pending(audio)); + } + if (rc < 0) + return rc; + + if (audio->reset_event) { + audio->reset_event = false; + pr_err("In SSR, post ENETRESET err\n"); + return -ENETRESET; + } + + if (audio->event_abort) { + audio->event_abort = 0; + return -ENODEV; + } + + rc = 0; + + spin_lock_irqsave(&audio->event_queue_lock, flags); + if (!list_empty(&audio->event_queue)) { + drv_evt = list_first_entry(&audio->event_queue, + struct audio_aio_event, list); + list_del(&drv_evt->list); + } + if (drv_evt) { + usr_evt->event_type = drv_evt->event_type; + usr_evt->event_payload = drv_evt->payload; + list_add_tail(&drv_evt->list, &audio->free_event_queue); + } else { + pr_err("%s[%pK]:Unexpected path\n", __func__, audio); + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + return -EPERM; + } + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + + if (drv_evt->event_type == AUDIO_EVENT_WRITE_DONE) { + pr_debug("%s[%pK]:posted AUDIO_EVENT_WRITE_DONE to user\n", + __func__, audio); + mutex_lock(&audio->write_lock); + audio_aio_ion_fixup(audio, drv_evt->payload.aio_buf.buf_addr, + drv_evt->payload.aio_buf.buf_len, 0, 0); + mutex_unlock(&audio->write_lock); + } else if (drv_evt->event_type == AUDIO_EVENT_READ_DONE) { + pr_debug("%s[%pK]:posted AUDIO_EVENT_READ_DONE to user\n", + __func__, audio); + mutex_lock(&audio->read_lock); + audio_aio_ion_fixup(audio, drv_evt->payload.aio_buf.buf_addr, + drv_evt->payload.aio_buf.buf_len, 0, 0); + mutex_unlock(&audio->read_lock); + } + + /* Some read buffer might be held up in DSP,release all + * Once EOS indicated + */ + if (audio->eos_rsp && !list_empty(&audio->in_queue)) { + pr_debug("%s[%pK]:Send flush command to release read buffers held up in DSP\n", + __func__, audio); + mutex_lock(&audio->lock); + audio_aio_flush(audio); + mutex_unlock(&audio->lock); + } + + return rc; +} + +static long audio_aio_process_event_req(struct q6audio_aio *audio, + void __user *arg) +{ + long rc; + struct msm_audio_event usr_evt; + + if (copy_from_user(&usr_evt, arg, sizeof(struct msm_audio_event))) { + pr_err("%s: copy_from_user failed\n", __func__); + return -EFAULT; + } + + rc = audio_aio_process_event_req_common(audio, &usr_evt); + + if (copy_to_user(arg, &usr_evt, sizeof(usr_evt))) { + pr_err("%s: copy_to_user failed\n", __func__); + rc = -EFAULT; + } + return rc; +} + +#ifdef CONFIG_COMPAT + +struct msm_audio_aio_buf32 { + compat_uptr_t buf_addr; + u32 buf_len; + u32 data_len; + compat_uptr_t private_data; + u16 mfield_sz; /*only useful for data has meta field */ +}; + +struct msm_audio_bitstream_info32 { + u32 codec_type; + u32 chan_info; + u32 sample_rate; + u32 bit_stream_info; + u32 bit_rate; + u32 unused[3]; +}; + +struct msm_audio_bitstream_error_info32 { + u32 dec_id; + u32 err_msg_indicator; + u32 err_type; +}; + +union msm_audio_event_payload32 { + struct msm_audio_aio_buf32 aio_buf; + struct msm_audio_bitstream_info32 stream_info; + struct msm_audio_bitstream_error_info32 error_info; + s32 reserved; +}; + +struct msm_audio_event32 { + s32 event_type; + s32 timeout_ms; + union msm_audio_event_payload32 event_payload; +}; + +static long audio_aio_process_event_req_compat(struct q6audio_aio *audio, + void __user *arg) +{ + long rc; + struct msm_audio_event32 usr_evt_32; + struct msm_audio_event usr_evt; + memset(&usr_evt, 0, sizeof(struct msm_audio_event)); + + if (copy_from_user(&usr_evt_32, arg, + sizeof(struct msm_audio_event32))) { + pr_err("%s: copy_from_user failed\n", __func__); + return -EFAULT; + } + usr_evt.timeout_ms = usr_evt_32.timeout_ms; + + rc = audio_aio_process_event_req_common(audio, &usr_evt); + if (rc < 0) { + pr_err("%s: audio process event failed, rc = %ld", + __func__, rc); + return rc; + } + + usr_evt_32.event_type = usr_evt.event_type; + switch (usr_evt_32.event_type) { + case AUDIO_EVENT_SUSPEND: + case AUDIO_EVENT_RESUME: + case AUDIO_EVENT_WRITE_DONE: + case AUDIO_EVENT_READ_DONE: + usr_evt_32.event_payload.aio_buf.buf_addr = + ptr_to_compat(usr_evt.event_payload.aio_buf.buf_addr); + usr_evt_32.event_payload.aio_buf.buf_len = + usr_evt.event_payload.aio_buf.buf_len; + usr_evt_32.event_payload.aio_buf.data_len = + usr_evt.event_payload.aio_buf.data_len; + usr_evt_32.event_payload.aio_buf.private_data = + ptr_to_compat(usr_evt.event_payload.aio_buf.private_data); + usr_evt_32.event_payload.aio_buf.mfield_sz = + usr_evt.event_payload.aio_buf.mfield_sz; + break; + case AUDIO_EVENT_STREAM_INFO: + usr_evt_32.event_payload.stream_info.codec_type = + usr_evt.event_payload.stream_info.codec_type; + usr_evt_32.event_payload.stream_info.chan_info = + usr_evt.event_payload.stream_info.chan_info; + usr_evt_32.event_payload.stream_info.sample_rate = + usr_evt.event_payload.stream_info.sample_rate; + usr_evt_32.event_payload.stream_info.bit_stream_info = + usr_evt.event_payload.stream_info.bit_stream_info; + usr_evt_32.event_payload.stream_info.bit_rate = + usr_evt.event_payload.stream_info.bit_rate; + break; + case AUDIO_EVENT_BITSTREAM_ERROR_INFO: + usr_evt_32.event_payload.error_info.dec_id = + usr_evt.event_payload.error_info.dec_id; + usr_evt_32.event_payload.error_info.err_msg_indicator = + usr_evt.event_payload.error_info.err_msg_indicator; + usr_evt_32.event_payload.error_info.err_type = + usr_evt.event_payload.error_info.err_type; + break; + default: + pr_debug("%s: unknown audio event type = %d rc = %ld", + __func__, usr_evt_32.event_type, rc); + return rc; + } + if (copy_to_user(arg, &usr_evt_32, sizeof(usr_evt_32))) { + pr_err("%s: copy_to_user failed\n", __func__); + rc = -EFAULT; + } + return rc; +} +#endif + +static int audio_aio_ion_check(struct q6audio_aio *audio, + void *vaddr, unsigned long len) +{ + struct audio_aio_ion_region *region_elt; + struct audio_aio_ion_region t = {.vaddr = vaddr, .len = len }; + + list_for_each_entry(region_elt, &audio->ion_region_queue, list) { + if (CONTAINS(region_elt, &t) || CONTAINS(&t, region_elt) || + OVERLAPS(region_elt, &t)) { + pr_err("%s[%pK]:region (vaddr %pK len %ld) clashes with registered region (vaddr %pK paddr %pK len %ld)\n", + __func__, audio, vaddr, len, + region_elt->vaddr, + ®ion_elt->paddr, region_elt->len); + return -EINVAL; + } + } + + return 0; +} + +static int audio_aio_ion_add(struct q6audio_aio *audio, + struct msm_audio_ion_info *info) +{ + dma_addr_t paddr = 0; + size_t len = 0; + struct audio_aio_ion_region *region; + int rc = -EINVAL; + struct dma_buf *dma_buf = NULL; + unsigned long ionflag; + void *kvaddr = NULL; + + pr_debug("%s[%pK]:\n", __func__, audio); + region = kmalloc(sizeof(*region), GFP_KERNEL); + + if (!region) { + rc = -ENOMEM; + goto end; + } + + rc = msm_audio_ion_import(&dma_buf, info->fd, &ionflag, + 0, &paddr, &len, &kvaddr); + if (rc) { + pr_err("%s: msm audio ion alloc failed\n", __func__); + goto import_error; + } + + rc = audio_aio_ion_check(audio, info->vaddr, len); + if (rc < 0) { + pr_err("%s: audio_aio_ion_check failed\n", __func__); + goto ion_error; + } + + region->dma_buf = dma_buf; + region->vaddr = info->vaddr; + region->fd = info->fd; + region->paddr = paddr; + region->kvaddr = kvaddr; + region->len = len; + region->ref_cnt = 0; + pr_debug("%s[%pK]:add region paddr %pK vaddr %pK, len %lu kvaddr %pK\n", + __func__, audio, + ®ion->paddr, region->vaddr, region->len, + region->kvaddr); + list_add_tail(®ion->list, &audio->ion_region_queue); + rc = q6asm_memory_map(audio->ac, paddr, IN, len, 1); + if (rc < 0) { + pr_err("%s[%pK]: memory map failed\n", __func__, audio); + goto mmap_error; + } else { + goto end; + } +mmap_error: + list_del(®ion->list); +ion_error: + msm_audio_ion_free(dma_buf); +import_error: + kfree(region); +end: + return rc; +} + +static int audio_aio_ion_remove(struct q6audio_aio *audio, + struct msm_audio_ion_info *info) +{ + struct audio_aio_ion_region *region; + struct list_head *ptr, *next; + int rc = -EINVAL; + + pr_debug("%s[%pK]:info fd %d vaddr %pK\n", + __func__, audio, info->fd, info->vaddr); + + list_for_each_safe(ptr, next, &audio->ion_region_queue) { + region = list_entry(ptr, struct audio_aio_ion_region, list); + + if ((region->fd == info->fd) && + (region->vaddr == info->vaddr)) { + if (region->ref_cnt) { + pr_debug("%s[%pK]:region %pK in use ref_cnt %d\n", + __func__, audio, region, + region->ref_cnt); + break; + } + pr_debug("%s[%pK]:remove region fd %d vaddr %pK\n", + __func__, audio, info->fd, info->vaddr); + rc = q6asm_memory_unmap(audio->ac, + region->paddr, IN); + if (rc < 0) + pr_err("%s[%pK]: memory unmap failed\n", + __func__, audio); + + list_del(®ion->list); + msm_audio_ion_free(region->dma_buf); + kfree(region); + rc = 0; + break; + } + } + + return rc; +} + +static int audio_aio_async_write(struct q6audio_aio *audio, + struct audio_aio_buffer_node *buf_node) +{ + int rc; + struct audio_client *ac; + struct audio_aio_write_param param; + + memset(¶m, 0, sizeof(param)); + + if (!audio || !buf_node) { + pr_err("%s NULL pointer audio=[0x%pK], buf_node=[0x%pK]\n", + __func__, audio, buf_node); + return -EINVAL; + } + pr_debug("%s[%pK]: Send write buff %pK phy %pK len %d meta_enable = %d\n", + __func__, audio, buf_node, &buf_node->paddr, + buf_node->buf.data_len, + audio->buf_cfg.meta_info_enable); + pr_debug("%s[%pK]: flags = 0x%x\n", __func__, audio, + buf_node->meta_info.meta_in.nflags); + + ac = audio->ac; + /* Offset with appropriate meta */ + if (audio->feedback) { + /* Non Tunnel mode */ + param.paddr = buf_node->paddr + sizeof(struct dec_meta_in); + param.len = buf_node->buf.data_len - sizeof(struct dec_meta_in); + } else { + /* Tunnel mode */ + param.paddr = buf_node->paddr; + param.len = buf_node->buf.data_len; + } + param.msw_ts = buf_node->meta_info.meta_in.ntimestamp.highpart; + param.lsw_ts = buf_node->meta_info.meta_in.ntimestamp.lowpart; + param.flags = buf_node->meta_info.meta_in.nflags; + /* If no meta_info enaled, indicate no time stamp valid */ + if (!audio->buf_cfg.meta_info_enable) + param.flags = 0xFF00; + + if (buf_node->meta_info.meta_in.nflags & AUDIO_DEC_EOF_SET) + param.flags |= AUDIO_DEC_EOF_SET; + + param.uid = ac->session; + /* Read command will populate session id as token */ + buf_node->token = ac->session; + rc = q6asm_async_write(ac, ¶m); + if (rc < 0) + pr_err_ratelimited("%s[%pK]:failed\n", __func__, audio); + return rc; +} + +void audio_aio_post_event(struct q6audio_aio *audio, int type, + union msm_audio_event_payload payload) +{ + struct audio_aio_event *e_node = NULL; + unsigned long flags; + + spin_lock_irqsave(&audio->event_queue_lock, flags); + + if (!list_empty(&audio->free_event_queue)) { + e_node = list_first_entry(&audio->free_event_queue, + struct audio_aio_event, list); + list_del(&e_node->list); + } else { + e_node = kmalloc(sizeof(struct audio_aio_event), GFP_ATOMIC); + if (!e_node) { + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + return; + } + } + + e_node->event_type = type; + e_node->payload = payload; + + list_add_tail(&e_node->list, &audio->event_queue); + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + wake_up(&audio->event_wait); +} + +static int audio_aio_async_read(struct q6audio_aio *audio, + struct audio_aio_buffer_node *buf_node) +{ + struct audio_client *ac; + struct audio_aio_read_param param; + int rc; + + pr_debug("%s[%pK]: Send read buff %pK phy %pK len %d\n", + __func__, audio, buf_node, + &buf_node->paddr, buf_node->buf.buf_len); + ac = audio->ac; + /* Provide address so driver can append nr frames information */ + param.paddr = buf_node->paddr + + sizeof(struct dec_meta_out); + param.len = buf_node->buf.buf_len - + sizeof(struct dec_meta_out); + param.uid = ac->session; + /* Write command will populate session_id as token */ + buf_node->token = ac->session; + rc = q6asm_async_read(ac, ¶m); + if (rc < 0 && rc != -ENETRESET) + pr_err_ratelimited("%s[%pK]:failed\n", __func__, audio); + return rc; +} + +static int audio_aio_buf_add_shared(struct q6audio_aio *audio, u32 dir, + struct audio_aio_buffer_node *buf_node) +{ + unsigned long flags; + int ret = 0; + + pr_debug("%s[%pK]:node %pK dir %x buf_addr %pK buf_len %d data_len %d\n", + __func__, audio, buf_node, dir, buf_node->buf.buf_addr, + buf_node->buf.buf_len, buf_node->buf.data_len); + buf_node->paddr = audio_aio_ion_fixup(audio, buf_node->buf.buf_addr, + buf_node->buf.buf_len, 1, + &buf_node->kvaddr); + if (dir) { + /* write */ + if (!buf_node->paddr || + (buf_node->paddr & 0x1) || + (!audio->feedback && !buf_node->buf.data_len)) { + kfree(buf_node); + return -EINVAL; + } + ret = extract_meta_out_info(audio, buf_node, 1); + if (ret) { + pr_debug("%s: extract meta failed with %d\n", + __func__, ret); + kfree(buf_node); + return ret; + } + /* Not a EOS buffer */ + if (!(buf_node->meta_info.meta_in.nflags & AUDIO_DEC_EOS_SET)) { + spin_lock_irqsave(&audio->dsp_lock, flags); + ret = audio_aio_async_write(audio, buf_node); + /* EOS buffer handled in driver */ + list_add_tail(&buf_node->list, &audio->out_queue); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + } else if (buf_node->meta_info.meta_in.nflags + & AUDIO_DEC_EOS_SET) { + if (!audio->wflush) { + pr_debug("%s[%pK]:Send EOS cmd at i/p\n", + __func__, audio); + /* Driver will forcefully post writedone event + * once eos ack recived from DSP + */ + audio->eos_write_payload.aio_buf = + buf_node->buf; + audio->eos_flag = 1; + audio->eos_rsp = 0; + q6asm_cmd(audio->ac, CMD_EOS); + kfree(buf_node); + } else { /* Flush in progress, send back i/p + * EOS buffer as is + */ + union msm_audio_event_payload event_payload; + + event_payload.aio_buf = buf_node->buf; + audio_aio_post_event(audio, + AUDIO_EVENT_WRITE_DONE, + event_payload); + kfree(buf_node); + } + } + } else { + /* read */ + if (!buf_node->paddr || + (buf_node->paddr & 0x1) || + (buf_node->buf.buf_len < PCM_BUFSZ_MIN)) { + kfree(buf_node); + return -EINVAL; + } + /* No EOS reached */ + if (!audio->eos_rsp) { + spin_lock_irqsave(&audio->dsp_lock, flags); + ret = audio_aio_async_read(audio, buf_node); + /* EOS buffer handled in driver */ + list_add_tail(&buf_node->list, &audio->in_queue); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + } + /* EOS reached at input side fake all upcoming read buffer to + * indicate the same + */ + else { + union msm_audio_event_payload event_payload; + + event_payload.aio_buf = buf_node->buf; + event_payload.aio_buf.data_len = + insert_eos_buf(audio, buf_node); + pr_debug("%s[%pK]: propagate READ_DONE as EOS done\n", + __func__, audio); + audio_aio_post_event(audio, AUDIO_EVENT_READ_DONE, + event_payload); + kfree(buf_node); + } + } + return ret; +} +#ifdef CONFIG_COMPAT +static int audio_aio_buf_add_compat(struct q6audio_aio *audio, u32 dir, + void __user *arg) +{ + struct audio_aio_buffer_node *buf_node; + struct msm_audio_aio_buf32 aio_buf_32; + + buf_node = kzalloc(sizeof(*buf_node), GFP_KERNEL); + + if (!buf_node) + return -ENOMEM; + + if (copy_from_user(&aio_buf_32, arg, sizeof(aio_buf_32))) { + kfree(buf_node); + pr_err("%s: copy_from_user failed\n", __func__); + return -EFAULT; + } + + buf_node->buf.buf_addr = compat_ptr(aio_buf_32.buf_addr); + buf_node->buf.buf_len = aio_buf_32.buf_len; + buf_node->buf.data_len = aio_buf_32.data_len; + buf_node->buf.private_data = compat_ptr(aio_buf_32.private_data); + buf_node->buf.mfield_sz = aio_buf_32.mfield_sz; + + return audio_aio_buf_add_shared(audio, dir, buf_node); +} +#endif + +static int audio_aio_buf_add(struct q6audio_aio *audio, u32 dir, + void __user *arg) +{ + struct audio_aio_buffer_node *buf_node; + + buf_node = kzalloc(sizeof(*buf_node), GFP_KERNEL); + + if (!buf_node) + return -ENOMEM; + + if (copy_from_user(&buf_node->buf, arg, sizeof(buf_node->buf))) { + kfree(buf_node); + pr_err("%s: copy_from_user failed\n", __func__); + return -EFAULT; + } + + return audio_aio_buf_add_shared(audio, dir, buf_node); +} + +void audio_aio_ioport_reset(struct q6audio_aio *audio) +{ + if (audio->drv_status & ADRV_STATUS_AIO_INTF) { + /* If fsync is in progress, make sure + * return value of fsync indicates + * abort due to flush + */ + if (audio->drv_status & ADRV_STATUS_FSYNC) { + pr_debug("%s[%pK]:fsync in progress\n", + __func__, audio); + audio->drv_ops.out_flush(audio); + } else + audio->drv_ops.out_flush(audio); + if (audio->feedback == NON_TUNNEL_MODE) + audio->drv_ops.in_flush(audio); + } +} + +int audio_aio_open(struct q6audio_aio *audio, struct file *file) +{ + int rc = 0; + int i; + struct audio_aio_event *e_node = NULL; + struct list_head *ptr, *next; + + /* Settings will be re-config at AUDIO_SET_CONFIG, + * but at least we need to have initial config + */ + audio->str_cfg.buffer_size = FRAME_SIZE; + audio->str_cfg.buffer_count = FRAME_NUM; + audio->pcm_cfg.buffer_count = PCM_BUF_COUNT; + audio->pcm_cfg.sample_rate = 48000; + audio->pcm_cfg.channel_count = 2; + + /* Only AIO interface */ + if (file->f_flags & O_NONBLOCK) { + pr_debug("%s[%pK]:set to aio interface\n", __func__, audio); + audio->drv_status |= ADRV_STATUS_AIO_INTF; + audio->drv_ops.out_flush = audio_aio_async_out_flush; + audio->drv_ops.in_flush = audio_aio_async_in_flush; + q6asm_set_io_mode(audio->ac, ASYNC_IO_MODE); + } else { + pr_err("%s[%pK]:SIO interface not supported\n", + __func__, audio); + rc = -EACCES; + goto fail; + } + + /* Initialize all locks of audio instance */ + mutex_init(&audio->lock); + mutex_init(&audio->read_lock); + mutex_init(&audio->write_lock); + mutex_init(&audio->get_event_lock); + spin_lock_init(&audio->dsp_lock); + spin_lock_init(&audio->event_queue_lock); + init_waitqueue_head(&audio->cmd_wait); + init_waitqueue_head(&audio->write_wait); + init_waitqueue_head(&audio->event_wait); + INIT_LIST_HEAD(&audio->out_queue); + INIT_LIST_HEAD(&audio->in_queue); + INIT_LIST_HEAD(&audio->ion_region_queue); + INIT_LIST_HEAD(&audio->free_event_queue); + INIT_LIST_HEAD(&audio->event_queue); + + audio->drv_ops.out_flush(audio); + audio->opened = 1; + audio->reset_event = false; + file->private_data = audio; + audio->codec_ioctl = audio_aio_ioctl; + audio->codec_compat_ioctl = audio_aio_compat_ioctl; + for (i = 0; i < AUDIO_EVENT_NUM; i++) { + e_node = kmalloc(sizeof(struct audio_aio_event), GFP_KERNEL); + if (e_node) + list_add_tail(&e_node->list, &audio->free_event_queue); + else { + rc = -ENOMEM; + goto cleanup; + } + } + + rc = register_volume_listener(audio); + if (rc < 0) + goto cleanup; + + return 0; +cleanup: + list_for_each_safe(ptr, next, &audio->free_event_queue) { + e_node = list_first_entry(&audio->free_event_queue, + struct audio_aio_event, list); + list_del(&e_node->list); + kfree(e_node); + } +fail: + return rc; +} + +static long audio_aio_shared_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct q6audio_aio *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_ABORT_GET_EVENT: { + audio->event_abort = 1; + wake_up(&audio->event_wait); + break; + } + case AUDIO_OUTPORT_FLUSH: { + pr_debug("%s[%pK]:AUDIO_OUTPORT_FLUSH\n", __func__, audio); + mutex_lock(&audio->read_lock); + rc = audio_aio_outport_flush(audio); + if (rc < 0) { + pr_err_ratelimited("%s[%pK]: AUDIO_OUTPORT_FLUSH failed\n", + __func__, audio); + rc = -EINTR; + } + mutex_unlock(&audio->read_lock); + break; + } + case AUDIO_STOP: { + pr_debug("%s[%pK]: AUDIO_STOP session_id[%d]\n", __func__, + audio, audio->ac->session); + mutex_lock(&audio->lock); + audio->stopped = 1; + rc = audio_aio_flush(audio); + if (rc < 0) { + pr_err_ratelimited("%s[%pK]:Audio Stop procedure failed rc=%d\n", + __func__, audio, rc); + mutex_unlock(&audio->lock); + break; + } + audio->enabled = 0; + audio->drv_status &= ~ADRV_STATUS_PAUSE; + if (audio->drv_status & ADRV_STATUS_FSYNC) { + pr_debug("%s[%pK] Waking up the audio_aio_fsync\n", + __func__, audio); + wake_up(&audio->write_wait); + } + mutex_unlock(&audio->lock); + break; + } + case AUDIO_PAUSE: { + pr_debug("%s[%pK]:AUDIO_PAUSE %ld\n", __func__, audio, arg); + mutex_lock(&audio->lock); + if (arg == 1) { + rc = audio_aio_pause(audio); + if (rc < 0) { + pr_err_ratelimited("%s[%pK]: pause FAILED rc=%d\n", + __func__, audio, rc); + mutex_unlock(&audio->lock); + break; + } + audio->drv_status |= ADRV_STATUS_PAUSE; + } else if (arg == 0) { + if (audio->drv_status & ADRV_STATUS_PAUSE) { + rc = audio_aio_enable(audio); + if (rc) + pr_err_ratelimited("%s[%pK]: audio enable failed\n", + __func__, audio); + else { + audio->drv_status &= ~ADRV_STATUS_PAUSE; + audio->enabled = 1; + } + } + } + mutex_unlock(&audio->lock); + break; + } + case AUDIO_FLUSH: { + pr_debug("%s[%pK]: AUDIO_FLUSH sessionid[%d]\n", __func__, + audio, audio->ac->session); + mutex_lock(&audio->lock); + audio->rflush = 1; + audio->wflush = 1; + if (audio->drv_status & ADRV_STATUS_FSYNC) { + pr_debug("%s[%pK] Waking up the audio_aio_fsync\n", + __func__, audio); + wake_up(&audio->write_wait); + } + /* Flush DSP */ + rc = audio_aio_flush(audio); + /* Flush input / Output buffer in software*/ + audio_aio_ioport_reset(audio); + if (rc < 0) { + pr_err_ratelimited("%s[%pK]:AUDIO_FLUSH interrupted\n", + __func__, audio); + rc = -EINTR; + } else { + audio->rflush = 0; + if (audio->drv_status & ADRV_STATUS_FSYNC) + wake_up(&audio->write_wait); + else + audio->wflush = 0; + + } + audio->eos_flag = 0; + audio->eos_rsp = 0; + mutex_unlock(&audio->lock); + break; + } + case AUDIO_GET_SESSION_ID: { + mutex_lock(&audio->lock); + if (copy_to_user((void *)arg, &audio->ac->session, + sizeof(u16))) { + pr_err_ratelimited("%s: copy_to_user for AUDIO_GET_SESSION_ID failed\n", + __func__); + rc = -EFAULT; + } + mutex_unlock(&audio->lock); + break; + } + case AUDIO_PM_AWAKE: { + if ((audio->audio_ws_mgr == NULL) || + (audio->miscdevice == NULL)) { + pr_err_ratelimited("%s[%pK]: invalid ws_mgr or miscdevice", + __func__, audio); + rc = -EACCES; + break; + } + pr_debug("%s[%pK]:AUDIO_PM_AWAKE\n", __func__, audio); + mutex_lock(&audio->lock); + if (!audio->wakelock_voted) { + audio->wakelock_voted = true; + mutex_lock(&audio->audio_ws_mgr->ws_lock); + if (audio->audio_ws_mgr->ref_cnt++ == 0) + pm_stay_awake(audio->miscdevice->this_device); + mutex_unlock(&audio->audio_ws_mgr->ws_lock); + } + mutex_unlock(&audio->lock); + break; + } + case AUDIO_PM_RELAX: { + if ((audio->audio_ws_mgr == NULL) || + (audio->miscdevice == NULL)) { + pr_err_ratelimited("%s[%pK]: invalid ws_mgr or miscdevice", + __func__, audio); + rc = -EACCES; + break; + } + pr_debug("%s[%pK]:AUDIO_PM_RELAX\n", __func__, audio); + mutex_lock(&audio->lock); + if (audio->wakelock_voted) { + audio->wakelock_voted = false; + mutex_lock(&audio->audio_ws_mgr->ws_lock); + if ((audio->audio_ws_mgr->ref_cnt > 0) && + (--audio->audio_ws_mgr->ref_cnt == 0)) { + pm_relax(audio->miscdevice->this_device); + } + mutex_unlock(&audio->audio_ws_mgr->ws_lock); + } + mutex_unlock(&audio->lock); + break; + } + default: + pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd); + rc = -EINVAL; + } + return rc; + + +} + +static long audio_aio_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct q6audio_aio *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_ABORT_GET_EVENT: + case AUDIO_OUTPORT_FLUSH: + case AUDIO_STOP: + case AUDIO_PAUSE: + case AUDIO_FLUSH: + case AUDIO_GET_SESSION_ID: + case AUDIO_PM_AWAKE: + case AUDIO_PM_RELAX: + rc = audio_aio_shared_ioctl(file, cmd, arg); + break; + case AUDIO_GET_STATS: { + struct msm_audio_stats stats; + uint64_t timestamp; + + memset(&stats, 0, sizeof(struct msm_audio_stats)); + stats.byte_count = atomic_read(&audio->in_bytes); + stats.sample_count = atomic_read(&audio->in_samples); + rc = q6asm_get_session_time(audio->ac, ×tamp); + if (rc >= 0) + memcpy(&stats.unused[0], ×tamp, sizeof(timestamp)); + else + pr_debug("Error while getting timestamp\n"); + if (copy_to_user((void *)arg, &stats, sizeof(stats))) { + pr_err("%s: copy_frm_user for AUDIO_GET_STATS failed\n", + __func__); + rc = -EFAULT; + } + break; + } + case AUDIO_GET_EVENT: { + pr_debug("%s[%pK]:AUDIO_GET_EVENT\n", __func__, audio); + if (mutex_trylock(&audio->get_event_lock)) { + rc = audio_aio_process_event_req(audio, + (void __user *)arg); + mutex_unlock(&audio->get_event_lock); + } else + rc = -EBUSY; + break; + } + case AUDIO_ASYNC_WRITE: { + mutex_lock(&audio->write_lock); + if (audio->drv_status & ADRV_STATUS_FSYNC) + rc = -EBUSY; + else { + if (audio->enabled) + rc = audio_aio_buf_add(audio, 1, + (void __user *)arg); + else + rc = -EPERM; + } + mutex_unlock(&audio->write_lock); + break; + } + case AUDIO_ASYNC_READ: { + mutex_lock(&audio->read_lock); + if (audio->feedback) + rc = audio_aio_buf_add(audio, 0, + (void __user *)arg); + else + rc = -EPERM; + mutex_unlock(&audio->read_lock); + break; + } + + case AUDIO_GET_STREAM_CONFIG: { + struct msm_audio_stream_config cfg; + + mutex_lock(&audio->lock); + memset(&cfg, 0, sizeof(cfg)); + cfg.buffer_size = audio->str_cfg.buffer_size; + cfg.buffer_count = audio->str_cfg.buffer_count; + pr_debug("%s[%pK]:GET STREAM CFG %d %d\n", + __func__, audio, cfg.buffer_size, cfg.buffer_count); + if (copy_to_user((void *)arg, &cfg, sizeof(cfg))) { + pr_err( + "%s: copy_to_user for AUDIO_GET_STREAM_CONFIG failed\n", + __func__); + rc = -EFAULT; + } + mutex_unlock(&audio->lock); + break; + } + case AUDIO_SET_STREAM_CONFIG: { + struct msm_audio_stream_config cfg; + + pr_debug("%s[%pK]:SET STREAM CONFIG\n", __func__, audio); + mutex_lock(&audio->lock); + if (copy_from_user(&cfg, (void *)arg, sizeof(cfg))) { + pr_err( + "%s: copy_from_user for AUDIO_SET_STREAM_CONFIG failed\n", + __func__); + rc = -EFAULT; + mutex_unlock(&audio->lock); + break; + } + audio->str_cfg.buffer_size = FRAME_SIZE; + audio->str_cfg.buffer_count = FRAME_NUM; + rc = 0; + mutex_unlock(&audio->lock); + break; + } + case AUDIO_GET_CONFIG: { + struct msm_audio_config cfg; + + mutex_lock(&audio->lock); + if (copy_to_user((void *)arg, &audio->pcm_cfg, sizeof(cfg))) { + pr_err( + "%s: copy_to_user for AUDIO_GET_CONFIG failed\n", + __func__); + rc = -EFAULT; + } + mutex_unlock(&audio->lock); + break; + } + case AUDIO_SET_CONFIG: { + struct msm_audio_config config; + + pr_debug("%s[%pK]:AUDIO_SET_CONFIG\n", __func__, audio); + mutex_lock(&audio->lock); + if (copy_from_user(&config, (void *)arg, sizeof(config))) { + pr_err( + "%s: copy_from_user for AUDIO_SET_CONFIG failed\n", + __func__); + rc = -EFAULT; + mutex_unlock(&audio->lock); + break; + } + if (audio->feedback != NON_TUNNEL_MODE) { + pr_err("%s[%pK]:Not sufficient permission to change the playback mode\n", + __func__, audio); + rc = -EACCES; + mutex_unlock(&audio->lock); + break; + } + if ((config.buffer_count > PCM_BUF_COUNT) || + (config.buffer_count == 1)) + config.buffer_count = PCM_BUF_COUNT; + + if (config.buffer_size < PCM_BUFSZ_MIN) + config.buffer_size = PCM_BUFSZ_MIN; + + audio->pcm_cfg.buffer_count = config.buffer_count; + audio->pcm_cfg.buffer_size = config.buffer_size; + audio->pcm_cfg.channel_count = config.channel_count; + audio->pcm_cfg.sample_rate = config.sample_rate; + rc = 0; + mutex_unlock(&audio->lock); + break; + } + case AUDIO_SET_BUF_CFG: { + struct msm_audio_buf_cfg cfg; + + mutex_lock(&audio->lock); + if (copy_from_user(&cfg, (void *)arg, sizeof(cfg))) { + pr_err( + "%s: copy_from_user for AUDIO_GET_BUF CONFIG failed\n", + __func__); + rc = -EFAULT; + mutex_unlock(&audio->lock); + break; + } + if ((audio->feedback == NON_TUNNEL_MODE) && + !cfg.meta_info_enable) { + rc = -EFAULT; + mutex_unlock(&audio->lock); + break; + } + + audio->buf_cfg.meta_info_enable = cfg.meta_info_enable; + pr_debug("%s[%pK]:session id %d: Set-buf-cfg: meta[%d]", + __func__, audio, + audio->ac->session, cfg.meta_info_enable); + mutex_unlock(&audio->lock); + break; + } + case AUDIO_GET_BUF_CFG: { + pr_debug("%s[%pK]:session id %d: Get-buf-cfg: meta[%d] framesperbuf[%d]\n", + __func__, audio, + audio->ac->session, audio->buf_cfg.meta_info_enable, + audio->buf_cfg.frames_per_buf); + + mutex_lock(&audio->lock); + if (copy_to_user((void *)arg, &audio->buf_cfg, + sizeof(struct msm_audio_buf_cfg))) { + pr_err( + "%s: copy_to_user for AUDIO_GET_BUF_CONFIG failed\n", + __func__); + rc = -EFAULT; + } + mutex_unlock(&audio->lock); + break; + } + case AUDIO_REGISTER_ION: { + struct msm_audio_ion_info info; + + pr_debug("%s[%pK]:AUDIO_REGISTER_ION\n", __func__, audio); + mutex_lock(&audio->lock); + if (copy_from_user(&info, (void *)arg, sizeof(info))) { + pr_err( + "%s: copy_from_user for AUDIO_REGISTER_ION failed\n", + __func__); + rc = -EFAULT; + } else { + mutex_lock(&audio->read_lock); + mutex_lock(&audio->write_lock); + rc = audio_aio_ion_add(audio, &info); + mutex_unlock(&audio->write_lock); + mutex_unlock(&audio->read_lock); + } + mutex_unlock(&audio->lock); + break; + } + case AUDIO_DEREGISTER_ION: { + struct msm_audio_ion_info info; + + mutex_lock(&audio->lock); + pr_debug("%s[%pK]:AUDIO_DEREGISTER_ION\n", __func__, audio); + if (copy_from_user(&info, (void *)arg, sizeof(info))) { + pr_err( + "%s: copy_from_user for AUDIO_DEREGISTER_ION failed\n", + __func__); + rc = -EFAULT; + } else { + mutex_lock(&audio->read_lock); + mutex_lock(&audio->write_lock); + rc = audio_aio_ion_remove(audio, &info); + mutex_unlock(&audio->write_lock); + mutex_unlock(&audio->read_lock); + } + mutex_unlock(&audio->lock); + break; + } + default: + pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd); + rc = -EINVAL; + } + return rc; +} + +#ifdef CONFIG_COMPAT +struct msm_audio_stream_config32 { + u32 buffer_size; + u32 buffer_count; +}; + +struct msm_audio_stats32 { + u32 byte_count; + u32 sample_count; + u32 unused[2]; +}; + +struct msm_audio_config32 { + u32 buffer_size; + u32 buffer_count; + u32 channel_count; + u32 sample_rate; + u32 type; + u32 meta_field; + u32 bits; + u32 unused[3]; +}; + +struct msm_audio_buf_cfg32 { + u32 meta_info_enable; + u32 frames_per_buf; +}; + +struct msm_audio_ion_info32 { + int fd; + compat_uptr_t vaddr; +}; + +enum { + AUDIO_GET_CONFIG_32 = _IOR(AUDIO_IOCTL_MAGIC, 3, + struct msm_audio_config32), + AUDIO_SET_CONFIG_32 = _IOW(AUDIO_IOCTL_MAGIC, 4, + struct msm_audio_config32), + AUDIO_GET_STATS_32 = _IOR(AUDIO_IOCTL_MAGIC, 5, + struct msm_audio_stats32), + AUDIO_GET_EVENT_32 = _IOR(AUDIO_IOCTL_MAGIC, 13, + struct msm_audio_event32), + AUDIO_ASYNC_WRITE_32 = _IOW(AUDIO_IOCTL_MAGIC, 17, + struct msm_audio_aio_buf32), + AUDIO_ASYNC_READ_32 = _IOW(AUDIO_IOCTL_MAGIC, 18, + struct msm_audio_aio_buf32), + AUDIO_SET_STREAM_CONFIG_32 = _IOW(AUDIO_IOCTL_MAGIC, 80, + struct msm_audio_stream_config32), + AUDIO_GET_STREAM_CONFIG_32 = _IOR(AUDIO_IOCTL_MAGIC, 81, + struct msm_audio_stream_config32), + AUDIO_GET_BUF_CFG_32 = _IOW(AUDIO_IOCTL_MAGIC, 93, + struct msm_audio_buf_cfg32), + AUDIO_SET_BUF_CFG_32 = _IOW(AUDIO_IOCTL_MAGIC, 94, + struct msm_audio_buf_cfg32), + AUDIO_REGISTER_ION_32 = _IOW(AUDIO_IOCTL_MAGIC, 97, + struct msm_audio_ion_info32), + AUDIO_DEREGISTER_ION_32 = _IOW(AUDIO_IOCTL_MAGIC, 98, + struct msm_audio_ion_info32), +}; + +static long audio_aio_compat_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct q6audio_aio *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_ABORT_GET_EVENT: + case AUDIO_OUTPORT_FLUSH: + case AUDIO_STOP: + case AUDIO_PAUSE: + case AUDIO_FLUSH: + case AUDIO_GET_SESSION_ID: + case AUDIO_PM_AWAKE: + case AUDIO_PM_RELAX: + rc = audio_aio_shared_ioctl(file, cmd, arg); + break; + case AUDIO_GET_STATS_32: { + struct msm_audio_stats32 stats; + uint64_t timestamp; + + memset(&stats, 0, sizeof(struct msm_audio_stats32)); + stats.byte_count = atomic_read(&audio->in_bytes); + stats.sample_count = atomic_read(&audio->in_samples); + rc = q6asm_get_session_time(audio->ac, ×tamp); + if (rc >= 0) + memcpy(&stats.unused[0], ×tamp, sizeof(timestamp)); + else + pr_debug("Error while getting timestamp\n"); + if (copy_to_user((void *)arg, &stats, sizeof(stats))) { + pr_err( + "%s: copy_to_user for AUDIO_GET_STATS_32 failed\n", + __func__); + rc = -EFAULT; + } + break; + } + case AUDIO_GET_EVENT_32: { + pr_debug("%s[%pK]:AUDIO_GET_EVENT\n", __func__, audio); + if (mutex_trylock(&audio->get_event_lock)) { + rc = audio_aio_process_event_req_compat(audio, + (void __user *)arg); + mutex_unlock(&audio->get_event_lock); + } else + rc = -EBUSY; + break; + } + case AUDIO_ASYNC_WRITE_32: { + mutex_lock(&audio->write_lock); + if (audio->drv_status & ADRV_STATUS_FSYNC) + rc = -EBUSY; + else { + if (audio->enabled) + rc = audio_aio_buf_add_compat(audio, 1, + (void __user *)arg); + else + rc = -EPERM; + } + mutex_unlock(&audio->write_lock); + break; + } + case AUDIO_ASYNC_READ_32: { + mutex_lock(&audio->read_lock); + if (audio->feedback) + rc = audio_aio_buf_add_compat(audio, 0, + (void __user *)arg); + else + rc = -EPERM; + mutex_unlock(&audio->read_lock); + break; + } + + case AUDIO_GET_STREAM_CONFIG_32: { + struct msm_audio_stream_config32 cfg; + + mutex_lock(&audio->lock); + memset(&cfg, 0, sizeof(cfg)); + cfg.buffer_size = audio->str_cfg.buffer_size; + cfg.buffer_count = audio->str_cfg.buffer_count; + pr_debug("%s[%pK]:GET STREAM CFG %d %d\n", + __func__, audio, cfg.buffer_size, cfg.buffer_count); + if (copy_to_user((void *)arg, &cfg, sizeof(cfg))) { + pr_err("%s: copy_to_user for AUDIO_GET_STREAM_CONFIG_32 failed\n", + __func__); + rc = -EFAULT; + } + mutex_unlock(&audio->lock); + break; + } + case AUDIO_SET_STREAM_CONFIG_32: { + struct msm_audio_stream_config32 cfg_32; + struct msm_audio_stream_config cfg; + + pr_debug("%s[%pK]:SET STREAM CONFIG\n", __func__, audio); + mutex_lock(&audio->lock); + if (copy_from_user(&cfg_32, (void *)arg, sizeof(cfg_32))) { + pr_err("%s: copy_from_user for AUDIO_SET_STREAM_CONFIG_32 failed\n", + __func__); + rc = -EFAULT; + mutex_unlock(&audio->lock); + break; + } + cfg.buffer_size = cfg_32.buffer_size; + cfg.buffer_count = cfg_32.buffer_count; + + audio->str_cfg.buffer_size = FRAME_SIZE; + audio->str_cfg.buffer_count = FRAME_NUM; + rc = 0; + mutex_unlock(&audio->lock); + break; + } + case AUDIO_GET_CONFIG_32: { + struct msm_audio_config32 cfg_32; + + mutex_lock(&audio->lock); + memset(&cfg_32, 0, sizeof(cfg_32)); + cfg_32.buffer_size = audio->pcm_cfg.buffer_size; + cfg_32.buffer_count = audio->pcm_cfg.buffer_count; + cfg_32.channel_count = audio->pcm_cfg.channel_count; + cfg_32.sample_rate = audio->pcm_cfg.sample_rate; + cfg_32.type = audio->pcm_cfg.type; + cfg_32.meta_field = audio->pcm_cfg.meta_field; + cfg_32.bits = audio->pcm_cfg.bits; + + if (copy_to_user((void *)arg, &cfg_32, sizeof(cfg_32))) { + pr_err("%s: copy_to_user for AUDIO_GET_CONFIG_32 failed\n", + __func__); + rc = -EFAULT; + } + mutex_unlock(&audio->lock); + break; + } + case AUDIO_SET_CONFIG_32: { + struct msm_audio_config config; + struct msm_audio_config32 config_32; + + mutex_lock(&audio->lock); + + if (audio->feedback != NON_TUNNEL_MODE) { + pr_err("%s[%pK]:Not sufficient permission to change the playback mode\n", + __func__, audio); + rc = -EACCES; + mutex_unlock(&audio->lock); + break; + } + pr_debug("%s[%pK]:AUDIO_SET_CONFIG\n", __func__, audio); + if (copy_from_user(&config_32, (void *)arg, + sizeof(config_32))) { + pr_err("%s: copy_from_user for AUDIO_SET_CONFIG_32 failed\n", + __func__); + rc = -EFAULT; + mutex_unlock(&audio->lock); + break; + } + config.buffer_size = config_32.buffer_size; + config.buffer_count = config_32.buffer_count; + config.channel_count = config_32.channel_count; + config.sample_rate = config_32.sample_rate; + config.type = config_32.type; + config.meta_field = config_32.meta_field; + config.bits = config_32.bits; + + if ((config.buffer_count > PCM_BUF_COUNT) || + (config.buffer_count == 1)) + config.buffer_count = PCM_BUF_COUNT; + + if (config.buffer_size < PCM_BUFSZ_MIN) + config.buffer_size = PCM_BUFSZ_MIN; + + audio->pcm_cfg.buffer_count = config.buffer_count; + audio->pcm_cfg.buffer_size = config.buffer_size; + audio->pcm_cfg.channel_count = config.channel_count; + audio->pcm_cfg.sample_rate = config.sample_rate; + rc = 0; + mutex_unlock(&audio->lock); + break; + } + case AUDIO_SET_BUF_CFG_32: { + struct msm_audio_buf_cfg cfg; + struct msm_audio_buf_cfg32 cfg_32; + + mutex_lock(&audio->lock); + if (copy_from_user(&cfg_32, (void *)arg, sizeof(cfg_32))) { + pr_err("%s: copy_from_user for AUDIO_SET_CONFIG_32 failed\n", + __func__); + rc = -EFAULT; + mutex_unlock(&audio->lock); + break; + } + cfg.meta_info_enable = cfg_32.meta_info_enable; + cfg.frames_per_buf = cfg_32.frames_per_buf; + + if ((audio->feedback == NON_TUNNEL_MODE) && + !cfg.meta_info_enable) { + rc = -EFAULT; + mutex_unlock(&audio->lock); + break; + } + + audio->buf_cfg.meta_info_enable = cfg.meta_info_enable; + pr_debug("%s[%pK]:session id %d: Set-buf-cfg: meta[%d]", + __func__, audio, + audio->ac->session, cfg.meta_info_enable); + mutex_unlock(&audio->lock); + break; + } + case AUDIO_GET_BUF_CFG_32: { + struct msm_audio_buf_cfg32 cfg_32; + + pr_debug("%s[%pK]:session id %d: Get-buf-cfg: meta[%d] framesperbuf[%d]\n", + __func__, audio, + audio->ac->session, audio->buf_cfg.meta_info_enable, + audio->buf_cfg.frames_per_buf); + mutex_lock(&audio->lock); + memset(&cfg_32, 0, sizeof(cfg_32)); + cfg_32.meta_info_enable = audio->buf_cfg.meta_info_enable; + cfg_32.frames_per_buf = audio->buf_cfg.frames_per_buf; + if (copy_to_user((void *)arg, &cfg_32, + sizeof(struct msm_audio_buf_cfg32))) { + pr_err("%s: copy_to_user for AUDIO_GET_BUF_CFG_32 failed\n", + __func__); + rc = -EFAULT; + } + mutex_unlock(&audio->lock); + break; + } + case AUDIO_REGISTER_ION_32: { + struct msm_audio_ion_info32 info_32; + struct msm_audio_ion_info info; + + pr_debug("%s[%pK]:AUDIO_REGISTER_ION\n", __func__, audio); + mutex_lock(&audio->lock); + if (copy_from_user(&info_32, (void *)arg, sizeof(info_32))) { + pr_err("%s: copy_from_user for AUDIO_REGISTER_ION_32 failed\n", + __func__); + rc = -EFAULT; + } else { + info.fd = info_32.fd; + info.vaddr = compat_ptr(info_32.vaddr); + mutex_lock(&audio->read_lock); + mutex_lock(&audio->write_lock); + rc = audio_aio_ion_add(audio, &info); + mutex_unlock(&audio->write_lock); + mutex_unlock(&audio->read_lock); + } + mutex_unlock(&audio->lock); + break; + } + case AUDIO_DEREGISTER_ION_32: { + struct msm_audio_ion_info32 info_32; + struct msm_audio_ion_info info; + + mutex_lock(&audio->lock); + pr_debug("%s[%pK]:AUDIO_DEREGISTER_ION\n", __func__, audio); + if (copy_from_user(&info_32, (void *)arg, sizeof(info_32))) { + pr_err("%s: copy_from_user for AUDIO_DEREGISTER_ION_32 failed\n", + __func__); + rc = -EFAULT; + } else { + info.fd = info_32.fd; + info.vaddr = compat_ptr(info_32.vaddr); + mutex_lock(&audio->read_lock); + mutex_lock(&audio->write_lock); + rc = audio_aio_ion_remove(audio, &info); + mutex_unlock(&audio->write_lock); + mutex_unlock(&audio->read_lock); + } + mutex_unlock(&audio->lock); + break; + } + default: + pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd); + rc = -EINVAL; + } + return rc; +} +#endif diff --git a/audio-kernel/dsp/codecs/audio_utils_aio.h b/audio-kernel/dsp/codecs/audio_utils_aio.h new file mode 100644 index 000000000..bd99c3680 --- /dev/null +++ b/audio-kernel/dsp/codecs/audio_utils_aio.h @@ -0,0 +1,230 @@ +/* Copyright (C) 2008 Google, Inc. + * Copyright (C) 2008 HTC Corporation + * Copyright (c) 2009-2018, The Linux Foundation. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "q6audio_common.h" + +#define TUNNEL_MODE 0x0000 +#define NON_TUNNEL_MODE 0x0001 + +#define ADRV_STATUS_AIO_INTF 0x00000001 /* AIO interface */ +#define ADRV_STATUS_FSYNC 0x00000008 +#define ADRV_STATUS_PAUSE 0x00000010 +#define AUDIO_DEC_EOS_SET 0x00000001 +#define AUDIO_DEC_EOF_SET 0x00000010 +#define AUDIO_EVENT_NUM 10 + +#define __CONTAINS(r, v, l) ({ \ + typeof(r) __r = r; \ + typeof(v) __v = v; \ + typeof(v) __e = __v + l; \ + int res = ((__v >= __r->vaddr) && \ + (__e <= __r->vaddr + __r->len)); \ + res; \ +}) + +#define CONTAINS(r1, r2) ({ \ + typeof(r2) __r2 = r2; \ + __CONTAINS(r1, __r2->vaddr, __r2->len); \ +}) + +#define IN_RANGE(r, v) ({ \ + typeof(r) __r = r; \ + typeof(v) __vv = v; \ + int res = ((__vv >= __r->vaddr) && \ + (__vv < (__r->vaddr + __r->len))); \ + res; \ +}) + +#define OVERLAPS(r1, r2) ({ \ + typeof(r1) __r1 = r1; \ + typeof(r2) __r2 = r2; \ + typeof(__r2->vaddr) __v = __r2->vaddr; \ + typeof(__v) __e = __v + __r2->len - 1; \ + int res = (IN_RANGE(__r1, __v) || IN_RANGE(__r1, __e)); \ + res; \ +}) + +struct timestamp { + u32 lowpart; + u32 highpart; +} __packed; + +struct meta_out_dsp { + u32 offset_to_frame; + u32 frame_size; + u32 encoded_pcm_samples; + u32 msw_ts; + u32 lsw_ts; + u32 nflags; +} __packed; + +struct dec_meta_in { + unsigned char reserved[18]; + unsigned short offset; + struct timestamp ntimestamp; + unsigned int nflags; +} __packed; + +struct dec_meta_out { + unsigned int reserved[7]; + unsigned int num_of_frames; + struct meta_out_dsp meta_out_dsp[]; +} __packed; + +/* General meta field to store meta info locally */ +union meta_data { + struct dec_meta_out meta_out; + struct dec_meta_in meta_in; +} __packed; + +/* per device wakeup source manager */ +struct ws_mgr { + struct mutex ws_lock; + uint32_t ref_cnt; +}; + +#define PCM_BUF_COUNT (2) +/* Buffer with meta */ +#define PCM_BUFSZ_MIN ((4*1024) + sizeof(struct dec_meta_out)) + +/* FRAME_NUM must be a power of two */ +#define FRAME_NUM (2) +#define FRAME_SIZE ((4*1536) + sizeof(struct dec_meta_in)) + +struct audio_aio_ion_region { + struct list_head list; + struct dma_buf *dma_buf; + int fd; + void *vaddr; + phys_addr_t paddr; + void *kvaddr; + unsigned long len; + unsigned int ref_cnt; +}; + +struct audio_aio_event { + struct list_head list; + int event_type; + union msm_audio_event_payload payload; +}; + +struct audio_aio_buffer_node { + struct list_head list; + struct msm_audio_aio_buf buf; + unsigned long paddr; + uint32_t token; + void *kvaddr; + union meta_data meta_info; +}; + +struct q6audio_aio; +struct audio_aio_drv_operations { + void (*out_flush)(struct q6audio_aio *); + void (*in_flush)(struct q6audio_aio *); +}; + +struct q6audio_aio { + atomic_t in_bytes; + atomic_t in_samples; + + struct msm_audio_stream_config str_cfg; + struct msm_audio_buf_cfg buf_cfg; + struct msm_audio_config pcm_cfg; + void *codec_cfg; + + struct audio_client *ac; + + struct mutex lock; + struct mutex read_lock; + struct mutex write_lock; + struct mutex get_event_lock; + wait_queue_head_t cmd_wait; + wait_queue_head_t write_wait; + wait_queue_head_t event_wait; + spinlock_t dsp_lock; + spinlock_t event_queue_lock; + + struct miscdevice *miscdevice; + uint32_t wakelock_voted; + struct ws_mgr *audio_ws_mgr; + +#ifdef CONFIG_DEBUG_FS + struct dentry *dentry; +#endif + struct list_head out_queue; /* queue to retain output buffers */ + struct list_head in_queue; /* queue to retain input buffers */ + struct list_head free_event_queue; + struct list_head event_queue; + struct list_head ion_region_queue; /* protected by lock */ + struct audio_aio_drv_operations drv_ops; + union msm_audio_event_payload eos_write_payload; + uint32_t device_events; + uint16_t volume; + uint32_t drv_status; + int event_abort; + int eos_rsp; + int eos_flag; + int opened; + int enabled; + int stopped; + int feedback; + int rflush; /* Read flush */ + int wflush; /* Write flush */ + bool reset_event; + long (*codec_ioctl)(struct file *, unsigned int, unsigned long); + long (*codec_compat_ioctl)(struct file *, unsigned int, unsigned long); +}; + +void audio_aio_async_write_ack(struct q6audio_aio *audio, uint32_t token, + uint32_t *payload); + +void audio_aio_async_read_ack(struct q6audio_aio *audio, uint32_t token, + uint32_t *payload); + +int insert_eos_buf(struct q6audio_aio *audio, + struct audio_aio_buffer_node *buf_node); + +int extract_meta_out_info(struct q6audio_aio *audio, + struct audio_aio_buffer_node *buf_node, int dir); + +int audio_aio_open(struct q6audio_aio *audio, struct file *file); +int audio_aio_enable(struct q6audio_aio *audio); +void audio_aio_post_event(struct q6audio_aio *audio, int type, + union msm_audio_event_payload payload); +int audio_aio_release(struct inode *inode, struct file *file); +int audio_aio_fsync(struct file *file, loff_t start, loff_t end, int datasync); +void audio_aio_async_out_flush(struct q6audio_aio *audio); +void audio_aio_async_in_flush(struct q6audio_aio *audio); +void audio_aio_ioport_reset(struct q6audio_aio *audio); +int enable_volume_ramp(struct q6audio_aio *audio); +#ifdef CONFIG_DEBUG_FS +int audio_aio_debug_open(struct inode *inode, struct file *file); +ssize_t audio_aio_debug_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos); +#endif diff --git a/audio-kernel/dsp/codecs/audio_wma.c b/audio-kernel/dsp/codecs/audio_wma.c new file mode 100644 index 000000000..0e8341e10 --- /dev/null +++ b/audio-kernel/dsp/codecs/audio_wma.c @@ -0,0 +1,349 @@ +/* wma audio output device + * + * Copyright (C) 2008 Google, Inc. + * Copyright (C) 2008 HTC Corporation + * Copyright (c) 2009-2018, The Linux Foundation. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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 +#include +#include +#include "audio_utils_aio.h" + +static struct miscdevice audio_wma_misc; +static struct ws_mgr audio_wma_ws_mgr; + +#ifdef CONFIG_DEBUG_FS +static const struct file_operations audio_wma_debug_fops = { + .read = audio_aio_debug_read, + .open = audio_aio_debug_open, +}; +#endif + +static long audio_ioctl_shared(struct file *file, unsigned int cmd, + void *arg) +{ + struct q6audio_aio *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_START: { + struct asm_wma_cfg wma_cfg; + struct msm_audio_wma_config_v2 *wma_config; + + pr_debug("%s[%pK]: AUDIO_START session_id[%d]\n", __func__, + audio, audio->ac->session); + if (audio->feedback == NON_TUNNEL_MODE) { + /* Configure PCM output block */ + rc = q6asm_enc_cfg_blk_pcm(audio->ac, + audio->pcm_cfg.sample_rate, + audio->pcm_cfg.channel_count); + if (rc < 0) { + pr_err("pcm output block config failed\n"); + break; + } + } + wma_config = (struct msm_audio_wma_config_v2 *)audio->codec_cfg; + wma_cfg.format_tag = wma_config->format_tag; + wma_cfg.ch_cfg = wma_config->numchannels; + wma_cfg.sample_rate = wma_config->samplingrate; + wma_cfg.avg_bytes_per_sec = wma_config->avgbytespersecond; + wma_cfg.block_align = wma_config->block_align; + wma_cfg.valid_bits_per_sample = + wma_config->validbitspersample; + wma_cfg.ch_mask = wma_config->channelmask; + wma_cfg.encode_opt = wma_config->encodeopt; + /* Configure Media format block */ + rc = q6asm_media_format_block_wma(audio->ac, &wma_cfg, + audio->ac->stream_id); + if (rc < 0) { + pr_err("cmd media format block failed\n"); + break; + } + rc = audio_aio_enable(audio); + audio->eos_rsp = 0; + audio->eos_flag = 0; + if (!rc) { + audio->enabled = 1; + } else { + audio->enabled = 0; + pr_err("Audio Start procedure failed rc=%d\n", rc); + break; + } + pr_debug("AUDIO_START success enable[%d]\n", audio->enabled); + if (audio->stopped == 1) + audio->stopped = 0; + break; + } + default: + pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd); + break; + } + return rc; +} + +static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct q6audio_aio *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_START: { + rc = audio_ioctl_shared(file, cmd, (void *)arg); + break; + } + case AUDIO_GET_WMA_CONFIG_V2: { + if (copy_to_user((void *)arg, audio->codec_cfg, + sizeof(struct msm_audio_wma_config_v2))) { + pr_err("%s:copy_to_user for AUDIO_SET_WMA_CONFIG_V2 failed\n", + __func__); + rc = -EFAULT; + break; + } + break; + } + case AUDIO_SET_WMA_CONFIG_V2: { + if (copy_from_user(audio->codec_cfg, (void *)arg, + sizeof(struct msm_audio_wma_config_v2))) { + pr_err("%s:copy_from_user for AUDIO_SET_WMA_CONFIG_V2 failed\n", + __func__); + rc = -EFAULT; + break; + } + break; + } + default: { + pr_debug("%s[%pK]: Calling utils ioctl\n", __func__, audio); + rc = audio->codec_ioctl(file, cmd, arg); + if (rc) + pr_err_ratelimited("Failed in utils_ioctl: %d\n", rc); + break; + } + } + return rc; +} + +#ifdef CONFIG_COMPAT +struct msm_audio_wma_config_v2_32 { + u16 format_tag; + u16 numchannels; + u32 samplingrate; + u32 avgbytespersecond; + u16 block_align; + u16 validbitspersample; + u32 channelmask; + u16 encodeopt; +}; + +enum { + AUDIO_GET_WMA_CONFIG_V2_32 = _IOR(AUDIO_IOCTL_MAGIC, + (AUDIO_MAX_COMMON_IOCTL_NUM+2), struct msm_audio_wma_config_v2_32), + AUDIO_SET_WMA_CONFIG_V2_32 = _IOW(AUDIO_IOCTL_MAGIC, + (AUDIO_MAX_COMMON_IOCTL_NUM+3), struct msm_audio_wma_config_v2_32) +}; + +static long audio_compat_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct q6audio_aio *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_START: { + rc = audio_ioctl_shared(file, cmd, (void *)arg); + break; + } + case AUDIO_GET_WMA_CONFIG_V2_32: { + struct msm_audio_wma_config_v2 *wma_config; + struct msm_audio_wma_config_v2_32 wma_config_32; + + memset(&wma_config_32, 0, sizeof(wma_config_32)); + + wma_config = (struct msm_audio_wma_config_v2 *)audio->codec_cfg; + wma_config_32.format_tag = wma_config->format_tag; + wma_config_32.numchannels = wma_config->numchannels; + wma_config_32.samplingrate = wma_config->samplingrate; + wma_config_32.avgbytespersecond = wma_config->avgbytespersecond; + wma_config_32.block_align = wma_config->block_align; + wma_config_32.validbitspersample = + wma_config->validbitspersample; + wma_config_32.channelmask = wma_config->channelmask; + wma_config_32.encodeopt = wma_config->encodeopt; + if (copy_to_user((void *)arg, &wma_config_32, + sizeof(wma_config_32))) { + pr_err("%s: copy_to_user for GET_WMA_CONFIG_V2_32 failed\n", + __func__); + rc = -EFAULT; + break; + } + break; + } + case AUDIO_SET_WMA_CONFIG_V2_32: { + struct msm_audio_wma_config_v2 *wma_config; + struct msm_audio_wma_config_v2_32 wma_config_32; + + if (copy_from_user(&wma_config_32, (void *)arg, + sizeof(wma_config_32))) { + pr_err("%s: copy_from_user for SET_WMA_CONFIG_V2_32 failed\n" + , __func__); + rc = -EFAULT; + break; + } + wma_config = (struct msm_audio_wma_config_v2 *)audio->codec_cfg; + wma_config->format_tag = wma_config_32.format_tag; + wma_config->numchannels = wma_config_32.numchannels; + wma_config->samplingrate = wma_config_32.samplingrate; + wma_config->avgbytespersecond = wma_config_32.avgbytespersecond; + wma_config->block_align = wma_config_32.block_align; + wma_config->validbitspersample = + wma_config_32.validbitspersample; + wma_config->channelmask = wma_config_32.channelmask; + wma_config->encodeopt = wma_config_32.encodeopt; + break; + } + default: { + pr_debug("%s[%pK]: Calling utils ioctl\n", __func__, audio); + rc = audio->codec_compat_ioctl(file, cmd, arg); + if (rc) + pr_err_ratelimited("Failed in utils_ioctl: %d\n", rc); + break; + } + } + return rc; +} +#else +#define audio_compat_ioctl NULL +#endif + +static int audio_open(struct inode *inode, struct file *file) +{ + struct q6audio_aio *audio = NULL; + int rc = 0; + +#ifdef CONFIG_DEBUG_FS + /* 4 bytes represents decoder number, 1 byte for terminate string */ + char name[sizeof "msm_wma_" + 5]; +#endif + audio = kzalloc(sizeof(struct q6audio_aio), GFP_KERNEL); + + if (audio == NULL) + return -ENOMEM; + + audio->codec_cfg = kzalloc(sizeof(struct msm_audio_wma_config_v2), + GFP_KERNEL); + if (audio->codec_cfg == NULL) { + kfree(audio); + return -ENOMEM; + } + + audio->pcm_cfg.buffer_size = PCM_BUFSZ_MIN; + audio->miscdevice = &audio_wma_misc; + audio->wakelock_voted = false; + audio->audio_ws_mgr = &audio_wma_ws_mgr; + + audio->ac = q6asm_audio_client_alloc((app_cb) q6_audio_cb, + (void *)audio); + + if (!audio->ac) { + pr_err("Could not allocate memory for audio client\n"); + kfree(audio->codec_cfg); + kfree(audio); + return -ENOMEM; + } + rc = audio_aio_open(audio, file); + if (rc < 0) { + pr_err("%s: audio_aio_open rc=%d\n", + __func__, rc); + goto fail; + } + /* open in T/NT mode */ + if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) { + rc = q6asm_open_read_write(audio->ac, FORMAT_LINEAR_PCM, + FORMAT_WMA_V9); + if (rc < 0) { + pr_err("NT mode Open failed rc=%d\n", rc); + rc = -ENODEV; + goto fail; + } + audio->feedback = NON_TUNNEL_MODE; + /* open WMA decoder, expected frames is always 1*/ + audio->buf_cfg.frames_per_buf = 0x01; + audio->buf_cfg.meta_info_enable = 0x01; + } else if ((file->f_mode & FMODE_WRITE) && + !(file->f_mode & FMODE_READ)) { + rc = q6asm_open_write(audio->ac, FORMAT_WMA_V9); + if (rc < 0) { + pr_err("T mode Open failed rc=%d\n", rc); + rc = -ENODEV; + goto fail; + } + audio->feedback = TUNNEL_MODE; + audio->buf_cfg.meta_info_enable = 0x00; + } else { + pr_err("Not supported mode\n"); + rc = -EACCES; + goto fail; + } + +#ifdef CONFIG_DEBUG_FS + snprintf(name, sizeof(name), "msm_wma_%04x", audio->ac->session); + audio->dentry = debugfs_create_file(name, S_IFREG | 0444, + NULL, (void *)audio, + &audio_wma_debug_fops); + + if (IS_ERR(audio->dentry)) + pr_debug("debugfs_create_file failed\n"); +#endif + pr_info("%s:wmadec success mode[%d]session[%d]\n", __func__, + audio->feedback, + audio->ac->session); + return rc; +fail: + q6asm_audio_client_free(audio->ac); + kfree(audio->codec_cfg); + kfree(audio); + return rc; +} + +static const struct file_operations audio_wma_fops = { + .owner = THIS_MODULE, + .open = audio_open, + .release = audio_aio_release, + .unlocked_ioctl = audio_ioctl, + .fsync = audio_aio_fsync, + .compat_ioctl = audio_compat_ioctl +}; + +static struct miscdevice audio_wma_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_wma", + .fops = &audio_wma_fops, +}; + +int __init audio_wma_init(void) +{ + int ret = misc_register(&audio_wma_misc); + + if (ret == 0) + device_init_wakeup(audio_wma_misc.this_device, true); + audio_wma_ws_mgr.ref_cnt = 0; + mutex_init(&audio_wma_ws_mgr.ws_lock); + + return ret; +} + +void audio_wma_exit(void) +{ + mutex_destroy(&audio_wma_ws_mgr.ws_lock); + misc_deregister(&audio_wma_misc); +} diff --git a/audio-kernel/dsp/codecs/audio_wmapro.c b/audio-kernel/dsp/codecs/audio_wmapro.c new file mode 100644 index 000000000..6b1e46f45 --- /dev/null +++ b/audio-kernel/dsp/codecs/audio_wmapro.c @@ -0,0 +1,422 @@ +/* wmapro audio output device + * + * Copyright (C) 2008 Google, Inc. + * Copyright (C) 2008 HTC Corporation + * Copyright (c) 2009-2018, The Linux Foundation. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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 +#include +#include +#include "audio_utils_aio.h" + +static struct miscdevice audio_wmapro_misc; +static struct ws_mgr audio_wmapro_ws_mgr; + +#ifdef CONFIG_DEBUG_FS +static const struct file_operations audio_wmapro_debug_fops = { + .read = audio_aio_debug_read, + .open = audio_aio_debug_open, +}; +#endif + +static long audio_ioctl_shared(struct file *file, unsigned int cmd, + void *arg) +{ + struct q6audio_aio *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_START: { + struct asm_wmapro_cfg wmapro_cfg; + struct msm_audio_wmapro_config *wmapro_config; + + pr_debug("%s: AUDIO_START session_id[%d]\n", __func__, + audio->ac->session); + if (audio->feedback == NON_TUNNEL_MODE) { + /* Configure PCM output block */ + rc = q6asm_enc_cfg_blk_pcm_v2(audio->ac, + audio->pcm_cfg.sample_rate, + audio->pcm_cfg.channel_count, + 16, /* bits per sample */ + true, /* use default channel map */ + true, /* use back channel map flavor */ + NULL); + if (rc < 0) { + pr_err("pcm output block config failed\n"); + break; + } + } + wmapro_config = (struct msm_audio_wmapro_config *) + audio->codec_cfg; + if ((wmapro_config->formattag == 0x162) || + (wmapro_config->formattag == 0x163) || + (wmapro_config->formattag == 0x166) || + (wmapro_config->formattag == 0x167)) { + wmapro_cfg.format_tag = wmapro_config->formattag; + } else { + pr_err("%s:AUDIO_START failed: formattag = %d\n", + __func__, wmapro_config->formattag); + rc = -EINVAL; + break; + } + if (wmapro_config->numchannels > 0) { + wmapro_cfg.ch_cfg = wmapro_config->numchannels; + } else { + pr_err("%s:AUDIO_START failed: channels = %d\n", + __func__, wmapro_config->numchannels); + rc = -EINVAL; + break; + } + if (wmapro_config->samplingrate > 0) { + wmapro_cfg.sample_rate = wmapro_config->samplingrate; + } else { + pr_err("%s:AUDIO_START failed: sample_rate = %d\n", + __func__, wmapro_config->samplingrate); + rc = -EINVAL; + break; + } + wmapro_cfg.avg_bytes_per_sec = + wmapro_config->avgbytespersecond; + if ((wmapro_config->asfpacketlength <= 13376) || + (wmapro_config->asfpacketlength > 0)) { + wmapro_cfg.block_align = + wmapro_config->asfpacketlength; + } else { + pr_err("%s:AUDIO_START failed: block_align = %d\n", + __func__, wmapro_config->asfpacketlength); + rc = -EINVAL; + break; + } + if ((wmapro_config->validbitspersample == 16) || + (wmapro_config->validbitspersample == 24)) { + wmapro_cfg.valid_bits_per_sample = + wmapro_config->validbitspersample; + } else { + pr_err("%s:AUDIO_START failed: bitspersample = %d\n", + __func__, wmapro_config->validbitspersample); + rc = -EINVAL; + break; + } + wmapro_cfg.ch_mask = wmapro_config->channelmask; + wmapro_cfg.encode_opt = wmapro_config->encodeopt; + wmapro_cfg.adv_encode_opt = + wmapro_config->advancedencodeopt; + wmapro_cfg.adv_encode_opt2 = + wmapro_config->advancedencodeopt2; + /* Configure Media format block */ + rc = q6asm_media_format_block_wmapro(audio->ac, &wmapro_cfg, + audio->ac->stream_id); + if (rc < 0) { + pr_err("cmd media format block failed\n"); + break; + } + rc = audio_aio_enable(audio); + audio->eos_rsp = 0; + audio->eos_flag = 0; + if (!rc) { + audio->enabled = 1; + } else { + audio->enabled = 0; + pr_err("Audio Start procedure failed rc=%d\n", rc); + break; + } + pr_debug("AUDIO_START success enable[%d]\n", audio->enabled); + if (audio->stopped == 1) + audio->stopped = 0; + break; + } + default: + pr_err("%s: Unknown ioctl cmd %d\n", __func__, cmd); + rc = -EINVAL; + break; + } + return rc; +} + +static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct q6audio_aio *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_GET_WMAPRO_CONFIG: { + if (copy_to_user((void *)arg, audio->codec_cfg, + sizeof(struct msm_audio_wmapro_config))) { + pr_err("%s: copy_to_user for AUDIO_GET_WMAPRO_CONFIG failed\n", + __func__); + rc = -EFAULT; + } + break; + } + case AUDIO_SET_WMAPRO_CONFIG: { + if (copy_from_user(audio->codec_cfg, (void *)arg, + sizeof(struct msm_audio_wmapro_config))) { + pr_err("%s: copy_from_user for AUDIO_SET_WMAPRO_CONFIG_V2 failed\n", + __func__); + rc = -EFAULT; + break; + } + break; + } + case AUDIO_START: { + rc = audio_ioctl_shared(file, cmd, (void *)arg); + break; + } + default: { + pr_debug("%s[%pK]: Calling utils ioctl\n", __func__, audio); + rc = audio->codec_ioctl(file, cmd, arg); + if (rc) + pr_err_ratelimited("Failed in utils_ioctl: %d\n", rc); + break; + } + } + return rc; +} + +#ifdef CONFIG_COMPAT + +struct msm_audio_wmapro_config32 { + u16 armdatareqthr; + u8 validbitspersample; + u8 numchannels; + u16 formattag; + u32 samplingrate; + u32 avgbytespersecond; + u16 asfpacketlength; + u32 channelmask; + u16 encodeopt; + u16 advancedencodeopt; + u32 advancedencodeopt2; +}; + +enum { + AUDIO_GET_WMAPRO_CONFIG_32 = _IOR(AUDIO_IOCTL_MAGIC, + (AUDIO_MAX_COMMON_IOCTL_NUM+0), struct msm_audio_wmapro_config32), + AUDIO_SET_WMAPRO_CONFIG_32 = _IOW(AUDIO_IOCTL_MAGIC, + (AUDIO_MAX_COMMON_IOCTL_NUM+1), struct msm_audio_wmapro_config32) +}; + +static long audio_compat_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct q6audio_aio *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_GET_WMAPRO_CONFIG_32: { + struct msm_audio_wmapro_config *wmapro_config; + struct msm_audio_wmapro_config32 wmapro_config_32; + + memset(&wmapro_config_32, 0, sizeof(wmapro_config_32)); + + wmapro_config = + (struct msm_audio_wmapro_config *)audio->codec_cfg; + wmapro_config_32.armdatareqthr = wmapro_config->armdatareqthr; + wmapro_config_32.validbitspersample = + wmapro_config->validbitspersample; + wmapro_config_32.numchannels = wmapro_config->numchannels; + wmapro_config_32.formattag = wmapro_config->formattag; + wmapro_config_32.samplingrate = wmapro_config->samplingrate; + wmapro_config_32.avgbytespersecond = + wmapro_config->avgbytespersecond; + wmapro_config_32.asfpacketlength = + wmapro_config->asfpacketlength; + wmapro_config_32.channelmask = wmapro_config->channelmask; + wmapro_config_32.encodeopt = wmapro_config->encodeopt; + wmapro_config_32.advancedencodeopt = + wmapro_config->advancedencodeopt; + wmapro_config_32.advancedencodeopt2 = + wmapro_config->advancedencodeopt2; + + if (copy_to_user((void *)arg, &wmapro_config_32, + sizeof(struct msm_audio_wmapro_config32))) { + pr_err("%s: copy_to_user for AUDIO_GET_WMAPRO_CONFIG_V2_32 failed\n", + __func__); + rc = -EFAULT; + } + break; + } + case AUDIO_SET_WMAPRO_CONFIG_32: { + struct msm_audio_wmapro_config *wmapro_config; + struct msm_audio_wmapro_config32 wmapro_config_32; + + if (copy_from_user(&wmapro_config_32, (void *)arg, + sizeof(struct msm_audio_wmapro_config32))) { + pr_err( + "%s: copy_from_user for AUDIO_SET_WMAPRO_CONFG_V2_32 failed\n", + __func__); + rc = -EFAULT; + break; + } + wmapro_config = + (struct msm_audio_wmapro_config *)audio->codec_cfg; + wmapro_config->armdatareqthr = wmapro_config_32.armdatareqthr; + wmapro_config->validbitspersample = + wmapro_config_32.validbitspersample; + wmapro_config->numchannels = wmapro_config_32.numchannels; + wmapro_config->formattag = wmapro_config_32.formattag; + wmapro_config->samplingrate = wmapro_config_32.samplingrate; + wmapro_config->avgbytespersecond = + wmapro_config_32.avgbytespersecond; + wmapro_config->asfpacketlength = + wmapro_config_32.asfpacketlength; + wmapro_config->channelmask = wmapro_config_32.channelmask; + wmapro_config->encodeopt = wmapro_config_32.encodeopt; + wmapro_config->advancedencodeopt = + wmapro_config_32.advancedencodeopt; + wmapro_config->advancedencodeopt2 = + wmapro_config_32.advancedencodeopt2; + break; + } + case AUDIO_START: { + rc = audio_ioctl_shared(file, cmd, (void *)arg); + break; + } + default: { + pr_debug("%s[%pK]: Calling utils ioctl\n", __func__, audio); + rc = audio->codec_compat_ioctl(file, cmd, arg); + if (rc) + pr_err_ratelimited("Failed in utils_ioctl: %d\n", rc); + break; + } + } + return rc; +} +#else +#define audio_compat_ioctl NULL +#endif + +static int audio_open(struct inode *inode, struct file *file) +{ + struct q6audio_aio *audio = NULL; + int rc = 0; + +#ifdef CONFIG_DEBUG_FS + /* 4 bytes represents decoder number, 1 byte for terminate string */ + char name[sizeof "msm_wmapro_" + 5]; +#endif + audio = kzalloc(sizeof(struct q6audio_aio), GFP_KERNEL); + + if (audio == NULL) + return -ENOMEM; + + audio->codec_cfg = kzalloc(sizeof(struct msm_audio_wmapro_config), + GFP_KERNEL); + if (audio->codec_cfg == NULL) { + kfree(audio); + return -ENOMEM; + } + + + audio->pcm_cfg.buffer_size = PCM_BUFSZ_MIN; + audio->miscdevice = &audio_wmapro_misc; + audio->wakelock_voted = false; + audio->audio_ws_mgr = &audio_wmapro_ws_mgr; + + audio->ac = q6asm_audio_client_alloc((app_cb) q6_audio_cb, + (void *)audio); + + if (!audio->ac) { + pr_err("Could not allocate memory for audio client\n"); + kfree(audio->codec_cfg); + kfree(audio); + return -ENOMEM; + } + + rc = audio_aio_open(audio, file); + if (rc < 0) { + pr_err("%s: audio_aio_open rc=%d\n", + __func__, rc); + goto fail; + } + /* open in T/NT mode */ + if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) { + rc = q6asm_open_read_write(audio->ac, FORMAT_LINEAR_PCM, + FORMAT_WMA_V10PRO); + if (rc < 0) { + pr_err("NT mode Open failed rc=%d\n", rc); + rc = -ENODEV; + goto fail; + } + audio->feedback = NON_TUNNEL_MODE; + /* open WMA decoder, expected frames is always 1*/ + audio->buf_cfg.frames_per_buf = 0x01; + audio->buf_cfg.meta_info_enable = 0x01; + } else if ((file->f_mode & FMODE_WRITE) && + !(file->f_mode & FMODE_READ)) { + rc = q6asm_open_write(audio->ac, FORMAT_WMA_V10PRO); + if (rc < 0) { + pr_err("T mode Open failed rc=%d\n", rc); + rc = -ENODEV; + goto fail; + } + audio->feedback = TUNNEL_MODE; + audio->buf_cfg.meta_info_enable = 0x00; + } else { + pr_err("Not supported mode\n"); + rc = -EACCES; + goto fail; + } + +#ifdef CONFIG_DEBUG_FS + snprintf(name, sizeof(name), "msm_wmapro_%04x", audio->ac->session); + audio->dentry = debugfs_create_file(name, S_IFREG | 0444, + NULL, (void *)audio, + &audio_wmapro_debug_fops); + + if (IS_ERR(audio->dentry)) + pr_debug("debugfs_create_file failed\n"); +#endif + pr_info("%s:wmapro decoder open success, session_id = %d\n", __func__, + audio->ac->session); + return rc; +fail: + q6asm_audio_client_free(audio->ac); + kfree(audio->codec_cfg); + kfree(audio); + return rc; +} + +static const struct file_operations audio_wmapro_fops = { + .owner = THIS_MODULE, + .open = audio_open, + .release = audio_aio_release, + .unlocked_ioctl = audio_ioctl, + .fsync = audio_aio_fsync, + .compat_ioctl = audio_compat_ioctl +}; + +static struct miscdevice audio_wmapro_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_wmapro", + .fops = &audio_wmapro_fops, +}; + +int __init audio_wmapro_init(void) +{ + int ret = misc_register(&audio_wmapro_misc); + + if (ret == 0) + device_init_wakeup(audio_wmapro_misc.this_device, true); + audio_wmapro_ws_mgr.ref_cnt = 0; + mutex_init(&audio_wmapro_ws_mgr.ws_lock); + + return ret; +} + +void audio_wmapro_exit(void) +{ + mutex_destroy(&audio_wmapro_ws_mgr.ws_lock); + misc_deregister(&audio_wmapro_misc); +} diff --git a/audio-kernel/dsp/codecs/evrc_in.c b/audio-kernel/dsp/codecs/evrc_in.c new file mode 100644 index 000000000..15f08e745 --- /dev/null +++ b/audio-kernel/dsp/codecs/evrc_in.c @@ -0,0 +1,413 @@ +/* Copyright (c) 2010-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 + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "audio_utils.h" + +/* Buffer with meta*/ +#define PCM_BUF_SIZE (4096 + sizeof(struct meta_in)) + +/* Maximum 10 frames in buffer with meta */ +#define FRAME_SIZE (1 + ((23+sizeof(struct meta_out_dsp)) * 10)) + +static long evrc_in_ioctl_shared(struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct q6audio_in *audio = file->private_data; + int rc = 0; + int cnt = 0; + + switch (cmd) { + case AUDIO_START: { + struct msm_audio_evrc_enc_config *enc_cfg; + + enc_cfg = audio->enc_cfg; + pr_debug("%s:session id %d: default buf alloc[%d]\n", __func__, + audio->ac->session, audio->buf_alloc); + if (audio->enabled == 1) { + pr_info("%s:AUDIO_START already over\n", __func__); + rc = 0; + break; + } + rc = audio_in_buf_alloc(audio); + if (rc < 0) { + pr_err("%s:session id %d: buffer allocation failed\n", + __func__, audio->ac->session); + break; + } + + /* rate_modulation_cmd set to zero + * currently not configurable from user space + */ + rc = q6asm_enc_cfg_blk_evrc(audio->ac, + audio->buf_cfg.frames_per_buf, + enc_cfg->min_bit_rate, + enc_cfg->max_bit_rate, 0); + + if (rc < 0) { + pr_err("%s:session id %d: cmd evrc media format block failed\n", + __func__, audio->ac->session); + break; + } + if (audio->feedback == NON_TUNNEL_MODE) { + rc = q6asm_media_format_block_pcm(audio->ac, + audio->pcm_cfg.sample_rate, + audio->pcm_cfg.channel_count); + + if (rc < 0) { + pr_err("%s:session id %d: media format block failed\n", + __func__, audio->ac->session); + break; + } + } + pr_debug("%s:session id %d: AUDIO_START enable[%d]\n", + __func__, audio->ac->session, audio->enabled); + rc = audio_in_enable(audio); + if (!rc) { + audio->enabled = 1; + } else { + audio->enabled = 0; + pr_err("%s:session id %d: Audio Start procedure failed rc=%d\n", + __func__, audio->ac->session, rc); + break; + } + while (cnt++ < audio->str_cfg.buffer_count) + q6asm_read(audio->ac); /* Push buffer to DSP */ + rc = 0; + pr_debug("%s:session id %d: AUDIO_START success enable[%d]\n", + __func__, audio->ac->session, audio->enabled); + break; + } + case AUDIO_STOP: { + pr_debug("%s:session id %d: AUDIO_STOP\n", __func__, + audio->ac->session); + rc = audio_in_disable(audio); + if (rc < 0) { + pr_err("%s:session id %d: Audio Stop procedure failed rc=%d\n", + __func__, audio->ac->session, rc); + break; + } + break; + } + case AUDIO_SET_EVRC_ENC_CONFIG: { + struct msm_audio_evrc_enc_config *cfg; + struct msm_audio_evrc_enc_config *enc_cfg; + + enc_cfg = audio->enc_cfg; + cfg = (struct msm_audio_evrc_enc_config *)arg; + if (cfg == NULL) { + pr_err("%s: NULL config pointer for %s\n", + __func__, "AUDIO_SET_EVRC_ENC_CONFIG"); + rc = -EINVAL; + break; + } + if (cfg->min_bit_rate > 4 || + cfg->min_bit_rate < 1 || + (cfg->min_bit_rate == 2)) { + pr_err("%s:session id %d: invalid min bitrate\n", + __func__, audio->ac->session); + rc = -EINVAL; + break; + } + if (cfg->max_bit_rate > 4 || + cfg->max_bit_rate < 1 || + (cfg->max_bit_rate == 2)) { + pr_err("%s:session id %d: invalid max bitrate\n", + __func__, audio->ac->session); + rc = -EINVAL; + break; + } + enc_cfg->min_bit_rate = cfg->min_bit_rate; + enc_cfg->max_bit_rate = cfg->max_bit_rate; + pr_debug("%s:session id %d: min_bit_rate= 0x%x max_bit_rate=0x%x\n", + __func__, + audio->ac->session, enc_cfg->min_bit_rate, + enc_cfg->max_bit_rate); + break; + } + default: + pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd); + rc = -EINVAL; + } + return rc; +} + +static long evrc_in_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct q6audio_in *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_START: + case AUDIO_STOP: { + rc = evrc_in_ioctl_shared(file, cmd, arg); + break; + } + case AUDIO_GET_EVRC_ENC_CONFIG: { + if (copy_to_user((void *)arg, audio->enc_cfg, + sizeof(struct msm_audio_evrc_enc_config))) { + pr_err("%s: copy_to_user for AUDIO_GET_EVRC_ENC_CONFIG failed\n", + __func__); + rc = -EFAULT; + } + break; + } + case AUDIO_SET_EVRC_ENC_CONFIG: { + struct msm_audio_evrc_enc_config cfg; + + if (copy_from_user(&cfg, (void *) arg, + sizeof(struct msm_audio_evrc_enc_config))) { + pr_err("%s: copy_from_user for AUDIO_SET_EVRC_ENC_CONFIG failed\n", + __func__); + rc = -EFAULT; + break; + } + rc = evrc_in_ioctl_shared(file, cmd, (unsigned long)&cfg); + if (rc) + pr_err("%s:AUDIO_SET_EVRC_ENC_CONFIG failed. rc= %d\n", + __func__, rc); + break; + } + default: + pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd); + rc = -EINVAL; + } + return rc; +} + +#ifdef CONFIG_COMPAT +struct msm_audio_evrc_enc_config32 { + u32 cdma_rate; + u32 min_bit_rate; + u32 max_bit_rate; +}; + +enum { + AUDIO_SET_EVRC_ENC_CONFIG_32 = _IOW(AUDIO_IOCTL_MAGIC, + 2, struct msm_audio_evrc_enc_config32), + AUDIO_GET_EVRC_ENC_CONFIG_32 = _IOR(AUDIO_IOCTL_MAGIC, + 3, struct msm_audio_evrc_enc_config32) +}; + +static long evrc_in_compat_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct q6audio_in *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_START: + case AUDIO_STOP: { + rc = evrc_in_ioctl_shared(file, cmd, arg); + break; + } + case AUDIO_GET_EVRC_ENC_CONFIG_32: { + struct msm_audio_evrc_enc_config32 cfg_32; + struct msm_audio_evrc_enc_config *enc_cfg; + + memset(&cfg_32, 0, sizeof(cfg_32)); + + enc_cfg = audio->enc_cfg; + cfg_32.cdma_rate = enc_cfg->cdma_rate; + cfg_32.min_bit_rate = enc_cfg->min_bit_rate; + cfg_32.max_bit_rate = enc_cfg->max_bit_rate; + + if (copy_to_user((void *)arg, &cfg_32, + sizeof(cfg_32))) { + pr_err("%s: copy_to_user for AUDIO_GET_EVRC_ENC_CONFIG_32 failed\n", + __func__); + rc = -EFAULT; + } + break; + } + case AUDIO_SET_EVRC_ENC_CONFIG_32: { + struct msm_audio_evrc_enc_config cfg; + struct msm_audio_evrc_enc_config32 cfg_32; + + if (copy_from_user(&cfg_32, (void *) arg, + sizeof(cfg_32))) { + pr_err("%s: copy_from_user for AUDIO_SET_EVRC_ENC_CONFIG_32 failed\n", + __func__); + rc = -EFAULT; + break; + } + cfg.cdma_rate = cfg_32.cdma_rate; + cfg.min_bit_rate = cfg_32.min_bit_rate; + cfg.max_bit_rate = cfg_32.max_bit_rate; + cmd = AUDIO_SET_EVRC_ENC_CONFIG; + rc = evrc_in_ioctl_shared(file, cmd, (unsigned long)&cfg); + if (rc) + pr_err("%s:AUDIO_SET_EVRC_ENC_CONFIG failed. rc= %d\n", + __func__, rc); + break; + } + default: + pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd); + rc = -EINVAL; + } + return rc; +} +#else +#define evrc_in_compat_ioctl NULL +#endif + +static int evrc_in_open(struct inode *inode, struct file *file) +{ + struct q6audio_in *audio = NULL; + struct msm_audio_evrc_enc_config *enc_cfg; + int rc = 0; + + audio = kzalloc(sizeof(struct q6audio_in), GFP_KERNEL); + + if (audio == NULL) + return -ENOMEM; + + /* Allocate memory for encoder config param */ + audio->enc_cfg = kzalloc(sizeof(struct msm_audio_evrc_enc_config), + GFP_KERNEL); + if (audio->enc_cfg == NULL) { + kfree(audio); + return -ENOMEM; + } + enc_cfg = audio->enc_cfg; + mutex_init(&audio->lock); + mutex_init(&audio->read_lock); + mutex_init(&audio->write_lock); + spin_lock_init(&audio->dsp_lock); + init_waitqueue_head(&audio->read_wait); + init_waitqueue_head(&audio->write_wait); + + /* Settings will be re-config at AUDIO_SET_CONFIG, + * but at least we need to have initial config + */ + audio->str_cfg.buffer_size = FRAME_SIZE; + audio->str_cfg.buffer_count = FRAME_NUM; + audio->min_frame_size = 23; + audio->max_frames_per_buf = 10; + audio->pcm_cfg.buffer_size = PCM_BUF_SIZE; + audio->pcm_cfg.buffer_count = PCM_BUF_COUNT; + enc_cfg->min_bit_rate = 4; + enc_cfg->max_bit_rate = 4; + audio->pcm_cfg.channel_count = 1; + audio->pcm_cfg.sample_rate = 8000; + audio->buf_cfg.meta_info_enable = 0x01; + audio->buf_cfg.frames_per_buf = 0x01; + audio->event_abort = 0; + + audio->ac = q6asm_audio_client_alloc((app_cb)q6asm_in_cb, + (void *)audio); + + if (!audio->ac) { + pr_err("%s: Could not allocate memory for audio client\n", + __func__); + kfree(audio->enc_cfg); + kfree(audio); + return -ENOMEM; + } + + /* open evrc encoder in T/NT mode */ + if ((file->f_mode & FMODE_WRITE) && + (file->f_mode & FMODE_READ)) { + audio->feedback = NON_TUNNEL_MODE; + rc = q6asm_open_read_write(audio->ac, FORMAT_EVRC, + FORMAT_LINEAR_PCM); + if (rc < 0) { + pr_err("%s:session id %d: NT mode Open failed rc=%d\n", + __func__, audio->ac->session, rc); + rc = -ENODEV; + goto fail; + } + pr_info("%s:session id %d: NT mode encoder success\n", + __func__, audio->ac->session); + } else if (!(file->f_mode & FMODE_WRITE) && + (file->f_mode & FMODE_READ)) { + audio->feedback = TUNNEL_MODE; + rc = q6asm_open_read(audio->ac, FORMAT_EVRC); + if (rc < 0) { + pr_err("%s:session id %d: T mode Open failed rc=%d\n", + __func__, audio->ac->session, rc); + rc = -ENODEV; + goto fail; + } + /* register for tx overflow (valid for tunnel mode only) */ + rc = q6asm_reg_tx_overflow(audio->ac, 0x01); + if (rc < 0) { + pr_err("%s:session id %d: TX Overflow registration failed rc=%d\n", + __func__, + audio->ac->session, rc); + rc = -ENODEV; + goto fail; + } + pr_info("%s:session id %d: T mode encoder success\n", __func__, + audio->ac->session); + } else { + pr_err("%s:session id %d: Unexpected mode\n", __func__, + audio->ac->session); + rc = -EACCES; + goto fail; + } + + audio->opened = 1; + audio->reset_event = false; + atomic_set(&audio->in_count, PCM_BUF_COUNT); + atomic_set(&audio->out_count, 0x00); + audio->enc_compat_ioctl = evrc_in_compat_ioctl; + audio->enc_ioctl = evrc_in_ioctl; + file->private_data = audio; + + pr_info("%s:session id %d: success\n", __func__, audio->ac->session); + return 0; +fail: + q6asm_audio_client_free(audio->ac); + kfree(audio->enc_cfg); + kfree(audio); + return rc; +} + +static const struct file_operations audio_in_fops = { + .owner = THIS_MODULE, + .open = evrc_in_open, + .release = audio_in_release, + .read = audio_in_read, + .write = audio_in_write, + .unlocked_ioctl = audio_in_ioctl, + .compat_ioctl = audio_in_compat_ioctl +}; + +struct miscdevice audio_evrc_in_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_evrc_in", + .fops = &audio_in_fops, +}; + +int __init evrc_in_init(void) +{ + return misc_register(&audio_evrc_in_misc); +} + +void evrc_in_exit(void) +{ + misc_deregister(&audio_evrc_in_misc); +} diff --git a/audio-kernel/dsp/codecs/g711alaw_in.c b/audio-kernel/dsp/codecs/g711alaw_in.c new file mode 100644 index 000000000..c668b48b8 --- /dev/null +++ b/audio-kernel/dsp/codecs/g711alaw_in.c @@ -0,0 +1,385 @@ +/* Copyright (c) 2010-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 + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "audio_utils.h" + +/* Buffer with meta*/ +#define PCM_BUF_SIZE (4096 + sizeof(struct meta_in)) + +/* Maximum 10 frames in buffer with meta */ +#define FRAME_SIZE (1 + ((320+sizeof(struct meta_out_dsp)) * 10)) +static long g711_in_ioctl_shared(struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct q6audio_in *audio = file->private_data; + int rc = 0; + int cnt = 0; + + switch (cmd) { + case AUDIO_START: { + struct msm_audio_g711_enc_config *enc_cfg; + + enc_cfg = (struct msm_audio_g711_enc_config *)audio->enc_cfg; + pr_debug("%s:session id %d: default buf alloc[%d]\n", __func__, + audio->ac->session, audio->buf_alloc); + if (audio->enabled == 1) { + rc = 0; + break; + } + rc = audio_in_buf_alloc(audio); + if (rc < 0) { + pr_err("%s:session id %d: buffer allocation failed rc=%d\n", + __func__, audio->ac->session, rc); + break; + } + pr_debug("%s: sample rate %d", __func__, enc_cfg->sample_rate); + rc = q6asm_enc_cfg_blk_g711(audio->ac, + audio->buf_cfg.frames_per_buf, + enc_cfg->sample_rate); + + if (rc < 0) { + pr_err("%s:session id %d: cmd g711 media format block failed rc=%d\n", + __func__, audio->ac->session, rc); + break; + } + if (audio->feedback == NON_TUNNEL_MODE) { + rc = q6asm_media_format_block_pcm(audio->ac, + audio->pcm_cfg.sample_rate, + audio->pcm_cfg.channel_count); + + if (rc < 0) { + pr_err("%s:session id %d: media format block failed rc=%d\n", + __func__, audio->ac->session, rc); + break; + } + } + pr_debug("%s:session id %d: AUDIO_START enable[%d]\n", __func__, + audio->ac->session, audio->enabled); + rc = audio_in_enable(audio); + if (!rc) { + audio->enabled = 1; + } else { + audio->enabled = 0; + pr_err("%s:session id %d: Audio Start procedure failed rc=%d\n", + __func__, audio->ac->session, rc); + break; + } + while (cnt++ < audio->str_cfg.buffer_count) + q6asm_read(audio->ac); /* Push buffer to DSP */ + rc = 0; + pr_debug("%s:session id %d: AUDIO_START success enable[%d]\n", + __func__, audio->ac->session, audio->enabled); + break; + } + case AUDIO_STOP: { + pr_debug("%s:session id %d: AUDIO_STOP\n", __func__, + audio->ac->session); + rc = audio_in_disable(audio); + if (rc < 0) { + pr_err("%s:session id %d: Audio Stop procedure failed rc=%d\n", + __func__, audio->ac->session, + rc); + break; + } + break; + } + case AUDIO_SET_G711_ENC_CONFIG: { + struct msm_audio_g711_enc_config *cfg; + struct msm_audio_g711_enc_config *enc_cfg; + + enc_cfg = (struct msm_audio_g711_enc_config *)audio->enc_cfg; + + cfg = (struct msm_audio_g711_enc_config *)arg; + if (cfg == NULL) { + pr_err("%s: NULL config pointer\n", __func__); + rc = -EINVAL; + break; + } + if (cfg->sample_rate != 8000 && + cfg->sample_rate != 16000) { + pr_err("%s:session id %d: invalid sample rate\n", + __func__, audio->ac->session); + rc = -EINVAL; + break; + } + enc_cfg->sample_rate = cfg->sample_rate; + pr_debug("%s:session id %d: sample_rate= 0x%x", + __func__, + audio->ac->session, enc_cfg->sample_rate); + break; + } + default: + pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd); + rc = -ENOIOCTLCMD; + } + return rc; +} + +static long g711_in_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct q6audio_in *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_START: + case AUDIO_STOP: { + rc = g711_in_ioctl_shared(file, cmd, arg); + break; + } + case AUDIO_GET_G711_ENC_CONFIG: { + if (copy_to_user((void *)arg, audio->enc_cfg, + sizeof(struct msm_audio_g711_enc_config))) { + pr_err( + "%s: copy_to_user for AUDIO_GET_g711_ENC_CONFIG failed", + __func__); + rc = -EFAULT; + } + break; + } + case AUDIO_SET_G711_ENC_CONFIG: { + struct msm_audio_g711_enc_config cfg; + + if (copy_from_user(&cfg, (void *) arg, + sizeof(cfg))) { + pr_err( + "%s: copy_from_user for AUDIO_GET_G711_ENC_CONFIG failed", + __func__); + rc = -EFAULT; + break; + } + rc = g711_in_ioctl_shared(file, cmd, (unsigned long)&cfg); + if (rc) + pr_err("%s:AUDIO_GET_G711_ENC_CONFIG failed. Rc= %d\n", + __func__, rc); + break; + } + default: + pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd); + rc = -ENOIOCTLCMD; + } + return rc; +} + +#ifdef CONFIG_COMPAT +struct msm_audio_g711_enc_config32 { + uint32_t sample_rate; +}; + +enum { + AUDIO_SET_G711_ENC_CONFIG_32 = _IOW(AUDIO_IOCTL_MAGIC, + (AUDIO_MAX_COMMON_IOCTL_NUM+0), struct msm_audio_g711_enc_config32), + AUDIO_GET_G711_ENC_CONFIG_32 = _IOR(AUDIO_IOCTL_MAGIC, + (AUDIO_MAX_COMMON_IOCTL_NUM+1), struct msm_audio_g711_enc_config32) +}; + +static long g711_in_compat_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct q6audio_in *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_START: + case AUDIO_STOP: { + rc = g711_in_ioctl_shared(file, cmd, arg); + break; + } + case AUDIO_GET_G711_ENC_CONFIG_32: { + struct msm_audio_g711_enc_config32 cfg_32; + struct msm_audio_g711_enc_config32 *enc_cfg; + + enc_cfg = (struct msm_audio_g711_enc_config32 *)audio->enc_cfg; + cfg_32.sample_rate = enc_cfg->sample_rate; + if (copy_to_user((void *)arg, &cfg_32, + sizeof(cfg_32))) { + pr_err("%s: copy_to_user for AUDIO_GET_G711_ENC_CONFIG_32 failed\n", + __func__); + rc = -EFAULT; + } + break; + } + case AUDIO_SET_G711_ENC_CONFIG_32: { + struct msm_audio_g711_enc_config32 cfg_32; + struct msm_audio_g711_enc_config32 cfg; + + if (copy_from_user(&cfg_32, (void *) arg, + sizeof(cfg_32))) { + pr_err("%s: copy_from_user for AUDIO_SET_G711_ENC_CONFIG_32 failed\n", + __func__); + rc = -EFAULT; + break; + } + cfg.sample_rate = cfg_32.sample_rate; + cmd = AUDIO_SET_G711_ENC_CONFIG; + rc = g711_in_ioctl_shared(file, cmd, (unsigned long)&cfg); + if (rc) + pr_err("%s:AUDIO_SET_G711_ENC_CONFIG failed. rc= %d\n", + __func__, rc); + break; + } + default: + pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd); + rc = -ENOIOCTLCMD; + } + return rc; +} +#else +#define g711_in_compat_ioctl NULL +#endif + +static int g711_in_open(struct inode *inode, struct file *file) +{ + struct q6audio_in *audio = NULL; + struct msm_audio_g711_enc_config *enc_cfg; + int rc = 0; + + audio = kzalloc(sizeof(struct q6audio_in), GFP_KERNEL); + + if (audio == NULL) + return -ENOMEM; + /* Allocate memory for encoder config param */ + audio->enc_cfg = kzalloc(sizeof(struct msm_audio_g711_enc_config), + GFP_KERNEL); + if (audio->enc_cfg == NULL) { + kfree(audio); + return -ENOMEM; + } + enc_cfg = audio->enc_cfg; + + mutex_init(&audio->lock); + mutex_init(&audio->read_lock); + mutex_init(&audio->write_lock); + spin_lock_init(&audio->dsp_lock); + init_waitqueue_head(&audio->read_wait); + init_waitqueue_head(&audio->write_wait); + + /* + * Settings will be re-config at AUDIO_SET_CONFIG, + * but at least we need to have initial config + */ + audio->str_cfg.buffer_size = FRAME_SIZE; + audio->str_cfg.buffer_count = FRAME_NUM; + audio->min_frame_size = 320; + audio->max_frames_per_buf = 10; + audio->pcm_cfg.buffer_size = PCM_BUF_SIZE; + audio->pcm_cfg.buffer_count = PCM_BUF_COUNT; + enc_cfg->sample_rate = 8000; + audio->pcm_cfg.channel_count = 1; + audio->pcm_cfg.sample_rate = 8000; + audio->buf_cfg.meta_info_enable = 0x01; + audio->buf_cfg.frames_per_buf = 0x01; + audio->event_abort = 0; + + audio->ac = q6asm_audio_client_alloc((app_cb)q6asm_in_cb, + (void *)audio); + + if (!audio->ac) { + kfree(audio->enc_cfg); + kfree(audio); + return -ENOMEM; + } + + /* open g711 encoder in T/NT mode */ + if ((file->f_mode & FMODE_WRITE) && + (file->f_mode & FMODE_READ)) { + audio->feedback = NON_TUNNEL_MODE; + rc = q6asm_open_read_write(audio->ac, FORMAT_G711_ALAW_FS, + FORMAT_LINEAR_PCM); + if (rc < 0) { + pr_err("%s:session id %d: NT mode Open failed rc=%d\n", + __func__, audio->ac->session, rc); + rc = -ENODEV; + goto fail; + } + } else if (!(file->f_mode & FMODE_WRITE) && + (file->f_mode & FMODE_READ)) { + audio->feedback = TUNNEL_MODE; + rc = q6asm_open_read(audio->ac, FORMAT_G711_ALAW_FS); + if (rc < 0) { + pr_err("%s:session id %d: T mode Open failed rc=%d\n", + __func__, audio->ac->session, rc); + rc = -ENODEV; + goto fail; + } + /* register for tx overflow (valid for tunnel mode only) */ + rc = q6asm_reg_tx_overflow(audio->ac, 0x01); + if (rc < 0) { + pr_err("%s:session id %d: TX Overflow registration failed rc=%d\n", + __func__, audio->ac->session, rc); + rc = -ENODEV; + goto fail; + } + } else { + pr_err("%s:session id %d: Unexpected mode\n", __func__, + audio->ac->session); + rc = -EACCES; + goto fail; + } + + audio->opened = 1; + audio->reset_event = false; + atomic_set(&audio->in_count, PCM_BUF_COUNT); + atomic_set(&audio->out_count, 0x00); + audio->enc_compat_ioctl = g711_in_compat_ioctl; + audio->enc_ioctl = g711_in_ioctl; + file->private_data = audio; + + pr_info("%s:session id %d: success\n", __func__, audio->ac->session); + return 0; +fail: + q6asm_audio_client_free(audio->ac); + kfree(audio->enc_cfg); + kfree(audio); + return rc; +} + +static const struct file_operations audio_in_fops = { + .owner = THIS_MODULE, + .open = g711_in_open, + .release = audio_in_release, + .read = audio_in_read, + .write = audio_in_write, + .unlocked_ioctl = audio_in_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = audio_in_compat_ioctl, +#endif +}; + +struct miscdevice audio_g711alaw_in_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_g711alaw_in", + .fops = &audio_in_fops, +}; + +int __init g711alaw_in_init(void) +{ + return misc_register(&audio_g711alaw_in_misc); +} + +void g711alaw_in_exit(void) +{ + misc_deregister(&audio_g711alaw_in_misc); +} diff --git a/audio-kernel/dsp/codecs/g711mlaw_in.c b/audio-kernel/dsp/codecs/g711mlaw_in.c new file mode 100644 index 000000000..2f20c1335 --- /dev/null +++ b/audio-kernel/dsp/codecs/g711mlaw_in.c @@ -0,0 +1,388 @@ +/* Copyright (c) 2010-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 + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "audio_utils.h" + +#ifdef CONFIG_COMPAT +#undef PROC_ADD +#endif +/* Buffer with meta*/ +#define PCM_BUF_SIZE (4096 + sizeof(struct meta_in)) + +/* Maximum 10 frames in buffer with meta */ +#define FRAME_SIZE (1 + ((320+sizeof(struct meta_out_dsp)) * 10)) +static long g711_in_ioctl_shared(struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct q6audio_in *audio = file->private_data; + int rc = 0; + int cnt = 0; + + switch (cmd) { + case AUDIO_START: { + struct msm_audio_g711_enc_config *enc_cfg; + + enc_cfg = (struct msm_audio_g711_enc_config *)audio->enc_cfg; + pr_debug("%s:session id %d: default buf alloc[%d]\n", __func__, + audio->ac->session, audio->buf_alloc); + if (audio->enabled == 1) { + rc = 0; + break; + } + rc = audio_in_buf_alloc(audio); + if (rc < 0) { + pr_err("%s:session id %d: buffer allocation failed rc=%d\n", + __func__, audio->ac->session, rc); + break; + } + pr_debug("%s: sample rate %d", __func__, enc_cfg->sample_rate); + rc = q6asm_enc_cfg_blk_g711(audio->ac, + audio->buf_cfg.frames_per_buf, + enc_cfg->sample_rate); + + if (rc < 0) { + pr_err("%s:session id %d: cmd g711 media format block failed rc=%d\n", + __func__, audio->ac->session, rc); + break; + } + if (audio->feedback == NON_TUNNEL_MODE) { + rc = q6asm_media_format_block_pcm(audio->ac, + audio->pcm_cfg.sample_rate, + audio->pcm_cfg.channel_count); + + if (rc < 0) { + pr_err("%s:session id %d: media format block failed rc=%d\n", + __func__, audio->ac->session, rc); + break; + } + } + pr_debug("%s:session id %d: AUDIO_START enable[%d]\n", __func__, + audio->ac->session, audio->enabled); + rc = audio_in_enable(audio); + if (!rc) { + audio->enabled = 1; + } else { + audio->enabled = 0; + pr_err("%s:session id %d: Audio Start procedure failed rc=%d\n", + __func__, audio->ac->session, rc); + break; + } + while (cnt++ < audio->str_cfg.buffer_count) + q6asm_read(audio->ac); /* Push buffer to DSP */ + rc = 0; + pr_debug("%s:session id %d: AUDIO_START success enable[%d]\n", + __func__, audio->ac->session, audio->enabled); + break; + } + case AUDIO_STOP: { + pr_debug("%s:session id %d: AUDIO_STOP\n", __func__, + audio->ac->session); + rc = audio_in_disable(audio); + if (rc < 0) { + pr_err("%s:session id %d: Audio Stop procedure failed rc=%d\n", + __func__, audio->ac->session, + rc); + break; + } + break; + } + case AUDIO_SET_G711_ENC_CONFIG: { + struct msm_audio_g711_enc_config *cfg; + struct msm_audio_g711_enc_config *enc_cfg; + + enc_cfg = (struct msm_audio_g711_enc_config *)audio->enc_cfg; + + cfg = (struct msm_audio_g711_enc_config *)arg; + if (cfg == NULL) { + pr_err("%s: NULL config pointer\n", __func__); + rc = -EINVAL; + break; + } + if (cfg->sample_rate != 8000 && + cfg->sample_rate != 16000) { + pr_err("%s:session id %d: invalid sample rate\n", + __func__, audio->ac->session); + rc = -EINVAL; + break; + } + enc_cfg->sample_rate = cfg->sample_rate; + pr_debug("%s:session id %d: sample_rate= 0x%x", + __func__, + audio->ac->session, enc_cfg->sample_rate); + break; + } + default: + pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd); + rc = -ENOIOCTLCMD; + } + return rc; +} + +static long g711_in_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct q6audio_in *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_START: + case AUDIO_STOP: { + rc = g711_in_ioctl_shared(file, cmd, arg); + break; + } + case AUDIO_GET_G711_ENC_CONFIG: { + if (copy_to_user((void *)arg, audio->enc_cfg, + sizeof(struct msm_audio_g711_enc_config))) { + pr_err( + "%s: copy_to_user for AUDIO_GET_g711_ENC_CONFIG failed", + __func__); + rc = -EFAULT; + } + break; + } + case AUDIO_SET_G711_ENC_CONFIG: { + struct msm_audio_g711_enc_config cfg; + + if (copy_from_user(&cfg, (void *) arg, + sizeof(cfg))) { + pr_err( + "%s: copy_from_user for AUDIO_GET_G711_ENC_CONFIG failed", + __func__); + rc = -EFAULT; + break; + } + rc = g711_in_ioctl_shared(file, cmd, (unsigned long)&cfg); + if (rc) + pr_err("%s:AUDIO_GET_G711_ENC_CONFIG failed. Rc= %d\n", + __func__, rc); + break; + } + default: + pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd); + rc = -ENOIOCTLCMD; + } + return rc; +} + +#ifdef CONFIG_COMPAT +struct msm_audio_g711_enc_config32 { + uint32_t sample_rate; +}; + +enum { + AUDIO_SET_G711_ENC_CONFIG_32 = _IOW(AUDIO_IOCTL_MAGIC, + (AUDIO_MAX_COMMON_IOCTL_NUM+0), struct msm_audio_g711_enc_config32), + AUDIO_GET_G711_ENC_CONFIG_32 = _IOR(AUDIO_IOCTL_MAGIC, + (AUDIO_MAX_COMMON_IOCTL_NUM+1), struct msm_audio_g711_enc_config32) +}; + +static long g711_in_compat_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct q6audio_in *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_START: + case AUDIO_STOP: { + rc = g711_in_ioctl_shared(file, cmd, arg); + break; + } + case AUDIO_GET_G711_ENC_CONFIG_32: { + struct msm_audio_g711_enc_config32 cfg_32; + struct msm_audio_g711_enc_config32 *enc_cfg; + + enc_cfg = (struct msm_audio_g711_enc_config32 *)audio->enc_cfg; + cfg_32.sample_rate = enc_cfg->sample_rate; + if (copy_to_user((void *)arg, &cfg_32, + sizeof(cfg_32))) { + pr_err("%s: copy_to_user for AUDIO_GET_G711_ENC_CONFIG_32 failed\n", + __func__); + rc = -EFAULT; + } + break; + } + case AUDIO_SET_G711_ENC_CONFIG_32: { + struct msm_audio_g711_enc_config32 cfg_32; + struct msm_audio_g711_enc_config32 cfg; + + if (copy_from_user(&cfg_32, (void *) arg, + sizeof(cfg_32))) { + pr_err("%s: copy_from_user for AUDIO_SET_G711_ENC_CONFIG_32 failed\n", + __func__); + rc = -EFAULT; + break; + } + cfg.sample_rate = cfg_32.sample_rate; + cmd = AUDIO_SET_G711_ENC_CONFIG; + rc = g711_in_ioctl_shared(file, cmd, (unsigned long)&cfg); + if (rc) + pr_err("%s:AUDIO_SET_G711_ENC_CONFIG failed. rc= %d\n", + __func__, rc); + break; + } + default: + pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd); + rc = -ENOIOCTLCMD; + } + return rc; +} +#else +#define g711_in_compat_ioctl NULL +#endif + +static int g711_in_open(struct inode *inode, struct file *file) +{ + struct q6audio_in *audio = NULL; + struct msm_audio_g711_enc_config *enc_cfg; + int rc = 0; + + audio = kzalloc(sizeof(struct q6audio_in), GFP_KERNEL); + + if (audio == NULL) + return -ENOMEM; + /* Allocate memory for encoder config param */ + audio->enc_cfg = kzalloc(sizeof(struct msm_audio_g711_enc_config), + GFP_KERNEL); + if (audio->enc_cfg == NULL) { + kfree(audio); + return -ENOMEM; + } + enc_cfg = audio->enc_cfg; + + mutex_init(&audio->lock); + mutex_init(&audio->read_lock); + mutex_init(&audio->write_lock); + spin_lock_init(&audio->dsp_lock); + init_waitqueue_head(&audio->read_wait); + init_waitqueue_head(&audio->write_wait); + + /* + * Settings will be re-config at AUDIO_SET_CONFIG, + * but at least we need to have initial config + */ + audio->str_cfg.buffer_size = FRAME_SIZE; + audio->str_cfg.buffer_count = FRAME_NUM; + audio->min_frame_size = 320; + audio->max_frames_per_buf = 10; + audio->pcm_cfg.buffer_size = PCM_BUF_SIZE; + audio->pcm_cfg.buffer_count = PCM_BUF_COUNT; + enc_cfg->sample_rate = 8000; + audio->pcm_cfg.channel_count = 1; + audio->pcm_cfg.sample_rate = 8000; + audio->buf_cfg.meta_info_enable = 0x01; + audio->buf_cfg.frames_per_buf = 0x01; + audio->event_abort = 0; + + audio->ac = q6asm_audio_client_alloc((app_cb)q6asm_in_cb, + (void *)audio); + + if (!audio->ac) { + kfree(audio->enc_cfg); + kfree(audio); + return -ENOMEM; + } + + /* open g711 encoder in T/NT mode */ + if ((file->f_mode & FMODE_WRITE) && + (file->f_mode & FMODE_READ)) { + audio->feedback = NON_TUNNEL_MODE; + rc = q6asm_open_read_write(audio->ac, FORMAT_G711_MLAW_FS, + FORMAT_LINEAR_PCM); + if (rc < 0) { + pr_err("%s:session id %d: NT mode Open failed rc=%d\n", + __func__, audio->ac->session, rc); + rc = -ENODEV; + goto fail; + } + } else if (!(file->f_mode & FMODE_WRITE) && + (file->f_mode & FMODE_READ)) { + audio->feedback = TUNNEL_MODE; + rc = q6asm_open_read(audio->ac, FORMAT_G711_MLAW_FS); + if (rc < 0) { + pr_err("%s:session id %d: T mode Open failed rc=%d\n", + __func__, audio->ac->session, rc); + rc = -ENODEV; + goto fail; + } + /* register for tx overflow (valid for tunnel mode only) */ + rc = q6asm_reg_tx_overflow(audio->ac, 0x01); + if (rc < 0) { + pr_err("%s:session id %d: TX Overflow registration failed rc=%d\n", + __func__, audio->ac->session, rc); + rc = -ENODEV; + goto fail; + } + } else { + pr_err("%s:session id %d: Unexpected mode\n", __func__, + audio->ac->session); + rc = -EACCES; + goto fail; + } + + audio->opened = 1; + audio->reset_event = false; + atomic_set(&audio->in_count, PCM_BUF_COUNT); + atomic_set(&audio->out_count, 0x00); + audio->enc_compat_ioctl = g711_in_compat_ioctl; + audio->enc_ioctl = g711_in_ioctl; + file->private_data = audio; + + pr_info("%s:session id %d: success\n", __func__, audio->ac->session); + return 0; +fail: + q6asm_audio_client_free(audio->ac); + kfree(audio->enc_cfg); + kfree(audio); + return rc; +} + +static const struct file_operations audio_in_fops = { + .owner = THIS_MODULE, + .open = g711_in_open, + .release = audio_in_release, + .read = audio_in_read, + .write = audio_in_write, + .unlocked_ioctl = audio_in_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = audio_in_compat_ioctl, +#endif +}; + +struct miscdevice audio_g711mlaw_in_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_g711mlaw_in", + .fops = &audio_in_fops, +}; + +int __init g711mlaw_in_init(void) +{ + return misc_register(&audio_g711mlaw_in_misc); +} + +void g711mlaw_in_exit(void) +{ + misc_deregister(&audio_g711mlaw_in_misc); +} diff --git a/audio-kernel/dsp/codecs/q6audio_common.h b/audio-kernel/dsp/codecs/q6audio_common.h new file mode 100644 index 000000000..75f20e5ac --- /dev/null +++ b/audio-kernel/dsp/codecs/q6audio_common.h @@ -0,0 +1,37 @@ +/* Copyright (c) 2012-2014, 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 + * 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. + * + */ + + +/* For Decoders */ +#ifndef __Q6_AUDIO_COMMON_H__ +#define __Q6_AUDIO_COMMON_H__ + +#include +#include + + +void q6_audio_cb(uint32_t opcode, uint32_t token, + uint32_t *payload, void *priv); + +void audio_aio_cb(uint32_t opcode, uint32_t token, + uint32_t *payload, void *audio); + + +/* For Encoders */ +void q6asm_in_cb(uint32_t opcode, uint32_t token, + uint32_t *payload, void *priv); + +void audio_in_get_dsp_frames(void *audio, + uint32_t token, uint32_t *payload); + +#endif /*__Q6_AUDIO_COMMON_H__*/ diff --git a/audio-kernel/dsp/codecs/q6audio_v2.c b/audio-kernel/dsp/codecs/q6audio_v2.c new file mode 100644 index 000000000..ea3113217 --- /dev/null +++ b/audio-kernel/dsp/codecs/q6audio_v2.c @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2012-2013, 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 + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "audio_utils.h" + +void q6asm_in_cb(uint32_t opcode, uint32_t token, + uint32_t *payload, void *priv) +{ + struct q6audio_in *audio = (struct q6audio_in *)priv; + unsigned long flags; + + pr_debug("%s:session id %d: opcode[0x%x]\n", __func__, + audio->ac->session, opcode); + + spin_lock_irqsave(&audio->dsp_lock, flags); + switch (opcode) { + case ASM_DATA_EVENT_READ_DONE_V2: + audio_in_get_dsp_frames(audio, token, payload); + break; + case ASM_DATA_EVENT_WRITE_DONE_V2: + atomic_inc(&audio->in_count); + wake_up(&audio->write_wait); + break; + case ASM_DATA_EVENT_RENDERED_EOS: + audio->eos_rsp = 1; + wake_up(&audio->read_wait); + break; + case ASM_STREAM_CMDRSP_GET_PP_PARAMS_V2: + break; + case ASM_SESSION_EVENTX_OVERFLOW: + pr_err("%s:session id %d: ASM_SESSION_EVENT_TX_OVERFLOW\n", + __func__, audio->ac->session); + break; + case RESET_EVENTS: + pr_debug("%s:received RESET EVENTS\n", __func__); + audio->enabled = 0; + audio->stopped = 1; + audio->event_abort = 1; + audio->reset_event = true; + wake_up(&audio->read_wait); + wake_up(&audio->write_wait); + break; + default: + pr_debug("%s:session id %d: Ignore opcode[0x%x]\n", __func__, + audio->ac->session, opcode); + break; + } + spin_unlock_irqrestore(&audio->dsp_lock, flags); +} + +void audio_in_get_dsp_frames(void *priv, + uint32_t token, uint32_t *payload) +{ + struct q6audio_in *audio = (struct q6audio_in *)priv; + uint32_t index; + + index = q6asm_get_buf_index_from_token(token); + pr_debug("%s:session id %d: index=%d nr frames=%d offset[%d]\n", + __func__, audio->ac->session, token, payload[9], + payload[5]); + pr_debug("%s:session id %d: timemsw=%d lsw=%d\n", __func__, + audio->ac->session, payload[7], payload[6]); + pr_debug("%s:session id %d: uflags=0x%8x uid=0x%8x\n", __func__, + audio->ac->session, payload[8], payload[10]); + pr_debug("%s:session id %d: enc_framesotal_size=0x%8x\n", __func__, + audio->ac->session, payload[4]); + + /* Ensure the index is within max array size: FRAME_NUM */ + if (index >= FRAME_NUM) { + pr_err("%s: Invalid index %d\n", + __func__, index); + return; + } + + audio->out_frame_info[index][0] = payload[9]; + audio->out_frame_info[index][1] = payload[5]; + + /* statistics of read */ + atomic_add(payload[4], &audio->in_bytes); + atomic_add(payload[9], &audio->in_samples); + + if (atomic_read(&audio->out_count) <= audio->str_cfg.buffer_count) { + atomic_inc(&audio->out_count); + wake_up(&audio->read_wait); + } +} diff --git a/audio-kernel/dsp/codecs/q6audio_v2_aio.c b/audio-kernel/dsp/codecs/q6audio_v2_aio.c new file mode 100644 index 000000000..973108de9 --- /dev/null +++ b/audio-kernel/dsp/codecs/q6audio_v2_aio.c @@ -0,0 +1,232 @@ +/* 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 + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "audio_utils_aio.h" + +void q6_audio_cb(uint32_t opcode, uint32_t token, + uint32_t *payload, void *priv) +{ + struct q6audio_aio *audio = (struct q6audio_aio *)priv; + + pr_debug("%s:opcode = %x token = 0x%x\n", __func__, opcode, token); + switch (opcode) { + case ASM_DATA_EVENT_WRITE_DONE_V2: + case ASM_DATA_EVENT_READ_DONE_V2: + case ASM_DATA_EVENT_RENDERED_EOS: + case ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2: + case ASM_STREAM_CMD_SET_ENCDEC_PARAM: + case ASM_DATA_EVENT_SR_CM_CHANGE_NOTIFY: + case ASM_DATA_EVENT_ENC_SR_CM_CHANGE_NOTIFY: + case RESET_EVENTS: + audio_aio_cb(opcode, token, payload, audio); + break; + default: + pr_debug("%s:Unhandled event = 0x%8x\n", __func__, opcode); + break; + } +} + +void audio_aio_cb(uint32_t opcode, uint32_t token, + uint32_t *payload, void *priv/*struct q6audio_aio *audio*/) +{ + struct q6audio_aio *audio = (struct q6audio_aio *)priv; + union msm_audio_event_payload e_payload; + + switch (opcode) { + case ASM_DATA_EVENT_WRITE_DONE_V2: + pr_debug("%s[%pK]:ASM_DATA_EVENT_WRITE_DONE token = 0x%x\n", + __func__, audio, token); + audio_aio_async_write_ack(audio, token, payload); + break; + case ASM_DATA_EVENT_READ_DONE_V2: + pr_debug("%s[%pK]:ASM_DATA_EVENT_READ_DONE token = 0x%x\n", + __func__, audio, token); + audio_aio_async_read_ack(audio, token, payload); + break; + case ASM_DATA_EVENT_RENDERED_EOS: + /* EOS Handle */ + pr_debug("%s[%pK]:ASM_DATA_CMDRSP_EOS\n", __func__, audio); + if (audio->feedback) { /* Non-Tunnel mode */ + audio->eos_rsp = 1; + /* propagate input EOS i/p buffer, + * after receiving DSP acknowledgment + */ + if (audio->eos_flag && + (audio->eos_write_payload.aio_buf.buf_addr)) { + audio_aio_post_event(audio, + AUDIO_EVENT_WRITE_DONE, + audio->eos_write_payload); + memset(&audio->eos_write_payload, 0, + sizeof(union msm_audio_event_payload)); + audio->eos_flag = 0; + } + } else { /* Tunnel mode */ + audio->eos_rsp = 1; + wake_up(&audio->write_wait); + wake_up(&audio->cmd_wait); + } + break; + case ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2: + case ASM_STREAM_CMD_SET_ENCDEC_PARAM: + pr_debug("%s[%pK]:payload0[%x] payloa1d[%x]opcode= 0x%x\n", + __func__, audio, payload[0], payload[1], opcode); + break; + case ASM_DATA_EVENT_SR_CM_CHANGE_NOTIFY: + case ASM_DATA_EVENT_ENC_SR_CM_CHANGE_NOTIFY: + pr_debug("%s[%pK]: ASM_DATA_EVENT_SR_CM_CHANGE_NOTIFY, payload[0]-sr = %d, payload[1]-chl = %d, payload[2] = %d, payload[3] = %d\n", + __func__, audio, payload[0], + payload[1], payload[2], payload[3]); + + pr_debug("%s[%pK]: ASM_DATA_EVENT_SR_CM_CHANGE_NOTIFY, sr(prev) = %d, chl(prev) = %d,", + __func__, audio, audio->pcm_cfg.sample_rate, + audio->pcm_cfg.channel_count); + + audio->pcm_cfg.sample_rate = payload[0]; + audio->pcm_cfg.channel_count = payload[1] & 0xFFFF; + e_payload.stream_info.chan_info = audio->pcm_cfg.channel_count; + e_payload.stream_info.sample_rate = audio->pcm_cfg.sample_rate; + audio_aio_post_event(audio, AUDIO_EVENT_STREAM_INFO, e_payload); + break; + case RESET_EVENTS: + pr_err("%s: Received opcode:0x%x\n", __func__, opcode); + audio->stopped = 1; + audio->reset_event = true; + wake_up(&audio->event_wait); + break; + default: + break; + } +} + +int extract_meta_out_info(struct q6audio_aio *audio, + struct audio_aio_buffer_node *buf_node, int dir) +{ + struct dec_meta_out *meta_data = buf_node->kvaddr; + uint32_t temp; + + if (dir) { /* input buffer - Write */ + if (audio->buf_cfg.meta_info_enable) { + if (buf_node->buf.buf_len < + sizeof(struct dec_meta_in)) { + pr_debug("%s: invalid buf len %d\n", + __func__, buf_node->buf.buf_len); + return -EINVAL; + } + memcpy(&buf_node->meta_info.meta_in, + (char *)buf_node->kvaddr, sizeof(struct dec_meta_in)); + } else { + memset(&buf_node->meta_info.meta_in, + 0, sizeof(struct dec_meta_in)); + } + pr_debug("%s[%pK]:i/p: msw_ts %d lsw_ts %d nflags 0x%8x\n", + __func__, audio, + buf_node->meta_info.meta_in.ntimestamp.highpart, + buf_node->meta_info.meta_in.ntimestamp.lowpart, + buf_node->meta_info.meta_in.nflags); + } else { /* output buffer - Read */ + memcpy((char *)buf_node->kvaddr, + &buf_node->meta_info.meta_out, + sizeof(struct dec_meta_out)); + meta_data->meta_out_dsp[0].nflags = 0x00000000; + temp = meta_data->meta_out_dsp[0].msw_ts; + meta_data->meta_out_dsp[0].msw_ts = + meta_data->meta_out_dsp[0].lsw_ts; + meta_data->meta_out_dsp[0].lsw_ts = temp; + + pr_debug("%s[%pK]:o/p: msw_ts %d lsw_ts %d nflags 0x%8x, num_frames = %d\n", + __func__, audio, + ((struct dec_meta_out *)buf_node->kvaddr)-> + meta_out_dsp[0].msw_ts, + ((struct dec_meta_out *)buf_node->kvaddr)-> + meta_out_dsp[0].lsw_ts, + ((struct dec_meta_out *)buf_node->kvaddr)-> + meta_out_dsp[0].nflags, + ((struct dec_meta_out *)buf_node->kvaddr)->num_of_frames); + } + return 0; +} + +/* Read buffer from DSP / Handle Ack from DSP */ +void audio_aio_async_read_ack(struct q6audio_aio *audio, uint32_t token, + uint32_t *payload) +{ + unsigned long flags; + union msm_audio_event_payload event_payload; + struct audio_aio_buffer_node *filled_buf; + int ret; + + pr_debug("%s\n", __func__); + + /* No active flush in progress */ + if (audio->rflush) + return; + + /* Statistics of read */ + atomic_add(payload[4], &audio->in_bytes); + atomic_add(payload[9], &audio->in_samples); + + spin_lock_irqsave(&audio->dsp_lock, flags); + if (list_empty(&audio->in_queue)) { + spin_unlock_irqrestore(&audio->dsp_lock, flags); + pr_warn("%s unexpected ack from dsp\n", __func__); + return; + } + filled_buf = list_first_entry(&audio->in_queue, + struct audio_aio_buffer_node, list); + + pr_debug("%s token: 0x[%x], filled_buf->token: 0x[%x]", + __func__, token, filled_buf->token); + if (token == (filled_buf->token)) { + list_del(&filled_buf->list); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + event_payload.aio_buf = filled_buf->buf; + /* Read done Buffer due to flush/normal condition + * after EOS event, so append EOS buffer + */ + if (audio->eos_rsp == 0x1) { + event_payload.aio_buf.data_len = + insert_eos_buf(audio, filled_buf); + /* Reset flag back to indicate eos intimated */ + audio->eos_rsp = 0; + } else { + filled_buf->meta_info.meta_out.num_of_frames + = payload[9]; + event_payload.aio_buf.data_len = payload[4] + + payload[5] + sizeof(struct dec_meta_out); + pr_debug("%s[%pK]:nr of frames 0x%8x len=%d\n", + __func__, audio, + filled_buf->meta_info.meta_out.num_of_frames, + event_payload.aio_buf.data_len); + ret = extract_meta_out_info(audio, filled_buf, 0); + audio->eos_rsp = 0; + } + pr_debug("%s, posting read done to the app here\n", __func__); + audio_aio_post_event(audio, AUDIO_EVENT_READ_DONE, + event_payload); + kfree(filled_buf); + } else { + pr_err("%s[%pK]:expected=%x ret=%x\n", + __func__, audio, filled_buf->token, token); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + } +} diff --git a/audio-kernel/dsp/codecs/qcelp_in.c b/audio-kernel/dsp/codecs/qcelp_in.c new file mode 100644 index 000000000..be09ea4a0 --- /dev/null +++ b/audio-kernel/dsp/codecs/qcelp_in.c @@ -0,0 +1,413 @@ +/* Copyright (c) 2010-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 + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "audio_utils.h" + +/* Buffer with meta*/ +#define PCM_BUF_SIZE (4096 + sizeof(struct meta_in)) + +/* Maximum 10 frames in buffer with meta */ +#define FRAME_SIZE (1 + ((35+sizeof(struct meta_out_dsp)) * 10)) + +static long qcelp_in_ioctl_shared(struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct q6audio_in *audio = file->private_data; + int rc = 0; + int cnt = 0; + + switch (cmd) { + case AUDIO_START: { + struct msm_audio_qcelp_enc_config *enc_cfg; + + enc_cfg = audio->enc_cfg; + pr_debug("%s:session id %d: default buf alloc[%d]\n", __func__, + audio->ac->session, audio->buf_alloc); + if (audio->enabled == 1) { + pr_info("%s:AUDIO_START already over\n", __func__); + rc = 0; + break; + } + rc = audio_in_buf_alloc(audio); + if (rc < 0) { + pr_err("%s:session id %d: buffer allocation failed\n", + __func__, audio->ac->session); + break; + } + + /* reduced_rate_level, rate_modulation_cmd set to zero + * currently not configurable from user space + */ + rc = q6asm_enc_cfg_blk_qcelp(audio->ac, + audio->buf_cfg.frames_per_buf, + enc_cfg->min_bit_rate, + enc_cfg->max_bit_rate, 0, 0); + + if (rc < 0) { + pr_err("%s:session id %d: cmd qcelp media format block failed\n", + __func__, audio->ac->session); + break; + } + if (audio->feedback == NON_TUNNEL_MODE) { + rc = q6asm_media_format_block_pcm(audio->ac, + audio->pcm_cfg.sample_rate, + audio->pcm_cfg.channel_count); + + if (rc < 0) { + pr_err("%s:session id %d: media format block failed\n", + __func__, audio->ac->session); + break; + } + } + pr_debug("%s:session id %d: AUDIO_START enable[%d]\n", __func__, + audio->ac->session, audio->enabled); + rc = audio_in_enable(audio); + if (!rc) { + audio->enabled = 1; + } else { + audio->enabled = 0; + pr_err("%s:session id %d: Audio Start procedure failed rc=%d\n", + __func__, audio->ac->session, rc); + break; + } + while (cnt++ < audio->str_cfg.buffer_count) + q6asm_read(audio->ac); /* Push buffer to DSP */ + rc = 0; + pr_debug("%s:session id %d: AUDIO_START success enable[%d]\n", + __func__, audio->ac->session, audio->enabled); + break; + } + case AUDIO_STOP: { + pr_debug("%s:session id %d: AUDIO_STOP\n", __func__, + audio->ac->session); + rc = audio_in_disable(audio); + if (rc < 0) { + pr_err("%s:session id %d: Audio Stop procedure failed rc=%d\n", + __func__, audio->ac->session, + rc); + break; + } + break; + } + case AUDIO_SET_QCELP_ENC_CONFIG: { + struct msm_audio_qcelp_enc_config *cfg; + struct msm_audio_qcelp_enc_config *enc_cfg; + + enc_cfg = audio->enc_cfg; + cfg = (struct msm_audio_qcelp_enc_config *)arg; + if (cfg == NULL) { + pr_err("%s: NULL config pointer\n", __func__); + rc = -EINVAL; + break; + } + if (cfg->min_bit_rate > 4 || + cfg->min_bit_rate < 1) { + pr_err("%s:session id %d: invalid min bitrate\n", + __func__, audio->ac->session); + rc = -EINVAL; + break; + } + if (cfg->max_bit_rate > 4 || + cfg->max_bit_rate < 1) { + pr_err("%s:session id %d: invalid max bitrate\n", + __func__, audio->ac->session); + rc = -EINVAL; + break; + } + enc_cfg->cdma_rate = cfg->cdma_rate; + enc_cfg->min_bit_rate = cfg->min_bit_rate; + enc_cfg->max_bit_rate = cfg->max_bit_rate; + pr_debug("%s:session id %d: min_bit_rate= 0x%x max_bit_rate=0x%x\n", + __func__, + audio->ac->session, enc_cfg->min_bit_rate, + enc_cfg->max_bit_rate); + break; + } + default: + pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd); + rc = -EINVAL; + } + return rc; +} + +static long qcelp_in_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct q6audio_in *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_START: + case AUDIO_STOP: { + rc = qcelp_in_ioctl_shared(file, cmd, arg); + break; + } + case AUDIO_GET_QCELP_ENC_CONFIG: { + if (copy_to_user((void *)arg, audio->enc_cfg, + sizeof(struct msm_audio_qcelp_enc_config))) { + pr_err( + "%s: copy_to_user for AUDIO_GET_QCELP_ENC_CONFIG failed", + __func__); + rc = -EFAULT; + } + break; + } + case AUDIO_SET_QCELP_ENC_CONFIG: { + struct msm_audio_qcelp_enc_config cfg; + + if (copy_from_user(&cfg, (void *) arg, + sizeof(cfg))) { + pr_err( + "%s: copy_from_user for AUDIO_SET_QCELP_ENC_CONFIG failed", + __func__); + rc = -EFAULT; + break; + } + rc = qcelp_in_ioctl_shared(file, cmd, (unsigned long)&cfg); + if (rc) + pr_err("%s:AUDIO_SET_QCELP_ENC_CONFIG failed. Rc= %d\n", + __func__, rc); + break; + } + default: + pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd); + rc = -EINVAL; + } + return rc; +} + +#ifdef CONFIG_COMPAT +struct msm_audio_qcelp_enc_config32 { + u32 cdma_rate; + u32 min_bit_rate; + u32 max_bit_rate; +}; + +enum { + AUDIO_SET_QCELP_ENC_CONFIG_32 = _IOW(AUDIO_IOCTL_MAGIC, + 0, struct msm_audio_qcelp_enc_config32), + AUDIO_GET_QCELP_ENC_CONFIG_32 = _IOR(AUDIO_IOCTL_MAGIC, + 1, struct msm_audio_qcelp_enc_config32) +}; + +static long qcelp_in_compat_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct q6audio_in *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_START: + case AUDIO_STOP: { + rc = qcelp_in_ioctl_shared(file, cmd, arg); + break; + } + case AUDIO_GET_QCELP_ENC_CONFIG_32: { + struct msm_audio_qcelp_enc_config32 cfg_32; + struct msm_audio_qcelp_enc_config *enc_cfg; + + memset(&cfg_32, 0, sizeof(cfg_32)); + + enc_cfg = (struct msm_audio_qcelp_enc_config *)audio->enc_cfg; + cfg_32.cdma_rate = enc_cfg->cdma_rate; + cfg_32.min_bit_rate = enc_cfg->min_bit_rate; + cfg_32.max_bit_rate = enc_cfg->max_bit_rate; + if (copy_to_user((void *)arg, &cfg_32, + sizeof(cfg_32))) { + pr_err("%s: copy_to_user for AUDIO_GET_QCELP_ENC_CONFIG_32 failed\n", + __func__); + rc = -EFAULT; +} + break; + } + case AUDIO_SET_QCELP_ENC_CONFIG_32: { + struct msm_audio_qcelp_enc_config32 cfg_32; + struct msm_audio_qcelp_enc_config cfg; + + if (copy_from_user(&cfg_32, (void *) arg, + sizeof(cfg_32))) { + pr_err("%s: copy_from_user for AUDIO_SET_QCELP_ENC_CONFIG_32 failed\n", + __func__); + rc = -EFAULT; + break; + } + cfg.cdma_rate = cfg_32.cdma_rate; + cfg.min_bit_rate = cfg_32.min_bit_rate; + cfg.max_bit_rate = cfg_32.max_bit_rate; + cmd = AUDIO_SET_QCELP_ENC_CONFIG; + rc = qcelp_in_ioctl_shared(file, cmd, (unsigned long)&cfg); + if (rc) + pr_err("%s:AUDIO_SET_QCELP_ENC_CONFIG failed. rc= %d\n", + __func__, rc); + break; + } + default: + pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd); + rc = -EINVAL; + } + return rc; +} +#else +#define qcelp_in_compat_ioctl NULL +#endif + +static int qcelp_in_open(struct inode *inode, struct file *file) +{ + struct q6audio_in *audio = NULL; + struct msm_audio_qcelp_enc_config *enc_cfg; + int rc = 0; + + audio = kzalloc(sizeof(struct q6audio_in), GFP_KERNEL); + + if (audio == NULL) + return -ENOMEM; + + /* Allocate memory for encoder config param */ + audio->enc_cfg = kzalloc(sizeof(struct msm_audio_qcelp_enc_config), + GFP_KERNEL); + if (audio->enc_cfg == NULL) { + kfree(audio); + return -ENOMEM; + } + enc_cfg = audio->enc_cfg; + + mutex_init(&audio->lock); + mutex_init(&audio->read_lock); + mutex_init(&audio->write_lock); + spin_lock_init(&audio->dsp_lock); + init_waitqueue_head(&audio->read_wait); + init_waitqueue_head(&audio->write_wait); + + /* Settings will be re-config at AUDIO_SET_CONFIG, + * but at least we need to have initial config + */ + audio->str_cfg.buffer_size = FRAME_SIZE; + audio->str_cfg.buffer_count = FRAME_NUM; + audio->min_frame_size = 35; + audio->max_frames_per_buf = 10; + audio->pcm_cfg.buffer_size = PCM_BUF_SIZE; + audio->pcm_cfg.buffer_count = PCM_BUF_COUNT; + enc_cfg->min_bit_rate = 4; + enc_cfg->max_bit_rate = 4; + audio->pcm_cfg.channel_count = 1; + audio->pcm_cfg.sample_rate = 8000; + audio->buf_cfg.meta_info_enable = 0x01; + audio->buf_cfg.frames_per_buf = 0x01; + audio->event_abort = 0; + + audio->ac = q6asm_audio_client_alloc((app_cb)q6asm_in_cb, + (void *)audio); + + if (!audio->ac) { + pr_err("%s: Could not allocate memory for audio client\n", + __func__); + kfree(audio->enc_cfg); + kfree(audio); + return -ENOMEM; + } + + /* open qcelp encoder in T/NT mode */ + if ((file->f_mode & FMODE_WRITE) && + (file->f_mode & FMODE_READ)) { + audio->feedback = NON_TUNNEL_MODE; + rc = q6asm_open_read_write(audio->ac, FORMAT_V13K, + FORMAT_LINEAR_PCM); + if (rc < 0) { + pr_err("%s:session id %d: NT mode Open failed rc=%d\n", + __func__, audio->ac->session, rc); + rc = -ENODEV; + goto fail; + } + pr_info("%s:session id %d: NT mode encoder success\n", __func__, + audio->ac->session); + } else if (!(file->f_mode & FMODE_WRITE) && + (file->f_mode & FMODE_READ)) { + audio->feedback = TUNNEL_MODE; + rc = q6asm_open_read(audio->ac, FORMAT_V13K); + if (rc < 0) { + pr_err("%s:session id %d: T mode Open failed rc=%d\n", + __func__, audio->ac->session, rc); + rc = -ENODEV; + goto fail; + } + /* register for tx overflow (valid for tunnel mode only) */ + rc = q6asm_reg_tx_overflow(audio->ac, 0x01); + if (rc < 0) { + pr_err("%s:session id %d: TX Overflow registration failed rc=%d\n", + __func__, audio->ac->session, rc); + rc = -ENODEV; + goto fail; + } + pr_info("%s:session id %d: T mode encoder success\n", __func__, + audio->ac->session); + } else { + pr_err("%s:session id %d: Unexpected mode\n", __func__, + audio->ac->session); + rc = -EACCES; + goto fail; + } + + audio->opened = 1; + audio->reset_event = false; + atomic_set(&audio->in_count, PCM_BUF_COUNT); + atomic_set(&audio->out_count, 0x00); + audio->enc_compat_ioctl = qcelp_in_compat_ioctl; + audio->enc_ioctl = qcelp_in_ioctl; + file->private_data = audio; + + pr_info("%s:session id %d: success\n", __func__, audio->ac->session); + return 0; +fail: + q6asm_audio_client_free(audio->ac); + kfree(audio->enc_cfg); + kfree(audio); + return rc; +} + +static const struct file_operations audio_in_fops = { + .owner = THIS_MODULE, + .open = qcelp_in_open, + .release = audio_in_release, + .read = audio_in_read, + .write = audio_in_write, + .unlocked_ioctl = audio_in_ioctl, + .compat_ioctl = audio_in_compat_ioctl +}; + +struct miscdevice audio_qcelp_in_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_qcelp_in", + .fops = &audio_in_fops, +}; + +int __init qcelp_in_init(void) +{ + return misc_register(&audio_qcelp_in_misc); +} + +void qcelp_in_exit(void) +{ + misc_deregister(&audio_qcelp_in_misc); +} diff --git a/audio-kernel/dsp/msm-audio-event-notify.c b/audio-kernel/dsp/msm-audio-event-notify.c new file mode 100644 index 000000000..3f02ebb6a --- /dev/null +++ b/audio-kernel/dsp/msm-audio-event-notify.c @@ -0,0 +1,81 @@ +/* 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 +#include + +static ATOMIC_NOTIFIER_HEAD(msm_aud_evt_notifier_list); +static BLOCKING_NOTIFIER_HEAD(msm_aud_evt_blocking_notifier_list); + +/** + * msm_aud_evt_register_client - register a client notifier + * @nb: notifier block to callback on events + */ +int msm_aud_evt_register_client(struct notifier_block *nb) +{ + return atomic_notifier_chain_register(&msm_aud_evt_notifier_list, nb); +} +EXPORT_SYMBOL(msm_aud_evt_register_client); + +/** + * msm_aud_evt_unregister_client - unregister a client notifier + * @nb: notifier block to callback on events + */ +int msm_aud_evt_unregister_client(struct notifier_block *nb) +{ + return atomic_notifier_chain_unregister(&msm_aud_evt_notifier_list, nb); +} +EXPORT_SYMBOL(msm_aud_evt_unregister_client); + +/** + * msm_aud_evt_notifier_call_chain - notify clients of fb_events + * + */ +int msm_aud_evt_notifier_call_chain(unsigned long val, void *v) +{ + return atomic_notifier_call_chain(&msm_aud_evt_notifier_list, val, v); +} +EXPORT_SYMBOL_GPL(msm_aud_evt_notifier_call_chain); + +/** + * msm_aud_evt_blocking_register_client - register a client notifier + * @nb: notifier block to callback on events + */ +int msm_aud_evt_blocking_register_client(struct notifier_block *nb) +{ + return blocking_notifier_chain_register( + &msm_aud_evt_blocking_notifier_list, nb); +} +EXPORT_SYMBOL(msm_aud_evt_blocking_register_client); + +/** + * msm_aud_evt_unregister_client - unregister a client notifier + * @nb: notifier block to callback on events + */ +int msm_aud_evt_blocking_unregister_client(struct notifier_block *nb) +{ + return blocking_notifier_chain_unregister( + &msm_aud_evt_blocking_notifier_list, nb); +} +EXPORT_SYMBOL(msm_aud_evt_blocking_unregister_client); + +/** + * msm_aud_evt_notifier_call_chain - notify clients of fb_events + * @val: event or enum maintained by caller + * @v: private data pointer + * + */ +int msm_aud_evt_blocking_notifier_call_chain(unsigned long val, void *v) +{ + return blocking_notifier_call_chain( + &msm_aud_evt_blocking_notifier_list, val, v); +} +EXPORT_SYMBOL_GPL(msm_aud_evt_blocking_notifier_call_chain); diff --git a/audio-kernel/dsp/msm-dts-srs-tm-config.c b/audio-kernel/dsp/msm-dts-srs-tm-config.c new file mode 100644 index 000000000..fff98b572 --- /dev/null +++ b/audio-kernel/dsp/msm-dts-srs-tm-config.c @@ -0,0 +1,381 @@ +/* + * Copyright (c) 2012-2014, 2016-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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int srs_port_id[AFE_MAX_PORTS] = {-1}; +static int srs_copp_idx[AFE_MAX_PORTS] = {-1}; +static union srs_trumedia_params_u msm_srs_trumedia_params; +struct dma_buf *dma_buf; +static struct param_outband po; +static atomic_t ref_cnt; +#define ION_MEM_SIZE (8 * 1024) + +static int set_port_id(int port_id, int copp_idx) +{ + int index = adm_validate_and_get_port_index(port_id); + + if (index < 0) { + pr_err("%s: Invalid port idx %d port_id %#x\n", __func__, index, + port_id); + return -EINVAL; + } + srs_port_id[index] = port_id; + srs_copp_idx[index] = copp_idx; + return 0; +} + +static void msm_dts_srs_tm_send_params(__s32 port_id, __u32 techs) +{ + __s32 index = adm_validate_and_get_port_index(port_id); + + if (index < 0) { + pr_err("%s: Invalid port idx %d port_id 0x%x\n", + __func__, index, port_id); + return; + } + if ((srs_copp_idx[index] < 0) || + (srs_copp_idx[index] >= MAX_COPPS_PER_PORT)) { + pr_debug("%s: send params called before copp open. so, caching\n", + __func__); + return; + } + pr_debug("SRS %s: called, port_id = %d, techs flags = %u\n", + __func__, port_id, techs); + /* force all if techs is set to 1 */ + if (techs == 1) + techs = 0xFFFFFFFF; + + if (techs & (1 << SRS_ID_WOWHD)) + srs_trumedia_open(port_id, srs_copp_idx[index], SRS_ID_WOWHD, + (void *)&msm_srs_trumedia_params.srs_params.wowhd); + if (techs & (1 << SRS_ID_CSHP)) + srs_trumedia_open(port_id, srs_copp_idx[index], SRS_ID_CSHP, + (void *)&msm_srs_trumedia_params.srs_params.cshp); + if (techs & (1 << SRS_ID_HPF)) + srs_trumedia_open(port_id, srs_copp_idx[index], SRS_ID_HPF, + (void *)&msm_srs_trumedia_params.srs_params.hpf); + if (techs & (1 << SRS_ID_AEQ)) + srs_trumedia_open(port_id, srs_copp_idx[index], SRS_ID_AEQ, + (void *)&msm_srs_trumedia_params.srs_params.aeq); + if (techs & (1 << SRS_ID_HL)) + srs_trumedia_open(port_id, srs_copp_idx[index], SRS_ID_HL, + (void *)&msm_srs_trumedia_params.srs_params.hl); + if (techs & (1 << SRS_ID_GEQ)) + srs_trumedia_open(port_id, srs_copp_idx[index], SRS_ID_GEQ, + (void *)&msm_srs_trumedia_params.srs_params.geq); + if (techs & (1 << SRS_ID_GLOBAL)) + srs_trumedia_open(port_id, srs_copp_idx[index], SRS_ID_GLOBAL, + (void *)&msm_srs_trumedia_params.srs_params.global); +} + + +static int msm_dts_srs_trumedia_control_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = 0; + return 0; +} + +static int msm_dts_srs_trumedia_control_set_(int port_id, + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + + __u16 offset, value, max = sizeof(msm_srs_trumedia_params) >> 1; + + if (SRS_CMD_UPLOAD == + (ucontrol->value.integer.value[0] & SRS_CMD_UPLOAD)) { + __u32 techs = ucontrol->value.integer.value[0] & 0xFF; + __s32 index = adm_validate_and_get_port_index(port_id); + + if (index < 0) { + pr_err("%s: Invalid port idx %d port_id 0x%x\n", + __func__, index, port_id); + return -EINVAL; + } + pr_debug("SRS %s: send params request, flag = %u\n", + __func__, techs); + if (srs_port_id[index] >= 0 && techs) + msm_dts_srs_tm_send_params(port_id, techs); + return 0; + } + offset = (__u16)((ucontrol->value.integer.value[0] & + SRS_PARAM_OFFSET_MASK) >> 16); + value = (__u16)(ucontrol->value.integer.value[0] & + SRS_PARAM_VALUE_MASK); + if (offset < max) { + msm_srs_trumedia_params.raw_params[offset] = value; + pr_debug("SRS %s: index set... (max %d, requested %d, value 0x%X)\n", + __func__, max, offset, value); + } else { + pr_err("SRS %s: index out of bounds! (max %d, requested %d)\n", + __func__, max, offset); + } + return 0; +} + +static int msm_dts_srs_trumedia_control_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret, port_id; + + pr_debug("SRS control normal called\n"); + msm_dts_srs_acquire_lock(); + port_id = SLIMBUS_0_RX; + ret = msm_dts_srs_trumedia_control_set_(port_id, kcontrol, ucontrol); + msm_dts_srs_release_lock(); + return ret; +} + +static int msm_dts_srs_trumedia_control_i2s_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret, port_id; + + pr_debug("SRS control I2S called\n"); + msm_dts_srs_acquire_lock(); + port_id = PRIMARY_I2S_RX; + ret = msm_dts_srs_trumedia_control_set_(port_id, kcontrol, ucontrol); + msm_dts_srs_release_lock(); + return ret; +} + +static int msm_dts_srs_trumedia_control_mi2s_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret, port_id; + + pr_debug("SRS control MI2S called\n"); + msm_dts_srs_acquire_lock(); + port_id = AFE_PORT_ID_PRIMARY_MI2S_RX; + ret = msm_dts_srs_trumedia_control_set_(port_id, kcontrol, ucontrol); + msm_dts_srs_release_lock(); + return ret; +} + +static int msm_dts_srs_trumedia_control_hdmi_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret, port_id; + + pr_debug("SRS control HDMI called\n"); + msm_dts_srs_acquire_lock(); + port_id = HDMI_RX; + ret = msm_dts_srs_trumedia_control_set_(port_id, kcontrol, ucontrol); + msm_dts_srs_release_lock(); + return ret; +} + +static const struct snd_kcontrol_new lpa_srs_trumedia_controls[] = { + {.iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "SRS TruMedia", + .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | + SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = snd_soc_info_volsw, + .get = msm_dts_srs_trumedia_control_get, + .put = msm_dts_srs_trumedia_control_set, + .private_value = ((unsigned long)&(struct soc_mixer_control) + {.reg = SND_SOC_NOPM, + .rreg = SND_SOC_NOPM, + .shift = 0, + .rshift = 0, + .max = 0xFFFFFFFF, + .platform_max = 0xFFFFFFFF, + .invert = 0 + }) + } +}; + +static const struct snd_kcontrol_new lpa_srs_trumedia_controls_hdmi[] = { + {.iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "SRS TruMedia HDMI", + .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | + SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = snd_soc_info_volsw, + .get = msm_dts_srs_trumedia_control_get, + .put = msm_dts_srs_trumedia_control_hdmi_set, + .private_value = ((unsigned long)&(struct soc_mixer_control) + {.reg = SND_SOC_NOPM, + .rreg = SND_SOC_NOPM, + .shift = 0, + .rshift = 0, + .max = 0xFFFFFFFF, + .platform_max = 0xFFFFFFFF, + .invert = 0 + }) + } +}; + +static const struct snd_kcontrol_new lpa_srs_trumedia_controls_i2s[] = { + {.iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "SRS TruMedia I2S", + .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | + SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = snd_soc_info_volsw, + .get = msm_dts_srs_trumedia_control_get, + .put = msm_dts_srs_trumedia_control_i2s_set, + .private_value = ((unsigned long)&(struct soc_mixer_control) + {.reg = SND_SOC_NOPM, + .rreg = SND_SOC_NOPM, + .shift = 0, + .rshift = 0, + .max = 0xFFFFFFFF, + .platform_max = 0xFFFFFFFF, + .invert = 0 + }) + } +}; + +static const struct snd_kcontrol_new lpa_srs_trumedia_controls_mi2s[] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "SRS TruMedia MI2S", + .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | + SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = snd_soc_info_volsw, + .get = msm_dts_srs_trumedia_control_get, + .put = msm_dts_srs_trumedia_control_mi2s_set, + .private_value = ((unsigned long)&(struct soc_mixer_control) + { + .reg = SND_SOC_NOPM, + .rreg = SND_SOC_NOPM, + .shift = 0, + .rshift = 0, + .max = 0xFFFFFFFF, + .platform_max = 0xFFFFFFFF, + .invert = 0 + }) + } +}; + +/** + * msm_dts_srs_tm_add_controls - + * Add DTS SRS module controls + * + * @platform: component to which controls can be registered + * + */ +void msm_dts_srs_tm_add_controls(struct snd_soc_platform *platform) +{ + snd_soc_add_platform_controls(platform, + lpa_srs_trumedia_controls, + ARRAY_SIZE(lpa_srs_trumedia_controls)); + + snd_soc_add_platform_controls(platform, + lpa_srs_trumedia_controls_hdmi, + ARRAY_SIZE(lpa_srs_trumedia_controls_hdmi)); + + snd_soc_add_platform_controls(platform, + lpa_srs_trumedia_controls_i2s, + ARRAY_SIZE(lpa_srs_trumedia_controls_i2s)); + snd_soc_add_platform_controls(platform, + lpa_srs_trumedia_controls_mi2s, + ARRAY_SIZE(lpa_srs_trumedia_controls_mi2s)); +} +EXPORT_SYMBOL(msm_dts_srs_tm_add_controls); + +static int reg_ion_mem(void) +{ + int rc; + + rc = msm_audio_ion_alloc(&dma_buf, ION_MEM_SIZE, + &po.paddr, (size_t *)&po.size, + &po.kvaddr); + if (rc != 0) + pr_err("%s: failed to allocate memory.\n", __func__); + pr_debug("%s: exited dma_buf = %pK, phys_addr = %lu, length = %d, vaddr = %pK, rc = 0x%x\n", + __func__, dma_buf, (long)po.paddr, + (unsigned int)po.size, po.kvaddr, rc); + return rc; +} + +void msm_dts_srs_tm_ion_memmap(struct param_outband *po_) +{ + if (po.kvaddr == NULL) { + pr_debug("%s: callingreg_ion_mem()\n", __func__); + reg_ion_mem(); + } + po_->size = ION_MEM_SIZE; + po_->kvaddr = po.kvaddr; + po_->paddr = po.paddr; +} + +static void unreg_ion_mem(void) +{ + msm_audio_ion_free(dma_buf); + dma_buf = NULL; + po.kvaddr = NULL; + po.paddr = 0; + po.size = 0; +} + +/** + * msm_dts_srs_tm_deinit - + * De-Initializes DTS SRS module + * + * @port_id: Port ID number + * + */ +void msm_dts_srs_tm_deinit(int port_id) +{ + set_port_id(port_id, -1); + atomic_dec(&ref_cnt); + if (po.kvaddr != NULL) { + if (!atomic_read(&ref_cnt)) { + pr_debug("%s: calling unreg_ion_mem()\n", __func__); + unreg_ion_mem(); + } + } +} +EXPORT_SYMBOL(msm_dts_srs_tm_deinit); + +/** + * msm_dts_srs_tm_init - + * Initializes DTS SRS module + * + * @port_id: Port ID number + * @copp_idx: COPP index + * + */ +void msm_dts_srs_tm_init(int port_id, int copp_idx) +{ + int cur_ref_cnt = 0; + + if (set_port_id(port_id, copp_idx) < 0) { + pr_err("%s: Invalid port_id: %d\n", __func__, port_id); + return; + } + + cur_ref_cnt = atomic_read(&ref_cnt); + atomic_inc(&ref_cnt); + if (!cur_ref_cnt && po.kvaddr == NULL) { + pr_debug("%s: calling reg_ion_mem()\n", __func__); + if (reg_ion_mem() != 0) { + atomic_dec(&ref_cnt); + po.kvaddr = NULL; + return; + } + } + msm_dts_srs_tm_send_params(port_id, 1); +} +EXPORT_SYMBOL(msm_dts_srs_tm_init); diff --git a/audio-kernel/dsp/msm_audio_ion.c b/audio-kernel/dsp/msm_audio_ion.c new file mode 100644 index 000000000..cb9753de0 --- /dev/null +++ b/audio-kernel/dsp/msm_audio_ion.c @@ -0,0 +1,881 @@ +/* + * Copyright (c) 2013-2019, 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MSM_AUDIO_ION_PROBED (1 << 0) + +#define MSM_AUDIO_ION_PHYS_ADDR(alloc_data) \ + alloc_data->table->sgl->dma_address + +#define MSM_AUDIO_ION_VA_START 0x10000000 +#define MSM_AUDIO_ION_VA_LEN 0x0FFFFFFF + +#define MSM_AUDIO_SMMU_SID_OFFSET 32 + +struct msm_audio_ion_private { + bool smmu_enabled; + struct device *cb_dev; + struct dma_iommu_mapping *mapping; + u8 device_status; + struct list_head alloc_list; + struct mutex list_mutex; + u64 smmu_sid_bits; + u32 smmu_version; + u32 iova_start_addr; +}; + +struct msm_audio_alloc_data { + size_t len; + void *vaddr; + struct dma_buf *dma_buf; + struct dma_buf_attachment *attach; + struct sg_table *table; + struct list_head list; +}; + +static struct msm_audio_ion_private msm_audio_ion_data = {0,}; + +static void msm_audio_ion_add_allocation( + struct msm_audio_ion_private *msm_audio_ion_data, + struct msm_audio_alloc_data *alloc_data) +{ + /* + * Since these APIs can be invoked by multiple + * clients, there is need to make sure the list + * of allocations is always protected + */ + mutex_lock(&(msm_audio_ion_data->list_mutex)); + list_add_tail(&(alloc_data->list), + &(msm_audio_ion_data->alloc_list)); + mutex_unlock(&(msm_audio_ion_data->list_mutex)); +} + +static int msm_audio_dma_buf_map(struct dma_buf *dma_buf, + dma_addr_t *addr, size_t *len) +{ + + struct msm_audio_alloc_data *alloc_data; + struct device *cb_dev; + unsigned long ionflag = 0; + int rc = 0; + + cb_dev = msm_audio_ion_data.cb_dev; + + /* Data required per buffer mapping */ + alloc_data = kzalloc(sizeof(*alloc_data), GFP_KERNEL); + if (!alloc_data) + return -ENOMEM; + + alloc_data->dma_buf = dma_buf; + alloc_data->len = dma_buf->size; + *len = dma_buf->size; + + /* Attach the dma_buf to context bank device */ + alloc_data->attach = dma_buf_attach(alloc_data->dma_buf, + cb_dev); + if (IS_ERR(alloc_data->attach)) { + rc = PTR_ERR(alloc_data->attach); + dev_err(cb_dev, + "%s: Fail to attach dma_buf to CB, rc = %d\n", + __func__, rc); + goto free_alloc_data; + } + + /* For uncached buffers, avoid cache maintanance */ + rc = dma_buf_get_flags(alloc_data->dma_buf, &ionflag); + if (rc) { + dev_err(cb_dev, "%s: dma_buf_get_flags failed: %d\n", + __func__, rc); + goto detach_dma_buf; + } + + if (!(ionflag & ION_FLAG_CACHED)) + alloc_data->attach->dma_map_attrs |= DMA_ATTR_SKIP_CPU_SYNC; + + /* + * Get the scatter-gather list. + * There is no info as this is a write buffer or + * read buffer, hence the request is bi-directional + * to accommodate both read and write mappings. + */ + alloc_data->table = dma_buf_map_attachment(alloc_data->attach, + DMA_BIDIRECTIONAL); + if (IS_ERR(alloc_data->table)) { + rc = PTR_ERR(alloc_data->table); + dev_err(cb_dev, + "%s: Fail to map attachment, rc = %d\n", + __func__, rc); + goto detach_dma_buf; + } + + /* physical address from mapping */ + *addr = MSM_AUDIO_ION_PHYS_ADDR(alloc_data); + + msm_audio_ion_add_allocation(&msm_audio_ion_data, + alloc_data); + return rc; + +detach_dma_buf: + dma_buf_detach(alloc_data->dma_buf, + alloc_data->attach); +free_alloc_data: + kfree(alloc_data); + + return rc; +} + +static int msm_audio_dma_buf_unmap(struct dma_buf *dma_buf) +{ + int rc = 0; + struct msm_audio_alloc_data *alloc_data = NULL; + struct list_head *ptr, *next; + struct device *cb_dev = msm_audio_ion_data.cb_dev; + bool found = false; + + /* + * Though list_for_each_safe is delete safe, lock + * should be explicitly acquired to avoid race condition + * on adding elements to the list. + */ + mutex_lock(&(msm_audio_ion_data.list_mutex)); + list_for_each_safe(ptr, next, + &(msm_audio_ion_data.alloc_list)) { + + alloc_data = list_entry(ptr, struct msm_audio_alloc_data, + list); + + if (alloc_data->dma_buf == dma_buf) { + found = true; + dma_buf_unmap_attachment(alloc_data->attach, + alloc_data->table, + DMA_BIDIRECTIONAL); + + dma_buf_detach(alloc_data->dma_buf, + alloc_data->attach); + + dma_buf_put(alloc_data->dma_buf); + + list_del(&(alloc_data->list)); + kfree(alloc_data); + break; + } + } + mutex_unlock(&(msm_audio_ion_data.list_mutex)); + + if (!found) { + dev_err(cb_dev, + "%s: cannot find allocation, dma_buf %pK", + __func__, dma_buf); + rc = -EINVAL; + } + + return rc; +} + +static int msm_audio_ion_get_phys(struct dma_buf *dma_buf, + dma_addr_t *addr, size_t *len) +{ + int rc = 0; + + rc = msm_audio_dma_buf_map(dma_buf, addr, len); + if (rc) { + pr_err("%s: failed to map DMA buf, err = %d\n", + __func__, rc); + goto err; + } + if (msm_audio_ion_data.smmu_enabled) { + /* Append the SMMU SID information to the IOVA address */ + *addr |= msm_audio_ion_data.smmu_sid_bits; + } + + pr_debug("phys=%pK, len=%zd, rc=%d\n", &(*addr), *len, rc); +err: + return rc; +} + +int msm_audio_ion_get_smmu_info(struct device **cb_dev, + u64 *smmu_sid) +{ + if (!cb_dev || !smmu_sid) { + pr_err("%s: Invalid params\n", + __func__); + return -EINVAL; + } + + if (!msm_audio_ion_data.cb_dev || + !msm_audio_ion_data.smmu_sid_bits) { + pr_err("%s: Params not initialized\n", + __func__); + return -EINVAL; + } + + *cb_dev = msm_audio_ion_data.cb_dev; + *smmu_sid = msm_audio_ion_data.smmu_sid_bits; + + return 0; +} + +static void *msm_audio_ion_map_kernel(struct dma_buf *dma_buf) +{ + int rc = 0; + void *addr = NULL; + struct msm_audio_alloc_data *alloc_data = NULL; + + rc = dma_buf_begin_cpu_access(dma_buf, DMA_BIDIRECTIONAL); + if (rc) { + pr_err("%s: kmap dma_buf_begin_cpu_access fail\n", __func__); + goto exit; + } + + addr = dma_buf_vmap(dma_buf); + if (!addr) { + pr_err("%s: kernel mapping of dma_buf failed\n", + __func__); + goto exit; + } + + /* + * TBD: remove the below section once new API + * for mapping kernel virtual address is available. + */ + mutex_lock(&(msm_audio_ion_data.list_mutex)); + list_for_each_entry(alloc_data, &(msm_audio_ion_data.alloc_list), + list) { + if (alloc_data->dma_buf == dma_buf) { + alloc_data->vaddr = addr; + break; + } + } + mutex_unlock(&(msm_audio_ion_data.list_mutex)); + +exit: + return addr; +} + +static int msm_audio_ion_unmap_kernel(struct dma_buf *dma_buf) +{ + int rc = 0; + void *vaddr = NULL; + struct msm_audio_alloc_data *alloc_data = NULL; + struct device *cb_dev = msm_audio_ion_data.cb_dev; + + /* + * TBD: remove the below section once new API + * for unmapping kernel virtual address is available. + */ + mutex_lock(&(msm_audio_ion_data.list_mutex)); + list_for_each_entry(alloc_data, &(msm_audio_ion_data.alloc_list), + list) { + if (alloc_data->dma_buf == dma_buf) { + vaddr = alloc_data->vaddr; + break; + } + } + mutex_unlock(&(msm_audio_ion_data.list_mutex)); + + if (!vaddr) { + dev_err(cb_dev, + "%s: cannot find allocation for dma_buf %pK", + __func__, dma_buf); + rc = -EINVAL; + goto err; + } + + dma_buf_vunmap(dma_buf, vaddr); + + rc = dma_buf_end_cpu_access(dma_buf, DMA_BIDIRECTIONAL); + if (rc) { + dev_err(cb_dev, "%s: kmap dma_buf_end_cpu_access fail\n", + __func__); + goto err; + } + +err: + return rc; +} + +static int msm_audio_ion_map_buf(struct dma_buf *dma_buf, dma_addr_t *paddr, + size_t *plen, void **vaddr) +{ + int rc = 0; + + rc = msm_audio_ion_get_phys(dma_buf, paddr, plen); + if (rc) { + pr_err("%s: ION Get Physical for AUDIO failed, rc = %d\n", + __func__, rc); + goto err; + } + + *vaddr = msm_audio_ion_map_kernel(dma_buf); + if (IS_ERR_OR_NULL(*vaddr)) { + pr_err("%s: ION memory mapping for AUDIO failed\n", __func__); + rc = -ENOMEM; + goto err; + } + +err: + return rc; +} + +static u32 msm_audio_ion_get_smmu_sid_mode32(void) +{ + if (msm_audio_ion_data.smmu_enabled) + return upper_32_bits(msm_audio_ion_data.smmu_sid_bits); + else + return 0; +} + +/** + * msm_audio_ion_alloc - + * Allocs ION memory for given client name + * + * @dma_buf: dma_buf for the ION memory + * @bufsz: buffer size + * @paddr: Physical address to be assigned with allocated region + * @plen: length of allocated region to be assigned + * vaddr: virtual address to be assigned + * + * Returns 0 on success or error on failure + */ +int msm_audio_ion_alloc(struct dma_buf **dma_buf, size_t bufsz, + dma_addr_t *paddr, size_t *plen, void **vaddr) +{ + int rc = -EINVAL; + unsigned long err_ion_ptr = 0; + + if (!(msm_audio_ion_data.device_status & MSM_AUDIO_ION_PROBED)) { + pr_debug("%s:probe is not done, deferred\n", __func__); + return -EPROBE_DEFER; + } + if (!dma_buf || !paddr || !vaddr || !bufsz || !plen) { + pr_err("%s: Invalid params\n", __func__); + return -EINVAL; + } + + if (msm_audio_ion_data.smmu_enabled == true) { + pr_debug("%s: system heap is used\n", __func__); + *dma_buf = ion_alloc(bufsz, ION_HEAP(ION_SYSTEM_HEAP_ID), 0); + } else { + pr_debug("%s: audio heap is used\n", __func__); + *dma_buf = ion_alloc(bufsz, ION_HEAP(ION_AUDIO_HEAP_ID), 0); + } + if (IS_ERR_OR_NULL((void *)(*dma_buf))) { + if (IS_ERR((void *)(*dma_buf))) + err_ion_ptr = PTR_ERR((int *)(*dma_buf)); + pr_err("%s: ION alloc fail err ptr=%ld, smmu_enabled=%d\n", + __func__, err_ion_ptr, msm_audio_ion_data.smmu_enabled); + rc = -ENOMEM; + goto err; + } + + rc = msm_audio_ion_map_buf(*dma_buf, paddr, plen, vaddr); + if (rc) { + pr_err("%s: failed to map ION buf, rc = %d\n", __func__, rc); + goto err_dma_buf; + } + pr_debug("%s: mapped address = %pK, size=%zd\n", __func__, + *vaddr, bufsz); + + memset(*vaddr, 0, bufsz); + + return rc; + +err_dma_buf: + dma_buf_put(*dma_buf); +err: + return rc; +} +EXPORT_SYMBOL(msm_audio_ion_alloc); + +/** + * msm_audio_ion_dma_map - + * Memory maps for a given DMA buffer + * + * @phys_addr: Physical address of DMA buffer to be mapped + * @iova_base: IOVA address of memory mapped DMA buffer + * @size: buffer size + * @dir: DMA direction + * Returns 0 on success or error on failure + */ +int msm_audio_ion_dma_map(dma_addr_t *phys_addr, dma_addr_t *iova_base, + u32 size, enum dma_data_direction dir) +{ + dma_addr_t iova; + struct device *cb_dev = msm_audio_ion_data.cb_dev; + + if (!phys_addr || !iova_base || !size) + return -EINVAL; + + iova = dma_map_resource(cb_dev, *phys_addr, size, + dir, 0); + if (dma_mapping_error(cb_dev, iova)) { + pr_err("%s: dma_mapping_error\n", __func__); + return -EIO; + } + pr_debug("%s: dma_mapping_success iova:0x%lx\n", __func__, + (unsigned long)iova); + if (msm_audio_ion_data.smmu_enabled) + /* Append the SMMU SID information to the IOVA address */ + iova |= msm_audio_ion_data.smmu_sid_bits; + + *iova_base = iova; + + return 0; +} +EXPORT_SYMBOL(msm_audio_ion_dma_map); + +/** + * msm_audio_ion_import- + * Import ION buffer with given file descriptor + * + * @dma_buf: dma_buf for the ION memory + * @fd: file descriptor for the ION memory + * @ionflag: flags associated with ION buffer + * @bufsz: buffer size + * @paddr: Physical address to be assigned with allocated region + * @plen: length of allocated region to be assigned + * vaddr: virtual address to be assigned + * + * Returns 0 on success or error on failure + */ +int msm_audio_ion_import(struct dma_buf **dma_buf, int fd, + unsigned long *ionflag, size_t bufsz, + dma_addr_t *paddr, size_t *plen, void **vaddr) +{ + int rc = 0; + + if (!(msm_audio_ion_data.device_status & MSM_AUDIO_ION_PROBED)) { + pr_debug("%s: probe is not done, deferred\n", __func__); + return -EPROBE_DEFER; + } + + if (!dma_buf || !paddr || !vaddr || !plen) { + pr_err("%s: Invalid params\n", __func__); + return -EINVAL; + } + + /* bufsz should be 0 and fd shouldn't be 0 as of now */ + *dma_buf = dma_buf_get(fd); + pr_debug("%s: dma_buf =%pK, fd=%d\n", __func__, *dma_buf, fd); + if (IS_ERR_OR_NULL((void *)(*dma_buf))) { + pr_err("%s: dma_buf_get failed\n", __func__); + rc = -EINVAL; + goto err; + } + + if (ionflag != NULL) { + rc = dma_buf_get_flags(*dma_buf, ionflag); + if (rc) { + pr_err("%s: could not get flags for the dma_buf\n", + __func__); + goto err_ion_flag; + } + } + + rc = msm_audio_ion_map_buf(*dma_buf, paddr, plen, vaddr); + if (rc) { + pr_err("%s: failed to map ION buf, rc = %d\n", __func__, rc); + goto err_ion_flag; + } + pr_debug("%s: mapped address = %pK, size=%zd\n", __func__, + *vaddr, bufsz); + + return 0; + +err_ion_flag: + dma_buf_put(*dma_buf); +err: + *dma_buf = NULL; + return rc; +} +EXPORT_SYMBOL(msm_audio_ion_import); + +/** + * msm_audio_ion_free - + * fress ION memory for given client and handle + * + * @dma_buf: dma_buf for the ION memory + * + * Returns 0 on success or error on failure + */ +int msm_audio_ion_free(struct dma_buf *dma_buf) +{ + int ret = 0; + + if (!dma_buf) { + pr_err("%s: dma_buf invalid\n", __func__); + return -EINVAL; + } + + ret = msm_audio_ion_unmap_kernel(dma_buf); + if (ret) + return ret; + + msm_audio_dma_buf_unmap(dma_buf); + + return 0; +} +EXPORT_SYMBOL(msm_audio_ion_free); + +/** + * msm_audio_ion_mmap - + * Audio ION memory map + * + * @abuff: audio buf pointer + * @vma: virtual mem area + * + * Returns 0 on success or error on failure + */ +int msm_audio_ion_mmap(struct audio_buffer *abuff, + struct vm_area_struct *vma) +{ + struct msm_audio_alloc_data *alloc_data = NULL; + struct sg_table *table; + unsigned long addr = vma->vm_start; + unsigned long offset = vma->vm_pgoff * PAGE_SIZE; + struct scatterlist *sg; + unsigned int i; + struct page *page; + int ret = 0; + bool found = false; + struct device *cb_dev = msm_audio_ion_data.cb_dev; + + mutex_lock(&(msm_audio_ion_data.list_mutex)); + list_for_each_entry(alloc_data, &(msm_audio_ion_data.alloc_list), + list) { + if (alloc_data->dma_buf == abuff->dma_buf) { + found = true; + table = alloc_data->table; + break; + } + } + mutex_unlock(&(msm_audio_ion_data.list_mutex)); + + if (!found) { + dev_err(cb_dev, + "%s: cannot find allocation, dma_buf %pK", + __func__, abuff->dma_buf); + return -EINVAL; + } + /* uncached */ + vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); + + /* We need to check if a page is associated with this sg list because: + * If the allocation came from a carveout we currently don't have + * pages associated with carved out memory. This might change in the + * future and we can remove this check and the else statement. + */ + page = sg_page(table->sgl); + if (page) { + pr_debug("%s: page is NOT null\n", __func__); + for_each_sg(table->sgl, sg, table->nents, i) { + unsigned long remainder = vma->vm_end - addr; + unsigned long len = sg->length; + + page = sg_page(sg); + + if (offset >= len) { + offset -= len; + continue; + } else if (offset) { + page += offset / PAGE_SIZE; + len -= offset; + offset = 0; + } + len = min(len, remainder); + pr_debug("vma=%pK, addr=%x len=%ld vm_start=%x vm_end=%x vm_page_prot=%lu\n", + vma, (unsigned int)addr, len, + (unsigned int)vma->vm_start, + (unsigned int)vma->vm_end, + (unsigned long)pgprot_val(vma->vm_page_prot)); + remap_pfn_range(vma, addr, page_to_pfn(page), len, + vma->vm_page_prot); + addr += len; + if (addr >= vma->vm_end) + return 0; + } + } else { + pr_debug("%s: page is NULL\n", __func__); + ret = -EINVAL; + } + + return ret; +} +EXPORT_SYMBOL(msm_audio_ion_mmap); + +/** + * msm_audio_ion_cache_operations- + * Cache operations on cached Audio ION buffers + * + * @abuff: audio buf pointer + * @cache_op: cache operation to be performed + * + * Returns 0 on success or error on failure + */ +int msm_audio_ion_cache_operations(struct audio_buffer *abuff, int cache_op) +{ + unsigned long ionflag = 0; + int rc = 0; + + if (!abuff) { + pr_err("%s: Invalid params: %pK\n", __func__, abuff); + return -EINVAL; + } + rc = dma_buf_get_flags(abuff->dma_buf, &ionflag); + if (rc) { + pr_err("%s: dma_buf_get_flags failed: %d\n", __func__, rc); + goto cache_op_failed; + } + + /* Has to be CACHED */ + if (ionflag & ION_FLAG_CACHED) { + /* MSM_AUDIO_ION_INV_CACHES or MSM_AUDIO_ION_CLEAN_CACHES */ + switch (cache_op) { + case MSM_AUDIO_ION_INV_CACHES: + case MSM_AUDIO_ION_CLEAN_CACHES: + dma_buf_begin_cpu_access(abuff->dma_buf, + DMA_BIDIRECTIONAL); + dma_buf_end_cpu_access(abuff->dma_buf, + DMA_BIDIRECTIONAL); + break; + default: + pr_err("%s: Invalid cache operation %d\n", + __func__, cache_op); + } + } else { + pr_err("%s: Cache ops called on uncached buffer: %pK\n", + __func__, abuff->dma_buf); + rc = -EINVAL; + } + +cache_op_failed: + return rc; +} +EXPORT_SYMBOL(msm_audio_ion_cache_operations); + +/** + * msm_audio_populate_upper_32_bits - + * retrieve upper 32bits of 64bit address + * + * @pa: 64bit physical address + * + */ +u32 msm_audio_populate_upper_32_bits(dma_addr_t pa) +{ + if (sizeof(dma_addr_t) == sizeof(u32)) + return msm_audio_ion_get_smmu_sid_mode32(); + else + return upper_32_bits(pa); +} +EXPORT_SYMBOL(msm_audio_populate_upper_32_bits); + +static int msm_audio_smmu_init(struct device *dev) +{ + struct dma_iommu_mapping *mapping; + int ret; + + mapping = arm_iommu_create_mapping(&platform_bus_type, + msm_audio_ion_data.iova_start_addr, + MSM_AUDIO_ION_VA_LEN); + if (IS_ERR(mapping)) + return PTR_ERR(mapping); + + ret = arm_iommu_attach_device(dev, mapping); + if (ret) { + dev_err(dev, "%s: Attach failed, err = %d\n", + __func__, ret); + goto fail_attach; + } + + msm_audio_ion_data.mapping = mapping; + INIT_LIST_HEAD(&msm_audio_ion_data.alloc_list); + mutex_init(&(msm_audio_ion_data.list_mutex)); + + return 0; + +fail_attach: + arm_iommu_release_mapping(mapping); + return ret; +} + +static const struct of_device_id msm_audio_ion_dt_match[] = { + { .compatible = "qcom,msm-audio-ion" }, + { } +}; +MODULE_DEVICE_TABLE(of, msm_audio_ion_dt_match); + +static int msm_audio_ion_probe(struct platform_device *pdev) +{ + int rc = 0; + u64 smmu_sid = 0; + u64 smmu_sid_mask = 0; + const char *msm_audio_ion_dt = "qcom,smmu-enabled"; + const char *msm_audio_ion_smmu = "qcom,smmu-version"; + const char *msm_audio_ion_iova_start_addr = "qcom,iova-start-addr"; + const char *msm_audio_ion_smmu_sid_mask = "qcom,smmu-sid-mask"; + bool smmu_enabled; + enum apr_subsys_state q6_state; + struct device *dev = &pdev->dev; + struct of_phandle_args iommuspec; + + + if (dev->of_node == NULL) { + dev_err(dev, + "%s: device tree is not found\n", + __func__); + msm_audio_ion_data.smmu_enabled = 0; + return 0; + } + + smmu_enabled = of_property_read_bool(dev->of_node, + msm_audio_ion_dt); + msm_audio_ion_data.smmu_enabled = smmu_enabled; + + if (!smmu_enabled) { + dev_dbg(dev, "%s: SMMU is Disabled\n", __func__); + goto exit; + } + + q6_state = apr_get_q6_state(); + if (q6_state == APR_SUBSYS_DOWN) { + dev_dbg(dev, + "defering %s, adsp_state %d\n", + __func__, q6_state); + return -EPROBE_DEFER; + } + dev_dbg(dev, "%s: adsp is ready\n", __func__); + + rc = of_property_read_u32(dev->of_node, + msm_audio_ion_smmu, + &msm_audio_ion_data.smmu_version); + if (rc) { + dev_err(dev, + "%s: qcom,smmu_version missing in DT node\n", + __func__); + return rc; + } + dev_dbg(dev, "%s: SMMU is Enabled. SMMU version is (%d)", + __func__, msm_audio_ion_data.smmu_version); + + rc = of_property_read_u32(dev->of_node, + msm_audio_ion_iova_start_addr, + &msm_audio_ion_data.iova_start_addr); + if (rc) { + dev_dbg(dev, + "%s: qcom,iova_start_addr missing in DT node, initialize with default val\n", + __func__); + msm_audio_ion_data.iova_start_addr = MSM_AUDIO_ION_VA_START; + } else { + dev_dbg(dev, "%s:IOVA start addr: 0x%x\n", + __func__, msm_audio_ion_data.iova_start_addr); + } + /* Get SMMU SID information from Devicetree */ + rc = of_property_read_u64(dev->of_node, + msm_audio_ion_smmu_sid_mask, + &smmu_sid_mask); + if (rc) { + dev_err(dev, + "%s: qcom,smmu-sid-mask missing in DT node, using default\n", + __func__); + smmu_sid_mask = 0xFFFFFFFFFFFFFFFF; + } + + rc = of_parse_phandle_with_args(dev->of_node, "iommus", + "#iommu-cells", 0, &iommuspec); + if (rc) + dev_err(dev, "%s: could not get smmu SID, ret = %d\n", + __func__, rc); + else + smmu_sid = (iommuspec.args[0] & smmu_sid_mask); + + msm_audio_ion_data.smmu_sid_bits = + smmu_sid << MSM_AUDIO_SMMU_SID_OFFSET; + + if (msm_audio_ion_data.smmu_version == 0x2) { + rc = msm_audio_smmu_init(dev); + } else { + dev_err(dev, "%s: smmu version invalid %d\n", + __func__, msm_audio_ion_data.smmu_version); + rc = -EINVAL; + } + if (rc) + dev_err(dev, "%s: smmu init failed, err = %d\n", + __func__, rc); + +exit: + if (!rc) + msm_audio_ion_data.device_status |= MSM_AUDIO_ION_PROBED; + + msm_audio_ion_data.cb_dev = dev; + + return rc; +} + +static int msm_audio_ion_remove(struct platform_device *pdev) +{ + struct dma_iommu_mapping *mapping; + struct device *audio_cb_dev; + + mapping = msm_audio_ion_data.mapping; + audio_cb_dev = msm_audio_ion_data.cb_dev; + + if (audio_cb_dev && mapping) { + arm_iommu_detach_device(audio_cb_dev); + arm_iommu_release_mapping(mapping); + } + + msm_audio_ion_data.smmu_enabled = 0; + msm_audio_ion_data.device_status = 0; + return 0; +} + +static struct platform_driver msm_audio_ion_driver = { + .driver = { + .name = "msm-audio-ion", + .owner = THIS_MODULE, + .of_match_table = msm_audio_ion_dt_match, + }, + .probe = msm_audio_ion_probe, + .remove = msm_audio_ion_remove, +}; + +int __init msm_audio_ion_init(void) +{ + return platform_driver_register(&msm_audio_ion_driver); +} + +void msm_audio_ion_exit(void) +{ + platform_driver_unregister(&msm_audio_ion_driver); +} + +MODULE_DESCRIPTION("MSM Audio ION module"); +MODULE_LICENSE("GPL v2"); diff --git a/audio-kernel/dsp/msm_mdf.c b/audio-kernel/dsp/msm_mdf.c new file mode 100644 index 000000000..9c73e55f1 --- /dev/null +++ b/audio-kernel/dsp/msm_mdf.c @@ -0,0 +1,728 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define VMID_SSC_Q6 5 +#define VMID_LPASS 6 +#define VMID_MSS_MSA 15 +#define VMID_CDSP 30 + +#define MSM_MDF_PROBED (1 << 0) +#define MSM_MDF_INITIALIZED (1 << 1) +#define MSM_MDF_MEM_ALLOCATED (1 << 2) +#define MSM_MDF_MEM_MAPPED (1 << 3) +#define MSM_MDF_MEM_PERMISSION (1 << 4) /* 0 - HLOS, 1 - Subsys */ + +/* TODO: Update IOVA range for subsys SMMUs */ +#define MSM_MDF_IOVA_START 0x80000000 +#define MSM_MDF_IOVA_LEN 0x800000 + +#define MSM_MDF_SMMU_SID_OFFSET 32 + +#define ADSP_STATE_READY_TIMEOUT_MS 3000 + +/* mem protection defines */ +#define TZ_MPU_LOCK_NS_REGION 0x00000025 +#define MEM_PROTECT_AC_PERM_READ 0x4 +#define MEM_PROTECT_AC_PERM_WRITE 0x2 + +#define MSM_AUDIO_SMMU_SID_OFFSET 32 + +enum { + SUBSYS_ADSP, /* Audio DSP must have index 0 */ + SUBSYS_SCC, /* Sensor DSP */ + SUBSYS_MSS, /* Modem DSP */ + SUBSYS_CDSP, /* Compute DSP */ + SUBSYS_MAX, +}; + +struct msm_mdf_dest_vm_and_perm_info { + uint32_t dst_vm; + /* Destination VM defined by ACVirtualMachineId. */ + uint32_t dst_vm_perm; + /* Permissions of the IPA to be mapped to VM, bitwise OR of AC_PERM. */ + uint64_t ctx; + /* Destination of the VM-specific context information. */ + uint32_t ctx_size; + /* Size of context buffer in bytes. */ +}; + +struct msm_mdf_protect_mem { + uint64_t dma_start_address; + uint64_t dma_end_address; + struct msm_mdf_dest_vm_and_perm_info dest_info[SUBSYS_MAX]; + uint32_t dest_info_size; +}; + +struct msm_mdf_mem { + struct device *dev; + uint8_t device_status; + uint32_t map_handle; + struct dma_buf *dma_buf; + dma_addr_t dma_addr; + size_t size; + void *va; +}; + +static struct msm_mdf_mem mdf_mem_data = {NULL,}; + +struct msm_mdf_smmu { + bool enabled; + char *subsys; + int vmid; + uint32_t proc_id; + struct device *cb_dev; + uint8_t device_status; + uint64_t sid; + struct dma_iommu_mapping *mapping; + dma_addr_t pa; + size_t pa_len; +}; + +static struct msm_mdf_smmu mdf_smmu_data[SUBSYS_MAX] = { + { + .subsys = "adsp", + .vmid = VMID_LPASS, + }, + { + .subsys = "dsps", + .vmid = VMID_SSC_Q6, + .proc_id = AVS_MDF_SSC_PROC_ID, + }, + { + .subsys = "modem", + .vmid = VMID_MSS_MSA, + .proc_id = AVS_MDF_MDSP_PROC_ID, + }, + { + .subsys = "cdsp", + .vmid = VMID_CDSP, + .proc_id = AVS_MDF_CDSP_PROC_ID, + }, +}; + +static void *ssr_handle; + +static inline uint64_t buf_page_start(uint64_t buf) +{ + uint64_t start = (uint64_t) buf & PAGE_MASK; + return start; +} + +static inline uint64_t buf_page_offset(uint64_t buf) +{ + uint64_t offset = (uint64_t) buf & (PAGE_SIZE - 1); + return offset; +} + +static inline int buf_num_pages(uint64_t buf, ssize_t len) +{ + uint64_t start = buf_page_start(buf) >> PAGE_SHIFT; + uint64_t end = (((uint64_t) buf + len - 1) & PAGE_MASK) >> PAGE_SHIFT; + int nPages = end - start + 1; + return nPages; +} + +static inline uint64_t buf_page_size(uint32_t size) +{ + uint64_t sz = (size + (PAGE_SIZE - 1)) & PAGE_MASK; + + return sz > PAGE_SIZE ? sz : PAGE_SIZE; +} + +static inline void *uint64_to_ptr(uint64_t addr) +{ + void *ptr = (void *)((uintptr_t)addr); + return ptr; +} + +static inline uint64_t ptr_to_uint64(void *ptr) +{ + uint64_t addr = (uint64_t)((uintptr_t)ptr); + return addr; +} + +static int msm_mdf_dma_buf_map(struct msm_mdf_mem *mem, + struct msm_mdf_smmu *smmu) +{ + int rc = 0; + + if (!smmu) + return -EINVAL; + if (smmu->device_status & MSM_MDF_MEM_MAPPED) + return 0; + if (smmu->enabled) { + if (smmu->cb_dev == NULL) { + pr_err("%s: cb device is not initialized\n", + __func__); + /* Retry if LPASS cb device is not ready + * from audio ION during probing. + */ + if (!strcmp("adsp", smmu->subsys)) { + rc = msm_audio_ion_get_smmu_info(&smmu->cb_dev, + &smmu->sid); + if (rc) { + pr_err("%s: msm_audio_ion_get_smmu_info failed, rc = %d\n", + __func__, rc); + goto err; + } + } else + return -ENODEV; + } + + smmu->pa = dma_map_single_attrs(smmu->cb_dev, mem->va, + mem->size, DMA_BIDIRECTIONAL, DMA_ATTR_SKIP_CPU_SYNC); + if (dma_mapping_error(smmu->cb_dev, smmu->pa)) { + rc = -ENOMEM; + pr_err("%s: failed to map single, rc = %d\n", + __func__, rc); + goto err; + } + smmu->pa_len = mem->size; + + /* Append the SMMU SID information to the IOVA address */ + if (smmu->sid) + smmu->pa |= smmu->sid; + } else { + smmu->pa = mem->dma_addr; + smmu->pa_len = mem->size; + } + pr_err("%s: pa=%pa, pa_len=%zd\n", __func__, + &smmu->pa, smmu->pa_len); + + smmu->device_status |= MSM_MDF_MEM_MAPPED; + + return 0; +err: + return rc; +} + +static int msm_mdf_alloc_dma_buf(struct msm_mdf_mem *mem) +{ + int rc = 0; + + if (!mem) + return -EINVAL; + + if (mem->device_status & MSM_MDF_MEM_ALLOCATED) + return 0; + + if (mem->dev == NULL) { + pr_err("%s: device is not initialized\n", + __func__); + return -ENODEV; + } + + mem->va = dma_alloc_attrs(mem->dev, mem->size, + &mem->dma_addr, GFP_KERNEL, DMA_ATTR_NO_KERNEL_MAPPING); + if (IS_ERR_OR_NULL(mem->va)) { + pr_err("%s: failed to allocate dma memory, rc = %d\n", + __func__, rc); + return -ENOMEM; + } + mem->va = phys_to_virt(mem->dma_addr); + mem->device_status |= MSM_MDF_MEM_ALLOCATED; + return rc; +} + +static int msm_mdf_free_dma_buf(struct msm_mdf_mem *mem) +{ + if (!mem) + return -EINVAL; + + if (mem->dev == NULL) { + pr_err("%s: device is not initialized\n", + __func__); + return -ENODEV; + } + + //dma_free_coherent(mem->dev, mem->size, mem->va, + // mem->dma_addr); + + mem->device_status &= ~MSM_MDF_MEM_ALLOCATED; + return 0; +} + +static int msm_mdf_dma_buf_unmap(struct msm_mdf_mem *mem, + struct msm_mdf_smmu *smmu) +{ + if (!smmu) + return -EINVAL; + + if (smmu->enabled) { + if (smmu->cb_dev == NULL) { + pr_err("%s: cb device is not initialized\n", + __func__); + return -ENODEV; + } + //if (smmu->pa && mem->size) + //dma_unmap_single(smmu->cb_dev, smmu->pa, + // mem->size, DMA_BIDIRECTIONAL); + } + + smmu->device_status &= ~MSM_MDF_MEM_MAPPED; + + return 0; +} + +static int msm_mdf_map_memory_to_subsys(struct msm_mdf_mem *mem, + struct msm_mdf_smmu *smmu) +{ + int rc = 0; + + if (!mem || !smmu) + return -EINVAL; + + /* Map mdf shared memory to ADSP */ + if (!strcmp("adsp", smmu->subsys)) { + rc = q6core_map_memory_regions((phys_addr_t *)&smmu->pa, + ADSP_MEMORY_MAP_MDF_SHMEM_4K_POOL, + (uint32_t *)&smmu->pa_len, 1, &mem->map_handle); + if (rc) { + pr_err("%s: q6core_map_memory_regions failed, rc = %d\n", + __func__, rc); + } + } else { + if (mem->map_handle) { + /* Map mdf shared memory to remote DSPs */ + rc = q6core_map_mdf_shared_memory(mem->map_handle, + (phys_addr_t *)&smmu->pa, smmu->proc_id, + (uint32_t *)&smmu->pa_len, 1); + if (rc) { + pr_err("%s: q6core_map_mdf_shared_memory failed, rc = %d\n", + __func__, rc); + } + } + } + return rc; +} + +static void msm_mdf_unmap_memory_to_subsys(struct msm_mdf_mem *mem, + struct msm_mdf_smmu *smmu) +{ + if (!mem || !smmu) + return; + + if (!strcmp("adsp", smmu->subsys)) { + if (mem->map_handle) + q6core_memory_unmap_regions(mem->map_handle); + } +} + +static int msm_mdf_assign_memory_to_subsys(struct msm_mdf_mem *mem) +{ + int ret = 0, i; + struct scm_desc desc = {0}; + struct msm_mdf_protect_mem *scm_buffer; + uint32_t fnid; + + scm_buffer = kzalloc(sizeof(struct msm_mdf_protect_mem), GFP_KERNEL); + if (!scm_buffer) + return -ENOMEM; + + scm_buffer->dma_start_address = mem->dma_addr; + scm_buffer->dma_end_address = mem->dma_addr + buf_page_size(mem->size); + for (i = 0; i < SUBSYS_MAX; i++) { + scm_buffer->dest_info[i].dst_vm = mdf_smmu_data[i].vmid; + scm_buffer->dest_info[i].dst_vm_perm = + MEM_PROTECT_AC_PERM_READ | MEM_PROTECT_AC_PERM_WRITE; + scm_buffer->dest_info[i].ctx = 0; + scm_buffer->dest_info[i].ctx_size = 0; + } + scm_buffer->dest_info_size = + sizeof(struct msm_mdf_dest_vm_and_perm_info) * SUBSYS_MAX; + + /* flush cache required by scm_call2 */ + dmac_flush_range(scm_buffer, ((void *)scm_buffer) + + sizeof(struct msm_mdf_protect_mem)); + + desc.args[0] = scm_buffer->dma_start_address; + desc.args[1] = scm_buffer->dma_end_address; + desc.args[2] = virt_to_phys(&(scm_buffer->dest_info[0])); + desc.args[3] = scm_buffer->dest_info_size; + + desc.arginfo = SCM_ARGS(4, SCM_VAL, SCM_VAL, SCM_RO, SCM_VAL); + + fnid = SCM_SIP_FNID(SCM_SVC_MP, TZ_MPU_LOCK_NS_REGION); + ret = scm_call2(fnid, &desc); + if (ret < 0) { + pr_err("%s: SCM call2 failed, ret %d scm_resp %llu\n", + __func__, ret, desc.ret[0]); + } + /* No More need for scm_buffer, freeing the same */ + kfree(scm_buffer); + return ret; +} + +/** + * msm_mdf_mem_init - Initializes MDF memory pool and + * map memory to subsystem + * + * Returns 0 on success or ret on failure. + */ + +int msm_mdf_mem_init(void) +{ + int rc = 0, i, j; + struct msm_mdf_mem *mem = &mdf_mem_data; + struct msm_mdf_smmu *smmu; + unsigned long timeout = jiffies + + msecs_to_jiffies(ADSP_STATE_READY_TIMEOUT_MS); + int adsp_ready = 0; + + if (!(mdf_mem_data.device_status & MSM_MDF_PROBED)) + return -ENODEV; + + if (mdf_mem_data.device_status & MSM_MDF_INITIALIZED) + return 0; + + /* TODO: pulling may not be needed as Q6 Core state should be + * checked during machine driver probing. + */ + do { + if (!q6core_is_adsp_ready()) { + pr_err("%s: ADSP Audio NOT Ready\n", + __func__); + /* ADSP will be coming up after subsystem restart and + * it might not be fully up when the control reaches + * here. So, wait for 50msec before checking ADSP state + */ + msleep(50); + } else { + pr_debug("%s: ADSP Audio Ready\n", + __func__); + adsp_ready = 1; + break; + } + } while (time_after(timeout, jiffies)); + + if (!adsp_ready) { + pr_err("%s: timed out waiting for ADSP Audio\n", + __func__); + return -ETIMEDOUT; + } + + if (mem->device_status & MSM_MDF_MEM_ALLOCATED) { + for (i = 0; i < SUBSYS_MAX; i++) { + smmu = &mdf_smmu_data[i]; + rc = msm_mdf_dma_buf_map(mem, smmu); + if (rc) { + pr_err("%s: msm_mdf_dma_buf_map failed, rc = %d\n", + __func__, rc); + goto err; + } + } + + rc = msm_mdf_assign_memory_to_subsys(mem); + if (rc) { + pr_err("%s: msm_mdf_assign_memory_to_subsys failed\n", + __func__); + goto err; + } + + for (j = 0; j < SUBSYS_MAX; j++) { + smmu = &mdf_smmu_data[j]; + rc = msm_mdf_map_memory_to_subsys(mem, smmu); + if (rc) { + pr_err("%s: msm_mdf_map_memory_to_subsys failed\n", + __func__); + goto err; + } + } + + mdf_mem_data.device_status |= MSM_MDF_INITIALIZED; + } + return 0; +err: + return rc; +} +EXPORT_SYMBOL(msm_mdf_mem_init); + +int msm_mdf_mem_deinit(void) +{ + int rc = 0, i; + struct msm_mdf_mem *mem = &mdf_mem_data; + struct msm_mdf_smmu *smmu; + + if (!(mdf_mem_data.device_status & MSM_MDF_INITIALIZED)) + return -ENODEV; + + for (i = SUBSYS_MAX - 1; i >= 0; i--) { + smmu = &mdf_smmu_data[i]; + msm_mdf_unmap_memory_to_subsys(mem, smmu); + } + + if (!rc) { + for (i = SUBSYS_MAX - 1; i >= 0; i--) { + smmu = &mdf_smmu_data[i]; + msm_mdf_dma_buf_unmap(mem, smmu); + } + + msm_mdf_free_dma_buf(mem); + mem->device_status &= ~MSM_MDF_MEM_ALLOCATED; + } + + mdf_mem_data.device_status &= ~MSM_MDF_INITIALIZED; + + return 0; +} +EXPORT_SYMBOL(msm_mdf_mem_deinit); + +static int msm_mdf_restart_notifier_cb(struct notifier_block *this, + unsigned long code, + void *_cmd) +{ + static int boot_count = 3; + + /* During LPASS boot, HLOS receives events: + * SUBSYS_BEFORE_POWERUP + * SUBSYS_PROXY_VOTE + * SUBSYS_AFTER_POWERUP - need skip + * SUBSYS_PROXY_UNVOTE + */ + if (boot_count) { + boot_count--; + return NOTIFY_OK; + } + + switch (code) { + case SUBSYS_BEFORE_SHUTDOWN: + pr_debug("Subsys Notify: Shutdown Started\n"); + /* Unmap and free memory upon restart event. */ + msm_mdf_mem_deinit(); + break; + case SUBSYS_AFTER_SHUTDOWN: + pr_debug("Subsys Notify: Shutdown Completed\n"); + break; + case SUBSYS_BEFORE_POWERUP: + pr_debug("Subsys Notify: Bootup Started\n"); + break; + case SUBSYS_AFTER_POWERUP: + pr_debug("Subsys Notify: Bootup Completed\n"); + /* Allocate and map memory after restart complete. */ + if (msm_mdf_mem_init()) + pr_err("msm_mdf_mem_init failed\n"); + break; + default: + pr_err("Subsys Notify: Generel: %lu\n", code); + break; + } + return NOTIFY_DONE; +} + +static const struct of_device_id msm_mdf_match_table[] = { + { .compatible = "qcom,msm-mdf", }, + { .compatible = "qcom,msm-mdf-mem-region", }, + { .compatible = "qcom,msm-mdf-cb", }, + {} +}; +MODULE_DEVICE_TABLE(of, msm_mdf_match_table); + +static int msm_mdf_cb_probe(struct device *dev) +{ + struct msm_mdf_smmu *smmu; + u64 smmu_sid = 0; + u64 smmu_sid_mask = 0; + struct of_phandle_args iommuspec; + const char *subsys; + int rc = 0, i; + + subsys = of_get_property(dev->of_node, "label", NULL); + if (!subsys) { + dev_err(dev, "%s: could not get label\n", + __func__); + return -EINVAL; + } + + for (i = 0; i < SUBSYS_MAX; i++) { + if (!mdf_smmu_data[i].subsys) + continue; + if (!strcmp(subsys, mdf_smmu_data[i].subsys)) + break; + } + if (i >= SUBSYS_MAX) { + dev_err(dev, "%s: subsys %s not supported\n", + __func__, subsys); + return -EINVAL; + } + + smmu = &mdf_smmu_data[i]; + + smmu->enabled = of_property_read_bool(dev->of_node, + "qcom,smmu-enabled"); + + dev_info(dev, "%s: SMMU is %s for %s\n", __func__, + (smmu->enabled) ? "enabled" : "disabled", + smmu->subsys); + + if (smmu->enabled) { + /* Get SMMU SID information from Devicetree */ + rc = of_property_read_u64(dev->of_node, + "qcom,smmu-sid-mask", + &smmu_sid_mask); + if (rc) { + dev_err(dev, + "%s: qcom,smmu-sid-mask missing in DT node, using default\n", + __func__); + smmu_sid_mask = 0xFFFFFFFFFFFFFFFF; + } + + rc = of_parse_phandle_with_args(dev->of_node, "iommus", + "#iommu-cells", 0, &iommuspec); + if (rc) + dev_err(dev, "%s: could not get smmu SID, ret = %d\n", + __func__, rc); + else + smmu_sid = (iommuspec.args[0] & smmu_sid_mask); + + smmu->sid = + smmu_sid << MSM_AUDIO_SMMU_SID_OFFSET; + + smmu->cb_dev = dev; + } + return 0; +} + +static int msm_mdf_remove(struct platform_device *pdev) +{ + int rc = 0, i; + + for (i = 0; i < SUBSYS_MAX; i++) { + if (!IS_ERR_OR_NULL(mdf_smmu_data[i].cb_dev)) + arm_iommu_detach_device(mdf_smmu_data[i].cb_dev); + if (!IS_ERR_OR_NULL(mdf_smmu_data[i].mapping)) + arm_iommu_release_mapping(mdf_smmu_data[i].mapping); + mdf_smmu_data[i].enabled = 0; + } + mdf_mem_data.device_status = 0; + + return rc; +} + +static int msm_mdf_probe(struct platform_device *pdev) +{ + int rc = 0; + enum apr_subsys_state q6_state; + struct device *dev = &pdev->dev; + uint32_t mdf_mem_data_size = 0; + + /* TODO: MDF probing should have no dependency + * on ADSP Q6 state. + */ + q6_state = apr_get_q6_state(); + if (q6_state == APR_SUBSYS_DOWN) { + dev_dbg(dev, "defering %s, adsp_state %d\n", + __func__, q6_state); + rc = -EPROBE_DEFER; + goto err; + } else + dev_dbg(dev, "%s: adsp is ready\n", __func__); + + if (of_device_is_compatible(dev->of_node, + "qcom,msm-mdf-cb")) + return msm_mdf_cb_probe(dev); + + if (of_device_is_compatible(dev->of_node, + "qcom,msm-mdf-mem-region")) { + mdf_mem_data.dev = dev; + + rc = of_property_read_u32(dev->of_node, + "qcom,msm-mdf-mem-data-size", + &mdf_mem_data_size); + if (rc) { + dev_dbg(&pdev->dev, "MDF mem data size entry not found\n"); + goto err; + } + + mdf_mem_data.size = mdf_mem_data_size; + dev_info(dev, "%s: mem region size %zd\n", + __func__, mdf_mem_data.size); + msm_mdf_alloc_dma_buf(&mdf_mem_data); + return 0; + } + + rc = of_platform_populate(pdev->dev.of_node, + msm_mdf_match_table, + NULL, &pdev->dev); + if (rc) { + dev_err(&pdev->dev, "%s: failed to populate child nodes", + __func__); + goto err; + } + mdf_mem_data.device_status |= MSM_MDF_PROBED; + +err: + return rc; +} + +static struct platform_driver msm_mdf_driver = { + .probe = msm_mdf_probe, + .remove = msm_mdf_remove, + .driver = { + .name = "msm-mdf", + .owner = THIS_MODULE, + .of_match_table = msm_mdf_match_table, + }, +}; + +static struct notifier_block nb = { + .priority = 0, + .notifier_call = msm_mdf_restart_notifier_cb, +}; + +int __init msm_mdf_init(void) +{ + /* Only need to monitor SSR from ADSP, which + * is the master DSP managing MDF memory. + */ + ssr_handle = subsys_notif_register_notifier("adsp", &nb); + return platform_driver_register(&msm_mdf_driver); +} + +void __exit msm_mdf_exit(void) +{ + platform_driver_unregister(&msm_mdf_driver); + + if (ssr_handle) + subsys_notif_unregister_notifier(ssr_handle, &nb); +} + +MODULE_DESCRIPTION("MSM MDF Module"); +MODULE_LICENSE("GPL v2"); diff --git a/audio-kernel/dsp/q6_init.c b/audio-kernel/dsp/q6_init.c new file mode 100644 index 000000000..202955a96 --- /dev/null +++ b/audio-kernel/dsp/q6_init.c @@ -0,0 +1,61 @@ +/* +Copyright (c) 2017, 2019 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 +#include +#include "q6_init.h" + +static int __init audio_q6_init(void) +{ + adsp_err_init(); + audio_cal_init(); + rtac_init(); + adm_init(); + afe_init(); + spk_params_init(); + q6asm_init(); + q6lsm_init(); + voice_init(); + core_init(); + msm_audio_ion_init(); + audio_slimslave_init(); + avtimer_init(); + msm_mdf_init(); + voice_mhi_init(); + return 0; +} + +static void __exit audio_q6_exit(void) +{ + msm_mdf_exit(); + avtimer_exit(); + audio_slimslave_exit(); + msm_audio_ion_exit(); + core_exit(); + voice_exit(); + q6lsm_exit(); + q6asm_exit(); + afe_exit(); + spk_params_exit(); + adm_exit(); + rtac_exit(); + audio_cal_exit(); + adsp_err_exit(); + voice_mhi_exit(); +} + +module_init(audio_q6_init); +module_exit(audio_q6_exit); +MODULE_DESCRIPTION("Q6 module"); +MODULE_LICENSE("GPL v2"); diff --git a/audio-kernel/dsp/q6_init.h b/audio-kernel/dsp/q6_init.h new file mode 100644 index 000000000..5014c37a6 --- /dev/null +++ b/audio-kernel/dsp/q6_init.h @@ -0,0 +1,84 @@ +/* +Copyright (c) 2017, 2019 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 __Q6_INIT_H__ +#define __Q6_INIT_H__ +int adsp_err_init(void); +int adm_init(void); +int afe_init(void); +int q6asm_init(void); +int q6lsm_init(void); +int voice_init(void); +int audio_cal_init(void); +int core_init(void); +int rtac_init(void); +int msm_audio_ion_init(void); +int audio_slimslave_init(void); +int avtimer_init(void); +#ifdef CONFIG_MSM_MDF +int msm_mdf_init(void); +void msm_mdf_exit(void); +#else +static inline int msm_mdf_init(void) +{ + return 0; +} + +static inline void msm_mdf_exit(void) +{ + return; +} +#endif +#ifdef CONFIG_XT_LOGGING +int spk_params_init(void); +void spk_params_exit(void); +#else +static inline int spk_params_init(void) +{ + return 0; +} +static inline void spk_params_exit(void) +{ +} +#endif + +void avtimer_exit(void); +void audio_slimslave_exit(void); +void msm_audio_ion_exit(void); +void rtac_exit(void); +void core_exit(void); +void audio_cal_exit(void); +void voice_exit(void); +void q6lsm_exit(void); +void q6asm_exit(void); +void afe_exit(void); +void adm_exit(void); +void adsp_err_exit(void); + +#ifdef CONFIG_VOICE_MHI +int voice_mhi_init(void); +void voice_mhi_exit(void); +#else +static inline int voice_mhi_init(void) +{ + return 0; +} + +static inline void voice_mhi_exit(void) +{ + return; +} +#endif +#endif + diff --git a/audio-kernel/dsp/q6adm.c b/audio-kernel/dsp/q6adm.c new file mode 100644 index 000000000..91e5b3cbb --- /dev/null +++ b/audio-kernel/dsp/q6adm.c @@ -0,0 +1,5045 @@ +/* Copyright (c) 2012-2019, 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "adsp_err.h" + +#define TIMEOUT_MS 1000 + +#define RESET_COPP_ID 99 +#define INVALID_COPP_ID 0xFF +/* Used for inband payload copy, max size is 4k */ +/* 3 is to account for module, instance & param ID in payload */ +#define ADM_GET_PARAMETER_LENGTH (4096 - APR_HDR_SIZE - 3 * sizeof(uint32_t)) + +#define ULL_SUPPORTED_BITS_PER_SAMPLE 16 +#define ULL_SUPPORTED_SAMPLE_RATE 48000 + +#ifndef CONFIG_DOLBY_DAP +#undef DOLBY_ADM_COPP_TOPOLOGY_ID +#define DOLBY_ADM_COPP_TOPOLOGY_ID 0xFFFFFFFE +#endif + +#ifndef CONFIG_DOLBY_DS2 +#undef DS2_ADM_COPP_TOPOLOGY_ID +#define DS2_ADM_COPP_TOPOLOGY_ID 0xFFFFFFFF +#endif + +/* ENUM for adm_status */ +enum adm_cal_status { + ADM_STATUS_CALIBRATION_REQUIRED = 0, + ADM_STATUS_MAX, +}; + +struct adm_copp { + + atomic_t id[AFE_MAX_PORTS][MAX_COPPS_PER_PORT]; + atomic_t cnt[AFE_MAX_PORTS][MAX_COPPS_PER_PORT]; + atomic_t topology[AFE_MAX_PORTS][MAX_COPPS_PER_PORT]; + atomic_t mode[AFE_MAX_PORTS][MAX_COPPS_PER_PORT]; + atomic_t stat[AFE_MAX_PORTS][MAX_COPPS_PER_PORT]; + atomic_t rate[AFE_MAX_PORTS][MAX_COPPS_PER_PORT]; + atomic_t bit_width[AFE_MAX_PORTS][MAX_COPPS_PER_PORT]; + atomic_t channels[AFE_MAX_PORTS][MAX_COPPS_PER_PORT]; + atomic_t app_type[AFE_MAX_PORTS][MAX_COPPS_PER_PORT]; + atomic_t acdb_id[AFE_MAX_PORTS][MAX_COPPS_PER_PORT]; + wait_queue_head_t wait[AFE_MAX_PORTS][MAX_COPPS_PER_PORT]; + wait_queue_head_t adm_delay_wait[AFE_MAX_PORTS][MAX_COPPS_PER_PORT]; + atomic_t adm_delay_stat[AFE_MAX_PORTS][MAX_COPPS_PER_PORT]; + uint32_t adm_delay[AFE_MAX_PORTS][MAX_COPPS_PER_PORT]; + unsigned long adm_status[AFE_MAX_PORTS][MAX_COPPS_PER_PORT]; +}; + +struct source_tracking_data { + struct dma_buf *dma_buf; + struct param_outband memmap; + int apr_cmd_status; +}; + +struct adm_ctl { + void *apr; + + struct adm_copp copp; + + atomic_t matrix_map_stat; + wait_queue_head_t matrix_map_wait; + + atomic_t adm_stat; + wait_queue_head_t adm_wait; + + struct cal_type_data *cal_data[ADM_MAX_CAL_TYPES]; + + atomic_t mem_map_handles[ADM_MEM_MAP_INDEX_MAX]; + atomic_t mem_map_index; + + struct param_outband outband_memmap; + struct source_tracking_data sourceTrackingData; + + int set_custom_topology; + int ec_ref_rx; + int num_ec_ref_rx_chans; + int ec_ref_rx_bit_width; + int ec_ref_rx_sampling_rate; + + int native_mode; +}; + +static struct adm_ctl this_adm; + +struct adm_multi_ch_map { + bool set_channel_map; + char channel_mapping[PCM_FORMAT_MAX_NUM_CHANNEL_V8]; +}; + +#define ADM_MCH_MAP_IDX_PLAYBACK 0 +#define ADM_MCH_MAP_IDX_REC 1 +static struct adm_multi_ch_map multi_ch_maps[2] = { + { false, + {0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0} + }, + { false, + {0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0} + } +}; + +static int adm_get_parameters[MAX_COPPS_PER_PORT * ADM_GET_PARAMETER_LENGTH]; +static int adm_module_topo_list[MAX_COPPS_PER_PORT * + ADM_GET_TOPO_MODULE_INSTANCE_LIST_LENGTH]; +static struct mutex dts_srs_lock; + +void msm_dts_srs_acquire_lock(void) +{ + mutex_lock(&dts_srs_lock); +} + +void msm_dts_srs_release_lock(void) +{ + mutex_unlock(&dts_srs_lock); +} + +/** + * adm_validate_and_get_port_index - + * validate given port id + * + * @port_id: Port ID number + * + * Returns valid index on success or error on failure + */ +int adm_validate_and_get_port_index(int port_id) +{ + int index; + int ret; + + ret = q6audio_validate_port(port_id); + if (ret < 0) { + pr_err("%s: port validation failed id 0x%x ret %d\n", + __func__, port_id, ret); + return -EINVAL; + } + + index = afe_get_port_index(port_id); + if (index < 0 || index >= AFE_MAX_PORTS) { + pr_err("%s: Invalid port idx %d port_id 0x%x\n", + __func__, index, + port_id); + return -EINVAL; + } + pr_debug("%s: port_idx- %d\n", __func__, index); + return index; +} +EXPORT_SYMBOL(adm_validate_and_get_port_index); + +/** + * adm_get_default_copp_idx - + * retrieve default copp_idx for given port + * + * @port_id: Port ID number + * + * Returns valid value on success or error on failure + */ +int adm_get_default_copp_idx(int port_id) +{ + int port_idx = adm_validate_and_get_port_index(port_id), idx; + + if (port_idx < 0) { + pr_err("%s: Invalid port id: 0x%x", __func__, port_id); + return -EINVAL; + } + pr_debug("%s: port_idx:%d\n", __func__, port_idx); + for (idx = 0; idx < MAX_COPPS_PER_PORT; idx++) { + if (atomic_read(&this_adm.copp.id[port_idx][idx]) != + RESET_COPP_ID) + return idx; + } + return -EINVAL; +} +EXPORT_SYMBOL(adm_get_default_copp_idx); + +int adm_get_topology_for_port_from_copp_id(int port_id, int copp_id) +{ + int port_idx = adm_validate_and_get_port_index(port_id), idx; + + if (port_idx < 0) { + pr_err("%s: Invalid port id: 0x%x", __func__, port_id); + return 0; + } + for (idx = 0; idx < MAX_COPPS_PER_PORT; idx++) + if (atomic_read(&this_adm.copp.id[port_idx][idx]) == copp_id) + return atomic_read(&this_adm.copp.topology[port_idx] + [idx]); + pr_err("%s: Invalid copp_id %d port_id 0x%x\n", + __func__, copp_id, port_id); + return 0; +} + +/** + * adm_get_topology_for_port_copp_idx - + * retrieve topology of given port/copp_idx + * + * @port_id: Port ID number + * @copp_idx: copp index of ADM copp + * + * Returns valid value on success or 0 on failure + */ +int adm_get_topology_for_port_copp_idx(int port_id, int copp_idx) +{ + int port_idx = adm_validate_and_get_port_index(port_id); + + if (port_idx < 0 || copp_idx >= MAX_COPPS_PER_PORT) { + pr_err("%s: Invalid port: 0x%x copp id: 0x%x", + __func__, port_id, copp_idx); + return 0; + } + return atomic_read(&this_adm.copp.topology[port_idx][copp_idx]); +} +EXPORT_SYMBOL(adm_get_topology_for_port_copp_idx); + +int adm_get_indexes_from_copp_id(int copp_id, int *copp_idx, int *port_idx) +{ + int p_idx, c_idx; + + for (p_idx = 0; p_idx < AFE_MAX_PORTS; p_idx++) { + for (c_idx = 0; c_idx < MAX_COPPS_PER_PORT; c_idx++) { + if (atomic_read(&this_adm.copp.id[p_idx][c_idx]) + == copp_id) { + if (copp_idx != NULL) + *copp_idx = c_idx; + if (port_idx != NULL) + *port_idx = p_idx; + return 0; + } + } + } + return -EINVAL; +} + +static int adm_get_copp_id(int port_idx, int copp_idx) +{ + pr_debug("%s: port_idx:%d copp_idx:%d\n", __func__, port_idx, copp_idx); + + if (copp_idx < 0 || copp_idx >= MAX_COPPS_PER_PORT) { + pr_err("%s: Invalid copp_num: %d\n", __func__, copp_idx); + return -EINVAL; + } + return atomic_read(&this_adm.copp.id[port_idx][copp_idx]); +} + +static int adm_get_idx_if_copp_exists(int port_idx, int topology, int mode, + int rate, int bit_width, int app_type) +{ + int idx; + + pr_debug("%s: port_idx-%d, topology-0x%x, mode-%d, rate-%d, bit_width-%d\n", + __func__, port_idx, topology, mode, rate, bit_width); + + for (idx = 0; idx < MAX_COPPS_PER_PORT; idx++) + if ((topology == + atomic_read(&this_adm.copp.topology[port_idx][idx])) && + (mode == atomic_read(&this_adm.copp.mode[port_idx][idx])) && + (rate == atomic_read(&this_adm.copp.rate[port_idx][idx])) && + (bit_width == + atomic_read(&this_adm.copp.bit_width[port_idx][idx])) && + (app_type == + atomic_read(&this_adm.copp.app_type[port_idx][idx]))) + return idx; + return -EINVAL; +} + +static int adm_get_next_available_copp(int port_idx) +{ + int idx; + + pr_debug("%s:\n", __func__); + for (idx = 0; idx < MAX_COPPS_PER_PORT; idx++) { + pr_debug("%s: copp_id:0x%x port_idx:%d idx:%d\n", __func__, + atomic_read(&this_adm.copp.id[port_idx][idx]), + port_idx, idx); + if (atomic_read(&this_adm.copp.id[port_idx][idx]) == + RESET_COPP_ID) + break; + } + return idx; +} + +/** + * srs_trumedia_open - + * command to set SRS trumedia open + * + * @port_id: Port ID number + * @copp_idx: copp index of ADM copp + * @srs_tech_id: SRS tech index + * @srs_params: params pointer + * + * Returns 0 on success or error on failure + */ +int srs_trumedia_open(int port_id, int copp_idx, __s32 srs_tech_id, + void *srs_params) +{ + struct param_hdr_v3 param_hdr; + struct mem_mapping_hdr mem_hdr; + u32 total_param_size = 0; + bool outband = false; + int port_idx; + int ret = 0; + + pr_debug("SRS - %s", __func__); + + memset(¶m_hdr, 0, sizeof(param_hdr)); + memset(&mem_hdr, 0, sizeof(mem_hdr)); + port_id = afe_convert_virtual_to_portid(port_id); + port_idx = adm_validate_and_get_port_index(port_id); + if (port_idx < 0) { + pr_err("%s: Invalid port_id %#x\n", __func__, port_id); + return -EINVAL; + } + + param_hdr.module_id = SRS_TRUMEDIA_MODULE_ID; + param_hdr.instance_id = INSTANCE_ID_0; + + switch (srs_tech_id) { + case SRS_ID_GLOBAL: { + param_hdr.param_id = SRS_TRUMEDIA_PARAMS; + param_hdr.param_size = + sizeof(struct srs_trumedia_params_GLOBAL); + break; + } + case SRS_ID_WOWHD: { + param_hdr.param_id = SRS_TRUMEDIA_PARAMS_WOWHD; + param_hdr.param_size = sizeof(struct srs_trumedia_params_WOWHD); + break; + } + case SRS_ID_CSHP: { + param_hdr.param_id = SRS_TRUMEDIA_PARAMS_CSHP; + param_hdr.param_size = sizeof(struct srs_trumedia_params_CSHP); + break; + } + case SRS_ID_HPF: { + param_hdr.param_id = SRS_TRUMEDIA_PARAMS_HPF; + param_hdr.param_size = sizeof(struct srs_trumedia_params_HPF); + break; + } + case SRS_ID_AEQ: { + u8 *update_params_ptr = (u8 *) this_adm.outband_memmap.kvaddr; + + outband = true; + + if (update_params_ptr == NULL) { + pr_err("ADM_SRS_TRUMEDIA - %s: null memmap for AEQ params\n", + __func__); + ret = -EINVAL; + goto fail_cmd; + } + + param_hdr.param_id = SRS_TRUMEDIA_PARAMS_AEQ; + param_hdr.param_size = sizeof(struct srs_trumedia_params_AEQ); + + ret = q6common_pack_pp_params(update_params_ptr, ¶m_hdr, + srs_params, &total_param_size); + if (ret) { + pr_err("%s: Failed to pack param header and data, error %d\n", + __func__, ret); + goto fail_cmd; + } + break; + } + case SRS_ID_HL: { + param_hdr.param_id = SRS_TRUMEDIA_PARAMS_HL; + param_hdr.param_size = sizeof(struct srs_trumedia_params_HL); + break; + } + case SRS_ID_GEQ: { + param_hdr.param_id = SRS_TRUMEDIA_PARAMS_GEQ; + param_hdr.param_size = sizeof(struct srs_trumedia_params_GEQ); + break; + } + default: + goto fail_cmd; + } + + if (outband && this_adm.outband_memmap.paddr) { + mem_hdr.data_payload_addr_lsw = + lower_32_bits(this_adm.outband_memmap.paddr); + mem_hdr.data_payload_addr_msw = + msm_audio_populate_upper_32_bits( + this_adm.outband_memmap.paddr); + mem_hdr.mem_map_handle = atomic_read( + &this_adm.mem_map_handles[ADM_SRS_TRUMEDIA]); + + ret = adm_set_pp_params(port_id, copp_idx, &mem_hdr, NULL, + total_param_size); + } else { + ret = adm_pack_and_set_one_pp_param(port_id, copp_idx, + param_hdr, + (u8 *) srs_params); + } + + if (ret < 0) + pr_err("SRS - %s: ADM enable for port %d failed\n", __func__, + port_id); + +fail_cmd: + return ret; +} +EXPORT_SYMBOL(srs_trumedia_open); + +static int adm_populate_channel_weight(u16 *ptr, + struct msm_pcm_channel_mixer *ch_mixer, + int channel_index) +{ + u16 i, j, start_index = 0; + + if (channel_index > ch_mixer->output_channel) { + pr_err("%s: channel index %d is larger than output_channel %d\n", + __func__, channel_index, ch_mixer->output_channel); + return -EINVAL; + } + + for (i = 0; i < ch_mixer->output_channel; i++) { + pr_debug("%s: weight for output %d:", __func__, i); + for (j = 0; j < ADM_MAX_CHANNELS; j++) + pr_debug(" %d", + ch_mixer->channel_weight[i][j]); + pr_debug("\n"); + } + + for (i = 0; i < channel_index; ++i) + start_index += ch_mixer->input_channels[i]; + + for (i = 0; i < ch_mixer->output_channel; ++i) { + for (j = start_index; + j < start_index + + ch_mixer->input_channels[channel_index]; j++) { + *ptr = ch_mixer->channel_weight[i][j]; + pr_debug("%s: ptr[%d][%d] = %d\n", + __func__, i, j, *ptr); + ptr++; + } + } + + return 0; +} + +/* + * adm_programable_channel_mixer + * + * Receives port_id, copp_idx, session_id, session_type, ch_mixer + * and channel_index to send ADM command to mix COPP data. + * + * port_id - Passed value, port_id for which backend is wanted + * copp_idx - Passed value, copp_idx for which COPP is wanted + * session_id - Passed value, session_id for which session is needed + * session_type - Passed value, session_type for RX or TX + * ch_mixer - Passed value, ch_mixer for which channel mixer config is needed + * channel_index - Passed value, channel_index for which channel is needed + */ +int adm_programable_channel_mixer(int port_id, int copp_idx, int session_id, + int session_type, + struct msm_pcm_channel_mixer *ch_mixer, + int channel_index) +{ + struct adm_cmd_set_pspd_mtmx_strtr_params_v5 *adm_params = NULL; + struct param_hdr_v1 data_v5; + int ret = 0, port_idx, sz = 0, param_size = 0; + u16 *adm_pspd_params; + u16 *ptr; + int index = 0; + + pr_debug("%s: port_id = %d\n", __func__, port_id); + port_id = afe_convert_virtual_to_portid(port_id); + port_idx = adm_validate_and_get_port_index(port_id); + if (port_idx < 0) { + pr_err("%s: Invalid port_id %#x\n", __func__, port_id); + return -EINVAL; + } + /* + * First 8 bytes are 4 bytes as rule number, 2 bytes as output + * channel and 2 bytes as input channel. + * 2 * ch_mixer->output_channel means output channel mapping. + * 2 * ch_mixer->input_channels[channel_index]) means input + * channel mapping. + * 2 * ch_mixer->input_channels[channel_index] * + * ch_mixer->output_channel) means the channel mixer weighting + * coefficients. + * param_size needs to be a multiple of 4 bytes. + */ + + param_size = 2 * (4 + ch_mixer->output_channel + + ch_mixer->input_channels[channel_index] + + ch_mixer->input_channels[channel_index] * + ch_mixer->output_channel); + param_size = roundup(param_size, 4); + + sz = sizeof(struct adm_cmd_set_pspd_mtmx_strtr_params_v5) + + sizeof(struct default_chmixer_param_id_coeff) + + sizeof(struct param_hdr_v1) + param_size; + pr_debug("%s: sz = %d\n", __func__, sz); + adm_params = kzalloc(sz, GFP_KERNEL); + if (!adm_params) + return -ENOMEM; + + adm_params->payload_addr_lsw = 0; + adm_params->payload_addr_msw = 0; + adm_params->mem_map_handle = 0; + adm_params->direction = session_type; + adm_params->sessionid = session_id; + pr_debug("%s: copp_id = %d, session id %d\n", __func__, + atomic_read(&this_adm.copp.id[port_idx][copp_idx]), + session_id); + adm_params->deviceid = atomic_read( + &this_adm.copp.id[port_idx][copp_idx]); + adm_params->reserved = 0; + + /* + * This module is internal to ADSP and cannot be configured with + * an instance id + */ + data_v5.module_id = MTMX_MODULE_ID_DEFAULT_CHMIXER; + data_v5.param_id = DEFAULT_CHMIXER_PARAM_ID_COEFF; + data_v5.reserved = 0; + data_v5.param_size = param_size; + adm_params->payload_size = + sizeof(struct default_chmixer_param_id_coeff) + + sizeof(struct param_hdr_v1) + data_v5.param_size; + adm_pspd_params = (u16 *)((u8 *)adm_params + + sizeof(struct adm_cmd_set_pspd_mtmx_strtr_params_v5)); + memcpy(adm_pspd_params, &data_v5, sizeof(data_v5)); + + adm_pspd_params = (u16 *)((u8 *)adm_params + + sizeof(struct adm_cmd_set_pspd_mtmx_strtr_params_v5) + + sizeof(data_v5)); + + adm_pspd_params[0] = ch_mixer->rule; + adm_pspd_params[2] = ch_mixer->output_channel; + adm_pspd_params[3] = ch_mixer->input_channels[channel_index]; + index = 4; + + if (ch_mixer->output_channel == 1) { + adm_pspd_params[index] = PCM_CHANNEL_FC; + } else if (ch_mixer->output_channel == 2) { + adm_pspd_params[index] = PCM_CHANNEL_FL; + adm_pspd_params[index + 1] = PCM_CHANNEL_FR; + } else if (ch_mixer->output_channel == 3) { + adm_pspd_params[index] = PCM_CHANNEL_FL; + adm_pspd_params[index + 1] = PCM_CHANNEL_FR; + adm_pspd_params[index + 2] = PCM_CHANNEL_FC; + } else if (ch_mixer->output_channel == 4) { + adm_pspd_params[index] = PCM_CHANNEL_FL; + adm_pspd_params[index + 1] = PCM_CHANNEL_FR; + adm_pspd_params[index + 2] = PCM_CHANNEL_LS; + adm_pspd_params[index + 3] = PCM_CHANNEL_RS; + } else if (ch_mixer->output_channel == 5) { + adm_pspd_params[index] = PCM_CHANNEL_FL; + adm_pspd_params[index + 1] = PCM_CHANNEL_FR; + adm_pspd_params[index + 2] = PCM_CHANNEL_FC; + adm_pspd_params[index + 3] = PCM_CHANNEL_LS; + adm_pspd_params[index + 4] = PCM_CHANNEL_RS; + } else if (ch_mixer->output_channel == 6) { + adm_pspd_params[index] = PCM_CHANNEL_FL; + adm_pspd_params[index + 1] = PCM_CHANNEL_FR; + adm_pspd_params[index + 2] = PCM_CHANNEL_LFE; + adm_pspd_params[index + 3] = PCM_CHANNEL_FC; + adm_pspd_params[index + 4] = PCM_CHANNEL_LS; + adm_pspd_params[index + 5] = PCM_CHANNEL_RS; + } else if (ch_mixer->output_channel == 8) { + adm_pspd_params[index] = PCM_CHANNEL_FL; + adm_pspd_params[index + 1] = PCM_CHANNEL_FR; + adm_pspd_params[index + 2] = PCM_CHANNEL_LFE; + adm_pspd_params[index + 3] = PCM_CHANNEL_FC; + adm_pspd_params[index + 4] = PCM_CHANNEL_LS; + adm_pspd_params[index + 5] = PCM_CHANNEL_RS; + adm_pspd_params[index + 6] = PCM_CHANNEL_LB; + adm_pspd_params[index + 7] = PCM_CHANNEL_RB; + } + + index = index + ch_mixer->output_channel; + if (ch_mixer->input_channels[channel_index] == 1) { + adm_pspd_params[index] = PCM_CHANNEL_FC; + } else if (ch_mixer->input_channels[channel_index] == 2) { + adm_pspd_params[index] = PCM_CHANNEL_FL; + adm_pspd_params[index + 1] = PCM_CHANNEL_FR; + } else if (ch_mixer->input_channels[channel_index] == 3) { + adm_pspd_params[index] = PCM_CHANNEL_FL; + adm_pspd_params[index + 1] = PCM_CHANNEL_FR; + adm_pspd_params[index + 2] = PCM_CHANNEL_FC; + } else if (ch_mixer->input_channels[channel_index] == 4) { + adm_pspd_params[index] = PCM_CHANNEL_FL; + adm_pspd_params[index + 1] = PCM_CHANNEL_FR; + adm_pspd_params[index + 2] = PCM_CHANNEL_LS; + adm_pspd_params[index + 3] = PCM_CHANNEL_RS; + } else if (ch_mixer->input_channels[channel_index] == 5) { + adm_pspd_params[index] = PCM_CHANNEL_FL; + adm_pspd_params[index + 1] = PCM_CHANNEL_FR; + adm_pspd_params[index + 2] = PCM_CHANNEL_FC; + adm_pspd_params[index + 3] = PCM_CHANNEL_LS; + adm_pspd_params[index + 4] = PCM_CHANNEL_RS; + } else if (ch_mixer->input_channels[channel_index] == 6) { + adm_pspd_params[index] = PCM_CHANNEL_FL; + adm_pspd_params[index + 1] = PCM_CHANNEL_FR; + adm_pspd_params[index + 2] = PCM_CHANNEL_LFE; + adm_pspd_params[index + 3] = PCM_CHANNEL_FC; + adm_pspd_params[index + 4] = PCM_CHANNEL_LS; + adm_pspd_params[index + 5] = PCM_CHANNEL_RS; + } else if (ch_mixer->input_channels[channel_index] == 8) { + adm_pspd_params[index] = PCM_CHANNEL_FL; + adm_pspd_params[index + 1] = PCM_CHANNEL_FR; + adm_pspd_params[index + 2] = PCM_CHANNEL_LFE; + adm_pspd_params[index + 3] = PCM_CHANNEL_FC; + adm_pspd_params[index + 4] = PCM_CHANNEL_LS; + adm_pspd_params[index + 5] = PCM_CHANNEL_RS; + adm_pspd_params[index + 6] = PCM_CHANNEL_LB; + adm_pspd_params[index + 7] = PCM_CHANNEL_RB; + } + + index = index + ch_mixer->input_channels[channel_index]; + ret = adm_populate_channel_weight(&adm_pspd_params[index], + ch_mixer, channel_index); + if (ret) { + pr_err("%s: fail to get channel weight with error %d\n", + __func__, ret); + goto fail_cmd; + } + + adm_params->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + adm_params->hdr.src_svc = APR_SVC_ADM; + adm_params->hdr.src_domain = APR_DOMAIN_APPS; + adm_params->hdr.src_port = port_id; + adm_params->hdr.dest_svc = APR_SVC_ADM; + adm_params->hdr.dest_domain = APR_DOMAIN_ADSP; + adm_params->hdr.dest_port = + atomic_read(&this_adm.copp.id[port_idx][copp_idx]); + adm_params->hdr.token = port_idx << 16 | copp_idx; + adm_params->hdr.opcode = ADM_CMD_SET_PSPD_MTMX_STRTR_PARAMS_V5; + adm_params->hdr.pkt_size = sz; + adm_params->payload_addr_lsw = 0; + adm_params->payload_addr_msw = 0; + adm_params->mem_map_handle = 0; + adm_params->reserved = 0; + + ptr = (u16 *)adm_params; + for (index = 0; index < (sz / 2); index++) + pr_debug("%s: adm_params[%d] = 0x%x\n", + __func__, index, (unsigned int)ptr[index]); + + atomic_set(&this_adm.copp.stat[port_idx][copp_idx], 0); + ret = apr_send_pkt(this_adm.apr, (uint32_t *)adm_params); + if (ret < 0) { + pr_err("%s: Set params failed port %d rc %d\n", __func__, + port_id, ret); + ret = -EINVAL; + goto fail_cmd; + } + + ret = wait_event_timeout(this_adm.copp.wait[port_idx][copp_idx], + atomic_read( + &this_adm.copp.stat[port_idx][copp_idx]) >= 0, + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: set params timed out port = %d\n", + __func__, port_id); + ret = -ETIMEDOUT; + goto fail_cmd; + } + ret = 0; +fail_cmd: + kfree(adm_params); + + return ret; +} +EXPORT_SYMBOL(adm_programable_channel_mixer); + +/** + * adm_set_stereo_to_custom_stereo - + * command to update custom stereo + * + * @port_id: Port ID number + * @copp_idx: copp index of ADM copp + * @session_id: session id to be updated + * @params: params pointer + * @param_length: length of params + * + * Returns 0 on success or error on failure + */ +int adm_set_stereo_to_custom_stereo(int port_id, int copp_idx, + unsigned int session_id, char *params, + uint32_t params_length) +{ + struct adm_cmd_set_pspd_mtmx_strtr_params_v5 *adm_params = NULL; + int sz, rc = 0, port_idx; + + pr_debug("%s:\n", __func__); + port_id = afe_convert_virtual_to_portid(port_id); + port_idx = adm_validate_and_get_port_index(port_id); + if (port_idx < 0) { + pr_err("%s: Invalid port_id 0x%x\n", __func__, port_id); + return -EINVAL; + } + + sz = sizeof(struct adm_cmd_set_pspd_mtmx_strtr_params_v5) + + params_length; + adm_params = kzalloc(sz, GFP_KERNEL); + if (!adm_params) { + pr_err("%s, adm params memory alloc failed\n", __func__); + return -ENOMEM; + } + + memcpy(((u8 *)adm_params + + sizeof(struct adm_cmd_set_pspd_mtmx_strtr_params_v5)), + params, params_length); + adm_params->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + adm_params->hdr.pkt_size = sz; + adm_params->hdr.src_svc = APR_SVC_ADM; + adm_params->hdr.src_domain = APR_DOMAIN_APPS; + adm_params->hdr.src_port = port_id; + adm_params->hdr.dest_svc = APR_SVC_ADM; + adm_params->hdr.dest_domain = APR_DOMAIN_ADSP; + adm_params->hdr.dest_port = 0; /* Ignored */; + adm_params->hdr.token = port_idx << 16 | copp_idx; + adm_params->hdr.opcode = ADM_CMD_SET_PSPD_MTMX_STRTR_PARAMS_V5; + adm_params->payload_addr_lsw = 0; + adm_params->payload_addr_msw = 0; + adm_params->mem_map_handle = 0; + adm_params->payload_size = params_length; + /* direction RX as 0 */ + adm_params->direction = ADM_MATRIX_ID_AUDIO_RX; + /* session id for this cmd to be applied on */ + adm_params->sessionid = session_id; + adm_params->deviceid = + atomic_read(&this_adm.copp.id[port_idx][copp_idx]); + adm_params->reserved = 0; + pr_debug("%s: deviceid %d, session_id %d, src_port %d, dest_port %d\n", + __func__, adm_params->deviceid, adm_params->sessionid, + adm_params->hdr.src_port, adm_params->hdr.dest_port); + atomic_set(&this_adm.copp.stat[port_idx][copp_idx], -1); + rc = apr_send_pkt(this_adm.apr, (uint32_t *)adm_params); + if (rc < 0) { + pr_err("%s: Set params failed port = 0x%x rc %d\n", + __func__, port_id, rc); + rc = -EINVAL; + goto set_stereo_to_custom_stereo_return; + } + /* Wait for the callback */ + rc = wait_event_timeout(this_adm.copp.wait[port_idx][copp_idx], + atomic_read(&this_adm.copp.stat + [port_idx][copp_idx]) >= 0, + msecs_to_jiffies(TIMEOUT_MS)); + if (!rc) { + pr_err("%s: Set params timed out port = 0x%x\n", __func__, + port_id); + rc = -EINVAL; + goto set_stereo_to_custom_stereo_return; + } else if (atomic_read(&this_adm.copp.stat + [port_idx][copp_idx]) > 0) { + pr_err("%s: DSP returned error[%s]\n", __func__, + adsp_err_get_err_str(atomic_read( + &this_adm.copp.stat + [port_idx][copp_idx]))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&this_adm.copp.stat + [port_idx][copp_idx])); + goto set_stereo_to_custom_stereo_return; + } + rc = 0; +set_stereo_to_custom_stereo_return: + kfree(adm_params); + return rc; +} +EXPORT_SYMBOL(adm_set_stereo_to_custom_stereo); + +/* + * adm_set_custom_chmix_cfg: + * Set the custom channel mixer configuration for ADM + * + * @port_id: Backend port id + * @copp_idx: ADM copp index + * @session_id: ID of the requesting session + * @params: Expected packaged params for channel mixer + * @params_length: Length of the params to be set + * @direction: RX or TX direction + * @stream_type: Audio or Listen stream type + */ +int adm_set_custom_chmix_cfg(int port_id, int copp_idx, + unsigned int session_id, char *params, + uint32_t params_length, int direction, + int stream_type) +{ + struct adm_cmd_set_pspd_mtmx_strtr_params_v6 *adm_params = NULL; + int sz, rc = 0, port_idx; + + port_id = afe_convert_virtual_to_portid(port_id); + port_idx = adm_validate_and_get_port_index(port_id); + if (port_idx < 0) { + pr_err("%s: Invalid port_id 0x%x\n", __func__, port_id); + return -EINVAL; + } + + sz = sizeof(struct adm_cmd_set_pspd_mtmx_strtr_params_v6) + + params_length; + adm_params = kzalloc(sz, GFP_KERNEL); + if (!adm_params) { + pr_err("%s, adm params memory alloc failed\n", __func__); + return -ENOMEM; + } + + memcpy(((u8 *)adm_params + + sizeof(struct adm_cmd_set_pspd_mtmx_strtr_params_v6)), + params, params_length); + adm_params->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + adm_params->hdr.pkt_size = sz; + adm_params->hdr.src_svc = APR_SVC_ADM; + adm_params->hdr.src_domain = APR_DOMAIN_APPS; + adm_params->hdr.src_port = port_id; + adm_params->hdr.dest_svc = APR_SVC_ADM; + adm_params->hdr.dest_domain = APR_DOMAIN_ADSP; + adm_params->hdr.dest_port = 0; /* Ignored */; + adm_params->hdr.token = port_idx << 16 | copp_idx; + adm_params->hdr.opcode = ADM_CMD_SET_PSPD_MTMX_STRTR_PARAMS_V6; + adm_params->payload_addr_lsw = 0; + adm_params->payload_addr_msw = 0; + adm_params->mem_map_handle = 0; + adm_params->payload_size = params_length; + adm_params->direction = direction; + /* session id for this cmd to be applied on */ + adm_params->sessionid = session_id; + adm_params->deviceid = + atomic_read(&this_adm.copp.id[port_idx][copp_idx]); + /* connecting stream type i.e. lsm or asm */ + adm_params->stream_type = stream_type; + pr_debug("%s: deviceid %d, session_id %d, src_port %d, dest_port %d\n", + __func__, adm_params->deviceid, adm_params->sessionid, + adm_params->hdr.src_port, adm_params->hdr.dest_port); + atomic_set(&this_adm.copp.stat[port_idx][copp_idx], -1); + rc = apr_send_pkt(this_adm.apr, (uint32_t *)adm_params); + if (rc < 0) { + pr_err("%s: Set params failed port = 0x%x rc %d\n", + __func__, port_id, rc); + rc = -EINVAL; + goto exit; + } + /* Wait for the callback */ + rc = wait_event_timeout(this_adm.copp.wait[port_idx][copp_idx], + atomic_read(&this_adm.copp.stat + [port_idx][copp_idx]), + msecs_to_jiffies(TIMEOUT_MS)); + if (!rc) { + pr_err("%s: Set params timed out port = 0x%x\n", __func__, + port_id); + rc = -EINVAL; + goto exit; + } else if (atomic_read(&this_adm.copp.stat + [port_idx][copp_idx]) > 0) { + pr_err("%s: DSP returned error[%s]\n", __func__, + adsp_err_get_err_str(atomic_read( + &this_adm.copp.stat + [port_idx][copp_idx]))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&this_adm.copp.stat + [port_idx][copp_idx])); + goto exit; + } + + rc = 0; +exit: + kfree(adm_params); + return rc; +} +EXPORT_SYMBOL(adm_set_custom_chmix_cfg); + +/* + * With pre-packed data, only the opcode differes from V5 and V6. + * Use q6common_pack_pp_params to pack the data correctly. + */ +int adm_set_pp_params(int port_id, int copp_idx, + struct mem_mapping_hdr *mem_hdr, u8 *param_data, + u32 param_size) +{ + struct adm_cmd_set_pp_params *adm_set_params = NULL; + int size = 0; + int port_idx = 0; + atomic_t *copp_stat = NULL; + int ret = 0; + + port_id = afe_convert_virtual_to_portid(port_id); + port_idx = adm_validate_and_get_port_index(port_id); + if (port_idx < 0 || port_idx >= AFE_MAX_PORTS) { + pr_err("%s: Invalid port_idx 0x%x\n", __func__, port_idx); + return -EINVAL; + } else if (copp_idx < 0 || copp_idx >= MAX_COPPS_PER_PORT) { + pr_err("%s: Invalid copp_idx 0x%x\n", __func__, copp_idx); + return -EINVAL; + } + + /* Only add params_size in inband case */ + size = sizeof(struct adm_cmd_set_pp_params); + if (param_data != NULL) + size += param_size; + adm_set_params = kzalloc(size, GFP_KERNEL); + if (!adm_set_params) + return -ENOMEM; + + adm_set_params->apr_hdr.hdr_field = + APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + adm_set_params->apr_hdr.pkt_size = size; + adm_set_params->apr_hdr.src_svc = APR_SVC_ADM; + adm_set_params->apr_hdr.src_domain = APR_DOMAIN_APPS; + adm_set_params->apr_hdr.src_port = port_id; + adm_set_params->apr_hdr.dest_svc = APR_SVC_ADM; + adm_set_params->apr_hdr.dest_domain = APR_DOMAIN_ADSP; + adm_set_params->apr_hdr.dest_port = + atomic_read(&this_adm.copp.id[port_idx][copp_idx]); + adm_set_params->apr_hdr.token = port_idx << 16 | copp_idx; + + if (q6common_is_instance_id_supported()) + adm_set_params->apr_hdr.opcode = ADM_CMD_SET_PP_PARAMS_V6; + else + adm_set_params->apr_hdr.opcode = ADM_CMD_SET_PP_PARAMS_V5; + + adm_set_params->payload_size = param_size; + + if (mem_hdr != NULL) { + /* Out of Band Case */ + adm_set_params->mem_hdr = *mem_hdr; + } else if (param_data != NULL) { + /* + * In band case. Parameter data must be pre-packed with its + * header before calling this function. Use + * q6common_pack_pp_params to pack parameter data and header + * correctly. + */ + memcpy(&adm_set_params->param_data, param_data, param_size); + } else { + pr_err("%s: Received NULL pointers for both memory header and param data\n", + __func__); + ret = -EINVAL; + goto done; + } + + copp_stat = &this_adm.copp.stat[port_idx][copp_idx]; + atomic_set(copp_stat, -1); + ret = apr_send_pkt(this_adm.apr, (uint32_t *) adm_set_params); + if (ret < 0) { + pr_err("%s: Set params APR send failed port = 0x%x ret %d\n", + __func__, port_id, ret); + goto done; + } + ret = wait_event_timeout(this_adm.copp.wait[port_idx][copp_idx], + atomic_read(copp_stat) >= 0, + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: Set params timed out port = 0x%x\n", __func__, + port_id); + ret = -ETIMEDOUT; + goto done; + } + if (atomic_read(copp_stat) > 0) { + pr_err("%s: DSP returned error[%s]\n", __func__, + adsp_err_get_err_str(atomic_read(copp_stat))); + ret = adsp_err_get_lnx_err_code(atomic_read(copp_stat)); + goto done; + } + + ret = 0; +done: + kfree(adm_set_params); + return ret; +} +EXPORT_SYMBOL(adm_set_pp_params); + +int adm_pack_and_set_one_pp_param(int port_id, int copp_idx, + struct param_hdr_v3 param_hdr, u8 *param_data) +{ + u8 *packed_data = NULL; + u32 total_size = 0; + int ret = 0; + + total_size = sizeof(union param_hdrs) + param_hdr.param_size; + packed_data = kzalloc(total_size, GFP_KERNEL); + if (!packed_data) + return -ENOMEM; + + ret = q6common_pack_pp_params(packed_data, ¶m_hdr, param_data, + &total_size); + if (ret) { + pr_err("%s: Failed to pack parameter data, error %d\n", + __func__, ret); + goto done; + } + + ret = adm_set_pp_params(port_id, copp_idx, NULL, packed_data, + total_size); + if (ret) + pr_err("%s: Failed to set parameter data, error %d\n", __func__, + ret); +done: + kfree(packed_data); + return ret; +} +EXPORT_SYMBOL(adm_pack_and_set_one_pp_param); + +/* + * Only one parameter can be requested at a time. Therefore, packing and sending + * the request can be handled locally. + */ +int adm_get_pp_params(int port_id, int copp_idx, uint32_t client_id, + struct mem_mapping_hdr *mem_hdr, + struct param_hdr_v3 *param_hdr, u8 *returned_param_data) +{ + struct adm_cmd_get_pp_params adm_get_params; + int total_size = 0; + int get_param_array_sz = ARRAY_SIZE(adm_get_parameters); + int returned_param_size = 0; + int returned_param_size_in_bytes = 0; + int port_idx = 0; + int idx = 0; + atomic_t *copp_stat = NULL; + int ret = 0; + + if (param_hdr == NULL) { + pr_err("%s: Received NULL pointer for parameter header\n", + __func__); + return -EINVAL; + } + + port_id = afe_convert_virtual_to_portid(port_id); + port_idx = adm_validate_and_get_port_index(port_id); + if (port_idx < 0 || port_idx >= AFE_MAX_PORTS) { + pr_err("%s: Invalid port_idx 0x%x\n", __func__, port_idx); + return -EINVAL; + } + if (copp_idx < 0 || copp_idx >= MAX_COPPS_PER_PORT) { + pr_err("%s: Invalid copp_idx 0x%x\n", __func__, copp_idx); + return -EINVAL; + } + + memset(&adm_get_params, 0, sizeof(adm_get_params)); + + if (mem_hdr != NULL) + adm_get_params.mem_hdr = *mem_hdr; + + q6common_pack_pp_params((u8 *) &adm_get_params.param_hdr, param_hdr, + NULL, &total_size); + + /* Pack APR header after filling body so total_size has correct value */ + adm_get_params.apr_hdr.hdr_field = + APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + adm_get_params.apr_hdr.pkt_size = sizeof(adm_get_params); + adm_get_params.apr_hdr.src_svc = APR_SVC_ADM; + adm_get_params.apr_hdr.src_domain = APR_DOMAIN_APPS; + adm_get_params.apr_hdr.src_port = port_id; + adm_get_params.apr_hdr.dest_svc = APR_SVC_ADM; + adm_get_params.apr_hdr.dest_domain = APR_DOMAIN_ADSP; + adm_get_params.apr_hdr.dest_port = + atomic_read(&this_adm.copp.id[port_idx][copp_idx]); + adm_get_params.apr_hdr.token = + port_idx << 16 | client_id << 8 | copp_idx; + + if (q6common_is_instance_id_supported()) + adm_get_params.apr_hdr.opcode = ADM_CMD_GET_PP_PARAMS_V6; + else + adm_get_params.apr_hdr.opcode = ADM_CMD_GET_PP_PARAMS_V5; + + copp_stat = &this_adm.copp.stat[port_idx][copp_idx]; + atomic_set(copp_stat, -1); + + ret = apr_send_pkt(this_adm.apr, (uint32_t *) &adm_get_params); + if (ret < 0) { + pr_err("%s: Get params APR send failed port = 0x%x ret %d\n", + __func__, port_id, ret); + ret = -EINVAL; + goto done; + } + ret = wait_event_timeout(this_adm.copp.wait[port_idx][copp_idx], + atomic_read(copp_stat) >= 0, + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: Get params timed out port = 0x%x\n", __func__, + port_id); + ret = -ETIMEDOUT; + goto done; + } + if (atomic_read(copp_stat) > 0) { + pr_err("%s: DSP returned error[%s]\n", __func__, + adsp_err_get_err_str(atomic_read(copp_stat))); + ret = adsp_err_get_lnx_err_code(atomic_read(copp_stat)); + goto done; + } + + ret = 0; + + /* Copy data to caller if sent in band */ + if (!returned_param_data) { + pr_debug("%s: Received NULL pointer for param destination, not copying payload\n", + __func__); + return 0; + } + + idx = ADM_GET_PARAMETER_LENGTH * copp_idx; + returned_param_size = adm_get_parameters[idx]; + if (returned_param_size < 0 || + returned_param_size + idx + 1 > get_param_array_sz) { + pr_err("%s: Invalid parameter size %d\n", __func__, + returned_param_size); + return -EINVAL; + } + + returned_param_size_in_bytes = returned_param_size * sizeof(uint32_t); + if (param_hdr->param_size < returned_param_size_in_bytes) { + pr_err("%s: Provided buffer is not big enough, provided buffer size(%d) size needed(%d)\n", + __func__, param_hdr->param_size, + returned_param_size_in_bytes); + return -EINVAL; + } + + memcpy(returned_param_data, &adm_get_parameters[idx + 1], + returned_param_size_in_bytes); +done: + return ret; +} +EXPORT_SYMBOL(adm_get_pp_params); + +int adm_get_pp_topo_module_list_v2(int port_id, int copp_idx, + int32_t param_length, + int32_t *returned_params) +{ + struct adm_cmd_get_pp_topo_module_list adm_get_module_list; + bool iid_supported = q6common_is_instance_id_supported(); + int *topo_list; + int num_modules = 0; + int list_size = 0; + int port_idx, idx; + int i = 0; + atomic_t *copp_stat = NULL; + int ret = 0; + + pr_debug("%s : port_id %x", __func__, port_id); + port_id = afe_convert_virtual_to_portid(port_id); + port_idx = adm_validate_and_get_port_index(port_id); + if (port_idx < 0) { + pr_err("%s: Invalid port_id 0x%x\n", __func__, port_id); + return -EINVAL; + } + + if (copp_idx < 0 || copp_idx >= MAX_COPPS_PER_PORT) { + pr_err("%s: Invalid copp_num: %d\n", __func__, copp_idx); + return -EINVAL; + } + + memset(&adm_get_module_list, 0, sizeof(adm_get_module_list)); + + adm_get_module_list.apr_hdr.pkt_size = sizeof(adm_get_module_list); + adm_get_module_list.apr_hdr.src_svc = APR_SVC_ADM; + adm_get_module_list.apr_hdr.src_domain = APR_DOMAIN_APPS; + adm_get_module_list.apr_hdr.src_port = port_id; + adm_get_module_list.apr_hdr.dest_svc = APR_SVC_ADM; + adm_get_module_list.apr_hdr.dest_domain = APR_DOMAIN_ADSP; + adm_get_module_list.apr_hdr.dest_port = + atomic_read(&this_adm.copp.id[port_idx][copp_idx]); + adm_get_module_list.apr_hdr.token = port_idx << 16 | copp_idx; + /* + * Out of band functionality is not currently utilized. + * Assume in band. + */ + if (iid_supported) { + adm_get_module_list.apr_hdr.opcode = + ADM_CMD_GET_PP_TOPO_MODULE_LIST_V2; + adm_get_module_list.param_max_size = param_length; + } else { + adm_get_module_list.apr_hdr.opcode = + ADM_CMD_GET_PP_TOPO_MODULE_LIST; + + if (param_length > U16_MAX) { + pr_err("%s: Invalid param length for V1 %d\n", __func__, + param_length); + return -EINVAL; + } + adm_get_module_list.param_max_size = param_length << 16; + } + + copp_stat = &this_adm.copp.stat[port_idx][copp_idx]; + atomic_set(copp_stat, -1); + ret = apr_send_pkt(this_adm.apr, (uint32_t *) &adm_get_module_list); + if (ret < 0) { + pr_err("%s: APR send pkt failed for port_id: 0x%x failed ret %d\n", + __func__, port_id, ret); + ret = -EINVAL; + goto done; + } + ret = wait_event_timeout(this_adm.copp.wait[port_idx][copp_idx], + atomic_read(copp_stat) >= 0, + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: Timeout for port_id: 0x%x\n", __func__, port_id); + ret = -ETIMEDOUT; + goto done; + } + if (atomic_read(copp_stat) > 0) { + pr_err("%s: DSP returned error[%s]\n", __func__, + adsp_err_get_err_str(atomic_read(copp_stat))); + ret = adsp_err_get_lnx_err_code(atomic_read(copp_stat)); + goto done; + } + + ret = 0; + + if (returned_params) { + /* + * When processing ADM_CMDRSP_GET_PP_TOPO_MODULE_LIST IID is + * added since it is not present. Therefore, there is no need to + * do anything different if IID is not supported here as it is + * already taken care of. + */ + idx = ADM_GET_TOPO_MODULE_INSTANCE_LIST_LENGTH * copp_idx; + num_modules = adm_module_topo_list[idx]; + if (num_modules < 0 || num_modules > MAX_MODULES_IN_TOPO) { + pr_err("%s: Invalid number of modules returned %d\n", + __func__, num_modules); + return -EINVAL; + } + + list_size = num_modules * sizeof(struct module_instance_info); + if (param_length < list_size) { + pr_err("%s: Provided buffer not big enough to hold module-instance list, provided size %d, needed size %d\n", + __func__, param_length, list_size); + return -EINVAL; + } + + topo_list = (int32_t *) (&adm_module_topo_list[idx]); + memcpy(returned_params, topo_list, list_size); + for (i = 1; i <= num_modules; i += 2) { + pr_debug("module = 0x%x instance = 0x%x\n", + returned_params[i], returned_params[i + 1]); + } + } +done: + return ret; +} +EXPORT_SYMBOL(adm_get_pp_topo_module_list_v2); + +static void adm_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); +} + +/** + * adm_set_multi_ch_map - + * Update multi channel map info + * + * @channel_map: pointer with channel map info + * @path: direction or ADM path type + * + * Returns 0 on success or error on failure + */ +int adm_set_multi_ch_map(char *channel_map, int path) +{ + int idx; + + if (path == ADM_PATH_PLAYBACK) { + idx = ADM_MCH_MAP_IDX_PLAYBACK; + } else if (path == ADM_PATH_LIVE_REC) { + idx = ADM_MCH_MAP_IDX_REC; + } else { + pr_err("%s: invalid attempt to set path %d\n", __func__, path); + return -EINVAL; + } + + memcpy(multi_ch_maps[idx].channel_mapping, channel_map, + PCM_FORMAT_MAX_NUM_CHANNEL_V8); + multi_ch_maps[idx].set_channel_map = true; + + return 0; +} +EXPORT_SYMBOL(adm_set_multi_ch_map); + +/** + * adm_get_multi_ch_map - + * Retrieves multi channel map info + * + * @channel_map: pointer to be updated with channel map + * @path: direction or ADM path type + * + * Returns 0 on success or error on failure + */ +int adm_get_multi_ch_map(char *channel_map, int path) +{ + int idx; + + if (path == ADM_PATH_PLAYBACK) { + idx = ADM_MCH_MAP_IDX_PLAYBACK; + } else if (path == ADM_PATH_LIVE_REC) { + idx = ADM_MCH_MAP_IDX_REC; + } else { + pr_err("%s: invalid attempt to get path %d\n", __func__, path); + return -EINVAL; + } + + if (multi_ch_maps[idx].set_channel_map) { + memcpy(channel_map, multi_ch_maps[idx].channel_mapping, + PCM_FORMAT_MAX_NUM_CHANNEL_V8); + } + + return 0; +} +EXPORT_SYMBOL(adm_get_multi_ch_map); + +static int adm_process_get_param_response(u32 opcode, u32 idx, u32 *payload, + u32 payload_size) +{ + struct adm_cmd_rsp_get_pp_params_v5 *v5_rsp = NULL; + struct adm_cmd_rsp_get_pp_params_v6 *v6_rsp = NULL; + u32 *param_data = NULL; + int data_size = 0; + int struct_size = 0; + + if (payload == NULL) { + pr_err("%s: Payload is NULL\n", __func__); + return -EINVAL; + } + + switch (opcode) { + case ADM_CMDRSP_GET_PP_PARAMS_V5: + struct_size = sizeof(struct adm_cmd_rsp_get_pp_params_v5); + if (payload_size < struct_size) { + pr_err("%s: payload size %d < expected size %d\n", + __func__, payload_size, struct_size); + break; + } + v5_rsp = (struct adm_cmd_rsp_get_pp_params_v5 *) payload; + data_size = v5_rsp->param_hdr.param_size; + param_data = v5_rsp->param_data; + break; + case ADM_CMDRSP_GET_PP_PARAMS_V6: + struct_size = sizeof(struct adm_cmd_rsp_get_pp_params_v6); + if (payload_size < struct_size) { + pr_err("%s: payload size %d < expected size %d\n", + __func__, payload_size, struct_size); + break; + } + v6_rsp = (struct adm_cmd_rsp_get_pp_params_v6 *) payload; + data_size = v6_rsp->param_hdr.param_size; + param_data = v6_rsp->param_data; + break; + default: + pr_err("%s: Invalid opcode %d\n", __func__, opcode); + return -EINVAL; + } + + /* + * Just store the returned parameter data, not the header. The calling + * function is expected to know what it asked for. Therefore, there is + * no difference between V5 and V6. + */ + if ((payload_size >= struct_size + data_size) && + (ARRAY_SIZE(adm_get_parameters) > idx) && + (ARRAY_SIZE(adm_get_parameters) > idx + 1 + data_size)) { + pr_debug("%s: Received parameter data in band\n", + __func__); + /* + * data_size is expressed in number of bytes, store in number of + * ints + */ + adm_get_parameters[idx] = + data_size / sizeof(*adm_get_parameters); + pr_debug("%s: GET_PP PARAM: received parameter length: 0x%x\n", + __func__, adm_get_parameters[idx]); + /* store params after param_size */ + memcpy(&adm_get_parameters[idx + 1], param_data, data_size); + } else if (payload_size == sizeof(uint32_t)) { + adm_get_parameters[idx] = -1; + pr_debug("%s: Out of band case, setting size to %d\n", + __func__, adm_get_parameters[idx]); + } else { + pr_err("%s: Invalid parameter combination, payload_size %d, idx %d\n", + __func__, payload_size, idx); + return -EINVAL; + } + return 0; +} + +static int adm_process_get_topo_list_response(u32 opcode, int copp_idx, + u32 num_modules, u32 *payload, + u32 payload_size) +{ + u32 *fill_list = NULL; + int idx = 0; + int i = 0; + int j = 0; + + if (payload == NULL) { + pr_err("%s: Payload is NULL\n", __func__); + return -EINVAL; + } else if (copp_idx < 0 || copp_idx >= MAX_COPPS_PER_PORT) { + pr_err("%s: Invalid COPP index %d\n", __func__, copp_idx); + return -EINVAL; + } + + idx = ADM_GET_TOPO_MODULE_INSTANCE_LIST_LENGTH * copp_idx; + fill_list = adm_module_topo_list + idx; + *fill_list++ = num_modules; + for (i = 0; i < num_modules; i++) { + if (j > payload_size / sizeof(u32)) { + pr_err("%s: Invalid number of modules specified %d\n", + __func__, num_modules); + return -EINVAL; + } + + /* store module ID */ + *fill_list++ = payload[j]; + j++; + + switch (opcode) { + case ADM_CMDRSP_GET_PP_TOPO_MODULE_LIST_V2: + /* store instance ID */ + *fill_list++ = payload[j]; + j++; + break; + case ADM_CMDRSP_GET_PP_TOPO_MODULE_LIST: + /* Insert IID 0 when repacking */ + *fill_list++ = INSTANCE_ID_0; + break; + default: + pr_err("%s: Invalid opcode %d\n", __func__, opcode); + return -EINVAL; + } + } + + return 0; +} + +static void adm_reset_data(void) +{ + int i, j; + + apr_reset(this_adm.apr); + for (i = 0; i < AFE_MAX_PORTS; i++) { + for (j = 0; j < MAX_COPPS_PER_PORT; j++) { + atomic_set(&this_adm.copp.id[i][j], + RESET_COPP_ID); + atomic_set(&this_adm.copp.cnt[i][j], 0); + atomic_set( + &this_adm.copp.topology[i][j], 0); + atomic_set(&this_adm.copp.mode[i][j], + 0); + atomic_set(&this_adm.copp.stat[i][j], + 0); + atomic_set(&this_adm.copp.rate[i][j], + 0); + atomic_set( + &this_adm.copp.channels[i][j], + 0); + atomic_set( + &this_adm.copp.bit_width[i][j], 0); + atomic_set( + &this_adm.copp.app_type[i][j], 0); + atomic_set( + &this_adm.copp.acdb_id[i][j], 0); + this_adm.copp.adm_status[i][j] = + ADM_STATUS_CALIBRATION_REQUIRED; + } + } + this_adm.apr = NULL; + cal_utils_clear_cal_block_q6maps(ADM_MAX_CAL_TYPES, + this_adm.cal_data); + mutex_lock(&this_adm.cal_data + [ADM_CUSTOM_TOP_CAL]->lock); + this_adm.set_custom_topology = 1; + mutex_unlock(&this_adm.cal_data[ + ADM_CUSTOM_TOP_CAL]->lock); + rtac_clear_mapping(ADM_RTAC_CAL); + /* + * Free the ION memory and clear the map handles + * for Source Tracking + */ + if (this_adm.sourceTrackingData.memmap.paddr != 0) { + msm_audio_ion_free( + this_adm.sourceTrackingData.dma_buf); + this_adm.sourceTrackingData.dma_buf = NULL; + this_adm.sourceTrackingData.memmap.size = 0; + this_adm.sourceTrackingData.memmap.kvaddr = + NULL; + this_adm.sourceTrackingData.memmap.paddr = 0; + this_adm.sourceTrackingData.apr_cmd_status = -1; + atomic_set(&this_adm.mem_map_handles[ + ADM_MEM_MAP_INDEX_SOURCE_TRACKING], 0); + } +} + +static int32_t adm_callback(struct apr_client_data *data, void *priv) +{ + uint32_t *payload; + int port_idx, copp_idx, idx, client_id; + int num_modules; + int ret; + + if (data == NULL) { + pr_err("%s: data parameter is null\n", __func__); + return -EINVAL; + } + + payload = data->payload; + + if (data->opcode == RESET_EVENTS) { + pr_debug("%s: Reset event is received: %d %d apr[%pK]\n", + __func__, + data->reset_event, data->reset_proc, this_adm.apr); + if (this_adm.apr) + adm_reset_data(); + return 0; + } + + adm_callback_debug_print(data); + if (data->payload_size) { + copp_idx = (data->token) & 0XFF; + port_idx = ((data->token) >> 16) & 0xFF; + client_id = ((data->token) >> 8) & 0xFF; + if (port_idx < 0 || port_idx >= AFE_MAX_PORTS) { + pr_err("%s: Invalid port idx %d token %d\n", + __func__, port_idx, data->token); + return 0; + } + if (copp_idx < 0 || copp_idx >= MAX_COPPS_PER_PORT) { + pr_err("%s: Invalid copp idx %d token %d\n", + __func__, copp_idx, data->token); + return 0; + } + if (client_id < 0 || client_id >= ADM_CLIENT_ID_MAX) { + pr_err("%s: Invalid client id %d\n", __func__, + client_id); + return 0; + } + if (data->opcode == APR_BASIC_RSP_RESULT) { + if (data->payload_size < (2 * sizeof(uint32_t))) { + pr_err("%s: Invalid payload size %d\n", __func__, + data->payload_size); + return 0; + } + pr_debug("%s: APR_BASIC_RSP_RESULT id 0x%x\n", + __func__, payload[0]); + if (payload[1] != 0) { + pr_err("%s: cmd = 0x%x returned error = 0x%x\n", + __func__, payload[0], payload[1]); + } + switch (payload[0]) { + case ADM_CMD_SET_PP_PARAMS_V5: + case ADM_CMD_SET_PP_PARAMS_V6: + pr_debug("%s: ADM_CMD_SET_PP_PARAMS\n", + __func__); + if (client_id == ADM_CLIENT_ID_SOURCE_TRACKING) + this_adm.sourceTrackingData. + apr_cmd_status = payload[1]; + else if (rtac_make_adm_callback(payload, + data->payload_size)) + break; + /* + * if soft volume is called and already + * interrupted break out of the sequence here + */ + case ADM_CMD_DEVICE_OPEN_V5: + case ADM_CMD_DEVICE_CLOSE_V5: + case ADM_CMD_DEVICE_OPEN_V6: + case ADM_CMD_DEVICE_OPEN_V8: + pr_debug("%s: Basic callback received, wake up.\n", + __func__); + atomic_set(&this_adm.copp.stat[port_idx] + [copp_idx], payload[1]); + wake_up( + &this_adm.copp.wait[port_idx][copp_idx]); + break; + case ADM_CMD_ADD_TOPOLOGIES: + pr_debug("%s: callback received, ADM_CMD_ADD_TOPOLOGIES.\n", + __func__); + atomic_set(&this_adm.adm_stat, payload[1]); + wake_up(&this_adm.adm_wait); + break; + case ADM_CMD_MATRIX_MAP_ROUTINGS_V5: + case ADM_CMD_STREAM_DEVICE_MAP_ROUTINGS_V5: + pr_debug("%s: Basic callback received, wake up.\n", + __func__); + atomic_set(&this_adm.matrix_map_stat, + payload[1]); + wake_up(&this_adm.matrix_map_wait); + break; + case ADM_CMD_SHARED_MEM_UNMAP_REGIONS: + pr_debug("%s: ADM_CMD_SHARED_MEM_UNMAP_REGIONS\n", + __func__); + atomic_set(&this_adm.adm_stat, payload[1]); + wake_up(&this_adm.adm_wait); + break; + case ADM_CMD_SHARED_MEM_MAP_REGIONS: + pr_debug("%s: ADM_CMD_SHARED_MEM_MAP_REGIONS\n", + __func__); + /* Should only come here if there is an APR */ + /* error or malformed APR packet. Otherwise */ + /* response will be returned as */ + if (payload[1] != 0) { + pr_err("%s: ADM map error, resuming\n", + __func__); + atomic_set(&this_adm.adm_stat, + payload[1]); + wake_up(&this_adm.adm_wait); + } + break; + case ADM_CMD_GET_PP_PARAMS_V5: + case ADM_CMD_GET_PP_PARAMS_V6: + pr_debug("%s: ADM_CMD_GET_PP_PARAMS\n", + __func__); + /* Should only come here if there is an APR */ + /* error or malformed APR packet. Otherwise */ + /* response will be returned as */ + /* ADM_CMDRSP_GET_PP_PARAMS_V5 */ + if (client_id == + ADM_CLIENT_ID_SOURCE_TRACKING) { + this_adm.sourceTrackingData. + apr_cmd_status = payload[1]; + if (payload[1] != 0) + pr_err("%s: ADM get param error = %d\n", + __func__, payload[1]); + + atomic_set(&this_adm.copp.stat + [port_idx][copp_idx], + payload[1]); + wake_up(&this_adm.copp.wait + [port_idx][copp_idx]); + } else { + if (payload[1] != 0) { + pr_err("%s: ADM get param error = %d, resuming\n", + __func__, payload[1]); + + rtac_make_adm_callback(payload, + data->payload_size); + } + } + break; + case ADM_CMD_SET_PSPD_MTMX_STRTR_PARAMS_V5: + case ADM_CMD_SET_PSPD_MTMX_STRTR_PARAMS_V6: + pr_debug("%s:callback received PSPD MTMX, wake up\n", + __func__); + atomic_set(&this_adm.copp.stat[port_idx] + [copp_idx], payload[1]); + wake_up( + &this_adm.copp.wait[port_idx][copp_idx]); + break; + case ADM_CMD_GET_PP_TOPO_MODULE_LIST: + case ADM_CMD_GET_PP_TOPO_MODULE_LIST_V2: + pr_debug("%s:ADM_CMD_GET_PP_TOPO_MODULE_LIST\n", + __func__); + if (payload[1] != 0) + pr_err("%s: ADM get topo list error = %d\n", + __func__, payload[1]); + break; + default: + pr_err("%s: Unknown Cmd: 0x%x\n", __func__, + payload[0]); + break; + } + return 0; + } + + switch (data->opcode) { + case ADM_CMDRSP_DEVICE_OPEN_V5: + case ADM_CMDRSP_DEVICE_OPEN_V6: + case ADM_CMDRSP_DEVICE_OPEN_V8: { + struct adm_cmd_rsp_device_open_v5 *open = NULL; + if (data->payload_size < + sizeof(struct adm_cmd_rsp_device_open_v5)) { + pr_err("%s: Invalid payload size %d\n", __func__, + data->payload_size); + return 0; + } + open = (struct adm_cmd_rsp_device_open_v5 *)data->payload; + if (open->copp_id == INVALID_COPP_ID) { + pr_err("%s: invalid coppid rxed %d\n", + __func__, open->copp_id); + atomic_set(&this_adm.copp.stat[port_idx] + [copp_idx], ADSP_EBADPARAM); + wake_up( + &this_adm.copp.wait[port_idx][copp_idx]); + break; + } + atomic_set(&this_adm.copp.stat + [port_idx][copp_idx], payload[0]); + atomic_set(&this_adm.copp.id[port_idx][copp_idx], + open->copp_id); + pr_debug("%s: coppid rxed=%d\n", __func__, + open->copp_id); + wake_up(&this_adm.copp.wait[port_idx][copp_idx]); + } + break; + case ADM_CMDRSP_GET_PP_PARAMS_V5: + case ADM_CMDRSP_GET_PP_PARAMS_V6: + pr_debug("%s: ADM_CMDRSP_GET_PP_PARAMS\n", __func__); + if (client_id == ADM_CLIENT_ID_SOURCE_TRACKING) + this_adm.sourceTrackingData.apr_cmd_status = + payload[0]; + else if (rtac_make_adm_callback(payload, + data->payload_size)) + break; + + idx = ADM_GET_PARAMETER_LENGTH * copp_idx; + if (payload[0] == 0 && data->payload_size > 0) { + ret = adm_process_get_param_response( + data->opcode, idx, payload, + data->payload_size); + if (ret) + pr_err("%s: Failed to process get param response, error %d\n", + __func__, ret); + } else { + adm_get_parameters[idx] = -1; + pr_err("%s: ADM_CMDRSP_GET_PP_PARAMS returned error 0x%x\n", + __func__, payload[0]); + } + atomic_set(&this_adm.copp.stat[port_idx][copp_idx], + payload[0]); + wake_up(&this_adm.copp.wait[port_idx][copp_idx]); + break; + case ADM_CMDRSP_GET_PP_TOPO_MODULE_LIST: + case ADM_CMDRSP_GET_PP_TOPO_MODULE_LIST_V2: + pr_debug("%s: ADM_CMDRSP_GET_PP_TOPO_MODULE_LIST\n", + __func__); + num_modules = payload[1]; + pr_debug("%s: Num modules %d\n", __func__, num_modules); + if (payload[0]) { + pr_err("%s: ADM_CMDRSP_GET_PP_TOPO_MODULE_LIST, error = %d\n", + __func__, payload[0]); + } else if (num_modules > MAX_MODULES_IN_TOPO) { + pr_err("%s: ADM_CMDRSP_GET_PP_TOPO_MODULE_LIST invalid num modules received, num modules = %d\n", + __func__, num_modules); + } else { + ret = adm_process_get_topo_list_response( + data->opcode, copp_idx, num_modules, + payload, data->payload_size); + if (ret) + pr_err("%s: Failed to process get topo modules list response, error %d\n", + __func__, ret); + } + atomic_set(&this_adm.copp.stat[port_idx][copp_idx], + payload[0]); + wake_up(&this_adm.copp.wait[port_idx][copp_idx]); + break; + case ADM_CMDRSP_SHARED_MEM_MAP_REGIONS: + pr_debug("%s: ADM_CMDRSP_SHARED_MEM_MAP_REGIONS\n", + __func__); + atomic_set(&this_adm.mem_map_handles[ + atomic_read(&this_adm.mem_map_index)], + *payload); + atomic_set(&this_adm.adm_stat, 0); + wake_up(&this_adm.adm_wait); + break; + default: + pr_err("%s: Unknown cmd:0x%x\n", __func__, + data->opcode); + break; + } + } + return 0; +} + +static int adm_memory_map_regions(phys_addr_t *buf_add, uint32_t mempool_id, + uint32_t *bufsz, uint32_t bufcnt) +{ + struct avs_cmd_shared_mem_map_regions *mmap_regions = NULL; + struct avs_shared_map_region_payload *mregions = NULL; + void *mmap_region_cmd = NULL; + void *payload = NULL; + int ret = 0; + int i = 0; + int cmd_size = 0; + + pr_debug("%s:\n", __func__); + if (this_adm.apr == NULL) { + this_adm.apr = apr_register("ADSP", "ADM", adm_callback, + 0xFFFFFFFF, &this_adm); + if (this_adm.apr == NULL) { + pr_err("%s: Unable to register ADM\n", __func__); + ret = -ENODEV; + return ret; + } + rtac_set_adm_handle(this_adm.apr); + } + + cmd_size = sizeof(struct avs_cmd_shared_mem_map_regions) + + sizeof(struct avs_shared_map_region_payload) + * bufcnt; + + mmap_region_cmd = kzalloc(cmd_size, GFP_KERNEL); + if (!mmap_region_cmd) + return -ENOMEM; + + mmap_regions = (struct avs_cmd_shared_mem_map_regions *)mmap_region_cmd; + mmap_regions->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + mmap_regions->hdr.pkt_size = cmd_size; + mmap_regions->hdr.src_port = 0; + + mmap_regions->hdr.dest_port = 0; + mmap_regions->hdr.token = 0; + mmap_regions->hdr.opcode = ADM_CMD_SHARED_MEM_MAP_REGIONS; + mmap_regions->mem_pool_id = ADSP_MEMORY_MAP_SHMEM8_4K_POOL & 0x00ff; + mmap_regions->num_regions = bufcnt & 0x00ff; + mmap_regions->property_flag = 0x00; + + pr_debug("%s: map_regions->num_regions = %d\n", __func__, + mmap_regions->num_regions); + payload = ((u8 *) mmap_region_cmd + + sizeof(struct avs_cmd_shared_mem_map_regions)); + mregions = (struct avs_shared_map_region_payload *)payload; + + for (i = 0; i < bufcnt; i++) { + mregions->shm_addr_lsw = lower_32_bits(buf_add[i]); + mregions->shm_addr_msw = + msm_audio_populate_upper_32_bits(buf_add[i]); + mregions->mem_size_bytes = bufsz[i]; + ++mregions; + } + + atomic_set(&this_adm.adm_stat, -1); + ret = apr_send_pkt(this_adm.apr, (uint32_t *) mmap_region_cmd); + if (ret < 0) { + pr_err("%s: mmap_regions op[0x%x]rc[%d]\n", __func__, + mmap_regions->hdr.opcode, ret); + ret = -EINVAL; + goto fail_cmd; + } + + ret = wait_event_timeout(this_adm.adm_wait, + atomic_read(&this_adm.adm_stat) >= 0, + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: timeout. waited for memory_map\n", __func__); + ret = -EINVAL; + goto fail_cmd; + } else if (atomic_read(&this_adm.adm_stat) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&this_adm.adm_stat))); + ret = adsp_err_get_lnx_err_code( + atomic_read(&this_adm.adm_stat)); + goto fail_cmd; + } +fail_cmd: + kfree(mmap_region_cmd); + return ret; +} + +static int adm_memory_unmap_regions(void) +{ + struct avs_cmd_shared_mem_unmap_regions unmap_regions; + int ret = 0; + + pr_debug("%s:\n", __func__); + if (this_adm.apr == NULL) { + pr_err("%s: APR handle NULL\n", __func__); + return -EINVAL; + } + + unmap_regions.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + unmap_regions.hdr.pkt_size = sizeof(unmap_regions); + unmap_regions.hdr.src_port = 0; + unmap_regions.hdr.dest_port = 0; + unmap_regions.hdr.token = 0; + unmap_regions.hdr.opcode = ADM_CMD_SHARED_MEM_UNMAP_REGIONS; + unmap_regions.mem_map_handle = atomic_read(&this_adm. + mem_map_handles[atomic_read(&this_adm.mem_map_index)]); + atomic_set(&this_adm.adm_stat, -1); + ret = apr_send_pkt(this_adm.apr, (uint32_t *) &unmap_regions); + if (ret < 0) { + pr_err("%s: mmap_regions op[0x%x]rc[%d]\n", __func__, + unmap_regions.hdr.opcode, ret); + ret = -EINVAL; + goto fail_cmd; + } + + ret = wait_event_timeout(this_adm.adm_wait, + atomic_read(&this_adm.adm_stat) >= 0, + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: timeout. waited for memory_unmap\n", + __func__); + ret = -EINVAL; + goto fail_cmd; + } else if (atomic_read(&this_adm.adm_stat) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&this_adm.adm_stat))); + ret = adsp_err_get_lnx_err_code( + atomic_read(&this_adm.adm_stat)); + goto fail_cmd; + } else { + pr_debug("%s: Unmap handle 0x%x succeeded\n", __func__, + unmap_regions.mem_map_handle); + } +fail_cmd: + return ret; +} + +static int remap_cal_data(struct cal_block_data *cal_block, int cal_index) +{ + int ret = 0; + + if (cal_block->map_data.dma_buf == NULL) { + pr_err("%s: No ION allocation for cal index %d!\n", + __func__, cal_index); + ret = -EINVAL; + goto done; + } + + if ((cal_block->map_data.map_size > 0) && + (cal_block->map_data.q6map_handle == 0)) { + atomic_set(&this_adm.mem_map_index, cal_index); + ret = adm_memory_map_regions(&cal_block->cal_data.paddr, 0, + (uint32_t *)&cal_block->map_data.map_size, 1); + if (ret < 0) { + pr_err("%s: ADM mmap did not work! size = %zd ret %d\n", + __func__, + cal_block->map_data.map_size, ret); + pr_debug("%s: ADM mmap did not work! addr = 0x%pK, size = %zd ret %d\n", + __func__, + &cal_block->cal_data.paddr, + cal_block->map_data.map_size, ret); + goto done; + } + cal_block->map_data.q6map_handle = atomic_read(&this_adm. + mem_map_handles[cal_index]); + } +done: + return ret; +} + +static void send_adm_custom_topology(void) +{ + struct cal_block_data *cal_block = NULL; + struct cmd_set_topologies adm_top; + int cal_index = ADM_CUSTOM_TOP_CAL; + int result; + + if (this_adm.cal_data[cal_index] == NULL) + goto done; + + mutex_lock(&this_adm.cal_data[cal_index]->lock); + if (!this_adm.set_custom_topology) + goto unlock; + this_adm.set_custom_topology = 0; + + cal_block = cal_utils_get_only_cal_block(this_adm.cal_data[cal_index]); + if (cal_block == NULL || cal_utils_is_cal_stale(cal_block)) + goto unlock; + + pr_debug("%s: Sending cal_index %d\n", __func__, cal_index); + + result = remap_cal_data(cal_block, cal_index); + if (result) { + pr_err("%s: Remap_cal_data failed for cal %d!\n", + __func__, cal_index); + goto unlock; + } + atomic_set(&this_adm.mem_map_index, cal_index); + atomic_set(&this_adm.mem_map_handles[cal_index], + cal_block->map_data.q6map_handle); + + if (cal_block->cal_data.size == 0) { + pr_debug("%s: No ADM cal to send\n", __func__); + goto unlock; + } + + adm_top.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(20), APR_PKT_VER); + adm_top.hdr.pkt_size = sizeof(adm_top); + adm_top.hdr.src_svc = APR_SVC_ADM; + adm_top.hdr.src_domain = APR_DOMAIN_APPS; + adm_top.hdr.src_port = 0; + adm_top.hdr.dest_svc = APR_SVC_ADM; + adm_top.hdr.dest_domain = APR_DOMAIN_ADSP; + adm_top.hdr.dest_port = 0; + adm_top.hdr.token = 0; + adm_top.hdr.opcode = ADM_CMD_ADD_TOPOLOGIES; + adm_top.payload_addr_lsw = lower_32_bits(cal_block->cal_data.paddr); + adm_top.payload_addr_msw = msm_audio_populate_upper_32_bits( + cal_block->cal_data.paddr); + adm_top.mem_map_handle = cal_block->map_data.q6map_handle; + adm_top.payload_size = cal_block->cal_data.size; + + atomic_set(&this_adm.adm_stat, -1); + pr_debug("%s: Sending ADM_CMD_ADD_TOPOLOGIES payload = 0x%pK, size = %d\n", + __func__, &cal_block->cal_data.paddr, + adm_top.payload_size); + result = apr_send_pkt(this_adm.apr, (uint32_t *)&adm_top); + if (result < 0) { + pr_err("%s: Set topologies failed payload size = %zd result %d\n", + __func__, cal_block->cal_data.size, result); + goto unlock; + } + /* Wait for the callback */ + result = wait_event_timeout(this_adm.adm_wait, + atomic_read(&this_adm.adm_stat) >= 0, + msecs_to_jiffies(TIMEOUT_MS)); + if (!result) { + pr_err("%s: Set topologies timed out payload size = %zd\n", + __func__, cal_block->cal_data.size); + goto unlock; + } else if (atomic_read(&this_adm.adm_stat) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&this_adm.adm_stat))); + result = adsp_err_get_lnx_err_code( + atomic_read(&this_adm.adm_stat)); + goto unlock; + } +unlock: + mutex_unlock(&this_adm.cal_data[cal_index]->lock); +done: + return; +} + +static int send_adm_cal_block(int port_id, int copp_idx, + struct cal_block_data *cal_block, int perf_mode) +{ + struct mem_mapping_hdr mem_hdr; + int payload_size = 0; + int port_idx = 0; + int topology = 0; + int result = 0; + + pr_debug("%s: Port id 0x%x,\n", __func__, port_id); + + if (!cal_block) { + pr_debug("%s: No ADM cal to send for port_id = 0x%x!\n", + __func__, port_id); + result = -EINVAL; + goto done; + } + if (cal_block->cal_data.size <= 0) { + pr_debug("%s: No ADM cal sent for port_id = 0x%x!\n", __func__, + port_id); + result = -EINVAL; + goto done; + } + + memset(&mem_hdr, 0, sizeof(mem_hdr)); + port_id = afe_convert_virtual_to_portid(port_id); + port_idx = adm_validate_and_get_port_index(port_id); + if (port_idx < 0 || port_idx >= AFE_MAX_PORTS) { + pr_err("%s: Invalid port_id 0x%x\n", __func__, port_id); + return -EINVAL; + } else if (copp_idx < 0 || copp_idx >= MAX_COPPS_PER_PORT) { + pr_err("%s: Invalid copp_idx 0x%x\n", __func__, copp_idx); + return -EINVAL; + } + + topology = atomic_read(&this_adm.copp.topology[port_idx][copp_idx]); + if (perf_mode == LEGACY_PCM_MODE && + topology == DS2_ADM_COPP_TOPOLOGY_ID) { + pr_err("%s: perf_mode %d, topology 0x%x\n", __func__, perf_mode, + topology); + goto done; + } + + mem_hdr.data_payload_addr_lsw = + lower_32_bits(cal_block->cal_data.paddr); + mem_hdr.data_payload_addr_msw = + msm_audio_populate_upper_32_bits(cal_block->cal_data.paddr); + mem_hdr.mem_map_handle = cal_block->map_data.q6map_handle; + payload_size = cal_block->cal_data.size; + + adm_set_pp_params(port_id, copp_idx, &mem_hdr, NULL, payload_size); + +done: + return result; +} + +static struct cal_block_data *adm_find_cal_by_path(int cal_index, int path) +{ + struct list_head *ptr, *next; + struct cal_block_data *cal_block = NULL; + struct audio_cal_info_audproc *audproc_cal_info = NULL; + struct audio_cal_info_audvol *audvol_cal_info = NULL; + + pr_debug("%s:\n", __func__); + + list_for_each_safe(ptr, next, + &this_adm.cal_data[cal_index]->cal_blocks) { + + cal_block = list_entry(ptr, + struct cal_block_data, list); + + if (cal_utils_is_cal_stale(cal_block)) + continue; + + if (cal_index == ADM_AUDPROC_CAL || + cal_index == ADM_LSM_AUDPROC_CAL || + cal_index == ADM_LSM_AUDPROC_PERSISTENT_CAL) { + audproc_cal_info = cal_block->cal_info; + if ((audproc_cal_info->path == path) && + (cal_block->cal_data.size > 0)) + return cal_block; + } else if (cal_index == ADM_AUDVOL_CAL) { + audvol_cal_info = cal_block->cal_info; + if ((audvol_cal_info->path == path) && + (cal_block->cal_data.size > 0)) + return cal_block; + } + } + pr_debug("%s: Can't find ADM cal for cal_index %d, path %d\n", + __func__, cal_index, path); + return NULL; +} + +static struct cal_block_data *adm_find_cal_by_app_type(int cal_index, int path, + int app_type) +{ + struct list_head *ptr, *next; + struct cal_block_data *cal_block = NULL; + struct audio_cal_info_audproc *audproc_cal_info = NULL; + struct audio_cal_info_audvol *audvol_cal_info = NULL; + + pr_debug("%s\n", __func__); + + list_for_each_safe(ptr, next, + &this_adm.cal_data[cal_index]->cal_blocks) { + + cal_block = list_entry(ptr, + struct cal_block_data, list); + + if (cal_utils_is_cal_stale(cal_block)) + continue; + + if (cal_index == ADM_AUDPROC_CAL || + cal_index == ADM_LSM_AUDPROC_CAL || + cal_index == ADM_LSM_AUDPROC_PERSISTENT_CAL) { + audproc_cal_info = cal_block->cal_info; + if ((audproc_cal_info->path == path) && + (audproc_cal_info->app_type == app_type) && + (cal_block->cal_data.size > 0)) + return cal_block; + } else if (cal_index == ADM_AUDVOL_CAL) { + audvol_cal_info = cal_block->cal_info; + if ((audvol_cal_info->path == path) && + (audvol_cal_info->app_type == app_type) && + (cal_block->cal_data.size > 0)) + return cal_block; + } + } + pr_debug("%s: Can't find ADM cali for cal_index %d, path %d, app %d, defaulting to search by path\n", + __func__, cal_index, path, app_type); + return adm_find_cal_by_path(cal_index, path); +} + + +static struct cal_block_data *adm_find_cal(int cal_index, int path, + int app_type, int acdb_id, + int sample_rate) +{ + struct list_head *ptr, *next; + struct cal_block_data *cal_block = NULL; + struct audio_cal_info_audproc *audproc_cal_info = NULL; + struct audio_cal_info_audvol *audvol_cal_info = NULL; + + pr_debug("%s:\n", __func__); + + list_for_each_safe(ptr, next, + &this_adm.cal_data[cal_index]->cal_blocks) { + + cal_block = list_entry(ptr, + struct cal_block_data, list); + if (cal_utils_is_cal_stale(cal_block)) + continue; + + if (cal_index == ADM_AUDPROC_CAL || + cal_index == ADM_LSM_AUDPROC_CAL || + cal_index == ADM_LSM_AUDPROC_PERSISTENT_CAL) { + audproc_cal_info = cal_block->cal_info; + if ((audproc_cal_info->path == path) && + (audproc_cal_info->app_type == app_type) && + (audproc_cal_info->acdb_id == acdb_id) && + (audproc_cal_info->sample_rate == sample_rate) && + (cal_block->cal_data.size > 0)) + return cal_block; + } else if (cal_index == ADM_AUDVOL_CAL) { + audvol_cal_info = cal_block->cal_info; + if ((audvol_cal_info->path == path) && + (audvol_cal_info->app_type == app_type) && + (audvol_cal_info->acdb_id == acdb_id) && + (cal_block->cal_data.size > 0)) + return cal_block; + } + } + pr_debug("%s: Can't find ADM cal for cal_index %d, path %d, app %d, acdb_id %d sample_rate %d defaulting to search by app type\n", + __func__, cal_index, path, app_type, acdb_id, sample_rate); + return adm_find_cal_by_app_type(cal_index, path, app_type); +} + +static int adm_remap_and_send_cal_block(int cal_index, int port_id, + int copp_idx, struct cal_block_data *cal_block, int perf_mode, + int app_type, int acdb_id, int sample_rate) +{ + int ret = 0; + + pr_debug("%s: Sending cal_index cal %d\n", __func__, cal_index); + ret = remap_cal_data(cal_block, cal_index); + if (ret) { + pr_err("%s: Remap_cal_data failed for cal %d!\n", + __func__, cal_index); + goto done; + } + ret = send_adm_cal_block(port_id, copp_idx, cal_block, perf_mode); + if (ret < 0) + pr_debug("%s: No cal sent for cal_index %d, port_id = 0x%x! ret %d sample_rate %d\n", + __func__, cal_index, port_id, ret, sample_rate); +done: + return ret; +} + +static void send_adm_cal_type(int cal_index, int path, int port_id, + int copp_idx, int perf_mode, int app_type, + int acdb_id, int sample_rate) +{ + struct cal_block_data *cal_block = NULL; + int ret; + + pr_debug("%s: cal index %d\n", __func__, cal_index); + + if (this_adm.cal_data[cal_index] == NULL) { + pr_debug("%s: cal_index %d not allocated!\n", + __func__, cal_index); + goto done; + } + + mutex_lock(&this_adm.cal_data[cal_index]->lock); + cal_block = adm_find_cal(cal_index, path, app_type, acdb_id, + sample_rate); + if (cal_block == NULL) + goto unlock; + + ret = adm_remap_and_send_cal_block(cal_index, port_id, copp_idx, + cal_block, perf_mode, app_type, acdb_id, sample_rate); + + cal_utils_mark_cal_used(cal_block); +unlock: + mutex_unlock(&this_adm.cal_data[cal_index]->lock); +done: + return; +} + +static int get_cal_path(int path) +{ + if (path == 0x1) + return RX_DEVICE; + else + return TX_DEVICE; +} + +static void send_adm_cal(int port_id, int copp_idx, int path, int perf_mode, + int app_type, int acdb_id, int sample_rate, + int passthr_mode) +{ + pr_debug("%s: port id 0x%x copp_idx %d\n", __func__, port_id, copp_idx); + + if (passthr_mode != LISTEN) { + send_adm_cal_type(ADM_AUDPROC_CAL, path, port_id, copp_idx, + perf_mode, app_type, acdb_id, sample_rate); + } else { + send_adm_cal_type(ADM_LSM_AUDPROC_CAL, path, port_id, copp_idx, + perf_mode, app_type, acdb_id, sample_rate); + + send_adm_cal_type(ADM_LSM_AUDPROC_PERSISTENT_CAL, path, + port_id, copp_idx, perf_mode, app_type, + acdb_id, sample_rate); + } + + send_adm_cal_type(ADM_AUDVOL_CAL, path, port_id, copp_idx, perf_mode, + app_type, acdb_id, sample_rate); +} + +/** + * adm_connect_afe_port - + * command to send ADM connect AFE port + * + * @mode: value of mode for ADM connect AFE + * @session_id: session active to connect + * @port_id: Port ID number + * + * Returns 0 on success or error on failure + */ +int adm_connect_afe_port(int mode, int session_id, int port_id) +{ + struct adm_cmd_connect_afe_port_v5 cmd; + int ret = 0; + int port_idx, copp_idx = 0; + + pr_debug("%s: port_id: 0x%x session id:%d mode:%d\n", __func__, + port_id, session_id, mode); + + port_id = afe_convert_virtual_to_portid(port_id); + port_idx = adm_validate_and_get_port_index(port_id); + if (port_idx < 0) { + pr_err("%s: Invalid port_id 0x%x\n", __func__, port_id); + return -EINVAL; + } + + if (this_adm.apr == NULL) { + this_adm.apr = apr_register("ADSP", "ADM", adm_callback, + 0xFFFFFFFF, &this_adm); + if (this_adm.apr == NULL) { + pr_err("%s: Unable to register ADM\n", __func__); + ret = -ENODEV; + return ret; + } + rtac_set_adm_handle(this_adm.apr); + } + pr_debug("%s: Port ID 0x%x, index %d\n", __func__, port_id, port_idx); + + cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + cmd.hdr.pkt_size = sizeof(cmd); + cmd.hdr.src_svc = APR_SVC_ADM; + cmd.hdr.src_domain = APR_DOMAIN_APPS; + cmd.hdr.src_port = port_id; + cmd.hdr.dest_svc = APR_SVC_ADM; + cmd.hdr.dest_domain = APR_DOMAIN_ADSP; + cmd.hdr.dest_port = 0; /* Ignored */ + cmd.hdr.token = port_idx << 16 | copp_idx; + cmd.hdr.opcode = ADM_CMD_CONNECT_AFE_PORT_V5; + + cmd.mode = mode; + cmd.session_id = session_id; + cmd.afe_port_id = port_id; + + atomic_set(&this_adm.copp.stat[port_idx][copp_idx], -1); + ret = apr_send_pkt(this_adm.apr, (uint32_t *)&cmd); + if (ret < 0) { + pr_err("%s: ADM enable for port_id: 0x%x failed ret %d\n", + __func__, port_id, ret); + ret = -EINVAL; + goto fail_cmd; + } + /* Wait for the callback with copp id */ + ret = wait_event_timeout(this_adm.copp.wait[port_idx][copp_idx], + atomic_read(&this_adm.copp.stat[port_idx][copp_idx]) >= 0, + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: ADM connect timedout for port_id: 0x%x\n", + __func__, port_id); + ret = -EINVAL; + goto fail_cmd; + } else if (atomic_read(&this_adm.copp.stat + [port_idx][copp_idx]) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&this_adm.copp.stat + [port_idx][copp_idx]))); + ret = adsp_err_get_lnx_err_code( + atomic_read(&this_adm.copp.stat + [port_idx][copp_idx])); + goto fail_cmd; + } + atomic_inc(&this_adm.copp.cnt[port_idx][copp_idx]); + return 0; + +fail_cmd: + + return ret; +} +EXPORT_SYMBOL(adm_connect_afe_port); + +int adm_arrange_mch_map(struct adm_cmd_device_open_v5 *open, int path, + int channel_mode) +{ + int rc = 0, idx; + + pr_debug("%s: channel mode %d", __func__, channel_mode); + + memset(open->dev_channel_mapping, 0, PCM_FORMAT_MAX_NUM_CHANNEL); + switch (path) { + case ADM_PATH_PLAYBACK: + idx = ADM_MCH_MAP_IDX_PLAYBACK; + break; + case ADM_PATH_LIVE_REC: + case ADM_PATH_NONLIVE_REC: + idx = ADM_MCH_MAP_IDX_REC; + break; + default: + goto non_mch_path; + }; + if ((open->dev_num_channel > 2) && multi_ch_maps[idx].set_channel_map) { + memcpy(open->dev_channel_mapping, + multi_ch_maps[idx].channel_mapping, + PCM_FORMAT_MAX_NUM_CHANNEL); + } else { + if (channel_mode == 1) { + open->dev_channel_mapping[0] = PCM_CHANNEL_FC; + } else if (channel_mode == 2) { + open->dev_channel_mapping[0] = PCM_CHANNEL_FL; + open->dev_channel_mapping[1] = PCM_CHANNEL_FR; + } else if (channel_mode == 3) { + open->dev_channel_mapping[0] = PCM_CHANNEL_FL; + open->dev_channel_mapping[1] = PCM_CHANNEL_FR; + open->dev_channel_mapping[2] = PCM_CHANNEL_FC; + } else if (channel_mode == 4) { + open->dev_channel_mapping[0] = PCM_CHANNEL_FL; + open->dev_channel_mapping[1] = PCM_CHANNEL_FR; + open->dev_channel_mapping[2] = PCM_CHANNEL_LS; + open->dev_channel_mapping[3] = PCM_CHANNEL_RS; + } else if (channel_mode == 5) { + open->dev_channel_mapping[0] = PCM_CHANNEL_FL; + open->dev_channel_mapping[1] = PCM_CHANNEL_FR; + open->dev_channel_mapping[2] = PCM_CHANNEL_FC; + open->dev_channel_mapping[3] = PCM_CHANNEL_LS; + open->dev_channel_mapping[4] = PCM_CHANNEL_RS; + } else if (channel_mode == 6) { + open->dev_channel_mapping[0] = PCM_CHANNEL_FL; + open->dev_channel_mapping[1] = PCM_CHANNEL_FR; + open->dev_channel_mapping[2] = PCM_CHANNEL_LFE; + open->dev_channel_mapping[3] = PCM_CHANNEL_FC; + open->dev_channel_mapping[4] = PCM_CHANNEL_LS; + open->dev_channel_mapping[5] = PCM_CHANNEL_RS; + } else if (channel_mode == 7) { + open->dev_channel_mapping[0] = PCM_CHANNEL_FL; + open->dev_channel_mapping[1] = PCM_CHANNEL_FR; + open->dev_channel_mapping[2] = PCM_CHANNEL_FC; + open->dev_channel_mapping[3] = PCM_CHANNEL_LFE; + open->dev_channel_mapping[4] = PCM_CHANNEL_LB; + open->dev_channel_mapping[5] = PCM_CHANNEL_RB; + open->dev_channel_mapping[6] = PCM_CHANNEL_CS; + } else if (channel_mode == 8) { + open->dev_channel_mapping[0] = PCM_CHANNEL_FL; + open->dev_channel_mapping[1] = PCM_CHANNEL_FR; + open->dev_channel_mapping[2] = PCM_CHANNEL_LFE; + open->dev_channel_mapping[3] = PCM_CHANNEL_FC; + open->dev_channel_mapping[4] = PCM_CHANNEL_LS; + open->dev_channel_mapping[5] = PCM_CHANNEL_RS; + open->dev_channel_mapping[6] = PCM_CHANNEL_LB; + open->dev_channel_mapping[7] = PCM_CHANNEL_RB; + } else { + pr_err("%s: invalid num_chan %d\n", __func__, + channel_mode); + rc = -EINVAL; + goto inval_ch_mod; + } + } + +non_mch_path: +inval_ch_mod: + return rc; +} + +int adm_arrange_mch_ep2_map(struct adm_cmd_device_open_v6 *open_v6, + int channel_mode) +{ + int rc = 0; + + memset(open_v6->dev_channel_mapping_eid2, 0, + PCM_FORMAT_MAX_NUM_CHANNEL); + + if (channel_mode == 1) { + open_v6->dev_channel_mapping_eid2[0] = PCM_CHANNEL_FC; + } else if (channel_mode == 2) { + open_v6->dev_channel_mapping_eid2[0] = PCM_CHANNEL_FL; + open_v6->dev_channel_mapping_eid2[1] = PCM_CHANNEL_FR; + } else if (channel_mode == 3) { + open_v6->dev_channel_mapping_eid2[0] = PCM_CHANNEL_FL; + open_v6->dev_channel_mapping_eid2[1] = PCM_CHANNEL_FR; + open_v6->dev_channel_mapping_eid2[2] = PCM_CHANNEL_FC; + } else if (channel_mode == 4) { + open_v6->dev_channel_mapping_eid2[0] = PCM_CHANNEL_FL; + open_v6->dev_channel_mapping_eid2[1] = PCM_CHANNEL_FR; + open_v6->dev_channel_mapping_eid2[2] = PCM_CHANNEL_LS; + open_v6->dev_channel_mapping_eid2[3] = PCM_CHANNEL_RS; + } else if (channel_mode == 5) { + open_v6->dev_channel_mapping_eid2[0] = PCM_CHANNEL_FL; + open_v6->dev_channel_mapping_eid2[1] = PCM_CHANNEL_FR; + open_v6->dev_channel_mapping_eid2[2] = PCM_CHANNEL_FC; + open_v6->dev_channel_mapping_eid2[3] = PCM_CHANNEL_LS; + open_v6->dev_channel_mapping_eid2[4] = PCM_CHANNEL_RS; + } else if (channel_mode == 6) { + open_v6->dev_channel_mapping_eid2[0] = PCM_CHANNEL_FL; + open_v6->dev_channel_mapping_eid2[1] = PCM_CHANNEL_FR; + open_v6->dev_channel_mapping_eid2[2] = PCM_CHANNEL_LFE; + open_v6->dev_channel_mapping_eid2[3] = PCM_CHANNEL_FC; + open_v6->dev_channel_mapping_eid2[4] = PCM_CHANNEL_LS; + open_v6->dev_channel_mapping_eid2[5] = PCM_CHANNEL_RS; + } else if (channel_mode == 8) { + open_v6->dev_channel_mapping_eid2[0] = PCM_CHANNEL_FL; + open_v6->dev_channel_mapping_eid2[1] = PCM_CHANNEL_FR; + open_v6->dev_channel_mapping_eid2[2] = PCM_CHANNEL_LFE; + open_v6->dev_channel_mapping_eid2[3] = PCM_CHANNEL_FC; + open_v6->dev_channel_mapping_eid2[4] = PCM_CHANNEL_LS; + open_v6->dev_channel_mapping_eid2[5] = PCM_CHANNEL_RS; + open_v6->dev_channel_mapping_eid2[6] = PCM_CHANNEL_LB; + open_v6->dev_channel_mapping_eid2[7] = PCM_CHANNEL_RB; + } else { + pr_err("%s: invalid num_chan %d\n", __func__, + channel_mode); + rc = -EINVAL; + } + + return rc; +} + +static int adm_arrange_mch_map_v8( + struct adm_device_endpoint_payload *ep_payload, + int path, + int channel_mode) +{ + int rc = 0, idx; + + memset(ep_payload->dev_channel_mapping, + 0, PCM_FORMAT_MAX_NUM_CHANNEL_V8); + switch (path) { + case ADM_PATH_PLAYBACK: + idx = ADM_MCH_MAP_IDX_PLAYBACK; + break; + case ADM_PATH_LIVE_REC: + case ADM_PATH_NONLIVE_REC: + idx = ADM_MCH_MAP_IDX_REC; + break; + default: + goto non_mch_path; + }; + + if ((ep_payload->dev_num_channel > 2) && + multi_ch_maps[idx].set_channel_map) { + memcpy(ep_payload->dev_channel_mapping, + multi_ch_maps[idx].channel_mapping, + PCM_FORMAT_MAX_NUM_CHANNEL_V8); + } else { + if (channel_mode == 1) { + ep_payload->dev_channel_mapping[0] = PCM_CHANNEL_FC; + } else if (channel_mode == 2) { + ep_payload->dev_channel_mapping[0] = PCM_CHANNEL_FL; + ep_payload->dev_channel_mapping[1] = PCM_CHANNEL_FR; + } else if (channel_mode == 3) { + ep_payload->dev_channel_mapping[0] = PCM_CHANNEL_FL; + ep_payload->dev_channel_mapping[1] = PCM_CHANNEL_FR; + ep_payload->dev_channel_mapping[2] = PCM_CHANNEL_FC; + } else if (channel_mode == 4) { + ep_payload->dev_channel_mapping[0] = PCM_CHANNEL_FL; + ep_payload->dev_channel_mapping[1] = PCM_CHANNEL_FR; + ep_payload->dev_channel_mapping[2] = PCM_CHANNEL_LS; + ep_payload->dev_channel_mapping[3] = PCM_CHANNEL_RS; + } else if (channel_mode == 5) { + ep_payload->dev_channel_mapping[0] = PCM_CHANNEL_FL; + ep_payload->dev_channel_mapping[1] = PCM_CHANNEL_FR; + ep_payload->dev_channel_mapping[2] = PCM_CHANNEL_FC; + ep_payload->dev_channel_mapping[3] = PCM_CHANNEL_LS; + ep_payload->dev_channel_mapping[4] = PCM_CHANNEL_RS; + } else if (channel_mode == 6) { + ep_payload->dev_channel_mapping[0] = PCM_CHANNEL_FL; + ep_payload->dev_channel_mapping[1] = PCM_CHANNEL_FR; + ep_payload->dev_channel_mapping[2] = PCM_CHANNEL_LFE; + ep_payload->dev_channel_mapping[3] = PCM_CHANNEL_FC; + ep_payload->dev_channel_mapping[4] = PCM_CHANNEL_LS; + ep_payload->dev_channel_mapping[5] = PCM_CHANNEL_RS; + } else if (channel_mode == 7) { + ep_payload->dev_channel_mapping[0] = PCM_CHANNEL_FL; + ep_payload->dev_channel_mapping[1] = PCM_CHANNEL_FR; + ep_payload->dev_channel_mapping[2] = PCM_CHANNEL_FC; + ep_payload->dev_channel_mapping[3] = PCM_CHANNEL_LFE; + ep_payload->dev_channel_mapping[4] = PCM_CHANNEL_LB; + ep_payload->dev_channel_mapping[5] = PCM_CHANNEL_RB; + ep_payload->dev_channel_mapping[6] = PCM_CHANNEL_CS; + } else if (channel_mode == 8) { + ep_payload->dev_channel_mapping[0] = PCM_CHANNEL_FL; + ep_payload->dev_channel_mapping[1] = PCM_CHANNEL_FR; + ep_payload->dev_channel_mapping[2] = PCM_CHANNEL_LFE; + ep_payload->dev_channel_mapping[3] = PCM_CHANNEL_FC; + ep_payload->dev_channel_mapping[4] = PCM_CHANNEL_LS; + ep_payload->dev_channel_mapping[5] = PCM_CHANNEL_RS; + ep_payload->dev_channel_mapping[6] = PCM_CHANNEL_LB; + ep_payload->dev_channel_mapping[7] = PCM_CHANNEL_RB; + } else if (channel_mode == 10) { + ep_payload->dev_channel_mapping[0] = PCM_CHANNEL_FL; + ep_payload->dev_channel_mapping[1] = PCM_CHANNEL_FR; + ep_payload->dev_channel_mapping[2] = PCM_CHANNEL_LFE; + ep_payload->dev_channel_mapping[3] = PCM_CHANNEL_FC; + ep_payload->dev_channel_mapping[4] = PCM_CHANNEL_LB; + ep_payload->dev_channel_mapping[5] = PCM_CHANNEL_RB; + ep_payload->dev_channel_mapping[6] = PCM_CHANNEL_LS; + ep_payload->dev_channel_mapping[7] = PCM_CHANNEL_RS; + ep_payload->dev_channel_mapping[8] = PCM_CHANNEL_TFL; + ep_payload->dev_channel_mapping[9] = PCM_CHANNEL_TFR; + } else if (channel_mode == 12) { + ep_payload->dev_channel_mapping[0] = PCM_CHANNEL_FL; + ep_payload->dev_channel_mapping[1] = PCM_CHANNEL_FR; + ep_payload->dev_channel_mapping[2] = PCM_CHANNEL_LFE; + ep_payload->dev_channel_mapping[3] = PCM_CHANNEL_FC; + ep_payload->dev_channel_mapping[4] = PCM_CHANNEL_LB; + ep_payload->dev_channel_mapping[5] = PCM_CHANNEL_RB; + ep_payload->dev_channel_mapping[6] = PCM_CHANNEL_LS; + ep_payload->dev_channel_mapping[7] = PCM_CHANNEL_RS; + ep_payload->dev_channel_mapping[8] = PCM_CHANNEL_TFL; + ep_payload->dev_channel_mapping[9] = PCM_CHANNEL_TFR; + ep_payload->dev_channel_mapping[10] = PCM_CHANNEL_TSL; + ep_payload->dev_channel_mapping[11] = PCM_CHANNEL_TSR; + } else if (channel_mode == 16) { + ep_payload->dev_channel_mapping[0] = PCM_CHANNEL_FL; + ep_payload->dev_channel_mapping[1] = PCM_CHANNEL_FR; + ep_payload->dev_channel_mapping[2] = PCM_CHANNEL_LFE; + ep_payload->dev_channel_mapping[3] = PCM_CHANNEL_FC; + ep_payload->dev_channel_mapping[4] = PCM_CHANNEL_LB; + ep_payload->dev_channel_mapping[5] = PCM_CHANNEL_RB; + ep_payload->dev_channel_mapping[6] = PCM_CHANNEL_LS; + ep_payload->dev_channel_mapping[7] = PCM_CHANNEL_RS; + ep_payload->dev_channel_mapping[8] = PCM_CHANNEL_TFL; + ep_payload->dev_channel_mapping[9] = PCM_CHANNEL_TFR; + ep_payload->dev_channel_mapping[10] = PCM_CHANNEL_TSL; + ep_payload->dev_channel_mapping[11] = PCM_CHANNEL_TSR; + ep_payload->dev_channel_mapping[12] = PCM_CHANNEL_FLC; + ep_payload->dev_channel_mapping[13] = PCM_CHANNEL_FRC; + ep_payload->dev_channel_mapping[14] = PCM_CHANNEL_RLC; + ep_payload->dev_channel_mapping[15] = PCM_CHANNEL_RRC; + } else { + pr_err("%s: invalid num_chan %d\n", __func__, + channel_mode); + rc = -EINVAL; + goto inval_ch_mod; + } + } + +non_mch_path: +inval_ch_mod: + return rc; +} + +static int adm_arrange_mch_ep2_map_v8( + struct adm_device_endpoint_payload *ep_payload, + int channel_mode) +{ + int rc = 0; + + memset(ep_payload->dev_channel_mapping, 0, + PCM_FORMAT_MAX_NUM_CHANNEL_V8); + + if (channel_mode == 1) { + ep_payload->dev_channel_mapping[0] = PCM_CHANNEL_FC; + } else if (channel_mode == 2) { + ep_payload->dev_channel_mapping[0] = PCM_CHANNEL_FL; + ep_payload->dev_channel_mapping[1] = PCM_CHANNEL_FR; + } else if (channel_mode == 3) { + ep_payload->dev_channel_mapping[0] = PCM_CHANNEL_FL; + ep_payload->dev_channel_mapping[1] = PCM_CHANNEL_FR; + ep_payload->dev_channel_mapping[2] = PCM_CHANNEL_FC; + } else if (channel_mode == 4) { + ep_payload->dev_channel_mapping[0] = PCM_CHANNEL_FL; + ep_payload->dev_channel_mapping[1] = PCM_CHANNEL_FR; + ep_payload->dev_channel_mapping[2] = PCM_CHANNEL_LS; + ep_payload->dev_channel_mapping[3] = PCM_CHANNEL_RS; + } else if (channel_mode == 5) { + ep_payload->dev_channel_mapping[0] = PCM_CHANNEL_FL; + ep_payload->dev_channel_mapping[1] = PCM_CHANNEL_FR; + ep_payload->dev_channel_mapping[2] = PCM_CHANNEL_FC; + ep_payload->dev_channel_mapping[3] = PCM_CHANNEL_LS; + ep_payload->dev_channel_mapping[4] = PCM_CHANNEL_RS; + } else if (channel_mode == 6) { + ep_payload->dev_channel_mapping[0] = PCM_CHANNEL_FL; + ep_payload->dev_channel_mapping[1] = PCM_CHANNEL_FR; + ep_payload->dev_channel_mapping[2] = PCM_CHANNEL_LFE; + ep_payload->dev_channel_mapping[3] = PCM_CHANNEL_FC; + ep_payload->dev_channel_mapping[4] = PCM_CHANNEL_LS; + ep_payload->dev_channel_mapping[5] = PCM_CHANNEL_RS; + } else if (channel_mode == 8) { + ep_payload->dev_channel_mapping[0] = PCM_CHANNEL_FL; + ep_payload->dev_channel_mapping[1] = PCM_CHANNEL_FR; + ep_payload->dev_channel_mapping[2] = PCM_CHANNEL_LFE; + ep_payload->dev_channel_mapping[3] = PCM_CHANNEL_FC; + ep_payload->dev_channel_mapping[4] = PCM_CHANNEL_LS; + ep_payload->dev_channel_mapping[5] = PCM_CHANNEL_RS; + ep_payload->dev_channel_mapping[6] = PCM_CHANNEL_LB; + ep_payload->dev_channel_mapping[7] = PCM_CHANNEL_RB; + } else if (channel_mode == 10) { + ep_payload->dev_channel_mapping[0] = PCM_CHANNEL_FL; + ep_payload->dev_channel_mapping[1] = PCM_CHANNEL_FR; + ep_payload->dev_channel_mapping[2] = PCM_CHANNEL_LFE; + ep_payload->dev_channel_mapping[3] = PCM_CHANNEL_FC; + ep_payload->dev_channel_mapping[4] = PCM_CHANNEL_LS; + ep_payload->dev_channel_mapping[5] = PCM_CHANNEL_RS; + ep_payload->dev_channel_mapping[6] = PCM_CHANNEL_LB; + ep_payload->dev_channel_mapping[7] = PCM_CHANNEL_RB; + ep_payload->dev_channel_mapping[8] = PCM_CHANNEL_CS; + ep_payload->dev_channel_mapping[9] = PCM_CHANNELS; + } else if (channel_mode == 12) { + ep_payload->dev_channel_mapping[0] = PCM_CHANNEL_FL; + ep_payload->dev_channel_mapping[1] = PCM_CHANNEL_FR; + ep_payload->dev_channel_mapping[2] = PCM_CHANNEL_LFE; + ep_payload->dev_channel_mapping[3] = PCM_CHANNEL_FC; + ep_payload->dev_channel_mapping[4] = PCM_CHANNEL_LS; + ep_payload->dev_channel_mapping[5] = PCM_CHANNEL_RS; + ep_payload->dev_channel_mapping[6] = PCM_CHANNEL_LB; + ep_payload->dev_channel_mapping[7] = PCM_CHANNEL_RB; + ep_payload->dev_channel_mapping[8] = PCM_CHANNEL_TFL; + ep_payload->dev_channel_mapping[9] = PCM_CHANNEL_TFR; + ep_payload->dev_channel_mapping[10] = PCM_CHANNEL_TSL; + ep_payload->dev_channel_mapping[11] = PCM_CHANNEL_TSR; + } else if (channel_mode == 16) { + ep_payload->dev_channel_mapping[0] = PCM_CHANNEL_FL; + ep_payload->dev_channel_mapping[1] = PCM_CHANNEL_FR; + ep_payload->dev_channel_mapping[2] = PCM_CHANNEL_LFE; + ep_payload->dev_channel_mapping[3] = PCM_CHANNEL_FC; + ep_payload->dev_channel_mapping[4] = PCM_CHANNEL_LS; + ep_payload->dev_channel_mapping[5] = PCM_CHANNEL_RS; + ep_payload->dev_channel_mapping[6] = PCM_CHANNEL_LB; + ep_payload->dev_channel_mapping[7] = PCM_CHANNEL_RB; + ep_payload->dev_channel_mapping[8] = PCM_CHANNEL_CS; + ep_payload->dev_channel_mapping[9] = PCM_CHANNELS; + ep_payload->dev_channel_mapping[10] = PCM_CHANNEL_CVH; + ep_payload->dev_channel_mapping[11] = PCM_CHANNEL_MS; + ep_payload->dev_channel_mapping[12] = PCM_CHANNEL_FLC; + ep_payload->dev_channel_mapping[13] = PCM_CHANNEL_FRC; + ep_payload->dev_channel_mapping[14] = PCM_CHANNEL_RLC; + ep_payload->dev_channel_mapping[15] = PCM_CHANNEL_RRC; + } else { + pr_err("%s: invalid num_chan %d\n", __func__, + channel_mode); + rc = -EINVAL; + } + + return rc; +} +/** + * adm_open - + * command to send ADM open + * + * @port_id: port id number + * @path: direction or ADM path type + * @rate: sample rate of session + * @channel_mode: number of channels set + * @topology: topology active for this session + * @perf_mode: performance mode like LL/ULL/.. + * @bit_width: bit width to set for copp + * @app_type: App type used for this session + * @acdb_id: ACDB ID of this device + * + * Returns 0 on success or error on failure + */ +int adm_open(int port_id, int path, int rate, int channel_mode, int topology, + int perf_mode, uint16_t bit_width, int app_type, int acdb_id) +{ + struct adm_cmd_device_open_v5 open; + struct adm_cmd_device_open_v6 open_v6; + struct adm_cmd_device_open_v8 open_v8; + struct adm_device_endpoint_payload ep1_payload; + struct adm_device_endpoint_payload ep2_payload; + int ep1_payload_size = 0; + int ep2_payload_size = 0; + int ret = 0; + int port_idx, flags; + int copp_idx = -1; + int tmp_port = q6audio_get_port_id(port_id); + void *adm_params = NULL; + int param_size; + + pr_debug("%s:port %#x path:%d rate:%d mode:%d perf_mode:%d,topo_id %d\n", + __func__, port_id, path, rate, channel_mode, perf_mode, + topology); + + port_id = q6audio_convert_virtual_to_portid(port_id); + port_idx = adm_validate_and_get_port_index(port_id); + if (port_idx < 0) { + pr_err("%s: Invalid port_id 0x%x\n", __func__, port_id); + return -EINVAL; + } + if (channel_mode < 0 || channel_mode > 32) { + pr_err("%s: Invalid channel number 0x%x\n", + __func__, channel_mode); + return -EINVAL; + } + + if (this_adm.apr == NULL) { + this_adm.apr = apr_register("ADSP", "ADM", adm_callback, + 0xFFFFFFFF, &this_adm); + if (this_adm.apr == NULL) { + pr_err("%s: Unable to register ADM\n", __func__); + return -ENODEV; + } + rtac_set_adm_handle(this_adm.apr); + } + + if (perf_mode == ULL_POST_PROCESSING_PCM_MODE) { + flags = ADM_ULL_POST_PROCESSING_DEVICE_SESSION; + if ((topology == DOLBY_ADM_COPP_TOPOLOGY_ID) || + (topology == DS2_ADM_COPP_TOPOLOGY_ID) || + (topology == SRS_TRUMEDIA_TOPOLOGY_ID)) + topology = DEFAULT_COPP_TOPOLOGY; + } else if (perf_mode == ULTRA_LOW_LATENCY_PCM_MODE) { + flags = ADM_ULTRA_LOW_LATENCY_DEVICE_SESSION; + topology = NULL_COPP_TOPOLOGY; + rate = ULL_SUPPORTED_SAMPLE_RATE; + bit_width = ULL_SUPPORTED_BITS_PER_SAMPLE; + } else if (perf_mode == LOW_LATENCY_PCM_MODE) { + flags = ADM_LOW_LATENCY_DEVICE_SESSION; + if ((topology == DOLBY_ADM_COPP_TOPOLOGY_ID) || + (topology == DS2_ADM_COPP_TOPOLOGY_ID) || + (topology == SRS_TRUMEDIA_TOPOLOGY_ID)) + topology = DEFAULT_COPP_TOPOLOGY; + } else { + if ((path == ADM_PATH_COMPRESSED_RX) || + (path == ADM_PATH_COMPRESSED_TX)) + flags = 0; + else + flags = ADM_LEGACY_DEVICE_SESSION; + } + + if ((topology == VPM_TX_SM_ECNS_V2_COPP_TOPOLOGY) || + (topology == VPM_TX_DM_FLUENCE_COPP_TOPOLOGY) || + (topology == VPM_TX_DM_RFECNS_COPP_TOPOLOGY)|| + (topology == VPM_TX_DM_FLUENCE_EF_COPP_TOPOLOGY)) { + if ((rate != ADM_CMD_COPP_OPEN_SAMPLE_RATE_8K) && + (rate != ADM_CMD_COPP_OPEN_SAMPLE_RATE_16K) && + (rate != ADM_CMD_COPP_OPEN_SAMPLE_RATE_32K) && + (rate != ADM_CMD_COPP_OPEN_SAMPLE_RATE_48K)) + rate = 16000; + } + + if (topology == VPM_TX_VOICE_SMECNS_V2_COPP_TOPOLOGY) + channel_mode = 1; + + /* + * Routing driver reuses the same adm for streams with the same + * app_type, sample_rate etc. + * This isn't allowed for ULL streams as per the DSP interface + */ + if (perf_mode != ULTRA_LOW_LATENCY_PCM_MODE) + copp_idx = adm_get_idx_if_copp_exists(port_idx, topology, + perf_mode, + rate, bit_width, + app_type); + + if (copp_idx < 0) { + copp_idx = adm_get_next_available_copp(port_idx); + if (copp_idx >= MAX_COPPS_PER_PORT) { + pr_err("%s: exceeded copp id %d\n", + __func__, copp_idx); + return -EINVAL; + } + atomic_set(&this_adm.copp.cnt[port_idx][copp_idx], 0); + atomic_set(&this_adm.copp.topology[port_idx][copp_idx], + topology); + atomic_set(&this_adm.copp.mode[port_idx][copp_idx], + perf_mode); + atomic_set(&this_adm.copp.rate[port_idx][copp_idx], + rate); + atomic_set(&this_adm.copp.channels[port_idx][copp_idx], + channel_mode); + atomic_set(&this_adm.copp.bit_width[port_idx][copp_idx], + bit_width); + atomic_set(&this_adm.copp.app_type[port_idx][copp_idx], + app_type); + atomic_set(&this_adm.copp.acdb_id[port_idx][copp_idx], + acdb_id); + set_bit(ADM_STATUS_CALIBRATION_REQUIRED, + (void *)&this_adm.copp.adm_status[port_idx][copp_idx]); + if ((path != ADM_PATH_COMPRESSED_RX) && + (path != ADM_PATH_COMPRESSED_TX)) + send_adm_custom_topology(); + } + + if (this_adm.copp.adm_delay[port_idx][copp_idx] && + perf_mode == LEGACY_PCM_MODE) { + atomic_set(&this_adm.copp.adm_delay_stat[port_idx][copp_idx], + 1); + this_adm.copp.adm_delay[port_idx][copp_idx] = 0; + wake_up(&this_adm.copp.adm_delay_wait[port_idx][copp_idx]); + } + + /* Create a COPP if port id are not enabled */ + if (atomic_read(&this_adm.copp.cnt[port_idx][copp_idx]) == 0) { + pr_debug("%s: open ADM: port_idx: %d, copp_idx: %d\n", __func__, + port_idx, copp_idx); + if ((topology == SRS_TRUMEDIA_TOPOLOGY_ID) && + perf_mode == LEGACY_PCM_MODE) { + int res; + + atomic_set(&this_adm.mem_map_index, ADM_SRS_TRUMEDIA); + msm_dts_srs_tm_ion_memmap(&this_adm.outband_memmap); + res = adm_memory_map_regions( + &this_adm.outband_memmap.paddr, 0, + (uint32_t *)&this_adm.outband_memmap.size, 1); + if (res < 0) { + pr_err("%s: SRS adm_memory_map_regions failed! addr = 0x%pK, size = %d\n", + __func__, + (void *)this_adm.outband_memmap.paddr, + (uint32_t)this_adm.outband_memmap.size); + } + } + + + if ((q6core_get_avcs_api_version_per_service( + APRV2_IDS_SERVICE_ID_ADSP_ADM_V) >= + ADSP_ADM_API_VERSION_V3) && + q6core_use_Q6_32ch_support()) { + memset(&open_v8, 0, sizeof(open_v8)); + memset(&ep1_payload, 0, sizeof(ep1_payload)); + memset(&ep2_payload, 0, sizeof(ep2_payload)); + + open_v8.hdr.hdr_field = APR_HDR_FIELD( + APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + open_v8.hdr.src_svc = APR_SVC_ADM; + open_v8.hdr.src_domain = APR_DOMAIN_APPS; + open_v8.hdr.src_port = tmp_port; + open_v8.hdr.dest_svc = APR_SVC_ADM; + open_v8.hdr.dest_domain = APR_DOMAIN_ADSP; + open_v8.hdr.dest_port = tmp_port; + open_v8.hdr.token = port_idx << 16 | copp_idx; + open_v8.hdr.opcode = ADM_CMD_DEVICE_OPEN_V8; + + if (this_adm.native_mode != 0) { + open_v8.flags = flags | + (this_adm.native_mode << 11); + this_adm.native_mode = 0; + } else { + open_v8.flags = flags; + } + open_v8.mode_of_operation = path; + open_v8.endpoint_id_1 = tmp_port; + open_v8.endpoint_id_2 = 0xFFFF; + open_v8.endpoint_id_3 = 0xFFFF; + open_v8.topology_id = topology; + open_v8.reserved = 0; + + /* variable endpoint payload */ + ep1_payload.dev_num_channel = channel_mode & 0x00FF; + ep1_payload.bit_width = bit_width; + ep1_payload.sample_rate = rate; + ret = adm_arrange_mch_map_v8(&ep1_payload, path, + channel_mode); + if (ret) + return ret; + + pr_debug("%s: port_id=0x%x %x %x topology_id=0x%X flags %x ref_ch %x\n", + __func__, open_v8.endpoint_id_1, + open_v8.endpoint_id_2, + open_v8.endpoint_id_3, + open_v8.topology_id, + open_v8.flags, + this_adm.num_ec_ref_rx_chans); + + ep1_payload_size = 8 + + roundup(ep1_payload.dev_num_channel, 4); + param_size = sizeof(struct adm_cmd_device_open_v8) + + ep1_payload_size; + atomic_set(&this_adm.copp.stat[port_idx][copp_idx], -1); + + if ((this_adm.num_ec_ref_rx_chans != 0) + && (path != ADM_PATH_PLAYBACK) + && (open_v8.endpoint_id_2 != 0xFFFF)) { + open_v8.endpoint_id_2 = this_adm.ec_ref_rx; + this_adm.ec_ref_rx = -1; + ep2_payload.dev_num_channel = + this_adm.num_ec_ref_rx_chans; + this_adm.num_ec_ref_rx_chans = 0; + + if (this_adm.ec_ref_rx_bit_width != 0) { + ep2_payload.bit_width = + this_adm.ec_ref_rx_bit_width; + } else { + ep2_payload.bit_width = bit_width; + } + + if (this_adm.ec_ref_rx_sampling_rate != 0) { + ep2_payload.sample_rate = + this_adm.ec_ref_rx_sampling_rate; + } else { + ep2_payload.sample_rate = rate; + } + + pr_debug("%s: adm open_v8 eid2_channels=%d eid2_bit_width=%d eid2_rate=%d\n", + __func__, + ep2_payload.dev_num_channel, + ep2_payload.bit_width, + ep2_payload.sample_rate); + + ret = adm_arrange_mch_ep2_map_v8(&ep2_payload, + ep2_payload.dev_num_channel); + + if (ret) + return ret; + ep2_payload_size = 8 + + roundup(ep2_payload.dev_num_channel, 4); + param_size += ep2_payload_size; + } + + open_v8.hdr.pkt_size = param_size; + adm_params = kzalloc(param_size, GFP_KERNEL); + if (!adm_params) + return -ENOMEM; + memcpy(adm_params, &open_v8, sizeof(open_v8)); + memcpy(adm_params + sizeof(open_v8), + (void *)&ep1_payload, + ep1_payload_size); + + if ((this_adm.num_ec_ref_rx_chans != 0) + && (path != ADM_PATH_PLAYBACK) + && (open_v8.endpoint_id_2 != 0xFFFF)) { + memcpy(adm_params + sizeof(open_v8) + + ep1_payload_size, + (void *)&ep2_payload, + ep2_payload_size); + } + + ret = apr_send_pkt(this_adm.apr, + (uint32_t *)adm_params); + if (ret < 0) { + pr_err("%s: port_id: 0x%x for[0x%x] failed %d for open_v8\n", + __func__, tmp_port, port_id, ret); + return -EINVAL; + } + kfree(adm_params); + } else { + + open.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + open.hdr.pkt_size = sizeof(open); + open.hdr.src_svc = APR_SVC_ADM; + open.hdr.src_domain = APR_DOMAIN_APPS; + open.hdr.src_port = tmp_port; + open.hdr.dest_svc = APR_SVC_ADM; + open.hdr.dest_domain = APR_DOMAIN_ADSP; + open.hdr.dest_port = tmp_port; + open.hdr.token = port_idx << 16 | copp_idx; + open.hdr.opcode = ADM_CMD_DEVICE_OPEN_V5; + open.flags = flags; + open.mode_of_operation = path; + open.endpoint_id_1 = tmp_port; + open.endpoint_id_2 = 0xFFFF; + + if (this_adm.ec_ref_rx && (path != 1)) { + open.endpoint_id_2 = this_adm.ec_ref_rx; + } + + open.topology_id = topology; + + open.dev_num_channel = channel_mode & 0x00FF; + open.bit_width = bit_width; + WARN_ON((perf_mode == ULTRA_LOW_LATENCY_PCM_MODE) && + (rate != ULL_SUPPORTED_SAMPLE_RATE)); + open.sample_rate = rate; + + ret = adm_arrange_mch_map(&open, path, channel_mode); + + if (ret) + return ret; + + pr_debug("%s: port_id=0x%x rate=%d topology_id=0x%X\n", + __func__, open.endpoint_id_1, open.sample_rate, + open.topology_id); + + atomic_set(&this_adm.copp.stat[port_idx][copp_idx], -1); + + if ((this_adm.num_ec_ref_rx_chans != 0) && + (path != 1) && (open.endpoint_id_2 != 0xFFFF)) { + memset(&open_v6, 0, + sizeof(struct adm_cmd_device_open_v6)); + memcpy(&open_v6, &open, + sizeof(struct adm_cmd_device_open_v5)); + open_v6.hdr.opcode = ADM_CMD_DEVICE_OPEN_V6; + open_v6.hdr.pkt_size = sizeof(open_v6); + open_v6.dev_num_channel_eid2 = + this_adm.num_ec_ref_rx_chans; + + if (this_adm.ec_ref_rx_bit_width != 0) { + open_v6.bit_width_eid2 = + this_adm.ec_ref_rx_bit_width; + } else { + open_v6.bit_width_eid2 = bit_width; + } + + if (this_adm.ec_ref_rx_sampling_rate != 0) { + open_v6.sample_rate_eid2 = + this_adm.ec_ref_rx_sampling_rate; + } else { + open_v6.sample_rate_eid2 = rate; + } + + pr_debug("%s: eid2_channels=%d eid2_bit_width=%d eid2_rate=%d\n", + __func__, open_v6.dev_num_channel_eid2, + open_v6.bit_width_eid2, + open_v6.sample_rate_eid2); + + ret = adm_arrange_mch_ep2_map(&open_v6, + open_v6.dev_num_channel_eid2); + + if (ret) + return ret; + + ret = apr_send_pkt(this_adm.apr, + (uint32_t *)&open_v6); + } else { + ret = apr_send_pkt(this_adm.apr, + (uint32_t *)&open); + } + if (ret < 0) { + pr_err("%s: port_id: 0x%x for[0x%x] failed %d\n", + __func__, tmp_port, port_id, ret); + return -EINVAL; + } + } + + /* Wait for the callback with copp id */ + ret = wait_event_timeout(this_adm.copp.wait[port_idx][copp_idx], + atomic_read(&this_adm.copp.stat + [port_idx][copp_idx]) >= 0, + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: ADM open timedout for port_id: 0x%x for [0x%x]\n", + __func__, tmp_port, port_id); + return -EINVAL; + } else if (atomic_read(&this_adm.copp.stat + [port_idx][copp_idx]) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&this_adm.copp.stat + [port_idx][copp_idx]))); + return adsp_err_get_lnx_err_code( + atomic_read(&this_adm.copp.stat + [port_idx][copp_idx])); + } + } + atomic_inc(&this_adm.copp.cnt[port_idx][copp_idx]); + return copp_idx; +} +EXPORT_SYMBOL(adm_open); + +/** + * adm_copp_mfc_cfg - + * command to send ADM MFC config + * + * @port_id: Port ID number + * @copp_idx: copp index assigned + * @dst_sample_rate: sink sample rate + * + */ +void adm_copp_mfc_cfg(int port_id, int copp_idx, int dst_sample_rate) +{ + struct audproc_mfc_param_media_fmt mfc_cfg; + struct adm_cmd_device_open_v5 open; + struct param_hdr_v3 param_hdr; + int port_idx; + int rc = 0; + int i = 0; + + port_id = q6audio_convert_virtual_to_portid(port_id); + port_idx = adm_validate_and_get_port_index(port_id); + + if (port_idx < 0) { + pr_err("%s: Invalid port_id %#x\n", __func__, port_id); + goto fail_cmd; + } + + if (copp_idx < 0 || copp_idx >= MAX_COPPS_PER_PORT) { + pr_err("%s: Invalid copp_num: %d\n", __func__, copp_idx); + goto fail_cmd; + } + + memset(&mfc_cfg, 0, sizeof(mfc_cfg)); + memset(&open, 0, sizeof(open)); + memset(¶m_hdr, 0, sizeof(param_hdr)); + + param_hdr.module_id = AUDPROC_MODULE_ID_MFC; + param_hdr.instance_id = INSTANCE_ID_0; + param_hdr.param_id = AUDPROC_PARAM_ID_MFC_OUTPUT_MEDIA_FORMAT; + param_hdr.param_size = sizeof(mfc_cfg); + + mfc_cfg.sampling_rate = dst_sample_rate; + mfc_cfg.bits_per_sample = + atomic_read(&this_adm.copp.bit_width[port_idx][copp_idx]); + open.dev_num_channel = mfc_cfg.num_channels = + atomic_read(&this_adm.copp.channels[port_idx][copp_idx]); + + rc = adm_arrange_mch_map(&open, ADM_PATH_PLAYBACK, + mfc_cfg.num_channels); + if (rc < 0) { + pr_err("%s: unable to get channal map\n", __func__); + goto fail_cmd; + } + + for (i = 0; i < mfc_cfg.num_channels; i++) + mfc_cfg.channel_type[i] = + (uint16_t) open.dev_channel_mapping[i]; + + atomic_set(&this_adm.copp.stat[port_idx][copp_idx], -1); + + pr_debug("%s: mfc config: port_idx %d copp_idx %d copp SR %d copp BW %d copp chan %d o/p SR %d\n", + __func__, port_idx, copp_idx, + atomic_read(&this_adm.copp.rate[port_idx][copp_idx]), + mfc_cfg.bits_per_sample, mfc_cfg.num_channels, + mfc_cfg.sampling_rate); + + rc = adm_pack_and_set_one_pp_param(port_id, copp_idx, param_hdr, + (uint8_t *) &mfc_cfg); + if (rc) + pr_err("%s: Failed to set media format configuration data, err %d\n", + __func__, rc); + +fail_cmd: + return; +} +EXPORT_SYMBOL(adm_copp_mfc_cfg); + +static void route_set_opcode_matrix_id( + struct adm_cmd_matrix_map_routings_v5 **route_addr, + int path, uint32_t passthr_mode) +{ + struct adm_cmd_matrix_map_routings_v5 *route = *route_addr; + + switch (path) { + case ADM_PATH_PLAYBACK: + route->hdr.opcode = ADM_CMD_MATRIX_MAP_ROUTINGS_V5; + route->matrix_id = ADM_MATRIX_ID_AUDIO_RX; + break; + case ADM_PATH_LIVE_REC: + if (passthr_mode == LISTEN) { + route->hdr.opcode = + ADM_CMD_STREAM_DEVICE_MAP_ROUTINGS_V5; + route->matrix_id = ADM_MATRIX_ID_LISTEN_TX; + break; + } + /* fall through to set matrix id for non-listen case */ + case ADM_PATH_NONLIVE_REC: + route->hdr.opcode = ADM_CMD_MATRIX_MAP_ROUTINGS_V5; + route->matrix_id = ADM_MATRIX_ID_AUDIO_TX; + break; + case ADM_PATH_COMPRESSED_RX: + route->hdr.opcode = ADM_CMD_STREAM_DEVICE_MAP_ROUTINGS_V5; + route->matrix_id = ADM_MATRIX_ID_COMPRESSED_AUDIO_RX; + break; + case ADM_PATH_COMPRESSED_TX: + route->hdr.opcode = ADM_CMD_STREAM_DEVICE_MAP_ROUTINGS_V5; + route->matrix_id = ADM_MATRIX_ID_COMPRESSED_AUDIO_TX; + break; + default: + pr_err("%s: Wrong path set[%d]\n", __func__, path); + break; + } + pr_debug("%s: opcode 0x%x, matrix id %d\n", + __func__, route->hdr.opcode, route->matrix_id); +} + +/** + * adm_matrix_map - + * command to send ADM matrix map for ADM copp list + * + * @path: direction or ADM path type + * @payload_map: have info of session id and associated copp_idx/num_copps + * @perf_mode: performance mode like LL/ULL/.. + * @passthr_mode: flag to indicate passthrough mode + * + * Returns 0 on success or error on failure + */ +int adm_matrix_map(int path, struct route_payload payload_map, int perf_mode, + uint32_t passthr_mode) +{ + struct adm_cmd_matrix_map_routings_v5 *route; + struct adm_session_map_node_v5 *node; + uint16_t *copps_list; + int cmd_size = 0; + int ret = 0, i = 0; + void *payload = NULL; + void *matrix_map = NULL; + int port_idx, copp_idx; + + /* Assumes port_ids have already been validated during adm_open */ + cmd_size = (sizeof(struct adm_cmd_matrix_map_routings_v5) + + sizeof(struct adm_session_map_node_v5) + + (sizeof(uint32_t) * payload_map.num_copps)); + matrix_map = kzalloc(cmd_size, GFP_KERNEL); + if (matrix_map == NULL) { + pr_err("%s: Mem alloc failed\n", __func__); + ret = -EINVAL; + return ret; + } + route = (struct adm_cmd_matrix_map_routings_v5 *)matrix_map; + + route->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + route->hdr.pkt_size = cmd_size; + route->hdr.src_svc = 0; + route->hdr.src_domain = APR_DOMAIN_APPS; + route->hdr.src_port = 0; /* Ignored */; + route->hdr.dest_svc = APR_SVC_ADM; + route->hdr.dest_domain = APR_DOMAIN_ADSP; + route->hdr.dest_port = 0; /* Ignored */; + route->hdr.token = 0; + route->num_sessions = 1; + route_set_opcode_matrix_id(&route, path, passthr_mode); + + payload = ((u8 *)matrix_map + + sizeof(struct adm_cmd_matrix_map_routings_v5)); + node = (struct adm_session_map_node_v5 *)payload; + + node->session_id = payload_map.session_id; + node->num_copps = payload_map.num_copps; + payload = (u8 *)node + sizeof(struct adm_session_map_node_v5); + copps_list = (uint16_t *)payload; + for (i = 0; i < payload_map.num_copps; i++) { + port_idx = + adm_validate_and_get_port_index(payload_map.port_id[i]); + if (port_idx < 0) { + pr_err("%s: Invalid port_id 0x%x\n", __func__, + payload_map.port_id[i]); + ret = -EINVAL; + goto fail_cmd; + } + copp_idx = payload_map.copp_idx[i]; + copps_list[i] = atomic_read(&this_adm.copp.id[port_idx] + [copp_idx]); + } + atomic_set(&this_adm.matrix_map_stat, -1); + + ret = apr_send_pkt(this_adm.apr, (uint32_t *)matrix_map); + if (ret < 0) { + pr_err("%s: routing for syream %d failed ret %d\n", + __func__, payload_map.session_id, ret); + ret = -EINVAL; + goto fail_cmd; + } + ret = wait_event_timeout(this_adm.matrix_map_wait, + atomic_read(&this_adm.matrix_map_stat) >= 0, + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: routing for syream %d failed\n", __func__, + payload_map.session_id); + ret = -EINVAL; + goto fail_cmd; + } else if (atomic_read(&this_adm.matrix_map_stat) > 0) { + pr_err("%s: DSP returned error[%s]\n", __func__, + adsp_err_get_err_str(atomic_read( + &this_adm.matrix_map_stat))); + ret = adsp_err_get_lnx_err_code( + atomic_read(&this_adm.matrix_map_stat)); + goto fail_cmd; + } + + if ((perf_mode != ULTRA_LOW_LATENCY_PCM_MODE) && + (path != ADM_PATH_COMPRESSED_RX)) { + for (i = 0; i < payload_map.num_copps; i++) { + port_idx = afe_get_port_index(payload_map.port_id[i]); + copp_idx = payload_map.copp_idx[i]; + if (port_idx < 0 || copp_idx < 0 || + (copp_idx > MAX_COPPS_PER_PORT - 1)) { + pr_err("%s: Invalid idx port_idx %d copp_idx %d\n", + __func__, port_idx, copp_idx); + continue; + } + rtac_add_adm_device(payload_map.port_id[i], + atomic_read(&this_adm.copp.id + [port_idx][copp_idx]), + get_cal_path(path), + payload_map.session_id, + payload_map.app_type[i], + payload_map.acdb_dev_id[i]); + + if (!test_bit(ADM_STATUS_CALIBRATION_REQUIRED, + (void *)&this_adm.copp.adm_status[port_idx] + [copp_idx])) { + pr_debug("%s: adm copp[0x%x][%d] already sent", + __func__, port_idx, copp_idx); + continue; + } + send_adm_cal(payload_map.port_id[i], copp_idx, + get_cal_path(path), perf_mode, + payload_map.app_type[i], + payload_map.acdb_dev_id[i], + payload_map.sample_rate[i], + passthr_mode); + /* ADM COPP calibration is already sent */ + clear_bit(ADM_STATUS_CALIBRATION_REQUIRED, + (void *)&this_adm.copp. + adm_status[port_idx][copp_idx]); + pr_debug("%s: copp_id: %d\n", __func__, + atomic_read(&this_adm.copp.id[port_idx] + [copp_idx])); + } + } + +fail_cmd: + kfree(matrix_map); + return ret; +} +EXPORT_SYMBOL(adm_matrix_map); + +/** + * adm_ec_ref_rx_id - + * Update EC ref port ID + * + */ +void adm_ec_ref_rx_id(int port_id) +{ + this_adm.ec_ref_rx = port_id; + pr_debug("%s: ec_ref_rx:%d\n", __func__, this_adm.ec_ref_rx); +} +EXPORT_SYMBOL(adm_ec_ref_rx_id); + +/** + * adm_num_ec_ref_rx_chans - + * Update EC ref number of channels + * + */ +void adm_num_ec_ref_rx_chans(int num_chans) +{ + this_adm.num_ec_ref_rx_chans = num_chans; + pr_debug("%s: num_ec_ref_rx_chans:%d\n", + __func__, this_adm.num_ec_ref_rx_chans); +} +EXPORT_SYMBOL(adm_num_ec_ref_rx_chans); + +/** + * adm_ec_ref_rx_bit_width - + * Update EC ref bit_width + * + */ +void adm_ec_ref_rx_bit_width(int bit_width) +{ + this_adm.ec_ref_rx_bit_width = bit_width; + pr_debug("%s: ec_ref_rx_bit_width:%d\n", + __func__, this_adm.ec_ref_rx_bit_width); +} +EXPORT_SYMBOL(adm_ec_ref_rx_bit_width); + +/** + * adm_ec_ref_rx_sampling_rate - + * Update EC ref sample rate + * + */ +void adm_ec_ref_rx_sampling_rate(int sampling_rate) +{ + this_adm.ec_ref_rx_sampling_rate = sampling_rate; + pr_debug("%s: ec_ref_rx_sampling_rate:%d\n", + __func__, this_adm.ec_ref_rx_sampling_rate); +} +EXPORT_SYMBOL(adm_ec_ref_rx_sampling_rate); + +/** + * adm_set_native_mode - + * Set adm channel native mode. + * If enabled matrix mixer will be + * running in native mode for channel + * configuration for this device session. + * + */ +void adm_set_native_mode(int mode) +{ + this_adm.native_mode = mode; + pr_debug("%s: enable native_mode :%d\n", + __func__, this_adm.native_mode); +} +EXPORT_SYMBOL(adm_set_native_mode); + +/** + * adm_close - + * command to close ADM copp + * + * @port_id: Port ID number + * @perf_mode: performance mode like LL/ULL/.. + * @copp_idx: copp index assigned + * + * Returns 0 on success or error on failure + */ +int adm_close(int port_id, int perf_mode, int copp_idx) +{ + struct apr_hdr close; + + int ret = 0, port_idx; + int copp_id = RESET_COPP_ID; + + pr_debug("%s: port_id=0x%x perf_mode: %d copp_idx: %d\n", __func__, + port_id, perf_mode, copp_idx); + + port_id = q6audio_convert_virtual_to_portid(port_id); + port_idx = adm_validate_and_get_port_index(port_id); + if (port_idx < 0) { + pr_err("%s: Invalid port_id 0x%x\n", + __func__, port_id); + return -EINVAL; + } + + if ((copp_idx < 0) || (copp_idx >= MAX_COPPS_PER_PORT)) { + pr_err("%s: Invalid copp idx: %d\n", __func__, copp_idx); + return -EINVAL; + } + + if (this_adm.copp.adm_delay[port_idx][copp_idx] && perf_mode + == LEGACY_PCM_MODE) { + atomic_set(&this_adm.copp.adm_delay_stat[port_idx][copp_idx], + 1); + this_adm.copp.adm_delay[port_idx][copp_idx] = 0; + wake_up(&this_adm.copp.adm_delay_wait[port_idx][copp_idx]); + } + + atomic_dec(&this_adm.copp.cnt[port_idx][copp_idx]); + if (!(atomic_read(&this_adm.copp.cnt[port_idx][copp_idx]))) { + copp_id = adm_get_copp_id(port_idx, copp_idx); + pr_debug("%s: Closing ADM port_idx:%d copp_idx:%d copp_id:0x%x\n", + __func__, port_idx, copp_idx, copp_id); + if ((!perf_mode) && (this_adm.outband_memmap.paddr != 0) && + (atomic_read(&this_adm.copp.topology[port_idx][copp_idx]) == + SRS_TRUMEDIA_TOPOLOGY_ID)) { + atomic_set(&this_adm.mem_map_index, + ADM_SRS_TRUMEDIA); + ret = adm_memory_unmap_regions(); + if (ret < 0) { + pr_err("%s: adm mem unmmap err %d", + __func__, ret); + } else { + atomic_set(&this_adm.mem_map_handles + [ADM_SRS_TRUMEDIA], 0); + } + } + + + if ((afe_get_port_type(port_id) == MSM_AFE_PORT_TYPE_TX) && + this_adm.sourceTrackingData.memmap.paddr) { + atomic_set(&this_adm.mem_map_index, + ADM_MEM_MAP_INDEX_SOURCE_TRACKING); + ret = adm_memory_unmap_regions(); + if (ret < 0) { + pr_err("%s: adm mem unmmap err %d", + __func__, ret); + } + msm_audio_ion_free( + this_adm.sourceTrackingData.dma_buf); + this_adm.sourceTrackingData.dma_buf = NULL; + this_adm.sourceTrackingData.memmap.size = 0; + this_adm.sourceTrackingData.memmap.kvaddr = NULL; + this_adm.sourceTrackingData.memmap.paddr = 0; + this_adm.sourceTrackingData.apr_cmd_status = -1; + atomic_set(&this_adm.mem_map_handles[ + ADM_MEM_MAP_INDEX_SOURCE_TRACKING], 0); + } + + close.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + close.pkt_size = sizeof(close); + close.src_svc = APR_SVC_ADM; + close.src_domain = APR_DOMAIN_APPS; + close.src_port = port_id; + close.dest_svc = APR_SVC_ADM; + close.dest_domain = APR_DOMAIN_ADSP; + close.dest_port = copp_id; + close.token = port_idx << 16 | copp_idx; + close.opcode = ADM_CMD_DEVICE_CLOSE_V5; + + atomic_set(&this_adm.copp.id[port_idx][copp_idx], + RESET_COPP_ID); + atomic_set(&this_adm.copp.cnt[port_idx][copp_idx], 0); + atomic_set(&this_adm.copp.topology[port_idx][copp_idx], 0); + atomic_set(&this_adm.copp.mode[port_idx][copp_idx], 0); + atomic_set(&this_adm.copp.stat[port_idx][copp_idx], -1); + atomic_set(&this_adm.copp.rate[port_idx][copp_idx], 0); + atomic_set(&this_adm.copp.channels[port_idx][copp_idx], 0); + atomic_set(&this_adm.copp.bit_width[port_idx][copp_idx], 0); + atomic_set(&this_adm.copp.app_type[port_idx][copp_idx], 0); + + clear_bit(ADM_STATUS_CALIBRATION_REQUIRED, + (void *)&this_adm.copp.adm_status[port_idx][copp_idx]); + + ret = apr_send_pkt(this_adm.apr, (uint32_t *)&close); + if (ret < 0) { + pr_err("%s: ADM close failed %d\n", __func__, ret); + return -EINVAL; + } + + ret = wait_event_timeout(this_adm.copp.wait[port_idx][copp_idx], + atomic_read(&this_adm.copp.stat + [port_idx][copp_idx]) >= 0, + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: ADM cmd Route timedout for port 0x%x\n", + __func__, port_id); + return -EINVAL; + } else if (atomic_read(&this_adm.copp.stat + [port_idx][copp_idx]) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&this_adm.copp.stat + [port_idx][copp_idx]))); + return adsp_err_get_lnx_err_code( + atomic_read(&this_adm.copp.stat + [port_idx][copp_idx])); + } + } + + if (perf_mode != ULTRA_LOW_LATENCY_PCM_MODE) { + pr_debug("%s: remove adm device from rtac\n", __func__); + rtac_remove_adm_device(port_id, copp_id); + } + return 0; +} +EXPORT_SYMBOL(adm_close); + +int send_rtac_audvol_cal(void) +{ + int ret = 0; + int ret2 = 0; + int i = 0; + int copp_idx, port_idx, acdb_id, app_id, path; + struct cal_block_data *cal_block = NULL; + struct audio_cal_info_audvol *audvol_cal_info = NULL; + struct rtac_adm rtac_adm_data; + + mutex_lock(&this_adm.cal_data[ADM_RTAC_AUDVOL_CAL]->lock); + + cal_block = cal_utils_get_only_cal_block( + this_adm.cal_data[ADM_RTAC_AUDVOL_CAL]); + if (cal_block == NULL || cal_utils_is_cal_stale(cal_block)) { + pr_err("%s: can't find cal block!\n", __func__); + goto unlock; + } + + audvol_cal_info = cal_block->cal_info; + if (audvol_cal_info == NULL) { + pr_err("%s: audvol_cal_info is NULL!\n", __func__); + goto unlock; + } + + get_rtac_adm_data(&rtac_adm_data); + for (; i < rtac_adm_data.num_of_dev; i++) { + + acdb_id = rtac_adm_data.device[i].acdb_dev_id; + if (acdb_id == 0) + acdb_id = audvol_cal_info->acdb_id; + + app_id = rtac_adm_data.device[i].app_type; + if (app_id == 0) + app_id = audvol_cal_info->app_type; + + path = afe_get_port_type(rtac_adm_data.device[i].afe_port); + if ((acdb_id == audvol_cal_info->acdb_id) && + (app_id == audvol_cal_info->app_type) && + (path == audvol_cal_info->path)) { + + if (adm_get_indexes_from_copp_id(rtac_adm_data. + device[i].copp, &copp_idx, &port_idx) != 0) { + pr_debug("%s: Copp Id %d is not active\n", + __func__, + rtac_adm_data.device[i].copp); + continue; + } + + ret2 = adm_remap_and_send_cal_block(ADM_RTAC_AUDVOL_CAL, + rtac_adm_data.device[i].afe_port, + copp_idx, cal_block, + atomic_read(&this_adm.copp. + mode[port_idx][copp_idx]), + audvol_cal_info->app_type, + audvol_cal_info->acdb_id, + atomic_read(&this_adm.copp. + rate[port_idx][copp_idx])); + if (ret2 < 0) { + pr_debug("%s: remap and send failed for copp Id %d, acdb id %d, app type %d, path %d\n", + __func__, rtac_adm_data.device[i].copp, + audvol_cal_info->acdb_id, + audvol_cal_info->app_type, + audvol_cal_info->path); + ret = ret2; + } + } + } +unlock: + mutex_unlock(&this_adm.cal_data[ADM_RTAC_AUDVOL_CAL]->lock); + return ret; +} + +int adm_map_rtac_block(struct rtac_cal_block_data *cal_block) +{ + int result = 0; + + pr_debug("%s:\n", __func__); + + if (cal_block == NULL) { + pr_err("%s: cal_block is NULL!\n", + __func__); + result = -EINVAL; + goto done; + } + + if (cal_block->cal_data.paddr == 0) { + pr_debug("%s: No address to map!\n", + __func__); + result = -EINVAL; + goto done; + } + + if (cal_block->map_data.map_size == 0) { + pr_debug("%s: map size is 0!\n", + __func__); + result = -EINVAL; + goto done; + } + + /* valid port ID needed for callback use primary I2S */ + atomic_set(&this_adm.mem_map_index, ADM_RTAC_APR_CAL); + result = adm_memory_map_regions(&cal_block->cal_data.paddr, 0, + &cal_block->map_data.map_size, 1); + if (result < 0) { + pr_err("%s: RTAC mmap did not work! size = %d result %d\n", + __func__, + cal_block->map_data.map_size, result); + pr_debug("%s: RTAC mmap did not work! addr = 0x%pK, size = %d\n", + __func__, + &cal_block->cal_data.paddr, + cal_block->map_data.map_size); + goto done; + } + + cal_block->map_data.map_handle = atomic_read( + &this_adm.mem_map_handles[ADM_RTAC_APR_CAL]); +done: + return result; +} + +int adm_unmap_rtac_block(uint32_t *mem_map_handle) +{ + int result = 0; + + pr_debug("%s:\n", __func__); + + if (mem_map_handle == NULL) { + pr_debug("%s: Map handle is NULL, nothing to unmap\n", + __func__); + goto done; + } + + if (*mem_map_handle == 0) { + pr_debug("%s: Map handle is 0, nothing to unmap\n", + __func__); + goto done; + } + + if (*mem_map_handle != atomic_read( + &this_adm.mem_map_handles[ADM_RTAC_APR_CAL])) { + pr_err("%s: Map handles do not match! Unmapping RTAC, RTAC map 0x%x, ADM map 0x%x\n", + __func__, *mem_map_handle, atomic_read( + &this_adm.mem_map_handles[ADM_RTAC_APR_CAL])); + + /* if mismatch use handle passed in to unmap */ + atomic_set(&this_adm.mem_map_handles[ADM_RTAC_APR_CAL], + *mem_map_handle); + } + + /* valid port ID needed for callback use primary I2S */ + atomic_set(&this_adm.mem_map_index, ADM_RTAC_APR_CAL); + result = adm_memory_unmap_regions(); + if (result < 0) { + pr_debug("%s: adm_memory_unmap_regions failed, error %d\n", + __func__, result); + } else { + atomic_set(&this_adm.mem_map_handles[ADM_RTAC_APR_CAL], 0); + *mem_map_handle = 0; + } +done: + return result; +} + +static int get_cal_type_index(int32_t cal_type) +{ + int ret = -EINVAL; + + switch (cal_type) { + case ADM_AUDPROC_CAL_TYPE: + ret = ADM_AUDPROC_CAL; + break; + case ADM_LSM_AUDPROC_CAL_TYPE: + ret = ADM_LSM_AUDPROC_CAL; + break; + case ADM_AUDVOL_CAL_TYPE: + ret = ADM_AUDVOL_CAL; + break; + case ADM_CUST_TOPOLOGY_CAL_TYPE: + ret = ADM_CUSTOM_TOP_CAL; + break; + case ADM_RTAC_INFO_CAL_TYPE: + ret = ADM_RTAC_INFO_CAL; + break; + case ADM_RTAC_APR_CAL_TYPE: + ret = ADM_RTAC_APR_CAL; + break; + case ADM_RTAC_AUDVOL_CAL_TYPE: + ret = ADM_RTAC_AUDVOL_CAL; + break; + case ADM_LSM_AUDPROC_PERSISTENT_CAL_TYPE: + ret = ADM_LSM_AUDPROC_PERSISTENT_CAL; + break; + default: + pr_err("%s: invalid cal type %d!\n", __func__, cal_type); + } + return ret; +} + +static int adm_alloc_cal(int32_t cal_type, size_t data_size, void *data) +{ + int ret = 0; + int cal_index; + + pr_debug("%s:\n", __func__); + + cal_index = get_cal_type_index(cal_type); + if (cal_index < 0) { + pr_err("%s: could not get cal index %d!\n", + __func__, cal_index); + ret = -EINVAL; + goto done; + } + + ret = cal_utils_alloc_cal(data_size, data, + this_adm.cal_data[cal_index], 0, NULL); + if (ret < 0) { + pr_err("%s: cal_utils_alloc_block failed, ret = %d, cal type = %d!\n", + __func__, ret, cal_type); + ret = -EINVAL; + goto done; + } +done: + return ret; +} + +static int adm_dealloc_cal(int32_t cal_type, size_t data_size, void *data) +{ + int ret = 0; + int cal_index; + + pr_debug("%s:\n", __func__); + + cal_index = get_cal_type_index(cal_type); + if (cal_index < 0) { + pr_err("%s: could not get cal index %d!\n", + __func__, cal_index); + ret = -EINVAL; + goto done; + } + + ret = cal_utils_dealloc_cal(data_size, data, + this_adm.cal_data[cal_index]); + if (ret < 0) { + pr_err("%s: cal_utils_dealloc_block failed, ret = %d, cal type = %d!\n", + __func__, ret, cal_type); + ret = -EINVAL; + goto done; + } +done: + return ret; +} + +static int adm_set_cal(int32_t cal_type, size_t data_size, void *data) +{ + int ret = 0; + int cal_index; + + pr_debug("%s:\n", __func__); + + cal_index = get_cal_type_index(cal_type); + if (cal_index < 0) { + pr_err("%s: could not get cal index %d!\n", + __func__, cal_index); + ret = -EINVAL; + goto done; + } + + ret = cal_utils_set_cal(data_size, data, + this_adm.cal_data[cal_index], 0, NULL); + if (ret < 0) { + pr_err("%s: cal_utils_set_cal failed, ret = %d, cal type = %d!\n", + __func__, ret, cal_type); + ret = -EINVAL; + goto done; + } + + if (cal_index == ADM_CUSTOM_TOP_CAL) { + mutex_lock(&this_adm.cal_data[ADM_CUSTOM_TOP_CAL]->lock); + this_adm.set_custom_topology = 1; + mutex_unlock(&this_adm.cal_data[ADM_CUSTOM_TOP_CAL]->lock); + } else if (cal_index == ADM_RTAC_AUDVOL_CAL) { + send_rtac_audvol_cal(); + } +done: + return ret; +} + +static int adm_map_cal_data(int32_t cal_type, + struct cal_block_data *cal_block) +{ + int ret = 0; + int cal_index; + + pr_debug("%s:\n", __func__); + + cal_index = get_cal_type_index(cal_type); + if (cal_index < 0) { + pr_err("%s: could not get cal index %d!\n", + __func__, cal_index); + ret = -EINVAL; + goto done; + } + + atomic_set(&this_adm.mem_map_index, cal_index); + ret = adm_memory_map_regions(&cal_block->cal_data.paddr, 0, + (uint32_t *)&cal_block->map_data.map_size, 1); + if (ret < 0) { + pr_err("%s: map did not work! cal_type %i ret %d\n", + __func__, cal_index, ret); + ret = -ENODEV; + goto done; + } + cal_block->map_data.q6map_handle = atomic_read(&this_adm. + mem_map_handles[cal_index]); +done: + return ret; +} + +static int adm_unmap_cal_data(int32_t cal_type, + struct cal_block_data *cal_block) +{ + int ret = 0; + int cal_index; + + pr_debug("%s:\n", __func__); + + cal_index = get_cal_type_index(cal_type); + if (cal_index < 0) { + pr_err("%s: could not get cal index %d!\n", + __func__, cal_index); + ret = -EINVAL; + goto done; + } + + if (cal_block == NULL) { + pr_err("%s: Cal block is NULL!\n", + __func__); + goto done; + } + + if (cal_block->map_data.q6map_handle == 0) { + pr_err("%s: Map handle is NULL, nothing to unmap\n", + __func__); + goto done; + } + + atomic_set(&this_adm.mem_map_handles[cal_index], + cal_block->map_data.q6map_handle); + atomic_set(&this_adm.mem_map_index, cal_index); + ret = adm_memory_unmap_regions(); + if (ret < 0) { + pr_err("%s: unmap did not work! cal_type %i ret %d\n", + __func__, cal_index, ret); + ret = -ENODEV; + goto done; + } + cal_block->map_data.q6map_handle = 0; +done: + return ret; +} + +static void adm_delete_cal_data(void) +{ + pr_debug("%s:\n", __func__); + + cal_utils_destroy_cal_types(ADM_MAX_CAL_TYPES, this_adm.cal_data); +} + +static int adm_init_cal_data(void) +{ + int ret = 0; + struct cal_type_info cal_type_info[] = { + {{ADM_CUST_TOPOLOGY_CAL_TYPE, + {adm_alloc_cal, adm_dealloc_cal, NULL, + adm_set_cal, NULL, NULL} }, + {adm_map_cal_data, adm_unmap_cal_data, + cal_utils_match_buf_num} }, + + {{ADM_AUDPROC_CAL_TYPE, + {adm_alloc_cal, adm_dealloc_cal, NULL, + adm_set_cal, NULL, NULL} }, + {adm_map_cal_data, adm_unmap_cal_data, + cal_utils_match_buf_num} }, + + {{ADM_LSM_AUDPROC_CAL_TYPE, + {adm_alloc_cal, adm_dealloc_cal, NULL, + adm_set_cal, NULL, NULL} }, + {adm_map_cal_data, adm_unmap_cal_data, + cal_utils_match_buf_num} }, + + {{ADM_AUDVOL_CAL_TYPE, + {adm_alloc_cal, adm_dealloc_cal, NULL, + adm_set_cal, NULL, NULL} }, + {adm_map_cal_data, adm_unmap_cal_data, + cal_utils_match_buf_num} }, + + {{ADM_RTAC_INFO_CAL_TYPE, + {NULL, NULL, NULL, NULL, NULL, NULL} }, + {NULL, NULL, cal_utils_match_buf_num} }, + + {{ADM_RTAC_APR_CAL_TYPE, + {NULL, NULL, NULL, NULL, NULL, NULL} }, + {NULL, NULL, cal_utils_match_buf_num} }, + + {{SRS_TRUMEDIA_CAL_TYPE, + {NULL, NULL, NULL, NULL, NULL, NULL} }, + {NULL, NULL, cal_utils_match_buf_num} }, + + {{ADM_RTAC_AUDVOL_CAL_TYPE, + {adm_alloc_cal, adm_dealloc_cal, NULL, + adm_set_cal, NULL, NULL} }, + {adm_map_cal_data, adm_unmap_cal_data, + cal_utils_match_buf_num} }, + + {{ADM_LSM_AUDPROC_PERSISTENT_CAL_TYPE, + {adm_alloc_cal, adm_dealloc_cal, NULL, + adm_set_cal, NULL, NULL} }, + {adm_map_cal_data, adm_unmap_cal_data, + cal_utils_match_buf_num} }, + }; + pr_debug("%s:\n", __func__); + + ret = cal_utils_create_cal_types(ADM_MAX_CAL_TYPES, this_adm.cal_data, + cal_type_info); + if (ret < 0) { + pr_err("%s: could not create cal type! ret %d\n", + __func__, ret); + ret = -EINVAL; + goto err; + } + + return ret; +err: + adm_delete_cal_data(); + return ret; +} + +/** + * adm_set_volume - + * command to set volume on ADM copp + * + * @port_id: Port ID number + * @copp_idx: copp index assigned + * @volume: gain value to set + * + * Returns 0 on success or error on failure + */ +int adm_set_volume(int port_id, int copp_idx, int volume) +{ + struct audproc_volume_ctrl_master_gain audproc_vol; + struct param_hdr_v3 param_hdr; + int rc = 0; + + pr_debug("%s: port_id %d, volume %d\n", __func__, port_id, volume); + + memset(&audproc_vol, 0, sizeof(audproc_vol)); + memset(¶m_hdr, 0, sizeof(param_hdr)); + param_hdr.module_id = AUDPROC_MODULE_ID_VOL_CTRL; + param_hdr.instance_id = INSTANCE_ID_0; + param_hdr.param_id = AUDPROC_PARAM_ID_VOL_CTRL_MASTER_GAIN; + param_hdr.param_size = sizeof(audproc_vol); + + audproc_vol.master_gain = volume; + + rc = adm_pack_and_set_one_pp_param(port_id, copp_idx, param_hdr, + (uint8_t *) &audproc_vol); + if (rc) + pr_err("%s: Failed to set volume, err %d\n", __func__, rc); + + return rc; +} +EXPORT_SYMBOL(adm_set_volume); + +/** + * adm_set_softvolume - + * command to set softvolume + * + * @port_id: Port ID number + * @copp_idx: copp index assigned + * @softvol_param: Params to set for softvolume + * + * Returns 0 on success or error on failure + */ +int adm_set_softvolume(int port_id, int copp_idx, + struct audproc_softvolume_params *softvol_param) +{ + struct audproc_soft_step_volume_params audproc_softvol; + struct param_hdr_v3 param_hdr; + int rc = 0; + + pr_debug("%s: period %d step %d curve %d\n", __func__, + softvol_param->period, softvol_param->step, + softvol_param->rampingcurve); + + memset(&audproc_softvol, 0, sizeof(audproc_softvol)); + memset(¶m_hdr, 0, sizeof(param_hdr)); + param_hdr.module_id = AUDPROC_MODULE_ID_VOL_CTRL; + param_hdr.instance_id = INSTANCE_ID_0; + param_hdr.param_id = AUDPROC_PARAM_ID_SOFT_VOL_STEPPING_PARAMETERS; + param_hdr.param_size = sizeof(audproc_softvol); + + audproc_softvol.period = softvol_param->period; + audproc_softvol.step = softvol_param->step; + audproc_softvol.ramping_curve = softvol_param->rampingcurve; + + pr_debug("%s: period %d, step %d, curve %d\n", __func__, + audproc_softvol.period, audproc_softvol.step, + audproc_softvol.ramping_curve); + + rc = adm_pack_and_set_one_pp_param(port_id, copp_idx, param_hdr, + (uint8_t *) &audproc_softvol); + if (rc) + pr_err("%s: Failed to set soft volume, err %d\n", __func__, rc); + + return rc; +} +EXPORT_SYMBOL(adm_set_softvolume); + +/** + * adm_set_mic_gain - + * command to set MIC gain + * + * @port_id: Port ID number + * @copp_idx: copp index assigned + * @volume: gain value to set + * + * Returns 0 on success or error on failure + */ +int adm_set_mic_gain(int port_id, int copp_idx, int volume) +{ + struct admx_mic_gain mic_gain_params; + struct param_hdr_v3 param_hdr; + int rc = 0; + + pr_debug("%s: Setting mic gain to %d at port_id 0x%x\n", __func__, + volume, port_id); + + memset(&mic_gain_params, 0, sizeof(mic_gain_params)); + memset(¶m_hdr, 0, sizeof(param_hdr)); + param_hdr.module_id = ADM_MODULE_IDX_MIC_GAIN_CTRL; + param_hdr.instance_id = INSTANCE_ID_0; + param_hdr.param_id = ADM_PARAM_IDX_MIC_GAIN; + param_hdr.param_size = sizeof(mic_gain_params); + + mic_gain_params.tx_mic_gain = volume; + + rc = adm_pack_and_set_one_pp_param(port_id, copp_idx, param_hdr, + (uint8_t *) &mic_gain_params); + if (rc) + pr_err("%s: Failed to set mic gain, err %d\n", __func__, rc); + + return rc; +} +EXPORT_SYMBOL(adm_set_mic_gain); + +/** + * adm_send_set_multichannel_ec_primary_mic_ch - + * command to set multi-ch EC primary mic + * + * @port_id: Port ID number + * @copp_idx: copp index assigned + * @primary_mic_ch: channel number of primary mic + * + * Returns 0 on success or error on failure + */ +int adm_send_set_multichannel_ec_primary_mic_ch(int port_id, int copp_idx, + int primary_mic_ch) +{ + struct admx_sec_primary_mic_ch sec_primary_ch_params; + struct param_hdr_v3 param_hdr; + int rc = 0; + + pr_debug("%s port_id 0x%x, copp_idx 0x%x, primary_mic_ch %d\n", + __func__, port_id, copp_idx, primary_mic_ch); + + memset(&sec_primary_ch_params, 0, sizeof(sec_primary_ch_params)); + memset(¶m_hdr, 0, sizeof(param_hdr)); + param_hdr.module_id = AUDPROC_MODULE_ID_VOICE_TX_SECNS; + param_hdr.instance_id = INSTANCE_ID_0; + param_hdr.param_id = AUDPROC_PARAM_IDX_SEC_PRIMARY_MIC_CH; + param_hdr.param_size = sizeof(sec_primary_ch_params); + + sec_primary_ch_params.version = 0; + sec_primary_ch_params.sec_primary_mic_ch = primary_mic_ch; + + rc = adm_pack_and_set_one_pp_param(port_id, copp_idx, param_hdr, + (uint8_t *) &sec_primary_ch_params); + if (rc) + pr_err("%s: Failed to set primary mic chanel, err %d\n", + __func__, rc); + + return rc; +} +EXPORT_SYMBOL(adm_send_set_multichannel_ec_primary_mic_ch); + +/** + * adm_param_enable - + * command to send params to ADM for given module + * + * @port_id: Port ID number + * @copp_idx: copp index assigned + * @module_id: ADM module + * @enable: flag to enable or disable module + * + * Returns 0 on success or error on failure + */ +int adm_param_enable(int port_id, int copp_idx, int module_id, int enable) +{ + struct module_instance_info mod_inst_info; + + memset(&mod_inst_info, 0, sizeof(mod_inst_info)); + mod_inst_info.module_id = module_id; + mod_inst_info.instance_id = INSTANCE_ID_0; + + return adm_param_enable_v2(port_id, copp_idx, mod_inst_info, enable); +} +EXPORT_SYMBOL(adm_param_enable); + +/** + * adm_param_enable_v2 - + * command to send params to ADM for given module + * + * @port_id: Port ID number + * @copp_idx: copp index assigned + * @mod_inst_info: module and instance ID info + * @enable: flag to enable or disable module + * + * Returns 0 on success or error on failure + */ +int adm_param_enable_v2(int port_id, int copp_idx, + struct module_instance_info mod_inst_info, int enable) +{ + uint32_t enable_param; + struct param_hdr_v3 param_hdr; + int rc = 0; + + if (enable < 0 || enable > 1) { + pr_err("%s: Invalid value for enable %d\n", __func__, enable); + return -EINVAL; + } + + pr_debug("%s port_id %d, module_id 0x%x, instance_id 0x%x, enable %d\n", + __func__, port_id, mod_inst_info.module_id, + mod_inst_info.instance_id, enable); + + memset(¶m_hdr, 0, sizeof(param_hdr)); + param_hdr.module_id = mod_inst_info.module_id; + param_hdr.instance_id = mod_inst_info.instance_id; + param_hdr.param_id = AUDPROC_PARAM_ID_ENABLE; + param_hdr.param_size = sizeof(enable_param); + + enable_param = enable; + + rc = adm_pack_and_set_one_pp_param(port_id, copp_idx, param_hdr, + (uint8_t *) &enable_param); + if (rc) + pr_err("%s: Failed to set enable of module(%d) instance(%d) to %d, err %d\n", + __func__, mod_inst_info.module_id, + mod_inst_info.instance_id, enable, rc); + + return rc; + +} +EXPORT_SYMBOL(adm_param_enable_v2); + +/** + * adm_send_calibration - + * send ADM calibration to DSP + * + * @port_id: Port ID number + * @copp_idx: copp index assigned + * @path: direction or ADM path type + * @perf_mode: performance mode like LL/ULL/.. + * @cal_type: calibration type to use + * @params: pointer with cal data + * @size: cal size + * + * Returns 0 on success or error on failure + */ +int adm_send_calibration(int port_id, int copp_idx, int path, int perf_mode, + int cal_type, char *params, int size) +{ + + int rc = 0; + + pr_debug("%s:port_id %d, path %d, perf_mode %d, cal_type %d, size %d\n", + __func__, port_id, path, perf_mode, cal_type, size); + + /* Maps audio_dev_ctrl path definition to ACDB definition */ + if (get_cal_path(path) != RX_DEVICE) { + pr_err("%s: acdb_path %d\n", __func__, path); + rc = -EINVAL; + goto end; + } + + rc = adm_set_pp_params(port_id, copp_idx, NULL, (u8 *) params, size); + +end: + return rc; +} +EXPORT_SYMBOL(adm_send_calibration); + +/* + * adm_update_wait_parameters must be called with routing driver locks. + * adm_reset_wait_parameters must be called with routing driver locks. + * set and reset parmeters are separated to make sure it is always called + * under routing driver lock. + * adm_wait_timeout is to block until timeout or interrupted. Timeout is + * not a an error. + */ +int adm_set_wait_parameters(int port_id, int copp_idx) +{ + + int ret = 0, port_idx; + + pr_debug("%s: port_id 0x%x, copp_idx %d\n", __func__, port_id, + copp_idx); + port_id = afe_convert_virtual_to_portid(port_id); + port_idx = adm_validate_and_get_port_index(port_id); + if (port_idx < 0) { + pr_err("%s: Invalid port_id %#x\n", __func__, port_id); + ret = -EINVAL; + goto end; + } + + if (copp_idx < 0 || copp_idx >= MAX_COPPS_PER_PORT) { + pr_err("%s: Invalid copp_num: %d\n", __func__, copp_idx); + return -EINVAL; + } + + this_adm.copp.adm_delay[port_idx][copp_idx] = 1; + atomic_set(&this_adm.copp.adm_delay_stat[port_idx][copp_idx], 0); + +end: + return ret; + +} +EXPORT_SYMBOL(adm_set_wait_parameters); + +/** + * adm_reset_wait_parameters - + * reset wait parameters or ADM delay value + * + * @port_id: Port ID number + * @copp_idx: copp index assigned + * + * Returns 0 on success or error on failure + */ +int adm_reset_wait_parameters(int port_id, int copp_idx) +{ + int ret = 0, port_idx; + + pr_debug("%s: port_id 0x%x copp_idx %d\n", __func__, port_id, + copp_idx); + port_id = afe_convert_virtual_to_portid(port_id); + port_idx = adm_validate_and_get_port_index(port_id); + if (port_idx < 0) { + pr_err("%s: Invalid port_id %#x\n", __func__, port_id); + ret = -EINVAL; + goto end; + } + + if (copp_idx < 0 || copp_idx >= MAX_COPPS_PER_PORT) { + pr_err("%s: Invalid copp_num: %d\n", __func__, copp_idx); + return -EINVAL; + } + + atomic_set(&this_adm.copp.adm_delay_stat[port_idx][copp_idx], 1); + this_adm.copp.adm_delay[port_idx][copp_idx] = 0; + +end: + return ret; +} +EXPORT_SYMBOL(adm_reset_wait_parameters); + +/** + * adm_wait_timeout - + * ADM wait command after command send to DSP + * + * @port_id: Port ID number + * @copp_idx: copp index assigned + * @wait_time: value in ms for command timeout + * + * Returns 0 on success or error on failure + */ +int adm_wait_timeout(int port_id, int copp_idx, int wait_time) +{ + int ret = 0, port_idx; + + pr_debug("%s: port_id 0x%x, copp_idx %d, wait_time %d\n", __func__, + port_id, copp_idx, wait_time); + port_id = afe_convert_virtual_to_portid(port_id); + port_idx = adm_validate_and_get_port_index(port_id); + if (port_idx < 0) { + pr_err("%s: Invalid port_id %#x\n", __func__, port_id); + ret = -EINVAL; + goto end; + } + + if (copp_idx < 0 || copp_idx >= MAX_COPPS_PER_PORT) { + pr_err("%s: Invalid copp_num: %d\n", __func__, copp_idx); + return -EINVAL; + } + + ret = wait_event_timeout( + this_adm.copp.adm_delay_wait[port_idx][copp_idx], + atomic_read(&this_adm.copp.adm_delay_stat[port_idx][copp_idx]), + msecs_to_jiffies(wait_time)); + pr_debug("%s: return %d\n", __func__, ret); + if (ret != 0) + ret = -EINTR; +end: + pr_debug("%s: return %d--\n", __func__, ret); + return ret; +} +EXPORT_SYMBOL(adm_wait_timeout); + +/** + * adm_store_cal_data - + * Retrieve calibration data for ADM copp device + * + * @port_id: Port ID number + * @copp_idx: copp index assigned + * @path: direction or copp type + * @perf_mode: performance mode like LL/ULL/.. + * @cal_index: calibration index to use + * @params: pointer to store cal data + * @size: pointer to fill with cal size + * + * Returns 0 on success or error on failure + */ +int adm_store_cal_data(int port_id, int copp_idx, int path, int perf_mode, + int cal_index, char *params, int *size) +{ + int rc = 0; + struct cal_block_data *cal_block = NULL; + int app_type, acdb_id, port_idx, sample_rate; + + if (this_adm.cal_data[cal_index] == NULL) { + pr_debug("%s: cal_index %d not allocated!\n", + __func__, cal_index); + goto end; + } + + if (get_cal_path(path) != RX_DEVICE) { + pr_debug("%s: Invalid path to store calibration %d\n", + __func__, path); + rc = -EINVAL; + goto end; + } + + port_id = afe_convert_virtual_to_portid(port_id); + port_idx = adm_validate_and_get_port_index(port_id); + if (port_idx < 0) { + pr_err("%s: Invalid port_id 0x%x\n", __func__, port_id); + rc = -EINVAL; + goto end; + } + + if (copp_idx < 0 || copp_idx >= MAX_COPPS_PER_PORT) { + pr_err("%s: Invalid copp_num: %d\n", __func__, copp_idx); + return -EINVAL; + } + + acdb_id = atomic_read(&this_adm.copp.acdb_id[port_idx][copp_idx]); + app_type = atomic_read(&this_adm.copp.app_type[port_idx][copp_idx]); + sample_rate = atomic_read(&this_adm.copp.rate[port_idx][copp_idx]); + + mutex_lock(&this_adm.cal_data[cal_index]->lock); + cal_block = adm_find_cal(cal_index, get_cal_path(path), app_type, + acdb_id, sample_rate); + if (cal_block == NULL) + goto unlock; + + if (cal_block->cal_data.size <= 0) { + pr_debug("%s: No ADM cal send for port_id = 0x%x!\n", + __func__, port_id); + rc = -EINVAL; + goto unlock; + } + + if (cal_index == ADM_AUDPROC_CAL || cal_index == ADM_LSM_AUDPROC_CAL) { + if (cal_block->cal_data.size > AUD_PROC_BLOCK_SIZE) { + pr_err("%s:audproc:invalid size exp/actual[%zd, %d]\n", + __func__, cal_block->cal_data.size, *size); + rc = -ENOMEM; + goto unlock; + } + } else if (cal_index == ADM_LSM_AUDPROC_PERSISTENT_CAL) { + if (cal_block->cal_data.size > AUD_PROC_PERSIST_BLOCK_SIZE) { + pr_err("%s:persist invalid size exp/actual[%zd, %d]\n", + __func__, cal_block->cal_data.size, *size); + rc = -ENOMEM; + goto unlock; + } + } else if (cal_index == ADM_AUDVOL_CAL) { + if (cal_block->cal_data.size > AUD_VOL_BLOCK_SIZE) { + pr_err("%s:aud_vol:invalid size exp/actual[%zd, %d]\n", + __func__, cal_block->cal_data.size, *size); + rc = -ENOMEM; + goto unlock; + } + } else { + pr_debug("%s: Not valid calibration for dolby topolgy\n", + __func__); + rc = -EINVAL; + goto unlock; + } + memcpy(params, cal_block->cal_data.kvaddr, cal_block->cal_data.size); + *size = cal_block->cal_data.size; + + pr_debug("%s:port_id %d, copp_idx %d, path %d", + __func__, port_id, copp_idx, path); + pr_debug("perf_mode %d, cal_type %d, size %d\n", + perf_mode, cal_index, *size); + +unlock: + mutex_unlock(&this_adm.cal_data[cal_index]->lock); +end: + return rc; +} +EXPORT_SYMBOL(adm_store_cal_data); + +/** + * adm_send_compressed_device_mute - + * command to send mute for compressed device + * + * @port_id: Port ID number + * @copp_idx: copp index assigned + * @mute_on: flag to indicate mute or unmute + * + * Returns 0 on success or error on failure + */ +int adm_send_compressed_device_mute(int port_id, int copp_idx, bool mute_on) +{ + u32 mute_param = mute_on ? 1 : 0; + struct param_hdr_v3 param_hdr; + int ret = 0; + + pr_debug("%s port_id: 0x%x, copp_idx %d, mute_on: %d\n", + __func__, port_id, copp_idx, mute_on); + + memset(¶m_hdr, 0, sizeof(param_hdr)); + param_hdr.module_id = AUDPROC_MODULE_ID_COMPRESSED_MUTE; + param_hdr.instance_id = INSTANCE_ID_0; + param_hdr.param_id = AUDPROC_PARAM_ID_COMPRESSED_MUTE; + param_hdr.param_size = sizeof(mute_param); + + ret = adm_pack_and_set_one_pp_param(port_id, copp_idx, param_hdr, + (uint8_t *) &mute_param); + if (ret) + pr_err("%s: Failed to set mute, err %d\n", __func__, ret); + + return ret; +} +EXPORT_SYMBOL(adm_send_compressed_device_mute); + +/** + * adm_send_compressed_device_latency - + * command to send latency for compressed device + * + * @port_id: Port ID number + * @copp_idx: copp index assigned + * @latency: latency value to pass + * + * Returns 0 on success or error on failure + */ +int adm_send_compressed_device_latency(int port_id, int copp_idx, int latency) +{ + u32 latency_param; + struct param_hdr_v3 param_hdr; + int ret = 0; + + pr_debug("%s port_id: 0x%x, copp_idx %d latency: %d\n", __func__, + port_id, copp_idx, latency); + + if (latency < 0) { + pr_err("%s: Invalid value for latency %d", __func__, latency); + return -EINVAL; + } + + memset(¶m_hdr, 0, sizeof(param_hdr)); + param_hdr.module_id = AUDPROC_MODULE_ID_COMPRESSED_LATENCY; + param_hdr.instance_id = INSTANCE_ID_0; + param_hdr.param_id = AUDPROC_PARAM_ID_COMPRESSED_LATENCY; + param_hdr.param_size = sizeof(latency_param); + + latency_param = latency; + + ret = adm_pack_and_set_one_pp_param(port_id, copp_idx, param_hdr, + (uint8_t *) &latency_param); + if (ret) + pr_err("%s: Failed to set latency, err %d\n", __func__, ret); + + return ret; +} +EXPORT_SYMBOL(adm_send_compressed_device_latency); + +/** + * adm_swap_speaker_channels + * + * Receives port_id, copp_idx, sample rate, spk_swap and + * send MFC command to swap speaker channel. + * Return zero on success. On failure returns nonzero. + * + * port_id - Passed value, port_id for which channels swap is wanted + * copp_idx - Passed value, copp_idx for which channels swap is wanted + * sample_rate - Passed value, sample rate used by app type config + * spk_swap - Passed value, spk_swap for check if swap flag is set + */ +int adm_swap_speaker_channels(int port_id, int copp_idx, + int sample_rate, bool spk_swap) +{ + struct audproc_mfc_param_media_fmt mfc_cfg; + struct param_hdr_v3 param_hdr; + uint16_t num_channels; + int port_idx = 0; + int ret = 0; + + pr_debug("%s: Enter, port_id %d, copp_idx %d\n", + __func__, port_id, copp_idx); + port_id = q6audio_convert_virtual_to_portid(port_id); + port_idx = adm_validate_and_get_port_index(port_id); + if (port_idx < 0 || port_idx >= AFE_MAX_PORTS) { + pr_err("%s: Invalid port_id %#x\n", __func__, port_id); + return -EINVAL; + } else if (copp_idx < 0 || copp_idx >= MAX_COPPS_PER_PORT) { + pr_err("%s: Invalid copp_idx 0x%x\n", __func__, copp_idx); + return -EINVAL; + } + + num_channels = atomic_read(&this_adm.copp.channels[port_idx][copp_idx]); + if (num_channels != 2) { + pr_debug("%s: Invalid number of channels: %d\n", + __func__, num_channels); + return -EINVAL; + } + + memset(&mfc_cfg, 0, sizeof(mfc_cfg)); + memset(¶m_hdr, 0, sizeof(param_hdr)); + + param_hdr.module_id = AUDPROC_MODULE_ID_MFC; + param_hdr.instance_id = INSTANCE_ID_0; + param_hdr.param_id = AUDPROC_PARAM_ID_MFC_OUTPUT_MEDIA_FORMAT; + param_hdr.param_size = sizeof(mfc_cfg); + + mfc_cfg.sampling_rate = sample_rate; + mfc_cfg.bits_per_sample = + atomic_read(&this_adm.copp.bit_width[port_idx][copp_idx]); + mfc_cfg.num_channels = num_channels; + + /* Currently applying speaker swap for only 2 channel use case */ + if (spk_swap) { + mfc_cfg.channel_type[0] = + (uint16_t) PCM_CHANNEL_FR; + mfc_cfg.channel_type[1] = + (uint16_t) PCM_CHANNEL_FL; + } else { + mfc_cfg.channel_type[0] = + (uint16_t) PCM_CHANNEL_FL; + mfc_cfg.channel_type[1] = + (uint16_t) PCM_CHANNEL_FR; + } + + ret = adm_pack_and_set_one_pp_param(port_id, copp_idx, param_hdr, + (u8 *) &mfc_cfg); + if (ret < 0) { + pr_err("%s: Failed to set swap speaker channels on port[0x%x] failed %d\n", + __func__, port_id, ret); + return ret; + } + + pr_debug("%s: mfc_cfg Set params returned success", __func__); + return 0; +} +EXPORT_SYMBOL(adm_swap_speaker_channels); + +/** + * adm_set_sound_focus - + * Update sound focus info + * + * @port_id: Port ID number + * @copp_idx: copp index assigned + * @soundFocusData: sound focus data to pass + * + * Returns 0 on success or error on failure + */ +int adm_set_sound_focus(int port_id, int copp_idx, + struct sound_focus_param soundFocusData) +{ + struct adm_param_fluence_soundfocus_t soundfocus_params; + struct param_hdr_v3 param_hdr; + int ret = 0; + int i; + + pr_debug("%s: Enter, port_id %d, copp_idx %d\n", + __func__, port_id, copp_idx); + + memset(¶m_hdr, 0, sizeof(param_hdr)); + param_hdr.module_id = VOICEPROC_MODULE_ID_FLUENCE_PRO_VC_TX; + param_hdr.instance_id = INSTANCE_ID_0; + param_hdr.param_id = VOICEPROC_PARAM_ID_FLUENCE_SOUNDFOCUS; + param_hdr.param_size = sizeof(soundfocus_params); + + memset(&(soundfocus_params), 0xFF, sizeof(soundfocus_params)); + for (i = 0; i < MAX_SECTORS; i++) { + soundfocus_params.start_angles[i] = + soundFocusData.start_angle[i]; + soundfocus_params.enables[i] = soundFocusData.enable[i]; + pr_debug("%s: start_angle[%d] = %d\n", + __func__, i, soundFocusData.start_angle[i]); + pr_debug("%s: enable[%d] = %d\n", + __func__, i, soundFocusData.enable[i]); + } + soundfocus_params.gain_step = soundFocusData.gain_step; + pr_debug("%s: gain_step = %d\n", __func__, soundFocusData.gain_step); + + soundfocus_params.reserved = 0; + + ret = adm_pack_and_set_one_pp_param(port_id, copp_idx, param_hdr, + (uint8_t *) &soundfocus_params); + if (ret) + pr_err("%s: Failed to set sound focus params, err %d\n", + __func__, ret); + + pr_debug("%s: Exit, ret=%d\n", __func__, ret); + + return ret; +} +EXPORT_SYMBOL(adm_set_sound_focus); + +/** + * adm_get_sound_focus - + * Retrieve sound focus info + * + * @port_id: Port ID number + * @copp_idx: copp index assigned + * @soundFocusData: pointer for sound focus data to be updated with + * + * Returns 0 on success or error on failure + */ +int adm_get_sound_focus(int port_id, int copp_idx, + struct sound_focus_param *soundFocusData) +{ + int ret = 0, i; + char *params_value; + uint32_t max_param_size = 0; + struct adm_param_fluence_soundfocus_t *soundfocus_params = NULL; + struct param_hdr_v3 param_hdr; + + pr_debug("%s: Enter, port_id %d, copp_idx %d\n", + __func__, port_id, copp_idx); + + max_param_size = sizeof(struct adm_param_fluence_soundfocus_t) + + sizeof(union param_hdrs); + params_value = kzalloc(max_param_size, GFP_KERNEL); + if (!params_value) + return -ENOMEM; + + memset(¶m_hdr, 0, sizeof(param_hdr)); + param_hdr.module_id = VOICEPROC_MODULE_ID_FLUENCE_PRO_VC_TX; + param_hdr.instance_id = INSTANCE_ID_0; + param_hdr.param_id = VOICEPROC_PARAM_ID_FLUENCE_SOUNDFOCUS; + param_hdr.param_size = max_param_size; + ret = adm_get_pp_params(port_id, copp_idx, + ADM_CLIENT_ID_SOURCE_TRACKING, NULL, ¶m_hdr, + params_value); + if (ret) { + pr_err("%s: get parameters failed ret:%d\n", __func__, ret); + ret = -EINVAL; + goto done; + } + + if (this_adm.sourceTrackingData.apr_cmd_status != 0) { + pr_err("%s - get params returned error [%s]\n", + __func__, adsp_err_get_err_str( + this_adm.sourceTrackingData.apr_cmd_status)); + ret = adsp_err_get_lnx_err_code( + this_adm.sourceTrackingData.apr_cmd_status); + goto done; + } + + soundfocus_params = (struct adm_param_fluence_soundfocus_t *) + params_value; + for (i = 0; i < MAX_SECTORS; i++) { + soundFocusData->start_angle[i] = + soundfocus_params->start_angles[i]; + soundFocusData->enable[i] = soundfocus_params->enables[i]; + pr_debug("%s: start_angle[%d] = %d\n", + __func__, i, soundFocusData->start_angle[i]); + pr_debug("%s: enable[%d] = %d\n", + __func__, i, soundFocusData->enable[i]); + } + soundFocusData->gain_step = soundfocus_params->gain_step; + pr_debug("%s: gain_step = %d\n", __func__, soundFocusData->gain_step); + +done: + pr_debug("%s: Exit, ret = %d\n", __func__, ret); + + kfree(params_value); + return ret; +} +EXPORT_SYMBOL(adm_get_sound_focus); + +static int adm_source_tracking_alloc_map_memory(void) +{ + int ret; + + pr_debug("%s: Enter\n", __func__); + + ret = msm_audio_ion_alloc(&this_adm.sourceTrackingData.dma_buf, + AUD_PROC_BLOCK_SIZE, + &this_adm.sourceTrackingData.memmap.paddr, + &this_adm.sourceTrackingData.memmap.size, + &this_adm.sourceTrackingData.memmap.kvaddr); + if (ret) { + pr_err("%s: failed to allocate memory\n", __func__); + + ret = -EINVAL; + goto done; + } + + atomic_set(&this_adm.mem_map_index, ADM_MEM_MAP_INDEX_SOURCE_TRACKING); + ret = adm_memory_map_regions(&this_adm.sourceTrackingData.memmap.paddr, + 0, + (uint32_t *)&this_adm.sourceTrackingData.memmap.size, + 1); + if (ret < 0) { + pr_err("%s: failed to map memory, paddr = 0x%pK, size = %d\n", + __func__, + (void *)this_adm.sourceTrackingData.memmap.paddr, + (uint32_t)this_adm.sourceTrackingData.memmap.size); + + msm_audio_ion_free(this_adm.sourceTrackingData.dma_buf); + this_adm.sourceTrackingData.dma_buf = NULL; + this_adm.sourceTrackingData.memmap.size = 0; + this_adm.sourceTrackingData.memmap.kvaddr = NULL; + this_adm.sourceTrackingData.memmap.paddr = 0; + this_adm.sourceTrackingData.apr_cmd_status = -1; + atomic_set(&this_adm.mem_map_handles + [ADM_MEM_MAP_INDEX_SOURCE_TRACKING], 0); + + ret = -EINVAL; + goto done; + } + ret = 0; + pr_debug("%s: paddr = 0x%pK, size = %d, mem_map_handle = 0x%x\n", + __func__, (void *)this_adm.sourceTrackingData.memmap.paddr, + (uint32_t)this_adm.sourceTrackingData.memmap.size, + atomic_read(&this_adm.mem_map_handles + [ADM_MEM_MAP_INDEX_SOURCE_TRACKING])); + +done: + pr_debug("%s: Exit, ret = %d\n", __func__, ret); + + return ret; +} + +/** + * adm_get_source_tracking - + * Retrieve source tracking info + * + * @port_id: Port ID number + * @copp_idx: copp index assigned + * @sourceTrackingData: pointer for source track data to be updated with + * + * Returns 0 on success or error on failure + */ +int adm_get_source_tracking(int port_id, int copp_idx, + struct source_tracking_param *sourceTrackingData) +{ + struct adm_param_fluence_sourcetracking_t *source_tracking_params = + NULL; + struct mem_mapping_hdr mem_hdr; + struct param_hdr_v3 param_hdr; + int i = 0; + int ret = 0; + + pr_debug("%s: Enter, port_id %d, copp_idx %d\n", + __func__, port_id, copp_idx); + + if (!this_adm.sourceTrackingData.memmap.paddr) { + /* Allocate and map shared memory for out of band usage */ + ret = adm_source_tracking_alloc_map_memory(); + if (ret != 0) { + ret = -EINVAL; + goto done; + } + } + + memset(&mem_hdr, 0, sizeof(mem_hdr)); + memset(¶m_hdr, 0, sizeof(param_hdr)); + mem_hdr.data_payload_addr_lsw = + lower_32_bits(this_adm.sourceTrackingData.memmap.paddr); + mem_hdr.data_payload_addr_msw = msm_audio_populate_upper_32_bits( + this_adm.sourceTrackingData.memmap.paddr); + mem_hdr.mem_map_handle = atomic_read( + &this_adm.mem_map_handles[ADM_MEM_MAP_INDEX_SOURCE_TRACKING]); + + param_hdr.module_id = VOICEPROC_MODULE_ID_FLUENCE_PRO_VC_TX; + param_hdr.instance_id = INSTANCE_ID_0; + param_hdr.param_id = VOICEPROC_PARAM_ID_FLUENCE_SOURCETRACKING; + /* + * This size should be the max size of the calibration data + header. + * Use the union size to ensure max size is used. + */ + param_hdr.param_size = + sizeof(struct adm_param_fluence_sourcetracking_t) + + sizeof(union param_hdrs); + + /* + * Retrieving parameters out of band, so no need to provide a buffer for + * the returned parameter data as it will be at the memory location + * provided. + */ + ret = adm_get_pp_params(port_id, copp_idx, + ADM_CLIENT_ID_SOURCE_TRACKING, &mem_hdr, + ¶m_hdr, NULL); + if (ret) { + pr_err("%s: Failed to get params, error %d\n", __func__, ret); + goto done; + } + + if (this_adm.sourceTrackingData.apr_cmd_status != 0) { + pr_err("%s - get params returned error [%s]\n", + __func__, adsp_err_get_err_str( + this_adm.sourceTrackingData.apr_cmd_status)); + + ret = adsp_err_get_lnx_err_code( + this_adm.sourceTrackingData.apr_cmd_status); + goto done; + } + + /* How do we know what the param data was retrieved with for hdr size */ + source_tracking_params = + (struct adm_param_fluence_sourcetracking_t + *) (this_adm.sourceTrackingData.memmap.kvaddr + + sizeof(struct param_hdr_v1)); + for (i = 0; i < MAX_SECTORS; i++) { + sourceTrackingData->vad[i] = source_tracking_params->vad[i]; + pr_debug("%s: vad[%d] = %d\n", + __func__, i, sourceTrackingData->vad[i]); + } + sourceTrackingData->doa_speech = source_tracking_params->doa_speech; + pr_debug("%s: doa_speech = %d\n", + __func__, sourceTrackingData->doa_speech); + + for (i = 0; i < MAX_NOISE_SOURCE_INDICATORS; i++) { + sourceTrackingData->doa_noise[i] = + source_tracking_params->doa_noise[i]; + pr_debug("%s: doa_noise[%d] = %d\n", + __func__, i, sourceTrackingData->doa_noise[i]); + } + for (i = 0; i < MAX_POLAR_ACTIVITY_INDICATORS; i++) { + sourceTrackingData->polar_activity[i] = + source_tracking_params->polar_activity[i]; + pr_debug("%s: polar_activity[%d] = %d\n", + __func__, i, sourceTrackingData->polar_activity[i]); + } + + ret = 0; + +done: + pr_debug("%s: Exit, ret=%d\n", __func__, ret); + + return ret; +} +EXPORT_SYMBOL(adm_get_source_tracking); + +int __init adm_init(void) +{ + int i = 0, j; + + this_adm.ec_ref_rx = -1; + init_waitqueue_head(&this_adm.matrix_map_wait); + init_waitqueue_head(&this_adm.adm_wait); + + for (i = 0; i < AFE_MAX_PORTS; i++) { + for (j = 0; j < MAX_COPPS_PER_PORT; j++) { + atomic_set(&this_adm.copp.id[i][j], RESET_COPP_ID); + init_waitqueue_head(&this_adm.copp.wait[i][j]); + init_waitqueue_head( + &this_adm.copp.adm_delay_wait[i][j]); + } + } + + if (adm_init_cal_data()) + pr_err("%s: could not init cal data!\n", __func__); + + this_adm.sourceTrackingData.dma_buf = NULL; + this_adm.sourceTrackingData.memmap.size = 0; + this_adm.sourceTrackingData.memmap.kvaddr = NULL; + this_adm.sourceTrackingData.memmap.paddr = 0; + this_adm.sourceTrackingData.apr_cmd_status = -1; + + return 0; +} + +void adm_exit(void) +{ + if (this_adm.apr) + adm_reset_data(); + adm_delete_cal_data(); +} diff --git a/audio-kernel/dsp/q6afe.c b/audio-kernel/dsp/q6afe.c new file mode 100644 index 000000000..4fcd67f74 --- /dev/null +++ b/audio-kernel/dsp/q6afe.c @@ -0,0 +1,8840 @@ +/* Copyright (c) 2012-2019, 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "adsp_err.h" +#include "q6afecal-hwdep.h" + +#define WAKELOCK_TIMEOUT 5000 +enum { + AFE_COMMON_RX_CAL = 0, + AFE_COMMON_TX_CAL, + AFE_LSM_TX_CAL, + AFE_AANC_CAL, + AFE_FB_SPKR_PROT_CAL, + AFE_HW_DELAY_CAL, + AFE_SIDETONE_CAL, + AFE_SIDETONE_IIR_CAL, + AFE_TOPOLOGY_CAL, + AFE_LSM_TOPOLOGY_CAL, + AFE_CUST_TOPOLOGY_CAL, + AFE_FB_SPKR_PROT_TH_VI_CAL, + AFE_FB_SPKR_PROT_EX_VI_CAL, + MAX_AFE_CAL_TYPES +}; + +enum fbsp_state { + FBSP_INCORRECT_OP_MODE, + FBSP_INACTIVE, + FBSP_WARMUP, + FBSP_IN_PROGRESS, + FBSP_SUCCESS, + FBSP_FAILED, + MAX_FBSP_STATE +}; + +static char fbsp_state[MAX_FBSP_STATE][50] = { + [FBSP_INCORRECT_OP_MODE] = "incorrect operation mode", + [FBSP_INACTIVE] = "port not started", + [FBSP_WARMUP] = "waiting for warmup", + [FBSP_IN_PROGRESS] = "in progress state", + [FBSP_SUCCESS] = "success", + [FBSP_FAILED] = "failed" +}; + +enum v_vali_state { + V_VALI_FAILED, + V_VALI_SUCCESS, + V_VALI_INCORRECT_OP_MODE, + V_VALI_INACTIVE, + V_VALI_WARMUP, + V_VALI_IN_PROGRESS, + MAX_V_VALI_STATE +}; + +enum { + USE_CALIBRATED_R0TO, + USE_SAFE_R0TO +}; + +enum { + QUICK_CALIB_DISABLE, + QUICK_CALIB_ENABLE +}; + +enum { + Q6AFE_MSM_SPKR_PROCESSING = 0, + Q6AFE_MSM_SPKR_CALIBRATION, + Q6AFE_MSM_SPKR_FTM_MODE, + Q6AFE_MSM_SPKR_V_VALI_MODE +}; + +struct wlock { + struct wakeup_source ws; +}; + +static struct wlock wl; + +struct afe_ctl { + void *apr; + atomic_t state; + atomic_t status; + wait_queue_head_t wait[AFE_MAX_PORTS]; + wait_queue_head_t wait_wakeup; + wait_queue_head_t lpass_core_hw_wait; + uint32_t lpass_hw_core_client_hdl; + void (*tx_cb)(uint32_t opcode, + uint32_t token, uint32_t *payload, void *priv); + void (*rx_cb)(uint32_t opcode, + uint32_t token, uint32_t *payload, void *priv); + void *tx_private_data; + void *rx_private_data; + uint32_t mmap_handle; + + void (*pri_spdif_tx_cb)(uint32_t opcode, + uint32_t token, uint32_t *payload, void *priv); + void (*sec_spdif_tx_cb)(uint32_t opcode, + uint32_t token, uint32_t *payload, void *priv); + void *pri_spdif_tx_private_data; + void *sec_spdif_tx_private_data; + int pri_spdif_config_change; + int sec_spdif_config_change; + struct work_struct afe_spdif_work; + + int topology[AFE_MAX_PORTS]; + struct cal_type_data *cal_data[MAX_AFE_CAL_TYPES]; + + atomic_t mem_map_cal_handles[MAX_AFE_CAL_TYPES]; + atomic_t mem_map_cal_index; + u32 afe_cal_mode[AFE_MAX_PORTS]; + + u16 dtmf_gen_rx_portid; + struct audio_cal_info_spk_prot_cfg prot_cfg; + struct afe_spkr_prot_calib_get_resp calib_data; + struct audio_cal_info_sp_th_vi_ftm_cfg th_ftm_cfg; + struct audio_cal_info_sp_th_vi_v_vali_cfg v_vali_cfg; + struct audio_cal_info_sp_ex_vi_ftm_cfg ex_ftm_cfg; + struct afe_sp_th_vi_get_param_resp th_vi_resp; + struct afe_sp_th_vi_v_vali_get_param_resp th_vi_v_vali_resp; + struct afe_sp_ex_vi_get_param_resp ex_vi_resp; + struct afe_sp_rx_tmax_xmax_logging_resp xt_logging_resp; + struct afe_av_dev_drift_get_param_resp av_dev_drift_resp; + int vi_tx_port; + int vi_rx_port; + uint32_t afe_sample_rates[AFE_MAX_PORTS]; + struct aanc_data aanc_info; + struct mutex afe_cmd_lock; + int set_custom_topology; + int dev_acdb_id[AFE_MAX_PORTS]; + routing_cb rt_cb; + struct audio_uevent_data *uevent_data; + /* cal info for AFE */ + struct afe_fw_info *fw_data; + u32 island_mode[AFE_MAX_PORTS]; + struct vad_config vad_cfg[AFE_MAX_PORTS]; + struct work_struct afe_dc_work; + struct notifier_block event_notifier; + /* FTM spk params */ + uint32_t initial_cal; + uint32_t v_vali_flag; +}; + +static atomic_t afe_ports_mad_type[SLIMBUS_PORT_LAST - SLIMBUS_0_RX]; +static unsigned long afe_configured_cmd; + +static struct afe_ctl this_afe; + +#define TIMEOUT_MS 1000 +#define Q6AFE_MAX_VOLUME 0x3FFF + +static int pcm_afe_instance[2]; +static int proxy_afe_instance[2]; +bool afe_close_done[2] = {true, true}; + +#define SIZEOF_CFG_CMD(y) \ + (sizeof(struct apr_hdr) + sizeof(u16) + (sizeof(struct y))) + +static int afe_get_cal_hw_delay(int32_t path, + struct audio_cal_hw_delay_entry *entry); +static int remap_cal_data(struct cal_block_data *cal_block, int cal_index); + +int afe_get_spk_initial_cal(void) +{ + return this_afe.initial_cal; +} + +void afe_get_spk_r0(int *spk_r0) +{ + uint16_t i = 0; + + for (; i < SP_V2_NUM_MAX_SPKRS; i++) + spk_r0[i] = this_afe.prot_cfg.r0[i]; +} + +void afe_get_spk_t0(int *spk_t0) +{ + uint16_t i = 0; + + for (; i < SP_V2_NUM_MAX_SPKRS; i++) + spk_t0[i] = this_afe.prot_cfg.t0[i]; +} + +int afe_get_spk_v_vali_flag(void) +{ + return this_afe.v_vali_flag; +} + +void afe_get_spk_v_vali_sts(int *spk_v_vali_sts) +{ + uint16_t i = 0; + + for (; i < SP_V2_NUM_MAX_SPKRS; i++) + spk_v_vali_sts[i] = + this_afe.th_vi_v_vali_resp.param.status[i]; +} + +void afe_set_spk_initial_cal(int initial_cal) +{ + this_afe.initial_cal = initial_cal; +} + +void afe_set_spk_v_vali_flag(int v_vali_flag) +{ + this_afe.v_vali_flag = v_vali_flag; +} + +int afe_get_topology(int port_id) +{ + int topology; + int port_index = afe_get_port_index(port_id); + + if ((port_index < 0) || (port_index >= AFE_MAX_PORTS)) { + pr_err("%s: Invalid port index %d\n", __func__, port_index); + topology = -EINVAL; + goto done; + } + + topology = this_afe.topology[port_index]; +done: + return topology; +} + +/** + * afe_set_aanc_info - + * Update AFE AANC info + * + * @q6_aanc_info: AFE AANC info params + * + */ +void afe_set_aanc_info(struct aanc_data *q6_aanc_info) +{ + this_afe.aanc_info.aanc_active = q6_aanc_info->aanc_active; + this_afe.aanc_info.aanc_rx_port = q6_aanc_info->aanc_rx_port; + this_afe.aanc_info.aanc_tx_port = q6_aanc_info->aanc_tx_port; + + pr_debug("%s: aanc active is %d rx port is 0x%x, tx port is 0x%x\n", + __func__, + this_afe.aanc_info.aanc_active, + this_afe.aanc_info.aanc_rx_port, + this_afe.aanc_info.aanc_tx_port); +} +EXPORT_SYMBOL(afe_set_aanc_info); + +static void afe_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 void av_dev_drift_afe_cb_handler(uint32_t opcode, uint32_t *payload, + uint32_t payload_size) +{ + u32 param_id; + size_t expected_size = + sizeof(u32) + sizeof(struct afe_param_id_dev_timing_stats); + + /* Get param ID depending on command type */ + param_id = (opcode == AFE_PORT_CMDRSP_GET_PARAM_V3) ? payload[3] : + payload[2]; + if (param_id != AFE_PARAM_ID_DEV_TIMING_STATS) { + pr_err("%s: Unrecognized param ID %d\n", __func__, param_id); + return; + } + + switch (opcode) { + case AFE_PORT_CMDRSP_GET_PARAM_V2: + expected_size += sizeof(struct param_hdr_v1); + if (payload_size < expected_size) { + pr_err("%s: Error: received size %d, expected size %zu\n", + __func__, payload_size, expected_size); + return; + } + /* Repack response to add IID */ + this_afe.av_dev_drift_resp.status = payload[0]; + this_afe.av_dev_drift_resp.pdata.module_id = payload[1]; + this_afe.av_dev_drift_resp.pdata.instance_id = INSTANCE_ID_0; + this_afe.av_dev_drift_resp.pdata.param_id = payload[2]; + this_afe.av_dev_drift_resp.pdata.param_size = payload[3]; + memcpy(&this_afe.av_dev_drift_resp.timing_stats, &payload[4], + sizeof(struct afe_param_id_dev_timing_stats)); + break; + case AFE_PORT_CMDRSP_GET_PARAM_V3: + expected_size += sizeof(struct param_hdr_v3); + if (payload_size < expected_size) { + pr_err("%s: Error: received size %d, expected size %zu\n", + __func__, payload_size, expected_size); + return; + } + memcpy(&this_afe.av_dev_drift_resp, payload, + sizeof(this_afe.av_dev_drift_resp)); + break; + default: + pr_err("%s: Unrecognized command %d\n", __func__, opcode); + return; + } + + if (!this_afe.av_dev_drift_resp.status) { + atomic_set(&this_afe.state, 0); + } else { + pr_debug("%s: av_dev_drift_resp status: %d", __func__, + this_afe.av_dev_drift_resp.status); + atomic_set(&this_afe.state, -1); + } +} + +static int32_t sp_make_afe_callback(uint32_t opcode, uint32_t *payload, + uint32_t payload_size) +{ + struct param_hdr_v3 param_hdr; + u32 *data_dest = NULL; + u32 *data_start = NULL; + size_t expected_size = sizeof(u32); + + memset(¶m_hdr, 0, sizeof(param_hdr)); + + /* Set command specific details */ + switch (opcode) { + case AFE_PORT_CMDRSP_GET_PARAM_V2: + if (payload_size < (5 * sizeof(uint32_t))) { + pr_err("%s: Error: size %d is less than expected\n", + __func__, payload_size); + return -EINVAL; + } + expected_size += sizeof(struct param_hdr_v1); + param_hdr.module_id = payload[1]; + param_hdr.instance_id = INSTANCE_ID_0; + param_hdr.param_id = payload[2]; + param_hdr.param_size = payload[3]; + data_start = &payload[4]; + break; + case AFE_PORT_CMDRSP_GET_PARAM_V3: + if (payload_size < (6 * sizeof(uint32_t))) { + pr_err("%s: Error: size %d is less than expected\n", + __func__, payload_size); + return -EINVAL; + } + expected_size += sizeof(struct param_hdr_v3); + memcpy(¶m_hdr, &payload[1], sizeof(struct param_hdr_v3)); + data_start = &payload[5]; + break; + default: + pr_err("%s: Unrecognized command %d\n", __func__, opcode); + return -EINVAL; + } + + switch (param_hdr.param_id) { + case AFE_PARAM_ID_CALIB_RES_CFG_V2: + expected_size += sizeof(struct asm_calib_res_cfg); + data_dest = (u32 *) &this_afe.calib_data; + break; + case AFE_PARAM_ID_SP_V2_TH_VI_FTM_PARAMS: + expected_size += sizeof(struct afe_sp_th_vi_ftm_params); + data_dest = (u32 *) &this_afe.th_vi_resp; + break; + case AFE_PARAM_ID_SP_V2_TH_VI_V_VALI_PARAMS: + expected_size += sizeof(struct afe_sp_th_vi_v_vali_params); + data_dest = (u32 *) &this_afe.th_vi_v_vali_resp; + break; + case AFE_PARAM_ID_SP_V2_EX_VI_FTM_PARAMS: + expected_size += sizeof(struct afe_sp_ex_vi_ftm_params); + data_dest = (u32 *) &this_afe.ex_vi_resp; + break; + case AFE_PARAM_ID_SP_RX_TMAX_XMAX_LOGGING: + expected_size += sizeof( + struct afe_sp_rx_tmax_xmax_logging_param); + data_dest = (u32 *) &this_afe.xt_logging_resp; + break; + default: + pr_err("%s: Unrecognized param ID %d\n", __func__, + param_hdr.param_id); + return -EINVAL; + } + + if (payload_size < expected_size) { + pr_err("%s: Error: received size %d, expected size %zu for param %d\n", + __func__, payload_size, expected_size, + param_hdr.param_id); + return -EINVAL; + } + + data_dest[0] = payload[0]; + memcpy(&data_dest[1], ¶m_hdr, sizeof(struct param_hdr_v3)); + memcpy(&data_dest[5], data_start, param_hdr.param_size); + + if (!data_dest[0]) { + atomic_set(&this_afe.state, 0); + } else { + pr_debug("%s: status: %d", __func__, data_dest[0]); + atomic_set(&this_afe.state, -1); + } + + return 0; +} + +static void afe_notify_dc_presence(void) +{ + pr_debug("%s: DC detected\n", __func__); + msm_aud_evt_notifier_call_chain(MSM_AUD_DC_EVENT, NULL); + + schedule_work(&this_afe.afe_dc_work); +} + +static void afe_notify_dc_presence_work_fn(struct work_struct *work) +{ + int ret = 0; + char event[] = "DC_PRESENCE=TRUE"; + + ret = q6core_send_uevent(this_afe.uevent_data, event); + if (ret) + pr_err("%s: Send UEvent %s failed :%d\n", + __func__, event, ret); +} + +static int afe_aud_event_notify(struct notifier_block *self, + unsigned long action, void *data) +{ + switch (action) { + case SWR_WAKE_IRQ_REGISTER: + afe_send_cmd_wakeup_register(data, true); + break; + case SWR_WAKE_IRQ_DEREGISTER: + afe_send_cmd_wakeup_register(data, false); + break; + default: + pr_err("%s: invalid event type: %lu\n", __func__, action); + return -EINVAL; + } + + return 0; +} + +static void afe_notify_spdif_fmt_update_work_fn(struct work_struct *work) +{ + int ret = 0; + char event_pri[] = "PRI_SPDIF_TX=MEDIA_CONFIG_CHANGE"; + char event_sec[] = "SEC_SPDIF_TX=MEDIA_CONFIG_CHANGE"; + + if (this_afe.pri_spdif_config_change) { + this_afe.pri_spdif_config_change = 0; + ret = q6core_send_uevent(this_afe.uevent_data, event_pri); + if (ret) + pr_err("%s: Send UEvent %s failed :%d\n", + __func__, event_pri, ret); + } + if (this_afe.sec_spdif_config_change) { + this_afe.sec_spdif_config_change = 0; + ret = q6core_send_uevent(this_afe.uevent_data, event_sec); + if (ret) + pr_err("%s: Send UEvent %s failed :%d\n", + __func__, event_sec, ret); + } +} + +static void afe_notify_spdif_fmt_update(void *payload) +{ + struct afe_port_mod_evt_rsp_hdr *evt_pl; + + evt_pl = (struct afe_port_mod_evt_rsp_hdr *)payload; + if (evt_pl->port_id == AFE_PORT_ID_PRIMARY_SPDIF_TX) + this_afe.pri_spdif_config_change = 1; + else + this_afe.sec_spdif_config_change = 1; + + schedule_work(&this_afe.afe_spdif_work); +} + +static bool afe_token_is_valid(uint32_t token) +{ + if (token >= AFE_MAX_PORTS) { + pr_err("%s: token %d is invalid.\n", __func__, token); + return false; + } + return true; +} + +static int32_t afe_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_afe.apr); + + cal_utils_clear_cal_block_q6maps(MAX_AFE_CAL_TYPES, + this_afe.cal_data); + + /* Reset the custom topology mode: to resend again to AFE. */ + mutex_lock(&this_afe.cal_data[AFE_CUST_TOPOLOGY_CAL]->lock); + this_afe.set_custom_topology = 1; + mutex_unlock(&this_afe.cal_data[AFE_CUST_TOPOLOGY_CAL]->lock); + rtac_clear_mapping(AFE_RTAC_CAL); + + if (this_afe.apr) { + apr_reset(this_afe.apr); + atomic_set(&this_afe.state, 0); + this_afe.apr = NULL; + rtac_set_afe_handle(this_afe.apr); + } + + /* + * Pass reset events to proxy driver, if cb is registered + */ + if (data->payload_size < sizeof(uint32_t)) { + pr_err("%s: Error: size %d is less than expected\n", + __func__, data->payload_size); + return -EINVAL; + } + + if (this_afe.tx_cb) { + this_afe.tx_cb(data->opcode, data->token, + data->payload, + this_afe.tx_private_data); + this_afe.tx_cb = NULL; + } + if (this_afe.rx_cb) { + this_afe.rx_cb(data->opcode, data->token, + data->payload, + this_afe.rx_private_data); + this_afe.rx_cb = NULL; + } + + return 0; + } + afe_callback_debug_print(data); + if (data->opcode == AFE_PORT_CMDRSP_GET_PARAM_V2 || + data->opcode == AFE_PORT_CMDRSP_GET_PARAM_V3) { + uint32_t *payload = data->payload; + uint32_t param_id; + uint32_t param_id_pos = 0; + + 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 (data->opcode == AFE_PORT_CMDRSP_GET_PARAM_V3) + param_id_pos = 4; + else + param_id_pos = 3; + + if (data->payload_size >= param_id_pos * sizeof(uint32_t)) + param_id = payload[param_id_pos - 1]; + else { + pr_err("%s: Error: size %d is less than expected\n", + __func__, data->payload_size); + return -EINVAL; + } + + if (param_id == AFE_PARAM_ID_DEV_TIMING_STATS) { + av_dev_drift_afe_cb_handler(data->opcode, data->payload, + data->payload_size); + } else { + if (rtac_make_afe_callback(data->payload, + data->payload_size)) + return 0; + + if (sp_make_afe_callback(data->opcode, data->payload, + data->payload_size)) + return -EINVAL; + } + if (afe_token_is_valid(data->token)) + wake_up(&this_afe.wait[data->token]); + else + return -EINVAL; + } else if (data->opcode == AFE_EVENT_MBHC_DETECTION_SW_WA) { + msm_aud_evt_notifier_call_chain(SWR_WAKE_IRQ_EVENT, NULL); + } else if (data->opcode == + AFE_CMD_RSP_REMOTE_LPASS_CORE_HW_VOTE_REQUEST) { + uint32_t *payload = data->payload; + + pr_debug("%s: LPASS_CORE_HW_VOTE_REQUEST handle %d\n", + __func__, payload[0]); + this_afe.lpass_hw_core_client_hdl = payload[0]; + atomic_set(&this_afe.state, 0); + atomic_set(&this_afe.status, 0); + wake_up(&this_afe.lpass_core_hw_wait); + } else if (data->payload_size) { + uint32_t *payload; + uint16_t port_id = 0; + + payload = data->payload; + if (data->opcode == APR_BASIC_RSP_RESULT) { + if (data->payload_size < (2 * sizeof(uint32_t))) { + pr_err("%s: Error: size %d is less than expected\n", + __func__, data->payload_size); + return -EINVAL; + } + 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_afe.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_SET_PARAM_V3: + if (rtac_make_afe_callback(payload, + data->payload_size)) + return 0; + case AFE_PORT_CMD_DEVICE_STOP: + case AFE_PORT_CMD_DEVICE_START: + case AFE_PSEUDOPORT_CMD_START: + case AFE_PSEUDOPORT_CMD_STOP: + case AFE_SERVICE_CMD_SHARED_MEM_MAP_REGIONS: + case AFE_SERVICE_CMD_SHARED_MEM_UNMAP_REGIONS: + case AFE_SERVICE_CMD_UNREGISTER_RT_PORT_DRIVER: + case AFE_PORTS_CMD_DTMF_CTL: + case AFE_SVC_CMD_SET_PARAM: + case AFE_SVC_CMD_SET_PARAM_V2: + case AFE_PORT_CMD_MOD_EVENT_CFG: + atomic_set(&this_afe.state, 0); + if (afe_token_is_valid(data->token)) + wake_up(&this_afe.wait[data->token]); + else + return -EINVAL; + break; + case AFE_SERVICE_CMD_REGISTER_RT_PORT_DRIVER: + break; + case AFE_PORT_DATA_CMD_RT_PROXY_PORT_WRITE_V2: + port_id = RT_PROXY_PORT_001_TX; + break; + case AFE_PORT_DATA_CMD_RT_PROXY_PORT_READ_V2: + port_id = RT_PROXY_PORT_001_RX; + break; + case AFE_CMD_ADD_TOPOLOGIES: + atomic_set(&this_afe.state, 0); + if (afe_token_is_valid(data->token)) + wake_up(&this_afe.wait[data->token]); + else + return -EINVAL; + pr_debug("%s: AFE_CMD_ADD_TOPOLOGIES cmd 0x%x\n", + __func__, payload[1]); + break; + case AFE_PORT_CMD_GET_PARAM_V2: + case AFE_PORT_CMD_GET_PARAM_V3: + /* + * Should only come here if there is an APR + * error or malformed APR packet. Otherwise + * response will be returned as + * AFE_PORT_CMDRSP_GET_PARAM_V2/3 + */ + pr_debug("%s: AFE Get Param opcode 0x%x token 0x%x src %d dest %d\n", + __func__, data->opcode, data->token, + data->src_port, data->dest_port); + if (payload[1] != 0) { + pr_err("%s: AFE Get Param failed with error %d\n", + __func__, payload[1]); + if (rtac_make_afe_callback( + payload, + data->payload_size)) + return 0; + } + atomic_set(&this_afe.state, payload[1]); + if (afe_token_is_valid(data->token)) + wake_up(&this_afe.wait[data->token]); + else + return -EINVAL; + break; + case AFE_CMD_REMOTE_LPASS_CORE_HW_VOTE_REQUEST: + case AFE_CMD_REMOTE_LPASS_CORE_HW_DEVOTE_REQUEST: + atomic_set(&this_afe.state, 0); + wake_up(&this_afe.lpass_core_hw_wait); + break; + case AFE_SVC_CMD_EVENT_CFG: + atomic_set(&this_afe.state, payload[1]); + wake_up(&this_afe.wait_wakeup); + 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_debug("%s: mmap_handle: 0x%x, cal index %d\n", + __func__, payload[0], + atomic_read(&this_afe.mem_map_cal_index)); + if (atomic_read(&this_afe.mem_map_cal_index) != -1) + atomic_set(&this_afe.mem_map_cal_handles[ + atomic_read( + &this_afe.mem_map_cal_index)], + (uint32_t)payload[0]); + else + this_afe.mmap_handle = payload[0]; + atomic_set(&this_afe.state, 0); + if (afe_token_is_valid(data->token)) + wake_up(&this_afe.wait[data->token]); + else + return -EINVAL; + } else if (data->opcode == AFE_EVENT_RT_PROXY_PORT_STATUS) { + port_id = (uint16_t)(0x0000FFFF & payload[0]); + } else if (data->opcode == AFE_PORT_MOD_EVENT) { + u32 flag_dc_presence[2]; + uint32_t *payload = data->payload; + struct afe_port_mod_evt_rsp_hdr *evt_pl = + (struct afe_port_mod_evt_rsp_hdr *)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 ((evt_pl->module_id == AFE_MODULE_SPEAKER_PROTECTION_V2_EX_VI) && + (evt_pl->event_id == AFE_PORT_SP_DC_DETECTION_EVENT) && + (evt_pl->payload_size == sizeof(flag_dc_presence))) { + + memcpy(&flag_dc_presence, + (uint8_t *)payload + + sizeof(struct afe_port_mod_evt_rsp_hdr), + evt_pl->payload_size); + if (flag_dc_presence[0] == 1 || + flag_dc_presence[1] == 1) { + afe_notify_dc_presence(); + } + } else if (evt_pl->port_id == AFE_PORT_ID_PRIMARY_SPDIF_TX) { + if (this_afe.pri_spdif_tx_cb) { + this_afe.pri_spdif_tx_cb(data->opcode, + data->token, data->payload, + this_afe.pri_spdif_tx_private_data); + } + afe_notify_spdif_fmt_update(data->payload); + } else if (evt_pl->port_id == AFE_PORT_ID_SECONDARY_SPDIF_TX) { + if (this_afe.sec_spdif_tx_cb) { + this_afe.sec_spdif_tx_cb(data->opcode, + data->token, data->payload, + this_afe.sec_spdif_tx_private_data); + } + afe_notify_spdif_fmt_update(data->payload); + } else { + pr_debug("%s: mod ID = 0x%x event_id = 0x%x\n", + __func__, evt_pl->module_id, + evt_pl->event_id); + } + } + pr_debug("%s: port_id = 0x%x\n", __func__, port_id); + switch (port_id) { + case RT_PROXY_PORT_001_TX: { + if (this_afe.tx_cb) { + this_afe.tx_cb(data->opcode, data->token, + data->payload, + this_afe.tx_private_data); + } + break; + } + case RT_PROXY_PORT_001_RX: { + if (this_afe.rx_cb) { + this_afe.rx_cb(data->opcode, data->token, + data->payload, + this_afe.rx_private_data); + } + break; + } + default: + pr_debug("%s: default case 0x%x\n", __func__, port_id); + break; + } + } + return 0; +} + +/** + * afe_get_port_type - + * Retrieve AFE port type whether RX or TX + * + * @port_id: AFE Port ID number + * + * Returns RX/TX type. + */ +int afe_get_port_type(u16 port_id) +{ + int ret = MSM_AFE_PORT_TYPE_RX; + + switch (port_id) { + case VOICE_RECORD_RX: + case VOICE_RECORD_TX: + ret = MSM_AFE_PORT_TYPE_TX; + break; + case VOICE_PLAYBACK_TX: + case VOICE2_PLAYBACK_TX: + ret = MSM_AFE_PORT_TYPE_RX; + break; + default: + /* Odd numbered ports are TX and Rx are Even numbered */ + if (port_id & 0x1) + ret = MSM_AFE_PORT_TYPE_TX; + else + ret = MSM_AFE_PORT_TYPE_RX; + break; + } + + return ret; +} +EXPORT_SYMBOL(afe_get_port_type); + +int afe_sizeof_cfg_cmd(u16 port_id) +{ + int ret_size; + + switch (port_id) { + case PRIMARY_I2S_RX: + case PRIMARY_I2S_TX: + case SECONDARY_I2S_RX: + case SECONDARY_I2S_TX: + case MI2S_RX: + case MI2S_TX: + case AFE_PORT_ID_PRIMARY_MI2S_RX: + case AFE_PORT_ID_PRIMARY_MI2S_TX: + case AFE_PORT_ID_QUATERNARY_MI2S_RX: + case AFE_PORT_ID_QUATERNARY_MI2S_TX: + case AFE_PORT_ID_QUINARY_MI2S_RX: + case AFE_PORT_ID_QUINARY_MI2S_TX: + ret_size = SIZEOF_CFG_CMD(afe_param_id_i2s_cfg); + break; + case HDMI_RX: + case DISPLAY_PORT_RX: + ret_size = + SIZEOF_CFG_CMD(afe_param_id_hdmi_multi_chan_audio_cfg); + break; + case AFE_PORT_ID_PRIMARY_SPDIF_RX: + case AFE_PORT_ID_PRIMARY_SPDIF_TX: + case AFE_PORT_ID_SECONDARY_SPDIF_RX: + case AFE_PORT_ID_SECONDARY_SPDIF_TX: + ret_size = + SIZEOF_CFG_CMD(afe_param_id_spdif_cfg_v2); + break; + case SLIMBUS_0_RX: + case SLIMBUS_0_TX: + case SLIMBUS_1_RX: + case SLIMBUS_1_TX: + case SLIMBUS_2_RX: + case SLIMBUS_2_TX: + case SLIMBUS_3_RX: + case SLIMBUS_3_TX: + case SLIMBUS_4_RX: + case SLIMBUS_4_TX: + case SLIMBUS_5_RX: + case SLIMBUS_5_TX: + case SLIMBUS_6_RX: + case SLIMBUS_6_TX: + case SLIMBUS_7_RX: + case SLIMBUS_7_TX: + case SLIMBUS_8_RX: + case SLIMBUS_8_TX: + case SLIMBUS_9_RX: + case SLIMBUS_9_TX: + ret_size = SIZEOF_CFG_CMD(afe_param_id_slimbus_cfg); + break; + case VOICE_PLAYBACK_TX: + case VOICE2_PLAYBACK_TX: + case VOICE_RECORD_RX: + case VOICE_RECORD_TX: + ret_size = SIZEOF_CFG_CMD(afe_param_id_pseudo_port_cfg); + break; + case RT_PROXY_PORT_001_RX: + case RT_PROXY_PORT_001_TX: + ret_size = SIZEOF_CFG_CMD(afe_param_id_rt_proxy_port_cfg); + break; + case AFE_PORT_ID_USB_RX: + case AFE_PORT_ID_USB_TX: + ret_size = SIZEOF_CFG_CMD(afe_param_id_usb_audio_cfg); + break; + case AFE_PORT_ID_WSA_CODEC_DMA_RX_0: + case AFE_PORT_ID_WSA_CODEC_DMA_TX_0: + case AFE_PORT_ID_WSA_CODEC_DMA_RX_1: + case AFE_PORT_ID_WSA_CODEC_DMA_TX_1: + case AFE_PORT_ID_WSA_CODEC_DMA_TX_2: + case AFE_PORT_ID_VA_CODEC_DMA_TX_0: + case AFE_PORT_ID_VA_CODEC_DMA_TX_1: + case AFE_PORT_ID_RX_CODEC_DMA_RX_0: + case AFE_PORT_ID_TX_CODEC_DMA_TX_0: + case AFE_PORT_ID_RX_CODEC_DMA_RX_1: + case AFE_PORT_ID_TX_CODEC_DMA_TX_1: + case AFE_PORT_ID_RX_CODEC_DMA_RX_2: + case AFE_PORT_ID_TX_CODEC_DMA_TX_2: + case AFE_PORT_ID_RX_CODEC_DMA_RX_3: + case AFE_PORT_ID_TX_CODEC_DMA_TX_3: + case AFE_PORT_ID_RX_CODEC_DMA_RX_4: + case AFE_PORT_ID_TX_CODEC_DMA_TX_4: + case AFE_PORT_ID_RX_CODEC_DMA_RX_5: + case AFE_PORT_ID_TX_CODEC_DMA_TX_5: + case AFE_PORT_ID_RX_CODEC_DMA_RX_6: + case AFE_PORT_ID_RX_CODEC_DMA_RX_7: + ret_size = SIZEOF_CFG_CMD(afe_param_id_cdc_dma_cfg_t); + break; + case AFE_PORT_ID_PRIMARY_PCM_RX: + case AFE_PORT_ID_PRIMARY_PCM_TX: + case AFE_PORT_ID_SECONDARY_PCM_RX: + case AFE_PORT_ID_SECONDARY_PCM_TX: + case AFE_PORT_ID_TERTIARY_PCM_RX: + case AFE_PORT_ID_TERTIARY_PCM_TX: + case AFE_PORT_ID_QUATERNARY_PCM_RX: + case AFE_PORT_ID_QUATERNARY_PCM_TX: + case AFE_PORT_ID_QUINARY_PCM_RX: + case AFE_PORT_ID_QUINARY_PCM_TX: + case AFE_PORT_ID_SENARY_PCM_RX: + case AFE_PORT_ID_SENARY_PCM_TX: + default: + pr_debug("%s: default case 0x%x\n", __func__, port_id); + ret_size = SIZEOF_CFG_CMD(afe_param_id_pcm_cfg); + break; + } + return ret_size; +} + +/** + * afe_q6_interface_prepare - + * wrapper API to check Q6 AFE registered to APR otherwise registers + * + * Returns 0 on success or error on failure. + */ +int afe_q6_interface_prepare(void) +{ + int ret = 0; + + pr_debug("%s:\n", __func__); + + if (this_afe.apr == NULL) { + this_afe.apr = apr_register("ADSP", "AFE", afe_callback, + 0xFFFFFFFF, &this_afe); + if (this_afe.apr == NULL) { + pr_err("%s: Unable to register AFE\n", __func__); + ret = -ENETRESET; + } + rtac_set_afe_handle(this_afe.apr); + } + return ret; +} +EXPORT_SYMBOL(afe_q6_interface_prepare); + +/* + * afe_apr_send_pkt : returns 0 on success, negative otherwise. + */ +static int afe_apr_send_pkt(void *data, wait_queue_head_t *wait) +{ + int ret; + + if (wait) + atomic_set(&this_afe.state, 1); + atomic_set(&this_afe.status, 0); + ret = apr_send_pkt(this_afe.apr, data); + if (ret > 0) { + if (wait) { + ret = wait_event_timeout(*wait, + (atomic_read(&this_afe.state) == 0), + msecs_to_jiffies(2 * TIMEOUT_MS)); + if (!ret) { + ret = -ETIMEDOUT; + } else if (atomic_read(&this_afe.status) > 0) { + pr_err("%s: DSP returned error[%s]\n", __func__, + adsp_err_get_err_str(atomic_read( + &this_afe.status))); + ret = adsp_err_get_lnx_err_code( + atomic_read(&this_afe.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; +} + +/* This function shouldn't be called directly. Instead call q6afe_set_params. */ +static int q6afe_set_params_v2(u16 port_id, int index, + struct mem_mapping_hdr *mem_hdr, + u8 *packed_param_data, u32 packed_data_size) +{ + struct afe_port_cmd_set_param_v2 *set_param = NULL; + uint32_t size = sizeof(struct afe_port_cmd_set_param_v2); + int rc = 0; + + if (packed_param_data != NULL) + size += packed_data_size; + set_param = kzalloc(size, GFP_KERNEL); + if (set_param == NULL) + return -ENOMEM; + + set_param->apr_hdr.hdr_field = + APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + set_param->apr_hdr.pkt_size = size; + set_param->apr_hdr.src_port = 0; + set_param->apr_hdr.dest_port = 0; + set_param->apr_hdr.token = index; + set_param->apr_hdr.opcode = AFE_PORT_CMD_SET_PARAM_V2; + set_param->port_id = port_id; + if (packed_data_size > U16_MAX) { + pr_err("%s: Invalid data size for set params V2 %d\n", __func__, + packed_data_size); + rc = -EINVAL; + goto done; + } + set_param->payload_size = packed_data_size; + if (mem_hdr != NULL) { + set_param->mem_hdr = *mem_hdr; + } else if (packed_param_data != NULL) { + memcpy(&set_param->param_data, packed_param_data, + packed_data_size); + } else { + pr_err("%s: Both memory header and param data are NULL\n", + __func__); + rc = -EINVAL; + goto done; + } + + rc = afe_apr_send_pkt(set_param, &this_afe.wait[index]); +done: + kfree(set_param); + return rc; +} + +/* This function shouldn't be called directly. Instead call q6afe_set_params. */ +static int q6afe_set_params_v3(u16 port_id, int index, + struct mem_mapping_hdr *mem_hdr, + u8 *packed_param_data, u32 packed_data_size) +{ + struct afe_port_cmd_set_param_v3 *set_param = NULL; + uint32_t size = sizeof(struct afe_port_cmd_set_param_v3); + int rc = 0; + + if (packed_param_data != NULL) + size += packed_data_size; + set_param = kzalloc(size, GFP_KERNEL); + if (set_param == NULL) + return -ENOMEM; + + set_param->apr_hdr.hdr_field = + APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + set_param->apr_hdr.pkt_size = size; + set_param->apr_hdr.src_port = 0; + set_param->apr_hdr.dest_port = 0; + set_param->apr_hdr.token = index; + set_param->apr_hdr.opcode = AFE_PORT_CMD_SET_PARAM_V3; + set_param->port_id = port_id; + set_param->payload_size = packed_data_size; + if (mem_hdr != NULL) { + set_param->mem_hdr = *mem_hdr; + } else if (packed_param_data != NULL) { + memcpy(&set_param->param_data, packed_param_data, + packed_data_size); + } else { + pr_err("%s: Both memory header and param data are NULL\n", + __func__); + rc = -EINVAL; + goto done; + } + + rc = afe_apr_send_pkt(set_param, &this_afe.wait[index]); +done: + kfree(set_param); + return rc; +} + +static int q6afe_set_params(u16 port_id, int index, + struct mem_mapping_hdr *mem_hdr, + u8 *packed_param_data, u32 packed_data_size) +{ + int ret = 0; + + ret = afe_q6_interface_prepare(); + if (ret != 0) { + pr_err("%s: Q6 interface prepare failed %d\n", __func__, ret); + return ret; + } + + port_id = q6audio_get_port_id(port_id); + ret = q6audio_validate_port(port_id); + if (ret < 0) { + pr_err("%s: Not a valid port id = 0x%x ret %d\n", __func__, + port_id, ret); + return -EINVAL; + } + + if (index < 0 || index >= AFE_MAX_PORTS) { + pr_err("%s: AFE port index[%d] invalid\n", __func__, index); + return -EINVAL; + } + + if (q6common_is_instance_id_supported()) + return q6afe_set_params_v3(port_id, index, mem_hdr, + packed_param_data, packed_data_size); + else + return q6afe_set_params_v2(port_id, index, mem_hdr, + packed_param_data, packed_data_size); +} + +static int q6afe_pack_and_set_param_in_band(u16 port_id, int index, + struct param_hdr_v3 param_hdr, + u8 *param_data) +{ + u8 *packed_param_data = NULL; + int packed_data_size = sizeof(union param_hdrs) + param_hdr.param_size; + int ret; + + packed_param_data = kzalloc(packed_data_size, GFP_KERNEL); + if (packed_param_data == NULL) + return -ENOMEM; + + ret = q6common_pack_pp_params(packed_param_data, ¶m_hdr, param_data, + &packed_data_size); + if (ret) { + pr_err("%s: Failed to pack param header and data, error %d\n", + __func__, ret); + goto fail_cmd; + } + + ret = q6afe_set_params(port_id, index, NULL, packed_param_data, + packed_data_size); + +fail_cmd: + kfree(packed_param_data); + return ret; +} + +static int q6afe_set_aanc_level(void) +{ + struct param_hdr_v3 param_hdr; + struct afe_param_id_aanc_noise_reduction aanc_noise_level; + int ret = 0; + uint16_t tx_port = 0; + + if (!this_afe.aanc_info.aanc_active) + return -EINVAL; + + pr_debug("%s: level: %d\n", __func__, this_afe.aanc_info.level); + memset(&aanc_noise_level, 0, sizeof(aanc_noise_level)); + aanc_noise_level.minor_version = 1; + aanc_noise_level.ad_beta = this_afe.aanc_info.level; + + memset(¶m_hdr, 0, sizeof(param_hdr)); + param_hdr.module_id = AFE_MODULE_AANC; + param_hdr.instance_id = INSTANCE_ID_0; + param_hdr.param_id = AFE_PARAM_ID_AANC_NOISE_REDUCTION; + param_hdr.param_size = sizeof(struct afe_param_id_aanc_noise_reduction); + + tx_port = this_afe.aanc_info.aanc_tx_port; + ret = q6afe_pack_and_set_param_in_band(tx_port, + q6audio_get_port_index(tx_port), + param_hdr, + (u8 *) &aanc_noise_level); + if (ret) + pr_err("%s: AANC noise level enable failed for tx_port 0x%x ret %d\n", + __func__, tx_port, ret); + return ret; +} + +/** + * afe_set_aanc_noise_level - controls aanc noise reduction strength + * + * @level: Noise level to be controlled + * + * Returns 0 on success or error on failure. + */ +int afe_set_aanc_noise_level(int level) +{ + int ret = 0; + + if (this_afe.aanc_info.level == level) + return ret; + + mutex_lock(&this_afe.afe_cmd_lock); + this_afe.aanc_info.level = level; + ret = q6afe_set_aanc_level(); + mutex_unlock(&this_afe.afe_cmd_lock); + + return ret; +} +EXPORT_SYMBOL(afe_set_aanc_noise_level); + +/* This function shouldn't be called directly. Instead call q6afe_get_param. */ +static int q6afe_get_params_v2(u16 port_id, int index, + struct mem_mapping_hdr *mem_hdr, + struct param_hdr_v3 *param_hdr) +{ + struct afe_port_cmd_get_param_v2 afe_get_param; + u32 param_size = param_hdr->param_size; + + memset(&afe_get_param, 0, sizeof(afe_get_param)); + afe_get_param.apr_hdr.hdr_field = + APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + afe_get_param.apr_hdr.pkt_size = sizeof(afe_get_param) + param_size; + afe_get_param.apr_hdr.src_port = 0; + afe_get_param.apr_hdr.dest_port = 0; + afe_get_param.apr_hdr.token = index; + afe_get_param.apr_hdr.opcode = AFE_PORT_CMD_GET_PARAM_V2; + afe_get_param.port_id = port_id; + afe_get_param.payload_size = sizeof(struct param_hdr_v1) + param_size; + if (mem_hdr != NULL) + afe_get_param.mem_hdr = *mem_hdr; + /* Set MID and PID in command */ + afe_get_param.module_id = param_hdr->module_id; + afe_get_param.param_id = param_hdr->param_id; + /* Set param header in payload */ + afe_get_param.param_hdr.module_id = param_hdr->module_id; + afe_get_param.param_hdr.param_id = param_hdr->param_id; + afe_get_param.param_hdr.param_size = param_size; + + return afe_apr_send_pkt(&afe_get_param, &this_afe.wait[index]); +} + +/* This function shouldn't be called directly. Instead call q6afe_get_param. */ +static int q6afe_get_params_v3(u16 port_id, int index, + struct mem_mapping_hdr *mem_hdr, + struct param_hdr_v3 *param_hdr) +{ + struct afe_port_cmd_get_param_v3 afe_get_param; + + memset(&afe_get_param, 0, sizeof(afe_get_param)); + afe_get_param.apr_hdr.hdr_field = + APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + afe_get_param.apr_hdr.pkt_size = sizeof(afe_get_param); + afe_get_param.apr_hdr.src_port = 0; + afe_get_param.apr_hdr.dest_port = 0; + afe_get_param.apr_hdr.token = index; + afe_get_param.apr_hdr.opcode = AFE_PORT_CMD_GET_PARAM_V3; + afe_get_param.port_id = port_id; + if (mem_hdr != NULL) + afe_get_param.mem_hdr = *mem_hdr; + /* Set param header in command, no payload in V3 */ + afe_get_param.param_hdr = *param_hdr; + + return afe_apr_send_pkt(&afe_get_param, &this_afe.wait[index]); +} + +/* + * Calling functions copy param data directly from this_afe. Do not copy data + * back to caller here. + */ +static int q6afe_get_params(u16 port_id, struct mem_mapping_hdr *mem_hdr, + struct param_hdr_v3 *param_hdr) +{ + int index; + int ret; + + ret = afe_q6_interface_prepare(); + if (ret != 0) { + pr_err("%s: Q6 interface prepare failed %d\n", __func__, ret); + return ret; + } + + port_id = q6audio_get_port_id(port_id); + ret = q6audio_validate_port(port_id); + if (ret < 0) { + pr_err("%s: Not a valid port id = 0x%x ret %d\n", __func__, + port_id, ret); + return -EINVAL; + } + + 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; + } + + if (q6common_is_instance_id_supported()) + return q6afe_get_params_v3(port_id, index, NULL, param_hdr); + else + return q6afe_get_params_v2(port_id, index, NULL, param_hdr); +} + +/* + * This function shouldn't be called directly. Instead call + * q6afe_svc_set_params. + */ +static int q6afe_svc_set_params_v1(int index, struct mem_mapping_hdr *mem_hdr, + u8 *packed_param_data, u32 packed_data_size) +{ + struct afe_svc_cmd_set_param_v1 *svc_set_param = NULL; + uint32_t size = sizeof(struct afe_svc_cmd_set_param_v1); + int rc = 0; + + if (packed_param_data != NULL) + size += packed_data_size; + svc_set_param = kzalloc(size, GFP_KERNEL); + if (svc_set_param == NULL) + return -ENOMEM; + + svc_set_param->apr_hdr.hdr_field = + APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + svc_set_param->apr_hdr.pkt_size = size; + svc_set_param->apr_hdr.src_port = 0; + svc_set_param->apr_hdr.dest_port = 0; + svc_set_param->apr_hdr.token = index; + svc_set_param->apr_hdr.opcode = AFE_SVC_CMD_SET_PARAM; + svc_set_param->payload_size = packed_data_size; + + if (mem_hdr != NULL) { + /* Out of band case. */ + svc_set_param->mem_hdr = *mem_hdr; + } else if (packed_param_data != NULL) { + /* In band case. */ + memcpy(&svc_set_param->param_data, packed_param_data, + packed_data_size); + } else { + pr_err("%s: Both memory header and param data are NULL\n", + __func__); + rc = -EINVAL; + goto done; + } + + rc = afe_apr_send_pkt(svc_set_param, &this_afe.wait[index]); +done: + kfree(svc_set_param); + return rc; +} + +/* + * This function shouldn't be called directly. Instead call + * q6afe_svc_set_params. + */ +static int q6afe_svc_set_params_v2(int index, struct mem_mapping_hdr *mem_hdr, + u8 *packed_param_data, u32 packed_data_size) +{ + struct afe_svc_cmd_set_param_v2 *svc_set_param = NULL; + uint16_t size = sizeof(struct afe_svc_cmd_set_param_v2); + int rc = 0; + + if (packed_param_data != NULL) + size += packed_data_size; + svc_set_param = kzalloc(size, GFP_KERNEL); + if (svc_set_param == NULL) + return -ENOMEM; + + svc_set_param->apr_hdr.hdr_field = + APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + svc_set_param->apr_hdr.pkt_size = size; + svc_set_param->apr_hdr.src_port = 0; + svc_set_param->apr_hdr.dest_port = 0; + svc_set_param->apr_hdr.token = index; + svc_set_param->apr_hdr.opcode = AFE_SVC_CMD_SET_PARAM_V2; + svc_set_param->payload_size = packed_data_size; + + if (mem_hdr != NULL) { + /* Out of band case. */ + svc_set_param->mem_hdr = *mem_hdr; + } else if (packed_param_data != NULL) { + /* In band case. */ + memcpy(&svc_set_param->param_data, packed_param_data, + packed_data_size); + } else { + pr_err("%s: Both memory header and param data are NULL\n", + __func__); + rc = -EINVAL; + goto done; + } + + rc = afe_apr_send_pkt(svc_set_param, &this_afe.wait[index]); +done: + kfree(svc_set_param); + return rc; +} + +static int q6afe_svc_set_params(int index, struct mem_mapping_hdr *mem_hdr, + u8 *packed_param_data, u32 packed_data_size) +{ + int ret; + + ret = afe_q6_interface_prepare(); + if (ret != 0) { + pr_err("%s: Q6 interface prepare failed %d\n", __func__, ret); + return ret; + } + + if (q6common_is_instance_id_supported()) + return q6afe_svc_set_params_v2(index, mem_hdr, + packed_param_data, + packed_data_size); + else + return q6afe_svc_set_params_v1(index, mem_hdr, + packed_param_data, + packed_data_size); +} + +static int q6afe_svc_pack_and_set_param_in_band(int index, + struct param_hdr_v3 param_hdr, + u8 *param_data) +{ + u8 *packed_param_data = NULL; + u32 packed_data_size = + sizeof(struct param_hdr_v3) + param_hdr.param_size; + int ret = 0; + + packed_param_data = kzalloc(packed_data_size, GFP_KERNEL); + if (!packed_param_data) + return -ENOMEM; + + ret = q6common_pack_pp_params(packed_param_data, ¶m_hdr, param_data, + &packed_data_size); + if (ret) { + pr_err("%s: Failed to pack parameter header and data, error %d\n", + __func__, ret); + goto done; + } + + ret = q6afe_svc_set_params(index, NULL, packed_param_data, + packed_data_size); + +done: + kfree(packed_param_data); + return ret; +} + +static int afe_send_cal_block(u16 port_id, struct cal_block_data *cal_block) +{ + struct mem_mapping_hdr mem_hdr; + int payload_size = 0; + int result = 0; + + memset(&mem_hdr, 0, sizeof(mem_hdr)); + + if (!cal_block) { + pr_debug("%s: No AFE cal to send!\n", __func__); + result = -EINVAL; + goto done; + } + if (cal_block->cal_data.size <= 0) { + pr_debug("%s: AFE cal has invalid size!\n", __func__); + result = -EINVAL; + goto done; + } + + payload_size = cal_block->cal_data.size; + mem_hdr.data_payload_addr_lsw = + lower_32_bits(cal_block->cal_data.paddr); + mem_hdr.data_payload_addr_msw = + msm_audio_populate_upper_32_bits(cal_block->cal_data.paddr); + mem_hdr.mem_map_handle = cal_block->map_data.q6map_handle; + + pr_debug("%s: AFE cal sent for device port = 0x%x, cal size = %zd, cal addr = 0x%pK\n", + __func__, port_id, + cal_block->cal_data.size, &cal_block->cal_data.paddr); + + result = q6afe_set_params(port_id, q6audio_get_port_index(port_id), + &mem_hdr, NULL, payload_size); + if (result) + pr_err("%s: AFE cal for port 0x%x failed %d\n", + __func__, port_id, result); + +done: + return result; +} + + +static int afe_send_custom_topology_block(struct cal_block_data *cal_block) +{ + int result = 0; + int index = 0; + struct cmd_set_topologies afe_cal; + + if (!cal_block) { + pr_err("%s: No AFE SVC cal to send!\n", __func__); + return -EINVAL; + } + if (cal_block->cal_data.size <= 0) { + pr_err("%s: AFE SVC cal has invalid size: %zd!\n", + __func__, cal_block->cal_data.size); + return -EINVAL; + } + + afe_cal.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + afe_cal.hdr.pkt_size = sizeof(afe_cal); + afe_cal.hdr.src_port = 0; + afe_cal.hdr.dest_port = 0; + afe_cal.hdr.token = index; + afe_cal.hdr.opcode = AFE_CMD_ADD_TOPOLOGIES; + + afe_cal.payload_size = cal_block->cal_data.size; + afe_cal.payload_addr_lsw = + lower_32_bits(cal_block->cal_data.paddr); + afe_cal.payload_addr_msw = + msm_audio_populate_upper_32_bits(cal_block->cal_data.paddr); + afe_cal.mem_map_handle = cal_block->map_data.q6map_handle; + + pr_debug("%s:cmd_id:0x%x calsize:%zd memmap_hdl:0x%x caladdr:0x%pK", + __func__, AFE_CMD_ADD_TOPOLOGIES, cal_block->cal_data.size, + afe_cal.mem_map_handle, &cal_block->cal_data.paddr); + + result = afe_apr_send_pkt(&afe_cal, &this_afe.wait[index]); + if (result) + pr_err("%s: AFE send topology for command 0x%x failed %d\n", + __func__, AFE_CMD_ADD_TOPOLOGIES, result); + + return result; +} + +static void afe_send_custom_topology(void) +{ + struct cal_block_data *cal_block = NULL; + int cal_index = AFE_CUST_TOPOLOGY_CAL; + int ret; + + if (this_afe.cal_data[cal_index] == NULL) { + pr_err("%s: cal_index %d not allocated!\n", + __func__, cal_index); + return; + } + mutex_lock(&this_afe.cal_data[cal_index]->lock); + + if (!this_afe.set_custom_topology) + goto unlock; + this_afe.set_custom_topology = 0; + cal_block = cal_utils_get_only_cal_block(this_afe.cal_data[cal_index]); + if (cal_block == NULL || cal_utils_is_cal_stale(cal_block)) { + pr_err("%s cal_block not found!!\n", __func__); + goto unlock; + } + + pr_debug("%s: Sending cal_index cal %d\n", __func__, cal_index); + + ret = remap_cal_data(cal_block, cal_index); + if (ret) { + pr_err("%s: Remap_cal_data failed for cal %d!\n", + __func__, cal_index); + goto unlock; + } + ret = afe_send_custom_topology_block(cal_block); + if (ret < 0) { + pr_err("%s: No cal sent for cal_index %d! ret %d\n", + __func__, cal_index, ret); + goto unlock; + } + pr_debug("%s:sent custom topology for AFE\n", __func__); +unlock: + mutex_unlock(&this_afe.cal_data[cal_index]->lock); +} + +static int afe_spk_ramp_dn_cfg(int port) +{ + struct param_hdr_v3 param_info; + int ret = -EINVAL; + + memset(¶m_info, 0, sizeof(param_info)); + + if (afe_get_port_type(port) != MSM_AFE_PORT_TYPE_RX) { + pr_debug("%s: port doesn't match 0x%x\n", __func__, port); + return 0; + } + if (this_afe.prot_cfg.mode == MSM_SPKR_PROT_DISABLED || + (this_afe.vi_rx_port != port)) { + pr_debug("%s: spkr protection disabled port 0x%x %d 0x%x\n", + __func__, port, ret, this_afe.vi_rx_port); + return 0; + } + param_info.module_id = AFE_MODULE_FB_SPKR_PROT_V2_RX; + param_info.instance_id = INSTANCE_ID_0; + param_info.param_id = AFE_PARAM_ID_FBSP_PTONE_RAMP_CFG; + param_info.param_size = 0; + + ret = q6afe_pack_and_set_param_in_band(port, + q6audio_get_port_index(port), + param_info, NULL); + if (ret) { + pr_err("%s: Failed to set speaker ramp duration param, err %d\n", + __func__, ret); + goto fail_cmd; + } + + /* dsp needs atleast 15ms to ramp down pilot tone*/ + usleep_range(15000, 15010); + ret = 0; +fail_cmd: + pr_debug("%s: config.pdata.param_id 0x%x status %d\n", __func__, + param_info.param_id, ret); + return ret; +} + +static int afe_spk_prot_prepare(int src_port, int dst_port, int param_id, + union afe_spkr_prot_config *prot_config) +{ + struct param_hdr_v3 param_info; + int ret = -EINVAL; + + memset(¶m_info, 0, sizeof(param_info)); + + ret = q6audio_validate_port(src_port); + if (ret < 0) { + pr_err("%s: Invalid src port 0x%x ret %d", __func__, src_port, + ret); + ret = -EINVAL; + goto fail_cmd; + } + ret = q6audio_validate_port(dst_port); + if (ret < 0) { + pr_err("%s: Invalid dst port 0x%x ret %d", __func__, + dst_port, ret); + ret = -EINVAL; + goto fail_cmd; + } + + switch (param_id) { + case AFE_PARAM_ID_FBSP_MODE_RX_CFG: + case AFE_PARAM_ID_SP_RX_LIMITER_TH: + param_info.module_id = AFE_MODULE_FB_SPKR_PROT_V2_RX; + break; + case AFE_PARAM_ID_FEEDBACK_PATH_CFG: + this_afe.vi_tx_port = src_port; + this_afe.vi_rx_port = dst_port; + param_info.module_id = AFE_MODULE_FEEDBACK; + break; + /* + * AFE_PARAM_ID_SPKR_CALIB_VI_PROC_CFG_V2 is same as + * AFE_PARAM_ID_SP_V2_TH_VI_MODE_CFG. V_VALI_CFG uses + * same module TH_VI. + */ + case AFE_PARAM_ID_SPKR_CALIB_VI_PROC_CFG_V2: + case AFE_PARAM_ID_SP_V2_TH_VI_FTM_CFG: + case AFE_PARAM_ID_SP_V2_TH_VI_V_VALI_CFG: + param_info.module_id = AFE_MODULE_SPEAKER_PROTECTION_V2_TH_VI; + break; + case AFE_PARAM_ID_SP_V2_EX_VI_MODE_CFG: + case AFE_PARAM_ID_SP_V2_EX_VI_FTM_CFG: + param_info.module_id = AFE_MODULE_SPEAKER_PROTECTION_V2_EX_VI; + break; + default: + pr_err("%s: default case 0x%x\n", __func__, param_id); + goto fail_cmd; + } + + param_info.instance_id = INSTANCE_ID_0; + param_info.param_id = param_id; + param_info.param_size = sizeof(union afe_spkr_prot_config); + + ret = q6afe_pack_and_set_param_in_band(src_port, + q6audio_get_port_index(src_port), + param_info, (u8 *) prot_config); + if (ret) + pr_err("%s: port = 0x%x param = 0x%x failed %d\n", __func__, + src_port, param_id, ret); + +fail_cmd: + pr_debug("%s: config.pdata.param_id 0x%x status %d 0x%x\n", __func__, + param_info.param_id, ret, src_port); + return ret; +} + +static int afe_spkr_prot_reg_event_cfg(u16 port_id) +{ + struct afe_port_cmd_event_cfg *config; + struct afe_port_cmd_mod_evt_cfg_payload pl; + int index; + int ret; + int num_events = 1; + int cmd_size = sizeof(struct afe_port_cmd_event_cfg) + + (num_events * sizeof(struct afe_port_cmd_mod_evt_cfg_payload)); + + config = kzalloc(cmd_size, GFP_KERNEL); + if (!config) + return -ENOMEM; + + index = q6audio_get_port_index(port_id); + if (index < 0) { + pr_err("%s: Invalid index number: %d\n", __func__, index); + ret = -EINVAL; + goto fail_idx; + } + + memset(&pl, 0, sizeof(pl)); + pl.module_id = AFE_MODULE_SPEAKER_PROTECTION_V2_EX_VI; + pl.event_id = AFE_PORT_SP_DC_DETECTION_EVENT; + pl.reg_flag = AFE_MODULE_REGISTER_EVENT_FLAG; + + + 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 = cmd_size; + config->hdr.src_port = 0; + config->hdr.dest_port = 0; + config->hdr.token = index; + + config->hdr.opcode = AFE_PORT_CMD_MOD_EVENT_CFG; + config->port_id = q6audio_get_port_id(port_id); + config->num_events = num_events; + config->version = 1; + memcpy(config->payload, &pl, sizeof(pl)); + atomic_set(&this_afe.state, 1); + atomic_set(&this_afe.status, 0); + ret = apr_send_pkt(this_afe.apr, (uint32_t *) config); + if (ret < 0) { + pr_err("%s: port = 0x%x failed %d\n", + __func__, port_id, ret); + goto fail_cmd; + } + ret = wait_event_timeout(this_afe.wait[index], + (atomic_read(&this_afe.state) == 0), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + ret = -EINVAL; + goto fail_cmd; + } + if (atomic_read(&this_afe.status) > 0) { + pr_err("%s: config cmd failed [%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&this_afe.status))); + ret = adsp_err_get_lnx_err_code( + atomic_read(&this_afe.status)); + goto fail_idx; + } + ret = 0; +fail_cmd: + pr_debug("%s: config.opcode 0x%x status %d\n", + __func__, config->hdr.opcode, ret); + +fail_idx: + kfree(config); + return ret; +} + +static void afe_send_cal_spkr_prot_tx(int port_id) +{ + union afe_spkr_prot_config afe_spk_config; + + if (this_afe.cal_data[AFE_FB_SPKR_PROT_CAL] == NULL || + this_afe.cal_data[AFE_FB_SPKR_PROT_TH_VI_CAL] == NULL || + this_afe.cal_data[AFE_FB_SPKR_PROT_EX_VI_CAL] == NULL) + return; + + mutex_lock(&this_afe.cal_data[AFE_FB_SPKR_PROT_CAL]->lock); + if ((this_afe.prot_cfg.mode != MSM_SPKR_PROT_DISABLED) && + (this_afe.vi_tx_port == port_id)) { + if (this_afe.prot_cfg.mode == + MSM_SPKR_PROT_CALIBRATION_IN_PROGRESS) { + afe_spk_config.vi_proc_cfg.operation_mode = + Q6AFE_MSM_SPKR_CALIBRATION; + afe_spk_config.vi_proc_cfg.quick_calib_flag = + this_afe.prot_cfg.quick_calib_flag; + } else { + afe_spk_config.vi_proc_cfg.operation_mode = + Q6AFE_MSM_SPKR_PROCESSING; + } + + if (this_afe.th_ftm_cfg.mode == MSM_SPKR_PROT_IN_FTM_MODE) + afe_spk_config.vi_proc_cfg.operation_mode = + Q6AFE_MSM_SPKR_FTM_MODE; + else if (this_afe.v_vali_cfg.mode == + MSM_SPKR_PROT_IN_V_VALI_MODE) + afe_spk_config.vi_proc_cfg.operation_mode = + Q6AFE_MSM_SPKR_V_VALI_MODE; + afe_spk_config.vi_proc_cfg.minor_version = 1; + afe_spk_config.vi_proc_cfg.r0_cali_q24[SP_V2_SPKR_1] = + (uint32_t) this_afe.prot_cfg.r0[SP_V2_SPKR_1]; + afe_spk_config.vi_proc_cfg.r0_cali_q24[SP_V2_SPKR_2] = + (uint32_t) this_afe.prot_cfg.r0[SP_V2_SPKR_2]; + afe_spk_config.vi_proc_cfg.t0_cali_q6[SP_V2_SPKR_1] = + (uint32_t) this_afe.prot_cfg.t0[SP_V2_SPKR_1]; + afe_spk_config.vi_proc_cfg.t0_cali_q6[SP_V2_SPKR_2] = + (uint32_t) this_afe.prot_cfg.t0[SP_V2_SPKR_2]; + if (this_afe.prot_cfg.mode != MSM_SPKR_PROT_NOT_CALIBRATED) { + struct asm_spkr_calib_vi_proc_cfg *vi_proc_cfg; + + vi_proc_cfg = &afe_spk_config.vi_proc_cfg; + vi_proc_cfg->r0_t0_selection_flag[SP_V2_SPKR_1] = + USE_CALIBRATED_R0TO; + vi_proc_cfg->r0_t0_selection_flag[SP_V2_SPKR_2] = + USE_CALIBRATED_R0TO; + } else { + struct asm_spkr_calib_vi_proc_cfg *vi_proc_cfg; + + vi_proc_cfg = &afe_spk_config.vi_proc_cfg; + vi_proc_cfg->r0_t0_selection_flag[SP_V2_SPKR_1] = + USE_SAFE_R0TO; + vi_proc_cfg->r0_t0_selection_flag[SP_V2_SPKR_2] = + USE_SAFE_R0TO; + } + if (afe_spk_prot_prepare(port_id, 0, + AFE_PARAM_ID_SPKR_CALIB_VI_PROC_CFG_V2, + &afe_spk_config)) + pr_err("%s: SPKR_CALIB_VI_PROC_CFG failed\n", + __func__); + } + mutex_unlock(&this_afe.cal_data[AFE_FB_SPKR_PROT_CAL]->lock); + + mutex_lock(&this_afe.cal_data[AFE_FB_SPKR_PROT_TH_VI_CAL]->lock); + if ((this_afe.th_ftm_cfg.mode == MSM_SPKR_PROT_IN_FTM_MODE) && + (this_afe.vi_tx_port == port_id)) { + afe_spk_config.th_vi_ftm_cfg.minor_version = 1; + afe_spk_config.th_vi_ftm_cfg.wait_time_ms[SP_V2_SPKR_1] = + this_afe.th_ftm_cfg.wait_time[SP_V2_SPKR_1]; + afe_spk_config.th_vi_ftm_cfg.wait_time_ms[SP_V2_SPKR_2] = + this_afe.th_ftm_cfg.wait_time[SP_V2_SPKR_2]; + afe_spk_config.th_vi_ftm_cfg.ftm_time_ms[SP_V2_SPKR_1] = + this_afe.th_ftm_cfg.ftm_time[SP_V2_SPKR_1]; + afe_spk_config.th_vi_ftm_cfg.ftm_time_ms[SP_V2_SPKR_2] = + this_afe.th_ftm_cfg.ftm_time[SP_V2_SPKR_2]; + + if (afe_spk_prot_prepare(port_id, 0, + AFE_PARAM_ID_SP_V2_TH_VI_FTM_CFG, + &afe_spk_config)) + pr_err("%s: th vi ftm cfg failed\n", __func__); + this_afe.th_ftm_cfg.mode = MSM_SPKR_PROT_DISABLED; + } else if ((this_afe.v_vali_cfg.mode == + MSM_SPKR_PROT_IN_V_VALI_MODE) && + (this_afe.vi_tx_port == port_id)) { + afe_spk_config.th_vi_v_vali_cfg.minor_version = 1; + afe_spk_config.th_vi_v_vali_cfg.wait_time_ms[SP_V2_SPKR_1] = + this_afe.v_vali_cfg.wait_time[SP_V2_SPKR_1]; + afe_spk_config.th_vi_v_vali_cfg.wait_time_ms[SP_V2_SPKR_2] = + this_afe.v_vali_cfg.wait_time[SP_V2_SPKR_2]; + afe_spk_config.th_vi_v_vali_cfg.vali_time_ms[SP_V2_SPKR_1] = + this_afe.v_vali_cfg.vali_time[SP_V2_SPKR_1]; + afe_spk_config.th_vi_v_vali_cfg.vali_time_ms[SP_V2_SPKR_2] = + this_afe.v_vali_cfg.vali_time[SP_V2_SPKR_2]; + + if (afe_spk_prot_prepare(port_id, 0, + AFE_PARAM_ID_SP_V2_TH_VI_V_VALI_CFG, + &afe_spk_config)) + pr_err("%s: th vi v-vali cfg failed\n", __func__); + + this_afe.v_vali_cfg.mode = MSM_SPKR_PROT_DISABLED; + } + mutex_unlock(&this_afe.cal_data[AFE_FB_SPKR_PROT_TH_VI_CAL]->lock); + + mutex_lock(&this_afe.cal_data[AFE_FB_SPKR_PROT_EX_VI_CAL]->lock); + if ((this_afe.ex_ftm_cfg.mode == MSM_SPKR_PROT_IN_FTM_MODE) && + (this_afe.vi_tx_port == port_id)) { + afe_spk_config.ex_vi_mode_cfg.minor_version = 1; + afe_spk_config.ex_vi_mode_cfg.operation_mode = + Q6AFE_MSM_SPKR_FTM_MODE; + if (afe_spk_prot_prepare(port_id, 0, + AFE_PARAM_ID_SP_V2_EX_VI_MODE_CFG, + &afe_spk_config)) + pr_err("%s: ex vi mode cfg failed\n", __func__); + + afe_spk_config.ex_vi_ftm_cfg.minor_version = 1; + afe_spk_config.ex_vi_ftm_cfg.wait_time_ms[SP_V2_SPKR_1] = + this_afe.ex_ftm_cfg.wait_time[SP_V2_SPKR_1]; + afe_spk_config.ex_vi_ftm_cfg.wait_time_ms[SP_V2_SPKR_2] = + this_afe.ex_ftm_cfg.wait_time[SP_V2_SPKR_2]; + afe_spk_config.ex_vi_ftm_cfg.ftm_time_ms[SP_V2_SPKR_1] = + this_afe.ex_ftm_cfg.ftm_time[SP_V2_SPKR_1]; + afe_spk_config.ex_vi_ftm_cfg.ftm_time_ms[SP_V2_SPKR_2] = + this_afe.ex_ftm_cfg.ftm_time[SP_V2_SPKR_2]; + + if (afe_spk_prot_prepare(port_id, 0, + AFE_PARAM_ID_SP_V2_EX_VI_FTM_CFG, + &afe_spk_config)) + pr_err("%s: ex vi ftm cfg failed\n", __func__); + this_afe.ex_ftm_cfg.mode = MSM_SPKR_PROT_DISABLED; + } + mutex_unlock(&this_afe.cal_data[AFE_FB_SPKR_PROT_EX_VI_CAL]->lock); + + /* Register for DC detection event if speaker protection is enabled */ + if (this_afe.prot_cfg.mode != MSM_SPKR_PROT_DISABLED && + (this_afe.vi_tx_port == port_id)) { + afe_spkr_prot_reg_event_cfg(port_id); + } +} + +static void afe_send_cal_spkr_prot_rx(int port_id) +{ + union afe_spkr_prot_config afe_spk_config; + union afe_spkr_prot_config afe_spk_limiter_config; + + if (this_afe.cal_data[AFE_FB_SPKR_PROT_CAL] == NULL) + goto done; + + mutex_lock(&this_afe.cal_data[AFE_FB_SPKR_PROT_CAL]->lock); + + if (this_afe.prot_cfg.mode != MSM_SPKR_PROT_DISABLED && + (this_afe.vi_rx_port == port_id)) { + if (this_afe.prot_cfg.mode == + MSM_SPKR_PROT_CALIBRATION_IN_PROGRESS) + afe_spk_config.mode_rx_cfg.mode = + Q6AFE_MSM_SPKR_CALIBRATION; + else + afe_spk_config.mode_rx_cfg.mode = + Q6AFE_MSM_SPKR_PROCESSING; + afe_spk_config.mode_rx_cfg.minor_version = 1; + if (afe_spk_prot_prepare(port_id, 0, + AFE_PARAM_ID_FBSP_MODE_RX_CFG, + &afe_spk_config)) + pr_err("%s: RX MODE_VI_PROC_CFG failed\n", + __func__); + + if (afe_spk_config.mode_rx_cfg.mode == + Q6AFE_MSM_SPKR_PROCESSING) { + if (this_afe.prot_cfg.sp_version >= + AFE_API_VERSION_SUPPORT_SPV3) { + afe_spk_limiter_config.limiter_th_cfg. + minor_version = 1; + afe_spk_limiter_config.limiter_th_cfg. + lim_thr_per_calib_q27[SP_V2_SPKR_1] = + this_afe.prot_cfg.limiter_th[SP_V2_SPKR_1]; + afe_spk_limiter_config.limiter_th_cfg. + lim_thr_per_calib_q27[SP_V2_SPKR_2] = + this_afe.prot_cfg.limiter_th[SP_V2_SPKR_2]; + if (afe_spk_prot_prepare(port_id, 0, + AFE_PARAM_ID_SP_RX_LIMITER_TH, + &afe_spk_limiter_config)) + pr_err("%s: SP_RX_LIMITER_TH failed.\n", + __func__); + } else { + pr_debug("%s: SPv3 failed to apply on AFE API version=%d.\n", + __func__, + this_afe.prot_cfg.sp_version); + } + } + } + mutex_unlock(&this_afe.cal_data[AFE_FB_SPKR_PROT_CAL]->lock); +done: + return; +} + +static int afe_send_hw_delay(u16 port_id, u32 rate) +{ + struct audio_cal_hw_delay_entry delay_entry; + struct afe_param_id_device_hw_delay_cfg hw_delay; + struct param_hdr_v3 param_info; + int ret = -EINVAL; + + pr_debug("%s:\n", __func__); + + memset(&delay_entry, 0, sizeof(delay_entry)); + memset(¶m_info, 0, sizeof(param_info)); + + delay_entry.sample_rate = rate; + if (afe_get_port_type(port_id) == MSM_AFE_PORT_TYPE_TX) + ret = afe_get_cal_hw_delay(TX_DEVICE, &delay_entry); + else if (afe_get_port_type(port_id) == MSM_AFE_PORT_TYPE_RX) + ret = afe_get_cal_hw_delay(RX_DEVICE, &delay_entry); + + /* + * HW delay is only used for IMS calls to sync audio with video + * It is only needed for devices & sample rates used for IMS video + * calls. Values are received from ACDB calbration files + */ + if (ret != 0) { + pr_debug("%s: debug: HW delay info not available %d\n", + __func__, ret); + goto fail_cmd; + } + + param_info.module_id = AFE_MODULE_AUDIO_DEV_INTERFACE; + param_info.instance_id = INSTANCE_ID_0; + param_info.param_id = AFE_PARAM_ID_DEVICE_HW_DELAY; + param_info.param_size = sizeof(hw_delay); + + hw_delay.delay_in_us = delay_entry.delay_usec; + hw_delay.device_hw_delay_minor_version = + AFE_API_VERSION_DEVICE_HW_DELAY; + + ret = q6afe_pack_and_set_param_in_band(port_id, + q6audio_get_port_index(port_id), + param_info, (u8 *) &hw_delay); + if (ret) + pr_err("%s: AFE hw delay for port 0x%x failed %d\n", + __func__, port_id, ret); + +fail_cmd: + pr_debug("%s: port_id 0x%x rate %u delay_usec %d status %d\n", + __func__, port_id, rate, delay_entry.delay_usec, ret); + return ret; +} + +static struct cal_block_data *afe_find_cal_topo_id_by_port( + struct cal_type_data *cal_type, u16 port_id) +{ + struct list_head *ptr, *next; + struct cal_block_data *cal_block = NULL; + int32_t path; + struct audio_cal_info_afe_top *afe_top; + int afe_port_index = q6audio_get_port_index(port_id); + + if (afe_port_index < 0) + goto err_exit; + + list_for_each_safe(ptr, next, + &cal_type->cal_blocks) { + cal_block = list_entry(ptr, + struct cal_block_data, list); + /* Skip cal_block if it is already marked stale */ + if (cal_utils_is_cal_stale(cal_block)) + continue; + path = ((afe_get_port_type(port_id) == + MSM_AFE_PORT_TYPE_TX)?(TX_DEVICE):(RX_DEVICE)); + afe_top = + (struct audio_cal_info_afe_top *)cal_block->cal_info; + if (afe_top->path == path) { + if (this_afe.dev_acdb_id[afe_port_index] > 0) { + if (afe_top->acdb_id == + this_afe.dev_acdb_id[afe_port_index]) { + pr_debug("%s: top_id:%x acdb_id:%d afe_port_id:%d\n", + __func__, afe_top->topology, + afe_top->acdb_id, + q6audio_get_port_id(port_id)); + return cal_block; + } + } else { + pr_debug("%s: top_id:%x acdb_id:%d afe_port:%d\n", + __func__, afe_top->topology, afe_top->acdb_id, + q6audio_get_port_id(port_id)); + return cal_block; + } + } + } + +err_exit: + return NULL; +} + +/* + * Retrieving cal_block will mark cal_block as stale. + * Hence it cannot be reused or resent unless the flag + * is reset. + */ +static int afe_get_cal_topology_id(u16 port_id, u32 *topology_id, + int cal_type_index) +{ + int ret = 0; + + struct cal_block_data *cal_block = NULL; + struct audio_cal_info_afe_top *afe_top_info = NULL; + + if (this_afe.cal_data[cal_type_index] == NULL) { + pr_err("%s: cal_type %d not initialized\n", __func__, + cal_type_index); + return -EINVAL; + } + if (topology_id == NULL) { + pr_err("%s: topology_id is NULL\n", __func__); + return -EINVAL; + } + *topology_id = 0; + + mutex_lock(&this_afe.cal_data[cal_type_index]->lock); + cal_block = afe_find_cal_topo_id_by_port( + this_afe.cal_data[cal_type_index], port_id); + if (cal_block == NULL) { + pr_err("%s: cal_type %d not initialized for this port %d\n", + __func__, cal_type_index, port_id); + ret = -EINVAL; + goto unlock; + } + + afe_top_info = ((struct audio_cal_info_afe_top *) + cal_block->cal_info); + if (!afe_top_info->topology) { + pr_err("%s: invalid topology id : [%d, %d]\n", + __func__, afe_top_info->acdb_id, afe_top_info->topology); + ret = -EINVAL; + goto unlock; + } + *topology_id = (u32)afe_top_info->topology; + cal_utils_mark_cal_used(cal_block); + + pr_debug("%s: port_id = %u acdb_id = %d topology_id = %u ret=%d\n", + __func__, port_id, afe_top_info->acdb_id, + afe_top_info->topology, ret); +unlock: + mutex_unlock(&this_afe.cal_data[cal_type_index]->lock); + return ret; +} + +static int afe_send_port_topology_id(u16 port_id) +{ + struct afe_param_id_set_topology_cfg topology; + struct param_hdr_v3 param_info; + u32 topology_id = 0; + int index = 0; + int ret = 0; + + memset(&topology, 0, sizeof(topology)); + memset(¶m_info, 0, sizeof(param_info)); + 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 = afe_get_cal_topology_id(port_id, &topology_id, AFE_TOPOLOGY_CAL); + if (ret < 0) { + pr_debug("%s: Check for LSM topology\n", __func__); + ret = afe_get_cal_topology_id(port_id, &topology_id, + AFE_LSM_TOPOLOGY_CAL); + } + if (ret || !topology_id) { + pr_debug("%s: AFE port[%d] get_cal_topology[%d] invalid!\n", + __func__, port_id, topology_id); + goto done; + } + + param_info.module_id = AFE_MODULE_AUDIO_DEV_INTERFACE; + param_info.instance_id = INSTANCE_ID_0; + param_info.param_id = AFE_PARAM_ID_SET_TOPOLOGY; + param_info.param_size = sizeof(topology); + + topology.minor_version = AFE_API_VERSION_TOPOLOGY_V1; + topology.topology_id = topology_id; + + ret = q6afe_pack_and_set_param_in_band(port_id, + q6audio_get_port_index(port_id), + param_info, (u8 *) &topology); + if (ret) { + pr_err("%s: AFE set topology id enable for port 0x%x failed %d\n", + __func__, port_id, ret); + goto done; + } + + this_afe.topology[index] = topology_id; + rtac_update_afe_topology(port_id); +done: + pr_debug("%s: AFE set topology id 0x%x enable for port 0x%x ret %d\n", + __func__, topology_id, port_id, ret); + return ret; + +} + + +static int afe_get_island_mode(u16 port_id, u32 *island_mode) +{ + int ret = 0; + int index = 0; + *island_mode = 0; + + 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; + } + + *island_mode = this_afe.island_mode[index]; + return ret; +} + +/* + * afe_send_port_island_mode - + * for sending island mode to AFE + * + * @port_id: AFE port id number + * + * Returns 0 on success or error on failure. + */ +int afe_send_port_island_mode(u16 port_id) +{ + struct afe_param_id_island_cfg_t island_cfg; + struct param_hdr_v3 param_info; + u32 island_mode = 0; + int ret = 0; + + memset(&island_cfg, 0, sizeof(island_cfg)); + memset(¶m_info, 0, sizeof(param_info)); + + ret = afe_get_island_mode(port_id, &island_mode); + if (ret) { + pr_err("%s: AFE port[%d] get island mode is invalid!\n", + __func__, port_id); + return ret; + } + param_info.module_id = AFE_MODULE_AUDIO_DEV_INTERFACE; + param_info.instance_id = INSTANCE_ID_0; + param_info.param_id = AFE_PARAM_ID_ISLAND_CONFIG; + param_info.param_size = sizeof(island_cfg); + + island_cfg.island_cfg_minor_version = AFE_API_VERSION_ISLAND_CONFIG; + island_cfg.island_enable = island_mode; + + ret = q6afe_pack_and_set_param_in_band(port_id, + q6audio_get_port_index(port_id), + param_info, (u8 *) &island_cfg); + if (ret) { + pr_err("%s: AFE set island mode enable for port 0x%x failed %d\n", + __func__, port_id, ret); + return ret; + } + pr_debug("%s: AFE set island mode 0x%x enable for port 0x%x ret %d\n", + __func__, island_mode, port_id, ret); + return ret; +} +EXPORT_SYMBOL(afe_send_port_island_mode); + +static int afe_get_vad_preroll_cfg(u16 port_id, u32 *preroll_cfg) +{ + int ret = 0; + int index = 0; + *preroll_cfg = 0; + + 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; + } + + *preroll_cfg = this_afe.vad_cfg[index].pre_roll; + return ret; +} + +static int afe_send_port_vad_cfg_params(u16 port_id) +{ + struct afe_param_id_vad_cfg_t vad_cfg; + struct param_hdr_v3 param_info; + u32 pre_roll_cfg = 0; + struct firmware_cal *hwdep_cal = NULL; + int ret = 0; + + memset(&vad_cfg, 0, sizeof(vad_cfg)); + memset(¶m_info, 0, sizeof(param_info)); + + ret = afe_get_vad_preroll_cfg(port_id, &pre_roll_cfg); + if (ret) { + pr_err("%s: AFE port[%d] get preroll cfg is invalid!\n", + __func__, port_id); + return ret; + } + param_info.module_id = AFE_MODULE_VAD; + param_info.instance_id = INSTANCE_ID_0; + param_info.param_id = AFE_PARAM_ID_VAD_CFG; + param_info.param_size = sizeof(vad_cfg); + + vad_cfg.vad_cfg_minor_version = AFE_API_VERSION_VAD_CFG; + vad_cfg.pre_roll_in_ms = pre_roll_cfg; + + ret = q6afe_pack_and_set_param_in_band(port_id, + q6audio_get_port_index(port_id), + param_info, (u8 *) &vad_cfg); + if (ret) { + pr_err("%s: AFE set vad cfg for port 0x%x failed %d\n", + __func__, port_id, ret); + return ret; + } + + memset(¶m_info, 0, sizeof(param_info)); + + hwdep_cal = q6afecal_get_fw_cal(this_afe.fw_data, Q6AFE_VAD_CORE_CAL); + if (!hwdep_cal) { + pr_err("%s: error in retrieving vad core calibration", + __func__); + return -EINVAL; + } + + param_info.module_id = AFE_MODULE_VAD; + param_info.instance_id = INSTANCE_ID_0; + param_info.param_id = AFE_PARAM_ID_VAD_CORE_CFG; + param_info.param_size = hwdep_cal->size; + + ret = q6afe_pack_and_set_param_in_band(port_id, + q6audio_get_port_index(port_id), + param_info, (u8 *) hwdep_cal->data); + if (ret) { + pr_err("%s: AFE set vad cfg for port 0x%x failed %d\n", + __func__, port_id, ret); + return ret; + } + pr_debug("%s: AFE set preroll cfg %d vad core cfg port 0x%x ret %d\n", + __func__, pre_roll_cfg, port_id, ret); + return ret; +} + +static int remap_cal_data(struct cal_block_data *cal_block, int cal_index) +{ + int ret = 0; + + if (cal_block->map_data.dma_buf == NULL) { + pr_err("%s: No ION allocation for cal index %d!\n", + __func__, cal_index); + ret = -EINVAL; + goto done; + } + + if ((cal_block->map_data.map_size > 0) && + (cal_block->map_data.q6map_handle == 0)) { + atomic_set(&this_afe.mem_map_cal_index, cal_index); + ret = afe_cmd_memory_map(cal_block->cal_data.paddr, + cal_block->map_data.map_size); + atomic_set(&this_afe.mem_map_cal_index, -1); + if (ret < 0) { + pr_err("%s: mmap did not work! size = %zd ret %d\n", + __func__, + cal_block->map_data.map_size, ret); + pr_debug("%s: mmap did not work! addr = 0x%pK, size = %zd\n", + __func__, + &cal_block->cal_data.paddr, + cal_block->map_data.map_size); + goto done; + } + cal_block->map_data.q6map_handle = atomic_read(&this_afe. + mem_map_cal_handles[cal_index]); + } +done: + return ret; +} + +static struct cal_block_data *afe_find_cal(int cal_index, int port_id) +{ + struct list_head *ptr, *next; + struct cal_block_data *cal_block = NULL; + struct audio_cal_info_afe *afe_cal_info = NULL; + int afe_port_index = q6audio_get_port_index(port_id); + + pr_debug("%s: cal_index %d port_id %d port_index %d\n", __func__, + cal_index, port_id, afe_port_index); + if (afe_port_index < 0) { + pr_err("%s: Error getting AFE port index %d\n", + __func__, afe_port_index); + goto exit; + } + + list_for_each_safe(ptr, next, + &this_afe.cal_data[cal_index]->cal_blocks) { + cal_block = list_entry(ptr, struct cal_block_data, list); + afe_cal_info = cal_block->cal_info; + if ((afe_cal_info->acdb_id == + this_afe.dev_acdb_id[afe_port_index]) && + (afe_cal_info->sample_rate == + this_afe.afe_sample_rates[afe_port_index])) { + pr_debug("%s: cal block is a match, size is %zd\n", + __func__, cal_block->cal_data.size); + goto exit; + } + } + pr_debug("%s: no matching cal_block found\n", __func__); + cal_block = NULL; + +exit: + return cal_block; +} + +static int send_afe_cal_type(int cal_index, int port_id) +{ + struct cal_block_data *cal_block = NULL; + int ret; + int afe_port_index = q6audio_get_port_index(port_id); + + pr_debug("%s:\n", __func__); + + if (this_afe.cal_data[cal_index] == NULL) { + pr_warn("%s: cal_index %d not allocated!\n", + __func__, cal_index); + ret = -EINVAL; + goto done; + } + + if (afe_port_index < 0) { + pr_err("%s: Error getting AFE port index %d\n", + __func__, afe_port_index); + ret = -EINVAL; + goto done; + } + + mutex_lock(&this_afe.cal_data[cal_index]->lock); + + if (((cal_index == AFE_COMMON_RX_CAL) || + (cal_index == AFE_COMMON_TX_CAL) || + (cal_index == AFE_LSM_TX_CAL)) && + (this_afe.dev_acdb_id[afe_port_index] > 0)) + cal_block = afe_find_cal(cal_index, port_id); + else + cal_block = cal_utils_get_only_cal_block( + this_afe.cal_data[cal_index]); + + if (cal_block == NULL || cal_utils_is_cal_stale(cal_block)) { + pr_err("%s cal_block not found!!\n", __func__); + ret = -EINVAL; + goto unlock; + } + + pr_debug("%s: Sending cal_index cal %d\n", __func__, cal_index); + + ret = remap_cal_data(cal_block, cal_index); + if (ret) { + pr_err("%s: Remap_cal_data failed for cal %d!\n", + __func__, cal_index); + ret = -EINVAL; + goto unlock; + } + ret = afe_send_cal_block(port_id, cal_block); + if (ret < 0) + pr_debug("%s: No cal sent for cal_index %d, port_id = 0x%x! ret %d\n", + __func__, cal_index, port_id, ret); + + cal_utils_mark_cal_used(cal_block); + +unlock: + mutex_unlock(&this_afe.cal_data[cal_index]->lock); +done: + return ret; +} + +void afe_send_cal(u16 port_id) +{ + int ret; + + pr_debug("%s: port_id=0x%x\n", __func__, port_id); + + if (afe_get_port_type(port_id) == MSM_AFE_PORT_TYPE_TX) { + afe_send_cal_spkr_prot_tx(port_id); + ret = send_afe_cal_type(AFE_COMMON_TX_CAL, port_id); + if (ret < 0) + send_afe_cal_type(AFE_LSM_TX_CAL, port_id); + } else if (afe_get_port_type(port_id) == MSM_AFE_PORT_TYPE_RX) { + send_afe_cal_type(AFE_COMMON_RX_CAL, port_id); + afe_send_cal_spkr_prot_rx(port_id); + } +} + +int afe_turn_onoff_hw_mad(u16 mad_type, u16 enable) +{ + struct afe_param_hw_mad_ctrl mad_enable_param; + struct param_hdr_v3 param_info; + int ret; + + pr_debug("%s: enter\n", __func__); + + memset(&mad_enable_param, 0, sizeof(mad_enable_param)); + memset(¶m_info, 0, sizeof(param_info)); + param_info.module_id = AFE_MODULE_HW_MAD; + param_info.instance_id = INSTANCE_ID_0; + param_info.param_id = AFE_PARAM_ID_HW_MAD_CTRL; + param_info.param_size = sizeof(mad_enable_param); + + mad_enable_param.minor_version = 1; + mad_enable_param.mad_type = mad_type; + mad_enable_param.mad_enable = enable; + + ret = q6afe_pack_and_set_param_in_band(SLIMBUS_5_TX, IDX_GLOBAL_CFG, + param_info, + (u8 *) &mad_enable_param); + if (ret) + pr_err("%s: AFE_PARAM_ID_HW_MAD_CTRL failed %d\n", __func__, + ret); + return ret; +} + +static int afe_send_slimbus_slave_cfg( + struct afe_param_cdc_slimbus_slave_cfg *sb_slave_cfg) +{ + struct param_hdr_v3 param_hdr; + int ret; + + pr_debug("%s: enter\n", __func__); + + memset(¶m_hdr, 0, sizeof(param_hdr)); + param_hdr.module_id = AFE_MODULE_CDC_DEV_CFG; + param_hdr.instance_id = INSTANCE_ID_0; + param_hdr.param_id = AFE_PARAM_ID_CDC_SLIMBUS_SLAVE_CFG; + param_hdr.param_size = sizeof(struct afe_param_cdc_slimbus_slave_cfg); + + ret = q6afe_svc_pack_and_set_param_in_band(IDX_GLOBAL_CFG, param_hdr, + (u8 *) sb_slave_cfg); + if (ret) + pr_err("%s: AFE_PARAM_ID_CDC_SLIMBUS_SLAVE_CFG failed %d\n", + __func__, ret); + + pr_debug("%s: leave %d\n", __func__, ret); + return ret; +} + +static int afe_send_codec_reg_page_config( + struct afe_param_cdc_reg_page_cfg *cdc_reg_page_cfg) +{ + struct param_hdr_v3 param_hdr; + int ret; + + memset(¶m_hdr, 0, sizeof(param_hdr)); + param_hdr.module_id = AFE_MODULE_CDC_DEV_CFG; + param_hdr.instance_id = INSTANCE_ID_0; + param_hdr.param_id = AFE_PARAM_ID_CDC_REG_PAGE_CFG; + param_hdr.param_size = sizeof(struct afe_param_cdc_reg_page_cfg); + + ret = q6afe_svc_pack_and_set_param_in_band(IDX_GLOBAL_CFG, param_hdr, + (u8 *) cdc_reg_page_cfg); + if (ret) + pr_err("%s: AFE_PARAM_ID_CDC_REG_PAGE_CFG failed %d\n", + __func__, ret); + + return ret; +} + +static int afe_send_codec_reg_config( + struct afe_param_cdc_reg_cfg_data *cdc_reg_cfg) +{ + u8 *packed_param_data = NULL; + u32 packed_data_size = 0; + u32 single_param_size = 0; + u32 max_data_size = 0; + u32 max_single_param = 0; + struct param_hdr_v3 param_hdr; + int idx = 0; + int ret = -EINVAL; + + memset(¶m_hdr, 0, sizeof(param_hdr)); + max_single_param = sizeof(struct param_hdr_v3) + + sizeof(struct afe_param_cdc_reg_cfg); + max_data_size = APR_MAX_BUF - sizeof(struct afe_svc_cmd_set_param_v2); + packed_param_data = kzalloc(max_data_size, GFP_KERNEL); + if (!packed_param_data) + return -ENOMEM; + + /* param_hdr is the same for all params sent, set once at top */ + param_hdr.module_id = AFE_MODULE_CDC_DEV_CFG; + param_hdr.instance_id = INSTANCE_ID_0; + param_hdr.param_id = AFE_PARAM_ID_CDC_REG_CFG; + param_hdr.param_size = sizeof(struct afe_param_cdc_reg_cfg); + + while (idx < cdc_reg_cfg->num_registers) { + memset(packed_param_data, 0, max_data_size); + packed_data_size = 0; + single_param_size = 0; + + while (packed_data_size + max_single_param < max_data_size && + idx < cdc_reg_cfg->num_registers) { + ret = q6common_pack_pp_params( + packed_param_data + packed_data_size, + ¶m_hdr, (u8 *) &cdc_reg_cfg->reg_data[idx], + &single_param_size); + if (ret) { + pr_err("%s: Failed to pack parameters with error %d\n", + __func__, ret); + goto done; + } + packed_data_size += single_param_size; + idx++; + } + + ret = q6afe_svc_set_params(IDX_GLOBAL_CFG, NULL, + packed_param_data, packed_data_size); + if (ret) { + pr_err("%s: AFE_PARAM_ID_CDC_REG_CFG failed %d\n", + __func__, ret); + break; + } + } +done: + kfree(packed_param_data); + return ret; +} + +static int afe_init_cdc_reg_config(void) +{ + struct param_hdr_v3 param_hdr; + int ret; + + pr_debug("%s: enter\n", __func__); + memset(¶m_hdr, 0, sizeof(param_hdr)); + param_hdr.module_id = AFE_MODULE_CDC_DEV_CFG; + param_hdr.instance_id = INSTANCE_ID_0; + param_hdr.param_id = AFE_PARAM_ID_CDC_REG_CFG_INIT; + + ret = q6afe_svc_pack_and_set_param_in_band(IDX_GLOBAL_CFG, param_hdr, + NULL); + if (ret) + pr_err("%s: AFE_PARAM_ID_CDC_INIT_REG_CFG failed %d\n", + __func__, ret); + + return ret; +} + +static int afe_send_slimbus_slave_port_cfg( + struct afe_param_slimbus_slave_port_cfg *slim_slave_config, u16 port_id) +{ + struct param_hdr_v3 param_hdr; + int ret; + + pr_debug("%s: enter, port_id = 0x%x\n", __func__, port_id); + memset(¶m_hdr, 0, sizeof(param_hdr)); + param_hdr.module_id = AFE_MODULE_HW_MAD; + param_hdr.instance_id = INSTANCE_ID_0; + param_hdr.reserved = 0; + param_hdr.param_id = AFE_PARAM_ID_SLIMBUS_SLAVE_PORT_CFG; + param_hdr.param_size = sizeof(struct afe_param_slimbus_slave_port_cfg); + + ret = q6afe_pack_and_set_param_in_band(port_id, + q6audio_get_port_index(port_id), + param_hdr, + (u8 *) slim_slave_config); + if (ret) + pr_err("%s: AFE_PARAM_ID_SLIMBUS_SLAVE_PORT_CFG failed %d\n", + __func__, ret); + + pr_debug("%s: leave %d\n", __func__, ret); + return ret; +} +static int afe_aanc_port_cfg(void *apr, uint16_t tx_port, uint16_t rx_port) +{ + struct afe_param_aanc_port_cfg aanc_port_cfg; + struct param_hdr_v3 param_hdr; + int ret = 0; + + pr_debug("%s: tx_port 0x%x, rx_port 0x%x\n", + __func__, tx_port, rx_port); + + pr_debug("%s: AANC sample rate tx rate: %d rx rate %d\n", __func__, + this_afe.aanc_info.aanc_tx_port_sample_rate, + this_afe.aanc_info.aanc_rx_port_sample_rate); + + memset(&aanc_port_cfg, 0, sizeof(aanc_port_cfg)); + memset(¶m_hdr, 0, sizeof(param_hdr)); + + /* + * If aanc tx sample rate or rx sample rate is zero, skip aanc + * configuration as AFE resampler will fail for invalid sample + * rates. + */ + if (!this_afe.aanc_info.aanc_tx_port_sample_rate || + !this_afe.aanc_info.aanc_rx_port_sample_rate) { + return -EINVAL; + } + + param_hdr.module_id = AFE_MODULE_AANC; + param_hdr.instance_id = INSTANCE_ID_0; + param_hdr.param_id = AFE_PARAM_ID_AANC_PORT_CONFIG; + param_hdr.param_size = sizeof(struct afe_param_aanc_port_cfg); + + aanc_port_cfg.aanc_port_cfg_minor_version = + AFE_API_VERSION_AANC_PORT_CONFIG; + aanc_port_cfg.tx_port_sample_rate = + this_afe.aanc_info.aanc_tx_port_sample_rate; + aanc_port_cfg.tx_port_channel_map[0] = AANC_TX_VOICE_MIC; + aanc_port_cfg.tx_port_channel_map[1] = AANC_TX_NOISE_MIC; + aanc_port_cfg.tx_port_channel_map[2] = AANC_TX_ERROR_MIC; + aanc_port_cfg.tx_port_channel_map[3] = AANC_TX_MIC_UNUSED; + aanc_port_cfg.tx_port_channel_map[4] = AANC_TX_MIC_UNUSED; + aanc_port_cfg.tx_port_channel_map[5] = AANC_TX_MIC_UNUSED; + aanc_port_cfg.tx_port_channel_map[6] = AANC_TX_MIC_UNUSED; + aanc_port_cfg.tx_port_channel_map[7] = AANC_TX_MIC_UNUSED; + aanc_port_cfg.tx_port_num_channels = 3; + aanc_port_cfg.rx_path_ref_port_id = rx_port; + aanc_port_cfg.ref_port_sample_rate = + this_afe.aanc_info.aanc_rx_port_sample_rate; + + ret = q6afe_pack_and_set_param_in_band(tx_port, + q6audio_get_port_index(tx_port), + param_hdr, + (u8 *) &aanc_port_cfg); + if (ret) + pr_err("%s: AFE AANC port config failed for tx_port 0x%x, rx_port 0x%x ret %d\n", + __func__, tx_port, rx_port, ret); + else + q6afe_set_aanc_level(); + + return ret; +} + +static int afe_aanc_mod_enable(void *apr, uint16_t tx_port, uint16_t enable) +{ + struct afe_mod_enable_param mod_enable; + struct param_hdr_v3 param_hdr; + int ret = 0; + + pr_debug("%s: tx_port 0x%x\n", __func__, tx_port); + + memset(&mod_enable, 0, sizeof(mod_enable)); + memset(¶m_hdr, 0, sizeof(param_hdr)); + param_hdr.module_id = AFE_MODULE_AANC; + param_hdr.instance_id = INSTANCE_ID_0; + param_hdr.param_id = AFE_PARAM_ID_ENABLE; + param_hdr.param_size = sizeof(struct afe_mod_enable_param); + + mod_enable.enable = enable; + mod_enable.reserved = 0; + + ret = q6afe_pack_and_set_param_in_band(tx_port, + q6audio_get_port_index(tx_port), + param_hdr, (u8 *) &mod_enable); + if (ret) + pr_err("%s: AFE AANC enable failed for tx_port 0x%x ret %d\n", + __func__, tx_port, ret); + return ret; +} + +static int afe_send_bank_selection_clip( + struct afe_param_id_clip_bank_sel *param) +{ + struct param_hdr_v3 param_hdr; + int ret; + + if (!param) { + pr_err("%s: Invalid params", __func__); + return -EINVAL; + } + memset(¶m_hdr, 0, sizeof(param_hdr)); + param_hdr.module_id = AFE_MODULE_CDC_DEV_CFG; + param_hdr.instance_id = INSTANCE_ID_0; + param_hdr.param_id = AFE_PARAM_ID_CLIP_BANK_SEL_CFG; + param_hdr.param_size = sizeof(struct afe_param_id_clip_bank_sel); + + ret = q6afe_svc_pack_and_set_param_in_band(IDX_GLOBAL_CFG, param_hdr, + (u8 *) param); + if (ret) + pr_err("%s: AFE_PARAM_ID_CLIP_BANK_SEL_CFG failed %d\n", + __func__, ret); + return ret; +} +int afe_send_aanc_version( + struct afe_param_id_cdc_aanc_version *version_cfg) +{ + struct param_hdr_v3 param_hdr; + int ret; + + pr_debug("%s: enter\n", __func__); + memset(¶m_hdr, 0, sizeof(param_hdr)); + param_hdr.module_id = AFE_MODULE_CDC_DEV_CFG; + param_hdr.instance_id = INSTANCE_ID_0; + param_hdr.param_id = AFE_PARAM_ID_CDC_AANC_VERSION; + param_hdr.param_size = sizeof(struct afe_param_id_cdc_aanc_version); + + ret = q6afe_svc_pack_and_set_param_in_band(IDX_GLOBAL_CFG, param_hdr, + (u8 *) version_cfg); + if (ret) + pr_err("%s: AFE_PARAM_ID_CDC_AANC_VERSION failed %d\n", + __func__, ret); + return ret; +} + +/** + * afe_port_set_mad_type - + * to update mad type + * + * @port_id: AFE port id number + * @mad_type: MAD type enum value + * + * Returns 0 on success or error on failure. + */ +int afe_port_set_mad_type(u16 port_id, enum afe_mad_type mad_type) +{ + int i; + + if (port_id == AFE_PORT_ID_TERTIARY_MI2S_TX || + port_id == AFE_PORT_ID_INT3_MI2S_TX || + port_id == AFE_PORT_ID_TX_CODEC_DMA_TX_3) { + mad_type = MAD_SW_AUDIO; + return 0; + } + + i = port_id - SLIMBUS_0_RX; + if (i < 0 || i >= ARRAY_SIZE(afe_ports_mad_type)) { + pr_err("%s: Invalid port_id 0x%x\n", __func__, port_id); + return -EINVAL; + } + atomic_set(&afe_ports_mad_type[i], mad_type); + return 0; +} +EXPORT_SYMBOL(afe_port_set_mad_type); + +/** + * afe_port_get_mad_type - + * to retrieve mad type + * + * @port_id: AFE port id number + * + * Returns valid enum value on success or MAD_HW_NONE on failure. + */ +enum afe_mad_type afe_port_get_mad_type(u16 port_id) +{ + int i; + + if (port_id == AFE_PORT_ID_TERTIARY_MI2S_TX || + port_id == AFE_PORT_ID_INT3_MI2S_TX || + port_id == AFE_PORT_ID_TX_CODEC_DMA_TX_3) + return MAD_SW_AUDIO; + + i = port_id - SLIMBUS_0_RX; + if (i < 0 || i >= ARRAY_SIZE(afe_ports_mad_type)) { + pr_debug("%s: Non Slimbus port_id 0x%x\n", __func__, port_id); + return MAD_HW_NONE; + } + return (enum afe_mad_type) atomic_read(&afe_ports_mad_type[i]); +} +EXPORT_SYMBOL(afe_port_get_mad_type); + +/** + * afe_set_config - + * to configure AFE session with + * specified configuration for given config type + * + * @config_type: config type + * @config_data: configuration to pass to AFE session + * @arg: argument used in specific config types + * + * Returns 0 on success or error value on port start failure. + */ +int afe_set_config(enum afe_config_type config_type, void *config_data, int arg) +{ + int ret; + + pr_debug("%s: enter config_type %d\n", __func__, config_type); + ret = afe_q6_interface_prepare(); + if (ret) { + pr_err("%s: Q6 interface prepare failed %d\n", __func__, ret); + return ret; + } + + switch (config_type) { + case AFE_SLIMBUS_SLAVE_CONFIG: + ret = afe_send_slimbus_slave_cfg(config_data); + if (!ret) + ret = afe_init_cdc_reg_config(); + else + pr_err("%s: Sending slimbus slave config failed %d\n", + __func__, ret); + break; + case AFE_CDC_REGISTERS_CONFIG: + ret = afe_send_codec_reg_config(config_data); + break; + case AFE_SLIMBUS_SLAVE_PORT_CONFIG: + ret = afe_send_slimbus_slave_port_cfg(config_data, arg); + break; + case AFE_AANC_VERSION: + ret = afe_send_aanc_version(config_data); + break; + case AFE_CLIP_BANK_SEL: + ret = afe_send_bank_selection_clip(config_data); + break; + case AFE_CDC_CLIP_REGISTERS_CONFIG: + ret = afe_send_codec_reg_config(config_data); + break; + case AFE_CDC_REGISTER_PAGE_CONFIG: + ret = afe_send_codec_reg_page_config(config_data); + break; + default: + pr_err("%s: unknown configuration type %d", + __func__, config_type); + ret = -EINVAL; + } + + if (!ret) + set_bit(config_type, &afe_configured_cmd); + + return ret; +} +EXPORT_SYMBOL(afe_set_config); + +/* + * afe_clear_config - If SSR happens ADSP loses AFE configs, let AFE driver know + * about the state so client driver can wait until AFE is + * reconfigured. + */ +void afe_clear_config(enum afe_config_type config) +{ + clear_bit(config, &afe_configured_cmd); +} +EXPORT_SYMBOL(afe_clear_config); + +bool afe_has_config(enum afe_config_type config) +{ + return !!test_bit(config, &afe_configured_cmd); +} + +int afe_send_spdif_clk_cfg(struct afe_param_id_spdif_clk_cfg *cfg, + u16 port_id) +{ + struct afe_param_id_spdif_clk_cfg clk_cfg; + struct param_hdr_v3 param_hdr; + int ret = 0; + + if (!cfg) { + pr_err("%s: Error, no configuration data\n", __func__); + return -EINVAL; + } + + memset(&clk_cfg, 0, sizeof(clk_cfg)); + memset(¶m_hdr, 0, sizeof(param_hdr)); + param_hdr.module_id = AFE_MODULE_AUDIO_DEV_INTERFACE; + param_hdr.instance_id = INSTANCE_ID_0; + param_hdr.param_id = AFE_PARAM_ID_SPDIF_CLK_CONFIG; + param_hdr.param_size = sizeof(struct afe_param_id_spdif_clk_cfg); + + pr_debug("%s: Minor version = 0x%x clk val = %d clk root = 0x%x port id = 0x%x\n", + __func__, clk_cfg.clk_cfg_minor_version, clk_cfg.clk_value, + clk_cfg.clk_root, q6audio_get_port_id(port_id)); + + ret = q6afe_pack_and_set_param_in_band(port_id, + q6audio_get_port_index(port_id), + param_hdr, (u8 *) &clk_cfg); + if (ret < 0) + pr_err("%s: AFE send clock config for port 0x%x failed ret = %d\n", + __func__, port_id, ret); + return ret; +} + +/** + * afe_send_spdif_ch_status_cfg - + * to configure AFE session with + * specified channel status configuration + * + * @ch_status_cfg: channel status configutation + * @port_id: AFE port id number + * + * Returns 0 on success or error value on port start failure. + */ +int afe_send_spdif_ch_status_cfg(struct afe_param_id_spdif_ch_status_cfg + *ch_status_cfg, u16 port_id) +{ + struct param_hdr_v3 param_hdr; + int ret = 0; + + if (!ch_status_cfg) { + pr_err("%s: Error, no configuration data\n", __func__); + return -EINVAL; + } + + memset(¶m_hdr, 0, sizeof(param_hdr)); + param_hdr.module_id = AFE_MODULE_AUDIO_DEV_INTERFACE; + param_hdr.instance_id = INSTANCE_ID_0; + param_hdr.param_id = AFE_PARAM_ID_SPDIF_CLK_CONFIG; + param_hdr.param_size = sizeof(struct afe_param_id_spdif_ch_status_cfg); + + ret = q6afe_pack_and_set_param_in_band(port_id, + q6audio_get_port_index(port_id), + param_hdr, (u8 *) ch_status_cfg); + if (ret < 0) + pr_err("%s: AFE send channel status for port 0x%x failed ret = %d\n", + __func__, port_id, ret); + return ret; +} +EXPORT_SYMBOL(afe_send_spdif_ch_status_cfg); + +int afe_send_cmd_wakeup_register(void *handle, bool enable) +{ + struct afe_svc_cmd_evt_cfg_payload wakeup_irq; + int ret = 0; + + pr_debug("%s: enter\n", __func__); + + wakeup_irq.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + wakeup_irq.hdr.pkt_size = sizeof(wakeup_irq); + wakeup_irq.hdr.src_port = 0; + wakeup_irq.hdr.dest_port = 0; + wakeup_irq.hdr.token = 0x0; + wakeup_irq.hdr.opcode = AFE_SVC_CMD_EVENT_CFG; + wakeup_irq.event_id = AFE_EVENT_ID_MBHC_DETECTION_SW_WA; + wakeup_irq.reg_flag = enable; + pr_debug("%s: cmd wakeup register opcode[0x%x] register:%d\n", + __func__, wakeup_irq.hdr.opcode, wakeup_irq.reg_flag); + + ret = afe_apr_send_pkt(&wakeup_irq, &this_afe.wait_wakeup); + if (ret) + pr_err("%s: AFE wakeup command register %d failed %d\n", + __func__, enable, ret); + + return ret; +} +EXPORT_SYMBOL(afe_send_cmd_wakeup_register); + +static int afe_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 = afe_apr_send_pkt(&start, &this_afe.wait[index]); + if (ret) + pr_err("%s: AFE enable for port 0x%x failed %d\n", __func__, + port_id, ret); + + return ret; +} + +static int afe_aanc_start(uint16_t tx_port_id, uint16_t rx_port_id) +{ + int ret; + + pr_debug("%s: Tx port is 0x%x, Rx port is 0x%x\n", + __func__, tx_port_id, rx_port_id); + ret = afe_aanc_port_cfg(this_afe.apr, tx_port_id, rx_port_id); + if (ret) { + pr_err("%s: Send AANC Port Config failed %d\n", + __func__, ret); + goto fail_cmd; + } + send_afe_cal_type(AFE_AANC_CAL, tx_port_id); + +fail_cmd: + return ret; +} + +/** + * afe_spdif_port_start - to configure AFE session with + * specified port configuration + * + * @port_id: AFE port id number + * @spdif_port: spdif port configutation + * @rate: sampling rate of port + * + * Returns 0 on success or error value on port start failure. + */ +int afe_spdif_port_start(u16 port_id, struct afe_spdif_port_config *spdif_port, + u32 rate) +{ + struct param_hdr_v3 param_hdr; + uint16_t port_index; + int ret = 0; + + if (!spdif_port) { + pr_err("%s: Error, no configuration data\n", __func__); + ret = -EINVAL; + return ret; + } + + pr_debug("%s: port id: 0x%x\n", __func__, port_id); + + memset(¶m_hdr, 0, sizeof(param_hdr)); + 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; + } + + afe_send_cal(port_id); + afe_send_hw_delay(port_id, rate); + + param_hdr.module_id = AFE_MODULE_AUDIO_DEV_INTERFACE; + param_hdr.instance_id = INSTANCE_ID_0; + param_hdr.param_id = AFE_PARAM_ID_SPDIF_CONFIG; + param_hdr.param_size = sizeof(struct afe_spdif_port_config); + + ret = q6afe_pack_and_set_param_in_band(port_id, + q6audio_get_port_index(port_id), + param_hdr, (u8 *) spdif_port); + if (ret) { + pr_err("%s: AFE enable for port 0x%x failed ret = %d\n", + __func__, port_id, ret); + goto fail_cmd; + } + + port_index = afe_get_port_index(port_id); + if ((port_index >= 0) && (port_index < AFE_MAX_PORTS)) { + this_afe.afe_sample_rates[port_index] = rate; + } else { + pr_err("%s: Invalid port index %d\n", __func__, port_index); + ret = -EINVAL; + goto fail_cmd; + } + + if (afe_get_port_type(port_id) == MSM_AFE_PORT_TYPE_RX) { + ret = afe_send_spdif_ch_status_cfg(&spdif_port->ch_status, + port_id); + if (ret < 0) { + pr_err("%s: afe send failed %d\n", __func__, ret); + goto fail_cmd; + } + } + + return afe_send_cmd_port_start(port_id); + +fail_cmd: + return ret; +} +EXPORT_SYMBOL(afe_spdif_port_start); + +/** + * afe_spdif_reg_event_cfg - + * register for event from AFE spdif port + * + * @port_id: Port ID to register event + * @reg_flag: register or unregister + * @cb: callback function to invoke for events from module + * @private_data: private data to sent back in callback fn + * + * Returns 0 on success or error on failure + */ +int afe_spdif_reg_event_cfg(u16 port_id, u16 reg_flag, + void (*cb)(uint32_t opcode, + uint32_t token, uint32_t *payload, void *priv), + void *private_data) +{ + struct afe_port_cmd_event_cfg *config; + struct afe_port_cmd_mod_evt_cfg_payload pl; + int index; + int ret; + int num_events = 1; + int cmd_size = sizeof(struct afe_port_cmd_event_cfg) + + (num_events * sizeof(struct afe_port_cmd_mod_evt_cfg_payload)); + + config = kzalloc(cmd_size, GFP_KERNEL); + if (!config) + return -ENOMEM; + + if (port_id == AFE_PORT_ID_PRIMARY_SPDIF_TX) { + this_afe.pri_spdif_tx_cb = cb; + this_afe.pri_spdif_tx_private_data = private_data; + } else if (port_id == AFE_PORT_ID_SECONDARY_SPDIF_TX) { + this_afe.sec_spdif_tx_cb = cb; + this_afe.sec_spdif_tx_private_data = private_data; + } else { + pr_err("%s: wrong port id 0x%x\n", __func__, port_id); + ret = -EINVAL; + goto fail_idx; + } + + index = q6audio_get_port_index(port_id); + if (index < 0) { + pr_err("%s: Invalid index number: %d\n", __func__, index); + ret = -EINVAL; + goto fail_idx; + } + + memset(&pl, 0, sizeof(pl)); + pl.module_id = AFE_MODULE_CUSTOM_EVENTS; + pl.event_id = AFE_PORT_FMT_UPDATE_EVENT; + pl.reg_flag = reg_flag; + + 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 = cmd_size; + config->hdr.src_port = 1; + config->hdr.dest_port = 1; + config->hdr.token = index; + + config->hdr.opcode = AFE_PORT_CMD_MOD_EVENT_CFG; + config->port_id = q6audio_get_port_id(port_id); + config->num_events = num_events; + config->version = 1; + memcpy(config->payload, &pl, sizeof(pl)); + atomic_set(&this_afe.state, 1); + atomic_set(&this_afe.status, 0); + ret = apr_send_pkt(this_afe.apr, (uint32_t *) config); + if (ret < 0) { + pr_err("%s: port = 0x%x failed %d\n", + __func__, port_id, ret); + goto fail_cmd; + } + ret = wait_event_timeout(this_afe.wait[index], + (atomic_read(&this_afe.state) == 0), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + ret = -EINVAL; + goto fail_cmd; + } + if (atomic_read(&this_afe.status) > 0) { + pr_err("%s: config cmd failed [%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&this_afe.status))); + ret = adsp_err_get_lnx_err_code( + atomic_read(&this_afe.status)); + goto fail_idx; + } + ret = 0; +fail_cmd: + pr_debug("%s: config.opcode 0x%x status %d\n", + __func__, config->hdr.opcode, ret); + +fail_idx: + kfree(config); + return ret; +} +EXPORT_SYMBOL(afe_spdif_reg_event_cfg); + +int afe_send_slot_mapping_cfg( + struct afe_param_id_slot_mapping_cfg *slot_mapping_cfg, + u16 port_id) +{ + struct param_hdr_v3 param_hdr; + int ret = 0; + + if (!slot_mapping_cfg) { + pr_err("%s: Error, no configuration data\n", __func__); + return -EINVAL; + } + + pr_debug("%s: port id: 0x%x\n", __func__, port_id); + + memset(¶m_hdr, 0, sizeof(param_hdr)); + param_hdr.module_id = AFE_MODULE_TDM; + param_hdr.instance_id = INSTANCE_ID_0; + param_hdr.param_id = AFE_PARAM_ID_PORT_SLOT_MAPPING_CONFIG; + param_hdr.param_size = sizeof(struct afe_param_id_slot_mapping_cfg); + + ret = q6afe_pack_and_set_param_in_band(port_id, + q6audio_get_port_index(port_id), + param_hdr, + (u8 *) slot_mapping_cfg); + if (ret < 0) + pr_err("%s: AFE send slot mapping for port 0x%x failed ret = %d\n", + __func__, port_id, ret); + return ret; +} + +int afe_send_custom_tdm_header_cfg( + struct afe_param_id_custom_tdm_header_cfg *custom_tdm_header_cfg, + u16 port_id) +{ + struct param_hdr_v3 param_hdr; + int ret = 0; + + if (!custom_tdm_header_cfg) { + pr_err("%s: Error, no configuration data\n", __func__); + return -EINVAL; + } + + pr_debug("%s: port id: 0x%x\n", __func__, port_id); + + memset(¶m_hdr, 0, sizeof(param_hdr)); + param_hdr.module_id = AFE_MODULE_TDM; + param_hdr.instance_id = INSTANCE_ID_0; + param_hdr.param_id = AFE_PARAM_ID_CUSTOM_TDM_HEADER_CONFIG; + param_hdr.param_size = + sizeof(struct afe_param_id_custom_tdm_header_cfg); + + ret = q6afe_pack_and_set_param_in_band(port_id, + q6audio_get_port_index(port_id), + param_hdr, + (u8 *) custom_tdm_header_cfg); + if (ret < 0) + pr_err("%s: AFE send custom tdm header for port 0x%x failed ret = %d\n", + __func__, port_id, ret); + return ret; +} + +/** + * afe_tdm_port_start - to configure AFE session with + * specified port configuration + * + * @port_id: AFE port id number + * @tdm_port: TDM port configutation + * @rate: sampling rate of port + * @num_groups: number of TDM groups + * + * Returns 0 on success or error value on port start failure. + */ +int afe_tdm_port_start(u16 port_id, struct afe_tdm_port_config *tdm_port, + u32 rate, u16 num_groups) +{ + struct param_hdr_v3 param_hdr; + int index = 0; + uint16_t port_index = 0; + enum afe_mad_type mad_type = MAD_HW_NONE; + int ret = 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); + + memset(¶m_hdr, 0, sizeof(param_hdr)); + 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; + } + + ret = afe_q6_interface_prepare(); + if (ret != 0) { + pr_err("%s: Q6 interface prepare failed %d\n", __func__, ret); + return ret; + } + + if ((index >= 0) && (index < AFE_MAX_PORTS)) { + this_afe.afe_sample_rates[index] = rate; + + if (this_afe.rt_cb) + this_afe.dev_acdb_id[index] = this_afe.rt_cb(port_id); + } + + port_index = afe_get_port_index(port_id); + + if (q6core_get_avcs_api_version_per_service( + APRV2_IDS_SERVICE_ID_ADSP_AFE_V) >= AFE_API_VERSION_V4) { + /* send VAD configuration if enabled */ + if (this_afe.vad_cfg[port_index].is_enable) { + ret = afe_send_port_vad_cfg_params(port_id); + if (ret) { + pr_err("%s: afe send VAD config failed %d\n", + __func__, ret); + goto fail_cmd; + } + } + } + + /* Also send the topology id here: */ + if (!(this_afe.afe_cal_mode[port_index] == AFE_CAL_MODE_NONE)) { + /* One time call: only for first time */ + afe_send_custom_topology(); + afe_send_port_topology_id(port_id); + afe_send_cal(port_id); + afe_send_hw_delay(port_id, rate); + } + + /* Start SW MAD module */ + mad_type = afe_port_get_mad_type(port_id); + pr_debug("%s: port_id 0x%x, mad_type %d\n", __func__, port_id, + mad_type); + if (mad_type != MAD_HW_NONE && mad_type != MAD_SW_AUDIO) { + if (!afe_has_config(AFE_CDC_REGISTERS_CONFIG) || + !afe_has_config(AFE_SLIMBUS_SLAVE_CONFIG)) { + pr_err("%s: AFE isn't configured yet for\n" + "HW MAD try Again\n", __func__); + ret = -EAGAIN; + goto fail_cmd; + } + ret = afe_turn_onoff_hw_mad(mad_type, true); + if (ret) { + pr_err("%s: afe_turn_onoff_hw_mad failed %d\n", + __func__, ret); + goto fail_cmd; + } + } + + param_hdr.module_id = AFE_MODULE_AUDIO_DEV_INTERFACE; + param_hdr.instance_id = INSTANCE_ID_0; + param_hdr.param_id = AFE_PARAM_ID_TDM_CONFIG; + param_hdr.param_size = sizeof(struct afe_param_id_tdm_cfg); + + ret = q6afe_pack_and_set_param_in_band(port_id, + q6audio_get_port_index(port_id), + param_hdr, + (u8 *) &tdm_port->tdm); + if (ret) { + pr_err("%s: AFE enable for port 0x%x failed ret = %d\n", + __func__, port_id, ret); + goto fail_cmd; + } + + port_index = afe_get_port_index(port_id); + if ((port_index >= 0) && (port_index < AFE_MAX_PORTS)) { + this_afe.afe_sample_rates[port_index] = rate; + } else { + pr_err("%s: Invalid port index %d\n", __func__, port_index); + ret = -EINVAL; + goto fail_cmd; + } + + ret = afe_send_slot_mapping_cfg(&tdm_port->slot_mapping, + port_id); + if (ret < 0) { + pr_err("%s: afe send failed %d\n", __func__, ret); + goto fail_cmd; + } + + if (tdm_port->custom_tdm_header.header_type) { + ret = afe_send_custom_tdm_header_cfg( + &tdm_port->custom_tdm_header, port_id); + if (ret < 0) { + pr_err("%s: afe send failed %d\n", __func__, ret); + goto fail_cmd; + } + } + + ret = afe_send_cmd_port_start(port_id); + +fail_cmd: + return ret; +} +EXPORT_SYMBOL(afe_tdm_port_start); + +/** + * afe_set_cal_mode - + * set cal mode for AFE calibration + * + * @port_id: AFE port id number + * @afe_cal_mode: AFE calib mode + * + */ +void afe_set_cal_mode(u16 port_id, enum afe_cal_mode afe_cal_mode) +{ + uint16_t port_index; + + port_index = afe_get_port_index(port_id); + this_afe.afe_cal_mode[port_index] = afe_cal_mode; +} +EXPORT_SYMBOL(afe_set_cal_mode); + +/** + * afe_set_vad_cfg - + * set configuration for VAD + * + * @port_id: AFE port id number + * @vad_enable: enable/disable vad + * @preroll_config: Preroll configuration + * + */ +void afe_set_vad_cfg(u32 vad_enable, u32 preroll_config, + u32 port_id) +{ + uint16_t port_index; + + port_index = afe_get_port_index(port_id); + this_afe.vad_cfg[port_index].is_enable = vad_enable; + this_afe.vad_cfg[port_index].pre_roll = preroll_config; +} +EXPORT_SYMBOL(afe_set_vad_cfg); + +/** + * afe_get_island_mode_cfg - + * get island mode configuration + * + * @port_id: AFE port id number + * @enable_flag: Enable or Disable + * + */ +void afe_get_island_mode_cfg(u16 port_id, u32 *enable_flag) +{ + uint16_t port_index; + + if (enable_flag) { + port_index = afe_get_port_index(port_id); + *enable_flag = this_afe.island_mode[port_index]; + } +} +EXPORT_SYMBOL(afe_get_island_mode_cfg); + +/** + * afe_set_island_mode_cfg - + * set island mode configuration + * + * @port_id: AFE port id number + * @enable_flag: Enable or Disable + * + */ +void afe_set_island_mode_cfg(u16 port_id, u32 enable_flag) +{ + uint16_t port_index; + + port_index = afe_get_port_index(port_id); + this_afe.island_mode[port_index] = enable_flag; + +} +EXPORT_SYMBOL(afe_set_island_mode_cfg); + +/** + * afe_set_routing_callback - + * Update callback function for routing + * + * @cb: callback function to update with + * + */ +void afe_set_routing_callback(routing_cb cb) +{ + this_afe.rt_cb = cb; +} +EXPORT_SYMBOL(afe_set_routing_callback); + +int afe_port_send_usb_dev_param(u16 port_id, union afe_port_config *afe_config) +{ + struct afe_param_id_usb_audio_dev_params usb_dev; + struct afe_param_id_usb_audio_dev_lpcm_fmt lpcm_fmt; + struct afe_param_id_usb_audio_svc_interval svc_int; + struct param_hdr_v3 param_hdr; + int ret = 0, index = 0; + + if (!afe_config) { + pr_err("%s: Error, no configuration data\n", __func__); + ret = -EINVAL; + goto exit; + } + + 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; + } + memset(&usb_dev, 0, sizeof(usb_dev)); + memset(&lpcm_fmt, 0, sizeof(lpcm_fmt)); + memset(¶m_hdr, 0, sizeof(param_hdr)); + param_hdr.module_id = AFE_MODULE_AUDIO_DEV_INTERFACE; + param_hdr.instance_id = INSTANCE_ID_0; + + param_hdr.param_id = AFE_PARAM_ID_USB_AUDIO_DEV_PARAMS; + param_hdr.param_size = sizeof(usb_dev); + usb_dev.cfg_minor_version = AFE_API_MINOR_VERSION_USB_AUDIO_CONFIG; + usb_dev.dev_token = afe_config->usb_audio.dev_token; + + ret = q6afe_pack_and_set_param_in_band(port_id, + q6audio_get_port_index(port_id), + param_hdr, (u8 *) &usb_dev); + if (ret) { + pr_err("%s: AFE device param cmd failed %d\n", + __func__, ret); + goto exit; + } + + param_hdr.param_id = AFE_PARAM_ID_USB_AUDIO_DEV_LPCM_FMT; + param_hdr.param_size = sizeof(lpcm_fmt); + lpcm_fmt.cfg_minor_version = AFE_API_MINOR_VERSION_USB_AUDIO_CONFIG; + lpcm_fmt.endian = afe_config->usb_audio.endian; + + ret = q6afe_pack_and_set_param_in_band(port_id, + q6audio_get_port_index(port_id), + param_hdr, (u8 *) &lpcm_fmt); + if (ret) { + pr_err("%s: AFE device param cmd LPCM_FMT failed %d\n", + __func__, ret); + goto exit; + } + param_hdr.param_id = AFE_PARAM_ID_USB_AUDIO_SVC_INTERVAL; + param_hdr.param_size = sizeof(svc_int); + svc_int.cfg_minor_version = + AFE_API_MINOR_VERSION_USB_AUDIO_CONFIG; + svc_int.svc_interval = afe_config->usb_audio.service_interval; + + pr_debug("%s: AFE device param cmd sending SVC_INTERVAL %d\n", + __func__, svc_int.svc_interval); + + ret = q6afe_pack_and_set_param_in_band(port_id, + q6audio_get_port_index(port_id), + param_hdr, (u8 *) &svc_int); + + if (ret) { + pr_err("%s: AFE device param cmd svc_interval failed %d\n", + __func__, ret); + ret = -EINVAL; + goto exit; + } +exit: + return ret; +} + +static int q6afe_send_dec_config(u16 port_id, + union afe_port_config afe_config, + struct afe_dec_config *cfg, + u32 format, + u16 afe_in_channels, u16 afe_in_bit_width) +{ + struct afe_dec_media_fmt_t dec_media_fmt; + struct avs_dec_depacketizer_id_param_t dec_depkt_id_param; + struct avs_dec_congestion_buffer_param_t dec_buffer_id_param; + struct afe_enc_dec_imc_info_param_t imc_info_param; + struct afe_port_media_type_t media_type; + struct param_hdr_v3 param_hdr; + int ret; + u32 dec_fmt; + + memset(&dec_depkt_id_param, 0, sizeof(dec_depkt_id_param)); + memset(&dec_media_fmt, 0, sizeof(dec_media_fmt)); + memset(&imc_info_param, 0, sizeof(imc_info_param)); + memset(&media_type, 0, sizeof(media_type)); + memset(¶m_hdr, 0, sizeof(param_hdr)); + + param_hdr.module_id = AFE_MODULE_ID_DECODER; + param_hdr.instance_id = INSTANCE_ID_0; + + pr_debug("%s: sending AFE_DECODER_PARAM_ID_DEPACKETIZER to DSP payload\n", + __func__); + param_hdr.param_id = AFE_DECODER_PARAM_ID_DEPACKETIZER_ID; + param_hdr.param_size = sizeof(struct avs_dec_depacketizer_id_param_t); + dec_depkt_id_param.dec_depacketizer_id = + AFE_MODULE_ID_DEPACKETIZER_COP_V1; + if (cfg->format == ENC_CODEC_TYPE_LDAC) + dec_depkt_id_param.dec_depacketizer_id = + AFE_MODULE_ID_DEPACKETIZER_COP; + ret = q6afe_pack_and_set_param_in_band(port_id, + q6audio_get_port_index(port_id), + param_hdr, + (u8 *) &dec_depkt_id_param); + if (ret) { + pr_err("%s: AFE_DECODER_PARAM_ID_DEPACKETIZER for port 0x%x failed %d\n", + __func__, port_id, ret); + goto exit; + } + + switch (cfg->format) { + case ASM_MEDIA_FMT_SBC: + case ASM_MEDIA_FMT_AAC_V2: + case ASM_MEDIA_FMT_MP3: + if (port_id == SLIMBUS_9_TX) { + dec_buffer_id_param.max_nr_buffers = 200; + dec_buffer_id_param.pre_buffer_size = 200; + } else { + dec_buffer_id_param.max_nr_buffers = 0; + dec_buffer_id_param.pre_buffer_size = 0; + } + pr_debug("%s: sending AFE_DECODER_PARAM_ID_CONGESTION_BUFFER_SIZE to DSP payload\n", + __func__); + param_hdr.param_id = + AFE_DECODER_PARAM_ID_CONGESTION_BUFFER_SIZE; + param_hdr.param_size = + sizeof(struct avs_dec_congestion_buffer_param_t); + dec_buffer_id_param.version = 0; + ret = q6afe_pack_and_set_param_in_band(port_id, + q6audio_get_port_index(port_id), + param_hdr, + (u8 *) &dec_buffer_id_param); + if (ret) { + pr_err("%s: AFE_DECODER_PARAM_ID_CONGESTION_BUFFER_SIZE for port 0x%x failed %d\n", + __func__, port_id, ret); + goto exit; + } + break; + default: + pr_debug("%s:sending AFE_ENCDEC_PARAM_ID_DEC_TO_ENC_COMMUNICATION to DSP payload\n", + __func__); + param_hdr.param_id = + AFE_ENCDEC_PARAM_ID_DEC_TO_ENC_COMMUNICATION; + param_hdr.param_size = + sizeof(struct afe_enc_dec_imc_info_param_t); + imc_info_param.imc_info = cfg->abr_dec_cfg.imc_info; + ret = q6afe_pack_and_set_param_in_band(port_id, + q6audio_get_port_index(port_id), + param_hdr, + (u8 *) &imc_info_param); + if (ret) { + pr_err("%s: AFE_ENCDEC_PARAM_ID_DEC_TO_ENC_COMMUNICATION for port 0x%x failed %d\n", + __func__, port_id, ret); + goto exit; + } + break; + } + + pr_debug("%s:Sending AFE_API_VERSION_PORT_MEDIA_TYPE to DSP", __func__); + param_hdr.module_id = AFE_MODULE_PORT; + param_hdr.param_id = AFE_PARAM_ID_PORT_MEDIA_TYPE; + param_hdr.param_size = sizeof(struct afe_port_media_type_t); + media_type.minor_version = AFE_API_VERSION_PORT_MEDIA_TYPE; + switch (cfg->format) { + case ASM_MEDIA_FMT_AAC_V2: + media_type.sample_rate = + cfg->data.aac_config.sample_rate; + break; + default: + media_type.sample_rate = + afe_config.slim_sch.sample_rate; + } + if (afe_in_bit_width) + media_type.bit_width = afe_in_bit_width; + else + media_type.bit_width = afe_config.slim_sch.bit_width; + + if (afe_in_channels) + media_type.num_channels = afe_in_channels; + else + media_type.num_channels = afe_config.slim_sch.num_channels; + media_type.data_format = AFE_PORT_DATA_FORMAT_PCM; + media_type.reserved = 0; + + ret = q6afe_pack_and_set_param_in_band(port_id, + q6audio_get_port_index(port_id), + param_hdr, (u8 *) &media_type); + if (ret) { + pr_err("%s: AFE_API_VERSION_PORT_MEDIA_TYPE for port 0x%x failed %d\n", + __func__, port_id, ret); + goto exit; + } + + if (format != ASM_MEDIA_FMT_SBC && format != ASM_MEDIA_FMT_AAC_V2) { + pr_debug("%s:Unsuppported dec format. Ignore AFE config %u\n", + __func__, format); + goto exit; + } + pr_debug("%s: sending AFE_DECODER_PARAM_ID_DEC_MEDIA_FMT to DSP payload\n", + __func__); + param_hdr.module_id = AFE_MODULE_ID_DECODER; + param_hdr.instance_id = INSTANCE_ID_0; + param_hdr.param_id = AFE_DECODER_PARAM_ID_DEC_FMT_ID; + param_hdr.param_size = sizeof(dec_fmt); + dec_fmt = format; + ret = q6afe_pack_and_set_param_in_band(port_id, + q6audio_get_port_index(port_id), + param_hdr, (u8 *) &dec_fmt); + if (ret) { + pr_err("%s: AFE_DECODER_PARAM_ID_DEC_MEDIA_FMT for port 0x%x failed %d\n", + __func__, port_id, ret); + goto exit; + } + + switch (cfg->format) { + case ASM_MEDIA_FMT_AAC_V2: + param_hdr.param_size = sizeof(struct afe_dec_media_fmt_t); + + pr_debug("%s:send AFE_DECODER_PARAM_ID DEC_MEDIA_FMT to DSP payload\n", + __func__); + param_hdr.param_id = AVS_DECODER_PARAM_ID_DEC_MEDIA_FMT; + dec_media_fmt.dec_media_config = cfg->data; + ret = q6afe_pack_and_set_param_in_band(port_id, + q6audio_get_port_index(port_id), + param_hdr, + (u8 *) &dec_media_fmt); + if (ret) { + pr_err("%s: AFE_DECODER_PARAM_ID DEC_MEDIA_FMT for port 0x%x failed %d\n", + __func__, port_id, ret); + goto exit; + } + break; + default: + pr_debug("%s:No need to send DEC_MEDIA_FMT to DSP payload\n", + __func__); + } + +exit: + return ret; +} + +static int q6afe_send_enc_config(u16 port_id, + union afe_enc_config_data *cfg, u32 format, + union afe_port_config afe_config, + u16 afe_in_channels, u16 afe_in_bit_width, + u32 scrambler_mode, u32 mono_mode) +{ + u32 enc_fmt; + struct afe_enc_cfg_blk_param_t enc_blk_param; + struct afe_param_id_aptx_sync_mode sync_mode_param; + struct afe_id_aptx_adaptive_enc_init aptx_adaptive_enc_init; + struct avs_enc_packetizer_id_param_t enc_pkt_id_param; + struct avs_enc_set_scrambler_param_t enc_set_scrambler_param; + struct afe_enc_level_to_bitrate_map_param_t map_param; + struct afe_enc_dec_imc_info_param_t imc_info_param; + struct asm_aac_frame_size_control_t frame_ctl_param; + struct afe_port_media_type_t media_type; + struct aptx_channel_mode_param_t channel_mode_param; + struct param_hdr_v3 param_hdr; + int ret; + + pr_debug("%s:update DSP for enc format = %d\n", __func__, format); + + memset(&enc_blk_param, 0, sizeof(enc_blk_param)); + memset(&sync_mode_param, 0, sizeof(sync_mode_param)); + memset(&aptx_adaptive_enc_init, 0, sizeof(aptx_adaptive_enc_init)); + memset(&enc_pkt_id_param, 0, sizeof(enc_pkt_id_param)); + memset(&enc_set_scrambler_param, 0, sizeof(enc_set_scrambler_param)); + memset(&map_param, 0, sizeof(map_param)); + memset(&imc_info_param, 0, sizeof(imc_info_param)); + memset(&frame_ctl_param, 0, sizeof(frame_ctl_param)); + memset(&media_type, 0, sizeof(media_type)); + memset(¶m_hdr, 0, sizeof(param_hdr)); + + if (format != ASM_MEDIA_FMT_SBC && format != ASM_MEDIA_FMT_AAC_V2 && + format != ASM_MEDIA_FMT_APTX && format != ASM_MEDIA_FMT_APTX_HD && + format != ASM_MEDIA_FMT_CELT && format != ASM_MEDIA_FMT_LDAC && + format != ASM_MEDIA_FMT_APTX_ADAPTIVE) { + pr_err("%s:Unsuppported enc format. Ignore AFE config\n", + __func__); + return 0; + } + + param_hdr.module_id = AFE_MODULE_ID_ENCODER; + param_hdr.instance_id = INSTANCE_ID_0; + + param_hdr.param_id = AFE_ENCODER_PARAM_ID_ENC_FMT_ID; + param_hdr.param_size = sizeof(enc_fmt); + enc_fmt = format; + pr_debug("%s:sending AFE_ENCODER_PARAM_ID_ENC_FMT_ID payload\n", + __func__); + ret = q6afe_pack_and_set_param_in_band(port_id, + q6audio_get_port_index(port_id), + param_hdr, (u8 *) &enc_fmt); + if (ret) { + pr_err("%s:unable to send AFE_ENCODER_PARAM_ID_ENC_FMT_ID", + __func__); + goto exit; + } + + if (format == ASM_MEDIA_FMT_LDAC) { + param_hdr.param_size = sizeof(struct afe_enc_cfg_blk_param_t) + - sizeof(struct afe_abr_enc_cfg_t); + enc_blk_param.enc_cfg_blk_size = + sizeof(union afe_enc_config_data) + - sizeof(struct afe_abr_enc_cfg_t); + } else if (format == ASM_MEDIA_FMT_AAC_V2) { + param_hdr.param_size = sizeof(enc_blk_param) + - sizeof(struct asm_aac_frame_size_control_t); + enc_blk_param.enc_cfg_blk_size = + sizeof(enc_blk_param.enc_blk_config) + - sizeof(struct asm_aac_frame_size_control_t); + } else { + param_hdr.param_size = sizeof(struct afe_enc_cfg_blk_param_t); + enc_blk_param.enc_cfg_blk_size = + sizeof(union afe_enc_config_data); + } + pr_debug("%s:send AFE_ENCODER_PARAM_ID_ENC_CFG_BLK to DSP payload\n", + __func__); + param_hdr.param_id = AFE_ENCODER_PARAM_ID_ENC_CFG_BLK; + enc_blk_param.enc_blk_config = *cfg; + ret = q6afe_pack_and_set_param_in_band(port_id, + q6audio_get_port_index(port_id), + param_hdr, + (u8 *) &enc_blk_param); + if (ret) { + pr_err("%s: AFE_ENCODER_PARAM_ID_ENC_CFG_BLK for port 0x%x failed %d\n", + __func__, port_id, ret); + goto exit; + } + + if (format == ASM_MEDIA_FMT_AAC_V2) { + uint32_t frame_size_ctl_value = enc_blk_param.enc_blk_config. + aac_config.frame_ctl.ctl_value; + if (frame_size_ctl_value > 0) { + param_hdr.param_id = + AFE_PARAM_ID_AAC_FRM_SIZE_CONTROL; + param_hdr.param_size = sizeof(frame_ctl_param); + frame_ctl_param.ctl_type = enc_blk_param. + enc_blk_config.aac_config.frame_ctl.ctl_type; + frame_ctl_param.ctl_value = frame_size_ctl_value; + pr_debug("%s: send AFE_PARAM_ID_AAC_FRM_SIZE_CONTROL\n", + __func__); + ret = q6afe_pack_and_set_param_in_band(port_id, + q6audio_get_port_index(port_id), + param_hdr, + (u8 *) &frame_ctl_param); + if (ret) { + pr_err("%s: AAC_FRM_SIZE_CONTROL failed %d\n", + __func__, ret); + goto exit; + } + } + } + + if (format == ASM_MEDIA_FMT_APTX) { + pr_debug("%s: sending AFE_PARAM_ID_APTX_SYNC_MODE to DSP", + __func__); + param_hdr.param_id = AFE_PARAM_ID_APTX_SYNC_MODE; + param_hdr.param_size = + sizeof(struct afe_param_id_aptx_sync_mode); + sync_mode_param.sync_mode = + enc_blk_param.enc_blk_config.aptx_config. + aptx_v2_cfg.sync_mode; + ret = q6afe_pack_and_set_param_in_band(port_id, + q6audio_get_port_index(port_id), + param_hdr, + (u8 *) &sync_mode_param); + if (ret) { + pr_err("%s: AFE_PARAM_ID_APTX_SYNC_MODE for port 0x%x failed %d\n", + __func__, port_id, ret); + goto exit; + } + } + if (format == ASM_MEDIA_FMT_APTX_ADAPTIVE) { + pr_debug("%s: sending AFE_ID_APTX_ADAPTIVE_ENC_INIT to DSP\n", + __func__); + param_hdr.param_id = AFE_ID_APTX_ADAPTIVE_ENC_INIT; + param_hdr.param_size = + sizeof(struct afe_id_aptx_adaptive_enc_init); + aptx_adaptive_enc_init = + enc_blk_param.enc_blk_config.aptx_ad_config. + aptx_ad_cfg; + ret = q6afe_pack_and_set_param_in_band(port_id, + q6audio_get_port_index(port_id), + param_hdr, + (u8 *) &aptx_adaptive_enc_init); + if (ret) { + pr_err("%s: AFE_ID_APTX_ADAPTIVE_ENC_INIT for port 0x%x failed %d\n", + __func__, port_id, ret); + goto exit; + } + } + pr_debug("%s:sending AFE_ENCODER_PARAM_ID_PACKETIZER to DSP\n", + __func__); + param_hdr.param_id = AFE_ENCODER_PARAM_ID_PACKETIZER_ID; + param_hdr.param_size = sizeof(struct avs_enc_packetizer_id_param_t); + enc_pkt_id_param.enc_packetizer_id = AFE_MODULE_ID_PACKETIZER_COP; + ret = q6afe_pack_and_set_param_in_band(port_id, + q6audio_get_port_index(port_id), + param_hdr, + (u8 *) &enc_pkt_id_param); + if (ret) { + pr_err("%s: AFE_ENCODER_PARAM_ID_PACKETIZER for port 0x%x failed %d\n", + __func__, port_id, ret); + goto exit; + } + + pr_debug("%s:sending AFE_ENCODER_PARAM_ID_ENABLE_SCRAMBLING mode= %d to DSP payload\n", + __func__, scrambler_mode); + param_hdr.param_id = AFE_ENCODER_PARAM_ID_ENABLE_SCRAMBLING; + param_hdr.param_size = sizeof(struct avs_enc_set_scrambler_param_t); + enc_set_scrambler_param.enable_scrambler = scrambler_mode; + ret = q6afe_pack_and_set_param_in_band(port_id, + q6audio_get_port_index(port_id), + param_hdr, + (u8 *) &enc_set_scrambler_param); + if (ret) { + pr_err("%s: AFE_ENCODER_PARAM_ID_ENABLE_SCRAMBLING for port 0x%x failed %d\n", + __func__, port_id, ret); + goto exit; + } + + if (format == ASM_MEDIA_FMT_APTX) { + pr_debug("%s:sending CAPI_V2_PARAM_ID_APTX_ENC_SWITCH_TO_MONO mode= %d to DSP payload\n", + __func__, mono_mode); + param_hdr.param_id = CAPI_V2_PARAM_ID_APTX_ENC_SWITCH_TO_MONO; + param_hdr.param_size = sizeof(channel_mode_param); + channel_mode_param.channel_mode = mono_mode; + ret = q6afe_pack_and_set_param_in_band(port_id, + q6audio_get_port_index(port_id), + param_hdr, + (u8 *) &channel_mode_param); + + if (ret) { + pr_err("%s: CAPI_V2_PARAM_ID_APTX_ENC_SWITCH_TO_MONO for port 0x%x failed %d\n", + __func__, port_id, ret); + } + } + + if ((format == ASM_MEDIA_FMT_LDAC && + cfg->ldac_config.abr_config.is_abr_enabled) || + format == ASM_MEDIA_FMT_APTX_ADAPTIVE) { + pr_debug("%s:sending AFE_ENCODER_PARAM_ID_BIT_RATE_LEVEL_MAP to DSP payload", + __func__); + param_hdr.param_id = AFE_ENCODER_PARAM_ID_BIT_RATE_LEVEL_MAP; + param_hdr.param_size = + sizeof(struct afe_enc_level_to_bitrate_map_param_t); + map_param.mapping_table = + cfg->ldac_config.abr_config.mapping_info; + ret = q6afe_pack_and_set_param_in_band(port_id, + q6audio_get_port_index(port_id), + param_hdr, + (u8 *) &map_param); + if (ret) { + pr_err("%s: AFE_ENCODER_PARAM_ID_BIT_RATE_LEVEL_MAP for port 0x%x failed %d\n", + __func__, port_id, ret); + goto exit; + } + + pr_debug("%s: sending AFE_ENCDEC_PARAM_ID_DEC_TO_ENC_COMMUNICATION to DSP payload", + __func__); + param_hdr.param_id = + AFE_ENCDEC_PARAM_ID_DEC_TO_ENC_COMMUNICATION; + param_hdr.param_size = + sizeof(struct afe_enc_dec_imc_info_param_t); + if (format == ASM_MEDIA_FMT_APTX_ADAPTIVE) + imc_info_param.imc_info = + cfg->aptx_ad_config.abr_cfg.imc_info; + else + imc_info_param.imc_info = + cfg->ldac_config.abr_config.imc_info; + ret = q6afe_pack_and_set_param_in_band(port_id, + q6audio_get_port_index(port_id), + param_hdr, + (u8 *) &imc_info_param); + if (ret) { + pr_err("%s: AFE_ENCDEC_PARAM_ID_DEC_TO_ENC_COMMUNICATION for port 0x%x failed %d\n", + __func__, port_id, ret); + goto exit; + } + } + + pr_debug("%s:Sending AFE_API_VERSION_PORT_MEDIA_TYPE to DSP", __func__); + param_hdr.module_id = AFE_MODULE_PORT; + param_hdr.param_id = AFE_PARAM_ID_PORT_MEDIA_TYPE; + param_hdr.param_size = sizeof(struct afe_port_media_type_t); + media_type.minor_version = AFE_API_VERSION_PORT_MEDIA_TYPE; + if (format == ASM_MEDIA_FMT_LDAC) + media_type.sample_rate = + cfg->ldac_config.custom_config.sample_rate; + else if (format == ASM_MEDIA_FMT_APTX_ADAPTIVE) + media_type.sample_rate = + cfg->aptx_ad_config.custom_cfg.sample_rate; + else + media_type.sample_rate = + afe_config.slim_sch.sample_rate; + + if (afe_in_bit_width) + media_type.bit_width = afe_in_bit_width; + else + media_type.bit_width = afe_config.slim_sch.bit_width; + + if (afe_in_channels) + media_type.num_channels = afe_in_channels; + else + media_type.num_channels = afe_config.slim_sch.num_channels; + media_type.data_format = AFE_PORT_DATA_FORMAT_PCM; + media_type.reserved = 0; + + ret = q6afe_pack_and_set_param_in_band(port_id, + q6audio_get_port_index(port_id), + param_hdr, (u8 *) &media_type); + if (ret) { + pr_err("%s: AFE_API_VERSION_PORT_MEDIA_TYPE for port 0x%x failed %d\n", + __func__, port_id, ret); + goto exit; + } + +exit: + return ret; +} + +int afe_set_tws_channel_mode(u16 port_id, u32 channel_mode) +{ + struct aptx_channel_mode_param_t channel_mode_param; + struct param_hdr_v3 param_info; + int ret = 0; + + memset(¶m_info, 0, sizeof(param_info)); + memset(&channel_mode_param, 0, sizeof(channel_mode_param)); + + param_info.module_id = AFE_MODULE_ID_ENCODER; + param_info.instance_id = INSTANCE_ID_0; + param_info.param_id = CAPI_V2_PARAM_ID_APTX_ENC_SWITCH_TO_MONO; + param_info.param_size = sizeof(channel_mode_param); + + channel_mode_param.channel_mode = channel_mode; + + ret = q6afe_pack_and_set_param_in_band(port_id, + q6audio_get_port_index(port_id), + param_info, + (u8 *) &channel_mode_param); + if (ret) + pr_err("%s: AFE set channel mode cfg for port 0x%x failed %d\n", + __func__, port_id, ret); + + return ret; +} +EXPORT_SYMBOL(afe_set_tws_channel_mode); + +static int __afe_port_start(u16 port_id, union afe_port_config *afe_config, + u32 rate, u16 afe_in_channels, u16 afe_in_bit_width, + union afe_enc_config_data *enc_cfg, + u32 codec_format, u32 scrambler_mode, u32 mono_mode, + struct afe_dec_config *dec_cfg) +{ + union afe_port_config port_cfg; + struct param_hdr_v3 param_hdr; + int ret = 0; + int cfg_type; + int index = 0; + enum afe_mad_type mad_type; + uint16_t port_index; + + memset(¶m_hdr, 0, sizeof(param_hdr)); + memset(&port_cfg, 0, sizeof(port_cfg)); + + if (!afe_config) { + pr_err("%s: Error, no configuration data\n", __func__); + ret = -EINVAL; + return ret; + } + + if ((port_id == RT_PROXY_DAI_001_RX) || + (port_id == RT_PROXY_DAI_002_TX)) { + pr_debug("%s: before incrementing pcm_afe_instance %d port_id 0x%x\n", + __func__, + pcm_afe_instance[port_id & 0x1], port_id); + port_id = VIRTUAL_ID_TO_PORTID(port_id); + pcm_afe_instance[port_id & 0x1]++; + return 0; + } + if ((port_id == RT_PROXY_DAI_002_RX) || + (port_id == RT_PROXY_DAI_001_TX)) { + pr_debug("%s: before incrementing proxy_afe_instance %d port_id 0x%x\n", + __func__, + proxy_afe_instance[port_id & 0x1], port_id); + + if (!afe_close_done[port_id & 0x1]) { + /*close pcm dai corresponding to the proxy dai*/ + afe_close(port_id - 0x10); + pcm_afe_instance[port_id & 0x1]++; + pr_debug("%s: reconfigure afe port again\n", __func__); + } + proxy_afe_instance[port_id & 0x1]++; + afe_close_done[port_id & 0x1] = false; + port_id = VIRTUAL_ID_TO_PORTID(port_id); + } + + 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 = q6audio_validate_port(port_id); + if (ret < 0) { + pr_err("%s: port id: 0x%x ret %d\n", __func__, port_id, ret); + return -EINVAL; + } + + ret = afe_q6_interface_prepare(); + if (ret != 0) { + pr_err("%s: Q6 interface prepare failed %d\n", __func__, ret); + return ret; + } + + if ((index >= 0) && (index < AFE_MAX_PORTS)) { + this_afe.afe_sample_rates[index] = rate; + + if (this_afe.rt_cb) + this_afe.dev_acdb_id[index] = this_afe.rt_cb(port_id); + } + + mutex_lock(&this_afe.afe_cmd_lock); + port_index = afe_get_port_index(port_id); + + if (q6core_get_avcs_api_version_per_service( + APRV2_IDS_SERVICE_ID_ADSP_AFE_V) >= AFE_API_VERSION_V4) { + /* send VAD configuration if is enabled */ + if (this_afe.vad_cfg[port_index].is_enable) { + ret = afe_send_port_vad_cfg_params(port_id); + if (ret) { + pr_err("%s: afe send VAD config failed %d\n", + __func__, ret); + goto fail_cmd; + } + } + } + + /* Also send the topology id here: */ + if (!(this_afe.afe_cal_mode[port_index] == AFE_CAL_MODE_NONE)) { + /* One time call: only for first time */ + afe_send_custom_topology(); + afe_send_port_topology_id(port_id); + afe_send_cal(port_id); + afe_send_hw_delay(port_id, rate); + } + + /* Start SW MAD module */ + mad_type = afe_port_get_mad_type(port_id); + pr_debug("%s: port_id 0x%x, mad_type %d\n", __func__, port_id, + mad_type); + if (mad_type != MAD_HW_NONE && mad_type != MAD_SW_AUDIO) { + if (!afe_has_config(AFE_CDC_REGISTERS_CONFIG) || + !afe_has_config(AFE_SLIMBUS_SLAVE_CONFIG)) { + pr_err("%s: AFE isn't configured yet for\n" + "HW MAD try Again\n", __func__); + ret = -EAGAIN; + goto fail_cmd; + } + ret = afe_turn_onoff_hw_mad(mad_type, true); + if (ret) { + pr_err("%s: afe_turn_onoff_hw_mad failed %d\n", + __func__, ret); + goto fail_cmd; + } + } + + if ((this_afe.aanc_info.aanc_active) && + (this_afe.aanc_info.aanc_tx_port == port_id)) { + this_afe.aanc_info.aanc_tx_port_sample_rate = rate; + port_index = + afe_get_port_index(this_afe.aanc_info.aanc_rx_port); + if ((port_index >= 0) && (port_index < AFE_MAX_PORTS)) { + this_afe.aanc_info.aanc_rx_port_sample_rate = + this_afe.afe_sample_rates[port_index]; + } else { + pr_err("%s: Invalid port index %d\n", + __func__, port_index); + ret = -EINVAL; + goto fail_cmd; + } + ret = afe_aanc_start(this_afe.aanc_info.aanc_tx_port, + this_afe.aanc_info.aanc_rx_port); + pr_debug("%s: afe_aanc_start ret %d\n", __func__, ret); + } + + if ((port_id == AFE_PORT_ID_USB_RX) || + (port_id == AFE_PORT_ID_USB_TX)) { + ret = afe_port_send_usb_dev_param(port_id, afe_config); + if (ret) { + pr_err("%s: AFE device param for port 0x%x failed %d\n", + __func__, port_id, ret); + ret = -EINVAL; + goto fail_cmd; + } + } + + switch (port_id) { + case AFE_PORT_ID_PRIMARY_PCM_RX: + case AFE_PORT_ID_PRIMARY_PCM_TX: + case AFE_PORT_ID_SECONDARY_PCM_RX: + case AFE_PORT_ID_SECONDARY_PCM_TX: + case AFE_PORT_ID_TERTIARY_PCM_RX: + case AFE_PORT_ID_TERTIARY_PCM_TX: + case AFE_PORT_ID_QUATERNARY_PCM_RX: + case AFE_PORT_ID_QUATERNARY_PCM_TX: + case AFE_PORT_ID_QUINARY_PCM_RX: + case AFE_PORT_ID_QUINARY_PCM_TX: + case AFE_PORT_ID_SENARY_PCM_RX: + case AFE_PORT_ID_SENARY_PCM_TX: + cfg_type = AFE_PARAM_ID_PCM_CONFIG; + break; + case PRIMARY_I2S_RX: + case PRIMARY_I2S_TX: + case SECONDARY_I2S_RX: + case SECONDARY_I2S_TX: + case MI2S_RX: + case MI2S_TX: + case AFE_PORT_ID_PRIMARY_MI2S_RX: + case AFE_PORT_ID_PRIMARY_MI2S_TX: + case AFE_PORT_ID_SECONDARY_MI2S_RX: + case AFE_PORT_ID_SECONDARY_MI2S_RX_SD1: + case AFE_PORT_ID_SECONDARY_MI2S_TX: + case AFE_PORT_ID_TERTIARY_MI2S_RX: + case AFE_PORT_ID_TERTIARY_MI2S_TX: + case AFE_PORT_ID_QUATERNARY_MI2S_RX: + case AFE_PORT_ID_QUATERNARY_MI2S_TX: + case AFE_PORT_ID_QUINARY_MI2S_RX: + case AFE_PORT_ID_QUINARY_MI2S_TX: + case AFE_PORT_ID_SENARY_MI2S_TX: + case AFE_PORT_ID_INT0_MI2S_RX: + case AFE_PORT_ID_INT0_MI2S_TX: + case AFE_PORT_ID_INT1_MI2S_RX: + case AFE_PORT_ID_INT1_MI2S_TX: + case AFE_PORT_ID_INT2_MI2S_RX: + case AFE_PORT_ID_INT2_MI2S_TX: + case AFE_PORT_ID_INT3_MI2S_RX: + case AFE_PORT_ID_INT3_MI2S_TX: + case AFE_PORT_ID_INT4_MI2S_RX: + case AFE_PORT_ID_INT4_MI2S_TX: + case AFE_PORT_ID_INT5_MI2S_RX: + case AFE_PORT_ID_INT5_MI2S_TX: + case AFE_PORT_ID_INT6_MI2S_RX: + case AFE_PORT_ID_INT6_MI2S_TX: + cfg_type = AFE_PARAM_ID_I2S_CONFIG; + break; + case HDMI_RX: + case DISPLAY_PORT_RX: + cfg_type = AFE_PARAM_ID_HDMI_CONFIG; + break; + case VOICE_PLAYBACK_TX: + case VOICE2_PLAYBACK_TX: + case VOICE_RECORD_RX: + case VOICE_RECORD_TX: + cfg_type = AFE_PARAM_ID_PSEUDO_PORT_CONFIG; + break; + case SLIMBUS_0_RX: + case SLIMBUS_0_TX: + case SLIMBUS_1_RX: + case SLIMBUS_1_TX: + case SLIMBUS_2_RX: + case SLIMBUS_2_TX: + case SLIMBUS_3_RX: + case SLIMBUS_3_TX: + case SLIMBUS_4_RX: + case SLIMBUS_4_TX: + case SLIMBUS_5_RX: + case SLIMBUS_5_TX: + case SLIMBUS_6_RX: + case SLIMBUS_6_TX: + case SLIMBUS_7_RX: + case SLIMBUS_7_TX: + case SLIMBUS_8_RX: + case SLIMBUS_8_TX: + case SLIMBUS_9_RX: + case SLIMBUS_9_TX: + cfg_type = AFE_PARAM_ID_SLIMBUS_CONFIG; + break; + case AFE_PORT_ID_USB_RX: + case AFE_PORT_ID_USB_TX: + cfg_type = AFE_PARAM_ID_USB_AUDIO_CONFIG; + break; + case RT_PROXY_PORT_001_RX: + case RT_PROXY_PORT_001_TX: + cfg_type = AFE_PARAM_ID_RT_PROXY_CONFIG; + break; + case INT_BT_SCO_RX: + case INT_BT_A2DP_RX: + case INT_BT_SCO_TX: + case INT_FM_RX: + case INT_FM_TX: + cfg_type = AFE_PARAM_ID_INTERNAL_BT_FM_CONFIG; + break; + case AFE_PORT_ID_WSA_CODEC_DMA_RX_0: + case AFE_PORT_ID_WSA_CODEC_DMA_TX_0: + case AFE_PORT_ID_WSA_CODEC_DMA_RX_1: + case AFE_PORT_ID_WSA_CODEC_DMA_TX_1: + case AFE_PORT_ID_WSA_CODEC_DMA_TX_2: + case AFE_PORT_ID_VA_CODEC_DMA_TX_0: + case AFE_PORT_ID_VA_CODEC_DMA_TX_1: + case AFE_PORT_ID_RX_CODEC_DMA_RX_0: + case AFE_PORT_ID_TX_CODEC_DMA_TX_0: + case AFE_PORT_ID_RX_CODEC_DMA_RX_1: + case AFE_PORT_ID_TX_CODEC_DMA_TX_1: + case AFE_PORT_ID_RX_CODEC_DMA_RX_2: + case AFE_PORT_ID_TX_CODEC_DMA_TX_2: + case AFE_PORT_ID_RX_CODEC_DMA_RX_3: + case AFE_PORT_ID_TX_CODEC_DMA_TX_3: + case AFE_PORT_ID_RX_CODEC_DMA_RX_4: + case AFE_PORT_ID_TX_CODEC_DMA_TX_4: + case AFE_PORT_ID_RX_CODEC_DMA_RX_5: + case AFE_PORT_ID_TX_CODEC_DMA_TX_5: + case AFE_PORT_ID_RX_CODEC_DMA_RX_6: + case AFE_PORT_ID_RX_CODEC_DMA_RX_7: + cfg_type = AFE_PARAM_ID_CODEC_DMA_CONFIG; + break; + default: + pr_err("%s: Invalid port id 0x%x\n", __func__, port_id); + ret = -EINVAL; + goto fail_cmd; + } + + param_hdr.module_id = AFE_MODULE_AUDIO_DEV_INTERFACE; + param_hdr.instance_id = INSTANCE_ID_0; + param_hdr.param_id = cfg_type; + param_hdr.param_size = sizeof(union afe_port_config); + + port_cfg = *afe_config; + if (((enc_cfg != NULL) || (dec_cfg != NULL)) && + (codec_format != ASM_MEDIA_FMT_NONE) && + (cfg_type == AFE_PARAM_ID_SLIMBUS_CONFIG)) { + port_cfg.slim_sch.data_format = + AFE_SB_DATA_FORMAT_GENERIC_COMPRESSED; + } + ret = q6afe_pack_and_set_param_in_band(port_id, + q6audio_get_port_index(port_id), + param_hdr, (u8 *) &port_cfg); + if (ret) { + pr_err("%s: AFE enable for port 0x%x failed %d\n", + __func__, port_id, ret); + goto fail_cmd; + } + + if ((codec_format != ASM_MEDIA_FMT_NONE) && + (cfg_type == AFE_PARAM_ID_SLIMBUS_CONFIG)) { + if (enc_cfg != NULL) { + pr_debug("%s: Found AFE encoder support for SLIMBUS format = %d\n", + __func__, codec_format); + ret = q6afe_send_enc_config(port_id, enc_cfg, + codec_format, *afe_config, + afe_in_channels, + afe_in_bit_width, + scrambler_mode, mono_mode); + if (ret) { + pr_err("%s: AFE encoder config for port 0x%x failed %d\n", + __func__, port_id, ret); + goto fail_cmd; + } + } + if (dec_cfg != NULL) { + pr_debug("%s: Found AFE decoder support for SLIMBUS format = %d\n", + __func__, codec_format); + ret = q6afe_send_dec_config(port_id, *afe_config, + dec_cfg, codec_format, + afe_in_channels, + afe_in_bit_width); + if (ret) { + pr_err("%s: AFE decoder config for port 0x%x failed %d\n", + __func__, port_id, ret); + goto fail_cmd; + } + } + } + + port_index = afe_get_port_index(port_id); + if ((port_index >= 0) && (port_index < AFE_MAX_PORTS)) { + /* + * If afe_port_start() for tx port called before + * rx port, then aanc rx sample rate is zero. So, + * AANC state machine in AFE will not get triggered. + * Make sure to check whether aanc is active during + * afe_port_start() for rx port and if aanc rx + * sample rate is zero, call afe_aanc_start to configure + * aanc with valid sample rates. + */ + if (this_afe.aanc_info.aanc_active && + !this_afe.aanc_info.aanc_rx_port_sample_rate) { + this_afe.aanc_info.aanc_rx_port_sample_rate = + this_afe.afe_sample_rates[port_index]; + ret = afe_aanc_start(this_afe.aanc_info.aanc_tx_port, + this_afe.aanc_info.aanc_rx_port); + pr_debug("%s: afe_aanc_start ret %d\n", __func__, ret); + } + } else { + pr_err("%s: Invalid port index %d\n", __func__, port_index); + ret = -EINVAL; + goto fail_cmd; + } + ret = afe_send_cmd_port_start(port_id); + +fail_cmd: + mutex_unlock(&this_afe.afe_cmd_lock); + return ret; +} + +/** + * afe_port_start - to configure AFE session with + * specified port configuration + * + * @port_id: AFE port id number + * @afe_config: port configutation + * @rate: sampling rate of port + * + * Returns 0 on success or error value on port start failure. + */ +int afe_port_start(u16 port_id, union afe_port_config *afe_config, + u32 rate) +{ + return __afe_port_start(port_id, afe_config, rate, + 0, 0, NULL, ASM_MEDIA_FMT_NONE, 0, 0, NULL); +} +EXPORT_SYMBOL(afe_port_start); + +/** + * afe_port_start_v2 - to configure AFE session with + * specified port configuration and encoder /decoder params + * + * @port_id: AFE port id number + * @afe_config: port configutation + * @rate: sampling rate of port + * @enc_cfg: AFE enc configuration information to setup encoder + * @afe_in_channels: AFE input channel configuration, this needs + * update only if input channel is differ from AFE output + * @dec_cfg: AFE dec configuration information to set up decoder + * + * Returns 0 on success or error value on port start failure. + */ +int afe_port_start_v2(u16 port_id, union afe_port_config *afe_config, + u32 rate, u16 afe_in_channels, u16 afe_in_bit_width, + struct afe_enc_config *enc_cfg, + struct afe_dec_config *dec_cfg) +{ + int ret = 0; + + if (enc_cfg != NULL) + ret = __afe_port_start(port_id, afe_config, rate, + afe_in_channels, afe_in_bit_width, + &enc_cfg->data, enc_cfg->format, + enc_cfg->scrambler_mode, + enc_cfg->mono_mode, dec_cfg); + else if (dec_cfg != NULL) + ret = __afe_port_start(port_id, afe_config, rate, + afe_in_channels, afe_in_bit_width, + NULL, dec_cfg->format, 0, 0, dec_cfg); + + return ret; +} +EXPORT_SYMBOL(afe_port_start_v2); + +int afe_get_port_index(u16 port_id) +{ + switch (port_id) { + case PRIMARY_I2S_RX: return IDX_PRIMARY_I2S_RX; + case PRIMARY_I2S_TX: return IDX_PRIMARY_I2S_TX; + case AFE_PORT_ID_PRIMARY_PCM_RX: + return IDX_AFE_PORT_ID_PRIMARY_PCM_RX; + case AFE_PORT_ID_PRIMARY_PCM_TX: + return IDX_AFE_PORT_ID_PRIMARY_PCM_TX; + case AFE_PORT_ID_SECONDARY_PCM_RX: + return IDX_AFE_PORT_ID_SECONDARY_PCM_RX; + case AFE_PORT_ID_SECONDARY_PCM_TX: + return IDX_AFE_PORT_ID_SECONDARY_PCM_TX; + case AFE_PORT_ID_TERTIARY_PCM_RX: + return IDX_AFE_PORT_ID_TERTIARY_PCM_RX; + case AFE_PORT_ID_TERTIARY_PCM_TX: + return IDX_AFE_PORT_ID_TERTIARY_PCM_TX; + case AFE_PORT_ID_QUATERNARY_PCM_RX: + return IDX_AFE_PORT_ID_QUATERNARY_PCM_RX; + case AFE_PORT_ID_QUATERNARY_PCM_TX: + return IDX_AFE_PORT_ID_QUATERNARY_PCM_TX; + case AFE_PORT_ID_QUINARY_PCM_RX: + return IDX_AFE_PORT_ID_QUINARY_PCM_RX; + case AFE_PORT_ID_QUINARY_PCM_TX: + return IDX_AFE_PORT_ID_QUINARY_PCM_TX; + case AFE_PORT_ID_SENARY_PCM_RX: + return IDX_AFE_PORT_ID_SENARY_PCM_RX; + case AFE_PORT_ID_SENARY_PCM_TX: + return IDX_AFE_PORT_ID_SENARY_PCM_TX; + case SECONDARY_I2S_RX: return IDX_SECONDARY_I2S_RX; + case SECONDARY_I2S_TX: return IDX_SECONDARY_I2S_TX; + case MI2S_RX: return IDX_MI2S_RX; + case MI2S_TX: return IDX_MI2S_TX; + case HDMI_RX: return IDX_HDMI_RX; + case DISPLAY_PORT_RX: return IDX_DISPLAY_PORT_RX; + case AFE_PORT_ID_PRIMARY_SPDIF_RX: return IDX_PRIMARY_SPDIF_RX; + case AFE_PORT_ID_PRIMARY_SPDIF_TX: return IDX_PRIMARY_SPDIF_TX; + case AFE_PORT_ID_SECONDARY_SPDIF_RX: return IDX_SECONDARY_SPDIF_RX; + case AFE_PORT_ID_SECONDARY_SPDIF_TX: return IDX_SECONDARY_SPDIF_TX; + case RSVD_2: return IDX_RSVD_2; + case RSVD_3: return IDX_RSVD_3; + case DIGI_MIC_TX: return IDX_DIGI_MIC_TX; + case VOICE_RECORD_RX: return IDX_VOICE_RECORD_RX; + case VOICE_RECORD_TX: return IDX_VOICE_RECORD_TX; + case VOICE_PLAYBACK_TX: return IDX_VOICE_PLAYBACK_TX; + case VOICE2_PLAYBACK_TX: return IDX_VOICE2_PLAYBACK_TX; + case SLIMBUS_0_RX: return IDX_SLIMBUS_0_RX; + case SLIMBUS_0_TX: return IDX_SLIMBUS_0_TX; + case SLIMBUS_1_RX: return IDX_SLIMBUS_1_RX; + case SLIMBUS_1_TX: return IDX_SLIMBUS_1_TX; + case SLIMBUS_2_RX: return IDX_SLIMBUS_2_RX; + case SLIMBUS_2_TX: return IDX_SLIMBUS_2_TX; + case SLIMBUS_3_RX: return IDX_SLIMBUS_3_RX; + case SLIMBUS_3_TX: return IDX_SLIMBUS_3_TX; + case INT_BT_SCO_RX: return IDX_INT_BT_SCO_RX; + case INT_BT_SCO_TX: return IDX_INT_BT_SCO_TX; + case INT_BT_A2DP_RX: return IDX_INT_BT_A2DP_RX; + case INT_FM_RX: return IDX_INT_FM_RX; + case INT_FM_TX: return IDX_INT_FM_TX; + case RT_PROXY_PORT_001_RX: return IDX_RT_PROXY_PORT_001_RX; + case RT_PROXY_PORT_001_TX: return IDX_RT_PROXY_PORT_001_TX; + case SLIMBUS_4_RX: return IDX_SLIMBUS_4_RX; + case SLIMBUS_4_TX: return IDX_SLIMBUS_4_TX; + case SLIMBUS_5_RX: return IDX_SLIMBUS_5_RX; + case SLIMBUS_5_TX: return IDX_SLIMBUS_5_TX; + case SLIMBUS_6_RX: return IDX_SLIMBUS_6_RX; + case SLIMBUS_6_TX: return IDX_SLIMBUS_6_TX; + case SLIMBUS_7_RX: return IDX_SLIMBUS_7_RX; + case SLIMBUS_7_TX: return IDX_SLIMBUS_7_TX; + case SLIMBUS_8_RX: return IDX_SLIMBUS_8_RX; + case SLIMBUS_8_TX: return IDX_SLIMBUS_8_TX; + case SLIMBUS_9_RX: return IDX_SLIMBUS_9_RX; + case SLIMBUS_9_TX: return IDX_SLIMBUS_9_TX; + case AFE_PORT_ID_USB_RX: return IDX_AFE_PORT_ID_USB_RX; + case AFE_PORT_ID_USB_TX: return IDX_AFE_PORT_ID_USB_TX; + case AFE_PORT_ID_PRIMARY_MI2S_RX: + return IDX_AFE_PORT_ID_PRIMARY_MI2S_RX; + case AFE_PORT_ID_PRIMARY_MI2S_TX: + return IDX_AFE_PORT_ID_PRIMARY_MI2S_TX; + case AFE_PORT_ID_QUATERNARY_MI2S_RX: + return IDX_AFE_PORT_ID_QUATERNARY_MI2S_RX; + case AFE_PORT_ID_QUATERNARY_MI2S_TX: + return IDX_AFE_PORT_ID_QUATERNARY_MI2S_TX; + case AFE_PORT_ID_SECONDARY_MI2S_RX: + return IDX_AFE_PORT_ID_SECONDARY_MI2S_RX; + case AFE_PORT_ID_SECONDARY_MI2S_TX: + return IDX_AFE_PORT_ID_SECONDARY_MI2S_TX; + case AFE_PORT_ID_TERTIARY_MI2S_RX: + return IDX_AFE_PORT_ID_TERTIARY_MI2S_RX; + case AFE_PORT_ID_TERTIARY_MI2S_TX: + return IDX_AFE_PORT_ID_TERTIARY_MI2S_TX; + case AFE_PORT_ID_SECONDARY_MI2S_RX_SD1: + return IDX_AFE_PORT_ID_SECONDARY_MI2S_RX_SD1; + case AFE_PORT_ID_QUINARY_MI2S_RX: + return IDX_AFE_PORT_ID_QUINARY_MI2S_RX; + case AFE_PORT_ID_QUINARY_MI2S_TX: + return IDX_AFE_PORT_ID_QUINARY_MI2S_TX; + case AFE_PORT_ID_SENARY_MI2S_TX: + return IDX_AFE_PORT_ID_SENARY_MI2S_TX; + case AFE_PORT_ID_PRIMARY_TDM_RX: + return IDX_AFE_PORT_ID_PRIMARY_TDM_RX_0; + case AFE_PORT_ID_PRIMARY_TDM_TX: + return IDX_AFE_PORT_ID_PRIMARY_TDM_TX_0; + case AFE_PORT_ID_PRIMARY_TDM_RX_1: + return IDX_AFE_PORT_ID_PRIMARY_TDM_RX_1; + case AFE_PORT_ID_PRIMARY_TDM_TX_1: + return IDX_AFE_PORT_ID_PRIMARY_TDM_TX_1; + case AFE_PORT_ID_PRIMARY_TDM_RX_2: + return IDX_AFE_PORT_ID_PRIMARY_TDM_RX_2; + case AFE_PORT_ID_PRIMARY_TDM_TX_2: + return IDX_AFE_PORT_ID_PRIMARY_TDM_TX_2; + case AFE_PORT_ID_PRIMARY_TDM_RX_3: + return IDX_AFE_PORT_ID_PRIMARY_TDM_RX_3; + case AFE_PORT_ID_PRIMARY_TDM_TX_3: + return IDX_AFE_PORT_ID_PRIMARY_TDM_TX_3; + case AFE_PORT_ID_PRIMARY_TDM_RX_4: + return IDX_AFE_PORT_ID_PRIMARY_TDM_RX_4; + case AFE_PORT_ID_PRIMARY_TDM_TX_4: + return IDX_AFE_PORT_ID_PRIMARY_TDM_TX_4; + case AFE_PORT_ID_PRIMARY_TDM_RX_5: + return IDX_AFE_PORT_ID_PRIMARY_TDM_RX_5; + case AFE_PORT_ID_PRIMARY_TDM_TX_5: + return IDX_AFE_PORT_ID_PRIMARY_TDM_TX_5; + case AFE_PORT_ID_PRIMARY_TDM_RX_6: + return IDX_AFE_PORT_ID_PRIMARY_TDM_RX_6; + case AFE_PORT_ID_PRIMARY_TDM_TX_6: + return IDX_AFE_PORT_ID_PRIMARY_TDM_TX_6; + case AFE_PORT_ID_PRIMARY_TDM_RX_7: + return IDX_AFE_PORT_ID_PRIMARY_TDM_RX_7; + case AFE_PORT_ID_PRIMARY_TDM_TX_7: + return IDX_AFE_PORT_ID_PRIMARY_TDM_TX_7; + case AFE_PORT_ID_SECONDARY_TDM_RX: + return IDX_AFE_PORT_ID_SECONDARY_TDM_RX_0; + case AFE_PORT_ID_SECONDARY_TDM_TX: + return IDX_AFE_PORT_ID_SECONDARY_TDM_TX_0; + case AFE_PORT_ID_SECONDARY_TDM_RX_1: + return IDX_AFE_PORT_ID_SECONDARY_TDM_RX_1; + case AFE_PORT_ID_SECONDARY_TDM_TX_1: + return IDX_AFE_PORT_ID_SECONDARY_TDM_TX_1; + case AFE_PORT_ID_SECONDARY_TDM_RX_2: + return IDX_AFE_PORT_ID_SECONDARY_TDM_RX_2; + case AFE_PORT_ID_SECONDARY_TDM_TX_2: + return IDX_AFE_PORT_ID_SECONDARY_TDM_TX_2; + case AFE_PORT_ID_SECONDARY_TDM_RX_3: + return IDX_AFE_PORT_ID_SECONDARY_TDM_RX_3; + case AFE_PORT_ID_SECONDARY_TDM_TX_3: + return IDX_AFE_PORT_ID_SECONDARY_TDM_TX_3; + case AFE_PORT_ID_SECONDARY_TDM_RX_4: + return IDX_AFE_PORT_ID_SECONDARY_TDM_RX_4; + case AFE_PORT_ID_SECONDARY_TDM_TX_4: + return IDX_AFE_PORT_ID_SECONDARY_TDM_TX_4; + case AFE_PORT_ID_SECONDARY_TDM_RX_5: + return IDX_AFE_PORT_ID_SECONDARY_TDM_RX_5; + case AFE_PORT_ID_SECONDARY_TDM_TX_5: + return IDX_AFE_PORT_ID_SECONDARY_TDM_TX_5; + case AFE_PORT_ID_SECONDARY_TDM_RX_6: + return IDX_AFE_PORT_ID_SECONDARY_TDM_RX_6; + case AFE_PORT_ID_SECONDARY_TDM_TX_6: + return IDX_AFE_PORT_ID_SECONDARY_TDM_TX_6; + case AFE_PORT_ID_SECONDARY_TDM_RX_7: + return IDX_AFE_PORT_ID_SECONDARY_TDM_RX_7; + case AFE_PORT_ID_SECONDARY_TDM_TX_7: + return IDX_AFE_PORT_ID_SECONDARY_TDM_TX_7; + case AFE_PORT_ID_TERTIARY_TDM_RX: + return IDX_AFE_PORT_ID_TERTIARY_TDM_RX_0; + case AFE_PORT_ID_TERTIARY_TDM_TX: + return IDX_AFE_PORT_ID_TERTIARY_TDM_TX_0; + case AFE_PORT_ID_TERTIARY_TDM_RX_1: + return IDX_AFE_PORT_ID_TERTIARY_TDM_RX_1; + case AFE_PORT_ID_TERTIARY_TDM_TX_1: + return IDX_AFE_PORT_ID_TERTIARY_TDM_TX_1; + case AFE_PORT_ID_TERTIARY_TDM_RX_2: + return IDX_AFE_PORT_ID_TERTIARY_TDM_RX_2; + case AFE_PORT_ID_TERTIARY_TDM_TX_2: + return IDX_AFE_PORT_ID_TERTIARY_TDM_TX_2; + case AFE_PORT_ID_TERTIARY_TDM_RX_3: + return IDX_AFE_PORT_ID_TERTIARY_TDM_RX_3; + case AFE_PORT_ID_TERTIARY_TDM_TX_3: + return IDX_AFE_PORT_ID_TERTIARY_TDM_TX_3; + case AFE_PORT_ID_TERTIARY_TDM_RX_4: + return IDX_AFE_PORT_ID_TERTIARY_TDM_RX_4; + case AFE_PORT_ID_TERTIARY_TDM_TX_4: + return IDX_AFE_PORT_ID_TERTIARY_TDM_TX_4; + case AFE_PORT_ID_TERTIARY_TDM_RX_5: + return IDX_AFE_PORT_ID_TERTIARY_TDM_RX_5; + case AFE_PORT_ID_TERTIARY_TDM_TX_5: + return IDX_AFE_PORT_ID_TERTIARY_TDM_TX_5; + case AFE_PORT_ID_TERTIARY_TDM_RX_6: + return IDX_AFE_PORT_ID_TERTIARY_TDM_RX_6; + case AFE_PORT_ID_TERTIARY_TDM_TX_6: + return IDX_AFE_PORT_ID_TERTIARY_TDM_TX_6; + case AFE_PORT_ID_TERTIARY_TDM_RX_7: + return IDX_AFE_PORT_ID_TERTIARY_TDM_RX_7; + case AFE_PORT_ID_TERTIARY_TDM_TX_7: + return IDX_AFE_PORT_ID_TERTIARY_TDM_TX_7; + case AFE_PORT_ID_QUATERNARY_TDM_RX: + return IDX_AFE_PORT_ID_QUATERNARY_TDM_RX_0; + case AFE_PORT_ID_QUATERNARY_TDM_TX: + return IDX_AFE_PORT_ID_QUATERNARY_TDM_TX_0; + case AFE_PORT_ID_QUATERNARY_TDM_RX_1: + return IDX_AFE_PORT_ID_QUATERNARY_TDM_RX_1; + case AFE_PORT_ID_QUATERNARY_TDM_TX_1: + return IDX_AFE_PORT_ID_QUATERNARY_TDM_TX_1; + case AFE_PORT_ID_QUATERNARY_TDM_RX_2: + return IDX_AFE_PORT_ID_QUATERNARY_TDM_RX_2; + case AFE_PORT_ID_QUATERNARY_TDM_TX_2: + return IDX_AFE_PORT_ID_QUATERNARY_TDM_TX_2; + case AFE_PORT_ID_QUATERNARY_TDM_RX_3: + return IDX_AFE_PORT_ID_QUATERNARY_TDM_RX_3; + case AFE_PORT_ID_QUATERNARY_TDM_TX_3: + return IDX_AFE_PORT_ID_QUATERNARY_TDM_TX_3; + case AFE_PORT_ID_QUATERNARY_TDM_RX_4: + return IDX_AFE_PORT_ID_QUATERNARY_TDM_RX_4; + case AFE_PORT_ID_QUATERNARY_TDM_TX_4: + return IDX_AFE_PORT_ID_QUATERNARY_TDM_TX_4; + case AFE_PORT_ID_QUATERNARY_TDM_RX_5: + return IDX_AFE_PORT_ID_QUATERNARY_TDM_RX_5; + case AFE_PORT_ID_QUATERNARY_TDM_TX_5: + return IDX_AFE_PORT_ID_QUATERNARY_TDM_TX_5; + case AFE_PORT_ID_QUATERNARY_TDM_RX_6: + return IDX_AFE_PORT_ID_QUATERNARY_TDM_RX_6; + case AFE_PORT_ID_QUATERNARY_TDM_TX_6: + return IDX_AFE_PORT_ID_QUATERNARY_TDM_TX_6; + case AFE_PORT_ID_QUATERNARY_TDM_RX_7: + return IDX_AFE_PORT_ID_QUATERNARY_TDM_RX_7; + case AFE_PORT_ID_QUATERNARY_TDM_TX_7: + return IDX_AFE_PORT_ID_QUATERNARY_TDM_TX_7; + case AFE_PORT_ID_QUINARY_TDM_RX: + return IDX_AFE_PORT_ID_QUINARY_TDM_RX_0; + case AFE_PORT_ID_QUINARY_TDM_TX: + return IDX_AFE_PORT_ID_QUINARY_TDM_TX_0; + case AFE_PORT_ID_QUINARY_TDM_RX_1: + return IDX_AFE_PORT_ID_QUINARY_TDM_RX_1; + case AFE_PORT_ID_QUINARY_TDM_TX_1: + return IDX_AFE_PORT_ID_QUINARY_TDM_TX_1; + case AFE_PORT_ID_QUINARY_TDM_RX_2: + return IDX_AFE_PORT_ID_QUINARY_TDM_RX_2; + case AFE_PORT_ID_QUINARY_TDM_TX_2: + return IDX_AFE_PORT_ID_QUINARY_TDM_TX_2; + case AFE_PORT_ID_QUINARY_TDM_RX_3: + return IDX_AFE_PORT_ID_QUINARY_TDM_RX_3; + case AFE_PORT_ID_QUINARY_TDM_TX_3: + return IDX_AFE_PORT_ID_QUINARY_TDM_TX_3; + case AFE_PORT_ID_QUINARY_TDM_RX_4: + return IDX_AFE_PORT_ID_QUINARY_TDM_RX_4; + case AFE_PORT_ID_QUINARY_TDM_TX_4: + return IDX_AFE_PORT_ID_QUINARY_TDM_TX_4; + case AFE_PORT_ID_QUINARY_TDM_RX_5: + return IDX_AFE_PORT_ID_QUINARY_TDM_RX_5; + case AFE_PORT_ID_QUINARY_TDM_TX_5: + return IDX_AFE_PORT_ID_QUINARY_TDM_TX_5; + case AFE_PORT_ID_QUINARY_TDM_RX_6: + return IDX_AFE_PORT_ID_QUINARY_TDM_RX_6; + case AFE_PORT_ID_QUINARY_TDM_TX_6: + return IDX_AFE_PORT_ID_QUINARY_TDM_TX_6; + case AFE_PORT_ID_QUINARY_TDM_RX_7: + return IDX_AFE_PORT_ID_QUINARY_TDM_RX_7; + case AFE_PORT_ID_QUINARY_TDM_TX_7: + return IDX_AFE_PORT_ID_QUINARY_TDM_TX_7; + case AFE_PORT_ID_INT0_MI2S_RX: + return IDX_AFE_PORT_ID_INT0_MI2S_RX; + case AFE_PORT_ID_INT0_MI2S_TX: + return IDX_AFE_PORT_ID_INT0_MI2S_TX; + case AFE_PORT_ID_INT1_MI2S_RX: + return IDX_AFE_PORT_ID_INT1_MI2S_RX; + case AFE_PORT_ID_INT1_MI2S_TX: + return IDX_AFE_PORT_ID_INT1_MI2S_TX; + case AFE_PORT_ID_INT2_MI2S_RX: + return IDX_AFE_PORT_ID_INT2_MI2S_RX; + case AFE_PORT_ID_INT2_MI2S_TX: + return IDX_AFE_PORT_ID_INT2_MI2S_TX; + case AFE_PORT_ID_INT3_MI2S_RX: + return IDX_AFE_PORT_ID_INT3_MI2S_RX; + case AFE_PORT_ID_INT3_MI2S_TX: + return IDX_AFE_PORT_ID_INT3_MI2S_TX; + case AFE_PORT_ID_INT4_MI2S_RX: + return IDX_AFE_PORT_ID_INT4_MI2S_RX; + case AFE_PORT_ID_INT4_MI2S_TX: + return IDX_AFE_PORT_ID_INT4_MI2S_TX; + case AFE_PORT_ID_INT5_MI2S_RX: + return IDX_AFE_PORT_ID_INT5_MI2S_RX; + case AFE_PORT_ID_INT5_MI2S_TX: + return IDX_AFE_PORT_ID_INT5_MI2S_TX; + case AFE_PORT_ID_INT6_MI2S_RX: + return IDX_AFE_PORT_ID_INT6_MI2S_RX; + case AFE_PORT_ID_INT6_MI2S_TX: + return IDX_AFE_PORT_ID_INT6_MI2S_TX; + case AFE_PORT_ID_VA_CODEC_DMA_TX_0: + return IDX_AFE_PORT_ID_VA_CODEC_DMA_TX_0; + case AFE_PORT_ID_VA_CODEC_DMA_TX_1: + return IDX_AFE_PORT_ID_VA_CODEC_DMA_TX_1; + case AFE_PORT_ID_WSA_CODEC_DMA_RX_0: + return IDX_AFE_PORT_ID_WSA_CODEC_DMA_RX_0; + case AFE_PORT_ID_WSA_CODEC_DMA_TX_0: + return IDX_AFE_PORT_ID_WSA_CODEC_DMA_TX_0; + case AFE_PORT_ID_WSA_CODEC_DMA_RX_1: + return IDX_AFE_PORT_ID_WSA_CODEC_DMA_RX_1; + case AFE_PORT_ID_WSA_CODEC_DMA_TX_1: + return IDX_AFE_PORT_ID_WSA_CODEC_DMA_TX_1; + case AFE_PORT_ID_WSA_CODEC_DMA_TX_2: + return IDX_AFE_PORT_ID_WSA_CODEC_DMA_TX_2; + case AFE_PORT_ID_RX_CODEC_DMA_RX_0: + return IDX_AFE_PORT_ID_RX_CODEC_DMA_RX_0; + case AFE_PORT_ID_TX_CODEC_DMA_TX_0: + return IDX_AFE_PORT_ID_TX_CODEC_DMA_TX_0; + case AFE_PORT_ID_RX_CODEC_DMA_RX_1: + return IDX_AFE_PORT_ID_RX_CODEC_DMA_RX_1; + case AFE_PORT_ID_TX_CODEC_DMA_TX_1: + return IDX_AFE_PORT_ID_TX_CODEC_DMA_TX_1; + case AFE_PORT_ID_RX_CODEC_DMA_RX_2: + return IDX_AFE_PORT_ID_RX_CODEC_DMA_RX_2; + case AFE_PORT_ID_TX_CODEC_DMA_TX_2: + return IDX_AFE_PORT_ID_TX_CODEC_DMA_TX_2; + case AFE_PORT_ID_RX_CODEC_DMA_RX_3: + return IDX_AFE_PORT_ID_RX_CODEC_DMA_RX_3; + case AFE_PORT_ID_TX_CODEC_DMA_TX_3: + return IDX_AFE_PORT_ID_TX_CODEC_DMA_TX_3; + case AFE_PORT_ID_RX_CODEC_DMA_RX_4: + return IDX_AFE_PORT_ID_RX_CODEC_DMA_RX_4; + case AFE_PORT_ID_TX_CODEC_DMA_TX_4: + return IDX_AFE_PORT_ID_TX_CODEC_DMA_TX_4; + case AFE_PORT_ID_RX_CODEC_DMA_RX_5: + return IDX_AFE_PORT_ID_RX_CODEC_DMA_RX_5; + case AFE_PORT_ID_TX_CODEC_DMA_TX_5: + return IDX_AFE_PORT_ID_TX_CODEC_DMA_TX_5; + case AFE_PORT_ID_RX_CODEC_DMA_RX_6: + return IDX_AFE_PORT_ID_RX_CODEC_DMA_RX_6; + case AFE_PORT_ID_RX_CODEC_DMA_RX_7: + return IDX_AFE_PORT_ID_RX_CODEC_DMA_RX_7; + default: + pr_err("%s: port 0x%x\n", __func__, port_id); + return -EINVAL; + } +} + +/** + * afe_open - + * command to open AFE port + * + * @port_id: AFE port id + * @afe_config: AFE port config to pass + * @rate: sample rate + * + * Returns 0 on success or error on failure + */ +int afe_open(u16 port_id, + union afe_port_config *afe_config, int rate) +{ + struct afe_port_cmd_device_start start; + union afe_port_config port_cfg; + struct param_hdr_v3 param_hdr; + int ret = 0; + int cfg_type; + int index = 0; + + memset(¶m_hdr, 0, sizeof(param_hdr)); + memset(&start, 0, sizeof(start)); + memset(&port_cfg, 0, sizeof(port_cfg)); + + if (!afe_config) { + pr_err("%s: Error, no configuration data\n", __func__); + ret = -EINVAL; + return ret; + } + + pr_err("%s: port_id 0x%x rate %d\n", __func__, port_id, rate); + + 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; + } + + if ((port_id == RT_PROXY_DAI_001_RX) || + (port_id == RT_PROXY_DAI_002_TX)) { + pr_err("%s: wrong port 0x%x\n", __func__, port_id); + return -EINVAL; + } + if ((port_id == RT_PROXY_DAI_002_RX) || + (port_id == RT_PROXY_DAI_001_TX)) + port_id = VIRTUAL_ID_TO_PORTID(port_id); + + ret = afe_q6_interface_prepare(); + if (ret != 0) { + pr_err("%s: Q6 interface prepare failed %d\n", __func__, ret); + return -EINVAL; + } + + if ((index >= 0) && (index < AFE_MAX_PORTS)) { + this_afe.afe_sample_rates[index] = rate; + + if (this_afe.rt_cb) + this_afe.dev_acdb_id[index] = this_afe.rt_cb(port_id); + } + + /* Also send the topology id here: */ + afe_send_custom_topology(); /* One time call: only for first time */ + afe_send_port_topology_id(port_id); + + ret = q6audio_validate_port(port_id); + if (ret < 0) { + pr_err("%s: Failed : Invalid Port id = 0x%x ret %d\n", + __func__, port_id, ret); + return -EINVAL; + } + mutex_lock(&this_afe.afe_cmd_lock); + + switch (port_id) { + case PRIMARY_I2S_RX: + case PRIMARY_I2S_TX: + cfg_type = AFE_PARAM_ID_I2S_CONFIG; + break; + case AFE_PORT_ID_PRIMARY_PCM_RX: + case AFE_PORT_ID_PRIMARY_PCM_TX: + case AFE_PORT_ID_SECONDARY_PCM_RX: + case AFE_PORT_ID_SECONDARY_PCM_TX: + case AFE_PORT_ID_TERTIARY_PCM_RX: + case AFE_PORT_ID_TERTIARY_PCM_TX: + case AFE_PORT_ID_QUATERNARY_PCM_RX: + case AFE_PORT_ID_QUATERNARY_PCM_TX: + case AFE_PORT_ID_QUINARY_PCM_RX: + case AFE_PORT_ID_QUINARY_PCM_TX: + case AFE_PORT_ID_SENARY_PCM_RX: + case AFE_PORT_ID_SENARY_PCM_TX: + cfg_type = AFE_PARAM_ID_PCM_CONFIG; + break; + case SECONDARY_I2S_RX: + case SECONDARY_I2S_TX: + case AFE_PORT_ID_PRIMARY_MI2S_RX: + case AFE_PORT_ID_PRIMARY_MI2S_TX: + case AFE_PORT_ID_QUATERNARY_MI2S_RX: + case AFE_PORT_ID_QUATERNARY_MI2S_TX: + case MI2S_RX: + case MI2S_TX: + case AFE_PORT_ID_QUINARY_MI2S_RX: + case AFE_PORT_ID_QUINARY_MI2S_TX: + case AFE_PORT_ID_SENARY_MI2S_TX: + cfg_type = AFE_PARAM_ID_I2S_CONFIG; + break; + case HDMI_RX: + case DISPLAY_PORT_RX: + cfg_type = AFE_PARAM_ID_HDMI_CONFIG; + break; + case AFE_PORT_ID_PRIMARY_SPDIF_RX: + case AFE_PORT_ID_PRIMARY_SPDIF_TX: + case AFE_PORT_ID_SECONDARY_SPDIF_RX: + case AFE_PORT_ID_SECONDARY_SPDIF_TX: + cfg_type = AFE_PARAM_ID_SPDIF_CONFIG; + break; + case SLIMBUS_0_RX: + case SLIMBUS_0_TX: + case SLIMBUS_1_RX: + case SLIMBUS_1_TX: + case SLIMBUS_2_RX: + case SLIMBUS_2_TX: + case SLIMBUS_3_RX: + case SLIMBUS_3_TX: + case SLIMBUS_4_RX: + case SLIMBUS_4_TX: + case SLIMBUS_5_RX: + case SLIMBUS_6_RX: + case SLIMBUS_6_TX: + case SLIMBUS_7_RX: + case SLIMBUS_7_TX: + case SLIMBUS_8_RX: + case SLIMBUS_8_TX: + case SLIMBUS_9_RX: + case SLIMBUS_9_TX: + cfg_type = AFE_PARAM_ID_SLIMBUS_CONFIG; + break; + case AFE_PORT_ID_USB_RX: + case AFE_PORT_ID_USB_TX: + cfg_type = AFE_PARAM_ID_USB_AUDIO_CONFIG; + break; + case AFE_PORT_ID_WSA_CODEC_DMA_RX_0: + case AFE_PORT_ID_WSA_CODEC_DMA_TX_0: + case AFE_PORT_ID_WSA_CODEC_DMA_RX_1: + case AFE_PORT_ID_WSA_CODEC_DMA_TX_1: + case AFE_PORT_ID_WSA_CODEC_DMA_TX_2: + case AFE_PORT_ID_VA_CODEC_DMA_TX_0: + case AFE_PORT_ID_VA_CODEC_DMA_TX_1: + case AFE_PORT_ID_RX_CODEC_DMA_RX_0: + case AFE_PORT_ID_TX_CODEC_DMA_TX_0: + case AFE_PORT_ID_RX_CODEC_DMA_RX_1: + case AFE_PORT_ID_TX_CODEC_DMA_TX_1: + case AFE_PORT_ID_RX_CODEC_DMA_RX_2: + case AFE_PORT_ID_TX_CODEC_DMA_TX_2: + case AFE_PORT_ID_RX_CODEC_DMA_RX_3: + case AFE_PORT_ID_TX_CODEC_DMA_TX_3: + case AFE_PORT_ID_RX_CODEC_DMA_RX_4: + case AFE_PORT_ID_TX_CODEC_DMA_TX_4: + case AFE_PORT_ID_RX_CODEC_DMA_RX_5: + case AFE_PORT_ID_TX_CODEC_DMA_TX_5: + case AFE_PORT_ID_RX_CODEC_DMA_RX_6: + case AFE_PORT_ID_RX_CODEC_DMA_RX_7: + cfg_type = AFE_PARAM_ID_CODEC_DMA_CONFIG; + break; + default: + pr_err("%s: Invalid port id 0x%x\n", + __func__, port_id); + ret = -EINVAL; + goto fail_cmd; + } + + param_hdr.module_id = AFE_MODULE_AUDIO_DEV_INTERFACE; + param_hdr.instance_id = INSTANCE_ID_0; + param_hdr.param_id = cfg_type; + param_hdr.param_size = sizeof(union afe_port_config); + port_cfg = *afe_config; + + ret = q6afe_pack_and_set_param_in_band(port_id, + q6audio_get_port_index(port_id), + param_hdr, (u8 *) &port_cfg); + if (ret) { + pr_err("%s: AFE enable for port 0x%x opcode[0x%x]failed %d\n", + __func__, port_id, cfg_type, ret); + goto fail_cmd; + } + 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 = afe_apr_send_pkt(&start, &this_afe.wait[index]); + if (ret) { + pr_err("%s: AFE enable for port 0x%x failed %d\n", __func__, + port_id, ret); + goto fail_cmd; + } + +fail_cmd: + mutex_unlock(&this_afe.afe_cmd_lock); + return ret; +} +EXPORT_SYMBOL(afe_open); + +/** + * afe_loopback - + * command to set loopback between AFE ports + * + * @enable: enable or disable loopback + * @rx_port: AFE RX port ID + * @tx_port: AFE TX port ID + * + * Returns 0 on success or error on failure + */ +int afe_loopback(u16 enable, u16 rx_port, u16 tx_port) +{ + struct afe_loopback_cfg_v1 lb_param; + struct param_hdr_v3 param_hdr; + int ret = 0; + + memset(&lb_param, 0, sizeof(lb_param)); + memset(¶m_hdr, 0, sizeof(param_hdr)); + + if (rx_port == MI2S_RX) + rx_port = AFE_PORT_ID_PRIMARY_MI2S_RX; + if (tx_port == MI2S_TX) + tx_port = AFE_PORT_ID_PRIMARY_MI2S_TX; + + param_hdr.module_id = AFE_MODULE_LOOPBACK; + param_hdr.instance_id = INSTANCE_ID_0; + param_hdr.param_id = AFE_PARAM_ID_LOOPBACK_CONFIG; + param_hdr.param_size = sizeof(struct afe_loopback_cfg_v1); + + lb_param.dst_port_id = rx_port; + lb_param.routing_mode = LB_MODE_DEFAULT; + lb_param.enable = (enable ? 1 : 0); + lb_param.loopback_cfg_minor_version = AFE_API_VERSION_LOOPBACK_CONFIG; + + ret = q6afe_pack_and_set_param_in_band(tx_port, + q6audio_get_port_index(tx_port), + param_hdr, (u8 *) &lb_param); + if (ret) + pr_err("%s: AFE loopback failed %d\n", __func__, ret); + return ret; +} +EXPORT_SYMBOL(afe_loopback); + +/** + * afe_loopback_gain - + * command to set gain for AFE loopback + * + * @port_id: AFE port id + * @volume: gain value to set + * + * Returns 0 on success or error on failure + */ +int afe_loopback_gain(u16 port_id, u16 volume) +{ + struct afe_loopback_gain_per_path_param set_param; + struct param_hdr_v3 param_hdr; + int ret = 0; + + memset(&set_param, 0, sizeof(set_param)); + memset(¶m_hdr, 0, sizeof(param_hdr)); + + if (this_afe.apr == NULL) { + this_afe.apr = apr_register("ADSP", "AFE", afe_callback, + 0xFFFFFFFF, &this_afe); + pr_debug("%s: Register AFE\n", __func__); + if (this_afe.apr == NULL) { + pr_err("%s: Unable to register AFE\n", __func__); + ret = -ENODEV; + return ret; + } + rtac_set_afe_handle(this_afe.apr); + } + + ret = q6audio_validate_port(port_id); + if (ret < 0) { + pr_err("%s: Failed : Invalid Port id = 0x%x ret %d\n", + __func__, port_id, ret); + ret = -EINVAL; + goto fail_cmd; + } + + /* RX ports numbers are even .TX ports numbers are odd. */ + if (port_id % 2 == 0) { + pr_err("%s: Failed : afe loopback gain only for TX ports. port_id %d\n", + __func__, port_id); + ret = -EINVAL; + goto fail_cmd; + } + + pr_debug("%s: port 0x%x volume %d\n", __func__, port_id, volume); + + param_hdr.module_id = AFE_MODULE_LOOPBACK; + param_hdr.instance_id = INSTANCE_ID_0; + param_hdr.param_id = AFE_PARAM_ID_LOOPBACK_GAIN_PER_PATH; + param_hdr.param_size = sizeof(struct afe_loopback_gain_per_path_param); + set_param.rx_port_id = port_id; + set_param.gain = volume; + + ret = q6afe_pack_and_set_param_in_band(port_id, + q6audio_get_port_index(port_id), + param_hdr, (u8 *) &set_param); + if (ret) + pr_err("%s: AFE param set failed for port 0x%x ret %d\n", + __func__, port_id, ret); + +fail_cmd: + return ret; +} +EXPORT_SYMBOL(afe_loopback_gain); + +int afe_pseudo_port_start_nowait(u16 port_id) +{ + struct afe_pseudoport_start_command start; + int ret = 0; + + pr_debug("%s: port_id=0x%x\n", __func__, port_id); + if (this_afe.apr == NULL) { + pr_err("%s: AFE APR is not registered\n", __func__); + return -ENODEV; + } + + + 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 = 0; + start.hdr.opcode = AFE_PSEUDOPORT_CMD_START; + start.port_id = port_id; + start.timing = 1; + + ret = afe_apr_send_pkt(&start, NULL); + if (ret) { + pr_err("%s: AFE enable for port 0x%x failed %d\n", + __func__, port_id, ret); + return ret; + } + return 0; +} + +int afe_start_pseudo_port(u16 port_id) +{ + int ret = 0; + struct afe_pseudoport_start_command start; + int index = 0; + + pr_debug("%s: port_id = 0x%x\n", __func__, port_id); + + ret = afe_q6_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; + } + + 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 = 0; + start.hdr.opcode = AFE_PSEUDOPORT_CMD_START; + start.port_id = port_id; + start.timing = 1; + start.hdr.token = index; + + ret = afe_apr_send_pkt(&start, &this_afe.wait[index]); + if (ret) + pr_err("%s: AFE enable for port 0x%x failed %d\n", + __func__, port_id, ret); + return ret; +} + +int afe_pseudo_port_stop_nowait(u16 port_id) +{ + int ret = 0; + struct afe_pseudoport_stop_command stop; + int index = 0; + + pr_debug("%s: port_id = 0x%x\n", __func__, port_id); + + if (this_afe.apr == NULL) { + pr_err("%s: AFE is already closed\n", __func__); + return -EINVAL; + } + 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; + } + + 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_PSEUDOPORT_CMD_STOP; + stop.port_id = port_id; + stop.reserved = 0; + stop.hdr.token = index; + + ret = afe_apr_send_pkt(&stop, NULL); + if (ret) + pr_err("%s: AFE close failed %d\n", __func__, ret); + + return ret; +} + +int afe_port_group_set_param(u16 group_id, + union afe_port_group_config *afe_group_config) +{ + struct param_hdr_v3 param_hdr; + int cfg_type; + int ret; + + if (!afe_group_config) { + pr_err("%s: Error, no configuration data\n", __func__); + return -EINVAL; + } + + pr_debug("%s: group id: 0x%x\n", __func__, group_id); + + memset(¶m_hdr, 0, sizeof(param_hdr)); + + ret = afe_q6_interface_prepare(); + if (ret != 0) { + pr_err("%s: Q6 interface prepare failed %d\n", __func__, ret); + return ret; + } + + switch (group_id) { + case AFE_GROUP_DEVICE_ID_PRIMARY_TDM_RX: + case AFE_GROUP_DEVICE_ID_PRIMARY_TDM_TX: + case AFE_GROUP_DEVICE_ID_SECONDARY_TDM_RX: + case AFE_GROUP_DEVICE_ID_SECONDARY_TDM_TX: + case AFE_GROUP_DEVICE_ID_TERTIARY_TDM_RX: + case AFE_GROUP_DEVICE_ID_TERTIARY_TDM_TX: + case AFE_GROUP_DEVICE_ID_QUATERNARY_TDM_RX: + case AFE_GROUP_DEVICE_ID_QUATERNARY_TDM_TX: + case AFE_GROUP_DEVICE_ID_QUINARY_TDM_RX: + case AFE_GROUP_DEVICE_ID_QUINARY_TDM_TX: + cfg_type = AFE_PARAM_ID_GROUP_DEVICE_TDM_CONFIG; + break; + default: + pr_err("%s: Invalid group id 0x%x\n", __func__, group_id); + return -EINVAL; + } + + param_hdr.module_id = AFE_MODULE_GROUP_DEVICE; + param_hdr.instance_id = INSTANCE_ID_0; + param_hdr.param_id = cfg_type; + param_hdr.param_size = sizeof(union afe_port_group_config); + + ret = q6afe_svc_pack_and_set_param_in_band(IDX_GLOBAL_CFG, param_hdr, + (u8 *) afe_group_config); + if (ret) + pr_err("%s: AFE_PARAM_ID_GROUP_DEVICE_CFG failed %d\n", + __func__, ret); + + return ret; +} + +/** + * afe_port_group_enable - + * command to enable AFE port group + * + * @group_id: group ID for AFE port group + * @afe_group_config: config for AFE group + * @enable: flag to indicate enable or disable + * + * Returns 0 on success or error on failure + */ +int afe_port_group_enable(u16 group_id, + union afe_port_group_config *afe_group_config, + u16 enable) +{ + struct afe_group_device_enable group_enable; + struct param_hdr_v3 param_hdr; + int ret; + + pr_debug("%s: group id: 0x%x enable: %d\n", __func__, + group_id, enable); + + memset(&group_enable, 0, sizeof(group_enable)); + memset(¶m_hdr, 0, sizeof(param_hdr)); + + ret = afe_q6_interface_prepare(); + if (ret != 0) { + pr_err("%s: Q6 interface prepare failed %d\n", __func__, ret); + return ret; + } + + if (enable) { + ret = afe_port_group_set_param(group_id, afe_group_config); + if (ret < 0) { + pr_err("%s: afe send failed %d\n", __func__, ret); + return ret; + } + } + + param_hdr.module_id = AFE_MODULE_GROUP_DEVICE; + param_hdr.instance_id = INSTANCE_ID_0; + param_hdr.param_id = AFE_PARAM_ID_GROUP_DEVICE_ENABLE; + param_hdr.param_size = sizeof(struct afe_group_device_enable); + group_enable.group_id = group_id; + group_enable.enable = enable; + + ret = q6afe_svc_pack_and_set_param_in_band(IDX_GLOBAL_CFG, param_hdr, + (u8 *) &group_enable); + if (ret) + pr_err("%s: AFE_PARAM_ID_GROUP_DEVICE_ENABLE failed %d\n", + __func__, ret); + + return ret; +} +EXPORT_SYMBOL(afe_port_group_enable); + +int afe_stop_pseudo_port(u16 port_id) +{ + int ret = 0; + struct afe_pseudoport_stop_command stop; + int index = 0; + + pr_debug("%s: port_id = 0x%x\n", __func__, port_id); + + if (this_afe.apr == NULL) { + pr_err("%s: AFE is already closed\n", __func__); + return -EINVAL; + } + + 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\n", + __func__, port_id, ret); + return -EINVAL; + } + + 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_PSEUDOPORT_CMD_STOP; + stop.port_id = port_id; + stop.reserved = 0; + stop.hdr.token = index; + + ret = afe_apr_send_pkt(&stop, &this_afe.wait[index]); + if (ret) + pr_err("%s: AFE close failed %d\n", __func__, ret); + + return ret; +} + +/** + * afe_req_mmap_handle - + * Retrieve AFE memory map handle + * + * @ac: AFE audio client + * + * Returns memory map handle + */ +uint32_t afe_req_mmap_handle(struct afe_audio_client *ac) +{ + return ac->mem_map_handle; +} +EXPORT_SYMBOL(afe_req_mmap_handle); + +/** + * q6afe_audio_client_alloc - + * Assign new AFE audio client + * + * @priv: privata data to hold for audio client + * + * Returns ac pointer on success or NULL on failure + */ +struct afe_audio_client *q6afe_audio_client_alloc(void *priv) +{ + struct afe_audio_client *ac; + int lcnt = 0; + + ac = kzalloc(sizeof(struct afe_audio_client), GFP_KERNEL); + if (!ac) + return NULL; + + ac->priv = priv; + + init_waitqueue_head(&ac->cmd_wait); + INIT_LIST_HEAD(&ac->port[0].mem_map_handle); + INIT_LIST_HEAD(&ac->port[1].mem_map_handle); + pr_debug("%s: mem_map_handle list init'ed\n", __func__); + mutex_init(&ac->cmd_lock); + for (lcnt = 0; lcnt <= OUT; lcnt++) { + mutex_init(&ac->port[lcnt].lock); + spin_lock_init(&ac->port[lcnt].dsp_lock); + } + atomic_set(&ac->cmd_state, 0); + + return ac; +} +EXPORT_SYMBOL(q6afe_audio_client_alloc); + +/** + * q6afe_audio_client_buf_alloc_contiguous - + * Allocate contiguous shared buffers + * + * @dir: RX or TX direction of AFE port + * @ac: AFE audio client handle + * @bufsz: size of each shared buffer + * @bufcnt: number of buffers + * + * Returns 0 on success or error on failure + */ +int q6afe_audio_client_buf_alloc_contiguous(unsigned int dir, + struct afe_audio_client *ac, + unsigned int bufsz, + unsigned int bufcnt) +{ + int cnt = 0; + int rc = 0; + struct afe_audio_buffer *buf; + size_t len; + + if (!(ac) || ((dir != IN) && (dir != OUT))) { + pr_err("%s: ac %pK dir %d\n", __func__, ac, dir); + return -EINVAL; + } + + pr_debug("%s: bufsz[%d]bufcnt[%d]\n", + __func__, + bufsz, bufcnt); + + if (ac->port[dir].buf) { + pr_debug("%s: buffer already allocated\n", __func__); + return 0; + } + mutex_lock(&ac->cmd_lock); + buf = kzalloc(((sizeof(struct afe_audio_buffer))*bufcnt), + GFP_KERNEL); + + if (!buf) { + pr_err("%s: null buf\n", __func__); + mutex_unlock(&ac->cmd_lock); + goto fail; + } + + ac->port[dir].buf = buf; + + rc = msm_audio_ion_alloc(&buf[0].dma_buf, + bufsz * bufcnt, + &buf[0].phys, &len, + &buf[0].data); + if (rc) { + pr_err("%s: audio ION alloc failed, rc = %d\n", + __func__, rc); + mutex_unlock(&ac->cmd_lock); + goto fail; + } + + buf[0].used = dir ^ 1; + buf[0].size = bufsz; + buf[0].actual_size = bufsz; + cnt = 1; + while (cnt < bufcnt) { + if (bufsz > 0) { + buf[cnt].data = buf[0].data + (cnt * bufsz); + buf[cnt].phys = buf[0].phys + (cnt * bufsz); + if (!buf[cnt].data) { + pr_err("%s: Buf alloc failed\n", + __func__); + mutex_unlock(&ac->cmd_lock); + goto fail; + } + buf[cnt].used = dir ^ 1; + buf[cnt].size = bufsz; + buf[cnt].actual_size = bufsz; + pr_debug("%s: data[%pK]phys[%pK][%pK]\n", __func__, + buf[cnt].data, + &buf[cnt].phys, + &buf[cnt].phys); + } + cnt++; + } + ac->port[dir].max_buf_cnt = cnt; + mutex_unlock(&ac->cmd_lock); + return 0; +fail: + pr_err("%s: jump fail\n", __func__); + q6afe_audio_client_buf_free_contiguous(dir, ac); + return -EINVAL; +} +EXPORT_SYMBOL(q6afe_audio_client_buf_alloc_contiguous); + +/** + * afe_memory_map - + * command to map shared buffers to AFE + * + * @dma_addr_p: DMA physical address + * @dma_buf_sz: shared DMA buffer size + * @ac: AFE audio client handle + * + * Returns 0 on success or error on failure + */ +int afe_memory_map(phys_addr_t dma_addr_p, u32 dma_buf_sz, + struct afe_audio_client *ac) +{ + int ret = 0; + + mutex_lock(&this_afe.afe_cmd_lock); + ac->mem_map_handle = 0; + ret = afe_cmd_memory_map(dma_addr_p, dma_buf_sz); + if (ret < 0) { + pr_err("%s: afe_cmd_memory_map failed %d\n", + __func__, ret); + + mutex_unlock(&this_afe.afe_cmd_lock); + return ret; + } + ac->mem_map_handle = this_afe.mmap_handle; + mutex_unlock(&this_afe.afe_cmd_lock); + + return ret; +} +EXPORT_SYMBOL(afe_memory_map); + +int afe_cmd_memory_map(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__); + + if (this_afe.apr == NULL) { + this_afe.apr = apr_register("ADSP", "AFE", afe_callback, + 0xFFFFFFFF, &this_afe); + pr_debug("%s: Register AFE\n", __func__); + if (this_afe.apr == NULL) { + pr_err("%s: Unable to register AFE\n", __func__); + ret = -ENODEV; + return ret; + } + rtac_set_afe_handle(this_afe.apr); + } + if (dma_buf_sz % SZ_4K != 0) { + /* + * The memory allocated by msm_audio_ion_alloc is always 4kB + * aligned, ADSP expects the size to be 4kB aligned as well + * so re-adjusts the buffer size before passing to ADSP. + */ + dma_buf_sz = PAGE_ALIGN(dma_buf_sz); + } + + 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) + return -ENOMEM; + + 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 = 0; + 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; + /* Todo */ + index = mregion->hdr.token = IDX_RSVD_2; + + 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; + + pr_debug("%s: dma_addr_p 0x%pK , size %d\n", __func__, + &dma_addr_p, dma_buf_sz); + atomic_set(&this_afe.state, 1); + atomic_set(&this_afe.status, 0); + this_afe.mmap_handle = 0; + ret = apr_send_pkt(this_afe.apr, (uint32_t *) mmap_region_cmd); + if (ret < 0) { + pr_err("%s: AFE memory map cmd failed %d\n", + __func__, ret); + ret = -EINVAL; + goto fail_cmd; + } + + ret = wait_event_timeout(this_afe.wait[index], + (atomic_read(&this_afe.state) == 0), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + ret = -EINVAL; + goto fail_cmd; + } + if (atomic_read(&this_afe.status) > 0) { + pr_err("%s: config cmd failed [%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&this_afe.status))); + ret = adsp_err_get_lnx_err_code( + atomic_read(&this_afe.status)); + goto fail_cmd; + } + + kfree(mmap_region_cmd); + return 0; +fail_cmd: + kfree(mmap_region_cmd); + pr_err("%s: fail_cmd\n", __func__); + return ret; +} + +int afe_cmd_memory_map_nowait(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__); + + if (this_afe.apr == NULL) { + this_afe.apr = apr_register("ADSP", "AFE", afe_callback, + 0xFFFFFFFF, &this_afe); + pr_debug("%s: Register AFE\n", __func__); + if (this_afe.apr == NULL) { + pr_err("%s: Unable to register AFE\n", __func__); + ret = -ENODEV; + return ret; + } + rtac_set_afe_handle(this_afe.apr); + } + 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) + return -ENOMEM; + + 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 = sizeof(mregion); + mregion->hdr.src_port = 0; + mregion->hdr.dest_port = 0; + mregion->hdr.token = 0; + 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 = afe_apr_send_pkt(mmap_region_cmd, NULL); + if (ret) + pr_err("%s: AFE memory map cmd failed %d\n", + __func__, ret); + kfree(mmap_region_cmd); + return ret; +} + +/** + * q6afe_audio_client_buf_free_contiguous - + * frees the shared contiguous memory + * + * @dir: RX or TX direction of port + * @ac: AFE audio client handle + * + */ +int q6afe_audio_client_buf_free_contiguous(unsigned int dir, + struct afe_audio_client *ac) +{ + struct afe_audio_port_data *port; + int cnt = 0; + + mutex_lock(&ac->cmd_lock); + port = &ac->port[dir]; + if (!port->buf) { + pr_err("%s: buf is null\n", __func__); + mutex_unlock(&ac->cmd_lock); + return 0; + } + cnt = port->max_buf_cnt - 1; + + if (port->buf[0].data) { + pr_debug("%s: data[%pK], phys[%pK], dma_buf[%pK]\n", + __func__, + port->buf[0].data, + &port->buf[0].phys, + port->buf[0].dma_buf); + msm_audio_ion_free(port->buf[0].dma_buf); + port->buf[0].dma_buf = NULL; + } + + while (cnt >= 0) { + port->buf[cnt].data = NULL; + port->buf[cnt].phys = 0; + cnt--; + } + port->max_buf_cnt = 0; + kfree(port->buf); + port->buf = NULL; + mutex_unlock(&ac->cmd_lock); + return 0; +} +EXPORT_SYMBOL(q6afe_audio_client_buf_free_contiguous); + +/** + * q6afe_audio_client_free - + * frees the audio client from AFE + * + * @ac: AFE audio client handle + * + */ +void q6afe_audio_client_free(struct afe_audio_client *ac) +{ + int loopcnt; + struct afe_audio_port_data *port; + + if (!ac) { + pr_err("%s: audio client is NULL\n", __func__); + return; + } + for (loopcnt = 0; loopcnt <= OUT; loopcnt++) { + port = &ac->port[loopcnt]; + if (!port->buf) + continue; + pr_debug("%s: loopcnt = %d\n", __func__, loopcnt); + q6afe_audio_client_buf_free_contiguous(loopcnt, ac); + } + kfree(ac); +} +EXPORT_SYMBOL(q6afe_audio_client_free); + +/** + * afe_cmd_memory_unmap - + * command to unmap memory for AFE shared buffer + * + * @mem_map_handle: memory map handle to be unmapped + * + * Returns 0 on success or error on failure + */ +int afe_cmd_memory_unmap(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); + + if (this_afe.apr == NULL) { + this_afe.apr = apr_register("ADSP", "AFE", afe_callback, + 0xFFFFFFFF, &this_afe); + pr_debug("%s: Register AFE\n", __func__); + if (this_afe.apr == NULL) { + pr_err("%s: Unable to register AFE\n", __func__); + ret = -ENODEV; + return ret; + } + rtac_set_afe_handle(this_afe.apr); + } + + 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 = 0; + mregion.hdr.opcode = AFE_SERVICE_CMD_SHARED_MEM_UNMAP_REGIONS; + mregion.mem_map_handle = mem_map_handle; + + /* Todo */ + index = mregion.hdr.token = IDX_RSVD_2; + + atomic_set(&this_afe.status, 0); + ret = afe_apr_send_pkt(&mregion, &this_afe.wait[index]); + if (ret) + pr_err("%s: AFE memory unmap cmd failed %d\n", + __func__, ret); + + return ret; +} +EXPORT_SYMBOL(afe_cmd_memory_unmap); + +int afe_cmd_memory_unmap_nowait(u32 mem_map_handle) +{ + int ret = 0; + struct afe_service_cmd_shared_mem_unmap_regions mregion; + + pr_debug("%s: handle 0x%x\n", __func__, mem_map_handle); + + if (this_afe.apr == NULL) { + this_afe.apr = apr_register("ADSP", "AFE", afe_callback, + 0xFFFFFFFF, &this_afe); + pr_debug("%s: Register AFE\n", __func__); + if (this_afe.apr == NULL) { + pr_err("%s: Unable to register AFE\n", __func__); + ret = -ENODEV; + return ret; + } + rtac_set_afe_handle(this_afe.apr); + } + + 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 = 0; + mregion.hdr.opcode = AFE_SERVICE_CMD_SHARED_MEM_UNMAP_REGIONS; + mregion.mem_map_handle = mem_map_handle; + + ret = afe_apr_send_pkt(&mregion, NULL); + if (ret) + pr_err("%s: AFE memory unmap cmd failed %d\n", + __func__, ret); + return ret; +} + +/** + * afe_register_get_events - + * register for events from proxy port + * + * @port_id: Port ID to register events + * @cb: callback function to invoke for events from proxy port + * @private_data: private data to sent back in callback fn + * + * Returns 0 on success or error on failure + */ +int afe_register_get_events(u16 port_id, + void (*cb)(uint32_t opcode, + uint32_t token, uint32_t *payload, void *priv), + void *private_data) +{ + int ret = 0; + struct afe_service_cmd_register_rt_port_driver rtproxy; + + pr_debug("%s: port_id: 0x%x\n", __func__, port_id); + + if (this_afe.apr == NULL) { + this_afe.apr = apr_register("ADSP", "AFE", afe_callback, + 0xFFFFFFFF, &this_afe); + pr_debug("%s: Register AFE\n", __func__); + if (this_afe.apr == NULL) { + pr_err("%s: Unable to register AFE\n", __func__); + ret = -ENODEV; + return ret; + } + rtac_set_afe_handle(this_afe.apr); + } + if ((port_id == RT_PROXY_DAI_002_RX) || + (port_id == RT_PROXY_DAI_001_TX)) { + port_id = VIRTUAL_ID_TO_PORTID(port_id); + } else { + pr_err("%s: wrong port id 0x%x\n", __func__, port_id); + return -EINVAL; + } + + if (port_id == RT_PROXY_PORT_001_TX) { + this_afe.tx_cb = cb; + this_afe.tx_private_data = private_data; + } else if (port_id == RT_PROXY_PORT_001_RX) { + this_afe.rx_cb = cb; + this_afe.rx_private_data = private_data; + } + + rtproxy.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + rtproxy.hdr.pkt_size = sizeof(rtproxy); + rtproxy.hdr.src_port = 1; + rtproxy.hdr.dest_port = 1; + rtproxy.hdr.opcode = AFE_SERVICE_CMD_REGISTER_RT_PORT_DRIVER; + rtproxy.port_id = port_id; + rtproxy.reserved = 0; + + ret = afe_apr_send_pkt(&rtproxy, NULL); + if (ret) + pr_err("%s: AFE reg. rtproxy_event failed %d\n", + __func__, ret); + return ret; +} +EXPORT_SYMBOL(afe_register_get_events); + +/** + * afe_unregister_get_events - + * unregister for events from proxy port + * + * @port_id: Port ID to unregister events + * + * Returns 0 on success or error on failure + */ +int afe_unregister_get_events(u16 port_id) +{ + int ret = 0; + struct afe_service_cmd_unregister_rt_port_driver rtproxy; + int index = 0; + + pr_debug("%s:\n", __func__); + + if (this_afe.apr == NULL) { + this_afe.apr = apr_register("ADSP", "AFE", afe_callback, + 0xFFFFFFFF, &this_afe); + pr_debug("%s: Register AFE\n", __func__); + if (this_afe.apr == NULL) { + pr_err("%s: Unable to register AFE\n", __func__); + ret = -ENODEV; + return ret; + } + rtac_set_afe_handle(this_afe.apr); + } + + if ((port_id == RT_PROXY_DAI_002_RX) || + (port_id == RT_PROXY_DAI_001_TX)) { + port_id = VIRTUAL_ID_TO_PORTID(port_id); + } else { + pr_err("%s: wrong port id 0x%x\n", __func__, port_id); + return -EINVAL; + } + + 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; + } + + rtproxy.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + rtproxy.hdr.pkt_size = sizeof(rtproxy); + rtproxy.hdr.src_port = 0; + rtproxy.hdr.dest_port = 0; + rtproxy.hdr.token = 0; + rtproxy.hdr.opcode = AFE_SERVICE_CMD_UNREGISTER_RT_PORT_DRIVER; + rtproxy.port_id = port_id; + rtproxy.reserved = 0; + + rtproxy.hdr.token = index; + + if (port_id == RT_PROXY_PORT_001_TX) { + this_afe.tx_cb = NULL; + this_afe.tx_private_data = NULL; + } else if (port_id == RT_PROXY_PORT_001_RX) { + this_afe.rx_cb = NULL; + this_afe.rx_private_data = NULL; + } + + ret = afe_apr_send_pkt(&rtproxy, &this_afe.wait[index]); + if (ret) + pr_err("%s: AFE enable Unreg. rtproxy_event failed %d\n", + __func__, ret); + return ret; +} +EXPORT_SYMBOL(afe_unregister_get_events); + +/** + * afe_rt_proxy_port_write - + * command for AFE RT proxy port write + * + * @buf_addr_p: Physical buffer address with + * playback data to proxy port + * @mem_map_handle: memory map handle of write buffer + * @bytes: number of bytes to write + * + * Returns 0 on success or error on failure + */ +int afe_rt_proxy_port_write(phys_addr_t buf_addr_p, + u32 mem_map_handle, int bytes) +{ + int ret = 0; + struct afe_port_data_cmd_rt_proxy_port_write_v2 afecmd_wr; + + if (this_afe.apr == NULL) { + pr_err("%s: register to AFE is not done\n", __func__); + ret = -ENODEV; + return ret; + } + pr_debug("%s: buf_addr_p = 0x%pK bytes = %d\n", __func__, + &buf_addr_p, bytes); + + afecmd_wr.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + afecmd_wr.hdr.pkt_size = sizeof(afecmd_wr); + afecmd_wr.hdr.src_port = 0; + afecmd_wr.hdr.dest_port = 0; + afecmd_wr.hdr.token = 0; + afecmd_wr.hdr.opcode = AFE_PORT_DATA_CMD_RT_PROXY_PORT_WRITE_V2; + afecmd_wr.port_id = RT_PROXY_PORT_001_TX; + afecmd_wr.buffer_address_lsw = lower_32_bits(buf_addr_p); + afecmd_wr.buffer_address_msw = + msm_audio_populate_upper_32_bits(buf_addr_p); + afecmd_wr.mem_map_handle = mem_map_handle; + afecmd_wr.available_bytes = bytes; + afecmd_wr.reserved = 0; + + ret = afe_apr_send_pkt(&afecmd_wr, NULL); + if (ret) + pr_err("%s: AFE rtproxy write to port 0x%x failed %d\n", + __func__, afecmd_wr.port_id, ret); + return ret; + +} +EXPORT_SYMBOL(afe_rt_proxy_port_write); + +/** + * afe_rt_proxy_port_read - + * command for AFE RT proxy port read + * + * @buf_addr_p: Physical buffer address to fill read data + * @mem_map_handle: memory map handle for buffer read + * @bytes: number of bytes to read + * + * Returns 0 on success or error on failure + */ +int afe_rt_proxy_port_read(phys_addr_t buf_addr_p, + u32 mem_map_handle, int bytes) +{ + int ret = 0; + struct afe_port_data_cmd_rt_proxy_port_read_v2 afecmd_rd; + + if (this_afe.apr == NULL) { + pr_err("%s: register to AFE is not done\n", __func__); + ret = -ENODEV; + return ret; + } + pr_debug("%s: buf_addr_p = 0x%pK bytes = %d\n", __func__, + &buf_addr_p, bytes); + + afecmd_rd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + afecmd_rd.hdr.pkt_size = sizeof(afecmd_rd); + afecmd_rd.hdr.src_port = 0; + afecmd_rd.hdr.dest_port = 0; + afecmd_rd.hdr.token = 0; + afecmd_rd.hdr.opcode = AFE_PORT_DATA_CMD_RT_PROXY_PORT_READ_V2; + afecmd_rd.port_id = RT_PROXY_PORT_001_RX; + afecmd_rd.buffer_address_lsw = lower_32_bits(buf_addr_p); + afecmd_rd.buffer_address_msw = + msm_audio_populate_upper_32_bits(buf_addr_p); + afecmd_rd.available_bytes = bytes; + afecmd_rd.mem_map_handle = mem_map_handle; + + ret = afe_apr_send_pkt(&afecmd_rd, NULL); + if (ret) + pr_err("%s: AFE rtproxy read cmd to port 0x%x failed %d\n", + __func__, afecmd_rd.port_id, ret); + return ret; +} +EXPORT_SYMBOL(afe_rt_proxy_port_read); + +#ifdef CONFIG_DEBUG_FS +static struct dentry *debugfs_afelb; +static struct dentry *debugfs_afelb_gain; + +static int afe_debug_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + pr_info("%s: debug intf %s\n", __func__, (char *) file->private_data); + return 0; +} + +static int afe_get_parameters(char *buf, long int *param1, int num_of_par) +{ + char *token; + int base, cnt; + + token = strsep(&buf, " "); + + for (cnt = 0; cnt < num_of_par; cnt++) { + if (token != NULL) { + if ((token[1] == 'x') || (token[1] == 'X')) + base = 16; + else + base = 10; + + if (kstrtoul(token, base, ¶m1[cnt]) != 0) { + pr_err("%s: kstrtoul failed\n", + __func__); + return -EINVAL; + } + + token = strsep(&buf, " "); + } else { + pr_err("%s: token NULL\n", __func__); + return -EINVAL; + } + } + return 0; +} +#define AFE_LOOPBACK_ON (1) +#define AFE_LOOPBACK_OFF (0) +static ssize_t afe_debug_write(struct file *filp, + const char __user *ubuf, size_t cnt, loff_t *ppos) +{ + char *lb_str = filp->private_data; + char lbuf[32]; + int rc; + unsigned long param[5]; + + if (cnt > sizeof(lbuf) - 1) { + pr_err("%s: cnt %zd size %zd\n", __func__, cnt, sizeof(lbuf)-1); + return -EINVAL; + } + + rc = copy_from_user(lbuf, ubuf, cnt); + if (rc) { + pr_err("%s: copy from user failed %d\n", __func__, rc); + return -EFAULT; + } + + lbuf[cnt] = '\0'; + + if (!strcmp(lb_str, "afe_loopback")) { + rc = afe_get_parameters(lbuf, param, 3); + if (!rc) { + pr_info("%s: %lu %lu %lu\n", lb_str, param[0], param[1], + param[2]); + + if ((param[0] != AFE_LOOPBACK_ON) && (param[0] != + AFE_LOOPBACK_OFF)) { + pr_err("%s: Error, parameter 0 incorrect\n", + __func__); + rc = -EINVAL; + goto afe_error; + } + if ((q6audio_validate_port(param[1]) < 0) || + (q6audio_validate_port(param[2])) < 0) { + pr_err("%s: Error, invalid afe port\n", + __func__); + } + if (this_afe.apr == NULL) { + pr_err("%s: Error, AFE not opened\n", __func__); + rc = -EINVAL; + } else { + rc = afe_loopback(param[0], param[1], param[2]); + } + } else { + pr_err("%s: Error, invalid parameters\n", __func__); + rc = -EINVAL; + } + + } else if (!strcmp(lb_str, "afe_loopback_gain")) { + rc = afe_get_parameters(lbuf, param, 2); + if (!rc) { + pr_info("%s: %s %lu %lu\n", + __func__, lb_str, param[0], param[1]); + + rc = q6audio_validate_port(param[0]); + if (rc < 0) { + pr_err("%s: Error, invalid afe port %d %lu\n", + __func__, rc, param[0]); + rc = -EINVAL; + goto afe_error; + } + + if (param[1] > 100) { + pr_err("%s: Error, volume should be 0 to 100 percentage param = %lu\n", + __func__, param[1]); + rc = -EINVAL; + goto afe_error; + } + + param[1] = (Q6AFE_MAX_VOLUME * param[1]) / 100; + + if (this_afe.apr == NULL) { + pr_err("%s: Error, AFE not opened\n", __func__); + rc = -EINVAL; + } else { + rc = afe_loopback_gain(param[0], param[1]); + } + } else { + pr_err("%s: Error, invalid parameters\n", __func__); + rc = -EINVAL; + } + } + +afe_error: + if (rc == 0) + rc = cnt; + else + pr_err("%s: rc = %d\n", __func__, rc); + + return rc; +} + +static const struct file_operations afe_debug_fops = { + .open = afe_debug_open, + .write = afe_debug_write +}; + +static void config_debug_fs_init(void) +{ + debugfs_afelb = debugfs_create_file("afe_loopback", + 0664, NULL, (void *) "afe_loopback", + &afe_debug_fops); + + debugfs_afelb_gain = debugfs_create_file("afe_loopback_gain", + 0664, NULL, (void *) "afe_loopback_gain", + &afe_debug_fops); +} +static void config_debug_fs_exit(void) +{ + debugfs_remove(debugfs_afelb); + debugfs_remove(debugfs_afelb_gain); +} +#else +static void config_debug_fs_init(void) +{ +} +static void config_debug_fs_exit(void) +{ +} +#endif + +/** + * afe_set_dtmf_gen_rx_portid - + * Set port_id for DTMF tone generation + * + * @port_id: AFE port id + * @set: set or reset port id value for dtmf gen + * + */ +void afe_set_dtmf_gen_rx_portid(u16 port_id, int set) +{ + if (set) + this_afe.dtmf_gen_rx_portid = port_id; + else if (this_afe.dtmf_gen_rx_portid == port_id) + this_afe.dtmf_gen_rx_portid = -1; +} +EXPORT_SYMBOL(afe_set_dtmf_gen_rx_portid); + +/** + * afe_dtmf_generate_rx - command to generate AFE DTMF RX + * + * @duration_in_ms: Duration in ms for dtmf tone + * @high_freq: Higher frequency for dtmf + * @low_freq: lower frequency for dtmf + * @gain: Gain value for DTMF tone + * + * Returns 0 on success, appropriate error code otherwise + */ +int afe_dtmf_generate_rx(int64_t duration_in_ms, + uint16_t high_freq, + uint16_t low_freq, uint16_t gain) +{ + int ret = 0; + int index = 0; + struct afe_dtmf_generation_command cmd_dtmf; + + pr_debug("%s: DTMF AFE Gen\n", __func__); + + if (afe_validate_port(this_afe.dtmf_gen_rx_portid) < 0) { + pr_err("%s: Failed : Invalid Port id = 0x%x\n", + __func__, this_afe.dtmf_gen_rx_portid); + ret = -EINVAL; + goto fail_cmd; + } + + if (this_afe.apr == NULL) { + this_afe.apr = apr_register("ADSP", "AFE", afe_callback, + 0xFFFFFFFF, &this_afe); + pr_debug("%s: Register AFE\n", __func__); + if (this_afe.apr == NULL) { + pr_err("%s: Unable to register AFE\n", __func__); + ret = -ENODEV; + return ret; + } + rtac_set_afe_handle(this_afe.apr); + } + + pr_debug("%s: dur=%lld: hfreq=%d lfreq=%d gain=%d portid=0x%x\n", + __func__, + duration_in_ms, high_freq, low_freq, gain, + this_afe.dtmf_gen_rx_portid); + + cmd_dtmf.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + cmd_dtmf.hdr.pkt_size = sizeof(cmd_dtmf); + cmd_dtmf.hdr.src_port = 0; + cmd_dtmf.hdr.dest_port = 0; + cmd_dtmf.hdr.token = 0; + cmd_dtmf.hdr.opcode = AFE_PORTS_CMD_DTMF_CTL; + cmd_dtmf.duration_in_ms = duration_in_ms; + cmd_dtmf.high_freq = high_freq; + cmd_dtmf.low_freq = low_freq; + cmd_dtmf.gain = gain; + cmd_dtmf.num_ports = 1; + cmd_dtmf.port_ids = q6audio_get_port_id(this_afe.dtmf_gen_rx_portid); + + atomic_set(&this_afe.state, 1); + atomic_set(&this_afe.status, 0); + ret = apr_send_pkt(this_afe.apr, (uint32_t *) &cmd_dtmf); + if (ret < 0) { + pr_err("%s: AFE DTMF failed for num_ports:%d ids:0x%x\n", + __func__, cmd_dtmf.num_ports, cmd_dtmf.port_ids); + ret = -EINVAL; + goto fail_cmd; + } + index = q6audio_get_port_index(this_afe.dtmf_gen_rx_portid); + if (index < 0 || index >= AFE_MAX_PORTS) { + pr_err("%s: AFE port index[%d] invalid!\n", + __func__, index); + ret = -EINVAL; + goto fail_cmd; + } + ret = wait_event_timeout(this_afe.wait[index], + (atomic_read(&this_afe.state) == 0), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + ret = -EINVAL; + goto fail_cmd; + } + if (atomic_read(&this_afe.status) > 0) { + pr_err("%s: config cmd failed [%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&this_afe.status))); + ret = adsp_err_get_lnx_err_code( + atomic_read(&this_afe.status)); + goto fail_cmd; + } + return 0; + +fail_cmd: + pr_err("%s: failed %d\n", __func__, ret); + return ret; +} +EXPORT_SYMBOL(afe_dtmf_generate_rx); + +static int afe_sidetone_iir(u16 tx_port_id) +{ + int ret; + uint16_t size = 0; + int cal_index = AFE_SIDETONE_IIR_CAL; + int iir_pregain = 0; + int iir_num_biquad_stages = 0; + int iir_enable; + struct cal_block_data *cal_block; + int mid; + struct afe_mod_enable_param enable; + struct afe_sidetone_iir_filter_config_params filter_data; + struct param_hdr_v3 param_hdr; + u8 *packed_param_data = NULL; + u32 packed_param_size = 0; + u32 single_param_size = 0; + struct audio_cal_info_sidetone_iir *st_iir_cal_info = NULL; + + memset(&enable, 0, sizeof(enable)); + memset(&filter_data, 0, sizeof(filter_data)); + memset(¶m_hdr, 0, sizeof(param_hdr)); + + if (this_afe.cal_data[cal_index] == NULL) { + pr_err("%s: cal data is NULL\n", __func__); + ret = -EINVAL; + goto done; + } + mutex_lock(&this_afe.cal_data[cal_index]->lock); + cal_block = cal_utils_get_only_cal_block(this_afe.cal_data[cal_index]); + if (cal_block == NULL || cal_utils_is_cal_stale(cal_block)) { + pr_err("%s: cal_block not found\n ", __func__); + mutex_unlock(&this_afe.cal_data[cal_index]->lock); + ret = -EINVAL; + goto done; + } + + /* Cache data from cal block while inside lock to reduce locked time */ + st_iir_cal_info = + (struct audio_cal_info_sidetone_iir *) cal_block->cal_info; + iir_pregain = st_iir_cal_info->pregain; + iir_enable = st_iir_cal_info->iir_enable; + iir_num_biquad_stages = st_iir_cal_info->num_biquad_stages; + mid = st_iir_cal_info->mid; + + /* + * calculate the actual size of payload based on no of stages + * enabled in calibration + */ + size = (MAX_SIDETONE_IIR_DATA_SIZE / MAX_NO_IIR_FILTER_STAGE) * + iir_num_biquad_stages; + /* + * For an odd number of stages, 2 bytes of padding are + * required at the end of the payload. + */ + if (iir_num_biquad_stages % 2) { + pr_debug("%s: adding 2 to size:%d\n", __func__, size); + size = size + 2; + } + memcpy(&filter_data.iir_config, &st_iir_cal_info->iir_config, size); + mutex_unlock(&this_afe.cal_data[cal_index]->lock); + + packed_param_size = + sizeof(param_hdr) * 2 + sizeof(enable) + sizeof(filter_data); + packed_param_data = kzalloc(packed_param_size, GFP_KERNEL); + if (!packed_param_data) + return -ENOMEM; + packed_param_size = 0; + + /* + * Set IIR enable params + */ + param_hdr.module_id = mid; + param_hdr.param_id = INSTANCE_ID_0; + param_hdr.param_id = AFE_PARAM_ID_ENABLE; + param_hdr.param_size = sizeof(enable); + enable.enable = iir_enable; + ret = q6common_pack_pp_params(packed_param_data, ¶m_hdr, + (u8 *) &enable, &single_param_size); + if (ret) { + pr_err("%s: Failed to pack param data, error %d\n", __func__, + ret); + goto done; + } + packed_param_size += single_param_size; + + /* + * Set IIR filter config params + */ + param_hdr.module_id = mid; + param_hdr.instance_id = INSTANCE_ID_0; + param_hdr.param_id = AFE_PARAM_ID_SIDETONE_IIR_FILTER_CONFIG; + param_hdr.param_size = sizeof(filter_data.num_biquad_stages) + + sizeof(filter_data.pregain) + size; + filter_data.num_biquad_stages = iir_num_biquad_stages; + filter_data.pregain = iir_pregain; + ret = q6common_pack_pp_params(packed_param_data + packed_param_size, + ¶m_hdr, (u8 *) &filter_data, + &single_param_size); + if (ret) { + pr_err("%s: Failed to pack param data, error %d\n", __func__, + ret); + goto done; + } + packed_param_size += single_param_size; + + pr_debug("%s: tx(0x%x)mid(0x%x)iir_en(%d)stg(%d)gain(0x%x)size(%d)\n", + __func__, tx_port_id, mid, enable.enable, + filter_data.num_biquad_stages, filter_data.pregain, + param_hdr.param_size); + + ret = q6afe_set_params(tx_port_id, q6audio_get_port_index(tx_port_id), + NULL, packed_param_data, packed_param_size); + if (ret) + pr_err("%s: AFE sidetone failed for tx_port(0x%x)\n", + __func__, tx_port_id); + +done: + kfree(packed_param_data); + return ret; +} + +static int afe_sidetone(u16 tx_port_id, u16 rx_port_id, bool enable) +{ + int ret; + int cal_index = AFE_SIDETONE_CAL; + int sidetone_gain; + int sidetone_enable; + struct cal_block_data *cal_block; + int mid = 0; + struct afe_loopback_sidetone_gain gain_data; + struct loopback_cfg_data cfg_data; + struct param_hdr_v3 param_hdr; + u8 *packed_param_data = NULL; + u32 packed_param_size = 0; + u32 single_param_size = 0; + struct audio_cal_info_sidetone *st_cal_info = NULL; + + if (this_afe.cal_data[cal_index] == NULL) { + pr_err("%s: cal data is NULL\n", __func__); + ret = -EINVAL; + goto done; + } + + memset(&gain_data, 0, sizeof(gain_data)); + memset(&cfg_data, 0, sizeof(cfg_data)); + memset(¶m_hdr, 0, sizeof(param_hdr)); + + packed_param_size = + sizeof(param_hdr) * 2 + sizeof(gain_data) + sizeof(cfg_data); + packed_param_data = kzalloc(packed_param_size, GFP_KERNEL); + if (!packed_param_data) + return -ENOMEM; + packed_param_size = 0; + + mutex_lock(&this_afe.cal_data[cal_index]->lock); + cal_block = cal_utils_get_only_cal_block(this_afe.cal_data[cal_index]); + if (cal_block == NULL || cal_utils_is_cal_stale(cal_block)) { + pr_err("%s: cal_block not found\n", __func__); + mutex_unlock(&this_afe.cal_data[cal_index]->lock); + ret = -EINVAL; + goto done; + } + + /* Cache data from cal block while inside lock to reduce locked time */ + st_cal_info = (struct audio_cal_info_sidetone *) cal_block->cal_info; + sidetone_gain = st_cal_info->gain; + sidetone_enable = st_cal_info->enable; + mid = st_cal_info->mid; + mutex_unlock(&this_afe.cal_data[cal_index]->lock); + + /* Set gain data. */ + param_hdr.module_id = AFE_MODULE_LOOPBACK; + param_hdr.instance_id = INSTANCE_ID_0; + param_hdr.param_id = AFE_PARAM_ID_LOOPBACK_GAIN_PER_PATH; + param_hdr.param_size = sizeof(struct afe_loopback_sidetone_gain); + gain_data.rx_port_id = rx_port_id; + gain_data.gain = sidetone_gain; + ret = q6common_pack_pp_params(packed_param_data, ¶m_hdr, + (u8 *) &gain_data, &single_param_size); + if (ret) { + pr_err("%s: Failed to pack param data, error %d\n", __func__, + ret); + goto done; + } + packed_param_size += single_param_size; + + /* Set configuration data. */ + param_hdr.module_id = AFE_MODULE_LOOPBACK; + param_hdr.instance_id = INSTANCE_ID_0; + param_hdr.param_id = AFE_PARAM_ID_LOOPBACK_CONFIG; + param_hdr.param_size = sizeof(struct loopback_cfg_data); + cfg_data.loopback_cfg_minor_version = AFE_API_VERSION_LOOPBACK_CONFIG; + cfg_data.dst_port_id = rx_port_id; + cfg_data.routing_mode = LB_MODE_SIDETONE; + cfg_data.enable = enable; + ret = q6common_pack_pp_params(packed_param_data + packed_param_size, + ¶m_hdr, (u8 *) &cfg_data, + &single_param_size); + if (ret) { + pr_err("%s: Failed to pack param data, error %d\n", __func__, + ret); + goto done; + } + packed_param_size += single_param_size; + + pr_debug("%s rx(0x%x) tx(0x%x) enable(%d) mid(0x%x) gain(%d) sidetone_enable(%d)\n", + __func__, rx_port_id, tx_port_id, + enable, mid, sidetone_gain, sidetone_enable); + + ret = q6afe_set_params(tx_port_id, q6audio_get_port_index(tx_port_id), + NULL, packed_param_data, packed_param_size); + if (ret) + pr_err("%s: AFE sidetone send failed for tx_port:%d rx_port:%d ret:%d\n", + __func__, tx_port_id, rx_port_id, ret); + +done: + kfree(packed_param_data); + return ret; +} + +int afe_sidetone_enable(u16 tx_port_id, u16 rx_port_id, bool enable) +{ + int ret; + int index; + + index = q6audio_get_port_index(rx_port_id); + if (index < 0 || index >= AFE_MAX_PORTS) { + pr_err("%s: AFE port index[%d] invalid!\n", + __func__, index); + ret = -EINVAL; + goto done; + } + if (q6audio_validate_port(rx_port_id) < 0) { + pr_err("%s: Invalid port 0x%x\n", + __func__, rx_port_id); + ret = -EINVAL; + goto done; + } + index = q6audio_get_port_index(tx_port_id); + if (index < 0 || index >= AFE_MAX_PORTS) { + pr_err("%s: AFE port index[%d] invalid!\n", + __func__, index); + ret = -EINVAL; + goto done; + } + if (q6audio_validate_port(tx_port_id) < 0) { + pr_err("%s: Invalid port 0x%x\n", + __func__, tx_port_id); + ret = -EINVAL; + goto done; + } + if (enable) { + ret = afe_sidetone_iir(tx_port_id); + if (ret) + goto done; + } + + ret = afe_sidetone(tx_port_id, rx_port_id, enable); + +done: + return ret; +} + +/** + * afe_set_display_stream - command to update AFE dp port params + * + * @rx_port_id: AFE port id + * @stream_idx: dp controller stream index + * @ctl_idx: dp controller index + * + * Returns 0 on success, appropriate error code otherwise + */ +int afe_set_display_stream(u16 rx_port_id, u32 stream_idx, u32 ctl_idx) +{ + int ret; + struct param_hdr_v3 param_hdr; + u32 packed_param_size = 0; + u8 *packed_param_data = NULL; + struct afe_display_stream_idx stream_data; + struct afe_display_ctl_idx ctl_data; + u32 single_param_size = 0; + + memset(¶m_hdr, 0, sizeof(param_hdr)); + memset(&stream_data, 0, sizeof(stream_data)); + memset(&ctl_data, 0, sizeof(ctl_data)); + + packed_param_size = + sizeof(param_hdr) * 2 + sizeof(stream_data) + sizeof(ctl_data); + packed_param_data = kzalloc(packed_param_size, GFP_KERNEL); + if (!packed_param_data) + return -ENOMEM; + packed_param_size = 0; + + /* Set stream index */ + param_hdr.module_id = AFE_MODULE_AUDIO_DEV_INTERFACE; + param_hdr.instance_id = INSTANCE_ID_0; + param_hdr.param_id = AFE_PARAM_ID_HDMI_DP_MST_VID_IDX_CFG; + param_hdr.param_size = sizeof(struct afe_display_stream_idx); + stream_data.minor_version = 1; + stream_data.stream_idx = stream_idx; + ret = q6common_pack_pp_params(packed_param_data, ¶m_hdr, + (u8 *) &stream_data, &single_param_size); + if (ret) { + pr_err("%s: Failed to pack param data, error %d\n", __func__, + ret); + goto done; + } + packed_param_size += single_param_size; + + /* Set controller dptx index */ + param_hdr.module_id = AFE_MODULE_AUDIO_DEV_INTERFACE; + param_hdr.instance_id = INSTANCE_ID_0; + param_hdr.param_id = AFE_PARAM_ID_HDMI_DPTX_IDX_CFG; + param_hdr.param_size = sizeof(struct afe_display_ctl_idx); + ctl_data.minor_version = 1; + ctl_data.ctl_idx = ctl_idx; + ret = q6common_pack_pp_params(packed_param_data + packed_param_size, + ¶m_hdr, (u8 *) &ctl_data, + &single_param_size); + if (ret) { + pr_err("%s: Failed to pack param data, error %d\n", __func__, + ret); + goto done; + } + packed_param_size += single_param_size; + + pr_debug("%s: rx(0x%x) stream(%d) controller(%d)\n", + __func__, rx_port_id, stream_idx, ctl_idx); + + ret = q6afe_set_params(rx_port_id, q6audio_get_port_index(rx_port_id), + NULL, packed_param_data, packed_param_size); + if (ret) + pr_err("%s: AFE display stream send failed for rx_port:%d ret:%d\n", + __func__, rx_port_id, ret); + +done: + kfree(packed_param_data); + return ret; + +} +EXPORT_SYMBOL(afe_set_display_stream); + +int afe_validate_port(u16 port_id) +{ + int ret; + + switch (port_id) { + case PRIMARY_I2S_RX: + case PRIMARY_I2S_TX: + case AFE_PORT_ID_PRIMARY_PCM_RX: + case AFE_PORT_ID_PRIMARY_PCM_TX: + case AFE_PORT_ID_SECONDARY_PCM_RX: + case AFE_PORT_ID_SECONDARY_PCM_TX: + case AFE_PORT_ID_TERTIARY_PCM_RX: + case AFE_PORT_ID_TERTIARY_PCM_TX: + case AFE_PORT_ID_QUATERNARY_PCM_RX: + case AFE_PORT_ID_QUATERNARY_PCM_TX: + case AFE_PORT_ID_QUINARY_PCM_RX: + case AFE_PORT_ID_QUINARY_PCM_TX: + case SECONDARY_I2S_RX: + case SECONDARY_I2S_TX: + case MI2S_RX: + case MI2S_TX: + case HDMI_RX: + case DISPLAY_PORT_RX: + case AFE_PORT_ID_PRIMARY_SPDIF_RX: + case AFE_PORT_ID_PRIMARY_SPDIF_TX: + case AFE_PORT_ID_SECONDARY_SPDIF_RX: + case AFE_PORT_ID_SECONDARY_SPDIF_TX: + case RSVD_2: + case RSVD_3: + case DIGI_MIC_TX: + case VOICE_RECORD_RX: + case VOICE_RECORD_TX: + case VOICE_PLAYBACK_TX: + case VOICE2_PLAYBACK_TX: + case SLIMBUS_0_RX: + case SLIMBUS_0_TX: + case SLIMBUS_1_RX: + case SLIMBUS_1_TX: + case SLIMBUS_2_RX: + case SLIMBUS_2_TX: + case SLIMBUS_3_RX: + case INT_BT_SCO_RX: + case INT_BT_SCO_TX: + case INT_BT_A2DP_RX: + case INT_FM_RX: + case INT_FM_TX: + case RT_PROXY_PORT_001_RX: + case RT_PROXY_PORT_001_TX: + case SLIMBUS_4_RX: + case SLIMBUS_4_TX: + case SLIMBUS_5_RX: + case SLIMBUS_6_RX: + case SLIMBUS_6_TX: + case SLIMBUS_7_RX: + case SLIMBUS_7_TX: + case SLIMBUS_8_RX: + case SLIMBUS_8_TX: + case SLIMBUS_9_RX: + case SLIMBUS_9_TX: + case AFE_PORT_ID_USB_RX: + case AFE_PORT_ID_USB_TX: + case AFE_PORT_ID_PRIMARY_MI2S_RX: + case AFE_PORT_ID_PRIMARY_MI2S_TX: + case AFE_PORT_ID_SECONDARY_MI2S_RX: + case AFE_PORT_ID_SECONDARY_MI2S_TX: + case AFE_PORT_ID_QUATERNARY_MI2S_RX: + case AFE_PORT_ID_QUATERNARY_MI2S_TX: + case AFE_PORT_ID_TERTIARY_MI2S_RX: + case AFE_PORT_ID_TERTIARY_MI2S_TX: + case AFE_PORT_ID_QUINARY_MI2S_RX: + case AFE_PORT_ID_QUINARY_MI2S_TX: + case AFE_PORT_ID_SENARY_MI2S_TX: + case AFE_PORT_ID_PRIMARY_TDM_RX: + case AFE_PORT_ID_PRIMARY_TDM_TX: + case AFE_PORT_ID_PRIMARY_TDM_RX_1: + case AFE_PORT_ID_PRIMARY_TDM_TX_1: + case AFE_PORT_ID_PRIMARY_TDM_RX_2: + case AFE_PORT_ID_PRIMARY_TDM_TX_2: + case AFE_PORT_ID_PRIMARY_TDM_RX_3: + case AFE_PORT_ID_PRIMARY_TDM_TX_3: + case AFE_PORT_ID_PRIMARY_TDM_RX_4: + case AFE_PORT_ID_PRIMARY_TDM_TX_4: + case AFE_PORT_ID_PRIMARY_TDM_RX_5: + case AFE_PORT_ID_PRIMARY_TDM_TX_5: + case AFE_PORT_ID_PRIMARY_TDM_RX_6: + case AFE_PORT_ID_PRIMARY_TDM_TX_6: + case AFE_PORT_ID_PRIMARY_TDM_RX_7: + case AFE_PORT_ID_PRIMARY_TDM_TX_7: + case AFE_PORT_ID_SECONDARY_TDM_RX: + case AFE_PORT_ID_SECONDARY_TDM_TX: + case AFE_PORT_ID_SECONDARY_TDM_RX_1: + case AFE_PORT_ID_SECONDARY_TDM_TX_1: + case AFE_PORT_ID_SECONDARY_TDM_RX_2: + case AFE_PORT_ID_SECONDARY_TDM_TX_2: + case AFE_PORT_ID_SECONDARY_TDM_RX_3: + case AFE_PORT_ID_SECONDARY_TDM_TX_3: + case AFE_PORT_ID_SECONDARY_TDM_RX_4: + case AFE_PORT_ID_SECONDARY_TDM_TX_4: + case AFE_PORT_ID_SECONDARY_TDM_RX_5: + case AFE_PORT_ID_SECONDARY_TDM_TX_5: + case AFE_PORT_ID_SECONDARY_TDM_RX_6: + case AFE_PORT_ID_SECONDARY_TDM_TX_6: + case AFE_PORT_ID_SECONDARY_TDM_RX_7: + case AFE_PORT_ID_SECONDARY_TDM_TX_7: + case AFE_PORT_ID_TERTIARY_TDM_RX: + case AFE_PORT_ID_TERTIARY_TDM_TX: + case AFE_PORT_ID_TERTIARY_TDM_RX_1: + case AFE_PORT_ID_TERTIARY_TDM_TX_1: + case AFE_PORT_ID_TERTIARY_TDM_RX_2: + case AFE_PORT_ID_TERTIARY_TDM_TX_2: + case AFE_PORT_ID_TERTIARY_TDM_RX_3: + case AFE_PORT_ID_TERTIARY_TDM_TX_3: + case AFE_PORT_ID_TERTIARY_TDM_RX_4: + case AFE_PORT_ID_TERTIARY_TDM_TX_4: + case AFE_PORT_ID_TERTIARY_TDM_RX_5: + case AFE_PORT_ID_TERTIARY_TDM_TX_5: + case AFE_PORT_ID_TERTIARY_TDM_RX_6: + case AFE_PORT_ID_TERTIARY_TDM_TX_6: + case AFE_PORT_ID_TERTIARY_TDM_RX_7: + case AFE_PORT_ID_TERTIARY_TDM_TX_7: + case AFE_PORT_ID_QUATERNARY_TDM_RX: + case AFE_PORT_ID_QUATERNARY_TDM_TX: + case AFE_PORT_ID_QUATERNARY_TDM_RX_1: + case AFE_PORT_ID_QUATERNARY_TDM_TX_1: + case AFE_PORT_ID_QUATERNARY_TDM_RX_2: + case AFE_PORT_ID_QUATERNARY_TDM_TX_2: + case AFE_PORT_ID_QUATERNARY_TDM_RX_3: + case AFE_PORT_ID_QUATERNARY_TDM_TX_3: + case AFE_PORT_ID_QUATERNARY_TDM_RX_4: + case AFE_PORT_ID_QUATERNARY_TDM_TX_4: + case AFE_PORT_ID_QUATERNARY_TDM_RX_5: + case AFE_PORT_ID_QUATERNARY_TDM_TX_5: + case AFE_PORT_ID_QUATERNARY_TDM_RX_6: + case AFE_PORT_ID_QUATERNARY_TDM_TX_6: + case AFE_PORT_ID_QUATERNARY_TDM_RX_7: + case AFE_PORT_ID_QUATERNARY_TDM_TX_7: + case AFE_PORT_ID_QUINARY_TDM_RX: + case AFE_PORT_ID_QUINARY_TDM_TX: + case AFE_PORT_ID_QUINARY_TDM_RX_1: + case AFE_PORT_ID_QUINARY_TDM_TX_1: + case AFE_PORT_ID_QUINARY_TDM_RX_2: + case AFE_PORT_ID_QUINARY_TDM_TX_2: + case AFE_PORT_ID_QUINARY_TDM_RX_3: + case AFE_PORT_ID_QUINARY_TDM_TX_3: + case AFE_PORT_ID_QUINARY_TDM_RX_4: + case AFE_PORT_ID_QUINARY_TDM_TX_4: + case AFE_PORT_ID_QUINARY_TDM_RX_5: + case AFE_PORT_ID_QUINARY_TDM_TX_5: + case AFE_PORT_ID_QUINARY_TDM_RX_6: + case AFE_PORT_ID_QUINARY_TDM_TX_6: + case AFE_PORT_ID_QUINARY_TDM_RX_7: + case AFE_PORT_ID_QUINARY_TDM_TX_7: + case AFE_PORT_ID_INT0_MI2S_RX: + case AFE_PORT_ID_INT1_MI2S_RX: + case AFE_PORT_ID_INT2_MI2S_RX: + case AFE_PORT_ID_INT3_MI2S_RX: + case AFE_PORT_ID_INT4_MI2S_RX: + case AFE_PORT_ID_INT5_MI2S_RX: + case AFE_PORT_ID_INT6_MI2S_RX: + case AFE_PORT_ID_INT0_MI2S_TX: + case AFE_PORT_ID_INT1_MI2S_TX: + case AFE_PORT_ID_INT2_MI2S_TX: + case AFE_PORT_ID_INT3_MI2S_TX: + case AFE_PORT_ID_INT4_MI2S_TX: + case AFE_PORT_ID_INT5_MI2S_TX: + case AFE_PORT_ID_INT6_MI2S_TX: + case AFE_PORT_ID_WSA_CODEC_DMA_RX_0: + case AFE_PORT_ID_WSA_CODEC_DMA_TX_0: + case AFE_PORT_ID_WSA_CODEC_DMA_RX_1: + case AFE_PORT_ID_WSA_CODEC_DMA_TX_1: + case AFE_PORT_ID_WSA_CODEC_DMA_TX_2: + case AFE_PORT_ID_VA_CODEC_DMA_TX_0: + case AFE_PORT_ID_VA_CODEC_DMA_TX_1: + case AFE_PORT_ID_RX_CODEC_DMA_RX_0: + case AFE_PORT_ID_TX_CODEC_DMA_TX_0: + case AFE_PORT_ID_RX_CODEC_DMA_RX_1: + case AFE_PORT_ID_TX_CODEC_DMA_TX_1: + case AFE_PORT_ID_RX_CODEC_DMA_RX_2: + case AFE_PORT_ID_TX_CODEC_DMA_TX_2: + case AFE_PORT_ID_RX_CODEC_DMA_RX_3: + case AFE_PORT_ID_TX_CODEC_DMA_TX_3: + case AFE_PORT_ID_RX_CODEC_DMA_RX_4: + case AFE_PORT_ID_TX_CODEC_DMA_TX_4: + case AFE_PORT_ID_RX_CODEC_DMA_RX_5: + case AFE_PORT_ID_TX_CODEC_DMA_TX_5: + case AFE_PORT_ID_RX_CODEC_DMA_RX_6: + case AFE_PORT_ID_RX_CODEC_DMA_RX_7: + { + ret = 0; + break; + } + + default: + pr_err("%s: default ret 0x%x\n", __func__, port_id); + ret = -EINVAL; + } + + return ret; +} + +int afe_convert_virtual_to_portid(u16 port_id) +{ + int ret; + + /* + * if port_id is virtual, convert to physical.. + * if port_id is already physical, return physical + */ + if (afe_validate_port(port_id) < 0) { + if (port_id == RT_PROXY_DAI_001_RX || + port_id == RT_PROXY_DAI_001_TX || + port_id == RT_PROXY_DAI_002_RX || + port_id == RT_PROXY_DAI_002_TX) { + ret = VIRTUAL_ID_TO_PORTID(port_id); + } else { + pr_err("%s: wrong port 0x%x\n", + __func__, port_id); + ret = -EINVAL; + } + } else + ret = port_id; + + return ret; +} +int afe_port_stop_nowait(int port_id) +{ + struct afe_port_cmd_device_stop stop; + int ret = 0; + + if (this_afe.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 = afe_apr_send_pkt(&stop, NULL); + if (ret) + pr_err("%s: AFE close failed %d\n", __func__, ret); + +fail_cmd: + return ret; + +} + +/** + * afe_close - command to close AFE port + * + * @port_id: AFE port id + * + * Returns 0 on success, appropriate error code otherwise + */ +int afe_close(int port_id) +{ + struct afe_port_cmd_device_stop stop; + enum afe_mad_type mad_type; + int ret = 0; + int index = 0; + uint16_t port_index; + + if (this_afe.apr == NULL) { + pr_err("%s: AFE is already closed\n", __func__); + if ((port_id == RT_PROXY_DAI_001_RX) || + (port_id == RT_PROXY_DAI_002_TX)) + pcm_afe_instance[port_id & 0x1] = 0; + if ((port_id == RT_PROXY_DAI_002_RX) || + (port_id == RT_PROXY_DAI_001_TX)) + proxy_afe_instance[port_id & 0x1] = 0; + afe_close_done[port_id & 0x1] = true; + ret = -EINVAL; + goto fail_cmd; + } + pr_debug("%s: port_id = 0x%x\n", __func__, port_id); + if ((port_id == RT_PROXY_DAI_001_RX) || + (port_id == RT_PROXY_DAI_002_TX)) { + pr_debug("%s: before decrementing pcm_afe_instance %d\n", + __func__, pcm_afe_instance[port_id & 0x1]); + port_id = VIRTUAL_ID_TO_PORTID(port_id); + pcm_afe_instance[port_id & 0x1]--; + if ((!(pcm_afe_instance[port_id & 0x1] == 0 && + proxy_afe_instance[port_id & 0x1] == 0)) || + afe_close_done[port_id & 0x1] == true) + return 0; + + afe_close_done[port_id & 0x1] = true; + } + + if ((port_id == RT_PROXY_DAI_002_RX) || + (port_id == RT_PROXY_DAI_001_TX)) { + pr_debug("%s: before decrementing proxy_afe_instance %d\n", + __func__, proxy_afe_instance[port_id & 0x1]); + port_id = VIRTUAL_ID_TO_PORTID(port_id); + proxy_afe_instance[port_id & 0x1]--; + if ((!(pcm_afe_instance[port_id & 0x1] == 0 && + proxy_afe_instance[port_id & 0x1] == 0)) || + afe_close_done[port_id & 0x1] == true) + return 0; + + afe_close_done[port_id & 0x1] = true; + } + + port_id = q6audio_convert_virtual_to_portid(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 = q6audio_validate_port(port_id); + if (ret < 0) { + pr_warn("%s: Not a valid port id 0x%x ret %d\n", + __func__, port_id, ret); + return -EINVAL; + } + + mad_type = afe_port_get_mad_type(port_id); + pr_debug("%s: port_id 0x%x, mad_type %d\n", __func__, port_id, + mad_type); + if (mad_type != MAD_HW_NONE && mad_type != MAD_SW_AUDIO) { + pr_debug("%s: Turn off MAD\n", __func__); + ret = afe_turn_onoff_hw_mad(mad_type, false); + if (ret) { + pr_err("%s: afe_turn_onoff_hw_mad failed %d\n", + __func__, ret); + return ret; + } + } else { + pr_debug("%s: Not a MAD port\n", __func__); + } + + port_index = afe_get_port_index(port_id); + if ((port_index >= 0) && (port_index < AFE_MAX_PORTS)) { + this_afe.afe_sample_rates[port_index] = 0; + this_afe.topology[port_index] = 0; + this_afe.dev_acdb_id[port_index] = 0; + } else { + pr_err("%s: port %d\n", __func__, port_index); + ret = -EINVAL; + goto fail_cmd; + } + + if ((port_id == this_afe.aanc_info.aanc_tx_port) && + (this_afe.aanc_info.aanc_active)) { + memset(&this_afe.aanc_info, 0x00, sizeof(this_afe.aanc_info)); + ret = afe_aanc_mod_enable(this_afe.apr, port_id, 0); + if (ret) + pr_err("%s: AFE mod disable failed %d\n", + __func__, ret); + } + + /* + * even if ramp down configuration failed it is not serious enough to + * warrant bailaing out. + */ + if (afe_spk_ramp_dn_cfg(port_id) < 0) + pr_err("%s: ramp down configuration failed\n", __func__); + + 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 = index; + stop.hdr.opcode = AFE_PORT_CMD_DEVICE_STOP; + stop.port_id = q6audio_get_port_id(port_id); + stop.reserved = 0; + + ret = afe_apr_send_pkt(&stop, &this_afe.wait[index]); + if (ret) + pr_err("%s: AFE close failed %d\n", __func__, ret); + +fail_cmd: + return ret; +} +EXPORT_SYMBOL(afe_close); + +int afe_set_digital_codec_core_clock(u16 port_id, + struct afe_digital_clk_cfg *cfg) +{ + struct afe_digital_clk_cfg clk_cfg; + struct param_hdr_v3 param_hdr; + int ret = 0; + + if (!cfg) { + pr_err("%s: clock cfg is NULL\n", __func__); + return -EINVAL; + } + + memset(&clk_cfg, 0, sizeof(clk_cfg)); + memset(¶m_hdr, 0, sizeof(param_hdr)); + + /*default rx port is taken to enable the codec digital clock*/ + param_hdr.module_id = AFE_MODULE_AUDIO_DEV_INTERFACE; + param_hdr.instance_id = INSTANCE_ID_0; + param_hdr.param_id = AFE_PARAM_ID_INTERNAL_DIGIATL_CDC_CLK_CONFIG; + param_hdr.param_size = sizeof(struct afe_digital_clk_cfg); + clk_cfg = *cfg; + + pr_debug("%s: Minor version =0x%x clk val = %d\n" + "clk root = 0x%x resrv = 0x%x\n", + __func__, cfg->i2s_cfg_minor_version, cfg->clk_val, + cfg->clk_root, cfg->reserved); + + ret = q6afe_pack_and_set_param_in_band(port_id, + q6audio_get_port_index(port_id), + param_hdr, (u8 *) &clk_cfg); + if (ret < 0) + pr_err("%s: AFE enable for port 0x%x ret %d\n", __func__, + port_id, ret); + return ret; +} + +/** + * afe_set_lpass_clock - Enable AFE lpass clock + * + * @port_id: AFE port id + * @cfg: pointer to clk set struct + * + * Returns 0 on success, appropriate error code otherwise + */ +int afe_set_lpass_clock(u16 port_id, struct afe_clk_cfg *cfg) +{ + struct afe_clk_cfg clk_cfg; + struct param_hdr_v3 param_hdr; + int ret = 0; + + if (!cfg) { + pr_err("%s: clock cfg is NULL\n", __func__); + return -EINVAL; + } + + memset(&clk_cfg, 0, sizeof(clk_cfg)); + memset(¶m_hdr, 0, sizeof(param_hdr)); + + ret = q6audio_is_digital_pcm_interface(port_id); + if (ret < 0) { + pr_err("%s: q6audio_is_digital_pcm_interface fail %d\n", + __func__, ret); + return -EINVAL; + } + + mutex_lock(&this_afe.afe_cmd_lock); + param_hdr.module_id = AFE_MODULE_AUDIO_DEV_INTERFACE; + param_hdr.instance_id = INSTANCE_ID_0; + param_hdr.param_id = AFE_PARAM_ID_LPAIF_CLK_CONFIG; + param_hdr.param_size = sizeof(clk_cfg); + clk_cfg = *cfg; + + pr_debug("%s: Minor version =0x%x clk val1 = %d\n" + "clk val2 = %d, clk src = 0x%x\n" + "clk root = 0x%x clk mode = 0x%x resrv = 0x%x\n" + "port id = 0x%x\n", + __func__, cfg->i2s_cfg_minor_version, + cfg->clk_val1, cfg->clk_val2, cfg->clk_src, + cfg->clk_root, cfg->clk_set_mode, + cfg->reserved, q6audio_get_port_id(port_id)); + + ret = q6afe_pack_and_set_param_in_band(port_id, + q6audio_get_port_index(port_id), + param_hdr, (u8 *) &clk_cfg); + if (ret < 0) + pr_err("%s: AFE enable for port 0x%x ret %d\n", + __func__, port_id, ret); + + mutex_unlock(&this_afe.afe_cmd_lock); + return ret; +} +EXPORT_SYMBOL(afe_set_lpass_clock); + +/** + * afe_set_lpass_clk_cfg - Set AFE clk config + * + * @index: port index + * @cfg: pointer to clk set struct + * + * Returns 0 on success, appropriate error code otherwise + */ +int afe_set_lpass_clk_cfg(int index, struct afe_clk_set *cfg) +{ + struct param_hdr_v3 param_hdr; + int ret = 0; + + if (!cfg) { + pr_err("%s: clock cfg is NULL\n", __func__); + ret = -EINVAL; + return ret; + } + + if (index < 0 || index >= AFE_MAX_PORTS) { + pr_err("%s: index[%d] invalid!\n", __func__, index); + return -EINVAL; + } + + memset(¶m_hdr, 0, sizeof(param_hdr)); + + ret = afe_q6_interface_prepare(); + if (ret != 0) { + pr_err_ratelimited("%s: Q6 interface prepare failed %d\n", __func__, ret); + return ret; + } + + mutex_lock(&this_afe.afe_cmd_lock); + param_hdr.module_id = AFE_MODULE_CLOCK_SET; + param_hdr.instance_id = INSTANCE_ID_0; + param_hdr.param_id = AFE_PARAM_ID_CLOCK_SET; + param_hdr.param_size = sizeof(struct afe_clk_set); + + + pr_debug("%s: Minor version =0x%x clk id = %d\n" + "clk freq (Hz) = %d, clk attri = 0x%x\n" + "clk root = 0x%x clk enable = 0x%x\n", + __func__, cfg->clk_set_minor_version, + cfg->clk_id, cfg->clk_freq_in_hz, cfg->clk_attri, + cfg->clk_root, cfg->enable); + + ret = q6afe_svc_pack_and_set_param_in_band(index, param_hdr, + (u8 *) cfg); + if (ret < 0) + pr_err_ratelimited("%s: AFE clk cfg failed with ret %d\n", + __func__, ret); + + mutex_unlock(&this_afe.afe_cmd_lock); + return ret; +} +EXPORT_SYMBOL(afe_set_lpass_clk_cfg); + +/** + * afe_set_lpass_clock_v2 - Enable AFE lpass clock + * + * @port_id: AFE port id + * @cfg: pointer to clk set struct + * + * Returns 0 on success, appropriate error code otherwise + */ +int afe_set_lpass_clock_v2(u16 port_id, struct afe_clk_set *cfg) +{ + int index = 0; + int ret = 0; + + 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_is_digital_pcm_interface(port_id); + if (ret < 0) { + pr_err("%s: q6audio_is_digital_pcm_interface fail %d\n", + __func__, ret); + return -EINVAL; + } + + ret = afe_set_lpass_clk_cfg(index, cfg); + if (ret) + pr_err("%s: afe_set_lpass_clk_cfg_v2 failed %d\n", + __func__, ret); + + return ret; +} +EXPORT_SYMBOL(afe_set_lpass_clock_v2); + +int afe_set_lpass_internal_digital_codec_clock(u16 port_id, + struct afe_digital_clk_cfg *cfg) +{ + struct afe_digital_clk_cfg clk_cfg; + struct param_hdr_v3 param_hdr; + int ret = 0; + + if (!cfg) { + pr_err("%s: clock cfg is NULL\n", __func__); + return -EINVAL; + } + + memset(&clk_cfg, 0, sizeof(clk_cfg)); + memset(¶m_hdr, 0, sizeof(param_hdr)); + + ret = q6audio_is_digital_pcm_interface(port_id); + if (ret < 0) { + pr_err("%s: q6audio_is_digital_pcm_interface fail %d\n", + __func__, ret); + return -EINVAL; + } + + param_hdr.module_id = AFE_MODULE_AUDIO_DEV_INTERFACE; + param_hdr.instance_id = INSTANCE_ID_0; + param_hdr.param_id = AFE_PARAM_ID_INTERNAL_DIGIATL_CDC_CLK_CONFIG; + param_hdr.param_size = sizeof(clk_cfg); + clk_cfg = *cfg; + + pr_debug("%s: Minor version =0x%x clk val = %d\n" + "clk root = 0x%x resrv = 0x%x port id = 0x%x\n", + __func__, cfg->i2s_cfg_minor_version, + cfg->clk_val, cfg->clk_root, cfg->reserved, + q6audio_get_port_id(port_id)); + + ret = q6afe_pack_and_set_param_in_band(port_id, + q6audio_get_port_index(port_id), + param_hdr, (u8 *) &clk_cfg); + if (ret < 0) + pr_err("%s: AFE enable for port 0x0x%x ret %d\n", + __func__, port_id, ret); + + return ret; +} + +int afe_enable_lpass_core_shared_clock(u16 port_id, u32 enable) +{ + struct afe_param_id_lpass_core_shared_clk_cfg clk_cfg; + struct param_hdr_v3 param_hdr; + int ret = 0; + + memset(&clk_cfg, 0, sizeof(clk_cfg)); + memset(¶m_hdr, 0, sizeof(param_hdr)); + + ret = q6audio_is_digital_pcm_interface(port_id); + if (ret < 0) { + pr_err("%s: q6audio_is_digital_pcm_interface fail %d\n", + __func__, ret); + return -EINVAL; + } + + mutex_lock(&this_afe.afe_cmd_lock); + param_hdr.module_id = AFE_MODULE_AUDIO_DEV_INTERFACE; + param_hdr.instance_id = INSTANCE_ID_0; + param_hdr.param_id = AFE_PARAM_ID_LPASS_CORE_SHARED_CLOCK_CONFIG; + param_hdr.param_size = sizeof(clk_cfg); + clk_cfg.lpass_core_shared_clk_cfg_minor_version = + AFE_API_VERSION_LPASS_CORE_SHARED_CLK_CONFIG; + clk_cfg.enable = enable; + + pr_debug("%s: port id = %d, enable = %d\n", + __func__, q6audio_get_port_id(port_id), enable); + + ret = q6afe_pack_and_set_param_in_band(port_id, + q6audio_get_port_index(port_id), + param_hdr, (u8 *) &clk_cfg); + if (ret < 0) + pr_err("%s: AFE enable for port 0x%x ret %d\n", + __func__, port_id, ret); + + mutex_unlock(&this_afe.afe_cmd_lock); + return ret; +} + +int q6afe_check_osr_clk_freq(u32 freq) +{ + int ret = 0; + + switch (freq) { + case Q6AFE_LPASS_OSR_CLK_12_P288_MHZ: + case Q6AFE_LPASS_OSR_CLK_9_P600_MHZ: + case Q6AFE_LPASS_OSR_CLK_8_P192_MHZ: + case Q6AFE_LPASS_OSR_CLK_6_P144_MHZ: + case Q6AFE_LPASS_OSR_CLK_4_P096_MHZ: + case Q6AFE_LPASS_OSR_CLK_3_P072_MHZ: + case Q6AFE_LPASS_OSR_CLK_2_P048_MHZ: + case Q6AFE_LPASS_OSR_CLK_1_P536_MHZ: + case Q6AFE_LPASS_OSR_CLK_1_P024_MHZ: + case Q6AFE_LPASS_OSR_CLK_768_kHZ: + case Q6AFE_LPASS_OSR_CLK_512_kHZ: + break; + default: + pr_err("%s: default freq 0x%x\n", + __func__, freq); + ret = -EINVAL; + } + return ret; +} + +static int afe_get_sp_th_vi_v_vali_data( + struct afe_sp_th_vi_v_vali_get_param *th_vi_v_vali) +{ + struct param_hdr_v3 param_hdr; + int port = SLIMBUS_4_TX; + int ret = -EINVAL; + + if (!th_vi_v_vali) { + pr_err("%s: Invalid params\n", __func__); + goto done; + } + if (this_afe.vi_tx_port != -1) + port = this_afe.vi_tx_port; + + memset(¶m_hdr, 0, sizeof(param_hdr)); + + param_hdr.module_id = AFE_MODULE_SPEAKER_PROTECTION_V2_TH_VI; + param_hdr.instance_id = INSTANCE_ID_0; + param_hdr.param_id = AFE_PARAM_ID_SP_V2_TH_VI_V_VALI_PARAMS; + param_hdr.param_size = sizeof(struct afe_sp_th_vi_v_vali_params); + + ret = q6afe_get_params(port, NULL, ¶m_hdr); + if (ret) { + pr_err("%s: Failed to get TH VI V-Vali data\n", __func__); + goto done; + } + + th_vi_v_vali->pdata = param_hdr; + memcpy(&th_vi_v_vali->param, &this_afe.th_vi_v_vali_resp.param, + sizeof(this_afe.th_vi_v_vali_resp.param)); + pr_debug("%s: Vrms %d %d status %d %d\n", __func__, + th_vi_v_vali->param.vrms_q24[SP_V2_SPKR_1], + th_vi_v_vali->param.vrms_q24[SP_V2_SPKR_2], + th_vi_v_vali->param.status[SP_V2_SPKR_1], + th_vi_v_vali->param.status[SP_V2_SPKR_2]); + ret = 0; +done: + return ret; +} + +int afe_get_sp_th_vi_ftm_data(struct afe_sp_th_vi_get_param *th_vi) +{ + struct param_hdr_v3 param_hdr; + int port = SLIMBUS_4_TX; + int ret = -EINVAL; + + if (!th_vi) { + pr_err("%s: Invalid params\n", __func__); + goto done; + } + if (this_afe.vi_tx_port != -1) + port = this_afe.vi_tx_port; + + memset(¶m_hdr, 0, sizeof(param_hdr)); + + param_hdr.module_id = AFE_MODULE_SPEAKER_PROTECTION_V2_TH_VI; + param_hdr.instance_id = INSTANCE_ID_0; + param_hdr.param_id = AFE_PARAM_ID_SP_V2_TH_VI_FTM_PARAMS; + param_hdr.param_size = sizeof(struct afe_sp_th_vi_ftm_params); + + ret = q6afe_get_params(port, NULL, ¶m_hdr); + if (ret) { + pr_err("%s: Failed to get TH VI FTM data\n", __func__); + goto done; + } + + th_vi->pdata = param_hdr; + memcpy(&th_vi->param, &this_afe.th_vi_resp.param, + sizeof(this_afe.th_vi_resp.param)); + pr_debug("%s: DC resistance %d %d temp %d %d status %d %d\n", + __func__, th_vi->param.dc_res_q24[SP_V2_SPKR_1], + th_vi->param.dc_res_q24[SP_V2_SPKR_2], + th_vi->param.temp_q22[SP_V2_SPKR_1], + th_vi->param.temp_q22[SP_V2_SPKR_2], + th_vi->param.status[SP_V2_SPKR_1], + th_vi->param.status[SP_V2_SPKR_2]); + ret = 0; +done: + return ret; +} + +int afe_get_sp_ex_vi_ftm_data(struct afe_sp_ex_vi_get_param *ex_vi) +{ + struct param_hdr_v3 param_hdr; + int port = SLIMBUS_4_TX; + int ret = -EINVAL; + + if (!ex_vi) { + pr_err("%s: Invalid params\n", __func__); + goto done; + } + if (this_afe.vi_tx_port != -1) + port = this_afe.vi_tx_port; + + memset(¶m_hdr, 0, sizeof(param_hdr)); + + param_hdr.module_id = AFE_MODULE_SPEAKER_PROTECTION_V2_EX_VI; + param_hdr.instance_id = INSTANCE_ID_0; + param_hdr.param_id = AFE_PARAM_ID_SP_V2_EX_VI_FTM_PARAMS; + param_hdr.param_size = sizeof(struct afe_sp_ex_vi_ftm_params); + + ret = q6afe_get_params(port, NULL, ¶m_hdr); + if (ret < 0) { + pr_err("%s: get param port 0x%x param id[0x%x]failed %d\n", + __func__, port, param_hdr.param_id, ret); + goto done; + } + + ex_vi->pdata = param_hdr; + memcpy(&ex_vi->param, &this_afe.ex_vi_resp.param, + sizeof(this_afe.ex_vi_resp.param)); + pr_debug("%s: freq %d %d resistance %d %d qfactor %d %d state %d %d\n", + __func__, ex_vi->param.freq_q20[SP_V2_SPKR_1], + ex_vi->param.freq_q20[SP_V2_SPKR_2], + ex_vi->param.resis_q24[SP_V2_SPKR_1], + ex_vi->param.resis_q24[SP_V2_SPKR_2], + ex_vi->param.qmct_q24[SP_V2_SPKR_1], + ex_vi->param.qmct_q24[SP_V2_SPKR_2], + ex_vi->param.status[SP_V2_SPKR_1], + ex_vi->param.status[SP_V2_SPKR_2]); + ret = 0; +done: + return ret; +} + +/** + * afe_get_sp_rx_tmax_xmax_logging_data - + * command to get excursion logging data from DSP + * + * @xt_logging: excursion logging params + * @port: AFE port ID + * + * Returns 0 on success or error on failure + */ +int afe_get_sp_rx_tmax_xmax_logging_data( + struct afe_sp_rx_tmax_xmax_logging_param *xt_logging, + u16 port_id) +{ + struct param_hdr_v3 param_hdr; + int ret = -EINVAL; + + if (!xt_logging) { + pr_err("%s: Invalid params\n", __func__); + goto done; + } + + memset(¶m_hdr, 0, sizeof(param_hdr)); + + param_hdr.module_id = AFE_MODULE_FB_SPKR_PROT_V2_RX; + param_hdr.instance_id = INSTANCE_ID_0; + param_hdr.param_id = AFE_PARAM_ID_SP_RX_TMAX_XMAX_LOGGING; + param_hdr.param_size = sizeof(struct afe_sp_rx_tmax_xmax_logging_param); + + ret = q6afe_get_params(port_id, NULL, ¶m_hdr); + if (ret < 0) { + pr_err("%s: get param port 0x%x param id[0x%x]failed %d\n", + __func__, port_id, param_hdr.param_id, ret); + goto done; + } + + memcpy(xt_logging, &this_afe.xt_logging_resp.param, + sizeof(this_afe.xt_logging_resp.param)); + pr_debug("%s: max_excursion %d %d count_exceeded_excursion %d %d max_temperature %d %d count_exceeded_temperature %d %d\n", + __func__, xt_logging->max_excursion[SP_V2_SPKR_1], + xt_logging->max_excursion[SP_V2_SPKR_2], + xt_logging->count_exceeded_excursion[SP_V2_SPKR_1], + xt_logging->count_exceeded_excursion[SP_V2_SPKR_2], + xt_logging->max_temperature[SP_V2_SPKR_1], + xt_logging->max_temperature[SP_V2_SPKR_2], + xt_logging->count_exceeded_temperature[SP_V2_SPKR_1], + xt_logging->count_exceeded_temperature[SP_V2_SPKR_2]); + ret = 0; +done: + return ret; +} +EXPORT_SYMBOL(afe_get_sp_rx_tmax_xmax_logging_data); + +/** + * afe_get_av_dev_drift - + * command to retrieve AV drift + * + * @timing_stats: timing stats to be updated with AV drift values + * @port: AFE port ID + * + * Returns 0 on success or error on failure + */ +int afe_get_av_dev_drift(struct afe_param_id_dev_timing_stats *timing_stats, + u16 port) +{ + struct param_hdr_v3 param_hdr; + int ret = -EINVAL; + + if (!timing_stats) { + pr_err("%s: Invalid params\n", __func__); + goto exit; + } + + memset(¶m_hdr, 0, sizeof(param_hdr)); + param_hdr.module_id = AFE_MODULE_AUDIO_DEV_INTERFACE; + param_hdr.instance_id = INSTANCE_ID_0; + param_hdr.param_id = AFE_PARAM_ID_DEV_TIMING_STATS; + param_hdr.param_size = sizeof(struct afe_param_id_dev_timing_stats); + + ret = q6afe_get_params(port, NULL, ¶m_hdr); + if (ret < 0) { + pr_err("%s: get param port 0x%x param id[0x%x] failed %d\n", + __func__, port, param_hdr.param_id, ret); + goto exit; + } + + memcpy(timing_stats, &this_afe.av_dev_drift_resp.timing_stats, + param_hdr.param_size); + ret = 0; +exit: + return ret; +} +EXPORT_SYMBOL(afe_get_av_dev_drift); + +int afe_spk_prot_get_calib_data(struct afe_spkr_prot_get_vi_calib *calib_resp) +{ + struct param_hdr_v3 param_hdr; + int port = SLIMBUS_4_TX; + int ret = -EINVAL; + + if (!calib_resp) { + pr_err("%s: Invalid params\n", __func__); + goto fail_cmd; + } + if (this_afe.vi_tx_port != -1) + port = this_afe.vi_tx_port; + + memset(¶m_hdr, 0, sizeof(param_hdr)); + param_hdr.module_id = AFE_MODULE_FB_SPKR_PROT_VI_PROC_V2; + param_hdr.instance_id = INSTANCE_ID_0; + param_hdr.param_id = AFE_PARAM_ID_CALIB_RES_CFG_V2; + param_hdr.param_size = sizeof(struct afe_spkr_prot_get_vi_calib); + + ret = q6afe_get_params(port, NULL, ¶m_hdr); + if (ret < 0) { + pr_err("%s: get param port 0x%x param id[0x%x]failed %d\n", + __func__, port, param_hdr.param_id, ret); + goto fail_cmd; + } + memcpy(&calib_resp->res_cfg, &this_afe.calib_data.res_cfg, + sizeof(this_afe.calib_data.res_cfg)); + pr_info("%s: state %s resistance %d %d\n", __func__, + fbsp_state[calib_resp->res_cfg.th_vi_ca_state], + calib_resp->res_cfg.r0_cali_q24[SP_V2_SPKR_1], + calib_resp->res_cfg.r0_cali_q24[SP_V2_SPKR_2]); + ret = 0; +fail_cmd: + return ret; +} + +/** + * afe_spk_prot_feed_back_cfg - + * command to setup spk protection feedback config + * + * @src_port: source port id + * @dst_port: destination port id + * @l_ch: left speaker active or not + * @r_ch: right speaker active or not + * @enable: flag to enable or disable + * + * Returns 0 on success or error on failure + */ +int afe_spk_prot_feed_back_cfg(int src_port, int dst_port, + int l_ch, int r_ch, u32 enable) +{ + int ret = -EINVAL; + union afe_spkr_prot_config prot_config; + int index = 0; + + if (!enable) { + pr_debug("%s: Disable Feedback tx path", __func__); + this_afe.vi_tx_port = -1; + this_afe.vi_rx_port = -1; + return 0; + } + + if ((q6audio_validate_port(src_port) < 0) || + (q6audio_validate_port(dst_port) < 0)) { + pr_err("%s: invalid ports src 0x%x dst 0x%x", + __func__, src_port, dst_port); + goto fail_cmd; + } + if (!l_ch && !r_ch) { + pr_err("%s: error ch values zero\n", __func__); + goto fail_cmd; + } + pr_debug("%s: src_port 0x%x dst_port 0x%x l_ch %d r_ch %d\n", + __func__, src_port, dst_port, l_ch, r_ch); + memset(&prot_config, 0, sizeof(prot_config)); + prot_config.feedback_path_cfg.dst_portid = + q6audio_get_port_id(dst_port); + if (l_ch) { + prot_config.feedback_path_cfg.chan_info[index++] = 1; + prot_config.feedback_path_cfg.chan_info[index++] = 2; + } + if (r_ch) { + prot_config.feedback_path_cfg.chan_info[index++] = 3; + prot_config.feedback_path_cfg.chan_info[index++] = 4; + } + prot_config.feedback_path_cfg.num_channels = index; + pr_debug("%s no of channels: %d\n", __func__, index); + prot_config.feedback_path_cfg.minor_version = 1; + ret = afe_spk_prot_prepare(src_port, dst_port, + AFE_PARAM_ID_FEEDBACK_PATH_CFG, &prot_config); +fail_cmd: + return ret; +} +EXPORT_SYMBOL(afe_spk_prot_feed_back_cfg); + +static int get_cal_type_index(int32_t cal_type) +{ + int ret = -EINVAL; + + switch (cal_type) { + case AFE_COMMON_RX_CAL_TYPE: + ret = AFE_COMMON_RX_CAL; + break; + case AFE_COMMON_TX_CAL_TYPE: + ret = AFE_COMMON_TX_CAL; + break; + case AFE_LSM_TX_CAL_TYPE: + ret = AFE_LSM_TX_CAL; + break; + case AFE_AANC_CAL_TYPE: + ret = AFE_AANC_CAL; + break; + case AFE_HW_DELAY_CAL_TYPE: + ret = AFE_HW_DELAY_CAL; + break; + case AFE_FB_SPKR_PROT_CAL_TYPE: + ret = AFE_FB_SPKR_PROT_CAL; + break; + case AFE_SIDETONE_CAL_TYPE: + ret = AFE_SIDETONE_CAL; + break; + case AFE_SIDETONE_IIR_CAL_TYPE: + ret = AFE_SIDETONE_IIR_CAL; + break; + case AFE_TOPOLOGY_CAL_TYPE: + ret = AFE_TOPOLOGY_CAL; + break; + case AFE_LSM_TOPOLOGY_CAL_TYPE: + ret = AFE_LSM_TOPOLOGY_CAL; + break; + case AFE_CUST_TOPOLOGY_CAL_TYPE: + ret = AFE_CUST_TOPOLOGY_CAL; + break; + default: + pr_err("%s: invalid cal type %d!\n", __func__, cal_type); + } + return ret; +} + +int afe_alloc_cal(int32_t cal_type, size_t data_size, + void *data) +{ + int ret = 0; + int cal_index; + + cal_index = get_cal_type_index(cal_type); + pr_debug("%s: cal_type = %d cal_index = %d\n", + __func__, cal_type, cal_index); + + if (cal_index < 0) { + pr_err("%s: could not get cal index %d!\n", + __func__, cal_index); + ret = -EINVAL; + goto done; + } + + mutex_lock(&this_afe.afe_cmd_lock); + ret = cal_utils_alloc_cal(data_size, data, + this_afe.cal_data[cal_index], 0, NULL); + if (ret < 0) { + pr_err("%s: cal_utils_alloc_block failed, ret = %d, cal type = %d!\n", + __func__, ret, cal_type); + ret = -EINVAL; + mutex_unlock(&this_afe.afe_cmd_lock); + goto done; + } + mutex_unlock(&this_afe.afe_cmd_lock); +done: + return ret; +} + +static int afe_dealloc_cal(int32_t cal_type, size_t data_size, + void *data) +{ + int ret = 0; + int cal_index; + + pr_debug("%s:\n", __func__); + + cal_index = get_cal_type_index(cal_type); + if (cal_index < 0) { + pr_err("%s: could not get cal index %d!\n", + __func__, cal_index); + ret = -EINVAL; + goto done; + } + + ret = cal_utils_dealloc_cal(data_size, data, + this_afe.cal_data[cal_index]); + if (ret < 0) { + pr_err("%s: cal_utils_dealloc_block failed, ret = %d, cal type = %d!\n", + __func__, ret, cal_type); + ret = -EINVAL; + goto done; + } +done: + return ret; +} + +static int afe_set_cal(int32_t cal_type, size_t data_size, + void *data) +{ + int ret = 0; + int cal_index; + + pr_debug("%s:\n", __func__); + + cal_index = get_cal_type_index(cal_type); + if (cal_index < 0) { + pr_err("%s: could not get cal index %d!\n", + __func__, cal_index); + ret = -EINVAL; + goto done; + } + + ret = cal_utils_set_cal(data_size, data, + this_afe.cal_data[cal_index], 0, NULL); + if (ret < 0) { + pr_err("%s: cal_utils_set_cal failed, ret = %d, cal type = %d!\n", + __func__, ret, cal_type); + ret = -EINVAL; + goto done; + } + + if (cal_index == AFE_CUST_TOPOLOGY_CAL) { + mutex_lock(&this_afe.cal_data[AFE_CUST_TOPOLOGY_CAL]->lock); + this_afe.set_custom_topology = 1; + pr_debug("%s:[AFE_CUSTOM_TOPOLOGY] ret = %d, cal type = %d!\n", + __func__, ret, cal_type); + mutex_unlock(&this_afe.cal_data[AFE_CUST_TOPOLOGY_CAL]->lock); + } + +done: + return ret; +} + +static struct cal_block_data *afe_find_hw_delay_by_path( + struct cal_type_data *cal_type, int path) +{ + struct list_head *ptr, *next; + struct cal_block_data *cal_block = NULL; + + pr_debug("%s:\n", __func__); + + list_for_each_safe(ptr, next, + &cal_type->cal_blocks) { + + cal_block = list_entry(ptr, + struct cal_block_data, list); + + if (cal_utils_is_cal_stale(cal_block)) + continue; + + if (((struct audio_cal_info_hw_delay *)cal_block->cal_info) + ->path == path) { + return cal_block; + } + } + return NULL; +} + +static int afe_get_cal_hw_delay(int32_t path, + struct audio_cal_hw_delay_entry *entry) +{ + int ret = 0; + int i; + struct cal_block_data *cal_block = NULL; + struct audio_cal_hw_delay_data *hw_delay_info = NULL; + + pr_debug("%s:\n", __func__); + + if (this_afe.cal_data[AFE_HW_DELAY_CAL] == NULL) { + pr_err("%s: AFE_HW_DELAY_CAL not initialized\n", __func__); + ret = -EINVAL; + goto done; + } + if (entry == NULL) { + pr_err("%s: entry is NULL\n", __func__); + ret = -EINVAL; + goto done; + } + if ((path >= MAX_PATH_TYPE) || (path < 0)) { + pr_err("%s: bad path: %d\n", + __func__, path); + ret = -EINVAL; + goto done; + } + + mutex_lock(&this_afe.cal_data[AFE_HW_DELAY_CAL]->lock); + cal_block = afe_find_hw_delay_by_path( + this_afe.cal_data[AFE_HW_DELAY_CAL], path); + if (cal_block == NULL) + goto unlock; + + hw_delay_info = &((struct audio_cal_info_hw_delay *) + cal_block->cal_info)->data; + if (hw_delay_info->num_entries > MAX_HW_DELAY_ENTRIES) { + pr_err("%s: invalid num entries: %d\n", + __func__, hw_delay_info->num_entries); + ret = -EINVAL; + goto unlock; + } + + for (i = 0; i < hw_delay_info->num_entries; i++) { + if (hw_delay_info->entry[i].sample_rate == + entry->sample_rate) { + entry->delay_usec = hw_delay_info->entry[i].delay_usec; + break; + } + } + if (i == hw_delay_info->num_entries) { + pr_err("%s: Unable to find delay for sample rate %d\n", + __func__, entry->sample_rate); + ret = -EFAULT; + goto unlock; + } + + cal_utils_mark_cal_used(cal_block); + pr_debug("%s: Path = %d samplerate = %u usec = %u status %d\n", + __func__, path, entry->sample_rate, entry->delay_usec, ret); +unlock: + mutex_unlock(&this_afe.cal_data[AFE_HW_DELAY_CAL]->lock); +done: + return ret; +} + +static int afe_set_cal_sp_th_vi_v_vali_cfg(int32_t cal_type, size_t data_size, + void *data) +{ + int ret = 0; + struct audio_cal_type_sp_th_vi_v_vali_cfg *cal_data = data; + + if (cal_data == NULL || data_size != sizeof(*cal_data)) + goto done; + + memcpy(&this_afe.v_vali_cfg, &cal_data->cal_info, + sizeof(this_afe.v_vali_cfg)); +done: + return ret; +} + +static int afe_set_cal_sp_th_vi_ftm_cfg(int32_t cal_type, size_t data_size, + void *data) +{ + int ret = 0; + struct audio_cal_type_sp_th_vi_ftm_cfg *cal_data = data; + + if (cal_data == NULL || data_size != sizeof(*cal_data)) + goto done; + + memcpy(&this_afe.th_ftm_cfg, &cal_data->cal_info, + sizeof(this_afe.th_ftm_cfg)); +done: + return ret; +} + +static int afe_set_cal_sp_th_vi_cfg(int32_t cal_type, size_t data_size, + void *data) +{ + int ret = 0; + struct audio_cal_type_sp_th_vi_ftm_cfg *cal_data = data; + uint32_t mode; + + if (cal_data == NULL || + this_afe.cal_data[AFE_FB_SPKR_PROT_TH_VI_CAL] == NULL) + goto done; + + mutex_lock(&this_afe.cal_data[AFE_FB_SPKR_PROT_TH_VI_CAL]->lock); + mode = cal_data->cal_info.mode; + pr_debug("%s: cal_type = %d, mode = %d\n", __func__, cal_type, mode); + if (mode == MSM_SPKR_PROT_IN_FTM_MODE) { + ret = afe_set_cal_sp_th_vi_ftm_cfg(cal_type, + data_size, data); + } else if (mode == MSM_SPKR_PROT_IN_V_VALI_MODE) { + ret = afe_set_cal_sp_th_vi_v_vali_cfg(cal_type, + data_size, data); + } + mutex_unlock(&this_afe.cal_data[AFE_FB_SPKR_PROT_TH_VI_CAL]->lock); +done: + return ret; +} + +static int afe_set_cal_sp_ex_vi_ftm_cfg(int32_t cal_type, size_t data_size, + void *data) +{ + int ret = 0; + struct audio_cal_type_sp_ex_vi_ftm_cfg *cal_data = data; + + if (this_afe.cal_data[AFE_FB_SPKR_PROT_EX_VI_CAL] == NULL || + cal_data == NULL || + data_size != sizeof(*cal_data)) + goto done; + + pr_debug("%s: cal_type = %d\n", __func__, cal_type); + mutex_lock(&this_afe.cal_data[AFE_FB_SPKR_PROT_EX_VI_CAL]->lock); + memcpy(&this_afe.ex_ftm_cfg, &cal_data->cal_info, + sizeof(this_afe.ex_ftm_cfg)); + mutex_unlock(&this_afe.cal_data[AFE_FB_SPKR_PROT_EX_VI_CAL]->lock); +done: + return ret; +} + +static int afe_set_cal_fb_spkr_prot(int32_t cal_type, size_t data_size, + void *data) +{ + int ret = 0; + struct audio_cal_type_fb_spk_prot_cfg *cal_data = data; + + pr_debug("%s:\n", __func__); + + if (this_afe.cal_data[AFE_FB_SPKR_PROT_CAL] == NULL) + goto done; + if (cal_data == NULL) + goto done; + if (data_size != sizeof(*cal_data)) + goto done; + + if (cal_data->cal_info.mode == MSM_SPKR_PROT_CALIBRATION_IN_PROGRESS) + __pm_wakeup_event(&wl.ws, jiffies_to_msecs(WAKELOCK_TIMEOUT)); + mutex_lock(&this_afe.cal_data[AFE_FB_SPKR_PROT_CAL]->lock); + memcpy(&this_afe.prot_cfg, &cal_data->cal_info, + sizeof(this_afe.prot_cfg)); + mutex_unlock(&this_afe.cal_data[AFE_FB_SPKR_PROT_CAL]->lock); +done: + return ret; +} + +static int afe_get_cal_sp_th_vi_v_vali_param(int32_t cal_type, size_t data_size, + void *data) +{ + int i, ret = 0; + struct audio_cal_type_sp_th_vi_v_vali_param *cal_data = data; + struct afe_sp_th_vi_v_vali_get_param th_vi_v_vali; + + if (this_afe.cal_data[AFE_FB_SPKR_PROT_TH_VI_CAL] == NULL || + cal_data == NULL || + data_size != sizeof(*cal_data)) + goto done; + + for (i = 0; i < SP_V2_NUM_MAX_SPKRS; i++) { + cal_data->cal_info.status[i] = -EINVAL; + cal_data->cal_info.vrms_q24[i] = -1; + } + if (!afe_get_sp_th_vi_v_vali_data(&th_vi_v_vali)) { + for (i = 0; i < SP_V2_NUM_MAX_SPKRS; i++) { + pr_debug("%s: v-vali param status = %d\n", + __func__, th_vi_v_vali.param.status[i]); + if (th_vi_v_vali.param.status[i] == + V_VALI_IN_PROGRESS) { + cal_data->cal_info.status[i] = -EAGAIN; + } else if (th_vi_v_vali.param.status[i] == + V_VALI_SUCCESS) { + cal_data->cal_info.status[i] = V_VALI_SUCCESS; + cal_data->cal_info.vrms_q24[i] = + th_vi_v_vali.param.vrms_q24[i]; + } + } + } + this_afe.v_vali_flag = 0; +done: + return ret; +} + +static int afe_get_cal_sp_th_vi_ftm_param(int32_t cal_type, size_t data_size, + void *data) +{ + int i, ret = 0; + struct audio_cal_type_sp_th_vi_param *cal_data = data; + struct afe_sp_th_vi_get_param th_vi; + + if (this_afe.cal_data[AFE_FB_SPKR_PROT_TH_VI_CAL] == NULL || + cal_data == NULL || + data_size != sizeof(*cal_data)) + goto done; + + for (i = 0; i < SP_V2_NUM_MAX_SPKRS; i++) { + cal_data->cal_info.status[i] = -EINVAL; + cal_data->cal_info.r_dc_q24[i] = -1; + cal_data->cal_info.temp_q22[i] = -1; + } + if (!afe_get_sp_th_vi_ftm_data(&th_vi)) { + for (i = 0; i < SP_V2_NUM_MAX_SPKRS; i++) { + pr_debug("%s: ftm param status = %d\n", + __func__, th_vi.param.status[i]); + if (th_vi.param.status[i] == FBSP_IN_PROGRESS) { + cal_data->cal_info.status[i] = -EAGAIN; + } else if (th_vi.param.status[i] == FBSP_SUCCESS) { + cal_data->cal_info.status[i] = 0; + cal_data->cal_info.r_dc_q24[i] = + th_vi.param.dc_res_q24[i]; + cal_data->cal_info.temp_q22[i] = + th_vi.param.temp_q22[i]; + } + } + } +done: + return ret; +} + +static int afe_get_cal_sp_th_vi_param(int32_t cal_type, size_t data_size, + void *data) +{ + struct audio_cal_type_sp_th_vi_param *cal_data = data; + uint32_t mode; + int ret = 0; + + if (cal_data == NULL || + this_afe.cal_data[AFE_FB_SPKR_PROT_TH_VI_CAL] == NULL) + return 0; + + mutex_lock(&this_afe.cal_data[AFE_FB_SPKR_PROT_TH_VI_CAL]->lock); + mode = cal_data->cal_info.mode; + pr_debug("%s: cal_type = %d,mode = %d\n", __func__, cal_type, mode); + if (mode == MSM_SPKR_PROT_IN_V_VALI_MODE) + ret = afe_get_cal_sp_th_vi_v_vali_param(cal_type, + data_size, data); + else + ret = afe_get_cal_sp_th_vi_ftm_param(cal_type, + data_size, data); + mutex_unlock(&this_afe.cal_data[AFE_FB_SPKR_PROT_TH_VI_CAL]->lock); + return ret; +} + +static int afe_get_cal_sp_ex_vi_ftm_param(int32_t cal_type, size_t data_size, + void *data) +{ + int i, ret = 0; + struct audio_cal_type_sp_ex_vi_param *cal_data = data; + struct afe_sp_ex_vi_get_param ex_vi; + + pr_debug("%s: cal_type = %d\n", __func__, cal_type); + if (this_afe.cal_data[AFE_FB_SPKR_PROT_EX_VI_CAL] == NULL || + cal_data == NULL || + data_size != sizeof(*cal_data)) + goto done; + + mutex_lock(&this_afe.cal_data[AFE_FB_SPKR_PROT_EX_VI_CAL]->lock); + for (i = 0; i < SP_V2_NUM_MAX_SPKRS; i++) { + cal_data->cal_info.status[i] = -EINVAL; + cal_data->cal_info.freq_q20[i] = -1; + cal_data->cal_info.resis_q24[i] = -1; + cal_data->cal_info.qmct_q24[i] = -1; + } + if (!afe_get_sp_ex_vi_ftm_data(&ex_vi)) { + for (i = 0; i < SP_V2_NUM_MAX_SPKRS; i++) { + pr_debug("%s: ftm param status = %d\n", + __func__, ex_vi.param.status[i]); + if (ex_vi.param.status[i] == FBSP_IN_PROGRESS) { + cal_data->cal_info.status[i] = -EAGAIN; + } else if (ex_vi.param.status[i] == FBSP_SUCCESS) { + cal_data->cal_info.status[i] = 0; + cal_data->cal_info.freq_q20[i] = + ex_vi.param.freq_q20[i]; + cal_data->cal_info.resis_q24[i] = + ex_vi.param.resis_q24[i]; + cal_data->cal_info.qmct_q24[i] = + ex_vi.param.qmct_q24[i]; + } + } + } + mutex_unlock(&this_afe.cal_data[AFE_FB_SPKR_PROT_EX_VI_CAL]->lock); +done: + return ret; +} + +static int afe_get_cal_fb_spkr_prot(int32_t cal_type, size_t data_size, + void *data) +{ + int ret = 0; + struct audio_cal_type_fb_spk_prot_status *cal_data = data; + struct afe_spkr_prot_get_vi_calib calib_resp; + + pr_debug("%s:\n", __func__); + + if (this_afe.cal_data[AFE_FB_SPKR_PROT_CAL] == NULL) + goto done; + if (cal_data == NULL) + goto done; + if (data_size != sizeof(*cal_data)) + goto done; + + mutex_lock(&this_afe.cal_data[AFE_FB_SPKR_PROT_CAL]->lock); + if (this_afe.prot_cfg.mode == MSM_SPKR_PROT_CALIBRATED) { + cal_data->cal_info.r0[SP_V2_SPKR_1] = + this_afe.prot_cfg.r0[SP_V2_SPKR_1]; + cal_data->cal_info.r0[SP_V2_SPKR_2] = + this_afe.prot_cfg.r0[SP_V2_SPKR_2]; + cal_data->cal_info.status = 0; + } else if (this_afe.prot_cfg.mode == + MSM_SPKR_PROT_CALIBRATION_IN_PROGRESS) { + /*Call AFE to query the status*/ + cal_data->cal_info.status = -EINVAL; + cal_data->cal_info.r0[SP_V2_SPKR_1] = -1; + cal_data->cal_info.r0[SP_V2_SPKR_2] = -1; + if (!afe_spk_prot_get_calib_data(&calib_resp)) { + if (calib_resp.res_cfg.th_vi_ca_state == + FBSP_IN_PROGRESS) + cal_data->cal_info.status = -EAGAIN; + else if (calib_resp.res_cfg.th_vi_ca_state == + FBSP_SUCCESS) { + cal_data->cal_info.status = 0; + cal_data->cal_info.r0[SP_V2_SPKR_1] = + calib_resp.res_cfg.r0_cali_q24[SP_V2_SPKR_1]; + cal_data->cal_info.r0[SP_V2_SPKR_2] = + calib_resp.res_cfg.r0_cali_q24[SP_V2_SPKR_2]; + } + } + if (!cal_data->cal_info.status) { + this_afe.prot_cfg.mode = + MSM_SPKR_PROT_CALIBRATED; + this_afe.prot_cfg.r0[SP_V2_SPKR_1] = + cal_data->cal_info.r0[SP_V2_SPKR_1]; + this_afe.prot_cfg.r0[SP_V2_SPKR_2] = + cal_data->cal_info.r0[SP_V2_SPKR_2]; + } + } else { + /*Indicates calibration data is invalid*/ + cal_data->cal_info.status = -EINVAL; + cal_data->cal_info.r0[SP_V2_SPKR_1] = -1; + cal_data->cal_info.r0[SP_V2_SPKR_2] = -1; + } + this_afe.initial_cal = 0; + mutex_unlock(&this_afe.cal_data[AFE_FB_SPKR_PROT_CAL]->lock); + __pm_relax(&wl.ws); +done: + return ret; +} + +static int afe_map_cal_data(int32_t cal_type, + struct cal_block_data *cal_block) +{ + int ret = 0; + int cal_index; + + pr_debug("%s:\n", __func__); + + cal_index = get_cal_type_index(cal_type); + if (cal_index < 0) { + pr_err("%s: could not get cal index %d!\n", + __func__, cal_index); + ret = -EINVAL; + goto done; + } + + atomic_set(&this_afe.mem_map_cal_index, cal_index); + ret = afe_cmd_memory_map(cal_block->cal_data.paddr, + cal_block->map_data.map_size); + atomic_set(&this_afe.mem_map_cal_index, -1); + if (ret < 0) { + pr_err("%s: mmap did not work! size = %zd ret %d\n", + __func__, + cal_block->map_data.map_size, ret); + pr_debug("%s: mmap did not work! addr = 0x%pK, size = %zd\n", + __func__, + &cal_block->cal_data.paddr, + cal_block->map_data.map_size); + goto done; + } + cal_block->map_data.q6map_handle = atomic_read(&this_afe. + mem_map_cal_handles[cal_index]); +done: + return ret; +} + +static int afe_unmap_cal_data(int32_t cal_type, + struct cal_block_data *cal_block) +{ + int ret = 0; + int cal_index; + + pr_debug("%s:\n", __func__); + + cal_index = get_cal_type_index(cal_type); + if (cal_index < 0) { + pr_err("%s: could not get cal index %d!\n", + __func__, cal_index); + ret = -EINVAL; + goto done; + } + + if (cal_block == NULL) { + pr_err("%s: Cal block is NULL!\n", + __func__); + goto done; + } + + if (cal_block->map_data.q6map_handle == 0) { + pr_err("%s: Map handle is NULL, nothing to unmap\n", + __func__); + goto done; + } + + atomic_set(&this_afe.mem_map_cal_handles[cal_index], + cal_block->map_data.q6map_handle); + atomic_set(&this_afe.mem_map_cal_index, cal_index); + ret = afe_cmd_memory_unmap_nowait( + cal_block->map_data.q6map_handle); + atomic_set(&this_afe.mem_map_cal_index, -1); + if (ret < 0) { + pr_err("%s: unmap did not work! cal_type %i ret %d\n", + __func__, cal_index, ret); + } + cal_block->map_data.q6map_handle = 0; +done: + return ret; +} + +static void afe_delete_cal_data(void) +{ + pr_debug("%s:\n", __func__); + + cal_utils_destroy_cal_types(MAX_AFE_CAL_TYPES, this_afe.cal_data); +} + +static int afe_init_cal_data(void) +{ + int ret = 0; + struct cal_type_info cal_type_info[] = { + {{AFE_COMMON_RX_CAL_TYPE, + {afe_alloc_cal, afe_dealloc_cal, NULL, + afe_set_cal, NULL, NULL} }, + {afe_map_cal_data, afe_unmap_cal_data, + cal_utils_match_buf_num} }, + + {{AFE_COMMON_TX_CAL_TYPE, + {afe_alloc_cal, afe_dealloc_cal, NULL, + afe_set_cal, NULL, NULL} }, + {afe_map_cal_data, afe_unmap_cal_data, + cal_utils_match_buf_num} }, + + {{AFE_LSM_TX_CAL_TYPE, + {afe_alloc_cal, afe_dealloc_cal, NULL, + afe_set_cal, NULL, NULL} }, + {afe_map_cal_data, afe_unmap_cal_data, + cal_utils_match_buf_num} }, + + {{AFE_AANC_CAL_TYPE, + {afe_alloc_cal, afe_dealloc_cal, NULL, + afe_set_cal, NULL, NULL} }, + {afe_map_cal_data, afe_unmap_cal_data, + cal_utils_match_buf_num} }, + + {{AFE_FB_SPKR_PROT_CAL_TYPE, + {NULL, NULL, NULL, afe_set_cal_fb_spkr_prot, + afe_get_cal_fb_spkr_prot, NULL} }, + {NULL, NULL, cal_utils_match_buf_num} }, + + {{AFE_HW_DELAY_CAL_TYPE, + {NULL, NULL, NULL, + afe_set_cal, NULL, NULL} }, + {NULL, NULL, cal_utils_match_buf_num} }, + + {{AFE_SIDETONE_CAL_TYPE, + {NULL, NULL, NULL, + afe_set_cal, NULL, NULL} }, + {NULL, NULL, cal_utils_match_buf_num} }, + + {{AFE_SIDETONE_IIR_CAL_TYPE, + {NULL, NULL, NULL, + afe_set_cal, NULL, NULL} }, + {NULL, NULL, cal_utils_match_buf_num} }, + + {{AFE_TOPOLOGY_CAL_TYPE, + {NULL, NULL, NULL, + afe_set_cal, NULL, NULL} }, + {NULL, NULL, + cal_utils_match_buf_num} }, + + {{AFE_LSM_TOPOLOGY_CAL_TYPE, + {NULL, NULL, NULL, + afe_set_cal, NULL, NULL} }, + {NULL, NULL, + cal_utils_match_buf_num} }, + + {{AFE_CUST_TOPOLOGY_CAL_TYPE, + {afe_alloc_cal, afe_dealloc_cal, NULL, + afe_set_cal, NULL, NULL} }, + {afe_map_cal_data, afe_unmap_cal_data, + cal_utils_match_buf_num} }, + + {{AFE_FB_SPKR_PROT_TH_VI_CAL_TYPE, + {NULL, NULL, NULL, afe_set_cal_sp_th_vi_cfg, + afe_get_cal_sp_th_vi_param, NULL} }, + {NULL, NULL, cal_utils_match_buf_num} }, + + {{AFE_FB_SPKR_PROT_EX_VI_CAL_TYPE, + {NULL, NULL, NULL, afe_set_cal_sp_ex_vi_ftm_cfg, + afe_get_cal_sp_ex_vi_ftm_param, NULL} }, + {NULL, NULL, cal_utils_match_buf_num} }, + }; + pr_debug("%s:\n", __func__); + + ret = cal_utils_create_cal_types(MAX_AFE_CAL_TYPES, this_afe.cal_data, + cal_type_info); + if (ret < 0) { + pr_err("%s: could not create cal type! %d\n", + __func__, ret); + ret = -EINVAL; + goto err; + } + + return ret; +err: + afe_delete_cal_data(); + return ret; +} + +int afe_map_rtac_block(struct rtac_cal_block_data *cal_block) +{ + int result = 0; + + pr_debug("%s:\n", __func__); + + if (cal_block == NULL) { + pr_err("%s: cal_block is NULL!\n", + __func__); + result = -EINVAL; + goto done; + } + + if (cal_block->cal_data.paddr == 0) { + pr_debug("%s: No address to map!\n", + __func__); + result = -EINVAL; + goto done; + } + + if (cal_block->map_data.map_size == 0) { + pr_debug("%s: map size is 0!\n", + __func__); + result = -EINVAL; + goto done; + } + + result = afe_cmd_memory_map(cal_block->cal_data.paddr, + cal_block->map_data.map_size); + if (result < 0) { + pr_err("%s: afe_cmd_memory_map failed for addr = 0x%pK, size = %d, err %d\n", + __func__, &cal_block->cal_data.paddr, + cal_block->map_data.map_size, result); + return result; + } + cal_block->map_data.map_handle = this_afe.mmap_handle; + +done: + return result; +} + +int afe_unmap_rtac_block(uint32_t *mem_map_handle) +{ + int result = 0; + + pr_debug("%s:\n", __func__); + + if (mem_map_handle == NULL) { + pr_err("%s: Map handle is NULL, nothing to unmap\n", + __func__); + goto done; + } + + if (*mem_map_handle == 0) { + pr_debug("%s: Map handle is 0, nothing to unmap\n", + __func__); + goto done; + } + + result = afe_cmd_memory_unmap(*mem_map_handle); + if (result) { + pr_err("%s: AFE memory unmap failed %d, handle 0x%x\n", + __func__, result, *mem_map_handle); + goto done; + } else { + *mem_map_handle = 0; + } + +done: + return result; +} + +static void afe_release_uevent_data(struct kobject *kobj) +{ + struct audio_uevent_data *data = container_of(kobj, + struct audio_uevent_data, kobj); + + kfree(data); +} + +int __init afe_init(void) +{ + int i = 0, ret; + + atomic_set(&this_afe.state, 0); + atomic_set(&this_afe.status, 0); + atomic_set(&this_afe.mem_map_cal_index, -1); + this_afe.apr = NULL; + this_afe.dtmf_gen_rx_portid = -1; + this_afe.mmap_handle = 0; + this_afe.vi_tx_port = -1; + this_afe.vi_rx_port = -1; + this_afe.prot_cfg.mode = MSM_SPKR_PROT_DISABLED; + this_afe.th_ftm_cfg.mode = MSM_SPKR_PROT_DISABLED; + this_afe.ex_ftm_cfg.mode = MSM_SPKR_PROT_DISABLED; + mutex_init(&this_afe.afe_cmd_lock); + for (i = 0; i < AFE_MAX_PORTS; i++) { + this_afe.afe_cal_mode[i] = AFE_CAL_MODE_DEFAULT; + this_afe.afe_sample_rates[i] = 0; + this_afe.dev_acdb_id[i] = 0; + this_afe.island_mode[i] = 0; + this_afe.vad_cfg[i].is_enable = 0; + this_afe.vad_cfg[i].pre_roll = 0; + init_waitqueue_head(&this_afe.wait[i]); + } + init_waitqueue_head(&this_afe.wait_wakeup); + init_waitqueue_head(&this_afe.lpass_core_hw_wait); + wakeup_source_init(&wl.ws, "spkr-prot"); + ret = afe_init_cal_data(); + if (ret) + pr_err("%s: could not init cal data! %d\n", __func__, ret); + + config_debug_fs_init(); + + this_afe.uevent_data = kzalloc(sizeof(*(this_afe.uevent_data)), GFP_KERNEL); + if (!this_afe.uevent_data) + return -ENOMEM; + + /* + * Set release function to cleanup memory related to kobject + * before initializing the kobject. + */ + this_afe.uevent_data->ktype.release = afe_release_uevent_data; + q6core_init_uevent_data(this_afe.uevent_data, "q6afe_uevent"); + + INIT_WORK(&this_afe.afe_dc_work, afe_notify_dc_presence_work_fn); + INIT_WORK(&this_afe.afe_spdif_work, + afe_notify_spdif_fmt_update_work_fn); + + this_afe.event_notifier.notifier_call = afe_aud_event_notify; + msm_aud_evt_blocking_register_client(&this_afe.event_notifier); + + return 0; +} + +void afe_exit(void) +{ + if (this_afe.apr) { + apr_reset(this_afe.apr); + atomic_set(&this_afe.state, 0); + this_afe.apr = NULL; + rtac_set_afe_handle(this_afe.apr); + } + + q6core_destroy_uevent_data(this_afe.uevent_data); + + afe_delete_cal_data(); + + config_debug_fs_exit(); + mutex_destroy(&this_afe.afe_cmd_lock); + wakeup_source_trash(&wl.ws); +} + +/* + * afe_cal_init_hwdep - + * Initiliaze AFE HW dependent Node + * + * @card: pointer to sound card + * + */ +int afe_cal_init_hwdep(void *card) +{ + int ret = 0; + + this_afe.fw_data = kzalloc(sizeof(*(this_afe.fw_data)), + GFP_KERNEL); + if (!this_afe.fw_data) + return -ENOMEM; + + set_bit(Q6AFE_VAD_CORE_CAL, this_afe.fw_data->cal_bit); + ret = q6afe_cal_create_hwdep(this_afe.fw_data, Q6AFE_HWDEP_NODE, card); + if (ret < 0) { + pr_err("%s: couldn't create hwdep for AFE %d\n", __func__, ret); + return ret; + } + return ret; +} +EXPORT_SYMBOL(afe_cal_init_hwdep); + +/* + * afe_vote_lpass_core_hw - + * Voting for lpass core hardware + * + * @hw_block_id: ID of hw block to vote for + * @client_name: Name of the client + * @client_handle: Handle for the client + * + */ +int afe_vote_lpass_core_hw(uint32_t hw_block_id, char *client_name, + uint32_t *client_handle) +{ + struct afe_cmd_remote_lpass_core_hw_vote_request hw_vote_cfg; + struct afe_cmd_remote_lpass_core_hw_vote_request *cmd_ptr = + &hw_vote_cfg; + int ret = 0; + + if (!client_handle) { + pr_err("%s: Invalid client_handle\n", __func__); + return -EINVAL; + } + + if (!client_name) { + pr_err("%s: Invalid client_name\n", __func__); + *client_handle = 0; + return -EINVAL; + } + + ret = afe_q6_interface_prepare(); + if (ret != 0) { + pr_err("%s: Q6 interface prepare failed %d\n", __func__, ret); + return ret; + } + + mutex_lock(&this_afe.afe_cmd_lock); + + memset(cmd_ptr, 0, sizeof(hw_vote_cfg)); + + cmd_ptr->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + cmd_ptr->hdr.pkt_size = sizeof(hw_vote_cfg); + cmd_ptr->hdr.src_port = 0; + cmd_ptr->hdr.dest_port = 0; + cmd_ptr->hdr.token = 0; + cmd_ptr->hdr.opcode = AFE_CMD_REMOTE_LPASS_CORE_HW_VOTE_REQUEST; + cmd_ptr->hw_block_id = hw_block_id; + strlcpy(cmd_ptr->client_name, client_name, + sizeof(cmd_ptr->client_name)); + + pr_debug("%s: lpass core hw vote opcode[0x%x] hw id[0x%x]\n", + __func__, cmd_ptr->hdr.opcode, cmd_ptr->hw_block_id); + + *client_handle = 0; + atomic_set(&this_afe.status, 0); + atomic_set(&this_afe.state, 1); + ret = apr_send_pkt(this_afe.apr, (uint32_t *) cmd_ptr); + if (ret < 0) { + pr_err("%s: lpass core hw vote failed %d\n", + __func__, ret); + goto done; + } + + ret = wait_event_timeout(this_afe.lpass_core_hw_wait, + (atomic_read(&this_afe.state) == 0), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: timeout. waited for lpass core hw vote\n", + __func__); + ret = -ETIMEDOUT; + goto done; + } else { + /* set ret to 0 as no timeout happened */ + ret = 0; + } + + if (atomic_read(&this_afe.status) > 0) { + pr_err("%s: lpass core hw vote cmd failed [%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&this_afe.status))); + ret = adsp_err_get_lnx_err_code( + atomic_read(&this_afe.status)); + goto done; + } + + *client_handle = this_afe.lpass_hw_core_client_hdl; + pr_debug("%s: lpass_hw_core_client_hdl %d\n", __func__, + this_afe.lpass_hw_core_client_hdl); +done: + mutex_unlock(&this_afe.afe_cmd_lock); + return ret; +} +EXPORT_SYMBOL(afe_vote_lpass_core_hw); + +/* + * afe_unvote_lpass_core_hw - + * unvoting for lpass core hardware + * + * @hw_block_id: ID of hw block to vote for + * @client_handle: Handle for the client + * + */ +int afe_unvote_lpass_core_hw(uint32_t hw_block_id, uint32_t client_handle) +{ + struct afe_cmd_remote_lpass_core_hw_devote_request hw_vote_cfg; + struct afe_cmd_remote_lpass_core_hw_devote_request *cmd_ptr = + &hw_vote_cfg; + int ret = 0; + + ret = afe_q6_interface_prepare(); + if (ret != 0) { + pr_err("%s: Q6 interface prepare failed %d\n", __func__, ret); + return ret; + } + + mutex_lock(&this_afe.afe_cmd_lock); + + memset(cmd_ptr, 0, sizeof(hw_vote_cfg)); + + cmd_ptr->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + cmd_ptr->hdr.pkt_size = sizeof(hw_vote_cfg); + cmd_ptr->hdr.src_port = 0; + cmd_ptr->hdr.dest_port = 0; + cmd_ptr->hdr.token = 0; + cmd_ptr->hdr.opcode = AFE_CMD_REMOTE_LPASS_CORE_HW_DEVOTE_REQUEST; + cmd_ptr->hw_block_id = hw_block_id; + cmd_ptr->client_handle = client_handle; + + pr_debug("%s: lpass core hw devote opcode[0x%x] hw id[0x%x]\n", + __func__, cmd_ptr->hdr.opcode, cmd_ptr->hw_block_id); + + if (cmd_ptr->client_handle <= 0) { + pr_err("%s: invalid client handle\n", __func__); + ret = -EINVAL; + goto done; + } + + atomic_set(&this_afe.status, 0); + atomic_set(&this_afe.state, 1); + ret = apr_send_pkt(this_afe.apr, (uint32_t *) cmd_ptr); + if (ret < 0) { + pr_err("%s: lpass core hw devote failed %d\n", + __func__, ret); + goto done; + } + + ret = wait_event_timeout(this_afe.lpass_core_hw_wait, + (atomic_read(&this_afe.state) == 0), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: timeout. waited for lpass core hw devote\n", + __func__); + ret = -ETIMEDOUT; + goto done; + } else { + /* set ret to 0 as no timeout happened */ + ret = 0; + } + + if (atomic_read(&this_afe.status) > 0) { + pr_err("%s: lpass core hw devote cmd failed [%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&this_afe.status))); + ret = adsp_err_get_lnx_err_code( + atomic_read(&this_afe.status)); + } + +done: + mutex_unlock(&this_afe.afe_cmd_lock); + return ret; +} +EXPORT_SYMBOL(afe_unvote_lpass_core_hw); + diff --git a/audio-kernel/dsp/q6afecal-hwdep.c b/audio-kernel/dsp/q6afecal-hwdep.c new file mode 100644 index 000000000..5fea2d27a --- /dev/null +++ b/audio-kernel/dsp/q6afecal-hwdep.c @@ -0,0 +1,235 @@ +/* + * Copyright (c) 2015, 2017 - 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 +#include +#include +#include +#include +#include +#include "q6afecal-hwdep.h" + +const int cal_size_info[Q6AFE_MAX_CAL] = { + [Q6AFE_VAD_CORE_CAL] = 132, +}; + +const char *cal_name_info[Q6AFE_MAX_CAL] = { + [Q6AFE_VAD_CORE_CAL] = "vad_core", +}; + +#define AFE_HW_NAME_LENGTH 40 + +/* + * q6afecal_get_fw_cal - + * To get calibration from AFE HW dependent node + * + * @fw_data: pointer to firmware data + * type: AFE calibration type + * + */ +struct firmware_cal *q6afecal_get_fw_cal(struct afe_fw_info *fw_data, + enum q6afe_cal_type type) +{ + if (!fw_data) { + pr_err("%s: fw_data is NULL\n", __func__); + return NULL; + } + if (type >= Q6AFE_MAX_CAL || + type < Q6AFE_MIN_CAL) { + pr_err("%s: wrong cal type sent %d\n", __func__, type); + return NULL; + } + mutex_lock(&fw_data->lock); + if (!test_bit(Q6AFECAL_RECEIVED, + &fw_data->q6afecal_state[type])) { + pr_err("%s: cal not sent by userspace %d\n", + __func__, type); + mutex_unlock(&fw_data->lock); + return NULL; + } + set_bit(Q6AFECAL_INITIALISED, &fw_data->q6afecal_state[type]); + mutex_unlock(&fw_data->lock); + return fw_data->fw[type]; +} +EXPORT_SYMBOL(q6afecal_get_fw_cal); + +static int q6afecal_hwdep_ioctl_shared(struct snd_hwdep *hw, + struct q6afecal_ioctl_buffer fw_user) +{ + struct afe_fw_info *fw_data = hw->private_data; + struct firmware_cal **fw = fw_data->fw; + void *data; + + if (!test_bit(fw_user.cal_type, fw_data->cal_bit)) { + pr_err("%s: q6afe didn't set this %d!!\n", + __func__, fw_user.cal_type); + return -EFAULT; + } + if (fw_user.cal_type >= Q6AFE_MAX_CAL || + fw_user.cal_type < Q6AFE_MIN_CAL) { + pr_err("%s: wrong cal type sent %d\n", + __func__, fw_user.cal_type); + return -EFAULT; + } + if (fw_user.size > cal_size_info[fw_user.cal_type] || + fw_user.size <= 0) { + pr_err("%s: incorrect firmware size %d for %s\n", + __func__, fw_user.size, + cal_name_info[fw_user.cal_type]); + return -EFAULT; + } + data = fw[fw_user.cal_type]->data; + if (copy_from_user(data, fw_user.buffer, fw_user.size)) + return -EFAULT; + fw[fw_user.cal_type]->size = fw_user.size; + mutex_lock(&fw_data->lock); + set_bit(Q6AFECAL_RECEIVED, &fw_data->q6afecal_state[fw_user.cal_type]); + mutex_unlock(&fw_data->lock); + return 0; +} + +#ifdef CONFIG_COMPAT +struct q6afecal_ioctl_buffer32 { + u32 size; + compat_uptr_t buffer; + enum q6afe_cal_type cal_type; +}; + +enum { + SNDRV_CTL_IOCTL_HWDEP_CAL_TYPE32 = + _IOW('U', 0x1, struct q6afecal_ioctl_buffer32), +}; + +static int q6afecal_hwdep_ioctl_compat(struct snd_hwdep *hw, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct q6afecal_ioctl_buffer __user *argp = (void __user *)arg; + struct q6afecal_ioctl_buffer32 fw_user32; + struct q6afecal_ioctl_buffer fw_user_compat; + + if (cmd != SNDRV_CTL_IOCTL_HWDEP_CAL_TYPE32) { + pr_err("%s: wrong ioctl command sent %u!\n", __func__, cmd); + return -ENOIOCTLCMD; + } + if (copy_from_user(&fw_user32, argp, sizeof(fw_user32))) { + pr_err("%s: failed to copy\n", __func__); + return -EFAULT; + } + fw_user_compat.size = fw_user32.size; + fw_user_compat.buffer = compat_ptr(fw_user32.buffer); + fw_user_compat.cal_type = fw_user32.cal_type; + return q6afecal_hwdep_ioctl_shared(hw, fw_user_compat); +} +#else +#define q6afecal_hwdep_ioctl_compat NULL +#endif + +static int q6afecal_hwdep_ioctl(struct snd_hwdep *hw, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct q6afecal_ioctl_buffer __user *argp = (void __user *)arg; + struct q6afecal_ioctl_buffer fw_user; + + if (cmd != SNDRV_IOCTL_HWDEP_VAD_CAL_TYPE) { + pr_err("%s: wrong ioctl command sent %d!\n", __func__, cmd); + return -ENOIOCTLCMD; + } + if (copy_from_user(&fw_user, argp, sizeof(fw_user))) { + pr_err("%s: failed to copy\n", __func__); + return -EFAULT; + } + return q6afecal_hwdep_ioctl_shared(hw, fw_user); +} + +static int q6afecal_hwdep_release(struct snd_hwdep *hw, struct file *file) +{ + struct afe_fw_info *fw_data = hw->private_data; + + mutex_lock(&fw_data->lock); + /* clear all the calibrations */ + memset(fw_data->q6afecal_state, 0, + sizeof(fw_data->q6afecal_state)); + mutex_unlock(&fw_data->lock); + return 0; +} + +/** + * q6afe_cal_create_hwdep - + * for creating HW dependent node for AFE + * + * @data: Pointer to hold fw data + * @node: node type + * @card: Pointer to sound card + * + */ +int q6afe_cal_create_hwdep(void *data, int node, void *card) +{ + char hwname[AFE_HW_NAME_LENGTH]; + struct snd_hwdep *hwdep; + struct firmware_cal **fw; + struct afe_fw_info *fw_data = data; + int err, cal_bit; + + if (!fw_data || !card) { + pr_err("%s: Invalid parameters\n", __func__); + return -EINVAL; + } + + fw = fw_data->fw; + snprintf(hwname, strlen("Q6AFE"), "Q6AFE"); + err = snd_hwdep_new(((struct snd_soc_card *)card)->snd_card, + hwname, node, &hwdep); + if (err < 0) { + pr_err("%s: new hwdep for q6afe failed %d\n", __func__, err); + return err; + } + snprintf(hwdep->name, strlen("Q6AFECAL"), "Q6AFECAL"); + hwdep->iface = SNDRV_HWDEP_IFACE_AUDIO_BE; + hwdep->private_data = fw_data; + hwdep->ops.ioctl_compat = q6afecal_hwdep_ioctl_compat; + hwdep->ops.ioctl = q6afecal_hwdep_ioctl; + hwdep->ops.release = q6afecal_hwdep_release; + mutex_init(&fw_data->lock); + + for_each_set_bit(cal_bit, fw_data->cal_bit, Q6AFE_MAX_CAL) { + set_bit(Q6AFECAL_UNINITIALISED, + &fw_data->q6afecal_state[cal_bit]); + fw[cal_bit] = kzalloc(sizeof *(fw[cal_bit]), GFP_KERNEL); + if (!fw[cal_bit]) { + pr_err("%s: no memory for %s cal\n", + __func__, cal_name_info[cal_bit]); + goto end; + } + } + for_each_set_bit(cal_bit, fw_data->cal_bit, Q6AFE_MAX_CAL) { + fw[cal_bit]->data = kzalloc(cal_size_info[cal_bit], + GFP_KERNEL); + if (!fw[cal_bit]->data) + goto exit; + set_bit(Q6AFECAL_INITIALISED, + &fw_data->q6afecal_state[cal_bit]); + } + return 0; +exit: + for_each_set_bit(cal_bit, fw_data->cal_bit, Q6AFE_MAX_CAL) { + kfree(fw[cal_bit]->data); + fw[cal_bit]->data = NULL; + } +end: + for_each_set_bit(cal_bit, fw_data->cal_bit, Q6AFE_MAX_CAL) { + kfree(fw[cal_bit]); + fw[cal_bit] = NULL; + } + return -ENOMEM; +} +EXPORT_SYMBOL(q6afe_cal_create_hwdep); diff --git a/audio-kernel/dsp/q6afecal-hwdep.h b/audio-kernel/dsp/q6afecal-hwdep.h new file mode 100644 index 000000000..7e6efdac5 --- /dev/null +++ b/audio-kernel/dsp/q6afecal-hwdep.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2014,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 __Q6AFECAL_HWDEP_H__ +#define __Q6AFECAL_HWDEP_H__ +#include + +enum q6afe_cal_states { + Q6AFECAL_UNINITIALISED, + Q6AFECAL_INITIALISED, + Q6AFECAL_RECEIVED +}; + +struct afe_fw_info { + struct firmware_cal *fw[Q6AFE_MAX_CAL]; + DECLARE_BITMAP(cal_bit, Q6AFE_MAX_CAL); + /* for calibration tracking */ + unsigned long q6afecal_state[Q6AFE_MAX_CAL]; + struct mutex lock; +}; + +struct firmware_cal { + u8 *data; + size_t size; +}; + +#if IS_ENABLED(CONFIG_AFE_HWDEP) +int q6afe_cal_create_hwdep(void *fw, int node, void *card); +struct firmware_cal *q6afecal_get_fw_cal(struct afe_fw_info *fw_data, + enum q6afe_cal_type type); +#else /* CONFIG_AFE_HWDEP */ +static inline int q6afe_cal_create_hwdep(void *fw, int node, void *card) +{ + return 0; +} + +static inline struct firmware_cal *q6afecal_get_fw_cal( + struct afe_fw_info *fw_data, + enum q6afe_cal_type type) +{ + return NULL; +} +#endif /* CONFIG_AFE_HWDEP */ +#endif /* __Q6AFECAL_HWDEP_H__ */ diff --git a/audio-kernel/dsp/q6asm.c b/audio-kernel/dsp/q6asm.c new file mode 100644 index 000000000..e24300016 --- /dev/null +++ b/audio-kernel/dsp/q6asm.c @@ -0,0 +1,11136 @@ +/* + * Copyright (c) 2012-2019, The Linux Foundation. All rights reserved. + * Author: Brian Swetland + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include "adsp_err.h" + +#define TIMEOUT_MS 1000 +#define TRUE 0x01 +#define FALSE 0x00 +#define SESSION_MAX 8 + +#define ENC_FRAMES_PER_BUFFER 0x01 + +enum { + ASM_TOPOLOGY_CAL = 0, + ASM_CUSTOM_TOP_CAL, + ASM_AUDSTRM_CAL, + ASM_RTAC_APR_CAL, + ASM_MAX_CAL_TYPES +}; + +union asm_token_struct { + struct { + u8 stream_id; + u8 session_id; + u8 buf_index; + u8 flags; + } _token; + u32 token; +} __packed; + + +enum { + ASM_DIRECTION_OFFSET, + ASM_CMD_NO_WAIT_OFFSET, + /* + * Offset is limited to 7 because flags is stored in u8 + * field in asm_token_structure defined above. The offset + * starts from 0. + */ + ASM_MAX_OFFSET = 7, +}; + +enum { + WAIT_CMD, + NO_WAIT_CMD +}; + +#define ASM_SET_BIT(n, x) (n |= 1 << x) +#define ASM_TEST_BIT(n, x) ((n >> x) & 1) + +/* TODO, combine them together */ +static DEFINE_MUTEX(session_lock); +struct asm_mmap { + atomic_t ref_cnt; + void *apr; +}; + +static struct asm_mmap this_mmap; + +struct audio_session { + struct audio_client *ac; + spinlock_t session_lock; + struct mutex mutex_lock_per_session; +}; +/* session id: 0 reserved */ +static struct audio_session session[ASM_ACTIVE_STREAMS_ALLOWED + 1]; + +struct asm_buffer_node { + struct list_head list; + phys_addr_t buf_phys_addr; + uint32_t mmap_hdl; +}; +static int32_t q6asm_srvc_callback(struct apr_client_data *data, void *priv); +static int32_t q6asm_callback(struct apr_client_data *data, void *priv); +static void q6asm_add_hdr(struct audio_client *ac, struct apr_hdr *hdr, + uint32_t pkt_size, uint32_t cmd_flg); +static void q6asm_add_hdr_custom_topology(struct audio_client *ac, + struct apr_hdr *hdr, + uint32_t pkt_size); +static void q6asm_add_hdr_async(struct audio_client *ac, struct apr_hdr *hdr, + uint32_t pkt_size, uint32_t cmd_flg); +static int q6asm_memory_map_regions(struct audio_client *ac, int dir, + uint32_t bufsz, uint32_t bufcnt, + bool is_contiguous); +static int q6asm_memory_unmap_regions(struct audio_client *ac, int dir); +static void q6asm_reset_buf_state(struct audio_client *ac); + +static int q6asm_map_channels(u8 *channel_mapping, uint32_t channels, + bool use_back_flavor); +void *q6asm_mmap_apr_reg(void); + +static int q6asm_is_valid_session(struct apr_client_data *data, void *priv); +static int q6asm_get_asm_topology_apptype(struct q6asm_cal_info *cal_info); + +/* for ASM custom topology */ +static struct cal_type_data *cal_data[ASM_MAX_CAL_TYPES]; +static struct audio_buffer common_buf[2]; +static struct audio_client common_client; +static int set_custom_topology; +static int topology_map_handle; + +struct generic_get_data_ { + int valid; + int is_inband; + int size_in_ints; + int ints[]; +}; +static struct generic_get_data_ *generic_get_data; + +#ifdef CONFIG_DEBUG_FS +#define OUT_BUFFER_SIZE 56 +#define IN_BUFFER_SIZE 24 + +static struct timeval out_cold_tv; +static struct timeval out_warm_tv; +static struct timeval out_cont_tv; +static struct timeval in_cont_tv; +static long out_enable_flag; +static long in_enable_flag; +static struct dentry *out_dentry; +static struct dentry *in_dentry; +static int in_cont_index; +/*This var is used to keep track of first write done for cold output latency */ +static int out_cold_index; +static char *out_buffer; +static char *in_buffer; + +static uint32_t adsp_reg_event_opcode[] = { + ASM_STREAM_CMD_REGISTER_PP_EVENTS, + ASM_STREAM_CMD_REGISTER_ENCDEC_EVENTS, + ASM_STREAM_CMD_REGISTER_IEC_61937_FMT_UPDATE }; + +static uint32_t adsp_raise_event_opcode[] = { + ASM_STREAM_PP_EVENT, + ASM_STREAM_CMD_ENCDEC_EVENTS, + ASM_IEC_61937_MEDIA_FMT_EVENT }; + +static int is_adsp_reg_event(uint32_t cmd) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(adsp_reg_event_opcode); i++) { + if (cmd == adsp_reg_event_opcode[i]) + return i; + } + return -EINVAL; +} + +static int is_adsp_raise_event(uint32_t cmd) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(adsp_raise_event_opcode); i++) { + if (cmd == adsp_raise_event_opcode[i]) + return i; + } + return -EINVAL; +} + +static inline void q6asm_set_flag_in_token(union asm_token_struct *asm_token, + int flag, int flag_offset) +{ + if (flag) + ASM_SET_BIT(asm_token->_token.flags, flag_offset); +} + +static inline int q6asm_get_flag_from_token(union asm_token_struct *asm_token, + int flag_offset) +{ + return ASM_TEST_BIT(asm_token->_token.flags, flag_offset); +} + +static inline void q6asm_update_token(u32 *token, u8 session_id, u8 stream_id, + u8 buf_index, u8 dir, u8 nowait_flag) +{ + union asm_token_struct asm_token; + + asm_token.token = 0; + asm_token._token.session_id = session_id; + asm_token._token.stream_id = stream_id; + asm_token._token.buf_index = buf_index; + q6asm_set_flag_in_token(&asm_token, dir, ASM_DIRECTION_OFFSET); + q6asm_set_flag_in_token(&asm_token, nowait_flag, + ASM_CMD_NO_WAIT_OFFSET); + *token = asm_token.token; +} + +static inline uint32_t q6asm_get_pcm_format_id(uint32_t media_format_block_ver) +{ + uint32_t pcm_format_id; + + switch (media_format_block_ver) { + case PCM_MEDIA_FORMAT_V5: + pcm_format_id = ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V5; + break; + case PCM_MEDIA_FORMAT_V4: + pcm_format_id = ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V4; + break; + case PCM_MEDIA_FORMAT_V3: + pcm_format_id = ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V3; + break; + case PCM_MEDIA_FORMAT_V2: + default: + pcm_format_id = ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V2; + break; + } + return pcm_format_id; +} + +/* + * q6asm_get_buf_index_from_token: + * Retrieve buffer index from token. + * + * @token: token value sent to ASM service on q6. + * Returns buffer index in the read/write commands. + */ +uint8_t q6asm_get_buf_index_from_token(uint32_t token) +{ + union asm_token_struct asm_token; + + asm_token.token = token; + return asm_token._token.buf_index; +} +EXPORT_SYMBOL(q6asm_get_buf_index_from_token); + +/* + * q6asm_get_stream_id_from_token: + * Retrieve stream id from token. + * + * @token: token value sent to ASM service on q6. + * Returns stream id. + */ +uint8_t q6asm_get_stream_id_from_token(uint32_t token) +{ + union asm_token_struct asm_token; + + asm_token.token = token; + return asm_token._token.stream_id; +} +EXPORT_SYMBOL(q6asm_get_stream_id_from_token); + +static int audio_output_latency_dbgfs_open(struct inode *inode, + struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} +static ssize_t audio_output_latency_dbgfs_read(struct file *file, + char __user *buf, size_t count, loff_t *ppos) +{ + if (out_buffer == NULL) { + pr_err("%s: out_buffer is null\n", __func__); + return 0; + } + if (count < OUT_BUFFER_SIZE) { + pr_err("%s: read size %d exceeds buf size %zd\n", __func__, + OUT_BUFFER_SIZE, count); + return 0; + } + snprintf(out_buffer, OUT_BUFFER_SIZE, "%ld,%ld,%ld,%ld,%ld,%ld,", + out_cold_tv.tv_sec, out_cold_tv.tv_usec, out_warm_tv.tv_sec, + out_warm_tv.tv_usec, out_cont_tv.tv_sec, out_cont_tv.tv_usec); + return simple_read_from_buffer(buf, OUT_BUFFER_SIZE, ppos, + out_buffer, OUT_BUFFER_SIZE); +} +static ssize_t audio_output_latency_dbgfs_write(struct file *file, + const char __user *buf, size_t count, loff_t *ppos) +{ + char *temp; + + if (count > 2*sizeof(char)) { + pr_err("%s: err count is more %zd\n", __func__, count); + return -EINVAL; + } + temp = kmalloc(2*sizeof(char), GFP_KERNEL); + + out_cold_index = 0; + + if (temp) { + if (copy_from_user(temp, buf, 2*sizeof(char))) { + pr_err("%s: copy from user failed for size %zd\n", + __func__, 2*sizeof(char)); + kfree(temp); + return -EFAULT; + } + if (!kstrtol(temp, 10, &out_enable_flag)) { + kfree(temp); + return count; + } + kfree(temp); + } + return -EINVAL; +} +static const struct file_operations audio_output_latency_debug_fops = { + .open = audio_output_latency_dbgfs_open, + .read = audio_output_latency_dbgfs_read, + .write = audio_output_latency_dbgfs_write +}; +static int audio_input_latency_dbgfs_open(struct inode *inode, + struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} +static ssize_t audio_input_latency_dbgfs_read(struct file *file, + char __user *buf, size_t count, loff_t *ppos) +{ + if (in_buffer == NULL) { + pr_err("%s: in_buffer is null\n", __func__); + return 0; + } + if (count < IN_BUFFER_SIZE) { + pr_err("%s: read size %d exceeds buf size %zd\n", __func__, + IN_BUFFER_SIZE, count); + return 0; + } + snprintf(in_buffer, IN_BUFFER_SIZE, "%ld,%ld,", + in_cont_tv.tv_sec, in_cont_tv.tv_usec); + return simple_read_from_buffer(buf, IN_BUFFER_SIZE, ppos, + in_buffer, IN_BUFFER_SIZE); +} +static ssize_t audio_input_latency_dbgfs_write(struct file *file, + const char __user *buf, size_t count, loff_t *ppos) +{ + char *temp; + + if (count > 2*sizeof(char)) { + pr_err("%s: err count is more %zd\n", __func__, count); + return -EINVAL; + } + temp = kmalloc(2*sizeof(char), GFP_KERNEL); + + if (temp) { + if (copy_from_user(temp, buf, 2*sizeof(char))) { + pr_err("%s: copy from user failed for size %zd\n", + __func__, 2*sizeof(char)); + kfree(temp); + return -EFAULT; + } + if (!kstrtol(temp, 10, &in_enable_flag)) { + kfree(temp); + return count; + } + kfree(temp); + } + return -EINVAL; +} +static const struct file_operations audio_input_latency_debug_fops = { + .open = audio_input_latency_dbgfs_open, + .read = audio_input_latency_dbgfs_read, + .write = audio_input_latency_dbgfs_write +}; + +static void config_debug_fs_write_cb(void) +{ + if (out_enable_flag) { + /* For first Write done log the time and reset + * out_cold_index + */ + if (out_cold_index != 1) { + do_gettimeofday(&out_cold_tv); + pr_debug("COLD: apr_send_pkt at %ld sec %ld microsec\n", + out_cold_tv.tv_sec, + out_cold_tv.tv_usec); + out_cold_index = 1; + } + pr_debug("%s: out_enable_flag %ld\n", + __func__, out_enable_flag); + } +} +static void config_debug_fs_read_cb(void) +{ + if (in_enable_flag) { + /* when in_cont_index == 7, DSP would be + * writing into the 8th 512 byte buffer and this + * timestamp is tapped here.Once done it then writes + * to 9th 512 byte buffer.These two buffers(8th, 9th) + * reach the test application in 5th iteration and that + * timestamp is tapped at user level. The difference + * of these two timestamps gives us the time between + * the time at which dsp started filling the sample + * required and when it reached the test application. + * Hence continuous input latency + */ + if (in_cont_index == 7) { + do_gettimeofday(&in_cont_tv); + pr_info("%s: read buffer at %ld sec %ld microsec\n", + __func__, + in_cont_tv.tv_sec, in_cont_tv.tv_usec); + } + in_cont_index++; + } +} + +static void config_debug_fs_reset_index(void) +{ + in_cont_index = 0; +} + +static void config_debug_fs_run(void) +{ + if (out_enable_flag) { + do_gettimeofday(&out_cold_tv); + pr_debug("%s: COLD apr_send_pkt at %ld sec %ld microsec\n", + __func__, out_cold_tv.tv_sec, out_cold_tv.tv_usec); + } +} + +static void config_debug_fs_write(struct audio_buffer *ab) +{ + if (out_enable_flag) { + char zero_pattern[2] = {0x00, 0x00}; + /* If First two byte is non zero and last two byte + * is zero then it is warm output pattern + */ + if ((strcmp(((char *)ab->data), zero_pattern)) && + (!strcmp(((char *)ab->data + 2), zero_pattern))) { + do_gettimeofday(&out_warm_tv); + pr_debug("%s: WARM:apr_send_pkt at %ld sec %ld microsec\n", + __func__, + out_warm_tv.tv_sec, + out_warm_tv.tv_usec); + pr_debug("%s: Warm Pattern Matched\n", __func__); + } + /* If First two byte is zero and last two byte is + * non zero then it is cont output pattern + */ + else if ((!strcmp(((char *)ab->data), zero_pattern)) + && (strcmp(((char *)ab->data + 2), zero_pattern))) { + do_gettimeofday(&out_cont_tv); + pr_debug("%s: CONT:apr_send_pkt at %ld sec %ld microsec\n", + __func__, + out_cont_tv.tv_sec, + out_cont_tv.tv_usec); + pr_debug("%s: Cont Pattern Matched\n", __func__); + } + } +} +static void config_debug_fs_init(void) +{ + out_buffer = kzalloc(OUT_BUFFER_SIZE, GFP_KERNEL); + if (out_buffer == NULL) + goto outbuf_fail; + + in_buffer = kzalloc(IN_BUFFER_SIZE, GFP_KERNEL); + if (in_buffer == NULL) + goto inbuf_fail; + + out_dentry = debugfs_create_file("audio_out_latency_measurement_node", + 0664, + NULL, NULL, &audio_output_latency_debug_fops); + if (IS_ERR(out_dentry)) { + pr_err("%s: debugfs_create_file failed\n", __func__); + goto file_fail; + } + in_dentry = debugfs_create_file("audio_in_latency_measurement_node", + 0664, + NULL, NULL, &audio_input_latency_debug_fops); + if (IS_ERR(in_dentry)) { + pr_err("%s: debugfs_create_file failed\n", __func__); + goto file_fail; + } + return; +file_fail: + kfree(in_buffer); +inbuf_fail: + kfree(out_buffer); +outbuf_fail: + in_buffer = NULL; + out_buffer = NULL; +} +#else +static void config_debug_fs_write(struct audio_buffer *ab) +{ +} +static void config_debug_fs_run(void) +{ +} +static void config_debug_fs_reset_index(void) +{ +} +static void config_debug_fs_read_cb(void) +{ +} +static void config_debug_fs_write_cb(void) +{ +} +static void config_debug_fs_init(void) +{ +} +#endif + +int q6asm_mmap_apr_dereg(void) +{ + int c; + + c = atomic_sub_return(1, &this_mmap.ref_cnt); + if (c == 0) { + apr_deregister(this_mmap.apr); + common_client.mmap_apr = NULL; + pr_debug("%s: APR De-Register common port\n", __func__); + } else if (c < 0) { + pr_err("%s: APR Common Port Already Closed %d\n", + __func__, c); + atomic_set(&this_mmap.ref_cnt, 0); + } + + return 0; +} + +static int q6asm_session_alloc(struct audio_client *ac) +{ + int n; + + for (n = 1; n <= ASM_ACTIVE_STREAMS_ALLOWED; n++) { + if (!(session[n].ac)) { + session[n].ac = ac; + return n; + } + } + pr_err("%s: session not available\n", __func__); + return -ENOMEM; +} + +static int q6asm_get_session_id_from_audio_client(struct audio_client *ac) +{ + int n; + + for (n = 1; n <= ASM_ACTIVE_STREAMS_ALLOWED; n++) { + if (session[n].ac == ac) + return n; + } + + pr_debug("%s: cannot find matching audio client. ac = %pK\n", + __func__, ac); + + return 0; +} + +static bool q6asm_is_valid_audio_client(struct audio_client *ac) +{ + return q6asm_get_session_id_from_audio_client(ac) ? 1 : 0; +} + +static void q6asm_session_free(struct audio_client *ac) +{ + int session_id; + unsigned long flags = 0; + + pr_debug("%s: sessionid[%d]\n", __func__, ac->session); + session_id = ac->session; + mutex_lock(&session[session_id].mutex_lock_per_session); + rtac_remove_popp_from_adm_devices(ac->session); + spin_lock_irqsave(&(session[session_id].session_lock), flags); + session[ac->session].ac = NULL; + ac->session = 0; + ac->perf_mode = LEGACY_PCM_MODE; + ac->fptr_cache_ops = NULL; + ac->cb = NULL; + ac->priv = NULL; + kfree(ac); + ac = NULL; + spin_unlock_irqrestore(&(session[session_id].session_lock), flags); + mutex_unlock(&session[session_id].mutex_lock_per_session); +} + +static uint32_t q6asm_get_next_buf(struct audio_client *ac, + uint32_t curr_buf, uint32_t max_buf_cnt) +{ + dev_vdbg(ac->dev, "%s: curr_buf = %d, max_buf_cnt = %d\n", + __func__, curr_buf, max_buf_cnt); + curr_buf += 1; + return (curr_buf >= max_buf_cnt) ? 0 : curr_buf; +} + +static int q6asm_map_cal_memory(int32_t cal_type, + struct cal_block_data *cal_block) +{ + int result = 0; + struct asm_buffer_node *buf_node = NULL; + struct list_head *ptr, *next; + + if (cal_block == NULL) { + pr_err("%s: cal_block is NULL!\n", + __func__); + goto done; + } + + if (cal_block->cal_data.paddr == 0) { + pr_debug("%s: No address to map!\n", + __func__); + goto done; + } + + common_client.mmap_apr = q6asm_mmap_apr_reg(); + if (common_client.mmap_apr == NULL) { + pr_err("%s: q6asm_mmap_apr_reg failed\n", + __func__); + result = -EPERM; + goto done; + } + common_client.apr = common_client.mmap_apr; + if (cal_block->map_data.map_size == 0) { + pr_debug("%s: map size is 0!\n", + __func__); + goto done; + } + + /* Use second asm buf to map memory */ + if (common_client.port[IN].buf == NULL) { + pr_err("%s: common buf is NULL\n", + __func__); + result = -EINVAL; + goto done; + } + + common_client.port[IN].buf->phys = cal_block->cal_data.paddr; + + result = q6asm_memory_map_regions(&common_client, + IN, cal_block->map_data.map_size, 1, 1); + if (result < 0) { + pr_err("%s: mmap did not work! size = %zd result %d\n", + __func__, + cal_block->map_data.map_size, result); + pr_debug("%s: mmap did not work! addr = 0x%pK, size = %zd\n", + __func__, + &cal_block->cal_data.paddr, + cal_block->map_data.map_size); + goto done; + } + + list_for_each_safe(ptr, next, + &common_client.port[IN].mem_map_handle) { + buf_node = list_entry(ptr, struct asm_buffer_node, + list); + if (buf_node->buf_phys_addr == cal_block->cal_data.paddr) { + cal_block->map_data.q6map_handle = buf_node->mmap_hdl; + break; + } + } +done: + return result; +} + +static int remap_cal_data(int32_t cal_type, struct cal_block_data *cal_block) +{ + int ret = 0; + + if (cal_block->map_data.dma_buf == NULL) { + pr_err("%s: No ION allocation for cal type %d!\n", + __func__, cal_type); + ret = -EINVAL; + goto done; + } + + if ((cal_block->map_data.map_size > 0) && + (cal_block->map_data.q6map_handle == 0)) { + + ret = q6asm_map_cal_memory(cal_type, cal_block); + if (ret < 0) { + pr_err("%s: mmap did not work! size = %zd ret %d\n", + __func__, cal_block->map_data.map_size, ret); + goto done; + } + } +done: + return ret; +} + +static int q6asm_unmap_cal_memory(int32_t cal_type, + struct cal_block_data *cal_block) +{ + int result = 0; + int result2 = 0; + + if (cal_block == NULL) { + pr_err("%s: cal_block is NULL!\n", + __func__); + result = -EINVAL; + goto done; + } + + if (cal_block->map_data.q6map_handle == 0) { + pr_debug("%s: No address to unmap!\n", + __func__); + result = -EINVAL; + goto done; + } + + if (common_client.mmap_apr == NULL) { + common_client.mmap_apr = q6asm_mmap_apr_reg(); + if (common_client.mmap_apr == NULL) { + pr_err("%s: q6asm_mmap_apr_reg failed\n", + __func__); + result = -EPERM; + goto done; + } + } + + result2 = q6asm_memory_unmap_regions(&common_client, IN); + if (result2 < 0) { + pr_err("%s: unmap failed, err %d\n", + __func__, result2); + result = result2; + } + + cal_block->map_data.q6map_handle = 0; +done: + return result; +} + +int q6asm_unmap_cal_data(int cal_type, struct cal_block_data *cal_block) +{ + int ret = 0; + + if ((cal_block->map_data.map_size > 0) && + (cal_block->map_data.q6map_handle != 0)) { + + ret = q6asm_unmap_cal_memory(cal_type, cal_block); + if (ret < 0) { + pr_err("%s: unmap did not work! size = %zd ret %d\n", + __func__, cal_block->map_data.map_size, ret); + goto done; + } + } +done: + return ret; +} + +int send_asm_custom_topology(struct audio_client *ac) +{ + struct cal_block_data *cal_block = NULL; + struct cmd_set_topologies asm_top; + int result = 0; + int result1 = 0; + + if (cal_data[ASM_CUSTOM_TOP_CAL] == NULL) + goto done; + + mutex_lock(&cal_data[ASM_CUSTOM_TOP_CAL]->lock); + if (!set_custom_topology) + goto unlock; + set_custom_topology = 0; + + cal_block = cal_utils_get_only_cal_block(cal_data[ASM_CUSTOM_TOP_CAL]); + if (cal_block == NULL || cal_utils_is_cal_stale(cal_block)) + goto unlock; + + if (cal_block->cal_data.size == 0) { + pr_debug("%s: No cal to send!\n", __func__); + goto unlock; + } + + pr_debug("%s: Sending cal_index %d\n", __func__, ASM_CUSTOM_TOP_CAL); + + result = remap_cal_data(ASM_CUST_TOPOLOGY_CAL_TYPE, cal_block); + if (result) { + pr_err("%s: Remap_cal_data failed for cal %d!\n", + __func__, ASM_CUSTOM_TOP_CAL); + goto unlock; + } + + q6asm_add_hdr_custom_topology(ac, &asm_top.hdr, sizeof(asm_top)); + atomic_set(&ac->mem_state, -1); + asm_top.hdr.opcode = ASM_CMD_ADD_TOPOLOGIES; + asm_top.payload_addr_lsw = lower_32_bits(cal_block->cal_data.paddr); + asm_top.payload_addr_msw = msm_audio_populate_upper_32_bits( + cal_block->cal_data.paddr); + asm_top.mem_map_handle = cal_block->map_data.q6map_handle; + asm_top.payload_size = cal_block->cal_data.size; + + pr_debug("%s: Sending ASM_CMD_ADD_TOPOLOGIES payload = %pK, size = %d, map handle = 0x%x\n", + __func__, &cal_block->cal_data.paddr, + asm_top.payload_size, asm_top.mem_map_handle); + + result = apr_send_pkt(ac->apr, (uint32_t *) &asm_top); + if (result < 0) { + pr_err("%s: Set topologies failed result %d\n", + __func__, result); + pr_debug("%s: Set topologies failed payload = 0x%pK\n", + __func__, &cal_block->cal_data.paddr); + goto unmap; + + } + + result = wait_event_timeout(ac->mem_wait, + (atomic_read(&ac->mem_state) >= 0), + msecs_to_jiffies(TIMEOUT_MS)); + if (!result) { + pr_err("%s: Set topologies failed timeout\n", __func__); + pr_debug("%s: Set topologies failed after timedout payload = 0x%pK\n", + __func__, &cal_block->cal_data.paddr); + result = -ETIMEDOUT; + goto unmap; + } + if (atomic_read(&ac->mem_state) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->mem_state))); + result = adsp_err_get_lnx_err_code( + atomic_read(&ac->mem_state)); + goto unmap; + } + +unmap: + result1 = q6asm_unmap_cal_memory(ASM_CUST_TOPOLOGY_CAL_TYPE, + cal_block); + if (result1 < 0) { + result = result1; + pr_debug("%s: unmap cal failed! %d\n", __func__, result); + } +unlock: + mutex_unlock(&cal_data[ASM_CUSTOM_TOP_CAL]->lock); +done: + return result; +} + +int q6asm_map_rtac_block(struct rtac_cal_block_data *cal_block) +{ + int result = 0; + struct asm_buffer_node *buf_node = NULL; + struct list_head *ptr, *next; + + pr_debug("%s:\n", __func__); + + if (cal_block == NULL) { + pr_err("%s: cal_block is NULL!\n", + __func__); + result = -EINVAL; + goto done; + } + + if (cal_block->cal_data.paddr == 0) { + pr_debug("%s: No address to map!\n", + __func__); + result = -EINVAL; + goto done; + } + + if (common_client.mmap_apr == NULL) { + common_client.mmap_apr = q6asm_mmap_apr_reg(); + if (common_client.mmap_apr == NULL) { + pr_err("%s: q6asm_mmap_apr_reg failed\n", + __func__); + result = -EPERM; + goto done; + } + } + + if (cal_block->map_data.map_size == 0) { + pr_debug("%s: map size is 0!\n", + __func__); + result = -EINVAL; + goto done; + } + + /* Use second asm buf to map memory */ + if (common_client.port[OUT].buf == NULL) { + pr_err("%s: common buf is NULL\n", + __func__); + result = -EINVAL; + goto done; + } + + common_client.port[OUT].buf->phys = cal_block->cal_data.paddr; + + result = q6asm_memory_map_regions(&common_client, + OUT, cal_block->map_data.map_size, 1, 1); + if (result < 0) { + pr_err("%s: mmap did not work! size = %d result %d\n", + __func__, + cal_block->map_data.map_size, result); + pr_debug("%s: mmap did not work! addr = 0x%pK, size = %d\n", + __func__, + &cal_block->cal_data.paddr, + cal_block->map_data.map_size); + goto done; + } + + list_for_each_safe(ptr, next, + &common_client.port[OUT].mem_map_handle) { + buf_node = list_entry(ptr, struct asm_buffer_node, + list); + if (buf_node->buf_phys_addr == cal_block->cal_data.paddr) { + cal_block->map_data.map_handle = buf_node->mmap_hdl; + break; + } + } +done: + return result; +} + +int q6asm_unmap_rtac_block(uint32_t *mem_map_handle) +{ + int result = 0; + int result2 = 0; + + pr_debug("%s:\n", __func__); + + if (mem_map_handle == NULL) { + pr_debug("%s: Map handle is NULL, nothing to unmap\n", + __func__); + goto done; + } + + if (*mem_map_handle == 0) { + pr_debug("%s: Map handle is 0, nothing to unmap\n", + __func__); + goto done; + } + + if (common_client.mmap_apr == NULL) { + common_client.mmap_apr = q6asm_mmap_apr_reg(); + if (common_client.mmap_apr == NULL) { + pr_err("%s: q6asm_mmap_apr_reg failed\n", + __func__); + result = -EPERM; + goto done; + } + } + + + result2 = q6asm_memory_unmap_regions(&common_client, OUT); + if (result2 < 0) { + pr_err("%s: unmap failed, err %d\n", + __func__, result2); + result = result2; + } else { + *mem_map_handle = 0; + } + + result2 = q6asm_mmap_apr_dereg(); + if (result2 < 0) { + pr_err("%s: q6asm_mmap_apr_dereg failed, err %d\n", + __func__, result2); + result = result2; + } +done: + return result; +} + +int q6asm_audio_client_buf_free(unsigned int dir, + struct audio_client *ac) +{ + struct audio_port_data *port; + int cnt = 0; + int rc = 0; + + pr_debug("%s: Session id %d\n", __func__, ac->session); + mutex_lock(&ac->cmd_lock); + if (ac->io_mode & SYNC_IO_MODE) { + port = &ac->port[dir]; + if (!port->buf) { + pr_err("%s: buf NULL\n", __func__); + mutex_unlock(&ac->cmd_lock); + return 0; + } + cnt = port->max_buf_cnt - 1; + + if (cnt >= 0) { + rc = q6asm_memory_unmap_regions(ac, dir); + if (rc < 0) + pr_err("%s: Memory_unmap_regions failed %d\n", + __func__, rc); + } + + while (cnt >= 0) { + if (port->buf[cnt].data) { + if (!rc || atomic_read(&ac->reset)) + msm_audio_ion_free( + port->buf[cnt].dma_buf); + + port->buf[cnt].dma_buf = NULL; + port->buf[cnt].data = NULL; + port->buf[cnt].phys = 0; + --(port->max_buf_cnt); + } + --cnt; + } + kfree(port->buf); + port->buf = NULL; + } + mutex_unlock(&ac->cmd_lock); + return 0; +} + +/** + * q6asm_audio_client_buf_free_contiguous - + * frees the memory buffers for ASM + * + * @dir: RX or TX direction + * @ac: audio client handle + * + * Returns 0 on success or error on failure + */ +int q6asm_audio_client_buf_free_contiguous(unsigned int dir, + struct audio_client *ac) +{ + struct audio_port_data *port; + int cnt = 0; + int rc = 0; + + pr_debug("%s: Session id %d\n", __func__, ac->session); + mutex_lock(&ac->cmd_lock); + port = &ac->port[dir]; + if (!port->buf) { + mutex_unlock(&ac->cmd_lock); + return 0; + } + cnt = port->max_buf_cnt - 1; + + if (cnt >= 0) { + rc = q6asm_memory_unmap(ac, port->buf[0].phys, dir); + if (rc < 0) + pr_err("%s: Memory_unmap_regions failed %d\n", + __func__, rc); + } + + if (port->buf[0].data) { + pr_debug("%s: data[%pK], phys[%pK], dma_buf[%pK]\n", + __func__, + port->buf[0].data, + &port->buf[0].phys, + port->buf[0].dma_buf); + if (!rc || atomic_read(&ac->reset)) + msm_audio_ion_free(port->buf[0].dma_buf); + port->buf[0].dma_buf = NULL; + } + + while (cnt >= 0) { + port->buf[cnt].data = NULL; + port->buf[cnt].phys = 0; + cnt--; + } + port->max_buf_cnt = 0; + kfree(port->buf); + port->buf = NULL; + mutex_unlock(&ac->cmd_lock); + return 0; +} +EXPORT_SYMBOL(q6asm_audio_client_buf_free_contiguous); + +/** + * q6asm_audio_client_free - + * frees the audio client for ASM + * + * @ac: audio client handle + * + */ +void q6asm_audio_client_free(struct audio_client *ac) +{ + int loopcnt; + struct audio_port_data *port; + + if (!ac) { + pr_err("%s: ac %pK\n", __func__, ac); + return; + } + if (!ac->session) { + pr_err("%s: ac session invalid\n", __func__); + return; + } + + mutex_lock(&session_lock); + + pr_debug("%s: Session id %d\n", __func__, ac->session); + if (ac->io_mode & SYNC_IO_MODE) { + for (loopcnt = 0; loopcnt <= OUT; loopcnt++) { + port = &ac->port[loopcnt]; + if (!port->buf) + continue; + pr_debug("%s: loopcnt = %d\n", + __func__, loopcnt); + q6asm_audio_client_buf_free(loopcnt, ac); + } + } + + rtac_set_asm_handle(ac->session, NULL); + apr_deregister(ac->apr2); + apr_deregister(ac->apr); + q6asm_mmap_apr_dereg(); + ac->apr2 = NULL; + ac->apr = NULL; + ac->mmap_apr = NULL; + q6asm_session_free(ac); + + pr_debug("%s: APR De-Register\n", __func__); + +/*done:*/ + mutex_unlock(&session_lock); +} +EXPORT_SYMBOL(q6asm_audio_client_free); + +/** + * q6asm_set_io_mode - + * Update IO mode for ASM + * + * @ac: audio client handle + * @mode1: IO mode to update + * + * Returns 0 on success or error on failure + */ +int q6asm_set_io_mode(struct audio_client *ac, uint32_t mode1) +{ + uint32_t mode; + int ret = 0; + + if (ac == NULL) { + pr_err("%s: APR handle NULL\n", __func__); + return -EINVAL; + } + + ac->io_mode &= 0xFF00; + mode = (mode1 & 0xF); + + pr_debug("%s: ac->mode after anding with FF00:0x%x,\n", + __func__, ac->io_mode); + + if ((mode == ASYNC_IO_MODE) || (mode == SYNC_IO_MODE)) { + ac->io_mode |= mode1; + pr_debug("%s: Set Mode to 0x%x\n", __func__, ac->io_mode); + } else { + pr_err("%s: Not an valid IO Mode:%d\n", __func__, ac->io_mode); + ret = -EINVAL; + } + + return ret; +} +EXPORT_SYMBOL(q6asm_set_io_mode); + +void *q6asm_mmap_apr_reg(void) +{ + if ((atomic_read(&this_mmap.ref_cnt) == 0) || + (this_mmap.apr == NULL)) { + this_mmap.apr = apr_register("ADSP", "ASM", + (apr_fn)q6asm_srvc_callback, + 0x0FFFFFFFF, &this_mmap); + if (this_mmap.apr == NULL) { + pr_debug("%s: Unable to register APR ASM common port\n", + __func__); + goto fail; + } + } + atomic_inc(&this_mmap.ref_cnt); + + return this_mmap.apr; +fail: + return NULL; +} + +/** + * q6asm_send_stream_cmd - + * command to send for ASM stream + * + * @ac: audio client handle + * @data: event data + * + * Returns 0 on success or error on failure + */ +int q6asm_send_stream_cmd(struct audio_client *ac, + struct msm_adsp_event_data *data) +{ + char *asm_params = NULL; + struct apr_hdr hdr; + int rc; + uint32_t sz = 0; + uint64_t actual_sz = 0; + int session_id = 0; + + if (!data || !ac) { + pr_err("%s: %s is NULL\n", __func__, + (!data) ? "data" : "ac"); + rc = -EINVAL; + goto done; + } + + session_id = q6asm_get_session_id_from_audio_client(ac); + if (!session_id) { + rc = -EINVAL; + goto done; + } + + if (data->event_type >= ARRAY_SIZE(adsp_reg_event_opcode)) { + pr_err("%s: event %u out of boundary of array size of (%lu)\n", + __func__, data->event_type, + (long)ARRAY_SIZE(adsp_reg_event_opcode)); + rc = -EINVAL; + goto done; + } + + actual_sz = sizeof(struct apr_hdr) + data->payload_len; + if (actual_sz > U32_MAX) { + pr_err("%s: payload size 0x%X exceeds limit\n", + __func__, data->payload_len); + rc = -EINVAL; + goto done; + } + + sz = (uint32_t)actual_sz; + asm_params = kzalloc(sz, GFP_KERNEL); + if (!asm_params) { + rc = -ENOMEM; + goto done; + } + + mutex_lock(&session[session_id].mutex_lock_per_session); + if (!q6asm_is_valid_audio_client(ac)) { + rc = -EINVAL; + goto fail_send_param; + } + + q6asm_add_hdr_async(ac, &hdr, sz, TRUE); + atomic_set(&ac->cmd_state_pp, -1); + hdr.opcode = adsp_reg_event_opcode[data->event_type]; + memcpy(asm_params, &hdr, sizeof(struct apr_hdr)); + memcpy(asm_params + sizeof(struct apr_hdr), + data->payload, data->payload_len); + rc = apr_send_pkt(ac->apr, (uint32_t *) asm_params); + if (rc < 0) { + pr_err("%s: stream event cmd apr pkt failed\n", __func__); + rc = -EINVAL; + goto fail_send_param; + } + + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state_pp) >= 0), + msecs_to_jiffies(TIMEOUT_MS)); + if (!rc) { + pr_err("%s: timeout for stream event cmd resp\n", __func__); + rc = -ETIMEDOUT; + goto fail_send_param; + } + + if (atomic_read(&ac->cmd_state_pp) > 0) { + pr_err("%s: DSP returned error[%s] for stream event cmd\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->cmd_state_pp))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state_pp)); + goto fail_send_param; + } + + rc = 0; +fail_send_param: + mutex_unlock(&session[session_id].mutex_lock_per_session); + kfree(asm_params); +done: + return rc; +} +EXPORT_SYMBOL(q6asm_send_stream_cmd); + +/** + * q6asm_audio_client_alloc - + * Alloc audio client for ASM + * + * @cb: callback fn + * @priv: private data + * + * Returns ac pointer on success or NULL on failure + */ +struct audio_client *q6asm_audio_client_alloc(app_cb cb, void *priv) +{ + struct audio_client *ac; + int n; + int lcnt = 0; + int rc = 0; + + ac = kzalloc(sizeof(struct audio_client), GFP_KERNEL); + if (!ac) + return NULL; + + mutex_lock(&session_lock); + n = q6asm_session_alloc(ac); + if (n <= 0) { + pr_err("%s: ASM Session alloc fail n=%d\n", __func__, n); + mutex_unlock(&session_lock); + kfree(ac); + goto fail_session; + } + ac->session = n; + ac->cb = cb; + ac->path_delay = UINT_MAX; + ac->priv = priv; + ac->io_mode = SYNC_IO_MODE; + ac->perf_mode = LEGACY_PCM_MODE; + ac->fptr_cache_ops = NULL; + /* DSP expects stream id from 1 */ + ac->stream_id = 1; + ac->apr = apr_register("ADSP", "ASM", + (apr_fn)q6asm_callback, + ((ac->session) << 8 | 0x0001), + ac); + + if (ac->apr == NULL) { + pr_err("%s: Registration with APR failed\n", __func__); + mutex_unlock(&session_lock); + goto fail_apr1; + } + ac->apr2 = apr_register("ADSP", "ASM", + (apr_fn)q6asm_callback, + ((ac->session) << 8 | 0x0002), + ac); + + if (ac->apr2 == NULL) { + pr_err("%s: Registration with APR-2 failed\n", __func__); + mutex_unlock(&session_lock); + goto fail_apr2; + } + + rtac_set_asm_handle(n, ac->apr); + + pr_debug("%s: Registering the common port with APR\n", __func__); + ac->mmap_apr = q6asm_mmap_apr_reg(); + if (ac->mmap_apr == NULL) { + mutex_unlock(&session_lock); + goto fail_mmap; + } + + init_waitqueue_head(&ac->cmd_wait); + init_waitqueue_head(&ac->time_wait); + init_waitqueue_head(&ac->mem_wait); + atomic_set(&ac->time_flag, 1); + atomic_set(&ac->reset, 0); + INIT_LIST_HEAD(&ac->port[0].mem_map_handle); + INIT_LIST_HEAD(&ac->port[1].mem_map_handle); + pr_debug("%s: mem_map_handle list init'ed\n", __func__); + mutex_init(&ac->cmd_lock); + for (lcnt = 0; lcnt <= OUT; lcnt++) { + mutex_init(&ac->port[lcnt].lock); + spin_lock_init(&ac->port[lcnt].dsp_lock); + } + atomic_set(&ac->cmd_state, 0); + atomic_set(&ac->cmd_state_pp, 0); + atomic_set(&ac->mem_state, 0); + + rc = send_asm_custom_topology(ac); + if (rc < 0) { + mutex_unlock(&session_lock); + goto fail_mmap; + } + + pr_debug("%s: session[%d]\n", __func__, ac->session); + + mutex_unlock(&session_lock); + + return ac; +fail_mmap: + apr_deregister(ac->apr2); +fail_apr2: + apr_deregister(ac->apr); +fail_apr1: + q6asm_session_free(ac); +fail_session: + return NULL; +} +EXPORT_SYMBOL(q6asm_audio_client_alloc); + +/** + * q6asm_get_audio_client - + * Retrieve audio client for ASM + * + * @session_id: ASM session id + * + * Returns valid pointer on success or NULL on failure + */ +struct audio_client *q6asm_get_audio_client(int session_id) +{ + if (session_id == ASM_CONTROL_SESSION) + return &common_client; + + if ((session_id <= 0) || (session_id > ASM_ACTIVE_STREAMS_ALLOWED)) { + pr_err("%s: invalid session: %d\n", __func__, session_id); + goto err; + } + + if (!(session[session_id].ac)) { + pr_err("%s: session not active: %d\n", __func__, session_id); + goto err; + } + return session[session_id].ac; +err: + return NULL; +} +EXPORT_SYMBOL(q6asm_get_audio_client); + +/** + * q6asm_audio_client_buf_alloc - + * Allocs memory from ION for ASM + * + * @dir: RX or TX direction + * @ac: Audio client handle + * @bufsz: size of each buffer + * @bufcnt: number of buffers to alloc + * + * Returns 0 on success or error on failure + */ +int q6asm_audio_client_buf_alloc(unsigned int dir, + struct audio_client *ac, + unsigned int bufsz, + uint32_t bufcnt) +{ + int cnt = 0; + int rc = 0; + struct audio_buffer *buf; + size_t len; + + if (!(ac) || !(bufsz) || ((dir != IN) && (dir != OUT))) { + pr_err("%s: ac %pK bufsz %d dir %d\n", __func__, ac, bufsz, + dir); + return -EINVAL; + } + + pr_debug("%s: session[%d]bufsz[%d]bufcnt[%d]\n", __func__, ac->session, + bufsz, bufcnt); + + if (ac->session <= 0 || ac->session > 8) { + pr_err("%s: Session ID is invalid, session = %d\n", __func__, + ac->session); + goto fail; + } + + if (ac->io_mode & SYNC_IO_MODE) { + if (ac->port[dir].buf) { + pr_debug("%s: buffer already allocated\n", __func__); + return 0; + } + mutex_lock(&ac->cmd_lock); + if (bufcnt > (U32_MAX/sizeof(struct audio_buffer))) { + pr_err("%s: Buffer size overflows", __func__); + mutex_unlock(&ac->cmd_lock); + goto fail; + } + buf = kzalloc(((sizeof(struct audio_buffer))*bufcnt), + GFP_KERNEL); + + if (!buf) { + mutex_unlock(&ac->cmd_lock); + goto fail; + } + + ac->port[dir].buf = buf; + + while (cnt < bufcnt) { + if (bufsz > 0) { + if (!buf[cnt].data) { + rc = msm_audio_ion_alloc( + &buf[cnt].dma_buf, + bufsz, + &buf[cnt].phys, + &len, + &buf[cnt].data); + if (rc) { + pr_err("%s: ION Get Physical for AUDIO failed, rc = %d\n", + __func__, rc); + mutex_unlock(&ac->cmd_lock); + goto fail; + } + + buf[cnt].used = 1; + buf[cnt].size = bufsz; + buf[cnt].actual_size = bufsz; + pr_debug("%s: data[%pK]phys[%pK][%pK]\n", + __func__, + buf[cnt].data, + &buf[cnt].phys, + &buf[cnt].phys); + cnt++; + } + } + } + ac->port[dir].max_buf_cnt = cnt; + + mutex_unlock(&ac->cmd_lock); + rc = q6asm_memory_map_regions(ac, dir, bufsz, cnt, 0); + if (rc < 0) { + pr_err("%s: CMD Memory_map_regions failed %d for size %d\n", + __func__, rc, bufsz); + goto fail; + } + } + return 0; +fail: + q6asm_audio_client_buf_free(dir, ac); + return -EINVAL; +} +EXPORT_SYMBOL(q6asm_audio_client_buf_alloc); + +/** + * q6asm_audio_client_buf_alloc_contiguous - + * Alloc contiguous memory from ION for ASM + * + * @dir: RX or TX direction + * @ac: Audio client handle + * @bufsz: size of each buffer + * @bufcnt: number of buffers to alloc + * + * Returns 0 on success or error on failure + */ +int q6asm_audio_client_buf_alloc_contiguous(unsigned int dir, + struct audio_client *ac, + unsigned int bufsz, + unsigned int bufcnt) +{ + int cnt = 0; + int rc = 0; + struct audio_buffer *buf; + size_t len; + int bytes_to_alloc; + + if (!(ac) || ((dir != IN) && (dir != OUT))) { + pr_err("%s: ac %pK dir %d\n", __func__, ac, dir); + return -EINVAL; + } + + pr_debug("%s: session[%d]bufsz[%d]bufcnt[%d]\n", + __func__, ac->session, + bufsz, bufcnt); + + if (ac->session <= 0 || ac->session > 8) { + pr_err("%s: Session ID is invalid, session = %d\n", __func__, + ac->session); + goto fail; + } + + if (ac->port[dir].buf) { + pr_err("%s: buffer already allocated\n", __func__); + return 0; + } + mutex_lock(&ac->cmd_lock); + buf = kzalloc(((sizeof(struct audio_buffer))*bufcnt), + GFP_KERNEL); + + if (!buf) { + pr_err("%s: buffer allocation failed\n", __func__); + mutex_unlock(&ac->cmd_lock); + goto fail; + } + + ac->port[dir].buf = buf; + + /* check for integer overflow */ + if ((bufcnt > 0) && ((INT_MAX / bufcnt) < bufsz)) { + pr_err("%s: integer overflow\n", __func__); + mutex_unlock(&ac->cmd_lock); + goto fail; + } + bytes_to_alloc = bufsz * bufcnt; + + /* The size to allocate should be multiple of 4K bytes */ + bytes_to_alloc = PAGE_ALIGN(bytes_to_alloc); + + rc = msm_audio_ion_alloc(&buf[0].dma_buf, + bytes_to_alloc, + &buf[0].phys, &len, + &buf[0].data); + if (rc) { + pr_err("%s: Audio ION alloc is failed, rc = %d\n", + __func__, rc); + mutex_unlock(&ac->cmd_lock); + goto fail; + } + + buf[0].used = dir ^ 1; + buf[0].size = bufsz; + buf[0].actual_size = bufsz; + cnt = 1; + while (cnt < bufcnt) { + if (bufsz > 0) { + buf[cnt].data = buf[0].data + (cnt * bufsz); + buf[cnt].phys = buf[0].phys + (cnt * bufsz); + if (!buf[cnt].data) { + pr_err("%s: Buf alloc failed\n", + __func__); + mutex_unlock(&ac->cmd_lock); + goto fail; + } + buf[cnt].used = dir ^ 1; + buf[cnt].size = bufsz; + buf[cnt].actual_size = bufsz; + pr_debug("%s: data[%pK]phys[%pK][%pK]\n", + __func__, + buf[cnt].data, + &buf[cnt].phys, + &buf[cnt].phys); + } + cnt++; + } + ac->port[dir].max_buf_cnt = cnt; + mutex_unlock(&ac->cmd_lock); + rc = q6asm_memory_map_regions(ac, dir, bufsz, cnt, 1); + if (rc < 0) { + pr_err("%s: CMD Memory_map_regions failed %d for size %d\n", + __func__, rc, bufsz); + goto fail; + } + return 0; +fail: + q6asm_audio_client_buf_free_contiguous(dir, ac); + return -EINVAL; +} +EXPORT_SYMBOL(q6asm_audio_client_buf_alloc_contiguous); + +static int32_t q6asm_srvc_callback(struct apr_client_data *data, void *priv) +{ + uint32_t dir = 0; + uint32_t i = IN; + uint32_t *payload; + unsigned long dsp_flags = 0; + unsigned long flags = 0; + struct asm_buffer_node *buf_node = NULL; + struct list_head *ptr, *next; + union asm_token_struct asm_token; + + struct audio_client *ac = NULL; + struct audio_port_data *port; + + int session_id; + + if (!data) { + pr_err("%s: Invalid CB\n", __func__); + return 0; + } + + payload = data->payload; + + if (data->opcode == RESET_EVENTS) { + pr_debug("%s: Reset event is received: %d %d apr[%pK]\n", + __func__, + data->reset_event, + data->reset_proc, + this_mmap.apr); + atomic_set(&this_mmap.ref_cnt, 0); + apr_reset(this_mmap.apr); + this_mmap.apr = NULL; + for (; i <= OUT; i++) { + list_for_each_safe(ptr, next, + &common_client.port[i].mem_map_handle) { + buf_node = list_entry(ptr, + struct asm_buffer_node, + list); + if (buf_node->buf_phys_addr == + common_client.port[i].buf->phys) { + list_del(&buf_node->list); + kfree(buf_node); + } + } + pr_debug("%s: Clearing custom topology\n", __func__); + } + + cal_utils_clear_cal_block_q6maps(ASM_MAX_CAL_TYPES, cal_data); + common_client.mmap_apr = NULL; + mutex_lock(&cal_data[ASM_CUSTOM_TOP_CAL]->lock); + set_custom_topology = 1; + mutex_unlock(&cal_data[ASM_CUSTOM_TOP_CAL]->lock); + topology_map_handle = 0; + rtac_clear_mapping(ASM_RTAC_CAL); + return 0; + } + asm_token.token = data->token; + session_id = asm_token._token.session_id; + + if ((session_id > 0 && session_id <= ASM_ACTIVE_STREAMS_ALLOWED)) + spin_lock_irqsave(&(session[session_id].session_lock), flags); + + ac = q6asm_get_audio_client(session_id); + dir = q6asm_get_flag_from_token(&asm_token, ASM_DIRECTION_OFFSET); + + if (!ac) { + pr_debug("%s: session[%d] already freed\n", + __func__, session_id); + if ((session_id > 0 && + session_id <= ASM_ACTIVE_STREAMS_ALLOWED)) + spin_unlock_irqrestore( + &(session[session_id].session_lock), flags); + return 0; + } + + if (data->payload_size >= 2 * sizeof(uint32_t)) { + pr_debug("%s:ptr0[0x%x]ptr1[0x%x]opcode[0x%x] token[0x%x]payload_s[%d] src[%d] dest[%d]sid[%d]dir[%d]\n", + __func__, payload[0], payload[1], data->opcode, + data->token, data->payload_size, data->src_port, + data->dest_port, asm_token._token.session_id, dir); + pr_debug("%s:Payload = [0x%x] status[0x%x]\n", + __func__, payload[0], payload[1]); + } else if (data->payload_size == sizeof(uint32_t)) { + pr_debug("%s:ptr0[0x%x]opcode[0x%x] token[0x%x]payload_s[%d] src[%d] dest[%d]sid[%d]dir[%d]\n", + __func__, payload[0], data->opcode, + data->token, data->payload_size, data->src_port, + data->dest_port, asm_token._token.session_id, dir); + pr_debug("%s:Payload = [0x%x]\n", + __func__, payload[0]); + } + + if (data->opcode == APR_BASIC_RSP_RESULT) { + switch (payload[0]) { + case ASM_CMD_SHARED_MEM_MAP_REGIONS: + case ASM_CMD_SHARED_MEM_UNMAP_REGIONS: + case ASM_CMD_ADD_TOPOLOGIES: + if (data->payload_size >= 2 * sizeof(uint32_t) && payload[1] != 0) { + pr_err("%s: cmd = 0x%x returned error = 0x%x sid:%d\n", + __func__, payload[0], payload[1], + asm_token._token.session_id); + if (payload[0] == + ASM_CMD_SHARED_MEM_UNMAP_REGIONS) + atomic_set(&ac->unmap_cb_success, 0); + + atomic_set(&ac->mem_state, payload[1]); + wake_up(&ac->mem_wait); + } else { + if (payload[0] == + ASM_CMD_SHARED_MEM_UNMAP_REGIONS) + atomic_set(&ac->unmap_cb_success, 1); + } + + if (atomic_cmpxchg(&ac->mem_state, -1, 0) == -1) + wake_up(&ac->mem_wait); + if (data->payload_size >= 2 * sizeof(uint32_t)) + dev_vdbg(ac->dev, "%s: Payload = [0x%x] status[0x%x]\n", + __func__, payload[0], payload[1]); + else + dev_vdbg(ac->dev, "%s: Payload size of %d is less than expected.\n", + __func__, data->payload_size); + break; + default: + pr_debug("%s: command[0x%x] not expecting rsp\n", + __func__, payload[0]); + break; + } + if ((session_id > 0 && + session_id <= ASM_ACTIVE_STREAMS_ALLOWED)) + spin_unlock_irqrestore( + &(session[session_id].session_lock), flags); + return 0; + } + + port = &ac->port[dir]; + + switch (data->opcode) { + case ASM_CMDRSP_SHARED_MEM_MAP_REGIONS:{ + pr_debug("%s:PL#0[0x%x] dir=0x%x s_id=0x%x\n", + __func__, payload[0], dir, asm_token._token.session_id); + spin_lock_irqsave(&port->dsp_lock, dsp_flags); + if (atomic_cmpxchg(&ac->mem_state, -1, 0) == -1) { + ac->port[dir].tmp_hdl = payload[0]; + wake_up(&ac->mem_wait); + } + spin_unlock_irqrestore(&port->dsp_lock, dsp_flags); + break; + } + case ASM_CMD_SHARED_MEM_UNMAP_REGIONS:{ + if (data->payload_size >= 2 * sizeof(uint32_t)) + pr_debug("%s: PL#0[0x%x]PL#1 [0x%x]\n", + __func__, payload[0], payload[1]); + else + pr_debug("%s: Payload size of %d is less than expected.\n", + __func__, data->payload_size); + + spin_lock_irqsave(&port->dsp_lock, dsp_flags); + if (atomic_cmpxchg(&ac->mem_state, -1, 0) == -1) + wake_up(&ac->mem_wait); + spin_unlock_irqrestore(&port->dsp_lock, dsp_flags); + + break; + } + default: + if (data->payload_size >= 2 * sizeof(uint32_t)) + pr_debug("%s: command[0x%x]success [0x%x]\n", + __func__, payload[0], payload[1]); + else + pr_debug("%s: Payload size of %d is less than expected.\n", + __func__, data->payload_size); + } + if (ac->cb) + ac->cb(data->opcode, data->token, + data->payload, ac->priv); + if ((session_id > 0 && session_id <= ASM_ACTIVE_STREAMS_ALLOWED)) + spin_unlock_irqrestore( + &(session[session_id].session_lock), flags); + + return 0; +} + +static void q6asm_process_mtmx_get_param_rsp(struct audio_client *ac, + struct asm_mtmx_strtr_get_params_cmdrsp *cmdrsp) +{ + struct asm_session_mtmx_strtr_param_session_time_v3_t *time; + + if (cmdrsp->err_code) { + dev_err_ratelimited(ac->dev, + "%s: err=%x, mod_id=%x, param_id=%x\n", + __func__, cmdrsp->err_code, + cmdrsp->param_info.module_id, + cmdrsp->param_info.param_id); + return; + } + dev_dbg_ratelimited(ac->dev, + "%s: mod_id=%x, param_id=%x\n", __func__, + cmdrsp->param_info.module_id, + cmdrsp->param_info.param_id); + + switch (cmdrsp->param_info.module_id) { + case ASM_SESSION_MTMX_STRTR_MODULE_ID_AVSYNC: + switch (cmdrsp->param_info.param_id) { + case ASM_SESSION_MTMX_STRTR_PARAM_SESSION_TIME_V3: + time = &cmdrsp->param_data.session_time; + dev_vdbg(ac->dev, "%s: GET_TIME_V3, time_lsw=%x, time_msw=%x\n", + __func__, time->session_time_lsw, + time->session_time_msw); + ac->time_stamp = (uint64_t)(((uint64_t) + time->session_time_msw << 32) | + time->session_time_lsw); + if (time->flags & + ASM_SESSION_MTMX_STRTR_PARAM_STIME_TSTMP_FLG_BMASK) + dev_warn_ratelimited(ac->dev, + "%s: recv inval tstmp\n", + __func__); + if (atomic_cmpxchg(&ac->time_flag, 1, 0)) + wake_up(&ac->time_wait); + + break; + default: + dev_err(ac->dev, "%s: unexpected param_id %x\n", + __func__, cmdrsp->param_info.param_id); + break; + } + break; + default: + dev_err(ac->dev, "%s: unexpected mod_id %x\n", __func__, + cmdrsp->param_info.module_id); + break; + } +} + +static int32_t q6asm_callback(struct apr_client_data *data, void *priv) +{ + int i = 0; + struct audio_client *ac = (struct audio_client *)priv; + unsigned long dsp_flags = 0; + uint32_t *payload; + uint32_t wakeup_flag = 1; + int32_t ret = 0; + union asm_token_struct asm_token; + uint8_t buf_index; + struct msm_adsp_event_data *pp_event_package = NULL; + uint32_t payload_size = 0; + unsigned long flags = 0; + int session_id; + + if (ac == NULL) { + pr_err("%s: ac NULL\n", __func__); + return -EINVAL; + } + if (data == NULL) { + pr_err("%s: data NULL\n", __func__); + return -EINVAL; + } + + session_id = q6asm_get_session_id_from_audio_client(ac); + if (session_id <= 0 || session_id > ASM_ACTIVE_STREAMS_ALLOWED) { + pr_err("%s: Session ID is invalid, session = %d\n", __func__, + session_id); + return -EINVAL; + } + spin_lock_irqsave(&(session[session_id].session_lock), flags); + + if (!q6asm_is_valid_audio_client(ac)) { + pr_err("%s: audio client pointer is invalid, ac = %pK\n", + __func__, ac); + spin_unlock_irqrestore( + &(session[session_id].session_lock), flags); + return -EINVAL; + } + + payload = data->payload; + asm_token.token = data->token; + if (q6asm_get_flag_from_token(&asm_token, ASM_CMD_NO_WAIT_OFFSET)) { + pr_debug("%s: No wait command opcode[0x%x] cmd_opcode:%x\n", + __func__, data->opcode, payload ? payload[0] : 0); + wakeup_flag = 0; + } + + if (data->opcode == RESET_EVENTS) { + atomic_set(&ac->reset, 1); + if (ac->apr == NULL) { + ac->apr = ac->apr2; + ac->apr2 = NULL; + } + pr_debug("%s: Reset event is received: %d %d apr[%pK]\n", + __func__, + data->reset_event, data->reset_proc, ac->apr); + if (ac->cb) + ac->cb(data->opcode, data->token, + (uint32_t *)data->payload, ac->priv); + apr_reset(ac->apr); + ac->apr = NULL; + atomic_set(&ac->time_flag, 0); + atomic_set(&ac->cmd_state, 0); + atomic_set(&ac->mem_state, 0); + atomic_set(&ac->cmd_state_pp, 0); + wake_up(&ac->time_wait); + wake_up(&ac->cmd_wait); + wake_up(&ac->mem_wait); + spin_unlock_irqrestore( + &(session[session_id].session_lock), flags); + return 0; + } + + dev_vdbg(ac->dev, "%s: session[%d]opcode[0x%x] token[0x%x]payload_size[%d] src[%d] dest[%d]\n", + __func__, + ac->session, data->opcode, + data->token, data->payload_size, data->src_port, + data->dest_port); + if ((data->opcode != ASM_DATA_EVENT_RENDERED_EOS) && + (data->opcode != ASM_DATA_EVENT_EOS) && + (data->opcode != ASM_SESSION_EVENTX_OVERFLOW) && + (data->opcode != ASM_SESSION_EVENT_RX_UNDERFLOW)) { + if (payload == NULL) { + pr_err("%s: payload is null\n", __func__); + spin_unlock_irqrestore( + &(session[session_id].session_lock), flags); + return -EINVAL; + } + if(data->payload_size >= 2 * sizeof(uint32_t)) + dev_vdbg(ac->dev, "%s: Payload = [0x%x] status[0x%x] opcode 0x%x\n", + __func__, payload[0], payload[1], data->opcode); + else + dev_vdbg(ac->dev, "%s: Payload size of %d is less than expected.\n", + __func__, data->payload_size); + } + if (data->opcode == APR_BASIC_RSP_RESULT) { + switch (payload[0]) { + case ASM_STREAM_CMD_SET_PP_PARAMS_V2: + case ASM_STREAM_CMD_SET_PP_PARAMS_V3: + if (rtac_make_asm_callback(ac->session, payload, + data->payload_size)) + break; + case ASM_SESSION_CMD_PAUSE: + case ASM_SESSION_CMD_SUSPEND: + case ASM_DATA_CMD_EOS: + case ASM_STREAM_CMD_CLOSE: + case ASM_STREAM_CMD_FLUSH: + case ASM_SESSION_CMD_RUN_V2: + case ASM_SESSION_CMD_REGISTER_FORX_OVERFLOW_EVENTS: + case ASM_STREAM_CMD_FLUSH_READBUFS: + pr_debug("%s: session %d opcode 0x%x token 0x%x Payload = [0x%x] src %d dest %d\n", + __func__, ac->session, data->opcode, data->token, + payload[0], data->src_port, data->dest_port); + ret = q6asm_is_valid_session(data, priv); + if (ret != 0) { + pr_err("%s: session invalid %d\n", __func__, ret); + spin_unlock_irqrestore( + &(session[session_id].session_lock), flags); + return ret; + } + case ASM_SESSION_CMD_SET_MTMX_STRTR_PARAMS_V2: + case ASM_STREAM_CMD_OPEN_READ_V3: + case ASM_STREAM_CMD_OPEN_WRITE_V3: + case ASM_STREAM_CMD_OPEN_PULL_MODE_WRITE: + case ASM_STREAM_CMD_OPEN_PUSH_MODE_READ: + case ASM_STREAM_CMD_OPEN_READWRITE_V2: + case ASM_STREAM_CMD_OPEN_LOOPBACK_V2: + case ASM_STREAM_CMD_OPEN_TRANSCODE_LOOPBACK: + case ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2: + case ASM_DATA_CMD_IEC_60958_MEDIA_FMT: + case ASM_STREAM_CMD_SET_ENCDEC_PARAM: + case ASM_STREAM_CMD_SET_ENCDEC_PARAM_V2: + case ASM_STREAM_CMD_REGISTER_ENCDEC_EVENTS: + case ASM_STREAM_CMD_REGISTER_IEC_61937_FMT_UPDATE: + case ASM_DATA_CMD_REMOVE_INITIAL_SILENCE: + case ASM_DATA_CMD_REMOVE_TRAILING_SILENCE: + case ASM_SESSION_CMD_REGISTER_FOR_RX_UNDERFLOW_EVENTS: + case ASM_STREAM_CMD_OPEN_READ_COMPRESSED: + case ASM_STREAM_CMD_OPEN_WRITE_COMPRESSED: + if (data->payload_size >= 2 * sizeof(uint32_t)) { + pr_debug("%s: session %d opcode 0x%x token 0x%x Payload = [0x%x] stat 0x%x src %d dest %d\n", + __func__, ac->session, + data->opcode, data->token, + payload[0], payload[1], + data->src_port, data->dest_port); + if (payload[1] != 0) { + pr_err("%s: cmd = 0x%x returned error = 0x%x\n", + __func__, payload[0], payload[1]); + if (wakeup_flag) { + if ((is_adsp_reg_event(payload[0]) >= + 0) || + (payload[0] == + ASM_STREAM_CMD_SET_PP_PARAMS_V2) || + (payload[0] == + ASM_STREAM_CMD_SET_PP_PARAMS_V3)) + atomic_set(&ac->cmd_state_pp, + payload[1]); + else + atomic_set(&ac->cmd_state, + payload[1]); + wake_up(&ac->cmd_wait); + } + spin_unlock_irqrestore( + &(session[session_id].session_lock), + flags); + return 0; + } + } else { + pr_err("%s: payload size of %x is less than expected.\n", + __func__, data->payload_size); + } + if ((is_adsp_reg_event(payload[0]) >= 0) || + (payload[0] == ASM_STREAM_CMD_SET_PP_PARAMS_V2) || + (payload[0] == ASM_STREAM_CMD_SET_PP_PARAMS_V3)) { + if (atomic_read(&ac->cmd_state_pp) && + wakeup_flag) { + atomic_set(&ac->cmd_state_pp, 0); + wake_up(&ac->cmd_wait); + } + } else { + if (atomic_read(&ac->cmd_state) && + wakeup_flag) { + atomic_set(&ac->cmd_state, 0); + wake_up(&ac->cmd_wait); + } + } + if (ac->cb) + ac->cb(data->opcode, data->token, + (uint32_t *)data->payload, ac->priv); + break; + case ASM_CMD_ADD_TOPOLOGIES: + if (data->payload_size >= 2 * sizeof(uint32_t)) { + pr_debug("%s:Payload = [0x%x]stat[0x%x]\n", + __func__, payload[0], payload[1]); + if (payload[1] != 0) { + pr_err("%s: cmd = 0x%x returned error = 0x%x\n", + __func__, payload[0], payload[1]); + if (wakeup_flag) { + atomic_set(&ac->mem_state, payload[1]); + wake_up(&ac->mem_wait); + } + spin_unlock_irqrestore( + &(session[session_id].session_lock), + flags); + return 0; + } + } else { + pr_err("%s: payload size of %x is less than expected.\n", + __func__, data->payload_size); + } + if (atomic_read(&ac->mem_state) && wakeup_flag) { + atomic_set(&ac->mem_state, 0); + wake_up(&ac->mem_wait); + } + if (ac->cb) + ac->cb(data->opcode, data->token, + (uint32_t *)data->payload, ac->priv); + break; + case ASM_DATA_EVENT_WATERMARK: { + if (data->payload_size >= 2 * sizeof(uint32_t)) + pr_debug("%s: Watermark opcode[0x%x] status[0x%x]", + __func__, payload[0], payload[1]); + else + pr_err("%s: payload size of %x is less than expected.\n", + __func__, data->payload_size); + break; + } + case ASM_STREAM_CMD_GET_PP_PARAMS_V2: + case ASM_STREAM_CMD_GET_PP_PARAMS_V3: + pr_debug("%s: ASM_STREAM_CMD_GET_PP_PARAMS session %d opcode 0x%x token 0x%x src %d dest %d\n", + __func__, ac->session, data->opcode, + data->token, data->src_port, data->dest_port); + /* Should only come here if there is an APR */ + /* error or malformed APR packet. Otherwise */ + /* response will be returned as */ + /* ASM_STREAM_CMDRSP_GET_PP_PARAMS_V2 */ + if (data->payload_size >= 2 * sizeof(uint32_t)) { + if (payload[1] != 0) { + pr_err("%s: ASM get param error = %d, resuming\n", + __func__, payload[1]); + rtac_make_asm_callback(ac->session, payload, + data->payload_size); + } + } else { + pr_err("%s: payload size of %x is less than expected.\n", + __func__, data->payload_size); + } + break; + case ASM_STREAM_CMD_REGISTER_PP_EVENTS: + pr_debug("%s: ASM_STREAM_CMD_REGISTER_PP_EVENTS session %d opcode 0x%x token 0x%x src %d dest %d\n", + __func__, ac->session, + data->opcode, data->token, + data->src_port, data->dest_port); + if (data->payload_size >= 2 * sizeof(uint32_t)) { + if (payload[1] != 0) + pr_err("%s: ASM get param error = %d, resuming\n", + __func__, payload[1]); + atomic_set(&ac->cmd_state_pp, payload[1]); + wake_up(&ac->cmd_wait); + } else { + pr_err("%s: payload size of %x is less than expected.\n", + __func__, data->payload_size); + } + break; + default: + pr_debug("%s: command[0x%x] not expecting rsp\n", + __func__, payload[0]); + break; + } + + spin_unlock_irqrestore( + &(session[session_id].session_lock), flags); + return 0; + } + + switch (data->opcode) { + case ASM_DATA_EVENT_WRITE_DONE_V2:{ + struct audio_port_data *port = &ac->port[IN]; + if (data->payload_size >= 2 * sizeof(uint32_t)) + dev_vdbg(ac->dev, "%s: Rxed opcode[0x%x] status[0x%x] token[%d]", + __func__, payload[0], payload[1], + data->token); + else + dev_err(ac->dev, "%s: payload size of %x is less than expected.\n", + __func__, data->payload_size); + if (ac->io_mode & SYNC_IO_MODE) { + if (port->buf == NULL) { + pr_err("%s: Unexpected Write Done\n", + __func__); + spin_unlock_irqrestore( + &(session[session_id].session_lock), + flags); + return -EINVAL; + } + spin_lock_irqsave(&port->dsp_lock, dsp_flags); + buf_index = asm_token._token.buf_index; + if (buf_index < 0 || buf_index >= port->max_buf_cnt) { + pr_debug("%s: Invalid buffer index %u\n", + __func__, buf_index); + spin_unlock_irqrestore(&port->dsp_lock, + dsp_flags); + spin_unlock_irqrestore( + &(session[session_id].session_lock), + flags); + return -EINVAL; + } + if (data->payload_size >= 2 * sizeof(uint32_t) && + (lower_32_bits(port->buf[buf_index].phys) != + payload[0] || + msm_audio_populate_upper_32_bits( + port->buf[buf_index].phys) != payload[1])) { + pr_debug("%s: Expected addr %pK\n", + __func__, &port->buf[buf_index].phys); + pr_err("%s: rxedl[0x%x] rxedu [0x%x]\n", + __func__, payload[0], payload[1]); + spin_unlock_irqrestore(&port->dsp_lock, + dsp_flags); + spin_unlock_irqrestore( + &(session[session_id].session_lock), + flags); + return -EINVAL; + } + port->buf[buf_index].used = 1; + spin_unlock_irqrestore(&port->dsp_lock, dsp_flags); + + config_debug_fs_write_cb(); + + for (i = 0; i < port->max_buf_cnt; i++) + dev_vdbg(ac->dev, "%s %d\n", + __func__, port->buf[i].used); + + } + break; + } + case ASM_STREAM_CMDRSP_GET_PP_PARAMS_V2: + case ASM_STREAM_CMDRSP_GET_PP_PARAMS_V3: + pr_debug("%s: ASM_STREAM_CMDRSP_GET_PP_PARAMS session %d opcode 0x%x token 0x%x src %d dest %d\n", + __func__, ac->session, data->opcode, data->token, + data->src_port, data->dest_port); + if (payload[0] != 0) { + pr_err("%s: ASM_STREAM_CMDRSP_GET_PP_PARAMS returned error = 0x%x\n", + __func__, payload[0]); + } else if (generic_get_data) { + generic_get_data->valid = 1; + if (generic_get_data->is_inband) { + if (data->payload_size >= 4 * sizeof(uint32_t)) + pr_debug("%s: payload[1] = 0x%x, payload[2]=0x%x, payload[3]=0x%x\n", + __func__, payload[1], payload[2], payload[3]); + else + pr_err("%s: payload size of %x is less than expected.\n", + __func__, + data->payload_size); + + if (data->payload_size >= (4 + (payload[3]>>2)) * sizeof(uint32_t)) { + generic_get_data->size_in_ints = payload[3]>>2; + for (i = 0; i < payload[3]>>2; i++) { + generic_get_data->ints[i] = + payload[4+i]; + pr_debug("%s: ASM callback val %i = %i\n", + __func__, i, payload[4+i]); + } + } else { + pr_err("%s: payload size of %x is less than expected.\n", + __func__, data->payload_size); + } + pr_debug("%s: callback size in ints = %i\n", + __func__, + generic_get_data->size_in_ints); + } + if (atomic_read(&ac->cmd_state) && wakeup_flag) { + atomic_set(&ac->cmd_state, 0); + wake_up(&ac->cmd_wait); + } + break; + } + rtac_make_asm_callback(ac->session, payload, + data->payload_size); + break; + case ASM_DATA_EVENT_READ_DONE_V2:{ + + struct audio_port_data *port = &ac->port[OUT]; + + config_debug_fs_read_cb(); + + dev_vdbg(ac->dev, "%s: ReadDone: status=%d buff_add=0x%x act_size=%d offset=%d\n", + __func__, payload[READDONE_IDX_STATUS], + payload[READDONE_IDX_BUFADD_LSW], + payload[READDONE_IDX_SIZE], + payload[READDONE_IDX_OFFSET]); + + dev_vdbg(ac->dev, "%s: ReadDone:msw_ts=%d lsw_ts=%d memmap_hdl=0x%x flags=%d id=%d num=%d\n", + __func__, payload[READDONE_IDX_MSW_TS], + payload[READDONE_IDX_LSW_TS], + payload[READDONE_IDX_MEMMAP_HDL], + payload[READDONE_IDX_FLAGS], + payload[READDONE_IDX_SEQ_ID], + payload[READDONE_IDX_NUMFRAMES]); + + if (ac->io_mode & SYNC_IO_MODE) { + if (port->buf == NULL) { + pr_err("%s: Unexpected Read Done\n", __func__); + spin_unlock_irqrestore( + &(session[session_id].session_lock), + flags); + return -EINVAL; + } + spin_lock_irqsave(&port->dsp_lock, dsp_flags); + buf_index = asm_token._token.buf_index; + if (buf_index < 0 || buf_index >= port->max_buf_cnt) { + pr_debug("%s: Invalid buffer index %u\n", + __func__, buf_index); + spin_unlock_irqrestore(&port->dsp_lock, + dsp_flags); + spin_unlock_irqrestore( + &(session[session_id].session_lock), + flags); + return -EINVAL; + } + port->buf[buf_index].used = 0; + if (lower_32_bits(port->buf[buf_index].phys) != + payload[READDONE_IDX_BUFADD_LSW] || + msm_audio_populate_upper_32_bits( + port->buf[buf_index].phys) != + payload[READDONE_IDX_BUFADD_MSW]) { + dev_vdbg(ac->dev, "%s: Expected addr %pK\n", + __func__, &port->buf[buf_index].phys); + pr_err("%s: rxedl[0x%x] rxedu[0x%x]\n", + __func__, + payload[READDONE_IDX_BUFADD_LSW], + payload[READDONE_IDX_BUFADD_MSW]); + spin_unlock_irqrestore(&port->dsp_lock, + dsp_flags); + break; + } + port->buf[buf_index].actual_size = + payload[READDONE_IDX_SIZE]; + spin_unlock_irqrestore(&port->dsp_lock, dsp_flags); + } + break; + } + case ASM_DATA_EVENT_EOS: + case ASM_DATA_EVENT_RENDERED_EOS: + pr_debug("%s: EOS ACK received: rxed session %d opcode 0x%x token 0x%x src %d dest %d\n", + __func__, ac->session, + data->opcode, data->token, + data->src_port, data->dest_port); + break; + case ASM_SESSION_EVENTX_OVERFLOW: + pr_debug("%s: ASM_SESSION_EVENTX_OVERFLOW session %d opcode 0x%x token 0x%x src %d dest %d\n", + __func__, ac->session, + data->opcode, data->token, + data->src_port, data->dest_port); + break; + case ASM_SESSION_EVENT_RX_UNDERFLOW: + pr_debug("%s: ASM_SESSION_EVENT_RX_UNDERFLOW session %d opcode 0x%x token 0x%x src %d dest %d\n", + __func__, ac->session, + data->opcode, data->token, + data->src_port, data->dest_port); + break; + case ASM_SESSION_CMDRSP_GET_SESSIONTIME_V3: + if (data->payload_size >= 3 * sizeof(uint32_t)) { + dev_vdbg(ac->dev, "%s: ASM_SESSION_CMDRSP_GET_SESSIONTIME_V3, payload[0] = %d, payload[1] = %d, payload[2] = %d\n", + __func__, + payload[0], payload[1], payload[2]); + ac->time_stamp = (uint64_t)(((uint64_t)payload[2] << 32) | + payload[1]); + } else { + dev_err(ac->dev, "%s: payload size of %x is less than expected.n", + __func__, data->payload_size); + } + if (atomic_cmpxchg(&ac->time_flag, 1, 0)) + wake_up(&ac->time_wait); + break; + case ASM_DATA_EVENT_SR_CM_CHANGE_NOTIFY: + case ASM_DATA_EVENT_ENC_SR_CM_CHANGE_NOTIFY: + pr_debug("%s: ASM_DATA_EVENT_SR_CM_CHANGE_NOTIFY session %d opcode 0x%x token 0x%x src %d dest %d\n", + __func__, ac->session, + data->opcode, data->token, + data->src_port, data->dest_port); + if (data->payload_size >= 4 * sizeof(uint32_t)) + pr_debug("%s: ASM_DATA_EVENT_SR_CM_CHANGE_NOTIFY, payload[0] = %d, payload[1] = %d, payload[2] = %d, payload[3] = %d\n", + __func__, + payload[0], payload[1], payload[2], + payload[3]); + else + pr_debug("%s: payload size of %x is less than expected.\n", + __func__, data->payload_size); + break; + case ASM_SESSION_CMDRSP_GET_MTMX_STRTR_PARAMS_V2: + q6asm_process_mtmx_get_param_rsp(ac, (void *) payload); + break; + case ASM_STREAM_PP_EVENT: + case ASM_STREAM_CMD_ENCDEC_EVENTS: + case ASM_STREAM_CMD_REGISTER_IEC_61937_FMT_UPDATE: + if (data->payload_size >= 2 * sizeof(uint32_t)) + pr_debug("%s: ASM_STREAM_EVENT payload[0][0x%x] payload[1][0x%x]", + __func__, payload[0], payload[1]); + else + pr_debug("%s: payload size of %x is less than expected.\n", + __func__, data->payload_size); + i = is_adsp_raise_event(data->opcode); + if (i < 0) { + spin_unlock_irqrestore( + &(session[session_id].session_lock), flags); + return 0; + } + + /* repack payload for asm_stream_pp_event + * package is composed of event type + size + actual payload + */ + payload_size = data->payload_size; + if (payload_size > UINT_MAX - sizeof(struct msm_adsp_event_data)) { + pr_err("%s: payload size = %d exceeds limit.\n", + __func__, payload_size); + spin_unlock(&(session[session_id].session_lock)); + return -EINVAL; + } + + pp_event_package = kzalloc(payload_size + + sizeof(struct msm_adsp_event_data), + GFP_ATOMIC); + if (!pp_event_package) { + spin_unlock_irqrestore( + &(session[session_id].session_lock), flags); + return -ENOMEM; + } + + pp_event_package->event_type = i; + pp_event_package->payload_len = payload_size; + memcpy((void *)pp_event_package->payload, + data->payload, payload_size); + ac->cb(data->opcode, data->token, + (void *)pp_event_package, ac->priv); + kfree(pp_event_package); + spin_unlock_irqrestore( + &(session[session_id].session_lock), flags); + return 0; + case ASM_SESSION_CMDRSP_ADJUST_SESSION_CLOCK_V2: + if (data->payload_size >= 3 * sizeof(uint32_t)) + pr_debug("%s: ASM_SESSION_CMDRSP_ADJUST_SESSION_CLOCK_V2 sesion %d status 0x%x msw %u lsw %u\n", + __func__, ac->session, payload[0], payload[2], + payload[1]); + else + pr_err("%s: payload size of %x is less than expected.\n", + __func__, data->payload_size); + wake_up(&ac->cmd_wait); + break; + case ASM_SESSION_CMDRSP_GET_PATH_DELAY_V2: + if (data->payload_size >= 3 * sizeof(uint32_t)) + pr_debug("%s: ASM_SESSION_CMDRSP_GET_PATH_DELAY_V2 session %d status 0x%x msw %u lsw %u\n", + __func__, ac->session, payload[0], payload[2], + payload[1]); + else + pr_err("%s: payload size of %x is less than expected.\n", + __func__, data->payload_size); + if (payload[0] == 0 && data->payload_size >= 2 * sizeof(uint32_t)) { + atomic_set(&ac->cmd_state, 0); + /* ignore msw, as a delay that large shouldn't happen */ + ac->path_delay = payload[1]; + } else { + atomic_set(&ac->cmd_state, payload[0]); + ac->path_delay = UINT_MAX; + } + wake_up(&ac->cmd_wait); + break; + } + if (ac->cb) + ac->cb(data->opcode, data->token, + data->payload, ac->priv); + spin_unlock_irqrestore( + &(session[session_id].session_lock), flags); + return 0; +} + +/** + * q6asm_is_cpu_buf_avail - + * retrieve next CPU buf avail + * + * @dir: RX or TX direction + * @ac: Audio client handle + * @size: size pointer to be updated with size of buffer + * @index: index pointer to be updated with + * CPU buffer index available + * + * Returns buffer pointer on success or NULL on failure + */ +void *q6asm_is_cpu_buf_avail(int dir, struct audio_client *ac, uint32_t *size, + uint32_t *index) +{ + void *data; + unsigned char idx; + struct audio_port_data *port; + + if (!ac || ((dir != IN) && (dir != OUT))) { + pr_err("%s: ac %pK dir %d\n", __func__, ac, dir); + return NULL; + } + + if (ac->io_mode & SYNC_IO_MODE) { + port = &ac->port[dir]; + + mutex_lock(&port->lock); + idx = port->cpu_buf; + if (port->buf == NULL) { + pr_err("%s: Buffer pointer null\n", __func__); + mutex_unlock(&port->lock); + return NULL; + } + /* dir 0: used = 0 means buf in use + * dir 1: used = 1 means buf in use + */ + if (port->buf[idx].used == dir) { + /* To make it more robust, we could loop and get the + * next avail buf, its risky though + */ + pr_err("%s: Next buf idx[0x%x] not available, dir[%d]\n", + __func__, idx, dir); + mutex_unlock(&port->lock); + return NULL; + } + *size = port->buf[idx].actual_size; + *index = port->cpu_buf; + data = port->buf[idx].data; + dev_vdbg(ac->dev, "%s: session[%d]index[%d] data[%pK]size[%d]\n", + __func__, + ac->session, + port->cpu_buf, + data, *size); + /* By default increase the cpu_buf cnt + * user accesses this function,increase cpu + * buf(to avoid another api) + */ + port->buf[idx].used = dir; + port->cpu_buf = q6asm_get_next_buf(ac, port->cpu_buf, + port->max_buf_cnt); + mutex_unlock(&port->lock); + return data; + } + return NULL; +} +EXPORT_SYMBOL(q6asm_is_cpu_buf_avail); + +/** + * q6asm_cpu_buf_release - + * releases cpu buffer for ASM + * + * @dir: RX or TX direction + * @ac: Audio client handle + * + * Returns 0 on success or error on failure + */ +int q6asm_cpu_buf_release(int dir, struct audio_client *ac) +{ + struct audio_port_data *port; + int ret = 0; + int idx; + + if (!ac || ((dir != IN) && (dir != OUT))) { + pr_err("%s: ac %pK dir %d\n", __func__, ac, dir); + ret = -EINVAL; + goto exit; + } + + if (ac->io_mode & SYNC_IO_MODE) { + port = &ac->port[dir]; + mutex_lock(&port->lock); + idx = port->cpu_buf; + if (port->cpu_buf == 0) { + port->cpu_buf = port->max_buf_cnt - 1; + } else if (port->cpu_buf < port->max_buf_cnt) { + port->cpu_buf = port->cpu_buf - 1; + } else { + pr_err("%s: buffer index(%d) out of range\n", + __func__, port->cpu_buf); + ret = -EINVAL; + mutex_unlock(&port->lock); + goto exit; + } + port->buf[port->cpu_buf].used = dir ^ 1; + mutex_unlock(&port->lock); + } +exit: + return ret; +} +EXPORT_SYMBOL(q6asm_cpu_buf_release); + +/** + * q6asm_is_cpu_buf_avail_nolock - + * retrieve next CPU buf avail without lock acquire + * + * @dir: RX or TX direction + * @ac: Audio client handle + * @size: size pointer to be updated with size of buffer + * @index: index pointer to be updated with + * CPU buffer index available + * + * Returns buffer pointer on success or NULL on failure + */ +void *q6asm_is_cpu_buf_avail_nolock(int dir, struct audio_client *ac, + uint32_t *size, uint32_t *index) +{ + void *data; + unsigned char idx; + struct audio_port_data *port; + + if (!ac || ((dir != IN) && (dir != OUT))) { + pr_err("%s: ac %pK dir %d\n", __func__, ac, dir); + return NULL; + } + + port = &ac->port[dir]; + + idx = port->cpu_buf; + if (port->buf == NULL) { + pr_err("%s: Buffer pointer null\n", __func__); + return NULL; + } + /* + * dir 0: used = 0 means buf in use + * dir 1: used = 1 means buf in use + */ + if (port->buf[idx].used == dir) { + /* + * To make it more robust, we could loop and get the + * next avail buf, its risky though + */ + pr_err("%s: Next buf idx[0x%x] not available, dir[%d]\n", + __func__, idx, dir); + return NULL; + } + *size = port->buf[idx].actual_size; + *index = port->cpu_buf; + data = port->buf[idx].data; + dev_vdbg(ac->dev, "%s: session[%d]index[%d] data[%pK]size[%d]\n", + __func__, ac->session, port->cpu_buf, + data, *size); + /* + * By default increase the cpu_buf cnt + * user accesses this function,increase cpu + * buf(to avoid another api) + */ + port->buf[idx].used = dir; + port->cpu_buf = q6asm_get_next_buf(ac, port->cpu_buf, + port->max_buf_cnt); + return data; +} +EXPORT_SYMBOL(q6asm_is_cpu_buf_avail_nolock); + +int q6asm_is_dsp_buf_avail(int dir, struct audio_client *ac) +{ + int ret = -1; + struct audio_port_data *port; + uint32_t idx; + + if (!ac || (dir != OUT)) { + pr_err("%s: ac %pK dir %d\n", __func__, ac, dir); + return ret; + } + + if (ac->io_mode & SYNC_IO_MODE) { + port = &ac->port[dir]; + + mutex_lock(&port->lock); + idx = port->dsp_buf; + + if (port->buf[idx].used == (dir ^ 1)) { + /* To make it more robust, we could loop and get the + * next avail buf, its risky though + */ + pr_err("%s: Next buf idx[0x%x] not available, dir[%d]\n", + __func__, idx, dir); + mutex_unlock(&port->lock); + return ret; + } + dev_vdbg(ac->dev, "%s: session[%d]dsp_buf=%d cpu_buf=%d\n", + __func__, + ac->session, port->dsp_buf, port->cpu_buf); + ret = ((port->dsp_buf != port->cpu_buf) ? 0 : -1); + mutex_unlock(&port->lock); + } + return ret; +} + +static void __q6asm_add_hdr(struct audio_client *ac, struct apr_hdr *hdr, + uint32_t pkt_size, uint32_t cmd_flg, uint32_t stream_id) +{ + unsigned long flags = 0; + + dev_vdbg(ac->dev, "%s: pkt_size=%d cmd_flg=%d session=%d stream_id=%d\n", + __func__, pkt_size, cmd_flg, ac->session, stream_id); + mutex_lock(&ac->cmd_lock); + spin_lock_irqsave(&(session[ac->session].session_lock), flags); + if (ac->apr == NULL) { + pr_err("%s: AC APR handle NULL", __func__); + spin_unlock_irqrestore( + &(session[ac->session].session_lock), flags); + mutex_unlock(&ac->cmd_lock); + return; + } + + hdr->hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(sizeof(struct apr_hdr)), + APR_PKT_VER); + hdr->src_svc = ((struct apr_svc *)ac->apr)->id; + hdr->src_domain = APR_DOMAIN_APPS; + hdr->dest_svc = APR_SVC_ASM; + hdr->dest_domain = APR_DOMAIN_ADSP; + hdr->src_port = ((ac->session << 8) & 0xFF00) | (stream_id); + hdr->dest_port = ((ac->session << 8) & 0xFF00) | (stream_id); + if (cmd_flg) + q6asm_update_token(&hdr->token, + ac->session, + 0, /* Stream ID is NA */ + 0, /* Buffer index is NA */ + 0, /* Direction flag is NA */ + WAIT_CMD); + + hdr->pkt_size = pkt_size; + spin_unlock_irqrestore( + &(session[ac->session].session_lock), flags); + mutex_unlock(&ac->cmd_lock); +} + +static void q6asm_add_hdr(struct audio_client *ac, struct apr_hdr *hdr, + uint32_t pkt_size, uint32_t cmd_flg) +{ + __q6asm_add_hdr(ac, hdr, pkt_size, cmd_flg, ac->stream_id); +} + +static void q6asm_stream_add_hdr(struct audio_client *ac, struct apr_hdr *hdr, + uint32_t pkt_size, uint32_t cmd_flg, int32_t stream_id) +{ + __q6asm_add_hdr(ac, hdr, pkt_size, cmd_flg, stream_id); +} + +static void __q6asm_add_hdr_async(struct audio_client *ac, struct apr_hdr *hdr, + uint32_t pkt_size, uint32_t cmd_flg, + uint32_t stream_id, u8 no_wait_flag) +{ + dev_vdbg(ac->dev, "%s: pkt_size = %d, cmd_flg = %d, session = %d stream_id=%d\n", + __func__, pkt_size, cmd_flg, ac->session, stream_id); + hdr->hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(sizeof(struct apr_hdr)), + APR_PKT_VER); + if (ac->apr == NULL) { + pr_err("%s: AC APR is NULL", __func__); + return; + } + hdr->src_svc = ((struct apr_svc *)ac->apr)->id; + hdr->src_domain = APR_DOMAIN_APPS; + hdr->dest_svc = APR_SVC_ASM; + hdr->dest_domain = APR_DOMAIN_ADSP; + hdr->src_port = ((ac->session << 8) & 0xFF00) | (stream_id); + hdr->dest_port = ((ac->session << 8) & 0xFF00) | (stream_id); + if (cmd_flg) { + q6asm_update_token(&hdr->token, + ac->session, + 0, /* Stream ID is NA */ + 0, /* Buffer index is NA */ + 0, /* Direction flag is NA */ + no_wait_flag); + + } + hdr->pkt_size = pkt_size; +} + +static void q6asm_add_hdr_async(struct audio_client *ac, struct apr_hdr *hdr, + uint32_t pkt_size, uint32_t cmd_flg) +{ + __q6asm_add_hdr_async(ac, hdr, pkt_size, cmd_flg, + ac->stream_id, WAIT_CMD); +} + +static void q6asm_stream_add_hdr_async(struct audio_client *ac, + struct apr_hdr *hdr, uint32_t pkt_size, + uint32_t cmd_flg, int32_t stream_id) +{ + __q6asm_add_hdr_async(ac, hdr, pkt_size, cmd_flg, + stream_id, NO_WAIT_CMD); +} + +static void q6asm_add_hdr_custom_topology(struct audio_client *ac, + struct apr_hdr *hdr, + uint32_t pkt_size) +{ + pr_debug("%s: pkt_size=%d session=%d\n", + __func__, pkt_size, ac->session); + if (ac->apr == NULL) { + pr_err("%s: AC APR handle NULL\n", __func__); + return; + } + + mutex_lock(&ac->cmd_lock); + hdr->hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(sizeof(struct apr_hdr)), + APR_PKT_VER); + hdr->src_svc = ((struct apr_svc *)ac->apr)->id; + hdr->src_domain = APR_DOMAIN_APPS; + hdr->dest_svc = APR_SVC_ASM; + hdr->dest_domain = APR_DOMAIN_ADSP; + hdr->src_port = ((ac->session << 8) & 0xFF00) | 0x01; + hdr->dest_port = 0; + q6asm_update_token(&hdr->token, + ac->session, + 0, /* Stream ID is NA */ + 0, /* Buffer index is NA */ + 0, /* Direction flag is NA */ + WAIT_CMD); + hdr->pkt_size = pkt_size; + mutex_unlock(&ac->cmd_lock); +} + +static void q6asm_add_mmaphdr(struct audio_client *ac, struct apr_hdr *hdr, + u32 pkt_size, int dir) +{ + pr_debug("%s: pkt size=%d\n", + __func__, pkt_size); + hdr->hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + hdr->src_port = 0; + hdr->dest_port = 0; + q6asm_update_token(&hdr->token, + ac->session, + 0, /* Stream ID is NA */ + 0, /* Buffer index is NA */ + dir, + WAIT_CMD); + hdr->pkt_size = pkt_size; +} + +/** + * q6asm_set_pp_params + * command to set ASM parameter data + * send memory mapping header for out of band case + * send pre-packed parameter data for in band case + * + * @ac: audio client handle + * @mem_hdr: memory mapping header + * @param_data: pre-packed parameter payload + * @param_size: size of pre-packed parameter data + * + * Returns 0 on success or error on failure + */ +int q6asm_set_pp_params(struct audio_client *ac, + struct mem_mapping_hdr *mem_hdr, u8 *param_data, + u32 param_size) +{ + struct asm_stream_cmd_set_pp_params *asm_set_param = NULL; + int pkt_size = 0; + int ret = 0; + int session_id = 0; + + if (ac == NULL) { + pr_err("%s: Audio Client is NULL\n", __func__); + return -EINVAL; + } else if (ac->apr == NULL) { + pr_err("%s: APR pointer is NULL\n", __func__); + return -EINVAL; + } + + session_id = q6asm_get_session_id_from_audio_client(ac); + if (!session_id) + return -EINVAL; + + pkt_size = sizeof(struct asm_stream_cmd_set_pp_params); + /* Add param size to packet size when sending in-band only */ + if (param_data != NULL) + pkt_size += param_size; + asm_set_param = kzalloc(pkt_size, GFP_KERNEL); + if (!asm_set_param) + return -ENOMEM; + + mutex_lock(&session[session_id].mutex_lock_per_session); + if (!q6asm_is_valid_audio_client(ac)) { + ret = -EINVAL; + goto done; + } + + if (ac->apr == NULL) { + pr_err("%s: AC APR handle NULL\n", __func__); + ret = -EINVAL; + goto done; + } + + q6asm_add_hdr_async(ac, &asm_set_param->apr_hdr, pkt_size, TRUE); + + /* With pre-packed data, only the opcode differs from V2 and V3. */ + if (q6common_is_instance_id_supported()) + asm_set_param->apr_hdr.opcode = ASM_STREAM_CMD_SET_PP_PARAMS_V3; + else + asm_set_param->apr_hdr.opcode = ASM_STREAM_CMD_SET_PP_PARAMS_V2; + + asm_set_param->payload_size = param_size; + + if (mem_hdr != NULL) { + /* Out of band case */ + asm_set_param->mem_hdr = *mem_hdr; + } else if (param_data != NULL) { + /* + * In band case. Parameter data must be pre-packed with its + * header before calling this function. Use + * q6common_pack_pp_params to pack parameter data and header + * correctly. + */ + memcpy(&asm_set_param->param_data, param_data, param_size); + } else { + pr_err("%s: Received NULL pointers for both mem header and param data\n", + __func__); + ret = -EINVAL; + goto done; + } + + atomic_set(&ac->cmd_state_pp, -1); + ret = apr_send_pkt(ac->apr, (uint32_t *)asm_set_param); + if (ret < 0) { + pr_err("%s: apr send failed rc %d\n", __func__, ret); + ret = -EINVAL; + goto done; + } + + ret = wait_event_timeout(ac->cmd_wait, + atomic_read(&ac->cmd_state_pp) >= 0, + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: timeout sending apr pkt\n", __func__); + ret = -ETIMEDOUT; + goto done; + } + + if (atomic_read(&ac->cmd_state_pp) > 0) { + pr_err("%s: DSP returned error[%s]\n", __func__, + adsp_err_get_err_str(atomic_read(&ac->cmd_state_pp))); + ret = adsp_err_get_lnx_err_code(atomic_read(&ac->cmd_state_pp)); + goto done; + } + ret = 0; +done: + mutex_unlock(&session[session_id].mutex_lock_per_session); + kfree(asm_set_param); + return ret; +} +EXPORT_SYMBOL(q6asm_set_pp_params); + +/** + * q6asm_pack_and_set_pp_param_in_band + * command to pack and set parameter data for in band case + * + * @ac: audio client handle + * @param_hdr: parameter header + * @param_data: parameter data + * + * Returns 0 on success or error on failure + */ +int q6asm_pack_and_set_pp_param_in_band(struct audio_client *ac, + struct param_hdr_v3 param_hdr, + u8 *param_data) +{ + u8 *packed_data = NULL; + u32 packed_size = sizeof(union param_hdrs) + param_hdr.param_size; + int ret = 0; + + if (ac == NULL) { + pr_err("%s: Audio Client is NULL\n", __func__); + return -EINVAL; + } + + packed_data = kzalloc(packed_size, GFP_KERNEL); + if (packed_data == NULL) + return -ENOMEM; + + ret = q6common_pack_pp_params(packed_data, ¶m_hdr, param_data, + &packed_size); + if (ret) { + pr_err("%s: Failed to pack params, error %d\n", __func__, ret); + goto done; + } + + ret = q6asm_set_pp_params(ac, NULL, packed_data, packed_size); +done: + kfree(packed_data); + return ret; +} +EXPORT_SYMBOL(q6asm_pack_and_set_pp_param_in_band); + +/** + * q6asm_set_soft_volume_module_instance_ids + * command to set module and instance ids for soft volume + * + * @instance: soft volume instance + * @param_hdr: parameter header + * + * Returns 0 on success or error on failure + */ +int q6asm_set_soft_volume_module_instance_ids(int instance, + struct param_hdr_v3 *param_hdr) +{ + if (param_hdr == NULL) { + pr_err("%s: Param header is NULL\n", __func__); + return -EINVAL; + } + + switch (instance) { + case SOFT_VOLUME_INSTANCE_2: + param_hdr->module_id = ASM_MODULE_ID_VOL_CTRL2; + param_hdr->instance_id = INSTANCE_ID_0; + return 0; + case SOFT_VOLUME_INSTANCE_1: + param_hdr->module_id = ASM_MODULE_ID_VOL_CTRL; + param_hdr->instance_id = INSTANCE_ID_0; + return 0; + default: + pr_err("%s: Invalid instance %d\n", __func__, instance); + return -EINVAL; + } +} +EXPORT_SYMBOL(q6asm_set_soft_volume_module_instance_ids); + +/** + * q6asm_open_read_compressed - + * command to open ASM in compressed read mode + * + * @ac: Audio client handle + * @format: capture format for ASM + * @passthrough_flag: flag to indicate passthrough option + * + * Returns 0 on success or error on failure + */ +int q6asm_open_read_compressed(struct audio_client *ac, uint32_t format, + uint32_t passthrough_flag) +{ + int rc = 0; + struct asm_stream_cmd_open_read_compressed open; + + if (ac == NULL) { + pr_err("%s: ac[%pK] NULL\n", __func__, ac); + rc = -EINVAL; + goto fail_cmd; + } + + if (ac->apr == NULL) { + pr_err("%s: APR handle[%pK] NULL\n", __func__, ac->apr); + rc = -EINVAL; + goto fail_cmd; + } + pr_debug("%s: session[%d] wr_format[0x%x]\n", __func__, ac->session, + format); + + q6asm_add_hdr(ac, &open.hdr, sizeof(open), TRUE); + open.hdr.opcode = ASM_STREAM_CMD_OPEN_READ_COMPRESSED; + atomic_set(&ac->cmd_state, -1); + + /* + * Below flag indicates whether DSP shall keep IEC61937 packing or + * unpack to raw compressed format + */ + if (format == FORMAT_IEC61937) { + open.mode_flags = 0x1; + pr_debug("%s: Flag 1 IEC61937 output\n", __func__); + } else { + open.mode_flags = 0; + open.frames_per_buf = 1; + pr_debug("%s: Flag 0 RAW_COMPR output\n", __func__); + } + + rc = apr_send_pkt(ac->apr, (uint32_t *) &open); + if (rc < 0) { + pr_err("%s: open failed op[0x%x]rc[%d]\n", + __func__, open.hdr.opcode, rc); + rc = -EINVAL; + goto fail_cmd; + } + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), + msecs_to_jiffies(TIMEOUT_MS)); + if (!rc) { + pr_err("%s: timeout. waited for OPEN_READ_COMPR rc[%d]\n", + __func__, rc); + rc = -ETIMEDOUT; + goto fail_cmd; + } + + if (atomic_read(&ac->cmd_state) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->cmd_state))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state)); + goto fail_cmd; + } + + return 0; + +fail_cmd: + return rc; +} +EXPORT_SYMBOL(q6asm_open_read_compressed); + +static int __q6asm_open_read(struct audio_client *ac, + uint32_t format, uint16_t bits_per_sample, + uint32_t pcm_format_block_ver, + bool ts_mode, uint32_t enc_cfg_id) +{ + int rc = 0x00; + struct asm_stream_cmd_open_read_v3 open; + struct q6asm_cal_info cal_info; + + config_debug_fs_reset_index(); + + if (ac == NULL) { + pr_err("%s: APR handle NULL\n", __func__); + return -EINVAL; + } + if (ac->apr == NULL) { + pr_err("%s: AC APR handle NULL\n", __func__); + return -EINVAL; + } + pr_debug("%s: session[%d]\n", __func__, ac->session); + + q6asm_add_hdr(ac, &open.hdr, sizeof(open), TRUE); + atomic_set(&ac->cmd_state, -1); + open.hdr.opcode = ASM_STREAM_CMD_OPEN_READ_V3; + /* Stream prio : High, provide meta info with encoded frames */ + open.src_endpointype = ASM_END_POINT_DEVICE_MATRIX; + + rc = q6asm_get_asm_topology_apptype(&cal_info); + open.preprocopo_id = cal_info.topology_id; + + + open.bits_per_sample = bits_per_sample; + open.mode_flags = 0x0; + + ac->topology = open.preprocopo_id; + ac->app_type = cal_info.app_type; + if (ac->perf_mode == LOW_LATENCY_PCM_MODE) { + open.mode_flags |= ASM_LOW_LATENCY_TX_STREAM_SESSION << + ASM_SHIFT_STREAM_PERF_MODE_FLAG_IN_OPEN_READ; + } else { + open.mode_flags |= ASM_LEGACY_STREAM_SESSION << + ASM_SHIFT_STREAM_PERF_MODE_FLAG_IN_OPEN_READ; + } + + switch (format) { + case FORMAT_LINEAR_PCM: + open.mode_flags |= 0x00; + open.enc_cfg_id = q6asm_get_pcm_format_id(pcm_format_block_ver); + if (ts_mode) + open.mode_flags |= ABSOLUTE_TIMESTAMP_ENABLE; + break; + case FORMAT_MPEG4_AAC: + open.mode_flags |= BUFFER_META_ENABLE; + open.enc_cfg_id = ASM_MEDIA_FMT_AAC_V2; + break; + case FORMAT_G711_ALAW_FS: + open.mode_flags |= BUFFER_META_ENABLE; + open.enc_cfg_id = ASM_MEDIA_FMT_G711_ALAW_FS; + break; + case FORMAT_G711_MLAW_FS: + open.mode_flags |= BUFFER_META_ENABLE; + open.enc_cfg_id = ASM_MEDIA_FMT_G711_MLAW_FS; + break; + case FORMAT_V13K: + open.mode_flags |= BUFFER_META_ENABLE; + open.enc_cfg_id = ASM_MEDIA_FMT_V13K_FS; + break; + case FORMAT_EVRC: + open.mode_flags |= BUFFER_META_ENABLE; + open.enc_cfg_id = ASM_MEDIA_FMT_EVRC_FS; + break; + case FORMAT_AMRNB: + open.mode_flags |= BUFFER_META_ENABLE; + open.enc_cfg_id = ASM_MEDIA_FMT_AMRNB_FS; + break; + case FORMAT_AMRWB: + open.mode_flags |= BUFFER_META_ENABLE; + open.enc_cfg_id = ASM_MEDIA_FMT_AMRWB_FS; + break; + case FORMAT_BESPOKE: + open.mode_flags |= BUFFER_META_ENABLE; + open.enc_cfg_id = enc_cfg_id; + if (ts_mode) + open.mode_flags |= ABSOLUTE_TIMESTAMP_ENABLE; + break; + default: + pr_err("%s: Invalid format 0x%x\n", + __func__, format); + rc = -EINVAL; + goto fail_cmd; + } + rc = apr_send_pkt(ac->apr, (uint32_t *) &open); + if (rc < 0) { + pr_err("%s: open failed op[0x%x]rc[%d]\n", + __func__, open.hdr.opcode, rc); + rc = -EINVAL; + goto fail_cmd; + } + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), + msecs_to_jiffies(TIMEOUT_MS)); + if (!rc) { + pr_err("%s: timeout. waited for open read\n", + __func__); + rc = -ETIMEDOUT; + goto fail_cmd; + } + if (atomic_read(&ac->cmd_state) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->cmd_state))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state)); + goto fail_cmd; + } + + ac->io_mode |= TUN_READ_IO_MODE; + + return 0; +fail_cmd: + return rc; +} + +/** + * q6asm_open_read - + * command to open ASM in read mode + * + * @ac: Audio client handle + * @format: capture format for ASM + * + * Returns 0 on success or error on failure + */ +int q6asm_open_read(struct audio_client *ac, + uint32_t format) +{ + return __q6asm_open_read(ac, format, 16, + PCM_MEDIA_FORMAT_V2 /*media fmt block ver*/, + false/*ts_mode*/, ENC_CFG_ID_NONE); +} +EXPORT_SYMBOL(q6asm_open_read); + +int q6asm_open_read_v2(struct audio_client *ac, uint32_t format, + uint16_t bits_per_sample) +{ + return __q6asm_open_read(ac, format, bits_per_sample, + PCM_MEDIA_FORMAT_V2 /*media fmt block ver*/, + false/*ts_mode*/, ENC_CFG_ID_NONE); +} + +/* + * asm_open_read_v3 - Opens audio capture session + * + * @ac: Client session handle + * @format: encoder format + * @bits_per_sample: bit width of capture session + */ +int q6asm_open_read_v3(struct audio_client *ac, uint32_t format, + uint16_t bits_per_sample) +{ + return __q6asm_open_read(ac, format, bits_per_sample, + PCM_MEDIA_FORMAT_V3/*media fmt block ver*/, + false/*ts_mode*/, ENC_CFG_ID_NONE); +} +EXPORT_SYMBOL(q6asm_open_read_v3); + +/* + * asm_open_read_v4 - Opens audio capture session + * + * @ac: Client session handle + * @format: encoder format + * @bits_per_sample: bit width of capture session + * @ts_mode: timestamp mode + */ +int q6asm_open_read_v4(struct audio_client *ac, uint32_t format, + uint16_t bits_per_sample, bool ts_mode, + uint32_t enc_cfg_id) +{ + return __q6asm_open_read(ac, format, bits_per_sample, + PCM_MEDIA_FORMAT_V4 /*media fmt block ver*/, + ts_mode, enc_cfg_id); +} +EXPORT_SYMBOL(q6asm_open_read_v4); + + +/* + * asm_open_read_v5 - Opens audio capture session + * + * @ac: Client session handle + * @format: encoder format + * @bits_per_sample: bit width of capture session + * @ts_mode: timestamp mode + */ +int q6asm_open_read_v5(struct audio_client *ac, uint32_t format, + uint16_t bits_per_sample, bool ts_mode, uint32_t enc_cfg_id) +{ + return __q6asm_open_read(ac, format, bits_per_sample, + PCM_MEDIA_FORMAT_V5 /*media fmt block ver*/, + ts_mode, enc_cfg_id); +} +EXPORT_SYMBOL(q6asm_open_read_v5); + + +/** + * q6asm_open_write_compressed - + * command to open ASM in compressed write mode + * + * @ac: Audio client handle + * @format: playback format for ASM + * @passthrough_flag: flag to indicate passthrough option + * + * Returns 0 on success or error on failure + */ +int q6asm_open_write_compressed(struct audio_client *ac, uint32_t format, + uint32_t passthrough_flag) +{ + int rc = 0; + struct asm_stream_cmd_open_write_compressed open; + + if (ac == NULL) { + pr_err("%s: ac[%pK] NULL\n", __func__, ac); + rc = -EINVAL; + goto fail_cmd; + } + + if (ac->apr == NULL) { + pr_err("%s: APR handle[%pK] NULL\n", __func__, ac->apr); + rc = -EINVAL; + goto fail_cmd; + } + pr_debug("%s: session[%d] wr_format[0x%x]", __func__, ac->session, + format); + + q6asm_add_hdr(ac, &open.hdr, sizeof(open), TRUE); + open.hdr.opcode = ASM_STREAM_CMD_OPEN_WRITE_COMPRESSED; + atomic_set(&ac->cmd_state, -1); + + switch (format) { + case FORMAT_AC3: + open.fmt_id = ASM_MEDIA_FMT_AC3; + break; + case FORMAT_EAC3: + open.fmt_id = ASM_MEDIA_FMT_EAC3; + break; + case FORMAT_DTS: + open.fmt_id = ASM_MEDIA_FMT_DTS; + break; + case FORMAT_DSD: + open.fmt_id = ASM_MEDIA_FMT_DSD; + break; + case FORMAT_GEN_COMPR: + open.fmt_id = ASM_MEDIA_FMT_GENERIC_COMPRESSED; + break; + case FORMAT_TRUEHD: + open.fmt_id = ASM_MEDIA_FMT_TRUEHD; + break; + case FORMAT_IEC61937: + open.fmt_id = ASM_MEDIA_FMT_IEC; + break; + default: + pr_err("%s: Invalid format[%d]\n", __func__, format); + rc = -EINVAL; + goto fail_cmd; + } + /* Below flag indicates the DSP that Compressed audio input + * stream is not IEC 61937 or IEC 60958 packetizied + */ + if (passthrough_flag == COMPRESSED_PASSTHROUGH || + passthrough_flag == COMPRESSED_PASSTHROUGH_DSD || + passthrough_flag == COMPRESSED_PASSTHROUGH_GEN) { + open.flags = 0x0; + pr_debug("%s: Flag 0 COMPRESSED_PASSTHROUGH\n", __func__); + } else if (passthrough_flag == COMPRESSED_PASSTHROUGH_CONVERT) { + open.flags = 0x8; + pr_debug("%s: Flag 8 - COMPRESSED_PASSTHROUGH_CONVERT\n", + __func__); + } else if (passthrough_flag == COMPRESSED_PASSTHROUGH_IEC61937) { + open.flags = 0x1; + pr_debug("%s: Flag 1 - COMPRESSED_PASSTHROUGH_IEC61937\n", + __func__); + } else { + pr_err("%s: Invalid passthrough type[%d]\n", + __func__, passthrough_flag); + rc = -EINVAL; + goto fail_cmd; + } + rc = apr_send_pkt(ac->apr, (uint32_t *) &open); + if (rc < 0) { + pr_err("%s: open failed op[0x%x]rc[%d]\n", + __func__, open.hdr.opcode, rc); + rc = -EINVAL; + goto fail_cmd; + } + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), + msecs_to_jiffies(TIMEOUT_MS)); + if (!rc) { + pr_err("%s: timeout. waited for OPEN_WRITE_COMPR rc[%d]\n", + __func__, rc); + rc = -ETIMEDOUT; + goto fail_cmd; + } + + if (atomic_read(&ac->cmd_state) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->cmd_state))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state)); + goto fail_cmd; + } + + return 0; + +fail_cmd: + return rc; +} +EXPORT_SYMBOL(q6asm_open_write_compressed); + +static int __q6asm_open_write(struct audio_client *ac, uint32_t format, + uint16_t bits_per_sample, uint32_t stream_id, + bool is_gapless_mode, + uint32_t pcm_format_block_ver) +{ + int rc = 0x00; + struct asm_stream_cmd_open_write_v3 open; + struct q6asm_cal_info cal_info; + + if (ac == NULL) { + pr_err("%s: APR handle NULL\n", __func__); + return -EINVAL; + } + if (ac->apr == NULL) { + pr_err("%s: AC APR handle NULL\n", __func__); + return -EINVAL; + } + + dev_vdbg(ac->dev, "%s: session[%d] wr_format[0x%x]\n", + __func__, ac->session, format); + + q6asm_stream_add_hdr(ac, &open.hdr, sizeof(open), TRUE, stream_id); + atomic_set(&ac->cmd_state, -1); + /* + * Updated the token field with stream/session for compressed playback + * Platform driver must know the the stream with which the command is + * associated + */ + if (ac->io_mode & COMPRESSED_STREAM_IO) + q6asm_update_token(&open.hdr.token, + ac->session, + stream_id, + 0, /* Buffer index is NA */ + 0, /* Direction flag is NA */ + WAIT_CMD); + + dev_vdbg(ac->dev, "%s: token = 0x%x, stream_id %d, session 0x%x\n", + __func__, open.hdr.token, stream_id, ac->session); + open.hdr.opcode = ASM_STREAM_CMD_OPEN_WRITE_V3; + open.mode_flags = 0x00; + if (ac->perf_mode == ULL_POST_PROCESSING_PCM_MODE) + open.mode_flags |= ASM_ULL_POST_PROCESSING_STREAM_SESSION; + else if (ac->perf_mode == ULTRA_LOW_LATENCY_PCM_MODE) + open.mode_flags |= ASM_ULTRA_LOW_LATENCY_STREAM_SESSION; + else if (ac->perf_mode == LOW_LATENCY_PCM_MODE) + open.mode_flags |= ASM_LOW_LATENCY_STREAM_SESSION; + else { + open.mode_flags |= ASM_LEGACY_STREAM_SESSION; + if (is_gapless_mode) + open.mode_flags |= 1 << ASM_SHIFT_GAPLESS_MODE_FLAG; + } + + /* source endpoint : matrix */ + open.sink_endpointype = ASM_END_POINT_DEVICE_MATRIX; + open.bits_per_sample = bits_per_sample; + + rc = q6asm_get_asm_topology_apptype(&cal_info); + open.postprocopo_id = cal_info.topology_id; + + if (ac->perf_mode != LEGACY_PCM_MODE) + open.postprocopo_id = ASM_STREAM_POSTPROCOPO_ID_NONE; + + pr_debug("%s: perf_mode %d asm_topology 0x%x bps %d\n", __func__, + ac->perf_mode, open.postprocopo_id, open.bits_per_sample); + + /* + * For Gapless playback it will use the same session for next stream, + * So use the same topology + */ + if (!ac->topology) { + ac->topology = open.postprocopo_id; + ac->app_type = cal_info.app_type; + } + switch (format) { + case FORMAT_LINEAR_PCM: + open.dec_fmt_id = q6asm_get_pcm_format_id(pcm_format_block_ver); + break; + case FORMAT_MPEG4_AAC: + open.dec_fmt_id = ASM_MEDIA_FMT_AAC_V2; + break; + case FORMAT_MPEG4_MULTI_AAC: + open.dec_fmt_id = ASM_MEDIA_FMT_AAC_V2; + break; + case FORMAT_WMA_V9: + open.dec_fmt_id = ASM_MEDIA_FMT_WMA_V9_V2; + break; + case FORMAT_WMA_V10PRO: + open.dec_fmt_id = ASM_MEDIA_FMT_WMA_V10PRO_V2; + break; + case FORMAT_MP3: + open.dec_fmt_id = ASM_MEDIA_FMT_MP3; + break; + case FORMAT_AC3: + open.dec_fmt_id = ASM_MEDIA_FMT_AC3; + break; + case FORMAT_EAC3: + open.dec_fmt_id = ASM_MEDIA_FMT_EAC3; + break; + case FORMAT_MP2: + open.dec_fmt_id = ASM_MEDIA_FMT_MP2; + break; + case FORMAT_FLAC: + open.dec_fmt_id = ASM_MEDIA_FMT_FLAC; + break; + case FORMAT_ALAC: + open.dec_fmt_id = ASM_MEDIA_FMT_ALAC; + break; + case FORMAT_VORBIS: + open.dec_fmt_id = ASM_MEDIA_FMT_VORBIS; + break; + case FORMAT_APE: + open.dec_fmt_id = ASM_MEDIA_FMT_APE; + break; + case FORMAT_DSD: + open.dec_fmt_id = ASM_MEDIA_FMT_DSD; + break; + case FORMAT_APTX: + open.dec_fmt_id = ASM_MEDIA_FMT_APTX; + break; + case FORMAT_GEN_COMPR: + open.dec_fmt_id = ASM_MEDIA_FMT_GENERIC_COMPRESSED; + break; + default: + pr_err("%s: Invalid format 0x%x\n", __func__, format); + rc = -EINVAL; + goto fail_cmd; + } + rc = apr_send_pkt(ac->apr, (uint32_t *) &open); + if (rc < 0) { + pr_err("%s: open failed op[0x%x]rc[%d]\n", + __func__, open.hdr.opcode, rc); + rc = -EINVAL; + goto fail_cmd; + } + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), + msecs_to_jiffies(TIMEOUT_MS)); + if (!rc) { + pr_err("%s: timeout. waited for open write\n", __func__); + rc = -ETIMEDOUT; + goto fail_cmd; + } + if (atomic_read(&ac->cmd_state) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->cmd_state))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state)); + goto fail_cmd; + } + ac->io_mode |= TUN_WRITE_IO_MODE; + + return 0; +fail_cmd: + return rc; +} + +int q6asm_open_write(struct audio_client *ac, uint32_t format) +{ + return __q6asm_open_write(ac, format, 16, ac->stream_id, + false /*gapless*/, + PCM_MEDIA_FORMAT_V2 /*pcm_format_block_ver*/); +} +EXPORT_SYMBOL(q6asm_open_write); + +int q6asm_open_write_v2(struct audio_client *ac, uint32_t format, + uint16_t bits_per_sample) +{ + return __q6asm_open_write(ac, format, bits_per_sample, + ac->stream_id, false /*gapless*/, + PCM_MEDIA_FORMAT_V2 /*pcm_format_block_ver*/); +} + +/* + * q6asm_open_write_v3 - Opens audio playback session + * + * @ac: Client session handle + * @format: decoder format + * @bits_per_sample: bit width of playback session + */ +int q6asm_open_write_v3(struct audio_client *ac, uint32_t format, + uint16_t bits_per_sample) +{ + return __q6asm_open_write(ac, format, bits_per_sample, + ac->stream_id, false /*gapless*/, + PCM_MEDIA_FORMAT_V3 /*pcm_format_block_ver*/); +} +EXPORT_SYMBOL(q6asm_open_write_v3); + +/* + * q6asm_open_write_v4 - Opens audio playback session + * + * @ac: Client session handle + * @format: decoder format + * @bits_per_sample: bit width of playback session + */ +int q6asm_open_write_v4(struct audio_client *ac, uint32_t format, + uint16_t bits_per_sample) +{ + return __q6asm_open_write(ac, format, bits_per_sample, + ac->stream_id, false /*gapless*/, + PCM_MEDIA_FORMAT_V4 /*pcm_format_block_ver*/); +} +EXPORT_SYMBOL(q6asm_open_write_v4); + +int q6asm_stream_open_write_v2(struct audio_client *ac, uint32_t format, + uint16_t bits_per_sample, int32_t stream_id, + bool is_gapless_mode) +{ + return __q6asm_open_write(ac, format, bits_per_sample, + stream_id, is_gapless_mode, + PCM_MEDIA_FORMAT_V2 /*pcm_format_block_ver*/); +} + +/* + * q6asm_stream_open_write_v3 - Creates audio stream for playback + * + * @ac: Client session handle + * @format: asm playback format + * @bits_per_sample: bit width of requested stream + * @stream_id: stream id of stream to be associated with this session + * @is_gapless_mode: true if gapless mode needs to be enabled + */ +int q6asm_stream_open_write_v3(struct audio_client *ac, uint32_t format, + uint16_t bits_per_sample, int32_t stream_id, + bool is_gapless_mode) +{ + return __q6asm_open_write(ac, format, bits_per_sample, + stream_id, is_gapless_mode, + PCM_MEDIA_FORMAT_V3 /*pcm_format_block_ver*/); +} +EXPORT_SYMBOL(q6asm_stream_open_write_v3); + +/* + * q6asm_open_write_v5 - Opens audio playback session + * + * @ac: Client session handle + * @format: decoder format + * @bits_per_sample: bit width of playback session + */ +int q6asm_open_write_v5(struct audio_client *ac, uint32_t format, + uint16_t bits_per_sample) +{ + return __q6asm_open_write(ac, format, bits_per_sample, + ac->stream_id, false /*gapless*/, + PCM_MEDIA_FORMAT_V5 /*pcm_format_block_ver*/); +} +EXPORT_SYMBOL(q6asm_open_write_v5); + + +/* + * q6asm_stream_open_write_v4 - Creates audio stream for playback + * + * @ac: Client session handle + * @format: asm playback format + * @bits_per_sample: bit width of requested stream + * @stream_id: stream id of stream to be associated with this session + * @is_gapless_mode: true if gapless mode needs to be enabled + */ +int q6asm_stream_open_write_v4(struct audio_client *ac, uint32_t format, + uint16_t bits_per_sample, int32_t stream_id, + bool is_gapless_mode) +{ + return __q6asm_open_write(ac, format, bits_per_sample, + stream_id, is_gapless_mode, + PCM_MEDIA_FORMAT_V4 /*pcm_format_block_ver*/); +} +EXPORT_SYMBOL(q6asm_stream_open_write_v4); + +/* + * q6asm_stream_open_write_v5 - Creates audio stream for playback + * + * @ac: Client session handle + * @format: asm playback format + * @bits_per_sample: bit width of requested stream + * @stream_id: stream id of stream to be associated with this session + * @is_gapless_mode: true if gapless mode needs to be enabled + */ +int q6asm_stream_open_write_v5(struct audio_client *ac, uint32_t format, + uint16_t bits_per_sample, int32_t stream_id, + bool is_gapless_mode) +{ + return __q6asm_open_write(ac, format, bits_per_sample, + stream_id, is_gapless_mode, + PCM_MEDIA_FORMAT_V5 /*pcm_format_block_ver*/); +} +EXPORT_SYMBOL(q6asm_stream_open_write_v5); + + +static int __q6asm_open_read_write(struct audio_client *ac, uint32_t rd_format, + uint32_t wr_format, bool is_meta_data_mode, + uint32_t bits_per_sample, + bool overwrite_topology, int topology) +{ + int rc = 0x00; + struct asm_stream_cmd_open_readwrite_v2 open; + struct q6asm_cal_info cal_info; + + if (ac == NULL) { + pr_err("%s: APR handle NULL\n", __func__); + return -EINVAL; + } + if (ac->apr == NULL) { + pr_err("%s: AC APR handle NULL\n", __func__); + return -EINVAL; + } + pr_debug("%s: session[%d]\n", __func__, ac->session); + pr_debug("%s: wr_format[0x%x]rd_format[0x%x]\n", + __func__, wr_format, rd_format); + + ac->io_mode |= NT_MODE; + q6asm_add_hdr(ac, &open.hdr, sizeof(open), TRUE); + atomic_set(&ac->cmd_state, -1); + open.hdr.opcode = ASM_STREAM_CMD_OPEN_READWRITE_V2; + + open.mode_flags = is_meta_data_mode ? BUFFER_META_ENABLE : 0; + open.bits_per_sample = bits_per_sample; + /* source endpoint : matrix */ + rc = q6asm_get_asm_topology_apptype(&cal_info); + open.postprocopo_id = cal_info.topology_id; + + open.postprocopo_id = overwrite_topology ? + topology : open.postprocopo_id; + ac->topology = open.postprocopo_id; + ac->app_type = cal_info.app_type; + + + switch (wr_format) { + case FORMAT_LINEAR_PCM: + case FORMAT_MULTI_CHANNEL_LINEAR_PCM: + open.dec_fmt_id = ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V2; + break; + case FORMAT_MPEG4_AAC: + open.dec_fmt_id = ASM_MEDIA_FMT_AAC_V2; + break; + case FORMAT_MPEG4_MULTI_AAC: + open.dec_fmt_id = ASM_MEDIA_FMT_AAC_V2; + break; + case FORMAT_WMA_V9: + open.dec_fmt_id = ASM_MEDIA_FMT_WMA_V9_V2; + break; + case FORMAT_WMA_V10PRO: + open.dec_fmt_id = ASM_MEDIA_FMT_WMA_V10PRO_V2; + break; + case FORMAT_AMRNB: + open.dec_fmt_id = ASM_MEDIA_FMT_AMRNB_FS; + break; + case FORMAT_AMRWB: + open.dec_fmt_id = ASM_MEDIA_FMT_AMRWB_FS; + break; + case FORMAT_AMR_WB_PLUS: + open.dec_fmt_id = ASM_MEDIA_FMT_AMR_WB_PLUS_V2; + break; + case FORMAT_V13K: + open.dec_fmt_id = ASM_MEDIA_FMT_V13K_FS; + break; + case FORMAT_EVRC: + open.dec_fmt_id = ASM_MEDIA_FMT_EVRC_FS; + break; + case FORMAT_EVRCB: + open.dec_fmt_id = ASM_MEDIA_FMT_EVRCB_FS; + break; + case FORMAT_EVRCWB: + open.dec_fmt_id = ASM_MEDIA_FMT_EVRCWB_FS; + break; + case FORMAT_MP3: + open.dec_fmt_id = ASM_MEDIA_FMT_MP3; + break; + case FORMAT_ALAC: + open.dec_fmt_id = ASM_MEDIA_FMT_ALAC; + break; + case FORMAT_APE: + open.dec_fmt_id = ASM_MEDIA_FMT_APE; + break; + case FORMAT_DSD: + open.dec_fmt_id = ASM_MEDIA_FMT_DSD; + break; + case FORMAT_G711_ALAW_FS: + open.dec_fmt_id = ASM_MEDIA_FMT_G711_ALAW_FS; + break; + case FORMAT_G711_MLAW_FS: + open.dec_fmt_id = ASM_MEDIA_FMT_G711_MLAW_FS; + break; + default: + pr_err("%s: Invalid format 0x%x\n", + __func__, wr_format); + rc = -EINVAL; + goto fail_cmd; + } + + switch (rd_format) { + case FORMAT_LINEAR_PCM: + case FORMAT_MULTI_CHANNEL_LINEAR_PCM: + open.enc_cfg_id = ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V2; + break; + case FORMAT_MPEG4_AAC: + open.enc_cfg_id = ASM_MEDIA_FMT_AAC_V2; + break; + case FORMAT_G711_ALAW_FS: + open.enc_cfg_id = ASM_MEDIA_FMT_G711_ALAW_FS; + break; + case FORMAT_G711_MLAW_FS: + open.enc_cfg_id = ASM_MEDIA_FMT_G711_MLAW_FS; + break; + case FORMAT_V13K: + open.enc_cfg_id = ASM_MEDIA_FMT_V13K_FS; + break; + case FORMAT_EVRC: + open.enc_cfg_id = ASM_MEDIA_FMT_EVRC_FS; + break; + case FORMAT_AMRNB: + open.enc_cfg_id = ASM_MEDIA_FMT_AMRNB_FS; + break; + case FORMAT_AMRWB: + open.enc_cfg_id = ASM_MEDIA_FMT_AMRWB_FS; + break; + case FORMAT_ALAC: + open.enc_cfg_id = ASM_MEDIA_FMT_ALAC; + break; + case FORMAT_APE: + open.enc_cfg_id = ASM_MEDIA_FMT_APE; + break; + default: + pr_err("%s: Invalid format 0x%x\n", + __func__, rd_format); + rc = -EINVAL; + goto fail_cmd; + } + dev_vdbg(ac->dev, "%s: rdformat[0x%x]wrformat[0x%x]\n", __func__, + open.enc_cfg_id, open.dec_fmt_id); + + rc = apr_send_pkt(ac->apr, (uint32_t *) &open); + if (rc < 0) { + pr_err("%s: open failed op[0x%x]rc[%d]\n", + __func__, open.hdr.opcode, rc); + rc = -EINVAL; + goto fail_cmd; + } + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), + msecs_to_jiffies(TIMEOUT_MS)); + if (!rc) { + pr_err("%s: timeout. waited for open read-write\n", + __func__); + rc = -ETIMEDOUT; + goto fail_cmd; + } + if (atomic_read(&ac->cmd_state) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->cmd_state))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state)); + goto fail_cmd; + } + + return 0; +fail_cmd: + return rc; +} + +/** + * q6asm_open_read_write - + * command to open ASM in read/write mode + * + * @ac: Audio client handle + * @rd_format: capture format for ASM + * @wr_format: playback format for ASM + * + * Returns 0 on success or error on failure + */ +int q6asm_open_read_write(struct audio_client *ac, uint32_t rd_format, + uint32_t wr_format) +{ + return __q6asm_open_read_write(ac, rd_format, wr_format, + true/*meta data mode*/, + 16 /*bits_per_sample*/, + false /*overwrite_topology*/, 0); +} +EXPORT_SYMBOL(q6asm_open_read_write); + +/** + * q6asm_open_read_write_v2 - + * command to open ASM in bi-directional read/write mode + * + * @ac: Audio client handle + * @rd_format: capture format for ASM + * @wr_format: playback format for ASM + * @is_meta_data_mode: mode to indicate if meta data present + * @bits_per_sample: number of bits per sample + * @overwrite_topology: topology to be overwritten flag + * @topology: Topology for ASM + * + * Returns 0 on success or error on failure + */ +int q6asm_open_read_write_v2(struct audio_client *ac, uint32_t rd_format, + uint32_t wr_format, bool is_meta_data_mode, + uint32_t bits_per_sample, bool overwrite_topology, + int topology) +{ + return __q6asm_open_read_write(ac, rd_format, wr_format, + is_meta_data_mode, bits_per_sample, + overwrite_topology, topology); +} +EXPORT_SYMBOL(q6asm_open_read_write_v2); + +/** + * q6asm_open_loopback_v2 - + * command to open ASM in loopback mode + * + * @ac: Audio client handle + * @bits_per_sample: number of bits per sample + * + * Returns 0 on success or error on failure + */ +int q6asm_open_loopback_v2(struct audio_client *ac, uint16_t bits_per_sample) +{ + int rc = 0x00; + struct q6asm_cal_info cal_info; + + if (ac == NULL) { + pr_err("%s: APR handle NULL\n", __func__); + return -EINVAL; + } + if (ac->apr == NULL) { + pr_err("%s: AC APR handle NULL\n", __func__); + return -EINVAL; + } + pr_debug("%s: session[%d]\n", __func__, ac->session); + + if (ac->perf_mode == LOW_LATENCY_PCM_MODE) { + struct asm_stream_cmd_open_transcode_loopback_t open; + + q6asm_add_hdr(ac, &open.hdr, sizeof(open), TRUE); + atomic_set(&ac->cmd_state, -1); + open.hdr.opcode = ASM_STREAM_CMD_OPEN_TRANSCODE_LOOPBACK; + + open.mode_flags = 0; + open.src_endpoint_type = 0; + open.sink_endpoint_type = 0; + open.src_format_id = ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V2; + open.sink_format_id = ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V2; + /* source endpoint : matrix */ + rc = q6asm_get_asm_topology_apptype(&cal_info); + open.audproc_topo_id = cal_info.topology_id; + + ac->app_type = cal_info.app_type; + if (ac->perf_mode == LOW_LATENCY_PCM_MODE) + open.mode_flags |= ASM_LOW_LATENCY_STREAM_SESSION; + else + open.mode_flags |= ASM_LEGACY_STREAM_SESSION; + ac->topology = open.audproc_topo_id; + open.bits_per_sample = bits_per_sample; + open.reserved = 0; + pr_debug("%s: opening a transcode_loopback with mode_flags =[%d] session[%d]\n", + __func__, open.mode_flags, ac->session); + + rc = apr_send_pkt(ac->apr, (uint32_t *) &open); + if (rc < 0) { + pr_err("%s: open failed op[0x%x]rc[%d]\n", + __func__, open.hdr.opcode, rc); + rc = -EINVAL; + goto fail_cmd; + } + } else {/*if(ac->perf_mode == LEGACY_PCM_MODE)*/ + struct asm_stream_cmd_open_loopback_v2 open; + + q6asm_add_hdr(ac, &open.hdr, sizeof(open), TRUE); + atomic_set(&ac->cmd_state, -1); + open.hdr.opcode = ASM_STREAM_CMD_OPEN_LOOPBACK_V2; + + open.mode_flags = 0; + open.src_endpointype = 0; + open.sink_endpointype = 0; + /* source endpoint : matrix */ + rc = q6asm_get_asm_topology_apptype(&cal_info); + open.postprocopo_id = cal_info.topology_id; + + ac->app_type = cal_info.app_type; + ac->topology = open.postprocopo_id; + open.bits_per_sample = bits_per_sample; + open.reserved = 0; + pr_debug("%s: opening a loopback_v2 with mode_flags =[%d] session[%d]\n", + __func__, open.mode_flags, ac->session); + + rc = apr_send_pkt(ac->apr, (uint32_t *) &open); + if (rc < 0) { + pr_err("%s: open failed op[0x%x]rc[%d]\n", + __func__, open.hdr.opcode, rc); + rc = -EINVAL; + goto fail_cmd; + } + } + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), + msecs_to_jiffies(TIMEOUT_MS)); + if (!rc) { + pr_err("%s: timeout. waited for open_loopback\n", + __func__); + rc = -ETIMEDOUT; + goto fail_cmd; + } + if (atomic_read(&ac->cmd_state) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->cmd_state))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state)); + goto fail_cmd; + } + + return 0; +fail_cmd: + return rc; +} +EXPORT_SYMBOL(q6asm_open_loopback_v2); + +/** + * q6asm_open_transcode_loopback - + * command to open ASM in transcode loopback mode + * + * @ac: Audio client handle + * @bits_per_sample: number of bits per sample + * @source_format: Format of clip + * @sink_format: end device supported format + * + * Returns 0 on success or error on failure + */ +int q6asm_open_transcode_loopback(struct audio_client *ac, + uint16_t bits_per_sample, + uint32_t source_format, uint32_t sink_format) +{ + int rc = 0x00; + struct asm_stream_cmd_open_transcode_loopback_t open; + struct q6asm_cal_info cal_info; + + if (ac == NULL) { + pr_err("%s: APR handle NULL\n", __func__); + return -EINVAL; + } + if (ac->apr == NULL) { + pr_err("%s: AC APR handle NULL\n", __func__); + return -EINVAL; + } + + pr_debug("%s: session[%d]\n", __func__, ac->session); + + q6asm_add_hdr(ac, &open.hdr, sizeof(open), TRUE); + atomic_set(&ac->cmd_state, -1); + open.hdr.opcode = ASM_STREAM_CMD_OPEN_TRANSCODE_LOOPBACK; + + open.mode_flags = 0; + open.src_endpoint_type = 0; + open.sink_endpoint_type = 0; + switch (source_format) { + case FORMAT_LINEAR_PCM: + case FORMAT_MULTI_CHANNEL_LINEAR_PCM: + open.src_format_id = ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V3; + break; + case FORMAT_AC3: + open.src_format_id = ASM_MEDIA_FMT_AC3; + break; + case FORMAT_EAC3: + open.src_format_id = ASM_MEDIA_FMT_EAC3; + break; + default: + pr_err("%s: Unsupported src fmt [%d]\n", + __func__, source_format); + return -EINVAL; + } + switch (sink_format) { + case FORMAT_LINEAR_PCM: + case FORMAT_MULTI_CHANNEL_LINEAR_PCM: + open.sink_format_id = ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V3; + break; + default: + pr_err("%s: Unsupported sink fmt [%d]\n", + __func__, sink_format); + return -EINVAL; + } + + /* source endpoint : matrix */ + rc = q6asm_get_asm_topology_apptype(&cal_info); + open.audproc_topo_id = cal_info.topology_id; + + + ac->app_type = cal_info.app_type; + if (ac->perf_mode == LOW_LATENCY_PCM_MODE) + open.mode_flags |= ASM_LOW_LATENCY_STREAM_SESSION; + else + open.mode_flags |= ASM_LEGACY_STREAM_SESSION; + ac->topology = open.audproc_topo_id; + open.bits_per_sample = bits_per_sample; + open.reserved = 0; + pr_debug("%s: opening a transcode_loopback with mode_flags =[%d] session[%d]\n", + __func__, open.mode_flags, ac->session); + + rc = apr_send_pkt(ac->apr, (uint32_t *) &open); + if (rc < 0) { + pr_err("%s: open failed op[0x%x]rc[%d]\n", + __func__, open.hdr.opcode, rc); + rc = -EINVAL; + goto fail_cmd; + } + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), + msecs_to_jiffies(TIMEOUT_MS)); + if (!rc) { + pr_err("%s: timeout. waited for open_transcode_loopback\n", + __func__); + rc = -ETIMEDOUT; + goto fail_cmd; + } + if (atomic_read(&ac->cmd_state) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->cmd_state))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state)); + goto fail_cmd; + } + + return 0; +fail_cmd: + return rc; +} +EXPORT_SYMBOL(q6asm_open_transcode_loopback); + +static +int q6asm_set_shared_circ_buff(struct audio_client *ac, + struct asm_stream_cmd_open_shared_io *open, + int bufsz, int bufcnt, + int dir) +{ + struct audio_buffer *buf_circ; + int bytes_to_alloc, rc; + size_t len; + + mutex_lock(&ac->cmd_lock); + + if (ac->port[dir].buf) { + pr_err("%s: Buffer already allocated\n", __func__); + rc = -EINVAL; + goto done; + } + + buf_circ = kzalloc(sizeof(struct audio_buffer), GFP_KERNEL); + + if (!buf_circ) { + rc = -ENOMEM; + goto done; + } + + bytes_to_alloc = bufsz * bufcnt; + bytes_to_alloc = PAGE_ALIGN(bytes_to_alloc); + + rc = msm_audio_ion_alloc(&buf_circ->dma_buf, + bytes_to_alloc, + &buf_circ->phys, + &len, &buf_circ->data); + + if (rc) { + pr_err("%s: Audio ION alloc is failed, rc = %d\n", __func__, + rc); + kfree(buf_circ); + goto done; + } + + ac->port[dir].buf = buf_circ; + buf_circ->used = dir ^ 1; + buf_circ->size = bytes_to_alloc; + buf_circ->actual_size = bytes_to_alloc; + memset(buf_circ->data, 0, buf_circ->actual_size); + + ac->port[dir].max_buf_cnt = 1; + + open->shared_circ_buf_mem_pool_id = ADSP_MEMORY_MAP_SHMEM8_4K_POOL; + open->shared_circ_buf_num_regions = 1; + open->shared_circ_buf_property_flag = 0x00; + open->shared_circ_buf_start_phy_addr_lsw = + lower_32_bits(buf_circ->phys); + open->shared_circ_buf_start_phy_addr_msw = + msm_audio_populate_upper_32_bits(buf_circ->phys); + open->shared_circ_buf_size = bufsz * bufcnt; + + open->map_region_circ_buf.shm_addr_lsw = lower_32_bits(buf_circ->phys); + open->map_region_circ_buf.shm_addr_msw = + msm_audio_populate_upper_32_bits(buf_circ->phys); + open->map_region_circ_buf.mem_size_bytes = bytes_to_alloc; + +done: + mutex_unlock(&ac->cmd_lock); + return rc; +} + + +static +int q6asm_set_shared_pos_buff(struct audio_client *ac, + struct asm_stream_cmd_open_shared_io *open, + int dir) +{ + struct audio_buffer *buf_pos = &ac->shared_pos_buf; + int rc; + size_t len; + int bytes_to_alloc = sizeof(struct asm_shared_position_buffer); + + mutex_lock(&ac->cmd_lock); + + bytes_to_alloc = PAGE_ALIGN(bytes_to_alloc); + + rc = msm_audio_ion_alloc(&buf_pos->dma_buf, + bytes_to_alloc, + &buf_pos->phys, &len, + &buf_pos->data); + + if (rc) { + pr_err("%s: Audio pos buf ION alloc is failed, rc = %d\n", + __func__, rc); + goto done; + } + + buf_pos->used = dir ^ 1; + buf_pos->size = bytes_to_alloc; + buf_pos->actual_size = bytes_to_alloc; + + open->shared_pos_buf_mem_pool_id = ADSP_MEMORY_MAP_SHMEM8_4K_POOL; + open->shared_pos_buf_num_regions = 1; + open->shared_pos_buf_property_flag = 0x00; + open->shared_pos_buf_phy_addr_lsw = lower_32_bits(buf_pos->phys); + open->shared_pos_buf_phy_addr_msw = + msm_audio_populate_upper_32_bits(buf_pos->phys); + + open->map_region_pos_buf.shm_addr_lsw = lower_32_bits(buf_pos->phys); + open->map_region_pos_buf.shm_addr_msw = + msm_audio_populate_upper_32_bits(buf_pos->phys); + open->map_region_pos_buf.mem_size_bytes = bytes_to_alloc; + +done: + mutex_unlock(&ac->cmd_lock); + return rc; +} + +/* + * q6asm_open_shared_io: Open an ASM session for pull mode (playback) + * or push mode (capture). + * parameters + * config - session parameters (channels, bits_per_sample, sr) + * dir - stream direction (IN for playback, OUT for capture) + * use_default_chmap: true if default channel map to be used + * channel_map: input channel map + * returns 0 if successful, error code otherwise + */ +int q6asm_open_shared_io(struct audio_client *ac, + struct shared_io_config *config, + int dir, bool use_default_chmap, u8 *channel_map) +{ + struct asm_stream_cmd_open_shared_io *open; + u8 *channel_mapping; + int i, size_of_open, num_watermarks, bufsz, bufcnt, rc, flags = 0; + struct q6asm_cal_info cal_info; + + if (!ac || !config) + return -EINVAL; + + if (!use_default_chmap && (channel_map == NULL)) { + pr_err("%s: No valid chan map and can't use default\n", + __func__); + return -EINVAL; + } + + if (config->channels > PCM_FORMAT_MAX_NUM_CHANNEL) { + pr_err("%s: Invalid channel count %d\n", __func__, + config->channels); + return -EINVAL; + } + + bufsz = config->bufsz; + bufcnt = config->bufcnt; + num_watermarks = 0; + + ac->config = *config; + + if (ac->session <= 0 || ac->session > SESSION_MAX) { + pr_err("%s: Session %d is out of bounds\n", + __func__, ac->session); + return -EINVAL; + } + + size_of_open = sizeof(struct asm_stream_cmd_open_shared_io) + + (sizeof(struct asm_shared_watermark_level) * num_watermarks); + + open = kzalloc(PAGE_ALIGN(size_of_open), GFP_KERNEL); + if (!open) + return -ENOMEM; + + q6asm_stream_add_hdr(ac, &open->hdr, size_of_open, TRUE, + ac->stream_id); + + atomic_set(&ac->cmd_state, 1); + + pr_debug("%s: token = 0x%x, stream_id %d, session 0x%x, perf %d\n", + __func__, open->hdr.token, ac->stream_id, ac->session, + ac->perf_mode); + + open->hdr.opcode = + dir == IN ? ASM_STREAM_CMD_OPEN_PULL_MODE_WRITE : + ASM_STREAM_CMD_OPEN_PUSH_MODE_READ; + + pr_debug("%s perf_mode %d\n", __func__, ac->perf_mode); + if (dir == IN) + if (ac->perf_mode == ULL_POST_PROCESSING_PCM_MODE) + flags = 4 << ASM_SHIFT_STREAM_PERF_FLAG_PULL_MODE_WRITE; + else if (ac->perf_mode == ULTRA_LOW_LATENCY_PCM_MODE) + flags = 2 << ASM_SHIFT_STREAM_PERF_FLAG_PULL_MODE_WRITE; + else if (ac->perf_mode == LOW_LATENCY_PCM_MODE) + flags = 1 << ASM_SHIFT_STREAM_PERF_FLAG_PULL_MODE_WRITE; + else + pr_err("Invalid perf mode for pull write\n"); + else + if (ac->perf_mode == LOW_LATENCY_PCM_MODE) + flags = ASM_LOW_LATENCY_TX_STREAM_SESSION << + ASM_SHIFT_STREAM_PERF_FLAG_PUSH_MODE_READ; + else + pr_err("Invalid perf mode for push read\n"); + + if (flags == 0) { + pr_err("%s: Invalid mode[%d]\n", __func__, + ac->perf_mode); + kfree(open); + return -EINVAL; + + } + + pr_debug("open.mode_flags = 0x%x\n", flags); + open->mode_flags = flags; + open->endpoint_type = ASM_END_POINT_DEVICE_MATRIX; + open->topo_bits_per_sample = config->bits_per_sample; + + rc = q6asm_get_asm_topology_apptype(&cal_info); + open->topo_id = cal_info.topology_id; + + if (config->format == FORMAT_LINEAR_PCM) + open->fmt_id = ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V3; + else { + pr_err("%s: Invalid format[%d]\n", __func__, config->format); + rc = -EINVAL; + goto done; + } + + rc = q6asm_set_shared_circ_buff(ac, open, bufsz, bufcnt, dir); + + if (rc) + goto done; + + ac->port[dir].tmp_hdl = 0; + + rc = q6asm_set_shared_pos_buff(ac, open, dir); + + if (rc) + goto done; + + /* asm_multi_channel_pcm_fmt_blk_v3 */ + open->fmt.num_channels = config->channels; + open->fmt.bits_per_sample = config->bits_per_sample; + open->fmt.sample_rate = config->rate; + open->fmt.is_signed = 1; + open->fmt.sample_word_size = config->sample_word_size; + + channel_mapping = open->fmt.channel_mapping; + + memset(channel_mapping, 0, PCM_FORMAT_MAX_NUM_CHANNEL); + + if (use_default_chmap) { + rc = q6asm_map_channels(channel_mapping, config->channels, + false); + if (rc) { + pr_err("%s: Map channels failed, ret: %d\n", + __func__, rc); + goto done; + } + } else { + memcpy(channel_mapping, channel_map, + PCM_FORMAT_MAX_NUM_CHANNEL); + } + + open->num_watermark_levels = num_watermarks; + for (i = 0; i < num_watermarks; i++) { + open->watermark[i].watermark_level_bytes = i * + ((bufsz * bufcnt) / num_watermarks); + pr_debug("%s: Watermark level set for %i\n", + __func__, + open->watermark[i].watermark_level_bytes); + } + + rc = apr_send_pkt(ac->apr, (uint32_t *) open); + if (rc < 0) { + pr_err("%s: Open failed op[0x%x]rc[%d]\n", + __func__, open->hdr.opcode, rc); + goto done; + } + + pr_debug("%s: sent open apr pkt\n", __func__); + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) <= 0), + msecs_to_jiffies(TIMEOUT_MS)); + if (!rc) { + pr_err("%s: Timeout. Waited for open write apr pkt rc[%d]\n", + __func__, rc); + rc = -ETIMEDOUT; + goto done; + } + + if (atomic_read(&ac->cmd_state) < 0) { + pr_err("%s: DSP returned error [%d]\n", __func__, + atomic_read(&ac->cmd_state)); + rc = -EINVAL; + goto done; + } + + ac->io_mode |= TUN_WRITE_IO_MODE; + rc = 0; +done: + kfree(open); + return rc; +} +EXPORT_SYMBOL(q6asm_open_shared_io); + +/* + * q6asm_shared_io_buf: Returns handle to the shared circular buffer being + * used for pull/push mode. + * parameters + * dir - used to identify input/output port + * returns buffer handle + */ +struct audio_buffer *q6asm_shared_io_buf(struct audio_client *ac, + int dir) +{ + struct audio_port_data *port; + + if (!ac) { + pr_err("%s: ac is null\n", __func__); + return NULL; + } + port = &ac->port[dir]; + return port->buf; +} +EXPORT_SYMBOL(q6asm_shared_io_buf); + +/* + * q6asm_shared_io_free: Frees memory allocated for a pull/push session + * parameters + * dir - port direction + * returns 0 if successful, error otherwise + */ +int q6asm_shared_io_free(struct audio_client *ac, int dir) +{ + struct audio_port_data *port; + + if (!ac) { + pr_err("%s: audio client is null\n", __func__); + return -EINVAL; + } + port = &ac->port[dir]; + mutex_lock(&ac->cmd_lock); + if (port->buf && port->buf->data) { + msm_audio_ion_free(port->buf->dma_buf); + port->buf->dma_buf = NULL; + port->max_buf_cnt = 0; + kfree(port->buf); + port->buf = NULL; + } + if (ac->shared_pos_buf.data) { + msm_audio_ion_free(ac->shared_pos_buf.dma_buf); + ac->shared_pos_buf.dma_buf = NULL; + } + mutex_unlock(&ac->cmd_lock); + return 0; +} +EXPORT_SYMBOL(q6asm_shared_io_free); + +/* + * q6asm_get_shared_pos: Returns current read index/write index as observed + * by the DSP. Note that this is an offset and iterates from [0,BUF_SIZE - 1] + * parameters - (all output) + * read_index - offset + * wall_clk_msw1 - ADSP wallclock msw + * wall_clk_lsw1 - ADSP wallclock lsw + * returns 0 if successful, -EAGAIN if DSP failed to update after some + * retries + */ +int q6asm_get_shared_pos(struct audio_client *ac, uint32_t *read_index, + uint32_t *wall_clk_msw1, uint32_t *wall_clk_lsw1) +{ + struct asm_shared_position_buffer *pos_buf; + uint32_t frame_cnt1, frame_cnt2; + int i, j; + + if (!ac) { + pr_err("%s: audio client is null\n", __func__); + return -EINVAL; + } + + pos_buf = ac->shared_pos_buf.data; + + /* always try to get the latest update in the shared pos buffer */ + for (i = 0; i < 2; i++) { + /* retry until there is an update from DSP */ + for (j = 0; j < 5; j++) { + frame_cnt1 = pos_buf->frame_counter; + if (frame_cnt1 != 0) + break; + } + + *wall_clk_msw1 = pos_buf->wall_clock_us_msw; + *wall_clk_lsw1 = pos_buf->wall_clock_us_lsw; + *read_index = pos_buf->index; + frame_cnt2 = pos_buf->frame_counter; + + if (frame_cnt1 != frame_cnt2) + continue; + return 0; + } + pr_err("%s out of tries trying to get a good read, try again\n", + __func__); + return -EAGAIN; +} +EXPORT_SYMBOL(q6asm_get_shared_pos); + +/** + * q6asm_run - + * command to set ASM to run state + * + * @ac: Audio client handle + * @flags: Flags for session + * @msw_ts: upper 32bits timestamp + * @lsw_ts: lower 32bits timestamp + * + * Returns 0 on success or error on failure + */ +int q6asm_run(struct audio_client *ac, uint32_t flags, + uint32_t msw_ts, uint32_t lsw_ts) +{ + struct asm_session_cmd_run_v2 run; + int rc; + + if (ac == NULL) { + pr_err("%s: APR handle NULL\n", __func__); + return -EINVAL; + } + if (ac->apr == NULL) { + pr_err("%s: AC APR handle NULL\n", __func__); + return -EINVAL; + } + pr_debug("%s: session[%d]\n", __func__, ac->session); + + q6asm_add_hdr(ac, &run.hdr, sizeof(run), TRUE); + atomic_set(&ac->cmd_state, -1); + + run.hdr.opcode = ASM_SESSION_CMD_RUN_V2; + run.flags = flags; + run.time_lsw = lsw_ts; + run.time_msw = msw_ts; + + config_debug_fs_run(); + + rc = apr_send_pkt(ac->apr, (uint32_t *) &run); + if (rc < 0) { + pr_err("%s: Commmand run failed[%d]", + __func__, rc); + rc = -EINVAL; + goto fail_cmd; + } + + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), + msecs_to_jiffies(TIMEOUT_MS)); + if (!rc) { + pr_err("%s: timeout. waited for run success", + __func__); + rc = -ETIMEDOUT; + goto fail_cmd; + } + if (atomic_read(&ac->cmd_state) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->cmd_state))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state)); + goto fail_cmd; + } + + return 0; +fail_cmd: + return rc; +} +EXPORT_SYMBOL(q6asm_run); + +static int __q6asm_run_nowait(struct audio_client *ac, uint32_t flags, + uint32_t msw_ts, uint32_t lsw_ts, uint32_t stream_id) +{ + struct asm_session_cmd_run_v2 run; + int rc; + + if (ac == NULL) { + pr_err("%s: APR handle NULL\n", __func__); + return -EINVAL; + } + if (ac->apr == NULL) { + pr_err("%s: AC APR handle NULL\n", __func__); + return -EINVAL; + } + pr_debug("%s: session[%d]\n", __func__, ac->session); + + q6asm_stream_add_hdr_async(ac, &run.hdr, sizeof(run), TRUE, stream_id); + atomic_set(&ac->cmd_state, 1); + run.hdr.opcode = ASM_SESSION_CMD_RUN_V2; + run.flags = flags; + run.time_lsw = lsw_ts; + run.time_msw = msw_ts; + + rc = apr_send_pkt(ac->apr, (uint32_t *) &run); + if (rc < 0) { + pr_err("%s: Commmand run failed[%d]", __func__, rc); + return -EINVAL; + } + return 0; +} + +/** + * q6asm_run_nowait - + * command to set ASM to run state with no wait for ack + * + * @ac: Audio client handle + * @flags: Flags for session + * @msw_ts: upper 32bits timestamp + * @lsw_ts: lower 32bits timestamp + * + * Returns 0 on success or error on failure + */ +int q6asm_run_nowait(struct audio_client *ac, uint32_t flags, + uint32_t msw_ts, uint32_t lsw_ts) +{ + return __q6asm_run_nowait(ac, flags, msw_ts, lsw_ts, ac->stream_id); +} +EXPORT_SYMBOL(q6asm_run_nowait); + +int q6asm_stream_run_nowait(struct audio_client *ac, uint32_t flags, + uint32_t msw_ts, uint32_t lsw_ts, uint32_t stream_id) +{ + return __q6asm_run_nowait(ac, flags, msw_ts, lsw_ts, stream_id); +} + +/** + * q6asm_enc_cfg_blk_custom - + * command to set encode cfg block for custom + * + * @ac: Audio client handle + * @sample_rate: Sample rate + * @channels: number of ASM channels + * @format: custom format flag + * @cfg: generic encoder config + * + * Returns 0 on success or error on failure + */ +int q6asm_enc_cfg_blk_custom(struct audio_client *ac, + uint32_t sample_rate, uint32_t channels, + uint32_t format, void *cfg) +{ + struct asm_custom_enc_cfg_t_v2 enc_cfg; + int rc = 0; + uint32_t custom_size; + struct snd_enc_generic *enc_generic = (struct snd_enc_generic *) cfg; + + custom_size = enc_generic->reserved[1]; + + if (channels > PCM_FORMAT_MAX_NUM_CHANNEL) { + pr_err("%s: Invalid channel count %d\n", __func__, channels); + return -EINVAL; + } + + pr_debug("%s: session[%d] size[%d] res[2]=[%d] res[3]=[%d]\n", + __func__, ac->session, custom_size, enc_generic->reserved[2], + enc_generic->reserved[3]); + + pr_debug("%s: res[4]=[%d] sr[%d] ch[%d] format[%d]\n", + __func__, enc_generic->reserved[4], sample_rate, + channels, format); + + memset(&enc_cfg, 0, sizeof(struct asm_custom_enc_cfg_t_v2)); + q6asm_add_hdr(ac, &enc_cfg.hdr, sizeof(enc_cfg), TRUE); + atomic_set(&ac->cmd_state, -1); + + enc_cfg.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM; + enc_cfg.encdec.param_id = ASM_PARAM_ID_ENCDEC_ENC_CFG_BLK_V2; + enc_cfg.encdec.param_size = sizeof(struct asm_custom_enc_cfg_t_v2) - + sizeof(struct asm_stream_cmd_set_encdec_param); + enc_cfg.encblk.frames_per_buf = ENC_FRAMES_PER_BUFFER; + enc_cfg.encblk.enc_cfg_blk_size = enc_cfg.encdec.param_size - + sizeof(struct asm_enc_cfg_blk_param_v2); + + enc_cfg.num_channels = channels; + enc_cfg.sample_rate = sample_rate; + + if (q6asm_map_channels(enc_cfg.channel_mapping, channels, false)) { + pr_err("%s: map channels failed %d\n", + __func__, channels); + rc = -EINVAL; + goto fail_cmd; + } + + if (format == FORMAT_BESPOKE && custom_size && + custom_size <= sizeof(enc_cfg.custom_data)) { + memcpy(enc_cfg.custom_data, &enc_generic->reserved[2], + custom_size); + enc_cfg.custom_size = custom_size; + } + + rc = apr_send_pkt(ac->apr, (uint32_t *) &enc_cfg); + if (rc < 0) { + pr_err("%s: Comamnd %d failed %d\n", + __func__, ASM_STREAM_CMD_SET_ENCDEC_PARAM, rc); + rc = -EINVAL; + goto fail_cmd; + } + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), + msecs_to_jiffies(TIMEOUT_MS)); + if (!rc) { + pr_err("%s: timeout. waited for FORMAT_UPDATE\n", + __func__); + rc = -ETIMEDOUT; + goto fail_cmd; + } + if (atomic_read(&ac->cmd_state) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->cmd_state))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state)); + goto fail_cmd; + } + return 0; +fail_cmd: + return rc; +} +EXPORT_SYMBOL(q6asm_enc_cfg_blk_custom); + +/** + * q6asm_enc_cfg_blk_aac - + * command to set encode cfg block for aac + * + * @ac: Audio client handle + * @frames_per_buf: number of frames per buffer + * @sample_rate: Sample rate + * @channels: number of ASM channels + * @bit_rate: Bit rate info + * @mode: mode of AAC stream encode + * @format: aac format flag + * + * Returns 0 on success or error on failure + */ +int q6asm_enc_cfg_blk_aac(struct audio_client *ac, + uint32_t frames_per_buf, + uint32_t sample_rate, uint32_t channels, + uint32_t bit_rate, uint32_t mode, uint32_t format) +{ + struct asm_aac_enc_cfg_v2 enc_cfg; + int rc = 0; + + pr_debug("%s: session[%d]frames[%d]SR[%d]ch[%d]bitrate[%d]mode[%d] format[%d]\n", + __func__, ac->session, frames_per_buf, + sample_rate, channels, bit_rate, mode, format); + + q6asm_add_hdr(ac, &enc_cfg.hdr, sizeof(enc_cfg), TRUE); + atomic_set(&ac->cmd_state, -1); + + enc_cfg.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM; + enc_cfg.encdec.param_id = ASM_PARAM_ID_ENCDEC_ENC_CFG_BLK_V2; + enc_cfg.encdec.param_size = sizeof(struct asm_aac_enc_cfg_v2) - + sizeof(struct asm_stream_cmd_set_encdec_param); + enc_cfg.encblk.frames_per_buf = frames_per_buf; + enc_cfg.encblk.enc_cfg_blk_size = enc_cfg.encdec.param_size - + sizeof(struct asm_enc_cfg_blk_param_v2); + enc_cfg.bit_rate = bit_rate; + enc_cfg.enc_mode = mode; + enc_cfg.aac_fmt_flag = format; + enc_cfg.channel_cfg = channels; + enc_cfg.sample_rate = sample_rate; + + rc = apr_send_pkt(ac->apr, (uint32_t *) &enc_cfg); + if (rc < 0) { + pr_err("%s: Comamnd %d failed %d\n", + __func__, ASM_STREAM_CMD_SET_ENCDEC_PARAM, rc); + rc = -EINVAL; + goto fail_cmd; + } + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), + msecs_to_jiffies(TIMEOUT_MS)); + if (!rc) { + pr_err("%s: timeout. waited for FORMAT_UPDATE\n", + __func__); + rc = -ETIMEDOUT; + goto fail_cmd; + } + if (atomic_read(&ac->cmd_state) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->cmd_state))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state)); + goto fail_cmd; + } + return 0; +fail_cmd: + return rc; +} +EXPORT_SYMBOL(q6asm_enc_cfg_blk_aac); + +/** + * q6asm_enc_cfg_blk_g711 - + * command to set encode cfg block for g711 + * + * @ac: Audio client handle + * @frames_per_buf: number of frames per buffer + * @sample_rate: Sample rate + * + * Returns 0 on success or error on failure + */ +int q6asm_enc_cfg_blk_g711(struct audio_client *ac, + uint32_t frames_per_buf, + uint32_t sample_rate) +{ + struct asm_g711_enc_cfg_v2 enc_cfg; + int rc = 0; + + pr_debug("%s: session[%d]frames[%d]SR[%d]\n", + __func__, ac->session, frames_per_buf, + sample_rate); + + q6asm_add_hdr(ac, &enc_cfg.hdr, sizeof(enc_cfg), TRUE); + atomic_set(&ac->cmd_state, -1); + + enc_cfg.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM; + enc_cfg.encdec.param_id = ASM_PARAM_ID_ENCDEC_ENC_CFG_BLK_V2; + enc_cfg.encdec.param_size = sizeof(struct asm_g711_enc_cfg_v2) - + sizeof(struct asm_stream_cmd_set_encdec_param); + enc_cfg.encblk.frames_per_buf = frames_per_buf; + enc_cfg.encblk.enc_cfg_blk_size = enc_cfg.encdec.param_size - + sizeof(struct asm_enc_cfg_blk_param_v2); + enc_cfg.sample_rate = sample_rate; + + rc = apr_send_pkt(ac->apr, (uint32_t *) &enc_cfg); + if (rc < 0) { + pr_err("%s: Comamnd %d failed %d\n", + __func__, ASM_STREAM_CMD_SET_ENCDEC_PARAM, rc); + rc = -EINVAL; + goto fail_cmd; + } + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), + msecs_to_jiffies(TIMEOUT_MS)); + if (!rc) { + pr_err("%s: timeout. waited for FORMAT_UPDATE\n", + __func__); + rc = -ETIMEDOUT; + goto fail_cmd; + } + if (atomic_read(&ac->cmd_state) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->cmd_state))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state)); + goto fail_cmd; + } + return 0; +fail_cmd: + return rc; +} +EXPORT_SYMBOL(q6asm_enc_cfg_blk_g711); + +/** + * q6asm_set_encdec_chan_map - + * command to set encdec channel map + * + * @ac: Audio client handle + * @channels: number of channels + * + * Returns 0 on success or error on failure + */ +int q6asm_set_encdec_chan_map(struct audio_client *ac, + uint32_t num_channels) +{ + struct asm_dec_out_chan_map_param chan_map; + u8 *channel_mapping; + int rc = 0; + + if (num_channels > MAX_CHAN_MAP_CHANNELS) { + pr_err("%s: Invalid channel count %d\n", __func__, + num_channels); + return -EINVAL; + } + + pr_debug("%s: Session %d, num_channels = %d\n", + __func__, ac->session, num_channels); + q6asm_add_hdr(ac, &chan_map.hdr, sizeof(chan_map), TRUE); + atomic_set(&ac->cmd_state, -1); + chan_map.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM; + chan_map.encdec.param_id = ASM_PARAM_ID_DEC_OUTPUT_CHAN_MAP; + chan_map.encdec.param_size = sizeof(struct asm_dec_out_chan_map_param) - + (sizeof(struct apr_hdr) + + sizeof(struct asm_stream_cmd_set_encdec_param)); + chan_map.num_channels = num_channels; + channel_mapping = chan_map.channel_mapping; + memset(channel_mapping, PCM_CHANNEL_NULL, MAX_CHAN_MAP_CHANNELS); + + if (q6asm_map_channels(channel_mapping, num_channels, false)) { + pr_err("%s: map channels failed %d\n", __func__, num_channels); + return -EINVAL; + } + + rc = apr_send_pkt(ac->apr, (uint32_t *) &chan_map); + if (rc < 0) { + pr_err("%s: Command opcode[0x%x]paramid[0x%x] failed %d\n", + __func__, ASM_STREAM_CMD_SET_ENCDEC_PARAM, + ASM_PARAM_ID_DEC_OUTPUT_CHAN_MAP, rc); + goto fail_cmd; + } + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), + msecs_to_jiffies(TIMEOUT_MS)); + if (!rc) { + pr_err("%s: timeout opcode[0x%x]\n", __func__, + chan_map.hdr.opcode); + rc = -ETIMEDOUT; + goto fail_cmd; + } + if (atomic_read(&ac->cmd_state) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->cmd_state))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state)); + goto fail_cmd; + } + return 0; +fail_cmd: + return rc; +} +EXPORT_SYMBOL(q6asm_set_encdec_chan_map); + +/* + * q6asm_enc_cfg_blk_pcm_v5 - sends encoder configuration parameters + * + * @ac: Client session handle + * @rate: sample rate + * @channels: number of channels + * @bits_per_sample: bit width of encoder session + * @use_default_chmap: true if default channel map to be used + * @use_back_flavor: to configure back left and right channel + * @channel_map: input channel map + * @sample_word_size: Size in bits of the word that holds a sample of a channel + * @endianness: endianness of the pcm data + * @mode: Mode to provide additional info about the pcm input data + */ +static int q6asm_enc_cfg_blk_pcm_v5(struct audio_client *ac, + uint32_t rate, uint32_t channels, + uint16_t bits_per_sample, bool use_default_chmap, + bool use_back_flavor, u8 *channel_map, + uint16_t sample_word_size, uint16_t endianness, + uint16_t mode) +{ + struct asm_multi_channel_pcm_enc_cfg_v5 enc_cfg; + struct asm_enc_cfg_blk_param_v2 enc_fg_blk; + u8 *channel_mapping; + u32 frames_per_buf = 0; + int rc; + + if (!use_default_chmap && (channel_map == NULL)) { + pr_err("%s: No valid chan map and can't use default\n", + __func__); + rc = -EINVAL; + goto fail_cmd; + } + + if (channels > PCM_FORMAT_MAX_NUM_CHANNEL_V8) { + pr_err("%s: Invalid channel count %d\n", __func__, channels); + rc = -EINVAL; + goto fail_cmd; + } + + pr_debug("%s: session[%d]rate[%d]ch[%d]bps[%d]wordsize[%d]\n", __func__, + ac->session, rate, channels, + bits_per_sample, sample_word_size); + + memset(&enc_cfg, 0, sizeof(enc_cfg)); + q6asm_add_hdr(ac, &enc_cfg.hdr, sizeof(enc_cfg), TRUE); + atomic_set(&ac->cmd_state, -1); + enc_cfg.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM; + enc_cfg.encdec.param_id = ASM_PARAM_ID_ENCDEC_ENC_CFG_BLK_V2; + enc_cfg.encdec.param_size = sizeof(enc_cfg) - sizeof(enc_cfg.hdr) - + sizeof(enc_cfg.encdec); + enc_cfg.encblk.frames_per_buf = frames_per_buf; + enc_cfg.encblk.enc_cfg_blk_size = enc_cfg.encdec.param_size - + sizeof(enc_fg_blk); + enc_cfg.num_channels = channels; + enc_cfg.bits_per_sample = bits_per_sample; + enc_cfg.sample_rate = rate; + enc_cfg.is_signed = 1; + enc_cfg.sample_word_size = sample_word_size; + enc_cfg.endianness = endianness; + enc_cfg.mode = mode; + channel_mapping = enc_cfg.channel_mapping; + + memset(channel_mapping, 0, PCM_FORMAT_MAX_NUM_CHANNEL_V8); + + if (use_default_chmap) { + pr_debug("%s: setting default channel map for %d channels", + __func__, channels); + if (q6asm_map_channels(channel_mapping, channels, + use_back_flavor)) { + pr_err("%s: map channels failed %d\n", + __func__, channels); + rc = -EINVAL; + goto fail_cmd; + } + } else { + pr_debug("%s: Using pre-defined channel map", __func__); + memcpy(channel_mapping, channel_map, + PCM_FORMAT_MAX_NUM_CHANNEL_V8); + } + + rc = apr_send_pkt(ac->apr, (uint32_t *) &enc_cfg); + if (rc < 0) { + pr_err("%s: Command open failed %d\n", __func__, rc); + goto fail_cmd; + } + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), 5*HZ); + if (!rc) { + pr_err("%s: timeout opcode[0x%x]\n", + __func__, enc_cfg.hdr.opcode); + rc = -ETIMEDOUT; + goto fail_cmd; + } + if (atomic_read(&ac->cmd_state) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->cmd_state))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state)); + goto fail_cmd; + } + return 0; +fail_cmd: + return rc; +} +EXPORT_SYMBOL(q6asm_enc_cfg_blk_pcm_v5); + +/* + * q6asm_enc_cfg_blk_pcm_v4 - sends encoder configuration parameters + * + * @ac: Client session handle + * @rate: sample rate + * @channels: number of channels + * @bits_per_sample: bit width of encoder session + * @use_default_chmap: true if default channel map to be used + * @use_back_flavor: to configure back left and right channel + * @channel_map: input channel map + * @sample_word_size: Size in bits of the word that holds a sample of a channel + * @endianness: endianness of the pcm data + * @mode: Mode to provide additional info about the pcm input data + */ +int q6asm_enc_cfg_blk_pcm_v4(struct audio_client *ac, + uint32_t rate, uint32_t channels, + uint16_t bits_per_sample, bool use_default_chmap, + bool use_back_flavor, u8 *channel_map, + uint16_t sample_word_size, uint16_t endianness, + uint16_t mode) +{ + struct asm_multi_channel_pcm_enc_cfg_v4 enc_cfg; + struct asm_enc_cfg_blk_param_v2 enc_fg_blk; + u8 *channel_mapping; + u32 frames_per_buf = 0; + int rc; + + if (!use_default_chmap && (channel_map == NULL)) { + pr_err("%s: No valid chan map and can't use default\n", + __func__); + rc = -EINVAL; + goto fail_cmd; + } + + if (channels > PCM_FORMAT_MAX_NUM_CHANNEL) { + pr_err("%s: Invalid channel count %d\n", __func__, channels); + rc = -EINVAL; + goto fail_cmd; + } + + pr_debug("%s: session[%d]rate[%d]ch[%d]bps[%d]wordsize[%d]\n", __func__, + ac->session, rate, channels, + bits_per_sample, sample_word_size); + + memset(&enc_cfg, 0, sizeof(enc_cfg)); + q6asm_add_hdr(ac, &enc_cfg.hdr, sizeof(enc_cfg), TRUE); + atomic_set(&ac->cmd_state, -1); + enc_cfg.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM; + enc_cfg.encdec.param_id = ASM_PARAM_ID_ENCDEC_ENC_CFG_BLK_V2; + enc_cfg.encdec.param_size = sizeof(enc_cfg) - sizeof(enc_cfg.hdr) - + sizeof(enc_cfg.encdec); + enc_cfg.encblk.frames_per_buf = frames_per_buf; + enc_cfg.encblk.enc_cfg_blk_size = enc_cfg.encdec.param_size - + sizeof(enc_fg_blk); + enc_cfg.num_channels = channels; + enc_cfg.bits_per_sample = bits_per_sample; + enc_cfg.sample_rate = rate; + enc_cfg.is_signed = 1; + enc_cfg.sample_word_size = sample_word_size; + enc_cfg.endianness = endianness; + enc_cfg.mode = mode; + channel_mapping = enc_cfg.channel_mapping; + + memset(channel_mapping, 0, PCM_FORMAT_MAX_NUM_CHANNEL); + + if (use_default_chmap) { + pr_debug("%s: setting default channel map for %d channels", + __func__, channels); + if (q6asm_map_channels(channel_mapping, channels, + use_back_flavor)) { + pr_err("%s: map channels failed %d\n", + __func__, channels); + rc = -EINVAL; + goto fail_cmd; + } + } else { + pr_debug("%s: Using pre-defined channel map", __func__); + memcpy(channel_mapping, channel_map, + PCM_FORMAT_MAX_NUM_CHANNEL); + } + + rc = apr_send_pkt(ac->apr, (uint32_t *) &enc_cfg); + if (rc < 0) { + pr_err("%s: Command open failed %d\n", __func__, rc); + goto fail_cmd; + } + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), + msecs_to_jiffies(TIMEOUT_MS)); + if (!rc) { + pr_err("%s: timeout opcode[0x%x]\n", + __func__, enc_cfg.hdr.opcode); + rc = -ETIMEDOUT; + goto fail_cmd; + } + if (atomic_read(&ac->cmd_state) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->cmd_state))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state)); + goto fail_cmd; + } + return 0; +fail_cmd: + return rc; +} +EXPORT_SYMBOL(q6asm_enc_cfg_blk_pcm_v4); + +/* + * q6asm_enc_cfg_blk_pcm_v3 - sends encoder configuration parameters + * + * @ac: Client session handle + * @rate: sample rate + * @channels: number of channels + * @bits_per_sample: bit width of encoder session + * @use_default_chmap: true if default channel map to be used + * @use_back_flavor: to configure back left and right channel + * @channel_map: input channel map + * @sample_word_size: Size in bits of the word that holds a sample of a channel + */ +int q6asm_enc_cfg_blk_pcm_v3(struct audio_client *ac, + uint32_t rate, uint32_t channels, + uint16_t bits_per_sample, bool use_default_chmap, + bool use_back_flavor, u8 *channel_map, + uint16_t sample_word_size) +{ + struct asm_multi_channel_pcm_enc_cfg_v3 enc_cfg; + struct asm_enc_cfg_blk_param_v2 enc_fg_blk; + u8 *channel_mapping; + u32 frames_per_buf = 0; + int rc; + + if (!use_default_chmap && (channel_map == NULL)) { + pr_err("%s: No valid chan map and can't use default\n", + __func__); + rc = -EINVAL; + goto fail_cmd; + } + + if (channels > PCM_FORMAT_MAX_NUM_CHANNEL) { + pr_err("%s: Invalid channel count %d\n", __func__, channels); + rc = -EINVAL; + goto fail_cmd; + } + + pr_debug("%s: session[%d]rate[%d]ch[%d]bps[%d]wordsize[%d]\n", __func__, + ac->session, rate, channels, + bits_per_sample, sample_word_size); + + memset(&enc_cfg, 0, sizeof(enc_cfg)); + q6asm_add_hdr(ac, &enc_cfg.hdr, sizeof(enc_cfg), TRUE); + atomic_set(&ac->cmd_state, -1); + enc_cfg.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM; + enc_cfg.encdec.param_id = ASM_PARAM_ID_ENCDEC_ENC_CFG_BLK_V2; + enc_cfg.encdec.param_size = sizeof(enc_cfg) - sizeof(enc_cfg.hdr) - + sizeof(enc_cfg.encdec); + enc_cfg.encblk.frames_per_buf = frames_per_buf; + enc_cfg.encblk.enc_cfg_blk_size = enc_cfg.encdec.param_size - + sizeof(enc_fg_blk); + enc_cfg.num_channels = channels; + enc_cfg.bits_per_sample = bits_per_sample; + enc_cfg.sample_rate = rate; + enc_cfg.is_signed = 1; + enc_cfg.sample_word_size = sample_word_size; + channel_mapping = enc_cfg.channel_mapping; + + memset(channel_mapping, 0, PCM_FORMAT_MAX_NUM_CHANNEL); + + if (use_default_chmap) { + pr_debug("%s: setting default channel map for %d channels", + __func__, channels); + if (q6asm_map_channels(channel_mapping, channels, + use_back_flavor)) { + pr_err("%s: map channels failed %d\n", + __func__, channels); + rc = -EINVAL; + goto fail_cmd; + } + } else { + pr_debug("%s: Using pre-defined channel map", __func__); + memcpy(channel_mapping, channel_map, + PCM_FORMAT_MAX_NUM_CHANNEL); + } + + rc = apr_send_pkt(ac->apr, (uint32_t *) &enc_cfg); + if (rc < 0) { + pr_err("%s: Comamnd open failed %d\n", __func__, rc); + goto fail_cmd; + } + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), + msecs_to_jiffies(TIMEOUT_MS)); + if (!rc) { + pr_err("%s: timeout opcode[0x%x]\n", + __func__, enc_cfg.hdr.opcode); + rc = -ETIMEDOUT; + goto fail_cmd; + } + if (atomic_read(&ac->cmd_state) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->cmd_state))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state)); + goto fail_cmd; + } + return 0; +fail_cmd: + return rc; +} +EXPORT_SYMBOL(q6asm_enc_cfg_blk_pcm_v3); + +/** + * q6asm_enc_cfg_blk_pcm_v2 - + * command to set encode config block for pcm_v2 + * + * @ac: Audio client handle + * @rate: sample rate + * @channels: number of channels + * @bits_per_sample: number of bits per sample + * @use_default_chmap: Flag indicating to use default ch_map or not + * @use_back_flavor: back flavor flag + * @channel_map: Custom channel map settings + * + * Returns 0 on success or error on failure + */ +int q6asm_enc_cfg_blk_pcm_v2(struct audio_client *ac, + uint32_t rate, uint32_t channels, uint16_t bits_per_sample, + bool use_default_chmap, bool use_back_flavor, u8 *channel_map) +{ + struct asm_multi_channel_pcm_enc_cfg_v2 enc_cfg; + u8 *channel_mapping; + u32 frames_per_buf = 0; + + int rc = 0; + + if (!use_default_chmap && (channel_map == NULL)) { + pr_err("%s: No valid chan map and can't use default\n", + __func__); + return -EINVAL; + } + + if (channels > PCM_FORMAT_MAX_NUM_CHANNEL) { + pr_err("%s: Invalid channel count %d\n", __func__, channels); + return -EINVAL; + } + + pr_debug("%s: Session %d, rate = %d, channels = %d\n", __func__, + ac->session, rate, channels); + + q6asm_add_hdr(ac, &enc_cfg.hdr, sizeof(enc_cfg), TRUE); + atomic_set(&ac->cmd_state, -1); + enc_cfg.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM; + enc_cfg.encdec.param_id = ASM_PARAM_ID_ENCDEC_ENC_CFG_BLK_V2; + enc_cfg.encdec.param_size = sizeof(enc_cfg) - sizeof(enc_cfg.hdr) - + sizeof(enc_cfg.encdec); + enc_cfg.encblk.frames_per_buf = frames_per_buf; + enc_cfg.encblk.enc_cfg_blk_size = enc_cfg.encdec.param_size - + sizeof(struct asm_enc_cfg_blk_param_v2); + + enc_cfg.num_channels = channels; + enc_cfg.bits_per_sample = bits_per_sample; + enc_cfg.sample_rate = rate; + enc_cfg.is_signed = 1; + channel_mapping = enc_cfg.channel_mapping; + + memset(channel_mapping, 0, PCM_FORMAT_MAX_NUM_CHANNEL); + + if (use_default_chmap) { + pr_debug("%s: setting default channel map for %d channels", + __func__, channels); + if (q6asm_map_channels(channel_mapping, channels, + use_back_flavor)) { + pr_err("%s: map channels failed %d\n", + __func__, channels); + return -EINVAL; + } + } else { + pr_debug("%s: Using pre-defined channel map", __func__); + memcpy(channel_mapping, channel_map, + PCM_FORMAT_MAX_NUM_CHANNEL); + } + + rc = apr_send_pkt(ac->apr, (uint32_t *) &enc_cfg); + if (rc < 0) { + pr_err("%s: Comamnd open failed %d\n", __func__, rc); + rc = -EINVAL; + goto fail_cmd; + } + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), + msecs_to_jiffies(TIMEOUT_MS)); + if (!rc) { + pr_err("%s: timeout opcode[0x%x]\n", + __func__, enc_cfg.hdr.opcode); + rc = -ETIMEDOUT; + goto fail_cmd; + } + if (atomic_read(&ac->cmd_state) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->cmd_state))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state)); + goto fail_cmd; + } + return 0; +fail_cmd: + return rc; +} +EXPORT_SYMBOL(q6asm_enc_cfg_blk_pcm_v2); + +static int __q6asm_enc_cfg_blk_pcm_v5(struct audio_client *ac, + uint32_t rate, uint32_t channels, + uint16_t bits_per_sample, + uint16_t sample_word_size, + uint16_t endianness, + uint16_t mode) +{ + return q6asm_enc_cfg_blk_pcm_v5(ac, rate, channels, + bits_per_sample, true, false, NULL, + sample_word_size, endianness, mode); +} + +static int __q6asm_enc_cfg_blk_pcm_v4(struct audio_client *ac, + uint32_t rate, uint32_t channels, + uint16_t bits_per_sample, + uint16_t sample_word_size, + uint16_t endianness, + uint16_t mode) +{ + return q6asm_enc_cfg_blk_pcm_v4(ac, rate, channels, + bits_per_sample, true, false, NULL, + sample_word_size, endianness, mode); +} + +static int __q6asm_enc_cfg_blk_pcm_v3(struct audio_client *ac, + uint32_t rate, uint32_t channels, + uint16_t bits_per_sample, + uint16_t sample_word_size) +{ + return q6asm_enc_cfg_blk_pcm_v3(ac, rate, channels, + bits_per_sample, true, false, NULL, + sample_word_size); +} + +static int __q6asm_enc_cfg_blk_pcm(struct audio_client *ac, + uint32_t rate, uint32_t channels, uint16_t bits_per_sample) +{ + return q6asm_enc_cfg_blk_pcm_v2(ac, rate, channels, + bits_per_sample, true, false, NULL); +} + +/** + * q6asm_enc_cfg_blk_pcm - + * command to set encode config block for pcm + * + * @ac: Audio client handle + * @rate: sample rate + * @channels: number of channels + * + * Returns 0 on success or error on failure + */ +int q6asm_enc_cfg_blk_pcm(struct audio_client *ac, + uint32_t rate, uint32_t channels) +{ + return __q6asm_enc_cfg_blk_pcm(ac, rate, channels, 16); +} +EXPORT_SYMBOL(q6asm_enc_cfg_blk_pcm); + +int q6asm_enc_cfg_blk_pcm_format_support(struct audio_client *ac, + uint32_t rate, uint32_t channels, uint16_t bits_per_sample) +{ + return __q6asm_enc_cfg_blk_pcm(ac, rate, channels, bits_per_sample); +} + +/* + * q6asm_enc_cfg_blk_pcm_format_support_v3 - sends encoder configuration + * parameters + * + * @ac: Client session handle + * @rate: sample rate + * @channels: number of channels + * @bits_per_sample: bit width of encoder session + * @sample_word_size: Size in bits of the word that holds a sample of a channel + */ +int q6asm_enc_cfg_blk_pcm_format_support_v3(struct audio_client *ac, + uint32_t rate, uint32_t channels, + uint16_t bits_per_sample, + uint16_t sample_word_size) +{ + return __q6asm_enc_cfg_blk_pcm_v3(ac, rate, channels, + bits_per_sample, sample_word_size); +} +EXPORT_SYMBOL(q6asm_enc_cfg_blk_pcm_format_support_v3); + +/* + * q6asm_enc_cfg_blk_pcm_format_support_v4 - sends encoder configuration + * parameters + * + * @ac: Client session handle + * @rate: sample rate + * @channels: number of channels + * @bits_per_sample: bit width of encoder session + * @sample_word_size: Size in bits of the word that holds a sample of a channel + * @endianness: endianness of the pcm data + * @mode: Mode to provide additional info about the pcm input data + */ +int q6asm_enc_cfg_blk_pcm_format_support_v4(struct audio_client *ac, + uint32_t rate, uint32_t channels, + uint16_t bits_per_sample, + uint16_t sample_word_size, + uint16_t endianness, + uint16_t mode) +{ + return __q6asm_enc_cfg_blk_pcm_v4(ac, rate, channels, + bits_per_sample, sample_word_size, + endianness, mode); +} +EXPORT_SYMBOL(q6asm_enc_cfg_blk_pcm_format_support_v4); + +/* + * q6asm_enc_cfg_blk_pcm_format_support_v5 - sends encoder configuration + * parameters + * + * @ac: Client session handle + * @rate: sample rate + * @channels: number of channels + * @bits_per_sample: bit width of encoder session + * @sample_word_size: Size in bits of the word that holds a sample of a channel + * @endianness: endianness of the pcm data + * @mode: Mode to provide additional info about the pcm input data + */ +int q6asm_enc_cfg_blk_pcm_format_support_v5(struct audio_client *ac, + uint32_t rate, uint32_t channels, + uint16_t bits_per_sample, + uint16_t sample_word_size, + uint16_t endianness, + uint16_t mode) +{ + return __q6asm_enc_cfg_blk_pcm_v5(ac, rate, channels, + bits_per_sample, sample_word_size, + endianness, mode); +} + +EXPORT_SYMBOL(q6asm_enc_cfg_blk_pcm_format_support_v5); +/** + * q6asm_enc_cfg_blk_pcm_native - + * command to set encode config block for pcm_native + * + * @ac: Audio client handle + * @rate: sample rate + * @channels: number of channels + * + * Returns 0 on success or error on failure + */ +int q6asm_enc_cfg_blk_pcm_native(struct audio_client *ac, + uint32_t rate, uint32_t channels) +{ + struct asm_multi_channel_pcm_enc_cfg_v2 enc_cfg; + u8 *channel_mapping; + u32 frames_per_buf = 0; + int rc = 0; + + if (channels > PCM_FORMAT_MAX_NUM_CHANNEL) { + pr_err("%s: Invalid channel count %d\n", __func__, channels); + return -EINVAL; + } + + pr_debug("%s: Session %d, rate = %d, channels = %d\n", __func__, + ac->session, rate, channels); + + q6asm_add_hdr(ac, &enc_cfg.hdr, sizeof(enc_cfg), TRUE); + atomic_set(&ac->cmd_state, -1); + enc_cfg.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM; + enc_cfg.encdec.param_id = ASM_PARAM_ID_ENCDEC_ENC_CFG_BLK_V2; + enc_cfg.encdec.param_size = sizeof(enc_cfg) - sizeof(enc_cfg.hdr) - + sizeof(enc_cfg.encdec); + enc_cfg.encblk.frames_per_buf = frames_per_buf; + enc_cfg.encblk.enc_cfg_blk_size = enc_cfg.encdec.param_size - + sizeof(struct asm_enc_cfg_blk_param_v2); + + enc_cfg.num_channels = 0;/*channels;*/ + enc_cfg.bits_per_sample = 16; + enc_cfg.sample_rate = 0;/*rate;*/ + enc_cfg.is_signed = 1; + channel_mapping = enc_cfg.channel_mapping; + + + memset(channel_mapping, 0, PCM_FORMAT_MAX_NUM_CHANNEL); + + if (q6asm_map_channels(channel_mapping, channels, false)) { + pr_err("%s: map channels failed %d\n", __func__, channels); + return -EINVAL; + } + + rc = apr_send_pkt(ac->apr, (uint32_t *) &enc_cfg); + if (rc < 0) { + pr_err("%s: Comamnd open failed %d\n", __func__, rc); + rc = -EINVAL; + goto fail_cmd; + } + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), + msecs_to_jiffies(TIMEOUT_MS)); + if (!rc) { + pr_err("%s: timeout opcode[0x%x]\n", + __func__, enc_cfg.hdr.opcode); + rc = -ETIMEDOUT; + goto fail_cmd; + } + if (atomic_read(&ac->cmd_state) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->cmd_state))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state)); + goto fail_cmd; + } + return 0; +fail_cmd: + return rc; +} +EXPORT_SYMBOL(q6asm_enc_cfg_blk_pcm_native); + +static int q6asm_map_channels(u8 *channel_mapping, uint32_t channels, + bool use_back_flavor) +{ + u8 *lchannel_mapping; + + lchannel_mapping = channel_mapping; + pr_debug("%s: channels passed: %d\n", __func__, channels); + if (channels == 1) { + lchannel_mapping[0] = PCM_CHANNEL_FC; + } else if (channels == 2) { + lchannel_mapping[0] = PCM_CHANNEL_FL; + lchannel_mapping[1] = PCM_CHANNEL_FR; + } else if (channels == 3) { + lchannel_mapping[0] = PCM_CHANNEL_FL; + lchannel_mapping[1] = PCM_CHANNEL_FR; + lchannel_mapping[2] = PCM_CHANNEL_FC; + } else if (channels == 4) { + lchannel_mapping[0] = PCM_CHANNEL_FL; + lchannel_mapping[1] = PCM_CHANNEL_FR; + lchannel_mapping[2] = use_back_flavor ? + PCM_CHANNEL_LB : PCM_CHANNEL_LS; + lchannel_mapping[3] = use_back_flavor ? + PCM_CHANNEL_RB : PCM_CHANNEL_RS; + } else if (channels == 5) { + lchannel_mapping[0] = PCM_CHANNEL_FL; + lchannel_mapping[1] = PCM_CHANNEL_FR; + lchannel_mapping[2] = PCM_CHANNEL_FC; + lchannel_mapping[3] = use_back_flavor ? + PCM_CHANNEL_LB : PCM_CHANNEL_LS; + lchannel_mapping[4] = use_back_flavor ? + PCM_CHANNEL_RB : PCM_CHANNEL_RS; + } else if (channels == 6) { + lchannel_mapping[0] = PCM_CHANNEL_FL; + lchannel_mapping[1] = PCM_CHANNEL_FR; + lchannel_mapping[2] = PCM_CHANNEL_FC; + lchannel_mapping[3] = PCM_CHANNEL_LFE; + lchannel_mapping[4] = use_back_flavor ? + PCM_CHANNEL_LB : PCM_CHANNEL_LS; + lchannel_mapping[5] = use_back_flavor ? + PCM_CHANNEL_RB : PCM_CHANNEL_RS; + } else if (channels == 7) { + /* + * Configured for 5.1 channel mapping + 1 channel for debug + * Can be customized based on DSP. + */ + lchannel_mapping[0] = PCM_CHANNEL_FL; + lchannel_mapping[1] = PCM_CHANNEL_FR; + lchannel_mapping[2] = PCM_CHANNEL_FC; + lchannel_mapping[3] = PCM_CHANNEL_LFE; + lchannel_mapping[4] = use_back_flavor ? + PCM_CHANNEL_LB : PCM_CHANNEL_LS; + lchannel_mapping[5] = use_back_flavor ? + PCM_CHANNEL_RB : PCM_CHANNEL_RS; + lchannel_mapping[6] = PCM_CHANNEL_CS; + } else if (channels == 8) { + lchannel_mapping[0] = PCM_CHANNEL_FL; + lchannel_mapping[1] = PCM_CHANNEL_FR; + lchannel_mapping[2] = PCM_CHANNEL_FC; + lchannel_mapping[3] = PCM_CHANNEL_LFE; + lchannel_mapping[4] = PCM_CHANNEL_LB; + lchannel_mapping[5] = PCM_CHANNEL_RB; + lchannel_mapping[6] = PCM_CHANNEL_LS; + lchannel_mapping[7] = PCM_CHANNEL_RS; + } else if (channels == 12) { + /* + * Configured for 7.1.4 channel mapping + * Todo: Needs to be checked + */ + lchannel_mapping[0] = PCM_CHANNEL_FL; + lchannel_mapping[1] = PCM_CHANNEL_FR; + lchannel_mapping[2] = PCM_CHANNEL_FC; + lchannel_mapping[3] = PCM_CHANNEL_LFE; + lchannel_mapping[4] = PCM_CHANNEL_LB; + lchannel_mapping[5] = PCM_CHANNEL_RB; + lchannel_mapping[6] = PCM_CHANNEL_LS; + lchannel_mapping[7] = PCM_CHANNEL_RS; + lchannel_mapping[8] = PCM_CHANNEL_TFL; + lchannel_mapping[9] = PCM_CHANNEL_TFR; + lchannel_mapping[10] = PCM_CHANNEL_TSL; + lchannel_mapping[11] = PCM_CHANNEL_TSR; + } else if (channels == 16) { + /* + * Configured for 7.1.8 channel mapping + * Todo: Needs to be checked + */ + lchannel_mapping[0] = PCM_CHANNEL_FL; + lchannel_mapping[1] = PCM_CHANNEL_FR; + lchannel_mapping[2] = PCM_CHANNEL_FC; + lchannel_mapping[3] = PCM_CHANNEL_LFE; + lchannel_mapping[4] = PCM_CHANNEL_LB; + lchannel_mapping[5] = PCM_CHANNEL_RB; + lchannel_mapping[6] = PCM_CHANNEL_LS; + lchannel_mapping[7] = PCM_CHANNEL_RS; + lchannel_mapping[8] = PCM_CHANNEL_TFL; + lchannel_mapping[9] = PCM_CHANNEL_TFR; + lchannel_mapping[10] = PCM_CHANNEL_TSL; + lchannel_mapping[11] = PCM_CHANNEL_TSR; + lchannel_mapping[12] = PCM_CHANNEL_FLC; + lchannel_mapping[13] = PCM_CHANNEL_FRC; + lchannel_mapping[14] = PCM_CHANNEL_RLC; + lchannel_mapping[15] = PCM_CHANNEL_RRC; + } else { + pr_err("%s: ERROR.unsupported num_ch = %u\n", + __func__, channels); + return -EINVAL; + } + return 0; +} + +/** + * q6asm_enable_sbrps - + * command to enable sbrps for ASM + * + * @ac: Audio client handle + * @sbr_ps_enable: flag for sbr_ps enable or disable + * + * Returns 0 on success or error on failure + */ +int q6asm_enable_sbrps(struct audio_client *ac, + uint32_t sbr_ps_enable) +{ + struct asm_aac_sbr_ps_flag_param sbrps; + u32 frames_per_buf = 0; + + int rc = 0; + + pr_debug("%s: Session %d\n", __func__, ac->session); + + q6asm_add_hdr(ac, &sbrps.hdr, sizeof(sbrps), TRUE); + atomic_set(&ac->cmd_state, -1); + + sbrps.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM; + sbrps.encdec.param_id = ASM_PARAM_ID_AAC_SBR_PS_FLAG; + sbrps.encdec.param_size = sizeof(struct asm_aac_sbr_ps_flag_param) - + sizeof(struct asm_stream_cmd_set_encdec_param); + sbrps.encblk.frames_per_buf = frames_per_buf; + sbrps.encblk.enc_cfg_blk_size = sbrps.encdec.param_size - + sizeof(struct asm_enc_cfg_blk_param_v2); + + sbrps.sbr_ps_flag = sbr_ps_enable; + + rc = apr_send_pkt(ac->apr, (uint32_t *) &sbrps); + if (rc < 0) { + pr_err("%s: Command opcode[0x%x]paramid[0x%x] failed %d\n", + __func__, + ASM_STREAM_CMD_SET_ENCDEC_PARAM, + ASM_PARAM_ID_AAC_SBR_PS_FLAG, rc); + rc = -EINVAL; + goto fail_cmd; + } + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), + msecs_to_jiffies(TIMEOUT_MS)); + if (!rc) { + pr_err("%s: timeout opcode[0x%x] ", __func__, sbrps.hdr.opcode); + rc = -ETIMEDOUT; + goto fail_cmd; + } + if (atomic_read(&ac->cmd_state) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->cmd_state))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state)); + goto fail_cmd; + } + return 0; +fail_cmd: + return rc; +} +EXPORT_SYMBOL(q6asm_enable_sbrps); + +/** + * q6asm_cfg_dual_mono_aac - + * command to set config for dual mono aac + * + * @ac: Audio client handle + * @sce_left: left sce val + * @sce_right: right sce val + * + * Returns 0 on success or error on failure + */ +int q6asm_cfg_dual_mono_aac(struct audio_client *ac, + uint16_t sce_left, uint16_t sce_right) +{ + struct asm_aac_dual_mono_mapping_param dual_mono; + + int rc = 0; + + pr_debug("%s: Session %d, sce_left = %d, sce_right = %d\n", + __func__, ac->session, sce_left, sce_right); + + q6asm_add_hdr(ac, &dual_mono.hdr, sizeof(dual_mono), TRUE); + atomic_set(&ac->cmd_state, -1); + + dual_mono.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM; + dual_mono.encdec.param_id = ASM_PARAM_ID_AAC_DUAL_MONO_MAPPING; + dual_mono.encdec.param_size = sizeof(dual_mono.left_channel_sce) + + sizeof(dual_mono.right_channel_sce); + dual_mono.left_channel_sce = sce_left; + dual_mono.right_channel_sce = sce_right; + + rc = apr_send_pkt(ac->apr, (uint32_t *) &dual_mono); + if (rc < 0) { + pr_err("%s: Command opcode[0x%x]paramid[0x%x] failed %d\n", + __func__, ASM_STREAM_CMD_SET_ENCDEC_PARAM, + ASM_PARAM_ID_AAC_DUAL_MONO_MAPPING, rc); + rc = -EINVAL; + goto fail_cmd; + } + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), + msecs_to_jiffies(TIMEOUT_MS)); + if (!rc) { + pr_err("%s: timeout opcode[0x%x]\n", __func__, + dual_mono.hdr.opcode); + rc = -ETIMEDOUT; + goto fail_cmd; + } + if (atomic_read(&ac->cmd_state) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->cmd_state))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state)); + goto fail_cmd; + } + return 0; +fail_cmd: + return rc; +} +EXPORT_SYMBOL(q6asm_cfg_dual_mono_aac); + +/* Support for selecting stereo mixing coefficients for B family not done */ +int q6asm_cfg_aac_sel_mix_coef(struct audio_client *ac, uint32_t mix_coeff) +{ + struct asm_aac_stereo_mix_coeff_selection_param_v2 aac_mix_coeff; + int rc = 0; + + q6asm_add_hdr(ac, &aac_mix_coeff.hdr, sizeof(aac_mix_coeff), TRUE); + atomic_set(&ac->cmd_state, -1); + aac_mix_coeff.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM; + aac_mix_coeff.param_id = + ASM_PARAM_ID_AAC_STEREO_MIX_COEFF_SELECTION_FLAG_V2; + aac_mix_coeff.param_size = + sizeof(struct asm_aac_stereo_mix_coeff_selection_param_v2); + aac_mix_coeff.aac_stereo_mix_coeff_flag = mix_coeff; + pr_debug("%s: mix_coeff = %u\n", __func__, mix_coeff); + rc = apr_send_pkt(ac->apr, (uint32_t *) &aac_mix_coeff); + if (rc < 0) { + pr_err("%s: Command opcode[0x%x]paramid[0x%x] failed %d\n", + __func__, ASM_STREAM_CMD_SET_ENCDEC_PARAM, + ASM_PARAM_ID_AAC_STEREO_MIX_COEFF_SELECTION_FLAG_V2, + rc); + rc = -EINVAL; + goto fail_cmd; + } + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), + msecs_to_jiffies(TIMEOUT_MS)); + if (!rc) { + pr_err("%s: timeout opcode[0x%x]\n", + __func__, aac_mix_coeff.hdr.opcode); + rc = -ETIMEDOUT; + goto fail_cmd; + } + if (atomic_read(&ac->cmd_state) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->cmd_state))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state)); + goto fail_cmd; + } + return 0; +fail_cmd: + return rc; +} +EXPORT_SYMBOL(q6asm_cfg_aac_sel_mix_coef); + +/** + * q6asm_enc_cfg_blk_qcelp - + * command to set encode config block for QCELP + * + * @ac: Audio client handle + * @frames_per_buf: Number of frames per buffer + * @min_rate: Minimum Enc rate + * @max_rate: Maximum Enc rate + * reduced_rate_level: Reduced rate level + * @rate_modulation_cmd: rate modulation command + * + * Returns 0 on success or error on failure + */ +int q6asm_enc_cfg_blk_qcelp(struct audio_client *ac, uint32_t frames_per_buf, + uint16_t min_rate, uint16_t max_rate, + uint16_t reduced_rate_level, uint16_t rate_modulation_cmd) +{ + struct asm_v13k_enc_cfg enc_cfg; + int rc = 0; + + pr_debug("%s: session[%d]frames[%d]min_rate[0x%4x]max_rate[0x%4x] reduced_rate_level[0x%4x]rate_modulation_cmd[0x%4x]\n", + __func__, + ac->session, frames_per_buf, min_rate, max_rate, + reduced_rate_level, rate_modulation_cmd); + + q6asm_add_hdr(ac, &enc_cfg.hdr, sizeof(enc_cfg), TRUE); + atomic_set(&ac->cmd_state, -1); + enc_cfg.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM; + enc_cfg.encdec.param_id = ASM_PARAM_ID_ENCDEC_ENC_CFG_BLK_V2; + enc_cfg.encdec.param_size = sizeof(struct asm_v13k_enc_cfg) - + sizeof(struct asm_stream_cmd_set_encdec_param); + enc_cfg.encblk.frames_per_buf = frames_per_buf; + enc_cfg.encblk.enc_cfg_blk_size = enc_cfg.encdec.param_size - + sizeof(struct asm_enc_cfg_blk_param_v2); + + enc_cfg.min_rate = min_rate; + enc_cfg.max_rate = max_rate; + enc_cfg.reduced_rate_cmd = reduced_rate_level; + enc_cfg.rate_mod_cmd = rate_modulation_cmd; + + rc = apr_send_pkt(ac->apr, (uint32_t *) &enc_cfg); + if (rc < 0) { + pr_err("%s: Comamnd %d failed %d\n", + __func__, ASM_STREAM_CMD_SET_ENCDEC_PARAM, rc); + rc = -EINVAL; + goto fail_cmd; + } + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), + msecs_to_jiffies(TIMEOUT_MS)); + if (!rc) { + pr_err("%s: timeout. waited for setencdec v13k resp\n", + __func__); + rc = -ETIMEDOUT; + goto fail_cmd; + } + if (atomic_read(&ac->cmd_state) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->cmd_state))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state)); + goto fail_cmd; + } + return 0; +fail_cmd: + return rc; +} +EXPORT_SYMBOL(q6asm_enc_cfg_blk_qcelp); + +/** + * q6asm_enc_cfg_blk_evrc - + * command to set encode config block for EVRC + * + * @ac: Audio client handle + * @frames_per_buf: Number of frames per buffer + * @min_rate: Minimum Enc rate + * @max_rate: Maximum Enc rate + * @rate_modulation_cmd: rate modulation command + * + * Returns 0 on success or error on failure + */ +int q6asm_enc_cfg_blk_evrc(struct audio_client *ac, uint32_t frames_per_buf, + uint16_t min_rate, uint16_t max_rate, + uint16_t rate_modulation_cmd) +{ + struct asm_evrc_enc_cfg enc_cfg; + int rc = 0; + + pr_debug("%s: session[%d]frames[%d]min_rate[0x%4x]max_rate[0x%4x] rate_modulation_cmd[0x%4x]\n", + __func__, ac->session, + frames_per_buf, min_rate, max_rate, rate_modulation_cmd); + + q6asm_add_hdr(ac, &enc_cfg.hdr, sizeof(enc_cfg), TRUE); + atomic_set(&ac->cmd_state, -1); + enc_cfg.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM; + enc_cfg.encdec.param_id = ASM_PARAM_ID_ENCDEC_ENC_CFG_BLK_V2; + enc_cfg.encdec.param_size = sizeof(struct asm_evrc_enc_cfg) - + sizeof(struct asm_stream_cmd_set_encdec_param); + enc_cfg.encblk.frames_per_buf = frames_per_buf; + enc_cfg.encblk.enc_cfg_blk_size = enc_cfg.encdec.param_size - + sizeof(struct asm_enc_cfg_blk_param_v2); + + enc_cfg.min_rate = min_rate; + enc_cfg.max_rate = max_rate; + enc_cfg.rate_mod_cmd = rate_modulation_cmd; + enc_cfg.reserved = 0; + + rc = apr_send_pkt(ac->apr, (uint32_t *) &enc_cfg); + if (rc < 0) { + pr_err("%s: Comamnd %d failed %d\n", + __func__, ASM_STREAM_CMD_SET_ENCDEC_PARAM, rc); + rc = -EINVAL; + goto fail_cmd; + } + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), + msecs_to_jiffies(TIMEOUT_MS)); + if (!rc) { + pr_err("%s: timeout. waited for encdec evrc\n", __func__); + rc = -ETIMEDOUT; + goto fail_cmd; + } + if (atomic_read(&ac->cmd_state) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->cmd_state))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state)); + goto fail_cmd; + } + return 0; +fail_cmd: + return rc; +} +EXPORT_SYMBOL(q6asm_enc_cfg_blk_evrc); + +/** + * q6asm_enc_cfg_blk_amrnb - + * command to set encode config block for AMRNB + * + * @ac: Audio client handle + * @frames_per_buf: Number of frames per buffer + * @band_mode: Band mode used + * @dtx_enable: DTX en flag + * + * Returns 0 on success or error on failure + */ +int q6asm_enc_cfg_blk_amrnb(struct audio_client *ac, uint32_t frames_per_buf, + uint16_t band_mode, uint16_t dtx_enable) +{ + struct asm_amrnb_enc_cfg enc_cfg; + int rc = 0; + + pr_debug("%s: session[%d]frames[%d]band_mode[0x%4x]dtx_enable[0x%4x]\n", + __func__, ac->session, frames_per_buf, band_mode, dtx_enable); + + q6asm_add_hdr(ac, &enc_cfg.hdr, sizeof(enc_cfg), TRUE); + atomic_set(&ac->cmd_state, -1); + enc_cfg.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM; + enc_cfg.encdec.param_id = ASM_PARAM_ID_ENCDEC_ENC_CFG_BLK_V2; + enc_cfg.encdec.param_size = sizeof(struct asm_amrnb_enc_cfg) - + sizeof(struct asm_stream_cmd_set_encdec_param); + enc_cfg.encblk.frames_per_buf = frames_per_buf; + enc_cfg.encblk.enc_cfg_blk_size = enc_cfg.encdec.param_size - + sizeof(struct asm_enc_cfg_blk_param_v2); + + enc_cfg.enc_mode = band_mode; + enc_cfg.dtx_mode = dtx_enable; + + rc = apr_send_pkt(ac->apr, (uint32_t *) &enc_cfg); + if (rc < 0) { + pr_err("%s: Comamnd %d failed %d\n", + __func__, ASM_STREAM_CMD_SET_ENCDEC_PARAM, rc); + rc = -EINVAL; + goto fail_cmd; + } + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), + msecs_to_jiffies(TIMEOUT_MS)); + if (!rc) { + pr_err("%s: timeout. waited for set encdec amrnb\n", __func__); + rc = -ETIMEDOUT; + goto fail_cmd; + } + if (atomic_read(&ac->cmd_state) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->cmd_state))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state)); + goto fail_cmd; + } + return 0; +fail_cmd: + return rc; +} +EXPORT_SYMBOL(q6asm_enc_cfg_blk_amrnb); + +/** + * q6asm_enc_cfg_blk_amrwb - + * command to set encode config block for AMRWB + * + * @ac: Audio client handle + * @frames_per_buf: Number of frames per buffer + * @band_mode: Band mode used + * @dtx_enable: DTX en flag + * + * Returns 0 on success or error on failure + */ +int q6asm_enc_cfg_blk_amrwb(struct audio_client *ac, uint32_t frames_per_buf, + uint16_t band_mode, uint16_t dtx_enable) +{ + struct asm_amrwb_enc_cfg enc_cfg; + int rc = 0; + + pr_debug("%s: session[%d]frames[%d]band_mode[0x%4x]dtx_enable[0x%4x]\n", + __func__, ac->session, frames_per_buf, band_mode, dtx_enable); + + q6asm_add_hdr(ac, &enc_cfg.hdr, sizeof(enc_cfg), TRUE); + atomic_set(&ac->cmd_state, -1); + enc_cfg.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM; + enc_cfg.encdec.param_id = ASM_PARAM_ID_ENCDEC_ENC_CFG_BLK_V2; + enc_cfg.encdec.param_size = sizeof(struct asm_amrwb_enc_cfg) - + sizeof(struct asm_stream_cmd_set_encdec_param); + enc_cfg.encblk.frames_per_buf = frames_per_buf; + enc_cfg.encblk.enc_cfg_blk_size = enc_cfg.encdec.param_size - + sizeof(struct asm_enc_cfg_blk_param_v2); + + enc_cfg.enc_mode = band_mode; + enc_cfg.dtx_mode = dtx_enable; + + rc = apr_send_pkt(ac->apr, (uint32_t *) &enc_cfg); + if (rc < 0) { + pr_err("%s: Comamnd %d failed %d\n", + __func__, ASM_STREAM_CMD_SET_ENCDEC_PARAM, rc); + rc = -EINVAL; + goto fail_cmd; + } + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), + msecs_to_jiffies(TIMEOUT_MS)); + if (!rc) { + pr_err("%s: timeout. waited for FORMAT_UPDATE\n", __func__); + rc = -ETIMEDOUT; + goto fail_cmd; + } + if (atomic_read(&ac->cmd_state) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->cmd_state))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state)); + goto fail_cmd; + } + return 0; +fail_cmd: + return rc; +} +EXPORT_SYMBOL(q6asm_enc_cfg_blk_amrwb); + + +static int __q6asm_media_format_block_pcm(struct audio_client *ac, + uint32_t rate, uint32_t channels, + uint16_t bits_per_sample, int stream_id, + bool use_default_chmap, char *channel_map) +{ + struct asm_multi_channel_pcm_fmt_blk_v2 fmt; + u8 *channel_mapping; + int rc = 0; + + if (channels > PCM_FORMAT_MAX_NUM_CHANNEL) { + pr_err("%s: Invalid channel count %d\n", __func__, channels); + return -EINVAL; + } + + pr_debug("%s: session[%d]rate[%d]ch[%d]\n", __func__, ac->session, rate, + channels); + + q6asm_stream_add_hdr(ac, &fmt.hdr, sizeof(fmt), TRUE, stream_id); + atomic_set(&ac->cmd_state, -1); + /* + * Updated the token field with stream/session for compressed playback + * Platform driver must know the the stream with which the command is + * associated + */ + if (ac->io_mode & COMPRESSED_STREAM_IO) + q6asm_update_token(&fmt.hdr.token, + ac->session, + stream_id, + 0, /* Buffer index is NA */ + 0, /* Direction flag is NA */ + WAIT_CMD); + + pr_debug("%s: token = 0x%x, stream_id %d, session 0x%x\n", + __func__, fmt.hdr.token, stream_id, ac->session); + + fmt.hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2; + fmt.fmt_blk.fmt_blk_size = sizeof(fmt) - sizeof(fmt.hdr) - + sizeof(fmt.fmt_blk); + fmt.num_channels = channels; + fmt.bits_per_sample = bits_per_sample; + fmt.sample_rate = rate; + fmt.is_signed = 1; + + channel_mapping = fmt.channel_mapping; + + memset(channel_mapping, 0, PCM_FORMAT_MAX_NUM_CHANNEL); + + if (use_default_chmap) { + if (q6asm_map_channels(channel_mapping, channels, false)) { + pr_err("%s: map channels failed %d\n", + __func__, channels); + return -EINVAL; + } + } else { + memcpy(channel_mapping, channel_map, + PCM_FORMAT_MAX_NUM_CHANNEL); + } + + rc = apr_send_pkt(ac->apr, (uint32_t *) &fmt); + if (rc < 0) { + pr_err("%s: Comamnd open failed %d\n", __func__, rc); + rc = -EINVAL; + goto fail_cmd; + } + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), + msecs_to_jiffies(TIMEOUT_MS)); + if (!rc) { + pr_err("%s: timeout. waited for format update\n", __func__); + rc = -ETIMEDOUT; + goto fail_cmd; + } + if (atomic_read(&ac->cmd_state) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->cmd_state))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state)); + goto fail_cmd; + } + return 0; +fail_cmd: + return rc; +} + +static int __q6asm_media_format_block_pcm_v3(struct audio_client *ac, + uint32_t rate, uint32_t channels, + uint16_t bits_per_sample, + int stream_id, + bool use_default_chmap, + char *channel_map, + uint16_t sample_word_size) +{ + struct asm_multi_channel_pcm_fmt_blk_param_v3 fmt; + u8 *channel_mapping; + int rc; + + if (channels > PCM_FORMAT_MAX_NUM_CHANNEL) { + pr_err("%s: Invalid channel count %d\n", __func__, channels); + return -EINVAL; + } + + pr_debug("%s: session[%d]rate[%d]ch[%d]bps[%d]wordsize[%d]\n", __func__, + ac->session, rate, channels, + bits_per_sample, sample_word_size); + + memset(&fmt, 0, sizeof(fmt)); + q6asm_stream_add_hdr(ac, &fmt.hdr, sizeof(fmt), TRUE, stream_id); + atomic_set(&ac->cmd_state, -1); + /* + * Updated the token field with stream/session for compressed playback + * Platform driver must know the the stream with which the command is + * associated + */ + if (ac->io_mode & COMPRESSED_STREAM_IO) + fmt.hdr.token = ((ac->session << 8) & 0xFFFF00) | + (stream_id & 0xFF); + + pr_debug("%s: token = 0x%x, stream_id %d, session 0x%x\n", + __func__, fmt.hdr.token, stream_id, ac->session); + + fmt.hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2; + fmt.fmt_blk.fmt_blk_size = sizeof(fmt) - sizeof(fmt.hdr) - + sizeof(fmt.fmt_blk); + fmt.param.num_channels = channels; + fmt.param.bits_per_sample = bits_per_sample; + fmt.param.sample_rate = rate; + fmt.param.is_signed = 1; + fmt.param.sample_word_size = sample_word_size; + channel_mapping = fmt.param.channel_mapping; + + memset(channel_mapping, 0, PCM_FORMAT_MAX_NUM_CHANNEL); + + if (use_default_chmap) { + if (q6asm_map_channels(channel_mapping, channels, false)) { + pr_err("%s: map channels failed %d\n", + __func__, channels); + rc = -EINVAL; + goto fail_cmd; + } + } else { + memcpy(channel_mapping, channel_map, + PCM_FORMAT_MAX_NUM_CHANNEL); + } + + rc = apr_send_pkt(ac->apr, (uint32_t *) &fmt); + if (rc < 0) { + pr_err("%s: Comamnd open failed %d\n", __func__, rc); + rc = -EINVAL; + goto fail_cmd; + } + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), + msecs_to_jiffies(TIMEOUT_MS)); + if (!rc) { + pr_err("%s: timeout. waited for format update\n", __func__); + rc = -ETIMEDOUT; + goto fail_cmd; + } + if (atomic_read(&ac->cmd_state) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->cmd_state))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state)); + goto fail_cmd; + } + return 0; +fail_cmd: + return rc; +} + +static int __q6asm_media_format_block_pcm_v4(struct audio_client *ac, + uint32_t rate, uint32_t channels, + uint16_t bits_per_sample, + int stream_id, + bool use_default_chmap, + char *channel_map, + uint16_t sample_word_size, + uint16_t endianness, + uint16_t mode) +{ + struct asm_multi_channel_pcm_fmt_blk_param_v4 fmt; + u8 *channel_mapping; + int rc; + + if (channels > PCM_FORMAT_MAX_NUM_CHANNEL) { + pr_err("%s: Invalid channel count %d\n", __func__, channels); + return -EINVAL; + } + + pr_debug("%s: session[%d]rate[%d]ch[%d]bps[%d]wordsize[%d]\n", __func__, + ac->session, rate, channels, + bits_per_sample, sample_word_size); + + memset(&fmt, 0, sizeof(fmt)); + q6asm_stream_add_hdr(ac, &fmt.hdr, sizeof(fmt), TRUE, stream_id); + atomic_set(&ac->cmd_state, -1); + /* + * Updated the token field with stream/session for compressed playback + * Platform driver must know the the stream with which the command is + * associated + */ + if (ac->io_mode & COMPRESSED_STREAM_IO) + fmt.hdr.token = ((ac->session << 8) & 0xFFFF00) | + (stream_id & 0xFF); + + pr_debug("%s: token = 0x%x, stream_id %d, session 0x%x\n", + __func__, fmt.hdr.token, stream_id, ac->session); + + fmt.hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2; + fmt.fmt_blk.fmt_blk_size = sizeof(fmt) - sizeof(fmt.hdr) - + sizeof(fmt.fmt_blk); + fmt.param.num_channels = channels; + fmt.param.bits_per_sample = bits_per_sample; + fmt.param.sample_rate = rate; + fmt.param.is_signed = 1; + fmt.param.sample_word_size = sample_word_size; + fmt.param.endianness = endianness; + fmt.param.mode = mode; + channel_mapping = fmt.param.channel_mapping; + + memset(channel_mapping, 0, PCM_FORMAT_MAX_NUM_CHANNEL); + + if (use_default_chmap) { + if (q6asm_map_channels(channel_mapping, channels, false)) { + pr_err("%s: map channels failed %d\n", + __func__, channels); + rc = -EINVAL; + goto fail_cmd; + } + } else { + memcpy(channel_mapping, channel_map, + PCM_FORMAT_MAX_NUM_CHANNEL); + } + + rc = apr_send_pkt(ac->apr, (uint32_t *) &fmt); + if (rc < 0) { + pr_err("%s: Comamnd open failed %d\n", __func__, rc); + rc = -EINVAL; + goto fail_cmd; + } + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), + msecs_to_jiffies(TIMEOUT_MS)); + if (!rc) { + pr_err("%s: timeout. waited for format update\n", __func__); + rc = -ETIMEDOUT; + goto fail_cmd; + } + if (atomic_read(&ac->cmd_state) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->cmd_state))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state)); + goto fail_cmd; + } + return 0; +fail_cmd: + return rc; +} + + +static int __q6asm_media_format_block_pcm_v5(struct audio_client *ac, + uint32_t rate, uint32_t channels, + uint16_t bits_per_sample, + int stream_id, + bool use_default_chmap, + char *channel_map, + uint16_t sample_word_size, + uint16_t endianness, + uint16_t mode) +{ + struct asm_multi_channel_pcm_fmt_blk_param_v5 fmt; + u8 *channel_mapping; + int rc; + + if (channels > PCM_FORMAT_MAX_NUM_CHANNEL_V8) { + pr_err("%s: Invalid channel count %d\n", __func__, channels); + return -EINVAL; + } + + pr_debug("%s: session[%d]rate[%d]ch[%d]bps[%d]wordsize[%d]\n", __func__, + ac->session, rate, channels, + bits_per_sample, sample_word_size); + + memset(&fmt, 0, sizeof(fmt)); + q6asm_stream_add_hdr(ac, &fmt.hdr, sizeof(fmt), TRUE, stream_id); + atomic_set(&ac->cmd_state, -1); + /* + * Updated the token field with stream/session for compressed playback + * Platform driver must know the the stream with which the command is + * associated + */ + if (ac->io_mode & COMPRESSED_STREAM_IO) + fmt.hdr.token = ((ac->session << 8) & 0xFFFF00) | + (stream_id & 0xFF); + + pr_debug("%s: token = 0x%x, stream_id %d, session 0x%x\n", + __func__, fmt.hdr.token, stream_id, ac->session); + + fmt.hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2; + fmt.fmt_blk.fmt_blk_size = sizeof(fmt) - sizeof(fmt.hdr) - + sizeof(fmt.fmt_blk); + fmt.param.num_channels = (uint16_t) channels & 0xFFFF; + fmt.param.bits_per_sample = bits_per_sample; + fmt.param.sample_rate = rate; + fmt.param.is_signed = 1; + fmt.param.sample_word_size = sample_word_size; + fmt.param.endianness = endianness; + fmt.param.mode = mode; + channel_mapping = fmt.param.channel_mapping; + + memset(channel_mapping, 0, PCM_FORMAT_MAX_NUM_CHANNEL_V8); + + if (use_default_chmap) { + if (q6asm_map_channels(channel_mapping, fmt.param.num_channels, false)) { + pr_err("%s: map channels failed %d\n", + __func__, channels); + rc = -EINVAL; + goto fail_cmd; + } + } else { + memcpy(channel_mapping, channel_map, + PCM_FORMAT_MAX_NUM_CHANNEL_V8); + } + + rc = apr_send_pkt(ac->apr, (uint32_t *) &fmt); + if (rc < 0) { + pr_err("%s: Comamnd open failed %d\n", __func__, rc); + rc = -EINVAL; + goto fail_cmd; + } + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), 5*HZ); + if (!rc) { + pr_err("%s: timeout. waited for format update\n", __func__); + rc = -ETIMEDOUT; + goto fail_cmd; + } + if (atomic_read(&ac->cmd_state) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->cmd_state))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state)); + goto fail_cmd; + } + return 0; +fail_cmd: + return rc; +} + +/** + * q6asm_media_format_block_pcm - + * command to set mediafmt block for PCM on ASM stream + * + * @ac: Audio client handle + * @rate: sample rate + * @channels: number of ASM channels + * + * Returns 0 on success or error on failure + */ +int q6asm_media_format_block_pcm(struct audio_client *ac, + uint32_t rate, uint32_t channels) +{ + return __q6asm_media_format_block_pcm(ac, rate, + channels, 16, ac->stream_id, + true, NULL); +} +EXPORT_SYMBOL(q6asm_media_format_block_pcm); + +/** + * q6asm_media_format_block_pcm_format_support - + * command to set mediafmt block for PCM format support + * + * @ac: Audio client handle + * @rate: sample rate + * @channels: number of ASM channels + * @bits_per_sample: number of bits per sample + * + * Returns 0 on success or error on failure + */ +int q6asm_media_format_block_pcm_format_support(struct audio_client *ac, + uint32_t rate, uint32_t channels, + uint16_t bits_per_sample) +{ + return __q6asm_media_format_block_pcm(ac, rate, + channels, bits_per_sample, ac->stream_id, + true, NULL); +} +EXPORT_SYMBOL(q6asm_media_format_block_pcm_format_support); + +int q6asm_media_format_block_pcm_format_support_v2(struct audio_client *ac, + uint32_t rate, uint32_t channels, + uint16_t bits_per_sample, int stream_id, + bool use_default_chmap, char *channel_map) +{ + if (!use_default_chmap && (channel_map == NULL)) { + pr_err("%s: No valid chan map and can't use default\n", + __func__); + return -EINVAL; + } + return __q6asm_media_format_block_pcm(ac, rate, + channels, bits_per_sample, stream_id, + use_default_chmap, channel_map); +} + +/* + * q6asm_media_format_block_pcm_format_support_v3- sends pcm decoder + * configuration parameters + * + * @ac: Client session handle + * @rate: sample rate + * @channels: number of channels + * @bits_per_sample: bit width of encoder session + * @stream_id: stream id of stream to be associated with this session + * @use_default_chmap: true if default channel map to be used + * @channel_map: input channel map + * @sample_word_size: Size in bits of the word that holds a sample of a channel + */ +int q6asm_media_format_block_pcm_format_support_v3(struct audio_client *ac, + uint32_t rate, + uint32_t channels, + uint16_t bits_per_sample, + int stream_id, + bool use_default_chmap, + char *channel_map, + uint16_t sample_word_size) +{ + if (!use_default_chmap && (channel_map == NULL)) { + pr_err("%s: No valid chan map and can't use default\n", + __func__); + return -EINVAL; + } + return __q6asm_media_format_block_pcm_v3(ac, rate, + channels, bits_per_sample, stream_id, + use_default_chmap, channel_map, + sample_word_size); + +} +EXPORT_SYMBOL(q6asm_media_format_block_pcm_format_support_v3); + +/* + * q6asm_media_format_block_pcm_format_support_v4- sends pcm decoder + * configuration parameters + * + * @ac: Client session handle + * @rate: sample rate + * @channels: number of channels + * @bits_per_sample: bit width of encoder session + * @stream_id: stream id of stream to be associated with this session + * @use_default_chmap: true if default channel map to be used + * @channel_map: input channel map + * @sample_word_size: Size in bits of the word that holds a sample of a channel + * @endianness: endianness of the pcm data + * @mode: Mode to provide additional info about the pcm input data + */ +int q6asm_media_format_block_pcm_format_support_v4(struct audio_client *ac, + uint32_t rate, + uint32_t channels, + uint16_t bits_per_sample, + int stream_id, + bool use_default_chmap, + char *channel_map, + uint16_t sample_word_size, + uint16_t endianness, + uint16_t mode) +{ + if (!use_default_chmap && (channel_map == NULL)) { + pr_err("%s: No valid chan map and can't use default\n", + __func__); + return -EINVAL; + } + return __q6asm_media_format_block_pcm_v4(ac, rate, + channels, bits_per_sample, stream_id, + use_default_chmap, channel_map, + sample_word_size, endianness, + mode); + +} +EXPORT_SYMBOL(q6asm_media_format_block_pcm_format_support_v4); + + +/* + * q6asm_media_format_block_pcm_format_support_v5- sends pcm decoder + * configuration parameters + * + * @ac: Client session handle + * @rate: sample rate + * @channels: number of channels + * @bits_per_sample: bit width of encoder session + * @stream_id: stream id of stream to be associated with this session + * @use_default_chmap: true if default channel map to be used + * @channel_map: input channel map + * @sample_word_size: Size in bits of the word that holds a sample of a channel + * @endianness: endianness of the pcm data + * @mode: Mode to provide additional info about the pcm input data + */ +int q6asm_media_format_block_pcm_format_support_v5(struct audio_client *ac, + uint32_t rate, + uint32_t channels, + uint16_t bits_per_sample, + int stream_id, + bool use_default_chmap, + char *channel_map, + uint16_t sample_word_size, + uint16_t endianness, + uint16_t mode) +{ + if (!use_default_chmap && (channel_map == NULL)) { + pr_err("%s: No valid chan map and can't use default\n", + __func__); + return -EINVAL; + } + return __q6asm_media_format_block_pcm_v5(ac, rate, + channels, bits_per_sample, stream_id, + use_default_chmap, channel_map, + sample_word_size, endianness, + mode); + +} +EXPORT_SYMBOL(q6asm_media_format_block_pcm_format_support_v5); + + +static int __q6asm_media_format_block_multi_ch_pcm(struct audio_client *ac, + uint32_t rate, uint32_t channels, + bool use_default_chmap, char *channel_map, + uint16_t bits_per_sample) +{ + struct asm_multi_channel_pcm_fmt_blk_v2 fmt; + u8 *channel_mapping; + int rc = 0; + + if (channels > PCM_FORMAT_MAX_NUM_CHANNEL) { + pr_err("%s: Invalid channel count %d\n", __func__, channels); + return -EINVAL; + } + + pr_debug("%s: session[%d]rate[%d]ch[%d]\n", __func__, ac->session, rate, + channels); + + q6asm_add_hdr(ac, &fmt.hdr, sizeof(fmt), TRUE); + atomic_set(&ac->cmd_state, -1); + + fmt.hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2; + fmt.fmt_blk.fmt_blk_size = sizeof(fmt) - sizeof(fmt.hdr) - + sizeof(fmt.fmt_blk); + fmt.num_channels = channels; + fmt.bits_per_sample = bits_per_sample; + fmt.sample_rate = rate; + fmt.is_signed = 1; + + channel_mapping = fmt.channel_mapping; + + memset(channel_mapping, 0, PCM_FORMAT_MAX_NUM_CHANNEL); + + if (use_default_chmap) { + if (q6asm_map_channels(channel_mapping, channels, false)) { + pr_err("%s: map channels failed %d\n", + __func__, channels); + return -EINVAL; + } + } else { + memcpy(channel_mapping, channel_map, + PCM_FORMAT_MAX_NUM_CHANNEL); + } + + rc = apr_send_pkt(ac->apr, (uint32_t *) &fmt); + if (rc < 0) { + pr_err("%s: Comamnd open failed %d\n", __func__, rc); + rc = -EINVAL; + goto fail_cmd; + } + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), + msecs_to_jiffies(TIMEOUT_MS)); + if (!rc) { + pr_err("%s: timeout. waited for format update\n", __func__); + rc = -ETIMEDOUT; + goto fail_cmd; + } + if (atomic_read(&ac->cmd_state) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->cmd_state))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state)); + goto fail_cmd; + } + return 0; +fail_cmd: + return rc; +} + +static int __q6asm_media_format_block_multi_ch_pcm_v3(struct audio_client *ac, + uint32_t rate, + uint32_t channels, + bool use_default_chmap, + char *channel_map, + uint16_t bits_per_sample, + uint16_t sample_word_size) +{ + struct asm_multi_channel_pcm_fmt_blk_param_v3 fmt; + u8 *channel_mapping; + int rc; + + if (channels > PCM_FORMAT_MAX_NUM_CHANNEL) { + pr_err("%s: Invalid channel count %d\n", __func__, channels); + return -EINVAL; + } + + pr_debug("%s: session[%d]rate[%d]ch[%d]bps[%d]wordsize[%d]\n", __func__, + ac->session, rate, channels, + bits_per_sample, sample_word_size); + + memset(&fmt, 0, sizeof(fmt)); + q6asm_add_hdr(ac, &fmt.hdr, sizeof(fmt), TRUE); + atomic_set(&ac->cmd_state, -1); + + fmt.hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2; + fmt.fmt_blk.fmt_blk_size = sizeof(fmt) - sizeof(fmt.hdr) - + sizeof(fmt.fmt_blk); + fmt.param.num_channels = channels; + fmt.param.bits_per_sample = bits_per_sample; + fmt.param.sample_rate = rate; + fmt.param.is_signed = 1; + fmt.param.sample_word_size = sample_word_size; + channel_mapping = fmt.param.channel_mapping; + + memset(channel_mapping, 0, PCM_FORMAT_MAX_NUM_CHANNEL); + + if (use_default_chmap) { + if (q6asm_map_channels(channel_mapping, channels, false)) { + pr_err("%s: map channels failed %d\n", + __func__, channels); + rc = -EINVAL; + goto fail_cmd; + } + } else { + memcpy(channel_mapping, channel_map, + PCM_FORMAT_MAX_NUM_CHANNEL); + } + + rc = apr_send_pkt(ac->apr, (uint32_t *) &fmt); + if (rc < 0) { + pr_err("%s: Comamnd open failed %d\n", __func__, rc); + goto fail_cmd; + } + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), + msecs_to_jiffies(TIMEOUT_MS)); + if (!rc) { + pr_err("%s: timeout. waited for format update\n", __func__); + rc = -ETIMEDOUT; + goto fail_cmd; + } + if (atomic_read(&ac->cmd_state) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->cmd_state))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state)); + goto fail_cmd; + } + return 0; +fail_cmd: + return rc; +} + +static int __q6asm_media_format_block_multi_ch_pcm_v4(struct audio_client *ac, + uint32_t rate, + uint32_t channels, + bool use_default_chmap, + char *channel_map, + uint16_t bits_per_sample, + uint16_t sample_word_size, + uint16_t endianness, + uint16_t mode) +{ + struct asm_multi_channel_pcm_fmt_blk_param_v4 fmt; + u8 *channel_mapping; + int rc; + + if (channels > PCM_FORMAT_MAX_NUM_CHANNEL) { + pr_err("%s: Invalid channel count %d\n", __func__, channels); + return -EINVAL; + } + + pr_debug("%s: session[%d]rate[%d]ch[%d]bps[%d]wordsize[%d]\n", __func__, + ac->session, rate, channels, + bits_per_sample, sample_word_size); + + memset(&fmt, 0, sizeof(fmt)); + q6asm_add_hdr(ac, &fmt.hdr, sizeof(fmt), TRUE); + atomic_set(&ac->cmd_state, -1); + + fmt.hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2; + fmt.fmt_blk.fmt_blk_size = sizeof(fmt) - sizeof(fmt.hdr) - + sizeof(fmt.fmt_blk); + fmt.param.num_channels = channels; + fmt.param.bits_per_sample = bits_per_sample; + fmt.param.sample_rate = rate; + fmt.param.is_signed = 1; + fmt.param.sample_word_size = sample_word_size; + fmt.param.endianness = endianness; + fmt.param.mode = mode; + channel_mapping = fmt.param.channel_mapping; + + memset(channel_mapping, 0, PCM_FORMAT_MAX_NUM_CHANNEL); + + if (use_default_chmap) { + if (q6asm_map_channels(channel_mapping, channels, false)) { + pr_err("%s: map channels failed %d\n", + __func__, channels); + rc = -EINVAL; + goto fail_cmd; + } + } else { + memcpy(channel_mapping, channel_map, + PCM_FORMAT_MAX_NUM_CHANNEL); + } + + rc = apr_send_pkt(ac->apr, (uint32_t *) &fmt); + if (rc < 0) { + pr_err("%s: Comamnd open failed %d\n", __func__, rc); + goto fail_cmd; + } + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), + msecs_to_jiffies(TIMEOUT_MS)); + if (!rc) { + pr_err("%s: timeout. waited for format update\n", __func__); + rc = -ETIMEDOUT; + goto fail_cmd; + } + if (atomic_read(&ac->cmd_state) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->cmd_state))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state)); + goto fail_cmd; + } + return 0; +fail_cmd: + return rc; +} + +static int __q6asm_media_format_block_multi_ch_pcm_v5(struct audio_client *ac, + uint32_t rate, + uint32_t channels, + bool use_default_chmap, + char *channel_map, + uint16_t bits_per_sample, + uint16_t sample_word_size, + uint16_t endianness, + uint16_t mode) +{ + struct asm_multi_channel_pcm_fmt_blk_param_v5 fmt; + u8 *channel_mapping; + int rc; + + if (channels > PCM_FORMAT_MAX_NUM_CHANNEL_V8) { + pr_err("%s: Invalid channel count %d\n", __func__, channels); + return -EINVAL; + } + + pr_debug("%s: session[%d]rate[%d]ch[%d]bps[%d]wordsize[%d]\n", __func__, + ac->session, rate, channels, + bits_per_sample, sample_word_size); + + memset(&fmt, 0, sizeof(fmt)); + q6asm_add_hdr(ac, &fmt.hdr, sizeof(fmt), TRUE); + atomic_set(&ac->cmd_state, -1); + + fmt.hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2; + fmt.fmt_blk.fmt_blk_size = sizeof(fmt) - sizeof(fmt.hdr) - + sizeof(fmt.fmt_blk); + fmt.param.num_channels = channels; + fmt.param.bits_per_sample = bits_per_sample; + fmt.param.sample_rate = rate; + fmt.param.is_signed = 1; + fmt.param.sample_word_size = sample_word_size; + fmt.param.endianness = endianness; + fmt.param.mode = mode; + channel_mapping = fmt.param.channel_mapping; + + memset(channel_mapping, 0, PCM_FORMAT_MAX_NUM_CHANNEL_V8); + + if (use_default_chmap) { + if (q6asm_map_channels(channel_mapping, channels, false)) { + pr_err("%s: map channels failed %d\n", + __func__, channels); + rc = -EINVAL; + goto fail_cmd; + } + } else { + memcpy(channel_mapping, channel_map, + PCM_FORMAT_MAX_NUM_CHANNEL_V8); + } + + rc = apr_send_pkt(ac->apr, (uint32_t *) &fmt); + if (rc < 0) { + pr_err("%s: Comamnd open failed %d\n", __func__, rc); + goto fail_cmd; + } + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), 5*HZ); + if (!rc) { + pr_err("%s: timeout. waited for format update\n", __func__); + rc = -ETIMEDOUT; + goto fail_cmd; + } + if (atomic_read(&ac->cmd_state) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->cmd_state))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state)); + goto fail_cmd; + } + return 0; +fail_cmd: + return rc; +} + +int q6asm_media_format_block_multi_ch_pcm(struct audio_client *ac, + uint32_t rate, uint32_t channels, + bool use_default_chmap, char *channel_map) +{ + return __q6asm_media_format_block_multi_ch_pcm(ac, rate, + channels, use_default_chmap, channel_map, 16); +} + +int q6asm_media_format_block_multi_ch_pcm_v2( + struct audio_client *ac, + uint32_t rate, uint32_t channels, + bool use_default_chmap, char *channel_map, + uint16_t bits_per_sample) +{ + return __q6asm_media_format_block_multi_ch_pcm(ac, rate, + channels, use_default_chmap, channel_map, + bits_per_sample); +} + +/* + * q6asm_media_format_block_multi_ch_pcm_v3 - sends pcm decoder configuration + * parameters + * + * @ac: Client session handle + * @rate: sample rate + * @channels: number of channels + * @bits_per_sample: bit width of encoder session + * @use_default_chmap: true if default channel map to be used + * @channel_map: input channel map + * @sample_word_size: Size in bits of the word that holds a sample of a channel + */ +int q6asm_media_format_block_multi_ch_pcm_v3(struct audio_client *ac, + uint32_t rate, uint32_t channels, + bool use_default_chmap, + char *channel_map, + uint16_t bits_per_sample, + uint16_t sample_word_size) +{ + return __q6asm_media_format_block_multi_ch_pcm_v3(ac, rate, channels, + use_default_chmap, + channel_map, + bits_per_sample, + sample_word_size); +} +EXPORT_SYMBOL(q6asm_media_format_block_multi_ch_pcm_v3); + +/* + * q6asm_media_format_block_multi_ch_pcm_v4 - sends pcm decoder configuration + * parameters + * + * @ac: Client session handle + * @rate: sample rate + * @channels: number of channels + * @bits_per_sample: bit width of encoder session + * @use_default_chmap: true if default channel map to be used + * @channel_map: input channel map + * @sample_word_size: Size in bits of the word that holds a sample of a channel + * @endianness: endianness of the pcm data + * @mode: Mode to provide additional info about the pcm input data + */ +int q6asm_media_format_block_multi_ch_pcm_v4(struct audio_client *ac, + uint32_t rate, uint32_t channels, + bool use_default_chmap, + char *channel_map, + uint16_t bits_per_sample, + uint16_t sample_word_size, + uint16_t endianness, + uint16_t mode) +{ + return __q6asm_media_format_block_multi_ch_pcm_v4(ac, rate, channels, + use_default_chmap, + channel_map, + bits_per_sample, + sample_word_size, + endianness, + mode); +} +EXPORT_SYMBOL(q6asm_media_format_block_multi_ch_pcm_v4); + + +/* + * q6asm_media_format_block_multi_ch_pcm_v5 - sends pcm decoder configuration + * parameters + * + * @ac: Client session handle + * @rate: sample rate + * @channels: number of channels + * @bits_per_sample: bit width of encoder session + * @use_default_chmap: true if default channel map to be used + * @channel_map: input channel map + * @sample_word_size: Size in bits of the word that holds a sample of a channel + * @endianness: endianness of the pcm data + * @mode: Mode to provide additional info about the pcm input data + */ +int q6asm_media_format_block_multi_ch_pcm_v5(struct audio_client *ac, + uint32_t rate, uint32_t channels, + bool use_default_chmap, + char *channel_map, + uint16_t bits_per_sample, + uint16_t sample_word_size, + uint16_t endianness, + uint16_t mode) +{ + return __q6asm_media_format_block_multi_ch_pcm_v5(ac, rate, channels, + use_default_chmap, + channel_map, + bits_per_sample, + sample_word_size, + endianness, + mode); +} +EXPORT_SYMBOL(q6asm_media_format_block_multi_ch_pcm_v5); + + +/* + * q6asm_media_format_block_gen_compr - set up generic compress format params + * + * @ac: Client session handle + * @rate: sample rate + * @channels: number of channels + * @use_default_chmap: true if default channel map to be used + * @channel_map: input channel map + * @bits_per_sample: bit width of gen compress stream + */ +int q6asm_media_format_block_gen_compr(struct audio_client *ac, + uint32_t rate, uint32_t channels, + bool use_default_chmap, char *channel_map, + uint16_t bits_per_sample) +{ + struct asm_generic_compressed_fmt_blk_t fmt; + u8 *channel_mapping; + int rc = 0; + + if (channels > PCM_FORMAT_MAX_NUM_CHANNEL) { + pr_err("%s: Invalid channel count %d\n", __func__, channels); + return -EINVAL; + } + + pr_debug("%s: session[%d]rate[%d]ch[%d]bps[%d]\n", + __func__, ac->session, rate, + channels, bits_per_sample); + + memset(&fmt, 0, sizeof(fmt)); + q6asm_add_hdr(ac, &fmt.hdr, sizeof(fmt), TRUE); + + fmt.hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2; + fmt.fmt_blk.fmt_blk_size = sizeof(fmt) - sizeof(fmt.hdr) - + sizeof(fmt.fmt_blk); + fmt.num_channels = channels; + fmt.bits_per_sample = bits_per_sample; + fmt.sampling_rate = rate; + + channel_mapping = fmt.channel_mapping; + + memset(channel_mapping, 0, PCM_FORMAT_MAX_NUM_CHANNEL); + + if (use_default_chmap) { + if (q6asm_map_channels(channel_mapping, channels, false)) { + pr_err("%s: map channels failed %d\n", + __func__, channels); + return -EINVAL; + } + } else { + memcpy(channel_mapping, channel_map, + PCM_FORMAT_MAX_NUM_CHANNEL); + } + + atomic_set(&ac->cmd_state, -1); + rc = apr_send_pkt(ac->apr, (uint32_t *) &fmt); + if (rc < 0) { + pr_err("%s: Comamnd open failed %d\n", __func__, rc); + rc = -EINVAL; + goto fail_cmd; + } + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), + msecs_to_jiffies(TIMEOUT_MS)); + if (!rc) { + pr_err("%s: timeout. waited for format update\n", __func__); + rc = -ETIMEDOUT; + goto fail_cmd; + } + + if (atomic_read(&ac->cmd_state) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->cmd_state))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state)); + } + return 0; +fail_cmd: + return rc; +} +EXPORT_SYMBOL(q6asm_media_format_block_gen_compr); + + +/* + * q6asm_media_format_block_iec - set up IEC61937 (compressed) or IEC60958 + * (pcm) format params. Both audio standards + * use the same format and are used for + * HDMI or SPDIF. + * + * @ac: Client session handle + * @rate: sample rate + * @channels: number of channels + */ +int q6asm_media_format_block_iec(struct audio_client *ac, + uint32_t rate, uint32_t channels) +{ + struct asm_iec_compressed_fmt_blk_t fmt; + int rc = 0; + + pr_debug("%s: session[%d]rate[%d]ch[%d]\n", + __func__, ac->session, rate, + channels); + + memset(&fmt, 0, sizeof(fmt)); + q6asm_add_hdr(ac, &fmt.hdr, sizeof(fmt), TRUE); + + fmt.hdr.opcode = ASM_DATA_CMD_IEC_60958_MEDIA_FMT; + fmt.num_channels = channels; + fmt.sampling_rate = rate; + + atomic_set(&ac->cmd_state, -1); + rc = apr_send_pkt(ac->apr, (uint32_t *) &fmt); + if (rc < 0) { + pr_err("%s: Comamnd open failed %d\n", __func__, rc); + rc = -EINVAL; + goto fail_cmd; + } + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), + msecs_to_jiffies(TIMEOUT_MS)); + if (!rc) { + pr_err("%s: timeout. waited for format update\n", __func__); + rc = -ETIMEDOUT; + goto fail_cmd; + } + + if (atomic_read(&ac->cmd_state) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->cmd_state))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state)); + } + return 0; +fail_cmd: + return rc; +} +EXPORT_SYMBOL(q6asm_media_format_block_iec); + +static int __q6asm_media_format_block_multi_aac(struct audio_client *ac, + struct asm_aac_cfg *cfg, int stream_id) +{ + struct asm_aac_fmt_blk_v2 fmt; + int rc = 0; + + pr_debug("%s: session[%d]rate[%d]ch[%d]\n", __func__, ac->session, + cfg->sample_rate, cfg->ch_cfg); + + q6asm_stream_add_hdr(ac, &fmt.hdr, sizeof(fmt), TRUE, stream_id); + atomic_set(&ac->cmd_state, -1); + /* + * Updated the token field with stream/session for compressed playback + * Platform driver must know the the stream with which the command is + * associated + */ + if (ac->io_mode & COMPRESSED_STREAM_IO) + q6asm_update_token(&fmt.hdr.token, + ac->session, + stream_id, + 0, /* Buffer index is NA */ + 0, /* Direction flag is NA */ + WAIT_CMD); + + pr_debug("%s: token = 0x%x, stream_id %d, session 0x%x\n", + __func__, fmt.hdr.token, stream_id, ac->session); + fmt.hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2; + fmt.fmt_blk.fmt_blk_size = sizeof(fmt) - sizeof(fmt.hdr) - + sizeof(fmt.fmt_blk); + fmt.aac_fmt_flag = cfg->format; + fmt.audio_objype = cfg->aot; + /* If zero, PCE is assumed to be available in bitstream*/ + fmt.total_size_of_PCE_bits = 0; + fmt.channel_config = cfg->ch_cfg; + fmt.sample_rate = cfg->sample_rate; + + pr_debug("%s: format=0x%x cfg_size=%d aac-cfg=0x%x aot=%d ch=%d sr=%d\n", + __func__, fmt.aac_fmt_flag, fmt.fmt_blk.fmt_blk_size, + fmt.aac_fmt_flag, + fmt.audio_objype, + fmt.channel_config, + fmt.sample_rate); + rc = apr_send_pkt(ac->apr, (uint32_t *) &fmt); + if (rc < 0) { + pr_err("%s: Comamnd open failed %d\n", __func__, rc); + rc = -EINVAL; + goto fail_cmd; + } + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), + msecs_to_jiffies(TIMEOUT_MS)); + if (!rc) { + pr_err("%s: timeout. waited for FORMAT_UPDATE\n", __func__); + rc = -ETIMEDOUT; + goto fail_cmd; + } + if (atomic_read(&ac->cmd_state) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->cmd_state))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state)); + goto fail_cmd; + } + return 0; +fail_cmd: + return rc; +} + +/** + * q6asm_media_format_block_multi_aac - + * command to set mediafmt block for multi_aac on ASM stream + * + * @ac: Audio client handle + * @cfg: multi_aac config + * + * Returns 0 on success or error on failure + */ +int q6asm_media_format_block_multi_aac(struct audio_client *ac, + struct asm_aac_cfg *cfg) +{ + return __q6asm_media_format_block_multi_aac(ac, cfg, ac->stream_id); +} +EXPORT_SYMBOL(q6asm_media_format_block_multi_aac); + +/** + * q6asm_media_format_block_aac - + * command to set mediafmt block for aac on ASM + * + * @ac: Audio client handle + * @cfg: aac config + * + * Returns 0 on success or error on failure + */ +int q6asm_media_format_block_aac(struct audio_client *ac, + struct asm_aac_cfg *cfg) +{ + return __q6asm_media_format_block_multi_aac(ac, cfg, ac->stream_id); +} +EXPORT_SYMBOL(q6asm_media_format_block_aac); + +/** + * q6asm_stream_media_format_block_aac - + * command to set mediafmt block for aac on ASM stream + * + * @ac: Audio client handle + * @cfg: aac config + * @stream_id: stream ID info + * + * Returns 0 on success or error on failure + */ +int q6asm_stream_media_format_block_aac(struct audio_client *ac, + struct asm_aac_cfg *cfg, int stream_id) +{ + return __q6asm_media_format_block_multi_aac(ac, cfg, stream_id); +} +EXPORT_SYMBOL(q6asm_stream_media_format_block_aac); + +/** + * q6asm_media_format_block_wma - + * command to set mediafmt block for wma on ASM stream + * + * @ac: Audio client handle + * @cfg: wma config + * @stream_id: stream ID info + * + * Returns 0 on success or error on failure + */ +int q6asm_media_format_block_wma(struct audio_client *ac, + void *cfg, int stream_id) +{ + struct asm_wmastdv9_fmt_blk_v2 fmt; + struct asm_wma_cfg *wma_cfg = (struct asm_wma_cfg *)cfg; + int rc = 0; + + pr_debug("session[%d]format_tag[0x%4x] rate[%d] ch[0x%4x] bps[%d], balign[0x%4x], bit_sample[0x%4x], ch_msk[%d], enc_opt[0x%4x]\n", + ac->session, wma_cfg->format_tag, wma_cfg->sample_rate, + wma_cfg->ch_cfg, wma_cfg->avg_bytes_per_sec, + wma_cfg->block_align, wma_cfg->valid_bits_per_sample, + wma_cfg->ch_mask, wma_cfg->encode_opt); + + q6asm_stream_add_hdr(ac, &fmt.hdr, sizeof(fmt), TRUE, stream_id); + atomic_set(&ac->cmd_state, -1); + + fmt.hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2; + fmt.fmtblk.fmt_blk_size = sizeof(fmt) - sizeof(fmt.hdr) - + sizeof(fmt.fmtblk); + fmt.fmtag = wma_cfg->format_tag; + fmt.num_channels = wma_cfg->ch_cfg; + fmt.sample_rate = wma_cfg->sample_rate; + fmt.avg_bytes_per_sec = wma_cfg->avg_bytes_per_sec; + fmt.blk_align = wma_cfg->block_align; + fmt.bits_per_sample = + wma_cfg->valid_bits_per_sample; + fmt.channel_mask = wma_cfg->ch_mask; + fmt.enc_options = wma_cfg->encode_opt; + + rc = apr_send_pkt(ac->apr, (uint32_t *) &fmt); + if (rc < 0) { + pr_err("%s: Comamnd open failed %d\n", __func__, rc); + rc = -EINVAL; + goto fail_cmd; + } + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), + msecs_to_jiffies(TIMEOUT_MS)); + if (!rc) { + pr_err("%s: timeout. waited for FORMAT_UPDATE\n", __func__); + rc = -ETIMEDOUT; + goto fail_cmd; + } + if (atomic_read(&ac->cmd_state) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->cmd_state))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state)); + goto fail_cmd; + } + return 0; +fail_cmd: + return rc; +} +EXPORT_SYMBOL(q6asm_media_format_block_wma); + +/** + * q6asm_media_format_block_wmapro - + * command to set mediafmt block for wmapro on ASM stream + * + * @ac: Audio client handle + * @cfg: wmapro config + * @stream_id: stream ID info + * + * Returns 0 on success or error on failure + */ +int q6asm_media_format_block_wmapro(struct audio_client *ac, + void *cfg, int stream_id) +{ + struct asm_wmaprov10_fmt_blk_v2 fmt; + struct asm_wmapro_cfg *wmapro_cfg = (struct asm_wmapro_cfg *)cfg; + int rc = 0; + + pr_debug("%s: session[%d]format_tag[0x%4x] rate[%d] ch[0x%4x] bps[%d], balign[0x%4x], bit_sample[0x%4x], ch_msk[%d], enc_opt[0x%4x], adv_enc_opt[0x%4x], adv_enc_opt2[0x%8x]\n", + __func__, + ac->session, wmapro_cfg->format_tag, wmapro_cfg->sample_rate, + wmapro_cfg->ch_cfg, wmapro_cfg->avg_bytes_per_sec, + wmapro_cfg->block_align, wmapro_cfg->valid_bits_per_sample, + wmapro_cfg->ch_mask, wmapro_cfg->encode_opt, + wmapro_cfg->adv_encode_opt, wmapro_cfg->adv_encode_opt2); + + q6asm_stream_add_hdr(ac, &fmt.hdr, sizeof(fmt), TRUE, stream_id); + atomic_set(&ac->cmd_state, -1); + + fmt.hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2; + fmt.fmtblk.fmt_blk_size = sizeof(fmt) - sizeof(fmt.hdr) - + sizeof(fmt.fmtblk); + + fmt.fmtag = wmapro_cfg->format_tag; + fmt.num_channels = wmapro_cfg->ch_cfg; + fmt.sample_rate = wmapro_cfg->sample_rate; + fmt.avg_bytes_per_sec = + wmapro_cfg->avg_bytes_per_sec; + fmt.blk_align = wmapro_cfg->block_align; + fmt.bits_per_sample = wmapro_cfg->valid_bits_per_sample; + fmt.channel_mask = wmapro_cfg->ch_mask; + fmt.enc_options = wmapro_cfg->encode_opt; + fmt.usAdvancedEncodeOpt = wmapro_cfg->adv_encode_opt; + fmt.advanced_enc_options2 = wmapro_cfg->adv_encode_opt2; + + rc = apr_send_pkt(ac->apr, (uint32_t *) &fmt); + if (rc < 0) { + pr_err("%s: Comamnd open failed %d\n", __func__, rc); + rc = -EINVAL; + goto fail_cmd; + } + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), + msecs_to_jiffies(TIMEOUT_MS)); + if (!rc) { + pr_err("%s: timeout. waited for FORMAT_UPDATE\n", __func__); + rc = -ETIMEDOUT; + goto fail_cmd; + } + if (atomic_read(&ac->cmd_state) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->cmd_state))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state)); + goto fail_cmd; + } + return 0; +fail_cmd: + return rc; +} +EXPORT_SYMBOL(q6asm_media_format_block_wmapro); + +/** + * q6asm_media_format_block_amrwbplus - + * command to set mediafmt block for amrwbplus on ASM stream + * + * @ac: Audio client handle + * @cfg: amrwbplus config + * @stream_id: stream ID info + * + * Returns 0 on success or error on failure + */ +int q6asm_media_format_block_amrwbplus(struct audio_client *ac, + struct asm_amrwbplus_cfg *cfg) +{ + struct asm_amrwbplus_fmt_blk_v2 fmt; + int rc = 0; + + pr_debug("%s: session[%d]band-mode[%d]frame-fmt[%d]ch[%d]\n", + __func__, + ac->session, + cfg->amr_band_mode, + cfg->amr_frame_fmt, + cfg->num_channels); + + q6asm_add_hdr(ac, &fmt.hdr, sizeof(fmt), TRUE); + atomic_set(&ac->cmd_state, -1); + + fmt.hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2; + fmt.fmtblk.fmt_blk_size = sizeof(fmt) - sizeof(fmt.hdr) - + sizeof(fmt.fmtblk); + fmt.amr_frame_fmt = cfg->amr_frame_fmt; + + rc = apr_send_pkt(ac->apr, (uint32_t *) &fmt); + if (rc < 0) { + pr_err("%s: Comamnd media format update failed.. %d\n", + __func__, rc); + rc = -EINVAL; + goto fail_cmd; + } + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), + msecs_to_jiffies(TIMEOUT_MS)); + if (!rc) { + pr_err("%s: timeout. waited for FORMAT_UPDATE\n", __func__); + rc = -ETIMEDOUT; + goto fail_cmd; + } + if (atomic_read(&ac->cmd_state) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->cmd_state))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state)); + goto fail_cmd; + } + return 0; +fail_cmd: + return rc; +} +EXPORT_SYMBOL(q6asm_media_format_block_amrwbplus); + +/** + * q6asm_stream_media_format_block_flac - + * command to set mediafmt block for flac on ASM stream + * + * @ac: Audio client handle + * @cfg: FLAC config + * @stream_id: stream ID info + * + * Returns 0 on success or error on failure + */ +int q6asm_stream_media_format_block_flac(struct audio_client *ac, + struct asm_flac_cfg *cfg, int stream_id) +{ + struct asm_flac_fmt_blk_v2 fmt; + int rc = 0; + + pr_debug("%s :session[%d] rate[%d] ch[%d] size[%d] stream_id[%d]\n", + __func__, ac->session, cfg->sample_rate, cfg->ch_cfg, + cfg->sample_size, stream_id); + + q6asm_stream_add_hdr(ac, &fmt.hdr, sizeof(fmt), TRUE, stream_id); + atomic_set(&ac->cmd_state, -1); + + fmt.hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2; + fmt.fmtblk.fmt_blk_size = sizeof(fmt) - sizeof(fmt.hdr) - + sizeof(fmt.fmtblk); + + fmt.is_stream_info_present = cfg->stream_info_present; + fmt.num_channels = cfg->ch_cfg; + fmt.min_blk_size = cfg->min_blk_size; + fmt.max_blk_size = cfg->max_blk_size; + fmt.sample_rate = cfg->sample_rate; + fmt.min_frame_size = cfg->min_frame_size; + fmt.max_frame_size = cfg->max_frame_size; + fmt.sample_size = cfg->sample_size; + + rc = apr_send_pkt(ac->apr, (uint32_t *) &fmt); + if (rc < 0) { + pr_err("%s :Comamnd media format update failed %d\n", + __func__, rc); + goto fail_cmd; + } + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), + msecs_to_jiffies(TIMEOUT_MS)); + if (!rc) { + pr_err("%s :timeout. waited for FORMAT_UPDATE\n", __func__); + rc = -ETIMEDOUT; + goto fail_cmd; + } + + if (atomic_read(&ac->cmd_state) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->cmd_state))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state)); + goto fail_cmd; + } + return 0; +fail_cmd: + return rc; +} +EXPORT_SYMBOL(q6asm_stream_media_format_block_flac); + +/** + * q6asm_media_format_block_alac - + * command to set mediafmt block for alac on ASM stream + * + * @ac: Audio client handle + * @cfg: ALAC config + * @stream_id: stream ID info + * + * Returns 0 on success or error on failure + */ +int q6asm_media_format_block_alac(struct audio_client *ac, + struct asm_alac_cfg *cfg, int stream_id) +{ + struct asm_alac_fmt_blk_v2 fmt; + int rc = 0; + + pr_debug("%s :session[%d]rate[%d]ch[%d]\n", __func__, + ac->session, cfg->sample_rate, cfg->num_channels); + + q6asm_stream_add_hdr(ac, &fmt.hdr, sizeof(fmt), TRUE, stream_id); + atomic_set(&ac->cmd_state, -1); + + fmt.hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2; + fmt.fmtblk.fmt_blk_size = sizeof(fmt) - sizeof(fmt.hdr) - + sizeof(fmt.fmtblk); + + fmt.frame_length = cfg->frame_length; + fmt.compatible_version = cfg->compatible_version; + fmt.bit_depth = cfg->bit_depth; + fmt.pb = cfg->pb; + fmt.mb = cfg->mb; + fmt.kb = cfg->kb; + fmt.num_channels = cfg->num_channels; + fmt.max_run = cfg->max_run; + fmt.max_frame_bytes = cfg->max_frame_bytes; + fmt.avg_bit_rate = cfg->avg_bit_rate; + fmt.sample_rate = cfg->sample_rate; + fmt.channel_layout_tag = cfg->channel_layout_tag; + + rc = apr_send_pkt(ac->apr, (uint32_t *) &fmt); + if (rc < 0) { + pr_err("%s :Comamnd media format update failed %d\n", + __func__, rc); + goto fail_cmd; + } + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), + msecs_to_jiffies(TIMEOUT_MS)); + if (!rc) { + pr_err("%s :timeout. waited for FORMAT_UPDATE\n", __func__); + rc = -ETIMEDOUT; + goto fail_cmd; + } + + if (atomic_read(&ac->cmd_state) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->cmd_state))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state)); + goto fail_cmd; + } + return 0; +fail_cmd: + return rc; +} +EXPORT_SYMBOL(q6asm_media_format_block_alac); + +/* + * q6asm_media_format_block_g711 - sends g711 decoder configuration + * parameters + * @ac: Client session handle + * @cfg: Audio stream manager configuration parameters + * @stream_id: Stream id + */ +int q6asm_media_format_block_g711(struct audio_client *ac, + struct asm_g711_dec_cfg *cfg, int stream_id) +{ + struct asm_g711_dec_fmt_blk_v2 fmt; + int rc = 0; + + if (!ac) { + pr_err("%s: audio client is null\n", __func__); + return -EINVAL; + } + if (!cfg) { + pr_err("%s: Invalid ASM config\n", __func__); + return -EINVAL; + } + + if (stream_id <= 0) { + pr_err("%s: Invalid stream id\n", __func__); + return -EINVAL; + } + + pr_debug("%s :session[%d]rate[%d]\n", __func__, + ac->session, cfg->sample_rate); + + memset(&fmt, 0, sizeof(struct asm_g711_dec_fmt_blk_v2)); + + q6asm_stream_add_hdr(ac, &fmt.hdr, sizeof(fmt), TRUE, stream_id); + atomic_set(&ac->cmd_state, -1); + + fmt.hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2; + fmt.fmtblk.fmt_blk_size = sizeof(fmt) - sizeof(fmt.hdr) - + sizeof(fmt.fmtblk); + + fmt.sample_rate = cfg->sample_rate; + + rc = apr_send_pkt(ac->apr, (uint32_t *) &fmt); + if (rc < 0) { + pr_err("%s :Command media format update failed %d\n", + __func__, rc); + goto fail_cmd; + } + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), + msecs_to_jiffies(TIMEOUT_MS)); + if (!rc) { + pr_err("%s :timeout. waited for FORMAT_UPDATE\n", __func__); + rc = -ETIMEDOUT; + goto fail_cmd; + } + + if (atomic_read(&ac->cmd_state) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->cmd_state))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state)); + goto fail_cmd; + } + return 0; +fail_cmd: + return rc; +} +EXPORT_SYMBOL(q6asm_media_format_block_g711); + +/** + * q6asm_stream_media_format_block_vorbis - + * command to set mediafmt block for vorbis on ASM stream + * + * @ac: Audio client handle + * @cfg: vorbis config + * @stream_id: stream ID info + * + * Returns 0 on success or error on failure + */ +int q6asm_stream_media_format_block_vorbis(struct audio_client *ac, + struct asm_vorbis_cfg *cfg, int stream_id) +{ + struct asm_vorbis_fmt_blk_v2 fmt; + int rc = 0; + + pr_debug("%s :session[%d] bit_stream_fmt[%d] stream_id[%d]\n", + __func__, ac->session, cfg->bit_stream_fmt, stream_id); + + q6asm_stream_add_hdr(ac, &fmt.hdr, sizeof(fmt), TRUE, stream_id); + atomic_set(&ac->cmd_state, -1); + + fmt.hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2; + fmt.fmtblk.fmt_blk_size = sizeof(fmt) - sizeof(fmt.hdr) - + sizeof(fmt.fmtblk); + + fmt.bit_stream_fmt = cfg->bit_stream_fmt; + + rc = apr_send_pkt(ac->apr, (uint32_t *) &fmt); + if (rc < 0) { + pr_err("%s :Comamnd media format update failed %d\n", + __func__, rc); + goto fail_cmd; + } + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), + msecs_to_jiffies(TIMEOUT_MS)); + if (!rc) { + pr_err("%s :timeout. waited for FORMAT_UPDATE\n", __func__); + rc = -ETIMEDOUT; + goto fail_cmd; + } + + if (atomic_read(&ac->cmd_state) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->cmd_state))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state)); + goto fail_cmd; + } + return 0; +fail_cmd: + return rc; +} +EXPORT_SYMBOL(q6asm_stream_media_format_block_vorbis); + +/** + * q6asm_media_format_block_ape - + * command to set mediafmt block for APE on ASM stream + * + * @ac: Audio client handle + * @cfg: APE config + * @stream_id: stream ID info + * + * Returns 0 on success or error on failure + */ +int q6asm_media_format_block_ape(struct audio_client *ac, + struct asm_ape_cfg *cfg, int stream_id) +{ + struct asm_ape_fmt_blk_v2 fmt; + int rc = 0; + + pr_debug("%s :session[%d]rate[%d]ch[%d]\n", __func__, + ac->session, cfg->sample_rate, cfg->num_channels); + + q6asm_stream_add_hdr(ac, &fmt.hdr, sizeof(fmt), TRUE, stream_id); + atomic_set(&ac->cmd_state, -1); + + fmt.hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2; + fmt.fmtblk.fmt_blk_size = sizeof(fmt) - sizeof(fmt.hdr) - + sizeof(fmt.fmtblk); + + fmt.compatible_version = cfg->compatible_version; + fmt.compression_level = cfg->compression_level; + fmt.format_flags = cfg->format_flags; + fmt.blocks_per_frame = cfg->blocks_per_frame; + fmt.final_frame_blocks = cfg->final_frame_blocks; + fmt.total_frames = cfg->total_frames; + fmt.bits_per_sample = cfg->bits_per_sample; + fmt.num_channels = cfg->num_channels; + fmt.sample_rate = cfg->sample_rate; + fmt.seek_table_present = cfg->seek_table_present; + + rc = apr_send_pkt(ac->apr, (uint32_t *) &fmt); + if (rc < 0) { + pr_err("%s :Comamnd media format update failed %d\n", + __func__, rc); + goto fail_cmd; + } + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), + msecs_to_jiffies(TIMEOUT_MS)); + if (!rc) { + pr_err("%s :timeout. waited for FORMAT_UPDATE\n", __func__); + rc = -ETIMEDOUT; + goto fail_cmd; + } + + if (atomic_read(&ac->cmd_state) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->cmd_state))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state)); + goto fail_cmd; + } + return 0; +fail_cmd: + return rc; +} +EXPORT_SYMBOL(q6asm_media_format_block_ape); + +/* + * q6asm_media_format_block_dsd- Sends DSD Decoder + * configuration parameters + * + * @ac: Client session handle + * @cfg: DSD Media Format Configuration. + * @stream_id: stream id of stream to be associated with this session + * + * Return 0 on success or negative error code on failure + */ +int q6asm_media_format_block_dsd(struct audio_client *ac, + struct asm_dsd_cfg *cfg, int stream_id) +{ + struct asm_dsd_fmt_blk_v2 fmt; + int rc; + + pr_debug("%s: session[%d] data_rate[%d] ch[%d]\n", __func__, + ac->session, cfg->dsd_data_rate, cfg->num_channels); + + memset(&fmt, 0, sizeof(fmt)); + q6asm_stream_add_hdr(ac, &fmt.hdr, sizeof(fmt), TRUE, stream_id); + + fmt.hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2; + fmt.fmtblk.fmt_blk_size = sizeof(fmt) - sizeof(fmt.hdr) - + sizeof(fmt.fmtblk); + + fmt.num_version = cfg->num_version; + fmt.is_bitwise_big_endian = cfg->is_bitwise_big_endian; + fmt.dsd_channel_block_size = cfg->dsd_channel_block_size; + fmt.num_channels = cfg->num_channels; + fmt.dsd_data_rate = cfg->dsd_data_rate; + atomic_set(&ac->cmd_state, -1); + rc = apr_send_pkt(ac->apr, (uint32_t *) &fmt); + if (rc < 0) { + pr_err("%s: Command DSD media format update failed, err: %d\n", + __func__, rc); + goto done; + } + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), + msecs_to_jiffies(TIMEOUT_MS)); + if (!rc) { + pr_err("%s: timeout. waited for DSD FORMAT_UPDATE\n", __func__); + rc = -ETIMEDOUT; + goto done; + } + + if (atomic_read(&ac->cmd_state) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->cmd_state))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state)); + goto done; + } + return 0; +done: + return rc; +} +EXPORT_SYMBOL(q6asm_media_format_block_dsd); + +/** + * q6asm_stream_media_format_block_aptx_dec - + * command to set mediafmt block for APTX dec on ASM stream + * + * @ac: Audio client handle + * @srate: sample rate + * @stream_id: stream ID info + * + * Returns 0 on success or error on failure + */ +int q6asm_stream_media_format_block_aptx_dec(struct audio_client *ac, + uint32_t srate, int stream_id) +{ + struct asm_aptx_dec_fmt_blk_v2 aptx_fmt; + int rc = 0; + + if (!ac->session) { + pr_err("%s: ac session invalid\n", __func__); + rc = -EINVAL; + goto fail_cmd; + } + pr_debug("%s :session[%d] rate[%d] stream_id[%d]\n", + __func__, ac->session, srate, stream_id); + + q6asm_stream_add_hdr(ac, &aptx_fmt.hdr, sizeof(aptx_fmt), TRUE, + stream_id); + atomic_set(&ac->cmd_state, -1); + + aptx_fmt.hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2; + aptx_fmt.fmtblk.fmt_blk_size = sizeof(aptx_fmt) - sizeof(aptx_fmt.hdr) - + sizeof(aptx_fmt.fmtblk); + + aptx_fmt.sample_rate = srate; + + rc = apr_send_pkt(ac->apr, (uint32_t *) &aptx_fmt); + if (rc < 0) { + pr_err("%s :Comamnd media format update failed %d\n", + __func__, rc); + goto fail_cmd; + } + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), + msecs_to_jiffies(TIMEOUT_MS)); + if (!rc) { + pr_err("%s :timeout. waited for FORMAT_UPDATE\n", __func__); + rc = -ETIMEDOUT; + goto fail_cmd; + } + + if (atomic_read(&ac->cmd_state) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->cmd_state))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state)); + goto fail_cmd; + } + rc = 0; +fail_cmd: + return rc; +} +EXPORT_SYMBOL(q6asm_stream_media_format_block_aptx_dec); + +static int __q6asm_ds1_set_endp_params(struct audio_client *ac, int param_id, + int param_value, int stream_id) +{ + struct asm_dec_ddp_endp_param_v2 ddp_cfg; + int rc = 0; + + pr_debug("%s: session[%d] stream[%d],param_id[%d]param_value[%d]", + __func__, ac->session, stream_id, param_id, param_value); + + q6asm_stream_add_hdr(ac, &ddp_cfg.hdr, sizeof(ddp_cfg), TRUE, + stream_id); + atomic_set(&ac->cmd_state, -1); + /* + * Updated the token field with stream/session for compressed playback + * Platform driver must know the stream with which the command is + * associated + */ + if (ac->io_mode & COMPRESSED_STREAM_IO) + q6asm_update_token(&ddp_cfg.hdr.token, + ac->session, + stream_id, + 0, /* Buffer index is NA */ + 0, /* Direction flag is NA */ + WAIT_CMD); + ddp_cfg.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM; + ddp_cfg.encdec.param_id = param_id; + ddp_cfg.encdec.param_size = sizeof(struct asm_dec_ddp_endp_param_v2) - + (sizeof(struct apr_hdr) + + sizeof(struct asm_stream_cmd_set_encdec_param)); + ddp_cfg.endp_param_value = param_value; + rc = apr_send_pkt(ac->apr, (uint32_t *) &ddp_cfg); + if (rc < 0) { + pr_err("%s: Command opcode[0x%x] failed %d\n", + __func__, ASM_STREAM_CMD_SET_ENCDEC_PARAM, rc); + goto fail_cmd; + } + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), + msecs_to_jiffies(TIMEOUT_MS)); + if (!rc) { + pr_err("%s: timeout opcode[0x%x]\n", __func__, + ddp_cfg.hdr.opcode); + rc = -ETIMEDOUT; + goto fail_cmd; + } + if (atomic_read(&ac->cmd_state) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->cmd_state))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state)); + goto fail_cmd; + } + return 0; +fail_cmd: + return rc; +} + +/** + * q6asm_ds1_set_endp_params - + * command to set DS1 params for ASM + * + * @ac: Audio client handle + * @param_id: param id + * @param_value: value of param + * + * Returns 0 on success or error on failure + */ +int q6asm_ds1_set_endp_params(struct audio_client *ac, + int param_id, int param_value) +{ + return __q6asm_ds1_set_endp_params(ac, param_id, param_value, + ac->stream_id); +} + +/** + * q6asm_ds1_set_stream_endp_params - + * command to set DS1 params for ASM stream + * + * @ac: Audio client handle + * @param_id: param id + * @param_value: value of param + * @stream_id: stream ID info + * + * Returns 0 on success or error on failure + */ +int q6asm_ds1_set_stream_endp_params(struct audio_client *ac, + int param_id, int param_value, + int stream_id) +{ + return __q6asm_ds1_set_endp_params(ac, param_id, param_value, + stream_id); +} +EXPORT_SYMBOL(q6asm_ds1_set_stream_endp_params); + +/** + * q6asm_memory_map - + * command to send memory map for ASM + * + * @ac: Audio client handle + * @buf_add: buffer address to map + * @dir: RX or TX session + * @bufsz: size of each buffer + * @bufcnt: buffer count + * + * Returns 0 on success or error on failure + */ +int q6asm_memory_map(struct audio_client *ac, phys_addr_t buf_add, int dir, + uint32_t bufsz, uint32_t bufcnt) +{ + struct avs_cmd_shared_mem_map_regions *mmap_regions = NULL; + struct avs_shared_map_region_payload *mregions = NULL; + struct audio_port_data *port = NULL; + void *mmap_region_cmd = NULL; + void *payload = NULL; + struct asm_buffer_node *buffer_node = NULL; + int rc = 0; + int cmd_size = 0; + + if (!ac) { + pr_err("%s: APR handle NULL\n", __func__); + return -EINVAL; + } + if (ac->mmap_apr == NULL) { + pr_err("%s: mmap APR handle NULL\n", __func__); + return -EINVAL; + } + pr_debug("%s: Session[%d]\n", __func__, ac->session); + + buffer_node = kmalloc(sizeof(struct asm_buffer_node), GFP_KERNEL); + if (!buffer_node) + return -ENOMEM; + + cmd_size = sizeof(struct avs_cmd_shared_mem_map_regions) + + sizeof(struct avs_shared_map_region_payload) * bufcnt; + + mmap_region_cmd = kzalloc(cmd_size, GFP_KERNEL); + if (mmap_region_cmd == NULL) { + rc = -EINVAL; + kfree(buffer_node); + return rc; + } + mmap_regions = (struct avs_cmd_shared_mem_map_regions *) + mmap_region_cmd; + q6asm_add_mmaphdr(ac, &mmap_regions->hdr, cmd_size, dir); + atomic_set(&ac->mem_state, -1); + mmap_regions->hdr.opcode = ASM_CMD_SHARED_MEM_MAP_REGIONS; + mmap_regions->mem_pool_id = ADSP_MEMORY_MAP_SHMEM8_4K_POOL; + mmap_regions->num_regions = bufcnt & 0x00ff; + mmap_regions->property_flag = 0x00; + payload = ((u8 *) mmap_region_cmd + + sizeof(struct avs_cmd_shared_mem_map_regions)); + mregions = (struct avs_shared_map_region_payload *)payload; + + ac->port[dir].tmp_hdl = 0; + port = &ac->port[dir]; + pr_debug("%s: buf_add 0x%pK, bufsz: %d\n", __func__, + &buf_add, bufsz); + mregions->shm_addr_lsw = lower_32_bits(buf_add); + mregions->shm_addr_msw = msm_audio_populate_upper_32_bits(buf_add); + mregions->mem_size_bytes = bufsz; + ++mregions; + + rc = apr_send_pkt(ac->mmap_apr, (uint32_t *) mmap_region_cmd); + if (rc < 0) { + pr_err("%s: mmap op[0x%x]rc[%d]\n", __func__, + mmap_regions->hdr.opcode, rc); + rc = -EINVAL; + kfree(buffer_node); + goto fail_cmd; + } + + rc = wait_event_timeout(ac->mem_wait, + (atomic_read(&ac->mem_state) >= 0 && + ac->port[dir].tmp_hdl), + msecs_to_jiffies(TIMEOUT_MS)); + if (!rc) { + pr_err("%s: timeout. waited for memory_map\n", __func__); + rc = -ETIMEDOUT; + kfree(buffer_node); + goto fail_cmd; + } + if (atomic_read(&ac->mem_state) > 0) { + pr_err("%s: DSP returned error[%s] for memory_map\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->mem_state))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->mem_state)); + kfree(buffer_node); + goto fail_cmd; + } + buffer_node->buf_phys_addr = buf_add; + buffer_node->mmap_hdl = ac->port[dir].tmp_hdl; + list_add_tail(&buffer_node->list, &ac->port[dir].mem_map_handle); + ac->port[dir].tmp_hdl = 0; + rc = 0; + +fail_cmd: + kfree(mmap_region_cmd); + return rc; +} +EXPORT_SYMBOL(q6asm_memory_map); + +/** + * q6asm_memory_unmap - + * command to send memory unmap for ASM + * + * @ac: Audio client handle + * @buf_add: buffer address to unmap + * @dir: RX or TX session + * + * Returns 0 on success or error on failure + */ +int q6asm_memory_unmap(struct audio_client *ac, phys_addr_t buf_add, int dir) +{ + struct avs_cmd_shared_mem_unmap_regions mem_unmap; + struct asm_buffer_node *buf_node = NULL; + struct list_head *ptr, *next; + + int rc = 0; + + if (!ac) { + pr_err("%s: APR handle NULL\n", __func__); + return -EINVAL; + } + if (this_mmap.apr == NULL) { + pr_err("%s: APR handle NULL\n", __func__); + return -EINVAL; + } + pr_debug("%s: Session[%d]\n", __func__, ac->session); + + q6asm_add_mmaphdr(ac, &mem_unmap.hdr, + sizeof(struct avs_cmd_shared_mem_unmap_regions), + dir); + atomic_set(&ac->mem_state, -1); + mem_unmap.hdr.opcode = ASM_CMD_SHARED_MEM_UNMAP_REGIONS; + mem_unmap.mem_map_handle = 0; + list_for_each_safe(ptr, next, &ac->port[dir].mem_map_handle) { + buf_node = list_entry(ptr, struct asm_buffer_node, + list); + if (buf_node->buf_phys_addr == buf_add) { + pr_debug("%s: Found the element\n", __func__); + mem_unmap.mem_map_handle = buf_node->mmap_hdl; + break; + } + } + pr_debug("%s: mem_unmap-mem_map_handle: 0x%x\n", + __func__, mem_unmap.mem_map_handle); + + if (mem_unmap.mem_map_handle == 0) { + pr_err("%s: Do not send null mem handle to DSP\n", __func__); + rc = 0; + goto fail_cmd; + } + rc = apr_send_pkt(ac->mmap_apr, (uint32_t *) &mem_unmap); + if (rc < 0) { + pr_err("%s: mem_unmap op[0x%x]rc[%d]\n", __func__, + mem_unmap.hdr.opcode, rc); + rc = -EINVAL; + goto fail_cmd; + } + + rc = wait_event_timeout(ac->mem_wait, + (atomic_read(&ac->mem_state) >= 0), + msecs_to_jiffies(TIMEOUT_MS)); + if (!rc) { + pr_err("%s: timeout. waited for memory_unmap of handle 0x%x\n", + __func__, mem_unmap.mem_map_handle); + rc = -ETIMEDOUT; + goto fail_cmd; + } else if (atomic_read(&ac->mem_state) > 0) { + pr_err("%s DSP returned error [%s] map handle 0x%x\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->mem_state)), + mem_unmap.mem_map_handle); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->mem_state)); + goto fail_cmd; + } else if (atomic_read(&ac->unmap_cb_success) == 0) { + pr_err("%s: Error in mem unmap callback of handle 0x%x\n", + __func__, mem_unmap.mem_map_handle); + rc = -EINVAL; + goto fail_cmd; + } + + rc = 0; +fail_cmd: + list_for_each_safe(ptr, next, &ac->port[dir].mem_map_handle) { + buf_node = list_entry(ptr, struct asm_buffer_node, + list); + if (buf_node->buf_phys_addr == buf_add) { + list_del(&buf_node->list); + kfree(buf_node); + break; + } + } + return rc; +} +EXPORT_SYMBOL(q6asm_memory_unmap); + +/** + * q6asm_memory_map_regions - + * command to send memory map regions for ASM + * + * @ac: Audio client handle + * @dir: RX or TX session + * @bufsz: size of each buffer + * @bufcnt: buffer count + * @is_contiguous: alloc contiguous mem or not + * + * Returns 0 on success or error on failure + */ +static int q6asm_memory_map_regions(struct audio_client *ac, int dir, + uint32_t bufsz, uint32_t bufcnt, + bool is_contiguous) +{ + struct avs_cmd_shared_mem_map_regions *mmap_regions = NULL; + struct avs_shared_map_region_payload *mregions = NULL; + struct audio_port_data *port = NULL; + struct audio_buffer *ab = NULL; + void *mmap_region_cmd = NULL; + void *payload = NULL; + struct asm_buffer_node *buffer_node = NULL; + int rc = 0; + int i = 0; + uint32_t cmd_size = 0; + uint32_t bufcnt_t; + uint32_t bufsz_t; + + if (!ac) { + pr_err("%s: APR handle NULL\n", __func__); + return -EINVAL; + } + if (ac->mmap_apr == NULL) { + pr_err("%s: mmap APR handle NULL\n", __func__); + return -EINVAL; + } + pr_debug("%s: Session[%d]\n", __func__, ac->session); + + bufcnt_t = (is_contiguous) ? 1 : bufcnt; + bufsz_t = (is_contiguous) ? (bufsz * bufcnt) : bufsz; + + if (is_contiguous) { + /* The size to memory map should be multiple of 4K bytes */ + bufsz_t = PAGE_ALIGN(bufsz_t); + } + + if (bufcnt_t > (UINT_MAX + - sizeof(struct avs_cmd_shared_mem_map_regions)) + / sizeof(struct avs_shared_map_region_payload)) { + pr_err("%s: Unsigned Integer Overflow. bufcnt_t = %u\n", + __func__, bufcnt_t); + return -EINVAL; + } + + cmd_size = sizeof(struct avs_cmd_shared_mem_map_regions) + + (sizeof(struct avs_shared_map_region_payload) + * bufcnt_t); + + + if (bufcnt > (UINT_MAX / sizeof(struct asm_buffer_node))) { + pr_err("%s: Unsigned Integer Overflow. bufcnt = %u\n", + __func__, bufcnt); + return -EINVAL; + } + + buffer_node = kzalloc(sizeof(struct asm_buffer_node) * bufcnt, + GFP_KERNEL); + if (!buffer_node) + return -ENOMEM; + + mmap_region_cmd = kzalloc(cmd_size, GFP_KERNEL); + if (mmap_region_cmd == NULL) { + rc = -EINVAL; + kfree(buffer_node); + return rc; + } + mmap_regions = (struct avs_cmd_shared_mem_map_regions *) + mmap_region_cmd; + q6asm_add_mmaphdr(ac, &mmap_regions->hdr, cmd_size, dir); + atomic_set(&ac->mem_state, -1); + pr_debug("%s: mmap_region=0x%pK token=0x%x\n", __func__, + mmap_regions, ((ac->session << 8) | dir)); + + mmap_regions->hdr.opcode = ASM_CMD_SHARED_MEM_MAP_REGIONS; + mmap_regions->mem_pool_id = ADSP_MEMORY_MAP_SHMEM8_4K_POOL; + mmap_regions->num_regions = bufcnt_t; /*bufcnt & 0x00ff; */ + mmap_regions->property_flag = 0x00; + pr_debug("%s: map_regions->nregions = %d\n", __func__, + mmap_regions->num_regions); + payload = ((u8 *) mmap_region_cmd + + sizeof(struct avs_cmd_shared_mem_map_regions)); + mregions = (struct avs_shared_map_region_payload *)payload; + + ac->port[dir].tmp_hdl = 0; + port = &ac->port[dir]; + for (i = 0; i < bufcnt_t; i++) { + ab = &port->buf[i]; + mregions->shm_addr_lsw = lower_32_bits(ab->phys); + mregions->shm_addr_msw = + msm_audio_populate_upper_32_bits(ab->phys); + mregions->mem_size_bytes = bufsz_t; + ++mregions; + } + + rc = apr_send_pkt(ac->mmap_apr, (uint32_t *) mmap_region_cmd); + if (rc < 0) { + pr_err("%s: mmap_regions op[0x%x]rc[%d]\n", __func__, + mmap_regions->hdr.opcode, rc); + rc = -EINVAL; + kfree(buffer_node); + goto fail_cmd; + } + + rc = wait_event_timeout(ac->mem_wait, + (atomic_read(&ac->mem_state) >= 0 && + ac->port[dir].tmp_hdl), + msecs_to_jiffies(TIMEOUT_MS)); + if (!rc) { + pr_err("%s: timeout. waited for memory_map\n", __func__); + rc = -ETIMEDOUT; + kfree(buffer_node); + goto fail_cmd; + } + if (atomic_read(&ac->mem_state) > 0) { + pr_err("%s DSP returned error for memory_map [%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->mem_state))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->mem_state)); + kfree(buffer_node); + goto fail_cmd; + } + mutex_lock(&ac->cmd_lock); + + for (i = 0; i < bufcnt; i++) { + ab = &port->buf[i]; + buffer_node[i].buf_phys_addr = ab->phys; + buffer_node[i].mmap_hdl = ac->port[dir].tmp_hdl; + list_add_tail(&buffer_node[i].list, + &ac->port[dir].mem_map_handle); + pr_debug("%s: i=%d, bufadd[i] = 0x%pK, maphdl[i] = 0x%x\n", + __func__, i, &buffer_node[i].buf_phys_addr, + buffer_node[i].mmap_hdl); + } + ac->port[dir].tmp_hdl = 0; + mutex_unlock(&ac->cmd_lock); + rc = 0; +fail_cmd: + kfree(mmap_region_cmd); + return rc; +} +EXPORT_SYMBOL(q6asm_memory_map_regions); + +/** + * q6asm_memory_unmap_regions - + * command to send memory unmap regions for ASM + * + * @ac: Audio client handle + * @dir: RX or TX session + * + * Returns 0 on success or error on failure + */ +static int q6asm_memory_unmap_regions(struct audio_client *ac, int dir) +{ + struct avs_cmd_shared_mem_unmap_regions mem_unmap; + struct audio_port_data *port = NULL; + struct asm_buffer_node *buf_node = NULL; + struct list_head *ptr, *next; + phys_addr_t buf_add; + int rc = 0; + int cmd_size = 0; + + if (!ac) { + pr_err("%s: APR handle NULL\n", __func__); + return -EINVAL; + } + if (ac->mmap_apr == NULL) { + pr_err("%s: mmap APR handle NULL\n", __func__); + return -EINVAL; + } + pr_debug("%s: Session[%d]\n", __func__, ac->session); + + cmd_size = sizeof(struct avs_cmd_shared_mem_unmap_regions); + q6asm_add_mmaphdr(ac, &mem_unmap.hdr, cmd_size, dir); + atomic_set(&ac->mem_state, -1); + port = &ac->port[dir]; + buf_add = port->buf->phys; + mem_unmap.hdr.opcode = ASM_CMD_SHARED_MEM_UNMAP_REGIONS; + mem_unmap.mem_map_handle = 0; + list_for_each_safe(ptr, next, &ac->port[dir].mem_map_handle) { + buf_node = list_entry(ptr, struct asm_buffer_node, + list); + if (buf_node->buf_phys_addr == buf_add) { + pr_debug("%s: Found the element\n", __func__); + mem_unmap.mem_map_handle = buf_node->mmap_hdl; + break; + } + } + + pr_debug("%s: mem_unmap-mem_map_handle: 0x%x\n", + __func__, mem_unmap.mem_map_handle); + + if (mem_unmap.mem_map_handle == 0) { + pr_err("%s: Do not send null mem handle to DSP\n", __func__); + rc = 0; + goto fail_cmd; + } + rc = apr_send_pkt(ac->mmap_apr, (uint32_t *) &mem_unmap); + if (rc < 0) { + pr_err("mmap_regions op[0x%x]rc[%d]\n", + mem_unmap.hdr.opcode, rc); + goto fail_cmd; + } + + rc = wait_event_timeout(ac->mem_wait, + (atomic_read(&ac->mem_state) >= 0), + msecs_to_jiffies(TIMEOUT_MS)); + if (!rc) { + pr_err("%s: timeout. waited for memory_unmap of handle 0x%x\n", + __func__, mem_unmap.mem_map_handle); + rc = -ETIMEDOUT; + goto fail_cmd; + } else if (atomic_read(&ac->mem_state) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->mem_state))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->mem_state)); + goto fail_cmd; + } else if (atomic_read(&ac->unmap_cb_success) == 0) { + pr_err("%s: Error in mem unmap callback of handle 0x%x\n", + __func__, mem_unmap.mem_map_handle); + rc = -EINVAL; + goto fail_cmd; + } + rc = 0; + +fail_cmd: + list_for_each_safe(ptr, next, &ac->port[dir].mem_map_handle) { + buf_node = list_entry(ptr, struct asm_buffer_node, + list); + if (buf_node->buf_phys_addr == buf_add) { + list_del(&buf_node->list); + kfree(buf_node); + break; + } + } + return rc; +} +EXPORT_SYMBOL(q6asm_memory_unmap_regions); + +int q6asm_set_lrgain(struct audio_client *ac, int left_gain, int right_gain) +{ + struct asm_volume_ctrl_multichannel_gain multi_ch_gain; + struct param_hdr_v3 param_info; + int rc = 0; + + memset(¶m_info, 0, sizeof(param_info)); + memset(&multi_ch_gain, 0, sizeof(multi_ch_gain)); + + param_info.module_id = ASM_MODULE_ID_VOL_CTRL; + param_info.instance_id = INSTANCE_ID_0; + param_info.param_id = ASM_PARAM_ID_MULTICHANNEL_GAIN; + param_info.param_size = sizeof(multi_ch_gain); + + multi_ch_gain.gain_data[0].channeltype = PCM_CHANNEL_FL; + multi_ch_gain.gain_data[0].gain = left_gain << 15; + multi_ch_gain.gain_data[1].channeltype = PCM_CHANNEL_FR; + multi_ch_gain.gain_data[1].gain = right_gain << 15; + multi_ch_gain.num_channels = 2; + rc = q6asm_pack_and_set_pp_param_in_band(ac, param_info, + (u8 *) &multi_ch_gain); + if (rc < 0) + pr_err("%s: set-params send failed paramid[0x%x] rc %d\n", + __func__, param_info.param_id, rc); + + return rc; +} + +/* + * q6asm_set_multich_gain: set multiple channel gains on an ASM session + * @ac: audio client handle + * @channels: number of channels caller intends to set gains + * @gains: list of gains of audio channels + * @ch_map: list of channel mapping. Only valid if use_default is false + * @use_default: flag to indicate whether to use default mapping + */ +int q6asm_set_multich_gain(struct audio_client *ac, uint32_t channels, + uint32_t *gains, uint8_t *ch_map, bool use_default) +{ + struct asm_volume_ctrl_multichannel_gain multich_gain; + struct param_hdr_v3 param_info; + int rc = 0; + int i; + u8 default_chmap[VOLUME_CONTROL_MAX_CHANNELS]; + + if (ac == NULL) { + pr_err("%s: Audio client is NULL\n", __func__); + return -EINVAL; + } + if (gains == NULL) { + dev_err(ac->dev, "%s: gain_list is NULL\n", __func__); + rc = -EINVAL; + goto done; + } + if (channels > VOLUME_CONTROL_MAX_CHANNELS) { + dev_err(ac->dev, "%s: Invalid channel count %d\n", + __func__, channels); + rc = -EINVAL; + goto done; + } + if (!use_default && ch_map == NULL) { + dev_err(ac->dev, "%s: NULL channel map\n", __func__); + rc = -EINVAL; + goto done; + } + + memset(¶m_info, 0, sizeof(param_info)); + memset(&multich_gain, 0, sizeof(multich_gain)); + param_info.module_id = ASM_MODULE_ID_VOL_CTRL; + param_info.instance_id = INSTANCE_ID_0; + param_info.param_id = ASM_PARAM_ID_MULTICHANNEL_GAIN; + param_info.param_size = sizeof(multich_gain); + + if (use_default) { + rc = q6asm_map_channels(default_chmap, channels, false); + if (rc < 0) + goto done; + for (i = 0; i < channels; i++) { + multich_gain.gain_data[i].channeltype = + default_chmap[i]; + multich_gain.gain_data[i].gain = gains[i] << 15; + } + } else { + for (i = 0; i < channels; i++) { + multich_gain.gain_data[i].channeltype = ch_map[i]; + multich_gain.gain_data[i].gain = gains[i] << 15; + } + } + multich_gain.num_channels = channels; + + rc = q6asm_pack_and_set_pp_param_in_band(ac, param_info, + (u8 *) &multich_gain); + if (rc) + pr_err("%s: set-params send failed paramid[0x%x] rc %d\n", + __func__, param_info.param_id, rc); +done: + return rc; +} +EXPORT_SYMBOL(q6asm_set_multich_gain); + +/** + * q6asm_set_mute - + * command to set mute for ASM + * + * @ac: Audio client handle + * @muteflag: mute value + * + * Returns 0 on success or error on failure + */ +int q6asm_set_mute(struct audio_client *ac, int muteflag) +{ + struct asm_volume_ctrl_mute_config mute; + struct param_hdr_v3 param_info; + int rc = 0; + + memset(&mute, 0, sizeof(mute)); + memset(¶m_info, 0, sizeof(param_info)); + param_info.module_id = ASM_MODULE_ID_VOL_CTRL; + param_info.instance_id = INSTANCE_ID_0; + param_info.param_id = ASM_PARAM_ID_VOL_CTRL_MUTE_CONFIG; + param_info.param_size = sizeof(mute); + mute.mute_flag = muteflag; + + rc = q6asm_pack_and_set_pp_param_in_band(ac, param_info, (u8 *) &mute); + if (rc) + pr_err("%s: set-params send failed paramid[0x%x] rc %d\n", + __func__, param_info.param_id, rc); + return rc; +} +EXPORT_SYMBOL(q6asm_set_mute); + +static int __q6asm_set_volume(struct audio_client *ac, int volume, int instance) +{ + struct asm_volume_ctrl_master_gain vol; + struct param_hdr_v3 param_info; + int rc = 0; + + memset(&vol, 0, sizeof(vol)); + memset(¶m_info, 0, sizeof(param_info)); + + rc = q6asm_set_soft_volume_module_instance_ids(instance, ¶m_info); + if (rc) { + pr_err("%s: Failed to pack soft volume module and instance IDs, error %d\n", + __func__, rc); + return rc; + } + + param_info.param_id = ASM_PARAM_ID_VOL_CTRL_MASTER_GAIN; + param_info.param_size = sizeof(vol); + vol.master_gain = volume; + + rc = q6asm_pack_and_set_pp_param_in_band(ac, param_info, (u8 *) &vol); + if (rc) + pr_err("%s: set-params send failed paramid[0x%x] rc %d\n", + __func__, param_info.param_id, rc); + + return rc; +} + +/** + * q6asm_set_volume - + * command to set volume for ASM + * + * @ac: Audio client handle + * @volume: volume level + * + * Returns 0 on success or error on failure + */ +int q6asm_set_volume(struct audio_client *ac, int volume) +{ + return __q6asm_set_volume(ac, volume, SOFT_VOLUME_INSTANCE_1); +} +EXPORT_SYMBOL(q6asm_set_volume); + +int q6asm_set_volume_v2(struct audio_client *ac, int volume, int instance) +{ + return __q6asm_set_volume(ac, volume, instance); +} + +/** + * q6asm_set_aptx_dec_bt_addr - + * command to aptx decoder BT addr for ASM + * + * @ac: Audio client handle + * @cfg: APTX decoder bt addr config + * + * Returns 0 on success or error on failure + */ +int q6asm_set_aptx_dec_bt_addr(struct audio_client *ac, + struct aptx_dec_bt_addr_cfg *cfg) +{ + struct aptx_dec_bt_dev_addr paylod; + int sz = 0; + int rc = 0; + + pr_debug("%s: BT addr nap %d, uap %d, lap %d\n", __func__, cfg->nap, + cfg->uap, cfg->lap); + + if (ac == NULL) { + pr_err("%s: AC handle NULL\n", __func__); + rc = -EINVAL; + goto fail_cmd; + } + if (ac->apr == NULL) { + pr_err("%s: AC APR handle NULL\n", __func__); + rc = -EINVAL; + goto fail_cmd; + } + + sz = sizeof(struct aptx_dec_bt_dev_addr); + q6asm_add_hdr_async(ac, &paylod.hdr, sz, TRUE); + atomic_set(&ac->cmd_state, -1); + paylod.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM; + paylod.encdec.param_id = APTX_DECODER_BT_ADDRESS; + paylod.encdec.param_size = sz - sizeof(paylod.hdr) + - sizeof(paylod.encdec); + paylod.bt_addr_cfg.lap = cfg->lap; + paylod.bt_addr_cfg.uap = cfg->uap; + paylod.bt_addr_cfg.nap = cfg->nap; + + rc = apr_send_pkt(ac->apr, (uint32_t *) &paylod); + if (rc < 0) { + pr_err("%s: set-params send failed paramid[0x%x] rc %d\n", + __func__, paylod.encdec.param_id, rc); + rc = -EINVAL; + goto fail_cmd; + } + + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), + msecs_to_jiffies(TIMEOUT_MS)); + if (!rc) { + pr_err("%s: timeout, set-params paramid[0x%x]\n", __func__, + paylod.encdec.param_id); + rc = -ETIMEDOUT; + goto fail_cmd; + } + if (atomic_read(&ac->cmd_state) > 0) { + pr_err("%s: DSP returned error[%s] set-params paramid[0x%x]\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->cmd_state)), + paylod.encdec.param_id); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state)); + goto fail_cmd; + } + pr_debug("%s: set BT addr is success\n", __func__); + rc = 0; +fail_cmd: + return rc; +} +EXPORT_SYMBOL(q6asm_set_aptx_dec_bt_addr); + +/** + * q6asm_send_ion_fd - + * command to send ION memory map for ASM + * + * @ac: Audio client handle + * @fd: ION file desc + * + * Returns 0 on success or error on failure + */ +int q6asm_send_ion_fd(struct audio_client *ac, int fd) +{ + struct dma_buf *dma_buf; + dma_addr_t paddr; + size_t pa_len = 0; + void *vaddr; + int ret; + int sz = 0; + struct avs_rtic_shared_mem_addr shm; + + if (ac == NULL) { + pr_err("%s: APR handle NULL\n", __func__); + ret = -EINVAL; + goto fail_cmd; + } + if (ac->apr == NULL) { + pr_err("%s: AC APR handle NULL\n", __func__); + ret = -EINVAL; + goto fail_cmd; + } + + ret = msm_audio_ion_import(&dma_buf, + fd, + NULL, + 0, + &paddr, + &pa_len, + &vaddr); + if (ret) { + pr_err("%s: audio ION import failed, rc = %d\n", + __func__, ret); + ret = -ENOMEM; + goto fail_cmd; + } + /* get payload length */ + sz = sizeof(struct avs_rtic_shared_mem_addr); + q6asm_add_hdr_async(ac, &shm.hdr, sz, TRUE); + atomic_set(&ac->cmd_state, -1); + shm.shm_buf_addr_lsw = lower_32_bits(paddr); + shm.shm_buf_addr_msw = msm_audio_populate_upper_32_bits(paddr); + shm.buf_size = pa_len; + shm.shm_buf_num_regions = 1; + shm.shm_buf_mem_pool_id = ADSP_MEMORY_MAP_SHMEM8_4K_POOL; + shm.shm_buf_flag = 0x00; + shm.encdec.param_id = AVS_PARAM_ID_RTIC_SHARED_MEMORY_ADDR; + shm.encdec.param_size = sizeof(struct avs_rtic_shared_mem_addr) - + sizeof(struct apr_hdr) - + sizeof(struct asm_stream_cmd_set_encdec_param_v2); + shm.encdec.service_id = OUT; + shm.encdec.reserved = 0; + shm.map_region.shm_addr_lsw = shm.shm_buf_addr_lsw; + shm.map_region.shm_addr_msw = shm.shm_buf_addr_msw; + shm.map_region.mem_size_bytes = pa_len; + shm.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM_V2; + ret = apr_send_pkt(ac->apr, (uint32_t *) &shm); + if (ret < 0) { + pr_err("%s: set-params send failed paramid[0x%x] rc %d\n", + __func__, shm.encdec.param_id, ret); + ret = -EINVAL; + goto fail_cmd; + } + + ret = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: timeout, shm.encdec paramid[0x%x]\n", __func__, + shm.encdec.param_id); + ret = -ETIMEDOUT; + goto fail_cmd; + } + if (atomic_read(&ac->cmd_state) > 0) { + pr_err("%s: DSP returned error[%s] shm.encdec paramid[0x%x]\n", + __func__, + adsp_err_get_err_str(atomic_read(&ac->cmd_state)), + shm.encdec.param_id); + ret = adsp_err_get_lnx_err_code(atomic_read(&ac->cmd_state)); + goto fail_cmd; + } + ret = 0; +fail_cmd: + return ret; +} +EXPORT_SYMBOL(q6asm_send_ion_fd); + +/** + * q6asm_send_rtic_event_ack - + * command to send RTIC event ack + * + * @ac: Audio client handle + * @param: params for event ack + * @params_length: length of params + * + * Returns 0 on success or error on failure + */ +int q6asm_send_rtic_event_ack(struct audio_client *ac, + void *param, uint32_t params_length) +{ + char *asm_params = NULL; + int sz, rc; + struct avs_param_rtic_event_ack ack; + + if (!param || !ac) { + pr_err("%s: %s is NULL\n", __func__, + (!param) ? "param" : "ac"); + rc = -EINVAL; + goto done; + } + + sz = sizeof(struct avs_param_rtic_event_ack) + params_length; + asm_params = kzalloc(sz, GFP_KERNEL); + if (!asm_params) { + rc = -ENOMEM; + goto done; + } + + q6asm_add_hdr_async(ac, &ack.hdr, + sizeof(struct avs_param_rtic_event_ack) + + params_length, TRUE); + atomic_set(&ac->cmd_state, -1); + ack.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM_V2; + ack.encdec.param_id = AVS_PARAM_ID_RTIC_EVENT_ACK; + ack.encdec.param_size = params_length; + ack.encdec.reserved = 0; + ack.encdec.service_id = OUT; + memcpy(asm_params, &ack, sizeof(struct avs_param_rtic_event_ack)); + memcpy(asm_params + sizeof(struct avs_param_rtic_event_ack), + param, params_length); + rc = apr_send_pkt(ac->apr, (uint32_t *) asm_params); + if (rc < 0) { + pr_err("%s: apr pkt failed for rtic event ack\n", __func__); + rc = -EINVAL; + goto fail_send_param; + } + + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), + msecs_to_jiffies(TIMEOUT_MS)); + if (!rc) { + pr_err("%s: timeout for rtic event ack cmd\n", __func__); + rc = -ETIMEDOUT; + goto fail_send_param; + } + + if (atomic_read(&ac->cmd_state) > 0) { + pr_err("%s: DSP returned error[%s] for rtic event ack cmd\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->cmd_state))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state)); + goto fail_send_param; + } + rc = 0; + +fail_send_param: + kfree(asm_params); +done: + return rc; +} +EXPORT_SYMBOL(q6asm_send_rtic_event_ack); + +/** + * q6asm_set_softpause - + * command to set pause for ASM + * + * @ac: Audio client handle + * @pause_param: params for pause + * + * Returns 0 on success or error on failure + */ +int q6asm_set_softpause(struct audio_client *ac, + struct asm_softpause_params *pause_param) +{ + struct asm_soft_pause_params softpause; + struct param_hdr_v3 param_info; + int rc = 0; + + memset(&softpause, 0, sizeof(softpause)); + memset(¶m_info, 0, sizeof(param_info)); + param_info.module_id = ASM_MODULE_ID_VOL_CTRL; + param_info.instance_id = INSTANCE_ID_0; + param_info.param_id = ASM_PARAM_ID_SOFT_PAUSE_PARAMETERS; + param_info.param_size = sizeof(softpause); + + softpause.enable_flag = pause_param->enable; + softpause.period = pause_param->period; + softpause.step = pause_param->step; + softpause.ramping_curve = pause_param->rampingcurve; + + rc = q6asm_pack_and_set_pp_param_in_band(ac, param_info, + (u8 *) &softpause); + if (rc) + pr_err("%s: set-params send failed paramid[0x%x] rc %d\n", + __func__, param_info.param_id, rc); + + return rc; +} +EXPORT_SYMBOL(q6asm_set_softpause); + +static int __q6asm_set_softvolume(struct audio_client *ac, + struct asm_softvolume_params *softvol_param, + int instance) +{ + struct asm_soft_step_volume_params softvol; + struct param_hdr_v3 param_info; + int rc = 0; + + memset(&softvol, 0, sizeof(softvol)); + memset(¶m_info, 0, sizeof(param_info)); + + rc = q6asm_set_soft_volume_module_instance_ids(instance, ¶m_info); + if (rc) { + pr_err("%s: Failed to pack soft volume module and instance IDs, error %d\n", + __func__, rc); + return rc; + } + + param_info.param_id = ASM_PARAM_ID_SOFT_VOL_STEPPING_PARAMETERS; + param_info.param_size = sizeof(softvol); + + softvol.period = softvol_param->period; + softvol.step = softvol_param->step; + softvol.ramping_curve = softvol_param->rampingcurve; + + rc = q6asm_pack_and_set_pp_param_in_band(ac, param_info, + (u8 *) &softvol); + if (rc) + pr_err("%s: set-params send failed paramid[0x%x] rc %d\n", + __func__, param_info.param_id, rc); + + return rc; +} + +/** + * q6asm_set_softvolume - + * command to set softvolume for ASM + * + * @ac: Audio client handle + * @softvol_param: params for softvol + * + * Returns 0 on success or error on failure + */ +int q6asm_set_softvolume(struct audio_client *ac, + struct asm_softvolume_params *softvol_param) +{ + return __q6asm_set_softvolume(ac, softvol_param, + SOFT_VOLUME_INSTANCE_1); +} +EXPORT_SYMBOL(q6asm_set_softvolume); + +/** + * q6asm_set_softvolume_v2 - + * command to set softvolume V2 for ASM + * + * @ac: Audio client handle + * @softvol_param: params for softvol + * @instance: instance to apply softvol + * + * Returns 0 on success or error on failure + */ +int q6asm_set_softvolume_v2(struct audio_client *ac, + struct asm_softvolume_params *softvol_param, + int instance) +{ + return __q6asm_set_softvolume(ac, softvol_param, instance); +} +EXPORT_SYMBOL(q6asm_set_softvolume_v2); + +/** + * q6asm_equalizer - + * command to set equalizer for ASM + * + * @ac: Audio client handle + * @eq_p: Equalizer params + * + * Returns 0 on success or error on failure + */ +int q6asm_equalizer(struct audio_client *ac, void *eq_p) +{ + struct asm_eq_params eq; + struct msm_audio_eq_stream_config *eq_params = NULL; + struct param_hdr_v3 param_info; + int i = 0; + int rc = 0; + + if (ac == NULL) { + pr_err("%s: Audio client is NULL\n", __func__); + return -EINVAL; + } + if (eq_p == NULL) { + pr_err("%s: [%d]: Invalid Eq param\n", __func__, ac->session); + rc = -EINVAL; + goto fail_cmd; + } + + memset(&eq, 0, sizeof(eq)); + memset(¶m_info, 0, sizeof(param_info)); + eq_params = (struct msm_audio_eq_stream_config *) eq_p; + param_info.module_id = ASM_MODULE_ID_EQUALIZER; + param_info.instance_id = INSTANCE_ID_0; + param_info.param_id = ASM_PARAM_ID_EQUALIZER_PARAMETERS; + param_info.param_size = sizeof(eq); + eq.enable_flag = eq_params->enable; + eq.num_bands = eq_params->num_bands; + + pr_debug("%s: enable:%d numbands:%d\n", __func__, eq_params->enable, + eq_params->num_bands); + for (i = 0; i < eq_params->num_bands; i++) { + eq.eq_bands[i].band_idx = + eq_params->eq_bands[i].band_idx; + eq.eq_bands[i].filterype = + eq_params->eq_bands[i].filter_type; + eq.eq_bands[i].center_freq_hz = + eq_params->eq_bands[i].center_freq_hz; + eq.eq_bands[i].filter_gain = + eq_params->eq_bands[i].filter_gain; + eq.eq_bands[i].q_factor = + eq_params->eq_bands[i].q_factor; + pr_debug("%s: filter_type:%u bandnum:%d\n", __func__, + eq_params->eq_bands[i].filter_type, i); + pr_debug("%s: center_freq_hz:%u bandnum:%d\n", __func__, + eq_params->eq_bands[i].center_freq_hz, i); + pr_debug("%s: filter_gain:%d bandnum:%d\n", __func__, + eq_params->eq_bands[i].filter_gain, i); + pr_debug("%s: q_factor:%d bandnum:%d\n", __func__, + eq_params->eq_bands[i].q_factor, i); + } + rc = q6asm_pack_and_set_pp_param_in_band(ac, param_info, (u8 *) &eq); + if (rc) + pr_err("%s: set-params send failed paramid[0x%x] rc %d\n", + __func__, param_info.param_id, rc); + +fail_cmd: + return rc; +} +EXPORT_SYMBOL(q6asm_equalizer); + +static int __q6asm_read(struct audio_client *ac, bool is_custom_len_reqd, + int len) +{ + struct asm_data_cmd_read_v2 read; + struct asm_buffer_node *buf_node = NULL; + struct list_head *ptr, *next; + struct audio_buffer *ab; + int dsp_buf; + struct audio_port_data *port; + int rc; + + if (ac == NULL) { + pr_err("%s: APR handle NULL\n", __func__); + return -EINVAL; + } + if (ac->apr == NULL) { + pr_err("%s: AC APR handle NULL\n", __func__); + return -EINVAL; + } + + if (ac->io_mode & SYNC_IO_MODE) { + port = &ac->port[OUT]; + + q6asm_add_hdr(ac, &read.hdr, sizeof(read), FALSE); + + mutex_lock(&port->lock); + + dsp_buf = port->dsp_buf; + if (port->buf == NULL) { + pr_err("%s: buf is NULL\n", __func__); + mutex_unlock(&port->lock); + return -EINVAL; + } + ab = &port->buf[dsp_buf]; + + dev_vdbg(ac->dev, "%s: session[%d]dsp-buf[%d][%pK]cpu_buf[%d][%pK]\n", + __func__, + ac->session, + dsp_buf, + port->buf[dsp_buf].data, + port->cpu_buf, + &port->buf[port->cpu_buf].phys); + + read.hdr.opcode = ASM_DATA_CMD_READ_V2; + read.buf_addr_lsw = lower_32_bits(ab->phys); + read.buf_addr_msw = msm_audio_populate_upper_32_bits(ab->phys); + + list_for_each_safe(ptr, next, &ac->port[OUT].mem_map_handle) { + buf_node = list_entry(ptr, struct asm_buffer_node, + list); + if (buf_node->buf_phys_addr == ab->phys) { + read.mem_map_handle = buf_node->mmap_hdl; + break; + } + } + dev_vdbg(ac->dev, "memory_map handle in q6asm_read: [%0x]:", + read.mem_map_handle); + read.buf_size = is_custom_len_reqd ? len : ab->size; + read.seq_id = port->dsp_buf; + q6asm_update_token(&read.hdr.token, + 0, /* Session ID is NA */ + 0, /* Stream ID is NA */ + port->dsp_buf, + 0, /* Direction flag is NA */ + WAIT_CMD); + port->dsp_buf = q6asm_get_next_buf(ac, port->dsp_buf, + port->max_buf_cnt); + mutex_unlock(&port->lock); + dev_vdbg(ac->dev, "%s: buf add[%pK] token[0x%x] uid[%d]\n", + __func__, &ab->phys, read.hdr.token, + read.seq_id); + rc = apr_send_pkt(ac->apr, (uint32_t *) &read); + if (rc < 0) { + pr_err("%s: read op[0x%x]rc[%d]\n", + __func__, read.hdr.opcode, rc); + goto fail_cmd; + } + return 0; + } +fail_cmd: + return -EINVAL; +} + +/** + * q6asm_read - + * command to read buffer data from DSP + * + * @ac: Audio client handle + * + * Returns 0 on success or error on failure + */ +int q6asm_read(struct audio_client *ac) +{ + return __q6asm_read(ac, false/*is_custom_len_reqd*/, 0); +} +EXPORT_SYMBOL(q6asm_read); + + +/** + * q6asm_read_v2 - + * command to read buffer data from DSP + * + * @ac: Audio client handle + * @len: buffer size to read + * + * Returns 0 on success or error on failure + */ +int q6asm_read_v2(struct audio_client *ac, uint32_t len) +{ + return __q6asm_read(ac, true /*is_custom_len_reqd*/, len); +} +EXPORT_SYMBOL(q6asm_read_v2); + +/** + * q6asm_read_nolock - + * command to read buffer data from DSP + * with no wait for ack. + * + * @ac: Audio client handle + * + * Returns 0 on success or error on failure + */ +int q6asm_read_nolock(struct audio_client *ac) +{ + struct asm_data_cmd_read_v2 read; + struct asm_buffer_node *buf_node = NULL; + struct list_head *ptr, *next; + struct audio_buffer *ab; + int dsp_buf; + struct audio_port_data *port; + int rc; + + if (ac == NULL) { + pr_err("%s: APR handle NULL\n", __func__); + return -EINVAL; + } + if (ac->apr == NULL) { + pr_err("%s: AC APR handle NULL\n", __func__); + return -EINVAL; + } + + if (ac->io_mode & SYNC_IO_MODE) { + port = &ac->port[OUT]; + + q6asm_add_hdr_async(ac, &read.hdr, sizeof(read), FALSE); + + + dsp_buf = port->dsp_buf; + ab = &port->buf[dsp_buf]; + + dev_vdbg(ac->dev, "%s: session[%d]dsp-buf[%d][%pK]cpu_buf[%d][%pK]\n", + __func__, + ac->session, + dsp_buf, + port->buf[dsp_buf].data, + port->cpu_buf, + &port->buf[port->cpu_buf].phys); + + read.hdr.opcode = ASM_DATA_CMD_READ_V2; + read.buf_addr_lsw = lower_32_bits(ab->phys); + read.buf_addr_msw = msm_audio_populate_upper_32_bits(ab->phys); + read.buf_size = ab->size; + read.seq_id = port->dsp_buf; + q6asm_update_token(&read.hdr.token, + 0, /* Session ID is NA */ + 0, /* Stream ID is NA */ + port->dsp_buf, + 0, /* Direction flag is NA */ + WAIT_CMD); + + list_for_each_safe(ptr, next, &ac->port[OUT].mem_map_handle) { + buf_node = list_entry(ptr, struct asm_buffer_node, + list); + if (buf_node->buf_phys_addr == ab->phys) { + read.mem_map_handle = buf_node->mmap_hdl; + break; + } + } + + port->dsp_buf = q6asm_get_next_buf(ac, port->dsp_buf, + port->max_buf_cnt); + dev_vdbg(ac->dev, "%s: buf add[%pK] token[0x%x] uid[%d]\n", + __func__, &ab->phys, read.hdr.token, + read.seq_id); + rc = apr_send_pkt(ac->apr, (uint32_t *) &read); + if (rc < 0) { + pr_err("%s: read op[0x%x]rc[%d]\n", + __func__, read.hdr.opcode, rc); + goto fail_cmd; + } + return 0; + } +fail_cmd: + return -EINVAL; +} +EXPORT_SYMBOL(q6asm_read_nolock); + +/** + * q6asm_async_write - + * command to write DSP buffer + * + * @ac: Audio client handle + * @param: params for async write + * + * Returns 0 on success or error on failure + */ +int q6asm_async_write(struct audio_client *ac, + struct audio_aio_write_param *param) +{ + int rc = 0; + struct asm_data_cmd_write_v2 write; + struct asm_buffer_node *buf_node = NULL; + struct list_head *ptr, *next; + struct audio_buffer *ab; + struct audio_port_data *port; + phys_addr_t lbuf_phys_addr; + u32 liomode; + u32 io_compressed; + u32 io_compressed_stream; + + if (ac == NULL) { + pr_err("%s: APR handle NULL\n", __func__); + return -EINVAL; + } + if (ac->apr == NULL) { + pr_err_ratelimited("%s: AC APR handle NULL\n", __func__); + return -EINVAL; + } + + q6asm_stream_add_hdr_async( + ac, &write.hdr, sizeof(write), TRUE, ac->stream_id); + port = &ac->port[IN]; + ab = &port->buf[port->dsp_buf]; + + /* Pass session id as token for AIO scheme */ + write.hdr.token = param->uid; + write.hdr.opcode = ASM_DATA_CMD_WRITE_V2; + write.buf_addr_lsw = lower_32_bits(param->paddr); + write.buf_addr_msw = msm_audio_populate_upper_32_bits(param->paddr); + write.buf_size = param->len; + write.timestamp_msw = param->msw_ts; + write.timestamp_lsw = param->lsw_ts; + liomode = (ASYNC_IO_MODE | NT_MODE); + io_compressed = (ASYNC_IO_MODE | COMPRESSED_IO); + io_compressed_stream = (ASYNC_IO_MODE | COMPRESSED_STREAM_IO); + + if (ac->io_mode == liomode) + lbuf_phys_addr = (param->paddr - 32); + else if (ac->io_mode == io_compressed || + ac->io_mode == io_compressed_stream) + lbuf_phys_addr = (param->paddr - param->metadata_len); + else { + if (param->flags & SET_TIMESTAMP) + lbuf_phys_addr = param->paddr - + sizeof(struct snd_codec_metadata); + else + lbuf_phys_addr = param->paddr; + } + dev_vdbg(ac->dev, "%s: token[0x%x], buf_addr[%pK], buf_size[0x%x], ts_msw[0x%x], ts_lsw[0x%x], lbuf_phys_addr: 0x[%pK]\n", + __func__, + write.hdr.token, ¶m->paddr, + write.buf_size, write.timestamp_msw, + write.timestamp_lsw, &lbuf_phys_addr); + + /* Use 0xFF00 for disabling timestamps */ + if (param->flags == 0xFF00) + write.flags = (0x00000000 | (param->flags & 0x800000FF)); + else + write.flags = (0x80000000 | param->flags); + write.flags |= param->last_buffer << ASM_SHIFT_LAST_BUFFER_FLAG; + write.seq_id = param->uid; + list_for_each_safe(ptr, next, &ac->port[IN].mem_map_handle) { + buf_node = list_entry(ptr, struct asm_buffer_node, + list); + if (buf_node->buf_phys_addr == lbuf_phys_addr) { + write.mem_map_handle = buf_node->mmap_hdl; + break; + } + } + + rc = apr_send_pkt(ac->apr, (uint32_t *) &write); + if (rc < 0) { + pr_err("%s: write op[0x%x]rc[%d]\n", __func__, + write.hdr.opcode, rc); + goto fail_cmd; + } + return 0; +fail_cmd: + return -EINVAL; +} +EXPORT_SYMBOL(q6asm_async_write); + +/** + * q6asm_async_read - + * command to read DSP buffer + * + * @ac: Audio client handle + * @param: params for async read + * + * Returns 0 on success or error on failure + */ +int q6asm_async_read(struct audio_client *ac, + struct audio_aio_read_param *param) +{ + int rc = 0; + struct asm_data_cmd_read_v2 read; + struct asm_buffer_node *buf_node = NULL; + struct list_head *ptr, *next; + phys_addr_t lbuf_phys_addr; + u32 liomode; + u32 io_compressed; + int dir = 0; + + if (ac == NULL) { + pr_err("%s: APR handle NULL\n", __func__); + return -EINVAL; + } + if (ac->apr == NULL) { + pr_err_ratelimited("%s: AC APR handle NULL\n", __func__); + return -EINVAL; + } + + q6asm_add_hdr_async(ac, &read.hdr, sizeof(read), FALSE); + + /* Pass session id as token for AIO scheme */ + read.hdr.token = param->uid; + read.hdr.opcode = ASM_DATA_CMD_READ_V2; + read.buf_addr_lsw = lower_32_bits(param->paddr); + read.buf_addr_msw = msm_audio_populate_upper_32_bits(param->paddr); + read.buf_size = param->len; + read.seq_id = param->uid; + liomode = (NT_MODE | ASYNC_IO_MODE); + io_compressed = (ASYNC_IO_MODE | COMPRESSED_IO); + if (ac->io_mode == liomode) { + lbuf_phys_addr = (param->paddr - 32); + /*legacy wma driver case*/ + dir = IN; + } else if (ac->io_mode == io_compressed) { + lbuf_phys_addr = (param->paddr - 64); + dir = OUT; + } else { + if (param->flags & COMPRESSED_TIMESTAMP_FLAG) + lbuf_phys_addr = param->paddr - + sizeof(struct snd_codec_metadata); + else + lbuf_phys_addr = param->paddr; + dir = OUT; + } + + list_for_each_safe(ptr, next, &ac->port[dir].mem_map_handle) { + buf_node = list_entry(ptr, struct asm_buffer_node, + list); + if (buf_node->buf_phys_addr == lbuf_phys_addr) { + read.mem_map_handle = buf_node->mmap_hdl; + break; + } + } + + rc = apr_send_pkt(ac->apr, (uint32_t *) &read); + if (rc < 0 && rc != -ENETRESET) { + pr_err_ratelimited("%s: read op[0x%x]rc[%d]\n", __func__, + read.hdr.opcode, rc); + goto fail_cmd; + } + return 0; +fail_cmd: + return -EINVAL; +} +EXPORT_SYMBOL(q6asm_async_read); + +/** + * q6asm_write - + * command to write buffer data to DSP + * + * @ac: Audio client handle + * @len: buffer size + * @msw_ts: upper 32bits of timestamp + * @lsw_ts: lower 32bits of timestamp + * @flags: Flags for timestamp mode + * + * Returns 0 on success or error on failure + */ +int q6asm_write(struct audio_client *ac, uint32_t len, uint32_t msw_ts, + uint32_t lsw_ts, uint32_t flags) +{ + int rc = 0; + struct asm_data_cmd_write_v2 write; + struct asm_buffer_node *buf_node = NULL; + struct audio_port_data *port; + struct audio_buffer *ab; + int dsp_buf = 0; + + if (ac == NULL) { + pr_err("%s: APR handle NULL\n", __func__); + return -EINVAL; + } + if (ac->apr == NULL) { + pr_err("%s: AC APR handle NULL\n", __func__); + return -EINVAL; + } + + dev_vdbg(ac->dev, "%s: session[%d] len=%d\n", + __func__, ac->session, len); + if (ac->io_mode & SYNC_IO_MODE) { + port = &ac->port[IN]; + + q6asm_add_hdr(ac, &write.hdr, sizeof(write), + FALSE); + mutex_lock(&port->lock); + + dsp_buf = port->dsp_buf; + ab = &port->buf[dsp_buf]; + + q6asm_update_token(&write.hdr.token, + 0, /* Session ID is NA */ + 0, /* Stream ID is NA */ + port->dsp_buf, + 0, /* Direction flag is NA */ + NO_WAIT_CMD); + write.hdr.opcode = ASM_DATA_CMD_WRITE_V2; + write.buf_addr_lsw = lower_32_bits(ab->phys); + write.buf_addr_msw = msm_audio_populate_upper_32_bits(ab->phys); + write.buf_size = len; + write.seq_id = port->dsp_buf; + write.timestamp_lsw = lsw_ts; + write.timestamp_msw = msw_ts; + /* Use 0xFF00 for disabling timestamps */ + if (flags == 0xFF00) + write.flags = (0x00000000 | (flags & 0x800000FF)); + else + write.flags = (0x80000000 | flags); + port->dsp_buf = q6asm_get_next_buf(ac, port->dsp_buf, + port->max_buf_cnt); + buf_node = list_first_entry(&ac->port[IN].mem_map_handle, + struct asm_buffer_node, + list); + write.mem_map_handle = buf_node->mmap_hdl; + + dev_vdbg(ac->dev, "%s: ab->phys[%pK]bufadd[0x%x] token[0x%x]buf_id[0x%x]buf_size[0x%x]mmaphdl[0x%x]" + , __func__, + &ab->phys, + write.buf_addr_lsw, + write.hdr.token, + write.seq_id, + write.buf_size, + write.mem_map_handle); + mutex_unlock(&port->lock); + + config_debug_fs_write(ab); + + rc = apr_send_pkt(ac->apr, (uint32_t *) &write); + if (rc < 0) { + pr_err("%s: write op[0x%x]rc[%d]\n", + __func__, write.hdr.opcode, rc); + goto fail_cmd; + } + return 0; + } +fail_cmd: + return -EINVAL; +} +EXPORT_SYMBOL(q6asm_write); + +/** + * q6asm_write_nolock - + * command to write buffer data to DSP + * with no wait for ack. + * + * @ac: Audio client handle + * @len: buffer size + * @msw_ts: upper 32bits of timestamp + * @lsw_ts: lower 32bits of timestamp + * @flags: Flags for timestamp mode + * + * Returns 0 on success or error on failure + */ +int q6asm_write_nolock(struct audio_client *ac, uint32_t len, uint32_t msw_ts, + uint32_t lsw_ts, uint32_t flags) +{ + int rc = 0; + struct asm_data_cmd_write_v2 write; + struct asm_buffer_node *buf_node = NULL; + struct audio_port_data *port; + struct audio_buffer *ab; + int dsp_buf = 0; + + if (ac == NULL) { + pr_err("%s: APR handle NULL\n", __func__); + return -EINVAL; + } + if (ac->apr == NULL) { + pr_err("%s: AC APR handle NULL\n", __func__); + return -EINVAL; + } + + dev_vdbg(ac->dev, "%s: session[%d] len=%d\n", + __func__, ac->session, len); + if (ac->io_mode & SYNC_IO_MODE) { + port = &ac->port[IN]; + + q6asm_add_hdr_async(ac, &write.hdr, sizeof(write), + FALSE); + + dsp_buf = port->dsp_buf; + ab = &port->buf[dsp_buf]; + + q6asm_update_token(&write.hdr.token, + 0, /* Session ID is NA */ + 0, /* Stream ID is NA */ + port->dsp_buf, + 0, /* Direction flag is NA */ + NO_WAIT_CMD); + + write.hdr.opcode = ASM_DATA_CMD_WRITE_V2; + write.buf_addr_lsw = lower_32_bits(ab->phys); + write.buf_addr_msw = msm_audio_populate_upper_32_bits(ab->phys); + write.buf_size = len; + write.seq_id = port->dsp_buf; + write.timestamp_lsw = lsw_ts; + write.timestamp_msw = msw_ts; + buf_node = list_first_entry(&ac->port[IN].mem_map_handle, + struct asm_buffer_node, + list); + write.mem_map_handle = buf_node->mmap_hdl; + /* Use 0xFF00 for disabling timestamps */ + if (flags == 0xFF00) + write.flags = (0x00000000 | (flags & 0x800000FF)); + else + write.flags = (0x80000000 | flags); + port->dsp_buf = q6asm_get_next_buf(ac, port->dsp_buf, + port->max_buf_cnt); + + dev_vdbg(ac->dev, "%s: ab->phys[%pK]bufadd[0x%x]token[0x%x] buf_id[0x%x]buf_size[0x%x]mmaphdl[0x%x]" + , __func__, + &ab->phys, + write.buf_addr_lsw, + write.hdr.token, + write.seq_id, + write.buf_size, + write.mem_map_handle); + + rc = apr_send_pkt(ac->apr, (uint32_t *) &write); + if (rc < 0) { + pr_err("%s: write op[0x%x]rc[%d]\n", + __func__, write.hdr.opcode, rc); + goto fail_cmd; + } + return 0; + } +fail_cmd: + return -EINVAL; +} +EXPORT_SYMBOL(q6asm_write_nolock); + +/** + * q6asm_get_session_time - + * command to retrieve timestamp info + * + * @ac: Audio client handle + * @tstamp: pointer to fill with timestamp info + * + * Returns 0 on success or error on failure + */ +int q6asm_get_session_time(struct audio_client *ac, uint64_t *tstamp) +{ + struct asm_mtmx_strtr_get_params mtmx_params; + int rc; + + if (ac == NULL) { + pr_err("%s: APR handle NULL\n", __func__); + return -EINVAL; + } + if (ac->apr == NULL) { + pr_err("%s: AC APR handle NULL\n", __func__); + return -EINVAL; + } + if (tstamp == NULL) { + pr_err("%s: tstamp NULL\n", __func__); + return -EINVAL; + } + + q6asm_add_hdr(ac, &mtmx_params.hdr, sizeof(mtmx_params), TRUE); + mtmx_params.hdr.opcode = ASM_SESSION_CMD_GET_MTMX_STRTR_PARAMS_V2; + mtmx_params.param_info.data_payload_addr_lsw = 0; + mtmx_params.param_info.data_payload_addr_msw = 0; + mtmx_params.param_info.mem_map_handle = 0; + mtmx_params.param_info.direction = (ac->io_mode & TUN_READ_IO_MODE + ? 1 : 0); + mtmx_params.param_info.module_id = + ASM_SESSION_MTMX_STRTR_MODULE_ID_AVSYNC; + mtmx_params.param_info.param_id = + ASM_SESSION_MTMX_STRTR_PARAM_SESSION_TIME_V3; + mtmx_params.param_info.param_max_size = + sizeof(struct param_hdr_v1) + + sizeof(struct asm_session_mtmx_strtr_param_session_time_v3_t); + atomic_set(&ac->time_flag, 1); + + dev_vdbg(ac->dev, "%s: session[%d]opcode[0x%x]\n", __func__, + ac->session, mtmx_params.hdr.opcode); + rc = apr_send_pkt(ac->apr, (uint32_t *) &mtmx_params); + if (rc < 0) { + pr_err("%s: Commmand 0x%x failed %d\n", __func__, + mtmx_params.hdr.opcode, rc); + goto fail_cmd; + } + rc = wait_event_timeout(ac->time_wait, + (atomic_read(&ac->time_flag) == 0), + msecs_to_jiffies(TIMEOUT_MS)); + if (!rc) { + pr_err("%s: timeout in getting session time from DSP\n", + __func__); + goto fail_cmd; + } + + *tstamp = ac->time_stamp; + return 0; + +fail_cmd: + return -EINVAL; +} +EXPORT_SYMBOL(q6asm_get_session_time); + +/** + * q6asm_get_session_time_legacy - + * command to retrieve timestamp info + * + * @ac: Audio client handle + * @tstamp: pointer to fill with timestamp info + * + * Returns 0 on success or error on failure + */ +int q6asm_get_session_time_legacy(struct audio_client *ac, uint64_t *tstamp) +{ + struct apr_hdr hdr; + int rc; + + if (ac == NULL) { + pr_err("%s: APR handle NULL\n", __func__); + return -EINVAL; + } + if (ac->apr == NULL) { + pr_err("%s: AC APR handle NULL\n", __func__); + return -EINVAL; + } + if (tstamp == NULL) { + pr_err("%s: tstamp NULL\n", __func__); + return -EINVAL; + } + + q6asm_add_hdr(ac, &hdr, sizeof(hdr), TRUE); + hdr.opcode = ASM_SESSION_CMD_GET_SESSIONTIME_V3; + atomic_set(&ac->time_flag, 1); + + dev_vdbg(ac->dev, "%s: session[%d]opcode[0x%x]\n", __func__, + ac->session, + hdr.opcode); + rc = apr_send_pkt(ac->apr, (uint32_t *) &hdr); + if (rc < 0) { + pr_err("%s: Commmand 0x%x failed %d\n", + __func__, hdr.opcode, rc); + goto fail_cmd; + } + rc = wait_event_timeout(ac->time_wait, + (atomic_read(&ac->time_flag) == 0), + msecs_to_jiffies(TIMEOUT_MS)); + if (!rc) { + pr_err("%s: timeout in getting session time from DSP\n", + __func__); + goto fail_cmd; + } + + *tstamp = ac->time_stamp; + return 0; + +fail_cmd: + return -EINVAL; +} +EXPORT_SYMBOL(q6asm_get_session_time_legacy); + +/** + * q6asm_send_mtmx_strtr_window - + * command to send matrix for window params + * + * @ac: Audio client handle + * @window_param: window params + * @param_id: param id for window + * + * Returns 0 on success or error on failure + */ +int q6asm_send_mtmx_strtr_window(struct audio_client *ac, + struct asm_session_mtmx_strtr_param_window_v2_t *window_param, + uint32_t param_id) +{ + struct asm_mtmx_strtr_params matrix; + int sz = 0; + int rc = 0; + + pr_debug("%s: Window lsw is %d, window msw is %d\n", __func__, + window_param->window_lsw, window_param->window_msw); + + if (!ac) { + pr_err("%s: audio client handle is NULL\n", __func__); + rc = -EINVAL; + goto fail_cmd; + } + + if (ac->apr == NULL) { + pr_err("%s: ac->apr is NULL", __func__); + rc = -EINVAL; + goto fail_cmd; + } + + sz = sizeof(struct asm_mtmx_strtr_params); + q6asm_add_hdr(ac, &matrix.hdr, sz, TRUE); + atomic_set(&ac->cmd_state, -1); + matrix.hdr.opcode = ASM_SESSION_CMD_SET_MTMX_STRTR_PARAMS_V2; + + matrix.param.data_payload_addr_lsw = 0; + matrix.param.data_payload_addr_msw = 0; + matrix.param.mem_map_handle = 0; + matrix.param.data_payload_size = + sizeof(struct param_hdr_v1) + + sizeof(struct asm_session_mtmx_strtr_param_window_v2_t); + matrix.param.direction = 0; /* RX */ + matrix.data.module_id = ASM_SESSION_MTMX_STRTR_MODULE_ID_AVSYNC; + matrix.data.param_id = param_id; + matrix.data.param_size = + sizeof(struct asm_session_mtmx_strtr_param_window_v2_t); + matrix.data.reserved = 0; + memcpy(&(matrix.config.window_param), + window_param, + sizeof(struct asm_session_mtmx_strtr_param_window_v2_t)); + + rc = apr_send_pkt(ac->apr, (uint32_t *) &matrix); + if (rc < 0) { + pr_err("%s: Render window start send failed paramid [0x%x]\n", + __func__, matrix.data.param_id); + rc = -EINVAL; + goto fail_cmd; + } + + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), + msecs_to_jiffies(TIMEOUT_MS)); + if (!rc) { + pr_err("%s: timeout, Render window start paramid[0x%x]\n", + __func__, matrix.data.param_id); + rc = -ETIMEDOUT; + goto fail_cmd; + } + + if (atomic_read(&ac->cmd_state) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->cmd_state))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state)); + goto fail_cmd; + } + rc = 0; +fail_cmd: + return rc; +} +EXPORT_SYMBOL(q6asm_send_mtmx_strtr_window); + +/** + * q6asm_send_mtmx_strtr_render_mode - + * command to send matrix for render mode + * + * @ac: Audio client handle + * @render_mode: rendering mode + * + * Returns 0 on success or error on failure + */ +int q6asm_send_mtmx_strtr_render_mode(struct audio_client *ac, + uint32_t render_mode) +{ + struct asm_mtmx_strtr_params matrix; + struct asm_session_mtmx_strtr_param_render_mode_t render_param; + int sz = 0; + int rc = 0; + + pr_debug("%s: render mode is %d\n", __func__, render_mode); + + if (!ac) { + pr_err("%s: audio client handle is NULL\n", __func__); + rc = -EINVAL; + goto exit; + } + + if (ac->apr == NULL) { + pr_err("%s: ac->apr is NULL\n", __func__); + rc = -EINVAL; + goto exit; + } + + if ((render_mode != ASM_SESSION_MTMX_STRTR_PARAM_RENDER_DEFAULT) && + (render_mode != ASM_SESSION_MTMX_STRTR_PARAM_RENDER_LOCAL_STC)) { + pr_err("%s: Invalid render mode %d\n", __func__, render_mode); + rc = -EINVAL; + goto exit; + } + + memset(&render_param, 0, + sizeof(struct asm_session_mtmx_strtr_param_render_mode_t)); + render_param.flags = render_mode; + + memset(&matrix, 0, sizeof(struct asm_mtmx_strtr_params)); + sz = sizeof(struct asm_mtmx_strtr_params); + q6asm_add_hdr(ac, &matrix.hdr, sz, TRUE); + atomic_set(&ac->cmd_state, -1); + matrix.hdr.opcode = ASM_SESSION_CMD_SET_MTMX_STRTR_PARAMS_V2; + + matrix.param.data_payload_addr_lsw = 0; + matrix.param.data_payload_addr_msw = 0; + matrix.param.mem_map_handle = 0; + matrix.param.data_payload_size = + sizeof(struct param_hdr_v1) + + sizeof(struct asm_session_mtmx_strtr_param_render_mode_t); + matrix.param.direction = 0; /* RX */ + matrix.data.module_id = ASM_SESSION_MTMX_STRTR_MODULE_ID_AVSYNC; + matrix.data.param_id = ASM_SESSION_MTMX_STRTR_PARAM_RENDER_MODE_CMD; + matrix.data.param_size = + sizeof(struct asm_session_mtmx_strtr_param_render_mode_t); + matrix.data.reserved = 0; + memcpy(&(matrix.config.render_param), + &render_param, + sizeof(struct asm_session_mtmx_strtr_param_render_mode_t)); + + rc = apr_send_pkt(ac->apr, (uint32_t *) &matrix); + if (rc < 0) { + pr_err("%s: Render mode send failed paramid [0x%x]\n", + __func__, matrix.data.param_id); + rc = -EINVAL; + goto exit; + } + + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), + msecs_to_jiffies(TIMEOUT_MS)); + if (!rc) { + pr_err("%s: timeout, Render mode send paramid [0x%x]\n", + __func__, matrix.data.param_id); + rc = -ETIMEDOUT; + goto exit; + } + + if (atomic_read(&ac->cmd_state) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->cmd_state))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state)); + goto exit; + } + rc = 0; +exit: + return rc; +} +EXPORT_SYMBOL(q6asm_send_mtmx_strtr_render_mode); + +/** + * q6asm_send_mtmx_strtr_clk_rec_mode - + * command to send matrix for clock rec + * + * @ac: Audio client handle + * @clk_rec_mode: mode for clock rec + * + * Returns 0 on success or error on failure + */ +int q6asm_send_mtmx_strtr_clk_rec_mode(struct audio_client *ac, + uint32_t clk_rec_mode) +{ + struct asm_mtmx_strtr_params matrix; + struct asm_session_mtmx_strtr_param_clk_rec_t clk_rec_param; + int sz = 0; + int rc = 0; + + pr_debug("%s: clk rec mode is %d\n", __func__, clk_rec_mode); + + if (!ac) { + pr_err("%s: audio client handle is NULL\n", __func__); + rc = -EINVAL; + goto exit; + } + + if (ac->apr == NULL) { + pr_err("%s: ac->apr is NULL\n", __func__); + rc = -EINVAL; + goto exit; + } + + if ((clk_rec_mode != ASM_SESSION_MTMX_STRTR_PARAM_CLK_REC_NONE) && + (clk_rec_mode != ASM_SESSION_MTMX_STRTR_PARAM_CLK_REC_AUTO)) { + pr_err("%s: Invalid clk rec mode %d\n", __func__, clk_rec_mode); + rc = -EINVAL; + goto exit; + } + + memset(&clk_rec_param, 0, + sizeof(struct asm_session_mtmx_strtr_param_clk_rec_t)); + clk_rec_param.flags = clk_rec_mode; + + memset(&matrix, 0, sizeof(struct asm_mtmx_strtr_params)); + sz = sizeof(struct asm_mtmx_strtr_params); + q6asm_add_hdr(ac, &matrix.hdr, sz, TRUE); + atomic_set(&ac->cmd_state, -1); + matrix.hdr.opcode = ASM_SESSION_CMD_SET_MTMX_STRTR_PARAMS_V2; + + matrix.param.data_payload_addr_lsw = 0; + matrix.param.data_payload_addr_msw = 0; + matrix.param.mem_map_handle = 0; + matrix.param.data_payload_size = + sizeof(struct param_hdr_v1) + + sizeof(struct asm_session_mtmx_strtr_param_clk_rec_t); + matrix.param.direction = 0; /* RX */ + matrix.data.module_id = ASM_SESSION_MTMX_STRTR_MODULE_ID_AVSYNC; + matrix.data.param_id = ASM_SESSION_MTMX_STRTR_PARAM_CLK_REC_CMD; + matrix.data.param_size = + sizeof(struct asm_session_mtmx_strtr_param_clk_rec_t); + matrix.data.reserved = 0; + memcpy(&(matrix.config.clk_rec_param), + &clk_rec_param, + sizeof(struct asm_session_mtmx_strtr_param_clk_rec_t)); + + rc = apr_send_pkt(ac->apr, (uint32_t *) &matrix); + if (rc < 0) { + pr_err("%s: clk rec mode send failed paramid [0x%x]\n", + __func__, matrix.data.param_id); + rc = -EINVAL; + goto exit; + } + + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), + msecs_to_jiffies(TIMEOUT_MS)); + if (!rc) { + pr_err("%s: timeout, clk rec mode send paramid [0x%x]\n", + __func__, matrix.data.param_id); + rc = -ETIMEDOUT; + goto exit; + } + + if (atomic_read(&ac->cmd_state) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->cmd_state))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state)); + goto exit; + } + rc = 0; +exit: + return rc; +} +EXPORT_SYMBOL(q6asm_send_mtmx_strtr_clk_rec_mode); + +/** + * q6asm_send_mtmx_strtr_enable_adjust_session_clock - + * command to send matrix for adjust time + * + * @ac: Audio client handle + * @enable: flag to adjust time or not + * + * Returns 0 on success or error on failure + */ +int q6asm_send_mtmx_strtr_enable_adjust_session_clock(struct audio_client *ac, + bool enable) +{ + struct asm_mtmx_strtr_params matrix; + struct asm_session_mtmx_param_adjust_session_time_ctl_t adjust_time; + int sz = 0; + int rc = 0; + + pr_debug("%s: adjust session enable %d\n", __func__, enable); + + if (!ac) { + pr_err("%s: audio client handle is NULL\n", __func__); + rc = -EINVAL; + goto exit; + } + + if (ac->apr == NULL) { + pr_err("%s: ac->apr is NULL\n", __func__); + rc = -EINVAL; + goto exit; + } + + adjust_time.enable = enable; + memset(&matrix, 0, sizeof(struct asm_mtmx_strtr_params)); + sz = sizeof(struct asm_mtmx_strtr_params); + q6asm_add_hdr(ac, &matrix.hdr, sz, TRUE); + atomic_set(&ac->cmd_state, -1); + matrix.hdr.opcode = ASM_SESSION_CMD_SET_MTMX_STRTR_PARAMS_V2; + + matrix.param.data_payload_addr_lsw = 0; + matrix.param.data_payload_addr_msw = 0; + matrix.param.mem_map_handle = 0; + matrix.param.data_payload_size = + sizeof(struct param_hdr_v1) + + sizeof(struct asm_session_mtmx_param_adjust_session_time_ctl_t); + matrix.param.direction = 0; /* RX */ + matrix.data.module_id = ASM_SESSION_MTMX_STRTR_MODULE_ID_AVSYNC; + matrix.data.param_id = ASM_SESSION_MTMX_PARAM_ADJUST_SESSION_TIME_CTL; + matrix.data.param_size = + sizeof(struct asm_session_mtmx_param_adjust_session_time_ctl_t); + matrix.data.reserved = 0; + matrix.config.adj_time_param.enable = adjust_time.enable; + + rc = apr_send_pkt(ac->apr, (uint32_t *) &matrix); + if (rc < 0) { + pr_err("%s: enable adjust session failed failed paramid [0x%x]\n", + __func__, matrix.data.param_id); + rc = -EINVAL; + goto exit; + } + + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), + msecs_to_jiffies(TIMEOUT_MS)); + if (!rc) { + pr_err("%s: enable adjust session failed failed paramid [0x%x]\n", + __func__, matrix.data.param_id); + rc = -ETIMEDOUT; + goto exit; + } + + if (atomic_read(&ac->cmd_state) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->cmd_state))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state)); + goto exit; + } + rc = 0; +exit: + return rc; +} +EXPORT_SYMBOL(q6asm_send_mtmx_strtr_enable_adjust_session_clock); + + +static int __q6asm_cmd(struct audio_client *ac, int cmd, uint32_t stream_id) +{ + struct apr_hdr hdr; + int rc; + atomic_t *state; + int cnt = 0; + + if (!ac) { + pr_err_ratelimited("%s: APR handle NULL\n", __func__); + return -EINVAL; + } + if (ac->apr == NULL) { + pr_err_ratelimited("%s: AC APR handle NULL\n", __func__); + return -EINVAL; + } + q6asm_stream_add_hdr(ac, &hdr, sizeof(hdr), TRUE, stream_id); + atomic_set(&ac->cmd_state, -1); + /* + * Updated the token field with stream/session for compressed playback + * Platform driver must know the the stream with which the command is + * associated + */ + if (ac->io_mode & COMPRESSED_STREAM_IO) + q6asm_update_token(&hdr.token, + ac->session, + stream_id, + 0, /* Buffer index is NA */ + 0, /* Direction flag is NA */ + WAIT_CMD); + pr_debug("%s: token = 0x%x, stream_id %d, session 0x%x\n", + __func__, hdr.token, stream_id, ac->session); + switch (cmd) { + case CMD_PAUSE: + pr_debug("%s: CMD_PAUSE\n", __func__); + hdr.opcode = ASM_SESSION_CMD_PAUSE; + state = &ac->cmd_state; + break; + case CMD_SUSPEND: + pr_debug("%s: CMD_SUSPEND\n", __func__); + hdr.opcode = ASM_SESSION_CMD_SUSPEND; + state = &ac->cmd_state; + break; + case CMD_FLUSH: + pr_debug("%s: CMD_FLUSH\n", __func__); + hdr.opcode = ASM_STREAM_CMD_FLUSH; + state = &ac->cmd_state; + break; + case CMD_OUT_FLUSH: + pr_debug("%s: CMD_OUT_FLUSH\n", __func__); + hdr.opcode = ASM_STREAM_CMD_FLUSH_READBUFS; + state = &ac->cmd_state; + break; + case CMD_EOS: + pr_debug("%s: CMD_EOS\n", __func__); + hdr.opcode = ASM_DATA_CMD_EOS; + atomic_set(&ac->cmd_state, 0); + state = &ac->cmd_state; + break; + case CMD_CLOSE: + pr_debug("%s: CMD_CLOSE\n", __func__); + hdr.opcode = ASM_STREAM_CMD_CLOSE; + state = &ac->cmd_state; + break; + default: + pr_err("%s: Invalid format[%d]\n", __func__, cmd); + rc = -EINVAL; + goto fail_cmd; + } + pr_debug("%s: session[%d]opcode[0x%x]\n", __func__, + ac->session, + hdr.opcode); + rc = apr_send_pkt(ac->apr, (uint32_t *) &hdr); + if (rc < 0) { + pr_err("%s: Commmand 0x%x failed %d\n", + __func__, hdr.opcode, rc); + rc = -EINVAL; + goto fail_cmd; + } + rc = wait_event_timeout(ac->cmd_wait, (atomic_read(state) >= 0), + msecs_to_jiffies(TIMEOUT_MS)); + if (!rc) { + pr_err("%s: timeout. waited for response opcode[0x%x]\n", + __func__, hdr.opcode); + rc = -ETIMEDOUT; + goto fail_cmd; + } + if (atomic_read(state) > 0) { + pr_err("%s: DSP returned error[%s] opcode %d\n", + __func__, adsp_err_get_err_str( + atomic_read(state)), + hdr.opcode); + rc = adsp_err_get_lnx_err_code(atomic_read(state)); + goto fail_cmd; + } + + if (cmd == CMD_FLUSH) + q6asm_reset_buf_state(ac); + if (cmd == CMD_CLOSE) { + /* check if DSP return all buffers */ + if (ac->port[IN].buf) { + for (cnt = 0; cnt < ac->port[IN].max_buf_cnt; + cnt++) { + if (ac->port[IN].buf[cnt].used == IN) { + dev_vdbg(ac->dev, "Write Buf[%d] not returned\n", + cnt); + } + } + } + if (ac->port[OUT].buf) { + for (cnt = 0; cnt < ac->port[OUT].max_buf_cnt; cnt++) { + if (ac->port[OUT].buf[cnt].used == OUT) { + dev_vdbg(ac->dev, "Read Buf[%d] not returned\n", + cnt); + } + } + } + } + return 0; +fail_cmd: + return rc; +} + +/** + * q6asm_cmd - + * Function used to send commands for + * ASM with wait for ack. + * + * @ac: Audio client handle + * @cmd: command to send + * + * Returns 0 on success or error on failure + */ +int q6asm_cmd(struct audio_client *ac, int cmd) +{ + return __q6asm_cmd(ac, cmd, ac->stream_id); +} +EXPORT_SYMBOL(q6asm_cmd); + +/** + * q6asm_stream_cmd - + * Function used to send commands for + * ASM stream with wait for ack. + * + * @ac: Audio client handle + * @cmd: command to send + * @stream_id: Stream ID + * + * Returns 0 on success or error on failure + */ +int q6asm_stream_cmd(struct audio_client *ac, int cmd, uint32_t stream_id) +{ + return __q6asm_cmd(ac, cmd, stream_id); +} +EXPORT_SYMBOL(q6asm_stream_cmd); + +/** + * q6asm_cmd_nowait - + * Function used to send commands for + * ASM stream without wait for ack. + * + * @ac: Audio client handle + * @cmd: command to send + * @stream_id: Stream ID + * + * Returns 0 on success or error on failure + */ +static int __q6asm_cmd_nowait(struct audio_client *ac, int cmd, + uint32_t stream_id) +{ + struct apr_hdr hdr; + int rc; + + if (!ac) { + pr_err_ratelimited("%s: APR handle NULL\n", __func__); + return -EINVAL; + } + if (ac->apr == NULL) { + pr_err_ratelimited("%s: AC APR handle NULL\n", __func__); + return -EINVAL; + } + q6asm_stream_add_hdr_async(ac, &hdr, sizeof(hdr), TRUE, stream_id); + atomic_set(&ac->cmd_state, 1); + /* + * Updated the token field with stream/session for compressed playback + * Platform driver must know the the stream with which the command is + * associated + */ + if (ac->io_mode & COMPRESSED_STREAM_IO) + q6asm_update_token(&hdr.token, + ac->session, + stream_id, + 0, /* Buffer index is NA */ + 0, /* Direction flag is NA */ + NO_WAIT_CMD); + + pr_debug("%s: token = 0x%x, stream_id %d, session 0x%x\n", + __func__, hdr.token, stream_id, ac->session); + switch (cmd) { + case CMD_PAUSE: + pr_debug("%s: CMD_PAUSE\n", __func__); + hdr.opcode = ASM_SESSION_CMD_PAUSE; + break; + case CMD_EOS: + pr_debug("%s: CMD_EOS\n", __func__); + hdr.opcode = ASM_DATA_CMD_EOS; + break; + case CMD_CLOSE: + pr_debug("%s: CMD_CLOSE\n", __func__); + hdr.opcode = ASM_STREAM_CMD_CLOSE; + break; + default: + pr_err("%s: Invalid format[%d]\n", __func__, cmd); + goto fail_cmd; + } + pr_debug("%s: session[%d]opcode[0x%x]\n", __func__, + ac->session, + hdr.opcode); + + rc = apr_send_pkt(ac->apr, (uint32_t *) &hdr); + if (rc < 0) { + pr_err("%s: Commmand 0x%x failed %d\n", + __func__, hdr.opcode, rc); + goto fail_cmd; + } + return 0; +fail_cmd: + return -EINVAL; +} + +int q6asm_cmd_nowait(struct audio_client *ac, int cmd) +{ + pr_debug("%s: stream_id: %d\n", __func__, ac->stream_id); + return __q6asm_cmd_nowait(ac, cmd, ac->stream_id); +} +EXPORT_SYMBOL(q6asm_cmd_nowait); + +/** + * q6asm_stream_cmd_nowait - + * Function used to send commands for + * ASM stream without wait for ack. + * + * @ac: Audio client handle + * @cmd: command to send + * @stream_id: Stream ID + * + * Returns 0 on success or error on failure + */ +int q6asm_stream_cmd_nowait(struct audio_client *ac, int cmd, + uint32_t stream_id) +{ + pr_debug("%s: stream_id: %d\n", __func__, stream_id); + return __q6asm_cmd_nowait(ac, cmd, stream_id); +} +EXPORT_SYMBOL(q6asm_stream_cmd_nowait); + +int __q6asm_send_meta_data(struct audio_client *ac, uint32_t stream_id, + uint32_t initial_samples, uint32_t trailing_samples) +{ + struct asm_data_cmd_remove_silence silence; + int rc = 0; + + if (!ac) { + pr_err_ratelimited("%s: APR handle NULL\n", __func__); + return -EINVAL; + } + if (ac->apr == NULL) { + pr_err_ratelimited("%s: AC APR handle NULL\n", __func__); + return -EINVAL; + } + pr_debug("%s: session[%d]\n", __func__, ac->session); + q6asm_stream_add_hdr_async(ac, &silence.hdr, sizeof(silence), TRUE, + stream_id); + + /* + * Updated the token field with stream/session for compressed playback + * Platform driver must know the the stream with which the command is + * associated + */ + if (ac->io_mode & COMPRESSED_STREAM_IO) + q6asm_update_token(&silence.hdr.token, + ac->session, + stream_id, + 0, /* Buffer index is NA */ + 0, /* Direction flag is NA */ + NO_WAIT_CMD); + pr_debug("%s: token = 0x%x, stream_id %d, session 0x%x\n", + __func__, silence.hdr.token, stream_id, ac->session); + + silence.hdr.opcode = ASM_DATA_CMD_REMOVE_INITIAL_SILENCE; + silence.num_samples_to_remove = initial_samples; + + rc = apr_send_pkt(ac->apr, (uint32_t *) &silence); + if (rc < 0) { + pr_err("%s: Commmand silence failed[%d]", __func__, rc); + + goto fail_cmd; + } + + silence.hdr.opcode = ASM_DATA_CMD_REMOVE_TRAILING_SILENCE; + silence.num_samples_to_remove = trailing_samples; + + + rc = apr_send_pkt(ac->apr, (uint32_t *) &silence); + if (rc < 0) { + pr_err("%s: Commmand silence failed[%d]", __func__, rc); + goto fail_cmd; + } + + return 0; +fail_cmd: + return -EINVAL; +} + +/** + * q6asm_stream_send_meta_data - + * command to send meta data for stream + * + * @ac: Audio client handle + * @stream_id: Stream ID + * @initial_samples: Initial samples of stream + * @trailing_samples: Trailing samples of stream + * + * Returns 0 on success or error on failure + */ +int q6asm_stream_send_meta_data(struct audio_client *ac, uint32_t stream_id, + uint32_t initial_samples, uint32_t trailing_samples) +{ + return __q6asm_send_meta_data(ac, stream_id, initial_samples, + trailing_samples); +} +EXPORT_SYMBOL(q6asm_stream_send_meta_data); + +int q6asm_send_meta_data(struct audio_client *ac, uint32_t initial_samples, + uint32_t trailing_samples) +{ + return __q6asm_send_meta_data(ac, ac->stream_id, initial_samples, + trailing_samples); +} + +static void q6asm_reset_buf_state(struct audio_client *ac) +{ + int cnt = 0; + int loopcnt = 0; + int used; + struct audio_port_data *port = NULL; + + if (ac->io_mode & SYNC_IO_MODE) { + used = (ac->io_mode & TUN_WRITE_IO_MODE ? 1 : 0); + mutex_lock(&ac->cmd_lock); + for (loopcnt = 0; loopcnt <= OUT; loopcnt++) { + port = &ac->port[loopcnt]; + cnt = port->max_buf_cnt - 1; + port->dsp_buf = 0; + port->cpu_buf = 0; + while (cnt >= 0) { + if (!port->buf) + continue; + port->buf[cnt].used = used; + cnt--; + } + } + mutex_unlock(&ac->cmd_lock); + } +} + +/** + * q6asm_reg_tx_overflow - + * command to register for TX overflow events + * + * @ac: Audio client handle + * @enable: flag to enable or disable events + * + * Returns 0 on success or error on failure + */ +int q6asm_reg_tx_overflow(struct audio_client *ac, uint16_t enable) +{ + struct asm_session_cmd_regx_overflow tx_overflow; + int rc; + + if (!ac) { + pr_err("%s: APR handle NULL\n", __func__); + return -EINVAL; + } + if (ac->apr == NULL) { + pr_err("%s: AC APR handle NULL\n", __func__); + return -EINVAL; + } + pr_debug("%s: session[%d]enable[%d]\n", __func__, + ac->session, enable); + q6asm_add_hdr(ac, &tx_overflow.hdr, sizeof(tx_overflow), TRUE); + atomic_set(&ac->cmd_state, -1); + + tx_overflow.hdr.opcode = + ASM_SESSION_CMD_REGISTER_FORX_OVERFLOW_EVENTS; + /* tx overflow event: enable */ + tx_overflow.enable_flag = enable; + + rc = apr_send_pkt(ac->apr, (uint32_t *) &tx_overflow); + if (rc < 0) { + pr_err("%s: tx overflow op[0x%x]rc[%d]\n", + __func__, tx_overflow.hdr.opcode, rc); + rc = -EINVAL; + goto fail_cmd; + } + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), + msecs_to_jiffies(TIMEOUT_MS)); + if (!rc) { + pr_err("%s: timeout. waited for tx overflow\n", __func__); + rc = -ETIMEDOUT; + goto fail_cmd; + } + if (atomic_read(&ac->cmd_state) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->cmd_state))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state)); + goto fail_cmd; + } + + return 0; +fail_cmd: + return rc; +} +EXPORT_SYMBOL(q6asm_reg_tx_overflow); + +int q6asm_reg_rx_underflow(struct audio_client *ac, uint16_t enable) +{ + struct asm_session_cmd_rgstr_rx_underflow rx_underflow; + int rc; + + if (!ac) { + pr_err("%s: AC APR handle NULL\n", __func__); + return -EINVAL; + } + if (ac->apr == NULL) { + pr_err("%s: APR handle NULL\n", __func__); + return -EINVAL; + } + pr_debug("%s: session[%d]enable[%d]\n", __func__, + ac->session, enable); + q6asm_add_hdr_async(ac, &rx_underflow.hdr, sizeof(rx_underflow), FALSE); + + rx_underflow.hdr.opcode = + ASM_SESSION_CMD_REGISTER_FOR_RX_UNDERFLOW_EVENTS; + /* tx overflow event: enable */ + rx_underflow.enable_flag = enable; + + rc = apr_send_pkt(ac->apr, (uint32_t *) &rx_underflow); + if (rc < 0) { + pr_err("%s: tx overflow op[0x%x]rc[%d]\n", + __func__, rx_underflow.hdr.opcode, rc); + goto fail_cmd; + } + return 0; +fail_cmd: + return -EINVAL; +} + +/** + * q6asm_adjust_session_clock - + * command to adjust session clock + * + * @ac: Audio client handle + * @adjust_time_lsw: lower 32bits + * @adjust_time_msw: upper 32bits + * + * Returns 0 on success or error on failure + */ +int q6asm_adjust_session_clock(struct audio_client *ac, + uint32_t adjust_time_lsw, + uint32_t adjust_time_msw) +{ + int rc = 0; + int sz = 0; + struct asm_session_cmd_adjust_session_clock_v2 adjust_clock; + + pr_debug("%s: adjust_time_lsw is %x, adjust_time_msw is %x\n", __func__, + adjust_time_lsw, adjust_time_msw); + + if (!ac) { + pr_err("%s: audio client handle is NULL\n", __func__); + rc = -EINVAL; + goto fail_cmd; + } + + if (ac->apr == NULL) { + pr_err("%s: ac->apr is NULL", __func__); + rc = -EINVAL; + goto fail_cmd; + } + + sz = sizeof(struct asm_session_cmd_adjust_session_clock_v2); + q6asm_add_hdr(ac, &adjust_clock.hdr, sz, TRUE); + atomic_set(&ac->cmd_state, -1); + adjust_clock.hdr.opcode = ASM_SESSION_CMD_ADJUST_SESSION_CLOCK_V2; + + adjust_clock.adjustime_lsw = adjust_time_lsw; + adjust_clock.adjustime_msw = adjust_time_msw; + + + rc = apr_send_pkt(ac->apr, (uint32_t *) &adjust_clock); + if (rc < 0) { + pr_err("%s: adjust_clock send failed paramid [0x%x]\n", + __func__, adjust_clock.hdr.opcode); + rc = -EINVAL; + goto fail_cmd; + } + + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), + msecs_to_jiffies(TIMEOUT_MS)); + if (!rc) { + pr_err("%s: timeout, adjust_clock paramid[0x%x]\n", + __func__, adjust_clock.hdr.opcode); + rc = -ETIMEDOUT; + goto fail_cmd; + } + + if (atomic_read(&ac->cmd_state) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->cmd_state))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state)); + goto fail_cmd; + } + rc = 0; +fail_cmd: + return rc; +} +EXPORT_SYMBOL(q6asm_adjust_session_clock); + +/* + * q6asm_get_path_delay() - get the path delay for an audio session + * @ac: audio client handle + * + * Retrieves the current audio DSP path delay for the given audio session. + * + * Return: 0 on success, error code otherwise + */ +int q6asm_get_path_delay(struct audio_client *ac) +{ + int rc = 0; + struct apr_hdr hdr; + + if (!ac || ac->apr == NULL) { + pr_err("%s: invalid audio client\n", __func__); + return -EINVAL; + } + + hdr.opcode = ASM_SESSION_CMD_GET_PATH_DELAY_V2; + q6asm_add_hdr(ac, &hdr, sizeof(hdr), TRUE); + atomic_set(&ac->cmd_state, -1); + + rc = apr_send_pkt(ac->apr, (uint32_t *) &hdr); + if (rc < 0) { + pr_err("%s: Commmand 0x%x failed %d\n", __func__, + hdr.opcode, rc); + return rc; + } + + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), + msecs_to_jiffies(TIMEOUT_MS)); + if (!rc) { + pr_err("%s: timeout. waited for response opcode[0x%x]\n", + __func__, hdr.opcode); + return -ETIMEDOUT; + } + + if (atomic_read(&ac->cmd_state) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->cmd_state))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state)); + return rc; + } + + return 0; +} +EXPORT_SYMBOL(q6asm_get_path_delay); + +int q6asm_get_apr_service_id(int session_id) +{ + pr_debug("%s:\n", __func__); + + if (session_id <= 0 || session_id > ASM_ACTIVE_STREAMS_ALLOWED) { + pr_err("%s: invalid session_id = %d\n", __func__, session_id); + return -EINVAL; + } + + return ((struct apr_svc *)(session[session_id].ac)->apr)->id; +} + +int q6asm_get_asm_topology(int session_id) +{ + int topology = -EINVAL; + + if (session_id <= 0 || session_id > ASM_ACTIVE_STREAMS_ALLOWED) { + pr_err("%s: invalid session_id = %d\n", __func__, session_id); + goto done; + } + if (session[session_id].ac == NULL) { + pr_err("%s: session not created for session id = %d\n", + __func__, session_id); + goto done; + } + topology = (session[session_id].ac)->topology; +done: + return topology; +} + +int q6asm_get_asm_app_type(int session_id) +{ + int app_type = -EINVAL; + + if (session_id <= 0 || session_id > ASM_ACTIVE_STREAMS_ALLOWED) { + pr_err("%s: invalid session_id = %d\n", __func__, session_id); + goto done; + } + if (session[session_id].ac == NULL) { + pr_err("%s: session not created for session id = %d\n", + __func__, session_id); + goto done; + } + app_type = (session[session_id].ac)->app_type; +done: + return app_type; +} + +/* + * Retrieving cal_block will mark cal_block as stale. + * Hence it cannot be reused or resent unless the flag + * is reset. + */ +static int q6asm_get_asm_topology_apptype(struct q6asm_cal_info *cal_info) +{ + struct cal_block_data *cal_block = NULL; + + cal_info->topology_id = DEFAULT_POPP_TOPOLOGY; + cal_info->app_type = DEFAULT_APP_TYPE; + + if (cal_data[ASM_TOPOLOGY_CAL] == NULL) + goto done; + + mutex_lock(&cal_data[ASM_TOPOLOGY_CAL]->lock); + cal_block = cal_utils_get_only_cal_block(cal_data[ASM_TOPOLOGY_CAL]); + if (cal_block == NULL || cal_utils_is_cal_stale(cal_block)) + goto unlock; + cal_info->topology_id = ((struct audio_cal_info_asm_top *) + cal_block->cal_info)->topology; + cal_info->app_type = ((struct audio_cal_info_asm_top *) + cal_block->cal_info)->app_type; + + cal_utils_mark_cal_used(cal_block); + +unlock: + mutex_unlock(&cal_data[ASM_TOPOLOGY_CAL]->lock); +done: + pr_debug("%s: Using topology %d app_type %d\n", __func__, + cal_info->topology_id, cal_info->app_type); + + return 0; +} + +/** + * q6asm_send_cal - + * command to send ASM calibration + * + * @ac: Audio client handle + * + * Returns 0 on success or error on failure + */ +int q6asm_send_cal(struct audio_client *ac) +{ + struct cal_block_data *cal_block = NULL; + struct mem_mapping_hdr mem_hdr; + u32 payload_size = 0; + int rc = -EINVAL; + pr_debug("%s:\n", __func__); + + if (!ac) { + pr_err("%s: Audio client is NULL\n", __func__); + return -EINVAL; + } + if (ac->io_mode & NT_MODE) { + pr_debug("%s: called for NT MODE, exiting\n", __func__); + goto done; + } + + if (cal_data[ASM_AUDSTRM_CAL] == NULL) + goto done; + + if (ac->perf_mode == ULTRA_LOW_LATENCY_PCM_MODE) { + rc = 0; /* no cal is required, not error case */ + goto done; + } + + memset(&mem_hdr, 0, sizeof(mem_hdr)); + mutex_lock(&cal_data[ASM_AUDSTRM_CAL]->lock); + cal_block = cal_utils_get_only_cal_block(cal_data[ASM_AUDSTRM_CAL]); + if (cal_block == NULL) { + pr_err("%s: cal_block is NULL\n", + __func__); + goto unlock; + } + + if (cal_utils_is_cal_stale(cal_block)) { + rc = 0; /* not error case */ + pr_debug("%s: cal_block is stale\n", + __func__); + goto unlock; + } + + if (cal_block->cal_data.size == 0) { + rc = 0; /* not error case */ + pr_debug("%s: cal_data.size is 0, don't send cal data\n", + __func__); + goto unlock; + } + + rc = remap_cal_data(ASM_AUDSTRM_CAL_TYPE, cal_block); + if (rc) { + pr_err("%s: Remap_cal_data failed for cal %d!\n", + __func__, ASM_AUDSTRM_CAL); + goto unlock; + } + + mem_hdr.data_payload_addr_lsw = + lower_32_bits(cal_block->cal_data.paddr); + mem_hdr.data_payload_addr_msw = + msm_audio_populate_upper_32_bits(cal_block->cal_data.paddr); + mem_hdr.mem_map_handle = cal_block->map_data.q6map_handle; + payload_size = cal_block->cal_data.size; + + pr_debug("%s: phyaddr lsw = %x msw = %x, maphdl = %x calsize = %d\n", + __func__, mem_hdr.data_payload_addr_lsw, + mem_hdr.data_payload_addr_msw, mem_hdr.mem_map_handle, + payload_size); + + rc = q6asm_set_pp_params(ac, &mem_hdr, NULL, payload_size); + if (rc) { + pr_err("%s: audio audstrm cal send failed\n", __func__); + goto unlock; + } + + if (cal_block) + cal_utils_mark_cal_used(cal_block); + rc = 0; + +unlock: + mutex_unlock(&cal_data[ASM_AUDSTRM_CAL]->lock); +done: + return rc; +} +EXPORT_SYMBOL(q6asm_send_cal); + +static int get_cal_type_index(int32_t cal_type) +{ + int ret = -EINVAL; + + switch (cal_type) { + case ASM_TOPOLOGY_CAL_TYPE: + ret = ASM_TOPOLOGY_CAL; + break; + case ASM_CUST_TOPOLOGY_CAL_TYPE: + ret = ASM_CUSTOM_TOP_CAL; + break; + case ASM_AUDSTRM_CAL_TYPE: + ret = ASM_AUDSTRM_CAL; + break; + case ASM_RTAC_APR_CAL_TYPE: + ret = ASM_RTAC_APR_CAL; + break; + default: + pr_err("%s: invalid cal type %d!\n", __func__, cal_type); + } + return ret; +} + +static int q6asm_alloc_cal(int32_t cal_type, + size_t data_size, void *data) +{ + int ret = 0; + int cal_index; + + pr_debug("%s:\n", __func__); + + cal_index = get_cal_type_index(cal_type); + if (cal_index < 0) { + pr_err("%s: could not get cal index %d!\n", + __func__, cal_index); + ret = -EINVAL; + goto done; + } + + ret = cal_utils_alloc_cal(data_size, data, + cal_data[cal_index], 0, NULL); + if (ret < 0) { + pr_err("%s: cal_utils_alloc_block failed, ret = %d, cal type = %d!\n", + __func__, ret, cal_type); + ret = -EINVAL; + goto done; + } +done: + return ret; +} + +static int q6asm_dealloc_cal(int32_t cal_type, + size_t data_size, void *data) +{ + int ret = 0; + int cal_index; + + pr_debug("%s:\n", __func__); + + cal_index = get_cal_type_index(cal_type); + if (cal_index < 0) { + pr_err("%s: could not get cal index %d!\n", + __func__, cal_index); + ret = -EINVAL; + goto done; + } + + ret = cal_utils_dealloc_cal(data_size, data, + cal_data[cal_index]); + if (ret < 0) { + pr_err("%s: cal_utils_dealloc_block failed, ret = %d, cal type = %d!\n", + __func__, ret, cal_type); + ret = -EINVAL; + goto done; + } +done: + return ret; +} + +static int q6asm_set_cal(int32_t cal_type, + size_t data_size, void *data) +{ + int ret = 0; + int cal_index; + + pr_debug("%s:\n", __func__); + + cal_index = get_cal_type_index(cal_type); + if (cal_index < 0) { + pr_err("%s: could not get cal index %d!\n", + __func__, cal_index); + ret = -EINVAL; + goto done; + } + + ret = cal_utils_set_cal(data_size, data, + cal_data[cal_index], 0, NULL); + if (ret < 0) { + pr_err("%s: cal_utils_set_cal failed, ret = %d, cal type = %d!\n", + __func__, ret, cal_type); + ret = -EINVAL; + goto done; + } + + if (cal_index == ASM_CUSTOM_TOP_CAL) { + mutex_lock(&cal_data[ASM_CUSTOM_TOP_CAL]->lock); + set_custom_topology = 1; + mutex_unlock(&cal_data[ASM_CUSTOM_TOP_CAL]->lock); + } +done: + return ret; +} + +static void q6asm_delete_cal_data(void) +{ + pr_debug("%s:\n", __func__); + cal_utils_destroy_cal_types(ASM_MAX_CAL_TYPES, cal_data); +} + +static int q6asm_init_cal_data(void) +{ + int ret = 0; + struct cal_type_info cal_type_info[] = { + {{ASM_TOPOLOGY_CAL_TYPE, + {NULL, NULL, NULL, + q6asm_set_cal, NULL, NULL} }, + {NULL, NULL, cal_utils_match_buf_num} }, + + {{ASM_CUST_TOPOLOGY_CAL_TYPE, + {q6asm_alloc_cal, q6asm_dealloc_cal, NULL, + q6asm_set_cal, NULL, NULL} }, + {NULL, q6asm_unmap_cal_memory, cal_utils_match_buf_num} }, + + {{ASM_AUDSTRM_CAL_TYPE, + {q6asm_alloc_cal, q6asm_dealloc_cal, NULL, + q6asm_set_cal, NULL, NULL} }, + {NULL, q6asm_unmap_cal_memory, cal_utils_match_buf_num} }, + + {{ASM_RTAC_APR_CAL_TYPE, + {NULL, NULL, NULL, NULL, NULL, NULL} }, + {NULL, NULL, cal_utils_match_buf_num} } + }; + pr_debug("%s\n", __func__); + + ret = cal_utils_create_cal_types(ASM_MAX_CAL_TYPES, cal_data, + cal_type_info); + if (ret < 0) { + pr_err("%s: could not create cal type! %d\n", + __func__, ret); + ret = -EINVAL; + goto err; + } + + return ret; +err: + q6asm_delete_cal_data(); + return ret; +} + +static int q6asm_is_valid_session(struct apr_client_data *data, void *priv) +{ + struct audio_client *ac = (struct audio_client *)priv; + union asm_token_struct asm_token; + + asm_token.token = data->token; + if (asm_token._token.session_id != ac->session) { + pr_err("%s: Invalid session[%d] rxed expected[%d]", + __func__, asm_token._token.session_id, ac->session); + return -EINVAL; + } + return 0; +} + +int __init q6asm_init(void) +{ + int lcnt, ret; + + pr_debug("%s:\n", __func__); + + memset(session, 0, sizeof(struct audio_session) * + (ASM_ACTIVE_STREAMS_ALLOWED + 1)); + for (lcnt = 0; lcnt <= ASM_ACTIVE_STREAMS_ALLOWED; lcnt++) { + spin_lock_init(&(session[lcnt].session_lock)); + mutex_init(&(session[lcnt].mutex_lock_per_session)); + } + set_custom_topology = 1; + + /*setup common client used for cal mem map */ + common_client.session = ASM_CONTROL_SESSION; + common_client.port[0].buf = &common_buf[0]; + common_client.port[1].buf = &common_buf[1]; + init_waitqueue_head(&common_client.cmd_wait); + init_waitqueue_head(&common_client.time_wait); + init_waitqueue_head(&common_client.mem_wait); + atomic_set(&common_client.time_flag, 1); + INIT_LIST_HEAD(&common_client.port[0].mem_map_handle); + INIT_LIST_HEAD(&common_client.port[1].mem_map_handle); + mutex_init(&common_client.cmd_lock); + for (lcnt = 0; lcnt <= OUT; lcnt++) { + mutex_init(&common_client.port[lcnt].lock); + spin_lock_init(&common_client.port[lcnt].dsp_lock); + } + atomic_set(&common_client.cmd_state, 0); + atomic_set(&common_client.mem_state, 0); + + ret = q6asm_init_cal_data(); + if (ret) + pr_err("%s: could not init cal data! ret %d\n", + __func__, ret); + + config_debug_fs_init(); + + return 0; +} + +void q6asm_exit(void) +{ + q6asm_delete_cal_data(); +} diff --git a/audio-kernel/dsp/q6audio-v2.c b/audio-kernel/dsp/q6audio-v2.c new file mode 100644 index 000000000..5a7fa6f62 --- /dev/null +++ b/audio-kernel/dsp/q6audio-v2.c @@ -0,0 +1,1097 @@ +/* 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 + * 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 +#include +#include +#include +#include +#include +#include +#include + +int q6audio_get_port_index(u16 port_id) +{ + switch (port_id) { + case PRIMARY_I2S_RX: return IDX_PRIMARY_I2S_RX; + case PRIMARY_I2S_TX: return IDX_PRIMARY_I2S_TX; + case AFE_PORT_ID_PRIMARY_PCM_RX: + return IDX_AFE_PORT_ID_PRIMARY_PCM_RX; + case AFE_PORT_ID_PRIMARY_PCM_TX: + return IDX_AFE_PORT_ID_PRIMARY_PCM_TX; + case AFE_PORT_ID_SECONDARY_PCM_RX: + return IDX_AFE_PORT_ID_SECONDARY_PCM_RX; + case AFE_PORT_ID_SECONDARY_PCM_TX: + return IDX_AFE_PORT_ID_SECONDARY_PCM_TX; + case AFE_PORT_ID_TERTIARY_PCM_RX: + return IDX_AFE_PORT_ID_TERTIARY_PCM_RX; + case AFE_PORT_ID_TERTIARY_PCM_TX: + return IDX_AFE_PORT_ID_TERTIARY_PCM_TX; + case AFE_PORT_ID_QUATERNARY_PCM_RX: + return IDX_AFE_PORT_ID_QUATERNARY_PCM_RX; + case AFE_PORT_ID_QUATERNARY_PCM_TX: + return IDX_AFE_PORT_ID_QUATERNARY_PCM_TX; + case AFE_PORT_ID_QUINARY_PCM_RX: + return IDX_AFE_PORT_ID_QUINARY_PCM_RX; + case AFE_PORT_ID_QUINARY_PCM_TX: + return IDX_AFE_PORT_ID_QUINARY_PCM_TX; + case AFE_PORT_ID_SENARY_PCM_RX: + return IDX_AFE_PORT_ID_SENARY_PCM_RX; + case AFE_PORT_ID_SENARY_PCM_TX: + return IDX_AFE_PORT_ID_SENARY_PCM_TX; + case SECONDARY_I2S_RX: return IDX_SECONDARY_I2S_RX; + case SECONDARY_I2S_TX: return IDX_SECONDARY_I2S_TX; + case MI2S_RX: return IDX_MI2S_RX; + case MI2S_TX: return IDX_MI2S_TX; + case HDMI_RX: return IDX_HDMI_RX; + case DISPLAY_PORT_RX: return IDX_DISPLAY_PORT_RX; + case AFE_PORT_ID_PRIMARY_SPDIF_RX: return IDX_PRIMARY_SPDIF_RX; + case AFE_PORT_ID_PRIMARY_SPDIF_TX: return IDX_PRIMARY_SPDIF_TX; + case AFE_PORT_ID_SECONDARY_SPDIF_RX: return IDX_SECONDARY_SPDIF_RX; + case AFE_PORT_ID_SECONDARY_SPDIF_TX: return IDX_SECONDARY_SPDIF_TX; + case RSVD_2: return IDX_RSVD_2; + case RSVD_3: return IDX_RSVD_3; + case DIGI_MIC_TX: return IDX_DIGI_MIC_TX; + case VOICE_RECORD_RX: return IDX_VOICE_RECORD_RX; + case VOICE_RECORD_TX: return IDX_VOICE_RECORD_TX; + case VOICE_PLAYBACK_TX: return IDX_VOICE_PLAYBACK_TX; + case VOICE2_PLAYBACK_TX: return IDX_VOICE2_PLAYBACK_TX; + case SLIMBUS_0_RX: return IDX_SLIMBUS_0_RX; + case SLIMBUS_0_TX: return IDX_SLIMBUS_0_TX; + case SLIMBUS_1_RX: return IDX_SLIMBUS_1_RX; + case SLIMBUS_1_TX: return IDX_SLIMBUS_1_TX; + case SLIMBUS_2_RX: return IDX_SLIMBUS_2_RX; + case SLIMBUS_2_TX: return IDX_SLIMBUS_2_TX; + case SLIMBUS_3_RX: return IDX_SLIMBUS_3_RX; + case SLIMBUS_3_TX: return IDX_SLIMBUS_3_TX; + case SLIMBUS_4_RX: return IDX_SLIMBUS_4_RX; + case SLIMBUS_4_TX: return IDX_SLIMBUS_4_TX; + case SLIMBUS_5_RX: return IDX_SLIMBUS_5_RX; + case SLIMBUS_5_TX: return IDX_SLIMBUS_5_TX; + case SLIMBUS_6_RX: return IDX_SLIMBUS_6_RX; + case SLIMBUS_6_TX: return IDX_SLIMBUS_6_TX; + case SLIMBUS_7_RX: return IDX_SLIMBUS_7_RX; + case SLIMBUS_7_TX: return IDX_SLIMBUS_7_TX; + case SLIMBUS_8_RX: return IDX_SLIMBUS_8_RX; + case SLIMBUS_8_TX: return IDX_SLIMBUS_8_TX; + case SLIMBUS_9_RX: return IDX_SLIMBUS_9_RX; + case SLIMBUS_9_TX: return IDX_SLIMBUS_9_TX; + case INT_BT_SCO_RX: return IDX_INT_BT_SCO_RX; + case INT_BT_SCO_TX: return IDX_INT_BT_SCO_TX; + case INT_BT_A2DP_RX: return IDX_INT_BT_A2DP_RX; + case INT_FM_RX: return IDX_INT_FM_RX; + case INT_FM_TX: return IDX_INT_FM_TX; + case RT_PROXY_PORT_001_RX: return IDX_RT_PROXY_PORT_001_RX; + case RT_PROXY_PORT_001_TX: return IDX_RT_PROXY_PORT_001_TX; + case AFE_PORT_ID_PRIMARY_MI2S_RX: + return IDX_AFE_PORT_ID_PRIMARY_MI2S_RX; + case AFE_PORT_ID_PRIMARY_MI2S_TX: + return IDX_AFE_PORT_ID_PRIMARY_MI2S_TX; + case AFE_PORT_ID_QUATERNARY_MI2S_RX: + return IDX_AFE_PORT_ID_QUATERNARY_MI2S_RX; + case AFE_PORT_ID_QUATERNARY_MI2S_TX: + return IDX_AFE_PORT_ID_QUATERNARY_MI2S_TX; + case AFE_PORT_ID_SECONDARY_MI2S_RX: + return IDX_AFE_PORT_ID_SECONDARY_MI2S_RX; + case AFE_PORT_ID_SECONDARY_MI2S_TX: + return IDX_AFE_PORT_ID_SECONDARY_MI2S_TX; + case AFE_PORT_ID_TERTIARY_MI2S_RX: + return IDX_AFE_PORT_ID_TERTIARY_MI2S_RX; + case AFE_PORT_ID_TERTIARY_MI2S_TX: + return IDX_AFE_PORT_ID_TERTIARY_MI2S_TX; + case AFE_PORT_ID_QUINARY_MI2S_RX: + return IDX_AFE_PORT_ID_QUINARY_MI2S_RX; + case AFE_PORT_ID_QUINARY_MI2S_TX: + return IDX_AFE_PORT_ID_QUINARY_MI2S_TX; + case AUDIO_PORT_ID_I2S_RX: + return IDX_AUDIO_PORT_ID_I2S_RX; + case AFE_PORT_ID_SECONDARY_MI2S_RX_SD1: + return IDX_AFE_PORT_ID_SECONDARY_MI2S_RX_SD1; + case AFE_PORT_ID_PRIMARY_TDM_RX: + return IDX_AFE_PORT_ID_PRIMARY_TDM_RX_0; + case AFE_PORT_ID_PRIMARY_TDM_TX: + return IDX_AFE_PORT_ID_PRIMARY_TDM_TX_0; + case AFE_PORT_ID_PRIMARY_TDM_RX_1: + return IDX_AFE_PORT_ID_PRIMARY_TDM_RX_1; + case AFE_PORT_ID_PRIMARY_TDM_TX_1: + return IDX_AFE_PORT_ID_PRIMARY_TDM_TX_1; + case AFE_PORT_ID_PRIMARY_TDM_RX_2: + return IDX_AFE_PORT_ID_PRIMARY_TDM_RX_2; + case AFE_PORT_ID_PRIMARY_TDM_TX_2: + return IDX_AFE_PORT_ID_PRIMARY_TDM_TX_2; + case AFE_PORT_ID_PRIMARY_TDM_RX_3: + return IDX_AFE_PORT_ID_PRIMARY_TDM_RX_3; + case AFE_PORT_ID_PRIMARY_TDM_TX_3: + return IDX_AFE_PORT_ID_PRIMARY_TDM_TX_3; + case AFE_PORT_ID_PRIMARY_TDM_RX_4: + return IDX_AFE_PORT_ID_PRIMARY_TDM_RX_4; + case AFE_PORT_ID_PRIMARY_TDM_TX_4: + return IDX_AFE_PORT_ID_PRIMARY_TDM_TX_4; + case AFE_PORT_ID_PRIMARY_TDM_RX_5: + return IDX_AFE_PORT_ID_PRIMARY_TDM_RX_5; + case AFE_PORT_ID_PRIMARY_TDM_TX_5: + return IDX_AFE_PORT_ID_PRIMARY_TDM_TX_5; + case AFE_PORT_ID_PRIMARY_TDM_RX_6: + return IDX_AFE_PORT_ID_PRIMARY_TDM_RX_6; + case AFE_PORT_ID_PRIMARY_TDM_TX_6: + return IDX_AFE_PORT_ID_PRIMARY_TDM_TX_6; + case AFE_PORT_ID_PRIMARY_TDM_RX_7: + return IDX_AFE_PORT_ID_PRIMARY_TDM_RX_7; + case AFE_PORT_ID_PRIMARY_TDM_TX_7: + return IDX_AFE_PORT_ID_PRIMARY_TDM_TX_7; + case AFE_PORT_ID_SECONDARY_TDM_RX: + return IDX_AFE_PORT_ID_SECONDARY_TDM_RX_0; + case AFE_PORT_ID_SECONDARY_TDM_TX: + return IDX_AFE_PORT_ID_SECONDARY_TDM_TX_0; + case AFE_PORT_ID_SECONDARY_TDM_RX_1: + return IDX_AFE_PORT_ID_SECONDARY_TDM_RX_1; + case AFE_PORT_ID_SECONDARY_TDM_TX_1: + return IDX_AFE_PORT_ID_SECONDARY_TDM_TX_1; + case AFE_PORT_ID_SECONDARY_TDM_RX_2: + return IDX_AFE_PORT_ID_SECONDARY_TDM_RX_2; + case AFE_PORT_ID_SECONDARY_TDM_TX_2: + return IDX_AFE_PORT_ID_SECONDARY_TDM_TX_2; + case AFE_PORT_ID_SECONDARY_TDM_RX_3: + return IDX_AFE_PORT_ID_SECONDARY_TDM_RX_3; + case AFE_PORT_ID_SECONDARY_TDM_TX_3: + return IDX_AFE_PORT_ID_SECONDARY_TDM_TX_3; + case AFE_PORT_ID_SECONDARY_TDM_RX_4: + return IDX_AFE_PORT_ID_SECONDARY_TDM_RX_4; + case AFE_PORT_ID_SECONDARY_TDM_TX_4: + return IDX_AFE_PORT_ID_SECONDARY_TDM_TX_4; + case AFE_PORT_ID_SECONDARY_TDM_RX_5: + return IDX_AFE_PORT_ID_SECONDARY_TDM_RX_5; + case AFE_PORT_ID_SECONDARY_TDM_TX_5: + return IDX_AFE_PORT_ID_SECONDARY_TDM_TX_5; + case AFE_PORT_ID_SECONDARY_TDM_RX_6: + return IDX_AFE_PORT_ID_SECONDARY_TDM_RX_6; + case AFE_PORT_ID_SECONDARY_TDM_TX_6: + return IDX_AFE_PORT_ID_SECONDARY_TDM_TX_6; + case AFE_PORT_ID_SECONDARY_TDM_RX_7: + return IDX_AFE_PORT_ID_SECONDARY_TDM_RX_7; + case AFE_PORT_ID_SECONDARY_TDM_TX_7: + return IDX_AFE_PORT_ID_SECONDARY_TDM_TX_7; + case AFE_PORT_ID_TERTIARY_TDM_RX: + return IDX_AFE_PORT_ID_TERTIARY_TDM_RX_0; + case AFE_PORT_ID_TERTIARY_TDM_TX: + return IDX_AFE_PORT_ID_TERTIARY_TDM_TX_0; + case AFE_PORT_ID_TERTIARY_TDM_RX_1: + return IDX_AFE_PORT_ID_TERTIARY_TDM_RX_1; + case AFE_PORT_ID_TERTIARY_TDM_TX_1: + return IDX_AFE_PORT_ID_TERTIARY_TDM_TX_1; + case AFE_PORT_ID_TERTIARY_TDM_RX_2: + return IDX_AFE_PORT_ID_TERTIARY_TDM_RX_2; + case AFE_PORT_ID_TERTIARY_TDM_TX_2: + return IDX_AFE_PORT_ID_TERTIARY_TDM_TX_2; + case AFE_PORT_ID_TERTIARY_TDM_RX_3: + return IDX_AFE_PORT_ID_TERTIARY_TDM_RX_3; + case AFE_PORT_ID_TERTIARY_TDM_TX_3: + return IDX_AFE_PORT_ID_TERTIARY_TDM_TX_3; + case AFE_PORT_ID_TERTIARY_TDM_RX_4: + return IDX_AFE_PORT_ID_TERTIARY_TDM_RX_4; + case AFE_PORT_ID_TERTIARY_TDM_TX_4: + return IDX_AFE_PORT_ID_TERTIARY_TDM_TX_4; + case AFE_PORT_ID_TERTIARY_TDM_RX_5: + return IDX_AFE_PORT_ID_TERTIARY_TDM_RX_5; + case AFE_PORT_ID_TERTIARY_TDM_TX_5: + return IDX_AFE_PORT_ID_TERTIARY_TDM_TX_5; + case AFE_PORT_ID_TERTIARY_TDM_RX_6: + return IDX_AFE_PORT_ID_TERTIARY_TDM_RX_6; + case AFE_PORT_ID_TERTIARY_TDM_TX_6: + return IDX_AFE_PORT_ID_TERTIARY_TDM_TX_6; + case AFE_PORT_ID_TERTIARY_TDM_RX_7: + return IDX_AFE_PORT_ID_TERTIARY_TDM_RX_7; + case AFE_PORT_ID_TERTIARY_TDM_TX_7: + return IDX_AFE_PORT_ID_TERTIARY_TDM_TX_7; + case AFE_PORT_ID_QUATERNARY_TDM_RX: + return IDX_AFE_PORT_ID_QUATERNARY_TDM_RX_0; + case AFE_PORT_ID_QUATERNARY_TDM_TX: + return IDX_AFE_PORT_ID_QUATERNARY_TDM_TX_0; + case AFE_PORT_ID_QUATERNARY_TDM_RX_1: + return IDX_AFE_PORT_ID_QUATERNARY_TDM_RX_1; + case AFE_PORT_ID_QUATERNARY_TDM_TX_1: + return IDX_AFE_PORT_ID_QUATERNARY_TDM_TX_1; + case AFE_PORT_ID_QUATERNARY_TDM_RX_2: + return IDX_AFE_PORT_ID_QUATERNARY_TDM_RX_2; + case AFE_PORT_ID_QUATERNARY_TDM_TX_2: + return IDX_AFE_PORT_ID_QUATERNARY_TDM_TX_2; + case AFE_PORT_ID_QUATERNARY_TDM_RX_3: + return IDX_AFE_PORT_ID_QUATERNARY_TDM_RX_3; + case AFE_PORT_ID_QUATERNARY_TDM_TX_3: + return IDX_AFE_PORT_ID_QUATERNARY_TDM_TX_3; + case AFE_PORT_ID_QUATERNARY_TDM_RX_4: + return IDX_AFE_PORT_ID_QUATERNARY_TDM_RX_4; + case AFE_PORT_ID_QUATERNARY_TDM_TX_4: + return IDX_AFE_PORT_ID_QUATERNARY_TDM_TX_4; + case AFE_PORT_ID_QUATERNARY_TDM_RX_5: + return IDX_AFE_PORT_ID_QUATERNARY_TDM_RX_5; + case AFE_PORT_ID_QUATERNARY_TDM_TX_5: + return IDX_AFE_PORT_ID_QUATERNARY_TDM_TX_5; + case AFE_PORT_ID_QUATERNARY_TDM_RX_6: + return IDX_AFE_PORT_ID_QUATERNARY_TDM_RX_6; + case AFE_PORT_ID_QUATERNARY_TDM_TX_6: + return IDX_AFE_PORT_ID_QUATERNARY_TDM_TX_6; + case AFE_PORT_ID_QUATERNARY_TDM_RX_7: + return IDX_AFE_PORT_ID_QUATERNARY_TDM_RX_7; + case AFE_PORT_ID_QUATERNARY_TDM_TX_7: + return IDX_AFE_PORT_ID_QUATERNARY_TDM_TX_7; + case AFE_PORT_ID_QUINARY_TDM_RX: + return IDX_AFE_PORT_ID_QUINARY_TDM_RX_0; + case AFE_PORT_ID_QUINARY_TDM_TX: + return IDX_AFE_PORT_ID_QUINARY_TDM_TX_0; + case AFE_PORT_ID_QUINARY_TDM_RX_1: + return IDX_AFE_PORT_ID_QUINARY_TDM_RX_1; + case AFE_PORT_ID_QUINARY_TDM_TX_1: + return IDX_AFE_PORT_ID_QUINARY_TDM_TX_1; + case AFE_PORT_ID_QUINARY_TDM_RX_2: + return IDX_AFE_PORT_ID_QUINARY_TDM_RX_2; + case AFE_PORT_ID_QUINARY_TDM_TX_2: + return IDX_AFE_PORT_ID_QUINARY_TDM_TX_2; + case AFE_PORT_ID_QUINARY_TDM_RX_3: + return IDX_AFE_PORT_ID_QUINARY_TDM_RX_3; + case AFE_PORT_ID_QUINARY_TDM_TX_3: + return IDX_AFE_PORT_ID_QUINARY_TDM_TX_3; + case AFE_PORT_ID_QUINARY_TDM_RX_4: + return IDX_AFE_PORT_ID_QUINARY_TDM_RX_4; + case AFE_PORT_ID_QUINARY_TDM_TX_4: + return IDX_AFE_PORT_ID_QUINARY_TDM_TX_4; + case AFE_PORT_ID_QUINARY_TDM_RX_5: + return IDX_AFE_PORT_ID_QUINARY_TDM_RX_5; + case AFE_PORT_ID_QUINARY_TDM_TX_5: + return IDX_AFE_PORT_ID_QUINARY_TDM_TX_5; + case AFE_PORT_ID_QUINARY_TDM_RX_6: + return IDX_AFE_PORT_ID_QUINARY_TDM_RX_6; + case AFE_PORT_ID_QUINARY_TDM_TX_6: + return IDX_AFE_PORT_ID_QUINARY_TDM_TX_6; + case AFE_PORT_ID_QUINARY_TDM_RX_7: + return IDX_AFE_PORT_ID_QUINARY_TDM_RX_7; + case AFE_PORT_ID_QUINARY_TDM_TX_7: + return IDX_AFE_PORT_ID_QUINARY_TDM_TX_7; + case AFE_PORT_ID_SENARY_MI2S_TX: + return IDX_AFE_PORT_ID_SENARY_MI2S_TX; + case AFE_PORT_ID_USB_RX: + return IDX_AFE_PORT_ID_USB_RX; + case AFE_PORT_ID_USB_TX: + return IDX_AFE_PORT_ID_USB_TX; + case AFE_PORT_ID_INT0_MI2S_RX: + return IDX_AFE_PORT_ID_INT0_MI2S_RX; + case AFE_PORT_ID_INT0_MI2S_TX: + return IDX_AFE_PORT_ID_INT0_MI2S_TX; + case AFE_PORT_ID_INT1_MI2S_RX: + return IDX_AFE_PORT_ID_INT1_MI2S_RX; + case AFE_PORT_ID_INT1_MI2S_TX: + return IDX_AFE_PORT_ID_INT1_MI2S_TX; + case AFE_PORT_ID_INT2_MI2S_RX: + return IDX_AFE_PORT_ID_INT2_MI2S_RX; + case AFE_PORT_ID_INT2_MI2S_TX: + return IDX_AFE_PORT_ID_INT2_MI2S_TX; + case AFE_PORT_ID_INT3_MI2S_RX: + return IDX_AFE_PORT_ID_INT3_MI2S_RX; + case AFE_PORT_ID_INT3_MI2S_TX: + return IDX_AFE_PORT_ID_INT3_MI2S_TX; + case AFE_PORT_ID_INT4_MI2S_RX: + return IDX_AFE_PORT_ID_INT4_MI2S_RX; + case AFE_PORT_ID_INT4_MI2S_TX: + return IDX_AFE_PORT_ID_INT4_MI2S_TX; + case AFE_PORT_ID_INT5_MI2S_RX: + return IDX_AFE_PORT_ID_INT5_MI2S_RX; + case AFE_PORT_ID_INT5_MI2S_TX: + return IDX_AFE_PORT_ID_INT5_MI2S_TX; + case AFE_PORT_ID_INT6_MI2S_RX: + return IDX_AFE_PORT_ID_INT6_MI2S_RX; + case AFE_PORT_ID_INT6_MI2S_TX: + return IDX_AFE_PORT_ID_INT6_MI2S_TX; + case AFE_PORT_ID_WSA_CODEC_DMA_RX_0: + return IDX_AFE_PORT_ID_WSA_CODEC_DMA_RX_0; + case AFE_PORT_ID_WSA_CODEC_DMA_TX_0: + return IDX_AFE_PORT_ID_WSA_CODEC_DMA_TX_0; + case AFE_PORT_ID_WSA_CODEC_DMA_RX_1: + return IDX_AFE_PORT_ID_WSA_CODEC_DMA_RX_1; + case AFE_PORT_ID_WSA_CODEC_DMA_TX_1: + return IDX_AFE_PORT_ID_WSA_CODEC_DMA_TX_1; + case AFE_PORT_ID_WSA_CODEC_DMA_TX_2: + return IDX_AFE_PORT_ID_WSA_CODEC_DMA_TX_2; + case AFE_PORT_ID_VA_CODEC_DMA_TX_0: + return IDX_AFE_PORT_ID_VA_CODEC_DMA_TX_0; + case AFE_PORT_ID_VA_CODEC_DMA_TX_1: + return IDX_AFE_PORT_ID_VA_CODEC_DMA_TX_1; + case AFE_PORT_ID_RX_CODEC_DMA_RX_0: + return IDX_AFE_PORT_ID_RX_CODEC_DMA_RX_0; + case AFE_PORT_ID_TX_CODEC_DMA_TX_0: + return IDX_AFE_PORT_ID_TX_CODEC_DMA_TX_0; + case AFE_PORT_ID_RX_CODEC_DMA_RX_1: + return IDX_AFE_PORT_ID_RX_CODEC_DMA_RX_1; + case AFE_PORT_ID_TX_CODEC_DMA_TX_1: + return IDX_AFE_PORT_ID_TX_CODEC_DMA_TX_1; + case AFE_PORT_ID_RX_CODEC_DMA_RX_2: + return IDX_AFE_PORT_ID_RX_CODEC_DMA_RX_2; + case AFE_PORT_ID_TX_CODEC_DMA_TX_2: + return IDX_AFE_PORT_ID_TX_CODEC_DMA_TX_2; + case AFE_PORT_ID_RX_CODEC_DMA_RX_3: + return IDX_AFE_PORT_ID_RX_CODEC_DMA_RX_3; + case AFE_PORT_ID_TX_CODEC_DMA_TX_3: + return IDX_AFE_PORT_ID_TX_CODEC_DMA_TX_3; + case AFE_PORT_ID_RX_CODEC_DMA_RX_4: + return IDX_AFE_PORT_ID_RX_CODEC_DMA_RX_4; + case AFE_PORT_ID_TX_CODEC_DMA_TX_4: + return IDX_AFE_PORT_ID_TX_CODEC_DMA_TX_4; + case AFE_PORT_ID_RX_CODEC_DMA_RX_5: + return IDX_AFE_PORT_ID_RX_CODEC_DMA_RX_5; + case AFE_PORT_ID_TX_CODEC_DMA_TX_5: + return IDX_AFE_PORT_ID_TX_CODEC_DMA_TX_5; + case AFE_PORT_ID_RX_CODEC_DMA_RX_6: + return IDX_AFE_PORT_ID_RX_CODEC_DMA_RX_6; + case AFE_PORT_ID_RX_CODEC_DMA_RX_7: + return IDX_AFE_PORT_ID_RX_CODEC_DMA_RX_7; + default: return -EINVAL; + } +} + +int q6audio_get_port_id(u16 port_id) +{ + switch (port_id) { + case PRIMARY_I2S_RX: return PRIMARY_I2S_RX; + case PRIMARY_I2S_TX: return PRIMARY_I2S_TX; + case AFE_PORT_ID_PRIMARY_PCM_RX: + return AFE_PORT_ID_PRIMARY_PCM_RX; + case AFE_PORT_ID_PRIMARY_PCM_TX: + return AFE_PORT_ID_PRIMARY_PCM_TX; + case AFE_PORT_ID_SECONDARY_PCM_RX: + return AFE_PORT_ID_SECONDARY_PCM_RX; + case AFE_PORT_ID_SECONDARY_PCM_TX: + return AFE_PORT_ID_SECONDARY_PCM_TX; + case AFE_PORT_ID_TERTIARY_PCM_RX: + return AFE_PORT_ID_TERTIARY_PCM_RX; + case AFE_PORT_ID_TERTIARY_PCM_TX: + return AFE_PORT_ID_TERTIARY_PCM_TX; + case AFE_PORT_ID_QUATERNARY_PCM_RX: + return AFE_PORT_ID_QUATERNARY_PCM_RX; + case AFE_PORT_ID_QUATERNARY_PCM_TX: + return AFE_PORT_ID_QUATERNARY_PCM_TX; + case AFE_PORT_ID_QUINARY_PCM_RX: + return AFE_PORT_ID_QUINARY_PCM_RX; + case AFE_PORT_ID_QUINARY_PCM_TX: + return AFE_PORT_ID_QUINARY_PCM_TX; + case AFE_PORT_ID_SENARY_PCM_RX: + return AFE_PORT_ID_SENARY_PCM_RX; + case AFE_PORT_ID_SENARY_PCM_TX: + return AFE_PORT_ID_SENARY_PCM_TX; + case SECONDARY_I2S_RX: return AFE_PORT_ID_SECONDARY_MI2S_RX; + case SECONDARY_I2S_TX: return AFE_PORT_ID_SECONDARY_MI2S_TX; + case MI2S_RX: return AFE_PORT_ID_PRIMARY_MI2S_RX; + case MI2S_TX: return AFE_PORT_ID_PRIMARY_MI2S_TX; + case HDMI_RX: return AFE_PORT_ID_MULTICHAN_HDMI_RX; + case DISPLAY_PORT_RX: + return AFE_PORT_ID_HDMI_OVER_DP_RX; + case AFE_PORT_ID_PRIMARY_SPDIF_RX: + return AFE_PORT_ID_PRIMARY_SPDIF_RX; + case AFE_PORT_ID_PRIMARY_SPDIF_TX: + return AFE_PORT_ID_PRIMARY_SPDIF_TX; + case AFE_PORT_ID_SECONDARY_SPDIF_RX: + return AFE_PORT_ID_SECONDARY_SPDIF_RX; + case AFE_PORT_ID_SECONDARY_SPDIF_TX: + return AFE_PORT_ID_SECONDARY_SPDIF_TX; + case RSVD_2: return IDX_RSVD_2; + case RSVD_3: return IDX_RSVD_3; + case DIGI_MIC_TX: return AFE_PORT_ID_DIGITAL_MIC_TX; + case VOICE_RECORD_RX: return AFE_PORT_ID_VOICE_RECORD_RX; + case VOICE_RECORD_TX: return AFE_PORT_ID_VOICE_RECORD_TX; + case VOICE_PLAYBACK_TX: return AFE_PORT_ID_VOICE_PLAYBACK_TX; + case VOICE2_PLAYBACK_TX: return AFE_PORT_ID_VOICE2_PLAYBACK_TX; + case SLIMBUS_0_RX: return AFE_PORT_ID_SLIMBUS_MULTI_CHAN_0_RX; + case SLIMBUS_0_TX: return AFE_PORT_ID_SLIMBUS_MULTI_CHAN_0_TX; + case SLIMBUS_1_RX: return AFE_PORT_ID_SLIMBUS_MULTI_CHAN_1_RX; + case SLIMBUS_1_TX: return AFE_PORT_ID_SLIMBUS_MULTI_CHAN_1_TX; + case SLIMBUS_2_RX: return AFE_PORT_ID_SLIMBUS_MULTI_CHAN_2_RX; + case SLIMBUS_2_TX: return AFE_PORT_ID_SLIMBUS_MULTI_CHAN_2_TX; + case SLIMBUS_3_RX: return AFE_PORT_ID_SLIMBUS_MULTI_CHAN_3_RX; + case SLIMBUS_3_TX: return AFE_PORT_ID_SLIMBUS_MULTI_CHAN_3_TX; + case SLIMBUS_4_RX: return AFE_PORT_ID_SLIMBUS_MULTI_CHAN_4_RX; + case SLIMBUS_4_TX: return AFE_PORT_ID_SLIMBUS_MULTI_CHAN_4_TX; + case SLIMBUS_5_RX: return AFE_PORT_ID_SLIMBUS_MULTI_CHAN_5_RX; + case SLIMBUS_5_TX: return AFE_PORT_ID_SLIMBUS_MULTI_CHAN_5_TX; + case SLIMBUS_6_RX: return AFE_PORT_ID_SLIMBUS_MULTI_CHAN_6_RX; + case SLIMBUS_6_TX: return AFE_PORT_ID_SLIMBUS_MULTI_CHAN_6_TX; + case SLIMBUS_7_RX: return AFE_PORT_ID_SLIMBUS_MULTI_CHAN_7_RX; + case SLIMBUS_7_TX: return AFE_PORT_ID_SLIMBUS_MULTI_CHAN_7_TX; + case SLIMBUS_8_RX: return AFE_PORT_ID_SLIMBUS_MULTI_CHAN_8_RX; + case SLIMBUS_8_TX: return AFE_PORT_ID_SLIMBUS_MULTI_CHAN_8_TX; + case SLIMBUS_9_RX: return AFE_PORT_ID_SLIMBUS_MULTI_CHAN_9_RX; + case SLIMBUS_9_TX: return AFE_PORT_ID_SLIMBUS_MULTI_CHAN_9_TX; + case INT_BT_SCO_RX: return AFE_PORT_ID_INTERNAL_BT_SCO_RX; + case INT_BT_SCO_TX: return AFE_PORT_ID_INTERNAL_BT_SCO_TX; + case INT_BT_A2DP_RX: return AFE_PORT_ID_INTERNAL_BT_A2DP_RX; + case INT_FM_RX: return AFE_PORT_ID_INTERNAL_FM_RX; + case INT_FM_TX: return AFE_PORT_ID_INTERNAL_FM_TX; + case RT_PROXY_PORT_001_RX: return AFE_PORT_ID_RT_PROXY_PORT_001_RX; + case RT_PROXY_PORT_001_TX: return AFE_PORT_ID_RT_PROXY_PORT_001_TX; + case AFE_PORT_ID_PRIMARY_MI2S_RX: + return AFE_PORT_ID_PRIMARY_MI2S_RX; + case AFE_PORT_ID_PRIMARY_MI2S_TX: + return AFE_PORT_ID_PRIMARY_MI2S_TX; + case AFE_PORT_ID_QUATERNARY_MI2S_RX: + return AFE_PORT_ID_QUATERNARY_MI2S_RX; + case AFE_PORT_ID_QUATERNARY_MI2S_TX: + return AFE_PORT_ID_QUATERNARY_MI2S_TX; + case AFE_PORT_ID_SECONDARY_MI2S_RX: + return AFE_PORT_ID_SECONDARY_MI2S_RX; + case AFE_PORT_ID_SECONDARY_MI2S_TX: + return AFE_PORT_ID_SECONDARY_MI2S_TX; + case AFE_PORT_ID_TERTIARY_MI2S_RX: + return AFE_PORT_ID_TERTIARY_MI2S_RX; + case AFE_PORT_ID_TERTIARY_MI2S_TX: + return AFE_PORT_ID_TERTIARY_MI2S_TX; + case AFE_PORT_ID_QUINARY_MI2S_RX: + return AFE_PORT_ID_QUINARY_MI2S_RX; + case AFE_PORT_ID_QUINARY_MI2S_TX: + return AFE_PORT_ID_QUINARY_MI2S_TX; + case AUDIO_PORT_ID_I2S_RX: + return AUDIO_PORT_ID_I2S_RX; + case AFE_PORT_ID_SECONDARY_MI2S_RX_SD1: + return AFE_PORT_ID_SECONDARY_MI2S_RX_SD1; + case AFE_PORT_ID_PRIMARY_TDM_RX: + return AFE_PORT_ID_PRIMARY_TDM_RX; + case AFE_PORT_ID_PRIMARY_TDM_TX: + return AFE_PORT_ID_PRIMARY_TDM_TX; + case AFE_PORT_ID_PRIMARY_TDM_RX_1: + return AFE_PORT_ID_PRIMARY_TDM_RX_1; + case AFE_PORT_ID_PRIMARY_TDM_TX_1: + return AFE_PORT_ID_PRIMARY_TDM_TX_1; + case AFE_PORT_ID_PRIMARY_TDM_RX_2: + return AFE_PORT_ID_PRIMARY_TDM_RX_2; + case AFE_PORT_ID_PRIMARY_TDM_TX_2: + return AFE_PORT_ID_PRIMARY_TDM_TX_2; + case AFE_PORT_ID_PRIMARY_TDM_RX_3: + return AFE_PORT_ID_PRIMARY_TDM_RX_3; + case AFE_PORT_ID_PRIMARY_TDM_TX_3: + return AFE_PORT_ID_PRIMARY_TDM_TX_3; + case AFE_PORT_ID_PRIMARY_TDM_RX_4: + return AFE_PORT_ID_PRIMARY_TDM_RX_4; + case AFE_PORT_ID_PRIMARY_TDM_TX_4: + return AFE_PORT_ID_PRIMARY_TDM_TX_4; + case AFE_PORT_ID_PRIMARY_TDM_RX_5: + return AFE_PORT_ID_PRIMARY_TDM_RX_5; + case AFE_PORT_ID_PRIMARY_TDM_TX_5: + return AFE_PORT_ID_PRIMARY_TDM_TX_5; + case AFE_PORT_ID_PRIMARY_TDM_RX_6: + return AFE_PORT_ID_PRIMARY_TDM_RX_6; + case AFE_PORT_ID_PRIMARY_TDM_TX_6: + return AFE_PORT_ID_PRIMARY_TDM_TX_6; + case AFE_PORT_ID_PRIMARY_TDM_RX_7: + return AFE_PORT_ID_PRIMARY_TDM_RX_7; + case AFE_PORT_ID_PRIMARY_TDM_TX_7: + return AFE_PORT_ID_PRIMARY_TDM_TX_7; + case AFE_PORT_ID_SECONDARY_TDM_RX: + return AFE_PORT_ID_SECONDARY_TDM_RX; + case AFE_PORT_ID_SECONDARY_TDM_TX: + return AFE_PORT_ID_SECONDARY_TDM_TX; + case AFE_PORT_ID_SECONDARY_TDM_RX_1: + return AFE_PORT_ID_SECONDARY_TDM_RX_1; + case AFE_PORT_ID_SECONDARY_TDM_TX_1: + return AFE_PORT_ID_SECONDARY_TDM_TX_1; + case AFE_PORT_ID_SECONDARY_TDM_RX_2: + return AFE_PORT_ID_SECONDARY_TDM_RX_2; + case AFE_PORT_ID_SECONDARY_TDM_TX_2: + return AFE_PORT_ID_SECONDARY_TDM_TX_2; + case AFE_PORT_ID_SECONDARY_TDM_RX_3: + return AFE_PORT_ID_SECONDARY_TDM_RX_3; + case AFE_PORT_ID_SECONDARY_TDM_TX_3: + return AFE_PORT_ID_SECONDARY_TDM_TX_3; + case AFE_PORT_ID_SECONDARY_TDM_RX_4: + return AFE_PORT_ID_SECONDARY_TDM_RX_4; + case AFE_PORT_ID_SECONDARY_TDM_TX_4: + return AFE_PORT_ID_SECONDARY_TDM_TX_4; + case AFE_PORT_ID_SECONDARY_TDM_RX_5: + return AFE_PORT_ID_SECONDARY_TDM_RX_5; + case AFE_PORT_ID_SECONDARY_TDM_TX_5: + return AFE_PORT_ID_SECONDARY_TDM_TX_5; + case AFE_PORT_ID_SECONDARY_TDM_RX_6: + return AFE_PORT_ID_SECONDARY_TDM_RX_6; + case AFE_PORT_ID_SECONDARY_TDM_TX_6: + return AFE_PORT_ID_SECONDARY_TDM_TX_6; + case AFE_PORT_ID_SECONDARY_TDM_RX_7: + return AFE_PORT_ID_SECONDARY_TDM_RX_7; + case AFE_PORT_ID_SECONDARY_TDM_TX_7: + return AFE_PORT_ID_SECONDARY_TDM_TX_7; + case AFE_PORT_ID_TERTIARY_TDM_RX: + return AFE_PORT_ID_TERTIARY_TDM_RX; + case AFE_PORT_ID_TERTIARY_TDM_TX: + return AFE_PORT_ID_TERTIARY_TDM_TX; + case AFE_PORT_ID_TERTIARY_TDM_RX_1: + return AFE_PORT_ID_TERTIARY_TDM_RX_1; + case AFE_PORT_ID_TERTIARY_TDM_TX_1: + return AFE_PORT_ID_TERTIARY_TDM_TX_1; + case AFE_PORT_ID_TERTIARY_TDM_RX_2: + return AFE_PORT_ID_TERTIARY_TDM_RX_2; + case AFE_PORT_ID_TERTIARY_TDM_TX_2: + return AFE_PORT_ID_TERTIARY_TDM_TX_2; + case AFE_PORT_ID_TERTIARY_TDM_RX_3: + return AFE_PORT_ID_TERTIARY_TDM_RX_3; + case AFE_PORT_ID_TERTIARY_TDM_TX_3: + return AFE_PORT_ID_TERTIARY_TDM_TX_3; + case AFE_PORT_ID_TERTIARY_TDM_RX_4: + return AFE_PORT_ID_TERTIARY_TDM_RX_4; + case AFE_PORT_ID_TERTIARY_TDM_TX_4: + return AFE_PORT_ID_TERTIARY_TDM_TX_4; + case AFE_PORT_ID_TERTIARY_TDM_RX_5: + return AFE_PORT_ID_TERTIARY_TDM_RX_5; + case AFE_PORT_ID_TERTIARY_TDM_TX_5: + return AFE_PORT_ID_TERTIARY_TDM_TX_5; + case AFE_PORT_ID_TERTIARY_TDM_RX_6: + return AFE_PORT_ID_TERTIARY_TDM_RX_6; + case AFE_PORT_ID_TERTIARY_TDM_TX_6: + return AFE_PORT_ID_TERTIARY_TDM_TX_6; + case AFE_PORT_ID_TERTIARY_TDM_RX_7: + return AFE_PORT_ID_TERTIARY_TDM_RX_7; + case AFE_PORT_ID_TERTIARY_TDM_TX_7: + return AFE_PORT_ID_TERTIARY_TDM_TX_7; + case AFE_PORT_ID_QUATERNARY_TDM_RX: + return AFE_PORT_ID_QUATERNARY_TDM_RX; + case AFE_PORT_ID_QUATERNARY_TDM_TX: + return AFE_PORT_ID_QUATERNARY_TDM_TX; + case AFE_PORT_ID_QUATERNARY_TDM_RX_1: + return AFE_PORT_ID_QUATERNARY_TDM_RX_1; + case AFE_PORT_ID_QUATERNARY_TDM_TX_1: + return AFE_PORT_ID_QUATERNARY_TDM_TX_1; + case AFE_PORT_ID_QUATERNARY_TDM_RX_2: + return AFE_PORT_ID_QUATERNARY_TDM_RX_2; + case AFE_PORT_ID_QUATERNARY_TDM_TX_2: + return AFE_PORT_ID_QUATERNARY_TDM_TX_2; + case AFE_PORT_ID_QUATERNARY_TDM_RX_3: + return AFE_PORT_ID_QUATERNARY_TDM_RX_3; + case AFE_PORT_ID_QUATERNARY_TDM_TX_3: + return AFE_PORT_ID_QUATERNARY_TDM_TX_3; + case AFE_PORT_ID_QUATERNARY_TDM_RX_4: + return AFE_PORT_ID_QUATERNARY_TDM_RX_4; + case AFE_PORT_ID_QUATERNARY_TDM_TX_4: + return AFE_PORT_ID_QUATERNARY_TDM_TX_4; + case AFE_PORT_ID_QUATERNARY_TDM_RX_5: + return AFE_PORT_ID_QUATERNARY_TDM_RX_5; + case AFE_PORT_ID_QUATERNARY_TDM_TX_5: + return AFE_PORT_ID_QUATERNARY_TDM_TX_5; + case AFE_PORT_ID_QUATERNARY_TDM_RX_6: + return AFE_PORT_ID_QUATERNARY_TDM_RX_6; + case AFE_PORT_ID_QUATERNARY_TDM_TX_6: + return AFE_PORT_ID_QUATERNARY_TDM_TX_6; + case AFE_PORT_ID_QUATERNARY_TDM_RX_7: + return AFE_PORT_ID_QUATERNARY_TDM_RX_7; + case AFE_PORT_ID_QUATERNARY_TDM_TX_7: + return AFE_PORT_ID_QUATERNARY_TDM_TX_7; + case AFE_PORT_ID_QUINARY_TDM_RX: + return AFE_PORT_ID_QUINARY_TDM_RX; + case AFE_PORT_ID_QUINARY_TDM_TX: + return AFE_PORT_ID_QUINARY_TDM_TX; + case AFE_PORT_ID_QUINARY_TDM_RX_1: + return AFE_PORT_ID_QUINARY_TDM_RX_1; + case AFE_PORT_ID_QUINARY_TDM_TX_1: + return AFE_PORT_ID_QUINARY_TDM_TX_1; + case AFE_PORT_ID_QUINARY_TDM_RX_2: + return AFE_PORT_ID_QUINARY_TDM_RX_2; + case AFE_PORT_ID_QUINARY_TDM_TX_2: + return AFE_PORT_ID_QUINARY_TDM_TX_2; + case AFE_PORT_ID_QUINARY_TDM_RX_3: + return AFE_PORT_ID_QUINARY_TDM_RX_3; + case AFE_PORT_ID_QUINARY_TDM_TX_3: + return AFE_PORT_ID_QUINARY_TDM_TX_3; + case AFE_PORT_ID_QUINARY_TDM_RX_4: + return AFE_PORT_ID_QUINARY_TDM_RX_4; + case AFE_PORT_ID_QUINARY_TDM_TX_4: + return AFE_PORT_ID_QUINARY_TDM_TX_4; + case AFE_PORT_ID_QUINARY_TDM_RX_5: + return AFE_PORT_ID_QUINARY_TDM_RX_5; + case AFE_PORT_ID_QUINARY_TDM_TX_5: + return AFE_PORT_ID_QUINARY_TDM_TX_5; + case AFE_PORT_ID_QUINARY_TDM_RX_6: + return AFE_PORT_ID_QUINARY_TDM_RX_6; + case AFE_PORT_ID_QUINARY_TDM_TX_6: + return AFE_PORT_ID_QUINARY_TDM_TX_6; + case AFE_PORT_ID_QUINARY_TDM_RX_7: + return AFE_PORT_ID_QUINARY_TDM_RX_7; + case AFE_PORT_ID_QUINARY_TDM_TX_7: + return AFE_PORT_ID_QUINARY_TDM_TX_7; + case AFE_PORT_ID_SENARY_MI2S_TX: + return AFE_PORT_ID_SENARY_MI2S_TX; + case AFE_PORT_ID_USB_RX: + return AFE_PORT_ID_USB_RX; + case AFE_PORT_ID_USB_TX: + return AFE_PORT_ID_USB_TX; + case AFE_PORT_ID_INT0_MI2S_RX: + return AFE_PORT_ID_INT0_MI2S_RX; + case AFE_PORT_ID_INT0_MI2S_TX: + return AFE_PORT_ID_INT0_MI2S_TX; + case AFE_PORT_ID_INT1_MI2S_RX: + return AFE_PORT_ID_INT1_MI2S_RX; + case AFE_PORT_ID_INT1_MI2S_TX: + return AFE_PORT_ID_INT1_MI2S_TX; + case AFE_PORT_ID_INT2_MI2S_RX: + return AFE_PORT_ID_INT2_MI2S_RX; + case AFE_PORT_ID_INT2_MI2S_TX: + return AFE_PORT_ID_INT2_MI2S_TX; + case AFE_PORT_ID_INT3_MI2S_RX: + return AFE_PORT_ID_INT3_MI2S_RX; + case AFE_PORT_ID_INT3_MI2S_TX: + return AFE_PORT_ID_INT3_MI2S_TX; + case AFE_PORT_ID_INT4_MI2S_RX: + return AFE_PORT_ID_INT4_MI2S_RX; + case AFE_PORT_ID_INT4_MI2S_TX: + return AFE_PORT_ID_INT4_MI2S_TX; + case AFE_PORT_ID_INT5_MI2S_RX: + return AFE_PORT_ID_INT5_MI2S_RX; + case AFE_PORT_ID_INT5_MI2S_TX: + return AFE_PORT_ID_INT5_MI2S_TX; + case AFE_PORT_ID_INT6_MI2S_RX: + return AFE_PORT_ID_INT6_MI2S_RX; + case AFE_PORT_ID_INT6_MI2S_TX: + return AFE_PORT_ID_INT6_MI2S_TX; + case AFE_PORT_ID_WSA_CODEC_DMA_RX_0: + return AFE_PORT_ID_WSA_CODEC_DMA_RX_0; + case AFE_PORT_ID_WSA_CODEC_DMA_TX_0: + return AFE_PORT_ID_WSA_CODEC_DMA_TX_0; + case AFE_PORT_ID_WSA_CODEC_DMA_RX_1: + return AFE_PORT_ID_WSA_CODEC_DMA_RX_1; + case AFE_PORT_ID_WSA_CODEC_DMA_TX_1: + return AFE_PORT_ID_WSA_CODEC_DMA_TX_1; + case AFE_PORT_ID_WSA_CODEC_DMA_TX_2: + return AFE_PORT_ID_WSA_CODEC_DMA_TX_2; + case AFE_PORT_ID_VA_CODEC_DMA_TX_0: + return AFE_PORT_ID_VA_CODEC_DMA_TX_0; + case AFE_PORT_ID_VA_CODEC_DMA_TX_1: + return AFE_PORT_ID_VA_CODEC_DMA_TX_1; + case AFE_PORT_ID_RX_CODEC_DMA_RX_0: + return AFE_PORT_ID_RX_CODEC_DMA_RX_0; + case AFE_PORT_ID_TX_CODEC_DMA_TX_0: + return AFE_PORT_ID_TX_CODEC_DMA_TX_0; + case AFE_PORT_ID_RX_CODEC_DMA_RX_1: + return AFE_PORT_ID_RX_CODEC_DMA_RX_1; + case AFE_PORT_ID_TX_CODEC_DMA_TX_1: + return AFE_PORT_ID_TX_CODEC_DMA_TX_1; + case AFE_PORT_ID_RX_CODEC_DMA_RX_2: + return AFE_PORT_ID_RX_CODEC_DMA_RX_2; + case AFE_PORT_ID_TX_CODEC_DMA_TX_2: + return AFE_PORT_ID_TX_CODEC_DMA_TX_2; + case AFE_PORT_ID_RX_CODEC_DMA_RX_3: + return AFE_PORT_ID_RX_CODEC_DMA_RX_3; + case AFE_PORT_ID_TX_CODEC_DMA_TX_3: + return AFE_PORT_ID_TX_CODEC_DMA_TX_3; + case AFE_PORT_ID_RX_CODEC_DMA_RX_4: + return AFE_PORT_ID_RX_CODEC_DMA_RX_4; + case AFE_PORT_ID_TX_CODEC_DMA_TX_4: + return AFE_PORT_ID_TX_CODEC_DMA_TX_4; + case AFE_PORT_ID_RX_CODEC_DMA_RX_5: + return AFE_PORT_ID_RX_CODEC_DMA_RX_5; + case AFE_PORT_ID_TX_CODEC_DMA_TX_5: + return AFE_PORT_ID_TX_CODEC_DMA_TX_5; + case AFE_PORT_ID_RX_CODEC_DMA_RX_6: + return AFE_PORT_ID_RX_CODEC_DMA_RX_6; + case AFE_PORT_ID_RX_CODEC_DMA_RX_7: + return AFE_PORT_ID_RX_CODEC_DMA_RX_7; + default: + pr_warn("%s: Invalid port_id %d\n", __func__, port_id); + return -EINVAL; + } +} +int q6audio_convert_virtual_to_portid(u16 port_id) +{ + int ret; + + /* if port_id is virtual, convert to physical.. + * if port_id is already physical, return physical + */ + if (q6audio_validate_port(port_id) < 0) { + if (port_id == RT_PROXY_DAI_001_RX || + port_id == RT_PROXY_DAI_001_TX || + port_id == RT_PROXY_DAI_002_RX || + port_id == RT_PROXY_DAI_002_TX) + ret = VIRTUAL_ID_TO_PORTID(port_id); + else + ret = -EINVAL; + } else + ret = port_id; + + return ret; +} + +int q6audio_is_digital_pcm_interface(u16 port_id) +{ + int ret = 0; + + switch (port_id) { + case PRIMARY_I2S_RX: + case PRIMARY_I2S_TX: + case AFE_PORT_ID_PRIMARY_PCM_RX: + case AFE_PORT_ID_PRIMARY_PCM_TX: + case AFE_PORT_ID_SECONDARY_PCM_RX: + case AFE_PORT_ID_SECONDARY_PCM_TX: + case AFE_PORT_ID_TERTIARY_PCM_RX: + case AFE_PORT_ID_TERTIARY_PCM_TX: + case AFE_PORT_ID_QUATERNARY_PCM_RX: + case AFE_PORT_ID_QUATERNARY_PCM_TX: + case AFE_PORT_ID_QUINARY_PCM_RX: + case AFE_PORT_ID_QUINARY_PCM_TX: + case AFE_PORT_ID_SENARY_PCM_RX: + case AFE_PORT_ID_SENARY_PCM_TX: + case SECONDARY_I2S_RX: + case SECONDARY_I2S_TX: + case MI2S_RX: + case MI2S_TX: + case AFE_PORT_ID_TERTIARY_MI2S_TX: + case AFE_PORT_ID_TERTIARY_MI2S_RX: + case AFE_PORT_ID_QUINARY_MI2S_TX: + case AFE_PORT_ID_QUINARY_MI2S_RX: + case AFE_PORT_ID_QUATERNARY_MI2S_RX: + case AFE_PORT_ID_QUATERNARY_MI2S_TX: + case AFE_PORT_ID_PRIMARY_MI2S_RX: + case AFE_PORT_ID_PRIMARY_MI2S_TX: + case AFE_PORT_ID_SECONDARY_MI2S_RX: + case AFE_PORT_ID_SECONDARY_MI2S_TX: + case AUDIO_PORT_ID_I2S_RX: + case AFE_PORT_ID_SECONDARY_MI2S_RX_SD1: + case AFE_PORT_ID_PRIMARY_TDM_RX: + case AFE_PORT_ID_PRIMARY_TDM_TX: + case AFE_PORT_ID_PRIMARY_TDM_RX_1: + case AFE_PORT_ID_PRIMARY_TDM_TX_1: + case AFE_PORT_ID_PRIMARY_TDM_RX_2: + case AFE_PORT_ID_PRIMARY_TDM_TX_2: + case AFE_PORT_ID_PRIMARY_TDM_RX_3: + case AFE_PORT_ID_PRIMARY_TDM_TX_3: + case AFE_PORT_ID_PRIMARY_TDM_RX_4: + case AFE_PORT_ID_PRIMARY_TDM_TX_4: + case AFE_PORT_ID_PRIMARY_TDM_RX_5: + case AFE_PORT_ID_PRIMARY_TDM_TX_5: + case AFE_PORT_ID_PRIMARY_TDM_RX_6: + case AFE_PORT_ID_PRIMARY_TDM_TX_6: + case AFE_PORT_ID_PRIMARY_TDM_RX_7: + case AFE_PORT_ID_PRIMARY_TDM_TX_7: + case AFE_PORT_ID_SECONDARY_TDM_RX: + case AFE_PORT_ID_SECONDARY_TDM_TX: + case AFE_PORT_ID_SECONDARY_TDM_RX_1: + case AFE_PORT_ID_SECONDARY_TDM_TX_1: + case AFE_PORT_ID_SECONDARY_TDM_RX_2: + case AFE_PORT_ID_SECONDARY_TDM_TX_2: + case AFE_PORT_ID_SECONDARY_TDM_RX_3: + case AFE_PORT_ID_SECONDARY_TDM_TX_3: + case AFE_PORT_ID_SECONDARY_TDM_RX_4: + case AFE_PORT_ID_SECONDARY_TDM_TX_4: + case AFE_PORT_ID_SECONDARY_TDM_RX_5: + case AFE_PORT_ID_SECONDARY_TDM_TX_5: + case AFE_PORT_ID_SECONDARY_TDM_RX_6: + case AFE_PORT_ID_SECONDARY_TDM_TX_6: + case AFE_PORT_ID_SECONDARY_TDM_RX_7: + case AFE_PORT_ID_SECONDARY_TDM_TX_7: + case AFE_PORT_ID_TERTIARY_TDM_RX: + case AFE_PORT_ID_TERTIARY_TDM_TX: + case AFE_PORT_ID_TERTIARY_TDM_RX_1: + case AFE_PORT_ID_TERTIARY_TDM_TX_1: + case AFE_PORT_ID_TERTIARY_TDM_RX_2: + case AFE_PORT_ID_TERTIARY_TDM_TX_2: + case AFE_PORT_ID_TERTIARY_TDM_RX_3: + case AFE_PORT_ID_TERTIARY_TDM_TX_3: + case AFE_PORT_ID_TERTIARY_TDM_RX_4: + case AFE_PORT_ID_TERTIARY_TDM_TX_4: + case AFE_PORT_ID_TERTIARY_TDM_RX_5: + case AFE_PORT_ID_TERTIARY_TDM_TX_5: + case AFE_PORT_ID_TERTIARY_TDM_RX_6: + case AFE_PORT_ID_TERTIARY_TDM_TX_6: + case AFE_PORT_ID_TERTIARY_TDM_RX_7: + case AFE_PORT_ID_TERTIARY_TDM_TX_7: + case AFE_PORT_ID_QUATERNARY_TDM_RX: + case AFE_PORT_ID_QUATERNARY_TDM_TX: + case AFE_PORT_ID_QUATERNARY_TDM_RX_1: + case AFE_PORT_ID_QUATERNARY_TDM_TX_1: + case AFE_PORT_ID_QUATERNARY_TDM_RX_2: + case AFE_PORT_ID_QUATERNARY_TDM_TX_2: + case AFE_PORT_ID_QUATERNARY_TDM_RX_3: + case AFE_PORT_ID_QUATERNARY_TDM_TX_3: + case AFE_PORT_ID_QUATERNARY_TDM_RX_4: + case AFE_PORT_ID_QUATERNARY_TDM_TX_4: + case AFE_PORT_ID_QUATERNARY_TDM_RX_5: + case AFE_PORT_ID_QUATERNARY_TDM_TX_5: + case AFE_PORT_ID_QUATERNARY_TDM_RX_6: + case AFE_PORT_ID_QUATERNARY_TDM_TX_6: + case AFE_PORT_ID_QUATERNARY_TDM_RX_7: + case AFE_PORT_ID_QUATERNARY_TDM_TX_7: + case AFE_PORT_ID_QUINARY_TDM_RX: + case AFE_PORT_ID_QUINARY_TDM_TX: + case AFE_PORT_ID_QUINARY_TDM_RX_1: + case AFE_PORT_ID_QUINARY_TDM_TX_1: + case AFE_PORT_ID_QUINARY_TDM_RX_2: + case AFE_PORT_ID_QUINARY_TDM_TX_2: + case AFE_PORT_ID_QUINARY_TDM_RX_3: + case AFE_PORT_ID_QUINARY_TDM_TX_3: + case AFE_PORT_ID_QUINARY_TDM_RX_4: + case AFE_PORT_ID_QUINARY_TDM_TX_4: + case AFE_PORT_ID_QUINARY_TDM_RX_5: + case AFE_PORT_ID_QUINARY_TDM_TX_5: + case AFE_PORT_ID_QUINARY_TDM_RX_6: + case AFE_PORT_ID_QUINARY_TDM_TX_6: + case AFE_PORT_ID_QUINARY_TDM_RX_7: + case AFE_PORT_ID_QUINARY_TDM_TX_7: + case AFE_PORT_ID_SENARY_MI2S_TX: + case AFE_PORT_ID_INT0_MI2S_RX: + case AFE_PORT_ID_INT0_MI2S_TX: + case AFE_PORT_ID_INT1_MI2S_RX: + case AFE_PORT_ID_INT1_MI2S_TX: + case AFE_PORT_ID_INT2_MI2S_RX: + case AFE_PORT_ID_INT2_MI2S_TX: + case AFE_PORT_ID_INT3_MI2S_RX: + case AFE_PORT_ID_INT3_MI2S_TX: + case AFE_PORT_ID_INT4_MI2S_RX: + case AFE_PORT_ID_INT4_MI2S_TX: + case AFE_PORT_ID_INT5_MI2S_RX: + 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_WSA_CODEC_DMA_RX_0: + case AFE_PORT_ID_WSA_CODEC_DMA_TX_0: + case AFE_PORT_ID_WSA_CODEC_DMA_RX_1: + case AFE_PORT_ID_WSA_CODEC_DMA_TX_1: + case AFE_PORT_ID_WSA_CODEC_DMA_TX_2: + case AFE_PORT_ID_VA_CODEC_DMA_TX_0: + case AFE_PORT_ID_VA_CODEC_DMA_TX_1: + case AFE_PORT_ID_RX_CODEC_DMA_RX_0: + case AFE_PORT_ID_TX_CODEC_DMA_TX_0: + case AFE_PORT_ID_RX_CODEC_DMA_RX_1: + case AFE_PORT_ID_TX_CODEC_DMA_TX_1: + case AFE_PORT_ID_RX_CODEC_DMA_RX_2: + case AFE_PORT_ID_TX_CODEC_DMA_TX_2: + case AFE_PORT_ID_RX_CODEC_DMA_RX_3: + case AFE_PORT_ID_TX_CODEC_DMA_TX_3: + case AFE_PORT_ID_RX_CODEC_DMA_RX_4: + case AFE_PORT_ID_TX_CODEC_DMA_TX_4: + case AFE_PORT_ID_RX_CODEC_DMA_RX_5: + case AFE_PORT_ID_TX_CODEC_DMA_TX_5: + case AFE_PORT_ID_RX_CODEC_DMA_RX_6: + case AFE_PORT_ID_RX_CODEC_DMA_RX_7: + case AFE_PORT_ID_PRIMARY_SPDIF_RX: + case AFE_PORT_ID_PRIMARY_SPDIF_TX: + case AFE_PORT_ID_SECONDARY_SPDIF_RX: + case AFE_PORT_ID_SECONDARY_SPDIF_TX: + break; + default: + ret = -EINVAL; + } + + return ret; +} + +/** + * q6audio_validate_port - + * validates port id + * + * @port_id: port id to validate + * + * Returns 0 on success or error on invalid port + */ +int q6audio_validate_port(u16 port_id) +{ + int ret; + + switch (port_id) { + case PRIMARY_I2S_RX: + case PRIMARY_I2S_TX: + case AFE_PORT_ID_PRIMARY_PCM_RX: + case AFE_PORT_ID_PRIMARY_PCM_TX: + case AFE_PORT_ID_SECONDARY_PCM_RX: + case AFE_PORT_ID_SECONDARY_PCM_TX: + case AFE_PORT_ID_TERTIARY_PCM_RX: + case AFE_PORT_ID_TERTIARY_PCM_TX: + case AFE_PORT_ID_QUATERNARY_PCM_RX: + case AFE_PORT_ID_QUATERNARY_PCM_TX: + case AFE_PORT_ID_QUINARY_PCM_RX: + case AFE_PORT_ID_QUINARY_PCM_TX: + case AFE_PORT_ID_SENARY_PCM_RX: + case AFE_PORT_ID_SENARY_PCM_TX: + case SECONDARY_I2S_RX: + case SECONDARY_I2S_TX: + case MI2S_RX: + case MI2S_TX: + case HDMI_RX: + case DISPLAY_PORT_RX: + case RSVD_2: + case RSVD_3: + case DIGI_MIC_TX: + case VOICE_RECORD_RX: + case VOICE_RECORD_TX: + case VOICE_PLAYBACK_TX: + case VOICE2_PLAYBACK_TX: + case SLIMBUS_0_RX: + case SLIMBUS_0_TX: + case SLIMBUS_1_RX: + case SLIMBUS_1_TX: + case SLIMBUS_2_RX: + case SLIMBUS_2_TX: + case SLIMBUS_3_RX: + case SLIMBUS_3_TX: + case SLIMBUS_4_RX: + case SLIMBUS_4_TX: + case SLIMBUS_5_RX: + case SLIMBUS_5_TX: + case SLIMBUS_6_RX: + case SLIMBUS_6_TX: + case SLIMBUS_7_RX: + case SLIMBUS_7_TX: + case SLIMBUS_8_RX: + case SLIMBUS_8_TX: + case SLIMBUS_9_RX: + case SLIMBUS_9_TX: + case INT_BT_SCO_RX: + case INT_BT_SCO_TX: + case INT_BT_A2DP_RX: + case INT_FM_RX: + case INT_FM_TX: + case RT_PROXY_PORT_001_RX: + case RT_PROXY_PORT_001_TX: + case AFE_PORT_ID_PRIMARY_MI2S_RX: + case AFE_PORT_ID_PRIMARY_MI2S_TX: + case AFE_PORT_ID_QUATERNARY_MI2S_RX: + case AFE_PORT_ID_QUATERNARY_MI2S_TX: + case AFE_PORT_ID_SECONDARY_MI2S_RX: + case AFE_PORT_ID_SECONDARY_MI2S_TX: + case AFE_PORT_ID_PRIMARY_SPDIF_RX: + case AFE_PORT_ID_PRIMARY_SPDIF_TX: + case AFE_PORT_ID_SECONDARY_SPDIF_RX: + case AFE_PORT_ID_SECONDARY_SPDIF_TX: + case AFE_PORT_ID_TERTIARY_MI2S_RX: + case AFE_PORT_ID_TERTIARY_MI2S_TX: + case AFE_PORT_ID_QUINARY_MI2S_RX: + case AFE_PORT_ID_QUINARY_MI2S_TX: + case AFE_PORT_ID_SECONDARY_MI2S_RX_SD1: + case AFE_PORT_ID_PRIMARY_TDM_RX: + case AFE_PORT_ID_PRIMARY_TDM_TX: + case AFE_PORT_ID_PRIMARY_TDM_RX_1: + case AFE_PORT_ID_PRIMARY_TDM_TX_1: + case AFE_PORT_ID_PRIMARY_TDM_RX_2: + case AFE_PORT_ID_PRIMARY_TDM_TX_2: + case AFE_PORT_ID_PRIMARY_TDM_RX_3: + case AFE_PORT_ID_PRIMARY_TDM_TX_3: + case AFE_PORT_ID_PRIMARY_TDM_RX_4: + case AFE_PORT_ID_PRIMARY_TDM_TX_4: + case AFE_PORT_ID_PRIMARY_TDM_RX_5: + case AFE_PORT_ID_PRIMARY_TDM_TX_5: + case AFE_PORT_ID_PRIMARY_TDM_RX_6: + case AFE_PORT_ID_PRIMARY_TDM_TX_6: + case AFE_PORT_ID_PRIMARY_TDM_RX_7: + case AFE_PORT_ID_PRIMARY_TDM_TX_7: + case AFE_PORT_ID_SECONDARY_TDM_RX: + case AFE_PORT_ID_SECONDARY_TDM_TX: + case AFE_PORT_ID_SECONDARY_TDM_RX_1: + case AFE_PORT_ID_SECONDARY_TDM_TX_1: + case AFE_PORT_ID_SECONDARY_TDM_RX_2: + case AFE_PORT_ID_SECONDARY_TDM_TX_2: + case AFE_PORT_ID_SECONDARY_TDM_RX_3: + case AFE_PORT_ID_SECONDARY_TDM_TX_3: + case AFE_PORT_ID_SECONDARY_TDM_RX_4: + case AFE_PORT_ID_SECONDARY_TDM_TX_4: + case AFE_PORT_ID_SECONDARY_TDM_RX_5: + case AFE_PORT_ID_SECONDARY_TDM_TX_5: + case AFE_PORT_ID_SECONDARY_TDM_RX_6: + case AFE_PORT_ID_SECONDARY_TDM_TX_6: + case AFE_PORT_ID_SECONDARY_TDM_RX_7: + case AFE_PORT_ID_SECONDARY_TDM_TX_7: + case AFE_PORT_ID_TERTIARY_TDM_RX: + case AFE_PORT_ID_TERTIARY_TDM_TX: + case AFE_PORT_ID_TERTIARY_TDM_RX_1: + case AFE_PORT_ID_TERTIARY_TDM_TX_1: + case AFE_PORT_ID_TERTIARY_TDM_RX_2: + case AFE_PORT_ID_TERTIARY_TDM_TX_2: + case AFE_PORT_ID_TERTIARY_TDM_RX_3: + case AFE_PORT_ID_TERTIARY_TDM_TX_3: + case AFE_PORT_ID_TERTIARY_TDM_RX_4: + case AFE_PORT_ID_TERTIARY_TDM_TX_4: + case AFE_PORT_ID_TERTIARY_TDM_RX_5: + case AFE_PORT_ID_TERTIARY_TDM_TX_5: + case AFE_PORT_ID_TERTIARY_TDM_RX_6: + case AFE_PORT_ID_TERTIARY_TDM_TX_6: + case AFE_PORT_ID_TERTIARY_TDM_RX_7: + case AFE_PORT_ID_TERTIARY_TDM_TX_7: + case AFE_PORT_ID_QUATERNARY_TDM_RX: + case AFE_PORT_ID_QUATERNARY_TDM_TX: + case AFE_PORT_ID_QUATERNARY_TDM_RX_1: + case AFE_PORT_ID_QUATERNARY_TDM_TX_1: + case AFE_PORT_ID_QUATERNARY_TDM_RX_2: + case AFE_PORT_ID_QUATERNARY_TDM_TX_2: + case AFE_PORT_ID_QUATERNARY_TDM_RX_3: + case AFE_PORT_ID_QUATERNARY_TDM_TX_3: + case AFE_PORT_ID_QUATERNARY_TDM_RX_4: + case AFE_PORT_ID_QUATERNARY_TDM_TX_4: + case AFE_PORT_ID_QUATERNARY_TDM_RX_5: + case AFE_PORT_ID_QUATERNARY_TDM_TX_5: + case AFE_PORT_ID_QUATERNARY_TDM_RX_6: + case AFE_PORT_ID_QUATERNARY_TDM_TX_6: + case AFE_PORT_ID_QUATERNARY_TDM_RX_7: + case AFE_PORT_ID_QUATERNARY_TDM_TX_7: + case AFE_PORT_ID_QUINARY_TDM_RX: + case AFE_PORT_ID_QUINARY_TDM_TX: + case AFE_PORT_ID_QUINARY_TDM_RX_1: + case AFE_PORT_ID_QUINARY_TDM_TX_1: + case AFE_PORT_ID_QUINARY_TDM_RX_2: + case AFE_PORT_ID_QUINARY_TDM_TX_2: + case AFE_PORT_ID_QUINARY_TDM_RX_3: + case AFE_PORT_ID_QUINARY_TDM_TX_3: + case AFE_PORT_ID_QUINARY_TDM_RX_4: + case AFE_PORT_ID_QUINARY_TDM_TX_4: + case AFE_PORT_ID_QUINARY_TDM_RX_5: + case AFE_PORT_ID_QUINARY_TDM_TX_5: + case AFE_PORT_ID_QUINARY_TDM_RX_6: + case AFE_PORT_ID_QUINARY_TDM_TX_6: + case AFE_PORT_ID_QUINARY_TDM_RX_7: + case AFE_PORT_ID_QUINARY_TDM_TX_7: + case AFE_PORT_ID_SENARY_MI2S_TX: + case AFE_PORT_ID_USB_RX: + case AFE_PORT_ID_USB_TX: + case AFE_PORT_ID_INT0_MI2S_RX: + case AFE_PORT_ID_INT0_MI2S_TX: + case AFE_PORT_ID_INT1_MI2S_RX: + case AFE_PORT_ID_INT1_MI2S_TX: + case AFE_PORT_ID_INT2_MI2S_RX: + case AFE_PORT_ID_INT2_MI2S_TX: + case AFE_PORT_ID_INT3_MI2S_RX: + case AFE_PORT_ID_INT3_MI2S_TX: + case AFE_PORT_ID_INT4_MI2S_RX: + case AFE_PORT_ID_INT4_MI2S_TX: + case AFE_PORT_ID_INT5_MI2S_RX: + 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_WSA_CODEC_DMA_RX_0: + case AFE_PORT_ID_WSA_CODEC_DMA_TX_0: + case AFE_PORT_ID_WSA_CODEC_DMA_RX_1: + case AFE_PORT_ID_WSA_CODEC_DMA_TX_1: + case AFE_PORT_ID_WSA_CODEC_DMA_TX_2: + case AFE_PORT_ID_VA_CODEC_DMA_TX_0: + case AFE_PORT_ID_VA_CODEC_DMA_TX_1: + case AFE_PORT_ID_RX_CODEC_DMA_RX_0: + case AFE_PORT_ID_TX_CODEC_DMA_TX_0: + case AFE_PORT_ID_RX_CODEC_DMA_RX_1: + case AFE_PORT_ID_TX_CODEC_DMA_TX_1: + case AFE_PORT_ID_RX_CODEC_DMA_RX_2: + case AFE_PORT_ID_TX_CODEC_DMA_TX_2: + case AFE_PORT_ID_RX_CODEC_DMA_RX_3: + case AFE_PORT_ID_TX_CODEC_DMA_TX_3: + case AFE_PORT_ID_RX_CODEC_DMA_RX_4: + case AFE_PORT_ID_TX_CODEC_DMA_TX_4: + case AFE_PORT_ID_RX_CODEC_DMA_RX_5: + case AFE_PORT_ID_TX_CODEC_DMA_TX_5: + case AFE_PORT_ID_RX_CODEC_DMA_RX_6: + case AFE_PORT_ID_RX_CODEC_DMA_RX_7: + { + ret = 0; + break; + } + + default: + ret = -EINVAL; + } + + return ret; +} +EXPORT_SYMBOL(q6audio_validate_port); diff --git a/audio-kernel/dsp/q6common.c b/audio-kernel/dsp/q6common.c new file mode 100644 index 000000000..7a186dc56 --- /dev/null +++ b/audio-kernel/dsp/q6common.c @@ -0,0 +1,113 @@ +/* Copyright (c) 2017-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 + +struct q6common_ctl { + bool instance_id_supported; +}; + +static struct q6common_ctl common; + +/** + * q6common_update_instance_id_support + * + * Update instance ID support flag to true/false + * + * @supported: enable/disable for instance ID support + */ +void q6common_update_instance_id_support(bool supported) +{ + common.instance_id_supported = supported; +} +EXPORT_SYMBOL(q6common_update_instance_id_support); + +/** + * q6common_is_instance_id_supported + * + * Returns true/false for instance ID support + */ +bool q6common_is_instance_id_supported(void) +{ + return common.instance_id_supported; +} +EXPORT_SYMBOL(q6common_is_instance_id_supported); + +/** + * q6common_pack_pp_params + * + * Populate params header based on instance ID support and pack + * it with payload. + * Instance ID support - + * yes - param_hdr_v3 + payload + * no - param_hdr_v1 + payload + * + * @dest: destination data pointer to be packed into + * @v3_hdr: param header v3 + * @param_data: param payload + * @total_size: total size of packed data (hdr + payload) + * + * Returns 0 on success or error on failure + */ +int q6common_pack_pp_params(u8 *dest, struct param_hdr_v3 *v3_hdr, + u8 *param_data, u32 *total_size) +{ + struct param_hdr_v1 *v1_hdr = NULL; + u32 packed_size = 0; + u32 param_size = 0; + bool iid_supported = q6common_is_instance_id_supported(); + + if (dest == NULL) { + pr_err("%s: Received NULL pointer for destination\n", __func__); + return -EINVAL; + } else if (v3_hdr == NULL) { + pr_err("%s: Received NULL pointer for header\n", __func__); + return -EINVAL; + } else if (total_size == NULL) { + pr_err("%s: Received NULL pointer for total size\n", __func__); + return -EINVAL; + } + + param_size = v3_hdr->param_size; + packed_size = iid_supported ? sizeof(struct param_hdr_v3) : + sizeof(struct param_hdr_v1); + + if (iid_supported) { + memcpy(dest, v3_hdr, packed_size); + } else { + v1_hdr = (struct param_hdr_v1 *) dest; + v1_hdr->module_id = v3_hdr->module_id; + v1_hdr->param_id = v3_hdr->param_id; + + if (param_size > U16_MAX) { + pr_err("%s: Invalid param size for V1 %d\n", __func__, + param_size); + return -EINVAL; + } + v1_hdr->param_size = param_size; + v1_hdr->reserved = 0; + } + + /* + * Make param_data optional for cases when there is no data + * present as in some set cases and all get cases. + */ + if (param_data != NULL) { + memcpy(dest + packed_size, param_data, param_size); + packed_size += param_size; + } + + *total_size = packed_size; + + return 0; +} +EXPORT_SYMBOL(q6common_pack_pp_params); diff --git a/audio-kernel/dsp/q6core.c b/audio-kernel/dsp/q6core.c new file mode 100644 index 000000000..43bb928a3 --- /dev/null +++ b/audio-kernel/dsp/q6core.c @@ -0,0 +1,1588 @@ +/* Copyright (c) 2012-2019, 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "adsp_err.h" + +#define TIMEOUT_MS 1000 +/* + * AVS bring up in the modem is optimitized for the new + * Sub System Restart design and 100 milliseconds timeout + * is sufficient to make sure the Q6 will be ready. + */ +#define Q6_READY_TIMEOUT_MS 100 + +#define ADSP_STATE_READY_TIMEOUT_MS 3000 + +#define APR_ENOTREADY 10 + +enum { + META_CAL, + CUST_TOP_CAL, + CORE_MAX_CAL +}; + +enum ver_query_status { + VER_QUERY_UNATTEMPTED, + VER_QUERY_UNSUPPORTED, + VER_QUERY_SUPPORTED +}; + +struct q6core_avcs_ver_info { + enum ver_query_status status; + struct avcs_fwk_ver_info *ver_info; +}; + +struct q6core_str { + struct apr_svc *core_handle_q; + wait_queue_head_t bus_bw_req_wait; + wait_queue_head_t cmd_req_wait; + wait_queue_head_t avcs_fwk_ver_req_wait; + u32 bus_bw_resp_received; + enum cmd_flags { + FLAG_NONE, + FLAG_CMDRSP_LICENSE_RESULT + } cmd_resp_received_flag; + u32 avcs_fwk_ver_resp_received; + struct mutex cmd_lock; + struct mutex ver_lock; + union { + struct avcs_cmdrsp_get_license_validation_result + cmdrsp_license_result; + } cmd_resp_payload; + u32 param; + struct cal_type_data *cal_data[CORE_MAX_CAL]; + uint32_t mem_map_cal_handle; + int32_t adsp_status; + int32_t avs_state; + struct q6core_avcs_ver_info q6core_avcs_ver_info; +}; + +static struct q6core_str q6core_lcl; + +struct generic_get_data_ { + int valid; + int size_in_ints; + int ints[]; +}; +static struct generic_get_data_ *generic_get_data; + +static DEFINE_MUTEX(kset_lock); +static struct kset *audio_uevent_kset; + +static int q6core_init_uevent_kset(void) +{ + int ret = 0; + + mutex_lock(&kset_lock); + if (audio_uevent_kset) + goto done; + + /* Create a kset under /sys/kernel/ */ + audio_uevent_kset = kset_create_and_add("q6audio", NULL, kernel_kobj); + if (!audio_uevent_kset) { + pr_err("%s: error creating uevent kernel set", __func__); + ret = -EINVAL; + } +done: + mutex_unlock(&kset_lock); + return ret; +} + +static void q6core_destroy_uevent_kset(void) +{ + if (audio_uevent_kset) { + kset_unregister(audio_uevent_kset); + audio_uevent_kset = NULL; + } +} + +/** + * q6core_init_uevent_data - initialize kernel object required to send uevents. + * + * @uevent_data: uevent data (dynamically allocated memory). + * @name: name of the kernel object. + * + * Returns 0 on success or error otherwise. + */ +int q6core_init_uevent_data(struct audio_uevent_data *uevent_data, char *name) +{ + int ret = -EINVAL; + + if (!uevent_data || !name) + return ret; + + ret = q6core_init_uevent_kset(); + if (ret) + return ret; + + /* Set kset for kobject before initializing the kobject */ + uevent_data->kobj.kset = audio_uevent_kset; + + /* Initialize kobject and add it to kernel */ + ret = kobject_init_and_add(&uevent_data->kobj, &uevent_data->ktype, + NULL, "%s", name); + if (ret) { + pr_err("%s: error initializing uevent kernel object: %d", + __func__, ret); + kobject_put(&uevent_data->kobj); + return ret; + } + + /* Send kobject add event to the system */ + kobject_uevent(&uevent_data->kobj, KOBJ_ADD); + + return ret; +} +EXPORT_SYMBOL(q6core_init_uevent_data); + +/** + * q6core_destroy_uevent_data - destroy kernel object. + * + * @uevent_data: uevent data. + */ +void q6core_destroy_uevent_data(struct audio_uevent_data *uevent_data) +{ + if (uevent_data) + kobject_put(&uevent_data->kobj); +} +EXPORT_SYMBOL(q6core_destroy_uevent_data); + +/** + * q6core_send_uevent - send uevent to userspace. + * + * @uevent_data: uevent data. + * @event: event to send. + * + * Returns 0 on success or error otherwise. + */ +int q6core_send_uevent(struct audio_uevent_data *uevent_data, char *event) +{ + char *env[] = { event, NULL }; + + if (!event || !uevent_data) + return -EINVAL; + + return kobject_uevent_env(&uevent_data->kobj, KOBJ_CHANGE, env); +} +EXPORT_SYMBOL(q6core_send_uevent); + +static int parse_fwk_version_info(uint32_t *payload) +{ + size_t ver_size; + int num_services; + + pr_debug("%s: Payload info num services %d\n", + __func__, payload[4]); + + /* + * payload1[4] is the number of services running on DSP + * Based on this info, we copy the payload into core + * avcs version info structure. + */ + num_services = payload[4]; + if (num_services > VSS_MAX_AVCS_NUM_SERVICES) { + pr_err("%s: num_services: %d greater than max services: %d\n", + __func__, num_services, VSS_MAX_AVCS_NUM_SERVICES); + return -EINVAL; + } + + /* + * Dynamically allocate memory for all + * the services based on num_services + */ + ver_size = sizeof(struct avcs_get_fwk_version) + + num_services * sizeof(struct avs_svc_api_info); + + q6core_lcl.q6core_avcs_ver_info.ver_info = + kzalloc(ver_size, GFP_ATOMIC); + if (q6core_lcl.q6core_avcs_ver_info.ver_info == NULL) + return -ENOMEM; + + memcpy(q6core_lcl.q6core_avcs_ver_info.ver_info, (uint8_t *) payload, + ver_size); + return 0; +} + +static int32_t aprv2_core_fn_q(struct apr_client_data *data, void *priv) +{ + uint32_t *payload1; + int ret = 0; + + if (data == NULL) { + pr_err("%s: data argument is null\n", __func__); + return -EINVAL; + } + + pr_debug("%s: core msg: payload len = %u, apr resp opcode = 0x%x\n", + __func__, + data->payload_size, data->opcode); + + switch (data->opcode) { + + case APR_BASIC_RSP_RESULT:{ + + if (data->payload_size == 0) { + pr_err("%s: APR_BASIC_RSP_RESULT No Payload ", + __func__); + return 0; + } + + payload1 = data->payload; + + switch (payload1[0]) { + + case AVCS_CMD_SHARED_MEM_UNMAP_REGIONS: + pr_debug("%s: Cmd = AVCS_CMD_SHARED_MEM_UNMAP_REGIONS status[0x%x]\n", + __func__, payload1[1]); + /* -ADSP status to match Linux error standard */ + q6core_lcl.adsp_status = -payload1[1]; + q6core_lcl.bus_bw_resp_received = 1; + wake_up(&q6core_lcl.bus_bw_req_wait); + break; + case AVCS_CMD_SHARED_MEM_MAP_REGIONS: + pr_debug("%s: Cmd = AVCS_CMD_SHARED_MEM_MAP_REGIONS status[0x%x]\n", + __func__, payload1[1]); + /* -ADSP status to match Linux error standard */ + q6core_lcl.adsp_status = -payload1[1]; + q6core_lcl.bus_bw_resp_received = 1; + wake_up(&q6core_lcl.bus_bw_req_wait); + break; + case AVCS_CMD_MAP_MDF_SHARED_MEMORY: + pr_debug("%s: Cmd = AVCS_CMD_MAP_MDF_SHARED_MEMORY status[0x%x]\n", + __func__, payload1[1]); + /* -ADSP status to match Linux error standard */ + q6core_lcl.adsp_status = -payload1[1]; + q6core_lcl.bus_bw_resp_received = 1; + wake_up(&q6core_lcl.bus_bw_req_wait); + break; + case AVCS_CMD_REGISTER_TOPOLOGIES: + pr_debug("%s: Cmd = AVCS_CMD_REGISTER_TOPOLOGIES status[0x%x]\n", + __func__, payload1[1]); + /* -ADSP status to match Linux error standard */ + q6core_lcl.adsp_status = -payload1[1]; + q6core_lcl.bus_bw_resp_received = 1; + wake_up(&q6core_lcl.bus_bw_req_wait); + break; + case AVCS_CMD_DEREGISTER_TOPOLOGIES: + pr_debug("%s: Cmd = AVCS_CMD_DEREGISTER_TOPOLOGIES status[0x%x]\n", + __func__, payload1[1]); + q6core_lcl.bus_bw_resp_received = 1; + wake_up(&q6core_lcl.bus_bw_req_wait); + break; + case AVCS_CMD_GET_FWK_VERSION: + pr_debug("%s: Cmd = AVCS_CMD_GET_FWK_VERSION status[%s]\n", + __func__, adsp_err_get_err_str(payload1[1])); + /* ADSP status to match Linux error standard */ + q6core_lcl.adsp_status = -payload1[1]; + if (payload1[1] == ADSP_EUNSUPPORTED) + q6core_lcl.q6core_avcs_ver_info.status = + VER_QUERY_UNSUPPORTED; + q6core_lcl.avcs_fwk_ver_resp_received = 1; + wake_up(&q6core_lcl.avcs_fwk_ver_req_wait); + break; + case AVCS_CMD_LOAD_TOPO_MODULES: + case AVCS_CMD_UNLOAD_TOPO_MODULES: + pr_debug("%s: Cmd = %s status[%s]\n", + __func__, + (payload1[0] == AVCS_CMD_LOAD_TOPO_MODULES) ? + "AVCS_CMD_LOAD_TOPO_MODULES" : + "AVCS_CMD_UNLOAD_TOPO_MODULES", + adsp_err_get_err_str(payload1[1])); + break; + default: + pr_err("%s: Invalid cmd rsp[0x%x][0x%x] opcode %d\n", + __func__, + payload1[0], payload1[1], data->opcode); + break; + } + break; + } + + case RESET_EVENTS:{ + pr_debug("%s: Reset event received in Core service\n", + __func__); + /* + * no reset for q6core_avcs_ver_info done as + * the data will not change after SSR + */ + apr_reset(q6core_lcl.core_handle_q); + q6core_lcl.core_handle_q = NULL; + break; + } + case AVCS_CMDRSP_SHARED_MEM_MAP_REGIONS: + payload1 = data->payload; + pr_debug("%s: AVCS_CMDRSP_SHARED_MEM_MAP_REGIONS handle %d\n", + __func__, payload1[0]); + q6core_lcl.mem_map_cal_handle = payload1[0]; + q6core_lcl.bus_bw_resp_received = 1; + wake_up(&q6core_lcl.bus_bw_req_wait); + break; + case AVCS_CMDRSP_ADSP_EVENT_GET_STATE: + payload1 = data->payload; + q6core_lcl.param = payload1[0]; + pr_debug("%s: Received ADSP get state response 0x%x\n", + __func__, q6core_lcl.param); + /* ensure .param is updated prior to .bus_bw_resp_received */ + wmb(); + q6core_lcl.bus_bw_resp_received = 1; + wake_up(&q6core_lcl.bus_bw_req_wait); + break; + case AVCS_CMDRSP_GET_LICENSE_VALIDATION_RESULT: + payload1 = data->payload; + pr_debug("%s: cmd = LICENSE_VALIDATION_RESULT, result = 0x%x\n", + __func__, payload1[0]); + q6core_lcl.cmd_resp_payload.cmdrsp_license_result.result + = payload1[0]; + q6core_lcl.cmd_resp_received_flag = FLAG_CMDRSP_LICENSE_RESULT; + wake_up(&q6core_lcl.cmd_req_wait); + break; + case AVCS_CMDRSP_GET_FWK_VERSION: + pr_debug("%s: Received AVCS_CMDRSP_GET_FWK_VERSION\n", + __func__); + payload1 = data->payload; + ret = parse_fwk_version_info(payload1); + if (ret < 0) { + q6core_lcl.adsp_status = ret; + pr_err("%s: Failed to parse payload:%d\n", + __func__, ret); + } else { + q6core_lcl.q6core_avcs_ver_info.status = + VER_QUERY_SUPPORTED; + } + q6core_lcl.avcs_fwk_ver_resp_received = 1; + wake_up(&q6core_lcl.avcs_fwk_ver_req_wait); + break; + default: + pr_err("%s: Message id from adsp core svc: 0x%x\n", + __func__, data->opcode); + if (generic_get_data) { + generic_get_data->valid = 1; + generic_get_data->size_in_ints = + data->payload_size/sizeof(int); + pr_debug("callback size = %i\n", + data->payload_size); + memcpy(generic_get_data->ints, data->payload, + data->payload_size); + q6core_lcl.bus_bw_resp_received = 1; + wake_up(&q6core_lcl.bus_bw_req_wait); + break; + } + break; + } + + return 0; +} + +void ocm_core_open(void) +{ + if (q6core_lcl.core_handle_q == NULL) + q6core_lcl.core_handle_q = apr_register("ADSP", "CORE", + aprv2_core_fn_q, 0xFFFFFFFF, NULL); + pr_debug("%s: Open_q %pK\n", __func__, q6core_lcl.core_handle_q); + if (q6core_lcl.core_handle_q == NULL) + pr_err_ratelimited("%s: Unable to register CORE\n", __func__); +} + +struct cal_block_data *cal_utils_get_cal_block_by_key( + struct cal_type_data *cal_type, uint32_t key) +{ + struct list_head *ptr, *next; + struct cal_block_data *cal_block = NULL; + struct audio_cal_info_metainfo *metainfo; + + list_for_each_safe(ptr, next, + &cal_type->cal_blocks) { + + cal_block = list_entry(ptr, + struct cal_block_data, list); + metainfo = (struct audio_cal_info_metainfo *) + cal_block->cal_info; + if (metainfo->nKey != key) { + pr_debug("%s: metainfo key mismatch!!! found:%x, needed:%x\n", + __func__, metainfo->nKey, key); + } else { + pr_debug("%s: metainfo key match found", __func__); + return cal_block; + } + } + return NULL; +} + +static int q6core_send_get_avcs_fwk_ver_cmd(void) +{ + struct apr_hdr avcs_ver_cmd; + int ret; + + avcs_ver_cmd.hdr_field = + APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + avcs_ver_cmd.pkt_size = sizeof(struct apr_hdr); + avcs_ver_cmd.src_port = 0; + avcs_ver_cmd.dest_port = 0; + avcs_ver_cmd.token = 0; + avcs_ver_cmd.opcode = AVCS_CMD_GET_FWK_VERSION; + + q6core_lcl.adsp_status = 0; + q6core_lcl.avcs_fwk_ver_resp_received = 0; + + ret = apr_send_pkt(q6core_lcl.core_handle_q, + (uint32_t *) &avcs_ver_cmd); + if (ret < 0) { + pr_err("%s: failed to send apr packet, ret=%d\n", __func__, + ret); + goto done; + } + + ret = wait_event_timeout(q6core_lcl.avcs_fwk_ver_req_wait, + (q6core_lcl.avcs_fwk_ver_resp_received == 1), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout for AVCS fwk version info\n", + __func__); + ret = -ETIMEDOUT; + goto done; + } + + if (q6core_lcl.adsp_status < 0) { + /* + * adsp_err_get_err_str expects a positive value but we store + * the DSP error as negative to match the Linux error standard. + * Pass in the negated value so adsp_err_get_err_str returns + * the correct string. + */ + pr_err("%s: DSP returned error[%s]\n", __func__, + adsp_err_get_err_str(-q6core_lcl.adsp_status)); + ret = adsp_err_get_lnx_err_code(q6core_lcl.adsp_status); + goto done; + } + + ret = 0; + +done: + return ret; +} + +int q6core_get_service_version(uint32_t service_id, + struct avcs_fwk_ver_info *ver_info, + size_t size) +{ + struct avcs_fwk_ver_info *cached_ver_info = NULL; + int i; + uint32_t num_services; + size_t ver_size; + int ret; + + if (ver_info == NULL) { + pr_err("%s: ver_info is NULL\n", __func__); + return -EINVAL; + } + + ret = q6core_get_fwk_version_size(service_id); + if (ret < 0) { + pr_err("%s: Failed to get service size for service id %d with error %d\n", + __func__, service_id, ret); + return ret; + } + + ver_size = ret; + if (ver_size != size) { + pr_err("%s: Expected size %zu and provided size %zu do not match\n", + __func__, ver_size, size); + return -EINVAL; + } + + cached_ver_info = q6core_lcl.q6core_avcs_ver_info.ver_info; + num_services = cached_ver_info->avcs_fwk_version.num_services; + + if (service_id == AVCS_SERVICE_ID_ALL) { + memcpy(ver_info, cached_ver_info, ver_size); + return 0; + } + + ver_info->avcs_fwk_version = cached_ver_info->avcs_fwk_version; + for (i = 0; i < num_services; i++) { + if (cached_ver_info->services[i].service_id == service_id) { + ver_info->services[0] = cached_ver_info->services[i]; + return 0; + } + } + pr_err("%s: No service matching service ID %d\n", __func__, service_id); + return -EINVAL; +} +EXPORT_SYMBOL(q6core_get_service_version); + +static int q6core_get_avcs_fwk_version(void) +{ + int ret = 0; + + mutex_lock(&(q6core_lcl.ver_lock)); + pr_debug("%s: q6core_avcs_ver_info.status(%d)\n", __func__, + q6core_lcl.q6core_avcs_ver_info.status); + + switch (q6core_lcl.q6core_avcs_ver_info.status) { + case VER_QUERY_SUPPORTED: + pr_debug("%s: AVCS FWK version query already attempted\n", + __func__); + break; + case VER_QUERY_UNSUPPORTED: + ret = -EOPNOTSUPP; + break; + case VER_QUERY_UNATTEMPTED: + pr_debug("%s: Attempting AVCS FWK version query\n", __func__); + if (q6core_is_adsp_ready()) { + ret = q6core_send_get_avcs_fwk_ver_cmd(); + } else { + pr_err("%s: ADSP is not ready to query version\n", + __func__); + ret = -ENODEV; + } + break; + default: + pr_err("%s: Invalid version query status %d\n", __func__, + q6core_lcl.q6core_avcs_ver_info.status); + ret = -EINVAL; + break; + } + mutex_unlock(&(q6core_lcl.ver_lock)); + return ret; +} + +size_t q6core_get_fwk_version_size(uint32_t service_id) +{ + int ret = 0; + uint32_t num_services; + + ret = q6core_get_avcs_fwk_version(); + if (ret) + goto done; + + if (q6core_lcl.q6core_avcs_ver_info.ver_info != NULL) { + num_services = q6core_lcl.q6core_avcs_ver_info.ver_info + ->avcs_fwk_version.num_services; + } else { + pr_err("%s: ver_info is NULL\n", __func__); + ret = -EINVAL; + goto done; + } + + ret = sizeof(struct avcs_get_fwk_version); + if (service_id == AVCS_SERVICE_ID_ALL) + ret += num_services * sizeof(struct avs_svc_api_info); + else + ret += sizeof(struct avs_svc_api_info); +done: + return ret; +} +EXPORT_SYMBOL(q6core_get_fwk_version_size); + +/** + * q6core_get_avcs_version_per_service - + * to get api version of a particular service + * + * @service_id: id of the service + * + * Returns valid version on success or error (negative value) on failure + */ +int q6core_get_avcs_api_version_per_service(uint32_t service_id) +{ + struct avcs_fwk_ver_info *cached_ver_info = NULL; + int i; + uint32_t num_services; + int ret = 0; + + if (service_id == AVCS_SERVICE_ID_ALL) + return -EINVAL; + + ret = q6core_get_avcs_fwk_version(); + if (ret < 0) { + pr_err("%s: failure in getting AVCS version\n", __func__); + return ret; + } + + cached_ver_info = q6core_lcl.q6core_avcs_ver_info.ver_info; + num_services = cached_ver_info->avcs_fwk_version.num_services; + + for (i = 0; i < num_services; i++) { + if (cached_ver_info->services[i].service_id == service_id) + return cached_ver_info->services[i].api_version; + } + pr_err("%s: No service matching service ID %d\n", __func__, service_id); + return -EINVAL; +} +EXPORT_SYMBOL(q6core_get_avcs_api_version_per_service); + +/** + * core_set_license - + * command to set license for module + * + * @key: license key hash + * @module_id: DSP Module ID + * + * Returns 0 on success or error on failure + */ +int32_t core_set_license(uint32_t key, uint32_t module_id) +{ + struct avcs_cmd_set_license *cmd_setl = NULL; + struct cal_block_data *cal_block = NULL; + int rc = 0, packet_size = 0; + + pr_debug("%s: key:0x%x, id:0x%x\n", __func__, key, module_id); + + mutex_lock(&(q6core_lcl.cmd_lock)); + if (q6core_lcl.cal_data[META_CAL] == NULL) { + pr_err("%s: cal_data not initialized yet!!\n", __func__); + rc = -EINVAL; + goto cmd_unlock; + } + + mutex_lock(&((q6core_lcl.cal_data[META_CAL])->lock)); + cal_block = cal_utils_get_cal_block_by_key( + q6core_lcl.cal_data[META_CAL], key); + if (cal_block == NULL || + cal_block->cal_data.kvaddr == NULL || + cal_block->cal_data.size <= 0) { + pr_err("%s: Invalid cal block to send", __func__); + rc = -EINVAL; + goto cal_data_unlock; + } + + packet_size = sizeof(struct avcs_cmd_set_license) + + cal_block->cal_data.size; + /*round up total packet_size to next 4 byte boundary*/ + packet_size = ((packet_size + 0x3)>>2)<<2; + + cmd_setl = kzalloc(packet_size, GFP_KERNEL); + if (cmd_setl == NULL) { + rc = -ENOMEM; + goto cal_data_unlock; + } + + ocm_core_open(); + if (q6core_lcl.core_handle_q == NULL) { + pr_err("%s: apr registration for CORE failed\n", __func__); + rc = -ENODEV; + goto fail_cmd; + } + + cmd_setl->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_EVENT, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + cmd_setl->hdr.pkt_size = packet_size; + cmd_setl->hdr.src_port = 0; + cmd_setl->hdr.dest_port = 0; + cmd_setl->hdr.token = 0; + cmd_setl->hdr.opcode = AVCS_CMD_SET_LICENSE; + cmd_setl->id = module_id; + cmd_setl->overwrite = 1; + cmd_setl->size = cal_block->cal_data.size; + memcpy((uint8_t *)cmd_setl + sizeof(struct avcs_cmd_set_license), + cal_block->cal_data.kvaddr, + cal_block->cal_data.size); + pr_info("%s: Set license opcode=0x%x, id =0x%x, size = %d\n", + __func__, cmd_setl->hdr.opcode, + cmd_setl->id, cmd_setl->size); + rc = apr_send_pkt(q6core_lcl.core_handle_q, (uint32_t *)cmd_setl); + if (rc < 0) + pr_err("%s: SET_LICENSE failed op[0x%x]rc[%d]\n", + __func__, cmd_setl->hdr.opcode, rc); + +fail_cmd: + kfree(cmd_setl); +cal_data_unlock: + mutex_unlock(&((q6core_lcl.cal_data[META_CAL])->lock)); +cmd_unlock: + mutex_unlock(&(q6core_lcl.cmd_lock)); + + return rc; +} +EXPORT_SYMBOL(core_set_license); + +/** + * core_get_license_status - + * command to retrieve license status for module + * + * @module_id: DSP Module ID + * + * Returns 0 on success or error on failure + */ +int32_t core_get_license_status(uint32_t module_id) +{ + struct avcs_cmd_get_license_validation_result get_lvr_cmd; + int ret = 0; + + pr_debug("%s: module_id 0x%x", __func__, module_id); + + mutex_lock(&(q6core_lcl.cmd_lock)); + ocm_core_open(); + if (q6core_lcl.core_handle_q == NULL) { + pr_err("%s: apr registration for CORE failed\n", __func__); + ret = -ENODEV; + goto fail_cmd; + } + + get_lvr_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + get_lvr_cmd.hdr.pkt_size = + sizeof(struct avcs_cmd_get_license_validation_result); + + get_lvr_cmd.hdr.src_port = 0; + get_lvr_cmd.hdr.dest_port = 0; + get_lvr_cmd.hdr.token = 0; + get_lvr_cmd.hdr.opcode = AVCS_CMD_GET_LICENSE_VALIDATION_RESULT; + get_lvr_cmd.id = module_id; + + + ret = apr_send_pkt(q6core_lcl.core_handle_q, (uint32_t *) &get_lvr_cmd); + if (ret < 0) { + pr_err("%s: license_validation request failed, err %d\n", + __func__, ret); + ret = -EREMOTE; + goto fail_cmd; + } + + q6core_lcl.cmd_resp_received_flag &= ~(FLAG_CMDRSP_LICENSE_RESULT); + mutex_unlock(&(q6core_lcl.cmd_lock)); + ret = wait_event_timeout(q6core_lcl.cmd_req_wait, + (q6core_lcl.cmd_resp_received_flag == + FLAG_CMDRSP_LICENSE_RESULT), + msecs_to_jiffies(TIMEOUT_MS)); + mutex_lock(&(q6core_lcl.cmd_lock)); + if (!ret) { + pr_err("%s: wait_event timeout for CMDRSP_LICENSE_RESULT\n", + __func__); + ret = -ETIME; + goto fail_cmd; + } + q6core_lcl.cmd_resp_received_flag &= ~(FLAG_CMDRSP_LICENSE_RESULT); + ret = q6core_lcl.cmd_resp_payload.cmdrsp_license_result.result; + +fail_cmd: + mutex_unlock(&(q6core_lcl.cmd_lock)); + pr_info("%s: cmdrsp_license_result.result = 0x%x for module 0x%x\n", + __func__, ret, module_id); + return ret; +} +EXPORT_SYMBOL(core_get_license_status); + +/** + * core_set_dolby_manufacturer_id - + * command to set dolby manufacturer id + * + * @manufacturer_id: Dolby manufacturer id + * + * Returns 0 on success or error on failure + */ +uint32_t core_set_dolby_manufacturer_id(int manufacturer_id) +{ + struct adsp_dolby_manufacturer_id payload; + int rc = 0; + + pr_debug("%s: manufacturer_id :%d\n", __func__, manufacturer_id); + mutex_lock(&(q6core_lcl.cmd_lock)); + ocm_core_open(); + if (q6core_lcl.core_handle_q) { + payload.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_EVENT, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + payload.hdr.pkt_size = + sizeof(struct adsp_dolby_manufacturer_id); + payload.hdr.src_port = 0; + payload.hdr.dest_port = 0; + payload.hdr.token = 0; + payload.hdr.opcode = ADSP_CMD_SET_DOLBY_MANUFACTURER_ID; + payload.manufacturer_id = manufacturer_id; + pr_debug("%s: Send Dolby security opcode=0x%x manufacturer ID = %d\n", + __func__, + payload.hdr.opcode, payload.manufacturer_id); + rc = apr_send_pkt(q6core_lcl.core_handle_q, + (uint32_t *)&payload); + if (rc < 0) + pr_err("%s: SET_DOLBY_MANUFACTURER_ID failed op[0x%x]rc[%d]\n", + __func__, payload.hdr.opcode, rc); + } + mutex_unlock(&(q6core_lcl.cmd_lock)); + return rc; +} +EXPORT_SYMBOL(core_set_dolby_manufacturer_id); + +int32_t q6core_load_unload_topo_modules(uint32_t topo_id, + bool preload_type) +{ + struct avcs_cmd_load_unload_topo_modules load_unload_topo_modules; + int ret = 0; + + mutex_lock(&(q6core_lcl.cmd_lock)); + ocm_core_open(); + if (q6core_lcl.core_handle_q == NULL) { + pr_err("%s: apr registration for CORE failed\n", __func__); + ret = -ENODEV; + goto done; + } + + memset(&load_unload_topo_modules, 0, sizeof(load_unload_topo_modules)); + load_unload_topo_modules.hdr.hdr_field = + APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + load_unload_topo_modules.hdr.pkt_size = + sizeof(struct avcs_cmd_load_unload_topo_modules); + load_unload_topo_modules.hdr.src_port = 0; + load_unload_topo_modules.hdr.dest_port = 0; + load_unload_topo_modules.hdr.token = 0; + + if (preload_type == CORE_LOAD_TOPOLOGY) + load_unload_topo_modules.hdr.opcode = + AVCS_CMD_LOAD_TOPO_MODULES; + else + load_unload_topo_modules.hdr.opcode = + AVCS_CMD_UNLOAD_TOPO_MODULES; + + load_unload_topo_modules.topology_id = topo_id; + ret = apr_send_pkt(q6core_lcl.core_handle_q, + (uint32_t *) &load_unload_topo_modules); + if (ret < 0) { + pr_err("%s: Load/unload topo modules failed for topology = %d ret = %d\n", + __func__, topo_id, ret); + ret = -EINVAL; + } + +done: + mutex_unlock(&(q6core_lcl.cmd_lock)); + + return ret; +} +EXPORT_SYMBOL(q6core_load_unload_topo_modules); + +/** + * q6core_is_adsp_ready - check adsp ready status + * + * Returns true if adsp is ready otherwise returns false + */ +bool q6core_is_adsp_ready(void) +{ + int rc = 0; + bool ret = false; + struct apr_hdr hdr; + + pr_debug("%s: enter\n", __func__); + memset(&hdr, 0, sizeof(hdr)); + hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, 0); + hdr.opcode = AVCS_CMD_ADSP_EVENT_GET_STATE; + + mutex_lock(&(q6core_lcl.cmd_lock)); + ocm_core_open(); + if (q6core_lcl.core_handle_q) { + q6core_lcl.bus_bw_resp_received = 0; + rc = apr_send_pkt(q6core_lcl.core_handle_q, (uint32_t *)&hdr); + if (rc < 0) { + pr_err_ratelimited("%s: Get ADSP state APR packet send event %d\n", + __func__, rc); + goto bail; + } + + rc = wait_event_timeout(q6core_lcl.bus_bw_req_wait, + (q6core_lcl.bus_bw_resp_received == 1), + msecs_to_jiffies(Q6_READY_TIMEOUT_MS)); + if (rc > 0 && q6core_lcl.bus_bw_resp_received) { + /* ensure to read updated param by callback thread */ + rmb(); + ret = !!q6core_lcl.param; + } + } +bail: + pr_debug("%s: leave, rc %d, adsp ready %d\n", __func__, rc, ret); + mutex_unlock(&(q6core_lcl.cmd_lock)); + return ret; +} +EXPORT_SYMBOL(q6core_is_adsp_ready); + +int q6core_map_memory_regions(phys_addr_t *buf_add, uint32_t mempool_id, + uint32_t *bufsz, uint32_t bufcnt, uint32_t *map_handle) +{ + struct avs_cmd_shared_mem_map_regions *mmap_regions = NULL; + struct avs_shared_map_region_payload *mregions = NULL; + void *mmap_region_cmd = NULL; + void *payload = NULL; + int ret = 0; + int i = 0; + int cmd_size = 0; + + cmd_size = sizeof(struct avs_cmd_shared_mem_map_regions) + + sizeof(struct avs_shared_map_region_payload) + * bufcnt; + + mmap_region_cmd = kzalloc(cmd_size, GFP_KERNEL); + if (mmap_region_cmd == NULL) + return -ENOMEM; + + mmap_regions = (struct avs_cmd_shared_mem_map_regions *)mmap_region_cmd; + mmap_regions->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + mmap_regions->hdr.pkt_size = cmd_size; + mmap_regions->hdr.src_port = 0; + mmap_regions->hdr.dest_port = 0; + mmap_regions->hdr.token = 0; + mmap_regions->hdr.opcode = AVCS_CMD_SHARED_MEM_MAP_REGIONS; + mmap_regions->mem_pool_id = mempool_id & 0x00ff; + mmap_regions->num_regions = bufcnt & 0x00ff; + mmap_regions->property_flag = 0x00; + + payload = ((u8 *) mmap_region_cmd + + sizeof(struct avs_cmd_shared_mem_map_regions)); + mregions = (struct avs_shared_map_region_payload *)payload; + + for (i = 0; i < bufcnt; i++) { + mregions->shm_addr_lsw = lower_32_bits(buf_add[i]); + mregions->shm_addr_msw = + msm_audio_populate_upper_32_bits(buf_add[i]); + mregions->mem_size_bytes = bufsz[i]; + ++mregions; + } + + pr_debug("%s: sending memory map, addr %pK, size %d, bufcnt = %d\n", + __func__, buf_add, bufsz[0], mmap_regions->num_regions); + + *map_handle = 0; + q6core_lcl.adsp_status = 0; + q6core_lcl.bus_bw_resp_received = 0; + ret = apr_send_pkt(q6core_lcl.core_handle_q, (uint32_t *) + mmap_regions); + if (ret < 0) { + pr_err("%s: mmap regions failed %d\n", + __func__, ret); + ret = -EINVAL; + goto done; + } + + ret = wait_event_timeout(q6core_lcl.bus_bw_req_wait, + (q6core_lcl.bus_bw_resp_received == 1), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: timeout. waited for memory map\n", __func__); + ret = -ETIME; + goto done; + } else { + /* set ret to 0 as no timeout happened */ + ret = 0; + } + + if (q6core_lcl.adsp_status < 0) { + pr_err("%s: DSP returned error %d\n", + __func__, q6core_lcl.adsp_status); + ret = q6core_lcl.adsp_status; + goto done; + } + + *map_handle = q6core_lcl.mem_map_cal_handle; +done: + kfree(mmap_region_cmd); + return ret; +} + +int q6core_memory_unmap_regions(uint32_t mem_map_handle) +{ + struct avs_cmd_shared_mem_unmap_regions unmap_regions; + int ret = 0; + + memset(&unmap_regions, 0, sizeof(unmap_regions)); + unmap_regions.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + unmap_regions.hdr.pkt_size = sizeof(unmap_regions); + unmap_regions.hdr.src_svc = APR_SVC_ADSP_CORE; + unmap_regions.hdr.src_domain = APR_DOMAIN_APPS; + unmap_regions.hdr.src_port = 0; + unmap_regions.hdr.dest_svc = APR_SVC_ADSP_CORE; + unmap_regions.hdr.dest_domain = APR_DOMAIN_ADSP; + unmap_regions.hdr.dest_port = 0; + unmap_regions.hdr.token = 0; + unmap_regions.hdr.opcode = AVCS_CMD_SHARED_MEM_UNMAP_REGIONS; + unmap_regions.mem_map_handle = mem_map_handle; + + q6core_lcl.adsp_status = 0; + q6core_lcl.bus_bw_resp_received = 0; + + pr_debug("%s: unmap regions map handle %d\n", + __func__, mem_map_handle); + + ret = apr_send_pkt(q6core_lcl.core_handle_q, (uint32_t *) + &unmap_regions); + if (ret < 0) { + pr_err("%s: unmap regions failed %d\n", + __func__, ret); + ret = -EINVAL; + goto done; + } + + ret = wait_event_timeout(q6core_lcl.bus_bw_req_wait, + (q6core_lcl.bus_bw_resp_received == 1), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: timeout. waited for memory_unmap\n", + __func__); + ret = -ETIME; + goto done; + } else { + /* set ret to 0 as no timeout happened */ + ret = 0; + } + if (q6core_lcl.adsp_status < 0) { + pr_err("%s: DSP returned error %d\n", + __func__, q6core_lcl.adsp_status); + ret = q6core_lcl.adsp_status; + goto done; + } +done: + return ret; +} + + +int q6core_map_mdf_shared_memory(uint32_t map_handle, phys_addr_t *buf_add, + uint32_t proc_id, uint32_t *bufsz, uint32_t bufcnt) +{ + struct avs_cmd_map_mdf_shared_memory *mmap_regions = NULL; + struct avs_shared_map_region_payload *mregions = NULL; + void *mmap_region_cmd = NULL; + void *payload = NULL; + int ret = 0; + int i = 0; + int cmd_size = 0; + + cmd_size = sizeof(struct avs_cmd_map_mdf_shared_memory) + + sizeof(struct avs_shared_map_region_payload) + * bufcnt; + + mmap_region_cmd = kzalloc(cmd_size, GFP_KERNEL); + if (mmap_region_cmd == NULL) + return -ENOMEM; + + mmap_regions = (struct avs_cmd_map_mdf_shared_memory *)mmap_region_cmd; + mmap_regions->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + mmap_regions->hdr.pkt_size = cmd_size; + mmap_regions->hdr.src_port = 0; + mmap_regions->hdr.dest_port = 0; + mmap_regions->hdr.token = 0; + mmap_regions->hdr.opcode = AVCS_CMD_MAP_MDF_SHARED_MEMORY; + mmap_regions->mem_map_handle = map_handle; + mmap_regions->proc_id = proc_id & 0x00ff; + mmap_regions->num_regions = bufcnt & 0x00ff; + + payload = ((u8 *) mmap_region_cmd + + sizeof(struct avs_cmd_map_mdf_shared_memory)); + mregions = (struct avs_shared_map_region_payload *)payload; + + for (i = 0; i < bufcnt; i++) { + mregions->shm_addr_lsw = lower_32_bits(buf_add[i]); + mregions->shm_addr_msw = + msm_audio_populate_upper_32_bits(buf_add[i]); + mregions->mem_size_bytes = bufsz[i]; + ++mregions; + } + + pr_debug("%s: sending mdf memory map, addr %pa, size %d, bufcnt = %d\n", + __func__, buf_add, bufsz[0], mmap_regions->num_regions); + + q6core_lcl.adsp_status = 0; + q6core_lcl.bus_bw_resp_received = 0; + ret = apr_send_pkt(q6core_lcl.core_handle_q, (uint32_t *) + mmap_regions); + if (ret < 0) { + pr_err("%s: mdf memory map failed %d\n", + __func__, ret); + ret = -EINVAL; + goto done; + } + + ret = wait_event_timeout(q6core_lcl.bus_bw_req_wait, + (q6core_lcl.bus_bw_resp_received == 1), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: timeout. waited for mdf memory map\n", + __func__); + ret = -ETIME; + goto done; + } else { + /* set ret to 0 as no timeout happened */ + ret = 0; + } + + /* + * When the remote DSP is not ready, the ADSP will validate and store + * the memory information and return APR_ENOTREADY to HLOS. The ADSP + * will map the memory with remote DSP when it is ready. HLOS should + * not treat APR_ENOTREADY as an error. + */ + if (q6core_lcl.adsp_status != -APR_ENOTREADY) { + pr_err("%s: DSP returned error %d\n", + __func__, q6core_lcl.adsp_status); + ret = q6core_lcl.adsp_status; + goto done; + } + +done: + kfree(mmap_region_cmd); + return ret; +} + +static int q6core_dereg_all_custom_topologies(void) +{ + int ret = 0; + struct avcs_cmd_deregister_topologies dereg_top; + + memset(&dereg_top, 0, sizeof(dereg_top)); + dereg_top.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + dereg_top.hdr.pkt_size = sizeof(dereg_top); + dereg_top.hdr.src_svc = APR_SVC_ADSP_CORE; + dereg_top.hdr.src_domain = APR_DOMAIN_APPS; + dereg_top.hdr.src_port = 0; + dereg_top.hdr.dest_svc = APR_SVC_ADSP_CORE; + dereg_top.hdr.dest_domain = APR_DOMAIN_ADSP; + dereg_top.hdr.dest_port = 0; + dereg_top.hdr.token = 0; + dereg_top.hdr.opcode = AVCS_CMD_DEREGISTER_TOPOLOGIES; + dereg_top.payload_addr_lsw = 0; + dereg_top.payload_addr_msw = 0; + dereg_top.mem_map_handle = 0; + dereg_top.payload_size = 0; + dereg_top.mode = AVCS_MODE_DEREGISTER_ALL_CUSTOM_TOPOLOGIES; + + q6core_lcl.bus_bw_resp_received = 0; + + pr_debug("%s: Deregister topologies mode %d\n", + __func__, dereg_top.mode); + + ret = apr_send_pkt(q6core_lcl.core_handle_q, (uint32_t *) &dereg_top); + if (ret < 0) { + pr_err("%s: Deregister topologies failed %d\n", + __func__, ret); + goto done; + } + + ret = wait_event_timeout(q6core_lcl.bus_bw_req_wait, + (q6core_lcl.bus_bw_resp_received == 1), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout for Deregister topologies\n", + __func__); + goto done; + } +done: + return ret; +} + +static int q6core_send_custom_topologies(void) +{ + int ret = 0; + int ret2 = 0; + struct cal_block_data *cal_block = NULL; + struct avcs_cmd_register_topologies reg_top; + + if (!q6core_is_adsp_ready()) { + pr_err("%s: ADSP is not ready!\n", __func__); + return -ENODEV; + } + + memset(®_top, 0, sizeof(reg_top)); + mutex_lock(&q6core_lcl.cal_data[CUST_TOP_CAL]->lock); + mutex_lock(&q6core_lcl.cmd_lock); + + cal_block = cal_utils_get_only_cal_block( + q6core_lcl.cal_data[CUST_TOP_CAL]); + if (cal_block == NULL) { + pr_debug("%s: cal block is NULL!\n", __func__); + goto unlock; + } + if (cal_block->cal_data.size <= 0) { + pr_debug("%s: cal size is %zd not sending\n", + __func__, cal_block->cal_data.size); + goto unlock; + } + + q6core_dereg_all_custom_topologies(); + + ret = q6core_map_memory_regions(&cal_block->cal_data.paddr, + ADSP_MEMORY_MAP_SHMEM8_4K_POOL, + (uint32_t *)&cal_block->map_data.map_size, 1, + &cal_block->map_data.q6map_handle); + if (ret) { + pr_err("%s: q6core_map_memory_regions failed\n", __func__); + goto unlock; + } + + reg_top.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + reg_top.hdr.pkt_size = sizeof(reg_top); + reg_top.hdr.src_svc = APR_SVC_ADSP_CORE; + reg_top.hdr.src_domain = APR_DOMAIN_APPS; + reg_top.hdr.src_port = 0; + reg_top.hdr.dest_svc = APR_SVC_ADSP_CORE; + reg_top.hdr.dest_domain = APR_DOMAIN_ADSP; + reg_top.hdr.dest_port = 0; + reg_top.hdr.token = 0; + reg_top.hdr.opcode = AVCS_CMD_REGISTER_TOPOLOGIES; + reg_top.payload_addr_lsw = + lower_32_bits(cal_block->cal_data.paddr); + reg_top.payload_addr_msw = + msm_audio_populate_upper_32_bits(cal_block->cal_data.paddr); + reg_top.mem_map_handle = cal_block->map_data.q6map_handle; + reg_top.payload_size = cal_block->cal_data.size; + + q6core_lcl.adsp_status = 0; + q6core_lcl.bus_bw_resp_received = 0; + + pr_debug("%s: Register topologies addr %pK, size %zd, map handle %d\n", + __func__, &cal_block->cal_data.paddr, cal_block->cal_data.size, + cal_block->map_data.q6map_handle); + + ret = apr_send_pkt(q6core_lcl.core_handle_q, (uint32_t *) ®_top); + if (ret < 0) { + pr_err("%s: Register topologies failed %d\n", + __func__, ret); + goto unmap; + } + + ret = wait_event_timeout(q6core_lcl.bus_bw_req_wait, + (q6core_lcl.bus_bw_resp_received == 1), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout for Register topologies\n", + __func__); + goto unmap; + } + + if (q6core_lcl.adsp_status < 0) + ret = q6core_lcl.adsp_status; +unmap: + ret2 = q6core_memory_unmap_regions(cal_block->map_data.q6map_handle); + if (ret2) { + pr_err("%s: q6core_memory_unmap_regions failed for map handle %d\n", + __func__, cal_block->map_data.q6map_handle); + ret = ret2; + goto unlock; + } + +unlock: + mutex_unlock(&q6core_lcl.cmd_lock); + mutex_unlock(&q6core_lcl.cal_data[CUST_TOP_CAL]->lock); + + return ret; +} + +static int get_cal_type_index(int32_t cal_type) +{ + int ret = -EINVAL; + + switch (cal_type) { + case AUDIO_CORE_METAINFO_CAL_TYPE: + ret = META_CAL; + break; + case CORE_CUSTOM_TOPOLOGIES_CAL_TYPE: + ret = CUST_TOP_CAL; + break; + default: + pr_err("%s: invalid cal type %d!\n", __func__, cal_type); + } + return ret; +} + +static int q6core_alloc_cal(int32_t cal_type, + size_t data_size, void *data) +{ + int ret = 0; + int cal_index; + + cal_index = get_cal_type_index(cal_type); + if (cal_index < 0) { + pr_err("%s: could not get cal index %d!\n", + __func__, cal_index); + ret = -EINVAL; + goto done; + } + + + ret = cal_utils_alloc_cal(data_size, data, + q6core_lcl.cal_data[cal_index], 0, NULL); + if (ret < 0) { + pr_err("%s: cal_utils_alloc_block failed, ret = %d, cal type = %d!\n", + __func__, ret, cal_type); + goto done; + } +done: + return ret; +} + +static int q6core_dealloc_cal(int32_t cal_type, + size_t data_size, void *data) +{ + int ret = 0; + int cal_index; + + cal_index = get_cal_type_index(cal_type); + if (cal_index < 0) { + pr_err("%s: could not get cal index %d!\n", + __func__, cal_index); + ret = -EINVAL; + goto done; + } + + + ret = cal_utils_dealloc_cal(data_size, data, + q6core_lcl.cal_data[cal_index]); + if (ret < 0) { + pr_err("%s: cal_utils_dealloc_block failed, ret = %d, cal type = %d!\n", + __func__, ret, cal_type); + goto done; + } +done: + return ret; +} + +static int q6core_set_cal(int32_t cal_type, + size_t data_size, void *data) +{ + int ret = 0; + int cal_index; + + cal_index = get_cal_type_index(cal_type); + if (cal_index < 0) { + pr_err("%s: could not get cal index %d!\n", + __func__, cal_index); + ret = -EINVAL; + goto done; + } + + + ret = cal_utils_set_cal(data_size, data, + q6core_lcl.cal_data[cal_index], 0, NULL); + if (ret < 0) { + pr_err("%s: cal_utils_set_cal failed, ret = %d, cal type = %d!\n", + __func__, ret, cal_type); + goto done; + } + + if (cal_index == CUST_TOP_CAL) + ret = q6core_send_custom_topologies(); +done: + return ret; +} + +static void q6core_delete_cal_data(void) +{ + pr_debug("%s:\n", __func__); + + cal_utils_destroy_cal_types(CORE_MAX_CAL, q6core_lcl.cal_data); +} + + +static int q6core_init_cal_data(void) +{ + int ret = 0; + struct cal_type_info cal_type_info[] = { + {{AUDIO_CORE_METAINFO_CAL_TYPE, + {q6core_alloc_cal, q6core_dealloc_cal, NULL, + q6core_set_cal, NULL, NULL} }, + {NULL, NULL, cal_utils_match_buf_num} }, + + {{CORE_CUSTOM_TOPOLOGIES_CAL_TYPE, + {q6core_alloc_cal, q6core_dealloc_cal, NULL, + q6core_set_cal, NULL, NULL} }, + {NULL, NULL, cal_utils_match_buf_num} } + }; + pr_debug("%s:\n", __func__); + + ret = cal_utils_create_cal_types(CORE_MAX_CAL, + q6core_lcl.cal_data, cal_type_info); + if (ret < 0) { + pr_err("%s: could not create cal type!\n", + __func__); + goto err; + } + + return ret; +err: + q6core_delete_cal_data(); + return ret; +} + +int q6core_is_avs_up(int32_t *avs_state) +{ + unsigned long timeout; + int32_t adsp_ready = 0; + int ret = 0; + + timeout = jiffies + + msecs_to_jiffies(ADSP_STATE_READY_TIMEOUT_MS); + + /* sleep for 100ms before querying AVS up */ + msleep(100); + do { + adsp_ready = q6core_is_adsp_ready(); + pr_debug("%s: ADSP Audio is %s\n", __func__, + adsp_ready ? "ready" : "not ready"); + if (adsp_ready) + break; + + /* + * ADSP will be coming up after boot up and AVS might + * not be fully up when the control reaches here. + * So, wait for 50msec before checking ADSP state again. + */ + msleep(50); + } while (time_after(timeout, jiffies)); + + *avs_state = adsp_ready; + pr_debug("%s: ADSP Audio is %s\n", __func__, + adsp_ready ? "ready" : "not ready"); + + if (!adsp_ready) { + pr_err_ratelimited("%s: Timeout. ADSP Audio is not ready\n", + __func__); + ret = -ETIMEDOUT; + } + + return ret; +} +EXPORT_SYMBOL(q6core_is_avs_up); + +static int q6core_ssr_enable(struct device *dev, void *data) +{ + int32_t avs_state = 0; + int ret = 0; + + if (!dev) { + pr_err("%s: dev is NULL\n", __func__); + return -EINVAL; + } + + if (!q6core_lcl.avs_state) { + ret = q6core_is_avs_up(&avs_state); + if (ret < 0) + goto err; + q6core_lcl.avs_state = avs_state; + } + +err: + return ret; +} + +static void q6core_ssr_disable(struct device *dev, void *data) +{ + /* Reset AVS state to 0 */ + q6core_lcl.avs_state = 0; +} + +static const struct snd_event_ops q6core_ssr_ops = { + .enable = q6core_ssr_enable, + .disable = q6core_ssr_disable, +}; + +static int q6core_probe(struct platform_device *pdev) +{ + int32_t avs_state = 0; + int rc = 0; + + rc = q6core_is_avs_up(&avs_state); + if (rc < 0) + goto err; + q6core_lcl.avs_state = avs_state; + + rc = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev); + if (rc) { + dev_err(&pdev->dev, "%s: failed to add child nodes, rc=%d\n", + __func__, rc); + rc = -EINVAL; + goto err; + } + dev_dbg(&pdev->dev, "%s: added child node\n", __func__); + + rc = snd_event_client_register(&pdev->dev, &q6core_ssr_ops, NULL); + if (!rc) { + snd_event_notify(&pdev->dev, SND_EVENT_UP); + } else { + dev_err(&pdev->dev, + "%s: Registration with SND event fwk failed rc = %d\n", + __func__, rc); + rc = 0; + } + +err: + return rc; +} + +static int q6core_remove(struct platform_device *pdev) +{ + snd_event_client_deregister(&pdev->dev); + of_platform_depopulate(&pdev->dev); + return 0; +} + +static const struct of_device_id q6core_of_match[] = { + { .compatible = "qcom,q6core-audio", }, + {}, +}; + +static struct platform_driver q6core_driver = { + .probe = q6core_probe, + .remove = q6core_remove, + .driver = { + .name = "q6core_audio", + .owner = THIS_MODULE, + .of_match_table = q6core_of_match, + } +}; + +int __init core_init(void) +{ + memset(&q6core_lcl, 0, sizeof(struct q6core_str)); + init_waitqueue_head(&q6core_lcl.bus_bw_req_wait); + init_waitqueue_head(&q6core_lcl.cmd_req_wait); + init_waitqueue_head(&q6core_lcl.avcs_fwk_ver_req_wait); + q6core_lcl.cmd_resp_received_flag = FLAG_NONE; + mutex_init(&q6core_lcl.cmd_lock); + mutex_init(&q6core_lcl.ver_lock); + + q6core_init_cal_data(); + q6core_init_uevent_kset(); + + return platform_driver_register(&q6core_driver); +} + +void core_exit(void) +{ + mutex_destroy(&q6core_lcl.cmd_lock); + mutex_destroy(&q6core_lcl.ver_lock); + q6core_delete_cal_data(); + q6core_destroy_uevent_kset(); + platform_driver_unregister(&q6core_driver); +} +MODULE_DESCRIPTION("ADSP core driver"); +MODULE_LICENSE("GPL v2"); diff --git a/audio-kernel/dsp/q6lsm.c b/audio-kernel/dsp/q6lsm.c new file mode 100644 index 000000000..93dd941c7 --- /dev/null +++ b/audio-kernel/dsp/q6lsm.c @@ -0,0 +1,2716 @@ +/* + * Copyright (c) 2013-2018, 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "adsp_err.h" + +#define APR_TIMEOUT (HZ) +#define LSM_ALIGN_BOUNDARY 512 +#define LSM_SAMPLE_RATE 16000 +#define QLSM_PARAM_ID_MINOR_VERSION 1 +#define QLSM_PARAM_ID_MINOR_VERSION_2 2 + +static int lsm_afe_port; + +enum { + LSM_CUSTOM_TOP_IDX, + LSM_TOP_IDX, + LSM_CAL_IDX, + LSM_MAX_CAL_IDX +}; + +enum { + CMD_STATE_CLEARED = 0, + CMD_STATE_WAIT_RESP = 1, +}; + +enum { + LSM_INVALID_SESSION_ID = 0, + LSM_MIN_SESSION_ID = 1, + LSM_MAX_SESSION_ID = 8, + LSM_CONTROL_SESSION = 0x0F, +}; + +#define CHECK_SESSION(x) (x < LSM_MIN_SESSION_ID || x > LSM_MAX_SESSION_ID) +struct lsm_common { + void *apr; + atomic_t apr_users; + struct lsm_client common_client[LSM_MAX_SESSION_ID + 1]; + + int set_custom_topology; + struct cal_type_data *cal_data[LSM_MAX_CAL_IDX]; + + struct mutex apr_lock; +}; + +static struct lsm_common lsm_common; +static DEFINE_MUTEX(session_lock); + +/* + * mmap_handle_p can point either client->sound_model.mem_map_handle or + * lsm_common.mmap_handle_for_cal. + * mmap_lock must be held while accessing this. + */ +static spinlock_t mmap_lock; +static uint32_t *mmap_handle_p; + +static spinlock_t lsm_session_lock; +static struct lsm_client *lsm_session[LSM_MAX_SESSION_ID + 1]; + +static int q6lsm_mmapcallback(struct apr_client_data *data, void *priv); +static int q6lsm_send_cal(struct lsm_client *client, + u32 set_params_opcode, struct lsm_params_info_v2 *p_info); +static int q6lsm_memory_map_regions(struct lsm_client *client, + dma_addr_t dma_addr_p, uint32_t dma_buf_sz, + uint32_t *mmap_p); +static int q6lsm_memory_unmap_regions(struct lsm_client *client, + uint32_t handle); + +static int q6lsm_get_session_id_from_lsm_client(struct lsm_client *client) +{ + int n; + + for (n = LSM_MIN_SESSION_ID; n <= LSM_MAX_SESSION_ID; n++) { + if (lsm_session[n] == client) + return n; + } + pr_err("%s: cannot find matching lsm client. client = %pa\n", + __func__, client); + return LSM_INVALID_SESSION_ID; +} + +static bool q6lsm_is_valid_lsm_client(struct lsm_client *client) +{ + return q6lsm_get_session_id_from_lsm_client(client) ? 1 : 0; +} + +static int q6lsm_callback(struct apr_client_data *data, void *priv) +{ + struct lsm_client *client = (struct lsm_client *)priv; + uint32_t token; + uint32_t *payload; + + if (!client || !data) { + pr_err("%s: client %pK data %pK\n", + __func__, client, data); + WARN_ON(1); + return -EINVAL; + } + + if (data->opcode == RESET_EVENTS) { + pr_debug("%s: SSR event received 0x%x, event 0x%x, proc 0x%x\n", + __func__, data->opcode, data->reset_event, + data->reset_proc); + + mutex_lock(&session_lock); + if (!client || !q6lsm_is_valid_lsm_client(client)) { + pr_err("%s: client already freed/invalid, return\n", + __func__); + mutex_unlock(&session_lock); + return 0; + } + apr_reset(client->apr); + client->apr = NULL; + atomic_set(&client->cmd_state, CMD_STATE_CLEARED); + wake_up(&client->cmd_wait); + cal_utils_clear_cal_block_q6maps(LSM_MAX_CAL_IDX, + lsm_common.cal_data); + mutex_lock(&lsm_common.cal_data[LSM_CUSTOM_TOP_IDX]->lock); + lsm_common.set_custom_topology = 1; + mutex_unlock(&lsm_common.cal_data[LSM_CUSTOM_TOP_IDX]->lock); + mutex_unlock(&session_lock); + return 0; + } + + payload = data->payload; + pr_debug("%s: Session %d opcode 0x%x token 0x%x payload size %d\n" + "payload [0] = 0x%x\n", __func__, client->session, + data->opcode, data->token, data->payload_size, payload[0]); + if (data->opcode == LSM_DATA_EVENT_READ_DONE) { + struct lsm_cmd_read_done read_done; + + token = data->token; + if (data->payload_size > sizeof(read_done)) { + pr_err("%s: read done error payload size %d expected size %zd\n", + __func__, data->payload_size, + sizeof(read_done)); + return -EINVAL; + } + pr_debug("%s: opcode %x status %x lsw %x msw %x mem_map handle %x\n", + __func__, data->opcode, payload[0], payload[1], + payload[2], payload[3]); + read_done.status = payload[0]; + read_done.buf_addr_lsw = payload[1]; + read_done.buf_addr_msw = payload[2]; + read_done.mem_map_handle = payload[3]; + read_done.total_size = payload[4]; + read_done.offset = payload[5]; + if (client->cb) + client->cb(data->opcode, data->token, + (void *)&read_done, + client->priv); + return 0; + } else if (data->opcode == APR_BASIC_RSP_RESULT) { + token = data->token; + switch (payload[0]) { + case LSM_SESSION_CMD_START: + case LSM_SESSION_CMD_STOP: + case LSM_SESSION_CMD_SET_PARAMS: + case LSM_SESSION_CMD_OPEN_TX: + case LSM_SESSION_CMD_CLOSE_TX: + case LSM_SESSION_CMD_REGISTER_SOUND_MODEL: + case LSM_SESSION_CMD_DEREGISTER_SOUND_MODEL: + case LSM_SESSION_CMD_SHARED_MEM_UNMAP_REGIONS: + case LSM_SESSION_CMD_EOB: + case LSM_SESSION_CMD_READ: + case LSM_SESSION_CMD_OPEN_TX_V2: + case LSM_SESSION_CMD_OPEN_TX_V3: + case LSM_CMD_ADD_TOPOLOGIES: + case LSM_SESSION_CMD_SET_PARAMS_V2: + case LSM_SESSION_CMD_SET_PARAMS_V3: + if (token != client->session && + payload[0] != + LSM_SESSION_CMD_DEREGISTER_SOUND_MODEL) { + pr_err("%s: Invalid session %d receivced expected %d\n", + __func__, token, client->session); + return -EINVAL; + } + client->cmd_err_code = payload[1]; + if (client->cmd_err_code) + pr_err("%s: cmd 0x%x failed status %d\n", + __func__, payload[0], client->cmd_err_code); + if (atomic_cmpxchg(&client->cmd_state, + CMD_STATE_WAIT_RESP, + CMD_STATE_CLEARED) == + CMD_STATE_WAIT_RESP) + wake_up(&client->cmd_wait); + break; + default: + pr_debug("%s: Unknown command 0x%x\n", + __func__, payload[0]); + break; + } + return 0; + } + + if (client->cb) + client->cb(data->opcode, data->token, data->payload, + client->priv); + + return 0; +} + +static int q6lsm_session_alloc(struct lsm_client *client) +{ + unsigned long flags; + int n, ret = -ENOMEM; + + spin_lock_irqsave(&lsm_session_lock, flags); + for (n = LSM_MIN_SESSION_ID; n <= LSM_MAX_SESSION_ID; n++) { + if (!lsm_session[n]) { + lsm_session[n] = client; + ret = n; + break; + } + } + spin_unlock_irqrestore(&lsm_session_lock, flags); + pr_debug("%s: Alloc Session %d", __func__, n); + return ret; +} + +static void q6lsm_session_free(struct lsm_client *client) +{ + unsigned long flags; + + pr_debug("%s: Freeing session ID %d\n", __func__, client->session); + spin_lock_irqsave(&lsm_session_lock, flags); + lsm_session[client->session] = NULL; + spin_unlock_irqrestore(&lsm_session_lock, flags); + client->session = LSM_INVALID_SESSION_ID; +} + +static void *q6lsm_mmap_apr_reg(void) +{ + if (atomic_inc_return(&lsm_common.apr_users) == 1) { + lsm_common.apr = + apr_register("ADSP", "LSM", q6lsm_mmapcallback, + 0x0FFFFFFFF, &lsm_common); + if (!lsm_common.apr) { + pr_debug("%s: Unable to register APR LSM common port\n", + __func__); + atomic_dec(&lsm_common.apr_users); + } + } + return lsm_common.apr; +} + +static int q6lsm_mmap_apr_dereg(void) +{ + if (atomic_read(&lsm_common.apr_users) <= 0) { + WARN("%s: APR common port already closed\n", __func__); + } else { + if (atomic_dec_return(&lsm_common.apr_users) == 0) { + apr_deregister(lsm_common.apr); + pr_debug("%s: APR De-Register common port\n", __func__); + } + } + return 0; +} + +/** + * q6lsm_client_alloc - + * Allocate session for LSM client + * + * @cb: callback fn + * @priv: private data + * + * Returns LSM client handle on success or NULL on failure + */ +struct lsm_client *q6lsm_client_alloc(lsm_app_cb cb, void *priv) +{ + struct lsm_client *client; + int n; + + client = kzalloc(sizeof(struct lsm_client), GFP_KERNEL); + if (!client) + return NULL; + n = q6lsm_session_alloc(client); + if (n <= 0) { + kfree(client); + return NULL; + } + client->session = n; + client->cb = cb; + client->priv = priv; + if (CHECK_SESSION(client->session)) { + pr_err("%s: Client session %d\n", + __func__, client->session); + kfree(client); + return NULL; + } + pr_debug("%s: Client Session %d\n", __func__, client->session); + client->apr = apr_register("ADSP", "LSM", q6lsm_callback, + ((client->session) << 8 | client->session), + client); + + if (client->apr == NULL) { + pr_err("%s: Registration with APR failed\n", __func__); + goto fail; + } + + pr_debug("%s: Registering the common port with APR\n", __func__); + client->mmap_apr = q6lsm_mmap_apr_reg(); + if (!client->mmap_apr) { + pr_err("%s: APR registration failed\n", __func__); + goto fail; + } + + init_waitqueue_head(&client->cmd_wait); + mutex_init(&client->cmd_lock); + atomic_set(&client->cmd_state, CMD_STATE_CLEARED); + pr_debug("%s: New client allocated\n", __func__); + return client; +fail: + q6lsm_client_free(client); + return NULL; +} +EXPORT_SYMBOL(q6lsm_client_alloc); + +/** + * q6lsm_client_free - + * Performs LSM client free + * + * @client: LSM client handle + * + */ +void q6lsm_client_free(struct lsm_client *client) +{ + if (!client) + return; + if (CHECK_SESSION(client->session)) { + pr_err("%s: Invalid Session %d\n", __func__, client->session); + return; + } + mutex_lock(&session_lock); + apr_deregister(client->apr); + client->mmap_apr = NULL; + q6lsm_session_free(client); + q6lsm_mmap_apr_dereg(); + mutex_destroy(&client->cmd_lock); + kfree(client); + client = NULL; + mutex_unlock(&session_lock); +} +EXPORT_SYMBOL(q6lsm_client_free); + +/* + * q6lsm_apr_send_pkt : If wait == true, hold mutex to prevent from preempting + * other thread's wait. + * If mmap_handle_p != NULL, disable irq and spin lock to + * protect mmap_handle_p + */ +static int q6lsm_apr_send_pkt(struct lsm_client *client, void *handle, + void *data, bool wait, uint32_t *mmap_p) +{ + int ret; + unsigned long flags = 0; + struct apr_hdr *msg_hdr = (struct apr_hdr *) data; + + if (!handle) { + pr_err("%s: handle is NULL\n", __func__); + return -EINVAL; + } + + pr_debug("%s: enter wait %d\n", __func__, wait); + if (wait) + mutex_lock(&lsm_common.apr_lock); + if (mmap_p) { + WARN_ON(!wait); + spin_lock_irqsave(&mmap_lock, flags); + mmap_handle_p = mmap_p; + } + atomic_set(&client->cmd_state, CMD_STATE_WAIT_RESP); + client->cmd_err_code = 0; + ret = apr_send_pkt(handle, data); + if (mmap_p) + spin_unlock_irqrestore(&mmap_lock, flags); + + if (ret < 0) { + pr_err("%s: apr_send_pkt failed %d\n", __func__, ret); + } else if (wait) { + ret = wait_event_timeout(client->cmd_wait, + (atomic_read(&client->cmd_state) == + CMD_STATE_CLEARED), + APR_TIMEOUT); + if (likely(ret)) { + /* q6 returned error */ + if (client->cmd_err_code) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + client->cmd_err_code)); + ret = adsp_err_get_lnx_err_code( + client->cmd_err_code); + } else { + ret = 0; + } + } else { + pr_err("%s: wait timedout, apr_opcode = 0x%x, size = %d\n", + __func__, msg_hdr->opcode, msg_hdr->pkt_size); + /* ret = 0 means wait timed out */ + ret = -ETIMEDOUT; + } + } else { + ret = 0; + } + if (wait) + mutex_unlock(&lsm_common.apr_lock); + + pr_debug("%s: leave ret %d\n", __func__, ret); + return ret; +} + +static void q6lsm_add_hdr(struct lsm_client *client, struct apr_hdr *hdr, + uint32_t pkt_size, bool cmd_flg) +{ + pr_debug("%s: pkt_size %d cmd_flg %d session %d\n", __func__, + pkt_size, cmd_flg, client->session); + hdr->hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(sizeof(struct apr_hdr)), + APR_PKT_VER); + hdr->src_svc = APR_SVC_LSM; + hdr->src_domain = APR_DOMAIN_APPS; + hdr->dest_svc = APR_SVC_LSM; + hdr->dest_domain = APR_DOMAIN_ADSP; + hdr->src_port = ((client->session << 8) & 0xFF00) | client->session; + hdr->dest_port = ((client->session << 8) & 0xFF00) | client->session; + hdr->pkt_size = pkt_size; + if (cmd_flg) + hdr->token = client->session; +} + +static int q6lsm_pack_params(u8 *dest, struct param_hdr_v3 *param_info, + u8 *param_data, size_t *final_length, + u32 set_param_opcode) +{ + bool iid_supported = q6common_is_instance_id_supported(); + union param_hdrs *param_hdr = NULL; + u32 param_size = param_info->param_size; + size_t hdr_size; + size_t provided_size = *final_length; + + hdr_size = iid_supported ? sizeof(struct param_hdr_v3) : + sizeof(struct param_hdr_v2); + if (provided_size < hdr_size) { + pr_err("%s: Provided size %zu is not large enough, need %zu\n", + __func__, provided_size, hdr_size); + return -EINVAL; + } + + if (iid_supported) { + memcpy(dest, param_info, hdr_size); + } else { + /* MID, PID and structure size are the same in V1 and V2 */ + param_hdr = (union param_hdrs *) dest; + param_hdr->v2.module_id = param_info->module_id; + param_hdr->v2.param_id = param_info->param_id; + + switch (set_param_opcode) { + case LSM_SESSION_CMD_SET_PARAMS_V2: + param_hdr->v2.param_size = param_size; + break; + case LSM_SESSION_CMD_SET_PARAMS: + default: + if (param_size > U16_MAX) { + pr_err("%s: Invalid param size %d\n", __func__, + param_size); + return -EINVAL; + } + + param_hdr->v1.param_size = param_size; + param_hdr->v1.reserved = 0; + break; + } + } + + *final_length = hdr_size; + + if (param_data != NULL) { + if (provided_size < hdr_size + param_size) { + pr_err("%s: Provided size %zu is not large enough, need %zu\n", + __func__, provided_size, hdr_size + param_size); + return -EINVAL; + } + memcpy(dest + hdr_size, param_data, param_size); + *final_length += param_size; + } + return 0; +} + +static int q6lsm_set_params_v2(struct lsm_client *client, + struct mem_mapping_hdr *mem_hdr, + uint8_t *param_data, uint32_t param_size, + uint32_t set_param_opcode) +{ + struct lsm_session_cmd_set_params_v2 *lsm_set_param = NULL; + uint32_t pkt_size = 0; + int ret; + + pkt_size = sizeof(struct lsm_session_cmd_set_params_v2); + /* Only include param size in packet size when inband */ + if (param_data != NULL) + pkt_size += param_size; + + lsm_set_param = kzalloc(pkt_size, GFP_KERNEL); + if (!lsm_set_param) + return -ENOMEM; + + q6lsm_add_hdr(client, &lsm_set_param->apr_hdr, pkt_size, true); + lsm_set_param->apr_hdr.opcode = set_param_opcode; + lsm_set_param->payload_size = param_size; + + if (mem_hdr != NULL) { + lsm_set_param->mem_hdr = *mem_hdr; + } else if (param_data != NULL) { + memcpy(lsm_set_param->param_data, param_data, param_size); + } else { + pr_err("%s: Received NULL pointers for both memory header and data\n", + __func__); + ret = -EINVAL; + goto done; + } + + ret = q6lsm_apr_send_pkt(client, client->apr, lsm_set_param, true, + NULL); +done: + kfree(lsm_set_param); + return ret; +} + +static int q6lsm_set_params_v3(struct lsm_client *client, + struct mem_mapping_hdr *mem_hdr, + uint8_t *param_data, uint32_t param_size) +{ + struct lsm_session_cmd_set_params_v3 *lsm_set_param = NULL; + uint16_t pkt_size = 0; + int ret = 0; + + pkt_size = sizeof(struct lsm_session_cmd_set_params_v3); + /* Only include param size in packet size when inband */ + if (param_data != NULL) + pkt_size += param_size; + + lsm_set_param = kzalloc(pkt_size, GFP_KERNEL); + if (!lsm_set_param) + return -ENOMEM; + + q6lsm_add_hdr(client, &lsm_set_param->apr_hdr, pkt_size, true); + lsm_set_param->apr_hdr.opcode = LSM_SESSION_CMD_SET_PARAMS_V3; + lsm_set_param->payload_size = param_size; + + if (mem_hdr != NULL) { + lsm_set_param->mem_hdr = *mem_hdr; + } else if (param_data != NULL) { + memcpy(lsm_set_param->param_data, param_data, param_size); + } else { + pr_err("%s: Received NULL pointers for both memory header and data\n", + __func__); + ret = -EINVAL; + goto done; + } + + ret = q6lsm_apr_send_pkt(client, client->apr, lsm_set_param, true, + NULL); +done: + kfree(lsm_set_param); + return ret; +} + +static int q6lsm_set_params(struct lsm_client *client, + struct mem_mapping_hdr *mem_hdr, + uint8_t *param_data, uint32_t param_size, + uint32_t set_param_opcode) + +{ + if (q6common_is_instance_id_supported()) + return q6lsm_set_params_v3(client, mem_hdr, param_data, + param_size); + else + return q6lsm_set_params_v2(client, mem_hdr, param_data, + param_size, set_param_opcode); +} + +static int q6lsm_pack_and_set_params(struct lsm_client *client, + struct param_hdr_v3 *param_info, + uint8_t *param_data, + uint32_t set_param_opcode) + +{ + u8 *packed_data = NULL; + size_t total_size = 0; + int ret = 0; + + total_size = sizeof(union param_hdrs) + param_info->param_size; + packed_data = kzalloc(total_size, GFP_KERNEL); + if (!packed_data) + return -ENOMEM; + + ret = q6lsm_pack_params(packed_data, param_info, param_data, + &total_size, set_param_opcode); + if (ret) + goto done; + + ret = q6lsm_set_params(client, NULL, packed_data, total_size, + set_param_opcode); + +done: + kfree(packed_data); + return ret; +} + +static int q6lsm_send_custom_topologies(struct lsm_client *client) +{ + int rc; + struct cal_block_data *cal_block = NULL; + struct lsm_custom_topologies cstm_top; + + if (lsm_common.cal_data[LSM_CUSTOM_TOP_IDX] == NULL) { + pr_err("%s: LSM_CUSTOM_TOP_IDX invalid\n", __func__); + rc = -EINVAL; + goto done; + } + + lsm_common.set_custom_topology = 0; + + mutex_lock(&lsm_common.cal_data[LSM_CUSTOM_TOP_IDX]->lock); + cal_block = cal_utils_get_only_cal_block( + lsm_common.cal_data[LSM_CUSTOM_TOP_IDX]); + if (!cal_block) { + pr_err("%s: Cal block for LSM_CUSTOM_TOP_IDX not found\n", + __func__); + rc = -EINVAL; + goto unlock; + } + + if (cal_block->cal_data.size <= 0) { + pr_err("%s: Invalid size for LSM_CUSTOM_TOP %zd\n", + __func__, cal_block->cal_data.size); + rc = -EINVAL; + goto unlock; + } + + memset(&cstm_top, 0, sizeof(cstm_top)); + /* Map the memory for out-of-band data */ + rc = q6lsm_memory_map_regions(client, cal_block->cal_data.paddr, + cal_block->map_data.map_size, + &cal_block->map_data.q6map_handle); + if (rc < 0) { + pr_err("%s: Failed to map custom topologied, err = %d\n", + __func__, rc); + goto unlock; + } + + q6lsm_add_hdr(client, &cstm_top.hdr, + sizeof(cstm_top), true); + cstm_top.hdr.opcode = LSM_CMD_ADD_TOPOLOGIES; + + /* + * For ADD_TOPOLOGIES, the dest_port should be 0 + * Note that source port cannot be zero as it is used + * to route the response to a specific client registered + * on APR + */ + cstm_top.hdr.dest_port = 0; + + cstm_top.data_payload_addr_lsw = + lower_32_bits(cal_block->cal_data.paddr); + cstm_top.data_payload_addr_msw = + msm_audio_populate_upper_32_bits( + cal_block->cal_data.paddr); + cstm_top.mem_map_handle = cal_block->map_data.q6map_handle; + cstm_top.buffer_size = cal_block->cal_data.size; + + rc = q6lsm_apr_send_pkt(client, client->apr, + &cstm_top, true, NULL); + if (rc) + pr_err("%s: Failed to add custom top, err = %d\n", + __func__, rc); + /* go ahead and unmap even if custom top failed */ + rc = q6lsm_memory_unmap_regions(client, + cal_block->map_data.q6map_handle); + if (rc) { + pr_err("%s: Failed to unmap, err = %d\n", + __func__, rc); + /* Even if mem unmap failed, treat the cmd as success */ + rc = 0; + } + +unlock: + mutex_unlock(&lsm_common.cal_data[LSM_CUSTOM_TOP_IDX]->lock); +done: + return rc; +} + +static int q6lsm_get_topology_for_app_type(struct lsm_client *client, + int app_type, uint32_t *topology) +{ + int rc = -EINVAL; + struct cal_block_data *cal_block = NULL; + struct audio_cal_info_lsm_top *lsm_top; + struct list_head *ptr; + + if (lsm_common.cal_data[LSM_TOP_IDX] == NULL) { + pr_err("%s: LSM_TOP_IDX invalid\n", __func__); + return rc; + } + + mutex_lock(&lsm_common.cal_data[LSM_TOP_IDX]->lock); + list_for_each(ptr, &lsm_common.cal_data[LSM_TOP_IDX]->cal_blocks) { + cal_block = list_entry(ptr, struct cal_block_data, list); + if (!cal_block) { + pr_err("%s: Cal block for LSM_TOP_IDX not found\n", + __func__); + break; + } + + lsm_top = (struct audio_cal_info_lsm_top *) cal_block->cal_info; + if (!lsm_top) { + pr_err("%s: cal_info for LSM_TOP_IDX not found\n", + __func__); + break; + } + + pr_debug("%s: checking topology 0x%x, app_type 0x%x\n", + __func__, lsm_top->topology, lsm_top->app_type); + + if (app_type == 0 || lsm_top->app_type == app_type) { + *topology = lsm_top->topology; + rc = 0; + break; + } + } + mutex_unlock(&lsm_common.cal_data[LSM_TOP_IDX]->lock); + + pr_debug("%s: found topology_id = 0x%x, app_type = 0x%x\n", + __func__, *topology, app_type); + + return rc; +} + +static int q6lsm_do_open_v3(struct lsm_client *client) +{ + int rc, app_type; + struct lsm_stream_cmd_open_tx_v3 *open_v3; + size_t cmd_size = 0; + int stage_idx = LSM_STAGE_INDEX_FIRST; + uint32_t topology_id = 0, *uint32_ptr = NULL; + + cmd_size = sizeof(struct lsm_stream_cmd_open_tx_v3); + cmd_size += client->num_stages * sizeof(struct lsm_stream_stage_info); + open_v3 = kzalloc(cmd_size, GFP_KERNEL); + if (!open_v3) + return -ENOMEM; + + q6lsm_add_hdr(client, &open_v3->hdr, cmd_size, true); + open_v3->hdr.opcode = LSM_SESSION_CMD_OPEN_TX_V3; + open_v3->num_stages = client->num_stages; + uint32_ptr = &open_v3->num_stages; + uint32_ptr++; + + for (; stage_idx < client->num_stages; stage_idx++) { + app_type = client->stage_cfg[stage_idx].app_type; + rc = q6lsm_get_topology_for_app_type(client, app_type, &topology_id); + if (rc) { + pr_err("%s: failed to get topology for stage %d\n", + __func__, stage_idx); + return -EINVAL; + } + *uint32_ptr++ = topology_id; + *uint32_ptr++ = client->stage_cfg[stage_idx].lpi_enable; + } + + rc = q6lsm_apr_send_pkt(client, client->apr, open_v3, true, NULL); + if (rc) + pr_err("%s: open_v3 failed, err = %d\n", __func__, rc); + else + client->use_topology = true; + + kfree(open_v3); + return rc; + +} + +static int q6lsm_do_open_v2(struct lsm_client *client, + uint16_t app_id) +{ + int rc; + struct cal_block_data *cal_block = NULL; + struct audio_cal_info_lsm_top *lsm_top; + struct lsm_stream_cmd_open_tx_v2 open_v2; + + if (lsm_common.cal_data[LSM_TOP_IDX] == NULL) { + pr_err("%s: LSM_TOP_IDX invalid\n", __func__); + rc = -EINVAL; + goto done; + } + + mutex_lock(&lsm_common.cal_data[LSM_TOP_IDX]->lock); + cal_block = cal_utils_get_only_cal_block( + lsm_common.cal_data[LSM_TOP_IDX]); + if (!cal_block) { + pr_err("%s: Cal block for LSM_TOP_IDX not found\n", + __func__); + rc = -EINVAL; + goto unlock; + } + + lsm_top = (struct audio_cal_info_lsm_top *) + cal_block->cal_info; + if (!lsm_top) { + pr_err("%s: cal_info for LSM_TOP_IDX not found\n", + __func__); + rc = -EINVAL; + goto unlock; + } + + pr_debug("%s: topology_id = 0x%x, app_type = 0x%x\n", + __func__, lsm_top->topology, lsm_top->app_type); + + if (lsm_top->topology == 0) { + pr_err("%s: toplogy id not sent for app_type 0x%x\n", + __func__, lsm_top->app_type); + rc = -EINVAL; + goto unlock; + } + + client->app_id = lsm_top->app_type; + memset(&open_v2, 0, sizeof(open_v2)); + q6lsm_add_hdr(client, &open_v2.hdr, + sizeof(open_v2), true); + open_v2.topology_id = lsm_top->topology; + open_v2.hdr.opcode = LSM_SESSION_CMD_OPEN_TX_V2; + + rc = q6lsm_apr_send_pkt(client, client->apr, + &open_v2, true, NULL); + if (rc) + pr_err("%s: open_v2 failed, err = %d\n", + __func__, rc); + else + client->use_topology = true; +unlock: + mutex_unlock(&lsm_common.cal_data[LSM_TOP_IDX]->lock); +done: + return rc; + +} + +/** + * q6lsm_sm_set_param_data - + * Update sound model param data + * + * @client: LSM client handle + * @p_info: param info + * @offset: pointer to retrieve size + * + */ +void q6lsm_sm_set_param_data(struct lsm_client *client, + struct lsm_params_info_v2 *p_info, + size_t *offset) +{ + struct param_hdr_v3 param_hdr; + int ret; + struct lsm_sound_model *sm; + + sm = &client->stage_cfg[p_info->stage_idx].sound_model; + memset(¶m_hdr, 0, sizeof(param_hdr)); + + param_hdr.module_id = p_info->module_id; + param_hdr.instance_id = p_info->instance_id; + param_hdr.param_id = p_info->param_id; + param_hdr.param_size = p_info->param_size; + + ret = q6lsm_pack_params(sm->data, ¶m_hdr, + NULL, offset, LSM_SESSION_CMD_SET_PARAMS_V2); + if (ret) + pr_err("%s: Failed to pack params, error %d\n", __func__, ret); +} +EXPORT_SYMBOL(q6lsm_sm_set_param_data); + +/** + * q6lsm_support_multi_stage_detection - + * check for multi-stage support in adsp lsm framework service + * + * Returns true if multi-stage support available, else false + */ +bool q6lsm_adsp_supports_multi_stage_detection(void) +{ + return q6core_get_avcs_api_version_per_service( + APRV2_IDS_SERVICE_ID_ADSP_LSM_V) >= LSM_API_VERSION_V3; +} +EXPORT_SYMBOL(q6lsm_adsp_supports_multi_stage_detection); + +/** + * q6lsm_open - + * command to open LSM session + * + * @client: LSM client handle + * @app_id: App ID for LSM + * + * Returns 0 on success or error on failure + */ +int q6lsm_open(struct lsm_client *client, uint16_t app_id) +{ + int rc = 0; + struct lsm_stream_cmd_open_tx open; + + /* Add Custom topologies if needed */ + if (lsm_common.set_custom_topology) { + rc = q6lsm_send_custom_topologies(client); + if (rc) + pr_err("%s: Failed to send cust_top, err = %d\n", + __func__, rc); + } + + /* Try to open with topology first */ + if ((client->stage_cfg[LSM_STAGE_INDEX_FIRST].app_type != 0) && + q6lsm_adsp_supports_multi_stage_detection()) + rc = q6lsm_do_open_v3(client); + else + rc = q6lsm_do_open_v2(client, app_id); + if (!rc) + /* open_v2/v3 was successful */ + goto done; + + pr_debug("%s: try without topology\n", + __func__); + + memset(&open, 0, sizeof(open)); + q6lsm_add_hdr(client, &open.hdr, sizeof(open), true); + switch (client->app_id) { + case LSM_VOICE_WAKEUP_APP_ID_V2: + open.app_id = client->app_id; + break; + default: + pr_err("%s: default err 0x%x\n", __func__, client->app_id); + rc = -EINVAL; + break; + } + + open.sampling_rate = LSM_SAMPLE_RATE; + open.hdr.opcode = LSM_SESSION_CMD_OPEN_TX; + rc = q6lsm_apr_send_pkt(client, client->apr, + &open, true, NULL); + if (rc) + pr_err("%s: Open failed opcode 0x%x, rc %d\n", + __func__, open.hdr.opcode, rc); + else + client->use_topology = false; +done: + pr_debug("%s: leave %d\n", __func__, rc); + return rc; +} +EXPORT_SYMBOL(q6lsm_open); + +static int q6lsm_send_confidence_levels(struct lsm_client *client, + struct param_hdr_v3 *param_info, + uint32_t set_param_opcode) +{ + struct lsm_param_confidence_levels *conf_levels = NULL; + uint32_t num_conf_levels = client->num_confidence_levels; + uint8_t i = 0; + uint8_t padd_size = 0; + uint32_t param_size = 0; + int rc = 0; + + /* Data must be 4 byte aligned so add any necessary padding. */ + padd_size = (4 - (num_conf_levels % 4)) - 1; + param_size = (sizeof(uint8_t) + num_conf_levels + padd_size) * + sizeof(uint8_t); + param_info->param_size = param_size; + pr_debug("%s: Set Conf Levels PARAM SIZE = %d\n", __func__, param_size); + + conf_levels = kzalloc(param_size, GFP_KERNEL); + if (!conf_levels) + return -ENOMEM; + + conf_levels->num_confidence_levels = num_conf_levels; + pr_debug("%s: Num conf_level = %d\n", __func__, num_conf_levels); + + memcpy(conf_levels->confidence_levels, client->confidence_levels, + num_conf_levels); + for (i = 0; i < num_conf_levels; i++) + pr_debug("%s: Confidence_level[%d] = %d\n", __func__, i, + conf_levels->confidence_levels[i]); + + rc = q6lsm_pack_and_set_params(client, param_info, + (uint8_t *) conf_levels, + set_param_opcode); + if (rc) + pr_err("%s: Send confidence_levels cmd failed, err = %d\n", + __func__, rc); + kfree(conf_levels); + return rc; +} + +static int q6lsm_send_param_opmode(struct lsm_client *client, + struct param_hdr_v3 *param_info, + u32 set_param_opcode) +{ + struct lsm_param_op_mode op_mode; + int rc = 0; + + memset(&op_mode, 0, sizeof(op_mode)); + param_info->param_size = sizeof(op_mode); + + op_mode.minor_version = QLSM_PARAM_ID_MINOR_VERSION; + op_mode.mode = client->mode; + pr_debug("%s: mode = 0x%x", __func__, op_mode.mode); + + rc = q6lsm_pack_and_set_params(client, param_info, (uint8_t *) &op_mode, + set_param_opcode); + if (rc) + pr_err("%s: Failed set_params, rc %d\n", __func__, rc); + + pr_debug("%s: leave %d\n", __func__, rc); + return rc; +} + +/** + * set_lsm_port - + * Update LSM AFE port + * + */ +void set_lsm_port(int lsm_port) +{ + lsm_afe_port = lsm_port; +} +EXPORT_SYMBOL(set_lsm_port); + +int get_lsm_port(void) +{ + return lsm_afe_port; +} + +/** + * q6lsm_set_port_connected - + * command to set LSM port connected + * + * @client: LSM client handle + * + * Returns 0 on success or error on failure + */ +int q6lsm_set_port_connected(struct lsm_client *client) +{ + struct lsm_param_connect_to_port connect_port; + struct param_hdr_v3 connectport_hdr; + u32 set_param_opcode = 0; + int rc = 0; + + memset(&connect_port, 0, sizeof(connect_port)); + memset(&connectport_hdr, 0, sizeof(connectport_hdr)); + + if (client->use_topology) { + set_param_opcode = LSM_SESSION_CMD_SET_PARAMS_V2; + connectport_hdr.module_id = LSM_MODULE_ID_FRAMEWORK; + } else { + set_param_opcode = LSM_SESSION_CMD_SET_PARAMS; + connectport_hdr.module_id = LSM_MODULE_ID_VOICE_WAKEUP; + } + connectport_hdr.instance_id = INSTANCE_ID_0; + connectport_hdr.param_id = LSM_PARAM_ID_CONNECT_TO_PORT; + connectport_hdr.param_size = sizeof(connect_port); + + client->connect_to_port = get_lsm_port(); + connect_port.minor_version = QLSM_PARAM_ID_MINOR_VERSION; + connect_port.port_id = client->connect_to_port; + + rc = q6lsm_pack_and_set_params(client, &connectport_hdr, + (uint8_t *) &connect_port, + set_param_opcode); + if (rc) + pr_err("%s: Failed set_params, rc %d\n", __func__, rc); + return rc; +} +EXPORT_SYMBOL(q6lsm_set_port_connected); + +static int q6lsm_send_param_polling_enable(struct lsm_client *client, + bool poll_en, + struct param_hdr_v3 *param_info, + u32 set_param_opcode) +{ + struct lsm_param_poll_enable polling_enable; + int rc = 0; + + memset(&polling_enable, 0, sizeof(polling_enable)); + param_info->param_size = sizeof(polling_enable); + + polling_enable.minor_version = QLSM_PARAM_ID_MINOR_VERSION; + polling_enable.polling_enable = (poll_en) ? 1 : 0; + + rc = q6lsm_pack_and_set_params(client, param_info, + (uint8_t *) &polling_enable, + set_param_opcode); + if (rc) + pr_err("%s: Failed set_params, rc %d\n", __func__, rc); + return rc; +} + +/** + * q6lsm_set_fwk_mode_cfg - + * command to set LSM fwk mode cfg + * + * @client: LSM client handle + * @event_mode: mode for fwk cfg + * + * Returns 0 on success or error on failure + */ +int q6lsm_set_fwk_mode_cfg(struct lsm_client *client, + uint32_t event_mode) +{ + struct lsm_param_fwk_mode_cfg fwk_mode_cfg; + struct param_hdr_v3 fwk_mode_cfg_hdr; + int rc = 0; + + memset(&fwk_mode_cfg, 0, sizeof(fwk_mode_cfg)); + memset(&fwk_mode_cfg_hdr, 0, sizeof(fwk_mode_cfg_hdr)); + + if (!client->use_topology) { + pr_debug("%s: Ignore sending event mode\n", __func__); + return rc; + } + + fwk_mode_cfg_hdr.module_id = LSM_MODULE_ID_FRAMEWORK; + fwk_mode_cfg_hdr.instance_id = INSTANCE_ID_0; + fwk_mode_cfg_hdr.param_id = LSM_PARAM_ID_FWK_MODE_CONFIG; + fwk_mode_cfg_hdr.param_size = sizeof(fwk_mode_cfg); + + fwk_mode_cfg.minor_version = QLSM_PARAM_ID_MINOR_VERSION; + fwk_mode_cfg.mode = event_mode; + pr_debug("%s: mode = %d\n", __func__, fwk_mode_cfg.mode); + + rc = q6lsm_pack_and_set_params(client, &fwk_mode_cfg_hdr, + (uint8_t *) &fwk_mode_cfg, + LSM_SESSION_CMD_SET_PARAMS_V2); + if (rc) + pr_err("%s: Failed set_params, rc %d\n", __func__, rc); + return rc; +} +EXPORT_SYMBOL(q6lsm_set_fwk_mode_cfg); + +static int q6lsm_arrange_mch_map(uint8_t *ch_map, int ch_cnt) +{ + int ch_idx; + u8 mch_map[LSM_V3P0_MAX_NUM_CHANNELS] = { + PCM_CHANNEL_FL, PCM_CHANNEL_FR, PCM_CHANNEL_FC, + PCM_CHANNEL_LS, PCM_CHANNEL_RS, PCM_CHANNEL_LFE, + PCM_CHANNEL_LB, PCM_CHANNEL_RB, PCM_CHANNEL_CS}; + + + if (ch_cnt > LSM_V3P0_MAX_NUM_CHANNELS) { + pr_err("%s: invalid num_chan %d\n", __func__, ch_cnt); + return -EINVAL; + } + + if (ch_cnt == 1) { + ch_map[0] = PCM_CHANNEL_FC; + } else if (ch_cnt == 4) { + ch_map[0] = PCM_CHANNEL_FL; + ch_map[1] = PCM_CHANNEL_FR; + ch_map[2] = PCM_CHANNEL_LS; + ch_map[3] = PCM_CHANNEL_RS; + } else { + for (ch_idx = 0; ch_idx < ch_cnt; ch_idx++) + ch_map[ch_idx] = mch_map[ch_idx]; + } + + return 0; +} + +/** + * q6lsm_set_media_fmt_params - + * command to set LSM media fmt params + * + * @client: LSM client handle + * + * Returns 0 on success or error on failure + */ +int q6lsm_set_media_fmt_params(struct lsm_client *client) +{ + struct lsm_param_media_fmt media_fmt; + struct lsm_hw_params in_param = client->in_hw_params; + struct param_hdr_v3 media_fmt_hdr; + int rc = 0; + + memset(&media_fmt, 0, sizeof(media_fmt)); + memset(&media_fmt_hdr, 0, sizeof(media_fmt_hdr)); + + if (!client->use_topology) { + pr_debug("%s: Ignore sending media format\n", __func__); + goto err_ret; + } + + media_fmt_hdr.module_id = LSM_MODULE_ID_FRAMEWORK; + media_fmt_hdr.instance_id = INSTANCE_ID_0; + media_fmt_hdr.param_id = LSM_PARAM_ID_MEDIA_FMT; + media_fmt_hdr.param_size = sizeof(media_fmt); + + media_fmt.minor_version = QLSM_PARAM_ID_MINOR_VERSION_2; + media_fmt.sample_rate = in_param.sample_rate; + media_fmt.num_channels = in_param.num_chs; + media_fmt.bit_width = in_param.sample_size; + rc = q6lsm_arrange_mch_map(media_fmt.channel_mapping, + media_fmt.num_channels); + if (rc) + goto err_ret; + + pr_debug("%s: sample rate= %d, channels %d bit width %d\n", __func__, + media_fmt.sample_rate, media_fmt.num_channels, + media_fmt.bit_width); + + rc = q6lsm_pack_and_set_params(client, &media_fmt_hdr, + (uint8_t *) &media_fmt, + LSM_SESSION_CMD_SET_PARAMS_V2); + if (rc) + pr_err("%s: Failed set_params, rc %d\n", __func__, rc); +err_ret: + return rc; +} +EXPORT_SYMBOL(q6lsm_set_media_fmt_params); + +/* + * q6lsm_set_media_fmt_v2_params - + * command to set LSM media fmt (version2) params + * + * @client: LSM client handle + * + * Returns 0 on success or error on failure + */ +int q6lsm_set_media_fmt_v2_params(struct lsm_client *client) +{ + u8 *param_buf; + struct lsm_param_media_fmt_v2 *media_fmt_v2; + struct lsm_hw_params *in_param = &client->in_hw_params; + struct param_hdr_v3 media_fmt_v2_hdr; + int param_len = 0, rc = 0; + + memset(&media_fmt_v2_hdr, 0, sizeof(media_fmt_v2_hdr)); + + param_len = sizeof(*media_fmt_v2) + + (sizeof(uint8_t) * in_param->num_chs); + + /* Add padding to make sure total length is 4-byte aligned */ + if (param_len % 4) + param_len += (4 - (param_len % 4)); + + param_buf = kzalloc(param_len, GFP_KERNEL); + if (!param_buf) + return -ENOMEM; + media_fmt_v2 = (struct lsm_param_media_fmt_v2 *) param_buf; + media_fmt_v2->minor_version = QLSM_PARAM_ID_MINOR_VERSION; + media_fmt_v2->sample_rate = in_param->sample_rate; + media_fmt_v2->num_channels = in_param->num_chs; + media_fmt_v2->bit_width = in_param->sample_size; + rc = q6lsm_arrange_mch_map(media_fmt_v2->channel_mapping, + in_param->num_chs); + if (rc) + goto err_mch_map; + + media_fmt_v2_hdr.module_id = LSM_MODULE_ID_FRAMEWORK; + media_fmt_v2_hdr.instance_id = INSTANCE_ID_0; + media_fmt_v2_hdr.param_id = LSM_PARAM_ID_MEDIA_FMT_V2; + media_fmt_v2_hdr.param_size = param_len; + + pr_debug("%s: sample rate= %d, channels %d bit width %d\n", __func__, + media_fmt_v2->sample_rate, media_fmt_v2->num_channels, + media_fmt_v2->bit_width); + + rc = q6lsm_pack_and_set_params(client, &media_fmt_v2_hdr, + param_buf, + LSM_SESSION_CMD_SET_PARAMS_V2); + if (rc) + pr_err("%s: Failed set_params, rc %d\n", __func__, rc); + +err_mch_map: + kfree(param_buf); + return rc; +} +EXPORT_SYMBOL(q6lsm_set_media_fmt_v2_params); + +/** + * q6lsm_set_data - + * Command to set LSM data + * + * @client: LSM client handle + * @mode: LSM detection mode value + * @detectfailure: flag for detect failure + * + * Returns 0 on success or error on failure + */ +int q6lsm_set_data(struct lsm_client *client, + enum lsm_detection_mode mode, + bool detectfailure) +{ + struct param_hdr_v3 param_hdr; + int rc = 0; + struct lsm_params_info_v2 p_info = {0}; + + memset(¶m_hdr, 0, sizeof(param_hdr)); + + if (!client->confidence_levels) { + /* + * It is possible that confidence levels are + * not provided. This is not a error condition. + * Return gracefully without any error + */ + pr_debug("%s: no conf levels to set\n", + __func__); + return rc; + } + + if (mode == LSM_MODE_KEYWORD_ONLY_DETECTION) { + client->mode = 0x01; + } else if (mode == LSM_MODE_USER_KEYWORD_DETECTION) { + client->mode = 0x03; + } else { + pr_err("%s: Incorrect detection mode %d\n", __func__, mode); + rc = -EINVAL; + goto err_ret; + } + client->mode |= detectfailure << 2; + + param_hdr.module_id = LSM_MODULE_ID_VOICE_WAKEUP; + param_hdr.instance_id = INSTANCE_ID_0; + param_hdr.param_id = LSM_PARAM_ID_OPERATION_MODE; + rc = q6lsm_send_param_opmode(client, ¶m_hdr, + LSM_SESSION_CMD_SET_PARAMS); + if (rc) { + pr_err("%s: Failed to set lsm config params %d\n", + __func__, rc); + goto err_ret; + } + + param_hdr.param_id = LSM_PARAM_ID_MIN_CONFIDENCE_LEVELS; + rc = q6lsm_send_confidence_levels(client, ¶m_hdr, + LSM_SESSION_CMD_SET_PARAMS); + if (rc) { + pr_err("%s: Failed to send conf_levels, err = %d\n", + __func__, rc); + goto err_ret; + } + + p_info.stage_idx = LSM_STAGE_INDEX_FIRST; + rc = q6lsm_send_cal(client, LSM_SESSION_CMD_SET_PARAMS, &p_info); + if (rc) { + pr_err("%s: Failed to send calibration data %d\n", + __func__, rc); + goto err_ret; + } + +err_ret: + return rc; +} +EXPORT_SYMBOL(q6lsm_set_data); + +/** + * q6lsm_register_sound_model - + * Register LSM snd model + * + * @client: LSM client handle + * + * Returns 0 on success or error on failure + */ +int q6lsm_register_sound_model(struct lsm_client *client, + enum lsm_detection_mode mode, + bool detectfailure) +{ + int rc; + struct lsm_cmd_reg_snd_model cmd; + struct lsm_sound_model *sm; + + memset(&cmd, 0, sizeof(cmd)); + rc = q6lsm_set_data(client, mode, detectfailure); + if (rc) { + pr_err("%s: Failed to set lsm data, err = %d\n", + __func__, rc); + return rc; + } + + sm = &client->stage_cfg[LSM_STAGE_INDEX_FIRST].sound_model; + + q6lsm_add_hdr(client, &cmd.hdr, sizeof(cmd), true); + cmd.hdr.opcode = LSM_SESSION_CMD_REGISTER_SOUND_MODEL; + cmd.model_addr_lsw = lower_32_bits(sm->phys); + cmd.model_addr_msw = msm_audio_populate_upper_32_bits(sm->phys); + cmd.model_size = sm->size; + /* read updated mem_map_handle by q6lsm_mmapcallback */ + rmb(); + cmd.mem_map_handle = sm->mem_map_handle; + + pr_debug("%s: addr %pK, size %d, handle 0x%x\n", __func__, + &sm->phys, cmd.model_size, cmd.mem_map_handle); + rc = q6lsm_apr_send_pkt(client, client->apr, &cmd, true, NULL); + if (rc) + pr_err("%s: Failed cmd op[0x%x]rc[%d]\n", __func__, + cmd.hdr.opcode, rc); + else + pr_debug("%s: Register sound model succeeded\n", __func__); + + return rc; +} +EXPORT_SYMBOL(q6lsm_register_sound_model); + +/** + * q6lsm_deregister_sound_model - + * De-register LSM snd model + * + * @client: LSM client handle + * + * Returns 0 on success or error on failure + */ +int q6lsm_deregister_sound_model(struct lsm_client *client) +{ + int rc; + struct lsm_cmd_reg_snd_model cmd; + /* + * With multi-stage support sm buff allocation/free usage param info + * to check stage index for which this sound model is being set, and + * to check whether sm data is sent using set param command or not. + * Hence, set param ids to '0' to indicate allocation is for legacy + * reg_sm cmd, where buffer for param header need not be allocated, + * also set stage index to LSM_STAGE_INDEX_FIRST. + */ + struct lsm_params_info_v2 p_info = {0}; + p_info.stage_idx = LSM_STAGE_INDEX_FIRST; + + if (!client) { + pr_err("APR handle NULL\n"); + return -EINVAL; + } + if (!client->apr) { + pr_err("APR client handle NULL\n"); + return -EINVAL; + } + + if (CHECK_SESSION(client->session)) { + pr_err("%s: session[%d]", __func__, client->session); + return -EINVAL; + } + + memset(&cmd, 0, sizeof(cmd)); + q6lsm_add_hdr(client, &cmd.hdr, sizeof(cmd.hdr), false); + cmd.hdr.opcode = LSM_SESSION_CMD_DEREGISTER_SOUND_MODEL; + + rc = q6lsm_apr_send_pkt(client, client->apr, &cmd.hdr, true, NULL); + if (rc) { + pr_err("%s: Failed cmd opcode 0x%x, rc %d\n", __func__, + cmd.hdr.opcode, rc); + } else { + pr_debug("%s: Deregister sound model succeeded\n", __func__); + } + + q6lsm_snd_model_buf_free(client, &p_info); + + return rc; +} +EXPORT_SYMBOL(q6lsm_deregister_sound_model); + +static void q6lsm_add_mmaphdr(struct lsm_client *client, struct apr_hdr *hdr, + u32 pkt_size, u32 cmd_flg, u32 token) +{ + pr_debug("%s: pkt size=%d cmd_flg=%d session=%d\n", __func__, pkt_size, + cmd_flg, client->session); + hdr->hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + hdr->src_port = 0x00; + hdr->dest_port = client->session; + if (cmd_flg) + hdr->token = token; + hdr->pkt_size = pkt_size; +} + +static int q6lsm_memory_map_regions(struct lsm_client *client, + dma_addr_t dma_addr_p, uint32_t dma_buf_sz, + uint32_t *mmap_p) +{ + struct avs_cmd_shared_mem_map_regions *mmap_regions = NULL; + struct avs_shared_map_region_payload *mregions = NULL; + void *mmap_region_cmd = NULL; + void *payload = NULL; + int rc; + int cmd_size = 0; + + pr_debug("%s: dma_addr_p 0x%pK, dma_buf_sz %d, mmap_p 0x%pK, session %d\n", + __func__, &dma_addr_p, dma_buf_sz, mmap_p, + client->session); + if (CHECK_SESSION(client->session)) { + pr_err("%s: session[%d]", __func__, client->session); + return -EINVAL; + } + cmd_size = sizeof(struct avs_cmd_shared_mem_map_regions) + + sizeof(struct avs_shared_map_region_payload); + + mmap_region_cmd = kzalloc(cmd_size, GFP_KERNEL); + if (!mmap_region_cmd) + return -ENOMEM; + + mmap_regions = (struct avs_cmd_shared_mem_map_regions *)mmap_region_cmd; + q6lsm_add_mmaphdr(client, &mmap_regions->hdr, cmd_size, true, + (client->session << 8)); + + mmap_regions->hdr.opcode = LSM_SESSION_CMD_SHARED_MEM_MAP_REGIONS; + mmap_regions->mem_pool_id = ADSP_MEMORY_MAP_SHMEM8_4K_POOL; + mmap_regions->num_regions = 1; + mmap_regions->property_flag = 0x00; + payload = ((u8 *)mmap_region_cmd + + sizeof(struct avs_cmd_shared_mem_map_regions)); + mregions = (struct avs_shared_map_region_payload *)payload; + + mregions->shm_addr_lsw = lower_32_bits(dma_addr_p); + mregions->shm_addr_msw = msm_audio_populate_upper_32_bits(dma_addr_p); + mregions->mem_size_bytes = dma_buf_sz; + + rc = q6lsm_apr_send_pkt(client, client->mmap_apr, mmap_region_cmd, + true, mmap_p); + if (rc) + pr_err("%s: Failed mmap_regions opcode 0x%x, rc %d\n", + __func__, mmap_regions->hdr.opcode, rc); + + pr_debug("%s: leave %d\n", __func__, rc); + kfree(mmap_region_cmd); + return rc; +} + +static int q6lsm_memory_unmap_regions(struct lsm_client *client, + uint32_t handle) +{ + struct avs_cmd_shared_mem_unmap_regions unmap; + int rc = 0; + int cmd_size = 0; + + if (CHECK_SESSION(client->session)) { + pr_err("%s: session[%d]", __func__, client->session); + return -EINVAL; + } + cmd_size = sizeof(struct avs_cmd_shared_mem_unmap_regions); + q6lsm_add_mmaphdr(client, &unmap.hdr, cmd_size, + true, (client->session << 8)); + unmap.hdr.opcode = LSM_SESSION_CMD_SHARED_MEM_UNMAP_REGIONS; + unmap.mem_map_handle = handle; + + pr_debug("%s: unmap handle 0x%x\n", __func__, unmap.mem_map_handle); + rc = q6lsm_apr_send_pkt(client, client->mmap_apr, &unmap, true, + NULL); + if (rc) + pr_err("%s: Failed mmap_regions opcode 0x%x rc %d\n", + __func__, unmap.hdr.opcode, rc); + + return rc; +} + +static int q6lsm_send_cal(struct lsm_client *client, + u32 set_params_opcode, struct lsm_params_info_v2 *p_info) +{ + int rc = 0, stage_idx = p_info->stage_idx; + struct mem_mapping_hdr mem_hdr; + dma_addr_t lsm_cal_phy_addr; + + memset(&mem_hdr, 0, sizeof(mem_hdr)); + + pr_debug("%s: Session id %d\n", __func__, client->session); + if (CHECK_SESSION(client->session)) { + pr_err("%s: session[%d]", __func__, client->session); + return -EINVAL; + } + + lsm_cal_phy_addr = client->stage_cfg[stage_idx].cal_info.phys; + if (lsm_cal_phy_addr != 0) { + lsm_common.common_client[client->session].session = client->session; + mem_hdr.data_payload_addr_lsw = lower_32_bits(lsm_cal_phy_addr); + mem_hdr.data_payload_addr_msw = + msm_audio_populate_upper_32_bits(lsm_cal_phy_addr); + mem_hdr.mem_map_handle = + client->stage_cfg[stage_idx].cal_info.mem_map_handle; + + rc = q6lsm_set_params(client, &mem_hdr, NULL, + client->stage_cfg[stage_idx].cal_info.size, set_params_opcode); + if (rc) + pr_err("%s: Failed set_params, rc %d\n", __func__, rc); + } + + return rc; +} + +static int q6lsm_snd_cal_free(struct lsm_client *client, + struct lsm_params_info_v2 *p_info) +{ + int rc = 0, stage_idx = p_info->stage_idx; + struct lsm_cal_data_info *cal = NULL; + + if (!client->stage_cfg[stage_idx].cal_info.data) + return 0; + + mutex_lock(&client->cmd_lock); + cal = &client->stage_cfg[stage_idx].cal_info; + if (cal->mem_map_handle != 0) { + rc = q6lsm_memory_unmap_regions(client, cal->mem_map_handle); + if (rc) + pr_err("%s: CMD Memory_unmap_regions failed %d\n", + __func__, rc); + cal->mem_map_handle = 0; + } + msm_audio_ion_free(cal->dma_buf); + cal->dma_buf = NULL; + cal->data = NULL; + cal->phys = 0; + mutex_unlock(&client->cmd_lock); + + return rc; +} + +static int q6lsm_snd_cal_alloc(struct lsm_client *client, + struct lsm_params_info_v2 *p_info) +{ + int rc = 0; + size_t len = 0, total_mem = 0; + struct lsm_cal_data_info *cal = NULL; + struct cal_block_data *cal_block = NULL; + struct audio_cal_info_lsm *lsm_cal_info = NULL; + struct list_head *ptr = NULL; + int app_type, stage_idx = p_info->stage_idx; + bool cal_block_found = false; + + app_type = client->stage_cfg[stage_idx].app_type; + pr_debug("%s: app_type %d, stage_idx %d\n", + __func__, app_type, stage_idx); + + mutex_lock(&client->cmd_lock); + mutex_lock(&lsm_common.cal_data[LSM_CAL_IDX]->lock); + list_for_each(ptr, &lsm_common.cal_data[LSM_CAL_IDX]->cal_blocks) { + cal_block = list_entry(ptr, struct cal_block_data, list); + lsm_cal_info = (struct audio_cal_info_lsm *) + (cal_block) ? cal_block->cal_info : NULL; + if ((cal_block && cal_block->cal_data.paddr) && + (lsm_cal_info != NULL) && + (app_type == 0 || app_type == lsm_cal_info->app_type)) { + cal_block_found = true; + len = cal_block->cal_data.size; + break; + } + } + + if (!cal_block_found) { + pr_info("%s: cal not found for stage_idx %d\n", __func__, stage_idx); + goto exit; + } + + if (!len) { + pr_debug("%s: cal size is 0, for stage_idx %d\n", __func__, stage_idx); + goto exit; + } + + cal = &client->stage_cfg[stage_idx].cal_info; + if (cal->data) { + pr_debug("%s: cal data for stage_idx(%d) is already set \n", + __func__, stage_idx); + goto exit; + } + + cal->size = len; + total_mem = PAGE_ALIGN(len); + pr_debug("%s: cal info data size %zd Total mem %zd, stage_idx %d\n", + __func__, len, total_mem, stage_idx); + + rc = msm_audio_ion_alloc(&cal->dma_buf, total_mem, + &cal->phys, &len, &cal->data); + if (rc) { + pr_err("%s: Audio ION alloc is failed for stage_idx %d, rc = %d\n", + __func__, stage_idx, rc); + cal->dma_buf = NULL; + cal->data = NULL; + goto exit; + } + + memcpy(cal->data, (uint32_t *)cal_block->cal_data.kvaddr, cal->size); + mutex_unlock(&lsm_common.cal_data[LSM_CAL_IDX]->lock); + mutex_unlock(&client->cmd_lock); + rc = q6lsm_memory_map_regions(client, cal->phys, len, &cal->mem_map_handle); + if (rc) { + pr_err("%s: CMD Memory_map_regions failed for stage_idx %d, rc = %d\n", + __func__, stage_idx, rc); + cal->mem_map_handle = 0; + goto fail; + } + + return 0; + +exit: + mutex_unlock(&client->cmd_lock); + mutex_unlock(&lsm_common.cal_data[LSM_CAL_IDX]->lock); +fail: + q6lsm_snd_cal_free(client, p_info); + return rc; +} + +/** + * q6lsm_snd_model_buf_free - + * Free memory for LSM snd model + * + * @client: LSM client handle + * @p_info: sound model param info + * + * Returns 0 on success or error on failure + */ +int q6lsm_snd_model_buf_free(struct lsm_client *client, + struct lsm_params_info_v2 *p_info) +{ + int rc = 0, stage_idx = p_info->stage_idx; + struct lsm_sound_model *sm = NULL; + + pr_debug("%s: Session id %d\n", __func__, client->session); + if (CHECK_SESSION(client->session)) { + pr_err("%s: session[%d]", __func__, client->session); + return -EINVAL; + } + + if (!client->stage_cfg[stage_idx].sound_model.data) + return 0; + + mutex_lock(&client->cmd_lock); + sm = &client->stage_cfg[stage_idx].sound_model; + if (sm->mem_map_handle != 0) { + rc = q6lsm_memory_unmap_regions(client, sm->mem_map_handle); + if (rc) + pr_err("%s: CMD Memory_unmap_regions failed %d\n", + __func__, rc); + sm->mem_map_handle = 0; + } + msm_audio_ion_free(sm->dma_buf); + sm->dma_buf = NULL; + sm->data = NULL; + sm->phys = 0; + mutex_unlock(&client->cmd_lock); + + rc = q6lsm_snd_cal_free(client, p_info); + return rc; +} +EXPORT_SYMBOL(q6lsm_snd_model_buf_free); + +static struct lsm_client *q6lsm_get_lsm_client(int session_id) +{ + unsigned long flags; + struct lsm_client *client = NULL; + + spin_lock_irqsave(&lsm_session_lock, flags); + if (session_id < LSM_MIN_SESSION_ID || session_id > LSM_MAX_SESSION_ID) + pr_err("%s: Invalid session %d\n", __func__, session_id); + else if (!lsm_session[session_id]) + pr_err("%s: Not an active session %d\n", __func__, session_id); + else + client = lsm_session[session_id]; + spin_unlock_irqrestore(&lsm_session_lock, flags); + return client; +} + +/* + * q6lsm_mmapcallback : atomic context + */ +static int q6lsm_mmapcallback(struct apr_client_data *data, void *priv) +{ + unsigned long flags; + uint32_t command; + uint32_t retcode; + uint32_t sid; + const uint32_t *payload = data->payload; + struct lsm_client *client = NULL; + + if (data->opcode == RESET_EVENTS) { + sid = (data->token >> 8) & 0x0F; + pr_debug("%s: SSR event received 0x%x, event 0x%x,\n" + "proc 0x%x SID 0x%x\n", __func__, data->opcode, + data->reset_event, data->reset_proc, sid); + + apr_reset(lsm_common.apr); + lsm_common.apr = NULL; + atomic_set(&lsm_common.apr_users, 0); + cal_utils_clear_cal_block_q6maps(LSM_MAX_CAL_IDX, + lsm_common.cal_data); + lsm_common.set_custom_topology = 1; + return 0; + } + + command = payload[0]; + retcode = payload[1]; + sid = (data->token >> 8) & 0x0F; + pr_debug("%s: opcode 0x%x command 0x%x return code 0x%x SID 0x%x\n", + __func__, data->opcode, command, retcode, sid); + client = q6lsm_get_lsm_client(sid); + if (!client) { + pr_debug("%s: Session %d already freed\n", __func__, sid); + return 0; + } + + switch (data->opcode) { + case LSM_SESSION_CMDRSP_SHARED_MEM_MAP_REGIONS: + if (atomic_read(&client->cmd_state) == CMD_STATE_WAIT_RESP) { + spin_lock_irqsave(&mmap_lock, flags); + *mmap_handle_p = command; + /* spin_unlock_irqrestore implies barrier */ + spin_unlock_irqrestore(&mmap_lock, flags); + atomic_set(&client->cmd_state, CMD_STATE_CLEARED); + wake_up(&client->cmd_wait); + } + break; + case APR_BASIC_RSP_RESULT: + switch (command) { + case LSM_SESSION_CMD_SHARED_MEM_UNMAP_REGIONS: + atomic_set(&client->cmd_state, CMD_STATE_CLEARED); + wake_up(&client->cmd_wait); + break; + case LSM_SESSION_CMD_SHARED_MEM_MAP_REGIONS: + if (retcode != 0) { + /* error state, signal to stop waiting */ + if (atomic_read(&client->cmd_state) == + CMD_STATE_WAIT_RESP) { + spin_lock_irqsave(&mmap_lock, flags); + /* implies barrier */ + spin_unlock_irqrestore(&mmap_lock, + flags); + atomic_set(&client->cmd_state, + CMD_STATE_CLEARED); + wake_up(&client->cmd_wait); + } + } + break; + default: + pr_warn("%s: Unexpected command 0x%x\n", __func__, + command); + } + /* fallthrough */ + default: + pr_debug("%s: command 0x%x return code 0x%x opcode 0x%x\n", + __func__, command, retcode, data->opcode); + break; + } + if (client->cb) + client->cb(data->opcode, data->token, + data->payload, client->priv); + return 0; +} + +/** + * q6lsm_snd_model_buf_alloc - + * Allocate memory for LSM snd model + * + * @client: LSM client handle + * @len: size of sound model + * @p_info: sound model param info + * p_info->param_id != 0 when using set param to register sound model + * + * Returns 0 on success or error on failure + */ +int q6lsm_snd_model_buf_alloc(struct lsm_client *client, size_t len, + struct lsm_params_info_v2 *p_info) +{ + int rc = -EINVAL, stage_idx = p_info->stage_idx; + size_t total_mem = 0; + struct lsm_sound_model *sm = NULL; + + if (!client || len <= LSM_ALIGN_BOUNDARY) + return rc; + + pr_debug("%s:Snd Model len = %zd, stage idx %d\n", + __func__, len, stage_idx); + + mutex_lock(&client->cmd_lock); + sm = &client->stage_cfg[stage_idx].sound_model; + if (!sm->data) { + /* + * If sound model is sent as set_param, i.e. param_id != 0, + * Then memory needs to be allocated for + * set_param payload as well. + */ + if (p_info->param_id != 0) + len += sizeof(union param_hdrs); + + sm->size = len; + total_mem = PAGE_ALIGN(len); + pr_debug("%s: sm param size %zd Total mem %zd, stage_idx %d\n", + __func__, len, total_mem, stage_idx); + rc = msm_audio_ion_alloc(&sm->dma_buf, total_mem, + &sm->phys, &len, &sm->data); + if (rc) { + pr_err("%s: Audio ION alloc is failed, rc = %d, stage_idx = %d\n", + __func__, rc, stage_idx); + goto fail; + } + } else { + pr_err("%s: sound model busy, stage_idx %d\n", __func__, stage_idx); + rc = -EBUSY; + goto fail; + } + + rc = q6lsm_memory_map_regions(client, sm->phys, len, &sm->mem_map_handle); + if (rc) { + pr_err("%s: CMD Memory_map_regions failed %d, stage_idx %d\n", + __func__, rc, stage_idx); + sm->mem_map_handle = 0; + goto fail; + } + mutex_unlock(&client->cmd_lock); + + rc = q6lsm_snd_cal_alloc(client, p_info); + if (rc) { + pr_err("%s: cal alloc failed %d, stage_idx %d\n", + __func__, rc, stage_idx); + goto fail_1; + } + return rc; + +fail: + mutex_unlock(&client->cmd_lock); +fail_1: + q6lsm_snd_model_buf_free(client, p_info); + return rc; +} +EXPORT_SYMBOL(q6lsm_snd_model_buf_alloc); + +static int q6lsm_cmd(struct lsm_client *client, int opcode, bool wait) +{ + struct apr_hdr hdr; + int rc; + + pr_debug("%s: enter opcode %x wait %d\n", __func__, opcode, wait); + q6lsm_add_hdr(client, &hdr, sizeof(hdr), true); + switch (opcode) { + case LSM_SESSION_CMD_START: + case LSM_SESSION_CMD_STOP: + case LSM_SESSION_CMD_CLOSE_TX: + case LSM_SESSION_CMD_EOB: + hdr.opcode = opcode; + break; + default: + pr_err("%s: Invalid opcode 0x%x\n", __func__, opcode); + return -EINVAL; + } + rc = q6lsm_apr_send_pkt(client, client->apr, &hdr, wait, NULL); + if (rc) + pr_err("%s: Failed commmand 0x%x\n", __func__, hdr.opcode); + + pr_debug("%s: leave %d\n", __func__, rc); + return rc; +} + +static int q6lsm_send_param_epd_thres(struct lsm_client *client, void *data, + struct param_hdr_v3 *param_info) +{ + struct snd_lsm_ep_det_thres *ep_det_data = NULL; + struct lsm_param_epd_thres epd_thres; + int rc = 0; + + memset(&epd_thres, 0, sizeof(epd_thres)); + param_info->param_size = sizeof(epd_thres); + + ep_det_data = (struct snd_lsm_ep_det_thres *) data; + epd_thres.minor_version = QLSM_PARAM_ID_MINOR_VERSION; + epd_thres.epd_begin = ep_det_data->epd_begin; + epd_thres.epd_end = ep_det_data->epd_end; + + rc = q6lsm_pack_and_set_params(client, param_info, + (uint8_t *) &epd_thres, + LSM_SESSION_CMD_SET_PARAMS_V2); + if (unlikely(rc)) + pr_err("%s: EPD_THRESHOLD failed, rc %d\n", __func__, rc); + return rc; +} + +static int q6lsm_send_param_gain(struct lsm_client *client, u16 gain, + struct param_hdr_v3 *param_info) +{ + struct lsm_param_gain lsm_gain; + int rc = 0; + + memset(&lsm_gain, 0, sizeof(lsm_gain)); + param_info->param_size = sizeof(lsm_gain); + + lsm_gain.minor_version = QLSM_PARAM_ID_MINOR_VERSION; + lsm_gain.gain = gain; + + rc = q6lsm_pack_and_set_params(client, param_info, + (uint8_t *) &lsm_gain, + LSM_SESSION_CMD_SET_PARAMS_V2); + if (unlikely(rc)) + pr_err("%s: LSM_GAIN CMD send failed, rc %d\n", __func__, rc); + return rc; +} + +/** + * q6lsm_set_one_param - + * command for LSM set params + * + * @client: LSM client handle + * p_info: Params info + * data: payload based on param type + * param_type: LSM param type + * + * Returns 0 on success or error on failure + */ +int q6lsm_set_one_param(struct lsm_client *client, + struct lsm_params_info_v2 *p_info, void *data, + uint32_t param_type) +{ + struct param_hdr_v3 param_info; + int rc = 0; + + memset(¶m_info, 0, sizeof(param_info)); + + switch (param_type) { + case LSM_ENDPOINT_DETECT_THRESHOLD: { + param_info.module_id = p_info->module_id; + param_info.instance_id = p_info->instance_id; + param_info.param_id = p_info->param_id; + rc = q6lsm_send_param_epd_thres(client, data, ¶m_info); + if (rc) + pr_err("%s: LSM_ENDPOINT_DETECT_THRESHOLD failed, rc %d\n", + __func__, rc); + break; + } + + case LSM_OPERATION_MODE: { + struct snd_lsm_detect_mode *det_mode = data; + + if (det_mode->mode == LSM_MODE_KEYWORD_ONLY_DETECTION) { + client->mode = 0x01; + } else if (det_mode->mode == LSM_MODE_USER_KEYWORD_DETECTION) { + client->mode = 0x03; + } else { + pr_err("%s: Incorrect detection mode %d\n", + __func__, det_mode->mode); + return -EINVAL; + } + + client->mode |= det_mode->detect_failure << 2; + + param_info.module_id = p_info->module_id; + param_info.instance_id = p_info->instance_id; + param_info.param_id = p_info->param_id; + + rc = q6lsm_send_param_opmode(client, ¶m_info, + LSM_SESSION_CMD_SET_PARAMS_V2); + if (rc) + pr_err("%s: OPERATION_MODE failed, rc %d\n", + __func__, rc); + break; + } + + case LSM_GAIN: { + struct snd_lsm_gain *lsm_gain = (struct snd_lsm_gain *) data; + param_info.module_id = p_info->module_id; + param_info.instance_id = p_info->instance_id; + param_info.param_id = p_info->param_id; + rc = q6lsm_send_param_gain(client, lsm_gain->gain, ¶m_info); + if (rc) + pr_err("%s: LSM_GAIN command failed, rc %d\n", + __func__, rc); + break; + } + + case LSM_MIN_CONFIDENCE_LEVELS: + param_info.module_id = p_info->module_id; + param_info.instance_id = p_info->instance_id; + param_info.param_id = p_info->param_id; + rc = q6lsm_send_confidence_levels( + client, ¶m_info, LSM_SESSION_CMD_SET_PARAMS_V2); + if (rc) + pr_err("%s: CONFIDENCE_LEVELS cmd failed, rc %d\n", + __func__, rc); + break; + case LSM_POLLING_ENABLE: { + struct snd_lsm_poll_enable *lsm_poll_enable = + (struct snd_lsm_poll_enable *) data; + param_info.module_id = p_info->module_id; + param_info.instance_id = p_info->instance_id; + param_info.param_id = p_info->param_id; + rc = q6lsm_send_param_polling_enable( + client, lsm_poll_enable->poll_en, ¶m_info, + LSM_SESSION_CMD_SET_PARAMS_V2); + if (rc) + pr_err("%s: POLLING ENABLE cmd failed, rc %d\n", + __func__, rc); + break; + } + + case LSM_REG_SND_MODEL: { + struct mem_mapping_hdr mem_hdr; + u32 payload_size; + struct lsm_sound_model *sm = NULL; + + memset(&mem_hdr, 0, sizeof(mem_hdr)); + + if (q6common_is_instance_id_supported()) + payload_size = p_info->param_size + + sizeof(struct param_hdr_v3); + else + payload_size = p_info->param_size + + sizeof(struct param_hdr_v2); + + sm = &client->stage_cfg[p_info->stage_idx].sound_model; + + mem_hdr.data_payload_addr_lsw = + lower_32_bits(sm->phys); + mem_hdr.data_payload_addr_msw = + msm_audio_populate_upper_32_bits( + sm->phys), + mem_hdr.mem_map_handle = sm->mem_map_handle; + + rc = q6lsm_set_params(client, &mem_hdr, NULL, payload_size, + LSM_SESSION_CMD_SET_PARAMS_V2); + if (rc) { + pr_err("%s: REG_SND_MODEL failed, rc %d\n", + __func__, rc); + return rc; + } + + rc = q6lsm_send_cal(client, LSM_SESSION_CMD_SET_PARAMS, p_info); + if (rc) + pr_err("%s: Failed to send lsm cal, err = %d\n", + __func__, rc); + break; + } + + case LSM_DEREG_SND_MODEL: { + param_info.module_id = p_info->module_id; + param_info.instance_id = p_info->instance_id; + param_info.param_id = p_info->param_id; + param_info.param_size = 0; + rc = q6lsm_pack_and_set_params(client, ¶m_info, NULL, + LSM_SESSION_CMD_SET_PARAMS_V2); + if (rc) + pr_err("%s: DEREG_SND_MODEL failed, rc %d\n", + __func__, rc); + break; + } + + case LSM_CUSTOM_PARAMS: { + u32 param_size = p_info->param_size; + + /* Check minimum size, V2 structure is smaller than V3 */ + if (param_size < sizeof(struct param_hdr_v2)) { + pr_err("%s: Invalid param_size %d\n", __func__, + param_size); + return -EINVAL; + } + + rc = q6lsm_set_params(client, NULL, data, param_size, + LSM_SESSION_CMD_SET_PARAMS_V2); + if (rc) + pr_err("%s: CUSTOM_PARAMS failed, rc %d\n", + __func__, rc); + break; + } + + case LSM_DET_EVENT_TYPE: { + struct lsm_param_det_event_type det_event_type; + struct snd_lsm_det_event_type *det_event_data = + (struct snd_lsm_det_event_type *)data; + + param_info.module_id = p_info->module_id; + param_info.instance_id = p_info->instance_id; + param_info.param_id = p_info->param_id; + param_info.param_size = sizeof(det_event_type); + + memset(&det_event_type, 0, sizeof(det_event_type)); + + det_event_type.minor_version = QLSM_PARAM_ID_MINOR_VERSION; + det_event_type.event_type = det_event_data->event_type; + det_event_type.mode = det_event_data->mode; + + rc = q6lsm_pack_and_set_params(client, ¶m_info, + (uint8_t *)&det_event_type, + LSM_SESSION_CMD_SET_PARAMS_V2); + if (rc) + pr_err("%s: DET_EVENT_TYPE cmd failed, rc %d\n", + __func__, rc); + break; + } + + default: + pr_err("%s: wrong param_type 0x%x\n", + __func__, p_info->param_type); + } + + return rc; +} +EXPORT_SYMBOL(q6lsm_set_one_param); + + +/** + * q6lsm_start - + * command for LSM start + * + * @client: LSM client handle + * + * Returns 0 on success or error on failure + */ +int q6lsm_start(struct lsm_client *client, bool wait) +{ + return q6lsm_cmd(client, LSM_SESSION_CMD_START, wait); +} +EXPORT_SYMBOL(q6lsm_start); + +/** + * q6lsm_stop - + * command for LSM stop + * + * @client: LSM client handle + * + * Returns 0 on success or error on failure + */ +int q6lsm_stop(struct lsm_client *client, bool wait) +{ + return q6lsm_cmd(client, LSM_SESSION_CMD_STOP, wait); +} +EXPORT_SYMBOL(q6lsm_stop); + +/** + * q6lsm_close - + * command for LSM close + * + * @client: LSM client handle + * + * Returns 0 on success or error on failure + */ +int q6lsm_close(struct lsm_client *client) +{ + return q6lsm_cmd(client, LSM_SESSION_CMD_CLOSE_TX, true); +} +EXPORT_SYMBOL(q6lsm_close); + +/** + * q6lsm_lab_control - + * command to set LSM LAB control params + * + * @client: LSM client handle + * @enable: bool flag to enable or disable LAB on DSP + * @p_info: param info to be used for sending lab control param + * + * Returns 0 on success or error on failure + */ +int q6lsm_lab_control(struct lsm_client *client, u32 enable, + struct lsm_params_info_v2 *p_info) +{ + struct lsm_param_lab_enable lab_enable; + struct param_hdr_v3 lab_enable_hdr; + struct lsm_param_lab_config lab_config; + struct param_hdr_v3 lab_config_hdr; + int rc = 0; + + memset(&lab_enable, 0, sizeof(lab_enable)); + memset(&lab_enable_hdr, 0, sizeof(lab_enable_hdr)); + memset(&lab_config, 0, sizeof(lab_config)); + memset(&lab_config_hdr, 0, sizeof(lab_config_hdr)); + + if (!client) { + pr_err("%s: invalid param client %pK\n", __func__, client); + return -EINVAL; + } + + /* enable/disable lab on dsp */ + lab_enable_hdr.module_id = p_info->module_id; + lab_enable_hdr.instance_id = p_info->instance_id; + lab_enable_hdr.param_id = LSM_PARAM_ID_LAB_ENABLE; + lab_enable_hdr.param_size = sizeof(lab_enable); + lab_enable.enable = (enable) ? 1 : 0; + rc = q6lsm_pack_and_set_params(client, &lab_enable_hdr, + (uint8_t *) &lab_enable, + LSM_SESSION_CMD_SET_PARAMS); + if (rc) { + pr_err("%s: Lab enable failed rc %d\n", __func__, rc); + return rc; + } + if (!enable) + goto exit; + + /* lab session is being enabled set the config values */ + lab_config_hdr.module_id = p_info->module_id; + lab_config_hdr.instance_id = p_info->instance_id; + lab_config_hdr.param_id = LSM_PARAM_ID_LAB_CONFIG; + lab_config_hdr.param_size = sizeof(lab_config); + lab_config.minor_version = 1; + lab_config.wake_up_latency_ms = 250; + rc = q6lsm_pack_and_set_params(client, &lab_config_hdr, + (uint8_t *) &lab_config, + LSM_SESSION_CMD_SET_PARAMS); + if (rc) { + pr_err("%s: Lab config failed rc %d disable lab\n", + __func__, rc); + /* Lab config failed disable lab */ + lab_enable.enable = 0; + if (q6lsm_pack_and_set_params(client, &lab_enable_hdr, + (uint8_t *) &lab_enable, + LSM_SESSION_CMD_SET_PARAMS)) + pr_err("%s: Lab disable failed\n", __func__); + } +exit: + return rc; +} +EXPORT_SYMBOL(q6lsm_lab_control); + +/* + * q6lsm_lab_out_ch_cfg - + * Command to set the channel configuration + * for look-ahead buffer. + * + * @client: LSM client handle + * @ch_map: Channel map indicating the order + * of channels to be configured. + * @p_info: param info to be used for sending lab config param + * + * Returns 0 on success or error on failure + */ +int q6lsm_lab_out_ch_cfg(struct lsm_client *client, + u8 *ch_map, struct lsm_params_info_v2 *p_info) +{ + u8 *param_buf; + struct lsm_param_lab_out_ch_cfg *lab_out_cfg; + struct param_hdr_v3 lab_out_cfg_hdr; + struct lsm_hw_params *out_params = &client->out_hw_params; + int i, rc = 0, param_len = 0; + + param_len = sizeof(*lab_out_cfg) + + sizeof(u8) * out_params->num_chs; + + if (param_len % 4) + param_len += (4 - (param_len % 4)); + + param_buf = kzalloc(param_len, GFP_KERNEL); + if (!param_buf) + return -ENOMEM; + + lab_out_cfg = (struct lsm_param_lab_out_ch_cfg *) param_buf; + lab_out_cfg->minor_version = QLSM_PARAM_ID_MINOR_VERSION; + lab_out_cfg->num_channels = out_params->num_chs; + + for (i = 0; i < out_params->num_chs; i++) + lab_out_cfg->channel_indices[i] = ch_map[i]; + + memset(&lab_out_cfg_hdr, 0, sizeof(lab_out_cfg_hdr)); + lab_out_cfg_hdr.module_id = p_info->module_id; + lab_out_cfg_hdr.instance_id = p_info->instance_id; + lab_out_cfg_hdr.param_id = LSM_PARAM_ID_LAB_OUTPUT_CHANNEL_CONFIG; + lab_out_cfg_hdr.param_size = param_len; + + rc = q6lsm_pack_and_set_params(client, &lab_out_cfg_hdr, + param_buf, + LSM_SESSION_CMD_SET_PARAMS_V2); + if (rc) + pr_err("%s: Lab out channel config failed %d\n", + __func__, rc); + + kfree(param_buf); + + return rc; +} +EXPORT_SYMBOL(q6lsm_lab_out_ch_cfg); + +/** + * q6lsm_stop_lab - + * command to stop LSM LAB + * + * @client: LSM client handle + * + * Returns 0 on success or error on failure + */ +int q6lsm_stop_lab(struct lsm_client *client) +{ + int rc = 0; + + if (!client) { + pr_err("%s: invalid param client %pK\n", __func__, client); + return -EINVAL; + } + rc = q6lsm_cmd(client, LSM_SESSION_CMD_EOB, true); + if (rc) + pr_err("%s: Lab stop failed %d\n", __func__, rc); + return rc; +} +EXPORT_SYMBOL(q6lsm_stop_lab); + +/** + * q6lsm_read - + * command for LSM read + * + * @client: LSM client handle + * @lsm_cmd_read: LSM read command + * + * Returns 0 on success or error on failure + */ +int q6lsm_read(struct lsm_client *client, struct lsm_cmd_read *read) +{ + int rc = 0; + + if (!client || !read) { + pr_err("%s: Invalid params client %pK read %pK\n", __func__, + client, read); + return -EINVAL; + } + pr_debug("%s: read call memmap handle %x address %x%x size %d\n", + __func__, read->mem_map_handle, read->buf_addr_msw, + read->buf_addr_lsw, read->buf_size); + q6lsm_add_hdr(client, &read->hdr, sizeof(struct lsm_cmd_read), true); + read->hdr.opcode = LSM_SESSION_CMD_READ; + rc = q6lsm_apr_send_pkt(client, client->apr, read, false, NULL); + if (rc) + pr_err("%s: read buffer call failed rc %d\n", __func__, rc); + return rc; +} +EXPORT_SYMBOL(q6lsm_read); + +/** + * q6lsm_lab_buffer_alloc - + * Lab buffer allocation or de-alloc + * + * @client: LSM client handle + * @alloc: Allocate or free ion memory + * + * Returns 0 on success or error on failure + */ +int q6lsm_lab_buffer_alloc(struct lsm_client *client, bool alloc) +{ + int ret = 0, i = 0; + size_t allocate_size = 0, len = 0; + struct lsm_hw_params *out_params = &client->out_hw_params; + + if (!client) { + pr_err("%s: invalid client\n", __func__); + return -EINVAL; + } + if (alloc) { + if (client->lab_buffer) { + pr_err("%s: buffers are allocated period count %d period size %d\n", + __func__, + out_params->period_count, + out_params->buf_sz); + return -EINVAL; + } + allocate_size = out_params->period_count * + out_params->buf_sz; + allocate_size = PAGE_ALIGN(allocate_size); + client->lab_buffer = + kzalloc(sizeof(struct lsm_lab_buffer) * + out_params->period_count, GFP_KERNEL); + if (!client->lab_buffer) { + pr_err("%s: memory allocation for lab buffer failed count %d\n" + , __func__, + out_params->period_count); + return -ENOMEM; + } + ret = msm_audio_ion_alloc(&client->lab_buffer[0].dma_buf, + allocate_size, &client->lab_buffer[0].phys, + &len, + &client->lab_buffer[0].data); + if (ret) + pr_err("%s: ion alloc failed ret %d size %zd\n", + __func__, ret, allocate_size); + else { + ret = q6lsm_memory_map_regions(client, + client->lab_buffer[0].phys, len, + &client->lab_buffer[0].mem_map_handle); + if (ret) { + pr_err("%s: memory map filed ret %d size %zd\n", + __func__, ret, len); + msm_audio_ion_free( + client->lab_buffer[0].dma_buf); + } + } + if (ret) { + pr_err("%s: alloc lab buffer failed ret %d\n", + __func__, ret); + kfree(client->lab_buffer); + client->lab_buffer = NULL; + } else { + pr_debug("%s: Memory map handle %x phys %pK size %d\n", + __func__, + client->lab_buffer[0].mem_map_handle, + &client->lab_buffer[0].phys, + out_params->buf_sz); + + for (i = 0; i < out_params->period_count; i++) { + client->lab_buffer[i].phys = + client->lab_buffer[0].phys + + (i * out_params->buf_sz); + client->lab_buffer[i].size = + out_params->buf_sz; + client->lab_buffer[i].data = + (u8 *)(client->lab_buffer[0].data) + + (i * out_params->buf_sz); + client->lab_buffer[i].mem_map_handle = + client->lab_buffer[0].mem_map_handle; + } + } + } else { + ret = q6lsm_memory_unmap_regions(client, + client->lab_buffer[0].mem_map_handle); + if (!ret) + msm_audio_ion_free(client->lab_buffer[0].dma_buf); + else + pr_err("%s: unmap failed not freeing memory\n", + __func__); + kfree(client->lab_buffer); + client->lab_buffer = NULL; + } + return ret; +} +EXPORT_SYMBOL(q6lsm_lab_buffer_alloc); + +static int get_cal_type_index(int32_t cal_type) +{ + int ret = -EINVAL; + + switch (cal_type) { + case LSM_CUST_TOPOLOGY_CAL_TYPE: + ret = LSM_CUSTOM_TOP_IDX; + break; + case LSM_TOPOLOGY_CAL_TYPE: + ret = LSM_TOP_IDX; + break; + case LSM_CAL_TYPE: + ret = LSM_CAL_IDX; + break; + default: + pr_err("%s: invalid cal type %d!\n", __func__, cal_type); + } + return ret; +} + +static int q6lsm_alloc_cal(int32_t cal_type, + size_t data_size, void *data) +{ + int ret = 0; + int cal_index; + + pr_debug("%s:\n", __func__); + + cal_index = get_cal_type_index(cal_type); + if (cal_index < 0) { + pr_err("%s: could not get cal index %d!\n", + __func__, cal_index); + ret = -EINVAL; + goto done; + } + + ret = cal_utils_alloc_cal(data_size, data, + lsm_common.cal_data[cal_index], 0, NULL); + if (ret < 0) { + pr_err("%s: cal_utils_alloc_block failed, ret = %d, cal type = %d!\n", + __func__, ret, cal_type); + ret = -EINVAL; + goto done; + } +done: + return ret; +} + +static int q6lsm_dealloc_cal(int32_t cal_type, + size_t data_size, void *data) +{ + int ret = 0; + int cal_index; + + pr_debug("%s:\n", __func__); + + cal_index = get_cal_type_index(cal_type); + if (cal_index < 0) { + pr_err("%s: could not get cal index %d!\n", + __func__, cal_index); + ret = -EINVAL; + goto done; + } + + ret = cal_utils_dealloc_cal(data_size, data, + lsm_common.cal_data[cal_index]); + if (ret < 0) { + pr_err("%s: cal_utils_dealloc_block failed, ret = %d, cal type = %d!\n", + __func__, ret, cal_type); + ret = -EINVAL; + goto done; + } +done: + return ret; +} + +static int q6lsm_set_cal(int32_t cal_type, + size_t data_size, void *data) +{ + int ret = 0; + int cal_index; + + pr_debug("%s:\n", __func__); + + cal_index = get_cal_type_index(cal_type); + if (cal_index < 0) { + pr_err("%s: could not get cal index %d!\n", + __func__, cal_index); + ret = -EINVAL; + goto done; + } + + ret = cal_utils_set_cal(data_size, data, + lsm_common.cal_data[cal_index], 0, NULL); + if (ret < 0) { + pr_err("%s: cal_utils_set_cal failed, ret = %d, cal type = %d!\n", + __func__, ret, cal_type); + ret = -EINVAL; + goto done; + } + + if (cal_index == LSM_CUSTOM_TOP_IDX) { + mutex_lock(&lsm_common.cal_data[LSM_CUSTOM_TOP_IDX]->lock); + lsm_common.set_custom_topology = 1; + mutex_unlock(&lsm_common.cal_data[LSM_CUSTOM_TOP_IDX]->lock); + } + +done: + return ret; +} + +static void lsm_delete_cal_data(void) +{ + pr_debug("%s:\n", __func__); + + cal_utils_destroy_cal_types(LSM_MAX_CAL_IDX, lsm_common.cal_data); +} + +static int q6lsm_init_cal_data(void) +{ + int ret = 0; + struct cal_type_info cal_type_info[] = { + {{LSM_CUST_TOPOLOGY_CAL_TYPE, + {q6lsm_alloc_cal, q6lsm_dealloc_cal, NULL, + q6lsm_set_cal, NULL, NULL} }, + {NULL, NULL, cal_utils_match_buf_num} }, + + {{LSM_TOPOLOGY_CAL_TYPE, + {NULL, NULL, NULL, + q6lsm_set_cal, NULL, NULL} }, + {NULL, NULL, cal_utils_match_buf_num} }, + + {{LSM_CAL_TYPE, + {q6lsm_alloc_cal, q6lsm_dealloc_cal, NULL, + q6lsm_set_cal, NULL, NULL} }, + {NULL, NULL, cal_utils_match_buf_num} } + }; + pr_debug("%s:\n", __func__); + + ret = cal_utils_create_cal_types(LSM_MAX_CAL_IDX, + lsm_common.cal_data, cal_type_info); + if (ret < 0) { + pr_err("%s: could not create cal type!\n", + __func__); + ret = -EINVAL; + goto err; + } + + return ret; +err: + lsm_delete_cal_data(); + return ret; +} + +int __init q6lsm_init(void) +{ + int i = 0; + + pr_debug("%s:\n", __func__); + + memset(&lsm_common, 0, sizeof(lsm_common)); + spin_lock_init(&lsm_session_lock); + spin_lock_init(&mmap_lock); + mutex_init(&lsm_common.apr_lock); + for (; i <= LSM_MAX_SESSION_ID; i++) { + lsm_common.common_client[i].session = LSM_CONTROL_SESSION; + init_waitqueue_head(&lsm_common.common_client[i].cmd_wait); + mutex_init(&lsm_common.common_client[i].cmd_lock); + atomic_set(&lsm_common.common_client[i].cmd_state, + CMD_STATE_CLEARED); + } + + if (q6lsm_init_cal_data()) + pr_err("%s: could not init cal data!\n", __func__); + + return 0; +} + +void q6lsm_exit(void) +{ + lsm_delete_cal_data(); +} diff --git a/audio-kernel/dsp/q6usm.c b/audio-kernel/dsp/q6usm.c new file mode 100644 index 000000000..4af5b8695 --- /dev/null +++ b/audio-kernel/dsp/q6usm.c @@ -0,0 +1,1481 @@ +/* Copyright (c) 2012-2019, 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 +#include +#include +#include +#include +#include +#include +#include +#include "q6usm.h" + +#define ADSP_MEMORY_MAP_SHMEM8_4K_POOL 3 + +#define MEM_4K_OFFSET 4095 +#define MEM_4K_MASK 0xfffff000 + +#define USM_SESSION_MAX 0x02 /* aDSP:USM limit */ + +#define READDONE_IDX_STATUS 0 + +#define WRITEDONE_IDX_STATUS 0 + +/* Standard timeout in the asynchronous ops */ +#define Q6USM_TIMEOUT_JIFFIES (1*HZ) /* 1 sec */ + +static DEFINE_MUTEX(session_lock); + +static struct us_client *session[USM_SESSION_MAX]; +static int32_t q6usm_mmapcallback(struct apr_client_data *data, void *priv); +static int32_t q6usm_callback(struct apr_client_data *data, void *priv); +static void q6usm_add_hdr(struct us_client *usc, struct apr_hdr *hdr, + uint32_t pkt_size, bool cmd_flg); + +struct usm_mmap { + atomic_t ref_cnt; + atomic_t cmd_state; + wait_queue_head_t cmd_wait; + void *apr; + int mem_handle; +}; + +static struct usm_mmap this_mmap; + +static void q6usm_add_mmaphdr(struct apr_hdr *hdr, + uint32_t pkt_size, bool cmd_flg, u32 token) +{ + hdr->hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + hdr->src_port = 0; + hdr->dest_port = 0; + if (cmd_flg) { + hdr->token = token; + atomic_set(&this_mmap.cmd_state, 1); + } + hdr->pkt_size = pkt_size; +} + +static int q6usm_memory_map(phys_addr_t buf_add, int dir, uint32_t bufsz, + uint32_t bufcnt, uint32_t session, uint32_t *mem_handle) +{ + struct usm_cmd_memory_map_region mem_region_map; + int rc = 0; + + if (this_mmap.apr == NULL) { + pr_err("%s: APR handle NULL\n", __func__); + return -EINVAL; + } + + q6usm_add_mmaphdr(&mem_region_map.hdr, + sizeof(struct usm_cmd_memory_map_region), true, + ((session << 8) | dir)); + + mem_region_map.hdr.opcode = USM_CMD_SHARED_MEM_MAP_REGION; + mem_region_map.mempool_id = ADSP_MEMORY_MAP_SHMEM8_4K_POOL; + + mem_region_map.num_regions = 1; + mem_region_map.flags = 0; + + mem_region_map.shm_addr_lsw = lower_32_bits(buf_add); + mem_region_map.shm_addr_msw = + msm_audio_populate_upper_32_bits(buf_add); + mem_region_map.mem_size_bytes = bufsz * bufcnt; + + rc = apr_send_pkt(this_mmap.apr, (uint32_t *) &mem_region_map); + if (rc < 0) { + pr_err("%s: mem_map op[0x%x]rc[%d]\n", + __func__, mem_region_map.hdr.opcode, rc); + rc = -EINVAL; + goto fail_cmd; + } + + rc = wait_event_timeout(this_mmap.cmd_wait, + (atomic_read(&this_mmap.cmd_state) == 0), + Q6USM_TIMEOUT_JIFFIES); + if (!rc) { + rc = -ETIME; + pr_err("%s: timeout. waited for memory_map\n", __func__); + } else { + *mem_handle = this_mmap.mem_handle; + rc = 0; + } +fail_cmd: + return rc; +} + +int q6usm_memory_unmap(phys_addr_t buf_add, int dir, uint32_t session, + uint32_t mem_handle) +{ + struct usm_cmd_memory_unmap_region mem_unmap; + int rc = 0; + + if (this_mmap.apr == NULL) { + pr_err("%s: APR handle NULL\n", __func__); + return -EINVAL; + } + + q6usm_add_mmaphdr(&mem_unmap.hdr, + sizeof(struct usm_cmd_memory_unmap_region), true, + ((session << 8) | dir)); + mem_unmap.hdr.opcode = USM_CMD_SHARED_MEM_UNMAP_REGION; + mem_unmap.mem_map_handle = mem_handle; + + rc = apr_send_pkt(this_mmap.apr, (uint32_t *) &mem_unmap); + if (rc < 0) { + pr_err("%s: mem_unmap op[0x%x] rc[%d]\n", + __func__, mem_unmap.hdr.opcode, rc); + goto fail_cmd; + } + + rc = wait_event_timeout(this_mmap.cmd_wait, + (atomic_read(&this_mmap.cmd_state) == 0), + Q6USM_TIMEOUT_JIFFIES); + if (!rc) { + rc = -ETIME; + pr_err("%s: timeout. waited for memory_unmap\n", __func__); + } else + rc = 0; +fail_cmd: + return rc; +} + +static int q6usm_session_alloc(struct us_client *usc) +{ + int ind = 0; + + mutex_lock(&session_lock); + for (ind = 0; ind < USM_SESSION_MAX; ++ind) { + if (!session[ind]) { + session[ind] = usc; + mutex_unlock(&session_lock); + ++ind; /* session id: 0 reserved */ + pr_debug("%s: session[%d] was allocated\n", + __func__, ind); + return ind; + } + } + mutex_unlock(&session_lock); + return -ENOMEM; +} + +static void q6usm_session_free(struct us_client *usc) +{ + /* Session index was incremented during allocation */ + uint16_t ind = (uint16_t)usc->session - 1; + + pr_debug("%s: to free session[%d]\n", __func__, ind); + if (ind < USM_SESSION_MAX) { + mutex_lock(&session_lock); + session[ind] = NULL; + mutex_unlock(&session_lock); + } +} + +static int q6usm_us_client_buf_free(unsigned int dir, + struct us_client *usc) +{ + struct us_port_data *port; + int rc = 0; + + if ((usc == NULL) || + ((dir != IN) && (dir != OUT))) + return -EINVAL; + + mutex_lock(&usc->cmd_lock); + port = &usc->port[dir]; + if (port == NULL) { + mutex_unlock(&usc->cmd_lock); + return -EINVAL; + } + + if (port->data == NULL) { + mutex_unlock(&usc->cmd_lock); + return 0; + } + + rc = q6usm_memory_unmap(port->phys, dir, usc->session, + *((uint32_t *)port->ext)); + pr_debug("%s: data[%pK]phys[%llx][%pK]\n", __func__, + (void *)port->data, (u64)port->phys, (void *)&port->phys); + + msm_audio_ion_free(port->dma_buf); + + port->data = NULL; + port->phys = 0; + port->buf_size = 0; + port->buf_cnt = 0; + port->dma_buf = NULL; + + mutex_unlock(&usc->cmd_lock); + return rc; +} + +int q6usm_us_param_buf_free(unsigned int dir, + struct us_client *usc) +{ + struct us_port_data *port; + int rc = 0; + + if ((usc == NULL) || + ((dir != IN) && (dir != OUT))) + return -EINVAL; + + mutex_lock(&usc->cmd_lock); + port = &usc->port[dir]; + if (port == NULL) { + mutex_unlock(&usc->cmd_lock); + return -EINVAL; + } + + if (port->param_buf == NULL) { + mutex_unlock(&usc->cmd_lock); + return 0; + } + + rc = q6usm_memory_unmap(port->param_phys, dir, usc->session, + *((uint32_t *)port->param_buf_mem_handle)); + pr_debug("%s: data[%pK]phys[%llx][%pK]\n", __func__, + (void *)port->param_buf, (u64)port->param_phys, + (void *)&port->param_phys); + + msm_audio_ion_free(port->param_dma_buf); + + port->param_buf = NULL; + port->param_phys = 0; + port->param_buf_size = 0; + port->param_dma_buf = NULL; + + mutex_unlock(&usc->cmd_lock); + return rc; +} + +void q6usm_us_client_free(struct us_client *usc) +{ + int loopcnt = 0; + struct us_port_data *port; + uint32_t *p_mem_handle = NULL; + + if ((usc == NULL) || + !(usc->session)) + return; + + for (loopcnt = 0; loopcnt <= OUT; ++loopcnt) { + port = &usc->port[loopcnt]; + if (port->data == NULL) + continue; + pr_debug("%s: loopcnt = %d\n", __func__, loopcnt); + q6usm_us_client_buf_free(loopcnt, usc); + q6usm_us_param_buf_free(loopcnt, usc); + } + q6usm_session_free(usc); + apr_deregister(usc->apr); + + pr_debug("%s: APR De-Register\n", __func__); + + if (atomic_read(&this_mmap.ref_cnt) <= 0) { + pr_err("%s: APR Common Port Already Closed\n", __func__); + goto done; + } + + atomic_dec(&this_mmap.ref_cnt); + if (atomic_read(&this_mmap.ref_cnt) == 0) { + apr_deregister(this_mmap.apr); + pr_debug("%s: APR De-Register common port\n", __func__); + } + +done: + p_mem_handle = (uint32_t *)usc->port[IN].ext; + kfree(p_mem_handle); + kfree(usc); + pr_debug("%s:\n", __func__); +} + +struct us_client *q6usm_us_client_alloc( + void (*cb)(uint32_t, uint32_t, uint32_t *, void *), + void *priv) +{ + struct us_client *usc; + uint32_t *p_mem_handle = NULL; + int n; + int lcnt = 0; + + usc = kzalloc(sizeof(struct us_client), GFP_KERNEL); + if (usc == NULL) + return NULL; + + p_mem_handle = kzalloc(sizeof(uint32_t) * 4, GFP_KERNEL); + if (p_mem_handle == NULL) { + kfree(usc); + return NULL; + } + + n = q6usm_session_alloc(usc); + if (n <= 0) + goto fail_session; + usc->session = n; + usc->cb = cb; + usc->priv = priv; + usc->apr = apr_register("ADSP", "USM", + (apr_fn)q6usm_callback, + ((usc->session) << 8 | 0x0001), + usc); + + if (usc->apr == NULL) { + pr_err("%s: Registration with APR failed\n", __func__); + goto fail; + } + pr_debug("%s: Registering the common port with APR\n", __func__); + if (atomic_read(&this_mmap.ref_cnt) == 0) { + this_mmap.apr = apr_register("ADSP", "USM", + (apr_fn)q6usm_mmapcallback, + 0x0FFFFFFFF, &this_mmap); + if (this_mmap.apr == NULL) { + pr_err("%s: USM port registration failed\n", + __func__); + goto fail; + } + } + + atomic_inc(&this_mmap.ref_cnt); + init_waitqueue_head(&usc->cmd_wait); + mutex_init(&usc->cmd_lock); + for (lcnt = 0; lcnt <= OUT; ++lcnt) { + mutex_init(&usc->port[lcnt].lock); + spin_lock_init(&usc->port[lcnt].dsp_lock); + usc->port[lcnt].ext = (void *)p_mem_handle++; + usc->port[lcnt].param_buf_mem_handle = (void *)p_mem_handle++; + pr_err("%s: usc->port[%d].ext=%pK;\n", + __func__, lcnt, usc->port[lcnt].ext); + } + atomic_set(&usc->cmd_state, 0); + + return usc; +fail: + kfree(p_mem_handle); + q6usm_us_client_free(usc); + return NULL; +fail_session: + kfree(p_mem_handle); + kfree(usc); + return NULL; +} + +int q6usm_us_client_buf_alloc(unsigned int dir, + struct us_client *usc, + unsigned int bufsz, + unsigned int bufcnt) +{ + int rc = 0; + struct us_port_data *port = NULL; + unsigned int size = bufsz*bufcnt; + size_t len; + + if ((usc == NULL) || + ((dir != IN) && (dir != OUT)) || (size == 0) || + (usc->session <= 0 || usc->session > USM_SESSION_MAX)) { + pr_err("%s: wrong parameters: size=%d; bufcnt=%d\n", + __func__, size, bufcnt); + return -EINVAL; + } + + mutex_lock(&usc->cmd_lock); + + port = &usc->port[dir]; + + /* The size to allocate should be multiple of 4K bytes */ + size = PAGE_ALIGN(size); + + rc = msm_audio_ion_alloc(&port->dma_buf, + size, &port->phys, + &len, &port->data); + + if (rc) { + pr_err("%s: US ION allocation failed, rc = %d\n", + __func__, rc); + mutex_unlock(&usc->cmd_lock); + return -ENOMEM; + } + + port->buf_cnt = bufcnt; + port->buf_size = bufsz; + pr_debug("%s: data[%pK]; phys[%llx]; [%pK]\n", __func__, + (void *)port->data, + (u64)port->phys, + (void *)&port->phys); + + rc = q6usm_memory_map(port->phys, dir, size, 1, usc->session, + (uint32_t *)port->ext); + if (rc < 0) { + pr_err("%s: CMD Memory_map failed\n", __func__); + mutex_unlock(&usc->cmd_lock); + q6usm_us_client_buf_free(dir, usc); + q6usm_us_param_buf_free(dir, usc); + } else { + mutex_unlock(&usc->cmd_lock); + rc = 0; + } + + return rc; +} + +int q6usm_us_param_buf_alloc(unsigned int dir, + struct us_client *usc, + unsigned int bufsz) +{ + int rc = 0; + struct us_port_data *port = NULL; + unsigned int size = bufsz; + size_t len; + + if ((usc == NULL) || + ((dir != IN) && (dir != OUT)) || + (usc->session <= 0 || usc->session > USM_SESSION_MAX)) { + pr_err("%s: wrong parameters: direction=%d, bufsz=%d\n", + __func__, dir, bufsz); + return -EINVAL; + } + + mutex_lock(&usc->cmd_lock); + + port = &usc->port[dir]; + + if (bufsz == 0) { + pr_debug("%s: bufsz=0, get/set param commands are forbidden\n", + __func__); + port->param_buf = NULL; + mutex_unlock(&usc->cmd_lock); + return rc; + } + + /* The size to allocate should be multiple of 4K bytes */ + size = PAGE_ALIGN(size); + + rc = msm_audio_ion_alloc(&port->param_dma_buf, + size, &port->param_phys, + &len, &port->param_buf); + + if (rc) { + pr_err("%s: US ION allocation failed, rc = %d\n", + __func__, rc); + mutex_unlock(&usc->cmd_lock); + return -ENOMEM; + } + + port->param_buf_size = bufsz; + pr_debug("%s: param_buf[%pK]; param_phys[%llx]; [%pK]\n", __func__, + (void *)port->param_buf, + (u64)port->param_phys, + (void *)&port->param_phys); + + rc = q6usm_memory_map(port->param_phys, (IN | OUT), size, 1, + usc->session, (uint32_t *)port->param_buf_mem_handle); + if (rc < 0) { + pr_err("%s: CMD Memory_map failed\n", __func__); + mutex_unlock(&usc->cmd_lock); + q6usm_us_client_buf_free(dir, usc); + q6usm_us_param_buf_free(dir, usc); + } else { + mutex_unlock(&usc->cmd_lock); + rc = 0; + } + + return rc; +} + +static int32_t q6usm_mmapcallback(struct apr_client_data *data, void *priv) +{ + uint32_t token; + uint32_t *payload = data->payload; + + if (data->payload_size < (2 * sizeof(uint32_t))) { + pr_err("%s: payload has invalid size[%d]\n", __func__, + data->payload_size); + return -EINVAL; + } + pr_debug("%s: ptr0[0x%x]; ptr1[0x%x]; opcode[0x%x]\n", + __func__, payload[0], payload[1], data->opcode); + pr_debug("%s: token[0x%x]; payload_size[%d]; src[%d]; dest[%d];\n", + __func__, data->token, data->payload_size, + data->src_port, data->dest_port); + + if (data->opcode == APR_BASIC_RSP_RESULT) { + /* status field check */ + if (payload[1]) { + pr_err("%s: wrong response[%d] on cmd [%d]\n", + __func__, payload[1], payload[0]); + } else { + token = data->token; + switch (payload[0]) { + case USM_CMD_SHARED_MEM_UNMAP_REGION: + if (atomic_read(&this_mmap.cmd_state)) { + atomic_set(&this_mmap.cmd_state, 0); + wake_up(&this_mmap.cmd_wait); + } + /* fallthrough */ + case USM_CMD_SHARED_MEM_MAP_REGION: + /* For MEM_MAP, additional answer is waited, */ + /* therfore, no wake-up here */ + pr_debug("%s: cmd[0x%x]; result[0x%x]\n", + __func__, payload[0], payload[1]); + break; + default: + pr_debug("%s: wrong command[0x%x]\n", + __func__, payload[0]); + break; + } + } + } else { + if (data->opcode == USM_CMDRSP_SHARED_MEM_MAP_REGION) { + this_mmap.mem_handle = payload[0]; + pr_debug("%s: memory map handle = 0x%x", + __func__, payload[0]); + if (atomic_read(&this_mmap.cmd_state)) { + atomic_set(&this_mmap.cmd_state, 0); + wake_up(&this_mmap.cmd_wait); + } + } + } + return 0; +} + + +static int32_t q6usm_callback(struct apr_client_data *data, void *priv) +{ + struct us_client *usc = (struct us_client *)priv; + unsigned long dsp_flags; + uint32_t *payload = data->payload; + uint32_t token = data->token; + uint32_t opcode = Q6USM_EVENT_UNDEF; + + if (usc == NULL) { + pr_err("%s: client info is NULL\n", __func__); + return -EINVAL; + } + + if (data->opcode == APR_BASIC_RSP_RESULT) { + if (data->payload_size < (2 * sizeof(uint32_t))) { + pr_err("%s: payload has invalid size[%d]\n", __func__, + data->payload_size); + return -EINVAL; + } + /* status field check */ + if (payload[1]) { + pr_err("%s: wrong response[%d] on cmd [%d]\n", + __func__, payload[1], payload[0]); + if (usc->cb) + usc->cb(data->opcode, token, + (uint32_t *)data->payload, usc->priv); + } else { + switch (payload[0]) { + case USM_SESSION_CMD_RUN: + case USM_STREAM_CMD_CLOSE: + if (token != usc->session) { + pr_err("%s: wrong token[%d]", + __func__, token); + break; + } + case USM_STREAM_CMD_OPEN_READ: + case USM_STREAM_CMD_OPEN_WRITE: + case USM_STREAM_CMD_SET_ENC_PARAM: + case USM_DATA_CMD_MEDIA_FORMAT_UPDATE: + case USM_SESSION_CMD_SIGNAL_DETECT_MODE: + case USM_STREAM_CMD_SET_PARAM: + case USM_STREAM_CMD_GET_PARAM: + if (atomic_read(&usc->cmd_state)) { + atomic_set(&usc->cmd_state, 0); + wake_up(&usc->cmd_wait); + } + if (usc->cb) + usc->cb(data->opcode, token, + (uint32_t *)data->payload, + usc->priv); + break; + default: + break; + } + } + return 0; + } + + switch (data->opcode) { + case RESET_EVENTS: { + pr_err("%s: Reset event is received: %d %d\n", + __func__, + data->reset_event, + data->reset_proc); + + opcode = RESET_EVENTS; + + apr_reset(this_mmap.apr); + this_mmap.apr = NULL; + + apr_reset(usc->apr); + usc->apr = NULL; + + break; + } + + + case USM_DATA_EVENT_READ_DONE: { + struct us_port_data *port = &usc->port[OUT]; + + opcode = Q6USM_EVENT_READ_DONE; + spin_lock_irqsave(&port->dsp_lock, dsp_flags); + if (data->payload_size < + (sizeof(uint32_t)*(READDONE_IDX_STATUS + 1))) { + pr_err("%s: Invalid payload size for READDONE[%d]\n", + __func__, data->payload_size); + return -EINVAL; + } + if (payload[READDONE_IDX_STATUS]) { + pr_err("%s: wrong READDONE[%d]; token[%d]\n", + __func__, + payload[READDONE_IDX_STATUS], + token); + token = USM_WRONG_TOKEN; + spin_unlock_irqrestore(&port->dsp_lock, + dsp_flags); + break; + } + + if (port->expected_token != token) { + u32 cpu_buf = port->cpu_buf; + + pr_err("%s: expected[%d] != token[%d]\n", + __func__, port->expected_token, token); + pr_debug("%s: dsp_buf=%d; cpu_buf=%d;\n", + __func__, port->dsp_buf, cpu_buf); + + token = USM_WRONG_TOKEN; + /* To prevent data handle continiue */ + port->expected_token = USM_WRONG_TOKEN; + spin_unlock_irqrestore(&port->dsp_lock, + dsp_flags); + break; + } /* port->expected_token != data->token */ + + port->expected_token = token + 1; + if (port->expected_token == port->buf_cnt) + port->expected_token = 0; + + /* gap support */ + if (port->expected_token != port->cpu_buf) { + port->dsp_buf = port->expected_token; + token = port->dsp_buf; /* for callback */ + } else + port->dsp_buf = token; + + spin_unlock_irqrestore(&port->dsp_lock, dsp_flags); + break; + } /* case USM_DATA_EVENT_READ_DONE */ + + case USM_DATA_EVENT_WRITE_DONE: { + struct us_port_data *port = &usc->port[IN]; + + opcode = Q6USM_EVENT_WRITE_DONE; + if (data->payload_size < + (sizeof(uint32_t)*(WRITEDONE_IDX_STATUS + 1))) { + pr_err("%s: Invalid payload size for WRITEDONE[%d]\n", + __func__, data->payload_size); + return -EINVAL; + } + if (payload[WRITEDONE_IDX_STATUS]) { + pr_err("%s: wrong WRITEDONE_IDX_STATUS[%d]\n", + __func__, + payload[WRITEDONE_IDX_STATUS]); + break; + } + + spin_lock_irqsave(&port->dsp_lock, dsp_flags); + port->dsp_buf = token + 1; + if (port->dsp_buf == port->buf_cnt) + port->dsp_buf = 0; + spin_unlock_irqrestore(&port->dsp_lock, dsp_flags); + + break; + } /* case USM_DATA_EVENT_WRITE_DONE */ + + case USM_SESSION_EVENT_SIGNAL_DETECT_RESULT: { + pr_debug("%s: US detect result: result=%d", + __func__, + payload[0]); + opcode = Q6USM_EVENT_SIGNAL_DETECT_RESULT; + + break; + } /* case USM_SESSION_EVENT_SIGNAL_DETECT_RESULT */ + + default: + return 0; + + } /* switch */ + + if (usc->cb) + usc->cb(opcode, token, + data->payload, usc->priv); + + return 0; +} + +uint32_t q6usm_get_virtual_address(int dir, + struct us_client *usc, + struct vm_area_struct *vms) +{ + uint32_t ret = 0xffffffff; + + if (vms && (usc != NULL) && ((dir == IN) || (dir == OUT))) { + struct us_port_data *port = &usc->port[dir]; + int size = PAGE_ALIGN(port->buf_size * port->buf_cnt); + struct audio_buffer ab; + + ab.phys = port->phys; + ab.data = port->data; + ab.used = 1; + ab.size = size; + ab.actual_size = size; + ab.dma_buf = port->dma_buf; + + ret = msm_audio_ion_mmap(&ab, vms); + + } + return ret; +} + +static void q6usm_add_hdr(struct us_client *usc, struct apr_hdr *hdr, + uint32_t pkt_size, bool cmd_flg) +{ + mutex_lock(&usc->cmd_lock); + hdr->hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(sizeof(struct apr_hdr)), + APR_PKT_VER); + hdr->src_svc = ((struct apr_svc *)usc->apr)->id; + hdr->src_domain = APR_DOMAIN_APPS; + hdr->dest_svc = APR_SVC_USM; + hdr->dest_domain = APR_DOMAIN_ADSP; + hdr->src_port = (usc->session << 8) | 0x0001; + hdr->dest_port = (usc->session << 8) | 0x0001; + if (cmd_flg) { + hdr->token = usc->session; + atomic_set(&usc->cmd_state, 1); + } + hdr->pkt_size = pkt_size; + mutex_unlock(&usc->cmd_lock); +} + +static uint32_t q6usm_ext2int_format(uint32_t ext_format) +{ + uint32_t int_format = INVALID_FORMAT; + + switch (ext_format) { + case FORMAT_USPS_EPOS: + int_format = US_POINT_EPOS_FORMAT_V2; + break; + case FORMAT_USRAW: + int_format = US_RAW_FORMAT_V2; + break; + case FORMAT_USPROX: + int_format = US_PROX_FORMAT_V4; + break; + case FORMAT_USGES_SYNC: + int_format = US_GES_SYNC_FORMAT; + break; + case FORMAT_USRAW_SYNC: + int_format = US_RAW_SYNC_FORMAT; + break; + default: + pr_err("%s: Invalid format[%d]\n", __func__, ext_format); + break; + } + + return int_format; +} + +int q6usm_open_read(struct us_client *usc, + uint32_t format) +{ + uint32_t int_format = INVALID_FORMAT; + int rc = 0x00; + struct usm_stream_cmd_open_read open; + + if ((usc == NULL) || (usc->apr == NULL)) { + pr_err("%s: client or its apr is NULL\n", __func__); + return -EINVAL; + } + + pr_debug("%s: session[%d]", __func__, usc->session); + + q6usm_add_hdr(usc, &open.hdr, sizeof(open), true); + open.hdr.opcode = USM_STREAM_CMD_OPEN_READ; + open.src_endpoint = 0; /* AFE */ + open.pre_proc_top = 0; /* No preprocessing required */ + + int_format = q6usm_ext2int_format(format); + if (int_format == INVALID_FORMAT) + return -EINVAL; + + open.uMode = STREAM_PRIORITY_NORMAL; + open.format = int_format; + + rc = apr_send_pkt(usc->apr, (uint32_t *) &open); + if (rc < 0) { + pr_err("%s: open failed op[0x%x]rc[%d]\n", + __func__, open.hdr.opcode, rc); + goto fail_cmd; + } + rc = wait_event_timeout(usc->cmd_wait, + (atomic_read(&usc->cmd_state) == 0), + Q6USM_TIMEOUT_JIFFIES); + if (!rc) { + rc = -ETIME; + pr_err("%s: timeout, waited for OPEN_READ rc[%d]\n", + __func__, rc); + goto fail_cmd; + } else + rc = 0; +fail_cmd: + return rc; +} + + +int q6usm_enc_cfg_blk(struct us_client *usc, struct us_encdec_cfg *us_cfg) +{ + uint32_t int_format = INVALID_FORMAT; + struct usm_stream_cmd_encdec_cfg_blk enc_cfg_obj; + struct usm_stream_cmd_encdec_cfg_blk *enc_cfg = &enc_cfg_obj; + int rc = 0; + uint32_t total_cfg_size = + sizeof(struct usm_stream_cmd_encdec_cfg_blk); + uint32_t round_params_size = 0; + uint8_t is_allocated = 0; + + + if ((usc == NULL) || (us_cfg == NULL)) { + pr_err("%s: wrong input", __func__); + return -EINVAL; + } + + int_format = q6usm_ext2int_format(us_cfg->format_id); + if (int_format == INVALID_FORMAT) { + pr_err("%s: wrong input format[%d]", + __func__, us_cfg->format_id); + return -EINVAL; + } + + /* Transparent configuration data is after enc_cfg */ + /* Integer number of u32s is required */ + round_params_size = ((us_cfg->params_size + 3)/4) * 4; + if (round_params_size > USM_MAX_CFG_DATA_SIZE) { + /* Dynamic allocated encdec_cfg_blk is required */ + /* static part use */ + round_params_size -= USM_MAX_CFG_DATA_SIZE; + total_cfg_size += round_params_size; + enc_cfg = kzalloc(total_cfg_size, GFP_KERNEL); + if (enc_cfg == NULL) { + pr_err("%s: enc_cfg[%d] allocation failed\n", + __func__, total_cfg_size); + return -ENOMEM; + } + is_allocated = 1; + } else + round_params_size = 0; + + q6usm_add_hdr(usc, &enc_cfg->hdr, total_cfg_size, true); + + enc_cfg->hdr.opcode = USM_STREAM_CMD_SET_ENC_PARAM; + enc_cfg->param_id = USM_PARAM_ID_ENCDEC_ENC_CFG_BLK; + enc_cfg->param_size = sizeof(struct usm_encode_cfg_blk)+ + round_params_size; + enc_cfg->enc_blk.frames_per_buf = 1; + enc_cfg->enc_blk.format_id = int_format; + enc_cfg->enc_blk.cfg_size = sizeof(struct usm_cfg_common)+ + USM_MAX_CFG_DATA_SIZE + + round_params_size; + memcpy(&(enc_cfg->enc_blk.cfg_common), &(us_cfg->cfg_common), + sizeof(struct usm_cfg_common)); + + /* Transparent data copy */ + memcpy(enc_cfg->enc_blk.transp_data, us_cfg->params, + us_cfg->params_size); + pr_debug("%s: cfg_size[%d], params_size[%d]\n", + __func__, + enc_cfg->enc_blk.cfg_size, + us_cfg->params_size); + pr_debug("%s: params[%d,%d,%d,%d, %d,%d,%d,%d]\n", + __func__, + enc_cfg->enc_blk.transp_data[0], + enc_cfg->enc_blk.transp_data[1], + enc_cfg->enc_blk.transp_data[2], + enc_cfg->enc_blk.transp_data[3], + enc_cfg->enc_blk.transp_data[4], + enc_cfg->enc_blk.transp_data[5], + enc_cfg->enc_blk.transp_data[6], + enc_cfg->enc_blk.transp_data[7] + ); + pr_debug("%s: srate:%d, ch=%d, bps= %d;\n", + __func__, enc_cfg->enc_blk.cfg_common.sample_rate, + enc_cfg->enc_blk.cfg_common.ch_cfg, + enc_cfg->enc_blk.cfg_common.bits_per_sample); + pr_debug("dmap:[0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x]; dev_id=0x%x\n", + enc_cfg->enc_blk.cfg_common.data_map[0], + enc_cfg->enc_blk.cfg_common.data_map[1], + enc_cfg->enc_blk.cfg_common.data_map[2], + enc_cfg->enc_blk.cfg_common.data_map[3], + enc_cfg->enc_blk.cfg_common.data_map[4], + enc_cfg->enc_blk.cfg_common.data_map[5], + enc_cfg->enc_blk.cfg_common.data_map[6], + enc_cfg->enc_blk.cfg_common.data_map[7], + enc_cfg->enc_blk.cfg_common.dev_id); + + rc = apr_send_pkt(usc->apr, (uint32_t *) enc_cfg); + if (rc < 0) { + pr_err("%s:Comamnd open failed\n", __func__); + rc = -EINVAL; + goto fail_cmd; + } + rc = wait_event_timeout(usc->cmd_wait, + (atomic_read(&usc->cmd_state) == 0), + Q6USM_TIMEOUT_JIFFIES); + if (!rc) { + rc = -ETIME; + pr_err("%s: timeout opcode[0x%x]\n", + __func__, enc_cfg->hdr.opcode); + } else + rc = 0; + +fail_cmd: + if (is_allocated == 1) + kfree(enc_cfg); + + return rc; +} + +int q6usm_dec_cfg_blk(struct us_client *usc, struct us_encdec_cfg *us_cfg) +{ + + uint32_t int_format = INVALID_FORMAT; + struct usm_stream_media_format_update dec_cfg_obj; + struct usm_stream_media_format_update *dec_cfg = &dec_cfg_obj; + + int rc = 0; + uint32_t total_cfg_size = sizeof(struct usm_stream_media_format_update); + uint32_t round_params_size = 0; + uint8_t is_allocated = 0; + + + if ((usc == NULL) || (us_cfg == NULL)) { + pr_err("%s: wrong input", __func__); + return -EINVAL; + } + + int_format = q6usm_ext2int_format(us_cfg->format_id); + if (int_format == INVALID_FORMAT) { + pr_err("%s: wrong input format[%d]", + __func__, us_cfg->format_id); + return -EINVAL; + } + + /* Transparent configuration data is after enc_cfg */ + /* Integer number of u32s is required */ + round_params_size = ((us_cfg->params_size + 3)/4) * 4; + if (round_params_size > USM_MAX_CFG_DATA_SIZE) { + /* Dynamic allocated encdec_cfg_blk is required */ + /* static part use */ + round_params_size -= USM_MAX_CFG_DATA_SIZE; + total_cfg_size += round_params_size; + dec_cfg = kzalloc(total_cfg_size, GFP_KERNEL); + if (dec_cfg == NULL) { + pr_err("%s:dec_cfg[%d] allocation failed\n", + __func__, total_cfg_size); + return -ENOMEM; + } + is_allocated = 1; + } else { /* static transp_data is enough */ + round_params_size = 0; + } + + q6usm_add_hdr(usc, &dec_cfg->hdr, total_cfg_size, true); + + dec_cfg->hdr.opcode = USM_DATA_CMD_MEDIA_FORMAT_UPDATE; + dec_cfg->format_id = int_format; + dec_cfg->cfg_size = sizeof(struct usm_cfg_common) + + USM_MAX_CFG_DATA_SIZE + + round_params_size; + memcpy(&(dec_cfg->cfg_common), &(us_cfg->cfg_common), + sizeof(struct usm_cfg_common)); + /* Transparent data copy */ + memcpy(dec_cfg->transp_data, us_cfg->params, us_cfg->params_size); + pr_debug("%s: cfg_size[%d], params_size[%d]; parambytes[%d,%d,%d,%d]\n", + __func__, + dec_cfg->cfg_size, + us_cfg->params_size, + dec_cfg->transp_data[0], + dec_cfg->transp_data[1], + dec_cfg->transp_data[2], + dec_cfg->transp_data[3] + ); + + rc = apr_send_pkt(usc->apr, (uint32_t *) dec_cfg); + if (rc < 0) { + pr_err("%s:Comamnd open failed\n", __func__); + rc = -EINVAL; + goto fail_cmd; + } + rc = wait_event_timeout(usc->cmd_wait, + (atomic_read(&usc->cmd_state) == 0), + Q6USM_TIMEOUT_JIFFIES); + if (!rc) { + rc = -ETIME; + pr_err("%s: timeout opcode[0x%x]\n", + __func__, dec_cfg->hdr.opcode); + } else + rc = 0; + +fail_cmd: + if (is_allocated == 1) + kfree(dec_cfg); + + return rc; +} + +int q6usm_open_write(struct us_client *usc, + uint32_t format) +{ + int rc = 0; + uint32_t int_format = INVALID_FORMAT; + struct usm_stream_cmd_open_write open; + + if ((usc == NULL) || (usc->apr == NULL)) { + pr_err("%s: APR handle NULL\n", __func__); + return -EINVAL; + } + + pr_debug("%s: session[%d]", __func__, usc->session); + + q6usm_add_hdr(usc, &open.hdr, sizeof(open), true); + open.hdr.opcode = USM_STREAM_CMD_OPEN_WRITE; + + int_format = q6usm_ext2int_format(format); + if (int_format == INVALID_FORMAT) { + pr_err("%s: wrong format[%d]", __func__, format); + return -EINVAL; + } + + open.format = int_format; + + rc = apr_send_pkt(usc->apr, (uint32_t *) &open); + if (rc < 0) { + pr_err("%s:open failed op[0x%x]rc[%d]\n", + __func__, open.hdr.opcode, rc); + goto fail_cmd; + } + rc = wait_event_timeout(usc->cmd_wait, + (atomic_read(&usc->cmd_state) == 0), + Q6USM_TIMEOUT_JIFFIES); + if (!rc) { + rc = -ETIME; + pr_err("%s:timeout. waited for OPEN_WRITR rc[%d]\n", + __func__, rc); + goto fail_cmd; + } else + rc = 0; + +fail_cmd: + return rc; +} + +int q6usm_run(struct us_client *usc, uint32_t flags, + uint32_t msw_ts, uint32_t lsw_ts) +{ + struct usm_stream_cmd_run run; + int rc = 0; + + if ((usc == NULL) || (usc->apr == NULL)) { + pr_err("%s: APR handle NULL\n", __func__); + return -EINVAL; + } + q6usm_add_hdr(usc, &run.hdr, sizeof(run), true); + + run.hdr.opcode = USM_SESSION_CMD_RUN; + run.flags = flags; + run.msw_ts = msw_ts; + run.lsw_ts = lsw_ts; + + rc = apr_send_pkt(usc->apr, (uint32_t *) &run); + if (rc < 0) { + pr_err("%s: Commmand run failed[%d]\n", __func__, rc); + goto fail_cmd; + } + + rc = wait_event_timeout(usc->cmd_wait, + (atomic_read(&usc->cmd_state) == 0), + Q6USM_TIMEOUT_JIFFIES); + if (!rc) { + rc = -ETIME; + pr_err("%s: timeout. waited for run success rc[%d]\n", + __func__, rc); + } else + rc = 0; + +fail_cmd: + return rc; +} + + + +int q6usm_read(struct us_client *usc, uint32_t read_ind) +{ + struct usm_stream_cmd_read read; + struct us_port_data *port = NULL; + int rc = 0; + u32 read_counter = 0; + u32 loop_ind = 0; + u64 buf_addr = 0; + + if ((usc == NULL) || (usc->apr == NULL)) { + pr_err("%s: APR handle NULL\n", __func__); + return -EINVAL; + } + port = &usc->port[OUT]; + + if (read_ind > port->buf_cnt) { + pr_err("%s: wrong read_ind[%d]\n", + __func__, read_ind); + return -EINVAL; + } + if (read_ind == port->cpu_buf) { + pr_err("%s: no free region\n", __func__); + return 0; + } + + if (read_ind > port->cpu_buf) { /* 1 range */ + read_counter = read_ind - port->cpu_buf; + } else { /* 2 ranges */ + read_counter = (port->buf_cnt - port->cpu_buf) + read_ind; + } + + q6usm_add_hdr(usc, &read.hdr, sizeof(read), false); + + read.hdr.opcode = USM_DATA_CMD_READ; + read.buf_size = port->buf_size; + buf_addr = (u64)(port->phys) + port->buf_size * (port->cpu_buf); + read.buf_addr_lsw = lower_32_bits(buf_addr); + read.buf_addr_msw = msm_audio_populate_upper_32_bits(buf_addr); + read.mem_map_handle = *((uint32_t *)(port->ext)); + + for (loop_ind = 0; loop_ind < read_counter; ++loop_ind) { + u32 temp_cpu_buf = port->cpu_buf; + + buf_addr = (u64)(port->phys) + + port->buf_size * (port->cpu_buf); + read.buf_addr_lsw = lower_32_bits(buf_addr); + read.buf_addr_msw = msm_audio_populate_upper_32_bits(buf_addr); + read.seq_id = port->cpu_buf; + read.hdr.token = port->cpu_buf; + read.counter = 1; + + ++(port->cpu_buf); + if (port->cpu_buf == port->buf_cnt) + port->cpu_buf = 0; + + rc = apr_send_pkt(usc->apr, (uint32_t *) &read); + + if (rc < 0) { + port->cpu_buf = temp_cpu_buf; + + pr_err("%s:read op[0x%x]rc[%d]\n", + __func__, read.hdr.opcode, rc); + break; + } + + rc = 0; + } /* bufs loop */ + + return rc; +} + +int q6usm_write(struct us_client *usc, uint32_t write_ind) +{ + int rc = 0; + struct usm_stream_cmd_write cmd_write; + struct us_port_data *port = NULL; + u32 current_dsp_buf = 0; + u64 buf_addr = 0; + + if ((usc == NULL) || (usc->apr == NULL)) { + pr_err("%s: APR handle NULL\n", __func__); + return -EINVAL; + } + port = &usc->port[IN]; + + current_dsp_buf = port->dsp_buf; + /* free region, caused by new dsp_buf report from DSP, */ + /* can be only extended */ + if (port->cpu_buf >= current_dsp_buf) { + /* 2 -part free region, including empty buffer */ + if ((write_ind <= port->cpu_buf) && + (write_ind > current_dsp_buf)) { + pr_err("%s: wrong w_ind[%d]; d_buf=%d; c_buf=%d\n", + __func__, write_ind, + current_dsp_buf, port->cpu_buf); + return -EINVAL; + } + } else { + /* 1 -part free region */ + if ((write_ind <= port->cpu_buf) || + (write_ind > current_dsp_buf)) { + pr_err("%s: wrong w_ind[%d]; d_buf=%d; c_buf=%d\n", + __func__, write_ind, + current_dsp_buf, port->cpu_buf); + return -EINVAL; + } + } + + q6usm_add_hdr(usc, &cmd_write.hdr, sizeof(cmd_write), false); + + cmd_write.hdr.opcode = USM_DATA_CMD_WRITE; + cmd_write.buf_size = port->buf_size; + buf_addr = (u64)(port->phys) + port->buf_size * (port->cpu_buf); + cmd_write.buf_addr_lsw = lower_32_bits(buf_addr); + cmd_write.buf_addr_msw = msm_audio_populate_upper_32_bits(buf_addr); + cmd_write.mem_map_handle = *((uint32_t *)(port->ext)); + cmd_write.res0 = 0; + cmd_write.res1 = 0; + cmd_write.res2 = 0; + + while (port->cpu_buf != write_ind) { + u32 temp_cpu_buf = port->cpu_buf; + + buf_addr = (u64)(port->phys) + + port->buf_size * (port->cpu_buf); + cmd_write.buf_addr_lsw = lower_32_bits(buf_addr); + cmd_write.buf_addr_msw = + msm_audio_populate_upper_32_bits(buf_addr); + cmd_write.seq_id = port->cpu_buf; + cmd_write.hdr.token = port->cpu_buf; + + ++(port->cpu_buf); + if (port->cpu_buf == port->buf_cnt) + port->cpu_buf = 0; + + rc = apr_send_pkt(usc->apr, (uint32_t *) &cmd_write); + + if (rc < 0) { + port->cpu_buf = temp_cpu_buf; + pr_err("%s:write op[0x%x];rc[%d];cpu_buf[%d]\n", + __func__, cmd_write.hdr.opcode, + rc, port->cpu_buf); + break; + } + + rc = 0; + } + + return rc; +} + +bool q6usm_is_write_buf_full(struct us_client *usc, uint32_t *free_region) +{ + struct us_port_data *port = NULL; + u32 cpu_buf = 0; + + if ((usc == NULL) || !free_region) { + pr_err("%s: input data wrong\n", __func__); + return false; + } + port = &usc->port[IN]; + cpu_buf = port->cpu_buf + 1; + if (cpu_buf == port->buf_cnt) + cpu_buf = 0; + + *free_region = port->dsp_buf; + + return cpu_buf == *free_region; +} + +int q6usm_cmd(struct us_client *usc, int cmd) +{ + struct apr_hdr hdr; + int rc = 0; + atomic_t *state; + + if ((usc == NULL) || (usc->apr == NULL)) { + pr_err("%s: APR handle NULL\n", __func__); + return -EINVAL; + } + q6usm_add_hdr(usc, &hdr, sizeof(hdr), true); + switch (cmd) { + case CMD_CLOSE: + hdr.opcode = USM_STREAM_CMD_CLOSE; + state = &usc->cmd_state; + break; + + default: + pr_err("%s:Invalid format[%d]\n", __func__, cmd); + goto fail_cmd; + } + + rc = apr_send_pkt(usc->apr, (uint32_t *) &hdr); + if (rc < 0) { + pr_err("%s: Command 0x%x failed\n", __func__, hdr.opcode); + goto fail_cmd; + } + rc = wait_event_timeout(usc->cmd_wait, (atomic_read(state) == 0), + Q6USM_TIMEOUT_JIFFIES); + if (!rc) { + rc = -ETIME; + pr_err("%s:timeout. waited for response opcode[0x%x]\n", + __func__, hdr.opcode); + } else + rc = 0; +fail_cmd: + return rc; +} + +int q6usm_set_us_detection(struct us_client *usc, + struct usm_session_cmd_detect_info *detect_info, + uint16_t detect_info_size) +{ + int rc = 0; + + if ((usc == NULL) || + (detect_info_size == 0) || + (detect_info == NULL)) { + pr_err("%s: wrong input: usc=0x%pK, inf_size=%d; info=0x%pK", + __func__, + usc, + detect_info_size, + detect_info); + return -EINVAL; + } + + q6usm_add_hdr(usc, &detect_info->hdr, detect_info_size, true); + + detect_info->hdr.opcode = USM_SESSION_CMD_SIGNAL_DETECT_MODE; + + rc = apr_send_pkt(usc->apr, (uint32_t *)detect_info); + if (rc < 0) { + pr_err("%s:Comamnd signal detect failed\n", __func__); + return -EINVAL; + } + rc = wait_event_timeout(usc->cmd_wait, + (atomic_read(&usc->cmd_state) == 0), + Q6USM_TIMEOUT_JIFFIES); + if (!rc) { + rc = -ETIME; + pr_err("%s: CMD_SIGNAL_DETECT_MODE: timeout=%d\n", + __func__, Q6USM_TIMEOUT_JIFFIES); + } else + rc = 0; + + return rc; +} + +int q6usm_set_us_stream_param(int dir, struct us_client *usc, + uint32_t module_id, uint32_t param_id, uint32_t buf_size) +{ + int rc = 0; + struct usm_stream_cmd_set_param cmd_set_param; + struct us_port_data *port = NULL; + + if ((usc == NULL) || (usc->apr == NULL)) { + pr_err("%s: APR handle NULL\n", __func__); + return -EINVAL; + } + port = &usc->port[dir]; + + q6usm_add_hdr(usc, &cmd_set_param.hdr, sizeof(cmd_set_param), true); + + cmd_set_param.hdr.opcode = USM_STREAM_CMD_SET_PARAM; + cmd_set_param.buf_size = buf_size; + cmd_set_param.buf_addr_msw = + msm_audio_populate_upper_32_bits(port->param_phys); + cmd_set_param.buf_addr_lsw = lower_32_bits(port->param_phys); + cmd_set_param.mem_map_handle = + *((uint32_t *)(port->param_buf_mem_handle)); + cmd_set_param.module_id = module_id; + cmd_set_param.param_id = param_id; + cmd_set_param.hdr.token = 0; + + rc = apr_send_pkt(usc->apr, (uint32_t *) &cmd_set_param); + + if (rc < 0) { + pr_err("%s:write op[0x%x];rc[%d]\n", + __func__, cmd_set_param.hdr.opcode, rc); + } + + rc = wait_event_timeout(usc->cmd_wait, + (atomic_read(&usc->cmd_state) == 0), + Q6USM_TIMEOUT_JIFFIES); + if (!rc) { + rc = -ETIME; + pr_err("%s: CMD_SET_PARAM: timeout=%d\n", + __func__, Q6USM_TIMEOUT_JIFFIES); + } else + rc = 0; + + return rc; +} + +int q6usm_get_us_stream_param(int dir, struct us_client *usc, + uint32_t module_id, uint32_t param_id, uint32_t buf_size) +{ + int rc = 0; + struct usm_stream_cmd_get_param cmd_get_param; + struct us_port_data *port = NULL; + + if ((usc == NULL) || (usc->apr == NULL)) { + pr_err("%s: APR handle NULL\n", __func__); + return -EINVAL; + } + port = &usc->port[dir]; + + q6usm_add_hdr(usc, &cmd_get_param.hdr, sizeof(cmd_get_param), true); + + cmd_get_param.hdr.opcode = USM_STREAM_CMD_GET_PARAM; + cmd_get_param.buf_size = buf_size; + cmd_get_param.buf_addr_msw = + msm_audio_populate_upper_32_bits(port->param_phys); + cmd_get_param.buf_addr_lsw = lower_32_bits(port->param_phys); + cmd_get_param.mem_map_handle = + *((uint32_t *)(port->param_buf_mem_handle)); + cmd_get_param.module_id = module_id; + cmd_get_param.param_id = param_id; + cmd_get_param.hdr.token = 0; + + rc = apr_send_pkt(usc->apr, (uint32_t *) &cmd_get_param); + + if (rc < 0) { + pr_err("%s:write op[0x%x];rc[%d]\n", + __func__, cmd_get_param.hdr.opcode, rc); + } + + rc = wait_event_timeout(usc->cmd_wait, + (atomic_read(&usc->cmd_state) == 0), + Q6USM_TIMEOUT_JIFFIES); + if (!rc) { + rc = -ETIME; + pr_err("%s: CMD_GET_PARAM: timeout=%d\n", + __func__, Q6USM_TIMEOUT_JIFFIES); + } else + rc = 0; + + return rc; +} + +int __init q6usm_init(void) +{ + pr_debug("%s\n", __func__); + init_waitqueue_head(&this_mmap.cmd_wait); + memset(session, 0, sizeof(session)); + return 0; +} diff --git a/audio-kernel/dsp/q6usm.h b/audio-kernel/dsp/q6usm.h new file mode 100644 index 000000000..ba4f9a9a8 --- /dev/null +++ b/audio-kernel/dsp/q6usm.h @@ -0,0 +1,127 @@ +/* Copyright (c) 2011-2014, 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 __Q6_USM_H__ +#define __Q6_USM_H__ + +#include + +#define Q6USM_EVENT_UNDEF 0 +#define Q6USM_EVENT_READ_DONE 1 +#define Q6USM_EVENT_WRITE_DONE 2 +#define Q6USM_EVENT_SIGNAL_DETECT_RESULT 3 + +/* cyclic buffer with 1 gap support */ +#define USM_MIN_BUF_CNT 3 + +#define FORMAT_USPS_EPOS 0x00000000 +#define FORMAT_USRAW 0x00000001 +#define FORMAT_USPROX 0x00000002 +#define FORMAT_USGES_SYNC 0x00000003 +#define FORMAT_USRAW_SYNC 0x00000004 +#define INVALID_FORMAT 0xffffffff + +#define IN 0x000 +#define OUT 0x001 + +#define USM_WRONG_TOKEN 0xffffffff +#define USM_UNDEF_TOKEN 0xfffffffe + +#define CMD_CLOSE 0x0004 + +/* bit 0:1 represents priority of stream */ +#define STREAM_PRIORITY_NORMAL 0x0000 +#define STREAM_PRIORITY_LOW 0x0001 +#define STREAM_PRIORITY_HIGH 0x0002 + +/* bit 4 represents META enable of encoded data buffer */ +#define BUFFER_META_ENABLE 0x0010 + +struct us_port_data { + dma_addr_t phys; + /* cyclic region of buffers with 1 gap */ + void *data; + /* number of buffers in the region */ + uint32_t buf_cnt; + /* size of buffer */ + size_t buf_size; + /* write index */ + uint32_t dsp_buf; + /* read index */ + uint32_t cpu_buf; + /* expected token from dsp */ + uint32_t expected_token; + /* read or write locks */ + struct mutex lock; + spinlock_t dsp_lock; + /* ION dma_buf memory */ + struct dma_buf *dma_buf; + /* extended parameters, related to q6 variants */ + void *ext; + /* physical address of parameter buffer */ + dma_addr_t param_phys; + /* buffer which stores the parameter data */ + void *param_buf; + /* size of parameter buffer */ + uint32_t param_buf_size; + /* parameter buffer memory handle */ + void *param_buf_mem_handle; + /* ION dma_buf memory for parameter buffer */ + struct dma_buf *param_dma_buf; +}; + +struct us_client { + int session; + /* idx:1 out port, 0: in port*/ + struct us_port_data port[2]; + + struct apr_svc *apr; + struct mutex cmd_lock; + + atomic_t cmd_state; + atomic_t eos_state; + wait_queue_head_t cmd_wait; + + void (*cb)(uint32_t, uint32_t, uint32_t *, void *); + void *priv; +}; + +int q6usm_run(struct us_client *usc, uint32_t flags, + uint32_t msw_ts, uint32_t lsw_ts); +int q6usm_cmd(struct us_client *usc, int cmd); +int q6usm_us_client_buf_alloc(unsigned int dir, struct us_client *usc, + unsigned int bufsz, unsigned int bufcnt); +int q6usm_us_param_buf_alloc(unsigned int dir, struct us_client *usc, + unsigned int bufsz); +int q6usm_enc_cfg_blk(struct us_client *usc, struct us_encdec_cfg *us_cfg); +int q6usm_dec_cfg_blk(struct us_client *usc, struct us_encdec_cfg *us_cfg); +int q6usm_read(struct us_client *usc, uint32_t read_ind); +struct us_client *q6usm_us_client_alloc( + void (*cb)(uint32_t, uint32_t, uint32_t *, void *), + void *priv); +int q6usm_open_read(struct us_client *usc, uint32_t format); +void q6usm_us_client_free(struct us_client *usc); +uint32_t q6usm_get_virtual_address(int dir, struct us_client *usc, + struct vm_area_struct *vms); +int q6usm_open_write(struct us_client *usc, uint32_t format); +int q6usm_write(struct us_client *usc, uint32_t write_ind); +bool q6usm_is_write_buf_full(struct us_client *usc, uint32_t *free_region); +int q6usm_set_us_detection(struct us_client *usc, + struct usm_session_cmd_detect_info *detect_info, + uint16_t detect_info_size); +int q6usm_set_us_stream_param(int dir, struct us_client *usc, + uint32_t module_id, uint32_t param_id, uint32_t buf_size); +int q6usm_get_us_stream_param(int dir, struct us_client *usc, + uint32_t module_id, uint32_t param_id, uint32_t buf_size); +int q6usm_init(void); + +#endif /* __Q6_USM_H__ */ diff --git a/audio-kernel/dsp/q6voice.c b/audio-kernel/dsp/q6voice.c new file mode 100644 index 000000000..4ce41e4c9 --- /dev/null +++ b/audio-kernel/dsp/q6voice.c @@ -0,0 +1,10004 @@ +/* Copyright (c) 2012-2019, 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 +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "adsp_err.h" +#include + +#define TIMEOUT_MS 300 + + +#define CMD_STATUS_SUCCESS 0 +#define CMD_STATUS_FAIL 1 +#define NUM_CHANNELS_MONO 1 +#define NUM_CHANNELS_STEREO 2 +#define NUM_CHANNELS_THREE 3 +#define NUM_CHANNELS_QUAD 4 +#define CVP_VERSION_2 2 +#define GAIN_Q14_FORMAT(a) (a << 14) + +enum { + VOC_TOKEN_NONE, + VOIP_MEM_MAP_TOKEN, + VOC_CAL_MEM_MAP_TOKEN, + VOC_VOICE_HOST_PCM_MAP_TOKEN, + VOC_RTAC_MEM_MAP_TOKEN, + VOC_SOURCE_TRACKING_MEM_MAP_TOKEN +}; + +struct cvd_version_table cvd_version_table_mapping[CVD_INT_VERSION_MAX] = { + {CVD_VERSION_DEFAULT, CVD_INT_VERSION_DEFAULT}, + {CVD_VERSION_0_0, CVD_INT_VERSION_0_0}, + {CVD_VERSION_2_1, CVD_INT_VERSION_2_1}, + {CVD_VERSION_2_2, CVD_INT_VERSION_2_2}, + {CVD_VERSION_2_3, CVD_INT_VERSION_2_3}, +}; + +static struct common_data common; +static bool module_initialized; + +static int voice_send_enable_vocproc_cmd(struct voice_data *v); +static int voice_send_netid_timing_cmd(struct voice_data *v); +static int voice_send_attach_vocproc_cmd(struct voice_data *v); +static int voice_send_set_device_cmd(struct voice_data *v); +static int voice_send_vol_step_cmd(struct voice_data *v); +static int voice_send_mvm_unmap_memory_physical_cmd(struct voice_data *v, + uint32_t mem_handle); +static int voice_send_mvm_cal_network_cmd(struct voice_data *v); +static int voice_send_mvm_media_type_cmd(struct voice_data *v); +static int voice_send_mvm_cvd_version_cmd(struct voice_data *v); +static int voice_send_mvm_event_class_cmd(struct voice_data *v, + uint32_t event_id, + uint32_t class_id); +static int voice_send_cvs_data_exchange_mode_cmd(struct voice_data *v); +static int voice_send_cvs_packet_exchange_config_cmd(struct voice_data *v); +static int voice_set_packet_exchange_mode_and_config(uint32_t session_id, + uint32_t mode); + +static int voice_send_cvs_register_cal_cmd(struct voice_data *v); +static int voice_send_cvs_deregister_cal_cmd(struct voice_data *v); +static int voice_send_cvp_create_cmd(struct voice_data *v); +static int voice_send_cvp_register_dev_cfg_cmd(struct voice_data *v); +static int voice_send_cvp_deregister_dev_cfg_cmd(struct voice_data *v); +static int voice_send_cvp_register_cal_cmd(struct voice_data *v); +static int voice_send_cvp_deregister_cal_cmd(struct voice_data *v); +static int voice_send_cvp_register_vol_cal_cmd(struct voice_data *v); +static int voice_send_cvp_deregister_vol_cal_cmd(struct voice_data *v); +static int voice_send_cvp_media_fmt_info_cmd(struct voice_data *v); +static int voice_send_cvp_device_channels_cmd(struct voice_data *v); +static int voice_send_cvp_media_format_cmd(struct voice_data *v, + uint32_t param_type); +static int voice_send_cvp_topology_commit_cmd(struct voice_data *v); +static int voice_send_cvp_channel_info_cmd(struct voice_data *v); +static int voice_send_cvp_channel_info_v2(struct voice_data *v, + uint32_t param_type); +static int voice_get_avcs_version_per_service(uint32_t service_id); + +static void voice_load_topo_modules(int cal_index); +static void voice_unload_topo_modules(void); + +static int voice_cvs_stop_playback(struct voice_data *v); +static int voice_cvs_start_playback(struct voice_data *v); +static int voice_cvs_start_record(struct voice_data *v, uint32_t rec_mode); +static int voice_cvs_stop_record(struct voice_data *v); + +static int32_t qdsp_mvm_callback(struct apr_client_data *data, void *priv); +static int32_t qdsp_cvs_callback(struct apr_client_data *data, void *priv); +static int32_t qdsp_cvp_callback(struct apr_client_data *data, void *priv); + +static int voice_send_set_pp_enable_cmd( + struct voice_data *v, struct module_instance_info mod_inst_info, + int enable); +static int is_cal_memory_allocated(void); +static bool is_cvd_version_queried(void); +static int is_voip_memory_allocated(void); +static int voice_get_cvd_int_version(char *cvd_ver_string); +static int voice_alloc_cal_mem_map_table(void); +static int voice_alloc_rtac_mem_map_table(void); +static int voice_alloc_oob_shared_mem(void); +static int voice_free_oob_shared_mem(void); +static int voice_alloc_oob_mem_table(void); +static int voice_alloc_and_map_oob_mem(struct voice_data *v); + +static struct voice_data *voice_get_session_by_idx(int idx); + +static int remap_cal_data(struct cal_block_data *cal_block, + uint32_t session_id); +static int voice_unmap_cal_memory(int32_t cal_type, + struct cal_block_data *cal_block); + +static int is_source_tracking_shared_memomry_allocated(void); +static int voice_alloc_source_tracking_shared_memory(void); +static int voice_alloc_and_map_source_tracking_shared_memory( + struct voice_data *v); +static int voice_unmap_and_free_source_tracking_shared_memory( + struct voice_data *v); +static int voice_send_set_sound_focus_cmd(struct voice_data *v, + struct sound_focus_param soundFocusData); +static int voice_send_get_sound_focus_cmd(struct voice_data *v, + struct sound_focus_param *soundFocusData); +static int voice_send_get_source_tracking_cmd(struct voice_data *v, + struct source_tracking_param *sourceTrackingData); +static int voice_pack_and_set_cvp_param(struct voice_data *v, + struct param_hdr_v3 param_hdr, + u8 *param_data); +static int voice_pack_and_set_cvs_ui_property(struct voice_data *v, + struct param_hdr_v3 param_hdr, + u8 *param_data); + +static void voice_itr_init(struct voice_session_itr *itr, + u32 session_id) +{ + if (itr == NULL) + return; + itr->session_idx = voice_get_idx_for_session(session_id); + if (session_id == ALL_SESSION_VSID) + itr->cur_idx = 0; + else + itr->cur_idx = itr->session_idx; + +} + +static bool voice_itr_get_next_session(struct voice_session_itr *itr, + struct voice_data **voice) +{ + bool ret = false; + + if (itr == NULL) + return false; + pr_debug("%s : cur idx = %d session idx = %d\n", + __func__, itr->cur_idx, itr->session_idx); + + if (itr->cur_idx <= itr->session_idx) { + ret = true; + *voice = voice_get_session_by_idx(itr->cur_idx); + itr->cur_idx++; + } else { + *voice = NULL; + } + + return ret; +} + +static bool voice_is_valid_session_id(uint32_t session_id) +{ + bool ret = false; + + switch (session_id) { + case VOICE_SESSION_VSID: + case VOICE2_SESSION_VSID: + case VOLTE_SESSION_VSID: + case VOIP_SESSION_VSID: + case QCHAT_SESSION_VSID: + case VOWLAN_SESSION_VSID: + case VOICEMMODE1_VSID: + case VOICEMMODE2_VSID: + case ALL_SESSION_VSID: + ret = true; + break; + default: + pr_err("%s: Invalid session_id : %x\n", __func__, session_id); + + break; + } + + return ret; +} + +static u16 voice_get_mvm_handle(struct voice_data *v) +{ + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + return 0; + } + + pr_debug("%s: mvm_handle %d\n", __func__, v->mvm_handle); + + return v->mvm_handle; +} + +static void voice_set_mvm_handle(struct voice_data *v, u16 mvm_handle) +{ + pr_debug("%s: mvm_handle %d\n", __func__, mvm_handle); + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + return; + } + + v->mvm_handle = mvm_handle; +} + +static u16 voice_get_cvs_handle(struct voice_data *v) +{ + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + return 0; + } + + pr_debug("%s: cvs_handle %d\n", __func__, v->cvs_handle); + + return v->cvs_handle; +} + +static void voice_set_cvs_handle(struct voice_data *v, u16 cvs_handle) +{ + pr_debug("%s: cvs_handle %d\n", __func__, cvs_handle); + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + return; + } + + v->cvs_handle = cvs_handle; +} + +static u16 voice_get_cvp_handle(struct voice_data *v) +{ + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + return 0; + } + + pr_debug("%s: cvp_handle %d\n", __func__, v->cvp_handle); + + return v->cvp_handle; +} + +static void voice_set_cvp_handle(struct voice_data *v, u16 cvp_handle) +{ + pr_debug("%s: cvp_handle %d\n", __func__, cvp_handle); + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + return; + } + + v->cvp_handle = cvp_handle; +} + +char *voc_get_session_name(u32 session_id) +{ + char *session_name = NULL; + + if (session_id == common.voice[VOC_PATH_PASSIVE].session_id) { + session_name = VOICE_SESSION_NAME; + } else if (session_id == + common.voice[VOC_PATH_VOLTE_PASSIVE].session_id) { + session_name = VOLTE_SESSION_NAME; + } else if (session_id == + common.voice[VOC_PATH_QCHAT_PASSIVE].session_id) { + session_name = QCHAT_SESSION_NAME; + } else if (session_id == + common.voice[VOC_PATH_VOWLAN_PASSIVE].session_id) { + session_name = VOWLAN_SESSION_NAME; + } else if (session_id == + common.voice[VOC_PATH_VOICEMMODE1_PASSIVE].session_id) { + session_name = VOICEMMODE1_NAME; + } else if (session_id == + common.voice[VOC_PATH_VOICEMMODE2_PASSIVE].session_id) { + session_name = VOICEMMODE2_NAME; + } else if (session_id == common.voice[VOC_PATH_FULL].session_id) { + session_name = VOIP_SESSION_NAME; + } + return session_name; +} + +/** + * voc_get_session_id - + * Get session ID of given voice session name + * + * @name: voice session name + * + * Returns session id for valid session or 0 if invalid. + */ +uint32_t voc_get_session_id(char *name) +{ + u32 session_id = 0; + + if (name != NULL) { + if (!strcmp(name, "Voice session")) + session_id = common.voice[VOC_PATH_PASSIVE].session_id; + else if (!strcmp(name, "Voice2 session")) + session_id = + common.voice[VOC_PATH_VOICE2_PASSIVE].session_id; + else if (!strcmp(name, "VoLTE session")) + session_id = + common.voice[VOC_PATH_VOLTE_PASSIVE].session_id; + else if (!strcmp(name, "QCHAT session")) + session_id = + common.voice[VOC_PATH_QCHAT_PASSIVE].session_id; + else if (!strcmp(name, "VoWLAN session")) + session_id = + common.voice[VOC_PATH_VOWLAN_PASSIVE].session_id; + else if (!strcmp(name, "VoiceMMode1")) + session_id = + common.voice[VOC_PATH_VOICEMMODE1_PASSIVE].session_id; + else if (!strcmp(name, "VoiceMMode2")) + session_id = + common.voice[VOC_PATH_VOICEMMODE2_PASSIVE].session_id; + else + session_id = common.voice[VOC_PATH_FULL].session_id; + + pr_debug("%s: %s has session id 0x%x\n", __func__, name, + session_id); + } + + return session_id; +} +EXPORT_SYMBOL(voc_get_session_id); + +static struct voice_data *voice_get_session(u32 session_id) +{ + struct voice_data *v = NULL; + + switch (session_id) { + case VOICE_SESSION_VSID: + v = &common.voice[VOC_PATH_PASSIVE]; + break; + + case VOICE2_SESSION_VSID: + v = &common.voice[VOC_PATH_VOICE2_PASSIVE]; + break; + + case VOLTE_SESSION_VSID: + v = &common.voice[VOC_PATH_VOLTE_PASSIVE]; + break; + + case VOIP_SESSION_VSID: + v = &common.voice[VOC_PATH_FULL]; + break; + + case QCHAT_SESSION_VSID: + v = &common.voice[VOC_PATH_QCHAT_PASSIVE]; + break; + + case VOWLAN_SESSION_VSID: + v = &common.voice[VOC_PATH_VOWLAN_PASSIVE]; + break; + + case VOICEMMODE1_VSID: + v = &common.voice[VOC_PATH_VOICEMMODE1_PASSIVE]; + break; + + case VOICEMMODE2_VSID: + v = &common.voice[VOC_PATH_VOICEMMODE2_PASSIVE]; + break; + + case ALL_SESSION_VSID: + break; + + default: + pr_err("%s: Invalid session_id : %x\n", __func__, session_id); + + break; + } + + pr_debug("%s:session_id 0x%x session handle %pK\n", + __func__, session_id, v); + + return v; +} + +int voice_get_idx_for_session(u32 session_id) +{ + int idx = 0; + + switch (session_id) { + case VOICE_SESSION_VSID: + idx = VOC_PATH_PASSIVE; + break; + + case VOICE2_SESSION_VSID: + idx = VOC_PATH_VOICE2_PASSIVE; + break; + + case VOLTE_SESSION_VSID: + idx = VOC_PATH_VOLTE_PASSIVE; + break; + + case VOIP_SESSION_VSID: + idx = VOC_PATH_FULL; + break; + + case QCHAT_SESSION_VSID: + idx = VOC_PATH_QCHAT_PASSIVE; + break; + + case VOWLAN_SESSION_VSID: + idx = VOC_PATH_VOWLAN_PASSIVE; + break; + + case VOICEMMODE1_VSID: + idx = VOC_PATH_VOICEMMODE1_PASSIVE; + break; + + case VOICEMMODE2_VSID: + idx = VOC_PATH_VOICEMMODE2_PASSIVE; + break; + + case ALL_SESSION_VSID: + idx = MAX_VOC_SESSIONS - 1; + break; + + default: + pr_err("%s: Invalid session_id : %x\n", __func__, session_id); + + break; + } + + return idx; +} + +static struct voice_data *voice_get_session_by_idx(int idx) +{ + return ((idx < 0 || idx >= MAX_VOC_SESSIONS) ? + NULL : &common.voice[idx]); +} + +static bool is_voip_session(u32 session_id) +{ + return (session_id == common.voice[VOC_PATH_FULL].session_id); +} + +static bool is_volte_session(u32 session_id) +{ + return (session_id == common.voice[VOC_PATH_VOLTE_PASSIVE].session_id); +} + +static bool is_voice2_session(u32 session_id) +{ + return (session_id == common.voice[VOC_PATH_VOICE2_PASSIVE].session_id); +} + +static bool is_qchat_session(u32 session_id) +{ + return (session_id == common.voice[VOC_PATH_QCHAT_PASSIVE].session_id); +} + +static bool is_vowlan_session(u32 session_id) +{ + return (session_id == common.voice[VOC_PATH_VOWLAN_PASSIVE].session_id); +} + +static bool is_voicemmode1(u32 session_id) +{ + return session_id == + common.voice[VOC_PATH_VOICEMMODE1_PASSIVE].session_id; +} + +static bool is_voicemmode2(u32 session_id) +{ + return session_id == + common.voice[VOC_PATH_VOICEMMODE2_PASSIVE].session_id; +} + +static bool is_voc_state_active(int voc_state) +{ + if ((voc_state == VOC_RUN) || + (voc_state == VOC_CHANGE) || + (voc_state == VOC_STANDBY)) + return true; + + return false; +} + +static void voc_set_error_state(uint16_t reset_proc) +{ + struct voice_data *v = NULL; + int i; + + for (i = 0; i < MAX_VOC_SESSIONS; i++) { + v = &common.voice[i]; + if (v != NULL) { + v->voc_state = VOC_ERROR; + v->rec_info.recording = 0; + } + } +} + +static bool is_other_session_active(u32 session_id) +{ + int i; + bool ret = false; + + /* Check if there is other active session except the input one */ + for (i = 0; i < MAX_VOC_SESSIONS; i++) { + if (common.voice[i].session_id == session_id) + continue; + + if (is_voc_state_active(common.voice[i].voc_state)) { + ret = true; + break; + } + } + pr_debug("%s: ret %d\n", __func__, ret); + + return ret; +} + +static bool is_sub1_vsid(u32 session_id) +{ + bool ret; + + switch (session_id) { + case VOICE_SESSION_VSID: + case VOLTE_SESSION_VSID: + case VOWLAN_SESSION_VSID: + case VOICEMMODE1_VSID: + ret = true; + break; + default: + ret = false; + } + + return ret; +} + +static bool is_sub2_vsid(u32 session_id) +{ + bool ret; + + switch (session_id) { + case VOICE2_SESSION_VSID: + case VOICEMMODE2_VSID: + ret = true; + break; + default: + ret = false; + } + + return ret; +} + +static bool is_voice_app_id(u32 session_id) +{ + return is_sub1_vsid(session_id) || is_sub2_vsid(session_id); +} + +static void init_session_id(void) +{ + common.voice[VOC_PATH_PASSIVE].session_id = VOICE_SESSION_VSID; + common.voice[VOC_PATH_VOLTE_PASSIVE].session_id = VOLTE_SESSION_VSID; + common.voice[VOC_PATH_VOICE2_PASSIVE].session_id = VOICE2_SESSION_VSID; + common.voice[VOC_PATH_FULL].session_id = VOIP_SESSION_VSID; + common.voice[VOC_PATH_QCHAT_PASSIVE].session_id = QCHAT_SESSION_VSID; + common.voice[VOC_PATH_VOWLAN_PASSIVE].session_id = VOWLAN_SESSION_VSID; + common.voice[VOC_PATH_VOICEMMODE1_PASSIVE].session_id = + VOICEMMODE1_VSID; + common.voice[VOC_PATH_VOICEMMODE2_PASSIVE].session_id = + VOICEMMODE2_VSID; +} + +static bool is_cvd_version_queried(void) +{ + bool ret = 0; + + if (!strcmp(common.cvd_version, CVD_VERSION_DEFAULT)) + ret = false; + else + ret = true; + + return ret; +} + +static int voice_get_cvd_int_version(char *cvd_ver_string) +{ + unsigned int idx; + int cvd_int_ver = CVD_INT_VERSION_DEFAULT; + + for (idx = 0; idx < CVD_INT_VERSION_MAX; idx++) { + if (strcmp((char *)cvd_ver_string, + cvd_version_table_mapping[idx].cvd_ver) == 0) { + cvd_int_ver = + cvd_version_table_mapping[idx].cvd_ver_int; + break; + } + } + return cvd_int_ver; +} + +static int voice_apr_register(uint32_t session_id) +{ + + pr_debug("%s\n", __func__); + + mutex_lock(&common.common_lock); + + /* register callback to APR */ + if (common.apr_q6_mvm == NULL) { + pr_debug("%s: Start to register MVM callback\n", __func__); + + common.apr_q6_mvm = apr_register("ADSP", "MVM", + qdsp_mvm_callback, + 0xFFFFFFFF, &common); + + if (common.apr_q6_mvm == NULL) { + pr_err("%s: Unable to register MVM\n", __func__); + goto err; + } + } + + if (common.apr_q6_cvs == NULL) { + pr_debug("%s: Start to register CVS callback\n", __func__); + + common.apr_q6_cvs = apr_register("ADSP", "CVS", + qdsp_cvs_callback, + 0xFFFFFFFF, &common); + + if (common.apr_q6_cvs == NULL) { + pr_err("%s: Unable to register CVS\n", __func__); + goto err; + } + rtac_set_voice_handle(RTAC_CVS, common.apr_q6_cvs); + } + + if (common.apr_q6_cvp == NULL) { + pr_debug("%s: Start to register CVP callback\n", __func__); + + common.apr_q6_cvp = apr_register("ADSP", "CVP", + qdsp_cvp_callback, + 0xFFFFFFFF, &common); + + if (common.apr_q6_cvp == NULL) { + pr_err("%s: Unable to register CVP\n", __func__); + goto err; + } + rtac_set_voice_handle(RTAC_CVP, common.apr_q6_cvp); + } + + mutex_unlock(&common.common_lock); + + return 0; + +err: + if (common.apr_q6_cvs != NULL) { + apr_deregister(common.apr_q6_cvs); + common.apr_q6_cvs = NULL; + rtac_set_voice_handle(RTAC_CVS, NULL); + } + if (common.apr_q6_mvm != NULL) { + apr_deregister(common.apr_q6_mvm); + common.apr_q6_mvm = NULL; + } + + mutex_unlock(&common.common_lock); + + return -ENODEV; +} + +static int voice_send_mvm_cvd_version_cmd(struct voice_data *v) +{ + int ret; + struct apr_hdr cvd_version_get_cmd; + void *apr_mvm; + + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + + ret = -EINVAL; + goto done; + } + + apr_mvm = common.apr_q6_mvm; + if (!apr_mvm) { + pr_err("%s: apr_mvm is NULL.\n", __func__); + + ret = -EINVAL; + goto done; + } + + /* Send command to CVD to retrieve Version */ + cvd_version_get_cmd.hdr_field = APR_HDR_FIELD( + APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + cvd_version_get_cmd.pkt_size = APR_PKT_SIZE( + APR_HDR_SIZE, + sizeof(cvd_version_get_cmd) - + APR_HDR_SIZE); + cvd_version_get_cmd.src_port = + voice_get_idx_for_session(v->session_id); + cvd_version_get_cmd.dest_port = 0; + cvd_version_get_cmd.token = 0; + cvd_version_get_cmd.opcode = VSS_IVERSION_CMD_GET; + + pr_debug("%s: send CVD version get cmd, pkt size = %d\n", + __func__, cvd_version_get_cmd.pkt_size); + + v->mvm_state = CMD_STATUS_FAIL; + v->async_err = 0; + ret = apr_send_pkt(apr_mvm, + (uint32_t *) &cvd_version_get_cmd); + if (ret < 0) { + pr_err("%s: Error sending command\n", __func__); + + ret = -EINVAL; + goto done; + } + + ret = wait_event_timeout(v->mvm_wait, + (v->mvm_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout, fall back to default\n", + __func__); + + ret = -EINVAL; + goto done; + } + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + v->async_err)); + ret = adsp_err_get_lnx_err_code( + v->async_err); + goto done; + } + ret = 0; + +done: + if (ret) { + strlcpy(common.cvd_version, CVD_VERSION_0_0, + sizeof(common.cvd_version)); + } + pr_debug("%s: CVD Version retrieved=%s\n", + __func__, common.cvd_version); + + return ret; +} + +static int voice_send_mvm_event_class_cmd(struct voice_data *v, + uint32_t event_id, + uint32_t class_id) +{ + struct vss_inotify_cmd_event_class_t mvm_event; + int ret = 0; + void *apr_mvm = NULL; + u16 mvm_handle = 0; + + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + return -EINVAL; + } + + apr_mvm = common.apr_q6_mvm; + if (!apr_mvm) { + pr_err("%s: apr_mvm is NULL.\n", __func__); + return -EINVAL; + } + + memset(&mvm_event, 0, sizeof(mvm_event)); + mvm_handle = voice_get_mvm_handle(v); + mvm_event.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + mvm_event.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(mvm_event) - APR_HDR_SIZE); + mvm_event.hdr.src_port = + voice_get_idx_for_session(v->session_id); + mvm_event.hdr.dest_port = mvm_handle; + mvm_event.hdr.token = 0; + mvm_event.hdr.opcode = event_id; + mvm_event.class_id = class_id; + + v->mvm_state = CMD_STATUS_FAIL; + v->async_err = 0; + ret = apr_send_pkt(apr_mvm, (uint32_t *) &mvm_event); + if (ret < 0) { + pr_err("%s: Error %d sending %x event\n", __func__, ret, + event_id); + goto fail; + } + + ret = wait_event_timeout(v->mvm_wait, + (v->mvm_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout %d\n", __func__, ret); + ret = -ETIMEDOUT; + goto fail; + } + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + v->async_err)); + ret = adsp_err_get_lnx_err_code( + v->async_err); + goto fail; + } + return 0; +fail: + return ret; +} + +static int voice_send_dual_control_cmd(struct voice_data *v) +{ + int ret = 0; + struct mvm_modem_dual_control_session_cmd mvm_voice_ctl_cmd; + void *apr_mvm; + u16 mvm_handle; + + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + return -EINVAL; + } + apr_mvm = common.apr_q6_mvm; + if (!apr_mvm) { + pr_err("%s: apr_mvm is NULL.\n", __func__); + return -EINVAL; + } + pr_debug("%s: Send Dual Control command to MVM\n", __func__); + if (!is_voip_session(v->session_id)) { + mvm_handle = voice_get_mvm_handle(v); + mvm_voice_ctl_cmd.hdr.hdr_field = APR_HDR_FIELD( + APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + mvm_voice_ctl_cmd.hdr.pkt_size = APR_PKT_SIZE( + APR_HDR_SIZE, + sizeof(mvm_voice_ctl_cmd) - + APR_HDR_SIZE); + pr_debug("%s: send mvm Voice Ctl pkt size = %d\n", + __func__, mvm_voice_ctl_cmd.hdr.pkt_size); + mvm_voice_ctl_cmd.hdr.src_port = + voice_get_idx_for_session(v->session_id); + mvm_voice_ctl_cmd.hdr.dest_port = mvm_handle; + mvm_voice_ctl_cmd.hdr.token = 0; + mvm_voice_ctl_cmd.hdr.opcode = + VSS_IMVM_CMD_SET_POLICY_DUAL_CONTROL; + mvm_voice_ctl_cmd.voice_ctl.enable_flag = true; + v->mvm_state = CMD_STATUS_FAIL; + v->async_err = 0; + + ret = apr_send_pkt(apr_mvm, (uint32_t *) &mvm_voice_ctl_cmd); + if (ret < 0) { + pr_err("%s: Error sending MVM Voice CTL CMD\n", + __func__); + ret = -EINVAL; + goto fail; + } + ret = wait_event_timeout(v->mvm_wait, + (v->mvm_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + ret = -EINVAL; + goto fail; + } + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + v->async_err)); + ret = adsp_err_get_lnx_err_code( + v->async_err); + goto fail; + } + } + ret = 0; +fail: + return ret; +} + + +static int voice_create_mvm_cvs_session(struct voice_data *v) +{ + int ret = 0; + struct mvm_create_ctl_session_cmd mvm_session_cmd; + struct cvs_create_passive_ctl_session_cmd cvs_session_cmd; + struct cvs_create_full_ctl_session_cmd cvs_full_ctl_cmd; + struct mvm_attach_stream_cmd attach_stream_cmd; + void *apr_mvm, *apr_cvs, *apr_cvp; + u16 mvm_handle, cvs_handle, cvp_handle; + + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + return -EINVAL; + } + apr_mvm = common.apr_q6_mvm; + apr_cvs = common.apr_q6_cvs; + apr_cvp = common.apr_q6_cvp; + + if (!apr_mvm || !apr_cvs || !apr_cvp) { + pr_err("%s: apr_mvm or apr_cvs or apr_cvp is NULL\n", __func__); + return -EINVAL; + } + mvm_handle = voice_get_mvm_handle(v); + cvs_handle = voice_get_cvs_handle(v); + cvp_handle = voice_get_cvp_handle(v); + + pr_debug("%s: mvm_hdl=%d, cvs_hdl=%d\n", __func__, + mvm_handle, cvs_handle); + /* send cmd to create mvm session and wait for response */ + + if (!mvm_handle) { + memset(mvm_session_cmd.mvm_session.name, 0, + sizeof(mvm_session_cmd.mvm_session.name)); + if (!is_voip_session(v->session_id)) { + mvm_session_cmd.hdr.hdr_field = APR_HDR_FIELD( + APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + mvm_session_cmd.hdr.pkt_size = APR_PKT_SIZE( + APR_HDR_SIZE, + sizeof(mvm_session_cmd) - + APR_HDR_SIZE); + pr_debug("%s: send mvm create session pkt size = %d\n", + __func__, mvm_session_cmd.hdr.pkt_size); + mvm_session_cmd.hdr.src_port = + voice_get_idx_for_session(v->session_id); + mvm_session_cmd.hdr.dest_port = 0; + mvm_session_cmd.hdr.token = 0; + mvm_session_cmd.hdr.opcode = + VSS_IMVM_CMD_CREATE_PASSIVE_CONTROL_SESSION; + if (is_volte_session(v->session_id)) { + strlcpy(mvm_session_cmd.mvm_session.name, + "default volte voice", + strlen("default volte voice")+1); + } else if (is_voice2_session(v->session_id)) { + strlcpy(mvm_session_cmd.mvm_session.name, + VOICE2_SESSION_VSID_STR, + strlen(VOICE2_SESSION_VSID_STR)+1); + } else if (is_qchat_session(v->session_id)) { + strlcpy(mvm_session_cmd.mvm_session.name, + QCHAT_SESSION_VSID_STR, + strlen(QCHAT_SESSION_VSID_STR)+1); + } else if (is_vowlan_session(v->session_id)) { + strlcpy(mvm_session_cmd.mvm_session.name, + VOWLAN_SESSION_VSID_STR, + strlen(VOWLAN_SESSION_VSID_STR)+1); + } else if (is_voicemmode1(v->session_id)) { + strlcpy(mvm_session_cmd.mvm_session.name, + VOICEMMODE1_VSID_STR, + strlen(VOICEMMODE1_VSID_STR) + 1); + } else if (is_voicemmode2(v->session_id)) { + strlcpy(mvm_session_cmd.mvm_session.name, + VOICEMMODE2_VSID_STR, + strlen(VOICEMMODE2_VSID_STR) + 1); + } else { + strlcpy(mvm_session_cmd.mvm_session.name, + "default modem voice", + strlen("default modem voice")+1); + } + + v->mvm_state = CMD_STATUS_FAIL; + v->async_err = 0; + + ret = apr_send_pkt(apr_mvm, + (uint32_t *) &mvm_session_cmd); + if (ret < 0) { + pr_err("%s: Error sending MVM_CONTROL_SESSION\n", + __func__); + goto fail; + } + ret = wait_event_timeout(v->mvm_wait, + (v->mvm_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + goto fail; + } + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + v->async_err)); + ret = adsp_err_get_lnx_err_code( + v->async_err); + goto fail; + } + } else { + pr_debug("%s: creating MVM full ctrl\n", __func__); + mvm_session_cmd.hdr.hdr_field = + APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + mvm_session_cmd.hdr.pkt_size = + APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(mvm_session_cmd) - + APR_HDR_SIZE); + mvm_session_cmd.hdr.src_port = + voice_get_idx_for_session(v->session_id); + mvm_session_cmd.hdr.dest_port = 0; + mvm_session_cmd.hdr.token = 0; + mvm_session_cmd.hdr.opcode = + VSS_IMVM_CMD_CREATE_FULL_CONTROL_SESSION; + strlcpy(mvm_session_cmd.mvm_session.name, + "default voip", + strlen("default voip")+1); + + v->mvm_state = CMD_STATUS_FAIL; + v->async_err = 0; + + ret = apr_send_pkt(apr_mvm, + (uint32_t *) &mvm_session_cmd); + if (ret < 0) { + pr_err("Fail in sending MVM_CONTROL_SESSION\n"); + goto fail; + } + ret = wait_event_timeout(v->mvm_wait, + (v->mvm_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + goto fail; + } + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + v->async_err)); + ret = adsp_err_get_lnx_err_code( + v->async_err); + goto fail; + } + } + /* Get the created MVM handle. */ + mvm_handle = voice_get_mvm_handle(v); + } + /* send cmd to create cvs session */ + if (!cvs_handle) { + memset(cvs_session_cmd.cvs_session.name, 0, + sizeof(cvs_session_cmd.cvs_session.name)); + if (!is_voip_session(v->session_id)) { + pr_debug("%s: creating CVS passive session\n", + __func__); + + cvs_session_cmd.hdr.hdr_field = APR_HDR_FIELD( + APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + cvs_session_cmd.hdr.pkt_size = + APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(cvs_session_cmd) - + APR_HDR_SIZE); + cvs_session_cmd.hdr.src_port = + voice_get_idx_for_session(v->session_id); + cvs_session_cmd.hdr.dest_port = 0; + cvs_session_cmd.hdr.token = 0; + cvs_session_cmd.hdr.opcode = + VSS_ISTREAM_CMD_CREATE_PASSIVE_CONTROL_SESSION; + if (is_volte_session(v->session_id)) { + strlcpy(cvs_session_cmd.cvs_session.name, + "default volte voice", + strlen("default volte voice")+1); + } else if (is_voice2_session(v->session_id)) { + strlcpy(cvs_session_cmd.cvs_session.name, + VOICE2_SESSION_VSID_STR, + strlen(VOICE2_SESSION_VSID_STR)+1); + } else if (is_qchat_session(v->session_id)) { + strlcpy(cvs_session_cmd.cvs_session.name, + QCHAT_SESSION_VSID_STR, + strlen(QCHAT_SESSION_VSID_STR)+1); + } else if (is_vowlan_session(v->session_id)) { + strlcpy(cvs_session_cmd.cvs_session.name, + VOWLAN_SESSION_VSID_STR, + strlen(VOWLAN_SESSION_VSID_STR)+1); + } else if (is_voicemmode1(v->session_id)) { + strlcpy(cvs_session_cmd.cvs_session.name, + VOICEMMODE1_VSID_STR, + strlen(VOICEMMODE1_VSID_STR) + 1); + } else if (is_voicemmode2(v->session_id)) { + strlcpy(cvs_session_cmd.cvs_session.name, + VOICEMMODE2_VSID_STR, + strlen(VOICEMMODE2_VSID_STR) + 1); + } else { + strlcpy(cvs_session_cmd.cvs_session.name, + "default modem voice", + strlen("default modem voice")+1); + } + v->cvs_state = CMD_STATUS_FAIL; + v->async_err = 0; + + ret = apr_send_pkt(apr_cvs, + (uint32_t *) &cvs_session_cmd); + if (ret < 0) { + pr_err("Fail in sending STREAM_CONTROL_SESSION\n"); + goto fail; + } + ret = wait_event_timeout(v->cvs_wait, + (v->cvs_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + goto fail; + } + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + v->async_err)); + ret = adsp_err_get_lnx_err_code( + v->async_err); + goto fail; + } + /* Get the created CVS handle. */ + cvs_handle = voice_get_cvs_handle(v); + + } else { + pr_debug("%s: creating CVS full session\n", __func__); + + cvs_full_ctl_cmd.hdr.hdr_field = + APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + + cvs_full_ctl_cmd.hdr.pkt_size = + APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(cvs_full_ctl_cmd) - + APR_HDR_SIZE); + + cvs_full_ctl_cmd.hdr.src_port = + voice_get_idx_for_session(v->session_id); + cvs_full_ctl_cmd.hdr.dest_port = 0; + cvs_full_ctl_cmd.hdr.token = 0; + cvs_full_ctl_cmd.hdr.opcode = + VSS_ISTREAM_CMD_CREATE_FULL_CONTROL_SESSION; + cvs_full_ctl_cmd.cvs_session.direction = 2; + cvs_full_ctl_cmd.cvs_session.enc_media_type = + common.mvs_info.media_type; + cvs_full_ctl_cmd.cvs_session.dec_media_type = + common.mvs_info.media_type; + cvs_full_ctl_cmd.cvs_session.network_id = + common.mvs_info.network_type; + strlcpy(cvs_full_ctl_cmd.cvs_session.name, + "default q6 voice", + strlen("default q6 voice")+1); + + v->cvs_state = CMD_STATUS_FAIL; + v->async_err = 0; + + ret = apr_send_pkt(apr_cvs, + (uint32_t *) &cvs_full_ctl_cmd); + + if (ret < 0) { + pr_err("%s: Err %d sending CREATE_FULL_CTRL\n", + __func__, ret); + goto fail; + } + ret = wait_event_timeout(v->cvs_wait, + (v->cvs_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + goto fail; + } + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + v->async_err)); + ret = adsp_err_get_lnx_err_code( + v->async_err); + goto fail; + } + /* Get the created CVS handle. */ + cvs_handle = voice_get_cvs_handle(v); + + /* Attach MVM to CVS. */ + pr_debug("%s: Attach MVM to stream\n", __func__); + + attach_stream_cmd.hdr.hdr_field = + APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + attach_stream_cmd.hdr.pkt_size = + APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(attach_stream_cmd) - + APR_HDR_SIZE); + attach_stream_cmd.hdr.src_port = + voice_get_idx_for_session(v->session_id); + attach_stream_cmd.hdr.dest_port = mvm_handle; + attach_stream_cmd.hdr.token = 0; + attach_stream_cmd.hdr.opcode = + VSS_IMVM_CMD_ATTACH_STREAM; + attach_stream_cmd.attach_stream.handle = cvs_handle; + + v->mvm_state = CMD_STATUS_FAIL; + v->async_err = 0; + ret = apr_send_pkt(apr_mvm, + (uint32_t *) &attach_stream_cmd); + if (ret < 0) { + pr_err("%s: Error %d sending ATTACH_STREAM\n", + __func__, ret); + goto fail; + } + ret = wait_event_timeout(v->mvm_wait, + (v->mvm_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + goto fail; + } + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + v->async_err)); + ret = adsp_err_get_lnx_err_code( + v->async_err); + goto fail; + } + } + } + return 0; + +fail: + return ret; +} + +static int voice_unmap_cal_block(struct voice_data *v, int cal_index) +{ + int result = 0; + struct cal_block_data *cal_block; + + if (common.cal_data[cal_index] == NULL) { + pr_err("%s: Cal type is NULL, index %d!\n", + __func__, cal_index); + + goto done; + } + + mutex_lock(&common.cal_data[cal_index]->lock); + cal_block = cal_utils_get_only_cal_block( + common.cal_data[cal_index]); + if (cal_block == NULL) { + pr_err("%s: Cal block is NULL, index %d!\n", + __func__, cal_index); + + result = -EINVAL; + goto unlock; + } + + if (cal_block->map_data.q6map_handle == 0) { + pr_debug("%s: Q6 handle is not set!\n", __func__); + + result = -EINVAL; + goto unlock; + } + + mutex_lock(&common.common_lock); + result = voice_send_mvm_unmap_memory_physical_cmd( + v, cal_block->map_data.q6map_handle); + if (result) + pr_err("%s: Voice_send_mvm_unmap_memory_physical_cmd failed for session 0x%x, err %d!\n", + __func__, v->session_id, result); + + cal_block->map_data.q6map_handle = 0; + mutex_unlock(&common.common_lock); +unlock: + mutex_unlock(&common.cal_data[cal_index]->lock); +done: + return result; +} + +static int voice_destroy_mvm_cvs_session(struct voice_data *v) +{ + int ret = 0; + struct mvm_detach_stream_cmd detach_stream; + struct apr_hdr mvm_destroy; + struct apr_hdr cvs_destroy; + void *apr_mvm, *apr_cvs; + u16 mvm_handle, cvs_handle; + + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + return -EINVAL; + } + apr_mvm = common.apr_q6_mvm; + apr_cvs = common.apr_q6_cvs; + + if (!apr_mvm || !apr_cvs) { + pr_err("%s: apr_mvm or apr_cvs is NULL\n", __func__); + return -EINVAL; + } + mvm_handle = voice_get_mvm_handle(v); + cvs_handle = voice_get_cvs_handle(v); + + /* MVM, CVS sessions are destroyed only for Full control sessions. */ + if (is_voip_session(v->session_id)) { + pr_debug("%s: MVM detach stream, VOC_STATE: %d\n", __func__, + v->voc_state); + + /* Detach voice stream. */ + detach_stream.hdr.hdr_field = + APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + detach_stream.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(detach_stream) - APR_HDR_SIZE); + detach_stream.hdr.src_port = + voice_get_idx_for_session(v->session_id); + detach_stream.hdr.dest_port = mvm_handle; + detach_stream.hdr.token = 0; + detach_stream.hdr.opcode = VSS_IMVM_CMD_DETACH_STREAM; + detach_stream.detach_stream.handle = cvs_handle; + + v->mvm_state = CMD_STATUS_FAIL; + v->async_err = 0; + ret = apr_send_pkt(apr_mvm, (uint32_t *) &detach_stream); + if (ret < 0) { + pr_err("%s: Error %d sending DETACH_STREAM\n", + __func__, ret); + + goto fail; + } + ret = wait_event_timeout(v->mvm_wait, + (v->mvm_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait event timeout\n", __func__); + + goto fail; + } + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + v->async_err)); + ret = adsp_err_get_lnx_err_code( + v->async_err); + goto fail; + } + + /* Unmap memory */ + if (v->shmem_info.mem_handle != 0) { + ret = voice_send_mvm_unmap_memory_physical_cmd(v, + v->shmem_info.mem_handle); + if (ret < 0) { + pr_err("%s Memory_unmap for voip failed %d\n", + __func__, ret); + + goto fail; + } + v->shmem_info.mem_handle = 0; + } + } + + /* Unmap Source Tracking shared memory if mapped earlier */ + voice_unmap_and_free_source_tracking_shared_memory(v); + + if (is_voip_session(v->session_id) || + is_qchat_session(v->session_id) || + is_volte_session(v->session_id) || + is_vowlan_session(v->session_id) || + v->voc_state == VOC_ERROR || common.is_destroy_cvd) { + /* Destroy CVS. */ + pr_debug("%s: CVS destroy session\n", __func__); + + cvs_destroy.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + cvs_destroy.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(cvs_destroy) - APR_HDR_SIZE); + cvs_destroy.src_port = + voice_get_idx_for_session(v->session_id); + cvs_destroy.dest_port = cvs_handle; + cvs_destroy.token = 0; + cvs_destroy.opcode = APRV2_IBASIC_CMD_DESTROY_SESSION; + + v->cvs_state = CMD_STATUS_FAIL; + v->async_err = 0; + ret = apr_send_pkt(apr_cvs, (uint32_t *) &cvs_destroy); + if (ret < 0) { + pr_err("%s: Error %d sending CVS DESTROY\n", + __func__, ret); + + goto fail; + } + ret = wait_event_timeout(v->cvs_wait, + (v->cvs_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait event timeout\n", __func__); + + goto fail; + } + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + v->async_err)); + ret = adsp_err_get_lnx_err_code( + v->async_err); + goto fail; + } + cvs_handle = 0; + voice_set_cvs_handle(v, cvs_handle); + + /* Unmap physical memory for all calibration buffers */ + if (!is_other_session_active(v->session_id)) { + if (voice_unmap_cal_block(v, CVP_VOCPROC_CAL)) + pr_err("%s: Unmap VOCPROC cal failed\n", + __func__); + if (voice_unmap_cal_block(v, CVP_VOCVOL_CAL)) + pr_err("%s: Unmap VOCVOL cal failed\n", + __func__); + if (voice_unmap_cal_block(v, CVP_VOCDEV_CFG_CAL)) + pr_err("%s: Unmap VOCDEV_CFG cal failed\n", + __func__); + if (voice_unmap_cal_block(v, CVS_VOCSTRM_CAL)) + pr_err("%s: Unmap VOCSTRM cal failed\n", + __func__); + } + + /* Destroy MVM. */ + pr_debug("%s: MVM destroy session\n", __func__); + + mvm_destroy.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + mvm_destroy.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(mvm_destroy) - APR_HDR_SIZE); + mvm_destroy.src_port = + voice_get_idx_for_session(v->session_id); + mvm_destroy.dest_port = mvm_handle; + mvm_destroy.token = 0; + mvm_destroy.opcode = APRV2_IBASIC_CMD_DESTROY_SESSION; + + v->mvm_state = CMD_STATUS_FAIL; + v->async_err = 0; + + ret = apr_send_pkt(apr_mvm, (uint32_t *) &mvm_destroy); + if (ret < 0) { + pr_err("%s: Error %d sending MVM DESTROY\n", + __func__, ret); + + goto fail; + } + ret = wait_event_timeout(v->mvm_wait, + (v->mvm_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait event timeout\n", __func__); + goto fail; + } + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + v->async_err)); + ret = adsp_err_get_lnx_err_code( + v->async_err); + goto fail; + } + mvm_handle = 0; + voice_set_mvm_handle(v, mvm_handle); + } + return 0; +fail: + return ret; +} + +static int voice_send_tty_mode_cmd(struct voice_data *v) +{ + int ret = 0; + struct mvm_set_tty_mode_cmd mvm_tty_mode_cmd; + void *apr_mvm; + u16 mvm_handle; + + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + return -EINVAL; + } + apr_mvm = common.apr_q6_mvm; + + if (!apr_mvm) { + pr_err("%s: apr_mvm is NULL.\n", __func__); + return -EINVAL; + } + mvm_handle = voice_get_mvm_handle(v); + + /* send tty mode cmd to mvm */ + mvm_tty_mode_cmd.hdr.hdr_field = APR_HDR_FIELD( + APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + mvm_tty_mode_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(mvm_tty_mode_cmd) - + APR_HDR_SIZE); + pr_debug("%s: pkt size = %d\n", + __func__, mvm_tty_mode_cmd.hdr.pkt_size); + mvm_tty_mode_cmd.hdr.src_port = + voice_get_idx_for_session(v->session_id); + mvm_tty_mode_cmd.hdr.dest_port = mvm_handle; + mvm_tty_mode_cmd.hdr.token = 0; + mvm_tty_mode_cmd.hdr.opcode = VSS_ISTREAM_CMD_SET_TTY_MODE; + mvm_tty_mode_cmd.tty_mode.mode = v->tty_mode; + pr_debug("tty mode =%d\n", mvm_tty_mode_cmd.tty_mode.mode); + + v->mvm_state = CMD_STATUS_FAIL; + v->async_err = 0; + ret = apr_send_pkt(apr_mvm, (uint32_t *) &mvm_tty_mode_cmd); + if (ret < 0) { + pr_err("%s: Error %d sending SET_TTY_MODE\n", + __func__, ret); + goto fail; + } + ret = wait_event_timeout(v->mvm_wait, + (v->mvm_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + goto fail; + } + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + v->async_err)); + ret = adsp_err_get_lnx_err_code( + v->async_err); + goto fail; + } + return 0; +fail: + return ret; +} + +static int voice_send_set_pp_enable_cmd( + struct voice_data *v, struct module_instance_info mod_inst_info, + int enable) +{ + struct enable_param enable_param; + struct param_hdr_v3 param_hdr; + int ret = 0; + + memset(&enable_param, 0, sizeof(enable_param)); + memset(¶m_hdr, 0, sizeof(param_hdr)); + param_hdr.module_id = mod_inst_info.module_id; + param_hdr.instance_id = mod_inst_info.instance_id; + param_hdr.param_id = VOICE_PARAM_MOD_ENABLE; + param_hdr.param_size = sizeof(enable_param); + enable_param.enable = enable ? 1 : 0; + + pr_debug("%s: module_id=%d, instance_id=%d, enable=%d\n", + __func__, mod_inst_info.module_id, mod_inst_info.instance_id, + enable); + + ret = voice_pack_and_set_cvs_ui_property(v, param_hdr, + (uint8_t *) &enable_param); + if (ret < 0) + pr_err("Fail: sending cvs set pp enable\n"); + + return ret; +} + +static int voice_send_hd_cmd(struct voice_data *v, int enable) +{ + struct mvm_set_hd_enable_cmd mvm_set_hd_cmd; + int ret = 0; + void *apr_mvm; + u16 mvm_handle; + + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + + ret = -EINVAL; + goto done; + } + + apr_mvm = common.apr_q6_mvm; + if (!apr_mvm) { + pr_err("%s: apr_mvm is NULL.\n", __func__); + + ret = -EINVAL; + goto done; + } + + mvm_handle = voice_get_mvm_handle(v); + if (!mvm_handle) { + pr_err("%s: mvm_handle is NULL\n", __func__); + + ret = -EINVAL; + goto done; + } + + mvm_set_hd_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + mvm_set_hd_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(mvm_set_hd_cmd) - + APR_HDR_SIZE); + mvm_set_hd_cmd.hdr.src_port = voice_get_idx_for_session(v->session_id); + mvm_set_hd_cmd.hdr.dest_port = mvm_handle; + mvm_set_hd_cmd.hdr.token = 0; + + if (enable) + mvm_set_hd_cmd.hdr.opcode = VSS_IHDVOICE_CMD_ENABLE; + else + mvm_set_hd_cmd.hdr.opcode = VSS_IHDVOICE_CMD_DISABLE; + + pr_debug("%s: enable=%d\n", __func__, enable); + + v->mvm_state = CMD_STATUS_FAIL; + v->async_err = 0; + ret = apr_send_pkt(apr_mvm, (uint32_t *) &mvm_set_hd_cmd); + if (ret < 0) { + pr_err("%s: Failed to sending mvm set HD Voice enable %d\n", + __func__, ret); + + ret = -EINVAL; + goto done; + } + + ret = wait_event_timeout(v->mvm_wait, + (v->mvm_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + + ret = -EINVAL; + goto done; + } + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + v->async_err)); + ret = adsp_err_get_lnx_err_code( + v->async_err); + goto done; + } + +done: + return ret; +} + +static int voice_set_dtx(struct voice_data *v) +{ + int ret = 0; + void *apr_cvs; + u16 cvs_handle; + struct cvs_set_enc_dtx_mode_cmd cvs_set_dtx; + + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + return -EINVAL; + } + apr_cvs = common.apr_q6_cvs; + + if (!apr_cvs) { + pr_err("%s: apr_cvs is NULL.\n", __func__); + return -EINVAL; + } + + cvs_handle = voice_get_cvs_handle(v); + + /* Set DTX */ + cvs_set_dtx.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + cvs_set_dtx.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(cvs_set_dtx) - APR_HDR_SIZE); + cvs_set_dtx.hdr.src_port = + voice_get_idx_for_session(v->session_id); + cvs_set_dtx.hdr.dest_port = cvs_handle; + cvs_set_dtx.hdr.token = 0; + cvs_set_dtx.hdr.opcode = VSS_ISTREAM_CMD_SET_ENC_DTX_MODE; + cvs_set_dtx.dtx_mode.enable = common.mvs_info.dtx_mode; + + pr_debug("%s: Setting DTX %d\n", __func__, common.mvs_info.dtx_mode); + + v->cvs_state = CMD_STATUS_FAIL; + v->async_err = 0; + + ret = apr_send_pkt(apr_cvs, (uint32_t *) &cvs_set_dtx); + if (ret < 0) { + pr_err("%s: Error %d sending SET_DTX\n", __func__, ret); + return -EINVAL; + } + + ret = wait_event_timeout(v->cvs_wait, + (v->cvs_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + return -EINVAL; + } + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + v->async_err)); + ret = adsp_err_get_lnx_err_code( + v->async_err); + return ret; + } + + return 0; +} + +static int voice_send_mvm_media_type_cmd(struct voice_data *v) +{ + struct vss_imvm_cmd_set_cal_media_type_t mvm_set_cal_media_type; + int ret = 0; + void *apr_mvm; + u16 mvm_handle; + + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + return -EINVAL; + } + apr_mvm = common.apr_q6_mvm; + + if (!apr_mvm) { + pr_err("%s: apr_mvm is NULL.\n", __func__); + return -EINVAL; + } + mvm_handle = voice_get_mvm_handle(v); + + mvm_set_cal_media_type.hdr.hdr_field = + APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + mvm_set_cal_media_type.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(mvm_set_cal_media_type) - + APR_HDR_SIZE); + mvm_set_cal_media_type.hdr.src_port = + voice_get_idx_for_session(v->session_id); + mvm_set_cal_media_type.hdr.dest_port = mvm_handle; + mvm_set_cal_media_type.hdr.token = 0; + mvm_set_cal_media_type.hdr.opcode = VSS_IMVM_CMD_SET_CAL_MEDIA_TYPE; + mvm_set_cal_media_type.media_id = common.mvs_info.media_type; + pr_debug("%s: setting media_id as %x\n", + __func__, mvm_set_cal_media_type.media_id); + + v->mvm_state = CMD_STATUS_FAIL; + v->async_err = 0; + ret = apr_send_pkt(apr_mvm, (uint32_t *) &mvm_set_cal_media_type); + if (ret < 0) { + pr_err("%s: Error %d sending media type\n", __func__, ret); + goto fail; + } + + ret = wait_event_timeout(v->mvm_wait, + (v->mvm_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout %d\n", __func__, ret); + goto fail; + } + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + v->async_err)); + ret = adsp_err_get_lnx_err_code( + v->async_err); + goto fail; + } + return 0; +fail: + return ret; +} + +static int voice_send_dtmf_rx_detection_cmd(struct voice_data *v, + uint32_t enable) +{ + int ret = 0; + void *apr_cvs; + u16 cvs_handle; + struct cvs_set_rx_dtmf_detection_cmd cvs_dtmf_rx_detection; + + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + return -EINVAL; + } + apr_cvs = common.apr_q6_cvs; + + if (!apr_cvs) { + pr_err("%s: apr_cvs is NULL.\n", __func__); + return -EINVAL; + } + + cvs_handle = voice_get_cvs_handle(v); + + /* Set SET_DTMF_RX_DETECTION */ + cvs_dtmf_rx_detection.hdr.hdr_field = + APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + cvs_dtmf_rx_detection.hdr.pkt_size = + APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(cvs_dtmf_rx_detection) - APR_HDR_SIZE); + cvs_dtmf_rx_detection.hdr.src_port = + voice_get_idx_for_session(v->session_id); + cvs_dtmf_rx_detection.hdr.dest_port = cvs_handle; + cvs_dtmf_rx_detection.hdr.token = 0; + cvs_dtmf_rx_detection.hdr.opcode = + VSS_ISTREAM_CMD_SET_RX_DTMF_DETECTION; + cvs_dtmf_rx_detection.cvs_dtmf_det.enable = enable; + + v->cvs_state = CMD_STATUS_FAIL; + v->async_err = 0; + + ret = apr_send_pkt(apr_cvs, (uint32_t *) &cvs_dtmf_rx_detection); + if (ret < 0) { + pr_err("%s: Error %d sending SET_DTMF_RX_DETECTION\n", + __func__, + ret); + return -EINVAL; + } + + ret = wait_event_timeout(v->cvs_wait, + (v->cvs_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + return -EINVAL; + } + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + v->async_err)); + ret = adsp_err_get_lnx_err_code( + v->async_err); + return ret; + } + + return ret; +} + +/** + * voc_disable_dtmf_det_on_active_sessions - + * command to disable DTMF detection for voice sessions + * + */ +void voc_disable_dtmf_det_on_active_sessions(void) +{ + struct voice_data *v = NULL; + int i; + + for (i = 0; i < MAX_VOC_SESSIONS; i++) { + v = &common.voice[i]; + if ((v->dtmf_rx_detect_en) && + is_voc_state_active(v->voc_state)) { + + pr_debug("disable dtmf det on ses_id=%d\n", + v->session_id); + voice_send_dtmf_rx_detection_cmd(v, 0); + } + } +} +EXPORT_SYMBOL(voc_disable_dtmf_det_on_active_sessions); + +/** + * voc_enable_dtmf_rx_detection - + * command to set DTMF RX detection + * + * @session_id: voice session ID to send this command + * @enable: Enable or Disable detection + * + * Returns 0 on success or error on failure + */ +int voc_enable_dtmf_rx_detection(uint32_t session_id, uint32_t enable) +{ + struct voice_data *v = voice_get_session(session_id); + int ret = 0; + + if (v == NULL) { + pr_err("%s: invalid session_id 0x%x\n", __func__, session_id); + return -EINVAL; + } + + mutex_lock(&v->lock); + v->dtmf_rx_detect_en = enable; + + if (is_voc_state_active(v->voc_state)) + ret = voice_send_dtmf_rx_detection_cmd(v, + v->dtmf_rx_detect_en); + + mutex_unlock(&v->lock); + + return ret; +} +EXPORT_SYMBOL(voc_enable_dtmf_rx_detection); + +/** + * voc_set_destroy_cvd_flag - + * set flag for destroy CVD session + * + * @is_destroy_cvd: bool value used to indicate + * destroy CVD session or not. + * + */ +void voc_set_destroy_cvd_flag(bool is_destroy_cvd) +{ + pr_debug("%s: %d\n", __func__, is_destroy_cvd); + common.is_destroy_cvd = is_destroy_cvd; +} +EXPORT_SYMBOL(voc_set_destroy_cvd_flag); + +/** + * voc_alloc_cal_shared_memory - + * Alloc mem map table for calibration + * + * Returns 0 on success or error on failure + */ +int voc_alloc_cal_shared_memory(void) +{ + int rc = 0; + + mutex_lock(&common.common_lock); + if (is_cal_memory_allocated()) { + pr_debug("%s: Calibration shared buffer already allocated", + __func__); + } else { + /* Allocate memory for calibration memory map table. */ + rc = voice_alloc_cal_mem_map_table(); + if ((rc < 0) && (rc != -EPROBE_DEFER)) { + pr_err("%s: Failed to allocate cal memory, err=%d", + __func__, rc); + } + } + mutex_unlock(&common.common_lock); + + return rc; +} +EXPORT_SYMBOL(voc_alloc_cal_shared_memory); + +/** + * voc_alloc_voip_shared_memory - + * Alloc mem map table for OOB + * + * Returns 0 on success or error on failure + */ +int voc_alloc_voip_shared_memory(void) +{ + int rc = 0; + + /* Allocate shared memory for OOB Voip */ + rc = voice_alloc_oob_shared_mem(); + if (rc < 0) { + pr_err("%s: Failed to alloc shared memory for OOB rc:%d\n", + __func__, rc); + } else { + /* Allocate mem map table for OOB */ + rc = voice_alloc_oob_mem_table(); + if (rc < 0) { + pr_err("%s: Failed to alloc mem map talbe rc:%d\n", + __func__, rc); + + voice_free_oob_shared_mem(); + } + } + + return rc; +} +EXPORT_SYMBOL(voc_alloc_voip_shared_memory); + +static int is_cal_memory_allocated(void) +{ + bool ret; + + if (common.cal_mem_map_table.dma_buf != NULL) + ret = true; + else + ret = false; + + return ret; +} + + +static int free_cal_map_table(void) +{ + int ret = 0; + + if (common.cal_mem_map_table.dma_buf == NULL) + goto done; + + ret = msm_audio_ion_free(common.cal_mem_map_table.dma_buf); + if (ret < 0) + pr_err("%s: msm_audio_ion_free failed:\n", __func__); + +done: + common.cal_mem_map_table.dma_buf = NULL; + return ret; +} + +static int is_rtac_memory_allocated(void) +{ + bool ret; + + if (common.rtac_mem_map_table.dma_buf != NULL) + ret = true; + else + ret = false; + + return ret; +} + +static int free_rtac_map_table(void) +{ + int ret = 0; + + if (common.rtac_mem_map_table.dma_buf == NULL) + goto done; + + ret = msm_audio_ion_free(common.rtac_mem_map_table.dma_buf); + if (ret < 0) + pr_err("%s: msm_audio_ion_free failed:\n", __func__); + +done: + common.rtac_mem_map_table.dma_buf = NULL; + return ret; +} + + +static int is_voip_memory_allocated(void) +{ + bool ret; + struct voice_data *v = voice_get_session( + common.voice[VOC_PATH_FULL].session_id); + + if (v == NULL) { + pr_err("%s: v is NULL, session_id:%d\n", __func__, + common.voice[VOC_PATH_FULL].session_id); + + ret = false; + goto done; + } + + mutex_lock(&common.common_lock); + if (v->shmem_info.sh_buf.dma_buf != NULL) + ret = true; + else + ret = false; + mutex_unlock(&common.common_lock); + +done: + return ret; +} + +static int voice_config_cvs_vocoder_amr_rate(struct voice_data *v) +{ + int ret = 0; + void *apr_cvs; + u16 cvs_handle; + struct cvs_set_amr_enc_rate_cmd cvs_set_amr_rate; + + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + + ret = -EINVAL; + goto done; + } + apr_cvs = common.apr_q6_cvs; + + if (!apr_cvs) { + pr_err("%s: apr_cvs is NULL.\n", __func__); + + ret = -EINVAL; + goto done; + } + + cvs_handle = voice_get_cvs_handle(v); + + pr_debug("%s: Setting AMR rate. Media Type: %d\n", __func__, + common.mvs_info.media_type); + + cvs_set_amr_rate.hdr.hdr_field = + APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + cvs_set_amr_rate.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(cvs_set_amr_rate) - APR_HDR_SIZE); + cvs_set_amr_rate.hdr.src_port = + voice_get_idx_for_session(v->session_id); + cvs_set_amr_rate.hdr.dest_port = cvs_handle; + cvs_set_amr_rate.hdr.token = 0; + + if (common.mvs_info.media_type == VSS_MEDIA_ID_AMR_NB_MODEM) + cvs_set_amr_rate.hdr.opcode = + VSS_ISTREAM_CMD_VOC_AMR_SET_ENC_RATE; + else if (common.mvs_info.media_type == VSS_MEDIA_ID_AMR_WB_MODEM) + cvs_set_amr_rate.hdr.opcode = + VSS_ISTREAM_CMD_VOC_AMRWB_SET_ENC_RATE; + + cvs_set_amr_rate.amr_rate.mode = common.mvs_info.rate; + + v->cvs_state = CMD_STATUS_FAIL; + v->async_err = 0; + + ret = apr_send_pkt(apr_cvs, (uint32_t *) &cvs_set_amr_rate); + if (ret < 0) { + pr_err("%s: Error %d sending SET_AMR_RATE\n", + __func__, ret); + + goto done; + } + ret = wait_event_timeout(v->cvs_wait, + (v->cvs_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + + ret = -EINVAL; + goto done; + } + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + v->async_err)); + ret = adsp_err_get_lnx_err_code( + v->async_err); + goto done; + } + + return 0; +done: + return ret; +} + +static int voice_config_cvs_vocoder(struct voice_data *v) +{ + int ret = 0; + void *apr_cvs; + u16 cvs_handle; + /* Set media type. */ + struct cvs_set_media_type_cmd cvs_set_media_cmd; + + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + return -EINVAL; + } + apr_cvs = common.apr_q6_cvs; + + if (!apr_cvs) { + pr_err("%s: apr_cvs is NULL.\n", __func__); + return -EINVAL; + } + + cvs_handle = voice_get_cvs_handle(v); + + cvs_set_media_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + cvs_set_media_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(cvs_set_media_cmd) - + APR_HDR_SIZE); + cvs_set_media_cmd.hdr.src_port = + voice_get_idx_for_session(v->session_id); + cvs_set_media_cmd.hdr.dest_port = cvs_handle; + cvs_set_media_cmd.hdr.token = 0; + cvs_set_media_cmd.hdr.opcode = VSS_ISTREAM_CMD_SET_MEDIA_TYPE; + cvs_set_media_cmd.media_type.tx_media_id = common.mvs_info.media_type; + cvs_set_media_cmd.media_type.rx_media_id = common.mvs_info.media_type; + + v->cvs_state = CMD_STATUS_FAIL; + v->async_err = 0; + + ret = apr_send_pkt(apr_cvs, (uint32_t *) &cvs_set_media_cmd); + if (ret < 0) { + pr_err("%s: Error %d sending SET_MEDIA_TYPE\n", + __func__, ret); + + goto fail; + } + ret = wait_event_timeout(v->cvs_wait, + (v->cvs_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + + goto fail; + } + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + v->async_err)); + ret = adsp_err_get_lnx_err_code( + v->async_err); + goto fail; + } + /* Set encoder properties. */ + switch (common.mvs_info.media_type) { + case VSS_MEDIA_ID_EVRC_MODEM: + case VSS_MEDIA_ID_4GV_NB_MODEM: + case VSS_MEDIA_ID_4GV_WB_MODEM: + case VSS_MEDIA_ID_4GV_NW_MODEM: { + struct cvs_set_cdma_enc_minmax_rate_cmd cvs_set_cdma_rate; + + pr_debug("Setting EVRC min-max rate\n"); + + cvs_set_cdma_rate.hdr.hdr_field = + APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + cvs_set_cdma_rate.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(cvs_set_cdma_rate) - APR_HDR_SIZE); + cvs_set_cdma_rate.hdr.src_port = + voice_get_idx_for_session(v->session_id); + cvs_set_cdma_rate.hdr.dest_port = cvs_handle; + cvs_set_cdma_rate.hdr.token = 0; + cvs_set_cdma_rate.hdr.opcode = + VSS_ISTREAM_CMD_CDMA_SET_ENC_MINMAX_RATE; + cvs_set_cdma_rate.cdma_rate.min_rate = + common.mvs_info.evrc_min_rate; + cvs_set_cdma_rate.cdma_rate.max_rate = + common.mvs_info.evrc_max_rate; + + v->cvs_state = CMD_STATUS_FAIL; + v->async_err = 0; + + ret = apr_send_pkt(apr_cvs, (uint32_t *) &cvs_set_cdma_rate); + if (ret < 0) { + pr_err("%s: Error %d sending SET_EVRC_MINMAX_RATE\n", + __func__, ret); + goto fail; + } + ret = wait_event_timeout(v->cvs_wait, + (v->cvs_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + + goto fail; + } + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + v->async_err)); + ret = adsp_err_get_lnx_err_code( + v->async_err); + goto fail; + } + + if (common.mvs_info.media_type != VSS_MEDIA_ID_EVRC_MODEM) { + ret = voice_set_dtx(v); + if (ret < 0) + goto fail; + } + + break; + } + case VSS_MEDIA_ID_AMR_NB_MODEM: + case VSS_MEDIA_ID_AMR_WB_MODEM: { + ret = voice_config_cvs_vocoder_amr_rate(v); + if (ret) { + pr_err("%s: Failed to update vocoder rate. %d\n", + __func__, ret); + + goto fail; + } + + ret = voice_set_dtx(v); + if (ret < 0) + goto fail; + + break; + } + case VSS_MEDIA_ID_G729: + case VSS_MEDIA_ID_G711_ALAW: + case VSS_MEDIA_ID_G711_MULAW: { + ret = voice_set_dtx(v); + + break; + } + default: + /* Do nothing. */ + break; + } + return 0; + +fail: + return ret; +} + +/** + * voc_update_amr_vocoder_rate - + * command to update AMR rate for voice session + * + * @session_id: voice session ID to send this command + * + * Returns 0 on success or error on failure + */ +int voc_update_amr_vocoder_rate(uint32_t session_id) +{ + int ret = 0; + struct voice_data *v; + + pr_debug("%s: session_id:%d", __func__, session_id); + + v = voice_get_session(session_id); + + if (v == NULL) { + pr_err("%s: v is NULL, session_id:%d\n", __func__, + session_id); + + ret = -EINVAL; + goto done; + } + + mutex_lock(&v->lock); + ret = voice_config_cvs_vocoder_amr_rate(v); + mutex_unlock(&v->lock); + +done: + return ret; +} +EXPORT_SYMBOL(voc_update_amr_vocoder_rate); + +static int voice_send_start_voice_cmd(struct voice_data *v) +{ + struct apr_hdr mvm_start_voice_cmd; + int ret = 0; + void *apr_mvm; + u16 mvm_handle; + + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + return -EINVAL; + } + apr_mvm = common.apr_q6_mvm; + + if (!apr_mvm) { + pr_err("%s: apr_mvm is NULL.\n", __func__); + return -EINVAL; + } + mvm_handle = voice_get_mvm_handle(v); + + mvm_start_voice_cmd.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + mvm_start_voice_cmd.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(mvm_start_voice_cmd) - APR_HDR_SIZE); + pr_debug("send mvm_start_voice_cmd pkt size = %d\n", + mvm_start_voice_cmd.pkt_size); + mvm_start_voice_cmd.src_port = + voice_get_idx_for_session(v->session_id); + mvm_start_voice_cmd.dest_port = mvm_handle; + mvm_start_voice_cmd.token = 0; + mvm_start_voice_cmd.opcode = VSS_IMVM_CMD_START_VOICE; + + v->mvm_state = CMD_STATUS_FAIL; + v->async_err = 0; + ret = apr_send_pkt(apr_mvm, (uint32_t *) &mvm_start_voice_cmd); + if (ret < 0) { + pr_err("Fail in sending VSS_IMVM_CMD_START_VOICE\n"); + goto fail; + } + ret = wait_event_timeout(v->mvm_wait, + (v->mvm_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + goto fail; + } + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + v->async_err)); + ret = adsp_err_get_lnx_err_code( + v->async_err); + goto fail; + } + return 0; +fail: + return ret; +} + +static void voc_get_tx_rx_topology(struct voice_data *v, + uint32_t *tx_topology_id, + uint32_t *rx_topology_id) +{ + uint32_t tx_id = 0; + uint32_t rx_id = 0; + + if (v->lch_mode == VOICE_LCH_START || v->disable_topology) { + pr_debug("%s: Setting TX and RX topology to NONE for LCH\n", + __func__); + + tx_id = VSS_IVOCPROC_TOPOLOGY_ID_NONE; + rx_id = VSS_IVOCPROC_TOPOLOGY_ID_NONE; + } else { + tx_id = voice_get_topology(CVP_VOC_TX_TOPOLOGY_CAL); + rx_id = voice_get_topology(CVP_VOC_RX_TOPOLOGY_CAL); + } + + *tx_topology_id = tx_id; + *rx_topology_id = rx_id; +} + +static int voice_send_set_device_cmd(struct voice_data *v) +{ + struct cvp_set_device_cmd cvp_setdev_cmd; + int ret = 0; + void *apr_cvp; + u16 cvp_handle; + + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + return -EINVAL; + } + apr_cvp = common.apr_q6_cvp; + + if (!apr_cvp) { + pr_err("%s: apr_cvp is NULL.\n", __func__); + return -EINVAL; + } + cvp_handle = voice_get_cvp_handle(v); + + /* set device and wait for response */ + cvp_setdev_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + cvp_setdev_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(cvp_setdev_cmd) - APR_HDR_SIZE); + pr_debug(" send create cvp setdev, pkt size = %d\n", + cvp_setdev_cmd.hdr.pkt_size); + cvp_setdev_cmd.hdr.src_port = + voice_get_idx_for_session(v->session_id); + cvp_setdev_cmd.hdr.dest_port = cvp_handle; + cvp_setdev_cmd.hdr.token = 0; + + if (voice_get_cvd_int_version(common.cvd_version) >= + CVD_INT_VERSION_2_2) + cvp_setdev_cmd.hdr.opcode = + VSS_IVOCPROC_CMD_SET_DEVICE_V3; + else + cvp_setdev_cmd.hdr.opcode = + VSS_IVOCPROC_CMD_SET_DEVICE_V2; + + voc_get_tx_rx_topology(v, + &cvp_setdev_cmd.cvp_set_device_v2.tx_topology_id, + &cvp_setdev_cmd.cvp_set_device_v2.rx_topology_id); + + voice_set_topology_specific_info(v, CVP_VOC_RX_TOPOLOGY_CAL); + voice_set_topology_specific_info(v, CVP_VOC_TX_TOPOLOGY_CAL); + + cvp_setdev_cmd.cvp_set_device_v2.tx_port_id = v->dev_tx.port_id; + cvp_setdev_cmd.cvp_set_device_v2.rx_port_id = v->dev_rx.port_id; + + if (common.ec_ref_ext) { + cvp_setdev_cmd.cvp_set_device_v2.vocproc_mode = + VSS_IVOCPROC_VOCPROC_MODE_EC_EXT_MIXING; + cvp_setdev_cmd.cvp_set_device_v2.ec_ref_port_id = + common.ec_media_fmt_info.port_id; + } else { + cvp_setdev_cmd.cvp_set_device_v2.vocproc_mode = + VSS_IVOCPROC_VOCPROC_MODE_EC_INT_MIXING; + cvp_setdev_cmd.cvp_set_device_v2.ec_ref_port_id = + VSS_IVOCPROC_PORT_ID_NONE; + } + pr_debug("topology=%d , tx_port_id=%d, rx_port_id=%d\n", + cvp_setdev_cmd.cvp_set_device_v2.tx_topology_id, + cvp_setdev_cmd.cvp_set_device_v2.tx_port_id, + cvp_setdev_cmd.cvp_set_device_v2.rx_port_id); + + v->cvp_state = CMD_STATUS_FAIL; + v->async_err = 0; + ret = apr_send_pkt(apr_cvp, (uint32_t *) &cvp_setdev_cmd); + if (ret < 0) { + pr_err("Fail in sending VSS_IVOCPROC_CMD_SET_DEVICE\n"); + goto fail; + } + + ret = wait_event_timeout(v->cvp_wait, + (v->cvp_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + goto fail; + } + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + v->async_err)); + ret = adsp_err_get_lnx_err_code( + v->async_err); + goto fail; + } + + return 0; +fail: + return ret; +} + +static int voice_send_stop_voice_cmd(struct voice_data *v) +{ + struct apr_hdr mvm_stop_voice_cmd; + int ret = 0; + void *apr_mvm; + u16 mvm_handle; + + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + return -EINVAL; + } + apr_mvm = common.apr_q6_mvm; + + if (!apr_mvm) { + pr_err("%s: apr_mvm is NULL.\n", __func__); + return -EINVAL; + } + mvm_handle = voice_get_mvm_handle(v); + + mvm_stop_voice_cmd.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + mvm_stop_voice_cmd.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(mvm_stop_voice_cmd) - APR_HDR_SIZE); + pr_debug("send mvm_stop_voice_cmd pkt size = %d\n", + mvm_stop_voice_cmd.pkt_size); + mvm_stop_voice_cmd.src_port = + voice_get_idx_for_session(v->session_id); + mvm_stop_voice_cmd.dest_port = mvm_handle; + mvm_stop_voice_cmd.token = 0; + mvm_stop_voice_cmd.opcode = VSS_IMVM_CMD_STOP_VOICE; + + v->mvm_state = CMD_STATUS_FAIL; + v->async_err = 0; + ret = apr_send_pkt(apr_mvm, (uint32_t *) &mvm_stop_voice_cmd); + if (ret < 0) { + pr_err("Fail in sending VSS_IMVM_CMD_STOP_VOICE\n"); + goto fail; + } + ret = wait_event_timeout(v->mvm_wait, + (v->mvm_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + goto fail; + } + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + v->async_err)); + ret = adsp_err_get_lnx_err_code( + v->async_err); + goto fail; + } + + return 0; +fail: + return ret; +} +static int voice_get_cal(struct cal_block_data **cal_block, + int cal_block_idx, + struct cal_block_data **col_data, + int col_data_idx, int session_id) +{ + int ret = 0; + + *cal_block = cal_utils_get_only_cal_block( + common.cal_data[cal_block_idx]); + if (*cal_block == NULL) { + pr_err("%s: No cal data for cal %d!\n", + __func__, cal_block_idx); + + ret = -ENODEV; + goto done; + } + ret = remap_cal_data(*cal_block, session_id); + if (ret < 0) { + pr_err("%s: Remap_cal_data failed for cal %d!\n", + __func__, cal_block_idx); + + ret = -ENODEV; + goto done; + } + + if (col_data == NULL) + goto done; + + *col_data = cal_utils_get_only_cal_block( + common.cal_data[col_data_idx]); + if (*col_data == NULL) { + pr_err("%s: No cal data for cal %d!\n", + __func__, col_data_idx); + + ret = -ENODEV; + goto done; + } +done: + return ret; +} + +static int voice_send_cvs_register_cal_cmd(struct voice_data *v) +{ + struct cvs_register_cal_data_cmd cvs_reg_cal_cmd; + struct cal_block_data *cal_block = NULL; + struct cal_block_data *col_data = NULL; + int ret = 0; + + memset(&cvs_reg_cal_cmd, 0, sizeof(cvs_reg_cal_cmd)); + + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + + ret = -EINVAL; + goto done; + } + + if (!common.apr_q6_cvs) { + pr_err("%s: apr_cvs is NULL\n", __func__); + + ret = -EINVAL; + goto done; + } + + mutex_lock(&common.cal_data[CVS_VOCSTRM_CAL]->lock); + mutex_lock(&common.cal_data[CVS_VOCSTRM_COL_CAL]->lock); + + ret = voice_get_cal(&cal_block, CVS_VOCSTRM_CAL, &col_data, + CVS_VOCSTRM_COL_CAL, v->session_id); + if (ret < 0) { + pr_err("%s: Voice_get_cal failed for cal %d!\n", + __func__, CVS_VOCSTRM_CAL); + + goto unlock; + } + + memcpy(&cvs_reg_cal_cmd.cvs_cal_data.column_info[0], + (void *) &((struct audio_cal_info_voc_col *) + col_data->cal_info)->data, + col_data->cal_data.size); + + cvs_reg_cal_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + cvs_reg_cal_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(cvs_reg_cal_cmd) - APR_HDR_SIZE); + cvs_reg_cal_cmd.hdr.src_port = + voice_get_idx_for_session(v->session_id); + cvs_reg_cal_cmd.hdr.dest_port = voice_get_cvs_handle(v); + cvs_reg_cal_cmd.hdr.token = 0; + if (common.is_per_vocoder_cal_enabled) + cvs_reg_cal_cmd.hdr.opcode = + VSS_ISTREAM_CMD_REGISTER_STATIC_CALIBRATION_DATA; + else + cvs_reg_cal_cmd.hdr.opcode = + VSS_ISTREAM_CMD_REGISTER_CALIBRATION_DATA_V2; + + cvs_reg_cal_cmd.cvs_cal_data.cal_mem_handle = + cal_block->map_data.q6map_handle; + cvs_reg_cal_cmd.cvs_cal_data.cal_mem_address_lsw = + lower_32_bits(cal_block->cal_data.paddr); + cvs_reg_cal_cmd.cvs_cal_data.cal_mem_address_msw = + msm_audio_populate_upper_32_bits(cal_block->cal_data.paddr); + cvs_reg_cal_cmd.cvs_cal_data.cal_mem_size = + cal_block->cal_data.size; + + v->cvs_state = CMD_STATUS_FAIL; + v->async_err = 0; + ret = apr_send_pkt(common.apr_q6_cvs, (uint32_t *) &cvs_reg_cal_cmd); + if (ret < 0) { + pr_err("%s: Error %d registering CVS cal\n", __func__, ret); + + ret = -EINVAL; + goto unlock; + } + ret = wait_event_timeout(v->cvs_wait, + (v->cvs_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: Command timeout\n", __func__); + + ret = -EINVAL; + goto unlock; + } + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + v->async_err)); + ret = adsp_err_get_lnx_err_code( + v->async_err); + goto unlock; + } +unlock: + mutex_unlock(&common.cal_data[CVS_VOCSTRM_COL_CAL]->lock); + mutex_unlock(&common.cal_data[CVS_VOCSTRM_CAL]->lock); +done: + return ret; +} + +static int voice_send_cvs_deregister_cal_cmd(struct voice_data *v) +{ + struct cvs_deregister_cal_data_cmd cvs_dereg_cal_cmd; + int ret = 0; + + memset(&cvs_dereg_cal_cmd, 0, sizeof(cvs_dereg_cal_cmd)); + + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + + ret = -EINVAL; + goto done; + } + + if (!common.apr_q6_cvs) { + pr_err("%s: apr_cvs is NULL\n", __func__); + + ret = -EPERM; + goto done; + } + + cvs_dereg_cal_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + cvs_dereg_cal_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(cvs_dereg_cal_cmd) - APR_HDR_SIZE); + cvs_dereg_cal_cmd.hdr.src_port = + voice_get_idx_for_session(v->session_id); + cvs_dereg_cal_cmd.hdr.dest_port = voice_get_cvs_handle(v); + cvs_dereg_cal_cmd.hdr.token = 0; + if (common.is_per_vocoder_cal_enabled) + cvs_dereg_cal_cmd.hdr.opcode = + VSS_ISTREAM_CMD_DEREGISTER_STATIC_CALIBRATION_DATA; + else + cvs_dereg_cal_cmd.hdr.opcode = + VSS_ISTREAM_CMD_DEREGISTER_CALIBRATION_DATA; + + v->cvs_state = CMD_STATUS_FAIL; + v->async_err = 0; + ret = apr_send_pkt(common.apr_q6_cvs, (uint32_t *) &cvs_dereg_cal_cmd); + if (ret < 0) { + pr_err("%s: Error %d de-registering CVS cal\n", __func__, ret); + goto done; + } + ret = wait_event_timeout(v->cvs_wait, + (v->cvs_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: Command timeout\n", __func__); + goto done; + } + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + v->async_err)); + ret = adsp_err_get_lnx_err_code( + v->async_err); + goto done; + } + +done: + return ret; + +} + +static void voice_load_topo_modules(int cal_index) +{ + uint32_t topology_id; + int ret; + + topology_id = voice_get_topology(cal_index); + ret = q6core_load_unload_topo_modules(topology_id, CORE_LOAD_TOPOLOGY); + if (ret < 0) + pr_debug("%s ret:%d load topo modules %d failed\n", + __func__, ret, topology_id); + +} + +static void voice_unload_topo_modules(void) +{ + uint32_t topology_id; + int i, ret; + + for (i = CVP_VOC_RX_TOPOLOGY_CAL; i <= CVP_VOC_TX_TOPOLOGY_CAL; i++) { + topology_id = voice_get_topology(i); + ret = q6core_load_unload_topo_modules(topology_id, + CORE_UNLOAD_TOPOLOGY); + if (ret < 0) { + pr_debug("%s ret:%d unload topo modules %d failed\n", + __func__, ret, topology_id); + } + } +} + +static int voice_send_cvp_create_cmd(struct voice_data *v) +{ + struct cvp_create_full_ctl_session_cmd cvp_session_cmd; + void *apr_cvp; + int ret = 0; + + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + ret = -EINVAL; + goto done; + } + + apr_cvp = common.apr_q6_cvp; + if (!apr_cvp) { + pr_err("%s: apr_cvp is NULL.\n", __func__); + + ret = -EINVAL; + goto done; + } + + /* create cvp session and wait for response */ + cvp_session_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + cvp_session_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(cvp_session_cmd) - APR_HDR_SIZE); + pr_debug("%s: send create cvp session, pkt size = %d\n", + __func__, cvp_session_cmd.hdr.pkt_size); + cvp_session_cmd.hdr.src_port = + voice_get_idx_for_session(v->session_id); + cvp_session_cmd.hdr.dest_port = 0; + cvp_session_cmd.hdr.token = 0; + + if (voice_get_cvd_int_version(common.cvd_version) >= + CVD_INT_VERSION_2_2) + cvp_session_cmd.hdr.opcode = + VSS_IVOCPROC_CMD_CREATE_FULL_CONTROL_SESSION_V3; + else + cvp_session_cmd.hdr.opcode = + VSS_IVOCPROC_CMD_CREATE_FULL_CONTROL_SESSION_V2; + + voc_get_tx_rx_topology(v, + &cvp_session_cmd.cvp_session.tx_topology_id, + &cvp_session_cmd.cvp_session.rx_topology_id); + + voice_set_topology_specific_info(v, CVP_VOC_RX_TOPOLOGY_CAL); + voice_set_topology_specific_info(v, CVP_VOC_TX_TOPOLOGY_CAL); + + cvp_session_cmd.cvp_session.direction = 2; /*tx and rx*/ + cvp_session_cmd.cvp_session.tx_port_id = v->dev_tx.port_id; + cvp_session_cmd.cvp_session.rx_port_id = v->dev_rx.port_id; + cvp_session_cmd.cvp_session.profile_id = + VSS_ICOMMON_CAL_NETWORK_ID_NONE; + if (common.ec_ref_ext) { + cvp_session_cmd.cvp_session.vocproc_mode = + VSS_IVOCPROC_VOCPROC_MODE_EC_EXT_MIXING; + cvp_session_cmd.cvp_session.ec_ref_port_id = + common.ec_media_fmt_info.port_id; + } else { + cvp_session_cmd.cvp_session.vocproc_mode = + VSS_IVOCPROC_VOCPROC_MODE_EC_INT_MIXING; + cvp_session_cmd.cvp_session.ec_ref_port_id = + VSS_IVOCPROC_PORT_ID_NONE; + } + + pr_debug("tx_topology: %d tx_port_id=%d, rx_port_id=%d, mode: 0x%x\n", + cvp_session_cmd.cvp_session.tx_topology_id, + cvp_session_cmd.cvp_session.tx_port_id, + cvp_session_cmd.cvp_session.rx_port_id, + cvp_session_cmd.cvp_session.vocproc_mode); + pr_debug("rx_topology: %d, profile_id: 0x%x, pkt_size: %d\n", + cvp_session_cmd.cvp_session.rx_topology_id, + cvp_session_cmd.cvp_session.profile_id, + cvp_session_cmd.hdr.pkt_size); + + v->cvp_state = CMD_STATUS_FAIL; + v->async_err = 0; + ret = apr_send_pkt(apr_cvp, (uint32_t *) &cvp_session_cmd); + if (ret < 0) { + pr_err("Fail in sending VOCPROC_FULL_CONTROL_SESSION\n"); + + ret = -EINVAL; + goto done; + } + + ret = wait_event_timeout(v->cvp_wait, + (v->cvp_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + + ret = -EINVAL; + goto done; + } + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + v->async_err)); + ret = adsp_err_get_lnx_err_code( + v->async_err); + goto done; + } + +done: + return ret; +} + +static int voice_send_cvp_register_dev_cfg_cmd(struct voice_data *v) +{ + struct cvp_register_dev_cfg_cmd cvp_reg_dev_cfg_cmd; + struct cal_block_data *cal_block = NULL; + int ret = 0; + + memset(&cvp_reg_dev_cfg_cmd, 0, sizeof(cvp_reg_dev_cfg_cmd)); + + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + + ret = -EINVAL; + goto done; + } + + if (!common.apr_q6_cvp) { + pr_err("%s: apr_cvp is NULL\n", __func__); + + ret = -EPERM; + goto done; + } + + mutex_lock(&common.cal_data[CVP_VOCDEV_CFG_CAL]->lock); + + ret = voice_get_cal(&cal_block, CVP_VOCDEV_CFG_CAL, NULL, + 0, v->session_id); + if (ret < 0) { + pr_err("%s: Voice_get_cal failed for cal %d!\n", + __func__, CVP_VOCDEV_CFG_CAL); + + goto unlock; + } + + cvp_reg_dev_cfg_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + cvp_reg_dev_cfg_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(cvp_reg_dev_cfg_cmd) - APR_HDR_SIZE); + cvp_reg_dev_cfg_cmd.hdr.src_port = + voice_get_idx_for_session(v->session_id); + cvp_reg_dev_cfg_cmd.hdr.dest_port = voice_get_cvp_handle(v); + cvp_reg_dev_cfg_cmd.hdr.token = 0; + cvp_reg_dev_cfg_cmd.hdr.opcode = + VSS_IVOCPROC_CMD_REGISTER_DEVICE_CONFIG; + + cvp_reg_dev_cfg_cmd.cvp_dev_cfg_data.mem_handle = + cal_block->map_data.q6map_handle; + cvp_reg_dev_cfg_cmd.cvp_dev_cfg_data.mem_address_lsw = + lower_32_bits(cal_block->cal_data.paddr); + cvp_reg_dev_cfg_cmd.cvp_dev_cfg_data.mem_address_msw = + msm_audio_populate_upper_32_bits(cal_block->cal_data.paddr); + cvp_reg_dev_cfg_cmd.cvp_dev_cfg_data.mem_size = + cal_block->cal_data.size; + + v->cvp_state = CMD_STATUS_FAIL; + v->async_err = 0; + ret = apr_send_pkt(common.apr_q6_cvp, + (uint32_t *) &cvp_reg_dev_cfg_cmd); + if (ret < 0) { + pr_err("%s: Error %d registering CVP dev cfg cal\n", + __func__, ret); + + ret = -EINVAL; + goto unlock; + } + ret = wait_event_timeout(v->cvp_wait, + (v->cvp_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: Command timeout\n", __func__); + + ret = -EINVAL; + goto unlock; + } + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + v->async_err)); + ret = adsp_err_get_lnx_err_code( + v->async_err); + goto unlock; + } +unlock: + mutex_unlock(&common.cal_data[CVP_VOCDEV_CFG_CAL]->lock); +done: + return ret; +} + +static int voice_send_cvp_deregister_dev_cfg_cmd(struct voice_data *v) +{ + struct cvp_deregister_dev_cfg_cmd cvp_dereg_dev_cfg_cmd; + int ret = 0; + + memset(&cvp_dereg_dev_cfg_cmd, 0, sizeof(cvp_dereg_dev_cfg_cmd)); + + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + + ret = -EINVAL; + goto done; + } + + if (!common.apr_q6_cvp) { + pr_err("%s: apr_cvp is NULL\n", __func__); + + ret = -EPERM; + goto done; + } + + cvp_dereg_dev_cfg_cmd.hdr.hdr_field = + APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + cvp_dereg_dev_cfg_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(cvp_dereg_dev_cfg_cmd) - APR_HDR_SIZE); + cvp_dereg_dev_cfg_cmd.hdr.src_port = + voice_get_idx_for_session(v->session_id); + cvp_dereg_dev_cfg_cmd.hdr.dest_port = voice_get_cvp_handle(v); + cvp_dereg_dev_cfg_cmd.hdr.token = 0; + cvp_dereg_dev_cfg_cmd.hdr.opcode = + VSS_IVOCPROC_CMD_DEREGISTER_DEVICE_CONFIG; + + v->cvp_state = CMD_STATUS_FAIL; + v->async_err = 0; + ret = apr_send_pkt(common.apr_q6_cvp, + (uint32_t *) &cvp_dereg_dev_cfg_cmd); + if (ret < 0) { + pr_err("%s: Error %d de-registering CVP dev cfg cal\n", + __func__, ret); + goto done; + } + ret = wait_event_timeout(v->cvp_wait, + (v->cvp_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: Command timeout\n", __func__); + goto done; + } + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + v->async_err)); + ret = adsp_err_get_lnx_err_code( + v->async_err); + goto done; + } + +done: + return ret; +} + +static int voice_send_cvp_register_cal_cmd(struct voice_data *v) +{ + struct cvp_register_cal_data_cmd cvp_reg_cal_cmd; + struct cal_block_data *cal_block = NULL; + struct cal_block_data *col_data = NULL; + int ret = 0; + + memset(&cvp_reg_cal_cmd, 0, sizeof(cvp_reg_cal_cmd)); + + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + + ret = -EINVAL; + goto done; + } + + if (!common.apr_q6_cvp) { + pr_err("%s: apr_cvp is NULL\n", __func__); + + ret = -EINVAL; + goto done; + } + + mutex_lock(&common.cal_data[CVP_VOCPROC_CAL]->lock); + mutex_lock(&common.cal_data[CVP_VOCPROC_COL_CAL]->lock); + + ret = voice_get_cal(&cal_block, CVP_VOCPROC_CAL, &col_data, + CVP_VOCPROC_COL_CAL, v->session_id); + if (ret < 0) { + pr_err("%s: Voice_get_cal failed for cal %d!\n", + __func__, CVP_VOCPROC_CAL); + + goto unlock; + } + + v->dev_tx.dev_id = ((struct audio_cal_info_vocproc *) + cal_block->cal_info)->tx_acdb_id; + v->dev_rx.dev_id = ((struct audio_cal_info_vocproc *) + cal_block->cal_info)->rx_acdb_id; + pr_debug("%s: %s: Tx acdb id = %d and Rx acdb id = %d", __func__, + voc_get_session_name(v->session_id), v->dev_tx.dev_id, + v->dev_rx.dev_id); + + memcpy(&cvp_reg_cal_cmd.cvp_cal_data.column_info[0], + (void *) &((struct audio_cal_info_voc_col *) + col_data->cal_info)->data, + col_data->cal_data.size); + + cvp_reg_cal_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + cvp_reg_cal_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(cvp_reg_cal_cmd) - APR_HDR_SIZE); + cvp_reg_cal_cmd.hdr.src_port = + voice_get_idx_for_session(v->session_id); + cvp_reg_cal_cmd.hdr.dest_port = voice_get_cvp_handle(v); + cvp_reg_cal_cmd.hdr.token = 0; + if (common.is_per_vocoder_cal_enabled) + cvp_reg_cal_cmd.hdr.opcode = + VSS_IVOCPROC_CMD_REGISTER_STATIC_CALIBRATION_DATA; + else + cvp_reg_cal_cmd.hdr.opcode = + VSS_IVOCPROC_CMD_REGISTER_CALIBRATION_DATA_V2; + + cvp_reg_cal_cmd.cvp_cal_data.cal_mem_handle = + cal_block->map_data.q6map_handle; + cvp_reg_cal_cmd.cvp_cal_data.cal_mem_address_lsw = + lower_32_bits(cal_block->cal_data.paddr); + cvp_reg_cal_cmd.cvp_cal_data.cal_mem_address_msw = + msm_audio_populate_upper_32_bits(cal_block->cal_data.paddr); + cvp_reg_cal_cmd.cvp_cal_data.cal_mem_size = + cal_block->cal_data.size; + + v->cvp_state = CMD_STATUS_FAIL; + v->async_err = 0; + ret = apr_send_pkt(common.apr_q6_cvp, (uint32_t *) &cvp_reg_cal_cmd); + if (ret < 0) { + pr_err("%s: Error %d registering CVP cal\n", __func__, ret); + + ret = -EINVAL; + goto unlock; + } + ret = wait_event_timeout(v->cvp_wait, + (v->cvp_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: Command timeout\n", __func__); + + ret = -EINVAL; + goto unlock; + } + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + v->async_err)); + ret = adsp_err_get_lnx_err_code( + v->async_err); + goto unlock; + } +unlock: + mutex_unlock(&common.cal_data[CVP_VOCPROC_COL_CAL]->lock); + mutex_unlock(&common.cal_data[CVP_VOCPROC_CAL]->lock); +done: + return ret; +} + +static int voice_send_cvp_deregister_cal_cmd(struct voice_data *v) +{ + struct cvp_deregister_cal_data_cmd cvp_dereg_cal_cmd; + int ret = 0; + + memset(&cvp_dereg_cal_cmd, 0, sizeof(cvp_dereg_cal_cmd)); + + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + + ret = -EINVAL; + goto done; + } + + if (!common.apr_q6_cvp) { + pr_err("%s: apr_cvp is NULL.\n", __func__); + + ret = -EPERM; + goto done; + } + + cvp_dereg_cal_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + cvp_dereg_cal_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(cvp_dereg_cal_cmd) - APR_HDR_SIZE); + cvp_dereg_cal_cmd.hdr.src_port = + voice_get_idx_for_session(v->session_id); + cvp_dereg_cal_cmd.hdr.dest_port = voice_get_cvp_handle(v); + cvp_dereg_cal_cmd.hdr.token = 0; + if (common.is_per_vocoder_cal_enabled) + cvp_dereg_cal_cmd.hdr.opcode = + VSS_IVOCPROC_CMD_DEREGISTER_STATIC_CALIBRATION_DATA; + else + cvp_dereg_cal_cmd.hdr.opcode = + VSS_IVOCPROC_CMD_DEREGISTER_CALIBRATION_DATA; + + v->cvp_state = CMD_STATUS_FAIL; + v->async_err = 0; + ret = apr_send_pkt(common.apr_q6_cvp, (uint32_t *) &cvp_dereg_cal_cmd); + if (ret < 0) { + pr_err("%s: Error %d de-registering CVP cal\n", __func__, ret); + goto done; + } + ret = wait_event_timeout(v->cvp_wait, + (v->cvp_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: Command timeout\n", __func__); + goto done; + } + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + v->async_err)); + ret = adsp_err_get_lnx_err_code( + v->async_err); + goto done; + } + +done: + return ret; +} + +static int voice_send_cvp_register_vol_cal_cmd(struct voice_data *v) +{ + struct cvp_register_vol_cal_data_cmd cvp_reg_vol_cal_cmd; + struct cal_block_data *cal_block = NULL; + struct cal_block_data *col_data = NULL; + int ret = 0; + + memset(&cvp_reg_vol_cal_cmd, 0, sizeof(cvp_reg_vol_cal_cmd)); + + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + + ret = -EINVAL; + goto done; + } + + if (!common.apr_q6_cvp) { + pr_err("%s: apr_cvp is NULL\n", __func__); + + ret = -EINVAL; + goto done; + } + + mutex_lock(&common.cal_data[CVP_VOCVOL_CAL]->lock); + mutex_lock(&common.cal_data[CVP_VOCVOL_COL_CAL]->lock); + + ret = voice_get_cal(&cal_block, CVP_VOCVOL_CAL, &col_data, + CVP_VOCVOL_COL_CAL, v->session_id); + if (ret < 0) { + pr_err("%s: Voice_get_cal failed for cal %d!\n", + __func__, CVP_VOCVOL_CAL); + + goto unlock; + } + + memcpy(&cvp_reg_vol_cal_cmd.cvp_vol_cal_data.column_info[0], + (void *) &((struct audio_cal_info_voc_col *) + col_data->cal_info)->data, + col_data->cal_data.size); + + cvp_reg_vol_cal_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + cvp_reg_vol_cal_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(cvp_reg_vol_cal_cmd) - APR_HDR_SIZE); + cvp_reg_vol_cal_cmd.hdr.src_port = + voice_get_idx_for_session(v->session_id); + cvp_reg_vol_cal_cmd.hdr.dest_port = voice_get_cvp_handle(v); + cvp_reg_vol_cal_cmd.hdr.token = 0; + if (common.is_per_vocoder_cal_enabled) + cvp_reg_vol_cal_cmd.hdr.opcode = + VSS_IVOCPROC_CMD_REGISTER_DYNAMIC_CALIBRATION_DATA; + else + cvp_reg_vol_cal_cmd.hdr.opcode = + VSS_IVOCPROC_CMD_REGISTER_VOL_CALIBRATION_DATA; + + cvp_reg_vol_cal_cmd.cvp_vol_cal_data.cal_mem_handle = + cal_block->map_data.q6map_handle; + cvp_reg_vol_cal_cmd.cvp_vol_cal_data.cal_mem_address_lsw = + lower_32_bits(cal_block->cal_data.paddr); + cvp_reg_vol_cal_cmd.cvp_vol_cal_data.cal_mem_address_msw = + msm_audio_populate_upper_32_bits(cal_block->cal_data.paddr); + cvp_reg_vol_cal_cmd.cvp_vol_cal_data.cal_mem_size = + cal_block->cal_data.size; + + v->cvp_state = CMD_STATUS_FAIL; + v->async_err = 0; + ret = apr_send_pkt(common.apr_q6_cvp, + (uint32_t *) &cvp_reg_vol_cal_cmd); + if (ret < 0) { + pr_err("%s: Error %d registering CVP vol cal\n", __func__, ret); + + ret = -EINVAL; + goto unlock; + } + ret = wait_event_timeout(v->cvp_wait, + (v->cvp_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: Command timeout\n", __func__); + + ret = -EINVAL; + goto unlock; + } + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + v->async_err)); + ret = adsp_err_get_lnx_err_code( + v->async_err); + goto unlock; + } +unlock: + mutex_unlock(&common.cal_data[CVP_VOCVOL_COL_CAL]->lock); + mutex_unlock(&common.cal_data[CVP_VOCVOL_CAL]->lock); +done: + return ret; +} + +static int voice_send_cvp_deregister_vol_cal_cmd(struct voice_data *v) +{ + struct cvp_deregister_vol_cal_data_cmd cvp_dereg_vol_cal_cmd; + int ret = 0; + + memset(&cvp_dereg_vol_cal_cmd, 0, sizeof(cvp_dereg_vol_cal_cmd)); + + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + + ret = -EINVAL; + goto done; + } + + if (!common.apr_q6_cvp) { + pr_err("%s: apr_cvp is NULL\n", __func__); + + ret = -EPERM; + goto done; + } + + cvp_dereg_vol_cal_cmd.hdr.hdr_field = + APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + cvp_dereg_vol_cal_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(cvp_dereg_vol_cal_cmd) - APR_HDR_SIZE); + cvp_dereg_vol_cal_cmd.hdr.src_port = + voice_get_idx_for_session(v->session_id); + cvp_dereg_vol_cal_cmd.hdr.dest_port = voice_get_cvp_handle(v); + cvp_dereg_vol_cal_cmd.hdr.token = 0; + if (common.is_per_vocoder_cal_enabled) + cvp_dereg_vol_cal_cmd.hdr.opcode = + VSS_IVOCPROC_CMD_DEREGISTER_DYNAMIC_CALIBRATION_DATA; + else + cvp_dereg_vol_cal_cmd.hdr.opcode = + VSS_IVOCPROC_CMD_DEREGISTER_VOL_CALIBRATION_DATA; + + v->cvp_state = CMD_STATUS_FAIL; + v->async_err = 0; + ret = apr_send_pkt(common.apr_q6_cvp, + (uint32_t *) &cvp_dereg_vol_cal_cmd); + if (ret < 0) { + pr_err("%s: Error %d de-registering CVP vol cal\n", + __func__, ret); + goto done; + } + ret = wait_event_timeout(v->cvp_wait, + (v->cvp_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: Command timeout\n", __func__); + goto done; + } + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + v->async_err)); + ret = adsp_err_get_lnx_err_code( + v->async_err); + goto done; + } + +done: + return ret; +} + +static int voice_map_memory_physical_cmd(struct voice_data *v, + struct mem_map_table *table_info, + dma_addr_t phys, + uint32_t size, + uint32_t token) +{ + struct vss_imemory_cmd_map_physical_t mvm_map_phys_cmd; + uint32_t *memtable; + int ret = 0; + + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + ret = -EINVAL; + goto fail; + } + + if (!common.apr_q6_mvm) { + pr_err("%s: apr_mvm is NULL.\n", __func__); + ret = -EINVAL; + goto fail; + } + + if (!table_info->data) { + pr_err("%s: memory table is NULL.\n", __func__); + ret = -EINVAL; + goto fail; + } + + memtable = (uint32_t *) table_info->data; + + /* + * Store next table descriptor's address(64 bit) as NULL as there + * is only one memory block + */ + memtable[0] = 0; + memtable[1] = 0; + + /* Store next table descriptor's size */ + memtable[2] = 0; + + /* Store shared mem adddress (64 bit) */ + memtable[3] = lower_32_bits(phys); + memtable[4] = msm_audio_populate_upper_32_bits(phys); + + /* Store shared memory size */ + memtable[5] = size; + + mvm_map_phys_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + mvm_map_phys_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(mvm_map_phys_cmd) - APR_HDR_SIZE); + mvm_map_phys_cmd.hdr.src_port = + voice_get_idx_for_session(v->session_id); + mvm_map_phys_cmd.hdr.dest_port = voice_get_mvm_handle(v); + mvm_map_phys_cmd.hdr.token = token; + mvm_map_phys_cmd.hdr.opcode = VSS_IMEMORY_CMD_MAP_PHYSICAL; + + mvm_map_phys_cmd.table_descriptor.mem_address_lsw = + lower_32_bits(table_info->phys); + mvm_map_phys_cmd.table_descriptor.mem_address_msw = + msm_audio_populate_upper_32_bits(table_info->phys); + mvm_map_phys_cmd.table_descriptor.mem_size = + sizeof(struct vss_imemory_block_t) + + sizeof(struct vss_imemory_table_descriptor_t); + mvm_map_phys_cmd.is_cached = true; + mvm_map_phys_cmd.cache_line_size = 128; + mvm_map_phys_cmd.access_mask = 3; + mvm_map_phys_cmd.page_align = 4096; + mvm_map_phys_cmd.min_data_width = 8; + mvm_map_phys_cmd.max_data_width = 64; + + pr_debug("%s: next table desc: add: %lld, size: %d\n", + __func__, *((uint64_t *) memtable), + *(((uint32_t *) memtable) + 2)); + pr_debug("%s: phy add of of mem being mapped LSW:0x%x, MSW:0x%x size: %d\n", + __func__, *(((uint32_t *) memtable) + 3), + *(((uint32_t *) memtable) + 4), *(((uint32_t *) memtable) + 5)); + + v->mvm_state = CMD_STATUS_FAIL; + v->async_err = 0; + ret = apr_send_pkt(common.apr_q6_mvm, (uint32_t *) &mvm_map_phys_cmd); + if (ret < 0) { + pr_err("%s: Error %d sending mvm map phy cmd\n", __func__, ret); + + goto fail; + } + + ret = wait_event_timeout(v->mvm_wait, + (v->mvm_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: Command timeout\n", __func__); + + goto fail; + } + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + v->async_err)); + ret = adsp_err_get_lnx_err_code( + v->async_err); + goto fail; + } + + return 0; + +fail: + return ret; +} + +static int voice_pause_voice_call(struct voice_data *v) +{ + struct apr_hdr mvm_pause_voice_cmd; + void *apr_mvm; + int ret = 0; + + pr_debug("%s\n", __func__); + + if (v == NULL) { + pr_err("%s: Voice data is NULL\n", __func__); + + ret = -EINVAL; + goto done; + } + + apr_mvm = common.apr_q6_mvm; + if (!apr_mvm) { + pr_err("%s: apr_mvm is NULL.\n", __func__); + + ret = -EINVAL; + goto done; + } + + mvm_pause_voice_cmd.hdr_field = + APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + mvm_pause_voice_cmd.pkt_size = + APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(mvm_pause_voice_cmd) - APR_HDR_SIZE); + mvm_pause_voice_cmd.src_port = + voice_get_idx_for_session(v->session_id); + mvm_pause_voice_cmd.dest_port = voice_get_mvm_handle(v); + mvm_pause_voice_cmd.token = 0; + mvm_pause_voice_cmd.opcode = VSS_IMVM_CMD_PAUSE_VOICE; + v->mvm_state = CMD_STATUS_FAIL; + v->async_err = 0; + + pr_debug("%s: send mvm_pause_voice_cmd pkt size = %d\n", + __func__, mvm_pause_voice_cmd.pkt_size); + + ret = apr_send_pkt(apr_mvm, + (uint32_t *)&mvm_pause_voice_cmd); + if (ret < 0) { + pr_err("Fail in sending VSS_IMVM_CMD_PAUSE_VOICE\n"); + + ret = -EINVAL; + goto done; + } + + ret = wait_event_timeout(v->mvm_wait, + (v->mvm_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: Command timeout\n", __func__); + + ret = -EINVAL; + goto done; + } + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + v->async_err)); + ret = adsp_err_get_lnx_err_code( + v->async_err); + goto done; + } + +done: + return ret; +} + +static int voice_map_cal_memory(struct cal_block_data *cal_block, + uint32_t session_id) +{ + int result = 0; + int voc_index; + struct voice_data *v = NULL; + + pr_debug("%s\n", __func__); + + if (cal_block == NULL) { + pr_err("%s: Cal block is NULL!\n", __func__); + + result = -EINVAL; + goto done; + } + + if (cal_block->cal_data.paddr == 0) { + pr_debug("%s: No address to map!\n", __func__); + + result = -EINVAL; + goto done; + } + + if (cal_block->map_data.map_size == 0) { + pr_debug("%s: Map size is 0!\n", __func__); + + result = -EINVAL; + goto done; + } + + voc_index = voice_get_idx_for_session(session_id); + if (voc_index < 0) { + pr_err("%s: Invalid session ID %d\n", __func__, session_id); + + goto done; + } + + mutex_lock(&common.common_lock); + v = &common.voice[voc_index]; + + result = voice_map_memory_physical_cmd(v, + &common.cal_mem_map_table, + (dma_addr_t)cal_block->cal_data.paddr, + cal_block->map_data.map_size, + VOC_CAL_MEM_MAP_TOKEN); + if (result < 0) { + pr_err("%s: Mmap did not work! addr = 0x%pK, size = %zd\n", + __func__, + &cal_block->cal_data.paddr, + cal_block->map_data.map_size); + + goto done_unlock; + } + + cal_block->map_data.q6map_handle = common.cal_mem_handle; +done_unlock: + mutex_unlock(&common.common_lock); +done: + return result; +} + +static int remap_cal_data(struct cal_block_data *cal_block, + uint32_t session_id) +{ + int ret = 0; + + pr_debug("%s\n", __func__); + + if (cal_block->map_data.dma_buf == NULL) { + pr_err("%s: No ION allocation for session_id %d!\n", + __func__, session_id); + ret = -EINVAL; + goto done; + } + + if ((cal_block->map_data.map_size > 0) && + (cal_block->map_data.q6map_handle == 0)) { + + /* cal type not used */ + ret = voice_map_cal_memory(cal_block, session_id); + if (ret < 0) { + pr_err("%s: Mmap did not work! size = %zd\n", + __func__, cal_block->map_data.map_size); + + goto done; + } + } else { + pr_debug("%s: Cal block 0x%pK, size %zd already mapped. Q6 map handle = %d\n", + __func__, &cal_block->cal_data.paddr, + cal_block->map_data.map_size, + cal_block->map_data.q6map_handle); + } +done: + return ret; +} + +static int voice_unmap_cal_memory(int32_t cal_type, + struct cal_block_data *cal_block) +{ + int result = 0; + int result2 = 0; + int i; + struct voice_data *v = NULL; + + pr_debug("%s\n", __func__); + + if (cal_block == NULL) { + pr_err("%s: Cal block is NULL!\n", __func__); + + result = -EINVAL; + goto done; + } + + if (cal_block->map_data.q6map_handle == 0) { + pr_debug("%s: Q6 handle is not set!\n", __func__); + + result = -EINVAL; + goto done; + } + + mutex_lock(&common.common_lock); + + for (i = 0; i < MAX_VOC_SESSIONS; i++) { + v = &common.voice[i]; + + mutex_lock(&v->lock); + if (is_voc_state_active(v->voc_state)) { + result2 = voice_pause_voice_call(v); + if (result2 < 0) { + pr_err("%s: Voice_pause_voice_call failed for session 0x%x, err %d!\n", + __func__, v->session_id, result2); + + result = result2; + } + + if (cal_type == CVP_VOCPROC_DYNAMIC_CAL_TYPE) + voice_send_cvp_deregister_vol_cal_cmd(v); + else if (cal_type == CVP_VOCPROC_STATIC_CAL_TYPE) + voice_send_cvp_deregister_cal_cmd(v); + else if (cal_type == CVP_VOCDEV_CFG_CAL_TYPE) + voice_send_cvp_deregister_dev_cfg_cmd(v); + else if (cal_type == CVS_VOCSTRM_STATIC_CAL_TYPE) + voice_send_cvs_deregister_cal_cmd(v); + else + pr_err("%s: Invalid cal type %d!\n", + __func__, cal_type); + + result2 = voice_send_start_voice_cmd(v); + if (result2) { + pr_err("%s: Voice_send_start_voice_cmd failed for session 0x%x, err %d!\n", + __func__, v->session_id, result2); + + result = result2; + } + } + + if ((cal_block->map_data.q6map_handle != 0) && + (!is_other_session_active(v->session_id))) { + + result2 = voice_send_mvm_unmap_memory_physical_cmd( + v, cal_block->map_data.q6map_handle); + if (result2) { + pr_err("%s: Voice_send_mvm_unmap_memory_physical_cmd failed for session 0x%x, err %d!\n", + __func__, v->session_id, result2); + + result = result2; + } + cal_block->map_data.q6map_handle = 0; + } + mutex_unlock(&v->lock); + } + mutex_unlock(&common.common_lock); +done: + return result; +} + +int voc_register_vocproc_vol_table(void) +{ + int result = 0; + int result2 = 0; + int i; + struct voice_data *v = NULL; + + pr_debug("%s\n", __func__); + + mutex_lock(&common.common_lock); + for (i = 0; i < MAX_VOC_SESSIONS; i++) { + v = &common.voice[i]; + + mutex_lock(&v->lock); + if (is_voc_state_active(v->voc_state)) { + result2 = voice_send_cvp_register_vol_cal_cmd(v); + if (result2 < 0) { + pr_err("%s: Failed to register vocvol table for session 0x%x!\n", + __func__, v->session_id); + + result = result2; + /* Still try to register other sessions */ + } + } + mutex_unlock(&v->lock); + } + + mutex_unlock(&common.common_lock); + return result; +} + +int voc_deregister_vocproc_vol_table(void) +{ + int result = 0; + int success = 0; + int i; + struct voice_data *v = NULL; + + pr_debug("%s\n", __func__); + + mutex_lock(&common.common_lock); + for (i = 0; i < MAX_VOC_SESSIONS; i++) { + v = &common.voice[i]; + + mutex_lock(&v->lock); + if (is_voc_state_active(v->voc_state)) { + result = voice_send_cvp_deregister_vol_cal_cmd(v); + if (result < 0) { + pr_err("%s: Failed to deregister vocvol table for session 0x%x!\n", + __func__, v->session_id); + + mutex_unlock(&v->lock); + mutex_unlock(&common.common_lock); + if (success) { + pr_err("%s: Try to re-register all deregistered sessions!\n", + __func__); + + voc_register_vocproc_vol_table(); + } + goto done; + } else { + success = 1; + } + } + mutex_unlock(&v->lock); + } + mutex_unlock(&common.common_lock); +done: + return result; +} + +int voc_map_rtac_block(struct rtac_cal_block_data *cal_block) +{ + int result = 0; + struct voice_data *v = NULL; + + pr_debug("%s\n", __func__); + + if (cal_block == NULL) { + pr_err("%s: cal_block is NULL!\n", + __func__); + + result = -EINVAL; + goto done; + } + + if (cal_block->cal_data.paddr == 0) { + pr_debug("%s: No address to map!\n", + __func__); + + result = -EINVAL; + goto done; + } + + if (cal_block->map_data.map_size == 0) { + pr_debug("%s: map size is 0!\n", + __func__); + + result = -EINVAL; + goto done; + } + + mutex_lock(&common.common_lock); + /* use first session */ + v = &common.voice[0]; + mutex_lock(&v->lock); + + if (!is_rtac_memory_allocated()) { + result = voice_alloc_rtac_mem_map_table(); + if (result < 0) { + pr_err("%s: RTAC alloc mem map table did not work! addr = 0x%pK, size = %d\n", + __func__, + &cal_block->cal_data.paddr, + cal_block->map_data.map_size); + + goto done_unlock; + } + } + + result = voice_map_memory_physical_cmd(v, + &common.rtac_mem_map_table, + (dma_addr_t)cal_block->cal_data.paddr, + cal_block->map_data.map_size, + VOC_RTAC_MEM_MAP_TOKEN); + if (result < 0) { + pr_err("%s: RTAC mmap did not work! addr = 0x%pK, size = %d\n", + __func__, + &cal_block->cal_data.paddr, + cal_block->map_data.map_size); + + free_rtac_map_table(); + goto done_unlock; + } + + cal_block->map_data.map_handle = common.rtac_mem_handle; +done_unlock: + mutex_unlock(&v->lock); + mutex_unlock(&common.common_lock); +done: + return result; +} + +int voc_unmap_rtac_block(uint32_t *mem_map_handle) +{ + int result = 0; + struct voice_data *v = NULL; + + pr_debug("%s\n", __func__); + + if (mem_map_handle == NULL) { + pr_debug("%s: Map handle is NULL, nothing to unmap\n", + __func__); + + goto done; + } + + if (*mem_map_handle == 0) { + pr_debug("%s: Map handle is 0, nothing to unmap\n", + __func__); + + goto done; + } + + mutex_lock(&common.common_lock); + /* Use first session */ + /* Only used for apr wait lock */ + v = &common.voice[0]; + mutex_lock(&v->lock); + + result = voice_send_mvm_unmap_memory_physical_cmd( + v, *mem_map_handle); + if (result) { + pr_err("%s: voice_send_mvm_unmap_memory_physical_cmd Failed for session 0x%x!\n", + __func__, v->session_id); + } else { + *mem_map_handle = 0; + common.rtac_mem_handle = 0; + free_rtac_map_table(); + } + mutex_unlock(&v->lock); + mutex_unlock(&common.common_lock); +done: + return result; +} + +static int voice_send_cvp_channel_info_v2(struct voice_data *v, + uint32_t param_type) +{ + int ret; + struct cvp_set_channel_info_cmd_v2 cvp_set_channel_info_cmd; + void *apr_cvp; + u16 cvp_handle; + struct vss_icommon_param_data_channel_info_v2_t + *channel_info_param_data = + &cvp_set_channel_info_cmd. + cvp_set_ch_info_param_v2.param_data; + struct vss_param_vocproc_dev_channel_info_t *channel_info = + &channel_info_param_data->channel_info; + + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + ret = -EINVAL; + goto done; + } + + apr_cvp = common.apr_q6_cvp; + if (!apr_cvp) { + pr_err("%s: apr_cvp is NULL\n", __func__); + ret = -EINVAL; + goto done; + } + + cvp_handle = voice_get_cvp_handle(v); + memset(&cvp_set_channel_info_cmd, 0, sizeof(cvp_set_channel_info_cmd)); + + cvp_set_channel_info_cmd.hdr.hdr_field = + APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + cvp_set_channel_info_cmd.hdr.pkt_size = + APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(cvp_set_channel_info_cmd) - APR_HDR_SIZE); + cvp_set_channel_info_cmd.hdr.src_svc = 0; + cvp_set_channel_info_cmd.hdr.src_domain = APR_DOMAIN_APPS; + cvp_set_channel_info_cmd.hdr.src_port = + voice_get_idx_for_session(v->session_id); + cvp_set_channel_info_cmd.hdr.dest_svc = 0; + cvp_set_channel_info_cmd.hdr.dest_domain = APR_DOMAIN_ADSP; + cvp_set_channel_info_cmd.hdr.dest_port = cvp_handle; + cvp_set_channel_info_cmd.hdr.token = 0; + cvp_set_channel_info_cmd.hdr.opcode = VSS_ICOMMON_CMD_SET_PARAM_V2; + + cvp_set_channel_info_cmd.cvp_set_ch_info_param_v2.mem_size = + sizeof(struct vss_icommon_param_data_channel_info_v2_t); + + channel_info_param_data->module_id = VSS_MODULE_CVD_GENERIC; + channel_info_param_data->param_size = + sizeof(struct vss_param_vocproc_dev_channel_info_t); + + /* Device specific data */ + switch (param_type) { + case RX_PATH: + channel_info_param_data->param_id = + VSS_PARAM_VOCPROC_RX_CHANNEL_INFO; + channel_info->num_channels = v->dev_rx.no_of_channels; + channel_info->bits_per_sample = v->dev_rx.bits_per_sample; + memcpy(&channel_info->channel_mapping, + v->dev_rx.channel_mapping, + VSS_NUM_CHANNELS_MAX * sizeof(uint8_t)); + break; + + case TX_PATH: + channel_info_param_data->param_id = + VSS_PARAM_VOCPROC_TX_CHANNEL_INFO; + channel_info->num_channels = v->dev_tx.no_of_channels; + channel_info->bits_per_sample = v->dev_tx.bits_per_sample; + memcpy(&channel_info->channel_mapping, + v->dev_tx.channel_mapping, + VSS_NUM_CHANNELS_MAX * sizeof(uint8_t)); + break; + + case EC_REF_PATH: + channel_info_param_data->param_id = + VSS_PARAM_VOCPROC_EC_REF_CHANNEL_INFO; + channel_info->num_channels = v->dev_rx.no_of_channels; + channel_info->bits_per_sample = v->dev_rx.bits_per_sample; + memcpy(&channel_info->channel_mapping, + v->dev_rx.channel_mapping, + VSS_NUM_CHANNELS_MAX * sizeof(uint8_t)); + break; + default: + pr_err("%s: Invalid param type\n", + __func__); + ret = -EINVAL; + goto done; + } + + + v->cvp_state = CMD_STATUS_FAIL; + v->async_err = 0; + ret = apr_send_pkt(apr_cvp, (uint32_t *) &cvp_set_channel_info_cmd); + if (ret < 0) { + pr_err("%s: Failed to send VSS_ICOMMON_CMD_SET_PARAM_V2\n", + __func__); + goto done; + } + + ret = wait_event_timeout(v->cvp_wait, + (v->cvp_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + ret = -ETIMEDOUT; + goto done; + } + + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s] handle = %d\n", __func__, + adsp_err_get_err_str(v->async_err), cvp_handle); + ret = adsp_err_get_lnx_err_code(v->async_err); + goto done; + } + ret = 0; +done: + return ret; +} + +static int voice_send_cvp_channel_info_cmd(struct voice_data *v) +{ + int ret = 0; + + ret = voice_send_cvp_channel_info_v2(v, RX_PATH); + if (ret < 0) { + pr_err("%s: Error in sending cvp_channel_info RX: %d\n", + __func__, ret); + goto done; + } + + ret = voice_send_cvp_channel_info_v2(v, TX_PATH); + if (ret < 0) { + pr_err("%s: Error in sending cvp_channel_info TX: %d\n", + __func__, ret); + goto done; + } + + ret = voice_send_cvp_channel_info_v2(v, EC_REF_PATH); + if (ret < 0) { + pr_err("%s: Error in sending cvp_channel_info EC Ref: %d\n", + __func__, ret); + goto done; + } +done: + return ret; +} + +static int voice_send_cvp_ch_mixer_info_v2(struct voice_data *v) +{ + int ret; + struct cvp_set_channel_mixer_info_cmd_v2 cvp_set_ch_mixer_info_cmd; + void *apr_cvp; + u16 cvp_handle; + struct vss_icommon_param_data_ch_mixer_v2_t *cvp_config_param_data = + &cvp_set_ch_mixer_info_cmd. + cvp_set_ch_mixer_param_v2.param_data; + struct vss_param_channel_mixer_info_t *ch_mixer_info = + &cvp_config_param_data->ch_mixer_info; + + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + ret = -EINVAL; + goto done; + } + + apr_cvp = common.apr_q6_cvp; + if (!apr_cvp) { + pr_err("%s: apr_cvp is NULL\n", __func__); + ret = -EINVAL; + goto done; + } + + cvp_handle = voice_get_cvp_handle(v); + memset(&cvp_set_ch_mixer_info_cmd, 0, + sizeof(cvp_set_ch_mixer_info_cmd)); + + cvp_set_ch_mixer_info_cmd.hdr.hdr_field = + APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + cvp_set_ch_mixer_info_cmd.hdr.pkt_size = + APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(cvp_set_ch_mixer_info_cmd) - APR_HDR_SIZE); + cvp_set_ch_mixer_info_cmd.hdr.src_svc = 0; + cvp_set_ch_mixer_info_cmd.hdr.src_domain = APR_DOMAIN_APPS; + cvp_set_ch_mixer_info_cmd.hdr.src_port = + voice_get_idx_for_session(v->session_id); + cvp_set_ch_mixer_info_cmd.hdr.dest_svc = 0; + cvp_set_ch_mixer_info_cmd.hdr.dest_domain = APR_DOMAIN_ADSP; + cvp_set_ch_mixer_info_cmd.hdr.dest_port = cvp_handle; + cvp_set_ch_mixer_info_cmd.hdr.token = VOC_GENERIC_SET_PARAM_TOKEN; + cvp_set_ch_mixer_info_cmd.hdr.opcode = VSS_ICOMMON_CMD_SET_PARAM_V2; + cvp_set_ch_mixer_info_cmd.cvp_set_ch_mixer_param_v2.mem_size = + sizeof(struct vss_icommon_param_data_ch_mixer_v2_t); + + cvp_config_param_data->module_id = AUDPROC_MODULE_ID_MFC; + cvp_config_param_data->param_id = + AUDPROC_CHMIXER_PARAM_ID_COEFF; + cvp_config_param_data->param_size = + sizeof(struct vss_param_channel_mixer_info_t); + + ch_mixer_info->index = 0; + ch_mixer_info->num_output_channels = v->dev_rx.no_of_channels; + /* + * Configure Rx input to be mono for channel mixer as the DSP + * configures vocproc input as mono. + */ + ch_mixer_info->num_input_channels = NUM_CHANNELS_MONO; + ch_mixer_info->out_channel_map[0] = PCM_CHANNEL_L; + ch_mixer_info->out_channel_map[1] = PCM_CHANNEL_R; + ch_mixer_info->in_channel_map[0] = PCM_CHANNEL_L; + ch_mixer_info->channel_weight_coeff[0][0] = GAIN_Q14_FORMAT(1); + ch_mixer_info->channel_weight_coeff[1][0] = GAIN_Q14_FORMAT(1); + ch_mixer_info->reserved = 0; + + v->cvp_state = CMD_STATUS_FAIL; + v->async_err = 0; + ret = apr_send_pkt(apr_cvp, (uint32_t *)&cvp_set_ch_mixer_info_cmd); + if (ret < 0) { + pr_err("%s: Failed to send VSS_ICOMMON_CMD_SET_PARAM_V2 %d\n", + __func__, ret); + goto done; + } + ret = wait_event_timeout(v->cvp_wait, + (v->cvp_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + ret = -ETIMEDOUT; + goto done; + } + + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s] handle = %d\n", __func__, + adsp_err_get_err_str(v->async_err), cvp_handle); + ret = adsp_err_get_lnx_err_code(v->async_err); + goto done; + } + ret = 0; +done: + return ret; +} + +static int voice_send_cvp_mfc_config_v2(struct voice_data *v) +{ + int ret; + struct cvp_set_mfc_config_cmd_v2 cvp_set_mfc_config_cmd; + void *apr_cvp; + u16 cvp_handle; + uint8_t ch_idx; + struct vss_icommon_param_data_mfc_config_v2_t *cvp_config_param_data = + &cvp_set_mfc_config_cmd.cvp_set_mfc_param_v2.param_data; + struct vss_param_mfc_config_info_t *mfc_config_info = + &cvp_config_param_data->mfc_config_info; + + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + ret = -EINVAL; + goto done; + } + + apr_cvp = common.apr_q6_cvp; + if (!apr_cvp) { + pr_err("%s: apr_cvp is NULL\n", __func__); + ret = -EINVAL; + goto done; + } + + cvp_handle = voice_get_cvp_handle(v); + memset(&cvp_set_mfc_config_cmd, 0, sizeof(cvp_set_mfc_config_cmd)); + + cvp_set_mfc_config_cmd.hdr.hdr_field = + APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + cvp_set_mfc_config_cmd.hdr.pkt_size = + APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(cvp_set_mfc_config_cmd) - APR_HDR_SIZE); + cvp_set_mfc_config_cmd.hdr.src_svc = 0; + cvp_set_mfc_config_cmd.hdr.src_domain = APR_DOMAIN_APPS; + cvp_set_mfc_config_cmd.hdr.src_port = + voice_get_idx_for_session(v->session_id); + cvp_set_mfc_config_cmd.hdr.dest_svc = 0; + cvp_set_mfc_config_cmd.hdr.dest_domain = APR_DOMAIN_ADSP; + cvp_set_mfc_config_cmd.hdr.dest_port = cvp_handle; + cvp_set_mfc_config_cmd.hdr.token = 0; + cvp_set_mfc_config_cmd.hdr.opcode = VSS_ICOMMON_CMD_SET_PARAM_V2; + cvp_set_mfc_config_cmd.cvp_set_mfc_param_v2.mem_size = + sizeof(struct vss_icommon_param_data_mfc_config_v2_t); + + cvp_config_param_data->module_id = AUDPROC_MODULE_ID_MFC; + cvp_config_param_data->param_id = + AUDPROC_PARAM_ID_MFC_OUTPUT_MEDIA_FORMAT; + cvp_config_param_data->param_size = + sizeof(struct vss_param_mfc_config_info_t); + + mfc_config_info->num_channels = v->dev_rx.no_of_channels; + mfc_config_info->bits_per_sample = 16; + mfc_config_info->sample_rate = v->dev_rx.sample_rate; + + /* + * Do not use memcpy here as channel_type in mfc_config structure is a + * uint16_t array while channel_mapping array of device is of uint8_t + */ + for (ch_idx = 0; ch_idx < VSS_NUM_CHANNELS_MAX; ch_idx++) { + mfc_config_info->channel_type[ch_idx] = + v->dev_rx.channel_mapping[ch_idx]; + } + + v->cvp_state = CMD_STATUS_FAIL; + v->async_err = 0; + ret = apr_send_pkt(apr_cvp, (uint32_t *)&cvp_set_mfc_config_cmd); + if (ret < 0) { + pr_err("%s: Failed to send VSS_ICOMMON_CMD_SET_PARAM_V2 %d\n", + __func__, ret); + goto done; + } + ret = wait_event_timeout(v->cvp_wait, + (v->cvp_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + ret = -ETIMEDOUT; + goto done; + } + + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s] handle = %d\n", __func__, + adsp_err_get_err_str(v->async_err), cvp_handle); + ret = adsp_err_get_lnx_err_code(v->async_err); + goto done; + } + ret = 0; +done: + return ret; +} + +static int voice_send_cvp_mfc_config_cmd(struct voice_data *v) +{ + int ret = 0; + + if (common.cvp_version >= CVP_VERSION_2) { + ret = voice_send_cvp_ch_mixer_info_v2(v); + if (ret < 0) + pr_warn("%s: Set channel mixer config failed err:%d", + __func__, ret); + + ret = voice_send_cvp_mfc_config_v2(v); + if (ret < 0) + pr_warn("%s: Set MFC config failed err:%d", + __func__, ret); + } else { + pr_warn("%s: CVP Version not supported\n", __func__); + ret = -EINVAL; + } + + return ret; +} + +static int voice_get_avcs_version_per_service(uint32_t service_id) +{ + int ret = 0; + size_t ver_size; + struct avcs_fwk_ver_info *ver_info = NULL; + + if (service_id == AVCS_SERVICE_ID_ALL) { + pr_err("%s: Invalid service id: %d", __func__, + AVCS_SERVICE_ID_ALL); + return -EINVAL; + } + + ver_size = sizeof(struct avcs_get_fwk_version) + + sizeof(struct avs_svc_api_info); + ver_info = kzalloc(ver_size, GFP_KERNEL); + if (ver_info == NULL) + return -ENOMEM; + + ret = q6core_get_service_version(service_id, ver_info, ver_size); + if (ret < 0) + goto done; + + ret = ver_info->services[0].api_version; + common.is_avcs_version_queried = true; +done: + kfree(ver_info); + return ret; +} + +static void voice_mic_break_work_fn(struct work_struct *work) +{ + int ret = 0; + char event[25] = ""; + struct voice_data *v = container_of(work, struct voice_data, + voice_mic_break_work); + + snprintf(event, sizeof(event), "MIC_BREAK_STATUS=%s", + v->mic_break_status ? "TRUE" : "FALSE"); + + mutex_lock(&common.common_lock); + ret = q6core_send_uevent(common.uevent_data, event); + if (ret) + pr_err("%s: Send UEvent %s failed :%d\n", __func__, event, ret); + mutex_unlock(&common.common_lock); +} + +static int voice_setup_vocproc(struct voice_data *v) +{ + struct module_instance_info mod_inst_info; + int ret = 0; + + memset(&mod_inst_info, 0, sizeof(mod_inst_info)); + ret = voice_send_cvp_create_cmd(v); + if (ret < 0) { + pr_err("%s: CVP create failed err:%d\n", __func__, ret); + goto fail; + } + + if (common.is_avcs_version_queried == false) + common.cvp_version = voice_get_avcs_version_per_service( + APRV2_IDS_SERVICE_ID_ADSP_CVP_V); + + if (common.cvp_version < 0) { + pr_err("%s: Invalid CVP version %d\n", + __func__, common.cvp_version); + ret = -EINVAL; + goto fail; + } + pr_debug("%s: CVP Version %d\n", __func__, common.cvp_version); + + ret = voice_send_cvp_media_fmt_info_cmd(v); + if (ret < 0) { + pr_err("%s: Set media format info failed err:%d\n", __func__, + ret); + goto fail; + } + + ret = voice_send_cvp_topology_commit_cmd(v); + if (ret < 0) { + pr_err("%s: Set topology commit failed err:%d\n", + __func__, ret); + goto fail; + } + + /* Send MFC config only when the no of channels are more than 1 */ + if (v->dev_rx.no_of_channels > NUM_CHANNELS_MONO) { + ret = voice_send_cvp_mfc_config_cmd(v); + if (ret < 0) { + pr_warn("%s: Set mfc config failed err:%d\n", + __func__, ret); + } + } + + mod_inst_info.module_id = MODULE_ID_VOICE_MODULE_ST; + mod_inst_info.instance_id = INSTANCE_ID_0; + + voice_send_cvs_register_cal_cmd(v); + voice_send_cvp_register_dev_cfg_cmd(v); + voice_send_cvp_register_cal_cmd(v); + voice_send_cvp_register_vol_cal_cmd(v); + + /* enable vocproc */ + ret = voice_send_enable_vocproc_cmd(v); + if (ret < 0) + goto fail; + + /* attach vocproc */ + ret = voice_send_attach_vocproc_cmd(v); + if (ret < 0) + goto fail; + + /* send tty mode if tty device is used */ + voice_send_tty_mode_cmd(v); + + if (is_voip_session(v->session_id)) { + ret = voice_send_mvm_cal_network_cmd(v); + if (ret < 0) + pr_err("%s: voice_send_mvm_cal_network_cmd: %d\n", + __func__, ret); + + ret = voice_send_mvm_media_type_cmd(v); + if (ret < 0) + pr_err("%s: voice_send_mvm_media_type_cmd: %d\n", + __func__, ret); + + voice_send_netid_timing_cmd(v); + } + + if (v->st_enable && !v->tty_mode) + voice_send_set_pp_enable_cmd(v, mod_inst_info, v->st_enable); + /* Start in-call music delivery if this feature is enabled */ + if (v->music_info.play_enable) + voice_cvs_start_playback(v); + + /* Start in-call recording if this feature is enabled */ + if (v->rec_info.rec_enable) + voice_cvs_start_record(v, v->rec_info.rec_mode); + + if (v->dtmf_rx_detect_en) + voice_send_dtmf_rx_detection_cmd(v, v->dtmf_rx_detect_en); + + if (v->hd_enable) + voice_send_hd_cmd(v, v->hd_enable); + + if (common.mic_break_enable) + voice_send_mvm_event_class_cmd(v, + VSS_INOTIFY_CMD_LISTEN_FOR_EVENT_CLASS, + VSS_ICOMMON_EVENT_CLASS_VOICE_ACTIVITY_UPDATE); + + rtac_add_voice(voice_get_cvs_handle(v), + voice_get_cvp_handle(v), + v->dev_rx.port_id, v->dev_tx.port_id, + v->dev_rx.dev_id, v->dev_tx.dev_id, + v->session_id); + + return 0; + +fail: + return ret; +} + +static int voice_send_cvp_device_channels_cmd(struct voice_data *v) +{ + int ret = 0; + struct cvp_set_dev_channels_cmd cvp_set_dev_channels_cmd; + void *apr_cvp; + u16 cvp_handle; + + if (!(voice_get_cvd_int_version(common.cvd_version) >= + CVD_INT_VERSION_2_2)) { + pr_debug("%s CVD ver %s doesn't support send_device_channels cmd\n", + __func__, common.cvd_version); + + goto done; + } + + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + + ret = -EINVAL; + goto done; + } + + apr_cvp = common.apr_q6_cvp; + if (!apr_cvp) { + pr_err("%s: apr_cvp is NULL.\n", __func__); + + ret = -EINVAL; + goto done; + } + + cvp_handle = voice_get_cvp_handle(v); + cvp_set_dev_channels_cmd.hdr.hdr_field = + APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + cvp_set_dev_channels_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(cvp_set_dev_channels_cmd) - APR_HDR_SIZE); + cvp_set_dev_channels_cmd.hdr.src_port = + voice_get_idx_for_session(v->session_id); + cvp_set_dev_channels_cmd.hdr.dest_port = cvp_handle; + cvp_set_dev_channels_cmd.hdr.token = 0; + cvp_set_dev_channels_cmd.hdr.opcode = + VSS_IVOCPROC_CMD_TOPOLOGY_SET_DEV_CHANNELS; + cvp_set_dev_channels_cmd.cvp_set_channels.rx_num_channels = + VSS_NUM_DEV_CHANNELS_1; + cvp_set_dev_channels_cmd.cvp_set_channels.tx_num_channels = + v->dev_tx.no_of_channels; + + v->cvp_state = CMD_STATUS_FAIL; + v->async_err = 0; + ret = apr_send_pkt(apr_cvp, (uint32_t *) &cvp_set_dev_channels_cmd); + if (ret < 0) { + pr_err("%s: Fail in sending VSS_IVOCPROC_CMD_TOPOLOGY_SET_DEV_CHANNELS\n", + __func__); + + ret = -EINVAL; + goto done; + } + + ret = wait_event_timeout(v->cvp_wait, + (v->cvp_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + + ret = -EINVAL; + goto done; + } + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + v->async_err)); + ret = adsp_err_get_lnx_err_code( + v->async_err); + goto done; + } + +done: + return ret; +} + +static int voice_send_cvp_media_fmt_info_cmd(struct voice_data *v) +{ + int ret = 0; + + if (common.cvp_version < CVP_VERSION_2) + ret = voice_send_cvp_device_channels_cmd(v); + else + ret = voice_send_cvp_channel_info_cmd(v); + + if (ret < 0) { + pr_err("%s: Set channel info failed err: %d\n", __func__, + ret); + goto done; + } + + if (voice_get_cvd_int_version(common.cvd_version) >= + CVD_INT_VERSION_2_3) { + ret = voice_send_cvp_media_format_cmd(v, RX_PATH); + if (ret < 0) + goto done; + + ret = voice_send_cvp_media_format_cmd(v, TX_PATH); + if (ret < 0) + goto done; + + if (common.ec_ref_ext) + ret = voice_send_cvp_media_format_cmd(v, EC_REF_PATH); + } + +done: + return ret; +} + +static int voice_send_cvp_media_format_cmd(struct voice_data *v, + uint32_t param_type) +{ + struct vss_param_endpoint_media_format_info media_fmt_info; + struct param_hdr_v3 param_hdr; + int ret = 0; + + memset(&media_fmt_info, 0, sizeof(media_fmt_info)); + memset(¶m_hdr, 0, sizeof(param_hdr)); + + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + ret = -EINVAL; + goto done; + } + + param_hdr.module_id = VSS_MODULE_CVD_GENERIC; + param_hdr.instance_id = INSTANCE_ID_0; + param_hdr.param_size = sizeof(media_fmt_info); + + switch (param_type) { + case RX_PATH: + param_hdr.param_id = VSS_PARAM_RX_PORT_ENDPOINT_MEDIA_INFO; + media_fmt_info.port_id = v->dev_rx.port_id; + media_fmt_info.num_channels = v->dev_rx.no_of_channels; + media_fmt_info.bits_per_sample = v->dev_rx.bits_per_sample; + media_fmt_info.sample_rate = v->dev_rx.sample_rate; + memcpy(&media_fmt_info.channel_mapping, + &v->dev_rx.channel_mapping, VSS_CHANNEL_MAPPING_SIZE); + break; + + case TX_PATH: + param_hdr.param_id = VSS_PARAM_TX_PORT_ENDPOINT_MEDIA_INFO; + media_fmt_info.port_id = v->dev_tx.port_id; + media_fmt_info.num_channels = v->dev_tx.no_of_channels; + media_fmt_info.bits_per_sample = v->dev_tx.bits_per_sample; + media_fmt_info.sample_rate = v->dev_tx.sample_rate; + memcpy(&media_fmt_info.channel_mapping, + &v->dev_tx.channel_mapping, VSS_CHANNEL_MAPPING_SIZE); + break; + + case EC_REF_PATH: + param_hdr.param_id = VSS_PARAM_EC_REF_PORT_ENDPOINT_MEDIA_INFO; + media_fmt_info.port_id = common.ec_media_fmt_info.port_id; + media_fmt_info.num_channels = + common.ec_media_fmt_info.num_channels; + media_fmt_info.bits_per_sample = + common.ec_media_fmt_info.bits_per_sample; + media_fmt_info.sample_rate = + common.ec_media_fmt_info.sample_rate; + memcpy(&media_fmt_info.channel_mapping, + &common.ec_media_fmt_info.channel_mapping, + VSS_CHANNEL_MAPPING_SIZE); + break; + + default: + pr_err("%s: Invalid param type %d\n", __func__, param_type); + ret = -EINVAL; + goto done; + } + + ret = voice_pack_and_set_cvp_param(v, param_hdr, + (u8 *) &media_fmt_info); + if (ret) + pr_err("%s: Failed to set media format params on CVP, err %d\n", + __func__, ret); + +done: + return ret; +} + +static int voice_send_cvp_topology_commit_cmd(struct voice_data *v) +{ + int ret = 0; + struct apr_hdr cvp_topology_commit_cmd; + void *apr_cvp; + u16 cvp_handle; + + if (!(voice_get_cvd_int_version(common.cvd_version) >= + CVD_INT_VERSION_2_2)) { + pr_debug("%s CVD version string %s doesn't support this command\n", + __func__, common.cvd_version); + + goto done; + } + + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + + ret = -EINVAL; + goto done; + } + + apr_cvp = common.apr_q6_cvp; + if (!apr_cvp) { + pr_err("%s: apr_cvp is NULL.\n", __func__); + + ret = -EINVAL; + goto done; + } + + cvp_handle = voice_get_cvp_handle(v); + cvp_topology_commit_cmd.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + cvp_topology_commit_cmd.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(cvp_topology_commit_cmd) - APR_HDR_SIZE); + cvp_topology_commit_cmd.src_port = + voice_get_idx_for_session(v->session_id); + cvp_topology_commit_cmd.dest_port = cvp_handle; + cvp_topology_commit_cmd.token = 0; + cvp_topology_commit_cmd.opcode = VSS_IVOCPROC_CMD_TOPOLOGY_COMMIT; + + v->cvp_state = CMD_STATUS_FAIL; + v->async_err = 0; + ret = apr_send_pkt(apr_cvp, (uint32_t *) &cvp_topology_commit_cmd); + if (ret < 0) { + pr_err("%s: Fail in sending VSS_IVOCPROC_CMD_TOPOLOGY_COMMIT\n", + __func__); + + ret = -EINVAL; + goto done; + } + + ret = wait_event_timeout(v->cvp_wait, + (v->cvp_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + ret = -EINVAL; + goto done; + } + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + v->async_err)); + ret = adsp_err_get_lnx_err_code( + v->async_err); + goto done; + } + +done: + return ret; +} + +static int voice_send_enable_vocproc_cmd(struct voice_data *v) +{ + int ret = 0; + struct apr_hdr cvp_enable_cmd; + void *apr_cvp; + u16 cvp_handle; + + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + return -EINVAL; + } + apr_cvp = common.apr_q6_cvp; + + if (!apr_cvp) { + pr_err("%s: apr_cvp is NULL.\n", __func__); + return -EINVAL; + } + cvp_handle = voice_get_cvp_handle(v); + + /* enable vocproc and wait for respose */ + cvp_enable_cmd.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + cvp_enable_cmd.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(cvp_enable_cmd) - APR_HDR_SIZE); + pr_debug("cvp_enable_cmd pkt size = %d, cvp_handle=%d\n", + cvp_enable_cmd.pkt_size, cvp_handle); + cvp_enable_cmd.src_port = + voice_get_idx_for_session(v->session_id); + cvp_enable_cmd.dest_port = cvp_handle; + cvp_enable_cmd.token = 0; + cvp_enable_cmd.opcode = VSS_IVOCPROC_CMD_ENABLE; + + v->cvp_state = CMD_STATUS_FAIL; + v->async_err = 0; + ret = apr_send_pkt(apr_cvp, (uint32_t *) &cvp_enable_cmd); + if (ret < 0) { + pr_err("Fail in sending VSS_IVOCPROC_CMD_ENABLE\n"); + goto fail; + } + ret = wait_event_timeout(v->cvp_wait, + (v->cvp_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + goto fail; + } + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + v->async_err)); + ret = adsp_err_get_lnx_err_code( + v->async_err); + goto fail; + } + + return 0; +fail: + return ret; +} + +static int voice_send_mvm_cal_network_cmd(struct voice_data *v) +{ + struct vss_imvm_cmd_set_cal_network_t mvm_set_cal_network; + int ret = 0; + void *apr_mvm; + u16 mvm_handle; + + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + return -EINVAL; + } + apr_mvm = common.apr_q6_mvm; + + if (!apr_mvm) { + pr_err("%s: apr_mvm is NULL.\n", __func__); + return -EINVAL; + } + mvm_handle = voice_get_mvm_handle(v); + + mvm_set_cal_network.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + mvm_set_cal_network.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(mvm_set_cal_network) - APR_HDR_SIZE); + mvm_set_cal_network.hdr.src_port = + voice_get_idx_for_session(v->session_id); + mvm_set_cal_network.hdr.dest_port = mvm_handle; + mvm_set_cal_network.hdr.token = 0; + mvm_set_cal_network.hdr.opcode = VSS_IMVM_CMD_SET_CAL_NETWORK; + mvm_set_cal_network.network_id = VSS_ICOMMON_CAL_NETWORK_ID_NONE; + + v->mvm_state = CMD_STATUS_FAIL; + v->async_err = 0; + ret = apr_send_pkt(apr_mvm, (uint32_t *) &mvm_set_cal_network); + if (ret < 0) { + pr_err("%s: Error %d sending SET_NETWORK\n", __func__, ret); + goto fail; + } + + ret = wait_event_timeout(v->mvm_wait, + (v->mvm_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout %d\n", __func__, ret); + goto fail; + } + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + v->async_err)); + ret = adsp_err_get_lnx_err_code( + v->async_err); + goto fail; + } + return 0; +fail: + return ret; +} + +static int voice_send_netid_timing_cmd(struct voice_data *v) +{ + int ret = 0; + void *apr_mvm; + u16 mvm_handle; + struct mvm_set_network_cmd mvm_set_network; + struct mvm_set_voice_timing_cmd mvm_set_voice_timing; + + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + return -EINVAL; + } + apr_mvm = common.apr_q6_mvm; + + if (!apr_mvm) { + pr_err("%s: apr_mvm is NULL.\n", __func__); + return -EINVAL; + } + mvm_handle = voice_get_mvm_handle(v); + + ret = voice_config_cvs_vocoder(v); + if (ret < 0) { + pr_err("%s: Error %d configuring CVS voc", + __func__, ret); + goto fail; + } + /* Set network ID. */ + pr_debug("Setting network ID %x\n", common.mvs_info.network_type); + + mvm_set_network.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + mvm_set_network.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(mvm_set_network) - APR_HDR_SIZE); + mvm_set_network.hdr.src_port = + voice_get_idx_for_session(v->session_id); + mvm_set_network.hdr.dest_port = mvm_handle; + mvm_set_network.hdr.token = 0; + mvm_set_network.hdr.opcode = VSS_IMVM_CMD_SET_CAL_NETWORK; + mvm_set_network.network.network_id = common.mvs_info.network_type; + + v->mvm_state = CMD_STATUS_FAIL; + v->async_err = 0; + ret = apr_send_pkt(apr_mvm, (uint32_t *) &mvm_set_network); + if (ret < 0) { + pr_err("%s: Error %d sending SET_NETWORK\n", __func__, ret); + goto fail; + } + + ret = wait_event_timeout(v->mvm_wait, + (v->mvm_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + goto fail; + } + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + v->async_err)); + ret = adsp_err_get_lnx_err_code( + v->async_err); + goto fail; + } + + /* Set voice timing. */ + pr_debug("Setting voice timing\n"); + + mvm_set_voice_timing.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + mvm_set_voice_timing.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(mvm_set_voice_timing) - + APR_HDR_SIZE); + mvm_set_voice_timing.hdr.src_port = + voice_get_idx_for_session(v->session_id); + mvm_set_voice_timing.hdr.dest_port = mvm_handle; + mvm_set_voice_timing.hdr.token = 0; + mvm_set_voice_timing.hdr.opcode = VSS_ICOMMON_CMD_SET_VOICE_TIMING; + mvm_set_voice_timing.timing.mode = 0; + mvm_set_voice_timing.timing.enc_offset = 8000; + mvm_set_voice_timing.timing.dec_req_offset = 3300; + mvm_set_voice_timing.timing.dec_offset = 8300; + + v->mvm_state = CMD_STATUS_FAIL; + v->async_err = 0; + + ret = apr_send_pkt(apr_mvm, (uint32_t *) &mvm_set_voice_timing); + if (ret < 0) { + pr_err("%s: Error %d sending SET_TIMING\n", __func__, ret); + goto fail; + } + + ret = wait_event_timeout(v->mvm_wait, + (v->mvm_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + goto fail; + } + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + v->async_err)); + ret = adsp_err_get_lnx_err_code( + v->async_err); + goto fail; + } + + return 0; +fail: + return ret; +} + +static int voice_send_attach_vocproc_cmd(struct voice_data *v) +{ + int ret = 0; + struct mvm_attach_vocproc_cmd mvm_a_vocproc_cmd; + void *apr_mvm; + u16 mvm_handle, cvp_handle; + + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + return -EINVAL; + } + apr_mvm = common.apr_q6_mvm; + + if (!apr_mvm) { + pr_err("%s: apr_mvm is NULL.\n", __func__); + return -EINVAL; + } + mvm_handle = voice_get_mvm_handle(v); + cvp_handle = voice_get_cvp_handle(v); + + /* attach vocproc and wait for response */ + mvm_a_vocproc_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + mvm_a_vocproc_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(mvm_a_vocproc_cmd) - APR_HDR_SIZE); + pr_debug("send mvm_a_vocproc_cmd pkt size = %d\n", + mvm_a_vocproc_cmd.hdr.pkt_size); + mvm_a_vocproc_cmd.hdr.src_port = + voice_get_idx_for_session(v->session_id); + mvm_a_vocproc_cmd.hdr.dest_port = mvm_handle; + mvm_a_vocproc_cmd.hdr.token = 0; + mvm_a_vocproc_cmd.hdr.opcode = VSS_IMVM_CMD_ATTACH_VOCPROC; + mvm_a_vocproc_cmd.mvm_attach_cvp_handle.handle = cvp_handle; + + v->mvm_state = CMD_STATUS_FAIL; + v->async_err = 0; + ret = apr_send_pkt(apr_mvm, (uint32_t *) &mvm_a_vocproc_cmd); + if (ret < 0) { + pr_err("Fail in sending VSS_IMVM_CMD_ATTACH_VOCPROC\n"); + goto fail; + } + ret = wait_event_timeout(v->mvm_wait, + (v->mvm_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + goto fail; + } + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + v->async_err)); + ret = adsp_err_get_lnx_err_code( + v->async_err); + goto fail; + } + + return 0; +fail: + return ret; +} + +static void voc_update_session_params(struct voice_data *v) +{ + /* reset LCH mode */ + v->lch_mode = 0; + + /* clear disable topology setting */ + v->disable_topology = false; + + /* clear mute setting */ + v->dev_rx.dev_mute = common.default_mute_val; + v->dev_tx.dev_mute = common.default_mute_val; + v->stream_rx.stream_mute = common.default_mute_val; + v->stream_tx.stream_mute = common.default_mute_val; +} + +static int voice_destroy_vocproc(struct voice_data *v) +{ + struct mvm_detach_vocproc_cmd mvm_d_vocproc_cmd; + struct apr_hdr cvp_destroy_session_cmd; + struct module_instance_info mod_inst_info; + int ret = 0; + void *apr_mvm, *apr_cvp; + u16 mvm_handle, cvp_handle; + + memset(&mod_inst_info, 0, sizeof(mod_inst_info)); + + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + return -EINVAL; + } + apr_mvm = common.apr_q6_mvm; + apr_cvp = common.apr_q6_cvp; + + if (!apr_mvm || !apr_cvp) { + pr_err("%s: apr_mvm or apr_cvp is NULL.\n", __func__); + return -EINVAL; + } + mvm_handle = voice_get_mvm_handle(v); + cvp_handle = voice_get_cvp_handle(v); + + mod_inst_info.module_id = MODULE_ID_VOICE_MODULE_ST; + mod_inst_info.instance_id = INSTANCE_ID_0; + + /* disable slowtalk if st_enable is set */ + if (v->st_enable) + voice_send_set_pp_enable_cmd(v, mod_inst_info, 0); + + /* Disable HD Voice if hd_enable is set */ + if (v->hd_enable) + voice_send_hd_cmd(v, 0); + + /* stop playback or recording */ + v->music_info.force = 1; + voice_cvs_stop_playback(v); + voice_cvs_stop_record(v); + /* If voice call is active during VoLTE, SRVCC happens. + * Start recording on voice session if recording started during VoLTE. + */ + if (is_volte_session(v->session_id) && + ((common.voice[VOC_PATH_PASSIVE].voc_state == VOC_RUN) || + (common.voice[VOC_PATH_PASSIVE].voc_state == VOC_CHANGE))) { + if (v->rec_info.rec_enable) { + voice_cvs_start_record( + &common.voice[VOC_PATH_PASSIVE], + v->rec_info.rec_mode); + common.srvcc_rec_flag = true; + + pr_debug("%s: switch recording, srvcc_rec_flag %d\n", + __func__, common.srvcc_rec_flag); + } + } + + /* send stop voice cmd */ + voice_send_stop_voice_cmd(v); + + /* send stop dtmf detecton cmd */ + if (v->dtmf_rx_detect_en) + voice_send_dtmf_rx_detection_cmd(v, 0); + + /* detach VOCPROC and wait for response from mvm */ + mvm_d_vocproc_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + mvm_d_vocproc_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(mvm_d_vocproc_cmd) - APR_HDR_SIZE); + pr_debug("mvm_d_vocproc_cmd pkt size = %d\n", + mvm_d_vocproc_cmd.hdr.pkt_size); + mvm_d_vocproc_cmd.hdr.src_port = + voice_get_idx_for_session(v->session_id); + mvm_d_vocproc_cmd.hdr.dest_port = mvm_handle; + mvm_d_vocproc_cmd.hdr.token = 0; + mvm_d_vocproc_cmd.hdr.opcode = VSS_IMVM_CMD_DETACH_VOCPROC; + mvm_d_vocproc_cmd.mvm_detach_cvp_handle.handle = cvp_handle; + + v->mvm_state = CMD_STATUS_FAIL; + v->async_err = 0; + ret = apr_send_pkt(apr_mvm, (uint32_t *) &mvm_d_vocproc_cmd); + if (ret < 0) { + pr_err("Fail in sending VSS_IMVM_CMD_DETACH_VOCPROC\n"); + goto fail; + } + ret = wait_event_timeout(v->mvm_wait, + (v->mvm_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + goto fail; + } + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + v->async_err)); + ret = adsp_err_get_lnx_err_code( + v->async_err); + goto fail; + } + + voice_send_cvp_deregister_vol_cal_cmd(v); + voice_send_cvp_deregister_cal_cmd(v); + voice_send_cvp_deregister_dev_cfg_cmd(v); + voice_send_cvs_deregister_cal_cmd(v); + + /* Unload topology modules */ + voice_unload_topo_modules(); + + if (common.mic_break_enable) + voice_send_mvm_event_class_cmd(v, + VSS_INOTIFY_CMD_CANCEL_EVENT_CLASS, + VSS_ICOMMON_EVENT_CLASS_VOICE_ACTIVITY_UPDATE); + + /* destrop cvp session */ + cvp_destroy_session_cmd.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + cvp_destroy_session_cmd.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(cvp_destroy_session_cmd) - APR_HDR_SIZE); + pr_debug("cvp_destroy_session_cmd pkt size = %d\n", + cvp_destroy_session_cmd.pkt_size); + cvp_destroy_session_cmd.src_port = + voice_get_idx_for_session(v->session_id); + cvp_destroy_session_cmd.dest_port = cvp_handle; + cvp_destroy_session_cmd.token = 0; + cvp_destroy_session_cmd.opcode = APRV2_IBASIC_CMD_DESTROY_SESSION; + + v->cvp_state = CMD_STATUS_FAIL; + v->async_err = 0; + ret = apr_send_pkt(apr_cvp, (uint32_t *) &cvp_destroy_session_cmd); + if (ret < 0) { + pr_err("Fail in sending APRV2_IBASIC_CMD_DESTROY_SESSION\n"); + goto fail; + } + ret = wait_event_timeout(v->cvp_wait, + (v->cvp_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + goto fail; + } + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + v->async_err)); + ret = adsp_err_get_lnx_err_code( + v->async_err); + goto fail; + } + + rtac_remove_voice(voice_get_cvs_handle(v)); + cvp_handle = 0; + voice_set_cvp_handle(v, cvp_handle); + return 0; +fail: + return ret; +} + +static int voice_send_mvm_unmap_memory_physical_cmd(struct voice_data *v, + uint32_t mem_handle) +{ + struct vss_imemory_cmd_unmap_t mem_unmap; + int ret = 0; + void *apr_mvm; + u16 mvm_handle; + + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + return -EINVAL; + } + apr_mvm = common.apr_q6_mvm; + + if (!apr_mvm) { + pr_err("%s: apr_mvm is NULL.\n", __func__); + return -EINVAL; + } + mvm_handle = voice_get_mvm_handle(v); + + mem_unmap.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + mem_unmap.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(mem_unmap) - APR_HDR_SIZE); + mem_unmap.hdr.src_port = + voice_get_idx_for_session(v->session_id); + mem_unmap.hdr.dest_port = mvm_handle; + mem_unmap.hdr.token = 0; + mem_unmap.hdr.opcode = VSS_IMEMORY_CMD_UNMAP; + mem_unmap.mem_handle = mem_handle; + + pr_debug("%s: mem_handle: 0x%x\n", __func__, mem_unmap.mem_handle); + + v->mvm_state = CMD_STATUS_FAIL; + v->async_err = 0; + ret = apr_send_pkt(apr_mvm, (uint32_t *) &mem_unmap); + if (ret < 0) { + pr_err("mem_unmap op[0x%x]ret[%d]\n", + mem_unmap.hdr.opcode, ret); + goto fail; + } + + ret = wait_event_timeout(v->mvm_wait, + (v->mvm_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout %d\n", __func__, ret); + goto fail; + } + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + v->async_err)); + ret = adsp_err_get_lnx_err_code( + v->async_err); + goto fail; + } + return 0; + +fail: + return ret; +} + +static int voice_send_cvs_packet_exchange_config_cmd(struct voice_data *v) +{ + struct vss_istream_cmd_set_oob_packet_exchange_config_t + packet_exchange_config_pkt; + int ret = 0; + void *apr_cvs; + u16 cvs_handle; + + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + return -EINVAL; + } + + apr_cvs = common.apr_q6_cvs; + + if (!apr_cvs) { + pr_err("%s: apr_cvs is NULL.\n", __func__); + return -EINVAL; + } + cvs_handle = voice_get_cvs_handle(v); + + packet_exchange_config_pkt.hdr.hdr_field = APR_HDR_FIELD( + APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + packet_exchange_config_pkt.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(packet_exchange_config_pkt) - + APR_HDR_SIZE); + packet_exchange_config_pkt.hdr.src_port = + voice_get_idx_for_session(v->session_id); + packet_exchange_config_pkt.hdr.dest_port = cvs_handle; + packet_exchange_config_pkt.hdr.token = 0; + packet_exchange_config_pkt.hdr.opcode = + VSS_ISTREAM_CMD_SET_OOB_PACKET_EXCHANGE_CONFIG; + packet_exchange_config_pkt.mem_handle = v->shmem_info.mem_handle; + /* dec buffer address */ + packet_exchange_config_pkt.dec_buf_addr_lsw = + lower_32_bits(v->shmem_info.sh_buf.buf[0].phys); + packet_exchange_config_pkt.dec_buf_addr_msw = + msm_audio_populate_upper_32_bits( + v->shmem_info.sh_buf.buf[0].phys); + packet_exchange_config_pkt.dec_buf_size = 4096; + /* enc buffer address */ + packet_exchange_config_pkt.enc_buf_addr_lsw = + lower_32_bits(v->shmem_info.sh_buf.buf[1].phys); + packet_exchange_config_pkt.enc_buf_addr_msw = + msm_audio_populate_upper_32_bits( + v->shmem_info.sh_buf.buf[1].phys); + packet_exchange_config_pkt.enc_buf_size = 4096; + + pr_debug("%s: dec buf add: lsw %0x msw %0x, size %d, enc buf add: lsw %0x msw %0x, size %d\n", + __func__, + packet_exchange_config_pkt.dec_buf_addr_lsw, + packet_exchange_config_pkt.dec_buf_addr_msw, + packet_exchange_config_pkt.dec_buf_size, + packet_exchange_config_pkt.enc_buf_addr_lsw, + packet_exchange_config_pkt.enc_buf_addr_msw, + packet_exchange_config_pkt.enc_buf_size); + + v->cvs_state = CMD_STATUS_FAIL; + v->async_err = 0; + ret = apr_send_pkt(apr_cvs, (uint32_t *) &packet_exchange_config_pkt); + if (ret < 0) { + pr_err("Failed to send packet exchange config cmd %d\n", ret); + goto fail; + } + + ret = wait_event_timeout(v->cvs_wait, + (v->cvs_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) + pr_err("%s: wait_event timeout %d\n", __func__, ret); + + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + v->async_err)); + ret = adsp_err_get_lnx_err_code( + v->async_err); + goto fail; + } + + return 0; +fail: + return ret; +} + +static int voice_send_cvs_data_exchange_mode_cmd(struct voice_data *v) +{ + struct vss_istream_cmd_set_packet_exchange_mode_t data_exchange_pkt; + int ret = 0; + void *apr_cvs; + u16 cvs_handle; + + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + return -EINVAL; + } + apr_cvs = common.apr_q6_cvs; + + if (!apr_cvs) { + pr_err("%s: apr_cvs is NULL.\n", __func__); + return -EINVAL; + } + cvs_handle = voice_get_cvs_handle(v); + + data_exchange_pkt.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + data_exchange_pkt.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(data_exchange_pkt) - APR_HDR_SIZE); + data_exchange_pkt.hdr.src_port = + voice_get_idx_for_session(v->session_id); + data_exchange_pkt.hdr.dest_port = cvs_handle; + data_exchange_pkt.hdr.token = 0; + data_exchange_pkt.hdr.opcode = VSS_ISTREAM_CMD_SET_PACKET_EXCHANGE_MODE; + data_exchange_pkt.mode = VSS_ISTREAM_PACKET_EXCHANGE_MODE_OUT_OF_BAND; + + v->cvs_state = CMD_STATUS_FAIL; + v->async_err = 0; + ret = apr_send_pkt(apr_cvs, (uint32_t *) &data_exchange_pkt); + if (ret < 0) { + pr_err("Failed to send data exchange mode %d\n", ret); + goto fail; + } + + ret = wait_event_timeout(v->cvs_wait, + (v->cvs_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) + pr_err("%s: wait_event timeout %d\n", __func__, ret); + + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + v->async_err)); + ret = adsp_err_get_lnx_err_code( + v->async_err); + goto fail; + } + return 0; +fail: + return ret; +} + +static int voice_send_stream_mute_cmd(struct voice_data *v, uint16_t direction, + uint16_t mute_flag, uint32_t ramp_duration) +{ + struct cvs_set_mute_cmd cvs_mute_cmd; + int ret = 0; + + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + ret = -EINVAL; + goto fail; + } + + if (!common.apr_q6_cvs) { + pr_err("%s: apr_cvs is NULL.\n", __func__); + ret = -EINVAL; + goto fail; + } + + /* send mute/unmute to cvs */ + cvs_mute_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + cvs_mute_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(cvs_mute_cmd) - APR_HDR_SIZE); + cvs_mute_cmd.hdr.src_port = + voice_get_idx_for_session(v->session_id); + cvs_mute_cmd.hdr.dest_port = voice_get_cvs_handle(v); + cvs_mute_cmd.hdr.token = 0; + cvs_mute_cmd.hdr.opcode = VSS_IVOLUME_CMD_MUTE_V2; + cvs_mute_cmd.cvs_set_mute.direction = direction; + cvs_mute_cmd.cvs_set_mute.mute_flag = mute_flag; + cvs_mute_cmd.cvs_set_mute.ramp_duration_ms = ramp_duration; + + v->cvs_state = CMD_STATUS_FAIL; + v->async_err = 0; + ret = apr_send_pkt(common.apr_q6_cvs, (uint32_t *) &cvs_mute_cmd); + if (ret < 0) { + pr_err("%s: Error %d sending stream mute\n", __func__, ret); + + goto fail; + } + ret = wait_event_timeout(v->cvs_wait, + (v->cvs_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: Command timeout\n", __func__); + goto fail; + } + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + v->async_err)); + ret = adsp_err_get_lnx_err_code( + v->async_err); + goto fail; + } + + return 0; + +fail: + return ret; +} + +static int voice_send_device_mute_cmd(struct voice_data *v, uint16_t direction, + uint16_t mute_flag, uint32_t ramp_duration) +{ + struct cvp_set_mute_cmd cvp_mute_cmd; + int ret = 0; + + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + ret = -EINVAL; + goto fail; + } + + if (!common.apr_q6_cvp) { + pr_err("%s: apr_cvp is NULL.\n", __func__); + ret = -EINVAL; + goto fail; + } + + cvp_mute_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + cvp_mute_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(cvp_mute_cmd) - APR_HDR_SIZE); + cvp_mute_cmd.hdr.src_port = + voice_get_idx_for_session(v->session_id); + cvp_mute_cmd.hdr.dest_port = voice_get_cvp_handle(v); + cvp_mute_cmd.hdr.token = 0; + cvp_mute_cmd.hdr.opcode = VSS_IVOLUME_CMD_MUTE_V2; + cvp_mute_cmd.cvp_set_mute.direction = direction; + cvp_mute_cmd.cvp_set_mute.mute_flag = mute_flag; + cvp_mute_cmd.cvp_set_mute.ramp_duration_ms = ramp_duration; + + v->cvp_state = CMD_STATUS_FAIL; + v->async_err = 0; + ret = apr_send_pkt(common.apr_q6_cvp, (uint32_t *) &cvp_mute_cmd); + if (ret < 0) { + pr_err("%s: Error %d sending rx device cmd\n", __func__, ret); + + goto fail; + } + ret = wait_event_timeout(v->cvp_wait, + (v->cvp_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: Command timeout\n", __func__); + goto fail; + } + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + v->async_err)); + ret = adsp_err_get_lnx_err_code( + v->async_err); + goto fail; + } + + return 0; + +fail: + return ret; +} + +static int voice_send_vol_step_cmd(struct voice_data *v) +{ + struct cvp_set_rx_volume_step_cmd cvp_vol_step_cmd; + int ret = 0; + void *apr_cvp; + u16 cvp_handle; + + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + return -EINVAL; + } + apr_cvp = common.apr_q6_cvp; + + if (!apr_cvp) { + pr_err("%s: apr_cvp is NULL.\n", __func__); + return -EINVAL; + } + cvp_handle = voice_get_cvp_handle(v); + + /* send volume index to cvp */ + cvp_vol_step_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + cvp_vol_step_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(cvp_vol_step_cmd) - APR_HDR_SIZE); + cvp_vol_step_cmd.hdr.src_port = + voice_get_idx_for_session(v->session_id); + cvp_vol_step_cmd.hdr.dest_port = cvp_handle; + cvp_vol_step_cmd.hdr.token = 0; + cvp_vol_step_cmd.hdr.opcode = VSS_IVOLUME_CMD_SET_STEP; + cvp_vol_step_cmd.cvp_set_vol_step.direction = VSS_IVOLUME_DIRECTION_RX; + cvp_vol_step_cmd.cvp_set_vol_step.value = v->dev_rx.volume_step_value; + cvp_vol_step_cmd.cvp_set_vol_step.ramp_duration_ms = + v->dev_rx.volume_ramp_duration_ms; + pr_debug("%s step_value:%d, ramp_duration_ms:%d", + __func__, + cvp_vol_step_cmd.cvp_set_vol_step.value, + cvp_vol_step_cmd.cvp_set_vol_step.ramp_duration_ms); + + v->cvp_state = CMD_STATUS_FAIL; + v->async_err = 0; + ret = apr_send_pkt(apr_cvp, (uint32_t *) &cvp_vol_step_cmd); + if (ret < 0) { + pr_err("Fail in sending RX VOL step\n"); + return -EINVAL; + } + ret = wait_event_timeout(v->cvp_wait, + (v->cvp_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + return -EINVAL; + } + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + v->async_err)); + ret = adsp_err_get_lnx_err_code( + v->async_err); + return ret; + } + return 0; +} + +static int voice_cvs_start_record(struct voice_data *v, uint32_t rec_mode) +{ + int ret = 0; + void *apr_cvs; + u16 cvs_handle; + + struct cvs_start_record_cmd cvs_start_record; + + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + return -EINVAL; + } + apr_cvs = common.apr_q6_cvs; + + if (!apr_cvs) { + pr_err("%s: apr_cvs is NULL.\n", __func__); + return -EINVAL; + } + + cvs_handle = voice_get_cvs_handle(v); + + if (!v->rec_info.recording) { + cvs_start_record.hdr.hdr_field = APR_HDR_FIELD( + APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + cvs_start_record.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(cvs_start_record) - APR_HDR_SIZE); + cvs_start_record.hdr.src_port = + voice_get_idx_for_session(v->session_id); + cvs_start_record.hdr.dest_port = cvs_handle; + cvs_start_record.hdr.token = 0; + cvs_start_record.hdr.opcode = VSS_IRECORD_CMD_START; + + cvs_start_record.rec_mode.port_id = + VSS_IRECORD_PORT_ID_DEFAULT; + if (rec_mode == VOC_REC_UPLINK) { + cvs_start_record.rec_mode.rx_tap_point = + VSS_IRECORD_TAP_POINT_NONE; + cvs_start_record.rec_mode.tx_tap_point = + VSS_IRECORD_TAP_POINT_STREAM_END; + } else if (rec_mode == VOC_REC_DOWNLINK) { + cvs_start_record.rec_mode.rx_tap_point = + VSS_IRECORD_TAP_POINT_STREAM_END; + cvs_start_record.rec_mode.tx_tap_point = + VSS_IRECORD_TAP_POINT_NONE; + } else if (rec_mode == VOC_REC_BOTH) { + cvs_start_record.rec_mode.rx_tap_point = + VSS_IRECORD_TAP_POINT_STREAM_END; + cvs_start_record.rec_mode.tx_tap_point = + VSS_IRECORD_TAP_POINT_STREAM_END; + } else { + pr_err("%s: Invalid in-call rec_mode %d\n", __func__, + rec_mode); + + ret = -EINVAL; + goto fail; + } + + v->cvs_state = CMD_STATUS_FAIL; + v->async_err = 0; + + ret = apr_send_pkt(apr_cvs, (uint32_t *) &cvs_start_record); + if (ret < 0) { + pr_err("%s: Error %d sending START_RECORD\n", __func__, + ret); + + goto fail; + } + + ret = wait_event_timeout(v->cvs_wait, + (v->cvs_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + + goto fail; + } + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + v->async_err)); + ret = adsp_err_get_lnx_err_code( + v->async_err); + goto fail; + } + v->rec_info.recording = 1; + } else { + pr_debug("%s: Start record already sent\n", __func__); + } + + return 0; + +fail: + return ret; +} + +static int voice_cvs_stop_record(struct voice_data *v) +{ + int ret = 0; + void *apr_cvs; + u16 cvs_handle; + struct apr_hdr cvs_stop_record; + + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + return -EINVAL; + } + apr_cvs = common.apr_q6_cvs; + + if (!apr_cvs) { + pr_err("%s: apr_cvs is NULL.\n", __func__); + return -EINVAL; + } + + cvs_handle = voice_get_cvs_handle(v); + + if (v->rec_info.recording) { + cvs_stop_record.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + cvs_stop_record.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(cvs_stop_record) - APR_HDR_SIZE); + cvs_stop_record.src_port = + voice_get_idx_for_session(v->session_id); + cvs_stop_record.dest_port = cvs_handle; + cvs_stop_record.token = 0; + cvs_stop_record.opcode = VSS_IRECORD_CMD_STOP; + + v->cvs_state = CMD_STATUS_FAIL; + v->async_err = 0; + + ret = apr_send_pkt(apr_cvs, (uint32_t *) &cvs_stop_record); + if (ret < 0) { + pr_err("%s: Error %d sending STOP_RECORD\n", + __func__, ret); + + goto fail; + } + + ret = wait_event_timeout(v->cvs_wait, + (v->cvs_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + + goto fail; + } + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + v->async_err)); + ret = adsp_err_get_lnx_err_code( + v->async_err); + goto fail; + } + v->rec_info.recording = 0; + } else { + pr_debug("%s: Stop record already sent\n", __func__); + } + + return 0; + +fail: + return ret; +} + +/** + * voc_start_record - + * command to set record for voice session + * + * @port_id: Pseudo Port ID for record data + * @set: Enable or Disable for record start/stop + * @session_id: voice session ID to send this command + * + * Returns 0 on success or error on failure + */ +int voc_start_record(uint32_t port_id, uint32_t set, uint32_t session_id) +{ + int ret = 0; + int rec_mode = 0; + u16 cvs_handle; + int rec_set = 0; + struct voice_session_itr itr; + struct voice_data *v = NULL; + + /* check if session_id is valid */ + if (!voice_is_valid_session_id(session_id)) { + pr_err("%s: Invalid session id:%u\n", __func__, + session_id); + + return -EINVAL; + } + + voice_itr_init(&itr, session_id); + pr_debug("%s: session_id:%u\n", __func__, session_id); + + while (voice_itr_get_next_session(&itr, &v)) { + if (v == NULL) { + pr_err("%s: v is NULL, sessionid:%u\n", __func__, + session_id); + + break; + } + pr_debug("%s: port_id: %d, set: %d, v: %pK\n", + __func__, port_id, set, v); + + mutex_lock(&v->lock); + rec_mode = v->rec_info.rec_mode; + rec_set = set; + if (set) { + if ((v->rec_route_state.ul_flag != 0) && + (v->rec_route_state.dl_flag != 0)) { + pr_debug("%s: rec mode already set.\n", + __func__); + + mutex_unlock(&v->lock); + continue; + } + + if (port_id == VOICE_RECORD_TX) { + if ((v->rec_route_state.ul_flag == 0) + && (v->rec_route_state.dl_flag == 0)) { + rec_mode = VOC_REC_UPLINK; + v->rec_route_state.ul_flag = 1; + } else if ((v->rec_route_state.ul_flag == 0) + && (v->rec_route_state.dl_flag != 0)) { + voice_cvs_stop_record(v); + rec_mode = VOC_REC_BOTH; + v->rec_route_state.ul_flag = 1; + } + } else if (port_id == VOICE_RECORD_RX) { + if ((v->rec_route_state.ul_flag == 0) + && (v->rec_route_state.dl_flag == 0)) { + rec_mode = VOC_REC_DOWNLINK; + v->rec_route_state.dl_flag = 1; + } else if ((v->rec_route_state.ul_flag != 0) + && (v->rec_route_state.dl_flag == 0)) { + voice_cvs_stop_record(v); + rec_mode = VOC_REC_BOTH; + v->rec_route_state.dl_flag = 1; + } + } + rec_set = 1; + } else { + if ((v->rec_route_state.ul_flag == 0) && + (v->rec_route_state.dl_flag == 0)) { + pr_debug("%s: rec already stops.\n", + __func__); + mutex_unlock(&v->lock); + continue; + } + + if (port_id == VOICE_RECORD_TX) { + if ((v->rec_route_state.ul_flag != 0) + && (v->rec_route_state.dl_flag == 0)) { + v->rec_route_state.ul_flag = 0; + rec_set = 0; + } else if ((v->rec_route_state.ul_flag != 0) + && (v->rec_route_state.dl_flag != 0)) { + voice_cvs_stop_record(v); + v->rec_route_state.ul_flag = 0; + rec_mode = VOC_REC_DOWNLINK; + rec_set = 1; + } + } else if (port_id == VOICE_RECORD_RX) { + if ((v->rec_route_state.ul_flag == 0) + && (v->rec_route_state.dl_flag != 0)) { + v->rec_route_state.dl_flag = 0; + rec_set = 0; + } else if ((v->rec_route_state.ul_flag != 0) + && (v->rec_route_state.dl_flag != 0)) { + voice_cvs_stop_record(v); + v->rec_route_state.dl_flag = 0; + rec_mode = VOC_REC_UPLINK; + rec_set = 1; + } + } + } + pr_debug("%s: mode =%d, set =%d\n", __func__, + rec_mode, rec_set); + cvs_handle = voice_get_cvs_handle(v); + + if (cvs_handle != 0) { + if (rec_set) + ret = voice_cvs_start_record(v, rec_mode); + else + ret = voice_cvs_stop_record(v); + } + + /* During SRVCC, recording will switch from VoLTE session to + * voice session. + * Then stop recording, need to stop recording on voice session. + */ + if ((!rec_set) && common.srvcc_rec_flag) { + pr_debug("%s, srvcc_rec_flag:%d\n", __func__, + common.srvcc_rec_flag); + + voice_cvs_stop_record(&common.voice[VOC_PATH_PASSIVE]); + common.srvcc_rec_flag = false; + } + + /* Cache the value */ + v->rec_info.rec_enable = rec_set; + v->rec_info.rec_mode = rec_mode; + + mutex_unlock(&v->lock); + } + + return ret; +} +EXPORT_SYMBOL(voc_start_record); + +static int voice_cvs_start_playback(struct voice_data *v) +{ + int ret = 0; + struct cvs_start_playback_cmd cvs_start_playback; + void *apr_cvs; + u16 cvs_handle; + + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + return -EINVAL; + } + apr_cvs = common.apr_q6_cvs; + + if (!apr_cvs) { + pr_err("%s: apr_cvs is NULL.\n", __func__); + return -EINVAL; + } + + cvs_handle = voice_get_cvs_handle(v); + + if (!v->music_info.playing && v->music_info.count) { + cvs_start_playback.hdr.hdr_field = APR_HDR_FIELD( + APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + cvs_start_playback.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(cvs_start_playback) - APR_HDR_SIZE); + cvs_start_playback.hdr.src_port = + voice_get_idx_for_session(v->session_id); + cvs_start_playback.hdr.dest_port = cvs_handle; + cvs_start_playback.hdr.token = 0; + cvs_start_playback.hdr.opcode = VSS_IPLAYBACK_CMD_START; + cvs_start_playback.playback_mode.port_id = + v->music_info.port_id; + + v->cvs_state = CMD_STATUS_FAIL; + v->async_err = 0; + + ret = apr_send_pkt(apr_cvs, (uint32_t *) &cvs_start_playback); + + if (ret < 0) { + pr_err("%s: Error %d sending START_PLAYBACK\n", + __func__, ret); + + goto fail; + } + + ret = wait_event_timeout(v->cvs_wait, + (v->cvs_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + + goto fail; + } + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + v->async_err)); + ret = adsp_err_get_lnx_err_code( + v->async_err); + goto fail; + } + + v->music_info.playing = 1; + } else { + pr_debug("%s: Start playback already sent\n", __func__); + } + + return 0; + +fail: + return ret; +} + +static int voice_cvs_stop_playback(struct voice_data *v) +{ + int ret = 0; + struct apr_hdr cvs_stop_playback; + void *apr_cvs; + u16 cvs_handle; + + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + return -EINVAL; + } + apr_cvs = common.apr_q6_cvs; + + if (!apr_cvs) { + pr_err("%s: apr_cvs is NULL.\n", __func__); + return -EINVAL; + } + + cvs_handle = voice_get_cvs_handle(v); + + if (v->music_info.playing && ((!v->music_info.count) || + (v->music_info.force))) { + cvs_stop_playback.hdr_field = + APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + cvs_stop_playback.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(cvs_stop_playback) - APR_HDR_SIZE); + cvs_stop_playback.src_port = + voice_get_idx_for_session(v->session_id); + cvs_stop_playback.dest_port = cvs_handle; + cvs_stop_playback.token = 0; + + cvs_stop_playback.opcode = VSS_IPLAYBACK_CMD_STOP; + + v->cvs_state = CMD_STATUS_FAIL; + v->async_err = 0; + + ret = apr_send_pkt(apr_cvs, (uint32_t *) &cvs_stop_playback); + if (ret < 0) { + pr_err("%s: Error %d sending STOP_PLAYBACK\n", + __func__, ret); + + + goto fail; + } + + ret = wait_event_timeout(v->cvs_wait, + (v->cvs_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + + goto fail; + } + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + v->async_err)); + ret = adsp_err_get_lnx_err_code( + v->async_err); + goto fail; + } + + v->music_info.playing = 0; + v->music_info.force = 0; + } else { + pr_debug("%s: Stop playback already sent\n", __func__); + } + + return 0; + +fail: + return ret; +} + +static int voc_lch_ops(struct voice_data *v, enum voice_lch_mode lch_mode) +{ + int ret = 0; + + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + + ret = -EINVAL; + goto done; + } + + switch (lch_mode) { + case VOICE_LCH_START: + + ret = voc_end_voice_call(v->session_id); + if (ret < 0) + pr_err("%s: voice call end failed %d\n", + __func__, ret); + break; + case VOICE_LCH_STOP: + + ret = voc_start_voice_call(v->session_id); + if (ret < 0) { + pr_err("%s: voice call start failed %d\n", + __func__, ret); + goto done; + } + break; + default: + pr_err("%s: Invalid LCH mode: %d\n", + __func__, v->lch_mode); + break; + } +done: + return ret; +} + +/** + * voc_start_playback - + * command to set playback for voice session + * + * @set: Enable or Disable for playback start/stop + * @port_id: Pseudo Port ID for playback data + * + * Returns 0 on success or error on failure + */ +int voc_start_playback(uint32_t set, uint16_t port_id) +{ + struct voice_data *v = NULL; + int ret = 0; + struct voice_session_itr itr; + u16 cvs_handle; + + pr_debug("%s port_id = %#x set = %d", __func__, port_id, set); + + voice_itr_init(&itr, ALL_SESSION_VSID); + while (voice_itr_get_next_session(&itr, &v)) { + if ((v != NULL) && + (((port_id == VOICE_PLAYBACK_TX) && + is_sub1_vsid(v->session_id)) || + ((port_id == VOICE2_PLAYBACK_TX) && + is_sub2_vsid(v->session_id)))) { + + mutex_lock(&v->lock); + v->music_info.port_id = port_id; + v->music_info.play_enable = set; + if (set) + v->music_info.count++; + else + v->music_info.count--; + pr_debug("%s: music_info count=%d\n", __func__, + v->music_info.count); + + cvs_handle = voice_get_cvs_handle(v); + if (cvs_handle != 0) { + if (set) + ret = voice_cvs_start_playback(v); + else + ret = voice_cvs_stop_playback(v); + } + mutex_unlock(&v->lock); + } else { + pr_err("%s: Invalid session\n", __func__); + } + } + + return ret; +} +EXPORT_SYMBOL(voc_start_playback); + +/** + * voc_disable_topology - + * disable topology for voice session + * + * @session_id: voice session ID to send this command + * @disable: disable value + * + * Returns 0 on success or error on failure + */ +int voc_disable_topology(uint32_t session_id, uint32_t disable) +{ + struct voice_data *v = voice_get_session(session_id); + int ret = 0; + + if (v == NULL) { + pr_err("%s: invalid session_id 0x%x\n", __func__, session_id); + + return -EINVAL; + } + + mutex_lock(&v->lock); + + v->disable_topology = disable; + + mutex_unlock(&v->lock); + + return ret; +} +EXPORT_SYMBOL(voc_disable_topology); + +static int voice_set_packet_exchange_mode_and_config(uint32_t session_id, + uint32_t mode) +{ + struct voice_data *v = voice_get_session(session_id); + int ret = 0; + + if (v == NULL) { + pr_err("%s: invalid session_id 0x%x\n", __func__, session_id); + return -EINVAL; + } + + if (v->voc_state != VOC_RUN) + ret = voice_send_cvs_data_exchange_mode_cmd(v); + + if (ret) { + pr_err("%s: Error voice_send_data_exchange_mode_cmd %d\n", + __func__, ret); + goto fail; + } + + ret = voice_send_cvs_packet_exchange_config_cmd(v); + if (ret) { + pr_err("%s: Error: voice_send_packet_exchange_config_cmd %d\n", + __func__, ret); + goto fail; + } + + return ret; +fail: + return -EINVAL; +} + +/** + * voc_set_tx_mute - + * command to send TX mute for voice session + * + * @session_id: voice session ID to send this command + * @dir: RX or TX + * @mute: TX mute value + * @ramp_duration: Ramp duration in ms + * + * Returns 0 on success or error on failure + */ +int voc_set_tx_mute(uint32_t session_id, uint32_t dir, uint32_t mute, + uint32_t ramp_duration) +{ + struct voice_data *v = NULL; + int ret = 0; + struct voice_session_itr itr; + + voice_itr_init(&itr, session_id); + while (voice_itr_get_next_session(&itr, &v)) { + if (v != NULL) { + mutex_lock(&v->lock); + v->stream_tx.stream_mute = mute; + v->stream_tx.stream_mute_ramp_duration_ms = + ramp_duration; + if (is_voc_state_active(v->voc_state) && + (v->lch_mode == 0)) + ret = voice_send_stream_mute_cmd(v, + VSS_IVOLUME_DIRECTION_TX, + v->stream_tx.stream_mute, + v->stream_tx.stream_mute_ramp_duration_ms); + mutex_unlock(&v->lock); + } else { + pr_err("%s: invalid session_id 0x%x\n", __func__, + session_id); + + ret = -EINVAL; + break; + } + } + + return ret; +} +EXPORT_SYMBOL(voc_set_tx_mute); + +/** + * voc_set_device_mute - + * command to set device mute for voice session + * + * @session_id: voice session ID to send this command + * @dir: RX or TX + * @mute: mute value + * @ramp_duration: Ramp duration in ms + * + * Returns 0 on success or error on failure + */ +int voc_set_device_mute(uint32_t session_id, uint32_t dir, uint32_t mute, + uint32_t ramp_duration) +{ + struct voice_data *v = NULL; + int ret = 0; + struct voice_session_itr itr; + + voice_itr_init(&itr, session_id); + while (voice_itr_get_next_session(&itr, &v)) { + if (v != NULL) { + mutex_lock(&v->lock); + if (dir == VSS_IVOLUME_DIRECTION_TX) { + v->dev_tx.dev_mute = mute; + v->dev_tx.dev_mute_ramp_duration_ms = + ramp_duration; + } else { + v->dev_rx.dev_mute = mute; + v->dev_rx.dev_mute_ramp_duration_ms = + ramp_duration; + } + + if (((v->voc_state == VOC_RUN) || + (v->voc_state == VOC_STANDBY)) && + (v->lch_mode == 0)) + ret = voice_send_device_mute_cmd(v, + dir, + mute, + ramp_duration); + mutex_unlock(&v->lock); + } else { + pr_err("%s: invalid session_id 0x%x\n", __func__, + session_id); + + ret = -EINVAL; + break; + } + } + + return ret; +} +EXPORT_SYMBOL(voc_set_device_mute); + +int voc_get_rx_device_mute(uint32_t session_id) +{ + struct voice_data *v = voice_get_session(session_id); + int ret = 0; + + if (v == NULL) { + pr_err("%s: invalid session_id 0x%x\n", __func__, session_id); + + return -EINVAL; + } + + mutex_lock(&v->lock); + + ret = v->dev_rx.dev_mute; + + mutex_unlock(&v->lock); + + return ret; +} + +/** + * voc_set_tty_mode - + * Update tty mode for voice session + * + * @session_id: voice session ID + * @tty_mode: TTY mode value + * + * Returns 0 on success or error on failure + */ +int voc_set_tty_mode(uint32_t session_id, uint8_t tty_mode) +{ + struct voice_data *v = voice_get_session(session_id); + int ret = 0; + + if (v == NULL) { + pr_err("%s: invalid session_id 0x%x\n", __func__, session_id); + + return -EINVAL; + } + + mutex_lock(&v->lock); + + v->tty_mode = tty_mode; + + mutex_unlock(&v->lock); + + return ret; +} +EXPORT_SYMBOL(voc_set_tty_mode); + +/** + * voc_get_tty_mode - + * Retrieve tty mode for voice session + * + * @session_id: voice session ID + * + * Returns 0 on success or error on failure + */ +uint8_t voc_get_tty_mode(uint32_t session_id) +{ + struct voice_data *v = voice_get_session(session_id); + int ret = 0; + + if (v == NULL) { + pr_err("%s: invalid session_id 0x%x\n", __func__, session_id); + + return -EINVAL; + } + + mutex_lock(&v->lock); + + ret = v->tty_mode; + + mutex_unlock(&v->lock); + + return ret; +} +EXPORT_SYMBOL(voc_get_tty_mode); + +/** + * voc_set_pp_enable - + * Command to set PP for voice module + * + * @session_id: voice session ID to send this command + * @module_id: voice module id + * @enable: enable/disable flag + * + * Returns 0 on success or error on failure + */ +int voc_set_pp_enable(uint32_t session_id, + struct module_instance_info mod_inst_info, + uint32_t enable) +{ + struct voice_data *v = NULL; + int ret = 0; + struct voice_session_itr itr; + int mid = mod_inst_info.module_id; + int iid = mod_inst_info.instance_id; + + voice_itr_init(&itr, session_id); + while (voice_itr_get_next_session(&itr, &v)) { + if (v != NULL) { + if (!(is_voice_app_id(v->session_id))) + continue; + + mutex_lock(&v->lock); + if (mid == MODULE_ID_VOICE_MODULE_ST && + iid == INSTANCE_ID_0) + v->st_enable = enable; + + if (v->voc_state == VOC_RUN) { + if ((mid == MODULE_ID_VOICE_MODULE_ST) && + iid == INSTANCE_ID_0 && (!v->tty_mode)) + ret = voice_send_set_pp_enable_cmd( + v, mod_inst_info, enable); + } + mutex_unlock(&v->lock); + } else { + pr_err("%s: invalid session_id 0x%x\n", __func__, + session_id); + ret = -EINVAL; + break; + } + } + + return ret; +} +EXPORT_SYMBOL(voc_set_pp_enable); + +/** + * voc_set_hd_enable - + * Command to set HD for voice session + * + * @session_id: voice session ID to send this command + * @enable: enable/disable flag + * + * Returns 0 on success or error on failure + */ +int voc_set_hd_enable(uint32_t session_id, uint32_t enable) +{ + struct voice_data *v = NULL; + int ret = 0; + struct voice_session_itr itr; + + voice_itr_init(&itr, session_id); + while (voice_itr_get_next_session(&itr, &v)) { + if (v != NULL) { + mutex_lock(&v->lock); + v->hd_enable = enable; + + if (v->voc_state == VOC_RUN) + ret = voice_send_hd_cmd(v, enable); + + mutex_unlock(&v->lock); + } else { + pr_err("%s: invalid session_id 0x%x\n", __func__, + session_id); + ret = -EINVAL; + break; + } + } + + return ret; +} +EXPORT_SYMBOL(voc_set_hd_enable); + +/** + * voc_set_afe_sidetone - + * Command to set sidetone at AFE + * + * @session_id: voice session ID to send this command + * @sidetone_enable: enable/disable flag for sidetone + * + * Returns 0 on success or error on failure + */ +int voc_set_afe_sidetone(uint32_t session_id, bool sidetone_enable) +{ + struct voice_data *v = NULL; + int ret = -EINVAL; + struct voice_session_itr itr; + u16 rx_port, tx_port; + + common.sidetone_enable = sidetone_enable; + voice_itr_init(&itr, session_id); + while (voice_itr_get_next_session(&itr, &v)) { + if (v == NULL) { + pr_err("%s: invalid session_id 0x%x\n", __func__, + session_id); + ret = -EINVAL; + break; + } + mutex_lock(&v->lock); + if (v->voc_state != VOC_RUN) { + mutex_unlock(&v->lock); + continue; + } + rx_port = v->dev_rx.port_id; + tx_port = v->dev_tx.port_id; + ret = afe_sidetone_enable(tx_port, rx_port, + sidetone_enable); + if (!ret) { + mutex_unlock(&v->lock); + break; + } + mutex_unlock(&v->lock); + } + return ret; +} +EXPORT_SYMBOL(voc_set_afe_sidetone); + +/** + * voc_get_afe_sidetone - + * Retrieve sidetone status at AFE + * + * Returns sidetone enable status + */ +bool voc_get_afe_sidetone(void) +{ + bool ret; + + ret = common.sidetone_enable; + return ret; +} +EXPORT_SYMBOL(voc_get_afe_sidetone); +int voc_get_pp_enable(uint32_t session_id, + struct module_instance_info mod_inst_info) +{ + struct voice_data *v = voice_get_session(session_id); + int ret = 0; + + if (v == NULL) { + pr_err("%s: invalid session_id 0x%x\n", __func__, session_id); + + return -EINVAL; + } + + mutex_lock(&v->lock); + if (mod_inst_info.module_id == MODULE_ID_VOICE_MODULE_ST && + mod_inst_info.instance_id == INSTANCE_ID_0) + ret = v->st_enable; + mutex_unlock(&v->lock); + + return ret; +} + +/** + * voc_set_rx_vol_step - + * command to send voice RX volume in step value + * + * @session_id: voice session ID + * @dir: direction RX or TX + * @vol_step: Volume step value + * @ramp_duration: Ramp duration in ms + * + * Returns 0 on success or -EINVAL on failure + */ +int voc_set_rx_vol_step(uint32_t session_id, uint32_t dir, uint32_t vol_step, + uint32_t ramp_duration) +{ + struct voice_data *v = NULL; + int ret = 0; + struct voice_session_itr itr; + + pr_debug("%s session id = %#x vol = %u", __func__, session_id, + vol_step); + + voice_itr_init(&itr, session_id); + while (voice_itr_get_next_session(&itr, &v)) { + if (v != NULL) { + mutex_lock(&v->lock); + v->dev_rx.volume_step_value = vol_step; + v->dev_rx.volume_ramp_duration_ms = ramp_duration; + if (is_voc_state_active(v->voc_state)) + ret = voice_send_vol_step_cmd(v); + mutex_unlock(&v->lock); + } else { + pr_err("%s: invalid session_id 0x%x\n", __func__, + session_id); + + ret = -EINVAL; + break; + } + } + + return ret; +} +EXPORT_SYMBOL(voc_set_rx_vol_step); + +/** + * voc_set_device_config - + * Set voice path config for RX or TX + * + * @session_id: voice session ID + * @path_dir: direction RX or TX + * @finfo: format config info + * + * Returns 0 on success or -EINVAL on failure + */ +int voc_set_device_config(uint32_t session_id, uint8_t path_dir, + struct media_format_info *finfo) +{ + struct voice_data *v = voice_get_session(session_id); + + if (v == NULL) { + pr_err("%s: Invalid session_id 0x%x\n", __func__, session_id); + + return -EINVAL; + } + + pr_debug("%s: path_dir=%d port_id=%x, channels=%d, sample_rate=%d, bits_per_sample=%d\n", + __func__, path_dir, finfo->port_id, finfo->num_channels, + finfo->sample_rate, finfo->bits_per_sample); + + mutex_lock(&v->lock); + switch (path_dir) { + case RX_PATH: + v->dev_rx.port_id = q6audio_get_port_id(finfo->port_id); + v->dev_rx.no_of_channels = finfo->num_channels; + v->dev_rx.sample_rate = finfo->sample_rate; + v->dev_rx.bits_per_sample = finfo->bits_per_sample; + memcpy(&v->dev_rx.channel_mapping, &finfo->channel_mapping, + VSS_CHANNEL_MAPPING_SIZE); + break; + case TX_PATH: + v->dev_tx.port_id = q6audio_get_port_id(finfo->port_id); + v->dev_tx.no_of_channels = finfo->num_channels; + v->dev_tx.sample_rate = finfo->sample_rate; + v->dev_tx.bits_per_sample = finfo->bits_per_sample; + memcpy(&v->dev_tx.channel_mapping, &finfo->channel_mapping, + VSS_CHANNEL_MAPPING_SIZE); + break; + default: + pr_err("%s: Invalid path_dir %d\n", __func__, path_dir); + return -EINVAL; + } + + mutex_unlock(&v->lock); + + return 0; +} +EXPORT_SYMBOL(voc_set_device_config); + +/** + * voc_set_ext_ec_ref_media_fmt_info - + * Update voice EC media format info + * + * @finfo: media format info + * + */ +int voc_set_ext_ec_ref_media_fmt_info(struct media_format_info *finfo) +{ + mutex_lock(&common.common_lock); + if (common.ec_ref_ext) { + common.ec_media_fmt_info.num_channels = finfo->num_channels; + common.ec_media_fmt_info.bits_per_sample = + finfo->bits_per_sample; + common.ec_media_fmt_info.sample_rate = finfo->sample_rate; + memcpy(&common.ec_media_fmt_info.channel_mapping, + &finfo->channel_mapping, VSS_CHANNEL_MAPPING_SIZE); + } else { + pr_debug("%s: Ext Ec Ref not active, returning", __func__); + } + mutex_unlock(&common.common_lock); + return 0; +} +EXPORT_SYMBOL(voc_set_ext_ec_ref_media_fmt_info); + +/** + * voc_set_route_flag - + * Set voice route state for RX or TX + * + * @session_id: voice session ID + * @path_dir: direction RX or TX + * @set: Value of route state to set + * + * Returns 0 on success or -EINVAL on failure + */ +int voc_set_route_flag(uint32_t session_id, uint8_t path_dir, uint8_t set) +{ + struct voice_data *v = voice_get_session(session_id); + + if (v == NULL) { + pr_err("%s: invalid session_id 0x%x\n", __func__, session_id); + + return -EINVAL; + } + + pr_debug("%s: path_dir=%d, set=%d\n", __func__, path_dir, set); + + mutex_lock(&v->lock); + + if (path_dir == RX_PATH) + v->voc_route_state.rx_route_flag = set; + else + v->voc_route_state.tx_route_flag = set; + + mutex_unlock(&v->lock); + + return 0; +} +EXPORT_SYMBOL(voc_set_route_flag); + +/** + * voc_get_route_flag - + * Retrieve voice route state for RX or TX + * + * @session_id: voice session ID + * @path_dir: direction RX or TX + * + * Returns route state on success or 0 on failure + */ +uint8_t voc_get_route_flag(uint32_t session_id, uint8_t path_dir) +{ + struct voice_data *v = voice_get_session(session_id); + int ret = 0; + + if (v == NULL) { + pr_err("%s: invalid session_id 0x%x\n", __func__, session_id); + + return 0; + } + + mutex_lock(&v->lock); + + if (path_dir == RX_PATH) + ret = v->voc_route_state.rx_route_flag; + else + ret = v->voc_route_state.tx_route_flag; + + mutex_unlock(&v->lock); + + return ret; +} +EXPORT_SYMBOL(voc_get_route_flag); + +/** + * voc_get_mbd_enable - + * Retrieve mic break detection enable state + * + * Returns true if mic break detection is enabled or false if disabled + */ +bool voc_get_mbd_enable(void) +{ + bool enable = false; + + mutex_lock(&common.common_lock); + enable = common.mic_break_enable; + mutex_unlock(&common.common_lock); + + return enable; +} +EXPORT_SYMBOL(voc_get_mbd_enable); + +/** + * voc_set_mbd_enable - + * Set mic break detection enable state + * + * @enable: mic break detection state to set + * + * Returns 0 + */ +uint8_t voc_set_mbd_enable(bool enable) +{ + struct voice_data *v = NULL; + struct voice_session_itr itr; + bool check_and_send_event = false; + uint32_t event_id = VSS_INOTIFY_CMD_LISTEN_FOR_EVENT_CLASS; + uint32_t class_id = VSS_ICOMMON_EVENT_CLASS_VOICE_ACTIVITY_UPDATE; + + mutex_lock(&common.common_lock); + if (common.mic_break_enable != enable) + check_and_send_event = true; + common.mic_break_enable = enable; + mutex_unlock(&common.common_lock); + + if (!check_and_send_event) + return 0; + + if (!enable) + event_id = VSS_INOTIFY_CMD_CANCEL_EVENT_CLASS; + + memset(&itr, 0, sizeof(itr)); + + voice_itr_init(&itr, ALL_SESSION_VSID); + while (voice_itr_get_next_session(&itr, &v)) { + if (v != NULL) { + mutex_lock(&v->lock); + if (is_voc_state_active(v->voc_state)) { + voice_send_mvm_event_class_cmd(v, event_id, + class_id); + } + mutex_unlock(&v->lock); + } + } + + return 0; +} +EXPORT_SYMBOL(voc_set_mbd_enable); + +/** + * voc_end_voice_call - + * command to end voice call + * + * @session_id: voice session ID to send this command + * + * Returns 0 on success or error on failure + */ +int voc_end_voice_call(uint32_t session_id) +{ + struct voice_data *v = voice_get_session(session_id); + int ret = 0; + + if (v == NULL) { + pr_err("%s: invalid session_id 0x%x\n", __func__, session_id); + + return -EINVAL; + } + + mutex_lock(&v->lock); + + if (v->voc_state == VOC_RUN || v->voc_state == VOC_ERROR || + v->voc_state == VOC_CHANGE || v->voc_state == VOC_STANDBY) { + + pr_debug("%s: VOC_STATE: %d\n", __func__, v->voc_state); + + ret = voice_destroy_vocproc(v); + if (ret < 0) + pr_err("%s: destroy voice failed\n", __func__); + + voc_update_session_params(v); + + voice_destroy_mvm_cvs_session(v); + + ret = voice_mhi_end(); + if (ret < 0) + pr_debug("%s: voice_mhi_end failed! %d\n", + __func__, ret); + v->voc_state = VOC_RELEASE; + } else { + pr_err("%s: Error: End voice called in state %d\n", + __func__, v->voc_state); + + ret = -EINVAL; + } + + mutex_unlock(&v->lock); + return ret; +} +EXPORT_SYMBOL(voc_end_voice_call); + +/** + * voc_standby_voice_call - + * command to standy voice call + * + * @session_id: voice session ID to send this command + * + * Returns 0 on success or error on failure + */ +int voc_standby_voice_call(uint32_t session_id) +{ + struct voice_data *v = voice_get_session(session_id); + struct apr_hdr mvm_standby_voice_cmd; + void *apr_mvm; + u16 mvm_handle; + int ret = 0; + + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + return -EINVAL; + } + pr_debug("%s: voc state=%d", __func__, v->voc_state); + + if (v->voc_state == VOC_RUN) { + apr_mvm = common.apr_q6_mvm; + if (!apr_mvm) { + pr_err("%s: apr_mvm is NULL.\n", __func__); + ret = -EINVAL; + goto fail; + } + mvm_handle = voice_get_mvm_handle(v); + mvm_standby_voice_cmd.hdr_field = + APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + mvm_standby_voice_cmd.pkt_size = + APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(mvm_standby_voice_cmd) - APR_HDR_SIZE); + pr_debug("send mvm_standby_voice_cmd pkt size = %d\n", + mvm_standby_voice_cmd.pkt_size); + mvm_standby_voice_cmd.src_port = + voice_get_idx_for_session(v->session_id); + mvm_standby_voice_cmd.dest_port = mvm_handle; + mvm_standby_voice_cmd.token = 0; + mvm_standby_voice_cmd.opcode = VSS_IMVM_CMD_STANDBY_VOICE; + v->mvm_state = CMD_STATUS_FAIL; + ret = apr_send_pkt(apr_mvm, + (uint32_t *)&mvm_standby_voice_cmd); + if (ret < 0) { + pr_err("Fail in sending VSS_IMVM_CMD_STANDBY_VOICE\n"); + ret = -EINVAL; + goto fail; + } + v->voc_state = VOC_STANDBY; + } +fail: + return ret; +} +EXPORT_SYMBOL(voc_standby_voice_call); + +/** + * voc_disable_device - + * command to pause call and disable voice path + * + * @session_id: voice session ID to send this command + * + * Returns 0 on success or error on failure + */ +int voc_disable_device(uint32_t session_id) +{ + struct voice_data *v = voice_get_session(session_id); + int ret = 0; + + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + return -EINVAL; + } + + pr_debug("%s: voc state=%d\n", __func__, v->voc_state); + + mutex_lock(&v->lock); + if (v->voc_state == VOC_RUN) { + ret = voice_pause_voice_call(v); + if (ret < 0) { + pr_err("%s: Pause Voice Call failed for session 0x%x, err %d!\n", + __func__, v->session_id, ret); + goto done; + } + rtac_remove_voice(voice_get_cvs_handle(v)); + voice_send_cvp_deregister_vol_cal_cmd(v); + voice_send_cvp_deregister_cal_cmd(v); + voice_send_cvp_deregister_dev_cfg_cmd(v); + + /* Unload topology modules */ + voice_unload_topo_modules(); + + v->voc_state = VOC_CHANGE; + } else { + pr_debug("%s: called in voc state=%d, No_OP\n", + __func__, v->voc_state); + } + +done: + mutex_unlock(&v->lock); + + return ret; +} +EXPORT_SYMBOL(voc_disable_device); + +/** + * voc_enable_device - + * command to enable voice path and start call + * + * @session_id: voice session ID to send this command + * + * Returns 0 on success or error on failure + */ +int voc_enable_device(uint32_t session_id) +{ + struct voice_data *v = voice_get_session(session_id); + struct module_instance_info mod_inst_info; + int ret = 0; + + memset(&mod_inst_info, 0, sizeof(mod_inst_info)); + + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + return -EINVAL; + } + + pr_debug("%s: voc state=%d\n", __func__, v->voc_state); + mutex_lock(&v->lock); + if (v->voc_state == VOC_CHANGE) { + ret = voice_send_tty_mode_cmd(v); + if (ret < 0) { + pr_err("%s: Sending TTY mode failed, ret=%d\n", + __func__, ret); + /* Not a critical error, allow voice call to continue */ + } + + mod_inst_info.module_id = MODULE_ID_VOICE_MODULE_ST; + mod_inst_info.instance_id = INSTANCE_ID_0; + + if (v->tty_mode) { + /* disable slowtalk */ + voice_send_set_pp_enable_cmd(v, mod_inst_info, 0); + } else { + /* restore slowtalk */ + voice_send_set_pp_enable_cmd(v, mod_inst_info, + v->st_enable); + } + + ret = voice_send_set_device_cmd(v); + if (ret < 0) { + pr_err("%s: Set device failed, ret=%d\n", + __func__, ret); + goto done; + } + + ret = voice_send_cvp_media_fmt_info_cmd(v); + if (ret < 0) { + pr_err("%s: Set format failed err:%d\n", __func__, ret); + goto done; + } + + ret = voice_send_cvp_topology_commit_cmd(v); + if (ret < 0) { + pr_err("%s: Set topology commit failed\n", __func__); + goto done; + } + + /* Send MFC config only when the no of channels are > 1 */ + if (v->dev_rx.no_of_channels > NUM_CHANNELS_MONO) { + ret = voice_send_cvp_mfc_config_cmd(v); + if (ret < 0) { + pr_warn("%s: Set mfc config failed err: %d\n", + __func__, ret); + } + } + + voice_send_cvp_register_dev_cfg_cmd(v); + voice_send_cvp_register_cal_cmd(v); + voice_send_cvp_register_vol_cal_cmd(v); + + rtac_add_voice(voice_get_cvs_handle(v), + voice_get_cvp_handle(v), + v->dev_rx.port_id, v->dev_tx.port_id, + v->dev_rx.dev_id, v->dev_tx.dev_id, + v->session_id); + + ret = voice_send_start_voice_cmd(v); + if (ret < 0) { + pr_err("%s: Fail in sending START_VOICE, ret=%d\n", + __func__, ret); + goto done; + } + v->voc_state = VOC_RUN; + } else { + pr_debug("%s: called in voc state=%d, No_OP\n", + __func__, v->voc_state); + } + +done: + mutex_unlock(&v->lock); + + return ret; +} +EXPORT_SYMBOL(voc_enable_device); + +/** + * voc_set_lch - + * command to set hold/unhold call state + * + * @session_id: voice session ID to send this command + * @lch_mode: LCH mode to set + * + * Returns 0 on success or error on failure + */ +int voc_set_lch(uint32_t session_id, enum voice_lch_mode lch_mode) +{ + struct voice_data *v = voice_get_session(session_id); + int ret = 0; + + if (v == NULL) { + pr_err("%s: Invalid session_id 0x%x\n", __func__, session_id); + + ret = -EINVAL; + goto done; + } + + mutex_lock(&v->lock); + if (v->lch_mode == lch_mode) { + pr_debug("%s: Session %d already in LCH mode %d\n", + __func__, session_id, lch_mode); + + mutex_unlock(&v->lock); + goto done; + } + + v->lch_mode = lch_mode; + mutex_unlock(&v->lock); + + ret = voc_lch_ops(v, v->lch_mode); + if (ret < 0) { + pr_err("%s: lch ops failed %d\n", __func__, ret); + goto done; + } + +done: + return ret; +} +EXPORT_SYMBOL(voc_set_lch); + +/** + * voc_resume_voice_call - + * command to resume voice call + * + * @session_id: voice session ID to send this command + * + * Returns 0 on success or error on failure + */ +int voc_resume_voice_call(uint32_t session_id) +{ + struct voice_data *v = voice_get_session(session_id); + int ret = 0; + + ret = voice_send_start_voice_cmd(v); + if (ret < 0) { + pr_err("Fail in sending START_VOICE\n"); + goto fail; + } + v->voc_state = VOC_RUN; + return 0; +fail: + return -EINVAL; +} +EXPORT_SYMBOL(voc_resume_voice_call); + +/** + * voc_start_voice_call - + * command to start voice call + * + * @session_id: voice session ID to send this command + * + * Returns 0 on success or error on failure + */ +int voc_start_voice_call(uint32_t session_id) +{ + struct voice_data *v = voice_get_session(session_id); + int ret = 0; + + if (v == NULL) { + pr_err("%s: invalid session_id 0x%x\n", __func__, session_id); + + return -EINVAL; + } + + mutex_lock(&v->lock); + + if (v->voc_state == VOC_ERROR) { + pr_debug("%s: VOC in ERR state\n", __func__); + + voice_destroy_mvm_cvs_session(v); + v->voc_state = VOC_INIT; + } + + if ((v->voc_state == VOC_INIT) || + (v->voc_state == VOC_RELEASE)) { + ret = voice_apr_register(session_id); + if (ret < 0) { + pr_err("%s: apr register failed\n", __func__); + goto fail; + } + + if (is_cvd_version_queried()) { + pr_debug("%s: Returning the cached value %s\n", + __func__, common.cvd_version); + } else { + ret = voice_send_mvm_cvd_version_cmd(v); + if (ret < 0) + pr_debug("%s: Error retrieving CVD version %d\n", + __func__, ret); + } + + ret = voice_mhi_start(); + if (ret < 0) { + pr_debug("%s: voice_mhi_start failed! %d\n", + __func__, ret); + goto fail; + } + + ret = voice_create_mvm_cvs_session(v); + if (ret < 0) { + pr_err("create mvm and cvs failed\n"); + goto fail; + } + + if (is_voip_session(session_id)) { + /* Allocate oob mem if not already allocated and + * memory map the oob memory block. + */ + ret = voice_alloc_and_map_oob_mem(v); + if (ret < 0) { + pr_err("%s: voice_alloc_and_map_oob_mem() failed, ret:%d\n", + __func__, ret); + + goto fail; + } + + ret = voice_set_packet_exchange_mode_and_config( + session_id, + VSS_ISTREAM_PACKET_EXCHANGE_MODE_OUT_OF_BAND); + if (ret) { + pr_err("%s: Err: exchange_mode_and_config %d\n", + __func__, ret); + + goto fail; + } + } + ret = voice_send_dual_control_cmd(v); + if (ret < 0) { + pr_err("Err Dual command failed\n"); + goto fail; + } + ret = voice_setup_vocproc(v); + if (ret < 0) { + pr_err("setup voice failed\n"); + goto fail; + } + + ret = voice_send_vol_step_cmd(v); + if (ret < 0) + pr_err("voice volume failed\n"); + + ret = voice_send_stream_mute_cmd(v, + VSS_IVOLUME_DIRECTION_TX, + v->stream_tx.stream_mute, + v->stream_tx.stream_mute_ramp_duration_ms); + if (ret < 0) + pr_err("voice mute failed\n"); + + ret = voice_send_start_voice_cmd(v); + if (ret < 0) { + pr_err("start voice failed\n"); + goto fail; + } + + v->voc_state = VOC_RUN; + } else { + pr_err("%s: Error: Start voice called in state %d\n", + __func__, v->voc_state); + + ret = -EINVAL; + goto fail; + } +fail: + mutex_unlock(&v->lock); + return ret; +} +EXPORT_SYMBOL(voc_start_voice_call); + +/** + * voc_set_ext_ec_ref_port_id - + * Set EC ref port id + * + * Returns 0 on success or -EINVAL on failure + */ +int voc_set_ext_ec_ref_port_id(uint16_t port_id, bool state) +{ + int ret = 0; + + mutex_lock(&common.common_lock); + if (state == true) { + if (port_id == AFE_PORT_INVALID) { + pr_err("%s: Invalid port id", __func__); + ret = -EINVAL; + goto exit; + } + common.ec_ref_ext = true; + } else { + common.ec_ref_ext = false; + } + /* Cache EC Fromat Info in common */ + common.ec_media_fmt_info.port_id = port_id; +exit: + mutex_unlock(&common.common_lock); + return ret; +} +EXPORT_SYMBOL(voc_set_ext_ec_ref_port_id); + +/** + * voc_get_ext_ec_ref_port_id - + * Retrieve EC ref port id + * + * Returns EC Ref port id if present + * otherwise AFE_PORT_INVALID + */ +int voc_get_ext_ec_ref_port_id(void) +{ + if (common.ec_ref_ext) + return common.ec_media_fmt_info.port_id; + else + return AFE_PORT_INVALID; +} +EXPORT_SYMBOL(voc_get_ext_ec_ref_port_id); + +/** + * voc_register_mvs_cb - + * Update callback info for mvs + * + * @ul_cb: Uplink callback fn + * @dl_cb: downlink callback fn + * ssr_cb: SSR callback fn + * @private_data: private data of mvs + * + */ +void voc_register_mvs_cb(ul_cb_fn ul_cb, + dl_cb_fn dl_cb, + voip_ssr_cb ssr_cb, + void *private_data) +{ + common.mvs_info.ul_cb = ul_cb; + common.mvs_info.dl_cb = dl_cb; + common.mvs_info.ssr_cb = ssr_cb; + common.mvs_info.private_data = private_data; +} +EXPORT_SYMBOL(voc_register_mvs_cb); + +/** + * voc_register_dtmf_rx_detection_cb - + * Update callback info for dtmf + * + * @dtmf_rx_ul_cb: DTMF uplink RX callback fn + * @private_data: private data of dtmf info + * + */ +void voc_register_dtmf_rx_detection_cb(dtmf_rx_det_cb_fn dtmf_rx_ul_cb, + void *private_data) +{ + common.dtmf_info.dtmf_rx_ul_cb = dtmf_rx_ul_cb; + common.dtmf_info.private_data = private_data; +} +EXPORT_SYMBOL(voc_register_dtmf_rx_detection_cb); + +/** + * voc_config_vocoder - + * Update config for mvs params. + */ +void voc_config_vocoder(uint32_t media_type, + uint32_t rate, + uint32_t network_type, + uint32_t dtx_mode, + uint32_t evrc_min_rate, + uint32_t evrc_max_rate) +{ + common.mvs_info.media_type = media_type; + common.mvs_info.rate = rate; + common.mvs_info.network_type = network_type; + common.mvs_info.dtx_mode = dtx_mode; + common.mvs_info.evrc_min_rate = evrc_min_rate; + common.mvs_info.evrc_max_rate = evrc_max_rate; +} +EXPORT_SYMBOL(voc_config_vocoder); + +static int32_t qdsp_mvm_callback(struct apr_client_data *data, void *priv) +{ + uint32_t *ptr = NULL, min_payload_size = 0; + struct common_data *c = NULL; + struct voice_data *v = NULL; + struct vss_evt_voice_activity *voice_act_update = NULL; + int i = 0; + struct vss_iversion_rsp_get_t *version_rsp = NULL; + + if ((data == NULL) || (priv == NULL)) { + pr_err("%s: data or priv is NULL\n", __func__); + return -EINVAL; + } + + c = priv; + + pr_debug("%s: Payload Length = %d, opcode=%x\n", __func__, + data->payload_size, data->opcode); + + if (data->opcode == RESET_EVENTS) { + pr_debug("%s: Reset event received in Voice service\n", + __func__); + + if (common.mvs_info.ssr_cb) { + pr_debug("%s: Informing reset event to VoIP\n", + __func__); + common.mvs_info.ssr_cb(data->opcode, + common.mvs_info.private_data); + } + + apr_reset(c->apr_q6_mvm); + c->apr_q6_mvm = NULL; + + /* clean up memory handle */ + c->cal_mem_handle = 0; + c->rtac_mem_handle = 0; + cal_utils_clear_cal_block_q6maps(MAX_VOICE_CAL_TYPES, + common.cal_data); + rtac_clear_mapping(VOICE_RTAC_CAL); + + /* Sub-system restart is applicable to all sessions. */ + for (i = 0; i < MAX_VOC_SESSIONS; i++) { + c->voice[i].mvm_handle = 0; + c->voice[i].shmem_info.mem_handle = 0; + } + + /* Free the ION memory and clear handles for Source Tracking */ + if (is_source_tracking_shared_memomry_allocated()) { + msm_audio_ion_free( + common.source_tracking_sh_mem.sh_mem_block.dma_buf); + common.source_tracking_sh_mem.mem_handle = 0; + common.source_tracking_sh_mem.sh_mem_block.dma_buf = + NULL; + } + /* clean up srvcc rec flag */ + c->srvcc_rec_flag = false; + voc_set_error_state(data->reset_proc); + return 0; + } + + pr_debug("%s: session_idx 0x%x\n", __func__, data->dest_port); + + v = voice_get_session_by_idx(data->dest_port); + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + + return -EINVAL; + } + + if (data->opcode == APR_BASIC_RSP_RESULT) { + if (data->payload_size >= sizeof(ptr[0]) * 2) { + ptr = data->payload; + + pr_debug("%x %x\n", ptr[0], ptr[1]); + /* ping mvm service ACK */ + switch (ptr[0]) { + case VSS_IMVM_CMD_CREATE_PASSIVE_CONTROL_SESSION: + case VSS_IMVM_CMD_CREATE_FULL_CONTROL_SESSION: + /* Passive session is used for CS call + * Full session is used for VoIP call. + */ + pr_debug("%s: cmd = 0x%x\n", __func__, ptr[0]); + if (!ptr[1]) { + pr_debug("%s: MVM handle is %d\n", + __func__, data->src_port); + voice_set_mvm_handle(v, data->src_port); + } else + pr_err("got NACK for sending MVM create session\n"); + v->mvm_state = CMD_STATUS_SUCCESS; + v->async_err = ptr[1]; + wake_up(&v->mvm_wait); + break; + case VSS_IMVM_CMD_START_VOICE: + case VSS_IMVM_CMD_ATTACH_VOCPROC: + case VSS_IMVM_CMD_STOP_VOICE: + case VSS_IMVM_CMD_DETACH_VOCPROC: + case VSS_ISTREAM_CMD_SET_TTY_MODE: + case APRV2_IBASIC_CMD_DESTROY_SESSION: + case VSS_IMVM_CMD_ATTACH_STREAM: + case VSS_IMVM_CMD_DETACH_STREAM: + case VSS_ICOMMON_CMD_SET_NETWORK: + case VSS_ICOMMON_CMD_SET_VOICE_TIMING: + case VSS_IMVM_CMD_SET_POLICY_DUAL_CONTROL: + case VSS_IMVM_CMD_SET_CAL_NETWORK: + case VSS_IMVM_CMD_SET_CAL_MEDIA_TYPE: + case VSS_IMEMORY_CMD_MAP_PHYSICAL: + case VSS_IMEMORY_CMD_UNMAP: + case VSS_IMVM_CMD_PAUSE_VOICE: + case VSS_IMVM_CMD_STANDBY_VOICE: + case VSS_IHDVOICE_CMD_ENABLE: + case VSS_IHDVOICE_CMD_DISABLE: + case VSS_INOTIFY_CMD_LISTEN_FOR_EVENT_CLASS: + case VSS_INOTIFY_CMD_CANCEL_EVENT_CLASS: + pr_debug("%s: cmd = 0x%x\n", __func__, ptr[0]); + v->mvm_state = CMD_STATUS_SUCCESS; + v->async_err = ptr[1]; + wake_up(&v->mvm_wait); + break; + case VSS_IVERSION_CMD_GET: + pr_debug("%s: Error retrieving CVD Version, error:%d\n", + __func__, ptr[1]); + + strlcpy(common.cvd_version, CVD_VERSION_0_0, + sizeof(common.cvd_version)); + pr_debug("%s: Fall back to default value, CVD Version = %s\n", + __func__, common.cvd_version); + + v->mvm_state = CMD_STATUS_SUCCESS; + v->async_err = ptr[1]; + wake_up(&v->mvm_wait); + break; + default: + pr_debug("%s: not match cmd = 0x%x\n", + __func__, ptr[0]); + break; + } + } + } else if (data->opcode == VSS_IMEMORY_RSP_MAP) { + pr_debug("%s, Revd VSS_IMEMORY_RSP_MAP response\n", __func__); + + if (data->payload_size < sizeof(ptr[0])) { + pr_err("%s: payload has invalid size[%d]\n", __func__, + data->payload_size); + return -EINVAL; + } + + if (data->token == VOIP_MEM_MAP_TOKEN) { + ptr = data->payload; + if (ptr[0]) { + v->shmem_info.mem_handle = ptr[0]; + pr_debug("%s: shared mem_handle: 0x[%x]\n", + __func__, v->shmem_info.mem_handle); + v->mvm_state = CMD_STATUS_SUCCESS; + wake_up(&v->mvm_wait); + } + } else if (data->payload_size && + data->token == VOC_CAL_MEM_MAP_TOKEN) { + ptr = data->payload; + if (ptr[0]) { + c->cal_mem_handle = ptr[0]; + + pr_debug("%s: cal mem handle 0x%x\n", + __func__, c->cal_mem_handle); + + v->mvm_state = CMD_STATUS_SUCCESS; + wake_up(&v->mvm_wait); + } + } else if (data->payload_size && + data->token == VOC_VOICE_HOST_PCM_MAP_TOKEN) { + ptr = data->payload; + if (ptr[0]) { + common.voice_host_pcm_mem_handle = ptr[0]; + + pr_debug("%s: vhpcm mem handle 0x%x\n", + __func__, + common.voice_host_pcm_mem_handle); + v->mvm_state = CMD_STATUS_SUCCESS; + wake_up(&v->mvm_wait); + } + } else if (data->payload_size && + data->token == VOC_RTAC_MEM_MAP_TOKEN) { + ptr = data->payload; + if (ptr[0]) { + c->rtac_mem_handle = ptr[0]; + + pr_debug("%s: cal mem handle 0x%x\n", + __func__, c->rtac_mem_handle); + + v->mvm_state = CMD_STATUS_SUCCESS; + wake_up(&v->mvm_wait); + } + } else if (data->payload_size && + data->token == VOC_SOURCE_TRACKING_MEM_MAP_TOKEN) { + ptr = data->payload; + if (ptr[0]) { + common.source_tracking_sh_mem.mem_handle = + ptr[0]; + + pr_debug("%s: Source Tracking shared mem handle 0x%x\n", + __func__, + common.source_tracking_sh_mem.mem_handle); + + v->mvm_state = CMD_STATUS_SUCCESS; + wake_up(&v->mvm_wait); + } + } else { + pr_err("%s: Unknown mem map token %d\n", + __func__, data->token); + } + } else if (data->opcode == VSS_IVERSION_RSP_GET) { + pr_debug("%s: Received VSS_IVERSION_RSP_GET\n", __func__); + + if (data->payload_size) { + min_payload_size = min_t(u32, (int)data->payload_size, + CVD_VERSION_STRING_MAX_SIZE); + version_rsp = + (struct vss_iversion_rsp_get_t *)data->payload; + memcpy(common.cvd_version, version_rsp->version, + min_payload_size); + common.cvd_version[min_payload_size - 1] = '\0'; + pr_debug("%s: CVD Version = %s\n", + __func__, common.cvd_version); + + v->mvm_state = CMD_STATUS_SUCCESS; + wake_up(&v->mvm_wait); + } + } else if (data->opcode == VSS_ICOMMON_EVT_VOICE_ACTIVITY_UPDATE) { + if (data->payload_size == sizeof(struct vss_evt_voice_activity)) { + voice_act_update = + (struct vss_evt_voice_activity *) + data->payload; + + /* Drop notifications other than Mic Break */ + if ((voice_act_update->activity + != VSS_ICOMMON_VOICE_ACTIVITY_MIC_BREAK) + && (voice_act_update->activity + != VSS_ICOMMON_VOICE_ACITIVTY_MIC_UNBREAK)) + return 0; + + switch (voice_act_update->activity) { + case VSS_ICOMMON_VOICE_ACTIVITY_MIC_BREAK: + v->mic_break_status = true; + break; + case VSS_ICOMMON_VOICE_ACITIVTY_MIC_UNBREAK: + v->mic_break_status = false; + break; + } + + if (c->mic_break_enable) + schedule_work(&(v->voice_mic_break_work)); + } + } + + return 0; +} + +static int32_t qdsp_cvs_callback(struct apr_client_data *data, void *priv) +{ + uint32_t *ptr = NULL; + struct common_data *c = NULL; + struct voice_data *v = NULL; + int i = 0; + + if ((data == NULL) || (priv == NULL)) { + pr_err("%s: data or priv is NULL\n", __func__); + return -EINVAL; + } + + c = priv; + + pr_debug("%s: session_id 0x%x\n", __func__, data->dest_port); + pr_debug("%s: Payload Length = %d, opcode=%x\n", __func__, + data->payload_size, data->opcode); + + if (data->opcode == RESET_EVENTS) { + pr_debug("%s: Reset event received in Voice service\n", + __func__); + + apr_reset(c->apr_q6_cvs); + c->apr_q6_cvs = NULL; + + /* Sub-system restart is applicable to all sessions. */ + for (i = 0; i < MAX_VOC_SESSIONS; i++) + c->voice[i].cvs_handle = 0; + + cal_utils_clear_cal_block_q6maps(MAX_VOICE_CAL_TYPES, + common.cal_data); + + /* Free the ION memory and clear handles for Source Tracking */ + if (is_source_tracking_shared_memomry_allocated()) { + msm_audio_ion_free( + common.source_tracking_sh_mem.sh_mem_block.dma_buf); + common.source_tracking_sh_mem.mem_handle = 0; + common.source_tracking_sh_mem.sh_mem_block.dma_buf = + NULL; + } + voc_set_error_state(data->reset_proc); + return 0; + } + + v = voice_get_session_by_idx(data->dest_port); + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + + return -EINVAL; + } + + if (data->opcode == APR_BASIC_RSP_RESULT) { + if (data->payload_size) { + ptr = data->payload; + + pr_debug("%x %x\n", ptr[0], ptr[1]); + if (ptr[1] != 0) { + pr_err("%s: cmd = 0x%x returned error = 0x%x\n", + __func__, ptr[0], ptr[1]); + } + /*response from CVS */ + switch (ptr[0]) { + case VSS_ISTREAM_CMD_CREATE_PASSIVE_CONTROL_SESSION: + case VSS_ISTREAM_CMD_CREATE_FULL_CONTROL_SESSION: + if (!ptr[1]) { + pr_debug("%s: CVS handle is %d\n", + __func__, data->src_port); + voice_set_cvs_handle(v, data->src_port); + } else + pr_err("got NACK for sending CVS create session\n"); + v->cvs_state = CMD_STATUS_SUCCESS; + v->async_err = ptr[1]; + wake_up(&v->cvs_wait); + break; + case VSS_IVOLUME_CMD_MUTE_V2: + case VSS_ISTREAM_CMD_SET_MEDIA_TYPE: + case VSS_ISTREAM_CMD_VOC_AMR_SET_ENC_RATE: + case VSS_ISTREAM_CMD_VOC_AMRWB_SET_ENC_RATE: + case VSS_ISTREAM_CMD_SET_ENC_DTX_MODE: + case VSS_ISTREAM_CMD_CDMA_SET_ENC_MINMAX_RATE: + case APRV2_IBASIC_CMD_DESTROY_SESSION: + case VSS_ISTREAM_CMD_REGISTER_CALIBRATION_DATA_V2: + case VSS_ISTREAM_CMD_DEREGISTER_CALIBRATION_DATA: + case VSS_ISTREAM_CMD_REGISTER_STATIC_CALIBRATION_DATA: + case VSS_ISTREAM_CMD_DEREGISTER_STATIC_CALIBRATION_DATA: + case VSS_ICOMMON_CMD_MAP_MEMORY: + case VSS_ICOMMON_CMD_UNMAP_MEMORY: + case VSS_ICOMMON_CMD_SET_UI_PROPERTY: + case VSS_ICOMMON_CMD_SET_UI_PROPERTY_V2: + case VSS_IPLAYBACK_CMD_START: + case VSS_IPLAYBACK_CMD_STOP: + case VSS_IRECORD_CMD_START: + case VSS_IRECORD_CMD_STOP: + case VSS_ISTREAM_CMD_SET_PACKET_EXCHANGE_MODE: + case VSS_ISTREAM_CMD_SET_OOB_PACKET_EXCHANGE_CONFIG: + case VSS_ISTREAM_CMD_SET_RX_DTMF_DETECTION: + pr_debug("%s: cmd = 0x%x\n", __func__, ptr[0]); + v->cvs_state = CMD_STATUS_SUCCESS; + v->async_err = ptr[1]; + wake_up(&v->cvs_wait); + break; + case VSS_ICOMMON_CMD_SET_PARAM_V2: + case VSS_ICOMMON_CMD_SET_PARAM_V3: + pr_debug("%s: VSS_ICOMMON_CMD_SET_PARAM\n", + __func__); + rtac_make_voice_callback(RTAC_CVS, ptr, + data->payload_size); + break; + case VSS_ICOMMON_CMD_GET_PARAM_V2: + case VSS_ICOMMON_CMD_GET_PARAM_V3: + pr_debug("%s: VSS_ICOMMON_CMD_GET_PARAM_V2\n", + __func__); + /* Should only come here if there is an APR */ + /* error or malformed APR packet. Otherwise */ + /* response will be returned as */ + /* VSS_ICOMMON_RSP_GET_PARAM */ + if (ptr[1] != 0) { + pr_err("%s: CVP get param error = %d, resuming\n", + __func__, ptr[1]); + rtac_make_voice_callback(RTAC_CVP, + data->payload, + data->payload_size); + } + break; + default: + pr_debug("%s: cmd = 0x%x\n", __func__, ptr[0]); + break; + } + } + } else if (data->opcode == + VSS_ISTREAM_EVT_OOB_NOTIFY_ENC_BUFFER_READY) { + int ret = 0; + u16 cvs_handle; + uint32_t *cvs_voc_pkt; + struct cvs_enc_buffer_consumed_cmd send_enc_buf_consumed_cmd; + void *apr_cvs; + + pr_debug("Encoder buffer is ready\n"); + + apr_cvs = common.apr_q6_cvs; + if (!apr_cvs) { + pr_err("%s: apr_cvs is NULL\n", __func__); + return -EINVAL; + } + cvs_handle = voice_get_cvs_handle(v); + + send_enc_buf_consumed_cmd.hdr.hdr_field = + APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + send_enc_buf_consumed_cmd.hdr.pkt_size = + APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(send_enc_buf_consumed_cmd) - APR_HDR_SIZE); + + send_enc_buf_consumed_cmd.hdr.src_port = + voice_get_idx_for_session(v->session_id); + send_enc_buf_consumed_cmd.hdr.dest_port = cvs_handle; + send_enc_buf_consumed_cmd.hdr.token = 0; + send_enc_buf_consumed_cmd.hdr.opcode = + VSS_ISTREAM_EVT_OOB_NOTIFY_ENC_BUFFER_CONSUMED; + + cvs_voc_pkt = v->shmem_info.sh_buf.buf[1].data; + if (cvs_voc_pkt != NULL && common.mvs_info.ul_cb != NULL) { + if (v->shmem_info.sh_buf.buf[1].size < + ((3 * sizeof(uint32_t)) + cvs_voc_pkt[2])) { + pr_err("%s: invalid voc pkt size\n", __func__); + return -EINVAL; + } + /* cvs_voc_pkt[0] contains tx timestamp */ + common.mvs_info.ul_cb((uint8_t *)&cvs_voc_pkt[3], + cvs_voc_pkt[2], + cvs_voc_pkt[0], + common.mvs_info.private_data); + } else + pr_err("%s: cvs_voc_pkt or ul_cb is NULL\n", __func__); + + ret = apr_send_pkt(apr_cvs, + (uint32_t *) &send_enc_buf_consumed_cmd); + if (ret < 0) { + pr_err("%s: Err send ENC_BUF_CONSUMED_NOTIFY %d\n", + __func__, ret); + goto fail; + } + } else if (data->opcode == VSS_ISTREAM_EVT_SEND_ENC_BUFFER) { + pr_debug("Recd VSS_ISTREAM_EVT_SEND_ENC_BUFFER\n"); + } else if (data->opcode == + VSS_ISTREAM_EVT_OOB_NOTIFY_DEC_BUFFER_REQUEST) { + int ret = 0; + u16 cvs_handle; + uint32_t *cvs_voc_pkt; + struct cvs_dec_buffer_ready_cmd send_dec_buf; + void *apr_cvs; + + apr_cvs = common.apr_q6_cvs; + + if (!apr_cvs) { + pr_err("%s: apr_cvs is NULL\n", __func__); + return -EINVAL; + } + cvs_handle = voice_get_cvs_handle(v); + + send_dec_buf.hdr.hdr_field = + APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + + send_dec_buf.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(send_dec_buf) - APR_HDR_SIZE); + + send_dec_buf.hdr.src_port = + voice_get_idx_for_session(v->session_id); + send_dec_buf.hdr.dest_port = cvs_handle; + send_dec_buf.hdr.token = 0; + send_dec_buf.hdr.opcode = + VSS_ISTREAM_EVT_OOB_NOTIFY_DEC_BUFFER_READY; + + cvs_voc_pkt = (uint32_t *)(v->shmem_info.sh_buf.buf[0].data); + if (cvs_voc_pkt != NULL && common.mvs_info.dl_cb != NULL) { + /* Set timestamp to 0 and advance the pointer */ + cvs_voc_pkt[0] = 0; + /* Set media_type and advance the pointer */ + cvs_voc_pkt[1] = common.mvs_info.media_type; + common.mvs_info.dl_cb( + (uint8_t *)&cvs_voc_pkt[2], + common.mvs_info.private_data); + ret = apr_send_pkt(apr_cvs, (uint32_t *) &send_dec_buf); + if (ret < 0) { + pr_err("%s: Err send DEC_BUF_READY_NOTIFI %d\n", + __func__, ret); + goto fail; + } + } else { + pr_debug("%s: voc_pkt or dl_cb is NULL\n", __func__); + goto fail; + } + } else if (data->opcode == VSS_ISTREAM_EVT_REQUEST_DEC_BUFFER) { + pr_debug("Recd VSS_ISTREAM_EVT_REQUEST_DEC_BUFFER\n"); + } else if (data->opcode == VSS_ISTREAM_EVT_SEND_DEC_BUFFER) { + pr_debug("Send dec buf resp\n"); + } else if (data->opcode == APR_RSP_ACCEPTED) { + ptr = data->payload; + if (ptr[0]) + pr_debug("%s: APR_RSP_ACCEPTED for 0x%x:\n", + __func__, ptr[0]); + } else if (data->opcode == VSS_ISTREAM_EVT_NOT_READY) { + pr_debug("Recd VSS_ISTREAM_EVT_NOT_READY\n"); + } else if (data->opcode == VSS_ISTREAM_EVT_READY) { + pr_debug("Recd VSS_ISTREAM_EVT_READY\n"); + } else if (data->opcode == VSS_ICOMMON_RSP_GET_PARAM || + data->opcode == VSS_ICOMMON_RSP_GET_PARAM_V3) { + pr_debug("%s: VSS_ICOMMON_RSP_GET_PARAM\n", __func__); + ptr = data->payload; + if (ptr[0] != 0) { + pr_err("%s: VSS_ICOMMON_RSP_GET_PARAM returned error = 0x%x\n", + __func__, ptr[0]); + } + rtac_make_voice_callback(RTAC_CVS, data->payload, + data->payload_size); + } else if (data->opcode == VSS_ISTREAM_EVT_RX_DTMF_DETECTED) { + struct vss_istream_evt_rx_dtmf_detected *dtmf_rx_detected; + uint32_t *voc_pkt = data->payload; + uint32_t pkt_len = data->payload_size; + + if ((voc_pkt != NULL) && + (pkt_len == + sizeof(struct vss_istream_evt_rx_dtmf_detected))) { + + dtmf_rx_detected = + (struct vss_istream_evt_rx_dtmf_detected *) voc_pkt; + pr_debug("RX_DTMF_DETECTED low_freq=%d high_freq=%d\n", + dtmf_rx_detected->low_freq, + dtmf_rx_detected->high_freq); + if (c->dtmf_info.dtmf_rx_ul_cb) + c->dtmf_info.dtmf_rx_ul_cb((uint8_t *)voc_pkt, + voc_get_session_name(v->session_id), + c->dtmf_info.private_data); + } else { + pr_err("Invalid packet\n"); + } + } else + pr_debug("Unknown opcode 0x%x\n", data->opcode); + +fail: + return 0; +} + +static int32_t qdsp_cvp_callback(struct apr_client_data *data, void *priv) +{ + uint32_t *ptr = NULL; + struct common_data *c = NULL; + struct voice_data *v = NULL; + int i = 0; + + if ((data == NULL) || (priv == NULL)) { + pr_err("%s: data or priv is NULL\n", __func__); + return -EINVAL; + } + + c = priv; + + if (data->opcode == RESET_EVENTS) { + pr_debug("%s: Reset event received in Voice service\n", + __func__); + + apr_reset(c->apr_q6_cvp); + c->apr_q6_cvp = NULL; + cal_utils_clear_cal_block_q6maps(MAX_VOICE_CAL_TYPES, + common.cal_data); + + /* Sub-system restart is applicable to all sessions. */ + for (i = 0; i < MAX_VOC_SESSIONS; i++) + c->voice[i].cvp_handle = 0; + + /* + * Free the ION memory and clear handles for + * Source Tracking + */ + if (is_source_tracking_shared_memomry_allocated()) { + msm_audio_ion_free( + common.source_tracking_sh_mem.sh_mem_block.dma_buf); + common.source_tracking_sh_mem.mem_handle = 0; + common.source_tracking_sh_mem.sh_mem_block.dma_buf = + NULL; + } + voc_set_error_state(data->reset_proc); + return 0; + } + + v = voice_get_session_by_idx(data->dest_port); + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + + return -EINVAL; + } + + if (data->opcode == APR_BASIC_RSP_RESULT) { + if (data->payload_size >= (2 * sizeof(uint32_t))) { + ptr = data->payload; + + pr_debug("%x %x\n", ptr[0], ptr[1]); + if (ptr[1] != 0) { + pr_err("%s: cmd = 0x%x returned error = 0x%x\n", + __func__, ptr[0], ptr[1]); + } + switch (ptr[0]) { + case VSS_IVOCPROC_CMD_CREATE_FULL_CONTROL_SESSION_V2: + case VSS_IVOCPROC_CMD_CREATE_FULL_CONTROL_SESSION_V3: + /*response from CVP */ + pr_debug("%s: cmd = 0x%x\n", __func__, ptr[0]); + if (!ptr[1]) { + voice_set_cvp_handle(v, data->src_port); + pr_debug("status: %d, cvphdl=%d\n", + ptr[1], data->src_port); + } else + pr_err("got NACK from CVP create session response\n"); + v->cvp_state = CMD_STATUS_SUCCESS; + v->async_err = ptr[1]; + wake_up(&v->cvp_wait); + break; + case VSS_IVOCPROC_CMD_SET_DEVICE_V2: + case VSS_IVOCPROC_CMD_SET_DEVICE_V3: + case VSS_IVOLUME_CMD_SET_STEP: + case VSS_IVOCPROC_CMD_ENABLE: + case VSS_IVOCPROC_CMD_DISABLE: + case APRV2_IBASIC_CMD_DESTROY_SESSION: + case VSS_IVOCPROC_CMD_REGISTER_VOL_CALIBRATION_DATA: + case VSS_IVOCPROC_CMD_DEREGISTER_VOL_CALIBRATION_DATA: + case VSS_IVOCPROC_CMD_REGISTER_CALIBRATION_DATA_V2: + case VSS_IVOCPROC_CMD_DEREGISTER_CALIBRATION_DATA: + case VSS_IVOCPROC_CMD_REGISTER_DYNAMIC_CALIBRATION_DATA: + case VSS_IVOCPROC_CMD_DEREGISTER_DYNAMIC_CALIBRATION_DATA: + case VSS_IVOCPROC_CMD_REGISTER_STATIC_CALIBRATION_DATA: + case VSS_IVOCPROC_CMD_DEREGISTER_STATIC_CALIBRATION_DATA: + case VSS_IVOCPROC_CMD_REGISTER_DEVICE_CONFIG: + case VSS_IVOCPROC_CMD_DEREGISTER_DEVICE_CONFIG: + case VSS_ICOMMON_CMD_MAP_MEMORY: + case VSS_ICOMMON_CMD_UNMAP_MEMORY: + case VSS_IVOLUME_CMD_MUTE_V2: + case VSS_IVPCM_CMD_START_V2: + case VSS_IVPCM_CMD_STOP: + case VSS_IVOCPROC_CMD_TOPOLOGY_SET_DEV_CHANNELS: + case VSS_IVOCPROC_CMD_TOPOLOGY_COMMIT: + v->cvp_state = CMD_STATUS_SUCCESS; + v->async_err = ptr[1]; + wake_up(&v->cvp_wait); + break; + case VSS_IVPCM_EVT_PUSH_BUFFER_V2: + break; + case VSS_ICOMMON_CMD_SET_PARAM_V2: + case VSS_ICOMMON_CMD_SET_PARAM_V3: + switch (data->token) { + case VOC_SET_MEDIA_FORMAT_PARAM_TOKEN: + case VOC_GENERIC_SET_PARAM_TOKEN: + pr_debug("%s: VSS_ICOMMON_CMD_SET_PARAM called by voice_send_cvp_media_format_cmd\n", + __func__); + v->cvp_state = CMD_STATUS_SUCCESS; + v->async_err = ptr[1]; + wake_up(&v->cvp_wait); + break; + case VOC_RTAC_SET_PARAM_TOKEN: + pr_debug("%s: VSS_ICOMMON_CMD_SET_PARAM called by rtac\n", + __func__); + rtac_make_voice_callback( + RTAC_CVP, ptr, + data->payload_size); + break; + default: + pr_debug("%s: invalid token for command VSS_ICOMMON_CMD_SET_PARAM: %d\n", + __func__, data->token); + break; + } + break; + case VSS_ICOMMON_CMD_GET_PARAM_V2: + case VSS_ICOMMON_CMD_GET_PARAM_V3: + pr_debug("%s: VSS_ICOMMON_CMD_GET_PARAM_V2\n", + __func__); + /* Should only come here if there is an APR */ + /* error or malformed APR packet. Otherwise */ + /* response will be returned as */ + /* VSS_ICOMMON_RSP_GET_PARAM */ + if (ptr[1] != 0) { + pr_err("%s: CVP get param error = %d, resuming\n", + __func__, ptr[1]); + rtac_make_voice_callback(RTAC_CVP, + data->payload, + data->payload_size); + } + break; + case VSS_ISOUNDFOCUS_CMD_SET_SECTORS: + if (!ptr[1]) + common.is_sound_focus_resp_success = + true; + else + common.is_sound_focus_resp_success = + false; + v->cvp_state = CMD_STATUS_SUCCESS; + v->async_err = ptr[1]; + wake_up(&v->cvp_wait); + break; + case VSS_ISOUNDFOCUS_CMD_GET_SECTORS: + /* + * Should only come here if there is an error + * response received from ADSP. Otherwise + * response will be returned as + * VSS_ISOUNDFOCUS_RSP_GET_SECTORS + */ + pr_err("%s: VSS_ISOUNDFOCUS_CMD_GET_SECTORS failed\n", + __func__); + + common.is_sound_focus_resp_success = false; + v->cvp_state = CMD_STATUS_SUCCESS; + v->async_err = ptr[1]; + wake_up(&v->cvp_wait); + break; + case VSS_ISOURCETRACK_CMD_GET_ACTIVITY: + if (!ptr[1]) { + /* Read data from shared memory */ + memcpy(&common.sourceTrackingResponse, + common.source_tracking_sh_mem. + sh_mem_block.data, + sizeof(struct + vss_isourcetrack_activity_data_t)); + common.is_source_tracking_resp_success = + true; + } else { + common.is_source_tracking_resp_success = + false; + pr_err("%s: Error received for source tracking params\n", + __func__); + } + v->cvp_state = CMD_STATUS_SUCCESS; + v->async_err = ptr[1]; + wake_up(&v->cvp_wait); + break; + default: + pr_debug("%s: not match cmd = 0x%x\n", + __func__, ptr[0]); + break; + } + } + } else if (data->opcode == VSS_ICOMMON_RSP_GET_PARAM || + data->opcode == VSS_ICOMMON_RSP_GET_PARAM_V3) { + pr_debug("%s: VSS_ICOMMON_RSP_GET_PARAM\n", __func__); + ptr = data->payload; + if (ptr[0] != 0) { + pr_err("%s: VSS_ICOMMON_RSP_GET_PARAM returned error = 0x%x\n", + __func__, ptr[0]); + } + rtac_make_voice_callback(RTAC_CVP, data->payload, + data->payload_size); + } else if (data->opcode == VSS_IVPCM_EVT_NOTIFY_V2) { + if ((data->payload != NULL) && data->payload_size == + sizeof(struct vss_ivpcm_evt_notify_v2_t) && + common.hostpcm_info.hostpcm_evt_cb != NULL) { + common.hostpcm_info.hostpcm_evt_cb(data->payload, + voc_get_session_name(v->session_id), + common.hostpcm_info.private_data); + } + } else if (data->opcode == VSS_ISOUNDFOCUS_RSP_GET_SECTORS) { + if (data->payload && (data->payload_size == + sizeof(struct vss_isoundfocus_rsp_get_sectors_t))) { + common.is_sound_focus_resp_success = true; + memcpy(&common.soundFocusResponse, + (struct vss_isoundfocus_rsp_get_sectors_t *) + data->payload, + sizeof(struct + vss_isoundfocus_rsp_get_sectors_t)); + } else { + common.is_sound_focus_resp_success = false; + pr_debug("%s: Invalid payload received from CVD\n", + __func__); + } + v->cvp_state = CMD_STATUS_SUCCESS; + wake_up(&v->cvp_wait); + } + return 0; +} + +static int voice_free_oob_shared_mem(void) +{ + int rc = 0; + int cnt = 0; + int bufcnt = NUM_OF_BUFFERS; + struct voice_data *v = voice_get_session( + common.voice[VOC_PATH_FULL].session_id); + + mutex_lock(&common.common_lock); + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + + rc = -EINVAL; + goto done; + } + + rc = msm_audio_ion_free(v->shmem_info.sh_buf.dma_buf); + v->shmem_info.sh_buf.dma_buf = NULL; + if (rc < 0) { + pr_err("%s: Error:%d freeing memory\n", __func__, rc); + + goto done; + } + + + while (cnt < bufcnt) { + v->shmem_info.sh_buf.buf[cnt].data = NULL; + v->shmem_info.sh_buf.buf[cnt].phys = 0; + cnt++; + } + + v->shmem_info.sh_buf.dma_buf = NULL; + +done: + mutex_unlock(&common.common_lock); + return rc; +} + +static int voice_alloc_oob_shared_mem(void) +{ + int cnt = 0; + int rc = 0; + size_t len; + void *mem_addr = NULL; + dma_addr_t phys; + int bufsz = BUFFER_BLOCK_SIZE; + int bufcnt = NUM_OF_BUFFERS; + struct voice_data *v = voice_get_session( + common.voice[VOC_PATH_FULL].session_id); + + mutex_lock(&common.common_lock); + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + + rc = -EINVAL; + goto done; + } + + rc = msm_audio_ion_alloc(&(v->shmem_info.sh_buf.dma_buf), + bufsz * bufcnt, + &phys, &len, + &mem_addr); + if (rc < 0) { + pr_err("%s: audio ION alloc failed, rc = %d\n", + __func__, rc); + + goto done; + } + + while (cnt < bufcnt) { + v->shmem_info.sh_buf.buf[cnt].data = mem_addr + (cnt * bufsz); + v->shmem_info.sh_buf.buf[cnt].phys = phys + (cnt * bufsz); + v->shmem_info.sh_buf.buf[cnt].size = bufsz; + cnt++; + } + + pr_debug("%s buf[0].data:[%pK], buf[0].phys:[%pK], &buf[0].phys:[%pK],\n", + __func__, + (void *)v->shmem_info.sh_buf.buf[0].data, + &v->shmem_info.sh_buf.buf[0].phys, + (void *)&v->shmem_info.sh_buf.buf[0].phys); + pr_debug("%s: buf[1].data:[%pK], buf[1].phys[%pK], &buf[1].phys[%pK]\n", + __func__, + (void *)v->shmem_info.sh_buf.buf[1].data, + &v->shmem_info.sh_buf.buf[1].phys, + (void *)&v->shmem_info.sh_buf.buf[1].phys); + + memset((void *)v->shmem_info.sh_buf.buf[0].data, 0, (bufsz * bufcnt)); + +done: + mutex_unlock(&common.common_lock); + return rc; +} + +static int voice_alloc_oob_mem_table(void) +{ + int rc = 0; + size_t len; + struct voice_data *v = voice_get_session( + common.voice[VOC_PATH_FULL].session_id); + + mutex_lock(&common.common_lock); + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + + rc = -EINVAL; + goto done; + } + + rc = msm_audio_ion_alloc(&(v->shmem_info.memtbl.dma_buf), + sizeof(struct vss_imemory_table_t), + &v->shmem_info.memtbl.phys, + &len, + &(v->shmem_info.memtbl.data)); + if (rc < 0) { + pr_err("%s: audio ION alloc failed, rc = %d\n", + __func__, rc); + + goto done; + } + + v->shmem_info.memtbl.size = sizeof(struct vss_imemory_table_t); + pr_debug("%s data[%pK]phys[%pK][%pK]\n", __func__, + (void *)v->shmem_info.memtbl.data, + &v->shmem_info.memtbl.phys, + (void *)&v->shmem_info.memtbl.phys); + +done: + mutex_unlock(&common.common_lock); + return rc; +} + +/** + * voc_send_cvp_start_vocpcm - + * command to start voice hpcm + * + * @session_id: voice session ID to send this command + * + * Returns 0 on success or error on failure + */ +int voc_send_cvp_start_vocpcm(uint32_t session_id, + struct vss_ivpcm_tap_point *vpcm_tp, + uint32_t no_of_tp) +{ + struct cvp_start_cmd cvp_start_cmd; + int ret = 0; + void *apr_cvp; + u16 cvp_handle; + struct voice_data *v = voice_get_session(session_id); + int i = 0; + + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + ret = -EINVAL; + goto done; + } + apr_cvp = common.apr_q6_cvp; + + if (!apr_cvp) { + pr_err("%s: apr_cvp is NULL.\n", __func__); + ret = -EINVAL; + goto done; + } + + cvp_handle = voice_get_cvp_handle(v); + + /* Fill the header */ + cvp_start_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + cvp_start_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(struct vss_ivpcm_tap_point) * no_of_tp) + + sizeof(cvp_start_cmd.vpcm_start_cmd.num_tap_points) + + sizeof(cvp_start_cmd.vpcm_start_cmd.mem_handle); + cvp_start_cmd.hdr.src_port = voice_get_idx_for_session(v->session_id); + cvp_start_cmd.hdr.dest_port = cvp_handle; + cvp_start_cmd.hdr.token = 0; + cvp_start_cmd.hdr.opcode = VSS_IVPCM_CMD_START_V2; + + for (i = 0; i < no_of_tp; i++) { + cvp_start_cmd.vpcm_start_cmd.tap_points[i].tap_point = + vpcm_tp[i].tap_point; + cvp_start_cmd.vpcm_start_cmd.tap_points[i].direction = + vpcm_tp[i].direction; + cvp_start_cmd.vpcm_start_cmd.tap_points[i].sampling_rate = + vpcm_tp[i].sampling_rate; + cvp_start_cmd.vpcm_start_cmd.tap_points[i].duration = 0; + } + + cvp_start_cmd.vpcm_start_cmd.mem_handle = + common.voice_host_pcm_mem_handle; + cvp_start_cmd.vpcm_start_cmd.num_tap_points = no_of_tp; + + v->cvp_state = CMD_STATUS_FAIL; + v->async_err = 0; + ret = apr_send_pkt(apr_cvp, (uint32_t *) &cvp_start_cmd); + if (ret < 0) { + pr_err("%s: Fail: sending vocpcm map memory,\n", __func__); + goto done; + } + ret = wait_event_timeout(v->cvp_wait, + (v->cvp_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + goto done; + } + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + v->async_err)); + ret = adsp_err_get_lnx_err_code( + v->async_err); + goto done; + } + +done: + return ret; +} +EXPORT_SYMBOL(voc_send_cvp_start_vocpcm); + +/** + * voc_send_cvp_stop_vocpcm - + * command to stop voice hpcm + * + * @session_id: voice session ID to send this command + * + * Returns 0 on success or error on failure + */ +int voc_send_cvp_stop_vocpcm(uint32_t session_id) +{ + struct cvp_command vpcm_stop_cmd; + int ret = 0; + void *apr_cvp; + u16 cvp_handle; + struct voice_data *v = voice_get_session(session_id); + + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + ret = -EINVAL; + goto done; + } + apr_cvp = common.apr_q6_cvp; + + if (!apr_cvp) { + pr_err("%s: apr_cvp is NULL.\n", __func__); + ret = -EINVAL; + goto done; + } + + cvp_handle = voice_get_cvp_handle(v); + + /* fill in the header */ + vpcm_stop_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + vpcm_stop_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(vpcm_stop_cmd) - APR_HDR_SIZE); + vpcm_stop_cmd.hdr.src_port = voice_get_idx_for_session(v->session_id); + vpcm_stop_cmd.hdr.dest_port = cvp_handle; + vpcm_stop_cmd.hdr.token = 0; + vpcm_stop_cmd.hdr.opcode = VSS_IVPCM_CMD_STOP; + + v->cvp_state = CMD_STATUS_FAIL; + v->async_err = 0; + ret = apr_send_pkt(apr_cvp, (uint32_t *) &vpcm_stop_cmd); + if (ret < 0) { + pr_err("Fail: sending vocpcm stop,\n"); + goto done; + } + ret = wait_event_timeout(v->cvp_wait, + (v->cvp_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + goto done; + } + + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + v->async_err)); + ret = adsp_err_get_lnx_err_code( + v->async_err); + goto done; + } + +done: + return ret; +} +EXPORT_SYMBOL(voc_send_cvp_stop_vocpcm); + +/** + * voc_send_cvp_map_vocpcm_memory - + * command to map memory for voice hpcm + * + * @session_id: voice session ID to send this command + * @tp_mem_table: tap point memory table of hpcm + * paddr: Physical address of hpcm memory mapped area. + * bufsize: Buffer size of memory mapped area + * + * Returns 0 on success or error on failure + */ +int voc_send_cvp_map_vocpcm_memory(uint32_t session_id, + struct mem_map_table *tp_mem_table, + phys_addr_t paddr, uint32_t bufsize) +{ + return voice_map_memory_physical_cmd(voice_get_session(session_id), + tp_mem_table, + (dma_addr_t) paddr, bufsize, + VOC_VOICE_HOST_PCM_MAP_TOKEN); +} +EXPORT_SYMBOL(voc_send_cvp_map_vocpcm_memory); + +/** + * voc_send_cvp_unmap_vocpcm_memory - + * command to unmap memory for voice hpcm + * + * @session_id: voice session ID to send this command + * + * Returns 0 on success or error on failure + */ +int voc_send_cvp_unmap_vocpcm_memory(uint32_t session_id) +{ + int ret = 0; + + ret = voice_send_mvm_unmap_memory_physical_cmd( + voice_get_session(session_id), + common.voice_host_pcm_mem_handle); + + if (ret == 0) + common.voice_host_pcm_mem_handle = 0; + + return ret; +} +EXPORT_SYMBOL(voc_send_cvp_unmap_vocpcm_memory); + +/** + * voc_send_cvp_vocpcm_push_buf_evt - Send buf event command + * + * @session_id: voice session ID to send this command + * @push_buff_evt: pointer with buffer event details + * + * Returns 0 on success or error on failure + */ +int voc_send_cvp_vocpcm_push_buf_evt(uint32_t session_id, + struct vss_ivpcm_evt_push_buffer_v2_t *push_buff_evt) +{ + struct cvp_push_buf_cmd vpcm_push_buf_cmd; + int ret = 0; + void *apr_cvp; + u16 cvp_handle; + struct voice_data *v = voice_get_session(session_id); + + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + ret = -EINVAL; + goto done; + } + apr_cvp = common.apr_q6_cvp; + + if (!apr_cvp) { + pr_err("%s: apr_cvp is NULL.\n", __func__); + ret = -EINVAL; + goto done; + } + + memset(&vpcm_push_buf_cmd, 0, sizeof(vpcm_push_buf_cmd)); + cvp_handle = voice_get_cvp_handle(v); + + /* fill in the header */ + vpcm_push_buf_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + vpcm_push_buf_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(vpcm_push_buf_cmd) - APR_HDR_SIZE); + vpcm_push_buf_cmd.hdr.src_port = + voice_get_idx_for_session(v->session_id); + vpcm_push_buf_cmd.hdr.dest_port = cvp_handle; + vpcm_push_buf_cmd.hdr.token = 0; + vpcm_push_buf_cmd.hdr.opcode = VSS_IVPCM_EVT_PUSH_BUFFER_V2; + + vpcm_push_buf_cmd.vpcm_evt_push_buffer.tap_point = + push_buff_evt->tap_point; + vpcm_push_buf_cmd.vpcm_evt_push_buffer.push_buf_mask = + push_buff_evt->push_buf_mask; + vpcm_push_buf_cmd.vpcm_evt_push_buffer.out_buf_mem_address = + push_buff_evt->out_buf_mem_address; + vpcm_push_buf_cmd.vpcm_evt_push_buffer.in_buf_mem_address = + push_buff_evt->in_buf_mem_address; + vpcm_push_buf_cmd.vpcm_evt_push_buffer.out_buf_mem_size = + push_buff_evt->out_buf_mem_size; + vpcm_push_buf_cmd.vpcm_evt_push_buffer.in_buf_mem_size = + push_buff_evt->in_buf_mem_size; + vpcm_push_buf_cmd.vpcm_evt_push_buffer.sampling_rate = + push_buff_evt->sampling_rate; + vpcm_push_buf_cmd.vpcm_evt_push_buffer.num_in_channels = + push_buff_evt->num_in_channels; + + ret = apr_send_pkt(apr_cvp, (uint32_t *) &vpcm_push_buf_cmd); + if (ret < 0) { + pr_err("Fail: sending vocpcm map memory,\n"); + goto done; + } + +done: + return ret; +} +EXPORT_SYMBOL(voc_send_cvp_vocpcm_push_buf_evt); + +/** + * voc_register_hpcm_evt_cb - Updates hostpcm info. + * + * @hostpcm_cb: callback function for hostpcm event + * @private_data: private data for hostpcm + * + */ +void voc_register_hpcm_evt_cb(hostpcm_cb_fn hostpcm_cb, + void *private_data) +{ + common.hostpcm_info.hostpcm_evt_cb = hostpcm_cb; + common.hostpcm_info.private_data = private_data; +} +EXPORT_SYMBOL(voc_register_hpcm_evt_cb); + +/** + * voc_deregister_hpcm_evt_cb - resets hostpcm info. + * + */ +void voc_deregister_hpcm_evt_cb(void) +{ + common.hostpcm_info.hostpcm_evt_cb = NULL; + common.hostpcm_info.private_data = NULL; +} +EXPORT_SYMBOL(voc_deregister_hpcm_evt_cb); + +/** + * voc_get_cvd_version - retrieve CVD version. + * + * @cvd_version: pointer to be updated with CVD version info. + * + * Returns 0 on success or error on failure + */ +int voc_get_cvd_version(char *cvd_version) +{ + int ret = 0; + struct voice_data *v = voice_get_session(VOICE_SESSION_VSID); + + + if (v == NULL) { + pr_err("%s: invalid session_id 0x%x\n", + __func__, VOICE_SESSION_VSID); + + ret = -EINVAL; + goto done; + } + + if (is_cvd_version_queried()) { + pr_debug("%s: Returning the cached value %s\n", + __func__, common.cvd_version); + + goto done; + } + + /* Register callback to APR */ + ret = voice_apr_register(VOICE_SESSION_VSID); + if (ret < 0) { + pr_err("%s: apr register failed\n", __func__); + goto done; + } + + mutex_lock(&common.common_lock); + mutex_lock(&v->lock); + ret = voice_send_mvm_cvd_version_cmd(v); + if (ret < 0) { + pr_err("%s: voice_send_mvm_cvd_version_cmd failed\n", __func__); + goto unlock; + } + ret = 0; + +unlock: + mutex_unlock(&v->lock); + mutex_unlock(&common.common_lock); + +done: + if (cvd_version) + memcpy(cvd_version, common.cvd_version, + CVD_VERSION_STRING_MAX_SIZE); + + return ret; +} +EXPORT_SYMBOL(voc_get_cvd_version); + +static int voice_alloc_cal_mem_map_table(void) +{ + int ret = 0; + size_t len; + + ret = msm_audio_ion_alloc(&(common.cal_mem_map_table.dma_buf), + sizeof(struct vss_imemory_table_t), + &common.cal_mem_map_table.phys, + &len, + &(common.cal_mem_map_table.data)); + if ((ret < 0) && (ret != -EPROBE_DEFER)) { + pr_err("%s: audio ION alloc failed, rc = %d\n", + __func__, ret); + goto done; + } + + common.cal_mem_map_table.size = sizeof(struct vss_imemory_table_t); + pr_debug("%s: data %pK phys %pK\n", __func__, + common.cal_mem_map_table.data, + &common.cal_mem_map_table.phys); + +done: + return ret; +} + +static int voice_alloc_rtac_mem_map_table(void) +{ + int ret = 0; + size_t len; + + ret = msm_audio_ion_alloc( + &(common.rtac_mem_map_table.dma_buf), + sizeof(struct vss_imemory_table_t), + &common.rtac_mem_map_table.phys, + &len, + &(common.rtac_mem_map_table.data)); + if (ret < 0) { + pr_err("%s: audio ION alloc failed, rc = %d\n", + __func__, ret); + goto done; + } + + common.rtac_mem_map_table.size = sizeof(struct vss_imemory_table_t); + pr_debug("%s: data %pK phys %pK\n", __func__, + common.rtac_mem_map_table.data, + &common.rtac_mem_map_table.phys); + +done: + return ret; +} + +static int voice_alloc_and_map_oob_mem(struct voice_data *v) +{ + int ret = 0; + + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + + return -EINVAL; + } + + if (!is_voip_memory_allocated()) { + ret = voc_alloc_voip_shared_memory(); + if (ret < 0) { + pr_err("%s: Failed to create voip oob memory %d\n", + __func__, ret); + + goto done; + } + } + + ret = voice_map_memory_physical_cmd(v, + &v->shmem_info.memtbl, + v->shmem_info.sh_buf.buf[0].phys, + v->shmem_info.sh_buf.buf[0].size * NUM_OF_BUFFERS, + VOIP_MEM_MAP_TOKEN); + if (ret) { + pr_err("%s: mvm_map_memory_phy failed %d\n", + __func__, ret); + + goto done; + } + +done: + return ret; +} + +uint32_t voice_get_topology(uint32_t topology_idx) +{ + uint32_t topology = VSS_IVOCPROC_TOPOLOGY_ID_RX_DEFAULT; + struct cal_block_data *cal_block = NULL; + + /* initialize as default topology */ + if (topology_idx == CVP_VOC_RX_TOPOLOGY_CAL) { + topology = VSS_IVOCPROC_TOPOLOGY_ID_RX_DEFAULT; + } else if (topology_idx == CVP_VOC_TX_TOPOLOGY_CAL) { + topology = VSS_IVOCPROC_TOPOLOGY_ID_NONE; + } else { + pr_err("%s: cal index %x is invalid!\n", + __func__, topology_idx); + + goto done; + } + + if (common.cal_data[topology_idx] == NULL) { + pr_err("%s: cal type is NULL for cal index %x\n", + __func__, topology_idx); + + goto done; + } + + mutex_lock(&common.cal_data[topology_idx]->lock); + cal_block = cal_utils_get_only_cal_block( + common.cal_data[topology_idx]); + if (cal_block == NULL) { + pr_debug("%s: cal_block not found for cal index %x\n", + __func__, topology_idx); + + goto unlock; + } + + topology = ((struct audio_cal_info_voc_top *) + cal_block->cal_info)->topology; +unlock: + mutex_unlock(&common.cal_data[topology_idx]->lock); +done: + pr_debug("%s: Using topology %d\n", __func__, topology); + + return topology; +} + +int voice_set_topology_specific_info(struct voice_data *v, + uint32_t topology_idx) +{ + struct cal_block_data *cal_block = NULL; + int ret = 0; + uint32_t topo_channels; + + if (common.cal_data[topology_idx] == NULL) { + pr_err("%s: cal type is NULL for cal index %x\n", + __func__, topology_idx); + ret = -EINVAL; + goto done; + } + + mutex_lock(&common.cal_data[topology_idx]->lock); + cal_block = cal_utils_get_only_cal_block( + common.cal_data[topology_idx]); + if (cal_block == NULL) { + pr_debug("%s: cal_block not found for cal index %x\n", + __func__, topology_idx); + ret = -EINVAL; + goto unlock; + } + + if (topology_idx == CVP_VOC_RX_TOPOLOGY_CAL) { + topo_channels = ((struct audio_cal_info_voc_top *) + cal_block->cal_info)->num_channels; + if (topo_channels > 0) { + v->dev_rx.no_of_channels = topo_channels; + pr_debug("%s: Topology Rx no of channels: %d", + __func__, v->dev_rx.no_of_channels); + memcpy(&v->dev_rx.channel_mapping, + &((struct audio_cal_info_voc_top *) + cal_block->cal_info)->channel_mapping, + VSS_CHANNEL_MAPPING_SIZE); + } else { + pr_debug("%s: cal data is zero, default to Rx backend config\n", + __func__); + if (v->dev_rx.no_of_channels == NUM_CHANNELS_MONO) { + v->dev_rx.channel_mapping[0] = PCM_CHANNEL_FC; + } else if (v->dev_rx.no_of_channels == + NUM_CHANNELS_STEREO) { + v->dev_rx.channel_mapping[0] = PCM_CHANNEL_FL; + v->dev_rx.channel_mapping[1] = PCM_CHANNEL_FR; + } else { + pr_warn("%s: Unsupported Rx num channels: %d\n", + __func__, v->dev_rx.no_of_channels); + } + } + } else if (topology_idx == CVP_VOC_TX_TOPOLOGY_CAL) { + topo_channels = ((struct audio_cal_info_voc_top *) + cal_block->cal_info)->num_channels; + if (topo_channels > 0) { + v->dev_tx.no_of_channels = topo_channels; + pr_debug("%s: Topology Tx no of channels: %d", + __func__, v->dev_tx.no_of_channels); + memcpy(&v->dev_tx.channel_mapping, + &((struct audio_cal_info_voc_top *) + cal_block->cal_info)->channel_mapping, + VSS_CHANNEL_MAPPING_SIZE); + } else { + pr_debug("%s: cal data is zero, default to Tx backend config\n", + __func__); + if (v->dev_tx.no_of_channels == NUM_CHANNELS_MONO) { + v->dev_tx.channel_mapping[0] = PCM_CHANNEL_FC; + } else if (v->dev_tx.no_of_channels == + NUM_CHANNELS_STEREO) { + v->dev_tx.channel_mapping[0] = PCM_CHANNEL_FL; + v->dev_tx.channel_mapping[1] = PCM_CHANNEL_FR; + } else if (v->dev_tx.no_of_channels == + NUM_CHANNELS_THREE) { + v->dev_tx.channel_mapping[0] = PCM_CHANNEL_FL; + v->dev_tx.channel_mapping[1] = PCM_CHANNEL_FR; + v->dev_tx.channel_mapping[2] = PCM_CHANNEL_FC; + } else if (v->dev_tx.no_of_channels == + NUM_CHANNELS_QUAD) { + v->dev_tx.channel_mapping[0] = PCM_CHANNEL_FL; + v->dev_tx.channel_mapping[1] = PCM_CHANNEL_FR; + v->dev_tx.channel_mapping[2] = PCM_CHANNEL_LS; + v->dev_tx.channel_mapping[3] = PCM_CHANNEL_RS; + } else { + pr_warn("%s: Unsupported Tx num channels: %d\n", + __func__, v->dev_tx.no_of_channels); + } + } + } else { + pr_err("%s: topology index %x is invalid\n", + __func__, topology_idx); + } +unlock: + mutex_unlock(&common.cal_data[topology_idx]->lock); +done: + return ret; +} + +static int get_cal_type_index(int32_t cal_type) +{ + int ret = -EINVAL; + + switch (cal_type) { + case CVP_VOC_RX_TOPOLOGY_CAL_TYPE: + ret = CVP_VOC_RX_TOPOLOGY_CAL; + break; + case CVP_VOC_TX_TOPOLOGY_CAL_TYPE: + ret = CVP_VOC_TX_TOPOLOGY_CAL; + break; + case CVP_VOCPROC_STATIC_CAL_TYPE: + ret = CVP_VOCPROC_CAL; + break; + case CVP_VOCPROC_DYNAMIC_CAL_TYPE: + ret = CVP_VOCVOL_CAL; + break; + case CVS_VOCSTRM_STATIC_CAL_TYPE: + ret = CVS_VOCSTRM_CAL; + break; + case CVP_VOCDEV_CFG_CAL_TYPE: + ret = CVP_VOCDEV_CFG_CAL; + break; + case CVP_VOCPROC_STATIC_COL_CAL_TYPE: + ret = CVP_VOCPROC_COL_CAL; + break; + case CVP_VOCPROC_DYNAMIC_COL_CAL_TYPE: + ret = CVP_VOCVOL_COL_CAL; + break; + case CVS_VOCSTRM_STATIC_COL_CAL_TYPE: + ret = CVS_VOCSTRM_COL_CAL; + break; + case VOICE_RTAC_INFO_CAL_TYPE: + ret = VOICE_RTAC_INFO_CAL; + break; + case VOICE_RTAC_APR_CAL_TYPE: + ret = VOICE_RTAC_APR_CAL; + break; + default: + pr_err("%s: Invalid cal type %d!\n", __func__, cal_type); + } + return ret; +} + +static int voice_prepare_volume_boost(int32_t cal_type, + size_t data_size, void *data) +{ + return voc_deregister_vocproc_vol_table(); +} + +static int voice_enable_volume_boost(int32_t cal_type, + size_t data_size, void *data) +{ + return voc_register_vocproc_vol_table(); +} + +static int voice_alloc_cal(int32_t cal_type, + size_t data_size, void *data) +{ + int ret = 0; + int cal_index; + int cal_version; + + pr_debug("%s\n", __func__); + + cal_version = cal_utils_get_cal_type_version(data); + common.is_per_vocoder_cal_enabled = + !!(cal_version & PER_VOCODER_CAL_BIT_MASK); + + cal_index = get_cal_type_index(cal_type); + if (cal_index < 0) { + pr_err("%s: Could not get cal index %d!\n", + __func__, cal_index); + ret = -EINVAL; + goto done; + } + + ret = cal_utils_alloc_cal(data_size, data, + common.cal_data[cal_index], 0, NULL); + if (ret < 0) { + pr_err("%s: Cal_utils_alloc_block failed, ret = %d, cal type = %d!\n", + __func__, ret, cal_type); + ret = -EINVAL; + goto done; + } +done: + return ret; +} + +static int voice_dealloc_cal(int32_t cal_type, + size_t data_size, void *data) +{ + int ret = 0; + int cal_index; + + pr_debug("%s\n", __func__); + + cal_index = get_cal_type_index(cal_type); + if (cal_index < 0) { + pr_err("%s: Could not get cal index %d!\n", + __func__, cal_index); + + ret = -EINVAL; + goto done; + } + + ret = cal_utils_dealloc_cal(data_size, data, + common.cal_data[cal_index]); + if (ret < 0) { + pr_err("%s: Cal_utils_dealloc_block failed, ret = %d, cal type = %d!\n", + __func__, ret, cal_type); + + ret = -EINVAL; + goto done; + } +done: + return ret; +} + +static int voice_set_cal(int32_t cal_type, + size_t data_size, void *data) +{ + int ret = 0; + int cal_index; + + pr_debug("%s\n", __func__); + + cal_index = get_cal_type_index(cal_type); + if (cal_index < 0) { + pr_err("%s: Could not get cal index %d!\n", + __func__, cal_index); + + ret = -EINVAL; + goto done; + } + + ret = cal_utils_set_cal(data_size, data, + common.cal_data[cal_index], 0, NULL); + if (ret < 0) { + pr_err("%s: Cal_utils_set_cal failed, ret = %d, cal type = %d!\n", + __func__, ret, cal_type); + + ret = -EINVAL; + goto done; + } + /* Pre-load if it is voice Rx or Tx topology */ + if ((cal_index == CVP_VOC_RX_TOPOLOGY_CAL) || + (cal_index == CVP_VOC_TX_TOPOLOGY_CAL)) { + voice_load_topo_modules(cal_index); + } +done: + return ret; +} + +static void voice_delete_cal_data(void) +{ + pr_debug("%s\n", __func__); + + cal_utils_destroy_cal_types(MAX_VOICE_CAL_TYPES, common.cal_data); +} + +static int voice_init_cal_data(void) +{ + int ret = 0; + struct cal_type_info cal_type_info[] = { + {{CVP_VOC_RX_TOPOLOGY_CAL_TYPE, + {NULL, NULL, NULL, voice_set_cal, NULL, NULL} }, + {NULL, NULL, cal_utils_match_buf_num} }, + + {{CVP_VOC_TX_TOPOLOGY_CAL_TYPE, + {NULL, NULL, NULL, voice_set_cal, NULL, NULL} }, + {NULL, NULL, cal_utils_match_buf_num} }, + + {{CVP_VOCPROC_STATIC_CAL_TYPE, + {voice_alloc_cal, voice_dealloc_cal, NULL, + voice_set_cal, NULL, NULL} }, + {NULL, voice_unmap_cal_memory, + cal_utils_match_buf_num} }, + + {{CVP_VOCPROC_DYNAMIC_CAL_TYPE, + {voice_alloc_cal, voice_dealloc_cal, + voice_prepare_volume_boost, + voice_set_cal, NULL, + voice_enable_volume_boost} }, + {NULL, voice_unmap_cal_memory, + cal_utils_match_buf_num} }, + + {{CVP_VOCDEV_CFG_CAL_TYPE, + {voice_alloc_cal, voice_dealloc_cal, NULL, + voice_set_cal, NULL, NULL} }, + {NULL, voice_unmap_cal_memory, + cal_utils_match_buf_num} }, + + {{CVP_VOCPROC_STATIC_COL_CAL_TYPE, + {NULL, NULL, NULL, voice_set_cal, NULL, NULL} }, + {NULL, NULL, cal_utils_match_buf_num} }, + + {{CVP_VOCPROC_DYNAMIC_COL_CAL_TYPE, + {NULL, NULL, NULL, voice_set_cal, NULL, NULL} }, + {NULL, NULL, cal_utils_match_buf_num} }, + + {{CVS_VOCSTRM_STATIC_CAL_TYPE, + {voice_alloc_cal, voice_dealloc_cal, NULL, + voice_set_cal, NULL, NULL} }, + {NULL, voice_unmap_cal_memory, + cal_utils_match_buf_num} }, + + {{CVS_VOCSTRM_STATIC_COL_CAL_TYPE, + {NULL, NULL, NULL, voice_set_cal, NULL, NULL} }, + {NULL, NULL, cal_utils_match_buf_num} }, + + {{VOICE_RTAC_INFO_CAL_TYPE, + {NULL, NULL, NULL, NULL, NULL, NULL} }, + {NULL, NULL, cal_utils_match_buf_num} }, + + {{VOICE_RTAC_APR_CAL_TYPE, + {NULL, NULL, NULL, NULL, NULL, NULL} }, + {NULL, NULL, cal_utils_match_buf_num} }, + }; + + ret = cal_utils_create_cal_types(MAX_VOICE_CAL_TYPES, common.cal_data, + cal_type_info); + if (ret < 0) { + pr_err("%s: Could not create cal type!\n", + __func__); + + ret = -EINVAL; + goto err; + } + + return ret; +err: + voice_delete_cal_data(); + memset(&common, 0, sizeof(struct common_data)); + return ret; +} + +static int voice_send_set_sound_focus_cmd(struct voice_data *v, + struct sound_focus_param soundFocusData) +{ + struct cvp_set_sound_focus_param_cmd_t cvp_set_sound_focus_param_cmd; + int ret = 0; + void *apr_cvp; + u16 cvp_handle; + int i; + + pr_debug("%s: Enter\n", __func__); + + if (v == NULL) { + pr_err("%s: v is NULL\n", __func__); + + ret = -EINVAL; + goto done; + } + apr_cvp = common.apr_q6_cvp; + + if (!apr_cvp) { + pr_err("%s: apr_cvp is NULL.\n", __func__); + + ret = -EINVAL; + goto done; + } + cvp_handle = voice_get_cvp_handle(v); + + /* send Sound Focus Params to cvp */ + cvp_set_sound_focus_param_cmd.hdr.hdr_field = + APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + cvp_set_sound_focus_param_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(cvp_set_sound_focus_param_cmd) - APR_HDR_SIZE); + cvp_set_sound_focus_param_cmd.hdr.src_port = + voice_get_idx_for_session(v->session_id); + cvp_set_sound_focus_param_cmd.hdr.dest_port = cvp_handle; + cvp_set_sound_focus_param_cmd.hdr.token = 0; + cvp_set_sound_focus_param_cmd.hdr.opcode = + VSS_ISOUNDFOCUS_CMD_SET_SECTORS; + + memset(&(cvp_set_sound_focus_param_cmd.cvp_set_sound_focus_param), 0xFF, + sizeof(struct vss_isoundfocus_cmd_set_sectors_t)); + for (i = 0; i < MAX_SECTORS; i++) { + cvp_set_sound_focus_param_cmd.cvp_set_sound_focus_param. + start_angles[i] = soundFocusData.start_angle[i]; + cvp_set_sound_focus_param_cmd.cvp_set_sound_focus_param. + enables[i] = soundFocusData.enable[i]; + pr_debug("%s: start_angle[%d] = %d\n", + __func__, i, soundFocusData.start_angle[i]); + pr_debug("%s: enable[%d] = %d\n", + __func__, i, soundFocusData.enable[i]); + } + cvp_set_sound_focus_param_cmd.cvp_set_sound_focus_param.gain_step = + soundFocusData.gain_step; + pr_debug("%s: gain_step = %d\n", __func__, soundFocusData.gain_step); + + v->cvp_state = CMD_STATUS_FAIL; + v->async_err = 0; + + ret = apr_send_pkt(apr_cvp, (uint32_t *)&cvp_set_sound_focus_param_cmd); + if (ret < 0) { + pr_err("%s: Error in sending APR command\n", __func__); + + ret = -EINVAL; + goto done; + } + ret = wait_event_timeout(v->cvp_wait, + (v->cvp_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + + ret = -EINVAL; + goto done; + } + + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + v->async_err)); + ret = adsp_err_get_lnx_err_code( + v->async_err); + goto done; + } + + if (common.is_sound_focus_resp_success) { + ret = 0; + } else { + pr_err("%s: Error in setting sound focus params\n", __func__); + + ret = -EINVAL; + } + +done: + pr_debug("%s: Exit, ret=%d\n", __func__, ret); + + return ret; +} + +/** + * voc_set_sound_focus - sends sound focus data. + * + * @soundFocusData: sound focus data. + * + * Returns 0 on success or error on failure + */ +int voc_set_sound_focus(struct sound_focus_param soundFocusData) +{ + struct voice_data *v = NULL; + int ret = -EINVAL; + struct voice_session_itr itr; + + pr_debug("%s: Enter\n", __func__); + + mutex_lock(&common.common_lock); + voice_itr_init(&itr, ALL_SESSION_VSID); + while (voice_itr_get_next_session(&itr, &v)) { + if (v != NULL) { + mutex_lock(&v->lock); + if (is_voc_state_active(v->voc_state) && + (v->lch_mode != VOICE_LCH_START) && + !v->disable_topology) + ret = voice_send_set_sound_focus_cmd(v, + soundFocusData); + mutex_unlock(&v->lock); + } else { + pr_err("%s: invalid session\n", __func__); + + ret = -EINVAL; + break; + } + } + mutex_unlock(&common.common_lock); + pr_debug("%s: Exit, ret=%d\n", __func__, ret); + + return ret; +} +EXPORT_SYMBOL(voc_set_sound_focus); + +static int voice_send_get_sound_focus_cmd(struct voice_data *v, + struct sound_focus_param *soundFocusData) +{ + struct apr_hdr cvp_get_sound_focus_param_cmd; + int ret = 0; + void *apr_cvp; + u16 cvp_handle; + int i; + + pr_debug("%s: Enter\n", __func__); + + if (!v) { + pr_err("%s: v is NULL\n", __func__); + + ret = -EINVAL; + goto done; + } + apr_cvp = common.apr_q6_cvp; + + if (!apr_cvp) { + pr_err("%s: apr_cvp is NULL\n", __func__); + + ret = -EINVAL; + goto done; + } + + cvp_handle = voice_get_cvp_handle(v); + + /* send APR command to retrieve Sound Focus Params */ + cvp_get_sound_focus_param_cmd.hdr_field = + APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + cvp_get_sound_focus_param_cmd.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(cvp_get_sound_focus_param_cmd) - APR_HDR_SIZE); + cvp_get_sound_focus_param_cmd.src_port = + voice_get_idx_for_session(v->session_id); + cvp_get_sound_focus_param_cmd.dest_port = cvp_handle; + cvp_get_sound_focus_param_cmd.token = 0; + cvp_get_sound_focus_param_cmd.opcode = VSS_ISOUNDFOCUS_CMD_GET_SECTORS; + + v->cvp_state = CMD_STATUS_FAIL; + v->async_err = 0; + ret = apr_send_pkt(apr_cvp, (uint32_t *)&cvp_get_sound_focus_param_cmd); + if (ret < 0) { + pr_err("%s: Error in sending APR command\n", __func__); + + ret = -EINVAL; + goto done; + } + ret = wait_event_timeout(v->cvp_wait, + (v->cvp_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + + ret = -EINVAL; + goto done; + } + + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + v->async_err)); + ret = adsp_err_get_lnx_err_code( + v->async_err); + goto done; + } + + if (common.is_sound_focus_resp_success) { + for (i = 0; i < MAX_SECTORS; i++) { + soundFocusData->start_angle[i] = + common.soundFocusResponse.start_angles[i]; + soundFocusData->enable[i] = + common.soundFocusResponse.enables[i]; + pr_debug("%s: start_angle[%d] = %d\n", + __func__, i, soundFocusData->start_angle[i]); + pr_debug("%s: enable[%d] = %d\n", + __func__, i, soundFocusData->enable[i]); + } + soundFocusData->gain_step = common.soundFocusResponse.gain_step; + pr_debug("%s: gain_step = %d\n", __func__, + soundFocusData->gain_step); + + common.is_sound_focus_resp_success = false; + ret = 0; + } else { + pr_err("%s: Invalid payload received from CVD\n", __func__); + + ret = -EINVAL; + } +done: + pr_debug("%s: Exit, ret=%d\n", __func__, ret); + + return ret; +} + +/** + * voc_get_sound_focus - retrieves sound focus data. + * + * @soundFocusData: pointer to be updated with sound focus data. + * + * Returns 0 on success or error on failure + */ +int voc_get_sound_focus(struct sound_focus_param *soundFocusData) +{ + struct voice_data *v = NULL; + int ret = -EINVAL; + struct voice_session_itr itr; + + pr_debug("%s: Enter\n", __func__); + + mutex_lock(&common.common_lock); + voice_itr_init(&itr, ALL_SESSION_VSID); + while (voice_itr_get_next_session(&itr, &v)) { + if (v) { + mutex_lock(&v->lock); + if (is_voc_state_active(v->voc_state) && + (v->lch_mode != VOICE_LCH_START) && + !v->disable_topology) + ret = voice_send_get_sound_focus_cmd(v, + soundFocusData); + mutex_unlock(&v->lock); + } else { + pr_err("%s: invalid session\n", __func__); + + ret = -EINVAL; + break; + } + } + mutex_unlock(&common.common_lock); + pr_debug("%s: Exit, ret=%d\n", __func__, ret); + + return ret; +} +EXPORT_SYMBOL(voc_get_sound_focus); + +static int is_source_tracking_shared_memomry_allocated(void) +{ + bool ret; + + pr_debug("%s: Enter\n", __func__); + + if (common.source_tracking_sh_mem.sh_mem_block.dma_buf != NULL) + ret = true; + else + ret = false; + + pr_debug("%s: Exit\n", __func__); + + return ret; +} + +static int voice_alloc_source_tracking_shared_memory(void) +{ + int ret = 0; + + pr_debug("%s: Enter\n", __func__); + + ret = msm_audio_ion_alloc( + &(common.source_tracking_sh_mem.sh_mem_block.dma_buf), + BUFFER_BLOCK_SIZE, + &(common.source_tracking_sh_mem.sh_mem_block.phys), + (size_t *)&(common.source_tracking_sh_mem.sh_mem_block.size), + &(common.source_tracking_sh_mem.sh_mem_block.data)); + if (ret < 0) { + pr_err("%s: audio ION alloc failed for sh_mem block, ret = %d\n", + __func__, ret); + + ret = -EINVAL; + goto done; + } + memset((void *)(common.source_tracking_sh_mem.sh_mem_block.data), 0, + common.source_tracking_sh_mem.sh_mem_block.size); + + pr_debug("%s: sh_mem_block: phys:[%pK], data:[0x%pK], size:[%zd]\n", + __func__, + &(common.source_tracking_sh_mem.sh_mem_block.phys), + (void *)(common.source_tracking_sh_mem.sh_mem_block.data), + (size_t)(common.source_tracking_sh_mem.sh_mem_block.size)); + + ret = msm_audio_ion_alloc( + &(common.source_tracking_sh_mem.sh_mem_table.dma_buf), + sizeof(struct vss_imemory_table_t), + &(common.source_tracking_sh_mem.sh_mem_table.phys), + (size_t *)&(common.source_tracking_sh_mem.sh_mem_table.size), + &(common.source_tracking_sh_mem.sh_mem_table.data)); + if (ret < 0) { + pr_err("%s: audio ION alloc failed for sh_mem table, ret = %d\n", + __func__, ret); + + ret = msm_audio_ion_free( + common.source_tracking_sh_mem.sh_mem_block.dma_buf); + common.source_tracking_sh_mem.sh_mem_block.dma_buf = NULL; + if (ret < 0) + pr_err("%s: Error:%d freeing memory\n", __func__, ret); + + ret = -EINVAL; + goto done; + } + memset((void *)(common.source_tracking_sh_mem.sh_mem_table.data), 0, + common.source_tracking_sh_mem.sh_mem_table.size); + + pr_debug("%s sh_mem_table: phys:[%pK], data:[0x%pK], size:[%zd],\n", + __func__, + &(common.source_tracking_sh_mem.sh_mem_table.phys), + (void *)(common.source_tracking_sh_mem.sh_mem_table.data), + (size_t)(common.source_tracking_sh_mem.sh_mem_table.size)); + +done: + pr_debug("%s: Exit, ret=%d\n", __func__, ret); + + return ret; +} + +static int voice_alloc_and_map_source_tracking_shared_memory( + struct voice_data *v) +{ + int ret = 0; + + pr_debug("%s: Enter\n", __func__); + + ret = voice_alloc_source_tracking_shared_memory(); + if (ret) { + pr_err("%s: Failed to allocate shared memory %d\n", + __func__, ret); + + ret = -EINVAL; + goto done; + } + + ret = voice_map_memory_physical_cmd(v, + &(common.source_tracking_sh_mem.sh_mem_table), + common.source_tracking_sh_mem.sh_mem_block.phys, + common.source_tracking_sh_mem.sh_mem_block.size, + VOC_SOURCE_TRACKING_MEM_MAP_TOKEN); + if (ret) { + pr_err("%s: memory mapping failed %d\n", + __func__, ret); + + ret = -EINVAL; + goto done; + } + +done: + pr_debug("%s: Exit, ret=%d\n", __func__, ret); + + return ret; +} + +static int voice_unmap_and_free_source_tracking_shared_memory( + struct voice_data *v) +{ + int ret = 0; + + pr_debug("%s: Enter\n", __func__); + + if (common.source_tracking_sh_mem.mem_handle != 0) { + ret = voice_send_mvm_unmap_memory_physical_cmd(v, + common.source_tracking_sh_mem.mem_handle); + if (ret < 0) { + pr_err("%s: Memory_unmap failed err %d\n", + __func__, ret); + + ret = -EINVAL; + goto done; + } + } + + if (common.source_tracking_sh_mem.sh_mem_block.dma_buf == NULL) + goto done; + + ret = msm_audio_ion_free( + common.source_tracking_sh_mem.sh_mem_block.dma_buf); + if (ret < 0) { + pr_err("%s: Error:%d freeing memory\n", __func__, ret); + + ret = -EINVAL; + goto done; + } + +done: + common.source_tracking_sh_mem.mem_handle = 0; + common.source_tracking_sh_mem.sh_mem_block.dma_buf = NULL; + pr_debug("%s: Exit, ret=%d\n", __func__, ret); + + return ret; +} + +static int voice_send_get_source_tracking_cmd(struct voice_data *v, + struct source_tracking_param *sourceTrackingData) +{ + struct cvp_get_source_tracking_param_cmd_t st_cmd; + int ret = 0; + void *apr_cvp; + u16 cvp_handle; + int i; + + pr_debug("%s: Enter\n", __func__); + + if (!v) { + pr_err("%s: v is NULL\n", __func__); + return -EINVAL; + } + apr_cvp = common.apr_q6_cvp; + + if (!apr_cvp) { + pr_err("%s: apr_cvp is NULL.\n", __func__); + return -EINVAL; + } + + cvp_handle = voice_get_cvp_handle(v); + + if (!is_source_tracking_shared_memomry_allocated()) { + ret = voice_alloc_and_map_source_tracking_shared_memory(v); + if (ret) { + pr_err("%s: Fail in allocating/mapping shared memory\n", + __func__); + + ret = -EINVAL; + goto done; + } + } + st_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + st_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(st_cmd) - APR_HDR_SIZE); + st_cmd.hdr.src_port = voice_get_idx_for_session(v->session_id); + st_cmd.hdr.dest_port = cvp_handle; + st_cmd.hdr.token = 0; + st_cmd.hdr.opcode = VSS_ISOURCETRACK_CMD_GET_ACTIVITY; + + st_cmd.cvp_get_source_tracking_param.mem_handle = + common.source_tracking_sh_mem.mem_handle; + st_cmd.cvp_get_source_tracking_param.mem_address_lsw = + lower_32_bits(common.source_tracking_sh_mem.sh_mem_block.phys); + st_cmd.cvp_get_source_tracking_param.mem_address_msw = + msm_audio_populate_upper_32_bits(common.source_tracking_sh_mem. + sh_mem_block.phys); + st_cmd.cvp_get_source_tracking_param.mem_size = + (uint32_t)common.source_tracking_sh_mem.sh_mem_block.size; + pr_debug("%s: mem_handle=0x%x, mem_address_lsw=0x%x, msw=0x%x, mem_size=%d\n", + __func__, + st_cmd.cvp_get_source_tracking_param.mem_handle, + st_cmd.cvp_get_source_tracking_param.mem_address_lsw, + st_cmd.cvp_get_source_tracking_param.mem_address_msw, + (uint32_t)st_cmd.cvp_get_source_tracking_param.mem_size); + + v->cvp_state = CMD_STATUS_FAIL; + v->async_err = 0; + ret = apr_send_pkt(apr_cvp, + (uint32_t *) &st_cmd); + if (ret < 0) { + pr_err("%s: Error in sending APR command\n", __func__); + + ret = -EINVAL; + goto done; + } + ret = wait_event_timeout(v->cvp_wait, + (v->cvp_state == CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + + ret = -EINVAL; + goto done; + } + + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + v->async_err)); + ret = adsp_err_get_lnx_err_code( + v->async_err); + goto done; + } + + if (common.is_source_tracking_resp_success) { + for (i = 0; i < MAX_SECTORS; i++) { + sourceTrackingData->vad[i] = + common.sourceTrackingResponse.voice_active[i]; + pr_debug("%s: vad[%d] = %d\n", + __func__, i, sourceTrackingData->vad[i]); + } + sourceTrackingData->doa_speech = + common.sourceTrackingResponse.talker_doa; + pr_debug("%s: doa_speech = %d\n", + __func__, sourceTrackingData->doa_speech); + + for (i = 0; i < MAX_NOISE_SOURCE_INDICATORS; i++) { + sourceTrackingData->doa_noise[i] = + common.sourceTrackingResponse.interferer_doa[i]; + pr_debug("%s: doa_noise[%d] = %d\n", + __func__, i, sourceTrackingData->doa_noise[i]); + } + for (i = 0; i < MAX_POLAR_ACTIVITY_INDICATORS; i++) { + sourceTrackingData->polar_activity[i] = + common.sourceTrackingResponse.sound_strength[i]; + pr_debug("%s: polar_activity[%d] = %d\n", + __func__, i, sourceTrackingData->polar_activity[i]); + } + common.is_source_tracking_resp_success = false; + ret = 0; + } else { + pr_err("%s: Error response received from CVD\n", __func__); + + ret = -EINVAL; + } +done: + pr_debug("%s: Exit, ret=%d\n", __func__, ret); + + return ret; +} + +/** + * voc_get_source_tracking - retrieves source track data. + * + * @sourceTrackingData: pointer to be updated with source track data. + * + * Returns 0 on success or error on failure + */ +int voc_get_source_tracking(struct source_tracking_param *sourceTrackingData) +{ + struct voice_data *v = NULL; + int ret = -EINVAL; + struct voice_session_itr itr; + + pr_debug("%s: Enter\n", __func__); + + mutex_lock(&common.common_lock); + + voice_itr_init(&itr, ALL_SESSION_VSID); + while (voice_itr_get_next_session(&itr, &v)) { + if (v != NULL) { + mutex_lock(&v->lock); + if (is_voc_state_active(v->voc_state) && + (v->lch_mode != VOICE_LCH_START) && + !v->disable_topology) + ret = voice_send_get_source_tracking_cmd(v, + sourceTrackingData); + mutex_unlock(&v->lock); + } else { + pr_err("%s: invalid session\n", __func__); + + break; + } + } + + mutex_unlock(&common.common_lock); + pr_debug("%s: Exit, ret=%d\n", __func__, ret); + + return ret; +} +EXPORT_SYMBOL(voc_get_source_tracking); + +static int voice_set_cvp_param(struct voice_data *v, + struct vss_icommon_mem_mapping_hdr *mem_hdr, + u32 *param_data, u32 param_size) +{ + struct vss_icommon_cmd_set_param *set_param = NULL; + uint32_t pkt_size = sizeof(struct vss_icommon_cmd_set_param); + void *apr_cvp; + int ret = 0; + + apr_cvp = common.apr_q6_cvp; + if (!apr_cvp) { + pr_err("%s: apr_cvp is NULL\n", __func__); + return -EINVAL; + } + + if (param_data != NULL) + pkt_size += param_size; + set_param = kzalloc(pkt_size, GFP_KERNEL); + if (!set_param) + return -ENOMEM; + + set_param->apr_hdr.hdr_field = + APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + set_param->apr_hdr.pkt_size = + APR_PKT_SIZE(APR_HDR_SIZE, pkt_size - APR_HDR_SIZE); + set_param->apr_hdr.src_svc = 0; + set_param->apr_hdr.src_domain = APR_DOMAIN_APPS; + set_param->apr_hdr.src_port = voice_get_idx_for_session(v->session_id); + set_param->apr_hdr.dest_svc = 0; + set_param->apr_hdr.dest_domain = APR_DOMAIN_ADSP; + set_param->apr_hdr.dest_port = voice_get_cvp_handle(v); + set_param->apr_hdr.token = VOC_SET_MEDIA_FORMAT_PARAM_TOKEN; + set_param->apr_hdr.opcode = q6common_is_instance_id_supported() ? + VSS_ICOMMON_CMD_SET_PARAM_V3 : + VSS_ICOMMON_CMD_SET_PARAM_V2; + + set_param->payload_size = param_size; + + if (mem_hdr != NULL) { + set_param->mem_hdr = *mem_hdr; + } else if (param_data != NULL) { + memcpy(set_param->param_data, param_data, param_size); + } else { + pr_err("%s: Both memory header and param data are NULL\n", + __func__); + ret = -EINVAL; + goto done; + } + + v->cvp_state = CMD_STATUS_FAIL; + v->async_err = 0; + ret = apr_send_pkt(apr_cvp, (u32 *) set_param); + if (ret < 0) { + pr_err("%s: Failed to send apr packet, error %d\n", __func__, + ret); + goto done; + } + + ret = wait_event_timeout(v->cvp_wait, + v->cvp_state == CMD_STATUS_SUCCESS, + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + ret = -ETIMEDOUT; + goto done; + } + + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", __func__, + adsp_err_get_err_str(v->async_err)); + ret = adsp_err_get_lnx_err_code(v->async_err); + goto done; + } + ret = 0; + +done: + kfree(set_param); + return ret; +} + +static int voice_pack_and_set_cvp_param(struct voice_data *v, + struct param_hdr_v3 param_hdr, + u8 *param_data) +{ + u8 *packed_data = NULL; + u32 total_size = 0; + int ret = 0; + + total_size = sizeof(union param_hdrs) + param_hdr.param_size; + packed_data = kzalloc(total_size, GFP_KERNEL); + if (!packed_data) + return -ENOMEM; + + ret = q6common_pack_pp_params(packed_data, ¶m_hdr, param_data, + &total_size); + if (ret) { + pr_err("%s: Failed to pack params, error %d", __func__, ret); + goto done; + } + + ret = voice_set_cvp_param(v, NULL, (u32 *) packed_data, total_size); + +done: + kfree(packed_data); + return ret; +} + +/* + * Out of band is not supported and there are currently no pre-packed cases, + * so pack and set in the same function. When needed, split up. + */ +static int voice_pack_and_set_cvs_ui_property(struct voice_data *v, + struct param_hdr_v3 param_hdr, + u8 *param_data) +{ + struct vss_icommon_cmd_set_ui_property *set_ui_property = NULL; + u32 total_size = 0; + u32 pkt_size = 0; + u32 param_size = 0; + bool iid_supported = q6common_is_instance_id_supported(); + void *apr_cvs; + int ret = 0; + + apr_cvs = common.apr_q6_cvs; + if (!apr_cvs) { + pr_err("%s: apr_cvs is NULL\n", __func__); + return -EINVAL; + } + + pkt_size = sizeof(struct vss_icommon_cmd_set_ui_property); + param_size = sizeof(union param_hdrs) + param_hdr.param_size; + total_size = pkt_size + param_size; + set_ui_property = kzalloc(total_size, GFP_KERNEL); + if (!set_ui_property) + return -ENOMEM; + + ret = q6common_pack_pp_params(set_ui_property->param_data, ¶m_hdr, + param_data, ¶m_size); + if (ret) { + pr_err("%s: Failed to pack params, error %d", __func__, ret); + goto done; + } + + /* + * Pack the APR header after packing the data so we have the actual + * total size of the payload + */ + total_size = pkt_size + param_size; + set_ui_property->apr_hdr.hdr_field = + APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + set_ui_property->apr_hdr.pkt_size = total_size; + set_ui_property->apr_hdr.src_svc = 0; + set_ui_property->apr_hdr.src_domain = APR_DOMAIN_APPS; + set_ui_property->apr_hdr.src_port = + voice_get_idx_for_session(v->session_id); + set_ui_property->apr_hdr.dest_svc = 0; + set_ui_property->apr_hdr.dest_domain = APR_DOMAIN_ADSP; + set_ui_property->apr_hdr.dest_port = voice_get_cvs_handle(v); + set_ui_property->apr_hdr.token = 0; + + set_ui_property->apr_hdr.opcode = + iid_supported ? VSS_ICOMMON_CMD_SET_UI_PROPERTY_V2 : + VSS_ICOMMON_CMD_SET_UI_PROPERTY; + + v->cvs_state = CMD_STATUS_FAIL; + v->async_err = 0; + ret = apr_send_pkt(apr_cvs, (u32 *) set_ui_property); + if (ret < 0) { + pr_err("%s: Failed to send apr packet, error %d\n", __func__, + ret); + goto done; + } + + ret = wait_event_timeout(v->cvs_wait, + v->cvs_state == CMD_STATUS_SUCCESS, + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + ret = -ETIMEDOUT; + goto done; + } + + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s]\n", __func__, + adsp_err_get_err_str(v->async_err)); + ret = adsp_err_get_lnx_err_code(v->async_err); + goto done; + } + ret = 0; +done: + kfree(set_ui_property); + return ret; +} + +static void voc_release_uevent_data(struct kobject *kobj) +{ + struct audio_uevent_data *data = container_of(kobj, + struct audio_uevent_data, + kobj); + kfree(data); +} + +/** + * is_voc_initialized: + * + * Returns voice module init status + * + */ +int is_voc_initialized(void) +{ + return module_initialized; +} +EXPORT_SYMBOL(is_voc_initialized); + +int __init voice_init(void) +{ + int rc = 0, i = 0; + + memset(&common, 0, sizeof(struct common_data)); + + /* set default value */ + common.default_mute_val = 0; /* default is un-mute */ + common.default_sample_val = 8000; + common.default_vol_step_val = 0; + common.default_vol_ramp_duration_ms = DEFAULT_VOLUME_RAMP_DURATION; + common.default_mute_ramp_duration_ms = DEFAULT_MUTE_RAMP_DURATION; + common.cvp_version = 0; + common.is_avcs_version_queried = false; + /* Initialize EC Ref media format info */ + common.ec_ref_ext = false; + common.ec_media_fmt_info.port_id = AFE_PORT_INVALID; + common.ec_media_fmt_info.num_channels = 0; + common.ec_media_fmt_info.bits_per_sample = 16; + common.ec_media_fmt_info.sample_rate = 8000; + memset(&common.ec_media_fmt_info.channel_mapping, 0, + VSS_CHANNEL_MAPPING_SIZE); + + /* Initialize AFE Sidetone Enable */ + common.sidetone_enable = false; + + /* Initialize MVS info. */ + common.mvs_info.network_type = VSS_NETWORK_ID_DEFAULT; + + /* Initialize is low memory flag */ + common.is_destroy_cvd = false; + + /* Initialize CVD version */ + strlcpy(common.cvd_version, CVD_VERSION_DEFAULT, + sizeof(common.cvd_version)); + /* Initialize Per-Vocoder Calibration flag */ + common.is_per_vocoder_cal_enabled = false; + + mutex_init(&common.common_lock); + + common.uevent_data = kzalloc(sizeof(*(common.uevent_data)), GFP_KERNEL); + if (!common.uevent_data) + return -ENOMEM; + + /* + * Set release function to cleanup memory related to kobject + * before initializing the kobject. + */ + common.uevent_data->ktype.release = voc_release_uevent_data; + q6core_init_uevent_data(common.uevent_data, "q6voice_uevent"); + common.mic_break_enable = false; + + /* Initialize session id with vsid */ + init_session_id(); + + for (i = 0; i < MAX_VOC_SESSIONS; i++) { + + /* initialize dev_rx and dev_tx */ + common.voice[i].dev_rx.dev_mute = common.default_mute_val; + common.voice[i].dev_tx.dev_mute = common.default_mute_val; + common.voice[i].dev_rx.volume_step_value = + common.default_vol_step_val; + common.voice[i].dev_rx.volume_ramp_duration_ms = + common.default_vol_ramp_duration_ms; + common.voice[i].dev_rx.dev_mute_ramp_duration_ms = + common.default_mute_ramp_duration_ms; + common.voice[i].dev_tx.dev_mute_ramp_duration_ms = + common.default_mute_ramp_duration_ms; + common.voice[i].stream_rx.stream_mute = common.default_mute_val; + common.voice[i].stream_tx.stream_mute = common.default_mute_val; + + common.voice[i].dev_tx.port_id = 0x100B; + common.voice[i].dev_rx.port_id = 0x100A; + common.voice[i].dev_tx.dev_id = 0; + common.voice[i].dev_rx.dev_id = 0; + common.voice[i].dev_tx.no_of_channels = 0; + common.voice[i].dev_rx.no_of_channels = 0; + common.voice[i].dev_tx.sample_rate = 8000; + common.voice[i].dev_rx.sample_rate = 8000; + common.voice[i].dev_tx.bits_per_sample = 16; + common.voice[i].dev_rx.bits_per_sample = 16; + memset(&common.voice[i].dev_tx.channel_mapping, 0, + VSS_CHANNEL_MAPPING_SIZE); + memset(&common.voice[i].dev_rx.channel_mapping, 0, + VSS_CHANNEL_MAPPING_SIZE); + common.voice[i].sidetone_gain = 0x512; + common.voice[i].dtmf_rx_detect_en = 0; + common.voice[i].lch_mode = 0; + common.voice[i].disable_topology = false; + + common.voice[i].voc_state = VOC_INIT; + + INIT_WORK(&common.voice[i].voice_mic_break_work, + voice_mic_break_work_fn); + + init_waitqueue_head(&common.voice[i].mvm_wait); + init_waitqueue_head(&common.voice[i].cvs_wait); + init_waitqueue_head(&common.voice[i].cvp_wait); + + mutex_init(&common.voice[i].lock); + } + + if (voice_init_cal_data()) + pr_err("%s: Could not init cal data!\n", __func__); + + if (rc == 0) + module_initialized = true; + + pr_debug("%s: rc=%d\n", __func__, rc); + return rc; +} + + +void voice_exit(void) +{ + q6core_destroy_uevent_data(common.uevent_data); + voice_delete_cal_data(); + free_cal_map_table(); +} diff --git a/audio-kernel/dsp/rtac.c b/audio-kernel/dsp/rtac.c new file mode 100644 index 000000000..5a3ce3410 --- /dev/null +++ b/audio-kernel/dsp/rtac.c @@ -0,0 +1,2084 @@ +/* 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 + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "adsp_err.h" + + +/* Max size of payload (buf size - apr header) */ +#define MAX_PAYLOAD_SIZE 4076 +#define RTAC_MAX_ACTIVE_VOICE_COMBOS 2 +#define RTAC_MAX_ACTIVE_POPP 8 +#define RTAC_BUF_SIZE 163840 + +#define TIMEOUT_MS 1000 + +struct rtac_cal_block_data rtac_cal[MAX_RTAC_BLOCKS] = { +/* ADM_RTAC_CAL */ + {{RTAC_BUF_SIZE, 0, 0}, {0, 0, 0} }, +/* ASM_RTAC_CAL */ + {{RTAC_BUF_SIZE, 0, 0}, {0, 0, 0} }, +/* VOICE_RTAC_CAL */ + {{RTAC_BUF_SIZE, 0, 0}, {0, 0, 0} }, +/* AFE_RTAC_CAL */ + {{RTAC_BUF_SIZE, 0, 0}, {0, 0, 0} } +}; + +struct rtac_common_data { + atomic_t usage_count; + atomic_t apr_err_code; + struct mutex rtac_fops_mutex; +}; + +static struct rtac_common_data rtac_common; + +/* APR data */ +struct rtac_apr_data { + void *apr_handle; + atomic_t cmd_state; + wait_queue_head_t cmd_wait; +}; + +static struct rtac_apr_data rtac_adm_apr_data; +static struct rtac_apr_data rtac_asm_apr_data[ASM_ACTIVE_STREAMS_ALLOWED + 1]; +static struct rtac_apr_data rtac_afe_apr_data; +static struct rtac_apr_data rtac_voice_apr_data[RTAC_VOICE_MODES]; + +/* ADM info & APR */ +static struct rtac_adm rtac_adm_data; +static u32 *rtac_adm_buffer; + + +/* ASM APR */ +static u32 *rtac_asm_buffer; + +static u32 *rtac_afe_buffer; + +/* Voice info & APR */ +struct rtac_voice_data_t { + uint32_t tx_topology_id; + uint32_t rx_topology_id; + uint32_t tx_afe_topology; + uint32_t rx_afe_topology; + uint32_t tx_afe_port; + uint32_t rx_afe_port; + uint16_t cvs_handle; + uint16_t cvp_handle; + uint32_t tx_acdb_id; + uint32_t rx_acdb_id; +}; + +struct rtac_voice { + uint32_t num_of_voice_combos; + struct rtac_voice_data_t voice[RTAC_MAX_ACTIVE_VOICE_COMBOS]; +}; + +struct rtac_afe_user_data { + uint32_t buf_size; + uint32_t cmd_size; + uint32_t port_id; + union { + struct afe_rtac_user_data_set_v2 v2_set; + struct afe_rtac_user_data_set_v3 v3_set; + struct afe_rtac_user_data_get_v2 v2_get; + struct afe_rtac_user_data_get_v3 v3_get; + }; +} __packed; + +static struct rtac_voice rtac_voice_data; +static u32 *rtac_voice_buffer; +static u32 voice_session_id[RTAC_MAX_ACTIVE_VOICE_COMBOS]; + + +struct mutex rtac_adm_mutex; +struct mutex rtac_adm_apr_mutex; +struct mutex rtac_asm_apr_mutex; +struct mutex rtac_voice_mutex; +struct mutex rtac_voice_apr_mutex; +struct mutex rtac_afe_apr_mutex; + +int rtac_clear_mapping(uint32_t cal_type) +{ + int result = 0; + + pr_debug("%s\n", __func__); + + if (cal_type >= MAX_RTAC_BLOCKS) { + pr_debug("%s: invalid cal type %d\n", __func__, cal_type); + result = -EINVAL; + goto done; + } + + rtac_cal[cal_type].map_data.map_handle = 0; +done: + return result; +} + +int rtac_allocate_cal_buffer(uint32_t cal_type) +{ + int result = 0; + size_t len; + + pr_debug("%s\n", __func__); + + if (cal_type >= MAX_RTAC_BLOCKS) { + pr_err("%s: cal_type %d is invalid!\n", + __func__, cal_type); + result = -EINVAL; + goto done; + } + + if (rtac_cal[cal_type].cal_data.paddr != 0) { + pr_err("%s: memory already allocated! cal_type %d, paddr 0x%pK\n", + __func__, cal_type, &rtac_cal[cal_type].cal_data.paddr); + result = -EPERM; + goto done; + } + + result = msm_audio_ion_alloc(&rtac_cal[cal_type].map_data.dma_buf, + rtac_cal[cal_type].map_data.map_size, + &rtac_cal[cal_type].cal_data.paddr, + &len, + &rtac_cal[cal_type].cal_data.kvaddr); + if (result < 0) { + pr_err("%s: ION create client for RTAC failed\n", + __func__); + goto done; + } + + pr_debug("%s: cal_type %d, paddr 0x%pK, kvaddr 0x%pK, map_size 0x%x\n", + __func__, cal_type, + &rtac_cal[cal_type].cal_data.paddr, + rtac_cal[cal_type].cal_data.kvaddr, + rtac_cal[cal_type].map_data.map_size); +done: + return result; +} + +int rtac_free_cal_buffer(uint32_t cal_type) +{ + int result = 0; + + pr_debug("%s\n", __func__); + + if (cal_type >= MAX_RTAC_BLOCKS) { + pr_err("%s: cal_type %d is invalid!\n", + __func__, cal_type); + result = -EINVAL; + goto done; + } + + if (rtac_cal[cal_type].map_data.dma_buf == NULL) { + pr_debug("%s: cal_type %d not allocated!\n", + __func__, cal_type); + goto done; + } + + result = msm_audio_ion_free(rtac_cal[cal_type].map_data.dma_buf); + if (result < 0) { + pr_err("%s: ION free for RTAC failed! cal_type %d, paddr 0x%pK\n", + __func__, cal_type, &rtac_cal[cal_type].cal_data.paddr); + goto done; + } + + rtac_cal[cal_type].map_data.map_handle = 0; + rtac_cal[cal_type].map_data.dma_buf = NULL; + rtac_cal[cal_type].cal_data.size = 0; + rtac_cal[cal_type].cal_data.kvaddr = 0; + rtac_cal[cal_type].cal_data.paddr = 0; +done: + return result; +} + +int rtac_map_cal_buffer(uint32_t cal_type) +{ + int result = 0; + + pr_debug("%s\n", __func__); + + if (cal_type >= MAX_RTAC_BLOCKS) { + pr_err("%s: cal_type %d is invalid!\n", + __func__, cal_type); + result = -EINVAL; + goto done; + } + + if (rtac_cal[cal_type].map_data.map_handle != 0) { + pr_err("%s: already mapped cal_type %d\n", + __func__, cal_type); + result = -EPERM; + goto done; + } + + if (rtac_cal[cal_type].cal_data.paddr == 0) { + pr_err("%s: physical address is NULL cal_type %d\n", + __func__, cal_type); + result = -EPERM; + goto done; + } + + switch (cal_type) { + case ADM_RTAC_CAL: + result = adm_map_rtac_block(&rtac_cal[cal_type]); + break; + case ASM_RTAC_CAL: + result = q6asm_map_rtac_block(&rtac_cal[cal_type]); + break; + case VOICE_RTAC_CAL: + result = voc_map_rtac_block(&rtac_cal[cal_type]); + break; + case AFE_RTAC_CAL: + result = afe_map_rtac_block(&rtac_cal[cal_type]); + break; + } + if (result < 0) { + pr_err("%s: map RTAC failed! cal_type %d\n", + __func__, cal_type); + goto done; + } +done: + return result; +} + +int rtac_unmap_cal_buffer(uint32_t cal_type) +{ + int result = 0; + + pr_debug("%s\n", __func__); + + if (cal_type >= MAX_RTAC_BLOCKS) { + pr_err("%s: cal_type %d is invalid!\n", + __func__, cal_type); + result = -EINVAL; + goto done; + } + + if (rtac_cal[cal_type].map_data.map_handle == 0) { + pr_debug("%s: nothing to unmap cal_type %d\n", + __func__, cal_type); + goto done; + } + + switch (cal_type) { + case ADM_RTAC_CAL: + result = adm_unmap_rtac_block( + &rtac_cal[cal_type].map_data.map_handle); + break; + case ASM_RTAC_CAL: + result = q6asm_unmap_rtac_block( + &rtac_cal[cal_type].map_data.map_handle); + break; + case VOICE_RTAC_CAL: + result = voc_unmap_rtac_block( + &rtac_cal[cal_type].map_data.map_handle); + break; + case AFE_RTAC_CAL: + result = afe_unmap_rtac_block( + &rtac_cal[cal_type].map_data.map_handle); + break; + } + if (result < 0) { + pr_err("%s: unmap RTAC failed! cal_type %d\n", + __func__, cal_type); + goto done; + } +done: + return result; +} + +static int rtac_open(struct inode *inode, struct file *f) +{ + int result = 0; + + pr_debug("%s\n", __func__); + + mutex_lock(&rtac_common.rtac_fops_mutex); + atomic_inc(&rtac_common.usage_count); + mutex_unlock(&rtac_common.rtac_fops_mutex); + return result; +} + +static int rtac_release(struct inode *inode, struct file *f) +{ + int result = 0; + int result2 = 0; + int i; + + pr_debug("%s\n", __func__); + + mutex_lock(&rtac_common.rtac_fops_mutex); + atomic_dec(&rtac_common.usage_count); + pr_debug("%s: ref count %d!\n", __func__, + atomic_read(&rtac_common.usage_count)); + + if (atomic_read(&rtac_common.usage_count) > 0) { + mutex_unlock(&rtac_common.rtac_fops_mutex); + goto done; + } + + for (i = 0; i < MAX_RTAC_BLOCKS; i++) { + result2 = rtac_unmap_cal_buffer(i); + if (result2 < 0) { + pr_err("%s: unmap buffer failed! error %d!\n", + __func__, result2); + result = result2; + } + + result2 = rtac_free_cal_buffer(i); + if (result2 < 0) { + pr_err("%s: free buffer failed! error %d!\n", + __func__, result2); + result = result2; + } + } + mutex_unlock(&rtac_common.rtac_fops_mutex); +done: + return result; +} + + +/* ADM Info */ +void add_popp(u32 dev_idx, u32 port_id, u32 popp_id) +{ + u32 i = 0; + + for (; i < rtac_adm_data.device[dev_idx].num_of_popp; i++) + if (rtac_adm_data.device[dev_idx].popp[i].popp == popp_id) + goto done; + + if (rtac_adm_data.device[dev_idx].num_of_popp == + RTAC_MAX_ACTIVE_POPP) { + pr_err("%s, Max POPP!\n", __func__); + goto done; + } + rtac_adm_data.device[dev_idx].popp[ + rtac_adm_data.device[dev_idx].num_of_popp].popp = popp_id; + rtac_adm_data.device[dev_idx].popp[ + rtac_adm_data.device[dev_idx].num_of_popp].popp_topology = + q6asm_get_asm_topology(popp_id); + rtac_adm_data.device[dev_idx].popp[ + rtac_adm_data.device[dev_idx].num_of_popp++].app_type = + q6asm_get_asm_app_type(popp_id); + + pr_debug("%s: popp_id = %d, popp topology = 0x%x, popp app type = 0x%x\n", + __func__, + rtac_adm_data.device[dev_idx].popp[ + rtac_adm_data.device[dev_idx].num_of_popp - 1].popp, + rtac_adm_data.device[dev_idx].popp[ + rtac_adm_data.device[dev_idx].num_of_popp - 1].popp_topology, + rtac_adm_data.device[dev_idx].popp[ + rtac_adm_data.device[dev_idx].num_of_popp - 1].app_type); +done: + return; +} + +void rtac_update_afe_topology(u32 port_id) +{ + u32 i = 0; + + mutex_lock(&rtac_adm_mutex); + for (i = 0; i < rtac_adm_data.num_of_dev; i++) { + if (rtac_adm_data.device[i].afe_port == port_id) { + rtac_adm_data.device[i].afe_topology = + afe_get_topology(port_id); + pr_debug("%s: port_id = 0x%x topology_id = 0x%x copp_id = %d\n", + __func__, port_id, + rtac_adm_data.device[i].afe_topology, + rtac_adm_data.device[i].copp); + } + } + mutex_unlock(&rtac_adm_mutex); +} + +void rtac_add_adm_device(u32 port_id, u32 copp_id, u32 path_id, u32 popp_id, + u32 app_type, u32 acdb_id) +{ + u32 i = 0; + + pr_debug("%s: num rtac devices %d port_id = %d, copp_id = %d\n", + __func__, rtac_adm_data.num_of_dev, port_id, copp_id); + + mutex_lock(&rtac_adm_mutex); + if (rtac_adm_data.num_of_dev == RTAC_MAX_ACTIVE_DEVICES) { + pr_err("%s, Can't add anymore RTAC devices!\n", __func__); + goto done; + } + + /* Check if device already added */ + if (rtac_adm_data.num_of_dev != 0) { + for (; i < rtac_adm_data.num_of_dev; i++) { + if (rtac_adm_data.device[i].afe_port == port_id && + rtac_adm_data.device[i].copp == copp_id) { + add_popp(i, port_id, popp_id); + goto done; + } + if (rtac_adm_data.device[i].num_of_popp == + RTAC_MAX_ACTIVE_POPP) { + pr_err("%s, Max POPP!\n", __func__); + goto done; + } + } + } + + /* Add device */ + rtac_adm_data.num_of_dev++; + + rtac_adm_data.device[i].topology_id = + adm_get_topology_for_port_from_copp_id(port_id, copp_id); + rtac_adm_data.device[i].afe_topology = + afe_get_topology(port_id); + rtac_adm_data.device[i].afe_port = port_id; + rtac_adm_data.device[i].copp = copp_id; + rtac_adm_data.device[i].app_type = app_type; + rtac_adm_data.device[i].acdb_dev_id = acdb_id; + rtac_adm_data.device[i].popp[ + rtac_adm_data.device[i].num_of_popp].popp = popp_id; + rtac_adm_data.device[i].popp[ + rtac_adm_data.device[i].num_of_popp].popp_topology = + q6asm_get_asm_topology(popp_id); + rtac_adm_data.device[i].popp[ + rtac_adm_data.device[i].num_of_popp++].app_type = + q6asm_get_asm_app_type(popp_id); + + pr_debug("%s: topology = 0x%x, afe_topology = 0x%x, port_id = %d, copp_id = %d, app id = 0x%x, acdb id = %d, popp_id = %d, popp topology = 0x%x, popp app type = 0x%x\n", + __func__, + rtac_adm_data.device[i].topology_id, + rtac_adm_data.device[i].afe_topology, + rtac_adm_data.device[i].afe_port, + rtac_adm_data.device[i].copp, + rtac_adm_data.device[i].app_type, + rtac_adm_data.device[i].acdb_dev_id, + rtac_adm_data.device[i].popp[ + rtac_adm_data.device[i].num_of_popp - 1].popp, + rtac_adm_data.device[i].popp[ + rtac_adm_data.device[i].num_of_popp - 1].popp_topology, + rtac_adm_data.device[i].popp[ + rtac_adm_data.device[i].num_of_popp - 1].app_type); +done: + mutex_unlock(&rtac_adm_mutex); +} + +static void shift_adm_devices(u32 dev_idx) +{ + for (; dev_idx < rtac_adm_data.num_of_dev; dev_idx++) { + memcpy(&rtac_adm_data.device[dev_idx], + &rtac_adm_data.device[dev_idx + 1], + sizeof(rtac_adm_data.device[dev_idx])); + memset(&rtac_adm_data.device[dev_idx + 1], 0, + sizeof(rtac_adm_data.device[dev_idx])); + } +} + +static void shift_popp(u32 copp_idx, u32 popp_idx) +{ + for (; popp_idx < rtac_adm_data.device[copp_idx].num_of_popp; + popp_idx++) { + memcpy(&rtac_adm_data.device[copp_idx].popp[popp_idx].popp, + &rtac_adm_data.device[copp_idx].popp[popp_idx + 1]. + popp, sizeof(uint32_t)); + memcpy(&rtac_adm_data.device[copp_idx].popp[popp_idx]. + popp_topology, + &rtac_adm_data.device[copp_idx].popp[popp_idx + 1]. + popp_topology, + sizeof(uint32_t)); + memset(&rtac_adm_data.device[copp_idx].popp[popp_idx + 1]. + popp, 0, sizeof(uint32_t)); + memset(&rtac_adm_data.device[copp_idx].popp[popp_idx + 1]. + popp_topology, 0, sizeof(uint32_t)); + } +} + +void rtac_remove_adm_device(u32 port_id, u32 copp_id) +{ + s32 i; + + pr_debug("%s: num rtac devices %d port_id = %d, copp_id = %d\n", + __func__, rtac_adm_data.num_of_dev, port_id, copp_id); + + mutex_lock(&rtac_adm_mutex); + /* look for device */ + for (i = 0; i < rtac_adm_data.num_of_dev; i++) { + if (rtac_adm_data.device[i].afe_port == port_id && + rtac_adm_data.device[i].copp == copp_id) { + memset(&rtac_adm_data.device[i], 0, + sizeof(rtac_adm_data.device[i])); + rtac_adm_data.num_of_dev--; + + if (rtac_adm_data.num_of_dev >= 1) { + shift_adm_devices(i); + break; + } + } + } + + mutex_unlock(&rtac_adm_mutex); +} + +void rtac_remove_popp_from_adm_devices(u32 popp_id) +{ + s32 i, j; + + pr_debug("%s: popp_id = %d\n", __func__, popp_id); + + mutex_lock(&rtac_adm_mutex); + for (i = 0; i < rtac_adm_data.num_of_dev; i++) { + for (j = 0; j < rtac_adm_data.device[i].num_of_popp; j++) { + if (rtac_adm_data.device[i].popp[j].popp == + popp_id) { + rtac_adm_data.device[i].popp[j].popp = 0; + rtac_adm_data.device[i].popp[j]. + popp_topology = 0; + rtac_adm_data.device[i].num_of_popp--; + shift_popp(i, j); + } + } + } + mutex_unlock(&rtac_adm_mutex); +} + + +/* Voice Info */ +static void set_rtac_voice_data(int idx, u32 cvs_handle, u32 cvp_handle, + u32 rx_afe_port, u32 tx_afe_port, + u32 rx_acdb_id, u32 tx_acdb_id, + u32 session_id) +{ + rtac_voice_data.voice[idx].tx_topology_id = + voice_get_topology(CVP_VOC_TX_TOPOLOGY_CAL); + rtac_voice_data.voice[idx].rx_topology_id = + voice_get_topology(CVP_VOC_RX_TOPOLOGY_CAL); + rtac_voice_data.voice[idx].tx_afe_topology = + afe_get_topology(tx_afe_port); + rtac_voice_data.voice[idx].rx_afe_topology = + afe_get_topology(rx_afe_port); + rtac_voice_data.voice[idx].tx_afe_port = tx_afe_port; + rtac_voice_data.voice[idx].rx_afe_port = rx_afe_port; + rtac_voice_data.voice[idx].tx_acdb_id = tx_acdb_id; + rtac_voice_data.voice[idx].rx_acdb_id = rx_acdb_id; + rtac_voice_data.voice[idx].cvs_handle = cvs_handle; + rtac_voice_data.voice[idx].cvp_handle = cvp_handle; + pr_debug("%s\n%s: %x\n%s: %d %s: %d\n%s: %d %s: %d\n %s: %d\n %s: %d\n%s: %d %s: %d\n%s", + "<---- Voice Data Info ---->", "Session id", session_id, + "cvs_handle", cvs_handle, "cvp_handle", cvp_handle, + "rx_afe_topology", rtac_voice_data.voice[idx].rx_afe_topology, + "tx_afe_topology", rtac_voice_data.voice[idx].tx_afe_topology, + "rx_afe_port", rx_afe_port, "tx_afe_port", tx_afe_port, + "rx_acdb_id", rx_acdb_id, "tx_acdb_id", tx_acdb_id, + "<-----------End----------->"); + + /* Store session ID for voice RTAC */ + voice_session_id[idx] = session_id; +} + +void rtac_add_voice(u32 cvs_handle, u32 cvp_handle, u32 rx_afe_port, + u32 tx_afe_port, u32 rx_acdb_id, u32 tx_acdb_id, + u32 session_id) +{ + u32 i = 0; + + pr_debug("%s\n", __func__); + mutex_lock(&rtac_voice_mutex); + + if (rtac_voice_data.num_of_voice_combos == + RTAC_MAX_ACTIVE_VOICE_COMBOS) { + pr_err("%s, Can't add anymore RTAC devices!\n", __func__); + goto done; + } + + /* Check if device already added */ + if (rtac_voice_data.num_of_voice_combos != 0) { + for (; i < rtac_voice_data.num_of_voice_combos; i++) { + if (rtac_voice_data.voice[i].cvs_handle == + cvs_handle) { + set_rtac_voice_data(i, cvs_handle, cvp_handle, + rx_afe_port, tx_afe_port, rx_acdb_id, + tx_acdb_id, session_id); + goto done; + } + } + } + + /* Add device */ + rtac_voice_data.num_of_voice_combos++; + set_rtac_voice_data(i, cvs_handle, cvp_handle, + rx_afe_port, tx_afe_port, + rx_acdb_id, tx_acdb_id, + session_id); +done: + mutex_unlock(&rtac_voice_mutex); +} + +static void shift_voice_devices(u32 idx) +{ + for (; idx < rtac_voice_data.num_of_voice_combos - 1; idx++) { + memcpy(&rtac_voice_data.voice[idx], + &rtac_voice_data.voice[idx + 1], + sizeof(rtac_voice_data.voice[idx])); + voice_session_id[idx] = voice_session_id[idx + 1]; + } +} + +void rtac_remove_voice(u32 cvs_handle) +{ + u32 i = 0; + + pr_debug("%s\n", __func__); + + mutex_lock(&rtac_voice_mutex); + /* look for device */ + for (i = 0; i < rtac_voice_data.num_of_voice_combos; i++) { + if (rtac_voice_data.voice[i].cvs_handle == cvs_handle) { + shift_voice_devices(i); + rtac_voice_data.num_of_voice_combos--; + memset(&rtac_voice_data.voice[ + rtac_voice_data.num_of_voice_combos], 0, + sizeof(rtac_voice_data.voice + [rtac_voice_data.num_of_voice_combos])); + voice_session_id[rtac_voice_data.num_of_voice_combos] + = 0; + break; + } + } + mutex_unlock(&rtac_voice_mutex); +} + +static u32 get_voice_session_id_cvs(u32 cvs_handle) +{ + u32 i; + + for (i = 0; i < rtac_voice_data.num_of_voice_combos; i++) { + if (rtac_voice_data.voice[i].cvs_handle == cvs_handle) + return voice_session_id[i]; + } + + pr_err("%s: No voice index for CVS handle %d found returning 0\n", + __func__, cvs_handle); + return 0; +} + +static u32 get_voice_session_id_cvp(u32 cvp_handle) +{ + u32 i; + + for (i = 0; i < rtac_voice_data.num_of_voice_combos; i++) { + if (rtac_voice_data.voice[i].cvp_handle == cvp_handle) + return voice_session_id[i]; + } + + pr_err("%s: No voice index for CVP handle %d found returning 0\n", + __func__, cvp_handle); + return 0; +} + +static int get_voice_index(u32 mode, u32 handle) +{ + if (mode == RTAC_CVP) + return voice_get_idx_for_session( + get_voice_session_id_cvp(handle)); + if (mode == RTAC_CVS) + return voice_get_idx_for_session( + get_voice_session_id_cvs(handle)); + + pr_err("%s: Invalid mode %d, returning 0\n", + __func__, mode); + return 0; +} + + +/* ADM APR */ +void rtac_set_adm_handle(void *handle) +{ + pr_debug("%s: handle = %pK\n", __func__, handle); + + mutex_lock(&rtac_adm_apr_mutex); + rtac_adm_apr_data.apr_handle = handle; + mutex_unlock(&rtac_adm_apr_mutex); +} + +bool rtac_make_adm_callback(uint32_t *payload, u32 payload_size) +{ + pr_debug("%s:cmd_state = %d\n", __func__, + atomic_read(&rtac_adm_apr_data.cmd_state)); + if (atomic_read(&rtac_adm_apr_data.cmd_state) != 1) + return false; + + pr_debug("%s\n", __func__); + if (payload_size == sizeof(uint32_t)) + atomic_set(&rtac_common.apr_err_code, payload[0]); + else if (payload_size == (2*sizeof(uint32_t))) + atomic_set(&rtac_common.apr_err_code, payload[1]); + + atomic_set(&rtac_adm_apr_data.cmd_state, 0); + wake_up(&rtac_adm_apr_data.cmd_wait); + return true; +} + +int send_adm_apr(void *buf, u32 opcode) +{ + s32 result; + u32 user_buf_size = 0; + u32 bytes_returned = 0; + u32 copp_id; + u32 payload_size; + u32 data_size = 0; + int copp_idx; + int port_idx; + struct apr_hdr adm_params; + + pr_debug("%s\n", __func__); + + if (rtac_cal[ADM_RTAC_CAL].map_data.dma_buf == NULL) { + result = rtac_allocate_cal_buffer(ADM_RTAC_CAL); + if (result < 0) { + pr_err("%s: allocate buffer failed!", + __func__); + goto done; + } + } + + if (rtac_cal[ADM_RTAC_CAL].map_data.map_handle == 0) { + result = rtac_map_cal_buffer(ADM_RTAC_CAL); + if (result < 0) { + pr_err("%s: map buffer failed!", + __func__); + goto done; + } + } + + if (copy_from_user(&user_buf_size, (void *)buf, + sizeof(user_buf_size))) { + pr_err("%s: Copy from user failed! buf = 0x%pK\n", + __func__, buf); + goto done; + } + if (user_buf_size <= 0) { + pr_err("%s: Invalid buffer size = %d\n", + __func__, user_buf_size); + goto done; + } + + if (copy_from_user(&payload_size, buf + sizeof(u32), sizeof(u32))) { + pr_err("%s: Could not copy payload size from user buffer\n", + __func__); + goto done; + } + + if (copy_from_user(&copp_id, buf + 2 * sizeof(u32), sizeof(u32))) { + pr_err("%s: Could not copy port id from user buffer\n", + __func__); + goto done; + } + + if (adm_get_indexes_from_copp_id(copp_id, &copp_idx, &port_idx) != 0) { + pr_err("%s: Copp Id-%d is not active\n", __func__, copp_id); + goto done; + } + + mutex_lock(&rtac_adm_apr_mutex); + if (rtac_adm_apr_data.apr_handle == NULL) { + pr_err("%s: APR not initialized\n", __func__); + result = -EINVAL; + goto err; + } + + switch (opcode) { + case ADM_CMD_SET_PP_PARAMS_V5: + case ADM_CMD_SET_PP_PARAMS_V6: + /* set payload size to in-band payload */ + /* set data size to actual out of band payload size */ + data_size = payload_size - 4 * sizeof(u32); + if (data_size > rtac_cal[ADM_RTAC_CAL].map_data.map_size) { + pr_err("%s: Invalid data size = %d\n", + __func__, data_size); + result = -EINVAL; + goto err; + } + payload_size = 4 * sizeof(u32); + + /* Copy buffer to out-of-band payload */ + if (copy_from_user((void *) + rtac_cal[ADM_RTAC_CAL].cal_data.kvaddr, + buf + 7 * sizeof(u32), data_size)) { + pr_err("%s: Could not copy payload from user buffer\n", + __func__); + result = -EFAULT; + goto err; + } + + /* set payload size in packet */ + rtac_adm_buffer[8] = data_size; + break; + case ADM_CMD_GET_PP_PARAMS_V5: + case ADM_CMD_GET_PP_PARAMS_V6: + if (payload_size > MAX_PAYLOAD_SIZE) { + pr_err("%s: Invalid payload size = %d\n", + __func__, payload_size); + result = -EINVAL; + goto err; + } + + /* Copy buffer to in-band payload */ + if (copy_from_user(rtac_adm_buffer + + sizeof(adm_params)/sizeof(u32), + buf + 3 * sizeof(u32), payload_size)) { + pr_err("%s: Could not copy payload from user buffer\n", + __func__); + result = -EFAULT; + goto err; + } + break; + default: + pr_err("%s: Invalid opcode %d\n", __func__, opcode); + result = -EINVAL; + goto err; + } + + /* Pack header */ + adm_params.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(20), APR_PKT_VER); + adm_params.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + payload_size); + adm_params.src_svc = APR_SVC_ADM; + adm_params.src_domain = APR_DOMAIN_APPS; + adm_params.src_port = copp_id; + adm_params.dest_svc = APR_SVC_ADM; + adm_params.dest_domain = APR_DOMAIN_ADSP; + adm_params.dest_port = copp_id; + adm_params.token = port_idx << 16 | copp_idx; + adm_params.opcode = opcode; + + /* fill for out-of-band */ + rtac_adm_buffer[5] = + lower_32_bits(rtac_cal[ADM_RTAC_CAL].cal_data.paddr); + rtac_adm_buffer[6] = + msm_audio_populate_upper_32_bits( + rtac_cal[ADM_RTAC_CAL].cal_data.paddr); + rtac_adm_buffer[7] = rtac_cal[ADM_RTAC_CAL].map_data.map_handle; + + memcpy(rtac_adm_buffer, &adm_params, sizeof(adm_params)); + atomic_set(&rtac_adm_apr_data.cmd_state, 1); + + pr_debug("%s: Sending RTAC command ioctl 0x%x, paddr 0x%pK\n", + __func__, opcode, + &rtac_cal[ADM_RTAC_CAL].cal_data.paddr); + + result = apr_send_pkt(rtac_adm_apr_data.apr_handle, + (uint32_t *)rtac_adm_buffer); + if (result < 0) { + pr_err("%s: Set params failed copp = %d\n", __func__, copp_id); + goto err; + } + /* Wait for the callback */ + result = wait_event_timeout(rtac_adm_apr_data.cmd_wait, + (atomic_read(&rtac_adm_apr_data.cmd_state) == 0), + msecs_to_jiffies(TIMEOUT_MS)); + if (!result) { + pr_err("%s: Set params timed out copp = %d\n", __func__, + copp_id); + goto err; + } + if (atomic_read(&rtac_common.apr_err_code)) { + pr_err("%s: DSP returned error code = [%s], opcode = 0x%x\n", + __func__, adsp_err_get_err_str(atomic_read( + &rtac_common.apr_err_code)), + opcode); + result = adsp_err_get_lnx_err_code( + atomic_read( + &rtac_common.apr_err_code)); + goto err; + } + + if (opcode == ADM_CMD_GET_PP_PARAMS_V5) { + bytes_returned = ((u32 *)rtac_cal[ADM_RTAC_CAL].cal_data. + kvaddr)[2] + 3 * sizeof(u32); + } else if (opcode == ADM_CMD_GET_PP_PARAMS_V6) { + bytes_returned = + ((u32 *) rtac_cal[ADM_RTAC_CAL].cal_data.kvaddr)[3] + + 4 * sizeof(u32); + } else { + bytes_returned = data_size; + goto unlock; + } + + if (bytes_returned > rtac_cal[ADM_RTAC_CAL].map_data.map_size) { + pr_err("%s: Invalid data size = %d\n", __func__, + bytes_returned); + result = -EINVAL; + goto err; + } + + if (bytes_returned > user_buf_size) { + pr_err("%s: User buf not big enough, size = 0x%x, returned size = 0x%x\n", + __func__, user_buf_size, bytes_returned); + result = -EINVAL; + goto err; + } + + if (copy_to_user((void __user *) buf, + rtac_cal[ADM_RTAC_CAL].cal_data.kvaddr, + bytes_returned)) { + pr_err("%s: Could not copy buffer to user,size = %d\n", + __func__, bytes_returned); + result = -EFAULT; + goto err; + } + +unlock: + mutex_unlock(&rtac_adm_apr_mutex); +done: + return bytes_returned; +err: + mutex_unlock(&rtac_adm_apr_mutex); + return result; +} + + +/* ASM APR */ +void rtac_set_asm_handle(u32 session_id, void *handle) +{ + pr_debug("%s\n", __func__); + + mutex_lock(&rtac_asm_apr_mutex); + rtac_asm_apr_data[session_id].apr_handle = handle; + mutex_unlock(&rtac_asm_apr_mutex); +} + +bool rtac_make_asm_callback(u32 session_id, uint32_t *payload, + u32 payload_size) +{ + if (atomic_read(&rtac_asm_apr_data[session_id].cmd_state) != 1) + return false; + + pr_debug("%s\n", __func__); + if (payload_size == sizeof(uint32_t)) + atomic_set(&rtac_common.apr_err_code, payload[0]); + else if (payload_size == (2*sizeof(uint32_t))) + atomic_set(&rtac_common.apr_err_code, payload[1]); + + atomic_set(&rtac_asm_apr_data[session_id].cmd_state, 0); + wake_up(&rtac_asm_apr_data[session_id].cmd_wait); + return true; +} + +int send_rtac_asm_apr(void *buf, u32 opcode) +{ + s32 result; + u32 user_buf_size = 0; + u32 bytes_returned = 0; + u32 session_id = 0; + u32 payload_size; + u32 data_size = 0; + struct apr_hdr asm_params; + + pr_debug("%s\n", __func__); + + if (rtac_cal[ASM_RTAC_CAL].map_data.dma_buf == NULL) { + result = rtac_allocate_cal_buffer(ASM_RTAC_CAL); + if (result < 0) { + pr_err("%s: allocate buffer failed!", + __func__); + goto done; + } + } + + if (rtac_cal[ASM_RTAC_CAL].map_data.map_handle == 0) { + result = rtac_map_cal_buffer(ASM_RTAC_CAL); + if (result < 0) { + pr_err("%s: map buffer failed!", + __func__); + goto done; + } + } + + if (copy_from_user(&user_buf_size, (void *)buf, + sizeof(user_buf_size))) { + pr_err("%s: Copy from user failed! buf = 0x%pK\n", + __func__, buf); + goto done; + } + if (user_buf_size <= 0) { + pr_err("%s: Invalid buffer size = %d\n", + __func__, user_buf_size); + goto done; + } + + if (copy_from_user(&payload_size, buf + sizeof(u32), sizeof(u32))) { + pr_err("%s: Could not copy payload size from user buffer\n", + __func__); + goto done; + } + + if (copy_from_user(&session_id, buf + 2 * sizeof(u32), sizeof(u32))) { + pr_err("%s: Could not copy session id from user buffer\n", + __func__); + goto done; + } + if (session_id >= (ASM_ACTIVE_STREAMS_ALLOWED + 1)) { + pr_err("%s: Invalid Session = %d\n", __func__, session_id); + goto done; + } + + mutex_lock(&rtac_asm_apr_mutex); + if (rtac_asm_apr_data[session_id].apr_handle == NULL) { + pr_err("%s: APR not initialized\n", __func__); + result = -EINVAL; + goto err; + } + + switch (opcode) { + case ASM_STREAM_CMD_SET_PP_PARAMS_V2: + case ASM_STREAM_CMD_SET_PP_PARAMS_V3: + /* set payload size to in-band payload */ + /* set data size to actual out of band payload size */ + data_size = payload_size - 4 * sizeof(u32); + if (data_size > rtac_cal[ASM_RTAC_CAL].map_data.map_size) { + pr_err("%s: Invalid data size = %d\n", + __func__, data_size); + result = -EINVAL; + goto err; + } + payload_size = 4 * sizeof(u32); + + /* Copy buffer to out-of-band payload */ + if (copy_from_user((void *) + rtac_cal[ASM_RTAC_CAL].cal_data.kvaddr, + buf + 7 * sizeof(u32), data_size)) { + pr_err("%s: Could not copy payload from user buffer\n", + __func__); + result = -EFAULT; + goto err; + } + /* set payload size in packet */ + rtac_asm_buffer[8] = data_size; + break; + case ASM_STREAM_CMD_GET_PP_PARAMS_V2: + case ASM_STREAM_CMD_GET_PP_PARAMS_V3: + if (payload_size > MAX_PAYLOAD_SIZE) { + pr_err("%s: Invalid payload size = %d\n", + __func__, payload_size); + result = -EINVAL; + goto err; + } + + /* Copy buffer to in-band payload */ + if (copy_from_user(rtac_asm_buffer + + sizeof(asm_params)/sizeof(u32), + buf + 3 * sizeof(u32), payload_size)) { + pr_err("%s: Could not copy payload from user buffer\n", + __func__); + result = -EFAULT; + goto err; + } + + break; + default: + pr_err("%s: Invalid opcode %d\n", __func__, opcode); + result = -EINVAL; + goto err; + } + + /* Pack header */ + asm_params.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(20), APR_PKT_VER); + asm_params.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + payload_size); + asm_params.src_svc = q6asm_get_apr_service_id(session_id); + asm_params.src_domain = APR_DOMAIN_APPS; + asm_params.src_port = (session_id << 8) | 0x0001; + asm_params.dest_svc = APR_SVC_ASM; + asm_params.dest_domain = APR_DOMAIN_ADSP; + asm_params.dest_port = (session_id << 8) | 0x0001; + asm_params.token = session_id; + asm_params.opcode = opcode; + + /* fill for out-of-band */ + rtac_asm_buffer[5] = + lower_32_bits(rtac_cal[ASM_RTAC_CAL].cal_data.paddr); + rtac_asm_buffer[6] = + msm_audio_populate_upper_32_bits( + rtac_cal[ASM_RTAC_CAL].cal_data.paddr); + rtac_asm_buffer[7] = rtac_cal[ASM_RTAC_CAL].map_data.map_handle; + + memcpy(rtac_asm_buffer, &asm_params, sizeof(asm_params)); + atomic_set(&rtac_asm_apr_data[session_id].cmd_state, 1); + + pr_debug("%s: Sending RTAC command ioctl 0x%x, paddr 0x%pK\n", + __func__, opcode, + &rtac_cal[ASM_RTAC_CAL].cal_data.paddr); + + result = apr_send_pkt(rtac_asm_apr_data[session_id].apr_handle, + (uint32_t *)rtac_asm_buffer); + if (result < 0) { + pr_err("%s: Set params failed session = %d\n", + __func__, session_id); + goto err; + } + + /* Wait for the callback */ + result = wait_event_timeout(rtac_asm_apr_data[session_id].cmd_wait, + (atomic_read(&rtac_asm_apr_data[session_id].cmd_state) == 0), + msecs_to_jiffies(TIMEOUT_MS)); + if (!result) { + pr_err("%s: Set params timed out session = %d\n", + __func__, session_id); + goto err; + } + if (atomic_read(&rtac_common.apr_err_code)) { + pr_err("%s: DSP returned error code = [%s], opcode = 0x%x\n", + __func__, adsp_err_get_err_str(atomic_read( + &rtac_common.apr_err_code)), + opcode); + result = adsp_err_get_lnx_err_code( + atomic_read( + &rtac_common.apr_err_code)); + goto err; + } + + if (opcode == ASM_STREAM_CMD_GET_PP_PARAMS_V2) { + bytes_returned = ((u32 *)rtac_cal[ASM_RTAC_CAL].cal_data. + kvaddr)[2] + 3 * sizeof(u32); + } else if (opcode == ASM_STREAM_CMD_GET_PP_PARAMS_V3) { + bytes_returned = + ((u32 *) rtac_cal[ASM_RTAC_CAL].cal_data.kvaddr)[3] + + 4 * sizeof(u32); + } else { + bytes_returned = data_size; + goto unlock; + } + + if (bytes_returned > rtac_cal[ASM_RTAC_CAL].map_data.map_size) { + pr_err("%s: Invalid data size = %d\n", __func__, + bytes_returned); + result = -EINVAL; + goto err; + } + + if (bytes_returned > user_buf_size) { + pr_err("%s: User buf not big enough, size = 0x%x, returned size = 0x%x\n", + __func__, user_buf_size, bytes_returned); + result = -EINVAL; + goto err; + } + + if (copy_to_user((void __user *) buf, + rtac_cal[ASM_RTAC_CAL].cal_data.kvaddr, + bytes_returned)) { + pr_err("%s: Could not copy buffer to user,size = %d\n", + __func__, bytes_returned); + result = -EFAULT; + goto err; + } + +unlock: + mutex_unlock(&rtac_asm_apr_mutex); +done: + return bytes_returned; +err: + mutex_unlock(&rtac_asm_apr_mutex); + return result; +} + +/* AFE APR */ +void rtac_set_afe_handle(void *handle) +{ + mutex_lock(&rtac_afe_apr_mutex); + rtac_afe_apr_data.apr_handle = handle; + mutex_unlock(&rtac_afe_apr_mutex); +} + +bool rtac_make_afe_callback(uint32_t *payload, uint32_t payload_size) +{ + pr_debug("%s:cmd_state = %d\n", __func__, + atomic_read(&rtac_afe_apr_data.cmd_state)); + if (atomic_read(&rtac_afe_apr_data.cmd_state) != 1) + return false; + + if (payload_size == sizeof(uint32_t)) + atomic_set(&rtac_common.apr_err_code, payload[0]); + else if (payload_size == (2*sizeof(uint32_t))) + atomic_set(&rtac_common.apr_err_code, payload[1]); + + atomic_set(&rtac_afe_apr_data.cmd_state, 0); + wake_up(&rtac_afe_apr_data.cmd_wait); + return true; +} + +static int fill_afe_apr_hdr(struct apr_hdr *apr_hdr, uint32_t port, + uint32_t opcode, uint32_t apr_msg_size) +{ + if (apr_hdr == NULL) { + pr_err("%s: invalid APR pointer", __func__); + return -EINVAL; + } + + apr_hdr->hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + apr_hdr->pkt_size = apr_msg_size; + apr_hdr->src_svc = APR_SVC_AFE; + apr_hdr->src_domain = APR_DOMAIN_APPS; + apr_hdr->src_port = 0; + apr_hdr->dest_svc = APR_SVC_AFE; + apr_hdr->dest_domain = APR_DOMAIN_ADSP; + apr_hdr->dest_port = 0; + apr_hdr->token = port; + apr_hdr->opcode = opcode; + + return 0; + +} +static int send_rtac_afe_apr(void __user *buf, uint32_t opcode) +{ + int32_t result; + uint32_t bytes_returned = 0; + uint32_t payload_size = 0; + uint32_t port_index = 0; + uint32_t *afe_cmd = NULL; + uint32_t apr_msg_size = 0; + struct rtac_afe_user_data user_afe_buf; + struct mem_mapping_hdr *mem_hdr = NULL; + struct param_hdr_v1 *get_resp_v2; + struct param_hdr_v3 *get_resp_v3; + + pr_debug("%s\n", __func__); + + if (rtac_cal[AFE_RTAC_CAL].map_data.dma_buf == NULL) { + result = rtac_allocate_cal_buffer(AFE_RTAC_CAL); + if (result < 0) { + pr_err("%s: allocate buffer failed! ret = %d\n", + __func__, result); + goto done; + } + } + + if (rtac_cal[AFE_RTAC_CAL].map_data.map_handle == 0) { + result = rtac_map_cal_buffer(AFE_RTAC_CAL); + if (result < 0) { + pr_err("%s: map buffer failed! ret = %d\n", + __func__, result); + goto done; + } + } + + if (copy_from_user(&user_afe_buf, (void *)buf, + sizeof(struct rtac_afe_user_data))) { + pr_err("%s: Copy from user failed! buf = 0x%pK\n", + __func__, buf); + goto done; + } + + if (user_afe_buf.buf_size <= 0) { + pr_err("%s: Invalid buffer size = %d\n", + __func__, user_afe_buf.buf_size); + goto done; + } + + port_index = q6audio_get_port_index(user_afe_buf.port_id); + if (port_index >= AFE_MAX_PORTS) { + pr_err("%s: Invalid AFE port = 0x%x\n", + __func__, user_afe_buf.port_id); + goto done; + } + + mutex_lock(&rtac_afe_apr_mutex); + if (rtac_afe_apr_data.apr_handle == NULL) { + pr_err("%s: APR not initialized\n", __func__); + result = -EINVAL; + goto err; + } + + afe_cmd = + (u32 *) rtac_afe_buffer + sizeof(struct apr_hdr) / sizeof(u32); + + switch (opcode) { + case AFE_PORT_CMD_SET_PARAM_V2: + apr_msg_size = sizeof(struct afe_port_cmd_set_param_v2); + payload_size = user_afe_buf.v2_set.payload_size; + if (payload_size > rtac_cal[AFE_RTAC_CAL].map_data.map_size) { + pr_err("%s: Invalid payload size = %d\n", __func__, + payload_size); + result = -EINVAL; + goto err; + } + + /* Copy the command to the rtac buffer */ + memcpy(afe_cmd, &user_afe_buf.v2_set, + sizeof(user_afe_buf.v2_set)); + + /* Copy the param data to the out-of-band location */ + if (copy_from_user(rtac_cal[AFE_RTAC_CAL].cal_data.kvaddr, + (void __user *) buf + + offsetof(struct rtac_afe_user_data, + v2_set.param_hdr), + payload_size)) { + pr_err("%s: Could not copy payload from user buffer\n", + __func__); + result = -EFAULT; + goto err; + } + break; + case AFE_PORT_CMD_SET_PARAM_V3: + apr_msg_size = sizeof(struct afe_port_cmd_set_param_v3); + payload_size = user_afe_buf.v3_set.payload_size; + if (payload_size > rtac_cal[AFE_RTAC_CAL].map_data.map_size) { + pr_err("%s: Invalid payload size = %d\n", __func__, + payload_size); + result = -EINVAL; + goto err; + } + + /* Copy the command to the rtac buffer */ + memcpy(afe_cmd, &user_afe_buf.v3_set, + sizeof(user_afe_buf.v3_set)); + + /* Copy the param data to the out-of-band location */ + if (copy_from_user(rtac_cal[AFE_RTAC_CAL].cal_data.kvaddr, + (void __user *) buf + + offsetof(struct rtac_afe_user_data, + v3_set.param_hdr), + payload_size)) { + pr_err("%s: Could not copy payload from user buffer\n", + __func__); + result = -EFAULT; + goto err; + } + break; + case AFE_PORT_CMD_GET_PARAM_V2: + apr_msg_size = sizeof(struct afe_port_cmd_get_param_v2); + + if (user_afe_buf.cmd_size > MAX_PAYLOAD_SIZE) { + pr_err("%s: Invalid payload size = %d\n", __func__, + user_afe_buf.cmd_size); + result = -EINVAL; + goto err; + } + + /* Copy the command and param data in-band */ + if (copy_from_user(afe_cmd, + (void __user *) buf + + offsetof(struct rtac_afe_user_data, + v2_get), + user_afe_buf.cmd_size)) { + pr_err("%s: Could not copy payload from user buffer\n", + __func__); + result = -EFAULT; + goto err; + } + break; + case AFE_PORT_CMD_GET_PARAM_V3: + apr_msg_size = sizeof(struct afe_port_cmd_get_param_v3); + + if (user_afe_buf.cmd_size > MAX_PAYLOAD_SIZE) { + pr_err("%s: Invalid payload size = %d\n", __func__, + user_afe_buf.cmd_size); + result = -EINVAL; + goto err; + } + + /* Copy the command and param data in-band */ + if (copy_from_user(afe_cmd, + (void __user *) buf + + offsetof(struct rtac_afe_user_data, + v3_get), + user_afe_buf.cmd_size)) { + pr_err("%s: Could not copy payload from user buffer\n", + __func__); + result = -EFAULT; + goto err; + } + break; + default: + pr_err("%s: Invalid opcode %d\n", __func__, opcode); + result = -EINVAL; + goto err; + } + + /* + * The memory header is in the same location in all commands. Therefore, + * it doesn't matter what command the buffer is cast into. + */ + mem_hdr = &((struct afe_port_cmd_set_param_v3 *) rtac_afe_buffer) + ->mem_hdr; + mem_hdr->data_payload_addr_lsw = + lower_32_bits(rtac_cal[AFE_RTAC_CAL].cal_data.paddr); + mem_hdr->data_payload_addr_msw = msm_audio_populate_upper_32_bits( + rtac_cal[AFE_RTAC_CAL].cal_data.paddr); + mem_hdr->mem_map_handle = rtac_cal[AFE_RTAC_CAL].map_data.map_handle; + + /* Fill the APR header at the end so we have the correct message size */ + fill_afe_apr_hdr((struct apr_hdr *) rtac_afe_buffer, + port_index, opcode, apr_msg_size); + + atomic_set(&rtac_afe_apr_data.cmd_state, 1); + + pr_debug("%s: Sending RTAC command ioctl 0x%x, paddr 0x%pK\n", + __func__, opcode, + &rtac_cal[AFE_RTAC_CAL].cal_data.paddr); + + result = apr_send_pkt(rtac_afe_apr_data.apr_handle, + (uint32_t *)rtac_afe_buffer); + if (result < 0) { + pr_err("%s: Set params failed port = 0x%x, ret = %d\n", + __func__, user_afe_buf.port_id, result); + goto err; + } + /* Wait for the callback */ + result = wait_event_timeout(rtac_afe_apr_data.cmd_wait, + (atomic_read(&rtac_afe_apr_data.cmd_state) == 0), + msecs_to_jiffies(TIMEOUT_MS)); + if (!result) { + pr_err("%s: Set params timed out port = 0x%x, ret = %d\n", + __func__, user_afe_buf.port_id, result); + goto err; + } + if (atomic_read(&rtac_common.apr_err_code)) { + pr_err("%s: DSP returned error code = [%s], opcode = 0x%x\n", + __func__, adsp_err_get_err_str(atomic_read( + &rtac_common.apr_err_code)), + opcode); + result = adsp_err_get_lnx_err_code( + atomic_read( + &rtac_common.apr_err_code)); + goto err; + } + + if (opcode == AFE_PORT_CMD_GET_PARAM_V2) { + get_resp_v2 = (struct param_hdr_v1 *) rtac_cal[AFE_RTAC_CAL] + .cal_data.kvaddr; + bytes_returned = + get_resp_v2->param_size + sizeof(struct param_hdr_v1); + } else if (opcode == AFE_PORT_CMD_GET_PARAM_V3) { + get_resp_v3 = (struct param_hdr_v3 *) rtac_cal[AFE_RTAC_CAL] + .cal_data.kvaddr; + bytes_returned = + get_resp_v3->param_size + sizeof(struct param_hdr_v3); + } else { + bytes_returned = payload_size; + goto unlock; + } + + if (bytes_returned > rtac_cal[AFE_RTAC_CAL].map_data.map_size) { + pr_err("%s: Invalid data size = %d\n", __func__, + bytes_returned); + result = -EINVAL; + goto err; + } + + if (bytes_returned > user_afe_buf.buf_size) { + pr_err("%s: user size = 0x%x, returned size = 0x%x\n", __func__, + user_afe_buf.buf_size, bytes_returned); + result = -EINVAL; + goto err; + } + + if (copy_to_user((void __user *) buf, + rtac_cal[AFE_RTAC_CAL].cal_data.kvaddr, + bytes_returned)) { + pr_err("%s: Could not copy buffer to user,size = %d\n", + __func__, bytes_returned); + result = -EFAULT; + goto err; + } + +unlock: + mutex_unlock(&rtac_afe_apr_mutex); +done: + return bytes_returned; +err: + mutex_unlock(&rtac_afe_apr_mutex); + return result; +} + +/* Voice APR */ +void rtac_set_voice_handle(u32 mode, void *handle) +{ + pr_debug("%s\n", __func__); + + mutex_lock(&rtac_voice_apr_mutex); + rtac_voice_apr_data[mode].apr_handle = handle; + mutex_unlock(&rtac_voice_apr_mutex); +} + +bool rtac_make_voice_callback(u32 mode, uint32_t *payload, u32 payload_size) +{ + if ((atomic_read(&rtac_voice_apr_data[mode].cmd_state) != 1) || + (mode >= RTAC_VOICE_MODES)) + return false; + + pr_debug("%s\n", __func__); + if (payload_size == sizeof(uint32_t)) + atomic_set(&rtac_common.apr_err_code, payload[0]); + else if (payload_size == (2*sizeof(uint32_t))) + atomic_set(&rtac_common.apr_err_code, payload[1]); + + atomic_set(&rtac_voice_apr_data[mode].cmd_state, 0); + wake_up(&rtac_voice_apr_data[mode].cmd_wait); + return true; +} + +int send_voice_apr(u32 mode, void *buf, u32 opcode) +{ + s32 result; + u32 user_buf_size = 0; + u32 bytes_returned = 0; + u32 payload_size; + u32 dest_port; + u32 data_size = 0; + struct apr_hdr voice_params; + + pr_debug("%s\n", __func__); + + if (rtac_cal[VOICE_RTAC_CAL].map_data.dma_buf == NULL) { + result = rtac_allocate_cal_buffer(VOICE_RTAC_CAL); + if (result < 0) { + pr_err("%s: allocate buffer failed!", + __func__); + goto done; + } + } + + if (rtac_cal[VOICE_RTAC_CAL].map_data.map_handle == 0) { + result = rtac_map_cal_buffer(VOICE_RTAC_CAL); + if (result < 0) { + pr_err("%s: map buffer failed!", + __func__); + goto done; + } + } + + if (copy_from_user(&user_buf_size, (void *)buf, + sizeof(user_buf_size))) { + pr_err("%s: Copy from user failed! buf = 0x%pK\n", + __func__, buf); + goto done; + } + if (user_buf_size <= 0) { + pr_err("%s: Invalid buffer size = %d\n", + __func__, user_buf_size); + goto done; + } + + if (copy_from_user(&payload_size, buf + sizeof(u32), sizeof(u32))) { + pr_err("%s: Could not copy payload size from user buffer\n", + __func__); + goto done; + } + + if (copy_from_user(&dest_port, buf + 2 * sizeof(u32), sizeof(u32))) { + pr_err("%s: Could not copy port id from user buffer\n", + __func__); + goto done; + } + + if ((mode != RTAC_CVP) && (mode != RTAC_CVS)) { + pr_err("%s: Invalid Mode for APR, mode = %d\n", + __func__, mode); + goto done; + } + + mutex_lock(&rtac_voice_apr_mutex); + if (rtac_voice_apr_data[mode].apr_handle == NULL) { + pr_err("%s: APR not initialized\n", __func__); + result = -EINVAL; + goto err; + } + + switch (opcode) { + case VSS_ICOMMON_CMD_SET_PARAM_V2: + case VSS_ICOMMON_CMD_SET_PARAM_V3: + /* set payload size to in-band payload */ + /* set data size to actual out of band payload size */ + data_size = payload_size - 4 * sizeof(u32); + if (data_size > rtac_cal[VOICE_RTAC_CAL].map_data.map_size) { + pr_err("%s: Invalid data size = %d\n", + __func__, data_size); + result = -EINVAL; + goto err; + } + payload_size = 4 * sizeof(u32); + + /* Copy buffer to out-of-band payload */ + if (copy_from_user((void *) + rtac_cal[VOICE_RTAC_CAL].cal_data.kvaddr, + buf + 7 * sizeof(u32), data_size)) { + pr_err("%s: Could not copy payload from user buffer\n", + __func__); + result = -EFAULT; + goto err; + } + /* set payload size in packet */ + rtac_voice_buffer[8] = data_size; + /* set token for set param case */ + voice_params.token = VOC_RTAC_SET_PARAM_TOKEN; + break; + case VSS_ICOMMON_CMD_GET_PARAM_V2: + case VSS_ICOMMON_CMD_GET_PARAM_V3: + if (payload_size > MAX_PAYLOAD_SIZE) { + pr_err("%s: Invalid payload size = %d\n", + __func__, payload_size); + result = -EINVAL; + goto err; + } + + /* Copy buffer to in-band payload */ + if (copy_from_user(rtac_voice_buffer + + sizeof(voice_params)/sizeof(u32), + buf + 3 * sizeof(u32), payload_size)) { + pr_err("%s: Could not copy payload from user buffer\n", + __func__); + result = -EFAULT; + goto err; + } + /* set token for get param case */ + voice_params.token = 0; + break; + default: + pr_err("%s: Invalid opcode %d\n", __func__, opcode); + result = -EINVAL; + goto err; + } + + /* Pack header */ + voice_params.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(20), APR_PKT_VER); + voice_params.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + payload_size); + voice_params.src_svc = 0; + voice_params.src_domain = APR_DOMAIN_APPS; + voice_params.src_port = get_voice_index(mode, dest_port); + voice_params.dest_svc = 0; + voice_params.dest_domain = APR_DOMAIN_MODEM; + voice_params.dest_port = (u16)dest_port; + voice_params.opcode = opcode; + + /* fill for out-of-band */ + rtac_voice_buffer[5] = rtac_cal[VOICE_RTAC_CAL].map_data.map_handle; + rtac_voice_buffer[6] = + lower_32_bits(rtac_cal[VOICE_RTAC_CAL].cal_data.paddr); + rtac_voice_buffer[7] = msm_audio_populate_upper_32_bits( + rtac_cal[VOICE_RTAC_CAL].cal_data.paddr); + + memcpy(rtac_voice_buffer, &voice_params, sizeof(voice_params)); + atomic_set(&rtac_voice_apr_data[mode].cmd_state, 1); + + pr_debug("%s: Sending RTAC command ioctl 0x%x, paddr 0x%pK\n", + __func__, opcode, + &rtac_cal[VOICE_RTAC_CAL].cal_data.paddr); + + result = apr_send_pkt(rtac_voice_apr_data[mode].apr_handle, + (uint32_t *)rtac_voice_buffer); + if (result < 0) { + pr_err("%s: apr_send_pkt failed opcode = %x\n", + __func__, opcode); + goto err; + } + /* Wait for the callback */ + result = wait_event_timeout(rtac_voice_apr_data[mode].cmd_wait, + (atomic_read(&rtac_voice_apr_data[mode].cmd_state) == 0), + msecs_to_jiffies(TIMEOUT_MS)); + if (!result) { + pr_err("%s: apr_send_pkt timed out opcode = %x\n", + __func__, opcode); + goto err; + } + if (atomic_read(&rtac_common.apr_err_code)) { + pr_err("%s: DSP returned error code = [%s], opcode = 0x%x\n", + __func__, adsp_err_get_err_str(atomic_read( + &rtac_common.apr_err_code)), + opcode); + result = adsp_err_get_lnx_err_code( + atomic_read( + &rtac_common.apr_err_code)); + goto err; + } + + if (opcode == VSS_ICOMMON_CMD_GET_PARAM_V2) { + bytes_returned = ((u32 *)rtac_cal[VOICE_RTAC_CAL].cal_data. + kvaddr)[2] + 3 * sizeof(u32); + } else if (opcode == VSS_ICOMMON_CMD_GET_PARAM_V3) { + bytes_returned = + ((u32 *) rtac_cal[VOICE_RTAC_CAL].cal_data.kvaddr)[3] + + 4 * sizeof(u32); + } else { + bytes_returned = data_size; + goto unlock; + } + + if (bytes_returned > rtac_cal[VOICE_RTAC_CAL].map_data.map_size) { + pr_err("%s: Invalid data size = %d\n", __func__, + bytes_returned); + result = -EINVAL; + goto err; + } + + if (bytes_returned > user_buf_size) { + pr_err("%s: User buf not big enough, size = 0x%x, returned size = 0x%x\n", + __func__, user_buf_size, bytes_returned); + result = -EINVAL; + goto err; + } + + if (copy_to_user((void __user *) buf, + rtac_cal[VOICE_RTAC_CAL].cal_data.kvaddr, + bytes_returned)) { + pr_err("%s: Could not copy buffer to user, size = %d\n", + __func__, bytes_returned); + result = -EFAULT; + goto err; + } + +unlock: + mutex_unlock(&rtac_voice_apr_mutex); +done: + return bytes_returned; +err: + mutex_unlock(&rtac_voice_apr_mutex); + return result; +} + +void get_rtac_adm_data(struct rtac_adm *adm_data) +{ + mutex_lock(&rtac_adm_mutex); + memcpy(adm_data, &rtac_adm_data, sizeof(struct rtac_adm)); + mutex_unlock(&rtac_adm_mutex); +} + + +static long rtac_ioctl_shared(struct file *f, + unsigned int cmd, void *arg) +{ + u32 opcode; + int result = 0; + if (!arg) { + pr_err("%s: No data sent to driver!\n", __func__); + result = -EFAULT; + goto done; + } + + switch (cmd) { + case AUDIO_GET_RTAC_ADM_INFO: { + mutex_lock(&rtac_adm_mutex); + if (copy_to_user((void *)arg, &rtac_adm_data, + sizeof(rtac_adm_data))) { + pr_err("%s: copy_to_user failed for AUDIO_GET_RTAC_ADM_INFO\n", + __func__); + mutex_unlock(&rtac_adm_mutex); + return -EFAULT; + } + result = sizeof(rtac_adm_data); + mutex_unlock(&rtac_adm_mutex); + break; + } + case AUDIO_GET_RTAC_VOICE_INFO: { + mutex_lock(&rtac_voice_mutex); + if (copy_to_user((void *)arg, &rtac_voice_data, + sizeof(rtac_voice_data))) { + pr_err("%s: copy_to_user failed for AUDIO_GET_RTAC_VOICE_INFO\n", + __func__); + mutex_unlock(&rtac_voice_mutex); + return -EFAULT; + } + result = sizeof(rtac_voice_data); + mutex_unlock(&rtac_voice_mutex); + break; + } + + case AUDIO_GET_RTAC_ADM_CAL: + opcode = q6common_is_instance_id_supported() ? + ADM_CMD_GET_PP_PARAMS_V6 : + ADM_CMD_GET_PP_PARAMS_V5; + result = send_adm_apr((void *) arg, opcode); + break; + case AUDIO_SET_RTAC_ADM_CAL: + opcode = q6common_is_instance_id_supported() ? + ADM_CMD_SET_PP_PARAMS_V6 : + ADM_CMD_SET_PP_PARAMS_V5; + result = send_adm_apr((void *) arg, opcode); + break; + case AUDIO_GET_RTAC_ASM_CAL: + opcode = q6common_is_instance_id_supported() ? + ASM_STREAM_CMD_GET_PP_PARAMS_V3 : + ASM_STREAM_CMD_GET_PP_PARAMS_V2; + result = send_rtac_asm_apr((void *) arg, opcode); + break; + case AUDIO_SET_RTAC_ASM_CAL: + opcode = q6common_is_instance_id_supported() ? + ASM_STREAM_CMD_SET_PP_PARAMS_V3 : + ASM_STREAM_CMD_SET_PP_PARAMS_V2; + result = send_rtac_asm_apr((void *) arg, opcode); + break; + case AUDIO_GET_RTAC_CVS_CAL: + opcode = q6common_is_instance_id_supported() ? + VSS_ICOMMON_CMD_GET_PARAM_V3 : + VSS_ICOMMON_CMD_GET_PARAM_V2; + result = send_voice_apr(RTAC_CVS, (void *) arg, opcode); + break; + case AUDIO_SET_RTAC_CVS_CAL: + opcode = q6common_is_instance_id_supported() ? + VSS_ICOMMON_CMD_SET_PARAM_V3 : + VSS_ICOMMON_CMD_SET_PARAM_V2; + result = send_voice_apr(RTAC_CVS, (void *) arg, opcode); + break; + case AUDIO_GET_RTAC_CVP_CAL: + opcode = q6common_is_instance_id_supported() ? + VSS_ICOMMON_CMD_GET_PARAM_V3 : + VSS_ICOMMON_CMD_GET_PARAM_V2; + result = send_voice_apr(RTAC_CVP, (void *) arg, opcode); + break; + case AUDIO_SET_RTAC_CVP_CAL: + opcode = q6common_is_instance_id_supported() ? + VSS_ICOMMON_CMD_SET_PARAM_V3 : + VSS_ICOMMON_CMD_SET_PARAM_V2; + result = send_voice_apr(RTAC_CVP, (void *) arg, opcode); + break; + case AUDIO_GET_RTAC_AFE_CAL: + opcode = q6common_is_instance_id_supported() ? + AFE_PORT_CMD_GET_PARAM_V3 : + AFE_PORT_CMD_GET_PARAM_V2; + result = send_rtac_afe_apr((void __user *) arg, opcode); + break; + case AUDIO_SET_RTAC_AFE_CAL: + opcode = q6common_is_instance_id_supported() ? + AFE_PORT_CMD_SET_PARAM_V3 : + AFE_PORT_CMD_SET_PARAM_V2; + result = send_rtac_afe_apr((void __user *) arg, opcode); + break; + default: + pr_err("%s: Invalid IOCTL, command = %d!\n", + __func__, cmd); + result = -EINVAL; + } +done: + return result; +} + +static long rtac_ioctl(struct file *f, + unsigned int cmd, unsigned long arg) +{ + int result = 0; + + mutex_lock(&rtac_common.rtac_fops_mutex); + if (!arg) { + pr_err("%s: No data sent to driver!\n", __func__); + result = -EFAULT; + } else { + result = rtac_ioctl_shared(f, cmd, (void __user *)arg); + } + + mutex_unlock(&rtac_common.rtac_fops_mutex); + return result; +} + +#ifdef CONFIG_COMPAT +#define AUDIO_GET_RTAC_ADM_INFO_32 _IOR(CAL_IOCTL_MAGIC, 207, compat_uptr_t) +#define AUDIO_GET_RTAC_VOICE_INFO_32 _IOR(CAL_IOCTL_MAGIC, 208, compat_uptr_t) +#define AUDIO_GET_RTAC_ADM_CAL_32 _IOWR(CAL_IOCTL_MAGIC, 209, compat_uptr_t) +#define AUDIO_SET_RTAC_ADM_CAL_32 _IOWR(CAL_IOCTL_MAGIC, 210, compat_uptr_t) +#define AUDIO_GET_RTAC_ASM_CAL_32 _IOWR(CAL_IOCTL_MAGIC, 211, compat_uptr_t) +#define AUDIO_SET_RTAC_ASM_CAL_32 _IOWR(CAL_IOCTL_MAGIC, 212, compat_uptr_t) +#define AUDIO_GET_RTAC_CVS_CAL_32 _IOWR(CAL_IOCTL_MAGIC, 213, compat_uptr_t) +#define AUDIO_SET_RTAC_CVS_CAL_32 _IOWR(CAL_IOCTL_MAGIC, 214, compat_uptr_t) +#define AUDIO_GET_RTAC_CVP_CAL_32 _IOWR(CAL_IOCTL_MAGIC, 215, compat_uptr_t) +#define AUDIO_SET_RTAC_CVP_CAL_32 _IOWR(CAL_IOCTL_MAGIC, 216, compat_uptr_t) +#define AUDIO_GET_RTAC_AFE_CAL_32 _IOWR(CAL_IOCTL_MAGIC, 217, compat_uptr_t) +#define AUDIO_SET_RTAC_AFE_CAL_32 _IOWR(CAL_IOCTL_MAGIC, 218, compat_uptr_t) + +static long rtac_compat_ioctl(struct file *f, + unsigned int cmd, unsigned long arg) +{ + int result = 0; + + mutex_lock(&rtac_common.rtac_fops_mutex); + if (!arg) { + pr_err("%s: No data sent to driver!\n", __func__); + result = -EINVAL; + goto done; + } + + switch (cmd) { + case AUDIO_GET_RTAC_ADM_INFO_32: + cmd = AUDIO_GET_RTAC_ADM_INFO; + goto process; + case AUDIO_GET_RTAC_VOICE_INFO_32: + cmd = AUDIO_GET_RTAC_VOICE_INFO; + goto process; + case AUDIO_GET_RTAC_AFE_CAL_32: + cmd = AUDIO_GET_RTAC_AFE_CAL; + goto process; + case AUDIO_SET_RTAC_AFE_CAL_32: + cmd = AUDIO_SET_RTAC_AFE_CAL; + goto process; + case AUDIO_GET_RTAC_ADM_CAL_32: + cmd = AUDIO_GET_RTAC_ADM_CAL; + goto process; + case AUDIO_SET_RTAC_ADM_CAL_32: + cmd = AUDIO_SET_RTAC_ADM_CAL; + goto process; + case AUDIO_GET_RTAC_ASM_CAL_32: + cmd = AUDIO_GET_RTAC_ASM_CAL; + goto process; + case AUDIO_SET_RTAC_ASM_CAL_32: + cmd = AUDIO_SET_RTAC_ASM_CAL; + goto process; + case AUDIO_GET_RTAC_CVS_CAL_32: + cmd = AUDIO_GET_RTAC_CVS_CAL; + goto process; + case AUDIO_SET_RTAC_CVS_CAL_32: + cmd = AUDIO_SET_RTAC_CVS_CAL; + goto process; + case AUDIO_GET_RTAC_CVP_CAL_32: + cmd = AUDIO_GET_RTAC_CVP_CAL; + goto process; + case AUDIO_SET_RTAC_CVP_CAL_32: + cmd = AUDIO_SET_RTAC_CVP_CAL; +process: + result = rtac_ioctl_shared(f, cmd, compat_ptr(arg)); + break; + default: + result = -EINVAL; + pr_err("%s: Invalid IOCTL, command = %d!\n", + __func__, cmd); + break; + } +done: + mutex_unlock(&rtac_common.rtac_fops_mutex); + return result; +} +#else +#define rtac_compat_ioctl NULL +#endif + +static const struct file_operations rtac_fops = { + .owner = THIS_MODULE, + .open = rtac_open, + .release = rtac_release, + .unlocked_ioctl = rtac_ioctl, + .compat_ioctl = rtac_compat_ioctl, +}; + +struct miscdevice rtac_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_rtac", + .fops = &rtac_fops, +}; + +int __init rtac_init(void) +{ + int i = 0; + + /* Driver */ + atomic_set(&rtac_common.usage_count, 0); + atomic_set(&rtac_common.apr_err_code, 0); + mutex_init(&rtac_common.rtac_fops_mutex); + + /* ADM */ + memset(&rtac_adm_data, 0, sizeof(rtac_adm_data)); + rtac_adm_apr_data.apr_handle = NULL; + atomic_set(&rtac_adm_apr_data.cmd_state, 0); + init_waitqueue_head(&rtac_adm_apr_data.cmd_wait); + mutex_init(&rtac_adm_mutex); + mutex_init(&rtac_adm_apr_mutex); + + rtac_adm_buffer = kzalloc( + rtac_cal[ADM_RTAC_CAL].map_data.map_size, GFP_KERNEL); + if (rtac_adm_buffer == NULL) + goto nomem; + + /* ASM */ + for (i = 0; i < ASM_ACTIVE_STREAMS_ALLOWED+1; i++) { + rtac_asm_apr_data[i].apr_handle = NULL; + atomic_set(&rtac_asm_apr_data[i].cmd_state, 0); + init_waitqueue_head(&rtac_asm_apr_data[i].cmd_wait); + } + mutex_init(&rtac_asm_apr_mutex); + + rtac_asm_buffer = kzalloc( + rtac_cal[ASM_RTAC_CAL].map_data.map_size, GFP_KERNEL); + if (rtac_asm_buffer == NULL) { + kzfree(rtac_adm_buffer); + goto nomem; + } + + /* AFE */ + rtac_afe_apr_data.apr_handle = NULL; + atomic_set(&rtac_afe_apr_data.cmd_state, 0); + init_waitqueue_head(&rtac_afe_apr_data.cmd_wait); + mutex_init(&rtac_afe_apr_mutex); + + rtac_afe_buffer = kzalloc( + rtac_cal[AFE_RTAC_CAL].map_data.map_size, GFP_KERNEL); + if (rtac_afe_buffer == NULL) { + kzfree(rtac_adm_buffer); + kzfree(rtac_asm_buffer); + goto nomem; + } + + /* Voice */ + memset(&rtac_voice_data, 0, sizeof(rtac_voice_data)); + for (i = 0; i < RTAC_VOICE_MODES; i++) { + rtac_voice_apr_data[i].apr_handle = NULL; + atomic_set(&rtac_voice_apr_data[i].cmd_state, 0); + init_waitqueue_head(&rtac_voice_apr_data[i].cmd_wait); + } + mutex_init(&rtac_voice_mutex); + mutex_init(&rtac_voice_apr_mutex); + + rtac_voice_buffer = kzalloc( + rtac_cal[VOICE_RTAC_CAL].map_data.map_size, GFP_KERNEL); + if (rtac_voice_buffer == NULL) { + kzfree(rtac_adm_buffer); + kzfree(rtac_asm_buffer); + kzfree(rtac_afe_buffer); + goto nomem; + } + + if (misc_register(&rtac_misc) != 0) { + kzfree(rtac_adm_buffer); + kzfree(rtac_asm_buffer); + kzfree(rtac_afe_buffer); + kzfree(rtac_voice_buffer); + goto nomem; + } + + return 0; +nomem: + return -ENOMEM; +} + +void rtac_exit(void) +{ + misc_deregister(&rtac_misc); + kzfree(rtac_adm_buffer); + kzfree(rtac_asm_buffer); + kzfree(rtac_afe_buffer); + kzfree(rtac_voice_buffer); +} + +MODULE_DESCRIPTION("SoC QDSP6v2 Real-Time Audio Calibration driver"); +MODULE_LICENSE("GPL v2"); diff --git a/audio-kernel/dsp/sp_params.c b/audio-kernel/dsp/sp_params.c new file mode 100644 index 000000000..2abb82e0d --- /dev/null +++ b/audio-kernel/dsp/sp_params.c @@ -0,0 +1,434 @@ +/* + * Copyright (c) 2019, 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 +#include +#include +#include +#include +#include +#include + +/* export or show spk params at /sys/class/spk_params/cal_data */ +#define SPK_PARAMS "spk_params" +#define CLASS_NAME "cal_data" +#define BUF_SZ 20 +#define Q22 (1<<22) +#define Q13 (1<<13) +#define Q7 (1<<7) + +struct afe_spk_ctl { + struct class *p_class; + struct device *p_dev; + struct afe_sp_rx_tmax_xmax_logging_param xt_logging; + int32_t max_temperature_rd[SP_V2_NUM_MAX_SPKR]; +}; +struct afe_spk_ctl this_afe_spk; + +static ssize_t sp_count_exceeded_temperature_l_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + ssize_t ret = 0; + + ret = snprintf(buf, BUF_SZ, "%d\n", + this_afe_spk.xt_logging.count_exceeded_temperature[SP_V2_SPKR_1]); + this_afe_spk.xt_logging.count_exceeded_temperature[SP_V2_SPKR_1] = 0; + return ret; +} +static DEVICE_ATTR(count_exceeded_temperature, 0644, + sp_count_exceeded_temperature_l_show, NULL); + +static ssize_t sp_count_exceeded_temperature_r_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + ssize_t ret = 0; + + ret = snprintf(buf, BUF_SZ, "%d\n", + this_afe_spk.xt_logging.count_exceeded_temperature[SP_V2_SPKR_2]); + this_afe_spk.xt_logging.count_exceeded_temperature[SP_V2_SPKR_2] = 0; + return ret; +} +static DEVICE_ATTR(count_exceeded_temperature_r, 0644, + sp_count_exceeded_temperature_r_show, NULL); + +static ssize_t sp_count_exceeded_excursion_l_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + ssize_t ret = 0; + + ret = snprintf(buf, BUF_SZ, "%d\n", + this_afe_spk.xt_logging.count_exceeded_excursion[SP_V2_SPKR_1]); + this_afe_spk.xt_logging.count_exceeded_excursion[SP_V2_SPKR_1] = 0; + return ret; +} +static DEVICE_ATTR(count_exceeded_excursion, 0644, + sp_count_exceeded_excursion_l_show, NULL); + +static ssize_t sp_count_exceeded_excursion_r_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + ssize_t ret = 0; + + ret = snprintf(buf, BUF_SZ, "%d\n", + this_afe_spk.xt_logging.count_exceeded_excursion[SP_V2_SPKR_2]); + this_afe_spk.xt_logging.count_exceeded_excursion[SP_V2_SPKR_2] = 0; + return ret; +} +static DEVICE_ATTR(count_exceeded_excursion_r, 0644, + sp_count_exceeded_excursion_r_show, NULL); + +static ssize_t sp_max_excursion_l_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + ssize_t ret = 0; + int32_t ex_val_frac; + int32_t ex_q27 = this_afe_spk.xt_logging.max_excursion[SP_V2_SPKR_1]; + + ex_val_frac = ex_q27/Q13; + ex_val_frac = (ex_val_frac * 10000)/(Q7 * Q7); + ex_val_frac /= 100; + ret = snprintf(buf, BUF_SZ, "%d.%02d\n", 0, ex_val_frac); + this_afe_spk.xt_logging.max_excursion[SP_V2_SPKR_1] = 0; + return ret; +} +static DEVICE_ATTR(max_excursion, 0644, sp_max_excursion_l_show, NULL); + +static ssize_t sp_max_excursion_r_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + ssize_t ret = 0; + int32_t ex_val_frac; + int32_t ex_q27 = this_afe_spk.xt_logging.max_excursion[SP_V2_SPKR_2]; + + ex_val_frac = ex_q27/Q13; + ex_val_frac = (ex_val_frac * 10000)/(Q7 * Q7); + ex_val_frac /= 100; + ret = snprintf(buf, BUF_SZ, "%d.%02d\n", 0, ex_val_frac); + this_afe_spk.xt_logging.max_excursion[SP_V2_SPKR_2] = 0; + return ret; +} +static DEVICE_ATTR(max_excursion_r, 0644, sp_max_excursion_r_show, NULL); + +static ssize_t sp_max_temperature_l_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + ssize_t ret = 0; + + ret = snprintf(buf, BUF_SZ, "%d\n", + this_afe_spk.xt_logging.max_temperature[SP_V2_SPKR_1]/Q22); + this_afe_spk.xt_logging.max_temperature[SP_V2_SPKR_1] = 0; + return ret; +} +static DEVICE_ATTR(max_temperature, 0644, sp_max_temperature_l_show, NULL); + +static ssize_t sp_max_temperature_r_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + ssize_t ret = 0; + + ret = snprintf(buf, BUF_SZ, "%d\n", + this_afe_spk.xt_logging.max_temperature[SP_V2_SPKR_2]/Q22); + this_afe_spk.xt_logging.max_temperature[SP_V2_SPKR_2] = 0; + return ret; +} +static DEVICE_ATTR(max_temperature_r, 0644, sp_max_temperature_r_show, NULL); + +static ssize_t sp_max_temperature_rd_l_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return snprintf(buf, BUF_SZ, "%d\n", + this_afe_spk.max_temperature_rd[SP_V2_SPKR_1]/Q22); +} +static DEVICE_ATTR(max_temperature_rd, 0644, + sp_max_temperature_rd_l_show, NULL); + +static ssize_t sp_max_temperature_rd_r_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return snprintf(buf, BUF_SZ, "%d\n", + this_afe_spk.max_temperature_rd[SP_V2_SPKR_2]/Q22); +} +static DEVICE_ATTR(max_temperature_rd_r, 0644, + sp_max_temperature_rd_r_show, NULL); + +static ssize_t q6afe_initial_cal_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return snprintf(buf, BUF_SZ, "%d\n", afe_get_spk_initial_cal()); +} + +static ssize_t q6afe_initial_cal_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int initial_cal = 0; + + if (!kstrtou32(buf, 0, &initial_cal)) { + initial_cal = initial_cal > 0 ? 1 : 0; + if (initial_cal == afe_get_spk_initial_cal()) + dev_dbg(dev, "%s: same value already present\n", + __func__); + else + afe_set_spk_initial_cal(initial_cal); + } + return size; +} + +static DEVICE_ATTR(initial_cal, 0644, + q6afe_initial_cal_show, q6afe_initial_cal_store); + +static ssize_t q6afe_v_vali_flag_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return snprintf(buf, BUF_SZ, "%d\n", afe_get_spk_v_vali_flag()); +} + +static ssize_t q6afe_v_vali_flag_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int v_vali_flag = 0; + + if (!kstrtou32(buf, 0, &v_vali_flag)) { + v_vali_flag = v_vali_flag > 0 ? 1 : 0; + if (v_vali_flag == afe_get_spk_v_vali_flag()) + dev_dbg(dev, "%s: same value already present\n", + __func__); + else + afe_set_spk_v_vali_flag(v_vali_flag); + } + return size; +} + +static DEVICE_ATTR(v_vali_flag, 0644, + q6afe_v_vali_flag_show, q6afe_v_vali_flag_store); + +static ssize_t q6afe_spk_r0_l_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int r0[SP_V2_NUM_MAX_SPKRS]; + + afe_get_spk_r0(r0); + return snprintf(buf, BUF_SZ, "%d\n", r0[SP_V2_SPKR_1]); +} + +static DEVICE_ATTR(spk_r0, 0644, q6afe_spk_r0_l_show, NULL); + +static ssize_t q6afe_spk_t0_l_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int t0[SP_V2_NUM_MAX_SPKRS]; + + afe_get_spk_t0(t0); + return snprintf(buf, BUF_SZ, "%d\n", t0[SP_V2_SPKR_1]); +} + +static DEVICE_ATTR(spk_t0, 0644, q6afe_spk_t0_l_show, NULL); + +static ssize_t q6afe_spk_r0_r_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int r0[SP_V2_NUM_MAX_SPKRS]; + + afe_get_spk_r0(r0); + return snprintf(buf, BUF_SZ, "%d\n", r0[SP_V2_SPKR_2]); +} + +static DEVICE_ATTR(spk_r0_r, 0644, q6afe_spk_r0_r_show, NULL); + +static ssize_t q6afe_spk_t0_r_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int t0[SP_V2_NUM_MAX_SPKRS]; + + afe_get_spk_t0(t0); + return snprintf(buf, BUF_SZ, "%d\n", t0[SP_V2_SPKR_2]); +} + +static DEVICE_ATTR(spk_t0_r, 0644, q6afe_spk_t0_r_show, NULL); + +static ssize_t q6afe_spk_v_vali_l_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int v_vali_sts[SP_V2_NUM_MAX_SPKRS]; + + afe_get_spk_v_vali_sts(v_vali_sts); + return snprintf(buf, BUF_SZ, "%d\n", v_vali_sts[SP_V2_SPKR_1]); +} + +static DEVICE_ATTR(spk_v_vali_status, 0644, q6afe_spk_v_vali_l_show, NULL); + +static ssize_t q6afe_spk_v_vali_r_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int v_vali_sts[SP_V2_NUM_MAX_SPKRS]; + + afe_get_spk_v_vali_sts(v_vali_sts); + return snprintf(buf, BUF_SZ, "%d\n", v_vali_sts[SP_V2_SPKR_2]); +} + +static DEVICE_ATTR(spk_v_vali_r_status, 0644, q6afe_spk_v_vali_r_show, NULL); + +static struct attribute *afe_spk_cal_attr[] = { + &dev_attr_max_excursion.attr, + &dev_attr_max_excursion_r.attr, + &dev_attr_max_temperature.attr, + &dev_attr_max_temperature_r.attr, + &dev_attr_count_exceeded_excursion.attr, + &dev_attr_count_exceeded_excursion_r.attr, + &dev_attr_count_exceeded_temperature.attr, + &dev_attr_count_exceeded_temperature_r.attr, + &dev_attr_max_temperature_rd.attr, + &dev_attr_max_temperature_rd_r.attr, + &dev_attr_initial_cal.attr, + &dev_attr_spk_r0.attr, + &dev_attr_spk_t0.attr, + &dev_attr_spk_r0_r.attr, + &dev_attr_spk_t0_r.attr, + &dev_attr_v_vali_flag.attr, + &dev_attr_spk_v_vali_status.attr, + &dev_attr_spk_v_vali_r_status.attr, + NULL, +}; + +static struct attribute_group afe_spk_cal_attr_grp = { + .attrs = afe_spk_cal_attr, +}; + + +/** + * afe_get_sp_xt_logging_data - + * to get excursion logging data from DSP + * + * @port: AFE port ID + * + * Returns 0 on success or error on failure + */ +int afe_get_sp_xt_logging_data(u16 port_id) +{ + int ret = 0; + struct afe_sp_rx_tmax_xmax_logging_param xt_logging_data; + + ret = afe_get_sp_rx_tmax_xmax_logging_data(&xt_logging_data, port_id); + if (ret) { + pr_err("%s Excursion logging fail\n", __func__); + return ret; + } + /* storing max sp param value */ + if (this_afe_spk.xt_logging.max_temperature[SP_V2_SPKR_1] < + xt_logging_data.max_temperature[SP_V2_SPKR_1]) + this_afe_spk.xt_logging.max_temperature[SP_V2_SPKR_1] = + xt_logging_data.max_temperature[SP_V2_SPKR_1]; + + + if (this_afe_spk.xt_logging.max_temperature[SP_V2_SPKR_2] < + xt_logging_data.max_temperature[SP_V2_SPKR_2]) + this_afe_spk.xt_logging.max_temperature[SP_V2_SPKR_2] = + xt_logging_data.max_temperature[SP_V2_SPKR_2]; + + + /* update temp for max_temperature_rd node */ + if (this_afe_spk.max_temperature_rd[SP_V2_SPKR_1] < + xt_logging_data.max_temperature[SP_V2_SPKR_1]) + this_afe_spk.max_temperature_rd[SP_V2_SPKR_1] = + xt_logging_data.max_temperature[SP_V2_SPKR_1]; + + if (this_afe_spk.max_temperature_rd[SP_V2_SPKR_2] < + xt_logging_data.max_temperature[SP_V2_SPKR_2]) + this_afe_spk.max_temperature_rd[SP_V2_SPKR_2] = + xt_logging_data.max_temperature[SP_V2_SPKR_2]; + + + if (this_afe_spk.xt_logging.max_excursion[SP_V2_SPKR_1] < + xt_logging_data.max_excursion[SP_V2_SPKR_1]) + this_afe_spk.xt_logging.max_excursion[SP_V2_SPKR_1] = + xt_logging_data.max_excursion[SP_V2_SPKR_1]; + + if (this_afe_spk.xt_logging.max_excursion[SP_V2_SPKR_2] < + xt_logging_data.max_excursion[SP_V2_SPKR_2]) + this_afe_spk.xt_logging.max_excursion[SP_V2_SPKR_2] = + xt_logging_data.max_excursion[SP_V2_SPKR_2]; + + if (this_afe_spk.xt_logging.count_exceeded_temperature[SP_V2_SPKR_1] < + xt_logging_data.count_exceeded_temperature[SP_V2_SPKR_1]) + this_afe_spk.xt_logging.count_exceeded_temperature[SP_V2_SPKR_1] + += xt_logging_data.count_exceeded_temperature[SP_V2_SPKR_1]; + + if (this_afe_spk.xt_logging.count_exceeded_temperature[SP_V2_SPKR_2] < + xt_logging_data.count_exceeded_temperature[SP_V2_SPKR_2]) + this_afe_spk.xt_logging.count_exceeded_temperature[SP_V2_SPKR_2] + += xt_logging_data.count_exceeded_temperature[SP_V2_SPKR_2]; + + if (this_afe_spk.xt_logging.count_exceeded_excursion[SP_V2_SPKR_1] < + xt_logging_data.count_exceeded_excursion[SP_V2_SPKR_1]) + this_afe_spk.xt_logging.count_exceeded_excursion[SP_V2_SPKR_1] + += xt_logging_data.count_exceeded_excursion[SP_V2_SPKR_1]; + + if (this_afe_spk.xt_logging.count_exceeded_excursion[SP_V2_SPKR_2] < + xt_logging_data.count_exceeded_excursion[SP_V2_SPKR_2]) + this_afe_spk.xt_logging.count_exceeded_excursion[SP_V2_SPKR_2] + += xt_logging_data.count_exceeded_excursion[SP_V2_SPKR_2]; + + return ret; +} +EXPORT_SYMBOL(afe_get_sp_xt_logging_data); + +int __init spk_params_init(void) +{ + /* initialize xt param value with 0 */ + this_afe_spk.xt_logging.max_temperature[SP_V2_SPKR_1] = 0; + this_afe_spk.xt_logging.max_temperature[SP_V2_SPKR_2] = 0; + this_afe_spk.xt_logging.max_excursion[SP_V2_SPKR_1] = 0; + this_afe_spk.xt_logging.max_excursion[SP_V2_SPKR_2] = 0; + this_afe_spk.xt_logging.count_exceeded_temperature[SP_V2_SPKR_1] = 0; + this_afe_spk.xt_logging.count_exceeded_temperature[SP_V2_SPKR_2] = 0; + this_afe_spk.xt_logging.count_exceeded_excursion[SP_V2_SPKR_1] = 0; + this_afe_spk.xt_logging.count_exceeded_excursion[SP_V2_SPKR_2] = 0; + + this_afe_spk.p_class = class_create(THIS_MODULE, SPK_PARAMS); + if (this_afe_spk.p_class) { + this_afe_spk.p_dev = device_create(this_afe_spk.p_class, NULL, + 1, NULL, CLASS_NAME); + if (!IS_ERR(this_afe_spk.p_dev)) { + if (sysfs_create_group(&this_afe_spk.p_dev->kobj, + &afe_spk_cal_attr_grp)) + pr_err("%s: Failed to create sysfs group\n", + __func__); + } + } + return 0; +} + +void spk_params_exit(void) +{ + pr_debug("%s\n", __func__); +} diff --git a/audio-kernel/dsp/usf.c b/audio-kernel/dsp/usf.c new file mode 100644 index 000000000..263ba1f8b --- /dev/null +++ b/audio-kernel/dsp/usf.c @@ -0,0 +1,2474 @@ +/* Copyright (c) 2011-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 + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "q6usm.h" +#include "usf.h" +#include "usfcdev.h" +#include "q6_init.h" + +/* The driver version*/ +#define DRV_VERSION "1.7.1" +#define USF_VERSION_ID 0x0171 + +/* Standard timeout in the asynchronous ops */ +#define USF_TIMEOUT_JIFFIES (1*HZ) /* 1 sec */ + +/* Undefined USF device */ +#define USF_UNDEF_DEV_ID 0xffff + +/* TX memory mapping flag */ +#define USF_VM_READ 1 +/* RX memory mapping flag */ +#define USF_VM_WRITE 2 + +/* Number of events, copied from the user space to kernel one */ +#define USF_EVENTS_PORTION_SIZE 20 + +/* Indexes in range definitions */ +#define MIN_IND 0 +#define MAX_IND 1 + +/* The coordinates indexes */ +#define X_IND 0 +#define Y_IND 1 +#define Z_IND 2 + +/* Shared memory limits */ +/* max_buf_size = (port_size(65535*2) * port_num(8) * group_size(3) */ +#define USF_MAX_BUF_SIZE 3145680 +#define USF_MAX_BUF_NUM 32 + +/* max size for buffer set from user space */ +#define USF_MAX_USER_BUF_SIZE 100000 + +/* Place for opreation result, received from QDSP6 */ +#define APR_RESULT_IND 1 + +/* Place for US detection result, received from QDSP6 */ +#define APR_US_DETECT_RESULT_IND 0 + +#define BITS_IN_BYTE 8 + +/* Time to stay awake after tx read event (e.g., proximity) */ +#define STAY_AWAKE_AFTER_READ_MSECS 3000 + +/* The driver states */ +enum usf_state_type { + USF_IDLE_STATE, + USF_OPENED_STATE, + USF_CONFIGURED_STATE, + USF_WORK_STATE, + USF_ADSP_RESTART_STATE, + USF_ERROR_STATE +}; + +/* The US detection status upon FW/HW based US detection results */ +enum usf_us_detect_type { + USF_US_DETECT_UNDEF, + USF_US_DETECT_YES, + USF_US_DETECT_NO +}; + +struct usf_xx_type { + /* Name of the client - event calculator */ + char client_name[USF_MAX_CLIENT_NAME_SIZE]; + /* The driver state in TX or RX direction */ + enum usf_state_type usf_state; + /* wait for q6 events mechanism */ + wait_queue_head_t wait; + /* IF with q6usm info */ + struct us_client *usc; + /* Q6:USM' Encoder/decoder configuration */ + struct us_encdec_cfg encdec_cfg; + /* Shared buffer (with Q6:USM) size */ + uint32_t buffer_size; + /* Number of the shared buffers (with Q6:USM) */ + uint32_t buffer_count; + /* Shared memory (Cyclic buffer with 1 gap) control */ + uint32_t new_region; + uint32_t prev_region; + /* Q6:USM's events handler */ + void (*cb)(uint32_t, uint32_t, uint32_t *, void *); + /* US detection result */ + enum usf_us_detect_type us_detect_type; + /* User's update info isn't acceptable */ + u8 user_upd_info_na; +}; + +struct usf_type { + /* TX device component configuration & control */ + struct usf_xx_type usf_tx; + /* RX device component configuration & control */ + struct usf_xx_type usf_rx; + /* Index into the opened device container */ + /* To prevent mutual usage of the same device */ + uint16_t dev_ind; + /* Event types, supported by device */ + uint16_t event_types; + /* The input devices are "input" module registered clients */ + struct input_dev *input_ifs[USF_MAX_EVENT_IND]; + /* Bitmap of types of events, conflicting to USF's ones */ + uint16_t conflicting_event_types; + /* Bitmap of types of events from devs, conflicting with USF */ + uint16_t conflicting_event_filters; + /* The requested buttons bitmap */ + uint16_t req_buttons_bitmap; + /* Mutex for exclusive operations (all public APIs) */ + struct mutex mutex; +}; + +struct usf_input_dev_type { + /* Input event type, supported by the input device */ + uint16_t event_type; + /* Input device name */ + const char *input_dev_name; + /* Input device registration function */ + int (*prepare_dev)(uint16_t, struct usf_type *, + struct us_input_info_type *, + const char *); + /* Input event notification function */ + void (*notify_event)(struct usf_type *, + uint16_t, + struct usf_event_type * + ); +}; + + +/* The MAX number of the supported devices */ +#define MAX_DEVS_NUMBER 1 + +/* + * code for a special button that is used to show/hide a + * hovering cursor in the input framework. Must be in + * sync with the button code definition in the framework + * (EventHub.h) + */ +#define BTN_USF_HOVERING_CURSOR 0x230 + +/* Supported buttons container */ +static const int s_button_map[] = { + BTN_STYLUS, + BTN_STYLUS2, + BTN_TOOL_PEN, + BTN_TOOL_RUBBER, + BTN_TOOL_FINGER, + BTN_USF_HOVERING_CURSOR +}; + +/* The opened devices container */ +static atomic_t s_opened_devs[MAX_DEVS_NUMBER]; + +static struct wakeup_source usf_wakeup_source; + +#define USF_NAME_PREFIX "usf_" +#define USF_NAME_PREFIX_SIZE 4 + + +static struct input_dev *allocate_dev(uint16_t ind, const char *name) +{ + struct input_dev *in_dev = input_allocate_device(); + + if (in_dev == NULL) { + pr_err("%s: input_allocate_device() failed\n", __func__); + } else { + /* Common part configuration */ + in_dev->name = name; + in_dev->phys = NULL; + in_dev->id.bustype = BUS_HOST; + in_dev->id.vendor = 0x0001; + in_dev->id.product = 0x0001; + in_dev->id.version = USF_VERSION_ID; + } + return in_dev; +} + +static int prepare_tsc_input_device(uint16_t ind, + struct usf_type *usf_info, + struct us_input_info_type *input_info, + const char *name) +{ + int i = 0; + + int num_buttons = min(ARRAY_SIZE(s_button_map), + sizeof(input_info->req_buttons_bitmap) * + BITS_IN_BYTE); + uint16_t max_buttons_bitmap = ((1 << ARRAY_SIZE(s_button_map)) - 1); + + struct input_dev *in_dev = allocate_dev(ind, name); + + if (in_dev == NULL) + return -ENOMEM; + + if (input_info->req_buttons_bitmap > max_buttons_bitmap) { + pr_err("%s: Requested buttons[%d] exceeds max buttons available[%d]\n", + __func__, + input_info->req_buttons_bitmap, + max_buttons_bitmap); + input_free_device(in_dev); + return -EINVAL; + } + + usf_info->input_ifs[ind] = in_dev; + usf_info->req_buttons_bitmap = + input_info->req_buttons_bitmap; + in_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + in_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); + + for (i = 0; i < num_buttons; i++) + if (input_info->req_buttons_bitmap & (1 << i)) + in_dev->keybit[BIT_WORD(s_button_map[i])] |= + BIT_MASK(s_button_map[i]); + + input_set_abs_params(in_dev, ABS_X, + input_info->tsc_x_dim[MIN_IND], + input_info->tsc_x_dim[MAX_IND], + 0, 0); + input_set_abs_params(in_dev, ABS_Y, + input_info->tsc_y_dim[MIN_IND], + input_info->tsc_y_dim[MAX_IND], + 0, 0); + input_set_abs_params(in_dev, ABS_DISTANCE, + input_info->tsc_z_dim[MIN_IND], + input_info->tsc_z_dim[MAX_IND], + 0, 0); + + input_set_abs_params(in_dev, ABS_PRESSURE, + input_info->tsc_pressure[MIN_IND], + input_info->tsc_pressure[MAX_IND], + 0, 0); + + input_set_abs_params(in_dev, ABS_TILT_X, + input_info->tsc_x_tilt[MIN_IND], + input_info->tsc_x_tilt[MAX_IND], + 0, 0); + input_set_abs_params(in_dev, ABS_TILT_Y, + input_info->tsc_y_tilt[MIN_IND], + input_info->tsc_y_tilt[MAX_IND], + 0, 0); + + return 0; +} + +static int prepare_mouse_input_device(uint16_t ind, struct usf_type *usf_info, + struct us_input_info_type *input_info, + const char *name) +{ + struct input_dev *in_dev = allocate_dev(ind, name); + + if (in_dev == NULL) + return -ENOMEM; + + usf_info->input_ifs[ind] = in_dev; + in_dev->evbit[0] |= BIT_MASK(EV_KEY) | BIT_MASK(EV_REL); + + in_dev->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_LEFT) | + BIT_MASK(BTN_RIGHT) | + BIT_MASK(BTN_MIDDLE); + in_dev->relbit[0] = BIT_MASK(REL_X) | + BIT_MASK(REL_Y) | + BIT_MASK(REL_Z); + + return 0; +} + +static int prepare_keyboard_input_device( + uint16_t ind, + struct usf_type *usf_info, + struct us_input_info_type *input_info, + const char *name) +{ + struct input_dev *in_dev = allocate_dev(ind, name); + + if (in_dev == NULL) + return -ENOMEM; + + usf_info->input_ifs[ind] = in_dev; + in_dev->evbit[0] |= BIT_MASK(EV_KEY); + /* All keys are permitted */ + memset(in_dev->keybit, 0xff, sizeof(in_dev->keybit)); + + return 0; +} + +static void notify_tsc_event(struct usf_type *usf_info, + uint16_t if_ind, + struct usf_event_type *event) + +{ + int i = 0; + int num_buttons = min(ARRAY_SIZE(s_button_map), + sizeof(usf_info->req_buttons_bitmap) * + BITS_IN_BYTE); + + struct input_dev *input_if = usf_info->input_ifs[if_ind]; + struct point_event_type *pe = &(event->event_data.point_event); + + input_report_abs(input_if, ABS_X, pe->coordinates[X_IND]); + input_report_abs(input_if, ABS_Y, pe->coordinates[Y_IND]); + input_report_abs(input_if, ABS_DISTANCE, pe->coordinates[Z_IND]); + + input_report_abs(input_if, ABS_TILT_X, pe->inclinations[X_IND]); + input_report_abs(input_if, ABS_TILT_Y, pe->inclinations[Y_IND]); + + input_report_abs(input_if, ABS_PRESSURE, pe->pressure); + input_report_key(input_if, BTN_TOUCH, !!(pe->pressure)); + + for (i = 0; i < num_buttons; i++) { + uint16_t mask = (1 << i), + btn_state = !!(pe->buttons_state_bitmap & mask); + if (usf_info->req_buttons_bitmap & mask) + input_report_key(input_if, s_button_map[i], btn_state); + } + + input_sync(input_if); + + pr_debug("%s: TSC event: xyz[%d;%d;%d], incl[%d;%d], pressure[%d], buttons[%d]\n", + __func__, + pe->coordinates[X_IND], + pe->coordinates[Y_IND], + pe->coordinates[Z_IND], + pe->inclinations[X_IND], + pe->inclinations[Y_IND], + pe->pressure, + pe->buttons_state_bitmap); +} + +static void notify_mouse_event(struct usf_type *usf_info, + uint16_t if_ind, + struct usf_event_type *event) +{ + struct input_dev *input_if = usf_info->input_ifs[if_ind]; + struct mouse_event_type *me = &(event->event_data.mouse_event); + + input_report_rel(input_if, REL_X, me->rels[X_IND]); + input_report_rel(input_if, REL_Y, me->rels[Y_IND]); + input_report_rel(input_if, REL_Z, me->rels[Z_IND]); + + input_report_key(input_if, BTN_LEFT, + me->buttons_states & USF_BUTTON_LEFT_MASK); + input_report_key(input_if, BTN_MIDDLE, + me->buttons_states & USF_BUTTON_MIDDLE_MASK); + input_report_key(input_if, BTN_RIGHT, + me->buttons_states & USF_BUTTON_RIGHT_MASK); + + input_sync(input_if); + + pr_debug("%s: mouse event: dx[%d], dy[%d], buttons_states[%d]\n", + __func__, me->rels[X_IND], + me->rels[Y_IND], me->buttons_states); +} + +static void notify_key_event(struct usf_type *usf_info, + uint16_t if_ind, + struct usf_event_type *event) +{ + struct input_dev *input_if = usf_info->input_ifs[if_ind]; + struct key_event_type *ke = &(event->event_data.key_event); + + input_report_key(input_if, ke->key, ke->key_state); + input_sync(input_if); + pr_debug("%s: key event: key[%d], state[%d]\n", + __func__, + ke->key, + ke->key_state); + +} + +static struct usf_input_dev_type s_usf_input_devs[] = { + {USF_TSC_EVENT, "usf_tsc", + prepare_tsc_input_device, notify_tsc_event}, + {USF_TSC_PTR_EVENT, "usf_tsc_ptr", + prepare_tsc_input_device, notify_tsc_event}, + {USF_MOUSE_EVENT, "usf_mouse", + prepare_mouse_input_device, notify_mouse_event}, + {USF_KEYBOARD_EVENT, "usf_kb", + prepare_keyboard_input_device, notify_key_event}, + {USF_TSC_EXT_EVENT, "usf_tsc_ext", + prepare_tsc_input_device, notify_tsc_event}, +}; + +static void usf_rx_cb(uint32_t opcode, uint32_t token, + uint32_t *payload, void *priv) +{ + struct usf_xx_type *usf_xx = (struct usf_xx_type *) priv; + + if (usf_xx == NULL) { + pr_err("%s: the private data is NULL\n", __func__); + return; + } + + switch (opcode) { + case Q6USM_EVENT_WRITE_DONE: + wake_up(&usf_xx->wait); + break; + + case RESET_EVENTS: + pr_err("%s: received RESET_EVENTS\n", __func__); + usf_xx->usf_state = USF_ADSP_RESTART_STATE; + wake_up(&usf_xx->wait); + break; + + default: + break; + } +} + +static void usf_tx_cb(uint32_t opcode, uint32_t token, + uint32_t *payload, void *priv) +{ + struct usf_xx_type *usf_xx = (struct usf_xx_type *) priv; + + if (usf_xx == NULL) { + pr_err("%s: the private data is NULL\n", __func__); + return; + } + + switch (opcode) { + case Q6USM_EVENT_READ_DONE: + pr_debug("%s: acquiring %d msec wake lock\n", __func__, + STAY_AWAKE_AFTER_READ_MSECS); + __pm_wakeup_event(&usf_wakeup_source, + STAY_AWAKE_AFTER_READ_MSECS); + if (token == USM_WRONG_TOKEN) + usf_xx->usf_state = USF_ERROR_STATE; + usf_xx->new_region = token; + wake_up(&usf_xx->wait); + break; + + case Q6USM_EVENT_SIGNAL_DETECT_RESULT: + usf_xx->us_detect_type = (payload[APR_US_DETECT_RESULT_IND]) ? + USF_US_DETECT_YES : + USF_US_DETECT_NO; + + wake_up(&usf_xx->wait); + break; + + case APR_BASIC_RSP_RESULT: + if (payload[APR_RESULT_IND]) { + usf_xx->usf_state = USF_ERROR_STATE; + usf_xx->new_region = USM_WRONG_TOKEN; + wake_up(&usf_xx->wait); + } + break; + + case RESET_EVENTS: + pr_err("%s: received RESET_EVENTS\n", __func__); + usf_xx->usf_state = USF_ADSP_RESTART_STATE; + wake_up(&usf_xx->wait); + break; + + default: + break; + } +} + +static void release_xx(struct usf_xx_type *usf_xx) +{ + if (usf_xx != NULL) { + if (usf_xx->usc) { + q6usm_us_client_free(usf_xx->usc); + usf_xx->usc = NULL; + } + + if (usf_xx->encdec_cfg.params != NULL) { + kfree(usf_xx->encdec_cfg.params); + usf_xx->encdec_cfg.params = NULL; + } + } +} + +static void usf_disable(struct usf_xx_type *usf_xx) +{ + if (usf_xx != NULL) { + if ((usf_xx->usf_state != USF_IDLE_STATE) && + (usf_xx->usf_state != USF_OPENED_STATE)) { + (void)q6usm_cmd(usf_xx->usc, CMD_CLOSE); + usf_xx->usf_state = USF_OPENED_STATE; + wake_up(&usf_xx->wait); + } + release_xx(usf_xx); + } +} + +static int config_xx(struct usf_xx_type *usf_xx, struct us_xx_info_type *config) +{ + int rc = 0; + uint16_t data_map_size = 0; + uint16_t min_map_size = 0; + + if ((usf_xx == NULL) || + (config == NULL)) + return -EINVAL; + + if ((config->buf_size == 0) || + (config->buf_size > USF_MAX_BUF_SIZE) || + (config->buf_num == 0) || + (config->buf_num > USF_MAX_BUF_NUM)) { + pr_err("%s: wrong params: buf_size=%d; buf_num=%d\n", + __func__, config->buf_size, config->buf_num); + return -EINVAL; + } + + data_map_size = sizeof(usf_xx->encdec_cfg.cfg_common.data_map); + min_map_size = min(data_map_size, config->port_cnt); + + if (config->client_name != NULL) { + if (strncpy_from_user(usf_xx->client_name, + (char __user *)config->client_name, + sizeof(usf_xx->client_name) - 1) < 0) { + pr_err("%s: get client name failed\n", __func__); + return -EINVAL; + } + } + + pr_debug("%s: name=%s; buf_size:%d; dev_id:0x%x; sample_rate:%d\n", + __func__, usf_xx->client_name, config->buf_size, + config->dev_id, config->sample_rate); + + pr_debug("%s: buf_num:%d; format:%d; port_cnt:%d; data_size=%d\n", + __func__, config->buf_num, config->stream_format, + config->port_cnt, config->params_data_size); + + pr_debug("%s: id[0]=%d, id[1]=%d, id[2]=%d, id[3]=%d, id[4]=%d,\n", + __func__, + config->port_id[0], + config->port_id[1], + config->port_id[2], + config->port_id[3], + config->port_id[4]); + + pr_debug("id[5]=%d, id[6]=%d, id[7]=%d\n", + config->port_id[5], + config->port_id[6], + config->port_id[7]); + + /* q6usm allocation & configuration */ + usf_xx->buffer_size = config->buf_size; + usf_xx->buffer_count = config->buf_num; + usf_xx->encdec_cfg.cfg_common.bits_per_sample = + config->bits_per_sample; + usf_xx->encdec_cfg.cfg_common.sample_rate = config->sample_rate; + /* AFE port e.g. AFE_PORT_ID_SLIMBUS_MULTI_CHAN_1_RX */ + usf_xx->encdec_cfg.cfg_common.dev_id = config->dev_id; + + usf_xx->encdec_cfg.cfg_common.ch_cfg = config->port_cnt; + memcpy((void *)&usf_xx->encdec_cfg.cfg_common.data_map, + (void *)config->port_id, + min_map_size); + + usf_xx->encdec_cfg.format_id = config->stream_format; + usf_xx->encdec_cfg.params_size = config->params_data_size; + usf_xx->user_upd_info_na = 1; /* it's used in US_GET_TX_UPDATE */ + + if (config->params_data_size > 0) { /* transparent data copy */ + usf_xx->encdec_cfg.params = kzalloc(config->params_data_size, + GFP_KERNEL); + /* False memory leak here - pointer in packed struct + * is undetected by kmemleak tool + */ + kmemleak_ignore(usf_xx->encdec_cfg.params); + if (usf_xx->encdec_cfg.params == NULL) { + pr_err("%s: params memory alloc[%d] failure\n", + __func__, + config->params_data_size); + return -ENOMEM; + } + rc = copy_from_user(usf_xx->encdec_cfg.params, + (uint8_t __user *)config->params_data, + config->params_data_size); + if (rc) { + pr_err("%s: transparent data copy failure\n", + __func__); + kfree(usf_xx->encdec_cfg.params); + usf_xx->encdec_cfg.params = NULL; + return -EFAULT; + } + pr_debug("%s: params_size[%d]; params[%d,%d,%d,%d, %d]\n", + __func__, + config->params_data_size, + usf_xx->encdec_cfg.params[0], + usf_xx->encdec_cfg.params[1], + usf_xx->encdec_cfg.params[2], + usf_xx->encdec_cfg.params[3], + usf_xx->encdec_cfg.params[4] + ); + } + + usf_xx->usc = q6usm_us_client_alloc(usf_xx->cb, (void *)usf_xx); + if (!usf_xx->usc) { + pr_err("%s: Could not allocate q6usm client\n", __func__); + rc = -EFAULT; + } + + return rc; +} + +static bool usf_match(uint16_t event_type_ind, struct input_dev *dev) +{ + bool rc = false; + + rc = (event_type_ind < MAX_EVENT_TYPE_NUM) && + ((dev->name == NULL) || + strcmp(dev->name, USF_NAME_PREFIX)); + pr_debug("%s: name=[%s]; rc=%d\n", + __func__, dev->name, rc); + + return rc; +} + +static bool usf_register_conflicting_events(uint16_t event_types) +{ + bool rc = true; + uint16_t ind = 0; + uint16_t mask = 1; + + for (ind = 0; ind < MAX_EVENT_TYPE_NUM; ++ind) { + if (event_types & mask) { + rc = usfcdev_register(ind, usf_match); + if (!rc) + break; + } + mask = mask << 1; + } + + return rc; +} + +static void usf_unregister_conflicting_events(uint16_t event_types) +{ + uint16_t ind = 0; + uint16_t mask = 1; + + for (ind = 0; ind < MAX_EVENT_TYPE_NUM; ++ind) { + if (event_types & mask) + usfcdev_unregister(ind); + mask = mask << 1; + } +} + +static void usf_set_event_filters(struct usf_type *usf, uint16_t event_filters) +{ + uint16_t ind = 0; + uint16_t mask = 1; + + if (usf->conflicting_event_filters != event_filters) { + for (ind = 0; ind < MAX_EVENT_TYPE_NUM; ++ind) { + if (usf->conflicting_event_types & mask) + usfcdev_set_filter(ind, event_filters&mask); + mask = mask << 1; + } + usf->conflicting_event_filters = event_filters; + } +} + +static int register_input_device(struct usf_type *usf_info, + struct us_input_info_type *input_info) +{ + int rc = 0; + bool ret = true; + uint16_t ind = 0; + + if ((usf_info == NULL) || + (input_info == NULL) || + !(input_info->event_types & USF_ALL_EVENTS)) { + pr_err("%s: wrong input parameter(s)\n", __func__); + return -EINVAL; + } + + for (ind = 0; ind < USF_MAX_EVENT_IND; ++ind) { + if (usf_info->input_ifs[ind] != NULL) { + pr_err("%s: input_if[%d] is already allocated\n", + __func__, ind); + return -EFAULT; + } + if ((input_info->event_types & + s_usf_input_devs[ind].event_type) && + s_usf_input_devs[ind].prepare_dev) { + rc = (*s_usf_input_devs[ind].prepare_dev)( + ind, + usf_info, + input_info, + s_usf_input_devs[ind].input_dev_name); + if (rc) + return rc; + + rc = input_register_device(usf_info->input_ifs[ind]); + if (rc) { + pr_err("%s: input_reg_dev() failed; rc=%d\n", + __func__, rc); + input_free_device(usf_info->input_ifs[ind]); + usf_info->input_ifs[ind] = NULL; + } else { + usf_info->event_types |= + s_usf_input_devs[ind].event_type; + pr_debug("%s: input device[%s] was registered\n", + __func__, + s_usf_input_devs[ind].input_dev_name); + } + } /* supported event */ + } /* event types loop */ + + ret = usf_register_conflicting_events( + input_info->conflicting_event_types); + if (ret) + usf_info->conflicting_event_types = + input_info->conflicting_event_types; + + return 0; +} + + +static void handle_input_event(struct usf_type *usf_info, + uint16_t event_counter, + struct usf_event_type __user *event) +{ + uint16_t ind = 0; + uint16_t events_num = 0; + struct usf_event_type usf_events[USF_EVENTS_PORTION_SIZE]; + int rc = 0; + + if ((usf_info == NULL) || + (event == NULL) || (!event_counter)) { + return; + } + + while (event_counter > 0) { + if (event_counter > USF_EVENTS_PORTION_SIZE) { + events_num = USF_EVENTS_PORTION_SIZE; + event_counter -= USF_EVENTS_PORTION_SIZE; + } else { + events_num = event_counter; + event_counter = 0; + } + rc = copy_from_user(usf_events, + (struct usf_event_type __user *)event, + events_num * sizeof(struct usf_event_type)); + if (rc) { + pr_err("%s: copy upd_rx_info from user; rc=%d\n", + __func__, rc); + return; + } + for (ind = 0; ind < events_num; ++ind) { + struct usf_event_type *p_event = &usf_events[ind]; + uint16_t if_ind = p_event->event_type_ind; + + if ((if_ind >= USF_MAX_EVENT_IND) || + (usf_info->input_ifs[if_ind] == NULL)) + continue; /* event isn't supported */ + + if (s_usf_input_devs[if_ind].notify_event) + (*s_usf_input_devs[if_ind].notify_event)( + usf_info, + if_ind, + p_event); + } /* loop in the portion */ + } /* all events loop */ +} + +static int usf_start_tx(struct usf_xx_type *usf_xx) +{ + int rc = q6usm_run(usf_xx->usc, 0, 0, 0); + + pr_debug("%s: tx: q6usm_run; rc=%d\n", __func__, rc); + if (!rc) { + if (usf_xx->buffer_count >= USM_MIN_BUF_CNT) { + /* supply all buffers */ + rc = q6usm_read(usf_xx->usc, + usf_xx->buffer_count); + pr_debug("%s: q6usm_read[%d]\n", + __func__, rc); + + if (rc) + pr_err("%s: buf read failed", + __func__); + else + usf_xx->usf_state = + USF_WORK_STATE; + } else + usf_xx->usf_state = + USF_WORK_STATE; + } + + return rc; +} /* usf_start_tx */ + +static int usf_start_rx(struct usf_xx_type *usf_xx) +{ + int rc = q6usm_run(usf_xx->usc, 0, 0, 0); + + pr_debug("%s: rx: q6usm_run; rc=%d\n", + __func__, rc); + if (!rc) + usf_xx->usf_state = USF_WORK_STATE; + + return rc; +} /* usf_start_rx */ + +static int __usf_set_us_detection(struct usf_type *usf, + struct us_detect_info_type *detect_info) +{ + uint32_t timeout = 0; + struct usm_session_cmd_detect_info *p_allocated_memory = NULL; + struct usm_session_cmd_detect_info usm_detect_info; + struct usm_session_cmd_detect_info *p_usm_detect_info = + &usm_detect_info; + uint32_t detect_info_size = sizeof(struct usm_session_cmd_detect_info); + struct usf_xx_type *usf_xx = &usf->usf_tx; + int rc = 0; + + if (detect_info->us_detector != US_DETECT_FW) { + pr_err("%s: unsupported detector: %d\n", + __func__, detect_info->us_detector); + return -EINVAL; + } + + if ((detect_info->params_data_size != 0) && + (detect_info->params_data != NULL)) { + uint8_t *p_data = NULL; + + detect_info_size += detect_info->params_data_size; + p_allocated_memory = kzalloc(detect_info_size, GFP_KERNEL); + if (p_allocated_memory == NULL) { + pr_err("%s: detect_info[%d] allocation failed\n", + __func__, detect_info_size); + return -ENOMEM; + } + p_usm_detect_info = p_allocated_memory; + p_data = (uint8_t *)p_usm_detect_info + + sizeof(struct usm_session_cmd_detect_info); + + rc = copy_from_user(p_data, + (uint8_t __user *)(detect_info->params_data), + detect_info->params_data_size); + if (rc) { + pr_err("%s: copy params from user; rc=%d\n", + __func__, rc); + kfree(p_allocated_memory); + return -EFAULT; + } + p_usm_detect_info->algorithm_cfg_size = + detect_info->params_data_size; + } else + usm_detect_info.algorithm_cfg_size = 0; + + p_usm_detect_info->detect_mode = detect_info->us_detect_mode; + p_usm_detect_info->skip_interval = detect_info->skip_time; + + usf_xx->us_detect_type = USF_US_DETECT_UNDEF; + + rc = q6usm_set_us_detection(usf_xx->usc, + p_usm_detect_info, + detect_info_size); + if (rc || (detect_info->detect_timeout == USF_NO_WAIT_TIMEOUT)) { + kfree(p_allocated_memory); + return rc; + } + + /* Get US detection result */ + if (detect_info->detect_timeout == USF_INFINITIVE_TIMEOUT) { + rc = wait_event_interruptible(usf_xx->wait, + (usf_xx->us_detect_type != + USF_US_DETECT_UNDEF) || + (usf_xx->usf_state == + USF_ADSP_RESTART_STATE)); + } else { + if (detect_info->detect_timeout == USF_DEFAULT_TIMEOUT) + timeout = USF_TIMEOUT_JIFFIES; + else + timeout = detect_info->detect_timeout * HZ; + } + rc = wait_event_interruptible_timeout(usf_xx->wait, + (usf_xx->us_detect_type != + USF_US_DETECT_UNDEF) || + (usf_xx->usf_state == + USF_ADSP_RESTART_STATE), timeout); + + /* In the case of aDSP restart, "no US" is assumed */ + if (usf_xx->usf_state == USF_ADSP_RESTART_STATE) + rc = -EFAULT; + + /* In the case of timeout, "no US" is assumed */ + if (rc < 0) + pr_err("%s: Getting US detection failed rc[%d]\n", + __func__, rc); + else { + usf->usf_rx.us_detect_type = usf->usf_tx.us_detect_type; + detect_info->is_us = + (usf_xx->us_detect_type == USF_US_DETECT_YES); + } + + kfree(p_allocated_memory); + + return rc; +} /* __usf_set_us_detection */ + +static int usf_set_us_detection(struct usf_type *usf, unsigned long arg) +{ + struct us_detect_info_type detect_info; + + int rc = copy_from_user(&detect_info, + (struct us_detect_info_type __user *) arg, + sizeof(detect_info)); + + if (rc) { + pr_err("%s: copy detect_info from user; rc=%d\n", + __func__, rc); + return -EFAULT; + } + + if (detect_info.params_data_size > USF_MAX_USER_BUF_SIZE) { + pr_err("%s: user buffer size exceeds maximum\n", + __func__); + return -EFAULT; + } + + rc = __usf_set_us_detection(usf, &detect_info); + if (rc < 0) { + pr_err("%s: set us detection failed; rc=%d\n", + __func__, rc); + return rc; + } + + rc = copy_to_user((void __user *)arg, + &detect_info, + sizeof(detect_info)); + if (rc) { + pr_err("%s: copy detect_info to user; rc=%d\n", + __func__, rc); + rc = -EFAULT; + } + + return rc; +} /* usf_set_us_detection */ + +static int __usf_set_tx_info(struct usf_type *usf, + struct us_tx_info_type *config_tx) +{ + struct usf_xx_type *usf_xx = &usf->usf_tx; + int rc = 0; + + usf_xx->new_region = USM_UNDEF_TOKEN; + usf_xx->prev_region = USM_UNDEF_TOKEN; + usf_xx->cb = usf_tx_cb; + + init_waitqueue_head(&usf_xx->wait); + + if (config_tx->us_xx_info.client_name != NULL) { + int res = strncpy_from_user( + usf_xx->client_name, + (char __user *)(config_tx->us_xx_info.client_name), + sizeof(usf_xx->client_name)-1); + if (res < 0) { + pr_err("%s: get client name failed\n", + __func__); + return -EINVAL; + } + } + + rc = config_xx(usf_xx, &(config_tx->us_xx_info)); + if (rc) + return rc; + + rc = q6usm_open_read(usf_xx->usc, + usf_xx->encdec_cfg.format_id); + if (rc) + return rc; + + rc = q6usm_us_client_buf_alloc(OUT, usf_xx->usc, + usf_xx->buffer_size, + usf_xx->buffer_count); + if (rc) { + (void)q6usm_cmd(usf_xx->usc, CMD_CLOSE); + return rc; + } + + rc = q6usm_us_param_buf_alloc(OUT, usf_xx->usc, + config_tx->us_xx_info.max_get_set_param_buf_size); + if (rc) { + (void)q6usm_cmd(usf_xx->usc, CMD_CLOSE); + return rc; + } + + rc = q6usm_enc_cfg_blk(usf_xx->usc, + &usf_xx->encdec_cfg); + if (!rc && + (config_tx->input_info.event_types != USF_NO_EVENT)) { + rc = register_input_device(usf, + &(config_tx->input_info)); + } + + if (rc) + (void)q6usm_cmd(usf_xx->usc, CMD_CLOSE); + else + usf_xx->usf_state = USF_CONFIGURED_STATE; + + return rc; +} /* __usf_set_tx_info */ + +static int usf_set_tx_info(struct usf_type *usf, unsigned long arg) +{ + struct us_tx_info_type config_tx; + + int rc = copy_from_user(&config_tx, + (struct us_tx_info_type __user *) arg, + sizeof(config_tx)); + + if (rc) { + pr_err("%s: copy config_tx from user; rc=%d\n", + __func__, rc); + return -EFAULT; + } + + if (config_tx.us_xx_info.params_data_size > USF_MAX_USER_BUF_SIZE) { + pr_err("%s: user buffer size exceeds maximum\n", + __func__); + return -EFAULT; + } + + return __usf_set_tx_info(usf, &config_tx); +} /* usf_set_tx_info */ + +static int __usf_set_rx_info(struct usf_type *usf, + struct us_rx_info_type *config_rx) +{ + struct usf_xx_type *usf_xx = &usf->usf_rx; + int rc = 0; + + usf_xx->new_region = USM_UNDEF_TOKEN; + usf_xx->prev_region = USM_UNDEF_TOKEN; + + usf_xx->cb = usf_rx_cb; + + rc = config_xx(usf_xx, &(config_rx->us_xx_info)); + if (rc) + return rc; + + rc = q6usm_open_write(usf_xx->usc, + usf_xx->encdec_cfg.format_id); + if (rc) + return rc; + + rc = q6usm_us_client_buf_alloc( + IN, + usf_xx->usc, + usf_xx->buffer_size, + usf_xx->buffer_count); + if (rc) { + (void)q6usm_cmd(usf_xx->usc, CMD_CLOSE); + return rc; + } + + rc = q6usm_us_param_buf_alloc(IN, usf_xx->usc, + config_rx->us_xx_info.max_get_set_param_buf_size); + if (rc) { + (void)q6usm_cmd(usf_xx->usc, CMD_CLOSE); + return rc; + } + + rc = q6usm_dec_cfg_blk(usf_xx->usc, + &usf_xx->encdec_cfg); + if (rc) + (void)q6usm_cmd(usf_xx->usc, CMD_CLOSE); + else { + init_waitqueue_head(&usf_xx->wait); + usf_xx->usf_state = USF_CONFIGURED_STATE; + } + + return rc; +} /* __usf_set_rx_info */ + +static int usf_set_rx_info(struct usf_type *usf, unsigned long arg) +{ + struct us_rx_info_type config_rx; + + int rc = copy_from_user(&config_rx, + (struct us_rx_info_type __user *) arg, + sizeof(config_rx)); + + if (rc) { + pr_err("%s: copy config_rx from user; rc=%d\n", + __func__, rc); + return -EFAULT; + } + + if (config_rx.us_xx_info.params_data_size > USF_MAX_USER_BUF_SIZE) { + pr_err("%s: user buffer size exceeds maximum\n", + __func__); + return -EFAULT; + } + + return __usf_set_rx_info(usf, &config_rx); +} /* usf_set_rx_info */ + +static int __usf_get_tx_update(struct usf_type *usf, + struct us_tx_update_info_type *upd_tx_info) +{ + unsigned long prev_jiffies = 0; + uint32_t timeout = 0; + struct usf_xx_type *usf_xx = &usf->usf_tx; + int rc = 0; + + if (!usf_xx->user_upd_info_na) { + usf_set_event_filters(usf, upd_tx_info->event_filters); + handle_input_event(usf, + upd_tx_info->event_counter, + upd_tx_info->event); + + /* Release available regions */ + rc = q6usm_read(usf_xx->usc, + upd_tx_info->free_region); + if (rc) + return rc; + } else + usf_xx->user_upd_info_na = 0; + + /* Get data ready regions */ + if (upd_tx_info->timeout == USF_INFINITIVE_TIMEOUT) { + rc = wait_event_interruptible(usf_xx->wait, + (usf_xx->prev_region != + usf_xx->new_region) || + (usf_xx->usf_state != + USF_WORK_STATE)); + } else { + if (upd_tx_info->timeout == USF_NO_WAIT_TIMEOUT) + rc = (usf_xx->prev_region != usf_xx->new_region); + else { + prev_jiffies = jiffies; + if (upd_tx_info->timeout == USF_DEFAULT_TIMEOUT) { + timeout = USF_TIMEOUT_JIFFIES; + rc = wait_event_timeout( + usf_xx->wait, + (usf_xx->prev_region != + usf_xx->new_region) || + (usf_xx->usf_state != + USF_WORK_STATE), + timeout); + } else { + timeout = upd_tx_info->timeout * HZ; + rc = wait_event_interruptible_timeout( + usf_xx->wait, + (usf_xx->prev_region != + usf_xx->new_region) || + (usf_xx->usf_state != + USF_WORK_STATE), + timeout); + } + } + if (!rc) { + pr_debug("%s: timeout. prev_j=%lu; j=%lu\n", + __func__, prev_jiffies, jiffies); + pr_debug("%s: timeout. prev=%d; new=%d\n", + __func__, usf_xx->prev_region, + usf_xx->new_region); + pr_debug("%s: timeout. free_region=%d;\n", + __func__, upd_tx_info->free_region); + if (usf_xx->prev_region == + usf_xx->new_region) { + pr_err("%s:read data: timeout\n", + __func__); + return -ETIME; + } + } + } + + if ((usf_xx->usf_state != USF_WORK_STATE) || + (rc == -ERESTARTSYS)) { + pr_err("%s: Get ready region failure; state[%d]; rc[%d]\n", + __func__, usf_xx->usf_state, rc); + return -EINTR; + } + + upd_tx_info->ready_region = usf_xx->new_region; + usf_xx->prev_region = upd_tx_info->ready_region; + + if (upd_tx_info->ready_region == USM_WRONG_TOKEN) { + pr_err("%s: TX path corrupted; prev=%d\n", + __func__, usf_xx->prev_region); + return -EIO; + } + + return rc; +} /* __usf_get_tx_update */ + +static int usf_get_tx_update(struct usf_type *usf, unsigned long arg) +{ + struct us_tx_update_info_type upd_tx_info; + + int rc = copy_from_user(&upd_tx_info, + (struct us_tx_update_info_type __user *) arg, + sizeof(upd_tx_info)); + + if (rc < 0) { + pr_err("%s: copy upd_tx_info from user; rc=%d\n", + __func__, rc); + return -EFAULT; + } + + rc = __usf_get_tx_update(usf, &upd_tx_info); + if (rc < 0) { + pr_err("%s: get tx update failed; rc=%d\n", + __func__, rc); + return rc; + } + + rc = copy_to_user((void __user *)arg, + &upd_tx_info, + sizeof(upd_tx_info)); + if (rc) { + pr_err("%s: copy upd_tx_info to user; rc=%d\n", + __func__, rc); + rc = -EFAULT; + } + + return rc; +} /* usf_get_tx_update */ + +static int __usf_set_rx_update(struct usf_xx_type *usf_xx, + struct us_rx_update_info_type *upd_rx_info) +{ + int rc = 0; + + /* Send available data regions */ + if (upd_rx_info->ready_region != + usf_xx->buffer_count) { + rc = q6usm_write( + usf_xx->usc, + upd_rx_info->ready_region); + if (rc) + return rc; + } + + /* Get free regions */ + rc = wait_event_timeout( + usf_xx->wait, + !q6usm_is_write_buf_full( + usf_xx->usc, + &(upd_rx_info->free_region)) || + (usf_xx->usf_state == USF_IDLE_STATE), + USF_TIMEOUT_JIFFIES); + + if (!rc) { + rc = -ETIME; + pr_err("%s:timeout. wait for write buf not full\n", + __func__); + } else { + if (usf_xx->usf_state != + USF_WORK_STATE) { + pr_err("%s: RX: state[%d]\n", + __func__, + usf_xx->usf_state); + rc = -EINTR; + } + } + + return rc; +} /* __usf_set_rx_update */ + +static int usf_set_rx_update(struct usf_xx_type *usf_xx, unsigned long arg) +{ + struct us_rx_update_info_type upd_rx_info; + + int rc = copy_from_user(&upd_rx_info, + (struct us_rx_update_info_type __user *) arg, + sizeof(upd_rx_info)); + + if (rc) { + pr_err("%s: copy upd_rx_info from user; rc=%d\n", + __func__, rc); + return -EFAULT; + } + + rc = __usf_set_rx_update(usf_xx, &upd_rx_info); + if (rc < 0) { + pr_err("%s: set rx update failed; rc=%d\n", + __func__, rc); + return rc; + } + + rc = copy_to_user((void __user *)arg, + &upd_rx_info, + sizeof(upd_rx_info)); + if (rc) { + pr_err("%s: copy rx_info to user; rc=%d\n", + __func__, rc); + rc = -EFAULT; + } + + return rc; +} /* usf_set_rx_update */ + +static void usf_release_input(struct usf_type *usf) +{ + uint16_t ind = 0; + + usf_unregister_conflicting_events( + usf->conflicting_event_types); + usf->conflicting_event_types = 0; + for (ind = 0; ind < USF_MAX_EVENT_IND; ++ind) { + if (usf->input_ifs[ind] == NULL) + continue; + input_unregister_device(usf->input_ifs[ind]); + usf->input_ifs[ind] = NULL; + pr_debug("%s input_unregister_device[%s]\n", + __func__, + s_usf_input_devs[ind].input_dev_name); + } +} /* usf_release_input */ + +static int usf_stop_tx(struct usf_type *usf) +{ + struct usf_xx_type *usf_xx = &usf->usf_tx; + + usf_release_input(usf); + usf_disable(usf_xx); + + return 0; +} /* usf_stop_tx */ + +static int __usf_get_version(struct us_version_info_type *version_info) +{ + int rc = 0; + + if (version_info->buf_size < sizeof(DRV_VERSION)) { + pr_err("%s: buf_size (%d) < version string size (%zu)\n", + __func__, version_info->buf_size, sizeof(DRV_VERSION)); + return -EINVAL; + } + + rc = copy_to_user((void __user *)(version_info->pbuf), + DRV_VERSION, + sizeof(DRV_VERSION)); + if (rc) { + pr_err("%s: copy to version_info.pbuf; rc=%d\n", + __func__, rc); + rc = -EFAULT; + } + + return rc; +} /* __usf_get_version */ + +static int usf_get_version(unsigned long arg) +{ + struct us_version_info_type version_info; + + int rc = copy_from_user(&version_info, + (struct us_version_info_type __user *) arg, + sizeof(version_info)); + + if (rc) { + pr_err("%s: copy version_info from user; rc=%d\n", + __func__, rc); + return -EFAULT; + } + + rc = __usf_get_version(&version_info); + if (rc < 0) { + pr_err("%s: get version failed; rc=%d\n", + __func__, rc); + return rc; + } + + rc = copy_to_user((void __user *)arg, + &version_info, + sizeof(version_info)); + if (rc) { + pr_err("%s: copy version_info to user; rc=%d\n", + __func__, rc); + rc = -EFAULT; + } + + return rc; +} /* usf_get_version */ + +static int __usf_set_stream_param(struct usf_xx_type *usf_xx, + struct us_stream_param_type *set_stream_param, + int dir) +{ + struct us_client *usc = usf_xx->usc; + struct us_port_data *port; + int rc = 0; + + if (usc == NULL) { + pr_err("%s: usc is null\n", + __func__); + return -EFAULT; + } + + port = &usc->port[dir]; + if (port == NULL) { + pr_err("%s: port is null\n", + __func__); + return -EFAULT; + } + + if (port->param_buf == NULL) { + pr_err("%s: parameter buffer is null\n", + __func__); + return -EFAULT; + } + + if (set_stream_param->buf_size > port->param_buf_size) { + pr_err("%s: buf_size (%d) > maximum buf size (%d)\n", + __func__, set_stream_param->buf_size, + port->param_buf_size); + return -EINVAL; + } + + if (set_stream_param->buf_size == 0) { + pr_err("%s: buf_size is 0\n", __func__); + return -EINVAL; + } + + rc = copy_from_user(port->param_buf, + (uint8_t __user *) set_stream_param->pbuf, + set_stream_param->buf_size); + if (rc) { + pr_err("%s: copy param buf from user; rc=%d\n", + __func__, rc); + return -EFAULT; + } + + rc = q6usm_set_us_stream_param(dir, usc, set_stream_param->module_id, + set_stream_param->param_id, + set_stream_param->buf_size); + if (rc) { + pr_err("%s: q6usm_set_us_stream_param failed; rc=%d\n", + __func__, rc); + return -EFAULT; + } + + return rc; +} + +static int usf_set_stream_param(struct usf_xx_type *usf_xx, + unsigned long arg, int dir) +{ + struct us_stream_param_type set_stream_param; + int rc = 0; + + rc = copy_from_user(&set_stream_param, + (struct us_stream_param_type __user *) arg, + sizeof(set_stream_param)); + + if (rc) { + pr_err("%s: copy set_stream_param from user; rc=%d\n", + __func__, rc); + return -EFAULT; + } + + return __usf_set_stream_param(usf_xx, &set_stream_param, dir); +} /* usf_set_stream_param */ + +static int __usf_get_stream_param(struct usf_xx_type *usf_xx, + struct us_stream_param_type *get_stream_param, + int dir) +{ + struct us_client *usc = usf_xx->usc; + struct us_port_data *port; + int rc = 0; + + if (usc == NULL) { + pr_err("%s: us_client is null\n", + __func__); + return -EFAULT; + } + + port = &usc->port[dir]; + + if (port->param_buf == NULL) { + pr_err("%s: parameter buffer is null\n", + __func__); + return -EFAULT; + } + + if (get_stream_param->buf_size > port->param_buf_size) { + pr_err("%s: buf_size (%d) > maximum buf size (%d)\n", + __func__, get_stream_param->buf_size, + port->param_buf_size); + return -EINVAL; + } + + if (get_stream_param->buf_size == 0) { + pr_err("%s: buf_size is 0\n", __func__); + return -EINVAL; + } + + rc = q6usm_get_us_stream_param(dir, usc, get_stream_param->module_id, + get_stream_param->param_id, + get_stream_param->buf_size); + if (rc) { + pr_err("%s: q6usm_get_us_stream_param failed; rc=%d\n", + __func__, rc); + return -EFAULT; + } + + rc = copy_to_user((uint8_t __user *) get_stream_param->pbuf, + port->param_buf, + get_stream_param->buf_size); + if (rc) { + pr_err("%s: copy param buf to user; rc=%d\n", + __func__, rc); + return -EFAULT; + } + + return rc; +} + +static int usf_get_stream_param(struct usf_xx_type *usf_xx, + unsigned long arg, int dir) +{ + struct us_stream_param_type get_stream_param; + int rc = 0; + + rc = copy_from_user(&get_stream_param, + (struct us_stream_param_type __user *) arg, + sizeof(get_stream_param)); + + if (rc) { + pr_err("%s: copy get_stream_param from user; rc=%d\n", + __func__, rc); + return -EFAULT; + } + + return __usf_get_stream_param(usf_xx, &get_stream_param, dir); +} /* usf_get_stream_param */ + +static long __usf_ioctl(struct usf_type *usf, + unsigned int cmd, + unsigned long arg) +{ + + int rc = 0; + struct usf_xx_type *usf_xx = NULL; + + switch (cmd) { + case US_START_TX: { + usf_xx = &usf->usf_tx; + if (usf_xx->usf_state == USF_CONFIGURED_STATE) + rc = usf_start_tx(usf_xx); + else { + pr_err("%s: start_tx: wrong state[%d]\n", + __func__, + usf_xx->usf_state); + return -EBADFD; + } + break; + } + + case US_START_RX: { + usf_xx = &usf->usf_rx; + if (usf_xx->usf_state == USF_CONFIGURED_STATE) + rc = usf_start_rx(usf_xx); + else { + pr_err("%s: start_rx: wrong state[%d]\n", + __func__, + usf_xx->usf_state); + return -EBADFD; + } + break; + } + + case US_SET_TX_INFO: { + usf_xx = &usf->usf_tx; + if (usf_xx->usf_state == USF_OPENED_STATE) + rc = usf_set_tx_info(usf, arg); + else { + pr_err("%s: set_tx_info: wrong state[%d]\n", + __func__, + usf_xx->usf_state); + return -EBADFD; + } + + break; + } /* US_SET_TX_INFO */ + + case US_SET_RX_INFO: { + usf_xx = &usf->usf_rx; + if (usf_xx->usf_state == USF_OPENED_STATE) + rc = usf_set_rx_info(usf, arg); + else { + pr_err("%s: set_rx_info: wrong state[%d]\n", + __func__, + usf_xx->usf_state); + return -EBADFD; + } + + break; + } /* US_SET_RX_INFO */ + + case US_GET_TX_UPDATE: { + struct usf_xx_type *usf_xx = &usf->usf_tx; + + if (usf_xx->usf_state == USF_WORK_STATE) + rc = usf_get_tx_update(usf, arg); + else { + pr_err("%s: get_tx_update: wrong state[%d]\n", __func__, + usf_xx->usf_state); + rc = -EBADFD; + } + break; + } /* US_GET_TX_UPDATE */ + + case US_SET_RX_UPDATE: { + struct usf_xx_type *usf_xx = &usf->usf_rx; + + if (usf_xx->usf_state == USF_WORK_STATE) + rc = usf_set_rx_update(usf_xx, arg); + else { + pr_err("%s: set_rx_update: wrong state[%d]\n", + __func__, + usf_xx->usf_state); + rc = -EBADFD; + } + break; + } /* US_SET_RX_UPDATE */ + + case US_STOP_TX: { + usf_xx = &usf->usf_tx; + if ((usf_xx->usf_state == USF_WORK_STATE) + || (usf_xx->usf_state == USF_ADSP_RESTART_STATE)) + rc = usf_stop_tx(usf); + else { + pr_err("%s: stop_tx: wrong state[%d]\n", + __func__, + usf_xx->usf_state); + return -EBADFD; + } + break; + } /* US_STOP_TX */ + + case US_STOP_RX: { + usf_xx = &usf->usf_rx; + if ((usf_xx->usf_state == USF_WORK_STATE) + || (usf_xx->usf_state == USF_ADSP_RESTART_STATE)) + usf_disable(usf_xx); + else { + pr_err("%s: stop_rx: wrong state[%d]\n", + __func__, + usf_xx->usf_state); + return -EBADFD; + } + break; + } /* US_STOP_RX */ + + case US_SET_DETECTION: { + struct usf_xx_type *usf_xx = &usf->usf_tx; + + if (usf_xx->usf_state == USF_WORK_STATE) + rc = usf_set_us_detection(usf, arg); + else { + pr_err("%s: set us detection: wrong state[%d]\n", + __func__, + usf_xx->usf_state); + rc = -EBADFD; + } + break; + } /* US_SET_DETECTION */ + + case US_GET_VERSION: { + rc = usf_get_version(arg); + break; + } /* US_GET_VERSION */ + + case US_SET_TX_STREAM_PARAM: { + rc = usf_set_stream_param(&usf->usf_tx, arg, OUT); + break; + } /* US_SET_TX_STREAM_PARAM */ + + case US_GET_TX_STREAM_PARAM: { + rc = usf_get_stream_param(&usf->usf_tx, arg, OUT); + break; + } /* US_GET_TX_STREAM_PARAM */ + + case US_SET_RX_STREAM_PARAM: { + rc = usf_set_stream_param(&usf->usf_rx, arg, IN); + break; + } /* US_SET_RX_STREAM_PARAM */ + + case US_GET_RX_STREAM_PARAM: { + rc = usf_get_stream_param(&usf->usf_rx, arg, IN); + break; + } /* US_GET_RX_STREAM_PARAM */ + + default: + pr_err("%s: unsupported IOCTL command [%d]\n", + __func__, + cmd); + rc = -ENOTTY; + break; + } + + if (rc && + ((cmd == US_SET_TX_INFO) || + (cmd == US_SET_RX_INFO))) + release_xx(usf_xx); + + return rc; +} /* __usf_ioctl */ + +static long usf_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct usf_type *usf = file->private_data; + int rc = 0; + + mutex_lock(&usf->mutex); + rc = __usf_ioctl(usf, cmd, arg); + mutex_unlock(&usf->mutex); + + return rc; +} /* usf_ioctl */ + +#ifdef CONFIG_COMPAT + +#define US_SET_TX_INFO32 _IOW(USF_IOCTL_MAGIC, 0, \ + struct us_tx_info_type32) +#define US_GET_TX_UPDATE32 _IOWR(USF_IOCTL_MAGIC, 2, \ + struct us_tx_update_info_type32) +#define US_SET_RX_INFO32 _IOW(USF_IOCTL_MAGIC, 3, \ + struct us_rx_info_type32) +#define US_SET_RX_UPDATE32 _IOWR(USF_IOCTL_MAGIC, 4, \ + struct us_rx_update_info_type32) +#define US_SET_DETECTION32 _IOWR(USF_IOCTL_MAGIC, 8, \ + struct us_detect_info_type32) +#define US_GET_VERSION32 _IOWR(USF_IOCTL_MAGIC, 9, \ + struct us_version_info_type32) +#define US_SET_TX_STREAM_PARAM32 _IOW(USF_IOCTL_MAGIC, 10, \ + struct us_stream_param_type32) +#define US_GET_TX_STREAM_PARAM32 _IOWR(USF_IOCTL_MAGIC, 11, \ + struct us_stream_param_type32) +#define US_SET_RX_STREAM_PARAM32 _IOW(USF_IOCTL_MAGIC, 12, \ + struct us_stream_param_type32) +#define US_GET_RX_STREAM_PARAM32 _IOWR(USF_IOCTL_MAGIC, 13, \ + struct us_stream_param_type32) + +/* Info structure common for TX and RX */ +struct us_xx_info_type32 { +/* Input: general info */ +/* Name of the client - event calculator, ptr to char */ + const compat_uptr_t client_name; +/* Selected device identification, accepted in the kernel's CAD */ + uint32_t dev_id; +/* 0 - point_epos type; (e.g. 1 - gr_mmrd) */ + uint32_t stream_format; +/* Required sample rate in Hz */ + uint32_t sample_rate; +/* Size of a buffer (bytes) for US data transfer between the module and USF */ + uint32_t buf_size; +/* Number of the buffers for the US data transfer */ + uint16_t buf_num; +/* Number of the microphones (TX) or speakers(RX) */ + uint16_t port_cnt; +/* Microphones(TX) or speakers(RX) indexes in their enumeration */ + uint8_t port_id[USF_MAX_PORT_NUM]; +/* Bits per sample 16 or 32 */ + uint16_t bits_per_sample; +/* Input: Transparent info for encoder in the LPASS */ +/* Parameters data size in bytes */ + uint16_t params_data_size; +/* Pointer to the parameters, ptr to uint8_t */ + compat_uptr_t params_data; +/* Max size of buffer for get and set parameter */ + uint32_t max_get_set_param_buf_size; +}; + +struct us_tx_info_type32 { +/* Common info. This struct includes ptr and therefore the 32 version */ + struct us_xx_info_type32 us_xx_info; +/* Info specific for TX. This struct doesn't include long or ptr + * and therefore no 32 version + */ + struct us_input_info_type input_info; +}; + +struct us_tx_update_info_type32 { +/* Input general: */ +/* Number of calculated events */ + uint16_t event_counter; +/* Calculated events or NULL, ptr to struct usf_event_type */ + compat_uptr_t event; +/* Pointer (read index) to the end of available region */ +/* in the shared US data memory */ + uint32_t free_region; +/* Time (sec) to wait for data or special values: */ +/* USF_NO_WAIT_TIMEOUT, USF_INFINITIVE_TIMEOUT, USF_DEFAULT_TIMEOUT */ + uint32_t timeout; +/* Events (from conflicting devs) to be disabled/enabled */ + uint16_t event_filters; + +/* Input transparent data: */ +/* Parameters size */ + uint16_t params_data_size; +/* Pointer to the parameters, ptr to uint8_t */ + compat_uptr_t params_data; +/* Output parameters: */ +/* Pointer (write index) to the end of ready US data region */ +/* in the shared memory */ + uint32_t ready_region; +}; + +struct us_rx_info_type32 { + /* Common info */ + struct us_xx_info_type32 us_xx_info; + /* Info specific for RX*/ +}; + +struct us_rx_update_info_type32 { +/* Input general: */ +/* Pointer (write index) to the end of ready US data region */ +/* in the shared memory */ + uint32_t ready_region; +/* Input transparent data: */ +/* Parameters size */ + uint16_t params_data_size; +/* pPointer to the parameters, ptr to uint8_t */ + compat_uptr_t params_data; +/* Output parameters: */ +/* Pointer (read index) to the end of available region */ +/* in the shared US data memory */ + uint32_t free_region; +}; + +struct us_detect_info_type32 { +/* US detection place (HW|FW) */ +/* NA in the Active and OFF states */ + enum us_detect_place_enum us_detector; +/* US detection mode */ + enum us_detect_mode_enum us_detect_mode; +/* US data dropped during this time (msec) */ + uint32_t skip_time; +/* Transparent data size */ + uint16_t params_data_size; +/* Pointer to the transparent data, ptr to uint8_t */ + compat_uptr_t params_data; +/* Time (sec) to wait for US presence event */ + uint32_t detect_timeout; +/* Out parameter: US presence */ + bool is_us; +}; + +struct us_version_info_type32 { +/* Size of memory for the version string */ + uint16_t buf_size; +/* Pointer to the memory for the version string, ptr to char */ + compat_uptr_t pbuf; +}; + +struct us_stream_param_type32 { +/* Id of module */ + uint32_t module_id; +/* Id of parameter */ + uint32_t param_id; +/* Size of memory of the parameter buffer */ + uint32_t buf_size; +/* Pointer to the memory of the parameter buffer */ + compat_uptr_t pbuf; +}; + +static void usf_compat_xx_info_type(struct us_xx_info_type32 *us_xx_info32, + struct us_xx_info_type *us_xx_info) +{ + int i = 0; + + us_xx_info->client_name = compat_ptr(us_xx_info32->client_name); + us_xx_info->dev_id = us_xx_info32->dev_id; + us_xx_info->stream_format = us_xx_info32->stream_format; + us_xx_info->sample_rate = us_xx_info32->sample_rate; + us_xx_info->buf_size = us_xx_info32->buf_size; + us_xx_info->buf_num = us_xx_info32->buf_num; + us_xx_info->port_cnt = us_xx_info32->port_cnt; + for (i = 0; i < USF_MAX_PORT_NUM; i++) + us_xx_info->port_id[i] = us_xx_info32->port_id[i]; + us_xx_info->bits_per_sample = us_xx_info32->bits_per_sample; + us_xx_info->params_data_size = us_xx_info32->params_data_size; + us_xx_info->params_data = compat_ptr(us_xx_info32->params_data); + us_xx_info->max_get_set_param_buf_size = + us_xx_info32->max_get_set_param_buf_size; +} + +static int usf_set_tx_info32(struct usf_type *usf, unsigned long arg) +{ + struct us_tx_info_type32 config_tx32; + struct us_tx_info_type config_tx; + + int rc = copy_from_user(&config_tx32, + (struct us_tx_info_type32 __user *) arg, + sizeof(config_tx32)); + + if (rc) { + pr_err("%s: copy config_tx from user; rc=%d\n", + __func__, rc); + return -EFAULT; + } + memset(&config_tx, 0, sizeof(config_tx)); + usf_compat_xx_info_type(&(config_tx32.us_xx_info), + &(config_tx.us_xx_info)); + config_tx.input_info = config_tx32.input_info; + + return __usf_set_tx_info(usf, &config_tx); +} /* usf_set_tx_info 32*/ + +static int usf_set_rx_info32(struct usf_type *usf, unsigned long arg) +{ + struct us_rx_info_type32 config_rx32; + struct us_rx_info_type config_rx; + + int rc = copy_from_user(&config_rx32, + (struct us_rx_info_type32 __user *) arg, + sizeof(config_rx32)); + + if (rc) { + pr_err("%s: copy config_rx from user; rc=%d\n", + __func__, rc); + return -EFAULT; + } + memset(&config_rx, 0, sizeof(config_rx)); + usf_compat_xx_info_type(&(config_rx32.us_xx_info), + &(config_rx.us_xx_info)); + + return __usf_set_rx_info(usf, &config_rx); +} /* usf_set_rx_info32 */ + +static int usf_get_tx_update32(struct usf_type *usf, unsigned long arg) +{ + struct us_tx_update_info_type32 upd_tx_info32; + struct us_tx_update_info_type upd_tx_info; + + int rc = copy_from_user(&upd_tx_info32, + (struct us_tx_update_info_type32 __user *) arg, + sizeof(upd_tx_info32)); + + if (rc) { + pr_err("%s: copy upd_tx_info32 from user; rc=%d\n", + __func__, rc); + return -EFAULT; + } + + memset(&upd_tx_info, 0, sizeof(upd_tx_info)); + upd_tx_info.event_counter = upd_tx_info32.event_counter; + upd_tx_info.event = compat_ptr(upd_tx_info32.event); + upd_tx_info.free_region = upd_tx_info32.free_region; + upd_tx_info.timeout = upd_tx_info32.timeout; + upd_tx_info.event_filters = upd_tx_info32.event_filters; + upd_tx_info.params_data_size = upd_tx_info32.params_data_size; + upd_tx_info.params_data = compat_ptr(upd_tx_info32.params_data); + upd_tx_info.ready_region = upd_tx_info32.ready_region; + + rc = __usf_get_tx_update(usf, &upd_tx_info); + if (rc < 0) { + pr_err("%s: get tx update failed; rc=%d\n", + __func__, rc); + return rc; + } + + /* Update only the fields that were changed */ + upd_tx_info32.ready_region = upd_tx_info.ready_region; + + rc = copy_to_user((void __user *)arg, &upd_tx_info32, + sizeof(upd_tx_info32)); + if (rc) { + pr_err("%s: copy upd_tx_info32 to user; rc=%d\n", + __func__, rc); + rc = -EFAULT; + } + + return rc; +} /* usf_get_tx_update */ + +static int usf_set_rx_update32(struct usf_xx_type *usf_xx, unsigned long arg) +{ + struct us_rx_update_info_type32 upd_rx_info32; + struct us_rx_update_info_type upd_rx_info; + + int rc = copy_from_user(&upd_rx_info32, + (struct us_rx_update_info_type32 __user *) arg, + sizeof(upd_rx_info32)); + + if (rc) { + pr_err("%s: copy upd_rx_info32 from user; rc=%d\n", + __func__, rc); + return -EFAULT; + } + + memset(&upd_rx_info, 0, sizeof(upd_rx_info)); + upd_rx_info.ready_region = upd_rx_info32.ready_region; + upd_rx_info.params_data_size = upd_rx_info32.params_data_size; + upd_rx_info.params_data = compat_ptr(upd_rx_info32.params_data); + upd_rx_info.free_region = upd_rx_info32.free_region; + + rc = __usf_set_rx_update(usf_xx, &upd_rx_info); + if (rc < 0) { + pr_err("%s: set rx update failed; rc=%d\n", + __func__, rc); + return rc; + } + + /* Update only the fields that were changed */ + upd_rx_info32.free_region = upd_rx_info.free_region; + + rc = copy_to_user((void __user *)arg, + &upd_rx_info32, + sizeof(upd_rx_info32)); + if (rc) { + pr_err("%s: copy rx_info32 to user; rc=%d\n", + __func__, rc); + rc = -EFAULT; + } + + return rc; +} /* usf_set_rx_update32 */ + +static int usf_set_us_detection32(struct usf_type *usf, unsigned long arg) +{ + struct us_detect_info_type32 detect_info32; + struct us_detect_info_type detect_info; + + int rc = copy_from_user(&detect_info32, + (struct us_detect_info_type32 __user *) arg, + sizeof(detect_info32)); + + if (rc) { + pr_err("%s: copy detect_info32 from user; rc=%d\n", + __func__, rc); + return -EFAULT; + } + + if (detect_info32.params_data_size > USF_MAX_USER_BUF_SIZE) { + pr_err("%s: user buffer size exceeds maximum\n", + __func__); + return -EFAULT; + } + + memset(&detect_info, 0, sizeof(detect_info)); + detect_info.us_detector = detect_info32.us_detector; + detect_info.us_detect_mode = detect_info32.us_detect_mode; + detect_info.skip_time = detect_info32.skip_time; + detect_info.params_data_size = detect_info32.params_data_size; + detect_info.params_data = compat_ptr(detect_info32.params_data); + detect_info.detect_timeout = detect_info32.detect_timeout; + detect_info.is_us = detect_info32.is_us; + + rc = __usf_set_us_detection(usf, &detect_info); + if (rc < 0) { + pr_err("%s: set us detection failed; rc=%d\n", + __func__, rc); + return rc; + } + + /* Update only the fields that were changed */ + detect_info32.is_us = detect_info.is_us; + + rc = copy_to_user((void __user *)arg, + &detect_info32, + sizeof(detect_info32)); + if (rc) { + pr_err("%s: copy detect_info32 to user; rc=%d\n", + __func__, rc); + rc = -EFAULT; + } + + return rc; +} /* usf_set_us_detection32 */ + +static int usf_get_version32(unsigned long arg) +{ + struct us_version_info_type32 version_info32; + struct us_version_info_type version_info; + + int rc = copy_from_user(&version_info32, + (struct us_version_info_type32 __user *) arg, + sizeof(version_info32)); + + if (rc) { + pr_err("%s: copy version_info32 from user; rc=%d\n", + __func__, rc); + return -EFAULT; + } + + memset(&version_info, 0, sizeof(version_info)); + version_info.buf_size = version_info32.buf_size; + version_info.pbuf = compat_ptr(version_info32.pbuf); + + rc = __usf_get_version(&version_info); + if (rc < 0) { + pr_err("%s: get version failed; rc=%d\n", + __func__, rc); + return rc; + } + + /* None of the fields were changed */ + + rc = copy_to_user((void __user *)arg, + &version_info32, + sizeof(version_info32)); + if (rc) { + pr_err("%s: copy version_info32 to user; rc=%d\n", + __func__, rc); + rc = -EFAULT; + } + + return rc; +} /* usf_get_version32 */ + +static int usf_set_stream_param32(struct usf_xx_type *usf_xx, + unsigned long arg, int dir) +{ + struct us_stream_param_type32 set_stream_param32; + struct us_stream_param_type set_stream_param; + int rc = 0; + + rc = copy_from_user(&set_stream_param32, + (struct us_stream_param_type32 __user *) arg, + sizeof(set_stream_param32)); + + if (rc) { + pr_err("%s: copy set_stream_param from user; rc=%d\n", + __func__, rc); + return -EFAULT; + } + + memset(&set_stream_param, 0, sizeof(set_stream_param)); + set_stream_param.module_id = set_stream_param32.module_id; + set_stream_param.param_id = set_stream_param32.param_id; + set_stream_param.buf_size = set_stream_param32.buf_size; + set_stream_param.pbuf = compat_ptr(set_stream_param32.pbuf); + + return __usf_set_stream_param(usf_xx, &set_stream_param, dir); +} /* usf_set_stream_param32 */ + +static int usf_get_stream_param32(struct usf_xx_type *usf_xx, + unsigned long arg, int dir) +{ + struct us_stream_param_type32 get_stream_param32; + struct us_stream_param_type get_stream_param; + int rc = 0; + + rc = copy_from_user(&get_stream_param32, + (struct us_stream_param_type32 __user *) arg, + sizeof(get_stream_param32)); + + if (rc) { + pr_err("%s: copy get_stream_param from user; rc=%d\n", + __func__, rc); + return -EFAULT; + } + + memset(&get_stream_param, 0, sizeof(get_stream_param)); + get_stream_param.module_id = get_stream_param32.module_id; + get_stream_param.param_id = get_stream_param32.param_id; + get_stream_param.buf_size = get_stream_param32.buf_size; + get_stream_param.pbuf = compat_ptr(get_stream_param32.pbuf); + + return __usf_get_stream_param(usf_xx, &get_stream_param, dir); +} /* usf_get_stream_param32 */ + +static long __usf_compat_ioctl(struct usf_type *usf, + unsigned int cmd, + unsigned long arg) +{ + int rc = 0; + struct usf_xx_type *usf_xx = NULL; + + switch (cmd) { + case US_START_TX: + case US_START_RX: + case US_STOP_TX: + case US_STOP_RX: { + return __usf_ioctl(usf, cmd, arg); + } + + case US_SET_TX_INFO32: { + usf_xx = &usf->usf_tx; + if (usf_xx->usf_state == USF_OPENED_STATE) + rc = usf_set_tx_info32(usf, arg); + else { + pr_err("%s: set_tx_info32: wrong state[%d]\n", + __func__, + usf_xx->usf_state); + return -EBADFD; + } + + break; + } /* US_SET_TX_INFO32 */ + + case US_SET_RX_INFO32: { + usf_xx = &usf->usf_rx; + if (usf_xx->usf_state == USF_OPENED_STATE) + rc = usf_set_rx_info32(usf, arg); + else { + pr_err("%s: set_rx_info32: wrong state[%d]\n", + __func__, + usf_xx->usf_state); + return -EBADFD; + } + + break; + } /* US_SET_RX_INFO32 */ + + case US_GET_TX_UPDATE32: { + struct usf_xx_type *usf_xx = &usf->usf_tx; + + if (usf_xx->usf_state == USF_WORK_STATE) + rc = usf_get_tx_update32(usf, arg); + else { + pr_err("%s: get_tx_update32: wrong state[%d]\n", + __func__, + usf_xx->usf_state); + rc = -EBADFD; + } + break; + } /* US_GET_TX_UPDATE32 */ + + case US_SET_RX_UPDATE32: { + struct usf_xx_type *usf_xx = &usf->usf_rx; + + if (usf_xx->usf_state == USF_WORK_STATE) + rc = usf_set_rx_update32(usf_xx, arg); + else { + pr_err("%s: set_rx_update: wrong state[%d]\n", + __func__, + usf_xx->usf_state); + rc = -EBADFD; + } + break; + } /* US_SET_RX_UPDATE32 */ + + case US_SET_DETECTION32: { + struct usf_xx_type *usf_xx = &usf->usf_tx; + + if (usf_xx->usf_state == USF_WORK_STATE) + rc = usf_set_us_detection32(usf, arg); + else { + pr_err("%s: set us detection: wrong state[%d]\n", + __func__, + usf_xx->usf_state); + rc = -EBADFD; + } + break; + } /* US_SET_DETECTION32 */ + + case US_GET_VERSION32: { + rc = usf_get_version32(arg); + break; + } /* US_GET_VERSION32 */ + + case US_SET_TX_STREAM_PARAM32: { + rc = usf_set_stream_param32(&usf->usf_tx, arg, OUT); + break; + } /* US_SET_TX_STREAM_PARAM32 */ + + case US_GET_TX_STREAM_PARAM32: { + rc = usf_get_stream_param32(&usf->usf_tx, arg, OUT); + break; + } /* US_GET_TX_STREAM_PARAM32 */ + + case US_SET_RX_STREAM_PARAM32: { + rc = usf_set_stream_param32(&usf->usf_rx, arg, IN); + break; + } /* US_SET_RX_STREAM_PARAM32 */ + + case US_GET_RX_STREAM_PARAM32: { + rc = usf_get_stream_param32(&usf->usf_rx, arg, IN); + break; + } /* US_GET_RX_STREAM_PARAM32 */ + + default: + pr_err("%s: unsupported IOCTL command [%d]\n", + __func__, + cmd); + rc = -ENOTTY; + break; + } + + if (rc && + ((cmd == US_SET_TX_INFO) || + (cmd == US_SET_RX_INFO))) + release_xx(usf_xx); + + return rc; +} /* __usf_compat_ioctl */ + +static long usf_compat_ioctl(struct file *file, + unsigned int cmd, + unsigned long arg) +{ + struct usf_type *usf = file->private_data; + int rc = 0; + + mutex_lock(&usf->mutex); + rc = __usf_compat_ioctl(usf, cmd, arg); + mutex_unlock(&usf->mutex); + + return rc; +} /* usf_compat_ioctl */ +#endif /* CONFIG_COMPAT */ + +static int usf_mmap(struct file *file, struct vm_area_struct *vms) +{ + struct usf_type *usf = file->private_data; + int dir = OUT; + struct usf_xx_type *usf_xx = &usf->usf_tx; + int rc = 0; + + mutex_lock(&usf->mutex); + if (vms->vm_flags & USF_VM_WRITE) { /* RX buf mapping */ + dir = IN; + usf_xx = &usf->usf_rx; + } + rc = q6usm_get_virtual_address(dir, usf_xx->usc, vms); + mutex_unlock(&usf->mutex); + + return rc; +} + +static uint16_t add_opened_dev(int minor) +{ + uint16_t ind = 0; + + for (ind = 0; ind < MAX_DEVS_NUMBER; ++ind) { + if (minor == atomic_cmpxchg(&s_opened_devs[ind], 0, minor)) { + pr_err("%s: device %d is already opened\n", + __func__, minor); + return USF_UNDEF_DEV_ID; + } else { + pr_debug("%s: device %d is added; ind=%d\n", + __func__, minor, ind); + return ind; + } + } + + pr_err("%s: there is no place for device %d\n", + __func__, minor); + return USF_UNDEF_DEV_ID; +} + +static int usf_open(struct inode *inode, struct file *file) +{ + struct usf_type *usf = NULL; + uint16_t dev_ind = 0; + int minor = MINOR(inode->i_rdev); + + dev_ind = add_opened_dev(minor); + if (dev_ind == USF_UNDEF_DEV_ID) + return -EBUSY; + + usf = kzalloc(sizeof(struct usf_type), GFP_KERNEL); + if (usf == NULL) + return -ENOMEM; + + wakeup_source_init(&usf_wakeup_source, "usf"); + + file->private_data = usf; + usf->dev_ind = dev_ind; + + usf->usf_tx.usf_state = USF_OPENED_STATE; + usf->usf_rx.usf_state = USF_OPENED_STATE; + + usf->usf_tx.us_detect_type = USF_US_DETECT_UNDEF; + usf->usf_rx.us_detect_type = USF_US_DETECT_UNDEF; + + mutex_init(&usf->mutex); + + pr_debug("%s:usf in open\n", __func__); + return 0; +} + +static int usf_release(struct inode *inode, struct file *file) +{ + struct usf_type *usf = file->private_data; + + pr_debug("%s: release entry\n", __func__); + + mutex_lock(&usf->mutex); + usf_release_input(usf); + + usf_disable(&usf->usf_tx); + usf_disable(&usf->usf_rx); + + atomic_set(&s_opened_devs[usf->dev_ind], 0); + + wakeup_source_trash(&usf_wakeup_source); + mutex_unlock(&usf->mutex); + mutex_destroy(&usf->mutex); + kfree(usf); + pr_debug("%s: release exit\n", __func__); + return 0; +} + +extern long usf_compat_ioctl(struct file *file, + unsigned int cmd, + unsigned long arg); + +static const struct file_operations usf_fops = { + .owner = THIS_MODULE, + .open = usf_open, + .release = usf_release, + .unlocked_ioctl = usf_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = usf_compat_ioctl, +#endif /* CONFIG_COMPAT */ + .mmap = usf_mmap, +}; + +static struct miscdevice usf_misc[MAX_DEVS_NUMBER] = { + { + .minor = MISC_DYNAMIC_MINOR, + .name = "usf1", + .fops = &usf_fops, + }, +}; + +static int __init usf_init(void) +{ + int rc = 0; + uint16_t ind = 0; + + pr_debug("%s: USF SW version %s.\n", __func__, DRV_VERSION); + pr_debug("%s: Max %d devs registration\n", __func__, MAX_DEVS_NUMBER); + + for (ind = 0; ind < MAX_DEVS_NUMBER; ++ind) { + rc = misc_register(&usf_misc[ind]); + if (rc) { + pr_err("%s: misc_register() failed ind=%d; rc = %d\n", + __func__, ind, rc); + break; + } + } + if (!rc) q6usm_init(); + + return rc; +} +module_init(usf_init); + +static void __exit usf_exit(void) +{ + uint16_t ind = 0; + + for (ind = 0; ind < MAX_DEVS_NUMBER; ++ind) + misc_deregister(&usf_misc[ind]); +} +module_exit(usf_exit); +MODULE_DESCRIPTION("Ultrasound framework driver"); +MODULE_LICENSE("GPL v2"); diff --git a/audio-kernel/dsp/usf.h b/audio-kernel/dsp/usf.h new file mode 100644 index 000000000..544b624c2 --- /dev/null +++ b/audio-kernel/dsp/usf.h @@ -0,0 +1,298 @@ +/* Copyright (c) 2011-2014, 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 __USF_H__ +#define __USF_H__ + +#include +#include + +#define USF_IOCTL_MAGIC 'U' + +#define US_SET_TX_INFO _IOW(USF_IOCTL_MAGIC, 0, \ + struct us_tx_info_type) +#define US_START_TX _IO(USF_IOCTL_MAGIC, 1) +#define US_GET_TX_UPDATE _IOWR(USF_IOCTL_MAGIC, 2, \ + struct us_tx_update_info_type) +#define US_SET_RX_INFO _IOW(USF_IOCTL_MAGIC, 3, \ + struct us_rx_info_type) +#define US_SET_RX_UPDATE _IOWR(USF_IOCTL_MAGIC, 4, \ + struct us_rx_update_info_type) +#define US_START_RX _IO(USF_IOCTL_MAGIC, 5) + +#define US_STOP_TX _IO(USF_IOCTL_MAGIC, 6) +#define US_STOP_RX _IO(USF_IOCTL_MAGIC, 7) + +#define US_SET_DETECTION _IOWR(USF_IOCTL_MAGIC, 8, \ + struct us_detect_info_type) + +#define US_GET_VERSION _IOWR(USF_IOCTL_MAGIC, 9, \ + struct us_version_info_type) + +#define US_SET_TX_STREAM_PARAM _IOW(USF_IOCTL_MAGIC, 10, \ + struct us_stream_param_type) +#define US_GET_TX_STREAM_PARAM _IOWR(USF_IOCTL_MAGIC, 11, \ + struct us_stream_param_type) +#define US_SET_RX_STREAM_PARAM _IOW(USF_IOCTL_MAGIC, 12, \ + struct us_stream_param_type) +#define US_GET_RX_STREAM_PARAM _IOWR(USF_IOCTL_MAGIC, 13, \ + struct us_stream_param_type) + +/* Special timeout values */ +#define USF_NO_WAIT_TIMEOUT 0x00000000 +/* Infinitive */ +#define USF_INFINITIVE_TIMEOUT 0xffffffff +/* Default value, used by the driver */ +#define USF_DEFAULT_TIMEOUT 0xfffffffe + +/* US detection place (HW|FW) */ +enum us_detect_place_enum { +/* US is detected in HW */ + US_DETECT_HW, +/* US is detected in FW */ + US_DETECT_FW +}; + +/* US detection mode */ +enum us_detect_mode_enum { +/* US detection is disabled */ + US_DETECT_DISABLED_MODE, +/* US detection is enabled in continue mode */ + US_DETECT_CONTINUE_MODE, +/* US detection is enabled in one shot mode */ + US_DETECT_SHOT_MODE +}; + +/* Encoder (TX), decoder (RX) supported US data formats */ +#define USF_POINT_EPOS_FORMAT 0 +#define USF_RAW_FORMAT 1 + +/* Indexes of event types, produced by the calculators */ +#define USF_TSC_EVENT_IND 0 +#define USF_TSC_PTR_EVENT_IND 1 +#define USF_MOUSE_EVENT_IND 2 +#define USF_KEYBOARD_EVENT_IND 3 +#define USF_TSC_EXT_EVENT_IND 4 +#define USF_MAX_EVENT_IND 5 + +/* Types of events, produced by the calculators */ +#define USF_NO_EVENT 0 +#define USF_TSC_EVENT (1 << USF_TSC_EVENT_IND) +#define USF_TSC_PTR_EVENT (1 << USF_TSC_PTR_EVENT_IND) +#define USF_MOUSE_EVENT (1 << USF_MOUSE_EVENT_IND) +#define USF_KEYBOARD_EVENT (1 << USF_KEYBOARD_EVENT_IND) +#define USF_TSC_EXT_EVENT (1 << USF_TSC_EXT_EVENT_IND) +#define USF_ALL_EVENTS (USF_TSC_EVENT |\ + USF_TSC_PTR_EVENT |\ + USF_MOUSE_EVENT |\ + USF_KEYBOARD_EVENT |\ + USF_TSC_EXT_EVENT) + +/* min, max array dimension */ +#define MIN_MAX_DIM 2 + +/* coordinates (x,y,z) array dimension */ +#define COORDINATES_DIM 3 + +/* tilts (x,y) array dimension */ +#define TILTS_DIM 2 + +/* Max size of the client name */ +#define USF_MAX_CLIENT_NAME_SIZE 20 + +/* Max number of the ports (mics/speakers) */ +#define USF_MAX_PORT_NUM 8 + +/* Info structure common for TX and RX */ +struct us_xx_info_type { +/* Input: general info */ +/* Name of the client - event calculator */ + const char __user *client_name; +/* Selected device identification, accepted in the kernel's CAD */ + uint32_t dev_id; +/* 0 - point_epos type; (e.g. 1 - gr_mmrd) */ + uint32_t stream_format; +/* Required sample rate in Hz */ + uint32_t sample_rate; +/* Size of a buffer (bytes) for US data transfer between the module and USF */ + uint32_t buf_size; +/* Number of the buffers for the US data transfer */ + uint16_t buf_num; +/* Number of the microphones (TX) or speakers(RX) */ + uint16_t port_cnt; +/* Microphones(TX) or speakers(RX) indexes in their enumeration */ + uint8_t port_id[USF_MAX_PORT_NUM]; +/* Bits per sample 16 or 32 */ + uint16_t bits_per_sample; +/* Input: Transparent info for encoder in the LPASS */ +/* Parameters data size in bytes */ + uint16_t params_data_size; +/* Pointer to the parameters */ + uint8_t __user *params_data; +/* Max size of buffer for get and set parameter */ + uint32_t max_get_set_param_buf_size; +}; + +struct us_input_info_type { + /* Touch screen dimensions: min & max;for input module */ + int tsc_x_dim[MIN_MAX_DIM]; + int tsc_y_dim[MIN_MAX_DIM]; + int tsc_z_dim[MIN_MAX_DIM]; + /* Touch screen tilt dimensions: min & max;for input module */ + int tsc_x_tilt[MIN_MAX_DIM]; + int tsc_y_tilt[MIN_MAX_DIM]; + /* Touch screen pressure limits: min & max; for input module */ + int tsc_pressure[MIN_MAX_DIM]; + /* The requested buttons bitmap */ + uint16_t req_buttons_bitmap; + /* Bitmap of types of events (USF_X_EVENT), produced by calculator */ + uint16_t event_types; + /* Bitmap of types of events from devs, conflicting with USF */ + uint16_t conflicting_event_types; +}; + +struct us_tx_info_type { + /* Common info */ + struct us_xx_info_type us_xx_info; + /* Info specific for TX*/ + struct us_input_info_type input_info; +}; + +struct us_rx_info_type { + /* Common info */ + struct us_xx_info_type us_xx_info; + /* Info specific for RX*/ +}; + +struct point_event_type { +/* Pen coordinates (x, y, z) in units, defined by */ + int coordinates[COORDINATES_DIM]; + /* {x;y} in transparent units */ + int inclinations[TILTS_DIM]; +/* [0-1023] (10bits); 0 - pen up */ + uint32_t pressure; +/* Bitmap for button state. 1 - down, 0 - up */ + uint16_t buttons_state_bitmap; +}; + +/* Mouse buttons, supported by USF */ +#define USF_BUTTON_LEFT_MASK 1 +#define USF_BUTTON_MIDDLE_MASK 2 +#define USF_BUTTON_RIGHT_MASK 4 +struct mouse_event_type { +/* The mouse relative movement (dX, dY, dZ) */ + int rels[COORDINATES_DIM]; +/* Bitmap of mouse buttons states: 1 - down, 0 - up; */ + uint16_t buttons_states; +}; + +struct key_event_type { +/* Calculated MS key- see input.h. */ + uint32_t key; +/* Keyboard's key state: 1 - down, 0 - up; */ + uint8_t key_state; +}; + +struct usf_event_type { +/* Event sequence number */ + uint32_t seq_num; +/* Event generation system time */ + uint32_t timestamp; +/* Destination input event type index (e.g. touch screen, mouse, key) */ + uint16_t event_type_ind; + union { + struct point_event_type point_event; + struct mouse_event_type mouse_event; + struct key_event_type key_event; + } event_data; +}; + +struct us_tx_update_info_type { +/* Input general: */ +/* Number of calculated events */ + uint16_t event_counter; +/* Calculated events or NULL */ + struct usf_event_type __user *event; +/* Pointer (read index) to the end of available region */ +/* in the shared US data memory */ + uint32_t free_region; +/* Time (sec) to wait for data or special values: */ +/* USF_NO_WAIT_TIMEOUT, USF_INFINITIVE_TIMEOUT, USF_DEFAULT_TIMEOUT */ + uint32_t timeout; +/* Events (from conflicting devs) to be disabled/enabled */ + uint16_t event_filters; + +/* Input transparent data: */ +/* Parameters size */ + uint16_t params_data_size; +/* Pointer to the parameters */ + uint8_t __user *params_data; +/* Output parameters: */ +/* Pointer (write index) to the end of ready US data region */ +/* in the shared memory */ + uint32_t ready_region; +}; + +struct us_rx_update_info_type { +/* Input general: */ +/* Pointer (write index) to the end of ready US data region */ +/* in the shared memory */ + uint32_t ready_region; +/* Input transparent data: */ +/* Parameters size */ + uint16_t params_data_size; +/* pPointer to the parameters */ + uint8_t __user *params_data; +/* Output parameters: */ +/* Pointer (read index) to the end of available region */ +/* in the shared US data memory */ + uint32_t free_region; +}; + +struct us_detect_info_type { +/* US detection place (HW|FW) */ +/* NA in the Active and OFF states */ + enum us_detect_place_enum us_detector; +/* US detection mode */ + enum us_detect_mode_enum us_detect_mode; +/* US data dropped during this time (msec) */ + uint32_t skip_time; +/* Transparent data size */ + uint16_t params_data_size; +/* Pointer to the transparent data */ + uint8_t __user *params_data; +/* Time (sec) to wait for US presence event */ + uint32_t detect_timeout; +/* Out parameter: US presence */ + bool is_us; +}; + +struct us_version_info_type { +/* Size of memory for the version string */ + uint16_t buf_size; +/* Pointer to the memory for the version string */ + char __user *pbuf; +}; + +struct us_stream_param_type { +/* Id of module */ + uint32_t module_id; +/* Id of parameter */ + uint32_t param_id; +/* Size of memory of the parameter buffer */ + uint32_t buf_size; +/* Pointer to the memory of the parameter buffer */ + uint8_t __user *pbuf; +}; + +#endif /* __USF_H__ */ diff --git a/audio-kernel/dsp/usfcdev.c b/audio-kernel/dsp/usfcdev.c new file mode 100644 index 000000000..289616669 --- /dev/null +++ b/audio-kernel/dsp/usfcdev.c @@ -0,0 +1,413 @@ +/* Copyright (c) 2012-2013, 2016-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 + * 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 +#include +#include +#include +#include +#include +#include +#include "usfcdev.h" + +#define UNDEF_ID 0xffffffff +#define SLOT_CMD_ID 0 +#define MAX_RETRIES 10 + +enum usdev_event_status { + USFCDEV_EVENT_ENABLED, + USFCDEV_EVENT_DISABLING, + USFCDEV_EVENT_DISABLED, +}; + +struct usfcdev_event { + bool (*match_cb)(uint16_t, struct input_dev *dev); + bool registered_event; + bool interleaved; + enum usdev_event_status event_status; +}; +static struct usfcdev_event s_usfcdev_events[MAX_EVENT_TYPE_NUM]; + +struct usfcdev_input_command { + unsigned int type; + unsigned int code; + unsigned int value; +}; + +static long s_usf_pid; + +static bool usfcdev_filter(struct input_handle *handle, + unsigned int type, unsigned int code, int value); +static bool usfcdev_match(struct input_handler *handler, + struct input_dev *dev); +static int usfcdev_connect(struct input_handler *handler, + struct input_dev *dev, + const struct input_device_id *id); +static void usfcdev_disconnect(struct input_handle *handle); + +static const struct input_device_id usfc_tsc_ids[] = { + { + .flags = INPUT_DEVICE_ID_MATCH_EVBIT | + INPUT_DEVICE_ID_MATCH_KEYBIT | + INPUT_DEVICE_ID_MATCH_ABSBIT, + .evbit = { BIT_MASK(EV_ABS) | BIT_MASK(EV_KEY) }, + .keybit = { [BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH) }, + /* assumption: ABS_X & ABS_Y are in the same long */ + .absbit = { [BIT_WORD(ABS_X)] = BIT_MASK(ABS_X) | + BIT_MASK(ABS_Y) }, + }, + { + .flags = INPUT_DEVICE_ID_MATCH_EVBIT | + INPUT_DEVICE_ID_MATCH_KEYBIT | + INPUT_DEVICE_ID_MATCH_ABSBIT, + .evbit = { BIT_MASK(EV_ABS) | BIT_MASK(EV_KEY) }, + .keybit = { [BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH) }, + /* assumption: MT_.._X & MT_.._Y are in the same long */ + .absbit = { [BIT_WORD(ABS_MT_POSITION_X)] = + BIT_MASK(ABS_MT_POSITION_X) | + BIT_MASK(ABS_MT_POSITION_Y) }, + }, + { } /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(input, usfc_tsc_ids); + +static struct input_handler s_usfc_handlers[MAX_EVENT_TYPE_NUM] = { + { /* TSC handler */ + .filter = usfcdev_filter, + .match = usfcdev_match, + .connect = usfcdev_connect, + .disconnect = usfcdev_disconnect, + /* .minor can be used as index in the container, */ + /* because .fops isn't supported */ + .minor = TSC_EVENT_TYPE_IND, + .name = "usfc_tsc_handler", + .id_table = usfc_tsc_ids, + }, +}; + +/* + * For each event type, there are a number conflicting devices (handles) + * The first registered device (primary) is real TSC device; it's mandatory + * Optionally, later registered devices are simulated ones. + * They are dynamically managed + * The primary device's handles are stored in the below static array + */ +static struct input_handle s_usfc_primary_handles[MAX_EVENT_TYPE_NUM] = { + { /* TSC handle */ + .handler = &s_usfc_handlers[TSC_EVENT_TYPE_IND], + .name = "usfc_tsc_handle", + }, +}; + +static struct usfcdev_input_command initial_clear_cmds[] = { + {EV_ABS, ABS_PRESSURE, 0}, + {EV_KEY, BTN_TOUCH, 0}, +}; + +static struct usfcdev_input_command slot_clear_cmds[] = { + {EV_ABS, ABS_MT_SLOT, 0}, + {EV_ABS, ABS_MT_TRACKING_ID, UNDEF_ID}, +}; + +static struct usfcdev_input_command no_filter_cmds[] = { + {EV_ABS, ABS_MT_SLOT, 0}, + {EV_ABS, ABS_MT_TRACKING_ID, UNDEF_ID}, + {EV_SYN, SYN_REPORT, 0}, +}; + +static bool usfcdev_match(struct input_handler *handler, struct input_dev *dev) +{ + bool rc = false; + int ind = handler->minor; + + pr_debug("%s: name=[%s]; ind=%d\n", __func__, dev->name, ind); + + if (s_usfcdev_events[ind].registered_event && + s_usfcdev_events[ind].match_cb) { + rc = (*s_usfcdev_events[ind].match_cb)((uint16_t)ind, dev); + pr_debug("%s: [%s]; rc=%d\n", __func__, dev->name, rc); + } + return rc; +} + +static int usfcdev_connect(struct input_handler *handler, struct input_dev *dev, + const struct input_device_id *id) +{ + int ret = 0; + uint16_t ind = handler->minor; + struct input_handle *usfc_handle = NULL; + + if (s_usfc_primary_handles[ind].dev == NULL) { + pr_debug("%s: primary device; ind=%d\n", + __func__, + ind); + usfc_handle = &s_usfc_primary_handles[ind]; + } else { + pr_debug("%s: secondary device; ind=%d\n", + __func__, + ind); + usfc_handle = kzalloc(sizeof(struct input_handle), + GFP_KERNEL); + if (!usfc_handle) + return -ENOMEM; + + usfc_handle->handler = &s_usfc_handlers[ind]; + usfc_handle->name = s_usfc_primary_handles[ind].name; + } + usfc_handle->dev = dev; + ret = input_register_handle(usfc_handle); + pr_debug("%s: name=[%s]; ind=%d; dev=0x%pK\n", + __func__, + dev->name, + ind, + usfc_handle->dev); + if (ret) + pr_err("%s: input_register_handle[%d] failed: ret=%d\n", + __func__, + ind, + ret); + else { + ret = input_open_device(usfc_handle); + if (ret) { + pr_err("%s: input_open_device[%d] failed: ret=%d\n", + __func__, + ind, + ret); + input_unregister_handle(usfc_handle); + } else + pr_debug("%s: device[%d] is opened\n", + __func__, + ind); + } + + return ret; +} + +static void usfcdev_disconnect(struct input_handle *handle) +{ + int ind = handle->handler->minor; + + input_close_device(handle); + input_unregister_handle(handle); + pr_debug("%s: handle[%d], name=[%s] is disconnected\n", + __func__, + ind, + handle->dev->name); + if (s_usfc_primary_handles[ind].dev == handle->dev) + s_usfc_primary_handles[ind].dev = NULL; + else + kfree(handle); +} + +static bool usfcdev_filter(struct input_handle *handle, + unsigned int type, unsigned int code, int value) +{ + uint16_t i = 0; + uint16_t ind = (uint16_t)handle->handler->minor; + bool rc = (s_usfcdev_events[ind].event_status != USFCDEV_EVENT_ENABLED); + + if (s_usf_pid == current->pid) { + /* Pass events from usfcdev driver */ + rc = false; + pr_debug("%s: event_type=%d; type=%d; code=%d; val=%d", + __func__, + ind, + type, + code, + value); + } else if (s_usfcdev_events[ind].event_status == + USFCDEV_EVENT_DISABLING) { + uint32_t u_value = value; + + s_usfcdev_events[ind].interleaved = true; + /* Pass events for freeing slots from TSC driver */ + for (i = 0; i < ARRAY_SIZE(no_filter_cmds); ++i) { + if ((no_filter_cmds[i].type == type) && + (no_filter_cmds[i].code == code) && + (no_filter_cmds[i].value <= u_value)) { + rc = false; + pr_debug("%s: no_filter_cmds[%d]; %d", + __func__, + i, + no_filter_cmds[i].value); + break; + } + } + } + + return rc; +} + +bool usfcdev_register( + uint16_t event_type_ind, + bool (*match_cb)(uint16_t, struct input_dev *dev)) +{ + int ret = 0; + bool rc = false; + + if ((event_type_ind >= MAX_EVENT_TYPE_NUM) || !match_cb) { + pr_err("%s: wrong input: event_type_ind=%d; match_cb=0x%pK\n", + __func__, + event_type_ind, + match_cb); + return false; + } + + if (s_usfcdev_events[event_type_ind].registered_event) { + pr_info("%s: handler[%d] was already registered\n", + __func__, + event_type_ind); + return true; + } + + s_usfcdev_events[event_type_ind].registered_event = true; + s_usfcdev_events[event_type_ind].match_cb = match_cb; + s_usfcdev_events[event_type_ind].event_status = USFCDEV_EVENT_ENABLED; + ret = input_register_handler(&s_usfc_handlers[event_type_ind]); + if (!ret) { + rc = true; + pr_debug("%s: handler[%d] was registered\n", + __func__, + event_type_ind); + } else { + s_usfcdev_events[event_type_ind].registered_event = false; + s_usfcdev_events[event_type_ind].match_cb = NULL; + pr_err("%s: handler[%d] registration failed: ret=%d\n", + __func__, + event_type_ind, + ret); + } + + return rc; +} + +void usfcdev_unregister(uint16_t event_type_ind) +{ + if (event_type_ind >= MAX_EVENT_TYPE_NUM) { + pr_err("%s: wrong input: event_type_ind=%d\n", + __func__, + event_type_ind); + return; + } + if (s_usfcdev_events[event_type_ind].registered_event) { + input_unregister_handler(&s_usfc_handlers[event_type_ind]); + pr_debug("%s: handler[%d] was unregistered\n", + __func__, + event_type_ind); + s_usfcdev_events[event_type_ind].registered_event = false; + s_usfcdev_events[event_type_ind].match_cb = NULL; + s_usfcdev_events[event_type_ind].event_status = + USFCDEV_EVENT_ENABLED; + + } +} + +static inline void usfcdev_send_cmd( + struct input_dev *dev, + struct usfcdev_input_command cmd) +{ + input_event(dev, cmd.type, cmd.code, cmd.value); +} + +static void usfcdev_clean_dev(uint16_t event_type_ind) +{ + struct input_dev *dev = NULL; + int i; + int j; + int retries = 0; + + if (event_type_ind >= MAX_EVENT_TYPE_NUM) { + pr_err("%s: wrong input: event_type_ind=%d\n", + __func__, + event_type_ind); + return; + } + /* Only primary device must exist */ + dev = s_usfc_primary_handles[event_type_ind].dev; + if (dev == NULL) { + pr_err("%s: NULL primary device\n", + __func__); + return; + } + + for (i = 0; i < ARRAY_SIZE(initial_clear_cmds); i++) + usfcdev_send_cmd(dev, initial_clear_cmds[i]); + input_sync(dev); + + /* Send commands to free all slots */ + for (i = 0; i < dev->mt->num_slots; i++) { + s_usfcdev_events[event_type_ind].interleaved = false; + if (input_mt_get_value(&dev->mt->slots[i], + ABS_MT_TRACKING_ID) < 0) { + pr_debug("%s: skipping slot %d", + __func__, i); + continue; + } + slot_clear_cmds[SLOT_CMD_ID].value = i; + for (j = 0; j < ARRAY_SIZE(slot_clear_cmds); j++) + usfcdev_send_cmd(dev, slot_clear_cmds[j]); + + if (s_usfcdev_events[event_type_ind].interleaved) { + pr_debug("%s: interleaved(%d): slot(%d)", + __func__, i, dev->mt->slot); + if (retries++ < MAX_RETRIES) { + --i; + continue; + } + pr_warn("%s: index(%d) reached max retires", + __func__, i); + } + + retries = 0; + input_sync(dev); + } +} + +bool usfcdev_set_filter(uint16_t event_type_ind, bool filter) +{ + bool rc = true; + + if (event_type_ind >= MAX_EVENT_TYPE_NUM) { + pr_err("%s: wrong input: event_type_ind=%d\n", + __func__, + event_type_ind); + return false; + } + + if (s_usfcdev_events[event_type_ind].registered_event) { + + pr_debug("%s: event_type[%d]; filter=%d\n", + __func__, + event_type_ind, + filter + ); + if (filter) { + s_usfcdev_events[event_type_ind].event_status = + USFCDEV_EVENT_DISABLING; + s_usf_pid = current->pid; + usfcdev_clean_dev(event_type_ind); + s_usfcdev_events[event_type_ind].event_status = + USFCDEV_EVENT_DISABLED; + } else + s_usfcdev_events[event_type_ind].event_status = + USFCDEV_EVENT_ENABLED; + } else { + pr_err("%s: event_type[%d] isn't registered\n", + __func__, + event_type_ind); + rc = false; + } + + return rc; +} diff --git a/audio-kernel/dsp/usfcdev.h b/audio-kernel/dsp/usfcdev.h new file mode 100644 index 000000000..03b62c5ec --- /dev/null +++ b/audio-kernel/dsp/usfcdev.h @@ -0,0 +1,28 @@ +/* Copyright (c) 2012, 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 __USFCDEV_H__ +#define __USFCDEV_H__ + +#include + +/* TSC event type index in the containers of the handlers & handles */ +#define TSC_EVENT_TYPE_IND 0 +/* Number of supported event types to be filtered */ +#define MAX_EVENT_TYPE_NUM 1 + +bool usfcdev_register( + uint16_t event_type_ind, + bool (*match_cb)(uint16_t, struct input_dev *dev)); +void usfcdev_unregister(uint16_t event_type_ind); +bool usfcdev_set_filter(uint16_t event_type_ind, bool filter); +#endif /* __USFCDEV_H__ */ diff --git a/audio-kernel/dsp/voice_mhi.c b/audio-kernel/dsp/voice_mhi.c new file mode 100644 index 000000000..0e09c4f14 --- /dev/null +++ b/audio-kernel/dsp/voice_mhi.c @@ -0,0 +1,627 @@ +/* Copyright (c) 2019, 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "adsp_err.h" + +#define VSS_IPKTEXG_CMD_SET_MAILBOX_MEMORY_CONFIG 0x0001333B + +#define VOICE_MHI_STATE_SET(a, b) ((a) |= (1UL<<(b))) +#define VOICE_MHI_STATE_RESET(a, b) ((a) &= ~(1UL<<(b))) +#define VOICE_MHI_STATE_CHECK(a, b) (1UL & (a >> b)) + +#define CMD_STATUS_SUCCESS 0 +#define CMD_STATUS_FAIL 1 +#define TIMEOUT_MS 500 +#define PORT_NUM 0x01 +#define PORT_MASK 0x03 +#define CONVERT_PORT_APR(x, y) (x << 8 | y) + +enum voice_states { + VOICE_MHI_INIT = 0, + VOICE_MHI_PROBED = VOICE_MHI_INIT, + VOICE_MHI_ADSP_UP, + VOICE_MHI_SDX_UP, + VOICE_MHI_INCALL +}; + +struct voice_mhi_addr { + dma_addr_t base; + uint32_t size; +}; + +struct voice_mhi_dev_info { + struct platform_device *pdev; + struct voice_mhi_addr phys_addr; + struct voice_mhi_addr iova_pcie; + struct voice_mhi_addr iova_adsp; +}; + +struct voice_mhi { + struct voice_mhi_dev_info dev_info; + struct mhi_device *mhi_dev; + uint32_t vote_count; + struct mutex mutex; + enum voice_states voice_mhi_state; + bool vote_enable; + bool pcie_enabled; + void *apr_mvm_handle; + struct work_struct voice_mhi_work_pcie; + struct work_struct voice_mhi_work_adsp; + wait_queue_head_t voice_mhi_wait; + u32 mvm_state; + u32 async_err; +}; + +struct vss_ipktexg_cmd_set_mailbox_memory_config_t { + struct apr_hdr hdr; + uint64_t mailbox_mem_address_adsp; + /* + * IOVA of mailbox memory for ADSP access + */ + uint64_t mailbox_mem_address_pcie; + /* + * IOVA of mailbox memory for PCIe access + */ + uint32_t mem_size; + /* + * Size of mailbox memory allocated + */ +} __packed; + +static struct voice_mhi voice_mhi_lcl; + +static int voice_mhi_pcie_up_callback(struct mhi_device *, + const struct mhi_device_id *); +static void voice_mhi_pcie_down_callback(struct mhi_device *); +static void voice_mhi_pcie_status_callback(struct mhi_device *, enum MHI_CB); +static int32_t voice_mhi_apr_callback(struct apr_client_data *data, void *priv); +static int voice_mhi_notifier_service_cb(struct notifier_block *nb, + unsigned long opcode, void *ptr); +static int voice_mhi_apr_register(void); + +static struct notifier_block voice_mhi_service_nb = { + .notifier_call = voice_mhi_notifier_service_cb, + .priority = -INT_MAX, +}; + +static const struct mhi_device_id voice_mhi_match_table[] = { + { .chan = "AUDIO_VOICE_0", .driver_data = 0 }, + {}, +}; + +static struct mhi_driver voice_mhi_driver = { + .id_table = voice_mhi_match_table, + .probe = voice_mhi_pcie_up_callback, + .remove = voice_mhi_pcie_down_callback, + .status_cb = voice_mhi_pcie_status_callback, + .driver = { + .name = "voice_mhi_audio", + .owner = THIS_MODULE, + }, +}; + +static int voice_mhi_notifier_service_cb(struct notifier_block *nb, + unsigned long opcode, void *ptr) +{ + pr_debug("%s: opcode 0x%lx\n", __func__, opcode); + + switch (opcode) { + case AUDIO_NOTIFIER_SERVICE_DOWN: + if (voice_mhi_lcl.apr_mvm_handle) { + apr_reset(voice_mhi_lcl.apr_mvm_handle); + voice_mhi_lcl.apr_mvm_handle = NULL; + VOICE_MHI_STATE_RESET(voice_mhi_lcl.voice_mhi_state, + VOICE_MHI_ADSP_UP); + } + break; + case AUDIO_NOTIFIER_SERVICE_UP: + if (!VOICE_MHI_STATE_CHECK(voice_mhi_lcl.voice_mhi_state, + VOICE_MHI_ADSP_UP)) { + VOICE_MHI_STATE_SET(voice_mhi_lcl.voice_mhi_state, + VOICE_MHI_ADSP_UP); + schedule_work(&voice_mhi_lcl.voice_mhi_work_adsp); + } + break; + default: + break; + } + + return NOTIFY_OK; + +} + +static int32_t voice_mhi_apr_callback(struct apr_client_data *data, void *priv) +{ + uint32_t *ptr1; + + if (data == NULL) { + pr_err("%s: data is NULL\n", __func__); + return -EINVAL; + } + + pr_debug("%s: Payload Length = %d, opcode=%x\n", __func__, + data->payload_size, data->opcode); + + switch (data->opcode) { + + case APR_BASIC_RSP_RESULT: + if (data->payload_size < 2 * sizeof(uint32_t)) { + pr_err("%s: APR_BASIC_RSP_RESULT payload less than expected\n", + __func__); + return 0; + } + ptr1 = data->payload; + switch (ptr1[0]) { + case VSS_IPKTEXG_CMD_SET_MAILBOX_MEMORY_CONFIG: + pr_debug("%s: cmd VSS_IPKTEXG_CMD_SET_MAILBOX_MEMORY_CONFIG\n", + __func__); + voice_mhi_lcl.mvm_state = CMD_STATUS_SUCCESS; + voice_mhi_lcl.async_err = ptr1[1]; + wake_up(&voice_mhi_lcl.voice_mhi_wait); + break; + default: + pr_err("%s: Invalid cmd response 0x%x 0x%x\n", __func__, + ptr1[0], ptr1[1]); + break; + } + break; + + case APR_RSP_ACCEPTED: + if (data->payload_size < sizeof(uint32_t)) { + pr_err("%s: APR_RSP_ACCEPTED payload less than expected\n", + __func__); + return 0; + } + ptr1 = data->payload; + if (ptr1[0]) + pr_debug("%s: APR_RSP_ACCEPTED for 0x%x:\n", + __func__, ptr1[0]); + break; + + case RESET_EVENTS: + //Should we handle here or audio notifier down? + if (voice_mhi_lcl.apr_mvm_handle) { + apr_reset(voice_mhi_lcl.apr_mvm_handle); + voice_mhi_lcl.apr_mvm_handle = NULL; + VOICE_MHI_STATE_RESET(voice_mhi_lcl.voice_mhi_state, + VOICE_MHI_ADSP_UP); + } + break; + + default: + pr_err("%s: Invalid opcode %d\n", __func__, + data->opcode); + break; + + } + return 0; +} + +/** + * voice_mhi_start - + * Start vote for MHI/PCIe clock + * + * Returns 0 on success or error on failure + */ +int voice_mhi_start(void) +{ + int ret = 0; + + mutex_lock(&voice_mhi_lcl.mutex); + if (voice_mhi_lcl.pcie_enabled) { + if (!voice_mhi_lcl.mhi_dev) { + pr_err("%s: NULL device found\n", __func__); + ret = -EINVAL; + goto done; + } + if (voice_mhi_lcl.vote_count == 0) { + ret = mhi_device_get_sync(voice_mhi_lcl.mhi_dev); + if (ret) { + pr_err("%s: mhi_device_get_sync failed\n", + __func__); + ret = -EINVAL; + goto done; + } + pr_debug("%s: mhi_device_get_sync success\n", __func__); + } else { + /* For DSDA, no additional voting is needed */ + pr_debug("%s: mhi is already voted\n", __func__); + } + voice_mhi_lcl.vote_count++; + } else { + /* PCIe not supported - return success*/ + goto done; + } +done: + mutex_unlock(&voice_mhi_lcl.mutex); + + return ret; +} +EXPORT_SYMBOL(voice_mhi_start); + +/** + * voice_mhi_end - + * End vote for MHI/PCIe clock + * + * Returns 0 on success or error on failure + */ +int voice_mhi_end(void) +{ + mutex_lock(&voice_mhi_lcl.mutex); + if (voice_mhi_lcl.pcie_enabled) { + if (!voice_mhi_lcl.mhi_dev || voice_mhi_lcl.vote_count == 0) { + pr_err("%s: NULL device found\n", __func__); + mutex_unlock(&voice_mhi_lcl.mutex); + return -EINVAL; + } + + if (voice_mhi_lcl.vote_count == 1) + mhi_device_put(voice_mhi_lcl.mhi_dev); + voice_mhi_lcl.vote_count--; + } + mutex_unlock(&voice_mhi_lcl.mutex); + + return 0; +} +EXPORT_SYMBOL(voice_mhi_end); + +static int voice_mhi_set_mailbox_memory_config(void) +{ + struct vss_ipktexg_cmd_set_mailbox_memory_config_t mb_memory_config; + int ret = 0; + void *apr_mvm; + + if (!voice_mhi_lcl.apr_mvm_handle) { + pr_err("%s: APR handle is NULL\n", __func__); + return -EINVAL; + } + + memset(&mb_memory_config, 0, sizeof(mb_memory_config)); + mb_memory_config.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + mb_memory_config.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(mb_memory_config) - APR_HDR_SIZE); + pr_debug("%s: pkt size = %d\n", __func__, + mb_memory_config.hdr.pkt_size); + + mutex_lock(&voice_mhi_lcl.mutex); + apr_mvm = voice_mhi_lcl.apr_mvm_handle; + + /* + * Handle can be NULL as it is not tied to any session + */ + mb_memory_config.hdr.src_port = CONVERT_PORT_APR(PORT_NUM, PORT_MASK); + mb_memory_config.hdr.dest_port = 0; + mb_memory_config.hdr.token = 0; + mb_memory_config.hdr.opcode = VSS_IPKTEXG_CMD_SET_MAILBOX_MEMORY_CONFIG; + mb_memory_config.mailbox_mem_address_pcie = + voice_mhi_lcl.dev_info.iova_pcie.base; + mb_memory_config.mailbox_mem_address_adsp = + voice_mhi_lcl.dev_info.iova_adsp.base; + mb_memory_config.mem_size = voice_mhi_lcl.dev_info.iova_adsp.size; + voice_mhi_lcl.mvm_state = CMD_STATUS_FAIL; + voice_mhi_lcl.async_err = 0; + + ret = apr_send_pkt(apr_mvm, (uint32_t *) &mb_memory_config); + if (ret < 0) { + pr_err("%s: Set mailbox memory config failed ret=%d\n", + __func__, ret); + goto unlock; + } + + ret = wait_event_timeout(voice_mhi_lcl.voice_mhi_wait, + (voice_mhi_lcl.mvm_state == + CMD_STATUS_SUCCESS), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + ret = -ETIME; + goto unlock; + } + if (voice_mhi_lcl.async_err > 0) { + pr_err("%s: DSP returned error[%d]\n", + __func__, voice_mhi_lcl.async_err); + ret = voice_mhi_lcl.async_err; + goto unlock; + } + ret = 0; +unlock: + mutex_unlock(&voice_mhi_lcl.mutex); + return ret; +} + +static void voice_mhi_map_pcie_and_send(struct work_struct *work) +{ + dma_addr_t iova, phys_addr; + uint32_t mem_size; + struct device *md; + + mutex_lock(&voice_mhi_lcl.mutex); + if (voice_mhi_lcl.mhi_dev) { + md = &voice_mhi_lcl.mhi_dev->dev; + } else { + pr_err("%s: MHI device handle is NULL\n", __func__); + goto err; + } + + phys_addr = voice_mhi_lcl.dev_info.phys_addr.base; + mem_size = voice_mhi_lcl.dev_info.iova_pcie.size; + if (md) { + iova = dma_map_resource(md->parent, phys_addr, mem_size, + DMA_BIDIRECTIONAL, 0); + if (dma_mapping_error(md->parent, iova)) { + pr_err("%s: dma_mapping_error\n", __func__); + goto err; + } + pr_debug("%s: dma_mapping_success iova:0x%lx\n", + __func__, (unsigned long)iova); + voice_mhi_lcl.dev_info.iova_pcie.base = iova; + + if (q6core_is_adsp_ready()) { + if (VOICE_MHI_STATE_CHECK(voice_mhi_lcl.voice_mhi_state, + VOICE_MHI_SDX_UP)) { + mutex_unlock(&voice_mhi_lcl.mutex); + voice_mhi_set_mailbox_memory_config(); + return; + } + } + } + +err: + mutex_unlock(&voice_mhi_lcl.mutex); +} + +static void voice_mhi_register_apr_and_send(struct work_struct *work) +{ + int ret = 0; + + ret = voice_mhi_apr_register(); + if (ret) { + pr_err("%s: APR registration failed %d\n", __func__, ret); + return; + } + mutex_lock(&voice_mhi_lcl.mutex); + if (q6core_is_adsp_ready()) { + if (VOICE_MHI_STATE_CHECK(voice_mhi_lcl.voice_mhi_state, + VOICE_MHI_SDX_UP)) { + mutex_unlock(&voice_mhi_lcl.mutex); + voice_mhi_set_mailbox_memory_config(); + return; + } + } + mutex_unlock(&voice_mhi_lcl.mutex); +} + +static int voice_mhi_pcie_up_callback(struct mhi_device *voice_mhi_dev, + const struct mhi_device_id *id) +{ + + if ((!voice_mhi_dev) || (id != &voice_mhi_match_table[0])) { + pr_err("%s: Invalid device or table received\n", __func__); + return -EINVAL; + } + pr_debug("%s: MHI PCIe UP callback\n", __func__); + mutex_lock(&voice_mhi_lcl.mutex); + voice_mhi_lcl.mhi_dev = voice_mhi_dev; + VOICE_MHI_STATE_SET(voice_mhi_lcl.voice_mhi_state, VOICE_MHI_SDX_UP); + mutex_unlock(&voice_mhi_lcl.mutex); + schedule_work(&voice_mhi_lcl.voice_mhi_work_pcie); + return 0; +} + +static void voice_mhi_pcie_down_callback(struct mhi_device *voice_mhi_dev) +{ + dma_addr_t iova; + struct device *md; + + mutex_lock(&voice_mhi_lcl.mutex); + + if (voice_mhi_lcl.mhi_dev) + md = &voice_mhi_lcl.mhi_dev->dev; + + VOICE_MHI_STATE_RESET(voice_mhi_lcl.voice_mhi_state, VOICE_MHI_SDX_UP); + iova = voice_mhi_lcl.dev_info.iova_pcie.base; + + if (md) + dma_unmap_resource(md->parent, iova, PAGE_SIZE, + DMA_BIDIRECTIONAL, 0); + + voice_mhi_lcl.mhi_dev = NULL; + voice_mhi_lcl.vote_count = 0; + mutex_unlock(&voice_mhi_lcl.mutex); +} + +static void voice_mhi_pcie_status_callback(struct mhi_device *voice_mhi_dev, + enum MHI_CB mhi_cb) +{ + +} + +static int voice_mhi_apr_register(void) +{ + int ret = 0; + + mutex_lock(&voice_mhi_lcl.mutex); + voice_mhi_lcl.apr_mvm_handle = apr_register("ADSP", "MVM", + (apr_fn)voice_mhi_apr_callback, + CONVERT_PORT_APR(PORT_NUM, + PORT_MASK), + &voice_mhi_lcl); + if (voice_mhi_lcl.apr_mvm_handle == NULL) { + pr_err("%s: error in APR register\n", __func__); + ret = -ENODEV; + } + mutex_unlock(&voice_mhi_lcl.mutex); + + return ret; +} + +static int voice_mhi_probe(struct platform_device *pdev) +{ + int ret = 0; + struct device_node *node; + uint32_t mem_size = 0; + void *ptr; + dma_addr_t phys_addr, iova; + const __be32 *cell; + + pr_debug("%s:\n", __func__); + + INIT_WORK(&voice_mhi_lcl.voice_mhi_work_pcie, + voice_mhi_map_pcie_and_send); + INIT_WORK(&voice_mhi_lcl.voice_mhi_work_adsp, + voice_mhi_register_apr_and_send); + init_waitqueue_head(&voice_mhi_lcl.voice_mhi_wait); + + node = of_parse_phandle(pdev->dev.of_node, "memory-region", 0); + if (node) { + cell = of_get_property(node, "size", NULL); + if (cell) + mem_size = of_read_number(cell, 2); + else { + pr_err("%s: cell not found\n", __func__); + ret = -EINVAL; + goto done; + } + } else { + pr_err("%s: Node read failed\n", __func__); + ret = -EINVAL; + goto done; + } + + pr_debug("%s: mem_size = %d\n", __func__, mem_size); + + if (mem_size) { + ptr = dma_alloc_attrs(&pdev->dev, mem_size, &phys_addr, + GFP_KERNEL, DMA_ATTR_NO_KERNEL_MAPPING); + if (IS_ERR_OR_NULL(ptr)) { + pr_err("%s: Memory alloc failed\n", __func__); + ret = -ENOMEM; + goto done; + } else { + pr_debug("%s: Memory alloc success phys_addr:0x%lx\n", + __func__, (unsigned long)phys_addr); + } + + ret = msm_audio_ion_dma_map(&phys_addr, &iova, mem_size, + DMA_BIDIRECTIONAL); + if (ret) { + pr_err("%s: dma mapping failed %d\n", __func__, ret); + goto err_free; + } + pr_debug("%s: dma_mapping_success iova:0x%lx\n", + __func__, (unsigned long)iova); + + voice_mhi_lcl.dev_info.phys_addr.base = phys_addr; + voice_mhi_lcl.dev_info.iova_adsp.base = iova; + voice_mhi_lcl.dev_info.iova_adsp.size = mem_size; + voice_mhi_lcl.dev_info.iova_pcie.size = mem_size; + VOICE_MHI_STATE_SET(voice_mhi_lcl.voice_mhi_state, + VOICE_MHI_ADSP_UP); + + ret = voice_mhi_apr_register(); + /* If fails register during audio notifier UP event */ + if (ret) + pr_err("%s: APR register failed %d\n", __func__, ret); + ret = mhi_driver_register(&voice_mhi_driver); + if (ret) { + pr_err("%s: mhi register failed %d\n", __func__, ret); + goto done; + } + + ret = audio_notifier_register("voice_mhi", + AUDIO_NOTIFIER_ADSP_DOMAIN, + &voice_mhi_service_nb); + if (ret < 0) + pr_err("%s: Audio notifier register failed ret = %d\n", + __func__, ret); + + mutex_lock(&voice_mhi_lcl.mutex); + voice_mhi_lcl.dev_info.pdev = pdev; + voice_mhi_lcl.pcie_enabled = true; + VOICE_MHI_STATE_SET(voice_mhi_lcl.voice_mhi_state, + VOICE_MHI_PROBED); + mutex_unlock(&voice_mhi_lcl.mutex); + } else { + pr_err("%s: Memory size can't be zero\n", __func__); + ret = -ENOMEM; + goto done; + } + +done: + return ret; +err_free: + dma_free_attrs(&pdev->dev, mem_size, ptr, phys_addr, + DMA_ATTR_NO_KERNEL_MAPPING); + return 0; + +} + +static int voice_mhi_remove(struct platform_device *pdev) +{ + mhi_driver_unregister(&voice_mhi_driver); + return 0; +} +static const struct of_device_id voice_mhi_of_match[] = { + { .compatible = "qcom,voice-mhi-audio", }, + {}, +}; +static struct platform_driver voice_mhi_platform_driver = { + .probe = voice_mhi_probe, + .remove = voice_mhi_remove, + .driver = { + .name = "voice_mhi_audio", + .owner = THIS_MODULE, + .of_match_table = voice_mhi_of_match, + } +}; + +int __init voice_mhi_init(void) +{ + int ret = 0; + + memset(&voice_mhi_lcl, 0, sizeof(voice_mhi_lcl)); + mutex_init(&voice_mhi_lcl.mutex); + + //Add remaining init here + voice_mhi_lcl.pcie_enabled = false; + voice_mhi_lcl.voice_mhi_state = VOICE_MHI_INIT; + voice_mhi_lcl.vote_count = 0; + voice_mhi_lcl.apr_mvm_handle = NULL; + ret = platform_driver_register(&voice_mhi_platform_driver); + + return ret; +} + +void __exit voice_mhi_exit(void) +{ + mutex_destroy(&voice_mhi_lcl.mutex); + platform_driver_unregister(&voice_mhi_platform_driver); +} + +MODULE_DESCRIPTION("Voice MHI module"); +MODULE_LICENSE("GPL v2"); diff --git a/audio-kernel/include/Kbuild b/audio-kernel/include/Kbuild new file mode 100644 index 000000000..bab1145bc --- /dev/null +++ b/audio-kernel/include/Kbuild @@ -0,0 +1,2 @@ +# Top-level Makefile calls into asm-$(ARCH) +# List only non-arch directories below diff --git a/audio-kernel/include/asoc/wcd-irq.h b/audio-kernel/include/asoc/wcd-irq.h new file mode 100644 index 000000000..63502f828 --- /dev/null +++ b/audio-kernel/include/asoc/wcd-irq.h @@ -0,0 +1,63 @@ +/* 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 __WCD_IRQ_H_ +#define __WCD_IRQ_H_ + +#include +#include +#include + +struct wcd_irq_info { + struct regmap_irq_chip *wcd_regmap_irq_chip; + char *codec_name; + struct regmap *regmap; + struct regmap_irq_chip_data *irq_chip; + struct device *dev; +}; + +#if IS_ENABLED(CONFIG_WCD9XXX_CODEC_CORE) +int wcd_irq_init(struct wcd_irq_info *irq_info, struct irq_domain **virq); +int wcd_irq_exit(struct wcd_irq_info *irq_info, struct irq_domain *virq); +int wcd_request_irq(struct wcd_irq_info *irq_info, int irq, const char *name, + irq_handler_t handler, void *data); +void wcd_free_irq(struct wcd_irq_info *irq_info, int irq, void *data); +void wcd_enable_irq(struct wcd_irq_info *irq_info, int irq); +void wcd_disable_irq(struct wcd_irq_info *irq_info, int irq); +#else +static inline int wcd_irq_init(struct wcd_irq_info *irq_info, + struct irq_domain **virq) +{ + return 0; +}; +static inline int wcd_irq_exit(struct wcd_irq_info *irq_info, + struct irq_domain *virq) +{ + return 0; +}; +static inline int wcd_request_irq(struct wcd_irq_info *irq, + int irq, const char *name, + irq_handler_t handler, void *data) +{ + return 0; +}; +static inline void wcd_free_irq(struct wcd_irq_info *irq, int irq, void *data); +{ +}; +static inline void wcd_enable_irq(struct wcd_irq_info *irq, int irq); +{ +}; +static inline void wcd_disable_irq(struct wcd_irq_info *irq, int irq); +{ +}; +#endif +#endif /* __WCD_IRQ_H_ */ diff --git a/audio-kernel/include/asoc/wcd934x_registers.h b/audio-kernel/include/asoc/wcd934x_registers.h new file mode 100644 index 000000000..a82487509 --- /dev/null +++ b/audio-kernel/include/asoc/wcd934x_registers.h @@ -0,0 +1,1848 @@ +/* + * Copyright (c) 2016, 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 _WCD934X_REGISTERS_H +#define _WCD934X_REGISTERS_H + +#define WCD934X_PAGE_SIZE 256 +#define WCD934X_NUM_PAGES 256 + +extern const u8 * const wcd934x_reg[WCD934X_NUM_PAGES]; + +enum { + WCD934X_PAGE_0 = 0, + WCD934X_PAGE_1, + WCD934X_PAGE_2, + WCD934X_PAGE_4 = 4, + WCD934X_PAGE_5, + WCD934X_PAGE_6, + WCD934X_PAGE_7, + WCD934X_PAGE_10 = 0xA, + WCD934X_PAGE_11, + WCD934X_PAGE_12, + WCD934X_PAGE_13, + WCD934X_PAGE_14, + WCD934X_PAGE_15, + WCD934X_PAGE_0x50, + WCD934X_PAGE_0X80, +}; + +enum { + WCD934X_WRITE = 0, + WCD934X_READ, + WCD934X_READ_WRITE, +}; + +/* Page-0 Registers */ +#define WCD934X_PAGE0_PAGE_REGISTER 0x0000 +#define WCD934X_CODEC_RPM_CLK_BYPASS 0x0001 +#define WCD934X_CODEC_RPM_CLK_GATE 0x0002 +#define WCD934X_CODEC_RPM_CLK_MCLK_CFG 0x0003 +#define WCD934X_CODEC_RPM_CLK_MCLK2_CFG 0x0004 +#define WCD934X_CODEC_RPM_I2S_DSD_CLK_SEL 0x0005 +#define WCD934X_CODEC_RPM_RST_CTL 0x0009 +#define WCD934X_CODEC_RPM_PWR_CDC_DIG_HM_CTL 0x0011 +#define WCD934X_CHIP_TIER_CTRL_CHIP_ID_BYTE0 0x0021 +#define WCD934X_CHIP_TIER_CTRL_CHIP_ID_BYTE1 0x0022 +#define WCD934X_CHIP_TIER_CTRL_CHIP_ID_BYTE2 0x0023 +#define WCD934X_CHIP_TIER_CTRL_CHIP_ID_BYTE3 0x0024 +#define WCD934X_CHIP_TIER_CTRL_EFUSE_CTL 0x0025 +#define WCD934X_CHIP_TIER_CTRL_EFUSE_TEST0 0x0026 +#define WCD934X_CHIP_TIER_CTRL_EFUSE_TEST1 0x0027 +#define WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT0 0x0029 +#define WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT1 0x002a +#define WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT2 0x002b +#define WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT3 0x002c +#define WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT4 0x002d +#define WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT5 0x002e +#define WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT6 0x002f +#define WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT7 0x0030 +#define WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT8 0x0031 +#define WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT9 0x0032 +#define WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT10 0x0033 +#define WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT11 0x0034 +#define WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT12 0x0035 +#define WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT13 0x0036 +#define WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT14 0x0037 +#define WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT15 0x0038 +#define WCD934X_CHIP_TIER_CTRL_EFUSE_STATUS 0x0039 +#define WCD934X_CHIP_TIER_CTRL_I2C_SLAVE_ID_NONNEGO 0x003a +#define WCD934X_CHIP_TIER_CTRL_I2C_SLAVE_ID_1 0x003b +#define WCD934X_CHIP_TIER_CTRL_I2C_SLAVE_ID_2 0x003c +#define WCD934X_CHIP_TIER_CTRL_I2C_SLAVE_ID_3 0x003d +#define WCD934X_CHIP_TIER_CTRL_ANA_WAIT_STATE_CTL 0x003e +#define WCD934X_CHIP_TIER_CTRL_SLNQ_WAIT_STATE_CTL 0x003f +#define WCD934X_CHIP_TIER_CTRL_I2C_ACTIVE 0x0040 +#define WCD934X_CHIP_TIER_CTRL_ALT_FUNC_EN 0x0041 +#define WCD934X_CHIP_TIER_CTRL_GPIO_CTL_OE 0x0042 +#define WCD934X_CHIP_TIER_CTRL_GPIO_CTL_DATA 0x0043 +#define WCD934X_DATA_HUB_RX0_CFG 0x0051 +#define WCD934X_DATA_HUB_RX1_CFG 0x0052 +#define WCD934X_DATA_HUB_RX2_CFG 0x0053 +#define WCD934X_DATA_HUB_RX3_CFG 0x0054 +#define WCD934X_DATA_HUB_RX4_CFG 0x0055 +#define WCD934X_DATA_HUB_RX5_CFG 0x0056 +#define WCD934X_DATA_HUB_RX6_CFG 0x0057 +#define WCD934X_DATA_HUB_RX7_CFG 0x0058 +#define WCD934X_DATA_HUB_SB_TX0_INP_CFG 0x0061 +#define WCD934X_DATA_HUB_SB_TX1_INP_CFG 0x0062 +#define WCD934X_DATA_HUB_SB_TX2_INP_CFG 0x0063 +#define WCD934X_DATA_HUB_SB_TX3_INP_CFG 0x0064 +#define WCD934X_DATA_HUB_SB_TX4_INP_CFG 0x0065 +#define WCD934X_DATA_HUB_SB_TX5_INP_CFG 0x0066 +#define WCD934X_DATA_HUB_SB_TX6_INP_CFG 0x0067 +#define WCD934X_DATA_HUB_SB_TX7_INP_CFG 0x0068 +#define WCD934X_DATA_HUB_SB_TX8_INP_CFG 0x0069 +#define WCD934X_DATA_HUB_SB_TX9_INP_CFG 0x006a +#define WCD934X_DATA_HUB_SB_TX10_INP_CFG 0x006b +#define WCD934X_DATA_HUB_SB_TX11_INP_CFG 0x006c +#define WCD934X_DATA_HUB_SB_TX13_INP_CFG 0x006e +#define WCD934X_DATA_HUB_SB_TX14_INP_CFG 0x006f +#define WCD934X_DATA_HUB_SB_TX15_INP_CFG 0x0070 +#define WCD934X_DATA_HUB_I2S_TX0_CFG 0x0071 +#define WCD934X_DATA_HUB_I2S_TX1_0_CFG 0x0073 +#define WCD934X_DATA_HUB_I2S_TX1_1_CFG 0x0074 +#define WCD934X_DATA_HUB_I2S_0_CTL 0x0081 +#define WCD934X_DATA_HUB_I2S_1_CTL 0x0082 +#define WCD934X_DATA_HUB_I2S_2_CTL 0x0083 +#define WCD934X_DATA_HUB_I2S_3_CTL 0x0084 +#define WCD934X_DATA_HUB_I2S_CLKSRC_CTL 0x0085 +#define WCD934X_DATA_HUB_I2S_COMMON_CTL 0x0086 +#define WCD934X_DATA_HUB_I2S_0_TDM_CTL 0x0087 +#define WCD934X_DATA_HUB_I2S_STATUS 0x0088 +#define WCD934X_DMA_RDMA_CTL_0 0x0091 +#define WCD934X_DMA_CH_2_3_CFG_RDMA_0 0x0092 +#define WCD934X_DMA_CH_0_1_CFG_RDMA_0 0x0093 +#define WCD934X_DMA_RDMA_CTL_1 0x0094 +#define WCD934X_DMA_CH_2_3_CFG_RDMA_1 0x0095 +#define WCD934X_DMA_CH_0_1_CFG_RDMA_1 0x0096 +#define WCD934X_DMA_RDMA_CTL_2 0x0097 +#define WCD934X_DMA_CH_2_3_CFG_RDMA_2 0x0098 +#define WCD934X_DMA_CH_0_1_CFG_RDMA_2 0x0099 +#define WCD934X_DMA_RDMA_CTL_3 0x009A +#define WCD934X_DMA_CH_2_3_CFG_RDMA_3 0x009B +#define WCD934X_DMA_CH_0_1_CFG_RDMA_3 0x009C +#define WCD934X_DMA_RDMA_CTL_4 0x009D +#define WCD934X_DMA_CH_2_3_CFG_RDMA_4 0x009E +#define WCD934X_DMA_CH_0_1_CFG_RDMA_4 0x009F +#define WCD934X_DMA_RDMA4_PRT_CFG 0x00b1 +#define WCD934X_DMA_RDMA_SBTX0_7_CFG 0x00b9 +#define WCD934X_DMA_RDMA_SBTX8_11_CFG 0x00ba +#define WCD934X_DMA_WDMA_CTL_0 0x00c1 +#define WCD934X_DMA_CH_4_5_CFG_WDMA_0 0x00c2 +#define WCD934X_DMA_CH_2_3_CFG_WDMA_0 0x00c3 +#define WCD934X_DMA_CH_0_1_CFG_WDMA_0 0x00c4 +#define WCD934X_DMA_WDMA_CTL_1 0x00C6 +#define WCD934X_DMA_CH_4_5_CFG_WDMA_1 0x00C7 +#define WCD934X_DMA_CH_2_3_CFG_WDMA_1 0x00C8 +#define WCD934X_DMA_CH_0_1_CFG_WDMA_1 0x00C9 +#define WCD934X_DMA_WDMA_CTL_2 0x00CB +#define WCD934X_DMA_CH_4_5_CFG_WDMA_2 0x00CC +#define WCD934X_DMA_CH_2_3_CFG_WDMA_2 0x00CD +#define WCD934X_DMA_CH_0_1_CFG_WDMA_2 0x00CE +#define WCD934X_DMA_WDMA_CTL_3 0x00D0 +#define WCD934X_DMA_CH_4_5_CFG_WDMA_3 0x00D1 +#define WCD934X_DMA_CH_2_3_CFG_WDMA_3 0x00D2 +#define WCD934X_DMA_CH_0_1_CFG_WDMA_3 0x00D3 +#define WCD934X_DMA_WDMA_CTL_4 0x00D5 +#define WCD934X_DMA_CH_4_5_CFG_WDMA_4 0x00D6 +#define WCD934X_DMA_CH_2_3_CFG_WDMA_4 0x00D7 +#define WCD934X_DMA_CH_0_1_CFG_WDMA_4 0x00D8 +#define WCD934X_DMA_WDMA0_PRT_CFG 0x00E1 +#define WCD934X_DMA_WDMA3_PRT_CFG 0x00E2 +#define WCD934X_DMA_WDMA4_PRT0_3_CFG 0x00E3 +#define WCD934X_DMA_WDMA4_PRT4_7_CFG 0x00E4 +#define WCD934X_PAGE1_PAGE_REGISTER 0x0100 +#define WCD934X_CPE_FLL_USER_CTL_0 0x0101 +#define WCD934X_CPE_FLL_USER_CTL_1 0x0102 +#define WCD934X_CPE_FLL_USER_CTL_2 0x0103 +#define WCD934X_CPE_FLL_USER_CTL_3 0x0104 +#define WCD934X_CPE_FLL_USER_CTL_4 0x0105 +#define WCD934X_CPE_FLL_USER_CTL_5 0x0106 +#define WCD934X_CPE_FLL_USER_CTL_6 0x0107 +#define WCD934X_CPE_FLL_USER_CTL_7 0x0108 +#define WCD934X_CPE_FLL_USER_CTL_8 0x0109 +#define WCD934X_CPE_FLL_USER_CTL_9 0x010a +#define WCD934X_CPE_FLL_L_VAL_CTL_0 0x010b +#define WCD934X_CPE_FLL_L_VAL_CTL_1 0x010c +#define WCD934X_CPE_FLL_DSM_FRAC_CTL_0 0x010d +#define WCD934X_CPE_FLL_DSM_FRAC_CTL_1 0x010e +#define WCD934X_CPE_FLL_CONFIG_CTL_0 0x010f +#define WCD934X_CPE_FLL_CONFIG_CTL_1 0x0110 +#define WCD934X_CPE_FLL_CONFIG_CTL_2 0x0111 +#define WCD934X_CPE_FLL_CONFIG_CTL_3 0x0112 +#define WCD934X_CPE_FLL_CONFIG_CTL_4 0x0113 +#define WCD934X_CPE_FLL_TEST_CTL_0 0x0114 +#define WCD934X_CPE_FLL_TEST_CTL_1 0x0115 +#define WCD934X_CPE_FLL_TEST_CTL_2 0x0116 +#define WCD934X_CPE_FLL_TEST_CTL_3 0x0117 +#define WCD934X_CPE_FLL_TEST_CTL_4 0x0118 +#define WCD934X_CPE_FLL_TEST_CTL_5 0x0119 +#define WCD934X_CPE_FLL_TEST_CTL_6 0x011a +#define WCD934X_CPE_FLL_TEST_CTL_7 0x011b +#define WCD934X_CPE_FLL_FREQ_CTL_0 0x011c +#define WCD934X_CPE_FLL_FREQ_CTL_1 0x011d +#define WCD934X_CPE_FLL_FREQ_CTL_2 0x011e +#define WCD934X_CPE_FLL_FREQ_CTL_3 0x011f +#define WCD934X_CPE_FLL_SSC_CTL_0 0x0120 +#define WCD934X_CPE_FLL_SSC_CTL_1 0x0121 +#define WCD934X_CPE_FLL_SSC_CTL_2 0x0122 +#define WCD934X_CPE_FLL_SSC_CTL_3 0x0123 +#define WCD934X_CPE_FLL_FLL_MODE 0x0124 +#define WCD934X_CPE_FLL_STATUS_0 0x0125 +#define WCD934X_CPE_FLL_STATUS_1 0x0126 +#define WCD934X_CPE_FLL_STATUS_2 0x0127 +#define WCD934X_CPE_FLL_STATUS_3 0x0128 +#define WCD934X_I2S_FLL_USER_CTL_0 0x0141 +#define WCD934X_I2S_FLL_USER_CTL_1 0x0142 +#define WCD934X_I2S_FLL_USER_CTL_2 0x0143 +#define WCD934X_I2S_FLL_USER_CTL_3 0x0144 +#define WCD934X_I2S_FLL_USER_CTL_4 0x0145 +#define WCD934X_I2S_FLL_USER_CTL_5 0x0146 +#define WCD934X_I2S_FLL_USER_CTL_6 0x0147 +#define WCD934X_I2S_FLL_USER_CTL_7 0x0148 +#define WCD934X_I2S_FLL_USER_CTL_8 0x0149 +#define WCD934X_I2S_FLL_USER_CTL_9 0x014a +#define WCD934X_I2S_FLL_L_VAL_CTL_0 0x014b +#define WCD934X_I2S_FLL_L_VAL_CTL_1 0x014c +#define WCD934X_I2S_FLL_DSM_FRAC_CTL_0 0x014d +#define WCD934X_I2S_FLL_DSM_FRAC_CTL_1 0x014e +#define WCD934X_I2S_FLL_CONFIG_CTL_0 0x014f +#define WCD934X_I2S_FLL_CONFIG_CTL_1 0x0150 +#define WCD934X_I2S_FLL_CONFIG_CTL_2 0x0151 +#define WCD934X_I2S_FLL_CONFIG_CTL_3 0x0152 +#define WCD934X_I2S_FLL_CONFIG_CTL_4 0x0153 +#define WCD934X_I2S_FLL_TEST_CTL_0 0x0154 +#define WCD934X_I2S_FLL_TEST_CTL_1 0x0155 +#define WCD934X_I2S_FLL_TEST_CTL_2 0x0156 +#define WCD934X_I2S_FLL_TEST_CTL_3 0x0157 +#define WCD934X_I2S_FLL_TEST_CTL_4 0x0158 +#define WCD934X_I2S_FLL_TEST_CTL_5 0x0159 +#define WCD934X_I2S_FLL_TEST_CTL_6 0x015a +#define WCD934X_I2S_FLL_TEST_CTL_7 0x015b +#define WCD934X_I2S_FLL_FREQ_CTL_0 0x015c +#define WCD934X_I2S_FLL_FREQ_CTL_1 0x015d +#define WCD934X_I2S_FLL_FREQ_CTL_2 0x015e +#define WCD934X_I2S_FLL_FREQ_CTL_3 0x015f +#define WCD934X_I2S_FLL_SSC_CTL_0 0x0160 +#define WCD934X_I2S_FLL_SSC_CTL_1 0x0161 +#define WCD934X_I2S_FLL_SSC_CTL_2 0x0162 +#define WCD934X_I2S_FLL_SSC_CTL_3 0x0163 +#define WCD934X_I2S_FLL_FLL_MODE 0x0164 +#define WCD934X_I2S_FLL_STATUS_0 0x0165 +#define WCD934X_I2S_FLL_STATUS_1 0x0166 +#define WCD934X_I2S_FLL_STATUS_2 0x0167 +#define WCD934X_I2S_FLL_STATUS_3 0x0168 +#define WCD934X_SB_FLL_USER_CTL_0 0x0181 +#define WCD934X_SB_FLL_USER_CTL_1 0x0182 +#define WCD934X_SB_FLL_USER_CTL_2 0x0183 +#define WCD934X_SB_FLL_USER_CTL_3 0x0184 +#define WCD934X_SB_FLL_USER_CTL_4 0x0185 +#define WCD934X_SB_FLL_USER_CTL_5 0x0186 +#define WCD934X_SB_FLL_USER_CTL_6 0x0187 +#define WCD934X_SB_FLL_USER_CTL_7 0x0188 +#define WCD934X_SB_FLL_USER_CTL_8 0x0189 +#define WCD934X_SB_FLL_USER_CTL_9 0x018a +#define WCD934X_SB_FLL_L_VAL_CTL_0 0x018b +#define WCD934X_SB_FLL_L_VAL_CTL_1 0x018c +#define WCD934X_SB_FLL_DSM_FRAC_CTL_0 0x018d +#define WCD934X_SB_FLL_DSM_FRAC_CTL_1 0x018e +#define WCD934X_SB_FLL_CONFIG_CTL_0 0x018f +#define WCD934X_SB_FLL_CONFIG_CTL_1 0x0190 +#define WCD934X_SB_FLL_CONFIG_CTL_2 0x0191 +#define WCD934X_SB_FLL_CONFIG_CTL_3 0x0192 +#define WCD934X_SB_FLL_CONFIG_CTL_4 0x0193 +#define WCD934X_SB_FLL_TEST_CTL_0 0x0194 +#define WCD934X_SB_FLL_TEST_CTL_1 0x0195 +#define WCD934X_SB_FLL_TEST_CTL_2 0x0196 +#define WCD934X_SB_FLL_TEST_CTL_3 0x0197 +#define WCD934X_SB_FLL_TEST_CTL_4 0x0198 +#define WCD934X_SB_FLL_TEST_CTL_5 0x0199 +#define WCD934X_SB_FLL_TEST_CTL_6 0x019a +#define WCD934X_SB_FLL_TEST_CTL_7 0x019b +#define WCD934X_SB_FLL_FREQ_CTL_0 0x019c +#define WCD934X_SB_FLL_FREQ_CTL_1 0x019d +#define WCD934X_SB_FLL_FREQ_CTL_2 0x019e +#define WCD934X_SB_FLL_FREQ_CTL_3 0x019f +#define WCD934X_SB_FLL_SSC_CTL_0 0x01a0 +#define WCD934X_SB_FLL_SSC_CTL_1 0x01a1 +#define WCD934X_SB_FLL_SSC_CTL_2 0x01a2 +#define WCD934X_SB_FLL_SSC_CTL_3 0x01a3 +#define WCD934X_SB_FLL_FLL_MODE 0x01a4 +#define WCD934X_SB_FLL_STATUS_0 0x01a5 +#define WCD934X_SB_FLL_STATUS_1 0x01a6 +#define WCD934X_SB_FLL_STATUS_2 0x01a7 +#define WCD934X_SB_FLL_STATUS_3 0x01a8 +#define WCD934X_PAGE2_PAGE_REGISTER 0x0200 +#define WCD934X_CPE_SS_CPE_CTL 0x0201 +#define WCD934X_CPE_SS_PWR_SYS_PSTATE_CTL_0 0x0202 +#define WCD934X_CPE_SS_PWR_SYS_PSTATE_CTL_1 0x0203 +#define WCD934X_CPE_SS_PWR_CPEFLL_CTL 0x0204 +#define WCD934X_CPE_SS_PWR_CPE_SYSMEM_DEEPSLP_0 0x0205 +#define WCD934X_CPE_SS_PWR_CPE_SYSMEM_DEEPSLP_1 0x0206 +#define WCD934X_CPE_SS_PWR_CPE_SYSMEM_DEEPSLP_OVERRIDE 0x0207 +#define WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_0 0x0208 +#define WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_1 0x0209 +#define WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_2 0x020a +#define WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_3 0x020b +#define WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_4 0x020c +#define WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_5 0x020d +#define WCD934X_CPE_SS_PWR_CPE_DRAM1_SHUTDOWN 0x020e +#define WCD934X_CPE_SS_SOC_SW_COLLAPSE_CTL 0x020f +#define WCD934X_CPE_SS_SOC_SW_COLLAPSE_OVERRIDE_CTL 0x0210 +#define WCD934X_CPE_SS_SOC_SW_COLLAPSE_OVERRIDE_CTL1 0x0211 +#define WCD934X_CPE_SS_US_BUF_INT_PERIOD 0x0212 +#define WCD934X_CPE_SS_CPARMAD_BUFRDY_INT_PERIOD 0x0213 +#define WCD934X_CPE_SS_SVA_CFG 0x0214 +#define WCD934X_CPE_SS_US_CFG 0x0215 +#define WCD934X_CPE_SS_MAD_CTL 0x0216 +#define WCD934X_CPE_SS_CPAR_CTL 0x0217 +#define WCD934X_CPE_SS_DMIC0_CTL 0x0218 +#define WCD934X_CPE_SS_DMIC1_CTL 0x0219 +#define WCD934X_CPE_SS_DMIC2_CTL 0x021a +#define WCD934X_CPE_SS_DMIC_CFG 0x021b +#define WCD934X_CPE_SS_CPAR_CFG 0x021c +#define WCD934X_CPE_SS_WDOG_CFG 0x021d +#define WCD934X_CPE_SS_BACKUP_INT 0x021e +#define WCD934X_CPE_SS_STATUS 0x021f +#define WCD934X_CPE_SS_CPE_OCD_CFG 0x0220 +#define WCD934X_CPE_SS_SS_ERROR_INT_MASK_0A 0x0221 +#define WCD934X_CPE_SS_SS_ERROR_INT_MASK_0B 0x0222 +#define WCD934X_CPE_SS_SS_ERROR_INT_MASK_1A 0x0223 +#define WCD934X_CPE_SS_SS_ERROR_INT_MASK_1B 0x0224 +#define WCD934X_CPE_SS_SS_ERROR_INT_STATUS_0A 0x0225 +#define WCD934X_CPE_SS_SS_ERROR_INT_STATUS_0B 0x0226 +#define WCD934X_CPE_SS_SS_ERROR_INT_STATUS_1A 0x0227 +#define WCD934X_CPE_SS_SS_ERROR_INT_STATUS_1B 0x0228 +#define WCD934X_CPE_SS_SS_ERROR_INT_CLEAR_0A 0x0229 +#define WCD934X_CPE_SS_SS_ERROR_INT_CLEAR_0B 0x022a +#define WCD934X_CPE_SS_SS_ERROR_INT_CLEAR_1A 0x022b +#define WCD934X_CPE_SS_SS_ERROR_INT_CLEAR_1B 0x022c +#define WCD934X_SOC_MAD_MAIN_CTL_1 0x0281 +#define WCD934X_SOC_MAD_MAIN_CTL_2 0x0282 +#define WCD934X_SOC_MAD_AUDIO_CTL_1 0x0283 +#define WCD934X_SOC_MAD_AUDIO_CTL_2 0x0284 +#define WCD934X_SOC_MAD_AUDIO_CTL_3 0x0285 +#define WCD934X_SOC_MAD_AUDIO_CTL_4 0x0286 +#define WCD934X_SOC_MAD_AUDIO_CTL_5 0x0287 +#define WCD934X_SOC_MAD_AUDIO_CTL_6 0x0288 +#define WCD934X_SOC_MAD_AUDIO_CTL_7 0x0289 +#define WCD934X_SOC_MAD_AUDIO_CTL_8 0x028a +#define WCD934X_SOC_MAD_AUDIO_IIR_CTL_PTR 0x028b +#define WCD934X_SOC_MAD_AUDIO_IIR_CTL_VAL 0x028c +#define WCD934X_SOC_MAD_ULTR_CTL_1 0x028d +#define WCD934X_SOC_MAD_ULTR_CTL_2 0x028e +#define WCD934X_SOC_MAD_ULTR_CTL_3 0x028f +#define WCD934X_SOC_MAD_ULTR_CTL_4 0x0290 +#define WCD934X_SOC_MAD_ULTR_CTL_5 0x0291 +#define WCD934X_SOC_MAD_ULTR_CTL_6 0x0292 +#define WCD934X_SOC_MAD_ULTR_CTL_7 0x0293 +#define WCD934X_SOC_MAD_BEACON_CTL_1 0x0294 +#define WCD934X_SOC_MAD_BEACON_CTL_2 0x0295 +#define WCD934X_SOC_MAD_BEACON_CTL_3 0x0296 +#define WCD934X_SOC_MAD_BEACON_CTL_4 0x0297 +#define WCD934X_SOC_MAD_BEACON_CTL_5 0x0298 +#define WCD934X_SOC_MAD_BEACON_CTL_6 0x0299 +#define WCD934X_SOC_MAD_BEACON_CTL_7 0x029a +#define WCD934X_SOC_MAD_BEACON_CTL_8 0x029b +#define WCD934X_SOC_MAD_BEACON_IIR_CTL_PTR 0x029c +#define WCD934X_SOC_MAD_BEACON_IIR_CTL_VAL 0x029d +#define WCD934X_SOC_MAD_INP_SEL 0x029e +#define WCD934X_PAGE4_PAGE_REGISTER 0x0400 +#define WCD934X_INTR_CFG 0x0401 +#define WCD934X_INTR_CLR_COMMIT 0x0402 +#define WCD934X_INTR_PIN1_MASK0 0x0409 +#define WCD934X_INTR_PIN1_MASK1 0x040a +#define WCD934X_INTR_PIN1_MASK2 0x040b +#define WCD934X_INTR_PIN1_MASK3 0x040c +#define WCD934X_INTR_PIN1_STATUS0 0x0411 +#define WCD934X_INTR_PIN1_STATUS1 0x0412 +#define WCD934X_INTR_PIN1_STATUS2 0x0413 +#define WCD934X_INTR_PIN1_STATUS3 0x0414 +#define WCD934X_INTR_PIN1_CLEAR0 0x0419 +#define WCD934X_INTR_PIN1_CLEAR1 0x041a +#define WCD934X_INTR_PIN1_CLEAR2 0x041b +#define WCD934X_INTR_PIN1_CLEAR3 0x041c +#define WCD934X_INTR_PIN2_MASK3 0x0424 +#define WCD934X_INTR_PIN2_STATUS3 0x042c +#define WCD934X_INTR_PIN2_CLEAR3 0x0434 +#define WCD934X_INTR_CPESS_SUMRY_MASK2 0x043b +#define WCD934X_INTR_CPESS_SUMRY_MASK3 0x043c +#define WCD934X_INTR_CPESS_SUMRY_STATUS2 0x0443 +#define WCD934X_INTR_CPESS_SUMRY_STATUS3 0x0444 +#define WCD934X_INTR_CPESS_SUMRY_CLEAR2 0x044b +#define WCD934X_INTR_CPESS_SUMRY_CLEAR3 0x044c +#define WCD934X_INTR_LEVEL0 0x0461 +#define WCD934X_INTR_LEVEL1 0x0462 +#define WCD934X_INTR_LEVEL2 0x0463 +#define WCD934X_INTR_LEVEL3 0x0464 +#define WCD934X_INTR_BYPASS0 0x0469 +#define WCD934X_INTR_BYPASS1 0x046a +#define WCD934X_INTR_BYPASS2 0x046b +#define WCD934X_INTR_BYPASS3 0x046c +#define WCD934X_INTR_SET0 0x0471 +#define WCD934X_INTR_SET1 0x0472 +#define WCD934X_INTR_SET2 0x0473 +#define WCD934X_INTR_SET3 0x0474 +#define WCD934X_INTR_CODEC_MISC_MASK 0x04b1 +#define WCD934X_INTR_CODEC_MISC_STATUS 0x04b2 +#define WCD934X_INTR_CODEC_MISC_CLEAR 0x04b3 +#define WCD934X_PAGE5_PAGE_REGISTER 0x0500 +#define WCD934X_SLNQ_DIG_DEVICE 0x0501 +#define WCD934X_SLNQ_DIG_REVISION 0x0502 +#define WCD934X_SLNQ_DIG_H_COMMAND 0x0511 +#define WCD934X_SLNQ_DIG_NUMBER_OF_BYTE_MSB 0x0512 +#define WCD934X_SLNQ_DIG_NUMBER_OF_BYTE_LSB 0x0513 +#define WCD934X_SLNQ_DIG_MASTER_ADDRESS_MSB 0x0514 +#define WCD934X_SLNQ_DIG_MASTER_ADDRESS_LSB 0x0515 +#define WCD934X_SLNQ_DIG_SLAVE_ADDRESS_MSB 0x0516 +#define WCD934X_SLNQ_DIG_SLAVE_ADDRESS_LSB 0x0517 +#define WCD934X_SLNQ_DIG_TIMER0_INTERRUPT_MSB 0x0518 +#define WCD934X_SLNQ_DIG_TIMER0_INTERRUPT_LSB 0x0519 +#define WCD934X_SLNQ_DIG_TIMER1_INTERRUPT_MSB 0x051a +#define WCD934X_SLNQ_DIG_TIMER1_INTERRUPT_LSB 0x051b +#define WCD934X_SLNQ_DIG_TIMER2_INTERRUPT_MSB 0x051c +#define WCD934X_SLNQ_DIG_TIMER2_INTERRUPT_LSB 0x051d +#define WCD934X_SLNQ_DIG_COMM_CTL 0x0520 +#define WCD934X_SLNQ_DIG_FRAME_CTRL 0x0542 +#define WCD934X_SLNQ_DIG_PDM_2ND_DATA_CH1_2 0x055c +#define WCD934X_SLNQ_DIG_PDM_2ND_DATA_CH3_4 0x055d +#define WCD934X_SLNQ_DIG_PDM_2ND_DATA_CH5 0x055e +#define WCD934X_SLNQ_DIG_SW_EVENT_RD 0x0561 +#define WCD934X_SLNQ_DIG_SW_EVENT_CTRL 0x0562 +#define WCD934X_SLNQ_DIG_PDM_SELECT_1 0x0563 +#define WCD934X_SLNQ_DIG_PDM_SELECT_2 0x0564 +#define WCD934X_SLNQ_DIG_PDM_SELECT_3 0x0565 +#define WCD934X_SLNQ_DIG_PDM_SAMPLING_FREQ 0x0566 +#define WCD934X_SLNQ_DIG_PDM_DC_CONVERSION_CTL 0x0569 +#define WCD934X_SLNQ_DIG_PDM_DC_CONVERSION_SEL 0x056a +#define WCD934X_SLNQ_DIG_PDM_DC_CONV_CHA_MSB 0x056b +#define WCD934X_SLNQ_DIG_PDM_DC_CONV_CHA_LSB 0x056c +#define WCD934X_SLNQ_DIG_PDM_DC_CONV_CHB_MSB 0x056d +#define WCD934X_SLNQ_DIG_PDM_DC_CONV_CHB_LSB 0x056e +#define WCD934X_SLNQ_DIG_RAM_CNTRL 0x0571 +#define WCD934X_SLNQ_DIG_SRAM_BANK 0x0572 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_0 0x0573 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_1 0x0574 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_2 0x0575 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_3 0x0576 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_4 0x0577 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_5 0x0578 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_6 0x0579 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_7 0x057a +#define WCD934X_SLNQ_DIG_SRAM_BYTE_8 0x057b +#define WCD934X_SLNQ_DIG_SRAM_BYTE_9 0x057c +#define WCD934X_SLNQ_DIG_SRAM_BYTE_A 0x057d +#define WCD934X_SLNQ_DIG_SRAM_BYTE_B 0x057e +#define WCD934X_SLNQ_DIG_SRAM_BYTE_C 0x057f +#define WCD934X_SLNQ_DIG_SRAM_BYTE_D 0x0580 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_E 0x0581 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_F 0x0582 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_10 0x0583 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_11 0x0584 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_12 0x0585 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_13 0x0586 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_14 0x0587 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_15 0x0588 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_16 0x0589 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_17 0x058a +#define WCD934X_SLNQ_DIG_SRAM_BYTE_18 0x058b +#define WCD934X_SLNQ_DIG_SRAM_BYTE_19 0x058c +#define WCD934X_SLNQ_DIG_SRAM_BYTE_1A 0x058d +#define WCD934X_SLNQ_DIG_SRAM_BYTE_1B 0x058e +#define WCD934X_SLNQ_DIG_SRAM_BYTE_1C 0x058f +#define WCD934X_SLNQ_DIG_SRAM_BYTE_1D 0x0590 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_1E 0x0591 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_1F 0x0592 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_20 0x0593 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_21 0x0594 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_22 0x0595 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_23 0x0596 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_24 0x0597 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_25 0x0598 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_26 0x0599 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_27 0x059a +#define WCD934X_SLNQ_DIG_SRAM_BYTE_28 0x059b +#define WCD934X_SLNQ_DIG_SRAM_BYTE_29 0x059c +#define WCD934X_SLNQ_DIG_SRAM_BYTE_2A 0x059d +#define WCD934X_SLNQ_DIG_SRAM_BYTE_2B 0x059e +#define WCD934X_SLNQ_DIG_SRAM_BYTE_2C 0x059f +#define WCD934X_SLNQ_DIG_SRAM_BYTE_2D 0x05a0 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_2E 0x05a1 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_2F 0x05a2 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_30 0x05a3 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_31 0x05a4 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_32 0x05a5 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_33 0x05a6 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_34 0x05a7 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_35 0x05a8 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_36 0x05a9 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_37 0x05aa +#define WCD934X_SLNQ_DIG_SRAM_BYTE_38 0x05ab +#define WCD934X_SLNQ_DIG_SRAM_BYTE_39 0x05ac +#define WCD934X_SLNQ_DIG_SRAM_BYTE_3A 0x05ad +#define WCD934X_SLNQ_DIG_SRAM_BYTE_3B 0x05ae +#define WCD934X_SLNQ_DIG_SRAM_BYTE_3C 0x05af +#define WCD934X_SLNQ_DIG_SRAM_BYTE_3D 0x05b0 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_3E 0x05b1 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_3F 0x05b2 +#define WCD934X_SLNQ_DIG_TOP_CTRL1 0x05b3 +#define WCD934X_SLNQ_DIG_TOP_CTRL2 0x05b4 +#define WCD934X_SLNQ_DIG_PDM_CTRL 0x05b5 +#define WCD934X_SLNQ_DIG_PDM_MUTE_CTRL 0x05b6 +#define WCD934X_SLNQ_DIG_DEC_BYPASS_CTRL 0x05b7 +#define WCD934X_SLNQ_DIG_DEC_BYPASS_STATUS 0x05b8 +#define WCD934X_SLNQ_DIG_DEC_BYPASS_FS 0x05b9 +#define WCD934X_SLNQ_DIG_DEC_BYPASS_IN_SEL 0x05ba +#define WCD934X_SLNQ_DIG_GPOUT_ENABLE 0x05bb +#define WCD934X_SLNQ_DIG_GPOUT_VAL 0x05bc +#define WCD934X_SLNQ_DIG_ANA_INTERRUPT_MASK 0x05be +#define WCD934X_SLNQ_DIG_ANA_INTERRUPT_STATUS 0x05bf +#define WCD934X_SLNQ_DIG_ANA_INTERRUPT_CLR 0x05c0 +#define WCD934X_SLNQ_DIG_IP_TESTING 0x05c1 +#define WCD934X_SLNQ_DIG_INTERRUPT_CNTRL 0x05e3 +#define WCD934X_SLNQ_DIG_INTERRUPT_CNT 0x05e9 +#define WCD934X_SLNQ_DIG_INTERRUPT_CNT_MSB 0x05eb +#define WCD934X_SLNQ_DIG_INTERRUPT_CNT_LSB 0x05ec +#define WCD934X_SLNQ_DIG_INTERRUPT_MASK0 0x05f1 +#define WCD934X_SLNQ_DIG_INTERRUPT_MASK1 0x05f2 +#define WCD934X_SLNQ_DIG_INTERRUPT_MASK2 0x05f3 +#define WCD934X_SLNQ_DIG_INTERRUPT_MASK3 0x05f4 +#define WCD934X_SLNQ_DIG_INTERRUPT_MASK4 0x05f5 +#define WCD934X_SLNQ_DIG_INTERRUPT_STATUS0 0x05f6 +#define WCD934X_SLNQ_DIG_INTERRUPT_STATUS1 0x05f7 +#define WCD934X_SLNQ_DIG_INTERRUPT_STATUS2 0x05f8 +#define WCD934X_SLNQ_DIG_INTERRUPT_STATUS3 0x05f9 +#define WCD934X_SLNQ_DIG_INTERRUPT_STATUS4 0x05fa +#define WCD934X_SLNQ_DIG_INTERRUPT_CLR0 0x05fb +#define WCD934X_SLNQ_DIG_INTERRUPT_CLR1 0x05fc +#define WCD934X_SLNQ_DIG_INTERRUPT_CLR2 0x05fd +#define WCD934X_SLNQ_DIG_INTERRUPT_CLR3 0x05fe +#define WCD934X_SLNQ_DIG_INTERRUPT_CLR4 0x05ff +#define WCD934X_ANA_PAGE_REGISTER 0x0600 +#define WCD934X_ANA_BIAS 0x0601 +#define WCD934X_ANA_RCO 0x0603 +#define WCD934X_ANA_PAGE6_SPARE2 0x0604 +#define WCD934X_ANA_PAGE6_SPARE3 0x0605 +#define WCD934X_ANA_BUCK_CTL 0x0606 +#define WCD934X_ANA_BUCK_STATUS 0x0607 +#define WCD934X_ANA_RX_SUPPLIES 0x0608 +#define WCD934X_ANA_HPH 0x0609 +#define WCD934X_ANA_EAR 0x060a +#define WCD934X_ANA_LO_1_2 0x060b +#define WCD934X_ANA_MAD_SETUP 0x060d +#define WCD934X_ANA_AMIC1 0x060e +#define WCD934X_ANA_AMIC2 0x060f +#define WCD934X_ANA_AMIC3 0x0610 +#define WCD934X_ANA_AMIC4 0x0611 +#define WCD934X_ANA_MBHC_MECH 0x0614 +#define WCD934X_ANA_MBHC_ELECT 0x0615 +#define WCD934X_ANA_MBHC_ZDET 0x0616 +#define WCD934X_ANA_MBHC_RESULT_1 0x0617 +#define WCD934X_ANA_MBHC_RESULT_2 0x0618 +#define WCD934X_ANA_MBHC_RESULT_3 0x0619 +#define WCD934X_ANA_MBHC_BTN0 0x061a +#define WCD934X_ANA_MBHC_BTN1 0x061b +#define WCD934X_ANA_MBHC_BTN2 0x061c +#define WCD934X_ANA_MBHC_BTN3 0x061d +#define WCD934X_ANA_MBHC_BTN4 0x061e +#define WCD934X_ANA_MBHC_BTN5 0x061f +#define WCD934X_ANA_MBHC_BTN6 0x0620 +#define WCD934X_ANA_MBHC_BTN7 0x0621 +#define WCD934X_ANA_MICB1 0x0622 +#define WCD934X_ANA_MICB2 0x0623 +#define WCD934X_ANA_MICB2_RAMP 0x0624 +#define WCD934X_ANA_MICB3 0x0625 +#define WCD934X_ANA_MICB4 0x0626 +#define WCD934X_ANA_VBADC 0x0627 +#define WCD934X_BIAS_CTL 0x0628 +#define WCD934X_BIAS_VBG_FINE_ADJ 0x0629 +#define WCD934X_RCO_CTRL_1 0x062e +#define WCD934X_RCO_CTRL_2 0x062f +#define WCD934X_RCO_CAL 0x0630 +#define WCD934X_RCO_CAL_1 0x0631 +#define WCD934X_RCO_CAL_2 0x0632 +#define WCD934X_RCO_TEST_CTRL 0x0633 +#define WCD934X_RCO_CAL_OUT_1 0x0634 +#define WCD934X_RCO_CAL_OUT_2 0x0635 +#define WCD934X_RCO_CAL_OUT_3 0x0636 +#define WCD934X_RCO_CAL_OUT_4 0x0637 +#define WCD934X_RCO_CAL_OUT_5 0x0638 +#define WCD934X_SIDO_MODE_1 0x063a +#define WCD934X_SIDO_MODE_2 0x063b +#define WCD934X_SIDO_MODE_3 0x063c +#define WCD934X_SIDO_MODE_4 0x063d +#define WCD934X_SIDO_VCL_1 0x063e +#define WCD934X_SIDO_VCL_2 0x063f +#define WCD934X_SIDO_VCL_3 0x0640 +#define WCD934X_SIDO_CCL_1 0x0641 +#define WCD934X_SIDO_CCL_2 0x0642 +#define WCD934X_SIDO_CCL_3 0x0643 +#define WCD934X_SIDO_CCL_4 0x0644 +#define WCD934X_SIDO_CCL_5 0x0645 +#define WCD934X_SIDO_CCL_6 0x0646 +#define WCD934X_SIDO_CCL_7 0x0647 +#define WCD934X_SIDO_CCL_8 0x0648 +#define WCD934X_SIDO_CCL_9 0x0649 +#define WCD934X_SIDO_CCL_10 0x064a +#define WCD934X_SIDO_FILTER_1 0x064b +#define WCD934X_SIDO_FILTER_2 0x064c +#define WCD934X_SIDO_DRIVER_1 0x064d +#define WCD934X_SIDO_DRIVER_2 0x064e +#define WCD934X_SIDO_DRIVER_3 0x064f +#define WCD934X_SIDO_CAL_CODE_EXT_1 0x0650 +#define WCD934X_SIDO_CAL_CODE_EXT_2 0x0651 +#define WCD934X_SIDO_CAL_CODE_OUT_1 0x0652 +#define WCD934X_SIDO_CAL_CODE_OUT_2 0x0653 +#define WCD934X_SIDO_TEST_1 0x0654 +#define WCD934X_SIDO_TEST_2 0x0655 +#define WCD934X_MBHC_CTL_CLK 0x0656 +#define WCD934X_MBHC_CTL_ANA 0x0657 +#define WCD934X_MBHC_CTL_SPARE_1 0x0658 +#define WCD934X_MBHC_CTL_SPARE_2 0x0659 +#define WCD934X_MBHC_CTL_BCS 0x065a +#define WCD934X_MBHC_STATUS_SPARE_1 0x065b +#define WCD934X_MBHC_TEST_CTL 0x065c +#define WCD934X_VBADC_SUBBLOCK_EN 0x065d +#define WCD934X_VBADC_IBIAS_FE 0x065e +#define WCD934X_VBADC_BIAS_ADC 0x065f +#define WCD934X_VBADC_FE_CTRL 0x0660 +#define WCD934X_VBADC_ADC_REF 0x0661 +#define WCD934X_VBADC_ADC_IO 0x0662 +#define WCD934X_VBADC_ADC_SAR 0x0663 +#define WCD934X_VBADC_DEBUG 0x0664 +#define WCD934X_LDOH_MODE 0x0667 +#define WCD934X_LDOH_BIAS 0x0668 +#define WCD934X_LDOH_STB_LOADS 0x0669 +#define WCD934X_LDOH_SLOWRAMP 0x066a +#define WCD934X_MICB1_TEST_CTL_1 0x066b +#define WCD934X_MICB1_TEST_CTL_2 0x066c +#define WCD934X_MICB1_TEST_CTL_3 0x066d +#define WCD934X_MICB2_TEST_CTL_1 0x066e +#define WCD934X_MICB2_TEST_CTL_2 0x066f +#define WCD934X_MICB2_TEST_CTL_3 0x0670 +#define WCD934X_MICB3_TEST_CTL_1 0x0671 +#define WCD934X_MICB3_TEST_CTL_2 0x0672 +#define WCD934X_MICB3_TEST_CTL_3 0x0673 +#define WCD934X_MICB4_TEST_CTL_1 0x0674 +#define WCD934X_MICB4_TEST_CTL_2 0x0675 +#define WCD934X_MICB4_TEST_CTL_3 0x0676 +#define WCD934X_TX_COM_ADC_VCM 0x0677 +#define WCD934X_TX_COM_BIAS_ATEST 0x0678 +#define WCD934X_TX_COM_ADC_INT1_IB 0x0679 +#define WCD934X_TX_COM_ADC_INT2_IB 0x067a +#define WCD934X_TX_COM_TXFE_DIV_CTL 0x067b +#define WCD934X_TX_COM_TXFE_DIV_START 0x067c +#define WCD934X_TX_COM_TXFE_DIV_STOP_9P6M 0x067d +#define WCD934X_TX_COM_TXFE_DIV_STOP_12P288M 0x067e +#define WCD934X_TX_1_2_TEST_EN 0x067f +#define WCD934X_TX_1_2_ADC_IB 0x0680 +#define WCD934X_TX_1_2_ATEST_REFCTL 0x0681 +#define WCD934X_TX_1_2_TEST_CTL 0x0682 +#define WCD934X_TX_1_2_TEST_BLK_EN 0x0683 +#define WCD934X_TX_1_2_TXFE_CLKDIV 0x0684 +#define WCD934X_TX_1_2_SAR1_ERR 0x0685 +#define WCD934X_TX_1_2_SAR2_ERR 0x0686 +#define WCD934X_TX_3_4_TEST_EN 0x0687 +#define WCD934X_TX_3_4_ADC_IB 0x0688 +#define WCD934X_TX_3_4_ATEST_REFCTL 0x0689 +#define WCD934X_TX_3_4_TEST_CTL 0x068a +#define WCD934X_TX_3_4_TEST_BLK_EN 0x068b +#define WCD934X_TX_3_4_TXFE_CLKDIV 0x068c +#define WCD934X_TX_3_4_SAR1_ERR 0x068d +#define WCD934X_TX_3_4_SAR2_ERR 0x068e +#define WCD934X_CLASSH_MODE_1 0x0697 +#define WCD934X_CLASSH_MODE_2 0x0698 +#define WCD934X_CLASSH_MODE_3 0x0699 +#define WCD934X_CLASSH_CTRL_VCL_1 0x069a +#define WCD934X_CLASSH_CTRL_VCL_2 0x069b +#define WCD934X_CLASSH_CTRL_CCL_1 0x069c +#define WCD934X_CLASSH_CTRL_CCL_2 0x069d +#define WCD934X_CLASSH_CTRL_CCL_3 0x069e +#define WCD934X_CLASSH_CTRL_CCL_4 0x069f +#define WCD934X_CLASSH_CTRL_CCL_5 0x06a0 +#define WCD934X_CLASSH_BUCK_TMUX_A_D 0x06a1 +#define WCD934X_CLASSH_BUCK_SW_DRV_CNTL 0x06a2 +#define WCD934X_CLASSH_SPARE 0x06a3 +#define WCD934X_FLYBACK_EN 0x06a4 +#define WCD934X_FLYBACK_VNEG_CTRL_1 0x06a5 +#define WCD934X_FLYBACK_VNEG_CTRL_2 0x06a6 +#define WCD934X_FLYBACK_VNEG_CTRL_3 0x06a7 +#define WCD934X_FLYBACK_VNEG_CTRL_4 0x06a8 +#define WCD934X_FLYBACK_VNEG_CTRL_5 0x06a9 +#define WCD934X_FLYBACK_VNEG_CTRL_6 0x06aa +#define WCD934X_FLYBACK_VNEG_CTRL_7 0x06ab +#define WCD934X_FLYBACK_VNEG_CTRL_8 0x06ac +#define WCD934X_FLYBACK_VNEG_CTRL_9 0x06ad +#define WCD934X_FLYBACK_VNEGDAC_CTRL_1 0x06ae +#define WCD934X_FLYBACK_VNEGDAC_CTRL_2 0x06af +#define WCD934X_FLYBACK_VNEGDAC_CTRL_3 0x06b0 +#define WCD934X_FLYBACK_CTRL_1 0x06b1 +#define WCD934X_FLYBACK_TEST_CTL 0x06b2 +#define WCD934X_RX_AUX_SW_CTL 0x06b3 +#define WCD934X_RX_PA_AUX_IN_CONN 0x06b4 +#define WCD934X_RX_TIMER_DIV 0x06b5 +#define WCD934X_RX_OCP_CTL 0x06b6 +#define WCD934X_RX_OCP_COUNT 0x06b7 +#define WCD934X_RX_BIAS_EAR_DAC 0x06b8 +#define WCD934X_RX_BIAS_EAR_AMP 0x06b9 +#define WCD934X_RX_BIAS_HPH_LDO 0x06ba +#define WCD934X_RX_BIAS_HPH_PA 0x06bb +#define WCD934X_RX_BIAS_HPH_RDACBUFF_CNP2 0x06bc +#define WCD934X_RX_BIAS_HPH_RDAC_LDO 0x06bd +#define WCD934X_RX_BIAS_HPH_CNP1 0x06be +#define WCD934X_RX_BIAS_HPH_LOWPOWER 0x06bf +#define WCD934X_RX_BIAS_DIFFLO_PA 0x06c0 +#define WCD934X_RX_BIAS_DIFFLO_REF 0x06c1 +#define WCD934X_RX_BIAS_DIFFLO_LDO 0x06c2 +#define WCD934X_RX_BIAS_SELO_DAC_PA 0x06c3 +#define WCD934X_RX_BIAS_BUCK_RST 0x06c4 +#define WCD934X_RX_BIAS_BUCK_VREF_ERRAMP 0x06c5 +#define WCD934X_RX_BIAS_FLYB_ERRAMP 0x06c6 +#define WCD934X_RX_BIAS_FLYB_BUFF 0x06c7 +#define WCD934X_RX_BIAS_FLYB_MID_RST 0x06c8 +#define WCD934X_HPH_L_STATUS 0x06c9 +#define WCD934X_HPH_R_STATUS 0x06ca +#define WCD934X_HPH_CNP_EN 0x06cb +#define WCD934X_HPH_CNP_WG_CTL 0x06cc +#define WCD934X_HPH_CNP_WG_TIME 0x06cd +#define WCD934X_HPH_OCP_CTL 0x06ce +#define WCD934X_HPH_AUTO_CHOP 0x06cf +#define WCD934X_HPH_CHOP_CTL 0x06d0 +#define WCD934X_HPH_PA_CTL1 0x06d1 +#define WCD934X_HPH_PA_CTL2 0x06d2 +#define WCD934X_HPH_L_EN 0x06d3 +#define WCD934X_HPH_L_TEST 0x06d4 +#define WCD934X_HPH_L_ATEST 0x06d5 +#define WCD934X_HPH_R_EN 0x06d6 +#define WCD934X_HPH_R_TEST 0x06d7 +#define WCD934X_HPH_R_ATEST 0x06d8 +#define WCD934X_HPH_RDAC_CLK_CTL1 0x06d9 +#define WCD934X_HPH_RDAC_CLK_CTL2 0x06da +#define WCD934X_HPH_RDAC_LDO_CTL 0x06db +#define WCD934X_HPH_RDAC_CHOP_CLK_LP_CTL 0x06dc +#define WCD934X_HPH_REFBUFF_UHQA_CTL 0x06dd +#define WCD934X_HPH_REFBUFF_LP_CTL 0x06de +#define WCD934X_HPH_L_DAC_CTL 0x06df +#define WCD934X_HPH_R_DAC_CTL 0x06e0 +#define WCD934X_EAR_EN_REG 0x06e1 +#define WCD934X_EAR_CMBUFF 0x06e2 +#define WCD934X_EAR_ICTL 0x06e3 +#define WCD934X_EAR_EN_DBG_CTL 0x06e4 +#define WCD934X_EAR_CNP 0x06e5 +#define WCD934X_EAR_DAC_CTL_ATEST 0x06e6 +#define WCD934X_EAR_STATUS_REG 0x06e7 +#define WCD934X_EAR_EAR_MISC 0x06e8 +#define WCD934X_DIFF_LO_MISC 0x06e9 +#define WCD934X_DIFF_LO_LO2_COMPANDER 0x06ea +#define WCD934X_DIFF_LO_LO1_COMPANDER 0x06eb +#define WCD934X_DIFF_LO_COMMON 0x06ec +#define WCD934X_DIFF_LO_BYPASS_EN 0x06ed +#define WCD934X_DIFF_LO_CNP 0x06ee +#define WCD934X_DIFF_LO_CORE_OUT_PROG 0x06ef +#define WCD934X_DIFF_LO_LDO_OUT_PROG 0x06f0 +#define WCD934X_DIFF_LO_COM_SWCAP_REFBUF_FREQ 0x06f1 +#define WCD934X_DIFF_LO_COM_PA_FREQ 0x06f2 +#define WCD934X_DIFF_LO_RESERVED_REG 0x06f3 +#define WCD934X_DIFF_LO_LO1_STATUS_1 0x06f4 +#define WCD934X_DIFF_LO_LO1_STATUS_2 0x06f5 +#define WCD934X_ANA_NEW_PAGE_REGISTER 0x0700 +#define WCD934X_HPH_NEW_ANA_HPH2 0x0701 +#define WCD934X_HPH_NEW_ANA_HPH3 0x0702 +#define WCD934X_SLNQ_ANA_EN 0x0703 +#define WCD934X_SLNQ_ANA_STATUS 0x0704 +#define WCD934X_SLNQ_ANA_LDO_CONFIG 0x0705 +#define WCD934X_SLNQ_ANA_LDO_OCP_CONFIG 0x0706 +#define WCD934X_SLNQ_ANA_TX_LDO_CONFIG 0x0707 +#define WCD934X_SLNQ_ANA_TX_DRV_CONFIG 0x0708 +#define WCD934X_SLNQ_ANA_RX_CONFIG_1 0x0709 +#define WCD934X_SLNQ_ANA_RX_CONFIG_2 0x070a +#define WCD934X_SLNQ_ANA_PLL_ENABLES 0x070b +#define WCD934X_SLNQ_ANA_PLL_PRESET 0x070c +#define WCD934X_SLNQ_ANA_PLL_STATUS 0x070d +#define WCD934X_CLK_SYS_PLL_ENABLES 0x070e +#define WCD934X_CLK_SYS_PLL_PRESET 0x070f +#define WCD934X_CLK_SYS_PLL_STATUS 0x0710 +#define WCD934X_CLK_SYS_MCLK_PRG 0x0711 +#define WCD934X_CLK_SYS_MCLK2_PRG1 0x0712 +#define WCD934X_CLK_SYS_MCLK2_PRG2 0x0713 +#define WCD934X_CLK_SYS_XO_PRG 0x0714 +#define WCD934X_CLK_SYS_XO_CAP_XTP 0x0715 +#define WCD934X_CLK_SYS_XO_CAP_XTM 0x0716 +#define WCD934X_BOOST_BST_EN_DLY 0x0718 +#define WCD934X_BOOST_CTRL_ILIM 0x0719 +#define WCD934X_BOOST_VOUT_SETTING 0x071a +#define WCD934X_SIDO_NEW_VOUT_A_STARTUP 0x071b +#define WCD934X_SIDO_NEW_VOUT_D_STARTUP 0x071c +#define WCD934X_SIDO_NEW_VOUT_D_FREQ1 0x071d +#define WCD934X_SIDO_NEW_VOUT_D_FREQ2 0x071e +#define WCD934X_MBHC_NEW_ELECT_REM_CLAMP_CTL 0x071f +#define WCD934X_MBHC_NEW_CTL_1 0x0720 +#define WCD934X_MBHC_NEW_CTL_2 0x0721 +#define WCD934X_MBHC_NEW_PLUG_DETECT_CTL 0x0722 +#define WCD934X_MBHC_NEW_ZDET_ANA_CTL 0x0723 +#define WCD934X_MBHC_NEW_ZDET_RAMP_CTL 0x0724 +#define WCD934X_MBHC_NEW_FSM_STATUS 0x0725 +#define WCD934X_MBHC_NEW_ADC_RESULT 0x0726 +#define WCD934X_TX_NEW_AMIC_4_5_SEL 0x0727 +#define WCD934X_VBADC_NEW_ADC_MODE 0x072f +#define WCD934X_VBADC_NEW_ADC_DOUTMSB 0x0730 +#define WCD934X_VBADC_NEW_ADC_DOUTLSB 0x0731 +#define WCD934X_HPH_NEW_INT_RDAC_GAIN_CTL 0x0732 +#define WCD934X_HPH_NEW_INT_RDAC_HD2_CTL 0x0733 +#define WCD934X_HPH_NEW_INT_RDAC_HD2_CTL_L 0x0733 +#define WCD934X_HPH_NEW_INT_RDAC_VREF_CTL 0x0734 +#define WCD934X_HPH_NEW_INT_RDAC_OVERRIDE_CTL 0x0735 +#define WCD934X_HPH_NEW_INT_RDAC_MISC1 0x0736 +#define WCD934X_HPH_NEW_INT_RDAC_HD2_CTL_R 0x0736 +#define WCD934X_HPH_NEW_INT_PA_MISC1 0x0737 +#define WCD934X_HPH_NEW_INT_PA_MISC2 0x0738 +#define WCD934X_HPH_NEW_INT_PA_RDAC_MISC 0x0739 +#define WCD934X_HPH_NEW_INT_HPH_TIMER1 0x073a +#define WCD934X_HPH_NEW_INT_HPH_TIMER2 0x073b +#define WCD934X_HPH_NEW_INT_HPH_TIMER3 0x073c +#define WCD934X_HPH_NEW_INT_HPH_TIMER4 0x073d +#define WCD934X_HPH_NEW_INT_PA_RDAC_MISC2 0x073e +#define WCD934X_HPH_NEW_INT_PA_RDAC_MISC3 0x073f +#define WCD934X_RX_NEW_INT_HPH_RDAC_BIAS_LOHIFI 0x0745 +#define WCD934X_RX_NEW_INT_HPH_RDAC_BIAS_ULP 0x0746 +#define WCD934X_RX_NEW_INT_HPH_RDAC_LDO_LP 0x0747 +#define WCD934X_SLNQ_INT_ANA_INT_LDO_TEST 0x074b +#define WCD934X_SLNQ_INT_ANA_INT_LDO_DEBUG_1 0x074c +#define WCD934X_SLNQ_INT_ANA_INT_LDO_DEBUG_2 0x074d +#define WCD934X_SLNQ_INT_ANA_INT_TX_LDO_TEST 0x074e +#define WCD934X_SLNQ_INT_ANA_INT_TX_DRV_TEST 0x074f +#define WCD934X_SLNQ_INT_ANA_INT_RX_TEST 0x0750 +#define WCD934X_SLNQ_INT_ANA_INT_RX_TEST_STATUS 0x0751 +#define WCD934X_SLNQ_INT_ANA_INT_RX_DEBUG_1 0x0752 +#define WCD934X_SLNQ_INT_ANA_INT_RX_DEBUG_2 0x0753 +#define WCD934X_SLNQ_INT_ANA_INT_CLK_CTRL 0x0754 +#define WCD934X_SLNQ_INT_ANA_INT_RESERVED_1 0x0755 +#define WCD934X_SLNQ_INT_ANA_INT_RESERVED_2 0x0756 +#define WCD934X_SLNQ_INT_ANA_INT_PLL_POST_DIV_REG0 0x0757 +#define WCD934X_SLNQ_INT_ANA_INT_PLL_POST_DIV_REG1 0x0758 +#define WCD934X_SLNQ_INT_ANA_INT_PLL_REF_DIV_REG0 0x0759 +#define WCD934X_SLNQ_INT_ANA_INT_PLL_REF_DIV_REG1 0x075a +#define WCD934X_SLNQ_INT_ANA_INT_PLL_FILTER_REG0 0x075b +#define WCD934X_SLNQ_INT_ANA_INT_PLL_FILTER_REG1 0x075c +#define WCD934X_SLNQ_INT_ANA_INT_PLL_L_VAL 0x075d +#define WCD934X_SLNQ_INT_ANA_INT_PLL_M_VAL 0x075e +#define WCD934X_SLNQ_INT_ANA_INT_PLL_N_VAL 0x075f +#define WCD934X_SLNQ_INT_ANA_INT_PLL_TEST_REG0 0x0760 +#define WCD934X_SLNQ_INT_ANA_INT_PLL_PFD_CP_DSM_PROG 0x0761 +#define WCD934X_SLNQ_INT_ANA_INT_PLL_VCO_PROG 0x0762 +#define WCD934X_SLNQ_INT_ANA_INT_PLL_TEST_REG1 0x0763 +#define WCD934X_SLNQ_INT_ANA_INT_PLL_LDO_LOCK_CFG 0x0764 +#define WCD934X_SLNQ_INT_ANA_INT_PLL_DIG_LOCK_DET_CFG 0x0765 +#define WCD934X_CLK_SYS_INT_POST_DIV_REG0 0x076c +#define WCD934X_CLK_SYS_INT_POST_DIV_REG1 0x076d +#define WCD934X_CLK_SYS_INT_REF_DIV_REG0 0x076e +#define WCD934X_CLK_SYS_INT_REF_DIV_REG1 0x076f +#define WCD934X_CLK_SYS_INT_FILTER_REG0 0x0770 +#define WCD934X_CLK_SYS_INT_FILTER_REG1 0x0771 +#define WCD934X_CLK_SYS_INT_PLL_L_VAL 0x0772 +#define WCD934X_CLK_SYS_INT_PLL_M_VAL 0x0773 +#define WCD934X_CLK_SYS_INT_PLL_N_VAL 0x0774 +#define WCD934X_CLK_SYS_INT_TEST_REG0 0x0775 +#define WCD934X_CLK_SYS_INT_PFD_CP_DSM_PROG 0x0776 +#define WCD934X_CLK_SYS_INT_VCO_PROG 0x0777 +#define WCD934X_CLK_SYS_INT_TEST_REG1 0x0778 +#define WCD934X_CLK_SYS_INT_LDO_LOCK_CFG 0x0779 +#define WCD934X_CLK_SYS_INT_DIG_LOCK_DET_CFG 0x077a +#define WCD934X_CLK_SYS_INT_CLK_TEST1 0x077b +#define WCD934X_CLK_SYS_INT_CLK_TEST2 0x077c +#define WCD934X_CLK_SYS_INT_CLK_TEST3 0x077d +#define WCD934X_CLK_SYS_INT_XO_TEST1 0x077e +#define WCD934X_CLK_SYS_INT_XO_TEST2 0x077f +#define WCD934X_BOOST_INT_VCOMP_HYST 0x0787 +#define WCD934X_BOOST_INT_VLOOP_FILTER 0x0788 +#define WCD934X_BOOST_INT_CTRL_IDELTA 0x0789 +#define WCD934X_BOOST_INT_CTRL_ILIM_STARTUP 0x078a +#define WCD934X_BOOST_INT_CTRL_MIN_ONTIME 0x078b +#define WCD934X_BOOST_INT_CTRL_MAX_ONTIME 0x078c +#define WCD934X_BOOST_INT_CTRL_TIMING 0x078d +#define WCD934X_BOOST_INT_TMUX_A_D 0x078e +#define WCD934X_BOOST_INT_SW_DRV_CNTL 0x078f +#define WCD934X_BOOST_INT_SPARE1 0x0790 +#define WCD934X_BOOST_INT_SPARE2 0x0791 +#define WCD934X_SIDO_NEW_INT_RAMP_STATUS 0x0796 +#define WCD934X_SIDO_NEW_INT_SPARE_1 0x0797 +#define WCD934X_SIDO_NEW_INT_DEBUG_VOUT_SETTING_A 0x0798 +#define WCD934X_SIDO_NEW_INT_DEBUG_VOUT_SETTING_D 0x0799 +#define WCD934X_SIDO_NEW_INT_RAMP_INC_WAIT 0x079a +#define WCD934X_SIDO_NEW_INT_DYNAMIC_IPEAK_CTL 0x079b +#define WCD934X_SIDO_NEW_INT_RAMP_IBLEED_CTL 0x079c +#define WCD934X_SIDO_NEW_INT_DEBUG_CPROVR_TEST 0x079d +#define WCD934X_SIDO_NEW_INT_RAMP_CTL_A 0x079e +#define WCD934X_SIDO_NEW_INT_RAMP_CTL_D 0x079f +#define WCD934X_SIDO_NEW_INT_RAMP_TIMEOUT_PERIOD 0x07a0 +#define WCD934X_SIDO_NEW_INT_DYNAMIC_IPEAK_SETTING1 0x07a1 +#define WCD934X_SIDO_NEW_INT_DYNAMIC_IPEAK_SETTING2 0x07a2 +#define WCD934X_SIDO_NEW_INT_DYNAMIC_IPEAK_SETTING3 0x07a3 +#define WCD934X_SIDO_NEW_INT_HIGH_ACCU_MODE_SEL1 0x07a4 +#define WCD934X_SIDO_NEW_INT_HIGH_ACCU_MODE_SEL2 0x07a5 +#define WCD934X_MBHC_NEW_INT_SLNQ_HPF 0x07af +#define WCD934X_MBHC_NEW_INT_SLNQ_REF 0x07b0 +#define WCD934X_MBHC_NEW_INT_SLNQ_COMP 0x07b1 +#define WCD934X_MBHC_NEW_INT_SPARE_2 0x07b2 +#define WCD934X_PAGE10_PAGE_REGISTER 0x0a00 +#define WCD934X_CDC_ANC0_CLK_RESET_CTL 0x0a01 +#define WCD934X_CDC_ANC0_MODE_1_CTL 0x0a02 +#define WCD934X_CDC_ANC0_MODE_2_CTL 0x0a03 +#define WCD934X_CDC_ANC0_FF_SHIFT 0x0a04 +#define WCD934X_CDC_ANC0_FB_SHIFT 0x0a05 +#define WCD934X_CDC_ANC0_LPF_FF_A_CTL 0x0a06 +#define WCD934X_CDC_ANC0_LPF_FF_B_CTL 0x0a07 +#define WCD934X_CDC_ANC0_LPF_FB_CTL 0x0a08 +#define WCD934X_CDC_ANC0_SMLPF_CTL 0x0a09 +#define WCD934X_CDC_ANC0_DCFLT_SHIFT_CTL 0x0a0a +#define WCD934X_CDC_ANC0_IIR_ADAPT_CTL 0x0a0b +#define WCD934X_CDC_ANC0_IIR_COEFF_1_CTL 0x0a0c +#define WCD934X_CDC_ANC0_IIR_COEFF_2_CTL 0x0a0d +#define WCD934X_CDC_ANC0_FF_A_GAIN_CTL 0x0a0e +#define WCD934X_CDC_ANC0_FF_B_GAIN_CTL 0x0a0f +#define WCD934X_CDC_ANC0_FB_GAIN_CTL 0x0a10 +#define WCD934X_CDC_ANC0_RC_COMMON_CTL 0x0a11 +#define WCD934X_CDC_ANC0_FIFO_COMMON_CTL 0x0a13 +#define WCD934X_CDC_ANC0_RC0_STATUS_FMIN_CNTR 0x0a14 +#define WCD934X_CDC_ANC0_RC1_STATUS_FMIN_CNTR 0x0a15 +#define WCD934X_CDC_ANC0_RC0_STATUS_FMAX_CNTR 0x0a16 +#define WCD934X_CDC_ANC0_RC1_STATUS_FMAX_CNTR 0x0a17 +#define WCD934X_CDC_ANC0_STATUS_FIFO 0x0a18 +#define WCD934X_CDC_ANC1_CLK_RESET_CTL 0x0a19 +#define WCD934X_CDC_ANC1_MODE_1_CTL 0x0a1a +#define WCD934X_CDC_ANC1_MODE_2_CTL 0x0a1b +#define WCD934X_CDC_ANC1_FF_SHIFT 0x0a1c +#define WCD934X_CDC_ANC1_FB_SHIFT 0x0a1d +#define WCD934X_CDC_ANC1_LPF_FF_A_CTL 0x0a1e +#define WCD934X_CDC_ANC1_LPF_FF_B_CTL 0x0a1f +#define WCD934X_CDC_ANC1_LPF_FB_CTL 0x0a20 +#define WCD934X_CDC_ANC1_SMLPF_CTL 0x0a21 +#define WCD934X_CDC_ANC1_DCFLT_SHIFT_CTL 0x0a22 +#define WCD934X_CDC_ANC1_IIR_ADAPT_CTL 0x0a23 +#define WCD934X_CDC_ANC1_IIR_COEFF_1_CTL 0x0a24 +#define WCD934X_CDC_ANC1_IIR_COEFF_2_CTL 0x0a25 +#define WCD934X_CDC_ANC1_FF_A_GAIN_CTL 0x0a26 +#define WCD934X_CDC_ANC1_FF_B_GAIN_CTL 0x0a27 +#define WCD934X_CDC_ANC1_FB_GAIN_CTL 0x0a28 +#define WCD934X_CDC_ANC1_RC_COMMON_CTL 0x0a29 +#define WCD934X_CDC_ANC1_FIFO_COMMON_CTL 0x0a2b +#define WCD934X_CDC_ANC1_RC0_STATUS_FMIN_CNTR 0x0a2c +#define WCD934X_CDC_ANC1_RC1_STATUS_FMIN_CNTR 0x0a2d +#define WCD934X_CDC_ANC1_RC0_STATUS_FMAX_CNTR 0x0a2e +#define WCD934X_CDC_ANC1_RC1_STATUS_FMAX_CNTR 0x0a2f +#define WCD934X_CDC_ANC1_STATUS_FIFO 0x0a30 +#define WCD934X_CDC_TX0_TX_PATH_CTL 0x0a31 +#define WCD934X_CDC_TX0_TX_PATH_CFG0 0x0a32 +#define WCD934X_CDC_TX0_TX_PATH_CFG1 0x0a33 +#define WCD934X_CDC_TX0_TX_VOL_CTL 0x0a34 +#define WCD934X_CDC_TX0_TX_PATH_192_CTL 0x0a35 +#define WCD934X_CDC_TX0_TX_PATH_192_CFG 0x0a36 +#define WCD934X_CDC_TX0_TX_PATH_SEC0 0x0a37 +#define WCD934X_CDC_TX0_TX_PATH_SEC1 0x0a38 +#define WCD934X_CDC_TX0_TX_PATH_SEC2 0x0a39 +#define WCD934X_CDC_TX0_TX_PATH_SEC3 0x0a3a +#define WCD934X_CDC_TX0_TX_PATH_SEC4 0x0a3b +#define WCD934X_CDC_TX0_TX_PATH_SEC5 0x0a3c +#define WCD934X_CDC_TX0_TX_PATH_SEC6 0x0a3d +#define WCD934X_CDC_TX0_TX_PATH_SEC7 0x0a3e +#define WCD934X_CDC_TX1_TX_PATH_CTL 0x0a41 +#define WCD934X_CDC_TX1_TX_PATH_CFG0 0x0a42 +#define WCD934X_CDC_TX1_TX_PATH_CFG1 0x0a43 +#define WCD934X_CDC_TX1_TX_VOL_CTL 0x0a44 +#define WCD934X_CDC_TX1_TX_PATH_192_CTL 0x0a45 +#define WCD934X_CDC_TX1_TX_PATH_192_CFG 0x0a46 +#define WCD934X_CDC_TX1_TX_PATH_SEC0 0x0a47 +#define WCD934X_CDC_TX1_TX_PATH_SEC1 0x0a48 +#define WCD934X_CDC_TX1_TX_PATH_SEC2 0x0a49 +#define WCD934X_CDC_TX1_TX_PATH_SEC3 0x0a4a +#define WCD934X_CDC_TX1_TX_PATH_SEC4 0x0a4b +#define WCD934X_CDC_TX1_TX_PATH_SEC5 0x0a4c +#define WCD934X_CDC_TX1_TX_PATH_SEC6 0x0a4d +#define WCD934X_CDC_TX2_TX_PATH_CTL 0x0a51 +#define WCD934X_CDC_TX2_TX_PATH_CFG0 0x0a52 +#define WCD934X_CDC_TX2_TX_PATH_CFG1 0x0a53 +#define WCD934X_CDC_TX2_TX_VOL_CTL 0x0a54 +#define WCD934X_CDC_TX2_TX_PATH_192_CTL 0x0a55 +#define WCD934X_CDC_TX2_TX_PATH_192_CFG 0x0a56 +#define WCD934X_CDC_TX2_TX_PATH_SEC0 0x0a57 +#define WCD934X_CDC_TX2_TX_PATH_SEC1 0x0a58 +#define WCD934X_CDC_TX2_TX_PATH_SEC2 0x0a59 +#define WCD934X_CDC_TX2_TX_PATH_SEC3 0x0a5a +#define WCD934X_CDC_TX2_TX_PATH_SEC4 0x0a5b +#define WCD934X_CDC_TX2_TX_PATH_SEC5 0x0a5c +#define WCD934X_CDC_TX2_TX_PATH_SEC6 0x0a5d +#define WCD934X_CDC_TX3_TX_PATH_CTL 0x0a61 +#define WCD934X_CDC_TX3_TX_PATH_CFG0 0x0a62 +#define WCD934X_CDC_TX3_TX_PATH_CFG1 0x0a63 +#define WCD934X_CDC_TX3_TX_VOL_CTL 0x0a64 +#define WCD934X_CDC_TX3_TX_PATH_192_CTL 0x0a65 +#define WCD934X_CDC_TX3_TX_PATH_192_CFG 0x0a66 +#define WCD934X_CDC_TX3_TX_PATH_SEC0 0x0a67 +#define WCD934X_CDC_TX3_TX_PATH_SEC1 0x0a68 +#define WCD934X_CDC_TX3_TX_PATH_SEC2 0x0a69 +#define WCD934X_CDC_TX3_TX_PATH_SEC3 0x0a6a +#define WCD934X_CDC_TX3_TX_PATH_SEC4 0x0a6b +#define WCD934X_CDC_TX3_TX_PATH_SEC5 0x0a6c +#define WCD934X_CDC_TX3_TX_PATH_SEC6 0x0a6d +#define WCD934X_CDC_TX4_TX_PATH_CTL 0x0a71 +#define WCD934X_CDC_TX4_TX_PATH_CFG0 0x0a72 +#define WCD934X_CDC_TX4_TX_PATH_CFG1 0x0a73 +#define WCD934X_CDC_TX4_TX_VOL_CTL 0x0a74 +#define WCD934X_CDC_TX4_TX_PATH_192_CTL 0x0a75 +#define WCD934X_CDC_TX4_TX_PATH_192_CFG 0x0a76 +#define WCD934X_CDC_TX4_TX_PATH_SEC0 0x0a77 +#define WCD934X_CDC_TX4_TX_PATH_SEC1 0x0a78 +#define WCD934X_CDC_TX4_TX_PATH_SEC2 0x0a79 +#define WCD934X_CDC_TX4_TX_PATH_SEC3 0x0a7a +#define WCD934X_CDC_TX4_TX_PATH_SEC4 0x0a7b +#define WCD934X_CDC_TX4_TX_PATH_SEC5 0x0a7c +#define WCD934X_CDC_TX4_TX_PATH_SEC6 0x0a7d +#define WCD934X_CDC_TX5_TX_PATH_CTL 0x0a81 +#define WCD934X_CDC_TX5_TX_PATH_CFG0 0x0a82 +#define WCD934X_CDC_TX5_TX_PATH_CFG1 0x0a83 +#define WCD934X_CDC_TX5_TX_VOL_CTL 0x0a84 +#define WCD934X_CDC_TX5_TX_PATH_192_CTL 0x0a85 +#define WCD934X_CDC_TX5_TX_PATH_192_CFG 0x0a86 +#define WCD934X_CDC_TX5_TX_PATH_SEC0 0x0a87 +#define WCD934X_CDC_TX5_TX_PATH_SEC1 0x0a88 +#define WCD934X_CDC_TX5_TX_PATH_SEC2 0x0a89 +#define WCD934X_CDC_TX5_TX_PATH_SEC3 0x0a8a +#define WCD934X_CDC_TX5_TX_PATH_SEC4 0x0a8b +#define WCD934X_CDC_TX5_TX_PATH_SEC5 0x0a8c +#define WCD934X_CDC_TX5_TX_PATH_SEC6 0x0a8d +#define WCD934X_CDC_TX6_TX_PATH_CTL 0x0a91 +#define WCD934X_CDC_TX6_TX_PATH_CFG0 0x0a92 +#define WCD934X_CDC_TX6_TX_PATH_CFG1 0x0a93 +#define WCD934X_CDC_TX6_TX_VOL_CTL 0x0a94 +#define WCD934X_CDC_TX6_TX_PATH_192_CTL 0x0a95 +#define WCD934X_CDC_TX6_TX_PATH_192_CFG 0x0a96 +#define WCD934X_CDC_TX6_TX_PATH_SEC0 0x0a97 +#define WCD934X_CDC_TX6_TX_PATH_SEC1 0x0a98 +#define WCD934X_CDC_TX6_TX_PATH_SEC2 0x0a99 +#define WCD934X_CDC_TX6_TX_PATH_SEC3 0x0a9a +#define WCD934X_CDC_TX6_TX_PATH_SEC4 0x0a9b +#define WCD934X_CDC_TX6_TX_PATH_SEC5 0x0a9c +#define WCD934X_CDC_TX6_TX_PATH_SEC6 0x0a9d +#define WCD934X_CDC_TX7_TX_PATH_CTL 0x0aa1 +#define WCD934X_CDC_TX7_TX_PATH_CFG0 0x0aa2 +#define WCD934X_CDC_TX7_TX_PATH_CFG1 0x0aa3 +#define WCD934X_CDC_TX7_TX_VOL_CTL 0x0aa4 +#define WCD934X_CDC_TX7_TX_PATH_192_CTL 0x0aa5 +#define WCD934X_CDC_TX7_TX_PATH_192_CFG 0x0aa6 +#define WCD934X_CDC_TX7_TX_PATH_SEC0 0x0aa7 +#define WCD934X_CDC_TX7_TX_PATH_SEC1 0x0aa8 +#define WCD934X_CDC_TX7_TX_PATH_SEC2 0x0aa9 +#define WCD934X_CDC_TX7_TX_PATH_SEC3 0x0aaa +#define WCD934X_CDC_TX7_TX_PATH_SEC4 0x0aab +#define WCD934X_CDC_TX7_TX_PATH_SEC5 0x0aac +#define WCD934X_CDC_TX7_TX_PATH_SEC6 0x0aad +#define WCD934X_CDC_TX8_TX_PATH_CTL 0x0ab1 +#define WCD934X_CDC_TX8_TX_PATH_CFG0 0x0ab2 +#define WCD934X_CDC_TX8_TX_PATH_CFG1 0x0ab3 +#define WCD934X_CDC_TX8_TX_VOL_CTL 0x0ab4 +#define WCD934X_CDC_TX8_TX_PATH_192_CTL 0x0ab5 +#define WCD934X_CDC_TX8_TX_PATH_192_CFG 0x0ab6 +#define WCD934X_CDC_TX8_TX_PATH_SEC0 0x0ab7 +#define WCD934X_CDC_TX8_TX_PATH_SEC1 0x0ab8 +#define WCD934X_CDC_TX8_TX_PATH_SEC2 0x0ab9 +#define WCD934X_CDC_TX8_TX_PATH_SEC3 0x0aba +#define WCD934X_CDC_TX8_TX_PATH_SEC4 0x0abb +#define WCD934X_CDC_TX8_TX_PATH_SEC5 0x0abc +#define WCD934X_CDC_TX8_TX_PATH_SEC6 0x0abd +#define WCD934X_CDC_TX9_SPKR_PROT_PATH_CTL 0x0ac2 +#define WCD934X_CDC_TX9_SPKR_PROT_PATH_CFG0 0x0ac3 +#define WCD934X_CDC_TX10_SPKR_PROT_PATH_CTL 0x0ac6 +#define WCD934X_CDC_TX10_SPKR_PROT_PATH_CFG0 0x0ac7 +#define WCD934X_CDC_TX11_SPKR_PROT_PATH_CTL 0x0aca +#define WCD934X_CDC_TX11_SPKR_PROT_PATH_CFG0 0x0acb +#define WCD934X_CDC_TX12_SPKR_PROT_PATH_CTL 0x0ace +#define WCD934X_CDC_TX12_SPKR_PROT_PATH_CFG0 0x0acf +#define WCD934X_PAGE11_PAGE_REGISTER 0x0b00 +#define WCD934X_CDC_COMPANDER1_CTL0 0x0b01 +#define WCD934X_CDC_COMPANDER1_CTL1 0x0b02 +#define WCD934X_CDC_COMPANDER1_CTL2 0x0b03 +#define WCD934X_CDC_COMPANDER1_CTL3 0x0b04 +#define WCD934X_CDC_COMPANDER1_CTL4 0x0b05 +#define WCD934X_CDC_COMPANDER1_CTL5 0x0b06 +#define WCD934X_CDC_COMPANDER1_CTL6 0x0b07 +#define WCD934X_CDC_COMPANDER1_CTL7 0x0b08 +#define WCD934X_CDC_COMPANDER2_CTL0 0x0b09 +#define WCD934X_CDC_COMPANDER2_CTL1 0x0b0a +#define WCD934X_CDC_COMPANDER2_CTL2 0x0b0b +#define WCD934X_CDC_COMPANDER2_CTL3 0x0b0c +#define WCD934X_CDC_COMPANDER2_CTL4 0x0b0d +#define WCD934X_CDC_COMPANDER2_CTL5 0x0b0e +#define WCD934X_CDC_COMPANDER2_CTL6 0x0b0f +#define WCD934X_CDC_COMPANDER2_CTL7 0x0b10 +#define WCD934X_CDC_COMPANDER3_CTL0 0x0b11 +#define WCD934X_CDC_COMPANDER3_CTL1 0x0b12 +#define WCD934X_CDC_COMPANDER3_CTL2 0x0b13 +#define WCD934X_CDC_COMPANDER3_CTL3 0x0b14 +#define WCD934X_CDC_COMPANDER3_CTL4 0x0b15 +#define WCD934X_CDC_COMPANDER3_CTL5 0x0b16 +#define WCD934X_CDC_COMPANDER3_CTL6 0x0b17 +#define WCD934X_CDC_COMPANDER3_CTL7 0x0b18 +#define WCD934X_CDC_COMPANDER4_CTL0 0x0b19 +#define WCD934X_CDC_COMPANDER4_CTL1 0x0b1a +#define WCD934X_CDC_COMPANDER4_CTL2 0x0b1b +#define WCD934X_CDC_COMPANDER4_CTL3 0x0b1c +#define WCD934X_CDC_COMPANDER4_CTL4 0x0b1d +#define WCD934X_CDC_COMPANDER4_CTL5 0x0b1e +#define WCD934X_CDC_COMPANDER4_CTL6 0x0b1f +#define WCD934X_CDC_COMPANDER4_CTL7 0x0b20 +#define WCD934X_CDC_COMPANDER7_CTL0 0x0b31 +#define WCD934X_CDC_COMPANDER7_CTL1 0x0b32 +#define WCD934X_CDC_COMPANDER7_CTL2 0x0b33 +#define WCD934X_CDC_COMPANDER7_CTL3 0x0b34 +#define WCD934X_CDC_COMPANDER7_CTL4 0x0b35 +#define WCD934X_CDC_COMPANDER7_CTL5 0x0b36 +#define WCD934X_CDC_COMPANDER7_CTL6 0x0b37 +#define WCD934X_CDC_COMPANDER7_CTL7 0x0b38 +#define WCD934X_CDC_COMPANDER8_CTL0 0x0b39 +#define WCD934X_CDC_COMPANDER8_CTL1 0x0b3a +#define WCD934X_CDC_COMPANDER8_CTL2 0x0b3b +#define WCD934X_CDC_COMPANDER8_CTL3 0x0b3c +#define WCD934X_CDC_COMPANDER8_CTL4 0x0b3d +#define WCD934X_CDC_COMPANDER8_CTL5 0x0b3e +#define WCD934X_CDC_COMPANDER8_CTL6 0x0b3f +#define WCD934X_CDC_COMPANDER8_CTL7 0x0b40 +#define WCD934X_CDC_RX0_RX_PATH_CTL 0x0b41 +#define WCD934X_CDC_RX0_RX_PATH_CFG0 0x0b42 +#define WCD934X_CDC_RX0_RX_PATH_CFG1 0x0b43 +#define WCD934X_CDC_RX0_RX_PATH_CFG2 0x0b44 +#define WCD934X_CDC_RX0_RX_VOL_CTL 0x0b45 +#define WCD934X_CDC_RX0_RX_PATH_MIX_CTL 0x0b46 +#define WCD934X_CDC_RX0_RX_PATH_MIX_CFG 0x0b47 +#define WCD934X_CDC_RX0_RX_VOL_MIX_CTL 0x0b48 +#define WCD934X_CDC_RX0_RX_PATH_SEC0 0x0b49 +#define WCD934X_CDC_RX0_RX_PATH_SEC1 0x0b4a +#define WCD934X_CDC_RX0_RX_PATH_SEC2 0x0b4b +#define WCD934X_CDC_RX0_RX_PATH_SEC3 0x0b4c +#define WCD934X_CDC_RX0_RX_PATH_SEC5 0x0b4e +#define WCD934X_CDC_RX0_RX_PATH_SEC6 0x0b4f +#define WCD934X_CDC_RX0_RX_PATH_SEC7 0x0b50 +#define WCD934X_CDC_RX0_RX_PATH_MIX_SEC0 0x0b51 +#define WCD934X_CDC_RX0_RX_PATH_MIX_SEC1 0x0b52 +#define WCD934X_CDC_RX0_RX_PATH_DSMDEM_CTL 0x0b53 +#define WCD934X_CDC_RX1_RX_PATH_CTL 0x0b55 +#define WCD934X_CDC_RX1_RX_PATH_CFG0 0x0b56 +#define WCD934X_CDC_RX1_RX_PATH_CFG1 0x0b57 +#define WCD934X_CDC_RX1_RX_PATH_CFG2 0x0b58 +#define WCD934X_CDC_RX1_RX_VOL_CTL 0x0b59 +#define WCD934X_CDC_RX1_RX_PATH_MIX_CTL 0x0b5a +#define WCD934X_CDC_RX1_RX_PATH_MIX_CFG 0x0b5b +#define WCD934X_CDC_RX1_RX_VOL_MIX_CTL 0x0b5c +#define WCD934X_CDC_RX1_RX_PATH_SEC0 0x0b5d +#define WCD934X_CDC_RX1_RX_PATH_SEC1 0x0b5e +#define WCD934X_CDC_RX1_RX_PATH_SEC2 0x0b5f +#define WCD934X_CDC_RX1_RX_PATH_SEC3 0x0b60 +#define WCD934X_CDC_RX1_RX_PATH_SEC4 0x0b61 +#define WCD934X_CDC_RX1_RX_PATH_SEC5 0x0b62 +#define WCD934X_CDC_RX1_RX_PATH_SEC6 0x0b63 +#define WCD934X_CDC_RX1_RX_PATH_SEC7 0x0b64 +#define WCD934X_CDC_RX1_RX_PATH_MIX_SEC0 0x0b65 +#define WCD934X_CDC_RX1_RX_PATH_MIX_SEC1 0x0b66 +#define WCD934X_CDC_RX1_RX_PATH_DSMDEM_CTL 0x0b67 +#define WCD934X_CDC_RX2_RX_PATH_CTL 0x0b69 +#define WCD934X_CDC_RX2_RX_PATH_CFG0 0x0b6a +#define WCD934X_CDC_RX2_RX_PATH_CFG1 0x0b6b +#define WCD934X_CDC_RX2_RX_PATH_CFG2 0x0b6c +#define WCD934X_CDC_RX2_RX_VOL_CTL 0x0b6d +#define WCD934X_CDC_RX2_RX_PATH_MIX_CTL 0x0b6e +#define WCD934X_CDC_RX2_RX_PATH_MIX_CFG 0x0b6f +#define WCD934X_CDC_RX2_RX_VOL_MIX_CTL 0x0b70 +#define WCD934X_CDC_RX2_RX_PATH_SEC0 0x0b71 +#define WCD934X_CDC_RX2_RX_PATH_SEC1 0x0b72 +#define WCD934X_CDC_RX2_RX_PATH_SEC2 0x0b73 +#define WCD934X_CDC_RX2_RX_PATH_SEC3 0x0b74 +#define WCD934X_CDC_RX2_RX_PATH_SEC4 0x0b75 +#define WCD934X_CDC_RX2_RX_PATH_SEC5 0x0b76 +#define WCD934X_CDC_RX2_RX_PATH_SEC6 0x0b77 +#define WCD934X_CDC_RX2_RX_PATH_SEC7 0x0b78 +#define WCD934X_CDC_RX2_RX_PATH_MIX_SEC0 0x0b79 +#define WCD934X_CDC_RX2_RX_PATH_MIX_SEC1 0x0b7a +#define WCD934X_CDC_RX2_RX_PATH_DSMDEM_CTL 0x0b7b +#define WCD934X_CDC_RX3_RX_PATH_CTL 0x0b7d +#define WCD934X_CDC_RX3_RX_PATH_CFG0 0x0b7e +#define WCD934X_CDC_RX3_RX_PATH_CFG1 0x0b7f +#define WCD934X_CDC_RX3_RX_PATH_CFG2 0x0b80 +#define WCD934X_CDC_RX3_RX_VOL_CTL 0x0b81 +#define WCD934X_CDC_RX3_RX_PATH_MIX_CTL 0x0b82 +#define WCD934X_CDC_RX3_RX_PATH_MIX_CFG 0x0b83 +#define WCD934X_CDC_RX3_RX_VOL_MIX_CTL 0x0b84 +#define WCD934X_CDC_RX3_RX_PATH_SEC0 0x0b85 +#define WCD934X_CDC_RX3_RX_PATH_SEC1 0x0b86 +#define WCD934X_CDC_RX3_RX_PATH_SEC2 0x0b87 +#define WCD934X_CDC_RX3_RX_PATH_SEC3 0x0b88 +#define WCD934X_CDC_RX3_RX_PATH_SEC5 0x0b8a +#define WCD934X_CDC_RX3_RX_PATH_SEC6 0x0b8b +#define WCD934X_CDC_RX3_RX_PATH_SEC7 0x0b8c +#define WCD934X_CDC_RX3_RX_PATH_MIX_SEC0 0x0b8d +#define WCD934X_CDC_RX3_RX_PATH_MIX_SEC1 0x0b8e +#define WCD934X_CDC_RX3_RX_PATH_DSMDEM_CTL 0x0b8f +#define WCD934X_CDC_RX4_RX_PATH_CTL 0x0b91 +#define WCD934X_CDC_RX4_RX_PATH_CFG0 0x0b92 +#define WCD934X_CDC_RX4_RX_PATH_CFG1 0x0b93 +#define WCD934X_CDC_RX4_RX_PATH_CFG2 0x0b94 +#define WCD934X_CDC_RX4_RX_VOL_CTL 0x0b95 +#define WCD934X_CDC_RX4_RX_PATH_MIX_CTL 0x0b96 +#define WCD934X_CDC_RX4_RX_PATH_MIX_CFG 0x0b97 +#define WCD934X_CDC_RX4_RX_VOL_MIX_CTL 0x0b98 +#define WCD934X_CDC_RX4_RX_PATH_SEC0 0x0b99 +#define WCD934X_CDC_RX4_RX_PATH_SEC1 0x0b9a +#define WCD934X_CDC_RX4_RX_PATH_SEC2 0x0b9b +#define WCD934X_CDC_RX4_RX_PATH_SEC3 0x0b9c +#define WCD934X_CDC_RX4_RX_PATH_SEC5 0x0b9e +#define WCD934X_CDC_RX4_RX_PATH_SEC6 0x0b9f +#define WCD934X_CDC_RX4_RX_PATH_SEC7 0x0ba0 +#define WCD934X_CDC_RX4_RX_PATH_MIX_SEC0 0x0ba1 +#define WCD934X_CDC_RX4_RX_PATH_MIX_SEC1 0x0ba2 +#define WCD934X_CDC_RX4_RX_PATH_DSMDEM_CTL 0x0ba3 +#define WCD934X_CDC_RX7_RX_PATH_CTL 0x0bcd +#define WCD934X_CDC_RX7_RX_PATH_CFG0 0x0bce +#define WCD934X_CDC_RX7_RX_PATH_CFG1 0x0bcf +#define WCD934X_CDC_RX7_RX_PATH_CFG2 0x0bd0 +#define WCD934X_CDC_RX7_RX_VOL_CTL 0x0bd1 +#define WCD934X_CDC_RX7_RX_PATH_MIX_CTL 0x0bd2 +#define WCD934X_CDC_RX7_RX_PATH_MIX_CFG 0x0bd3 +#define WCD934X_CDC_RX7_RX_VOL_MIX_CTL 0x0bd4 +#define WCD934X_CDC_RX7_RX_PATH_SEC0 0x0bd5 +#define WCD934X_CDC_RX7_RX_PATH_SEC1 0x0bd6 +#define WCD934X_CDC_RX7_RX_PATH_SEC2 0x0bd7 +#define WCD934X_CDC_RX7_RX_PATH_SEC3 0x0bd8 +#define WCD934X_CDC_RX7_RX_PATH_SEC5 0x0bda +#define WCD934X_CDC_RX7_RX_PATH_SEC6 0x0bdb +#define WCD934X_CDC_RX7_RX_PATH_SEC7 0x0bdc +#define WCD934X_CDC_RX7_RX_PATH_MIX_SEC0 0x0bdd +#define WCD934X_CDC_RX7_RX_PATH_MIX_SEC1 0x0bde +#define WCD934X_CDC_RX7_RX_PATH_DSMDEM_CTL 0x0bdf +#define WCD934X_CDC_RX8_RX_PATH_CTL 0x0be1 +#define WCD934X_CDC_RX8_RX_PATH_CFG0 0x0be2 +#define WCD934X_CDC_RX8_RX_PATH_CFG1 0x0be3 +#define WCD934X_CDC_RX8_RX_PATH_CFG2 0x0be4 +#define WCD934X_CDC_RX8_RX_VOL_CTL 0x0be5 +#define WCD934X_CDC_RX8_RX_PATH_MIX_CTL 0x0be6 +#define WCD934X_CDC_RX8_RX_PATH_MIX_CFG 0x0be7 +#define WCD934X_CDC_RX8_RX_VOL_MIX_CTL 0x0be8 +#define WCD934X_CDC_RX8_RX_PATH_SEC0 0x0be9 +#define WCD934X_CDC_RX8_RX_PATH_SEC1 0x0bea +#define WCD934X_CDC_RX8_RX_PATH_SEC2 0x0beb +#define WCD934X_CDC_RX8_RX_PATH_SEC3 0x0bec +#define WCD934X_CDC_RX8_RX_PATH_SEC5 0x0bee +#define WCD934X_CDC_RX8_RX_PATH_SEC6 0x0bef +#define WCD934X_CDC_RX8_RX_PATH_SEC7 0x0bf0 +#define WCD934X_CDC_RX8_RX_PATH_MIX_SEC0 0x0bf1 +#define WCD934X_CDC_RX8_RX_PATH_MIX_SEC1 0x0bf2 +#define WCD934X_CDC_RX8_RX_PATH_DSMDEM_CTL 0x0bf3 +#define WCD934X_PAGE12_PAGE_REGISTER 0x0c00 +#define WCD934X_CDC_CLSH_CRC 0x0c01 +#define WCD934X_CDC_CLSH_DLY_CTRL 0x0c02 +#define WCD934X_CDC_CLSH_DECAY_CTRL 0x0c03 +#define WCD934X_CDC_CLSH_HPH_V_PA 0x0c04 +#define WCD934X_CDC_CLSH_EAR_V_PA 0x0c05 +#define WCD934X_CDC_CLSH_HPH_V_HD 0x0c06 +#define WCD934X_CDC_CLSH_EAR_V_HD 0x0c07 +#define WCD934X_CDC_CLSH_K1_MSB 0x0c08 +#define WCD934X_CDC_CLSH_K1_LSB 0x0c09 +#define WCD934X_CDC_CLSH_K2_MSB 0x0c0a +#define WCD934X_CDC_CLSH_K2_LSB 0x0c0b +#define WCD934X_CDC_CLSH_IDLE_CTRL 0x0c0c +#define WCD934X_CDC_CLSH_IDLE_HPH 0x0c0d +#define WCD934X_CDC_CLSH_IDLE_EAR 0x0c0e +#define WCD934X_CDC_CLSH_TEST0 0x0c0f +#define WCD934X_CDC_CLSH_TEST1 0x0c10 +#define WCD934X_CDC_CLSH_OVR_VREF 0x0c11 +#define WCD934X_CDC_BOOST0_BOOST_PATH_CTL 0x0c19 +#define WCD934X_CDC_BOOST0_BOOST_CTL 0x0c1a +#define WCD934X_CDC_BOOST0_BOOST_CFG1 0x0c1b +#define WCD934X_CDC_BOOST0_BOOST_CFG2 0x0c1c +#define WCD934X_CDC_BOOST1_BOOST_PATH_CTL 0x0c21 +#define WCD934X_CDC_BOOST1_BOOST_CTL 0x0c22 +#define WCD934X_CDC_BOOST1_BOOST_CFG1 0x0c23 +#define WCD934X_CDC_BOOST1_BOOST_CFG2 0x0c24 +#define WCD934X_CDC_VBAT_VBAT_PATH_CTL 0x0c3d +#define WCD934X_CDC_VBAT_VBAT_CFG 0x0c3e +#define WCD934X_CDC_VBAT_VBAT_ADC_CAL1 0x0c3f +#define WCD934X_CDC_VBAT_VBAT_ADC_CAL2 0x0c40 +#define WCD934X_CDC_VBAT_VBAT_ADC_CAL3 0x0c41 +#define WCD934X_CDC_VBAT_VBAT_PK_EST1 0x0c42 +#define WCD934X_CDC_VBAT_VBAT_PK_EST2 0x0c43 +#define WCD934X_CDC_VBAT_VBAT_PK_EST3 0x0c44 +#define WCD934X_CDC_VBAT_VBAT_RF_PROC1 0x0c45 +#define WCD934X_CDC_VBAT_VBAT_RF_PROC2 0x0c46 +#define WCD934X_CDC_VBAT_VBAT_TAC1 0x0c47 +#define WCD934X_CDC_VBAT_VBAT_TAC2 0x0c48 +#define WCD934X_CDC_VBAT_VBAT_TAC3 0x0c49 +#define WCD934X_CDC_VBAT_VBAT_TAC4 0x0c4a +#define WCD934X_CDC_VBAT_VBAT_GAIN_UPD1 0x0c4b +#define WCD934X_CDC_VBAT_VBAT_GAIN_UPD2 0x0c4c +#define WCD934X_CDC_VBAT_VBAT_GAIN_UPD3 0x0c4d +#define WCD934X_CDC_VBAT_VBAT_GAIN_UPD4 0x0c4e +#define WCD934X_CDC_VBAT_VBAT_DEBUG1 0x0c4f +#define WCD934X_CDC_VBAT_VBAT_GAIN_UPD_MON 0x0c50 +#define WCD934X_CDC_VBAT_VBAT_GAIN_MON_VAL 0x0c51 +#define WCD934X_CDC_VBAT_VBAT_BAN 0x0c52 +#define WCD934X_MIXING_ASRC0_CLK_RST_CTL 0x0c55 +#define WCD934X_MIXING_ASRC0_CTL0 0x0c56 +#define WCD934X_MIXING_ASRC0_CTL1 0x0c57 +#define WCD934X_MIXING_ASRC0_FIFO_CTL 0x0c58 +#define WCD934X_MIXING_ASRC0_STATUS_FMIN_CNTR_LSB 0x0c59 +#define WCD934X_MIXING_ASRC0_STATUS_FMIN_CNTR_MSB 0x0c5a +#define WCD934X_MIXING_ASRC0_STATUS_FMAX_CNTR_LSB 0x0c5b +#define WCD934X_MIXING_ASRC0_STATUS_FMAX_CNTR_MSB 0x0c5c +#define WCD934X_MIXING_ASRC0_STATUS_FIFO 0x0c5d +#define WCD934X_MIXING_ASRC1_CLK_RST_CTL 0x0c61 +#define WCD934X_MIXING_ASRC1_CTL0 0x0c62 +#define WCD934X_MIXING_ASRC1_CTL1 0x0c63 +#define WCD934X_MIXING_ASRC1_FIFO_CTL 0x0c64 +#define WCD934X_MIXING_ASRC1_STATUS_FMIN_CNTR_LSB 0x0c65 +#define WCD934X_MIXING_ASRC1_STATUS_FMIN_CNTR_MSB 0x0c66 +#define WCD934X_MIXING_ASRC1_STATUS_FMAX_CNTR_LSB 0x0c67 +#define WCD934X_MIXING_ASRC1_STATUS_FMAX_CNTR_MSB 0x0c68 +#define WCD934X_MIXING_ASRC1_STATUS_FIFO 0x0c69 +#define WCD934X_MIXING_ASRC2_CLK_RST_CTL 0x0c6d +#define WCD934X_MIXING_ASRC2_CTL0 0x0c6e +#define WCD934X_MIXING_ASRC2_CTL1 0x0c6f +#define WCD934X_MIXING_ASRC2_FIFO_CTL 0x0c70 +#define WCD934X_MIXING_ASRC2_STATUS_FMIN_CNTR_LSB 0x0c71 +#define WCD934X_MIXING_ASRC2_STATUS_FMIN_CNTR_MSB 0x0c72 +#define WCD934X_MIXING_ASRC2_STATUS_FMAX_CNTR_LSB 0x0c73 +#define WCD934X_MIXING_ASRC2_STATUS_FMAX_CNTR_MSB 0x0c74 +#define WCD934X_MIXING_ASRC2_STATUS_FIFO 0x0c75 +#define WCD934X_MIXING_ASRC3_CLK_RST_CTL 0x0c79 +#define WCD934X_MIXING_ASRC3_CTL0 0x0c7a +#define WCD934X_MIXING_ASRC3_CTL1 0x0c7b +#define WCD934X_MIXING_ASRC3_FIFO_CTL 0x0c7c +#define WCD934X_MIXING_ASRC3_STATUS_FMIN_CNTR_LSB 0x0c7d +#define WCD934X_MIXING_ASRC3_STATUS_FMIN_CNTR_MSB 0x0c7e +#define WCD934X_MIXING_ASRC3_STATUS_FMAX_CNTR_LSB 0x0c7f +#define WCD934X_MIXING_ASRC3_STATUS_FMAX_CNTR_MSB 0x0c80 +#define WCD934X_MIXING_ASRC3_STATUS_FIFO 0x0c81 +#define WCD934X_SWR_AHB_BRIDGE_WR_DATA_0 0x0c85 +#define WCD934X_SWR_AHB_BRIDGE_WR_DATA_1 0x0c86 +#define WCD934X_SWR_AHB_BRIDGE_WR_DATA_2 0x0c87 +#define WCD934X_SWR_AHB_BRIDGE_WR_DATA_3 0x0c88 +#define WCD934X_SWR_AHB_BRIDGE_WR_ADDR_0 0x0c89 +#define WCD934X_SWR_AHB_BRIDGE_WR_ADDR_1 0x0c8a +#define WCD934X_SWR_AHB_BRIDGE_WR_ADDR_2 0x0c8b +#define WCD934X_SWR_AHB_BRIDGE_WR_ADDR_3 0x0c8c +#define WCD934X_SWR_AHB_BRIDGE_RD_ADDR_0 0x0c8d +#define WCD934X_SWR_AHB_BRIDGE_RD_ADDR_1 0x0c8e +#define WCD934X_SWR_AHB_BRIDGE_RD_ADDR_2 0x0c8f +#define WCD934X_SWR_AHB_BRIDGE_RD_ADDR_3 0x0c90 +#define WCD934X_SWR_AHB_BRIDGE_RD_DATA_0 0x0c91 +#define WCD934X_SWR_AHB_BRIDGE_RD_DATA_1 0x0c92 +#define WCD934X_SWR_AHB_BRIDGE_RD_DATA_2 0x0c93 +#define WCD934X_SWR_AHB_BRIDGE_RD_DATA_3 0x0c94 +#define WCD934X_SWR_AHB_BRIDGE_ACCESS_CFG 0x0c95 +#define WCD934X_SWR_AHB_BRIDGE_ACCESS_STATUS 0x0c96 +#define WCD934X_CDC_SIDETONE_SRC0_ST_SRC_PATH_CTL 0x0cb5 +#define WCD934X_CDC_SIDETONE_SRC0_ST_SRC_PATH_CFG1 0x0cb6 +#define WCD934X_CDC_SIDETONE_SRC1_ST_SRC_PATH_CTL 0x0cb9 +#define WCD934X_CDC_SIDETONE_SRC1_ST_SRC_PATH_CFG1 0x0cba +#define WCD934X_SIDETONE_ASRC0_CLK_RST_CTL 0x0cbd +#define WCD934X_SIDETONE_ASRC0_CTL0 0x0cbe +#define WCD934X_SIDETONE_ASRC0_CTL1 0x0cbf +#define WCD934X_SIDETONE_ASRC0_FIFO_CTL 0x0cc0 +#define WCD934X_SIDETONE_ASRC0_STATUS_FMIN_CNTR_LSB 0x0cc1 +#define WCD934X_SIDETONE_ASRC0_STATUS_FMIN_CNTR_MSB 0x0cc2 +#define WCD934X_SIDETONE_ASRC0_STATUS_FMAX_CNTR_LSB 0x0cc3 +#define WCD934X_SIDETONE_ASRC0_STATUS_FMAX_CNTR_MSB 0x0cc4 +#define WCD934X_SIDETONE_ASRC0_STATUS_FIFO 0x0cc5 +#define WCD934X_SIDETONE_ASRC1_CLK_RST_CTL 0x0cc9 +#define WCD934X_SIDETONE_ASRC1_CTL0 0x0cca +#define WCD934X_SIDETONE_ASRC1_CTL1 0x0ccb +#define WCD934X_SIDETONE_ASRC1_FIFO_CTL 0x0ccc +#define WCD934X_SIDETONE_ASRC1_STATUS_FMIN_CNTR_LSB 0x0ccd +#define WCD934X_SIDETONE_ASRC1_STATUS_FMIN_CNTR_MSB 0x0cce +#define WCD934X_SIDETONE_ASRC1_STATUS_FMAX_CNTR_LSB 0x0ccf +#define WCD934X_SIDETONE_ASRC1_STATUS_FMAX_CNTR_MSB 0x0cd0 +#define WCD934X_SIDETONE_ASRC1_STATUS_FIFO 0x0cd1 +#define WCD934X_EC_REF_HQ0_EC_REF_HQ_PATH_CTL 0x0cd5 +#define WCD934X_EC_REF_HQ0_EC_REF_HQ_CFG0 0x0cd6 +#define WCD934X_EC_REF_HQ1_EC_REF_HQ_PATH_CTL 0x0cdd +#define WCD934X_EC_REF_HQ1_EC_REF_HQ_CFG0 0x0cde +#define WCD934X_EC_ASRC0_CLK_RST_CTL 0x0ce5 +#define WCD934X_EC_ASRC0_CTL0 0x0ce6 +#define WCD934X_EC_ASRC0_CTL1 0x0ce7 +#define WCD934X_EC_ASRC0_FIFO_CTL 0x0ce8 +#define WCD934X_EC_ASRC0_STATUS_FMIN_CNTR_LSB 0x0ce9 +#define WCD934X_EC_ASRC0_STATUS_FMIN_CNTR_MSB 0x0cea +#define WCD934X_EC_ASRC0_STATUS_FMAX_CNTR_LSB 0x0ceb +#define WCD934X_EC_ASRC0_STATUS_FMAX_CNTR_MSB 0x0cec +#define WCD934X_EC_ASRC0_STATUS_FIFO 0x0ced +#define WCD934X_EC_ASRC1_CLK_RST_CTL 0x0cf1 +#define WCD934X_EC_ASRC1_CTL0 0x0cf2 +#define WCD934X_EC_ASRC1_CTL1 0x0cf3 +#define WCD934X_EC_ASRC1_FIFO_CTL 0x0cf4 +#define WCD934X_EC_ASRC1_STATUS_FMIN_CNTR_LSB 0x0cf5 +#define WCD934X_EC_ASRC1_STATUS_FMIN_CNTR_MSB 0x0cf6 +#define WCD934X_EC_ASRC1_STATUS_FMAX_CNTR_LSB 0x0cf7 +#define WCD934X_EC_ASRC1_STATUS_FMAX_CNTR_MSB 0x0cf8 +#define WCD934X_EC_ASRC1_STATUS_FIFO 0x0cf9 +#define WCD934X_PAGE13_PAGE_REGISTER 0x0d00 +#define WCD934X_CDC_RX_INP_MUX_RX_INT0_CFG0 0x0d01 +#define WCD934X_CDC_RX_INP_MUX_RX_INT0_CFG1 0x0d02 +#define WCD934X_CDC_RX_INP_MUX_RX_INT1_CFG0 0x0d03 +#define WCD934X_CDC_RX_INP_MUX_RX_INT1_CFG1 0x0d04 +#define WCD934X_CDC_RX_INP_MUX_RX_INT2_CFG0 0x0d05 +#define WCD934X_CDC_RX_INP_MUX_RX_INT2_CFG1 0x0d06 +#define WCD934X_CDC_RX_INP_MUX_RX_INT3_CFG0 0x0d07 +#define WCD934X_CDC_RX_INP_MUX_RX_INT3_CFG1 0x0d08 +#define WCD934X_CDC_RX_INP_MUX_RX_INT4_CFG0 0x0d09 +#define WCD934X_CDC_RX_INP_MUX_RX_INT4_CFG1 0x0d0a +#define WCD934X_CDC_RX_INP_MUX_RX_INT7_CFG0 0x0d0f +#define WCD934X_CDC_RX_INP_MUX_RX_INT7_CFG1 0x0d10 +#define WCD934X_CDC_RX_INP_MUX_RX_INT8_CFG0 0x0d11 +#define WCD934X_CDC_RX_INP_MUX_RX_INT8_CFG1 0x0d12 +#define WCD934X_CDC_RX_INP_MUX_RX_MIX_CFG0 0x0d13 +#define WCD934X_CDC_RX_INP_MUX_RX_MIX_CFG1 0x0d14 +#define WCD934X_CDC_RX_INP_MUX_RX_MIX_CFG2 0x0d15 +#define WCD934X_CDC_RX_INP_MUX_RX_MIX_CFG3 0x0d16 +#define WCD934X_CDC_RX_INP_MUX_RX_MIX_CFG4 0x0d17 +#define WCD934X_CDC_RX_INP_MUX_SIDETONE_SRC_CFG0 0x0d18 +#define WCD934X_CDC_RX_INP_MUX_SIDETONE_SRC_CFG1 0x0d19 +#define WCD934X_CDC_RX_INP_MUX_ANC_CFG0 0x0d1a +#define WCD934X_CDC_RX_INP_MUX_SPLINE_ASRC_CFG0 0x0d1b +#define WCD934X_CDC_RX_INP_MUX_EC_REF_HQ_CFG0 0x0d1c +#define WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG0 0x0d1d +#define WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG1 0x0d1e +#define WCD934X_CDC_TX_INP_MUX_ADC_MUX1_CFG0 0x0d1f +#define WCD934X_CDC_TX_INP_MUX_ADC_MUX1_CFG1 0x0d20 +#define WCD934X_CDC_TX_INP_MUX_ADC_MUX2_CFG0 0x0d21 +#define WCD934X_CDC_TX_INP_MUX_ADC_MUX2_CFG1 0x0d22 +#define WCD934X_CDC_TX_INP_MUX_ADC_MUX3_CFG0 0x0d23 +#define WCD934X_CDC_TX_INP_MUX_ADC_MUX3_CFG1 0x0d25 +#define WCD934X_CDC_TX_INP_MUX_ADC_MUX4_CFG0 0x0d26 +#define WCD934X_CDC_TX_INP_MUX_ADC_MUX5_CFG0 0x0d27 +#define WCD934X_CDC_TX_INP_MUX_ADC_MUX6_CFG0 0x0d28 +#define WCD934X_CDC_TX_INP_MUX_ADC_MUX7_CFG0 0x0d29 +#define WCD934X_CDC_TX_INP_MUX_ADC_MUX8_CFG0 0x0d2a +#define WCD934X_CDC_TX_INP_MUX_ADC_MUX10_CFG0 0x0d2b +#define WCD934X_CDC_TX_INP_MUX_ADC_MUX11_CFG0 0x0d2c +#define WCD934X_CDC_TX_INP_MUX_ADC_MUX12_CFG0 0x0d2d +#define WCD934X_CDC_TX_INP_MUX_ADC_MUX13_CFG0 0x0d2e +#define WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG0 0x0d31 +#define WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG1 0x0d32 +#define WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG2 0x0d33 +#define WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG3 0x0d34 +#define WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG0 0x0d35 +#define WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG1 0x0d36 +#define WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG2 0x0d37 +#define WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG3 0x0d38 +#define WCD934X_CDC_IF_ROUTER_TX_MUX_CFG0 0x0d3a +#define WCD934X_CDC_IF_ROUTER_TX_MUX_CFG1 0x0d3b +#define WCD934X_CDC_IF_ROUTER_TX_MUX_CFG2 0x0d3c +#define WCD934X_CDC_IF_ROUTER_TX_MUX_CFG3 0x0d3d +#define WCD934X_CDC_CLK_RST_CTRL_MCLK_CONTROL 0x0d41 +#define WCD934X_CDC_CLK_RST_CTRL_FS_CNT_CONTROL 0x0d42 +#define WCD934X_CDC_CLK_RST_CTRL_SWR_CONTROL 0x0d43 +#define WCD934X_CDC_CLK_RST_CTRL_DSD_CONTROL 0x0d44 +#define WCD934X_CDC_CLK_RST_CTRL_ASRC_SHARE_CONTROL 0x0d45 +#define WCD934X_CDC_CLK_RST_CTRL_GFM_CONTROL 0x0d46 +#define WCD934X_CDC_PROX_DETECT_PROX_CTL 0x0d49 +#define WCD934X_CDC_PROX_DETECT_PROX_POLL_PERIOD0 0x0d4a +#define WCD934X_CDC_PROX_DETECT_PROX_POLL_PERIOD1 0x0d4b +#define WCD934X_CDC_PROX_DETECT_PROX_SIG_PATTERN_LSB 0x0d4c +#define WCD934X_CDC_PROX_DETECT_PROX_SIG_PATTERN_MSB 0x0d4d +#define WCD934X_CDC_PROX_DETECT_PROX_STATUS 0x0d4e +#define WCD934X_CDC_PROX_DETECT_PROX_TEST_CTRL 0x0d4f +#define WCD934X_CDC_PROX_DETECT_PROX_TEST_BUFF_LSB 0x0d50 +#define WCD934X_CDC_PROX_DETECT_PROX_TEST_BUFF_MSB 0x0d51 +#define WCD934X_CDC_PROX_DETECT_PROX_TEST_BUFF_LSB_RD 0x0d52 +#define WCD934X_CDC_PROX_DETECT_PROX_TEST_BUFF_MSB_RD 0x0d53 +#define WCD934X_CDC_PROX_DETECT_PROX_CTL_REPEAT_PAT 0x0d54 +#define WCD934X_CDC_SIDETONE_IIR0_IIR_PATH_CTL 0x0d55 +#define WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B1_CTL 0x0d56 +#define WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B2_CTL 0x0d57 +#define WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B3_CTL 0x0d58 +#define WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B4_CTL 0x0d59 +#define WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B5_CTL 0x0d5a +#define WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B6_CTL 0x0d5b +#define WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B7_CTL 0x0d5c +#define WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B8_CTL 0x0d5d +#define WCD934X_CDC_SIDETONE_IIR0_IIR_CTL 0x0d5e +#define WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_TIMER_CTL 0x0d5f +#define WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL 0x0d60 +#define WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL 0x0d61 +#define WCD934X_CDC_SIDETONE_IIR1_IIR_PATH_CTL 0x0d65 +#define WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B1_CTL 0x0d66 +#define WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B2_CTL 0x0d67 +#define WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B3_CTL 0x0d68 +#define WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B4_CTL 0x0d69 +#define WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B5_CTL 0x0d6a +#define WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B6_CTL 0x0d6b +#define WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B7_CTL 0x0d6c +#define WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B8_CTL 0x0d6d +#define WCD934X_CDC_SIDETONE_IIR1_IIR_CTL 0x0d6e +#define WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_TIMER_CTL 0x0d6f +#define WCD934X_CDC_SIDETONE_IIR1_IIR_COEF_B1_CTL 0x0d70 +#define WCD934X_CDC_SIDETONE_IIR1_IIR_COEF_B2_CTL 0x0d71 +#define WCD934X_CDC_TOP_TOP_CFG0 0x0d81 +#define WCD934X_CDC_TOP_TOP_CFG1 0x0d82 +#define WCD934X_CDC_TOP_TOP_CFG7 0x0d88 +#define WCD934X_CDC_TOP_HPHL_COMP_WR_LSB 0x0d89 +#define WCD934X_CDC_TOP_HPHL_COMP_WR_MSB 0x0d8a +#define WCD934X_CDC_TOP_HPHL_COMP_LUT 0x0d8b +#define WCD934X_CDC_TOP_HPHL_COMP_RD_LSB 0x0d8c +#define WCD934X_CDC_TOP_HPHL_COMP_RD_MSB 0x0d8d +#define WCD934X_CDC_TOP_HPHR_COMP_WR_LSB 0x0d8e +#define WCD934X_CDC_TOP_HPHR_COMP_WR_MSB 0x0d8f +#define WCD934X_CDC_TOP_HPHR_COMP_LUT 0x0d90 +#define WCD934X_CDC_TOP_HPHR_COMP_RD_LSB 0x0d91 +#define WCD934X_CDC_TOP_HPHR_COMP_RD_MSB 0x0d92 +#define WCD934X_CDC_TOP_DIFFL_COMP_WR_LSB 0x0d93 +#define WCD934X_CDC_TOP_DIFFL_COMP_WR_MSB 0x0d94 +#define WCD934X_CDC_TOP_DIFFL_COMP_LUT 0x0d95 +#define WCD934X_CDC_TOP_DIFFL_COMP_RD_LSB 0x0d96 +#define WCD934X_CDC_TOP_DIFFL_COMP_RD_MSB 0x0d97 +#define WCD934X_CDC_TOP_DIFFR_COMP_WR_LSB 0x0d98 +#define WCD934X_CDC_TOP_DIFFR_COMP_WR_MSB 0x0d99 +#define WCD934X_CDC_TOP_DIFFR_COMP_LUT 0x0d9a +#define WCD934X_CDC_TOP_DIFFR_COMP_RD_LSB 0x0d9b +#define WCD934X_CDC_TOP_DIFFR_COMP_RD_MSB 0x0d9c +#define WCD934X_CDC_DSD0_PATH_CTL 0x0db1 +#define WCD934X_CDC_DSD0_CFG0 0x0db2 +#define WCD934X_CDC_DSD0_CFG1 0x0db3 +#define WCD934X_CDC_DSD0_CFG2 0x0db4 +#define WCD934X_CDC_DSD0_CFG3 0x0db5 +#define WCD934X_CDC_DSD0_CFG4 0x0db6 +#define WCD934X_CDC_DSD0_CFG5 0x0db7 +#define WCD934X_CDC_DSD1_PATH_CTL 0x0dc1 +#define WCD934X_CDC_DSD1_CFG0 0x0dc2 +#define WCD934X_CDC_DSD1_CFG1 0x0dc3 +#define WCD934X_CDC_DSD1_CFG2 0x0dc4 +#define WCD934X_CDC_DSD1_CFG3 0x0dc5 +#define WCD934X_CDC_DSD1_CFG4 0x0dc6 +#define WCD934X_CDC_DSD1_CFG5 0x0dc7 +#define WCD934X_CDC_RX_IDLE_DET_PATH_CTL 0x0dd1 +#define WCD934X_CDC_RX_IDLE_DET_CFG0 0x0dd2 +#define WCD934X_CDC_RX_IDLE_DET_CFG1 0x0dd3 +#define WCD934X_CDC_RX_IDLE_DET_CFG2 0x0dd4 +#define WCD934X_CDC_RX_IDLE_DET_CFG3 0x0dd5 +#define WCD934X_PAGE14_PAGE_REGISTER 0x0e00 +#define WCD934X_CDC_RATE_EST0_RE_CLK_RST_CTL 0x0e01 +#define WCD934X_CDC_RATE_EST0_RE_CTL 0x0e02 +#define WCD934X_CDC_RATE_EST0_RE_PULSE_SUPR_CTL 0x0e03 +#define WCD934X_CDC_RATE_EST0_RE_TIMER 0x0e04 +#define WCD934X_CDC_RATE_EST0_RE_BW_SW 0x0e05 +#define WCD934X_CDC_RATE_EST0_RE_THRESH 0x0e06 +#define WCD934X_CDC_RATE_EST0_RE_STATUS 0x0e07 +#define WCD934X_CDC_RATE_EST0_RE_DIAG_CTRL 0x0e09 +#define WCD934X_CDC_RATE_EST0_RE_DIAG_TIMER2 0x0e0c +#define WCD934X_CDC_RATE_EST0_RE_DIAG_OFFSET_BW1 0x0e0d +#define WCD934X_CDC_RATE_EST0_RE_DIAG_OFFSET_BW2 0x0e0e +#define WCD934X_CDC_RATE_EST0_RE_DIAG_OFFSET_BW3 0x0e0f +#define WCD934X_CDC_RATE_EST0_RE_DIAG_OFFSET_BW4 0x0e10 +#define WCD934X_CDC_RATE_EST0_RE_DIAG_OFFSET_BW5 0x0e11 +#define WCD934X_CDC_RATE_EST0_RE_DIAG_LIMIT_BW1 0x0e12 +#define WCD934X_CDC_RATE_EST0_RE_DIAG_LIMIT_BW2 0x0e13 +#define WCD934X_CDC_RATE_EST0_RE_DIAG_LIMIT_BW3 0x0e14 +#define WCD934X_CDC_RATE_EST0_RE_DIAG_LIMIT_BW4 0x0e15 +#define WCD934X_CDC_RATE_EST0_RE_DIAG_LIMIT_BW5 0x0e16 +#define WCD934X_CDC_RATE_EST0_RE_DIAG_LIMITD1_BW1 0x0e17 +#define WCD934X_CDC_RATE_EST0_RE_DIAG_LIMITD1_BW2 0x0e18 +#define WCD934X_CDC_RATE_EST0_RE_DIAG_LIMITD1_BW3 0x0e19 +#define WCD934X_CDC_RATE_EST0_RE_DIAG_LIMITD1_BW4 0x0e1a +#define WCD934X_CDC_RATE_EST0_RE_DIAG_LIMITD1_BW5 0x0e1b +#define WCD934X_CDC_RATE_EST0_RE_DIAG_HYST_BW1 0x0e1c +#define WCD934X_CDC_RATE_EST0_RE_DIAG_HYST_BW2 0x0e1d +#define WCD934X_CDC_RATE_EST0_RE_DIAG_HYST_BW3 0x0e1e +#define WCD934X_CDC_RATE_EST0_RE_DIAG_HYST_BW4 0x0e1f +#define WCD934X_CDC_RATE_EST0_RE_DIAG_HYST_BW5 0x0e20 +#define WCD934X_CDC_RATE_EST0_RE_RMAX_DIAG 0x0e21 +#define WCD934X_CDC_RATE_EST0_RE_RMIN_DIAG 0x0e22 +#define WCD934X_CDC_RATE_EST0_RE_PH_DET 0x0e23 +#define WCD934X_CDC_RATE_EST0_RE_DIAG_CLR 0x0e24 +#define WCD934X_CDC_RATE_EST0_RE_MB_SW_STATE 0x0e25 +#define WCD934X_CDC_RATE_EST0_RE_MAST_DIAG_STATE 0x0e26 +#define WCD934X_CDC_RATE_EST0_RE_RATE_OUT_7_0 0x0e27 +#define WCD934X_CDC_RATE_EST0_RE_RATE_OUT_15_8 0x0e28 +#define WCD934X_CDC_RATE_EST0_RE_RATE_OUT_23_16 0x0e29 +#define WCD934X_CDC_RATE_EST0_RE_RATE_OUT_31_24 0x0e2a +#define WCD934X_CDC_RATE_EST0_RE_RATE_OUT_39_32 0x0e2b +#define WCD934X_CDC_RATE_EST0_RE_RATE_OUT_40_43 0x0e2c +#define WCD934X_CDC_RATE_EST1_RE_CLK_RST_CTL 0x0e31 +#define WCD934X_CDC_RATE_EST1_RE_CTL 0x0e32 +#define WCD934X_CDC_RATE_EST1_RE_PULSE_SUPR_CTL 0x0e33 +#define WCD934X_CDC_RATE_EST1_RE_TIMER 0x0e34 +#define WCD934X_CDC_RATE_EST1_RE_BW_SW 0x0e35 +#define WCD934X_CDC_RATE_EST1_RE_THRESH 0x0e36 +#define WCD934X_CDC_RATE_EST1_RE_STATUS 0x0e37 +#define WCD934X_CDC_RATE_EST1_RE_DIAG_CTRL 0x0e39 +#define WCD934X_CDC_RATE_EST1_RE_DIAG_TIMER2 0x0e3c +#define WCD934X_CDC_RATE_EST1_RE_DIAG_OFFSET_BW1 0x0e3d +#define WCD934X_CDC_RATE_EST1_RE_DIAG_OFFSET_BW2 0x0e3e +#define WCD934X_CDC_RATE_EST1_RE_DIAG_OFFSET_BW3 0x0e3f +#define WCD934X_CDC_RATE_EST1_RE_DIAG_OFFSET_BW4 0x0e40 +#define WCD934X_CDC_RATE_EST1_RE_DIAG_OFFSET_BW5 0x0e41 +#define WCD934X_CDC_RATE_EST1_RE_DIAG_LIMIT_BW1 0x0e42 +#define WCD934X_CDC_RATE_EST1_RE_DIAG_LIMIT_BW2 0x0e43 +#define WCD934X_CDC_RATE_EST1_RE_DIAG_LIMIT_BW3 0x0e44 +#define WCD934X_CDC_RATE_EST1_RE_DIAG_LIMIT_BW4 0x0e45 +#define WCD934X_CDC_RATE_EST1_RE_DIAG_LIMIT_BW5 0x0e46 +#define WCD934X_CDC_RATE_EST1_RE_DIAG_LIMITD1_BW1 0x0e47 +#define WCD934X_CDC_RATE_EST1_RE_DIAG_LIMITD1_BW2 0x0e48 +#define WCD934X_CDC_RATE_EST1_RE_DIAG_LIMITD1_BW3 0x0e49 +#define WCD934X_CDC_RATE_EST1_RE_DIAG_LIMITD1_BW4 0x0e4a +#define WCD934X_CDC_RATE_EST1_RE_DIAG_LIMITD1_BW5 0x0e4b +#define WCD934X_CDC_RATE_EST1_RE_DIAG_HYST_BW1 0x0e4c +#define WCD934X_CDC_RATE_EST1_RE_DIAG_HYST_BW2 0x0e4d +#define WCD934X_CDC_RATE_EST1_RE_DIAG_HYST_BW3 0x0e4e +#define WCD934X_CDC_RATE_EST1_RE_DIAG_HYST_BW4 0x0e4f +#define WCD934X_CDC_RATE_EST1_RE_DIAG_HYST_BW5 0x0e50 +#define WCD934X_CDC_RATE_EST1_RE_RMAX_DIAG 0x0e51 +#define WCD934X_CDC_RATE_EST1_RE_RMIN_DIAG 0x0e52 +#define WCD934X_CDC_RATE_EST1_RE_PH_DET 0x0e53 +#define WCD934X_CDC_RATE_EST1_RE_DIAG_CLR 0x0e54 +#define WCD934X_CDC_RATE_EST1_RE_MB_SW_STATE 0x0e55 +#define WCD934X_CDC_RATE_EST1_RE_MAST_DIAG_STATE 0x0e56 +#define WCD934X_CDC_RATE_EST1_RE_RATE_OUT_7_0 0x0e57 +#define WCD934X_CDC_RATE_EST1_RE_RATE_OUT_15_8 0x0e58 +#define WCD934X_CDC_RATE_EST1_RE_RATE_OUT_23_16 0x0e59 +#define WCD934X_CDC_RATE_EST1_RE_RATE_OUT_31_24 0x0e5a +#define WCD934X_CDC_RATE_EST1_RE_RATE_OUT_39_32 0x0e5b +#define WCD934X_CDC_RATE_EST1_RE_RATE_OUT_40_43 0x0e5c +#define WCD934X_CDC_RATE_EST2_RE_CLK_RST_CTL 0x0e61 +#define WCD934X_CDC_RATE_EST2_RE_CTL 0x0e62 +#define WCD934X_CDC_RATE_EST2_RE_PULSE_SUPR_CTL 0x0e63 +#define WCD934X_CDC_RATE_EST2_RE_TIMER 0x0e64 +#define WCD934X_CDC_RATE_EST2_RE_BW_SW 0x0e65 +#define WCD934X_CDC_RATE_EST2_RE_THRESH 0x0e66 +#define WCD934X_CDC_RATE_EST2_RE_STATUS 0x0e67 +#define WCD934X_CDC_RATE_EST2_RE_DIAG_CTRL 0x0e69 +#define WCD934X_CDC_RATE_EST2_RE_DIAG_TIMER2 0x0e6c +#define WCD934X_CDC_RATE_EST2_RE_DIAG_OFFSET_BW1 0x0e6d +#define WCD934X_CDC_RATE_EST2_RE_DIAG_OFFSET_BW2 0x0e6e +#define WCD934X_CDC_RATE_EST2_RE_DIAG_OFFSET_BW3 0x0e6f +#define WCD934X_CDC_RATE_EST2_RE_DIAG_OFFSET_BW4 0x0e70 +#define WCD934X_CDC_RATE_EST2_RE_DIAG_OFFSET_BW5 0x0e71 +#define WCD934X_CDC_RATE_EST2_RE_DIAG_LIMIT_BW1 0x0e72 +#define WCD934X_CDC_RATE_EST2_RE_DIAG_LIMIT_BW2 0x0e73 +#define WCD934X_CDC_RATE_EST2_RE_DIAG_LIMIT_BW3 0x0e74 +#define WCD934X_CDC_RATE_EST2_RE_DIAG_LIMIT_BW4 0x0e75 +#define WCD934X_CDC_RATE_EST2_RE_DIAG_LIMIT_BW5 0x0e76 +#define WCD934X_CDC_RATE_EST2_RE_DIAG_LIMITD1_BW1 0x0e77 +#define WCD934X_CDC_RATE_EST2_RE_DIAG_LIMITD1_BW2 0x0e78 +#define WCD934X_CDC_RATE_EST2_RE_DIAG_LIMITD1_BW3 0x0e79 +#define WCD934X_CDC_RATE_EST2_RE_DIAG_LIMITD1_BW4 0x0e7a +#define WCD934X_CDC_RATE_EST2_RE_DIAG_LIMITD1_BW5 0x0e7b +#define WCD934X_CDC_RATE_EST2_RE_DIAG_HYST_BW1 0x0e7c +#define WCD934X_CDC_RATE_EST2_RE_DIAG_HYST_BW2 0x0e7d +#define WCD934X_CDC_RATE_EST2_RE_DIAG_HYST_BW3 0x0e7e +#define WCD934X_CDC_RATE_EST2_RE_DIAG_HYST_BW4 0x0e7f +#define WCD934X_CDC_RATE_EST2_RE_DIAG_HYST_BW5 0x0e80 +#define WCD934X_CDC_RATE_EST2_RE_RMAX_DIAG 0x0e81 +#define WCD934X_CDC_RATE_EST2_RE_RMIN_DIAG 0x0e82 +#define WCD934X_CDC_RATE_EST2_RE_PH_DET 0x0e83 +#define WCD934X_CDC_RATE_EST2_RE_DIAG_CLR 0x0e84 +#define WCD934X_CDC_RATE_EST2_RE_MB_SW_STATE 0x0e85 +#define WCD934X_CDC_RATE_EST2_RE_MAST_DIAG_STATE 0x0e86 +#define WCD934X_CDC_RATE_EST2_RE_RATE_OUT_7_0 0x0e87 +#define WCD934X_CDC_RATE_EST2_RE_RATE_OUT_15_8 0x0e88 +#define WCD934X_CDC_RATE_EST2_RE_RATE_OUT_23_16 0x0e89 +#define WCD934X_CDC_RATE_EST2_RE_RATE_OUT_31_24 0x0e8a +#define WCD934X_CDC_RATE_EST2_RE_RATE_OUT_39_32 0x0e8b +#define WCD934X_CDC_RATE_EST2_RE_RATE_OUT_40_43 0x0e8c +#define WCD934X_CDC_RATE_EST3_RE_CLK_RST_CTL 0x0e91 +#define WCD934X_CDC_RATE_EST3_RE_CTL 0x0e92 +#define WCD934X_CDC_RATE_EST3_RE_PULSE_SUPR_CTL 0x0e93 +#define WCD934X_CDC_RATE_EST3_RE_TIMER 0x0e94 +#define WCD934X_CDC_RATE_EST3_RE_BW_SW 0x0e95 +#define WCD934X_CDC_RATE_EST3_RE_THRESH 0x0e96 +#define WCD934X_CDC_RATE_EST3_RE_STATUS 0x0e97 +#define WCD934X_CDC_RATE_EST3_RE_DIAG_CTRL 0x0e99 +#define WCD934X_CDC_RATE_EST3_RE_DIAG_TIMER2 0x0e9c +#define WCD934X_CDC_RATE_EST3_RE_DIAG_OFFSET_BW1 0x0e9d +#define WCD934X_CDC_RATE_EST3_RE_DIAG_OFFSET_BW2 0x0e9e +#define WCD934X_CDC_RATE_EST3_RE_DIAG_OFFSET_BW3 0x0e9f +#define WCD934X_CDC_RATE_EST3_RE_DIAG_OFFSET_BW4 0x0ea0 +#define WCD934X_CDC_RATE_EST3_RE_DIAG_OFFSET_BW5 0x0ea1 +#define WCD934X_CDC_RATE_EST3_RE_DIAG_LIMIT_BW1 0x0ea2 +#define WCD934X_CDC_RATE_EST3_RE_DIAG_LIMIT_BW2 0x0ea3 +#define WCD934X_CDC_RATE_EST3_RE_DIAG_LIMIT_BW3 0x0ea4 +#define WCD934X_CDC_RATE_EST3_RE_DIAG_LIMIT_BW4 0x0ea5 +#define WCD934X_CDC_RATE_EST3_RE_DIAG_LIMIT_BW5 0x0ea6 +#define WCD934X_CDC_RATE_EST3_RE_DIAG_LIMITD1_BW1 0x0ea7 +#define WCD934X_CDC_RATE_EST3_RE_DIAG_LIMITD1_BW2 0x0ea8 +#define WCD934X_CDC_RATE_EST3_RE_DIAG_LIMITD1_BW3 0x0ea9 +#define WCD934X_CDC_RATE_EST3_RE_DIAG_LIMITD1_BW4 0x0eaa +#define WCD934X_CDC_RATE_EST3_RE_DIAG_LIMITD1_BW5 0x0eab +#define WCD934X_CDC_RATE_EST3_RE_DIAG_HYST_BW1 0x0eac +#define WCD934X_CDC_RATE_EST3_RE_DIAG_HYST_BW2 0x0ead +#define WCD934X_CDC_RATE_EST3_RE_DIAG_HYST_BW3 0x0eae +#define WCD934X_CDC_RATE_EST3_RE_DIAG_HYST_BW4 0x0eaf +#define WCD934X_CDC_RATE_EST3_RE_DIAG_HYST_BW5 0x0eb0 +#define WCD934X_CDC_RATE_EST3_RE_RMAX_DIAG 0x0eb1 +#define WCD934X_CDC_RATE_EST3_RE_RMIN_DIAG 0x0eb2 +#define WCD934X_CDC_RATE_EST3_RE_PH_DET 0x0eb3 +#define WCD934X_CDC_RATE_EST3_RE_DIAG_CLR 0x0eb4 +#define WCD934X_CDC_RATE_EST3_RE_MB_SW_STATE 0x0eb5 +#define WCD934X_CDC_RATE_EST3_RE_MAST_DIAG_STATE 0x0eb6 +#define WCD934X_CDC_RATE_EST3_RE_RATE_OUT_7_0 0x0eb7 +#define WCD934X_CDC_RATE_EST3_RE_RATE_OUT_15_8 0x0eb8 +#define WCD934X_CDC_RATE_EST3_RE_RATE_OUT_23_16 0x0eb9 +#define WCD934X_CDC_RATE_EST3_RE_RATE_OUT_31_24 0x0eba +#define WCD934X_CDC_RATE_EST3_RE_RATE_OUT_39_32 0x0ebb +#define WCD934X_CDC_RATE_EST3_RE_RATE_OUT_40_43 0x0ebc +#define WCD934X_PAGE15_PAGE_REGISTER 0x0f00 +#define WCD934X_SPLINE_SRC0_CLK_RST_CTL_0 0x0f01 +#define WCD934X_SPLINE_SRC0_STATUS 0x0f02 +#define WCD934X_SPLINE_SRC1_CLK_RST_CTL_0 0x0f19 +#define WCD934X_SPLINE_SRC1_STATUS 0x0f1a +#define WCD934X_SPLINE_SRC2_CLK_RST_CTL_0 0x0f31 +#define WCD934X_SPLINE_SRC2_STATUS 0x0f32 +#define WCD934X_SPLINE_SRC3_CLK_RST_CTL_0 0x0f49 +#define WCD934X_SPLINE_SRC3_STATUS 0x0f4a +#define WCD934X_CDC_DEBUG_DSD0_DEBUG_CFG0 0x0fa1 +#define WCD934X_CDC_DEBUG_DSD0_DEBUG_CFG1 0x0fa2 +#define WCD934X_CDC_DEBUG_DSD0_DEBUG_CFG2 0x0fa3 +#define WCD934X_CDC_DEBUG_DSD0_DEBUG_CFG3 0x0fa4 +#define WCD934X_CDC_DEBUG_DSD1_DEBUG_CFG0 0x0fa5 +#define WCD934X_CDC_DEBUG_DSD1_DEBUG_CFG1 0x0fa6 +#define WCD934X_CDC_DEBUG_DSD1_DEBUG_CFG2 0x0fa7 +#define WCD934X_CDC_DEBUG_DSD1_DEBUG_CFG3 0x0fa8 +#define WCD934X_CDC_DEBUG_SPLINE_SRC_DEBUG_CFG0 0x0fa9 +#define WCD934X_CDC_DEBUG_SPLINE_SRC_DEBUG_CFG1 0x0faa +#define WCD934X_CDC_DEBUG_RC_RE_ASRC_DEBUG_CFG0 0x0fab +#define WCD934X_CDC_DEBUG_ANC0_RC0_FIFO_CTL 0x0fac +#define WCD934X_CDC_DEBUG_ANC0_RC1_FIFO_CTL 0x0fad +#define WCD934X_CDC_DEBUG_ANC1_RC0_FIFO_CTL 0x0fae +#define WCD934X_CDC_DEBUG_ANC1_RC1_FIFO_CTL 0x0faf +#define WCD934X_CDC_DEBUG_ANC_RC_RST_DBG_CNTR 0x0fb0 +#define WCD934X_PAGE80_PAGE_REGISTER 0x5000 +#define WCD934X_CODEC_CPR_WR_DATA_0 0x5001 +#define WCD934X_CODEC_CPR_WR_DATA_1 0x5002 +#define WCD934X_CODEC_CPR_WR_DATA_2 0x5003 +#define WCD934X_CODEC_CPR_WR_DATA_3 0x5004 +#define WCD934X_CODEC_CPR_WR_ADDR_0 0x5005 +#define WCD934X_CODEC_CPR_WR_ADDR_1 0x5006 +#define WCD934X_CODEC_CPR_WR_ADDR_2 0x5007 +#define WCD934X_CODEC_CPR_WR_ADDR_3 0x5008 +#define WCD934X_CODEC_CPR_RD_ADDR_0 0x5009 +#define WCD934X_CODEC_CPR_RD_ADDR_1 0x500a +#define WCD934X_CODEC_CPR_RD_ADDR_2 0x500b +#define WCD934X_CODEC_CPR_RD_ADDR_3 0x500c +#define WCD934X_CODEC_CPR_RD_DATA_0 0x500d +#define WCD934X_CODEC_CPR_RD_DATA_1 0x500e +#define WCD934X_CODEC_CPR_RD_DATA_2 0x500f +#define WCD934X_CODEC_CPR_RD_DATA_3 0x5010 +#define WCD934X_CODEC_CPR_ACCESS_CFG 0x5011 +#define WCD934X_CODEC_CPR_ACCESS_STATUS 0x5012 +#define WCD934X_CODEC_CPR_NOM_CX_VDD 0x5021 +#define WCD934X_CODEC_CPR_SVS_CX_VDD 0x5022 +#define WCD934X_CODEC_CPR_SVS2_CX_VDD 0x5023 +#define WCD934X_CODEC_CPR_NOM_MX_VDD 0x5024 +#define WCD934X_CODEC_CPR_SVS_MX_VDD 0x5025 +#define WCD934X_CODEC_CPR_SVS2_MX_VDD 0x5026 +#define WCD934X_CODEC_CPR_SVS2_MIN_CX_VDD 0x5027 +#define WCD934X_CODEC_CPR_MAX_SVS2_STEP 0x5028 +#define WCD934X_CODEC_CPR_CTL 0x5029 +#define WCD934X_CODEC_CPR_SW_MODECHNG_STATUS 0x502a +#define WCD934X_CODEC_CPR_SW_MODECHNG_START 0x502b +#define WCD934X_CODEC_CPR_CPR_STATUS 0x502c +#define WCD934X_PAGE128_PAGE_REGISTER 0x8000 +#define WCD934X_TLMM_BIST_MODE_PINCFG 0x8001 +#define WCD934X_TLMM_RF_PA_ON_PINCFG 0x8002 +#define WCD934X_TLMM_INTR1_PINCFG 0x8003 +#define WCD934X_TLMM_INTR2_PINCFG 0x8004 +#define WCD934X_TLMM_SWR_DATA_PINCFG 0x8005 +#define WCD934X_TLMM_SWR_CLK_PINCFG 0x8006 +#define WCD934X_TLMM_I2S_2_SCK_PINCFG 0x8007 +#define WCD934X_TLMM_SLIMBUS_DATA1_PINCFG 0x8008 +#define WCD934X_TLMM_SLIMBUS_DATA2_PINCFG 0x8009 +#define WCD934X_TLMM_SLIMBUS_CLK_PINCFG 0x800a +#define WCD934X_TLMM_I2C_CLK_PINCFG 0x800b +#define WCD934X_TLMM_I2C_DATA_PINCFG 0x800c +#define WCD934X_TLMM_I2S_0_RX_PINCFG 0x800d +#define WCD934X_TLMM_I2S_0_TX_PINCFG 0x800e +#define WCD934X_TLMM_I2S_0_SCK_PINCFG 0x800f +#define WCD934X_TLMM_I2S_0_WS_PINCFG 0x8010 +#define WCD934X_TLMM_I2S_1_RX_PINCFG 0x8011 +#define WCD934X_TLMM_I2S_1_TX_PINCFG 0x8012 +#define WCD934X_TLMM_I2S_1_SCK_PINCFG 0x8013 +#define WCD934X_TLMM_I2S_1_WS_PINCFG 0x8014 +#define WCD934X_TLMM_DMIC1_CLK_PINCFG 0x8015 +#define WCD934X_TLMM_DMIC1_DATA_PINCFG 0x8016 +#define WCD934X_TLMM_DMIC2_CLK_PINCFG 0x8017 +#define WCD934X_TLMM_DMIC2_DATA_PINCFG 0x8018 +#define WCD934X_TLMM_DMIC3_CLK_PINCFG 0x8019 +#define WCD934X_TLMM_DMIC3_DATA_PINCFG 0x801a +#define WCD934X_TLMM_JTCK_PINCFG 0x801b +#define WCD934X_TLMM_GPIO1_PINCFG 0x801c +#define WCD934X_TLMM_GPIO2_PINCFG 0x801d +#define WCD934X_TLMM_GPIO3_PINCFG 0x801e +#define WCD934X_TLMM_GPIO4_PINCFG 0x801f +#define WCD934X_TLMM_SPI_S_CSN_PINCFG 0x8020 +#define WCD934X_TLMM_SPI_S_CLK_PINCFG 0x8021 +#define WCD934X_TLMM_SPI_S_DOUT_PINCFG 0x8022 +#define WCD934X_TLMM_SPI_S_DIN_PINCFG 0x8023 +#define WCD934X_TLMM_BA_N_PINCFG 0x8024 +#define WCD934X_TLMM_GPIO0_PINCFG 0x8025 +#define WCD934X_TLMM_I2S_2_RX_PINCFG 0x8026 +#define WCD934X_TLMM_I2S_2_WS_PINCFG 0x8027 +#define WCD934X_TEST_DEBUG_PIN_CTL_OE_0 0x8031 +#define WCD934X_TEST_DEBUG_PIN_CTL_OE_1 0x8032 +#define WCD934X_TEST_DEBUG_PIN_CTL_OE_2 0x8033 +#define WCD934X_TEST_DEBUG_PIN_CTL_OE_3 0x8034 +#define WCD934X_TEST_DEBUG_PIN_CTL_OE_4 0x8035 +#define WCD934X_TEST_DEBUG_PIN_CTL_DATA_0 0x8036 +#define WCD934X_TEST_DEBUG_PIN_CTL_DATA_1 0x8037 +#define WCD934X_TEST_DEBUG_PIN_CTL_DATA_2 0x8038 +#define WCD934X_TEST_DEBUG_PIN_CTL_DATA_3 0x8039 +#define WCD934X_TEST_DEBUG_PIN_CTL_DATA_4 0x803a +#define WCD934X_TEST_DEBUG_PAD_DRVCTL_0 0x803b +#define WCD934X_TEST_DEBUG_PAD_DRVCTL_1 0x803c +#define WCD934X_TEST_DEBUG_PIN_STATUS 0x803d +#define WCD934X_TEST_DEBUG_NPL_DLY_TEST_1 0x803e +#define WCD934X_TEST_DEBUG_NPL_DLY_TEST_2 0x803f +#define WCD934X_TEST_DEBUG_MEM_CTRL 0x8040 +#define WCD934X_TEST_DEBUG_DEBUG_BUS_SEL 0x8041 +#define WCD934X_TEST_DEBUG_DEBUG_JTAG 0x8042 +#define WCD934X_TEST_DEBUG_DEBUG_EN_1 0x8043 +#define WCD934X_TEST_DEBUG_DEBUG_EN_2 0x8044 +#define WCD934X_TEST_DEBUG_DEBUG_EN_3 0x8045 +#define WCD934X_TEST_DEBUG_DEBUG_EN_4 0x8046 +#define WCD934X_TEST_DEBUG_DEBUG_EN_5 0x8047 +#define WCD934X_TEST_DEBUG_ANA_DTEST_DIR 0x804a +#define WCD934X_TEST_DEBUG_PAD_INP_DISABLE_0 0x804b +#define WCD934X_TEST_DEBUG_PAD_INP_DISABLE_1 0x804c +#define WCD934X_TEST_DEBUG_PAD_INP_DISABLE_2 0x804d +#define WCD934X_TEST_DEBUG_PAD_INP_DISABLE_3 0x804e +#define WCD934X_TEST_DEBUG_PAD_INP_DISABLE_4 0x804f +#define WCD934X_TEST_DEBUG_SYSMEM_CTRL 0x8050 +#define WCD934X_TEST_DEBUG_SOC_SW_PWR_SEQ_DELAY 0x8051 +#define WCD934X_TEST_DEBUG_LVAL_NOM_LOW 0x8052 +#define WCD934X_TEST_DEBUG_LVAL_NOM_HIGH 0x8053 +#define WCD934X_TEST_DEBUG_LVAL_SVS_SVS2_LOW 0x8054 +#define WCD934X_TEST_DEBUG_LVAL_SVS_SVS2_HIGH 0x8055 +#define WCD934X_TEST_DEBUG_SPI_SLAVE_CHAR 0x8056 +#define WCD934X_TEST_DEBUG_CODEC_DIAGS 0x8057 +#define WCD934X_MAX_REGISTER 0x80FF + +/* SLIMBUS Slave Registers */ +#define WCD934X_SLIM_PGD_PORT_INT_RX_EN0 (0x30) +#define WCD934X_SLIM_PGD_PORT_INT_TX_EN0 (0x32) +#define WCD934X_SLIM_PGD_PORT_INT_STATUS_RX_0 (0x34) +#define WCD934X_SLIM_PGD_PORT_INT_STATUS_RX_1 (0x35) +#define WCD934X_SLIM_PGD_PORT_INT_STATUS_TX_0 (0x36) +#define WCD934X_SLIM_PGD_PORT_INT_STATUS_TX_1 (0x37) +#define WCD934X_SLIM_PGD_PORT_INT_CLR_RX_0 (0x38) +#define WCD934X_SLIM_PGD_PORT_INT_CLR_RX_1 (0x39) +#define WCD934X_SLIM_PGD_PORT_INT_CLR_TX_0 (0x3A) +#define WCD934X_SLIM_PGD_PORT_INT_CLR_TX_1 (0x3B) +#define WCD934X_SLIM_PGD_PORT_INT_RX_SOURCE0 (0x60) +#define WCD934X_SLIM_PGD_PORT_INT_TX_SOURCE0 (0x70) + +#endif diff --git a/audio-kernel/include/asoc/wcd9360-registers.h b/audio-kernel/include/asoc/wcd9360-registers.h new file mode 100644 index 000000000..bca9de301 --- /dev/null +++ b/audio-kernel/include/asoc/wcd9360-registers.h @@ -0,0 +1,1158 @@ +/* + * 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 _WCD9360_REGISTERS_H +#define _WCD9360_REGISTERS_H + +#define WCD9360_PAGE_SIZE 256 +#define WCD9360_NUM_PAGES 256 + +enum { + WCD9360_PAGE_0 = 0, + WCD9360_PAGE_1, + WCD9360_PAGE_2, + WCD9360_PAGE_4 = 4, + WCD9360_PAGE_6 = 6, + WCD9360_PAGE_7, + WCD9360_PAGE_10 = 10, + WCD9360_PAGE_11, + WCD9360_PAGE_12, + WCD9360_PAGE_13, + WCD9360_PAGE_14, + WCD9360_PAGE_80, + WCD9360_PAGE_128, + WCD9360_PAGE_MAX, +}; + +enum { + WCD9360_WO = 0, + WCD9360_RO, + WCD9360_RW, +}; + +extern const u8 * const wcd9360_reg[WCD9360_PAGE_MAX]; + +/* Page-0 Registers */ +#define WCD9360_PAGE0_PAGE_REGISTER (0x00000000) +#define WCD9360_CODEC_RPM_CLK_BYPASS (0x00000001) +#define WCD9360_CODEC_RPM_CLK_GATE (0x00000002) +#define WCD9360_CODEC_RPM_CLK_MCLK_CFG (0x00000003) +#define WCD9360_CODEC_RPM_CLK_MCLK2_CFG (0x00000004) +#define WCD9360_CODEC_RPM_I2S_DSD_CLK_SEL (0x00000005) +#define WCD9360_CODEC_RPM_RST_CTL (0x00000009) +#define WCD9360_CODEC_RPM_PWR_CDC_DIG_HM_CTL (0x00000011) +#define WCD9360_CHIP_TIER_CTRL_CHIP_ID_BYTE0 (0x00000021) +#define WCD9360_CHIP_TIER_CTRL_CHIP_ID_BYTE1 (0x00000022) +#define WCD9360_CHIP_TIER_CTRL_CHIP_ID_BYTE2 (0x00000023) +#define WCD9360_CHIP_TIER_CTRL_CHIP_ID_BYTE3 (0x00000024) +#define WCD9360_CHIP_TIER_CTRL_EFUSE_CTL (0x00000025) +#define WCD9360_CHIP_TIER_CTRL_EFUSE_TEST0 (0x00000026) +#define WCD9360_CHIP_TIER_CTRL_EFUSE_TEST1 (0x00000027) +#define WCD9360_CHIP_TIER_CTRL_EFUSE_STATUS (0x00000039) +#define WCD9360_CHIP_TIER_CTRL_I2C_SLAVE_ID_NONNEGO (0x0000003A) +#define WCD9360_CHIP_TIER_CTRL_I2C_SLAVE_ID_1 (0x0000003B) +#define WCD9360_CHIP_TIER_CTRL_I2C_SLAVE_ID_2 (0x0000003C) +#define WCD9360_CHIP_TIER_CTRL_I2C_SLAVE_ID_3 (0x0000003D) +#define WCD9360_CHIP_TIER_CTRL_ANA_WAIT_STATE_CTL (0x0000003E) +#define WCD9360_CHIP_TIER_CTRL_I2C_ACTIVE (0x00000040) +#define WCD9360_CHIP_TIER_CTRL_ALT_FUNC_EN (0x00000041) +#define WCD9360_CHIP_TIER_CTRL_GPIO_CTL_OE (0x00000042) +#define WCD9360_CHIP_TIER_CTRL_GPIO_CTL_DATA (0x00000043) +#define WCD9360_DATA_HUB_RX0_CFG (0x00000051) +#define WCD9360_DATA_HUB_RX1_CFG (0x00000052) +#define WCD9360_DATA_HUB_RX2_CFG (0x00000053) +#define WCD9360_DATA_HUB_RX3_CFG (0x00000054) +#define WCD9360_DATA_HUB_RX4_CFG (0x00000055) +#define WCD9360_DATA_HUB_RX5_CFG (0x00000056) +#define WCD9360_DATA_HUB_RX6_CFG (0x00000057) +#define WCD9360_DATA_HUB_RX7_CFG (0x00000058) +#define WCD9360_DATA_HUB_SB_TX0_INP_CFG (0x00000061) +#define WCD9360_DATA_HUB_SB_TX1_INP_CFG (0x00000062) +#define WCD9360_DATA_HUB_SB_TX2_INP_CFG (0x00000063) +#define WCD9360_DATA_HUB_SB_TX3_INP_CFG (0x00000064) +#define WCD9360_DATA_HUB_SB_TX4_INP_CFG (0x00000065) +#define WCD9360_DATA_HUB_SB_TX5_INP_CFG (0x00000066) +#define WCD9360_DATA_HUB_SB_TX6_INP_CFG (0x00000067) +#define WCD9360_DATA_HUB_SB_TX7_INP_CFG (0x00000068) +#define WCD9360_DATA_HUB_SB_TX8_INP_CFG (0x00000069) +#define WCD9360_DATA_HUB_SB_TX9_INP_CFG (0x0000006A) +#define WCD9360_DATA_HUB_SB_TX10_INP_CFG (0x0000006B) +#define WCD9360_DATA_HUB_SB_TX11_INP_CFG (0x0000006C) +#define WCD9360_DATA_HUB_SB_TX12_INP_CFG (0x0000006D) +#define WCD9360_DATA_HUB_SB_TX13_INP_CFG (0x0000006E) +#define WCD9360_DATA_HUB_SB_TX14_INP_CFG (0x0000006F) +#define WCD9360_DATA_HUB_SB_TX15_INP_CFG (0x00000070) +#define WCD9360_DATA_HUB_I2S_TX0_CFG (0x00000071) +#define WCD9360_DATA_HUB_I2S_TX0_CFG2 (0x00000072) +#define WCD9360_DATA_HUB_I2S_TX1_0_CFG (0x00000073) +#define WCD9360_DATA_HUB_I2S_TX1_1_CFG (0x00000074) +#define WCD9360_DATA_HUB_DATA_HUB_CFG (0x00000079) +#define WCD9360_DATA_HUB_I2S_0_CTL (0x00000081) +#define WCD9360_DATA_HUB_I2S_1_CTL (0x00000082) +#define WCD9360_DATA_HUB_I2S_0_CTL2 (0x00000083) +#define WCD9360_DATA_HUB_I2S_1_CTL2 (0x00000084) +#define WCD9360_DATA_HUB_I2S_CLKSRC_CTL (0x00000085) +#define WCD9360_DATA_HUB_I2S_COMMON_CTL (0x00000086) +#define WCD9360_DATA_HUB_I2S_0_TDM_CTL (0x00000087) +#define WCD9360_DATA_HUB_I2S_0_TDM_CTL2 (0x00000089) +#define WCD9360_DATA_HUB_I2S_0_TDM_CH_RX (0x0000008A) +#define WCD9360_DATA_HUB_I2S_0_TDM_CH_TX (0x0000008B) +#define WCD9360_DATA_HUB_I2S_0_TDM_CFG (0x0000008C) +#define WCD9360_DATA_HUB_I2S_0_TDM_STRETCH (0x0000008D) +#define WCD9360_DATA_HUB_I2S_RESET_CTL (0x00000090) +#define WCD9360_DMA_RDMA_CTL_0 (0x00000091) +#define WCD9360_DMA_CH_2_3_CFG_RDMA_0 (0x00000092) +#define WCD9360_DMA_CH_0_1_CFG_RDMA_0 (0x00000093) +#define WCD9360_DMA_RDMA_CTL_1 (0x00000094) +#define WCD9360_DMA_CH_2_3_CFG_RDMA_1 (0x00000095) +#define WCD9360_DMA_CH_0_1_CFG_RDMA_1 (0x00000096) +#define WCD9360_DMA_RDMA_CTL_2 (0x00000097) +#define WCD9360_DMA_CH_2_3_CFG_RDMA_2 (0x00000098) +#define WCD9360_DMA_CH_0_1_CFG_RDMA_2 (0x00000099) +#define WCD9360_DMA_RDMA_CTL_3 (0x0000009A) +#define WCD9360_DMA_CH_2_3_CFG_RDMA_3 (0x0000009B) +#define WCD9360_DMA_CH_0_1_CFG_RDMA_3 (0x0000009C) +#define WCD9360_DMA_RDMA_CTL_4 (0x0000009D) +#define WCD9360_DMA_CH_2_3_CFG_RDMA_4 (0x0000009E) +#define WCD9360_DMA_CH_0_1_CFG_RDMA_4 (0x0000009F) +#define WCD9360_DMA_RDMA4_PRT_CFG (0x000000B1) +#define WCD9360_DMA_RDMA_SBTX0_7_CFG (0x000000B9) +#define WCD9360_DMA_RDMA_SBTX8_10_CFG (0x000000BA) +#define WCD9360_DMA_WDMA_CTL_0 (0x000000C1) +#define WCD9360_DMA_WDMA_CTL_1 (0x000000C6) +#define WCD9360_DMA_WDMA_CTL_2 (0x000000CB) +#define WCD9360_DMA_WDMA_CTL_3 (0x000000D0) +#define WCD9360_DMA_WDMA_CTL_4 (0x000000D5) +#define WCD9360_DMA_CH_4_5_CFG_WDMA_0 (0x000000C2) +#define WCD9360_DMA_CH_4_5_CFG_WDMA_1 (0x000000C7) +#define WCD9360_DMA_CH_4_5_CFG_WDMA_2 (0x000000CC) +#define WCD9360_DMA_CH_4_5_CFG_WDMA_3 (0x000000D1) +#define WCD9360_DMA_CH_4_5_CFG_WDMA_4 (0x000000D6) +#define WCD9360_DMA_CH_2_3_CFG_WDMA_0 (0x000000C3) +#define WCD9360_DMA_CH_2_3_CFG_WDMA_1 (0x000000C8) +#define WCD9360_DMA_CH_2_3_CFG_WDMA_2 (0x000000CD) +#define WCD9360_DMA_CH_2_3_CFG_WDMA_3 (0x000000D2) +#define WCD9360_DMA_CH_2_3_CFG_WDMA_4 (0x000000D7) +#define WCD9360_DMA_CH_0_1_CFG_WDMA_0 (0x000000C4) +#define WCD9360_DMA_CH_0_1_CFG_WDMA_1 (0x000000C9) +#define WCD9360_DMA_CH_0_1_CFG_WDMA_2 (0x000000CE) +#define WCD9360_DMA_CH_0_1_CFG_WDMA_3 (0x000000D3) +#define WCD9360_DMA_CH_0_1_CFG_WDMA_4 (0x000000D8) +#define WCD9360_DMA_WDMA0_PRT_CFG (0x000000E1) +#define WCD9360_DMA_WDMA3_PRT_CFG (0x000000E2) +#define WCD9360_DMA_WDMA4_PRT0_3_CFG (0x000000E3) +#define WCD9360_DMA_WDMA4_PRT4_7_CFG (0x000000E4) +#define WCD9360_PAGE1_PAGE_REGISTER (0x00000100) +#define WCD9360_CPE_FLL_USER_CTL_0 (0x00000101) +#define WCD9360_CPE_FLL_USER_CTL_1 (0x00000102) +#define WCD9360_CPE_FLL_USER_CTL_2 (0x00000103) +#define WCD9360_CPE_FLL_USER_CTL_3 (0x00000104) +#define WCD9360_CPE_FLL_USER_CTL_4 (0x00000105) +#define WCD9360_CPE_FLL_USER_CTL_5 (0x00000106) +#define WCD9360_CPE_FLL_USER_CTL_6 (0x00000107) +#define WCD9360_CPE_FLL_USER_CTL_7 (0x00000108) +#define WCD9360_CPE_FLL_USER_CTL_8 (0x00000109) +#define WCD9360_CPE_FLL_USER_CTL_9 (0x0000010A) +#define WCD9360_CPE_FLL_L_VAL_CTL_0 (0x0000010B) +#define WCD9360_CPE_FLL_L_VAL_CTL_1 (0x0000010C) +#define WCD9360_CPE_FLL_DSM_FRAC_CTL_0 (0x0000010D) +#define WCD9360_CPE_FLL_DSM_FRAC_CTL_1 (0x0000010E) +#define WCD9360_CPE_FLL_CONFIG_CTL_0 (0x0000010F) +#define WCD9360_CPE_FLL_CONFIG_CTL_1 (0x00000110) +#define WCD9360_CPE_FLL_CONFIG_CTL_2 (0x00000111) +#define WCD9360_CPE_FLL_CONFIG_CTL_3 (0x00000112) +#define WCD9360_CPE_FLL_CONFIG_CTL_4 (0x00000113) +#define WCD9360_CPE_FLL_TEST_CTL_0 (0x00000114) +#define WCD9360_CPE_FLL_TEST_CTL_1 (0x00000115) +#define WCD9360_CPE_FLL_TEST_CTL_2 (0x00000116) +#define WCD9360_CPE_FLL_TEST_CTL_3 (0x00000117) +#define WCD9360_CPE_FLL_TEST_CTL_4 (0x00000118) +#define WCD9360_CPE_FLL_TEST_CTL_5 (0x00000119) +#define WCD9360_CPE_FLL_TEST_CTL_6 (0x0000011A) +#define WCD9360_CPE_FLL_TEST_CTL_7 (0x0000011B) +#define WCD9360_CPE_FLL_FREQ_CTL_0 (0x0000011C) +#define WCD9360_CPE_FLL_FREQ_CTL_1 (0x0000011D) +#define WCD9360_CPE_FLL_FREQ_CTL_2 (0x0000011E) +#define WCD9360_CPE_FLL_FREQ_CTL_3 (0x0000011F) +#define WCD9360_CPE_FLL_SSC_CTL_0 (0x00000120) +#define WCD9360_CPE_FLL_SSC_CTL_1 (0x00000121) +#define WCD9360_CPE_FLL_SSC_CTL_2 (0x00000122) +#define WCD9360_CPE_FLL_SSC_CTL_3 (0x00000123) +#define WCD9360_CPE_FLL_FLL_MODE (0x00000124) +#define WCD9360_CPE_FLL_STATUS_0 (0x00000125) +#define WCD9360_CPE_FLL_STATUS_1 (0x00000126) +#define WCD9360_CPE_FLL_STATUS_2 (0x00000127) +#define WCD9360_CPE_FLL_STATUS_3 (0x00000128) +#define WCD9360_I2S_FLL_USER_CTL_0 (0x00000141) +#define WCD9360_I2S_FLL_USER_CTL_1 (0x00000142) +#define WCD9360_I2S_FLL_USER_CTL_2 (0x00000143) +#define WCD9360_I2S_FLL_USER_CTL_3 (0x00000144) +#define WCD9360_I2S_FLL_USER_CTL_4 (0x00000145) +#define WCD9360_I2S_FLL_USER_CTL_5 (0x00000146) +#define WCD9360_I2S_FLL_USER_CTL_6 (0x00000147) +#define WCD9360_I2S_FLL_USER_CTL_7 (0x00000148) +#define WCD9360_I2S_FLL_USER_CTL_8 (0x00000149) +#define WCD9360_I2S_FLL_USER_CTL_9 (0x0000014A) +#define WCD9360_I2S_FLL_L_VAL_CTL_0 (0x0000014B) +#define WCD9360_I2S_FLL_L_VAL_CTL_1 (0x0000014C) +#define WCD9360_I2S_FLL_DSM_FRAC_CTL_0 (0x0000014D) +#define WCD9360_I2S_FLL_DSM_FRAC_CTL_1 (0x0000014E) +#define WCD9360_I2S_FLL_CONFIG_CTL_0 (0x0000014F) +#define WCD9360_I2S_FLL_CONFIG_CTL_1 (0x00000150) +#define WCD9360_I2S_FLL_CONFIG_CTL_2 (0x00000151) +#define WCD9360_I2S_FLL_CONFIG_CTL_3 (0x00000152) +#define WCD9360_I2S_FLL_CONFIG_CTL_4 (0x00000153) +#define WCD9360_I2S_FLL_TEST_CTL_0 (0x00000154) +#define WCD9360_I2S_FLL_TEST_CTL_1 (0x00000155) +#define WCD9360_I2S_FLL_TEST_CTL_2 (0x00000156) +#define WCD9360_I2S_FLL_TEST_CTL_3 (0x00000157) +#define WCD9360_I2S_FLL_TEST_CTL_4 (0x00000158) +#define WCD9360_I2S_FLL_TEST_CTL_5 (0x00000159) +#define WCD9360_I2S_FLL_TEST_CTL_6 (0x0000015A) +#define WCD9360_I2S_FLL_TEST_CTL_7 (0x0000015B) +#define WCD9360_I2S_FLL_FREQ_CTL_0 (0x0000015C) +#define WCD9360_I2S_FLL_FREQ_CTL_1 (0x0000015D) +#define WCD9360_I2S_FLL_FREQ_CTL_2 (0x0000015E) +#define WCD9360_I2S_FLL_FREQ_CTL_3 (0x0000015F) +#define WCD9360_I2S_FLL_SSC_CTL_0 (0x00000160) +#define WCD9360_I2S_FLL_SSC_CTL_1 (0x00000161) +#define WCD9360_I2S_FLL_SSC_CTL_2 (0x00000162) +#define WCD9360_I2S_FLL_SSC_CTL_3 (0x00000163) +#define WCD9360_I2S_FLL_FLL_MODE (0x00000164) +#define WCD9360_I2S_FLL_STATUS_0 (0x00000165) +#define WCD9360_I2S_FLL_STATUS_1 (0x00000166) +#define WCD9360_I2S_FLL_STATUS_2 (0x00000167) +#define WCD9360_I2S_FLL_STATUS_3 (0x00000168) +#define WCD9360_PAGE2_PAGE_REGISTER (0x00000200) +#define WCD9360_CPE_SS_CPE_CTL (0x00000201) +#define WCD9360_CPE_SS_PWR_SYS_PSTATE_CTL_0 (0x00000202) +#define WCD9360_CPE_SS_PWR_SYS_PSTATE_CTL_1 (0x00000203) +#define WCD9360_CPE_SS_PWR_CPEFLL_CTL (0x00000204) +#define WCD9360_CPE_SS_PWR_CPE_SYSMEM_DEEPSLP_0 (0x00000205) +#define WCD9360_CPE_SS_PWR_CPE_SYSMEM_DEEPSLP_1 (0x00000206) +#define WCD9360_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_0 (0x00000208) +#define WCD9360_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_1 (0x00000209) +#define WCD9360_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_2 (0x0000020A) +#define WCD9360_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_3 (0x0000020B) +#define WCD9360_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_4 (0x0000020C) +#define WCD9360_CPE_SS_PWR_CPE_DRAM1_SHUTDOWN (0x0000020E) +#define WCD9360_CPE_SS_US_BUF_INT_PERIOD (0x00000212) +#define WCD9360_CPE_SS_CPARMAD_BUFRDY_INT_PERIOD (0x00000213) +#define WCD9360_CPE_SS_SVA_CFG (0x00000214) +#define WCD9360_CPE_SS_US_CFG (0x00000215) +#define WCD9360_CPE_SS_MAD_CTL (0x00000216) +#define WCD9360_CPE_SS_CPAR_CTL (0x00000217) +#define WCD9360_CPE_SS_DMIC0_CTL (0x00000218) +#define WCD9360_CPE_SS_DMIC1_CTL (0x00000219) +#define WCD9360_CPE_SS_DMIC2_CTL (0x0000021A) +#define WCD9360_CPE_SS_DMIC_CFG (0x0000021B) +#define WCD9360_CPE_SS_CPAR_CFG (0x0000021C) +#define WCD9360_CPE_SS_WDOG_CFG (0x0000021D) +#define WCD9360_CPE_SS_BACKUP_INT (0x0000021E) +#define WCD9360_CPE_SS_STATUS (0x0000021F) +#define WCD9360_CPE_SS_CPE_OCD_CFG (0x00000220) +#define WCD9360_CPE_SS_SS_ERROR_INT_MASK_0A (0x00000221) +#define WCD9360_CPE_SS_SS_ERROR_INT_MASK_0B (0x00000222) +#define WCD9360_CPE_SS_SS_ERROR_INT_MASK_1A (0x00000223) +#define WCD9360_CPE_SS_SS_ERROR_INT_MASK_1B (0x00000224) +#define WCD9360_CPE_SS_SS_ERROR_INT_STATUS_0A (0x00000225) +#define WCD9360_CPE_SS_SS_ERROR_INT_STATUS_0B (0x00000226) +#define WCD9360_CPE_SS_SS_ERROR_INT_STATUS_1A (0x00000227) +#define WCD9360_CPE_SS_SS_ERROR_INT_STATUS_1B (0x00000228) +#define WCD9360_CPE_SS_SS_ERROR_INT_CLEAR_0A (0x00000229) +#define WCD9360_CPE_SS_SS_ERROR_INT_CLEAR_0B (0x0000022A) +#define WCD9360_CPE_SS_SS_ERROR_INT_CLEAR_1A (0x0000022B) +#define WCD9360_CPE_SS_SS_ERROR_INT_CLEAR_1B (0x0000022C) +#define WCD9360_CPE_SS_DMIC3_CTL (0x00000231) +#define WCD9360_CPE_SS_WDOG_RESET (0x00000239) +#define WCD9360_CPE_SS_LPASS_MCLK_PRG (0x00000240) +#define WCD9360_CPE_SS_LPASS_IPC_IN_0 (0x00000241) +#define WCD9360_CPE_SS_LPASS_IPC_IN_1 (0x00000242) +#define WCD9360_CPE_SS_LPASS_IPC_IN_2 (0x00000243) +#define WCD9360_CPE_SS_LPASS_IPC_IN_3 (0x00000244) +#define WCD9360_CPE_SS_LPASS_IPC_IN_4 (0x00000245) +#define WCD9360_CPE_SS_LPASS_IPC_IN_5 (0x00000246) +#define WCD9360_CPE_SS_LPASS_IPC_IN_6 (0x00000247) +#define WCD9360_CPE_SS_LPASS_IPC_IN_7 (0x00000248) +#define WCD9360_CPE_SS_LPASS_IPC_IN_8 (0x00000249) +#define WCD9360_CPE_SS_LPASS_IPC_IN_9 (0x0000024A) +#define WCD9360_CPE_SS_LPASS_IPC_IN_10 (0x0000024B) +#define WCD9360_CPE_SS_LPASS_IPC_IN_11 (0x0000024C) +#define WCD9360_CPE_SS_LPASS_IPC_IN_12 (0x0000024D) +#define WCD9360_CPE_SS_LPASS_IPC_IN_13 (0x0000024E) +#define WCD9360_CPE_SS_LPASS_IPC_IN_14 (0x0000024F) +#define WCD9360_CPE_SS_LPASS_IPC_IN_15 (0x00000250) +#define WCD9360_CPE_SS_LPASS_IPC_OUT_0 (0x00000251) +#define WCD9360_CPE_SS_LPASS_IPC_OUT_1 (0x00000252) +#define WCD9360_CPE_SS_LPASS_IPC_OUT_2 (0x00000253) +#define WCD9360_CPE_SS_LPASS_IPC_OUT_3 (0x00000254) +#define WCD9360_CPE_SS_LPASS_IPC_OUT_4 (0x00000255) +#define WCD9360_CPE_SS_LPASS_IPC_OUT_5 (0x00000256) +#define WCD9360_CPE_SS_LPASS_IPC_OUT_6 (0x00000257) +#define WCD9360_CPE_SS_LPASS_IPC_OUT_7 (0x00000258) +#define WCD9360_CPE_SS_LPASS_IPC_OUT_8 (0x00000259) +#define WCD9360_CPE_SS_LPASS_IPC_OUT_9 (0x0000025A) +#define WCD9360_CPE_SS_LPASS_IPC_OUT_10 (0x0000025B) +#define WCD9360_CPE_SS_LPASS_IPC_OUT_11 (0x0000025C) +#define WCD9360_CPE_SS_LPASS_IPC_OUT_12 (0x0000025D) +#define WCD9360_CPE_SS_LPASS_IPC_OUT_13 (0x0000025E) +#define WCD9360_CPE_SS_LPASS_IPC_OUT_14 (0x0000025F) +#define WCD9360_CPE_SS_LPASS_IPC_OUT_15 (0x00000260) +#define WCD9360_CPE_SS_LPASS_ARB_CTL (0x00000261) +#define WCD9360_CPE_SS_MEM_DEEPSLEEP_RD_0 (0x00000271) +#define WCD9360_CPE_SS_MEM_DEEPSLEEP_RD_1 (0x00000272) +#define WCD9360_CPE_SS_MEM_DEEPSLEEP_BYPASS_0 (0x00000273) +#define WCD9360_CPE_SS_MEM_DEEPSLEEP_BYPASS_1 (0x00000274) +#define WCD9360_SOC_MAD_MAIN_CTL_1 (0x00000281) +#define WCD9360_SOC_MAD_MAIN_CTL_2 (0x00000282) +#define WCD9360_SOC_MAD_AUDIO_CTL_1 (0x00000283) +#define WCD9360_SOC_MAD_AUDIO_CTL_2 (0x00000284) +#define WCD9360_SOC_MAD_AUDIO_CTL_3 (0x00000285) +#define WCD9360_SOC_MAD_AUDIO_CTL_4 (0x00000286) +#define WCD9360_SOC_MAD_AUDIO_CTL_5 (0x00000287) +#define WCD9360_SOC_MAD_AUDIO_CTL_6 (0x00000288) +#define WCD9360_SOC_MAD_AUDIO_CTL_7 (0x00000289) +#define WCD9360_SOC_MAD_AUDIO_CTL_8 (0x0000028A) +#define WCD9360_SOC_MAD_AUDIO_IIR_CTL_PTR (0x0000028B) +#define WCD9360_SOC_MAD_AUDIO_IIR_CTL_VAL (0x0000028C) +#define WCD9360_SOC_MAD_ULTR_CTL_1 (0x0000028D) +#define WCD9360_SOC_MAD_ULTR_CTL_2 (0x0000028E) +#define WCD9360_SOC_MAD_ULTR_CTL_3 (0x0000028F) +#define WCD9360_SOC_MAD_ULTR_CTL_4 (0x00000290) +#define WCD9360_SOC_MAD_ULTR_CTL_5 (0x00000291) +#define WCD9360_SOC_MAD_ULTR_CTL_6 (0x00000292) +#define WCD9360_SOC_MAD_ULTR_CTL_7 (0x00000293) +#define WCD9360_SOC_MAD_BEACON_CTL_1 (0x00000294) +#define WCD9360_SOC_MAD_BEACON_CTL_2 (0x00000295) +#define WCD9360_SOC_MAD_BEACON_CTL_3 (0x00000296) +#define WCD9360_SOC_MAD_BEACON_CTL_4 (0x00000297) +#define WCD9360_SOC_MAD_BEACON_CTL_5 (0x00000298) +#define WCD9360_SOC_MAD_BEACON_CTL_6 (0x00000299) +#define WCD9360_SOC_MAD_BEACON_CTL_7 (0x0000029A) +#define WCD9360_SOC_MAD_BEACON_CTL_8 (0x0000029B) +#define WCD9360_SOC_MAD_BEACON_IIR_CTL_PTR (0x0000029C) +#define WCD9360_SOC_MAD_BEACON_IIR_CTL_VAL (0x0000029D) +#define WCD9360_SOC_MAD_INP_SEL (0x0000029E) +#define WCD9360_SOC_MAD_MAD2_INP_SEL (0x0000029F) +#define WCD9360_SWR_SAMPLE_PACK_SWR_SAMPLE_PACK_CTRL (0x000002B1) +#define WCD9360_SWR_SAMPLE_PACK_SWR_SAMPLE_PACK_STATUS (0x000002B2) +#define WCD9360_SWR_SAMPLE_PACK_SWR_SAMPLE_PACK_FS (0x000002B3) +#define WCD9360_SWR_SAMPLE_PACK_SWR_SAMPLE_PACK_IN_SEL (0x000002B4) +#define WCD9360_EFUSE_VALUE_EFUSE_VAL_OUT0 (0x000002C1) +#define WCD9360_EFUSE_VALUE_EFUSE_VAL_OUT1 (0x000002C2) +#define WCD9360_EFUSE_VALUE_EFUSE_VAL_OUT2 (0x000002C3) +#define WCD9360_EFUSE_VALUE_EFUSE_VAL_OUT3 (0x000002C4) +#define WCD9360_EFUSE_VALUE_EFUSE_VAL_OUT4 (0x000002C5) +#define WCD9360_EFUSE_VALUE_EFUSE_VAL_OUT5 (0x000002C6) +#define WCD9360_EFUSE_VALUE_EFUSE_VAL_OUT6 (0x000002C7) +#define WCD9360_EFUSE_VALUE_EFUSE_VAL_OUT7 (0x000002C8) +#define WCD9360_EFUSE_VALUE_EFUSE_VAL_OUT8 (0x000002C9) +#define WCD9360_EFUSE_VALUE_EFUSE_VAL_OUT9 (0x000002CA) +#define WCD9360_EFUSE_VALUE_EFUSE_VAL_OUT10 (0x000002CB) +#define WCD9360_EFUSE_VALUE_EFUSE_VAL_OUT11 (0x000002CC) +#define WCD9360_EFUSE_VALUE_EFUSE_VAL_OUT12 (0x000002CD) +#define WCD9360_EFUSE_VALUE_EFUSE_VAL_OUT13 (0x000002CE) +#define WCD9360_EFUSE_VALUE_EFUSE_VAL_OUT14 (0x000002CF) +#define WCD9360_EFUSE_VALUE_EFUSE_VAL_OUT15 (0x000002D0) +#define WCD9360_EFUSE_VALUE_EFUSE2_VAL_OUT0 (0x000002D1) +#define WCD9360_EFUSE_VALUE_EFUSE2_VAL_OUT1 (0x000002D2) +#define WCD9360_EFUSE_VALUE_EFUSE2_VAL_OUT2 (0x000002D3) +#define WCD9360_EFUSE_VALUE_EFUSE2_VAL_OUT3 (0x000002D4) +#define WCD9360_EFUSE_VALUE_EFUSE2_VAL_OUT4 (0x000002D5) +#define WCD9360_EFUSE_VALUE_EFUSE2_VAL_OUT5 (0x000002D6) +#define WCD9360_EFUSE_VALUE_EFUSE2_VAL_OUT6 (0x000002D7) +#define WCD9360_EFUSE_VALUE_EFUSE2_VAL_OUT7 (0x000002D8) +#define WCD9360_EFUSE_VALUE_EFUSE2_VAL_OUT8 (0x000002D9) +#define WCD9360_EFUSE_VALUE_EFUSE2_VAL_OUT9 (0x000002DA) +#define WCD9360_EFUSE_VALUE_EFUSE2_VAL_OUT10 (0x000002DB) +#define WCD9360_EFUSE_VALUE_EFUSE2_VAL_OUT11 (0x000002DC) +#define WCD9360_EFUSE_VALUE_EFUSE2_VAL_OUT12 (0x000002DD) +#define WCD9360_EFUSE_VALUE_EFUSE2_VAL_OUT13 (0x000002DE) +#define WCD9360_EFUSE_VALUE_EFUSE2_VAL_OUT14 (0x000002DF) +#define WCD9360_EFUSE_VALUE_EFUSE2_VAL_OUT15 (0x000002E0) +#define WCD9360_PAGE4_PAGE_REGISTER (0x00000400) +#define WCD9360_INTR_CFG (0x00000401) +#define WCD9360_INTR_CLR_COMMIT (0x00000402) +#define WCD9360_INTR_PIN1_MASK0 (0x00000409) +#define WCD9360_INTR_PIN1_MASK1 (0x0000040A) +#define WCD9360_INTR_PIN1_MASK2 (0x0000040B) +#define WCD9360_INTR_PIN1_MASK3 (0x0000040C) +#define WCD9360_INTR_PIN1_STATUS0 (0x00000411) +#define WCD9360_INTR_PIN1_STATUS1 (0x00000412) +#define WCD9360_INTR_PIN1_STATUS2 (0x00000413) +#define WCD9360_INTR_PIN1_STATUS3 (0x00000414) +#define WCD9360_INTR_PIN1_CLEAR0 (0x00000419) +#define WCD9360_INTR_PIN1_CLEAR1 (0x0000041A) +#define WCD9360_INTR_PIN1_CLEAR2 (0x0000041B) +#define WCD9360_INTR_PIN1_CLEAR3 (0x0000041C) +#define WCD9360_INTR_PIN2_MASK3 (0x00000424) +#define WCD9360_INTR_PIN2_STATUS3 (0x0000042C) +#define WCD9360_INTR_PIN2_CLEAR3 (0x00000434) +#define WCD9360_INTR_CPESS_SUMRY_MASK2 (0x0000043B) +#define WCD9360_INTR_CPESS_SUMRY_MASK3 (0x0000043C) +#define WCD9360_INTR_CPESS_SUMRY_STATUS2 (0x00000443) +#define WCD9360_INTR_CPESS_SUMRY_STATUS3 (0x00000444) +#define WCD9360_INTR_CPESS_SUMRY_CLEAR2 (0x0000044B) +#define WCD9360_INTR_CPESS_SUMRY_CLEAR3 (0x0000044C) +#define WCD9360_INTR_LEVEL0 (0x00000461) +#define WCD9360_INTR_LEVEL1 (0x00000462) +#define WCD9360_INTR_LEVEL2 (0x00000463) +#define WCD9360_INTR_LEVEL3 (0x00000464) +#define WCD9360_INTR_BYPASS0 (0x00000469) +#define WCD9360_INTR_BYPASS1 (0x0000046A) +#define WCD9360_INTR_BYPASS2 (0x0000046B) +#define WCD9360_INTR_BYPASS3 (0x0000046C) +#define WCD9360_INTR_SET0 (0x00000471) +#define WCD9360_INTR_SET1 (0x00000472) +#define WCD9360_INTR_SET2 (0x00000473) +#define WCD9360_INTR_SET3 (0x00000474) +#define WCD9360_INTR_CODEC_MISC_MASK (0x000004B1) +#define WCD9360_INTR_CODEC_MISC_STATUS (0x000004B2) +#define WCD9360_INTR_CODEC_MISC_CLEAR (0x000004B3) +#define WCD9360_ANA_PAGE_REGISTER (0x00000600) +#define WCD9360_ANA_BIAS (0x00000601) +#define WCD9360_ANA_AMIC_INPUT_SWITCH_CTL (0x00000602) +#define WCD9360_ANA_RCO (0x00000603) +#define WCD9360_ANA_BUCK_CTL (0x00000606) +#define WCD9360_ANA_BUCK_STATUS (0x00000607) +#define WCD9360_ANA_EAR (0x0000060A) +#define WCD9360_ANA_MAD_SETUP (0x0000060D) +#define WCD9360_ANA_AMIC1 (0x0000060E) +#define WCD9360_ANA_AMIC2 (0x0000060F) +#define WCD9360_ANA_AMIC3 (0x00000610) +#define WCD9360_ANA_AMIC4 (0x00000611) +#define WCD9360_ANA_MICB1 (0x00000622) +#define WCD9360_ANA_MICB2 (0x00000623) +#define WCD9360_ANA_MICB3 (0x00000625) +#define WCD9360_ANA_MICB4 (0x00000626) +#define WCD9360_BIAS_CTL (0x00000628) +#define WCD9360_BIAS_VBG_FINE_ADJ (0x00000629) +#define WCD9360_RCO_CTRL_1 (0x0000062E) +#define WCD9360_RCO_CTRL_2 (0x0000062F) +#define WCD9360_RCO_CAL (0x00000630) +#define WCD9360_RCO_CAL_1 (0x00000631) +#define WCD9360_RCO_CAL_2 (0x00000632) +#define WCD9360_RCO_TEST_CTRL (0x00000633) +#define WCD9360_RCO_CAL_OUT_1 (0x00000634) +#define WCD9360_RCO_CAL_OUT_2 (0x00000635) +#define WCD9360_RCO_CAL_OUT_3 (0x00000636) +#define WCD9360_RCO_CAL_OUT_4 (0x00000637) +#define WCD9360_RCO_CAL_OUT_5 (0x00000638) +#define WCD9360_SIDO_MODE_1 (0x0000063A) +#define WCD9360_SIDO_MODE_2 (0x0000063B) +#define WCD9360_SIDO_MODE_3 (0x0000063C) +#define WCD9360_SIDO_MODE_4 (0x0000063D) +#define WCD9360_SIDO_VCL_1 (0x0000063E) +#define WCD9360_SIDO_VCL_2 (0x0000063F) +#define WCD9360_SIDO_VCL_3 (0x00000640) +#define WCD9360_SIDO_CCL_1 (0x00000641) +#define WCD9360_SIDO_CCL_2 (0x00000642) +#define WCD9360_SIDO_CCL_3 (0x00000643) +#define WCD9360_SIDO_CCL_4 (0x00000644) +#define WCD9360_SIDO_CCL_5 (0x00000645) +#define WCD9360_SIDO_CCL_6 (0x00000646) +#define WCD9360_SIDO_CCL_7 (0x00000647) +#define WCD9360_SIDO_CCL_8 (0x00000648) +#define WCD9360_SIDO_CCL_9 (0x00000649) +#define WCD9360_SIDO_CCL_10 (0x0000064A) +#define WCD9360_SIDO_FILTER_1 (0x0000064B) +#define WCD9360_SIDO_FILTER_2 (0x0000064C) +#define WCD9360_SIDO_DRIVER_1 (0x0000064D) +#define WCD9360_SIDO_DRIVER_2 (0x0000064E) +#define WCD9360_SIDO_DRIVER_3 (0x0000064F) +#define WCD9360_SIDO_CAL_CODE_EXT_1 (0x00000650) +#define WCD9360_SIDO_CAL_CODE_EXT_2 (0x00000651) +#define WCD9360_SIDO_CAL_CODE_OUT_1 (0x00000652) +#define WCD9360_SIDO_CAL_CODE_OUT_2 (0x00000653) +#define WCD9360_SIDO_TEST_1 (0x00000654) +#define WCD9360_SIDO_TEST_2 (0x00000655) +#define WCD9360_LDOH_MODE (0x00000667) +#define WCD9360_LDOH_BIAS (0x00000668) +#define WCD9360_LDOH_STB_LOADS (0x00000669) +#define WCD9360_LDOH_SLOWRAMP (0x0000066A) +#define WCD9360_MICB1_TEST_CTL_1 (0x0000066B) +#define WCD9360_MICB1_TEST_CTL_2 (0x0000066C) +#define WCD9360_MICB1_TEST_CTL_3 (0x0000066D) +#define WCD9360_MICB2_TEST_CTL_1 (0x0000066E) +#define WCD9360_MICB2_TEST_CTL_2 (0x0000066F) +#define WCD9360_MICB2_TEST_CTL_3 (0x00000670) +#define WCD9360_MICB3_TEST_CTL_1 (0x00000671) +#define WCD9360_MICB3_TEST_CTL_2 (0x00000672) +#define WCD9360_MICB3_TEST_CTL_3 (0x00000673) +#define WCD9360_MICB4_TEST_CTL_1 (0x00000674) +#define WCD9360_MICB4_TEST_CTL_2 (0x00000675) +#define WCD9360_MICB4_TEST_CTL_3 (0x00000676) +#define WCD9360_TX_COM_ADC_VCM (0x00000677) +#define WCD9360_TX_COM_BIAS_ATEST (0x00000678) +#define WCD9360_TX_COM_ADC_INT1_IB (0x00000679) +#define WCD9360_TX_COM_ADC_INT2_IB (0x0000067A) +#define WCD9360_TX_COM_TXFE_DIV_CTL (0x0000067B) +#define WCD9360_TX_COM_TXFE_DIV_START (0x0000067C) +#define WCD9360_TX_COM_TXFE_DIV_STOP_9P6M (0x0000067D) +#define WCD9360_TX_COM_TXFE_DIV_STOP_12P288M (0x0000067E) +#define WCD9360_TX_1_2_TEST_EN (0x0000067F) +#define WCD9360_TX_1_2_ADC_IB (0x00000680) +#define WCD9360_TX_1_2_ATEST_REFCTL (0x00000681) +#define WCD9360_TX_1_2_TEST_CTL (0x00000682) +#define WCD9360_TX_1_2_TEST_BLK_EN (0x00000683) +#define WCD9360_TX_1_2_TXFE_CLKDIV (0x00000684) +#define WCD9360_TX_1_2_SAR1_ERR (0x00000685) +#define WCD9360_TX_1_2_SAR2_ERR (0x00000686) +#define WCD9360_TX_3_4_TEST_EN (0x00000687) +#define WCD9360_TX_3_4_ADC_IB (0x00000688) +#define WCD9360_TX_3_4_ATEST_REFCTL (0x00000689) +#define WCD9360_TX_3_4_TEST_CTL (0x0000068A) +#define WCD9360_TX_3_4_TEST_BLK_EN (0x0000068B) +#define WCD9360_TX_3_4_TXFE_CLKDIV (0x0000068C) +#define WCD9360_TX_3_4_SAR1_ERR (0x0000068D) +#define WCD9360_TX_3_4_SAR2_ERR (0x0000068E) +#define WCD9360_RX_RX_EAR_BIAS_CON_1 (0x000006B3) +#define WCD9360_RX_RX_EAR_BIAS_CON_2 (0x000006B4) +#define WCD9360_RX_RX_AUX_BIAS_CON_1 (0x000006B5) +#define WCD9360_RX_RX_AUX_BIAS_CON_2 (0x000006B6) +#define WCD9360_RX_RX_BIAS_ATEST (0x000006B7) +#define WCD9360_RX_RXTOP_RESERVED (0x000006B8) +#define WCD9360_EAR_EAR_EN_REG (0x000006E1) +#define WCD9360_EAR_EAR_PA_CON (0x000006E2) +#define WCD9360_EAR_EAR_SP_CON (0x000006E3) +#define WCD9360_EAR_EAR_DAC_CON (0x000006E4) +#define WCD9360_EAR_EAR_CNP_FSM_CON (0x000006E5) +#define WCD9360_EAR_DAC_CTL_TEST (0x000006E6) +#define WCD9360_EAR_STATUS_REG (0x000006E7) +#define WCD9360_EAR_EAR_COMPANDER_CON (0x000006E8) +#define WCD9360_ANA_NEW_PAGE_REGISTER (0x00000700) +#define WCD9360_CLK_SYS_PLL_ENABLES (0x0000070E) +#define WCD9360_CLK_SYS_PLL_PRESET (0x0000070F) +#define WCD9360_CLK_SYS_PLL_STATUS (0x00000710) +#define WCD9360_CLK_SYS_MCLK_PRG (0x00000711) +#define WCD9360_CLK_SYS_MCLK2_PRG1 (0x00000712) +#define WCD9360_CLK_SYS_MCLK_MISC (0x00000713) +#define WCD9360_SIDO_NEW_VOUT_A_STARTUP (0x0000071B) +#define WCD9360_SIDO_NEW_VOUT_D_STARTUP (0x0000071C) +#define WCD9360_SIDO_NEW_VOUT_D_FREQ1 (0x0000071D) +#define WCD9360_SIDO_NEW_VOUT_D_FREQ2 (0x0000071E) +#define WCD9360_AUX_ANA_EAR (0x00000728) +#define WCD9360_LDORXTX_LDORXTX (0x00000729) +#define WCD9360_DIE_CRACK_CTL (0x0000072A) +#define WCD9360_DIE_CRACK_OUT (0x0000072B) +#define WCD9360_LOOP_BACK_EN (0x0000072C) +#define WCD9360_CLK_SYS_INT_POST_DIV_REG0 (0x0000076C) +#define WCD9360_CLK_SYS_INT_POST_DIV_REG1 (0x0000076D) +#define WCD9360_CLK_SYS_INT_REF_DIV_REG0 (0x0000076E) +#define WCD9360_CLK_SYS_INT_REF_DIV_REG1 (0x0000076F) +#define WCD9360_CLK_SYS_INT_FILTER_REG0 (0x00000770) +#define WCD9360_CLK_SYS_INT_FILTER_REG1 (0x00000771) +#define WCD9360_CLK_SYS_INT_PLL_L_VAL (0x00000772) +#define WCD9360_CLK_SYS_INT_PLL_M_VAL (0x00000773) +#define WCD9360_CLK_SYS_INT_PLL_N_VAL (0x00000774) +#define WCD9360_CLK_SYS_INT_TEST_REG0 (0x00000775) +#define WCD9360_CLK_SYS_INT_PFD_CP_DSM_PROG (0x00000776) +#define WCD9360_CLK_SYS_INT_VCO_PROG (0x00000777) +#define WCD9360_CLK_SYS_INT_TEST_REG1 (0x00000778) +#define WCD9360_CLK_SYS_INT_LDO_LOCK_CFG (0x00000779) +#define WCD9360_CLK_SYS_INT_DIG_LOCK_DET_CFG (0x0000077A) +#define WCD9360_CLK_SYS_INT_CLK_TEST1 (0x0000077B) +#define WCD9360_CLK_SYS_INT_CLK_TEST2 (0x0000077C) +#define WCD9360_CLK_SYS_INT_CLK_TEST3 (0x0000077D) +#define WCD9360_SIDO_NEW_INT_RAMP_STATUS (0x00000796) +#define WCD9360_SIDO_NEW_INT_SPARE_1 (0x00000797) +#define WCD9360_SIDO_NEW_INT_DEBUG_VOUT_SETTING_A (0x00000798) +#define WCD9360_SIDO_NEW_INT_DEBUG_VOUT_SETTING_D (0x00000799) +#define WCD9360_SIDO_NEW_INT_RAMP_INC_WAIT (0x0000079A) +#define WCD9360_SIDO_NEW_INT_DYNAMIC_IPEAK_CTL (0x0000079B) +#define WCD9360_SIDO_NEW_INT_RAMP_IBLEED_CTL (0x0000079C) +#define WCD9360_SIDO_NEW_INT_DEBUG_CPROVR_TEST (0x0000079D) +#define WCD9360_SIDO_NEW_INT_RAMP_CTL_A (0x0000079E) +#define WCD9360_SIDO_NEW_INT_RAMP_CTL_D (0x0000079F) +#define WCD9360_SIDO_NEW_INT_RAMP_TIMEOUT_PERIOD (0x000007A0) +#define WCD9360_SIDO_NEW_INT_DYNAMIC_IPEAK_SETTING1 (0x000007A1) +#define WCD9360_SIDO_NEW_INT_DYNAMIC_IPEAK_SETTING2 (0x000007A2) +#define WCD9360_SIDO_NEW_INT_DYNAMIC_IPEAK_SETTING3 (0x000007A3) +#define WCD9360_SIDO_NEW_INT_HIGH_ACCU_MODE_SEL1 (0x000007A4) +#define WCD9360_SIDO_NEW_INT_HIGH_ACCU_MODE_SEL2 (0x000007A5) +#define WCD9360_EAR_INT_NEW_EAR_CHOPPER_CON (0x000007B7) +#define WCD9360_EAR_INT_NEW_EAR_VCM_GEN_CON1 (0x000007B8) +#define WCD9360_EAR_INT_NEW_EAR_VCM_GEN_CON2 (0x000007B9) +#define WCD9360_EAR_INT_NEW_EAR_DYNAMIC_BIAS (0x000007BA) +#define WCD9360_AUX_INT_AUX_EN_REG (0x000007BD) +#define WCD9360_AUX_INT_AUX_PA_CON (0x000007BE) +#define WCD9360_AUX_INT_AUX_SP_CON (0x000007BF) +#define WCD9360_AUX_INT_AUX_DAC_CON (0x000007C0) +#define WCD9360_AUX_INT_AUX_CNP_FSM_CON (0x000007C1) +#define WCD9360_AUX_INT_AUX_TEST (0x000007C2) +#define WCD9360_AUX_INT_STATUS_REG (0x000007C3) +#define WCD9360_AUX_INT_AUX_MISC (0x000007C4) +#define WCD9360_LDORXTX_INT_ANA_LDORXTX_CTRL1 (0x000007C5) +#define WCD9360_LDORXTX_INT_ANA_LDORXTX_CTRL2 (0x000007C6) +#define WCD9360_LDORXTX_INT_ANA_LDORXTX_CTRL3 (0x000007C7) +#define WCD9360_LDORXTX_INT_ANA_LDORXTX_CTRL4 (0x000007C8) +#define WCD9360_LDORXTX_INT_ANA_LDORXTX_CTRL5 (0x000007C9) +#define WCD9360_LDORXTX_INT_ANA_LDORXTX_STATUS (0x000007CA) +#define WCD9360_DIE_CRACK_INT_INT1 (0x000007CC) +#define WCD9360_DIE_CRACK_INT_INT2 (0x000007CD) +#define WCD9360_LOOP_BACK_INT_SPARE (0x000007CE) +#define WCD9360_PAGE10_PAGE_REGISTER (0x00000A00) +#define WCD9360_CDC_ANC0_CLK_RESET_CTL (0x00000A01) +#define WCD9360_CDC_ANC0_MODE_1_CTL (0x00000A02) +#define WCD9360_CDC_ANC0_MODE_2_CTL (0x00000A03) +#define WCD9360_CDC_ANC0_FF_SHIFT (0x00000A04) +#define WCD9360_CDC_ANC0_FB_SHIFT (0x00000A05) +#define WCD9360_CDC_ANC0_LPF_FF_A_CTL (0x00000A06) +#define WCD9360_CDC_ANC0_LPF_FF_B_CTL (0x00000A07) +#define WCD9360_CDC_ANC0_LPF_FB_CTL (0x00000A08) +#define WCD9360_CDC_ANC0_SMLPF_CTL (0x00000A09) +#define WCD9360_CDC_ANC0_DCFLT_SHIFT_CTL (0x00000A0A) +#define WCD9360_CDC_ANC0_IIR_ADAPT_CTL (0x00000A0B) +#define WCD9360_CDC_ANC0_IIR_COEFF_1_CTL (0x00000A0C) +#define WCD9360_CDC_ANC0_IIR_COEFF_2_CTL (0x00000A0D) +#define WCD9360_CDC_ANC0_FF_A_GAIN_CTL (0x00000A0E) +#define WCD9360_CDC_ANC0_FF_B_GAIN_CTL (0x00000A0F) +#define WCD9360_CDC_ANC0_FB_GAIN_CTL (0x00000A10) +#define WCD9360_CDC_TX0_TX_PATH_CTL (0x00000A31) +#define WCD9360_CDC_TX0_TX_PATH_CFG0 (0x00000A32) +#define WCD9360_CDC_TX0_TX_PATH_CFG1 (0x00000A33) +#define WCD9360_CDC_TX0_TX_VOL_CTL (0x00000A34) +#define WCD9360_CDC_TX0_TX_PATH_192_CTL (0x00000A35) +#define WCD9360_CDC_TX0_TX_PATH_192_CFG (0x00000A36) +#define WCD9360_CDC_TX0_TX_PATH_SEC0 (0x00000A37) +#define WCD9360_CDC_TX0_TX_PATH_SEC1 (0x00000A38) +#define WCD9360_CDC_TX0_TX_PATH_SEC2 (0x00000A39) +#define WCD9360_CDC_TX0_TX_PATH_SEC3 (0x00000A3A) +#define WCD9360_CDC_TX0_TX_PATH_SEC4 (0x00000A3B) +#define WCD9360_CDC_TX0_TX_PATH_SEC5 (0x00000A3C) +#define WCD9360_CDC_TX0_TX_PATH_SEC6 (0x00000A3D) +#define WCD9360_CDC_TX1_TX_PATH_CTL (0x00000A41) +#define WCD9360_CDC_TX1_TX_PATH_CFG0 (0x00000A42) +#define WCD9360_CDC_TX1_TX_PATH_CFG1 (0x00000A43) +#define WCD9360_CDC_TX1_TX_VOL_CTL (0x00000A44) +#define WCD9360_CDC_TX1_TX_PATH_192_CTL (0x00000A45) +#define WCD9360_CDC_TX1_TX_PATH_192_CFG (0x00000A46) +#define WCD9360_CDC_TX1_TX_PATH_SEC0 (0x00000A47) +#define WCD9360_CDC_TX1_TX_PATH_SEC1 (0x00000A48) +#define WCD9360_CDC_TX1_TX_PATH_SEC2 (0x00000A49) +#define WCD9360_CDC_TX1_TX_PATH_SEC3 (0x00000A4A) +#define WCD9360_CDC_TX1_TX_PATH_SEC4 (0x00000A4B) +#define WCD9360_CDC_TX1_TX_PATH_SEC5 (0x00000A4C) +#define WCD9360_CDC_TX1_TX_PATH_SEC6 (0x00000A4D) +#define WCD9360_CDC_TX2_TX_PATH_CTL (0x00000A51) +#define WCD9360_CDC_TX2_TX_PATH_CFG0 (0x00000A52) +#define WCD9360_CDC_TX2_TX_PATH_CFG1 (0x00000A53) +#define WCD9360_CDC_TX2_TX_VOL_CTL (0x00000A54) +#define WCD9360_CDC_TX2_TX_PATH_192_CTL (0x00000A55) +#define WCD9360_CDC_TX2_TX_PATH_192_CFG (0x00000A56) +#define WCD9360_CDC_TX2_TX_PATH_SEC0 (0x00000A57) +#define WCD9360_CDC_TX2_TX_PATH_SEC1 (0x00000A58) +#define WCD9360_CDC_TX2_TX_PATH_SEC2 (0x00000A59) +#define WCD9360_CDC_TX2_TX_PATH_SEC3 (0x00000A5A) +#define WCD9360_CDC_TX2_TX_PATH_SEC4 (0x00000A5B) +#define WCD9360_CDC_TX2_TX_PATH_SEC5 (0x00000A5C) +#define WCD9360_CDC_TX2_TX_PATH_SEC6 (0x00000A5D) +#define WCD9360_CDC_TX3_TX_PATH_CTL (0x00000A61) +#define WCD9360_CDC_TX3_TX_PATH_CFG0 (0x00000A62) +#define WCD9360_CDC_TX3_TX_PATH_CFG1 (0x00000A63) +#define WCD9360_CDC_TX3_TX_VOL_CTL (0x00000A64) +#define WCD9360_CDC_TX3_TX_PATH_192_CTL (0x00000A65) +#define WCD9360_CDC_TX3_TX_PATH_192_CFG (0x00000A66) +#define WCD9360_CDC_TX3_TX_PATH_SEC0 (0x00000A67) +#define WCD9360_CDC_TX3_TX_PATH_SEC1 (0x00000A68) +#define WCD9360_CDC_TX3_TX_PATH_SEC2 (0x00000A69) +#define WCD9360_CDC_TX3_TX_PATH_SEC3 (0x00000A6A) +#define WCD9360_CDC_TX3_TX_PATH_SEC4 (0x00000A6B) +#define WCD9360_CDC_TX3_TX_PATH_SEC5 (0x00000A6C) +#define WCD9360_CDC_TX3_TX_PATH_SEC6 (0x00000A6D) +#define WCD9360_CDC_TX4_TX_PATH_CTL (0x00000A71) +#define WCD9360_CDC_TX4_TX_PATH_CFG0 (0x00000A72) +#define WCD9360_CDC_TX4_TX_PATH_CFG1 (0x00000A73) +#define WCD9360_CDC_TX4_TX_VOL_CTL (0x00000A74) +#define WCD9360_CDC_TX4_TX_PATH_192_CTL (0x00000A75) +#define WCD9360_CDC_TX4_TX_PATH_192_CFG (0x00000A76) +#define WCD9360_CDC_TX4_TX_PATH_SEC0 (0x00000A77) +#define WCD9360_CDC_TX4_TX_PATH_SEC1 (0x00000A78) +#define WCD9360_CDC_TX4_TX_PATH_SEC2 (0x00000A79) +#define WCD9360_CDC_TX4_TX_PATH_SEC3 (0x00000A7A) +#define WCD9360_CDC_TX4_TX_PATH_SEC4 (0x00000A7B) +#define WCD9360_CDC_TX4_TX_PATH_SEC5 (0x00000A7C) +#define WCD9360_CDC_TX4_TX_PATH_SEC6 (0x00000A7D) +#define WCD9360_CDC_TX5_TX_PATH_CTL (0x00000A81) +#define WCD9360_CDC_TX5_TX_PATH_CFG0 (0x00000A82) +#define WCD9360_CDC_TX5_TX_PATH_CFG1 (0x00000A83) +#define WCD9360_CDC_TX5_TX_VOL_CTL (0x00000A84) +#define WCD9360_CDC_TX5_TX_PATH_192_CTL (0x00000A85) +#define WCD9360_CDC_TX5_TX_PATH_192_CFG (0x00000A86) +#define WCD9360_CDC_TX5_TX_PATH_SEC0 (0x00000A87) +#define WCD9360_CDC_TX5_TX_PATH_SEC1 (0x00000A88) +#define WCD9360_CDC_TX5_TX_PATH_SEC2 (0x00000A89) +#define WCD9360_CDC_TX5_TX_PATH_SEC3 (0x00000A8A) +#define WCD9360_CDC_TX5_TX_PATH_SEC4 (0x00000A8B) +#define WCD9360_CDC_TX5_TX_PATH_SEC5 (0x00000A8C) +#define WCD9360_CDC_TX5_TX_PATH_SEC6 (0x00000A8D) +#define WCD9360_CDC_TX6_TX_PATH_CTL (0x00000A91) +#define WCD9360_CDC_TX6_TX_PATH_CFG0 (0x00000A92) +#define WCD9360_CDC_TX6_TX_PATH_CFG1 (0x00000A93) +#define WCD9360_CDC_TX6_TX_VOL_CTL (0x00000A94) +#define WCD9360_CDC_TX6_TX_PATH_192_CTL (0x00000A95) +#define WCD9360_CDC_TX6_TX_PATH_192_CFG (0x00000A96) +#define WCD9360_CDC_TX6_TX_PATH_SEC0 (0x00000A97) +#define WCD9360_CDC_TX6_TX_PATH_SEC1 (0x00000A98) +#define WCD9360_CDC_TX6_TX_PATH_SEC2 (0x00000A99) +#define WCD9360_CDC_TX6_TX_PATH_SEC3 (0x00000A9A) +#define WCD9360_CDC_TX6_TX_PATH_SEC4 (0x00000A9B) +#define WCD9360_CDC_TX6_TX_PATH_SEC5 (0x00000A9C) +#define WCD9360_CDC_TX6_TX_PATH_SEC6 (0x00000A9D) +#define WCD9360_CDC_TX7_TX_PATH_CTL (0x00000AA1) +#define WCD9360_CDC_TX7_TX_PATH_CFG0 (0x00000AA2) +#define WCD9360_CDC_TX7_TX_PATH_CFG1 (0x00000AA3) +#define WCD9360_CDC_TX7_TX_VOL_CTL (0x00000AA4) +#define WCD9360_CDC_TX7_TX_PATH_192_CTL (0x00000AA5) +#define WCD9360_CDC_TX7_TX_PATH_192_CFG (0x00000AA6) +#define WCD9360_CDC_TX7_TX_PATH_SEC0 (0x00000AA7) +#define WCD9360_CDC_TX7_TX_PATH_SEC1 (0x00000AA8) +#define WCD9360_CDC_TX7_TX_PATH_SEC2 (0x00000AA9) +#define WCD9360_CDC_TX7_TX_PATH_SEC3 (0x00000AAA) +#define WCD9360_CDC_TX7_TX_PATH_SEC4 (0x00000AAB) +#define WCD9360_CDC_TX7_TX_PATH_SEC5 (0x00000AAC) +#define WCD9360_CDC_TX7_TX_PATH_SEC6 (0x00000AAD) +#define WCD9360_CDC_TX8_TX_PATH_CTL (0x00000AB1) +#define WCD9360_CDC_TX8_TX_PATH_CFG0 (0x00000AB2) +#define WCD9360_CDC_TX8_TX_PATH_CFG1 (0x00000AB3) +#define WCD9360_CDC_TX8_TX_VOL_CTL (0x00000AB4) +#define WCD9360_CDC_TX8_TX_PATH_192_CTL (0x00000AB5) +#define WCD9360_CDC_TX8_TX_PATH_192_CFG (0x00000AB6) +#define WCD9360_CDC_TX8_TX_PATH_SEC0 (0x00000AB7) +#define WCD9360_CDC_TX8_TX_PATH_SEC1 (0x00000AB8) +#define WCD9360_CDC_TX8_TX_PATH_SEC2 (0x00000AB9) +#define WCD9360_CDC_TX8_TX_PATH_SEC3 (0x00000ABA) +#define WCD9360_CDC_TX8_TX_PATH_SEC4 (0x00000ABB) +#define WCD9360_CDC_TX8_TX_PATH_SEC5 (0x00000ABC) +#define WCD9360_CDC_TX8_TX_PATH_SEC6 (0x00000ABD) +#define WCD9360_CDC_TX9_SPKR_PROT_PATH_CTL (0x00000AC2) +#define WCD9360_CDC_TX9_SPKR_PROT_PATH_CFG0 (0x00000AC3) +#define WCD9360_CDC_TX10_SPKR_PROT_PATH_CTL (0x00000AC6) +#define WCD9360_CDC_TX10_SPKR_PROT_PATH_CFG0 (0x00000AC7) +#define WCD9360_CDC_TX11_SPKR_PROT_PATH_CTL (0x00000ACA) +#define WCD9360_CDC_TX11_SPKR_PROT_PATH_CFG0 (0x00000ACB) +#define WCD9360_CDC_TX12_SPKR_PROT_PATH_CTL (0x00000ACE) +#define WCD9360_CDC_TX12_SPKR_PROT_PATH_CFG0 (0x00000ACF) +#define WCD9360_PAGE11_PAGE_REGISTER (0x00000B00) +#define WCD9360_CDC_COMPANDER0_CTL0 (0x00000B21) +#define WCD9360_CDC_COMPANDER0_CTL1 (0x00000B22) +#define WCD9360_CDC_COMPANDER0_CTL2 (0x00000B23) +#define WCD9360_CDC_COMPANDER0_CTL3 (0x00000B24) +#define WCD9360_CDC_COMPANDER0_CTL4 (0x00000B25) +#define WCD9360_CDC_COMPANDER0_CTL5 (0x00000B26) +#define WCD9360_CDC_COMPANDER0_CTL6 (0x00000B27) +#define WCD9360_CDC_COMPANDER0_CTL7 (0x00000B28) +#define WCD9360_CDC_COMPANDER7_CTL0 (0x00000B31) +#define WCD9360_CDC_COMPANDER7_CTL1 (0x00000B32) +#define WCD9360_CDC_COMPANDER7_CTL2 (0x00000B33) +#define WCD9360_CDC_COMPANDER7_CTL3 (0x00000B34) +#define WCD9360_CDC_COMPANDER7_CTL4 (0x00000B35) +#define WCD9360_CDC_COMPANDER7_CTL5 (0x00000B36) +#define WCD9360_CDC_COMPANDER7_CTL6 (0x00000B37) +#define WCD9360_CDC_COMPANDER7_CTL7 (0x00000B38) +#define WCD9360_CDC_COMPANDER8_CTL0 (0x00000B39) +#define WCD9360_CDC_COMPANDER8_CTL1 (0x00000B3A) +#define WCD9360_CDC_COMPANDER8_CTL2 (0x00000B3B) +#define WCD9360_CDC_COMPANDER8_CTL3 (0x00000B3C) +#define WCD9360_CDC_COMPANDER8_CTL4 (0x00000B3D) +#define WCD9360_CDC_COMPANDER8_CTL5 (0x00000B3E) +#define WCD9360_CDC_COMPANDER8_CTL6 (0x00000B3F) +#define WCD9360_CDC_COMPANDER8_CTL7 (0x00000B40) +#define WCD9360_CDC_RX0_RX_PATH_CTL (0x00000B41) +#define WCD9360_CDC_RX0_RX_PATH_CFG0 (0x00000B42) +#define WCD9360_CDC_RX0_RX_PATH_CFG1 (0x00000B43) +#define WCD9360_CDC_RX0_RX_PATH_CFG2 (0x00000B44) +#define WCD9360_CDC_RX0_RX_VOL_CTL (0x00000B45) +#define WCD9360_CDC_RX0_RX_PATH_MIX_CTL (0x00000B46) +#define WCD9360_CDC_RX0_RX_PATH_MIX_CFG (0x00000B47) +#define WCD9360_CDC_RX0_RX_VOL_MIX_CTL (0x00000B48) +#define WCD9360_CDC_RX0_RX_PATH_SEC0 (0x00000B49) +#define WCD9360_CDC_RX0_RX_PATH_SEC1 (0x00000B4A) +#define WCD9360_CDC_RX0_RX_PATH_SEC2 (0x00000B4B) +#define WCD9360_CDC_RX0_RX_PATH_SEC3 (0x00000B4C) +#define WCD9360_CDC_RX0_RX_PATH_SEC5 (0x00000B4E) +#define WCD9360_CDC_RX0_RX_PATH_SEC6 (0x00000B4F) +#define WCD9360_CDC_RX0_RX_PATH_SEC7 (0x00000B50) +#define WCD9360_CDC_RX0_RX_PATH_MIX_SEC0 (0x00000B51) +#define WCD9360_CDC_RX0_RX_PATH_MIX_SEC1 (0x00000B52) +#define WCD9360_CDC_RX0_RX_PATH_DSMDEM_CTL (0x00000B53) +#define WCD9360_CDC_RX9_RX_PATH_CTL (0x00000BA5) +#define WCD9360_CDC_RX9_RX_PATH_CFG0 (0x00000BA6) +#define WCD9360_CDC_RX9_RX_PATH_CFG1 (0x00000BA7) +#define WCD9360_CDC_RX9_RX_PATH_CFG2 (0x00000BA8) +#define WCD9360_CDC_RX9_RX_VOL_CTL (0x00000BA9) +#define WCD9360_CDC_RX9_RX_PATH_MIX_CTL (0x00000BAA) +#define WCD9360_CDC_RX9_RX_PATH_MIX_CFG (0x00000BAB) +#define WCD9360_CDC_RX9_RX_VOL_MIX_CTL (0x00000BAC) +#define WCD9360_CDC_RX9_RX_PATH_SEC0 (0x00000BAD) +#define WCD9360_CDC_RX9_RX_PATH_SEC1 (0x00000BAE) +#define WCD9360_CDC_RX9_RX_PATH_SEC2 (0x00000BAF) +#define WCD9360_CDC_RX9_RX_PATH_SEC3 (0x00000BB0) +#define WCD9360_CDC_RX9_RX_PATH_SEC5 (0x00000BB2) +#define WCD9360_CDC_RX9_RX_PATH_SEC6 (0x00000BB3) +#define WCD9360_CDC_RX9_RX_PATH_SEC7 (0x00000BB4) +#define WCD9360_CDC_RX9_RX_PATH_MIX_SEC0 (0x00000BB5) +#define WCD9360_CDC_RX9_RX_PATH_MIX_SEC1 (0x00000BB6) +#define WCD9360_CDC_RX9_RX_PATH_DSMDEM_CTL (0x00000BB7) +#define WCD9360_CDC_RX7_RX_PATH_CTL (0x00000BCD) +#define WCD9360_CDC_RX7_RX_PATH_CFG0 (0x00000BCE) +#define WCD9360_CDC_RX7_RX_PATH_CFG1 (0x00000BCF) +#define WCD9360_CDC_RX7_RX_PATH_CFG2 (0x00000BD0) +#define WCD9360_CDC_RX7_RX_VOL_CTL (0x00000BD1) +#define WCD9360_CDC_RX7_RX_PATH_MIX_CTL (0x00000BD2) +#define WCD9360_CDC_RX7_RX_PATH_MIX_CFG (0x00000BD3) +#define WCD9360_CDC_RX7_RX_VOL_MIX_CTL (0x00000BD4) +#define WCD9360_CDC_RX7_RX_PATH_SEC0 (0x00000BD5) +#define WCD9360_CDC_RX7_RX_PATH_SEC1 (0x00000BD6) +#define WCD9360_CDC_RX7_RX_PATH_SEC2 (0x00000BD7) +#define WCD9360_CDC_RX7_RX_PATH_SEC3 (0x00000BD8) +#define WCD9360_CDC_RX7_RX_PATH_SEC5 (0x00000BDA) +#define WCD9360_CDC_RX7_RX_PATH_SEC6 (0x00000BDB) +#define WCD9360_CDC_RX7_RX_PATH_SEC7 (0x00000BDC) +#define WCD9360_CDC_RX7_RX_PATH_MIX_SEC0 (0x00000BDD) +#define WCD9360_CDC_RX7_RX_PATH_MIX_SEC1 (0x00000BDE) +#define WCD9360_CDC_RX7_RX_PATH_DSMDEM_CTL (0x00000BDF) +#define WCD9360_CDC_RX8_RX_PATH_CTL (0x00000BE1) +#define WCD9360_CDC_RX8_RX_PATH_CFG0 (0x00000BE2) +#define WCD9360_CDC_RX8_RX_PATH_CFG1 (0x00000BE3) +#define WCD9360_CDC_RX8_RX_PATH_CFG2 (0x00000BE4) +#define WCD9360_CDC_RX8_RX_VOL_CTL (0x00000BE5) +#define WCD9360_CDC_RX8_RX_PATH_MIX_CTL (0x00000BE6) +#define WCD9360_CDC_RX8_RX_PATH_MIX_CFG (0x00000BE7) +#define WCD9360_CDC_RX8_RX_VOL_MIX_CTL (0x00000BE8) +#define WCD9360_CDC_RX8_RX_PATH_SEC0 (0x00000BE9) +#define WCD9360_CDC_RX8_RX_PATH_SEC1 (0x00000BEA) +#define WCD9360_CDC_RX8_RX_PATH_SEC2 (0x00000BEB) +#define WCD9360_CDC_RX8_RX_PATH_SEC3 (0x00000BEC) +#define WCD9360_CDC_RX8_RX_PATH_SEC5 (0x00000BEE) +#define WCD9360_CDC_RX8_RX_PATH_SEC6 (0x00000BEF) +#define WCD9360_CDC_RX8_RX_PATH_SEC7 (0x00000BF0) +#define WCD9360_CDC_RX8_RX_PATH_MIX_SEC0 (0x00000BF1) +#define WCD9360_CDC_RX8_RX_PATH_MIX_SEC1 (0x00000BF2) +#define WCD9360_CDC_RX8_RX_PATH_DSMDEM_CTL (0x00000BF3) +#define WCD9360_PAGE12_PAGE_REGISTER (0x00000C00) +#define WCD9360_CDC_BOOST0_BOOST_PATH_CTL (0x00000C19) +#define WCD9360_CDC_BOOST0_BOOST_CTL (0x00000C1A) +#define WCD9360_CDC_BOOST0_BOOST_CFG1 (0x00000C1B) +#define WCD9360_CDC_BOOST0_BOOST_CFG2 (0x00000C1C) +#define WCD9360_CDC_BOOST1_BOOST_PATH_CTL (0x00000C21) +#define WCD9360_CDC_BOOST1_BOOST_CTL (0x00000C22) +#define WCD9360_CDC_BOOST1_BOOST_CFG1 (0x00000C23) +#define WCD9360_CDC_BOOST1_BOOST_CFG2 (0x00000C24) +#define WCD9360_MIXING_ASRC2_CLK_RST_CTL (0x00000C6D) +#define WCD9360_MIXING_ASRC2_CTL0 (0x00000C6E) +#define WCD9360_MIXING_ASRC2_CTL1 (0x00000C6F) +#define WCD9360_MIXING_ASRC2_FIFO_CTL (0x00000C70) +#define WCD9360_MIXING_ASRC2_STATUS_FMIN_CNTR_LSB (0x00000C71) +#define WCD9360_MIXING_ASRC2_STATUS_FMIN_CNTR_MSB (0x00000C72) +#define WCD9360_MIXING_ASRC2_STATUS_FMAX_CNTR_LSB (0x00000C73) +#define WCD9360_MIXING_ASRC2_STATUS_FMAX_CNTR_MSB (0x00000C74) +#define WCD9360_MIXING_ASRC2_STATUS_FIFO (0x00000C75) +#define WCD9360_MIXING_ASRC3_CLK_RST_CTL (0x00000C79) +#define WCD9360_MIXING_ASRC3_CTL0 (0x00000C7A) +#define WCD9360_MIXING_ASRC3_CTL1 (0x00000C7B) +#define WCD9360_MIXING_ASRC3_FIFO_CTL (0x00000C7C) +#define WCD9360_MIXING_ASRC3_STATUS_FMIN_CNTR_LSB (0x00000C7D) +#define WCD9360_MIXING_ASRC3_STATUS_FMIN_CNTR_MSB (0x00000C7E) +#define WCD9360_MIXING_ASRC3_STATUS_FMAX_CNTR_LSB (0x00000C7F) +#define WCD9360_MIXING_ASRC3_STATUS_FMAX_CNTR_MSB (0x00000C80) +#define WCD9360_MIXING_ASRC3_STATUS_FIFO (0x00000C81) +#define WCD9360_SWR_AHB_BRIDGE_WR_DATA_0 (0x00000C85) +#define WCD9360_SWR_AHB_BRIDGE_WR_DATA_1 (0x00000C86) +#define WCD9360_SWR_AHB_BRIDGE_WR_DATA_2 (0x00000C87) +#define WCD9360_SWR_AHB_BRIDGE_WR_DATA_3 (0x00000C88) +#define WCD9360_SWR_AHB_BRIDGE_WR_ADDR_0 (0x00000C89) +#define WCD9360_SWR_AHB_BRIDGE_WR_ADDR_1 (0x00000C8A) +#define WCD9360_SWR_AHB_BRIDGE_WR_ADDR_2 (0x00000C8B) +#define WCD9360_SWR_AHB_BRIDGE_WR_ADDR_3 (0x00000C8C) +#define WCD9360_SWR_AHB_BRIDGE_RD_ADDR_0 (0x00000C8D) +#define WCD9360_SWR_AHB_BRIDGE_RD_ADDR_1 (0x00000C8E) +#define WCD9360_SWR_AHB_BRIDGE_RD_ADDR_2 (0x00000C8F) +#define WCD9360_SWR_AHB_BRIDGE_RD_ADDR_3 (0x00000C90) +#define WCD9360_SWR_AHB_BRIDGE_RD_DATA_0 (0x00000C91) +#define WCD9360_SWR_AHB_BRIDGE_RD_DATA_1 (0x00000C92) +#define WCD9360_SWR_AHB_BRIDGE_RD_DATA_2 (0x00000C93) +#define WCD9360_SWR_AHB_BRIDGE_RD_DATA_3 (0x00000C94) +#define WCD9360_SWR_AHB_BRIDGE_ACCESS_CFG (0x00000C95) +#define WCD9360_SWR_AHB_BRIDGE_ACCESS_STATUS (0x00000C96) +#define WCD9360_CDC_SIDETONE_SRC0_ST_SRC_PATH_CTL (0x00000CB5) +#define WCD9360_CDC_SIDETONE_SRC0_ST_SRC_PATH_CFG1 (0x00000CB6) +#define WCD9360_SIDETONE_ASRC0_CLK_RST_CTL (0x00000CBD) +#define WCD9360_SIDETONE_ASRC0_CTL0 (0x00000CBE) +#define WCD9360_SIDETONE_ASRC0_CTL1 (0x00000CBF) +#define WCD9360_SIDETONE_ASRC0_FIFO_CTL (0x00000CC0) +#define WCD9360_SIDETONE_ASRC0_STATUS_FMIN_CNTR_LSB (0x00000CC1) +#define WCD9360_SIDETONE_ASRC0_STATUS_FMIN_CNTR_MSB (0x00000CC2) +#define WCD9360_SIDETONE_ASRC0_STATUS_FMAX_CNTR_LSB (0x00000CC3) +#define WCD9360_SIDETONE_ASRC0_STATUS_FMAX_CNTR_MSB (0x00000CC4) +#define WCD9360_SIDETONE_ASRC0_STATUS_FIFO (0x00000CC5) +#define WCD9360_EC_REF_HQ0_EC_REF_HQ_PATH_CTL (0x00000CD5) +#define WCD9360_EC_REF_HQ0_EC_REF_HQ_CFG0 (0x00000CD6) +#define WCD9360_EC_REF_HQ1_EC_REF_HQ_PATH_CTL (0x00000CDD) +#define WCD9360_EC_REF_HQ1_EC_REF_HQ_CFG0 (0x00000CDE) +#define WCD9360_EC_ASRC0_CLK_RST_CTL (0x00000CE5) +#define WCD9360_EC_ASRC0_CTL0 (0x00000CE6) +#define WCD9360_EC_ASRC0_CTL1 (0x00000CE7) +#define WCD9360_EC_ASRC0_FIFO_CTL (0x00000CE8) +#define WCD9360_EC_ASRC0_STATUS_FMIN_CNTR_LSB (0x00000CE9) +#define WCD9360_EC_ASRC0_STATUS_FMIN_CNTR_MSB (0x00000CEA) +#define WCD9360_EC_ASRC0_STATUS_FMAX_CNTR_LSB (0x00000CEB) +#define WCD9360_EC_ASRC0_STATUS_FMAX_CNTR_MSB (0x00000CEC) +#define WCD9360_EC_ASRC0_STATUS_FIFO (0x00000CED) +#define WCD9360_EC_ASRC1_CLK_RST_CTL (0x00000CF1) +#define WCD9360_EC_ASRC1_CTL0 (0x00000CF2) +#define WCD9360_EC_ASRC1_CTL1 (0x00000CF3) +#define WCD9360_EC_ASRC1_FIFO_CTL (0x00000CF4) +#define WCD9360_EC_ASRC1_STATUS_FMIN_CNTR_LSB (0x00000CF5) +#define WCD9360_EC_ASRC1_STATUS_FMIN_CNTR_MSB (0x00000CF6) +#define WCD9360_EC_ASRC1_STATUS_FMAX_CNTR_LSB (0x00000CF7) +#define WCD9360_EC_ASRC1_STATUS_FMAX_CNTR_MSB (0x00000CF8) +#define WCD9360_EC_ASRC1_STATUS_FIFO (0x00000CF9) +#define WCD9360_PAGE13_PAGE_REGISTER (0x00000D00) +#define WCD9360_CDC_RX_INP_MUX_RX_INT0_CFG0 (0x00000D01) +#define WCD9360_CDC_RX_INP_MUX_RX_INT0_CFG1 (0x00000D02) +#define WCD9360_CDC_RX_INP_MUX_RX_INT9_CFG0 (0x00000D0B) +#define WCD9360_CDC_RX_INP_MUX_RX_INT9_CFG1 (0x00000D0C) +#define WCD9360_CDC_RX_INP_MUX_RX_INT7_CFG0 (0x00000D0F) +#define WCD9360_CDC_RX_INP_MUX_RX_INT7_CFG1 (0x00000D10) +#define WCD9360_CDC_RX_INP_MUX_RX_INT8_CFG0 (0x00000D11) +#define WCD9360_CDC_RX_INP_MUX_RX_INT8_CFG1 (0x00000D12) +#define WCD9360_CDC_RX_INP_MUX_RX_MIX_CFG0 (0x00000D13) +#define WCD9360_CDC_RX_INP_MUX_RX_MIX_CFG1 (0x00000D14) +#define WCD9360_CDC_RX_INP_MUX_RX_MIX_CFG2 (0x00000D15) +#define WCD9360_CDC_RX_INP_MUX_RX_MIX_CFG3 (0x00000D16) +#define WCD9360_CDC_RX_INP_MUX_RX_MIX_CFG4 (0x00000D17) +#define WCD9360_CDC_RX_INP_MUX_SIDETONE_SRC_CFG0 (0x00000D18) +#define WCD9360_CDC_RX_INP_MUX_SIDETONE_SRC_CFG1 (0x00000D19) +#define WCD9360_CDC_RX_INP_MUX_ANC_CFG0 (0x00000D1A) +#define WCD9360_CDC_RX_INP_MUX_SPLINE_ASRC_CFG0 (0x00000D1B) +#define WCD9360_CDC_RX_INP_MUX_EC_REF_HQ_CFG0 (0x00000D1C) +#define WCD9360_CDC_TX_INP_MUX_ADC_MUX0_CFG0 (0x00000D1D) +#define WCD9360_CDC_TX_INP_MUX_ADC_MUX0_CFG1 (0x00000D1E) +#define WCD9360_CDC_TX_INP_MUX_ADC_MUX1_CFG0 (0x00000D1F) +#define WCD9360_CDC_TX_INP_MUX_ADC_MUX1_CFG1 (0x00000D20) +#define WCD9360_CDC_TX_INP_MUX_ADC_MUX2_CFG0 (0x00000D21) +#define WCD9360_CDC_TX_INP_MUX_ADC_MUX2_CFG1 (0x00000D22) +#define WCD9360_CDC_TX_INP_MUX_ADC_MUX3_CFG0 (0x00000D23) +#define WCD9360_CDC_TX_INP_MUX_ADC_MUX3_CFG1 (0x00000D25) +#define WCD9360_CDC_TX_INP_MUX_ADC_MUX4_CFG0 (0x00000D26) +#define WCD9360_CDC_TX_INP_MUX_ADC_MUX5_CFG0 (0x00000D27) +#define WCD9360_CDC_TX_INP_MUX_ADC_MUX6_CFG0 (0x00000D28) +#define WCD9360_CDC_TX_INP_MUX_ADC_MUX7_CFG0 (0x00000D29) +#define WCD9360_CDC_TX_INP_MUX_ADC_MUX8_CFG0 (0x00000D2A) +#define WCD9360_CDC_TX_INP_MUX_ADC_MUX10_CFG0 (0x00000D2B) +#define WCD9360_CDC_TX_INP_MUX_ADC_MUX11_CFG0 (0x00000D2C) +#define WCD9360_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG0 (0x00000D31) +#define WCD9360_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG1 (0x00000D32) +#define WCD9360_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG2 (0x00000D33) +#define WCD9360_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG3 (0x00000D34) +#define WCD9360_CDC_IF_ROUTER_TX_MUX_CFG0 (0x00000D3A) +#define WCD9360_CDC_IF_ROUTER_TX_MUX_CFG1 (0x00000D3B) +#define WCD9360_CDC_IF_ROUTER_TX_MUX_CFG2 (0x00000D3C) +#define WCD9360_CDC_IF_ROUTER_TX_MUX_CFG3 (0x00000D3D) +#define WCD9360_CDC_CLK_RST_CTRL_MCLK_CONTROL (0x00000D41) +#define WCD9360_CDC_CLK_RST_CTRL_FS_CNT_CONTROL (0x00000D42) +#define WCD9360_CDC_CLK_RST_CTRL_SWR_CONTROL (0x00000D43) +#define WCD9360_CDC_CLK_RST_CTRL_ASRC_SHARE_CONTROL (0x00000D45) +#define WCD9360_CDC_PROX_DETECT_PROX_CTL (0x00000D49) +#define WCD9360_CDC_PROX_DETECT_PROX_POLL_PERIOD0 (0x00000D4A) +#define WCD9360_CDC_PROX_DETECT_PROX_POLL_PERIOD1 (0x00000D4B) +#define WCD9360_CDC_PROX_DETECT_PROX_SIG_PATTERN_LSB (0x00000D4C) +#define WCD9360_CDC_PROX_DETECT_PROX_SIG_PATTERN_MSB (0x00000D4D) +#define WCD9360_CDC_PROX_DETECT_PROX_STATUS (0x00000D4E) +#define WCD9360_CDC_PROX_DETECT_PROX_TEST_CTRL (0x00000D4F) +#define WCD9360_CDC_PROX_DETECT_PROX_TEST_BUFF_LSB (0x00000D50) +#define WCD9360_CDC_PROX_DETECT_PROX_TEST_BUFF_MSB (0x00000D51) +#define WCD9360_CDC_PROX_DETECT_PROX_TEST_BUFF_LSB_RD (0x00000D52) +#define WCD9360_CDC_PROX_DETECT_PROX_TEST_BUFF_MSB_RD (0x00000D53) +#define WCD9360_CDC_PROX_DETECT_PROX_CTL_REPEAT_PAT (0x00000D54) +#define WCD9360_CDC_SIDETONE_IIR0_IIR_PATH_CTL (0x00000D55) +#define WCD9360_CDC_SIDETONE_IIR0_IIR_GAIN_B1_CTL (0x00000D56) +#define WCD9360_CDC_SIDETONE_IIR0_IIR_GAIN_B2_CTL (0x00000D57) +#define WCD9360_CDC_SIDETONE_IIR0_IIR_GAIN_B3_CTL (0x00000D58) +#define WCD9360_CDC_SIDETONE_IIR0_IIR_GAIN_B4_CTL (0x00000D59) +#define WCD9360_CDC_SIDETONE_IIR0_IIR_GAIN_B5_CTL (0x00000D5A) +#define WCD9360_CDC_SIDETONE_IIR0_IIR_GAIN_B6_CTL (0x00000D5B) +#define WCD9360_CDC_SIDETONE_IIR0_IIR_GAIN_B7_CTL (0x00000D5C) +#define WCD9360_CDC_SIDETONE_IIR0_IIR_GAIN_B8_CTL (0x00000D5D) +#define WCD9360_CDC_SIDETONE_IIR0_IIR_CTL (0x00000D5E) +#define WCD9360_CDC_SIDETONE_IIR0_IIR_GAIN_TIMER_CTL (0x00000D5F) +#define WCD9360_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL (0x00000D60) +#define WCD9360_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL (0x00000D61) +#define WCD9360_CDC_TOP_TOP_CFG0 (0x00000D81) +#define WCD9360_CDC_TOP_TOP_CFG1 (0x00000D82) +#define WCD9360_CDC_TOP_TOP_CFG7 (0x00000D88) +#define WCD9360_CDC_TOP_EAR_COMP_WR_LSB (0x00000DA9) +#define WCD9360_CDC_TOP_EAR_COMP_WR_MSB (0x00000DAA) +#define WCD9360_CDC_TOP_EAR_COMP_LUT (0x00000DAB) +#define WCD9360_CDC_TOP_EAR_COMP_RD_LSB (0x00000DAC) +#define WCD9360_CDC_TOP_EAR_COMP_RD_MSB (0x00000DAD) +#define WCD9360_CDC_TOP_TOP_DEBUG (0x00000DAE) +#define WCD9360_PAGE80_PAGE_REGISTER (0x00005000) +#define WCD9360_CODEC_CPR_WR_DATA_0 (0x00005001) +#define WCD9360_CODEC_CPR_WR_DATA_1 (0x00005002) +#define WCD9360_CODEC_CPR_WR_DATA_2 (0x00005003) +#define WCD9360_CODEC_CPR_WR_DATA_3 (0x00005004) +#define WCD9360_CODEC_CPR_WR_ADDR_0 (0x00005005) +#define WCD9360_CODEC_CPR_WR_ADDR_1 (0x00005006) +#define WCD9360_CODEC_CPR_WR_ADDR_2 (0x00005007) +#define WCD9360_CODEC_CPR_WR_ADDR_3 (0x00005008) +#define WCD9360_CODEC_CPR_RD_ADDR_0 (0x00005009) +#define WCD9360_CODEC_CPR_RD_ADDR_1 (0x0000500A) +#define WCD9360_CODEC_CPR_RD_ADDR_2 (0x0000500B) +#define WCD9360_CODEC_CPR_RD_ADDR_3 (0x0000500C) +#define WCD9360_CODEC_CPR_RD_DATA_0 (0x0000500D) +#define WCD9360_CODEC_CPR_RD_DATA_1 (0x0000500E) +#define WCD9360_CODEC_CPR_RD_DATA_2 (0x0000500F) +#define WCD9360_CODEC_CPR_RD_DATA_3 (0x00005010) +#define WCD9360_CODEC_CPR_ACCESS_CFG (0x00005011) +#define WCD9360_CODEC_CPR_ACCESS_STATUS (0x00005012) +#define WCD9360_CODEC_CPR_NOM_CX_VDD (0x00005021) +#define WCD9360_CODEC_CPR_SVS_CX_VDD (0x00005022) +#define WCD9360_CODEC_CPR_SVS2_CX_VDD (0x00005023) +#define WCD9360_CODEC_CPR_NOM_MX_VDD (0x00005024) +#define WCD9360_CODEC_CPR_SVS_MX_VDD (0x00005025) +#define WCD9360_CODEC_CPR_SVS2_MX_VDD (0x00005026) +#define WCD9360_CODEC_CPR_SVS2_MIN_CX_VDD (0x00005027) +#define WCD9360_CODEC_CPR_MAX_SVS2_STEP (0x00005028) +#define WCD9360_CODEC_CPR_CTL (0x00005029) +#define WCD9360_CODEC_CPR_SW_MODECHNG_STATUS (0x0000502A) +#define WCD9360_CODEC_CPR_SW_MODECHNG_START (0x0000502B) +#define WCD9360_CODEC_CPR_CPR_STATUS (0x0000502C) +#define WCD9360_PAGE128_PAGE_REGISTER (0x00008000) +#define WCD9360_TLMM_JTCK_PINCFG (0x00008001) +#define WCD9360_TLMM_INTR1_PINCFG (0x00008002) +#define WCD9360_TLMM_INTR2_PINCFG (0x00008003) +#define WCD9360_TLMM_SWR_DATA_PINCFG (0x00008004) +#define WCD9360_TLMM_SWR_CLK_PINCFG (0x00008005) +#define WCD9360_TLMM_SLIMBUS_DATA1_PINCFG (0x00008006) +#define WCD9360_TLMM_SLIMBUS_DATA2_PINCFG (0x00008007) +#define WCD9360_TLMM_SLIMBUS_CLK_PINCFG (0x00008008) +#define WCD9360_TLMM_I2C_CLK_PINCFG (0x00008009) +#define WCD9360_TLMM_I2C_DATA_PINCFG (0x0000800A) +#define WCD9360_TLMM_I2S_0_RX_PINCFG (0x0000800B) +#define WCD9360_TLMM_I2S_0_TX_PINCFG (0x0000800C) +#define WCD9360_TLMM_I2S_0_SCK_PINCFG (0x0000800D) +#define WCD9360_TLMM_I2S_0_WS_PINCFG (0x0000800E) +#define WCD9360_TLMM_I2S_1_RX_PINCFG (0x0000800F) +#define WCD9360_TLMM_I2S_1_TX_PINCFG (0x00008010) +#define WCD9360_TLMM_I2S_1_SCK_PINCFG (0x00008011) +#define WCD9360_TLMM_I2S_1_WS_PINCFG (0x00008012) +#define WCD9360_TLMM_DMIC1_CLK_PINCFG (0x00008013) +#define WCD9360_TLMM_DMIC1_DATA_PINCFG (0x00008014) +#define WCD9360_TLMM_DMIC2_CLK_PINCFG (0x00008015) +#define WCD9360_TLMM_DMIC2_DATA_PINCFG (0x00008016) +#define WCD9360_TLMM_GPIO1_PINCFG (0x00008017) +#define WCD9360_TLMM_GPIO2_PINCFG (0x00008018) +#define WCD9360_TLMM_GPIO3_PINCFG (0x00008019) +#define WCD9360_TLMM_GPIO4_PINCFG (0x0000801A) +#define WCD9360_TLMM_SPI_S_CSN_PINCFG (0x0000801B) +#define WCD9360_TLMM_SPI_S_CLK_PINCFG (0x0000801C) +#define WCD9360_TLMM_SPI_S_DOUT_PINCFG (0x0000801D) +#define WCD9360_TLMM_SPI_S_DIN_PINCFG (0x0000801E) +#define WCD9360_TLMM_GPIO0_PINCFG (0x0000801F) +#define WCD9360_TLMM_DMIC3_CLK_PINCFG (0x00008020) +#define WCD9360_TLMM_DMIC3_DATA_PINCFG (0x00008021) +#define WCD9360_TLMM_DMIC4_CLK_PINCFG (0x00008022) +#define WCD9360_TLMM_DMIC4_DATA_PINCFG (0x00008023) +#define WCD9360_TEST_DEBUG_PIN_CTL_OE_0 (0x00008031) +#define WCD9360_TEST_DEBUG_PIN_CTL_OE_1 (0x00008032) +#define WCD9360_TEST_DEBUG_PIN_CTL_OE_2 (0x00008033) +#define WCD9360_TEST_DEBUG_PIN_CTL_OE_3 (0x00008034) +#define WCD9360_TEST_DEBUG_PIN_CTL_OE_4 (0x00008035) +#define WCD9360_TEST_DEBUG_PIN_CTL_DATA_0 (0x00008036) +#define WCD9360_TEST_DEBUG_PIN_CTL_DATA_1 (0x00008037) +#define WCD9360_TEST_DEBUG_PIN_CTL_DATA_2 (0x00008038) +#define WCD9360_TEST_DEBUG_PIN_CTL_DATA_3 (0x00008039) +#define WCD9360_TEST_DEBUG_PIN_CTL_DATA_4 (0x0000803A) +#define WCD9360_TEST_DEBUG_PAD_DRVCTL_0 (0x0000803B) +#define WCD9360_TEST_DEBUG_PAD_DRVCTL_1 (0x0000803C) +#define WCD9360_TEST_DEBUG_PIN_STATUS (0x0000803D) +#define WCD9360_TEST_DEBUG_NPL_DLY_TEST_1 (0x0000803E) +#define WCD9360_TEST_DEBUG_NPL_DLY_TEST_2 (0x0000803F) +#define WCD9360_TEST_DEBUG_MEM_CTRL (0x00008040) +#define WCD9360_TEST_DEBUG_DEBUG_BUS_SEL (0x00008041) +#define WCD9360_TEST_DEBUG_DEBUG_JTAG (0x00008042) +#define WCD9360_TEST_DEBUG_DEBUG_EN_1 (0x00008043) +#define WCD9360_TEST_DEBUG_DEBUG_EN_2 (0x00008044) +#define WCD9360_TEST_DEBUG_DEBUG_EN_3 (0x00008045) +#define WCD9360_TEST_DEBUG_DEBUG_EN_4 (0x00008046) +#define WCD9360_TEST_DEBUG_DEBUG_EN_5 (0x00008047) +#define WCD9360_TEST_DEBUG_ANA_DTEST_DIR (0x0000804A) +#define WCD9360_TEST_DEBUG_PAD_INP_DISABLE_0 (0x0000804B) +#define WCD9360_TEST_DEBUG_PAD_INP_DISABLE_1 (0x0000804C) +#define WCD9360_TEST_DEBUG_PAD_INP_DISABLE_2 (0x0000804D) +#define WCD9360_TEST_DEBUG_PAD_INP_DISABLE_3 (0x0000804E) +#define WCD9360_TEST_DEBUG_PAD_INP_DISABLE_4 (0x0000804F) +#define WCD9360_TEST_DEBUG_SYSMEM_CTRL (0x00008050) +#define WCD9360_TEST_DEBUG_LVAL_NOM_LOW (0x00008052) +#define WCD9360_TEST_DEBUG_LVAL_NOM_HIGH (0x00008053) +#define WCD9360_TEST_DEBUG_LVAL_SVS_SVS2_LOW (0x00008054) +#define WCD9360_TEST_DEBUG_LVAL_SVS_SVS2_HIGH (0x00008055) +#define WCD9360_TEST_DEBUG_SPI_SLAVE_CHAR (0x00008056) +#define WCD9360_TEST_DEBUG_CODEC_DIAGS (0x00008057) +#define WCD9360_TEST_DEBUG_PAD_TEST (0x00008058) +#define WCD9360_TEST_DEBUG_DEBUG_MUX_BIT_0 (0x00008061) +#define WCD9360_TEST_DEBUG_DEBUG_MUX_BIT_1 (0x00008062) +#define WCD9360_TEST_DEBUG_DEBUG_MUX_BIT_2 (0x00008063) +#define WCD9360_TEST_DEBUG_DEBUG_MUX_BIT_3 (0x00008064) +#define WCD9360_TEST_DEBUG_DEBUG_MUX_BIT_4 (0x00008065) +#define WCD9360_TEST_DEBUG_DEBUG_MUX_BIT_5 (0x00008066) +#define WCD9360_TEST_DEBUG_DEBUG_MUX_BIT_6 (0x00008067) +#define WCD9360_TEST_DEBUG_DEBUG_MUX_BIT_7 (0x00008068) +#define WCD9360_TEST_DEBUG_DEBUG_MUX_BIT_8 (0x00008069) +#define WCD9360_TEST_DEBUG_DEBUG_MUX_BIT_9 (0x0000806A) +#define WCD9360_TEST_DEBUG_DEBUG_MUX_BIT_10 (0x0000806B) +#define WCD9360_TEST_DEBUG_DEBUG_MUX_BIT_11 (0x0000806C) +#define WCD9360_TEST_DEBUG_DEBUG_MUX_BIT_12 (0x0000806D) +#define WCD9360_TEST_DEBUG_DEBUG_MUX_BIT_13 (0x0000806E) +#define WCD9360_TEST_DEBUG_DEBUG_MUX_BIT_14 (0x0000806F) +#define WCD9360_TEST_DEBUG_DEBUG_MUX_BIT_15 (0x00008070) +#define WCD9360_TEST_DEBUG_DEBUG_MUX_BIT_16 (0x00008071) +#define WCD9360_TEST_DEBUG_DEBUG_MUX_BIT_17 (0x00008072) +#define WCD9360_TEST_DEBUG_DEBUG_MUX_BIT_18 (0x00008073) +#define WCD9360_TEST_DEBUG_DEBUG_MUX_BIT_19 (0x00008074) +#define WCD9360_TEST_DEBUG_DEBUG_MUX_BIT_20 (0x00008075) +#define WCD9360_TEST_DEBUG_DEBUG_MUX_BIT_21 (0x00008076) +#define WCD9360_TEST_DEBUG_DEBUG_MUX_BIT_22 (0x00008077) +#define WCD9360_TEST_DEBUG_DEBUG_MUX_BIT_23 (0x00008078) +#define WCD9360_TEST_DEBUG_DEBUG_MUX_BIT_24 (0x00008079) +#define WCD9360_TEST_DEBUG_DEBUG_MUX_BIT_25 (0x0000807A) +#define WCD9360_TEST_DEBUG_DEBUG_MUX_BIT_26 (0x0000807B) +#define WCD9360_TEST_DEBUG_DEBUG_MUX_BIT_27 (0x0000807C) +#define WCD9360_TEST_DEBUG_DEBUG_MUX_BIT_28 (0x0000807D) +#define WCD9360_TEST_DEBUG_DEBUG_MUX_BIT_29 (0x0000807E) +#define WCD9360_TEST_DEBUG_DEBUG_MUX_BIT_30 (0x0000807F) +#define WCD9360_TEST_DEBUG_DEBUG_MUX_BIT_31 (0x00008080) +#define WCD9360_TEST_DEBUG_DEBUG_MUX_RD_CTRL (0x00008081) +#define WCD9360_TEST_DEBUG_DEBUG_MUX_RD_7_0 (0x00008082) +#define WCD9360_TEST_DEBUG_DEBUG_MUX_RD_15_8 (0x00008083) +#define WCD9360_TEST_DEBUG_DEBUG_MUX_RD_23_16 (0x00008084) +#define WCD9360_TEST_DEBUG_DEBUG_MUX_RD_31_24 (0x00008085) +#define WCD9360_MAX_REGISTER 0x80FF + +/* SLIMBUS Slave Registers */ +#define WCD9360_SLIM_PGD_PORT_INT_RX_EN0 (0x30) +#define WCD9360_SLIM_PGD_PORT_INT_TX_EN0 (0x32) +#define WCD9360_SLIM_PGD_PORT_INT_STATUS_RX_0 (0x34) +#define WCD9360_SLIM_PGD_PORT_INT_STATUS_RX_1 (0x35) +#define WCD9360_SLIM_PGD_PORT_INT_STATUS_TX_0 (0x36) +#define WCD9360_SLIM_PGD_PORT_INT_STATUS_TX_1 (0x37) +#define WCD9360_SLIM_PGD_PORT_INT_CLR_RX_0 (0x38) +#define WCD9360_SLIM_PGD_PORT_INT_CLR_RX_1 (0x39) +#define WCD9360_SLIM_PGD_PORT_INT_CLR_TX_0 (0x3A) +#define WCD9360_SLIM_PGD_PORT_INT_CLR_TX_1 (0x3B) +#define WCD9360_SLIM_PGD_PORT_INT_RX_SOURCE0 (0x60) +#define WCD9360_SLIM_PGD_PORT_INT_TX_SOURCE0 (0x70) + +#endif diff --git a/audio-kernel/include/asoc/wcd9xxx_registers.h b/audio-kernel/include/asoc/wcd9xxx_registers.h new file mode 100644 index 000000000..66809eff2 --- /dev/null +++ b/audio-kernel/include/asoc/wcd9xxx_registers.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2018-2019, 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 _WCD9XXX_REGISTERS_H +#define _WCD9XXX_REGISTERS_H + +#define WCD9XXX_BASE_ADDRESS 0x3000 + +#define WCD9XXX_ANA_RX_SUPPLIES (WCD9XXX_BASE_ADDRESS+0x008) +#define WCD9XXX_ANA_HPH (WCD9XXX_BASE_ADDRESS+0x009) +#define WCD9XXX_CLASSH_MODE_2 (WCD9XXX_BASE_ADDRESS+0x098) +#define WCD9XXX_CLASSH_MODE_3 (WCD9XXX_BASE_ADDRESS+0x099) +#define WCD9XXX_FLYBACK_VNEG_CTRL_1 (WCD9XXX_BASE_ADDRESS+0x0A5) +#define WCD9XXX_FLYBACK_VNEG_CTRL_4 (WCD9XXX_BASE_ADDRESS+0x0A8) +#define WCD9XXX_FLYBACK_VNEGDAC_CTRL_2 (WCD9XXX_BASE_ADDRESS+0x0AF) +#define WCD9XXX_RX_BIAS_HPH_LOWPOWER (WCD9XXX_BASE_ADDRESS+0x0BF) +#define WCD9XXX_RX_BIAS_FLYB_BUFF (WCD9XXX_BASE_ADDRESS+0x0C7) +#define WCD9XXX_HPH_PA_CTL1 (WCD9XXX_BASE_ADDRESS+0x0D1) +#define WCD9XXX_HPH_NEW_INT_PA_MISC2 (WCD9XXX_BASE_ADDRESS+0x138) + +#endif diff --git a/audio-kernel/include/dsp/apr_audio-v2.h b/audio-kernel/include/dsp/apr_audio-v2.h new file mode 100644 index 000000000..61182bc74 --- /dev/null +++ b/audio-kernel/include/dsp/apr_audio-v2.h @@ -0,0 +1,12372 @@ +/* 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 + * 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 _APR_AUDIO_V2_H_ +#define _APR_AUDIO_V2_H_ + +#include +#include + +/* size of header needed for passing data out of band */ +#define APR_CMD_OB_HDR_SZ 12 + +/* size of header needed for getting data */ +#define APR_CMD_GET_HDR_SZ 16 + +struct param_outband { + size_t size; + void *kvaddr; + phys_addr_t paddr; +}; + +/* Common structures and definitions used for instance ID support */ +/* Instance ID definitions */ +#define INSTANCE_ID_0 0x0000 + +struct mem_mapping_hdr { + /* + * LSW of parameter data payload address. Supported values: any. + * - Must be set to zero for in-band data. + */ + u32 data_payload_addr_lsw; + + /* + * MSW of Parameter data payload address. Supported values: any. + * - Must be set to zero for in-band data. + * - In the case of 32 bit Shared memory address, MSW field must be + * set to zero. + * - In the case of 36 bit shared memory address, bit 31 to bit 4 of + * MSW must be set to zero. + */ + u32 data_payload_addr_msw; + + /* + * Memory map handle returned by DSP through + * ASM_CMD_SHARED_MEM_MAP_REGIONS command. + * Supported Values: Any. + * If memory map handle is NULL, the parameter data payloads are + * within the message payload (in-band). + * If memory map handle is non-NULL, the parameter data payloads begin + * at the address specified in the address MSW and LSW (out-of-band). + */ + u32 mem_map_handle; + +} __packed; + +/* + * Payload format for parameter data. + * Immediately following these structures are param_size bytes of parameter + * data. + */ +struct param_hdr_v1 { + /* Valid ID of the module. */ + uint32_t module_id; + + /* Valid ID of the parameter. */ + uint32_t param_id; + + /* The size of the parameter specified by the module/param ID combo */ + uint16_t param_size; + + /* This field must be set to zero. */ + uint16_t reserved; +} __packed; + +struct param_hdr_v2 { + /* Valid ID of the module. */ + uint32_t module_id; + + /* Valid ID of the parameter. */ + uint32_t param_id; + + /* The size of the parameter specified by the module/param ID combo */ + uint32_t param_size; +} __packed; + +struct param_hdr_v3 { + /* Valid ID of the module. */ + uint32_t module_id; + + /* Instance of the module. */ + uint16_t instance_id; + + /* This field must be set to zero. */ + uint16_t reserved; + + /* Valid ID of the parameter. */ + uint32_t param_id; + + /* The size of the parameter specified by the module/param ID combo */ + uint32_t param_size; +} __packed; + +/* A union of all param_hdr versions for versitility and max size */ +union param_hdrs { + struct param_hdr_v1 v1; + struct param_hdr_v2 v2; + struct param_hdr_v3 v3; +}; + +struct module_instance_info { + /* Module ID. */ + u32 module_id; + + /* Instance of the module */ + u16 instance_id; + + /* Reserved. This field must be set to zero. */ + u16 reserved; +} __packed; + +/* Begin service specific definitions and structures */ + +#define ADSP_ADM_VERSION 0x00070000 + +#define ADM_CMD_SHARED_MEM_MAP_REGIONS 0x00010322 +#define ADM_CMDRSP_SHARED_MEM_MAP_REGIONS 0x00010323 +#define ADM_CMD_SHARED_MEM_UNMAP_REGIONS 0x00010324 + +#define ADM_CMD_MATRIX_MAP_ROUTINGS_V5 0x00010325 +#define ADM_CMD_STREAM_DEVICE_MAP_ROUTINGS_V5 0x0001033D +/* Enumeration for an audio Rx matrix ID.*/ +#define ADM_MATRIX_ID_AUDIO_RX 0 + +#define ADM_MATRIX_ID_AUDIO_TX 1 + +#define ADM_MATRIX_ID_COMPRESSED_AUDIO_RX 2 + +#define ADM_MATRIX_ID_COMPRESSED_AUDIO_TX 3 + +#define ADM_MATRIX_ID_LISTEN_TX 4 +/* Enumeration for an audio Tx matrix ID.*/ +#define ADM_MATRIX_ID_AUDIOX 1 + +#define ADM_MAX_COPPS 5 + +/* make sure this matches with msm_audio_calibration */ +#define SP_V2_NUM_MAX_SPKR 2 + +/* Session map node structure. + * Immediately following this structure are num_copps + * entries of COPP IDs. The COPP IDs are 16 bits, so + * there might be a padding 16-bit field if num_copps + * is odd. + */ +struct adm_session_map_node_v5 { + u16 session_id; + /* Handle of the ASM session to be routed. Supported values: 1 + * to 8. + */ + + + u16 num_copps; + /* Number of COPPs to which this session is to be routed. + * Supported values: 0 < num_copps <= ADM_MAX_COPPS. + */ +} __packed; + +/* Payload of the #ADM_CMD_MATRIX_MAP_ROUTINGS_V5 command. + * Immediately following this structure are num_sessions of the session map + * node payload (adm_session_map_node_v5). + */ + +struct adm_cmd_matrix_map_routings_v5 { + struct apr_hdr hdr; + + u32 matrix_id; + /* Specifies whether the matrix ID is Audio Rx (0) or Audio Tx + * (1). Use the ADM_MATRIX_ID_AUDIO_RX or ADM_MATRIX_ID_AUDIOX + * macros to set this field. + */ + u32 num_sessions; + /* Number of sessions being updated by this command (optional). */ +} __packed; + +/* This command allows a client to open a COPP/Voice Proc. TX module + * and sets up the device session: Matrix -> COPP -> AFE on the RX + * and AFE -> COPP -> Matrix on the TX. This enables PCM data to + * be transferred to/from the endpoint (AFEPortID). + * + * @return + * #ADM_CMDRSP_DEVICE_OPEN_V5 with the resulting status and COPP ID. + */ +#define ADM_CMD_DEVICE_OPEN_V5 0x00010326 + +/* This command allows a client to open a COPP/Voice Proc the + * way as ADM_CMD_DEVICE_OPEN_V5 but supports multiple endpoint2 + * channels. + * + * @return + * #ADM_CMDRSP_DEVICE_OPEN_V6 with the resulting status and + * COPP ID. + */ +#define ADM_CMD_DEVICE_OPEN_V6 0x00010356 + +/* This command allows a client to open a COPP/Voice Proc the +* way as ADM_CMD_DEVICE_OPEN_V8 but supports any number channel +* of configuration. +* +* @return +* #ADM_CMDRSP_DEVICE_OPEN_V8 with the resulting status and +* COPP ID. +*/ +#define ADM_CMD_DEVICE_OPEN_V8 0x0001036A + + +/* Definition for a low latency stream session. */ +#define ADM_LOW_LATENCY_DEVICE_SESSION 0x2000 + +/* Definition for a ultra low latency stream session. */ +#define ADM_ULTRA_LOW_LATENCY_DEVICE_SESSION 0x4000 + +/* Definition for a ultra low latency with Post Processing stream session. */ +#define ADM_ULL_POST_PROCESSING_DEVICE_SESSION 0x8000 + +/* Definition for a legacy device session. */ +#define ADM_LEGACY_DEVICE_SESSION 0 + +/* Indicates that endpoint_id_2 is to be ignored.*/ +#define ADM_CMD_COPP_OPEN_END_POINT_ID_2_IGNORE 0xFFFF + +#define ADM_CMD_COPP_OPEN_MODE_OF_OPERATION_RX_PATH_COPP 1 + +#define ADM_CMD_COPP_OPEN_MODE_OF_OPERATIONX_PATH_LIVE_COPP 2 + +#define ADM_CMD_COPP_OPEN_MODE_OF_OPERATIONX_PATH_NON_LIVE_COPP 3 + +/* Indicates that an audio COPP is to send/receive a mono PCM + * stream to/from + * END_POINT_ID_1. + */ +#define ADM_CMD_COPP_OPEN_CHANNEL_CONFIG_MONO 1 + +/* Indicates that an audio COPP is to send/receive a + * stereo PCM stream to/from END_POINT_ID_1. + */ +#define ADM_CMD_COPP_OPEN_CHANNEL_CONFIG_STEREO 2 + +/* Sample rate is 8000 Hz.*/ +#define ADM_CMD_COPP_OPEN_SAMPLE_RATE_8K 8000 + +/* Sample rate is 16000 Hz.*/ +#define ADM_CMD_COPP_OPEN_SAMPLE_RATE_16K 16000 + +/* Sample rate is 32000 Hz.*/ +#define ADM_CMD_COPP_OPEN_SAMPLE_RATE_32K 32000 + +/* Sample rate is 48000 Hz.*/ +#define ADM_CMD_COPP_OPEN_SAMPLE_RATE_48K 48000 + +/* Definition for a COPP live input flag bitmask.*/ +#define ADM_BIT_MASK_COPP_LIVE_INPUT_FLAG (0x0001U) + +/* Definition for a COPP live shift value bitmask.*/ +#define ADM_SHIFT_COPP_LIVE_INPUT_FLAG 0 + +/* Definition for the COPP ID bitmask.*/ +#define ADM_BIT_MASK_COPP_ID (0x0000FFFFUL) + +/* Definition for the COPP ID shift value.*/ +#define ADM_SHIFT_COPP_ID 0 + +/* Definition for the service ID bitmask.*/ +#define ADM_BIT_MASK_SERVICE_ID (0x00FF0000UL) + +/* Definition for the service ID shift value.*/ +#define ADM_SHIFT_SERVICE_ID 16 + +/* Definition for the domain ID bitmask.*/ +#define ADM_BIT_MASK_DOMAIN_ID (0xFF000000UL) + +/* Definition for the domain ID shift value.*/ +#define ADM_SHIFT_DOMAIN_ID 24 + +/* ADM device open command payload of the + * #ADM_CMD_DEVICE_OPEN_V5 command. + */ +struct adm_cmd_device_open_v5 { + struct apr_hdr hdr; + u16 flags; +/* Reserved for future use. Clients must set this field + * to zero. + */ + + u16 mode_of_operation; +/* Specifies whether the COPP must be opened on the Tx or Rx + * path. Use the ADM_CMD_COPP_OPEN_MODE_OF_OPERATION_* macros for + * supported values and interpretation. + * Supported values: + * - 0x1 -- Rx path COPP + * - 0x2 -- Tx path live COPP + * - 0x3 -- Tx path nonlive COPP + * Live connections cause sample discarding in the Tx device + * matrix if the destination output ports do not pull them + * fast enough. Nonlive connections queue the samples + * indefinitely. + */ + + u16 endpoint_id_1; +/* Logical and physical endpoint ID of the audio path. + * If the ID is a voice processor Tx block, it receives near + * samples. Supported values: Any pseudoport, AFE Rx port, + * or AFE Tx port For a list of valid IDs, refer to + * @xhyperref{Q4,[Q4]}. + * Q4 = Hexagon Multimedia: AFE Interface Specification + */ + + u16 endpoint_id_2; +/* Logical and physical endpoint ID 2 for a voice processor + * Tx block. + * This is not applicable to audio COPP. + * Supported values: + * - AFE Rx port + * - 0xFFFF -- Endpoint 2 is unavailable and the voice + * processor Tx + * block ignores this endpoint + * When the voice processor Tx block is created on the audio + * record path, + * it can receive far-end samples from an AFE Rx port if the + * voice call + * is active. The ID of the AFE port is provided in this + * field. + * For a list of valid IDs, refer @xhyperref{Q4,[Q4]}. + */ + + u32 topology_id; +/* Audio COPP topology ID; 32-bit GUID. */ + + u16 dev_num_channel; +/* Number of channels the audio COPP sends to/receives from + * the endpoint. + * Supported values: 1 to 8. + * The value is ignored for the voice processor Tx block, + * where channel + * configuration is derived from the topology ID. + */ + + u16 bit_width; +/* Bit width (in bits) that the audio COPP sends to/receives + * from the + * endpoint. The value is ignored for the voice processing + * Tx block, + * where the PCM width is 16 bits. + */ + + u32 sample_rate; +/* Sampling rate at which the audio COPP/voice processor + * Tx block + * interfaces with the endpoint. + * Supported values for voice processor Tx: 8000, 16000, + * 48000 Hz + * Supported values for audio COPP: >0 and <=192 kHz + */ + + u8 dev_channel_mapping[8]; +/* Array of channel mapping of buffers that the audio COPP + * sends to the endpoint. Channel[i] mapping describes channel + * I inside the buffer, where 0 < i < dev_num_channel. + * This value is relevant only for an audio Rx COPP. + * For the voice processor block and Tx audio block, this field + * is set to zero and is ignored. + */ +} __packed; + +/* ADM device open command payload of the + * #ADM_CMD_DEVICE_OPEN_V6 command. + */ +struct adm_cmd_device_open_v6 { + struct apr_hdr hdr; + u16 flags; +/* Reserved for future use. Clients must set this field + * to zero. + */ + + u16 mode_of_operation; +/* Specifies whether the COPP must be opened on the Tx or Rx + * path. Use the ADM_CMD_COPP_OPEN_MODE_OF_OPERATION_* macros for + * supported values and interpretation. + * Supported values: + * - 0x1 -- Rx path COPP + * - 0x2 -- Tx path live COPP + * - 0x3 -- Tx path nonlive COPP + * Live connections cause sample discarding in the Tx device + * matrix if the destination output ports do not pull them + * fast enough. Nonlive connections queue the samples + * indefinitely. + */ + + u16 endpoint_id_1; +/* Logical and physical endpoint ID of the audio path. + * If the ID is a voice processor Tx block, it receives near + * samples. Supported values: Any pseudoport, AFE Rx port, + * or AFE Tx port For a list of valid IDs, refer to + * @xhyperref{Q4,[Q4]}. + * Q4 = Hexagon Multimedia: AFE Interface Specification + */ + + u16 endpoint_id_2; +/* Logical and physical endpoint ID 2 for a voice processor + * Tx block. + * This is not applicable to audio COPP. + * Supported values: + * - AFE Rx port + * - 0xFFFF -- Endpoint 2 is unavailable and the voice + * processor Tx + * block ignores this endpoint + * When the voice processor Tx block is created on the audio + * record path, + * it can receive far-end samples from an AFE Rx port if the + * voice call + * is active. The ID of the AFE port is provided in this + * field. + * For a list of valid IDs, refer @xhyperref{Q4,[Q4]}. + */ + + u32 topology_id; +/* Audio COPP topology ID; 32-bit GUID. */ + + u16 dev_num_channel; +/* Number of channels the audio COPP sends to/receives from + * the endpoint. + * Supported values: 1 to 8. + * The value is ignored for the voice processor Tx block, + * where channel + * configuration is derived from the topology ID. + */ + + u16 bit_width; +/* Bit width (in bits) that the audio COPP sends to/receives + * from the + * endpoint. The value is ignored for the voice processing + * Tx block, + * where the PCM width is 16 bits. + */ + + u32 sample_rate; +/* Sampling rate at which the audio COPP/voice processor + * Tx block + * interfaces with the endpoint. + * Supported values for voice processor Tx: 8000, 16000, + * 48000 Hz + * Supported values for audio COPP: >0 and <=192 kHz + */ + + u8 dev_channel_mapping[8]; +/* Array of channel mapping of buffers that the audio COPP + * sends to the endpoint. Channel[i] mapping describes channel + * I inside the buffer, where 0 < i < dev_num_channel. + * This value is relevant only for an audio Rx COPP. + * For the voice processor block and Tx audio block, this field + * is set to zero and is ignored. + */ + + u16 dev_num_channel_eid2; +/* Number of channels the voice processor block sends + * to/receives from the endpoint2. + * Supported values: 1 to 8. + * The value is ignored for audio COPP or if endpoint_id_2 is + * set to 0xFFFF. + */ + + u16 bit_width_eid2; +/* Bit width (in bits) that the voice processor sends + * to/receives from the endpoint2. + * Supported values: 16 and 24. + * The value is ignored for audio COPP or if endpoint_id_2 is + * set to 0xFFFF. + */ + + u32 sample_rate_eid2; +/* Sampling rate at which the voice processor Tx block + * interfaces with the endpoint2. + * Supported values for Tx voice processor: >0 and <=384 kHz + * The value is ignored for audio COPP or if endpoint_id_2 is + * set to 0xFFFF. + */ + + u8 dev_channel_mapping_eid2[8]; +/* Array of channel mapping of buffers that the voice processor + * sends to the endpoint. Channel[i] mapping describes channel + * I inside the buffer, where 0 < i < dev_num_channel. + * This value is relevant only for the Tx voice processor. + * The values are ignored for audio COPP or if endpoint_id_2 is + * set to 0xFFFF. + */ +} __packed; + + +/* ADM device open endpoint payload the + * #ADM_CMD_DEVICE_OPEN_V8 command. + */ +struct adm_device_endpoint_payload { + u16 dev_num_channel; +/* Number of channels the audio COPP sends to/receives from + * the endpoint. + * Supported values: 1 to 32. + * The value is ignored for the voice processor Tx block, + * where channel + * configuration is derived from the topology ID. + */ + + u16 bit_width; +/* Bit width (in bits) that the audio COPP sends to/receives + * from the + * endpoint. The value is ignored for the voice processing + * Tx block, + * where the PCM width is 16 bits. + */ + + u32 sample_rate; +/* Sampling rate at which the audio COPP/voice processor + * Tx block + * interfaces with the endpoint. + * Supported values for voice processor Tx: 8000, 16000, + * 48000 Hz + * Supported values for audio COPP: >0 and <=192 kHz + */ + + u8 dev_channel_mapping[32]; +} __packed; + +/* ADM device open command payload of the + * #ADM_CMD_DEVICE_OPEN_V8 command. + */ +struct adm_cmd_device_open_v8 { + struct apr_hdr hdr; + u16 flags; +/* Bit width Native mode enabled : 11th bit of flag parameter +* If 11th bit of flag is set then that means matrix mixer will be +* running in native mode for bit width for this device session. +* +* Channel Native mode enabled : 12th bit of flag parameter +* If 12th bit of flag is set then that means matrix mixer will be +* running in native mode for channel configuration for this device session. +* All other bits are reserved; clients must set them to 0. +*/ + u16 mode_of_operation; +/* Specifies whether the COPP must be opened on the Tx or Rx + * path. Use the ADM_CMD_COPP_OPEN_MODE_OF_OPERATION_* macros for + * supported values and interpretation. + * Supported values: + * - 0x1 -- Rx path COPP + * - 0x2 -- Tx path live COPP + * - 0x3 -- Tx path nonlive COPP + * Live connections cause sample discarding in the Tx device + * matrix if the destination output ports do not pull them + * fast enough. Nonlive connections queue the samples + * indefinitely. + */ + u32 topology_id; +/* Audio COPP topology ID; 32-bit GUID. */ + + + u16 endpoint_id_1; +/* Logical and physical endpoint ID of the audio path. + * If the ID is a voice processor Tx block, it receives near + * samples. + * Supported values: Any pseudoport, AFE Rx port, + * or AFE Tx port For a list of valid IDs, refer to + * @xhyperref{Q4,[Q4]}. + * Q4 = Hexagon Multimedia: AFE Interface Specification + */ + + u16 endpoint_id_2; +/* Logical and physical endpoint ID 2 for a voice processor + * Tx block. + * This is not applicable to audio COPP. + * Supported values: + * - AFE Rx port + * - 0xFFFF -- Endpoint 2 is unavailable and the voice + * processor Tx + * block ignores this endpoint + * When the voice processor Tx block is created on the audio + * record path, + * it can receive far-end samples from an AFE Rx port if the + * voice call + * is active. The ID of the AFE port is provided in this + * field. + * For a list of valid IDs, refer @xhyperref{Q4,[Q4]}. + */ + + u16 endpoint_id_3; +/* + * Logical and physical endpoint ID of the audio path. + * This indicated afe rx port in ADM loopback use cases. + * In all other use cases this should be set to 0xffff + */ + + u16 reserved; +} __packed; + +/* + * This command allows the client to close a COPP and disconnect + * the device session. + */ +#define ADM_CMD_DEVICE_CLOSE_V5 0x00010327 + +/* Sets one or more parameters to a COPP. */ +#define ADM_CMD_SET_PP_PARAMS_V5 0x00010328 +#define ADM_CMD_SET_PP_PARAMS_V6 0x0001035D + +/* + * Structure of the ADM Set PP Params command. Parameter data must be + * pre-packed with correct header for either V2 or V3 when sent in-band. + * Use q6core_pack_pp_params to pack the header and data correctly depending on + * Instance ID support. + */ +struct adm_cmd_set_pp_params { + /* APR Header */ + struct apr_hdr apr_hdr; + + /* The memory mapping header to be used when sending out of band */ + struct mem_mapping_hdr mem_hdr; + + /* + * Size in bytes of the variable payload accompanying this + * message or + * in shared memory. This is used for parsing the parameter + * payload. + */ + u32 payload_size; + + /* + * Parameter data for in band payload. This should be structured as the + * parameter header immediately followed by the parameter data. Multiple + * parameters can be set in one command by repeating the header followed + * by the data for as many parameters as need to be set. + * Use q6core_pack_pp_params to pack the header and data correctly + * depending on Instance ID support. + */ + u8 param_data[0]; +} __packed; + + +#define ASM_STREAM_CMD_REGISTER_PP_EVENTS 0x00013213 +#define ASM_STREAM_PP_EVENT 0x00013214 +#define ASM_STREAM_CMD_REGISTER_IEC_61937_FMT_UPDATE 0x13333 +#define ASM_IEC_61937_MEDIA_FMT_EVENT 0x13334 + +#define DSP_STREAM_CMD "ADSP Stream Cmd" +#define DSP_STREAM_CALLBACK "ADSP Stream Callback Event" +#define DSP_STREAM_CALLBACK_QUEUE_SIZE 1024 + +struct dsp_stream_callback_list { + struct list_head list; + struct msm_adsp_event_data event; +}; + +struct dsp_stream_callback_prtd { + uint16_t event_count; + struct list_head event_queue; + spinlock_t prtd_spin_lock; +}; + +/* set customized mixing on matrix mixer */ +#define ADM_CMD_SET_PSPD_MTMX_STRTR_PARAMS_V5 0x00010344 +struct adm_cmd_set_pspd_mtmx_strtr_params_v5 { + struct apr_hdr hdr; + /* LSW of parameter data payload address.*/ + u32 payload_addr_lsw; + /* MSW of parameter data payload address.*/ + u32 payload_addr_msw; + /* Memory map handle returned by ADM_CMD_SHARED_MEM_MAP_REGIONS */ + /* command. If mem_map_handle is zero implies the message is in */ + /* the payload */ + u32 mem_map_handle; + /* Size in bytes of the variable payload accompanying this */ + /* message or in shared memory. This is used for parsing the */ + /* parameter payload. */ + u32 payload_size; + u16 direction; + u16 sessionid; + u16 deviceid; + u16 reserved; +} __packed; + +/* set customized mixing on matrix mixer. + * Updated to account for both LSM as well as ASM path. + */ +#define ADM_CMD_SET_PSPD_MTMX_STRTR_PARAMS_V6 0x00010364 +struct adm_cmd_set_pspd_mtmx_strtr_params_v6 { + struct apr_hdr hdr; + /* LSW of parameter data payload address.*/ + u32 payload_addr_lsw; + /* MSW of parameter data payload address.*/ + u32 payload_addr_msw; + /* Memory map handle returned by ADM_CMD_SHARED_MEM_MAP_REGIONS */ + /* command. If mem_map_handle is zero implies the message is in */ + /* the payload */ + u32 mem_map_handle; + /* Size in bytes of the variable payload accompanying this */ + /* message or in shared memory. This is used for parsing the */ + /* parameter payload. */ + u32 payload_size; + u16 direction; + u16 sessionid; + u16 deviceid; + u16 stream_type; +} __packed; +/* Returns the status and COPP ID to an #ADM_CMD_DEVICE_OPEN_V5 command. + */ +#define ADM_CMDRSP_DEVICE_OPEN_V5 0x00010329 + +/* Payload of the #ADM_CMDRSP_DEVICE_OPEN_V5 message, + * which returns the + * status and COPP ID to an #ADM_CMD_DEVICE_OPEN_V5 command. + */ +struct adm_cmd_rsp_device_open_v5 { + u32 status; + /* Status message (error code).*/ + + u16 copp_id; + /* COPP ID: Supported values: 0 <= copp_id < ADM_MAX_COPPS*/ + + u16 reserved; + /* Reserved. This field must be set to zero.*/ +} __packed; + +/* Returns the status and COPP ID to an #ADM_CMD_DEVICE_OPEN_V6 command. */ +#define ADM_CMDRSP_DEVICE_OPEN_V6 0x00010357 + +/* Returns the status and COPP ID to an #ADM_CMD_DEVICE_OPEN_V8 command. */ +#define ADM_CMDRSP_DEVICE_OPEN_V8 0x0001036B + +/* Payload of the #ADM_CMDRSP_DEVICE_OPEN_V6 message, + * which returns the + * status and COPP ID to an #ADM_CMD_DEVICE_OPEN_V6 command + * is the exact same as ADM_CMDRSP_DEVICE_OPEN_V5. + */ + +/* This command allows a query of one COPP parameter. */ +#define ADM_CMD_GET_PP_PARAMS_V5 0x0001032A +#define ADM_CMD_GET_PP_PARAMS_V6 0x0001035E + +/* + * Structure of the ADM Get PP Params command. Parameter header must be + * packed correctly for either V2 or V3. Use q6core_pack_pp_params to pack the + * header correctly depending on Instance ID support. + */ +struct adm_cmd_get_pp_params { + struct apr_hdr apr_hdr; + + /* The memory mapping header to be used when requesting outband */ + struct mem_mapping_hdr mem_hdr; + + /* Parameter header for in band payload. */ + union param_hdrs param_hdr; +} __packed; + +/* Returns parameter values + * in response to an #ADM_CMD_GET_PP_PARAMS_V5 command. + */ +#define ADM_CMDRSP_GET_PP_PARAMS_V5 0x0001032B + +/* Payload of the #ADM_CMDRSP_GET_PP_PARAMS_V5 message, + * which returns parameter values in response + * to an #ADM_CMD_GET_PP_PARAMS_V5 command. + * Immediately following this + * structure is the param_hdr_v1 + * structure containing the pre/postprocessing + * parameter data. For an in-band + * scenario, the variable payload depends + * on the size of the parameter. + */ +struct adm_cmd_rsp_get_pp_params_v5 { + /* Status message (error code).*/ + u32 status; + + /* The header that identifies the subsequent parameter data */ + struct param_hdr_v1 param_hdr; + + /* The parameter data returned */ + u32 param_data[0]; +} __packed; + +/* + * Returns parameter values in response to an #ADM_CMD_GET_PP_PARAMS_V5/6 + * command. + */ +#define ADM_CMDRSP_GET_PP_PARAMS_V6 0x0001035F + +/* + * Payload of the #ADM_CMDRSP_GET_PP_PARAMS_V6 message, + * which returns parameter values in response + * to an #ADM_CMD_GET_PP_PARAMS_V6 command. + * Immediately following this + * structure is the param_hdr_v3 + * structure containing the pre/postprocessing + * parameter data. For an in-band + * scenario, the variable payload depends + * on the size of the parameter. + */ +struct adm_cmd_rsp_get_pp_params_v6 { + /* Status message (error code).*/ + u32 status; + + /* The header that identifies the subsequent parameter data */ + struct param_hdr_v3 param_hdr; + + /* The parameter data returned */ + u32 param_data[0]; +} __packed; + +/* Structure for holding soft stepping volume parameters. */ + +/* + * Payload of the #ASM_PARAM_ID_SOFT_VOL_STEPPING_PARAMETERS + * parameters used by the Volume Control module. + */ + +struct audproc_softvolume_params { + u32 period; + u32 step; + u32 rampingcurve; +} __packed; + +/* + * ID of the Media Format Converter (MFC) module. + * This module supports the following parameter IDs: + * #AUDPROC_PARAM_ID_MFC_OUTPUT_MEDIA_FORMAT + * #AUDPROC_CHMIXER_PARAM_ID_COEFF + */ +#define AUDPROC_MODULE_ID_MFC 0x00010912 + +/* ID of the Output Media Format parameters used by AUDPROC_MODULE_ID_MFC. + * + */ +#define AUDPROC_PARAM_ID_MFC_OUTPUT_MEDIA_FORMAT 0x00010913 + +/* Param ID of Channel Mixer used by AUDPROC_MODULE_ID_MFC */ +#define AUDPROC_CHMIXER_PARAM_ID_COEFF 0x00010342 + + +struct adm_cmd_set_pp_params_v5 { + struct apr_hdr hdr; + u32 payload_addr_lsw; + /* LSW of parameter data payload address.*/ + u32 payload_addr_msw; + /* MSW of parameter data payload address.*/ + + u32 mem_map_handle; + /* + * Memory map handle returned by ADM_CMD_SHARED_MEM_MAP_REGIONS + * command. + * If mem_map_handle is zero implies the message is in + * the payload + */ + + u32 payload_size; + /* + * Size in bytes of the variable payload accompanying this + * message or + * in shared memory. This is used for parsing the parameter + * payload. + */ +} __packed; + +struct audproc_mfc_param_media_fmt { + uint32_t sampling_rate; + uint16_t bits_per_sample; + uint16_t num_channels; + uint16_t channel_type[8]; +} __packed; + +struct audproc_volume_ctrl_master_gain { + /* Linear gain in Q13 format. */ + uint16_t master_gain; + /* Clients must set this field to zero. */ + uint16_t reserved; +} __packed; + +struct audproc_soft_step_volume_params { +/* + * Period in milliseconds. + * Supported values: 0 to 15000 + */ + uint32_t period; +/* + * Step in microseconds. + * Supported values: 0 to 15000000 + */ + uint32_t step; +/* + * Ramping curve type. + * Supported values: + * - #AUDPROC_PARAM_SVC_RAMPINGCURVE_LINEAR + * - #AUDPROC_PARAM_SVC_RAMPINGCURVE_EXP + * - #AUDPROC_PARAM_SVC_RAMPINGCURVE_LOG + */ + uint32_t ramping_curve; +} __packed; + +struct audproc_enable_param_t { + /* + * Specifies whether the Audio processing module is enabled. + * This parameter is generic/common parameter to configure or + * determine the state of any audio processing module. + + * @values 0 : Disable 1: Enable + */ + uint32_t enable; +}; + +/* + * Allows a client to control the gains on various session-to-COPP paths. + */ +#define ADM_CMD_MATRIX_RAMP_GAINS_V5 0x0001032C + +/* + * Allows a client to control the gains on various session-to-COPP paths. + * Maximum support 32 channels + */ +#define ADM_CMD_MATRIX_RAMP_GAINS_V7 0x0001036C + +/* Indicates that the target gain in the + * current adm_session_copp_gain_v5 + * structure is to be applied to all + * the session-to-COPP paths that exist for + * the specified session. + */ +#define ADM_CMD_MATRIX_RAMP_GAINS_COPP_ID_ALL_CONNECTED_COPPS 0xFFFF + +/* Indicates that the target gain is + * to be immediately applied to the + * specified session-to-COPP path, + * without a ramping fashion. + */ +#define ADM_CMD_MATRIX_RAMP_GAINS_RAMP_DURATION_IMMEDIATE 0x0000 + +/* Enumeration for a linear ramping curve.*/ +#define ADM_CMD_MATRIX_RAMP_GAINS_RAMP_CURVE_LINEAR 0x0000 + +/* Payload of the #ADM_CMD_MATRIX_RAMP_GAINS_V5 command. + * Immediately following this structure are num_gains of the + * adm_session_copp_gain_v5structure. + */ +struct adm_cmd_matrix_ramp_gains_v5 { + u32 matrix_id; +/* Specifies whether the matrix ID is Audio Rx (0) or Audio Tx (1). + * Use the ADM_MATRIX_ID_AUDIO_RX or ADM_MATRIX_ID_AUDIOX + * macros to set this field. + */ + + u16 num_gains; + /* Number of gains being applied. */ + + u16 reserved_for_align; + /* Reserved. This field must be set to zero.*/ +} __packed; + +/* Session-to-COPP path gain structure, used by the + * #ADM_CMD_MATRIX_RAMP_GAINS_V5 command. + * This structure specifies the target + * gain (per channel) that must be applied + * to a particular session-to-COPP path in + * the audio matrix. The structure can + * also be used to apply the gain globally + * to all session-to-COPP paths that + * exist for the given session. + * The aDSP uses device channel mapping to + * determine which channel gains to + * use from this command. For example, + * if the device is configured as stereo, + * the aDSP uses only target_gain_ch_1 and + * target_gain_ch_2, and it ignores + * the others. + */ +struct adm_session_copp_gain_v5 { + u16 session_id; +/* Handle of the ASM session. + * Supported values: 1 to 8. + */ + + u16 copp_id; +/* Handle of the COPP. Gain will be applied on the Session ID + * COPP ID path. + */ + + u16 ramp_duration; +/* Duration (in milliseconds) of the ramp over + * which target gains are + * to be applied. Use + * #ADM_CMD_MATRIX_RAMP_GAINS_RAMP_DURATION_IMMEDIATE + * to indicate that gain must be applied immediately. + */ + + u16 step_duration; +/* Duration (in milliseconds) of each step in the ramp. + * This parameter is ignored if ramp_duration is equal to + * #ADM_CMD_MATRIX_RAMP_GAINS_RAMP_DURATION_IMMEDIATE. + * Supported value: 1 + */ + + u16 ramp_curve; +/* Type of ramping curve. + * Supported value: #ADM_CMD_MATRIX_RAMP_GAINS_RAMP_CURVE_LINEAR + */ + + u16 reserved_for_align; + /* Reserved. This field must be set to zero. */ + + u16 target_gain_ch_1; + /* Target linear gain for channel 1 in Q13 format; */ + + u16 target_gain_ch_2; + /* Target linear gain for channel 2 in Q13 format; */ + + u16 target_gain_ch_3; + /* Target linear gain for channel 3 in Q13 format; */ + + u16 target_gain_ch_4; + /* Target linear gain for channel 4 in Q13 format; */ + + u16 target_gain_ch_5; + /* Target linear gain for channel 5 in Q13 format; */ + + u16 target_gain_ch_6; + /* Target linear gain for channel 6 in Q13 format; */ + + u16 target_gain_ch_7; + /* Target linear gain for channel 7 in Q13 format; */ + + u16 target_gain_ch_8; + /* Target linear gain for channel 8 in Q13 format; */ +} __packed; + +/* Payload of the #ADM_CMD_MATRIX_RAMP_GAINS_V7 command. + * Immediately following this structure are num_gains of the + * adm_session_copp_gain_v5structure. + */ +struct adm_cmd_matrix_ramp_gains_v7 { + struct apr_hdr hdr; + u32 matrix_id; +/* Specifies whether the matrix ID is Audio Rx (0) or Audio Tx (1). + * Use the ADM_MATRIX_ID_AUDIO_RX or ADM_MATRIX_ID_AUDIOX + * macros to set this field. +*/ + + u16 num_gains; + /* Number of gains being applied. */ + + u16 reserved_for_align; + /* Reserved. This field must be set to zero.*/ +} __packed; + +/* Session-to-COPP path gain structure, used by the + * #ADM_CMD_MATRIX_RAMP_GAINS_V7 command. + * This structure specifies the target + * gain (per channel) that must be applied + * to a particular session-to-COPP path in + * the audio matrix. The structure can + * also be used to apply the gain globally + * to all session-to-COPP paths that + * exist for the given session. + * The aDSP uses device channel mapping to + * determine which channel gains to + * use from this command. For example, + * if the device is configured as stereo, + * the aDSP uses only target_gain_ch_1 and + * target_gain_ch_2, and it ignores + * the others. + */ +struct adm_session_copp_gain_v7 { + u16 session_id; +/* Handle of the ASM session. + * Supported values: 1 to 8. + */ + + u16 copp_id; +/* Handle of the COPP. Gain will be applied on the Session ID + * COPP ID path. + */ + + u16 ramp_duration; +/* Duration (in milliseconds) of the ramp over + * which target gains are + * to be applied. Use + * #ADM_CMD_MATRIX_RAMP_GAINS_RAMP_DURATION_IMMEDIATE + * to indicate that gain must be applied immediately. + */ + + u16 step_duration; +/* Duration (in milliseconds) of each step in the ramp. + * This parameter is ignored if ramp_duration is equal to + * #ADM_CMD_MATRIX_RAMP_GAINS_RAMP_DURATION_IMMEDIATE. + * Supported value: 1 + */ + + u16 ramp_curve; +/* Type of ramping curve. + * Supported value: #ADM_CMD_MATRIX_RAMP_GAINS_RAMP_CURVE_LINEAR + */ + + u16 stream_type; +/* Type of stream_type. + * Supported value: #STREAM_TYPE_ASM STREAM_TYPE_LSM + */ + u16 num_channels; +/* Number of channels on which gain needs to be applied. + * Supported value: 1 to 32. + */ + u16 reserved_for_align; + /* Reserved. This field must be set to zero. */ +} __packed; + +/* Allows to set mute/unmute on various session-to-COPP paths. + * For every session-to-COPP path (stream-device interconnection), + * mute/unmute can be set individually on the output channels. + */ +#define ADM_CMD_MATRIX_MUTE_V5 0x0001032D + +/* Allows to set mute/unmute on various session-to-COPP paths. + * For every session-to-COPP path (stream-device interconnection), + * mute/unmute can be set individually on the output channels. + */ +#define ADM_CMD_MATRIX_MUTE_V7 0x0001036D + +/* Indicates that mute/unmute in the + * current adm_session_copp_mute_v5structure + * is to be applied to all the session-to-COPP + * paths that exist for the specified session. + */ +#define ADM_CMD_MATRIX_MUTE_COPP_ID_ALL_CONNECTED_COPPS 0xFFFF + +/* Payload of the #ADM_CMD_MATRIX_MUTE_V5 command*/ +struct adm_cmd_matrix_mute_v5 { + u32 matrix_id; +/* Specifies whether the matrix ID is Audio Rx (0) or Audio Tx (1). + * Use the ADM_MATRIX_ID_AUDIO_RX or ADM_MATRIX_ID_AUDIOX + * macros to set this field. + */ + + u16 session_id; +/* Handle of the ASM session. + * Supported values: 1 to 8. + */ + + u16 copp_id; +/* Handle of the COPP. + * Use ADM_CMD_MATRIX_MUTE_COPP_ID_ALL_CONNECTED_COPPS + * to indicate that mute/unmute must be applied to + * all the COPPs connected to session_id. + * Supported values: + * - 0xFFFF -- Apply mute/unmute to all connected COPPs + * - Other values -- Valid COPP ID + */ + + u8 mute_flag_ch_1; + /* Mute flag for channel 1 is set to unmute (0) or mute (1). */ + + u8 mute_flag_ch_2; + /* Mute flag for channel 2 is set to unmute (0) or mute (1). */ + + u8 mute_flag_ch_3; + /* Mute flag for channel 3 is set to unmute (0) or mute (1). */ + + u8 mute_flag_ch_4; + /* Mute flag for channel 4 is set to unmute (0) or mute (1). */ + + u8 mute_flag_ch_5; + /* Mute flag for channel 5 is set to unmute (0) or mute (1). */ + + u8 mute_flag_ch_6; + /* Mute flag for channel 6 is set to unmute (0) or mute (1). */ + + u8 mute_flag_ch_7; + /* Mute flag for channel 7 is set to unmute (0) or mute (1). */ + + u8 mute_flag_ch_8; + /* Mute flag for channel 8 is set to unmute (0) or mute (1). */ + + u16 ramp_duration; +/* Period (in milliseconds) over which the soft mute/unmute will be + * applied. + * Supported values: 0 (Default) to 0xFFFF + * The default of 0 means mute/unmute will be applied immediately. + */ + + u16 reserved_for_align; + /* Clients must set this field to zero.*/ +} __packed; + + +/* Payload of the #ADM_CMD_MATRIX_MUTE_V7 command*/ +struct adm_cmd_matrix_mute_v7 { + struct apr_hdr hdr; + u32 matrix_id; +/* Specifies whether the matrix ID is Audio Rx (0) or Audio Tx (1). + * Use the ADM_MATRIX_ID_AUDIO_RX or ADM_MATRIX_ID_AUDIOX + * macros to set this field. + */ + + u16 session_id; +/* Handle of the ASM session. + * Supported values: 1 to . + */ + + u16 copp_id; +/* Handle of the COPP. + * Use ADM_CMD_MATRIX_MUTE_COPP_ID_ALL_CONNECTED_COPPS + * to indicate that mute/unmute must be applied to + * all the COPPs connected to session_id. + * Supported values: + * - 0xFFFF -- Apply mute/unmute to all connected COPPs + * - Other values -- Valid COPP ID + */ + + u16 ramp_duration; +/* Duration (in milliseconds) of the ramp over + * which target gains are + * to be applied. Use + * #ADM_CMD_MATRIX_RAMP_GAINS_RAMP_DURATION_IMMEDIATE + * to indicate that gain must be applied immediately. + */ + + u16 stream_type; +/* Specify whether the stream type is connectedon the ASM or LSM + * Supported value: 1 + */ + u16 num_channels; +/* Number of channels on which gain needs to be applied + * Supported value: 1 to 32 + */ +} __packed; + + +#define ASM_PARAM_ID_AAC_STEREO_MIX_COEFF_SELECTION_FLAG_V2 (0x00010DD8) + +struct asm_aac_stereo_mix_coeff_selection_param_v2 { + struct apr_hdr hdr; + u32 param_id; + u32 param_size; + u32 aac_stereo_mix_coeff_flag; +} __packed; + +/* Allows a client to connect the desired stream to + * the desired AFE port through the stream router + * + * This command allows the client to connect specified session to + * specified AFE port. This is used for compressed streams only + * opened using the #ASM_STREAM_CMD_OPEN_WRITE_COMPRESSED or + * #ASM_STREAM_CMD_OPEN_READ_COMPRESSED command. + * + * @prerequisites + * Session ID and AFE Port ID must be valid. + * #ASM_STREAM_CMD_OPEN_WRITE_COMPRESSED or + * #ASM_STREAM_CMD_OPEN_READ_COMPRESSED + * must have been called on this session. + */ + +#define ADM_CMD_CONNECT_AFE_PORT_V5 0x0001032E +#define ADM_CMD_DISCONNECT_AFE_PORT_V5 0x0001032F +/* Enumeration for the Rx stream router ID.*/ +#define ADM_STRTR_ID_RX 0 +/* Enumeration for the Tx stream router ID.*/ +#define ADM_STRTR_IDX 1 + +/* Payload of the #ADM_CMD_CONNECT_AFE_PORT_V5 command.*/ +struct adm_cmd_connect_afe_port_v5 { + struct apr_hdr hdr; + u8 mode; +/* ID of the stream router (RX/TX). Use the + * ADM_STRTR_ID_RX or ADM_STRTR_IDX macros + * to set this field. + */ + + u8 session_id; + /* Session ID of the stream to connect */ + + u16 afe_port_id; + /* Port ID of the AFE port to connect to.*/ + u32 num_channels; +/* Number of device channels + * Supported values: 2(Audio Sample Packet), + * 8 (HBR Audio Stream Sample Packet) + */ + + u32 sampling_rate; +/* Device sampling rate + * Supported values: Any + */ +} __packed; + + +/* adsp_adm_api.h */ + + +/* Port ID. Update afe_get_port_index + * when a new port is added here. + */ +#define PRIMARY_I2S_RX 0 +#define PRIMARY_I2S_TX 1 +#define SECONDARY_I2S_RX 4 +#define SECONDARY_I2S_TX 5 +#define MI2S_RX 6 +#define MI2S_TX 7 +#define HDMI_RX 8 +#define RSVD_2 9 +#define RSVD_3 10 +#define DIGI_MIC_TX 11 +#define VOICE2_PLAYBACK_TX 0x8002 +#define VOICE_RECORD_RX 0x8003 +#define VOICE_RECORD_TX 0x8004 +#define VOICE_PLAYBACK_TX 0x8005 + +/* Slimbus Multi channel port id pool */ +#define SLIMBUS_0_RX 0x4000 +#define SLIMBUS_0_TX 0x4001 +#define SLIMBUS_1_RX 0x4002 +#define SLIMBUS_1_TX 0x4003 +#define SLIMBUS_2_RX 0x4004 +#define SLIMBUS_2_TX 0x4005 +#define SLIMBUS_3_RX 0x4006 +#define SLIMBUS_3_TX 0x4007 +#define SLIMBUS_4_RX 0x4008 +#define SLIMBUS_4_TX 0x4009 +#define SLIMBUS_5_RX 0x400a +#define SLIMBUS_5_TX 0x400b +#define SLIMBUS_6_RX 0x400c +#define SLIMBUS_6_TX 0x400d +#define SLIMBUS_7_RX 0x400e +#define SLIMBUS_7_TX 0x400f +#define SLIMBUS_8_RX 0x4010 +#define SLIMBUS_8_TX 0x4011 +#define SLIMBUS_9_RX 0x4012 +#define SLIMBUS_9_TX 0x4013 +#define SLIMBUS_PORT_LAST SLIMBUS_9_TX +#define INT_BT_SCO_RX 0x3000 +#define INT_BT_SCO_TX 0x3001 +#define INT_BT_A2DP_RX 0x3002 +#define INT_FM_RX 0x3004 +#define INT_FM_TX 0x3005 +#define RT_PROXY_PORT_001_RX 0x2000 +#define RT_PROXY_PORT_001_TX 0x2001 +#define DISPLAY_PORT_RX 0x6020 + +#define AFE_PORT_INVALID 0xFFFF +#define SLIMBUS_INVALID AFE_PORT_INVALID + +#define AFE_PORT_CMD_START 0x000100ca + +#define AFE_EVENT_RTPORT_START 0 +#define AFE_EVENT_RTPORT_STOP 1 +#define AFE_EVENT_RTPORT_LOW_WM 2 +#define AFE_EVENT_RTPORT_HI_WM 3 + +#define ADSP_AFE_VERSION 0x00200000 + +/* Size of the range of port IDs for the audio interface. */ +#define AFE_PORT_ID_AUDIO_IF_PORT_RANGE_SIZE 0xF + +/* Size of the range of port IDs for internal BT-FM ports. */ +#define AFE_PORT_ID_INTERNAL_BT_FM_RANGE_SIZE 0x6 + +/* Size of the range of port IDs for SLIMbus® + * multichannel + * ports. + */ +#define AFE_PORT_ID_SLIMBUS_RANGE_SIZE 0xA + +/* Size of the range of port IDs for real-time proxy ports. */ +#define AFE_PORT_ID_RT_PROXY_PORT_RANGE_SIZE 0x2 + +/* Size of the range of port IDs for pseudoports. */ +#define AFE_PORT_ID_PSEUDOPORT_RANGE_SIZE 0x5 + +/* Start of the range of port IDs for the audio interface. */ +#define AFE_PORT_ID_AUDIO_IF_PORT_RANGE_START 0x1000 + +/* End of the range of port IDs for the audio interface. */ +#define AFE_PORT_ID_AUDIO_IF_PORT_RANGE_END \ + (AFE_PORT_ID_AUDIO_IF_PORT_RANGE_START +\ + AFE_PORT_ID_AUDIO_IF_PORT_RANGE_SIZE - 1) + +/* Start of the range of port IDs for real-time proxy ports. */ +#define AFE_PORT_ID_RT_PROXY_PORT_RANGE_START 0x2000 + +/* End of the range of port IDs for real-time proxy ports. */ +#define AFE_PORT_ID_RT_PROXY_PORT_RANGE_END \ + (AFE_PORT_ID_RT_PROXY_PORT_RANGE_START +\ + AFE_PORT_ID_RT_PROXY_PORT_RANGE_SIZE-1) + +/* Start of the range of port IDs for internal BT-FM devices. */ +#define AFE_PORT_ID_INTERNAL_BT_FM_RANGE_START 0x3000 + +/* End of the range of port IDs for internal BT-FM devices. */ +#define AFE_PORT_ID_INTERNAL_BT_FM_RANGE_END \ + (AFE_PORT_ID_INTERNAL_BT_FM_RANGE_START +\ + AFE_PORT_ID_INTERNAL_BT_FM_RANGE_SIZE-1) + +/* Start of the range of port IDs for SLIMbus devices. */ +#define AFE_PORT_ID_SLIMBUS_RANGE_START 0x4000 + +/* End of the range of port IDs for SLIMbus devices. */ +#define AFE_PORT_ID_SLIMBUS_RANGE_END \ + (AFE_PORT_ID_SLIMBUS_RANGE_START +\ + AFE_PORT_ID_SLIMBUS_RANGE_SIZE-1) + +/* Start of the range of port IDs for pseudoports. */ +#define AFE_PORT_ID_PSEUDOPORT_RANGE_START 0x8001 + +/* End of the range of port IDs for pseudoports. */ +#define AFE_PORT_ID_PSEUDOPORT_RANGE_END \ + (AFE_PORT_ID_PSEUDOPORT_RANGE_START +\ + AFE_PORT_ID_PSEUDOPORT_RANGE_SIZE-1) + +/* Start of the range of port IDs for TDM devices. */ +#define AFE_PORT_ID_TDM_PORT_RANGE_START 0x9000 + +/* End of the range of port IDs for TDM devices. */ +#define AFE_PORT_ID_TDM_PORT_RANGE_END \ + (AFE_PORT_ID_TDM_PORT_RANGE_START+0x50-1) + +/* Size of the range of port IDs for TDM ports. */ +#define AFE_PORT_ID_TDM_PORT_RANGE_SIZE \ + (AFE_PORT_ID_TDM_PORT_RANGE_END - \ + AFE_PORT_ID_TDM_PORT_RANGE_START+1) + +#define AFE_PORT_ID_PRIMARY_MI2S_RX 0x1000 +#define AFE_PORT_ID_PRIMARY_MI2S_TX 0x1001 +#define AFE_PORT_ID_SECONDARY_MI2S_RX 0x1002 +#define AFE_PORT_ID_SECONDARY_MI2S_TX 0x1003 +#define AFE_PORT_ID_TERTIARY_MI2S_RX 0x1004 +#define AFE_PORT_ID_TERTIARY_MI2S_TX 0x1005 +#define AFE_PORT_ID_QUATERNARY_MI2S_RX 0x1006 +#define AFE_PORT_ID_QUATERNARY_MI2S_TX 0x1007 +#define AUDIO_PORT_ID_I2S_RX 0x1008 +#define AFE_PORT_ID_DIGITAL_MIC_TX 0x1009 +#define AFE_PORT_ID_PRIMARY_PCM_RX 0x100A +#define AFE_PORT_ID_PRIMARY_PCM_TX 0x100B +#define AFE_PORT_ID_SECONDARY_PCM_RX 0x100C +#define AFE_PORT_ID_SECONDARY_PCM_TX 0x100D +#define AFE_PORT_ID_MULTICHAN_HDMI_RX 0x100E +#define AFE_PORT_ID_SECONDARY_MI2S_RX_SD1 0x1010 +#define AFE_PORT_ID_TERTIARY_PCM_RX 0x1012 +#define AFE_PORT_ID_TERTIARY_PCM_TX 0x1013 +#define AFE_PORT_ID_QUATERNARY_PCM_RX 0x1014 +#define AFE_PORT_ID_QUATERNARY_PCM_TX 0x1015 +#define AFE_PORT_ID_QUINARY_MI2S_RX 0x1016 +#define AFE_PORT_ID_QUINARY_MI2S_TX 0x1017 +/* ID of the senary MI2S Rx port. */ +#define AFE_PORT_ID_SENARY_MI2S_RX 0x1018 +/* ID of the senary MI2S Tx port. */ +#define AFE_PORT_ID_SENARY_MI2S_TX 0x1019 + +/* ID of the Internal 0 MI2S Rx port */ +#define AFE_PORT_ID_INT0_MI2S_RX 0x102E +/* ID of the Internal 0 MI2S Tx port */ +#define AFE_PORT_ID_INT0_MI2S_TX 0x102F +/* ID of the Internal 1 MI2S Rx port */ +#define AFE_PORT_ID_INT1_MI2S_RX 0x1030 +/* ID of the Internal 1 MI2S Tx port */ +#define AFE_PORT_ID_INT1_MI2S_TX 0x1031 +/* ID of the Internal 2 MI2S Rx port */ +#define AFE_PORT_ID_INT2_MI2S_RX 0x1032 +/* ID of the Internal 2 MI2S Tx port */ +#define AFE_PORT_ID_INT2_MI2S_TX 0x1033 +/* ID of the Internal 3 MI2S Rx port */ +#define AFE_PORT_ID_INT3_MI2S_RX 0x1034 +/* ID of the Internal 3 MI2S Tx port */ +#define AFE_PORT_ID_INT3_MI2S_TX 0x1035 +/* ID of the Internal 4 MI2S Rx port */ +#define AFE_PORT_ID_INT4_MI2S_RX 0x1036 +/* ID of the Internal 4 MI2S Tx port */ +#define AFE_PORT_ID_INT4_MI2S_TX 0x1037 +/* ID of the Internal 5 MI2S Rx port */ +#define AFE_PORT_ID_INT5_MI2S_RX 0x1038 +/* ID of the Internal 5 MI2S Tx port */ +#define AFE_PORT_ID_INT5_MI2S_TX 0x1039 +/* ID of the Internal 6 MI2S Rx port */ +#define AFE_PORT_ID_INT6_MI2S_RX 0x103A +/* ID of the Internal 6 MI2S Tx port */ +#define AFE_PORT_ID_INT6_MI2S_TX 0x103B + +#define AFE_PORT_ID_QUINARY_PCM_RX 0x103C +#define AFE_PORT_ID_QUINARY_PCM_TX 0x103D + +/* ID of the senary auxiliary PCM Rx port. */ +#define AFE_PORT_ID_SENARY_PCM_RX 0x103E +/* ID of the senary auxiliary PCM Tx port. */ +#define AFE_PORT_ID_SENARY_PCM_TX 0x103F + +#define AFE_PORT_ID_PRIMARY_SPDIF_RX 0x5000 +#define AFE_PORT_ID_PRIMARY_SPDIF_TX 0x5001 +#define AFE_PORT_ID_SECONDARY_SPDIF_RX 0x5002 +#define AFE_PORT_ID_SECONDARY_SPDIF_TX 0x5003 +#define AFE_PORT_ID_SPDIF_RX AFE_PORT_ID_PRIMARY_SPDIF_RX + +#define AFE_PORT_ID_RT_PROXY_PORT_001_RX 0x2000 +#define AFE_PORT_ID_RT_PROXY_PORT_001_TX 0x2001 +#define AFE_PORT_ID_INTERNAL_BT_SCO_RX 0x3000 +#define AFE_PORT_ID_INTERNAL_BT_SCO_TX 0x3001 +#define AFE_PORT_ID_INTERNAL_BT_A2DP_RX 0x3002 +#define AFE_PORT_ID_INTERNAL_FM_RX 0x3004 +#define AFE_PORT_ID_INTERNAL_FM_TX 0x3005 +/* SLIMbus Rx port on channel 0. */ +#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_0_RX 0x4000 +/* SLIMbus Tx port on channel 0. */ +#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_0_TX 0x4001 +/* SLIMbus Rx port on channel 1. */ +#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_1_RX 0x4002 +/* SLIMbus Tx port on channel 1. */ +#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_1_TX 0x4003 +/* SLIMbus Rx port on channel 2. */ +#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_2_RX 0x4004 +/* SLIMbus Tx port on channel 2. */ +#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_2_TX 0x4005 +/* SLIMbus Rx port on channel 3. */ +#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_3_RX 0x4006 +/* SLIMbus Tx port on channel 3. */ +#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_3_TX 0x4007 +/* SLIMbus Rx port on channel 4. */ +#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_4_RX 0x4008 +/* SLIMbus Tx port on channel 4. */ +#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_4_TX 0x4009 +/* SLIMbus Rx port on channel 5. */ +#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_5_RX 0x400a +/* SLIMbus Tx port on channel 5. */ +#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_5_TX 0x400b +/* SLIMbus Rx port on channel 6. */ +#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_6_RX 0x400c +/* SLIMbus Tx port on channel 6. */ +#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_6_TX 0x400d +/* SLIMbus Rx port on channel 7. */ +#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_7_RX 0x400e +/* SLIMbus Tx port on channel 7. */ +#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_7_TX 0x400f +/* SLIMbus Rx port on channel 8. */ +#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_8_RX 0x4010 +/* SLIMbus Tx port on channel 8. */ +#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_8_TX 0x4011 +/* SLIMbus Rx port on channel 9. */ +#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_9_RX 0x4012 +/* SLIMbus Tx port on channel 9. */ +#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_9_TX 0x4013 +/* AFE Rx port for audio over Display port */ +#define AFE_PORT_ID_HDMI_OVER_DP_RX 0x6020 +/*USB AFE port */ +#define AFE_PORT_ID_USB_RX 0x7000 +#define AFE_PORT_ID_USB_TX 0x7001 + +/* AFE WSA Codec DMA Rx port 0 */ +#define AFE_PORT_ID_WSA_CODEC_DMA_RX_0 0xB000 + +/* AFE WSA Codec DMA Tx port 0 */ +#define AFE_PORT_ID_WSA_CODEC_DMA_TX_0 0xB001 + +/* AFE WSA Codec DMA Rx port 1 */ +#define AFE_PORT_ID_WSA_CODEC_DMA_RX_1 0xB002 + +/* AFE WSA Codec DMA Tx port 1 */ +#define AFE_PORT_ID_WSA_CODEC_DMA_TX_1 0xB003 + +/* AFE WSA Codec DMA Tx port 2 */ +#define AFE_PORT_ID_WSA_CODEC_DMA_TX_2 0xB005 + +/* AFE VA Codec DMA Tx port 0 */ +#define AFE_PORT_ID_VA_CODEC_DMA_TX_0 0xB021 + +/* AFE VA Codec DMA Tx port 1 */ +#define AFE_PORT_ID_VA_CODEC_DMA_TX_1 0xB023 + +/* AFE Rx Codec DMA Rx port 0 */ +#define AFE_PORT_ID_RX_CODEC_DMA_RX_0 0xB030 + +/* AFE Tx Codec DMA Tx port 0 */ +#define AFE_PORT_ID_TX_CODEC_DMA_TX_0 0xB031 + +/* AFE Rx Codec DMA Rx port 1 */ +#define AFE_PORT_ID_RX_CODEC_DMA_RX_1 0xB032 + +/* AFE Tx Codec DMA Tx port 1 */ +#define AFE_PORT_ID_TX_CODEC_DMA_TX_1 0xB033 + +/* AFE Rx Codec DMA Rx port 2 */ +#define AFE_PORT_ID_RX_CODEC_DMA_RX_2 0xB034 + +/* AFE Tx Codec DMA Tx port 2 */ +#define AFE_PORT_ID_TX_CODEC_DMA_TX_2 0xB035 + +/* AFE Rx Codec DMA Rx port 3 */ +#define AFE_PORT_ID_RX_CODEC_DMA_RX_3 0xB036 + +/* AFE Tx Codec DMA Tx port 3 */ +#define AFE_PORT_ID_TX_CODEC_DMA_TX_3 0xB037 + +/* AFE Rx Codec DMA Rx port 4 */ +#define AFE_PORT_ID_RX_CODEC_DMA_RX_4 0xB038 + +/* AFE Tx Codec DMA Tx port 4 */ +#define AFE_PORT_ID_TX_CODEC_DMA_TX_4 0xB039 + +/* AFE Rx Codec DMA Rx port 5 */ +#define AFE_PORT_ID_RX_CODEC_DMA_RX_5 0xB03A + +/* AFE Tx Codec DMA Tx port 5 */ +#define AFE_PORT_ID_TX_CODEC_DMA_TX_5 0xB03B + +/* AFE Rx Codec DMA Rx port 6 */ +#define AFE_PORT_ID_RX_CODEC_DMA_RX_6 0xB03C + +/* AFE Rx Codec DMA Rx port 7 */ +#define AFE_PORT_ID_RX_CODEC_DMA_RX_7 0xB03E + +/* Generic pseudoport 1. */ +#define AFE_PORT_ID_PSEUDOPORT_01 0x8001 +/* Generic pseudoport 2. */ +#define AFE_PORT_ID_PSEUDOPORT_02 0x8002 + +/* @xreflabel{hdr:AfePortIdPrimaryAuxPcmTx} + * Primary Aux PCM Tx port ID. + */ +#define AFE_PORT_ID_PRIMARY_PCM_TX 0x100B +/* Pseudoport that corresponds to the voice Rx path. + * For recording, the voice Rx path samples are written to this + * port and consumed by the audio path. + */ + +#define AFE_PORT_ID_VOICE_RECORD_RX 0x8003 + +/* Pseudoport that corresponds to the voice Tx path. + * For recording, the voice Tx path samples are written to this + * port and consumed by the audio path. + */ + +#define AFE_PORT_ID_VOICE_RECORD_TX 0x8004 +/* Pseudoport that corresponds to in-call voice delivery samples. + * During in-call audio delivery, the audio path delivers samples + * to this port from where the voice path delivers them on the + * Rx path. + */ +#define AFE_PORT_ID_VOICE2_PLAYBACK_TX 0x8002 +#define AFE_PORT_ID_VOICE_PLAYBACK_TX 0x8005 + +#define AFE_PORT_ID_PRIMARY_TDM_RX \ + (AFE_PORT_ID_TDM_PORT_RANGE_START + 0x00) +#define AFE_PORT_ID_PRIMARY_TDM_RX_1 \ + (AFE_PORT_ID_PRIMARY_TDM_RX + 0x02) +#define AFE_PORT_ID_PRIMARY_TDM_RX_2 \ + (AFE_PORT_ID_PRIMARY_TDM_RX + 0x04) +#define AFE_PORT_ID_PRIMARY_TDM_RX_3 \ + (AFE_PORT_ID_PRIMARY_TDM_RX + 0x06) +#define AFE_PORT_ID_PRIMARY_TDM_RX_4 \ + (AFE_PORT_ID_PRIMARY_TDM_RX + 0x08) +#define AFE_PORT_ID_PRIMARY_TDM_RX_5 \ + (AFE_PORT_ID_PRIMARY_TDM_RX + 0x0A) +#define AFE_PORT_ID_PRIMARY_TDM_RX_6 \ + (AFE_PORT_ID_PRIMARY_TDM_RX + 0x0C) +#define AFE_PORT_ID_PRIMARY_TDM_RX_7 \ + (AFE_PORT_ID_PRIMARY_TDM_RX + 0x0E) + +#define AFE_PORT_ID_PRIMARY_TDM_TX \ + (AFE_PORT_ID_TDM_PORT_RANGE_START + 0x01) +#define AFE_PORT_ID_PRIMARY_TDM_TX_1 \ + (AFE_PORT_ID_PRIMARY_TDM_TX + 0x02) +#define AFE_PORT_ID_PRIMARY_TDM_TX_2 \ + (AFE_PORT_ID_PRIMARY_TDM_TX + 0x04) +#define AFE_PORT_ID_PRIMARY_TDM_TX_3 \ + (AFE_PORT_ID_PRIMARY_TDM_TX + 0x06) +#define AFE_PORT_ID_PRIMARY_TDM_TX_4 \ + (AFE_PORT_ID_PRIMARY_TDM_TX + 0x08) +#define AFE_PORT_ID_PRIMARY_TDM_TX_5 \ + (AFE_PORT_ID_PRIMARY_TDM_TX + 0x0A) +#define AFE_PORT_ID_PRIMARY_TDM_TX_6 \ + (AFE_PORT_ID_PRIMARY_TDM_TX + 0x0C) +#define AFE_PORT_ID_PRIMARY_TDM_TX_7 \ + (AFE_PORT_ID_PRIMARY_TDM_TX + 0x0E) + +#define AFE_PORT_ID_SECONDARY_TDM_RX \ + (AFE_PORT_ID_TDM_PORT_RANGE_START + 0x10) +#define AFE_PORT_ID_SECONDARY_TDM_RX_1 \ + (AFE_PORT_ID_SECONDARY_TDM_RX + 0x02) +#define AFE_PORT_ID_SECONDARY_TDM_RX_2 \ + (AFE_PORT_ID_SECONDARY_TDM_RX + 0x04) +#define AFE_PORT_ID_SECONDARY_TDM_RX_3 \ + (AFE_PORT_ID_SECONDARY_TDM_RX + 0x06) +#define AFE_PORT_ID_SECONDARY_TDM_RX_4 \ + (AFE_PORT_ID_SECONDARY_TDM_RX + 0x08) +#define AFE_PORT_ID_SECONDARY_TDM_RX_5 \ + (AFE_PORT_ID_SECONDARY_TDM_RX + 0x0A) +#define AFE_PORT_ID_SECONDARY_TDM_RX_6 \ + (AFE_PORT_ID_SECONDARY_TDM_RX + 0x0C) +#define AFE_PORT_ID_SECONDARY_TDM_RX_7 \ + (AFE_PORT_ID_SECONDARY_TDM_RX + 0x0E) + +#define AFE_PORT_ID_SECONDARY_TDM_TX \ + (AFE_PORT_ID_TDM_PORT_RANGE_START + 0x11) +#define AFE_PORT_ID_SECONDARY_TDM_TX_1 \ + (AFE_PORT_ID_SECONDARY_TDM_TX + 0x02) +#define AFE_PORT_ID_SECONDARY_TDM_TX_2 \ + (AFE_PORT_ID_SECONDARY_TDM_TX + 0x04) +#define AFE_PORT_ID_SECONDARY_TDM_TX_3 \ + (AFE_PORT_ID_SECONDARY_TDM_TX + 0x06) +#define AFE_PORT_ID_SECONDARY_TDM_TX_4 \ + (AFE_PORT_ID_SECONDARY_TDM_TX + 0x08) +#define AFE_PORT_ID_SECONDARY_TDM_TX_5 \ + (AFE_PORT_ID_SECONDARY_TDM_TX + 0x0A) +#define AFE_PORT_ID_SECONDARY_TDM_TX_6 \ + (AFE_PORT_ID_SECONDARY_TDM_TX + 0x0C) +#define AFE_PORT_ID_SECONDARY_TDM_TX_7 \ + (AFE_PORT_ID_SECONDARY_TDM_TX + 0x0E) + +#define AFE_PORT_ID_TERTIARY_TDM_RX \ + (AFE_PORT_ID_TDM_PORT_RANGE_START + 0x20) +#define AFE_PORT_ID_TERTIARY_TDM_RX_1 \ + (AFE_PORT_ID_TERTIARY_TDM_RX + 0x02) +#define AFE_PORT_ID_TERTIARY_TDM_RX_2 \ + (AFE_PORT_ID_TERTIARY_TDM_RX + 0x04) +#define AFE_PORT_ID_TERTIARY_TDM_RX_3 \ + (AFE_PORT_ID_TERTIARY_TDM_RX + 0x06) +#define AFE_PORT_ID_TERTIARY_TDM_RX_4 \ + (AFE_PORT_ID_TERTIARY_TDM_RX + 0x08) +#define AFE_PORT_ID_TERTIARY_TDM_RX_5 \ + (AFE_PORT_ID_TERTIARY_TDM_RX + 0x0A) +#define AFE_PORT_ID_TERTIARY_TDM_RX_6 \ + (AFE_PORT_ID_TERTIARY_TDM_RX + 0x0C) +#define AFE_PORT_ID_TERTIARY_TDM_RX_7 \ + (AFE_PORT_ID_TERTIARY_TDM_RX + 0x0E) + +#define AFE_PORT_ID_TERTIARY_TDM_TX \ + (AFE_PORT_ID_TDM_PORT_RANGE_START + 0x21) +#define AFE_PORT_ID_TERTIARY_TDM_TX_1 \ + (AFE_PORT_ID_TERTIARY_TDM_TX + 0x02) +#define AFE_PORT_ID_TERTIARY_TDM_TX_2 \ + (AFE_PORT_ID_TERTIARY_TDM_TX + 0x04) +#define AFE_PORT_ID_TERTIARY_TDM_TX_3 \ + (AFE_PORT_ID_TERTIARY_TDM_TX + 0x06) +#define AFE_PORT_ID_TERTIARY_TDM_TX_4 \ + (AFE_PORT_ID_TERTIARY_TDM_TX + 0x08) +#define AFE_PORT_ID_TERTIARY_TDM_TX_5 \ + (AFE_PORT_ID_TERTIARY_TDM_TX + 0x0A) +#define AFE_PORT_ID_TERTIARY_TDM_TX_6 \ + (AFE_PORT_ID_TERTIARY_TDM_TX + 0x0C) +#define AFE_PORT_ID_TERTIARY_TDM_TX_7 \ + (AFE_PORT_ID_TERTIARY_TDM_TX + 0x0E) + +#define AFE_PORT_ID_QUATERNARY_TDM_RX \ + (AFE_PORT_ID_TDM_PORT_RANGE_START + 0x30) +#define AFE_PORT_ID_QUATERNARY_TDM_RX_1 \ + (AFE_PORT_ID_QUATERNARY_TDM_RX + 0x02) +#define AFE_PORT_ID_QUATERNARY_TDM_RX_2 \ + (AFE_PORT_ID_QUATERNARY_TDM_RX + 0x04) +#define AFE_PORT_ID_QUATERNARY_TDM_RX_3 \ + (AFE_PORT_ID_QUATERNARY_TDM_RX + 0x06) +#define AFE_PORT_ID_QUATERNARY_TDM_RX_4 \ + (AFE_PORT_ID_QUATERNARY_TDM_RX + 0x08) +#define AFE_PORT_ID_QUATERNARY_TDM_RX_5 \ + (AFE_PORT_ID_QUATERNARY_TDM_RX + 0x0A) +#define AFE_PORT_ID_QUATERNARY_TDM_RX_6 \ + (AFE_PORT_ID_QUATERNARY_TDM_RX + 0x0C) +#define AFE_PORT_ID_QUATERNARY_TDM_RX_7 \ + (AFE_PORT_ID_QUATERNARY_TDM_RX + 0x0E) + +#define AFE_PORT_ID_QUATERNARY_TDM_TX \ + (AFE_PORT_ID_TDM_PORT_RANGE_START + 0x31) +#define AFE_PORT_ID_QUATERNARY_TDM_TX_1 \ + (AFE_PORT_ID_QUATERNARY_TDM_TX + 0x02) +#define AFE_PORT_ID_QUATERNARY_TDM_TX_2 \ + (AFE_PORT_ID_QUATERNARY_TDM_TX + 0x04) +#define AFE_PORT_ID_QUATERNARY_TDM_TX_3 \ + (AFE_PORT_ID_QUATERNARY_TDM_TX + 0x06) +#define AFE_PORT_ID_QUATERNARY_TDM_TX_4 \ + (AFE_PORT_ID_QUATERNARY_TDM_TX + 0x08) +#define AFE_PORT_ID_QUATERNARY_TDM_TX_5 \ + (AFE_PORT_ID_QUATERNARY_TDM_TX + 0x0A) +#define AFE_PORT_ID_QUATERNARY_TDM_TX_6 \ + (AFE_PORT_ID_QUATERNARY_TDM_TX + 0x0C) +#define AFE_PORT_ID_QUATERNARY_TDM_TX_7 \ + (AFE_PORT_ID_QUATERNARY_TDM_TX + 0x0E) + +#define AFE_PORT_ID_QUINARY_TDM_RX \ + (AFE_PORT_ID_TDM_PORT_RANGE_START + 0x40) +#define AFE_PORT_ID_QUINARY_TDM_RX_1 \ + (AFE_PORT_ID_QUINARY_TDM_RX + 0x02) +#define AFE_PORT_ID_QUINARY_TDM_RX_2 \ + (AFE_PORT_ID_QUINARY_TDM_RX + 0x04) +#define AFE_PORT_ID_QUINARY_TDM_RX_3 \ + (AFE_PORT_ID_QUINARY_TDM_RX + 0x06) +#define AFE_PORT_ID_QUINARY_TDM_RX_4 \ + (AFE_PORT_ID_QUINARY_TDM_RX + 0x08) +#define AFE_PORT_ID_QUINARY_TDM_RX_5 \ + (AFE_PORT_ID_QUINARY_TDM_RX + 0x0A) +#define AFE_PORT_ID_QUINARY_TDM_RX_6 \ + (AFE_PORT_ID_QUINARY_TDM_RX + 0x0C) +#define AFE_PORT_ID_QUINARY_TDM_RX_7 \ + (AFE_PORT_ID_QUINARY_TDM_RX + 0x0E) + +#define AFE_PORT_ID_QUINARY_TDM_TX \ + (AFE_PORT_ID_TDM_PORT_RANGE_START + 0x41) +#define AFE_PORT_ID_QUINARY_TDM_TX_1 \ + (AFE_PORT_ID_QUINARY_TDM_TX + 0x02) +#define AFE_PORT_ID_QUINARY_TDM_TX_2 \ + (AFE_PORT_ID_QUINARY_TDM_TX + 0x04) +#define AFE_PORT_ID_QUINARY_TDM_TX_3 \ + (AFE_PORT_ID_QUINARY_TDM_TX + 0x06) +#define AFE_PORT_ID_QUINARY_TDM_TX_4 \ + (AFE_PORT_ID_QUINARY_TDM_TX + 0x08) +#define AFE_PORT_ID_QUINARY_TDM_TX_5 \ + (AFE_PORT_ID_QUINARY_TDM_TX + 0x0A) +#define AFE_PORT_ID_QUINARY_TDM_TX_6 \ + (AFE_PORT_ID_QUINARY_TDM_TX + 0x0C) +#define AFE_PORT_ID_QUINARY_TDM_TX_7 \ + (AFE_PORT_ID_QUINARY_TDM_TX + 0x0E) + +#define AFE_PORT_ID_INVALID 0xFFFF + +#define AAC_ENC_MODE_AAC_LC 0x02 +#define AAC_ENC_MODE_AAC_P 0x05 +#define AAC_ENC_MODE_EAAC_P 0x1D + +#define AFE_PSEUDOPORT_CMD_START 0x000100cf +struct afe_pseudoport_start_command { + struct apr_hdr hdr; + u16 port_id; /* Pseudo Port 1 = 0x8000 */ + /* Pseudo Port 2 = 0x8001 */ + /* Pseudo Port 3 = 0x8002 */ + u16 timing; /* FTRT = 0 , AVTimer = 1, */ +} __packed; + +#define AFE_PSEUDOPORT_CMD_STOP 0x000100d0 +struct afe_pseudoport_stop_command { + struct apr_hdr hdr; + u16 port_id; /* Pseudo Port 1 = 0x8000 */ + /* Pseudo Port 2 = 0x8001 */ + /* Pseudo Port 3 = 0x8002 */ + u16 reserved; +} __packed; + + +#define AFE_MODULE_SIDETONE_IIR_FILTER 0x00010202 +#define AFE_PARAM_ID_ENABLE 0x00010203 + +/* Payload of the #AFE_PARAM_ID_ENABLE + * parameter, which enables or + * disables any module. + * The fixed size of this structure is four bytes. + */ + +struct afe_mod_enable_param { + u16 enable; + /* Enables (1) or disables (0) the module. */ + + u16 reserved; + /* This field must be set to zero. */ +} __packed; + +/* ID of the configuration parameter used by the + * #AFE_MODULE_SIDETONE_IIR_FILTER module. + */ +#define AFE_PARAM_ID_SIDETONE_IIR_FILTER_CONFIG 0x00010204 +#define MAX_SIDETONE_IIR_DATA_SIZE 224 +#define MAX_NO_IIR_FILTER_STAGE 10 + +struct afe_sidetone_iir_filter_config_params { + u16 num_biquad_stages; +/* Number of stages. + * Supported values: Minimum of 5 and maximum of 10 + */ + + u16 pregain; +/* Pregain for the compensating filter response. + * Supported values: Any number in Q13 format + */ + uint8_t iir_config[MAX_SIDETONE_IIR_DATA_SIZE]; +} __packed; + +#define AFE_MODULE_LOOPBACK 0x00010205 +#define AFE_PARAM_ID_LOOPBACK_GAIN_PER_PATH 0x00010206 + +/* Used by RTAC */ +struct afe_rtac_user_data_set_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; + + /* The header detailing the memory mapping for out of band. */ + struct mem_mapping_hdr mem_hdr; + + /* The parameter header for the parameter data to set */ + struct param_hdr_v1 param_hdr; + + /* The parameter data to be filled when sent inband */ + u32 *param_data; +} __packed; + +struct afe_rtac_user_data_set_v3 { + /* Port interface and direction (Rx or Tx) to start. */ + u16 port_id; + /* Reserved for future enhancements. Must be 0. */ + u16 reserved; + + /* The header detailing the memory mapping for out of band. */ + struct mem_mapping_hdr mem_hdr; + + /* The size of the parameter header and parameter data */ + u32 payload_size; + + /* The parameter header for the parameter data to set */ + struct param_hdr_v3 param_hdr; + + /* The parameter data to be filled when sent inband */ + u32 *param_data; +} __packed; + +struct afe_rtac_user_data_get_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; + + /* The header detailing the memory mapping for out of band. */ + struct mem_mapping_hdr mem_hdr; + + /* The module ID of the parameter to get */ + u32 module_id; + + /* The parameter ID of the parameter to get */ + u32 param_id; + + /* The parameter data to be filled when sent inband */ + struct param_hdr_v1 param_hdr; +} __packed; + +struct afe_rtac_user_data_get_v3 { + /* Port interface and direction (Rx or Tx) to start. */ + u16 port_id; + /* Reserved for future enhancements. Must be 0. */ + u16 reserved; + + /* The header detailing the memory mapping for out of band. */ + struct mem_mapping_hdr mem_hdr; + + /* The parameter data to be filled when sent inband */ + struct param_hdr_v3 param_hdr; +} __packed; + +#define AFE_PORT_CMD_SET_PARAM_V2 0x000100EF +struct afe_port_cmd_set_param_v2 { + /* APR Header */ + struct apr_hdr apr_hdr; + + /* 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; + + /* The header detailing the memory mapping for out of band. */ + struct mem_mapping_hdr mem_hdr; + + /* The parameter data to be filled when sent inband */ + u8 param_data[0]; +} __packed; + +#define AFE_PORT_CMD_SET_PARAM_V3 0x000100FA +struct afe_port_cmd_set_param_v3 { + /* APR Header */ + struct apr_hdr apr_hdr; + + /* Port ID of the AFE port to configure. Port interface and direction + * (Rx or Tx) to configure. An even number represents the Rx direction, + * and an odd number represents the Tx direction. + */ + u16 port_id; + + /* Reserved. This field must be set to zero. */ + u16 reserved; + + /* The memory mapping header to be used when sending outband */ + struct mem_mapping_hdr mem_hdr; + + /* The total size of the payload, including param_hdr_v3 */ + u32 payload_size; + + /* + * The parameter data to be filled when sent inband. + * Must include param_hdr packed correctly. + */ + u8 param_data[0]; +} __packed; + +/* Payload of the #AFE_PARAM_ID_LOOPBACK_GAIN_PER_PATH parameter, + * which gets/sets loopback gain of a port to an Rx port. + * The Tx port ID of the loopback is part of the set_param command. + */ + +struct afe_loopback_gain_per_path_param { + u16 rx_port_id; +/* Rx port of the loopback. */ + +u16 gain; +/* Loopback gain per path of the port. + * Supported values: Any number in Q13 format + */ +} __packed; + +/* Parameter ID used to configure and enable/disable the + * loopback path. The difference with respect to the existing + * API, AFE_PORT_CMD_LOOPBACK, is that it allows Rx port to be + * configured as source port in loopback path. Port-id in + * AFE_PORT_CMD_SET_PARAM cmd is the source port which can be + * Tx or Rx port. In addition, we can configure the type of + * routing mode to handle different use cases. + */ +#define AFE_PARAM_ID_LOOPBACK_CONFIG 0x0001020B +#define AFE_API_VERSION_LOOPBACK_CONFIG 0x1 + +enum afe_loopback_routing_mode { + LB_MODE_DEFAULT = 1, + /* Regular loopback from source to destination port */ + LB_MODE_SIDETONE, + /* Sidetone feed from Tx source to Rx destination port */ + LB_MODE_EC_REF_VOICE_AUDIO, + /* Echo canceller reference, voice + audio + DTMF */ + LB_MODE_EC_REF_VOICE + /* Echo canceller reference, voice alone */ +} __packed; + +/* Payload of the #AFE_PARAM_ID_LOOPBACK_CONFIG , + * which enables/disables one AFE loopback. + */ +struct afe_loopback_cfg_v1 { + u32 loopback_cfg_minor_version; +/* Minor version used for tracking the version of the RMC module + * configuration interface. + * Supported values: #AFE_API_VERSION_LOOPBACK_CONFIG + */ + u16 dst_port_id; + /* Destination Port Id. */ + u16 routing_mode; +/* Specifies data path type from src to dest port. + * Supported values: + * #LB_MODE_DEFAULT + * #LB_MODE_SIDETONE + * #LB_MODE_EC_REF_VOICE_AUDIO + * #LB_MODE_EC_REF_VOICE_A + * #LB_MODE_EC_REF_VOICE + */ + + u16 enable; +/* Specifies whether to enable (1) or + * disable (0) an AFE loopback. + */ + u16 reserved; +/* Reserved for 32-bit alignment. This field must be set to 0. + */ + +} __packed; + +struct afe_loopback_sidetone_gain { + u16 rx_port_id; + u16 gain; +} __packed; + +struct afe_display_stream_idx { + u32 minor_version; + u32 stream_idx; +} __packed; + +struct afe_display_ctl_idx { + u32 minor_version; + u32 ctl_idx; +} __packed; + +struct loopback_cfg_data { + u32 loopback_cfg_minor_version; +/* Minor version used for tracking the version of the RMC module + * configuration interface. + * Supported values: #AFE_API_VERSION_LOOPBACK_CONFIG + */ + u16 dst_port_id; + /* Destination Port Id. */ + u16 routing_mode; +/* Specifies data path type from src to dest port. + * Supported values: + * #LB_MODE_DEFAULT + * #LB_MODE_SIDETONE + * #LB_MODE_EC_REF_VOICE_AUDIO + * #LB_MODE_EC_REF_VOICE_A + * #LB_MODE_EC_REF_VOICE + */ + + u16 enable; +/* Specifies whether to enable (1) or + * disable (0) an AFE loopback. + */ + u16 reserved; +/* Reserved for 32-bit alignment. This field must be set to 0. + */ +} __packed; + +struct afe_st_loopback_cfg_v1 { + struct apr_hdr hdr; + struct mem_mapping_hdr mem_hdr; + struct param_hdr_v1 gain_pdata; + struct afe_loopback_sidetone_gain gain_data; + struct param_hdr_v1 cfg_pdata; + struct loopback_cfg_data cfg_data; +} __packed; + +struct afe_loopback_iir_cfg_v2 { + struct apr_hdr hdr; + struct mem_mapping_hdr param; + struct param_hdr_v1 st_iir_enable_pdata; + struct afe_mod_enable_param st_iir_mode_enable_data; + struct param_hdr_v1 st_iir_filter_config_pdata; + struct afe_sidetone_iir_filter_config_params st_iir_filter_config_data; +} __packed; + + +/* + * Param ID and related structures for AFE event + * registration. + */ +#define AFE_PORT_CMD_MOD_EVENT_CFG 0x000100FD + +struct afe_port_cmd_event_cfg { + struct apr_hdr hdr; + uint32_t version; + /* Version number. The current version is 0 */ + + uint32_t port_id; + /* + * Port ID for the AFE port hosting the modules + * being registered for the events + */ + + uint32_t num_events; + /* + * Number of events to be registered with the service + * Each event has the structure of + * afe_port_cmd_mod_evt_cfg_payload. + */ + uint8_t payload[0]; +}; + +/** Event registration for a module. */ +#define AFE_MODULE_REGISTER_EVENT_FLAG 1 + +/** Event de-registration for a module. */ +#define AFE_MODULE_DEREGISTER_EVENT_FLAG 0 + +struct afe_port_cmd_mod_evt_cfg_payload { + uint32_t module_id; + /* Valid ID of the module. */ + + uint16_t instance_id; + /* + * Valid ID of the module instance in the current topology. + * If both module_id and instance_id ID are set to 0, the event is + * registered with all modules and instances in the topology. + * If module_id is set to 0 and instance_id is set to a non-zero value, + * the payload is considered to be invalid. + */ + + uint16_t reserved; + /* Used for alignment; must be set to 0.*/ + + uint32_t event_id; + /* Unique ID of the event. */ + + uint32_t reg_flag; + /* + * Bit field for enabling or disabling event registration. + * values + * - #AFE_MODULE_REGISTER_EVENT_FLAG + * - #AFE_MODULE_DEREGISTER_EVENT_FLAG + */ +} __packed; + + +#define AFE_PORT_MOD_EVENT 0x0001010C + +struct afe_port_mod_evt_rsp_hdr { + uint32_t minor_version; + /* This indicates the minor version of the payload */ + + uint32_t port_id; + /* AFE port hosting this module */ + + uint32_t module_id; + /* Module ID which is raising the event */ + + uint16_t instance_id; + /* Instance ID of the module which is raising the event */ + + uint16_t reserved; + /* For alignment purpose, should be set to 0 */ + + uint32_t event_id; + /* Valid event ID registered by client */ + + uint32_t payload_size; + /* + * Size of the event payload + * This is followed by actual payload corresponding to the event + */ +} __packed; + +#define AFE_PORT_SP_DC_DETECTION_EVENT 0x0001010D + +#define AFE_MODULE_SPEAKER_PROTECTION 0x00010209 +#define AFE_PARAM_ID_SPKR_PROT_CONFIG 0x0001020a +#define AFE_API_VERSION_SPKR_PROT_CONFIG 0x1 +#define AFE_SPKR_PROT_EXCURSIONF_LEN 512 +struct afe_spkr_prot_cfg_param_v1 { + u32 spkr_prot_minor_version; +/* + * Minor version used for tracking the version of the + * speaker protection module configuration interface. + * Supported values: #AFE_API_VERSION_SPKR_PROT_CONFIG + */ + +int16_t win_size; +/* Analysis and synthesis window size (nWinSize). + * Supported values: 1024, 512, 256 samples + */ + +int16_t margin; +/* Allowable margin for excursion prediction, + * in L16Q15 format. This is a + * control parameter to allow + * for overestimation of peak excursion. + */ + +int16_t spkr_exc_limit; +/* Speaker excursion limit, in L16Q15 format.*/ + +int16_t spkr_resonance_freq; +/* Resonance frequency of the speaker; used + * to define a frequency range + * for signal modification. + * + * Supported values: 0 to 2000 Hz + */ + +int16_t limhresh; +/* Threshold of the hard limiter; used to + * prevent overshooting beyond a + * signal level that was set by the limiter + * prior to speaker protection. + * Supported values: 0 to 32767 + */ + +int16_t hpf_cut_off_freq; +/* High pass filter cutoff frequency. + * Supported values: 100, 200, 300 Hz + */ + +int16_t hpf_enable; +/* Specifies whether the high pass filter + * is enabled (0) or disabled (1). + */ + +int16_t reserved; +/* This field must be set to zero. */ + +int32_t amp_gain; +/* Amplifier gain in L32Q15 format. + * This is the RMS voltage at the + * loudspeaker when a 0dBFS tone + * is played in the digital domain. + */ + +int16_t excursionf[AFE_SPKR_PROT_EXCURSIONF_LEN]; +/* Array of the excursion transfer function. + * The peak excursion of the + * loudspeaker diaphragm is + * measured in millimeters for 1 Vrms Sine + * tone at all FFT bin frequencies. + * Supported values: Q15 format + */ +} __packed; + + +#define AFE_SERVICE_CMD_REGISTER_RT_PORT_DRIVER 0x000100E0 + +/* Payload of the #AFE_SERVICE_CMD_REGISTER_RT_PORT_DRIVER + * command, which registers a real-time port driver + * with the AFE service. + */ +struct afe_service_cmd_register_rt_port_driver { + struct apr_hdr hdr; + u16 port_id; +/* Port ID with which the real-time driver exchanges data + * (registers for events). + * Supported values: #AFE_PORT_ID_RT_PROXY_PORT_RANGE_START to + * #AFE_PORT_ID_RT_PROXY_PORT_RANGE_END + */ + + u16 reserved; + /* This field must be set to zero. */ +} __packed; + +#define AFE_SERVICE_CMD_UNREGISTER_RT_PORT_DRIVER 0x000100E1 + +/* Payload of the #AFE_SERVICE_CMD_UNREGISTER_RT_PORT_DRIVER + * command, which unregisters a real-time port driver from + * the AFE service. + */ +struct afe_service_cmd_unregister_rt_port_driver { + struct apr_hdr hdr; + u16 port_id; +/* Port ID from which the real-time + * driver unregisters for events. + * Supported values: #AFE_PORT_ID_RT_PROXY_PORT_RANGE_START to + * #AFE_PORT_ID_RT_PROXY_PORT_RANGE_END + */ + + u16 reserved; + /* This field must be set to zero. */ +} __packed; + +#define AFE_EVENT_RT_PROXY_PORT_STATUS 0x00010105 +#define AFE_EVENTYPE_RT_PROXY_PORT_START 0 +#define AFE_EVENTYPE_RT_PROXY_PORT_STOP 1 +#define AFE_EVENTYPE_RT_PROXY_PORT_LOW_WATER_MARK 2 +#define AFE_EVENTYPE_RT_PROXY_PORT_HIGH_WATER_MARK 3 +#define AFE_EVENTYPE_RT_PROXY_PORT_INVALID 0xFFFF + +/* Payload of the #AFE_EVENT_RT_PROXY_PORT_STATUS + * message, which sends an event from the AFE service + * to a registered client. + */ +struct afe_event_rt_proxy_port_status { + u16 port_id; +/* Port ID to which the event is sent. + * Supported values: #AFE_PORT_ID_RT_PROXY_PORT_RANGE_START to + * #AFE_PORT_ID_RT_PROXY_PORT_RANGE_END + */ + + u16 eventype; +/* Type of event. + * Supported values: + * - #AFE_EVENTYPE_RT_PROXY_PORT_START + * - #AFE_EVENTYPE_RT_PROXY_PORT_STOP + * - #AFE_EVENTYPE_RT_PROXY_PORT_LOW_WATER_MARK + * - #AFE_EVENTYPE_RT_PROXY_PORT_HIGH_WATER_MARK + */ +} __packed; + +#define AFE_PORT_DATA_CMD_RT_PROXY_PORT_WRITE_V2 0x000100ED + +struct afe_port_data_cmd_rt_proxy_port_write_v2 { + struct apr_hdr hdr; + u16 port_id; +/* Tx (mic) proxy port ID with which the real-time + * driver exchanges data. + * Supported values: #AFE_PORT_ID_RT_PROXY_PORT_RANGE_START to + * #AFE_PORT_ID_RT_PROXY_PORT_RANGE_END + */ + + u16 reserved; + /* This field must be set to zero. */ + + u32 buffer_address_lsw; +/* LSW Address of the buffer containing the + * data from the real-time source + * device on a client. + */ + + u32 buffer_address_msw; +/* MSW Address of the buffer containing the + * data from the real-time source + * device on a client. + */ + + u32 mem_map_handle; +/* A memory map handle encapsulating shared memory + * attributes is returned if + * AFE_SERVICE_CMD_SHARED_MEM_MAP_REGIONS + * command is successful. + * Supported Values: + * - Any 32 bit value + */ + + u32 available_bytes; +/* Number of valid bytes available + * in the buffer (including all + * channels: number of bytes per + * channel = availableBytesumChannels). + * Supported values: > 0 + * + * This field must be equal to the frame + * size specified in the #AFE_PORT_AUDIO_IF_CONFIG + * command that was sent to configure this + * port. + */ +} __packed; + +#define AFE_PORT_DATA_CMD_RT_PROXY_PORT_READ_V2 0x000100EE + +/* Payload of the + * #AFE_PORT_DATA_CMD_RT_PROXY_PORT_READ_V2 command, which + * delivers an empty buffer to the AFE service. On + * acknowledgment, data is filled in the buffer. + */ +struct afe_port_data_cmd_rt_proxy_port_read_v2 { + struct apr_hdr hdr; + u16 port_id; +/* Rx proxy port ID with which the real-time + * driver exchanges data. + * Supported values: #AFE_PORT_ID_RT_PROXY_PORT_RANGE_START to + * #AFE_PORT_ID_RT_PROXY_PORT_RANGE_END + * (This must be an Rx (speaker) port.) + */ + + u16 reserved; + /* This field must be set to zero. */ + + u32 buffer_address_lsw; +/* LSW Address of the buffer containing the data sent from the AFE + * service to a real-time sink device on the client. + */ + + + u32 buffer_address_msw; +/* MSW Address of the buffer containing the data sent from the AFE + * service to a real-time sink device on the client. + */ + + u32 mem_map_handle; +/* A memory map handle encapsulating shared memory attributes is + * returned if AFE_SERVICE_CMD_SHARED_MEM_MAP_REGIONS command is + * successful. + * Supported Values: + * - Any 32 bit value + */ + + u32 available_bytes; +/* Number of valid bytes available in the buffer (including all + * channels). + * Supported values: > 0 + * This field must be equal to the frame size specified in the + * #AFE_PORT_AUDIO_IF_CONFIG command that was sent to configure + * this port. + */ +} __packed; + +/* This module ID is related to device configuring like I2S,PCM, + * HDMI, SLIMBus etc. This module supports following parameter ids. + * - #AFE_PARAM_ID_I2S_CONFIG + * - #AFE_PARAM_ID_PCM_CONFIG + * - #AFE_PARAM_ID_DIGI_MIC_CONFIG + * - #AFE_PARAM_ID_HDMI_CONFIG + * - #AFE_PARAM_ID_INTERNAL_BT_FM_CONFIG + * - #AFE_PARAM_ID_SLIMBUS_CONFIG + * - #AFE_PARAM_ID_RT_PROXY_CONFIG + */ + +#define AFE_MODULE_AUDIO_DEV_INTERFACE 0x0001020C +#define AFE_PORT_SAMPLE_RATE_8K 8000 +#define AFE_PORT_SAMPLE_RATE_16K 16000 +#define AFE_PORT_SAMPLE_RATE_48K 48000 +#define AFE_PORT_SAMPLE_RATE_96K 96000 +#define AFE_PORT_SAMPLE_RATE_176P4K 176400 +#define AFE_PORT_SAMPLE_RATE_192K 192000 +#define AFE_PORT_SAMPLE_RATE_352P8K 352800 +#define AFE_LINEAR_PCM_DATA 0x0 +#define AFE_NON_LINEAR_DATA 0x1 +#define AFE_LINEAR_PCM_DATA_PACKED_60958 0x2 +#define AFE_NON_LINEAR_DATA_PACKED_60958 0x3 +#define AFE_GENERIC_COMPRESSED 0x8 +#define AFE_LINEAR_PCM_DATA_PACKED_16BIT 0X6 + +/* This param id is used to configure I2S interface */ +#define AFE_PARAM_ID_I2S_CONFIG 0x0001020D +#define AFE_API_VERSION_I2S_CONFIG 0x1 +/* Enumeration for setting the I2S configuration + * channel_mode parameter to + * serial data wire number 1-3 (SD3). + */ +#define AFE_PORT_I2S_SD0 0x1 +#define AFE_PORT_I2S_SD1 0x2 +#define AFE_PORT_I2S_SD2 0x3 +#define AFE_PORT_I2S_SD3 0x4 +#define AFE_PORT_I2S_QUAD01 0x5 +#define AFE_PORT_I2S_QUAD23 0x6 +#define AFE_PORT_I2S_6CHS 0x7 +#define AFE_PORT_I2S_8CHS 0x8 +#define AFE_PORT_I2S_10CHS 0x9 +#define AFE_PORT_I2S_12CHS 0xA +#define AFE_PORT_I2S_14CHS 0xB +#define AFE_PORT_I2S_16CHS 0xC +#define AFE_PORT_I2S_SD4 0xD +#define AFE_PORT_I2S_SD5 0xE +#define AFE_PORT_I2S_SD6 0xF +#define AFE_PORT_I2S_SD7 0x10 +#define AFE_PORT_I2S_QUAD45 0x11 +#define AFE_PORT_I2S_QUAD67 0x12 +#define AFE_PORT_I2S_8CHS_2 0x13 +#define AFE_PORT_I2S_MONO 0x0 +#define AFE_PORT_I2S_STEREO 0x1 +#define AFE_PORT_CONFIG_I2S_WS_SRC_EXTERNAL 0x0 +#define AFE_PORT_CONFIG_I2S_WS_SRC_INTERNAL 0x1 + +/* Payload of the #AFE_PARAM_ID_I2S_CONFIG + * command's (I2S configuration + * parameter). + */ +struct afe_param_id_i2s_cfg { + u32 i2s_cfg_minor_version; +/* Minor version used for tracking the version of the I2S + * configuration interface. + * Supported values: #AFE_API_VERSION_I2S_CONFIG + */ + + u16 bit_width; +/* Bit width of the sample. + * Supported values: 16, 24 + */ + + u16 channel_mode; +/* I2S lines and multichannel operation. + * Supported values: + * - #AFE_PORT_I2S_SD0 + * - #AFE_PORT_I2S_SD1 + * - #AFE_PORT_I2S_SD2 + * - #AFE_PORT_I2S_SD3 + * - #AFE_PORT_I2S_QUAD01 + * - #AFE_PORT_I2S_QUAD23 + * - #AFE_PORT_I2S_6CHS + * - #AFE_PORT_I2S_8CHS + * - #AFE_PORT_I2S_10CHS + * - #AFE_PORT_I2S_12CHS + * - #AFE_PORT_I2S_14CHS + * - #AFE_PORT_I2S_16CHS + * - #AFE_PORT_I2S_SD4 + * - #AFE_PORT_I2S_SD5 + * - #AFE_PORT_I2S_SD6 + * - #AFE_PORT_I2S_SD7 + * - #AFE_PORT_I2S_QUAD45 + * - #AFE_PORT_I2S_QUAD67 + * - #AFE_PORT_I2S_8CHS_2 + */ + + u16 mono_stereo; +/* Specifies mono or stereo. This applies only when + * a single I2S line is used. + * Supported values: + * - #AFE_PORT_I2S_MONO + * - #AFE_PORT_I2S_STEREO + */ + + u16 ws_src; +/* Word select source: internal or external. + * Supported values: + * - #AFE_PORT_CONFIG_I2S_WS_SRC_EXTERNAL + * - #AFE_PORT_CONFIG_I2S_WS_SRC_INTERNAL + */ + + u32 sample_rate; +/* Sampling rate of the port. + * Supported values: + * - #AFE_PORT_SAMPLE_RATE_8K + * - #AFE_PORT_SAMPLE_RATE_16K + * - #AFE_PORT_SAMPLE_RATE_48K + * - #AFE_PORT_SAMPLE_RATE_96K + * - #AFE_PORT_SAMPLE_RATE_192K + */ + + u16 data_format; +/* data format + * Supported values: + * - #LINEAR_PCM_DATA + * - #NON_LINEAR_DATA + * - #LINEAR_PCM_DATA_PACKED_IN_60958 + * - #NON_LINEAR_DATA_PACKED_IN_60958 + */ + u16 reserved; + /* This field must be set to zero. */ +} __packed; + +/* + * This param id is used to configure PCM interface + */ + +#define AFE_API_VERSION_SPDIF_CONFIG_V2 0x2 +#define AFE_API_VERSION_SPDIF_CONFIG 0x1 +#define AFE_API_VERSION_SPDIF_CH_STATUS_CONFIG 0x1 +#define AFE_API_VERSION_SPDIF_CLK_CONFIG 0x1 +#define AFE_CH_STATUS_A 1 +#define AFE_CH_STATUS_B 2 + +#define AFE_PARAM_ID_SPDIF_CONFIG 0x00010244 +#define AFE_PARAM_ID_CH_STATUS_CONFIG 0x00010245 +#define AFE_PARAM_ID_SPDIF_CLK_CONFIG 0x00010246 + +#define AFE_PORT_CLK_ROOT_LPAPLL 0x3 +#define AFE_PORT_CLK_ROOT_LPAQ6PLL 0x4 + +#define AFE_MODULE_CUSTOM_EVENTS 0x00010251 + +#define AFE_PORT_FMT_UPDATE_EVENT 0x0001010E + +#define AFE_API_VERSION_EVENT_FMT_UPDATE 0x1 +#define AFE_PORT_STATUS_NO_SIGNAL 0 +#define AFE_PORT_STATUS_AUDIO_ACTIVE 1 +#define AFE_PORT_STATUS_AUDIO_EOS 2 + +struct afe_param_id_spdif_cfg_v2 { +/* Minor version used for tracking the version of the SPDIF + * configuration interface. + * Supported values: #AFE_API_VERSION_SPDIF_CONFIG, + * #AFE_API_VERSION_SPDIF_CONFIG_V2 + */ + u32 spdif_cfg_minor_version; + +/* Sampling rate of the port. + * Supported values: + * - #AFE_PORT_SAMPLE_RATE_22_05K + * - #AFE_PORT_SAMPLE_RATE_32K + * - #AFE_PORT_SAMPLE_RATE_44_1K + * - #AFE_PORT_SAMPLE_RATE_48K + * - #AFE_PORT_SAMPLE_RATE_96K + * - #AFE_PORT_SAMPLE_RATE_176_4K + * - #AFE_PORT_SAMPLE_RATE_192K + */ + u32 sample_rate; + +/* data format + * Supported values: + * - #AFE_LINEAR_PCM_DATA + * - #AFE_NON_LINEAR_DATA + */ + u16 data_format; +/* Number of channels supported by the port + * - PCM - 1, Compressed Case - 2 + */ + u16 num_channels; +/* Bit width of the sample. + * Supported values: 16, 24 + */ + u16 bit_width; +/* This field must be set to zero. */ + u16 reserved; +/* Input select for spdif input, must be set to 0 for spdif output. */ + u32 src_sel; +} __packed; + +struct afe_param_id_spdif_ch_status_cfg { + u32 ch_status_cfg_minor_version; +/* Minor version used for tracking the version of channel + * status configuration. Current supported version is 1 + */ + + u32 status_type; +/* Indicate if the channel status is for channel A or B + * Supported values: + * - #AFE_CH_STATUS_A + * - #AFE_CH_STATUS_B + */ + + u8 status_bits[24]; +/* Channel status - 192 bits for channel + * Byte ordering as defined by IEC60958-3 + */ + + u8 status_mask[24]; +/* Channel status with mask bits 1 will be applied. + * Byte ordering as defined by IEC60958-3 + */ +} __packed; + +/* deprecated */ +struct afe_param_id_spdif_clk_cfg { + u32 clk_cfg_minor_version; +/* Minor version used for tracking the version of SPDIF + * interface clock configuration. Current supported version + * is 1 + */ + + u32 clk_value; +/* Specifies the clock frequency in Hz to set + * Supported values: + * 0 - Disable the clock + * 2 (byphase) * 32 (60958 subframe size) * sampling rate * 2 + * (channels A and B) + */ + + u32 clk_root; +/* Specifies SPDIF root clk source + * Supported Values: + * - #AFE_PORT_CLK_ROOT_LPAPLL + * - #AFE_PORT_CLK_ROOT_LPAQ6PLL + */ +} __packed; + +struct afe_event_fmt_update { + /* Tracks the configuration of this event. */ + u32 minor_version; + + /* Detected port status. + * Supported values: + * - #AFE_PORT_STATUS_NO_SIGNAL + * - #AFE_PORT_STATUS_AUDIO_ACTIVE + * - #AFE_PORT_STATUS_AUDIO_EOS + */ + u32 status; + + /* Sampling rate of the port. + * Supported values: + * - #AFE_PORT_SAMPLE_RATE_32K + * - #AFE_PORT_SAMPLE_RATE_44_1K + * - #AFE_PORT_SAMPLE_RATE_48K + * - #AFE_PORT_SAMPLE_RATE_88_2K + * - #AFE_PORT_SAMPLE_RATE_96K + * - #AFE_PORT_SAMPLE_RATE_176_4K + * - #AFE_PORT_SAMPLE_RATE_192K + */ + u32 sample_rate; + + /* Data format of the port. + * Supported values: + * - #AFE_LINEAR_PCM_DATA + * - #AFE_NON_LINEAR_DATA + */ + u16 data_format; + + /* First 6 bytes of channel status bits */ + u8 channel_status[6]; +} __packed; + +struct afe_spdif_port_config { + struct afe_param_id_spdif_cfg_v2 cfg; + struct afe_param_id_spdif_ch_status_cfg ch_status; +} __packed; + +#define AFE_PARAM_ID_PCM_CONFIG 0x0001020E +#define AFE_API_VERSION_PCM_CONFIG 0x1 +/* Enumeration for the auxiliary PCM synchronization signal + * provided by an external source. + */ + +#define AFE_PORT_PCM_SYNC_SRC_EXTERNAL 0x0 +/* Enumeration for the auxiliary PCM synchronization signal + * provided by an internal source. + */ +#define AFE_PORT_PCM_SYNC_SRC_INTERNAL 0x1 +/* Enumeration for the PCM configuration aux_mode parameter, + * which configures the auxiliary PCM interface to use + * short synchronization. + */ +#define AFE_PORT_PCM_AUX_MODE_PCM 0x0 +/* + * Enumeration for the PCM configuration aux_mode parameter, + * which configures the auxiliary PCM interface to use long + * synchronization. + */ +#define AFE_PORT_PCM_AUX_MODE_AUX 0x1 +/* + * Enumeration for setting the PCM configuration frame to 8. + */ +#define AFE_PORT_PCM_BITS_PER_FRAME_8 0x0 +/* + * Enumeration for setting the PCM configuration frame to 16. + */ +#define AFE_PORT_PCM_BITS_PER_FRAME_16 0x1 + +/* Enumeration for setting the PCM configuration frame to 32.*/ +#define AFE_PORT_PCM_BITS_PER_FRAME_32 0x2 + +/* Enumeration for setting the PCM configuration frame to 64.*/ +#define AFE_PORT_PCM_BITS_PER_FRAME_64 0x3 + +/* Enumeration for setting the PCM configuration frame to 128.*/ +#define AFE_PORT_PCM_BITS_PER_FRAME_128 0x4 + +/* Enumeration for setting the PCM configuration frame to 256.*/ +#define AFE_PORT_PCM_BITS_PER_FRAME_256 0x5 + +/* Enumeration for setting the PCM configuration + * quantype parameter to A-law with no padding. + */ +#define AFE_PORT_PCM_ALAW_NOPADDING 0x0 + +/* Enumeration for setting the PCM configuration quantype + * parameter to mu-law with no padding. + */ +#define AFE_PORT_PCM_MULAW_NOPADDING 0x1 +/* Enumeration for setting the PCM configuration quantype + * parameter to linear with no padding. + */ +#define AFE_PORT_PCM_LINEAR_NOPADDING 0x2 +/* Enumeration for setting the PCM configuration quantype + * parameter to A-law with padding. + */ +#define AFE_PORT_PCM_ALAW_PADDING 0x3 +/* Enumeration for setting the PCM configuration quantype + * parameter to mu-law with padding. + */ +#define AFE_PORT_PCM_MULAW_PADDING 0x4 +/* Enumeration for setting the PCM configuration quantype + * parameter to linear with padding. + */ +#define AFE_PORT_PCM_LINEAR_PADDING 0x5 +/* Enumeration for disabling the PCM configuration + * ctrl_data_out_enable parameter. + * The PCM block is the only master. + */ +#define AFE_PORT_PCM_CTRL_DATA_OE_DISABLE 0x0 +/* + * Enumeration for enabling the PCM configuration + * ctrl_data_out_enable parameter. The PCM block shares + * the signal with other masters. + */ +#define AFE_PORT_PCM_CTRL_DATA_OE_ENABLE 0x1 + +/* Payload of the #AFE_PARAM_ID_PCM_CONFIG command's + * (PCM configuration parameter). + */ + +struct afe_param_id_pcm_cfg { + u32 pcm_cfg_minor_version; +/* Minor version used for tracking the version of the AUX PCM + * configuration interface. + * Supported values: #AFE_API_VERSION_PCM_CONFIG + */ + + u16 aux_mode; +/* PCM synchronization setting. + * Supported values: + * - #AFE_PORT_PCM_AUX_MODE_PCM + * - #AFE_PORT_PCM_AUX_MODE_AUX + */ + + u16 sync_src; +/* Synchronization source. + * Supported values: + * - #AFE_PORT_PCM_SYNC_SRC_EXTERNAL + * - #AFE_PORT_PCM_SYNC_SRC_INTERNAL + */ + + u16 frame_setting; +/* Number of bits per frame. + * Supported values: + * - #AFE_PORT_PCM_BITS_PER_FRAME_8 + * - #AFE_PORT_PCM_BITS_PER_FRAME_16 + * - #AFE_PORT_PCM_BITS_PER_FRAME_32 + * - #AFE_PORT_PCM_BITS_PER_FRAME_64 + * - #AFE_PORT_PCM_BITS_PER_FRAME_128 + * - #AFE_PORT_PCM_BITS_PER_FRAME_256 + */ + + u16 quantype; +/* PCM quantization type. + * Supported values: + * - #AFE_PORT_PCM_ALAW_NOPADDING + * - #AFE_PORT_PCM_MULAW_NOPADDING + * - #AFE_PORT_PCM_LINEAR_NOPADDING + * - #AFE_PORT_PCM_ALAW_PADDING + * - #AFE_PORT_PCM_MULAW_PADDING + * - #AFE_PORT_PCM_LINEAR_PADDING + */ + + u16 ctrl_data_out_enable; +/* Specifies whether the PCM block shares the data-out + * signal to the drive with other masters. + * Supported values: + * - #AFE_PORT_PCM_CTRL_DATA_OE_DISABLE + * - #AFE_PORT_PCM_CTRL_DATA_OE_ENABLE + */ + u16 reserved; + /* This field must be set to zero. */ + + u32 sample_rate; +/* Sampling rate of the port. + * Supported values: + * - #AFE_PORT_SAMPLE_RATE_8K + * - #AFE_PORT_SAMPLE_RATE_16K + */ + + u16 bit_width; +/* Bit width of the sample. + * Supported values: 16 + */ + + u16 num_channels; +/* Number of channels. + * Supported values: 1 to 4 + */ + + u16 slot_number_mapping[4]; +/* Specifies the slot number for the each channel in + * multi channel scenario. + * Supported values: 1 to 32 + */ +} __packed; + +/* + * This param id is used to configure DIGI MIC interface + */ +#define AFE_PARAM_ID_DIGI_MIC_CONFIG 0x0001020F +/* This version information is used to handle the new + * additions to the config interface in future in backward + * compatible manner. + */ +#define AFE_API_VERSION_DIGI_MIC_CONFIG 0x1 + +/* Enumeration for setting the digital mic configuration + * channel_mode parameter to left 0. + */ + +#define AFE_PORT_DIGI_MIC_MODE_LEFT0 0x1 + +/*Enumeration for setting the digital mic configuration + * channel_mode parameter to right 0. + */ + + +#define AFE_PORT_DIGI_MIC_MODE_RIGHT0 0x2 + +/* Enumeration for setting the digital mic configuration + * channel_mode parameter to left 1. + */ + +#define AFE_PORT_DIGI_MIC_MODE_LEFT1 0x3 + +/* Enumeration for setting the digital mic configuration + * channel_mode parameter to right 1. + */ + +#define AFE_PORT_DIGI_MIC_MODE_RIGHT1 0x4 + +/* Enumeration for setting the digital mic configuration + * channel_mode parameter to stereo 0. + */ +#define AFE_PORT_DIGI_MIC_MODE_STEREO0 0x5 + +/* Enumeration for setting the digital mic configuration + * channel_mode parameter to stereo 1. + */ + + +#define AFE_PORT_DIGI_MIC_MODE_STEREO1 0x6 + +/* Enumeration for setting the digital mic configuration + * channel_mode parameter to quad. + */ + +#define AFE_PORT_DIGI_MIC_MODE_QUAD 0x7 + +/* Payload of the #AFE_PARAM_ID_DIGI_MIC_CONFIG command's + * (DIGI MIC configuration + * parameter). + */ +struct afe_param_id_digi_mic_cfg { + u32 digi_mic_cfg_minor_version; +/* Minor version used for tracking the version of the DIGI Mic + * configuration interface. + * Supported values: #AFE_API_VERSION_DIGI_MIC_CONFIG + */ + + u16 bit_width; +/* Bit width of the sample. + * Supported values: 16 + */ + + u16 channel_mode; +/* Digital mic and multichannel operation. + * Supported values: + * - #AFE_PORT_DIGI_MIC_MODE_LEFT0 + * - #AFE_PORT_DIGI_MIC_MODE_RIGHT0 + * - #AFE_PORT_DIGI_MIC_MODE_LEFT1 + * - #AFE_PORT_DIGI_MIC_MODE_RIGHT1 + * - #AFE_PORT_DIGI_MIC_MODE_STEREO0 + * - #AFE_PORT_DIGI_MIC_MODE_STEREO1 + * - #AFE_PORT_DIGI_MIC_MODE_QUAD + */ + + u32 sample_rate; +/* Sampling rate of the port. + * Supported values: + * - #AFE_PORT_SAMPLE_RATE_8K + * - #AFE_PORT_SAMPLE_RATE_16K + * - #AFE_PORT_SAMPLE_RATE_48K + */ +} __packed; + +/* This param id is used to configure HDMI interface */ +#define AFE_PARAM_ID_HDMI_CONFIG 0x00010210 +#define AFE_PARAM_ID_HDMI_DP_MST_VID_IDX_CFG 0x000102b5 +#define AFE_PARAM_ID_HDMI_DPTX_IDX_CFG 0x000102b6 + +/* This version information is used to handle the new + * additions to the config interface in future in backward + * compatible manner. + */ +#define AFE_API_VERSION_HDMI_CONFIG 0x1 + +/* Payload of the #AFE_PARAM_ID_HDMI_CONFIG command, + * which configures a multichannel HDMI audio interface. + */ +struct afe_param_id_hdmi_multi_chan_audio_cfg { + u32 hdmi_cfg_minor_version; +/* Minor version used for tracking the version of the HDMI + * configuration interface. + * Supported values: #AFE_API_VERSION_HDMI_CONFIG + */ + +u16 datatype; +/* data type + * Supported values: + * - #LINEAR_PCM_DATA + * - #NON_LINEAR_DATA + * - #LINEAR_PCM_DATA_PACKED_IN_60958 + * - #NON_LINEAR_DATA_PACKED_IN_60958 + */ + +u16 channel_allocation; +/* HDMI channel allocation information for programming an HDMI + * frame. The default is 0 (Stereo). + * + * This information is defined in the HDMI standard, CEA 861-D + * (refer to @xhyperref{S1,[S1]}). The number of channels is also + * inferred from this parameter. + */ + + +u32 sample_rate; +/* Sampling rate of the port. + * Supported values: + * - #AFE_PORT_SAMPLE_RATE_8K + * - #AFE_PORT_SAMPLE_RATE_16K + * - #AFE_PORT_SAMPLE_RATE_48K + * - #AFE_PORT_SAMPLE_RATE_96K + * - 22050, 44100, 176400 for compressed streams + */ + + u16 bit_width; +/* Bit width of the sample. + * Supported values: 16, 24 + */ + u16 reserved; + /* This field must be set to zero. */ +} __packed; + +/* This param id is used to configure BT or FM(RIVA) interface */ +#define AFE_PARAM_ID_INTERNAL_BT_FM_CONFIG 0x00010211 + +/* This version information is used to handle the new + * additions to the config interface in future in backward + * compatible manner. + */ +#define AFE_API_VERSION_INTERNAL_BT_FM_CONFIG 0x1 + +/* Payload of the #AFE_PARAM_ID_INTERNAL_BT_FM_CONFIG + * command's BT voice/BT audio/FM configuration parameter. + */ +struct afe_param_id_internal_bt_fm_cfg { + u32 bt_fm_cfg_minor_version; +/* Minor version used for tracking the version of the BT and FM + * configuration interface. + * Supported values: #AFE_API_VERSION_INTERNAL_BT_FM_CONFIG + */ + + u16 num_channels; +/* Number of channels. + * Supported values: 1 to 2 + */ + + u16 bit_width; +/* Bit width of the sample. + * Supported values: 16 + */ + + u32 sample_rate; +/* Sampling rate of the port. + * Supported values: + * - #AFE_PORT_SAMPLE_RATE_8K (only for BTSCO) + * - #AFE_PORT_SAMPLE_RATE_16K (only for BTSCO) + * - #AFE_PORT_SAMPLE_RATE_48K (FM and A2DP) + */ +} __packed; + +/* This param id is used to configure SLIMBUS interface using + * shared channel approach. + */ + +/* ID of the parameter used to set the latency mode of the + * USB audio device. + */ +#define AFE_PARAM_ID_PORT_LATENCY_MODE_CONFIG 0x000102B3 + +/* Minor version used for tracking USB audio latency mode */ +#define AFE_API_MINOR_VERSION_USB_AUDIO_LATENCY_MODE 0x1 + +/* Supported AFE port latency modes */ +#define AFE_PORT_DEFAULT_LATENCY_MODE 0x0 +#define AFE_PORT_LOW_LATENCY_MODE 0x1 + +#define AFE_PARAM_ID_SLIMBUS_CONFIG 0x00010212 + +/* This version information is used to handle the new + * additions to the config interface in future in backward + * compatible manner. + */ +#define AFE_API_VERSION_SLIMBUS_CONFIG 0x1 + +/* Enumeration for setting SLIMbus device ID 1. */ +#define AFE_SLIMBUS_DEVICE_1 0x0 + +/* Enumeration for setting SLIMbus device ID 2. */ +#define AFE_SLIMBUS_DEVICE_2 0x1 + +/* Enumeration for setting the SLIMbus data formats. */ +#define AFE_SB_DATA_FORMAT_NOT_INDICATED 0x0 + +/* Enumeration for setting the maximum number of streams per + * device. + */ + +#define AFE_PORT_MAX_AUDIO_CHAN_CNT 0x8 + +/* Payload of the #AFE_PORT_CMD_SLIMBUS_CONFIG command's SLIMbus + * port configuration parameter. + */ + +struct afe_param_id_slimbus_cfg { + u32 sb_cfg_minor_version; +/* Minor version used for tracking the version of the SLIMBUS + * configuration interface. + * Supported values: #AFE_API_VERSION_SLIMBUS_CONFIG + */ + + u16 slimbus_dev_id; +/* SLIMbus hardware device ID, which is required to handle + * multiple SLIMbus hardware blocks. + * Supported values: - #AFE_SLIMBUS_DEVICE_1 - #AFE_SLIMBUS_DEVICE_2 + */ + + + u16 bit_width; +/* Bit width of the sample. + * Supported values: 16, 24 + */ + + u16 data_format; +/* Data format supported by the SLIMbus hardware. The default is + * 0 (#AFE_SB_DATA_FORMAT_NOT_INDICATED), which indicates the + * hardware does not perform any format conversions before the data + * transfer. + */ + + + u16 num_channels; +/* Number of channels. + * Supported values: 1 to #AFE_PORT_MAX_AUDIO_CHAN_CNT + */ + + u8 shared_ch_mapping[AFE_PORT_MAX_AUDIO_CHAN_CNT]; +/* Mapping of shared channel IDs (128 to 255) to which the + * master port is to be connected. + * Shared_channel_mapping[i] represents the shared channel assigned + * for audio channel i in multichannel audio data. + */ + + u32 sample_rate; +/* Sampling rate of the port. + * Supported values: + * - #AFE_PORT_SAMPLE_RATE_8K + * - #AFE_PORT_SAMPLE_RATE_16K + * - #AFE_PORT_SAMPLE_RATE_48K + * - #AFE_PORT_SAMPLE_RATE_96K + * - #AFE_PORT_SAMPLE_RATE_192K + */ +} __packed; + + +/* ID of the parameter used by AFE_PARAM_ID_USB_AUDIO_DEV_PARAMS to configure + * USB audio device parameter. It should be used with + * AFE_MODULE_AUDIO_DEV_INTERFACE + */ +#define AFE_PARAM_ID_USB_AUDIO_DEV_PARAMS 0x000102A5 + + +/* ID of the parameter used to set the endianness value for the + * USB audio device. It should be used with + * AFE_MODULE_AUDIO_DEV_INTERFACE + */ +#define AFE_PARAM_ID_USB_AUDIO_DEV_LPCM_FMT 0x000102AA + +/* Minor version used for tracking USB audio configuration */ +#define AFE_API_MINOR_VERSION_USB_AUDIO_CONFIG 0x1 + +/* Payload of the AFE_PARAM_ID_USB_AUDIO_DEV_PARAMS parameter used by + * AFE_MODULE_AUDIO_DEV_INTERFACE. + */ +struct afe_param_id_usb_audio_dev_params { +/* Minor version used for tracking USB audio device parameter. + * Supported values: AFE_API_MINOR_VERSION_USB_AUDIO_CONFIG + */ + u32 cfg_minor_version; +/* Token of actual end USB aduio device */ + u32 dev_token; +} __packed; + +struct afe_param_id_usb_audio_dev_lpcm_fmt { +/* Minor version used for tracking USB audio device parameter. + * Supported values: AFE_API_MINOR_VERSION_USB_AUDIO_CONFIG + */ + u32 cfg_minor_version; +/* Endianness of actual end USB audio device */ + u32 endian; +} __packed; + +struct afe_param_id_usb_audio_dev_latency_mode { +/* Minor version used for tracking USB audio device parameter. + * Supported values: AFE_API_MINOR_VERSION_USB_AUDIO_LATENCY_MODE + */ + u32 minor_version; +/* latency mode for the USB audio device */ + u32 mode; +} __packed; + +#define AFE_PARAM_ID_USB_AUDIO_SVC_INTERVAL 0x000102B7 + +struct afe_param_id_usb_audio_svc_interval { +/* Minor version used for tracking USB audio device parameter. + * Supported values: AFE_API_MINOR_VERSION_USB_AUDIO_CONFIG + */ + u32 cfg_minor_version; +/* Endianness of actual end USB audio device */ + u32 svc_interval; +} __packed; + +/* ID of the parameter used by AFE_PARAM_ID_USB_AUDIO_CONFIG to configure + * USB audio interface. It should be used with AFE_MODULE_AUDIO_DEV_INTERFACE + */ +#define AFE_PARAM_ID_USB_AUDIO_CONFIG 0x000102A4 + +/* Payload of the AFE_PARAM_ID_USB_AUDIO_CONFIG parameter used by + * AFE_MODULE_AUDIO_DEV_INTERFACE. + */ +struct afe_param_id_usb_audio_cfg { +/* Minor version used for tracking USB audio device configuration. + * Supported values: AFE_API_MINOR_VERSION_USB_AUDIO_CONFIG + */ + u32 cfg_minor_version; +/* Sampling rate of the port. + * Supported values: + * - AFE_PORT_SAMPLE_RATE_8K + * - AFE_PORT_SAMPLE_RATE_11025 + * - AFE_PORT_SAMPLE_RATE_12K + * - AFE_PORT_SAMPLE_RATE_16K + * - AFE_PORT_SAMPLE_RATE_22050 + * - AFE_PORT_SAMPLE_RATE_24K + * - AFE_PORT_SAMPLE_RATE_32K + * - AFE_PORT_SAMPLE_RATE_44P1K + * - AFE_PORT_SAMPLE_RATE_48K + * - AFE_PORT_SAMPLE_RATE_96K + * - AFE_PORT_SAMPLE_RATE_192K + */ + u32 sample_rate; +/* Bit width of the sample. + * Supported values: 16, 24 + */ + u16 bit_width; +/* Number of channels. + * Supported values: 1 and 2 + */ + u16 num_channels; +/* Data format supported by the USB. The supported value is + * 0 (#AFE_USB_AUDIO_DATA_FORMAT_LINEAR_PCM). + */ + u16 data_format; +/* this field must be 0 */ + u16 reserved; +/* device token of actual end USB aduio device */ + u32 dev_token; +/* endianness of this interface */ + u32 endian; +/* service interval */ + u32 service_interval; +} __packed; + +/* This param id is used to configure Real Time Proxy interface. */ +#define AFE_PARAM_ID_RT_PROXY_CONFIG 0x00010213 + +/* This version information is used to handle the new + * additions to the config interface in future in backward + * compatible manner. + */ +#define AFE_API_VERSION_RT_PROXY_CONFIG 0x1 + +/* Payload of the #AFE_PARAM_ID_RT_PROXY_CONFIG + * command (real-time proxy port configuration parameter). + */ +struct afe_param_id_rt_proxy_port_cfg { + u32 rt_proxy_cfg_minor_version; +/* Minor version used for tracking the version of rt-proxy + * config interface. + */ + + u16 bit_width; +/* Bit width of the sample. + * Supported values: 16 + */ + + u16 interleaved; +/* Specifies whether the data exchanged between the AFE + * interface and real-time port is interleaved. + * Supported values: - 0 -- Non-interleaved (samples from each + * channel are contiguous in the buffer) - 1 -- Interleaved + * (corresponding samples from each input channel are interleaved + * within the buffer) + */ + + + u16 frame_size; +/* Size of the frames that are used for PCM exchanges with this + * port. + * Supported values: > 0, in bytes + * For example, 5 ms buffers of 16 bits and 16 kHz stereo samples + * is 5 ms * 16 samples/ms * 2 bytes/sample * 2 channels = 320 + * bytes. + */ + u16 jitter_allowance; +/* Configures the amount of jitter that the port will allow. + * Supported values: > 0 + * For example, if +/-10 ms of jitter is anticipated in the timing + * of sending frames to the port, and the configuration is 16 kHz + * mono with 16-bit samples, this field is 10 ms * 16 samples/ms * 2 + * bytes/sample = 320. + */ + + u16 low_water_mark; +/* Low watermark in bytes (including all channels). + * Supported values: + * - 0 -- Do not send any low watermark events + * - > 0 -- Low watermark for triggering an event + * If the number of bytes in an internal circular buffer is lower + * than this low_water_mark parameter, a LOW_WATER_MARK event is + * sent to applications (via the #AFE_EVENT_RT_PROXY_PORT_STATUS + * event). + * Use of watermark events is optional for debugging purposes. + */ + + u16 high_water_mark; +/* High watermark in bytes (including all channels). + * Supported values: + * - 0 -- Do not send any high watermark events + * - > 0 -- High watermark for triggering an event + * If the number of bytes in an internal circular buffer exceeds + * TOTAL_CIRC_BUF_SIZE minus high_water_mark, a high watermark event + * is sent to applications (via the #AFE_EVENT_RT_PROXY_PORT_STATUS + * event). + * The use of watermark events is optional and for debugging + * purposes. + */ + + + u32 sample_rate; +/* Sampling rate of the port. + * Supported values: + * - #AFE_PORT_SAMPLE_RATE_8K + * - #AFE_PORT_SAMPLE_RATE_16K + * - #AFE_PORT_SAMPLE_RATE_48K + */ + + u16 num_channels; +/* Number of channels. + * Supported values: 1 to #AFE_PORT_MAX_AUDIO_CHAN_CNT + */ + + u16 reserved; + /* For 32 bit alignment. */ +} __packed; + + +/* This param id is used to configure the Pseudoport interface */ + +#define AFE_PARAM_ID_PSEUDO_PORT_CONFIG 0x00010219 + +/* Version information used to handle future additions to the configuration + * interface (for backward compatibility). + */ +#define AFE_API_VERSION_PSEUDO_PORT_CONFIG 0x1 + +/* Enumeration for setting the timing_mode parameter to faster than real + * time. + */ +#define AFE_PSEUDOPORT_TIMING_MODE_FTRT 0x0 + +/* Enumeration for setting the timing_mode parameter to real time using + * timers. + */ +#define AFE_PSEUDOPORT_TIMING_MODE_TIMER 0x1 + +/* Payload of the AFE_PARAM_ID_PSEUDO_PORT_CONFIG parameter used by + * AFE_MODULE_AUDIO_DEV_INTERFACE. + */ +struct afe_param_id_pseudo_port_cfg { + u32 pseud_port_cfg_minor_version; + /* + * Minor version used for tracking the version of the pseudoport + * configuration interface. + */ + + u16 bit_width; + /* Bit width of the sample at values 16, 24 */ + + u16 num_channels; + /* Number of channels at values 1 to 8 */ + + u16 data_format; + /* Non-linear data format supported by the pseudoport (for future use). + * At values #AFE_LINEAR_PCM_DATA + */ + + u16 timing_mode; + /* Indicates whether the pseudoport synchronizes to the clock or + * operates faster than real time. + * at values + * - #AFE_PSEUDOPORT_TIMING_MODE_FTRT + * - #AFE_PSEUDOPORT_TIMING_MODE_TIMER @tablebulletend + */ + + u32 sample_rate; + /* Sample rate at which the pseudoport will run. + * at values + * - #AFE_PORT_SAMPLE_RATE_8K + * - #AFE_PORT_SAMPLE_RATE_32K + * - #AFE_PORT_SAMPLE_RATE_48K + * - #AFE_PORT_SAMPLE_RATE_96K + * - #AFE_PORT_SAMPLE_RATE_192K @tablebulletend + */ +} __packed; + +#define AFE_PARAM_ID_TDM_CONFIG 0x0001029D + +#define AFE_API_VERSION_TDM_CONFIG 1 + +#define AFE_PORT_TDM_SHORT_SYNC_BIT_MODE 0 +#define AFE_PORT_TDM_LONG_SYNC_MODE 1 +#define AFE_PORT_TDM_SHORT_SYNC_SLOT_MODE 2 + +#define AFE_PORT_TDM_SYNC_SRC_EXTERNAL 0 +#define AFE_PORT_TDM_SYNC_SRC_INTERNAL 1 + +#define AFE_PORT_TDM_CTRL_DATA_OE_DISABLE 0 +#define AFE_PORT_TDM_CTRL_DATA_OE_ENABLE 1 + +#define AFE_PORT_TDM_SYNC_NORMAL 0 +#define AFE_PORT_TDM_SYNC_INVERT 1 + +#define AFE_PORT_TDM_DATA_DELAY_0_BCLK_CYCLE 0 +#define AFE_PORT_TDM_DATA_DELAY_1_BCLK_CYCLE 1 +#define AFE_PORT_TDM_DATA_DELAY_2_BCLK_CYCLE 2 + +/* Payload of the AFE_PARAM_ID_TDM_CONFIG parameter used by + * AFE_MODULE_AUDIO_DEV_INTERFACE. + */ +struct afe_param_id_tdm_cfg { + u32 tdm_cfg_minor_version; + /* < Minor version used to track TDM configuration. + * @values #AFE_API_VERSION_TDM_CONFIG + */ + + u32 num_channels; + /* < Number of enabled slots for TDM frame. + * @values 1 to 8 + */ + + u32 sample_rate; + /* < Sampling rate of the port. + * @values + * - #AFE_PORT_SAMPLE_RATE_8K + * - #AFE_PORT_SAMPLE_RATE_16K + * - #AFE_PORT_SAMPLE_RATE_24K + * - #AFE_PORT_SAMPLE_RATE_32K + * - #AFE_PORT_SAMPLE_RATE_48K + * - #AFE_PORT_SAMPLE_RATE_176P4K + * - #AFE_PORT_SAMPLE_RATE_352P8K @tablebulletend + */ + + u32 bit_width; + /* < Bit width of the sample. + * @values 16, 24 + */ + + u16 data_format; + /* < Data format: linear ,compressed, generic compresssed + * @values + * - #AFE_LINEAR_PCM_DATA + * - #AFE_NON_LINEAR_DATA + * - #AFE_GENERIC_COMPRESSED + */ + + u16 sync_mode; + /* < TDM synchronization setting. + * @values (short, long, slot) sync mode + * - #AFE_PORT_TDM_SHORT_SYNC_BIT_MODE + * - #AFE_PORT_TDM_LONG_SYNC_MODE + * - #AFE_PORT_TDM_SHORT_SYNC_SLOT_MODE @tablebulletend + */ + + u16 sync_src; + /* < Synchronization source. + * @values + * - #AFE_PORT_TDM_SYNC_SRC_EXTERNAL + * - #AFE_PORT_TDM_SYNC_SRC_INTERNAL @tablebulletend + */ + + u16 nslots_per_frame; + /* < Number of slots per frame. Typical : 1, 2, 4, 8, 16, 32. + * @values 1 - 32 + */ + + u16 ctrl_data_out_enable; + /* < Specifies whether the TDM block shares the data-out signal to the + * drive with other masters. + * @values + * - #AFE_PORT_TDM_CTRL_DATA_OE_DISABLE + * - #AFE_PORT_TDM_CTRL_DATA_OE_ENABLE @tablebulletend + */ + + u16 ctrl_invert_sync_pulse; + /* < Specifies whether to invert the sync or not. + * @values + * - #AFE_PORT_TDM_SYNC_NORMAL + * - #AFE_PORT_TDM_SYNC_INVERT @tablebulletend + */ + + u16 ctrl_sync_data_delay; + /* < Specifies the number of bit clock to delay data with respect to + * sync edge. + * @values + * - #AFE_PORT_TDM_DATA_DELAY_0_BCLK_CYCLE + * - #AFE_PORT_TDM_DATA_DELAY_1_BCLK_CYCLE + * - #AFE_PORT_TDM_DATA_DELAY_2_BCLK_CYCLE @tablebulletend + */ + + u16 slot_width; + /* < Slot width of the slot in a TDM frame. (slot_width >= bit_width) + * have to be satisfied. + * @values 16, 24, 32 + */ + + u32 slot_mask; + /* < Position of active slots. When that bit is set, + * that paricular slot is active. + * Number of active slots can be inferred by number of + * bits set in the mask. Only 8 individual bits can be enabled. + * Bits 0..31 corresponding to slot 0..31 + * @values 1 to 2^32 - 1 + */ +} __packed; + +/* ID of Time Divsion Multiplexing (TDM) module, + * which is used for configuring the AFE TDM. + * + * This module supports following parameter IDs: + * - #AFE_PORT_TDM_SLOT_CONFIG + * + * To configure the TDM interface, the client must use the + * #AFE_PORT_CMD_SET_PARAM command, and fill the module ID with the + * respective parameter IDs as listed above. + */ + +#define AFE_MODULE_TDM 0x0001028A + +/* ID of the parameter used by #AFE_MODULE_TDM to configure + * the TDM slot mapping. #AFE_PORT_CMD_SET_PARAM can use this parameter ID. + */ +#define AFE_PARAM_ID_PORT_SLOT_MAPPING_CONFIG 0x00010297 + +/* Version information used to handle future additions to slot mapping + * configuration (for backward compatibility). + */ +#define AFE_API_VERSION_SLOT_MAPPING_CONFIG 0x1 + +/* Data align type */ +#define AFE_SLOT_MAPPING_DATA_ALIGN_MSB 0 +#define AFE_SLOT_MAPPING_DATA_ALIGN_LSB 1 + +#define AFE_SLOT_MAPPING_OFFSET_INVALID 0xFFFF + +/* Payload of the AFE_PARAM_ID_PORT_SLOT_MAPPING_CONFIG + * command's TDM configuration parameter. + */ +struct afe_param_id_slot_mapping_cfg { + u32 minor_version; + /* < Minor version used for tracking TDM slot configuration. + * @values #AFE_API_VERSION_TDM_SLOT_CONFIG + */ + + u16 num_channel; + /* < number of channel of the audio sample. + * @values 1, 2, 4, 6, 8 @tablebulletend + */ + + u16 bitwidth; + /* < Slot bit width for each channel + * @values 16, 24, 32 + */ + + u32 data_align_type; + /* < indicate how data packed from slot_offset for 32 slot bit width + * in case of sample bit width is 24. + * @values + * #AFE_SLOT_MAPPING_DATA_ALIGN_MSB + * #AFE_SLOT_MAPPING_DATA_ALIGN_LSB + */ + + u16 offset[AFE_PORT_MAX_AUDIO_CHAN_CNT]; + /* < Array of the slot mapping start offset in bytes for this frame. + * The bytes is counted from 0. The 0 is mapped to the 1st byte + * in or out of the digital serial data line this sub-frame belong to. + * slot_offset[] setting is per-channel based. + * The max num of channel supported is 8. + * The valid offset value must always be continuly placed in from + * index 0. + * Set offset as AFE_SLOT_MAPPING_OFFSET_INVALID for not used arrays. + * If "slot_bitwidth_per_channel" is 32 and "sample_bitwidth" is 24, + * "data_align_type" is used to indicate how 24 bit sample data in + * aligning with 32 bit slot width per-channel. + * @values, in byte + */ +} __packed; + +/* ID of the parameter used by #AFE_MODULE_TDM to configure + * the customer TDM header. #AFE_PORT_CMD_SET_PARAM can use this parameter ID. + */ +#define AFE_PARAM_ID_CUSTOM_TDM_HEADER_CONFIG 0x00010298 + +/* Version information used to handle future additions to custom TDM header + * configuration (for backward compatibility). + */ +#define AFE_API_VERSION_CUSTOM_TDM_HEADER_CONFIG 0x1 + +#define AFE_CUSTOM_TDM_HEADER_TYPE_INVALID 0x0 +#define AFE_CUSTOM_TDM_HEADER_TYPE_DEFAULT 0x1 +#define AFE_CUSTOM_TDM_HEADER_TYPE_ENTERTAINMENT_MOST 0x2 + +#define AFE_CUSTOM_TDM_HEADER_MAX_CNT 0x8 + +/* Payload of the AFE_PARAM_ID_CUSTOM_TDM_HEADER_CONFIG parameter ID */ +struct afe_param_id_custom_tdm_header_cfg { + u32 minor_version; + /* < Minor version used for tracking custom TDM header configuration. + * @values #AFE_API_VERSION_CUSTOM_TDM_HEADER_CONFIG + */ + + u16 start_offset; + /* < the slot mapping start offset in bytes from this sub-frame + * The bytes is counted from 0. The 0 is mapped to the 1st byte in or + * out of the digital serial data line this sub-frame belong to. + * @values, in byte, + * supported values are 0, 4, 8 + */ + + u16 header_width; + /* < the header width per-frame followed. + * 2 bytes for MOST/TDM case + * @values, in byte + * supported value is 2 + */ + + u16 header_type; + /* < Indicate what kind of custom TDM header it is. + * @values #AFE_CUSTOM_TDM_HEADER_TYPE_INVALID = 0 + * #AFE_CUSTOM_TDM_HEADER_TYPE_DEFAULT = 1 (for AAN channel per MOST) + * #AFE_CUSTOM_TDM_HEADER_TYPE_ENTERTAINMENT_MOST = 2 + * (for entertainment channel, which will overwrite + * AFE_API_VERSION_TDM_SAD_HEADER_TYPE_DEFAULT per MOST) + */ + + u16 num_frame_repeat; + /* < num of header followed. + * @values, supported value is 8 + */ + u16 header[AFE_CUSTOM_TDM_HEADER_MAX_CNT]; + /* < SAD header for MOST/TDM case is followed as payload as below. + * The size of followed SAD header in bytes is num_of_frame_repeat * + * header_width_per_frame, which is 2 * 8 = 16 bytes here. + * the supported payload format is in uint16_t as below + * uint16_t header0; SyncHi 0x3C Info[4] - CodecType -> 0x3C00 + * uint16_t header1; SyncLo 0xB2 Info[5] - SampleWidth -> 0xB218 + * uint16_t header2; DTCP Info Info[6] - unused -> 0x0 + * uint16_t header3; Extension Info[7] - ASAD-Value -> 0xC0 + * uint16_t header4; Reserved Info[0] - Num of bytes following -> 0x7 + * uint16_t header5; Reserved Info[1] - Media Type -> 0x0 + * uint16_t header6; Reserved Info[2] - Bitrate[kbps] - High Byte -> 0x0 + * uint16_t header7; Reserved Info[3] - Bitrate[kbps] - Low Byte -> 0x0 + */ +} __packed; + +struct afe_tdm_port_config { + struct afe_param_id_tdm_cfg tdm; + struct afe_param_id_slot_mapping_cfg slot_mapping; + struct afe_param_id_custom_tdm_header_cfg custom_tdm_header; +} __packed; + +#define AFE_PARAM_ID_DEVICE_HW_DELAY 0x00010243 +#define AFE_API_VERSION_DEVICE_HW_DELAY 0x1 + +struct afe_param_id_device_hw_delay_cfg { + uint32_t device_hw_delay_minor_version; + uint32_t delay_in_us; +} __packed; + +#define AFE_PARAM_ID_SET_TOPOLOGY 0x0001025A +#define AFE_API_VERSION_TOPOLOGY_V1 0x1 + +struct afe_param_id_set_topology_cfg { + /* + * Minor version used for tracking afe topology id configuration. + * @values #AFE_API_VERSION_TOPOLOGY_V1 + */ + u32 minor_version; + /* + * Id of the topology for the afe session. + * @values Any valid AFE topology ID + */ + u32 topology_id; +} __packed; + +#define MAX_ABR_LEVELS 5 + +struct afe_bit_rate_level_map_t { + /* + * Key value pair for link quality level to bitrate + * mapping in AFE + */ + uint32_t link_quality_level; + uint32_t bitrate; +} __packed; + +struct afe_quality_level_to_bitrate_info { + /* + * Number of quality levels being mapped. + * This will be equal to the size of mapping table. + */ + uint32_t num_levels; + /* + * Quality level to bitrate mapping table + */ + struct afe_bit_rate_level_map_t bit_rate_level_map[MAX_ABR_LEVELS]; +} __packed; + +struct afe_imc_dec_enc_info { + /* + * Decoder to encoder communication direction. + * Transmit = 0 / Receive = 1 + */ + uint32_t direction; + /* + * Enable / disable IMC between decoder and encoder + */ + uint32_t enable; + /* + * Purpose of IMC being set up between decoder and encoder. + * Param ID defined for link quality feedback in LPASS will + * be the default value sent as purpose. + * Supported values: + * AFE_ENCDEC_PURPOSE_ID_BT_INFO + */ + uint32_t purpose; + /* + * Unique communication instance ID. + * Data type a2dp_abr_instance used to set instance ID. + * purpose and comm_instance together form the actual key + * used in IMC registration, which must be the same for + * encoder and decoder for which IMC is being set up. + */ + uint32_t comm_instance; +} __packed; + +struct afe_abr_dec_cfg_t { + struct afe_imc_dec_enc_info imc_info; +} __packed; + +struct afe_abr_enc_cfg_t { + /* + * Link quality level to bitrate mapping info sent to DSP. + */ + struct afe_quality_level_to_bitrate_info mapping_info; + /* + * Information to set up IMC between decoder and encoder. + */ + struct afe_imc_dec_enc_info imc_info; + /* + * Flag to indicate whether ABR is enabled. + */ + bool is_abr_enabled; +} __packed; + +#define AFE_PARAM_ID_APTX_SYNC_MODE 0x00013205 + +struct afe_param_id_aptx_sync_mode { + /* + * sync mode: 0x0 = stereo sync mode (default) + * 0x01 = dual mono sync mode + * 0x02 = dual mono with no sync on either L or R + */ + uint32_t sync_mode; +} __packed; + +#define AFE_ID_APTX_ADAPTIVE_ENC_INIT 0x00013324 + +struct afe_id_aptx_adaptive_enc_init +{ + uint32_t sampling_freq; + uint32_t mtu; + uint32_t channel_mode; + uint32_t min_sink_modeA; + uint32_t max_sink_modeA; + uint32_t min_sink_modeB; + uint32_t max_sink_modeB; + uint32_t min_sink_modeC; + uint32_t max_sink_modeC; + uint32_t mode; +} __attribute__ ((packed)); + +/* + * Generic encoder module ID. + * This module supports the following parameter IDs: + * #AVS_ENCODER_PARAM_ID_ENC_FMT_ID (cannot be set run time) + * #AVS_ENCODER_PARAM_ID_ENC_CFG_BLK (may be set run time) + * #AVS_ENCODER_PARAM_ID_ENC_BITRATE (may be set run time) + * #AVS_ENCODER_PARAM_ID_PACKETIZER_ID (cannot be set run time) + * Opcode - AVS_MODULE_ID_ENCODER + * AFE Command AFE_PORT_CMD_SET_PARAM_V2 supports this module ID. + */ +#define AFE_MODULE_ID_ENCODER 0x00013229 + +/* Macro for defining the packetizer ID: COP. */ +#define AFE_MODULE_ID_PACKETIZER_COP 0x0001322A + +/* + * Packetizer type parameter for the #AVS_MODULE_ID_ENCODER module. + * This parameter cannot be set runtime. + */ +#define AFE_ENCODER_PARAM_ID_PACKETIZER_ID 0x0001322E + +/* + * Encoder config block parameter for the #AVS_MODULE_ID_ENCODER module. + * This parameter may be set runtime. + */ +#define AFE_ENCODER_PARAM_ID_ENC_CFG_BLK 0x0001322C + +/* + * Encoder format ID parameter for the #AVS_MODULE_ID_ENCODER module. + * This parameter cannot be set runtime. + */ +#define AFE_ENCODER_PARAM_ID_ENC_FMT_ID 0x0001322B + +/* + * Decoder format ID parameter for the #AVS_MODULE_ID_DECODER module. + * This parameter cannot be set runtime. + */ +#define AFE_DECODER_PARAM_ID_DEC_FMT_ID 0x00013234 + +/* + * Encoder scrambler parameter for the #AVS_MODULE_ID_ENCODER module. + * This parameter cannot be set runtime. + */ +#define AFE_ENCODER_PARAM_ID_ENABLE_SCRAMBLING 0x0001323C + +/* + * Link quality level to bitrate mapping info sent to AFE Encoder. + * This parameter may be set runtime. + */ +#define AFE_ENCODER_PARAM_ID_BIT_RATE_LEVEL_MAP 0x000132E1 + +/* + * Parameter to set up Inter Module Communication (IMC) between + * AFE Decoder and Encoder. + * This parameter may be set runtime. + */ +#define AFE_ENCDEC_PARAM_ID_DEC_TO_ENC_COMMUNICATION 0x0001323D + +/* + * Purpose of IMC set up between encoder and decoder. + * Communication instance and purpose together form the + * actual key used for IMC registration. + */ +#define AFE_ENCDEC_PURPOSE_ID_BT_INFO 0x000132E2 + +#define AFE_MODULE_ID_DECODER 0x00013231 + +/* + * Macro for defining the depacketizer ID: COP. + */ +#define AFE_MODULE_ID_DEPACKETIZER_COP 0x00013233 +#define AFE_MODULE_ID_DEPACKETIZER_COP_V1 0x000132E9 + +/* + * Depacketizer type parameter for the #AVS_MODULE_ID_DECODER module. + * This parameter cannot be set runtime. + */ +#define AFE_DECODER_PARAM_ID_DEPACKETIZER_ID 0x00013235 + +#define CAPI_V2_PARAM_ID_APTX_ENC_SWITCH_TO_MONO 0x0001332A + +struct aptx_channel_mode_param_t { + u32 channel_mode; +} __packed; +/* + * Decoder buffer ID parameter for the #AVS_MODULE_ID_DECODER module. + * This parameter cannot be set runtime. + */ +#define AFE_DECODER_PARAM_ID_CONGESTION_BUFFER_SIZE 0x000132ec + +/* + * Data format to send compressed data + * is transmitted/received over Slimbus lines. + */ +#define AFE_SB_DATA_FORMAT_GENERIC_COMPRESSED 0x3 + +/* + * Parameter to send frame control size + * to DSP for AAC encoder in AFE. + */ +#define AFE_PARAM_ID_AAC_FRM_SIZE_CONTROL 0x000132EA + +/* + * ID for AFE port module. This will be used to define port properties. + * This module supports following parameter IDs: + * #AFE_PARAM_ID_PORT_MEDIA_TYPE + * To configure the port property, the client must use the + * #AFE_PORT_CMD_SET_PARAM_V2 command, + * and fill the module ID with the respective parameter IDs as listed above. + * @apr_hdr_fields + * Opcode -- AFE_MODULE_PORT + */ +#define AFE_MODULE_PORT 0x000102a6 + +/* + * ID of the parameter used by #AFE_MODULE_PORT to set the port media type. + * parameter ID is currently supported using#AFE_PORT_CMD_SET_PARAM_V2 command. + */ +#define AFE_PARAM_ID_PORT_MEDIA_TYPE 0x000102a7 + +/* + * Macros for defining the "data_format" field in the + * #AFE_PARAM_ID_PORT_MEDIA_TYPE + */ +#define AFE_PORT_DATA_FORMAT_PCM 0x0 +#define AFE_PORT_DATA_FORMAT_GENERIC_COMPRESSED 0x1 + +/* + * Macro for defining the "minor_version" field in the + * #AFE_PARAM_ID_PORT_MEDIA_TYPE + */ +#define AFE_API_VERSION_PORT_MEDIA_TYPE 0x1 + +#define ASM_MEDIA_FMT_NONE 0x0 + +/* + * Media format ID for SBC encode configuration. + * @par SBC encode configuration (asm_sbc_enc_cfg_t) + * @table{weak__asm__sbc__enc__cfg__t} + */ +#define ASM_MEDIA_FMT_SBC 0x00010BF2 + +/* SBC channel Mono mode.*/ +#define ASM_MEDIA_FMT_SBC_CHANNEL_MODE_MONO 1 + +/* SBC channel Stereo mode. */ +#define ASM_MEDIA_FMT_SBC_CHANNEL_MODE_STEREO 2 + +/* SBC channel Dual Mono mode. */ +#define ASM_MEDIA_FMT_SBC_CHANNEL_MODE_DUAL_MONO 8 + +/* SBC channel Joint Stereo mode. */ +#define ASM_MEDIA_FMT_SBC_CHANNEL_MODE_JOINT_STEREO 9 + +/* SBC bit allocation method = loudness. */ +#define ASM_MEDIA_FMT_SBC_ALLOCATION_METHOD_LOUDNESS 0 + +/* SBC bit allocation method = SNR. */ +#define ASM_MEDIA_FMT_SBC_ALLOCATION_METHOD_SNR 1 + + +/* + * Payload of the SBC encoder configuration parameters in the + * #ASM_MEDIA_FMT_SBC media format. + */ +struct asm_sbc_enc_cfg_t { + /* + * Number of subbands. + * @values 4, 8 + */ + uint32_t num_subbands; + + /* + * Size of the encoded block in samples. + * @values 4, 8, 12, 16 + */ + uint32_t blk_len; + + /* + * Mode used to allocate bits between channels. + * @values + * 0 (Native mode) + * #ASM_MEDIA_FMT_SBC_CHANNEL_MODE_MONO + * #ASM_MEDIA_FMT_SBC_CHANNEL_MODE_STEREO + * #ASM_MEDIA_FMT_SBC_CHANNEL_MODE_DUAL_MONO + * #ASM_MEDIA_FMT_SBC_CHANNEL_MODE_JOINT_STEREO + * Native mode indicates that encoding must be performed with the number + * of channels at the input. + * If postprocessing outputs one-channel data, Mono mode is used. If + * postprocessing outputs two-channel data, Stereo mode is used. + * The number of channels must not change during encoding. + */ + uint32_t channel_mode; + + /* + * Encoder bit allocation method. + * @values + * #ASM_MEDIA_FMT_SBC_ALLOCATION_METHOD_LOUDNESS + * #ASM_MEDIA_FMT_SBC_ALLOCATION_METHOD_SNR @tablebulletend + */ + uint32_t alloc_method; + + /* + * Number of encoded bits per second. + * @values + * Mono channel -- Maximum of 320 kbps + * Stereo channel -- Maximum of 512 kbps @tablebulletend + */ + uint32_t bit_rate; + + /* + * Number of samples per second. + * @values 0 (Native mode), 16000, 32000, 44100, 48000 Hz + * Native mode indicates that encoding must be performed with the + * sampling rate at the input. + * The sampling rate must not change during encoding. + */ + uint32_t sample_rate; +}; + +#define ASM_MEDIA_FMT_AAC_AOT_LC 2 +#define ASM_MEDIA_FMT_AAC_AOT_SBR 5 +#define ASM_MEDIA_FMT_AAC_AOT_PS 29 +#define ASM_MEDIA_FMT_AAC_FORMAT_FLAG_ADTS 0 +#define ASM_MEDIA_FMT_AAC_FORMAT_FLAG_RAW 3 + +struct asm_aac_enc_cfg_v2_t { + + /* Encoding rate in bits per second.*/ + uint32_t bit_rate; + + /* + * Encoding mode. + * Supported values: + * #ASM_MEDIA_FMT_AAC_AOT_LC + * #ASM_MEDIA_FMT_AAC_AOT_SBR + * #ASM_MEDIA_FMT_AAC_AOT_PS + */ + uint32_t enc_mode; + + /* + * AAC format flag. + * Supported values: + * #ASM_MEDIA_FMT_AAC_FORMAT_FLAG_ADTS + * #ASM_MEDIA_FMT_AAC_FORMAT_FLAG_RAW + */ + uint16_t aac_fmt_flag; + + /* + * Number of channels to encode. + * Supported values: + * 0 - Native mode + * 1 - Mono + * 2 - Stereo + * Other values are not supported. + * @note1hang The eAAC+ encoder mode supports only stereo. + * Native mode indicates that encoding must be performed with the + * number of channels at the input. + * The number of channels must not change during encoding. + */ + uint16_t channel_cfg; + + /* + * Number of samples per second. + * Supported values: - 0 -- Native mode - For other values, + * Native mode indicates that encoding must be performed with the + * sampling rate at the input. + * The sampling rate must not change during encoding. + */ + uint32_t sample_rate; +} __packed; + +/* Structure to control frame size of AAC encoded frames. */ +struct asm_aac_frame_size_control_t { + /* Type of frame size control: MTU_SIZE / PEAK_BIT_RATE*/ + uint32_t ctl_type; + /* + * Control value + * MTU_SIZE: MTU size in bytes + * PEAK_BIT_RATE: Peak bitrate in bits per second. + */ + uint32_t ctl_value; +} __packed; + +struct asm_aac_enc_cfg_t { + struct asm_aac_enc_cfg_v2_t aac_cfg; + struct asm_aac_frame_size_control_t frame_ctl; +} __packed; + +/* FMT ID for apt-X Classic */ +#define ASM_MEDIA_FMT_APTX 0x000131ff + +/* FMT ID for apt-X HD */ +#define ASM_MEDIA_FMT_APTX_HD 0x00013200 + +/* FMT ID for apt-X Adaptive */ +#define ASM_MEDIA_FMT_APTX_ADAPTIVE 0x00013204 + +#define PCM_CHANNEL_L 1 +#define PCM_CHANNEL_R 2 +#define PCM_CHANNEL_C 3 + +struct asm_custom_enc_cfg_t { + uint32_t sample_rate; + /* Mono or stereo */ + uint16_t num_channels; + uint16_t reserved; + /* num_ch == 1, then PCM_CHANNEL_C, + * num_ch == 2, then {PCM_CHANNEL_L, PCM_CHANNEL_R} + */ + uint8_t channel_mapping[8]; + uint32_t custom_size; +} __packed; + +struct asm_aptx_v2_enc_cfg_ext_t { + /* + * sync mode: 0x0 = stereo sync mode (default) + * 0x01 = dual mono sync mode + * 0x02 = dual mono with no sync on either L or R + */ + uint32_t sync_mode; +} __packed; + +struct asm_aptx_enc_cfg_t { + struct asm_custom_enc_cfg_t custom_cfg; + struct asm_aptx_v2_enc_cfg_ext_t aptx_v2_cfg; +} __packed; + +struct asm_aptx_ad_enc_cfg_t +{ + struct asm_custom_enc_cfg_t custom_cfg; + struct afe_id_aptx_adaptive_enc_init aptx_ad_cfg; + struct afe_abr_enc_cfg_t abr_cfg; +} __attribute__ ((packed)); + +#define ASM_MEDIA_FMT_CELT 0x00013221 +struct asm_celt_specific_enc_cfg_t { + /* + * Bit rate used for encoding. + * This is used to calculate the upper threshold + * for bytes per frame if vbr_flag is 1. + * Or else, this will be used as a regular constant + * bit rate for encoder output. + * @Range : 32000 to 1536000 + * @Default: 128 + */ + uint32_t bit_rate; + /* + * Frame size used for encoding. + * @Range : 64, 128, 256, 512 + * @Default: 256 + */ + uint16_t frame_size; + /* + * complexity of algorithm. + * @Range : 0-10 + * @Default: 3 + */ + uint16_t complexity; + /* + * Switch variable for prediction feature. + * Used to choose between the level of interframe + * predictions allowed while encoding. + * @Range: + * 0: Independent Frames. + * 1: Short Term interframe prediction allowed. + * 2: Long term prediction allowed. + * @Default: 2 + */ + uint16_t prediction_mode; + /* + * Variable Bit Rate flag. + * @Default: 0 + */ + uint16_t vbr_flag; +} __packed; + +struct asm_celt_enc_cfg_t { + struct asm_custom_enc_cfg_t custom_config; + struct asm_celt_specific_enc_cfg_t celt_specific_config; +} __packed; + +#define ASM_MEDIA_FMT_LDAC 0x00013224 +#define ENC_CODEC_TYPE_LDAC 0x23000000 +struct asm_ldac_specific_enc_cfg_t { + /* + * This is used to calculate the encoder output + * bytes per frame (i.e. bytes per packet). + * Bit rate also configures the EQMID. + * The min bit rate 303000 bps is calculated for + * 44.1 kHz and 88.2 KHz sampling frequencies with + * Mobile use Quality. + * The max bit rate of 990000 bps is calculated for + * 96kHz and 48 KHz with High Quality + * @Range(in bits per second) + * 303000 for Mobile use Quality + * 606000 for standard Quality + * 909000 for High Quality + */ + uint32_t bit_rate; + /* + * The channel setting information for LDAC specification + * of Bluetooth A2DP which is determined by SRC and SNK + * devices in Bluetooth transmission. + * @Range: + * 0 for native mode + * 4 for mono + * 2 for dual channel + * 1 for stereo + */ + uint16_t channel_mode; + /* + * Maximum Transmission Unit (MTU). + * The minimum MTU that a L2CAP implementation for LDAC shall + * support is 679 bytes, because LDAC is optimized with 2-DH5 + * packet as its target. + * @Range : 679 + * @Default: 679 for LDACBT_MTU_2DH5 + */ + uint16_t mtu; +} __packed; + +struct asm_ldac_enc_cfg_t { + struct asm_custom_enc_cfg_t custom_config; + struct asm_ldac_specific_enc_cfg_t ldac_specific_config; + struct afe_abr_enc_cfg_t abr_config; +} __packed; + +struct afe_enc_fmt_id_param_t { + /* + * Supported values: + * #ASM_MEDIA_FMT_SBC + * #ASM_MEDIA_FMT_AAC_V2 + * Any OpenDSP supported values + */ + uint32_t fmt_id; +} __packed; + +struct afe_port_media_type_t { + /* + * Minor version + * @values #AFE_API_VERSION_PORT_MEDIA_TYPE. + */ + uint32_t minor_version; + + /* + * Sampling rate of the port. + * @values + * #AFE_PORT_SAMPLE_RATE_8K + * #AFE_PORT_SAMPLE_RATE_11_025K + * #AFE_PORT_SAMPLE_RATE_12K + * #AFE_PORT_SAMPLE_RATE_16K + * #AFE_PORT_SAMPLE_RATE_22_05K + * #AFE_PORT_SAMPLE_RATE_24K + * #AFE_PORT_SAMPLE_RATE_32K + * #AFE_PORT_SAMPLE_RATE_44_1K + * #AFE_PORT_SAMPLE_RATE_48K + * #AFE_PORT_SAMPLE_RATE_88_2K + * #AFE_PORT_SAMPLE_RATE_96K + * #AFE_PORT_SAMPLE_RATE_176_4K + * #AFE_PORT_SAMPLE_RATE_192K + * #AFE_PORT_SAMPLE_RATE_352_8K + * #AFE_PORT_SAMPLE_RATE_384K + */ + uint32_t sample_rate; + + /* + * Bit width of the sample. + * @values 16, 24 + */ + uint16_t bit_width; + + /* + * Number of channels. + * @values 1 to #AFE_PORT_MAX_AUDIO_CHAN_CNT + */ + uint16_t num_channels; + + /* + * Data format supported by this port. + * If the port media type and device media type are different, + * it signifies a encoding/decoding use case + * @values + * #AFE_PORT_DATA_FORMAT_PCM + * #AFE_PORT_DATA_FORMAT_GENERIC_COMPRESSED + */ + uint16_t data_format; + + /*This field must be set to zero.*/ + uint16_t reserved; +} __packed; + +/* + * Payload of the SBC decoder configuration parameters in the + * #ASM_MEDIA_FMT_SBC media format. + */ +struct asm_sbc_dec_cfg_t { + /* All configuration is extracted from the stream */ +} __packed; + +/* + * Payload of the MP3 decoder configuration parameters in the + * #ASM_MEDIA_FMT_MP3 media format. + */ +struct asm_mp3_dec_cfg_t { + /* All configuration is extracted from the stream */ +} __packed; + +struct asm_aac_dec_cfg_v2_t { + uint16_t aac_fmt_flag; + /* + * Bit stream format option. + * + * @values + * - #ASM_MEDIA_FMT_AAC_FORMAT_FLAG_ADTS + * - #ASM_MEDIA_FMT_AAC_FORMAT_FLAG_LOAS + * - #ASM_MEDIA_FMT_AAC_FORMAT_FLAG_ADIF + * - #ASM_MEDIA_FMT_AAC_FORMAT_FLAG_RAW + * - #ASM_MEDIA_FMT_AAC_FORMAT_FLAG_LATM + */ + + uint16_t audio_obj_type; + /* + * Audio Object Type (AOT) present in the AAC stream. + * + * @values + * - #ASM_MEDIA_FMT_AAC_AOT_LC + * - #ASM_MEDIA_FMT_AAC_AOT_SBR + * - #ASM_MEDIA_FMT_AAC_AOT_BSAC + * - #ASM_MEDIA_FMT_AAC_AOT_PS + * + * Other values are not supported. + */ + + uint16_t channel_config; + /* + * Number of channels present in the AAC stream. + * + * @values + * - 0 -- PCE + * - 1 -- Mono + * - 2 -- Stereo + * - 6 -- 5.1 content + */ + + uint16_t total_size_of_PCE_bits; + /* + * For RAW formats and if channel_config=0 (PCE), + * the client can send the bit stream containing PCE + * immediately following this structure (in band). + * + * @values @ge 0 (does not include the bits required + * for 32-bit alignment) + * + * If this field is set to 0, the PCE information is + * assumed to be available in the audio bit stream + * and not in band. + * + * If this field is greater than 0, the PCE information + * follows this structure. Additional bits might + * be required for 32-bit alignment. + */ + + uint32_t sample_rate; + /* + * Number of samples per second. + * + * @values 8000, 11025, 12000, 16000, 22050, 24000, 32000, + * 44100, 48000, 64000, 88200, 96000 Hz + * + * This field must be equal to the sample rate of the + * AAC-LC decoder output. + * - For MP4 or 3GP containers, this sample rate + * is indicated by the + * samplingFrequencyIndex field in the + * AudioSpecificConfig element. + * - For ADTS format, this sample rate is indicated by the + * samplingFrequencyIndex in the ADTS fixed header. + * - For ADIF format, this sample rate is indicated by the + * samplingFrequencyIndex in the program_config_element + * present in the ADIF header. + */ +} __packed; + +union afe_enc_config_data { + struct asm_sbc_enc_cfg_t sbc_config; + struct asm_aac_enc_cfg_t aac_config; + struct asm_custom_enc_cfg_t custom_config; + struct asm_celt_enc_cfg_t celt_config; + struct asm_aptx_enc_cfg_t aptx_config; + struct asm_ldac_enc_cfg_t ldac_config; + struct asm_aptx_ad_enc_cfg_t aptx_ad_config; +}; + +struct afe_enc_config { + u32 format; + u32 scrambler_mode; + u32 mono_mode; + union afe_enc_config_data data; +}; + +union afe_dec_config_data { + struct asm_sbc_dec_cfg_t sbc_config; + struct asm_aac_dec_cfg_v2_t aac_config; + struct asm_mp3_dec_cfg_t mp3_config; +}; + +struct afe_dec_config { + u32 format; + struct afe_abr_dec_cfg_t abr_dec_cfg; + union afe_dec_config_data data; +}; + +struct afe_enc_cfg_blk_param_t { + uint32_t enc_cfg_blk_size; + /* + *Size of the encoder configuration block that follows this member + */ + union afe_enc_config_data enc_blk_config; +}; + +/* + * Payload of the AVS_DECODER_PARAM_ID_DEC_MEDIA_FMT parameter used by + * AVS_MODULE_ID_DECODER. + */ +struct afe_dec_media_fmt_t { + union afe_dec_config_data dec_media_config; +} __packed; + +/* + * Payload of the AVS_ENCODER_PARAM_ID_PACKETIZER_ID parameter. + */ +struct avs_enc_packetizer_id_param_t { + /* + * Supported values: + * #AVS_MODULE_ID_PACKETIZER_COP + * Any OpenDSP supported values + */ + uint32_t enc_packetizer_id; +}; + +/* + * Payload of the AVS_ENCODER_PARAM_ID_ENABLE_SCRAMBLING parameter. + */ +struct avs_enc_set_scrambler_param_t { + /* + * Supported values: + * 1 : enable scrambler + * 0 : disable scrambler + */ + uint32_t enable_scrambler; +}; + +/* + * Payload of the AVS_ENCODER_PARAM_ID_BIT_RATE_LEVEL_MAP parameter. + */ +struct afe_enc_level_to_bitrate_map_param_t { + /* + * Parameter for mapping link quality level to bitrate. + */ + struct afe_quality_level_to_bitrate_info mapping_table; +}; + +/* + * Payload of the AVS_ENCDEC_PARAM_ID_DEC_TO_ENC_COMMUNICATION parameter. + */ +struct afe_enc_dec_imc_info_param_t { + /* + * Parameter to set up Inter Module Communication (IMC) between + * AFE Decoder and Encoder. + */ + struct afe_imc_dec_enc_info imc_info; +}; + +/* + * Payload of the AVS_DECODER_PARAM_ID_DEPACKETIZER_ID parameter. + */ +struct avs_dec_depacketizer_id_param_t { + /* + * Supported values: + * #AFE_MODULE_ID_DEPACKETIZER_COP + * #AFE_MODULE_ID_DEPACKETIZER_COP_V1 + * Any OpenDSP supported values + */ + uint32_t dec_depacketizer_id; +}; + +struct avs_dec_congestion_buffer_param_t { + uint32_t version; + uint16_t max_nr_buffers; + /* + * Maximum number of 1ms buffers: + * 0 - 256 + */ + uint16_t pre_buffer_size; + /* + * Pre-buffering size in 1ms: + * 1 - 128 + */ +}; + +/* + * ID of the parameter used by #AVS_MODULE_ID_DECODER to configure + * the decoder mode for the AFE module. + * This parameter cannot be set at runtime. + */ +#define AVS_DECODER_PARAM_ID_DEC_MEDIA_FMT 0x00013232 + +/* ID of the parameter used by #AFE_MODULE_AUDIO_DEV_INTERFACE to configure + * the island mode for a given AFE port. + */ +#define AFE_PARAM_ID_ISLAND_CONFIG 0x000102B4 + +/* Version information used to handle future additions to codec DMA + * configuration (for backward compatibility). + */ +#define AFE_API_VERSION_ISLAND_CONFIG 0x1 + +/* Payload of the AFE_PARAM_ID_ISLAND_CONFIG parameter used by + * AFE_MODULE_AUDIO_DEV_INTERFACE. + */ +struct afe_param_id_island_cfg_t { + uint32_t island_cfg_minor_version; + /* Tracks the configuration of this parameter. + * Supported values: #AFE_API_VERSION_ISLAND_CONFIG + */ + + uint32_t island_enable; + /* Specifies whether island mode should be enabled or disabled for the + * use-case being setup. + * Supported values: 0 - Disable, 1 - Enable + */ +} __packed; + +/* ID of the parameter used by #AFE_MODULE_AUDIO_DEV_INTERFACE to configure + * the Codec DMA interface. + */ + +#define AFE_PARAM_ID_CODEC_DMA_CONFIG 0x000102B8 + +/* Version information used to handle future additions to codec DMA + * configuration (for backward compatibility). + */ +#define AFE_API_VERSION_CODEC_DMA_CONFIG 0x1 + +/* Payload of the AFE_PARAM_ID_CODEC_DMA_CONFIG parameter used by + * AFE_MODULE_AUDIO_DEV_INTERFACE. + */ +struct afe_param_id_cdc_dma_cfg_t { + uint32_t cdc_dma_cfg_minor_version; + /* Tracks the configuration of this parameter. + * Supported values: #AFE_API_VERSION_CODEC_DMA_CONFIG + */ + + uint32_t sample_rate; + /* Sampling rate of the port. + * Supported values: + * - #AFE_PORT_SAMPLE_RATE_8K + * - #AFE_PORT_SAMPLE_RATE_11_025K + * - #AFE_PORT_SAMPLE_RATE_12K + * - #AFE_PORT_SAMPLE_RATE_16K + * - #AFE_PORT_SAMPLE_RATE_22_05K + * - #AFE_PORT_SAMPLE_RATE_24K + * - #AFE_PORT_SAMPLE_RATE_32K + * - #AFE_PORT_SAMPLE_RATE_44_1K + * - #AFE_PORT_SAMPLE_RATE_48K + * - #AFE_PORT_SAMPLE_RATE_88_2K + * - #AFE_PORT_SAMPLE_RATE_96K + * - #AFE_PORT_SAMPLE_RATE_176_4K + * - #AFE_PORT_SAMPLE_RATE_192K + * - #AFE_PORT_SAMPLE_RATE_352_8K + * - #AFE_PORT_SAMPLE_RATE_384K + */ + + uint16_t bit_width; + /* Bit width of the sample. + * Supported values: 16, 24, 32 + */ + + uint16_t data_format; + /* Data format supported by the codec DMA interface. + * Supported values: + * - #AFE_LINEAR_PCM_DATA + * - #AFE_LINEAR_PCM_DATA_PACKED_16BIT + */ + + uint16_t num_channels; + /* Number of channels. + * Supported values: 1 to Maximum number of channels supported + * for each interface + */ + + uint16_t active_channels_mask; + /* Active channels mask to denote the bit mask for active channels. + * Bits 0 to 7 denote channels 0 to 7. A 1 denotes the channel is active + * while a 0 denotes a channel is inactive. + * Supported values: + * Any mask with number of active bits equal to num_channels + */ +} __packed; + +union afe_port_config { + struct afe_param_id_pcm_cfg pcm; + struct afe_param_id_i2s_cfg i2s; + struct afe_param_id_hdmi_multi_chan_audio_cfg hdmi_multi_ch; + struct afe_param_id_slimbus_cfg slim_sch; + struct afe_param_id_rt_proxy_port_cfg rtproxy; + struct afe_param_id_internal_bt_fm_cfg int_bt_fm; + struct afe_param_id_pseudo_port_cfg pseudo_port; + struct afe_param_id_device_hw_delay_cfg hw_delay; + struct afe_param_id_spdif_cfg_v2 spdif; + struct afe_param_id_set_topology_cfg topology; + struct afe_param_id_tdm_cfg tdm; + struct afe_param_id_usb_audio_cfg usb_audio; + struct afe_param_id_aptx_sync_mode sync_mode_param; + struct asm_aac_frame_size_control_t frame_ctl_param; + struct afe_enc_fmt_id_param_t enc_fmt; + struct afe_port_media_type_t media_type; + struct afe_enc_cfg_blk_param_t enc_blk_param; + struct avs_enc_packetizer_id_param_t enc_pkt_id_param; + struct avs_enc_set_scrambler_param_t enc_set_scrambler_param; + struct avs_dec_depacketizer_id_param_t dec_depkt_id_param; + struct afe_dec_media_fmt_t dec_media_fmt; + struct afe_enc_level_to_bitrate_map_param_t map_param; + struct afe_enc_dec_imc_info_param_t imc_info_param; + struct afe_param_id_cdc_dma_cfg_t cdc_dma; +} __packed; + + +/* + * AFE event registration related APIs and corresponding payloads + */ +#define AFE_SVC_CMD_EVENT_CFG 0x000100FE + +#define AFE_CMD_APPS_WAKEUP_IRQ_REGISTER_MINOR_VERSION 0x1 + +/* Flag to indicate AFE to register APPS wakeup Interrupt */ +#define AFE_APPS_WAKEUP_IRQ_REGISTER_FLAG 1 + +/* Flag to indicate AFE to de-register APPS wakeup Interrupt */ +#define AFE_APPS_WAKEUP_IRQ_DEREGISTER_FLAG 0 + +/* Default interrupt trigger value. */ +#define DEFAULT_SETTINGS 0x00000001 + +/* Interrupt is triggered only if the input signal at the source is high. */ +#define LEVEL_HIGH_TRIGGER 0x00000002 + +/* Interrupt is triggered only if the input signal at the source is low. */ +#define LEVEL_LOW_TRIGGER 0x00000003 + +/* Interrupt is triggered only if the input signal at the source transitions + *from low to high. + */ +#define RISING_EDGE_TRIGGER 0x00000004 + +/* Interrupt is triggered only if the input signal at the source transitions + *from high to low. + */ +#define FALLING_EDGE_TRIGGER 0x00000005 + +/* Macro for invalid trigger type. This should not be used. */ +#define INVALID_TRIGGER 0x00000006 + +#define AFE_EVENT_ID_MBHC_DETECTION_SW_WA 0x1 + +/* @weakgroup weak_afe_svc_cmd_evt_cfg_payload + * + * This is payload of each event that is to be + * registered with AFE service. + */ +struct afe_svc_cmd_evt_cfg_payload { + struct apr_hdr hdr; + + uint32_t event_id; +/* Unique ID of the event. + * + * @values + * -# AFE_EVENT_ID_MBHC_DETECTION_SW_WA + */ + + uint32_t reg_flag; +/* Flag for registering or de-registering an event. + * @values + * - #AFE_SVC_REGISTER_EVENT_FLAG + * - #AFE_SVC_DEREGISTER_EVENT_FLAG + */ +} __packed; + +#define AFE_EVENT_MBHC_DETECTION_SW_WA 0x0001010F + +#define AFE_PORT_CMD_DEVICE_START 0x000100E5 + +/* Payload of the #AFE_PORT_CMD_DEVICE_START.*/ +struct afe_port_cmd_device_start { + struct apr_hdr hdr; + u16 port_id; +/* Port interface and direction (Rx or Tx) to start. An even + * number represents the Rx direction, and an odd number represents + * the Tx direction. + */ + + + u16 reserved; +/* Reserved for 32-bit alignment. This field must be set to 0.*/ + +} __packed; + +#define AFE_PORT_CMD_DEVICE_STOP 0x000100E6 + +/* Payload of the #AFE_PORT_CMD_DEVICE_STOP. */ +struct afe_port_cmd_device_stop { + struct apr_hdr hdr; + u16 port_id; +/* Port interface and direction (Rx or Tx) to start. An even + * number represents the Rx direction, and an odd number represents + * the Tx direction. + */ + + u16 reserved; +/* Reserved for 32-bit alignment. This field must be set to 0.*/ +} __packed; + +#define AFE_SERVICE_CMD_SHARED_MEM_MAP_REGIONS 0x000100EA + +/* Memory map regions command payload used by the + * #AFE_SERVICE_CMD_SHARED_MEM_MAP_REGIONS . + * This structure allows clients to map multiple shared memory + * regions in a single command. Following this structure are + * num_regions of afe_service_shared_map_region_payload. + */ +struct afe_service_cmd_shared_mem_map_regions { + struct apr_hdr hdr; +u16 mem_pool_id; +/* Type of memory on which this memory region is mapped. + * Supported values: + * - #ADSP_MEMORY_MAP_EBI_POOL + * - #ADSP_MEMORY_MAP_SMI_POOL + * - #ADSP_MEMORY_MAP_SHMEM8_4K_POOL + * - Other values are reserved + * + * The memory pool ID implicitly defines the characteristics of the + * memory. Characteristics may include alignment type, permissions, + * etc. + * + * ADSP_MEMORY_MAP_EBI_POOL is External Buffer Interface type memory + * ADSP_MEMORY_MAP_SMI_POOL is Shared Memory Interface type memory + * ADSP_MEMORY_MAP_SHMEM8_4K_POOL is shared memory, byte + * addressable, and 4 KB aligned. + */ + + + u16 num_regions; +/* Number of regions to map. + * Supported values: + * - Any value greater than zero + */ + + u32 property_flag; +/* Configures one common property for all the regions in the + * payload. + * + * Supported values: - 0x00000000 to 0x00000001 + * + * b0 - bit 0 indicates physical or virtual mapping 0 Shared memory + * address provided in afe_service_shared_map_region_payloadis a + * physical address. The shared memory needs to be mapped( hardware + * TLB entry) and a software entry needs to be added for internal + * book keeping. + * + * 1 Shared memory address provided in + * afe_service_shared_map_region_payloadis a virtual address. The + * shared memory must not be mapped (since hardware TLB entry is + * already available) but a software entry needs to be added for + * internal book keeping. This can be useful if two services with in + * ADSP is communicating via APR. They can now directly communicate + * via the Virtual address instead of Physical address. The virtual + * regions must be contiguous. num_regions must be 1 in this case. + * + * b31-b1 - reserved bits. must be set to zero + */ + + +} __packed; +/* Map region payload used by the + * afe_service_shared_map_region_payloadstructure. + */ +struct afe_service_shared_map_region_payload { + u32 shm_addr_lsw; +/* least significant word of starting address in the memory + * region to map. It must be contiguous memory, and it must be 4 KB + * aligned. + * Supported values: - Any 32 bit value + */ + + + u32 shm_addr_msw; +/* most significant word of startng address in the memory region + * to map. For 32 bit shared memory address, this field must be set + * to zero. For 36 bit shared memory address, bit31 to bit 4 must be + * set to zero + * + * Supported values: - For 32 bit shared memory address, this field + * must be set to zero. - For 36 bit shared memory address, bit31 to + * bit 4 must be set to zero - For 64 bit shared memory address, any + * 32 bit value + */ + + + u32 mem_size_bytes; +/* Number of bytes in the region. The aDSP will always map the + * regions as virtual contiguous memory, but the memory size must be + * in multiples of 4 KB to avoid gaps in the virtually contiguous + * mapped memory. + * + * Supported values: - multiples of 4KB + */ + +} __packed; + +#define AFE_SERVICE_CMDRSP_SHARED_MEM_MAP_REGIONS 0x000100EB +struct afe_service_cmdrsp_shared_mem_map_regions { + u32 mem_map_handle; +/* A memory map handle encapsulating shared memory attributes is + * returned iff AFE_SERVICE_CMD_SHARED_MEM_MAP_REGIONS command is + * successful. In the case of failure , a generic APR error response + * is returned to the client. + * + * Supported Values: - Any 32 bit value + */ + +} __packed; +#define AFE_SERVICE_CMD_SHARED_MEM_UNMAP_REGIONS 0x000100EC +/* Memory unmap regions command payload used by the + * #AFE_SERVICE_CMD_SHARED_MEM_UNMAP_REGIONS + * + * This structure allows clients to unmap multiple shared memory + * regions in a single command. + */ + + +struct afe_service_cmd_shared_mem_unmap_regions { + struct apr_hdr hdr; +u32 mem_map_handle; +/* memory map handle returned by + * AFE_SERVICE_CMD_SHARED_MEM_MAP_REGIONS commands + * + * Supported Values: + * - Any 32 bit value + */ +} __packed; + +/* Used by RTAC */ +struct afe_rtac_get_param_v2 { + u16 port_id; +/* Port interface and direction (Rx or Tx) to start. */ + + u16 payload_size; +/* Maximum data size of the parameter ID/module ID combination. + * This is a multiple of four bytes + * Supported values: > 0 + */ + + u32 payload_address_lsw; +/* LSW of 64 bit Payload address. Address should be 32-byte, + * 4kbyte aligned and must be contig memory. + */ + + + u32 payload_address_msw; +/* 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 mem_map_handle; +/* 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 afe_port_param_data_v2 + * structure + */ + + + u32 module_id; +/* ID of the module to be queried. + * Supported values: Valid module ID + */ + + u32 param_id; +/* ID of the parameter to be queried. + * Supported values: Valid parameter ID + */ +} __packed; + +#define AFE_PORT_CMD_GET_PARAM_V2 0x000100F0 + +/* Payload of the #AFE_PORT_CMD_GET_PARAM_V2 command, + * which queries for one post/preprocessing parameter of a + * stream. + */ +struct afe_port_cmd_get_param_v2 { + struct apr_hdr apr_hdr; + + /* 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; + + /* The memory mapping header to be used when requesting outband */ + struct mem_mapping_hdr mem_hdr; + + /* The module ID of the parameter data requested */ + u32 module_id; + + /* The parameter ID of the parameter data requested */ + u32 param_id; + + /* The header information for the parameter data */ + struct param_hdr_v1 param_hdr; +} __packed; + +#define AFE_PORT_CMDRSP_GET_PARAM_V2 0x00010106 + +/* Payload of the #AFE_PORT_CMDRSP_GET_PARAM_V2 message, which + * responds to an #AFE_PORT_CMD_GET_PARAM_V2 command. + * + * Immediately following this structure is the parameters structure + * (afe_port_param_data) containing the response(acknowledgment) + * parameter payload. This payload is included for an in-band + * scenario. For an address/shared memory-based set parameter, this + * payload is not needed. + */ + + +struct afe_port_cmdrsp_get_param_v2 { + u32 status; + struct param_hdr_v1 param_hdr; + u8 param_data[0]; +} __packed; + +#define AFE_PORT_CMD_GET_PARAM_V3 0x000100FB +struct afe_port_cmd_get_param_v3 { + /* APR Header */ + struct apr_hdr apr_hdr; + + /* Port ID of the AFE port to configure. Port interface and direction + * (Rx or Tx) to configure. An even number represents the Rx direction, + * and an odd number represents the Tx direction. + */ + u16 port_id; + + /* Reserved. This field must be set to zero. */ + u16 reserved; + + /* The memory mapping header to be used when requesting outband */ + struct mem_mapping_hdr mem_hdr; + + /* The header information for the parameter data */ + struct param_hdr_v3 param_hdr; +} __packed; + +#define AFE_PORT_CMDRSP_GET_PARAM_V3 0x00010108 +struct afe_port_cmdrsp_get_param_v3 { + /* The status of the command */ + uint32_t status; + + /* The header information for the parameter data */ + struct param_hdr_v3 param_hdr; + + /* The parameter data to be filled when sent inband */ + u8 param_data[0]; +} __packed; + +#define AFE_PARAM_ID_LPASS_CORE_SHARED_CLOCK_CONFIG 0x0001028C +#define AFE_API_VERSION_LPASS_CORE_SHARED_CLK_CONFIG 0x1 + +/* Payload of the AFE_PARAM_ID_LPASS_CORE_SHARED_CLOCK_CONFIG parameter used by + * AFE_MODULE_AUDIO_DEV_INTERFACE. + */ +struct afe_param_id_lpass_core_shared_clk_cfg { + u32 lpass_core_shared_clk_cfg_minor_version; +/* + * Minor version used for lpass core shared clock configuration + * Supported value: AFE_API_VERSION_LPASS_CORE_SHARED_CLK_CONFIG + */ + u32 enable; +/* + * Specifies whether the lpass core shared clock is + * enabled (1) or disabled (0). + */ +} __packed; + +/* adsp_afe_service_commands.h */ + +#define ADSP_MEMORY_MAP_EBI_POOL 0 + +#define ADSP_MEMORY_MAP_SMI_POOL 1 +#define ADSP_MEMORY_MAP_IMEM_POOL 2 +#define ADSP_MEMORY_MAP_SHMEM8_4K_POOL 3 +#define ADSP_MEMORY_MAP_MDF_SHMEM_4K_POOL 4 + +/* Definition of virtual memory flag */ +#define ADSP_MEMORY_MAP_VIRTUAL_MEMORY 1 + +/* Definition of physical memory flag */ +#define ADSP_MEMORY_MAP_PHYSICAL_MEMORY 0 + +#define NULL_POPP_TOPOLOGY 0x00010C68 +#define NULL_COPP_TOPOLOGY 0x00010312 +#define DEFAULT_COPP_TOPOLOGY 0x00010314 +#define DEFAULT_POPP_TOPOLOGY 0x00010BE4 +#define COMPRESSED_PASSTHROUGH_DEFAULT_TOPOLOGY 0x0001076B +#define COMPRESSED_PASSTHROUGH_NONE_TOPOLOGY 0x00010774 +#define VPM_TX_SM_ECNS_V2_COPP_TOPOLOGY 0x00010F89 +#define VPM_TX_VOICE_SMECNS_V2_COPP_TOPOLOGY 0x10000003 +#define VPM_TX_DM_FLUENCE_COPP_TOPOLOGY 0x00010F72 +#define VPM_TX_QMIC_FLUENCE_COPP_TOPOLOGY 0x00010F75 +#define VPM_TX_DM_RFECNS_COPP_TOPOLOGY 0x00010F86 +#define ADM_CMD_COPP_OPEN_TOPOLOGY_ID_DTS_HPX 0x10015002 +#define ADM_CMD_COPP_OPEN_TOPOLOGY_ID_AUDIOSPHERE 0x10028000 +#define VPM_TX_DM_FLUENCE_EF_COPP_TOPOLOGY 0x10000005 + +/* Memory map regions command payload used by the + * #ASM_CMD_SHARED_MEM_MAP_REGIONS ,#ADM_CMD_SHARED_MEM_MAP_REGIONS + * commands. + * + * This structure allows clients to map multiple shared memory + * regions in a single command. Following this structure are + * num_regions of avs_shared_map_region_payload. + */ + + +struct avs_cmd_shared_mem_map_regions { + struct apr_hdr hdr; + u16 mem_pool_id; +/* Type of memory on which this memory region is mapped. + * + * Supported values: - #ADSP_MEMORY_MAP_EBI_POOL - + * #ADSP_MEMORY_MAP_SMI_POOL - #ADSP_MEMORY_MAP_IMEM_POOL + * (unsupported) - #ADSP_MEMORY_MAP_SHMEM8_4K_POOL - Other values + * are reserved + * + * The memory ID implicitly defines the characteristics of the + * memory. Characteristics may include alignment type, permissions, + * etc. + * + * SHMEM8_4K is shared memory, byte addressable, and 4 KB aligned. + */ + + + u16 num_regions; + /* Number of regions to map.*/ + + u32 property_flag; +/* Configures one common property for all the regions in the + * payload. No two regions in the same memory map regions cmd can + * have differnt property. Supported values: - 0x00000000 to + * 0x00000001 + * + * b0 - bit 0 indicates physical or virtual mapping 0 shared memory + * address provided in avs_shared_map_regions_payload is physical + * address. The shared memory needs to be mapped( hardware TLB + * entry) + * + * and a software entry needs to be added for internal book keeping. + * + * 1 Shared memory address provided in MayPayload[usRegions] is + * virtual address. The shared memory must not be mapped (since + * hardware TLB entry is already available) but a software entry + * needs to be added for internal book keeping. This can be useful + * if two services with in ADSP is communicating via APR. They can + * now directly communicate via the Virtual address instead of + * Physical address. The virtual regions must be contiguous. + * + * b31-b1 - reserved bits. must be set to zero + */ + +} __packed; + +struct avs_shared_map_region_payload { + u32 shm_addr_lsw; +/* least significant word of shared memory address of the memory + * region to map. It must be contiguous memory, and it must be 4 KB + * aligned. + */ + + u32 shm_addr_msw; +/* most significant word of shared memory address of the memory + * region to map. For 32 bit shared memory address, this field must + * tbe set to zero. For 36 bit shared memory address, bit31 to bit 4 + * must be set to zero + */ + + u32 mem_size_bytes; +/* Number of bytes in the region. + * + * The aDSP will always map the regions as virtual contiguous + * memory, but the memory size must be in multiples of 4 KB to avoid + * gaps in the virtually contiguous mapped memory. + */ + +} __packed; + +struct avs_cmd_shared_mem_unmap_regions { + struct apr_hdr hdr; + u32 mem_map_handle; +/* memory map handle returned by ASM_CMD_SHARED_MEM_MAP_REGIONS + * , ADM_CMD_SHARED_MEM_MAP_REGIONS, commands + */ + +} __packed; + +/* Memory map command response payload used by the + * #ASM_CMDRSP_SHARED_MEM_MAP_REGIONS + * ,#ADM_CMDRSP_SHARED_MEM_MAP_REGIONS + */ + + +struct avs_cmdrsp_shared_mem_map_regions { + u32 mem_map_handle; +/* A memory map handle encapsulating shared memory attributes is + * returned + */ + +} __packed; + +#define AVS_MDF_MDSP_PROC_ID 0x2 +#define AVS_MDF_SSC_PROC_ID 0x3 +#define AVS_MDF_CDSP_PROC_ID 0x4 + +/* Shared memory map command payload used by the + * #AVCS_CMD_MAP_MDF_SHARED_MEMORY. + * + * This structure allows clients to map multiple shared memory + * regions with remote processor ID. All mapped regions must be + * from the same memory pool. Following this structure are + * num_regions of avs_shared_map_region_payload. + */ +struct avs_cmd_map_mdf_shared_memory { + struct apr_hdr hdr; + uint32_t mem_map_handle; +/* Unique identifier for the shared memory address. + * + * The aDSP returns this handle for + * #AVCS_CMD_SHARED_MEM_MAP_REGIONS + * + * Supported values: + * Any 32-bit value + * + * The aDSP uses this handle to retrieve the shared memory + * attributes. This handle can be an abstract representation + * of the shared memory regions that are being mapped. + */ + + uint32_t proc_id; +/* Supported values: + * #AVS_MDF_MDSP_PROC_ID + * #AVS_MDF_SSC_PROC_ID + * #AVS_MDF_CDSP_PROC_ID + */ + + uint32_t num_regions; +/* Number of regions to be mapped with the remote DSP processor + * mentioned by proc_id field. + * + * Array of structures of avs_shared_map_region_payload will follow. + * The address fields in those arrays should correspond to the remote + * processor mentioned by proc_id. + * In case of DSPs with SMMU enabled, the address should be IOVA. + * And for DSPs without SMMU, the address should be physical address. + */ +} __packed; + +/*adsp_audio_memmap_api.h*/ + +/* ASM related data structures */ +struct asm_wma_cfg { + u16 format_tag; + u16 ch_cfg; + u32 sample_rate; + u32 avg_bytes_per_sec; + u16 block_align; + u16 valid_bits_per_sample; + u32 ch_mask; + u16 encode_opt; + u16 adv_encode_opt; + u32 adv_encode_opt2; + u32 drc_peak_ref; + u32 drc_peak_target; + u32 drc_ave_ref; + u32 drc_ave_target; +} __packed; + +struct asm_wmapro_cfg { + u16 format_tag; + u16 ch_cfg; + u32 sample_rate; + u32 avg_bytes_per_sec; + u16 block_align; + u16 valid_bits_per_sample; + u32 ch_mask; + u16 encode_opt; + u16 adv_encode_opt; + u32 adv_encode_opt2; + u32 drc_peak_ref; + u32 drc_peak_target; + u32 drc_ave_ref; + u32 drc_ave_target; +} __packed; + +struct asm_aac_cfg { + u16 format; + u16 aot; + u16 ep_config; + u16 section_data_resilience; + u16 scalefactor_data_resilience; + u16 spectral_data_resilience; + u16 ch_cfg; + u16 reserved; + u32 sample_rate; +} __packed; + +struct asm_amrwbplus_cfg { + u32 size_bytes; + u32 version; + u32 num_channels; + u32 amr_band_mode; + u32 amr_dtx_mode; + u32 amr_frame_fmt; + u32 amr_lsf_idx; +} __packed; + +struct asm_flac_cfg { + u32 sample_rate; + u32 ext_sample_rate; + u32 min_frame_size; + u32 max_frame_size; + u16 stream_info_present; + u16 min_blk_size; + u16 max_blk_size; + u16 ch_cfg; + u16 sample_size; + u16 md5_sum; +}; + +struct asm_alac_cfg { + u32 frame_length; + u8 compatible_version; + u8 bit_depth; + u8 pb; + u8 mb; + u8 kb; + u8 num_channels; + u16 max_run; + u32 max_frame_bytes; + u32 avg_bit_rate; + u32 sample_rate; + u32 channel_layout_tag; +}; + +struct asm_g711_dec_cfg { + u32 sample_rate; +}; + +struct asm_vorbis_cfg { + u32 bit_stream_fmt; +}; + +struct asm_ape_cfg { + u16 compatible_version; + u16 compression_level; + u32 format_flags; + u32 blocks_per_frame; + u32 final_frame_blocks; + u32 total_frames; + u16 bits_per_sample; + u16 num_channels; + u32 sample_rate; + u32 seek_table_present; +}; + +struct asm_dsd_cfg { + u16 num_version; + u16 is_bitwise_big_endian; + u16 dsd_channel_block_size; + u16 num_channels; + u8 channel_mapping[8]; + u32 dsd_data_rate; +}; + +struct asm_softpause_params { + u32 enable; + u32 period; + u32 step; + u32 rampingcurve; +} __packed; + +struct asm_softvolume_params { + u32 period; + u32 step; + u32 rampingcurve; +} __packed; + +#define ASM_END_POINT_DEVICE_MATRIX 0 + +#define PCM_CHANNEL_NULL 0 + +/* Front left channel. */ +#define PCM_CHANNEL_FL 1 + +/* Front right channel. */ +#define PCM_CHANNEL_FR 2 + +/* Front center channel. */ +#define PCM_CHANNEL_FC 3 + +/* Left surround channel.*/ +#define PCM_CHANNEL_LS 4 + +/* Right surround channel.*/ +#define PCM_CHANNEL_RS 5 + +/* Low frequency effect channel. */ +#define PCM_CHANNEL_LFE 6 + +/* Center surround channel; Rear center channel. */ +#define PCM_CHANNEL_CS 7 + +/* Left back channel; Rear left channel. */ +#define PCM_CHANNEL_LB 8 + +/* Right back channel; Rear right channel. */ +#define PCM_CHANNEL_RB 9 + +/* Top surround channel. */ +#define PCM_CHANNELS 10 + +/* Center vertical height channel.*/ +#define PCM_CHANNEL_CVH 11 + +/* Mono surround channel.*/ +#define PCM_CHANNEL_MS 12 + +/* Front left of center. */ +#define PCM_CHANNEL_FLC 13 + +/* Front right of center. */ +#define PCM_CHANNEL_FRC 14 + +/* Rear left of center. */ +#define PCM_CHANNEL_RLC 15 + +/* Rear right of center. */ +#define PCM_CHANNEL_RRC 16 + +/* Second low frequency channel. */ +#define PCM_CHANNEL_LFE2 17 + +/* Side left channel. */ +#define PCM_CHANNEL_SL 18 + +/* Side right channel. */ +#define PCM_CHANNEL_SR 19 + +/* Top front left channel. */ +#define PCM_CHANNEL_TFL 20 + +/* Left vertical height channel. */ +#define PCM_CHANNEL_LVH 20 + +/* Top front right channel. */ +#define PCM_CHANNEL_TFR 21 + +/* Right vertical height channel. */ +#define PCM_CHANNEL_RVH 21 + +/* Top center channel. */ +#define PCM_CHANNEL_TC 22 + +/* Top back left channel. */ +#define PCM_CHANNEL_TBL 23 + +/* Top back right channel. */ +#define PCM_CHANNEL_TBR 24 + +/* Top side left channel. */ +#define PCM_CHANNEL_TSL 25 + +/* Top side right channel. */ +#define PCM_CHANNEL_TSR 26 + +/* Top back center channel. */ +#define PCM_CHANNEL_TBC 27 + +/* Bottom front center channel. */ +#define PCM_CHANNEL_BFC 28 + +/* Bottom front left channel. */ +#define PCM_CHANNEL_BFL 29 + +/* Bottom front right channel. */ +#define PCM_CHANNEL_BFR 30 + +/* Left wide channel. */ +#define PCM_CHANNEL_LW 31 + +/* Right wide channel. */ +#define PCM_CHANNEL_RW 32 + +/* Left side direct channel. */ +#define PCM_CHANNEL_LSD 33 + +/* Right side direct channel. */ +#define PCM_CHANNEL_RSD 34 + +#define PCM_FORMAT_MAX_NUM_CHANNEL 8 +#define PCM_FORMAT_MAX_CHANNELS_9 9 + +/* Used for ADM_CMD_DEVICE_OPEN_V8 */ +#define PCM_FORMAT_MAX_NUM_CHANNEL_V8 32 + +#define ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V2 0x00010DA5 + +#define ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V3 0x00010DDC + +#define ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V4 0x0001320C + +#define ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V5 0x00013222 + +#define ASM_MEDIA_FMT_EVRCB_FS 0x00010BEF + +#define ASM_MEDIA_FMT_EVRCWB_FS 0x00010BF0 + +#define ASM_MEDIA_FMT_GENERIC_COMPRESSED 0x00013212 + +#define ASM_MAX_EQ_BANDS 12 + +#define ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2 0x00010D98 + +struct asm_data_cmd_media_fmt_update_v2 { +u32 fmt_blk_size; + /* Media format block size in bytes.*/ +} __packed; + +struct asm_generic_compressed_fmt_blk_t { + struct apr_hdr hdr; + struct asm_data_cmd_media_fmt_update_v2 fmt_blk; + + /* + * Channel mapping array of bitstream output. + * Channel[i] mapping describes channel i inside the buffer, where + * i < num_channels. All valid used channels must be + * present at the beginning of the array. + */ + uint8_t channel_mapping[8]; + + /* + * Number of channels of the incoming bitstream. + * Supported values: 1,2,3,4,5,6,7,8 + */ + uint16_t num_channels; + + /* + * Nominal bits per sample value of the incoming bitstream. + * Supported values: 16, 32 + */ + uint16_t bits_per_sample; + + /* + * Nominal sampling rate of the incoming bitstream. + * Supported values: 8000, 11025, 16000, 22050, 24000, 32000, + * 44100, 48000, 88200, 96000, 176400, 192000, + * 352800, 384000 + */ + uint32_t sampling_rate; + +} __packed; + + +/* Command to send sample rate & channels for IEC61937 (compressed) or IEC60958 + * (pcm) streams. Both audio standards use the same format and are used for + * HDMI or SPDIF output. + */ +#define ASM_DATA_CMD_IEC_60958_MEDIA_FMT 0x0001321E + +struct asm_iec_compressed_fmt_blk_t { + struct apr_hdr hdr; + + /* + * Nominal sampling rate of the incoming bitstream. + * Supported values: 8000, 11025, 16000, 22050, 24000, 32000, + * 44100, 48000, 88200, 96000, 176400, 192000, + * 352800, 384000 + */ + uint32_t sampling_rate; + + /* + * Number of channels of the incoming bitstream. + * Supported values: 1,2,3,4,5,6,7,8 + */ + uint32_t num_channels; + +} __packed; + +struct asm_multi_channel_pcm_fmt_blk_v2 { + struct apr_hdr hdr; + struct asm_data_cmd_media_fmt_update_v2 fmt_blk; + + u16 num_channels; + /* Number of channels. Supported values: 1 to 8 */ + u16 bits_per_sample; +/* Number of bits per sample per channel. * Supported values: + * 16, 24 * When used for playback, the client must send 24-bit + * samples packed in 32-bit words. The 24-bit samples must be placed + * in the most significant 24 bits of the 32-bit word. When used for + * recording, the aDSP sends 24-bit samples packed in 32-bit words. + * The 24-bit samples are placed in the most significant 24 bits of + * the 32-bit word. + */ + + + u32 sample_rate; +/* Number of samples per second (in Hertz). + * Supported values: 2000 to 48000 + */ + + u16 is_signed; + /* Flag that indicates the samples are signed (1). */ + + u16 reserved; + /* reserved field for 32 bit alignment. must be set to zero. */ + + u8 channel_mapping[8]; +/* Channel array of size 8. + * Supported values: + * - #PCM_CHANNEL_L + * - #PCM_CHANNEL_R + * - #PCM_CHANNEL_C + * - #PCM_CHANNEL_LS + * - #PCM_CHANNEL_RS + * - #PCM_CHANNEL_LFE + * - #PCM_CHANNEL_CS + * - #PCM_CHANNEL_LB + * - #PCM_CHANNEL_RB + * - #PCM_CHANNELS + * - #PCM_CHANNEL_CVH + * - #PCM_CHANNEL_MS + * - #PCM_CHANNEL_FLC + * - #PCM_CHANNEL_FRC + * - #PCM_CHANNEL_RLC + * - #PCM_CHANNEL_RRC + * + * Channel[i] mapping describes channel I. Each element i of the + * array describes channel I inside the buffer where 0 @le I < + * num_channels. An unused channel is set to zero. + */ +} __packed; + +struct asm_multi_channel_pcm_fmt_blk_v3 { + uint16_t num_channels; +/* + * Number of channels + * Supported values: 1 to 8 + */ + + uint16_t bits_per_sample; +/* + * Number of bits per sample per channel + * Supported values: 16, 24 + */ + + uint32_t sample_rate; +/* + * Number of samples per second + * Supported values: 2000 to 48000, 96000,192000 Hz + */ + + uint16_t is_signed; +/* Flag that indicates that PCM samples are signed (1) */ + + uint16_t sample_word_size; +/* + * Size in bits of the word that holds a sample of a channel. + * Supported values: 12,24,32 + */ + + uint8_t channel_mapping[8]; +/* + * Each element, i, in the array describes channel i inside the buffer where + * 0 <= i < num_channels. Unused channels are set to 0. + */ +} __packed; + +struct asm_multi_channel_pcm_fmt_blk_v4 { + uint16_t num_channels; +/* + * Number of channels + * Supported values: 1 to 8 + */ + + uint16_t bits_per_sample; +/* + * Number of bits per sample per channel + * Supported values: 16, 24, 32 + */ + + uint32_t sample_rate; +/* + * Number of samples per second + * Supported values: 2000 to 48000, 96000,192000 Hz + */ + + uint16_t is_signed; +/* Flag that indicates that PCM samples are signed (1) */ + + uint16_t sample_word_size; +/* + * Size in bits of the word that holds a sample of a channel. + * Supported values: 12,24,32 + */ + + uint8_t channel_mapping[8]; +/* + * Each element, i, in the array describes channel i inside the buffer where + * 0 <= i < num_channels. Unused channels are set to 0. + */ + uint16_t endianness; +/* + * Flag to indicate the endianness of the pcm sample + * Supported values: 0 - Little endian (all other formats) + * 1 - Big endian (AIFF) + */ + uint16_t mode; +/* + * Mode to provide additional info about the pcm input data. + * Supported values: 0 - Default QFs (Q15 for 16b, Q23 for packed 24b, + * Q31 for unpacked 24b or 32b) + * 15 - for 16 bit + * 23 - for 24b packed or 8.24 format + * 31 - for 24b unpacked or 32bit + */ +} __packed; + + +struct asm_multi_channel_pcm_fmt_blk_v5 { + uint16_t num_channels; +/* + * Number of channels + * Supported values: 1 to 32 + */ + + uint16_t bits_per_sample; +/* + * Number of bits per sample per channel + * Supported values: 16, 24, 32 + */ + + uint32_t sample_rate; +/* + * Number of samples per second + * Supported values: 2000 to 48000, 96000,192000 Hz + */ + + uint16_t is_signed; +/* Flag that indicates that PCM samples are signed (1) */ + + uint16_t sample_word_size; +/* + * Size in bits of the word that holds a sample of a channel. + * Supported values: 12,24,32 + */ + uint16_t endianness; +/* + * Flag to indicate the endianness of the pcm sample + * Supported values: 0 - Little endian (all other formats) + * 1 - Big endian (AIFF) + */ + uint16_t mode; +/* + * Mode to provide additional info about the pcm input data. + * Supported values: 0 - Default QFs (Q15 for 16b, Q23 for packed 24b, + * Q31 for unpacked 24b or 32b) + * 15 - for 16 bit + * 23 - for 24b packed or 8.24 format + * 31 - for 24b unpacked or 32bit + */ + + uint8_t channel_mapping[32]; +/* + * Each element, i, in the array describes channel i inside the buffer where + * 0 <= i < num_channels. Unused channels are set to 0. + */ +} __packed; +/* + * Payload of the multichannel PCM configuration parameters in + * the ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V3 media format. + */ +struct asm_multi_channel_pcm_fmt_blk_param_v3 { + struct apr_hdr hdr; + struct asm_data_cmd_media_fmt_update_v2 fmt_blk; + struct asm_multi_channel_pcm_fmt_blk_v3 param; +} __packed; + +/* + * Payload of the multichannel PCM configuration parameters in + * the ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V4 media format. + */ +struct asm_multi_channel_pcm_fmt_blk_param_v4 { + struct apr_hdr hdr; + struct asm_data_cmd_media_fmt_update_v2 fmt_blk; + struct asm_multi_channel_pcm_fmt_blk_v4 param; +} __packed; + +/* + * Payload of the multichannel PCM configuration parameters in + * the ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V5 media format. + */ +struct asm_multi_channel_pcm_fmt_blk_param_v5 { + struct apr_hdr hdr; + struct asm_data_cmd_media_fmt_update_v2 fmt_blk; + struct asm_multi_channel_pcm_fmt_blk_v5 param; +} __packed; + +struct asm_stream_cmd_set_encdec_param { + u32 param_id; + /* ID of the parameter. */ + + u32 param_size; +/* Data size of this parameter, in bytes. The size is a multiple + * of 4 bytes. + */ + +} __packed; + +struct asm_enc_cfg_blk_param_v2 { + u32 frames_per_buf; +/* Number of encoded frames to pack into each buffer. + * + * @note1hang This is only guidance information for the aDSP. The + * number of encoded frames put into each buffer (specified by the + * client) is less than or equal to this number. + */ + + u32 enc_cfg_blk_size; +/* Size in bytes of the encoder configuration block that follows + * this member. + */ + +} __packed; + +struct asm_custom_enc_cfg_t_v2 { + struct apr_hdr hdr; + struct asm_stream_cmd_set_encdec_param encdec; + struct asm_enc_cfg_blk_param_v2 encblk; + uint32_t sample_rate; + + uint16_t num_channels; + uint16_t reserved; + /* num_ch == 1, then PCM_CHANNEL_C, + * num_ch == 2, then {PCM_CHANNEL_L, PCM_CHANNEL_R} + */ + uint8_t channel_mapping[8]; + uint32_t custom_size; + uint8_t custom_data[15]; +} __packed; + +/* @brief Dolby Digital Plus end point configuration structure + */ +struct asm_dec_ddp_endp_param_v2 { + struct apr_hdr hdr; + struct asm_stream_cmd_set_encdec_param encdec; + int endp_param_value; +} __packed; + +/* + * Payload of the multichannel PCM encoder configuration parameters in + * the ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V5 media format. + */ +struct asm_multi_channel_pcm_enc_cfg_v5 { + struct apr_hdr hdr; + struct asm_stream_cmd_set_encdec_param encdec; + struct asm_enc_cfg_blk_param_v2 encblk; + uint16_t num_channels; +/* + * Number of PCM channels. + * @values + * - 0 -- Native mode + * - 1 -- 8 channels + * Native mode indicates that encoding must be performed with the number + * of channels at the input. + */ + uint16_t bits_per_sample; +/* + * Number of bits per sample per channel. + * @values 16, 24 + */ + uint32_t sample_rate; +/* + * Number of samples per second. + * @values 0, 8000 to 48000 Hz + * A value of 0 indicates the native sampling rate. Encoding is + * performed at the input sampling rate. + */ + uint16_t is_signed; +/* + * Flag that indicates the PCM samples are signed (1). Currently, only + * signed PCM samples are supported. + */ + uint16_t sample_word_size; +/* + * The size in bits of the word that holds a sample of a channel. + * @values 16, 24, 32 + * 16-bit samples are always placed in 16-bit words: + * sample_word_size = 1. + * 24-bit samples can be placed in 32-bit words or in consecutive + * 24-bit words. + * - If sample_word_size = 32, 24-bit samples are placed in the + * most significant 24 bits of a 32-bit word. + * - If sample_word_size = 24, 24-bit samples are placed in + * 24-bit words. @tablebulletend + */ + uint16_t endianness; +/* + * Flag to indicate the endianness of the pcm sample + * Supported values: 0 - Little endian (all other formats) + * 1 - Big endian (AIFF) + */ + uint16_t mode; +/* + * Mode to provide additional info about the pcm input data. + * Supported values: 0 - Default QFs (Q15 for 16b, Q23 for packed 24b, + * Q31 for unpacked 24b or 32b) + * 15 - for 16 bit + * 23 - for 24b packed or 8.24 format + */ + uint8_t channel_mapping[PCM_FORMAT_MAX_NUM_CHANNEL_V8]; +/* + * Channel mapping array expected at the encoder output. + * Channel[i] mapping describes channel i inside the buffer, where + * 0 @le i < num_channels. All valid used channels must be present at + * the beginning of the array. + * If Native mode is set for the channels, this field is ignored. + * @values See Section @xref{dox:PcmChannelDefs} + */ +} __packed; + +/* + * Payload of the multichannel PCM encoder configuration parameters in + * the ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V4 media format. + */ + +struct asm_multi_channel_pcm_enc_cfg_v4 { + struct apr_hdr hdr; + struct asm_stream_cmd_set_encdec_param encdec; + struct asm_enc_cfg_blk_param_v2 encblk; + uint16_t num_channels; + /* + * Number of PCM channels. + * @values + * - 0 -- Native mode + * - 1 -- 8 channels + * Native mode indicates that encoding must be performed with the number + * of channels at the input. + */ + uint16_t bits_per_sample; + /* + * Number of bits per sample per channel. + * @values 16, 24 + */ + uint32_t sample_rate; + /* + * Number of samples per second. + * @values 0, 8000 to 48000 Hz + * A value of 0 indicates the native sampling rate. Encoding is + * performed at the input sampling rate. + */ + uint16_t is_signed; + /* + * Flag that indicates the PCM samples are signed (1). Currently, only + * signed PCM samples are supported. + */ + uint16_t sample_word_size; + /* + * The size in bits of the word that holds a sample of a channel. + * @values 16, 24, 32 + * 16-bit samples are always placed in 16-bit words: + * sample_word_size = 1. + * 24-bit samples can be placed in 32-bit words or in consecutive + * 24-bit words. + * - If sample_word_size = 32, 24-bit samples are placed in the + * most significant 24 bits of a 32-bit word. + * - If sample_word_size = 24, 24-bit samples are placed in + * 24-bit words. @tablebulletend + */ + uint8_t channel_mapping[8]; + /* + * Channel mapping array expected at the encoder output. + * Channel[i] mapping describes channel i inside the buffer, where + * 0 @le i < num_channels. All valid used channels must be present at + * the beginning of the array. + * If Native mode is set for the channels, this field is ignored. + * @values See Section @xref{dox:PcmChannelDefs} + */ + uint16_t endianness; + /* + * Flag to indicate the endianness of the pcm sample + * Supported values: 0 - Little endian (all other formats) + * 1 - Big endian (AIFF) + */ + uint16_t mode; + /* + * Mode to provide additional info about the pcm input data. + * Supported values: 0 - Default QFs (Q15 for 16b, Q23 for packed 24b, + * Q31 for unpacked 24b or 32b) + * 15 - for 16 bit + * 23 - for 24b packed or 8.24 format + * 31 - for 24b unpacked or 32bit + */ +} __packed; + +/* + * Payload of the multichannel PCM encoder configuration parameters in + * the ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V3 media format. + */ + +struct asm_multi_channel_pcm_enc_cfg_v3 { + struct apr_hdr hdr; + struct asm_stream_cmd_set_encdec_param encdec; + struct asm_enc_cfg_blk_param_v2 encblk; + uint16_t num_channels; + /* + * Number of PCM channels. + * @values + * - 0 -- Native mode + * - 1 -- 8 channels + * Native mode indicates that encoding must be performed with the number + * of channels at the input. + */ + uint16_t bits_per_sample; + /* + * Number of bits per sample per channel. + * @values 16, 24 + */ + uint32_t sample_rate; + /* + * Number of samples per second. + * @values 0, 8000 to 48000 Hz + * A value of 0 indicates the native sampling rate. Encoding is + * performed at the input sampling rate. + */ + uint16_t is_signed; + /* + * Flag that indicates the PCM samples are signed (1). Currently, only + * signed PCM samples are supported. + */ + uint16_t sample_word_size; + /* + * The size in bits of the word that holds a sample of a channel. + * @values 16, 24, 32 + * 16-bit samples are always placed in 16-bit words: + * sample_word_size = 1. + * 24-bit samples can be placed in 32-bit words or in consecutive + * 24-bit words. + * - If sample_word_size = 32, 24-bit samples are placed in the + * most significant 24 bits of a 32-bit word. + * - If sample_word_size = 24, 24-bit samples are placed in + * 24-bit words. @tablebulletend + */ + uint8_t channel_mapping[8]; + /* + * Channel mapping array expected at the encoder output. + * Channel[i] mapping describes channel i inside the buffer, where + * 0 @le i < num_channels. All valid used channels must be present at + * the beginning of the array. + * If Native mode is set for the channels, this field is ignored. + * @values See Section @xref{dox:PcmChannelDefs} + */ +}; + +/* @brief Multichannel PCM encoder configuration structure used + * in the #ASM_PARAM_ID_ENCDEC_ENC_CFG_BLK_V2 command. + */ + +struct asm_multi_channel_pcm_enc_cfg_v2 { + struct apr_hdr hdr; + struct asm_stream_cmd_set_encdec_param encdec; + struct asm_enc_cfg_blk_param_v2 encblk; + uint16_t num_channels; +/*< Number of PCM channels. + * + * Supported values: - 0 -- Native mode - 1 -- 8 Native mode + * indicates that encoding must be performed with the number of + * channels at the input. + */ + + uint16_t bits_per_sample; +/*< Number of bits per sample per channel. + * Supported values: 16, 24 + */ + + uint32_t sample_rate; +/*< Number of samples per second (in Hertz). + * + * Supported values: 0, 8000 to 48000 A value of 0 indicates the + * native sampling rate. Encoding is performed at the input sampling + * rate. + */ + + uint16_t is_signed; +/*< Specifies whether the samples are signed (1). Currently, + * only signed samples are supported. + */ + + uint16_t reserved; +/*< reserved field for 32 bit alignment. must be set to zero.*/ + + + uint8_t channel_mapping[8]; +} __packed; + +#define ASM_MEDIA_FMT_MP3 0x00010BE9 +#define ASM_MEDIA_FMT_AAC_V2 0x00010DA6 + +/* @xreflabel + * {hdr:AsmMediaFmtDolbyAac} Media format ID for the + * Dolby AAC decoder. This format ID is be used if the client wants + * to use the Dolby AAC decoder to decode MPEG2 and MPEG4 AAC + * contents. + */ + +#define ASM_MEDIA_FMT_DOLBY_AAC 0x00010D86 + +/* Enumeration for the audio data transport stream AAC format. */ +#define ASM_MEDIA_FMT_AAC_FORMAT_FLAG_ADTS 0 + +/* Enumeration for low overhead audio stream AAC format. */ +#define ASM_MEDIA_FMT_AAC_FORMAT_FLAG_LOAS 1 + +/* Enumeration for the audio data interchange format + * AAC format. + */ +#define ASM_MEDIA_FMT_AAC_FORMAT_FLAG_ADIF 2 + +/* Enumeration for the raw AAC format. */ +#define ASM_MEDIA_FMT_AAC_FORMAT_FLAG_RAW 3 + +/* Enumeration for the AAC LATM format. */ +#define ASM_MEDIA_FMT_AAC_FORMAT_FLAG_LATM 4 + +#define ASM_MEDIA_FMT_AAC_AOT_LC 2 +#define ASM_MEDIA_FMT_AAC_AOT_SBR 5 +#define ASM_MEDIA_FMT_AAC_AOT_PS 29 +#define ASM_MEDIA_FMT_AAC_AOT_BSAC 22 + +struct asm_aac_fmt_blk_v2 { + struct apr_hdr hdr; + struct asm_data_cmd_media_fmt_update_v2 fmt_blk; + + u16 aac_fmt_flag; +/* Bitstream format option. + * Supported values: + * - #ASM_MEDIA_FMT_AAC_FORMAT_FLAG_ADTS + * - #ASM_MEDIA_FMT_AAC_FORMAT_FLAG_LOAS + * - #ASM_MEDIA_FMT_AAC_FORMAT_FLAG_ADIF + * - #ASM_MEDIA_FMT_AAC_FORMAT_FLAG_RAW + */ + + u16 audio_objype; +/* Audio Object Type (AOT) present in the AAC stream. + * Supported values: + * - #ASM_MEDIA_FMT_AAC_AOT_LC + * - #ASM_MEDIA_FMT_AAC_AOT_SBR + * - #ASM_MEDIA_FMT_AAC_AOT_BSAC + * - #ASM_MEDIA_FMT_AAC_AOT_PS + * - Otherwise -- Not supported + */ + + u16 channel_config; +/* Number of channels present in the AAC stream. + * Supported values: + * - 1 -- Mono + * - 2 -- Stereo + * - 6 -- 5.1 content + */ + + u16 total_size_of_PCE_bits; +/* greater or equal to zero. * -In case of RAW formats and + * channel config = 0 (PCE), client can send * the bit stream + * containing PCE immediately following this structure * (in-band). + * -This number does not include bits included for 32 bit alignment. + * -If zero, then the PCE info is assumed to be available in the + * audio -bit stream & not in-band. + */ + + u32 sample_rate; +/* Number of samples per second (in Hertz). + * + * Supported values: 8000, 11025, 12000, 16000, 22050, 24000, 32000, + * 44100, 48000 + * + * This field must be equal to the sample rate of the AAC-LC + * decoder's output. - For MP4 or 3GP containers, this is indicated + * by the samplingFrequencyIndex field in the AudioSpecificConfig + * element. - For ADTS format, this is indicated by the + * samplingFrequencyIndex in the ADTS fixed header. - For ADIF + * format, this is indicated by the samplingFrequencyIndex in the + * program_config_element present in the ADIF header. + */ + +} __packed; + +struct asm_aac_enc_cfg_v2 { + struct apr_hdr hdr; + struct asm_stream_cmd_set_encdec_param encdec; + struct asm_enc_cfg_blk_param_v2 encblk; + + u32 bit_rate; + /* Encoding rate in bits per second. */ + u32 enc_mode; +/* Encoding mode. + * Supported values: + * - #ASM_MEDIA_FMT_AAC_AOT_LC + * - #ASM_MEDIA_FMT_AAC_AOT_SBR + * - #ASM_MEDIA_FMT_AAC_AOT_PS + */ + u16 aac_fmt_flag; +/* AAC format flag. + * Supported values: + * - #ASM_MEDIA_FMT_AAC_FORMAT_FLAG_ADTS + * - #ASM_MEDIA_FMT_AAC_FORMAT_FLAG_RAW + */ + u16 channel_cfg; +/* Number of channels to encode. + * Supported values: + * - 0 -- Native mode + * - 1 -- Mono + * - 2 -- Stereo + * - Other values are not supported. + * @note1hang The eAAC+ encoder mode supports only stereo. + * Native mode indicates that encoding must be performed with the + * number of channels at the input. + * The number of channels must not change during encoding. + */ + + u32 sample_rate; +/* Number of samples per second. + * Supported values: - 0 -- Native mode - For other values, + * Native mode indicates that encoding must be performed with the + * sampling rate at the input. + * The sampling rate must not change during encoding. + */ + +} __packed; + +#define ASM_MEDIA_FMT_G711_ALAW_FS 0x00010BF7 +#define ASM_MEDIA_FMT_G711_MLAW_FS 0x00010C2E + +struct asm_g711_enc_cfg_v2 { + struct apr_hdr hdr; + struct asm_stream_cmd_set_encdec_param encdec; + struct asm_enc_cfg_blk_param_v2 encblk; + + u32 sample_rate; +/* + * Number of samples per second. + * Supported values: 8000, 16000 Hz + */ + +} __packed; + +struct asm_vorbis_fmt_blk_v2 { + struct apr_hdr hdr; + struct asm_data_cmd_media_fmt_update_v2 fmtblk; + u32 bit_stream_fmt; +/* Bit stream format. + * Supported values: + * - 0 -- Raw bitstream + * - 1 -- Transcoded bitstream + * + * Transcoded bitstream containing the size of the frame as the first + * word in each frame. + */ + +} __packed; + +struct asm_flac_fmt_blk_v2 { + struct apr_hdr hdr; + struct asm_data_cmd_media_fmt_update_v2 fmtblk; + + u16 is_stream_info_present; +/* Specifies whether stream information is present in the FLAC format + * block. + * + * Supported values: + * - 0 -- Stream information is not present in this message + * - 1 -- Stream information is present in this message + * + * When set to 1, the FLAC bitstream was successfully parsed by the + * client, and other fields in the FLAC format block can be read by the + * decoder to get metadata stream information. + */ + + u16 num_channels; +/* Number of channels for decoding. + * Supported values: 1 to 2 + */ + + u16 min_blk_size; +/* Minimum block size (in samples) used in the stream. It must be less + * than or equal to max_blk_size. + */ + + u16 max_blk_size; +/* Maximum block size (in samples) used in the stream. If the + * minimum block size equals the maximum block size, a fixed block + * size stream is implied. + */ + + u16 md5_sum[8]; +/* MD5 signature array of the unencoded audio data. This allows the + * decoder to determine if an error exists in the audio data, even when + * the error does not result in an invalid bitstream. + */ + + u32 sample_rate; +/* Number of samples per second. + * Supported values: 8000 to 48000 Hz + */ + + u32 min_frame_size; +/* Minimum frame size used in the stream. + * Supported values: + * - > 0 bytes + * - 0 -- The value is unknown + */ + + u32 max_frame_size; +/* Maximum frame size used in the stream. + * Supported values: + * -- > 0 bytes + * -- 0 . The value is unknown + */ + + u16 sample_size; +/* Bits per sample.Supported values: 8, 16 */ + + u16 reserved; +/* Clients must set this field to zero + */ + +} __packed; + +struct asm_alac_fmt_blk_v2 { + struct apr_hdr hdr; + struct asm_data_cmd_media_fmt_update_v2 fmtblk; + + u32 frame_length; + u8 compatible_version; + u8 bit_depth; + u8 pb; + u8 mb; + u8 kb; + u8 num_channels; + u16 max_run; + u32 max_frame_bytes; + u32 avg_bit_rate; + u32 sample_rate; + u32 channel_layout_tag; + +} __packed; + +struct asm_g711_dec_fmt_blk_v2 { + struct apr_hdr hdr; + struct asm_data_cmd_media_fmt_update_v2 fmtblk; + u32 sample_rate; +} __packed; + +struct asm_ape_fmt_blk_v2 { + struct apr_hdr hdr; + struct asm_data_cmd_media_fmt_update_v2 fmtblk; + + u16 compatible_version; + u16 compression_level; + u32 format_flags; + u32 blocks_per_frame; + u32 final_frame_blocks; + u32 total_frames; + u16 bits_per_sample; + u16 num_channels; + u32 sample_rate; + u32 seek_table_present; + +} __packed; + +struct asm_dsd_fmt_blk_v2 { + struct apr_hdr hdr; + struct asm_data_cmd_media_fmt_update_v2 fmtblk; + + u16 num_version; + u16 is_bitwise_big_endian; + u16 dsd_channel_block_size; + u16 num_channels; + u8 channel_mapping[8]; + u32 dsd_data_rate; + +} __packed; + +#define ASM_MEDIA_FMT_AMRNB_FS 0x00010BEB + +/* Enumeration for 4.75 kbps AMR-NB Encoding mode. */ +#define ASM_MEDIA_FMT_AMRNB_FS_ENCODE_MODE_MR475 0 + +/* Enumeration for 5.15 kbps AMR-NB Encoding mode. */ +#define ASM_MEDIA_FMT_AMRNB_FS_ENCODE_MODE_MR515 1 + +/* Enumeration for 5.90 kbps AMR-NB Encoding mode. */ +#define ASM_MEDIA_FMT_AMRNB_FS_ENCODE_MODE_MMR59 2 + +/* Enumeration for 6.70 kbps AMR-NB Encoding mode. */ +#define ASM_MEDIA_FMT_AMRNB_FS_ENCODE_MODE_MMR67 3 + +/* Enumeration for 7.40 kbps AMR-NB Encoding mode. */ +#define ASM_MEDIA_FMT_AMRNB_FS_ENCODE_MODE_MMR74 4 + +/* Enumeration for 7.95 kbps AMR-NB Encoding mode. */ +#define ASM_MEDIA_FMT_AMRNB_FS_ENCODE_MODE_MMR795 5 + +/* Enumeration for 10.20 kbps AMR-NB Encoding mode. */ +#define ASM_MEDIA_FMT_AMRNB_FS_ENCODE_MODE_MMR102 6 + +/* Enumeration for 12.20 kbps AMR-NB Encoding mode. */ +#define ASM_MEDIA_FMT_AMRNB_FS_ENCODE_MODE_MMR122 7 + +/* Enumeration for AMR-NB Discontinuous Transmission mode off. */ +#define ASM_MEDIA_FMT_AMRNB_FS_DTX_MODE_OFF 0 + +/* Enumeration for AMR-NB DTX mode VAD1. */ +#define ASM_MEDIA_FMT_AMRNB_FS_DTX_MODE_VAD1 1 + +/* Enumeration for AMR-NB DTX mode VAD2. */ +#define ASM_MEDIA_FMT_AMRNB_FS_DTX_MODE_VAD2 2 + +/* Enumeration for AMR-NB DTX mode auto. */ +#define ASM_MEDIA_FMT_AMRNB_FS_DTX_MODE_AUTO 3 + +struct asm_amrnb_enc_cfg { + struct apr_hdr hdr; + struct asm_stream_cmd_set_encdec_param encdec; + struct asm_enc_cfg_blk_param_v2 encblk; + + u16 enc_mode; +/* AMR-NB encoding rate. + * Supported values: + * Use the ASM_MEDIA_FMT_AMRNB_FS_ENCODE_MODE_* + * macros + */ + + u16 dtx_mode; +/* Specifies whether DTX mode is disabled or enabled. + * Supported values: + * - #ASM_MEDIA_FMT_AMRNB_FS_DTX_MODE_OFF + * - #ASM_MEDIA_FMT_AMRNB_FS_DTX_MODE_VAD1 + */ +} __packed; + +#define ASM_MEDIA_FMT_AMRWB_FS 0x00010BEC + +/* Enumeration for 6.6 kbps AMR-WB Encoding mode. */ +#define ASM_MEDIA_FMT_AMRWB_FS_ENCODE_MODE_MR66 0 + +/* Enumeration for 8.85 kbps AMR-WB Encoding mode. */ +#define ASM_MEDIA_FMT_AMRWB_FS_ENCODE_MODE_MR885 1 + +/* Enumeration for 12.65 kbps AMR-WB Encoding mode. */ +#define ASM_MEDIA_FMT_AMRWB_FS_ENCODE_MODE_MR1265 2 + +/* Enumeration for 14.25 kbps AMR-WB Encoding mode. */ +#define ASM_MEDIA_FMT_AMRWB_FS_ENCODE_MODE_MR1425 3 + +/* Enumeration for 15.85 kbps AMR-WB Encoding mode. */ +#define ASM_MEDIA_FMT_AMRWB_FS_ENCODE_MODE_MR1585 4 + +/* Enumeration for 18.25 kbps AMR-WB Encoding mode. */ +#define ASM_MEDIA_FMT_AMRWB_FS_ENCODE_MODE_MR1825 5 + +/* Enumeration for 19.85 kbps AMR-WB Encoding mode. */ +#define ASM_MEDIA_FMT_AMRWB_FS_ENCODE_MODE_MR1985 6 + +/* Enumeration for 23.05 kbps AMR-WB Encoding mode. */ +#define ASM_MEDIA_FMT_AMRWB_FS_ENCODE_MODE_MR2305 7 + +/* Enumeration for 23.85 kbps AMR-WB Encoding mode. */ +#define ASM_MEDIA_FMT_AMRWB_FS_ENCODE_MODE_MR2385 8 + +struct asm_amrwb_enc_cfg { + struct apr_hdr hdr; + struct asm_stream_cmd_set_encdec_param encdec; + struct asm_enc_cfg_blk_param_v2 encblk; + + u16 enc_mode; +/* AMR-WB encoding rate. + * Suupported values: + * Use the ASM_MEDIA_FMT_AMRWB_FS_ENCODE_MODE_* + * macros + */ + + u16 dtx_mode; +/* Specifies whether DTX mode is disabled or enabled. + * Supported values: + * - #ASM_MEDIA_FMT_AMRNB_FS_DTX_MODE_OFF + * - #ASM_MEDIA_FMT_AMRNB_FS_DTX_MODE_VAD1 + */ +} __packed; + +#define ASM_MEDIA_FMT_V13K_FS 0x00010BED + +/* Enumeration for 14.4 kbps V13K Encoding mode. */ +#define ASM_MEDIA_FMT_V13K_FS_ENCODE_MODE_MR1440 0 + +/* Enumeration for 12.2 kbps V13K Encoding mode. */ +#define ASM_MEDIA_FMT_V13K_FS_ENCODE_MODE_MR1220 1 + +/* Enumeration for 11.2 kbps V13K Encoding mode. */ +#define ASM_MEDIA_FMT_V13K_FS_ENCODE_MODE_MR1120 2 + +/* Enumeration for 9.0 kbps V13K Encoding mode. */ +#define ASM_MEDIA_FMT_V13K_FS_ENCODE_MODE_MR90 3 + +/* Enumeration for 7.2 kbps V13K eEncoding mode. */ +#define ASM_MEDIA_FMT_V13K_FS_ENCODE_MODE_MR720 4 + +/* Enumeration for 1/8 vocoder rate.*/ +#define ASM_MEDIA_FMT_VOC_ONE_EIGHTH_RATE 1 + +/* Enumeration for 1/4 vocoder rate. */ +#define ASM_MEDIA_FMT_VOC_ONE_FOURTH_RATE 2 + +/* Enumeration for 1/2 vocoder rate. */ +#define ASM_MEDIA_FMT_VOC_HALF_RATE 3 + +/* Enumeration for full vocoder rate. */ +#define ASM_MEDIA_FMT_VOC_FULL_RATE 4 + +struct asm_v13k_enc_cfg { + struct apr_hdr hdr; + struct asm_stream_cmd_set_encdec_param encdec; + struct asm_enc_cfg_blk_param_v2 encblk; + u16 max_rate; +/* Maximum allowed encoder frame rate. + * Supported values: + * - #ASM_MEDIA_FMT_VOC_ONE_EIGHTH_RATE + * - #ASM_MEDIA_FMT_VOC_ONE_FOURTH_RATE + * - #ASM_MEDIA_FMT_VOC_HALF_RATE + * - #ASM_MEDIA_FMT_VOC_FULL_RATE + */ + + u16 min_rate; +/* Minimum allowed encoder frame rate. + * Supported values: + * - #ASM_MEDIA_FMT_VOC_ONE_EIGHTH_RATE + * - #ASM_MEDIA_FMT_VOC_ONE_FOURTH_RATE + * - #ASM_MEDIA_FMT_VOC_HALF_RATE + * - #ASM_MEDIA_FMT_VOC_FULL_RATE + */ + + u16 reduced_rate_cmd; +/* Reduced rate command, used to change + * the average bitrate of the V13K + * vocoder. + * Supported values: + * - #ASM_MEDIA_FMT_V13K_FS_ENCODE_MODE_MR1440 (Default) + * - #ASM_MEDIA_FMT_V13K_FS_ENCODE_MODE_MR1220 + * - #ASM_MEDIA_FMT_V13K_FS_ENCODE_MODE_MR1120 + * - #ASM_MEDIA_FMT_V13K_FS_ENCODE_MODE_MR90 + * - #ASM_MEDIA_FMT_V13K_FS_ENCODE_MODE_MR720 + */ + + u16 rate_mod_cmd; +/* Rate modulation command. Default = 0. + *- If bit 0=1, rate control is enabled. + *- If bit 1=1, the maximum number of consecutive full rate + * frames is limited with numbers supplied in + * bits 2 to 10. + *- If bit 1=0, the minimum number of non-full rate frames + * in between two full rate frames is forced to + * the number supplied in bits 2 to 10. In both cases, if necessary, + * half rate is used to substitute full rate. - Bits 15 to 10 are + * reserved and must all be set to zero. + */ + +} __packed; + +#define ASM_MEDIA_FMT_EVRC_FS 0x00010BEE + +/* EVRC encoder configuration structure used in the + * #ASM_PARAM_ID_ENCDEC_ENC_CFG_BLK_V2 command. + */ +struct asm_evrc_enc_cfg { + struct apr_hdr hdr; + struct asm_stream_cmd_set_encdec_param encdec; + struct asm_enc_cfg_blk_param_v2 encblk; + u16 max_rate; +/* Maximum allowed encoder frame rate. + * Supported values: + * - #ASM_MEDIA_FMT_VOC_ONE_EIGHTH_RATE + * - #ASM_MEDIA_FMT_VOC_ONE_FOURTH_RATE + * - #ASM_MEDIA_FMT_VOC_HALF_RATE + * - #ASM_MEDIA_FMT_VOC_FULL_RATE + */ + + u16 min_rate; +/* Minimum allowed encoder frame rate. + * Supported values: + * - #ASM_MEDIA_FMT_VOC_ONE_EIGHTH_RATE + * - #ASM_MEDIA_FMT_VOC_ONE_FOURTH_RATE + * - #ASM_MEDIA_FMT_VOC_HALF_RATE + * - #ASM_MEDIA_FMT_VOC_FULL_RATE + */ + + u16 rate_mod_cmd; +/* Rate modulation command. Default: 0. + * - If bit 0=1, rate control is enabled. + * - If bit 1=1, the maximum number of consecutive full rate frames + * is limited with numbers supplied in bits 2 to 10. + * + * - If bit 1=0, the minimum number of non-full rate frames in + * between two full rate frames is forced to the number supplied in + * bits 2 to 10. In both cases, if necessary, half rate is used to + * substitute full rate. + * + * - Bits 15 to 10 are reserved and must all be set to zero. + */ + + u16 reserved; + /* Reserved. Clients must set this field to zero. */ +} __packed; + +#define ASM_MEDIA_FMT_WMA_V10PRO_V2 0x00010DA7 + +struct asm_wmaprov10_fmt_blk_v2 { + struct apr_hdr hdr; + struct asm_data_cmd_media_fmt_update_v2 fmtblk; + + u16 fmtag; +/* WMA format type. + * Supported values: + * - 0x162 -- WMA 9 Pro + * - 0x163 -- WMA 9 Pro Lossless + * - 0x166 -- WMA 10 Pro + * - 0x167 -- WMA 10 Pro Lossless + */ + + u16 num_channels; +/* Number of channels encoded in the input stream. + * Supported values: 1 to 8 + */ + + u32 sample_rate; +/* Number of samples per second (in Hertz). + * Supported values: 11025, 16000, 22050, 32000, 44100, 48000, + * 88200, 96000 + */ + + u32 avg_bytes_per_sec; +/* Bitrate expressed as the average bytes per second. + * Supported values: 2000 to 96000 + */ + + u16 blk_align; +/* Size of the bitstream packet size in bytes. WMA Pro files + * have a payload of one block per bitstream packet. + * Supported values: @le 13376 + */ + + u16 bits_per_sample; +/* Number of bits per sample in the encoded WMA stream. + * Supported values: 16, 24 + */ + + u32 channel_mask; +/* Bit-packed double word (32-bits) that indicates the + * recommended speaker positions for each source channel. + */ + + u16 enc_options; +/* Bit-packed word with values that indicate whether certain + * features of the bitstream are used. + * Supported values: - 0x0001 -- ENCOPT3_PURE_LOSSLESS - 0x0006 -- + * ENCOPT3_FRM_SIZE_MOD - 0x0038 -- ENCOPT3_SUBFRM_DIV - 0x0040 -- + * ENCOPT3_WRITE_FRAMESIZE_IN_HDR - 0x0080 -- + * ENCOPT3_GENERATE_DRC_PARAMS - 0x0100 -- ENCOPT3_RTMBITS + */ + + + u16 usAdvancedEncodeOpt; + /* Advanced encoding option. */ + + u32 advanced_enc_options2; + /* Advanced encoding option 2. */ + +} __packed; + +#define ASM_MEDIA_FMT_WMA_V9_V2 0x00010DA8 +struct asm_wmastdv9_fmt_blk_v2 { + struct apr_hdr hdr; + struct asm_data_cmd_media_fmt_update_v2 fmtblk; + u16 fmtag; +/* WMA format tag. + * Supported values: 0x161 (WMA 9 standard) + */ + + u16 num_channels; +/* Number of channels in the stream. + * Supported values: 1, 2 + */ + + u32 sample_rate; +/* Number of samples per second (in Hertz). + * Supported values: 48000 + */ + + u32 avg_bytes_per_sec; + /* Bitrate expressed as the average bytes per second. */ + + u16 blk_align; +/* Block align. All WMA files with a maximum packet size of + * 13376 are supported. + */ + + + u16 bits_per_sample; +/* Number of bits per sample in the output. + * Supported values: 16 + */ + + u32 channel_mask; +/* Channel mask. + * Supported values: + * - 3 -- Stereo (front left/front right) + * - 4 -- Mono (center) + */ + + u16 enc_options; + /* Options used during encoding. */ + + u16 reserved; + +} __packed; + +#define ASM_MEDIA_FMT_WMA_V8 0x00010D91 + +struct asm_wmastdv8_enc_cfg { + struct apr_hdr hdr; + struct asm_stream_cmd_set_encdec_param encdec; + struct asm_enc_cfg_blk_param_v2 encblk; + u32 bit_rate; + /* Encoding rate in bits per second. */ + + u32 sample_rate; +/* Number of samples per second. + * + * Supported values: + * - 0 -- Native mode + * - Other Supported values are 22050, 32000, 44100, and 48000. + * + * Native mode indicates that encoding must be performed with the + * sampling rate at the input. + * The sampling rate must not change during encoding. + */ + + u16 channel_cfg; +/* Number of channels to encode. + * Supported values: + * - 0 -- Native mode + * - 1 -- Mono + * - 2 -- Stereo + * - Other values are not supported. + * + * Native mode indicates that encoding must be performed with the + * number of channels at the input. + * The number of channels must not change during encoding. + */ + + u16 reserved; + /* Reserved. Clients must set this field to zero.*/ + } __packed; + +#define ASM_MEDIA_FMT_AMR_WB_PLUS_V2 0x00010DA9 + +struct asm_amrwbplus_fmt_blk_v2 { + struct apr_hdr hdr; + struct asm_data_cmd_media_fmt_update_v2 fmtblk; + u32 amr_frame_fmt; +/* AMR frame format. + * Supported values: + * - 6 -- Transport Interface Format (TIF) + * - Any other value -- File storage format (FSF) + * + * TIF stream contains 2-byte header for each frame within the + * superframe. FSF stream contains one 2-byte header per superframe. + */ + +} __packed; + +#define ASM_MEDIA_FMT_AC3 0x00010DEE +#define ASM_MEDIA_FMT_EAC3 0x00010DEF +#define ASM_MEDIA_FMT_DTS 0x00010D88 +#define ASM_MEDIA_FMT_MP2 0x00010DE9 +#define ASM_MEDIA_FMT_FLAC 0x00010C16 +#define ASM_MEDIA_FMT_ALAC 0x00012F31 +#define ASM_MEDIA_FMT_VORBIS 0x00010C15 +#define ASM_MEDIA_FMT_APE 0x00012F32 +#define ASM_MEDIA_FMT_DSD 0x00012F3E +#define ASM_MEDIA_FMT_TRUEHD 0x00013215 +/* 0x0 is used for fomat ID since ADSP dynamically determines the + * format encapsulated in the IEC61937 (compressed) or IEC60958 + * (pcm) packets. + */ +#define ASM_MEDIA_FMT_IEC 0x00000000 + +/* Media format ID for adaptive transform acoustic coding. This + * ID is used by the #ASM_STREAM_CMD_OPEN_WRITE_COMPRESSED command + * only. + */ + +#define ASM_MEDIA_FMT_ATRAC 0x00010D89 + +/* Media format ID for metadata-enhanced audio transmission. + * This ID is used by the #ASM_STREAM_CMD_OPEN_WRITE_COMPRESSED + * command only. + */ + +#define ASM_MEDIA_FMT_MAT 0x00010D8A + +/* adsp_media_fmt.h */ + +#define ASM_DATA_CMD_WRITE_V2 0x00010DAB + +struct asm_data_cmd_write_v2 { + struct apr_hdr hdr; + u32 buf_addr_lsw; +/* The 64 bit address msw-lsw should be a valid, mapped address. + * 64 bit address should be a multiple of 32 bytes + */ + + u32 buf_addr_msw; +/* The 64 bit address msw-lsw should be a valid, mapped address. + * 64 bit address should be a multiple of 32 bytes. + * -Address of the buffer containing the data to be decoded. + * The buffer should be aligned to a 32 byte boundary. + * -In the case of 32 bit Shared memory address, msw field must + * -be set to zero. + * -In the case of 36 bit shared memory address, bit 31 to bit 4 + * -of msw must be set to zero. + */ + u32 mem_map_handle; +/* memory map handle returned by DSP through + * ASM_CMD_SHARED_MEM_MAP_REGIONS command + */ + u32 buf_size; +/* Number of valid bytes available in the buffer for decoding. The + * first byte starts at buf_addr. + */ + + u32 seq_id; + /* Optional buffer sequence ID. */ + + u32 timestamp_lsw; +/* Lower 32 bits of the 64-bit session time in microseconds of the + * first buffer sample. + */ + + u32 timestamp_msw; +/* Upper 32 bits of the 64-bit session time in microseconds of the + * first buffer sample. + */ + + u32 flags; +/* Bitfield of flags. + * Supported values for bit 31: + * - 1 -- Valid timestamp. + * - 0 -- Invalid timestamp. + * - Use #ASM_BIT_MASKIMESTAMP_VALID_FLAG as the bitmask and + * #ASM_SHIFTIMESTAMP_VALID_FLAG as the shift value to set this bit. + * Supported values for bit 30: + * - 1 -- Last buffer. + * - 0 -- Not the last buffer. + * + * Supported values for bit 29: + * - 1 -- Continue the timestamp from the previous buffer. + * - 0 -- Timestamp of the current buffer is not related + * to the timestamp of the previous buffer. + * - Use #ASM_BIT_MASKS_CONTINUE_FLAG and #ASM_SHIFTS_CONTINUE_FLAG + * to set this bit. + * + * Supported values for bit 4: + * - 1 -- End of the frame. + * - 0 -- Not the end of frame, or this information is not known. + * - Use #ASM_BIT_MASK_EOF_FLAG as the bitmask and #ASM_SHIFT_EOF_FLAG + * as the shift value to set this bit. + * + * All other bits are reserved and must be set to 0. + * + * If bit 31=0 and bit 29=1: The timestamp of the first sample in + * this buffer continues from the timestamp of the last sample in + * the previous buffer. If there is no previous buffer (i.e., this + * is the first buffer sent after opening the stream or after a + * flush operation), or if the previous buffer does not have a valid + * timestamp, the samples in the current buffer also do not have a + * valid timestamp. They are played out as soon as possible. + * + * + * If bit 31=0 and bit 29=0: No timestamp is associated with the + * first sample in this buffer. The samples are played out as soon + * as possible. + * + * + * If bit 31=1 and bit 29 is ignored: The timestamp specified in + * this payload is honored. + * + * + * If bit 30=0: Not the last buffer in the stream. This is useful + * in removing trailing samples. + * + * + * For bit 4: The client can set this flag for every buffer sent in + * which the last byte is the end of a frame. If this flag is set, + * the buffer can contain data from multiple frames, but it should + * always end at a frame boundary. Restrictions allow the aDSP to + * detect an end of frame without requiring additional processing. + */ + +} __packed; + +#define ASM_DATA_CMD_READ_V2 0x00010DAC + +struct asm_data_cmd_read_v2 { + struct apr_hdr hdr; + u32 buf_addr_lsw; +/* the 64 bit address msw-lsw should be a valid mapped address + * and should be a multiple of 32 bytes + */ + + + u32 buf_addr_msw; +/* the 64 bit address msw-lsw should be a valid mapped address + * and should be a multiple of 32 bytes. + * - Address of the buffer where the DSP puts the encoded data, + * potentially, at an offset specified by the uOffset field in + * ASM_DATA_EVENT_READ_DONE structure. The buffer should be aligned + * to a 32 byte boundary. + * - In the case of 32 bit Shared memory address, msw field must + * - be set to zero. + * - In the case of 36 bit shared memory address, bit 31 to bit + * - 4 of msw must be set to zero. + */ + u32 mem_map_handle; +/* memory map handle returned by DSP through + * ASM_CMD_SHARED_MEM_MAP_REGIONS command. + */ + + u32 buf_size; +/* Number of bytes available for the aDSP to write. The aDSP + * starts writing from buf_addr. + */ + + u32 seq_id; + /* Optional buffer sequence ID. */ +} __packed; + +#define ASM_DATA_CMD_EOS 0x00010BDB +#define ASM_DATA_EVENT_RENDERED_EOS 0x00010C1C +#define ASM_DATA_EVENT_EOS 0x00010BDD + +#define ASM_DATA_EVENT_WRITE_DONE_V2 0x00010D99 +struct asm_data_event_write_done_v2 { + u32 buf_addr_lsw; + /* lsw of the 64 bit address */ + u32 buf_addr_msw; + /* msw of the 64 bit address. address given by the client in + * ASM_DATA_CMD_WRITE_V2 command. + */ + u32 mem_map_handle; + /* memory map handle in the ASM_DATA_CMD_WRITE_V2 */ + + u32 status; +/* Status message (error code) that indicates whether the + * referenced buffer has been successfully consumed. + * Supported values: Refer to @xhyperref{Q3,[Q3]} + */ +} __packed; + +#define ASM_DATA_EVENT_READ_DONE_V2 0x00010D9A + +/* Definition of the frame metadata flag bitmask.*/ +#define ASM_BIT_MASK_FRAME_METADATA_FLAG (0x40000000UL) + +/* Definition of the frame metadata flag shift value. */ +#define ASM_SHIFT_FRAME_METADATA_FLAG 30 + +struct asm_data_event_read_done_v2 { + u32 status; +/* Status message (error code). + * Supported values: Refer to @xhyperref{Q3,[Q3]} + */ + +u32 buf_addr_lsw; +/* 64 bit address msw-lsw is a valid, mapped address. 64 bit + * address is a multiple of 32 bytes. + */ + +u32 buf_addr_msw; +/* 64 bit address msw-lsw is a valid, mapped address. 64 bit + * address is a multiple of 32 bytes. + * + * -Same address provided by the client in ASM_DATA_CMD_READ_V2 + * -In the case of 32 bit Shared memory address, msw field is set to + * zero. + * -In the case of 36 bit shared memory address, bit 31 to bit 4 + * -of msw is set to zero. + */ + +u32 mem_map_handle; +/* memory map handle in the ASM_DATA_CMD_READ_V2 */ + +u32 enc_framesotal_size; +/* Total size of the encoded frames in bytes. + * Supported values: >0 + */ + +u32 offset; +/* Offset (from buf_addr) to the first byte of the first encoded + * frame. All encoded frames are consecutive, starting from this + * offset. + * Supported values: > 0 + */ + +u32 timestamp_lsw; +/* Lower 32 bits of the 64-bit session time in microseconds of + * the first sample in the buffer. If Bit 5 of mode_flags flag of + * ASM_STREAM_CMD_OPEN_READ_V2 is 1 then the 64 bit timestamp is + * absolute capture time otherwise it is relative session time. The + * absolute timestamp doesn't reset unless the system is reset. + */ + + +u32 timestamp_msw; +/* Upper 32 bits of the 64-bit session time in microseconds of + * the first sample in the buffer. + */ + + +u32 flags; +/* Bitfield of flags. Bit 30 indicates whether frame metadata is + * present. If frame metadata is present, num_frames consecutive + * instances of @xhyperref{hdr:FrameMetaData,Frame metadata} start + * at the buffer address. + * Supported values for bit 31: + * - 1 -- Timestamp is valid. + * - 0 -- Timestamp is invalid. + * - Use #ASM_BIT_MASKIMESTAMP_VALID_FLAG and + * #ASM_SHIFTIMESTAMP_VALID_FLAG to set this bit. + * + * Supported values for bit 30: + * - 1 -- Frame metadata is present. + * - 0 -- Frame metadata is absent. + * - Use #ASM_BIT_MASK_FRAME_METADATA_FLAG and + * #ASM_SHIFT_FRAME_METADATA_FLAG to set this bit. + * + * All other bits are reserved; the aDSP sets them to 0. + */ + +u32 num_frames; +/* Number of encoded frames in the buffer. */ + +u32 seq_id; +/* Optional buffer sequence ID. */ +} __packed; + +struct asm_data_read_buf_metadata_v2 { + u32 offset; +/* Offset from buf_addr in #ASM_DATA_EVENT_READ_DONE_PAYLOAD to + * the frame associated with this metadata. + * Supported values: > 0 + */ + +u32 frm_size; +/* Size of the encoded frame in bytes. + * Supported values: > 0 + */ + +u32 num_encoded_pcm_samples; +/* Number of encoded PCM samples (per channel) in the frame + * associated with this metadata. + * Supported values: > 0 + */ + +u32 timestamp_lsw; +/* Lower 32 bits of the 64-bit session time in microseconds of the + * first sample for this frame. + * If Bit 5 of mode_flags flag of ASM_STREAM_CMD_OPEN_READ_V2 is 1 + * then the 64 bit timestamp is absolute capture time otherwise it + * is relative session time. The absolute timestamp doesn't reset + * unless the system is reset. + */ + + +u32 timestamp_msw; +/* Lower 32 bits of the 64-bit session time in microseconds of the + * first sample for this frame. + */ + +u32 flags; +/* Frame flags. + * Supported values for bit 31: + * - 1 -- Time stamp is valid + * - 0 -- Time stamp is not valid + * - All other bits are reserved; the aDSP sets them to 0. + */ +} __packed; + +/* Notifies the client of a change in the data sampling rate or + * Channel mode. This event is raised by the decoder service. The + * event is enabled through the mode flags of + * #ASM_STREAM_CMD_OPEN_WRITE_V2 or + * #ASM_STREAM_CMD_OPEN_READWRITE_V2. - The decoder detects a change + * in the output sampling frequency or the number/positioning of + * output channels, or if it is the first frame decoded.The new + * sampling frequency or the new channel configuration is + * communicated back to the client asynchronously. + */ + +#define ASM_DATA_EVENT_SR_CM_CHANGE_NOTIFY 0x00010C65 + +/* Payload of the #ASM_DATA_EVENT_SR_CM_CHANGE_NOTIFY event. + * This event is raised when the following conditions are both true: + * - The event is enabled through the mode_flags of + * #ASM_STREAM_CMD_OPEN_WRITE_V2 or + * #ASM_STREAM_CMD_OPEN_READWRITE_V2. - The decoder detects a change + * in either the output sampling frequency or the number/positioning + * of output channels, or if it is the first frame decoded. + * This event is not raised (even if enabled) if the decoder is + * MIDI, because + */ + + +struct asm_data_event_sr_cm_change_notify { + u32 sample_rate; +/* New sampling rate (in Hertz) after detecting a change in the + * bitstream. + * Supported values: 2000 to 48000 + */ + + u16 num_channels; +/* New number of channels after detecting a change in the + * bitstream. + * Supported values: 1 to 8 + */ + + + u16 reserved; + /* Reserved for future use. This field must be set to 0.*/ + + u8 channel_mapping[8]; + +} __packed; + +/* Notifies the client of a data sampling rate or channel mode + * change. This event is raised by the encoder service. + * This event is raised when : + * - Native mode encoding was requested in the encoder + * configuration (i.e., the channel number was 0), the sample rate + * was 0, or both were 0. + * + * - The input data frame at the encoder is the first one, or the + * sampling rate/channel mode is different from the previous input + * data frame. + * + */ +#define ASM_DATA_EVENT_ENC_SR_CM_CHANGE_NOTIFY 0x00010BDE + +struct asm_data_event_enc_sr_cm_change_notify { + u32 sample_rate; +/* New sampling rate (in Hertz) after detecting a change in the + * input data. + * Supported values: 2000 to 48000 + */ + + + u16 num_channels; +/* New number of channels after detecting a change in the input + * data. Supported values: 1 to 8 + */ + + + u16 bits_per_sample; +/* New bits per sample after detecting a change in the input + * data. + * Supported values: 16, 24 + */ + + + u8 channel_mapping[8]; + +} __packed; +#define ASM_DATA_CMD_IEC_60958_FRAME_RATE 0x00010D87 + + +/* Payload of the #ASM_DATA_CMD_IEC_60958_FRAME_RATE command, + * which is used to indicate the IEC 60958 frame rate of a given + * packetized audio stream. + */ + +struct asm_data_cmd_iec_60958_frame_rate { + u32 frame_rate; +/* IEC 60958 frame rate of the incoming IEC 61937 packetized stream. + * Supported values: Any valid frame rate + */ +} __packed; + +/* adsp_asm_data_commands.h*/ +/* Definition of the stream ID bitmask.*/ +#define ASM_BIT_MASK_STREAM_ID (0x000000FFUL) + +/* Definition of the stream ID shift value.*/ +#define ASM_SHIFT_STREAM_ID 0 + +/* Definition of the session ID bitmask.*/ +#define ASM_BIT_MASK_SESSION_ID (0x0000FF00UL) + +/* Definition of the session ID shift value.*/ +#define ASM_SHIFT_SESSION_ID 8 + +/* Definition of the service ID bitmask.*/ +#define ASM_BIT_MASK_SERVICE_ID (0x00FF0000UL) + +/* Definition of the service ID shift value.*/ +#define ASM_SHIFT_SERVICE_ID 16 + +/* Definition of the domain ID bitmask.*/ +#define ASM_BIT_MASK_DOMAIN_ID (0xFF000000UL) + +/* Definition of the domain ID shift value.*/ +#define ASM_SHIFT_DOMAIN_ID 24 + +#define ASM_CMD_SHARED_MEM_MAP_REGIONS 0x00010D92 +#define ASM_CMDRSP_SHARED_MEM_MAP_REGIONS 0x00010D93 +#define ASM_CMD_SHARED_MEM_UNMAP_REGIONS 0x00010D94 + +/* adsp_asm_service_commands.h */ + +#define ASM_MAX_SESSION_ID (15) + +/* Maximum number of sessions.*/ +#define ASM_MAX_NUM_SESSIONS ASM_MAX_SESSION_ID + +/* Maximum number of streams per session.*/ +#define ASM_MAX_STREAMS_PER_SESSION (8) +#define ASM_SESSION_CMD_RUN_V2 0x00010DAA +#define ASM_SESSION_CMD_RUN_STARTIME_RUN_IMMEDIATE 0 +#define ASM_SESSION_CMD_RUN_STARTIME_RUN_AT_ABSOLUTEIME 1 +#define ASM_SESSION_CMD_RUN_STARTIME_RUN_AT_RELATIVEIME 2 +#define ASM_SESSION_CMD_RUN_STARTIME_RUN_WITH_DELAY 3 + +#define ASM_BIT_MASK_RUN_STARTIME (0x00000003UL) + +/* Bit shift value used to specify the start time for the + * ASM_SESSION_CMD_RUN_V2 command. + */ +#define ASM_SHIFT_RUN_STARTIME 0 +struct asm_session_cmd_run_v2 { + struct apr_hdr hdr; + u32 flags; +/* Specifies whether to run immediately or at a specific + * rendering time or with a specified delay. Run with delay is + * useful for delaying in case of ASM loopback opened through + * ASM_STREAM_CMD_OPEN_LOOPBACK_V2. Use #ASM_BIT_MASK_RUN_STARTIME + * and #ASM_SHIFT_RUN_STARTIME to set this 2-bit flag. + * + * + *Bits 0 and 1 can take one of four possible values: + * + *- #ASM_SESSION_CMD_RUN_STARTIME_RUN_IMMEDIATE + *- #ASM_SESSION_CMD_RUN_STARTIME_RUN_AT_ABSOLUTEIME + *- #ASM_SESSION_CMD_RUN_STARTIME_RUN_AT_RELATIVEIME + *- #ASM_SESSION_CMD_RUN_STARTIME_RUN_WITH_DELAY + * + *All other bits are reserved; clients must set them to zero. + */ + + u32 time_lsw; +/* Lower 32 bits of the time in microseconds used to align the + * session origin time. When bits 0-1 of flags is + * ASM_SESSION_CMD_RUN_START_RUN_WITH_DELAY, time lsw is the lsw of + * the delay in us. For ASM_SESSION_CMD_RUN_START_RUN_WITH_DELAY, + * maximum value of the 64 bit delay is 150 ms. + */ + + u32 time_msw; +/* Upper 32 bits of the time in microseconds used to align the + * session origin time. When bits 0-1 of flags is + * ASM_SESSION_CMD_RUN_START_RUN_WITH_DELAY, time msw is the msw of + * the delay in us. For ASM_SESSION_CMD_RUN_START_RUN_WITH_DELAY, + * maximum value of the 64 bit delay is 150 ms. + */ + +} __packed; + +#define ASM_SESSION_CMD_PAUSE 0x00010BD3 +#define ASM_SESSION_CMD_SUSPEND 0x00010DEC +#define ASM_SESSION_CMD_GET_SESSIONTIME_V3 0x00010D9D +#define ASM_SESSION_CMD_REGISTER_FOR_RX_UNDERFLOW_EVENTS 0x00010BD5 + +struct asm_session_cmd_rgstr_rx_underflow { + struct apr_hdr hdr; + u16 enable_flag; +/* Specifies whether a client is to receive events when an Rx + * session underflows. + * Supported values: + * - 0 -- Do not send underflow events + * - 1 -- Send underflow events + */ + u16 reserved; + /* Reserved. This field must be set to zero.*/ +} __packed; + +#define ASM_SESSION_CMD_REGISTER_FORX_OVERFLOW_EVENTS 0x00010BD6 + +struct asm_session_cmd_regx_overflow { + struct apr_hdr hdr; + u16 enable_flag; +/* Specifies whether a client is to receive events when a Tx + * session overflows. + * Supported values: + * - 0 -- Do not send overflow events + * - 1 -- Send overflow events + */ + + u16 reserved; + /* Reserved. This field must be set to zero.*/ +} __packed; + +#define ASM_SESSION_EVENT_RX_UNDERFLOW 0x00010C17 +#define ASM_SESSION_EVENTX_OVERFLOW 0x00010C18 +#define ASM_SESSION_CMDRSP_GET_SESSIONTIME_V3 0x00010D9E + +struct asm_session_cmdrsp_get_sessiontime_v3 { + u32 status; + /* Status message (error code). + * Supported values: Refer to @xhyperref{Q3,[Q3]} + */ + + u32 sessiontime_lsw; + /* Lower 32 bits of the current session time in microseconds.*/ + + u32 sessiontime_msw; + /* Upper 32 bits of the current session time in microseconds.*/ + + u32 absolutetime_lsw; +/* Lower 32 bits in micro seconds of the absolute time at which + * the * sample corresponding to the above session time gets + * rendered * to hardware. This absolute time may be slightly in the + * future or past. + */ + + + u32 absolutetime_msw; +/* Upper 32 bits in micro seconds of the absolute time at which + * the * sample corresponding to the above session time gets + * rendered to * hardware. This absolute time may be slightly in the + * future or past. + */ + +} __packed; + +#define ASM_SESSION_CMD_ADJUST_SESSION_CLOCK_V2 0x00010D9F + +struct asm_session_cmd_adjust_session_clock_v2 { + struct apr_hdr hdr; +u32 adjustime_lsw; +/* Lower 32 bits of the signed 64-bit quantity that specifies the + * adjustment time in microseconds to the session clock. + * + * Positive values indicate advancement of the session clock. + * Negative values indicate delay of the session clock. + */ + + + u32 adjustime_msw; +/* Upper 32 bits of the signed 64-bit quantity that specifies + * the adjustment time in microseconds to the session clock. + * Positive values indicate advancement of the session clock. + * Negative values indicate delay of the session clock. + */ + +} __packed; + +#define ASM_SESSION_CMDRSP_ADJUST_SESSION_CLOCK_V2 0x00010DA0 + +struct asm_session_cmdrsp_adjust_session_clock_v2 { + u32 status; +/* Status message (error code). + * Supported values: Refer to @xhyperref{Q3,[Q3]} + * An error means the session clock is not adjusted. In this case, + * the next two fields are irrelevant. + */ + + + u32 actual_adjustime_lsw; +/* Lower 32 bits of the signed 64-bit quantity that specifies + * the actual adjustment in microseconds performed by the aDSP. + * A positive value indicates advancement of the session clock. A + * negative value indicates delay of the session clock. + */ + + + u32 actual_adjustime_msw; +/* Upper 32 bits of the signed 64-bit quantity that specifies + * the actual adjustment in microseconds performed by the aDSP. + * A positive value indicates advancement of the session clock. A + * negative value indicates delay of the session clock. + */ + + + u32 cmd_latency_lsw; +/* Lower 32 bits of the unsigned 64-bit quantity that specifies + * the amount of time in microseconds taken to perform the session + * clock adjustment. + */ + + + u32 cmd_latency_msw; +/* Upper 32 bits of the unsigned 64-bit quantity that specifies + * the amount of time in microseconds taken to perform the session + * clock adjustment. + */ + +} __packed; + +#define ASM_SESSION_CMD_GET_PATH_DELAY_V2 0x00010DAF +#define ASM_SESSION_CMDRSP_GET_PATH_DELAY_V2 0x00010DB0 + +struct asm_session_cmdrsp_get_path_delay_v2 { + u32 status; +/* Status message (error code). Whether this get delay operation + * is successful or not. Delay value is valid only if status is + * success. + * Supported values: Refer to @xhyperref{Q5,[Q5]} + */ + + u32 audio_delay_lsw; + /* Upper 32 bits of the aDSP delay in microseconds. */ + + u32 audio_delay_msw; + /* Lower 32 bits of the aDSP delay in microseconds. */ + +} __packed; + +/* adsp_asm_session_command.h*/ +#define ASM_STREAM_CMD_OPEN_WRITE_V3 0x00010DB3 + +#define ASM_LOW_LATENCY_STREAM_SESSION 0x10000000 + +#define ASM_ULTRA_LOW_LATENCY_STREAM_SESSION 0x20000000 + +#define ASM_ULL_POST_PROCESSING_STREAM_SESSION 0x40000000 + +#define ASM_LEGACY_STREAM_SESSION 0 + + +struct asm_stream_cmd_open_write_v3 { + struct apr_hdr hdr; + uint32_t mode_flags; +/* Mode flags that configure the stream to notify the client + * whenever it detects an SR/CM change at the input to its POPP. + * Supported values for bits 0 to 1: + * - Reserved; clients must set them to zero. + * Supported values for bit 2: + * - 0 -- SR/CM change notification event is disabled. + * - 1 -- SR/CM change notification event is enabled. + * - Use #ASM_BIT_MASK_SR_CM_CHANGE_NOTIFY_FLAG and + * #ASM_SHIFT_SR_CM_CHANGE_NOTIFY_FLAG to set or get this bit. + * + * Supported values for bit 31: + * - 0 -- Stream to be opened in on-Gapless mode. + * - 1 -- Stream to be opened in Gapless mode. In Gapless mode, + * successive streams must be opened with same session ID but + * different stream IDs. + * + * - Use #ASM_BIT_MASK_GAPLESS_MODE_FLAG and + * #ASM_SHIFT_GAPLESS_MODE_FLAG to set or get this bit. + * + * + * @note1hang MIDI and DTMF streams cannot be opened in Gapless mode. + */ + + uint16_t sink_endpointype; +/*< Sink point type. + * Supported values: + * - 0 -- Device matrix + * - Other values are reserved. + * + * The device matrix is the gateway to the hardware ports. + */ + + uint16_t bits_per_sample; +/*< Number of bits per sample processed by ASM modules. + * Supported values: 16 and 24 bits per sample + */ + + uint32_t postprocopo_id; +/*< Specifies the topology (order of processing) of + * postprocessing algorithms. None means no postprocessing. + * Supported values: + * - #ASM_STREAM_POSTPROCOPO_ID_DEFAULT + * - #ASM_STREAM_POSTPROCOPO_ID_MCH_PEAK_VOL + * - #ASM_STREAM_POSTPROCOPO_ID_NONE + * + * This field can also be enabled through SetParams flags. + */ + + uint32_t dec_fmt_id; +/*< Configuration ID of the decoder media format. + * + * Supported values: + * - #ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V2 + * - #ASM_MEDIA_FMT_ADPCM + * - #ASM_MEDIA_FMT_MP3 + * - #ASM_MEDIA_FMT_AAC_V2 + * - #ASM_MEDIA_FMT_DOLBY_AAC + * - #ASM_MEDIA_FMT_AMRNB_FS + * - #ASM_MEDIA_FMT_AMRWB_FS + * - #ASM_MEDIA_FMT_AMR_WB_PLUS_V2 + * - #ASM_MEDIA_FMT_V13K_FS + * - #ASM_MEDIA_FMT_EVRC_FS + * - #ASM_MEDIA_FMT_EVRCB_FS + * - #ASM_MEDIA_FMT_EVRCWB_FS + * - #ASM_MEDIA_FMT_SBC + * - #ASM_MEDIA_FMT_WMA_V10PRO_V2 + * - #ASM_MEDIA_FMT_WMA_V9_V2 + * - #ASM_MEDIA_FMT_AC3 + * - #ASM_MEDIA_FMT_EAC3 + * - #ASM_MEDIA_FMT_G711_ALAW_FS + * - #ASM_MEDIA_FMT_G711_MLAW_FS + * - #ASM_MEDIA_FMT_G729A_FS + * - #ASM_MEDIA_FMT_FR_FS + * - #ASM_MEDIA_FMT_VORBIS + * - #ASM_MEDIA_FMT_FLAC + * - #ASM_MEDIA_FMT_ALAC + * - #ASM_MEDIA_FMT_APE + * - #ASM_MEDIA_FMT_EXAMPLE + */ +} __packed; + +#define ASM_STREAM_CMD_OPEN_PULL_MODE_WRITE 0x00010DD9 + +/* Bitmask for the stream_perf_mode subfield. */ +#define ASM_BIT_MASK_STREAM_PERF_FLAG_PULL_MODE_WRITE 0xE0000000UL + +/* Bitmask for the stream_perf_mode subfield. */ +#define ASM_SHIFT_STREAM_PERF_FLAG_PULL_MODE_WRITE 29 + +#define ASM_STREAM_CMD_OPEN_PUSH_MODE_READ 0x00010DDA + +#define ASM_BIT_MASK_STREAM_PERF_FLAG_PUSH_MODE_READ 0xE0000000UL + +#define ASM_SHIFT_STREAM_PERF_FLAG_PUSH_MODE_READ 29 + +#define ASM_DATA_EVENT_WATERMARK 0x00010DDB + +struct asm_shared_position_buffer { + volatile uint32_t frame_counter; +/* Counter used to handle interprocessor synchronization issues. + * When frame_counter is 0: read_index, wall_clock_us_lsw, and + * wall_clock_us_msw are invalid. + * Supported values: >= 0. + */ + + volatile uint32_t index; +/* Index in bytes from where the aDSP is reading/writing. + * Supported values: 0 to circular buffer size - 1 + */ + + volatile uint32_t wall_clock_us_lsw; +/* Lower 32 bits of the 64-bit wall clock time in microseconds when the + * read index was updated. + * Supported values: >= 0 + */ + + volatile uint32_t wall_clock_us_msw; +/* Upper 32 bits of the 64 bit wall clock time in microseconds when the + * read index was updated + * Supported values: >= 0 + */ +} __packed; + +struct asm_shared_watermark_level { + uint32_t watermark_level_bytes; +} __packed; + +struct asm_stream_cmd_open_shared_io { + struct apr_hdr hdr; + uint32_t mode_flags; + uint16_t endpoint_type; + uint16_t topo_bits_per_sample; + uint32_t topo_id; + uint32_t fmt_id; + uint32_t shared_pos_buf_phy_addr_lsw; + uint32_t shared_pos_buf_phy_addr_msw; + uint16_t shared_pos_buf_mem_pool_id; + uint16_t shared_pos_buf_num_regions; + uint32_t shared_pos_buf_property_flag; + uint32_t shared_circ_buf_start_phy_addr_lsw; + uint32_t shared_circ_buf_start_phy_addr_msw; + uint32_t shared_circ_buf_size; + uint16_t shared_circ_buf_mem_pool_id; + uint16_t shared_circ_buf_num_regions; + uint32_t shared_circ_buf_property_flag; + uint32_t num_watermark_levels; + struct asm_multi_channel_pcm_fmt_blk_v3 fmt; + struct avs_shared_map_region_payload map_region_pos_buf; + struct avs_shared_map_region_payload map_region_circ_buf; + struct asm_shared_watermark_level watermark[0]; +} __packed; + +#define ASM_STREAM_CMD_OPEN_READ_V3 0x00010DB4 + +/* Definition of the timestamp type flag bitmask */ +#define ASM_BIT_MASKIMESTAMPYPE_FLAG (0x00000020UL) + +/* Definition of the timestamp type flag shift value. */ +#define ASM_SHIFTIMESTAMPYPE_FLAG 5 + +/* Relative timestamp is identified by this value.*/ +#define ASM_RELATIVEIMESTAMP 0 + +/* Absolute timestamp is identified by this value.*/ +#define ASM_ABSOLUTEIMESTAMP 1 + +/* Bit value for Low Latency Tx stream subfield */ +#define ASM_LOW_LATENCY_TX_STREAM_SESSION 1 + +/* Bit shift for the stream_perf_mode subfield. */ +#define ASM_SHIFT_STREAM_PERF_MODE_FLAG_IN_OPEN_READ 29 + +struct asm_stream_cmd_open_read_v3 { + struct apr_hdr hdr; + u32 mode_flags; +/* Mode flags that indicate whether meta information per encoded + * frame is to be provided. + * Supported values for bit 4: + * + * - 0 -- Return data buffer contains all encoded frames only; it + * does not contain frame metadata. + * + * - 1 -- Return data buffer contains an array of metadata and + * encoded frames. + * + * - Use #ASM_BIT_MASK_META_INFO_FLAG as the bitmask and + * #ASM_SHIFT_META_INFO_FLAG as the shift value for this bit. + * + * + * Supported values for bit 5: + * + * - ASM_RELATIVEIMESTAMP -- ASM_DATA_EVENT_READ_DONE_V2 will have + * - relative time-stamp. + * - ASM_ABSOLUTEIMESTAMP -- ASM_DATA_EVENT_READ_DONE_V2 will + * - have absolute time-stamp. + * + * - Use #ASM_BIT_MASKIMESTAMPYPE_FLAG as the bitmask and + * #ASM_SHIFTIMESTAMPYPE_FLAG as the shift value for this bit. + * + * All other bits are reserved; clients must set them to zero. + */ + + u32 src_endpointype; +/* Specifies the endpoint providing the input samples. + * Supported values: + * - 0 -- Device matrix + * - All other values are reserved; clients must set them to zero. + * Otherwise, an error is returned. + * The device matrix is the gateway from the tunneled Tx ports. + */ + + u32 preprocopo_id; +/* Specifies the topology (order of processing) of preprocessing + * algorithms. None means no preprocessing. + * Supported values: + * - #ASM_STREAM_PREPROCOPO_ID_DEFAULT + * - #ASM_STREAM_PREPROCOPO_ID_NONE + * + * This field can also be enabled through SetParams flags. + */ + + u32 enc_cfg_id; +/* Media configuration ID for encoded output. + * Supported values: + * - #ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V2 + * - #ASM_MEDIA_FMT_AAC_V2 + * - #ASM_MEDIA_FMT_AMRNB_FS + * - #ASM_MEDIA_FMT_AMRWB_FS + * - #ASM_MEDIA_FMT_V13K_FS + * - #ASM_MEDIA_FMT_EVRC_FS + * - #ASM_MEDIA_FMT_EVRCB_FS + * - #ASM_MEDIA_FMT_EVRCWB_FS + * - #ASM_MEDIA_FMT_SBC + * - #ASM_MEDIA_FMT_G711_ALAW_FS + * - #ASM_MEDIA_FMT_G711_MLAW_FS + * - #ASM_MEDIA_FMT_G729A_FS + * - #ASM_MEDIA_FMT_EXAMPLE + * - #ASM_MEDIA_FMT_WMA_V8 + */ + + u16 bits_per_sample; +/* Number of bits per sample processed by ASM modules. + * Supported values: 16 and 24 bits per sample + */ + + u16 reserved; +/* Reserved for future use. This field must be set to zero.*/ +} __packed; + +#define ASM_POPP_OUTPUT_SR_NATIVE_RATE 0 + +/* Enumeration for the maximum sampling rate at the POPP output.*/ +#define ASM_POPP_OUTPUT_SR_MAX_RATE 48000 + +#define ASM_STREAM_CMD_OPEN_READWRITE_V2 0x00010D8D +#define ASM_STREAM_CMD_OPEN_READWRITE_V2 0x00010D8D + +struct asm_stream_cmd_open_readwrite_v2 { + struct apr_hdr hdr; + u32 mode_flags; +/* Mode flags. + * Supported values for bit 2: + * - 0 -- SR/CM change notification event is disabled. + * - 1 -- SR/CM change notification event is enabled. Use + * #ASM_BIT_MASK_SR_CM_CHANGE_NOTIFY_FLAG and + * #ASM_SHIFT_SR_CM_CHANGE_NOTIFY_FLAG to set or + * getting this flag. + * + * Supported values for bit 4: + * - 0 -- Return read data buffer contains all encoded frames only; it + * does not contain frame metadata. + * - 1 -- Return read data buffer contains an array of metadata and + * encoded frames. + * + * All other bits are reserved; clients must set them to zero. + */ + + u32 postprocopo_id; +/* Specifies the topology (order of processing) of postprocessing + * algorithms. None means no postprocessing. + * + * Supported values: + * - #ASM_STREAM_POSTPROCOPO_ID_DEFAULT + * - #ASM_STREAM_POSTPROCOPO_ID_MCH_PEAK_VOL + * - #ASM_STREAM_POSTPROCOPO_ID_NONE + */ + + u32 dec_fmt_id; +/* Specifies the media type of the input data. PCM indicates that + * no decoding must be performed, e.g., this is an NT encoder + * session. + * Supported values: + * - #ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V2 + * - #ASM_MEDIA_FMT_ADPCM + * - #ASM_MEDIA_FMT_MP3 + * - #ASM_MEDIA_FMT_AAC_V2 + * - #ASM_MEDIA_FMT_DOLBY_AAC + * - #ASM_MEDIA_FMT_AMRNB_FS + * - #ASM_MEDIA_FMT_AMRWB_FS + * - #ASM_MEDIA_FMT_V13K_FS + * - #ASM_MEDIA_FMT_EVRC_FS + * - #ASM_MEDIA_FMT_EVRCB_FS + * - #ASM_MEDIA_FMT_EVRCWB_FS + * - #ASM_MEDIA_FMT_SBC + * - #ASM_MEDIA_FMT_WMA_V10PRO_V2 + * - #ASM_MEDIA_FMT_WMA_V9_V2 + * - #ASM_MEDIA_FMT_AMR_WB_PLUS_V2 + * - #ASM_MEDIA_FMT_AC3 + * - #ASM_MEDIA_FMT_G711_ALAW_FS + * - #ASM_MEDIA_FMT_G711_MLAW_FS + * - #ASM_MEDIA_FMT_G729A_FS + * - #ASM_MEDIA_FMT_EXAMPLE + */ + + u32 enc_cfg_id; +/* Specifies the media type for the output of the stream. PCM + * indicates that no encoding must be performed, e.g., this is an NT + * decoder session. + * Supported values: + * - #ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V2 + * - #ASM_MEDIA_FMT_AAC_V2 + * - #ASM_MEDIA_FMT_AMRNB_FS + * - #ASM_MEDIA_FMT_AMRWB_FS + * - #ASM_MEDIA_FMT_V13K_FS + * - #ASM_MEDIA_FMT_EVRC_FS + * - #ASM_MEDIA_FMT_EVRCB_FS + * - #ASM_MEDIA_FMT_EVRCWB_FS + * - #ASM_MEDIA_FMT_SBC + * - #ASM_MEDIA_FMT_G711_ALAW_FS + * - #ASM_MEDIA_FMT_G711_MLAW_FS + * - #ASM_MEDIA_FMT_G729A_FS + * - #ASM_MEDIA_FMT_EXAMPLE + * - #ASM_MEDIA_FMT_WMA_V8 + */ + + u16 bits_per_sample; +/* Number of bits per sample processed by ASM modules. + * Supported values: 16 and 24 bits per sample + */ + + u16 reserved; +/* Reserved for future use. This field must be set to zero.*/ + +} __packed; + +#define ASM_STREAM_CMD_OPEN_LOOPBACK_V2 0x00010D8E +struct asm_stream_cmd_open_loopback_v2 { + struct apr_hdr hdr; + u32 mode_flags; +/* Mode flags. + * Bit 0-31: reserved; client should set these bits to 0 + */ + u16 src_endpointype; + /* Endpoint type. 0 = Tx Matrix */ + u16 sink_endpointype; + /* Endpoint type. 0 = Rx Matrix */ + u32 postprocopo_id; +/* Postprocessor topology ID. Specifies the topology of + * postprocessing algorithms. + */ + + u16 bits_per_sample; +/* The number of bits per sample processed by ASM modules + * Supported values: 16 and 24 bits per sample + */ + u16 reserved; +/* Reserved for future use. This field must be set to zero. */ +} __packed; + + +#define ASM_STREAM_CMD_OPEN_TRANSCODE_LOOPBACK 0x00010DBA + +/* Bitmask for the stream's Performance mode. */ +#define ASM_BIT_MASK_STREAM_PERF_MODE_FLAG_IN_OPEN_TRANSCODE_LOOPBACK \ + (0x70000000UL) + +/* Bit shift for the stream's Performance mode. */ +#define ASM_SHIFT_STREAM_PERF_MODE_FLAG_IN_OPEN_TRANSCODE_LOOPBACK 28 + +/* Bitmask for the decoder converter enable flag. */ +#define ASM_BIT_MASK_DECODER_CONVERTER_FLAG (0x00000078UL) + +/* Shift value for the decoder converter enable flag. */ +#define ASM_SHIFT_DECODER_CONVERTER_FLAG 3 + +/* Converter mode is None (Default). */ +#define ASM_CONVERTER_MODE_NONE 0 + +/* Converter mode is DDP-to-DD. */ +#define ASM_DDP_DD_CONVERTER_MODE 1 + +/* Identifies a special converter mode where source and sink formats + * are the same but postprocessing must applied. Therefore, Decode + * @rarrow Re-encode is necessary. + */ +#define ASM_POST_PROCESS_CONVERTER_MODE 2 + + +struct asm_stream_cmd_open_transcode_loopback_t { + struct apr_hdr hdr; + u32 mode_flags; +/* Mode Flags specifies the performance mode in which this stream + * is to be opened. + * Supported values{for bits 30 to 28}(stream_perf_mode flag) + * + * #ASM_LEGACY_STREAM_SESSION -- This mode ensures backward + * compatibility to the original behavior + * of ASM_STREAM_CMD_OPEN_TRANSCODE_LOOPBACK + * + * #ASM_LOW_LATENCY_STREAM_SESSION -- Opens a loopback session by using + * shortened buffers in low latency POPP + * - Recommendation: Do not enable high latency algorithms. They might + * negate the benefits of opening a low latency stream, and they + * might also suffer quality degradation from unexpected jitter. + * - This Low Latency mode is supported only for PCM In and PCM Out + * loopbacks. An error is returned if Low Latency mode is opened for + * other transcode loopback modes. + * - To configure this subfield, use + * ASM_BIT_MASK_STREAM_PERF_MODE_FLAG_IN_OPEN_TRANSCODE_LOOPBACK and + * ASM_SHIFT_STREAM_PERF_MODE_FLAG_IN_OPEN_TRANSCODE_LOOPBACK. + * + * Supported values{for bits 6 to 3} (decoder-converter compatibility) + * #ASM_CONVERTER_MODE_NONE (0x0) -- Default + * #ASM_DDP_DD_CONVERTER_MODE (0x1) + * #ASM_POST_PROCESS_CONVERTER_MODE (0x2) + * 0x3-0xF -- Reserved for future use + * - Use #ASM_BIT_MASK_DECODER_CONVERTER_FLAG and + * ASM_SHIFT_DECODER_CONVERTER_FLAG to set this bit + * All other bits are reserved; clients must set them to 0. + */ + + u32 src_format_id; +/* Specifies the media format of the input audio stream. + * + * Supported values + * - #ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V2 + * - #ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V3 + * - #ASM_MEDIA_FMT_DTS + * - #ASM_MEDIA_FMT_EAC3_DEC + * - #ASM_MEDIA_FMT_EAC3 + * - #ASM_MEDIA_FMT_AC3_DEC + * - #ASM_MEDIA_FMT_AC3 + */ + u32 sink_format_id; +/* Specifies the media format of the output stream. + * + * Supported values + * - #ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V2 + * - #ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V3 + * - #ASM_MEDIA_FMT_DTS (not supported in Low Latency mode) + * - #ASM_MEDIA_FMT_EAC3_DEC (not supported in Low Latency mode) + * - #ASM_MEDIA_FMT_EAC3 (not supported in Low Latency mode) + * - #ASM_MEDIA_FMT_AC3_DEC (not supported in Low Latency mode) + * - #ASM_MEDIA_FMT_AC3 (not supported in Low Latency mode) + */ + + u32 audproc_topo_id; +/* Postprocessing topology ID, which specifies the topology (order of + * processing) of postprocessing algorithms. + * + * Supported values + * - #ASM_STREAM_POSTPROC_TOPO_ID_DEFAULT + * - #ASM_STREAM_POSTPROC_TOPO_ID_PEAKMETER + * - #ASM_STREAM_POSTPROC_TOPO_ID_MCH_PEAK_VOL + * - #ASM_STREAM_POSTPROC_TOPO_ID_NONE + * Topologies can be added through #ASM_CMD_ADD_TOPOLOGIES. + * This field is ignored for the Converter mode, in which no + * postprocessing is performed. + */ + + u16 src_endpoint_type; +/* Specifies the source endpoint that provides the input samples. + * + * Supported values + * - 0 -- Tx device matrix or stream router (gateway to the hardware + * ports) + * - All other values are reserved + * Clients must set this field to 0. Otherwise, an error is returned. + */ + + u16 sink_endpoint_type; +/* Specifies the sink endpoint type. + * + * Supported values + * - 0 -- Rx device matrix or stream router (gateway to the hardware + * ports) + * - All other values are reserved + * Clients must set this field to 0. Otherwise, an error is returned. + */ + + u16 bits_per_sample; +/* Number of bits per sample processed by the ASM modules. + * Supported values 16, 24 + */ + + u16 reserved; +/* This field must be set to 0. + */ +} __packed; + + +#define ASM_STREAM_CMD_CLOSE 0x00010BCD +#define ASM_STREAM_CMD_FLUSH 0x00010BCE + + +#define ASM_STREAM_CMD_FLUSH_READBUFS 0x00010C09 +#define ASM_STREAM_CMD_SET_PP_PARAMS_V2 0x00010DA1 +#define ASM_STREAM_CMD_SET_PP_PARAMS_V3 0x0001320D + +/* + * Structure for the ASM Stream Set PP Params command. Parameter data must be + * pre-packed with the correct header for either V2 or V3 when sent in-band. + * Use q6core_pack_pp_params to pack the header and data correctly depending on + * Instance ID support. + */ +struct asm_stream_cmd_set_pp_params { + /* APR Header */ + struct apr_hdr apr_hdr; + + /* The memory mapping header to be used when sending out of band */ + struct mem_mapping_hdr mem_hdr; + + /* The total size of the payload, including the parameter header */ + u32 payload_size; + + /* The parameter data to be filled when sent inband. Parameter data + * must be pre-packed with parameter header and then copied here. Use + * q6core_pack_pp_params to pack the header and param data correctly. + */ + u32 param_data[0]; +} __packed; + +#define ASM_STREAM_CMD_GET_PP_PARAMS_V2 0x00010DA2 +#define ASM_STREAM_CMD_GET_PP_PARAMS_V3 0x0001320E + +struct asm_stream_cmd_get_pp_params_v2 { + u32 data_payload_addr_lsw; + /* LSW of the parameter data payload address. */ + u32 data_payload_addr_msw; +/* MSW of the parameter data payload address. + * - Size of the shared memory, if specified, shall be large enough + * to contain the whole ParamData payload, including Module ID, + * Param ID, Param Size, and Param Values + * - Must be set to zero for in-band data + * - In the case of 32 bit Shared memory address, msw field must be + * set to zero. + * - In the case of 36 bit shared memory address, bit 31 to bit 4 of + * msw must be set to zero. + */ + + u32 mem_map_handle; +/* Supported Values: Any. + * memory map handle returned by DSP through ASM_CMD_SHARED_MEM_MAP_REGIONS + * command. + * if mmhandle is NULL, the ParamData payloads in the ACK are within the + * message payload (in-band). + * If mmhandle is non-NULL, the ParamData payloads in the ACK begin at the + * address specified in the address msw and lsw. + * (out-of-band). + */ + + u32 module_id; +/* Unique module ID. */ + + u32 param_id; +/* Unique parameter ID. */ + + u16 param_max_size; +/* Maximum data size of the module_id/param_id combination. This + * is a multiple of 4 bytes. + */ + + + u16 reserved; +/* Reserved for backward compatibility. Clients must set this + * field to zero. + */ +} __packed; + +#define ASM_STREAM_CMD_SET_ENCDEC_PARAM 0x00010C10 + +#define ASM_STREAM_CMD_SET_ENCDEC_PARAM_V2 0x00013218 + +struct asm_stream_cmd_set_encdec_param_v2 { + u16 service_id; + /* 0 - ASM_ENCODER_SVC; 1 - ASM_DECODER_SVC */ + + u16 reserved; + + u32 param_id; + /* ID of the parameter. */ + + u32 param_size; + /* + * Data size of this parameter, in bytes. The size is a multiple + * of 4 bytes. + */ +} __packed; + +#define ASM_STREAM_CMD_REGISTER_ENCDEC_EVENTS 0x00013219 + +#define ASM_STREAM_CMD_ENCDEC_EVENTS 0x0001321A + +#define AVS_PARAM_ID_RTIC_SHARED_MEMORY_ADDR 0x00013237 + +struct avs_rtic_shared_mem_addr { + struct apr_hdr hdr; + struct asm_stream_cmd_set_encdec_param_v2 encdec; + u32 shm_buf_addr_lsw; + /* Lower 32 bit of the RTIC shared memory */ + + u32 shm_buf_addr_msw; + /* Upper 32 bit of the RTIC shared memory */ + + u32 buf_size; + /* Size of buffer */ + + u16 shm_buf_mem_pool_id; + /* ADSP_MEMORY_MAP_SHMEM8_4K_POOL */ + + u16 shm_buf_num_regions; + /* number of regions to map */ + + u32 shm_buf_flag; + /* buffer property flag */ + + struct avs_shared_map_region_payload map_region; + /* memory map region*/ +} __packed; + +#define AVS_PARAM_ID_RTIC_EVENT_ACK 0x00013238 + +struct avs_param_rtic_event_ack { + struct apr_hdr hdr; + struct asm_stream_cmd_set_encdec_param_v2 encdec; +} __packed; + +#define ASM_PARAM_ID_ENCDEC_BITRATE 0x00010C13 + +struct asm_bitrate_param { + u32 bitrate; +/* Maximum supported bitrate. Only the AAC encoder is supported.*/ + +} __packed; + +#define ASM_PARAM_ID_ENCDEC_ENC_CFG_BLK_V2 0x00010DA3 +#define ASM_PARAM_ID_AAC_SBR_PS_FLAG 0x00010C63 + +/* Flag to turn off both SBR and PS processing, if they are + * present in the bitstream. + */ + +#define ASM_AAC_SBR_OFF_PS_OFF (2) + +/* Flag to turn on SBR but turn off PS processing,if they are + * present in the bitstream. + */ + +#define ASM_AAC_SBR_ON_PS_OFF (1) + +/* Flag to turn on both SBR and PS processing, if they are + * present in the bitstream (default behavior). + */ + + +#define ASM_AAC_SBR_ON_PS_ON (0) + +/* Structure for an AAC SBR PS processing flag. */ + +/* Payload of the #ASM_PARAM_ID_AAC_SBR_PS_FLAG parameter in the + * #ASM_STREAM_CMD_SET_ENCDEC_PARAM command. + */ +struct asm_aac_sbr_ps_flag_param { + struct apr_hdr hdr; + struct asm_stream_cmd_set_encdec_param encdec; + struct asm_enc_cfg_blk_param_v2 encblk; + + u32 sbr_ps_flag; +/* Control parameter to enable or disable SBR/PS processing in + * the AAC bitstream. Use the following macros to set this field: + * - #ASM_AAC_SBR_OFF_PS_OFF -- Turn off both SBR and PS + * processing, if they are present in the bitstream. + * - #ASM_AAC_SBR_ON_PS_OFF -- Turn on SBR processing, but not PS + * processing, if they are present in the bitstream. + * - #ASM_AAC_SBR_ON_PS_ON -- Turn on both SBR and PS processing, + * if they are present in the bitstream (default behavior). + * - All other values are invalid. + * Changes are applied to the next decoded frame. + */ +} __packed; + +#define ASM_PARAM_ID_AAC_DUAL_MONO_MAPPING 0x00010C64 + +/* First single channel element in a dual mono bitstream.*/ +#define ASM_AAC_DUAL_MONO_MAP_SCE_1 (1) + +/* Second single channel element in a dual mono bitstream.*/ +#define ASM_AAC_DUAL_MONO_MAP_SCE_2 (2) + +/* Structure for AAC decoder dual mono channel mapping. */ + + +struct asm_aac_dual_mono_mapping_param { + struct apr_hdr hdr; + struct asm_stream_cmd_set_encdec_param encdec; + u16 left_channel_sce; + u16 right_channel_sce; + +} __packed; + +#define ASM_STREAM_CMDRSP_GET_PP_PARAMS_V2 0x00010DA4 +#define ASM_STREAM_CMDRSP_GET_PP_PARAMS_V3 0x0001320F + +struct asm_stream_cmdrsp_get_pp_params_v2 { + u32 status; +} __packed; + +#define ASM_PARAM_ID_AC3_KARAOKE_MODE 0x00010D73 + +/* Enumeration for both vocals in a karaoke stream.*/ +#define AC3_KARAOKE_MODE_NO_VOCAL (0) + +/* Enumeration for only the left vocal in a karaoke stream.*/ +#define AC3_KARAOKE_MODE_LEFT_VOCAL (1) + +/* Enumeration for only the right vocal in a karaoke stream.*/ +#define AC3_KARAOKE_MODE_RIGHT_VOCAL (2) + +/* Enumeration for both vocal channels in a karaoke stream.*/ +#define AC3_KARAOKE_MODE_BOTH_VOCAL (3) +#define ASM_PARAM_ID_AC3_DRC_MODE 0x00010D74 +/* Enumeration for the Custom Analog mode.*/ +#define AC3_DRC_MODE_CUSTOM_ANALOG (0) + +/* Enumeration for the Custom Digital mode.*/ +#define AC3_DRC_MODE_CUSTOM_DIGITAL (1) +/* Enumeration for the Line Out mode (light compression).*/ +#define AC3_DRC_MODE_LINE_OUT (2) + +/* Enumeration for the RF remodulation mode (heavy compression).*/ +#define AC3_DRC_MODE_RF_REMOD (3) +#define ASM_PARAM_ID_AC3_DUAL_MONO_MODE 0x00010D75 + +/* Enumeration for playing dual mono in stereo mode.*/ +#define AC3_DUAL_MONO_MODE_STEREO (0) + +/* Enumeration for playing left mono.*/ +#define AC3_DUAL_MONO_MODE_LEFT_MONO (1) + +/* Enumeration for playing right mono.*/ +#define AC3_DUAL_MONO_MODE_RIGHT_MONO (2) + +/* Enumeration for mixing both dual mono channels and playing them.*/ +#define AC3_DUAL_MONO_MODE_MIXED_MONO (3) +#define ASM_PARAM_ID_AC3_STEREO_DOWNMIX_MODE 0x00010D76 + +/* Enumeration for using the Downmix mode indicated in the bitstream. */ + +#define AC3_STEREO_DOWNMIX_MODE_AUTO_DETECT (0) + +/* Enumeration for Surround Compatible mode (preserves the + * surround information). + */ + +#define AC3_STEREO_DOWNMIX_MODE_LT_RT (1) +/* Enumeration for Mono Compatible mode (if the output is to be + * further downmixed to mono). + */ + +#define AC3_STEREO_DOWNMIX_MODE_LO_RO (2) + +/* ID of the AC3 PCM scale factor parameter in the + * #ASM_STREAM_CMD_SET_ENCDEC_PARAM command. + */ +#define ASM_PARAM_ID_AC3_PCM_SCALEFACTOR 0x00010D78 + +/* ID of the AC3 DRC boost scale factor parameter in the + * #ASM_STREAM_CMD_SET_ENCDEC_PARAM command. + */ +#define ASM_PARAM_ID_AC3_DRC_BOOST_SCALEFACTOR 0x00010D79 + +/* ID of the AC3 DRC cut scale factor parameter in the + * #ASM_STREAM_CMD_SET_ENCDEC_PARAM command. + */ +#define ASM_PARAM_ID_AC3_DRC_CUT_SCALEFACTOR 0x00010D7A + +/* Structure for AC3 Generic Parameter. */ + +/* Payload of the AC3 parameters in the + * #ASM_STREAM_CMD_SET_ENCDEC_PARAM command. + */ +struct asm_ac3_generic_param { + struct apr_hdr hdr; + struct asm_stream_cmd_set_encdec_param encdec; + struct asm_enc_cfg_blk_param_v2 encblk; + u32 generic_parameter; +/* AC3 generic parameter. Select from one of the following + * possible values. + * + * For #ASM_PARAM_ID_AC3_KARAOKE_MODE, supported values are: + * - AC3_KARAOKE_MODE_NO_VOCAL + * - AC3_KARAOKE_MODE_LEFT_VOCAL + * - AC3_KARAOKE_MODE_RIGHT_VOCAL + * - AC3_KARAOKE_MODE_BOTH_VOCAL + * + * For #ASM_PARAM_ID_AC3_DRC_MODE, supported values are: + * - AC3_DRC_MODE_CUSTOM_ANALOG + * - AC3_DRC_MODE_CUSTOM_DIGITAL + * - AC3_DRC_MODE_LINE_OUT + * - AC3_DRC_MODE_RF_REMOD + * + * For #ASM_PARAM_ID_AC3_DUAL_MONO_MODE, supported values are: + * - AC3_DUAL_MONO_MODE_STEREO + * - AC3_DUAL_MONO_MODE_LEFT_MONO + * - AC3_DUAL_MONO_MODE_RIGHT_MONO + * - AC3_DUAL_MONO_MODE_MIXED_MONO + * + * For #ASM_PARAM_ID_AC3_STEREO_DOWNMIX_MODE, supported values are: + * - AC3_STEREO_DOWNMIX_MODE_AUTO_DETECT + * - AC3_STEREO_DOWNMIX_MODE_LT_RT + * - AC3_STEREO_DOWNMIX_MODE_LO_RO + * + * For #ASM_PARAM_ID_AC3_PCM_SCALEFACTOR, supported values are + * 0 to 1 in Q31 format. + * + * For #ASM_PARAM_ID_AC3_DRC_BOOST_SCALEFACTOR, supported values are + * 0 to 1 in Q31 format. + * + * For #ASM_PARAM_ID_AC3_DRC_CUT_SCALEFACTOR, supported values are + * 0 to 1 in Q31 format. + */ +} __packed; + +/* Enumeration for Raw mode (no downmixing), which specifies + * that all channels in the bitstream are to be played out as is + * without any downmixing. (Default) + */ + +#define WMAPRO_CHANNEL_MASK_RAW (-1) + +/* Enumeration for setting the channel mask to 0. The 7.1 mode + * (Home Theater) is assigned. + */ + + +#define WMAPRO_CHANNEL_MASK_ZERO 0x0000 + +/* Speaker layout mask for one channel (Home Theater, mono). + * - Speaker front center + */ +#define WMAPRO_CHANNEL_MASK_1_C 0x0004 + +/* Speaker layout mask for two channels (Home Theater, stereo). + * - Speaker front left + * - Speaker front right + */ +#define WMAPRO_CHANNEL_MASK_2_L_R 0x0003 + +/* Speaker layout mask for three channels (Home Theater). + * - Speaker front left + * - Speaker front right + * - Speaker front center + */ +#define WMAPRO_CHANNEL_MASK_3_L_C_R 0x0007 + +/* Speaker layout mask for two channels (stereo). + * - Speaker back left + * - Speaker back right + */ +#define WMAPRO_CHANNEL_MASK_2_Bl_Br 0x0030 + +/* Speaker layout mask for four channels. + * - Speaker front left + * - Speaker front right + * - Speaker back left + * - Speaker back right + */ +#define WMAPRO_CHANNEL_MASK_4_L_R_Bl_Br 0x0033 + +/* Speaker layout mask for four channels (Home Theater). + * - Speaker front left + * - Speaker front right + * - Speaker front center + * - Speaker back center + */ +#define WMAPRO_CHANNEL_MASK_4_L_R_C_Bc_HT 0x0107 +/* Speaker layout mask for five channels. + * - Speaker front left + * - Speaker front right + * - Speaker front center + * - Speaker back left + * - Speaker back right + */ +#define WMAPRO_CHANNEL_MASK_5_L_C_R_Bl_Br 0x0037 + +/* Speaker layout mask for five channels (5 mode, Home Theater). + * - Speaker front left + * - Speaker front right + * - Speaker front center + * - Speaker side left + * - Speaker side right + */ +#define WMAPRO_CHANNEL_MASK_5_L_C_R_Sl_Sr_HT 0x0607 +/* Speaker layout mask for six channels (5.1 mode). + * - Speaker front left + * - Speaker front right + * - Speaker front center + * - Speaker low frequency + * - Speaker back left + * - Speaker back right + */ +#define WMAPRO_CHANNEL_MASK_5DOT1_L_C_R_Bl_Br_SLF 0x003F +/* Speaker layout mask for six channels (5.1 mode, Home Theater). + * - Speaker front left + * - Speaker front right + * - Speaker front center + * - Speaker low frequency + * - Speaker side left + * - Speaker side right + */ +#define WMAPRO_CHANNEL_MASK_5DOT1_L_C_R_Sl_Sr_SLF_HT 0x060F +/* Speaker layout mask for six channels (5.1 mode, no LFE). + * - Speaker front left + * - Speaker front right + * - Speaker front center + * - Speaker back left + * - Speaker back right + * - Speaker back center + */ +#define WMAPRO_CHANNEL_MASK_5DOT1_L_C_R_Bl_Br_Bc 0x0137 +/* Speaker layout mask for six channels (5.1 mode, Home Theater, + * no LFE). + * - Speaker front left + * - Speaker front right + * - Speaker front center + * - Speaker back center + * - Speaker side left + * - Speaker side right + */ +#define WMAPRO_CHANNEL_MASK_5DOT1_L_C_R_Sl_Sr_Bc_HT 0x0707 + +/* Speaker layout mask for seven channels (6.1 mode). + * - Speaker front left + * - Speaker front right + * - Speaker front center + * - Speaker low frequency + * - Speaker back left + * - Speaker back right + * - Speaker back center + */ +#define WMAPRO_CHANNEL_MASK_6DOT1_L_C_R_Bl_Br_Bc_SLF 0x013F + +/* Speaker layout mask for seven channels (6.1 mode, Home + * Theater). + * - Speaker front left + * - Speaker front right + * - Speaker front center + * - Speaker low frequency + * - Speaker back center + * - Speaker side left + * - Speaker side right + */ +#define WMAPRO_CHANNEL_MASK_6DOT1_L_C_R_Sl_Sr_Bc_SLF_HT 0x070F + +/* Speaker layout mask for seven channels (6.1 mode, no LFE). + * - Speaker front left + * - Speaker front right + * - Speaker front center + * - Speaker back left + * - Speaker back right + * - Speaker front left of center + * - Speaker front right of center + */ +#define WMAPRO_CHANNEL_MASK_6DOT1_L_C_R_Bl_Br_SFLOC_SFROC 0x00F7 + +/* Speaker layout mask for seven channels (6.1 mode, Home + * Theater, no LFE). + * - Speaker front left + * - Speaker front right + * - Speaker front center + * - Speaker side left + * - Speaker side right + * - Speaker front left of center + * - Speaker front right of center + */ +#define WMAPRO_CHANNEL_MASK_6DOT1_L_C_R_Sl_Sr_SFLOC_SFROC_HT 0x0637 + +/* Speaker layout mask for eight channels (7.1 mode). + * - Speaker front left + * - Speaker front right + * - Speaker front center + * - Speaker back left + * - Speaker back right + * - Speaker low frequency + * - Speaker front left of center + * - Speaker front right of center + */ +#define WMAPRO_CHANNEL_MASK_7DOT1_L_C_R_Bl_Br_SLF_SFLOC_SFROC \ + 0x00FF + +/* Speaker layout mask for eight channels (7.1 mode, Home Theater). + * - Speaker front left + * - Speaker front right + * - Speaker front center + * - Speaker side left + * - Speaker side right + * - Speaker low frequency + * - Speaker front left of center + * - Speaker front right of center + * + */ +#define WMAPRO_CHANNEL_MASK_7DOT1_L_C_R_Sl_Sr_SLF_SFLOC_SFROC_HT \ + 0x063F + +#define ASM_PARAM_ID_DEC_OUTPUT_CHAN_MAP 0x00010D82 + +/* Maximum number of decoder output channels. */ +#define MAX_CHAN_MAP_CHANNELS 16 + +/* Structure for decoder output channel mapping. */ + +/* Payload of the #ASM_PARAM_ID_DEC_OUTPUT_CHAN_MAP parameter in the + * #ASM_STREAM_CMD_SET_ENCDEC_PARAM command. + */ +struct asm_dec_out_chan_map_param { + struct apr_hdr hdr; + struct asm_stream_cmd_set_encdec_param encdec; + u32 num_channels; +/* Number of decoder output channels. + * Supported values: 0 to #MAX_CHAN_MAP_CHANNELS + * + * A value of 0 indicates native channel mapping, which is valid + * only for NT mode. This means the output of the decoder is to be + * preserved as is. + */ + u8 channel_mapping[MAX_CHAN_MAP_CHANNELS]; +} __packed; + +#define ASM_STREAM_CMD_OPEN_WRITE_COMPRESSED 0x00010D84 + +/* Bitmask for the IEC 61937 enable flag.*/ +#define ASM_BIT_MASK_IEC_61937_STREAM_FLAG (0x00000001UL) + +/* Shift value for the IEC 61937 enable flag.*/ +#define ASM_SHIFT_IEC_61937_STREAM_FLAG 0 + +/* Bitmask for the IEC 60958 enable flag.*/ +#define ASM_BIT_MASK_IEC_60958_STREAM_FLAG (0x00000002UL) + +/* Shift value for the IEC 60958 enable flag.*/ +#define ASM_SHIFT_IEC_60958_STREAM_FLAG 1 + +/* Payload format for open write compressed command */ + +/* Payload format for the #ASM_STREAM_CMD_OPEN_WRITE_COMPRESSED + * command, which opens a stream for a given session ID and stream ID + * to be rendered in the compressed format. + */ + +struct asm_stream_cmd_open_write_compressed { + struct apr_hdr hdr; + u32 flags; +/* Mode flags that configure the stream for a specific format. + * Supported values: + * - Bit 0 -- IEC 61937 compatibility + * - 0 -- Stream is not in IEC 61937 format + * - 1 -- Stream is in IEC 61937 format + * - Bit 1 -- IEC 60958 compatibility + * - 0 -- Stream is not in IEC 60958 format + * - 1 -- Stream is in IEC 60958 format + * - Bits 2 to 31 -- 0 (Reserved) + * + * For the same stream, bit 0 cannot be set to 0 and bit 1 cannot + * be set to 1. A compressed stream connot have IEC 60958 + * packetization applied without IEC 61937 packetization. + * @note1hang Currently, IEC 60958 packetized input streams are not + * supported. + */ + + + u32 fmt_id; +/* Specifies the media type of the HDMI stream to be opened. + * Supported values: + * - #ASM_MEDIA_FMT_AC3 + * - #ASM_MEDIA_FMT_EAC3 + * - #ASM_MEDIA_FMT_DTS + * - #ASM_MEDIA_FMT_ATRAC + * - #ASM_MEDIA_FMT_MAT + * + * @note1hang This field must be set to a valid media type even if + * IEC 61937 packetization is not performed by the aDSP. + */ + +} __packed; + + +/* Indicates the number of samples per channel to be removed from the + * beginning of the stream. + */ +#define ASM_DATA_CMD_REMOVE_INITIAL_SILENCE 0x00010D67 + +/* Indicates the number of samples per channel to be removed from + * the end of the stream. + */ +#define ASM_DATA_CMD_REMOVE_TRAILING_SILENCE 0x00010D68 + +struct asm_data_cmd_remove_silence { + struct apr_hdr hdr; + u32 num_samples_to_remove; + /* < Number of samples per channel to be removed. + * @values 0 to (2@sscr{32}-1) + */ +} __packed; + +#define ASM_STREAM_CMD_OPEN_READ_COMPRESSED 0x00010D95 + +/* Bitmask for the IEC 61937 to 61937 pass-through capture. */ +#define ASM_BIT_MASK_IEC_61937_PASS_THROUGH_FLAG (0x00000001UL) + +/* Shift value for the IEC 61937 to 61937 pass-through capture. */ +#define ASM_SHIFT_IEC_61937_PASS_THROUGH_FLAG 0 + +struct asm_stream_cmd_open_read_compressed { + struct apr_hdr hdr; + u32 mode_flags; +/* Mode flags that indicate whether meta information per encoded + * frame is to be provided and packaging. + * Supported values for bit 0: (IEC 61937 pass-through mode) + * - 0 -- Unpack the IEC 61937 format stream to RAW compressed format + * - 1 -- Pass-through transfer of the IEC 61937 format stream + * - Use #ASM_BIT_MASK_IEC_61937_PASS_THROUGH_FLAG to set the bitmask + * and #ASM_SHIFT_IEC_61937_PASS_THROUGH_FLAG to set the shift value + * for this bit. + * Supported values for bit 4: + * - 0 -- Return data buffer contains all encoded frames only; it does + * not contain frame metadata. + * - 1 -- Return data buffer contains an array of metadata and encoded + * frames. + * - Use #ASM_BIT_MASK_META_INFO_FLAG to set the bitmask and + * #ASM_SHIFT_META_INFO_FLAG to set the shift value for this bit. + * All other bits are reserved; clients must set them to zero. + */ + + u32 frames_per_buf; +/* Indicates the number of frames that need to be returned per + * read buffer + * Supported values: should be greater than 0 for IEC to RAW compressed + * unpack mode. + * Value is don't care for IEC 61937 pass-through mode. + */ + +} __packed; + +/* adsp_asm_stream_commands.h*/ + + +/* adsp_asm_api.h (no changes)*/ +#define ASM_STREAM_POSTPROCOPO_ID_DEFAULT \ + 0x00010BE4 +#define ASM_STREAM_POSTPROCOPO_ID_PEAKMETER \ + 0x00010D83 +#define ASM_STREAM_POSTPROCOPO_ID_NONE \ + 0x00010C68 +#define ASM_STREAM_POSTPROCOPO_ID_MCH_PEAK_VOL \ + 0x00010D8B +#define ASM_STREAM_PREPROCOPO_ID_DEFAULT \ + ASM_STREAM_POSTPROCOPO_ID_DEFAULT +#define ASM_STREAM_PREPROCOPO_ID_NONE \ + ASM_STREAM_POSTPROCOPO_ID_NONE +#define ADM_CMD_COPP_OPENOPOLOGY_ID_NONE_AUDIO_COPP \ + 0x00010312 +#define ADM_CMD_COPP_OPENOPOLOGY_ID_SPEAKER_MONO_AUDIO_COPP \ + 0x00010313 +#define ADM_CMD_COPP_OPENOPOLOGY_ID_SPEAKER_STEREO_AUDIO_COPP \ + 0x00010314 +#define ADM_CMD_COPP_OPENOPOLOGY_ID_SPEAKER_STEREO_IIR_AUDIO_COPP\ + 0x00010704 +#define ADM_CMD_COPP_OPENOPOLOGY_ID_SPEAKER_MONO_AUDIO_COPP_MBDRCV2\ + 0x0001070D +#define ADM_CMD_COPP_OPENOPOLOGY_ID_SPEAKER_STEREO_AUDIO_COPP_MBDRCV2\ + 0x0001070E +#define ADM_CMD_COPP_OPENOPOLOGY_ID_SPEAKER_STEREO_IIR_AUDIO_COPP_MBDRCV2\ + 0x0001070F +#define ADM_CMD_COPP_OPENOPOLOGY_ID_SPEAKER_STEREO_AUDIO_COPP_MBDRC_V3 \ + 0x11000000 +#define ADM_CMD_COPP_OPENOPOLOGY_ID_SPEAKER_MCH_PEAK_VOL \ + 0x0001031B +#define ADM_CMD_COPP_OPENOPOLOGY_ID_MIC_MONO_AUDIO_COPP 0x00010315 +#define ADM_CMD_COPP_OPENOPOLOGY_ID_MIC_STEREO_AUDIO_COPP 0x00010316 +#define AUDPROC_COPPOPOLOGY_ID_MCHAN_IIR_AUDIO 0x00010715 +#define ADM_CMD_COPP_OPENOPOLOGY_ID_DEFAULT_AUDIO_COPP 0x00010BE3 +#define ADM_CMD_COPP_OPENOPOLOGY_ID_PEAKMETER_AUDIO_COPP 0x00010317 +#define AUDPROC_MODULE_ID_AIG 0x00010716 +#define AUDPROC_PARAM_ID_AIG_ENABLE 0x00010717 +#define AUDPROC_PARAM_ID_AIG_CONFIG 0x00010718 + +struct Audio_AigParam { + uint16_t mode; +/*< Mode word for enabling AIG/SIG mode . + * Byte offset: 0 + */ + int16_t staticGainL16Q12; +/*< Static input gain when aigMode is set to 1. + * Byte offset: 2 + */ + int16_t initialGainDBL16Q7; +/*= 0 for clock frequency to set @tablebulletend + */ + uint32_t clk_freq_in_hz; + + /* Use to specific divider for two clocks if needed. + * Set to Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO for no divider + * relation clocks + * @values + * - #Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO + * - #Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_DIVIDEND + * - #Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_DIVISOR @tablebulletend + */ + uint16_t clk_attri; + + /* + * Specifies the root clock source. + * Currently, only Q6AFE_LPASS_CLK_ROOT_DEFAULT is valid + * @values + * - 0 @tablebulletend + */ + uint16_t clk_root; + + /* + * for enable and disable clock. + * "clk_freq_in_hz", "clk_attri", and "clk_root" + * are ignored in disable clock case. + * @values  + * - 0 -- Disabled + * - 1 -- Enabled @tablebulletend + */ + uint32_t enable; +}; + +struct afe_clk_cfg { +/* Minor version used for tracking the version of the I2S + * configuration interface. + * Supported values: #AFE_API_VERSION_I2S_CONFIG + */ + u32 i2s_cfg_minor_version; + +/* clk value 1 in MHz. */ + u32 clk_val1; + +/* clk value 2 in MHz. */ + u32 clk_val2; + +/* clk_src + * #Q6AFE_LPASS_CLK_SRC_EXTERNAL + * #Q6AFE_LPASS_CLK_SRC_INTERNAL + */ + + u16 clk_src; + +/* clk_root -0 for default */ + u16 clk_root; + +/* clk_set_mode + * #Q6AFE_LPASS_MODE_BOTH_INVALID + * #Q6AFE_LPASS_MODE_CLK1_VALID + * #Q6AFE_LPASS_MODE_CLK2_VALID + * #Q6AFE_LPASS_MODE_BOTH_VALID + */ + u16 clk_set_mode; + +/* This param id is used to configure I2S clk */ + u16 reserved; +} __packed; + +/* This param id is used to configure I2S clk */ +#define AFE_PARAM_ID_LPAIF_CLK_CONFIG 0x00010238 +#define AFE_MODULE_CLOCK_SET 0x0001028F +#define AFE_PARAM_ID_CLOCK_SET 0x00010290 + +enum afe_lpass_digital_clk_src { + Q6AFE_LPASS_DIGITAL_ROOT_INVALID, + Q6AFE_LPASS_DIGITAL_ROOT_PRI_MI2S_OSR, + Q6AFE_LPASS_DIGITAL_ROOT_SEC_MI2S_OSR, + Q6AFE_LPASS_DIGITAL_ROOT_TER_MI2S_OSR, + Q6AFE_LPASS_DIGITAL_ROOT_QUAD_MI2S_OSR, + Q6AFE_LPASS_DIGITAL_ROOT_CDC_ROOT_CLK, +} __packed; + +/* This param id is used to configure internal clk */ +#define AFE_PARAM_ID_INTERNAL_DIGIATL_CDC_CLK_CONFIG 0x00010239 + +struct afe_digital_clk_cfg { +/* Minor version used for tracking the version of the I2S + * configuration interface. + * Supported values: #AFE_API_VERSION_I2S_CONFIG + */ + u32 i2s_cfg_minor_version; + +/* clk value in MHz. */ + u32 clk_val; + +/* INVALID + * PRI_MI2S_OSR + * SEC_MI2S_OSR + * TER_MI2S_OSR + * QUAD_MI2S_OSR + * DIGT_CDC_ROOT + */ + u16 clk_root; + +/* This field must be set to zero. */ + u16 reserved; +} __packed; + +/* + * Opcode for AFE to start DTMF. + */ +#define AFE_PORTS_CMD_DTMF_CTL 0x00010102 + +/** DTMF payload.*/ +struct afe_dtmf_generation_command { + struct apr_hdr hdr; + + /* + * Duration of the DTMF tone in ms. + * -1 -> continuous, + * 0 -> disable + */ + int64_t duration_in_ms; + + /* + * The DTMF high tone frequency. + */ + uint16_t high_freq; + + /* + * The DTMF low tone frequency. + */ + uint16_t low_freq; + + /* + * The DTMF volume setting + */ + uint16_t gain; + + /* + * The number of ports to enable/disable on. + */ + uint16_t num_ports; + + /* + * The Destination ports - array . + * For DTMF on multiple ports, portIds needs to + * be populated numPorts times. + */ + uint16_t port_ids; + + /* + * variable for 32 bit alignment of APR packet. + */ + uint16_t reserved; +} __packed; + +enum afe_config_type { + AFE_SLIMBUS_SLAVE_PORT_CONFIG, + AFE_SLIMBUS_SLAVE_CONFIG, + AFE_CDC_REGISTERS_CONFIG, + AFE_AANC_VERSION, + AFE_CDC_CLIP_REGISTERS_CONFIG, + AFE_CLIP_BANK_SEL, + AFE_CDC_REGISTER_PAGE_CONFIG, + AFE_MAX_CONFIG_TYPES, +}; + +struct afe_param_slimbus_slave_port_cfg { + uint32_t minor_version; + uint16_t slimbus_dev_id; + uint16_t slave_dev_pgd_la; + uint16_t slave_dev_intfdev_la; + uint16_t bit_width; + uint16_t data_format; + uint16_t num_channels; + uint16_t slave_port_mapping[AFE_PORT_MAX_AUDIO_CHAN_CNT]; +} __packed; + +struct afe_param_cdc_slimbus_slave_cfg { + uint32_t minor_version; + uint32_t device_enum_addr_lsw; + uint32_t device_enum_addr_msw; + uint16_t tx_slave_port_offset; + uint16_t rx_slave_port_offset; +} __packed; + +struct afe_param_cdc_reg_cfg { + uint32_t minor_version; + uint32_t reg_logical_addr; + uint32_t reg_field_type; + uint32_t reg_field_bit_mask; + uint16_t reg_bit_width; + uint16_t reg_offset_scale; +} __packed; + +#define AFE_API_VERSION_CDC_REG_PAGE_CFG 1 + +enum { + AFE_CDC_REG_PAGE_ASSIGN_PROC_ID_0 = 0, + AFE_CDC_REG_PAGE_ASSIGN_PROC_ID_1, + AFE_CDC_REG_PAGE_ASSIGN_PROC_ID_2, + AFE_CDC_REG_PAGE_ASSIGN_PROC_ID_3, +}; + +struct afe_param_cdc_reg_page_cfg { + uint32_t minor_version; + uint32_t enable; + uint32_t proc_id; +} __packed; + +struct afe_param_cdc_reg_cfg_data { + uint32_t num_registers; + struct afe_param_cdc_reg_cfg *reg_data; +} __packed; + +struct afe_svc_cmd_set_param_v1 { + /* APR Header */ + struct apr_hdr apr_hdr; + + /* The total size of the payload, including param_hdr_v3 */ + uint32_t payload_size; + + /* The memory mapping header to be used when sending outband */ + struct mem_mapping_hdr mem_hdr; + + /* The parameter data to be filled when sent inband */ + u32 param_data[0]; +} __packed; + +struct afe_svc_cmd_set_param_v2 { + /* APR Header */ + struct apr_hdr apr_hdr; + + /* The memory mapping header to be used when sending outband */ + struct mem_mapping_hdr mem_hdr; + + /* The total size of the payload, including param_hdr_v3 */ + u32 payload_size; + + /* The parameter data to be filled when sent inband */ + u32 param_data[0]; +} __packed; + +struct afe_param_hw_mad_ctrl { + uint32_t minor_version; + uint16_t mad_type; + uint16_t mad_enable; +} __packed; + +struct afe_port_cmd_set_aanc_acdb_table { + struct apr_hdr hdr; + struct mem_mapping_hdr mem_hdr; +} __packed; + +/* Dolby DAP topology */ +#define DOLBY_ADM_COPP_TOPOLOGY_ID 0x0001033B +#define DS2_ADM_COPP_TOPOLOGY_ID 0x1301033B + +/* RMS value from DSP */ +#define RMS_MODULEID_APPI_PASSTHRU 0x10009011 +#define RMS_PARAM_FIRST_SAMPLE 0x10009012 +#define RMS_PAYLOAD_LEN 4 + +/* Customized mixing in matix mixer */ +#define MTMX_MODULE_ID_DEFAULT_CHMIXER 0x00010341 +#define DEFAULT_CHMIXER_PARAM_ID_COEFF 0x00010342 +#define CUSTOM_STEREO_PAYLOAD_SIZE 9 +#define CUSTOM_STEREO_CMD_PARAM_SIZE 24 +#define CUSTOM_STEREO_NUM_OUT_CH 0x0002 +#define CUSTOM_STEREO_NUM_IN_CH 0x0002 +#define CUSTOM_STEREO_INDEX_PARAM 0x0002 +#define Q14_GAIN_ZERO_POINT_FIVE 0x2000 +#define Q14_GAIN_UNITY 0x4000 + +/* Ultrasound supported formats */ +#define US_POINT_EPOS_FORMAT_V2 0x0001272D +#define US_RAW_FORMAT_V2 0x0001272C +#define US_PROX_FORMAT_V4 0x0001273B +#define US_RAW_SYNC_FORMAT 0x0001272F +#define US_GES_SYNC_FORMAT 0x00012730 + +#define AFE_MODULE_GROUP_DEVICE 0x00010254 +#define AFE_PARAM_ID_GROUP_DEVICE_CFG 0x00010255 +#define AFE_PARAM_ID_GROUP_DEVICE_ENABLE 0x00010256 +#define AFE_GROUP_DEVICE_ID_SECONDARY_MI2S_RX 0x1102 + +/* Payload of the #AFE_PARAM_ID_GROUP_DEVICE_CFG + * parameter, which configures max of 8 AFE ports + * into a group. + * The fixed size of this structure is sixteen bytes. + */ +struct afe_group_device_group_cfg { + u32 minor_version; + u16 group_id; + u16 num_channels; + u16 port_id[8]; +} __packed; + +#define AFE_GROUP_DEVICE_ID_PRIMARY_TDM_RX \ + (AFE_PORT_ID_PRIMARY_TDM_RX + 0x100) +#define AFE_GROUP_DEVICE_ID_PRIMARY_TDM_TX \ + (AFE_PORT_ID_PRIMARY_TDM_TX + 0x100) +#define AFE_GROUP_DEVICE_ID_SECONDARY_TDM_RX \ + (AFE_PORT_ID_SECONDARY_TDM_RX + 0x100) +#define AFE_GROUP_DEVICE_ID_SECONDARY_TDM_TX \ + (AFE_PORT_ID_SECONDARY_TDM_TX + 0x100) +#define AFE_GROUP_DEVICE_ID_TERTIARY_TDM_RX \ + (AFE_PORT_ID_TERTIARY_TDM_RX + 0x100) +#define AFE_GROUP_DEVICE_ID_TERTIARY_TDM_TX \ + (AFE_PORT_ID_TERTIARY_TDM_TX + 0x100) +#define AFE_GROUP_DEVICE_ID_QUATERNARY_TDM_RX \ + (AFE_PORT_ID_QUATERNARY_TDM_RX + 0x100) +#define AFE_GROUP_DEVICE_ID_QUATERNARY_TDM_TX \ + (AFE_PORT_ID_QUATERNARY_TDM_TX + 0x100) +#define AFE_GROUP_DEVICE_ID_QUINARY_TDM_RX \ + (AFE_PORT_ID_QUINARY_TDM_RX + 0x100) +#define AFE_GROUP_DEVICE_ID_QUINARY_TDM_TX \ + (AFE_PORT_ID_QUINARY_TDM_TX + 0x100) + +/* ID of the parameter used by #AFE_MODULE_GROUP_DEVICE to configure the + * group device. #AFE_SVC_CMD_SET_PARAM can use this parameter ID. + * + * Requirements: + * - Configure the group before the member ports in the group are + * configured and started. + * - Enable the group only after it is configured. + * - Stop all member ports in the group before disabling the group. + */ +#define AFE_PARAM_ID_GROUP_DEVICE_TDM_CONFIG 0x0001029E + +/* Version information used to handle future additions to + * AFE_PARAM_ID_GROUP_DEVICE_TDM_CONFIG processing (for backward compatibility). + */ +#define AFE_API_VERSION_GROUP_DEVICE_TDM_CONFIG 0x1 + +/* Number of AFE ports in group device */ +#define AFE_GROUP_DEVICE_NUM_PORTS 8 + +/* Payload of the AFE_PARAM_ID_GROUP_DEVICE_TDM_CONFIG parameter ID + * used by AFE_MODULE_GROUP_DEVICE. + */ +struct afe_param_id_group_device_tdm_cfg { + u32 group_device_cfg_minor_version; + /* Minor version used to track group device configuration. + * @values #AFE_API_VERSION_GROUP_DEVICE_TDM_CONFIG + */ + + u16 group_id; + /* ID for the group device. + * @values + * - #AFE_GROUP_DEVICE_ID_PRIMARY_TDM_RX + * - #AFE_GROUP_DEVICE_ID_PRIMARY_TDM_TX + * - #AFE_GROUP_DEVICE_ID_SECONDARY_TDM_RX + * - #AFE_GROUP_DEVICE_ID_SECONDARY_TDM_TX + * - #AFE_GROUP_DEVICE_ID_TERTIARY_TDM_RX + * - #AFE_GROUP_DEVICE_ID_TERTIARY_TDM_TX + * - #AFE_GROUP_DEVICE_ID_QUATERNARY_TDM_RX + * - #AFE_GROUP_DEVICE_ID_QUATERNARY_TDM_TX + */ + + u16 reserved; + /* 0 */ + + u16 port_id[AFE_GROUP_DEVICE_NUM_PORTS]; + /* Array of member port IDs of this group. + * @values + * - #AFE_PORT_ID_PRIMARY_TDM_RX + * - #AFE_PORT_ID_PRIMARY_TDM_RX_1 + * - #AFE_PORT_ID_PRIMARY_TDM_RX_2 + * - #AFE_PORT_ID_PRIMARY_TDM_RX_3 + * - #AFE_PORT_ID_PRIMARY_TDM_RX_4 + * - #AFE_PORT_ID_PRIMARY_TDM_RX_5 + * - #AFE_PORT_ID_PRIMARY_TDM_RX_6 + * - #AFE_PORT_ID_PRIMARY_TDM_RX_7 + + * - #AFE_PORT_ID_PRIMARY_TDM_TX + * - #AFE_PORT_ID_PRIMARY_TDM_TX_1 + * - #AFE_PORT_ID_PRIMARY_TDM_TX_2 + * - #AFE_PORT_ID_PRIMARY_TDM_TX_3 + * - #AFE_PORT_ID_PRIMARY_TDM_TX_4 + * - #AFE_PORT_ID_PRIMARY_TDM_TX_5 + * - #AFE_PORT_ID_PRIMARY_TDM_TX_6 + * - #AFE_PORT_ID_PRIMARY_TDM_TX_7 + + * - #AFE_PORT_ID_SECONDARY_TDM_RX + * - #AFE_PORT_ID_SECONDARY_TDM_RX_1 + * - #AFE_PORT_ID_SECONDARY_TDM_RX_2 + * - #AFE_PORT_ID_SECONDARY_TDM_RX_3 + * - #AFE_PORT_ID_SECONDARY_TDM_RX_4 + * - #AFE_PORT_ID_SECONDARY_TDM_RX_5 + * - #AFE_PORT_ID_SECONDARY_TDM_RX_6 + * - #AFE_PORT_ID_SECONDARY_TDM_RX_7 + + * - #AFE_PORT_ID_SECONDARY_TDM_TX + * - #AFE_PORT_ID_SECONDARY_TDM_TX_1 + * - #AFE_PORT_ID_SECONDARY_TDM_TX_2 + * - #AFE_PORT_ID_SECONDARY_TDM_TX_3 + * - #AFE_PORT_ID_SECONDARY_TDM_TX_4 + * - #AFE_PORT_ID_SECONDARY_TDM_TX_5 + * - #AFE_PORT_ID_SECONDARY_TDM_TX_6 + * - #AFE_PORT_ID_SECONDARY_TDM_TX_7 + + * - #AFE_PORT_ID_TERTIARY_TDM_RX + * - #AFE_PORT_ID_TERTIARY_TDM_RX_1 + * - #AFE_PORT_ID_TERTIARY_TDM_RX_2 + * - #AFE_PORT_ID_TERTIARY_TDM_RX_3 + * - #AFE_PORT_ID_TERTIARY_TDM_RX_4 + * - #AFE_PORT_ID_TERTIARY_TDM_RX_5 + * - #AFE_PORT_ID_TERTIARY_TDM_RX_6 + * - #AFE_PORT_ID_TERTIARY_TDM_RX_7 + + * - #AFE_PORT_ID_TERTIARY_TDM_TX + * - #AFE_PORT_ID_TERTIARY_TDM_TX_1 + * - #AFE_PORT_ID_TERTIARY_TDM_TX_2 + * - #AFE_PORT_ID_TERTIARY_TDM_TX_3 + * - #AFE_PORT_ID_TERTIARY_TDM_TX_4 + * - #AFE_PORT_ID_TERTIARY_TDM_TX_5 + * - #AFE_PORT_ID_TERTIARY_TDM_TX_6 + * - #AFE_PORT_ID_TERTIARY_TDM_TX_7 + + * - #AFE_PORT_ID_QUATERNARY_TDM_RX + * - #AFE_PORT_ID_QUATERNARY_TDM_RX_1 + * - #AFE_PORT_ID_QUATERNARY_TDM_RX_2 + * - #AFE_PORT_ID_QUATERNARY_TDM_RX_3 + * - #AFE_PORT_ID_QUATERNARY_TDM_RX_4 + * - #AFE_PORT_ID_QUATERNARY_TDM_RX_5 + * - #AFE_PORT_ID_QUATERNARY_TDM_RX_6 + * - #AFE_PORT_ID_QUATERNARY_TDM_RX_7 + + * - #AFE_PORT_ID_QUATERNARY_TDM_TX + * - #AFE_PORT_ID_QUATERNARY_TDM_TX_1 + * - #AFE_PORT_ID_QUATERNARY_TDM_TX_2 + * - #AFE_PORT_ID_QUATERNARY_TDM_TX_3 + * - #AFE_PORT_ID_QUATERNARY_TDM_TX_4 + * - #AFE_PORT_ID_QUATERNARY_TDM_TX_5 + * - #AFE_PORT_ID_QUATERNARY_TDM_TX_6 + * - #AFE_PORT_ID_QUATERNARY_TDM_TX_7 + * @tablebulletend + */ + + u32 num_channels; + /* Number of enabled slots for TDM frame. + * @values 1 to 8 + */ + + u32 sample_rate; + /* Sampling rate of the port. + * @values + * - #AFE_PORT_SAMPLE_RATE_8K + * - #AFE_PORT_SAMPLE_RATE_16K + * - #AFE_PORT_SAMPLE_RATE_24K + * - #AFE_PORT_SAMPLE_RATE_32K + * - #AFE_PORT_SAMPLE_RATE_48K @tablebulletend + */ + + u32 bit_width; + /* Bit width of the sample. + * @values 16, 24, (32) + */ + + u16 nslots_per_frame; + /* Number of slots per frame. Typical : 1, 2, 4, 8, 16, 32. + * @values 1 - 32 + */ + + u16 slot_width; + /* Slot width of the slot in a TDM frame. (slot_width >= bit_width) + * have to be satisfied. + * @values 16, 24, 32 + */ + + u32 slot_mask; + /* Position of active slots. When that bit is set, that paricular + * slot is active. + * Number of active slots can be inferred by number of bits set in + * the mask. Only 8 individual bits can be enabled. + * Bits 0..31 corresponding to slot 0..31 + * @values 1 to 2^32 -1 + */ +} __packed; + +/* Payload of the #AFE_PARAM_ID_GROUP_DEVICE_ENABLE + * parameter, which enables or + * disables any module. + * The fixed size of this structure is four bytes. + */ + +struct afe_group_device_enable { + u16 group_id; + /* valid value is AFE_GROUP_DEVICE_ID_SECONDARY_MI2S_RX */ + u16 enable; + /* Enables (1) or disables (0) the module. */ +} __packed; + +union afe_port_group_config { + struct afe_group_device_group_cfg group_cfg; + struct afe_group_device_enable group_enable; + struct afe_param_id_group_device_tdm_cfg tdm_cfg; +} __packed; + +/* ID of the parameter used by #AFE_MODULE_AUDIO_DEV_INTERFACE to specify + * the timing statistics of the corresponding device interface. + * Client can periodically query for the device time statistics to help adjust + * the PLL based on the drift value. The get param command must be sent to + * AFE port ID corresponding to device interface + + * This parameter ID supports following get param commands: + * #AFE_PORT_CMD_GET_PARAM_V2 and + * #AFE_PORT_CMD_GET_PARAM_V3. + */ +#define AFE_PARAM_ID_DEV_TIMING_STATS 0x000102AD + +/* Version information used to handle future additions to AFE device + * interface timing statistics (for backward compatibility). + */ +#define AFE_API_VERSION_DEV_TIMING_STATS 0x1 + +/* Enumeration for specifying a sink(Rx) device */ +#define AFE_SINK_DEVICE 0x0 + +/* Enumeration for specifying a source(Tx) device */ +#define AFE_SOURCE_DEVICE 0x1 + +/* Enumeration for specifying the drift reference is of type AV Timer */ +#define AFE_REF_TIMER_TYPE_AVTIMER 0x0 + +/* Message payload structure for the + * AFE_PARAM_ID_DEV_TIMING_STATS parameter. + */ +struct afe_param_id_dev_timing_stats { + /* Minor version used to track the version of device interface timing + * statistics. Currently, the supported version is 1. + * @values #AFE_API_VERSION_DEV_TIMING_STATS + */ + u32 minor_version; + + /* Indicates the device interface direction as either + * source (Tx) or sink (Rx). + * @values + * #AFE_SINK_DEVICE + * #AFE_SOURCE_DEVICE + */ + u16 device_direction; + + /* Reference timer for drift accumulation and time stamp information. + * @values + * #AFE_REF_TIMER_TYPE_AVTIMER @tablebulletend + */ + u16 reference_timer; + + /* + * Flag to indicate if resync is required on the client side for + * drift correction. Flag is set to TRUE for the first get_param + * response after device interface starts. This flag value can be + * used by client to identify if device interface restart has + * happened and if any re-sync is required at their end for drift + * correction. + * @values + * 0: FALSE (Resync not required) + * 1: TRUE (Resync required) @tablebulletend + */ + u32 resync_flag; + + /* Accumulated drift value in microseconds. This value is updated + * every 100th ms. + * Positive drift value indicates AV timer is running faster than device + * Negative drift value indicates AV timer is running slower than device + * @values Any valid int32 number + */ + s32 acc_drift_value; + + /* Lower 32 bits of the 64-bit absolute timestamp of reference + * timer in microseconds. + + * This timestamp corresponds to the time when the drift values + * are accumlated for every 100th ms. + * @values Any valid uint32 number + */ + u32 ref_timer_abs_ts_lsw; + + /* Upper 32 bits of the 64-bit absolute timestamp of reference + * timer in microseconds. + * This timestamp corresponds to the time when the drift values + * are accumlated for every 100th ms. + * @values Any valid uint32 number + */ + u32 ref_timer_abs_ts_msw; +} __packed; + +struct afe_av_dev_drift_get_param_resp { + uint32_t status; + struct param_hdr_v3 pdata; + struct afe_param_id_dev_timing_stats timing_stats; +} __packed; + +/* Command for Matrix or Stream Router */ +#define ASM_SESSION_CMD_SET_MTMX_STRTR_PARAMS_V2 0x00010DCE +/* Module for AVSYNC */ +#define ASM_SESSION_MTMX_STRTR_MODULE_ID_AVSYNC 0x00010DC6 + +/* Parameter used by #ASM_SESSION_MTMX_STRTR_MODULE_ID_AVSYNC to specify the + * render window start value. This parameter is supported only for a Set + * command (not a Get command) in the Rx direction + * (#ASM_SESSION_CMD_SET_MTMX_STRTR_PARAMS_V2). + * Render window start is a value (session time minus timestamp, or ST-TS) + * below which frames are held, and after which frames are immediately + * rendered. + */ +#define ASM_SESSION_MTMX_STRTR_PARAM_RENDER_WINDOW_START_V2 0x00010DD1 + +/* Parameter used by #ASM_SESSION_MTMX_STRTR_MODULE_ID_AVSYNC to specify the + * render window end value. This parameter is supported only for a Set + * command (not a Get command) in the Rx direction + * (#ASM_SESSION_CMD_SET_MTMX_STRTR_PARAMS_V2). Render window end is a value + * (session time minus timestamp) above which frames are dropped, and below + * which frames are immediately rendered. + */ +#define ASM_SESSION_MTMX_STRTR_PARAM_RENDER_WINDOW_END_V2 0x00010DD2 + +/* Generic payload of the window parameters in the + * #ASM_SESSION_MTMX_STRTR_MODULE_ID_AVSYNC module. + * This payload is supported only for a Set command + * (not a Get command) on the Rx path. + */ +struct asm_session_mtmx_strtr_param_window_v2_t { + u32 window_lsw; + /* Lower 32 bits of the render window start value. */ + + u32 window_msw; + /* Upper 32 bits of the render window start value. + * + * The 64-bit number formed by window_lsw and window_msw specifies a + * signed 64-bit window value in microseconds. The sign extension is + * necessary. This value is used by the following parameter IDs: + * #ASM_SESSION_MTMX_STRTR_PARAM_RENDER_WINDOW_START_V2 + * #ASM_SESSION_MTMX_STRTR_PARAM_RENDER_WINDOW_END_V2 + * #ASM_SESSION_MTMX_STRTR_PARAM_STAT_WINDOW_START_V2 + * #ASM_SESSION_MTMX_STRTR_PARAM_STAT_WINDOW_END_V2 + * The value depends on which parameter ID is used. + * The aDSP honors the windows at a granularity of 1 ms. + */ +}; + +struct asm_session_cmd_set_mtmx_strstr_params_v2 { + uint32_t data_payload_addr_lsw; + /* Lower 32 bits of the 64-bit data payload address. */ + + uint32_t data_payload_addr_msw; + /* Upper 32 bits of the 64-bit data payload address. + * If the address is not sent (NULL), the message is in the payload. + * If the address is sent (non-NULL), the parameter data payloads + * begin at the specified address. + */ + + uint32_t mem_map_handle; + /* Unique identifier for an address. This memory map handle is returned + * by the aDSP through the #ASM_CMD_SHARED_MEM_MAP_REGIONS command. + * values + * - NULL -- Parameter data payloads are within the message payload + * (in-band). + * - Non-NULL -- Parameter data payloads begin at the address specified + * in the data_payload_addr_lsw and data_payload_addr_msw fields + * (out-of-band). + */ + + uint32_t data_payload_size; + /* Actual size of the variable payload accompanying the message, or in + * shared memory. This field is used for parsing the parameter payload. + * values > 0 bytes + */ + + uint32_t direction; + /* Direction of the entity (matrix mixer or stream router) on which + * the parameter is to be set. + * values + * - 0 -- Rx (for Rx stream router or Rx matrix mixer) + * - 1 -- Tx (for Tx stream router or Tx matrix mixer) + */ +}; + +/* Parameter used by #ASM_SESSION_MTMX_STRTR_MODULE_ID_AVSYNC which allows the + * audio client choose the rendering decision that the audio DSP should use. + */ +#define ASM_SESSION_MTMX_STRTR_PARAM_RENDER_MODE_CMD 0x00012F0D + +/* Indicates that rendering decision will be based on default rate + * (session clock based rendering, device driven). + * 1. The default session clock based rendering is inherently driven + * by the timing of the device. + * 2. After the initial decision is made (first buffer after a run + * command), subsequent data rendering decisions are made with + * respect to the rate at which the device is rendering, thus deriving + * its timing from the device. + * 3. While this decision making is simple, it has some inherent limitations + * (mentioned in the next section). + * 4. If this API is not set, the session clock based rendering will be assumed + * and this will ensure that the DSP is backward compatible. + */ +#define ASM_SESSION_MTMX_STRTR_PARAM_RENDER_DEFAULT 0 + +/* Indicates that rendering decision will be based on local clock rate. + * 1. In the DSP loopback/client loopback use cases (frame based + * inputs), the incoming data into audio DSP is time-stamped at the + * local clock rate (STC). + * 2. This TS rate may match the incoming data rate or maybe different + * from the incoming data rate. + * 3. Regardless, the data will be time-stamped with local STC and + * therefore, the client is recommended to set this mode for these + * use cases. This method is inherently more robust to sequencing + * (AFE Start/Stop) and device switches, among other benefits. + * 4. This API will inform the DSP to compare every incoming buffer TS + * against local STC. + * 5. DSP will continue to honor render windows APIs, as before. + */ +#define ASM_SESSION_MTMX_STRTR_PARAM_RENDER_LOCAL_STC 1 + +/* Structure for rendering decision parameter */ +struct asm_session_mtmx_strtr_param_render_mode_t { + /* Specifies the type of rendering decision the audio DSP should use. + * + * @values + * - #ASM_SESSION_MTMX_STRTR_PARAM_RENDER_DEFAULT + * - #ASM_SESSION_MTMX_STRTR_PARAM_RENDER_LOCAL_STC + */ + u32 flags; +} __packed; + +/* Parameter used by #ASM_SESSION_MTMX_STRTR_MODULE_ID_AVSYNC which allows the + * audio client to specify the clock recovery mechanism that the audio DSP + * should use. + */ + +#define ASM_SESSION_MTMX_STRTR_PARAM_CLK_REC_CMD 0x00012F0E + +/* Indicates that default clock recovery will be used (no clock recovery). + * If the client wishes that no clock recovery be done, the client can + * choose this. This means that no attempt will made by the DSP to try and + * match the rates of the input and output audio. + */ +#define ASM_SESSION_MTMX_STRTR_PARAM_CLK_REC_NONE 0 + +/* Indicates that independent clock recovery needs to be used. + * 1. In the DSP loopback/client loopback use cases (frame based inputs), + * the client should choose the independent clock recovery option. + * 2. This basically de-couples the audio and video from knowing each others + * clock sources and lets the audio DSP independently rate match the input + * and output rates. + * 3. After drift detection, the drift correction is achieved by either pulling + * the PLLs (if applicable) or by stream to device rate matching + * (for PCM use cases) by comparing drift with respect to STC. + * 4. For passthrough use cases, since the PLL pulling is the only option, + * a best effort will be made. + * If PLL pulling is not possible / available, the rendering will be + * done without rate matching. + */ +#define ASM_SESSION_MTMX_STRTR_PARAM_CLK_REC_AUTO 1 + +/* Payload of the #ASM_SESSION_MTMX_STRTR_PARAM_CLK_REC parameter. + */ +struct asm_session_mtmx_strtr_param_clk_rec_t { + /* Specifies the type of clock recovery that the audio DSP should + * use for rate matching. + */ + + /* @values + * #ASM_SESSION_MTMX_STRTR_PARAM_CLK_REC_DEFAULT + * #ASM_SESSION_MTMX_STRTR_PARAM_CLK_REC_INDEPENDENT + */ + u32 flags; +} __packed; + + +/* Parameter used by #ASM_SESSION_MTMX_STRTR_MODULE_ID_AVSYNC to + * realize smoother adjustment of audio session clock for a specified session. + * The desired audio session clock adjustment(in micro seconds) is specified + * using the command #ASM_SESSION_CMD_ADJUST_SESSION_CLOCK_V2. + * Delaying/Advancing the session clock would be implemented by inserting + * interpolated/dropping audio samples in the playback path respectively. + * Also, this parameter has to be configured before the Audio Session is put + * to RUN state to avoid cold start latency/glitches in the playback. + */ + +#define ASM_SESSION_MTMX_PARAM_ADJUST_SESSION_TIME_CTL 0x00013217 + +struct asm_session_mtmx_param_adjust_session_time_ctl_t { + /* Specifies whether the module is enabled or not + * @values + * 0 -- disabled + * 1 -- enabled + */ + u32 enable; +}; + +union asm_session_mtmx_strtr_param_config { + struct asm_session_mtmx_strtr_param_window_v2_t window_param; + struct asm_session_mtmx_strtr_param_render_mode_t render_param; + struct asm_session_mtmx_strtr_param_clk_rec_t clk_rec_param; + struct asm_session_mtmx_param_adjust_session_time_ctl_t adj_time_param; +} __packed; + +struct asm_mtmx_strtr_params { + struct apr_hdr hdr; + struct asm_session_cmd_set_mtmx_strstr_params_v2 param; + struct param_hdr_v1 data; + union asm_session_mtmx_strtr_param_config config; +} __packed; + +#define ASM_SESSION_CMD_GET_MTMX_STRTR_PARAMS_V2 0x00010DCF +#define ASM_SESSION_CMDRSP_GET_MTMX_STRTR_PARAMS_V2 0x00010DD0 + +#define ASM_SESSION_MTMX_STRTR_PARAM_SESSION_TIME_V3 0x00012F0B +#define ASM_SESSION_MTMX_STRTR_PARAM_STIME_TSTMP_FLG_BMASK (0x80000000UL) + +struct asm_session_cmd_get_mtmx_strstr_params_v2 { + uint32_t data_payload_addr_lsw; + /* Lower 32 bits of the 64-bit data payload address. */ + + uint32_t data_payload_addr_msw; + /* + * Upper 32 bits of the 64-bit data payload address. + * If the address is not sent (NULL), the message is in the payload. + * If the address is sent (non-NULL), the parameter data payloads + * begin at the specified address. + */ + + uint32_t mem_map_handle; + /* + * Unique identifier for an address. This memory map handle is returned + * by the aDSP through the #ASM_CMD_SHARED_MEM_MAP_REGIONS command. + * values + * - NULL -- Parameter data payloads are within the message payload + * (in-band). + * - Non-NULL -- Parameter data payloads begin at the address specified + * in the data_payload_addr_lsw and data_payload_addr_msw fields + * (out-of-band). + */ + uint32_t direction; + /* + * Direction of the entity (matrix mixer or stream router) on which + * the parameter is to be set. + * values + * - 0 -- Rx (for Rx stream router or Rx matrix mixer) + * - 1 -- Tx (for Tx stream router or Tx matrix mixer) + */ + uint32_t module_id; + /* Unique module ID. */ + + uint32_t param_id; + /* Unique parameter ID. */ + + uint32_t param_max_size; +}; + +struct asm_session_mtmx_strtr_param_session_time_v3_t { + uint32_t session_time_lsw; + /* Lower 32 bits of the current session time in microseconds */ + + uint32_t session_time_msw; + /* + * Upper 32 bits of the current session time in microseconds. + * The 64-bit number formed by session_time_lsw and session_time_msw + * is treated as signed. + */ + + uint32_t absolute_time_lsw; + /* + * Lower 32 bits of the 64-bit absolute time in microseconds. + * This is the time when the sample corresponding to the + * session_time_lsw is rendered to the hardware. This absolute + * time can be slightly in the future or past. + */ + + uint32_t absolute_time_msw; + /* + * Upper 32 bits of the 64-bit absolute time in microseconds. + * This is the time when the sample corresponding to the + * session_time_msw is rendered to hardware. This absolute + * time can be slightly in the future or past. The 64-bit number + * formed by absolute_time_lsw and absolute_time_msw is treated as + * unsigned. + */ + + uint32_t time_stamp_lsw; + /* Lower 32 bits of the last processed timestamp in microseconds */ + + uint32_t time_stamp_msw; + /* + * Upper 32 bits of the last processed timestamp in microseconds. + * The 64-bit number formed by time_stamp_lsw and time_stamp_lsw + * is treated as unsigned. + */ + + uint32_t flags; + /* + * Keeps track of any additional flags needed. + * @values{for bit 31} + * - 0 -- Uninitialized/invalid + * - 1 -- Valid + * All other bits are reserved; clients must set them to zero. + */ +}; + +union asm_session_mtmx_strtr_data_type { + struct asm_session_mtmx_strtr_param_session_time_v3_t session_time; +}; + +struct asm_mtmx_strtr_get_params { + struct apr_hdr hdr; + struct asm_session_cmd_get_mtmx_strstr_params_v2 param_info; +} __packed; + +struct asm_mtmx_strtr_get_params_cmdrsp { + uint32_t err_code; + struct param_hdr_v1 param_info; + union asm_session_mtmx_strtr_data_type param_data; +} __packed; + +#define AUDPROC_MODULE_ID_RESAMPLER 0x00010719 + +enum { + LEGACY_PCM = 0, + COMPRESSED_PASSTHROUGH, + COMPRESSED_PASSTHROUGH_CONVERT, + COMPRESSED_PASSTHROUGH_DSD, + LISTEN, + COMPRESSED_PASSTHROUGH_GEN, + COMPRESSED_PASSTHROUGH_IEC61937 +}; + +#define AUDPROC_MODULE_ID_COMPRESSED_MUTE 0x00010770 +#define AUDPROC_PARAM_ID_COMPRESSED_MUTE 0x00010771 + +struct adm_set_compressed_device_mute { + u32 mute_on; +} __packed; + +#define AUDPROC_MODULE_ID_COMPRESSED_LATENCY 0x0001076E +#define AUDPROC_PARAM_ID_COMPRESSED_LATENCY 0x0001076F + +struct adm_set_compressed_device_latency { + u32 latency; +} __packed; + +#define VOICEPROC_MODULE_ID_GENERIC_TX 0x00010EF6 +#define VOICEPROC_MODULE_ID_FLUENCE_PRO_VC_TX 0x00010F35 +#define VOICEPROC_PARAM_ID_FLUENCE_SOUNDFOCUS 0x00010E37 +#define VOICEPROC_PARAM_ID_FLUENCE_SOURCETRACKING 0x00010E38 +#define MAX_SECTORS 8 +#define MAX_NOISE_SOURCE_INDICATORS 3 +#define MAX_POLAR_ACTIVITY_INDICATORS 360 + +struct sound_focus_param { + uint16_t start_angle[MAX_SECTORS]; + uint8_t enable[MAX_SECTORS]; + uint16_t gain_step; +} __packed; + +struct source_tracking_param { + uint8_t vad[MAX_SECTORS]; + uint16_t doa_speech; + uint16_t doa_noise[MAX_NOISE_SOURCE_INDICATORS]; + uint8_t polar_activity[MAX_POLAR_ACTIVITY_INDICATORS]; +} __packed; + +struct adm_param_fluence_soundfocus_t { + uint16_t start_angles[MAX_SECTORS]; + uint8_t enables[MAX_SECTORS]; + uint16_t gain_step; + uint16_t reserved; +} __packed; + +struct adm_param_fluence_sourcetracking_t { + uint8_t vad[MAX_SECTORS]; + uint16_t doa_speech; + uint16_t doa_noise[MAX_NOISE_SOURCE_INDICATORS]; + uint8_t polar_activity[MAX_POLAR_ACTIVITY_INDICATORS]; +} __packed; + +#define AUDPROC_MODULE_ID_AUDIOSPHERE 0x00010916 +#define AUDPROC_PARAM_ID_AUDIOSPHERE_ENABLE 0x00010917 +#define AUDPROC_PARAM_ID_AUDIOSPHERE_STRENGTH 0x00010918 +#define AUDPROC_PARAM_ID_AUDIOSPHERE_CONFIG_MODE 0x00010919 + +#define AUDPROC_PARAM_ID_AUDIOSPHERE_COEFFS_STEREO_INPUT 0x0001091A +#define AUDPROC_PARAM_ID_AUDIOSPHERE_COEFFS_MULTICHANNEL_INPUT 0x0001091B +#define AUDPROC_PARAM_ID_AUDIOSPHERE_DESIGN_STEREO_INPUT 0x0001091C +#define AUDPROC_PARAM_ID_AUDIOSPHERE_DESIGN_MULTICHANNEL_INPUT 0x0001091D + +#define AUDPROC_PARAM_ID_AUDIOSPHERE_OPERATING_INPUT_MEDIA_INFO 0x0001091E + +#define AUDPROC_MODULE_ID_VOICE_TX_SECNS 0x10027059 +#define AUDPROC_PARAM_IDX_SEC_PRIMARY_MIC_CH 0x10014444 + +struct admx_sec_primary_mic_ch { + uint16_t version; + uint16_t reserved; + uint16_t sec_primary_mic_ch; + uint16_t reserved1; +} __packed; + +/** ID of the Voice Activity Detection (VAD) module, which is used to + * configure AFE VAD. + */ +#define AFE_MODULE_VAD 0x000102B9 + +/** ID of the parameter used by #AFE_MODULE_VAD to configure the VAD. + */ +#define AFE_PARAM_ID_VAD_CFG 0x000102BA + +#define AFE_API_VERSION_VAD_CFG 0x1 + +/* Payload of the AFE_PARAM_ID_VAD_CONFIG parameter used by + * AFE_MODULE_VAD. + */ +struct afe_param_id_vad_cfg_t { + uint32_t vad_cfg_minor_version; + /** Tracks the configuration of this parameter. + * Supported Values: #AFE_API_VERSION_VAD_CFG + */ + + uint32_t pre_roll_in_ms; + /** Pre-roll period in ms. + * Supported Values: 0x0 to 0x3E8 + */ +} __packed; + +#define AFE_PARAM_ID_VAD_CORE_CFG 0x000102BB + +#endif /*_APR_AUDIO_V2_H_ */ diff --git a/audio-kernel/include/dsp/audio_cal_utils.h b/audio-kernel/include/dsp/audio_cal_utils.h new file mode 100644 index 000000000..614ef2366 --- /dev/null +++ b/audio-kernel/include/dsp/audio_cal_utils.h @@ -0,0 +1,106 @@ +/* Copyright (c) 2014, 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_CAL_UTILS_H +#define _AUDIO_CAL_UTILS_H + +#include +#include +#include +#include + +struct cal_data { + size_t size; + void *kvaddr; + phys_addr_t paddr; +}; + +struct mem_map_data { + size_t map_size; + int32_t q6map_handle; + int32_t ion_map_handle; + struct dma_buf *dma_buf; +}; + +struct cal_block_data { + size_t client_info_size; + void *client_info; + void *cal_info; + struct list_head list; + struct cal_data cal_data; + bool cal_stale; + struct mem_map_data map_data; + int32_t buffer_number; +}; + +struct cal_util_callbacks { + int (*map_cal) + (int32_t cal_type, struct cal_block_data *cal_block); + int (*unmap_cal) + (int32_t cal_type, struct cal_block_data *cal_block); + bool (*match_block) + (struct cal_block_data *cal_block, void *user_data); +}; + +struct cal_type_info { + struct audio_cal_reg reg; + struct cal_util_callbacks cal_util_callbacks; +}; + +struct cal_type_data { + struct cal_type_info info; + struct mutex lock; + struct list_head cal_blocks; +}; + + +/* to register & degregister with cal util driver */ +int cal_utils_create_cal_types(int num_cal_types, + struct cal_type_data **cal_type, + struct cal_type_info *info); +void cal_utils_destroy_cal_types(int num_cal_types, + struct cal_type_data **cal_type); + +/* common functions for callbacks */ +int cal_utils_alloc_cal(size_t data_size, void *data, + struct cal_type_data *cal_type, + size_t client_info_size, void *client_info); +int cal_utils_dealloc_cal(size_t data_size, void *data, + struct cal_type_data *cal_type); +int cal_utils_set_cal(size_t data_size, void *data, + struct cal_type_data *cal_type, + size_t client_info_size, void *client_info); + +/* use for SSR */ +void cal_utils_clear_cal_block_q6maps(int num_cal_types, + struct cal_type_data **cal_type); + + +/* common matching functions used to add blocks */ +bool cal_utils_match_buf_num(struct cal_block_data *cal_block, + void *user_data); + +/* common matching functions to find cal blocks */ +struct cal_block_data *cal_utils_get_only_cal_block( + struct cal_type_data *cal_type); + +/* Size of calibration specific data */ +size_t get_cal_info_size(int32_t cal_type); +size_t get_user_cal_type_size(int32_t cal_type); + +/* Version of the cal type*/ +int32_t cal_utils_get_cal_type_version(void *cal_type_data); + +void cal_utils_mark_cal_used(struct cal_block_data *cal_block); + +bool cal_utils_is_cal_stale(struct cal_block_data *cal_block); +#endif diff --git a/audio-kernel/include/dsp/audio_calibration.h b/audio-kernel/include/dsp/audio_calibration.h new file mode 100644 index 000000000..5decff913 --- /dev/null +++ b/audio-kernel/include/dsp/audio_calibration.h @@ -0,0 +1,41 @@ +/* Copyright (c) 2014, 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 + * 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_CALIBRATION_H +#define _AUDIO_CALIBRATION_H + +#include + +/* Used by driver in buffer_number field to notify client + * To update all blocks, for example: freeing all memory + */ +#define ALL_CAL_BLOCKS -1 + + +struct audio_cal_callbacks { + int (*alloc)(int32_t cal_type, size_t data_size, void *data); + int (*dealloc)(int32_t cal_type, size_t data_size, void *data); + int (*pre_cal)(int32_t cal_type, size_t data_size, void *data); + int (*set_cal)(int32_t cal_type, size_t data_size, void *data); + int (*get_cal)(int32_t cal_type, size_t data_size, void *data); + int (*post_cal)(int32_t cal_type, size_t data_size, void *data); +}; + +struct audio_cal_reg { + int32_t cal_type; + struct audio_cal_callbacks callbacks; +}; + +int audio_cal_register(int num_cal_types, struct audio_cal_reg *reg_data); +int audio_cal_deregister(int num_cal_types, struct audio_cal_reg *reg_data); + +#endif diff --git a/audio-kernel/include/dsp/audio_notifier.h b/audio-kernel/include/dsp/audio_notifier.h new file mode 100644 index 000000000..7eda2c35c --- /dev/null +++ b/audio-kernel/include/dsp/audio_notifier.h @@ -0,0 +1,105 @@ +/* Copyright (c) 2016, 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_NOTIFIER_H_ +#define __AUDIO_NOTIFIER_H_ + +/* State of the notifier domain */ +enum { + AUDIO_NOTIFIER_SERVICE_DOWN, + AUDIO_NOTIFIER_SERVICE_UP +}; + +/* Service order determines connection priority + * Highest number connected first + */ +enum { + AUDIO_NOTIFIER_SSR_SERVICE, + AUDIO_NOTIFIER_PDR_SERVICE, + AUDIO_NOTIFIER_MAX_SERVICES +}; + +enum { + AUDIO_NOTIFIER_ADSP_DOMAIN, + AUDIO_NOTIFIER_MODEM_DOMAIN, + AUDIO_NOTIFIER_MAX_DOMAINS +}; + +/* Structure populated in void *data of nb function + * callback used for audio_notifier_register + */ +struct audio_notifier_cb_data { + int service; + int domain; +}; + +#if IS_ENABLED(CONFIG_MSM_QDSP6_NOTIFIER) + +/* + * Use audio_notifier_register to register any audio + * clients who need to be notified of a remote process. + * This API will determine and register the client with + * the best available subsystem (SSR or PDR) for that + * domain (Adsp or Modem). When an event is sent from that + * domain the notifier block callback function will be called. + * + * client_name - A unique user name defined by the client. + * If the same name is used for multiple calls each will + * be tracked & called back separately and a single call + * to deregister will delete them all. + * domain - Domain the client wants to get events from. + * AUDIO_NOTIFIER_ADSP_DOMAIN + * AUDIO_NOTIFIER_MODEM_DOMAIN + * *nb - Pointer to a notifier block. Provide a callback function + * to be notified of an even on that domain. + * + * nb_func(struct notifier_block *this, unsigned long opcode, void *data) + * this - pointer to own nb + * opcode - event from registered domain + * AUDIO_NOTIFIER_SERVICE_DOWN + * AUDIO_NOTIFIER_SERVICE_UP + * *data - pointer to struct audio_notifier_cb_data + * + * Returns: Success: 0 + * Error: -# + */ +int audio_notifier_register(char *client_name, int domain, + struct notifier_block *nb); + +/* + * Use audio_notifier_deregister to deregister the clients from + * all domains registered using audio_notifier_register that + * match the client name. + * + * client_name - Unique user name used in audio_notifier_register. + * Returns: Success: 0 + * Error: -# + */ +int audio_notifier_deregister(char *client_name); + +#else + +static inline int audio_notifier_register(char *client_name, int domain, + struct notifier_block *nb) +{ + return 0; +} + +static inline int audio_notifier_deregister(char *client_name) +{ + return 0; +} + +#endif /* CONFIG_MSM_QDSP6_NOTIFIER */ + +#endif diff --git a/audio-kernel/include/dsp/msm-audio-effects-q6-v2.h b/audio-kernel/include/dsp/msm-audio-effects-q6-v2.h new file mode 100644 index 000000000..6bc2338bc --- /dev/null +++ b/audio-kernel/include/dsp/msm-audio-effects-q6-v2.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2013-2016, 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 _MSM_AUDIO_EFFECTS_H +#define _MSM_AUDIO_EFFECTS_H + +#include + +#define MAX_PP_PARAMS_SZ 128 + +bool msm_audio_effects_is_effmodule_supp_in_top(int effect_module, + int topology); + +int msm_audio_effects_enable_extn(struct audio_client *ac, + struct msm_nt_eff_all_config *effects, + bool flag); + +int msm_audio_effects_reverb_handler(struct audio_client *ac, + struct reverb_params *reverb, + long *values); + +int msm_audio_effects_bass_boost_handler(struct audio_client *ac, + struct bass_boost_params *bass_boost, + long *values); + +int msm_audio_effects_pbe_handler(struct audio_client *ac, + struct pbe_params *pbe, + long *values); + +int msm_audio_effects_virtualizer_handler(struct audio_client *ac, + struct virtualizer_params *virtualizer, + long *values); + +int msm_audio_effects_popless_eq_handler(struct audio_client *ac, + struct eq_params *eq, + long *values); + +int msm_audio_effects_volume_handler(struct audio_client *ac, + struct soft_volume_params *vol, + long *values); + +int msm_audio_effects_volume_handler_v2(struct audio_client *ac, + struct soft_volume_params *vol, + long *values, int instance); +#endif /*_MSM_AUDIO_EFFECTS_H*/ diff --git a/audio-kernel/include/dsp/msm-audio-event-notify.h b/audio-kernel/include/dsp/msm-audio-event-notify.h new file mode 100644 index 000000000..fc47eff8a --- /dev/null +++ b/audio-kernel/include/dsp/msm-audio-event-notify.h @@ -0,0 +1,68 @@ +/* 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 __MSM_AUDIO_EVENT_NOTIFY_H_ +#define __MSM_AUDIO_EVENT_NOTIFY_H_ + +#include + +#if IS_ENABLED(CONFIG_SND_SOC_MSM_QDSP6V2_INTF) +int msm_aud_evt_register_client(struct notifier_block *nb); +int msm_aud_evt_unregister_client(struct notifier_block *nb); +int msm_aud_evt_notifier_call_chain(unsigned long val, void *v); +int msm_aud_evt_blocking_register_client(struct notifier_block *nb); +int msm_aud_evt_blocking_unregister_client(struct notifier_block *nb); +int msm_aud_evt_blocking_notifier_call_chain(unsigned long val, void *v); +#else +static inline int msm_aud_evt_register_client(struct notifier_block *nb) +{ + return -ENOSYS; +} + +static inline int msm_aud_evt_unregister_client(struct notifier_block *nb) +{ + return -ENOSYS; +} + +static inline int msm_aud_evt_notifier_call_chain(unsigned long val, void *v) +{ + return -ENOSYS; +} + +static inline int msm_aud_evt_blocking_register_client( + struct notifier_block *nb) +{ + return -ENOSYS; +} + +static inline int msm_aud_evt_blocking_unregister_client( + struct notifier_block *nb) +{ + return -ENOSYS; +} + +static inline int msm_aud_evt_blocking_notifier_call_chain( + unsigned long val, void *v) +{ + return -ENOSYS; +} +#endif + +enum { + MSM_AUD_DC_EVENT = 1, + SWR_WAKE_IRQ_REGISTER, + SWR_WAKE_IRQ_DEREGISTER, + SWR_WAKE_IRQ_EVENT, +}; + +#endif diff --git a/audio-kernel/include/dsp/msm-dts-srs-tm-config.h b/audio-kernel/include/dsp/msm-dts-srs-tm-config.h new file mode 100644 index 000000000..c00461b84 --- /dev/null +++ b/audio-kernel/include/dsp/msm-dts-srs-tm-config.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2012-2014, 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 + * 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 _MSM_DTS_SRS_TM_CONFIG_H_ +#define _MSM_DTS_SRS_TM_CONFIG_H_ + +#include +#include + +struct param_outband; + +#ifdef CONFIG_DTS_SRS_TM + +union srs_trumedia_params_u { + struct srs_trumedia_params srs_params; + __u16 raw_params[1]; +}; + +void msm_dts_srs_tm_ion_memmap(struct param_outband *po_); +void msm_dts_srs_tm_init(int port_id, int copp_idx); +void msm_dts_srs_tm_deinit(int port_id); +void msm_dts_srs_tm_add_controls(struct snd_soc_platform *platform); +#else +static inline void msm_dts_srs_tm_ion_memmap(struct param_outband *po_) { } +static inline void msm_dts_srs_tm_init(int port_id, int copp_idx) { } +static inline void msm_dts_srs_tm_deinit(int port_id) { } +static inline void msm_dts_srs_tm_add_controls( + struct snd_soc_platform *platform) { } + +#endif + +#endif diff --git a/audio-kernel/include/dsp/msm_audio_ion.h b/audio-kernel/include/dsp/msm_audio_ion.h new file mode 100644 index 000000000..01881cf69 --- /dev/null +++ b/audio-kernel/include/dsp/msm_audio_ion.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2013-2015, 2017-2019, 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 _LINUX_MSM_AUDIO_ION_H +#define _LINUX_MSM_AUDIO_ION_H +#include +#include +#include +#include + +enum { + MSM_AUDIO_ION_INV_CACHES = 0, + MSM_AUDIO_ION_CLEAN_CACHES, +}; + +int msm_audio_ion_alloc(struct dma_buf **dma_buf, size_t bufsz, + dma_addr_t *paddr, size_t *pa_len, void **vaddr); + +int msm_audio_ion_import(struct dma_buf **dma_buf, int fd, + unsigned long *ionflag, size_t bufsz, + dma_addr_t *paddr, size_t *pa_len, void **vaddr); +int msm_audio_ion_free(struct dma_buf *dma_buf); +int msm_audio_ion_mmap(struct audio_buffer *abuff, struct vm_area_struct *vma); +int msm_audio_ion_cache_operations(struct audio_buffer *abuff, int cache_op); + +u32 msm_audio_populate_upper_32_bits(dma_addr_t pa); +int msm_audio_ion_get_smmu_info(struct device **cb_dev, u64 *smmu_sid); + +int msm_audio_ion_dma_map(dma_addr_t *phys_addr, dma_addr_t *iova_base, + u32 size, enum dma_data_direction dir); +#endif /* _LINUX_MSM_AUDIO_ION_H */ diff --git a/audio-kernel/include/dsp/msm_mdf.h b/audio-kernel/include/dsp/msm_mdf.h new file mode 100644 index 000000000..5b27f1211 --- /dev/null +++ b/audio-kernel/include/dsp/msm_mdf.h @@ -0,0 +1,44 @@ +/* + * 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 _LINUX_MSM_MDF_H +#define _LINUX_MSM_MDF_H + +#ifdef CONFIG_MSM_MDF + +/** + * msm_mdf_mem_init - allocate and map memory to ADSP be shared + * across multiple remote DSPs. + */ +int msm_mdf_mem_init(void); + +/** + * msm_mdf_mem_init - unmap and free memory to ADSP. + */ +int msm_mdf_mem_deinit(void); + +#else + +static inline int msm_mdf_mem_init(void) +{ + return 0; +} + +static inline int msm_mdf_mem_deinit(void) +{ + return 0; +} + +#endif /* CONFIG_MSM_MDF */ + +#endif /* _LINUX_MSM_MDF_H */ diff --git a/audio-kernel/include/dsp/q6adm-v2.h b/audio-kernel/include/dsp/q6adm-v2.h new file mode 100644 index 000000000..bf3d0f048 --- /dev/null +++ b/audio-kernel/include/dsp/q6adm-v2.h @@ -0,0 +1,214 @@ +/* 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 + * 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 __Q6_ADM_V2_H__ +#define __Q6_ADM_V2_H__ + + +#define ADM_PATH_PLAYBACK 0x1 +#define ADM_PATH_LIVE_REC 0x2 +#define ADM_PATH_NONLIVE_REC 0x3 +#define ADM_PATH_COMPRESSED_RX 0x5 +#define ADM_PATH_COMPRESSED_TX 0x6 +#include +#include +#include + +#define MAX_MODULES_IN_TOPO 16 +#define ADM_GET_TOPO_MODULE_LIST_LENGTH\ + ((MAX_MODULES_IN_TOPO + 1) * sizeof(uint32_t)) +#define ADM_GET_TOPO_MODULE_INSTANCE_LIST_LENGTH \ + ((MAX_MODULES_IN_TOPO + 1) * 2 * sizeof(uint32_t)) +#define AUD_PROC_BLOCK_SIZE 4096 +#define AUD_VOL_BLOCK_SIZE 4096 +#define AUD_PROC_PERSIST_BLOCK_SIZE (2 * 1024 * 1020) +#define AUDIO_RX_CALIBRATION_SIZE (AUD_PROC_BLOCK_SIZE + \ + AUD_VOL_BLOCK_SIZE) +enum { + ADM_CUSTOM_TOP_CAL = 0, + ADM_AUDPROC_CAL, + ADM_LSM_AUDPROC_CAL, + ADM_AUDVOL_CAL, + ADM_RTAC_INFO_CAL, + ADM_RTAC_APR_CAL, + ADM_SRS_TRUMEDIA, + ADM_RTAC_AUDVOL_CAL, + ADM_LSM_AUDPROC_PERSISTENT_CAL, + ADM_MAX_CAL_TYPES +}; + +enum { + ADM_MEM_MAP_INDEX_SOURCE_TRACKING = ADM_MAX_CAL_TYPES, + ADM_MEM_MAP_INDEX_MAX +}; + +enum { + ADM_CLIENT_ID_DEFAULT = 0, + ADM_CLIENT_ID_SOURCE_TRACKING, + ADM_CLIENT_ID_MAX, +}; + +#define MAX_COPPS_PER_PORT 0x8 +#define ADM_MAX_CHANNELS 32 + +#define ADSP_ADM_API_VERSION_V3 3 + +/* multiple copp per stream. */ +struct route_payload { + unsigned int copp_idx[MAX_COPPS_PER_PORT]; + unsigned int port_id[MAX_COPPS_PER_PORT]; + int app_type[MAX_COPPS_PER_PORT]; + int acdb_dev_id[MAX_COPPS_PER_PORT]; + int sample_rate[MAX_COPPS_PER_PORT]; + unsigned short num_copps; + unsigned int session_id; +}; + +struct default_chmixer_param_id_coeff { + uint32_t index; + uint16_t num_output_channels; + uint16_t num_input_channels; +}; + +struct msm_pcm_channel_mixer { + int output_channel; + int input_channels[ADM_MAX_CHANNELS]; + bool enable; + int rule; + int channel_weight[ADM_MAX_CHANNELS][ADM_MAX_CHANNELS]; +}; + +int srs_trumedia_open(int port_id, int copp_idx, __s32 srs_tech_id, + void *srs_params); + +int adm_dts_eagle_set(int port_id, int copp_idx, int param_id, + void *data, uint32_t size); + +int adm_dts_eagle_get(int port_id, int copp_idx, int param_id, + void *data, uint32_t size); + +void adm_copp_mfc_cfg(int port_id, int copp_idx, int dst_sample_rate); + +int adm_get_pp_params(int port_id, int copp_idx, uint32_t client_id, + struct mem_mapping_hdr *mem_hdr, + struct param_hdr_v3 *param_hdr, u8 *returned_param_data); + +int adm_send_params_v5(int port_id, int copp_idx, char *params, + uint32_t params_length); + +int adm_set_pp_params(int port_id, int copp_idx, + struct mem_mapping_hdr *mem_hdr, u8 *param_data, + u32 params_size); + +int adm_pack_and_set_one_pp_param(int port_id, int copp_idx, + struct param_hdr_v3 param_hdr, + u8 *param_data); + +int adm_open(int port, int path, int rate, int mode, int topology, + int perf_mode, uint16_t bits_per_sample, + int app_type, int acdbdev_id); + +int adm_map_rtac_block(struct rtac_cal_block_data *cal_block); + +int adm_unmap_rtac_block(uint32_t *mem_map_handle); + +int adm_close(int port, int topology, int perf_mode); + +int adm_matrix_map(int path, struct route_payload payload_map, + int perf_mode, uint32_t passthr_mode); + +int adm_connect_afe_port(int mode, int session_id, int port_id); + +void adm_ec_ref_rx_id(int port_id); + +void adm_num_ec_ref_rx_chans(int num_chans); + +void adm_ec_ref_rx_bit_width(int bit_width); + +void adm_ec_ref_rx_sampling_rate(int sampling_rate); + +int adm_get_lowlatency_copp_id(int port_id); + +int adm_set_multi_ch_map(char *channel_map, int path); + +int adm_get_multi_ch_map(char *channel_map, int path); + +int adm_validate_and_get_port_index(int port_id); + +int adm_get_default_copp_idx(int port_id); + +int adm_get_topology_for_port_from_copp_id(int port_id, int copp_id); + +int adm_get_topology_for_port_copp_idx(int port_id, int copp_idx); + +int adm_get_indexes_from_copp_id(int copp_id, int *port_idx, int *copp_idx); + +int adm_set_stereo_to_custom_stereo(int port_id, int copp_idx, + unsigned int session_id, + char *params, uint32_t params_length); + +int adm_get_pp_topo_module_list(int port_id, int copp_idx, int32_t param_length, + char *params); + +int adm_get_pp_topo_module_list_v2(int port_id, int copp_idx, + int32_t param_length, + int32_t *returned_params); + +int adm_set_volume(int port_id, int copp_idx, int volume); + +int adm_set_softvolume(int port_id, int copp_idx, + struct audproc_softvolume_params *softvol_param); + +int adm_set_mic_gain(int port_id, int copp_idx, int volume); + +int adm_send_set_multichannel_ec_primary_mic_ch(int port_id, int copp_idx, + int primary_mic_ch); + +int adm_param_enable(int port_id, int copp_idx, int module_id, int enable); + +int adm_param_enable_v2(int port_id, int copp_idx, + struct module_instance_info mod_inst_info, int enable); + +int adm_send_calibration(int port_id, int copp_idx, int path, int perf_mode, + int cal_type, char *params, int size); + +int adm_set_wait_parameters(int port_id, int copp_idx); + +int adm_reset_wait_parameters(int port_id, int copp_idx); + +int adm_wait_timeout(int port_id, int copp_idx, int wait_time); + +int adm_store_cal_data(int port_id, int copp_idx, int path, int perf_mode, + int cal_type, char *params, int *size); + +int adm_send_compressed_device_mute(int port_id, int copp_idx, bool mute_on); + +int adm_send_compressed_device_latency(int port_id, int copp_idx, int latency); +int adm_set_sound_focus(int port_id, int copp_idx, + struct sound_focus_param soundFocusData); +int adm_get_sound_focus(int port_id, int copp_idx, + struct sound_focus_param *soundFocusData); +int adm_get_source_tracking(int port_id, int copp_idx, + struct source_tracking_param *sourceTrackingData); +int adm_set_custom_chmix_cfg(int port_id, int copp_idx, + unsigned int session_id, char *params, + uint32_t params_length, int direction, + int stream_type); +int adm_swap_speaker_channels(int port_id, int copp_idx, int sample_rate, + bool spk_swap); +int adm_programable_channel_mixer(int port_id, int copp_idx, int session_id, + int session_type, + struct msm_pcm_channel_mixer *ch_mixer, + int channel_index); +void msm_dts_srs_acquire_lock(void); +void msm_dts_srs_release_lock(void); +void adm_set_native_mode(int mode); +#endif /* __Q6_ADM_V2_H__ */ diff --git a/audio-kernel/include/dsp/q6afe-v2.h b/audio-kernel/include/dsp/q6afe-v2.h new file mode 100644 index 000000000..942e88269 --- /dev/null +++ b/audio-kernel/include/dsp/q6afe-v2.h @@ -0,0 +1,506 @@ +/* Copyright (c) 2012-2019, 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 __Q6AFE_V2_H__ +#define __Q6AFE_V2_H__ +#include +#include + +#define IN 0x000 +#define OUT 0x001 +#define MSM_AFE_MONO 0 +#define MSM_AFE_CH_STEREO 1 +#define MSM_AFE_MONO_RIGHT 1 +#define MSM_AFE_MONO_LEFT 2 +#define MSM_AFE_STEREO 3 +#define MSM_AFE_4CHANNELS 4 +#define MSM_AFE_6CHANNELS 6 +#define MSM_AFE_8CHANNELS 8 +#define MSM_AFE_10CHANNELS 10 +#define MSM_AFE_12CHANNELS 12 +#define MSM_AFE_14CHANNELS 14 +#define MSM_AFE_16CHANNELS 16 + +#define MSM_AFE_I2S_FORMAT_LPCM 0 +#define MSM_AFE_I2S_FORMAT_COMPR 1 +#define MSM_AFE_I2S_FORMAT_IEC60958_LPCM 2 +#define MSM_AFE_I2S_FORMAT_IEC60958_COMPR 3 + +#define MSM_AFE_PORT_TYPE_RX 0 +#define MSM_AFE_PORT_TYPE_TX 1 + +#define RT_PROXY_DAI_001_RX 0xE0 +#define RT_PROXY_DAI_001_TX 0xF0 +#define RT_PROXY_DAI_002_RX 0xF1 +#define RT_PROXY_DAI_002_TX 0xE1 +#define VIRTUAL_ID_TO_PORTID(val) ((val & 0xF) | 0x2000) + +#define AFE_CLK_VERSION_V1 1 +#define AFE_CLK_VERSION_V2 2 + +#define AFE_API_VERSION_SUPPORT_SPV3 2 +#define AFE_API_VERSION_V3 3 +/* for VAD and Island mode */ +#define AFE_API_VERSION_V4 4 + +typedef int (*routing_cb)(int port); + +enum { + /* IDX 0->4 */ + IDX_PRIMARY_I2S_RX, + IDX_PRIMARY_I2S_TX, + IDX_AFE_PORT_ID_PRIMARY_PCM_RX, + IDX_AFE_PORT_ID_PRIMARY_PCM_TX, + IDX_SECONDARY_I2S_RX, + /* IDX 5->9 */ + IDX_SECONDARY_I2S_TX, + IDX_MI2S_RX, + IDX_MI2S_TX, + IDX_HDMI_RX, + IDX_RSVD_2, + /* IDX 10->14 */ + IDX_RSVD_3, + IDX_DIGI_MIC_TX, + IDX_VOICE_RECORD_RX, + IDX_VOICE_RECORD_TX, + IDX_VOICE_PLAYBACK_TX, + /* IDX 15->19 */ + IDX_SLIMBUS_0_RX, + IDX_SLIMBUS_0_TX, + IDX_SLIMBUS_1_RX, + IDX_SLIMBUS_1_TX, + IDX_SLIMBUS_2_RX, + /* IDX 20->24 */ + IDX_SLIMBUS_2_TX, + IDX_SLIMBUS_3_RX, + IDX_SLIMBUS_3_TX, + IDX_SLIMBUS_4_RX, + IDX_SLIMBUS_4_TX, + /* IDX 25->29 */ + IDX_SLIMBUS_5_RX, + IDX_SLIMBUS_5_TX, + IDX_INT_BT_SCO_RX, + IDX_INT_BT_SCO_TX, + IDX_INT_BT_A2DP_RX, + /* IDX 30->34 */ + IDX_INT_FM_RX, + IDX_INT_FM_TX, + IDX_RT_PROXY_PORT_001_RX, + IDX_RT_PROXY_PORT_001_TX, + IDX_AFE_PORT_ID_QUATERNARY_MI2S_RX, + /* IDX 35->39 */ + IDX_AFE_PORT_ID_QUATERNARY_MI2S_TX, + IDX_AFE_PORT_ID_SECONDARY_MI2S_RX, + IDX_AFE_PORT_ID_SECONDARY_MI2S_TX, + IDX_AFE_PORT_ID_TERTIARY_MI2S_RX, + IDX_AFE_PORT_ID_TERTIARY_MI2S_TX, + /* IDX 40->44 */ + IDX_AFE_PORT_ID_PRIMARY_MI2S_RX, + IDX_AFE_PORT_ID_PRIMARY_MI2S_TX, + IDX_AFE_PORT_ID_SECONDARY_PCM_RX, + IDX_AFE_PORT_ID_SECONDARY_PCM_TX, + IDX_VOICE2_PLAYBACK_TX, + /* IDX 45->49 */ + IDX_SLIMBUS_6_RX, + IDX_SLIMBUS_6_TX, + IDX_PRIMARY_SPDIF_RX, + IDX_GLOBAL_CFG, + IDX_AUDIO_PORT_ID_I2S_RX, + /* IDX 50->53 */ + IDX_AFE_PORT_ID_SECONDARY_MI2S_RX_SD1, + IDX_AFE_PORT_ID_QUINARY_MI2S_RX, + IDX_AFE_PORT_ID_QUINARY_MI2S_TX, + IDX_AFE_PORT_ID_SENARY_MI2S_TX, + /* IDX 54->117 */ + IDX_AFE_PORT_ID_PRIMARY_TDM_RX_0, + IDX_AFE_PORT_ID_PRIMARY_TDM_TX_0, + IDX_AFE_PORT_ID_PRIMARY_TDM_RX_1, + IDX_AFE_PORT_ID_PRIMARY_TDM_TX_1, + IDX_AFE_PORT_ID_PRIMARY_TDM_RX_2, + IDX_AFE_PORT_ID_PRIMARY_TDM_TX_2, + IDX_AFE_PORT_ID_PRIMARY_TDM_RX_3, + IDX_AFE_PORT_ID_PRIMARY_TDM_TX_3, + IDX_AFE_PORT_ID_PRIMARY_TDM_RX_4, + IDX_AFE_PORT_ID_PRIMARY_TDM_TX_4, + IDX_AFE_PORT_ID_PRIMARY_TDM_RX_5, + IDX_AFE_PORT_ID_PRIMARY_TDM_TX_5, + IDX_AFE_PORT_ID_PRIMARY_TDM_RX_6, + IDX_AFE_PORT_ID_PRIMARY_TDM_TX_6, + IDX_AFE_PORT_ID_PRIMARY_TDM_RX_7, + IDX_AFE_PORT_ID_PRIMARY_TDM_TX_7, + IDX_AFE_PORT_ID_SECONDARY_TDM_RX_0, + IDX_AFE_PORT_ID_SECONDARY_TDM_TX_0, + IDX_AFE_PORT_ID_SECONDARY_TDM_RX_1, + IDX_AFE_PORT_ID_SECONDARY_TDM_TX_1, + IDX_AFE_PORT_ID_SECONDARY_TDM_RX_2, + IDX_AFE_PORT_ID_SECONDARY_TDM_TX_2, + IDX_AFE_PORT_ID_SECONDARY_TDM_RX_3, + IDX_AFE_PORT_ID_SECONDARY_TDM_TX_3, + IDX_AFE_PORT_ID_SECONDARY_TDM_RX_4, + IDX_AFE_PORT_ID_SECONDARY_TDM_TX_4, + IDX_AFE_PORT_ID_SECONDARY_TDM_RX_5, + IDX_AFE_PORT_ID_SECONDARY_TDM_TX_5, + IDX_AFE_PORT_ID_SECONDARY_TDM_RX_6, + IDX_AFE_PORT_ID_SECONDARY_TDM_TX_6, + IDX_AFE_PORT_ID_SECONDARY_TDM_RX_7, + IDX_AFE_PORT_ID_SECONDARY_TDM_TX_7, + IDX_AFE_PORT_ID_TERTIARY_TDM_RX_0, + IDX_AFE_PORT_ID_TERTIARY_TDM_TX_0, + IDX_AFE_PORT_ID_TERTIARY_TDM_RX_1, + IDX_AFE_PORT_ID_TERTIARY_TDM_TX_1, + IDX_AFE_PORT_ID_TERTIARY_TDM_RX_2, + IDX_AFE_PORT_ID_TERTIARY_TDM_TX_2, + IDX_AFE_PORT_ID_TERTIARY_TDM_RX_3, + IDX_AFE_PORT_ID_TERTIARY_TDM_TX_3, + IDX_AFE_PORT_ID_TERTIARY_TDM_RX_4, + IDX_AFE_PORT_ID_TERTIARY_TDM_TX_4, + IDX_AFE_PORT_ID_TERTIARY_TDM_RX_5, + IDX_AFE_PORT_ID_TERTIARY_TDM_TX_5, + IDX_AFE_PORT_ID_TERTIARY_TDM_RX_6, + IDX_AFE_PORT_ID_TERTIARY_TDM_TX_6, + IDX_AFE_PORT_ID_TERTIARY_TDM_RX_7, + IDX_AFE_PORT_ID_TERTIARY_TDM_TX_7, + IDX_AFE_PORT_ID_QUATERNARY_TDM_RX_0, + IDX_AFE_PORT_ID_QUATERNARY_TDM_TX_0, + IDX_AFE_PORT_ID_QUATERNARY_TDM_RX_1, + IDX_AFE_PORT_ID_QUATERNARY_TDM_TX_1, + IDX_AFE_PORT_ID_QUATERNARY_TDM_RX_2, + IDX_AFE_PORT_ID_QUATERNARY_TDM_TX_2, + IDX_AFE_PORT_ID_QUATERNARY_TDM_RX_3, + IDX_AFE_PORT_ID_QUATERNARY_TDM_TX_3, + IDX_AFE_PORT_ID_QUATERNARY_TDM_RX_4, + IDX_AFE_PORT_ID_QUATERNARY_TDM_TX_4, + IDX_AFE_PORT_ID_QUATERNARY_TDM_RX_5, + IDX_AFE_PORT_ID_QUATERNARY_TDM_TX_5, + IDX_AFE_PORT_ID_QUATERNARY_TDM_RX_6, + IDX_AFE_PORT_ID_QUATERNARY_TDM_TX_6, + IDX_AFE_PORT_ID_QUATERNARY_TDM_RX_7, + IDX_AFE_PORT_ID_QUATERNARY_TDM_TX_7, + /* IDX 118->121 */ + IDX_SLIMBUS_7_RX, + IDX_SLIMBUS_7_TX, + IDX_SLIMBUS_8_RX, + IDX_SLIMBUS_8_TX, + /* IDX 122-> 123 */ + IDX_AFE_PORT_ID_USB_RX, + IDX_AFE_PORT_ID_USB_TX, + /* IDX 124 */ + IDX_DISPLAY_PORT_RX, + /* IDX 125-> 128 */ + IDX_AFE_PORT_ID_TERTIARY_PCM_RX, + IDX_AFE_PORT_ID_TERTIARY_PCM_TX, + IDX_AFE_PORT_ID_QUATERNARY_PCM_RX, + IDX_AFE_PORT_ID_QUATERNARY_PCM_TX, + /* IDX 129-> 142 */ + IDX_AFE_PORT_ID_INT0_MI2S_RX, + IDX_AFE_PORT_ID_INT0_MI2S_TX, + IDX_AFE_PORT_ID_INT1_MI2S_RX, + IDX_AFE_PORT_ID_INT1_MI2S_TX, + IDX_AFE_PORT_ID_INT2_MI2S_RX, + IDX_AFE_PORT_ID_INT2_MI2S_TX, + IDX_AFE_PORT_ID_INT3_MI2S_RX, + IDX_AFE_PORT_ID_INT3_MI2S_TX, + IDX_AFE_PORT_ID_INT4_MI2S_RX, + IDX_AFE_PORT_ID_INT4_MI2S_TX, + IDX_AFE_PORT_ID_INT5_MI2S_RX, + IDX_AFE_PORT_ID_INT5_MI2S_TX, + IDX_AFE_PORT_ID_INT6_MI2S_RX, + IDX_AFE_PORT_ID_INT6_MI2S_TX, + /* IDX 143-> 160 */ + IDX_AFE_PORT_ID_QUINARY_PCM_RX, + IDX_AFE_PORT_ID_QUINARY_PCM_TX, + IDX_AFE_PORT_ID_QUINARY_TDM_RX_0, + IDX_AFE_PORT_ID_QUINARY_TDM_TX_0, + IDX_AFE_PORT_ID_QUINARY_TDM_RX_1, + IDX_AFE_PORT_ID_QUINARY_TDM_TX_1, + IDX_AFE_PORT_ID_QUINARY_TDM_RX_2, + IDX_AFE_PORT_ID_QUINARY_TDM_TX_2, + IDX_AFE_PORT_ID_QUINARY_TDM_RX_3, + IDX_AFE_PORT_ID_QUINARY_TDM_TX_3, + IDX_AFE_PORT_ID_QUINARY_TDM_RX_4, + IDX_AFE_PORT_ID_QUINARY_TDM_TX_4, + IDX_AFE_PORT_ID_QUINARY_TDM_RX_5, + IDX_AFE_PORT_ID_QUINARY_TDM_TX_5, + IDX_AFE_PORT_ID_QUINARY_TDM_RX_6, + IDX_AFE_PORT_ID_QUINARY_TDM_TX_6, + IDX_AFE_PORT_ID_QUINARY_TDM_RX_7, + IDX_AFE_PORT_ID_QUINARY_TDM_TX_7, + /* IDX 161 to 181 */ + IDX_AFE_PORT_ID_WSA_CODEC_DMA_RX_0, + IDX_AFE_PORT_ID_WSA_CODEC_DMA_TX_0, + IDX_AFE_PORT_ID_WSA_CODEC_DMA_RX_1, + IDX_AFE_PORT_ID_WSA_CODEC_DMA_TX_1, + IDX_AFE_PORT_ID_WSA_CODEC_DMA_TX_2, + IDX_AFE_PORT_ID_VA_CODEC_DMA_TX_0, + IDX_AFE_PORT_ID_VA_CODEC_DMA_TX_1, + IDX_AFE_PORT_ID_RX_CODEC_DMA_RX_0, + IDX_AFE_PORT_ID_TX_CODEC_DMA_TX_0, + IDX_AFE_PORT_ID_RX_CODEC_DMA_RX_1, + IDX_AFE_PORT_ID_TX_CODEC_DMA_TX_1, + IDX_AFE_PORT_ID_RX_CODEC_DMA_RX_2, + IDX_AFE_PORT_ID_TX_CODEC_DMA_TX_2, + IDX_AFE_PORT_ID_RX_CODEC_DMA_RX_3, + IDX_AFE_PORT_ID_TX_CODEC_DMA_TX_3, + IDX_AFE_PORT_ID_RX_CODEC_DMA_RX_4, + IDX_AFE_PORT_ID_TX_CODEC_DMA_TX_4, + IDX_AFE_PORT_ID_RX_CODEC_DMA_RX_5, + IDX_AFE_PORT_ID_TX_CODEC_DMA_TX_5, + IDX_AFE_PORT_ID_RX_CODEC_DMA_RX_6, + IDX_AFE_PORT_ID_RX_CODEC_DMA_RX_7, + /* IDX 182 to 184 */ + IDX_SECONDARY_SPDIF_RX, + IDX_PRIMARY_SPDIF_TX, + IDX_SECONDARY_SPDIF_TX, + /* IDX 185 to 186 */ + IDX_SLIMBUS_9_RX, + IDX_SLIMBUS_9_TX, + /* IDX 187 -> 189 */ + IDX_AFE_PORT_ID_SENARY_PCM_RX, + IDX_AFE_PORT_ID_SENARY_PCM_TX, + AFE_MAX_PORTS +}; + +enum afe_mad_type { + MAD_HW_NONE = 0x00, + MAD_HW_AUDIO = 0x01, + MAD_HW_BEACON = 0x02, + MAD_HW_ULTRASOUND = 0x04, + MAD_SW_AUDIO = 0x05, +}; + +enum afe_cal_mode { + AFE_CAL_MODE_DEFAULT = 0x00, + AFE_CAL_MODE_NONE, +}; + +enum afe_vad_cfg_type { + AFE_VAD_ENABLE = 0x00, + AFE_VAD_PREROLL, +}; + +struct vad_config { + u32 is_enable; + u32 pre_roll; +}; + +struct afe_audio_buffer { + dma_addr_t phys; + void *data; + uint32_t used; + uint32_t size;/* size of buffer */ + uint32_t actual_size; /* actual number of bytes read by DSP */ + struct dma_buf *dma_buf; +}; + +struct afe_audio_port_data { + struct afe_audio_buffer *buf; + uint32_t max_buf_cnt; + uint32_t dsp_buf; + uint32_t cpu_buf; + struct list_head mem_map_handle; + uint32_t tmp_hdl; + /* read or write locks */ + struct mutex lock; + spinlock_t dsp_lock; +}; + +struct afe_audio_client { + atomic_t cmd_state; + /* Relative or absolute TS */ + uint32_t time_flag; + void *priv; + uint64_t time_stamp; + struct mutex cmd_lock; + /* idx:1 out port, 0: in port*/ + struct afe_audio_port_data port[2]; + wait_queue_head_t cmd_wait; + uint32_t mem_map_handle; +}; + +struct aanc_data { + bool aanc_active; + uint16_t aanc_rx_port; + uint16_t aanc_tx_port; + uint32_t aanc_rx_port_sample_rate; + uint32_t aanc_tx_port_sample_rate; + int level; +}; + +int afe_open(u16 port_id, union afe_port_config *afe_config, int rate); +int afe_close(int port_id); +int afe_loopback(u16 enable, u16 rx_port, u16 tx_port); +int afe_sidetone_enable(u16 tx_port_id, u16 rx_port_id, bool enable); +int afe_set_display_stream(u16 rx_port_id, u32 stream_idx, u32 ctl_idx); +int afe_loopback_gain(u16 port_id, u16 volume); +int afe_validate_port(u16 port_id); +int afe_get_port_index(u16 port_id); +int afe_get_topology(int port_id); +int afe_start_pseudo_port(u16 port_id); +int afe_stop_pseudo_port(u16 port_id); +uint32_t afe_req_mmap_handle(struct afe_audio_client *ac); +int afe_memory_map(phys_addr_t dma_addr_p, u32 dma_buf_sz, + struct afe_audio_client *ac); +int afe_cmd_memory_map(phys_addr_t dma_addr_p, u32 dma_buf_sz); +int afe_cmd_memory_map_nowait(int port_id, phys_addr_t dma_addr_p, + u32 dma_buf_sz); +int afe_cmd_memory_unmap(u32 dma_addr_p); +int afe_cmd_memory_unmap_nowait(u32 dma_addr_p); +void afe_set_dtmf_gen_rx_portid(u16 rx_port_id, int set); +int afe_dtmf_generate_rx(int64_t duration_in_ms, + uint16_t high_freq, + uint16_t low_freq, uint16_t gain); +int afe_register_get_events(u16 port_id, + void (*cb)(uint32_t opcode, + uint32_t token, uint32_t *payload, void *priv), + void *private_data); +int afe_unregister_get_events(u16 port_id); +int afe_rt_proxy_port_write(phys_addr_t buf_addr_p, + u32 mem_map_handle, int bytes); +int afe_rt_proxy_port_read(phys_addr_t buf_addr_p, + u32 mem_map_handle, int bytes); +void afe_set_cal_mode(u16 port_id, enum afe_cal_mode afe_cal_mode); +void afe_set_vad_cfg(u32 vad_enable, u32 preroll_config, + u32 port_id); +void afe_set_island_mode_cfg(u16 port_id, u32 enable_flag); +void afe_get_island_mode_cfg(u16 port_id, u32 *enable_flag); +int afe_port_start(u16 port_id, union afe_port_config *afe_config, + u32 rate); +int afe_set_tws_channel_mode(u16 port_id, u32 channel_mode); +int afe_port_start_v2(u16 port_id, union afe_port_config *afe_config, + u32 rate, u16 afe_in_channels, u16 afe_in_bit_width, + struct afe_enc_config *enc_config, + struct afe_dec_config *dec_config); +int afe_spk_prot_feed_back_cfg(int src_port, int dst_port, + int l_ch, int r_ch, u32 enable); +int afe_spk_prot_get_calib_data(struct afe_spkr_prot_get_vi_calib *calib); +int afe_port_stop_nowait(int port_id); +int afe_apply_gain(u16 port_id, u16 gain); +int afe_q6_interface_prepare(void); +int afe_get_port_type(u16 port_id); +int q6afe_audio_client_buf_alloc_contiguous(unsigned int dir, + struct afe_audio_client *ac, + unsigned int bufsz, + unsigned int bufcnt); +struct afe_audio_client *q6afe_audio_client_alloc(void *priv); +int q6afe_audio_client_buf_free_contiguous(unsigned int dir, + struct afe_audio_client *ac); +void q6afe_audio_client_free(struct afe_audio_client *ac); +/* if port_id is virtual, convert to physical.. + * if port_id is already physical, return physical + */ +int afe_convert_virtual_to_portid(u16 port_id); + +int afe_pseudo_port_start_nowait(u16 port_id); +int afe_pseudo_port_stop_nowait(u16 port_id); +int afe_set_lpass_clock(u16 port_id, struct afe_clk_cfg *cfg); +int afe_set_lpass_clock_v2(u16 port_id, struct afe_clk_set *cfg); +int afe_set_lpass_clk_cfg(int index, struct afe_clk_set *cfg); +int afe_set_digital_codec_core_clock(u16 port_id, + struct afe_digital_clk_cfg *cfg); +int afe_set_lpass_internal_digital_codec_clock(u16 port_id, + struct afe_digital_clk_cfg *cfg); +int afe_enable_lpass_core_shared_clock(u16 port_id, u32 enable); + +int q6afe_check_osr_clk_freq(u32 freq); + +int afe_send_spdif_clk_cfg(struct afe_param_id_spdif_clk_cfg *cfg, + u16 port_id); +int afe_send_spdif_ch_status_cfg(struct afe_param_id_spdif_ch_status_cfg + *ch_status_cfg, u16 port_id); + +int afe_spdif_port_start(u16 port_id, struct afe_spdif_port_config *spdif_port, + u32 rate); + +int afe_spdif_reg_event_cfg(u16 port_id, u16 reg_flag, + void (*cb)(uint32_t opcode, + uint32_t token, uint32_t *payload, void *priv), + void *private_data); + +int afe_turn_onoff_hw_mad(u16 mad_type, u16 mad_enable); +int afe_port_set_mad_type(u16 port_id, enum afe_mad_type mad_type); +enum afe_mad_type afe_port_get_mad_type(u16 port_id); +int afe_set_config(enum afe_config_type config_type, void *config_data, + int arg); +void afe_clear_config(enum afe_config_type config); +bool afe_has_config(enum afe_config_type config); + +void afe_set_aanc_info(struct aanc_data *aanc_info); +int afe_set_aanc_noise_level(int val); +int afe_port_group_set_param(u16 group_id, + union afe_port_group_config *afe_group_config); +int afe_port_group_enable(u16 group_id, + union afe_port_group_config *afe_group_config, u16 enable); +int afe_unmap_rtac_block(uint32_t *mem_map_handle); +int afe_map_rtac_block(struct rtac_cal_block_data *cal_block); +int afe_send_slot_mapping_cfg( + struct afe_param_id_slot_mapping_cfg *slot_mapping_cfg, + u16 port_id); +int afe_send_custom_tdm_header_cfg( + struct afe_param_id_custom_tdm_header_cfg *custom_tdm_header_cfg, + u16 port_id); +int afe_tdm_port_start(u16 port_id, struct afe_tdm_port_config *tdm_port, + u32 rate, u16 num_groups); +void afe_set_routing_callback(routing_cb cb); +int afe_get_av_dev_drift(struct afe_param_id_dev_timing_stats *timing_stats, + u16 port); +int afe_get_sp_rx_tmax_xmax_logging_data( + struct afe_sp_rx_tmax_xmax_logging_param *xt_logging, + u16 port_id); +int afe_cal_init_hwdep(void *card); +int afe_send_port_island_mode(u16 port_id); +int afe_send_cmd_wakeup_register(void *handle, bool enable); +void afe_register_wakeup_irq_callback( + void (*afe_cb_wakeup_irq)(void *handle)); + +#define AFE_LPASS_CORE_HW_BLOCK_ID_NONE 0 +#define AFE_LPASS_CORE_HW_BLOCK_ID_AVTIMER 2 +#define AFE_LPASS_CORE_HW_MACRO_BLOCK 3 + +/* Handles audio-video timer (avtimer) and BTSC vote requests from clients */ +#define AFE_CMD_REMOTE_LPASS_CORE_HW_VOTE_REQUEST 0x000100f4 + +struct afe_cmd_remote_lpass_core_hw_vote_request { + struct apr_hdr hdr; + uint32_t hw_block_id; + /* ID of the hardware block. */ + char client_name[8]; + /* Name of the client. */ +} __packed; + +#define AFE_CMD_RSP_REMOTE_LPASS_CORE_HW_VOTE_REQUEST 0x000100f5 + +struct afe_cmd_rsp_remote_lpass_core_hw_vote_request { + uint32_t client_handle; + /**< Handle of the client. */ +} __packed; + +#define AFE_CMD_REMOTE_LPASS_CORE_HW_DEVOTE_REQUEST 0x000100f6 + +struct afe_cmd_remote_lpass_core_hw_devote_request { + struct apr_hdr hdr; + uint32_t hw_block_id; + /**< ID of the hardware block.*/ + + uint32_t client_handle; + /**< Handle of the client.*/ +} __packed; + +int afe_vote_lpass_core_hw(uint32_t hw_block_id, char *client_name, + uint32_t *client_handle); +int afe_unvote_lpass_core_hw(uint32_t hw_block_id, uint32_t client_handle); +int afe_get_spk_initial_cal(void); +void afe_get_spk_r0(int *spk_r0); +void afe_get_spk_t0(int *spk_t0); +int afe_get_spk_v_vali_flag(void); +void afe_get_spk_v_vali_sts(int *spk_v_vali_sts); +void afe_set_spk_initial_cal(int initial_cal); +void afe_set_spk_v_vali_flag(int v_vali_flag); +#endif /* __Q6AFE_V2_H__ */ diff --git a/audio-kernel/include/dsp/q6asm-v2.h b/audio-kernel/include/dsp/q6asm-v2.h new file mode 100644 index 000000000..2d738d635 --- /dev/null +++ b/audio-kernel/include/dsp/q6asm-v2.h @@ -0,0 +1,751 @@ +/* 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 + * 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 __Q6_ASM_V2_H__ +#define __Q6_ASM_V2_H__ + +#include +#include +#include +#include +#include + +#define IN 0x000 +#define OUT 0x001 +#define CH_MODE_MONO 0x001 +#define CH_MODE_STEREO 0x002 + +#define FORMAT_LINEAR_PCM 0x0000 +#define FORMAT_DTMF 0x0001 +#define FORMAT_ADPCM 0x0002 +#define FORMAT_YADPCM 0x0003 +#define FORMAT_MP3 0x0004 +#define FORMAT_MPEG4_AAC 0x0005 +#define FORMAT_AMRNB 0x0006 +#define FORMAT_AMRWB 0x0007 +#define FORMAT_V13K 0x0008 +#define FORMAT_EVRC 0x0009 +#define FORMAT_EVRCB 0x000a +#define FORMAT_EVRCWB 0x000b +#define FORMAT_MIDI 0x000c +#define FORMAT_SBC 0x000d +#define FORMAT_WMA_V10PRO 0x000e +#define FORMAT_WMA_V9 0x000f +#define FORMAT_AMR_WB_PLUS 0x0010 +#define FORMAT_MPEG4_MULTI_AAC 0x0011 +#define FORMAT_MULTI_CHANNEL_LINEAR_PCM 0x0012 +#define FORMAT_AC3 0x0013 +#define FORMAT_EAC3 0x0014 +#define FORMAT_MP2 0x0015 +#define FORMAT_FLAC 0x0016 +#define FORMAT_ALAC 0x0017 +#define FORMAT_VORBIS 0x0018 +#define FORMAT_APE 0x0019 +#define FORMAT_G711_ALAW_FS 0x001a +#define FORMAT_G711_MLAW_FS 0x001b +#define FORMAT_DTS 0x001c +#define FORMAT_DSD 0x001d +#define FORMAT_APTX 0x001e +#define FORMAT_GEN_COMPR 0x001f +#define FORMAT_TRUEHD 0x0020 +#define FORMAT_IEC61937 0x0021 +#define FORMAT_BESPOKE 0x0022 + +#define ENCDEC_SBCBITRATE 0x0001 +#define ENCDEC_IMMEDIATE_DECODE 0x0002 +#define ENCDEC_CFG_BLK 0x0003 + +#define ENC_CFG_ID_NONE 0x0000 + +#define CMD_PAUSE 0x0001 +#define CMD_FLUSH 0x0002 +#define CMD_EOS 0x0003 +#define CMD_CLOSE 0x0004 +#define CMD_OUT_FLUSH 0x0005 +#define CMD_SUSPEND 0x0006 + +/* bit 0:1 represents priority of stream */ +#define STREAM_PRIORITY_NORMAL 0x0000 +#define STREAM_PRIORITY_LOW 0x0001 +#define STREAM_PRIORITY_HIGH 0x0002 + +/* bit 4 represents META enable of encoded data buffer */ +#define BUFFER_META_ENABLE 0x0010 + +/* bit 5 represents timestamp */ +/* bit 5 - 0 -- ASM_DATA_EVENT_READ_DONE will have relative time-stamp*/ +/* bit 5 - 1 -- ASM_DATA_EVENT_READ_DONE will have absolute time-stamp*/ +#define ABSOLUTE_TIMESTAMP_ENABLE 0x0020 + +/* Enable Sample_Rate/Channel_Mode notification event from Decoder */ +#define SR_CM_NOTIFY_ENABLE 0x0004 + +#define TUN_WRITE_IO_MODE 0x0008 /* tunnel read write mode */ +#define TUN_READ_IO_MODE 0x0004 /* tunnel read write mode */ +#define SYNC_IO_MODE 0x0001 +#define ASYNC_IO_MODE 0x0002 +#define COMPRESSED_IO 0x0040 +#define COMPRESSED_STREAM_IO 0x0080 +#define NT_MODE 0x0400 + +#define NO_TIMESTAMP 0xFF00 +#define SET_TIMESTAMP 0x0000 + +#define SOFT_PAUSE_ENABLE 1 +#define SOFT_PAUSE_DISABLE 0 + +#define ASM_ACTIVE_STREAMS_ALLOWED 0x8 +/* Control session is used for mapping calibration memory */ +#define ASM_CONTROL_SESSION (ASM_ACTIVE_STREAMS_ALLOWED + 1) + +#define ASM_SHIFT_GAPLESS_MODE_FLAG 31 +#define ASM_SHIFT_LAST_BUFFER_FLAG 30 + +#define ASM_LITTLE_ENDIAN 0 +#define ASM_BIG_ENDIAN 1 + +#define ADSP_ASM_API_VERSION_V2 2 + +/* PCM_MEDIA_FORMAT_Version */ +enum { + PCM_MEDIA_FORMAT_V2 = 0, + PCM_MEDIA_FORMAT_V3, + PCM_MEDIA_FORMAT_V4, + PCM_MEDIA_FORMAT_V5, +}; + +/* PCM format modes in DSP */ +enum { + DEFAULT_QF = 0, + Q15 = 15, + Q23 = 23, + Q31 = 31, +}; + +/* payload structure bytes */ +#define READDONE_IDX_STATUS 0 +#define READDONE_IDX_BUFADD_LSW 1 +#define READDONE_IDX_BUFADD_MSW 2 +#define READDONE_IDX_MEMMAP_HDL 3 +#define READDONE_IDX_SIZE 4 +#define READDONE_IDX_OFFSET 5 +#define READDONE_IDX_LSW_TS 6 +#define READDONE_IDX_MSW_TS 7 +#define READDONE_IDX_FLAGS 8 +#define READDONE_IDX_NUMFRAMES 9 +#define READDONE_IDX_SEQ_ID 10 + +#define SOFT_PAUSE_PERIOD 30 /* ramp up/down for 30ms */ +#define SOFT_PAUSE_STEP 0 /* Step value 0ms or 0us */ +enum { + SOFT_PAUSE_CURVE_LINEAR = 0, + SOFT_PAUSE_CURVE_EXP, + SOFT_PAUSE_CURVE_LOG, +}; + +#define SOFT_VOLUME_PERIOD 30 /* ramp up/down for 30ms */ +#define SOFT_VOLUME_STEP 0 /* Step value 0ms or 0us */ +enum { + SOFT_VOLUME_CURVE_LINEAR = 0, + SOFT_VOLUME_CURVE_EXP, + SOFT_VOLUME_CURVE_LOG, +}; + +#define SOFT_VOLUME_INSTANCE_1 1 +#define SOFT_VOLUME_INSTANCE_2 2 + +typedef void (*app_cb)(uint32_t opcode, uint32_t token, + uint32_t *payload, void *priv); + +struct audio_buffer { + dma_addr_t phys; + void *data; + uint32_t used; + uint32_t size;/* size of buffer */ + uint32_t actual_size; /* actual number of bytes read by DSP */ + struct dma_buf *dma_buf; +}; + +struct audio_aio_write_param { + phys_addr_t paddr; + uint32_t len; + uint32_t uid; + uint32_t lsw_ts; + uint32_t msw_ts; + uint32_t flags; + uint32_t metadata_len; + uint32_t last_buffer; +}; + +struct audio_aio_read_param { + phys_addr_t paddr; + uint32_t len; + uint32_t uid; + uint32_t flags;/*meta data flags*/ +}; + +struct audio_port_data { + struct audio_buffer *buf; + uint32_t max_buf_cnt; + uint32_t dsp_buf; + uint32_t cpu_buf; + struct list_head mem_map_handle; + uint32_t tmp_hdl; + /* read or write locks */ + struct mutex lock; + spinlock_t dsp_lock; +}; + +struct shared_io_config { + uint32_t format; + uint16_t bits_per_sample; + uint32_t rate; + uint32_t channels; + uint16_t sample_word_size; + uint32_t bufsz; + uint32_t bufcnt; +}; + +struct audio_client { + int session; + app_cb cb; + atomic_t cmd_state; + atomic_t cmd_state_pp; + /* Relative or absolute TS */ + atomic_t time_flag; + atomic_t nowait_cmd_cnt; + atomic_t mem_state; + void *priv; + uint32_t io_mode; + uint64_t time_stamp; + struct apr_svc *apr; + struct apr_svc *mmap_apr; + struct apr_svc *apr2; + struct mutex cmd_lock; + /* idx:1 out port, 0: in port*/ + struct audio_port_data port[2]; + wait_queue_head_t cmd_wait; + wait_queue_head_t time_wait; + wait_queue_head_t mem_wait; + int perf_mode; + int stream_id; + struct device *dev; + int topology; + int app_type; + /* audio cache operations fptr*/ + int (*fptr_cache_ops)(struct audio_buffer *abuff, int cache_op); + atomic_t unmap_cb_success; + atomic_t reset; + /* holds latest DSP pipeline delay */ + uint32_t path_delay; + /* shared io */ + struct audio_buffer shared_pos_buf; + struct shared_io_config config; +}; + +struct q6asm_cal_info { + int topology_id; + int app_type; +}; + +void q6asm_audio_client_free(struct audio_client *ac); + +struct audio_client *q6asm_audio_client_alloc(app_cb cb, void *priv); + +struct audio_client *q6asm_get_audio_client(int session_id); + +int q6asm_audio_client_buf_alloc(unsigned int dir/* 1:Out,0:In */, + struct audio_client *ac, + unsigned int bufsz, + uint32_t bufcnt); +int q6asm_audio_client_buf_alloc_contiguous(unsigned int dir + /* 1:Out,0:In */, + struct audio_client *ac, + unsigned int bufsz, + unsigned int bufcnt); + +int q6asm_audio_client_buf_free_contiguous(unsigned int dir, + struct audio_client *ac); + +int q6asm_set_pp_params(struct audio_client *ac, + struct mem_mapping_hdr *mem_hdr, u8 *param_data, + u32 param_size); + +int q6asm_pack_and_set_pp_param_in_band(struct audio_client *ac, + struct param_hdr_v3 param_hdr, + u8 *param_data); + +int q6asm_set_soft_volume_module_instance_ids(int instance, + struct param_hdr_v3 *param_hdr); + +int q6asm_open_read(struct audio_client *ac, uint32_t format + /*, uint16_t bits_per_sample*/); + +int q6asm_open_read_v2(struct audio_client *ac, uint32_t format, + uint16_t bits_per_sample); + +int q6asm_open_read_v3(struct audio_client *ac, uint32_t format, + uint16_t bits_per_sample); + +int q6asm_open_read_v4(struct audio_client *ac, uint32_t format, + uint16_t bits_per_sample, bool ts_mode, + uint32_t enc_cfg_id); + +int q6asm_open_read_v5(struct audio_client *ac, uint32_t format, + uint16_t bits_per_sample, bool ts_mode, + uint32_t enc_cfg_id); + +int q6asm_open_write(struct audio_client *ac, uint32_t format + /*, uint16_t bits_per_sample*/); + +int q6asm_open_write_v2(struct audio_client *ac, uint32_t format, + uint16_t bits_per_sample); + +int q6asm_open_shared_io(struct audio_client *ac, + struct shared_io_config *c, int dir, + bool use_default_chmap, u8 *channel_map); + +int q6asm_open_write_v3(struct audio_client *ac, uint32_t format, + uint16_t bits_per_sample); + +int q6asm_open_write_v4(struct audio_client *ac, uint32_t format, + uint16_t bits_per_sample); + +int q6asm_open_write_v5(struct audio_client *ac, uint32_t format, + uint16_t bits_per_sample); + +int q6asm_stream_open_write_v2(struct audio_client *ac, uint32_t format, + uint16_t bits_per_sample, int32_t stream_id, + bool is_gapless_mode); + +int q6asm_stream_open_write_v3(struct audio_client *ac, uint32_t format, + uint16_t bits_per_sample, int32_t stream_id, + bool is_gapless_mode); + +int q6asm_stream_open_write_v4(struct audio_client *ac, uint32_t format, + uint16_t bits_per_sample, int32_t stream_id, + bool is_gapless_mode); + +int q6asm_stream_open_write_v5(struct audio_client *ac, uint32_t format, + uint16_t bits_per_sample, int32_t stream_id, + bool is_gapless_mode); + +int q6asm_open_read_compressed(struct audio_client *ac, uint32_t format, + uint32_t passthrough_flag); + +int q6asm_open_write_compressed(struct audio_client *ac, uint32_t format, + uint32_t passthrough_flag); + +int q6asm_open_read_write(struct audio_client *ac, + uint32_t rd_format, + uint32_t wr_format); + +int q6asm_open_read_write_v2(struct audio_client *ac, uint32_t rd_format, + uint32_t wr_format, bool is_meta_data_mode, + uint32_t bits_per_sample, bool overwrite_topology, + int topology); + +int q6asm_open_loopback_v2(struct audio_client *ac, + uint16_t bits_per_sample); + +int q6asm_open_transcode_loopback(struct audio_client *ac, + uint16_t bits_per_sample, uint32_t source_format, + uint32_t sink_format); + +int q6asm_write(struct audio_client *ac, uint32_t len, uint32_t msw_ts, + uint32_t lsw_ts, uint32_t flags); +int q6asm_write_nolock(struct audio_client *ac, uint32_t len, uint32_t msw_ts, + uint32_t lsw_ts, uint32_t flags); + +int q6asm_async_write(struct audio_client *ac, + struct audio_aio_write_param *param); + +int q6asm_async_read(struct audio_client *ac, + struct audio_aio_read_param *param); + +int q6asm_read(struct audio_client *ac); +int q6asm_read_v2(struct audio_client *ac, uint32_t len); +int q6asm_read_nolock(struct audio_client *ac); + +int q6asm_memory_map(struct audio_client *ac, phys_addr_t buf_add, + int dir, uint32_t bufsz, uint32_t bufcnt); + +int q6asm_memory_unmap(struct audio_client *ac, phys_addr_t buf_add, + int dir); + +struct audio_buffer *q6asm_shared_io_buf(struct audio_client *ac, int dir); + +int q6asm_shared_io_free(struct audio_client *ac, int dir); + +int q6asm_get_shared_pos(struct audio_client *ac, uint32_t *si, uint32_t *msw, + uint32_t *lsw); + +int q6asm_map_rtac_block(struct rtac_cal_block_data *cal_block); + +int q6asm_unmap_rtac_block(uint32_t *mem_map_handle); + +int q6asm_send_cal(struct audio_client *ac); + +int q6asm_run(struct audio_client *ac, uint32_t flags, + uint32_t msw_ts, uint32_t lsw_ts); + +int q6asm_run_nowait(struct audio_client *ac, uint32_t flags, + uint32_t msw_ts, uint32_t lsw_ts); + +int q6asm_stream_run_nowait(struct audio_client *ac, uint32_t flags, + uint32_t msw_ts, uint32_t lsw_ts, uint32_t stream_id); + +int q6asm_reg_tx_overflow(struct audio_client *ac, uint16_t enable); + +int q6asm_reg_rx_underflow(struct audio_client *ac, uint16_t enable); + +int q6asm_cmd(struct audio_client *ac, int cmd); + +int q6asm_stream_cmd(struct audio_client *ac, int cmd, uint32_t stream_id); + +int q6asm_cmd_nowait(struct audio_client *ac, int cmd); + +int q6asm_stream_cmd_nowait(struct audio_client *ac, int cmd, + uint32_t stream_id); + +void *q6asm_is_cpu_buf_avail(int dir, struct audio_client *ac, + uint32_t *size, uint32_t *idx); + +int q6asm_cpu_buf_release(int dir, struct audio_client *ac); + +void *q6asm_is_cpu_buf_avail_nolock(int dir, struct audio_client *ac, + uint32_t *size, uint32_t *idx); + +int q6asm_is_dsp_buf_avail(int dir, struct audio_client *ac); + +/* File format specific configurations to be added below */ + +int q6asm_enc_cfg_blk_aac(struct audio_client *ac, + uint32_t frames_per_buf, + uint32_t sample_rate, uint32_t channels, + uint32_t bit_rate, + uint32_t mode, uint32_t format); + +int q6asm_enc_cfg_blk_g711(struct audio_client *ac, + uint32_t frames_per_buf, + uint32_t sample_rate); + +int q6asm_enc_cfg_blk_pcm(struct audio_client *ac, + uint32_t rate, uint32_t channels); + +int q6asm_enc_cfg_blk_pcm_v2(struct audio_client *ac, + uint32_t rate, uint32_t channels, + uint16_t bits_per_sample, + bool use_default_chmap, bool use_back_flavor, + u8 *channel_map); + +int q6asm_enc_cfg_blk_pcm_v3(struct audio_client *ac, + uint32_t rate, uint32_t channels, + uint16_t bits_per_sample, bool use_default_chmap, + bool use_back_flavor, u8 *channel_map, + uint16_t sample_word_size); + +int q6asm_enc_cfg_blk_pcm_v4(struct audio_client *ac, + uint32_t rate, uint32_t channels, + uint16_t bits_per_sample, bool use_default_chmap, + bool use_back_flavor, u8 *channel_map, + uint16_t sample_word_size, uint16_t endianness, + uint16_t mode); + +int q6asm_enc_cfg_blk_pcm_format_support(struct audio_client *ac, + uint32_t rate, uint32_t channels, + uint16_t bits_per_sample); + +int q6asm_enc_cfg_blk_pcm_format_support_v3(struct audio_client *ac, + uint32_t rate, uint32_t channels, + uint16_t bits_per_sample, + uint16_t sample_word_size); + +int q6asm_enc_cfg_blk_pcm_format_support_v4(struct audio_client *ac, + uint32_t rate, uint32_t channels, + uint16_t bits_per_sample, + uint16_t sample_word_size, + uint16_t endianness, + uint16_t mode); + +int q6asm_enc_cfg_blk_pcm_format_support_v5(struct audio_client *ac, + uint32_t rate, uint32_t channels, + uint16_t bits_per_sample, + uint16_t sample_word_size, + uint16_t endianness, + uint16_t mode); + +int q6asm_enc_cfg_blk_custom(struct audio_client *ac, + uint32_t sample_rate, uint32_t channels, + uint32_t format, void *cfg); + +int q6asm_set_encdec_chan_map(struct audio_client *ac, + uint32_t num_channels); + +int q6asm_enc_cfg_blk_pcm_native(struct audio_client *ac, + uint32_t rate, uint32_t channels); + +int q6asm_enable_sbrps(struct audio_client *ac, + uint32_t sbr_ps); + +int q6asm_cfg_dual_mono_aac(struct audio_client *ac, + uint16_t sce_left, uint16_t sce_right); + +int q6asm_cfg_aac_sel_mix_coef(struct audio_client *ac, uint32_t mix_coeff); + +int q6asm_enc_cfg_blk_qcelp(struct audio_client *ac, uint32_t frames_per_buf, + uint16_t min_rate, uint16_t max_rate, + uint16_t reduced_rate_level, uint16_t rate_modulation_cmd); + +int q6asm_enc_cfg_blk_evrc(struct audio_client *ac, uint32_t frames_per_buf, + uint16_t min_rate, uint16_t max_rate, + uint16_t rate_modulation_cmd); + +int q6asm_enc_cfg_blk_amrnb(struct audio_client *ac, uint32_t frames_per_buf, + uint16_t band_mode, uint16_t dtx_enable); + +int q6asm_enc_cfg_blk_amrwb(struct audio_client *ac, uint32_t frames_per_buf, + uint16_t band_mode, uint16_t dtx_enable); + +int q6asm_media_format_block_pcm(struct audio_client *ac, + uint32_t rate, uint32_t channels); + +int q6asm_media_format_block_pcm_format_support(struct audio_client *ac, + uint32_t rate, uint32_t channels, + uint16_t bits_per_sample); + +int q6asm_media_format_block_pcm_format_support_v2(struct audio_client *ac, + uint32_t rate, uint32_t channels, + uint16_t bits_per_sample, int stream_id, + bool use_default_chmap, char *channel_map); + +int q6asm_media_format_block_pcm_format_support_v3(struct audio_client *ac, + uint32_t rate, + uint32_t channels, + uint16_t bits_per_sample, + int stream_id, + bool use_default_chmap, + char *channel_map, + uint16_t sample_word_size); + +int q6asm_media_format_block_pcm_format_support_v4(struct audio_client *ac, + uint32_t rate, + uint32_t channels, + uint16_t bits_per_sample, + int stream_id, + bool use_default_chmap, + char *channel_map, + uint16_t sample_word_size, + uint16_t endianness, + uint16_t mode); + +int q6asm_media_format_block_pcm_format_support_v5(struct audio_client *ac, + uint32_t rate, + uint32_t channels, + uint16_t bits_per_sample, + int stream_id, + bool use_default_chmap, + char *channel_map, + uint16_t sample_word_size, + uint16_t endianness, + uint16_t mode); + +int q6asm_media_format_block_multi_ch_pcm(struct audio_client *ac, + uint32_t rate, uint32_t channels, + bool use_default_chmap, char *channel_map); + +int q6asm_media_format_block_multi_ch_pcm_v2( + struct audio_client *ac, + uint32_t rate, uint32_t channels, + bool use_default_chmap, char *channel_map, + uint16_t bits_per_sample); +int q6asm_media_format_block_gen_compr( + struct audio_client *ac, + uint32_t rate, uint32_t channels, + bool use_default_chmap, char *channel_map, + uint16_t bits_per_sample); + +int q6asm_media_format_block_iec( + struct audio_client *ac, + uint32_t rate, uint32_t channels); + +int q6asm_media_format_block_multi_ch_pcm_v3(struct audio_client *ac, + uint32_t rate, uint32_t channels, + bool use_default_chmap, + char *channel_map, + uint16_t bits_per_sample, + uint16_t sample_word_size); + +int q6asm_media_format_block_multi_ch_pcm_v4(struct audio_client *ac, + uint32_t rate, uint32_t channels, + bool use_default_chmap, + char *channel_map, + uint16_t bits_per_sample, + uint16_t sample_word_size, + uint16_t endianness, + uint16_t mode); + +int q6asm_media_format_block_multi_ch_pcm_v5(struct audio_client *ac, + uint32_t rate, uint32_t channels, + bool use_default_chmap, + char *channel_map, + uint16_t bits_per_sample, + uint16_t sample_word_size, + uint16_t endianness, + uint16_t mode); + +int q6asm_media_format_block_aac(struct audio_client *ac, + struct asm_aac_cfg *cfg); + +int q6asm_stream_media_format_block_aac(struct audio_client *ac, + struct asm_aac_cfg *cfg, int stream_id); + +int q6asm_media_format_block_multi_aac(struct audio_client *ac, + struct asm_aac_cfg *cfg); + +int q6asm_media_format_block_wma(struct audio_client *ac, + void *cfg, int stream_id); + +int q6asm_media_format_block_wmapro(struct audio_client *ac, + void *cfg, int stream_id); + +int q6asm_media_format_block_amrwbplus(struct audio_client *ac, + struct asm_amrwbplus_cfg *cfg); + +int q6asm_stream_media_format_block_flac(struct audio_client *ac, + struct asm_flac_cfg *cfg, int stream_id); + +int q6asm_media_format_block_alac(struct audio_client *ac, + struct asm_alac_cfg *cfg, int stream_id); + +int q6asm_media_format_block_g711(struct audio_client *ac, + struct asm_g711_dec_cfg *cfg, int stream_id); + +int q6asm_stream_media_format_block_vorbis(struct audio_client *ac, + struct asm_vorbis_cfg *cfg, int stream_id); + +int q6asm_media_format_block_ape(struct audio_client *ac, + struct asm_ape_cfg *cfg, int stream_id); + +int q6asm_media_format_block_dsd(struct audio_client *ac, + struct asm_dsd_cfg *cfg, int stream_id); + +int q6asm_stream_media_format_block_aptx_dec(struct audio_client *ac, + uint32_t sr, int stream_id); + +int q6asm_ds1_set_endp_params(struct audio_client *ac, + int param_id, int param_value); + +/* Send stream based end params */ +int q6asm_ds1_set_stream_endp_params(struct audio_client *ac, int param_id, + int param_value, int stream_id); + +/* PP specific */ +int q6asm_equalizer(struct audio_client *ac, void *eq); + +/* Send Volume Command */ +int q6asm_set_volume(struct audio_client *ac, int volume); + +/* Send Volume Command */ +int q6asm_set_volume_v2(struct audio_client *ac, int volume, int instance); + +/* DTS Eagle Params */ +int q6asm_dts_eagle_set(struct audio_client *ac, int param_id, uint32_t size, + void *data, struct param_outband *po, int m_id); +int q6asm_dts_eagle_get(struct audio_client *ac, int param_id, uint32_t size, + void *data, struct param_outband *po, int m_id); + +/* Send aptx decoder BT address */ +int q6asm_set_aptx_dec_bt_addr(struct audio_client *ac, + struct aptx_dec_bt_addr_cfg *cfg); + +/* Set SoftPause Params */ +int q6asm_set_softpause(struct audio_client *ac, + struct asm_softpause_params *param); + +/* Set Softvolume Params */ +int q6asm_set_softvolume(struct audio_client *ac, + struct asm_softvolume_params *param); + +/* Set Softvolume Params */ +int q6asm_set_softvolume_v2(struct audio_client *ac, + struct asm_softvolume_params *param, int instance); + +/* Send left-right channel gain */ +int q6asm_set_lrgain(struct audio_client *ac, int left_gain, int right_gain); + +/* Send multi channel gain */ +int q6asm_set_multich_gain(struct audio_client *ac, uint32_t channels, + uint32_t *gains, uint8_t *ch_map, bool use_default); + +/* Enable Mute/unmute flag */ +int q6asm_set_mute(struct audio_client *ac, int muteflag); + +int q6asm_get_session_time(struct audio_client *ac, uint64_t *tstamp); + +int q6asm_get_session_time_legacy(struct audio_client *ac, uint64_t *tstamp); + +int q6asm_send_stream_cmd(struct audio_client *ac, + struct msm_adsp_event_data *data); + +int q6asm_send_ion_fd(struct audio_client *ac, int fd); + +int q6asm_send_rtic_event_ack(struct audio_client *ac, + void *param, uint32_t params_length); + +/* Client can set the IO mode to either AIO/SIO mode */ +int q6asm_set_io_mode(struct audio_client *ac, uint32_t mode); + +/* Get Service ID for APR communication */ +int q6asm_get_apr_service_id(int session_id); + +/* Common format block without any payload */ +int q6asm_media_format_block(struct audio_client *ac, uint32_t format); + +/* Send the meta data to remove initial and trailing silence */ +int q6asm_send_meta_data(struct audio_client *ac, uint32_t initial_samples, + uint32_t trailing_samples); + +/* Send the stream meta data to remove initial and trailing silence */ +int q6asm_stream_send_meta_data(struct audio_client *ac, uint32_t stream_id, + uint32_t initial_samples, uint32_t trailing_samples); + +int q6asm_get_asm_topology(int session_id); +int q6asm_get_asm_app_type(int session_id); + +int q6asm_send_mtmx_strtr_window(struct audio_client *ac, + struct asm_session_mtmx_strtr_param_window_v2_t *window_param, + uint32_t param_id); + +/* Configure DSP render mode */ +int q6asm_send_mtmx_strtr_render_mode(struct audio_client *ac, + uint32_t render_mode); + +/* Configure DSP clock recovery mode */ +int q6asm_send_mtmx_strtr_clk_rec_mode(struct audio_client *ac, + uint32_t clk_rec_mode); + +/* Enable adjust session clock in DSP */ +int q6asm_send_mtmx_strtr_enable_adjust_session_clock(struct audio_client *ac, + bool enable); + +/* Retrieve the current DSP path delay */ +int q6asm_get_path_delay(struct audio_client *ac); + +/* Helper functions to retrieve data from token */ +uint8_t q6asm_get_buf_index_from_token(uint32_t token); +uint8_t q6asm_get_stream_id_from_token(uint32_t token); + +/* Adjust session clock in DSP */ +int q6asm_adjust_session_clock(struct audio_client *ac, + uint32_t adjust_time_lsw, + uint32_t adjust_time_msw); +#endif /* __Q6_ASM_H__ */ diff --git a/audio-kernel/include/dsp/q6audio-v2.h b/audio-kernel/include/dsp/q6audio-v2.h new file mode 100644 index 000000000..5df88fcbb --- /dev/null +++ b/audio-kernel/include/dsp/q6audio-v2.h @@ -0,0 +1,36 @@ +/* Copyright (c) 2012-2013, 2015 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 _Q6_AUDIO_H_ +#define _Q6_AUDIO_H_ + +#include + +enum { + LEGACY_PCM_MODE = 0, + LOW_LATENCY_PCM_MODE, + ULTRA_LOW_LATENCY_PCM_MODE, + ULL_POST_PROCESSING_PCM_MODE, +}; + + +int q6audio_get_port_index(u16 port_id); + +int q6audio_convert_virtual_to_portid(u16 port_id); + +int q6audio_validate_port(u16 port_id); + +int q6audio_is_digital_pcm_interface(u16 port_id); + +int q6audio_get_port_id(u16 port_id); + +#endif diff --git a/audio-kernel/include/dsp/q6common.h b/audio-kernel/include/dsp/q6common.h new file mode 100644 index 000000000..552f97632 --- /dev/null +++ b/audio-kernel/include/dsp/q6common.h @@ -0,0 +1,23 @@ +/* Copyright (c) 2017-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 __Q6COMMON_H__ +#define __Q6COMMON_H__ + +#include + +void q6common_update_instance_id_support(bool supported); +bool q6common_is_instance_id_supported(void); +int q6common_pack_pp_params(u8 *dest, struct param_hdr_v3 *v3_hdr, + u8 *param_data, u32 *total_size); + +#endif /* __Q6COMMON_H__ */ diff --git a/audio-kernel/include/dsp/q6core.h b/audio-kernel/include/dsp/q6core.h new file mode 100644 index 000000000..ec629df44 --- /dev/null +++ b/audio-kernel/include/dsp/q6core.h @@ -0,0 +1,224 @@ +/* Copyright (c) 2012-2019, 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 __Q6CORE_H__ +#define __Q6CORE_H__ +#include +#include + + + +#define AVCS_CMD_ADSP_EVENT_GET_STATE 0x0001290C +#define AVCS_CMDRSP_ADSP_EVENT_GET_STATE 0x0001290D + +bool q6core_is_adsp_ready(void); + +int q6core_get_service_version(uint32_t service_id, + struct avcs_fwk_ver_info *ver_info, + size_t size); +size_t q6core_get_fwk_version_size(uint32_t service_id); + +struct audio_uevent_data { + struct kobject kobj; + struct kobj_type ktype; +}; + +int q6core_init_uevent_data(struct audio_uevent_data *uevent_data, char *name); +void q6core_destroy_uevent_data(struct audio_uevent_data *uevent_data); +int q6core_send_uevent(struct audio_uevent_data *uevent_data, char *name); +int q6core_get_avcs_api_version_per_service(uint32_t service_id); + +#define ADSP_CMD_SET_DTS_EAGLE_DATA_ID 0x00012919 +#define DTS_EAGLE_LICENSE_ID 0x00028346 +struct adsp_dts_eagle { + struct apr_hdr hdr; + uint32_t id; + uint32_t overwrite; + uint32_t size; + char data[]; +}; +int core_dts_eagle_set(int size, char *data); +int core_dts_eagle_get(int id, int size, char *data); + +#define ADSP_CMD_SET_DOLBY_MANUFACTURER_ID 0x00012918 + +struct adsp_dolby_manufacturer_id { + struct apr_hdr hdr; + int manufacturer_id; +}; + +uint32_t core_set_dolby_manufacturer_id(int manufacturer_id); + +/* Dolby Surround1 Module License ID. This ID is used as an identifier + * for DS1 license via ADSP generic license mechanism. + * Please refer AVCS_CMD_SET_LICENSE for more details. + */ +#define DOLBY_DS1_LICENSE_ID 0x00000001 + +#define AVCS_CMD_SET_LICENSE 0x00012919 +struct avcs_cmd_set_license { + struct apr_hdr hdr; + uint32_t id; /**< A unique ID used to refer to this license */ + uint32_t overwrite; + /* 0 = do not overwrite an existing license with this id. + * 1 = overwrite an existing license with this id. + */ + uint32_t size; + /**< Size in bytes of the license data following this header. */ + /* uint8_t* data , data and padding follows this structure + * total packet size needs to be multiple of 4 Bytes + */ + +}; + +#define AVCS_CMD_GET_LICENSE_VALIDATION_RESULT 0x0001291A +struct avcs_cmd_get_license_validation_result { + struct apr_hdr hdr; + uint32_t id; /**< A unique ID used to refer to this license */ +}; + +#define AVCS_CMDRSP_GET_LICENSE_VALIDATION_RESULT 0x0001291B +struct avcs_cmdrsp_get_license_validation_result { + uint32_t result; + /* ADSP_EOK if the license validation result was successfully retrieved. + * ADSP_ENOTEXIST if there is no license with the given id. + * ADSP_ENOTIMPL if there is no validation function for a license + * with this id. + */ + uint32_t size; + /* Length in bytes of the result that follows this structure*/ +}; + +/* Set Q6 topologies */ +/* + * Registers custom topologies in the aDSP for + * use in audio, voice, AFE and LSM. + */ + + +#define AVCS_CMD_SHARED_MEM_MAP_REGIONS 0x00012924 +#define AVCS_CMDRSP_SHARED_MEM_MAP_REGIONS 0x00012925 +#define AVCS_CMD_SHARED_MEM_UNMAP_REGIONS 0x00012926 + +/* Commands the AVCS to map multiple shared memory regions with remote + * processor ID. All mapped regions must be from the same memory pool. + * + * Return: + * ADSP_EOK : SUCCESS + * ADSP_EHANDLE : Failed due to incorrect handle. + * ADSP_EBADPARAM : Failed due to bad parameters. + * + * Dependencies: + * The mem_map_handle should be obtained earlier + * using AVCS_CMD_SHARED_MEM_MAP_REGIONS with pool ID + * ADSP_MEMORY_MAP_MDF_SHMEM_4K_POOL. + */ +#define AVCS_CMD_MAP_MDF_SHARED_MEMORY 0x00012930 + +#define AVCS_CMD_REGISTER_TOPOLOGIES 0x00012923 + +/* The payload for the AVCS_CMD_REGISTER_TOPOLOGIES command */ +struct avcs_cmd_register_topologies { + struct apr_hdr hdr; + uint32_t payload_addr_lsw; + /* Lower 32 bits of the topology buffer address. */ + + uint32_t payload_addr_msw; + /* Upper 32 bits of the topology buffer address. */ + + uint32_t mem_map_handle; + /* Unique identifier for an address. + * -This memory map handle is returned by the aDSP through the + * memory map command. + * -NULL mem_map_handle is interpreted as in-band parameter + * passing. + * -Client has the flexibility to choose in-band or out-of-band. + * -Out-of-band is recommended in this case. + */ + + uint32_t payload_size; + /* Size in bytes of the valid data in the topology buffer. */ +} __packed; + + +#define AVCS_CMD_DEREGISTER_TOPOLOGIES 0x0001292a + +/* The payload for the AVCS_CMD_DEREGISTER_TOPOLOGIES command */ +struct avcs_cmd_deregister_topologies { + struct apr_hdr hdr; + uint32_t payload_addr_lsw; + /* Lower 32 bits of the topology buffer address. */ + + uint32_t payload_addr_msw; + /* Upper 32 bits of the topology buffer address. */ + + uint32_t mem_map_handle; + /* Unique identifier for an address. + * -This memory map handle is returned by the aDSP through the + * memory map command. + * -NULL mem_map_handle is interpreted as in-band parameter + * passing. + * -Client has the flexibility to choose in-band or out-of-band. + * -Out-of-band is recommended in this case. + */ + + uint32_t payload_size; + /* Size in bytes of the valid data in the topology buffer. */ + + uint32_t mode; + /* 1: Deregister selected topologies + * 2: Deregister all topologies + */ +} __packed; + +#define AVCS_MODE_DEREGISTER_ALL_CUSTOM_TOPOLOGIES 2 + +#define AVCS_CMD_LOAD_TOPO_MODULES 0x0001296C + +#define AVCS_CMD_UNLOAD_TOPO_MODULES 0x0001296D + +#define CORE_LOAD_TOPOLOGY 0 + +#define CORE_UNLOAD_TOPOLOGY 1 + +struct avcs_cmd_load_unload_topo_modules { + struct apr_hdr hdr; + uint32_t topology_id; +} __packed; + + +int q6core_map_memory_regions(phys_addr_t *buf_add, uint32_t mempool_id, + uint32_t *bufsz, uint32_t bufcnt, uint32_t *map_handle); +int q6core_memory_unmap_regions(uint32_t mem_map_handle); +int q6core_map_mdf_shared_memory(uint32_t map_handle, phys_addr_t *buf_add, + uint32_t proc_id, uint32_t *bufsz, uint32_t bufcnt); + +int32_t core_set_license(uint32_t key, uint32_t module_id); +int32_t core_get_license_status(uint32_t module_id); + +int32_t q6core_load_unload_topo_modules(uint32_t topology_id, + bool preload_type); +int q6core_is_avs_up(int32_t *avs_state); + +#if IS_ENABLED(CONFIG_USE_Q6_32CH_SUPPORT) +static inline bool q6core_use_Q6_32ch_support(void) +{ + return true; +} +#else +static inline bool q6core_use_Q6_32ch_support(void) +{ + return false; +} +#endif + +#endif /* __Q6CORE_H__ */ diff --git a/audio-kernel/include/dsp/q6lsm.h b/audio-kernel/include/dsp/q6lsm.h new file mode 100644 index 000000000..5670d776c --- /dev/null +++ b/audio-kernel/include/dsp/q6lsm.h @@ -0,0 +1,306 @@ +/* + * Copyright (c) 2013-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 __Q6LSM_H__ +#define __Q6LSM_H__ + +#include +#include +#include +#include +#include + +#define MAX_NUM_CONFIDENCE 20 + +#define ADM_LSM_PORT_ID 0xADCB + +#define LSM_MAX_NUM_CHANNELS 8 +#define LSM_V3P0_MAX_NUM_CHANNELS 9 + +#define LSM_API_VERSION_V3 3 + +typedef void (*lsm_app_cb)(uint32_t opcode, uint32_t token, + uint32_t *payload, void *priv); + +struct lsm_sound_model { + dma_addr_t phys; + void *data; + size_t size; /* size of buffer */ + uint32_t actual_size; /* actual number of bytes read by DSP */ + struct dma_buf *dma_buf; + uint32_t mem_map_handle; +}; + +struct snd_lsm_event_status_v2 { + uint16_t status; + uint16_t payload_size; + uint8_t confidence_value[0]; +}; + +struct lsm_lab_buffer { + dma_addr_t phys; + void *data; + size_t size; + struct dma_buf *dma_buf; + uint32_t mem_map_handle; +}; + +struct lsm_hw_params { + u16 sample_rate; + u16 sample_size; + u32 buf_sz; + u32 period_count; + u16 num_chs; +}; + +struct lsm_cal_data_info { + dma_addr_t phys; + void *data; + size_t size; + struct dma_buf *dma_buf; + uint32_t mem_map_handle; +}; + +struct lsm_stage_config { + uint32_t app_type; + uint32_t topology_id; + bool lpi_enable; + bool lab_enable; + struct lsm_sound_model sound_model; + struct lsm_cal_data_info cal_info; +}; + + +struct lsm_client { + int session; + lsm_app_cb cb; + atomic_t cmd_state; + void *priv; + struct apr_svc *apr; + struct apr_svc *mmap_apr; + struct mutex cmd_lock; + wait_queue_head_t cmd_wait; + uint32_t cmd_err_code; + uint16_t mode; + uint16_t connect_to_port; + uint8_t num_confidence_levels; + uint8_t *confidence_levels; + bool opened; + bool started; + uint32_t app_id; + bool lab_enable; + bool lab_started; + struct lsm_lab_buffer *lab_buffer; + struct lsm_hw_params out_hw_params; + struct lsm_hw_params in_hw_params; + bool use_topology; + int session_state; + bool poll_enable; + int perf_mode; + uint32_t event_mode; + uint32_t event_type; + uint32_t num_stages; + struct lsm_stage_config stage_cfg[LSM_MAX_STAGES_PER_SESSION]; +}; + +struct lsm_stream_cmd_open_tx { + struct apr_hdr hdr; + uint16_t app_id; + uint16_t reserved; + uint32_t sampling_rate; +} __packed; + +struct lsm_stream_cmd_open_tx_v2 { + struct apr_hdr hdr; + uint32_t topology_id; +} __packed; + +struct lsm_stream_stage_info { + uint32_t topology_id; + uint32_t island_enable; +} __packed; + +struct lsm_stream_cmd_open_tx_v3 { + struct apr_hdr hdr; + uint32_t num_stages; + struct lsm_stream_stage_info stage_info[0]; +} __packed; + +struct lsm_custom_topologies { + struct apr_hdr hdr; + uint32_t data_payload_addr_lsw; + uint32_t data_payload_addr_msw; + uint32_t mem_map_handle; + uint32_t buffer_size; +} __packed; + +struct lsm_session_cmd_set_params_v2 { + struct apr_hdr apr_hdr; + uint32_t payload_size; + struct mem_mapping_hdr mem_hdr; + u32 param_data[0]; +} __packed; + +struct lsm_session_cmd_set_params_v3 { + struct apr_hdr apr_hdr; + struct mem_mapping_hdr mem_hdr; + uint32_t payload_size; + u32 param_data[0]; +} __packed; + +struct lsm_param_op_mode { + uint32_t minor_version; + uint16_t mode; + uint16_t reserved; +} __packed; + +struct lsm_param_connect_to_port { + uint32_t minor_version; + /* AFE port id that receives voice wake up data */ + uint16_t port_id; + uint16_t reserved; +} __packed; + +struct lsm_param_poll_enable { + uint32_t minor_version; + /* indicates to voice wakeup that HW MAD/SW polling is enabled or not */ + uint32_t polling_enable; +} __packed; + +struct lsm_param_fwk_mode_cfg { + uint32_t minor_version; + uint32_t mode; +} __packed; + +struct lsm_param_media_fmt { + uint32_t minor_version; + uint32_t sample_rate; + uint16_t num_channels; + uint16_t bit_width; + uint8_t channel_mapping[LSM_MAX_NUM_CHANNELS]; +} __packed; + +struct lsm_param_media_fmt_v2 { + uint32_t minor_version; + uint32_t sample_rate; + uint16_t bit_width; + uint16_t num_channels; + uint8_t channel_mapping[0]; +} __packed; + + +struct lsm_param_confidence_levels { + uint8_t num_confidence_levels; + uint8_t confidence_levels[0]; +} __packed; + +struct lsm_param_epd_thres { + uint32_t minor_version; + uint32_t epd_begin; + uint32_t epd_end; +} __packed; + +struct lsm_param_gain { + uint32_t minor_version; + uint16_t gain; + uint16_t reserved; +} __packed; + +struct lsm_cmd_reg_snd_model { + struct apr_hdr hdr; + uint32_t model_size; + uint32_t model_addr_lsw; + uint32_t model_addr_msw; + uint32_t mem_map_handle; +} __packed; + +struct lsm_param_lab_enable { + uint16_t enable; + uint16_t reserved; +} __packed; + +struct lsm_param_lab_config { + uint32_t minor_version; + uint32_t wake_up_latency_ms; +} __packed; + +struct lsm_param_lab_out_ch_cfg { + uint32_t minor_version; + uint32_t num_channels; + uint8_t channel_indices[0]; +} __packed; + +struct lsm_cmd_read { + struct apr_hdr hdr; + uint32_t buf_addr_lsw; + uint32_t buf_addr_msw; + uint32_t mem_map_handle; + uint32_t buf_size; +} __packed; + +struct lsm_cmd_read_done { + struct apr_hdr hdr; + uint32_t status; + uint32_t buf_addr_lsw; + uint32_t buf_addr_msw; + uint32_t mem_map_handle; + uint32_t total_size; + uint32_t offset; + uint32_t timestamp_lsw; + uint32_t timestamp_msw; + uint32_t flags; +} __packed; + +struct lsm_param_det_event_type { + uint32_t minor_version; + uint32_t event_type; + uint32_t mode; +} __packed; + +struct lsm_client *q6lsm_client_alloc(lsm_app_cb cb, void *priv); +void q6lsm_client_free(struct lsm_client *client); +int q6lsm_open(struct lsm_client *client, uint16_t app_id); +int q6lsm_start(struct lsm_client *client, bool wait); +int q6lsm_stop(struct lsm_client *client, bool wait); +int q6lsm_snd_model_buf_alloc(struct lsm_client *client, size_t len, + struct lsm_params_info_v2 *p_info); +int q6lsm_snd_model_buf_free(struct lsm_client *client, + struct lsm_params_info_v2 *p_info); +int q6lsm_close(struct lsm_client *client); +int q6lsm_register_sound_model(struct lsm_client *client, + enum lsm_detection_mode mode, + bool detectfailure); +int q6lsm_set_data(struct lsm_client *client, + enum lsm_detection_mode mode, + bool detectfailure); +int q6lsm_deregister_sound_model(struct lsm_client *client); +void set_lsm_port(int lsm_port); +int get_lsm_port(void); +int q6lsm_lab_control(struct lsm_client *client, u32 enable, + struct lsm_params_info_v2 *p_info); +int q6lsm_stop_lab(struct lsm_client *client); +int q6lsm_read(struct lsm_client *client, struct lsm_cmd_read *read); +int q6lsm_lab_buffer_alloc(struct lsm_client *client, bool alloc); +int q6lsm_set_one_param(struct lsm_client *client, + struct lsm_params_info_v2 *p_info, void *data, + uint32_t param_type); +void q6lsm_sm_set_param_data(struct lsm_client *client, + struct lsm_params_info_v2 *p_info, + size_t *offset); +int q6lsm_set_port_connected(struct lsm_client *client); +int q6lsm_set_fwk_mode_cfg(struct lsm_client *client, uint32_t event_mode); +int q6lsm_set_media_fmt_params(struct lsm_client *client); +int q6lsm_set_media_fmt_v2_params(struct lsm_client *client); +int q6lsm_lab_out_ch_cfg(struct lsm_client *client, u8 *ch_map, + struct lsm_params_info_v2 *p_info); +bool q6lsm_adsp_supports_multi_stage_detection(void); +#endif /* __Q6LSM_H__ */ diff --git a/audio-kernel/include/dsp/q6voice.h b/audio-kernel/include/dsp/q6voice.h new file mode 100644 index 000000000..ca4250872 --- /dev/null +++ b/audio-kernel/include/dsp/q6voice.h @@ -0,0 +1,2096 @@ +/* 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 + * 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 __QDSP6VOICE_H__ +#define __QDSP6VOICE_H__ + +#include +#include +#include +#include +#include + +#define MAX_VOC_PKT_SIZE 642 +#define SESSION_NAME_LEN 20 +#define NUM_OF_MEMORY_BLOCKS 1 +#define NUM_OF_BUFFERS 2 +#define VSS_NUM_CHANNELS_MAX 32 +#define VSS_CHANNEL_MAPPING_SIZE (sizeof(uint8_t) * VSS_NUM_CHANNELS_MAX) +/* + * BUFFER BLOCK SIZE based on + * the supported page size + */ +#define BUFFER_BLOCK_SIZE 4096 + +#define MAX_COL_INFO_SIZE 324 + +#define VOC_REC_UPLINK 0x00 +#define VOC_REC_DOWNLINK 0x01 +#define VOC_REC_BOTH 0x02 + +#define VSS_IVERSION_CMD_GET 0x00011378 +#define VSS_IVERSION_RSP_GET 0x00011379 +#define CVD_VERSION_STRING_MAX_SIZE 31 +#define CVD_VERSION_DEFAULT "" +#define CVD_VERSION_0_0 "0.0" +#define CVD_VERSION_2_1 "2.1" +#define CVD_VERSION_2_2 "2.2" +#define CVD_VERSION_2_3 "2.3" + +#define CVD_INT_VERSION_DEFAULT 0 +#define CVD_INT_VERSION_0_0 1 +#define CVD_INT_VERSION_2_1 2 +#define CVD_INT_VERSION_2_2 3 +#define CVD_INT_VERSION_2_3 4 +#define CVD_INT_VERSION_LAST CVD_INT_VERSION_2_3 +#define CVD_INT_VERSION_MAX (CVD_INT_VERSION_LAST + 1) + +struct cvd_version_table { + char cvd_ver[CVD_VERSION_STRING_MAX_SIZE]; + int cvd_ver_int; +}; + +int voc_get_cvd_version(char *cvd_version); + +/* Payload structure for the VSS_IVERSION_RSP_GET command response */ +struct vss_iversion_rsp_get_t { + char version[CVD_VERSION_STRING_MAX_SIZE]; + /* NULL-terminated version string */ +}; + +enum { + CVP_VOC_RX_TOPOLOGY_CAL = 0, + CVP_VOC_TX_TOPOLOGY_CAL, + CVP_VOCPROC_CAL, + CVP_VOCVOL_CAL, + CVP_VOCDEV_CFG_CAL, + CVP_VOCPROC_COL_CAL, + CVP_VOCVOL_COL_CAL, + CVS_VOCSTRM_CAL, + CVS_VOCSTRM_COL_CAL, + VOICE_RTAC_INFO_CAL, + VOICE_RTAC_APR_CAL, + MAX_VOICE_CAL_TYPES +}; + +struct voice_header { + uint32_t id; + uint32_t data_len; +}; + +struct voice_init { + struct voice_header hdr; + void *cb_handle; +}; + +/* Stream information payload structure */ +struct stream_data { + uint32_t stream_mute; + uint32_t stream_mute_ramp_duration_ms; +}; + +/* Device information payload structure */ +struct device_data { + uint32_t dev_mute; + uint32_t sample_rate; + uint16_t bits_per_sample; + uint8_t channel_mapping[VSS_NUM_CHANNELS_MAX]; + uint32_t enabled; + uint32_t dev_id; + uint32_t port_id; + uint32_t volume_step_value; + uint32_t volume_ramp_duration_ms; + uint32_t dev_mute_ramp_duration_ms; + uint32_t no_of_channels; +}; + +/* + * Format information structure to match + * vss_param_endpoint_media_format_info_t + */ +struct media_format_info { + uint32_t port_id; + uint16_t num_channels; + uint16_t bits_per_sample; + uint32_t sample_rate; + uint8_t channel_mapping[VSS_NUM_CHANNELS_MAX]; +}; + +enum { + VOC_GENERIC_SET_PARAM_TOKEN = 0, + VOC_RTAC_SET_PARAM_TOKEN, + VOC_SET_MEDIA_FORMAT_PARAM_TOKEN, + VOC_SET_PARAM_TOKEN_MAX +}; + +struct voice_dev_route_state { + u16 rx_route_flag; + u16 tx_route_flag; +}; + +struct voice_rec_route_state { + u16 ul_flag; + u16 dl_flag; +}; + +enum { + VOC_INIT = 0, + VOC_RUN, + VOC_CHANGE, + VOC_RELEASE, + VOC_ERROR, + VOC_STANDBY, +}; + +struct mem_buffer { + dma_addr_t phys; + void *data; + uint32_t size; /* size of buffer */ +}; + +struct share_mem_buf { + struct dma_buf *dma_buf; + struct mem_buffer buf[NUM_OF_BUFFERS]; +}; + +struct mem_map_table { + dma_addr_t phys; + void *data; + size_t size; /* size of buffer */ + struct dma_buf *dma_buf; +}; + +/* Common */ +#define VSS_ICOMMON_CMD_SET_UI_PROPERTY 0x00011103 +#define VSS_ICOMMON_CMD_SET_UI_PROPERTY_V2 0x00013248 +/* Set a UI property */ +#define VSS_ICOMMON_CMD_MAP_MEMORY 0x00011025 +#define VSS_ICOMMON_CMD_UNMAP_MEMORY 0x00011026 +/* General shared memory; byte-accessible, 4 kB-aligned. */ +#define VSS_ICOMMON_MAP_MEMORY_SHMEM8_4K_POOL 3 + +struct vss_icommon_cmd_map_memory_t { + uint32_t phys_addr; + /* Physical address of a memory region; must be at least + * 4 kB aligned. + */ + + uint32_t mem_size; + /* Number of bytes in the region; should be a multiple of 32. */ + + uint16_t mem_pool_id; + /* Type of memory being provided. The memory ID implicitly defines + * the characteristics of the memory. The characteristics might include + * alignment type, permissions, etc. + * Memory pool ID. Possible values: + * 3 -- VSS_ICOMMON_MEM_TYPE_SHMEM8_4K_POOL. + */ +} __packed; + +struct vss_icommon_cmd_unmap_memory_t { + uint32_t phys_addr; + /* Physical address of a memory region; must be at least + * 4 kB aligned. + */ +} __packed; + +struct vss_map_memory_cmd { + struct apr_hdr hdr; + struct vss_icommon_cmd_map_memory_t vss_map_mem; +} __packed; + +struct vss_unmap_memory_cmd { + struct apr_hdr hdr; + struct vss_icommon_cmd_unmap_memory_t vss_unmap_mem; +} __packed; + +struct vss_param_endpoint_media_format_info { + /* AFE port ID to which this media format corresponds to. */ + uint32_t port_id; + /* + * Number of channels of data. + * Supported values: 1 to 8 + */ + uint16_t num_channels; + /* + * Bits per sample of data. + * Supported values: 16 and 24 + */ + uint16_t bits_per_sample; + /* + * Sampling rate in Hz. + * Supported values: 8000, 11025, 16000, 22050, 24000, 32000, + * 44100, 48000, 88200, 96000, 176400, and 192000 + */ + uint32_t sample_rate; + /* + * The channel[i] mapping describes channel i. Each element i + * of the array describes channel i inside the data buffer. An + * unused or unknown channel is set to 0. + */ + uint8_t channel_mapping[VSS_NUM_CHANNELS_MAX]; +} __packed; + +struct vss_param_vocproc_dev_channel_info_t { + uint32_t num_channels; + uint32_t bits_per_sample; + uint8_t channel_mapping[VSS_NUM_CHANNELS_MAX]; +} __packed; + +struct vss_param_channel_mixer_info_t { + uint32_t index; + uint16_t num_output_channels; + uint16_t num_input_channels; + uint16_t out_channel_map[2]; + uint16_t in_channel_map[1]; + uint16_t channel_weight_coeff[2][1]; + uint16_t reserved; +} __packed; + +struct vss_param_mfc_config_info_t { + uint32_t sample_rate; + uint16_t bits_per_sample; + uint16_t num_channels; + uint16_t channel_type[VSS_NUM_CHANNELS_MAX]; +} __packed; + +struct vss_icommon_param_data_channel_info_v2_t { + /* Valid ID of the module. */ + uint32_t module_id; + /* Valid ID of the parameter. */ + uint32_t param_id; + /* + * Data size of the structure relating to the param_id/module_id + * combination in uint8_t bytes. + */ + uint16_t param_size; + /* This field must be set to zero. */ + uint16_t reserved; + struct vss_param_vocproc_dev_channel_info_t channel_info; +} __packed; + +struct vss_icommon_cmd_set_param_channel_info_v2_t { + /* + * Pointer to the unique identifier for an address (physical/virtual). + * + * If the parameter data payload is within the message payload + * (in-band), set this field to 0. The parameter data begins at the + * specified data payload address. + * + * If the parameter data is out-of-band, this field is the handle to + * the physical address in the shared memory that holds the parameter + * data. + */ + uint32_t mem_handle; + /* + * Location of the parameter data payload. + * + * The payload is an array of vss_icommon_param_data_t. If the + * mem_handle is 0, this field is ignored. + */ + uint64_t mem_address; + /* Size of the parameter data payload in bytes. */ + uint32_t mem_size; + struct vss_icommon_param_data_channel_info_v2_t param_data; +} __packed; + +struct vss_icommon_param_data_ch_mixer_v2_t { + /* Valid ID of the module. */ + uint32_t module_id; + /* Valid ID of the parameter. */ + uint32_t param_id; + /* + * Data size of the structure relating to the param_id/module_id + * combination in uint8_t bytes. + */ + uint16_t param_size; + /* This field must be set to zero. */ + uint16_t reserved; + struct vss_param_channel_mixer_info_t ch_mixer_info; +} __packed; + +struct vss_icommon_cmd_set_param_ch_mixer_v2_t { + /* + * Pointer to the unique identifier for an address (physical/virtual). + * + * If the parameter data payload is within the message payload + * (in-band), set this field to 0. The parameter data begins at the + * specified data payload address. + * + * If the parameter data is out-of-band, this field is the handle to + * the physical address in the shared memory that holds the parameter + * data. + */ + uint32_t mem_handle; + /* + * Location of the parameter data payload. + * + * The payload is an array of vss_icommon_param_data_t. If the + * mem_handle is 0, this field is ignored. + */ + uint64_t mem_address; + /* Size of the parameter data payload in bytes. */ + uint32_t mem_size; + + struct vss_icommon_param_data_ch_mixer_v2_t param_data; +} __packed; + +struct vss_icommon_param_data_mfc_config_v2_t { + /* Valid ID of the module. */ + uint32_t module_id; + /* Valid ID of the parameter. */ + uint32_t param_id; + /* + * Data size of the structure relating to the param_id/module_id + * combination in uint8_t bytes. + */ + uint16_t param_size; + /* This field must be set to zero. */ + uint16_t reserved; + struct vss_param_mfc_config_info_t mfc_config_info; +} __packed; + +struct vss_icommon_cmd_set_param_mfc_config_v2_t { + /* + * Pointer to the unique identifier for an address (physical/virtual). + * + * If the parameter data payload is within the message payload + * (in-band), set this field to 0. The parameter data begins at the + * specified data payload address. + * + * If the parameter data is out-of-band, this field is the handle to + * the physical address in the shared memory that holds the parameter + * data. + */ + + uint32_t mem_handle; + /* + * Location of the parameter data payload. + * + * The payload is an array of vss_icommon_param_data_t. If the + * mem_handle is 0, this field is ignored. + */ + uint64_t mem_address; + /* Size of the parameter data payload in bytes. */ + uint32_t mem_size; + + struct vss_icommon_param_data_mfc_config_v2_t param_data; +} __packed; + +struct vss_icommon_mem_mapping_hdr { + /* + * Pointer to the unique identifier for an address (physical/virtual). + * + * If the parameter data payload is within the message payload + * (in-band), set this field to 0. The parameter data begins at the + * specified data payload address. + * + * If the parameter data is out-of-band, this field is the handle to + * the physical address in the shared memory that holds the parameter + * data. + */ + uint32_t mem_handle; + /* + * Location of the parameter data payload. + * + * The payload is an array of vss_icommon_param_data_t. If the + * mem_handle is 0, this field is ignored. + */ + uint64_t mem_address; +} __packed; + +struct vss_icommon_cmd_set_param { + /* APR Header */ + struct apr_hdr apr_hdr; + + /* The memory mapping header to be used when sending outband */ + struct vss_icommon_mem_mapping_hdr mem_hdr; + + /* Size of the parameter data payload in bytes. */ + uint32_t payload_size; + + /* + * Parameter data payload when inband. Should have size param_size. + * Bit size of payload must be a multiple of 4. + */ + uint8_t param_data[0]; +} __packed; + +/* TO MVM commands */ +#define VSS_IMVM_CMD_CREATE_PASSIVE_CONTROL_SESSION 0x000110FF +/**< No payload. Wait for APRV2_IBASIC_RSP_RESULT response. */ + +#define VSS_IMVM_CMD_SET_POLICY_DUAL_CONTROL 0x00011327 +/* + * VSS_IMVM_CMD_SET_POLICY_DUAL_CONTROL + * Description: This command is required to let MVM know + * who is in control of session. + * Payload: Defined by vss_imvm_cmd_set_policy_dual_control_t. + * Result: Wait for APRV2_IBASIC_RSP_RESULT response. + */ + +#define VSS_IMVM_CMD_CREATE_FULL_CONTROL_SESSION 0x000110FE +/* Create a new full control MVM session. */ + +#define APRV2_IBASIC_CMD_DESTROY_SESSION 0x0001003C +/**< No payload. Wait for APRV2_IBASIC_RSP_RESULT response. */ + +#define VSS_IMVM_CMD_ATTACH_STREAM 0x0001123C +/* Attach a stream to the MVM. */ + +#define VSS_IMVM_CMD_DETACH_STREAM 0x0001123D +/* Detach a stream from the MVM. */ + +#define VSS_IMVM_CMD_ATTACH_VOCPROC 0x0001123E +/* Attach a vocproc to the MVM. The MVM will symmetrically connect this vocproc + * to all the streams currently attached to it. + */ + +#define VSS_IMVM_CMD_DETACH_VOCPROC 0x0001123F +/* Detach a vocproc from the MVM. The MVM will symmetrically disconnect this + * vocproc from all the streams to which it is currently attached. + */ + +#define VSS_IMVM_CMD_START_VOICE 0x00011190 +/**< No payload. Wait for APRV2_IBASIC_RSP_RESULT response. */ + +#define VSS_IMVM_CMD_STANDBY_VOICE 0x00011191 +/**< No payload. Wait for APRV2_IBASIC_RSP_RESULT response. */ + +#define VSS_IMVM_CMD_STOP_VOICE 0x00011192 +/**< No payload. Wait for APRV2_IBASIC_RSP_RESULT response. */ + +#define VSS_IMVM_CMD_PAUSE_VOICE 0x0001137D +/* No payload. Wait for APRV2_IBASIC_RSP_RESULT response. */ + +#define VSS_ISTREAM_CMD_ATTACH_VOCPROC 0x000110F8 +/**< Wait for APRV2_IBASIC_RSP_RESULT response. */ + +#define VSS_ISTREAM_CMD_DETACH_VOCPROC 0x000110F9 +/**< Wait for APRV2_IBASIC_RSP_RESULT response. */ + + +#define VSS_ISTREAM_CMD_SET_TTY_MODE 0x00011196 +/**< Wait for APRV2_IBASIC_RSP_RESULT response. */ + +#define VSS_ICOMMON_CMD_SET_NETWORK 0x0001119C +/* Set the network type. */ + +#define VSS_ICOMMON_CMD_SET_VOICE_TIMING 0x000111E0 +/* Set the voice timing parameters. */ + +#define VSS_IMEMORY_CMD_MAP_PHYSICAL 0x00011334 +#define VSS_IMEMORY_RSP_MAP 0x00011336 +#define VSS_IMEMORY_CMD_UNMAP 0x00011337 +#define VSS_IMVM_CMD_SET_CAL_NETWORK 0x0001137A +#define VSS_IMVM_CMD_SET_CAL_MEDIA_TYPE 0x0001137B +#define VSS_IHDVOICE_CMD_ENABLE 0x000130A2 +#define VSS_IHDVOICE_CMD_DISABLE 0x000130A3 + +/* To listen for events from MVM module */ +#define VSS_INOTIFY_CMD_LISTEN_FOR_EVENT_CLASS 0x00012E56 +/* To cancel listening for events from MVM module */ +#define VSS_INOTIFY_CMD_CANCEL_EVENT_CLASS 0x00012E57 +/* To receive ongoing voice activity notification */ +#define VSS_ICOMMON_EVENT_CLASS_VOICE_ACTIVITY_UPDATE 0x000131EF +/* Voice activity notification from MVM */ +#define VSS_ICOMMON_EVT_VOICE_ACTIVITY_UPDATE 0x000131F0 +/* Mic path is broken. */ +#define VSS_ICOMMON_VOICE_ACTIVITY_MIC_BREAK 0x000131F3 +/* Mic path is restored. */ +#define VSS_ICOMMON_VOICE_ACITIVTY_MIC_UNBREAK 0x000131F4 + +enum msm_audio_voc_rate { + VOC_0_RATE, /* Blank frame */ + VOC_8_RATE, /* 1/8 rate */ + VOC_4_RATE, /* 1/4 rate */ + VOC_2_RATE, /* 1/2 rate */ + VOC_1_RATE, /* Full rate */ + VOC_8_RATE_NC /* Noncritical 1/8 rate */ +}; + +struct vss_istream_cmd_set_tty_mode_t { + uint32_t mode; + /**< + * TTY mode. + * + * 0 : TTY disabled + * 1 : HCO + * 2 : VCO + * 3 : FULL + */ +} __packed; + +struct vss_istream_cmd_attach_vocproc_t { + uint16_t handle; + /**< Handle of vocproc being attached. */ +} __packed; + +struct vss_istream_cmd_detach_vocproc_t { + uint16_t handle; + /**< Handle of vocproc being detached. */ +} __packed; + +struct vss_imvm_cmd_attach_stream_t { + uint16_t handle; + /* The stream handle to attach. */ +} __packed; + +struct vss_imvm_cmd_detach_stream_t { + uint16_t handle; + /* The stream handle to detach. */ +} __packed; + +struct vss_icommon_cmd_set_network_t { + uint32_t network_id; + /* Network ID. (Refer to VSS_NETWORK_ID_XXX). */ +} __packed; + +struct vss_icommon_cmd_set_voice_timing_t { + uint16_t mode; + /* + * The vocoder frame synchronization mode. + * + * 0 : No frame sync. + * 1 : Hard VFR (20ms Vocoder Frame Reference interrupt). + */ + uint16_t enc_offset; + /* + * The offset in microseconds from the VFR to deliver a Tx vocoder + * packet. The offset should be less than 20000us. + */ + uint16_t dec_req_offset; + /* + * The offset in microseconds from the VFR to request for an Rx vocoder + * packet. The offset should be less than 20000us. + */ + uint16_t dec_offset; + /* + * The offset in microseconds from the VFR to indicate the deadline to + * receive an Rx vocoder packet. The offset should be less than 20000us. + * Rx vocoder packets received after this deadline are not guaranteed to + * be processed. + */ +} __packed; + +struct vss_imvm_cmd_create_control_session_t { + char name[SESSION_NAME_LEN]; + /* + * A variable-sized stream name. + * + * The stream name size is the payload size minus the size of the other + * fields. + */ +} __packed; + + +struct vss_imvm_cmd_set_policy_dual_control_t { + bool enable_flag; + /* Set to TRUE to enable modem state machine control */ +} __packed; + +struct mvm_attach_vocproc_cmd { + struct apr_hdr hdr; + struct vss_istream_cmd_attach_vocproc_t mvm_attach_cvp_handle; +} __packed; + +struct mvm_detach_vocproc_cmd { + struct apr_hdr hdr; + struct vss_istream_cmd_detach_vocproc_t mvm_detach_cvp_handle; +} __packed; + +struct mvm_create_ctl_session_cmd { + struct apr_hdr hdr; + struct vss_imvm_cmd_create_control_session_t mvm_session; +} __packed; + +struct mvm_modem_dual_control_session_cmd { + struct apr_hdr hdr; + struct vss_imvm_cmd_set_policy_dual_control_t voice_ctl; +} __packed; + +struct mvm_set_tty_mode_cmd { + struct apr_hdr hdr; + struct vss_istream_cmd_set_tty_mode_t tty_mode; +} __packed; + +struct mvm_attach_stream_cmd { + struct apr_hdr hdr; + struct vss_imvm_cmd_attach_stream_t attach_stream; +} __packed; + +struct mvm_detach_stream_cmd { + struct apr_hdr hdr; + struct vss_imvm_cmd_detach_stream_t detach_stream; +} __packed; + +struct mvm_set_network_cmd { + struct apr_hdr hdr; + struct vss_icommon_cmd_set_network_t network; +} __packed; + +struct mvm_set_voice_timing_cmd { + struct apr_hdr hdr; + struct vss_icommon_cmd_set_voice_timing_t timing; +} __packed; + +struct mvm_set_hd_enable_cmd { + struct apr_hdr hdr; +} __packed; + +struct vss_imemory_table_descriptor_t { + uint32_t mem_address_lsw; + uint32_t mem_address_msw; + /* + * Base physical address of the table. The address must be aligned + * to LCM( cache_line_size, page_align, max_data_width ), where the + * attributes are specified in #VSS_IMEMORY_CMD_MAP_PHYSICAL, and + * LCM = Least Common Multiple. The table at the address must have + * the format specified by #vss_imemory_table_t. + */ + uint32_t mem_size; + /* Size in bytes of the table. */ +} __packed; + +struct vss_imemory_block_t { + uint64_t mem_address; + /* + * Base address of the memory block. The address is virtual for virtual + * memory and physical for physical memory. The address must be aligned + * to LCM( cache_line_size, page_align, max_data_width ), where the + * attributes are specified in VSS_IMEMORY_CMD_MAP_VIRTUAL or + * VSS_IMEMORY_CMD_MAP_PHYSICAL, and LCM = Least Common Multiple. + */ + uint32_t mem_size; + /* + * Size in bytes of the memory block. The size must be multiple of + * page_align, where page_align is specified in + * VSS_IMEMORY_CMD_MAP_VIRTUAL or #VSS_IMEMORY_CMD_MAP_PHYSICAL. + */ +} __packed; + +struct vss_imemory_table_t { + struct vss_imemory_table_descriptor_t next_table_descriptor; + /* + * Specifies the next table. If there is no next table, + * set the size of the table to 0 and the table address is ignored. + */ + struct vss_imemory_block_t blocks[NUM_OF_MEMORY_BLOCKS]; + /* Specifies one ore more memory blocks. */ +} __packed; + +struct vss_imemory_cmd_map_physical_t { + struct apr_hdr hdr; + struct vss_imemory_table_descriptor_t table_descriptor; + bool is_cached; + /* + * Indicates cached or uncached memory. Supported values: + * TRUE - Cached. + */ + uint16_t cache_line_size; + /* Cache line size in bytes. Supported values: 128 */ + uint32_t access_mask; + /* + * CVD's access permission to the memory while it is mapped. + * Supported values: + * bit 0 - If set, the memory is readable. + * bit 1 - If set, the memory is writable. + */ + uint32_t page_align; + /* Page frame alignment in bytes. Supported values: 4096 */ + uint8_t min_data_width; + /* + * Minimum native data type width in bits that can be accessed. + * Supported values: 8 + */ + uint8_t max_data_width; + /* + * Maximum native data type width in bits that can be accessed. + * Supported values: 64 + */ +} __packed; + +struct vss_imvm_cmd_set_cal_network_t { + struct apr_hdr hdr; + uint32_t network_id; +} __packed; + +struct vss_imvm_cmd_set_cal_media_type_t { + struct apr_hdr hdr; + uint32_t media_id; +} __packed; + +struct vss_imemory_cmd_unmap_t { + struct apr_hdr hdr; + uint32_t mem_handle; +} __packed; + +/* + * Payload structure for VSS_INOTIFY_CMD_LISTEN_FOR_EVENT_CLASS and + * VSS_INOTIFY_CMD_CANCEL_EVENT_CLASS commands. + */ +struct vss_inotify_cmd_event_class_t { + struct apr_hdr hdr; + /* Event class ID to listen for. */ + uint32_t class_id; +} __packed; + +/* Payload structure for the VSS_ICOMMOM_EVT_VOICE_ACTIVITY_UPDATE event. */ +struct vss_evt_voice_activity { + uint32_t activity; + /* + * Specifies the voice acitivity. + * @values + * #VSS_ICOMMON_VOICE_ACTIVITY_VPTX_MUTE + * #VSS_ICOMMON_VOICE_ACTIVITY_VPTX_UNMUTE + * #VSS_ICOMMON_VOICE_ACTIVITY_MIC_BREAK + * #VSS_ICOMMON_VOICE_ACITIVTY_MIC_UNBREAK + * #VSS_ICOMMON_VOICE_ACTIVITY_UI_STREAM_TX_MUTE + * #VSS_ICOMMON_VOICE_ACTIVITY_UI_STREAM_TX_UNMUTE + * #VSS_ICOMMON_VOICE_ACTIVITY_UI_VOCPROC_TX_MUTE + * #VSS_ICOMMON_VOICE_ACTIVITY_UI_VOCPROC_TX_UNMUTE + */ +} __packed; + +/* TO CVS commands */ +#define VSS_ISTREAM_CMD_CREATE_PASSIVE_CONTROL_SESSION 0x00011140 +/**< Wait for APRV2_IBASIC_RSP_RESULT response. */ + +#define VSS_ISTREAM_CMD_CREATE_FULL_CONTROL_SESSION 0x000110F7 +/* Create a new full control stream session. */ + +#define APRV2_IBASIC_CMD_DESTROY_SESSION 0x0001003C + +/* + * This command changes the mute setting. The new mute setting will + * be applied over the specified ramp duration. + */ +#define VSS_IVOLUME_CMD_MUTE_V2 0x0001138B + +#define VSS_ISTREAM_CMD_REGISTER_CALIBRATION_DATA_V2 0x00011369 + +#define VSS_ISTREAM_CMD_DEREGISTER_CALIBRATION_DATA 0x0001127A + +#define VSS_ISTREAM_CMD_REGISTER_STATIC_CALIBRATION_DATA 0x0001307D +#define VSS_ISTREAM_CMD_DEREGISTER_STATIC_CALIBRATION_DATA 0x0001307E + +#define VSS_ISTREAM_CMD_SET_MEDIA_TYPE 0x00011186 +/* Set media type on the stream. */ + +#define VSS_ISTREAM_EVT_SEND_ENC_BUFFER 0x00011015 +/* Event sent by the stream to its client to provide an encoded packet. */ + +#define VSS_ISTREAM_EVT_REQUEST_DEC_BUFFER 0x00011017 +/* Event sent by the stream to its client requesting for a decoder packet. + * The client should respond with a VSS_ISTREAM_EVT_SEND_DEC_BUFFER event. + */ + +#define VSS_ISTREAM_EVT_OOB_NOTIFY_DEC_BUFFER_REQUEST 0x0001136E + +#define VSS_ISTREAM_EVT_SEND_DEC_BUFFER 0x00011016 +/* Event sent by the client to the stream in response to a + * VSS_ISTREAM_EVT_REQUEST_DEC_BUFFER event, providing a decoder packet. + */ + +#define VSS_ISTREAM_CMD_VOC_AMR_SET_ENC_RATE 0x0001113E +/* Set AMR encoder rate. */ + +#define VSS_ISTREAM_CMD_VOC_AMRWB_SET_ENC_RATE 0x0001113F +/* Set AMR-WB encoder rate. */ + +#define VSS_ISTREAM_CMD_CDMA_SET_ENC_MINMAX_RATE 0x00011019 +/* Set encoder minimum and maximum rate. */ + +#define VSS_ISTREAM_CMD_SET_ENC_DTX_MODE 0x0001101D +/* Set encoder DTX mode. */ + +#define MODULE_ID_VOICE_MODULE_ST 0x00010EE3 +#define VOICE_PARAM_MOD_ENABLE 0x00010E00 + +#define VSS_IPLAYBACK_CMD_START 0x000112BD +/* Start in-call music delivery on the Tx voice path. */ + +#define VSS_IPLAYBACK_CMD_STOP 0x00011239 +/* Stop the in-call music delivery on the Tx voice path. */ + +#define VSS_IPLAYBACK_PORT_ID_DEFAULT 0x0000FFFF +/* Default AFE port ID. */ + +#define VSS_IPLAYBACK_PORT_ID_VOICE 0x00008005 +/* AFE port ID for VOICE 1. */ + +#define VSS_IPLAYBACK_PORT_ID_VOICE2 0x00008002 +/* AFE port ID for VOICE 2. */ + +#define VSS_IRECORD_CMD_START 0x000112BE +/* Start in-call conversation recording. */ +#define VSS_IRECORD_CMD_STOP 0x00011237 +/* Stop in-call conversation recording. */ + +#define VSS_IRECORD_PORT_ID_DEFAULT 0x0000FFFF +/* Default AFE port ID. */ + +#define VSS_IRECORD_TAP_POINT_NONE 0x00010F78 +/* Indicates no tapping for specified path. */ + +#define VSS_IRECORD_TAP_POINT_STREAM_END 0x00010F79 +/* Indicates that specified path should be tapped at the end of the stream. */ + +#define VSS_IRECORD_MODE_TX_RX_STEREO 0x00010F7A +/* Select Tx on left channel and Rx on right channel. */ + +#define VSS_IRECORD_MODE_TX_RX_MIXING 0x00010F7B +/* Select mixed Tx and Rx paths. */ + +#define VSS_PARAM_VOCPROC_TX_CHANNEL_INFO 0x0001328E + +#define VSS_PARAM_VOCPROC_RX_CHANNEL_INFO 0x0001328F + +#define VSS_PARAM_VOCPROC_EC_REF_CHANNEL_INFO 0x00013290 + +#define VSS_PARAM_TX_PORT_ENDPOINT_MEDIA_INFO 0x00013253 + +#define VSS_PARAM_RX_PORT_ENDPOINT_MEDIA_INFO 0x00013254 + +#define VSS_PARAM_EC_REF_PORT_ENDPOINT_MEDIA_INFO 0x00013255 + +#define VSS_MODULE_CVD_GENERIC 0x0001316E + +#define VSS_ISTREAM_EVT_NOT_READY 0x000110FD + +#define VSS_ISTREAM_EVT_READY 0x000110FC + +#define VSS_ISTREAM_EVT_OOB_NOTIFY_DEC_BUFFER_READY 0x0001136F +/*notify dsp that decoder buffer is ready*/ + +#define VSS_ISTREAM_EVT_OOB_NOTIFY_ENC_BUFFER_READY 0x0001136C +/*dsp notifying client that encoder buffer is ready*/ + +#define VSS_ISTREAM_EVT_OOB_NOTIFY_ENC_BUFFER_CONSUMED 0x0001136D +/*notify dsp that encoder buffer is consumed*/ + +#define VSS_ISTREAM_CMD_SET_OOB_PACKET_EXCHANGE_CONFIG 0x0001136B + +#define VSS_ISTREAM_PACKET_EXCHANGE_MODE_INBAND 0 +/* In-band packet exchange mode. */ + +#define VSS_ISTREAM_PACKET_EXCHANGE_MODE_OUT_OF_BAND 1 +/* Out-of-band packet exchange mode. */ + +#define VSS_ISTREAM_CMD_SET_PACKET_EXCHANGE_MODE 0x0001136A + +struct vss_iplayback_cmd_start_t { + uint16_t port_id; + /* + * AFE Port ID from which the audio samples are available. + * To use the default AFE pseudo port (0x8005), set this value to + * #VSS_IPLAYBACK_PORT_ID_DEFAULT. + */ +} __packed; + +struct vss_irecord_cmd_start_t { + uint32_t rx_tap_point; + /* Tap point to use on the Rx path. Supported values are: + * VSS_IRECORD_TAP_POINT_NONE : Do not record Rx path. + * VSS_IRECORD_TAP_POINT_STREAM_END : Rx tap point is at the end of + * the stream. + */ + uint32_t tx_tap_point; + /* Tap point to use on the Tx path. Supported values are: + * VSS_IRECORD_TAP_POINT_NONE : Do not record tx path. + * VSS_IRECORD_TAP_POINT_STREAM_END : Tx tap point is at the end of + * the stream. + */ + uint16_t port_id; + /* AFE Port ID to which the conversation recording stream needs to be + * sent. Set this to #VSS_IRECORD_PORT_ID_DEFAULT to use default AFE + * pseudo ports (0x8003 for Rx and 0x8004 for Tx). + */ + uint32_t mode; + /* Recording Mode. The mode parameter value is ignored if the port_id + * is set to #VSS_IRECORD_PORT_ID_DEFAULT. + * The supported values: + * #VSS_IRECORD_MODE_TX_RX_STEREO + * #VSS_IRECORD_MODE_TX_RX_MIXING + */ +} __packed; + +struct vss_istream_cmd_create_passive_control_session_t { + char name[SESSION_NAME_LEN]; + /**< + * A variable-sized stream name. + * + * The stream name size is the payload size minus the size of the other + * fields. + */ +} __packed; + +#define VSS_IVOLUME_DIRECTION_TX 0 +#define VSS_IVOLUME_DIRECTION_RX 1 + +#define VSS_IVOLUME_MUTE_OFF 0 +#define VSS_IVOLUME_MUTE_ON 1 + +#define DEFAULT_MUTE_RAMP_DURATION 500 +#define DEFAULT_VOLUME_RAMP_DURATION 20 +#define MAX_RAMP_DURATION 5000 + +struct vss_ivolume_cmd_mute_v2_t { + uint16_t direction; + /* + * The direction field sets the direction to apply the mute command. + * The Supported values: + * VSS_IVOLUME_DIRECTION_TX + * VSS_IVOLUME_DIRECTION_RX + */ + uint16_t mute_flag; + /* + * Turn mute on or off. The Supported values: + * VSS_IVOLUME_MUTE_OFF + * VSS_IVOLUME_MUTE_ON + */ + uint16_t ramp_duration_ms; + /* + * Mute change ramp duration in milliseconds. + * The Supported values: 0 to 5000. + */ +} __packed; + +struct vss_istream_cmd_create_full_control_session_t { + uint16_t direction; + /* + * Stream direction. + * + * 0 : TX only + * 1 : RX only + * 2 : TX and RX + * 3 : TX and RX loopback + */ + uint32_t enc_media_type; + /* Tx vocoder type. (Refer to VSS_MEDIA_ID_XXX). */ + uint32_t dec_media_type; + /* Rx vocoder type. (Refer to VSS_MEDIA_ID_XXX). */ + uint32_t network_id; + /* Network ID. (Refer to VSS_NETWORK_ID_XXX). */ + char name[SESSION_NAME_LEN]; + /* + * A variable-sized stream name. + * + * The stream name size is the payload size minus the size of the other + * fields. + */ +} __packed; + +struct vss_istream_cmd_set_media_type_t { + uint32_t rx_media_id; + /* Set the Rx vocoder type. (Refer to VSS_MEDIA_ID_XXX). */ + uint32_t tx_media_id; + /* Set the Tx vocoder type. (Refer to VSS_MEDIA_ID_XXX). */ +} __packed; + +struct vss_istream_evt_send_enc_buffer_t { + uint32_t media_id; + /* Media ID of the packet. */ + uint8_t packet_data[MAX_VOC_PKT_SIZE]; + /* Packet data buffer. */ +} __packed; + +struct vss_istream_evt_send_dec_buffer_t { + uint32_t media_id; + /* Media ID of the packet. */ + uint8_t packet_data[MAX_VOC_PKT_SIZE]; + /* Packet data. */ +} __packed; + +struct vss_istream_cmd_voc_amr_set_enc_rate_t { + uint32_t mode; + /* Set the AMR encoder rate. + * + * 0x00000000 : 4.75 kbps + * 0x00000001 : 5.15 kbps + * 0x00000002 : 5.90 kbps + * 0x00000003 : 6.70 kbps + * 0x00000004 : 7.40 kbps + * 0x00000005 : 7.95 kbps + * 0x00000006 : 10.2 kbps + * 0x00000007 : 12.2 kbps + */ +} __packed; + +struct vss_istream_cmd_voc_amrwb_set_enc_rate_t { + uint32_t mode; + /* Set the AMR-WB encoder rate. + * + * 0x00000000 : 6.60 kbps + * 0x00000001 : 8.85 kbps + * 0x00000002 : 12.65 kbps + * 0x00000003 : 14.25 kbps + * 0x00000004 : 15.85 kbps + * 0x00000005 : 18.25 kbps + * 0x00000006 : 19.85 kbps + * 0x00000007 : 23.05 kbps + * 0x00000008 : 23.85 kbps + */ +} __packed; + +struct vss_istream_cmd_cdma_set_enc_minmax_rate_t { + uint16_t min_rate; + /* Set the lower bound encoder rate. + * + * 0x0000 : Blank frame + * 0x0001 : Eighth rate + * 0x0002 : Quarter rate + * 0x0003 : Half rate + * 0x0004 : Full rate + */ + uint16_t max_rate; + /* Set the upper bound encoder rate. + * + * 0x0000 : Blank frame + * 0x0001 : Eighth rate + * 0x0002 : Quarter rate + * 0x0003 : Half rate + * 0x0004 : Full rate + */ +} __packed; + +struct vss_istream_cmd_set_enc_dtx_mode_t { + uint32_t enable; + /* Toggle DTX on or off. + * + * 0 : Disables DTX + * 1 : Enables DTX + */ +} __packed; + +struct vss_istream_cmd_register_calibration_data_v2_t { + uint32_t cal_mem_handle; + /* Handle to the shared memory that holds the calibration data. */ + uint32_t cal_mem_address_lsw; + uint32_t cal_mem_address_msw; + /* Location of calibration data. */ + uint32_t cal_mem_size; + /* Size of the calibration data in bytes. */ + uint8_t column_info[MAX_COL_INFO_SIZE]; + /* + * Column info contains the number of columns and the array of columns + * in the calibration table. The order in which the columns are provided + * here must match the order in which they exist in the calibration + * table provided. + */ +} __packed; + +struct enable_param { + uint16_t enable; + uint16_t reserved_field; + /* Reserved, set to 0. */ +}; + +struct vss_icommon_cmd_set_ui_property { + /* APR Header */ + struct apr_hdr apr_hdr; + + /* The parameter data to be filled when sent inband */ + u8 param_data[0]; +} __packed; + +/* + * Event sent by the stream to the client that enables Rx DTMF + * detection whenever DTMF is detected in the Rx path. + * + * The DTMF detection feature can only be used to detect DTMF + * frequencies as listed in the vss_istream_evt_rx_dtmf_detected_t + * structure. + */ + +#define VSS_ISTREAM_EVT_RX_DTMF_DETECTED 0x0001101A + +struct vss_istream_cmd_set_rx_dtmf_detection { + /* + * Enables/disables Rx DTMF detection + * + * Possible values are + * 0 - disable + * 1 - enable + * + */ + uint32_t enable; +}; + +#define VSS_ISTREAM_CMD_SET_RX_DTMF_DETECTION 0x00011027 + +struct vss_istream_evt_rx_dtmf_detected { + uint16_t low_freq; + /* + * Detected low frequency. Possible values: + * 697 Hz + * 770 Hz + * 852 Hz + * 941 Hz + */ + uint16_t high_freq; + /* + * Detected high frequency. Possible values: + * 1209 Hz + * 1336 Hz + * 1477 Hz + * 1633 Hz + */ +}; + +struct cvs_set_rx_dtmf_detection_cmd { + struct apr_hdr hdr; + struct vss_istream_cmd_set_rx_dtmf_detection cvs_dtmf_det; +} __packed; + + +struct cvs_create_passive_ctl_session_cmd { + struct apr_hdr hdr; + struct vss_istream_cmd_create_passive_control_session_t cvs_session; +} __packed; + +struct cvs_create_full_ctl_session_cmd { + struct apr_hdr hdr; + struct vss_istream_cmd_create_full_control_session_t cvs_session; +} __packed; + +struct cvs_destroy_session_cmd { + struct apr_hdr hdr; +} __packed; + +struct cvs_set_mute_cmd { + struct apr_hdr hdr; + struct vss_ivolume_cmd_mute_v2_t cvs_set_mute; +} __packed; + +struct cvs_set_media_type_cmd { + struct apr_hdr hdr; + struct vss_istream_cmd_set_media_type_t media_type; +} __packed; + +struct cvs_send_dec_buf_cmd { + struct apr_hdr hdr; + struct vss_istream_evt_send_dec_buffer_t dec_buf; +} __packed; + +struct cvs_set_amr_enc_rate_cmd { + struct apr_hdr hdr; + struct vss_istream_cmd_voc_amr_set_enc_rate_t amr_rate; +} __packed; + +struct cvs_set_amrwb_enc_rate_cmd { + struct apr_hdr hdr; + struct vss_istream_cmd_voc_amrwb_set_enc_rate_t amrwb_rate; +} __packed; + +struct cvs_set_cdma_enc_minmax_rate_cmd { + struct apr_hdr hdr; + struct vss_istream_cmd_cdma_set_enc_minmax_rate_t cdma_rate; +} __packed; + +struct cvs_set_enc_dtx_mode_cmd { + struct apr_hdr hdr; + struct vss_istream_cmd_set_enc_dtx_mode_t dtx_mode; +} __packed; + +struct cvs_register_cal_data_cmd { + struct apr_hdr hdr; + struct vss_istream_cmd_register_calibration_data_v2_t cvs_cal_data; +} __packed; + +struct cvs_deregister_cal_data_cmd { + struct apr_hdr hdr; +} __packed; + +struct cvs_start_record_cmd { + struct apr_hdr hdr; + struct vss_irecord_cmd_start_t rec_mode; +} __packed; + +struct cvs_start_playback_cmd { + struct apr_hdr hdr; + struct vss_iplayback_cmd_start_t playback_mode; +} __packed; + +struct cvs_dec_buffer_ready_cmd { + struct apr_hdr hdr; +} __packed; + +struct cvs_enc_buffer_consumed_cmd { + struct apr_hdr hdr; +} __packed; + +struct vss_istream_cmd_set_oob_packet_exchange_config_t { + struct apr_hdr hdr; + uint32_t mem_handle; + uint32_t enc_buf_addr_lsw; + uint32_t enc_buf_addr_msw; + uint32_t enc_buf_size; + uint32_t dec_buf_addr_lsw; + uint32_t dec_buf_addr_msw; + uint32_t dec_buf_size; +} __packed; + +struct vss_istream_cmd_set_packet_exchange_mode_t { + struct apr_hdr hdr; + uint32_t mode; +} __packed; + +/* TO CVP commands */ + +#define VSS_IVOCPROC_CMD_CREATE_FULL_CONTROL_SESSION 0x000100C3 +/**< Wait for APRV2_IBASIC_RSP_RESULT response. */ + +#define APRV2_IBASIC_CMD_DESTROY_SESSION 0x0001003C + +#define VSS_IVOCPROC_CMD_SET_DEVICE_V2 0x000112C6 + +#define VSS_IVOCPROC_CMD_SET_DEVICE_V3 0x0001316A + +#define VSS_IVOCPROC_CMD_TOPOLOGY_SET_DEV_CHANNELS 0x00013199 + +#define VSS_IVOCPROC_CMD_TOPOLOGY_COMMIT 0x00013198 + +#define VSS_IVOCPROC_CMD_SET_VP3_DATA 0x000110EB + +#define VSS_IVOLUME_CMD_SET_STEP 0x000112C2 + +#define VSS_IVOCPROC_CMD_ENABLE 0x000100C6 +/**< No payload. Wait for APRV2_IBASIC_RSP_RESULT response. */ + +#define VSS_IVOCPROC_CMD_DISABLE 0x000110E1 +/**< No payload. Wait for APRV2_IBASIC_RSP_RESULT response. */ + +/* + * Registers the memory that contains device specific configuration data with + * the vocproc. The client must register device configuration data with the + * vocproc that corresponds with the device being set on the vocproc. + */ +#define VSS_IVOCPROC_CMD_REGISTER_DEVICE_CONFIG 0x00011371 + +/* + * Deregisters the memory that holds device configuration data from the + vocproc. +*/ +#define VSS_IVOCPROC_CMD_DEREGISTER_DEVICE_CONFIG 0x00011372 + +#define CVD_CAL_DATA_FORMAT_MINOR_VERSION_V0 0x00000000 +#define CVD_CAL_DATA_FORMAT_MINOR_VERSION_V1 0x00000001 +#define VSS_IVOCPROC_CMD_REGISTER_CALIBRATION_DATA_V2 0x00011373 +#define VSS_IVOCPROC_CMD_DEREGISTER_CALIBRATION_DATA 0x00011276 + +#define VSS_IVOCPROC_CMD_REGISTER_VOL_CALIBRATION_DATA 0x00011374 +#define VSS_IVOCPROC_CMD_DEREGISTER_VOL_CALIBRATION_DATA 0x00011375 + +#define VSS_IVOCPROC_CMD_REGISTER_STATIC_CALIBRATION_DATA 0x00013079 +#define VSS_IVOCPROC_CMD_DEREGISTER_STATIC_CALIBRATION_DATA 0x0001307A + +#define VSS_IVOCPROC_CMD_REGISTER_DYNAMIC_CALIBRATION_DATA 0x0001307B +#define VSS_IVOCPROC_CMD_DEREGISTER_DYNAMIC_CALIBRATION_DATA 0x0001307C + +#define VSS_IVOCPROC_TOPOLOGY_ID_NONE 0x00010F70 +#define VSS_IVOCPROC_TOPOLOGY_ID_TX_SM_ECNS_V2 0x00010F89 +#define VSS_IVOCPROC_TOPOLOGY_ID_TX_DM_FLUENCE 0x00010F72 + +#define VSS_IVOCPROC_TOPOLOGY_ID_RX_DEFAULT 0x00010F77 + +/* Newtwork IDs */ +#define VSS_ICOMMON_CAL_NETWORK_ID_NONE 0x0001135E + +/* Select internal mixing mode. */ +#define VSS_IVOCPROC_VOCPROC_MODE_EC_INT_MIXING 0x00010F7C + +/* Select external mixing mode. */ +#define VSS_IVOCPROC_VOCPROC_MODE_EC_EXT_MIXING 0x00010F7D + +/* Default AFE port ID. Applicable to Tx and Rx. */ +#define VSS_IVOCPROC_PORT_ID_NONE 0xFFFF + +#define VSS_NETWORK_ID_DEFAULT 0x00010037 + +/* Voice over Internet Protocol (VoIP) network ID. Common for all bands.*/ +#define VSS_NETWORK_ID_VOIP 0x00011362 + +/* Media types */ +#define VSS_MEDIA_ID_EVRC_MODEM 0x00010FC2 +/* 80-VF690-47 CDMA enhanced variable rate vocoder modem format. */ +#define VSS_MEDIA_ID_AMR_NB_MODEM 0x00010FC6 +/* 80-VF690-47 UMTS AMR-NB vocoder modem format. */ +#define VSS_MEDIA_ID_AMR_WB_MODEM 0x00010FC7 +/* 80-VF690-47 UMTS AMR-WB vocoder modem format. */ + +#define VSS_MEDIA_ID_PCM_8_KHZ 0x00010FCB +#define VSS_MEDIA_ID_PCM_16_KHZ 0x00010FCC +#define VSS_MEDIA_ID_PCM_32_KHZ 0x00010FD9 +#define VSS_MEDIA_ID_PCM_48_KHZ 0x00010FD6 + +/* Linear PCM (16-bit, little-endian). */ +#define VSS_MEDIA_ID_G711_ALAW 0x00010FCD +/* G.711 a-law (contains two 10ms vocoder frames). */ +#define VSS_MEDIA_ID_G711_MULAW 0x00010FCE +/* G.711 mu-law (contains two 10ms vocoder frames). */ +#define VSS_MEDIA_ID_G729 0x00010FD0 +/* G.729AB (contains two 10ms vocoder frames. */ +#define VSS_MEDIA_ID_4GV_NB_MODEM 0x00010FC3 +/*CDMA EVRC-B vocoder modem format */ +#define VSS_MEDIA_ID_4GV_WB_MODEM 0x00010FC4 +/*CDMA EVRC-WB vocoder modem format */ +#define VSS_MEDIA_ID_4GV_NW_MODEM 0x00010FC5 +/*CDMA EVRC-NW vocoder modem format */ + +#define VSS_IVOCPROC_CMD_CREATE_FULL_CONTROL_SESSION_V2 0x000112BF +#define VSS_IVOCPROC_CMD_CREATE_FULL_CONTROL_SESSION_V3 0x00013169 + +#define VSS_NUM_DEV_CHANNELS_1 1 +#define VSS_NUM_DEV_CHANNELS_2 2 +#define VSS_NUM_DEV_CHANNELS_3 3 +#define VSS_NUM_DEV_CHANNELS_4 4 + +struct vss_ivocproc_cmd_create_full_control_session_v2_t { + uint16_t direction; + /* + * Vocproc direction. The supported values: + * VSS_IVOCPROC_DIRECTION_RX + * VSS_IVOCPROC_DIRECTION_TX + * VSS_IVOCPROC_DIRECTION_RX_TX + */ + uint16_t tx_port_id; + /* + * Tx device port ID to which the vocproc connects. If a port ID is + * not being supplied, set this to #VSS_IVOCPROC_PORT_ID_NONE. + */ + uint32_t tx_topology_id; + /* + * Tx path topology ID. If a topology ID is not being supplied, set + * this to #VSS_IVOCPROC_TOPOLOGY_ID_NONE. + */ + uint16_t rx_port_id; + /* + * Rx device port ID to which the vocproc connects. If a port ID is + * not being supplied, set this to #VSS_IVOCPROC_PORT_ID_NONE. + */ + uint32_t rx_topology_id; + /* + * Rx path topology ID. If a topology ID is not being supplied, set + * this to #VSS_IVOCPROC_TOPOLOGY_ID_NONE. + */ + uint32_t profile_id; + /* Voice calibration profile ID. */ + uint32_t vocproc_mode; + /* + * Vocproc mode. The supported values: + * VSS_IVOCPROC_VOCPROC_MODE_EC_INT_MIXING + * VSS_IVOCPROC_VOCPROC_MODE_EC_EXT_MIXING + */ + uint16_t ec_ref_port_id; + /* + * Port ID to which the vocproc connects for receiving echo + * cancellation reference signal. If a port ID is not being supplied, + * set this to #VSS_IVOCPROC_PORT_ID_NONE. This parameter value is + * ignored when the vocproc_mode parameter is set to + * VSS_IVOCPROC_VOCPROC_MODE_EC_INT_MIXING. + */ + char name[SESSION_NAME_LEN]; + /* + * Session name string used to identify a session that can be shared + * with passive controllers (optional). The string size, including the + * NULL termination character, is limited to 31 characters. + */ +} __packed; + +struct vss_ivocproc_cmd_set_volume_index_t { + uint16_t vol_index; + /* + * Volume index utilized by the vocproc to index into the volume table + * provided in VSS_IVOCPROC_CMD_CACHE_VOLUME_CALIBRATION_TABLE and set + * volume on the VDSP. + */ +} __packed; + +struct vss_ivolume_cmd_set_step_t { + uint16_t direction; + /* + * The direction field sets the direction to apply the volume command. + * The supported values: + * #VSS_IVOLUME_DIRECTION_RX + */ + uint32_t value; + /* + * Volume step used to find the corresponding linear volume and + * the best match index in the registered volume calibration table. + */ + uint16_t ramp_duration_ms; + /* + * Volume change ramp duration in milliseconds. + * The supported values: 0 to 5000. + */ +} __packed; + +struct vss_ivocproc_cmd_set_device_v2_t { + uint16_t tx_port_id; + /* + * TX device port ID which vocproc will connect to. + * VSS_IVOCPROC_PORT_ID_NONE means vocproc will not connect to any port. + */ + uint32_t tx_topology_id; + /* + * TX leg topology ID. + * VSS_IVOCPROC_TOPOLOGY_ID_NONE means vocproc does not contain any + * pre/post-processing blocks and is pass-through. + */ + uint16_t rx_port_id; + /* + * RX device port ID which vocproc will connect to. + * VSS_IVOCPROC_PORT_ID_NONE means vocproc will not connect to any port. + */ + uint32_t rx_topology_id; + /* + * RX leg topology ID. + * VSS_IVOCPROC_TOPOLOGY_ID_NONE means vocproc does not contain any + * pre/post-processing blocks and is pass-through. + */ + uint32_t vocproc_mode; + /* Vocproc mode. The supported values: + * VSS_IVOCPROC_VOCPROC_MODE_EC_INT_MIXING - 0x00010F7C + * VSS_IVOCPROC_VOCPROC_MODE_EC_EXT_MIXING - 0x00010F7D + */ + uint16_t ec_ref_port_id; + /* Port ID to which the vocproc connects for receiving + * echo + */ +} __packed; + +struct vss_ivocproc_cmd_register_device_config_t { + uint32_t mem_handle; + /* + * Handle to the shared memory that holds the per-network calibration + * data. + */ + uint32_t mem_address_lsw; + uint32_t mem_address_msw; + /* Location of calibration data. */ + uint32_t mem_size; + /* Size of the calibration data in bytes. */ +} __packed; + +struct vss_ivocproc_cmd_register_calibration_data_v2_t { + uint32_t cal_mem_handle; + /* + * Handle to the shared memory that holds the per-network calibration + * data. + */ + uint32_t cal_mem_address_lsw; + uint32_t cal_mem_address_msw; + /* Location of calibration data. */ + uint32_t cal_mem_size; + /* Size of the calibration data in bytes. */ + uint8_t column_info[MAX_COL_INFO_SIZE]; + /* + * Column info contains the number of columns and the array of columns + * in the calibration table. The order in which the columns are provided + * here must match the order in which they exist in the calibration + * table provided. + */ +} __packed; + +struct vss_ivocproc_cmd_register_volume_cal_data_t { + uint32_t cal_mem_handle; + /* + * Handle to the shared memory that holds the volume calibration + * data. + */ + uint32_t cal_mem_address_lsw; + uint32_t cal_mem_address_msw; + /* Location of volume calibration data. */ + uint32_t cal_mem_size; + /* Size of the volume calibration data in bytes. */ + uint8_t column_info[MAX_COL_INFO_SIZE]; + /* + * Column info contains the number of columns and the array of columns + * in the calibration table. The order in which the columns are provided + * here must match the order in which they exist in the calibration + * table provided. + */ +} __packed; + +struct vss_ivocproc_cmd_topology_set_dev_channels_t { + uint16_t tx_num_channels; + /* + * Number of Mics. + * Supported values + * 1 VSS_NUM_DEV_CHANNELS_1 + * 2 VSS_NUM_DEV_CHANNELS_2 + * 3 VSS_NUM_DEV_CHANNELS_3 + * 4 VSS_NUM_DEV_CHANNELS_4 + */ + uint16_t rx_num_channels; + /* + * Number of speaker channels. + * Supported values + * 1 VSS_NUM_DEV_CHANNELS_1 + */ +} __packed; + +/* Starts a vocoder PCM session */ +#define VSS_IVPCM_CMD_START_V2 0x00011339 + +/* Default tap point location on the TX path. */ +#define VSS_IVPCM_TAP_POINT_TX_DEFAULT 0x00011289 + +/* Default tap point location on the RX path. */ +#define VSS_IVPCM_TAP_POINT_RX_DEFAULT 0x0001128A + +/* Indicates tap point direction is output. */ +#define VSS_IVPCM_TAP_POINT_DIR_OUT 0 + +/* Indicates tap point direction is input. */ +#define VSS_IVPCM_TAP_POINT_DIR_IN 1 + +/* Indicates tap point direction is output and input. */ +#define VSS_IVPCM_TAP_POINT_DIR_OUT_IN 2 + + +#define VSS_IVPCM_SAMPLING_RATE_AUTO 0 + +/* Indicates 8 KHz vocoder PCM sampling rate. */ +#define VSS_IVPCM_SAMPLING_RATE_8K 8000 + +/* Indicates 16 KHz vocoder PCM sampling rate. */ +#define VSS_IVPCM_SAMPLING_RATE_16K 16000 + +/* RX and TX */ +#define MAX_TAP_POINTS_SUPPORTED 2 + +struct vss_ivpcm_tap_point { + uint32_t tap_point; + uint16_t direction; + uint16_t sampling_rate; + uint16_t duration; +} __packed; + + +struct vss_ivpcm_cmd_start_v2_t { + uint32_t mem_handle; + uint32_t num_tap_points; + struct vss_ivpcm_tap_point tap_points[MAX_TAP_POINTS_SUPPORTED]; +} __packed; + +#define VSS_IVPCM_EVT_PUSH_BUFFER_V2 0x0001133A + +/* Push buffer event mask indicating output buffer is filled. */ +#define VSS_IVPCM_PUSH_BUFFER_MASK_OUTPUT_BUFFER 1 + +/* Push buffer event mask indicating input buffer is consumed. */ +#define VSS_IVPCM_PUSH_BUFFER_MASK_INPUT_BUFFER 2 + + +struct vss_ivpcm_evt_push_buffer_v2_t { + uint32_t tap_point; + uint32_t push_buf_mask; + uint64_t out_buf_mem_address; + uint16_t out_buf_mem_size; + uint64_t in_buf_mem_address; + uint16_t in_buf_mem_size; + uint16_t sampling_rate; + uint16_t num_in_channels; +} __packed; + +#define VSS_IVPCM_EVT_NOTIFY_V2 0x0001133B + +/* Notify event mask indicates output buffer is filled. */ +#define VSS_IVPCM_NOTIFY_MASK_OUTPUT_BUFFER 1 + +/* Notify event mask indicates input buffer is consumed. */ +#define VSS_IVPCM_NOTIFY_MASK_INPUT_BUFFER 2 + +/* Notify event mask indicates a timetick */ +#define VSS_IVPCM_NOTIFY_MASK_TIMETICK 4 + +/* Notify event mask indicates an error occurred in output buffer operation */ +#define VSS_IVPCM_NOTIFY_MASK_OUTPUT_ERROR 8 + +/* Notify event mask indicates an error occurred in input buffer operation */ +#define VSS_IVPCM_NOTIFY_MASK_INPUT_ERROR 16 + + +struct vss_ivpcm_evt_notify_v2_t { + uint32_t tap_point; + uint32_t notify_mask; + uint64_t out_buff_addr; + uint64_t in_buff_addr; + uint16_t filled_out_size; + uint16_t request_buf_size; + uint16_t sampling_rate; + uint16_t num_out_channels; +} __packed; + +struct cvp_start_cmd { + struct apr_hdr hdr; + struct vss_ivpcm_cmd_start_v2_t vpcm_start_cmd; +} __packed; + +struct cvp_push_buf_cmd { + struct apr_hdr hdr; + struct vss_ivpcm_evt_push_buffer_v2_t vpcm_evt_push_buffer; +} __packed; + +#define VSS_IVPCM_CMD_STOP 0x0001100B + +struct cvp_create_full_ctl_session_cmd { + struct apr_hdr hdr; + struct vss_ivocproc_cmd_create_full_control_session_v2_t cvp_session; +} __packed; + +struct cvp_command { + struct apr_hdr hdr; +} __packed; + +struct cvp_set_device_cmd { + struct apr_hdr hdr; + struct vss_ivocproc_cmd_set_device_v2_t cvp_set_device_v2; +} __packed; + +struct cvp_set_dev_channels_cmd { + struct apr_hdr hdr; + struct vss_ivocproc_cmd_topology_set_dev_channels_t cvp_set_channels; +} __packed; + +struct cvp_set_channel_info_cmd_v2 { + struct apr_hdr hdr; + struct vss_icommon_cmd_set_param_channel_info_v2_t + cvp_set_ch_info_param_v2; +} __packed; + +struct cvp_set_channel_mixer_info_cmd_v2 { + struct apr_hdr hdr; + struct vss_icommon_cmd_set_param_ch_mixer_v2_t + cvp_set_ch_mixer_param_v2; +} __packed; + +struct cvp_set_mfc_config_cmd_v2 { + struct apr_hdr hdr; + struct vss_icommon_cmd_set_param_mfc_config_v2_t cvp_set_mfc_param_v2; +} __packed; + +struct cvp_set_vp3_data_cmd { + struct apr_hdr hdr; +} __packed; + +struct cvp_set_rx_volume_index_cmd { + struct apr_hdr hdr; + struct vss_ivocproc_cmd_set_volume_index_t cvp_set_vol_idx; +} __packed; + +struct cvp_set_rx_volume_step_cmd { + struct apr_hdr hdr; + struct vss_ivolume_cmd_set_step_t cvp_set_vol_step; +} __packed; + +struct cvp_register_dev_cfg_cmd { + struct apr_hdr hdr; + struct vss_ivocproc_cmd_register_device_config_t cvp_dev_cfg_data; +} __packed; + +struct cvp_deregister_dev_cfg_cmd { + struct apr_hdr hdr; +} __packed; + +struct cvp_register_cal_data_cmd { + struct apr_hdr hdr; + struct vss_ivocproc_cmd_register_calibration_data_v2_t cvp_cal_data; +} __packed; + +struct cvp_deregister_cal_data_cmd { + struct apr_hdr hdr; +} __packed; + +struct cvp_register_vol_cal_data_cmd { + struct apr_hdr hdr; + struct vss_ivocproc_cmd_register_volume_cal_data_t cvp_vol_cal_data; +} __packed; + +struct cvp_deregister_vol_cal_data_cmd { + struct apr_hdr hdr; +} __packed; + +struct cvp_set_mute_cmd { + struct apr_hdr hdr; + struct vss_ivolume_cmd_mute_v2_t cvp_set_mute; +} __packed; + +/* CB for up-link packets. */ +typedef void (*ul_cb_fn)(uint8_t *voc_pkt, + uint32_t pkt_len, + uint32_t timestamp, + void *private_data); + +/* CB for down-link packets. */ +typedef void (*dl_cb_fn)(uint8_t *voc_pkt, + void *private_data); + +/* CB for DTMF RX Detection */ +typedef void (*dtmf_rx_det_cb_fn)(uint8_t *pkt, + char *session, + void *private_data); + +typedef void (*voip_ssr_cb) (uint32_t opcode, + void *private_data); + +typedef void (*hostpcm_cb_fn)(uint8_t *data, + char *session, + void *private_data); + +struct mvs_driver_info { + uint32_t media_type; + uint32_t rate; + uint32_t network_type; + uint32_t dtx_mode; + ul_cb_fn ul_cb; + dl_cb_fn dl_cb; + voip_ssr_cb ssr_cb; + void *private_data; + uint32_t evrc_min_rate; + uint32_t evrc_max_rate; +}; + +struct dtmf_driver_info { + dtmf_rx_det_cb_fn dtmf_rx_ul_cb; + void *private_data; +}; + +struct hostpcm_driver_info { + hostpcm_cb_fn hostpcm_evt_cb; + void *private_data; +}; + +struct incall_rec_info { + uint32_t rec_enable; + uint32_t rec_mode; + uint32_t recording; +}; + +struct incall_music_info { + uint32_t play_enable; + uint32_t playing; + int count; + int force; + uint16_t port_id; +}; + +struct share_memory_info { + u32 mem_handle; + struct share_mem_buf sh_buf; + struct mem_map_table memtbl; +}; + +#define VSS_ISOUNDFOCUS_CMD_SET_SECTORS 0x00013133 +#define VSS_ISOUNDFOCUS_CMD_GET_SECTORS 0x00013134 +#define VSS_ISOUNDFOCUS_RSP_GET_SECTORS 0x00013135 +#define VSS_ISOURCETRACK_CMD_GET_ACTIVITY 0x00013136 + +struct vss_isoundfocus_cmd_set_sectors_t { + uint16_t start_angles[8]; + uint8_t enables[8]; + uint16_t gain_step; +} __packed; + +/* Payload of the VSS_ISOUNDFOCUS_RSP_GET_SECTORS response */ +struct vss_isoundfocus_rsp_get_sectors_t { + uint16_t start_angles[8]; + uint8_t enables[8]; + uint16_t gain_step; +} __packed; + +struct cvp_set_sound_focus_param_cmd_t { + struct apr_hdr hdr; + struct vss_isoundfocus_cmd_set_sectors_t cvp_set_sound_focus_param; +} __packed; + +/* Payload structure for the VSS_ISOURCETRACK_CMD_GET_ACTIVITY command */ +struct vss_isourcetrack_cmd_get_activity_t { + uint32_t mem_handle; + uint32_t mem_address_lsw; + uint32_t mem_address_msw; + uint32_t mem_size; +} __packed; + +struct cvp_get_source_tracking_param_cmd_t { + struct apr_hdr hdr; + struct vss_isourcetrack_cmd_get_activity_t + cvp_get_source_tracking_param; +} __packed; + +/* Structure for the sound activity data */ +struct vss_isourcetrack_activity_data_t { + uint8_t voice_active[8]; + uint16_t talker_doa; + uint16_t interferer_doa[3]; + uint8_t sound_strength[360]; +} __packed; + +struct shared_mem_info { + uint32_t mem_handle; + struct mem_map_table sh_mem_block; + struct mem_map_table sh_mem_table; +}; + +struct voice_data { + int voc_state;/*INIT, CHANGE, RELEASE, RUN */ + + /* Shared mem to store decoder and encoder packets */ + struct share_memory_info shmem_info; + + wait_queue_head_t mvm_wait; + wait_queue_head_t cvs_wait; + wait_queue_head_t cvp_wait; + + /* Cache the values related to Rx and Tx devices */ + struct device_data dev_rx; + struct device_data dev_tx; + + /* Cache the values related to Rx and Tx streams */ + struct stream_data stream_rx; + struct stream_data stream_tx; + + u32 mvm_state; + u32 cvs_state; + u32 cvp_state; + + u32 async_err; + + /* Handle to MVM in the Q6 */ + u16 mvm_handle; + /* Handle to CVS in the Q6 */ + u16 cvs_handle; + /* Handle to CVP in the Q6 */ + u16 cvp_handle; + + struct mutex lock; + + bool disable_topology; + + uint16_t sidetone_gain; + uint8_t tty_mode; + /* slowtalk enable value */ + uint32_t st_enable; + uint32_t hd_enable; + uint32_t dtmf_rx_detect_en; + /* Local Call Hold mode */ + uint8_t lch_mode; + + struct voice_dev_route_state voc_route_state; + + u32 session_id; + + struct incall_rec_info rec_info; + + struct incall_music_info music_info; + + struct voice_rec_route_state rec_route_state; + + bool mic_break_status; + struct work_struct voice_mic_break_work; +}; + +#define MAX_VOC_SESSIONS 8 + +struct common_data { + /* these default values are for all devices */ + uint32_t default_mute_val; + uint32_t default_sample_val; + uint32_t default_vol_step_val; + uint32_t default_vol_ramp_duration_ms; + uint32_t default_mute_ramp_duration_ms; + bool ec_ref_ext; + struct media_format_info ec_media_fmt_info; + + /* APR to MVM in the Q6 */ + void *apr_q6_mvm; + /* APR to CVS in the Q6 */ + void *apr_q6_cvs; + /* APR to CVP in the Q6 */ + void *apr_q6_cvp; + + struct cal_type_data *cal_data[MAX_VOICE_CAL_TYPES]; + + struct mem_map_table cal_mem_map_table; + uint32_t cal_mem_handle; + + struct mem_map_table rtac_mem_map_table; + uint32_t rtac_mem_handle; + + uint32_t voice_host_pcm_mem_handle; + + struct mutex common_lock; + + struct mvs_driver_info mvs_info; + + struct dtmf_driver_info dtmf_info; + + struct hostpcm_driver_info hostpcm_info; + + struct voice_data voice[MAX_VOC_SESSIONS]; + + bool srvcc_rec_flag; + bool is_destroy_cvd; + char cvd_version[CVD_VERSION_STRING_MAX_SIZE]; + int cvp_version; + bool is_avcs_version_queried; + bool is_per_vocoder_cal_enabled; + bool is_sound_focus_resp_success; + bool is_source_tracking_resp_success; + struct vss_isoundfocus_rsp_get_sectors_t soundFocusResponse; + struct shared_mem_info source_tracking_sh_mem; + struct vss_isourcetrack_activity_data_t sourceTrackingResponse; + bool sidetone_enable; + bool mic_break_enable; + struct audio_uevent_data *uevent_data; +}; + +struct voice_session_itr { + int cur_idx; + int session_idx; +}; + +void voc_register_mvs_cb(ul_cb_fn ul_cb, + dl_cb_fn dl_cb, + voip_ssr_cb ssr_cb, + void *private_data); + +void voc_register_dtmf_rx_detection_cb(dtmf_rx_det_cb_fn dtmf_rx_ul_cb, + void *private_data); + +void voc_config_vocoder(uint32_t media_type, + uint32_t rate, + uint32_t network_type, + uint32_t dtx_mode, + uint32_t evrc_min_rate, + uint32_t evrc_max_rate); + +enum { + DEV_RX = 0, + DEV_TX, +}; + +enum { + RX_PATH = 0, + TX_PATH, + EC_REF_PATH, +}; + +#define VOC_PATH_PASSIVE 0 +#define VOC_PATH_FULL 1 +#define VOC_PATH_VOLTE_PASSIVE 2 +#define VOC_PATH_VOICE2_PASSIVE 3 +#define VOC_PATH_QCHAT_PASSIVE 4 +#define VOC_PATH_VOWLAN_PASSIVE 5 +#define VOC_PATH_VOICEMMODE1_PASSIVE 6 +#define VOC_PATH_VOICEMMODE2_PASSIVE 7 + +#define MAX_SESSION_NAME_LEN 32 +#define VOICE_SESSION_NAME "Voice session" +#define VOIP_SESSION_NAME "VoIP session" +#define VOLTE_SESSION_NAME "VoLTE session" +#define VOICE2_SESSION_NAME "Voice2 session" +#define QCHAT_SESSION_NAME "QCHAT session" +#define VOWLAN_SESSION_NAME "VoWLAN session" +#define VOICEMMODE1_NAME "VoiceMMode1" +#define VOICEMMODE2_NAME "VoiceMMode2" + +#define VOICE2_SESSION_VSID_STR "10DC1000" +#define QCHAT_SESSION_VSID_STR "10803000" +#define VOWLAN_SESSION_VSID_STR "10002000" +#define VOICEMMODE1_VSID_STR "11C05000" +#define VOICEMMODE2_VSID_STR "11DC5000" +#define VOICE_SESSION_VSID 0x10C01000 +#define VOICE2_SESSION_VSID 0x10DC1000 +#define VOLTE_SESSION_VSID 0x10C02000 +#define VOIP_SESSION_VSID 0x10004000 +#define QCHAT_SESSION_VSID 0x10803000 +#define VOWLAN_SESSION_VSID 0x10002000 +#define VOICEMMODE1_VSID 0x11C05000 +#define VOICEMMODE2_VSID 0x11DC5000 +#define ALL_SESSION_VSID 0xFFFFFFFF +#define VSID_MAX ALL_SESSION_VSID + +/* called by alsa driver */ +int voc_set_pp_enable(uint32_t session_id, + struct module_instance_info mod_inst_info, + uint32_t enable); +int voc_get_pp_enable(uint32_t session_id, + struct module_instance_info mod_inst_info); +int voc_set_hd_enable(uint32_t session_id, uint32_t enable); +uint8_t voc_get_tty_mode(uint32_t session_id); +int voc_set_tty_mode(uint32_t session_id, uint8_t tty_mode); +int voc_start_voice_call(uint32_t session_id); +int voc_end_voice_call(uint32_t session_id); +int voc_standby_voice_call(uint32_t session_id); +int voc_resume_voice_call(uint32_t session_id); +int voc_set_lch(uint32_t session_id, enum voice_lch_mode lch_mode); +int voc_set_rx_vol_step(uint32_t session_id, uint32_t dir, uint32_t vol_step, + uint32_t ramp_duration); +int voc_set_tx_mute(uint32_t session_id, uint32_t dir, uint32_t mute, + uint32_t ramp_duration); +int voc_set_device_mute(uint32_t session_id, uint32_t dir, uint32_t mute, + uint32_t ramp_duration); +int voc_get_rx_device_mute(uint32_t session_id); +int voc_set_route_flag(uint32_t session_id, uint8_t path_dir, uint8_t set); +uint8_t voc_get_route_flag(uint32_t session_id, uint8_t path_dir); +bool voc_get_mbd_enable(void); +uint8_t voc_set_mbd_enable(bool enable); +int voc_enable_dtmf_rx_detection(uint32_t session_id, uint32_t enable); +void voc_disable_dtmf_det_on_active_sessions(void); +int voc_alloc_cal_shared_memory(void); +int voc_alloc_voip_shared_memory(void); +int is_voc_initialized(void); +int voc_register_vocproc_vol_table(void); +int voc_deregister_vocproc_vol_table(void); +int voc_send_cvp_map_vocpcm_memory(uint32_t session_id, + struct mem_map_table *tp_mem_table, + phys_addr_t paddr, uint32_t bufsize); +int voc_send_cvp_unmap_vocpcm_memory(uint32_t session_id); +int voc_send_cvp_start_vocpcm(uint32_t session_id, + struct vss_ivpcm_tap_point *vpcm_tp, + uint32_t no_of_tp); +int voc_send_cvp_vocpcm_push_buf_evt(uint32_t session_id, + struct vss_ivpcm_evt_push_buffer_v2_t *push_buff_evt); +int voc_send_cvp_stop_vocpcm(uint32_t session_id); +void voc_register_hpcm_evt_cb(hostpcm_cb_fn hostpcm_cb, + void *private_data); +void voc_deregister_hpcm_evt_cb(void); + +int voc_map_rtac_block(struct rtac_cal_block_data *cal_block); +int voc_unmap_rtac_block(uint32_t *mem_map_handle); + +uint32_t voc_get_session_id(char *name); + +int voc_start_playback(uint32_t set, uint16_t port_id); +int voc_start_record(uint32_t port_id, uint32_t set, uint32_t session_id); +int voice_get_idx_for_session(u32 session_id); +int voc_set_ext_ec_ref_port_id(uint16_t port_id, bool state); +int voc_get_ext_ec_ref_port_id(void); +int voc_set_ext_ec_ref_media_fmt_info(struct media_format_info *finfo); +int voc_update_amr_vocoder_rate(uint32_t session_id); +int voc_disable_device(uint32_t session_id); +int voc_enable_device(uint32_t session_id); +void voc_set_destroy_cvd_flag(bool is_destroy_cvd); +int voc_disable_topology(uint32_t session_id, uint32_t disable); +int voc_set_device_config(uint32_t session_id, uint8_t path_dir, + struct media_format_info *finfo); +uint32_t voice_get_topology(uint32_t topology_idx); +int voice_set_topology_specific_info(struct voice_data *v, + uint32_t topology_idx); +int voc_set_sound_focus(struct sound_focus_param sound_focus_param); +int voc_get_sound_focus(struct sound_focus_param *soundFocusData); +int voc_get_source_tracking(struct source_tracking_param *sourceTrackingData); +int voc_set_afe_sidetone(uint32_t session_id, bool sidetone_enable); +bool voc_get_afe_sidetone(void); +#endif diff --git a/audio-kernel/include/dsp/rtac.h b/audio-kernel/include/dsp/rtac.h new file mode 100644 index 000000000..05dd82a7f --- /dev/null +++ b/audio-kernel/include/dsp/rtac.h @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2011, 2013-2015, 2017-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 __RTAC_H__ +#define __RTAC_H__ + +#include + +/* Voice Modes */ +#define RTAC_CVP 0 +#define RTAC_CVS 1 +#define RTAC_VOICE_MODES 2 + +#define RTAC_MAX_ACTIVE_DEVICES 4 +#define RTAC_MAX_ACTIVE_POPP 8 + +#define DEFAULT_APP_TYPE 0x00011130 + +enum { + ADM_RTAC_CAL, + ASM_RTAC_CAL, + VOICE_RTAC_CAL, + AFE_RTAC_CAL, + MAX_RTAC_BLOCKS +}; + +struct rtac_cal_mem_map_data { + uint32_t map_size; + uint32_t map_handle; + struct dma_buf *dma_buf; +}; + +struct rtac_cal_data { + size_t size; + void *kvaddr; + phys_addr_t paddr; +}; + +struct rtac_cal_block_data { + struct rtac_cal_mem_map_data map_data; + struct rtac_cal_data cal_data; +}; + +struct rtac_popp_data { + uint32_t popp; + uint32_t popp_topology; + uint32_t app_type; +}; + +struct rtac_adm_data { + uint32_t topology_id; + uint32_t afe_topology; + uint32_t afe_port; + uint32_t copp; + uint32_t num_of_popp; + uint32_t app_type; + uint32_t acdb_dev_id; + struct rtac_popp_data popp[RTAC_MAX_ACTIVE_POPP]; +}; + +struct rtac_adm { + uint32_t num_of_dev; + struct rtac_adm_data device[RTAC_MAX_ACTIVE_DEVICES]; +}; + +void rtac_add_adm_device(u32 port_id, u32 copp_id, u32 path_id, u32 popp_id, + u32 app_type, u32 acdb_dev_id); +void rtac_remove_adm_device(u32 port_id, u32 copp_id); +void rtac_remove_popp_from_adm_devices(u32 popp_id); +void rtac_add_voice(u32 cvs_handle, u32 cvp_handle, u32 rx_afe_port, + u32 tx_afe_port, u32 rx_acdb_id, u32 tx_acdb_id, u32 session_id); +void rtac_remove_voice(u32 cvs_handle); +void rtac_set_adm_handle(void *handle); +bool rtac_make_adm_callback(uint32_t *payload, u32 payload_size); +void rtac_copy_adm_payload_to_user(void *payload, u32 payload_size); +void rtac_set_asm_handle(u32 session_id, void *handle); +bool rtac_make_asm_callback(u32 session_id, uint32_t *payload, + u32 payload_size); +void rtac_copy_asm_payload_to_user(void *payload, u32 payload_size); +void rtac_set_voice_handle(u32 mode, void *handle); +bool rtac_make_voice_callback(u32 mode, uint32_t *payload, u32 payload_size); +void rtac_copy_voice_payload_to_user(void *payload, u32 payload_size); +int rtac_clear_mapping(uint32_t cal_type); +bool rtac_make_afe_callback(uint32_t *payload, u32 payload_size); +void rtac_set_afe_handle(void *handle); +void get_rtac_adm_data(struct rtac_adm *adm_data); +void rtac_update_afe_topology(u32 port_id); +#endif diff --git a/audio-kernel/include/dsp/sp_params.h b/audio-kernel/include/dsp/sp_params.h new file mode 100644 index 000000000..88b2d14a6 --- /dev/null +++ b/audio-kernel/include/dsp/sp_params.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2019, 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 __SP_PARAMS_H__ +#define __SP_PARAMS_H__ + +#if IS_ENABLED(CONFIG_XT_LOGGING) +int afe_get_sp_xt_logging_data(u16 port_id); +#else +static inline int afe_get_sp_xt_logging_data(u16 port_id) +{ + return 0; +} +#endif + +#endif /* __SP_PARAMS_H__ */ + diff --git a/audio-kernel/include/dsp/voice_mhi.h b/audio-kernel/include/dsp/voice_mhi.h new file mode 100644 index 000000000..49f09c397 --- /dev/null +++ b/audio-kernel/include/dsp/voice_mhi.h @@ -0,0 +1,30 @@ +/* Copyright (c) 2019, 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 __VOICE_MHI_H__ +#define __VOICE_MHI_H__ + +#ifdef CONFIG_VOICE_MHI +int voice_mhi_start(void); +int voice_mhi_end(void); +#else +static inline int voice_mhi_start(void) +{ + return 0; +} + +static inline int voice_mhi_end(void) +{ + return 0; +} +#endif + +#endif diff --git a/audio-kernel/include/ipc/apr.h b/audio-kernel/include/ipc/apr.h new file mode 100644 index 000000000..6b4817ede --- /dev/null +++ b/audio-kernel/include/ipc/apr.h @@ -0,0 +1,193 @@ +/* Copyright (c) 2010-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 + * 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 __APR_H_ +#define __APR_H_ + +#include +#include + +enum apr_subsys_state { + APR_SUBSYS_DOWN, + APR_SUBSYS_UP, + APR_SUBSYS_LOADED, +}; + +struct apr_q6 { + void *pil; + atomic_t q6_state; + atomic_t modem_state; + struct mutex lock; +}; + +struct apr_hdr { + uint16_t hdr_field; + uint16_t pkt_size; + uint8_t src_svc; + uint8_t src_domain; + uint16_t src_port; + uint8_t dest_svc; + uint8_t dest_domain; + uint16_t dest_port; + uint32_t token; + uint32_t opcode; +}; + +#define APR_HDR_LEN(hdr_len) ((hdr_len)/4) +#define APR_PKT_SIZE(hdr_len, payload_len) ((hdr_len) + (payload_len)) +#define APR_HDR_FIELD(msg_type, hdr_len, ver)\ + (((msg_type & 0x3) << 8) | ((hdr_len & 0xF) << 4) | (ver & 0xF)) + +#define APR_HDR_SIZE sizeof(struct apr_hdr) + +/* Version */ +#define APR_PKT_VER 0x0 + +/* Command and Response Types */ +#define APR_MSG_TYPE_EVENT 0x0 +#define APR_MSG_TYPE_CMD_RSP 0x1 +#define APR_MSG_TYPE_SEQ_CMD 0x2 +#define APR_MSG_TYPE_NSEQ_CMD 0x3 +#define APR_MSG_TYPE_MAX 0x04 + +/* APR Basic Response Message */ +#define APR_BASIC_RSP_RESULT 0x000110E8 +#define APR_RSP_ACCEPTED 0x000100BE + +/* Domain IDs */ +#define APR_DOMAIN_SIM 0x1 +#define APR_DOMAIN_PC 0x2 +#define APR_DOMAIN_MODEM 0x3 +#define APR_DOMAIN_ADSP 0x4 +#define APR_DOMAIN_APPS 0x5 +#define APR_DOMAIN_MAX 0x6 + +/* ADSP service IDs */ +#define APR_SVC_TEST_CLIENT 0x2 +#define APR_SVC_ADSP_CORE 0x3 +#define APR_SVC_AFE 0x4 +#define APR_SVC_VSM 0x5 +#define APR_SVC_VPM 0x6 +#define APR_SVC_ASM 0x7 +#define APR_SVC_ADM 0x8 +#define APR_SVC_ADSP_MVM 0x09 +#define APR_SVC_ADSP_CVS 0x0A +#define APR_SVC_ADSP_CVP 0x0B +#define APR_SVC_USM 0x0C +#define APR_SVC_LSM 0x0D +#define APR_SVC_VIDC 0x16 +#define APR_SVC_MAX 0x17 + +/* Modem Service IDs */ +#define APR_SVC_MVS 0x3 +#define APR_SVC_MVM 0x4 +#define APR_SVC_CVS 0x5 +#define APR_SVC_CVP 0x6 +#define APR_SVC_SRD 0x7 + +/* APR Port IDs */ +#define APR_MAX_PORTS 0x80 + +#define APR_NAME_MAX 0x40 + +#define RESET_EVENTS 0x000130D7 + +#define LPASS_RESTART_EVENT 0x1000 +#define LPASS_RESTART_READY 0x1001 + +struct apr_client_data { + uint16_t reset_event; + uint16_t reset_proc; + uint16_t payload_size; + uint16_t hdr_len; + uint16_t msg_type; + uint16_t src; + uint16_t dest_svc; + uint16_t src_port; + uint16_t dest_port; + uint32_t token; + uint32_t opcode; + void *payload; +}; + +typedef int32_t (*apr_fn)(struct apr_client_data *data, void *priv); + +struct apr_svc { + uint16_t id; + uint16_t dest_id; + uint16_t client_id; + uint16_t dest_domain; + uint8_t rvd; + uint8_t port_cnt; + uint8_t svc_cnt; + uint8_t need_reset; + apr_fn port_fn[APR_MAX_PORTS]; + void *port_priv[APR_MAX_PORTS]; + apr_fn fn; + void *priv; + struct mutex m_lock; + spinlock_t w_lock; + uint8_t pkt_owner; +}; + +struct apr_client { + uint8_t id; + uint8_t svc_cnt; + uint8_t rvd; + struct mutex m_lock; + struct apr_svc_ch_dev *handle; + struct apr_svc svc[APR_SVC_MAX]; +}; + +struct apr_rx_intents { + int num_of_intents; + uint32_t size; +}; + +struct apr_pkt_cfg { + uint8_t pkt_owner; + struct apr_rx_intents intents; +}; + +int apr_load_adsp_image(void); +struct apr_client *apr_get_client(int dest_id, int client_id); +int apr_wait_for_device_up(int dest_id); +int apr_get_svc(const char *svc_name, int dest_id, int *client_id, + int *svc_idx, int *svc_id); +void apr_cb_func(void *buf, int len, void *priv); +struct apr_svc *apr_register(char *dest, char *svc_name, apr_fn svc_fn, + uint32_t src_port, void *priv); +inline int apr_fill_hdr(void *handle, uint32_t *buf, uint16_t src_port, + uint16_t msg_type, uint16_t dest_port, + uint32_t token, uint32_t opcode, uint16_t len); + +int apr_send_pkt(void *handle, uint32_t *buf); +int apr_deregister(void *handle); +void subsys_notif_register(char *client_name, int domain, + struct notifier_block *nb); +void subsys_notif_deregister(char *client_name); +int apr_get_dest_id(char *dest); +uint16_t apr_get_data_src(struct apr_hdr *hdr); +void change_q6_state(int state); +void q6audio_dsp_not_responding(void); +void apr_reset(void *handle); +enum apr_subsys_state apr_get_subsys_state(void); +enum apr_subsys_state apr_get_modem_state(void); +void apr_set_modem_state(enum apr_subsys_state state); +enum apr_subsys_state apr_get_q6_state(void); +int apr_set_q6_state(enum apr_subsys_state state); +void apr_set_subsys_state(void); +const char *apr_get_lpass_subsys_name(void); +uint16_t apr_get_reset_domain(uint16_t proc); +int apr_start_rx_rt(void *handle); +int apr_end_rx_rt(void *handle); +#endif diff --git a/audio-kernel/include/ipc/apr_tal.h b/audio-kernel/include/ipc/apr_tal.h new file mode 100644 index 000000000..534fd84d2 --- /dev/null +++ b/audio-kernel/include/ipc/apr_tal.h @@ -0,0 +1,93 @@ +/* Copyright (c) 2010-2011, 2016-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 __APR_TAL_H_ +#define __APR_TAL_H_ + +#include +#include +#include + +/* APR Client IDs */ +#define APR_CLIENT_AUDIO 0x0 +#define APR_CLIENT_VOICE 0x1 +#define APR_CLIENT_MAX 0x2 + +#define APR_DL_SMD 0 +#define APR_DL_MAX 1 + +#define APR_DEST_MODEM 0 +#define APR_DEST_QDSP6 1 +#define APR_DEST_MAX 2 + +#if defined(CONFIG_MSM_QDSP6_APRV2_GLINK) || \ + defined(CONFIG_MSM_QDSP6_APRV3_GLINK) +#define APR_MAX_BUF 512 +#else +#define APR_MAX_BUF 8092 +#endif + +#define APR_DEFAULT_NUM_OF_INTENTS 20 + +#define APR_OPEN_TIMEOUT_MS 5000 + +enum { + /* If client sets the pkt_owner to APR_PKT_OWNER_DRIVER, APR + * driver will allocate a buffer, where the user packet is + * copied into, for each and every single Tx transmission. + * The buffer is thereafter passed to underlying link layer + * and freed upon the notification received from the link layer + * that the packet has been consumed. + */ + APR_PKT_OWNER_DRIVER, + /* If client sets the pkt_owner to APR_PKT_OWNER_CLIENT, APR + * will pass the user packet memory address directly to underlying + * link layer. In this case it is the client's responsibility to + * make sure the packet is intact until being notified that the + * packet has been consumed. + */ + APR_PKT_OWNER_CLIENT, +}; + +struct apr_pkt_priv { + /* This property is only applicable for APR over Glink. + * It is ignored in APR over SMD cases. + */ + uint8_t pkt_owner; +}; + +typedef void (*apr_svc_cb_fn)(void *buf, int len, void *priv); +struct apr_svc_ch_dev *apr_tal_open(uint32_t svc, uint32_t dest, + uint32_t dl, apr_svc_cb_fn func, void *priv); +int apr_tal_write(struct apr_svc_ch_dev *apr_ch, void *data, + struct apr_pkt_priv *pkt_priv, int len); +int apr_tal_close(struct apr_svc_ch_dev *apr_ch); +int apr_tal_rx_intents_config(struct apr_svc_ch_dev *apr_ch, + int num_of_intents, uint32_t size); +int apr_tal_init(void); +void apr_tal_exit(void); +int apr_tal_start_rx_rt(struct apr_svc_ch_dev *apr_ch); +int apr_tal_end_rx_rt(struct apr_svc_ch_dev *apr_ch); + +struct apr_svc_ch_dev { + void *handle; + spinlock_t w_lock; + spinlock_t r_lock; + struct mutex m_lock; + apr_svc_cb_fn func; + wait_queue_head_t wait; + void *priv; + unsigned int channel_state; + bool if_remote_intent_ready; +}; + +#endif diff --git a/audio-kernel/include/ipc/apr_us.h b/audio-kernel/include/ipc/apr_us.h new file mode 100644 index 000000000..6e2f63de9 --- /dev/null +++ b/audio-kernel/include/ipc/apr_us.h @@ -0,0 +1,193 @@ +/* Copyright (c) 2011-2014, 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 __APR_US_H__ +#define __APR_US_H__ + +#include + +/* ======================================================================= */ +/* Session Level commands */ + +#define USM_SESSION_CMD_RUN 0x00012306 +struct usm_stream_cmd_run { + struct apr_hdr hdr; + u32 flags; + u32 msw_ts; + u32 lsw_ts; +} __packed; + +/* Stream level commands */ +#define USM_STREAM_CMD_OPEN_READ 0x00012309 +struct usm_stream_cmd_open_read { + struct apr_hdr hdr; + u32 uMode; + u32 src_endpoint; + u32 pre_proc_top; + u32 format; +} __packed; + +#define USM_STREAM_CMD_OPEN_WRITE 0x00011271 +struct usm_stream_cmd_open_write { + struct apr_hdr hdr; + u32 format; +} __packed; + + +#define USM_STREAM_CMD_CLOSE 0x0001230A + +#define USM_STREAM_CMD_SET_PARAM 0x00012731 +struct usm_stream_cmd_set_param { + struct apr_hdr hdr; + u32 buf_addr_lsw; + u32 buf_addr_msw; + u32 mem_map_handle; + u32 buf_size; + u32 module_id; + u32 param_id; +} __packed; + +#define USM_STREAM_CMD_GET_PARAM 0x00012732 +struct usm_stream_cmd_get_param { + struct apr_hdr hdr; + u32 buf_addr_lsw; + u32 buf_addr_msw; + u32 mem_map_handle; + u32 buf_size; + u32 module_id; + u32 param_id; +} __packed; + +/* Encoder configuration definitions */ +#define USM_STREAM_CMD_SET_ENC_PARAM 0x0001230B +/* Decoder configuration definitions */ +#define USM_DATA_CMD_MEDIA_FORMAT_UPDATE 0x00011272 + +/* Encoder/decoder configuration block */ +#define USM_PARAM_ID_ENCDEC_ENC_CFG_BLK 0x0001230D + +/* Max number of static located ports (bytes) */ +#define USM_MAX_PORT_NUMBER 8 + +/* Max number of static located transparent data (bytes) */ +#define USM_MAX_CFG_DATA_SIZE 100 + +/* Parameter structures used in USM_STREAM_CMD_SET_ENCDEC_PARAM command */ +/* common declarations */ +struct usm_cfg_common { + u16 ch_cfg; + u16 bits_per_sample; + u32 sample_rate; + u32 dev_id; + u8 data_map[USM_MAX_PORT_NUMBER]; +} __packed; + +struct us_encdec_cfg { + u32 format_id; + struct usm_cfg_common cfg_common; + u16 params_size; + u8 *params; +} __packed; + +/* Start/stop US signal detection */ +#define USM_SESSION_CMD_SIGNAL_DETECT_MODE 0x00012719 + +struct usm_session_cmd_detect_info { + struct apr_hdr hdr; + u32 detect_mode; + u32 skip_interval; + u32 algorithm_cfg_size; +} __packed; + +/* US signal detection result */ +#define USM_SESSION_EVENT_SIGNAL_DETECT_RESULT 0x00012720 + +/* ======================================================================= */ +/* Session Level commands */ +#define USM_CMD_SHARED_MEM_MAP_REGION 0x00012728 +struct usm_cmd_memory_map_region { + struct apr_hdr hdr; + u16 mempool_id; + u16 num_regions; + u32 flags; + u32 shm_addr_lsw; + u32 shm_addr_msw; + u32 mem_size_bytes; +} __packed; + +#define USM_CMDRSP_SHARED_MEM_MAP_REGION 0x00012729 +struct usm_cmdrsp_memory_map_region { + u32 mem_map_handle; +} __packed; + +#define USM_CMD_SHARED_MEM_UNMAP_REGION 0x0001272A +struct usm_cmd_memory_unmap_region { + struct apr_hdr hdr; + u32 mem_map_handle; +} __packed; + +#define USM_DATA_CMD_READ 0x00012724 +struct usm_stream_cmd_read { + struct apr_hdr hdr; + u32 buf_addr_lsw; + u32 buf_addr_msw; + u32 mem_map_handle; + u32 buf_size; + u32 seq_id; + u32 counter; +} __packed; + +#define USM_DATA_EVENT_READ_DONE 0x00012725 + +#define USM_DATA_CMD_WRITE 0x00012726 +struct usm_stream_cmd_write { + struct apr_hdr hdr; + u32 buf_addr_lsw; + u32 buf_addr_msw; + u32 mem_map_handle; + u32 buf_size; + u32 seq_id; + u32 res0; + u32 res1; + u32 res2; +} __packed; + +#define USM_DATA_EVENT_WRITE_DONE 0x00012727 + +struct usm_stream_media_format_update { + struct apr_hdr hdr; + u32 format_id; + /* = sizeof(usm_cfg_common)+|transp_data| */ + u32 cfg_size; + struct usm_cfg_common cfg_common; + /* Transparent configuration data for specific encoder */ + u8 transp_data[USM_MAX_CFG_DATA_SIZE]; +} __packed; + +struct usm_encode_cfg_blk { + u32 frames_per_buf; + u32 format_id; + /* = sizeof(usm_cfg_common)+|transp_data| */ + u32 cfg_size; + struct usm_cfg_common cfg_common; + /* Transparent configuration data for specific encoder */ + u8 transp_data[USM_MAX_CFG_DATA_SIZE]; +} __packed; + +struct usm_stream_cmd_encdec_cfg_blk { + struct apr_hdr hdr; + u32 param_id; + u32 param_size; + struct usm_encode_cfg_blk enc_blk; +} __packed; + +#endif /* __APR_US_H__ */ diff --git a/audio-kernel/include/soc/internal.h b/audio-kernel/include/soc/internal.h new file mode 120000 index 000000000..ad0a98dfc --- /dev/null +++ b/audio-kernel/include/soc/internal.h @@ -0,0 +1 @@ +../../../../../../kernel/msm-4.14/drivers/base/regmap/internal.h \ No newline at end of file diff --git a/audio-kernel/include/soc/snd_event.h b/audio-kernel/include/soc/snd_event.h new file mode 100644 index 000000000..835941126 --- /dev/null +++ b/audio-kernel/include/soc/snd_event.h @@ -0,0 +1,79 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2018, The Linux Foundation. All rights reserved. + */ + +#ifndef _SND_EVENT_H_ +#define _SND_EVENT_H_ + +enum { + SND_EVENT_DOWN = 0, + SND_EVENT_UP, +}; + +struct snd_event_clients; + +struct snd_event_ops { + int (*enable)(struct device *dev, void *data); + void (*disable)(struct device *dev, void *data); +}; + +#ifdef CONFIG_SND_EVENT +int snd_event_client_register(struct device *dev, + const struct snd_event_ops *snd_ev_ops, + void *data); +int snd_event_client_deregister(struct device *dev); +int snd_event_master_register(struct device *dev, + const struct snd_event_ops *ops, + struct snd_event_clients *clients, + void *data); +int snd_event_master_deregister(struct device *dev); +int snd_event_notify(struct device *dev, unsigned int state); + +void snd_event_mstr_add_client(struct snd_event_clients **snd_clients, + int (*compare)(struct device *, void *), + void *data); +inline bool is_snd_event_fwk_enabled(void) +{ + return 1; +} +#else +static inline int snd_event_client_register(struct device *dev, + const struct snd_event_ops *snd_ev_ops, + void *data) +{ + return 0; +} +static inline int snd_event_client_deregister(struct device *dev) +{ + return 0; +} +static inline int snd_event_master_register(struct device *dev, + const struct snd_event_ops *ops, + struct snd_event_clients *clients, + void *data) +{ + return 0; +} +static inline int snd_event_master_deregister(struct device *dev) +{ + return 0; +} +static inline int snd_event_notify(struct device *dev, unsigned int state) +{ + return 0; +} + +static inline void snd_event_mstr_add_client(struct snd_event_clients **snd_clients, + int (*compare)(struct device *, void *), + void *data) +{ + return; +} +static inline bool is_snd_event_fwk_enabled(void) +{ + return 0; +} + +#endif /* CONFIG_SND_EVENT */ +#endif /* _SND_EVENT_H_ */ diff --git a/audio-kernel/include/soc/soundwire.h b/audio-kernel/include/soc/soundwire.h new file mode 100644 index 000000000..43ac1b9b9 --- /dev/null +++ b/audio-kernel/include/soc/soundwire.h @@ -0,0 +1,350 @@ +/* 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 + * 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 _LINUX_SOUNDWIRE_H +#define _LINUX_SOUNDWIRE_H +#include +#include +#include +#include + +extern struct bus_type soundwire_type; + +/* Soundwire supports max. of 8 channels per port */ +#define SWR_MAX_CHANNEL_NUM 8 +/* Soundwire supports max. of 14 ports on each device */ +#define SWR_MAX_DEV_PORT_NUM 14 +/* Maximum number of slave devices that a master can control */ +#define SWR_MAX_DEV_NUM 11 +/* Maximum number of ports on master so that it can accommodate all the port + * configurations of all devices + */ +#define SWR_MAX_MSTR_PORT_NUM (SWR_MAX_DEV_NUM * SWR_MAX_DEV_PORT_NUM) + +/* Indicates soundwire devices group information */ +enum { + SWR_GROUP_NONE = 0, + SWR_GROUP_12 = 12, + SWR_GROUP_13 = 13, + SWR_BROADCAST = 15, +}; + +/* + * struct swr_port_info - represents new soundwire frame shape + * with full data ports + * @list: link with other soundwire port info nodes + * @dev_num: logical device number of the soundwire slave device + * @port_en: flag indicates whether the port is enabled + * @slave_port_id: logical port number of the soundwire slave device + * @offset1: sample offset indicating the offset of the channel + * from the start of the frame + * @offset2: channel offset indicating offset between to channels + * @hstart: start offset for subframe window. + * @hstop: start offset for subframe window. + * @master_port_id: logical port number of corresponding soundwire master device + * @blk_grp_count: grouping count for n.o of channels. + * @blk_pack_mode: packing mode for channels in each port. + * @sinterval: sample interval indicates spacing from one sample + * event to the next + * @ch_en: channels enabled in a port. + * @req_ch: channels requested to be enabled in a port. + * @num_ch: number of channels enabled in a port + * @ch_rate: sampling rate of the channel with which data will be + * transferred + * + * Soundwire frame shape is created based on swr_port_info struct + * parameters. + */ +struct swr_port_info { + u8 dev_num; + u8 port_en; + u8 slave_port_id; + u8 offset1; + u8 offset2; + u8 sinterval; + struct list_head list; + u8 master_port_id; + u8 hstart; + u8 hstop; + u8 blk_grp_count; + u8 blk_pack_mode; + u8 word_length; + u8 lane_ctrl; + u8 ch_en; + u8 req_ch; + u8 num_ch; + u32 ch_rate; +}; + +/* + * struct swr_params - represent transfer of data from soundwire slave + * to soundwire master + * @tid: transaction ID to track each transaction + * @dev_num: logical device number of the soundwire slave device + * @num_port: number of ports that needs to be configured + * @port_id: array of logical port numbers of the soundwire slave device + * @num_ch: array of number of channels enabled + * @ch_rate: array of sampling rate of different channels that need to + * be configured + * @ch_en: array of channels mask for all the ports + * @port_type: the required master port type + */ +struct swr_params { + u8 tid; + u8 dev_num; + u8 num_port; + u8 port_id[SWR_MAX_DEV_PORT_NUM]; + u8 num_ch[SWR_MAX_DEV_PORT_NUM]; + u32 ch_rate[SWR_MAX_DEV_PORT_NUM]; + u8 ch_en[SWR_MAX_DEV_PORT_NUM]; + u8 port_type[SWR_MAX_DEV_PORT_NUM]; +}; + +/* + * struct swr_reg - struct to handle soundwire slave register read/writes + * @tid: transaction id for reg read/writes + * @dev_id: logical device number of the soundwire slave device + * @regaddr: 16 bit regaddr of soundwire slave + * @buf: value to be written/read to/from regaddr + * @len: length of the buffer buf + */ +struct swr_reg { + u8 tid; + u8 dev_id; + u32 regaddr; + u32 *buf; + u32 len; +}; + +/* + * struct swr_master - Interface to the soundwire master controller + * @dev: device interface to this driver + * @list: link with other soundwire master controllers + * @bus_num: board/SoC specific identifier for a soundwire master + * @mlock: mutex protecting master data structures + * @devices: list of devices on this master + * @port: logical port numbers of the soundwire master. This array + * can hold maximum master ports which is equal to number of slave + * devices multiplied by number of ports in each slave device + * @port_txn: table of port config transactions with transaction id + * @reg_txn: table of register transactions with transaction id + * @last_tid: size of table port_txn (can't grow beyond 256 since + * tid is 8 bits) + * @num_port: number of active ports on soundwire master + * @gr_sid: slave id used by the group for write operations + * @connect_port: callback for configuration of soundwire port(s) + * @disconnect_port: callback for disable of soundwire port(s) + * @read: callback for soundwire slave register read + * @write: callback for soundwire slave register write + * @get_logical_dev_num: callback to get soundwire slave logical + * device number + * @port_en_mask: bit mask of active ports on soundwire master + */ +struct swr_master { + struct device dev; + struct list_head list; + unsigned int bus_num; + struct mutex mlock; + struct list_head devices; + struct swr_port_info port[SWR_MAX_MSTR_PORT_NUM]; + struct swr_params **port_txn; + struct swr_reg **reg_txn; + u8 last_tid; + u8 num_port; + u8 num_dev; + u8 gr_sid; + int (*connect_port)(struct swr_master *mstr, struct swr_params *txn); + int (*disconnect_port)(struct swr_master *mstr, struct swr_params *txn); + int (*read)(struct swr_master *mstr, u8 dev_num, u16 reg_addr, + void *buf, u32 len); + int (*write)(struct swr_master *mstr, u8 dev_num, u16 reg_addr, + const void *buf); + int (*bulk_write)(struct swr_master *master, u8 dev_num, void *reg, + const void *buf, size_t len); + int (*get_logical_dev_num)(struct swr_master *mstr, u64 dev_id, + u8 *dev_num); + int (*slvdev_datapath_control)(struct swr_master *mstr, bool enable); + bool (*remove_from_group)(struct swr_master *mstr); + void (*device_wakeup_vote)(struct swr_master *mstr); + void (*device_wakeup_unvote)(struct swr_master *mstr); + u16 port_en_mask; + +}; + +static inline struct swr_master *to_swr_master(struct device *dev) +{ + return dev ? container_of(dev, struct swr_master, dev) : NULL; +} + +/* + * struct swr_device - represent a soundwire slave device + * @name: indicates the name of the device, defined in devicetree + * binding under soundwire slave device node as a compatible field. + * @master: soundwire master managing the bus hosting this device + * @driver: Device's driver. Pointer to access routines + * @dev_list: list of devices on a controller + * @dev_num: logical device number of the soundwire slave device + * @dev: driver model representation of the device + * @addr: represents "ea-addr" which is unique-id of soundwire slave + * device + * @group_id: group id supported by the slave device + * @slave_irq: irq handle of slave to be invoked by master + * during slave interrupt + */ +struct swr_device { + char name[SOUNDWIRE_NAME_SIZE]; + struct swr_master *master; + struct swr_driver *driver; + struct list_head dev_list; + u8 dev_num; + struct device dev; + unsigned long addr; + u8 group_id; + struct irq_domain *slave_irq; + bool slave_irq_pending; +}; + +static inline struct swr_device *to_swr_device(struct device *dev) +{ + return dev ? container_of(dev, struct swr_device, dev) : NULL; +} + +/* + * struct swr_driver - Manage soundwire slave device driver + * @probe: binds this driver to soundwire device + * @remove: unbinds this driver from soundwire device + * @shutdown: standard shutdown callback used during power down/halt + * @suspend: standard suspend callback used during system suspend + * @resume: standard resume callback used during system resume + * @driver: soundwire device drivers should initialize name and + * owner field of this structure + * @id_table: list of soundwire devices supported by this driver + */ +struct swr_driver { + int (*probe)(struct swr_device *swr); + int (*remove)(struct swr_device *swr); + void (*shutdown)(struct swr_device *swr); + int (*suspend)(struct swr_device *swr, pm_message_t pmesg); + int (*resume)(struct swr_device *swr); + int (*device_up)(struct swr_device *swr); + int (*device_down)(struct swr_device *swr); + int (*reset_device)(struct swr_device *swr); + struct device_driver driver; + const struct swr_device_id *id_table; +}; + +static inline struct swr_driver *to_swr_driver(struct device_driver *drv) +{ + return drv ? container_of(drv, struct swr_driver, driver) : NULL; +} + +/* + * struct swr_boardinfo - Declare board info for soundwire device bringup + * @name: name to initialize swr_device.name + * @bus_num: identifies which soundwire master parents the soundwire + * slave_device + * @addr: represents "ea-addr" of soundwire slave device + * @of_node: pointer to OpenFirmware device node + * @swr_slave: device to be registered with soundwire + */ +struct swr_boardinfo { + char name[SOUNDWIRE_NAME_SIZE]; + int bus_num; + u64 addr; + struct device_node *of_node; + struct swr_device *swr_slave; +}; + +static inline void *swr_get_ctrl_data(const struct swr_master *master) +{ + return master ? dev_get_drvdata(&master->dev) : NULL; +} + +static inline void swr_set_ctrl_data(struct swr_master *master, void *data) +{ + dev_set_drvdata(&master->dev, data); +} + +static inline void *swr_get_dev_data(const struct swr_device *dev) +{ + return dev ? dev_get_drvdata(&dev->dev) : NULL; +} + +static inline void swr_set_dev_data(struct swr_device *dev, void *data) +{ + dev_set_drvdata(&dev->dev, data); +} + +extern int swr_startup_devices(struct swr_device *swr_dev); + +extern struct swr_device *swr_new_device(struct swr_master *master, + struct swr_boardinfo const *info); + +extern int of_register_swr_devices(struct swr_master *master); + +extern void swr_port_response(struct swr_master *mstr, u8 tid); + +extern int swr_get_logical_dev_num(struct swr_device *dev, u64 dev_id, + u8 *dev_num); + +extern int swr_read(struct swr_device *dev, u8 dev_num, u16 reg_addr, + void *buf, u32 len); + +extern int swr_write(struct swr_device *dev, u8 dev_num, u16 reg_addr, + const void *buf); + +extern int swr_bulk_write(struct swr_device *dev, u8 dev_num, void *reg_addr, + const void *buf, size_t len); + +extern int swr_connect_port(struct swr_device *dev, u8 *port_id, u8 num_port, + u8 *ch_mask, u32 *ch_rate, u8 *num_ch, + u8 *port_type); + +extern int swr_disconnect_port(struct swr_device *dev, + u8 *port_id, u8 num_port, u8 *ch_mask, + u8 *port_type); + +extern int swr_set_device_group(struct swr_device *swr_dev, u8 id); + +extern int swr_driver_register(struct swr_driver *drv); + +extern void swr_driver_unregister(struct swr_driver *drv); + +extern int swr_add_device(struct swr_master *master, + struct swr_device *swrdev); +extern void swr_remove_device(struct swr_device *swr); + +extern void swr_master_add_boarddevices(struct swr_master *master); + +extern void swr_unregister_master(struct swr_master *master); + +extern int swr_register_master(struct swr_master *master); + +extern int swr_device_up(struct swr_device *swr_dev); + +extern int swr_device_down(struct swr_device *swr_dev); + +extern int swr_reset_device(struct swr_device *swr_dev); + +extern int swr_slvdev_datapath_control(struct swr_device *swr_dev, u8 dev_num, + bool enable); +extern int swr_remove_from_group(struct swr_device *dev, u8 dev_num); + +extern void swr_remove_device(struct swr_device *swr_dev); + +extern struct swr_device *get_matching_swr_slave_device(struct device_node *np); + +extern int swr_device_wakeup_vote(struct swr_device *dev); + +extern int swr_device_wakeup_unvote(struct swr_device *dev); + +#endif /* _LINUX_SOUNDWIRE_H */ diff --git a/audio-kernel/include/soc/swr-wcd.h b/audio-kernel/include/soc/swr-wcd.h new file mode 100644 index 000000000..7eaad2756 --- /dev/null +++ b/audio-kernel/include/soc/swr-wcd.h @@ -0,0 +1,50 @@ +/* Copyright (c) 2015, 2017-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 _LINUX_SWR_WCD_H +#define _LINUX_SWR_WCD_H +#include +#include +#include +#include + +enum { + SWR_CH_MAP, + SWR_DEVICE_DOWN, + SWR_DEVICE_UP, + SWR_SUBSYS_RESTART, + SWR_SET_NUM_RX_CH, + SWR_CLK_FREQ, + SWR_DEVICE_SSR_DOWN, + SWR_DEVICE_SSR_UP, + SWR_REGISTER_WAKE_IRQ, +}; + +struct swr_mstr_port { + int num_port; + u8 *port; +}; + +#define MCLK_FREQ 9600000 +#define MCLK_FREQ_NATIVE 11289600 + +#if (IS_ENABLED(CONFIG_SOUNDWIRE_WCD_CTRL) || \ + IS_ENABLED(CONFIG_SOUNDWIRE_MSTR_CTRL)) +extern int swrm_wcd_notify(struct platform_device *pdev, u32 id, void *data); +#else /* CONFIG_SOUNDWIRE_WCD_CTRL */ +static inline int swrm_wcd_notify(struct platform_device *pdev, u32 id, + void *data) +{ + return 0; +} +#endif /* CONFIG_SOUNDWIRE_WCD_CTRL */ +#endif /* _LINUX_SWR_WCD_H */ diff --git a/audio-kernel/include/uapi/Android.mk b/audio-kernel/include/uapi/Android.mk new file mode 100644 index 000000000..476c1c059 --- /dev/null +++ b/audio-kernel/include/uapi/Android.mk @@ -0,0 +1,30 @@ +# Use this by setting +# LOCAL_HEADER_LIBRARIES := audio_kernel_headers + +LOCAL_PATH := $(call my-dir) +MYLOCAL_PATH := $(LOCAL_PATH) + +UAPI_OUT := $(PRODUCT_OUT)/obj/vendor/qcom/opensource/audio-kernel/include + +AUDIO_KERNEL_HEADERS := $(call all-named-files-under,*.h,linux) $(call all-named-files-under,*.h,sound) + +HEADER_INSTALL_DIR := kernel/msm-$(TARGET_KERNEL_VERSION)/scripts + +BUILD_ROOT_RELATIVE := $(ANDROID_BUILD_TOP)/ + +include $(CLEAR_VARS) +LOCAL_MODULE := audio_kernel_headers +LOCAL_ADDITIONAL_DEPENDENCIES := $(TARGET_PREBUILT_INT_KERNEL) + +GEN := $(addprefix $(UAPI_OUT)/,$(AUDIO_KERNEL_HEADERS)) +$(GEN): $(KERNEL_USR) +$(GEN): $(TARGET_OUT_INTERMEDIATES)/KERNEL_OBJ/usr +$(GEN): PRIVATE_PATH := $(MYLOCAL_PATH) +$(GEN): PRIVATE_CUSTOM_TOOL = $(shell cd $(TARGET_OUT_INTERMEDIATES)/KERNEL_OBJ; $(BUILD_ROOT_RELATIVE)$(HEADER_INSTALL_DIR)/headers_install.sh $(BUILD_ROOT_RELATIVE)$(dir $@) $(BUILD_ROOT_RELATIVE)$(subst $(UAPI_OUT),$(MYLOCAL_PATH),$(dir $@)) $(notdir $@)) +$(GEN): $(addprefix $(MYLOCAL_PATH)/,$(AUDIO_KERNEL_HEADERS)) + $(transform-generated-source) + +LOCAL_GENERATED_SOURCES := $(GEN) +LOCAL_EXPORT_C_INCLUDE_DIRS := $(UAPI_OUT) + +include $(BUILD_HEADER_LIBRARY) diff --git a/audio-kernel/include/uapi/Kbuild b/audio-kernel/include/uapi/Kbuild new file mode 100644 index 000000000..2eb179d4d --- /dev/null +++ b/audio-kernel/include/uapi/Kbuild @@ -0,0 +1,6 @@ +# UAPI Header export list +# Top-level Makefile calls into asm-$(ARCH) +# List only non-arch directories below + +header-y += linux/ +header-y += sound/ diff --git a/audio-kernel/include/uapi/linux/Kbuild b/audio-kernel/include/uapi/linux/Kbuild new file mode 100644 index 000000000..f6af2bf8c --- /dev/null +++ b/audio-kernel/include/uapi/linux/Kbuild @@ -0,0 +1,20 @@ +# UAPI Header export list +header-y += avtimer.h +header-y += msm_audio.h +header-y += msm_audio_aac.h +header-y += msm_audio_ac3.h +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_mvs.h +header-y += msm_audio_qcp.h +header-y += msm_audio_sbc.h +header-y += msm_audio_voicememo.h +header-y += msm_audio_wma.h +header-y += msm_audio_wmapro.h +header-y += msm_audio_alac.h +header-y += msm_audio_ape.h +header-y += msm_audio_g711.h +header-y += msm_audio_g711_dec.h +header-y += mfd/ diff --git a/audio-kernel/include/uapi/linux/avtimer.h b/audio-kernel/include/uapi/linux/avtimer.h new file mode 100644 index 000000000..96b5483fb --- /dev/null +++ b/audio-kernel/include/uapi/linux/avtimer.h @@ -0,0 +1,10 @@ +#ifndef _UAPI_AVTIMER_H +#define _UAPI_AVTIMER_H + +#include + +#define MAJOR_NUM 100 + +#define IOCTL_GET_AVTIMER_TICK _IOR(MAJOR_NUM, 0, uint64_t) + +#endif diff --git a/audio-kernel/include/uapi/linux/mfd/Kbuild b/audio-kernel/include/uapi/linux/mfd/Kbuild new file mode 100644 index 000000000..9377c98b0 --- /dev/null +++ b/audio-kernel/include/uapi/linux/mfd/Kbuild @@ -0,0 +1 @@ +header-y += wcd9xxx/ diff --git a/audio-kernel/include/uapi/linux/mfd/wcd9xxx/Kbuild b/audio-kernel/include/uapi/linux/mfd/wcd9xxx/Kbuild new file mode 100644 index 000000000..8e55965bb --- /dev/null +++ b/audio-kernel/include/uapi/linux/mfd/wcd9xxx/Kbuild @@ -0,0 +1,2 @@ +header-y += wcd9xxx_registers.h +header-y += wcd9320_registers.h diff --git a/audio-kernel/include/uapi/linux/mfd/wcd9xxx/wcd9320_registers.h b/audio-kernel/include/uapi/linux/mfd/wcd9xxx/wcd9320_registers.h new file mode 100644 index 000000000..63ab62419 --- /dev/null +++ b/audio-kernel/include/uapi/linux/mfd/wcd9xxx/wcd9320_registers.h @@ -0,0 +1,1399 @@ +#ifndef WCD9320_REGISTERS_H +#define WCD9320_REGISTERS_H + +#include + +#define TAIKO_A_CHIP_CTL WCD9XXX_A_CHIP_CTL +#define TAIKO_A_CHIP_CTL__POR WCD9XXX_A_CHIP_CTL__POR +#define TAIKO_A_CHIP_STATUS WCD9XXX_A_CHIP_STATUS +#define TAIKO_A_CHIP_STATUS__POR WCD9XXX_A_CHIP_STATUS__POR +#define TAIKO_A_CHIP_ID_BYTE_0 WCD9XXX_A_CHIP_ID_BYTE_0 +#define TAIKO_A_CHIP_ID_BYTE_0__POR WCD9XXX_A_CHIP_ID_BYTE_0__POR +#define TAIKO_A_CHIP_ID_BYTE_1 WCD9XXX_A_CHIP_ID_BYTE_1 +#define TAIKO_A_CHIP_ID_BYTE_1__POR WCD9XXX_A_CHIP_ID_BYTE_1__POR +#define TAIKO_A_CHIP_ID_BYTE_2 WCD9XXX_A_CHIP_ID_BYTE_2 +#define TAIKO_A_CHIP_ID_BYTE_2__POR WCD9XXX_A_CHIP_ID_BYTE_2__POR +#define TAIKO_A_CHIP_ID_BYTE_3 WCD9XXX_A_CHIP_ID_BYTE_3 +#define TAIKO_A_CHIP_ID_BYTE_3__POR WCD9XXX_A_CHIP_ID_BYTE_3__POR +#define TAIKO_A_CHIP_VERSION WCD9XXX_A_CHIP_VERSION +#define TAIKO_A_CHIP_VERSION__POR WCD9XXX_A_CHIP_VERSION__POR +#define TAIKO_A_SB_VERSION WCD9XXX_A_SB_VERSION +#define TAIKO_A_SB_VERSION__POR WCD9XXX_A_SB_VERSION__POR +#define TAIKO_A_SLAVE_ID_1 WCD9XXX_A_SLAVE_ID_1 +#define TAIKO_A_SLAVE_ID_1__POR WCD9XXX_A_SLAVE_ID_1__POR +#define TAIKO_A_SLAVE_ID_2 WCD9XXX_A_SLAVE_ID_2 +#define TAIKO_A_SLAVE_ID_2__POR WCD9XXX_A_SLAVE_ID_2__POR +#define TAIKO_A_SLAVE_ID_3 WCD9XXX_A_SLAVE_ID_3 +#define TAIKO_A_SLAVE_ID_3__POR WCD9XXX_A_SLAVE_ID_3__POR +#define TAIKO_A_PIN_CTL_OE0 (0x010) +#define TAIKO_A_PIN_CTL_OE0__POR (0x00) +#define TAIKO_A_PIN_CTL_OE1 (0x011) +#define TAIKO_A_PIN_CTL_OE1__POR (0x00) +#define TAIKO_A_PIN_CTL_DATA0 (0x012) +#define TAIKO_A_PIN_CTL_DATA0__POR (0x00) +#define TAIKO_A_PIN_CTL_DATA1 (0x013) +#define TAIKO_A_PIN_CTL_DATA1__POR (0x00) +#define TAIKO_A_HDRIVE_GENERIC (0x018) +#define TAIKO_A_HDRIVE_GENERIC__POR (0x00) +#define TAIKO_A_HDRIVE_OVERRIDE (0x019) +#define TAIKO_A_HDRIVE_OVERRIDE__POR (0x08) +#define TAIKO_A_ANA_CSR_WAIT_STATE (0x020) +#define TAIKO_A_ANA_CSR_WAIT_STATE__POR (0x44) +#define TAIKO_A_PROCESS_MONITOR_CTL0 (0x040) +#define TAIKO_A_PROCESS_MONITOR_CTL0__POR (0x80) +#define TAIKO_A_PROCESS_MONITOR_CTL1 (0x041) +#define TAIKO_A_PROCESS_MONITOR_CTL1__POR (0x00) +#define TAIKO_A_PROCESS_MONITOR_CTL2 (0x042) +#define TAIKO_A_PROCESS_MONITOR_CTL2__POR (0x00) +#define TAIKO_A_PROCESS_MONITOR_CTL3 (0x043) +#define TAIKO_A_PROCESS_MONITOR_CTL3__POR (0x01) +#define TAIKO_A_QFUSE_CTL (0x048) +#define TAIKO_A_QFUSE_CTL__POR (0x00) +#define TAIKO_A_QFUSE_STATUS (0x049) +#define TAIKO_A_QFUSE_STATUS__POR (0x00) +#define TAIKO_A_QFUSE_DATA_OUT0 (0x04A) +#define TAIKO_A_QFUSE_DATA_OUT0__POR (0x00) +#define TAIKO_A_QFUSE_DATA_OUT1 (0x04B) +#define TAIKO_A_QFUSE_DATA_OUT1__POR (0x00) +#define TAIKO_A_QFUSE_DATA_OUT2 (0x04C) +#define TAIKO_A_QFUSE_DATA_OUT2__POR (0x00) +#define TAIKO_A_QFUSE_DATA_OUT3 (0x04D) +#define TAIKO_A_QFUSE_DATA_OUT3__POR (0x00) +#define TAIKO_A_QFUSE_DATA_OUT4 (0x04E) +#define TAIKO_A_QFUSE_DATA_OUT4__POR (0x00) +#define TAIKO_A_QFUSE_DATA_OUT5 (0x04F) +#define TAIKO_A_QFUSE_DATA_OUT5__POR (0x00) +#define TAIKO_A_QFUSE_DATA_OUT6 (0x050) +#define TAIKO_A_QFUSE_DATA_OUT6__POR (0x00) +#define TAIKO_A_QFUSE_DATA_OUT7 (0x051) +#define TAIKO_A_QFUSE_DATA_OUT7__POR (0x00) +#define TAIKO_A_CDC_CTL WCD9XXX_A_CDC_CTL +#define TAIKO_A_CDC_CTL__POR WCD9XXX_A_CDC_CTL__POR +#define TAIKO_A_LEAKAGE_CTL WCD9XXX_A_LEAKAGE_CTL +#define TAIKO_A_LEAKAGE_CTL__POR WCD9XXX_A_LEAKAGE_CTL__POR +#define TAIKO_A_INTR_MODE (0x090) +#define TAIKO_A_INTR_MODE__POR (0x00) +#define TAIKO_A_INTR_MASK0 (0x094) +#define TAIKO_A_INTR_MASK0__POR (0xFF) +#define TAIKO_A_INTR_MASK1 (0x095) +#define TAIKO_A_INTR_MASK1__POR (0xFF) +#define TAIKO_A_INTR_MASK2 (0x096) +#define TAIKO_A_INTR_MASK2__POR (0x3F) +#define TAIKO_A_INTR_MASK3 (0x097) +#define TAIKO_A_INTR_MASK3__POR (0x3F) +#define TAIKO_A_INTR_STATUS0 (0x098) +#define TAIKO_A_INTR_STATUS0__POR (0x00) +#define TAIKO_A_INTR_STATUS1 (0x099) +#define TAIKO_A_INTR_STATUS1__POR (0x00) +#define TAIKO_A_INTR_STATUS2 (0x09A) +#define TAIKO_A_INTR_STATUS2__POR (0x00) +#define TAIKO_A_INTR_STATUS3 (0x09B) +#define TAIKO_A_INTR_STATUS3__POR (0x00) +#define TAIKO_A_INTR_CLEAR0 (0x09C) +#define TAIKO_A_INTR_CLEAR0__POR (0x00) +#define TAIKO_A_INTR_CLEAR1 (0x09D) +#define TAIKO_A_INTR_CLEAR1__POR (0x00) +#define TAIKO_A_INTR_CLEAR2 (0x09E) +#define TAIKO_A_INTR_CLEAR2__POR (0x00) +#define TAIKO_A_INTR_CLEAR3 (0x09F) +#define TAIKO_A_INTR_CLEAR3__POR (0x00) +#define TAIKO_A_INTR_LEVEL0 (0x0A0) +#define TAIKO_A_INTR_LEVEL0__POR (0x01) +#define TAIKO_A_INTR_LEVEL1 (0x0A1) +#define TAIKO_A_INTR_LEVEL1__POR (0x00) +#define TAIKO_A_INTR_LEVEL2 (0x0A2) +#define TAIKO_A_INTR_LEVEL2__POR (0x00) +#define TAIKO_A_INTR_LEVEL3 (0x0A3) +#define TAIKO_A_INTR_LEVEL3__POR (0x00) +#define TAIKO_A_INTR_TEST0 (0x0A4) +#define TAIKO_A_INTR_TEST0__POR (0x00) +#define TAIKO_A_INTR_TEST1 (0x0A5) +#define TAIKO_A_INTR_TEST1__POR (0x00) +#define TAIKO_A_INTR_TEST2 (0x0A6) +#define TAIKO_A_INTR_TEST2__POR (0x00) +#define TAIKO_A_INTR_TEST3 (0x0A7) +#define TAIKO_A_INTR_TEST3__POR (0x00) +#define TAIKO_A_INTR_SET0 (0x0A8) +#define TAIKO_A_INTR_SET0__POR (0x00) +#define TAIKO_A_INTR_SET1 (0x0A9) +#define TAIKO_A_INTR_SET1__POR (0x00) +#define TAIKO_A_INTR_SET2 (0x0AA) +#define TAIKO_A_INTR_SET2__POR (0x00) +#define TAIKO_A_INTR_SET3 (0x0AB) +#define TAIKO_A_INTR_SET3__POR (0x00) +#define TAIKO_A_INTR_DESTN0 (0x0AC) +#define TAIKO_A_INTR_DESTN0__POR (0x00) +#define TAIKO_A_INTR_DESTN1 (0x0AD) +#define TAIKO_A_INTR_DESTN1__POR (0x00) +#define TAIKO_A_INTR_DESTN2 (0x0AE) +#define TAIKO_A_INTR_DESTN2__POR (0x00) +#define TAIKO_A_INTR_DESTN3 (0x0AF) +#define TAIKO_A_INTR_DESTN3__POR (0x00) +#define TAIKO_A_CDC_TX_I2S_SCK_MODE (0x0C0) +#define TAIKO_A_CDC_TX_I2S_SCK_MODE__POR (0x00) +#define TAIKO_A_CDC_TX_I2S_WS_MODE (0x0C1) +#define TAIKO_A_CDC_TX_I2S_WS_MODE__POR (0x00) +#define TAIKO_A_CDC_DMIC_DATA0_MODE (0x0C4) +#define TAIKO_A_CDC_DMIC_DATA0_MODE__POR (0x00) +#define TAIKO_A_CDC_DMIC_CLK0_MODE (0x0C5) +#define TAIKO_A_CDC_DMIC_CLK0_MODE__POR (0x00) +#define TAIKO_A_CDC_DMIC_DATA1_MODE (0x0C6) +#define TAIKO_A_CDC_DMIC_DATA1_MODE__POR (0x00) +#define TAIKO_A_CDC_DMIC_CLK1_MODE (0x0C7) +#define TAIKO_A_CDC_DMIC_CLK1_MODE__POR (0x00) +#define TAIKO_A_CDC_RX_I2S_SCK_MODE (0x0C8) +#define TAIKO_A_CDC_RX_I2S_SCK_MODE__POR (0x00) +#define TAIKO_A_CDC_RX_I2S_WS_MODE (0x0C9) +#define TAIKO_A_CDC_RX_I2S_WS_MODE__POR (0x00) +#define TAIKO_A_CDC_DMIC_DATA2_MODE (0x0CA) +#define TAIKO_A_CDC_DMIC_DATA2_MODE__POR (0x00) +#define TAIKO_A_CDC_DMIC_CLK2_MODE (0x0CB) +#define TAIKO_A_CDC_DMIC_CLK2_MODE__POR (0x00) +#define TAIKO_A_CDC_INTR1_MODE (0x0CC) +#define TAIKO_A_CDC_INTR1_MODE__POR (0x00) +#define TAIKO_A_CDC_SB_NRZ_SEL_MODE (0x0CD) +#define TAIKO_A_CDC_SB_NRZ_SEL_MODE__POR (0x00) +#define TAIKO_A_CDC_INTR2_MODE (0x0CE) +#define TAIKO_A_CDC_INTR2_MODE__POR (0x00) +#define TAIKO_A_CDC_RF_PA_ON_MODE (0x0CF) +#define TAIKO_A_CDC_RF_PA_ON_MODE__POR (0x00) +#define TAIKO_A_BIAS_REF_CTL (0x100) +#define TAIKO_A_BIAS_REF_CTL__POR (0x1C) +#define TAIKO_A_BIAS_CENTRAL_BG_CTL (0x101) +#define TAIKO_A_BIAS_CENTRAL_BG_CTL__POR (0x50) +#define TAIKO_A_BIAS_PRECHRG_CTL (0x102) +#define TAIKO_A_BIAS_PRECHRG_CTL__POR (0x07) +#define TAIKO_A_BIAS_CURR_CTL_1 (0x103) +#define TAIKO_A_BIAS_CURR_CTL_1__POR (0x52) +#define TAIKO_A_BIAS_CURR_CTL_2 (0x104) +#define TAIKO_A_BIAS_CURR_CTL_2__POR (0x00) +#define TAIKO_A_BIAS_OSC_BG_CTL (0x105) +#define TAIKO_A_BIAS_OSC_BG_CTL__POR (0x16) +#define TAIKO_A_CLK_BUFF_EN1 (0x108) +#define TAIKO_A_CLK_BUFF_EN1__POR (0x04) +#define TAIKO_A_CLK_BUFF_EN2 (0x109) +#define TAIKO_A_CLK_BUFF_EN2__POR (0x02) +#define TAIKO_A_LDO_H_MODE_1 (0x110) +#define TAIKO_A_LDO_H_MODE_1__POR (0x65) +#define TAIKO_A_LDO_H_MODE_2 (0x111) +#define TAIKO_A_LDO_H_MODE_2__POR (0xA8) +#define TAIKO_A_LDO_H_LOOP_CTL (0x112) +#define TAIKO_A_LDO_H_LOOP_CTL__POR (0x6B) +#define TAIKO_A_LDO_H_COMP_1 (0x113) +#define TAIKO_A_LDO_H_COMP_1__POR (0x84) +#define TAIKO_A_LDO_H_COMP_2 (0x114) +#define TAIKO_A_LDO_H_COMP_2__POR (0xE0) +#define TAIKO_A_LDO_H_BIAS_1 (0x115) +#define TAIKO_A_LDO_H_BIAS_1__POR (0x6D) +#define TAIKO_A_LDO_H_BIAS_2 (0x116) +#define TAIKO_A_LDO_H_BIAS_2__POR (0xA5) +#define TAIKO_A_LDO_H_BIAS_3 (0x117) +#define TAIKO_A_LDO_H_BIAS_3__POR (0x60) +#define TAIKO_A_VBAT_CLK (0x118) +#define TAIKO_A_VBAT_CLK__POR (0x03) +#define TAIKO_A_VBAT_LOOP (0x119) +#define TAIKO_A_VBAT_LOOP__POR (0x02) +#define TAIKO_A_VBAT_REF (0x11A) +#define TAIKO_A_VBAT_REF__POR (0x20) +#define TAIKO_A_VBAT_ADC_TEST (0x11B) +#define TAIKO_A_VBAT_ADC_TEST__POR (0x00) +#define TAIKO_A_VBAT_FE (0x11C) +#define TAIKO_A_VBAT_FE__POR (0x48) +#define TAIKO_A_VBAT_BIAS_1 (0x11D) +#define TAIKO_A_VBAT_BIAS_1__POR (0x03) +#define TAIKO_A_VBAT_BIAS_2 (0x11E) +#define TAIKO_A_VBAT_BIAS_2__POR (0x00) +#define TAIKO_A_VBAT_ADC_DATA_MSB (0x11F) +#define TAIKO_A_VBAT_ADC_DATA_MSB__POR (0x00) +#define TAIKO_A_VBAT_ADC_DATA_LSB (0x120) +#define TAIKO_A_VBAT_ADC_DATA_LSB__POR (0x00) +#define TAIKO_A_MICB_CFILT_1_CTL (0x128) +#define TAIKO_A_MICB_CFILT_1_CTL__POR (0x40) +#define TAIKO_A_MICB_CFILT_1_VAL (0x129) +#define TAIKO_A_MICB_CFILT_1_VAL__POR (0x80) +#define TAIKO_A_MICB_CFILT_1_PRECHRG (0x12A) +#define TAIKO_A_MICB_CFILT_1_PRECHRG__POR (0x38) +#define TAIKO_A_MICB_1_CTL (0x12B) +#define TAIKO_A_MICB_1_CTL__POR (0x16) +#define TAIKO_A_MICB_1_INT_RBIAS (0x12C) +#define TAIKO_A_MICB_1_INT_RBIAS__POR (0x24) +#define TAIKO_A_MICB_1_MBHC (0x12D) +#define TAIKO_A_MICB_1_MBHC__POR (0x01) +#define TAIKO_A_MICB_CFILT_2_CTL (0x12E) +#define TAIKO_A_MICB_CFILT_2_CTL__POR (0x40) +#define TAIKO_A_MICB_CFILT_2_VAL (0x12F) +#define TAIKO_A_MICB_CFILT_2_VAL__POR (0x80) +#define TAIKO_A_MICB_CFILT_2_PRECHRG (0x130) +#define TAIKO_A_MICB_CFILT_2_PRECHRG__POR (0x38) +#define TAIKO_A_MICB_2_CTL (0x131) +#define TAIKO_A_MICB_2_CTL__POR (0x16) +#define TAIKO_A_MICB_2_INT_RBIAS (0x132) +#define TAIKO_A_MICB_2_INT_RBIAS__POR (0x24) +#define TAIKO_A_MICB_2_MBHC (0x133) +#define TAIKO_A_MICB_2_MBHC__POR (0x02) +#define TAIKO_A_MICB_CFILT_3_CTL (0x134) +#define TAIKO_A_MICB_CFILT_3_CTL__POR (0x40) +#define TAIKO_A_MICB_CFILT_3_VAL (0x135) +#define TAIKO_A_MICB_CFILT_3_VAL__POR (0x80) +#define TAIKO_A_MICB_CFILT_3_PRECHRG (0x136) +#define TAIKO_A_MICB_CFILT_3_PRECHRG__POR (0x38) +#define TAIKO_A_MICB_3_CTL (0x137) +#define TAIKO_A_MICB_3_CTL__POR (0x16) +#define TAIKO_A_MICB_3_INT_RBIAS (0x138) +#define TAIKO_A_MICB_3_INT_RBIAS__POR (0x24) +#define TAIKO_A_MICB_3_MBHC (0x139) +#define TAIKO_A_MICB_3_MBHC__POR (0x00) +#define TAIKO_A_MICB_4_CTL (0x13D) +#define TAIKO_A_MICB_4_CTL__POR (0x16) +#define TAIKO_A_MICB_4_INT_RBIAS (0x13E) +#define TAIKO_A_MICB_4_INT_RBIAS__POR (0x24) +#define TAIKO_A_MICB_4_MBHC (0x13F) +#define TAIKO_A_MICB_4_MBHC__POR (0x01) +#define TAIKO_A_MBHC_INSERT_DETECT (0x14A) +#define TAIKO_A_MBHC_INSERT_DETECT__POR (0x00) +#define TAIKO_A_MBHC_INSERT_DET_STATUS (0x14B) +#define TAIKO_A_MBHC_INSERT_DET_STATUS__POR (0x00) +#define TAIKO_A_TX_COM_BIAS (0x14C) +#define TAIKO_A_TX_COM_BIAS__POR (0xF0) +#define TAIKO_A_MBHC_SCALING_MUX_1 (0x14E) +#define TAIKO_A_MBHC_SCALING_MUX_1__POR (0x00) +#define TAIKO_A_MBHC_SCALING_MUX_2 (0x14F) +#define TAIKO_A_MBHC_SCALING_MUX_2__POR (0x80) +#define TAIKO_A_MAD_ANA_CTRL (0x150) +#define TAIKO_A_MAD_ANA_CTRL__POR (0xF1) +#define TAIKO_A_TX_SUP_SWITCH_CTRL_1 (0x151) +#define TAIKO_A_TX_SUP_SWITCH_CTRL_1__POR (0x00) +#define TAIKO_A_TX_SUP_SWITCH_CTRL_2 (0x152) +#define TAIKO_A_TX_SUP_SWITCH_CTRL_2__POR (0x80) +#define TAIKO_A_TX_1_2_EN (0x153) +#define TAIKO_A_TX_1_2_EN__POR (0x00) +#define TAIKO_A_TX_1_2_TEST_EN (0x154) +#define TAIKO_A_TX_1_2_TEST_EN__POR (0xCC) +#define TAIKO_A_TX_1_2_ADC_CH1 (0x155) +#define TAIKO_A_TX_1_2_ADC_CH1__POR (0x44) +#define TAIKO_A_TX_1_2_ADC_CH2 (0x156) +#define TAIKO_A_TX_1_2_ADC_CH2__POR (0x44) +#define TAIKO_A_TX_1_2_ATEST_REFCTRL (0x157) +#define TAIKO_A_TX_1_2_ATEST_REFCTRL__POR (0x00) +#define TAIKO_A_TX_1_2_TEST_CTL (0x158) +#define TAIKO_A_TX_1_2_TEST_CTL__POR (0x38) +#define TAIKO_A_TX_1_2_TEST_BLOCK_EN (0x159) +#define TAIKO_A_TX_1_2_TEST_BLOCK_EN__POR (0xFC) +#define TAIKO_A_TX_1_2_TXFE_CLKDIV (0x15A) +#define TAIKO_A_TX_1_2_TXFE_CLKDIV__POR (0x55) +#define TAIKO_A_TX_1_2_SAR_ERR_CH1 (0x15B) +#define TAIKO_A_TX_1_2_SAR_ERR_CH1__POR (0x00) +#define TAIKO_A_TX_1_2_SAR_ERR_CH2 (0x15C) +#define TAIKO_A_TX_1_2_SAR_ERR_CH2__POR (0x00) +#define TAIKO_A_TX_3_4_EN (0x15D) +#define TAIKO_A_TX_3_4_EN__POR (0x00) +#define TAIKO_A_TX_3_4_TEST_EN (0x15E) +#define TAIKO_A_TX_3_4_TEST_EN__POR (0xCC) +#define TAIKO_A_TX_3_4_ADC_CH3 (0x15F) +#define TAIKO_A_TX_3_4_ADC_CH3__POR (0x44) +#define TAIKO_A_TX_3_4_ADC_CH4 (0x160) +#define TAIKO_A_TX_3_4_ADC_CH4__POR (0x44) +#define TAIKO_A_TX_3_4_ATEST_REFCTRL (0x161) +#define TAIKO_A_TX_3_4_ATEST_REFCTRL__POR (0x00) +#define TAIKO_A_TX_3_4_TEST_CTL (0x162) +#define TAIKO_A_TX_3_4_TEST_CTL__POR (0x38) +#define TAIKO_A_TX_3_4_TEST_BLOCK_EN (0x163) +#define TAIKO_A_TX_3_4_TEST_BLOCK_EN__POR (0xFC) +#define TAIKO_A_TX_3_4_TXFE_CKDIV (0x164) +#define TAIKO_A_TX_3_4_TXFE_CKDIV__POR (0x55) +#define TAIKO_A_TX_3_4_SAR_ERR_CH3 (0x165) +#define TAIKO_A_TX_3_4_SAR_ERR_CH3__POR (0x00) +#define TAIKO_A_TX_3_4_SAR_ERR_CH4 (0x166) +#define TAIKO_A_TX_3_4_SAR_ERR_CH4__POR (0x00) +#define TAIKO_A_TX_5_6_EN (0x167) +#define TAIKO_A_TX_5_6_EN__POR (0x11) +#define TAIKO_A_TX_5_6_TEST_EN (0x168) +#define TAIKO_A_TX_5_6_TEST_EN__POR (0xCC) +#define TAIKO_A_TX_5_6_ADC_CH5 (0x169) +#define TAIKO_A_TX_5_6_ADC_CH5__POR (0x44) +#define TAIKO_A_TX_5_6_ADC_CH6 (0x16A) +#define TAIKO_A_TX_5_6_ADC_CH6__POR (0x44) +#define TAIKO_A_TX_5_6_ATEST_REFCTRL (0x16B) +#define TAIKO_A_TX_5_6_ATEST_REFCTRL__POR (0x00) +#define TAIKO_A_TX_5_6_TEST_CTL (0x16C) +#define TAIKO_A_TX_5_6_TEST_CTL__POR (0x38) +#define TAIKO_A_TX_5_6_TEST_BLOCK_EN (0x16D) +#define TAIKO_A_TX_5_6_TEST_BLOCK_EN__POR (0xFC) +#define TAIKO_A_TX_5_6_TXFE_CKDIV (0x16E) +#define TAIKO_A_TX_5_6_TXFE_CKDIV__POR (0x55) +#define TAIKO_A_TX_5_6_SAR_ERR_CH5 (0x16F) +#define TAIKO_A_TX_5_6_SAR_ERR_CH5__POR (0x00) +#define TAIKO_A_TX_5_6_SAR_ERR_CH6 (0x170) +#define TAIKO_A_TX_5_6_SAR_ERR_CH6__POR (0x00) +#define TAIKO_A_TX_7_MBHC_EN (0x171) +#define TAIKO_A_TX_7_MBHC_EN__POR (0x0C) +#define TAIKO_A_TX_7_MBHC_ATEST_REFCTRL (0x172) +#define TAIKO_A_TX_7_MBHC_ATEST_REFCTRL__POR (0x00) +#define TAIKO_A_TX_7_MBHC_ADC (0x173) +#define TAIKO_A_TX_7_MBHC_ADC__POR (0x44) +#define TAIKO_A_TX_7_MBHC_TEST_CTL (0x174) +#define TAIKO_A_TX_7_MBHC_TEST_CTL__POR (0x38) +#define TAIKO_A_TX_7_MBHC_SAR_ERR (0x175) +#define TAIKO_A_TX_7_MBHC_SAR_ERR__POR (0x00) +#define TAIKO_A_TX_7_TXFE_CLKDIV (0x176) +#define TAIKO_A_TX_7_TXFE_CLKDIV__POR (0x0B) +#define TAIKO_A_BUCK_MODE_1 (0x181) +#define TAIKO_A_BUCK_MODE_1__POR (0x21) +#define TAIKO_A_BUCK_MODE_2 (0x182) +#define TAIKO_A_BUCK_MODE_2__POR (0xFF) +#define TAIKO_A_BUCK_MODE_3 (0x183) +#define TAIKO_A_BUCK_MODE_3__POR (0xCC) +#define TAIKO_A_BUCK_MODE_4 (0x184) +#define TAIKO_A_BUCK_MODE_4__POR (0x3A) +#define TAIKO_A_BUCK_MODE_5 (0x185) +#define TAIKO_A_BUCK_MODE_5__POR (0x00) +#define TAIKO_A_BUCK_CTRL_VCL_1 (0x186) +#define TAIKO_A_BUCK_CTRL_VCL_1__POR (0x48) +#define TAIKO_A_BUCK_CTRL_VCL_2 (0x187) +#define TAIKO_A_BUCK_CTRL_VCL_2__POR (0xA3) +#define TAIKO_A_BUCK_CTRL_VCL_3 (0x188) +#define TAIKO_A_BUCK_CTRL_VCL_3__POR (0x82) +#define TAIKO_A_BUCK_CTRL_CCL_1 (0x189) +#define TAIKO_A_BUCK_CTRL_CCL_1__POR (0xAB) +#define TAIKO_A_BUCK_CTRL_CCL_2 (0x18A) +#define TAIKO_A_BUCK_CTRL_CCL_2__POR (0xDC) +#define TAIKO_A_BUCK_CTRL_CCL_3 (0x18B) +#define TAIKO_A_BUCK_CTRL_CCL_3__POR (0x6A) +#define TAIKO_A_BUCK_CTRL_CCL_4 (0x18C) +#define TAIKO_A_BUCK_CTRL_CCL_4__POR (0x58) +#define TAIKO_A_BUCK_CTRL_PWM_DRVR_1 (0x18D) +#define TAIKO_A_BUCK_CTRL_PWM_DRVR_1__POR (0x50) +#define TAIKO_A_BUCK_CTRL_PWM_DRVR_2 (0x18E) +#define TAIKO_A_BUCK_CTRL_PWM_DRVR_2__POR (0x64) +#define TAIKO_A_BUCK_CTRL_PWM_DRVR_3 (0x18F) +#define TAIKO_A_BUCK_CTRL_PWM_DRVR_3__POR (0x77) +#define TAIKO_A_BUCK_TMUX_A_D (0x190) +#define TAIKO_A_BUCK_TMUX_A_D__POR (0x00) +#define TAIKO_A_NCP_BUCKREF (0x191) +#define TAIKO_A_NCP_BUCKREF__POR (0x00) +#define TAIKO_A_NCP_EN (0x192) +#define TAIKO_A_NCP_EN__POR (0xFE) +#define TAIKO_A_NCP_CLK (0x193) +#define TAIKO_A_NCP_CLK__POR (0x94) +#define TAIKO_A_NCP_STATIC (0x194) +#define TAIKO_A_NCP_STATIC__POR (0x28) +#define TAIKO_A_NCP_VTH_LOW (0x195) +#define TAIKO_A_NCP_VTH_LOW__POR (0x88) +#define TAIKO_A_NCP_VTH_HIGH (0x196) +#define TAIKO_A_NCP_VTH_HIGH__POR (0xA0) +#define TAIKO_A_NCP_ATEST (0x197) +#define TAIKO_A_NCP_ATEST__POR (0x00) +#define TAIKO_A_NCP_DTEST (0x198) +#define TAIKO_A_NCP_DTEST__POR (0x00) +#define TAIKO_A_NCP_DLY1 (0x199) +#define TAIKO_A_NCP_DLY1__POR (0x06) +#define TAIKO_A_NCP_DLY2 (0x19A) +#define TAIKO_A_NCP_DLY2__POR (0x06) +#define TAIKO_A_RX_AUX_SW_CTL (0x19B) +#define TAIKO_A_RX_AUX_SW_CTL__POR (0x00) +#define TAIKO_A_RX_PA_AUX_IN_CONN (0x19C) +#define TAIKO_A_RX_PA_AUX_IN_CONN__POR (0x00) +#define TAIKO_A_RX_COM_TIMER_DIV (0x19E) +#define TAIKO_A_RX_COM_TIMER_DIV__POR (0xE8) +#define TAIKO_A_RX_COM_OCP_CTL (0x19F) +#define TAIKO_A_RX_COM_OCP_CTL__POR (0x1F) +#define TAIKO_A_RX_COM_OCP_COUNT (0x1A0) +#define TAIKO_A_RX_COM_OCP_COUNT__POR (0x77) +#define TAIKO_A_RX_COM_DAC_CTL (0x1A1) +#define TAIKO_A_RX_COM_DAC_CTL__POR (0x00) +#define TAIKO_A_RX_COM_BIAS (0x1A2) +#define TAIKO_A_RX_COM_BIAS__POR (0x00) +#define TAIKO_A_RX_HPH_AUTO_CHOP (0x1A4) +#define TAIKO_A_RX_HPH_AUTO_CHOP__POR (0x38) +#define TAIKO_A_RX_HPH_CHOP_CTL (0x1A5) +#define TAIKO_A_RX_HPH_CHOP_CTL__POR (0xB4) +#define TAIKO_A_RX_HPH_BIAS_PA (0x1A6) +#define TAIKO_A_RX_HPH_BIAS_PA__POR (0xAA) +#define TAIKO_A_RX_HPH_BIAS_LDO (0x1A7) +#define TAIKO_A_RX_HPH_BIAS_LDO__POR (0x87) +#define TAIKO_A_RX_HPH_BIAS_CNP (0x1A8) +#define TAIKO_A_RX_HPH_BIAS_CNP__POR (0x8A) +#define TAIKO_A_RX_HPH_BIAS_WG_OCP (0x1A9) +#define TAIKO_A_RX_HPH_BIAS_WG_OCP__POR (0x2A) +#define TAIKO_A_RX_HPH_OCP_CTL (0x1AA) +#define TAIKO_A_RX_HPH_OCP_CTL__POR (0x68) +#define TAIKO_A_RX_HPH_CNP_EN (0x1AB) +#define TAIKO_A_RX_HPH_CNP_EN__POR (0x80) +#define TAIKO_A_RX_HPH_CNP_WG_CTL (0x1AC) +#define TAIKO_A_RX_HPH_CNP_WG_CTL__POR (0xDE) +#define TAIKO_A_RX_HPH_CNP_WG_TIME (0x1AD) +#define TAIKO_A_RX_HPH_CNP_WG_TIME__POR (0x2A) +#define TAIKO_A_RX_HPH_L_GAIN (0x1AE) +#define TAIKO_A_RX_HPH_L_GAIN__POR (0x00) +#define TAIKO_A_RX_HPH_L_TEST (0x1AF) +#define TAIKO_A_RX_HPH_L_TEST__POR (0x00) +#define TAIKO_A_RX_HPH_L_PA_CTL (0x1B0) +#define TAIKO_A_RX_HPH_L_PA_CTL__POR (0x40) +#define TAIKO_A_RX_HPH_L_DAC_CTL (0x1B1) +#define TAIKO_A_RX_HPH_L_DAC_CTL__POR (0x00) +#define TAIKO_A_RX_HPH_L_ATEST (0x1B2) +#define TAIKO_A_RX_HPH_L_ATEST__POR (0x00) +#define TAIKO_A_RX_HPH_L_STATUS (0x1B3) +#define TAIKO_A_RX_HPH_L_STATUS__POR (0x00) +#define TAIKO_A_RX_HPH_R_GAIN (0x1B4) +#define TAIKO_A_RX_HPH_R_GAIN__POR (0x00) +#define TAIKO_A_RX_HPH_R_TEST (0x1B5) +#define TAIKO_A_RX_HPH_R_TEST__POR (0x00) +#define TAIKO_A_RX_HPH_R_PA_CTL (0x1B6) +#define TAIKO_A_RX_HPH_R_PA_CTL__POR (0x40) +#define TAIKO_A_RX_HPH_R_DAC_CTL (0x1B7) +#define TAIKO_A_RX_HPH_R_DAC_CTL__POR (0x00) +#define TAIKO_A_RX_HPH_R_ATEST (0x1B8) +#define TAIKO_A_RX_HPH_R_ATEST__POR (0x00) +#define TAIKO_A_RX_HPH_R_STATUS (0x1B9) +#define TAIKO_A_RX_HPH_R_STATUS__POR (0x00) +#define TAIKO_A_RX_EAR_BIAS_PA (0x1BA) +#define TAIKO_A_RX_EAR_BIAS_PA__POR (0xA6) +#define TAIKO_A_RX_EAR_BIAS_CMBUFF (0x1BB) +#define TAIKO_A_RX_EAR_BIAS_CMBUFF__POR (0xA0) +#define TAIKO_A_RX_EAR_EN (0x1BC) +#define TAIKO_A_RX_EAR_EN__POR (0x00) +#define TAIKO_A_RX_EAR_GAIN (0x1BD) +#define TAIKO_A_RX_EAR_GAIN__POR (0x02) +#define TAIKO_A_RX_EAR_CMBUFF (0x1BE) +#define TAIKO_A_RX_EAR_CMBUFF__POR (0x04) +#define TAIKO_A_RX_EAR_ICTL (0x1BF) +#define TAIKO_A_RX_EAR_ICTL__POR (0x40) +#define TAIKO_A_RX_EAR_CCOMP (0x1C0) +#define TAIKO_A_RX_EAR_CCOMP__POR (0x08) +#define TAIKO_A_RX_EAR_VCM (0x1C1) +#define TAIKO_A_RX_EAR_VCM__POR (0x03) +#define TAIKO_A_RX_EAR_CNP (0x1C2) +#define TAIKO_A_RX_EAR_CNP__POR (0xF2) +#define TAIKO_A_RX_EAR_DAC_CTL_ATEST (0x1C3) +#define TAIKO_A_RX_EAR_DAC_CTL_ATEST__POR (0x00) +#define TAIKO_A_RX_EAR_STATUS (0x1C5) +#define TAIKO_A_RX_EAR_STATUS__POR (0x04) +#define TAIKO_A_RX_LINE_BIAS_PA (0x1C6) +#define TAIKO_A_RX_LINE_BIAS_PA__POR (0xA8) +#define TAIKO_A_RX_BUCK_BIAS1 (0x1C7) +#define TAIKO_A_RX_BUCK_BIAS1__POR (0x42) +#define TAIKO_A_RX_BUCK_BIAS2 (0x1C8) +#define TAIKO_A_RX_BUCK_BIAS2__POR (0x84) +#define TAIKO_A_RX_LINE_COM (0x1C9) +#define TAIKO_A_RX_LINE_COM__POR (0x80) +#define TAIKO_A_RX_LINE_CNP_EN (0x1CA) +#define TAIKO_A_RX_LINE_CNP_EN__POR (0x00) +#define TAIKO_A_RX_LINE_CNP_WG_CTL (0x1CB) +#define TAIKO_A_RX_LINE_CNP_WG_CTL__POR (0x00) +#define TAIKO_A_RX_LINE_CNP_WG_TIME (0x1CC) +#define TAIKO_A_RX_LINE_CNP_WG_TIME__POR (0x04) +#define TAIKO_A_RX_LINE_1_GAIN (0x1CD) +#define TAIKO_A_RX_LINE_1_GAIN__POR (0x00) +#define TAIKO_A_RX_LINE_1_TEST (0x1CE) +#define TAIKO_A_RX_LINE_1_TEST__POR (0x00) +#define TAIKO_A_RX_LINE_1_DAC_CTL (0x1CF) +#define TAIKO_A_RX_LINE_1_DAC_CTL__POR (0x00) +#define TAIKO_A_RX_LINE_1_STATUS (0x1D0) +#define TAIKO_A_RX_LINE_1_STATUS__POR (0x00) +#define TAIKO_A_RX_LINE_2_GAIN (0x1D1) +#define TAIKO_A_RX_LINE_2_GAIN__POR (0x00) +#define TAIKO_A_RX_LINE_2_TEST (0x1D2) +#define TAIKO_A_RX_LINE_2_TEST__POR (0x00) +#define TAIKO_A_RX_LINE_2_DAC_CTL (0x1D3) +#define TAIKO_A_RX_LINE_2_DAC_CTL__POR (0x00) +#define TAIKO_A_RX_LINE_2_STATUS (0x1D4) +#define TAIKO_A_RX_LINE_2_STATUS__POR (0x00) +#define TAIKO_A_RX_LINE_3_GAIN (0x1D5) +#define TAIKO_A_RX_LINE_3_GAIN__POR (0x00) +#define TAIKO_A_RX_LINE_3_TEST (0x1D6) +#define TAIKO_A_RX_LINE_3_TEST__POR (0x00) +#define TAIKO_A_RX_LINE_3_DAC_CTL (0x1D7) +#define TAIKO_A_RX_LINE_3_DAC_CTL__POR (0x00) +#define TAIKO_A_RX_LINE_3_STATUS (0x1D8) +#define TAIKO_A_RX_LINE_3_STATUS__POR (0x00) +#define TAIKO_A_RX_LINE_4_GAIN (0x1D9) +#define TAIKO_A_RX_LINE_4_GAIN__POR (0x00) +#define TAIKO_A_RX_LINE_4_TEST (0x1DA) +#define TAIKO_A_RX_LINE_4_TEST__POR (0x00) +#define TAIKO_A_RX_LINE_4_DAC_CTL (0x1DB) +#define TAIKO_A_RX_LINE_4_DAC_CTL__POR (0x00) +#define TAIKO_A_RX_LINE_4_STATUS (0x1DC) +#define TAIKO_A_RX_LINE_4_STATUS__POR (0x00) +#define TAIKO_A_RX_LINE_CNP_DBG (0x1DD) +#define TAIKO_A_RX_LINE_CNP_DBG__POR (0x00) +#define TAIKO_A_SPKR_DRV_EN (0x1DF) +#define TAIKO_A_SPKR_DRV_EN__POR (0x6F) +#define TAIKO_A_SPKR_DRV_GAIN (0x1E0) +#define TAIKO_A_SPKR_DRV_GAIN__POR (0x00) +#define TAIKO_A_SPKR_DRV_DAC_CTL (0x1E1) +#define TAIKO_A_SPKR_DRV_DAC_CTL__POR (0x04) +#define TAIKO_A_SPKR_DRV_OCP_CTL (0x1E2) +#define TAIKO_A_SPKR_DRV_OCP_CTL__POR (0x98) +#define TAIKO_A_SPKR_DRV_CLIP_DET (0x1E3) +#define TAIKO_A_SPKR_DRV_CLIP_DET__POR (0x48) +#define TAIKO_A_SPKR_DRV_IEC (0x1E4) +#define TAIKO_A_SPKR_DRV_IEC__POR (0x20) +#define TAIKO_A_SPKR_DRV_DBG_DAC (0x1E5) +#define TAIKO_A_SPKR_DRV_DBG_DAC__POR (0x05) +#define TAIKO_A_SPKR_DRV_DBG_PA (0x1E6) +#define TAIKO_A_SPKR_DRV_DBG_PA__POR (0x18) +#define TAIKO_A_SPKR_DRV_DBG_PWRSTG (0x1E7) +#define TAIKO_A_SPKR_DRV_DBG_PWRSTG__POR (0x00) +#define TAIKO_A_SPKR_DRV_BIAS_LDO (0x1E8) +#define TAIKO_A_SPKR_DRV_BIAS_LDO__POR (0x45) +#define TAIKO_A_SPKR_DRV_BIAS_INT (0x1E9) +#define TAIKO_A_SPKR_DRV_BIAS_INT__POR (0xA5) +#define TAIKO_A_SPKR_DRV_BIAS_PA (0x1EA) +#define TAIKO_A_SPKR_DRV_BIAS_PA__POR (0x55) +#define TAIKO_A_SPKR_DRV_STATUS_OCP (0x1EB) +#define TAIKO_A_SPKR_DRV_STATUS_OCP__POR (0x00) +#define TAIKO_A_SPKR_DRV_STATUS_PA (0x1EC) +#define TAIKO_A_SPKR_DRV_STATUS_PA__POR (0x00) +#define TAIKO_A_SPKR_PROT_EN (0x1ED) +#define TAIKO_A_SPKR_PROT_EN__POR (0x00) +#define TAIKO_A_SPKR_PROT_ADC_EN (0x1EE) +#define TAIKO_A_SPKR_PROT_ADC_EN__POR (0x44) +#define TAIKO_A_SPKR_PROT_ISENSE_BIAS (0x1EF) +#define TAIKO_A_SPKR_PROT_ISENSE_BIAS__POR (0x44) +#define TAIKO_A_SPKR_PROT_VSENSE_BIAS (0x1F0) +#define TAIKO_A_SPKR_PROT_VSENSE_BIAS__POR (0x44) +#define TAIKO_A_SPKR_PROT_ADC_ATEST_REFCTRL (0x1F1) +#define TAIKO_A_SPKR_PROT_ADC_ATEST_REFCTRL__POR (0x00) +#define TAIKO_A_SPKR_PROT_ADC_TEST_CTL (0x1F2) +#define TAIKO_A_SPKR_PROT_ADC_TEST_CTL__POR (0x38) +#define TAIKO_A_SPKR_PROT_TEST_BLOCK_EN (0x1F3) +#define TAIKO_A_SPKR_PROT_TEST_BLOCK_EN__POR (0xFC) +#define TAIKO_A_SPKR_PROT_ATEST (0x1F4) +#define TAIKO_A_SPKR_PROT_ATEST__POR (0x00) +#define TAIKO_A_SPKR_PROT_V_SAR_ERR (0x1F5) +#define TAIKO_A_SPKR_PROT_V_SAR_ERR__POR (0x00) +#define TAIKO_A_SPKR_PROT_I_SAR_ERR (0x1F6) +#define TAIKO_A_SPKR_PROT_I_SAR_ERR__POR (0x00) +#define TAIKO_A_SPKR_PROT_LDO_CTRL (0x1F7) +#define TAIKO_A_SPKR_PROT_LDO_CTRL__POR (0x00) +#define TAIKO_A_SPKR_PROT_ISENSE_CTRL (0x1F8) +#define TAIKO_A_SPKR_PROT_ISENSE_CTRL__POR (0x00) +#define TAIKO_A_SPKR_PROT_VSENSE_CTRL (0x1F9) +#define TAIKO_A_SPKR_PROT_VSENSE_CTRL__POR (0x00) +#define TAIKO_A_RC_OSC_FREQ (0x1FA) +#define TAIKO_A_RC_OSC_FREQ__POR (0x46) +#define TAIKO_A_RC_OSC_TEST (0x1FB) +#define TAIKO_A_RC_OSC_TEST__POR (0x0A) +#define TAIKO_A_RC_OSC_STATUS (0x1FC) +#define TAIKO_A_RC_OSC_STATUS__POR (0x18) +#define TAIKO_A_RC_OSC_TUNER (0x1FD) +#define TAIKO_A_RC_OSC_TUNER__POR (0x00) +#define TAIKO_A_MBHC_HPH (0x1FE) +#define TAIKO_A_MBHC_HPH__POR (0x44) +#define TAIKO_A_CDC_ANC1_B1_CTL (0x200) +#define TAIKO_A_CDC_ANC1_B1_CTL__POR (0x00) +#define TAIKO_A_CDC_ANC2_B1_CTL (0x280) +#define TAIKO_A_CDC_ANC2_B1_CTL__POR (0x00) +#define TAIKO_A_CDC_ANC1_SHIFT (0x201) +#define TAIKO_A_CDC_ANC1_SHIFT__POR (0x00) +#define TAIKO_A_CDC_ANC2_SHIFT (0x281) +#define TAIKO_A_CDC_ANC2_SHIFT__POR (0x00) +#define TAIKO_A_CDC_ANC1_IIR_B1_CTL (0x202) +#define TAIKO_A_CDC_ANC1_IIR_B1_CTL__POR (0x00) +#define TAIKO_A_CDC_ANC2_IIR_B1_CTL (0x282) +#define TAIKO_A_CDC_ANC2_IIR_B1_CTL__POR (0x00) +#define TAIKO_A_CDC_ANC1_IIR_B2_CTL (0x203) +#define TAIKO_A_CDC_ANC1_IIR_B2_CTL__POR (0x00) +#define TAIKO_A_CDC_ANC2_IIR_B2_CTL (0x283) +#define TAIKO_A_CDC_ANC2_IIR_B2_CTL__POR (0x00) +#define TAIKO_A_CDC_ANC1_IIR_B3_CTL (0x204) +#define TAIKO_A_CDC_ANC1_IIR_B3_CTL__POR (0x00) +#define TAIKO_A_CDC_ANC2_IIR_B3_CTL (0x284) +#define TAIKO_A_CDC_ANC2_IIR_B3_CTL__POR (0x00) +#define TAIKO_A_CDC_ANC1_LPF_B1_CTL (0x206) +#define TAIKO_A_CDC_ANC1_LPF_B1_CTL__POR (0x00) +#define TAIKO_A_CDC_ANC2_LPF_B1_CTL (0x286) +#define TAIKO_A_CDC_ANC2_LPF_B1_CTL__POR (0x00) +#define TAIKO_A_CDC_ANC1_LPF_B2_CTL (0x207) +#define TAIKO_A_CDC_ANC1_LPF_B2_CTL__POR (0x00) +#define TAIKO_A_CDC_ANC2_LPF_B2_CTL (0x287) +#define TAIKO_A_CDC_ANC2_LPF_B2_CTL__POR (0x00) +#define TAIKO_A_CDC_ANC1_SPARE (0x209) +#define TAIKO_A_CDC_ANC1_SPARE__POR (0x00) +#define TAIKO_A_CDC_ANC2_SPARE (0x289) +#define TAIKO_A_CDC_ANC2_SPARE__POR (0x00) +#define TAIKO_A_CDC_ANC1_SMLPF_CTL (0x20A) +#define TAIKO_A_CDC_ANC1_SMLPF_CTL__POR (0x00) +#define TAIKO_A_CDC_ANC2_SMLPF_CTL (0x28A) +#define TAIKO_A_CDC_ANC2_SMLPF_CTL__POR (0x00) +#define TAIKO_A_CDC_ANC1_DCFLT_CTL (0x20B) +#define TAIKO_A_CDC_ANC1_DCFLT_CTL__POR (0x00) +#define TAIKO_A_CDC_ANC2_DCFLT_CTL (0x28B) +#define TAIKO_A_CDC_ANC2_DCFLT_CTL__POR (0x00) +#define TAIKO_A_CDC_ANC1_GAIN_CTL (0x20C) +#define TAIKO_A_CDC_ANC1_GAIN_CTL__POR (0x00) +#define TAIKO_A_CDC_ANC2_GAIN_CTL (0x28C) +#define TAIKO_A_CDC_ANC2_GAIN_CTL__POR (0x00) +#define TAIKO_A_CDC_ANC1_B2_CTL (0x20D) +#define TAIKO_A_CDC_ANC1_B2_CTL__POR (0x00) +#define TAIKO_A_CDC_ANC2_B2_CTL (0x28D) +#define TAIKO_A_CDC_ANC2_B2_CTL__POR (0x00) +#define TAIKO_A_CDC_TX1_VOL_CTL_TIMER (0x220) +#define TAIKO_A_CDC_TX1_VOL_CTL_TIMER__POR (0x00) +#define TAIKO_A_CDC_TX2_VOL_CTL_TIMER (0x228) +#define TAIKO_A_CDC_TX2_VOL_CTL_TIMER__POR (0x00) +#define TAIKO_A_CDC_TX3_VOL_CTL_TIMER (0x230) +#define TAIKO_A_CDC_TX3_VOL_CTL_TIMER__POR (0x00) +#define TAIKO_A_CDC_TX4_VOL_CTL_TIMER (0x238) +#define TAIKO_A_CDC_TX4_VOL_CTL_TIMER__POR (0x00) +#define TAIKO_A_CDC_TX5_VOL_CTL_TIMER (0x240) +#define TAIKO_A_CDC_TX5_VOL_CTL_TIMER__POR (0x00) +#define TAIKO_A_CDC_TX6_VOL_CTL_TIMER (0x248) +#define TAIKO_A_CDC_TX6_VOL_CTL_TIMER__POR (0x00) +#define TAIKO_A_CDC_TX7_VOL_CTL_TIMER (0x250) +#define TAIKO_A_CDC_TX7_VOL_CTL_TIMER__POR (0x00) +#define TAIKO_A_CDC_TX8_VOL_CTL_TIMER (0x258) +#define TAIKO_A_CDC_TX8_VOL_CTL_TIMER__POR (0x00) +#define TAIKO_A_CDC_TX9_VOL_CTL_TIMER (0x260) +#define TAIKO_A_CDC_TX9_VOL_CTL_TIMER__POR (0x00) +#define TAIKO_A_CDC_TX10_VOL_CTL_TIMER (0x268) +#define TAIKO_A_CDC_TX10_VOL_CTL_TIMER__POR (0x00) +#define TAIKO_A_CDC_TX1_VOL_CTL_GAIN (0x221) +#define TAIKO_A_CDC_TX1_VOL_CTL_GAIN__POR (0x00) +#define TAIKO_A_CDC_TX2_VOL_CTL_GAIN (0x229) +#define TAIKO_A_CDC_TX2_VOL_CTL_GAIN__POR (0x00) +#define TAIKO_A_CDC_TX3_VOL_CTL_GAIN (0x231) +#define TAIKO_A_CDC_TX3_VOL_CTL_GAIN__POR (0x00) +#define TAIKO_A_CDC_TX4_VOL_CTL_GAIN (0x239) +#define TAIKO_A_CDC_TX4_VOL_CTL_GAIN__POR (0x00) +#define TAIKO_A_CDC_TX5_VOL_CTL_GAIN (0x241) +#define TAIKO_A_CDC_TX5_VOL_CTL_GAIN__POR (0x00) +#define TAIKO_A_CDC_TX6_VOL_CTL_GAIN (0x249) +#define TAIKO_A_CDC_TX6_VOL_CTL_GAIN__POR (0x00) +#define TAIKO_A_CDC_TX7_VOL_CTL_GAIN (0x251) +#define TAIKO_A_CDC_TX7_VOL_CTL_GAIN__POR (0x00) +#define TAIKO_A_CDC_TX8_VOL_CTL_GAIN (0x259) +#define TAIKO_A_CDC_TX8_VOL_CTL_GAIN__POR (0x00) +#define TAIKO_A_CDC_TX9_VOL_CTL_GAIN (0x261) +#define TAIKO_A_CDC_TX9_VOL_CTL_GAIN__POR (0x00) +#define TAIKO_A_CDC_TX10_VOL_CTL_GAIN (0x269) +#define TAIKO_A_CDC_TX10_VOL_CTL_GAIN__POR (0x00) +#define TAIKO_A_CDC_TX1_VOL_CTL_CFG (0x222) +#define TAIKO_A_CDC_TX1_VOL_CTL_CFG__POR (0x00) +#define TAIKO_A_CDC_TX2_VOL_CTL_CFG (0x22A) +#define TAIKO_A_CDC_TX2_VOL_CTL_CFG__POR (0x00) +#define TAIKO_A_CDC_TX3_VOL_CTL_CFG (0x232) +#define TAIKO_A_CDC_TX3_VOL_CTL_CFG__POR (0x00) +#define TAIKO_A_CDC_TX4_VOL_CTL_CFG (0x23A) +#define TAIKO_A_CDC_TX4_VOL_CTL_CFG__POR (0x00) +#define TAIKO_A_CDC_TX5_VOL_CTL_CFG (0x242) +#define TAIKO_A_CDC_TX5_VOL_CTL_CFG__POR (0x00) +#define TAIKO_A_CDC_TX6_VOL_CTL_CFG (0x24A) +#define TAIKO_A_CDC_TX6_VOL_CTL_CFG__POR (0x00) +#define TAIKO_A_CDC_TX7_VOL_CTL_CFG (0x252) +#define TAIKO_A_CDC_TX7_VOL_CTL_CFG__POR (0x00) +#define TAIKO_A_CDC_TX8_VOL_CTL_CFG (0x25A) +#define TAIKO_A_CDC_TX8_VOL_CTL_CFG__POR (0x00) +#define TAIKO_A_CDC_TX9_VOL_CTL_CFG (0x262) +#define TAIKO_A_CDC_TX9_VOL_CTL_CFG__POR (0x00) +#define TAIKO_A_CDC_TX10_VOL_CTL_CFG (0x26A) +#define TAIKO_A_CDC_TX10_VOL_CTL_CFG__POR (0x00) +#define TAIKO_A_CDC_TX1_MUX_CTL (0x223) +#define TAIKO_A_CDC_TX1_MUX_CTL__POR (0x08) +#define TAIKO_A_CDC_TX2_MUX_CTL (0x22B) +#define TAIKO_A_CDC_TX2_MUX_CTL__POR (0x08) +#define TAIKO_A_CDC_TX3_MUX_CTL (0x233) +#define TAIKO_A_CDC_TX3_MUX_CTL__POR (0x08) +#define TAIKO_A_CDC_TX4_MUX_CTL (0x23B) +#define TAIKO_A_CDC_TX4_MUX_CTL__POR (0x08) +#define TAIKO_A_CDC_TX5_MUX_CTL (0x243) +#define TAIKO_A_CDC_TX5_MUX_CTL__POR (0x08) +#define TAIKO_A_CDC_TX6_MUX_CTL (0x24B) +#define TAIKO_A_CDC_TX6_MUX_CTL__POR (0x08) +#define TAIKO_A_CDC_TX7_MUX_CTL (0x253) +#define TAIKO_A_CDC_TX7_MUX_CTL__POR (0x08) +#define TAIKO_A_CDC_TX8_MUX_CTL (0x25B) +#define TAIKO_A_CDC_TX8_MUX_CTL__POR (0x08) +#define TAIKO_A_CDC_TX9_MUX_CTL (0x263) +#define TAIKO_A_CDC_TX9_MUX_CTL__POR (0x08) +#define TAIKO_A_CDC_TX10_MUX_CTL (0x26B) +#define TAIKO_A_CDC_TX10_MUX_CTL__POR (0x08) +#define TAIKO_A_CDC_TX1_CLK_FS_CTL (0x224) +#define TAIKO_A_CDC_TX1_CLK_FS_CTL__POR (0x03) +#define TAIKO_A_CDC_TX2_CLK_FS_CTL (0x22C) +#define TAIKO_A_CDC_TX2_CLK_FS_CTL__POR (0x03) +#define TAIKO_A_CDC_TX3_CLK_FS_CTL (0x234) +#define TAIKO_A_CDC_TX3_CLK_FS_CTL__POR (0x03) +#define TAIKO_A_CDC_TX4_CLK_FS_CTL (0x23C) +#define TAIKO_A_CDC_TX4_CLK_FS_CTL__POR (0x03) +#define TAIKO_A_CDC_TX5_CLK_FS_CTL (0x244) +#define TAIKO_A_CDC_TX5_CLK_FS_CTL__POR (0x03) +#define TAIKO_A_CDC_TX6_CLK_FS_CTL (0x24C) +#define TAIKO_A_CDC_TX6_CLK_FS_CTL__POR (0x03) +#define TAIKO_A_CDC_TX7_CLK_FS_CTL (0x254) +#define TAIKO_A_CDC_TX7_CLK_FS_CTL__POR (0x03) +#define TAIKO_A_CDC_TX8_CLK_FS_CTL (0x25C) +#define TAIKO_A_CDC_TX8_CLK_FS_CTL__POR (0x03) +#define TAIKO_A_CDC_TX9_CLK_FS_CTL (0x264) +#define TAIKO_A_CDC_TX9_CLK_FS_CTL__POR (0x03) +#define TAIKO_A_CDC_TX10_CLK_FS_CTL (0x26C) +#define TAIKO_A_CDC_TX10_CLK_FS_CTL__POR (0x03) +#define TAIKO_A_CDC_TX1_DMIC_CTL (0x225) +#define TAIKO_A_CDC_TX1_DMIC_CTL__POR (0x00) +#define TAIKO_A_CDC_TX2_DMIC_CTL (0x22D) +#define TAIKO_A_CDC_TX2_DMIC_CTL__POR (0x00) +#define TAIKO_A_CDC_TX3_DMIC_CTL (0x235) +#define TAIKO_A_CDC_TX3_DMIC_CTL__POR (0x00) +#define TAIKO_A_CDC_TX4_DMIC_CTL (0x23D) +#define TAIKO_A_CDC_TX4_DMIC_CTL__POR (0x00) +#define TAIKO_A_CDC_TX5_DMIC_CTL (0x245) +#define TAIKO_A_CDC_TX5_DMIC_CTL__POR (0x00) +#define TAIKO_A_CDC_TX6_DMIC_CTL (0x24D) +#define TAIKO_A_CDC_TX6_DMIC_CTL__POR (0x00) +#define TAIKO_A_CDC_TX7_DMIC_CTL (0x255) +#define TAIKO_A_CDC_TX7_DMIC_CTL__POR (0x00) +#define TAIKO_A_CDC_TX8_DMIC_CTL (0x25D) +#define TAIKO_A_CDC_TX8_DMIC_CTL__POR (0x00) +#define TAIKO_A_CDC_TX9_DMIC_CTL (0x265) +#define TAIKO_A_CDC_TX9_DMIC_CTL__POR (0x00) +#define TAIKO_A_CDC_TX10_DMIC_CTL (0x26D) +#define TAIKO_A_CDC_TX10_DMIC_CTL__POR (0x00) +#define TAIKO_A_CDC_DEBUG_B1_CTL (0x278) +#define TAIKO_A_CDC_DEBUG_B1_CTL__POR (0x00) +#define TAIKO_A_CDC_DEBUG_B2_CTL (0x279) +#define TAIKO_A_CDC_DEBUG_B2_CTL__POR (0x00) +#define TAIKO_A_CDC_DEBUG_B3_CTL (0x27A) +#define TAIKO_A_CDC_DEBUG_B3_CTL__POR (0x00) +#define TAIKO_A_CDC_DEBUG_B4_CTL (0x27B) +#define TAIKO_A_CDC_DEBUG_B4_CTL__POR (0x00) +#define TAIKO_A_CDC_DEBUG_B5_CTL (0x27C) +#define TAIKO_A_CDC_DEBUG_B5_CTL__POR (0x00) +#define TAIKO_A_CDC_DEBUG_B6_CTL (0x27D) +#define TAIKO_A_CDC_DEBUG_B6_CTL__POR (0x00) +#define TAIKO_A_CDC_DEBUG_B7_CTL (0x27E) +#define TAIKO_A_CDC_DEBUG_B7_CTL__POR (0x00) +#define TAIKO_A_CDC_SRC1_PDA_CFG (0x2A0) +#define TAIKO_A_CDC_SRC1_PDA_CFG__POR (0x00) +#define TAIKO_A_CDC_SRC2_PDA_CFG (0x2A8) +#define TAIKO_A_CDC_SRC2_PDA_CFG__POR (0x00) +#define TAIKO_A_CDC_SRC1_FS_CTL (0x2A1) +#define TAIKO_A_CDC_SRC1_FS_CTL__POR (0x1B) +#define TAIKO_A_CDC_SRC2_FS_CTL (0x2A9) +#define TAIKO_A_CDC_SRC2_FS_CTL__POR (0x1B) +#define TAIKO_A_CDC_RX1_B1_CTL (0x2B0) +#define TAIKO_A_CDC_RX1_B1_CTL__POR (0x00) +#define TAIKO_A_CDC_RX2_B1_CTL (0x2B8) +#define TAIKO_A_CDC_RX2_B1_CTL__POR (0x00) +#define TAIKO_A_CDC_RX3_B1_CTL (0x2C0) +#define TAIKO_A_CDC_RX3_B1_CTL__POR (0x00) +#define TAIKO_A_CDC_RX4_B1_CTL (0x2C8) +#define TAIKO_A_CDC_RX4_B1_CTL__POR (0x00) +#define TAIKO_A_CDC_RX5_B1_CTL (0x2D0) +#define TAIKO_A_CDC_RX5_B1_CTL__POR (0x00) +#define TAIKO_A_CDC_RX6_B1_CTL (0x2D8) +#define TAIKO_A_CDC_RX6_B1_CTL__POR (0x00) +#define TAIKO_A_CDC_RX7_B1_CTL (0x2E0) +#define TAIKO_A_CDC_RX7_B1_CTL__POR (0x00) +#define TAIKO_A_CDC_RX1_B2_CTL (0x2B1) +#define TAIKO_A_CDC_RX1_B2_CTL__POR (0x00) +#define TAIKO_A_CDC_RX2_B2_CTL (0x2B9) +#define TAIKO_A_CDC_RX2_B2_CTL__POR (0x00) +#define TAIKO_A_CDC_RX3_B2_CTL (0x2C1) +#define TAIKO_A_CDC_RX3_B2_CTL__POR (0x00) +#define TAIKO_A_CDC_RX4_B2_CTL (0x2C9) +#define TAIKO_A_CDC_RX4_B2_CTL__POR (0x00) +#define TAIKO_A_CDC_RX5_B2_CTL (0x2D1) +#define TAIKO_A_CDC_RX5_B2_CTL__POR (0x00) +#define TAIKO_A_CDC_RX6_B2_CTL (0x2D9) +#define TAIKO_A_CDC_RX6_B2_CTL__POR (0x00) +#define TAIKO_A_CDC_RX7_B2_CTL (0x2E1) +#define TAIKO_A_CDC_RX7_B2_CTL__POR (0x00) +#define TAIKO_A_CDC_RX1_B3_CTL (0x2B2) +#define TAIKO_A_CDC_RX1_B3_CTL__POR (0x00) +#define TAIKO_A_CDC_RX2_B3_CTL (0x2BA) +#define TAIKO_A_CDC_RX2_B3_CTL__POR (0x00) +#define TAIKO_A_CDC_RX3_B3_CTL (0x2C2) +#define TAIKO_A_CDC_RX3_B3_CTL__POR (0x00) +#define TAIKO_A_CDC_RX4_B3_CTL (0x2CA) +#define TAIKO_A_CDC_RX4_B3_CTL__POR (0x00) +#define TAIKO_A_CDC_RX5_B3_CTL (0x2D2) +#define TAIKO_A_CDC_RX5_B3_CTL__POR (0x00) +#define TAIKO_A_CDC_RX6_B3_CTL (0x2DA) +#define TAIKO_A_CDC_RX6_B3_CTL__POR (0x00) +#define TAIKO_A_CDC_RX7_B3_CTL (0x2E2) +#define TAIKO_A_CDC_RX7_B3_CTL__POR (0x00) +#define TAIKO_A_CDC_RX1_B4_CTL (0x2B3) +#define TAIKO_A_CDC_RX1_B4_CTL__POR (0x00) +#define TAIKO_A_CDC_RX2_B4_CTL (0x2BB) +#define TAIKO_A_CDC_RX2_B4_CTL__POR (0x00) +#define TAIKO_A_CDC_RX3_B4_CTL (0x2C3) +#define TAIKO_A_CDC_RX3_B4_CTL__POR (0x00) +#define TAIKO_A_CDC_RX4_B4_CTL (0x2CB) +#define TAIKO_A_CDC_RX4_B4_CTL__POR (0x00) +#define TAIKO_A_CDC_RX5_B4_CTL (0x2D3) +#define TAIKO_A_CDC_RX5_B4_CTL__POR (0x00) +#define TAIKO_A_CDC_RX6_B4_CTL (0x2DB) +#define TAIKO_A_CDC_RX6_B4_CTL__POR (0x00) +#define TAIKO_A_CDC_RX7_B4_CTL (0x2E3) +#define TAIKO_A_CDC_RX7_B4_CTL__POR (0x00) +#define TAIKO_A_CDC_RX1_B5_CTL (0x2B4) +#define TAIKO_A_CDC_RX1_B5_CTL__POR (0x78) +#define TAIKO_A_CDC_RX2_B5_CTL (0x2BC) +#define TAIKO_A_CDC_RX2_B5_CTL__POR (0x78) +#define TAIKO_A_CDC_RX3_B5_CTL (0x2C4) +#define TAIKO_A_CDC_RX3_B5_CTL__POR (0x78) +#define TAIKO_A_CDC_RX4_B5_CTL (0x2CC) +#define TAIKO_A_CDC_RX4_B5_CTL__POR (0x78) +#define TAIKO_A_CDC_RX5_B5_CTL (0x2D4) +#define TAIKO_A_CDC_RX5_B5_CTL__POR (0x78) +#define TAIKO_A_CDC_RX6_B5_CTL (0x2DC) +#define TAIKO_A_CDC_RX6_B5_CTL__POR (0x78) +#define TAIKO_A_CDC_RX7_B5_CTL (0x2E4) +#define TAIKO_A_CDC_RX7_B5_CTL__POR (0x78) +#define TAIKO_A_CDC_RX1_B6_CTL (0x2B5) +#define TAIKO_A_CDC_RX1_B6_CTL__POR (0x80) +#define TAIKO_A_CDC_RX2_B6_CTL (0x2BD) +#define TAIKO_A_CDC_RX2_B6_CTL__POR (0x80) +#define TAIKO_A_CDC_RX3_B6_CTL (0x2C5) +#define TAIKO_A_CDC_RX3_B6_CTL__POR (0x80) +#define TAIKO_A_CDC_RX4_B6_CTL (0x2CD) +#define TAIKO_A_CDC_RX4_B6_CTL__POR (0x80) +#define TAIKO_A_CDC_RX5_B6_CTL (0x2D5) +#define TAIKO_A_CDC_RX5_B6_CTL__POR (0x80) +#define TAIKO_A_CDC_RX6_B6_CTL (0x2DD) +#define TAIKO_A_CDC_RX6_B6_CTL__POR (0x80) +#define TAIKO_A_CDC_RX7_B6_CTL (0x2E5) +#define TAIKO_A_CDC_RX7_B6_CTL__POR (0x80) +#define TAIKO_A_CDC_RX1_VOL_CTL_B1_CTL (0x2B6) +#define TAIKO_A_CDC_RX1_VOL_CTL_B1_CTL__POR (0x00) +#define TAIKO_A_CDC_RX2_VOL_CTL_B1_CTL (0x2BE) +#define TAIKO_A_CDC_RX2_VOL_CTL_B1_CTL__POR (0x00) +#define TAIKO_A_CDC_RX3_VOL_CTL_B1_CTL (0x2C6) +#define TAIKO_A_CDC_RX3_VOL_CTL_B1_CTL__POR (0x00) +#define TAIKO_A_CDC_RX4_VOL_CTL_B1_CTL (0x2CE) +#define TAIKO_A_CDC_RX4_VOL_CTL_B1_CTL__POR (0x00) +#define TAIKO_A_CDC_RX5_VOL_CTL_B1_CTL (0x2D6) +#define TAIKO_A_CDC_RX5_VOL_CTL_B1_CTL__POR (0x00) +#define TAIKO_A_CDC_RX6_VOL_CTL_B1_CTL (0x2DE) +#define TAIKO_A_CDC_RX6_VOL_CTL_B1_CTL__POR (0x00) +#define TAIKO_A_CDC_RX7_VOL_CTL_B1_CTL (0x2E6) +#define TAIKO_A_CDC_RX7_VOL_CTL_B1_CTL__POR (0x00) +#define TAIKO_A_CDC_RX1_VOL_CTL_B2_CTL (0x2B7) +#define TAIKO_A_CDC_RX1_VOL_CTL_B2_CTL__POR (0x00) +#define TAIKO_A_CDC_RX2_VOL_CTL_B2_CTL (0x2BF) +#define TAIKO_A_CDC_RX2_VOL_CTL_B2_CTL__POR (0x00) +#define TAIKO_A_CDC_RX3_VOL_CTL_B2_CTL (0x2C7) +#define TAIKO_A_CDC_RX3_VOL_CTL_B2_CTL__POR (0x00) +#define TAIKO_A_CDC_RX4_VOL_CTL_B2_CTL (0x2CF) +#define TAIKO_A_CDC_RX4_VOL_CTL_B2_CTL__POR (0x00) +#define TAIKO_A_CDC_RX5_VOL_CTL_B2_CTL (0x2D7) +#define TAIKO_A_CDC_RX5_VOL_CTL_B2_CTL__POR (0x00) +#define TAIKO_A_CDC_RX6_VOL_CTL_B2_CTL (0x2DF) +#define TAIKO_A_CDC_RX6_VOL_CTL_B2_CTL__POR (0x00) +#define TAIKO_A_CDC_RX7_VOL_CTL_B2_CTL (0x2E7) +#define TAIKO_A_CDC_RX7_VOL_CTL_B2_CTL__POR (0x00) +#define TAIKO_A_CDC_VBAT_CFG (0x2E8) +#define TAIKO_A_CDC_VBAT_CFG__POR (0x1A) +#define TAIKO_A_CDC_VBAT_ADC_CAL1 (0x2E9) +#define TAIKO_A_CDC_VBAT_ADC_CAL1__POR (0x00) +#define TAIKO_A_CDC_VBAT_ADC_CAL2 (0x2EA) +#define TAIKO_A_CDC_VBAT_ADC_CAL2__POR (0x00) +#define TAIKO_A_CDC_VBAT_ADC_CAL3 (0x2EB) +#define TAIKO_A_CDC_VBAT_ADC_CAL3__POR (0x04) +#define TAIKO_A_CDC_VBAT_PK_EST1 (0x2EC) +#define TAIKO_A_CDC_VBAT_PK_EST1__POR (0xE0) +#define TAIKO_A_CDC_VBAT_PK_EST2 (0x2ED) +#define TAIKO_A_CDC_VBAT_PK_EST2__POR (0x01) +#define TAIKO_A_CDC_VBAT_PK_EST3 (0x2EE) +#define TAIKO_A_CDC_VBAT_PK_EST3__POR (0x40) +#define TAIKO_A_CDC_VBAT_RF_PROC1 (0x2EF) +#define TAIKO_A_CDC_VBAT_RF_PROC1__POR (0x2A) +#define TAIKO_A_CDC_VBAT_RF_PROC2 (0x2F0) +#define TAIKO_A_CDC_VBAT_RF_PROC2__POR (0x86) +#define TAIKO_A_CDC_VBAT_TAC1 (0x2F1) +#define TAIKO_A_CDC_VBAT_TAC1__POR (0x70) +#define TAIKO_A_CDC_VBAT_TAC2 (0x2F2) +#define TAIKO_A_CDC_VBAT_TAC2__POR (0x18) +#define TAIKO_A_CDC_VBAT_TAC3 (0x2F3) +#define TAIKO_A_CDC_VBAT_TAC3__POR (0x18) +#define TAIKO_A_CDC_VBAT_TAC4 (0x2F4) +#define TAIKO_A_CDC_VBAT_TAC4__POR (0x03) +#define TAIKO_A_CDC_VBAT_GAIN_UPD1 (0x2F5) +#define TAIKO_A_CDC_VBAT_GAIN_UPD1__POR (0x01) +#define TAIKO_A_CDC_VBAT_GAIN_UPD2 (0x2F6) +#define TAIKO_A_CDC_VBAT_GAIN_UPD2__POR (0x00) +#define TAIKO_A_CDC_VBAT_GAIN_UPD3 (0x2F7) +#define TAIKO_A_CDC_VBAT_GAIN_UPD3__POR (0x64) +#define TAIKO_A_CDC_VBAT_GAIN_UPD4 (0x2F8) +#define TAIKO_A_CDC_VBAT_GAIN_UPD4__POR (0x01) +#define TAIKO_A_CDC_VBAT_DEBUG1 (0x2F9) +#define TAIKO_A_CDC_VBAT_DEBUG1__POR (0x00) +#define TAIKO_A_CDC_CLK_ANC_RESET_CTL (0x300) +#define TAIKO_A_CDC_CLK_ANC_RESET_CTL__POR (0x00) +#define TAIKO_A_CDC_CLK_RX_RESET_CTL (0x301) +#define TAIKO_A_CDC_CLK_RX_RESET_CTL__POR (0x00) +#define TAIKO_A_CDC_CLK_TX_RESET_B1_CTL (0x302) +#define TAIKO_A_CDC_CLK_TX_RESET_B1_CTL__POR (0x00) +#define TAIKO_A_CDC_CLK_TX_RESET_B2_CTL (0x303) +#define TAIKO_A_CDC_CLK_TX_RESET_B2_CTL__POR (0x00) +#define TAIKO_A_CDC_CLK_DMIC_B1_CTL (0x304) +#define TAIKO_A_CDC_CLK_DMIC_B1_CTL__POR (0x00) +#define TAIKO_A_CDC_CLK_DMIC_B2_CTL (0x305) +#define TAIKO_A_CDC_CLK_DMIC_B2_CTL__POR (0x00) +#define TAIKO_A_CDC_CLK_RX_I2S_CTL (0x306) +#define TAIKO_A_CDC_CLK_RX_I2S_CTL__POR (0x03) +#define TAIKO_A_CDC_CLK_TX_I2S_CTL (0x307) +#define TAIKO_A_CDC_CLK_TX_I2S_CTL__POR (0x03) +#define TAIKO_A_CDC_CLK_OTHR_RESET_B1_CTL (0x308) +#define TAIKO_A_CDC_CLK_OTHR_RESET_B1_CTL__POR (0x00) +#define TAIKO_A_CDC_CLK_OTHR_RESET_B2_CTL (0x309) +#define TAIKO_A_CDC_CLK_OTHR_RESET_B2_CTL__POR (0x00) +#define TAIKO_A_CDC_CLK_TX_CLK_EN_B1_CTL (0x30A) +#define TAIKO_A_CDC_CLK_TX_CLK_EN_B1_CTL__POR (0x00) +#define TAIKO_A_CDC_CLK_TX_CLK_EN_B2_CTL (0x30B) +#define TAIKO_A_CDC_CLK_TX_CLK_EN_B2_CTL__POR (0x00) +#define TAIKO_A_CDC_CLK_OTHR_CTL (0x30C) +#define TAIKO_A_CDC_CLK_OTHR_CTL__POR (0x00) +#define TAIKO_A_CDC_CLK_RDAC_CLK_EN_CTL (0x30D) +#define TAIKO_A_CDC_CLK_RDAC_CLK_EN_CTL__POR (0x00) +#define TAIKO_A_CDC_CLK_ANC_CLK_EN_CTL (0x30E) +#define TAIKO_A_CDC_CLK_ANC_CLK_EN_CTL__POR (0x00) +#define TAIKO_A_CDC_CLK_RX_B1_CTL (0x30F) +#define TAIKO_A_CDC_CLK_RX_B1_CTL__POR (0x00) +#define TAIKO_A_CDC_CLK_RX_B2_CTL (0x310) +#define TAIKO_A_CDC_CLK_RX_B2_CTL__POR (0x00) +#define TAIKO_A_CDC_CLK_MCLK_CTL (0x311) +#define TAIKO_A_CDC_CLK_MCLK_CTL__POR (0x00) +#define TAIKO_A_CDC_CLK_PDM_CTL (0x312) +#define TAIKO_A_CDC_CLK_PDM_CTL__POR (0x00) +#define TAIKO_A_CDC_CLK_SD_CTL (0x313) +#define TAIKO_A_CDC_CLK_SD_CTL__POR (0x00) +#define TAIKO_A_CDC_CLK_POWER_CTL (0x314) +#define TAIKO_A_CDC_CLK_POWER_CTL__POR (0x00) +#define TAIKO_A_CDC_CLSH_B1_CTL (0x320) +#define TAIKO_A_CDC_CLSH_B1_CTL__POR (0xE4) +#define TAIKO_A_CDC_CLSH_B2_CTL (0x321) +#define TAIKO_A_CDC_CLSH_B2_CTL__POR (0x00) +#define TAIKO_A_CDC_CLSH_B3_CTL (0x322) +#define TAIKO_A_CDC_CLSH_B3_CTL__POR (0x00) +#define TAIKO_A_CDC_CLSH_BUCK_NCP_VARS (0x323) +#define TAIKO_A_CDC_CLSH_BUCK_NCP_VARS__POR (0x00) +#define TAIKO_A_CDC_CLSH_IDLE_HPH_THSD (0x324) +#define TAIKO_A_CDC_CLSH_IDLE_HPH_THSD__POR (0x12) +#define TAIKO_A_CDC_CLSH_IDLE_EAR_THSD (0x325) +#define TAIKO_A_CDC_CLSH_IDLE_EAR_THSD__POR (0x0C) +#define TAIKO_A_CDC_CLSH_FCLKONLY_HPH_THSD (0x326) +#define TAIKO_A_CDC_CLSH_FCLKONLY_HPH_THSD__POR (0x18) +#define TAIKO_A_CDC_CLSH_FCLKONLY_EAR_THSD (0x327) +#define TAIKO_A_CDC_CLSH_FCLKONLY_EAR_THSD__POR (0x23) +#define TAIKO_A_CDC_CLSH_K_ADDR (0x328) +#define TAIKO_A_CDC_CLSH_K_ADDR__POR (0x00) +#define TAIKO_A_CDC_CLSH_K_DATA (0x329) +#define TAIKO_A_CDC_CLSH_K_DATA__POR (0xA4) +#define TAIKO_A_CDC_CLSH_I_PA_FACT_HPH_L (0x32A) +#define TAIKO_A_CDC_CLSH_I_PA_FACT_HPH_L__POR (0xD7) +#define TAIKO_A_CDC_CLSH_I_PA_FACT_HPH_U (0x32B) +#define TAIKO_A_CDC_CLSH_I_PA_FACT_HPH_U__POR (0x05) +#define TAIKO_A_CDC_CLSH_I_PA_FACT_EAR_L (0x32C) +#define TAIKO_A_CDC_CLSH_I_PA_FACT_EAR_L__POR (0x60) +#define TAIKO_A_CDC_CLSH_I_PA_FACT_EAR_U (0x32D) +#define TAIKO_A_CDC_CLSH_I_PA_FACT_EAR_U__POR (0x09) +#define TAIKO_A_CDC_CLSH_V_PA_HD_EAR (0x32E) +#define TAIKO_A_CDC_CLSH_V_PA_HD_EAR__POR (0x00) +#define TAIKO_A_CDC_CLSH_V_PA_HD_HPH (0x32F) +#define TAIKO_A_CDC_CLSH_V_PA_HD_HPH__POR (0x00) +#define TAIKO_A_CDC_CLSH_V_PA_MIN_EAR (0x330) +#define TAIKO_A_CDC_CLSH_V_PA_MIN_EAR__POR (0x00) +#define TAIKO_A_CDC_CLSH_V_PA_MIN_HPH (0x331) +#define TAIKO_A_CDC_CLSH_V_PA_MIN_HPH__POR (0x00) +#define TAIKO_A_CDC_IIR1_GAIN_B1_CTL (0x340) +#define TAIKO_A_CDC_IIR1_GAIN_B1_CTL__POR (0x00) +#define TAIKO_A_CDC_IIR2_GAIN_B1_CTL (0x350) +#define TAIKO_A_CDC_IIR2_GAIN_B1_CTL__POR (0x00) +#define TAIKO_A_CDC_IIR1_GAIN_B2_CTL (0x341) +#define TAIKO_A_CDC_IIR1_GAIN_B2_CTL__POR (0x00) +#define TAIKO_A_CDC_IIR2_GAIN_B2_CTL (0x351) +#define TAIKO_A_CDC_IIR2_GAIN_B2_CTL__POR (0x00) +#define TAIKO_A_CDC_IIR1_GAIN_B3_CTL (0x342) +#define TAIKO_A_CDC_IIR1_GAIN_B3_CTL__POR (0x00) +#define TAIKO_A_CDC_IIR2_GAIN_B3_CTL (0x352) +#define TAIKO_A_CDC_IIR2_GAIN_B3_CTL__POR (0x00) +#define TAIKO_A_CDC_IIR1_GAIN_B4_CTL (0x343) +#define TAIKO_A_CDC_IIR1_GAIN_B4_CTL__POR (0x00) +#define TAIKO_A_CDC_IIR2_GAIN_B4_CTL (0x353) +#define TAIKO_A_CDC_IIR2_GAIN_B4_CTL__POR (0x00) +#define TAIKO_A_CDC_IIR1_GAIN_B5_CTL (0x344) +#define TAIKO_A_CDC_IIR1_GAIN_B5_CTL__POR (0x00) +#define TAIKO_A_CDC_IIR2_GAIN_B5_CTL (0x354) +#define TAIKO_A_CDC_IIR2_GAIN_B5_CTL__POR (0x00) +#define TAIKO_A_CDC_IIR1_GAIN_B6_CTL (0x345) +#define TAIKO_A_CDC_IIR1_GAIN_B6_CTL__POR (0x00) +#define TAIKO_A_CDC_IIR2_GAIN_B6_CTL (0x355) +#define TAIKO_A_CDC_IIR2_GAIN_B6_CTL__POR (0x00) +#define TAIKO_A_CDC_IIR1_GAIN_B7_CTL (0x346) +#define TAIKO_A_CDC_IIR1_GAIN_B7_CTL__POR (0x00) +#define TAIKO_A_CDC_IIR2_GAIN_B7_CTL (0x356) +#define TAIKO_A_CDC_IIR2_GAIN_B7_CTL__POR (0x00) +#define TAIKO_A_CDC_IIR1_GAIN_B8_CTL (0x347) +#define TAIKO_A_CDC_IIR1_GAIN_B8_CTL__POR (0x00) +#define TAIKO_A_CDC_IIR2_GAIN_B8_CTL (0x357) +#define TAIKO_A_CDC_IIR2_GAIN_B8_CTL__POR (0x00) +#define TAIKO_A_CDC_IIR1_CTL (0x348) +#define TAIKO_A_CDC_IIR1_CTL__POR (0x40) +#define TAIKO_A_CDC_IIR2_CTL (0x358) +#define TAIKO_A_CDC_IIR2_CTL__POR (0x40) +#define TAIKO_A_CDC_IIR1_GAIN_TIMER_CTL (0x349) +#define TAIKO_A_CDC_IIR1_GAIN_TIMER_CTL__POR (0x00) +#define TAIKO_A_CDC_IIR2_GAIN_TIMER_CTL (0x359) +#define TAIKO_A_CDC_IIR2_GAIN_TIMER_CTL__POR (0x00) +#define TAIKO_A_CDC_IIR1_COEF_B1_CTL (0x34A) +#define TAIKO_A_CDC_IIR1_COEF_B1_CTL__POR (0x00) +#define TAIKO_A_CDC_IIR2_COEF_B1_CTL (0x35A) +#define TAIKO_A_CDC_IIR2_COEF_B1_CTL__POR (0x00) +#define TAIKO_A_CDC_IIR1_COEF_B2_CTL (0x34B) +#define TAIKO_A_CDC_IIR1_COEF_B2_CTL__POR (0x00) +#define TAIKO_A_CDC_IIR2_COEF_B2_CTL (0x35B) +#define TAIKO_A_CDC_IIR2_COEF_B2_CTL__POR (0x00) +#define TAIKO_A_CDC_TOP_GAIN_UPDATE (0x360) +#define TAIKO_A_CDC_TOP_GAIN_UPDATE__POR (0x00) +#define TAIKO_A_CDC_COMP0_B1_CTL (0x368) +#define TAIKO_A_CDC_COMP0_B1_CTL__POR (0x30) +#define TAIKO_A_CDC_COMP1_B1_CTL (0x370) +#define TAIKO_A_CDC_COMP1_B1_CTL__POR (0x30) +#define TAIKO_A_CDC_COMP2_B1_CTL (0x378) +#define TAIKO_A_CDC_COMP2_B1_CTL__POR (0x30) +#define TAIKO_A_CDC_COMP0_B2_CTL (0x369) +#define TAIKO_A_CDC_COMP0_B2_CTL__POR (0xB5) +#define TAIKO_A_CDC_COMP1_B2_CTL (0x371) +#define TAIKO_A_CDC_COMP1_B2_CTL__POR (0xB5) +#define TAIKO_A_CDC_COMP2_B2_CTL (0x379) +#define TAIKO_A_CDC_COMP2_B2_CTL__POR (0xB5) +#define TAIKO_A_CDC_COMP0_B3_CTL (0x36A) +#define TAIKO_A_CDC_COMP0_B3_CTL__POR (0x28) +#define TAIKO_A_CDC_COMP1_B3_CTL (0x372) +#define TAIKO_A_CDC_COMP1_B3_CTL__POR (0x28) +#define TAIKO_A_CDC_COMP2_B3_CTL (0x37A) +#define TAIKO_A_CDC_COMP2_B3_CTL__POR (0x28) +#define TAIKO_A_CDC_COMP0_B4_CTL (0x36B) +#define TAIKO_A_CDC_COMP0_B4_CTL__POR (0x3C) +#define TAIKO_A_CDC_COMP1_B4_CTL (0x373) +#define TAIKO_A_CDC_COMP1_B4_CTL__POR (0x3C) +#define TAIKO_A_CDC_COMP2_B4_CTL (0x37B) +#define TAIKO_A_CDC_COMP2_B4_CTL__POR (0x3C) +#define TAIKO_A_CDC_COMP0_B5_CTL (0x36C) +#define TAIKO_A_CDC_COMP0_B5_CTL__POR (0x1F) +#define TAIKO_A_CDC_COMP1_B5_CTL (0x374) +#define TAIKO_A_CDC_COMP1_B5_CTL__POR (0x1F) +#define TAIKO_A_CDC_COMP2_B5_CTL (0x37C) +#define TAIKO_A_CDC_COMP2_B5_CTL__POR (0x1F) +#define TAIKO_A_CDC_COMP0_B6_CTL (0x36D) +#define TAIKO_A_CDC_COMP0_B6_CTL__POR (0x00) +#define TAIKO_A_CDC_COMP1_B6_CTL (0x375) +#define TAIKO_A_CDC_COMP1_B6_CTL__POR (0x00) +#define TAIKO_A_CDC_COMP2_B6_CTL (0x37D) +#define TAIKO_A_CDC_COMP2_B6_CTL__POR (0x00) +#define TAIKO_A_CDC_COMP0_SHUT_DOWN_STATUS (0x36E) +#define TAIKO_A_CDC_COMP0_SHUT_DOWN_STATUS__POR (0x03) +#define TAIKO_A_CDC_COMP1_SHUT_DOWN_STATUS (0x376) +#define TAIKO_A_CDC_COMP1_SHUT_DOWN_STATUS__POR (0x03) +#define TAIKO_A_CDC_COMP2_SHUT_DOWN_STATUS (0x37E) +#define TAIKO_A_CDC_COMP2_SHUT_DOWN_STATUS__POR (0x03) +#define TAIKO_A_CDC_COMP0_FS_CFG (0x36F) +#define TAIKO_A_CDC_COMP0_FS_CFG__POR (0x03) +#define TAIKO_A_CDC_COMP1_FS_CFG (0x377) +#define TAIKO_A_CDC_COMP1_FS_CFG__POR (0x03) +#define TAIKO_A_CDC_COMP2_FS_CFG (0x37F) +#define TAIKO_A_CDC_COMP2_FS_CFG__POR (0x03) +#define TAIKO_A_CDC_CONN_RX1_B1_CTL (0x380) +#define TAIKO_A_CDC_CONN_RX1_B1_CTL__POR (0x00) +#define TAIKO_A_CDC_CONN_RX1_B2_CTL (0x381) +#define TAIKO_A_CDC_CONN_RX1_B2_CTL__POR (0x00) +#define TAIKO_A_CDC_CONN_RX1_B3_CTL (0x382) +#define TAIKO_A_CDC_CONN_RX1_B3_CTL__POR (0x00) +#define TAIKO_A_CDC_CONN_RX2_B1_CTL (0x383) +#define TAIKO_A_CDC_CONN_RX2_B1_CTL__POR (0x00) +#define TAIKO_A_CDC_CONN_RX2_B2_CTL (0x384) +#define TAIKO_A_CDC_CONN_RX2_B2_CTL__POR (0x00) +#define TAIKO_A_CDC_CONN_RX2_B3_CTL (0x385) +#define TAIKO_A_CDC_CONN_RX2_B3_CTL__POR (0x00) +#define TAIKO_A_CDC_CONN_RX3_B1_CTL (0x386) +#define TAIKO_A_CDC_CONN_RX3_B1_CTL__POR (0x00) +#define TAIKO_A_CDC_CONN_RX3_B2_CTL (0x387) +#define TAIKO_A_CDC_CONN_RX3_B2_CTL__POR (0x00) +#define TAIKO_A_CDC_CONN_RX4_B1_CTL (0x388) +#define TAIKO_A_CDC_CONN_RX4_B1_CTL__POR (0x00) +#define TAIKO_A_CDC_CONN_RX4_B2_CTL (0x389) +#define TAIKO_A_CDC_CONN_RX4_B2_CTL__POR (0x00) +#define TAIKO_A_CDC_CONN_RX5_B1_CTL (0x38A) +#define TAIKO_A_CDC_CONN_RX5_B1_CTL__POR (0x00) +#define TAIKO_A_CDC_CONN_RX5_B2_CTL (0x38B) +#define TAIKO_A_CDC_CONN_RX5_B2_CTL__POR (0x00) +#define TAIKO_A_CDC_CONN_RX6_B1_CTL (0x38C) +#define TAIKO_A_CDC_CONN_RX6_B1_CTL__POR (0x00) +#define TAIKO_A_CDC_CONN_RX6_B2_CTL (0x38D) +#define TAIKO_A_CDC_CONN_RX6_B2_CTL__POR (0x00) +#define TAIKO_A_CDC_CONN_RX7_B1_CTL (0x38E) +#define TAIKO_A_CDC_CONN_RX7_B1_CTL__POR (0x00) +#define TAIKO_A_CDC_CONN_RX7_B2_CTL (0x38F) +#define TAIKO_A_CDC_CONN_RX7_B2_CTL__POR (0x00) +#define TAIKO_A_CDC_CONN_RX7_B3_CTL (0x390) +#define TAIKO_A_CDC_CONN_RX7_B3_CTL__POR (0x00) +#define TAIKO_A_CDC_CONN_ANC_B1_CTL (0x391) +#define TAIKO_A_CDC_CONN_ANC_B1_CTL__POR (0x00) +#define TAIKO_A_CDC_CONN_ANC_B2_CTL (0x392) +#define TAIKO_A_CDC_CONN_ANC_B2_CTL__POR (0x00) +#define TAIKO_A_CDC_CONN_TX_B1_CTL (0x393) +#define TAIKO_A_CDC_CONN_TX_B1_CTL__POR (0x00) +#define TAIKO_A_CDC_CONN_TX_B2_CTL (0x394) +#define TAIKO_A_CDC_CONN_TX_B2_CTL__POR (0x00) +#define TAIKO_A_CDC_CONN_TX_B3_CTL (0x395) +#define TAIKO_A_CDC_CONN_TX_B3_CTL__POR (0x00) +#define TAIKO_A_CDC_CONN_TX_B4_CTL (0x396) +#define TAIKO_A_CDC_CONN_TX_B4_CTL__POR (0x00) +#define TAIKO_A_CDC_CONN_EQ1_B1_CTL (0x397) +#define TAIKO_A_CDC_CONN_EQ1_B1_CTL__POR (0x00) +#define TAIKO_A_CDC_CONN_EQ1_B2_CTL (0x398) +#define TAIKO_A_CDC_CONN_EQ1_B2_CTL__POR (0x00) +#define TAIKO_A_CDC_CONN_EQ1_B3_CTL (0x399) +#define TAIKO_A_CDC_CONN_EQ1_B3_CTL__POR (0x00) +#define TAIKO_A_CDC_CONN_EQ1_B4_CTL (0x39A) +#define TAIKO_A_CDC_CONN_EQ1_B4_CTL__POR (0x00) +#define TAIKO_A_CDC_CONN_EQ2_B1_CTL (0x39B) +#define TAIKO_A_CDC_CONN_EQ2_B1_CTL__POR (0x00) +#define TAIKO_A_CDC_CONN_EQ2_B2_CTL (0x39C) +#define TAIKO_A_CDC_CONN_EQ2_B2_CTL__POR (0x00) +#define TAIKO_A_CDC_CONN_EQ2_B3_CTL (0x39D) +#define TAIKO_A_CDC_CONN_EQ2_B3_CTL__POR (0x00) +#define TAIKO_A_CDC_CONN_EQ2_B4_CTL (0x39E) +#define TAIKO_A_CDC_CONN_EQ2_B4_CTL__POR (0x00) +#define TAIKO_A_CDC_CONN_SRC1_B1_CTL (0x39F) +#define TAIKO_A_CDC_CONN_SRC1_B1_CTL__POR (0x00) +#define TAIKO_A_CDC_CONN_SRC1_B2_CTL (0x3A0) +#define TAIKO_A_CDC_CONN_SRC1_B2_CTL__POR (0x00) +#define TAIKO_A_CDC_CONN_SRC2_B1_CTL (0x3A1) +#define TAIKO_A_CDC_CONN_SRC2_B1_CTL__POR (0x00) +#define TAIKO_A_CDC_CONN_SRC2_B2_CTL (0x3A2) +#define TAIKO_A_CDC_CONN_SRC2_B2_CTL__POR (0x00) +#define TAIKO_A_CDC_CONN_TX_SB_B1_CTL (0x3A3) +#define TAIKO_A_CDC_CONN_TX_SB_B1_CTL__POR (0x00) +#define TAIKO_A_CDC_CONN_TX_SB_B2_CTL (0x3A4) +#define TAIKO_A_CDC_CONN_TX_SB_B2_CTL__POR (0x00) +#define TAIKO_A_CDC_CONN_TX_SB_B3_CTL (0x3A5) +#define TAIKO_A_CDC_CONN_TX_SB_B3_CTL__POR (0x00) +#define TAIKO_A_CDC_CONN_TX_SB_B4_CTL (0x3A6) +#define TAIKO_A_CDC_CONN_TX_SB_B4_CTL__POR (0x00) +#define TAIKO_A_CDC_CONN_TX_SB_B5_CTL (0x3A7) +#define TAIKO_A_CDC_CONN_TX_SB_B5_CTL__POR (0x00) +#define TAIKO_A_CDC_CONN_TX_SB_B6_CTL (0x3A8) +#define TAIKO_A_CDC_CONN_TX_SB_B6_CTL__POR (0x00) +#define TAIKO_A_CDC_CONN_TX_SB_B7_CTL (0x3A9) +#define TAIKO_A_CDC_CONN_TX_SB_B7_CTL__POR (0x00) +#define TAIKO_A_CDC_CONN_TX_SB_B8_CTL (0x3AA) +#define TAIKO_A_CDC_CONN_TX_SB_B8_CTL__POR (0x00) +#define TAIKO_A_CDC_CONN_TX_SB_B9_CTL (0x3AB) +#define TAIKO_A_CDC_CONN_TX_SB_B9_CTL__POR (0x00) +#define TAIKO_A_CDC_CONN_TX_SB_B10_CTL (0x3AC) +#define TAIKO_A_CDC_CONN_TX_SB_B10_CTL__POR (0x00) +#define TAIKO_A_CDC_CONN_TX_SB_B11_CTL (0x3AD) +#define TAIKO_A_CDC_CONN_TX_SB_B11_CTL__POR (0x00) +#define TAIKO_A_CDC_CONN_RX_SB_B1_CTL (0x3AE) +#define TAIKO_A_CDC_CONN_RX_SB_B1_CTL__POR (0x00) +#define TAIKO_A_CDC_CONN_RX_SB_B2_CTL (0x3AF) +#define TAIKO_A_CDC_CONN_RX_SB_B2_CTL__POR (0x00) +#define TAIKO_A_CDC_CONN_CLSH_CTL (0x3B0) +#define TAIKO_A_CDC_CONN_CLSH_CTL__POR (0x00) +#define TAIKO_A_CDC_CONN_MISC (0x3B1) +#define TAIKO_A_CDC_CONN_MISC__POR (0x01) +#define TAIKO_A_CDC_CONN_MAD (0x3B2) +#define TAIKO_A_CDC_CONN_MAD__POR (0x01) +#define TAIKO_A_CDC_MBHC_EN_CTL (0x3C0) +#define TAIKO_A_CDC_MBHC_EN_CTL__POR (0x00) +#define TAIKO_A_CDC_MBHC_FIR_B1_CFG (0x3C1) +#define TAIKO_A_CDC_MBHC_FIR_B1_CFG__POR (0x00) +#define TAIKO_A_CDC_MBHC_FIR_B2_CFG (0x3C2) +#define TAIKO_A_CDC_MBHC_FIR_B2_CFG__POR (0x06) +#define TAIKO_A_CDC_MBHC_TIMER_B1_CTL (0x3C3) +#define TAIKO_A_CDC_MBHC_TIMER_B1_CTL__POR (0x03) +#define TAIKO_A_CDC_MBHC_TIMER_B2_CTL (0x3C4) +#define TAIKO_A_CDC_MBHC_TIMER_B2_CTL__POR (0x09) +#define TAIKO_A_CDC_MBHC_TIMER_B3_CTL (0x3C5) +#define TAIKO_A_CDC_MBHC_TIMER_B3_CTL__POR (0x1E) +#define TAIKO_A_CDC_MBHC_TIMER_B4_CTL (0x3C6) +#define TAIKO_A_CDC_MBHC_TIMER_B4_CTL__POR (0x45) +#define TAIKO_A_CDC_MBHC_TIMER_B5_CTL (0x3C7) +#define TAIKO_A_CDC_MBHC_TIMER_B5_CTL__POR (0x04) +#define TAIKO_A_CDC_MBHC_TIMER_B6_CTL (0x3C8) +#define TAIKO_A_CDC_MBHC_TIMER_B6_CTL__POR (0x78) +#define TAIKO_A_CDC_MBHC_B1_STATUS (0x3C9) +#define TAIKO_A_CDC_MBHC_B1_STATUS__POR (0x00) +#define TAIKO_A_CDC_MBHC_B2_STATUS (0x3CA) +#define TAIKO_A_CDC_MBHC_B2_STATUS__POR (0x00) +#define TAIKO_A_CDC_MBHC_B3_STATUS (0x3CB) +#define TAIKO_A_CDC_MBHC_B3_STATUS__POR (0x00) +#define TAIKO_A_CDC_MBHC_B4_STATUS (0x3CC) +#define TAIKO_A_CDC_MBHC_B4_STATUS__POR (0x00) +#define TAIKO_A_CDC_MBHC_B5_STATUS (0x3CD) +#define TAIKO_A_CDC_MBHC_B5_STATUS__POR (0x00) +#define TAIKO_A_CDC_MBHC_B1_CTL (0x3CE) +#define TAIKO_A_CDC_MBHC_B1_CTL__POR (0xC0) +#define TAIKO_A_CDC_MBHC_B2_CTL (0x3CF) +#define TAIKO_A_CDC_MBHC_B2_CTL__POR (0x5D) +#define TAIKO_A_CDC_MBHC_VOLT_B1_CTL (0x3D0) +#define TAIKO_A_CDC_MBHC_VOLT_B1_CTL__POR (0x00) +#define TAIKO_A_CDC_MBHC_VOLT_B2_CTL (0x3D1) +#define TAIKO_A_CDC_MBHC_VOLT_B2_CTL__POR (0x00) +#define TAIKO_A_CDC_MBHC_VOLT_B3_CTL (0x3D2) +#define TAIKO_A_CDC_MBHC_VOLT_B3_CTL__POR (0x00) +#define TAIKO_A_CDC_MBHC_VOLT_B4_CTL (0x3D3) +#define TAIKO_A_CDC_MBHC_VOLT_B4_CTL__POR (0x00) +#define TAIKO_A_CDC_MBHC_VOLT_B5_CTL (0x3D4) +#define TAIKO_A_CDC_MBHC_VOLT_B5_CTL__POR (0x00) +#define TAIKO_A_CDC_MBHC_VOLT_B6_CTL (0x3D5) +#define TAIKO_A_CDC_MBHC_VOLT_B6_CTL__POR (0x00) +#define TAIKO_A_CDC_MBHC_VOLT_B7_CTL (0x3D6) +#define TAIKO_A_CDC_MBHC_VOLT_B7_CTL__POR (0xFF) +#define TAIKO_A_CDC_MBHC_VOLT_B8_CTL (0x3D7) +#define TAIKO_A_CDC_MBHC_VOLT_B8_CTL__POR (0x07) +#define TAIKO_A_CDC_MBHC_VOLT_B9_CTL (0x3D8) +#define TAIKO_A_CDC_MBHC_VOLT_B9_CTL__POR (0xFF) +#define TAIKO_A_CDC_MBHC_VOLT_B10_CTL (0x3D9) +#define TAIKO_A_CDC_MBHC_VOLT_B10_CTL__POR (0x7F) +#define TAIKO_A_CDC_MBHC_VOLT_B11_CTL (0x3DA) +#define TAIKO_A_CDC_MBHC_VOLT_B11_CTL__POR (0x00) +#define TAIKO_A_CDC_MBHC_VOLT_B12_CTL (0x3DB) +#define TAIKO_A_CDC_MBHC_VOLT_B12_CTL__POR (0x80) +#define TAIKO_A_CDC_MBHC_CLK_CTL (0x3DC) +#define TAIKO_A_CDC_MBHC_CLK_CTL__POR (0x00) +#define TAIKO_A_CDC_MBHC_INT_CTL (0x3DD) +#define TAIKO_A_CDC_MBHC_INT_CTL__POR (0x00) +#define TAIKO_A_CDC_MBHC_DEBUG_CTL (0x3DE) +#define TAIKO_A_CDC_MBHC_DEBUG_CTL__POR (0x00) +#define TAIKO_A_CDC_MBHC_SPARE (0x3DF) +#define TAIKO_A_CDC_MBHC_SPARE__POR (0x00) +#define TAIKO_A_CDC_MAD_MAIN_CTL_1 (0x3E0) +#define TAIKO_A_CDC_MAD_MAIN_CTL_1__POR (0x00) +#define TAIKO_A_CDC_MAD_MAIN_CTL_2 (0x3E1) +#define TAIKO_A_CDC_MAD_MAIN_CTL_2__POR (0x00) +#define TAIKO_A_CDC_MAD_AUDIO_CTL_1 (0x3E2) +#define TAIKO_A_CDC_MAD_AUDIO_CTL_1__POR (0x00) +#define TAIKO_A_CDC_MAD_AUDIO_CTL_2 (0x3E3) +#define TAIKO_A_CDC_MAD_AUDIO_CTL_2__POR (0x00) +#define TAIKO_A_CDC_MAD_AUDIO_CTL_3 (0x3E4) +#define TAIKO_A_CDC_MAD_AUDIO_CTL_3__POR (0x00) +#define TAIKO_A_CDC_MAD_AUDIO_CTL_4 (0x3E5) +#define TAIKO_A_CDC_MAD_AUDIO_CTL_4__POR (0x00) +#define TAIKO_A_CDC_MAD_AUDIO_CTL_5 (0x3E6) +#define TAIKO_A_CDC_MAD_AUDIO_CTL_5__POR (0x00) +#define TAIKO_A_CDC_MAD_AUDIO_CTL_6 (0x3E7) +#define TAIKO_A_CDC_MAD_AUDIO_CTL_6__POR (0x00) +#define TAIKO_A_CDC_MAD_AUDIO_CTL_7 (0x3E8) +#define TAIKO_A_CDC_MAD_AUDIO_CTL_7__POR (0x00) +#define TAIKO_A_CDC_MAD_AUDIO_CTL_8 (0x3E9) +#define TAIKO_A_CDC_MAD_AUDIO_CTL_8__POR (0x00) +#define TAIKO_A_CDC_MAD_AUDIO_IIR_CTL_PTR (0x3EA) +#define TAIKO_A_CDC_MAD_AUDIO_IIR_CTL_PTR__POR (0x00) +#define TAIKO_A_CDC_MAD_AUDIO_IIR_CTL_VAL (0x3EB) +#define TAIKO_A_CDC_MAD_AUDIO_IIR_CTL_VAL__POR (0x40) +#define TAIKO_A_CDC_MAD_ULTR_CTL_1 (0x3EC) +#define TAIKO_A_CDC_MAD_ULTR_CTL_1__POR (0x00) +#define TAIKO_A_CDC_MAD_ULTR_CTL_2 (0x3ED) +#define TAIKO_A_CDC_MAD_ULTR_CTL_2__POR (0x00) +#define TAIKO_A_CDC_MAD_ULTR_CTL_3 (0x3EE) +#define TAIKO_A_CDC_MAD_ULTR_CTL_3__POR (0x00) +#define TAIKO_A_CDC_MAD_ULTR_CTL_4 (0x3EF) +#define TAIKO_A_CDC_MAD_ULTR_CTL_4__POR (0x00) +#define TAIKO_A_CDC_MAD_ULTR_CTL_5 (0x3F0) +#define TAIKO_A_CDC_MAD_ULTR_CTL_5__POR (0x00) +#define TAIKO_A_CDC_MAD_ULTR_CTL_6 (0x3F1) +#define TAIKO_A_CDC_MAD_ULTR_CTL_6__POR (0x00) +#define TAIKO_A_CDC_MAD_ULTR_CTL_7 (0x3F2) +#define TAIKO_A_CDC_MAD_ULTR_CTL_7__POR (0x00) +#define TAIKO_A_CDC_MAD_BEACON_CTL_1 (0x3F3) +#define TAIKO_A_CDC_MAD_BEACON_CTL_1__POR (0x00) +#define TAIKO_A_CDC_MAD_BEACON_CTL_2 (0x3F4) +#define TAIKO_A_CDC_MAD_BEACON_CTL_2__POR (0x00) +#define TAIKO_A_CDC_MAD_BEACON_CTL_3 (0x3F5) +#define TAIKO_A_CDC_MAD_BEACON_CTL_3__POR (0x00) +#define TAIKO_A_CDC_MAD_BEACON_CTL_4 (0x3F6) +#define TAIKO_A_CDC_MAD_BEACON_CTL_4__POR (0x00) +#define TAIKO_A_CDC_MAD_BEACON_CTL_5 (0x3F7) +#define TAIKO_A_CDC_MAD_BEACON_CTL_5__POR (0x00) +#define TAIKO_A_CDC_MAD_BEACON_CTL_6 (0x3F8) +#define TAIKO_A_CDC_MAD_BEACON_CTL_6__POR (0x00) +#define TAIKO_A_CDC_MAD_BEACON_CTL_7 (0x3F9) +#define TAIKO_A_CDC_MAD_BEACON_CTL_7__POR (0x00) +#define TAIKO_A_CDC_MAD_BEACON_CTL_8 (0x3FA) +#define TAIKO_A_CDC_MAD_BEACON_CTL_8__POR (0x00) +#define TAIKO_A_CDC_MAD_BEACON_IIR_CTL_PTR (0x3FB) +#define TAIKO_A_CDC_MAD_BEACON_IIR_CTL_PTR__POR (0x00) +#define TAIKO_A_CDC_MAD_BEACON_IIR_CTL_VAL (0x3FC) +#define TAIKO_A_CDC_MAD_BEACON_IIR_CTL_VAL__POR (0x00) + +/* Taiko v2+ registers */ +#define TAIKO_A_CDC_TX_1_GAIN (0x153) +#define TAIKO_A_CDC_TX_1_GAIN__POR (0x02) +#define TAIKO_A_CDC_TX_2_GAIN (0x155) +#define TAIKO_A_CDC_TX_2_GAIN__POR (0x02) +#define TAIKO_A_CDC_TX_1_2_ADC_IB (0x156) +#define TAIKO_A_CDC_TX_1_2_ADC_IB__POR (0x44) +#define TAIKO_A_CDC_TX_3_GAIN (0x15D) +#define TAIKO_A_CDC_TX_3_GAIN__POR (0x02) +#define TAIKO_A_CDC_TX_4_GAIN (0x15F) +#define TAIKO_A_CDC_TX_4_GAIN__POR (0x02) +#define TAIKO_A_CDC_TX_3_4_ADC_IB (0x160) +#define TAIKO_A_CDC_TX_3_4_ADC_IB__POR (0x44) +#define TAIKO_A_CDC_TX_5_GAIN (0x167) +#define TAIKO_A_CDC_TX_5_GAIN__POR (0x02) +#define TAIKO_A_CDC_TX_6_GAIN (0x169) +#define TAIKO_A_CDC_TX_6_GAIN__POR (0x02) +#define TAIKO_A_CDC_TX_5_6_ADC_IB (0x16A) +#define TAIKO_A_CDC_TX_5_6_ADC_IB__POR (0x44) +#define TAIKO_A_CDC_SPKR_CLIPDET_VAL0 (0x270) +#define TAIKO_A_CDC_SPKR_CLIPDET_VAL0__POR (0x00) +#define TAIKO_A_CDC_SPKR_CLIPDET_VAL1 (0x271) +#define TAIKO_A_CDC_SPKR_CLIPDET_VAL1__POR (0x00) +#define TAIKO_A_CDC_SPKR_CLIPDET_VAL2 (0x272) +#define TAIKO_A_CDC_SPKR_CLIPDET_VAL2__POR (0x00) +#define TAIKO_A_CDC_SPKR_CLIPDET_VAL3 (0x273) +#define TAIKO_A_CDC_SPKR_CLIPDET_VAL3__POR (0x00) +#define TAIKO_A_CDC_SPKR_CLIPDET_VAL4 (0x274) +#define TAIKO_A_CDC_SPKR_CLIPDET_VAL4__POR (0x00) +#define TAIKO_A_CDC_SPKR_CLIPDET_VAL5 (0x275) +#define TAIKO_A_CDC_SPKR_CLIPDET_VAL5__POR (0x00) +#define TAIKO_A_CDC_SPKR_CLIPDET_VAL6 (0x276) +#define TAIKO_A_CDC_SPKR_CLIPDET_VAL6__POR (0x00) +#define TAIKO_A_CDC_SPKR_CLIPDET_VAL7 (0x277) +#define TAIKO_A_CDC_SPKR_CLIPDET_VAL7__POR (0x00) +#define TAIKO_A_CDC_VBAT_GAIN_UPD_MON (0x2FA) +#define TAIKO_A_CDC_VBAT_GAIN_UPD_MON__POR (0x00) +#define TAIKO_A_CDC_VBAT_GAIN_MON_VAL (0x2FB) +#define TAIKO_A_CDC_VBAT_GAIN_MON_VAL__POR (0x00) +#define TAIKO_A_CDC_PA_RAMP_B1_CTL (0x361) +#define TAIKO_A_CDC_PA_RAMP_B1_CTL__POR (0x00) +#define TAIKO_A_CDC_PA_RAMP_B2_CTL (0x362) +#define TAIKO_A_CDC_PA_RAMP_B2_CTL__POR (0x00) +#define TAIKO_A_CDC_PA_RAMP_B3_CTL (0x363) +#define TAIKO_A_CDC_PA_RAMP_B3_CTL__POR (0x00) +#define TAIKO_A_CDC_PA_RAMP_B4_CTL (0x364) +#define TAIKO_A_CDC_PA_RAMP_B4_CTL__POR (0x00) +#define TAIKO_A_CDC_SPKR_CLIPDET_B1_CTL (0x365) +#define TAIKO_A_CDC_SPKR_CLIPDET_B1_CTL__POR (0x00) + +/* SLIMBUS Slave Registers */ +#define TAIKO_SLIM_PGD_PORT_INT_EN0 (0x30) +#define TAIKO_SLIM_PGD_PORT_INT_STATUS_RX_0 (0x34) +#define TAIKO_SLIM_PGD_PORT_INT_STATUS_RX_1 (0x35) +#define TAIKO_SLIM_PGD_PORT_INT_STATUS_TX_0 (0x36) +#define TAIKO_SLIM_PGD_PORT_INT_STATUS_TX_1 (0x37) +#define TAIKO_SLIM_PGD_PORT_INT_CLR_RX_0 (0x38) +#define TAIKO_SLIM_PGD_PORT_INT_CLR_RX_1 (0x39) +#define TAIKO_SLIM_PGD_PORT_INT_CLR_TX_0 (0x3A) +#define TAIKO_SLIM_PGD_PORT_INT_CLR_TX_1 (0x3B) +#define TAIKO_SLIM_PGD_PORT_INT_RX_SOURCE0 (0x60) +#define TAIKO_SLIM_PGD_PORT_INT_TX_SOURCE0 (0x70) + +/* Macros for Packing Register Writes into a U32 */ +#define TAIKO_PACKED_REG_SIZE sizeof(u32) + +#define TAIKO_CODEC_PACK_ENTRY(reg, mask, val) ((val & 0xff)|\ + ((mask & 0xff) << 8)|((reg & 0xffff) << 16)) + +#define TAIKO_CODEC_UNPACK_ENTRY(packed, reg, mask, val) \ + do { \ + ((reg) = ((packed >> 16) & (0xffff))); \ + ((mask) = ((packed >> 8) & (0xff))); \ + ((val) = ((packed) & (0xff))); \ + } while (0); + +#endif diff --git a/audio-kernel/include/uapi/linux/mfd/wcd9xxx/wcd9xxx_registers.h b/audio-kernel/include/uapi/linux/mfd/wcd9xxx/wcd9xxx_registers.h new file mode 100644 index 000000000..7902cfbaf --- /dev/null +++ b/audio-kernel/include/uapi/linux/mfd/wcd9xxx/wcd9xxx_registers.h @@ -0,0 +1,361 @@ +#ifndef WCD9XXX_CODEC_DIGITAL_H + +#define WCD9XXX_CODEC_DIGITAL_H + +#define WCD9XXX_A_CHIP_CTL (0x00) +#define WCD9XXX_A_CHIP_CTL__POR (0x00000000) +#define WCD9XXX_A_CHIP_STATUS (0x01) +#define WCD9XXX_A_CHIP_STATUS__POR (0x00000000) +#define WCD9XXX_A_CHIP_ID_BYTE_0 (0x04) +#define WCD9XXX_A_CHIP_ID_BYTE_0__POR (0x00000000) +#define WCD9XXX_A_CHIP_ID_BYTE_1 (0x05) +#define WCD9XXX_A_CHIP_ID_BYTE_1__POR (0x00000000) +#define WCD9XXX_A_CHIP_ID_BYTE_2 (0x06) +#define WCD9XXX_A_CHIP_ID_BYTE_2__POR (0x00000000) +#define WCD9XXX_A_CHIP_ID_BYTE_3 (0x07) +#define WCD9XXX_A_CHIP_ID_BYTE_3__POR (0x00000001) +#define WCD9XXX_A_CHIP_VERSION (0x08) +#define WCD9XXX_A_CHIP_VERSION__POR (0x00000020) +#define WCD9XXX_A_SB_VERSION (0x09) +#define WCD9XXX_A_SB_VERSION__POR (0x00000010) +#define WCD9XXX_A_SLAVE_ID_1 (0x0C) +#define WCD9XXX_A_SLAVE_ID_1__POR (0x00000077) +#define WCD9XXX_A_SLAVE_ID_2 (0x0D) +#define WCD9XXX_A_SLAVE_ID_2__POR (0x00000066) +#define WCD9XXX_A_SLAVE_ID_3 (0x0E) +#define WCD9XXX_A_SLAVE_ID_3__POR (0x00000055) +#define WCD9XXX_A_CDC_CTL (0x80) +#define WCD9XXX_A_CDC_CTL__POR (0x00000000) +#define WCD9XXX_A_LEAKAGE_CTL (0x88) +#define WCD9XXX_A_LEAKAGE_CTL__POR (0x00000004) +#define WCD9XXX_A_INTR_MODE (0x90) +#define WCD9XXX_A_INTR_MASK0 (0x94) +#define WCD9XXX_A_INTR_STATUS0 (0x98) +#define WCD9XXX_A_INTR_CLEAR0 (0x9C) +#define WCD9XXX_A_INTR_LEVEL0 (0xA0) +#define WCD9XXX_A_INTR_LEVEL1 (0xA1) +#define WCD9XXX_A_INTR_LEVEL2 (0xA2) +#define WCD9XXX_A_RX_HPH_CNP_EN (0x1AB) +#define WCD9XXX_A_RX_HPH_CNP_EN__POR (0x80) +#define WCD9XXX_A_RX_HPH_CNP_EN (0x1AB) +#define WCD9XXX_A_RX_HPH_CNP_EN__POR (0x80) +#define WCD9XXX_A_BIAS_CENTRAL_BG_CTL (0x101) +#define WCD9XXX_A_BIAS_CENTRAL_BG_CTL__POR (0x50) +#define WCD9XXX_A_CLK_BUFF_EN1 (0x108) +#define WCD9XXX_A_CLK_BUFF_EN1__POR (0x04) +#define WCD9XXX_A_CLK_BUFF_EN2 (0x109) +#define WCD9XXX_A_CLK_BUFF_EN2__POR (0x02) +#define WCD9XXX_A_RX_COM_BIAS (0x1A2) +#define WCD9XXX_A_RX_COM_BIAS__POR (0x00) +#define WCD9XXX_A_RC_OSC_FREQ (0x1FA) +#define WCD9XXX_A_RC_OSC_FREQ__POR (0x46) +#define WCD9XXX_A_BIAS_OSC_BG_CTL (0x105) +#define WCD9XXX_A_BIAS_OSC_BG_CTL__POR (0x16) +#define WCD9XXX_A_RC_OSC_TEST (0x1FB) +#define WCD9XXX_A_RC_OSC_TEST__POR (0x0A) +#define WCD9XXX_A_CDC_CLK_MCLK_CTL (0x311) +#define WCD9XXX_A_CDC_CLK_MCLK_CTL__POR (0x00) + +#define WCD9XXX_A_CDC_MBHC_EN_CTL (0x3C0) +#define WCD9XXX_A_CDC_MBHC_EN_CTL__POR (0x00) +#define WCD9XXX_A_CDC_MBHC_FIR_B1_CFG (0x3C1) +#define WCD9XXX_A_CDC_MBHC_FIR_B1_CFG__POR (0x00) +#define WCD9XXX_A_CDC_MBHC_FIR_B2_CFG (0x3C2) +#define WCD9XXX_A_CDC_MBHC_FIR_B2_CFG__POR (0x06) +#define WCD9XXX_A_CDC_MBHC_TIMER_B1_CTL (0x3C3) +#define WCD9XXX_A_CDC_MBHC_TIMER_B1_CTL__POR (0x03) +#define WCD9XXX_A_CDC_MBHC_TIMER_B2_CTL (0x3C4) +#define WCD9XXX_A_CDC_MBHC_TIMER_B2_CTL__POR (0x09) +#define WCD9XXX_A_CDC_MBHC_TIMER_B3_CTL (0x3C5) +#define WCD9XXX_A_CDC_MBHC_TIMER_B3_CTL__POR (0x1E) +#define WCD9XXX_A_CDC_MBHC_TIMER_B4_CTL (0x3C6) +#define WCD9XXX_A_CDC_MBHC_TIMER_B4_CTL__POR (0x45) +#define WCD9XXX_A_CDC_MBHC_TIMER_B5_CTL (0x3C7) +#define WCD9XXX_A_CDC_MBHC_TIMER_B5_CTL__POR (0x04) +#define WCD9XXX_A_CDC_MBHC_TIMER_B6_CTL (0x3C8) +#define WCD9XXX_A_CDC_MBHC_TIMER_B6_CTL__POR (0x78) +#define WCD9XXX_A_CDC_MBHC_B1_STATUS (0x3C9) +#define WCD9XXX_A_CDC_MBHC_B1_STATUS__POR (0x00) +#define WCD9XXX_A_CDC_MBHC_B2_STATUS (0x3CA) +#define WCD9XXX_A_CDC_MBHC_B2_STATUS__POR (0x00) +#define WCD9XXX_A_CDC_MBHC_B3_STATUS (0x3CB) +#define WCD9XXX_A_CDC_MBHC_B3_STATUS__POR (0x00) +#define WCD9XXX_A_CDC_MBHC_B4_STATUS (0x3CC) +#define WCD9XXX_A_CDC_MBHC_B4_STATUS__POR (0x00) +#define WCD9XXX_A_CDC_MBHC_B5_STATUS (0x3CD) +#define WCD9XXX_A_CDC_MBHC_B5_STATUS__POR (0x00) +#define WCD9XXX_A_CDC_MBHC_B1_CTL (0x3CE) +#define WCD9XXX_A_CDC_MBHC_B1_CTL__POR (0xC0) +#define WCD9XXX_A_CDC_MBHC_B2_CTL (0x3CF) +#define WCD9XXX_A_CDC_MBHC_B2_CTL__POR (0x5D) +#define WCD9XXX_A_CDC_MBHC_VOLT_B1_CTL (0x3D0) +#define WCD9XXX_A_CDC_MBHC_VOLT_B1_CTL__POR (0x00) +#define WCD9XXX_A_CDC_MBHC_VOLT_B2_CTL (0x3D1) +#define WCD9XXX_A_CDC_MBHC_VOLT_B2_CTL__POR (0x00) +#define WCD9XXX_A_CDC_MBHC_VOLT_B3_CTL (0x3D2) +#define WCD9XXX_A_CDC_MBHC_VOLT_B3_CTL__POR (0x00) +#define WCD9XXX_A_CDC_MBHC_VOLT_B4_CTL (0x3D3) +#define WCD9XXX_A_CDC_MBHC_VOLT_B4_CTL__POR (0x00) +#define WCD9XXX_A_CDC_MBHC_VOLT_B5_CTL (0x3D4) +#define WCD9XXX_A_CDC_MBHC_VOLT_B5_CTL__POR (0x00) +#define WCD9XXX_A_CDC_MBHC_VOLT_B6_CTL (0x3D5) +#define WCD9XXX_A_CDC_MBHC_VOLT_B6_CTL__POR (0x00) +#define WCD9XXX_A_CDC_MBHC_VOLT_B7_CTL (0x3D6) +#define WCD9XXX_A_CDC_MBHC_VOLT_B7_CTL__POR (0xFF) +#define WCD9XXX_A_CDC_MBHC_VOLT_B8_CTL (0x3D7) +#define WCD9XXX_A_CDC_MBHC_VOLT_B8_CTL__POR (0x07) +#define WCD9XXX_A_CDC_MBHC_VOLT_B9_CTL (0x3D8) +#define WCD9XXX_A_CDC_MBHC_VOLT_B9_CTL__POR (0xFF) +#define WCD9XXX_A_CDC_MBHC_VOLT_B10_CTL (0x3D9) +#define WCD9XXX_A_CDC_MBHC_VOLT_B10_CTL__POR (0x7F) +#define WCD9XXX_A_CDC_MBHC_VOLT_B11_CTL (0x3DA) +#define WCD9XXX_A_CDC_MBHC_VOLT_B11_CTL__POR (0x00) +#define WCD9XXX_A_CDC_MBHC_VOLT_B12_CTL (0x3DB) +#define WCD9XXX_A_CDC_MBHC_VOLT_B12_CTL__POR (0x80) +#define WCD9XXX_A_CDC_MBHC_CLK_CTL (0x3DC) +#define WCD9XXX_A_CDC_MBHC_CLK_CTL__POR (0x00) +#define WCD9XXX_A_CDC_MBHC_INT_CTL (0x3DD) +#define WCD9XXX_A_CDC_MBHC_INT_CTL__POR (0x00) +#define WCD9XXX_A_CDC_MBHC_DEBUG_CTL (0x3DE) +#define WCD9XXX_A_CDC_MBHC_DEBUG_CTL__POR (0x00) +#define WCD9XXX_A_CDC_MBHC_SPARE (0x3DF) +#define WCD9XXX_A_CDC_MBHC_SPARE__POR (0x00) +#define WCD9XXX_A_MBHC_SCALING_MUX_1 (0x14E) +#define WCD9XXX_A_MBHC_SCALING_MUX_1__POR (0x00) +#define WCD9XXX_A_RX_HPH_OCP_CTL (0x1AA) +#define WCD9XXX_A_RX_HPH_OCP_CTL__POR (0x68) +#define WCD9XXX_A_MICB_1_CTL (0x12B) +#define WCD9XXX_A_MICB_1_CTL__POR (0x16) +#define WCD9XXX_A_MICB_1_INT_RBIAS (0x12C) +#define WCD9XXX_A_MICB_1_INT_RBIAS__POR (0x24) +#define WCD9XXX_A_MICB_1_MBHC (0x12D) +#define WCD9XXX_A_MICB_1_MBHC__POR (0x01) +#define WCD9XXX_A_MICB_CFILT_2_CTL (0x12E) +#define WCD9XXX_A_MICB_CFILT_2_CTL__POR (0x40) +#define WCD9XXX_A_MICB_CFILT_2_VAL (0x12F) +#define WCD9XXX_A_MICB_CFILT_2_VAL__POR (0x80) +#define WCD9XXX_A_MICB_CFILT_2_PRECHRG (0x130) +#define WCD9XXX_A_MICB_CFILT_2_PRECHRG__POR (0x38) +#define WCD9XXX_A_MICB_2_CTL (0x131) +#define WCD9XXX_A_MICB_2_CTL__POR (0x16) +#define WCD9XXX_A_MICB_2_INT_RBIAS (0x132) +#define WCD9XXX_A_MICB_2_INT_RBIAS__POR (0x24) +#define WCD9XXX_A_MICB_2_MBHC (0x133) +#define WCD9XXX_A_MICB_2_MBHC__POR (0x02) +#define WCD9XXX_A_MICB_CFILT_3_CTL (0x134) +#define WCD9XXX_A_MICB_CFILT_3_CTL__POR (0x40) +#define WCD9XXX_A_MICB_CFILT_3_VAL (0x135) +#define WCD9XXX_A_MICB_CFILT_3_VAL__POR (0x80) +#define WCD9XXX_A_MICB_CFILT_3_PRECHRG (0x136) +#define WCD9XXX_A_MICB_CFILT_3_PRECHRG__POR (0x38) +#define WCD9XXX_A_MICB_3_CTL (0x137) +#define WCD9XXX_A_MICB_3_CTL__POR (0x16) +#define WCD9XXX_A_MICB_3_INT_RBIAS (0x138) +#define WCD9XXX_A_MICB_3_INT_RBIAS__POR (0x24) +#define WCD9XXX_A_MICB_3_MBHC (0x139) +#define WCD9XXX_A_MICB_3_MBHC__POR (0x00) +#define WCD9XXX_A_MICB_4_CTL (0x13D) +#define WCD9XXX_A_MICB_4_CTL__POR (0x16) +#define WCD9XXX_A_MICB_4_INT_RBIAS (0x13E) +#define WCD9XXX_A_MICB_4_INT_RBIAS__POR (0x24) +#define WCD9XXX_A_MICB_4_MBHC (0x13F) +#define WCD9XXX_A_MICB_4_MBHC__POR (0x01) +#define WCD9XXX_A_MICB_CFILT_1_VAL (0x129) +#define WCD9XXX_A_MICB_CFILT_1_VAL__POR (0x80) +#define WCD9XXX_A_RX_HPH_L_STATUS (0x1B3) +#define WCD9XXX_A_RX_HPH_L_STATUS__POR (0x00) +#define WCD9XXX_A_MBHC_HPH (0x1FE) +#define WCD9XXX_A_MBHC_HPH__POR (0x44) +#define WCD9XXX_A_RX_HPH_CNP_WG_TIME (0x1AD) +#define WCD9XXX_A_RX_HPH_CNP_WG_TIME__POR (0x2A) +#define WCD9XXX_A_RX_HPH_R_DAC_CTL (0x1B7) +#define WCD9XXX_A_RX_HPH_R_DAC_CTL__POR (0x00) +#define WCD9XXX_A_RX_HPH_L_DAC_CTL (0x1B1) +#define WCD9XXX_A_RX_HPH_L_DAC_CTL__POR (0x00) +#define WCD9XXX_A_TX_7_MBHC_EN (0x171) +#define WCD9XXX_A_TX_7_MBHC_EN__POR (0x0C) +#define WCD9XXX_A_PIN_CTL_OE0 (0x010) +#define WCD9XXX_A_PIN_CTL_OE0__POR (0x00) +#define WCD9XXX_A_PIN_CTL_OE1 (0x011) +#define WCD9XXX_A_PIN_CTL_OE1__POR (0x00) +#define WCD9XXX_A_MICB_CFILT_1_CTL (0x128) +#define WCD9XXX_A_LDO_H_MODE_1 (0x110) +#define WCD9XXX_A_LDO_H_MODE_1__POR (0x65) +#define WCD9XXX_A_MICB_CFILT_1_CTL__POR (0x40) +#define WCD9XXX_A_TX_7_MBHC_TEST_CTL (0x174) +#define WCD9XXX_A_TX_7_MBHC_TEST_CTL__POR (0x38) +#define WCD9XXX_A_MBHC_SCALING_MUX_2 (0x14F) +#define WCD9XXX_A_MBHC_SCALING_MUX_2__POR (0x80) +#define WCD9XXX_A_TX_COM_BIAS (0x14C) +#define WCD9XXX_A_TX_COM_BIAS__POR (0xF0) + +#define WCD9XXX_A_MBHC_INSERT_DETECT (0x14A) /* TAIKO and later */ +#define WCD9XXX_A_MBHC_INSERT_DETECT__POR (0x00) +#define WCD9XXX_A_MBHC_INSERT_DET_STATUS (0x14B) /* TAIKO and later */ +#define WCD9XXX_A_MBHC_INSERT_DET_STATUS__POR (0x00) +#define WCD9XXX_A_MAD_ANA_CTRL (0x150) +#define WCD9XXX_A_MAD_ANA_CTRL__POR (0xF1) + + +#define WCD9XXX_A_CDC_CLK_OTHR_CTL (0x30C) +#define WCD9XXX_A_CDC_CLK_OTHR_CTL__POR (0x00) + +/* Class H related common registers */ +#define WCD9XXX_A_BUCK_MODE_1 (0x181) +#define WCD9XXX_A_BUCK_MODE_1__POR (0x21) +#define WCD9XXX_A_BUCK_MODE_2 (0x182) +#define WCD9XXX_A_BUCK_MODE_2__POR (0xFF) +#define WCD9XXX_A_BUCK_MODE_3 (0x183) +#define WCD9XXX_A_BUCK_MODE_3__POR (0xCC) +#define WCD9XXX_A_BUCK_MODE_4 (0x184) +#define WCD9XXX_A_BUCK_MODE_4__POR (0x3A) +#define WCD9XXX_A_BUCK_MODE_5 (0x185) +#define WCD9XXX_A_BUCK_MODE_5__POR (0x00) +#define WCD9XXX_A_BUCK_CTRL_VCL_1 (0x186) +#define WCD9XXX_A_BUCK_CTRL_VCL_1__POR (0x48) +#define WCD9XXX_A_BUCK_CTRL_VCL_2 (0x187) +#define WCD9XXX_A_BUCK_CTRL_VCL_2__POR (0xA3) +#define WCD9XXX_A_BUCK_CTRL_VCL_3 (0x188) +#define WCD9XXX_A_BUCK_CTRL_VCL_3__POR (0x82) +#define WCD9XXX_A_BUCK_CTRL_CCL_1 (0x189) +#define WCD9XXX_A_BUCK_CTRL_CCL_1__POR (0xAB) +#define WCD9XXX_A_BUCK_CTRL_CCL_2 (0x18A) +#define WCD9XXX_A_BUCK_CTRL_CCL_2__POR (0xDC) +#define WCD9XXX_A_BUCK_CTRL_CCL_3 (0x18B) +#define WCD9XXX_A_BUCK_CTRL_CCL_3__POR (0x6A) +#define WCD9XXX_A_BUCK_CTRL_CCL_4 (0x18C) +#define WCD9XXX_A_BUCK_CTRL_CCL_4__POR (0x58) +#define WCD9XXX_A_BUCK_CTRL_PWM_DRVR_1 (0x18D) +#define WCD9XXX_A_BUCK_CTRL_PWM_DRVR_1__POR (0x50) +#define WCD9XXX_A_BUCK_CTRL_PWM_DRVR_2 (0x18E) +#define WCD9XXX_A_BUCK_CTRL_PWM_DRVR_2__POR (0x64) +#define WCD9XXX_A_BUCK_CTRL_PWM_DRVR_3 (0x18F) +#define WCD9XXX_A_BUCK_CTRL_PWM_DRVR_3__POR (0x77) +#define WCD9XXX_A_BUCK_TMUX_A_D (0x190) +#define WCD9XXX_A_BUCK_TMUX_A_D__POR (0x00) +#define WCD9XXX_A_NCP_EN (0x192) +#define WCD9XXX_A_NCP_EN__POR (0xFE) +#define WCD9XXX_A_NCP_STATIC (0x194) +#define WCD9XXX_A_NCP_STATIC__POR (0x28) +#define WCD9XXX_A_NCP_BUCKREF (0x191) +#define WCD9XXX_A_NCP_BUCKREF__POR (0x00) +#define WCD9XXX_A_CDC_CLSH_B1_CTL (0x320) +#define WCD9XXX_A_CDC_CLSH_B1_CTL__POR (0xE4) +#define WCD9XXX_A_CDC_CLSH_B2_CTL (0x321) +#define WCD9XXX_A_CDC_CLSH_B2_CTL__POR (0x00) +#define WCD9XXX_A_CDC_CLSH_B3_CTL (0x322) +#define WCD9XXX_A_CDC_CLSH_B3_CTL__POR (0x00) +#define WCD9XXX_A_CDC_CLSH_BUCK_NCP_VARS (0x323) +#define WCD9XXX_A_CDC_CLSH_BUCK_NCP_VARS__POR (0x00) +#define WCD9XXX_A_CDC_CLSH_IDLE_HPH_THSD (0x324) +#define WCD9XXX_A_CDC_CLSH_IDLE_HPH_THSD__POR (0x12) +#define WCD9XXX_A_CDC_CLSH_IDLE_EAR_THSD (0x325) +#define WCD9XXX_A_CDC_CLSH_IDLE_EAR_THSD__POR (0x0C) +#define WCD9XXX_A_CDC_CLSH_FCLKONLY_HPH_THSD (0x326) +#define WCD9XXX_A_CDC_CLSH_FCLKONLY_HPH_THSD__POR (0x18) +#define WCD9XXX_A_CDC_CLSH_FCLKONLY_EAR_THSD (0x327) +#define WCD9XXX_A_CDC_CLSH_FCLKONLY_EAR_THSD__POR (0x23) +#define WCD9XXX_A_CDC_CLSH_K_ADDR (0x328) +#define WCD9XXX_A_CDC_CLSH_K_ADDR__POR (0x00) +#define WCD9XXX_A_CDC_CLSH_K_DATA (0x329) +#define WCD9XXX_A_CDC_CLSH_K_DATA__POR (0xA4) +#define WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_L (0x32A) +#define WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_L__POR (0xD7) +#define WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_U (0x32B) +#define WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_U__POR (0x05) +#define WCD9XXX_A_CDC_CLSH_I_PA_FACT_EAR_L (0x32C) +#define WCD9XXX_A_CDC_CLSH_I_PA_FACT_EAR_L__POR (0x60) +#define WCD9XXX_A_CDC_CLSH_I_PA_FACT_EAR_U (0x32D) +#define WCD9XXX_A_CDC_CLSH_I_PA_FACT_EAR_U__POR (0x09) +#define WCD9XXX_A_CDC_CLSH_V_PA_HD_EAR (0x32E) +#define WCD9XXX_A_CDC_CLSH_V_PA_HD_EAR__POR (0x00) +#define WCD9XXX_A_CDC_CLSH_V_PA_HD_HPH (0x32F) +#define WCD9XXX_A_CDC_CLSH_V_PA_HD_HPH__POR (0x00) +#define WCD9XXX_A_CDC_CLSH_V_PA_MIN_EAR (0x330) +#define WCD9XXX_A_CDC_CLSH_V_PA_MIN_EAR__POR (0x00) +#define WCD9XXX_A_CDC_CLSH_V_PA_MIN_HPH (0x331) +#define WCD9XXX_A_CDC_CLSH_V_PA_MIN_HPH__POR (0x00) + +#define WCD9XXX_A_CDC_RX1_B6_CTL (0x2B5) +#define WCD9XXX_A_CDC_RX1_B6_CTL__POR (0x80) +#define WCD9XXX_A_CDC_RX2_B6_CTL (0x2BD) +#define WCD9XXX_A_CDC_RX2_B6_CTL__POR (0x80) +#define WCD9XXX_A_RX_HPH_L_GAIN (0x1AE) +#define WCD9XXX_A_RX_HPH_L_GAIN__POR (0x00) +#define WCD9XXX_A_RX_HPH_R_GAIN (0x1B4) +#define WCD9XXX_A_RX_HPH_R_GAIN__POR (0x00) +#define WCD9XXX_A_RX_HPH_CHOP_CTL (0x1A5) +#define WCD9XXX_A_RX_HPH_CHOP_CTL__POR (0xB4) +#define WCD9XXX_A_RX_HPH_BIAS_PA (0x1A6) +#define WCD9XXX_A_RX_HPH_BIAS_PA__POR (0x7A) +#define WCD9XXX_A_RX_HPH_L_TEST (0x1AF) +#define WCD9XXX_A_RX_HPH_L_TEST__POR (0x00) +#define WCD9XXX_A_RX_HPH_R_TEST (0x1B5) +#define WCD9XXX_A_RX_HPH_R_TEST__POR (0x00) +#define WCD9XXX_A_CDC_CLK_RX_B1_CTL (0x30F) +#define WCD9XXX_A_CDC_CLK_RX_B1_CTL__POR (0x00) +#define WCD9XXX_A_NCP_CLK (0x193) +#define WCD9XXX_A_NCP_CLK__POR (0x94) +#define WCD9XXX_A_RX_HPH_BIAS_WG_OCP (0x1A9) +#define WCD9XXX_A_RX_HPH_BIAS_WG_OCP__POR (0x2A) +#define WCD9XXX_A_RX_HPH_CNP_WG_CTL (0x1AC) +#define WCD9XXX_A_RX_HPH_CNP_WG_CTL__POR (0xDE) +#define WCD9XXX_A_RX_HPH_L_PA_CTL (0x1B0) +#define WCD9XXX_A_RX_HPH_L_PA_CTL__POR (0x42) +#define WCD9XXX_A_RX_HPH_R_PA_CTL (0x1B6) +#define WCD9XXX_A_RX_HPH_R_PA_CTL__POR (0x42) +#define WCD9XXX_A_CDC_CONN_RX2_B1_CTL (0x383) +#define WCD9XXX_A_CDC_CONN_RX2_B1_CTL__POR (0x00) +#define WCD9XXX_A_CDC_PA_RAMP_B1_CTL (0x361) +#define WCD9XXX_A_CDC_PA_RAMP_B1_CTL__POR (0x00) +#define WCD9XXX_A_CDC_PA_RAMP_B2_CTL (0x362) +#define WCD9XXX_A_CDC_PA_RAMP_B2_CTL__POR (0x00) +#define WCD9XXX_A_CDC_PA_RAMP_B3_CTL (0x363) +#define WCD9XXX_A_CDC_PA_RAMP_B3_CTL__POR (0x00) +#define WCD9XXX_A_CDC_PA_RAMP_B4_CTL (0x364) +#define WCD9XXX_A_CDC_PA_RAMP_B4_CTL__POR (0x00) + +#define WCD9330_A_LEAKAGE_CTL (0x03C) +#define WCD9330_A_LEAKAGE_CTL__POR (0x04) +#define WCD9330_A_CDC_CTL (0x034) +#define WCD9330_A_CDC_CTL__POR (0x00) + +/* Class-H registers for codecs from and above WCD9335 */ +#define WCD9XXX_A_CDC_RX0_RX_PATH_CFG0 (0xB42) +#define WCD9XXX_A_CDC_RX1_RX_PATH_CFG0 (0xB56) +#define WCD9XXX_A_CDC_RX2_RX_PATH_CFG0 (0xB6A) +#define WCD9XXX_A_CDC_CLSH_K1_MSB (0xC08) +#define WCD9XXX_A_CDC_CLSH_K1_LSB (0xC09) +#define WCD9XXX_A_ANA_RX_SUPPLIES (0x608) +#define WCD9XXX_A_ANA_HPH (0x609) +#define WCD9XXX_A_CDC_CLSH_CRC (0xC01) +#define WCD9XXX_FLYBACK_EN (0x6A4) +#define WCD9XXX_FLYBACK_VNEG_CTRL_1 (0x6A5) +#define WCD9XXX_FLYBACK_VNEGDAC_CTRL_2 (0x6AF) +#define WCD9XXX_RX_BIAS_FLYB_BUFF (0x6C7) +#define WCD9XXX_HPH_L_EN (0x6D3) +#define WCD9XXX_HPH_R_EN (0x6D6) +#define WCD9XXX_HPH_REFBUFF_UHQA_CTL (0x6DD) +#define WCD9XXX_CLASSH_CTRL_VCL_2 (0x69B) +#define WCD9XXX_CDC_CLSH_HPH_V_PA (0xC04) +#define WCD9XXX_CDC_RX0_RX_PATH_SEC0 (0xB49) +#define WCD9XXX_CDC_RX1_RX_PATH_CTL (0xB55) +#define WCD9XXX_CDC_RX2_RX_PATH_CTL (0xB69) +#define WCD9XXX_CDC_CLK_RST_CTRL_MCLK_CONTROL (0xD41) +#define WCD9XXX_CLASSH_CTRL_CCL_1 (0x69C) + +/* RX Gain control registers of codecs from and above WCD9335 */ +#define WCD9XXX_CDC_RX1_RX_VOL_CTL (0xB59) +#define WCD9XXX_CDC_RX1_RX_VOL_MIX_CTL (0xB5C) +#define WCD9XXX_CDC_RX1_RX_PATH_SEC1 (0xB5E) +#define WCD9XXX_CDC_RX2_RX_VOL_CTL (0xB6D) +#define WCD9XXX_CDC_RX2_RX_VOL_MIX_CTL (0xB70) +#define WCD9XXX_CDC_RX2_RX_PATH_SEC1 (0xB72) + +/* Class-H registers for codecs from and above WCD934X */ +#define WCD9XXX_HPH_CNP_WG_CTL (0x06cc) +#define WCD9XXX_FLYBACK_VNEG_CTRL_4 (0x06a8) +#define WCD9XXX_HPH_NEW_INT_PA_MISC2 (0x0738) +#define WCD9XXX_RX_BIAS_HPH_LOWPOWER (0x06bf) +#define WCD9XXX_HPH_PA_CTL1 (0x06d1) +#endif diff --git a/audio-kernel/include/uapi/linux/msm_audio.h b/audio-kernel/include/uapi/linux/msm_audio.h new file mode 100644 index 000000000..3213d0084 --- /dev/null +++ b/audio-kernel/include/uapi/linux/msm_audio.h @@ -0,0 +1,475 @@ +/* include/linux/msm_audio.h + * + * Copyright (C) 2008 Google, Inc. + * Copyright (c) 2012, 2014, 2017 The Linux Foundation. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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 _UAPI_LINUX_MSM_AUDIO_H +#define _UAPI_LINUX_MSM_AUDIO_H + +#include +#include + +/* PCM Audio */ + +#define AUDIO_IOCTL_MAGIC 'a' + +#define AUDIO_START _IOW(AUDIO_IOCTL_MAGIC, 0, unsigned int) +#define AUDIO_STOP _IOW(AUDIO_IOCTL_MAGIC, 1, unsigned int) +#define AUDIO_FLUSH _IOW(AUDIO_IOCTL_MAGIC, 2, unsigned int) +#define AUDIO_GET_CONFIG _IOR(AUDIO_IOCTL_MAGIC, 3, \ + struct msm_audio_config) +#define AUDIO_SET_CONFIG _IOW(AUDIO_IOCTL_MAGIC, 4, \ + struct msm_audio_config) +#define AUDIO_GET_STATS _IOR(AUDIO_IOCTL_MAGIC, 5, \ + struct msm_audio_stats) +#define AUDIO_ENABLE_AUDPP _IOW(AUDIO_IOCTL_MAGIC, 6, unsigned int) +#define AUDIO_SET_ADRC _IOW(AUDIO_IOCTL_MAGIC, 7, unsigned int) +#define AUDIO_SET_EQ _IOW(AUDIO_IOCTL_MAGIC, 8, unsigned int) +#define AUDIO_SET_RX_IIR _IOW(AUDIO_IOCTL_MAGIC, 9, unsigned int) +#define AUDIO_SET_VOLUME _IOW(AUDIO_IOCTL_MAGIC, 10, unsigned int) +#define AUDIO_PAUSE _IOW(AUDIO_IOCTL_MAGIC, 11, unsigned int) +#define AUDIO_PLAY_DTMF _IOW(AUDIO_IOCTL_MAGIC, 12, unsigned int) +#define AUDIO_GET_EVENT _IOR(AUDIO_IOCTL_MAGIC, 13, \ + struct msm_audio_event) +#define AUDIO_ABORT_GET_EVENT _IOW(AUDIO_IOCTL_MAGIC, 14, unsigned int) +#define AUDIO_REGISTER_PMEM _IOW(AUDIO_IOCTL_MAGIC, 15, unsigned int) +#define AUDIO_DEREGISTER_PMEM _IOW(AUDIO_IOCTL_MAGIC, 16, unsigned int) +#define AUDIO_ASYNC_WRITE _IOW(AUDIO_IOCTL_MAGIC, 17, \ + struct msm_audio_aio_buf) +#define AUDIO_ASYNC_READ _IOW(AUDIO_IOCTL_MAGIC, 18, \ + struct msm_audio_aio_buf) +#define AUDIO_SET_INCALL _IOW(AUDIO_IOCTL_MAGIC, 19, struct msm_voicerec_mode) +#define AUDIO_GET_NUM_SND_DEVICE _IOR(AUDIO_IOCTL_MAGIC, 20, unsigned int) +#define AUDIO_GET_SND_DEVICES _IOWR(AUDIO_IOCTL_MAGIC, 21, \ + struct msm_snd_device_list) +#define AUDIO_ENABLE_SND_DEVICE _IOW(AUDIO_IOCTL_MAGIC, 22, unsigned int) +#define AUDIO_DISABLE_SND_DEVICE _IOW(AUDIO_IOCTL_MAGIC, 23, unsigned int) +#define AUDIO_ROUTE_STREAM _IOW(AUDIO_IOCTL_MAGIC, 24, \ + struct msm_audio_route_config) +#define AUDIO_GET_PCM_CONFIG _IOR(AUDIO_IOCTL_MAGIC, 30, unsigned int) +#define AUDIO_SET_PCM_CONFIG _IOW(AUDIO_IOCTL_MAGIC, 31, unsigned int) +#define AUDIO_SWITCH_DEVICE _IOW(AUDIO_IOCTL_MAGIC, 32, unsigned int) +#define AUDIO_SET_MUTE _IOW(AUDIO_IOCTL_MAGIC, 33, unsigned int) +#define AUDIO_UPDATE_ACDB _IOW(AUDIO_IOCTL_MAGIC, 34, unsigned int) +#define AUDIO_START_VOICE _IOW(AUDIO_IOCTL_MAGIC, 35, unsigned int) +#define AUDIO_STOP_VOICE _IOW(AUDIO_IOCTL_MAGIC, 36, unsigned int) +#define AUDIO_REINIT_ACDB _IOW(AUDIO_IOCTL_MAGIC, 39, unsigned int) +#define AUDIO_OUTPORT_FLUSH _IOW(AUDIO_IOCTL_MAGIC, 40, unsigned short) +#define AUDIO_SET_ERR_THRESHOLD_VALUE _IOW(AUDIO_IOCTL_MAGIC, 41, \ + unsigned short) +#define AUDIO_GET_BITSTREAM_ERROR_INFO _IOR(AUDIO_IOCTL_MAGIC, 42, \ + struct msm_audio_bitstream_error_info) + +#define AUDIO_SET_SRS_TRUMEDIA_PARAM _IOW(AUDIO_IOCTL_MAGIC, 43, unsigned int) + +/* Qualcomm technologies inc extensions */ +#define AUDIO_SET_STREAM_CONFIG _IOW(AUDIO_IOCTL_MAGIC, 80, \ + struct msm_audio_stream_config) +#define AUDIO_GET_STREAM_CONFIG _IOR(AUDIO_IOCTL_MAGIC, 81, \ + struct msm_audio_stream_config) +#define AUDIO_GET_SESSION_ID _IOR(AUDIO_IOCTL_MAGIC, 82, unsigned short) +#define AUDIO_GET_STREAM_INFO _IOR(AUDIO_IOCTL_MAGIC, 83, \ + struct msm_audio_bitstream_info) +#define AUDIO_SET_PAN _IOW(AUDIO_IOCTL_MAGIC, 84, unsigned int) +#define AUDIO_SET_QCONCERT_PLUS _IOW(AUDIO_IOCTL_MAGIC, 85, unsigned int) +#define AUDIO_SET_MBADRC _IOW(AUDIO_IOCTL_MAGIC, 86, unsigned int) +#define AUDIO_SET_VOLUME_PATH _IOW(AUDIO_IOCTL_MAGIC, 87, \ + struct msm_vol_info) +#define AUDIO_SET_MAX_VOL_ALL _IOW(AUDIO_IOCTL_MAGIC, 88, unsigned int) +#define AUDIO_ENABLE_AUDPRE _IOW(AUDIO_IOCTL_MAGIC, 89, unsigned int) +#define AUDIO_SET_AGC _IOW(AUDIO_IOCTL_MAGIC, 90, unsigned int) +#define AUDIO_SET_NS _IOW(AUDIO_IOCTL_MAGIC, 91, unsigned int) +#define AUDIO_SET_TX_IIR _IOW(AUDIO_IOCTL_MAGIC, 92, unsigned int) +#define AUDIO_GET_BUF_CFG _IOW(AUDIO_IOCTL_MAGIC, 93, \ + struct msm_audio_buf_cfg) +#define AUDIO_SET_BUF_CFG _IOW(AUDIO_IOCTL_MAGIC, 94, \ + struct msm_audio_buf_cfg) +#define AUDIO_SET_ACDB_BLK _IOW(AUDIO_IOCTL_MAGIC, 95, \ + struct msm_acdb_cmd_device) +#define AUDIO_GET_ACDB_BLK _IOW(AUDIO_IOCTL_MAGIC, 96, \ + struct msm_acdb_cmd_device) + +#define AUDIO_REGISTER_ION _IOW(AUDIO_IOCTL_MAGIC, 97, \ + struct msm_audio_ion_info) +#define AUDIO_DEREGISTER_ION _IOW(AUDIO_IOCTL_MAGIC, 98, \ + struct msm_audio_ion_info) +#define AUDIO_SET_EFFECTS_CONFIG _IOW(AUDIO_IOCTL_MAGIC, 99, \ + struct msm_hwacc_effects_config) +#define AUDIO_EFFECTS_SET_BUF_LEN _IOW(AUDIO_IOCTL_MAGIC, 100, \ + struct msm_hwacc_buf_cfg) +#define AUDIO_EFFECTS_GET_BUF_AVAIL _IOW(AUDIO_IOCTL_MAGIC, 101, \ + struct msm_hwacc_buf_avail) +#define AUDIO_EFFECTS_WRITE _IOW(AUDIO_IOCTL_MAGIC, 102, void *) +#define AUDIO_EFFECTS_READ _IOWR(AUDIO_IOCTL_MAGIC, 103, void *) +#define AUDIO_EFFECTS_SET_PP_PARAMS _IOW(AUDIO_IOCTL_MAGIC, 104, void *) + +#define AUDIO_PM_AWAKE _IOW(AUDIO_IOCTL_MAGIC, 105, unsigned int) +#define AUDIO_PM_RELAX _IOW(AUDIO_IOCTL_MAGIC, 106, unsigned int) + +#define AUDIO_MAX_COMMON_IOCTL_NUM 107 + + +#define HANDSET_MIC 0x01 +#define HANDSET_SPKR 0x02 +#define HEADSET_MIC 0x03 +#define HEADSET_SPKR_MONO 0x04 +#define HEADSET_SPKR_STEREO 0x05 +#define SPKR_PHONE_MIC 0x06 +#define SPKR_PHONE_MONO 0x07 +#define SPKR_PHONE_STEREO 0x08 +#define BT_SCO_MIC 0x09 +#define BT_SCO_SPKR 0x0A +#define BT_A2DP_SPKR 0x0B +#define TTY_HEADSET_MIC 0x0C +#define TTY_HEADSET_SPKR 0x0D + +/* Default devices are not supported in a */ +/* device switching context. Only supported */ +/* for stream devices. */ +/* DO NOT USE */ +#define DEFAULT_TX 0x0E +#define DEFAULT_RX 0x0F + +#define BT_A2DP_TX 0x10 + +#define HEADSET_MONO_PLUS_SPKR_MONO_RX 0x11 +#define HEADSET_MONO_PLUS_SPKR_STEREO_RX 0x12 +#define HEADSET_STEREO_PLUS_SPKR_MONO_RX 0x13 +#define HEADSET_STEREO_PLUS_SPKR_STEREO_RX 0x14 + +#define I2S_RX 0x20 +#define I2S_TX 0x21 + +#define ADRC_ENABLE 0x0001 +#define EQUALIZER_ENABLE 0x0002 +#define IIR_ENABLE 0x0004 +#define QCONCERT_PLUS_ENABLE 0x0008 +#define MBADRC_ENABLE 0x0010 +#define SRS_ENABLE 0x0020 +#define SRS_DISABLE 0x0040 + +#define AGC_ENABLE 0x0001 +#define NS_ENABLE 0x0002 +#define TX_IIR_ENABLE 0x0004 +#define FLUENCE_ENABLE 0x0008 + +#define VOC_REC_UPLINK 0x00 +#define VOC_REC_DOWNLINK 0x01 +#define VOC_REC_BOTH 0x02 + +struct msm_audio_config { + uint32_t buffer_size; + uint32_t buffer_count; + uint32_t channel_count; + uint32_t sample_rate; + uint32_t type; + uint32_t meta_field; + uint32_t bits; + uint32_t unused[3]; +}; + +struct msm_audio_stream_config { + uint32_t buffer_size; + uint32_t buffer_count; +}; + +struct msm_audio_buf_cfg { + uint32_t meta_info_enable; + uint32_t frames_per_buf; +}; + +struct msm_audio_stats { + uint32_t byte_count; + uint32_t sample_count; + uint32_t unused[2]; +}; + +struct msm_audio_ion_info { + int fd; + void *vaddr; +}; + +struct msm_audio_pmem_info { + int fd; + void *vaddr; +}; + +struct msm_audio_aio_buf { + void *buf_addr; + uint32_t buf_len; + uint32_t data_len; + void *private_data; + unsigned short mfield_sz; /*only useful for data has meta field */ +}; + +/* Audio routing */ + +#define SND_IOCTL_MAGIC 's' + +#define SND_MUTE_UNMUTED 0 +#define SND_MUTE_MUTED 1 + +struct msm_mute_info { + uint32_t mute; + uint32_t path; +}; + +struct msm_vol_info { + uint32_t vol; + uint32_t path; +}; + +struct msm_voicerec_mode { + uint32_t rec_mode; +}; + +struct msm_snd_device_config { + uint32_t device; + uint32_t ear_mute; + uint32_t mic_mute; +}; + +#define SND_SET_DEVICE _IOW(SND_IOCTL_MAGIC, 2, struct msm_device_config *) + +enum cad_device_path_type { + CAD_DEVICE_PATH_RX, /*For Decoding session*/ + CAD_DEVICE_PATH_TX, /* For Encoding session*/ + CAD_DEVICE_PATH_RX_TX, /* For Voice call */ + CAD_DEVICE_PATH_LB, /* For loopback (FM Analog)*/ + CAD_DEVICE_PATH_MAX +}; + +struct cad_devices_type { + uint32_t rx_device; + uint32_t tx_device; + enum cad_device_path_type pathtype; +}; + +struct msm_cad_device_config { + struct cad_devices_type device; + uint32_t ear_mute; + uint32_t mic_mute; +}; + +#define CAD_SET_DEVICE _IOW(SND_IOCTL_MAGIC, 2, struct msm_cad_device_config *) + +#define SND_METHOD_VOICE 0 +#define SND_METHOD_MIDI 4 + +struct msm_snd_volume_config { + uint32_t device; + uint32_t method; + uint32_t volume; +}; + +#define SND_SET_VOLUME _IOW(SND_IOCTL_MAGIC, 3, struct msm_snd_volume_config *) + +struct msm_cad_volume_config { + struct cad_devices_type device; + uint32_t method; + uint32_t volume; +}; + +#define CAD_SET_VOLUME _IOW(SND_IOCTL_MAGIC, 3, struct msm_cad_volume_config *) + +/* Returns the number of SND endpoints supported. */ + +#define SND_GET_NUM_ENDPOINTS _IOR(SND_IOCTL_MAGIC, 4, unsigned int *) + +struct msm_snd_endpoint { + int id; /* input and output */ + char name[64]; /* output only */ +}; + +/* Takes an index between 0 and one less than the number returned by + * SND_GET_NUM_ENDPOINTS, and returns the SND index and name of a + * SND endpoint. On input, the .id field contains the number of the + * endpoint, and on exit it contains the SND index, while .name contains + * the description of the endpoint. + */ + +#define SND_GET_ENDPOINT _IOWR(SND_IOCTL_MAGIC, 5, struct msm_snd_endpoint *) + + +#define SND_AVC_CTL _IOW(SND_IOCTL_MAGIC, 6, unsigned int *) +#define SND_AGC_CTL _IOW(SND_IOCTL_MAGIC, 7, unsigned int *) + +/*return the number of CAD endpoints supported. */ + +#define CAD_GET_NUM_ENDPOINTS _IOR(SND_IOCTL_MAGIC, 4, unsigned int *) + +struct msm_cad_endpoint { + int id; /* input and output */ + char name[64]; /* output only */ +}; + +/* Takes an index between 0 and one less than the number returned by + * SND_GET_NUM_ENDPOINTS, and returns the CAD index and name of a + * CAD endpoint. On input, the .id field contains the number of the + * endpoint, and on exit it contains the SND index, while .name contains + * the description of the endpoint. + */ + +#define CAD_GET_ENDPOINT _IOWR(SND_IOCTL_MAGIC, 5, struct msm_cad_endpoint *) + +struct msm_audio_pcm_config { + uint32_t pcm_feedback; /* 0 - disable > 0 - enable */ + uint32_t buffer_count; /* Number of buffers to allocate */ + uint32_t buffer_size; /* Size of buffer for capturing of + * PCM samples + */ +}; + +#define AUDIO_EVENT_SUSPEND 0 +#define AUDIO_EVENT_RESUME 1 +#define AUDIO_EVENT_WRITE_DONE 2 +#define AUDIO_EVENT_READ_DONE 3 +#define AUDIO_EVENT_STREAM_INFO 4 +#define AUDIO_EVENT_BITSTREAM_ERROR_INFO 5 + +#define AUDIO_CODEC_TYPE_MP3 0 +#define AUDIO_CODEC_TYPE_AAC 1 + +struct msm_audio_bitstream_info { + uint32_t codec_type; + uint32_t chan_info; + uint32_t sample_rate; + uint32_t bit_stream_info; + uint32_t bit_rate; + uint32_t unused[3]; +}; + +struct msm_audio_bitstream_error_info { + uint32_t dec_id; + uint32_t err_msg_indicator; + uint32_t err_type; +}; + +union msm_audio_event_payload { + struct msm_audio_aio_buf aio_buf; + struct msm_audio_bitstream_info stream_info; + struct msm_audio_bitstream_error_info error_info; + int reserved; +}; + +struct msm_audio_event { + int event_type; + int timeout_ms; + union msm_audio_event_payload event_payload; +}; + +#define MSM_SNDDEV_CAP_RX 0x1 +#define MSM_SNDDEV_CAP_TX 0x2 +#define MSM_SNDDEV_CAP_VOICE 0x4 + +struct msm_snd_device_info { + uint32_t dev_id; + uint32_t dev_cap; /* bitmask describe capability of device */ + char dev_name[64]; +}; + +struct msm_snd_device_list { + uint32_t num_dev; /* Indicate number of device info to be retrieved */ + struct msm_snd_device_info *list; +}; + +struct msm_dtmf_config { + uint16_t path; + uint16_t dtmf_hi; + uint16_t dtmf_low; + uint16_t duration; + uint16_t tx_gain; + uint16_t rx_gain; + uint16_t mixing; +}; + +#define AUDIO_ROUTE_STREAM_VOICE_RX 0 +#define AUDIO_ROUTE_STREAM_VOICE_TX 1 +#define AUDIO_ROUTE_STREAM_PLAYBACK 2 +#define AUDIO_ROUTE_STREAM_REC 3 + +struct msm_audio_route_config { + uint32_t stream_type; + uint32_t stream_id; + uint32_t dev_id; +}; + +#define AUDIO_MAX_EQ_BANDS 12 + +struct msm_audio_eq_band { + uint16_t band_idx; /* The band index, 0 .. 11 */ + uint32_t filter_type; /* Filter band type */ + uint32_t center_freq_hz; /* Filter band center frequency */ + uint32_t filter_gain; /* Filter band initial gain (dB) */ + /* Range is +12 dB to -12 dB with 1dB increments. */ + uint32_t q_factor; +} __attribute__ ((packed)); + +struct msm_audio_eq_stream_config { + uint32_t enable; /* Number of consequtive bands specified */ + uint32_t num_bands; + struct msm_audio_eq_band eq_bands[AUDIO_MAX_EQ_BANDS]; +} __attribute__ ((packed)); + +struct msm_acdb_cmd_device { + uint32_t command_id; + uint32_t device_id; + uint32_t network_id; + uint32_t sample_rate_id; /* Actual sample rate value */ + uint32_t interface_id; /* See interface id's above */ + uint32_t algorithm_block_id; /* See enumerations above */ + uint32_t total_bytes; /* Length in bytes used by buffer */ + uint32_t *phys_buf; /* Physical Address of data */ +}; + +struct msm_hwacc_data_config { + __u32 buf_size; + __u32 num_buf; + __u32 num_channels; + __u8 channel_map[8]; + __u32 sample_rate; + __u32 bits_per_sample; +}; + +struct msm_hwacc_buf_cfg { + __u32 input_len; + __u32 output_len; +}; + +struct msm_hwacc_buf_avail { + __u32 input_num_avail; + __u32 output_num_avail; +}; + +struct msm_hwacc_effects_config { + struct msm_hwacc_data_config input; + struct msm_hwacc_data_config output; + struct msm_hwacc_buf_cfg buf_cfg; + __u32 meta_mode_enabled; + __u32 overwrite_topology; + __s32 topology; +}; + +#define ADSP_STREAM_PP_EVENT 0 +#define ADSP_STREAM_ENCDEC_EVENT 1 +#define ADSP_STREAM_IEC_61937_FMT_UPDATE_EVENT 2 +#define ADSP_STREAM_EVENT_MAX 3 + +struct msm_adsp_event_data { + __u32 event_type; + __u32 payload_len; + __u8 payload[0]; +}; + +#endif diff --git a/audio-kernel/include/uapi/linux/msm_audio_aac.h b/audio-kernel/include/uapi/linux/msm_audio_aac.h new file mode 100644 index 000000000..7e1e1b724 --- /dev/null +++ b/audio-kernel/include/uapi/linux/msm_audio_aac.h @@ -0,0 +1,76 @@ +#ifndef _UAPI_MSM_AUDIO_AAC_H +#define _UAPI_MSM_AUDIO_AAC_H + +#include + +#define AUDIO_SET_AAC_CONFIG _IOW(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_COMMON_IOCTL_NUM+0), struct msm_audio_aac_config) +#define AUDIO_GET_AAC_CONFIG _IOR(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_COMMON_IOCTL_NUM+1), struct msm_audio_aac_config) + +#define AUDIO_SET_AAC_ENC_CONFIG _IOW(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_COMMON_IOCTL_NUM+3), struct msm_audio_aac_enc_config) + +#define AUDIO_GET_AAC_ENC_CONFIG _IOR(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_COMMON_IOCTL_NUM+4), struct msm_audio_aac_enc_config) + +#define AUDIO_SET_AAC_MIX_CONFIG _IOR(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_COMMON_IOCTL_NUM+5), uint32_t) + +#define AUDIO_AAC_FORMAT_ADTS -1 +#define AUDIO_AAC_FORMAT_RAW 0x0000 +#define AUDIO_AAC_FORMAT_PSUEDO_RAW 0x0001 +#define AUDIO_AAC_FORMAT_LOAS 0x0002 +#define AUDIO_AAC_FORMAT_ADIF 0x0003 + +#define AUDIO_AAC_OBJECT_LC 0x0002 +#define AUDIO_AAC_OBJECT_LTP 0x0004 +#define AUDIO_AAC_OBJECT_ERLC 0x0011 +#define AUDIO_AAC_OBJECT_BSAC 0x0016 + +#define AUDIO_AAC_SEC_DATA_RES_ON 0x0001 +#define AUDIO_AAC_SEC_DATA_RES_OFF 0x0000 + +#define AUDIO_AAC_SCA_DATA_RES_ON 0x0001 +#define AUDIO_AAC_SCA_DATA_RES_OFF 0x0000 + +#define AUDIO_AAC_SPEC_DATA_RES_ON 0x0001 +#define AUDIO_AAC_SPEC_DATA_RES_OFF 0x0000 + +#define AUDIO_AAC_SBR_ON_FLAG_ON 0x0001 +#define AUDIO_AAC_SBR_ON_FLAG_OFF 0x0000 + +#define AUDIO_AAC_SBR_PS_ON_FLAG_ON 0x0001 +#define AUDIO_AAC_SBR_PS_ON_FLAG_OFF 0x0000 + +/* Primary channel on both left and right channels */ +#define AUDIO_AAC_DUAL_MONO_PL_PR 0 +/* Secondary channel on both left and right channels */ +#define AUDIO_AAC_DUAL_MONO_SL_SR 1 +/* Primary channel on right channel and 2nd on left channel */ +#define AUDIO_AAC_DUAL_MONO_SL_PR 2 +/* 2nd channel on right channel and primary on left channel */ +#define AUDIO_AAC_DUAL_MONO_PL_SR 3 + +struct msm_audio_aac_config { + signed short format; + unsigned short audio_object; + unsigned short ep_config; /* 0 ~ 3 useful only obj = ERLC */ + unsigned short aac_section_data_resilience_flag; + unsigned short aac_scalefactor_data_resilience_flag; + unsigned short aac_spectral_data_resilience_flag; + unsigned short sbr_on_flag; + unsigned short sbr_ps_on_flag; + unsigned short dual_mono_mode; + unsigned short channel_configuration; + unsigned short sample_rate; +}; + +struct msm_audio_aac_enc_config { + uint32_t channels; + uint32_t sample_rate; + uint32_t bit_rate; + uint32_t stream_format; +}; + +#endif /* _UAPI_MSM_AUDIO_AAC_H */ diff --git a/audio-kernel/include/uapi/linux/msm_audio_ac3.h b/audio-kernel/include/uapi/linux/msm_audio_ac3.h new file mode 100644 index 000000000..1df6e6949 --- /dev/null +++ b/audio-kernel/include/uapi/linux/msm_audio_ac3.h @@ -0,0 +1,41 @@ +#ifndef _UAPI_MSM_AUDIO_AC3_H +#define _UAPI_MSM_AUDIO_AC3_H + +#include + +#define AUDIO_SET_AC3_CONFIG _IOW(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_COMMON_IOCTL_NUM+0), unsigned int) +#define AUDIO_GET_AC3_CONFIG _IOR(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_COMMON_IOCTL_NUM+1), unsigned int) + +#define AUDAC3_DEF_WORDSIZE 0 +#define AUDAC3_DEF_USER_DOWNMIX_FLAG 0x0 +#define AUDAC3_DEF_USER_KARAOKE_FLAG 0x0 +#define AUDAC3_DEF_ERROR_CONCEALMENT 0 +#define AUDAC3_DEF_MAX_REPEAT_COUNT 0 + +struct msm_audio_ac3_config { + unsigned short numChans; + unsigned short wordSize; + unsigned short kCapableMode; + unsigned short compMode; + unsigned short outLfeOn; + unsigned short outputMode; + unsigned short stereoMode; + unsigned short dualMonoMode; + unsigned short fsCod; + unsigned short pcmScaleFac; + unsigned short dynRngScaleHi; + unsigned short dynRngScaleLow; + unsigned short user_downmix_flag; + unsigned short user_karaoke_flag; + unsigned short dm_address_high; + unsigned short dm_address_low; + unsigned short ko_address_high; + unsigned short ko_address_low; + unsigned short error_concealment; + unsigned short max_rep_count; + unsigned short channel_routing_mode[6]; +}; + +#endif /* _UAPI_MSM_AUDIO_AC3_H */ diff --git a/audio-kernel/include/uapi/linux/msm_audio_alac.h b/audio-kernel/include/uapi/linux/msm_audio_alac.h new file mode 100644 index 000000000..5476e96d0 --- /dev/null +++ b/audio-kernel/include/uapi/linux/msm_audio_alac.h @@ -0,0 +1,24 @@ +#ifndef _UAPI_MSM_AUDIO_ALAC_H +#define _UAPI_MSM_AUDIO_ALAC_H + +#define AUDIO_GET_ALAC_CONFIG _IOR(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_COMMON_IOCTL_NUM+0), struct msm_audio_alac_config) +#define AUDIO_SET_ALAC_CONFIG _IOW(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_COMMON_IOCTL_NUM+1), struct msm_audio_alac_config) + +struct msm_audio_alac_config { + uint32_t frameLength; + uint8_t compatVersion; + uint8_t bitDepth; + uint8_t pb; /* currently unused */ + uint8_t mb; /* currently unused */ + uint8_t kb; /* currently unused */ + uint8_t channelCount; + uint16_t maxRun; /* currently unused */ + uint32_t maxSize; + uint32_t averageBitRate; + uint32_t sampleRate; + uint32_t channelLayout; +}; + +#endif /* _UAPI_MSM_AUDIO_ALAC_H */ diff --git a/audio-kernel/include/uapi/linux/msm_audio_amrnb.h b/audio-kernel/include/uapi/linux/msm_audio_amrnb.h new file mode 100644 index 000000000..619f928f7 --- /dev/null +++ b/audio-kernel/include/uapi/linux/msm_audio_amrnb.h @@ -0,0 +1,34 @@ +#ifndef _UAPI_MSM_AUDIO_AMRNB_H +#define _UAPI_MSM_AUDIO_AMRNB_H + +#include + +#define AUDIO_GET_AMRNB_ENC_CONFIG _IOW(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_COMMON_IOCTL_NUM+0), unsigned int) +#define AUDIO_SET_AMRNB_ENC_CONFIG _IOR(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_COMMON_IOCTL_NUM+1), unsigned int) +#define AUDIO_GET_AMRNB_ENC_CONFIG_V2 _IOW(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_COMMON_IOCTL_NUM+2), \ + struct msm_audio_amrnb_enc_config_v2) +#define AUDIO_SET_AMRNB_ENC_CONFIG_V2 _IOR(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_COMMON_IOCTL_NUM+3), \ + struct msm_audio_amrnb_enc_config_v2) + +struct msm_audio_amrnb_enc_config { + unsigned short voicememoencweight1; + unsigned short voicememoencweight2; + unsigned short voicememoencweight3; + unsigned short voicememoencweight4; + unsigned short dtx_mode_enable; /* 0xFFFF - enable, 0- disable */ + unsigned short test_mode_enable; /* 0xFFFF - enable, 0- disable */ + unsigned short enc_mode; /* 0-MR475,1-MR515,2-MR59,3-MR67,4-MR74 + * 5-MR795, 6- MR102, 7- MR122(default) + */ +}; + +struct msm_audio_amrnb_enc_config_v2 { + uint32_t band_mode; + uint32_t dtx_enable; + uint32_t frame_format; +}; +#endif /* _UAPI_MSM_AUDIO_AMRNB_H */ diff --git a/audio-kernel/include/uapi/linux/msm_audio_amrwb.h b/audio-kernel/include/uapi/linux/msm_audio_amrwb.h new file mode 100644 index 000000000..512403899 --- /dev/null +++ b/audio-kernel/include/uapi/linux/msm_audio_amrwb.h @@ -0,0 +1,18 @@ +#ifndef _UAPI_MSM_AUDIO_AMRWB_H +#define _UAPI_MSM_AUDIO_AMRWB_H + +#include + +#define AUDIO_GET_AMRWB_ENC_CONFIG _IOW(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_COMMON_IOCTL_NUM+0), \ + struct msm_audio_amrwb_enc_config) +#define AUDIO_SET_AMRWB_ENC_CONFIG _IOR(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_COMMON_IOCTL_NUM+1), \ + struct msm_audio_amrwb_enc_config) + +struct msm_audio_amrwb_enc_config { + uint32_t band_mode; + uint32_t dtx_enable; + uint32_t frame_format; +}; +#endif /* _UAPI_MSM_AUDIO_AMRWB_H */ diff --git a/audio-kernel/include/uapi/linux/msm_audio_amrwbplus.h b/audio-kernel/include/uapi/linux/msm_audio_amrwbplus.h new file mode 100644 index 000000000..ba2d06e99 --- /dev/null +++ b/audio-kernel/include/uapi/linux/msm_audio_amrwbplus.h @@ -0,0 +1,18 @@ +#ifndef _UAPI_MSM_AUDIO_AMR_WB_PLUS_H +#define _UAPI_MSM_AUDIO_AMR_WB_PLUS_H + +#define AUDIO_GET_AMRWBPLUS_CONFIG_V2 _IOR(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_COMMON_IOCTL_NUM+2), struct msm_audio_amrwbplus_config_v2) +#define AUDIO_SET_AMRWBPLUS_CONFIG_V2 _IOW(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_COMMON_IOCTL_NUM+3), struct msm_audio_amrwbplus_config_v2) + +struct msm_audio_amrwbplus_config_v2 { + unsigned int size_bytes; + unsigned int version; + unsigned int num_channels; + unsigned int amr_band_mode; + unsigned int amr_dtx_mode; + unsigned int amr_frame_fmt; + unsigned int amr_lsf_idx; +}; +#endif /* _UAPI_MSM_AUDIO_AMR_WB_PLUS_H */ diff --git a/audio-kernel/include/uapi/linux/msm_audio_ape.h b/audio-kernel/include/uapi/linux/msm_audio_ape.h new file mode 100644 index 000000000..587d3bc18 --- /dev/null +++ b/audio-kernel/include/uapi/linux/msm_audio_ape.h @@ -0,0 +1,26 @@ +/* The following structure has been taken + * from Monkey's Audio SDK with permission + */ + +#ifndef _UAPI_MSM_AUDIO_APE_H +#define _UAPI_MSM_AUDIO_APE_H + +#define AUDIO_GET_APE_CONFIG _IOR(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_COMMON_IOCTL_NUM+0), struct msm_audio_ape_config) +#define AUDIO_SET_APE_CONFIG _IOW(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_COMMON_IOCTL_NUM+1), struct msm_audio_ape_config) + +struct msm_audio_ape_config { + uint16_t compatibleVersion; + uint16_t compressionLevel; + uint32_t formatFlags; + uint32_t blocksPerFrame; + uint32_t finalFrameBlocks; + uint32_t totalFrames; + uint16_t bitsPerSample; + uint16_t numChannels; + uint32_t sampleRate; + uint32_t seekTablePresent; +}; + +#endif /* _UAPI_MSM_AUDIO_APE_H */ diff --git a/audio-kernel/include/uapi/linux/msm_audio_calibration.h b/audio-kernel/include/uapi/linux/msm_audio_calibration.h new file mode 100644 index 000000000..35b596062 --- /dev/null +++ b/audio-kernel/include/uapi/linux/msm_audio_calibration.h @@ -0,0 +1,808 @@ +#ifndef _UAPI_MSM_AUDIO_CALIBRATION_H +#define _UAPI_MSM_AUDIO_CALIBRATION_H + +#include +#include + +#define CAL_IOCTL_MAGIC 'a' + +#define AUDIO_ALLOCATE_CALIBRATION _IOWR(CAL_IOCTL_MAGIC, \ + 200, void *) +#define AUDIO_DEALLOCATE_CALIBRATION _IOWR(CAL_IOCTL_MAGIC, \ + 201, void *) +#define AUDIO_PREPARE_CALIBRATION _IOWR(CAL_IOCTL_MAGIC, \ + 202, void *) +#define AUDIO_SET_CALIBRATION _IOWR(CAL_IOCTL_MAGIC, \ + 203, void *) +#define AUDIO_GET_CALIBRATION _IOWR(CAL_IOCTL_MAGIC, \ + 204, void *) +#define AUDIO_POST_CALIBRATION _IOWR(CAL_IOCTL_MAGIC, \ + 205, void *) + +/* For Real-Time Audio Calibration */ +#define AUDIO_GET_RTAC_ADM_INFO _IOR(CAL_IOCTL_MAGIC, \ + 207, void *) +#define AUDIO_GET_RTAC_VOICE_INFO _IOR(CAL_IOCTL_MAGIC, \ + 208, void *) +#define AUDIO_GET_RTAC_ADM_CAL _IOWR(CAL_IOCTL_MAGIC, \ + 209, void *) +#define AUDIO_SET_RTAC_ADM_CAL _IOWR(CAL_IOCTL_MAGIC, \ + 210, void *) +#define AUDIO_GET_RTAC_ASM_CAL _IOWR(CAL_IOCTL_MAGIC, \ + 211, void *) +#define AUDIO_SET_RTAC_ASM_CAL _IOWR(CAL_IOCTL_MAGIC, \ + 212, void *) +#define AUDIO_GET_RTAC_CVS_CAL _IOWR(CAL_IOCTL_MAGIC, \ + 213, void *) +#define AUDIO_SET_RTAC_CVS_CAL _IOWR(CAL_IOCTL_MAGIC, \ + 214, void *) +#define AUDIO_GET_RTAC_CVP_CAL _IOWR(CAL_IOCTL_MAGIC, \ + 215, void *) +#define AUDIO_SET_RTAC_CVP_CAL _IOWR(CAL_IOCTL_MAGIC, \ + 216, void *) +#define AUDIO_GET_RTAC_AFE_CAL _IOWR(CAL_IOCTL_MAGIC, \ + 217, void *) +#define AUDIO_SET_RTAC_AFE_CAL _IOWR(CAL_IOCTL_MAGIC, \ + 218, void *) +enum { + CVP_VOC_RX_TOPOLOGY_CAL_TYPE = 0, + CVP_VOC_TX_TOPOLOGY_CAL_TYPE, + CVP_VOCPROC_STATIC_CAL_TYPE, + CVP_VOCPROC_DYNAMIC_CAL_TYPE, + CVS_VOCSTRM_STATIC_CAL_TYPE, + CVP_VOCDEV_CFG_CAL_TYPE, + CVP_VOCPROC_STATIC_COL_CAL_TYPE, + CVP_VOCPROC_DYNAMIC_COL_CAL_TYPE, + CVS_VOCSTRM_STATIC_COL_CAL_TYPE, + + ADM_TOPOLOGY_CAL_TYPE, + ADM_CUST_TOPOLOGY_CAL_TYPE, + ADM_AUDPROC_CAL_TYPE, + ADM_AUDVOL_CAL_TYPE, + + ASM_TOPOLOGY_CAL_TYPE, + ASM_CUST_TOPOLOGY_CAL_TYPE, + ASM_AUDSTRM_CAL_TYPE, + + AFE_COMMON_RX_CAL_TYPE, + AFE_COMMON_TX_CAL_TYPE, + AFE_ANC_CAL_TYPE, + AFE_AANC_CAL_TYPE, + AFE_FB_SPKR_PROT_CAL_TYPE, + AFE_HW_DELAY_CAL_TYPE, + AFE_SIDETONE_CAL_TYPE, + AFE_TOPOLOGY_CAL_TYPE, + AFE_CUST_TOPOLOGY_CAL_TYPE, + + LSM_CUST_TOPOLOGY_CAL_TYPE, + LSM_TOPOLOGY_CAL_TYPE, + LSM_CAL_TYPE, + + ADM_RTAC_INFO_CAL_TYPE, + VOICE_RTAC_INFO_CAL_TYPE, + ADM_RTAC_APR_CAL_TYPE, + ASM_RTAC_APR_CAL_TYPE, + VOICE_RTAC_APR_CAL_TYPE, + + MAD_CAL_TYPE, + ULP_AFE_CAL_TYPE, + ULP_LSM_CAL_TYPE, + + DTS_EAGLE_CAL_TYPE, + AUDIO_CORE_METAINFO_CAL_TYPE, + SRS_TRUMEDIA_CAL_TYPE, + + CORE_CUSTOM_TOPOLOGIES_CAL_TYPE, + ADM_RTAC_AUDVOL_CAL_TYPE, + + ULP_LSM_TOPOLOGY_ID_CAL_TYPE, + AFE_FB_SPKR_PROT_TH_VI_CAL_TYPE, + AFE_FB_SPKR_PROT_EX_VI_CAL_TYPE, + AFE_SIDETONE_IIR_CAL_TYPE, + AFE_LSM_TOPOLOGY_CAL_TYPE, + AFE_LSM_TX_CAL_TYPE, + ADM_LSM_TOPOLOGY_CAL_TYPE, + ADM_LSM_AUDPROC_CAL_TYPE, + ADM_LSM_AUDPROC_PERSISTENT_CAL_TYPE, + MAX_CAL_TYPES, +}; + +#define AFE_FB_SPKR_PROT_TH_VI_CAL_TYPE AFE_FB_SPKR_PROT_TH_VI_CAL_TYPE +#define AFE_FB_SPKR_PROT_EX_VI_CAL_TYPE AFE_FB_SPKR_PROT_EX_VI_CAL_TYPE + +#define AFE_SIDETONE_IIR_CAL_TYPE AFE_SIDETONE_IIR_CAL_TYPE + +#define AFE_LSM_TOPOLOGY_CAL_TYPE AFE_LSM_TOPOLOGY_CAL_TYPE +#define AFE_LSM_TX_CAL_TYPE AFE_LSM_TX_CAL_TYPE +#define ADM_LSM_TOPOLOGY_CAL_TYPE ADM_LSM_TOPOLOGY_CAL_TYPE +#define ADM_LSM_AUDPROC_CAL_TYPE ADM_LSM_AUDPROC_CAL_TYPE +#define ADM_LSM_AUDPROC_PERSISTENT_CAL_TYPE ADM_LSM_AUDPROC_PERSISTENT_CAL_TYPE +#define LSM_CAL_TYPES + +#define TOPOLOGY_SPECIFIC_CHANNEL_INFO +#define MSM_SPKR_PROT_SPV3 + +enum { + VERSION_0_0, +}; + +enum { + PER_VOCODER_CAL_BIT_MASK = 0x10000, +}; + +#define MAX_IOCTL_CMD_SIZE 512 + +/* common structures */ + +struct audio_cal_header { + int32_t data_size; + int32_t version; + int32_t cal_type; + int32_t cal_type_size; +}; + +struct audio_cal_type_header { + int32_t version; + int32_t buffer_number; +}; + +struct audio_cal_data { + /* Size of cal data at mem_handle allocation or at vaddr */ + int32_t cal_size; + /* If mem_handle if shared memory is used*/ + int32_t mem_handle; + /* size of virtual memory if shared memory not used */ +}; + + +/* AUDIO_ALLOCATE_CALIBRATION */ +struct audio_cal_type_alloc { + struct audio_cal_type_header cal_hdr; + struct audio_cal_data cal_data; +}; + +struct audio_cal_alloc { + struct audio_cal_header hdr; + struct audio_cal_type_alloc cal_type; +}; + + +/* AUDIO_DEALLOCATE_CALIBRATION */ +struct audio_cal_type_dealloc { + struct audio_cal_type_header cal_hdr; + struct audio_cal_data cal_data; +}; + +struct audio_cal_dealloc { + struct audio_cal_header hdr; + struct audio_cal_type_dealloc cal_type; +}; + + +/* AUDIO_PREPARE_CALIBRATION */ +struct audio_cal_type_prepare { + struct audio_cal_type_header cal_hdr; + struct audio_cal_data cal_data; +}; + +struct audio_cal_prepare { + struct audio_cal_header hdr; + struct audio_cal_type_prepare cal_type; +}; + + +/* AUDIO_POST_CALIBRATION */ +struct audio_cal_type_post { + struct audio_cal_type_header cal_hdr; + struct audio_cal_data cal_data; +}; + +struct audio_cal_post { + struct audio_cal_header hdr; + struct audio_cal_type_post cal_type; +}; + +/*AUDIO_CORE_META_INFO */ + +struct audio_cal_info_metainfo { + uint32_t nKey; +}; + +/* Cal info types */ +enum { + RX_DEVICE, + TX_DEVICE, + MAX_PATH_TYPE +}; + +struct audio_cal_info_adm_top { + int32_t topology; + int32_t acdb_id; + /* RX_DEVICE or TX_DEVICE */ + int32_t path; + int32_t app_type; + int32_t sample_rate; +}; + +struct audio_cal_info_audproc { + int32_t acdb_id; + /* RX_DEVICE or TX_DEVICE */ + int32_t path; + int32_t app_type; + int32_t sample_rate; +}; + +struct audio_cal_info_audvol { + int32_t acdb_id; + /* RX_DEVICE or TX_DEVICE */ + int32_t path; + int32_t app_type; + int32_t vol_index; +}; + +struct audio_cal_info_afe { + int32_t acdb_id; + /* RX_DEVICE or TX_DEVICE */ + int32_t path; + int32_t sample_rate; +}; + +struct audio_cal_info_afe_top { + int32_t topology; + int32_t acdb_id; + /* RX_DEVICE or TX_DEVICE */ + int32_t path; + int32_t sample_rate; +}; + +struct audio_cal_info_asm_top { + int32_t topology; + int32_t app_type; +}; + +struct audio_cal_info_audstrm { + int32_t app_type; +}; + +struct audio_cal_info_aanc { + int32_t acdb_id; +}; + +#define MAX_HW_DELAY_ENTRIES 25 + +struct audio_cal_hw_delay_entry { + uint32_t sample_rate; + uint32_t delay_usec; +}; + +struct audio_cal_hw_delay_data { + uint32_t num_entries; + struct audio_cal_hw_delay_entry entry[MAX_HW_DELAY_ENTRIES]; +}; + +struct audio_cal_info_hw_delay { + int32_t acdb_id; + /* RX_DEVICE or TX_DEVICE */ + int32_t path; + int32_t property_type; + struct audio_cal_hw_delay_data data; +}; + +enum msm_spkr_prot_states { + MSM_SPKR_PROT_CALIBRATED, + MSM_SPKR_PROT_CALIBRATION_IN_PROGRESS, + MSM_SPKR_PROT_DISABLED, + MSM_SPKR_PROT_NOT_CALIBRATED, + MSM_SPKR_PROT_PRE_CALIBRATED, + MSM_SPKR_PROT_IN_FTM_MODE, + MSM_SPKR_PROT_IN_V_VALI_MODE +}; +#define MSM_SPKR_PROT_IN_FTM_MODE MSM_SPKR_PROT_IN_FTM_MODE +#define MSM_SPKR_PROT_IN_V_VALI_MODE MSM_SPKR_PROT_IN_V_VALI_MODE + +enum msm_spkr_count { + SP_V2_SPKR_1, + SP_V2_SPKR_2, + SP_V2_NUM_MAX_SPKRS +}; + +struct audio_cal_info_spk_prot_cfg { + int32_t r0[SP_V2_NUM_MAX_SPKRS]; + int32_t t0[SP_V2_NUM_MAX_SPKRS]; + uint32_t quick_calib_flag; + uint32_t mode; + /* + * 0 - Start spk prot + * 1 - Start calib + * 2 - Disable spk prot + */ +#ifdef MSM_SPKR_PROT_SPV3 + uint32_t sp_version; + int32_t limiter_th[SP_V2_NUM_MAX_SPKRS]; +#endif +}; + +struct audio_cal_info_sp_th_vi_ftm_cfg { + /* + * mode should be first param, add new params later to this. + * we use this mode(first 4 bytes) to differentiate + * whether it is TH_VI FTM or v-validation. + */ + uint32_t mode; + /* + * 0 - normal running mode + * 1 - Calibration + * 2 - FTM mode + */ + uint32_t wait_time[SP_V2_NUM_MAX_SPKRS]; + uint32_t ftm_time[SP_V2_NUM_MAX_SPKRS]; +}; + +struct audio_cal_info_sp_th_vi_v_vali_cfg { + /* + * mode should be first param, add new params later to this. + * we use this mode(first 4 bytes) to differentiate + * whether it is TH_VI FTM or v-validation. + */ + uint32_t mode; + /* + * 0 - normal running mode + * 1 - Calibration + * 2 - FTM mode + * 3 - V-Validation mode + */ + uint32_t wait_time[SP_V2_NUM_MAX_SPKRS]; + uint32_t vali_time[SP_V2_NUM_MAX_SPKRS]; + +}; + +struct audio_cal_info_sp_ex_vi_ftm_cfg { + uint32_t wait_time[SP_V2_NUM_MAX_SPKRS]; + uint32_t ftm_time[SP_V2_NUM_MAX_SPKRS]; + uint32_t mode; + /* + * 0 - normal running mode + * 2 - FTM mode + */ +}; + +struct audio_cal_info_sp_ex_vi_param { + int32_t freq_q20[SP_V2_NUM_MAX_SPKRS]; + int32_t resis_q24[SP_V2_NUM_MAX_SPKRS]; + int32_t qmct_q24[SP_V2_NUM_MAX_SPKRS]; + int32_t status[SP_V2_NUM_MAX_SPKRS]; +}; + +struct audio_cal_info_sp_th_vi_param { + /* + * mode should be first param, add new params later to this. + * we use this mode(first 4 bytes) to differentiate + * whether it is TH_VI FTM or v-validation. + */ + uint32_t mode; + int32_t r_dc_q24[SP_V2_NUM_MAX_SPKRS]; + int32_t temp_q22[SP_V2_NUM_MAX_SPKRS]; + int32_t status[SP_V2_NUM_MAX_SPKRS]; +}; + +struct audio_cal_info_sp_th_vi_v_vali_param { + /* + * mode should be first param, add new params later to this. + * we use this mode(first 4 bytes) to differentiate + * whether it is TH_VI FTM or v-validation. + */ + uint32_t mode; + uint32_t vrms_q24[SP_V2_NUM_MAX_SPKRS]; + int32_t status[SP_V2_NUM_MAX_SPKRS]; +}; + +struct audio_cal_info_msm_spk_prot_status { + int32_t r0[SP_V2_NUM_MAX_SPKRS]; + int32_t status; +}; + +struct audio_cal_info_sidetone { + uint16_t enable; + uint16_t gain; + int32_t tx_acdb_id; + int32_t rx_acdb_id; + int32_t mid; + int32_t pid; +}; + +#define MAX_SIDETONE_IIR_DATA_SIZE 224 +#define MAX_NO_IIR_FILTER_STAGE 10 + +struct audio_cal_info_sidetone_iir { + uint16_t iir_enable; + uint16_t num_biquad_stages; + uint16_t pregain; + int32_t tx_acdb_id; + int32_t rx_acdb_id; + int32_t mid; + int32_t pid; + uint8_t iir_config[MAX_SIDETONE_IIR_DATA_SIZE]; +}; +struct audio_cal_info_lsm_top { + int32_t topology; + int32_t acdb_id; + int32_t app_type; +}; + + +struct audio_cal_info_lsm { + int32_t acdb_id; + /* RX_DEVICE or TX_DEVICE */ + int32_t path; + int32_t app_type; +}; + +#define VSS_NUM_CHANNELS_MAX 32 + +struct audio_cal_info_voc_top { + int32_t topology; + int32_t acdb_id; +#ifdef TOPOLOGY_SPECIFIC_CHANNEL_INFO + uint32_t num_channels; + uint8_t channel_mapping[VSS_NUM_CHANNELS_MAX]; +#endif +}; + +struct audio_cal_info_vocproc { + int32_t tx_acdb_id; + int32_t rx_acdb_id; + int32_t tx_sample_rate; + int32_t rx_sample_rate; +}; + +enum { + DEFAULT_FEATURE_SET, + VOL_BOOST_FEATURE_SET, +}; + +struct audio_cal_info_vocvol { + int32_t tx_acdb_id; + int32_t rx_acdb_id; + /* DEFAULT_ or VOL_BOOST_FEATURE_SET */ + int32_t feature_set; +}; + +struct audio_cal_info_vocdev_cfg { + int32_t tx_acdb_id; + int32_t rx_acdb_id; +}; + +#define MAX_VOICE_COLUMNS 20 + +union audio_cal_col_na { + uint8_t val8; + uint16_t val16; + uint32_t val32; + uint64_t val64; +} __packed; + +struct audio_cal_col { + uint32_t id; + uint32_t type; + union audio_cal_col_na na_value; +} __packed; + +struct audio_cal_col_data { + uint32_t num_columns; + struct audio_cal_col column[MAX_VOICE_COLUMNS]; +} __packed; + +struct audio_cal_info_voc_col { + int32_t table_id; + int32_t tx_acdb_id; + int32_t rx_acdb_id; + struct audio_cal_col_data data; +}; + +/* AUDIO_SET_CALIBRATION & */ +struct audio_cal_type_basic { + struct audio_cal_type_header cal_hdr; + struct audio_cal_data cal_data; +}; + +struct audio_cal_basic { + struct audio_cal_header hdr; + struct audio_cal_type_basic cal_type; +}; + +struct audio_cal_type_adm_top { + struct audio_cal_type_header cal_hdr; + struct audio_cal_data cal_data; + struct audio_cal_info_adm_top cal_info; +}; + +struct audio_cal_adm_top { + struct audio_cal_header hdr; + struct audio_cal_type_adm_top cal_type; +}; + +struct audio_cal_type_metainfo { + struct audio_cal_type_header cal_hdr; + struct audio_cal_data cal_data; + struct audio_cal_info_metainfo cal_info; +}; + +struct audio_core_metainfo { + struct audio_cal_header hdr; + struct audio_cal_type_metainfo cal_type; +}; + +struct audio_cal_type_audproc { + struct audio_cal_type_header cal_hdr; + struct audio_cal_data cal_data; + struct audio_cal_info_audproc cal_info; +}; + +struct audio_cal_audproc { + struct audio_cal_header hdr; + struct audio_cal_type_audproc cal_type; +}; + +struct audio_cal_type_audvol { + struct audio_cal_type_header cal_hdr; + struct audio_cal_data cal_data; + struct audio_cal_info_audvol cal_info; +}; + +struct audio_cal_audvol { + struct audio_cal_header hdr; + struct audio_cal_type_audvol cal_type; +}; + +struct audio_cal_type_asm_top { + struct audio_cal_type_header cal_hdr; + struct audio_cal_data cal_data; + struct audio_cal_info_asm_top cal_info; +}; + +struct audio_cal_asm_top { + struct audio_cal_header hdr; + struct audio_cal_type_asm_top cal_type; +}; + +struct audio_cal_type_audstrm { + struct audio_cal_type_header cal_hdr; + struct audio_cal_data cal_data; + struct audio_cal_info_audstrm cal_info; +}; + +struct audio_cal_audstrm { + struct audio_cal_header hdr; + struct audio_cal_type_audstrm cal_type; +}; + +struct audio_cal_type_afe { + struct audio_cal_type_header cal_hdr; + struct audio_cal_data cal_data; + struct audio_cal_info_afe cal_info; +}; + +struct audio_cal_afe { + struct audio_cal_header hdr; + struct audio_cal_type_afe cal_type; +}; + +struct audio_cal_type_afe_top { + struct audio_cal_type_header cal_hdr; + struct audio_cal_data cal_data; + struct audio_cal_info_afe_top cal_info; +}; + +struct audio_cal_afe_top { + struct audio_cal_header hdr; + struct audio_cal_type_afe_top cal_type; +}; + +struct audio_cal_type_aanc { + struct audio_cal_type_header cal_hdr; + struct audio_cal_data cal_data; + struct audio_cal_info_aanc cal_info; +}; + +struct audio_cal_aanc { + struct audio_cal_header hdr; + struct audio_cal_type_aanc cal_type; +}; + +struct audio_cal_type_fb_spk_prot_cfg { + struct audio_cal_type_header cal_hdr; + struct audio_cal_data cal_data; + struct audio_cal_info_spk_prot_cfg cal_info; +}; + +struct audio_cal_fb_spk_prot_cfg { + struct audio_cal_header hdr; + struct audio_cal_type_fb_spk_prot_cfg cal_type; +}; + +struct audio_cal_type_sp_th_vi_ftm_cfg { + struct audio_cal_type_header cal_hdr; + struct audio_cal_data cal_data; + struct audio_cal_info_sp_th_vi_ftm_cfg cal_info; +}; + +struct audio_cal_sp_th_vi_ftm_cfg { + struct audio_cal_header hdr; + struct audio_cal_type_sp_th_vi_ftm_cfg cal_type; +}; + +struct audio_cal_type_sp_th_vi_v_vali_cfg { + struct audio_cal_type_header cal_hdr; + struct audio_cal_data cal_data; + struct audio_cal_info_sp_th_vi_v_vali_cfg cal_info; +}; + +struct audio_cal_sp_th_vi_v_vali_cfg { + struct audio_cal_header hdr; + struct audio_cal_type_sp_th_vi_v_vali_cfg cal_type; +}; + +struct audio_cal_type_sp_ex_vi_ftm_cfg { + struct audio_cal_type_header cal_hdr; + struct audio_cal_data cal_data; + struct audio_cal_info_sp_ex_vi_ftm_cfg cal_info; +}; + +struct audio_cal_sp_ex_vi_ftm_cfg { + struct audio_cal_header hdr; + struct audio_cal_type_sp_ex_vi_ftm_cfg cal_type; +}; +struct audio_cal_type_hw_delay { + struct audio_cal_type_header cal_hdr; + struct audio_cal_data cal_data; + struct audio_cal_info_hw_delay cal_info; +}; + +struct audio_cal_hw_delay { + struct audio_cal_header hdr; + struct audio_cal_type_hw_delay cal_type; +}; + +struct audio_cal_type_sidetone { + struct audio_cal_type_header cal_hdr; + struct audio_cal_data cal_data; + struct audio_cal_info_sidetone cal_info; +}; + +struct audio_cal_sidetone { + struct audio_cal_header hdr; + struct audio_cal_type_sidetone cal_type; +}; + +struct audio_cal_type_sidetone_iir { + struct audio_cal_type_header cal_hdr; + struct audio_cal_data cal_data; + struct audio_cal_info_sidetone_iir cal_info; +}; + +struct audio_cal_sidetone_iir { + struct audio_cal_header hdr; + struct audio_cal_type_sidetone_iir cal_type; +}; + +struct audio_cal_type_lsm_top { + struct audio_cal_type_header cal_hdr; + struct audio_cal_data cal_data; + struct audio_cal_info_lsm_top cal_info; +}; + +struct audio_cal_lsm_top { + struct audio_cal_header hdr; + struct audio_cal_type_lsm_top cal_type; +}; + +struct audio_cal_type_lsm { + struct audio_cal_type_header cal_hdr; + struct audio_cal_data cal_data; + struct audio_cal_info_lsm cal_info; +}; + +struct audio_cal_lsm { + struct audio_cal_header hdr; + struct audio_cal_type_lsm cal_type; +}; + +struct audio_cal_type_voc_top { + struct audio_cal_type_header cal_hdr; + struct audio_cal_data cal_data; + struct audio_cal_info_voc_top cal_info; +}; + +struct audio_cal_voc_top { + struct audio_cal_header hdr; + struct audio_cal_type_voc_top cal_type; +}; + +struct audio_cal_type_vocproc { + struct audio_cal_type_header cal_hdr; + struct audio_cal_data cal_data; + struct audio_cal_info_vocproc cal_info; +}; + +struct audio_cal_vocproc { + struct audio_cal_header hdr; + struct audio_cal_type_vocproc cal_type; +}; + +struct audio_cal_type_vocvol { + struct audio_cal_type_header cal_hdr; + struct audio_cal_data cal_data; + struct audio_cal_info_vocvol cal_info; +}; + +struct audio_cal_vocvol { + struct audio_cal_header hdr; + struct audio_cal_type_vocvol cal_type; +}; + +struct audio_cal_type_vocdev_cfg { + struct audio_cal_type_header cal_hdr; + struct audio_cal_data cal_data; + struct audio_cal_info_vocdev_cfg cal_info; +}; + +struct audio_cal_vocdev_cfg { + struct audio_cal_header hdr; + struct audio_cal_type_vocdev_cfg cal_type; +}; + +struct audio_cal_type_voc_col { + struct audio_cal_type_header cal_hdr; + struct audio_cal_data cal_data; + struct audio_cal_info_voc_col cal_info; +}; + +struct audio_cal_voc_col { + struct audio_cal_header hdr; + struct audio_cal_type_voc_col cal_type; +}; + +/* AUDIO_GET_CALIBRATION */ +struct audio_cal_type_fb_spk_prot_status { + struct audio_cal_type_header cal_hdr; + struct audio_cal_data cal_data; + struct audio_cal_info_msm_spk_prot_status cal_info; +}; + +struct audio_cal_fb_spk_prot_status { + struct audio_cal_header hdr; + struct audio_cal_type_fb_spk_prot_status cal_type; +}; + +struct audio_cal_type_sp_th_vi_param { + struct audio_cal_type_header cal_hdr; + struct audio_cal_data cal_data; + struct audio_cal_info_sp_th_vi_param cal_info; +}; + +struct audio_cal_sp_th_vi_param { + struct audio_cal_header hdr; + struct audio_cal_type_sp_th_vi_param cal_type; +}; + +struct audio_cal_type_sp_th_vi_v_vali_param { + struct audio_cal_type_header cal_hdr; + struct audio_cal_data cal_data; + struct audio_cal_info_sp_th_vi_v_vali_param cal_info; +}; + +struct audio_cal_sp_th_vi_v_vali_param { + struct audio_cal_header hdr; + struct audio_cal_type_sp_th_vi_v_vali_param cal_type; +}; +struct audio_cal_type_sp_ex_vi_param { + struct audio_cal_type_header cal_hdr; + struct audio_cal_data cal_data; + struct audio_cal_info_sp_ex_vi_param cal_info; +}; + +struct audio_cal_sp_ex_vi_param { + struct audio_cal_header hdr; + struct audio_cal_type_sp_ex_vi_param cal_type; +}; +#endif /* _UAPI_MSM_AUDIO_CALIBRATION_H */ diff --git a/audio-kernel/include/uapi/linux/msm_audio_g711.h b/audio-kernel/include/uapi/linux/msm_audio_g711.h new file mode 100644 index 000000000..48ebd6a11 --- /dev/null +++ b/audio-kernel/include/uapi/linux/msm_audio_g711.h @@ -0,0 +1,17 @@ +#ifndef _UAPI_MSM_AUDIO_G711_H +#define _UAPI_MSM_AUDIO_G711_H + +#include + +struct msm_audio_g711_enc_config { + uint32_t sample_rate; +}; + +#define AUDIO_SET_G711_ENC_CONFIG _IOW(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_COMMON_IOCTL_NUM+0), struct msm_audio_g711_enc_config) + +#define AUDIO_GET_G711_ENC_CONFIG _IOR(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_COMMON_IOCTL_NUM+1), struct msm_audio_g711_enc_config) + + +#endif /* _UAPI_MSM_AUDIO_G711_H */ diff --git a/audio-kernel/include/uapi/linux/msm_audio_g711_dec.h b/audio-kernel/include/uapi/linux/msm_audio_g711_dec.h new file mode 100644 index 000000000..ff7e4ce39 --- /dev/null +++ b/audio-kernel/include/uapi/linux/msm_audio_g711_dec.h @@ -0,0 +1,16 @@ +#ifndef _UAPI_MSM_AUDIO_G711_H +#define _UAPI_MSM_AUDIO_G711_H + +#include + +struct msm_audio_g711_dec_config { + uint32_t sample_rate; +}; + +#define AUDIO_SET_G711_DEC_CONFIG _IOW(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_COMMON_IOCTL_NUM+0), struct msm_audio_g711_dec_config) + +#define AUDIO_GET_G711_DEC_CONFIG _IOR(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_COMMON_IOCTL_NUM+1), struct msm_audio_g711_dec_config) + +#endif /* _UAPI_MSM_AUDIO_G711_H */ diff --git a/audio-kernel/include/uapi/linux/msm_audio_mvs.h b/audio-kernel/include/uapi/linux/msm_audio_mvs.h new file mode 100644 index 000000000..5b76bf99a --- /dev/null +++ b/audio-kernel/include/uapi/linux/msm_audio_mvs.h @@ -0,0 +1,155 @@ +#ifndef _UAPI_MSM_AUDIO_MVS_H +#define _UAPI_MSM_AUDIO_MVS_H + +#include + +#define AUDIO_GET_MVS_CONFIG _IOW(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_COMMON_IOCTL_NUM + 0), unsigned int) +#define AUDIO_SET_MVS_CONFIG _IOR(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_COMMON_IOCTL_NUM + 1), unsigned int) + +/* MVS modes */ +#define MVS_MODE_IS733 0x1 /*QCELP 13K*/ +#define MVS_MODE_IS127 0x2 /*EVRC-8k*/ +#define MVS_MODE_4GV_NB 0x3 /*EVRC-B*/ +#define MVS_MODE_4GV_WB 0x4 /*EVRC-WB*/ +#define MVS_MODE_AMR 0x5 +#define MVS_MODE_EFR 0x6 +#define MVS_MODE_FR 0x7 +#define MVS_MODE_HR 0x8 +#define MVS_MODE_LINEAR_PCM 0x9 +#define MVS_MODE_G711 0xA +#define MVS_MODE_PCM 0xC +#define MVS_MODE_AMR_WB 0xD +#define MVS_MODE_G729A 0xE +#define MVS_MODE_G711A 0xF +#define MVS_MODE_G722 0x10 +#define MVS_MODE_PCM_WB 0x12 + +enum msm_audio_amr_mode { + MVS_AMR_MODE_0475, /* AMR 4.75 kbps */ + MVS_AMR_MODE_0515, /* AMR 5.15 kbps */ + MVS_AMR_MODE_0590, /* AMR 5.90 kbps */ + MVS_AMR_MODE_0670, /* AMR 6.70 kbps */ + MVS_AMR_MODE_0740, /* AMR 7.40 kbps */ + MVS_AMR_MODE_0795, /* AMR 7.95 kbps */ + MVS_AMR_MODE_1020, /* AMR 10.20 kbps */ + MVS_AMR_MODE_1220, /* AMR 12.20 kbps */ + MVS_AMR_MODE_0660, /* AMR-WB 6.60 kbps */ + MVS_AMR_MODE_0885, /* AMR-WB 8.85 kbps */ + MVS_AMR_MODE_1265, /* AMR-WB 12.65 kbps */ + MVS_AMR_MODE_1425, /* AMR-WB 14.25 kbps */ + MVS_AMR_MODE_1585, /* AMR-WB 15.85 kbps */ + MVS_AMR_MODE_1825, /* AMR-WB 18.25 kbps */ + MVS_AMR_MODE_1985, /* AMR-WB 19.85 kbps */ + MVS_AMR_MODE_2305, /* AMR-WB 23.05 kbps */ + MVS_AMR_MODE_2385, /* AMR-WB 23.85 kbps */ + MVS_AMR_MODE_UNDEF +}; + +/* The MVS VOC rate type is used to identify the rate of QCELP 13K(IS733), + * EVRC(IS127), 4GV, or 4GV-WB frame. + */ +enum msm_audio_voc_rate { + MVS_VOC_0_RATE, /* Blank frame */ + MVS_VOC_8_RATE, /* 1/8 rate */ + MVS_VOC_4_RATE, /* 1/4 rate */ + MVS_VOC_2_RATE, /* 1/2 rate */ + MVS_VOC_1_RATE, /* Full rate */ + MVS_VOC_ERASURE, /* erasure frame */ + MVS_VOC_RATE_MAX, + MVS_VOC_RATE_UNDEF = MVS_VOC_RATE_MAX +}; + +enum msm_audio_amr_frame_type { + MVS_AMR_SPEECH_GOOD, /* Good speech frame */ + MVS_AMR_SPEECH_DEGRADED, /* Speech degraded */ + MVS_AMR_ONSET, /* Onset */ + MVS_AMR_SPEECH_BAD, /* Corrupt speech frame (bad CRC) */ + MVS_AMR_SID_FIRST, /* First silence descriptor */ + MVS_AMR_SID_UPDATE, /* Comfort noise frame */ + MVS_AMR_SID_BAD, /* Corrupt SID frame (bad CRC) */ + MVS_AMR_NO_DATA, /* Nothing to transmit */ + MVS_AMR_SPEECH_LOST /* Downlink speech lost */ +}; + +enum msm_audio_g711a_mode { + MVS_G711A_MODE_MULAW, + MVS_G711A_MODE_ALAW +}; + +enum msm_audio_g711_mode { + MVS_G711_MODE_MULAW, + MVS_G711_MODE_ALAW +}; + +enum mvs_g722_mode_type { + MVS_G722_MODE_01, + MVS_G722_MODE_02, + MVS_G722_MODE_03, + MVS_G722_MODE_MAX, + MVS_G722_MODE_UNDEF +}; + +enum msm_audio_g711a_frame_type { + MVS_G711A_SPEECH_GOOD, + MVS_G711A_SID, + MVS_G711A_NO_DATA, + MVS_G711A_ERASURE +}; + +enum msm_audio_g729a_frame_type { + MVS_G729A_NO_DATA, + MVS_G729A_SPEECH_GOOD, + MVS_G729A_SID, + MVS_G729A_ERASURE +}; + +struct min_max_rate { + uint32_t min_rate; + uint32_t max_rate; +}; + +struct msm_audio_mvs_config { + uint32_t mvs_mode; + uint32_t rate_type; + struct min_max_rate min_max_rate; + uint32_t dtx_mode; +}; + +#define MVS_MAX_VOC_PKT_SIZE 640 + +struct gsm_header { + uint8_t bfi; + uint8_t sid; + uint8_t taf; + uint8_t ufi; +}; + +struct q6_msm_audio_mvs_frame { + union { + uint32_t frame_type; + uint32_t packet_rate; + struct gsm_header gsm_frame_type; + } header; + uint32_t len; + uint8_t voc_pkt[MVS_MAX_VOC_PKT_SIZE]; + +}; + +struct msm_audio_mvs_frame { + uint32_t frame_type; + uint32_t len; + uint8_t voc_pkt[MVS_MAX_VOC_PKT_SIZE]; + +}; + +#define Q5V2_MVS_MAX_VOC_PKT_SIZE 320 + +struct q5v2_msm_audio_mvs_frame { + uint32_t frame_type; + uint32_t len; + uint8_t voc_pkt[Q5V2_MVS_MAX_VOC_PKT_SIZE]; + +}; +#endif /* _UAPI_MSM_AUDIO_MVS_H */ diff --git a/audio-kernel/include/uapi/linux/msm_audio_qcp.h b/audio-kernel/include/uapi/linux/msm_audio_qcp.h new file mode 100644 index 000000000..fdb234e91 --- /dev/null +++ b/audio-kernel/include/uapi/linux/msm_audio_qcp.h @@ -0,0 +1,37 @@ +#ifndef _UAPI_MSM_AUDIO_QCP_H +#define _UAPI_MSM_AUDIO_QCP_H + +#include + +#define AUDIO_SET_QCELP_ENC_CONFIG _IOW(AUDIO_IOCTL_MAGIC, \ + 0, struct msm_audio_qcelp_enc_config) + +#define AUDIO_GET_QCELP_ENC_CONFIG _IOR(AUDIO_IOCTL_MAGIC, \ + 1, struct msm_audio_qcelp_enc_config) + +#define AUDIO_SET_EVRC_ENC_CONFIG _IOW(AUDIO_IOCTL_MAGIC, \ + 2, struct msm_audio_evrc_enc_config) + +#define AUDIO_GET_EVRC_ENC_CONFIG _IOR(AUDIO_IOCTL_MAGIC, \ + 3, struct msm_audio_evrc_enc_config) + +#define CDMA_RATE_BLANK 0x00 +#define CDMA_RATE_EIGHTH 0x01 +#define CDMA_RATE_QUARTER 0x02 +#define CDMA_RATE_HALF 0x03 +#define CDMA_RATE_FULL 0x04 +#define CDMA_RATE_ERASURE 0x05 + +struct msm_audio_qcelp_enc_config { + uint32_t cdma_rate; + uint32_t min_bit_rate; + uint32_t max_bit_rate; +}; + +struct msm_audio_evrc_enc_config { + uint32_t cdma_rate; + uint32_t min_bit_rate; + uint32_t max_bit_rate; +}; + +#endif /* _UAPI_MSM_AUDIO_QCP_H */ diff --git a/audio-kernel/include/uapi/linux/msm_audio_sbc.h b/audio-kernel/include/uapi/linux/msm_audio_sbc.h new file mode 100644 index 000000000..1c7c63da3 --- /dev/null +++ b/audio-kernel/include/uapi/linux/msm_audio_sbc.h @@ -0,0 +1,36 @@ +#ifndef _UAPI_MSM_AUDIO_SBC_H +#define _UAPI_MSM_AUDIO_SBC_H + +#include + +#define AUDIO_SET_SBC_ENC_CONFIG _IOW(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_COMMON_IOCTL_NUM+0), struct msm_audio_sbc_enc_config) + +#define AUDIO_GET_SBC_ENC_CONFIG _IOR(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_COMMON_IOCTL_NUM+1), struct msm_audio_sbc_enc_config) + +#define AUDIO_SBC_BA_LOUDNESS 0x0 +#define AUDIO_SBC_BA_SNR 0x1 + +#define AUDIO_SBC_MODE_MONO 0x0 +#define AUDIO_SBC_MODE_DUAL 0x1 +#define AUDIO_SBC_MODE_STEREO 0x2 +#define AUDIO_SBC_MODE_JSTEREO 0x3 + +#define AUDIO_SBC_BANDS_8 0x1 + +#define AUDIO_SBC_BLOCKS_4 0x0 +#define AUDIO_SBC_BLOCKS_8 0x1 +#define AUDIO_SBC_BLOCKS_12 0x2 +#define AUDIO_SBC_BLOCKS_16 0x3 + +struct msm_audio_sbc_enc_config { + uint32_t channels; + uint32_t sample_rate; + uint32_t bit_allocation; + uint32_t number_of_subbands; + uint32_t number_of_blocks; + uint32_t bit_rate; + uint32_t mode; +}; +#endif /* _UAPI_MSM_AUDIO_SBC_H */ diff --git a/audio-kernel/include/uapi/linux/msm_audio_voicememo.h b/audio-kernel/include/uapi/linux/msm_audio_voicememo.h new file mode 100644 index 000000000..a7a7a4df1 --- /dev/null +++ b/audio-kernel/include/uapi/linux/msm_audio_voicememo.h @@ -0,0 +1,66 @@ +#ifndef _UAPI_MSM_AUDIO_VOICEMEMO_H +#define _UAPI_MSM_AUDIO_VOICEMEMO_H + +#include + +#define AUDIO_GET_VOICEMEMO_CONFIG _IOW(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_COMMON_IOCTL_NUM+0), unsigned int) +#define AUDIO_SET_VOICEMEMO_CONFIG _IOR(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_COMMON_IOCTL_NUM+1), unsigned int) + +/* rec_type */ +enum rpc_voc_rec_dir_type { + RPC_VOC_REC_NONE, + RPC_VOC_REC_FORWARD, + RPC_VOC_REC_REVERSE, + RPC_VOC_REC_BOTH, + RPC_VOC_MAX_REC_TYPE +}; + +/* capability */ +enum rpc_voc_capability_type { + RPC_VOC_CAP_IS733 = 4, + RPC_VOC_CAP_IS127 = 8, + RPC_VOC_CAP_AMR = 64, + RPC_VOC_CAP_32BIT_DUMMY = 2147483647 +}; + +/* Rate */ +enum rpc_voc_rate_type { + RPC_VOC_0_RATE = 0, + RPC_VOC_8_RATE, + RPC_VOC_4_RATE, + RPC_VOC_2_RATE, + RPC_VOC_1_RATE, + RPC_VOC_ERASURE, + RPC_VOC_ERR_RATE, + RPC_VOC_AMR_RATE_475 = 0, + RPC_VOC_AMR_RATE_515 = 1, + RPC_VOC_AMR_RATE_590 = 2, + RPC_VOC_AMR_RATE_670 = 3, + RPC_VOC_AMR_RATE_740 = 4, + RPC_VOC_AMR_RATE_795 = 5, + RPC_VOC_AMR_RATE_1020 = 6, + RPC_VOC_AMR_RATE_1220 = 7, +}; + +/* frame_format */ +enum rpc_voc_pb_len_rate_var_type { + RPC_VOC_PB_NATIVE_QCP = 3, + RPC_VOC_PB_AMR, + RPC_VOC_PB_EVB +}; + +struct msm_audio_voicememo_config { + uint32_t rec_type; + uint32_t rec_interval_ms; + uint32_t auto_stop_ms; + uint32_t capability; + uint32_t max_rate; + uint32_t min_rate; + uint32_t frame_format; + uint32_t dtx_enable; + uint32_t data_req_ms; +}; + +#endif /* _UAPI_MSM_AUDIO_VOICEMEMO_H */ diff --git a/audio-kernel/include/uapi/linux/msm_audio_wma.h b/audio-kernel/include/uapi/linux/msm_audio_wma.h new file mode 100644 index 000000000..523fadef0 --- /dev/null +++ b/audio-kernel/include/uapi/linux/msm_audio_wma.h @@ -0,0 +1,33 @@ +#ifndef _UAPI_MSM_AUDIO_WMA_H +#define _UAPI_MSM_AUDIO_WMA_H + +#define AUDIO_GET_WMA_CONFIG _IOR(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_COMMON_IOCTL_NUM+0), unsigned int) +#define AUDIO_SET_WMA_CONFIG _IOW(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_COMMON_IOCTL_NUM+1), unsigned int) + +#define AUDIO_GET_WMA_CONFIG_V2 _IOR(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_COMMON_IOCTL_NUM+2), struct msm_audio_wma_config_v2) +#define AUDIO_SET_WMA_CONFIG_V2 _IOW(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_COMMON_IOCTL_NUM+3), struct msm_audio_wma_config_v2) + +struct msm_audio_wma_config { + unsigned short armdatareqthr; + unsigned short channelsdecoded; + unsigned short wmabytespersec; + unsigned short wmasamplingfreq; + unsigned short wmaencoderopts; +}; + +struct msm_audio_wma_config_v2 { + unsigned short format_tag; + unsigned short numchannels; + uint32_t samplingrate; + uint32_t avgbytespersecond; + unsigned short block_align; + unsigned short validbitspersample; + uint32_t channelmask; + unsigned short encodeopt; +}; + +#endif /* _UAPI_MSM_AUDIO_WMA_H */ diff --git a/audio-kernel/include/uapi/linux/msm_audio_wmapro.h b/audio-kernel/include/uapi/linux/msm_audio_wmapro.h new file mode 100644 index 000000000..64cbf9e07 --- /dev/null +++ b/audio-kernel/include/uapi/linux/msm_audio_wmapro.h @@ -0,0 +1,22 @@ +#ifndef _UAPI_MSM_AUDIO_WMAPRO_H +#define _UAPI_MSM_AUDIO_WMAPRO_H + +#define AUDIO_GET_WMAPRO_CONFIG _IOR(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_COMMON_IOCTL_NUM+0), struct msm_audio_wmapro_config) +#define AUDIO_SET_WMAPRO_CONFIG _IOW(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_COMMON_IOCTL_NUM+1), struct msm_audio_wmapro_config) + +struct msm_audio_wmapro_config { + unsigned short armdatareqthr; + uint8_t validbitspersample; + uint8_t numchannels; + unsigned short formattag; + uint32_t samplingrate; + uint32_t avgbytespersecond; + unsigned short asfpacketlength; + uint32_t channelmask; + unsigned short encodeopt; + unsigned short advancedencodeopt; + uint32_t advancedencodeopt2; +}; +#endif /* _UAPI_MSM_AUDIO_WMAPRO_H */ diff --git a/audio-kernel/include/uapi/sound/Kbuild b/audio-kernel/include/uapi/sound/Kbuild new file mode 100644 index 000000000..175fb6f63 --- /dev/null +++ b/audio-kernel/include/uapi/sound/Kbuild @@ -0,0 +1,8 @@ +# UAPI Header export list +header-y += lsm_params.h +header-y += audio_slimslave.h +header-y += audio_effects.h +header-y += devdep_params.h +header-y += msmcal-hwdep.h +header-y += wcd-dsp-glink.h +header-y += voice_params.h diff --git a/audio-kernel/include/uapi/sound/audio_effects.h b/audio-kernel/include/uapi/sound/audio_effects.h new file mode 100644 index 000000000..f38b2121a --- /dev/null +++ b/audio-kernel/include/uapi/sound/audio_effects.h @@ -0,0 +1,365 @@ +#ifndef _AUDIO_EFFECTS_H +#define _AUDIO_EFFECTS_H + +/** AUDIO EFFECTS **/ + + +/* CONFIG GET/SET */ +#define CONFIG_CACHE 0 +#define CONFIG_SET 1 +#define CONFIG_GET 2 + +/* CONFIG HEADER */ +/* + * MODULE_ID, + * DEVICE, + * NUM_COMMANDS, + * COMMAND_ID_1, + * CONFIG_CACHE/SET/GET, + * OFFSET_1, + * LENGTH_1, + * VALUES_1, + * ..., + * ..., + * COMMAND_ID_2, + * CONFIG_CACHE/SET/GET, + * OFFSET_2, + * LENGTH_2, + * VALUES_2, + * ..., + * ..., + * COMMAND_ID_3, + * ... + */ + + +/* CONFIG PARAM IDs */ +#define VIRTUALIZER_MODULE 0x00001000 +#define VIRTUALIZER_ENABLE 0x00001001 +#define VIRTUALIZER_STRENGTH 0x00001002 +#define VIRTUALIZER_OUT_TYPE 0x00001003 +#define VIRTUALIZER_GAIN_ADJUST 0x00001004 +#define VIRTUALIZER_ENABLE_PARAM_LEN 1 +#define VIRTUALIZER_STRENGTH_PARAM_LEN 1 +#define VIRTUALIZER_OUT_TYPE_PARAM_LEN 1 +#define VIRTUALIZER_GAIN_ADJUST_PARAM_LEN 1 + +#define REVERB_MODULE 0x00002000 +#define REVERB_ENABLE 0x00002001 +#define REVERB_MODE 0x00002002 +#define REVERB_PRESET 0x00002003 +#define REVERB_WET_MIX 0x00002004 +#define REVERB_GAIN_ADJUST 0x00002005 +#define REVERB_ROOM_LEVEL 0x00002006 +#define REVERB_ROOM_HF_LEVEL 0x00002007 +#define REVERB_DECAY_TIME 0x00002008 +#define REVERB_DECAY_HF_RATIO 0x00002009 +#define REVERB_REFLECTIONS_LEVEL 0x0000200a +#define REVERB_REFLECTIONS_DELAY 0x0000200b +#define REVERB_LEVEL 0x0000200c +#define REVERB_DELAY 0x0000200d +#define REVERB_DIFFUSION 0x0000200e +#define REVERB_DENSITY 0x0000200f +#define REVERB_ENABLE_PARAM_LEN 1 +#define REVERB_MODE_PARAM_LEN 1 +#define REVERB_PRESET_PARAM_LEN 1 +#define REVERB_WET_MIX_PARAM_LEN 1 +#define REVERB_GAIN_ADJUST_PARAM_LEN 1 +#define REVERB_ROOM_LEVEL_PARAM_LEN 1 +#define REVERB_ROOM_HF_LEVEL_PARAM_LEN 1 +#define REVERB_DECAY_TIME_PARAM_LEN 1 +#define REVERB_DECAY_HF_RATIO_PARAM_LEN 1 +#define REVERB_REFLECTIONS_LEVEL_PARAM_LEN 1 +#define REVERB_REFLECTIONS_DELAY_PARAM_LEN 1 +#define REVERB_LEVEL_PARAM_LEN 1 +#define REVERB_DELAY_PARAM_LEN 1 +#define REVERB_DIFFUSION_PARAM_LEN 1 +#define REVERB_DENSITY_PARAM_LEN 1 + +#define BASS_BOOST_MODULE 0x00003000 +#define BASS_BOOST_ENABLE 0x00003001 +#define BASS_BOOST_MODE 0x00003002 +#define BASS_BOOST_STRENGTH 0x00003003 +#define BASS_BOOST_ENABLE_PARAM_LEN 1 +#define BASS_BOOST_MODE_PARAM_LEN 1 +#define BASS_BOOST_STRENGTH_PARAM_LEN 1 + +#define EQ_MODULE 0x00004000 +#define EQ_ENABLE 0x00004001 +#define EQ_CONFIG 0x00004002 +#define EQ_NUM_BANDS 0x00004003 +#define EQ_BAND_LEVELS 0x00004004 +#define EQ_BAND_LEVEL_RANGE 0x00004005 +#define EQ_BAND_FREQS 0x00004006 +#define EQ_SINGLE_BAND_FREQ_RANGE 0x00004007 +#define EQ_SINGLE_BAND_FREQ 0x00004008 +#define EQ_BAND_INDEX 0x00004009 +#define EQ_PRESET_ID 0x0000400a +#define EQ_NUM_PRESETS 0x0000400b +#define EQ_PRESET_NAME 0x0000400c +#define EQ_ENABLE_PARAM_LEN 1 +#define EQ_CONFIG_PARAM_LEN 3 +#define EQ_CONFIG_PER_BAND_PARAM_LEN 5 +#define EQ_NUM_BANDS_PARAM_LEN 1 +#define EQ_BAND_LEVELS_PARAM_LEN 13 +#define EQ_BAND_LEVEL_RANGE_PARAM_LEN 2 +#define EQ_BAND_FREQS_PARAM_LEN 13 +#define EQ_SINGLE_BAND_FREQ_RANGE_PARAM_LEN 2 +#define EQ_SINGLE_BAND_FREQ_PARAM_LEN 1 +#define EQ_BAND_INDEX_PARAM_LEN 1 +#define EQ_PRESET_ID_PARAM_LEN 1 +#define EQ_NUM_PRESETS_PARAM_LEN 1 +#define EQ_PRESET_NAME_PARAM_LEN 32 + +#define EQ_TYPE_NONE 0 +#define EQ_BASS_BOOST 1 +#define EQ_BASS_CUT 2 +#define EQ_TREBLE_BOOST 3 +#define EQ_TREBLE_CUT 4 +#define EQ_BAND_BOOST 5 +#define EQ_BAND_CUT 6 + +#define SOFT_VOLUME_MODULE 0x00006000 +#define SOFT_VOLUME_ENABLE 0x00006001 +#define SOFT_VOLUME_GAIN_2CH 0x00006002 +#define SOFT_VOLUME_GAIN_MASTER 0x00006003 +#define SOFT_VOLUME_ENABLE_PARAM_LEN 1 +#define SOFT_VOLUME_GAIN_2CH_PARAM_LEN 2 +#define SOFT_VOLUME_GAIN_MASTER_PARAM_LEN 1 + +#define SOFT_VOLUME2_MODULE 0x00007000 +#define SOFT_VOLUME2_ENABLE 0x00007001 +#define SOFT_VOLUME2_GAIN_2CH 0x00007002 +#define SOFT_VOLUME2_GAIN_MASTER 0x00007003 +#define SOFT_VOLUME2_ENABLE_PARAM_LEN SOFT_VOLUME_ENABLE_PARAM_LEN +#define SOFT_VOLUME2_GAIN_2CH_PARAM_LEN SOFT_VOLUME_GAIN_2CH_PARAM_LEN +#define SOFT_VOLUME2_GAIN_MASTER_PARAM_LEN \ + SOFT_VOLUME_GAIN_MASTER_PARAM_LEN + +#define PBE_CONF_MODULE_ID 0x00010C2A +#define PBE_CONF_PARAM_ID 0x00010C49 + +#define PBE_MODULE 0x00008000 +#define PBE_ENABLE 0x00008001 +#define PBE_CONFIG 0x00008002 +#define PBE_ENABLE_PARAM_LEN 1 +#define PBE_CONFIG_PARAM_LEN 28 + +/* Command Payload length and size for Non-IID commands */ +#define COMMAND_PAYLOAD_LEN 3 +#define COMMAND_PAYLOAD_SZ (COMMAND_PAYLOAD_LEN * sizeof(uint32_t)) +/* Command Payload length and size for IID commands */ +#define COMMAND_IID_PAYLOAD_LEN 4 +#define COMMAND_IID_PAYLOAD_SZ (COMMAND_IID_PAYLOAD_LEN * sizeof(uint32_t)) +#define MAX_INBAND_PARAM_SZ 4096 +#define Q27_UNITY (1 << 27) +#define Q8_UNITY (1 << 8) +#define CUSTOM_OPENSL_PRESET 18 + +#define VIRTUALIZER_ENABLE_PARAM_SZ \ + (VIRTUALIZER_ENABLE_PARAM_LEN*sizeof(uint32_t)) +#define VIRTUALIZER_STRENGTH_PARAM_SZ \ + (VIRTUALIZER_STRENGTH_PARAM_LEN*sizeof(uint32_t)) +#define VIRTUALIZER_OUT_TYPE_PARAM_SZ \ + (VIRTUALIZER_OUT_TYPE_PARAM_LEN*sizeof(uint32_t)) +#define VIRTUALIZER_GAIN_ADJUST_PARAM_SZ \ + (VIRTUALIZER_GAIN_ADJUST_PARAM_LEN*sizeof(uint32_t)) +struct virtualizer_params { + uint32_t device; + uint32_t enable_flag; + uint32_t strength; + uint32_t out_type; + int32_t gain_adjust; +}; + +#define NUM_OSL_REVERB_PRESETS_SUPPORTED 6 +#define REVERB_ENABLE_PARAM_SZ \ + (REVERB_ENABLE_PARAM_LEN*sizeof(uint32_t)) +#define REVERB_MODE_PARAM_SZ \ + (REVERB_MODE_PARAM_LEN*sizeof(uint32_t)) +#define REVERB_PRESET_PARAM_SZ \ + (REVERB_PRESET_PARAM_LEN*sizeof(uint32_t)) +#define REVERB_WET_MIX_PARAM_SZ \ + (REVERB_WET_MIX_PARAM_LEN*sizeof(uint32_t)) +#define REVERB_GAIN_ADJUST_PARAM_SZ \ + (REVERB_GAIN_ADJUST_PARAM_LEN*sizeof(uint32_t)) +#define REVERB_ROOM_LEVEL_PARAM_SZ \ + (REVERB_ROOM_LEVEL_PARAM_LEN*sizeof(uint32_t)) +#define REVERB_ROOM_HF_LEVEL_PARAM_SZ \ + (REVERB_ROOM_HF_LEVEL_PARAM_LEN*sizeof(uint32_t)) +#define REVERB_DECAY_TIME_PARAM_SZ \ + (REVERB_DECAY_TIME_PARAM_LEN*sizeof(uint32_t)) +#define REVERB_DECAY_HF_RATIO_PARAM_SZ \ + (REVERB_DECAY_HF_RATIO_PARAM_LEN*sizeof(uint32_t)) +#define REVERB_REFLECTIONS_LEVEL_PARAM_SZ \ + (REVERB_REFLECTIONS_LEVEL_PARAM_LEN*sizeof(uint32_t)) +#define REVERB_REFLECTIONS_DELAY_PARAM_SZ \ + (REVERB_REFLECTIONS_DELAY_PARAM_LEN*sizeof(uint32_t)) +#define REVERB_LEVEL_PARAM_SZ \ + (REVERB_LEVEL_PARAM_LEN*sizeof(uint32_t)) +#define REVERB_DELAY_PARAM_SZ \ + (REVERB_DELAY_PARAM_LEN*sizeof(uint32_t)) +#define REVERB_DIFFUSION_PARAM_SZ \ + (REVERB_DIFFUSION_PARAM_LEN*sizeof(uint32_t)) +#define REVERB_DENSITY_PARAM_SZ \ + (REVERB_DENSITY_PARAM_LEN*sizeof(uint32_t)) +struct reverb_params { + uint32_t device; + uint32_t enable_flag; + uint32_t mode; + uint32_t preset; + uint32_t wet_mix; + int32_t gain_adjust; + int32_t room_level; + int32_t room_hf_level; + uint32_t decay_time; + uint32_t decay_hf_ratio; + int32_t reflections_level; + uint32_t reflections_delay; + int32_t level; + uint32_t delay; + uint32_t diffusion; + uint32_t density; +}; + +#define BASS_BOOST_ENABLE_PARAM_SZ \ + (BASS_BOOST_ENABLE_PARAM_LEN*sizeof(uint32_t)) +#define BASS_BOOST_MODE_PARAM_SZ \ + (BASS_BOOST_MODE_PARAM_LEN*sizeof(uint32_t)) +#define BASS_BOOST_STRENGTH_PARAM_SZ \ + (BASS_BOOST_STRENGTH_PARAM_LEN*sizeof(uint32_t)) +struct bass_boost_params { + uint32_t device; + uint32_t enable_flag; + uint32_t mode; + uint32_t strength; +}; + + +#define MAX_EQ_BANDS 12 +#define MAX_OSL_EQ_BANDS 5 +#define EQ_ENABLE_PARAM_SZ \ + (EQ_ENABLE_PARAM_LEN*sizeof(uint32_t)) +#define EQ_CONFIG_PARAM_SZ \ + (EQ_CONFIG_PARAM_LEN*sizeof(uint32_t)) +#define EQ_CONFIG_PER_BAND_PARAM_SZ \ + (EQ_CONFIG_PER_BAND_PARAM_LEN*sizeof(uint32_t)) +#define EQ_CONFIG_PARAM_MAX_LEN (EQ_CONFIG_PARAM_LEN+\ + MAX_EQ_BANDS*EQ_CONFIG_PER_BAND_PARAM_LEN) +#define EQ_CONFIG_PARAM_MAX_SZ \ + (EQ_CONFIG_PARAM_MAX_LEN*sizeof(uint32_t)) +#define EQ_NUM_BANDS_PARAM_SZ \ + (EQ_NUM_BANDS_PARAM_LEN*sizeof(uint32_t)) +#define EQ_BAND_LEVELS_PARAM_SZ \ + (EQ_BAND_LEVELS_PARAM_LEN*sizeof(uint32_t)) +#define EQ_BAND_LEVEL_RANGE_PARAM_SZ \ + (EQ_BAND_LEVEL_RANGE_PARAM_LEN*sizeof(uint32_t)) +#define EQ_BAND_FREQS_PARAM_SZ \ + (EQ_BAND_FREQS_PARAM_LEN*sizeof(uint32_t)) +#define EQ_SINGLE_BAND_FREQ_RANGE_PARAM_SZ \ + (EQ_SINGLE_BAND_FREQ_RANGE_PARAM_LEN*sizeof(uint32_t)) +#define EQ_SINGLE_BAND_FREQ_PARAM_SZ \ + (EQ_SINGLE_BAND_FREQ_PARAM_LEN*sizeof(uint32_t)) +#define EQ_BAND_INDEX_PARAM_SZ \ + (EQ_BAND_INDEX_PARAM_LEN*sizeof(uint32_t)) +#define EQ_PRESET_ID_PARAM_SZ \ + (EQ_PRESET_ID_PARAM_LEN*sizeof(uint32_t)) +#define EQ_NUM_PRESETS_PARAM_SZ \ + (EQ_NUM_PRESETS_PARAM_LEN*sizeof(uint8_t)) +struct eq_config_t { + int32_t eq_pregain; + int32_t preset_id; + uint32_t num_bands; +}; +struct eq_per_band_config_t { + int32_t band_idx; + uint32_t filter_type; + uint32_t freq_millihertz; + int32_t gain_millibels; + uint32_t quality_factor; +}; +struct eq_per_band_freq_range_t { + uint32_t band_index; + uint32_t min_freq_millihertz; + uint32_t max_freq_millihertz; +}; + +struct eq_params { + uint32_t device; + uint32_t enable_flag; + struct eq_config_t config; + struct eq_per_band_config_t per_band_cfg[MAX_EQ_BANDS]; + struct eq_per_band_freq_range_t per_band_freq_range[MAX_EQ_BANDS]; + uint32_t band_index; + uint32_t freq_millihertz; +}; + +#define PBE_ENABLE_PARAM_SZ \ + (PBE_ENABLE_PARAM_LEN*sizeof(uint32_t)) +#define PBE_CONFIG_PARAM_SZ \ + (PBE_CONFIG_PARAM_LEN*sizeof(uint16_t)) +struct pbe_config_t { + int16_t real_bass_mix; + int16_t bass_color_control; + uint16_t main_chain_delay; + uint16_t xover_filter_order; + uint16_t bandpass_filter_order; + int16_t drc_delay; + uint16_t rms_tav; + int16_t exp_threshold; + uint16_t exp_slope; + int16_t comp_threshold; + uint16_t comp_slope; + uint16_t makeup_gain; + uint32_t comp_attack; + uint32_t comp_release; + uint32_t exp_attack; + uint32_t exp_release; + int16_t limiter_bass_threshold; + int16_t limiter_high_threshold; + int16_t limiter_bass_makeup_gain; + int16_t limiter_high_makeup_gain; + int16_t limiter_bass_gc; + int16_t limiter_high_gc; + int16_t limiter_delay; + uint16_t reserved; + /* place holder for filter coeffs to be followed */ + int32_t p1LowPassCoeffs[5*2]; + int32_t p1HighPassCoeffs[5*2]; + int32_t p1BandPassCoeffs[5*3]; + int32_t p1BassShelfCoeffs[5]; + int32_t p1TrebleShelfCoeffs[5]; +} __packed; + +struct pbe_params { + uint32_t device; + uint32_t enable_flag; + uint32_t cfg_len; + struct pbe_config_t config; +}; + +#define SOFT_VOLUME_ENABLE_PARAM_SZ \ + (SOFT_VOLUME_ENABLE_PARAM_LEN*sizeof(uint32_t)) +#define SOFT_VOLUME_GAIN_MASTER_PARAM_SZ \ + (SOFT_VOLUME_GAIN_MASTER_PARAM_LEN*sizeof(uint32_t)) +#define SOFT_VOLUME_GAIN_2CH_PARAM_SZ \ + (SOFT_VOLUME_GAIN_2CH_PARAM_LEN*sizeof(uint16_t)) +struct soft_volume_params { + uint32_t device; + uint32_t enable_flag; + uint32_t master_gain; + uint32_t left_gain; + uint32_t right_gain; +}; + +struct msm_nt_eff_all_config { + struct bass_boost_params bass_boost; + struct pbe_params pbe; + struct virtualizer_params virtualizer; + struct reverb_params reverb; + struct eq_params equalizer; + struct soft_volume_params saplus_vol; + struct soft_volume_params topo_switch_vol; +}; + +#endif /*_MSM_AUDIO_EFFECTS_H*/ diff --git a/audio-kernel/include/uapi/sound/audio_slimslave.h b/audio-kernel/include/uapi/sound/audio_slimslave.h new file mode 100644 index 000000000..316a5573f --- /dev/null +++ b/audio-kernel/include/uapi/sound/audio_slimslave.h @@ -0,0 +1,18 @@ +#ifndef __AUDIO_SLIMSLAVE_H__ +#define __AUDIO_SLIMSLAVE_H__ + +#include +#include + +#define AUDIO_SLIMSLAVE_IOCTL_NAME "audio_slimslave" +#define AUDIO_SLIMSLAVE_MAGIC 'S' + +#define AUDIO_SLIMSLAVE_IOCTL_UNVOTE _IO(AUDIO_SLIMSLAVE_MAGIC, 0x00) +#define AUDIO_SLIMSLAVE_IOCTL_VOTE _IO(AUDIO_SLIMSLAVE_MAGIC, 0x01) + +enum { + AUDIO_SLIMSLAVE_UNVOTE, + AUDIO_SLIMSLAVE_VOTE +}; + +#endif diff --git a/audio-kernel/include/uapi/sound/devdep_params.h b/audio-kernel/include/uapi/sound/devdep_params.h new file mode 100644 index 000000000..66a9cacfc --- /dev/null +++ b/audio-kernel/include/uapi/sound/devdep_params.h @@ -0,0 +1,66 @@ +#ifndef _DEV_DEP_H +#define _DEV_DEP_H + +struct dolby_param_data { + int32_t version; + int32_t device_id; + int32_t be_id; + int32_t param_id; + int32_t length; + int32_t __user *data; +}; + +struct dolby_param_license { + int32_t dmid; + int32_t license_key; +}; + +#define SNDRV_DEVDEP_DAP_IOCTL_SET_PARAM\ + _IOWR('U', 0x10, struct dolby_param_data) +#define SNDRV_DEVDEP_DAP_IOCTL_GET_PARAM\ + _IOR('U', 0x11, struct dolby_param_data) +#define SNDRV_DEVDEP_DAP_IOCTL_DAP_COMMAND\ + _IOWR('U', 0x13, struct dolby_param_data) +#define SNDRV_DEVDEP_DAP_IOCTL_DAP_LICENSE\ + _IOWR('U', 0x14, struct dolby_param_license) +#define SNDRV_DEVDEP_DAP_IOCTL_GET_VISUALIZER\ + _IOR('U', 0x15, struct dolby_param_data) + +#define DTS_EAGLE_MODULE 0x00005000 +#define DTS_EAGLE_MODULE_ENABLE 0x00005001 +#define EAGLE_DRIVER_ID 0xF2 +#define DTS_EAGLE_IOCTL_GET_CACHE_SIZE _IOR(EAGLE_DRIVER_ID, 0, int) +#define DTS_EAGLE_IOCTL_SET_CACHE_SIZE _IOW(EAGLE_DRIVER_ID, 1, int) +#define DTS_EAGLE_IOCTL_GET_PARAM _IOR(EAGLE_DRIVER_ID, 2, void*) +#define DTS_EAGLE_IOCTL_SET_PARAM _IOW(EAGLE_DRIVER_ID, 3, void*) +#define DTS_EAGLE_IOCTL_SET_CACHE_BLOCK _IOW(EAGLE_DRIVER_ID, 4, void*) +#define DTS_EAGLE_IOCTL_SET_ACTIVE_DEVICE _IOW(EAGLE_DRIVER_ID, 5, void*) +#define DTS_EAGLE_IOCTL_GET_LICENSE _IOR(EAGLE_DRIVER_ID, 6, void*) +#define DTS_EAGLE_IOCTL_SET_LICENSE _IOW(EAGLE_DRIVER_ID, 7, void*) +#define DTS_EAGLE_IOCTL_SEND_LICENSE _IOW(EAGLE_DRIVER_ID, 8, int) +#define DTS_EAGLE_IOCTL_SET_VOLUME_COMMANDS _IOW(EAGLE_DRIVER_ID, 9, void*) +#define DTS_EAGLE_FLAG_IOCTL_PRE (1<<30) +#define DTS_EAGLE_FLAG_IOCTL_JUSTSETCACHE (1<<31) +#define DTS_EAGLE_FLAG_IOCTL_GETFROMCORE DTS_EAGLE_FLAG_IOCTL_JUSTSETCACHE +#define DTS_EAGLE_FLAG_IOCTL_MASK (~(DTS_EAGLE_FLAG_IOCTL_PRE | \ + DTS_EAGLE_FLAG_IOCTL_JUSTSETCACHE)) +#define DTS_EAGLE_FLAG_ALSA_GET (1<<31) + +struct dts_eagle_param_desc { + uint32_t id; + uint32_t size; + int32_t offset; + uint32_t device; +} __packed; + +#define HWDEP_FE_BASE 3000 /*unique base for FE hw dep nodes*/ +struct snd_pcm_mmap_fd { + int32_t dir; + int32_t fd; + int32_t size; + int32_t actual_size; +}; + +#define SNDRV_PCM_IOCTL_MMAP_DATA_FD _IOWR('U', 0xd2, struct snd_pcm_mmap_fd) + +#endif diff --git a/audio-kernel/include/uapi/sound/lsm_params.h b/audio-kernel/include/uapi/sound/lsm_params.h new file mode 100644 index 000000000..57dc3be5d --- /dev/null +++ b/audio-kernel/include/uapi/sound/lsm_params.h @@ -0,0 +1,315 @@ +#ifndef _UAPI_LSM_PARAMS_H__ +#define _UAPI_LSM_PARAMS_H__ + +#define LSM_POLLING_ENABLE_SUPPORT +#define LSM_EVENT_TIMESTAMP_MODE_SUPPORT + +#include +#include + +#define SNDRV_LSM_VERSION SNDRV_PROTOCOL_VERSION(0, 3, 1) + +#define LSM_MAX_STAGES_PER_SESSION 2 +#define LSM_STAGE_INDEX_FIRST 0 + +#define LSM_OUT_FORMAT_PCM (0) +#define LSM_OUT_FORMAT_ADPCM (1 << 0) + +#define LSM_OUT_DATA_RAW (0) +#define LSM_OUT_DATA_PACKED (1) + +#define LSM_OUT_DATA_EVENTS_DISABLED (0) +#define LSM_OUT_DATA_EVENTS_ENABLED (1) + +#define LSM_OUT_TRANSFER_MODE_RT (0) +#define LSM_OUT_TRANSFER_MODE_FTRT (1) + +#define LSM_ENDPOINT_DETECT_THRESHOLD (0) +#define LSM_OPERATION_MODE (1) +#define LSM_GAIN (2) +#define LSM_MIN_CONFIDENCE_LEVELS (3) +#define LSM_REG_SND_MODEL (4) +#define LSM_DEREG_SND_MODEL (5) +#define LSM_CUSTOM_PARAMS (6) +#define LSM_POLLING_ENABLE (7) +#define LSM_DET_EVENT_TYPE (8) +#define LSM_LAB_CONTROL (9) +#define LSM_PARAMS_MAX (LSM_LAB_CONTROL + 1) + +#define LSM_EVENT_NON_TIME_STAMP_MODE (0) +#define LSM_EVENT_TIME_STAMP_MODE (1) + +#define LSM_DET_EVENT_TYPE_LEGACY (0) +#define LSM_DET_EVENT_TYPE_GENERIC (1) + +/* Valid sample rates for input hw_params */ +#define LSM_INPUT_SAMPLE_RATE_16K 16000 +#define LSM_INPUT_SAMPLE_RATE_48K 48000 + +/* Valid bit-widths for input hw_params */ +#define LSM_INPUT_BIT_WIDTH_16 16 +#define LSM_INPUT_BIT_WIDTH_24 24 + +/* Min and Max channels for input hw_params */ +#define LSM_INPUT_NUM_CHANNELS_MIN 1 +#define LSM_INPUT_NUM_CHANNELS_MAX 9 + +enum lsm_app_id { + LSM_VOICE_WAKEUP_APP_ID = 1, + LSM_VOICE_WAKEUP_APP_ID_V2 = 2, +}; + +enum lsm_detection_mode { + LSM_MODE_KEYWORD_ONLY_DETECTION = 1, + LSM_MODE_USER_KEYWORD_DETECTION +}; + +enum lsm_vw_status { + LSM_VOICE_WAKEUP_STATUS_RUNNING = 1, + LSM_VOICE_WAKEUP_STATUS_DETECTED, + LSM_VOICE_WAKEUP_STATUS_END_SPEECH, + LSM_VOICE_WAKEUP_STATUS_REJECTED +}; + +/* + * Data for LSM_ENDPOINT_DETECT_THRESHOLD param_type + * @epd_begin: Begin threshold + * @epd_end: End threshold + */ +struct snd_lsm_ep_det_thres { + __u32 epd_begin; + __u32 epd_end; +}; + +/* + * Data for LSM_OPERATION_MODE param_type + * @mode: The detection mode to be used + * @detect_failure: Setting to enable failure detections. + */ +struct snd_lsm_detect_mode { + enum lsm_detection_mode mode; + bool detect_failure; +}; + +/* + * Data for LSM_GAIN param_type + * @gain: The gain to be applied on LSM + */ +struct snd_lsm_gain { + __u16 gain; +}; + +/* + * Data for LSM_POLLING_ENABLE param_type + * @poll_en: Polling enable or disable + */ +struct snd_lsm_poll_enable { + bool poll_en; +}; + +/* + * Data for LSM_DET_EVENT_TYPE param_type + * @event_type: LSM_DET_EVENT_TYPE_LEGACY or LSM_DET_EVENT_TYPE_GENERIC + * @mode: Type of information in detection event payload + */ +struct snd_lsm_det_event_type { + __u32 event_type; + __u32 mode; +}; + +struct snd_lsm_sound_model_v2 { + __u8 __user *data; + __u8 *confidence_level; + __u32 data_size; + enum lsm_detection_mode detection_mode; + __u8 num_confidence_levels; + bool detect_failure; +}; + +struct snd_lsm_session_data { + enum lsm_app_id app_id; +}; + +/* + * Stage info for multi-stage session + * @app_type: acdb app_type to be used to map topology/cal for the stage + * @lpi_enable: low power island mode applicable for the stage + */ +struct snd_lsm_stage_info { + __u32 app_type; + __u32 lpi_enable; +}; + +/* + * Session info for multi-stage session + * @app_id: VoiceWakeup engine id, this is now used to just validate input arg + * @num_stages: number of detection stages to be used + * @stage_info: stage info for each of the stage being used, ordered by index + */ +struct snd_lsm_session_data_v2 { + enum lsm_app_id app_id; + __u32 num_stages; + struct snd_lsm_stage_info stage_info[LSM_MAX_STAGES_PER_SESSION]; +}; + +/* + * Data for LSM_LAB_CONTROL param_type + * @enable: lab enable or disable + */ +struct snd_lsm_lab_control { + __u32 enable; +}; + +struct snd_lsm_event_status { + __u16 status; + __u16 payload_size; + __u8 payload[0]; +}; + +struct snd_lsm_event_status_v3 { + __u32 timestamp_lsw; + __u32 timestamp_msw; + __u16 status; + __u16 payload_size; + __u8 payload[0]; +}; + +struct snd_lsm_detection_params { + __u8 *conf_level; + enum lsm_detection_mode detect_mode; + __u8 num_confidence_levels; + bool detect_failure; + bool poll_enable; +}; + +/* + * Param info for each parameter type + * @module_id: Module to which parameter is to be set + * @param_id: Parameter that is to be set + * @param_size: size (in number of bytes) for the data + * in param_data. + * For confidence levels, this is num_conf_levels + * For REG_SND_MODEL, this is size of sound model + * For CUSTOM_PARAMS, this is size of the entire blob of data + * @param_data: Data for the parameter. + * For some param_types this is a structure defined, ex: LSM_GAIN + * For CONFIDENCE_LEVELS, this is array of confidence levels + * For REG_SND_MODEL, this is the sound model data + * For CUSTOM_PARAMS, this is the blob of custom data. + * @param_type: Parameter type as defined in values upto LSM_PARAMS_MAX + */ +struct lsm_params_info { + __u32 module_id; + __u32 param_id; + __u32 param_size; + __u8 __user *param_data; + uint32_t param_type; +}; + +/* + * Param info(version 2) for each parameter type + * + * Existing member variables: + * @module_id: Module to which parameter is to be set + * @param_id: Parameter that is to be set + * @param_size: size (in number of bytes) for the data + * in param_data. + * For confidence levels, this is num_conf_levels + * For REG_SND_MODEL, this is size of sound model + * For CUSTOM_PARAMS, this is size of the entire blob of data + * @param_data: Data for the parameter. + * For some param_types this is a structure defined, ex: LSM_GAIN + * For CONFIDENCE_LEVELS, this is array of confidence levels + * For REG_SND_MODEL, this is the sound model data + * For CUSTOM_PARAMS, this is the blob of custom data. + * @param_type: Parameter type as defined in values upto LSM_PARAMS_MAX + * + * Member variables applicable only to V2: + * @instance_id: instance id of the param to which parameter is to be set + * @stage_idx: detection stage for which the param is applicable + */ +struct lsm_params_info_v2 { + __u32 module_id; + __u32 param_id; + __u32 param_size; + __u8 __user *param_data; + uint32_t param_type; + __u16 instance_id; + __u16 stage_idx; +}; + +/* + * Data passed to the SET_PARAM_V2 IOCTL + * @num_params: Number of params that are to be set + * should not be greater than LSM_PARAMS_MAX + * @params: Points to an array of lsm_params_info + * Each entry points to one parameter to set + * @data_size: size (in bytes) for params + * should be equal to + * num_params * sizeof(struct lsm_parms_info) + */ +struct snd_lsm_module_params { + __u8 __user *params; + __u32 num_params; + __u32 data_size; +}; + +/* + * Data passed to LSM_OUT_FORMAT_CFG IOCTL + * @format: The media format enum + * @packing: indicates the packing method used for data path + * @events: indicates whether data path events need to be enabled + * @transfer_mode: indicates whether FTRT mode or RT mode. + */ +struct snd_lsm_output_format_cfg { + __u8 format; + __u8 packing; + __u8 events; + __u8 mode; +}; + +/* + * Data passed to SNDRV_LSM_SET_INPUT_HW_PARAMS ioctl + * + * @sample_rate: Sample rate of input to lsm. + * valid values are 16000 and 48000 + * @bit_width: Bit width of audio samples input to lsm. + * valid values are 16 and 24 + * @num_channels: Number of channels input to lsm. + * valid values are range from 1 to 16 + */ +struct snd_lsm_input_hw_params { + __u32 sample_rate; + __u16 bit_width; + __u16 num_channels; +} __packed; + +#define SNDRV_LSM_DEREG_SND_MODEL _IOW('U', 0x01, int) +#define SNDRV_LSM_EVENT_STATUS _IOW('U', 0x02, struct snd_lsm_event_status) +#define SNDRV_LSM_ABORT_EVENT _IOW('U', 0x03, int) +#define SNDRV_LSM_START _IOW('U', 0x04, int) +#define SNDRV_LSM_STOP _IOW('U', 0x05, int) +#define SNDRV_LSM_SET_SESSION_DATA _IOW('U', 0x06, struct snd_lsm_session_data) +#define SNDRV_LSM_REG_SND_MODEL_V2 _IOW('U', 0x07,\ + struct snd_lsm_sound_model_v2) +#define SNDRV_LSM_LAB_CONTROL _IOW('U', 0x08, uint32_t) +#define SNDRV_LSM_STOP_LAB _IO('U', 0x09) +#define SNDRV_LSM_SET_PARAMS _IOW('U', 0x0A, \ + struct snd_lsm_detection_params) +#define SNDRV_LSM_SET_MODULE_PARAMS _IOW('U', 0x0B, \ + struct snd_lsm_module_params) +#define SNDRV_LSM_OUT_FORMAT_CFG _IOW('U', 0x0C, \ + struct snd_lsm_output_format_cfg) +#define SNDRV_LSM_SET_PORT _IO('U', 0x0D) +#define SNDRV_LSM_SET_FWK_MODE_CONFIG _IOW('U', 0x0E, uint32_t) +#define SNDRV_LSM_EVENT_STATUS_V3 _IOW('U', 0x0F, \ + struct snd_lsm_event_status_v3) +#define SNDRV_LSM_GENERIC_DET_EVENT _IOW('U', 0x10, struct snd_lsm_event_status) +#define SNDRV_LSM_SET_INPUT_HW_PARAMS _IOW('U', 0x11, \ + struct snd_lsm_input_hw_params) +#define SNDRV_LSM_SET_SESSION_DATA_V2 _IOW('U', 0x12, \ + struct snd_lsm_session_data_v2) +#define SNDRV_LSM_SET_MODULE_PARAMS_V2 _IOW('U', 0x13, \ + struct snd_lsm_module_params) + +#endif diff --git a/audio-kernel/include/uapi/sound/msmcal-hwdep.h b/audio-kernel/include/uapi/sound/msmcal-hwdep.h new file mode 100644 index 000000000..f2891863f --- /dev/null +++ b/audio-kernel/include/uapi/sound/msmcal-hwdep.h @@ -0,0 +1,40 @@ +#ifndef _CALIB_HWDEP_H +#define _CALIB_HWDEP_H + +#define WCD9XXX_CODEC_HWDEP_NODE 1000 +#define AQT1000_CODEC_HWDEP_NODE 1001 +#define Q6AFE_HWDEP_NODE 1002 +enum wcd_cal_type { + WCD9XXX_MIN_CAL, + WCD9XXX_ANC_CAL = WCD9XXX_MIN_CAL, + WCD9XXX_MAD_CAL, + WCD9XXX_MBHC_CAL, + WCD9XXX_VBAT_CAL, + WCD9XXX_MAX_CAL, +}; + +struct wcdcal_ioctl_buffer { + __u32 size; + __u8 __user *buffer; + enum wcd_cal_type cal_type; +}; + +#define SNDRV_CTL_IOCTL_HWDEP_CAL_TYPE \ + _IOW('U', 0x1, struct wcdcal_ioctl_buffer) + +enum q6afe_cal_type { + Q6AFE_MIN_CAL, + Q6AFE_VAD_CORE_CAL = Q6AFE_MIN_CAL, + Q6AFE_MAX_CAL, +}; + +struct q6afecal_ioctl_buffer { + __u32 size; + __u8 __user *buffer; + enum q6afe_cal_type cal_type; +}; + +#define SNDRV_IOCTL_HWDEP_VAD_CAL_TYPE \ + _IOW('U', 0x1, struct q6afecal_ioctl_buffer) + +#endif /*_CALIB_HWDEP_H*/ diff --git a/audio-kernel/include/uapi/sound/voice_params.h b/audio-kernel/include/uapi/sound/voice_params.h new file mode 100644 index 000000000..43e3b9d0a --- /dev/null +++ b/audio-kernel/include/uapi/sound/voice_params.h @@ -0,0 +1,14 @@ +#ifndef __VOICE_PARAMS_H__ +#define __VOICE_PARAMS_H__ + +#include +#include + +enum voice_lch_mode { + VOICE_LCH_START = 1, + VOICE_LCH_STOP +}; + +#define SNDRV_VOICE_IOCTL_LCH _IOW('U', 0x00, enum voice_lch_mode) + +#endif diff --git a/audio-kernel/include/uapi/sound/wcd-dsp-glink.h b/audio-kernel/include/uapi/sound/wcd-dsp-glink.h new file mode 100644 index 000000000..39d128d37 --- /dev/null +++ b/audio-kernel/include/uapi/sound/wcd-dsp-glink.h @@ -0,0 +1,60 @@ +#ifndef _WCD_DSP_GLINK_H +#define _WCD_DSP_GLINK_H + +#include + +#define WDSP_CH_NAME_MAX_LEN 50 + +enum { + WDSP_REG_PKT = 1, + WDSP_CMD_PKT, + WDSP_READY_PKT, +}; +#define WDSP_READY_PKT WDSP_READY_PKT + +/* + * struct wdsp_reg_pkt - Glink channel information structure format + * @no_of_channels: Number of glink channels to open + * @payload[0]: Dynamic array contains all the glink channels information + */ +struct wdsp_reg_pkt { + __u8 no_of_channels; + __u8 payload[0]; +}; + +/* + * struct wdsp_cmd_pkt - WDSP command packet format + * @ch_name: Name of the glink channel + * @payload_size: Size of the payload + * @payload[0]: Actual data payload + */ +struct wdsp_cmd_pkt { + char ch_name[WDSP_CH_NAME_MAX_LEN]; + __u32 payload_size; + __u8 payload[0]; +}; + +/* + * struct wdsp_write_pkt - Format that userspace send the data to driver. + * @pkt_type: Type of the packet(REG or CMD PKT) + * @payload[0]: Payload is either cmd or reg pkt structure based on pkt type + */ +struct wdsp_write_pkt { + __u8 pkt_type; + __u8 payload[0]; +}; + +/* + * struct wdsp_glink_ch_cfg - Defines the glink channel configuration. + * @ch_name: Name of the glink channel + * @latency_in_us: Latency specified in micro seconds for QOS + * @no_of_intents: Number of intents prequeued + * @intents_size[0]: Dynamic array to specify size of each intent + */ +struct wdsp_glink_ch_cfg { + char name[WDSP_CH_NAME_MAX_LEN]; + __u32 latency_in_us; + __u32 no_of_intents; + __u32 intents_size[0]; +}; +#endif /* _WCD_DSP_GLINK_H */ diff --git a/audio-kernel/ipc/Android.mk b/audio-kernel/ipc/Android.mk new file mode 100644 index 000000000..18dbedf7b --- /dev/null +++ b/audio-kernel/ipc/Android.mk @@ -0,0 +1,69 @@ +# Android makefile for audio kernel modules + +# Assume no targets will be supported + +# Check if this driver needs be built for current target +ifeq ($(call is-board-platform,sdm845),true) +AUDIO_SELECT := CONFIG_SND_SOC_SDM845=m +endif + +ifeq ($(call is-board-platform-in-list,msm8953 sdm670 qcs605),true) +AUDIO_SELECT := CONFIG_SND_SOC_SDM670=m +endif + +ifeq ($(call is-board-platform,msmnile),true) +AUDIO_SELECT := CONFIG_SND_SOC_SM8150=m +endif + +ifeq ($(call is-board-platform,$(MSMSTEPPE) $(TRINKET)),true) +AUDIO_SELECT := CONFIG_SND_SOC_SM6150=m +endif + +AUDIO_CHIPSET := audio +# Build/Package only in case of supported target +ifeq ($(call is-board-platform-in-list,msm8953 sdm845 sdm670 qcs605 msmnile $(MSMSTEPPE) $(TRINKET)),true) + +LOCAL_PATH := $(call my-dir) + +# This makefile is only for DLKM +ifneq ($(findstring vendor,$(LOCAL_PATH)),) + +ifneq ($(findstring opensource,$(LOCAL_PATH)),) + AUDIO_BLD_DIR := $(shell pwd)/vendor/qcom/opensource/audio-kernel +endif # opensource + +DLKM_DIR := $(TOP)/device/qcom/common/dlkm + +# Build audio.ko as $(AUDIO_CHIPSET)_audio.ko +########################################################### +# This is set once per LOCAL_PATH, not per (kernel) module +KBUILD_OPTIONS := AUDIO_ROOT=$(AUDIO_BLD_DIR) + +# We are actually building audio.ko here, as per the +# requirement we are specifying _audio.ko as LOCAL_MODULE. +# This means we need to rename the module to _audio.ko +# after audio.ko is built. +KBUILD_OPTIONS += MODNAME=apr_dlkm +KBUILD_OPTIONS += BOARD_PLATFORM=$(TARGET_BOARD_PLATFORM) +KBUILD_OPTIONS += $(AUDIO_SELECT) + +########################################################### +include $(CLEAR_VARS) +LOCAL_MODULE := $(AUDIO_CHIPSET)_apr.ko +LOCAL_MODULE_KBUILD_NAME := apr_dlkm.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_DEBUG_ENABLE := true +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk +########################################################### +include $(CLEAR_VARS) +LOCAL_MODULE := $(AUDIO_CHIPSET)_wglink.ko +LOCAL_MODULE_KBUILD_NAME := wglink_dlkm.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_DEBUG_ENABLE := true +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk +########################################################### + +endif # DLKM check +endif # supported target check diff --git a/audio-kernel/ipc/Kbuild b/audio-kernel/ipc/Kbuild new file mode 100644 index 000000000..11faab5e1 --- /dev/null +++ b/audio-kernel/ipc/Kbuild @@ -0,0 +1,162 @@ +# We can build either as part of a standalone Kernel build or as +# an external module. Determine which mechanism is being used +ifeq ($(MODNAME),) + KERNEL_BUILD := 1 +else + KERNEL_BUILD := 0 +endif + +# These are configurable via Kconfig for kernel-based builds +# Need to explicitly configure for Android-based builds +KDIR := $(TOP)/kernel/msm-4.14 + +ifeq ($(KERNEL_BUILD), 1) + AUDIO_ROOT := $(KDIR)/techpack/audio +endif + +ifeq ($(KERNEL_BUILD), 0) + ifeq ($(CONFIG_ARCH_SDM845), y) + include $(AUDIO_ROOT)/config/sdm845auto.conf + export + INCS += -include $(AUDIO_ROOT)/config/sdm845autoconf.h + endif + ifeq ($(CONFIG_ARCH_SDM670), y) + include $(AUDIO_ROOT)/config/sdm670auto.conf + export + INCS += -include $(AUDIO_ROOT)/config/sdm670autoconf.h + endif + ifeq ($(CONFIG_ARCH_SDM450), y) + include $(AUDIO_ROOT)/config/sdm670auto.conf + export + INCS += -include $(AUDIO_ROOT)/config/sdm670autoconf.h + endif + ifeq ($(CONFIG_ARCH_SM6150), y) + include $(AUDIO_ROOT)/config/sm6150auto.conf + export + INCS += -include $(AUDIO_ROOT)/config/sm6150autoconf.h + endif + ifeq ($(CONFIG_ARCH_TRINKET), y) + include $(AUDIO_ROOT)/config/trinketauto.conf + export + INCS += -include $(AUDIO_ROOT)/config/trinketautoconf.h + endif + ifeq ($(CONFIG_ARCH_SM8150), y) + include $(AUDIO_ROOT)/config/sm8150auto.conf + export + INCS += -include $(AUDIO_ROOT)/config/sm8150autoconf.h + endif + ifeq ($(CONFIG_ARCH_QCS405), y) + include $(AUDIO_ROOT)/config/qcs405auto.conf + export + INCS += -include $(AUDIO_ROOT)/config/qcs405autoconf.h + endif + ifeq ($(CONFIG_ARCH_SDMSHRIKE), y) + include $(AUDIO_ROOT)/config/sm8150auto.conf + export + INCS += -include $(AUDIO_ROOT)/config/sm8150autoconf.h + endif +endif + +# As per target team, build is done as follows: +# Defconfig : build with default flags +# Slub : defconfig + CONFIG_SLUB_DEBUG := y + +# CONFIG_SLUB_DEBUG_ON := y + CONFIG_PAGE_POISONING := y +# Perf : Using appropriate msmXXXX-perf_defconfig +# +# Shipment builds (user variants) should not have any debug feature +# enabled. This is identified using 'TARGET_BUILD_VARIANT'. Slub builds +# are identified using the CONFIG_SLUB_DEBUG_ON configuration. Since +# there is no other way to identify defconfig builds, QTI internal +# representation of perf builds (identified using the string 'perf'), +# is used to identify if the build is a slub or defconfig one. This +# way no critical debug feature will be enabled for perf and shipment +# builds. Other OEMs are also protected using the TARGET_BUILD_VARIANT +# config. + +############ UAPI ############ +UAPI_DIR := uapi +UAPI_INC := -I$(AUDIO_ROOT)/include/$(UAPI_DIR) + +############ COMMON ############ +COMMON_DIR := include +COMMON_INC := -I$(AUDIO_ROOT)/$(COMMON_DIR) + +############ IPC ############ + +ifdef CONFIG_MSM_QDSP6_APRV2_RPMSG +APRV_GLINK += apr.o +APRV_GLINK += apr_v2.o +APRV_GLINK += apr_tal_rpmsg.o +endif + +ifdef CONFIG_MSM_QDSP6_APRV3_RPMSG +APRV_GLINK += apr.o +APRV_GLINK += apr_v3.o +APRV_GLINK += apr_tal_rpmsg.o +endif + +ifdef CONFIG_WCD_DSP_GLINK +WDSP_GLINK += wcd-dsp-glink.o +endif + +LINUX_INC += -Iinclude/linux + +INCS += $(COMMON_INC) \ + $(UAPI_INC) + +EXTRA_CFLAGS += $(INCS) + + +CDEFINES += -DANI_LITTLE_BYTE_ENDIAN \ + -DANI_LITTLE_BIT_ENDIAN \ + -DDOT11F_LITTLE_ENDIAN_HOST \ + -DANI_COMPILER_TYPE_GCC \ + -DANI_OS_TYPE_ANDROID=6 \ + -DPTT_SOCK_SVC_ENABLE \ + -Wall\ + -Werror\ + -D__linux__ + +KBUILD_CPPFLAGS += $(CDEFINES) + +ifeq ($(KERNEL_BUILD), 0) +KBUILD_EXTRA_SYMBOLS +=$(OUT)/obj/vendor/qcom/opensource/audio-kernel/dsp/Module.symvers +endif + +# Currently, for versions of gcc which support it, the kernel Makefile +# is disabling the maybe-uninitialized warning. Re-enable it for the +# AUDIO driver. Note that we must use EXTRA_CFLAGS here so that it +# will override the kernel settings. +ifeq ($(call cc-option-yn, -Wmaybe-uninitialized),y) +EXTRA_CFLAGS += -Wmaybe-uninitialized +endif +#EXTRA_CFLAGS += -Wmissing-prototypes + +ifeq ($(call cc-option-yn, -Wheader-guard),y) +EXTRA_CFLAGS += -Wheader-guard +endif +# If the module name is not "wlan", then the define MULTI_IF_NAME to be the +# same a the QCA CHIP name. The host driver will then append MULTI_IF_NAME to +# any string that must be unique for all instances of the driver on the system. +# This allows multiple instances of the driver with different module names. +# If the module name is wlan, leave MULTI_IF_NAME undefined and the code will +# treat the driver as the primary driver. +ifneq ($(MODNAME), qdsp6v2) +CHIP_NAME ?= $(MODNAME) +CDEFINES += -DMULTI_IF_NAME=\"$(CHIP_NAME)\" +endif + +ifeq ($(CONFIG_SND_SOC_GCOV), y) +GCOV_PROFILE := y +endif + +# Module information used by KBuild framework +obj-$(CONFIG_MSM_QDSP6_APRV2_RPMSG) += apr_dlkm.o +obj-$(CONFIG_MSM_QDSP6_APRV3_RPMSG) += apr_dlkm.o +apr_dlkm-y := $(APRV_GLINK) + +obj-$(CONFIG_WCD_DSP_GLINK) += wglink_dlkm.o +wglink_dlkm-y := $(WDSP_GLINK) + +# inject some build related information +CDEFINES += -DBUILD_TIMESTAMP=\"$(shell date -u +'%Y-%m-%dT%H:%M:%SZ')\" diff --git a/audio-kernel/ipc/apr.c b/audio-kernel/ipc/apr.c new file mode 100644 index 000000000..dcda7c03a --- /dev/null +++ b/audio-kernel/ipc/apr.c @@ -0,0 +1,1251 @@ +/* Copyright (c) 2010-2014, 2016-2019 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define APR_PKT_IPC_LOG_PAGE_CNT 2 + +static struct apr_q6 q6; +static struct apr_client client[APR_DEST_MAX][APR_CLIENT_MAX]; +static void *apr_pkt_ctx; +static wait_queue_head_t modem_wait; +static bool is_modem_up; +static char *subsys_name = NULL; +/* Subsystem restart: QDSP6 data, functions */ +static struct workqueue_struct *apr_reset_workqueue; +static void apr_reset_deregister(struct work_struct *work); +static void dispatch_event(unsigned long code, uint16_t proc); +struct apr_reset_work { + void *handle; + struct work_struct work; +}; + +struct apr_chld_device { + struct platform_device *pdev; + struct list_head node; +}; + +struct apr_private { + struct device *dev; + spinlock_t apr_lock; + bool is_initial_boot; + struct work_struct add_chld_dev_work; +}; + +static struct apr_private *apr_priv; +static bool apr_cf_debug; + +#ifdef CONFIG_DEBUG_FS +static struct dentry *debugfs_apr_debug; +static ssize_t apr_debug_write(struct file *filp, const char __user *ubuf, + size_t cnt, loff_t *ppos) +{ + char cmd; + + if (copy_from_user(&cmd, ubuf, 1)) + return -EFAULT; + + apr_cf_debug = (cmd == '1') ? true : false; + + return cnt; +} + +static const struct file_operations apr_debug_ops = { + .write = apr_debug_write, +}; +#endif + +#define APR_PKT_INFO(x...) \ +do { \ + if (apr_pkt_ctx) \ + ipc_log_string(apr_pkt_ctx, ": "x); \ +} while (0) + + +struct apr_svc_table { + char name[64]; + int idx; + int id; + int client_id; +}; + +static const struct apr_svc_table svc_tbl_qdsp6[] = { + { + .name = "AFE", + .idx = 0, + .id = APR_SVC_AFE, + .client_id = APR_CLIENT_AUDIO, + }, + { + .name = "ASM", + .idx = 1, + .id = APR_SVC_ASM, + .client_id = APR_CLIENT_AUDIO, + }, + { + .name = "ADM", + .idx = 2, + .id = APR_SVC_ADM, + .client_id = APR_CLIENT_AUDIO, + }, + { + .name = "CORE", + .idx = 3, + .id = APR_SVC_ADSP_CORE, + .client_id = APR_CLIENT_AUDIO, + }, + { + .name = "TEST", + .idx = 4, + .id = APR_SVC_TEST_CLIENT, + .client_id = APR_CLIENT_AUDIO, + }, + { + .name = "MVM", + .idx = 5, + .id = APR_SVC_ADSP_MVM, + .client_id = APR_CLIENT_AUDIO, + }, + { + .name = "CVS", + .idx = 6, + .id = APR_SVC_ADSP_CVS, + .client_id = APR_CLIENT_AUDIO, + }, + { + .name = "CVP", + .idx = 7, + .id = APR_SVC_ADSP_CVP, + .client_id = APR_CLIENT_AUDIO, + }, + { + .name = "USM", + .idx = 8, + .id = APR_SVC_USM, + .client_id = APR_CLIENT_AUDIO, + }, + { + .name = "VIDC", + .idx = 9, + .id = APR_SVC_VIDC, + }, + { + .name = "LSM", + .idx = 10, + .id = APR_SVC_LSM, + .client_id = APR_CLIENT_AUDIO, + }, +}; + +static struct apr_svc_table svc_tbl_voice[] = { + { + .name = "VSM", + .idx = 0, + .id = APR_SVC_VSM, + .client_id = APR_CLIENT_VOICE, + }, + { + .name = "VPM", + .idx = 1, + .id = APR_SVC_VPM, + .client_id = APR_CLIENT_VOICE, + }, + { + .name = "MVS", + .idx = 2, + .id = APR_SVC_MVS, + .client_id = APR_CLIENT_VOICE, + }, + { + .name = "MVM", + .idx = 3, + .id = APR_SVC_MVM, + .client_id = APR_CLIENT_VOICE, + }, + { + .name = "CVS", + .idx = 4, + .id = APR_SVC_CVS, + .client_id = APR_CLIENT_VOICE, + }, + { + .name = "CVP", + .idx = 5, + .id = APR_SVC_CVP, + .client_id = APR_CLIENT_VOICE, + }, + { + .name = "SRD", + .idx = 6, + .id = APR_SVC_SRD, + .client_id = APR_CLIENT_VOICE, + }, + { + .name = "TEST", + .idx = 7, + .id = APR_SVC_TEST_CLIENT, + .client_id = APR_CLIENT_VOICE, + }, +}; + +/** + * apr_get_modem_state: + * + * Returns current modem load status + * + */ +enum apr_subsys_state apr_get_modem_state(void) +{ + return atomic_read(&q6.modem_state); +} +EXPORT_SYMBOL(apr_get_modem_state); + +/** + * apr_set_modem_state - Update modem load status. + * + * @state: State to update modem load status + * + */ +void apr_set_modem_state(enum apr_subsys_state state) +{ + atomic_set(&q6.modem_state, state); +} +EXPORT_SYMBOL(apr_set_modem_state); + +enum apr_subsys_state apr_cmpxchg_modem_state(enum apr_subsys_state prev, + enum apr_subsys_state new) +{ + return atomic_cmpxchg(&q6.modem_state, prev, new); +} + +static void apr_modem_down(unsigned long opcode) +{ + apr_set_modem_state(APR_SUBSYS_DOWN); + dispatch_event(opcode, APR_DEST_MODEM); +} + +static void apr_modem_up(void) +{ + if (apr_cmpxchg_modem_state(APR_SUBSYS_DOWN, APR_SUBSYS_UP) == + APR_SUBSYS_DOWN) + wake_up(&modem_wait); + is_modem_up = 1; +} + +enum apr_subsys_state apr_get_q6_state(void) +{ + return atomic_read(&q6.q6_state); +} +EXPORT_SYMBOL(apr_get_q6_state); + +int apr_set_q6_state(enum apr_subsys_state state) +{ + pr_debug("%s: setting adsp state %d\n", __func__, state); + if (state < APR_SUBSYS_DOWN || state > APR_SUBSYS_LOADED) + return -EINVAL; + atomic_set(&q6.q6_state, state); + return 0; +} +EXPORT_SYMBOL(apr_set_q6_state); + +static void apr_ssr_disable(struct device *dev, void *data) +{ + apr_set_q6_state(APR_SUBSYS_DOWN); +} + +static const struct snd_event_ops apr_ssr_ops = { + .disable = apr_ssr_disable, +}; + +static void apr_adsp_down(unsigned long opcode) +{ + pr_info("%s: Q6 is Down\n", __func__); + snd_event_notify(apr_priv->dev, SND_EVENT_DOWN); + apr_set_q6_state(APR_SUBSYS_DOWN); + dispatch_event(opcode, APR_DEST_QDSP6); +} + +static void apr_add_child_devices(struct work_struct *work) +{ + int ret; + + ret = of_platform_populate(apr_priv->dev->of_node, + NULL, NULL, apr_priv->dev); + if (ret) + dev_err(apr_priv->dev, "%s: failed to add child nodes, ret=%d\n", + __func__, ret); +} + +static void apr_adsp_up(void) +{ + pr_info("%s: Q6 is Up\n", __func__); + apr_set_q6_state(APR_SUBSYS_LOADED); + + spin_lock(&apr_priv->apr_lock); + if (apr_priv->is_initial_boot) + schedule_work(&apr_priv->add_chld_dev_work); + spin_unlock(&apr_priv->apr_lock); + snd_event_notify(apr_priv->dev, SND_EVENT_UP); +} + +int apr_load_adsp_image(void) +{ + int rc = 0; + + mutex_lock(&q6.lock); + if (apr_get_q6_state() == APR_SUBSYS_UP) { + q6.pil = subsystem_get("adsp"); + if (IS_ERR(q6.pil)) { + rc = PTR_ERR(q6.pil); + pr_err("APR: Unable to load q6 image, error:%d\n", rc); + } else { + apr_set_q6_state(APR_SUBSYS_LOADED); + pr_debug("APR: Image is loaded, stated\n"); + } + } else if (apr_get_q6_state() == APR_SUBSYS_LOADED) { + pr_debug("APR: q6 image already loaded\n"); + } else { + pr_debug("APR: cannot load state %d\n", apr_get_q6_state()); + } + mutex_unlock(&q6.lock); + return rc; +} + +struct apr_client *apr_get_client(int dest_id, int client_id) +{ + return &client[dest_id][client_id]; +} + +/** + * apr_send_pkt - Clients call to send packet + * to destination processor. + * + * @handle: APR service handle + * @buf: payload to send to destination processor. + * + * Returns Bytes(>0)pkt_size on success or error on failure. + */ +int apr_send_pkt(void *handle, uint32_t *buf) +{ + struct apr_svc *svc = handle; + struct apr_client *clnt; + struct apr_hdr *hdr; + uint16_t dest_id; + uint16_t client_id; + uint16_t w_len; + int rc; + unsigned long flags; + + if (!handle || !buf) { + pr_err("APR: Wrong parameters\n"); + return -EINVAL; + } + if (svc->need_reset) { + pr_err_ratelimited("apr: send_pkt service need reset\n"); + return -ENETRESET; + } + + if ((svc->dest_id == APR_DEST_QDSP6) && + (apr_get_q6_state() != APR_SUBSYS_LOADED)) { + pr_err_ratelimited("%s: Still dsp is not Up\n", __func__); + return -ENETRESET; + } else if ((svc->dest_id == APR_DEST_MODEM) && + (apr_get_modem_state() == APR_SUBSYS_DOWN)) { + pr_err("apr: Still Modem is not Up\n"); + return -ENETRESET; + } + + spin_lock_irqsave(&svc->w_lock, flags); + dest_id = svc->dest_id; + client_id = svc->client_id; + clnt = &client[dest_id][client_id]; + + if (!client[dest_id][client_id].handle) { + pr_err_ratelimited("APR: Still service is not yet opened\n"); + spin_unlock_irqrestore(&svc->w_lock, flags); + return -EINVAL; + } + hdr = (struct apr_hdr *)buf; + + hdr->src_domain = APR_DOMAIN_APPS; + hdr->src_svc = svc->id; + hdr->dest_domain = svc->dest_domain; + hdr->dest_svc = svc->id; + + if (unlikely(apr_cf_debug)) { + APR_PKT_INFO( + "Tx: src_addr[0x%X] dest_addr[0x%X] opcode[0x%X] token[0x%X]", + (hdr->src_domain << 8) | hdr->src_svc, + (hdr->dest_domain << 8) | hdr->dest_svc, hdr->opcode, + hdr->token); + } + + rc = apr_tal_write(clnt->handle, buf, + (struct apr_pkt_priv *)&svc->pkt_owner, + hdr->pkt_size); + if (rc >= 0) { + w_len = rc; + if (w_len != hdr->pkt_size) { + pr_err("%s: Unable to write whole APR pkt successfully: %d\n", + __func__, rc); + rc = -EINVAL; + } + } else { + pr_err_ratelimited("%s: Write APR pkt failed with error %d\n", + __func__, rc); + if (rc == -ECONNRESET) { + pr_err_ratelimited("%s: Received reset error from tal\n", + __func__); + rc = -ENETRESET; + } + } + spin_unlock_irqrestore(&svc->w_lock, flags); + + return rc; +} +EXPORT_SYMBOL(apr_send_pkt); + +int apr_pkt_config(void *handle, struct apr_pkt_cfg *cfg) +{ + struct apr_svc *svc = (struct apr_svc *)handle; + uint16_t dest_id; + uint16_t client_id; + struct apr_client *clnt; + + if (!handle) { + pr_err("%s: Invalid handle\n", __func__); + return -EINVAL; + } + + if (svc->need_reset) { + pr_err("%s: service need reset\n", __func__); + return -ENETRESET; + } + + svc->pkt_owner = cfg->pkt_owner; + dest_id = svc->dest_id; + client_id = svc->client_id; + clnt = &client[dest_id][client_id]; + + return apr_tal_rx_intents_config(clnt->handle, + cfg->intents.num_of_intents, cfg->intents.size); +} + +/** + * apr_register - Clients call to register + * to APR. + * + * @dest: destination processor + * @svc_name: name of service to register as + * @svc_fn: callback function to trigger when response + * ack or packets received from destination processor. + * @src_port: Port number within a service + * @priv: private data of client, passed back in cb fn. + * + * Returns apr_svc handle on success or NULL on failure. + */ +struct apr_svc *apr_register(char *dest, char *svc_name, apr_fn svc_fn, + uint32_t src_port, void *priv) +{ + struct apr_client *clnt; + int client_id = 0; + int svc_idx = 0; + int svc_id = 0; + int dest_id = 0; + int domain_id = 0; + int temp_port = 0; + struct apr_svc *svc = NULL; + int rc = 0; + bool can_open_channel = true; + + if (!dest || !svc_name || !svc_fn) + return NULL; + + if (!strcmp(dest, "ADSP")) + domain_id = APR_DOMAIN_ADSP; + else if (!strcmp(dest, "MODEM")) { + /* Don't request for SMD channels if destination is MODEM, + * as these channels are no longer used and these clients + * are to listen only for MODEM SSR events + */ + can_open_channel = false; + domain_id = APR_DOMAIN_MODEM; + } else { + pr_err("APR: wrong destination\n"); + goto done; + } + + dest_id = apr_get_dest_id(dest); + + if (dest_id == APR_DEST_QDSP6) { + if (apr_get_q6_state() != APR_SUBSYS_LOADED) { + pr_err_ratelimited("%s: adsp not up\n", __func__); + return NULL; + } + pr_debug("%s: adsp Up\n", __func__); + } else if (dest_id == APR_DEST_MODEM) { + if (apr_get_modem_state() == APR_SUBSYS_DOWN) { + if (is_modem_up) { + pr_err("%s: modem shutdown due to SSR, ret", + __func__); + return NULL; + } + pr_debug("%s: Wait for modem to bootup\n", __func__); + rc = wait_event_interruptible_timeout(modem_wait, + (apr_get_modem_state() == APR_SUBSYS_UP), + (1 * HZ)); + if (rc == 0) { + pr_err("%s: Modem is not Up\n", __func__); + return NULL; + } + } + pr_debug("%s: modem Up\n", __func__); + } + + if (apr_get_svc(svc_name, domain_id, &client_id, &svc_idx, &svc_id)) { + pr_err_ratelimited("%s: apr_get_svc failed\n", __func__); + goto done; + } + + clnt = &client[dest_id][client_id]; + mutex_lock(&clnt->m_lock); + if (!clnt->handle && can_open_channel) { + clnt->handle = apr_tal_open(client_id, dest_id, + APR_DL_SMD, apr_cb_func, NULL); + if (!clnt->handle) { + svc = NULL; + pr_err_ratelimited("APR: Unable to open handle\n"); + mutex_unlock(&clnt->m_lock); + goto done; + } + } + mutex_unlock(&clnt->m_lock); + svc = &clnt->svc[svc_idx]; + mutex_lock(&svc->m_lock); + clnt->id = client_id; + if (svc->need_reset) { + mutex_unlock(&svc->m_lock); + pr_err_ratelimited("APR: Service needs reset\n"); + svc = NULL; + goto done; + } + svc->id = svc_id; + svc->dest_id = dest_id; + svc->client_id = client_id; + svc->dest_domain = domain_id; + svc->pkt_owner = APR_PKT_OWNER_DRIVER; + + if (src_port != 0xFFFFFFFF) { + temp_port = ((src_port >> 8) * 8) + (src_port & 0xFF); + pr_debug("port = %d t_port = %d\n", src_port, temp_port); + if (temp_port >= APR_MAX_PORTS || temp_port < 0) { + pr_err("APR: temp_port out of bounds\n"); + mutex_unlock(&svc->m_lock); + return NULL; + } + if (!svc->svc_cnt) + clnt->svc_cnt++; + svc->port_cnt++; + svc->port_fn[temp_port] = svc_fn; + svc->port_priv[temp_port] = priv; + svc->svc_cnt++; + } else { + if (!svc->fn) { + if (!svc->svc_cnt) + clnt->svc_cnt++; + svc->fn = svc_fn; + svc->priv = priv; + svc->svc_cnt++; + } + } + + mutex_unlock(&svc->m_lock); +done: + return svc; +} +EXPORT_SYMBOL(apr_register); + + +void apr_cb_func(void *buf, int len, void *priv) +{ + struct apr_client_data data; + struct apr_client *apr_client; + struct apr_svc *c_svc; + struct apr_hdr *hdr; + uint16_t hdr_size; + uint16_t msg_type; + uint16_t ver; + uint16_t src; + uint16_t svc; + uint16_t clnt; + int i; + int temp_port = 0; + uint32_t *ptr; + + pr_debug("APR2: len = %d\n", len); + ptr = buf; + pr_debug("\n*****************\n"); + for (i = 0; i < len/4; i++) + pr_debug("%x ", ptr[i]); + pr_debug("\n"); + pr_debug("\n*****************\n"); + + if (!buf || len <= APR_HDR_SIZE) { + pr_err("APR: Improper apr pkt received:%pK %d\n", buf, len); + return; + } + hdr = buf; + + ver = hdr->hdr_field; + ver = (ver & 0x000F); + if (ver > APR_PKT_VER + 1) { + pr_err("APR: Wrong version: %d\n", ver); + return; + } + + hdr_size = hdr->hdr_field; + hdr_size = ((hdr_size & 0x00F0) >> 0x4) * 4; + if (hdr_size < APR_HDR_SIZE) { + pr_err("APR: Wrong hdr size:%d\n", hdr_size); + return; + } + + if (hdr->pkt_size < APR_HDR_SIZE) { + pr_err("APR: Wrong paket size\n"); + return; + } + + if (hdr->pkt_size < hdr_size) { + pr_err("APR: Packet size less than header size\n"); + return; + } + + msg_type = hdr->hdr_field; + msg_type = (msg_type >> 0x08) & 0x0003; + if (msg_type >= APR_MSG_TYPE_MAX && msg_type != APR_BASIC_RSP_RESULT) { + pr_err("APR: Wrong message type: %d\n", msg_type); + return; + } + + if (hdr->src_domain >= APR_DOMAIN_MAX || + hdr->dest_domain >= APR_DOMAIN_MAX || + hdr->src_svc >= APR_SVC_MAX || + hdr->dest_svc >= APR_SVC_MAX) { + pr_err("APR: Wrong APR header\n"); + return; + } + + svc = hdr->dest_svc; + if (hdr->src_domain == APR_DOMAIN_MODEM) { + if (svc == APR_SVC_MVS || svc == APR_SVC_MVM || + svc == APR_SVC_CVS || svc == APR_SVC_CVP || + svc == APR_SVC_TEST_CLIENT) + clnt = APR_CLIENT_VOICE; + else { + pr_err("APR: Wrong svc :%d\n", svc); + return; + } + } else if (hdr->src_domain == APR_DOMAIN_ADSP) { + if (svc == APR_SVC_AFE || svc == APR_SVC_ASM || + svc == APR_SVC_VSM || svc == APR_SVC_VPM || + svc == APR_SVC_ADM || svc == APR_SVC_ADSP_CORE || + svc == APR_SVC_USM || + svc == APR_SVC_TEST_CLIENT || svc == APR_SVC_ADSP_MVM || + svc == APR_SVC_ADSP_CVS || svc == APR_SVC_ADSP_CVP || + svc == APR_SVC_LSM) + clnt = APR_CLIENT_AUDIO; + else if (svc == APR_SVC_VIDC) + clnt = APR_CLIENT_AUDIO; + else { + pr_err("APR: Wrong svc :%d\n", svc); + return; + } + } else { + pr_err("APR: Pkt from wrong source: %d\n", hdr->src_domain); + return; + } + + src = apr_get_data_src(hdr); + if (src == APR_DEST_MAX) + return; + + pr_debug("src =%d clnt = %d\n", src, clnt); + apr_client = &client[src][clnt]; + for (i = 0; i < APR_SVC_MAX; i++) + if (apr_client->svc[i].id == svc) { + pr_debug("%d\n", apr_client->svc[i].id); + c_svc = &apr_client->svc[i]; + break; + } + + if (i == APR_SVC_MAX) { + pr_err("APR: service is not registered\n"); + return; + } + pr_debug("svc_idx = %d\n", i); + pr_debug("%x %x %x %pK %pK\n", c_svc->id, c_svc->dest_id, + c_svc->client_id, c_svc->fn, c_svc->priv); + data.payload_size = hdr->pkt_size - hdr_size; + data.opcode = hdr->opcode; + data.src = src; + data.src_port = hdr->src_port; + data.dest_port = hdr->dest_port; + data.token = hdr->token; + data.msg_type = msg_type; + data.payload = NULL; + if (data.payload_size > 0) + data.payload = (char *)hdr + hdr_size; + + if (unlikely(apr_cf_debug)) { + if (hdr->opcode == APR_BASIC_RSP_RESULT && data.payload) { + uint32_t *ptr = data.payload; + + APR_PKT_INFO( + "Rx: src_addr[0x%X] dest_addr[0x%X] opcode[0x%X] token[0x%X] rc[0x%X]", + (hdr->src_domain << 8) | hdr->src_svc, + (hdr->dest_domain << 8) | hdr->dest_svc, + hdr->opcode, hdr->token, ptr[1]); + } else { + APR_PKT_INFO( + "Rx: src_addr[0x%X] dest_addr[0x%X] opcode[0x%X] token[0x%X]", + (hdr->src_domain << 8) | hdr->src_svc, + (hdr->dest_domain << 8) | hdr->dest_svc, hdr->opcode, + hdr->token); + } + } + + temp_port = ((data.dest_port >> 8) * 8) + (data.dest_port & 0xFF); + if (((temp_port >= 0) && (temp_port < APR_MAX_PORTS)) + && (c_svc->port_cnt && c_svc->port_fn[temp_port])) + c_svc->port_fn[temp_port](&data, + c_svc->port_priv[temp_port]); + else if (c_svc->fn) + c_svc->fn(&data, c_svc->priv); + else + pr_err("APR: Rxed a packet for NULL callback\n"); +} + +int apr_get_svc(const char *svc_name, int domain_id, int *client_id, + int *svc_idx, int *svc_id) +{ + int i; + int size; + struct apr_svc_table *tbl; + int ret = 0; + + if (domain_id == APR_DOMAIN_ADSP) { + tbl = (struct apr_svc_table *)&svc_tbl_qdsp6; + size = ARRAY_SIZE(svc_tbl_qdsp6); + } else { + tbl = (struct apr_svc_table *)&svc_tbl_voice; + size = ARRAY_SIZE(svc_tbl_voice); + } + + for (i = 0; i < size; i++) { + if (!strcmp(svc_name, tbl[i].name)) { + *client_id = tbl[i].client_id; + *svc_idx = tbl[i].idx; + *svc_id = tbl[i].id; + break; + } + } + + pr_debug("%s: svc_name = %s c_id = %d domain_id = %d\n", + __func__, svc_name, *client_id, domain_id); + if (i == size) { + pr_err("%s: APR: Wrong svc name %s\n", __func__, svc_name); + ret = -EINVAL; + } + + return ret; +} + +static void apr_reset_deregister(struct work_struct *work) +{ + struct apr_svc *handle = NULL; + struct apr_reset_work *apr_reset = + container_of(work, struct apr_reset_work, work); + + handle = apr_reset->handle; + pr_debug("%s:handle[%pK]\n", __func__, handle); + apr_deregister(handle); + kfree(apr_reset); +} + +/** + * apr_start_rx_rt - Clients call to vote for thread + * priority upgrade whenever needed. + * + * @handle: APR service handle + * + * Returns 0 on success or error otherwise. + */ +int apr_start_rx_rt(void *handle) +{ + int rc = 0; + struct apr_svc *svc = handle; + uint16_t dest_id = 0; + uint16_t client_id = 0; + + if (!svc) { + pr_err("%s: Invalid APR handle\n", __func__); + return -EINVAL; + } + + mutex_lock(&svc->m_lock); + dest_id = svc->dest_id; + client_id = svc->client_id; + + if ((client_id >= APR_CLIENT_MAX) || (dest_id >= APR_DEST_MAX)) { + pr_err("%s: %s invalid. client_id = %u, dest_id = %u\n", + __func__, + client_id >= APR_CLIENT_MAX ? "Client ID" : "Dest ID", + client_id, dest_id); + rc = -EINVAL; + goto exit; + } + + if (!client[dest_id][client_id].handle) { + pr_err("%s: Client handle is NULL\n", __func__); + rc = -EINVAL; + goto exit; + } + + rc = apr_tal_start_rx_rt(client[dest_id][client_id].handle); + if (rc) + pr_err("%s: failed to set RT thread priority for APR RX. rc = %d\n", + __func__, rc); + +exit: + mutex_unlock(&svc->m_lock); + return rc; +} +EXPORT_SYMBOL(apr_start_rx_rt); + +/** + * apr_end_rx_rt - Clients call to unvote for thread + * priority upgrade (perviously voted with + * apr_start_rx_rt()). + * + * @handle: APR service handle + * + * Returns 0 on success or error otherwise. + */ +int apr_end_rx_rt(void *handle) +{ + int rc = 0; + struct apr_svc *svc = handle; + uint16_t dest_id = 0; + uint16_t client_id = 0; + + if (!svc) { + pr_err("%s: Invalid APR handle\n", __func__); + return -EINVAL; + } + + mutex_lock(&svc->m_lock); + dest_id = svc->dest_id; + client_id = svc->client_id; + + if ((client_id >= APR_CLIENT_MAX) || (dest_id >= APR_DEST_MAX)) { + pr_err("%s: %s invalid. client_id = %u, dest_id = %u\n", + __func__, + client_id >= APR_CLIENT_MAX ? "Client ID" : "Dest ID", + client_id, dest_id); + rc = -EINVAL; + goto exit; + } + + if (!client[dest_id][client_id].handle) { + pr_err("%s: Client handle is NULL\n", __func__); + rc = -EINVAL; + goto exit; + } + + rc = apr_tal_end_rx_rt(client[dest_id][client_id].handle); + if (rc) + pr_err("%s: failed to reset RT thread priority for APR RX. rc = %d\n", + __func__, rc); + +exit: + mutex_unlock(&svc->m_lock); + return rc; +} +EXPORT_SYMBOL(apr_end_rx_rt); + +/** + * apr_deregister - Clients call to de-register + * from APR. + * + * @handle: APR service handle to de-register + * + * Returns 0 on success or -EINVAL on error. + */ +int apr_deregister(void *handle) +{ + struct apr_svc *svc = handle; + struct apr_client *clnt; + uint16_t dest_id; + uint16_t client_id; + + if (!handle) + return -EINVAL; + + mutex_lock(&svc->m_lock); + if (!svc->svc_cnt) { + pr_err("%s: svc already deregistered. svc = %pK\n", + __func__, svc); + mutex_unlock(&svc->m_lock); + return -EINVAL; + } + + dest_id = svc->dest_id; + client_id = svc->client_id; + clnt = &client[dest_id][client_id]; + + if (svc->svc_cnt > 0) { + if (svc->port_cnt) + svc->port_cnt--; + svc->svc_cnt--; + if (!svc->svc_cnt) { + client[dest_id][client_id].svc_cnt--; + pr_debug("%s: service is reset %pK\n", __func__, svc); + } + } + + if (!svc->svc_cnt) { + svc->priv = NULL; + svc->id = 0; + svc->fn = NULL; + svc->dest_id = 0; + svc->client_id = 0; + svc->need_reset = 0x0; + } + if (client[dest_id][client_id].handle && + !client[dest_id][client_id].svc_cnt) { + apr_tal_close(client[dest_id][client_id].handle); + client[dest_id][client_id].handle = NULL; + } + mutex_unlock(&svc->m_lock); + + return 0; +} +EXPORT_SYMBOL(apr_deregister); + +/** + * apr_reset - sets up workqueue to de-register + * the given APR service handle. + * + * @handle: APR service handle + * + */ +void apr_reset(void *handle) +{ + struct apr_reset_work *apr_reset_worker = NULL; + + if (!handle) + return; + pr_debug("%s: handle[%pK]\n", __func__, handle); + + if (apr_reset_workqueue == NULL) { + pr_err("%s: apr_reset_workqueue is NULL\n", __func__); + return; + } + + apr_reset_worker = kzalloc(sizeof(struct apr_reset_work), + GFP_ATOMIC); + + if (apr_reset_worker == NULL) { + pr_err("%s: mem failure\n", __func__); + return; + } + + apr_reset_worker->handle = handle; + INIT_WORK(&apr_reset_worker->work, apr_reset_deregister); + queue_work(apr_reset_workqueue, &apr_reset_worker->work); +} +EXPORT_SYMBOL(apr_reset); + +/* Dispatch the Reset events to Modem and audio clients */ +static void dispatch_event(unsigned long code, uint16_t proc) +{ + struct apr_client *apr_client; + struct apr_client_data data; + struct apr_svc *svc; + uint16_t clnt; + int i, j; + + memset(&data, 0, sizeof(data)); + data.opcode = RESET_EVENTS; + data.reset_event = code; + + /* Service domain can be different from the processor */ + data.reset_proc = apr_get_reset_domain(proc); + + clnt = APR_CLIENT_AUDIO; + apr_client = &client[proc][clnt]; + for (i = 0; i < APR_SVC_MAX; i++) { + mutex_lock(&apr_client->svc[i].m_lock); + if (apr_client->svc[i].fn) { + apr_client->svc[i].need_reset = 0x1; + apr_client->svc[i].fn(&data, apr_client->svc[i].priv); + } + if (apr_client->svc[i].port_cnt) { + svc = &(apr_client->svc[i]); + svc->need_reset = 0x1; + for (j = 0; j < APR_MAX_PORTS; j++) + if (svc->port_fn[j]) + svc->port_fn[j](&data, + svc->port_priv[j]); + } + mutex_unlock(&apr_client->svc[i].m_lock); + } + + clnt = APR_CLIENT_VOICE; + apr_client = &client[proc][clnt]; + for (i = 0; i < APR_SVC_MAX; i++) { + mutex_lock(&apr_client->svc[i].m_lock); + if (apr_client->svc[i].fn) { + apr_client->svc[i].need_reset = 0x1; + apr_client->svc[i].fn(&data, apr_client->svc[i].priv); + } + if (apr_client->svc[i].port_cnt) { + svc = &(apr_client->svc[i]); + svc->need_reset = 0x1; + for (j = 0; j < APR_MAX_PORTS; j++) + if (svc->port_fn[j]) + svc->port_fn[j](&data, + svc->port_priv[j]); + } + mutex_unlock(&apr_client->svc[i].m_lock); + } +} + +static int apr_notifier_service_cb(struct notifier_block *this, + unsigned long opcode, void *data) +{ + struct audio_notifier_cb_data *cb_data = data; + + if (cb_data == NULL) { + pr_err("%s: Callback data is NULL!\n", __func__); + goto done; + } + + pr_debug("%s: Service opcode 0x%lx, domain %d\n", + __func__, opcode, cb_data->domain); + + switch (opcode) { + case AUDIO_NOTIFIER_SERVICE_DOWN: + /* + * Use flag to ignore down notifications during + * initial boot. There is no benefit from error + * recovery notifications during initial boot + * up since everything is expected to be down. + */ + spin_lock(&apr_priv->apr_lock); + if (apr_priv->is_initial_boot) { + spin_unlock(&apr_priv->apr_lock); + break; + } + spin_unlock(&apr_priv->apr_lock); + if (cb_data->domain == AUDIO_NOTIFIER_MODEM_DOMAIN) + apr_modem_down(opcode); + else + apr_adsp_down(opcode); + break; + case AUDIO_NOTIFIER_SERVICE_UP: + if (cb_data->domain == AUDIO_NOTIFIER_MODEM_DOMAIN) + apr_modem_up(); + else + apr_adsp_up(); + spin_lock(&apr_priv->apr_lock); + apr_priv->is_initial_boot = false; + spin_unlock(&apr_priv->apr_lock); + break; + default: + break; + } +done: + return NOTIFY_OK; +} + +static struct notifier_block adsp_service_nb = { + .notifier_call = apr_notifier_service_cb, + .priority = 0, +}; + +static struct notifier_block modem_service_nb = { + .notifier_call = apr_notifier_service_cb, + .priority = 0, +}; + +#ifdef CONFIG_DEBUG_FS +static int __init apr_debug_init(void) +{ + debugfs_apr_debug = debugfs_create_file("msm_apr_debug", + S_IFREG | 0444, NULL, NULL, + &apr_debug_ops); + return 0; +} +#else +static int __init apr_debug_init(void) +( + return 0; +) +#endif + +static void apr_cleanup(void) +{ + int i, j, k; + + of_platform_depopulate(apr_priv->dev); + subsys_notif_deregister(subsys_name); + if (apr_reset_workqueue) { + flush_workqueue(apr_reset_workqueue); + destroy_workqueue(apr_reset_workqueue); + } + mutex_destroy(&q6.lock); + for (i = 0; i < APR_DEST_MAX; i++) { + for (j = 0; j < APR_CLIENT_MAX; j++) { + mutex_destroy(&client[i][j].m_lock); + for (k = 0; k < APR_SVC_MAX; k++) + mutex_destroy(&client[i][j].svc[k].m_lock); + } + } + debugfs_remove(debugfs_apr_debug); +} + +static int apr_probe(struct platform_device *pdev) +{ + int i, j, k, ret = 0; + + init_waitqueue_head(&modem_wait); + + apr_priv = devm_kzalloc(&pdev->dev, sizeof(*apr_priv), GFP_KERNEL); + if (!apr_priv) + return -ENOMEM; + + apr_priv->dev = &pdev->dev; + spin_lock_init(&apr_priv->apr_lock); + INIT_WORK(&apr_priv->add_chld_dev_work, apr_add_child_devices); + + for (i = 0; i < APR_DEST_MAX; i++) + for (j = 0; j < APR_CLIENT_MAX; j++) { + mutex_init(&client[i][j].m_lock); + for (k = 0; k < APR_SVC_MAX; k++) { + mutex_init(&client[i][j].svc[k].m_lock); + spin_lock_init(&client[i][j].svc[k].w_lock); + } + } + apr_set_subsys_state(); + mutex_init(&q6.lock); + apr_reset_workqueue = create_singlethread_workqueue("apr_driver"); + if (!apr_reset_workqueue) { + apr_priv = NULL; + return -ENOMEM; + } + + apr_pkt_ctx = ipc_log_context_create(APR_PKT_IPC_LOG_PAGE_CNT, + "apr", 0); + if (!apr_pkt_ctx) + pr_err("%s: Unable to create ipc log context\n", __func__); + + spin_lock(&apr_priv->apr_lock); + apr_priv->is_initial_boot = true; + spin_unlock(&apr_priv->apr_lock); + ret = of_property_read_string(pdev->dev.of_node, + "qcom,subsys-name", + (const char **)(&subsys_name)); + if (ret) { + pr_err("%s: missing subsys-name entry in dt node\n", __func__); + return -EINVAL; + } + + if (!strcmp(subsys_name, "apr_adsp")) { + subsys_notif_register("apr_adsp", + AUDIO_NOTIFIER_ADSP_DOMAIN, + &adsp_service_nb); + } else if (!strcmp(subsys_name, "apr_modem")) { + subsys_notif_register("apr_modem", + AUDIO_NOTIFIER_MODEM_DOMAIN, + &modem_service_nb); + } else { + pr_err("%s: invalid subsys-name %s\n", __func__, subsys_name); + return -EINVAL; + } + + apr_tal_init(); + + ret = snd_event_client_register(&pdev->dev, &apr_ssr_ops, NULL); + if (ret) { + pr_err("%s: Registration with SND event fwk failed ret = %d\n", + __func__, ret); + ret = 0; + } + + return apr_debug_init(); +} + +static int apr_remove(struct platform_device *pdev) +{ + snd_event_client_deregister(&pdev->dev); + apr_cleanup(); + apr_tal_exit(); + apr_priv = NULL; + return 0; +} + +static const struct of_device_id apr_machine_of_match[] = { + { .compatible = "qcom,msm-audio-apr", }, + {}, +}; + +static struct platform_driver apr_driver = { + .probe = apr_probe, + .remove = apr_remove, + .driver = { + .name = "audio_apr", + .owner = THIS_MODULE, + .of_match_table = apr_machine_of_match, + } +}; + +module_platform_driver(apr_driver); + +MODULE_DESCRIPTION("APR DRIVER"); +MODULE_LICENSE("GPL v2"); +MODULE_DEVICE_TABLE(of, apr_machine_of_match); diff --git a/audio-kernel/ipc/apr_tal_rpmsg.c b/audio-kernel/ipc/apr_tal_rpmsg.c new file mode 100644 index 000000000..aaadca486 --- /dev/null +++ b/audio-kernel/ipc/apr_tal_rpmsg.c @@ -0,0 +1,303 @@ +/* + * Copyright (c) 2017-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 +#include +#include +#include +#include +#include +#include + +enum apr_channel_state { + APR_CH_DISCONNECTED, + APR_CH_CONNECTED, +}; + +#define APR_MAXIMUM_NUM_OF_RETRIES 2 + +static struct apr_svc_ch_dev + apr_svc_ch[APR_DL_MAX][APR_DEST_MAX][APR_CLIENT_MAX]; + +/** + * apr_tal_write() - Write a message across to the remote processor + * @apr_ch: apr channel handle + * @data: buffer that needs to be transferred over the channel + * @pkt_priv: private data of the packet + * @len: length of the buffer + * + * Returns len of buffer successfully transferred on success + * and an appropriate error value on failure. + */ +int apr_tal_write(struct apr_svc_ch_dev *apr_ch, void *data, + struct apr_pkt_priv *pkt_priv, int len) +{ + int rc = 0, retries = 0; + unsigned long flags; + struct rpmsg_device *rpdev = NULL; + + if (!apr_ch || len > APR_MAX_BUF || + apr_ch->channel_state != APR_CH_CONNECTED) + return -EINVAL; + + spin_lock_irqsave(&apr_ch->w_lock, flags); + rpdev = apr_ch->handle; + if (!rpdev) { + spin_unlock_irqrestore(&apr_ch->w_lock, flags); + return -EINVAL; + } + + do { + if (rc == -EAGAIN) + udelay(50); + rc = rpmsg_trysend(rpdev->ept, data, len); + } while (rc == -EAGAIN && retries++ < APR_MAXIMUM_NUM_OF_RETRIES); + spin_unlock_irqrestore(&apr_ch->w_lock, flags); + + if (rc) + pr_err("%s: Unable to send the packet, rc:%d\n", __func__, rc); + else + rc = len; + + return rc; +} +EXPORT_SYMBOL(apr_tal_write); + +/** + * apr_tal_rx_intents_config() - Configure glink intents for remote processor + * @apr_ch: apr channel handle + * @num_of_intents: number of intents + * @size: size of the intents + * + * This api is not supported with RPMSG. Returns 0 to indicate success + */ +int apr_tal_rx_intents_config(struct apr_svc_ch_dev *apr_ch, + int num_of_intents, uint32_t size) +{ + pr_debug("%s: NO-OP\n", __func__); + return 0; +} +EXPORT_SYMBOL(apr_tal_rx_intents_config); + +/** + * apr_tal_start_rx_rt() - Set RT thread priority for APR RX transfer + * @apr_ch: apr channel handle + * + * This api is not supported with RPMSG as message transfer occurs + * in client's context. Returns 0 to indicate success. + */ +int apr_tal_start_rx_rt(struct apr_svc_ch_dev *apr_ch) +{ + pr_debug("%s: NO-OP\n", __func__); + return 0; +} +EXPORT_SYMBOL(apr_tal_start_rx_rt); + +/** + * apr_tal_end_rx_rt() - Remove RT thread priority for APR RX transfer + * @apr_ch: apr channel handle + * + * This api is not supported with RPMSG. Returns 0 to indicate success + */ +int apr_tal_end_rx_rt(struct apr_svc_ch_dev *apr_ch) +{ + pr_debug("%s: NO-OP\n", __func__); + return 0; +} +EXPORT_SYMBOL(apr_tal_end_rx_rt); + +/** + * apr_tal_open() - Open a transport channel for data transfer + * on remote processor. + * @clnt: apr client, audio or voice + * @dest: destination remote processor for which apr channel is requested for. + * @dl: type of data link + * @func: callback function to handle data transfer from remote processor + * @priv: private data of the client + * + * Returns apr_svc_ch_dev handle on success and NULL on failure. + */ +struct apr_svc_ch_dev *apr_tal_open(uint32_t clnt, uint32_t dest, uint32_t dl, + apr_svc_cb_fn func, void *priv) +{ + int rc = 0; + struct apr_svc_ch_dev *apr_ch = NULL; + + if ((clnt != APR_CLIENT_AUDIO) || (dest != APR_DEST_QDSP6) || + (dl != APR_DL_SMD)) { + pr_err("%s: Invalid params, clnt:%d, dest:%d, dl:%d\n", + __func__, clnt, dest, dl); + return NULL; + } + + apr_ch = &apr_svc_ch[APR_DL_SMD][APR_DEST_QDSP6][APR_CLIENT_AUDIO]; + mutex_lock(&apr_ch->m_lock); + if (!apr_ch->handle) { + rc = wait_event_timeout(apr_ch->wait, + (apr_ch->channel_state == APR_CH_CONNECTED), 5 * HZ); + + if (rc == 0) { + pr_err("%s: TIMEOUT for APR_CH_CONNECTED event\n", + __func__); + rc = -ETIMEDOUT; + goto unlock; + } + } + + pr_debug("%s: Channel connected, returning handle :%pK\n", + __func__, apr_ch->handle); + apr_ch->func = func; + apr_ch->priv = priv; + +unlock: + mutex_unlock(&apr_ch->m_lock); + return rc ? NULL : apr_ch; +} +EXPORT_SYMBOL(apr_tal_open); + +/** + * apr_tal_close() - Close transport channel on remote processor. + * @apr_ch: apr channel handle + * + * Returns 0 on success and an appropriate error value on failure. + */ +int apr_tal_close(struct apr_svc_ch_dev *apr_ch) +{ + int rc = 0; + + if (!apr_ch || !apr_ch->handle) { + rc = -EINVAL; + goto exit; + } + + mutex_lock(&apr_ch->m_lock); + apr_ch->func = NULL; + apr_ch->priv = NULL; + mutex_unlock(&apr_ch->m_lock); + +exit: + return rc; +} +EXPORT_SYMBOL(apr_tal_close); + +static int apr_tal_rpmsg_callback(struct rpmsg_device *rpdev, + void *data, int len, void *priv, u32 addr) +{ + struct apr_svc_ch_dev *apr_ch = dev_get_drvdata(&rpdev->dev); + unsigned long flags; + + if (!apr_ch || !data) { + pr_err("%s: Invalid apr_ch or ptr\n", __func__); + return -EINVAL; + } + + dev_dbg(&rpdev->dev, "%s: Rx packet received, len:%d\n", + __func__, len); + + spin_lock_irqsave(&apr_ch->r_lock, flags); + if (apr_ch->func) + apr_ch->func((void *)data, len, apr_ch->priv); + spin_unlock_irqrestore(&apr_ch->r_lock, flags); + + return 0; +} + +static int apr_tal_rpmsg_probe(struct rpmsg_device *rpdev) +{ + struct apr_svc_ch_dev *apr_ch = NULL; + + if (!strcmp(rpdev->id.name, "apr_audio_svc")) { + dev_info(&rpdev->dev, "%s: Channel[%s] state[Up]\n", + __func__, rpdev->id.name); + + apr_ch = + &apr_svc_ch[APR_DL_SMD][APR_DEST_QDSP6][APR_CLIENT_AUDIO]; + apr_ch->handle = rpdev; + apr_ch->channel_state = APR_CH_CONNECTED; + dev_set_drvdata(&rpdev->dev, apr_ch); + wake_up(&apr_ch->wait); + } else { + dev_err(&rpdev->dev, "%s, Invalid Channel [%s]\n", + __func__, rpdev->id.name); + return -EINVAL; + } + + return 0; +} + +static void apr_tal_rpmsg_remove(struct rpmsg_device *rpdev) +{ + struct apr_svc_ch_dev *apr_ch = dev_get_drvdata(&rpdev->dev); + + if (!apr_ch) { + dev_err(&rpdev->dev, "%s: Invalid apr_ch\n", __func__); + return; + } + + dev_info(&rpdev->dev, "%s: Channel[%s] state[Down]\n", + __func__, rpdev->id.name); + apr_ch->handle = NULL; + apr_ch->channel_state = APR_CH_DISCONNECTED; + dev_set_drvdata(&rpdev->dev, NULL); +} + +static const struct rpmsg_device_id apr_tal_rpmsg_match[] = { + { "apr_audio_svc" }, + {} +}; + +static struct rpmsg_driver apr_tal_rpmsg_driver = { + .probe = apr_tal_rpmsg_probe, + .remove = apr_tal_rpmsg_remove, + .callback = apr_tal_rpmsg_callback, + .id_table = apr_tal_rpmsg_match, + .drv = { + .name = "apr_tal_rpmsg", + }, +}; + +/** + * apr_tal_int() - Registers rpmsg driver with rpmsg framework. + * + * Returns 0 on success and an appropriate error value on failure. + */ +int apr_tal_init(void) +{ + int i, j, k; + int ret; + + memset(apr_svc_ch, 0, sizeof(struct apr_svc_ch_dev)); + for (i = 0; i < APR_DL_MAX; i++) { + for (j = 0; j < APR_DEST_MAX; j++) { + for (k = 0; k < APR_CLIENT_MAX; k++) { + init_waitqueue_head(&apr_svc_ch[i][j][k].wait); + spin_lock_init(&apr_svc_ch[i][j][k].w_lock); + spin_lock_init(&apr_svc_ch[i][j][k].r_lock); + mutex_init(&apr_svc_ch[i][j][k].m_lock); + } + } + } + ret = register_rpmsg_driver(&apr_tal_rpmsg_driver); + return ret; +} +EXPORT_SYMBOL(apr_tal_init); + +/** + * apr_tal_exit() - De-register rpmsg driver with rpmsg framework. + */ +void apr_tal_exit(void) +{ + unregister_rpmsg_driver(&apr_tal_rpmsg_driver); +} +EXPORT_SYMBOL(apr_tal_exit); + diff --git a/audio-kernel/ipc/apr_v2.c b/audio-kernel/ipc/apr_v2.c new file mode 100644 index 000000000..e85173cd2 --- /dev/null +++ b/audio-kernel/ipc/apr_v2.c @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2012-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 + * 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 +#include +#include +#include +#include +#include +#include + +/** + * apr_get_subsys_state - get Q6 subsys status + * + * Returns apr_subsys_state + */ +enum apr_subsys_state apr_get_subsys_state(void) +{ + return apr_get_q6_state(); +} +EXPORT_SYMBOL(apr_get_subsys_state); + +void apr_set_subsys_state(void) +{ + apr_set_q6_state(APR_SUBSYS_DOWN); + apr_set_modem_state(APR_SUBSYS_UP); +} + +uint16_t apr_get_data_src(struct apr_hdr *hdr) +{ + if (hdr->src_domain == APR_DOMAIN_MODEM) + return APR_DEST_MODEM; + else if (hdr->src_domain == APR_DOMAIN_ADSP) + return APR_DEST_QDSP6; + + pr_err("APR: Pkt from wrong source: %d\n", hdr->src_domain); + return APR_DEST_MAX; /*RETURN INVALID VALUE*/ +} + +int apr_get_dest_id(char *dest) +{ + if (!strcmp(dest, "ADSP")) + return APR_DEST_QDSP6; + else + return APR_DEST_MODEM; +} + +void subsys_notif_register(char *client_name, int domain, + struct notifier_block *nb) +{ + int ret; + + ret = audio_notifier_register(client_name, domain, nb); + if (ret < 0) + pr_err("%s: Audio notifier register failed for domain %d ret = %d\n", + __func__, domain, ret); +} + +void subsys_notif_deregister(char *client_name) +{ + int ret; + + ret = audio_notifier_deregister(client_name); + if (ret < 0) + pr_err("%s: Audio notifier de-register failed for client %s\n", + __func__, client_name); +} + +uint16_t apr_get_reset_domain(uint16_t proc) +{ + return proc; +} diff --git a/audio-kernel/ipc/apr_v3.c b/audio-kernel/ipc/apr_v3.c new file mode 100644 index 000000000..fa68daf55 --- /dev/null +++ b/audio-kernel/ipc/apr_v3.c @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2013-2016, 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 +#include +#include +#include +#include +#include +#include + +#define DEST_ID APR_DEST_MODEM + +enum apr_subsys_state apr_get_subsys_state(void) +{ + return apr_get_modem_state(); +} + +void apr_set_subsys_state(void) +{ + apr_set_modem_state(APR_SUBSYS_DOWN); +} + +uint16_t apr_get_data_src(struct apr_hdr *hdr) +{ + return DEST_ID; +} + +int apr_get_dest_id(char *dest) +{ + return DEST_ID; +} + +void subsys_notif_register(char *client_name, int domain, + struct notifier_block *nb) +{ + int ret; + + if (domain != AUDIO_NOTIFIER_MODEM_DOMAIN) { + pr_debug("%s: Unused domain %d not registering with notifier\n", + __func__, domain); + return; + } + + ret = audio_notifier_register(client_name, domain, nb); + if (ret < 0) + pr_err("%s: Audio notifier register failed for domain %d ret = %d\n", + __func__, domain, ret); +} + +void subsys_notif_deregister(char *client_name) +{ + int ret; + + ret = audio_notifier_deregister(client_name); + if (ret < 0) + pr_err("%s: Audio notifier de-register failed for client %s\n", + __func__, client_name); +} + +uint16_t apr_get_reset_domain(uint16_t proc) +{ + return APR_DEST_QDSP6; +} diff --git a/audio-kernel/ipc/wcd-dsp-glink.c b/audio-kernel/ipc/wcd-dsp-glink.c new file mode 100644 index 000000000..79ff26aef --- /dev/null +++ b/audio-kernel/ipc/wcd-dsp-glink.c @@ -0,0 +1,781 @@ +/* Copyright (c) 2016-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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "sound/wcd-dsp-glink.h" + +#define WDSP_GLINK_DRIVER_NAME "wcd-dsp-glink" +#define WDSP_MAX_WRITE_SIZE (256 * 1024) +#define WDSP_MAX_READ_SIZE (4 * 1024) +#define WDSP_WRITE_PKT_SIZE (sizeof(struct wdsp_write_pkt)) +#define WDSP_CMD_PKT_SIZE (sizeof(struct wdsp_cmd_pkt)) + +#define MINOR_NUMBER_COUNT 1 +#define RESP_QUEUE_SIZE 3 +#define TIMEOUT_MS 2000 + +enum wdsp_ch_state { + WDSP_CH_DISCONNECTED, + WDSP_CH_CONNECTED, +}; + +struct wdsp_glink_dev { + struct class *cls; + struct device *dev; + struct cdev cdev; + dev_t dev_num; +}; + +struct wdsp_rsp_que { + /* Size of valid data in buffer */ + u32 buf_size; + + /* Response buffer */ + u8 buf[WDSP_MAX_READ_SIZE]; +}; + +struct wdsp_ch { + struct wdsp_glink_priv *wpriv; + /* rpmsg handle */ + void *handle; + /* Channel states like connect, disconnect */ + int ch_state; + char ch_name[RPMSG_NAME_SIZE]; + spinlock_t ch_lock; +}; + +struct wdsp_tx_buf { + struct work_struct tx_work; + + /* Glink channel information */ + struct wdsp_ch *ch; + + /* Tx buffer to send to glink */ + u8 buf[0]; +}; + +struct wdsp_glink_priv { + /* Respone buffer related */ + u8 rsp_cnt; + struct wdsp_rsp_que rsp[RESP_QUEUE_SIZE]; + u8 write_idx; + u8 read_idx; + struct completion rsp_complete; + spinlock_t rsp_lock; + + /* Glink channel related */ + int no_of_channels; + struct wdsp_ch **ch; + struct workqueue_struct *work_queue; + /* Wait for all channels state before sending any command */ + wait_queue_head_t ch_state_wait; + + struct wdsp_glink_dev *wdev; + struct device *dev; +}; +static struct wdsp_glink_priv *wpriv; + +static struct wdsp_ch *wdsp_get_ch(char *ch_name) +{ + int i; + + for (i = 0; i < wpriv->no_of_channels; i++) { + if (!strcmp(ch_name, wpriv->ch[i]->ch_name)) + return wpriv->ch[i]; + } + return NULL; +} + +/* + * wdsp_rpmsg_callback - Rpmsg callback for responses + * rpdev: Rpmsg device structure + * data: Pointer to the Rx data + * len: Size of the Rx data + * priv: Private pointer to the channel + * addr: Address variable + * Returns 0 on success and an appropriate error value on failure + */ +static int wdsp_rpmsg_callback(struct rpmsg_device *rpdev, void *data, + int len, void *priv, u32 addr__unused) +{ + struct wdsp_ch *ch = dev_get_drvdata(&rpdev->dev); + struct wdsp_glink_priv *wpriv; + unsigned long flags; + u8 *rx_buf; + u8 rsp_cnt = 0; + + if (!ch || !data) { + pr_err("%s: Invalid ch or data\n", __func__); + return -EINVAL; + } + + wpriv = ch->wpriv; + rx_buf = (u8 *)data; + if (len > WDSP_MAX_READ_SIZE) { + dev_info_ratelimited(wpriv->dev, "%s: Size %d is greater than allowed %d\n", + __func__, len, WDSP_MAX_READ_SIZE); + len = WDSP_MAX_READ_SIZE; + } + dev_dbg_ratelimited(wpriv->dev, "%s: copy into buffer %d\n", __func__, + wpriv->rsp_cnt); + + if (wpriv->rsp_cnt >= RESP_QUEUE_SIZE) { + dev_info_ratelimited(wpriv->dev, "%s: Resp Queue is Full. Ignore new one.\n", + __func__); + return -EINVAL; + } + spin_lock_irqsave(&wpriv->rsp_lock, flags); + rsp_cnt = wpriv->rsp_cnt; + memcpy(wpriv->rsp[wpriv->write_idx].buf, rx_buf, len); + wpriv->rsp[wpriv->write_idx].buf_size = len; + wpriv->write_idx = (wpriv->write_idx + 1) % RESP_QUEUE_SIZE; + wpriv->rsp_cnt = ++rsp_cnt; + spin_unlock_irqrestore(&wpriv->rsp_lock, flags); + + complete(&wpriv->rsp_complete); + + return 0; +} + +/* + * wdsp_rpmsg_probe - Rpmsg channel probe function + * rpdev: Rpmsg device structure + * Returns 0 on success and an appropriate error value on failure + */ +static int wdsp_rpmsg_probe(struct rpmsg_device *rpdev) +{ + struct wdsp_ch *ch; + + ch = wdsp_get_ch(rpdev->id.name); + if (!ch) { + dev_err(&rpdev->dev, "%s, Invalid Channel [%s]\n", + __func__, rpdev->id.name); + return -EINVAL; + } + + dev_dbg(&rpdev->dev, "%s: Channel[%s] state[Up]\n", + __func__, rpdev->id.name); + + spin_lock(&ch->ch_lock); + ch->handle = rpdev; + ch->ch_state = WDSP_CH_CONNECTED; + spin_unlock(&ch->ch_lock); + dev_set_drvdata(&rpdev->dev, ch); + wake_up(&wpriv->ch_state_wait); + + return 0; +} + +/* + * wdsp_rpmsg_remove - Rpmsg channel remove function + * rpdev: Rpmsg device structure + */ +static void wdsp_rpmsg_remove(struct rpmsg_device *rpdev) +{ + struct wdsp_ch *ch = dev_get_drvdata(&rpdev->dev); + + if (!ch) { + dev_err(&rpdev->dev, "%s: Invalid ch\n", __func__); + return; + } + + dev_dbg(&rpdev->dev, "%s: Channel[%s] state[Down]\n", + __func__, rpdev->id.name); + spin_lock(&ch->ch_lock); + ch->handle = NULL; + ch->ch_state = WDSP_CH_DISCONNECTED; + spin_unlock(&ch->ch_lock); + dev_set_drvdata(&rpdev->dev, NULL); +} + +static bool wdsp_is_ch_connected(struct wdsp_glink_priv *wpriv) +{ + int i; + + for (i = 0; i < wpriv->no_of_channels; i++) { + spin_lock(&wpriv->ch[i]->ch_lock); + if (wpriv->ch[i]->ch_state != WDSP_CH_CONNECTED) { + spin_unlock(&wpriv->ch[i]->ch_lock); + return false; + } + spin_unlock(&wpriv->ch[i]->ch_lock); + } + return true; +} + +static int wdsp_wait_for_all_ch_connect(struct wdsp_glink_priv *wpriv) +{ + int ret; + + ret = wait_event_timeout(wpriv->ch_state_wait, + wdsp_is_ch_connected(wpriv), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + dev_err_ratelimited(wpriv->dev, "%s: All channels are not connected\n", + __func__); + ret = -ETIMEDOUT; + goto err; + } + ret = 0; + +err: + return ret; +} + +/* + * wdsp_tx_buf_work - Work queue function to send tx buffer to glink + * work: Work structure + */ +static void wdsp_tx_buf_work(struct work_struct *work) +{ + struct wdsp_glink_priv *wpriv; + struct wdsp_ch *ch; + struct wdsp_tx_buf *tx_buf; + struct wdsp_write_pkt *wpkt; + struct wdsp_cmd_pkt *cpkt; + int ret = 0; + struct rpmsg_device *rpdev = NULL; + + tx_buf = container_of(work, struct wdsp_tx_buf, + tx_work); + ch = tx_buf->ch; + wpriv = ch->wpriv; + wpkt = (struct wdsp_write_pkt *)tx_buf->buf; + cpkt = (struct wdsp_cmd_pkt *)wpkt->payload; + + dev_dbg(wpriv->dev, "%s: ch name = %s, payload size = %d\n", + __func__, cpkt->ch_name, cpkt->payload_size); + + spin_lock(&ch->ch_lock); + rpdev = ch->handle; + if (rpdev && ch->ch_state == WDSP_CH_CONNECTED) { + spin_unlock(&ch->ch_lock); + ret = rpmsg_send(rpdev->ept, cpkt->payload, + cpkt->payload_size); + if (ret < 0) + dev_err(wpriv->dev, "%s: rpmsg send failed, ret = %d\n", + __func__, ret); + } else { + spin_unlock(&ch->ch_lock); + if (rpdev) + dev_err(wpriv->dev, "%s: channel %s is not in connected state\n", + __func__, ch->ch_name); + else + dev_err(wpriv->dev, "%s: rpdev is NULL\n", __func__); + } + vfree(tx_buf); +} + +/* + * wdsp_glink_read - Read API to send the data to userspace + * file: Pointer to the file structure + * buf: Pointer to the userspace buffer + * count: Number bytes to read from the file + * ppos: Pointer to the position into the file + * Returns 0 on success and an appropriate error value on failure + */ +static ssize_t wdsp_glink_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + int ret = 0, ret1 = 0; + struct wdsp_rsp_que *read_rsp = NULL; + struct wdsp_glink_priv *wpriv; + unsigned long flags; + + wpriv = (struct wdsp_glink_priv *)file->private_data; + if (!wpriv) { + pr_err("%s: Invalid private data\n", __func__); + return -EINVAL; + } + + if (count > WDSP_MAX_READ_SIZE) { + dev_info_ratelimited(wpriv->dev, "%s: count = %zd is more than WDSP_MAX_READ_SIZE\n", + __func__, count); + count = WDSP_MAX_READ_SIZE; + } + /* + * Complete signal has given from gwdsp_rpmsg_callback() + * or from flush API. Also use interruptible wait_for_completion API + * to allow the system to go in suspend. + */ + ret = wait_for_completion_interruptible(&wpriv->rsp_complete); + if (ret < 0) + return ret; + + read_rsp = kzalloc(sizeof(struct wdsp_rsp_que), GFP_KERNEL); + if (!read_rsp) + return -ENOMEM; + + spin_lock_irqsave(&wpriv->rsp_lock, flags); + if (wpriv->rsp_cnt) { + wpriv->rsp_cnt--; + dev_dbg(wpriv->dev, "%s: rsp_cnt=%d read from buffer %d\n", + __func__, wpriv->rsp_cnt, wpriv->read_idx); + + memcpy(read_rsp, &wpriv->rsp[wpriv->read_idx], + sizeof(struct wdsp_rsp_que)); + wpriv->read_idx = (wpriv->read_idx + 1) % RESP_QUEUE_SIZE; + spin_unlock_irqrestore(&wpriv->rsp_lock, flags); + + if (count < read_rsp->buf_size) { + ret1 = copy_to_user(buf, read_rsp->buf, count); + /* Return the number of bytes copied */ + ret = count; + } else { + ret1 = copy_to_user(buf, read_rsp->buf, + read_rsp->buf_size); + /* Return the number of bytes copied */ + ret = read_rsp->buf_size; + } + + if (ret1) { + dev_err_ratelimited(wpriv->dev, "%s: copy_to_user failed %d\n", + __func__, ret); + ret = -EFAULT; + goto done; + } + } else { + /* + * This will execute only if flush API is called or + * something wrong with ref_cnt + */ + dev_dbg(wpriv->dev, "%s: resp count = %d\n", __func__, + wpriv->rsp_cnt); + spin_unlock_irqrestore(&wpriv->rsp_lock, flags); + ret = -EINVAL; + } + +done: + kfree(read_rsp); + return ret; +} + +/* + * wdsp_glink_write - Write API to receive the data from userspace + * file: Pointer to the file structure + * buf: Pointer to the userspace buffer + * count: Number bytes to read from the file + * ppos: Pointer to the position into the file + * Returns 0 on success and an appropriate error value on failure + */ +static ssize_t wdsp_glink_write(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + int ret = 0, i, tx_buf_size; + struct wdsp_write_pkt *wpkt; + struct wdsp_cmd_pkt *cpkt; + struct wdsp_tx_buf *tx_buf; + struct wdsp_glink_priv *wpriv; + size_t pkt_max_size; + + wpriv = (struct wdsp_glink_priv *)file->private_data; + if (!wpriv) { + pr_err("%s: Invalid private data\n", __func__); + ret = -EINVAL; + goto done; + } + + if ((count < WDSP_WRITE_PKT_SIZE) || + (count > WDSP_MAX_WRITE_SIZE)) { + dev_err_ratelimited(wpriv->dev, "%s: Invalid count = %zd\n", + __func__, count); + ret = -EINVAL; + goto done; + } + + dev_dbg(wpriv->dev, "%s: count = %zd\n", __func__, count); + + tx_buf_size = count + sizeof(struct wdsp_tx_buf); + tx_buf = vzalloc(tx_buf_size); + if (!tx_buf) { + ret = -ENOMEM; + goto done; + } + + ret = copy_from_user(tx_buf->buf, buf, count); + if (ret) { + dev_err_ratelimited(wpriv->dev, "%s: copy_from_user failed %d\n", + __func__, ret); + ret = -EFAULT; + goto free_buf; + } + + wpkt = (struct wdsp_write_pkt *)tx_buf->buf; + switch (wpkt->pkt_type) { + case WDSP_REG_PKT: + /* Keep this case to support backward compatibility */ + vfree(tx_buf); + break; + case WDSP_READY_PKT: + ret = wdsp_wait_for_all_ch_connect(wpriv); + if (ret < 0) + dev_err_ratelimited(wpriv->dev, "%s: Channels not in connected state\n", + __func__); + vfree(tx_buf); + break; + case WDSP_CMD_PKT: + if (count <= (WDSP_WRITE_PKT_SIZE + WDSP_CMD_PKT_SIZE)) { + dev_err_ratelimited(wpriv->dev, "%s: Invalid cmd pkt size = %zd\n", + __func__, count); + ret = -EINVAL; + goto free_buf; + } + cpkt = (struct wdsp_cmd_pkt *)wpkt->payload; + pkt_max_size = sizeof(struct wdsp_write_pkt) + + sizeof(struct wdsp_cmd_pkt) + + cpkt->payload_size; + if (count < pkt_max_size) { + dev_err_ratelimited(wpriv->dev, "%s: Invalid cmd pkt count = %zd, pkt_size = %zd\n", + __func__, count, pkt_max_size); + ret = -EINVAL; + goto free_buf; + } + for (i = 0; i < wpriv->no_of_channels; i++) { + if (!strcmp(cpkt->ch_name, wpriv->ch[i]->ch_name)) { + tx_buf->ch = wpriv->ch[i]; + break; + } + } + if (!tx_buf->ch) { + dev_err_ratelimited(wpriv->dev, "%s: Failed to get channel\n", + __func__); + ret = -EINVAL; + goto free_buf; + } + dev_dbg(wpriv->dev, "%s: requested ch_name: %s, pkt_size: %zd\n", + __func__, cpkt->ch_name, pkt_max_size); + + spin_lock(&tx_buf->ch->ch_lock); + if (tx_buf->ch->ch_state != WDSP_CH_CONNECTED) { + spin_unlock(&tx_buf->ch->ch_lock); + ret = -ENETRESET; + dev_err_ratelimited(wpriv->dev, "%s: Channels are not in connected state\n", + __func__); + goto free_buf; + } + spin_unlock(&tx_buf->ch->ch_lock); + + INIT_WORK(&tx_buf->tx_work, wdsp_tx_buf_work); + queue_work(wpriv->work_queue, &tx_buf->tx_work); + break; + default: + dev_err_ratelimited(wpriv->dev, "%s: Invalid packet type\n", + __func__); + ret = -EINVAL; + vfree(tx_buf); + break; + } + goto done; + +free_buf: + vfree(tx_buf); + +done: + return ret; +} + +/* + * wdsp_glink_open - Open API to initialize private data + * inode: Pointer to the inode structure + * file: Pointer to the file structure + * Returns 0 on success and an appropriate error value on failure + */ +static int wdsp_glink_open(struct inode *inode, struct file *file) +{ + + pr_debug("%s: wpriv = %pK\n", __func__, wpriv); + file->private_data = wpriv; + + return 0; +} + +/* + * wdsp_glink_flush - Flush API to unblock read. + * file: Pointer to the file structure + * id: Lock owner ID + * Returns 0 on success and an appropriate error value on failure + */ +static int wdsp_glink_flush(struct file *file, fl_owner_t id) +{ + struct wdsp_glink_priv *wpriv; + + wpriv = (struct wdsp_glink_priv *)file->private_data; + if (!wpriv) { + pr_err("%s: Invalid private data\n", __func__); + return -EINVAL; + } + + complete(&wpriv->rsp_complete); + + return 0; +} + +/* + * wdsp_glink_release - Release API to clean up resources. + * Whenever a file structure is shared across multiple threads, + * release won't be invoked until all copies are closed + * (file->f_count.counter should be 0). If we need to flush pending + * data when any copy is closed, you should implement the flush method. + * + * inode: Pointer to the inode structure + * file: Pointer to the file structure + * Returns 0 on success and an appropriate error value on failure + */ +static int wdsp_glink_release(struct inode *inode, struct file *file) +{ + pr_debug("%s: file->private_data = %pK\n", __func__, + file->private_data); + file->private_data = NULL; + + return 0; +} + +static struct rpmsg_driver wdsp_rpmsg_driver = { + .probe = wdsp_rpmsg_probe, + .remove = wdsp_rpmsg_remove, + .callback = wdsp_rpmsg_callback, + /* Update this dynamically before register_rpmsg() */ + .id_table = NULL, + .drv = { + .name = "wdsp_rpmsg", + }, +}; + +static int wdsp_register_rpmsg(struct platform_device *pdev, + struct wdsp_glink_dev *wdev) +{ + int ret = 0; + int i, no_of_channels; + struct rpmsg_device_id *wdsp_rpmsg_id_table, *id_table; + const char *ch_name = NULL; + + wpriv = devm_kzalloc(&pdev->dev, + sizeof(struct wdsp_glink_priv), GFP_KERNEL); + if (!wpriv) + return -ENOMEM; + + no_of_channels = of_property_count_strings(pdev->dev.of_node, + "qcom,wdsp-channels"); + if (no_of_channels < 0) { + dev_err(&pdev->dev, "%s: channel name parse error %d\n", + __func__, no_of_channels); + return -EINVAL; + } + + wpriv->ch = devm_kzalloc(&pdev->dev, + (sizeof(struct wdsp_glink_priv *) * no_of_channels), + GFP_KERNEL); + if (!wpriv->ch) + return -ENOMEM; + + for (i = 0; i < no_of_channels; i++) { + ret = of_property_read_string_index(pdev->dev.of_node, + "qcom,wdsp-channels", i, + &ch_name); + if (ret) { + dev_err(&pdev->dev, "%s: channel name parse error %d\n", + __func__, ret); + return -EINVAL; + } + wpriv->ch[i] = devm_kzalloc(&pdev->dev, + sizeof(struct wdsp_glink_priv), + GFP_KERNEL); + if (!wpriv->ch[i]) + return -ENOMEM; + + strlcpy(wpriv->ch[i]->ch_name, ch_name, RPMSG_NAME_SIZE); + wpriv->ch[i]->wpriv = wpriv; + spin_lock_init(&wpriv->ch[i]->ch_lock); + } + init_waitqueue_head(&wpriv->ch_state_wait); + init_completion(&wpriv->rsp_complete); + spin_lock_init(&wpriv->rsp_lock); + + wpriv->wdev = wdev; + wpriv->dev = wdev->dev; + wpriv->work_queue = create_singlethread_workqueue("wdsp_glink_wq"); + if (!wpriv->work_queue) { + dev_err(&pdev->dev, "%s: Error creating wdsp_glink_wq\n", + __func__); + return -EINVAL; + } + + wdsp_rpmsg_id_table = devm_kzalloc(&pdev->dev, + (sizeof(struct rpmsg_device_id) * + (no_of_channels + 1)), + GFP_KERNEL); + if (!wdsp_rpmsg_id_table) { + ret = -ENOMEM; + goto err; + } + + wpriv->no_of_channels = no_of_channels; + id_table = wdsp_rpmsg_id_table; + for (i = 0; i < no_of_channels; i++) { + strlcpy(id_table->name, wpriv->ch[i]->ch_name, + RPMSG_NAME_SIZE); + id_table++; + } + wdsp_rpmsg_driver.id_table = wdsp_rpmsg_id_table; + ret = register_rpmsg_driver(&wdsp_rpmsg_driver); + if (ret < 0) { + dev_err(&pdev->dev, "%s: Rpmsg driver register failed, err = %d\n", + __func__, ret); + goto err; + } + + return 0; + +err: + destroy_workqueue(wpriv->work_queue); + return ret; +} + +static const struct file_operations wdsp_glink_fops = { + .owner = THIS_MODULE, + .open = wdsp_glink_open, + .read = wdsp_glink_read, + .write = wdsp_glink_write, + .flush = wdsp_glink_flush, + .release = wdsp_glink_release, +}; + +static int wdsp_glink_probe(struct platform_device *pdev) +{ + int ret; + struct wdsp_glink_dev *wdev; + + wdev = devm_kzalloc(&pdev->dev, sizeof(*wdev), GFP_KERNEL); + if (!wdev) { + ret = -ENOMEM; + goto done; + } + + ret = alloc_chrdev_region(&wdev->dev_num, 0, MINOR_NUMBER_COUNT, + WDSP_GLINK_DRIVER_NAME); + if (ret < 0) { + dev_err(&pdev->dev, "%s: Failed to alloc char dev, err = %d\n", + __func__, ret); + goto err_chrdev; + } + + wdev->cls = class_create(THIS_MODULE, WDSP_GLINK_DRIVER_NAME); + if (IS_ERR(wdev->cls)) { + ret = PTR_ERR(wdev->cls); + dev_err(&pdev->dev, "%s: Failed to create class, err = %d\n", + __func__, ret); + goto err_class; + } + + wdev->dev = device_create(wdev->cls, NULL, wdev->dev_num, + NULL, WDSP_GLINK_DRIVER_NAME); + if (IS_ERR(wdev->dev)) { + ret = PTR_ERR(wdev->dev); + dev_err(&pdev->dev, "%s: Failed to create device, err = %d\n", + __func__, ret); + goto err_dev_create; + } + + cdev_init(&wdev->cdev, &wdsp_glink_fops); + ret = cdev_add(&wdev->cdev, wdev->dev_num, MINOR_NUMBER_COUNT); + if (ret < 0) { + dev_err(&pdev->dev, "%s: Failed to register char dev, err = %d\n", + __func__, ret); + goto err_cdev_add; + } + + ret = wdsp_register_rpmsg(pdev, wdev); + if (ret < 0) { + dev_err(&pdev->dev, "%s: Failed to register with rpmsg, err = %d\n", + __func__, ret); + goto err_cdev_add; + } + platform_set_drvdata(pdev, wpriv); + + goto done; + +err_cdev_add: + device_destroy(wdev->cls, wdev->dev_num); + +err_dev_create: + class_destroy(wdev->cls); + +err_class: + unregister_chrdev_region(0, MINOR_NUMBER_COUNT); + +err_chrdev: +done: + return ret; +} + +static int wdsp_glink_remove(struct platform_device *pdev) +{ + struct wdsp_glink_priv *wpriv = platform_get_drvdata(pdev); + + unregister_rpmsg_driver(&wdsp_rpmsg_driver); + + if (wpriv) { + flush_workqueue(wpriv->work_queue); + destroy_workqueue(wpriv->work_queue); + if (wpriv->wdev) { + cdev_del(&wpriv->wdev->cdev); + device_destroy(wpriv->wdev->cls, wpriv->wdev->dev_num); + class_destroy(wpriv->wdev->cls); + unregister_chrdev_region(0, MINOR_NUMBER_COUNT); + } + } + + return 0; +} + +static const struct of_device_id wdsp_glink_of_match[] = { + {.compatible = "qcom,wcd-dsp-glink"}, + { } +}; +MODULE_DEVICE_TABLE(of, wdsp_glink_of_match); + +static struct platform_driver wdsp_glink_driver = { + .probe = wdsp_glink_probe, + .remove = wdsp_glink_remove, + .driver = { + .name = WDSP_GLINK_DRIVER_NAME, + .owner = THIS_MODULE, + .of_match_table = wdsp_glink_of_match, + }, +}; + +static int __init wdsp_glink_init(void) +{ + return platform_driver_register(&wdsp_glink_driver); +} + +static void __exit wdsp_glink_exit(void) +{ + platform_driver_unregister(&wdsp_glink_driver); +} + +module_init(wdsp_glink_init); +module_exit(wdsp_glink_exit); +MODULE_DESCRIPTION("SoC WCD_DSP GLINK Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/audio-kernel/soc/Android.mk b/audio-kernel/soc/Android.mk new file mode 100644 index 000000000..4192639a2 --- /dev/null +++ b/audio-kernel/soc/Android.mk @@ -0,0 +1,97 @@ +# Android makefile for audio kernel modules + +# Assume no targets will be supported + +# Check if this driver needs be built for current target +ifeq ($(call is-board-platform,sdm845),true) +AUDIO_SELECT := CONFIG_SND_SOC_SDM845=m +endif + +ifeq ($(call is-board-platform-in-list,msm8953 sdm670 qcs605),true) +AUDIO_SELECT := CONFIG_SND_SOC_SDM670=m +endif + +ifeq ($(call is-board-platform,msmnile),true) +AUDIO_SELECT := CONFIG_SND_SOC_SM8150=m +endif + +ifeq ($(call is-board-platform,$(MSMSTEPPE) $(TRINKET)),true) +AUDIO_SELECT := CONFIG_SND_SOC_SM6150=m +endif + +AUDIO_CHIPSET := audio +# Build/Package only in case of supported target +ifeq ($(call is-board-platform-in-list,msm8953 sdm845 sdm670 qcs605 msmnile $(MSMSTEPPE) $(TRINKET)),true) + +LOCAL_PATH := $(call my-dir) + +# This makefile is only for DLKM +ifneq ($(findstring vendor,$(LOCAL_PATH)),) + +ifneq ($(findstring opensource,$(LOCAL_PATH)),) + AUDIO_BLD_DIR := $(shell pwd)/vendor/qcom/opensource/audio-kernel +endif # opensource + +DLKM_DIR := $(TOP)/device/qcom/common/dlkm + +# Build audio.ko as $(AUDIO_CHIPSET)_audio.ko +########################################################### +# This is set once per LOCAL_PATH, not per (kernel) module +KBUILD_OPTIONS := AUDIO_ROOT=$(AUDIO_BLD_DIR) + +# We are actually building audio.ko here, as per the +# requirement we are specifying _audio.ko as LOCAL_MODULE. +# This means we need to rename the module to _audio.ko +# after audio.ko is built. +KBUILD_OPTIONS += MODNAME=soc_dlkm +KBUILD_OPTIONS += BOARD_PLATFORM=$(TARGET_BOARD_PLATFORM) +KBUILD_OPTIONS += $(AUDIO_SELECT) + +########################################################### +ifeq ($(call is-board-platform-in-list,msm8953 sdm670 qcs605 $(MSMSTEPPE)),true) +include $(CLEAR_VARS) +LOCAL_MODULE := $(AUDIO_CHIPSET)_pinctrl_lpi.ko +LOCAL_MODULE_KBUILD_NAME := pinctrl_lpi_dlkm.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_DEBUG_ENABLE := true +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk +endif +########################################################### +include $(CLEAR_VARS) +LOCAL_MODULE := $(AUDIO_CHIPSET)_pinctrl_wcd.ko +LOCAL_MODULE_KBUILD_NAME := pinctrl_wcd_dlkm.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_DEBUG_ENABLE := true +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk +########################################################### +include $(CLEAR_VARS) +LOCAL_MODULE := $(AUDIO_CHIPSET)_swr.ko +LOCAL_MODULE_KBUILD_NAME := swr_dlkm.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_DEBUG_ENABLE := true +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk +########################################################### +include $(CLEAR_VARS) +LOCAL_MODULE := $(AUDIO_CHIPSET)_swr_ctrl.ko +LOCAL_MODULE_KBUILD_NAME := swr_ctrl_dlkm.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_DEBUG_ENABLE := true +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk +########################################################### +ifeq ($(call is-board-platform-in-list, $(MSMSTEPPE) $(TRINKET)),true) +include $(CLEAR_VARS) +LOCAL_MODULE := $(AUDIO_CHIPSET)_snd_event.ko +LOCAL_MODULE_KBUILD_NAME := snd_event_dlkm.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_DEBUG_ENABLE := true +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk +endif +########################################################### + +endif # DLKM check +endif # supported target check diff --git a/audio-kernel/soc/Kbuild b/audio-kernel/soc/Kbuild new file mode 100644 index 000000000..1b37c4f97 --- /dev/null +++ b/audio-kernel/soc/Kbuild @@ -0,0 +1,173 @@ +# We can build either as part of a standalone Kernel build or as +# an external module. Determine which mechanism is being used +ifeq ($(MODNAME),) + KERNEL_BUILD := 1 +else + KERNEL_BUILD := 0 +endif + +ifeq ($(KERNEL_BUILD), 1) + # These are configurable via Kconfig for kernel-based builds + # Need to explicitly configure for Android-based builds + AUDIO_BLD_DIR := $(shell pwd)/kernel/msm-4.14 + AUDIO_ROOT := $(AUDIO_BLD_DIR)/techpack/audio +endif + +ifeq ($(KERNEL_BUILD), 0) + ifeq ($(CONFIG_ARCH_SDM845), y) + include $(AUDIO_ROOT)/config/sdm845auto.conf + export + INCS += -include $(AUDIO_ROOT)/config/sdm845autoconf.h + endif + ifeq ($(CONFIG_ARCH_SDM670), y) + include $(AUDIO_ROOT)/config/sdm670auto.conf + export + INCS += -include $(AUDIO_ROOT)/config/sdm670autoconf.h + endif + ifeq ($(CONFIG_ARCH_SDM450), y) + include $(AUDIO_ROOT)/config/sdm670auto.conf + export + INCS += -include $(AUDIO_ROOT)/config/sdm670autoconf.h + endif + ifeq ($(CONFIG_ARCH_SM8150), y) + include $(AUDIO_ROOT)/config/sm8150auto.conf + export + INCS += -include $(AUDIO_ROOT)/config/sm8150autoconf.h + endif + ifeq ($(CONFIG_ARCH_SM6150), y) + include $(AUDIO_ROOT)/config/sm6150auto.conf + export + INCS += -include $(AUDIO_ROOT)/config/sm6150autoconf.h + endif + ifeq ($(CONFIG_ARCH_TRINKET), y) + include $(AUDIO_ROOT)/config/trinketauto.conf + export + INCS += -include $(AUDIO_ROOT)/config/trinketautoconf.h + endif + ifeq ($(CONFIG_ARCH_SDMSHRIKE), y) + include $(AUDIO_ROOT)/config/sm8150auto.conf + export + INCS += -include $(AUDIO_ROOT)/config/sm8150autoconf.h + endif + ifeq ($(CONFIG_ARCH_QCS405), y) + include $(AUDIO_ROOT)/config/qcs405auto.conf + export + INCS += -include $(AUDIO_ROOT)/config/qcs405autoconf.h + endif +endif + +# As per target team, build is done as follows: +# Defconfig : build with default flags +# Slub : defconfig + CONFIG_SLUB_DEBUG := y + +# CONFIG_SLUB_DEBUG_ON := y + CONFIG_PAGE_POISONING := y +# Perf : Using appropriate msmXXXX-perf_defconfig +# +# Shipment builds (user variants) should not have any debug feature +# enabled. This is identified using 'TARGET_BUILD_VARIANT'. Slub builds +# are identified using the CONFIG_SLUB_DEBUG_ON configuration. Since +# there is no other way to identify defconfig builds, QTI internal +# representation of perf builds (identified using the string 'perf'), +# is used to identify if the build is a slub or defconfig one. This +# way no critical debug feature will be enabled for perf and shipment +# builds. Other OEMs are also protected using the TARGET_BUILD_VARIANT +# config. + +############ UAPI ############ +UAPI_DIR := uapi +UAPI_INC := -I$(AUDIO_ROOT)/include/$(UAPI_DIR) + +############ COMMON ############ +COMMON_DIR := include +COMMON_INC := -I$(AUDIO_ROOT)/$(COMMON_DIR) + +############ SoC Modules ############ + +# for pinctrl WCD driver +ifdef CONFIG_PINCTRL_WCD + PINCTRL_WCD_OBJS += pinctrl-wcd.o +endif + +# for pinctrl LPI driver +ifdef CONFIG_PINCTRL_LPI + PINCTRL_LPI_OBJS += pinctrl-lpi.o +endif + +# for soundwire driver +ifdef CONFIG_SOUNDWIRE_WCD_CTRL + SWR_CTRL_OBJS += swr-wcd-ctrl.o +endif + +# for new soundwire driver +ifdef CONFIG_SOUNDWIRE_MSTR_CTRL + SWR_CTRL_OBJS += swr-mstr-ctrl.o +endif + +ifdef CONFIG_SOUNDWIRE + SWR_OBJS += regmap-swr.o + SWR_OBJS += soundwire.o +endif + +ifdef CONFIG_SND_EVENT + SND_EVENT_OBJS += snd_event.o +endif + +LINUX_INC += -Iinclude/linux + +INCS += $(COMMON_INC) \ + $(UAPI_INC) + +EXTRA_CFLAGS += $(INCS) + + +CDEFINES += -DANI_LITTLE_BYTE_ENDIAN \ + -DANI_LITTLE_BIT_ENDIAN \ + -DDOT11F_LITTLE_ENDIAN_HOST \ + -DANI_COMPILER_TYPE_GCC \ + -DANI_OS_TYPE_ANDROID=6 \ + -DPTT_SOCK_SVC_ENABLE \ + -Wall\ + -Werror\ + -D__linux__ + +KBUILD_CPPFLAGS += $(CDEFINES) + +ifeq ($(KERNEL_BUILD), 0) +KBUILD_EXTRA_SYMBOLS +=$(OUT)/obj/vendor/qcom/opensource/audio-kernel/dsp/Module.symvers +endif + +# Currently, for versions of gcc which support it, the kernel Makefile +# is disabling the maybe-uninitialized warning. Re-enable it for the +# AUDIO driver. Note that we must use EXTRA_CFLAGS here so that it +# will override the kernel settings. +ifeq ($(call cc-option-yn, -Wmaybe-uninitialized),y) +EXTRA_CFLAGS += -Wmaybe-uninitialized +endif +#EXTRA_CFLAGS += -Wmissing-prototypes + +ifeq ($(call cc-option-yn, -Wheader-guard),y) +EXTRA_CFLAGS += -Wheader-guard +endif + +ifeq ($(CONFIG_SND_SOC_GCOV), y) +GCOV_PROFILE := y +endif + +# Module information used by KBuild framework +obj-$(CONFIG_PINCTRL_WCD) += pinctrl_wcd_dlkm.o +pinctrl_wcd_dlkm-y := $(PINCTRL_WCD_OBJS) + +obj-$(CONFIG_PINCTRL_LPI) += pinctrl_lpi_dlkm.o +pinctrl_lpi_dlkm-y := $(PINCTRL_LPI_OBJS) + +obj-$(CONFIG_SOUNDWIRE) += swr_dlkm.o +swr_dlkm-y := $(SWR_OBJS) + +obj-$(CONFIG_SND_EVENT) += snd_event_dlkm.o +snd_event_dlkm-y := $(SND_EVENT_OBJS) + +obj-$(CONFIG_SOUNDWIRE_WCD_CTRL) += swr_ctrl_dlkm.o +obj-$(CONFIG_SOUNDWIRE_MSTR_CTRL) += swr_ctrl_dlkm.o +swr_ctrl_dlkm-y := $(SWR_CTRL_OBJS) + +# inject some build related information +DEFINES += -DBUILD_TIMESTAMP=\"$(shell date -u +'%Y-%m-%dT%H:%M:%SZ')\" diff --git a/audio-kernel/soc/core.h b/audio-kernel/soc/core.h new file mode 120000 index 000000000..b9f94ca74 --- /dev/null +++ b/audio-kernel/soc/core.h @@ -0,0 +1 @@ +../../../../../kernel/msm-4.14/drivers/pinctrl/core.h \ No newline at end of file diff --git a/audio-kernel/soc/pinctrl-lpi.c b/audio-kernel/soc/pinctrl-lpi.c new file mode 100644 index 000000000..f670b001e --- /dev/null +++ b/audio-kernel/soc/pinctrl-lpi.c @@ -0,0 +1,740 @@ +/* + * Copyright (c) 2016-2019, 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "core.h" +#include "pinctrl-utils.h" + +#define LPI_AUTO_SUSPEND_DELAY 100 /* delay in msec */ + +#define LPI_ADDRESS_SIZE 0x20000 + +#define LPI_GPIO_REG_VAL_CTL 0x00 +#define LPI_GPIO_REG_DIR_CTL 0x04 + +#define LPI_GPIO_REG_PULL_SHIFT 0x0 +#define LPI_GPIO_REG_PULL_MASK 0x3 + +#define LPI_GPIO_REG_FUNCTION_SHIFT 0x2 +#define LPI_GPIO_REG_FUNCTION_MASK 0x3C + +#define LPI_GPIO_REG_OUT_STRENGTH_SHIFT 0x6 +#define LPI_GPIO_REG_OUT_STRENGTH_MASK 0x1C0 + +#define LPI_GPIO_REG_OE_SHIFT 0x9 +#define LPI_GPIO_REG_OE_MASK 0x200 + +#define LPI_GPIO_REG_DIR_SHIFT 0x1 +#define LPI_GPIO_REG_DIR_MASK 0x2 + +#define LPI_GPIO_BIAS_DISABLE 0x0 +#define LPI_GPIO_PULL_DOWN 0x1 +#define LPI_GPIO_KEEPER 0x2 +#define LPI_GPIO_PULL_UP 0x3 + +#define LPI_GPIO_FUNC_GPIO "gpio" +#define LPI_GPIO_FUNC_FUNC1 "func1" +#define LPI_GPIO_FUNC_FUNC2 "func2" +#define LPI_GPIO_FUNC_FUNC3 "func3" +#define LPI_GPIO_FUNC_FUNC4 "func4" +#define LPI_GPIO_FUNC_FUNC5 "func5" + +static bool lpi_dev_up; +static struct device *lpi_dev; + +/* The index of each function in lpi_gpio_functions[] array */ +enum lpi_gpio_func_index { + LPI_GPIO_FUNC_INDEX_GPIO = 0x00, + LPI_GPIO_FUNC_INDEX_FUNC1 = 0x01, + LPI_GPIO_FUNC_INDEX_FUNC2 = 0x02, + LPI_GPIO_FUNC_INDEX_FUNC3 = 0x03, + LPI_GPIO_FUNC_INDEX_FUNC4 = 0x04, + LPI_GPIO_FUNC_INDEX_FUNC5 = 0x05, +}; + +/** + * struct lpi_gpio_pad - keep current GPIO settings + * @offset: Nth GPIO in supported GPIOs. + * @output_enabled: Set to true if GPIO output logic is enabled. + * @value: value of a pin + * @base: Address base of LPI GPIO PAD. + * @pullup: Constant current which flow through GPIO output buffer. + * @strength: No, Low, Medium, High + * @function: See lpi_gpio_functions[] + */ +struct lpi_gpio_pad { + u32 offset; + bool output_enabled; + bool value; + char __iomem *base; + unsigned int pullup; + unsigned int strength; + unsigned int function; +}; + +struct lpi_gpio_state { + struct device *dev; + struct pinctrl_dev *ctrl; + struct gpio_chip chip; + char __iomem *base; + struct clk *lpass_core_hw_vote; +}; + +static const char *const lpi_gpio_groups[] = { + "gpio0", "gpio1", "gpio2", "gpio3", "gpio4", "gpio5", "gpio6", "gpio7", + "gpio8", "gpio9", "gpio10", "gpio11", "gpio12", "gpio13", "gpio14", + "gpio15", "gpio16", "gpio17", "gpio18", "gpio19", "gpio20", "gpio21", + "gpio22", "gpio23", "gpio24", "gpio25", "gpio26", "gpio27", "gpio28", + "gpio29", "gpio30", "gpio31", +}; + +#define LPI_TLMM_MAX_PINS 100 +static u32 lpi_offset[LPI_TLMM_MAX_PINS]; + +static const char *const lpi_gpio_functions[] = { + [LPI_GPIO_FUNC_INDEX_GPIO] = LPI_GPIO_FUNC_GPIO, + [LPI_GPIO_FUNC_INDEX_FUNC1] = LPI_GPIO_FUNC_FUNC1, + [LPI_GPIO_FUNC_INDEX_FUNC2] = LPI_GPIO_FUNC_FUNC2, + [LPI_GPIO_FUNC_INDEX_FUNC3] = LPI_GPIO_FUNC_FUNC3, + [LPI_GPIO_FUNC_INDEX_FUNC4] = LPI_GPIO_FUNC_FUNC4, + [LPI_GPIO_FUNC_INDEX_FUNC5] = LPI_GPIO_FUNC_FUNC5, +}; + +static int lpi_gpio_read(struct lpi_gpio_pad *pad, unsigned int addr) +{ + int ret; + + if (!lpi_dev_up) { + pr_err_ratelimited("%s: ADSP is down due to SSR, return\n", + __func__); + return 0; + } + pm_runtime_get_sync(lpi_dev); + + ret = ioread32(pad->base + pad->offset + addr); + if (ret < 0) + pr_err("%s: read 0x%x failed\n", __func__, addr); + + pm_runtime_mark_last_busy(lpi_dev); + pm_runtime_put_autosuspend(lpi_dev); + return ret; +} + +static int lpi_gpio_write(struct lpi_gpio_pad *pad, unsigned int addr, + unsigned int val) +{ + if (!lpi_dev_up) { + pr_err_ratelimited("%s: ADSP is down due to SSR, return\n", + __func__); + return 0; + } + pm_runtime_get_sync(lpi_dev); + + iowrite32(val, pad->base + pad->offset + addr); + + pm_runtime_mark_last_busy(lpi_dev); + pm_runtime_put_autosuspend(lpi_dev); + return 0; +} + +static int lpi_gpio_get_groups_count(struct pinctrl_dev *pctldev) +{ + /* Every PIN is a group */ + return pctldev->desc->npins; +} + +static const char *lpi_gpio_get_group_name(struct pinctrl_dev *pctldev, + unsigned int pin) +{ + return pctldev->desc->pins[pin].name; +} + +static int lpi_gpio_get_group_pins(struct pinctrl_dev *pctldev, + unsigned int pin, + const unsigned int **pins, + unsigned int *num_pins) +{ + *pins = &pctldev->desc->pins[pin].number; + *num_pins = 1; + return 0; +} + +static const struct pinctrl_ops lpi_gpio_pinctrl_ops = { + .get_groups_count = lpi_gpio_get_groups_count, + .get_group_name = lpi_gpio_get_group_name, + .get_group_pins = lpi_gpio_get_group_pins, + .dt_node_to_map = pinconf_generic_dt_node_to_map_group, + .dt_free_map = pinctrl_utils_free_map, +}; + +static int lpi_gpio_get_functions_count(struct pinctrl_dev *pctldev) +{ + return ARRAY_SIZE(lpi_gpio_functions); +} + +static const char *lpi_gpio_get_function_name(struct pinctrl_dev *pctldev, + unsigned int function) +{ + return lpi_gpio_functions[function]; +} + +static int lpi_gpio_get_function_groups(struct pinctrl_dev *pctldev, + unsigned int function, + const char *const **groups, + unsigned *const num_qgroups) +{ + *groups = lpi_gpio_groups; + *num_qgroups = pctldev->desc->npins; + return 0; +} + +static int lpi_gpio_set_mux(struct pinctrl_dev *pctldev, unsigned int function, + unsigned int pin) +{ + struct lpi_gpio_pad *pad; + unsigned int val; + + pad = pctldev->desc->pins[pin].drv_data; + + pad->function = function; + + val = lpi_gpio_read(pad, LPI_GPIO_REG_VAL_CTL); + val &= ~(LPI_GPIO_REG_FUNCTION_MASK); + val |= pad->function << LPI_GPIO_REG_FUNCTION_SHIFT; + lpi_gpio_write(pad, LPI_GPIO_REG_VAL_CTL, val); + return 0; +} + +static const struct pinmux_ops lpi_gpio_pinmux_ops = { + .get_functions_count = lpi_gpio_get_functions_count, + .get_function_name = lpi_gpio_get_function_name, + .get_function_groups = lpi_gpio_get_function_groups, + .set_mux = lpi_gpio_set_mux, +}; + +static int lpi_config_get(struct pinctrl_dev *pctldev, + unsigned int pin, unsigned long *config) +{ + unsigned int param = pinconf_to_config_param(*config); + struct lpi_gpio_pad *pad; + unsigned int arg; + + pad = pctldev->desc->pins[pin].drv_data; + + switch (param) { + case PIN_CONFIG_BIAS_DISABLE: + arg = pad->pullup = LPI_GPIO_BIAS_DISABLE; + break; + case PIN_CONFIG_BIAS_PULL_DOWN: + arg = pad->pullup == LPI_GPIO_PULL_DOWN; + break; + case PIN_CONFIG_BIAS_BUS_HOLD: + arg = pad->pullup = LPI_GPIO_KEEPER; + break; + case PIN_CONFIG_BIAS_PULL_UP: + arg = pad->pullup == LPI_GPIO_PULL_UP; + break; + case PIN_CONFIG_INPUT_ENABLE: + case PIN_CONFIG_OUTPUT: + arg = pad->output_enabled; + break; + default: + return -EINVAL; + } + + *config = pinconf_to_config_packed(param, arg); + return 0; +} + +static unsigned int lpi_drive_to_regval(u32 arg) +{ + return (arg/2 - 1); +} + +static int lpi_config_set(struct pinctrl_dev *pctldev, unsigned int pin, + unsigned long *configs, unsigned int nconfs) +{ + struct lpi_gpio_pad *pad; + unsigned int param, arg; + int i, ret = 0, val; + + pad = pctldev->desc->pins[pin].drv_data; + + for (i = 0; i < nconfs; i++) { + param = pinconf_to_config_param(configs[i]); + arg = pinconf_to_config_argument(configs[i]); + + dev_dbg(pctldev->dev, "%s: param: %d arg: %d pin: %d\n", + __func__, param, arg, pin); + + switch (param) { + case PIN_CONFIG_BIAS_DISABLE: + pad->pullup = LPI_GPIO_BIAS_DISABLE; + break; + case PIN_CONFIG_BIAS_PULL_DOWN: + pad->pullup = LPI_GPIO_PULL_DOWN; + break; + case PIN_CONFIG_BIAS_BUS_HOLD: + pad->pullup = LPI_GPIO_KEEPER; + break; + case PIN_CONFIG_BIAS_PULL_UP: + pad->pullup = LPI_GPIO_PULL_UP; + break; + case PIN_CONFIG_INPUT_ENABLE: + pad->output_enabled = false; + break; + case PIN_CONFIG_OUTPUT: + pad->output_enabled = true; + pad->value = arg; + break; + case PIN_CONFIG_DRIVE_STRENGTH: + pad->strength = arg; + break; + default: + ret = -EINVAL; + goto done; + } + } + + val = lpi_gpio_read(pad, LPI_GPIO_REG_VAL_CTL); + val &= ~(LPI_GPIO_REG_PULL_MASK | LPI_GPIO_REG_OUT_STRENGTH_MASK | + LPI_GPIO_REG_OE_MASK); + val |= pad->pullup << LPI_GPIO_REG_PULL_SHIFT; + val |= lpi_drive_to_regval(pad->strength) << + LPI_GPIO_REG_OUT_STRENGTH_SHIFT; + if (pad->output_enabled) + val |= pad->value << LPI_GPIO_REG_OE_SHIFT; + + lpi_gpio_write(pad, LPI_GPIO_REG_VAL_CTL, val); + lpi_gpio_write(pad, LPI_GPIO_REG_DIR_CTL, + pad->output_enabled << LPI_GPIO_REG_DIR_SHIFT); +done: + return ret; +} + +static const struct pinconf_ops lpi_gpio_pinconf_ops = { + .is_generic = true, + .pin_config_group_get = lpi_config_get, + .pin_config_group_set = lpi_config_set, +}; + +static int lpi_gpio_direction_input(struct gpio_chip *chip, unsigned int pin) +{ + struct lpi_gpio_state *state = gpiochip_get_data(chip); + unsigned long config; + + config = pinconf_to_config_packed(PIN_CONFIG_INPUT_ENABLE, 1); + + return lpi_config_set(state->ctrl, pin, &config, 1); +} + +static int lpi_gpio_direction_output(struct gpio_chip *chip, + unsigned int pin, int val) +{ + struct lpi_gpio_state *state = gpiochip_get_data(chip); + unsigned long config; + + config = pinconf_to_config_packed(PIN_CONFIG_OUTPUT, val); + + return lpi_config_set(state->ctrl, pin, &config, 1); +} + +static int lpi_gpio_get(struct gpio_chip *chip, unsigned int pin) +{ + struct lpi_gpio_state *state = gpiochip_get_data(chip); + struct lpi_gpio_pad *pad; + int value; + + pad = state->ctrl->desc->pins[pin].drv_data; + + value = lpi_gpio_read(pad, LPI_GPIO_REG_VAL_CTL); + return value; +} + +static void lpi_gpio_set(struct gpio_chip *chip, unsigned int pin, int value) +{ + struct lpi_gpio_state *state = gpiochip_get_data(chip); + unsigned long config; + + config = pinconf_to_config_packed(PIN_CONFIG_OUTPUT, value); + + lpi_config_set(state->ctrl, pin, &config, 1); +} + +static int lpi_notifier_service_cb(struct notifier_block *this, + unsigned long opcode, void *ptr) +{ + static bool initial_boot = true; + + pr_debug("%s: Service opcode 0x%lx\n", __func__, opcode); + + switch (opcode) { + case AUDIO_NOTIFIER_SERVICE_DOWN: + if (initial_boot) { + initial_boot = false; + break; + } + snd_event_notify(lpi_dev, SND_EVENT_DOWN); + lpi_dev_up = false; + break; + case AUDIO_NOTIFIER_SERVICE_UP: + if (initial_boot) + initial_boot = false; + lpi_dev_up = true; + snd_event_notify(lpi_dev, SND_EVENT_UP); + break; + default: + break; + } + return NOTIFY_OK; +} + +static struct notifier_block service_nb = { + .notifier_call = lpi_notifier_service_cb, + .priority = -INT_MAX, +}; + +static void lpi_pinctrl_ssr_disable(struct device *dev, void *data) +{ + lpi_dev_up = false; +} + +static const struct snd_event_ops lpi_pinctrl_ssr_ops = { + .disable = lpi_pinctrl_ssr_disable, +}; + +#ifdef CONFIG_DEBUG_FS +#include + +static unsigned int lpi_regval_to_drive(u32 val) +{ + return (val + 1) * 2; +} + +static void lpi_gpio_dbg_show_one(struct seq_file *s, + struct pinctrl_dev *pctldev, + struct gpio_chip *chip, + unsigned int offset, + unsigned int gpio) +{ + struct lpi_gpio_state *state = gpiochip_get_data(chip); + struct pinctrl_pin_desc pindesc; + struct lpi_gpio_pad *pad; + unsigned int func; + int is_out; + int drive; + int pull; + u32 ctl_reg; + + static const char * const pulls[] = { + "no pull", + "pull down", + "keeper", + "pull up" + }; + + pctldev = pctldev ? : state->ctrl; + pindesc = pctldev->desc->pins[offset]; + pad = pctldev->desc->pins[offset].drv_data; + ctl_reg = lpi_gpio_read(pad, LPI_GPIO_REG_DIR_CTL); + is_out = (ctl_reg & LPI_GPIO_REG_DIR_MASK) >> LPI_GPIO_REG_DIR_SHIFT; + ctl_reg = lpi_gpio_read(pad, LPI_GPIO_REG_VAL_CTL); + + func = (ctl_reg & LPI_GPIO_REG_FUNCTION_MASK) >> + LPI_GPIO_REG_FUNCTION_SHIFT; + drive = (ctl_reg & LPI_GPIO_REG_OUT_STRENGTH_MASK) >> + LPI_GPIO_REG_OUT_STRENGTH_SHIFT; + pull = (ctl_reg & LPI_GPIO_REG_PULL_MASK) >> LPI_GPIO_REG_PULL_SHIFT; + + seq_printf(s, " %-8s: %-3s %d", + pindesc.name, is_out ? "out" : "in", func); + seq_printf(s, " %dmA", lpi_regval_to_drive(drive)); + seq_printf(s, " %s", pulls[pull]); +} + +static void lpi_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) +{ + unsigned int gpio = chip->base; + unsigned int i; + + for (i = 0; i < chip->ngpio; i++, gpio++) { + lpi_gpio_dbg_show_one(s, NULL, chip, i, gpio); + seq_puts(s, "\n"); + } +} + +#else +#define lpi_gpio_dbg_show NULL +#endif + +static const struct gpio_chip lpi_gpio_template = { + .direction_input = lpi_gpio_direction_input, + .direction_output = lpi_gpio_direction_output, + .get = lpi_gpio_get, + .set = lpi_gpio_set, + .request = gpiochip_generic_request, + .free = gpiochip_generic_free, + .dbg_show = lpi_gpio_dbg_show, +}; + +static int lpi_get_lpass_core_hw_clk(struct device *dev, + struct lpi_gpio_state *state) +{ + struct clk *lpass_core_hw_vote = NULL; + int ret = 0; + + if (state->lpass_core_hw_vote == NULL) { + lpass_core_hw_vote = devm_clk_get(dev, "lpass_core_hw_vote"); + if (IS_ERR(lpass_core_hw_vote) || lpass_core_hw_vote == NULL) { + ret = PTR_ERR(lpass_core_hw_vote); + dev_dbg(dev, "%s: clk get %s failed %d\n", + __func__, "lpass_core_hw_vote", ret); + return ret; + } + state->lpass_core_hw_vote = lpass_core_hw_vote; + } + return 0; +} + +static int lpi_pinctrl_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct pinctrl_pin_desc *pindesc; + struct pinctrl_desc *pctrldesc; + struct lpi_gpio_pad *pad, *pads; + struct lpi_gpio_state *state; + int ret, npins, i; + char __iomem *lpi_base; + u32 reg; + + ret = of_property_read_u32(dev->of_node, "reg", ®); + if (ret < 0) { + dev_err(dev, "missing base address\n"); + return ret; + } + + ret = of_property_read_u32(dev->of_node, "qcom,num-gpios", &npins); + if (ret < 0) + return ret; + + WARN_ON(npins > ARRAY_SIZE(lpi_gpio_groups)); + + ret = of_property_read_u32_array(dev->of_node, "qcom,lpi-offset-tbl", + lpi_offset, npins); + if (ret < 0) { + dev_err(dev, "error in reading lpi offset table: %d\n", ret); + return ret; + } + + state = devm_kzalloc(dev, sizeof(*state), GFP_KERNEL); + if (!state) + return -ENOMEM; + + platform_set_drvdata(pdev, state); + + state->dev = &pdev->dev; + + pindesc = devm_kcalloc(dev, npins, sizeof(*pindesc), GFP_KERNEL); + if (!pindesc) + return -ENOMEM; + + pads = devm_kcalloc(dev, npins, sizeof(*pads), GFP_KERNEL); + if (!pads) + return -ENOMEM; + + pctrldesc = devm_kzalloc(dev, sizeof(*pctrldesc), GFP_KERNEL); + if (!pctrldesc) + return -ENOMEM; + + pctrldesc->pctlops = &lpi_gpio_pinctrl_ops; + pctrldesc->pmxops = &lpi_gpio_pinmux_ops; + pctrldesc->confops = &lpi_gpio_pinconf_ops; + pctrldesc->owner = THIS_MODULE; + pctrldesc->name = dev_name(dev); + pctrldesc->pins = pindesc; + pctrldesc->npins = npins; + + lpi_base = devm_ioremap(dev, reg, LPI_ADDRESS_SIZE); + if (lpi_base == NULL) { + dev_err(dev, "%s devm_ioremap failed\n", __func__); + return -ENOMEM; + } + + state->base = lpi_base; + + for (i = 0; i < npins; i++, pindesc++) { + pad = &pads[i]; + pindesc->drv_data = pad; + pindesc->number = i; + pindesc->name = lpi_gpio_groups[i]; + + pad->base = lpi_base; + pad->offset = lpi_offset[i]; + } + + state->chip = lpi_gpio_template; + state->chip.parent = dev; + state->chip.base = -1; + state->chip.ngpio = npins; + state->chip.label = dev_name(dev); + state->chip.of_gpio_n_cells = 2; + state->chip.can_sleep = false; + + state->ctrl = devm_pinctrl_register(dev, pctrldesc, state); + if (IS_ERR(state->ctrl)) + return PTR_ERR(state->ctrl); + + ret = gpiochip_add_data(&state->chip, state); + if (ret) { + dev_err(state->dev, "can't add gpio chip\n"); + goto err_chip; + } + + ret = gpiochip_add_pin_range(&state->chip, dev_name(dev), 0, 0, npins); + if (ret) { + dev_err(dev, "failed to add pin range\n"); + goto err_range; + } + + lpi_dev = &pdev->dev; + lpi_dev_up = true; + ret = audio_notifier_register("lpi_tlmm", AUDIO_NOTIFIER_ADSP_DOMAIN, + &service_nb); + if (ret < 0) { + pr_err("%s: Audio notifier register failed ret = %d\n", + __func__, ret); + goto err_range; + } + + ret = snd_event_client_register(dev, &lpi_pinctrl_ssr_ops, NULL); + if (!ret) { + snd_event_notify(dev, SND_EVENT_UP); + } else { + dev_err(dev, "%s: snd_event registration failed, ret [%d]\n", + __func__, ret); + goto err_snd_evt; + } + + /* Register LPASS core hw vote */ + ret = lpi_get_lpass_core_hw_clk(dev, state); + if (ret) + dev_dbg(dev, "%s: unable to get core clk handle %d\n", + __func__, ret); + + pm_runtime_set_autosuspend_delay(&pdev->dev, LPI_AUTO_SUSPEND_DELAY); + pm_runtime_use_autosuspend(&pdev->dev); + pm_runtime_set_suspended(&pdev->dev); + pm_runtime_enable(&pdev->dev); + + return 0; + +err_snd_evt: + audio_notifier_deregister("lpi_tlmm"); +err_range: + gpiochip_remove(&state->chip); +err_chip: + return ret; +} + +static int lpi_pinctrl_remove(struct platform_device *pdev) +{ + struct lpi_gpio_state *state = platform_get_drvdata(pdev); + pm_runtime_disable(&pdev->dev); + pm_runtime_set_suspended(&pdev->dev); + + snd_event_client_deregister(&pdev->dev); + audio_notifier_deregister("lpi_tlmm"); + gpiochip_remove(&state->chip); + return 0; +} + +static const struct of_device_id lpi_pinctrl_of_match[] = { + { .compatible = "qcom,lpi-pinctrl" }, /* Generic */ + { }, +}; + +MODULE_DEVICE_TABLE(of, lpi_pinctrl_of_match); + +static int lpi_pinctrl_runtime_resume(struct device *dev) +{ + struct lpi_gpio_state *state = dev_get_drvdata(dev); + int ret = 0; + + ret = lpi_get_lpass_core_hw_clk(dev, state); + if (ret) { + dev_err(dev, "%s: unable to get core clk handle %d\n", + __func__, ret); + return 0; + } + + ret = clk_prepare_enable(state->lpass_core_hw_vote); + if (ret < 0) + dev_err(dev, "%s: lpass core hw enable failed\n", + __func__); + + pm_runtime_set_autosuspend_delay(dev, LPI_AUTO_SUSPEND_DELAY); + return 0; +} + +static int lpi_pinctrl_runtime_suspend(struct device *dev) +{ + struct lpi_gpio_state *state = dev_get_drvdata(dev); + int ret = 0; + + ret = lpi_get_lpass_core_hw_clk(dev, state); + if (ret) { + dev_err(dev, "%s: unable to get core clk handle %d\n", + __func__, ret); + return 0; + } + + clk_disable_unprepare(state->lpass_core_hw_vote); + return 0; +} + +static const struct dev_pm_ops lpi_pinctrl_dev_pm_ops = { + SET_RUNTIME_PM_OPS( + lpi_pinctrl_runtime_suspend, + lpi_pinctrl_runtime_resume, + NULL + ) +}; + +static struct platform_driver lpi_pinctrl_driver = { + .driver = { + .name = "qcom-lpi-pinctrl", + .pm = &lpi_pinctrl_dev_pm_ops, + .of_match_table = lpi_pinctrl_of_match, + }, + .probe = lpi_pinctrl_probe, + .remove = lpi_pinctrl_remove, +}; + +module_platform_driver(lpi_pinctrl_driver); + +MODULE_DESCRIPTION("QTI LPI GPIO pin control driver"); +MODULE_LICENSE("GPL v2"); diff --git a/audio-kernel/soc/pinctrl-utils.h b/audio-kernel/soc/pinctrl-utils.h new file mode 120000 index 000000000..0f74549b3 --- /dev/null +++ b/audio-kernel/soc/pinctrl-utils.h @@ -0,0 +1 @@ +../../../../../kernel/msm-4.14/drivers/pinctrl/pinctrl-utils.h \ No newline at end of file diff --git a/audio-kernel/soc/pinctrl-wcd.c b/audio-kernel/soc/pinctrl-wcd.c new file mode 100644 index 000000000..d7695dbe7 --- /dev/null +++ b/audio-kernel/soc/pinctrl-wcd.c @@ -0,0 +1,435 @@ +/* + * Copyright (c) 2016-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 + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "core.h" +#include "pinctrl-utils.h" + +#define WCD_REG_DIR_CTL WCD934X_CHIP_TIER_CTRL_GPIO_CTL_OE +#define WCD_REG_VAL_CTL WCD934X_CHIP_TIER_CTRL_GPIO_CTL_DATA +#define WCD_GPIO_PULL_UP 1 +#define WCD_GPIO_PULL_DOWN 2 +#define WCD_GPIO_BIAS_DISABLE 3 +#define WCD_GPIO_STRING_LEN 20 + +/** + * struct wcd_gpio_pad - keep current GPIO settings + * @offset: offset of gpio. + * @is_valid: Set to false, when GPIO in high Z state. + * @value: value of a pin + * @output_enabled: Set to true if GPIO is output and false if it is input + * @pullup: Constant current which flow through GPIO output buffer. + * @strength: Drive strength of a pin + */ +struct wcd_gpio_pad { + u16 offset; + bool is_valid; + bool value; + bool output_enabled; + unsigned int pullup; + unsigned int strength; +}; + +struct wcd_gpio_priv { + struct device *dev; + struct regmap *map; + struct pinctrl_dev *ctrl; + struct gpio_chip chip; +}; + +static int wcd_gpio_read(struct wcd_gpio_priv *priv_data, + struct wcd_gpio_pad *pad, unsigned int addr) +{ + unsigned int val; + int ret; + + ret = regmap_read(priv_data->map, addr, &val); + if (ret < 0) + dev_err(priv_data->dev, "%s: read 0x%x failed\n", + __func__, addr); + else + ret = (val >> pad->offset); + + return ret; +} + +static int wcd_gpio_write(struct wcd_gpio_priv *priv_data, + struct wcd_gpio_pad *pad, unsigned int addr, + unsigned int val) +{ + int ret; + + ret = regmap_update_bits(priv_data->map, addr, (1 << pad->offset), + val << pad->offset); + if (ret < 0) + dev_err(priv_data->dev, "write 0x%x failed\n", addr); + + return ret; +} + +static int wcd_get_groups_count(struct pinctrl_dev *pctldev) +{ + return pctldev->desc->npins; +} + +static const char *wcd_get_group_name(struct pinctrl_dev *pctldev, + unsigned int pin) +{ + return pctldev->desc->pins[pin].name; +} + +static int wcd_get_group_pins(struct pinctrl_dev *pctldev, unsigned int pin, + const unsigned int **pins, unsigned int *num_pins) +{ + *pins = &pctldev->desc->pins[pin].number; + *num_pins = 1; + return 0; +} + +static const struct pinctrl_ops wcd_pinctrl_ops = { + .get_groups_count = wcd_get_groups_count, + .get_group_name = wcd_get_group_name, + .get_group_pins = wcd_get_group_pins, + .dt_node_to_map = pinconf_generic_dt_node_to_map_group, + .dt_free_map = pinctrl_utils_free_map, +}; + +static int wcd_config_get(struct pinctrl_dev *pctldev, + unsigned int pin, unsigned long *config) +{ + unsigned int param = pinconf_to_config_param(*config); + struct wcd_gpio_pad *pad; + unsigned int arg; + + pad = pctldev->desc->pins[pin].drv_data; + + switch (param) { + case PIN_CONFIG_BIAS_PULL_DOWN: + arg = pad->pullup == WCD_GPIO_PULL_DOWN; + break; + case PIN_CONFIG_BIAS_DISABLE: + arg = pad->pullup = WCD_GPIO_BIAS_DISABLE; + break; + case PIN_CONFIG_BIAS_PULL_UP: + arg = pad->pullup == WCD_GPIO_PULL_UP; + break; + case PIN_CONFIG_BIAS_HIGH_IMPEDANCE: + arg = !pad->is_valid; + break; + case PIN_CONFIG_INPUT_ENABLE: + arg = pad->output_enabled; + break; + case PIN_CONFIG_OUTPUT: + arg = pad->value; + break; + default: + return -EINVAL; + } + + *config = pinconf_to_config_packed(param, arg); + return 0; +} + +static int wcd_config_set(struct pinctrl_dev *pctldev, unsigned int pin, + unsigned long *configs, unsigned int nconfs) +{ + struct wcd_gpio_priv *priv_data = pinctrl_dev_get_drvdata(pctldev); + struct wcd_gpio_pad *pad; + unsigned int param, arg; + int i, ret; + + pad = pctldev->desc->pins[pin].drv_data; + + for (i = 0; i < nconfs; i++) { + param = pinconf_to_config_param(configs[i]); + arg = pinconf_to_config_argument(configs[i]); + + dev_dbg(priv_data->dev, "%s: param: %d arg: %d", + __func__, param, arg); + + switch (param) { + case PIN_CONFIG_BIAS_DISABLE: + pad->pullup = WCD_GPIO_BIAS_DISABLE; + break; + case PIN_CONFIG_BIAS_PULL_UP: + pad->pullup = WCD_GPIO_PULL_UP; + break; + case PIN_CONFIG_BIAS_PULL_DOWN: + pad->pullup = WCD_GPIO_PULL_DOWN; + break; + case PIN_CONFIG_BIAS_HIGH_IMPEDANCE: + pad->is_valid = false; + break; + case PIN_CONFIG_INPUT_ENABLE: + pad->output_enabled = false; + break; + case PIN_CONFIG_OUTPUT: + pad->output_enabled = true; + pad->value = arg; + break; + case PIN_CONFIG_DRIVE_STRENGTH: + pad->strength = arg; + break; + default: + ret = -EINVAL; + goto done; + } + } + + if (pad->output_enabled) { + ret = wcd_gpio_write(priv_data, pad, WCD_REG_DIR_CTL, + pad->output_enabled); + if (ret < 0) + goto done; + ret = wcd_gpio_write(priv_data, pad, WCD_REG_VAL_CTL, + pad->value); + } else + ret = wcd_gpio_write(priv_data, pad, WCD_REG_DIR_CTL, + pad->output_enabled); +done: + return ret; +} + +static const struct pinconf_ops wcd_pinconf_ops = { + .is_generic = true, + .pin_config_group_get = wcd_config_get, + .pin_config_group_set = wcd_config_set, +}; + +static int wcd_gpio_direction_input(struct gpio_chip *chip, unsigned int pin) +{ + struct wcd_gpio_priv *priv_data = gpiochip_get_data(chip); + unsigned long config; + + config = pinconf_to_config_packed(PIN_CONFIG_INPUT_ENABLE, 1); + + return wcd_config_set(priv_data->ctrl, pin, &config, 1); +} + +static int wcd_gpio_direction_output(struct gpio_chip *chip, + unsigned int pin, int val) +{ + struct wcd_gpio_priv *priv_data = gpiochip_get_data(chip); + unsigned long config; + + config = pinconf_to_config_packed(PIN_CONFIG_OUTPUT, val); + + return wcd_config_set(priv_data->ctrl, pin, &config, 1); +} + +static int wcd_gpio_get(struct gpio_chip *chip, unsigned int pin) +{ + struct wcd_gpio_priv *priv_data = gpiochip_get_data(chip); + struct wcd_gpio_pad *pad; + int value; + + pad = priv_data->ctrl->desc->pins[pin].drv_data; + + if (!pad->is_valid) + return -EINVAL; + + value = wcd_gpio_read(priv_data, pad, WCD_REG_VAL_CTL); + return value; +} + +static void wcd_gpio_set(struct gpio_chip *chip, unsigned int pin, int value) +{ + struct wcd_gpio_priv *priv_data = gpiochip_get_data(chip); + unsigned long config; + + config = pinconf_to_config_packed(PIN_CONFIG_OUTPUT, value); + + wcd_config_set(priv_data->ctrl, pin, &config, 1); +} + +static const struct gpio_chip wcd_gpio_chip = { + .direction_input = wcd_gpio_direction_input, + .direction_output = wcd_gpio_direction_output, + .get = wcd_gpio_get, + .set = wcd_gpio_set, +}; + +static int wcd_pinctrl_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct pinctrl_pin_desc *pindesc; + struct pinctrl_desc *pctrldesc; + struct wcd_gpio_pad *pad, *pads; + struct wcd_gpio_priv *priv_data; + int ret, i, j; + u32 npins; + char **name; + + ret = of_property_read_u32(dev->of_node, "qcom,num-gpios", &npins); + if (ret) { + dev_err(dev, "%s: Looking up %s property in node %s failed\n", + __func__, "qcom,num-gpios", dev->of_node->full_name); + ret = -EINVAL; + goto err_priv_alloc; + } + if (!npins) { + dev_err(dev, "%s: no.of pins are 0\n", __func__); + ret = -EINVAL; + goto err_priv_alloc; + } + + priv_data = devm_kzalloc(dev, sizeof(*priv_data), GFP_KERNEL); + if (!priv_data) { + ret = -ENOMEM; + goto err_priv_alloc; + } + + priv_data->dev = dev; + priv_data->map = dev_get_regmap(dev->parent, NULL); + if (!priv_data->map) { + dev_err(dev, "%s: failed to get regmap\n", __func__); + ret = -EINVAL; + goto err_regmap; + } + + pindesc = devm_kcalloc(dev, npins, sizeof(*pindesc), GFP_KERNEL); + if (!pindesc) { + ret = -ENOMEM; + goto err_pinsec_alloc; + } + + pads = devm_kcalloc(dev, npins, sizeof(*pads), GFP_KERNEL); + if (!pads) { + ret = -ENOMEM; + goto err_pads_alloc; + } + + pctrldesc = devm_kzalloc(dev, sizeof(*pctrldesc), GFP_KERNEL); + if (!pctrldesc) { + ret = -ENOMEM; + goto err_pinctrl_alloc; + } + + pctrldesc->pctlops = &wcd_pinctrl_ops; + pctrldesc->confops = &wcd_pinconf_ops; + pctrldesc->owner = THIS_MODULE; + pctrldesc->name = dev_name(dev); + pctrldesc->pins = pindesc; + pctrldesc->npins = npins; + + name = devm_kcalloc(dev, npins, sizeof(char *), GFP_KERNEL); + if (!name) { + ret = -ENOMEM; + goto err_name_alloc; + } + for (i = 0; i < npins; i++, pindesc++) { + name[i] = devm_kzalloc(dev, sizeof(char) * WCD_GPIO_STRING_LEN, + GFP_KERNEL); + if (!name[i]) { + ret = -ENOMEM; + goto err_pin; + } + pad = &pads[i]; + pindesc->drv_data = pad; + pindesc->number = i; + snprintf(name[i], (WCD_GPIO_STRING_LEN - 1), "gpio%d", (i+1)); + pindesc->name = name[i]; + pad->offset = i; + pad->is_valid = true; + } + + priv_data->chip = wcd_gpio_chip; + priv_data->chip.parent = dev; + priv_data->chip.base = -1; + priv_data->chip.ngpio = npins; + priv_data->chip.label = dev_name(dev); + priv_data->chip.of_gpio_n_cells = 2; + priv_data->chip.can_sleep = false; + + priv_data->ctrl = devm_pinctrl_register(dev, pctrldesc, priv_data); + if (IS_ERR(priv_data->ctrl)) { + dev_err(dev, "%s: failed to register to pinctrl\n", __func__); + ret = PTR_ERR(priv_data->ctrl); + goto err_pin; + } + + ret = gpiochip_add_data(&priv_data->chip, priv_data); + if (ret) { + dev_err(dev, "%s: can't add gpio chip\n", __func__); + goto err_pin; + } + + ret = gpiochip_add_pin_range(&priv_data->chip, dev_name(dev), 0, 0, + npins); + if (ret) { + dev_err(dev, "%s: failed to add pin range\n", __func__); + goto err_range; + } + platform_set_drvdata(pdev, priv_data); + + return 0; + +err_range: + gpiochip_remove(&priv_data->chip); +err_pin: + for (j = 0; j < i; j++) + devm_kfree(dev, name[j]); + devm_kfree(dev, name); +err_name_alloc: + devm_kfree(dev, pctrldesc); +err_pinctrl_alloc: + devm_kfree(dev, pads); +err_pads_alloc: + devm_kfree(dev, pindesc); +err_pinsec_alloc: +err_regmap: + devm_kfree(dev, priv_data); +err_priv_alloc: + return ret; +} + +static int wcd_pinctrl_remove(struct platform_device *pdev) +{ + struct wcd_gpio_priv *priv_data = platform_get_drvdata(pdev); + + gpiochip_remove(&priv_data->chip); + + return 0; +} + +static const struct of_device_id wcd_pinctrl_of_match[] = { + { .compatible = "qcom,wcd-pinctrl" }, + { }, +}; + +MODULE_DEVICE_TABLE(of, wcd_pinctrl_of_match); + +static struct platform_driver wcd_pinctrl_driver = { + .driver = { + .name = "qcom-wcd-pinctrl", + .of_match_table = wcd_pinctrl_of_match, + }, + .probe = wcd_pinctrl_probe, + .remove = wcd_pinctrl_remove, +}; + +module_platform_driver(wcd_pinctrl_driver); + +MODULE_DESCRIPTION("Qualcomm Technologies, Inc WCD GPIO pin control driver"); +MODULE_LICENSE("GPL v2"); diff --git a/audio-kernel/soc/regmap-swr.c b/audio-kernel/soc/regmap-swr.c new file mode 100644 index 000000000..ba092a2aa --- /dev/null +++ b/audio-kernel/soc/regmap-swr.c @@ -0,0 +1,216 @@ +/* + * 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 + * 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 +#include +#include +#include +#include +#include +#include +#include + + +static int regmap_swr_gather_write(void *context, + const void *reg, size_t reg_size, + const void *val, size_t val_len) +{ + struct device *dev = context; + struct swr_device *swr = to_swr_device(dev); + struct regmap *map = dev_get_regmap(dev, NULL); + size_t addr_bytes; + size_t val_bytes; + int i, ret = 0; + u16 reg_addr = 0; + u8 *value; + + if (map == NULL) { + dev_err(dev, "%s: regmap is NULL\n", __func__); + return -EINVAL; + } + addr_bytes = map->format.reg_bytes; + if (swr == NULL) { + dev_err(dev, "%s: swr device is NULL\n", __func__); + return -EINVAL; + } + if (reg_size != addr_bytes) { + dev_err(dev, "%s: reg size %zd bytes not supported\n", + __func__, reg_size); + return -EINVAL; + } + reg_addr = *(u16 *)reg; + val_bytes = map->format.val_bytes; + /* val_len = val_bytes * val_count */ + for (i = 0; i < (val_len / val_bytes); i++) { + value = (u8 *)val + (val_bytes * i); + ret = swr_write(swr, swr->dev_num, (reg_addr + i), value); + if (ret < 0) { + dev_err(dev, "%s: write reg 0x%x failed, err %d\n", + __func__, (reg_addr + i), ret); + break; + } + } + return ret; +} + +static int regmap_swr_raw_multi_reg_write(void *context, const void *data, + size_t count) +{ + struct device *dev = context; + struct swr_device *swr = to_swr_device(dev); + struct regmap *map = dev_get_regmap(dev, NULL); + size_t addr_bytes; + size_t val_bytes; + size_t pad_bytes; + size_t num_regs; + int i = 0; + int ret = 0; + u16 *reg; + u8 *val; + u8 *buf; + + if (swr == NULL) { + dev_err(dev, "%s: swr device is NULL\n", __func__); + return -EINVAL; + } + + if (map == NULL) { + dev_err(dev, "%s: regmap is NULL\n", __func__); + return -EINVAL; + } + + addr_bytes = map->format.reg_bytes; + val_bytes = map->format.val_bytes; + pad_bytes = map->format.pad_bytes; + + if (addr_bytes + val_bytes + pad_bytes == 0) { + dev_err(dev, "%s: sum of addr, value and pad is 0\n", __func__); + return -EINVAL; + } + num_regs = count / (addr_bytes + val_bytes + pad_bytes); + + reg = kcalloc(num_regs, sizeof(u16), GFP_KERNEL); + if (!reg) + return -ENOMEM; + + val = kcalloc(num_regs, sizeof(u8), GFP_KERNEL); + if (!val) { + ret = -ENOMEM; + goto mem_fail; + } + + buf = (u8 *)data; + for (i = 0; i < num_regs; i++) { + reg[i] = *(u16 *)buf; + buf += (map->format.reg_bytes + map->format.pad_bytes); + val[i] = *buf; + buf += map->format.val_bytes; + } + ret = swr_bulk_write(swr, swr->dev_num, reg, val, num_regs); + if (ret) + dev_err(dev, "%s: multi reg write failed\n", __func__); + + kfree(val); +mem_fail: + kfree(reg); + return ret; +} + +static int regmap_swr_write(void *context, const void *data, size_t count) +{ + struct device *dev = context; + struct regmap *map = dev_get_regmap(dev, NULL); + size_t addr_bytes; + size_t val_bytes; + size_t pad_bytes; + + if (map == NULL) { + dev_err(dev, "%s: regmap is NULL\n", __func__); + return -EINVAL; + } + addr_bytes = map->format.reg_bytes; + val_bytes = map->format.val_bytes; + pad_bytes = map->format.pad_bytes; + + WARN_ON(count < addr_bytes); + + if (count > (addr_bytes + val_bytes + pad_bytes)) + return regmap_swr_raw_multi_reg_write(context, data, count); + else + return regmap_swr_gather_write(context, data, addr_bytes, + (data + addr_bytes), + (count - addr_bytes)); +} + +static int regmap_swr_read(void *context, + const void *reg, size_t reg_size, + void *val, size_t val_size) +{ + struct device *dev = context; + struct swr_device *swr = to_swr_device(dev); + struct regmap *map = dev_get_regmap(dev, NULL); + size_t addr_bytes; + int ret = 0; + u16 reg_addr = 0; + + if (map == NULL) { + dev_err(dev, "%s: regmap is NULL\n", __func__); + return -EINVAL; + } + addr_bytes = map->format.reg_bytes; + if (swr == NULL) { + dev_err(dev, "%s: swr is NULL\n", __func__); + return -EINVAL; + } + if (reg_size != addr_bytes) { + dev_err(dev, "%s: register size %zd bytes not supported\n", + __func__, reg_size); + return -EINVAL; + } + reg_addr = *(u16 *)reg; + ret = swr_read(swr, swr->dev_num, reg_addr, val, val_size); + if (ret < 0) + dev_err(dev, "%s: codec reg 0x%x read failed %d\n", + __func__, reg_addr, ret); + return ret; +} + +static struct regmap_bus regmap_swr = { + .write = regmap_swr_write, + .gather_write = regmap_swr_gather_write, + .read = regmap_swr_read, + .reg_format_endian_default = REGMAP_ENDIAN_NATIVE, + .val_format_endian_default = REGMAP_ENDIAN_NATIVE, +}; + +struct regmap *__regmap_init_swr(struct swr_device *swr, + const struct regmap_config *config, + struct lock_class_key *lock_key, + const char *lock_name) +{ + return __regmap_init(&swr->dev, ®map_swr, &swr->dev, config, + lock_key, lock_name); +} +EXPORT_SYMBOL(__regmap_init_swr); + +struct regmap *__devm_regmap_init_swr(struct swr_device *swr, + const struct regmap_config *config, + struct lock_class_key *lock_key, + const char *lock_name) +{ + return __devm_regmap_init(&swr->dev, ®map_swr, &swr->dev, config, + lock_key, lock_name); +} +EXPORT_SYMBOL(__devm_regmap_init_swr); + +MODULE_LICENSE("GPL v2"); diff --git a/audio-kernel/soc/snd_event.c b/audio-kernel/soc/snd_event.c new file mode 100644 index 000000000..f8bffb81c --- /dev/null +++ b/audio-kernel/soc/snd_event.c @@ -0,0 +1,494 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2018, The Linux Foundation. All rights reserved. + */ + +#include +#include +#include +#include +#include + +struct snd_event_client { + struct list_head node; + + struct device *dev; + const struct snd_event_ops *ops; + void *data; + + bool attached; + bool state; +}; + +struct snd_event_client_array { + struct device *dev; + struct snd_event_client *clnt; + void *data; + int (*compare)(struct device *, void *); +}; + +struct snd_event_clients { + size_t num_clients; + struct snd_event_client_array *cl_arr; +}; + +struct snd_master { + struct device *dev; + const struct snd_event_ops *ops; + void *data; + + bool state; + bool fwk_state; + bool clients_found; + struct snd_event_clients *clients; +}; + +static DEFINE_MUTEX(snd_event_mutex); +static LIST_HEAD(snd_event_client_list); +static struct snd_master *master; + +static struct snd_event_client *find_snd_event_client(struct device *dev) +{ + struct snd_event_client *c; + + list_for_each_entry(c, &snd_event_client_list, node) + if ((c->dev == dev) && c->ops) + return c; + + return NULL; +} + +static int check_and_update_fwk_state(void) +{ + bool new_fwk_state = true; + struct snd_event_client *c; + int ret = 0; + int i = 0; + + for (i = 0; i < master->clients->num_clients; i++) { + c = master->clients->cl_arr[i].clnt; + new_fwk_state &= c->state; + } + new_fwk_state &= master->state; + + if (master->fwk_state ^ new_fwk_state) { + if (new_fwk_state) { + for (i = 0; i < master->clients->num_clients; i++) { + c = master->clients->cl_arr[i].clnt; + if (c->ops->enable) { + ret = c->ops->enable(c->dev, c->data); + if (ret) { + dev_err(c->dev, + "%s: enable failed\n", + __func__); + goto dev_en_failed; + } + } + } + if (master->ops->enable) { + ret = master->ops->enable(master->dev, + master->data); + if (ret) { + dev_err(master->dev, + "%s: enable failed\n", + __func__); + goto mstr_en_failed; + } + } + } else { + if (master->ops->disable) + master->ops->disable(master->dev, + master->data); + for (i = 0; i < master->clients->num_clients; i++) { + c = master->clients->cl_arr[i].clnt; + if (c->ops->disable) + c->ops->disable(c->dev, c->data); + } + } + master->fwk_state = new_fwk_state; + } + goto exit; + +mstr_en_failed: + i = master->clients->num_clients; +dev_en_failed: + for (; i > 0; i--) { + c = master->clients->cl_arr[i - 1].clnt; + if (c->ops->disable) + c->ops->disable(c->dev, c->data); + } +exit: + return ret; +} + +static int snd_event_find_clients(struct snd_master *master) +{ + struct snd_event_clients *clients = master->clients; + int i = 0; + int ret = 0; + + for (i = 0; i < clients->num_clients; i++) { + struct snd_event_client_array *c_arr = &clients->cl_arr[i]; + struct snd_event_client *c; + + if (c_arr->dev) { + pr_err("%s: client already present dev=%pK\n", + __func__, c_arr->dev); + continue; + } + + list_for_each_entry(c, &snd_event_client_list, node) { + if (c->attached) + continue; + + if (c_arr->compare(c->dev, c_arr->data)) { + dev_dbg(master->dev, + "%s: found client, dev=%pK\n", + __func__, c->dev); + c_arr->dev = c->dev; + c_arr->clnt = c; + c->attached = true; + break; + } + } + if (!c_arr->dev) { + dev_dbg(master->dev, + "%s: failed to find some client\n", + __func__); + ret = -ENXIO; + break; + } + } + + return ret; +} + +/* + * snd_event_client_register - Register a client with the SND event FW + * + * @dev: Pointer to the "struct device" associated with the client + * @snd_ev_ops: Pointer to the snd_event_ops struct for the client containing + * callback functions + * @data: Pointer to any additional data that the caller wants to get back + * with callback functions + * + * Returns 0 on success or error on failure. + */ +int snd_event_client_register(struct device *dev, + const struct snd_event_ops *snd_ev_ops, + void *data) +{ + struct snd_event_client *c; + + if (!dev) { + pr_err("%s: dev is NULL\n", __func__); + return -EINVAL; + } + + c = kzalloc(sizeof(*c), GFP_KERNEL); + if (!c) + return -ENOMEM; + + c->dev = dev; + c->ops = snd_ev_ops; + c->data = data; + + dev_dbg(dev, "%s: adding client to SND event FW (ops %pK)\n", + __func__, snd_ev_ops); + + mutex_lock(&snd_event_mutex); + list_add_tail(&c->node, &snd_event_client_list); + + if (master && !master->clients_found) { + if (snd_event_find_clients(master)) { + dev_dbg(dev, "%s: Failed to find all clients\n", + __func__); + goto exit; + } + master->clients_found = true; + } + +exit: + mutex_unlock(&snd_event_mutex); + return 0; +} +EXPORT_SYMBOL(snd_event_client_register); + +/* + * snd_event_client_deregister - Remove a client from the SND event FW + * + * @dev: Pointer to the "struct device" associated with the client + * + * Returns 0 on success or error on failure. + */ +int snd_event_client_deregister(struct device *dev) +{ + struct snd_event_client *c; + int ret = 0; + int i = 0; + + if (!dev) { + pr_err("%s: dev is NULL\n", __func__); + return -EINVAL; + } + + mutex_lock(&snd_event_mutex); + if (list_empty(&snd_event_client_list)) { + dev_dbg(dev, "%s: No SND client registered\n", __func__); + ret = -ENODEV; + goto exit; + } + + c = find_snd_event_client(dev); + if (!c || (c->dev != dev)) { + dev_dbg(dev, "%s: No matching snd dev found\n", __func__); + ret = -ENODEV; + goto exit; + } + + c->state = false; + + if (master && master->clients_found) { + struct snd_event_client *d; + bool dev_found = false; + + for (i = 0; i < master->clients->num_clients; i++) { + d = master->clients->cl_arr[i].clnt; + if (c->dev == d->dev) { + dev_found = true; + break; + } + } + if (dev_found) { + ret = check_and_update_fwk_state(); + master->clients_found = false; + } + } + + list_del(&c->node); + kfree(c); +exit: + mutex_unlock(&snd_event_mutex); + return ret; +} +EXPORT_SYMBOL(snd_event_client_deregister); + +/* + * snd_event_mstr_add_client - Add a client to the master's list of clients + * + * @snd_clients: list of clients associated with this master + * @compare: Pointer to the compare callback function that master will use to + * confirm the clients + * @data: Address to any additional data that the master wants to get back with + * compare callback functions + */ +void snd_event_mstr_add_client(struct snd_event_clients **snd_clients, + int (*compare)(struct device *, void *), + void *data) +{ + struct snd_event_clients *client = *snd_clients; + + if (IS_ERR(client)) { + pr_err("%s: snd_clients is invalid\n", __func__); + return; + } + + if (!client) { + client = kzalloc(sizeof(*client), GFP_KERNEL); + if (!client) { + *snd_clients = ERR_PTR(-ENOMEM); + return; + } + client->cl_arr = kzalloc(sizeof(struct snd_event_client_array), + GFP_KERNEL); + if (!client->cl_arr) { + *snd_clients = ERR_PTR(-ENOMEM); + return; + } + *snd_clients = client; + } else { + struct snd_event_client_array *new; + + new = krealloc(client->cl_arr, + (client->num_clients + 1) * sizeof(*new), + GFP_KERNEL | __GFP_ZERO); + if (!new) { + *snd_clients = ERR_PTR(-ENOMEM); + return; + } + client->cl_arr = new; + } + + client->cl_arr[client->num_clients].dev = NULL; + client->cl_arr[client->num_clients].data = data; + client->cl_arr[client->num_clients].compare = compare; + client->num_clients++; +} +EXPORT_SYMBOL(snd_event_mstr_add_client); + +/* + * snd_event_master_register - Register a master with the SND event FW + * + * @dev: Pointer to the "struct device" associated with the master + * @ops: Pointer to the snd_event_ops struct for the master containing + * callback functions + * @clients: List of clients for the master + * @data: Pointer to any additional data that the caller wants to get back + * with callback functions + * + * Returns 0 on success or error on failure. + * + * Prerequisite: + * clients list must not be empty. + * All clients for the master must have to be registered by calling + * snd_event_mstr_add_client() before calling this API to register a + * master with SND event fwk. + */ +int snd_event_master_register(struct device *dev, + const struct snd_event_ops *ops, + struct snd_event_clients *clients, + void *data) +{ + struct snd_master *new_master; + int ret = 0; + + if (!dev) { + pr_err("%s: dev is NULL\n", __func__); + return -EINVAL; + } + + mutex_lock(&snd_event_mutex); + if (master) { + dev_err(dev, "%s: master already allocated with %pK\n", + __func__, master->dev); + ret = -EALREADY; + goto exit; + } + mutex_unlock(&snd_event_mutex); + + if (!clients || IS_ERR(clients)) { + dev_err(dev, "%s: Invalid clients ptr\n", __func__); + return -EINVAL; + } + + new_master = kzalloc(sizeof(*new_master), GFP_KERNEL); + if (!new_master) + return -ENOMEM; + + new_master->dev = dev; + new_master->ops = ops; + new_master->data = data; + new_master->clients = clients; + + dev_dbg(dev, "adding master to SND event FW (ops %pK)\n", ops); + + mutex_lock(&snd_event_mutex); + + master = new_master; + + ret = snd_event_find_clients(master); + if (ret) { + dev_dbg(dev, "%s: Failed to find all clients\n", __func__); + ret = 0; + goto exit; + } + master->clients_found = true; + +exit: + mutex_unlock(&snd_event_mutex); + return ret; +} +EXPORT_SYMBOL(snd_event_master_register); + +/* + * snd_event_master_deregister - Remove a master from the SND event FW + * + * @dev: Pointer to the "struct device" associated with the master + * + * Returns 0 on success or error on failure. + */ +int snd_event_master_deregister(struct device *dev) +{ + int ret = 0; + + if (!dev) { + pr_err("%s: dev is NULL\n", __func__); + return -EINVAL; + } + + mutex_lock(&snd_event_mutex); + if (!master) { + dev_dbg(dev, "%s: No master found\n", __func__); + ret = -ENODEV; + goto exit; + } + + if (master->dev != dev) { + dev_dbg(dev, "%s: device is not a Master\n", __func__); + ret = -ENXIO; + goto exit; + } + + master->state = false; + + if (master && master->clients_found) + ret = check_and_update_fwk_state(); + + kfree(master->clients->cl_arr); + kfree(master->clients); + kfree(master); + master = NULL; +exit: + mutex_unlock(&snd_event_mutex); + return ret; +} +EXPORT_SYMBOL(snd_event_master_deregister); + +/* + * snd_event_notify - Update the state of the Master/client in the SND event FW + * + * @dev: Pointer to the "struct device" associated with the master/client + * @state: UP/DOWN state of the caller (master/client) + * + * Returns 0 on success or error on failure. + */ +int snd_event_notify(struct device *dev, unsigned int state) +{ + struct snd_event_client *c; + int ret = 0; + + if (!dev) { + pr_err("%s: dev is NULL\n", __func__); + return -EINVAL; + } + + mutex_lock(&snd_event_mutex); + if (list_empty(&snd_event_client_list) && !master) { + dev_err(dev, "%s: No device registered\n", __func__); + ret = -ENODEV; + goto exit; + } + + c = find_snd_event_client(dev); + if (!c && (!master || (master->dev != dev))) { + dev_err(dev, "%s: No snd dev entry found\n", __func__); + ret = -ENXIO; + goto exit; + } + + if (c) + c->state = !!state; + else + master->state = !!state; + + if (master && master->clients_found) + ret = check_and_update_fwk_state(); + +exit: + mutex_unlock(&snd_event_mutex); + return ret; +} +EXPORT_SYMBOL(snd_event_notify); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("SND event module"); diff --git a/audio-kernel/soc/soundwire.c b/audio-kernel/soc/soundwire.c new file mode 100644 index 000000000..c7a797887 --- /dev/null +++ b/audio-kernel/soc/soundwire.c @@ -0,0 +1,1126 @@ +/* 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 + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct boardinfo { + struct list_head list; + struct swr_boardinfo board_info; +}; + +static LIST_HEAD(board_list); +static LIST_HEAD(swr_master_list); +static DEFINE_MUTEX(board_lock); +static DEFINE_IDR(master_idr); +static DEFINE_MUTEX(swr_lock); + +static struct device_type swr_dev_type; + +#define SOUNDWIRE_NAME_SIZE 32 + +static void swr_master_put(struct swr_master *master) +{ + if (master) + put_device(&master->dev); +} + +static struct swr_master *swr_master_get(struct swr_master *master) +{ + if (!master || !get_device(&master->dev)) + return NULL; + return master; +} + +static void swr_dev_release(struct device *dev) +{ + struct swr_device *swr_dev = to_swr_device(dev); + struct swr_master *master; + + if (!swr_dev) + return; + master = swr_dev->master; + if (!master) + return; + mutex_lock(&master->mlock); + list_del_init(&swr_dev->dev_list); + mutex_unlock(&master->mlock); + swr_master_put(swr_dev->master); + kfree(swr_dev); +} + +/** + * swr_remove_device - remove a soundwire device + * @swr_dev: soundwire device to remove + * + * Remove a soundwire device. Go through the soundwire + * device list that master has and remove swr_dev from + * it. + */ +void swr_remove_device(struct swr_device *swr_dev) +{ + struct swr_device *swr_dev_loop, *safe; + + /* + * master still has reference to all nodes and deletes + * at platform_unregister, so need to init the deleted + * entry + */ + list_for_each_entry_safe(swr_dev_loop, safe, + &swr_dev->master->devices, + dev_list) { + if (swr_dev == swr_dev_loop) + list_del_init(&swr_dev_loop->dev_list); + } +} +EXPORT_SYMBOL(swr_remove_device); + +/** + * swr_new_device - instantiate a new soundwire device + * @master: Controller to which device is connected + * @info: Describes the soundwire device + * Context: can sleep + * + * Create a soundwire device. Binding is handled through driver model + * probe/remove methods. A driver may be bound to this device when + * the function gets returned. + * + * Returns a soundwire new device or NULL + */ +struct swr_device *swr_new_device(struct swr_master *master, + struct swr_boardinfo const *info) +{ + int result; + struct swr_device *swr; + + if (!master || !swr_master_get(master)) { + pr_err("%s: master is NULL\n", __func__); + return NULL; + } + + swr = kzalloc(sizeof(*swr), GFP_KERNEL); + if (!swr) { + put_device(&master->dev); + return NULL; + } + swr->master = master; + swr->addr = info->addr; + strlcpy(swr->name, info->name, sizeof(swr->name)); + swr->dev.type = &swr_dev_type; + swr->dev.parent = &master->dev; + swr->dev.bus = &soundwire_type; + swr->dev.release = swr_dev_release; + swr->dev.of_node = info->of_node; + mutex_lock(&master->mlock); + list_add_tail(&swr->dev_list, &master->devices); + mutex_unlock(&master->mlock); + + dev_set_name(&swr->dev, "%s.%lx", swr->name, swr->addr); + result = device_register(&swr->dev); + if (result) { + dev_err(&master->dev, "device [%s] register failed err %d\n", + swr->name, result); + goto err_out; + } + dev_dbg(&master->dev, "Device [%s] registered with bus id %s\n", + swr->name, dev_name(&swr->dev)); + return swr; + +err_out: + dev_dbg(&master->dev, "Failed to register swr device %s at 0x%lx %d\n", + swr->name, swr->addr, result); + swr_master_put(master); + kfree(swr); + return NULL; +} +EXPORT_SYMBOL(swr_new_device); + +/** + * of_register_swr_devices - register child devices on to the soundwire bus + * @master: pointer to soundwire master device + * + * Registers a soundwire device for each child node of master node which has + * a "swr-devid" property + * + */ +int of_register_swr_devices(struct swr_master *master) +{ + struct swr_device *swr; + struct device_node *node; + + if (!master->dev.of_node) + return -EINVAL; + + for_each_available_child_of_node(master->dev.of_node, node) { + struct swr_boardinfo info = {}; + u64 addr; + + dev_dbg(&master->dev, "of_swr:register %s\n", node->full_name); + + if (of_modalias_node(node, info.name, sizeof(info.name)) < 0) { + dev_err(&master->dev, "of_swr:modalias failure %s\n", + node->full_name); + continue; + } + if (of_property_read_u64(node, "reg", &addr)) { + dev_err(&master->dev, "of_swr:invalid reg %s\n", + node->full_name); + continue; + } + info.addr = addr; + info.of_node = of_node_get(node); + master->num_dev++; + swr = swr_new_device(master, &info); + if (!swr) { + dev_err(&master->dev, "of_swr: Register failed %s\n", + node->full_name); + of_node_put(node); + master->num_dev--; + continue; + } + } + return 0; +} +EXPORT_SYMBOL(of_register_swr_devices); + +/** + * swr_port_response - response from master to free the completed transaction + * @mstr: pointer to soundwire master device + * @tid: transaction id that indicates transaction to be freed. + * + * Master calls this function to free the compeleted transaction memory + */ +void swr_port_response(struct swr_master *mstr, u8 tid) +{ + struct swr_params *txn; + + txn = mstr->port_txn[tid]; + + if (txn == NULL) { + dev_err(&mstr->dev, "%s: transaction is already NULL\n", + __func__); + return; + } + mstr->port_txn[tid] = NULL; + kfree(txn); +} +EXPORT_SYMBOL(swr_port_response); + +/** + * swr_remove_from_group - remove soundwire slave devices from group + * @dev: pointer to the soundwire slave device + * dev_num: device number of the soundwire slave device + * + * Returns error code for failure and 0 for success + */ +int swr_remove_from_group(struct swr_device *dev, u8 dev_num) +{ + struct swr_master *master; + + if (!dev) + return -ENODEV; + + master = dev->master; + if (!master) + return -EINVAL; + + if (!dev->group_id) + return 0; + + if (master->gr_sid != dev_num) + return 0; + + if (master->remove_from_group && master->remove_from_group(master)) + dev_dbg(&master->dev, "%s: falling back to GROUP_NONE\n", + __func__); + + return 0; +} +EXPORT_SYMBOL(swr_remove_from_group); + +/** + * swr_slvdev_datapath_control - Enables/Disables soundwire slave device + * data path + * @dev: pointer to soundwire slave device + * @dev_num: device number of the soundwire slave device + * + * Returns error code for failure and 0 for success + */ +int swr_slvdev_datapath_control(struct swr_device *dev, u8 dev_num, + bool enable) +{ + struct swr_master *master; + int ret = 0; + + if (!dev) + return -ENODEV; + + master = dev->master; + if (!master) + return -EINVAL; + + if (dev->group_id) { + /* Broadcast */ + if (master->gr_sid != dev_num) { + if (!master->gr_sid) + master->gr_sid = dev_num; + else + return 0; + } + } + + if (master->slvdev_datapath_control) + ret = master->slvdev_datapath_control(master, enable); + + return ret; +} +EXPORT_SYMBOL(swr_slvdev_datapath_control); + +/** + * swr_connect_port - enable soundwire slave port(s) + * @dev: pointer to soundwire slave device + * @port_id: logical port id(s) of soundwire slave device + * @num_port: number of slave device ports need to be enabled + * @ch_mask: channels for each port that needs to be enabled + * @ch_rate: rate at which each port/channels operate + * @num_ch: number of channels for each port + * + * soundwire slave device call swr_connect_port API to enable all/some of + * its ports and corresponding channels and channel rate. This API will + * call master connect_port callback function to calculate frame structure + * and enable master and slave ports + */ +int swr_connect_port(struct swr_device *dev, u8 *port_id, u8 num_port, + u8 *ch_mask, u32 *ch_rate, u8 *num_ch, u8 *port_type) +{ + u8 i = 0; + int ret = 0; + struct swr_params *txn = NULL; + struct swr_params **temp_txn = NULL; + struct swr_master *master = dev->master; + + if (!master) { + pr_err("%s: Master is NULL\n", __func__); + return -EINVAL; + } + if (num_port > SWR_MAX_DEV_PORT_NUM) { + dev_err(&master->dev, "%s: num_port %d exceeds max port %d\n", + __func__, num_port, SWR_MAX_DEV_PORT_NUM); + return -EINVAL; + } + + /* + * create "txn" to accommodate ports enablement of + * different slave devices calling swr_connect_port at the + * same time. Once master process the txn data, it calls + * swr_port_response() to free the transaction. Maximum + * of 256 transactions can be allocated. + */ + txn = kzalloc(sizeof(struct swr_params), GFP_KERNEL); + if (!txn) + return -ENOMEM; + + mutex_lock(&master->mlock); + for (i = 0; i < master->last_tid; i++) { + if (master->port_txn[i] == NULL) + break; + } + if (i >= master->last_tid) { + if (master->last_tid == 255) { + mutex_unlock(&master->mlock); + kfree(txn); + dev_err(&master->dev, "%s Max tid reached\n", + __func__); + return -ENOMEM; + } + temp_txn = krealloc(master->port_txn, + (i + 1) * sizeof(struct swr_params *), + GFP_KERNEL); + if (!temp_txn) { + mutex_unlock(&master->mlock); + kfree(txn); + dev_err(&master->dev, "%s Not able to allocate\n" + "master port transaction memory\n", + __func__); + return -ENOMEM; + } + master->port_txn = temp_txn; + master->last_tid++; + } + master->port_txn[i] = txn; + mutex_unlock(&master->mlock); + txn->tid = i; + + txn->dev_num = dev->dev_num; + txn->num_port = num_port; + for (i = 0; i < num_port; i++) { + txn->port_id[i] = port_id[i]; + txn->num_ch[i] = num_ch[i]; + txn->ch_rate[i] = ch_rate[i]; + txn->ch_en[i] = ch_mask[i]; + txn->port_type[i] = port_type[i]; + } + ret = master->connect_port(master, txn); + return ret; +} +EXPORT_SYMBOL(swr_connect_port); + +/** + * swr_disconnect_port - disable soundwire slave port(s) + * @dev: pointer to soundwire slave device + * @port_id: logical port id(s) of soundwire slave device + * @num_port: number of slave device ports need to be disabled + * + * soundwire slave device call swr_disconnect_port API to disable all/some of + * its ports. This API will call master disconnect_port callback function to + * disable master and slave port and (re)configure frame structure + */ +int swr_disconnect_port(struct swr_device *dev, u8 *port_id, u8 num_port, + u8 *ch_mask, u8 *port_type) +{ + u8 i = 0; + int ret; + struct swr_params *txn = NULL; + struct swr_params **temp_txn = NULL; + struct swr_master *master = dev->master; + + if (!master) { + pr_err("%s: Master is NULL\n", __func__); + return -EINVAL; + } + + if (num_port > SWR_MAX_DEV_PORT_NUM) { + dev_err(&master->dev, "%s: num_port %d exceeds max port %d\n", + __func__, num_port, SWR_MAX_DEV_PORT_NUM); + return -EINVAL; + } + + txn = kzalloc(sizeof(struct swr_params), GFP_KERNEL); + if (!txn) + return -ENOMEM; + + mutex_lock(&master->mlock); + for (i = 0; i < master->last_tid; i++) { + if (master->port_txn[i] == NULL) + break; + } + if (i >= master->last_tid) { + if (master->last_tid == 255) { + mutex_unlock(&master->mlock); + kfree(txn); + dev_err(&master->dev, "%s Max tid reached\n", + __func__); + return -ENOMEM; + } + temp_txn = krealloc(master->port_txn, + (i + 1) * sizeof(struct swr_params *), + GFP_KERNEL); + if (!temp_txn) { + mutex_unlock(&master->mlock); + kfree(txn); + dev_err(&master->dev, "%s Not able to allocate\n" + "master port transaction memory\n", + __func__); + return -ENOMEM; + } + master->port_txn = temp_txn; + master->last_tid++; + } + master->port_txn[i] = txn; + mutex_unlock(&master->mlock); + txn->tid = i; + + txn->dev_num = dev->dev_num; + txn->num_port = num_port; + for (i = 0; i < num_port; i++) { + txn->port_id[i] = port_id[i]; + txn->ch_en[i] = ch_mask[i]; + txn->port_type[i] = port_type[i]; + } + ret = master->disconnect_port(master, txn); + return ret; +} +EXPORT_SYMBOL(swr_disconnect_port); + +/** + * swr_get_logical_dev_num - Get soundwire slave logical device number + * @dev: pointer to soundwire slave device + * @dev_id: physical device id of soundwire slave device + * @dev_num: pointer to logical device num of soundwire slave device + * + * This API will get the logical device number of soundwire slave device + */ +int swr_get_logical_dev_num(struct swr_device *dev, u64 dev_id, + u8 *dev_num) +{ + int ret = 0; + struct swr_master *master = dev->master; + + if (!master) { + pr_err("%s: Master is NULL\n", __func__); + return -EINVAL; + } + mutex_lock(&master->mlock); + ret = master->get_logical_dev_num(master, dev_id, dev_num); + if (ret) { + pr_err("%s: Error %d to get logical addr for device %llx\n", + __func__, ret, dev_id); + } + mutex_unlock(&master->mlock); + return ret; +} +EXPORT_SYMBOL(swr_get_logical_dev_num); + +/** + * swr_device_wakeup_vote - Wakeup master and slave devices from clock stop + * @dev: pointer to soundwire slave device + * + * This API will wake up soundwire master and slave device(s) from + * clock stop. + */ +int swr_device_wakeup_vote(struct swr_device *dev) +{ + int ret = 0; + struct swr_master *master = dev->master; + + if (!master) { + pr_err("%s: Master is NULL\n", __func__); + return -EINVAL; + } + mutex_lock(&master->mlock); + if (master->device_wakeup_vote) + master->device_wakeup_vote(master); + else + ret = -EINVAL; + mutex_unlock(&master->mlock); + + return ret; +} +EXPORT_SYMBOL(swr_device_wakeup_vote); + +/** + * swr_device_wakeup_unvote - Unvote Wakeup so that master and slave + * devices can go back to clock stop + * @dev: pointer to soundwire slave device + * + * This API will remove vote for wakeup so that soundwire master and + * slave device(s) can go back to clock stop. + */ +int swr_device_wakeup_unvote(struct swr_device *dev) +{ + int ret = 0; + struct swr_master *master = dev->master; + + if (!master) { + pr_err("%s: Master is NULL\n", __func__); + return -EINVAL; + } + mutex_lock(&master->mlock); + if (master->device_wakeup_unvote) + master->device_wakeup_unvote(master); + else + ret = -EINVAL; + mutex_unlock(&master->mlock); + + return ret; +} +EXPORT_SYMBOL(swr_device_wakeup_unvote); + +/** + * swr_read - read soundwire slave device registers + * @dev: pointer to soundwire slave device + * @dev_num: logical device num of soundwire slave device + * @reg_addr: base register address that needs to be read + * @buf: pointer to store the values of registers from base address + * @len: length of the buffer + * + * This API will read the value of the register address from + * soundwire slave device + */ +int swr_read(struct swr_device *dev, u8 dev_num, u16 reg_addr, + void *buf, u32 len) +{ + struct swr_master *master = dev->master; + + if (!master) + return -EINVAL; + return master->read(master, dev_num, reg_addr, buf, len); +} +EXPORT_SYMBOL(swr_read); + +/** + * swr_bulk_write - write soundwire slave device registers + * @dev: pointer to soundwire slave device + * @dev_num: logical device num of soundwire slave device + * @reg_addr: register address of soundwire slave device + * @buf: contains value of register address + * @len: indicates number of registers + * + * This API will write the value of the register address to + * soundwire slave device + */ +int swr_bulk_write(struct swr_device *dev, u8 dev_num, void *reg, + const void *buf, size_t len) +{ + struct swr_master *master; + + if (!dev || !dev->master) + return -EINVAL; + + master = dev->master; + if (dev->group_id) { + if (master->gr_sid != dev_num) { + if (!master->gr_sid) + master->gr_sid = dev_num; + else + return 0; + } + dev_num = dev->group_id; + } + if (master->bulk_write) + return master->bulk_write(master, dev_num, reg, buf, len); + + return -EOPNOTSUPP; +} +EXPORT_SYMBOL(swr_bulk_write); + +/** + * swr_write - write soundwire slave device registers + * @dev: pointer to soundwire slave device + * @dev_num: logical device num of soundwire slave device + * @reg_addr: register address of soundwire slave device + * @buf: contains value of register address + * + * This API will write the value of the register address to + * soundwire slave device + */ +int swr_write(struct swr_device *dev, u8 dev_num, u16 reg_addr, + const void *buf) +{ + struct swr_master *master = dev->master; + + if (!master) + return -EINVAL; + + if (dev->group_id) { + if (master->gr_sid != dev_num) { + if (!master->gr_sid) + master->gr_sid = dev_num; + else + return 0; + } + dev_num = dev->group_id; + } + return master->write(master, dev_num, reg_addr, buf); +} +EXPORT_SYMBOL(swr_write); + +/** + * swr_device_up - Function to bringup the soundwire slave device + * @swr_dev: pointer to soundwire slave device + * Context: can sleep + * + * This API will be called by soundwire master to bringup the slave + * device. + */ +int swr_device_up(struct swr_device *swr_dev) +{ + struct device *dev; + const struct swr_driver *sdrv; + + if (!swr_dev) + return -EINVAL; + + dev = &swr_dev->dev; + sdrv = to_swr_driver(dev->driver); + if (!sdrv) + return 0; + + if (sdrv->device_up) + return sdrv->device_up(to_swr_device(dev)); + + return -ENODEV; +} +EXPORT_SYMBOL(swr_device_up); + +/** + * swr_device_down - Function to call soundwire slave device down + * @swr_dev: pointer to soundwire slave device + * Context: can sleep + * + * This API will be called by soundwire master to put slave device in + * shutdown state. + */ +int swr_device_down(struct swr_device *swr_dev) +{ + struct device *dev; + const struct swr_driver *sdrv; + + if (!swr_dev) + return -EINVAL; + + dev = &swr_dev->dev; + sdrv = to_swr_driver(dev->driver); + if (!sdrv) + return 0; + + if (sdrv->device_down) + return sdrv->device_down(to_swr_device(dev)); + + return -ENODEV; +} +EXPORT_SYMBOL(swr_device_down); + +/** + * swr_reset_device - reset soundwire slave device + * @swr_dev: pointer to soundwire slave device + * Context: can sleep + * + * This API will be called by soundwire master to reset the slave + * device when the slave device is not responding or in undefined + * state + */ +int swr_reset_device(struct swr_device *swr_dev) +{ + struct device *dev; + const struct swr_driver *sdrv; + + if (!swr_dev) + return -EINVAL; + + dev = &swr_dev->dev; + sdrv = to_swr_driver(dev->driver); + if (!sdrv) + return -EINVAL; + + if (sdrv->reset_device) + return sdrv->reset_device(to_swr_device(dev)); + + return -ENODEV; +} +EXPORT_SYMBOL(swr_reset_device); + +/** + * swr_set_device_group - Assign group id to the slave devices + * @swr_dev: pointer to soundwire slave device + * @id: group id to be assigned to slave device + * Context: can sleep + * + * This API will be called either from soundwire master or slave + * device to assign group id. + */ +int swr_set_device_group(struct swr_device *swr_dev, u8 id) +{ + struct swr_master *master; + + if (!swr_dev) + return -EINVAL; + + swr_dev->group_id = id; + master = swr_dev->master; + if (!id && master) + master->gr_sid = 0; + + return 0; +} +EXPORT_SYMBOL(swr_set_device_group); + +static int swr_drv_probe(struct device *dev) +{ + const struct swr_driver *sdrv = to_swr_driver(dev->driver); + + if (!sdrv) + return -EINVAL; + + if (sdrv->probe) + return sdrv->probe(to_swr_device(dev)); + return -ENODEV; +} + +static int swr_drv_remove(struct device *dev) +{ + const struct swr_driver *sdrv = to_swr_driver(dev->driver); + + if (!sdrv) + return -EINVAL; + + if (sdrv->remove) + return sdrv->remove(to_swr_device(dev)); + return -ENODEV; +} + +static void swr_drv_shutdown(struct device *dev) +{ + const struct swr_driver *sdrv = to_swr_driver(dev->driver); + + if (!sdrv) + return; + + if (sdrv->shutdown) + sdrv->shutdown(to_swr_device(dev)); +} + +/** + * swr_driver_register - register a soundwire driver + * @drv: the driver to register + * Context: can sleep + */ +int swr_driver_register(struct swr_driver *drv) +{ + drv->driver.bus = &soundwire_type; + if (drv->probe) + drv->driver.probe = swr_drv_probe; + if (drv->remove) + drv->driver.remove = swr_drv_remove; + + if (drv->shutdown) + drv->driver.shutdown = swr_drv_shutdown; + + return driver_register(&drv->driver); +} +EXPORT_SYMBOL(swr_driver_register); + +/** + * swr_driver_unregister - unregister a soundwire driver + * @drv: the driver to unregister + */ +void swr_driver_unregister(struct swr_driver *drv) +{ + if (drv) + driver_unregister(&drv->driver); +} +EXPORT_SYMBOL(swr_driver_unregister); + +static void swr_match_ctrl_to_boardinfo(struct swr_master *master, + struct swr_boardinfo *bi) +{ + struct swr_device *swr; + + if (master->bus_num != bi->bus_num) { + dev_dbg(&master->dev, + "%s: master# %d and bi# %d does not match\n", + __func__, master->bus_num, bi->bus_num); + return; + } + + swr = swr_new_device(master, bi); + if (!swr) + dev_err(&master->dev, "can't create new device for %s\n", + bi->swr_slave->name); +} + +/** + * swr_master_add_boarddevices - Add devices registered by board info + * @master: master to which these devices are to be added to. + * + * This API is called by master when it is up and running. If devices + * on a master were registered before master, this will make sure that + * they get probed when master is up. + */ +void swr_master_add_boarddevices(struct swr_master *master) +{ + struct boardinfo *bi; + + mutex_lock(&board_lock); + list_add_tail(&master->list, &swr_master_list); + list_for_each_entry(bi, &board_list, list) + swr_match_ctrl_to_boardinfo(master, &bi->board_info); + mutex_unlock(&board_lock); +} +EXPORT_SYMBOL(swr_master_add_boarddevices); + +struct swr_device *get_matching_swr_slave_device(struct device_node *np) +{ + struct swr_device *swr = NULL; + struct swr_master *master; + bool found = false; + + mutex_lock(&board_lock); + list_for_each_entry(master, &swr_master_list, list) { + mutex_lock(&master->mlock); + list_for_each_entry(swr, &master->devices, dev_list) { + if (swr->dev.of_node == np) { + found = true; + mutex_unlock(&master->mlock); + goto exit; + } + } + mutex_unlock(&master->mlock); + } +exit: + mutex_unlock(&board_lock); + if (!found) + return NULL; + return swr; +} +EXPORT_SYMBOL(get_matching_swr_slave_device); + +static void swr_unregister_device(struct swr_device *swr) +{ + if (swr) + device_unregister(&swr->dev); +} + +static void swr_master_release(struct device *dev) +{ + /* kfree of master done at swrm_remove of device */ +} + +#define swr_master_attr_gr NULL +static struct device_type swr_master_type = { + .groups = swr_master_attr_gr, + .release = swr_master_release, +}; + +static int __unregister(struct device *dev, void *null) +{ + swr_unregister_device(to_swr_device(dev)); + return 0; +} + +/** + * swr_unregister_master - unregister soundwire master controller + * @master: the master being unregistered + * + * This API is called by master controller driver to unregister + * master controller that was registered by swr_register_master API. + */ +void swr_unregister_master(struct swr_master *master) +{ + int dummy; + struct swr_master *m_ctrl; + + mutex_lock(&swr_lock); + m_ctrl = idr_find(&master_idr, master->bus_num); + mutex_unlock(&swr_lock); + if (m_ctrl != master) + return; + + mutex_lock(&board_lock); + list_del(&master->list); + mutex_unlock(&board_lock); + + /* free bus id */ + mutex_lock(&swr_lock); + idr_remove(&master_idr, master->bus_num); + mutex_unlock(&swr_lock); + + dummy = device_for_each_child(&master->dev, NULL, __unregister); + device_unregister(&master->dev); +} +EXPORT_SYMBOL(swr_unregister_master); + +/** + * swr_register_master - register soundwire master controller + * @master: master to be registered + * + * This API will register master with the framework. master->bus_num + * is the desired number with which soundwire framework registers the + * master. + */ +int swr_register_master(struct swr_master *master) +{ + int id; + int status = 0; + + mutex_lock(&swr_lock); + id = of_alias_get_id(master->dev.of_node, "swr"); + + if (id >= 0) + master->bus_num = id; + id = idr_alloc(&master_idr, master, master->bus_num, + master->bus_num + 1, GFP_KERNEL); + mutex_unlock(&swr_lock); + if (id < 0) + return id; + + master->bus_num = id; + /* Can't register until driver model init */ + if (WARN_ON(!soundwire_type.p)) { + status = -EAGAIN; + goto done; + } + + dev_set_name(&master->dev, "swr%u", master->bus_num); + master->dev.bus = &soundwire_type; + master->dev.type = &swr_master_type; + mutex_init(&master->mlock); + status = device_register(&master->dev); + if (status < 0) + goto done; + + INIT_LIST_HEAD(&master->devices); + pr_debug("%s: SWR master registered successfully %s\n", + __func__, dev_name(&master->dev)); + return 0; + +done: + idr_remove(&master_idr, master->bus_num); + return status; +} +EXPORT_SYMBOL(swr_register_master); + +#define swr_device_attr_gr NULL +#define swr_device_uevent NULL +static struct device_type swr_dev_type = { + .groups = swr_device_attr_gr, + .uevent = swr_device_uevent, + .release = swr_dev_release, +}; + +static const struct swr_device_id *swr_match(const struct swr_device_id *id, + const struct swr_device *swr_dev) +{ + while (id->name[0]) { + if (strcmp(swr_dev->name, id->name) == 0) + return id; + id++; + } + return NULL; +} + +static int swr_device_match(struct device *dev, struct device_driver *driver) +{ + struct swr_device *swr_dev; + struct swr_driver *drv = to_swr_driver(driver); + + if (!drv) + return -EINVAL; + + if (dev->type == &swr_dev_type) + swr_dev = to_swr_device(dev); + else + return 0; + if (drv->id_table) + return swr_match(drv->id_table, swr_dev) != NULL; + + if (driver->name) + return strcmp(swr_dev->name, driver->name) == 0; + return 0; +} +#ifdef CONFIG_PM_SLEEP +static int swr_legacy_suspend(struct device *dev, pm_message_t mesg) +{ + struct swr_device *swr_dev = NULL; + struct swr_driver *driver; + + if (dev->type == &swr_dev_type) + swr_dev = to_swr_device(dev); + + if (!swr_dev || !dev->driver) + return 0; + + driver = to_swr_driver(dev->driver); + if (!driver->suspend) + return 0; + + return driver->suspend(swr_dev, mesg); +} + +static int swr_legacy_resume(struct device *dev) +{ + struct swr_device *swr_dev = NULL; + struct swr_driver *driver; + + if (dev->type == &swr_dev_type) + swr_dev = to_swr_device(dev); + + if (!swr_dev || !dev->driver) + return 0; + + driver = to_swr_driver(dev->driver); + if (!driver->resume) + return 0; + + return driver->resume(swr_dev); +} + +static int swr_pm_suspend(struct device *dev) +{ + const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; + + if (pm) + return pm_generic_suspend(dev); + else + return swr_legacy_suspend(dev, PMSG_SUSPEND); +} + +static int swr_pm_resume(struct device *dev) +{ + const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; + + if (pm) + return pm_generic_resume(dev); + else + return swr_legacy_resume(dev); +} +#else +#define swr_pm_suspend NULL +#define swr_pm_resume NULL +#endif /*CONFIG_PM_SLEEP*/ + +static const struct dev_pm_ops soundwire_pm = { + .suspend = swr_pm_suspend, + .resume = swr_pm_resume, + SET_RUNTIME_PM_OPS( + pm_generic_suspend, + pm_generic_resume, + NULL + ) +}; + +struct device soundwire_dev = { + .init_name = "soundwire", +}; + +struct bus_type soundwire_type = { + .name = "soundwire", + .match = swr_device_match, + .pm = &soundwire_pm, +}; +EXPORT_SYMBOL(soundwire_type); + +static void __exit soundwire_exit(void) +{ + device_unregister(&soundwire_dev); + bus_unregister(&soundwire_type); +} + +static int __init soundwire_init(void) +{ + int retval; + + retval = bus_register(&soundwire_type); + if (!retval) + retval = device_register(&soundwire_dev); + + if (retval) + bus_unregister(&soundwire_type); + + return retval; +} +module_init(soundwire_init); +module_exit(soundwire_exit); + + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Soundwire module"); +MODULE_ALIAS("platform:soundwire"); diff --git a/audio-kernel/soc/swr-mstr-ctrl.c b/audio-kernel/soc/swr-mstr-ctrl.c new file mode 100644 index 000000000..5efaa1c5d --- /dev/null +++ b/audio-kernel/soc/swr-mstr-ctrl.c @@ -0,0 +1,2635 @@ +/* Copyright (c) 2015-2019, 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "swrm_registers.h" +#include "swr-mstr-ctrl.h" +#include "swrm_port_config.h" + +#define SWRM_SYSTEM_RESUME_TIMEOUT_MS 700 +#define SWRM_SYS_SUSPEND_WAIT 1 +#define SWR_BROADCAST_CMD_ID 0x0F +#define SWR_AUTO_SUSPEND_DELAY 3 /* delay in sec */ +#define SWR_DEV_ID_MASK 0xFFFFFFFF +#define SWR_REG_VAL_PACK(data, dev, id, reg) \ + ((reg) | ((id) << 16) | ((dev) << 20) | ((data) << 24)) + +#define SWR_INVALID_PARAM 0xFF +#define SWR_HSTOP_MAX_VAL 0xF +#define SWR_HSTART_MIN_VAL 0x0 + +#define SWRM_INTERRUPT_STATUS_MASK 0x1FDFD +/* pm runtime auto suspend timer in msecs */ +static int auto_suspend_timer = SWR_AUTO_SUSPEND_DELAY * 1000; +module_param(auto_suspend_timer, int, 0664); +MODULE_PARM_DESC(auto_suspend_timer, "timer for auto suspend"); + +enum { + SWR_NOT_PRESENT, /* Device is detached/not present on the bus */ + SWR_ATTACHED_OK, /* Device is attached */ + SWR_ALERT, /* Device alters master for any interrupts */ + SWR_RESERVED, /* Reserved */ +}; + +enum { + MASTER_ID_WSA = 1, + MASTER_ID_RX, + MASTER_ID_TX +}; + +enum { + ENABLE_PENDING, + DISABLE_PENDING +}; +#define TRUE 1 +#define FALSE 0 + +#define SWRM_MAX_PORT_REG 120 +#define SWRM_MAX_INIT_REG 11 + +#define SWR_MSTR_MAX_REG_ADDR 0x1740 +#define SWR_MSTR_START_REG_ADDR 0x00 +#define SWR_MSTR_MAX_BUF_LEN 32 +#define BYTES_PER_LINE 12 +#define SWR_MSTR_RD_BUF_LEN 8 +#define SWR_MSTR_WR_BUF_LEN 32 + +#define MAX_FIFO_RD_FAIL_RETRY 3 + +static struct swr_mstr_ctrl *dbgswrm; +static struct dentry *debugfs_swrm_dent; +static struct dentry *debugfs_peek; +static struct dentry *debugfs_poke; +static struct dentry *debugfs_reg_dump; +static unsigned int read_data; + +static bool swrm_lock_sleep(struct swr_mstr_ctrl *swrm); +static void swrm_unlock_sleep(struct swr_mstr_ctrl *swrm); + +static bool swrm_is_msm_variant(int val) +{ + return (val == SWRM_VERSION_1_3); +} + +static int swrm_debug_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static int get_parameters(char *buf, u32 *param1, int num_of_par) +{ + char *token; + int base, cnt; + + token = strsep(&buf, " "); + for (cnt = 0; cnt < num_of_par; cnt++) { + if (token) { + if ((token[1] == 'x') || (token[1] == 'X')) + base = 16; + else + base = 10; + + if (kstrtou32(token, base, ¶m1[cnt]) != 0) + return -EINVAL; + + token = strsep(&buf, " "); + } else + return -EINVAL; + } + return 0; +} + +static ssize_t swrm_reg_show(char __user *ubuf, size_t count, + loff_t *ppos) +{ + int i, reg_val, len; + ssize_t total = 0; + char tmp_buf[SWR_MSTR_MAX_BUF_LEN]; + + if (!ubuf || !ppos) + return 0; + + for (i = (((int) *ppos / BYTES_PER_LINE) + SWR_MSTR_START_REG_ADDR); + i <= SWR_MSTR_MAX_REG_ADDR; i += 4) { + reg_val = dbgswrm->read(dbgswrm->handle, i); + len = snprintf(tmp_buf, 25, "0x%.3x: 0x%.2x\n", i, reg_val); + if ((total + len) >= count - 1) + break; + if (copy_to_user((ubuf + total), tmp_buf, len)) { + pr_err("%s: fail to copy reg dump\n", __func__); + total = -EFAULT; + goto copy_err; + } + *ppos += len; + total += len; + } + +copy_err: + return total; +} + +static ssize_t swrm_debug_read(struct file *file, char __user *ubuf, + size_t count, loff_t *ppos) +{ + char lbuf[SWR_MSTR_RD_BUF_LEN]; + char *access_str; + ssize_t ret_cnt; + + if (!count || !file || !ppos || !ubuf) + return -EINVAL; + + access_str = file->private_data; + if (*ppos < 0) + return -EINVAL; + + if (!strcmp(access_str, "swrm_peek")) { + snprintf(lbuf, sizeof(lbuf), "0x%x\n", read_data); + ret_cnt = simple_read_from_buffer(ubuf, count, ppos, lbuf, + strnlen(lbuf, 7)); + } else if (!strcmp(access_str, "swrm_reg_dump")) { + ret_cnt = swrm_reg_show(ubuf, count, ppos); + } else { + pr_err("%s: %s not permitted to read\n", __func__, access_str); + ret_cnt = -EPERM; + } + return ret_cnt; +} + +static ssize_t swrm_debug_write(struct file *filp, + const char __user *ubuf, size_t cnt, loff_t *ppos) +{ + char lbuf[SWR_MSTR_WR_BUF_LEN]; + int rc; + u32 param[5]; + char *access_str; + + if (!filp || !ppos || !ubuf) + return -EINVAL; + + access_str = filp->private_data; + if (cnt > sizeof(lbuf) - 1) + return -EINVAL; + + rc = copy_from_user(lbuf, ubuf, cnt); + if (rc) + return -EFAULT; + + lbuf[cnt] = '\0'; + if (!strcmp(access_str, "swrm_poke")) { + /* write */ + rc = get_parameters(lbuf, param, 2); + if ((param[0] <= SWR_MSTR_MAX_REG_ADDR) && + (param[1] <= 0xFFFFFFFF) && + (rc == 0)) + rc = dbgswrm->write(dbgswrm->handle, param[0], + param[1]); + else + rc = -EINVAL; + } else if (!strcmp(access_str, "swrm_peek")) { + /* read */ + rc = get_parameters(lbuf, param, 1); + if ((param[0] <= SWR_MSTR_MAX_REG_ADDR) && (rc == 0)) + read_data = dbgswrm->read(dbgswrm->handle, param[0]); + else + rc = -EINVAL; + } + if (rc == 0) + rc = cnt; + else + pr_err("%s: rc = %d\n", __func__, rc); + + return rc; +} + +static const struct file_operations swrm_debug_ops = { + .open = swrm_debug_open, + .write = swrm_debug_write, + .read = swrm_debug_read, +}; + +static int swrm_clk_request(struct swr_mstr_ctrl *swrm, bool enable) +{ + int ret = 0; + + if (!swrm->clk || !swrm->handle) + return -EINVAL; + + mutex_lock(&swrm->clklock); + if (enable) { + if (!swrm->dev_up) { + ret = -ENODEV; + goto exit; + } + swrm->clk_ref_count++; + if (swrm->clk_ref_count == 1) { + ret = swrm->clk(swrm->handle, true); + if (ret) { + dev_err_ratelimited(swrm->dev, + "%s: clock enable req failed", + __func__); + --swrm->clk_ref_count; + } + } + } else if (--swrm->clk_ref_count == 0) { + swrm->clk(swrm->handle, false); + complete(&swrm->clk_off_complete); + } + if (swrm->clk_ref_count < 0) { + pr_err("%s: swrm clk count mismatch\n", __func__); + swrm->clk_ref_count = 0; + } + +exit: + mutex_unlock(&swrm->clklock); + return ret; +} + +static int swrm_ahb_write(struct swr_mstr_ctrl *swrm, + u16 reg, u32 *value) +{ + u32 temp = (u32)(*value); + int ret = 0; + + mutex_lock(&swrm->devlock); + if (!swrm->dev_up) + goto err; + + ret = swrm_clk_request(swrm, TRUE); + if (ret) { + dev_err_ratelimited(swrm->dev, "%s: clock request failed\n", + __func__); + goto err; + } + iowrite32(temp, swrm->swrm_dig_base + reg); + swrm_clk_request(swrm, FALSE); +err: + mutex_unlock(&swrm->devlock); + return ret; +} + +static int swrm_ahb_read(struct swr_mstr_ctrl *swrm, + u16 reg, u32 *value) +{ + u32 temp = 0; + int ret = 0; + + mutex_lock(&swrm->devlock); + if (!swrm->dev_up) + goto err; + + ret = swrm_clk_request(swrm, TRUE); + if (ret) { + dev_err_ratelimited(swrm->dev, "%s: clock request failed\n", + __func__); + goto err; + } + temp = ioread32(swrm->swrm_dig_base + reg); + *value = temp; + swrm_clk_request(swrm, FALSE); +err: + mutex_unlock(&swrm->devlock); + return ret; +} + +static u32 swr_master_read(struct swr_mstr_ctrl *swrm, unsigned int reg_addr) +{ + u32 val = 0; + + if (swrm->read) + val = swrm->read(swrm->handle, reg_addr); + else + swrm_ahb_read(swrm, reg_addr, &val); + return val; +} + +static void swr_master_write(struct swr_mstr_ctrl *swrm, u16 reg_addr, u32 val) +{ + if (swrm->write) + swrm->write(swrm->handle, reg_addr, val); + else + swrm_ahb_write(swrm, reg_addr, &val); +} + +static int swr_master_bulk_write(struct swr_mstr_ctrl *swrm, u32 *reg_addr, + u32 *val, unsigned int length) +{ + int i = 0; + + if (swrm->bulk_write) + swrm->bulk_write(swrm->handle, reg_addr, val, length); + else { + mutex_lock(&swrm->iolock); + for (i = 0; i < length; i++) { + /* wait for FIFO WR command to complete to avoid overflow */ + usleep_range(100, 105); + swr_master_write(swrm, reg_addr[i], val[i]); + } + mutex_unlock(&swrm->iolock); + } + return 0; +} + +static bool swrm_is_port_en(struct swr_master *mstr) +{ + return !!(mstr->num_port); +} + +static void copy_port_tables(struct swr_mstr_ctrl *swrm, + struct port_params *params) +{ + u8 i; + struct port_params *config = params; + + for (i = 0; i < SWR_MSTR_PORT_LEN; i++) { + /* wsa uses single frame structure for all configurations */ + if (!swrm->mport_cfg[i].port_en) + continue; + swrm->mport_cfg[i].sinterval = config[i].si; + swrm->mport_cfg[i].offset1 = config[i].off1; + swrm->mport_cfg[i].offset2 = config[i].off2; + swrm->mport_cfg[i].hstart = config[i].hstart; + swrm->mport_cfg[i].hstop = config[i].hstop; + swrm->mport_cfg[i].blk_pack_mode = config[i].bp_mode; + swrm->mport_cfg[i].blk_grp_count = config[i].bgp_ctrl; + swrm->mport_cfg[i].word_length = config[i].wd_len; + swrm->mport_cfg[i].lane_ctrl = config[i].lane_ctrl; + } +} +static int swrm_get_port_config(struct swr_mstr_ctrl *swrm) +{ + struct port_params *params; + + switch (swrm->master_id) { + case MASTER_ID_WSA: + params = wsa_frame_superset; + break; + case MASTER_ID_RX: + /* Two RX tables for dsd and without dsd enabled */ + if (swrm->mport_cfg[4].port_en) + params = rx_frame_params_dsd; + else + params = rx_frame_params; + break; + case MASTER_ID_TX: + if ((swrm->mport_cfg[0].port_en && + swrm->mport_cfg[0].ch_rate == swrm->mclk_freq) || + (swrm->mport_cfg[1].port_en && + swrm->mport_cfg[1].ch_rate == swrm->mclk_freq)) + params = tx_perf_frame_params_superset; + else + params = tx_frame_params_superset; + break; + default: /* MASTER_GENERIC*/ + /* computer generic frame parameters */ + return -EINVAL; + } + + copy_port_tables(swrm, params); + return 0; +} + +static int swrm_get_master_port(struct swr_mstr_ctrl *swrm, u8 *mstr_port_id, + u8 *mstr_ch_mask, u8 mstr_prt_type, + u8 slv_port_id) +{ + int i, j; + *mstr_port_id = 0; + + for (i = 1; i <= swrm->num_ports; i++) { + for (j = 0; j < SWR_MAX_CH_PER_PORT; j++) { + if (swrm->port_mapping[i][j].port_type == mstr_prt_type) + goto found; + } + } +found: + if (i > swrm->num_ports || j == SWR_MAX_CH_PER_PORT) { + dev_err(swrm->dev, "%s: port type not supported by master\n", + __func__); + return -EINVAL; + } + /* id 0 corresponds to master port 1 */ + *mstr_port_id = i - 1; + *mstr_ch_mask = swrm->port_mapping[i][j].ch_mask; + + return 0; + +} + +static u32 swrm_get_packed_reg_val(u8 *cmd_id, u8 cmd_data, + u8 dev_addr, u16 reg_addr) +{ + u32 val; + u8 id = *cmd_id; + + if (id != SWR_BROADCAST_CMD_ID) { + if (id < 14) + id += 1; + else + id = 0; + *cmd_id = id; + } + val = SWR_REG_VAL_PACK(cmd_data, dev_addr, id, reg_addr); + + return val; +} + +static int swrm_cmd_fifo_rd_cmd(struct swr_mstr_ctrl *swrm, int *cmd_data, + u8 dev_addr, u8 cmd_id, u16 reg_addr, + u32 len) +{ + u32 val; + u32 retry_attempt = 0; + + mutex_lock(&swrm->iolock); + val = swrm_get_packed_reg_val(&swrm->rcmd_id, len, dev_addr, reg_addr); + if (swrm->read) { + /* skip delay if read is handled in platform driver */ + swr_master_write(swrm, SWRM_CMD_FIFO_RD_CMD, val); + } else { + /* wait for FIFO RD to complete to avoid overflow */ + usleep_range(100, 105); + swr_master_write(swrm, SWRM_CMD_FIFO_RD_CMD, val); + /* wait for FIFO RD CMD complete to avoid overflow */ + usleep_range(250, 255); + } +retry_read: + *cmd_data = swr_master_read(swrm, SWRM_CMD_FIFO_RD_FIFO_ADDR); + dev_dbg(swrm->dev, "%s: reg: 0x%x, cmd_id: 0x%x, rcmd_id: 0x%x, \ + dev_num: 0x%x, cmd_data: 0x%x\n", __func__, reg_addr, + cmd_id, swrm->rcmd_id, dev_addr, *cmd_data); + if ((((*cmd_data) & 0xF00) >> 8) != swrm->rcmd_id) { + if (retry_attempt < MAX_FIFO_RD_FAIL_RETRY) { + /* wait 500 us before retry on fifo read failure */ + usleep_range(500, 505); + retry_attempt++; + goto retry_read; + } else { + dev_err_ratelimited(swrm->dev, "%s: reg: 0x%x, cmd_id: 0x%x, \ + rcmd_id: 0x%x, dev_num: 0x%x, cmd_data: 0x%x\n", + __func__, reg_addr, cmd_id, swrm->rcmd_id, + dev_addr, *cmd_data); + + dev_err_ratelimited(swrm->dev, + "%s: failed to read fifo\n", __func__); + } + } + mutex_unlock(&swrm->iolock); + + return 0; +} + +static int swrm_cmd_fifo_wr_cmd(struct swr_mstr_ctrl *swrm, u8 cmd_data, + u8 dev_addr, u8 cmd_id, u16 reg_addr) +{ + u32 val; + int ret = 0; + + mutex_lock(&swrm->iolock); + if (!cmd_id) + val = swrm_get_packed_reg_val(&swrm->wcmd_id, cmd_data, + dev_addr, reg_addr); + else + val = swrm_get_packed_reg_val(&cmd_id, cmd_data, + dev_addr, reg_addr); + dev_dbg(swrm->dev, "%s: reg: 0x%x, cmd_id: 0x%x,wcmd_id: 0x%x, \ + dev_num: 0x%x, cmd_data: 0x%x\n", __func__, + reg_addr, cmd_id, swrm->wcmd_id,dev_addr, cmd_data); + swr_master_write(swrm, SWRM_CMD_FIFO_WR_CMD, val); + /* + * wait for FIFO WR command to complete to avoid overflow + * skip delay if write is handled in platform driver. + */ + if(!swrm->write) + usleep_range(250, 255); + if (cmd_id == 0xF) { + /* + * sleep for 10ms for MSM soundwire variant to allow broadcast + * command to complete. + */ + if (swrm_is_msm_variant(swrm->version)) + usleep_range(10000, 10100); + else + wait_for_completion_timeout(&swrm->broadcast, + (2 * HZ/10)); + } + mutex_unlock(&swrm->iolock); + return ret; +} + +static int swrm_read(struct swr_master *master, u8 dev_num, u16 reg_addr, + void *buf, u32 len) +{ + struct swr_mstr_ctrl *swrm = swr_get_ctrl_data(master); + int ret = 0; + int val; + u8 *reg_val = (u8 *)buf; + + if (!swrm) { + dev_err(&master->dev, "%s: swrm is NULL\n", __func__); + return -EINVAL; + } + if (!dev_num) { + dev_err(&master->dev, "%s: invalid slave dev num\n", __func__); + return -EINVAL; + } + mutex_lock(&swrm->devlock); + if (!swrm->dev_up) { + mutex_unlock(&swrm->devlock); + return 0; + } + mutex_unlock(&swrm->devlock); + + pm_runtime_get_sync(swrm->dev); + ret = swrm_cmd_fifo_rd_cmd(swrm, &val, dev_num, 0, reg_addr, len); + + if (!ret) + *reg_val = (u8)val; + + pm_runtime_put_autosuspend(swrm->dev); + pm_runtime_mark_last_busy(swrm->dev); + return ret; +} + +static int swrm_write(struct swr_master *master, u8 dev_num, u16 reg_addr, + const void *buf) +{ + struct swr_mstr_ctrl *swrm = swr_get_ctrl_data(master); + int ret = 0; + u8 reg_val = *(u8 *)buf; + + if (!swrm) { + dev_err(&master->dev, "%s: swrm is NULL\n", __func__); + return -EINVAL; + } + if (!dev_num) { + dev_err(&master->dev, "%s: invalid slave dev num\n", __func__); + return -EINVAL; + } + mutex_lock(&swrm->devlock); + if (!swrm->dev_up) { + mutex_unlock(&swrm->devlock); + return 0; + } + mutex_unlock(&swrm->devlock); + + pm_runtime_get_sync(swrm->dev); + ret = swrm_cmd_fifo_wr_cmd(swrm, reg_val, dev_num, 0, reg_addr); + + pm_runtime_put_autosuspend(swrm->dev); + pm_runtime_mark_last_busy(swrm->dev); + return ret; +} + +static int swrm_bulk_write(struct swr_master *master, u8 dev_num, void *reg, + const void *buf, size_t len) +{ + struct swr_mstr_ctrl *swrm = swr_get_ctrl_data(master); + int ret = 0; + int i; + u32 *val; + u32 *swr_fifo_reg; + + if (!swrm || !swrm->handle) { + dev_err(&master->dev, "%s: swrm is NULL\n", __func__); + return -EINVAL; + } + if (len <= 0) + return -EINVAL; + mutex_lock(&swrm->devlock); + if (!swrm->dev_up) { + mutex_unlock(&swrm->devlock); + return 0; + } + mutex_unlock(&swrm->devlock); + + pm_runtime_get_sync(swrm->dev); + if (dev_num) { + swr_fifo_reg = kcalloc(len, sizeof(u32), GFP_KERNEL); + if (!swr_fifo_reg) { + ret = -ENOMEM; + goto err; + } + val = kcalloc(len, sizeof(u32), GFP_KERNEL); + if (!val) { + ret = -ENOMEM; + goto mem_fail; + } + + for (i = 0; i < len; i++) { + val[i] = swrm_get_packed_reg_val(&swrm->wcmd_id, + ((u8 *)buf)[i], + dev_num, + ((u16 *)reg)[i]); + swr_fifo_reg[i] = SWRM_CMD_FIFO_WR_CMD; + } + ret = swr_master_bulk_write(swrm, swr_fifo_reg, val, len); + if (ret) { + dev_err(&master->dev, "%s: bulk write failed\n", + __func__); + ret = -EINVAL; + } + } else { + dev_err(&master->dev, + "%s: No support of Bulk write for master regs\n", + __func__); + ret = -EINVAL; + goto err; + } + kfree(val); +mem_fail: + kfree(swr_fifo_reg); +err: + pm_runtime_put_autosuspend(swrm->dev); + pm_runtime_mark_last_busy(swrm->dev); + return ret; +} + +static u8 get_inactive_bank_num(struct swr_mstr_ctrl *swrm) +{ + return (swr_master_read(swrm, SWRM_MCP_STATUS) & + SWRM_MCP_STATUS_BANK_NUM_MASK) ? 0 : 1; +} + +static void enable_bank_switch(struct swr_mstr_ctrl *swrm, u8 bank, + u8 row, u8 col) +{ + swrm_cmd_fifo_wr_cmd(swrm, ((row << 3) | col), 0xF, 0xF, + SWRS_SCP_FRAME_CTRL_BANK(bank)); +} + +static struct swr_port_info *swrm_get_port_req(struct swrm_mports *mport, + u8 slv_port, u8 dev_num) +{ + struct swr_port_info *port_req = NULL; + + list_for_each_entry(port_req, &mport->port_req_list, list) { + /* Store dev_id instead of dev_num if enumeration is changed run_time */ + if ((port_req->slave_port_id == slv_port) + && (port_req->dev_num == dev_num)) + return port_req; + } + return NULL; +} + +static bool swrm_remove_from_group(struct swr_master *master) +{ + struct swr_device *swr_dev; + struct swr_mstr_ctrl *swrm = swr_get_ctrl_data(master); + bool is_removed = false; + + if (!swrm) + goto end; + + mutex_lock(&swrm->mlock); + if ((swrm->num_rx_chs > 1) && + (swrm->num_rx_chs == swrm->num_cfg_devs)) { + list_for_each_entry(swr_dev, &master->devices, + dev_list) { + swr_dev->group_id = SWR_GROUP_NONE; + master->gr_sid = 0; + } + is_removed = true; + } + mutex_unlock(&swrm->mlock); + +end: + return is_removed; +} + +static void swrm_disable_ports(struct swr_master *master, + u8 bank) +{ + u32 value; + struct swr_port_info *port_req; + int i; + struct swrm_mports *mport; + struct swr_mstr_ctrl *swrm = swr_get_ctrl_data(master); + + if (!swrm) { + pr_err("%s: swrm is null\n", __func__); + return; + } + + dev_dbg(swrm->dev, "%s: master num_port: %d\n", __func__, + master->num_port); + + + for (i = 0; i < SWR_MSTR_PORT_LEN ; i++) { + + mport = &(swrm->mport_cfg[i]); + if (!mport->port_en) + continue; + + list_for_each_entry(port_req, &mport->port_req_list, list) { + /* skip ports with no change req's*/ + if (port_req->req_ch == port_req->ch_en) + continue; + + swrm_cmd_fifo_wr_cmd(swrm, port_req->req_ch, + port_req->dev_num, 0x00, + SWRS_DP_CHANNEL_ENABLE_BANK(port_req->slave_port_id, + bank)); + dev_dbg(swrm->dev, "%s: mport :%d, reg: 0x%x\n", + __func__, i, + (SWRM_DP_PORT_CTRL_BANK(i + 1, bank))); + } + value = ((mport->req_ch) + << SWRM_DP_PORT_CTRL_EN_CHAN_SHFT); + value |= ((mport->offset2) + << SWRM_DP_PORT_CTRL_OFFSET2_SHFT); + value |= ((mport->offset1) + << SWRM_DP_PORT_CTRL_OFFSET1_SHFT); + value |= mport->sinterval; + + swr_master_write(swrm, + SWRM_DP_PORT_CTRL_BANK(i+1, bank), + value); + dev_dbg(swrm->dev, "%s: mport :%d, reg: 0x%x, val: 0x%x\n", + __func__, i, + (SWRM_DP_PORT_CTRL_BANK(i+1, bank)), value); + } +} + +static void swrm_cleanup_disabled_port_reqs(struct swr_master *master) +{ + struct swr_port_info *port_req, *next; + int i; + struct swrm_mports *mport; + struct swr_mstr_ctrl *swrm = swr_get_ctrl_data(master); + + if (!swrm) { + pr_err("%s: swrm is null\n", __func__); + return; + } + dev_dbg(swrm->dev, "%s: master num_port: %d\n", __func__, + master->num_port); + + for (i = 0; i < SWR_MSTR_PORT_LEN; i++) { + mport = &(swrm->mport_cfg[i]); + list_for_each_entry_safe(port_req, next, + &mport->port_req_list, list) { + /* skip ports without new ch req */ + if (port_req->ch_en == port_req->req_ch) + continue; + + /* remove new ch req's*/ + port_req->ch_en = port_req->req_ch; + + /* If no streams enabled on port, remove the port req */ + if (port_req->ch_en == 0) { + list_del(&port_req->list); + kfree(port_req); + } + } + /* remove new ch req's on mport*/ + mport->ch_en = mport->req_ch; + + if (!(mport->ch_en)) { + mport->port_en = false; + master->port_en_mask &= ~i; + } + } +} +static void swrm_copy_data_port_config(struct swr_master *master, u8 bank) +{ + u32 value, slv_id; + struct swr_port_info *port_req; + int i; + struct swrm_mports *mport; + u32 reg[SWRM_MAX_PORT_REG]; + u32 val[SWRM_MAX_PORT_REG]; + int len = 0; + u8 hparams; + struct swr_mstr_ctrl *swrm = swr_get_ctrl_data(master); + + if (!swrm) { + pr_err("%s: swrm is null\n", __func__); + return; + } + + dev_dbg(swrm->dev, "%s: master num_port: %d\n", __func__, + master->num_port); + + for (i = 0; i < SWR_MSTR_PORT_LEN; i++) { + mport = &(swrm->mport_cfg[i]); + if (!mport->port_en) + continue; + + list_for_each_entry(port_req, &mport->port_req_list, list) { + slv_id = port_req->slave_port_id; + reg[len] = SWRM_CMD_FIFO_WR_CMD; + val[len++] = SWR_REG_VAL_PACK(port_req->req_ch, + port_req->dev_num, 0x00, + SWRS_DP_CHANNEL_ENABLE_BANK(slv_id, + bank)); + + reg[len] = SWRM_CMD_FIFO_WR_CMD; + val[len++] = SWR_REG_VAL_PACK(mport->sinterval, + port_req->dev_num, 0x00, + SWRS_DP_SAMPLE_CONTROL_1_BANK(slv_id, + bank)); + + reg[len] = SWRM_CMD_FIFO_WR_CMD; + val[len++] = SWR_REG_VAL_PACK(mport->offset1, + port_req->dev_num, 0x00, + SWRS_DP_OFFSET_CONTROL_1_BANK(slv_id, + bank)); + + if (mport->offset2 != SWR_INVALID_PARAM) { + reg[len] = SWRM_CMD_FIFO_WR_CMD; + val[len++] = SWR_REG_VAL_PACK(mport->offset2, + port_req->dev_num, 0x00, + SWRS_DP_OFFSET_CONTROL_2_BANK( + slv_id, bank)); + } + if (mport->hstart != SWR_INVALID_PARAM + && mport->hstop != SWR_INVALID_PARAM) { + hparams = (mport->hstart << 4) | mport->hstop; + + reg[len] = SWRM_CMD_FIFO_WR_CMD; + val[len++] = SWR_REG_VAL_PACK(hparams, + port_req->dev_num, 0x00, + SWRS_DP_HCONTROL_BANK(slv_id, + bank)); + } + if (mport->word_length != SWR_INVALID_PARAM) { + reg[len] = SWRM_CMD_FIFO_WR_CMD; + val[len++] = + SWR_REG_VAL_PACK(mport->word_length, + port_req->dev_num, 0x00, + SWRS_DP_BLOCK_CONTROL_1(slv_id)); + } + if (mport->blk_pack_mode != SWR_INVALID_PARAM + && swrm->master_id != MASTER_ID_WSA) { + reg[len] = SWRM_CMD_FIFO_WR_CMD; + val[len++] = + SWR_REG_VAL_PACK(mport->blk_pack_mode, + port_req->dev_num, 0x00, + SWRS_DP_BLOCK_CONTROL_3_BANK(slv_id, + bank)); + } + if (mport->blk_grp_count != SWR_INVALID_PARAM) { + reg[len] = SWRM_CMD_FIFO_WR_CMD; + val[len++] = + SWR_REG_VAL_PACK(mport->blk_grp_count, + port_req->dev_num, 0x00, + SWRS_DP_BLOCK_CONTROL_2_BANK(slv_id, + bank)); + } + if (mport->lane_ctrl != SWR_INVALID_PARAM) { + reg[len] = SWRM_CMD_FIFO_WR_CMD; + val[len++] = + SWR_REG_VAL_PACK(mport->lane_ctrl, + port_req->dev_num, 0x00, + SWRS_DP_LANE_CONTROL_BANK(slv_id, + bank)); + } + port_req->ch_en = port_req->req_ch; + } + value = ((mport->req_ch) + << SWRM_DP_PORT_CTRL_EN_CHAN_SHFT); + + if (mport->offset2 != SWR_INVALID_PARAM) + value |= ((mport->offset2) + << SWRM_DP_PORT_CTRL_OFFSET2_SHFT); + value |= ((mport->offset1) + << SWRM_DP_PORT_CTRL_OFFSET1_SHFT); + value |= mport->sinterval; + + + reg[len] = SWRM_DP_PORT_CTRL_BANK(i + 1, bank); + val[len++] = value; + dev_dbg(swrm->dev, "%s: mport :%d, reg: 0x%x, val: 0x%x\n", + __func__, i, + (SWRM_DP_PORT_CTRL_BANK(i + 1, bank)), value); + + if (mport->lane_ctrl != SWR_INVALID_PARAM) { + reg[len] = SWRM_DP_PORT_CTRL_2_BANK(i + 1, bank); + val[len++] = mport->lane_ctrl; + } + if (mport->word_length != SWR_INVALID_PARAM) { + reg[len] = SWRM_DP_BLOCK_CTRL_1(i + 1); + val[len++] = mport->word_length; + } + + if (mport->blk_grp_count != SWR_INVALID_PARAM) { + reg[len] = SWRM_DP_BLOCK_CTRL2_BANK(i + 1, bank); + val[len++] = mport->blk_grp_count; + } + if (mport->hstart != SWR_INVALID_PARAM + && mport->hstop != SWR_INVALID_PARAM) { + reg[len] = SWRM_DP_PORT_HCTRL_BANK(i + 1, bank); + hparams = (mport->hstop << 4) | mport->hstart; + val[len++] = hparams; + } else { + reg[len] = SWRM_DP_PORT_HCTRL_BANK(i + 1, bank); + hparams = (SWR_HSTOP_MAX_VAL << 4) | SWR_HSTART_MIN_VAL; + val[len++] = hparams; + } + if (mport->blk_pack_mode != SWR_INVALID_PARAM) { + reg[len] = SWRM_DP_BLOCK_CTRL3_BANK(i + 1, bank); + val[len++] = mport->blk_pack_mode; + } + mport->ch_en = mport->req_ch; + + } + swr_master_bulk_write(swrm, reg, val, len); +} + +static void swrm_apply_port_config(struct swr_master *master) +{ + u8 bank; + struct swr_mstr_ctrl *swrm = swr_get_ctrl_data(master); + + if (!swrm) { + pr_err("%s: Invalid handle to swr controller\n", + __func__); + return; + } + + bank = get_inactive_bank_num(swrm); + dev_dbg(swrm->dev, "%s: enter bank: %d master_ports: %d\n", + __func__, bank, master->num_port); + + + swrm_cmd_fifo_wr_cmd(swrm, 0x01, 0xF, 0x00, + SWRS_SCP_HOST_CLK_DIV2_CTL_BANK(bank)); + + swrm_copy_data_port_config(master, bank); +} + +static int swrm_slvdev_datapath_control(struct swr_master *master, bool enable) +{ + u8 bank; + u32 value, n_row, n_col; + int ret; + struct swr_mstr_ctrl *swrm = swr_get_ctrl_data(master); + int mask = (SWRM_MCP_FRAME_CTRL_BANK_ROW_CTRL_BMSK | + SWRM_MCP_FRAME_CTRL_BANK_COL_CTRL_BMSK | + SWRM_MCP_FRAME_CTRL_BANK_SSP_PERIOD_BMSK); + u8 inactive_bank; + + if (!swrm) { + pr_err("%s: swrm is null\n", __func__); + return -EFAULT; + } + + mutex_lock(&swrm->mlock); + + bank = get_inactive_bank_num(swrm); + + if (enable) { + if (!test_bit(ENABLE_PENDING, &swrm->port_req_pending)) { + dev_dbg(swrm->dev, "%s:No pending connect port req\n", + __func__); + goto exit; + } + clear_bit(ENABLE_PENDING, &swrm->port_req_pending); + ret = swrm_get_port_config(swrm); + if (ret) { + /* cannot accommodate ports */ + swrm_cleanup_disabled_port_reqs(master); + mutex_unlock(&swrm->mlock); + return -EINVAL; + } + swr_master_write(swrm, SWR_MSTR_RX_SWRM_CPU_INTERRUPT_EN, + SWRM_INTERRUPT_STATUS_MASK); + /* apply the new port config*/ + swrm_apply_port_config(master); + } else { + if (!test_bit(DISABLE_PENDING, &swrm->port_req_pending)) { + dev_dbg(swrm->dev, "%s:No pending disconn port req\n", + __func__); + goto exit; + } + clear_bit(DISABLE_PENDING, &swrm->port_req_pending); + swrm_disable_ports(master, bank); + } + dev_dbg(swrm->dev, "%s: enable: %d, cfg_devs: %d\n", + __func__, enable, swrm->num_cfg_devs); + + if (enable) { + /* set col = 16 */ + n_col = SWR_MAX_COL; + } else { + /* + * Do not change to col = 2 if there are still active ports + */ + if (!master->num_port) + n_col = SWR_MIN_COL; + else + n_col = SWR_MAX_COL; + } + /* Use default 50 * x, frame shape. Change based on mclk */ + if (swrm->mclk_freq == MCLK_FREQ_NATIVE) { + dev_dbg(swrm->dev, "setting 64 x %d frameshape\n", + n_col ? 16 : 2); + n_row = SWR_ROW_64; + } else { + dev_dbg(swrm->dev, "setting 50 x %d frameshape\n", + n_col ? 16 : 2); + n_row = SWR_ROW_50; + } + value = swr_master_read(swrm, SWRM_MCP_FRAME_CTRL_BANK_ADDR(bank)); + value &= (~mask); + value |= ((n_row << SWRM_MCP_FRAME_CTRL_BANK_ROW_CTRL_SHFT) | + (n_col << SWRM_MCP_FRAME_CTRL_BANK_COL_CTRL_SHFT) | + (0 << SWRM_MCP_FRAME_CTRL_BANK_SSP_PERIOD_SHFT)); + swr_master_write(swrm, SWRM_MCP_FRAME_CTRL_BANK_ADDR(bank), value); + + dev_dbg(swrm->dev, "%s: regaddr: 0x%x, value: 0x%x\n", __func__, + SWRM_MCP_FRAME_CTRL_BANK_ADDR(bank), value); + + enable_bank_switch(swrm, bank, n_row, n_col); + inactive_bank = bank ? 0 : 1; + + if (enable) + swrm_copy_data_port_config(master, inactive_bank); + else { + swrm_disable_ports(master, inactive_bank); + swrm_cleanup_disabled_port_reqs(master); + } + if (!swrm_is_port_en(master)) { + dev_dbg(&master->dev, "%s: pm_runtime auto suspend triggered\n", + __func__); + pm_runtime_mark_last_busy(swrm->dev); + pm_runtime_put_autosuspend(swrm->dev); + } +exit: + mutex_unlock(&swrm->mlock); +return 0; +} + +static int swrm_connect_port(struct swr_master *master, + struct swr_params *portinfo) +{ + int i; + struct swr_port_info *port_req; + int ret = 0; + struct swr_mstr_ctrl *swrm = swr_get_ctrl_data(master); + struct swrm_mports *mport; + u8 mstr_port_id, mstr_ch_msk; + + dev_dbg(&master->dev, "%s: enter\n", __func__); + if (!portinfo) + return -EINVAL; + + if (!swrm) { + dev_err(&master->dev, + "%s: Invalid handle to swr controller\n", + __func__); + return -EINVAL; + } + + mutex_lock(&swrm->mlock); + mutex_lock(&swrm->devlock); + if (!swrm->dev_up) { + mutex_unlock(&swrm->devlock); + mutex_unlock(&swrm->mlock); + return -EINVAL; + } + mutex_unlock(&swrm->devlock); + if (!swrm_is_port_en(master)) + pm_runtime_get_sync(swrm->dev); + + for (i = 0; i < portinfo->num_port; i++) { + ret = swrm_get_master_port(swrm, &mstr_port_id, &mstr_ch_msk, + portinfo->port_type[i], + portinfo->port_id[i]); + if (ret) { + dev_err(&master->dev, + "%s: mstr portid for slv port %d not found\n", + __func__, portinfo->port_id[i]); + goto port_fail; + } + + mport = &(swrm->mport_cfg[mstr_port_id]); + /* get port req */ + port_req = swrm_get_port_req(mport, portinfo->port_id[i], + portinfo->dev_num); + if (!port_req) { + dev_dbg(&master->dev, "%s: new req:port id %d dev %d\n", + __func__, portinfo->port_id[i], + portinfo->dev_num); + port_req = kzalloc(sizeof(struct swr_port_info), + GFP_KERNEL); + if (!port_req) { + ret = -ENOMEM; + goto mem_fail; + } + port_req->dev_num = portinfo->dev_num; + port_req->slave_port_id = portinfo->port_id[i]; + port_req->num_ch = portinfo->num_ch[i]; + port_req->ch_rate = portinfo->ch_rate[i]; + port_req->ch_en = 0; + port_req->master_port_id = mstr_port_id; + list_add(&port_req->list, &mport->port_req_list); + } + port_req->req_ch |= portinfo->ch_en[i]; + + dev_dbg(&master->dev, + "%s: mstr port %d, slv port %d ch_rate %d num_ch %d\n", + __func__, port_req->master_port_id, + port_req->slave_port_id, port_req->ch_rate, + port_req->num_ch); + /* Put the port req on master port */ + mport = &(swrm->mport_cfg[mstr_port_id]); + mport->port_en = true; + mport->req_ch |= mstr_ch_msk; + master->port_en_mask |= (1 << mstr_port_id); + } + master->num_port += portinfo->num_port; + set_bit(ENABLE_PENDING, &swrm->port_req_pending); + swr_port_response(master, portinfo->tid); + + mutex_unlock(&swrm->mlock); + return 0; + +port_fail: +mem_fail: + /* cleanup port reqs in error condition */ + swrm_cleanup_disabled_port_reqs(master); + mutex_unlock(&swrm->mlock); + return ret; +} + +static int swrm_disconnect_port(struct swr_master *master, + struct swr_params *portinfo) +{ + int i, ret = 0; + struct swr_port_info *port_req; + struct swrm_mports *mport; + struct swr_mstr_ctrl *swrm = swr_get_ctrl_data(master); + u8 mstr_port_id, mstr_ch_mask; + + if (!swrm) { + dev_err(&master->dev, + "%s: Invalid handle to swr controller\n", + __func__); + return -EINVAL; + } + + if (!portinfo) { + dev_err(&master->dev, "%s: portinfo is NULL\n", __func__); + return -EINVAL; + } + mutex_lock(&swrm->mlock); + + for (i = 0; i < portinfo->num_port; i++) { + + ret = swrm_get_master_port(swrm, &mstr_port_id, &mstr_ch_mask, + portinfo->port_type[i], portinfo->port_id[i]); + if (ret) { + dev_err(&master->dev, + "%s: mstr portid for slv port %d not found\n", + __func__, portinfo->port_id[i]); + mutex_unlock(&swrm->mlock); + return -EINVAL; + } + mport = &(swrm->mport_cfg[mstr_port_id]); + /* get port req */ + port_req = swrm_get_port_req(mport, portinfo->port_id[i], + portinfo->dev_num); + + if (!port_req) { + dev_err(&master->dev, "%s:port not enabled : port %d\n", + __func__, portinfo->port_id[i]); + mutex_unlock(&swrm->mlock); + return -EINVAL; + } + port_req->req_ch &= ~portinfo->ch_en[i]; + mport->req_ch &= ~mstr_ch_mask; + } + master->num_port -= portinfo->num_port; + set_bit(DISABLE_PENDING, &swrm->port_req_pending); + swr_port_response(master, portinfo->tid); + mutex_unlock(&swrm->mlock); + + return 0; +} + +static int swrm_find_alert_slave(struct swr_mstr_ctrl *swrm, + int status, u8 *devnum) +{ + int i; + bool found = false; + + for (i = 0; i < (swrm->master.num_dev + 1); i++) { + if ((status & SWRM_MCP_SLV_STATUS_MASK) == SWR_ALERT) { + *devnum = i; + found = true; + break; + } + status >>= 2; + } + if (found) + return 0; + else + return -EINVAL; +} + +static int swrm_check_slave_change_status(struct swr_mstr_ctrl *swrm, + int status, u8 *devnum) +{ + int i; + int new_sts = status; + int ret = SWR_NOT_PRESENT; + + if (status != swrm->slave_status) { + for (i = 0; i < (swrm->master.num_dev + 1); i++) { + if ((status & SWRM_MCP_SLV_STATUS_MASK) != + (swrm->slave_status & SWRM_MCP_SLV_STATUS_MASK)) { + ret = (status & SWRM_MCP_SLV_STATUS_MASK); + *devnum = i; + break; + } + status >>= 2; + swrm->slave_status >>= 2; + } + swrm->slave_status = new_sts; + } + return ret; +} + +static irqreturn_t swr_mstr_interrupt(int irq, void *dev) +{ + struct swr_mstr_ctrl *swrm = dev; + u32 value, intr_sts, intr_sts_masked; + u32 temp = 0; + u32 status, chg_sts, i; + u8 devnum = 0; + int ret = IRQ_HANDLED; + struct swr_device *swr_dev; + struct swr_master *mstr = &swrm->master; + + if (unlikely(swrm_lock_sleep(swrm) == false)) { + dev_err(swrm->dev, "%s Failed to hold suspend\n", __func__); + return IRQ_NONE; + } + + mutex_lock(&swrm->reslock); + if (swrm_clk_request(swrm, true)) { + dev_err_ratelimited(swrm->dev, "%s:clk request failed\n", + __func__); + mutex_unlock(&swrm->reslock); + goto exit; + } + mutex_unlock(&swrm->reslock); + + intr_sts = swr_master_read(swrm, SWRM_INTERRUPT_STATUS); + intr_sts_masked = intr_sts & swrm->intr_mask; +handle_irq: + for (i = 0; i < SWRM_INTERRUPT_MAX; i++) { + value = intr_sts_masked & (1 << i); + if (!value) + continue; + + switch (value) { + case SWRM_INTERRUPT_STATUS_SLAVE_PEND_IRQ: + dev_dbg(swrm->dev, "Trigger irq to slave device\n"); + status = swr_master_read(swrm, SWRM_MCP_SLV_STATUS); + ret = swrm_find_alert_slave(swrm, status, &devnum); + if (ret) { + dev_err_ratelimited(swrm->dev, + "no slave alert found.spurious interrupt\n"); + break; + } + swrm_cmd_fifo_rd_cmd(swrm, &temp, devnum, 0x0, + SWRS_SCP_INT_STATUS_CLEAR_1, 1); + swrm_cmd_fifo_wr_cmd(swrm, 0x4, devnum, 0x0, + SWRS_SCP_INT_STATUS_CLEAR_1); + swrm_cmd_fifo_wr_cmd(swrm, 0x0, devnum, 0x0, + SWRS_SCP_INT_STATUS_CLEAR_1); + + + list_for_each_entry(swr_dev, &mstr->devices, dev_list) { + if (swr_dev->dev_num != devnum) + continue; + if (swr_dev->slave_irq) { + do { + handle_nested_irq( + irq_find_mapping( + swr_dev->slave_irq, 0)); + } while (swr_dev->slave_irq_pending); + } + + } + break; + case SWRM_INTERRUPT_STATUS_NEW_SLAVE_ATTACHED: + dev_dbg(swrm->dev, "SWR new slave attached\n"); + break; + case SWRM_INTERRUPT_STATUS_CHANGE_ENUM_SLAVE_STATUS: + status = swr_master_read(swrm, SWRM_MCP_SLV_STATUS); + if (status == swrm->slave_status) { + dev_dbg(swrm->dev, + "%s: No change in slave status: %d\n", + __func__, status); + break; + } + chg_sts = swrm_check_slave_change_status(swrm, status, + &devnum); + switch (chg_sts) { + case SWR_NOT_PRESENT: + dev_dbg(swrm->dev, "device %d got detached\n", + devnum); + break; + case SWR_ATTACHED_OK: + dev_dbg(swrm->dev, "device %d got attached\n", + devnum); + /* enable host irq from slave device*/ + swrm_cmd_fifo_wr_cmd(swrm, 0xFF, devnum, 0x0, + SWRS_SCP_INT_STATUS_CLEAR_1); + swrm_cmd_fifo_wr_cmd(swrm, 0x4, devnum, 0x0, + SWRS_SCP_INT_STATUS_MASK_1); + + break; + case SWR_ALERT: + dev_dbg(swrm->dev, + "device %d has pending interrupt\n", + devnum); + break; + } + break; + case SWRM_INTERRUPT_STATUS_MASTER_CLASH_DET: + dev_err_ratelimited(swrm->dev, + "SWR bus clsh detected\n"); + break; + case SWRM_INTERRUPT_STATUS_RD_FIFO_OVERFLOW: + dev_dbg(swrm->dev, "SWR read FIFO overflow\n"); + break; + case SWRM_INTERRUPT_STATUS_RD_FIFO_UNDERFLOW: + dev_dbg(swrm->dev, "SWR read FIFO underflow\n"); + break; + case SWRM_INTERRUPT_STATUS_WR_CMD_FIFO_OVERFLOW: + dev_dbg(swrm->dev, "SWR write FIFO overflow\n"); + break; + case SWRM_INTERRUPT_STATUS_CMD_ERROR: + value = swr_master_read(swrm, SWRM_CMD_FIFO_STATUS); + dev_err_ratelimited(swrm->dev, + "SWR CMD error, fifo status 0x%x, flushing fifo\n", + value); + swr_master_write(swrm, SWRM_CMD_FIFO_CMD, 0x1); + break; + case SWRM_INTERRUPT_STATUS_DOUT_PORT_COLLISION: + dev_err_ratelimited(swrm->dev, "SWR Port collision detected\n"); + swrm->intr_mask &= ~SWRM_INTERRUPT_STATUS_DOUT_PORT_COLLISION; + swr_master_write(swrm, + SWR_MSTR_RX_SWRM_CPU_INTERRUPT_EN, swrm->intr_mask); + break; + case SWRM_INTERRUPT_STATUS_READ_EN_RD_VALID_MISMATCH: + dev_dbg(swrm->dev, "SWR read enable valid mismatch\n"); + swrm->intr_mask &= + ~SWRM_INTERRUPT_STATUS_READ_EN_RD_VALID_MISMATCH; + swr_master_write(swrm, + SWR_MSTR_RX_SWRM_CPU_INTERRUPT_EN, swrm->intr_mask); + break; + case SWRM_INTERRUPT_STATUS_SPECIAL_CMD_ID_FINISHED: + complete(&swrm->broadcast); + dev_dbg(swrm->dev, "SWR cmd id finished\n"); + break; + case SWRM_INTERRUPT_STATUS_NEW_SLAVE_AUTO_ENUM_FINISHED: + break; + case SWRM_INTERRUPT_STATUS_AUTO_ENUM_FAILED: + break; + case SWRM_INTERRUPT_STATUS_AUTO_ENUM_TABLE_IS_FULL: + break; + case SWRM_INTERRUPT_STATUS_BUS_RESET_FINISHED: + complete(&swrm->reset); + break; + case SWRM_INTERRUPT_STATUS_CLK_STOP_FINISHED: + break; + default: + dev_err_ratelimited(swrm->dev, + "SWR unknown interrupt\n"); + ret = IRQ_NONE; + break; + } + } + swr_master_write(swrm, SWRM_INTERRUPT_CLEAR, intr_sts); + swr_master_write(swrm, SWRM_INTERRUPT_CLEAR, 0x0); + + intr_sts = swr_master_read(swrm, SWRM_INTERRUPT_STATUS); + intr_sts_masked = intr_sts & swrm->intr_mask; + + if (intr_sts_masked) { + dev_dbg(swrm->dev, "%s: new interrupt received\n", __func__); + goto handle_irq; + } + + mutex_lock(&swrm->reslock); + swrm_clk_request(swrm, false); + mutex_unlock(&swrm->reslock); +exit: + swrm_unlock_sleep(swrm); + return ret; +} + +static irqreturn_t swrm_wakeup_interrupt(int irq, void *dev) +{ + struct swr_mstr_ctrl *swrm = dev; + int ret = IRQ_HANDLED; + + if (!swrm || !(swrm->dev)) { + pr_err("%s: swrm or dev is null\n", __func__); + return IRQ_NONE; + } + mutex_lock(&swrm->devlock); + if (!swrm->dev_up) { + if (swrm->wake_irq > 0) + disable_irq_nosync(swrm->wake_irq); + mutex_unlock(&swrm->devlock); + return ret; + } + mutex_unlock(&swrm->devlock); + if (unlikely(swrm_lock_sleep(swrm) == false)) { + dev_err(swrm->dev, "%s Failed to hold suspend\n", __func__); + goto exit; + } + if (swrm->wake_irq > 0) + disable_irq_nosync(swrm->wake_irq); + pm_runtime_get_sync(swrm->dev); + pm_runtime_mark_last_busy(swrm->dev); + pm_runtime_put_autosuspend(swrm->dev); + swrm_unlock_sleep(swrm); +exit: + return ret; +} + +static void swrm_wakeup_work(struct work_struct *work) +{ + struct swr_mstr_ctrl *swrm; + + swrm = container_of(work, struct swr_mstr_ctrl, + wakeup_work); + if (!swrm || !(swrm->dev)) { + pr_err("%s: swrm or dev is null\n", __func__); + return; + } + + mutex_lock(&swrm->devlock); + if (!swrm->dev_up) { + mutex_unlock(&swrm->devlock); + goto exit; + } + mutex_unlock(&swrm->devlock); + if (unlikely(swrm_lock_sleep(swrm) == false)) { + dev_err(swrm->dev, "%s Failed to hold suspend\n", __func__); + goto exit; + } + pm_runtime_get_sync(swrm->dev); + pm_runtime_mark_last_busy(swrm->dev); + pm_runtime_put_autosuspend(swrm->dev); + swrm_unlock_sleep(swrm); +exit: + pm_relax(swrm->dev); +} + +static int swrm_get_device_status(struct swr_mstr_ctrl *swrm, u8 devnum) +{ + u32 val; + + swrm->slave_status = swr_master_read(swrm, SWRM_MCP_SLV_STATUS); + val = (swrm->slave_status >> (devnum * 2)); + val &= SWRM_MCP_SLV_STATUS_MASK; + return val; +} + +static int swrm_get_logical_dev_num(struct swr_master *mstr, u64 dev_id, + u8 *dev_num) +{ + int i; + u64 id = 0; + int ret = -EINVAL; + struct swr_mstr_ctrl *swrm = swr_get_ctrl_data(mstr); + struct swr_device *swr_dev; + u32 num_dev = 0; + + if (!swrm) { + pr_err("%s: Invalid handle to swr controller\n", + __func__); + return ret; + } + if (swrm->num_dev) + num_dev = swrm->num_dev; + else + num_dev = mstr->num_dev; + + mutex_lock(&swrm->devlock); + if (!swrm->dev_up) { + mutex_unlock(&swrm->devlock); + return ret; + } + mutex_unlock(&swrm->devlock); + + pm_runtime_get_sync(swrm->dev); + for (i = 1; i < (num_dev + 1); i++) { + id = ((u64)(swr_master_read(swrm, + SWRM_ENUMERATOR_SLAVE_DEV_ID_2(i))) << 32); + id |= swr_master_read(swrm, + SWRM_ENUMERATOR_SLAVE_DEV_ID_1(i)); + + /* + * As pm_runtime_get_sync() brings all slaves out of reset + * update logical device number for all slaves. + */ + list_for_each_entry(swr_dev, &mstr->devices, dev_list) { + if (swr_dev->addr == (id & SWR_DEV_ID_MASK)) { + u32 status = swrm_get_device_status(swrm, i); + + if ((status == 0x01) || (status == 0x02)) { + swr_dev->dev_num = i; + if ((id & SWR_DEV_ID_MASK) == dev_id) { + *dev_num = i; + ret = 0; + } + dev_dbg(swrm->dev, + "%s: devnum %d is assigned for dev addr %lx\n", + __func__, i, swr_dev->addr); + } + } + } + } + if (ret) + dev_err(swrm->dev, "%s: device 0x%llx is not ready\n", + __func__, dev_id); + + pm_runtime_mark_last_busy(swrm->dev); + pm_runtime_put_autosuspend(swrm->dev); + return ret; +} + +static void swrm_device_wakeup_vote(struct swr_master *mstr) +{ + struct swr_mstr_ctrl *swrm = swr_get_ctrl_data(mstr); + + if (!swrm) { + pr_err("%s: Invalid handle to swr controller\n", + __func__); + return; + } + if (unlikely(swrm_lock_sleep(swrm) == false)) { + dev_err(swrm->dev, "%s Failed to hold suspend\n", __func__); + return; + } + pm_runtime_get_sync(swrm->dev); +} + +static void swrm_device_wakeup_unvote(struct swr_master *mstr) +{ + struct swr_mstr_ctrl *swrm = swr_get_ctrl_data(mstr); + + if (!swrm) { + pr_err("%s: Invalid handle to swr controller\n", + __func__); + return; + } + pm_runtime_mark_last_busy(swrm->dev); + pm_runtime_put_autosuspend(swrm->dev); + swrm_unlock_sleep(swrm); +} + +static int swrm_master_init(struct swr_mstr_ctrl *swrm) +{ + int ret = 0; + u32 val; + u8 row_ctrl = SWR_ROW_50; + u8 col_ctrl = SWR_MIN_COL; + u8 ssp_period = 1; + u8 retry_cmd_num = 3; + u32 reg[SWRM_MAX_INIT_REG]; + u32 value[SWRM_MAX_INIT_REG]; + int len = 0; + + /* Clear Rows and Cols */ + val = ((row_ctrl << SWRM_MCP_FRAME_CTRL_BANK_ROW_CTRL_SHFT) | + (col_ctrl << SWRM_MCP_FRAME_CTRL_BANK_COL_CTRL_SHFT) | + (ssp_period << SWRM_MCP_FRAME_CTRL_BANK_SSP_PERIOD_SHFT)); + + reg[len] = SWRM_MCP_FRAME_CTRL_BANK_ADDR(0); + value[len++] = val; + + /* Set Auto enumeration flag */ + reg[len] = SWRM_ENUMERATOR_CFG_ADDR; + value[len++] = 1; + + /* Configure No pings */ + val = swr_master_read(swrm, SWRM_MCP_CFG_ADDR); + val &= ~SWRM_MCP_CFG_MAX_NUM_OF_CMD_NO_PINGS_BMSK; + val |= (0x1f << SWRM_MCP_CFG_MAX_NUM_OF_CMD_NO_PINGS_SHFT); + reg[len] = SWRM_MCP_CFG_ADDR; + value[len++] = val; + + /* Configure number of retries of a read/write cmd */ + val = (retry_cmd_num << SWRM_CMD_FIFO_CFG_NUM_OF_CMD_RETRY_SHFT); + reg[len] = SWRM_CMD_FIFO_CFG_ADDR; + value[len++] = val; + + reg[len] = SWRM_MCP_BUS_CTRL_ADDR; + value[len++] = 0x2; + + /* Set IRQ to PULSE */ + reg[len] = SWRM_COMP_CFG_ADDR; + value[len++] = 0x02; + + reg[len] = SWRM_COMP_CFG_ADDR; + value[len++] = 0x03; + + reg[len] = SWRM_INTERRUPT_CLEAR; + value[len++] = 0xFFFFFFFF; + + swrm->intr_mask = SWRM_INTERRUPT_STATUS_MASK; + /* Mask soundwire interrupts */ + reg[len] = SWRM_INTERRUPT_MASK_ADDR; + value[len++] = swrm->intr_mask; + + reg[len] = SWR_MSTR_RX_SWRM_CPU_INTERRUPT_EN; + value[len++] = swrm->intr_mask; + + swr_master_bulk_write(swrm, reg, value, len); + + return ret; +} + +static int swrm_event_notify(struct notifier_block *self, + unsigned long action, void *data) +{ + struct swr_mstr_ctrl *swrm = container_of(self, struct swr_mstr_ctrl, + event_notifier); + + if (!swrm || !(swrm->dev)) { + pr_err("%s: swrm or dev is NULL\n", __func__); + return -EINVAL; + } + switch (action) { + case MSM_AUD_DC_EVENT: + schedule_work(&(swrm->dc_presence_work)); + break; + case SWR_WAKE_IRQ_EVENT: + if (swrm->ipc_wakeup && !swrm->ipc_wakeup_triggered) { + swrm->ipc_wakeup_triggered = true; + pm_stay_awake(swrm->dev); + schedule_work(&swrm->wakeup_work); + } + break; + default: + dev_err(swrm->dev, "%s: invalid event type: %lu\n", + __func__, action); + return -EINVAL; + } + + return 0; +} + +static void swrm_notify_work_fn(struct work_struct *work) +{ + struct swr_mstr_ctrl *swrm = container_of(work, struct swr_mstr_ctrl, + dc_presence_work); + + if (!swrm || !swrm->pdev) { + pr_err("%s: swrm or pdev is NULL\n", __func__); + return; + } + swrm_wcd_notify(swrm->pdev, SWR_DEVICE_DOWN, NULL); +} + +static int swrm_probe(struct platform_device *pdev) +{ + struct swr_mstr_ctrl *swrm; + struct swr_ctrl_platform_data *pdata; + u32 i, num_ports, port_num, port_type, ch_mask; + u32 *temp, map_size, map_length, ch_iter = 0, old_port_num = 0; + int ret = 0; + + /* Allocate soundwire master driver structure */ + swrm = devm_kzalloc(&pdev->dev, sizeof(struct swr_mstr_ctrl), + GFP_KERNEL); + if (!swrm) { + ret = -ENOMEM; + goto err_memory_fail; + } + swrm->pdev = pdev; + swrm->dev = &pdev->dev; + platform_set_drvdata(pdev, swrm); + swr_set_ctrl_data(&swrm->master, swrm); + pdata = dev_get_platdata(&pdev->dev); + if (!pdata) { + dev_err(&pdev->dev, "%s: pdata from parent is NULL\n", + __func__); + ret = -EINVAL; + goto err_pdata_fail; + } + swrm->handle = (void *)pdata->handle; + if (!swrm->handle) { + dev_err(&pdev->dev, "%s: swrm->handle is NULL\n", + __func__); + ret = -EINVAL; + goto err_pdata_fail; + } + ret = of_property_read_u32(pdev->dev.of_node, "qcom,swr_master_id", + &swrm->master_id); + if (ret) { + dev_err(&pdev->dev, "%s: failed to get master id\n", __func__); + goto err_pdata_fail; + } + if (!(of_property_read_u32(pdev->dev.of_node, + "swrm-io-base", &swrm->swrm_base_reg))) + ret = of_property_read_u32(pdev->dev.of_node, + "swrm-io-base", &swrm->swrm_base_reg); + if (!swrm->swrm_base_reg) { + swrm->read = pdata->read; + if (!swrm->read) { + dev_err(&pdev->dev, "%s: swrm->read is NULL\n", + __func__); + ret = -EINVAL; + goto err_pdata_fail; + } + swrm->write = pdata->write; + if (!swrm->write) { + dev_err(&pdev->dev, "%s: swrm->write is NULL\n", + __func__); + ret = -EINVAL; + goto err_pdata_fail; + } + swrm->bulk_write = pdata->bulk_write; + if (!swrm->bulk_write) { + dev_err(&pdev->dev, "%s: swrm->bulk_write is NULL\n", + __func__); + ret = -EINVAL; + goto err_pdata_fail; + } + } else { + swrm->swrm_dig_base = devm_ioremap(&pdev->dev, + swrm->swrm_base_reg, SWRM_MAX_REGISTER); + } + + swrm->clk = pdata->clk; + if (!swrm->clk) { + dev_err(&pdev->dev, "%s: swrm->clk is NULL\n", + __func__); + ret = -EINVAL; + goto err_pdata_fail; + } + if (of_property_read_u32(pdev->dev.of_node, + "qcom,swr-clock-stop-mode0", + &swrm->clk_stop_mode0_supp)) { + swrm->clk_stop_mode0_supp = FALSE; + } + + ret = of_property_read_u32(swrm->dev->of_node, "qcom,swr-num-dev", + &swrm->num_dev); + if (ret) { + dev_dbg(&pdev->dev, "%s: Looking up %s property failed\n", + __func__, "qcom,swr-num-dev"); + } else { + if (swrm->num_dev > SWR_MAX_SLAVE_DEVICES) { + dev_err(&pdev->dev, "%s: num_dev %d > max limit %d\n", + __func__, swrm->num_dev, SWR_MAX_SLAVE_DEVICES); + ret = -EINVAL; + goto err_pdata_fail; + } + } + + /* Parse soundwire port mapping */ + ret = of_property_read_u32(pdev->dev.of_node, "qcom,swr-num-ports", + &num_ports); + if (ret) { + dev_err(swrm->dev, "%s: Failed to get num_ports\n", __func__); + goto err_pdata_fail; + } + swrm->num_ports = num_ports; + + if (!of_find_property(pdev->dev.of_node, "qcom,swr-port-mapping", + &map_size)) { + dev_err(swrm->dev, "missing port mapping\n"); + goto err_pdata_fail; + } + + map_length = map_size / (3 * sizeof(u32)); + if (num_ports > SWR_MSTR_PORT_LEN) { + dev_err(&pdev->dev, "%s:invalid number of swr ports\n", + __func__); + ret = -EINVAL; + goto err_pdata_fail; + } + temp = devm_kzalloc(&pdev->dev, map_size, GFP_KERNEL); + + if (!temp) { + ret = -ENOMEM; + goto err_pdata_fail; + } + ret = of_property_read_u32_array(pdev->dev.of_node, + "qcom,swr-port-mapping", temp, 3 * map_length); + if (ret) { + dev_err(swrm->dev, "%s: Failed to read port mapping\n", + __func__); + goto err_pdata_fail; + } + + for (i = 0; i < map_length; i++) { + port_num = temp[3 * i]; + port_type = temp[3 * i + 1]; + ch_mask = temp[3 * i + 2]; + + if (port_num != old_port_num) + ch_iter = 0; + swrm->port_mapping[port_num][ch_iter].port_type = port_type; + swrm->port_mapping[port_num][ch_iter++].ch_mask = ch_mask; + old_port_num = port_num; + } + devm_kfree(&pdev->dev, temp); + + swrm->reg_irq = pdata->reg_irq; + swrm->master.read = swrm_read; + swrm->master.write = swrm_write; + swrm->master.bulk_write = swrm_bulk_write; + swrm->master.get_logical_dev_num = swrm_get_logical_dev_num; + swrm->master.connect_port = swrm_connect_port; + swrm->master.disconnect_port = swrm_disconnect_port; + swrm->master.slvdev_datapath_control = swrm_slvdev_datapath_control; + swrm->master.remove_from_group = swrm_remove_from_group; + swrm->master.device_wakeup_vote = swrm_device_wakeup_vote; + swrm->master.device_wakeup_unvote = swrm_device_wakeup_unvote; + swrm->master.dev.parent = &pdev->dev; + swrm->master.dev.of_node = pdev->dev.of_node; + swrm->master.num_port = 0; + swrm->rcmd_id = 0; + swrm->wcmd_id = 0; + swrm->slave_status = 0; + swrm->num_rx_chs = 0; + swrm->clk_ref_count = 0; + swrm->mclk_freq = MCLK_FREQ; + swrm->dev_up = true; + swrm->state = SWR_MSTR_UP; + swrm->ipc_wakeup = false; + swrm->ipc_wakeup_triggered = false; + init_completion(&swrm->reset); + init_completion(&swrm->broadcast); + init_completion(&swrm->clk_off_complete); + mutex_init(&swrm->mlock); + mutex_init(&swrm->reslock); + mutex_init(&swrm->force_down_lock); + mutex_init(&swrm->iolock); + mutex_init(&swrm->clklock); + mutex_init(&swrm->devlock); + mutex_init(&swrm->pm_lock); + swrm->wlock_holders = 0; + swrm->pm_state = SWRM_PM_SLEEPABLE; + init_waitqueue_head(&swrm->pm_wq); + pm_qos_add_request(&swrm->pm_qos_req, + PM_QOS_CPU_DMA_LATENCY, + PM_QOS_DEFAULT_VALUE); + + for (i = 0 ; i < SWR_MSTR_PORT_LEN; i++) + INIT_LIST_HEAD(&swrm->mport_cfg[i].port_req_list); + + if (swrm->reg_irq) { + ret = swrm->reg_irq(swrm->handle, swr_mstr_interrupt, swrm, + SWR_IRQ_REGISTER); + if (ret) { + dev_err(&pdev->dev, "%s: IRQ register failed ret %d\n", + __func__, ret); + goto err_irq_fail; + } + } else { + swrm->irq = platform_get_irq_byname(pdev, "swr_master_irq"); + if (swrm->irq < 0) { + dev_err(swrm->dev, "%s() error getting irq hdle: %d\n", + __func__, swrm->irq); + goto err_irq_fail; + } + + ret = request_threaded_irq(swrm->irq, NULL, + swr_mstr_interrupt, + IRQF_TRIGGER_RISING | IRQF_ONESHOT, + "swr_master_irq", swrm); + if (ret) { + dev_err(swrm->dev, "%s: Failed to request irq %d\n", + __func__, ret); + goto err_irq_fail; + } + + } + + ret = swr_register_master(&swrm->master); + if (ret) { + dev_err(&pdev->dev, "%s: error adding swr master\n", __func__); + goto err_mstr_fail; + } + + /* Add devices registered with board-info as the + * controller will be up now + */ + swr_master_add_boarddevices(&swrm->master); + mutex_lock(&swrm->mlock); + swrm_clk_request(swrm, true); + ret = swrm_master_init(swrm); + if (ret < 0) { + dev_err(&pdev->dev, + "%s: Error in master Initialization , err %d\n", + __func__, ret); + mutex_unlock(&swrm->mlock); + goto err_mstr_fail; + } + swrm->version = swr_master_read(swrm, SWRM_COMP_HW_VERSION); + + mutex_unlock(&swrm->mlock); + INIT_WORK(&swrm->wakeup_work, swrm_wakeup_work); + + if (pdev->dev.of_node) + of_register_swr_devices(&swrm->master); + + dbgswrm = swrm; + debugfs_swrm_dent = debugfs_create_dir(dev_name(&pdev->dev), 0); + if (!IS_ERR(debugfs_swrm_dent)) { + debugfs_peek = debugfs_create_file("swrm_peek", + S_IFREG | 0444, debugfs_swrm_dent, + (void *) "swrm_peek", &swrm_debug_ops); + + debugfs_poke = debugfs_create_file("swrm_poke", + S_IFREG | 0444, debugfs_swrm_dent, + (void *) "swrm_poke", &swrm_debug_ops); + + debugfs_reg_dump = debugfs_create_file("swrm_reg_dump", + S_IFREG | 0444, debugfs_swrm_dent, + (void *) "swrm_reg_dump", + &swrm_debug_ops); + } + pm_runtime_set_autosuspend_delay(&pdev->dev, auto_suspend_timer); + pm_runtime_use_autosuspend(&pdev->dev); + pm_runtime_set_active(&pdev->dev); + pm_runtime_enable(&pdev->dev); + pm_runtime_mark_last_busy(&pdev->dev); + + INIT_WORK(&swrm->dc_presence_work, swrm_notify_work_fn); + swrm->event_notifier.notifier_call = swrm_event_notify; + msm_aud_evt_register_client(&swrm->event_notifier); + + return 0; +err_mstr_fail: + if (swrm->reg_irq) + swrm->reg_irq(swrm->handle, swr_mstr_interrupt, + swrm, SWR_IRQ_FREE); + else if (swrm->irq) + free_irq(swrm->irq, swrm); +err_irq_fail: + mutex_destroy(&swrm->mlock); + mutex_destroy(&swrm->reslock); + mutex_destroy(&swrm->force_down_lock); + mutex_destroy(&swrm->iolock); + mutex_destroy(&swrm->clklock); + mutex_destroy(&swrm->pm_lock); + pm_qos_remove_request(&swrm->pm_qos_req); + +err_pdata_fail: +err_memory_fail: + return ret; +} + +static int swrm_remove(struct platform_device *pdev) +{ + struct swr_mstr_ctrl *swrm = platform_get_drvdata(pdev); + + if (swrm->reg_irq) + swrm->reg_irq(swrm->handle, swr_mstr_interrupt, + swrm, SWR_IRQ_FREE); + else if (swrm->irq) + free_irq(swrm->irq, swrm); + else if (swrm->wake_irq > 0) + free_irq(swrm->wake_irq, swrm); + cancel_work_sync(&swrm->wakeup_work); + pm_runtime_disable(&pdev->dev); + pm_runtime_set_suspended(&pdev->dev); + swr_unregister_master(&swrm->master); + msm_aud_evt_unregister_client(&swrm->event_notifier); + mutex_destroy(&swrm->mlock); + mutex_destroy(&swrm->reslock); + mutex_destroy(&swrm->iolock); + mutex_destroy(&swrm->clklock); + mutex_destroy(&swrm->force_down_lock); + mutex_destroy(&swrm->pm_lock); + pm_qos_remove_request(&swrm->pm_qos_req); + devm_kfree(&pdev->dev, swrm); + return 0; +} + +static int swrm_clk_pause(struct swr_mstr_ctrl *swrm) +{ + u32 val; + + dev_dbg(swrm->dev, "%s: state: %d\n", __func__, swrm->state); + swr_master_write(swrm, SWRM_INTERRUPT_MASK_ADDR, 0x1FDFD); + val = swr_master_read(swrm, SWRM_MCP_CFG_ADDR); + val |= SWRM_MCP_CFG_BUS_CLK_PAUSE_BMSK; + swr_master_write(swrm, SWRM_MCP_CFG_ADDR, val); + + return 0; +} + +#ifdef CONFIG_PM +static int swrm_runtime_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct swr_mstr_ctrl *swrm = platform_get_drvdata(pdev); + int ret = 0; + struct swr_master *mstr = &swrm->master; + struct swr_device *swr_dev; + + dev_dbg(dev, "%s: pm_runtime: resume, state:%d\n", + __func__, swrm->state); + mutex_lock(&swrm->reslock); + + if ((swrm->state == SWR_MSTR_DOWN) || + (swrm->state == SWR_MSTR_SSR && swrm->dev_up)) { + if (swrm->clk_stop_mode0_supp) { + if (swrm->ipc_wakeup) + msm_aud_evt_blocking_notifier_call_chain( + SWR_WAKE_IRQ_DEREGISTER, (void *)swrm); + } + + if (swrm_clk_request(swrm, true)) + goto exit; + if (!swrm->clk_stop_mode0_supp || swrm->state == SWR_MSTR_SSR) { + enable_bank_switch(swrm, 0, SWR_ROW_50, SWR_MIN_COL); + list_for_each_entry(swr_dev, &mstr->devices, dev_list) { + ret = swr_device_up(swr_dev); + if (ret) { + dev_err(dev, + "%s: failed to wakeup swr dev %d\n", + __func__, swr_dev->dev_num); + swrm_clk_request(swrm, false); + goto exit; + } + } + swr_master_write(swrm, SWRM_COMP_SW_RESET, 0x01); + swr_master_write(swrm, SWRM_COMP_SW_RESET, 0x01); + swrm_master_init(swrm); + swrm_cmd_fifo_wr_cmd(swrm, 0x4, 0xF, 0x0, + SWRS_SCP_INT_STATUS_MASK_1); + + } else { + /*wake up from clock stop*/ + swr_master_write(swrm, SWRM_MCP_BUS_CTRL_ADDR, 0x2); + usleep_range(100, 105); + } + swrm->state = SWR_MSTR_UP; + } +exit: + pm_runtime_set_autosuspend_delay(&pdev->dev, auto_suspend_timer); + mutex_unlock(&swrm->reslock); + return ret; +} + +static int swrm_runtime_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct swr_mstr_ctrl *swrm = platform_get_drvdata(pdev); + int ret = 0; + struct swr_master *mstr = &swrm->master; + struct swr_device *swr_dev; + int current_state = 0; + + dev_dbg(dev, "%s: pm_runtime: suspend state: %d\n", + __func__, swrm->state); + mutex_lock(&swrm->reslock); + mutex_lock(&swrm->force_down_lock); + current_state = swrm->state; + mutex_unlock(&swrm->force_down_lock); + if ((current_state == SWR_MSTR_UP) || + (current_state == SWR_MSTR_SSR)) { + + if ((current_state != SWR_MSTR_SSR) && + swrm_is_port_en(&swrm->master)) { + dev_dbg(dev, "%s ports are enabled\n", __func__); + ret = -EBUSY; + goto exit; + } + if (!swrm->clk_stop_mode0_supp || swrm->state == SWR_MSTR_SSR) { + enable_bank_switch(swrm, 0, SWR_ROW_50, SWR_MIN_COL); + swrm_clk_pause(swrm); + swr_master_write(swrm, SWRM_COMP_CFG_ADDR, 0x00); + list_for_each_entry(swr_dev, &mstr->devices, dev_list) { + ret = swr_device_down(swr_dev); + if (ret) { + dev_err(dev, + "%s: failed to shutdown swr dev %d\n", + __func__, swr_dev->dev_num); + goto exit; + } + } + } else { + /* clock stop sequence */ + swrm_cmd_fifo_wr_cmd(swrm, 0x2, 0xF, 0xF, + SWRS_SCP_CONTROL); + usleep_range(100, 105); + } + swrm_clk_request(swrm, false); + + if (swrm->clk_stop_mode0_supp) { + if (swrm->wake_irq > 0) { + enable_irq(swrm->wake_irq); + } else if (swrm->ipc_wakeup) { + msm_aud_evt_blocking_notifier_call_chain( + SWR_WAKE_IRQ_REGISTER, (void *)swrm); + swrm->ipc_wakeup_triggered = false; + } + } + + } + /* Retain SSR state until resume */ + if (current_state != SWR_MSTR_SSR) + swrm->state = SWR_MSTR_DOWN; +exit: + mutex_unlock(&swrm->reslock); + return ret; +} +#endif /* CONFIG_PM */ + +static int swrm_device_down(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct swr_mstr_ctrl *swrm = platform_get_drvdata(pdev); + int ret = 0; + + dev_dbg(dev, "%s: swrm state: %d\n", __func__, swrm->state); + + mutex_lock(&swrm->force_down_lock); + swrm->state = SWR_MSTR_SSR; + mutex_unlock(&swrm->force_down_lock); + if (!pm_runtime_enabled(dev) || !pm_runtime_suspended(dev)) { + ret = swrm_runtime_suspend(dev); + if (!ret) { + pm_runtime_disable(dev); + pm_runtime_set_suspended(dev); + pm_runtime_enable(dev); + } + } + + return 0; +} + +int swrm_register_wake_irq(struct swr_mstr_ctrl *swrm) +{ + int ret = 0; + int irq, dir_apps_irq; + + if (!swrm->ipc_wakeup) { + irq = of_get_named_gpio(swrm->dev->of_node, + "qcom,swr-wakeup-irq", 0); + if (gpio_is_valid(irq)) { + swrm->wake_irq = gpio_to_irq(irq); + if (swrm->wake_irq < 0) { + dev_err(swrm->dev, + "Unable to configure irq\n"); + return swrm->wake_irq; + } + } else { + dir_apps_irq = platform_get_irq_byname(swrm->pdev, + "swr_wake_irq"); + if (dir_apps_irq < 0) { + dev_err(swrm->dev, + "TLMM connect gpio not found\n"); + return -EINVAL; + } + swrm->wake_irq = dir_apps_irq; + } + ret = request_threaded_irq(swrm->wake_irq, NULL, + swrm_wakeup_interrupt, + IRQF_TRIGGER_HIGH | IRQF_ONESHOT, + "swr_wake_irq", swrm); + if (ret) { + dev_err(swrm->dev, "%s: Failed to request irq %d\n", + __func__, ret); + return -EINVAL; + } + irq_set_irq_wake(swrm->wake_irq, 1); + } + return ret; +} + +/** + * swrm_wcd_notify - parent device can notify to soundwire master through + * this function + * @pdev: pointer to platform device structure + * @id: command id from parent to the soundwire master + * @data: data from parent device to soundwire master + */ +int swrm_wcd_notify(struct platform_device *pdev, u32 id, void *data) +{ + struct swr_mstr_ctrl *swrm; + int ret = 0; + struct swr_master *mstr; + struct swr_device *swr_dev; + + if (!pdev) { + pr_err("%s: pdev is NULL\n", __func__); + return -EINVAL; + } + swrm = platform_get_drvdata(pdev); + if (!swrm) { + dev_err(&pdev->dev, "%s: swrm is NULL\n", __func__); + return -EINVAL; + } + mstr = &swrm->master; + + switch (id) { + case SWR_CLK_FREQ: + if (!data) { + dev_err(swrm->dev, "%s: data is NULL\n", __func__); + ret = -EINVAL; + } else { + mutex_lock(&swrm->mlock); + swrm->mclk_freq = *(int *)data; + mutex_unlock(&swrm->mlock); + } + break; + case SWR_DEVICE_SSR_DOWN: + mutex_lock(&swrm->devlock); + swrm->dev_up = false; + mutex_unlock(&swrm->devlock); + mutex_lock(&swrm->reslock); + swrm->state = SWR_MSTR_SSR; + mutex_unlock(&swrm->reslock); + break; + case SWR_DEVICE_SSR_UP: + /* wait for clk voting to be zero */ + reinit_completion(&swrm->clk_off_complete); + if (swrm->clk_ref_count && + !wait_for_completion_timeout(&swrm->clk_off_complete, + msecs_to_jiffies(5000))) + dev_err(swrm->dev, "%s: clock voting not zero\n", + __func__); + + mutex_lock(&swrm->devlock); + swrm->dev_up = true; + mutex_unlock(&swrm->devlock); + break; + case SWR_DEVICE_DOWN: + dev_dbg(swrm->dev, "%s: swr master down called\n", __func__); + mutex_lock(&swrm->mlock); + if (swrm->state == SWR_MSTR_DOWN) + dev_dbg(swrm->dev, "%s:SWR master is already Down:%d\n", + __func__, swrm->state); + else + swrm_device_down(&pdev->dev); + mutex_unlock(&swrm->mlock); + break; + case SWR_DEVICE_UP: + dev_dbg(swrm->dev, "%s: swr master up called\n", __func__); + mutex_lock(&swrm->devlock); + if (!swrm->dev_up) { + dev_dbg(swrm->dev, "SSR not complete yet\n"); + mutex_unlock(&swrm->devlock); + return -EBUSY; + } + mutex_unlock(&swrm->devlock); + mutex_lock(&swrm->mlock); + pm_runtime_mark_last_busy(&pdev->dev); + pm_runtime_get_sync(&pdev->dev); + mutex_lock(&swrm->reslock); + list_for_each_entry(swr_dev, &mstr->devices, dev_list) { + ret = swr_reset_device(swr_dev); + if (ret) { + dev_err(swrm->dev, + "%s: failed to reset swr device %d\n", + __func__, swr_dev->dev_num); + swrm_clk_request(swrm, false); + } + } + pm_runtime_mark_last_busy(&pdev->dev); + pm_runtime_put_autosuspend(&pdev->dev); + mutex_unlock(&swrm->reslock); + mutex_unlock(&swrm->mlock); + break; + case SWR_SET_NUM_RX_CH: + if (!data) { + dev_err(swrm->dev, "%s: data is NULL\n", __func__); + ret = -EINVAL; + } else { + mutex_lock(&swrm->mlock); + swrm->num_rx_chs = *(int *)data; + if ((swrm->num_rx_chs > 1) && !swrm->num_cfg_devs) { + list_for_each_entry(swr_dev, &mstr->devices, + dev_list) { + ret = swr_set_device_group(swr_dev, + SWR_BROADCAST); + if (ret) + dev_err(swrm->dev, + "%s: set num ch failed\n", + __func__); + } + } else { + list_for_each_entry(swr_dev, &mstr->devices, + dev_list) { + ret = swr_set_device_group(swr_dev, + SWR_GROUP_NONE); + if (ret) + dev_err(swrm->dev, + "%s: set num ch failed\n", + __func__); + } + } + mutex_unlock(&swrm->mlock); + } + break; + case SWR_REGISTER_WAKE_IRQ: + if (!data) { + dev_err(swrm->dev, "%s: reg wake irq data is NULL\n", + __func__); + ret = -EINVAL; + } else { + mutex_lock(&swrm->mlock); + swrm->ipc_wakeup = *(u32 *)data; + ret = swrm_register_wake_irq(swrm); + if (ret) + dev_err(swrm->dev, "%s: register wake_irq failed\n", + __func__); + mutex_unlock(&swrm->mlock); + } + break; + default: + dev_err(swrm->dev, "%s: swr master unknown id %d\n", + __func__, id); + break; + } + return ret; +} +EXPORT_SYMBOL(swrm_wcd_notify); + +/* + * swrm_pm_cmpxchg: + * Check old state and exchange with pm new state + * if old state matches with current state + * + * @swrm: pointer to wcd core resource + * @o: pm old state + * @n: pm new state + * + * Returns old state + */ +static enum swrm_pm_state swrm_pm_cmpxchg( + struct swr_mstr_ctrl *swrm, + enum swrm_pm_state o, + enum swrm_pm_state n) +{ + enum swrm_pm_state old; + + if (!swrm) + return o; + + mutex_lock(&swrm->pm_lock); + old = swrm->pm_state; + if (old == o) + swrm->pm_state = n; + mutex_unlock(&swrm->pm_lock); + + return old; +} + +static bool swrm_lock_sleep(struct swr_mstr_ctrl *swrm) +{ + enum swrm_pm_state os; + + /* + * swrm_{lock/unlock}_sleep will be called by swr irq handler + * and slave wake up requests.. + * + * If system didn't resume, we can simply return false so + * IRQ handler can return without handling IRQ. + */ + mutex_lock(&swrm->pm_lock); + if (swrm->wlock_holders++ == 0) { + dev_dbg(swrm->dev, "%s: holding wake lock\n", __func__); + pm_qos_update_request(&swrm->pm_qos_req, + msm_cpuidle_get_deep_idle_latency()); + pm_stay_awake(swrm->dev); + } + mutex_unlock(&swrm->pm_lock); + + if (!wait_event_timeout(swrm->pm_wq, + ((os = swrm_pm_cmpxchg(swrm, + SWRM_PM_SLEEPABLE, + SWRM_PM_AWAKE)) == + SWRM_PM_SLEEPABLE || + (os == SWRM_PM_AWAKE)), + msecs_to_jiffies( + SWRM_SYSTEM_RESUME_TIMEOUT_MS))) { + dev_err(swrm->dev, "%s: system didn't resume within %dms, s %d, w %d\n", + __func__, SWRM_SYSTEM_RESUME_TIMEOUT_MS, swrm->pm_state, + swrm->wlock_holders); + swrm_unlock_sleep(swrm); + return false; + } + wake_up_all(&swrm->pm_wq); + return true; +} + +static void swrm_unlock_sleep(struct swr_mstr_ctrl *swrm) +{ + mutex_lock(&swrm->pm_lock); + if (--swrm->wlock_holders == 0) { + dev_dbg(swrm->dev, "%s: releasing wake lock pm_state %d -> %d\n", + __func__, swrm->pm_state, SWRM_PM_SLEEPABLE); + /* + * if swrm_lock_sleep failed, pm_state would be still + * swrm_PM_ASLEEP, don't overwrite + */ + if (likely(swrm->pm_state == SWRM_PM_AWAKE)) + swrm->pm_state = SWRM_PM_SLEEPABLE; + pm_qos_update_request(&swrm->pm_qos_req, + PM_QOS_DEFAULT_VALUE); + pm_relax(swrm->dev); + } + mutex_unlock(&swrm->pm_lock); + wake_up_all(&swrm->pm_wq); +} + +#ifdef CONFIG_PM_SLEEP +static int swrm_suspend(struct device *dev) +{ + int ret = -EBUSY; + struct platform_device *pdev = to_platform_device(dev); + struct swr_mstr_ctrl *swrm = platform_get_drvdata(pdev); + + dev_dbg(dev, "%s: system suspend, state: %d\n", __func__, swrm->state); + + mutex_lock(&swrm->pm_lock); + + if (swrm->pm_state == SWRM_PM_SLEEPABLE) { + dev_dbg(swrm->dev, "%s: suspending system, state %d, wlock %d\n", + __func__, swrm->pm_state, + swrm->wlock_holders); + swrm->pm_state = SWRM_PM_ASLEEP; + } else if (swrm->pm_state == SWRM_PM_AWAKE) { + /* + * unlock to wait for pm_state == SWRM_PM_SLEEPABLE + * then set to SWRM_PM_ASLEEP + */ + dev_dbg(swrm->dev, "%s: waiting to suspend system, state %d, wlock %d\n", + __func__, swrm->pm_state, + swrm->wlock_holders); + mutex_unlock(&swrm->pm_lock); + if (!(wait_event_timeout(swrm->pm_wq, swrm_pm_cmpxchg( + swrm, SWRM_PM_SLEEPABLE, + SWRM_PM_ASLEEP) == + SWRM_PM_SLEEPABLE, + msecs_to_jiffies( + SWRM_SYS_SUSPEND_WAIT)))) { + dev_dbg(swrm->dev, "%s: suspend failed state %d, wlock %d\n", + __func__, swrm->pm_state, + swrm->wlock_holders); + return -EBUSY; + } else { + dev_dbg(swrm->dev, + "%s: done, state %d, wlock %d\n", + __func__, swrm->pm_state, + swrm->wlock_holders); + } + mutex_lock(&swrm->pm_lock); + } else if (swrm->pm_state == SWRM_PM_ASLEEP) { + dev_dbg(swrm->dev, "%s: system is already suspended, state %d, wlock %d\n", + __func__, swrm->pm_state, + swrm->wlock_holders); + } + + mutex_unlock(&swrm->pm_lock); + + if ((!pm_runtime_enabled(dev) || !pm_runtime_suspended(dev))) { + ret = swrm_runtime_suspend(dev); + if (!ret) { + /* + * Synchronize runtime-pm and system-pm states: + * At this point, we are already suspended. If + * runtime-pm still thinks its active, then + * make sure its status is in sync with HW + * status. The three below calls let the + * runtime-pm know that we are suspended + * already without re-invoking the suspend + * callback + */ + pm_runtime_disable(dev); + pm_runtime_set_suspended(dev); + pm_runtime_enable(dev); + } + } + if (ret == -EBUSY) { + /* + * There is a possibility that some audio stream is active + * during suspend. We dont want to return suspend failure in + * that case so that display and relevant components can still + * go to suspend. + * If there is some other error, then it should be passed-on + * to system level suspend + */ + ret = 0; + } + return ret; +} + +static int swrm_resume(struct device *dev) +{ + int ret = 0; + struct platform_device *pdev = to_platform_device(dev); + struct swr_mstr_ctrl *swrm = platform_get_drvdata(pdev); + + dev_dbg(dev, "%s: system resume, state: %d\n", __func__, swrm->state); + if (!pm_runtime_enabled(dev) || !pm_runtime_suspend(dev)) { + ret = swrm_runtime_resume(dev); + if (!ret) { + pm_runtime_mark_last_busy(dev); + pm_request_autosuspend(dev); + } + } + mutex_lock(&swrm->pm_lock); + if (swrm->pm_state == SWRM_PM_ASLEEP) { + dev_dbg(swrm->dev, + "%s: resuming system, state %d, wlock %d\n", + __func__, swrm->pm_state, + swrm->wlock_holders); + swrm->pm_state = SWRM_PM_SLEEPABLE; + } else { + dev_dbg(swrm->dev, "%s: system is already awake, state %d wlock %d\n", + __func__, swrm->pm_state, + swrm->wlock_holders); + } + mutex_unlock(&swrm->pm_lock); + wake_up_all(&swrm->pm_wq); + + return ret; +} +#endif /* CONFIG_PM_SLEEP */ + +static const struct dev_pm_ops swrm_dev_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS( + swrm_suspend, + swrm_resume + ) + SET_RUNTIME_PM_OPS( + swrm_runtime_suspend, + swrm_runtime_resume, + NULL + ) +}; + +static const struct of_device_id swrm_dt_match[] = { + { + .compatible = "qcom,swr-mstr", + }, + {} +}; + +static struct platform_driver swr_mstr_driver = { + .probe = swrm_probe, + .remove = swrm_remove, + .driver = { + .name = SWR_WCD_NAME, + .owner = THIS_MODULE, + .pm = &swrm_dev_pm_ops, + .of_match_table = swrm_dt_match, + }, +}; + +static int __init swrm_init(void) +{ + return platform_driver_register(&swr_mstr_driver); +} +module_init(swrm_init); + +static void __exit swrm_exit(void) +{ + platform_driver_unregister(&swr_mstr_driver); +} +module_exit(swrm_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("SoundWire Master Controller"); +MODULE_ALIAS("platform:swr-mstr"); diff --git a/audio-kernel/soc/swr-mstr-ctrl.h b/audio-kernel/soc/swr-mstr-ctrl.h new file mode 100644 index 000000000..049369abe --- /dev/null +++ b/audio-kernel/soc/swr-mstr-ctrl.h @@ -0,0 +1,175 @@ +/* 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 + * 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 _SWR_WCD_CTRL_H +#define _SWR_WCD_CTRL_H +#include +#include +#include +#include + +#define SWR_ROW_48 0 +#define SWR_ROW_50 1 +#define SWR_ROW_64 2 +#define SWR_MAX_COL 7 /* Cols = 16 */ +#define SWR_MIN_COL 0 /* Cols = 2 */ + +#define SWR_WCD_NAME "swr-wcd" + +#define SWR_MSTR_PORT_LEN 8 /* Number of master ports */ + +#define SWRM_VERSION_1_0 0x01010000 +#define SWRM_VERSION_1_2 0x01030000 +#define SWRM_VERSION_1_3 0x01040000 +#define SWRM_VERSION_1_5 0x01050000 + +#define SWR_MAX_CH_PER_PORT 8 + +#define SWR_MAX_SLAVE_DEVICES 11 + +enum { + SWR_MSTR_PAUSE, + SWR_MSTR_RESUME, + SWR_MSTR_UP, + SWR_MSTR_DOWN, + SWR_MSTR_SSR, +}; + +enum swrm_pm_state { + SWRM_PM_SLEEPABLE, + SWRM_PM_AWAKE, + SWRM_PM_ASLEEP, +}; + +enum { + SWR_IRQ_FREE, + SWR_IRQ_REGISTER, +}; + +enum { + SWR_DAC_PORT, + SWR_COMP_PORT, + SWR_BOOST_PORT, + SWR_VISENSE_PORT, +}; + +struct usecase { + u8 num_port; + u8 num_ch; + u32 chrate; +}; + +struct port_params { + u8 si; + u8 off1; + u8 off2; + u8 hstart;/* head start */ + u8 hstop; /* head stop */ + u8 wd_len;/* word length */ + u8 bp_mode; /* block pack mode */ + u8 bgp_ctrl;/* block group control */ + u8 lane_ctrl;/* lane to be used */ +}; + +struct swrm_mports { + struct list_head port_req_list; + bool port_en; + u8 ch_en; + u8 req_ch; + u8 ch_rate; + u8 offset1; + u8 offset2; + u8 sinterval; + u8 hstart; + u8 hstop; + u8 blk_grp_count; + u8 blk_pack_mode; + u8 word_length; + u8 lane_ctrl; +}; + +struct swrm_port_type { + u8 port_type; + u8 ch_mask; +}; + +struct swr_ctrl_platform_data { + void *handle; /* holds priv data */ + int (*read)(void *handle, int reg); + int (*write)(void *handle, int reg, int val); + int (*bulk_write)(void *handle, u32 *reg, u32 *val, size_t len); + int (*clk)(void *handle, bool enable); + int (*reg_irq)(void *handle, irqreturn_t(*irq_handler)(int irq, + void *data), void *swr_handle, int type); +}; + +struct swr_mstr_ctrl { + struct swr_master master; + struct device *dev; + struct resource *supplies; + struct clk *mclk; + int clk_ref_count; + struct completion clk_off_complete; + struct completion reset; + struct completion broadcast; + struct mutex clklock; + struct mutex iolock; + struct mutex devlock; + struct mutex mlock; + struct mutex reslock; + struct mutex pm_lock; + u32 swrm_base_reg; + char __iomem *swrm_dig_base; + u8 rcmd_id; + u8 wcmd_id; + u32 master_id; + void *handle; /* SWR Master handle from client for read and writes */ + int (*read)(void *handle, int reg); + int (*write)(void *handle, int reg, int val); + int (*bulk_write)(void *handle, u32 *reg, u32 *val, size_t len); + int (*clk)(void *handle, bool enable); + int (*reg_irq)(void *handle, irqreturn_t(*irq_handler)(int irq, + void *data), void *swr_handle, int type); + int irq; + int wake_irq; + int version; + int mclk_freq; + u32 num_dev; + int slave_status; + struct swrm_mports mport_cfg[SWR_MAX_MSTR_PORT_NUM]; + struct list_head port_req_list; + unsigned long port_req_pending; + int state; + struct platform_device *pdev; + int num_rx_chs; + u8 num_cfg_devs; + struct mutex force_down_lock; + int force_down_state; + struct notifier_block event_notifier; + struct work_struct dc_presence_work; + u8 num_ports; + struct swrm_port_type + port_mapping[SWR_MSTR_PORT_LEN][SWR_MAX_CH_PER_PORT]; + int swr_irq; + u32 clk_stop_mode0_supp; + struct work_struct wakeup_work; + u32 ipc_wakeup; + bool dev_up; + bool ipc_wakeup_triggered; + struct pm_qos_request pm_qos_req; + enum swrm_pm_state pm_state; + wait_queue_head_t pm_wq; + int wlock_holders; + u32 intr_mask; +}; + +#endif /* _SWR_WCD_CTRL_H */ diff --git a/audio-kernel/soc/swr-wcd-ctrl.c b/audio-kernel/soc/swr-wcd-ctrl.c new file mode 100644 index 000000000..91b48ce68 --- /dev/null +++ b/audio-kernel/soc/swr-wcd-ctrl.c @@ -0,0 +1,1957 @@ +/* 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 + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "swrm_registers.h" +#include "swr-wcd-ctrl.h" + +#define SWR_BROADCAST_CMD_ID 0x0F +#define SWR_AUTO_SUSPEND_DELAY 3 /* delay in sec */ +#define SWR_DEV_ID_MASK 0xFFFFFFFF +#define SWR_REG_VAL_PACK(data, dev, id, reg) \ + ((reg) | ((id) << 16) | ((dev) << 20) | ((data) << 24)) + +/* pm runtime auto suspend timer in msecs */ +static int auto_suspend_timer = SWR_AUTO_SUSPEND_DELAY * 1000; +module_param(auto_suspend_timer, int, 0664); +MODULE_PARM_DESC(auto_suspend_timer, "timer for auto suspend"); + +static u8 mstr_ports[] = {100, 101, 102, 103, 104, 105, 106, 107}; +static u8 mstr_port_type[] = {SWR_DAC_PORT, SWR_COMP_PORT, SWR_BOOST_PORT, + SWR_DAC_PORT, SWR_COMP_PORT, SWR_BOOST_PORT, + SWR_VISENSE_PORT, SWR_VISENSE_PORT}; + +struct usecase uc[] = { + {0, 0, 0}, /* UC0: no ports */ + {1, 1, 2400}, /* UC1: Spkr */ + {1, 4, 600}, /* UC2: Compander */ + {1, 2, 300}, /* UC3: Smart Boost */ + {1, 2, 1200}, /* UC4: VI Sense */ + {4, 9, 4500}, /* UC5: Spkr + Comp + SB + VI */ + {8, 18, 9000}, /* UC6: 2*(Spkr + Comp + SB + VI) */ + {2, 2, 4800}, /* UC7: 2*Spkr */ + {2, 5, 3000}, /* UC8: Spkr + Comp */ + {4, 10, 6000}, /* UC9: 2*(Spkr + Comp) */ + {3, 7, 3300}, /* UC10: Spkr + Comp + SB */ + {6, 14, 6600}, /* UC11: 2*(Spkr + Comp + SB) */ + {2, 3, 2700}, /* UC12: Spkr + SB */ + {4, 6, 5400}, /* UC13: 2*(Spkr + SB) */ + {3, 5, 3900}, /* UC14: Spkr + SB + VI */ + {6, 10, 7800}, /* UC15: 2*(Spkr + SB + VI) */ + {2, 3, 3600}, /* UC16: Spkr + VI */ + {4, 6, 7200}, /* UC17: 2*(Spkr + VI) */ + {3, 7, 4200}, /* UC18: Spkr + Comp + VI */ + {6, 14, 8400}, /* UC19: 2*(Spkr + Comp + VI) */ +}; +#define MAX_USECASE ARRAY_SIZE(uc) + +struct port_params pp[MAX_USECASE][SWR_MSTR_PORT_LEN] = { + /* UC 0 */ + { + {0, 0, 0}, + }, + /* UC 1 */ + { + {7, 1, 0}, + }, + /* UC 2 */ + { + {31, 2, 0}, + }, + /* UC 3 */ + { + {63, 12, 31}, + }, + /* UC 4 */ + { + {15, 7, 0}, + }, + /* UC 5 */ + { + {7, 1, 0}, + {31, 2, 0}, + {63, 12, 31}, + {15, 7, 0}, + }, + /* UC 6 */ + { + {7, 1, 0}, + {31, 2, 0}, + {63, 12, 31}, + {15, 7, 0}, + {7, 6, 0}, + {31, 18, 0}, + {63, 13, 31}, + {15, 10, 0}, + }, + /* UC 7 */ + { + {7, 1, 0}, + {7, 6, 0}, + + }, + /* UC 8 */ + { + {7, 1, 0}, + {31, 2, 0}, + }, + /* UC 9 */ + { + {7, 1, 0}, + {31, 2, 0}, + {7, 6, 0}, + {31, 18, 0}, + }, + /* UC 10 */ + { + {7, 1, 0}, + {31, 2, 0}, + {63, 12, 31}, + }, + /* UC 11 */ + { + {7, 1, 0}, + {31, 2, 0}, + {63, 12, 31}, + {7, 6, 0}, + {31, 18, 0}, + {63, 13, 31}, + }, + /* UC 12 */ + { + {7, 1, 0}, + {63, 12, 31}, + }, + /* UC 13 */ + { + {7, 1, 0}, + {63, 12, 31}, + {7, 6, 0}, + {63, 13, 31}, + }, + /* UC 14 */ + { + {7, 1, 0}, + {63, 12, 31}, + {15, 7, 0}, + }, + /* UC 15 */ + { + {7, 1, 0}, + {63, 12, 31}, + {15, 7, 0}, + {7, 6, 0}, + {63, 13, 31}, + {15, 10, 0}, + }, + /* UC 16 */ + { + {7, 1, 0}, + {15, 7, 0}, + }, + /* UC 17 */ + { + {7, 1, 0}, + {15, 7, 0}, + {7, 6, 0}, + {15, 10, 0}, + }, + /* UC 18 */ + { + {7, 1, 0}, + {31, 2, 0}, + {15, 7, 0}, + }, + /* UC 19 */ + { + {7, 1, 0}, + {31, 2, 0}, + {15, 7, 0}, + {7, 6, 0}, + {31, 18, 0}, + {15, 10, 0}, + }, +}; + +enum { + SWR_NOT_PRESENT, /* Device is detached/not present on the bus */ + SWR_ATTACHED_OK, /* Device is attached */ + SWR_ALERT, /* Device alters master for any interrupts */ + SWR_RESERVED, /* Reserved */ +}; + +#define SWRM_MAX_PORT_REG 40 +#define SWRM_MAX_INIT_REG 8 + +#define SWR_MSTR_MAX_REG_ADDR 0x1740 +#define SWR_MSTR_START_REG_ADDR 0x00 +#define SWR_MSTR_MAX_BUF_LEN 32 +#define BYTES_PER_LINE 12 +#define SWR_MSTR_RD_BUF_LEN 8 +#define SWR_MSTR_WR_BUF_LEN 32 + +static void swrm_copy_data_port_config(struct swr_master *master, + u8 inactive_bank); +static struct swr_mstr_ctrl *dbgswrm; +static struct dentry *debugfs_swrm_dent; +static struct dentry *debugfs_peek; +static struct dentry *debugfs_poke; +static struct dentry *debugfs_reg_dump; +static unsigned int read_data; + + +static bool swrm_is_msm_variant(int val) +{ + return (val == SWRM_VERSION_1_3); +} + +static int swrm_debug_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static int get_parameters(char *buf, u32 *param1, int num_of_par) +{ + char *token; + int base, cnt; + + token = strsep(&buf, " "); + for (cnt = 0; cnt < num_of_par; cnt++) { + if (token) { + if ((token[1] == 'x') || (token[1] == 'X')) + base = 16; + else + base = 10; + + if (kstrtou32(token, base, ¶m1[cnt]) != 0) + return -EINVAL; + + token = strsep(&buf, " "); + } else + return -EINVAL; + } + return 0; +} + +static ssize_t swrm_reg_show(char __user *ubuf, size_t count, + loff_t *ppos) +{ + int i, reg_val, len; + ssize_t total = 0; + char tmp_buf[SWR_MSTR_MAX_BUF_LEN]; + + if (!ubuf || !ppos) + return 0; + + for (i = (((int) *ppos / BYTES_PER_LINE) + SWR_MSTR_START_REG_ADDR); + i <= SWR_MSTR_MAX_REG_ADDR; i += 4) { + reg_val = dbgswrm->read(dbgswrm->handle, i); + len = snprintf(tmp_buf, 25, "0x%.3x: 0x%.2x\n", i, reg_val); + if ((total + len) >= count - 1) + break; + if (copy_to_user((ubuf + total), tmp_buf, len)) { + pr_err("%s: fail to copy reg dump\n", __func__); + total = -EFAULT; + goto copy_err; + } + *ppos += len; + total += len; + } + +copy_err: + return total; +} + +static ssize_t swrm_debug_read(struct file *file, char __user *ubuf, + size_t count, loff_t *ppos) +{ + char lbuf[SWR_MSTR_RD_BUF_LEN]; + char *access_str; + ssize_t ret_cnt; + + if (!count || !file || !ppos || !ubuf) + return -EINVAL; + + access_str = file->private_data; + if (*ppos < 0) + return -EINVAL; + + if (!strcmp(access_str, "swrm_peek")) { + snprintf(lbuf, sizeof(lbuf), "0x%x\n", read_data); + ret_cnt = simple_read_from_buffer(ubuf, count, ppos, lbuf, + strnlen(lbuf, 7)); + } else if (!strcmp(access_str, "swrm_reg_dump")) { + ret_cnt = swrm_reg_show(ubuf, count, ppos); + } else { + pr_err("%s: %s not permitted to read\n", __func__, access_str); + ret_cnt = -EPERM; + } + return ret_cnt; +} + +static ssize_t swrm_debug_write(struct file *filp, + const char __user *ubuf, size_t cnt, loff_t *ppos) +{ + char lbuf[SWR_MSTR_WR_BUF_LEN]; + int rc; + u32 param[5]; + char *access_str; + + if (!filp || !ppos || !ubuf) + return -EINVAL; + + access_str = filp->private_data; + if (cnt > sizeof(lbuf) - 1) + return -EINVAL; + + rc = copy_from_user(lbuf, ubuf, cnt); + if (rc) + return -EFAULT; + + lbuf[cnt] = '\0'; + if (!strcmp(access_str, "swrm_poke")) { + /* write */ + rc = get_parameters(lbuf, param, 2); + if ((param[0] <= SWR_MSTR_MAX_REG_ADDR) && + (param[1] <= 0xFFFFFFFF) && + (rc == 0)) + rc = dbgswrm->write(dbgswrm->handle, param[0], + param[1]); + else + rc = -EINVAL; + } else if (!strcmp(access_str, "swrm_peek")) { + /* read */ + rc = get_parameters(lbuf, param, 1); + if ((param[0] <= SWR_MSTR_MAX_REG_ADDR) && (rc == 0)) + read_data = dbgswrm->read(dbgswrm->handle, param[0]); + else + rc = -EINVAL; + } + if (rc == 0) + rc = cnt; + else + pr_err("%s: rc = %d\n", __func__, rc); + + return rc; +} + +static const struct file_operations swrm_debug_ops = { + .open = swrm_debug_open, + .write = swrm_debug_write, + .read = swrm_debug_read, +}; + +static int swrm_set_ch_map(struct swr_mstr_ctrl *swrm, void *data) +{ + struct swr_mstr_port *pinfo = (struct swr_mstr_port *)data; + + swrm->mstr_port = kzalloc(sizeof(struct swr_mstr_port), GFP_KERNEL); + if (swrm->mstr_port == NULL) + return -ENOMEM; + swrm->mstr_port->num_port = pinfo->num_port; + swrm->mstr_port->port = kzalloc((pinfo->num_port * sizeof(u8)), + GFP_KERNEL); + if (!swrm->mstr_port->port) { + kfree(swrm->mstr_port); + swrm->mstr_port = NULL; + return -ENOMEM; + } + memcpy(swrm->mstr_port->port, pinfo->port, pinfo->num_port); + return 0; +} + +static bool swrm_is_port_en(struct swr_master *mstr) +{ + return !!(mstr->num_port); +} + +static int swrm_clk_request(struct swr_mstr_ctrl *swrm, bool enable) +{ + if (!swrm->clk || !swrm->handle) + return -EINVAL; + + if (enable) { + swrm->clk_ref_count++; + if (swrm->clk_ref_count == 1) { + swrm->clk(swrm->handle, true); + swrm->state = SWR_MSTR_UP; + } + } else if (--swrm->clk_ref_count == 0) { + swrm->clk(swrm->handle, false); + swrm->state = SWR_MSTR_DOWN; + } else if (swrm->clk_ref_count < 0) { + pr_err("%s: swrm clk count mismatch\n", __func__); + swrm->clk_ref_count = 0; + } + return 0; +} + +static int swrm_get_port_config(struct swr_master *master) +{ + u32 ch_rate = 0; + u32 num_ch = 0; + int i, uc_idx; + u32 portcount = 0; + + for (i = 0; i < SWR_MSTR_PORT_LEN; i++) { + if (master->port[i].port_en) { + ch_rate += master->port[i].ch_rate; + num_ch += master->port[i].num_ch; + portcount++; + } + } + for (i = 0; i < ARRAY_SIZE(uc); i++) { + if ((uc[i].num_port == portcount) && + (uc[i].num_ch == num_ch) && + (uc[i].chrate == ch_rate)) { + uc_idx = i; + break; + } + } + + if (i >= ARRAY_SIZE(uc)) { + dev_err(&master->dev, + "%s: usecase port:%d, num_ch:%d, chrate:%d not found\n", + __func__, master->num_port, num_ch, ch_rate); + return -EINVAL; + } + for (i = 0; i < SWR_MSTR_PORT_LEN; i++) { + if (master->port[i].port_en) { + master->port[i].sinterval = pp[uc_idx][i].si; + master->port[i].offset1 = pp[uc_idx][i].off1; + master->port[i].offset2 = pp[uc_idx][i].off2; + } + } + return 0; +} + +static int swrm_get_master_port(u8 *mstr_port_id, u8 slv_port_id) +{ + int i; + + for (i = 0; i < SWR_MSTR_PORT_LEN; i++) { + if (mstr_ports[i] == slv_port_id) { + *mstr_port_id = i; + return 0; + } + } + return -EINVAL; +} + +static u32 swrm_get_packed_reg_val(u8 *cmd_id, u8 cmd_data, + u8 dev_addr, u16 reg_addr) +{ + u32 val; + u8 id = *cmd_id; + + if (id != SWR_BROADCAST_CMD_ID) { + if (id < 14) + id += 1; + else + id = 0; + *cmd_id = id; + } + val = SWR_REG_VAL_PACK(cmd_data, dev_addr, id, reg_addr); + + return val; +} + +static int swrm_cmd_fifo_rd_cmd(struct swr_mstr_ctrl *swrm, int *cmd_data, + u8 dev_addr, u8 cmd_id, u16 reg_addr, + u32 len) +{ + u32 val; + int ret = 0; + + val = swrm_get_packed_reg_val(&swrm->rcmd_id, len, dev_addr, reg_addr); + ret = swrm->write(swrm->handle, SWRM_CMD_FIFO_RD_CMD, val); + if (ret < 0) { + dev_err(swrm->dev, "%s: reg 0x%x write failed, err:%d\n", + __func__, val, ret); + goto err; + } + *cmd_data = swrm->read(swrm->handle, SWRM_CMD_FIFO_RD_FIFO_ADDR); + dev_dbg(swrm->dev, + "%s: reg: 0x%x, cmd_id: 0x%x, dev_id: 0x%x, cmd_data: 0x%x\n", + __func__, reg_addr, cmd_id, dev_addr, *cmd_data); +err: + return ret; +} + +static int swrm_cmd_fifo_wr_cmd(struct swr_mstr_ctrl *swrm, u8 cmd_data, + u8 dev_addr, u8 cmd_id, u16 reg_addr) +{ + u32 val; + int ret = 0; + + if (!cmd_id) + val = swrm_get_packed_reg_val(&swrm->wcmd_id, cmd_data, + dev_addr, reg_addr); + else + val = swrm_get_packed_reg_val(&cmd_id, cmd_data, + dev_addr, reg_addr); + + dev_dbg(swrm->dev, + "%s: reg: 0x%x, cmd_id: 0x%x, dev_id: 0x%x, cmd_data: 0x%x\n", + __func__, reg_addr, cmd_id, dev_addr, cmd_data); + ret = swrm->write(swrm->handle, SWRM_CMD_FIFO_WR_CMD, val); + if (ret < 0) { + dev_err(swrm->dev, "%s: reg 0x%x write failed, err:%d\n", + __func__, val, ret); + goto err; + } + if (cmd_id == 0xF) { + /* + * sleep for 10ms for MSM soundwire variant to allow broadcast + * command to complete. + */ + if (swrm_is_msm_variant(swrm->version)) + usleep_range(10000, 10100); + else + wait_for_completion_timeout(&swrm->broadcast, + (2 * HZ/10)); + } +err: + return ret; +} + +static int swrm_read(struct swr_master *master, u8 dev_num, u16 reg_addr, + void *buf, u32 len) +{ + struct swr_mstr_ctrl *swrm = swr_get_ctrl_data(master); + int ret = 0; + int val; + u8 *reg_val = (u8 *)buf; + + if (!swrm) { + dev_err(&master->dev, "%s: swrm is NULL\n", __func__); + return -EINVAL; + } + + if (dev_num) + ret = swrm_cmd_fifo_rd_cmd(swrm, &val, dev_num, 0, reg_addr, + len); + else + val = swrm->read(swrm->handle, reg_addr); + + if (!ret) + *reg_val = (u8)val; + + pm_runtime_mark_last_busy(&swrm->pdev->dev); + + return ret; +} + +static int swrm_write(struct swr_master *master, u8 dev_num, u16 reg_addr, + const void *buf) +{ + struct swr_mstr_ctrl *swrm = swr_get_ctrl_data(master); + int ret = 0; + u8 reg_val = *(u8 *)buf; + + if (!swrm) { + dev_err(&master->dev, "%s: swrm is NULL\n", __func__); + return -EINVAL; + } + + if (dev_num) + ret = swrm_cmd_fifo_wr_cmd(swrm, reg_val, dev_num, 0, reg_addr); + else + ret = swrm->write(swrm->handle, reg_addr, reg_val); + + pm_runtime_mark_last_busy(&swrm->pdev->dev); + + return ret; +} + +static int swrm_bulk_write(struct swr_master *master, u8 dev_num, void *reg, + const void *buf, size_t len) +{ + struct swr_mstr_ctrl *swrm = swr_get_ctrl_data(master); + int ret = 0; + int i; + u32 *val; + u32 *swr_fifo_reg; + + if (!swrm || !swrm->handle) { + dev_err(&master->dev, "%s: swrm is NULL\n", __func__); + return -EINVAL; + } + if (len <= 0) + return -EINVAL; + + if (dev_num) { + swr_fifo_reg = kcalloc(len, sizeof(u32), GFP_KERNEL); + if (!swr_fifo_reg) { + ret = -ENOMEM; + goto err; + } + val = kcalloc(len, sizeof(u32), GFP_KERNEL); + if (!val) { + ret = -ENOMEM; + goto mem_fail; + } + + for (i = 0; i < len; i++) { + val[i] = swrm_get_packed_reg_val(&swrm->wcmd_id, + ((u8 *)buf)[i], + dev_num, + ((u16 *)reg)[i]); + swr_fifo_reg[i] = SWRM_CMD_FIFO_WR_CMD; + } + ret = swrm->bulk_write(swrm->handle, swr_fifo_reg, val, len); + if (ret) { + dev_err(&master->dev, "%s: bulk write failed\n", + __func__); + ret = -EINVAL; + } + } else { + dev_err(&master->dev, + "%s: No support of Bulk write for master regs\n", + __func__); + ret = -EINVAL; + goto err; + } + kfree(val); +mem_fail: + kfree(swr_fifo_reg); +err: + pm_runtime_mark_last_busy(&swrm->pdev->dev); + return ret; +} + +static u8 get_inactive_bank_num(struct swr_mstr_ctrl *swrm) +{ + return (swrm->read(swrm->handle, SWRM_MCP_STATUS) & + SWRM_MCP_STATUS_BANK_NUM_MASK) ? 0 : 1; +} + +static void enable_bank_switch(struct swr_mstr_ctrl *swrm, u8 bank, + u8 row, u8 col) +{ + /* apply div2 setting for inactive bank before bank switch */ + swrm_cmd_fifo_wr_cmd(swrm, 0x01, 0xF, 0x00, + SWRS_SCP_HOST_CLK_DIV2_CTL_BANK(bank)); + + swrm_cmd_fifo_wr_cmd(swrm, ((row << 3) | col), 0xF, 0xF, + SWRS_SCP_FRAME_CTRL_BANK(bank)); +} + +static struct swr_port_info *swrm_get_port(struct swr_master *master, + u8 port_id) +{ + int i; + struct swr_port_info *port = NULL; + + for (i = 0; i < SWR_MSTR_PORT_LEN; i++) { + port = &master->port[i]; + if (port->slave_port_id == port_id) { + dev_dbg(&master->dev, "%s: port_id: %d, index: %d\n", + __func__, port_id, i); + return port; + } + } + + return NULL; +} + +static struct swr_port_info *swrm_get_avail_port(struct swr_master *master) +{ + int i; + struct swr_port_info *port = NULL; + + for (i = 0; i < SWR_MSTR_PORT_LEN; i++) { + port = &master->port[i]; + if (port->port_en) + continue; + + dev_dbg(&master->dev, "%s: port_id: %d, index: %d\n", + __func__, port->slave_port_id, i); + return port; + } + + return NULL; +} + +static struct swr_port_info *swrm_get_enabled_port(struct swr_master *master, + u8 port_id) +{ + int i; + struct swr_port_info *port = NULL; + + for (i = 0; i < SWR_MSTR_PORT_LEN; i++) { + port = &master->port[i]; + if ((port->slave_port_id == port_id) && (port->port_en == true)) + break; + } + if (i == SWR_MSTR_PORT_LEN) + port = NULL; + return port; +} + +static bool swrm_remove_from_group(struct swr_master *master) +{ + struct swr_device *swr_dev; + struct swr_mstr_ctrl *swrm = swr_get_ctrl_data(master); + bool is_removed = false; + + if (!swrm) + goto end; + + mutex_lock(&swrm->mlock); + if ((swrm->num_rx_chs > 1) && + (swrm->num_rx_chs == swrm->num_cfg_devs)) { + list_for_each_entry(swr_dev, &master->devices, + dev_list) { + swr_dev->group_id = SWR_GROUP_NONE; + master->gr_sid = 0; + } + is_removed = true; + } + mutex_unlock(&swrm->mlock); + +end: + return is_removed; +} + +static void swrm_cleanup_disabled_data_ports(struct swr_master *master, + u8 bank) +{ + u32 value; + struct swr_port_info *port; + int i; + int port_type; + struct swrm_mports *mport, *mport_next = NULL; + int port_disable_cnt = 0; + struct swr_mstr_ctrl *swrm = swr_get_ctrl_data(master); + + if (!swrm) { + pr_err("%s: swrm is null\n", __func__); + return; + } + + dev_dbg(swrm->dev, "%s: master num_port: %d\n", __func__, + master->num_port); + + mport = list_first_entry_or_null(&swrm->mport_list, + struct swrm_mports, + list); + if (!mport) { + dev_err(swrm->dev, "%s: list is empty\n", __func__); + return; + } + + for (i = 0; i < master->num_port; i++) { + port = swrm_get_port(master, mstr_ports[mport->id]); + if (!port || port->ch_en) + goto inc_loop; + + port_disable_cnt++; + port_type = mstr_port_type[mport->id]; + value = ((port->ch_en) + << SWRM_DP_PORT_CTRL_EN_CHAN_SHFT); + value |= ((port->offset2) + << SWRM_DP_PORT_CTRL_OFFSET2_SHFT); + value |= ((port->offset1) + << SWRM_DP_PORT_CTRL_OFFSET1_SHFT); + value |= port->sinterval; + + swrm->write(swrm->handle, + SWRM_DP_PORT_CTRL_BANK((mport->id+1), bank), + value); + swrm_cmd_fifo_wr_cmd(swrm, 0x00, port->dev_num, 0x00, + SWRS_DP_CHANNEL_ENABLE_BANK(port_type, bank)); + + dev_dbg(swrm->dev, "%s: mport :%d, reg: 0x%x, val: 0x%x\n", + __func__, mport->id, + (SWRM_DP_PORT_CTRL_BANK((mport->id+1), bank)), value); + +inc_loop: + mport_next = list_next_entry(mport, list); + if (port && !port->ch_en) { + list_del(&mport->list); + kfree(mport); + } + if (!mport_next) { + dev_err(swrm->dev, "%s: end of list\n", __func__); + break; + } + mport = mport_next; + } + master->num_port -= port_disable_cnt; + + dev_dbg(swrm->dev, "%s:disable ports: %d, active ports (rem): %d\n", + __func__, port_disable_cnt, master->num_port); +} + +static int swrm_slvdev_datapath_control(struct swr_master *master, + bool enable) +{ + u8 bank; + u32 value, n_col; + struct swr_mstr_ctrl *swrm = swr_get_ctrl_data(master); + int mask = (SWRM_MCP_FRAME_CTRL_BANK_ROW_CTRL_BMSK | + SWRM_MCP_FRAME_CTRL_BANK_COL_CTRL_BMSK | + SWRM_MCP_FRAME_CTRL_BANK_SSP_PERIOD_BMSK); + u8 inactive_bank; + + if (!swrm) { + pr_err("%s: swrm is null\n", __func__); + return 0; + } + + bank = get_inactive_bank_num(swrm); + + dev_dbg(swrm->dev, "%s: enable: %d, cfg_devs: %d\n", + __func__, enable, swrm->num_cfg_devs); + + if (enable) { + /* set Row = 48 and col = 16 */ + n_col = SWR_MAX_COL; + } else { + /* + * Do not change to 48x2 if number of channels configured + * as stereo and if disable datapath is called for the + * first slave device + */ + if (swrm->num_cfg_devs > 0) + n_col = SWR_MAX_COL; + else + n_col = SWR_MIN_COL; + + /* + * All ports are already disabled, no need to perform + * bank-switch and copy operation. This case can arise + * when speaker channels are enabled in stereo mode with + * BROADCAST and disabled in GROUP_NONE + */ + if (master->num_port == 0) + return 0; + } + + value = swrm->read(swrm->handle, SWRM_MCP_FRAME_CTRL_BANK_ADDR(bank)); + value &= (~mask); + value |= ((0 << SWRM_MCP_FRAME_CTRL_BANK_ROW_CTRL_SHFT) | + (n_col << SWRM_MCP_FRAME_CTRL_BANK_COL_CTRL_SHFT) | + (0 << SWRM_MCP_FRAME_CTRL_BANK_SSP_PERIOD_SHFT)); + swrm->write(swrm->handle, SWRM_MCP_FRAME_CTRL_BANK_ADDR(bank), value); + + dev_dbg(swrm->dev, "%s: regaddr: 0x%x, value: 0x%x\n", __func__, + SWRM_MCP_FRAME_CTRL_BANK_ADDR(bank), value); + + enable_bank_switch(swrm, bank, SWR_MAX_ROW, n_col); + + inactive_bank = bank ? 0 : 1; + if (enable) + swrm_copy_data_port_config(master, inactive_bank); + else + swrm_cleanup_disabled_data_ports(master, inactive_bank); + + if (!swrm_is_port_en(master)) { + dev_dbg(&master->dev, "%s: pm_runtime auto suspend triggered\n", + __func__); + pm_runtime_mark_last_busy(&swrm->pdev->dev); + pm_runtime_put_autosuspend(&swrm->pdev->dev); + } + return 0; +} + +static void swrm_apply_port_config(struct swr_master *master) +{ + u8 bank; + struct swr_mstr_ctrl *swrm = swr_get_ctrl_data(master); + + if (!swrm) { + pr_err("%s: Invalid handle to swr controller\n", + __func__); + return; + } + + bank = get_inactive_bank_num(swrm); + dev_dbg(swrm->dev, "%s: enter bank: %d master_ports: %d\n", + __func__, bank, master->num_port); + + swrm_copy_data_port_config(master, bank); +} + +static void swrm_copy_data_port_config(struct swr_master *master, u8 bank) +{ + u32 value; + struct swr_port_info *port; + int i; + int port_type; + struct swrm_mports *mport; + u32 reg[SWRM_MAX_PORT_REG]; + u32 val[SWRM_MAX_PORT_REG]; + int len = 0; + struct swr_mstr_ctrl *swrm = swr_get_ctrl_data(master); + + if (!swrm) { + pr_err("%s: swrm is null\n", __func__); + return; + } + + dev_dbg(swrm->dev, "%s: master num_port: %d\n", __func__, + master->num_port); + + mport = list_first_entry_or_null(&swrm->mport_list, + struct swrm_mports, + list); + if (!mport) { + dev_err(swrm->dev, "%s: list is empty\n", __func__); + return; + } + for (i = 0; i < master->num_port; i++) { + + port = swrm_get_enabled_port(master, mstr_ports[mport->id]); + if (!port) + continue; + port_type = mstr_port_type[mport->id]; + if (!port->dev_num || (port->dev_num > master->num_dev)) { + dev_dbg(swrm->dev, "%s: invalid device id = %d\n", + __func__, port->dev_num); + continue; + } + value = ((port->ch_en) + << SWRM_DP_PORT_CTRL_EN_CHAN_SHFT); + value |= ((port->offset2) + << SWRM_DP_PORT_CTRL_OFFSET2_SHFT); + value |= ((port->offset1) + << SWRM_DP_PORT_CTRL_OFFSET1_SHFT); + value |= port->sinterval; + + reg[len] = SWRM_DP_PORT_CTRL_BANK((mport->id+1), bank); + val[len++] = value; + + dev_dbg(swrm->dev, "%s: mport :%d, reg: 0x%x, val: 0x%x\n", + __func__, mport->id, + (SWRM_DP_PORT_CTRL_BANK((mport->id+1), bank)), value); + + reg[len] = SWRM_CMD_FIFO_WR_CMD; + val[len++] = SWR_REG_VAL_PACK(port->ch_en, port->dev_num, 0x00, + SWRS_DP_CHANNEL_ENABLE_BANK(port_type, bank)); + + reg[len] = SWRM_CMD_FIFO_WR_CMD; + val[len++] = SWR_REG_VAL_PACK(port->sinterval, + port->dev_num, 0x00, + SWRS_DP_SAMPLE_CONTROL_1_BANK(port_type, bank)); + + reg[len] = SWRM_CMD_FIFO_WR_CMD; + val[len++] = SWR_REG_VAL_PACK(port->offset1, + port->dev_num, 0x00, + SWRS_DP_OFFSET_CONTROL_1_BANK(port_type, bank)); + + if (port_type != 0) { + reg[len] = SWRM_CMD_FIFO_WR_CMD; + val[len++] = SWR_REG_VAL_PACK(port->offset2, + port->dev_num, 0x00, + SWRS_DP_OFFSET_CONTROL_2_BANK(port_type, + bank)); + } + mport = list_next_entry(mport, list); + if (!mport) { + dev_err(swrm->dev, "%s: end of list\n", __func__); + break; + } + } + swrm->bulk_write(swrm->handle, reg, val, len); +} + +static int swrm_connect_port(struct swr_master *master, + struct swr_params *portinfo) +{ + int i; + struct swr_port_info *port; + int ret = 0; + struct swr_mstr_ctrl *swrm = swr_get_ctrl_data(master); + struct swrm_mports *mport; + struct list_head *ptr, *next; + + dev_dbg(&master->dev, "%s: enter\n", __func__); + if (!portinfo) + return -EINVAL; + + if (!swrm) { + dev_err(&master->dev, + "%s: Invalid handle to swr controller\n", + __func__); + return -EINVAL; + } + + mutex_lock(&swrm->mlock); + if (!swrm_is_port_en(master)) + pm_runtime_get_sync(&swrm->pdev->dev); + + for (i = 0; i < portinfo->num_port; i++) { + mport = kzalloc(sizeof(struct swrm_mports), GFP_KERNEL); + if (!mport) { + ret = -ENOMEM; + goto mem_fail; + } + ret = swrm_get_master_port(&mport->id, + portinfo->port_id[i]); + if (ret < 0) { + dev_err(&master->dev, + "%s: mstr portid for slv port %d not found\n", + __func__, portinfo->port_id[i]); + goto port_fail; + } + port = swrm_get_avail_port(master); + if (!port) { + dev_err(&master->dev, + "%s: avail ports not found!\n", __func__); + goto port_fail; + } + list_add(&mport->list, &swrm->mport_list); + port->dev_num = portinfo->dev_num; + port->slave_port_id = portinfo->port_id[i]; + port->num_ch = portinfo->num_ch[i]; + port->ch_rate = portinfo->ch_rate[i]; + port->ch_en = portinfo->ch_en[i]; + port->port_en = true; + dev_dbg(&master->dev, + "%s: mstr port %d, slv port %d ch_rate %d num_ch %d\n", + __func__, mport->id, port->slave_port_id, port->ch_rate, + port->num_ch); + } + master->num_port += portinfo->num_port; + if (master->num_port >= SWR_MSTR_PORT_LEN) + master->num_port = SWR_MSTR_PORT_LEN; + + swrm_get_port_config(master); + swr_port_response(master, portinfo->tid); + swrm->num_cfg_devs += 1; + dev_dbg(&master->dev, "%s: cfg_devs: %d, rx_chs: %d\n", + __func__, swrm->num_cfg_devs, swrm->num_rx_chs); + if (swrm->num_rx_chs > 1) { + if (swrm->num_rx_chs == swrm->num_cfg_devs) + swrm_apply_port_config(master); + } else { + swrm_apply_port_config(master); + } + mutex_unlock(&swrm->mlock); + return 0; + +port_fail: + kfree(mport); +mem_fail: + list_for_each_safe(ptr, next, &swrm->mport_list) { + mport = list_entry(ptr, struct swrm_mports, list); + for (i = 0; i < portinfo->num_port; i++) { + if (portinfo->port_id[i] == mstr_ports[mport->id]) { + port = swrm_get_port(master, + portinfo->port_id[i]); + if (port) + port->ch_en = false; + list_del(&mport->list); + kfree(mport); + break; + } + } + } + mutex_unlock(&swrm->mlock); + return ret; +} + +static int swrm_disconnect_port(struct swr_master *master, + struct swr_params *portinfo) +{ + int i; + struct swr_port_info *port; + u8 bank; + u32 value; + int ret = 0; + u8 mport_id = 0; + int port_type = 0; + struct swr_mstr_ctrl *swrm = swr_get_ctrl_data(master); + + if (!swrm) { + dev_err(&master->dev, + "%s: Invalid handle to swr controller\n", + __func__); + return -EINVAL; + } + + if (!portinfo) { + dev_err(&master->dev, "%s: portinfo is NULL\n", __func__); + return -EINVAL; + } + mutex_lock(&swrm->mlock); + bank = get_inactive_bank_num(swrm); + for (i = 0; i < portinfo->num_port; i++) { + ret = swrm_get_master_port(&mport_id, + portinfo->port_id[i]); + if (ret < 0) { + dev_err(&master->dev, + "%s: mstr portid for slv port %d not found\n", + __func__, portinfo->port_id[i]); + mutex_unlock(&swrm->mlock); + return -EINVAL; + } + port = swrm_get_enabled_port(master, portinfo->port_id[i]); + if (!port) { + dev_dbg(&master->dev, "%s: port %d already disabled\n", + __func__, portinfo->port_id[i]); + continue; + } + port_type = mstr_port_type[mport_id]; + port->dev_num = portinfo->dev_num; + port->port_en = false; + port->ch_en = 0; + value = port->ch_en << SWRM_DP_PORT_CTRL_EN_CHAN_SHFT; + value |= (port->offset2 << SWRM_DP_PORT_CTRL_OFFSET2_SHFT); + value |= (port->offset1 << SWRM_DP_PORT_CTRL_OFFSET1_SHFT); + value |= port->sinterval; + + + swrm->write(swrm->handle, + SWRM_DP_PORT_CTRL_BANK((mport_id+1), bank), + value); + swrm_cmd_fifo_wr_cmd(swrm, 0x00, port->dev_num, 0x00, + SWRS_DP_CHANNEL_ENABLE_BANK(port_type, bank)); + } + + swr_port_response(master, portinfo->tid); + swrm->num_cfg_devs -= 1; + dev_dbg(&master->dev, "%s: cfg_devs: %d, rx_chs: %d, active ports: %d\n", + __func__, swrm->num_cfg_devs, swrm->num_rx_chs, + master->num_port); + mutex_unlock(&swrm->mlock); + + return 0; +} + +static int swrm_check_slave_change_status(struct swr_mstr_ctrl *swrm, + int status, u8 *devnum) +{ + int i; + int new_sts = status; + int ret = SWR_NOT_PRESENT; + + if (status != swrm->slave_status) { + for (i = 0; i < (swrm->master.num_dev + 1); i++) { + if ((status & SWRM_MCP_SLV_STATUS_MASK) != + (swrm->slave_status & SWRM_MCP_SLV_STATUS_MASK)) { + ret = (status & SWRM_MCP_SLV_STATUS_MASK); + *devnum = i; + break; + } + status >>= 2; + swrm->slave_status >>= 2; + } + swrm->slave_status = new_sts; + } + return ret; +} + +static irqreturn_t swr_mstr_interrupt(int irq, void *dev) +{ + struct swr_mstr_ctrl *swrm = dev; + u32 value, intr_sts; + int status, chg_sts, i; + u8 devnum = 0; + int ret = IRQ_HANDLED; + + mutex_lock(&swrm->reslock); + swrm_clk_request(swrm, true); + mutex_unlock(&swrm->reslock); + + intr_sts = swrm->read(swrm->handle, SWRM_INTERRUPT_STATUS); + intr_sts &= SWRM_INTERRUPT_STATUS_RMSK; + for (i = 0; i < SWRM_INTERRUPT_MAX; i++) { + value = intr_sts & (1 << i); + if (!value) + continue; + + swrm->write(swrm->handle, SWRM_INTERRUPT_CLEAR, value); + switch (value) { + case SWRM_INTERRUPT_STATUS_SLAVE_PEND_IRQ: + dev_dbg(swrm->dev, "SWR slave pend irq\n"); + break; + case SWRM_INTERRUPT_STATUS_NEW_SLAVE_ATTACHED: + dev_dbg(swrm->dev, "SWR new slave attached\n"); + break; + case SWRM_INTERRUPT_STATUS_CHANGE_ENUM_SLAVE_STATUS: + status = swrm->read(swrm->handle, SWRM_MCP_SLV_STATUS); + if (status == swrm->slave_status) { + dev_dbg(swrm->dev, + "%s: No change in slave status: %d\n", + __func__, status); + break; + } + chg_sts = swrm_check_slave_change_status(swrm, status, + &devnum); + switch (chg_sts) { + case SWR_NOT_PRESENT: + dev_dbg(swrm->dev, "device %d got detached\n", + devnum); + break; + case SWR_ATTACHED_OK: + dev_dbg(swrm->dev, "device %d got attached\n", + devnum); + break; + case SWR_ALERT: + dev_dbg(swrm->dev, + "device %d has pending interrupt\n", + devnum); + break; + } + break; + case SWRM_INTERRUPT_STATUS_MASTER_CLASH_DET: + dev_err_ratelimited(swrm->dev, "SWR bus clash detected\n"); + break; + case SWRM_INTERRUPT_STATUS_RD_FIFO_OVERFLOW: + dev_dbg(swrm->dev, "SWR read FIFO overflow\n"); + break; + case SWRM_INTERRUPT_STATUS_RD_FIFO_UNDERFLOW: + dev_dbg(swrm->dev, "SWR read FIFO underflow\n"); + break; + case SWRM_INTERRUPT_STATUS_WR_CMD_FIFO_OVERFLOW: + dev_dbg(swrm->dev, "SWR write FIFO overflow\n"); + break; + case SWRM_INTERRUPT_STATUS_CMD_ERROR: + value = swrm->read(swrm->handle, SWRM_CMD_FIFO_STATUS); + dev_err_ratelimited(swrm->dev, + "SWR CMD error, fifo status 0x%x, flushing fifo\n", + value); + swrm->write(swrm->handle, SWRM_CMD_FIFO_CMD, 0x1); + break; + case SWRM_INTERRUPT_STATUS_DOUT_PORT_COLLISION: + dev_dbg(swrm->dev, "SWR Port collision detected\n"); + break; + case SWRM_INTERRUPT_STATUS_READ_EN_RD_VALID_MISMATCH: + dev_dbg(swrm->dev, "SWR read enable valid mismatch\n"); + break; + case SWRM_INTERRUPT_STATUS_SPECIAL_CMD_ID_FINISHED: + complete(&swrm->broadcast); + dev_dbg(swrm->dev, "SWR cmd id finished\n"); + break; + case SWRM_INTERRUPT_STATUS_NEW_SLAVE_AUTO_ENUM_FINISHED: + break; + case SWRM_INTERRUPT_STATUS_AUTO_ENUM_FAILED: + break; + case SWRM_INTERRUPT_STATUS_AUTO_ENUM_TABLE_IS_FULL: + break; + case SWRM_INTERRUPT_STATUS_BUS_RESET_FINISHED: + complete(&swrm->reset); + break; + case SWRM_INTERRUPT_STATUS_CLK_STOP_FINISHED: + break; + default: + dev_err_ratelimited(swrm->dev, "SWR unknown interrupt\n"); + ret = IRQ_NONE; + break; + } + } + + mutex_lock(&swrm->reslock); + swrm_clk_request(swrm, false); + mutex_unlock(&swrm->reslock); + return ret; +} + +static int swrm_get_device_status(struct swr_mstr_ctrl *swrm, u8 devnum) +{ + u32 val; + + swrm->slave_status = swrm->read(swrm->handle, SWRM_MCP_SLV_STATUS); + val = (swrm->slave_status >> (devnum * 2)); + val &= SWRM_MCP_SLV_STATUS_MASK; + return val; +} + +static int swrm_get_logical_dev_num(struct swr_master *mstr, u64 dev_id, + u8 *dev_num) +{ + int i; + u64 id = 0; + int ret = -EINVAL; + struct swr_mstr_ctrl *swrm = swr_get_ctrl_data(mstr); + struct swr_device *swr_dev; + u32 num_dev = 0; + + if (!swrm) { + pr_err("%s: Invalid handle to swr controller\n", + __func__); + return ret; + } + if (swrm->num_dev) + num_dev = swrm->num_dev; + else + num_dev = mstr->num_dev; + + pm_runtime_get_sync(&swrm->pdev->dev); + for (i = 1; i < (num_dev + 1); i++) { + id = ((u64)(swrm->read(swrm->handle, + SWRM_ENUMERATOR_SLAVE_DEV_ID_2(i))) << 32); + id |= swrm->read(swrm->handle, + SWRM_ENUMERATOR_SLAVE_DEV_ID_1(i)); + /* + * As pm_runtime_get_sync() brings all slaves out of reset + * update logical device number for all slaves. + */ + list_for_each_entry(swr_dev, &mstr->devices, dev_list) { + if (swr_dev->addr == (id & SWR_DEV_ID_MASK)) { + u32 status = swrm_get_device_status(swrm, i); + + if ((status == 0x01) || (status == 0x02)) { + swr_dev->dev_num = i; + if ((id & SWR_DEV_ID_MASK) == dev_id) { + *dev_num = i; + ret = 0; + } + dev_dbg(swrm->dev, "%s: devnum %d is assigned for dev addr %lx\n", + __func__, i, swr_dev->addr); + } + } + } + } + if (ret) + dev_err(swrm->dev, "%s: device 0x%llx is not ready\n", + __func__, dev_id); + + pm_runtime_mark_last_busy(&swrm->pdev->dev); + pm_runtime_put_autosuspend(&swrm->pdev->dev); + return ret; +} +static int swrm_master_init(struct swr_mstr_ctrl *swrm) +{ + int ret = 0; + u32 val; + u8 row_ctrl = SWR_MAX_ROW; + u8 col_ctrl = SWR_MIN_COL; + u8 ssp_period = 1; + u8 retry_cmd_num = 3; + u32 reg[SWRM_MAX_INIT_REG]; + u32 value[SWRM_MAX_INIT_REG]; + int len = 0; + + /* Clear Rows and Cols */ + val = ((row_ctrl << SWRM_MCP_FRAME_CTRL_BANK_ROW_CTRL_SHFT) | + (col_ctrl << SWRM_MCP_FRAME_CTRL_BANK_COL_CTRL_SHFT) | + (ssp_period << SWRM_MCP_FRAME_CTRL_BANK_SSP_PERIOD_SHFT)); + + reg[len] = SWRM_MCP_FRAME_CTRL_BANK_ADDR(0); + value[len++] = val; + + /* Set Auto enumeration flag */ + reg[len] = SWRM_ENUMERATOR_CFG_ADDR; + value[len++] = 1; + + /* Mask soundwire interrupts */ + reg[len] = SWRM_INTERRUPT_MASK_ADDR; + value[len++] = 0x1FFFD; + + /* Configure No pings */ + val = swrm->read(swrm->handle, SWRM_MCP_CFG_ADDR); + val &= ~SWRM_MCP_CFG_MAX_NUM_OF_CMD_NO_PINGS_BMSK; + val |= (0x1f << SWRM_MCP_CFG_MAX_NUM_OF_CMD_NO_PINGS_SHFT); + reg[len] = SWRM_MCP_CFG_ADDR; + value[len++] = val; + + /* Configure number of retries of a read/write cmd */ + val = (retry_cmd_num << SWRM_CMD_FIFO_CFG_NUM_OF_CMD_RETRY_SHFT); + reg[len] = SWRM_CMD_FIFO_CFG_ADDR; + value[len++] = val; + + /* Set IRQ to PULSE */ + reg[len] = SWRM_COMP_CFG_ADDR; + value[len++] = 0x02; + + reg[len] = SWRM_COMP_CFG_ADDR; + value[len++] = 0x03; + + reg[len] = SWRM_INTERRUPT_CLEAR; + value[len++] = 0x08; + + swrm->bulk_write(swrm->handle, reg, value, len); + + return ret; +} + +static int swrm_event_notify(struct notifier_block *self, + unsigned long action, void *data) +{ + struct swr_mstr_ctrl *swrm = container_of(self, struct swr_mstr_ctrl, + event_notifier); + if (!swrm || !swrm->pdev) { + pr_err("%s: swrm or pdev is NULL\n", __func__); + return -EINVAL; + } + if (action != MSM_AUD_DC_EVENT) { + dev_err(&swrm->pdev->dev, "%s: invalid event type: %lu\n", __func__, action); + return -EINVAL; + } + + schedule_work(&(swrm->dc_presence_work)); + + return 0; +} + +static void swrm_notify_work_fn(struct work_struct *work) +{ + struct swr_mstr_ctrl *swrm = container_of(work, struct swr_mstr_ctrl, + dc_presence_work); + swrm_wcd_notify(swrm->pdev, SWR_DEVICE_DOWN, NULL); +} + +static int swrm_probe(struct platform_device *pdev) +{ + struct swr_mstr_ctrl *swrm; + struct swr_ctrl_platform_data *pdata; + int ret; + + /* Allocate soundwire master driver structure */ + swrm = kzalloc(sizeof(struct swr_mstr_ctrl), GFP_KERNEL); + if (!swrm) { + ret = -ENOMEM; + goto err_memory_fail; + } + swrm->dev = &pdev->dev; + swrm->pdev = pdev; + platform_set_drvdata(pdev, swrm); + swr_set_ctrl_data(&swrm->master, swrm); + pdata = dev_get_platdata(&pdev->dev); + if (!pdata) { + dev_err(&pdev->dev, "%s: pdata from parent is NULL\n", + __func__); + ret = -EINVAL; + goto err_pdata_fail; + } + swrm->handle = (void *)pdata->handle; + if (!swrm->handle) { + dev_err(&pdev->dev, "%s: swrm->handle is NULL\n", + __func__); + ret = -EINVAL; + goto err_pdata_fail; + } + swrm->read = pdata->read; + if (!swrm->read) { + dev_err(&pdev->dev, "%s: swrm->read is NULL\n", + __func__); + ret = -EINVAL; + goto err_pdata_fail; + } + swrm->write = pdata->write; + if (!swrm->write) { + dev_err(&pdev->dev, "%s: swrm->write is NULL\n", + __func__); + ret = -EINVAL; + goto err_pdata_fail; + } + swrm->bulk_write = pdata->bulk_write; + if (!swrm->bulk_write) { + dev_err(&pdev->dev, "%s: swrm->bulk_write is NULL\n", + __func__); + ret = -EINVAL; + goto err_pdata_fail; + } + swrm->clk = pdata->clk; + if (!swrm->clk) { + dev_err(&pdev->dev, "%s: swrm->clk is NULL\n", + __func__); + ret = -EINVAL; + goto err_pdata_fail; + } + swrm->reg_irq = pdata->reg_irq; + if (!swrm->reg_irq) { + dev_err(&pdev->dev, "%s: swrm->reg_irq is NULL\n", + __func__); + ret = -EINVAL; + goto err_pdata_fail; + } + swrm->master.read = swrm_read; + swrm->master.write = swrm_write; + swrm->master.bulk_write = swrm_bulk_write; + swrm->master.get_logical_dev_num = swrm_get_logical_dev_num; + swrm->master.connect_port = swrm_connect_port; + swrm->master.disconnect_port = swrm_disconnect_port; + swrm->master.slvdev_datapath_control = swrm_slvdev_datapath_control; + swrm->master.remove_from_group = swrm_remove_from_group; + swrm->master.dev.parent = &pdev->dev; + swrm->master.dev.of_node = pdev->dev.of_node; + swrm->master.num_port = 0; + swrm->num_enum_slaves = 0; + swrm->rcmd_id = 0; + swrm->wcmd_id = 0; + swrm->slave_status = 0; + swrm->num_rx_chs = 0; + swrm->clk_ref_count = 0; + swrm->state = SWR_MSTR_RESUME; + init_completion(&swrm->reset); + init_completion(&swrm->broadcast); + mutex_init(&swrm->mlock); + INIT_LIST_HEAD(&swrm->mport_list); + mutex_init(&swrm->reslock); + mutex_init(&swrm->force_down_lock); + + ret = of_property_read_u32(swrm->dev->of_node, "qcom,swr-num-dev", + &swrm->num_dev); + if (ret) + dev_dbg(&pdev->dev, "%s: Looking up %s property failed\n", + __func__, "qcom,swr-num-dev"); + else { + if (swrm->num_dev > SWR_MAX_SLAVE_DEVICES) { + dev_err(&pdev->dev, "%s: num_dev %d > max limit %d\n", + __func__, swrm->num_dev, SWR_MAX_SLAVE_DEVICES); + ret = -EINVAL; + goto err_pdata_fail; + } + } + ret = swrm->reg_irq(swrm->handle, swr_mstr_interrupt, swrm, + SWR_IRQ_REGISTER); + if (ret) { + dev_err(&pdev->dev, "%s: IRQ register failed ret %d\n", + __func__, ret); + goto err_irq_fail; + } + + ret = swr_register_master(&swrm->master); + if (ret) { + dev_err(&pdev->dev, "%s: error adding swr master\n", __func__); + goto err_mstr_fail; + } + + /* Add devices registered with board-info as the + * controller will be up now + */ + swr_master_add_boarddevices(&swrm->master); + mutex_lock(&swrm->mlock); + swrm_clk_request(swrm, true); + ret = swrm_master_init(swrm); + if (ret < 0) { + dev_err(&pdev->dev, + "%s: Error in master Initializaiton, err %d\n", + __func__, ret); + mutex_unlock(&swrm->mlock); + goto err_mstr_fail; + } + swrm->version = swrm->read(swrm->handle, SWRM_COMP_HW_VERSION); + + mutex_unlock(&swrm->mlock); + + if (pdev->dev.of_node) + of_register_swr_devices(&swrm->master); + + dbgswrm = swrm; + debugfs_swrm_dent = debugfs_create_dir(dev_name(&pdev->dev), 0); + if (!IS_ERR(debugfs_swrm_dent)) { + debugfs_peek = debugfs_create_file("swrm_peek", + S_IFREG | 0444, debugfs_swrm_dent, + (void *) "swrm_peek", &swrm_debug_ops); + + debugfs_poke = debugfs_create_file("swrm_poke", + S_IFREG | 0444, debugfs_swrm_dent, + (void *) "swrm_poke", &swrm_debug_ops); + + debugfs_reg_dump = debugfs_create_file("swrm_reg_dump", + S_IFREG | 0444, debugfs_swrm_dent, + (void *) "swrm_reg_dump", + &swrm_debug_ops); + } + pm_runtime_set_autosuspend_delay(&pdev->dev, auto_suspend_timer); + pm_runtime_use_autosuspend(&pdev->dev); + pm_runtime_set_active(&pdev->dev); + pm_runtime_enable(&pdev->dev); + pm_runtime_mark_last_busy(&pdev->dev); + + INIT_WORK(&swrm->dc_presence_work, swrm_notify_work_fn); + swrm->event_notifier.notifier_call = swrm_event_notify; + msm_aud_evt_register_client(&swrm->event_notifier); + + return 0; +err_mstr_fail: + swrm->reg_irq(swrm->handle, swr_mstr_interrupt, + swrm, SWR_IRQ_FREE); +err_irq_fail: + mutex_destroy(&swrm->mlock); + mutex_destroy(&swrm->reslock); + mutex_destroy(&swrm->force_down_lock); +err_pdata_fail: + kfree(swrm); +err_memory_fail: + return ret; +} + +static int swrm_remove(struct platform_device *pdev) +{ + struct swr_mstr_ctrl *swrm = platform_get_drvdata(pdev); + + if (swrm->reg_irq) + swrm->reg_irq(swrm->handle, swr_mstr_interrupt, + swrm, SWR_IRQ_FREE); + if (swrm->mstr_port) { + kfree(swrm->mstr_port->port); + swrm->mstr_port->port = NULL; + kfree(swrm->mstr_port); + swrm->mstr_port = NULL; + } + pm_runtime_disable(&pdev->dev); + pm_runtime_set_suspended(&pdev->dev); + swr_unregister_master(&swrm->master); + msm_aud_evt_unregister_client(&swrm->event_notifier); + mutex_destroy(&swrm->mlock); + mutex_destroy(&swrm->reslock); + mutex_destroy(&swrm->force_down_lock); + kfree(swrm); + return 0; +} + +static int swrm_clk_pause(struct swr_mstr_ctrl *swrm) +{ + u32 val; + + dev_dbg(swrm->dev, "%s: state: %d\n", __func__, swrm->state); + swrm->write(swrm->handle, SWRM_INTERRUPT_MASK_ADDR, 0x1FDFD); + val = swrm->read(swrm->handle, SWRM_MCP_CFG_ADDR); + val |= SWRM_MCP_CFG_BUS_CLK_PAUSE_BMSK; + swrm->write(swrm->handle, SWRM_MCP_CFG_ADDR, val); + swrm->state = SWR_MSTR_PAUSE; + + return 0; +} + +#ifdef CONFIG_PM +static int swrm_runtime_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct swr_mstr_ctrl *swrm = platform_get_drvdata(pdev); + int ret = 0; + struct swr_master *mstr = &swrm->master; + struct swr_device *swr_dev; + + dev_dbg(dev, "%s: pm_runtime: resume, state:%d\n", + __func__, swrm->state); + mutex_lock(&swrm->reslock); + if ((swrm->state == SWR_MSTR_PAUSE) || + (swrm->state == SWR_MSTR_DOWN)) { + if (swrm->state == SWR_MSTR_DOWN) { + if (swrm_clk_request(swrm, true)) + goto exit; + } + list_for_each_entry(swr_dev, &mstr->devices, dev_list) { + ret = swr_device_up(swr_dev); + if (ret) { + dev_err(dev, + "%s: failed to wakeup swr dev %d\n", + __func__, swr_dev->dev_num); + swrm_clk_request(swrm, false); + goto exit; + } + } + swrm->write(swrm->handle, SWRM_COMP_SW_RESET, 0x01); + swrm->write(swrm->handle, SWRM_COMP_SW_RESET, 0x01); + swrm_master_init(swrm); + } +exit: + pm_runtime_set_autosuspend_delay(&pdev->dev, auto_suspend_timer); + mutex_unlock(&swrm->reslock); + return ret; +} + +static int swrm_runtime_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct swr_mstr_ctrl *swrm = platform_get_drvdata(pdev); + int ret = 0; + struct swr_master *mstr = &swrm->master; + struct swr_device *swr_dev; + int current_state = 0; + + dev_dbg(dev, "%s: pm_runtime: suspend state: %d\n", + __func__, swrm->state); + mutex_lock(&swrm->reslock); + mutex_lock(&swrm->force_down_lock); + current_state = swrm->state; + mutex_unlock(&swrm->force_down_lock); + if ((current_state == SWR_MSTR_RESUME) || + (current_state == SWR_MSTR_UP) || + (current_state == SWR_MSTR_SSR)) { + + if ((current_state != SWR_MSTR_SSR) && + swrm_is_port_en(&swrm->master)) { + dev_dbg(dev, "%s ports are enabled\n", __func__); + ret = -EBUSY; + goto exit; + } + swrm_clk_pause(swrm); + swrm->write(swrm->handle, SWRM_COMP_CFG_ADDR, 0x00); + list_for_each_entry(swr_dev, &mstr->devices, dev_list) { + ret = swr_device_down(swr_dev); + if (ret) { + dev_err(dev, + "%s: failed to shutdown swr dev %d\n", + __func__, swr_dev->dev_num); + goto exit; + } + } + swrm_clk_request(swrm, false); + } +exit: + mutex_unlock(&swrm->reslock); + return ret; +} +#endif /* CONFIG_PM */ + +static int swrm_device_down(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct swr_mstr_ctrl *swrm = platform_get_drvdata(pdev); + int ret = 0; + + dev_dbg(dev, "%s: swrm state: %d\n", __func__, swrm->state); + + mutex_lock(&swrm->force_down_lock); + swrm->state = SWR_MSTR_SSR; + mutex_unlock(&swrm->force_down_lock); + /* Use pm runtime function to tear down */ + ret = pm_runtime_put_sync_suspend(dev); + pm_runtime_get_noresume(dev); + + return ret; +} + +/** + * swrm_wcd_notify - parent device can notify to soundwire master through + * this function + * @pdev: pointer to platform device structure + * @id: command id from parent to the soundwire master + * @data: data from parent device to soundwire master + */ +int swrm_wcd_notify(struct platform_device *pdev, u32 id, void *data) +{ + struct swr_mstr_ctrl *swrm; + int ret = 0; + struct swr_master *mstr; + struct swr_device *swr_dev; + + if (!pdev) { + pr_err("%s: pdev is NULL\n", __func__); + return -EINVAL; + } + swrm = platform_get_drvdata(pdev); + if (!swrm) { + dev_err(&pdev->dev, "%s: swrm is NULL\n", __func__); + return -EINVAL; + } + mstr = &swrm->master; + + switch (id) { + case SWR_CH_MAP: + if (!data) { + dev_err(swrm->dev, "%s: data is NULL\n", __func__); + ret = -EINVAL; + } else { + ret = swrm_set_ch_map(swrm, data); + } + break; + case SWR_DEVICE_DOWN: + dev_dbg(swrm->dev, "%s: swr master down called\n", __func__); + mutex_lock(&swrm->mlock); + if ((swrm->state == SWR_MSTR_PAUSE) || + (swrm->state == SWR_MSTR_DOWN)) + dev_dbg(swrm->dev, "%s: SWR master is already Down: %d\n", + __func__, swrm->state); + else + swrm_device_down(&pdev->dev); + mutex_unlock(&swrm->mlock); + break; + case SWR_DEVICE_UP: + dev_dbg(swrm->dev, "%s: swr master up called\n", __func__); + mutex_lock(&swrm->mlock); + mutex_lock(&swrm->reslock); + if ((swrm->state == SWR_MSTR_RESUME) || + (swrm->state == SWR_MSTR_UP)) { + dev_dbg(swrm->dev, "%s: SWR master is already UP: %d\n", + __func__, swrm->state); + list_for_each_entry(swr_dev, &mstr->devices, dev_list) + swr_reset_device(swr_dev); + } else { + pm_runtime_mark_last_busy(&pdev->dev); + mutex_unlock(&swrm->reslock); + pm_runtime_get_sync(&pdev->dev); + mutex_lock(&swrm->reslock); + list_for_each_entry(swr_dev, &mstr->devices, dev_list) { + ret = swr_reset_device(swr_dev); + if (ret) { + dev_err(swrm->dev, + "%s: failed to reset swr device %d\n", + __func__, swr_dev->dev_num); + swrm_clk_request(swrm, false); + } + } + pm_runtime_mark_last_busy(&pdev->dev); + pm_runtime_put_autosuspend(&pdev->dev); + } + mutex_unlock(&swrm->reslock); + mutex_unlock(&swrm->mlock); + break; + case SWR_SET_NUM_RX_CH: + if (!data) { + dev_err(swrm->dev, "%s: data is NULL\n", __func__); + ret = -EINVAL; + } else { + mutex_lock(&swrm->mlock); + swrm->num_rx_chs = *(int *)data; + if ((swrm->num_rx_chs > 1) && !swrm->num_cfg_devs) { + list_for_each_entry(swr_dev, &mstr->devices, + dev_list) { + ret = swr_set_device_group(swr_dev, + SWR_BROADCAST); + if (ret) + dev_err(swrm->dev, + "%s: set num ch failed\n", + __func__); + } + } else { + list_for_each_entry(swr_dev, &mstr->devices, + dev_list) { + ret = swr_set_device_group(swr_dev, + SWR_GROUP_NONE); + if (ret) + dev_err(swrm->dev, + "%s: set num ch failed\n", + __func__); + } + } + mutex_unlock(&swrm->mlock); + } + break; + default: + dev_err(swrm->dev, "%s: swr master unknown id %d\n", + __func__, id); + break; + } + return ret; +} +EXPORT_SYMBOL(swrm_wcd_notify); + +#ifdef CONFIG_PM_SLEEP +static int swrm_suspend(struct device *dev) +{ + int ret = -EBUSY; + struct platform_device *pdev = to_platform_device(dev); + struct swr_mstr_ctrl *swrm = platform_get_drvdata(pdev); + + dev_dbg(dev, "%s: system suspend, state: %d\n", __func__, swrm->state); + if (!pm_runtime_enabled(dev) || !pm_runtime_suspended(dev)) { + ret = swrm_runtime_suspend(dev); + if (!ret) { + /* + * Synchronize runtime-pm and system-pm states: + * At this point, we are already suspended. If + * runtime-pm still thinks its active, then + * make sure its status is in sync with HW + * status. The three below calls let the + * runtime-pm know that we are suspended + * already without re-invoking the suspend + * callback + */ + pm_runtime_disable(dev); + pm_runtime_set_suspended(dev); + pm_runtime_enable(dev); + } + } + if (ret == -EBUSY) { + /* + * There is a possibility that some audio stream is active + * during suspend. We dont want to return suspend failure in + * that case so that display and relevant components can still + * go to suspend. + * If there is some other error, then it should be passed-on + * to system level suspend + */ + ret = 0; + } + return ret; +} + +static int swrm_resume(struct device *dev) +{ + int ret = 0; + struct platform_device *pdev = to_platform_device(dev); + struct swr_mstr_ctrl *swrm = platform_get_drvdata(pdev); + + dev_dbg(dev, "%s: system resume, state: %d\n", __func__, swrm->state); + if (!pm_runtime_enabled(dev) || !pm_runtime_suspend(dev)) { + ret = swrm_runtime_resume(dev); + if (!ret) { + pm_runtime_mark_last_busy(dev); + pm_request_autosuspend(dev); + } + } + return ret; +} +#endif /* CONFIG_PM_SLEEP */ + +static const struct dev_pm_ops swrm_dev_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS( + swrm_suspend, + swrm_resume + ) + SET_RUNTIME_PM_OPS( + swrm_runtime_suspend, + swrm_runtime_resume, + NULL + ) +}; + +static const struct of_device_id swrm_dt_match[] = { + { + .compatible = "qcom,swr-wcd", + }, + {} +}; + +static struct platform_driver swr_mstr_driver = { + .probe = swrm_probe, + .remove = swrm_remove, + .driver = { + .name = SWR_WCD_NAME, + .owner = THIS_MODULE, + .pm = &swrm_dev_pm_ops, + .of_match_table = swrm_dt_match, + }, +}; + +static int __init swrm_init(void) +{ + return platform_driver_register(&swr_mstr_driver); +} +module_init(swrm_init); + +static void __exit swrm_exit(void) +{ + platform_driver_unregister(&swr_mstr_driver); +} +module_exit(swrm_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("WCD SoundWire Controller"); +MODULE_ALIAS("platform:swr-wcd"); diff --git a/audio-kernel/soc/swr-wcd-ctrl.h b/audio-kernel/soc/swr-wcd-ctrl.h new file mode 100644 index 000000000..14b2b48db --- /dev/null +++ b/audio-kernel/soc/swr-wcd-ctrl.h @@ -0,0 +1,116 @@ +/* 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 + * 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 _SWR_WCD_CTRL_H +#define _SWR_WCD_CTRL_H +#include +#include + +#define SWR_MAX_ROW 0 /* Rows = 48 */ +#define SWR_MAX_COL 7 /* Cols = 16 */ +#define SWR_MIN_COL 0 /* Cols = 2 */ + +#define SWR_WCD_NAME "swr-wcd" + +#define SWR_MSTR_PORT_LEN 8 /* Number of master ports */ + +#define SWR_MAX_SLAVE_DEVICES 11 + +#define SWRM_VERSION_1_0 0x01010000 +#define SWRM_VERSION_1_2 0x01030000 +#define SWRM_VERSION_1_3 0x01040000 + +enum { + SWR_MSTR_PAUSE, + SWR_MSTR_RESUME, + SWR_MSTR_UP, + SWR_MSTR_DOWN, + SWR_MSTR_SSR, +}; + +enum { + SWR_IRQ_FREE, + SWR_IRQ_REGISTER, +}; + +enum { + SWR_DAC_PORT, + SWR_COMP_PORT, + SWR_BOOST_PORT, + SWR_VISENSE_PORT, +}; + +struct usecase { + u8 num_port; + u8 num_ch; + u32 chrate; +}; + +struct port_params { + u8 si; + u8 off1; + u8 off2; +}; + +struct swrm_mports { + struct list_head list; + u8 id; +}; + +struct swr_ctrl_platform_data { + void *handle; /* holds priv data */ + int (*read)(void *handle, int reg); + int (*write)(void *handle, int reg, int val); + int (*bulk_write)(void *handle, u32 *reg, u32 *val, size_t len); + int (*clk)(void *handle, bool enable); + int (*reg_irq)(void *handle, irqreturn_t(*irq_handler)(int irq, + void *data), void *swr_handle, int type); +}; + +struct swr_mstr_ctrl { + struct swr_master master; + struct device *dev; + struct resource *supplies; + struct clk *mclk; + int clk_ref_count; + struct completion reset; + struct completion broadcast; + struct mutex mlock; + struct mutex reslock; + u8 rcmd_id; + u8 wcmd_id; + void *handle; /* SWR Master handle from client for read and writes */ + int (*read)(void *handle, int reg); + int (*write)(void *handle, int reg, int val); + int (*bulk_write)(void *handle, u32 *reg, u32 *val, size_t len); + int (*clk)(void *handle, bool enable); + int (*reg_irq)(void *handle, irqreturn_t(*irq_handler)(int irq, + void *data), void *swr_handle, int type); + int irq; + int version; + u32 num_dev; + int num_enum_slaves; + int slave_status; + struct swr_mstr_port *mstr_port; + struct list_head mport_list; + int state; + struct platform_device *pdev; + int num_rx_chs; + u8 num_cfg_devs; + struct mutex force_down_lock; + int force_down_state; + + struct notifier_block event_notifier; + struct work_struct dc_presence_work; +}; + +#endif /* _SWR_WCD_CTRL_H */ diff --git a/audio-kernel/soc/swrm_port_config.h b/audio-kernel/soc/swrm_port_config.h new file mode 100644 index 000000000..0ed74ec3c --- /dev/null +++ b/audio-kernel/soc/swrm_port_config.h @@ -0,0 +1,70 @@ +/* Copyright (c) 2018-2019 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 _SWRM_PORT_CONFIG +#define _SWRM_PORT_CONFIG + +#define WSA_MSTR_PORT_MASK 0xFF + + +/* + * Add port configuration in the format + *{ si, off1, off2, hstart, hstop, wd_len, bp_mode, bgp_ctrl, lane_ctrl} + */ + +/* WSA ports - DAC, COMP, BOOST, DAC, COMP, BOOST, VI, VI */ +struct port_params wsa_frame_superset[SWR_MSTR_PORT_LEN] = { + {7, 1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, + {31, 2, 0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, + {63, 12, 31, 0xFF, 0xFF, 0xFF, 0x1, 0xFF, 0xFF}, + {7, 6, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, + {31, 18, 0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, + {63, 13, 31, 0xFF, 0xFF, 0xFF, 0x1, 0xFF, 0xFF}, + {15, 7, 0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, + {15, 10, 0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, +}; + +/* RX ports - HPH, CLSH, COMP, LO, DSD */ +struct port_params rx_frame_params[SWR_MSTR_PORT_LEN] = { + {3, 0, 0, 0xFF, 0xFF, 1, 0xFF, 0xFF, 1}, + {31, 0, 0, 3, 6, 7, 0, 0xFF, 0}, + {31, 11, 11, 0xFF, 0xFF, 4, 1, 0xFF, 0}, + {3, 1, 0, 0xFF, 0xFF, 0xFF, 0xFF, 1, 0}, + {0, 0, 0, 0xFF, 0xFF, 0xFF, 0xFF, 0, 0}, +}; + +struct port_params rx_frame_params_dsd[SWR_MSTR_PORT_LEN] = { + {3, 0, 0, 0xFF, 0xFF, 1, 0xFF, 0xFF, 1}, + {31, 0, 0, 3, 6, 7, 0, 0xFF, 0}, + {31, 11, 11, 0xFF, 0xFF, 4, 1, 0xFF, 0}, + {7, 9, 0, 0xFF, 0xFF, 0xFF, 0xFF, 1, 0}, + {3, 1, 0, 0xFF, 0xFF, 0xFF, 0xFF, 3, 0}, +}; + +/* TX ports - PCM_OUT, TX1, TX2, TX3, TX4 */ +struct port_params tx_perf_frame_params_superset[SWR_MSTR_PORT_LEN] = { + {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, + {1, 1, 0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0}, + {1, 0, 0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 1}, + {3, 1, 0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0}, + {3, 0, 0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 1}, +}; + +struct port_params tx_frame_params_superset[SWR_MSTR_PORT_LEN] = { + {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, + {3, 1, 0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0}, + {3, 2, 0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0}, + {3, 1, 0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0}, + {3, 0, 0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0}, +}; + +#endif /* _SWRM_REGISTERS_H */ diff --git a/audio-kernel/soc/swrm_registers.h b/audio-kernel/soc/swrm_registers.h new file mode 100644 index 000000000..ecff48d69 --- /dev/null +++ b/audio-kernel/soc/swrm_registers.h @@ -0,0 +1,248 @@ +/* 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 + * 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 _SWRM_REGISTERS_H +#define _SWRM_REGISTERS_H + +#define SWRM_BASE_ADDRESS 0x00 + +#define SWRM_COMP_HW_VERSION SWRM_BASE_ADDRESS +#define SWRM_COMP_CFG_ADDR (SWRM_BASE_ADDRESS+0x00000004) +#define SWRM_COMP_CFG_RMSK 0x3 +#define SWRM_COMP_CFG_IRQ_LEVEL_OR_PULSE_BMSK 0x2 +#define SWRM_COMP_CFG_IRQ_LEVEL_OR_PULSE_SHFT 0x1 +#define SWRM_COMP_CFG_ENABLE_BMSK 0x1 +#define SWRM_COMP_CFG_ENABLE_SHFT 0x0 + +#define SWRM_COMP_SW_RESET (SWRM_BASE_ADDRESS+0x00000008) + +#define SWRM_COMP_PARAMS (SWRM_BASE_ADDRESS+0x100) +#define SWRM_COMP_PARAMS_DOUT_PORTS_MASK 0x0000001F +#define SWRM_COMP_PARAMS_DIN_PORTS_MASK 0x000003E0 +#define SWRM_COMP_PARAMS_WR_FIFO_DEPTH 0x00007C00 +#define SWRM_COMP_PARAMS_RD_FIFO_DEPTH 0x000F8000 +#define SWRM_COMP_PARAMS_AUTO_ENUM_SLAVES 0x00F00000 +#define SWRM_COMP_PARAMS_DATA_LANES 0x07000000 + +#define SWRM_COMP_MASTER_ID (SWRM_BASE_ADDRESS+0x104) + +#define SWRM_INTERRUPT_STATUS (SWRM_BASE_ADDRESS+0x00000200) +#define SWRM_INTERRUPT_STATUS_RMSK 0x1FFFD + +#define SWRM_INTERRUPT_STATUS_SLAVE_PEND_IRQ 0x1 +#define SWRM_INTERRUPT_STATUS_NEW_SLAVE_ATTACHED 0x2 +#define SWRM_INTERRUPT_STATUS_CHANGE_ENUM_SLAVE_STATUS 0x4 +#define SWRM_INTERRUPT_STATUS_MASTER_CLASH_DET 0x8 +#define SWRM_INTERRUPT_STATUS_RD_FIFO_OVERFLOW 0x10 +#define SWRM_INTERRUPT_STATUS_RD_FIFO_UNDERFLOW 0x20 +#define SWRM_INTERRUPT_STATUS_WR_CMD_FIFO_OVERFLOW 0x40 +#define SWRM_INTERRUPT_STATUS_CMD_ERROR 0x80 +#define SWRM_INTERRUPT_STATUS_DOUT_PORT_COLLISION 0x100 +#define SWRM_INTERRUPT_STATUS_READ_EN_RD_VALID_MISMATCH 0x200 +#define SWRM_INTERRUPT_STATUS_SPECIAL_CMD_ID_FINISHED 0x400 +#define SWRM_INTERRUPT_STATUS_NEW_SLAVE_AUTO_ENUM_FINISHED 0x800 +#define SWRM_INTERRUPT_STATUS_AUTO_ENUM_FAILED 0x1000 +#define SWRM_INTERRUPT_STATUS_AUTO_ENUM_TABLE_IS_FULL 0x2000 +#define SWRM_INTERRUPT_STATUS_BUS_RESET_FINISHED 0x4000 +#define SWRM_INTERRUPT_STATUS_CLK_STOP_FINISHED 0x8000 +#define SWRM_INTERRUPT_STATUS_ERROR_PORT_TEST 0x10000 + +#define SWRM_INTERRUPT_MASK_ADDR (SWRM_BASE_ADDRESS+0x00000204) +#define SWRM_INTERRUPT_MASK_RMSK 0x1FFFF + +#define SWRM_INTERRUPT_MASK_SLAVE_PEND_IRQ_BMSK 0x1 +#define SWRM_INTERRUPT_MASK_SLAVE_PEND_IRQ_SHFT 0x0 + +#define SWRM_INTERRUPT_MASK_NEW_SLAVE_ATTACHED_BMSK 0x2 +#define SWRM_INTERRUPT_MASK_NEW_SLAVE_ATTACHED_SHFT 0x1 + +#define SWRM_INTERRUPT_MASK_CHANGE_ENUM_SLAVE_STATUS_BMSK 0x4 +#define SWRM_INTERRUPT_MASK_CHANGE_ENUM_SLAVE_STATUS_SHFT 0x2 + +#define SWRM_INTERRUPT_MASK_MASTER_CLASH_DET_BMSK 0x8 +#define SWRM_INTERRUPT_MASK_MASTER_CLASH_DET_SHFT 0x3 + +#define SWRM_INTERRUPT_MASK_RD_FIFO_OVERFLOW_BMSK 0x10 +#define SWRM_INTERRUPT_MASK_RD_FIFO_OVERFLOW_SHFT 0x4 + +#define SWRM_INTERRUPT_MASK_RD_FIFO_UNDERFLOW_BMSK 0x20 +#define SWRM_INTERRUPT_MASK_RD_FIFO_UNDERFLOW_SHFT 0x5 + +#define SWRM_INTERRUPT_MASK_WR_CMD_FIFO_OVERFLOW_BMSK 0x40 +#define SWRM_INTERRUPT_MASK_WR_CMD_FIFO_OVERFLOW_SHFT 0x6 + +#define SWRM_INTERRUPT_MASK_CMD_ERROR_BMSK 0x80 +#define SWRM_INTERRUPT_MASK_CMD_ERROR_SHFT 0x7 + +#define SWRM_INTERRUPT_MASK_DOUT_PORT_COLLISION_BMSK 0x100 +#define SWRM_INTERRUPT_MASK_DOUT_PORT_COLLISION_SHFT 0x8 + +#define SWRM_INTERRUPT_MASK_READ_EN_RD_VALID_MISMATCH_BMSK 0x200 +#define SWRM_INTERRUPT_MASK_READ_EN_RD_VALID_MISMATCH_SHFT 0x9 + +#define SWRM_INTERRUPT_MASK_SPECIAL_CMD_ID_FINISHED_BMSK 0x400 +#define SWRM_INTERRUPT_MASK_SPECIAL_CMD_ID_FINISHED_SHFT 0xA + +#define SWRM_INTERRUPT_MASK_NEW_SLAVE_AUTO_ENUM_FINISHED_BMSK 0x800 +#define SWRM_INTERRUPT_MASK_NEW_SLAVE_AUTO_ENUM_FINISHED_SHFT 0xB + +#define SWRM_INTERRUPT_MASK_AUTO_ENUM_FAILED_BMSK 0x1000 +#define SWRM_INTERRUPT_MASK_AUTO_ENUM_FAILED_SHFT 0xC + +#define SWRM_INTERRUPT_MASK_AUTO_ENUM_TABLE_IS_FULL_BMSK 0x2000 +#define SWRM_INTERRUPT_MASK_AUTO_ENUM_TABLE_IS_FULL_SHFT 0xD + +#define SWRM_INTERRUPT_MASK_BUS_RESET_FINISHED_BMSK 0x4000 +#define SWRM_INTERRUPT_MASK_BUS_RESET_FINISHED_SHFT 0xE + +#define SWRM_INTERRUPT_MASK_CLK_STOP_FINISHED_BMSK 0x8000 +#define SWRM_INTERRUPT_MASK_CLK_STOP_FINISHED_SHFT 0xF + +#define SWRM_INTERRUPT_MASK_ERROR_PORT_TEST_BMSK 0x10000 +#define SWRM_INTERRUPT_MASK_ERROR_PORT_TEST_SHFT 0x10 + +#define SWRM_INTERRUPT_MAX 0x11 + +#define SWRM_INTERRUPT_CLEAR (SWRM_BASE_ADDRESS+0x00000208) + +#define SWR_MSTR_RX_SWRM_CPU_INTERRUPT_EN (SWRM_BASE_ADDRESS+0x00000210) + +#define SWRM_CMD_FIFO_WR_CMD (SWRM_BASE_ADDRESS + 0x00000300) +#define SWRM_CMD_FIFO_WR_CMD_MASK 0xFFFFFFFF +#define SWRM_CMD_FIFO_RD_CMD (SWRM_BASE_ADDRESS + 0x00000304) +#define SWRM_CMD_FIFO_RD_CMD_MASK 0xFFFFFFF +#define SWRM_CMD_FIFO_CMD (SWRM_BASE_ADDRESS + 0x00000308) +#define SWRM_CMD_FIFO_STATUS (SWRM_BASE_ADDRESS + 0x0000030C) + +#define SWRM_CMD_FIFO_STATUS_WR_CMD_FIFO_CNT_MASK 0x1F00 +#define SWRM_CMD_FIFO_STATUS_RD_CMD_FIFO_CNT_MASK 0x7C00000 + +#define SWRM_CMD_FIFO_CFG_ADDR (SWRM_BASE_ADDRESS+0x00000314) +#define SWRM_CMD_FIFO_CFG_NUM_OF_CMD_RETRY_BMSK 0x7 +#define SWRM_CMD_FIFO_CFG_NUM_OF_CMD_RETRY_SHFT 0x0 + +#define SWRM_CMD_FIFO_RD_FIFO_ADDR (SWRM_BASE_ADDRESS + 0x00000318) + +#define SWRM_ENUMERATOR_CFG_ADDR (SWRM_BASE_ADDRESS+0x00000500) +#define SWRM_ENUMERATOR_CFG_AUTO_ENUM_EN_BMSK 0x1 +#define SWRM_ENUMERATOR_CFG_AUTO_ENUM_EN_SHFT 0x0 + +#define SWRM_ENUMERATOR_SLAVE_DEV_ID_1(m) (SWRM_BASE_ADDRESS+0x530+0x8*m) +#define SWRM_ENUMERATOR_SLAVE_DEV_ID_2(m) (SWRM_BASE_ADDRESS+0x534+0x8*m) + +#define SWRM_MCP_FRAME_CTRL_BANK_ADDR(m) (SWRM_BASE_ADDRESS+0x101C+0x40*m) +#define SWRM_MCP_FRAME_CTRL_BANK_RMSK 0x00ff07ff +#define SWRM_MCP_FRAME_CTRL_BANK_SHFT 0 +#define SWRM_MCP_FRAME_CTRL_BANK_SSP_PERIOD_BMSK 0xff0000 +#define SWRM_MCP_FRAME_CTRL_BANK_SSP_PERIOD_SHFT 16 +#define SWRM_MCP_FRAME_CTRL_BANK_PHASE_BMSK 0xf800 +#define SWRM_MCP_FRAME_CTRL_BANK_PHASE_SHFT 11 +#define SWRM_MCP_FRAME_CTRL_BANK_CLK_DIV_VALUE_BMSK 0x700 +#define SWRM_MCP_FRAME_CTRL_BANK_CLK_DIV_VALUE_SHFT 8 +#define SWRM_MCP_FRAME_CTRL_BANK_ROW_CTRL_BMSK 0xF8 +#define SWRM_MCP_FRAME_CTRL_BANK_ROW_CTRL_SHFT 3 +#define SWRM_MCP_FRAME_CTRL_BANK_COL_CTRL_BMSK 0x7 +#define SWRM_MCP_FRAME_CTRL_BANK_COL_CTRL_SHFT 0 + +#define SWRM_MCP_BUS_CTRL_ADDR (SWRM_BASE_ADDRESS+0x00001044) +#define SWRM_MCP_BUS_CTRL_BUS_RESET_BMSK 0x1 +#define SWRM_MCP_BUS_CTRL_BUS_RESET_SHFT 0x0 +#define SWRM_MCP_BUS_CTRL_CLK_START_BMSK 0x2 +#define SWRM_MCP_BUS_CTRL_CLK_START_SHFT 0x1 + +#define SWRM_MCP_CFG_ADDR (SWRM_BASE_ADDRESS+0x00001048) +#define SWRM_MCP_CFG_MAX_NUM_OF_CMD_NO_PINGS_BMSK 0x3E0000 +#define SWRM_MCP_CFG_MAX_NUM_OF_CMD_NO_PINGS_SHFT 0x11 +#define SWRM_MCP_CFG_BUS_CLK_PAUSE_BMSK 0x02 + +#define SWRM_MCP_STATUS (SWRM_BASE_ADDRESS+0x104C) +#define SWRM_MCP_STATUS_BANK_NUM_MASK 0x01 + +#define SWRM_MCP_SLV_STATUS (SWRM_BASE_ADDRESS+0x1090) +#define SWRM_MCP_SLV_STATUS_MASK 0x03 + +#define SWRM_DP_PORT_CTRL_BANK(n, m) (SWRM_BASE_ADDRESS + \ + 0x00001124 + \ + 0x100*(n-1) + \ + 0x40*m) +#define SWRM_DP_PORT_CTRL_BANK_MASK 0xFFFFFFFF +#define SWRM_DP_PORT_CTRL_EN_CHAN_MASK 0xFF000000 +#define SWRM_DP_PORT_CTRL_EN_CHAN_SHFT 0x18 +#define SWRM_DP_PORT_CTRL_OFFSET2_SHFT 0x10 +#define SWRM_DP_PORT_CTRL_OFFSET1_SHFT 0x08 +#define SWRM_DP_PORT_CTRL_SAMPLE_INTERVAL 0x00 + +#define SWRM_DP_PORT_CTRL_2_BANK(n, m) (SWRM_BASE_ADDRESS + \ + 0x00001128 + \ + 0x100*(n-1) + \ + 0x40*m) + +#define SWRM_DP_BLOCK_CTRL_1(n) (SWRM_BASE_ADDRESS + \ + 0x0000112C + \ + 0x100*(n-1)) + +#define SWRM_DP_BLOCK_CTRL2_BANK(n, m) (SWRM_BASE_ADDRESS + \ + 0x00001130 + \ + 0x100*(n-1) + \ + 0x40*m) + +#define SWRM_DP_PORT_HCTRL_BANK(n, m) (SWRM_BASE_ADDRESS + \ + 0x00001134 + \ + 0x100*(n-1) + \ + 0x40*m) + +#define SWRM_DP_BLOCK_CTRL3_BANK(n, m) (SWRM_BASE_ADDRESS + \ + 0x00001138 + \ + 0x100*(n-1) + \ + 0x40*m) + + +#define SWRM_DIN_DPn_PCM_PORT_CTRL(n) (SWRM_BASE_ADDRESS + \ + 0x00001054 + 0x100*(n-1)) + +#define SWRM_MAX_REGISTER SWRM_DIN_DPn_PCM_PORT_CTRL(7) + +/* Soundwire Slave Register definition */ + +#define SWRS_BASE_ADDRESS 0x00 + +#define SWRS_DP_REG_OFFSET(port, bank) ((0x100*port)+(0x10*bank)) + +#define SWRS_SCP_INT_STATUS_CLEAR_1 0x40 +#define SWRS_SCP_INT_STATUS_MASK_1 0x41 + +#define SWRS_SCP_CONTROL 0x44 +#define SWRS_DP_BLOCK_CONTROL_1(n) (SWRS_BASE_ADDRESS + 0x103 + \ + 0x100 * n) + +#define SWRS_DP_CHANNEL_ENABLE_BANK(n, m) (SWRS_BASE_ADDRESS + 0x120 + \ + SWRS_DP_REG_OFFSET(n, m)) +#define SWRS_DP_BLOCK_CONTROL_2_BANK(n, m) (SWRS_BASE_ADDRESS + 0x121 + \ + SWRS_DP_REG_OFFSET(n, m)) +#define SWRS_DP_SAMPLE_CONTROL_1_BANK(n, m) (SWRS_BASE_ADDRESS + 0x122 + \ + SWRS_DP_REG_OFFSET(n, m)) +#define SWRS_DP_OFFSET_CONTROL_1_BANK(n, m) (SWRS_BASE_ADDRESS + 0x124 + \ + SWRS_DP_REG_OFFSET(n, m)) +#define SWRS_DP_OFFSET_CONTROL_2_BANK(n, m) (SWRS_BASE_ADDRESS + 0x125 + \ + SWRS_DP_REG_OFFSET(n, m)) +#define SWRS_DP_HCONTROL_BANK(n, m) (SWRS_BASE_ADDRESS + 0x126 + \ + SWRS_DP_REG_OFFSET(n, m)) +#define SWRS_DP_BLOCK_CONTROL_3_BANK(n, m) (SWRS_BASE_ADDRESS + 0x127 + \ + SWRS_DP_REG_OFFSET(n, m)) +#define SWRS_DP_LANE_CONTROL_BANK(n, m) (SWRS_BASE_ADDRESS + 0x128 + \ + SWRS_DP_REG_OFFSET(n, m)) +#define SWRS_SCP_FRAME_CTRL_BANK(m) (SWRS_BASE_ADDRESS + 0x60 + \ + 0x10*m) +#define SWRS_SCP_HOST_CLK_DIV2_CTL_BANK(m) (SWRS_BASE_ADDRESS + 0xE0 + \ + 0x10*m) + +#endif /* _SWRM_REGISTERS_H */